Datei UU_D.TXT - Das UUEncode-Format JB ----------------------------------------------------------------------- Das UUencode-Format ################################################### UUEncode (sprich Ju-Ju-Encode) ist ein Verfahren, um 8-Bit-Daten so umzusetzen, dass das Ergebnis nur Zeichen aus dem Bereich 32 bis 95 enthaelt. Dies erfolgt in sieben Schritten: 1.: Einlesen der Datei in Pascal-Strings der maximalen Laenge 45 (Alle bis auf den letzten String sind 45 lang, der letzte ist meist kuerzer als 45). Der Encoder merkt sich die Laenge des jeweils eingelesenen Pascal-Strings. 2.: Umwandeln der 8-Bit-Daten der einzelnen Strings in 6-Bit-Daten. Dazu werden die Strings in Gruppen vom je 3 Bytes aufgeteilt. Diese 3 Bytes werden dann zu jeweils 4 Bytes konvertiert. Beispiel: aus 11110001 11110001 11110001 (hex. F1 F1 F1) | | \ | \ | \ \ | | \ | \ | \ \ wird 111100 011111 000111 110001 | | | | | | | | und dann 00111100 00011111 00000111 00110001 (hex. 3C 1F 07 31) Da alle Strings die Laenge 45 haben, bereitet die Aufteilung in 3-Byte-Gruppen keine Probleme. Eine Ausnahme bildet der letzte String. Dieser ist meist kuerzer als 45 und nicht durch 3 teilbar. Die letzte Gruppe besteht dann nur aus 2 oder 1 Byte. Diese werden konvertiert zu 3 bzw. 2 Bytes. Beispiel fuer 2-Byte-Restgruppe: aus 11110001 11110001 (hex. F1 F1) | | \ | \ \ | | \ | \ \ wird 111100 011111 000100 | | | | | | und dann 00111100 00011111 00000100 (hex. 3C 1F 04) Beispiel fuer 1-Byte-Restgruppe: aus 11110001 (hex. F1) | | \\ | | \\ wird 111100 010000 | | | | und dann 00111100 00010000 (hex. 3C 10) 3.: Die einzelnen Bytes der Ergebnis-Strings liegen jetzt im Bereich 0 bis 63 (00000000 bis 00111111). Damit keine Werte unter 32 mehr vorkommen, werden nun zu jedem Byte 32 addiert. Beispiel: aus 00111100 00011111 00000111 00110001 wird 01011100 00111111 00100111 01010001 4.: Die so modifizierten Ergebnis-Strings werden zeilenweise (getrennt durch Ascii_13 und Ascii_10) ausgegeben. Dabei wird jeder Zeile ein Zeichen vorangestellt, das die oben gemerkte Laenge des urspruenglichen Pascal-Strings plus 32 enthaelt. Dieses Zeichen ist meist ein "M", also Ascii_77, denn 45+32=77. 5.: Damit der Decoder in einer E-Mail den Beginn und das Ende eines UUENCODE-Blocks erkennt, wird vor der ersten Zeile eine Header- Zeile und hinterher eine end-Zeile gesendet. Meist wird vor der end-Zeile noch eine Leerzeile eingefuegt, die auch ein einzelnes Leerzeichen enthalten darf (Ein Leerzeichen (Ascii_32) ergibt einen String der Laenge 0). Die end-Zeile hat den Inhalt "end". Die Header-Zeile hat den Inhalt "begin ", gefolgt von einer drei- stelligen Zahl, einem Leerzeichen und dem Namen der verschlues- selten Datei. Die drei Stellen der Zahl enthalten die UNIX- spezifischen Rechte der verschluesselten Datei und sind unter DOS bzw. Windows uninteressant. Es empfiehlt sich, die 777 zu benutzen. Eine Mail kann nacheinander mehrere UUENCUDE-Bloecke enthalten. Beispiel ############################################################## Eine Datei "TEST.TXT" der Laenge 8 mit dem Inhalt "™dipussi" (™ steht fuer Ascii_153) habe die UNIX-Rechte 666. Das bedeutet "Lesen" und "Schreiben" ist fuer alle Benutzer erlaubt. Der UUENCODE-Block dieser Datei sieht dann so aus: begin 666 TEST.TXT (F61I<'5S1 then begin writeln('Syntax: UUENCODE '); 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. Ein UUEncoder/Decoder ########### als Beispiel ############################## Dieses Pascal-Programm ist komfortabler. Mit WIN95 kann man sogar ein Mail-Textfile per Drag and Drop auf das Programm ziehen, ansonsten muss man sich mit der Eingabe der Kommandozeile UU zufriedengeben, wobei das Textfile ist. UU arbeitet in beiden Richtungen. Wenn es einen uugeencodeten Block (oder wie auch immer man das auf deutsch sagt) findet, fragt es, ob er decodiert werden soll. Wenn es jedoch eine Zeile findet, die mit !PLEASE!UUENCODE! beginnt, dann ersetzt es diese Zeile duech einen UUEncode-Block der die Datei enthaelt, die nach diesem Kommando angegeben ist. Findet das Programm in einem Textfile sowohl einen UUEncode-Block, als auch ein !PLEASE!UUENCODE!-Kommando, dann wiss es nicht, ob es nun en- oder decoden soll, und bringt eine Fehlermeldung. Beispiel fuer eine !PLEASE!UUENCODE!-Zeile: !PLEASE!UUENCODE!C:\WINDOWS\DESKTOP\TEST.DOC In einer Text-Datei duerfen mehrere solcher Kommando-Zeilen sein. In UUEncode-Bloecken, akzeptiert das Programm nur DOS-kompatible Dateinamen. Wenn also eine e-Mail einen Block mit einem inkompatiblen Namen, z.B. begin 777 index.html enthaelt, sollte man diesen vorher mit Hilfe eines Text-Editors aendern. 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)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 '); 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. Diese Datei ################################################################# HTTP://WWW.SERVE.COM/JB/UU_D.TXT wurde erstellt von Joerg Buchwitz im Maerz 1996. Letzte Aenderung: 09/96. Die Datei ist verfuegbar auf JBs HomePage http://www.serve.com/jb