-- file: FormatImpl.mesa; last edit:
  -- Mark: Apr 11, 1980 12:37 PM
  -- Evans: May 19, 1980 10:28 AM
  -- JGS: August 12, 1980  12:09 PM
  -- Karlton: July 24, 1980  8:41 AM
  -- Smokey: Aug 4, 1980 2:56 PM
  -- Forrest: October 25, 1980  8:32 PM
  
DIRECTORY
  Format USING [
    DateFormat, LongSubString, LongSubStringDescriptor, NumberFormat, StringProc],
  String USING [AppendLongNumber, AppendNumber, AppendChar, SubString],
  Time USING [Append, Packed, Unpack];
  
FormatImpl: PROGRAM IMPORTS String, Time EXPORTS Format =
  BEGIN OPEN Format, String;
  
-- Public procedures
  
  Char: PUBLIC PROCEDURE [c: CHARACTER, proc: StringProc] =
    BEGIN 
    s: STRING ← [2];
    s[0] ← c; s.length ← 1;
    proc[s]
    END;
    
  Date: PUBLIC PROCEDURE [
    pt: Time.Packed, format: DateFormat ← noSeconds, proc: StringProc] =
    BEGIN 
    s: STRING ← [22];
    Time.Append[s, Time.Unpack[pt], TRUE];
    s.length ← s.length-(SELECT format FROM
      dateOnly => 13, noSeconds => 7, dateTime => 4, ENDCASE => 0);
    proc[s]
    END;
    
  LongNumber: PUBLIC PROCEDURE [
    n: LONG UNSPECIFIED, format: NumberFormat, proc: StringProc] =
    BEGIN 
    s: STRING ← [34];
    neg: BOOLEAN;
    IF LOOPHOLE[n, LONG INTEGER] < 0 AND ~format.unsigned THEN
      {n ← -LOOPHOLE[n, LONG INTEGER]; neg ← TRUE}
    ELSE neg ← FALSE;
    AppendLongNumber[s, n, format.base];
    FormatNumber[s: s, format: format, neg: neg, proc: proc];
    END;
    
  LongOctal: PUBLIC PROCEDURE [n: LONG CARDINAL, proc: StringProc] =
    BEGIN 
    s: STRING ← [12];
    AppendLongNumber[s, n, 8];
    IF n > 7 THEN AppendChar[s, 'B];
    proc[s];
    END;
    
  LongString: PUBLIC PROCEDURE [s: LONG STRING, proc: StringProc] =
    BEGIN
    IF s#NIL THEN
      BEGIN
      lss: LongSubStringDescriptor ← [base: s, offset: 0, length: s.length];
      LongSubStringItem[@lss, proc];
      END;
    END;
    
  LongSubStringItem: PUBLIC PROCEDURE [ss: LongSubString, proc: StringProc] =
    BEGIN                                                -- Repeatedly calls proc with strings filled from ss
    s: STRING ← [100];
    sPos, ssPos: CARDINAL ← 0;
    IF ss = NIL OR ss.base = NIL OR ss.base.length = 0 THEN RETURN[];
    s.length ← 0;
    FOR ssPos IN [ss.offset..MIN[ss.base.length, ss.offset+ss.length]) DO
      IF sPos >= s.maxlength THEN {IF s.length # 0 THEN proc[s]; sPos ← 0; s.length ← 0};
      s[sPos] ← ss.base[ssPos];
      s.length ← s.length+1;
      sPos ← sPos+1;
      REPEAT
	FINISHED => IF s.length # 0 THEN proc[s];
      ENDLOOP;
    RETURN[];
    END;
    
  Number: PUBLIC PROCEDURE [n: UNSPECIFIED, format: NumberFormat, proc: StringProc] =
    BEGIN 
    s: STRING ← [18];
    neg: BOOLEAN;
    IF INTEGER[n] < 0 AND ~format.unsigned THEN {n ← -INTEGER[n]; neg ← TRUE}
    ELSE neg ← FALSE;
    AppendNumber[s, n, format.base];
    FormatNumber[s: s, format: format, neg: neg, proc: proc];
    END;
    
  Octal: PUBLIC PROCEDURE [n: CARDINAL, proc: StringProc] =
    BEGIN 
    s: STRING ← [10];
    AppendNumber[s, n, 8];
    IF n > 7 THEN AppendChar[s, 'B];
    proc[s];
    END;
    
  SubString: PUBLIC PROCEDURE [ss: String.SubString, proc: StringProc] =
    BEGIN
    IF ss#NIL THEN
      BEGIN
      lss: LongSubStringDescriptor ← [base: ss.base, offset: ss.offset, length: ss.length];
      LongSubStringItem[@lss, proc];
      END;
    END;
    
  
-- Private procedures
  
  FormatNumber: PROCEDURE [
    s: STRING, format: NumberFormat, neg: BOOLEAN, proc: StringProc] =
    BEGIN 
    l: CARDINAL;
    fill: STRING ←
      (IF format.zerofill THEN "00000000"L ELSE "        "L );
    l ← s.length + (IF neg THEN 1 ELSE 0);
    IF l < format.columns THEN
      BEGIN
      fillChars: CARDINAL ← format.columns - l;
      IF neg AND format.zerofill THEN Char['-, proc];
      THROUGH [0..fillChars/8) DO proc[fill]; ENDLOOP;
      IF (fill.length ← fillChars MOD 8) # 0 THEN proc[fill];
      IF neg AND ~format.zerofill THEN Char['-, proc];
      END
    ELSE
      IF neg THEN Char['-, proc];
    proc[s];
    END;
    
  
-- Mainline Code

  END.