-- Print Labels.mesa
--   Edited by Sweet,  2-Oct-82 21:45:30

DIRECTORY
  AltoFileDefs,
  Ascii,
  Inline,
  IODefs,
  ImageDefs,
  MiscDefs,
  PressDefs,
  PressUtilities,
  SegmentDefs,
  Storage,
  StreamDefs,
  String;

PrintLabels: PROGRAM 
  IMPORTS  
    ImageDefs, Inline, IODefs, MiscDefs, PressDefs,
    PressUtilities, SegmentDefs, Storage, StreamDefs, String =
  BEGIN OPEN IODefs, PressDefs, String, StreamDefs;

  in: StreamHandle;

  Label: TYPE = BASE POINTER TO LabelBody;
  RelLabel: TYPE = Label RELATIVE POINTER TO StringBody;
  LabelBody: TYPE = RECORD [
   count, last: [0..256), 
   lines: ARRAY [0..8) OF RelLabel];
  nextRP: RelLabel;

  AddLine: PROC =
    BEGIN
    s: STRING = @buffer[nextRP];
    s↑ ← [length: 0, maxlength: line.length, text: NULL];
    String.AppendString[s, line];
    buffer.lines[buffer.count] ← nextRP;
    nextRP ← nextRP + String.WordsForString[line.length];
    WHILE s.length # 0 AND s[s.length-1] = Ascii.SP DO
      s.length ← s.length - 1;
      ENDLOOP;
    IF ~slashSeen AND line[0] = '/ THEN {
      slashSeen ← TRUE; buffer.last ← buffer.count};
    buffer.count ← buffer.count + 1;
    END;

  StartLabel: PROC =
    BEGIN
    buffer.count ← 0; buffer.last ← 0;
    slashSeen ← FALSE;
    nextRP ← LOOPHOLE[SIZE[LabelBody]];
    END;

  slashSeen: BOOLEAN;
  pfdBody: PressFileDescriptor;
  pfd: POINTER TO PressFileDescriptor = @pfdBody;
  Mica: TYPE = CARDINAL;
  MBox: TYPE = RECORD [x,y,w,h: Mica];
  CharHeight: Mica;
  CharWidth: POINTER TO ARRAY CHARACTER OF Mica;
  TextCharWidth: ARRAY CHARACTER OF Mica;
  TextCharHeight: Mica;

  PointsToMicas: PROC [points: CARDINAL] RETURNS [Mica] =
    {RETURN [Inline.LongDiv[Inline.LongMult[points, MicasPerInch],72]]};

  StringWidth: PROC [s: STRING] RETURNS [l: Mica] =
    BEGIN
    l ← 0;
    FOR i: CARDINAL IN [0..s.length) DO
      l ← l + CharWidth[s[i]];
      ENDLOOP;
    END;

  LineY: PROC [box: POINTER TO MBox, line, of: CARDINAL, lead: Mica ← 0] 
    RETURNS [Mica] =
    BEGIN
    h: Mica = CharHeight;
    bottom: Mica;
    line ← of-1-line; -- count from top
    bottom ← (box.h- of*h - (of-1)*lead)/2;
    RETURN [box.y + bottom + (line-1)*(h+lead)];
    END;

  LJLine: PROC [s: STRING, box: POINTER TO MBox, line, of: CARDINAL, lead: Mica ← 1] =
    BEGIN
    y: Mica = LineY[box: box, line: line, of: of, lead: lead];
    PutText[pfd, s, box.x, y];
    END;

  P1: Mica = PointsToMicas[1];
  P2: Mica = PointsToMicas[2];
  M1: Mica = MicasPerInch;
  M12: Mica = MicasPerInch/2;
  M14: Mica = MicasPerInch/4;
  M34: Mica = (3*MicasPerInch)/4;
  M38: Mica = (3*MicasPerInch)/8;

  TextFont: PROC =
    BEGIN
    SetFont[p: pfd, Name: "Helvetica", PointSize: outPointSize, Face: 0];
    CharHeight ← TextCharHeight;
    CharWidth ← @TextCharWidth;
    END;

  outPointSize: CARDINAL ← 10;
  
  DigestFonts: PROC =
    BEGIN
    [] ← PressUtilities.FindFontWidths[
      family: "Helvetica"L,
      points: outPointSize,
      weight: medium,
      slope: regular,
      widths: LOOPHOLE[@TextCharWidth]];
    TextCharHeight ← PointsToMicas[outPointSize];
    END;

  commandStream: StreamHandle;

  SetUpCommands: PROCEDURE =
    BEGIN
    cfa: POINTER TO AltoFileDefs.CFA ← MiscDefs.CommandLineCFA[];
    cfile: SegmentDefs.FileHandle ← SegmentDefs.InsertFile[@cfa.fp,Read];
    commandStream ← NIL;
    commandStream ← CreateByteStream[cfile,Read
      ! SegmentDefs.InvalidFP => CONTINUE];
    IF commandStream # NIL THEN
      BEGIN
      JumpToFA[commandStream,@cfa.fa];
      WHILE commandStream.get[commandStream
              ! StreamError => GOTO nocommands] <= SP DO
        NULL ENDLOOP;
      SetIndex[commandStream,ModifyIndex[GetIndex[commandStream],-1]];
      EXITS nocommands =>
        BEGIN commandStream.destroy[commandStream]; commandStream ← NIL END;
      END;
    END;

  ReadName: PROCEDURE [s: STRING] =
    BEGIN
    c: CHARACTER;
    IF commandStream = NIL THEN ReadID[s]
    ELSE
      BEGIN
      s.length ← 0;
      DO
        IF (c←commandStream.get[commandStream
              ! StreamError => GOTO endoffile]) <= SP THEN
          BEGIN IF s.length # 0 THEN EXIT END
  	ELSE BEGIN String.AppendChar[s,c]; WriteChar[c] END;
        REPEAT endoffile =>
          BEGIN commandStream.destroy[commandStream]; commandStream ← NIL END;
        ENDLOOP;
      END;
    END;

  GetFile: PROCEDURE [prompt: STRING, access: AccessOptions, switches: STRING ← NIL] RETURNS [StreamHandle] =
    BEGIN
    name: STRING ← [40];
    WriteString[prompt];
    ReadName[name];
    IF switches # NIL THEN FOR i:CARDINAL IN[0..name.length) DO
      IF name[i] = '/ THEN
	BEGIN
	FOR j: CARDINAL IN (i..name.length) DO
	  String.AppendChar[switches,name[j]] ENDLOOP;
	name.length ← i;
	EXIT;
	END;
      ENDLOOP;
    WriteChar[CR];
    IF name.length = 0 THEN ImageDefs.StopMesa[];
    RETURN[NewByteStream[name,access]]
    END;

  ReadLabel: PROC =
    BEGIN
    StartLabel[];
    DO
      ReadLine[];
      IF line.length = 0 AND (buffer.count # 0 OR in.endof[in]) THEN EXIT;
      IF line.length # 0 THEN AddLine[];
      ENDLOOP;
    IF ~slashSeen THEN buffer.last ← buffer.count;
    END;

  ReadLine: PROC =
    BEGIN
    c: CHARACTER;
    n: CARDINAL ← 0;
    line.length ← 0;
    BEGIN
    DO
      c ← in.get[in ! StreamError => EXIT];
      IF c = CR THEN EXIT;
      IF n = line.maxlength-1 THEN GO TO tooLong;
      line[n] ← c;
      n ← n + 1;
      ENDLOOP;
    line.length ← n;
    EXITS
      tooLong => {
	IODefs.WriteLine["Line truncated:"L];
	IODefs.WriteLine[line];
	line.length ← n};
    END;
    END;

  line: STRING ← [200];

  row, col: CARDINAL;
  LWidth: Mica = (8*M1 + M12)/3;
  LHeight: Mica = M1;
  yDelta: Mica ← 0;
  scaleFactor: CARDINAL ← 100;
  
  Scale: PROC [d: Mica] RETURNS [Mica] = { -- d must be > 0
    RETURN [Inline.LongDiv[Inline.LongMult[d, scaleFactor], 100]]};

  PressLabel: PROC [p: Label] =
    BEGIN
    name: STRING = [100];
    l1: STRING = @p[p.lines[0]];
    comma: CARDINAL ← l1.length;
    j: CARDINAL;
    box: MBox;
    IF row = 11 THEN {row ← 0; col ← col + 1};
    IF col = 3 THEN {WritePage[pfd]; col ← 0};
    box.x ← M38 + col * LWidth;
    box.y ← yDelta + 10*M1 - Scale[row*LHeight];
    box.w ← LWidth - M38;
    box.h ← M1;
    FOR i: CARDINAL IN [0..l1.length) DO 
      IF l1[i] = ', THEN {comma ← i; EXIT};
      ENDLOOP;
    j ← comma + 1;
    WHILE j < l1.length AND l1[j] = Ascii.SP DO j ← j + 1; ENDLOOP;
    FOR i: CARDINAL IN [j..l1.length) DO 
      String.AppendChar[name, l1[i]];
      ENDLOOP;
    IF name.length # 0 AND name[name.length-1] # Ascii.SP THEN
      String.AppendChar[name, Ascii.SP];
    FOR i: CARDINAL IN [0..comma) DO 
      String.AppendChar[name, l1[i]];
      ENDLOOP;
    LJLine[s: name, box: @box, line: 0, of: p.last];
    FOR i: CARDINAL IN [1..p.last) DO
      LJLine[s: @p[p.lines[i]], box: @box, line: i, of: p.last];
      ENDLOOP;
    row ← row + 1;
    END;

  InitPressThings: PROC =
    BEGIN
    InitPressFileDescriptor[pfd, "Labels.press"];
    DigestFonts[];
    TextFont[];
    row ← col ← 0;
    END;

  buffer: Label;

  makePress, paginateList: BOOLEAN ← FALSE;

  DoIt: PROC =
    BEGIN
    c: CHARACTER;
    switches: STRING ← [20];
      BEGIN
      i: CARDINAL;
      GetSwitchNumber: PROC RETURNS [INTEGER] =
        BEGIN
	neg: BOOLEAN ← FALSE;
	n: CARDINAL ← 0;
	i ← i+1;
	IF switches[i] = '- THEN {neg ← TRUE; i ← i+1};
	WHILE switches[i] IN ['0..'9] DO
	  n ← n * 10 + (switches[i] - '0);
	  i ← i+1;
	  ENDLOOP;
	i ← i-1; -- since loop increments at bottom
	IF neg THEN RETURN [-INTEGER[n]] ELSE RETURN [n];
	END;
	
      in ← GetFile["input: "L, StreamDefs.Read, switches !
	SegmentDefs.FileNameError => GO TO cantFind];
      i ← 0;
      WHILE i < switches.length DO
	SELECT (c ← switches[i]) FROM
	  's, 'S => scaleFactor ← GetSwitchNumber[];
	  'y, 'Y => yDelta ← GetSwitchNumber[];
	  'p, 'P => outPointSize ← GetSwitchNumber[];
	  ENDCASE => GO TO badSwitch;
	i ← i+1;
	ENDLOOP;
      buffer ← Storage.Pages[3];
      InitPressThings[];
    -- go through list of names, calling PressLabel
      DO
	ReadLabel[];
	IF buffer.count = 0 THEN EXIT;
	PressLabel[buffer];
        ENDLOOP;
      in.destroy[in];
      WritePage[pfd];
      ClosePressFile[pfd];
      Storage.FreePages[buffer];
      IODefs.WriteLine["--done"L];
      EXITS
        cantFind => WriteLine[" file not found"L];
        badSwitch => WriteLine[" bad switch"L];
      END;
    END;

  SetUpCommands[];
  DoIt[];
  ImageDefs.StopMesa[];
  END.