home *** CD-ROM | disk | FTP | other *** search
/ ftp.cs.arizona.edu / ftp.cs.arizona.edu.tar / ftp.cs.arizona.edu / icon / historic / v941.tgz / icon.v941src.tar / icon.v941src / ipl / progs / iidecode.icn < prev    next >
Text File  |  2001-05-02  |  7KB  |  249 lines

  1. ############################################################################
  2. #
  3. #       File:     iidecode.icn
  4. #
  5. #       Subject:  Program to decode text in style of uudecode
  6. #
  7. #       Author:   Richard L. Goerwitz, enhanced by Frank J. Lhota
  8. #
  9. #       Date:     May 2, 2001
  10. #
  11. ###########################################################################
  12. #
  13. #   This file is in the public domain.
  14. #
  15. ############################################################################
  16. #
  17. #       Version:  2.0 
  18. #
  19. ###########################################################################
  20. #
  21. #  This is an Icon port of the UNIX/C uudecode utility.  Since
  22. #  uudecode is publicly distributable BSD code, I simply grabbed a
  23. #  copy, and rewrote it in Icon.  The only basic functional changes I
  24. #  made to the program were:  (1) To simplify the notion of file mode
  25. #  (everything is encoded with 0644 permissions), and (2) to add a
  26. #  command-line switch for xxencoded files (similar to uuencoded
  27. #  files, but capable of passing unscathed through non-ASCII EBCDIC
  28. #  sites).
  29. #
  30. #         usage:  iidecode [infile] [-x]
  31. #
  32. #  Usage is compatible with that of the UNIX uudecode command, i.e. a
  33. #  first (optional) argument gives the name the file to be decoded.
  34. #  If this is omitted, iidecode just uses the standard input.  The -x
  35. #  switch (peculiar to iidecode) forces use of the the xxdecoding
  36. #  algorithm.  If you try to decode an xxencoded file without speci-
  37. #  -x on the command line, iidecode will try to forge ahead anyway.
  38. #  If it thinks you've made a mistake, iidecode will inform you after
  39. #  the decode is finished.
  40. #
  41. #
  42. #  FIXES: Speeded up substantially (more than twice as fast on my
  43. #  machine) by using a more icon-ish algorithm.  We decode in two
  44. #  steps:
  45. #
  46. #  1)   The coded characters are mapped to "small bytes",
  47. #       each with 2 zero high bits, i.e. <<= "\x3F".                        
  48. #  2)   We then 'pack' the small bytes by taking groups of 4 small bytes
  49. #       (each with 2 zero high bits and 6 data bits) and packing
  50. #       the data bits into groups of 3 bytes.
  51. #
  52. #  There are numerous advantages to this approach.  The icon map
  53. #  function is much faster than the 'C'-ish alternatives.  We can
  54. #  process things one line at a time. Also, the different decoding
  55. #  mechanisms (old BSD, new BSD, xxdecode) can be produces by simply
  56. #  using different map parameters.
  57. #
  58. ############################################################################
  59. #
  60. #  See also: iiencode.icn
  61. #
  62. ############################################################################
  63.  
  64. link options
  65.  
  66. global oversizes
  67.  
  68. procedure main ( a )
  69.  
  70.     local opt, in, out, dest, is_xx
  71.     initial oversizes := 0
  72.  
  73.  
  74.     opt := options ( a, "-x" )
  75.     is_xx := opt [ "x" ]
  76.  
  77.     # Check for correct number of args.
  78.     case *a of
  79.     {
  80.     0       : in := &input
  81.     1       : in := open ( a [ 1 ], "r" ) |
  82.         {
  83.         write ( &errout, "Can't open input file, ", a [ 1 ], ".\n_
  84.         usage:  iidecode [infile] [-x]" )
  85.         exit ( 1 )
  86.         }
  87.     default :
  88.         {
  89.         write ( &errout, "usage:  iidecode [infile] [-x]" )
  90.         exit ( 2 )
  91.         }
  92.     }
  93.  
  94.  
  95.     # Find the "begin" line, and determine the destination file name.
  96.     !in ? {
  97.     ="begin "                  &
  98.     tab ( many ( &digits ) )   & # mode ignored
  99.     tab ( many ( ' ' ) )       &
  100.     dest := tab ( 0 )
  101.     }
  102.  
  103.     # If dest is null, the begin line either isn't present, or is
  104.     # corrupt (which necessitates our aborting with an error msg.).
  105.     if /dest then {
  106.     write ( &errout, "No begin line." )
  107.     exit ( 3 )
  108.     }
  109.  
  110.     # Tilde expansion is heavily UNIX dependent, and we can't always
  111.     # safely write the file to the current directory.  Our only choice
  112.     # is to abort.
  113.     if match ( "~", dest ) then {
  114.     write ( &errout, "Please remove ~ from input file begin line." )
  115.     exit ( 4 )
  116.     }
  117.        
  118.     out := open ( dest, "wu" )
  119.     decode ( in, out, is_xx )   # decode checks for "end" line
  120.     if not match ( "end", !in ) then {
  121.     write ( &errout, "No end line.\n" )
  122.     exit ( 5 )
  123.     }
  124.  
  125.     # Check global variable oversizes (set by decode)
  126.     # to see if we used the correct decoding algorithm.
  127.     if oversizes > 0 then {
  128.     if \is_xx then {
  129.         write ( &errout, "Input file appears to have been uuencoded.\n_
  130.         Try invoking iidecode without the -x arg." )
  131.     }
  132.     else {
  133.         write ( &errout, "Input file is either corrupt, or xxencoded.\n_
  134.         Please check the output; try the -x option." )
  135.     }
  136.     }
  137.  
  138.     every close ( ( &input ~=== in ) | out )
  139.  
  140.     exit ( 0 )
  141.  
  142. end
  143.  
  144. ###########################################################################
  145. #
  146. # Reads encoded lines from file in, decodes them,
  147. # and writes the decoded data# to out.
  148. # "uu" decoding is done unless \is_xx, in which case "xx" decoding is done.
  149. #
  150. ###########################################################################
  151. procedure decode(in, out, is_xx)
  152.     
  153.     # Copy from in to out, decoding as you go along.
  154.  
  155.  
  156.     local line, n, coded, unpacked, badchars
  157.  
  158.     if \is_xx then {
  159.     coded := "_
  160.         +-0123456789ABCD_
  161.         EFGHIJKLMNOPQRST_
  162.         UVWXYZabcdefghij_
  163.         klmnopqrstuvwxyz"
  164.     unpacked := "_
  165.         \x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F_
  166.         \x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1A\x1B\x1C\x1D\x1E\x1F_
  167.         \x20\x21\x22\x23\x24\x25\x26\x27\x28\x29\x2A\x2B\x2C\x2D\x2E\x2F_
  168.         \x30\x31\x32\x33\x34\x35\x36\x37\x38\x39\x3A\x3B\x3C\x3D\x3E\x3F"
  169.     }
  170.     else {
  171.     #
  172.     # To be safe, we map both " " and "`" to "\x00"
  173.     #
  174.     coded := " _
  175.          !\"#$%&'()*+,-./_
  176.         0123456789:;<=>?_
  177.         @ABCDEFGHIJKLMNO_
  178.         PQRSTUVWXYZ[\\]^_`"
  179.     unpacked := "_
  180.         \x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F_
  181.         \x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1A\x1B\x1C\x1D\x1E\x1F_
  182.         \x20\x21\x22\x23\x24\x25\x26\x27\x28\x29\x2A\x2B\x2C\x2D\x2E\x2F_
  183.         \x30\x31\x32\x33\x34\x35\x36\x37\x38\x39\x3A\x3B\x3C\x3D\x3E\x3F\x00"
  184.     }
  185.  
  186.     badchars := ~ coded
  187.  
  188.     while line := read ( in ) do {
  189.  
  190.     if *line = 0 then {
  191.         write ( &errout, "Short file.\n" )
  192.         exit ( 10 )
  193.     }
  194.  
  195.     line ? while tab ( upto ( badchars ) + 1 ) do oversizes +:= 1
  196.  
  197.     map ( line, coded, unpacked ) ? {
  198.         n := ord ( move ( 1 ) )
  199.         line := tab ( 0 )
  200.  
  201.         if not ( *line % 4 = 0, n <= ( ( *line / 4 ) * 3 ) ) then {
  202.         write ( &errout, "Short and/or corrupt line:\n", line )
  203.         if /is_xx & oversizes > 0 then
  204.             write ( &errout, "Try -x option?" )
  205.         exit ( 15 )
  206.         }
  207.  
  208.         # Uuencode signals the end of the coded text by a space
  209.         # and a line (i.e. a zero-length line, coded as a space).
  210.         if n <= 0 then break
  211.         
  212.         writes ( out, left ( repack ( line ),  n ) )
  213.     }
  214.     }
  215.     
  216.     return
  217.  
  218. end
  219.  
  220.  
  221. ###########################################################################
  222. #
  223. # Takes groups of 4 bytes in s (each byte should have 2 zero high bits)
  224. # and packs the 6 lower data bits into group of 3 bytes.
  225. #
  226. ###########################################################################
  227. procedure repack ( s )
  228.  
  229.     local n, grp
  230.  
  231.     s ? {
  232.     s := ""
  233.     while grp := move ( 4 ) do
  234.         {
  235.         n := 0
  236.         grp ? while n := ord ( move ( 1 ) ) % 16r40 + ( n * 16r40 )
  237.  
  238.         s ||:=
  239.         char ( ishift ( iand ( n, 16rFF0000 ), -16 ) ) ||
  240.         char ( ishift ( iand ( n, 16r00FF00 ), - 8 ) ) ||
  241.         char (          iand ( n, 16r0000FF )        )
  242.         }
  243.     }
  244.  
  245.     return s
  246.  
  247. end
  248.  
  249.