Document UU_E.TXT - UUEncode Specification                                 JB
-----------------------------------------------------------------------------

The UUencode Format #########################################################

UUEncode ist a method to transform 8-bit data to text which contains only
characters in the range from 32 to 95.
This takes place in five steps:

1.: Reading of the File into Pascal strings of the maximum length 45
    (All Strings except the last are 45 long; the last string is
    mostly shorter than 45). The encoder stores the length of each
    Pascal string that has been read in at last.

2.: Changing the 8-bit data of the single strings into 6-bit data.
    During that, each 3 bytes turn to 4 bytes. Therefore, the length of
    the result strings is maximum 60.
    Example:
    The data     11110001   11110001   11110001
    turns to     111100   011111   000111   110001
    and then   00111100 00011111 00000111 00110001

3.: The single bytes of the result strings are now in the range of
    0 to 63. Because values less than 32 are forbidden, now you add
    32 to each byte.
    Example:
    The data   00111100 00011111 00000111 00110001
    turns to   01011100 00111111 00100111 01010001

4.: The so-modified result strings will be outputted linewise (separated
    by Ascii_13 and Ascii_10). Doing that, each line will be headed
    by a character, that contains the stored (in Step 1) length of the
    original Pascal string plus 32. This Character is mostly a "M",
    i.e. Ascii_77, because 45+32=77.

5.: In order a decoder to be able to realize the beginning and the end
    of a UUEncode block within an e-mail, the Encoder sends a header-line
    before the first UU line, and an end-line after the last.
    Usually, before the end-line the UU-Encoder inserts an additional empty
    line. This empty line can even contain a SPACE character (A Space Char
    (Ascii_32) will be decoded to a string of length 0).
    The end-line contains only the word "end".
    The header-line contains the string  "begin ", followed by a 3-digit-
    Number, a SPACE character and the name of the encoded document.
    The three digits of the Number contain the UNIX-specific rights
    of the encoded Document and are uninteresting in DOS.
    An e-mail can contain several UUEncode blocks in serial.

Example #####################################################################

A Document "TEST.TXT" of the length 8 with the content "dipussi"
  ( stands for Ascii_153) has the UNIX rights "Read" and "Write"
  for all Users. Then the UUEncode block of this Document looks like that:

