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