1implement lstar; 2 3include "sys.m"; 4 sys: Sys; 5 print, sprint, fprint: import sys; 6 stdin, stderr: ref sys->FD; 7include "draw.m"; 8 9TBLOCK: con 512; # tar logical blocksize 10Header: adt{ 11 name: string; 12 size: int; 13 mtime: int; 14 skip: int; 15}; 16 17lstar: module{ 18 init: fn(nil: ref Draw->Context, nil: list of string); 19}; 20 21Error(mess: string){ 22 fprint(stderr,"lstar: %s: %r\n",mess); 23 exit; 24} 25 26 27NBLOCK: con 20; # blocking factor for efficient read 28tarbuf := array[NBLOCK*TBLOCK] of byte; # static buffer 29nblock := NBLOCK; # how many blocks of data are in tarbuf 30recno := NBLOCK; # how many blocks in tarbuf have been consumed 31getblock():array of byte{ 32 if(recno>=nblock){ 33 i := sys->read(stdin,tarbuf,TBLOCK*NBLOCK); 34 if(i==0) 35 return tarbuf[0:0]; 36 if(i<0) 37 Error("read error"); 38 if(i%TBLOCK!=0) 39 Error("blocksize error"); 40 nblock = i/TBLOCK; 41 recno = 0; 42 } 43 recno++; 44 return tarbuf[(recno-1)*TBLOCK:recno*TBLOCK]; 45} 46 47octal(b:array of byte):int{ 48 sum := 0; 49 for(i:=0; i<len b; i++){ 50 bi := int b[i]; 51 if(bi==' ') continue; 52 if(bi==0) break; 53 sum = 8*sum + bi-'0'; 54 } 55 return sum; 56} 57 58nullterm(b:array of byte):string{ 59 for(i:=0; i<len b; i++) 60 if(b[i]==byte 0) break; 61 return string b[0:i]; 62} 63 64getdir():ref Header{ 65 dblock := getblock(); 66 if(len dblock==0) 67 return nil; 68 if(dblock[0]==byte 0) 69 return nil; 70 71 name := nullterm(dblock[0:100]); 72 if(int dblock[345]!=0) 73 name = nullterm(dblock[345:500])+"/"+name; 74 75 magic := string(dblock[257:262]); 76 if(magic[0]!=0 && magic!="ustar") 77 Error("bad magic "+name); 78 chksum := octal(dblock[148:156]); 79 for(ci:=148; ci<156; ci++) dblock[ci] = byte ' '; 80 for(i:=0; i<TBLOCK; i++) 81 chksum -= int dblock[i]; 82 if(chksum!=0) 83 Error("directory checksum error "+name); 84 85 skip := 1; 86 size := 0; 87 mtime := 0; 88 case int dblock[156]{ 89 '0' or '5' or '7' or 0 => 90 skip = 0; 91 size = octal(dblock[124:136]); 92 mtime = octal(dblock[136:148]); 93 '1' => 94 fprint(stderr,"skipping link %s -> %s\n",name,string(dblock[157:257])); 95 '2' or 's' => 96 fprint(stderr,"skipping symlink %s\n",name); 97 '3' or '4' or '6' => 98 fprint(stderr,"skipping special file %s\n",name); 99 * => 100 Error(sprint("unrecognized typeflag %d for %s",int dblock[156],name)); 101 } 102 return ref Header(name,size,mtime,skip); 103} 104 105 106init(nil: ref Draw->Context, nil: list of string){ 107 sys = load Sys Sys->PATH; 108 stdin = sys->fildes(0); 109 stderr = sys->fildes(2); 110 ofile: ref sys->FD; 111 112 while((file := getdir())!=nil){ 113 bytes := file.size; 114 blocks := (bytes+TBLOCK-1)/TBLOCK; 115 for(; blocks>0; blocks--) 116 getblock(); 117 print("%s %d %d 0\n",file.name,file.mtime,file.size); 118 ofile = nil; 119 } 120} 121