xref: /inferno-os/appl/cmd/lstar.b (revision 37da2899f40661e3e9631e497da8dc59b971cbd0)
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