home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 18 REXX / 18-REXX.zip / rxdecode.zip / rxDecode.cmd next >
OS/2 REXX Batch file  |  1993-11-13  |  33KB  |  867 lines

  1. /* 
  2. program: RxDecode.cmd
  3. type:    REXXSAA-OS/2, 32bit OS/2 !
  4. purpose: harvest all XX- and UU-chunks, reorder them, reassamble them to the original, provide
  5.          for the translation table, if it is missing (thereby allowing decoding with both,
  6.          XXDECODE and UUDECODE), strip mail-headers, -trailers, comments, shar-leadins,
  7.          shar-ends etc. and finally have
  8.          them decoded. After successful decoding the chunks- and work-files will be deleted, if 
  9.          an error occurs, they remain untouched. It takes care of the newer UU-codetables which
  10.          replace the blank-character with the `-character (unfortunately some use both characters
  11.          in the coded file, so `-characters are translated into blank-characters).
  12.  
  13. version: 2.3
  14. date:    1992-02-04
  15. changed: 1992-06-05, RGF, load all RexxUtils, if not loaded
  16.          1993-09-08, RGF, added additional logic to find first valid encoded data and
  17.                           last valid encoded data, check leadin of first line in order
  18.                           to determine encoding set, if no translation-table was
  19.                           provided
  20.          1993-09-20, changed the definition of ANSI-color-sequences; gets them from
  21.                           procedure ScrColor.CMD
  22.          1993-11-06, allow more than one encoded part within a file, added more colors
  23.          1993-11-13, took care of cases, in which lines coincidentally look like encoded 
  24.                           ones, right before the truely encoded body;
  25.                           if RxDecode has to supply the UU-translation table, do a translation
  26.                           of ` to blanks by default 
  27.  
  28. needs:   SysFileTree(), SysTempFileName() loaded (in 32bit OS/2 available),
  29.          SCRCOLOR.CMD
  30.  
  31. usage:   RXDECODE [X|U] [/B]
  32.          ... decodes all XX- and UU-encoded files in present directory
  33.  
  34.              X ... use XXDECODE only to process UU- and XX-encoded files
  35.              U ... use UUDECODE only to process UU- and XX-encoded files
  36.  
  37.  
  38.          possible formats: 
  39.              chunks-order in file extension: 
  40.                  foo.uu1 foo.uu2 foo.uu3 foo.uu4 foo.uu5 foo.uu6  
  41.                  foo.uu7 foo.uu8 foo.uu9 foo.u10 foo.u11 foo.u12  
  42.          
  43.              chunks-order in file-body:
  44.                  foo1.uue foo2.uue foo3.uue foo4.uue foo5.uue foo6.uue     
  45.                  foo7.uue foo8.uue foo9.uue foo10.uue foo11.uue foo12.uue  
  46.          
  47.              normal encoded file:
  48.                  foo.xxe
  49.  
  50. author:  Rony G. Flatscher,
  51.          Wirtschaftsuniversitaet/Vienna
  52.          RONY@AWIWUW11.BITNET
  53.          rony@wu-wien.ac.at
  54.  
  55. All rights reserved, copyrighted 1992, 1993, no guarantee that it works without
  56. errors, etc. etc.
  57.  
  58. donated to the public domain granted that you are not charging anything
  59. (money etc.) for it and derivates based upon it, as you did not write it,
  60. etc. if that holds you may bundle it with commercial programs too
  61.  
  62. Please, if you find an error, post me a message describing it, I will
  63. try to fix and rerelease it to the net.
  64.  
  65.         RxDecode.cmd: catenate XX- and UU-encoded files and execute UUDECODE or XXDECODE
  66. */
  67.  
  68.  
  69. SIGNAL ON HALT  /* if user presses CTL-C */
  70.  
  71. /* check whether RxFuncs are loaded, if not, load them */
  72. IF RxFuncQuery('SysLoadFuncs') THEN
  73. DO
  74.     /* load the load-function */
  75.     CALL RxFuncAdd 'SysLoadFuncs', 'RexxUtil', 'SysLoadFuncs'       
  76.  
  77.     /* load the Sys* utilities */
  78.     CALL SysLoadFuncs                                                 
  79. END
  80.  
  81.  
  82. /* UUencoded files */
  83. file.  = ""                     /* default is empty */
  84. file.1 = "*.u*"                 /* uu-encoded files */
  85. file.1.ext  = "UUE"
  86. file.1.text = "processing" file.1.ext"-encoded files..."
  87. file.1.pgm  = "UUDECODE"        /* decoding program */
  88. file.2 = "*.x*"                 /* xx-encoded files */
  89. file.2.ext  = "XXE"
  90. file.2.text = "processing" file.2.ext"-encoded files..."
  91. file.2.pgm  = "XXDECODE"        /* decoding program */
  92.  
  93. g. = ""                         /* default for g.-elements (global-array) is empty string */
  94.  
  95. PARSE UPPER ARG argument 
  96.  
  97. IF POS("/B", argument) = 0 THEN /* get screen-colors */
  98.    PARSE VALUE ScrColor() WITH g.eScrNorm g.eScrInv     g.eTxtNorm    g.eTxtInf   g.eTxtHi,
  99.                                g.eTxtAla  g.eTxtNormInv g.eTxtInfInv  g.eTxtHiInv g.eTxtAlaInv .
  100. ELSE                            /* remove "/B" from argument */
  101. DO
  102.    PARSE VAR argument left "/B" right
  103.    argument = left right           
  104. END
  105.  
  106. IF argument <> "" THEN
  107. DO
  108.  
  109.    IF      POS("X", argument) > 0 THEN file.1.pgm = "XXDECODE"  /* use XXDECODE for both, XX- & UU-encoded files */
  110.    ELSE IF POS("U", argument) > 0 THEN file.2.pgm = "UUDECODE"  /* use UUDECODE for both, XX- & UU-encoded files */
  111.    ELSE SIGNAL usage
  112. END
  113.  
  114.  
  115.  
  116. files_to_delete.0 = 0           /* initialize stem-count to 0 */
  117.  
  118. DO i = 1 TO 2 
  119.    CALL say_c COPIES("=", 79)
  120.    CALL say_c g.eTxtHi || file.i.text
  121.    CALL say_c
  122.    CALL SysFileTree file.i, "filestmp", "FO"       /* get fully qualified filenames */
  123.    CALL say_c "   " || g.eTxtHi || filestmp.0 || g.eTxtInf "file(s) found"
  124.  
  125.    IF filestmp.0 = 0 THEN       /* if no files were found iterate */
  126.    DO
  127.       DROP filestmp.
  128.       ITERATE
  129.    END
  130.  
  131.    CALL say_c
  132.    CALL sort_procedure          /* sort files according to ASCII-table */
  133.    CALL reorder                 /* reorder those files which consist of more than 9 chunks, 
  134.                                    because ASCII-sort sorts numbers before letters ! */
  135.  
  136.    IF (filestmp.0 - files.0) <> 0 THEN
  137.    DO
  138.       CALL say_c g.eTxtAla || "  ("filestmp.0 - files.0 "file(s) cannot be processed.)"
  139.       CALL say_c
  140.    END
  141.  
  142.    DROP filestmp.               /* not needed anymore */
  143.    CALL say_c "   " || g.eTxtHi || files.0 || g.eTxtInf "file(s) is (are) being processed..."
  144.    /* catenate and decode files */
  145.    g.eTempFile = ""        /* temporary file to hold concatenated chunks */
  146.    g.eTargetFile = ""      /* name of target to build */
  147.    g.eTranslate = 0        /* for UUencoded files only: if the UUencoded file does not have a translation
  148.                            table, it will be provided; if it is encoded with a newer UUencode-program
  149.                            there will be `-characters instead of blank-characters; unfortunately some
  150.                            UUencode programs will use both characters in the same file; in order for
  151.                            XXdecode or older UUdecode programs to work correctly, this program will 
  152.                            use the original UU-translation table with blank-characters instead of
  153.                            the newer `-characters */
  154.  
  155.    g.eLinesWritten = 0
  156.    DO j = 1 TO files.0          /* process files */
  157.       g.eLinesWritten = g.eLinesWritten + assemble(files.j, file.i.ext)
  158.  
  159.       IF files.j.iChange = "LAST" | j = files.0 THEN   /* last chunks, decode temporary file */
  160.       DO
  161.          IF g.eLinesWritten > 0 THEN
  162.          DO
  163.             CALL decode
  164.          END
  165.          ELSE IF g.eTmpFile <> "" THEN  /* if in error, tmpFile could have been deleted already */
  166.          DO
  167.             CALL error g.eTmpFile": no lines to decode found !", "DELETE"
  168.          END
  169.  
  170.          g.eTranslate = 0
  171.          g.eLinesWritten = 0
  172.          g.eLeadin = ""
  173.       END
  174.    END 
  175. END
  176.  
  177. EXIT
  178.  
  179.  
  180.  
  181. /*********************************** reordering --begin -- ********************************/
  182. /*
  183.         This procedure takes care of the correct ordering of the encoded file-chunks, after  
  184.         the ASCII-sort, which may yield the following results:
  185.  
  186.             foo.u10 foo.u11 foo.u12 foo.uu1 foo.uu2 foo.uu3 
  187.             foo.uu4 foo.uu5 foo.uu6 foo.uu7 foo.uu8 foo.uu9 
  188.             foo.uue 
  189.             foo10.uue foo11.uue foo12.uue foo1.uue foo2.uue foo3.uue
  190.             foo4.uue  foo5.uue  foo6.uue  foo7.uue foo8.uue foo9.uue
  191.  
  192.         the correct ordering, so that the encoded chunks are assembled in the correct order, should be:
  193.  
  194.             foo.uu1 foo.uu2 foo.uu3 foo.uu4 foo.uu5 foo.uu6 
  195.             foo.uu7 foo.uu8 foo.uu9 foo.u10 foo.u11 foo.u12 
  196.             foo.uue 
  197.             foo1.uue foo2.uue foo3.uue foo4.uue foo5.uue foo6.uue  
  198.             foo7.uue foo8.uue foo9.uue foo10.uue foo11.uue foo12.uue 
  199. */
  200. REORDER: PROCEDURE EXPOSE files. filestmp. g.
  201.     tmp     = ''
  202.     files.    = ""      /* set default to ""                         */
  203.     work.    = ""       /* set default to "" for unassigned elements */
  204.  
  205.     chunks    = 0        /* reset chunks-counter */
  206.     highest  = 0        /* set the highest serial number to 0 */
  207.     dirty    = 0        /* clear dirty-flag */
  208.     base     = 1        /* start-index for next set of chunks */
  209.     first    = 1        /* starting out  */
  210.     last_state = 0      /* last state    */
  211.     last_body_name = "" /* last name of body of filename */
  212.  
  213.     DO i = 1 TO filestmp.0
  214.        name = FILESPEC("NAME", filestmp.i)         /* have filename extracted */
  215.        pos = LASTPOS(".", name)                 /* get last dot in filename */
  216.        name_body = SUBSTR(name, 1, pos - 1)     /* get stem-name of file, without dot */
  217.        name_ext = SUBSTR(name, pos + 1)         /* get extension */
  218.        serial = ""
  219.        ext_length  = LENGTH(name_ext)
  220.        body_length = LENGTH(name_body)
  221.  
  222.        IF DATATYPE(SUBSTR(name_ext, ext_length, 1), "N") THEN           
  223.                   filetype = 1          /* number in extension, chunksed */
  224.        ELSE IF DATATYPE(SUBSTR(name_body, body_length, 1), "N") THEN    
  225.                   filetype = 2          /* number in body, chunks expected  */
  226.        ELSE filetype = 3                /* no number found, chunks is entire file */                                        
  227.  
  228.        IF filetype = 1 THEN     /* files come in like foo.uu1, foo.uu2, foo.xx1, foo.xx2, etc. */
  229.        DO
  230.           DO j = ext_length TO 1 BY -1 FOR 3      /* the last three digits at a maximum */
  231.              char = SUBSTR(name_ext, j, 1)
  232.  
  233.              IF \DATATYPE(char, "N") THEN
  234.                 LEAVE j
  235.  
  236.              serial = char || serial    /* build sequence number */
  237.           END 
  238.                                         
  239.           tmp = name_body
  240.        END
  241.        ELSE IF filetype = 2 THEN /* files come in like foo1.uue, foo2.uue, foo1.xxe, foo2.xxe, etc. */
  242.        DO
  243.           DO j = body_length TO 1 BY -1 FOR 3      /* use the last three digits */
  244.              char = SUBSTR(name_body, j, 1)
  245.  
  246.              IF \DATATYPE(char, "N") THEN
  247.                 LEAVE j
  248.  
  249.              serial = char || serial        /* build sequence number */
  250.           END 
  251.  
  252.           tmp = SUBSTR(name_body, 1, body_length - LENGTH(serial))
  253.        END
  254.        ELSE tmp = name_body     /* encoded file is not chunksed */
  255.  
  256.        IF first THEN    /* first time in loop ? */
  257.        DO
  258.           last_state = filetype
  259.           last_body_name = tmp
  260.           first = 0     /* no need to get into this IF-statement anymore */
  261.        END
  262.  
  263.        /* did the state or the body of the filename change ? */
  264.        IF last_state <> filetype | last_body_name <> tmp THEN
  265.        DO
  266.           CALL set_up_this_series_of_chunks     /* save present intermediate work-files */
  267.           last_state = filetype
  268.           last_body_name = tmp
  269.        END
  270.  
  271.        CALL populate_work_array         /* memorize present chunks with the needed position in array */
  272.  
  273.     END
  274.  
  275.     IF dirty THEN CALL set_up_this_series_of_chunks     /* one set to process left */
  276.  
  277.     files.0 = base - 1    /* set number of elements in array */
  278.  
  279. /*
  280. /* debug */
  281. say ""
  282. say "Result: files.0="files.0
  283. say
  284.  
  285. DO i = 1 TO files.0
  286.    CALL say_c files.i "|" files.i.iChange "|"
  287. END
  288. */
  289.  
  290.     RETURN
  291.  
  292.  
  293.  
  294.  
  295. POPULATE_WORK_ARRAY:
  296.     chunks = chunks + 1                   /* process present chunks */
  297.     dirty = 1                           /* chunks pending     */
  298.  
  299.     work.chunks.iFilename = filestmp.i   /* save fully qualified filename */
  300.     work.chunks.iFiletype  = last_state  /* save filetype */
  301.  
  302.     IF serial = '' THEN serial = 1
  303.     work.chunks.iPosition = serial       /* serial number */          
  304.     highest = MAX(highest, serial)      /* get highest serial number, so one knows of how many pieces 
  305.                                            the file consists of */
  306.     RETURN
  307.  
  308.  
  309.  
  310.  
  311. SET_UP_THIS_SERIES_OF_CHUNKS:
  312.     /* if more than one chunk, the highest serial number must be the same as 
  313.        the number of total chunks, otherwise some pieces are missing */
  314.     error = (chunks > 1 & chunks <> highest)
  315.  
  316. /*
  317.     error = 0                           /* default to no error */
  318.     /* if chunks expected, there must be more than one */
  319.     IF work.1.iFiletype = '1' | work.1.iFiletype = '2' THEN
  320.     DO
  321.        error = error | (chunks = 1)     /* several chunks expected, only one received */
  322.     END
  323. */
  324.  
  325. /*
  326. say "debug:  filetype ["work.1.iFiletype"], chunks ["chunks"], highest["highest"] ---> error ["error"]"
  327. */
  328.  
  329.     IF error THEN
  330.     DO
  331.        CALL BEEP 500, 100
  332.        max_length = MAX(LENGTH(chunks), LENGTH(highest))
  333.        CALL say_c
  334.        CALL say_c "PROBLEM:"
  335.        CALL say_c
  336.        CALL say_c "      according to the highest serial number availabe, there must be"
  337.        CALL say_c "      >>>" g.eTxtAla || RIGHT(highest, max_length) g.eTxtInf || "<<< encoded files to be merged, but there could be only "
  338.        CALL say_c "      >>>" g.eTxtAla || RIGHT(chunks, max_length) g.eTxtInf || "<<< file(s) found !"
  339.        CALL say_c 
  340.        CALL say_c "      This is a list of the available pieces among which some are missing:"
  341.        CALL say_c 
  342.        DO q = 1 TO chunks
  343.           CALL say_c "         " g.eTxtAla || work.q.iFilename
  344.        END
  345.        CALL say_c 
  346.     END
  347.     ELSE        /* insert chunks in the correct sequence into the files-array */
  348.     DO
  349.        DO j = 1 TO chunks 
  350.           IF chunks = 1 THEN nr1 = base
  351.                        ELSE nr1 = base + work.j.iPosition - 1
  352.  
  353.           files.nr1 = work.j.iFilename
  354.        END
  355.        nr1 = base + chunks - 1           /* calculate next starting index into array */
  356.        files.nr1.iChange = "LAST"        /* indicate that last chunks was processed */
  357.        base = base + chunks              /* new index for rest of files */
  358.     END
  359.   
  360.     chunks    = 0        /* reset chunks-counter */
  361.     DROP work.          /* DROP work-array */
  362.     work.    = ""       /* set default to "" for unassigned elements */
  363.     highest  = 0        /* set the highest serial number to 0 */
  364.     dirty    = 0        /* clear dirty-flag */
  365.     RETURN
  366.  
  367.  
  368.  
  369. /************************************* reordering --end-- ********************************/
  370.  
  371.  
  372.  
  373.  
  374.  
  375.  
  376.  
  377. /* do the decoding stuff */
  378. DECODE: 
  379. /*
  380. call debug "total of lines written:", g.eLinesWritten
  381. */
  382.     IF g.eTargetFile = "" THEN 
  383.     DO
  384.        IF g.eLinesWritten <> 0 THEN
  385.           CALL error g.eTempFile": no target-filename found !", "DELETE"
  386.     END
  387.     ELSE
  388.     DO
  389.        CALL say_c "      decoding:" g.eTempFile || g.eTxtInf "====>" g.eTxtHi || g.eTargetFile || g.eTxtInf "("file.i.pgm")"
  390.        ADDRESS CMD "@"file.i.pgm '"'g.eTempFile'"' '"'g.eTargetFile'" >NUL'        /* decode file */
  391.  
  392.        IF rc <> 0 THEN 
  393.        DO
  394.           CALL BEEP 1500, 1000
  395.           CALL say_c g.eTxtAla || "           return code:" rc "--- something went wrong while decoding!"
  396.           CALL say_c g.eTxtAla || "look up temporary file:" || g.eTxtAla g.eTempFile
  397.           ADDRESS CMD '@DEL "'g.eTargetFile'"'    /* delete uncompleted target file */
  398.        END
  399.        ELSE                /* everything went fine, delete files */
  400.        DO
  401.           CALL say_c "                done."
  402.           ADDRESS CMD "@DEL" '"'g.eTempFile'"' 
  403.           DO z = 1 to files_to_delete.0
  404.              ADDRESS CMD "@DEL" '"'files_to_delete.z'"'
  405.           END
  406.    
  407.           CALL BEEP 150, 100
  408.        END
  409.     END
  410.  
  411.     CALL say_c
  412.  
  413.     g.eTargetFile = ""            /* name of target to build */
  414.     g.eTempFile = ""              /* temporary file to hold concatenated chunks */
  415.  
  416.     DROP files_to_delete.       
  417.     files_to_delete.0 = 0
  418.  
  419.     RETURN
  420.  
  421.  
  422.  
  423.  
  424.  
  425.  
  426. /* read the XX- or UU-encoded file and build a new, clean working file */
  427. ASSEMBLE: PROCEDURE EXPOSE g.eTargetFile g.eTempFile files_to_delete. g.eTranslate table_00x_char g.
  428.     workfile = ARG(1)
  429.     ext      = ARG(2) 
  430.  
  431.     IF g.eLeadin = "" THEN
  432.     DO
  433.       IF ext = "UUE" THEN g.eLeadin = "M"    /* normal leadin for UUencoded lines */
  434.                      ELSE g.eLeadin = "h"    /* normal leadin for XXencoded lines */
  435.     END
  436.  
  437.     /* get name of target file */
  438.     CALL STREAM workfile, 'C', 'OPEN READ'  /* open file for input */
  439.  
  440.     first = (g.eTempFile = "")
  441.     IF first THEN                       /* no g.eTempFile as of yet, therefore first chunks */
  442.     DO
  443.        tmp = FILESPEC("NAME", workfile)
  444.  
  445.        /* create unique filename, use first 1 letter to be appended after "tmp" */
  446.        tmp =  "tmp" || SUBSTR(tmp, 1, 1) || "????." || ext
  447.  
  448.        g.eTempFile = SysTempFileName(tmp)
  449.        CALL say_c
  450.        CALL say_c "temporary file:" || g.eTxtHi g.eTempFile
  451.     END
  452.  
  453.     CALL say_c "    processing:" g.eTxtHi || FILESPEC("NAME", workfile) || g.eTxtInf "====>" g.eTempFile
  454.     tmp = files_to_delete.0 + 1        /* increase index */
  455.     files_to_delete.tmp = workfile     /* insert this file to be deleted, if everything works o.k. */
  456.     files_to_delete.0   = tmp          /* update index-counter */
  457.  
  458.  
  459.     CALL STREAM g.eTempFile, "C", "OPEN WRITE"     /* open output file */
  460.  
  461.     table = 0           /* translation table written ? */
  462.     begin = 0           /* begin-line with filename written ? */
  463.     valid_encoded_data = 0           /* writing encoded body */
  464.     i     = 0           /* number of lines written */
  465.  
  466.     DO WHILE LINES(workfile) > 0        /* as long as lines are left to be read */
  467.        line = LINEIN(workfile)          /* read line */
  468.  
  469.        IF g.eTranslate THEN                /* g.eTranslate "`" into blank (" ") */
  470.           line = TRANSLATE(line, " ", "`")
  471.  
  472.        IF line = "" THEN 
  473.        DO
  474.           IF \valid_encoded_data THEN ITERATE /* skip empty lines from the top */
  475.           ELSE 
  476.           DO            /* sometimes there is an empty line before the very last line */
  477.              tmp = LINEIN(workfile)     /* get next line */
  478.              IF tmp = "end" THEN
  479.              DO
  480.                 /* indicate by the means of the table-code that no values 
  481.                    are to be expected (empty line right before the end-line */
  482.                 CALL LINEOUT g.eTempFile, table_00x_char || "M"
  483.                 CALL LINEOUT g.eTempFile, tmp
  484.                 i = i + 2
  485.              END
  486.  
  487.              valid_encoded_data = 0     /* look for another valid chunk within file */
  488.              ITERATE
  489. /*
  490.              LEAVE                    /* empty line at the end, leave  */
  491. */
  492.           END
  493.        END
  494.  
  495.        IF first THEN                    /* first chunks ? */
  496.        DO
  497.           IF POS("BEGIN", line) > 0 THEN ITERATE        /* e.g. BEGIN ---cut here--- , "sed .../^BEGIN..." */
  498.  
  499.           IF \table THEN                /* table in encoded file ? */   
  500.           DO
  501.              IF line = "table" THEN     /* table line found */
  502.              DO
  503.                 CALL LINEOUT g.eTempFile, line
  504.                 i = i + 1
  505.                 table = 1               /* table found */
  506.  
  507.  
  508.                 line = LINEIN(workfile)         /* get first table-line */
  509.                 table_00x_char = LEFT(line, 1)  /* get code for x00 */
  510.                 CALL LINEOUT g.eTempFile, line
  511.                 i = i + 1
  512.                 g.eDecodeTable = line
  513.  
  514.                 /* get & write second table-line */
  515.                 line = LINEIN(workfile)       
  516.                 CALL LINEOUT g.eTempFile, line
  517.                 i = i + 1
  518.                 g.eDecodeTable = g.eDecodeTable || line
  519.  
  520.                 ITERATE
  521.              END
  522.           END
  523.  
  524.           /* find first valid encoded data */
  525.           IF \begin THEN
  526.           DO
  527.              PARSE VAR line "begin" mode g.eTargetFile .
  528.              IF \table & \DATATYPE(mode, "N") THEN ITERATE    /* leadin e.g. "begin ---cut here --" */
  529.  
  530.              IF \table THEN         /* no table information, supply defaults */
  531.              DO
  532.  
  533.                 /* check whether UU- (leadin 'M') or XX-encoded (leadin 'h') data are in the file */
  534.                 save_pos = STREAM(workfile, "C", "SEEK +0")  /* get present read/write position in workfile */
  535.                 g.eLeadin = LEFT(LINEIN(workfile),1)       /* read first char of next line */
  536.  
  537.                 IF g.eLeadin = 'h' THEN       /* XXencoded (original translation table) */
  538.                 DO
  539.                    /* XX-translation table */
  540.                    tmpCode1 = "+-0123456789ABCDEFGHIJKLMNOPQRST"
  541.                    tmpCode2 = "UVWXYZabcdefghijklmnopqrstuvwxyz"
  542.                    tmpCoding = "XX"
  543.                 END
  544.                 ELSE                 /* assume UUencoded (translation table) */
  545.                 DO
  546. /* 
  547. removed, imply translation all the time, if RxDecode has to supply the translation table
  548.  
  549.                    /* check for `-char in encoded file for older UUDECODE-programs */
  550.                    g.eTranslate = original_uue_table()  
  551. */
  552.                    g.eTranslate = 1             /* translate ` to blanks in any case,
  553.                                                    so older UUDECODErs and XXDECODErs
  554.                                                    can handle the encoded files */
  555.  
  556.                    /* original UU-translation table */
  557.                    tmpCode1 = " !""#$%&'()*+,-./0123456789:;<=>?"
  558.                    tmpCode2 = "@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_"
  559.                    tmpCoding = "UU"
  560.                 END
  561.  
  562.                 /* produce assumed-table */
  563.                 CALL LINEOUT g.eTempFile, "table"
  564.                 CALL LINEOUT g.eTempFile, tmpCode1
  565.                 CALL LINEOUT g.eTempFile, tmpCode2
  566.                 g.eDecodeTable = tmpCode1 || tmpCode2
  567.  
  568.                 table_00x_char =  LEFT(g.eDecodeTable, 1)    /* code for x00 */
  569.                 CALL say_c "                [" || g.eTxtHi || tmpCoding || g.eTxtInf || "-encoding assumed, translation-table inserted.]"
  570.  
  571.                 i = i + 3
  572.                 CALL STREAM workfile, "C", "SEEK =" || save_pos     /* reposition to initial read/write position */
  573.              END
  574.  
  575.  
  576.              CALL LINEOUT g.eTempFile, line     /* write begin-line */
  577.              i = i + 1
  578.  
  579.              begin = 1               /* beginning line found */
  580.              valid_encoded_data = 1  /* writing body is ok now */
  581.              first = 0               /* no need to get into this IF-block anymore */
  582.           END
  583.  
  584.           ITERATE
  585.        END
  586.  
  587.        /* not the first chunk, find first valid encoded data */
  588.        IF \valid_encoded_data THEN   /* search first line to write */
  589.        DO
  590.           tmp = TRANSLATE(line)
  591.           /* does line contain "BEGIN" ? */
  592.           IF POS("BEGIN", tmp) > 0 THEN
  593.           DO
  594.              /* is it a Unix-shar-script using sed and the key-word "BEGIN" ?
  595.                 or is there a unix-shell-comment leadin,
  596.                 if so, then skip until "BEGIN" appears */
  597.  
  598.              IF POS("SED", tmp) > 0 | LEFT(tmp, 1) = '#' THEN
  599.                 ITERATE
  600.  
  601.              valid_encoded_data = 1
  602.              ITERATE
  603.           END
  604.  
  605.  
  606.           /* first line, assuming 62 characters long, sometimes 61 only */
  607.           tmp = LENGTH(line)
  608.           IF \(tmp = 61 | tmp = 62) | LEFT(line, 1) <> g.eLeadin THEN ITERATE
  609.  
  610.           /* is this truly an encoded line ? */
  611.           IF VERIFY(line, g.eDecodeTable) <> 0 THEN
  612.              ITERATE
  613.  
  614.           valid_encoded_data = 1          /* no "BEGIN"-leadin, but assuming being in coding-part of file */
  615.        END
  616.        ELSE                     /* search for last line to write */
  617.        DO
  618.           IF last_line(line) THEN 
  619.           DO
  620.              valid_encoded_data = 0     /* look for another valid chunk within file */
  621.              ITERATE
  622.           END
  623.        END
  624.  
  625.        CALL LINEOUT g.eTempFile, line     /* write line in hand */
  626.        i = i + 1
  627.  
  628. /*
  629. /* debug */
  630. tmp_rgf_length = POS(LEFT(line,1), g.eDecodeTable) - 1
  631. tmp_line_length = length(line)
  632.  
  633. tmp_rgf_chars = tmp_rgf_length * 4 / 3
  634.  
  635. if line <> "end" & tmp_rgf_chars <> (tmp_line_length-1) & tmp_rgf_chars <> (tmp_line_length) then
  636. do
  637.    say "assemble(): line_no ["i"], expected ["tmp_rgf_chars"] <> ["length(line)"] chars[" || tmp_rgf_length  || "]"
  638. end
  639. */
  640.  
  641.        IF line = "end" THEN LEAVE       /* last chunks, writing finished */            
  642.     END
  643.  
  644.     CALL STREAM workfile, 'C', 'CLOSE'  /* close file */
  645.     CALL STREAM g.eTempFile, 'C', 'CLOSE'  /* close file */
  646.  
  647.     IF \begin & first THEN           /* no begin found, error in hand !!! */
  648.        CALL error "ASSEMBLE() no begin-line found !", "DELETE"
  649.  
  650. /*
  651. call debug "number of lines written", i
  652. call debug "worked on:", "["workfile"]"
  653. say
  654. call debug "first couple of lines in file", g.eTempFile
  655. "@head "g.eTempFile
  656. call debug "last couple of lines in file", g.eTempFile
  657. "@tail "g.eTempFile
  658. say
  659. "@pause"
  660. */
  661.  
  662.     RETURN i
  663.  
  664.  
  665. /* not necessary anymore, implying translation all the time, 
  666. if RxDecode has to supply the UU-translation table
  667.  
  668. /* Is the content of the workfile made up of the original UUencoding set ?
  669.    If not, then replace the `-character with the blank-character */
  670. ORIGINAL_UUE_TABLE: PROCEDURE EXPOSE workfile ext g.
  671.  
  672.     save_pos = STREAM(workfile, "C", "SEEK +0")  /* get present read/write position in workfile */
  673.     found = 0
  674.  
  675.     DO WHILE LINES(workfile) > 0        /* scan encoded UU-part for ` */
  676.        line = LINEIN(workfile)          /* read line */
  677.  
  678.        IF last_line(line) THEN          /* last line in this chunks */
  679.           LEAVE
  680.  
  681.        found = POS("`", line)   /* search for `-character */
  682.  
  683.        IF found > 0 THEN LEAVE
  684.     END
  685.  
  686.     CALL STREAM workfile, "C", "SEEK =" || save_pos     /* reposition to initial read/write position */
  687.  
  688.     RETURN found > 0
  689.  
  690.  
  691. */
  692.  
  693.  
  694. /* is it the last line (the one which indicates the end of the encoding part) ? */
  695. LAST_LINE: PROCEDURE EXPOSE g.
  696.     line = ARG(1)
  697.  
  698.     /* this is not the end-indication for this part of the encoded file, but
  699.        the end of the encoded file itself and the line needs to be written
  700.     */
  701.     IF line = "end" THEN RETURN 0
  702.  
  703.     /* if any line greater than maximum line for XX- or UU-encoded files (62 char)
  704.        assume that EOF for this chunks has arrived */
  705.     IF LENGTH(line) > 62 THEN RETURN 1
  706.  
  707.     tmp_line = TRANSLATE(line)
  708.  
  709.     /* assume that a lead-in of "end" means EOF for this chunk has arrived */
  710.  
  711.     IF POS("END", tmp_line) > 0 THEN
  712.     DO
  713.        IF LEFT(tmp_line, 3) = "END" THEN RETURN 1
  714.        IF POS("---", tmp_line) > 0 | POS("===", tmp_line) > 0 THEN RETURN 1
  715.     END
  716.  
  717.  
  718.     /* "cut here" means EOF for this chunk has arrived */
  719.     IF POS("CUT HERE", tmp_line) > 0 THEN RETURN 1
  720.  
  721.     /* "C U T" means EOF for this chunk has arrived */
  722.     IF POS("C U T", tmp_line) > 0 THEN RETURN 1
  723.  
  724. /*
  725. /* 
  726.    temporary, sometimes encoded files are truly in error in the
  727.    line right before the last, therefore not activated 
  728. */
  729.     IF g.eDecodeTable <> "" THEN
  730.     DO
  731.        tmp_rgf_length = POS(LEFT(line,1), g.eDecodeTable) - 1      /* get number of char expected */
  732.        tmp_line_length = length(line)              /* length of line */
  733.        
  734.        tmp_rgf_chars = tmp_rgf_length * 4 / 3      /* number of chars encoded */
  735.    
  736.        if (tmp_rgf_chars <> tmp_line_length) & (tmp_rgf_chars <> (tmp_line_length - 1)) then
  737.        do
  738.           say "  assemble(): line ["line"]"
  739.           say "  assemble(): expected char ["tmp_rgf_chars"] <> length(line) ["length(line)"] # of chars indicated [" || tmp_rgf_length  || "]"
  740.           return 1
  741.        end
  742.     END
  743. /* temporary end */
  744. */
  745.  
  746.     RETURN 0            /* no last line-indicator in hand */
  747.  
  748.  
  749.  
  750.  
  751.  
  752.  
  753. /* one of Knuth's algorithms; sort read lines in array */
  754. SORT_PROCEDURE: PROCEDURE EXPOSE filestmp. g.
  755.  
  756.     DO i = 1 TO filestmp.0                 /* translate filenames into uppercase */
  757.        filestmp.i = TRANSLATE(filestmp.i)
  758.     END
  759.  
  760.  
  761.    /* define M for passes */
  762.    M = 1
  763.    DO WHILE (9 * M + 4) < filestmp.0
  764.       M = M * 3 + 1
  765.    END
  766.  
  767.    /* sort stem */
  768.    DO WHILE M > 0
  769.       K = filestmp.0 - M
  770.       DO J = 1 TO K
  771.          Q = J
  772.          DO WHILE Q > 0
  773.             L = Q + M
  774.             /* tell REXX to do comparison exact, i.e. take
  775.                leading & trailing blanks into account */
  776.             IF filestmp.Q <<= filestmp.L THEN LEAVE
  777.  
  778.             /* switch elements */
  779.             tmp         = filestmp.Q
  780.             filestmp.Q  = filestmp.L
  781.             filestmp.L  = tmp
  782.             Q = Q - M
  783.          END
  784.       END
  785.       M = M % 3
  786.    END
  787.  
  788.    RETURN
  789.  
  790.  
  791.  
  792.  
  793. ERROR: PROCEDURE EXPOSE g.
  794.    CALL Beep 500, 10
  795.    CALL say_c g.eTxtAla || ARG(1)
  796.  
  797.    IF TRANSLATE(LEFT(ARG(2), 1)) = "D" & g.eTempFile <> "" THEN
  798.    DO
  799.       CALL STREAM g.eTempFile, "C", "CLOSE"       /* close it */
  800.       ADDRESS CMD "@DEL" '"'g.eTempFile'"'        /* erase temp-file */
  801.       g.eTempFile = ""
  802.    END
  803.  
  804.    RETURN
  805.  
  806. DEBUG:
  807.    CALL say_c "***debug:" ARG(1) ">"ARG(2)"<"
  808.    RETURN
  809.  
  810.  
  811. /* display error message & terminate program */
  812. HALT:
  813.    /* Is there a temporary file open */
  814.    IF g.eTempFile <> "" THEN
  815.    DO
  816.       CALL STREAM g.eTempFile, "C", "CLOSE"       /* close it */
  817.       ADDRESS CMD "@DEL" '"'g.eTempFile'"'        /* erase it */
  818.    END
  819.  
  820.    PARSE SOURCE . . this_file   /* get this procedure's filename */
  821.    this_file = FILESPEC('NAME', this_file)      /* get filename only */
  822.    /* error message on device "STDERR" */
  823.    '@ECHO' g.eTxtInf || this_file || g.eTxtNorm':' g.eTxtAla || 'Ctrl-C pressed, aborting ...' || g.eScrNorm '>&2'
  824.    EXIT ''
  825.  
  826.  
  827.  
  828.  
  829. USAGE:
  830.    CALL say_c g.eTxtHi || "RxDecode" || g.eTxtInf || ":  concatenates XX- or UU-encoded file chunks and decodes them"
  831.    CALL say_c "           usage:   " || g.eTxtHi || "RXDECODE [X|U] [/B]"
  832.    CALL say_c
  833.    CALL say_c "  option [X|U]:" g.eTxtHi || "X" || g.eTxtInf " ... use XXDECODE to decode UU- and XX-encoded files"
  834.    CALL say_c "               " g.eTxtHi || "U" || g.eTxtInf " ... use UUDECODE to decode UU- and XX-encoded files"
  835.    CALL say_c
  836.    CALL say_c "  option [/B]: " g.eTxtHi || "/B" || g.eTxtInf '... show output in' g.eTxtHi || 'b' || g.eTxtInf || 'lack/white (no ANSI-colors)'
  837.  
  838.  
  839.    CALL say_c
  840.    CALL say_c "This program decodes XX- or UU-encoded files (even if split), e.g.:"
  841.    CALL say_c
  842.    CALL say_c "    chunks-order in file extension:"   g.eTxtNorm g.eTxtInf
  843.    CALL say_c g.eTxtHi || "        foo.uu"||g.eTxtNorm||"1"||g.eTxtHi "foo.uu"||g.eTxtNorm||"2"||g.eTxtHi "foo.uu"||g.eTxtNorm||"3"||g.eTxtHi,
  844.               "foo.uu"||g.eTxtNorm||"4"||g.eTxtHi "foo.uu"||g.eTxtNorm||"5"|| g.eTxtHi "foo.uu"||g.eTxtNorm||"6"
  845.    CALL say_c g.eTxtHi || "        foo.uu"||g.eTxtNorm||"7"||g.eTxtHi "foo.uu"||g.eTxtNorm||"8"||g.eTxtHi "foo.uu"||g.eTxtNorm||"9"||g.eTxtHi,
  846.               "foo.u"||g.eTxtNorm||"10"||g.eTxtHi "foo.u"||g.eTxtNorm||"11"||g.eTxtHi "foo.u"||g.eTxtNorm||"12"
  847.    CALL say_c
  848.    CALL say_c "    chunks-order in file-body:"
  849.    CALL say_c g.eTxtHi || "        foo"||g.eTxtNorm||"1"||g.eTxtHi||."uue foo"||g.eTxtNorm||"2"||g.eTxtHi||."uue foo"||g.eTxtNorm||"3"||g.eTxtHi||."uue foo"||g.eTxtNorm||, 
  850.               "4"||g.eTxtHi||."uue  foo"||g.eTxtNorm||"5"||g.eTxtHi||."uue  foo"||g.eTxtNorm||"6"||g.eTxtHi||."uue"
  851.    CALL say_c g.eTxtHi || "        foo"||g.eTxtNorm||"7"||g.eTxtHi||."uue foo"||g.eTxtNorm||"8"||g.eTxtHi||."uue foo"||g.eTxtNorm||"9"||g.eTxtHi||."uue",
  852.               "foo"||g.eTxtNorm||"10"||g.eTxtHi||".uue foo"||g.eTxtNorm||"11"||g.eTxtHi||".uue foo"||g.eTxtNorm||"12"||g.eTxtHi||".uue "
  853.    CALL say_c 
  854.    CALL say_c "    normal encoded file:"
  855.    CALL say_c g.eTxtHi || "        foo.xxe"
  856.    CALL say_c
  857.    CALL say_c "It handles XX- and UU-encoded files, removes mail-headers and mail-trailers, "
  858.    CALL say_c "as well as information supplied by the sender. In addition it takes care of "
  859.    CALL say_c "newer UU-encoded files which changed the translation-table. Chunks are ordered"
  860.    CALL say_c "in the ascending order implied by the chunks-number (serial-number)."
  861.    CALL say_c "Hint: A 'chunk' may contain several parts of an encoded file."
  862.    EXIT
  863.  
  864. SAY_C: PROCEDURE EXPOSE g.
  865.    SAY g.eTxtInf || ARG(1) || g.eScrNorm
  866.    RETURN
  867.