* reordered a little bit
[mascara-docs.git] / i86 / elks / elkscmd / sash / cmd_tar.c
blob7ecc38e15dbb528adcaaabd06120e2ffea76f9c7
1 /*
2 * Copyright (c) 1993 by David I. Bell
3 * Permission is granted to use, distribute, or modify this source,
4 * provided that this copyright notice remains intact.
6 * The "tar" built-in command.
7 */
9 #include "sash.h"
10 #ifdef CMD_TAR
12 #include <sys/types.h>
13 #include <sys/stat.h>
17 * Tar file format.
19 #define TBLOCK 512
20 #define NAMSIZ 100
22 union hblock {
23 char dummy[TBLOCK];
24 struct header {
25 char name[NAMSIZ];
26 char mode[8];
27 char uid[8];
28 char gid[8];
29 char size[12];
30 char mtime[12];
31 char chksum[8];
32 char linkflag;
33 char linkname[NAMSIZ];
34 char extno[4];
35 char extotal[4];
36 char efsize[12];
37 } dbuf;
38 } dblock;
41 static BOOL inheader;
42 static BOOL badheader;
43 static BOOL badwrite;
44 static BOOL extracting;
45 static BOOL warnedroot;
46 static BOOL eof;
47 static BOOL verbose;
48 static long datacc;
49 static int outfd;
50 static char outname[NAMSIZ];
52 static void doheader();
53 static void dodata();
54 static void createpath();
55 static long getoctal();
58 void
59 do_tar(argc, argv)
60 char **argv;
62 char *str;
63 char *devname;
64 char *cp;
65 int devfd;
66 int cc;
67 long incc;
68 int blocksize;
69 BOOL createflag;
70 BOOL listflag;
71 BOOL fileflag;
72 char buf[8192];
74 if (argc < 2) {
75 fprintf(stderr, "Too few arguments for tar\n");
76 return;
79 createflag = FALSE;
80 extracting = FALSE;
81 listflag = FALSE;
82 fileflag = FALSE;
83 verbose = FALSE;
84 badwrite = FALSE;
85 badheader = FALSE;
86 warnedroot = FALSE;
87 eof = FALSE;
88 inheader = TRUE;
89 incc = 0;
90 datacc = 0;
91 outfd = -1;
92 blocksize = sizeof(buf);
94 for (str = argv[1]; *str; str++) {
95 switch (*str) {
96 case 'f': fileflag = TRUE; break;
97 case 't': listflag = TRUE; break;
98 case 'x': extracting = TRUE; break;
99 case 'v': verbose = TRUE; break;
101 case 'c':
102 case 'a':
103 fprintf(stderr, "Writing is not supported\n");
104 return;
106 default:
107 fprintf(stderr, "Unknown tar flag\n");
108 return;
112 if (!fileflag) {
113 fprintf(stderr, "The 'f' flag must be specified\n");
114 return;
117 if (argc < 3) {
118 fprintf(stderr, "Missing input name\n");
119 return;
121 devname = argv[2];
123 if (extracting + listflag != 1) {
124 fprintf(stderr, "Exactly one of 'x' or 't' must be specified\n");
125 return;
128 devfd = open(devname, 0);
129 if (devfd < 0) {
130 perror(devname);
131 return;
134 while (TRUE) {
135 if ((incc == 0) && !eof) {
136 while (incc < blocksize) {
137 cc = read(devfd, &buf[incc], blocksize - incc);
138 if (cc < 0) {
139 perror(devname);
140 goto done;
143 if (cc == 0)
144 break;
146 incc += cc;
148 cp = buf;
151 if (intflag) {
152 if (extracting && (outfd >= 0))
153 close(outfd);
154 close(devfd);
155 return;
158 if (inheader) {
159 if ((incc == 0) || eof)
160 goto done;
162 if (incc < TBLOCK) {
163 fprintf(stderr, "Short block for header\n");
164 goto done;
167 doheader((struct header *) cp);
169 cp += TBLOCK;
170 incc -= TBLOCK;
172 continue;
175 cc = incc;
176 if (cc > datacc)
177 cc = datacc;
179 dodata(cp, cc);
181 if (cc % TBLOCK)
182 cc += TBLOCK - (cc % TBLOCK);
184 cp += cc;
185 incc -= cc;
188 done:
189 close(devfd);
193 static void
194 doheader(hp)
195 struct header *hp;
197 int mode;
198 int uid;
199 int gid;
200 int chksum;
201 long size;
202 long mtime;
203 char *name;
204 int cc;
205 BOOL hardlink;
206 BOOL softlink;
209 * If the block is completely empty, then this is the end of the
210 * archive file. If the name is null, then just skip this header.
212 name = hp->name;
213 if (*name == '\0') {
214 for (cc = TBLOCK; cc > 0; cc--) {
215 if (*name++)
216 return;
219 eof = TRUE;
220 return;
223 mode = getoctal(hp->mode, sizeof(hp->mode));
224 uid = getoctal(hp->uid, sizeof(hp->uid));
225 gid = getoctal(hp->gid, sizeof(hp->gid));
226 size = getoctal(hp->size, sizeof(hp->size));
227 mtime = getoctal(hp->mtime, sizeof(hp->mtime));
228 chksum = getoctal(hp->chksum, sizeof(hp->chksum));
230 if ((mode < 0) || (uid < 0) || (gid < 0) || (size < 0)) {
231 if (!badheader)
232 fprintf(stderr, "Bad tar header, skipping\n");
233 badheader = TRUE;
234 return;
237 badheader = FALSE;
238 badwrite = FALSE;
240 hardlink = ((hp->linkflag == 1) || (hp->linkflag == '1'));
241 softlink = ((hp->linkflag == 2) || (hp->linkflag == '2'));
243 if (name[strlen(name) - 1] == '/')
244 mode |= S_IFDIR;
245 else if ((mode & S_IFMT) == 0)
246 mode |= S_IFREG;
248 if (*name == '/') {
249 while (*name == '/')
250 name++;
252 if (!warnedroot)
253 fprintf(stderr, "Absolute paths detected, removing leading slashes\n");
254 warnedroot = TRUE;
257 if (!extracting) {
258 if (verbose)
259 printf("%s %3d/%-d %9d %s %s", modestring(mode),
260 uid, gid, size, timestring(mtime), name);
261 else
262 printf("%s", name);
264 if (hardlink)
265 printf(" (link to \"%s\")", hp->linkname);
266 else if (softlink)
267 printf(" (symlink to \"%s\")", hp->linkname);
268 else if (S_ISREG(mode)) {
269 inheader = (size == 0);
270 datacc = size;
273 printf("\n");
274 return;
277 if (verbose)
278 printf("x %s\n", name);
281 if (hardlink) {
282 if (link(hp->linkname, name) < 0)
283 perror(name);
284 return;
287 if (softlink) {
288 #ifdef S_ISLNK
289 if (symlink(hp->linkname, name) < 0)
290 perror(name);
291 #else
292 fprintf(stderr, "Cannot create symbolic links\n");
293 #endif
294 return;
297 if (S_ISDIR(mode)) {
298 createpath(name, mode);
299 return;
302 createpath(name, 0777);
304 inheader = (size == 0);
305 datacc = size;
307 outfd = creat(name, mode);
308 if (outfd < 0) {
309 perror(name);
310 badwrite = TRUE;
311 return;
314 if (size == 0) {
315 close(outfd);
316 outfd = -1;
323 * Handle a data block of some specified size.
325 static void
326 dodata(cp, count)
327 char *cp;
329 int cc;
331 datacc -= count;
332 if (datacc <= 0)
333 inheader = TRUE;
335 if (badwrite || !extracting)
336 return;
338 while (count > 0) {
339 cc = write(outfd, cp, count);
340 if (cc < 0) {
341 perror(outname);
342 close(outfd);
343 outfd = -1;
344 badwrite = TRUE;
345 return;
347 count -= cc;
350 if (datacc <= 0) {
351 if (close(outfd))
352 perror(outname);
353 outfd = -1;
359 * Attempt to create the directories along the specified path, except for
360 * the final component. The mode is given for the final directory only,
361 * while all previous ones get default protections. Errors are not reported
362 * here, as failures to restore files can be reported later.
364 static void
365 createpath(name, mode)
366 char *name;
368 char *cp;
369 char *cpold;
370 char buf[NAMSIZ];
372 strcpy(buf, name);
374 cp = strchr(buf, '/');
376 while (cp) {
377 cpold = cp;
378 cp = strchr(cp + 1, '/');
380 *cpold = '\0';
381 if (mkdir(buf, cp ? 0777 : mode) == 0)
382 printf("Directory \"%s\" created\n", buf);
384 *cpold = '/';
390 * Read an octal value in a field of the specified width, with optional
391 * spaces on both sides of the number and with an optional null character
392 * at the end. Returns -1 on an illegal format.
394 static long
395 getoctal(cp, len)
396 char *cp;
398 long val;
400 while ((len > 0) && (*cp == ' ')) {
401 cp++;
402 len--;
405 if ((len == 0) || !isoctal(*cp))
406 return -1;
408 val = 0;
409 while ((len > 0) && isoctal(*cp)) {
410 val = val * 8 + *cp++ - '0';
411 len--;
414 while ((len > 0) && (*cp == ' ')) {
415 cp++;
416 len--;
419 if ((len > 0) && *cp)
420 return -1;
422 return val;
424 #endif /* CMD_TAR */
426 /* END CODE */