lane@dalcs.UUCP (John Wright/Dr. Pat Lane) (07/23/88)
In article <7521@cup.portal.com>, mslater@cup.portal.com writes: > Can someone point me to a version of UUENCODE that runs under MS-DOS? > MKS toolkit does not include it. Hi There. Funny you should mention that. I recently decided I could loosen some of my downloading log-jam if I started doing my UUDECODEing, etc. on the PC end (download first..think later, you know). So I downloaded a C version and two Pascal versions that had been posted to the net in times past. The second Pascal pgm was a minor mod to the first (see "Attempted revision history" in the comments of the Pascal programs). Neither had documentation; the posters pointed to regular UNIX manuals for the C version and only appologized for the Pascal version. I found that several minor changes were required to get the Pascal version to compile. Then I discovered that the operation and results of the Pascal and C pgms differed. (The C version works as advertised; I have written comment/documentation the the Pascal program). The C version was 3 times faster but I found the Pascal version more "user friendly" (no doubt a matter of taste). Anyway, I made some mods of my own to the Pascal programs (again, see comments) and now present to the world just what it didn't need, yet another version of UUENCODE/UUDECODE. BTW, I'm not that experienced in Pascal (and ran out of time as if I had any in the first place :-) so there are a few hitches left (in both C and Pascal vers) to solve...see comments and be my guest. I include here the Pascal sources of my modified version. If you (or anyone wan't binaries, I can mail them). I also have the C version and the previous two versions of the Pascal programs I could mail if requested. I did't include them as they have been posted many times before. Enjoy. PS: In the same spirit I downloaded a pgm called UNSHAR supposed to extract "shar" archives on MSDOS. It starts to work but then hangs every time. Anybody gotten this to work? Following are the sources of my Pascal version of UUENCODE and UUDECODE. ---------------------UUENCODE.PAS--------------------------------------- Program uuencode; { Program UUENCODE - Encodes a file to printable ascii characters so that it can be sent over mail networks, etc. A companion pgm UUDECODE decodes the file and restores the original. } { Usage: uuencode input-file [mode] [output-file] Mode is distinguised by a leading digit and defaults to 644. If given, it should be 3 octal digits representing the permission modes to be given the file when it is restored. Each digit has bits read, write, execute and the three digits are for owner, group and world. Thus "644" would give the owner read and write but only read for anyone else. For MSDOS, all digits should be the same, either 6 for normal files or 4 for read-only. Note that the companion UUDECODE program for this program ignorres mode. The default for the output-file is the input-file name with ext. ".UUE". The default extension for an explicitly specified output-file is ".UUE". While running the program lists the input and output file names, the size in bytes on the input file, a running count of bytes read from input and the percentage of the file processed (updated as each output line is written). Upon finishing the output file size is printed. If no arguments are given the program printes a short "usage" message. } { The input-file name and mode are stored in the output file in a header line with the format "begin <mode> <file name>". These are used by UUDECODE to restore the file. The mode is ignorred for MSDOS systems; On UNIX systems it is the file's permission mode. The header is followed by lines of encoded information where the first char is a coded line length (the number of original data bytes plus 32 to make it a printing char) and the rest of the chars are coded data; every 3 data bytes become 4 ascii chars in the range of decimal 33 ("!") to 96 ("`"). All lines but the last should be 61 chars long and begin with "M", signifying 45 data bytes. The last data line is followed by a line containing "`" (or a blank) in col 1, signifying 0 data chars and then a line containing "end". See the UNIX Reference Manual for details. Note that in some versions, blanks (decimal 32 in ascii) are output in place of "`" chars. UUDECODE treats them as equivalent. The coded file is typically 35% larger than the original. } { UUENCODE and UUDECODE originated on UNIX systems and were written in C. This program was written in Pascal specifically for MSDOS. The C programs are also avaliable on MSDOS and there are some differences. The usage differs in that the C version is a "filter". By default it takes its input from the standard input and always outputs to the standard output. Thus, there is no default for the output file name (and no default exten.). In the C version, the file name for the header is explicitly specified as a non-optional command line parameter while in this program it is taken from the input file name which is a non-optional command line parameter. In the C version the mode is derived from the file using the "fstat" function and cannot be over-ridden. In this program it will be "644" unless specified in the command line. The C version of UUDECODE properly handles the mode while the Pascal version ignorres it. The C version provides no progress display for the user as the standard output is used for the data output. Finally, note that some C versions do not translate blanks in the coded data lines to "`". All versions of UUDECODE appear to treat them as equivalent. } { Known bugs: Appears to open the input file in write mode (read-only files fail). Program does not read and encode file read-only attribute (not implemented). Bytes counts over 32768 go negative in user report. } { Attempted revision history. The origins of the UNIX C version are unknown to me but one version I've seen has the SCCS id: "@(#)uuencode.c 5.1 (Berkeley) 7/2/83" The C version for MSDOS that I have was adapated (or at least commented) by Don Kneller (kneller@ucsfcgl.UUCP) and posted to USENET by Tom Reingold (reintom@rocky2.UUCP) on 25/May/86. The original version of this program was also contained in Tom's posting. Nobody seems to know the author. Slightly modified version were posted by Pierre Darmon (darmon@polaris) of IBM's T.J.Watson Labs on 30/Oct/86. The orginal pgms wrote periods to the screen as processing progressed. Pierre's version gave the input file size, a running byte count and percentage left to process. On 17/July/88, John Wright (lane@dalcs) made the following changes: - Added the line "Uses Crt;" and replaced "read (kbd, ch);" with "ch := ReadKey;" to make it compile. - The progress display gives the byte count and percentage processed and the size of the output file. - The default extension for the output file (when it is given of the command line) was dropped. the ".UUE" extension is still used with the input file name when the output file name is not specified. - These comments were written. - the "Usage" message was added. - Other minor coding improvements. } uses Crt; CONST header = 'begin'; trailer = 'end'; defaultMode = '644'; defaultExtension = '.uue'; offset = 32; charsPerLine = 60; bytesPerHunk = 3; sixBitMask = $3F; TYPE string80 = string[80]; VAR infile: file of byte; outfile: text; infilename, outfilename, mode: string80; lineLength, numbytes, bytesInLine: integer; line: array [0..59] of char; hunk: array [0..2] of byte; chars: array [0..3] of byte; size,total :integer; procedure Usage; begin {usage} writeln; writeln('Usage: uuencode input-file [mode] [output-file]'); writeln; writeln('Mode is distinguised by a leading digit and default is 644.'); writeln('Default output-file is the input-file name with ext. ".UUE".'); { writeln('Extension on output-file name defaults to ".UUE"');} writeln; halt end; {usage} procedure Abort (message: string80); begin {abort} writeln(message); close(infile); close(outfile); halt end; {abort} procedure Init; procedure GetFiles; VAR i: integer; temp: string80; ch: char; begin {GetFiles} if ParamCount < 1 then Usage; infilename := ParamStr(1); {$I-} assign (infile, infilename); reset (infile); {$i+} if IOResult > 0 then abort (concat ('Can''t open file ', infilename)); writeln; writeln('Uuencoding file: ', infilename); i := pos('.', infilename); if i = 0 then outfilename := infilename else outfilename := copy (infilename, 1, pred(i)); outfilename := concat(outfilename, defaultExtension); mode := defaultMode; if ParamCount > 1 then for i := 2 to ParamCount do begin temp := Paramstr(i); if temp[1] in ['0'..'9'] then mode := temp else begin outfilename := temp {; if pos ('.', outfilename) = 0 then outfilename := concat(outfilename, defaultExtension)} end; end; assign (outfile, outfilename); writeln('Output to file: ', outfilename); {$i-} reset(outfile); {$i+} if IOresult = 0 then begin Write ('Overwrite current ', outfilename, '? [Y/N] '); repeat ch := ReadKey; { read (kbd, ch);} ch := Upcase(ch) until ch in ['Y', 'N']; writeln (ch); if ch = 'N' then abort(concat (outfilename, ' not overwritten.')) end; { close(outfile); {this line was in original pgm but made pgm bomb} {$i-} rewrite(outfile); {$i+} if ioresult > 0 then abort(concat('Can''t open ', outfilename)); total:=FileSize(infile); if total < 0 then total:=total+65536; writeln('Input file size: ',total:7,' bytes'); end; {getfiles} begin {Init} GetFiles; bytesInLine := 0; lineLength := 0; numbytes := 0; size:=0; writeln (outfile, header, ' ', mode, ' ', infilename); end; {init} procedure FlushLine; VAR i: integer; procedure writeout(ch: char); begin {writeout} if ch = ' ' then write(outfile, '`') else write(outfile, ch) end; {writeout} procedure DispSize; begin {DispSize} write('Bytes processed: ',size:7,' (', 100.0*size/total:3:0,'%)',chr(13)); end; {DispSize} begin {FlushLine} {write ('.');} DispSize; writeout(chr(bytesInLine + offset)); for i := 0 to pred(lineLength) do writeout(line[i]); writeln (outfile); lineLength := 0; bytesInLine := 0 end; {FlushLine} procedure FlushHunk; VAR i: integer; begin {FlushHunk} if lineLength = charsPerLine then FlushLine; chars[0] := hunk[0] shr 2; chars[1] := (hunk[0] shl 4) + (hunk[1] shr 4); chars[2] := (hunk[1] shl 2) + (hunk[2] shr 6); chars[3] := hunk[2] and sixBitMask; for i := 0 to 3 do begin line[lineLength] := chr((chars[i] and sixBitMask) + offset); lineLength := succ(lineLength) end; bytesInLine := bytesInLine + numbytes; numbytes := 0 end; {FlushHunk} procedure Encode1; begin {encode1}; if numbytes = bytesperhunk then flushhunk; read (infile, hunk[numbytes]); size:=size+1; numbytes := succ(numbytes) end; {encode1} procedure Terminate; function GetFileSize (filename: string80): integer; VAR fi: file of byte; size: integer; begin {GetFileSize} assign(fi,filename); reset(fi); size:=FileSize(fi); close(fi); if size < 0 then size:=size+65536; GetFileSize := size end; {GetFileSize} begin {Terminate} if numbytes > 0 then flushhunk; if lineLength > 0 then flushline; flushline; writeln (outfile, trailer); close (outfile); close (infile); size:=GetFileSize(outfilename); writeln; writeln('Output file size: ',size:7,' bytes'); end; {Terminate} begin {uuencode} Init; while not eof (infile) do Encode1; Terminate; end. {uuencode} ---------------------UUDECODE.PAS--------------------------------------- program uudecode; { Program UUDECODE - Decodes a file of printable ascii characters created by companion pgm UUENCODE, restoring the original file. } { Usage: uudecode input-file The name for the restored file is taken from the header line in the input file. The mode on the header line is ignorred and the file is restored as read/write. Not applicable: The default extension for the input-file is ".UUE". While running, the program lists the input and output file names, the size in bytes on the input file, a running count of bytes read from input and the percentage of the file processed (updated as each input line is read). Upon finishing the output file size is printed. If no arguments are given the program printes a short "usage" message. } { The program reads the input file looking for a header line with "begin" in col 1-6 followed by the permission mode and file name for the restored file. See commants in the UUENCODE program for a desciption of the mode. On UNIX systems it is the file's permission mode bits as three octal digits. This program ignorres it. The header is followed by lines of encoded information where the first char is a coded line length (the number of original data bytes plus 32 to make it a printing char) and the rest of the chars are coded data; every 3 data bytes become 4 ascii chars in the range of decimal 33 ("!") to 96 ("`"). All lines but the last should be 61 chars long and begin with "M", signifying 45 data bytes. The last data line is followed by a line containing only "`" (or a blank) in col 1, signifying 0 data chars and then a line containing "end". See the UNIX Reference Manual for details. Note that in some versions of UUENCODE, blanks (decimal 32 in ascii) are output in place of "`" chars. This program treats them as equivalent. The coded file is typically 35% larger than the original. } { UUENCODE and UUDECODE originated on UNIX systems and were written in C. This program was written in Pascal specifically for MSDOS. The C programs are also avaliable on MSDOS and there are some differences. The usage differs in that the C version is a "filter". By default, it takes its input from the standard input. Both pgms use the name on the header for the output file name. There is no default extension for the input file name. The C version provides no progress display for the user. The C version of UUDECODE calls "chmod" to set the attribute bits of the file based on mode (read as an octal number). It appears to set the read-only bit if and only if the 2nd bit of the first octal digit is 0. Finally, note that some C versions do not translate blanks in the coded data lines to "`". All versions of UUDECODE appear to treat them as equivalent. } { Known bugs: Appears to open the input file in write mode (read-only files fail). Program does not restore file read-only attribute (not implemented). Bytes counts over 32768 go negative in user report. } { Attempted revision history. The origins of the UNIX C version are unknown to me but the program that was adapted for MSDOS and posted (see below) has the SCCS id: "@(#)uudecode.c 5.1 (Berkeley) 7/2/83" The C version for MSDOS that I have was adapated (or at least commented) by Don Kneller (kneller@ucsfcgl.UUCP) and posted to USENET by Tom Reingold (reintom@rocky2.UUCP) on 25/May/86. The original version of this program was also contained in Tom's posting. Nobody seems to know the author. Slightly modified version were posted by Pierre Darmon (darmon@polaris) of IBM's T.J.Watson Labs on 30/Oct/86. The orginal pgms wrote periods to the screen as processing progressed. Pierre's version gave the input file size, a running byte count and percentage left to process. On 17/July/88, John Wright (lane@dalcs) made the following changes: - Added the line "Uses Crt;" and replaced "read (kbd, ch);" with "ch := ReadKey;" to make it compile. - The progress display gives the byte count and percentage processed and the size of the output file. - The default extension for the input file was dropped. - These comments were written. - the "Usage" message was added. - Other minor coding improvements. } uses Crt; CONST {defaultSuffix = '.uue';} offset = 32; TYPE string80 = string[80]; VAR infilename: string80; outfilename: string80; infile: text; outfile: file of byte; line: string80; lineNum: integer; size,total :integer; procedure Abort(message: string80); begin {abort} writeln; if lineNum > 0 then write('Line ', lineNum, ': '); writeln(message); halt end; {Abort} procedure Usage; begin {usage} writeln; writeln('Usage: uudecode input-file'); writeln; { writeln('Default extension for the input file name is ".UUE".');} writeln('Output file name is taken from header ("begin") line.'); writeln('Mode from the header line is ignorred. Lines before'); writeln('the "begin" line or after the "end" line are ignorred.'); writeln; halt end; {usage} procedure NextLine; begin {NextLine} LineNum := succ(LineNum); readln(infile, line); size := size+length(line)+2; {+2 is for CR/LF} end; {NextLine} procedure Init; function GetFileSize (filename: string80): integer; VAR fi: file of byte; size: integer; begin {GetFileSize} assign(fi,filename); reset(fi); size:=FileSize(fi); close(fi); if size < 0 then size:=size+65536; GetFileSize := size end; {GetFileSize} procedure GetInFile; begin {GetInFile} infilename := ParamStr(1); { if pos('.', infilename) = 0 then infilename := concat(infilename, defaultSuffix);} assign(infile, infilename); {$i-} reset(infile); {$i+} if IOresult > 0 then abort (concat('Can''t open ', infilename)); writeln; writeln ('Uudecoding file: ', infilename); end; {GetInFile} procedure GetOutFile; var header, mode: string80; ch: char; procedure ParseHeader; VAR index: integer; Procedure NextWord(var word:string80; var index: integer); begin {nextword} word := ''; while header[index] = ' ' do begin index := succ(index); if index > length(header) then abort ('Incomplete header') end; while header[index] <> ' ' do begin word := concat(word, header[index]); index := succ(index) end end; {NextWord} begin {ParseHeader} header := concat(header, ' '); index := 7; NextWord(mode, index); NextWord(outfilename, index) end; {ParseHeader} begin {GetOutFile} if eof(infile) then abort('Nothing to decode.'); NextLine; while not ((copy(line, 1, 6) = 'begin ') or eof(infile)) do NextLine; if eof(infile) then abort('Nothing to decode.'); header := line; ParseHeader; assign(outfile, outfilename); writeln ('Output to file: ', outfilename); {$i-} reset(outfile); {$i+} if IOresult = 0 then begin write ('Overwrite current ', outfilename, '? [Y/N] '); repeat ch := ReadKey; { read (kbd, ch);} ch := UpCase(ch) until ch in ['Y', 'N']; write(ch); if ch = 'N' then abort ('Overwrite cancelled.'); writeln end; rewrite (outfile); end; {GetOutFile} begin {init} if ParamCount = 0 then Usage; lineNum := 0; GetInFile; size:=0; GetOutFile; total:=GetFileSize(infilename); writeln('Input file size: ',total:7,' bytes'); end; { init} procedure DecodeLine; VAR lineIndex, byteNum, count, i: integer; chars: array [0..3] of byte; hunk: array [0..2] of byte; function nextch: char; begin {nextch} lineIndex := succ(lineIndex); if lineIndex > length(line) then abort('Line too short.'); if not (line[lineindex] in [' '..'`']) then abort('Illegal character in line.'); if line[lineindex] = '`' then nextch := ' ' else nextch := line[lineIndex] end; {nextch} procedure DecodeByte; procedure GetNextHunk; VAR i: integer; begin {GetNextHunk} for i := 0 to 3 do chars[i] := ord(nextch) - offset; hunk[0] := (chars[0] shl 2) + (chars[1] shr 4); hunk[1] := (chars[1] shl 4) + (chars[2] shr 2); hunk[2] := (chars[2] shl 6) + chars[3]; byteNum := 0 end; {GetNextHunk} begin {DecodeByte} if byteNum = 3 then GetNextHunk; write (outfile, hunk[byteNum]); {writeln(bytenum, ' ', hunk[byteNum]);} byteNum := succ(byteNum) end; {DecodeByte} begin {DecodeLine} lineIndex := 0; byteNum := 3; count := (ord(nextch) - offset); for i := 1 to count do DecodeByte end; {DecodeLine} function CheckLine: boolean; begin {CheckLine} if line = '' then abort ('Blank line in file'); CheckLine := not (line[1] in [' ', '`']) end; {CheckLine} procedure DispSize; begin {NextSize} write('Bytes processed: ',size:7,' (', 100.0*size/total:3:0,'%)',chr(13)); end; {DispSize} procedure Terminate; var trailer: string80; begin {Terminate} if eof(infile) then abort ('Abnormal end.'); NextLine; DispSize; if length (line) < 3 then abort ('Abnormal end.'); if copy (line, 1, 3) <> 'end' then abort ('Abnormal end.'); writeln; close (infile); size:=FileSize(outfile); if size < 0 then size:=size+65536; writeln('Output file size: ',size:7,' bytes'); close (outfile) end; {Terminate} begin {uudecode} Init; NextLine; while CheckLine do begin DecodeLine; NextLine; DispSize end; Terminate end. --------------------------------------------------------------- -- John Wright ////////////////// Phone: 902-424-3805 or 902-424-6527 Post: c/o Dr Pat Lane, Biology Dept, Dalhousie U, Halifax N.S., CANADA B3H-4H8 Cdn/Bitnet: lane@cs.dal.cdn Arpa: lane%dalcs.uucp@uunet.uu.net Uucp: lane@dalcs.uucp or {uunet,watmath,utai,garfield}!dalcs!lane
jimmy@ncr-sd.SanDiego.NCR.COM (Jim Mealhouse) (07/26/88)
In article <2960@dalcs.UUCP> lane@dalcs.UUCP (John Wright/Dr. Pat Lane) writes: >Following are the sources of my Pascal version of UUENCODE and UUDECODE. >---------------------UUENCODE.PAS--------------------------------------- >---------------------UUDECODE.PAS--------------------------------------- >{ Known bugs: > Appears to open the input file in write mode (read-only files fail). > Program does not read and encode file read-only attribute (not implemented). > Bytes counts over 32768 go negative in user report. >-- >John Wright ////////////////// Phone: 902-424-3805 or 902-424-6527 >Post: c/o Dr Pat Lane, Biology Dept, Dalhousie U, Halifax N.S., CANADA B3H-4H8 >Cdn/Bitnet: lane@cs.dal.cdn Arpa: lane%dalcs.uucp@uunet.uu.net >Uucp: lane@dalcs.uucp or {uunet,watmath,utai,garfield}!dalcs!lane There is a fix for the byte counts over 32768. In Turbo Pascal 3.0 there are some new functions - LongFileSize,LongFilePosition,LongSeek-. They use Reals. So change the routines that use FileSize to use the new functions. Remember to use reals. page 199 in TP 3.0 manual. [================] James Mealhouse UseNet<wherever>!<sdcsvax,ihnp4>!ncr-sd!jimmy jimmy@ncr-sd.SanDiego.NCR.COM [================]