1# read list of pathnames on stdin, write POSIX.1 tar on stdout 2# Copyright(c)1996 Lucent Technologies. All Rights Reserved. 3# 22 Dec 1996 ehg@bell-labs.com 4 5implement puttar; 6include "sys.m"; 7 sys: Sys; 8 print, sprint, fprint: import sys; 9 stdout, stderr: ref sys->FD; 10include "draw.m"; 11include "bufio.m"; 12 bufio: Bufio; 13 Iobuf: import bufio; 14 15puttar: module{ 16 init: fn(nil: ref Draw->Context, nil: list of string); 17}; 18 19Warning(mess: string) 20{ 21 fprint(stderr,"warning: puttar: %s: %r\n",mess); 22} 23 24Error(mess: string){ 25 fprint(stderr,"puttar: %s: %r\n",mess); 26 exit; 27} 28 29TBLOCK: con 512; # tar logical blocksize 30NBLOCK: con 20; # blocking factor for efficient write 31tarbuf := array[NBLOCK*TBLOCK] of byte; # for output 32nblock := 0; # how many blocks of data are in tarbuf 33 34flushblocks(){ 35 if(nblock<=0) return; 36 if(nblock<NBLOCK){ 37 for(i:=(nblock+1)*TBLOCK;i<NBLOCK*TBLOCK;i++) 38 tarbuf[i] = byte 0; 39 } 40 i := sys->write(stdout,tarbuf,NBLOCK*TBLOCK); 41 if(i!=NBLOCK*TBLOCK) 42 Error("write error"); 43 nblock = 0; 44} 45 46putblock(data:array of byte){ 47 # all writes are done through here, so we can guarantee 48 # 10kbyte blocks if writing to tape device 49 if(len data!=TBLOCK) 50 Error("putblock wants TBLOCK chunks"); 51 tarbuf[nblock*TBLOCK:] = data; 52 nblock++; 53 if(nblock>=NBLOCK) 54 flushblocks(); 55} 56 57packname(hdr:array of byte, name:string){ 58 utf := array of byte name; 59 n := len utf; 60 if(n<=100){ 61 hdr[0:] = utf; 62 return; 63 } 64 for(i:=n-101; i<n && int utf[i] != '/'; i++){} 65 if(i==n) Error(sprint("%s > 100 bytes",name)); 66 if(i>155) Error(sprint("%s too long\n",name)); 67 hdr[0:] = utf[i+1:n]; 68 hdr[345:] = utf[0:i]; # tar supplies implicit slash 69} 70 71octal(width:int, val:int):array of byte{ 72 octal := array of byte "01234567"; 73 a := array[width] of byte; 74 for(i:=width-1; i>=0; i--){ 75 a[i] = octal[val&7]; 76 val >>= 3; 77 } 78 return a; 79} 80 81chksum(hdr: array of byte):int{ 82 sum := 0; 83 for(i:=0; i<len hdr; i++) 84 sum += int hdr[i]; 85 return sum; 86} 87 88hdr, zeros, ibuf : array of byte; 89 90tar(file : string) 91{ 92 ifile: ref sys->FD; 93 94 (rc,stat) := sys->stat(file); 95 if(rc<0){ Warning(sprint("cannot stat %s",file)); return; }; 96 ifile = sys->open(file,sys->OREAD); 97 if(ifile==nil) Error(sprint("cannot open %s",file)); 98 hdr[0:] = zeros; 99 packname(hdr,file); 100 hdr[100:] = octal(7,stat.mode&8r777); 101 hdr[108:] = octal(7,1); 102 hdr[116:] = octal(7,1); 103 hdr[124:] = octal(11,int stat.length); 104 hdr[136:] = octal(11,stat.mtime); 105 hdr[148:] = array of byte " "; # for chksum 106 hdr[156] = byte '0'; 107 if(stat.mode&Sys->DMDIR) hdr[156] = byte '5'; 108 hdr[257:] = array of byte "ustar"; 109 hdr[263:] = array of byte "00"; 110 hdr[265:] = array of byte stat.uid; # assumes len uid<=32 111 hdr[297:] = array of byte stat.gid; 112 hdr[329:] = octal(8,stat.dev); 113 hdr[337:] = octal(8,int stat.qid.path); 114 hdr[148:] = octal(7,chksum(hdr)); 115 hdr[155] = byte 0; 116 putblock(hdr); 117 for(bytes := int stat.length; bytes>0;){ 118 n := len ibuf; if(n>bytes) n = bytes; # min 119 if(sys->read(ifile,ibuf,n)!=n) 120 Error(sprint("read error on %s",file)); 121 nb := (n+TBLOCK-1)/TBLOCK; 122 fill := nb*TBLOCK; 123 for(i:=n; i<fill; i++) ibuf[i] = byte 0; 124 for(i=0; i<nb; i++) 125 putblock(ibuf[i*TBLOCK:(i+1)*TBLOCK]); 126 bytes -= n; 127 } 128 ifile = nil; 129} 130 131rtar(file : string) 132{ 133 tar(file); 134 # recurse if directory 135 (ok, dir) := sys->stat(file); 136 if (ok < 0){ 137 Warning(sprint("cannot stat %s", file)); 138 return; 139 } 140 if (dir.mode & Sys->DMDIR) { 141 fd := sys->open(file, sys->OREAD); 142 if (fd == nil) 143 Error(sprint("cannot open %s", file)); 144 for (;;) { 145 (n, d) := sys->dirread(fd); 146 if (n <= 0) 147 break; 148 for (i := 0; i < n; i++) { 149 if (file[len file - 1] == '/') 150 rtar(file + d[i].name); 151 else 152 rtar(file + "/" + d[i].name); 153 } 154 } 155 } 156} 157 158init(nil: ref Draw->Context, args: list of string){ 159 sys = load Sys Sys->PATH; 160 bufio = load Bufio Bufio->PATH; 161 stdout = sys->fildes(1); 162 stderr = sys->fildes(2); 163 164 hdr = array[TBLOCK] of byte; 165 zeros = array[TBLOCK] of {* => byte 0}; 166 ibuf = array[len tarbuf] of byte; 167 168 if (tl args == nil) { 169 stdin := bufio->fopen(sys->fildes(0),bufio->OREAD); 170 if(stdin==nil) Error("can't fopen stdin"); 171 while((file := stdin.gets('\n'))!=nil){ 172 if(file[len file-1]=='\n') file = file[0:len file-1]; 173 tar(file); 174 } 175 } 176 else { 177 for (args = tl args; args != nil; args = tl args) 178 rtar(hd args); 179 } 180 putblock(zeros); 181 putblock(zeros); # format requires two empty blocks at end 182 flushblocks(); 183} 184