home *** CD-ROM | disk | FTP | other *** search
/ CP/M / CPM_CDROM.iso / cpm / turbopas / decuf10.ark / DECUF.PAS < prev    next >
Pascal/Delphi Source File  |  1989-09-27  |  15KB  |  415 lines

  1. program DECode_Uuencoded_File ;
  2. {$A+}  { Generate NON-recursive code (shorter and faster) }
  3. {$C+}  { Do check for ^C and ^S during console output     }
  4. {$R-}  { Check all subrange variables to be within range  }
  5. {$U-}  { Do NOT check for ^C continuously                 }
  6. {$W2}  { Allow up to 2 simultanous active WITH statements }
  7. {* ====================================================================
  8.  *
  9.  * DecUF - Decode 'uuencoded' files.  The input file may contain one
  10.  *         or more 'uuencoded' files.
  11.  *
  12.  * Version 0.1 , 199101, original coding
  13.  *  Wim J.M. Nelis      (Nelis@NLR.NL)
  14.  *  Rozenhof 4
  15.  *  8316 CX  Marknesse
  16.  *  the Netherlands
  17.  * Version 0.2 , 199105, include extended file name unit.
  18.  *
  19.  * Note that this is a preliminary version: both parameters and error
  20.  * control still need to be implemented!
  21.  *}
  22. const
  23.    CurrentDrive     =     0  ;  { Indication for current drive in DOS calls }
  24.    DefaultExtension = '.UUE' ;  { Default extension of input file name }
  25.    MaxChunkSize     =   512  ;  { Max size of one chunk of converted data }
  26.  
  27. type
  28.    KeyWordString = string[16] ;  { A keyword, possibly a file name }
  29.    Message       = string[50] ;  { (Error) message }
  30.  
  31.    ChunkPtr   = ^Chunks ;
  32.    ChunkSizes = 0..MaxChunkSize ;
  33.    Chunks   = record
  34.                 NextChunk :   ChunkPtr ;  { Pointer to next chunk }
  35.                 ChunkSize : ChunkSizes ;  { Size of this chunk }
  36.                 Data : array[1..MaxChunkSize] of Byte ;
  37.               end ;
  38.  
  39.    DecodingStates   = ( Sextet0,   { Decode 4 sextets into 3 octets }
  40.                         Sextet1,
  41.                         Sextet2,
  42.                         Sextet3 ) ;
  43.  
  44.    LineFormats      = ( Unknown,   { Format is not known yet }
  45.                         Single ,   { Length only at start of line }
  46.                         Double ) ; { Length at start and end of line }
  47.  
  48.    ProcessingStates = ( Look_For_Start,   { Look for either TABLE or BEGIN }
  49.                         Reading_Table ,   { Read the conversion table }
  50.                         Reading_Begin ,   { Proces BEGIN statement }
  51.                         Reading_Data  ,   { Convert the next data line }
  52.                         Completed     ) ; { END encountered }
  53.  
  54.    ErrorTypes = ( Catastrophic,   { Error is fatal to program execution }
  55.                   Fatal       ,   { Error is fatal to file conversion }
  56.                   Warning     ,   { Error can optionally be ignored }
  57.                   Informative ) ; { Error is result of other error }
  58.  
  59.    LineErrors = ( IllegalCharacter,   { Control character skipped }
  60.                   Overflow        ,   { Line buffer too short }
  61.                   IllegalFormat   ,   { Length field cannot be decoded }
  62.                   WrongLength     ,   { Line length mismatch }
  63.                   WrongTerminator ) ; { Terminator is not 'M' }
  64.  
  65. {$IEXTFN.UNT}
  66.  
  67. var
  68. {* --- Input/Output files --- *}
  69.    InputFile       :            Text ;  { Input file }
  70.    InputFileName   :   FullFileNames ;  { Input file Name }
  71.    InputFileDesc   : FileDescriptors ;  { Input file name, internal format }
  72.    OutputFile      :            File ;  { Result file, may be any type }
  73.    OutputFilePath  :   FullFileNames ;  { Path to directory to receive result file }
  74.    OutputFileName  :   FullFileNames ;  { Full name of result file }
  75.    OutputFileDesc  : FileDescriptors ;  { Outputfile name, internal format }
  76.    OutputFileOpen  :         Boolean ;  { OutputFile is opened }
  77.  
  78. {* --- Option flags --- *}
  79.    OnlyOneFile     : Boolean ;  { Input file contains one encoded file }
  80.    StrictChecking  : Boolean ;  { Check every line in encoded file }
  81.    VerboseMode     : Boolean ;  { Give a progres report }
  82.  
  83.    DecodeTable     : array[' '..#255] of Byte ;  { Decoding table }
  84.    DecodeState     : DecodingStates ;
  85.  
  86.    HeadChunkList   : ChunkPtr ;  { Head of the list of chunks }
  87.    TailChunkList   : ChunkPtr ;  { Tail of the list of chunks }
  88.  
  89.    ProcessingState :  ProcessingStates ;  { Current state }
  90.    NextLine        :        string[80] ;  { Current line }
  91.    LineIndex       :             1..80 ;  { Index in NextLine }
  92.    LineFormat      :       LineFormats ;  { Format of encoded lines }
  93.    ErrorsInLine    : set of LineErrors ;  { Errors detected in current line }
  94.  
  95. {* --- Statistics per result file --- *}
  96.    BytesWritten      : Integer ;  { Number of bytes written }
  97.    CharactersRead    : Integer ;  { Number of characters read }
  98.    CharactersSkipped : Integer ;  { Number of characters skipped }
  99.    HeapFlushes       : Integer ;  { Number of times the heap is flushed }
  100.    LinesRead         : Integer ;  { Number of lines read }
  101.    LinesSkipped      : Integer ;  { Number of lines NOT decoded }
  102.    LinesWritten      : Integer ;  { Number of lines written }
  103.  
  104.  
  105. {*
  106.  * The functions Dec and Inc are included to obtain a little bit of
  107.  * compatibility with TP 5.0 and higher.
  108.  *}
  109.  
  110. procedure Dec( var SomeValue : Integer ) ;
  111. begin
  112.    SomeValue:= Pred( SomeValue ) ;
  113. end ;  { of Dec }
  114.  
  115. procedure Inc( var SomeValue : Integer ) ;
  116. begin
  117.    SomeValue:= Succ( SomeValue ) ;
  118. end ;  { of Inc }
  119.  
  120. {$IDECUF.IF0}
  121. {$IDECUF.IF1}
  122.  
  123. {* ----------------------------------------------------------------- *
  124.  *                                                                   *
  125.  *      S T A T E   P R O C E S S O R    P R O C E D U R E S         *
  126.  *                                                                   *
  127.  * ----------------------------------------------------------------- *}
  128.  
  129. procedure ProcesBegin ;
  130. {*
  131.  * ProcesBegin - Extract the result file name from the 'begin'
  132.  *               statement and open a file with that name.
  133.  *}
  134. const
  135.    PathDelimiter : array[0..2] of char = ':\/' ;
  136. var
  137.    KeyWord  : KeyWordString ;  { Keyword from line }
  138.    Mode     : KeyWordString ;  { Octal encoded permission flags }
  139.    FileName : KeyWordString ;  { Original file name }
  140.    I        :       Integer ;  { Loop control variable }
  141. begin
  142.    LineIndex:= 1 ;  { Preset index for interpretation of header }
  143.    Keyword:= NextWord ;
  144.    if KeyWord='begin' then
  145.     begin
  146.      Mode    := NextWord ;
  147.      FileName:= NextWord ;
  148.      if FileName<>'' then
  149.       begin
  150. {*
  151.  * Remove any path indication from the file name.  CP/M paths, MS-DOS
  152.  * paths as well as UNIX paths are removed from the supplied name.
  153.  *}
  154.        for I:= 0 to 2 do
  155.          while Pos( PathDelimiter[I], FileName ) <> 0 do
  156.            FileName:= Copy( Filename,
  157.                             Succ(Pos(PathDelimiter[I],FileName)), 255 ) ;
  158. {*
  159.  * Open the output file and swap to the Reading_Data state.  WARNING: The
  160.  * procedure OpenOutputFile might change the processing state as well, in
  161.  * case of an error. Thus the order of the 4 statements below is crucial!
  162.  *}
  163.        OutputFileName := OutputFilePath + FileName ;
  164.        ProcessingState:= Reading_Data ;
  165.        OpenOutputFile ;
  166.        if ProcessingState=Reading_Data then
  167.          WriteLn( 'Decoding to file ', OutputFileName ) ;
  168.       end
  169.      else  { there is no filename on begin statement }
  170.        Panic( Fatal, 'File name missing in begin statement' ) ;
  171.     end
  172.    else  { the line is not a begin statement }
  173.      Panic( Fatal, 'Expecting a begin statement' ) ;
  174. end ;  { of ProcesBegin }
  175.  
  176. procedure CheckForStart ;
  177. {*
  178.  * CheckForStart - Check the line read for the keywords TABLE or BEGIN,
  179.  *                 which indicate the start of the encoded file.
  180.  *}
  181. var
  182.    KeyWord : KeyWordString ;  { Keyword from line }
  183. begin
  184.    if ErrorsInLine=[] then
  185.      if Length(NextLine)>=5 then
  186.       begin
  187.        KeyWord:= Copy( NextLine, 1, 5 ) ;
  188.        if Keyword='table' then
  189.          ProcessingState:= Reading_Table
  190.        else
  191.          if KeyWord='begin' then
  192.            ProcesBegin ;
  193.       end ;  { of if/if }
  194. end ;  { of CheckForStart }
  195.  
  196. procedure ReadConversionTable ;
  197. {*
  198.  * ReadConversionTable - Read the conversion table from the input file
  199.  *                       and save it in the DecodeTable.
  200.  *}
  201. var
  202.    EntryCount : Integer ;  { Number of entries in conversion table }
  203.    I0         :    Char ;  { Loop control variable }
  204.    I1         : Integer ;  { Loop control variable }
  205. begin
  206.    for I0:=' ' to Chr(255) do   { Preset conversion table .. }
  207.      DecodeTable[I0]:= $FF ;    { with invalid entriers }
  208.  
  209.    if (ErrorsInLine<>[]) or (Length(NextLine)<>32) then
  210.     begin
  211.      Panic( Fatal, 'Illegal conversion table' ) ;
  212.      Exit ;
  213.     end ;  { of if }
  214.    for I1:= 1 to 32 do
  215.      DecodeTable[NextLine[I1]]:= Pred(I1) ;
  216.  
  217.    ReadNextLine ;
  218.    if (ErrorsInLine<>[]) or (Length(NextLine)<>32) then
  219.     begin
  220.      Panic( Fatal, 'Illegal conversion table' ) ;
  221.      Exit ;
  222.     end ;  { of if }
  223.    for I1:= 1 to 32 do
  224.      DecodeTable[NextLine[I1]]:= Pred(I1) + 32 ;
  225.  
  226.    EntryCount:= 0 ;
  227.    for I0:= ' ' to Chr(255) do
  228.      if DecodeTable[I0]<>$FF then
  229.        Inc( EntryCount ) ;
  230.    if EntryCount<>64 then
  231.     begin
  232.      Panic( Fatal, 'Ambiguous conversion table' ) ;
  233.      Exit ;
  234.     end ;  { of if }
  235.  
  236.    ProcessingState:= Reading_Begin ;
  237. end ;  { of ReadConversionTable }
  238.  
  239. procedure ProcesData ;
  240. {*
  241.  * ProcesData - Decode the (encoded) line read if the end of input
  242.  *              is not encountered.  The end of input is signalled
  243.  *              by the END statement.
  244.  *}
  245. var
  246.    ExpectedLength : Integer ;  { Length encoded in line image }
  247.    DeltaLength    : Integer ;  { Expected minus actual length }
  248.    Byte1          :    Byte ;  { Decode bytes }
  249.    Byte2          :    Byte ;
  250.    Byte3          :    Byte ;
  251.    I              : Integer ;  { Loop control variable }
  252.  
  253.  procedure DecodeCharacter( NextChar : Char ) ;
  254.  {*
  255.   * DecodeCharacter - Decode one character.
  256.   *}
  257.  var
  258.     Sextet : Byte ;  { One decoded character }
  259.  begin
  260.     Sextet:= DecodeTable[NextChar] ;
  261.     if Sextet<>$FF then
  262.       case DecodeState of
  263.         Sextet0 : begin
  264.                     Byte1:= Sextet shl 2 ;
  265.                     DecodeState:= Sextet1 ;
  266.                   end ;  { of case 0 }
  267.         Sextet1 : begin
  268.                     Byte1:= Byte1 + ( (Sextet shr 4) and $03 ) ;
  269.                     Byte2:=           (Sextet shl 4) and $F0   ;
  270.                     WriteByteToChunk( Byte1 ) ;
  271.                     DecodeState:= Sextet2 ;
  272.                   end ;  { of case 1 }
  273.         Sextet2 : begin
  274.                     Byte2:= Byte2 + (Sextet          shr 2) ;
  275.                     Byte3:=         (Sextet and $03) shl 6  ;
  276.                     WriteByteToChunk( Byte2 ) ;
  277.                     DecodeState:= Sextet3 ;
  278.                   end ;  { of case 2 }
  279.         Sextet3 : begin
  280.                     Byte3:= Byte3 + Sextet ;
  281.                     WriteByteToChunk( Byte3 ) ;
  282.                     DecodeState:= Sextet0 ;
  283.                   end ;  { of case 3 }
  284.       end   { of cases }
  285.     else  { character cannot be decoded }
  286.       ErrorsInLine:= ErrorsInLine + [IllegalCharacter] ;
  287.  end ;  { of DecodeCharacter }
  288.  
  289. begin
  290. {*
  291.  * If in verbose mode, give an indication of the progres.
  292.  *}
  293.    if VerboseMode then
  294.      if (LinesRead and $000F)=0 then
  295.        Write( ^M'Line ', LinesRead ) ;
  296. {*
  297.  * Check if the current line contains the statement 'end'.  If so,
  298.  * swap to the processingstate Completed.
  299.  *}
  300.    if Length(NextLine)=3 then
  301.      if NextLine='end' then
  302.       begin
  303.        FileEpilogue ;  { Finish processing current file }
  304.        FilePrologue ;  { Setup to convert next file }
  305.        ProcessingState:= Completed ;
  306.        Exit ;
  307.       end ;  { of if }
  308.  
  309. {*
  310.  * Extract the length of the line from the line itself: it is encoded
  311.  * in the first character of the line.  This length should match the
  312.  * actual length of the line.
  313.  *}
  314.    if ErrorsInLine=[] then
  315.     begin
  316.      ExpectedLength:= (DecodeTable[NextLine[1]]*4) div 3 ;
  317.      if (ExpectedLength>=0) then
  318.       begin
  319.        DeltaLength:= Length(NextLine) - ExpectedLength ;
  320.        case LineFormat of
  321.          Unknown : case DeltaLength of
  322.                      1 : begin
  323.                           LineFormat:= Single ;
  324.                           NextLine:= Copy( NextLine, 2, ExpectedLength ) ;
  325.                          end ;  { of case 1 }
  326.                      2 : begin
  327.                           LineFormat:= Double ;
  328.                           NextLine:= Copy( NextLine, 2, ExpectedLength ) ;
  329.                          end ;  { of case 2 }
  330.                    else
  331.                      ErrorsInLine:= ErrorsInLine + [WrongLength] ;
  332.                    end ;  { of case Unknown }
  333.          Single  : if DeltaLength=1 then
  334.                      NextLine:= Copy( NextLine, 2, ExpectedLength )
  335.                    else
  336.                      ErrorsInLine:= ErrorsInLine + [WrongLength] ;
  337.          Double  : if DeltaLength<>2 then
  338.                      ErrorsInLine:= ErrorsInLine + [WrongLength]
  339.                    else
  340.                      if NextLine[Length(NextLine)]<>'M' then
  341.                        ErrorsInLine:= ErrorsInLine + [WrongTerminator]
  342.                      else
  343.                        NextLine:= Copy( NextLine, 2, ExpectedLength ) ;
  344.        end ;  { of case }
  345.       end
  346.      else  { the length field cannot be decoded }
  347.        ErrorsInLine:= ErrorsInLine + [IllegalFormat] ;
  348.    end ;  { of if }
  349.  
  350. {*
  351.  * If no errors are detected, decode the line.  If any errors are found,
  352.  * the reaction will depend on the checking mode: strict or relaxed.  In
  353.  * the latter case the line and the error(s) are ignored.
  354.  *}
  355.    if ErrorsInLine=[] then
  356.     begin
  357.      I:= 0 ;
  358.      while (ErrorsInLine=[]) and (I<Length(NextLine)) do
  359.       begin
  360.        I:= Succ(I) ;
  361.        DecodeCharacter( NextLine[I] ) ;
  362.        if ErrorsInLine<>[] then
  363.          Panic( Fatal, 'Illegal character : '+NextLine[I] ) ;
  364.       end ;  { of while }
  365.     end
  366.    else
  367.     begin
  368.      Inc( LinesSkipped ) ;
  369.      Panic( Warning, 'Illegal format of line' ) ;
  370.     end ;  { of if }
  371. end ;  { of ProcesData }
  372.  
  373.  
  374. {*
  375.  * Start of main program.
  376.  *}
  377. var
  378.    Done : Boolean ;  { Conversion of file completed }
  379. begin
  380.    InitFileNameUnit ;
  381.    ProgramPrologue ;
  382.  
  383.    OpenInputFile ;
  384.    ProcessingState:= Look_For_Start ;
  385.    Done           :=          False ;
  386.    while not (Eof(InputFile) or Done) do
  387.     begin
  388.      ReadNextLine ;
  389.      case ProcessingState of
  390.        Look_For_Start : CheckForStart ;
  391.        Reading_Table  : ReadConversionTable ;
  392.        Reading_Begin  : ProcesBegin ;
  393.        Reading_Data   : ProcesData ;
  394.        Completed      : if OnlyOneFile then
  395.                           Done:= True
  396.                         else
  397.                           CheckForStart ;
  398.      else
  399.        Panic( Fatal, 'Unexpected processing state' ) ;
  400.      end ;  { of case }
  401.     end ; { of while }
  402.  
  403.    case ProcessingState of
  404.       Look_For_Start  : Panic( Informative,
  405.                         'No valid encoded data found on '+InputFileName ) ;
  406.       Reading_Table   ,
  407.       Reading_Begin   ,
  408.       Reading_Data    : Panic( Informative,
  409.                         'Unexpected end-of-file found on'+InputFileName ) ;
  410.    end ;  { of cases }
  411.  
  412.    ProgramEpilogue ;
  413.    UnInitFileNameUnit ;
  414. end.
  415.