home *** CD-ROM | disk | FTP | other *** search
- ############################################################################
- #
- # Name: iidecode.icn
- #
- # Title: iidecode (port of the Unix/C uudecode program to Icon)
- #
- # Author: Richard L. Goerwitz
- #
- # Version: 1.7
- #
- ############################################################################
- #
- # 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.
- #
- # BUGS: Slow. I decided to go for clarity and symmetry, rather than
- # speed, and so opted to do things like use ishift(i,j) instead of
- # straight multiplication (which under Icon v8 is much faster).
- #
- ############################################################################
- #
- # See also: iiencode.icn
- #
- ############################################################################
-
-
- global oversizes
-
- procedure main(a)
-
- local ARG, in, out, dest, is_xx
-
- # Check for correct number of args.
- if *a > 2 then {
- write(&errout,"usage: iidecode [infile] [-x]")
- exit (2)
- }
-
- # Check for optional input filename and -x
- every ARG := !a do {
- if ARG == "-x" then
- is_xx := 1
- else {
- if not (in := open(ARG, "r")) then {
- write(&errout,"Can't open input file, ",a[1],".")
- write(&errout,"usage: iidecode [infile] [-x]")
- exit(1)
- }
- }
- }
- /in := &input
-
- # Find the "begin" line, and determine the destination file name.
- !in ? {
- tab(match("begin ")) &
- tab(many(&digits)) & # mode ignored
- tab(many(' ')) &
- dest := trim(tab(0),'\r') # concession to MS-DOS
- }
-
- # 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 DEC) to see if we used the
- # correct decoding algorithm.
- if \is_xx then {
- if oversizes = 0 then {
- write(&errout, "Input file appears to have been uuencoded.")
- write(&errout, "Try invoking iidecode without the -x arg.")
- }
- }
- else {
- if oversizes > 1 then {
- write(&errout, "Input file is either corrupt, or xxencoded.")
- write(&errout, "Please check the output; try the -x option.")
- }
- }
-
- every close(\in | out)
-
- exit(0)
-
- end
-
-
-
- procedure decode(in, out, is_xx)
-
- # Copy from in to out, decoding as you go along.
-
- local line, chunk
-
- if \is_xx then
- DEC := xxDEC
-
- while line := read(in) do {
-
- if *line = 0 then {
- write(&errout,"Short file.\n")
- exit(10)
- }
-
- line ? {
- n := DEC(ord(move(1)))
-
- if not ((*line-1) % 4 = 0, n <= ((*line / 4)*3)) then {
- write(&errout,"Short and/or corrupt line:\n",line)
- if /is_xx & oversizes > 1 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
-
- while (n > 0) do {
- chunk := move(4) | tab(0)
- outdec(chunk, out, n)
- n -:= 3
- }
- }
- }
-
- return
-
- end
-
-
-
- procedure outdec(s, f, n)
-
- # Output a group of 3 bytes (4 input characters). N is used to
- # tell us not to output all of the chars at the end of the file.
-
- local c1, c2, c3
-
- c1 := iand(
- ior(
- ishift(DEC(ord(s[1])),+2),
- ishift(DEC(ord(s[2])),-4)
- ),
- 8r0377)
- c2 := iand(
- ior(
- ishift(DEC(ord(s[2])),+4),
- ishift(DEC(ord(s[3])),-2)
- ),
- 8r0377)
- c3 := iand(
- ior(
- ishift(DEC(ord(s[3])),+6),
- DEC(ord(s[4]))
- ),
- 8r0377)
-
- if (n >= 1) then
- writes(f,char(c1))
- if (n >= 2) then
- writes(f,char(c2))
- if (n >= 3) then
- writes(f,char(c3))
-
- end
-
-
-
- procedure DEC(c)
-
- # global oversizes
- initial oversizes := 0
-
- # Count characters lexically greater or equal to 'a.'
- # If we get a lot of these, the file is corrupt, or perhaps
- # xxencoded (in which case -x should have been specified).
- if c >= 97 then
- oversizes +:= 1
-
- # Subtract 32 and mask off seventh and higher bits.
- return iand(c - 32, 8r077)
-
- end
-
-
-
- procedure xxDEC(c)
-
- local k, ordval
- static ordtbl
- # global oversizes
- initial {
- ordval := -1
- ordtbl := table()
- every k := ord(!"+-0123456789ABCDEFGHIJKLMNOPQRST_
- UVWXYZabcdefghijklmnopqrstuvwxyz")
- do insert(ordtbl, k, ordval +:= 1)
- oversizes := 0
- }
-
- # Mask off eighth and higher bits.
- new_c := iand(c, 8r177)
-
- # Count characters lexically greater or equal to 'a.'
- # If we find none of these, the file probably wasn't xxencoded.
- if new_c >= 97 then
- oversizes +:= 1
-
- # Map to 0-63 range (00111111 or less), mask off extra bits.
- return iand(ordtbl[new_c], 8r077)
-
- end
-