diff options
Diffstat (limited to 'ipl/progs/iidecode.icn')
-rw-r--r-- | ipl/progs/iidecode.icn | 248 |
1 files changed, 248 insertions, 0 deletions
diff --git a/ipl/progs/iidecode.icn b/ipl/progs/iidecode.icn new file mode 100644 index 0000000..3aaa760 --- /dev/null +++ b/ipl/progs/iidecode.icn @@ -0,0 +1,248 @@ +############################################################################ +# +# File: iidecode.icn +# +# Subject: Program to decode text in style of uudecode +# +# Author: Richard L. Goerwitz, enhanced by Frank J. Lhota +# +# Date: May 2, 2001 +# +########################################################################### +# +# This file is in the public domain. +# +############################################################################ +# +# Version: 2.0 +# +########################################################################### +# +# This is an Icon port of the UNIX/C uudecode utility. Since +# uudecode is publicly distributable BSD code, I simply grabbed a +# copy, and rewrote it in Icon. The only basic functional changes I +# made to the program were: (1) To simplify the notion of file mode +# (everything is encoded with 0644 permissions), and (2) to add a +# command-line switch for xxencoded files (similar to uuencoded +# files, but capable of passing unscathed through non-ASCII EBCDIC +# sites). +# +# usage: iidecode [infile] [-x] +# +# Usage is compatible with that of the UNIX uudecode command, i.e. a +# first (optional) argument gives the name the file to be decoded. +# If this is omitted, iidecode just uses the standard input. The -x +# switch (peculiar to iidecode) forces use of the the xxdecoding +# algorithm. If you try to decode an xxencoded file without speci- +# -x on the command line, iidecode will try to forge ahead anyway. +# If it thinks you've made a mistake, iidecode will inform you after +# the decode is finished. +# +# +# FIXES: Speeded up substantially (more than twice as fast on my +# machine) by using a more icon-ish algorithm. We decode in two +# steps: +# +# 1) The coded characters are mapped to "small bytes", +# each with 2 zero high bits, i.e. <<= "\x3F". +# 2) We then 'pack' the small bytes by taking groups of 4 small bytes +# (each with 2 zero high bits and 6 data bits) and packing +# the data bits into groups of 3 bytes. +# +# There are numerous advantages to this approach. The icon map +# function is much faster than the 'C'-ish alternatives. We can +# process things one line at a time. Also, the different decoding +# mechanisms (old BSD, new BSD, xxdecode) can be produces by simply +# using different map parameters. +# +############################################################################ +# +# See also: iiencode.icn +# +############################################################################ + +link options + +global oversizes + +procedure main ( a ) + + local opt, in, out, dest, is_xx + initial oversizes := 0 + + + opt := options ( a, "-x" ) + is_xx := opt [ "x" ] + + # Check for correct number of args. + case *a of + { + 0 : in := &input + 1 : in := open ( a [ 1 ], "r" ) | + { + write ( &errout, "Can't open input file, ", a [ 1 ], ".\n_ + usage: iidecode [infile] [-x]" ) + exit ( 1 ) + } + default : + { + write ( &errout, "usage: iidecode [infile] [-x]" ) + exit ( 2 ) + } + } + + + # Find the "begin" line, and determine the destination file name. + !in ? { + ="begin " & + tab ( many ( &digits ) ) & # mode ignored + tab ( many ( ' ' ) ) & + dest := tab ( 0 ) + } + + # If dest is null, the begin line either isn't present, or is + # corrupt (which necessitates our aborting with an error msg.). + if /dest then { + write ( &errout, "No begin line." ) + exit ( 3 ) + } + + # Tilde expansion is heavily UNIX dependent, and we can't always + # safely write the file to the current directory. Our only choice + # is to abort. + if match ( "~", dest ) then { + write ( &errout, "Please remove ~ from input file begin line." ) + exit ( 4 ) + } + + out := open ( dest, "wu" ) + decode ( in, out, is_xx ) # decode checks for "end" line + if not match ( "end", !in ) then { + write ( &errout, "No end line.\n" ) + exit ( 5 ) + } + + # Check global variable oversizes (set by decode) + # to see if we used the correct decoding algorithm. + if oversizes > 0 then { + if \is_xx then { + write ( &errout, "Input file appears to have been uuencoded.\n_ + Try invoking iidecode without the -x arg." ) + } + else { + write ( &errout, "Input file is either corrupt, or xxencoded.\n_ + Please check the output; try the -x option." ) + } + } + + every close ( ( &input ~=== in ) | out ) + + exit ( 0 ) + +end + +########################################################################### +# +# Reads encoded lines from file in, decodes them, +# and writes the decoded data# to out. +# "uu" decoding is done unless \is_xx, in which case "xx" decoding is done. +# +########################################################################### +procedure decode(in, out, is_xx) + + # Copy from in to out, decoding as you go along. + + + local line, n, coded, unpacked, badchars + + if \is_xx then { + coded := "_ + +-0123456789ABCD_ + EFGHIJKLMNOPQRST_ + UVWXYZabcdefghij_ + klmnopqrstuvwxyz" + unpacked := "_ + \x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F_ + \x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1A\x1B\x1C\x1D\x1E\x1F_ + \x20\x21\x22\x23\x24\x25\x26\x27\x28\x29\x2A\x2B\x2C\x2D\x2E\x2F_ + \x30\x31\x32\x33\x34\x35\x36\x37\x38\x39\x3A\x3B\x3C\x3D\x3E\x3F" + } + else { + # + # To be safe, we map both " " and "`" to "\x00" + # + coded := " _ + !\"#$%&'()*+,-./_ + 0123456789:;<=>?_ + @ABCDEFGHIJKLMNO_ + PQRSTUVWXYZ[\\]^_`" + unpacked := "_ + \x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F_ + \x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1A\x1B\x1C\x1D\x1E\x1F_ + \x20\x21\x22\x23\x24\x25\x26\x27\x28\x29\x2A\x2B\x2C\x2D\x2E\x2F_ + \x30\x31\x32\x33\x34\x35\x36\x37\x38\x39\x3A\x3B\x3C\x3D\x3E\x3F\x00" + } + + badchars := ~ coded + + while line := read ( in ) do { + + if *line = 0 then { + write ( &errout, "Short file.\n" ) + exit ( 10 ) + } + + line ? while tab ( upto ( badchars ) + 1 ) do oversizes +:= 1 + + map ( line, coded, unpacked ) ? { + n := ord ( move ( 1 ) ) + line := tab ( 0 ) + + if not ( *line % 4 = 0, n <= ( ( *line / 4 ) * 3 ) ) then { + write ( &errout, "Short and/or corrupt line:\n", line ) + if /is_xx & oversizes > 0 then + write ( &errout, "Try -x option?" ) + exit ( 15 ) + } + + # Uuencode signals the end of the coded text by a space + # and a line (i.e. a zero-length line, coded as a space). + if n <= 0 then break + + writes ( out, left ( repack ( line ), n ) ) + } + } + + return + +end + + +########################################################################### +# +# Takes groups of 4 bytes in s (each byte should have 2 zero high bits) +# and packs the 6 lower data bits into group of 3 bytes. +# +########################################################################### +procedure repack ( s ) + + local n, grp + + s ? { + s := "" + while grp := move ( 4 ) do + { + n := 0 + grp ? while n := ord ( move ( 1 ) ) % 16r40 + ( n * 16r40 ) + + s ||:= + char ( ishift ( iand ( n, 16rFF0000 ), -16 ) ) || + char ( ishift ( iand ( n, 16r00FF00 ), - 8 ) ) || + char ( iand ( n, 16r0000FF ) ) + } + } + + return s + +end + |