begin 666 TEST.TXT
(F61I<'5S<VD
end

A UUEncoder ########### as Example ##########################################

  The sense of this Pascal program is, to be short, passably sure, and first
  of all easy to understand. Therefore we have to put up with the fact,
  that the program is not very efficient and, moreover user-unfriendly.
  The program reads a Document and writes the result into a Document named
  "UU.TXT" in the same directory.

program UUENCODE;
USES crt;
VAR
  s,s2,nam,path:string;
  f:file;
  f2:text;
  b1,b2,b3,b4:byte;
  nread,nwrite:word;
BEGIN;
  if paramcount<>1 then begin writeln('Syntax: UUENCODE <filename>');
    write(#10+'Press any Key...'); s:=readkey; halt; end;
  nam:=paramstr(1);
  assign(f,nam); {$I-}reset(f,1){$I+};
  if ioresult<>0 then begin writeln('File "'+nam+'" not found');
    write(#10+'Press any Key...'); s:=readkey; halt; end;
  path:='';
  while pos('\',nam)>0 do begin
    path:=path+copy(nam,1,pos('\',nam)); delete(nam,1,pos('\',nam));
  end;
  assign(f2,path+'UU.TXT'); rewrite(f2);
  writeln(f2,'begin 777 '+nam);
  repeat
    blockread(f,mem[seg(s):ofs(s)+1],45,nread); s[0]:=chr(nread);
    s:=s+#0#0; s2:='';
    nwrite:=nread*4 div 3;
    if nread*4 mod 3>0 then nwrite:=nwrite+nread*4 mod 3+1;
    while length(s)>2 do begin
      b1:=ord(s[1]) shr 2+32;
      b2:=(ord(s[1])and 3)shl 4 + ord(s[2])shr 4+32;
      b3:=(ord(s[2])and 15)shl 2 + ord(s[3])shr 6+32;
      b4:=ord(s[3])and 63+32;
      s2:=s2+chr(b1)+chr(b2)+chr(b3)+chr(b4);
      delete(s,1,3);
    end;
    writeln(f2,chr(nread+32)+copy(s2,1,nwrite));
    gotoxy(1,1); write(filepos(f)*100 div filesize(f),'%');
  until nread<45;
  writeln(f2,'end');
  close(f); close(f2);
END.

A UUEncoder/Decoder ########### as Example ##################################

  This Pascal Program is more comfortable. If you got WIN95 you can drag'n
  drop your mail text file on the program, otherwise you can use it by the
  command line 
  
  UU <filename>

  where <filename> is the text file.
  UU works in 2 directions. If it detects any uuencoded block, it will prompt
  you for decoding. If it detects a line that starts with !PLEASE!UUENCODE!,
  then it will replace this line by a uuencoded block containing the file,
  wich is specified after the command. If it detects both a uuencoded block
  and a !PLEASE!UUENCODE! command, then the program does not know wether it
  shall encode or decode. In that case it returns an error message.
  Example for a !PLEASE!UUENCODE! line:

!PLEASE!UUENCODE!C:\WINDOWS\DESKTOP\TEST.DOC
   
  A file can contain more than one of such command lines.
  In a UUEncode block, this program accepts only DOS compatible file names.
  So if your e-mail contains a block with an incompatible name, for example
  begin 777 index.html
  you should change the filename uing a text editor, before dropping it
  to the UU program.  

  program UU;
  uses crt;
  CONST cmd='!PLEASE!UUENCODE!';
  procedure UUENCODE(nam,d_name:string; msgline:integer);
    var
      s,s2,path:string;
      f:file;
      f2:text;
      b1,b2,b3,b4:byte;
      nread,nwrite:word;
    begin {procedure UUENCODE}
      assign(f,nam); {$I-}reset(f,1){$I+};
      path:='';
      while pos('\',nam)>0 do begin
        path:=path+copy(nam,1,pos('\',nam)); delete(nam,1,pos('\',nam));
      end;
      assign(f2,d_name); append(f2); writeln(f2,'begin 777 '+nam);
      gotoxy(1,msgline); write('     Encoding '+nam); clreol;
      repeat
        blockread(f,mem[seg(s):ofs(s)+1],45,nread); s[0]:=chr(nread);
        s:=s+#0#0; s2:='';
        nwrite:=nread*4 div 3;
        if nread*4 mod 3>0 then nwrite:=nwrite+nread*4 mod 3+1;
        while length(s)>2 do begin
          b1:=ord(s[1]) shr 2+32;
          b2:=(ord(s[1])and 3)shl 4 + ord(s[2])shr 4+32;
          b3:=(ord(s[2])and 15)shl 2 + ord(s[3])shr 6+32;
          b4:=ord(s[3])and 63+32;
          s2:=s2+chr(b1)+chr(b2)+chr(b3)+chr(b4);
          delete(s,1,3);
        end;
        writeln(f2,chr(nread+32)+copy(s2,1,nwrite));
        gotoxy(1,msgline); write(filepos(f)*100 div filesize(f),'%');
      until nread<45; writeln(f2,'end'); close(f); close(f2);
    end; {procedure UUENCODE}
  procedure UUDECODE(nam1,path2,nam2:string; lin1,lin2:longint; msgline:integer);
    var f1,f2:text; s,s2:string; i,k:longint; nr,nw:word; c:char;
      nwrite:integer;
      b1,b2,b3:word;
    begin assign(f1,nam1); reset(f1);
      gotoxy(1,msgline); write('     Decoding '+nam2); clreol;
      for i:=1 to lin1-1 do readln(f1,s);
      assign(f2,path2+nam2); rewrite(f2);
      for i:=lin1 to lin2 do begin
        gotoxy(1,msgline); write((i-lin1)*100 div(lin2-lin1),'%');
        readln(f1,s); if s='' then nwrite:=0 else nwrite:=ord(s[1])-32;
        if nwrite>0 then begin s2:=''; delete(s,1,1);
          for k:=0 to nwrite div 3 do begin
            b1:=(ord(s[k*4+1])-32)shl 2+ (ord(s[k*4+2])-32)shr 4;
            b2:=(ord(s[k*4+2])-32)shl 4+ (ord(s[k*4+3])-32)shr 2;
            b3:=(ord(s[k*4+3])-32)shl 6+ (ord(s[k*4+4])-32);
            s2:=s2+chr(b1 and 255)+chr(b2 and 255)+chr(b3 and 255);
          end;
          s2:=copy(s2,1,nwrite); write(f2,s2);
        end;
      end;
      close(f1); close(f2); end;
  function TESTPATH(path,nam:string):string; var s:string; f:text; c:char;
    begin nam:=copy(nam,1,pos('.',nam))+'ENC';
    getdir(0,s); {$I-}chdir(path+nam){$I-};
    if ioresult=0 then begin chdir(s); writeln('Error: Folder "'+path+nam+
      '" already exists.'); write(#10+'Press any Key...'); c:=readkey; halt;
    end; chdir(s); assign(f,path+nam); {$I-}reset(f){$I-};
    if ioresult=0 then begin close(f); writeln('Error: Folder "'+path+nam+
      '" cannot be created.'); write(#10+'Press any Key...'); c:=readkey;
      halt; end;
    testpath:=path+nam;
  end; {function TESTPATH}
  VAR
    c:char;
    f,fnew:text; fbin,f1,f2:file;
    s,s2,nam,path,newpath:string;
    lines:longint;
    n_uu,n_cmd,p,n,e,i:integer;
    a,a2:array[1..10]of string; al1,al2:array[1..10]of longint;
    buf:array[1..30000]of byte;
    nr,nw:word;
  function DOSNAME(nam:string;nth:integer):string;
    var bad:integer; i:integer; c:char;
    begin if pos('.',nam)<1 then nam:=nam+'.'; bad:=0;
    if length(nam)>12 then bad:=1
    else if pos('.',nam)>9 then bad:=2
    else if pos('.',nam)<(length(nam)-3) then bad:=4
    else begin for i:=1 to length(nam) do
      if not(nam[i]in['!','#'..')','-','.','0'..'9','@'..'Z','^'..'{','}',
        '~',#128..#255]) then bad:=4;
      if pos('.',nam)<length(nam) then
        if pos('.',copy(nam,pos('.',nam)+1,255))>0 then bad:=5;
    end;
    if bad>0 then begin writeln('Error: File "'+nam+'" has a DOS-'+
      'incompatible name. (Err.No.',bad,')'); write(#10+'Press any Key...');
      c:=readkey; halt; end;
    for i:=1 to nth-1 do if a2[i]=nam then begin writeln('Error: File "'+
      nam+'" appears more than 1 time.'); write(#10+'Press any Key...');
      c:=readkey; halt; end;
    dosname:=nam;
  end; {function DOSNAME}
  BEGIN; clrscr;
    if paramcount<>1 then begin writeln('Syntax: UU <filename>');
      write(#10+'Press any Key...'); c:=readkey; halt; end;
    nam:=paramstr(1); p:=pos('.',nam); if p<1 then nam:=nam+'.'; path:='';
    while pos('\',nam)>0 do begin
      path:=path+copy(nam,1,pos('\',nam)); delete(nam,1,pos('\',nam)); end;
    assign(f,path+nam); {$I-}reset(f){$I+};
    if ioresult<>0 then begin writeln('File "'+paramstr(1)+'" not found');
      write(#10+'Press any Key...'); c:=readkey; halt; end;
    lines:=0; n_uu:=0; n_cmd:=0;
    while not eof(f) do begin
      lines:=lines+1; readln(f,s);
      if length(s)=255 then begin writeln('Line ',lines,' too long.');
        write(#10+'Press any Key...'); c:=readkey; close(f); halt; end;
      if odd(n_uu) then begin
        if s='end' then begin n_uu:=n_uu+1; al2[n_uu div 2]:=lines-1;
        end; end
      else begin
        if (pos('begin ',s)=1)and(length(s)>10) then begin
          val(copy(s,7,3),n,e); str(n,s2);
          if (s2=copy(s,7,3))and(s[10]=' ')and(s[11]<>' ') then begin
            delete(s,1,10); n_uu:=n_uu+1; a[n_uu div 2+1]:=s;
            al1[n_uu div 2+1]:=lines+1;
            if n_cmd>0 then begin writeln('Error: File can contain either '+
              'uuencoded blocks'#13#10'or "'+cmd+'" enclosure commands,'+
              ' but not both!');
              write(#10+'Press any Key...'); c:=readkey; close(f); halt; end;
          end; end
        else if (pos(cmd,s)=1)and(length(s)>length(cmd)) then begin
          if n_uu>0 then begin writeln('Error: File can contain either '+
            'uuencoded blocks'#13#10'or "'+cmd+'" enclosure commands,'+
            ' but not both!');
            write(#10+'Press any Key...'); c:=readkey; close(f); halt; end;
          n_cmd:=n_cmd+1; a[n_cmd]:=copy(s,length(cmd)+1,255);
        end;
      end;
    end; close(f); n_uu:=n_uu div 2;
    if n_uu+n_cmd=0 then begin writeln('File does not contain any '+
      'uuencoded block '#13#10'or any "'+cmd+'" enclosure command.');
      write(#10+'Press any Key...'); c:=readkey; end
    else begin 
      if n_cmd>0 then begin
        writeln('File "'+nam+'" contains enclosure commands for the '+
        'following files:'#10);
        for i:=1 to n_cmd do writeln('File',i:2,'  "'+a[i]+'"');
        writeln(#10#10#10);
        for i:=1 to n_cmd do begin
          assign(fbin,a[i]); {$I-}reset(fbin,1){$I-};
          if ioresult=0 then close(fbin) else begin writeln(#13#10+
            'Error: File',i:2,' not found'); write(#10+'Press any Key...');
            c:=readkey; close(f); halt; end; end;
        writeln('Do you want to enclose the contents of these files?');
        writeln('The orig. text file will be backupped as "'+
          copy(nam,1,pos('.',nam))+'BAK"'#10);
        write('Do you confirm? y'); gotoxy(wherex-1,wherey); c:=readkey;
        if c in ['Y','y','J','j',' ',#13]then begin
          writeln('Yes'#10#10); gotoxy(1,wherey-1);
          assign(f1,path+nam); reset(f1,1);
          assign(f2,path+copy(nam,1,pos('.',nam))+'BAK');
          {$I-}rewrite(f2,1){$I+};
          if ioresult<>0 then begin writeln('File "'+copy(nam,1,pos('.',nam))
            +'BAK" could not be created.'); write(#10+'Press any Key...');
            c:=readkey; close(f1); halt; end;
          repeat blockread(f1,buf,sizeof(buf),nr);
            if nr>0 then blockwrite(f2,buf,nr,nw);
          until nr=0; close(f1); close(f2);
          assign(f,path+copy(nam,1,pos('.',nam))+'BAK'); {$I-}reset(f){$I+};
          if ioresult<>0 then begin writeln('Error'); write(#10+'Press any '+
            'Key...'); c:=readkey; halt; end;
          assign(fnew,path+nam); rewrite(fnew);
          while not eof(f) do begin readln(f,s);
            if (pos(cmd,s)=1)and(length(s)>length(cmd)) then begin
              close(fnew);
              uuencode(copy(s,length(cmd)+1,255),path+nam,wherey); writeln;
              assign(fnew,path+nam); append(fnew); end
            else writeln(fnew,s);
          end; close(f); close(fnew);
        end else writeln('No'); end
      else begin
        newpath:=testpath(path,nam);
        for i:=1 to n_uu do a2[i]:=dosname(a[i],i);
        writeln('File "'+nam+'" contains the following uuencoded blocks:'#10);
        for i:=1 to n_uu do begin writeln('File',i:2,'  "'+a[i]+'"');
          writeln('  save as "'+newpath+'\'+a2[i]+'"'); end;
        write(#10'Do you confirm? y'); gotoxy(wherex-1,wherey); c:=readkey;
        if c in ['Y','y','J','j',' ',#13]then begin
          writeln('Yes'#10#10); gotoxy(1,wherey-1);
          {$I-}mkdir(newpath){$I+};
          if ioresult<>0 then begin writeln('Folder "'+newpath+'" could '+
            'not be created.'); write(#10+'Press any Key...');
            c:=readkey; halt; end;
          for i:=1 to n_uu do begin
            uudecode(path+nam,newpath+'\',a2[i],al1[i],al2[i],wherey);
            writeln;
          end;
        end else writeln('No');
      end;
    end;
  END.

This Document ###############################################################

  HTTP://WWW.SERVE.COM/JB/UU_E.TXT was created by Joerg Buchwitz in
    March, 1996 in German and then translated in 04/96.
  The document is available on JBs HomePage http://www.serve.com/jb
