Bell 32V release
[unix-history] / usr / src / cmd / cpio.c
CommitLineData
8f731832
DH
1/* cpio COMPILE: cc -O cpio.c -s -i -o cpio -lS
2 cpio -- copy file collections
3
4*/
5#include <stdio.h>
6#include <sys/types.h>
7#include <sys/stat.h>
8#include <signal.h>
9#define EQ(x,y) (strcmp(x,y)==0)
10/* for VAX, Interdata, ... */
11#define MKSHORT(v,lv) {U.l=1L;if(U.c[0]) U.l=lv,v[0]=U.s[1],v[1]=U.s[0]; else U.l=lv,v[0]=U.s[0],v[1]=U.s[1];}
12#define MAGIC 070707
13#define FILETYPE 060000
14#define IN 1
15#define OUT 2
16#define PASS 3
17#define HDRSIZE ((sizeof Hdr)-256)
18#define LINKS 1000
19
20struct stat Statb, Xstatb;
21
22struct header {
23 short h_magic,
24 h_dev,
25 h_ino,
26 h_mode,
27 h_uid,
28 h_gid,
29 h_nlink,
30 h_rdev;
31 short h_mtime[2];
32 short h_namesize;
33 short h_filesize[2];
34 char h_name[256];
35} Hdr;
36
37int Bufsize = 512;
38short Buf[256], *Dbuf;
39int Wct;
40short *Wp;
41
42short Option,
43 Dir,
44 Uncond,
45 Link,
46 Rename,
47 Toc,
48 Verbose,
49 Select,
50 Mod_time,
51 Swap;
52
53int Ifile,
54 Ofile,
55 Input = 0,
56 Output = 1;
57long Blocks;
58
59char Fullname[256],
60 Name[256];
61int Pathend;
62
63FILE *Rtty,
64 *Wtty;
65
66char *Pattern;
67short Dev,
68 Uid,
69 Gid,
70 A_directory,
71 A_special;
72extern errno;
73char *malloc();
74char *cd();
75char *Cd_name;
76FILE *popen();
77
78union { long l; short s[2]; char c[4]; } U;
79
80/* for VAX, Interdata, ... */
81long mklong(v)
82short v[];
83{
84 U.l = 1;
85 if(U.c[0])
86 U.s[0] = v[1], U.s[1] = v[0];
87 else
88 U.s[0] = v[0], U.s[1] = v[1];
89 return U.l;
90}
91
92main(argc, argv)
93char **argv;
94{
95 register ct;
96 long filesz;
97 register char *lastarg, *fullp;
98
99 if(argc < 2 || argc > 4) {
100usage:
101 err("Usage: cpio -o[vB] <name-list >collection\n%s\n%s\n",
102 " cpio -i[drstuvB] [pattern] <collection",
103 " cpio -p[dlruv] [pattern] directory <name-list");
104 exit(1);
105 }
106 signal(SIGSYS, 1);
107 lastarg = argv[argc-1];
108 if(*argv[1] != '-')
109 goto usage;
110 Uid = getuid();
111 umask(0);
112 Gid = getgid();
113
114 while(*++argv[1]) {
115 switch(*argv[1]) {
116 case 'B':
117 Bufsize = 5120;
118 break;
119 case 'i':
120 Option = IN;
121 Pattern = argc == 2? "*": argv[2];
122 break;
123 case 'o':
124 Option = OUT;
125 break;
126 case 'p':
127 if(access(lastarg, 2) == -1) {
128accerr:
129 err("cannot write in <%s>\n", lastarg);
130 exit(1);
131 }
132 strcpy(Fullname, lastarg);
133 strcat(Fullname, "/");
134 stat(Fullname, &Xstatb);
135 if((Xstatb.st_mode&S_IFMT) != S_IFDIR)
136 goto accerr;
137 Option = PASS;
138 Dev = Xstatb.st_dev;
139 Pattern = argc == 3? "*": argv[2];
140 break;
141 case 'd':
142 Dir++;
143 break;
144 case 'l':
145 Link++;
146 break;
147 case 'm':
148 Mod_time++;
149 break;
150 case 'r':
151 Rename++;
152 Rtty = fopen("/dev/tty", "r");
153 Wtty = fopen("/dev/tty", "w");
154 if(Rtty==NULL || Wtty==NULL) {
155 err(
156 "Cannot rename (/dev/tty missing)\n");
157 exit(1);
158 }
159 break;
160 case 's':
161 Swap++;
162 break;
163 case 't':
164 Toc++;
165 break;
166 case 'u':
167 Uncond++;
168 break;
169 case 'v':
170 Verbose++;
171 break;
172 default:
173 goto usage;
174 }
175 }
176 if(!Option) {
177 err("Options must include o|i|p\n");
178 exit(1);
179 }
180
181 if(Option != PASS)
182 Wp = Dbuf = malloc(Bufsize);
183 Wct = Bufsize >> 1;
184
185 if(Option == PASS && Rename) {
186 err("Pass and Rename cannot be used together");
187 exit(1);
188 }
189 switch(Option) {
190
191 case OUT:
192 while(getname()) {
193 if(mklong(Hdr.h_filesize) == 0L) {
194 bwrite(&Hdr, HDRSIZE+Hdr.h_namesize);
195 continue;
196 }
197 if((Ifile = open(Hdr.h_name, 0)) < 0) {
198 err("<%s> ?\n", Hdr.h_name);
199 continue;
200 }
201 bwrite(&Hdr, HDRSIZE+Hdr.h_namesize);
202 for(filesz=mklong(Hdr.h_filesize); filesz>0; filesz-= 512){
203 ct = filesz>512? 512: filesz;
204 if(read(Ifile, Buf, ct) < 0) {
205 err("Cannot read %s\n", Hdr.h_name);
206 continue;
207 }
208 bwrite(Buf, ct);
209 }
210 close(Ifile);
211 if(Verbose)
212 err("%s\n", Hdr.h_name);
213 }
214 strcpy(Hdr.h_name, "TRAILER!!!");
215 MKSHORT(Hdr.h_filesize, 0L);
216 Hdr.h_namesize = strlen("TRAILER!!!") + 1;
217 bwrite(&Hdr, HDRSIZE+Hdr.h_namesize);
218 bwrite(Dbuf, Bufsize);
219 break;
220
221 case IN:
222 pwd();
223 while(gethdr()) {
224 Ofile = ckname(Hdr.h_name)? openout(Hdr.h_name): 0;
225 for(filesz=mklong(Hdr.h_filesize); filesz>0; filesz-= 512){
226 ct = filesz>512? 512: filesz;
227 bread(Buf, ct);
228 if(Ofile) {
229 if(Swap)
230 swap(Buf, ct);
231 if(write(Ofile, Buf, ct) < 0) {
232 err("Cannot write %s\n", Hdr.h_name);
233 continue;
234 }
235 }
236 }
237 if(Ofile) {
238 close(Ofile);
239 set_time(Cd_name, mklong(Hdr.h_mtime), mklong(Hdr.h_mtime));
240 }
241 if(!Select)
242 continue;
243 if(Verbose)
244 if(Toc)
245 pentry(Hdr.h_name);
246 else
247 puts(Hdr.h_name);
248 else if(Toc)
249 puts(Hdr.h_name);
250 }
251 break;
252
253 case PASS:
254 fullp = Fullname + strlen(Fullname);
255
256 while(getname()) {
257 if(!ckname(Hdr.h_name))
258 continue;
259 strcpy(fullp, Hdr.h_name);
260
261 if(Link
262 && !A_directory
263 && Dev == Statb.st_dev
264 && (Uid == Statb.st_uid || !Uid)) {
265 unlink(Fullname);
266 if(link(Hdr.h_name, Fullname) < 0) {
267 err(
268 "Cannot link <%s> & <%s>\n",
269 Hdr.h_name, Fullname);
270 continue;
271 }
272 set_time(Hdr.h_name, mklong(Hdr.h_mtime), mklong(Hdr.h_mtime));
273 goto ckverbose;
274 }
275 if(!(Ofile = openout(Fullname)))
276 continue;
277 if((Ifile = open(Hdr.h_name, 0)) < 0) {
278 err("<%s> ?\n", Hdr.h_name);
279 close(Ofile);
280 continue;
281 }
282 filesz = Statb.st_size;
283 for(; filesz > 0; filesz -= 512) {
284 ct = filesz>512? 512: filesz;
285 if(read(Ifile, Buf, ct) < 0) {
286 err("Cannot read %s\n", Hdr.h_name);
287 break;
288 }
289 if(Ofile)
290 if(write(Ofile, Buf, ct) < 0) {
291 err("Cannot write %s\n", Hdr.h_name);
292 break;
293 }
294 ++Blocks;
295 }
296 close(Ifile);
297 if(Ofile) {
298 close(Ofile);
299 set_time(Fullname, Statb.st_atime, mklong(Hdr.h_mtime));
300ckverbose:
301 if(Verbose)
302 puts(Fullname);
303 }
304 }
305 }
306 err("%D blocks\n", Blocks * (Bufsize>>9));
307 exit(0);
308}
309
310getname()
311{
312 register char *namep = Name;
313 static long tlong;
314
315 for(;;) {
316 if(gets(namep) == NULL)
317 return 0;
318 if(*namep == '.' && namep[1] == '/')
319 namep += 2;
320 strcpy(Hdr.h_name, namep);
321 if(stat(namep, &Statb) < 0) {
322 err("< %s > ?\n", Hdr.h_name);
323 continue;
324 }
325 A_directory = (Statb.st_mode & FILETYPE) == S_IFDIR;
326 A_special = ((Statb.st_mode & FILETYPE) == S_IFBLK)
327 || ((Statb.st_mode & FILETYPE) == S_IFCHR);
328 Hdr.h_magic = MAGIC;
329 Hdr.h_namesize = strlen(Hdr.h_name) + 1;
330 Hdr.h_uid = Statb.st_uid;
331 Hdr.h_gid = Statb.st_gid;
332 Hdr.h_dev = Statb.st_dev;
333 Hdr.h_ino = Statb.st_ino;
334 Hdr.h_mode = Statb.st_mode;
335 MKSHORT(Hdr.h_mtime, Statb.st_mtime);
336 Hdr.h_nlink = Statb.st_nlink;
337 tlong = Hdr.h_mode & S_IFREG? Statb.st_size: 0L;
338 MKSHORT(Hdr.h_filesize, tlong);
339 Hdr.h_rdev = Statb.st_rdev;
340 return 1;
341 }
342}
343
344gethdr()
345{
346
347 bread(&Hdr, HDRSIZE);
348
349 if(Hdr.h_magic != MAGIC) {
350 err("Out of phase--get help");
351abort();
352 exit(1);
353 }
354 bread(Hdr.h_name, Hdr.h_namesize);
355 if(Swap) {
356 swap(Hdr.h_name, Hdr.h_namesize);
357 }
358 if(EQ(Hdr.h_name, "TRAILER!!!"))
359 return 0;
360 A_directory = (Hdr.h_mode & FILETYPE) == S_IFDIR;
361 A_special =((Hdr.h_mode & FILETYPE) == S_IFBLK)
362 || ((Hdr.h_mode & FILETYPE) == S_IFCHR);
363 return 1;
364}
365
366ckname(namep)
367register char *namep;
368{
369 ++Select;
370 if(!gmatch(namep, Pattern)) {
371 Select = 0;
372 return 0;
373 }
374 if(Rename && !A_directory) {
375 fprintf(Wtty, "Rename <%s>\n", namep);
376 fflush(Wtty);
377 fgets(namep, 128, Rtty);
378 if(feof(Rtty))
379 exit(1);
380 namep[strlen(namep) - 1] = '\0';
381 if(EQ(namep, "")) {
382 printf("Skipped\n");
383 return 0;
384 }
385 }
386 return !Toc;
387}
388
389openout(namep)
390register char *namep;
391{
392 register f;
393 register char *np;
394
395 if(!strncmp(namep, "./", 2))
396 namep += 2;
397 np = namep;
398 if(Option == IN)
399 Cd_name = namep = cd(namep);
400 if(A_directory) {
401 if(!Dir
402 || Rename
403 || EQ(namep, ".")
404 || EQ(namep, "..")
405 || stat(namep, &Xstatb) == 0)
406 return 0;
407
408 while(!makdir(namep))
409 missdir(namep);
410ret:
411 chmod(namep, Hdr.h_mode);
412 if(Uid == 0)
413 chown(namep, Hdr.h_uid, Hdr.h_gid);
414 set_time(namep, mklong(Hdr.h_mtime), mklong(Hdr.h_mtime));
415 return 0;
416 }
417 if(Hdr.h_nlink > 1)
418 if(!postml(namep, np))
419 return 0;
420 if(A_special) {
421s_again:
422 if(mknod(namep, Hdr.h_mode, Hdr.h_rdev) < 0) {
423 if(missdir(namep))
424 goto s_again;
425 err("Cannot mknod <%s>\n", namep);
426 return 0;
427 }
428 goto ret;
429 }
430 if(stat(namep, &Xstatb) == 0)
431 if(!Uncond && (mklong(Hdr.h_mtime) < Xstatb.st_mtime)) {
432 err("current <%s> newer\n", namep);
433 return 0;
434 }
435 if(Option == PASS
436 && Hdr.h_ino == Xstatb.st_ino
437 && Hdr.h_dev == Xstatb.st_dev) {
438 err("Attempt to pass file to self!\n");
439 exit(1);
440 }
441c_again:
442 if((f = creat(namep, Hdr.h_mode)) < 0) {
443 if(missdir(namep))
444 goto c_again;
445 err("Cannot create <%s> (errno:%d)\n", namep, errno);
446 return 0;
447 }
448 if(Uid == 0)
449 chown(namep, Hdr.h_uid, Hdr.h_gid);
450 return f;
451}
452
453bread(b, c)
454register c;
455register short *b;
456{
457 static nleft = 0;
458 static short *ip;
459 register short *p = ip;
460
461 c = (c+1)>>1;
462 while(c--) {
463 if(!nleft) {
464again:
465 if(read(Input, Dbuf, Bufsize)!=Bufsize) {
466 Input = chgreel(0, Input);
467 goto again;
468 }
469 nleft = Bufsize >> 1;
470 p = Dbuf;
471 ++Blocks;
472 }
473 *b++ = *p++;
474 --nleft;
475 }
476 ip = p;
477}
478
479bwrite(rp, c)
480register short *rp;
481register c;
482{
483 register short *wp = Wp;
484
485 c = (c+1) >> 1;
486 while(c--) {
487 if(!Wct) {
488again:
489 if(write(Output, Dbuf, Bufsize)<0) {
490 Output = chgreel(1, Output);
491 goto again;
492 }
493 Wct = Bufsize >> 1;
494 wp = Dbuf;
495 ++Blocks;
496 }
497 *wp++ = *rp++;
498 --Wct;
499 }
500 Wp = wp;
501}
502
503postml(namep, np)
504register char *namep, *np;
505{
506 register i;
507 static struct ml {
508 short m_dev,
509 m_ino;
510 char m_name[2];
511 } *ml[LINKS];
512 static mlinks = 0;
513 char *mlp;
514
515 for(i = 0; i < mlinks; ++i) {
516 if(mlinks == LINKS) break;
517 if(ml[i]->m_ino==Hdr.h_ino &&
518 ml[i]->m_dev==Hdr.h_dev) {
519 if(Verbose)
520 printf("%s linked to %s\n", ml[i]->m_name,
521 np);
522 unlink(namep);
523 if(Option == IN) {
524 Fullname[Pathend] = '\0';
525 strcat(Fullname, ml[i]->m_name);
526 mlp = Fullname;
527 } else
528 mlp = ml[i]->m_name;
529l_again:
530 if(link(mlp, namep) < 0) {
531 if(missdir(np))
532 goto l_again;
533 err("Cannot link <%s>&<%s>.\n",
534 ml[i]->m_name, np);
535 }
536 set_time(namep, mklong(Hdr.h_mtime), mklong(Hdr.h_mtime));
537 return 0;
538 }
539 }
540 if(mlinks == LINKS
541 || (ml[mlinks] = malloc(strlen(np) + sizeof(struct ml))) == 0) {
542 static int first=1;
543
544 if(first)
545 if(mlinks == LINKS)
546 err("Too many links\n");
547 else
548 err("No memory for links\n");
549 mlinks = LINKS;
550 first = 0;
551 return 1;
552 }
553 ml[mlinks]->m_dev = Hdr.h_dev;
554 ml[mlinks]->m_ino = Hdr.h_ino;
555 strcpy(ml[mlinks]->m_name, np);
556 ++mlinks;
557 return 1;
558}
559
560pentry(namep)
561register char *namep;
562{
563
564 register i;
565 static short lastid = -1;
566#include <pwd.h>
567 static struct passwd *pw;
568 struct passwd *getpwuid();
569 static char tbuf[32];
570
571 printf("%-7o", Hdr.h_mode & 0177777);
572 if(lastid == Hdr.h_uid)
573 printf("%-6s", pw->pw_name);
574 else {
575 setpwent();
576 if(pw = getpwuid(Hdr.h_uid)) {
577 printf("%-6s", pw->pw_name);
578 lastid = Hdr.h_uid;
579 } else
580 printf("%-6d", Hdr.h_uid);
581 }
582 printf("%7D ", mklong(Hdr.h_filesize));
583 U.l = mklong(Hdr.h_mtime);
584 strcpy(tbuf, ctime(&U.l));
585 tbuf[24] = '\0';
586 printf(" %s %s\n", &tbuf[4], namep);
587}
588
589gmatch(s, p)
590register char *s, *p;
591{
592 register int c;
593 int cc, ok, lc, scc;
594
595 if(EQ(p, "*"))
596 return 1;
597 scc = *s;
598 lc = 077777;
599 switch (c = *p) {
600
601 case '[':
602 ok = 0;
603 while (cc = *++p) {
604 switch (cc) {
605
606 case ']':
607 if (ok)
608 return(gmatch(++s, ++p));
609 else
610 return(0);
611
612 case '-':
613 ok |= (lc <= scc & scc <= (cc=p[1]));
614 }
615 if (scc==(lc=cc)) ok++;
616 }
617 return(0);
618
619 case '?':
620 caseq:
621 if(scc) return(gmatch(++s, ++p));
622 return(0);
623 case '*':
624 return(umatch(s, ++p));
625 case 0:
626 return(!scc);
627 }
628 if (c==scc) goto caseq;
629 return(0);
630}
631
632umatch(s, p)
633register char *s, *p;
634{
635 if(*p==0) return(1);
636 while(*s)
637 if (gmatch(s++,p)) return(1);
638 return(0);
639}
640
641makdir(namep)
642register char *namep;
643{
644 static status;
645
646 if(fork())
647 wait(&status);
648 else {
649 close(2);
650 execl("/bin/mkdir", "mkdir", namep, 0);
651 exit(1);
652 }
653 return ((status>>8) & 0377)? 0: 1;
654}
655
656swap(buf, ct)
657register ct;
658register union swp { short shortw; char charv[2]; } *buf;
659{
660 register char c;
661
662 ct = (ct + 1) >> 1;
663
664 while(ct--) {
665 c = buf->charv[0];
666 buf->charv[0] = buf->charv[1];
667 buf->charv[1] = c;
668 ++buf;
669 }
670}
671set_time(namep, atime, mtime)
672register *namep;
673long atime, mtime;
674{
675 static long timevec[2];
676
677 if(Uid || !Mod_time)
678 return;
679 timevec[0] = atime;
680 timevec[1] = mtime;
681 utime(namep, timevec);
682}
683chgreel(x, fl)
684{
685 register f;
686 char str[22];
687 FILE *devtty;
688 struct stat statb;
689
690 err("errno: %d, ", errno);
691 err("Can't %s\n", x? "write output": "read input");
692 fstat(fl, &statb);
693 if((statb.st_mode&S_IFMT) != S_IFCHR)
694 exit(1);
695again:
696 err("If you want to go on, type device/file name when ready\n");
697 devtty = fopen("/dev/tty", "r");
698 fgets(str, 20, devtty);
699 str[strlen(str) - 1] = '\0';
700 if(!*str)
701 exit(1);
702 close(fl);
703 if((f = open(str, x? 1: 0)) < 0) {
704 err("That didn't work");
705 fclose(devtty);
706 goto again;
707 }
708 return f;
709}
710missdir(namep)
711register char *namep;
712{
713 register char *np;
714 register ct = 0;
715
716 if(!Dir)
717 return 0;
718 for(np = namep; *np; ++np)
719 if(*np == '/') {
720 *np = '\0';
721 if(stat(namep, &Xstatb) == -1)
722 makdir(namep), ++ct;
723 *np = '/';
724 }
725 return ct;
726}
727err(a, b, c)
728{
729 fprintf(stderr, a, b, c);
730}
731pwd()
732{
733 FILE *dir;
734
735 dir = popen("pwd", "r");
736 fgets(Fullname, 128, dir);
737 pclose(dir);
738 Pathend = strlen(Fullname);
739 Fullname[Pathend - 1] = '/';
740}
741char * cd(n)
742register char *n;
743{
744 char *p_save = Name, *n_save = n, *p_end = 0;
745 register char *p = Name;
746 static char dotdot[]="../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../../";
747 int slashes;
748
749 for(; *p && *n == *p; ++p, ++n) { /* whatever part of strings == */
750 if(*p == '/')
751 p_save = p+1, n_save = n+1;
752 }
753
754 p = p_save;
755 *p++ = '\0';
756 for(slashes = 0; *p; ++p) { /* if prev is longer, chdir("..") */
757 if(*p == '/')
758 ++slashes;
759 }
760 p = p_save;
761 if(slashes) {
762 slashes = slashes * 3 - 1;
763 dotdot[slashes] = '\0';
764 chdir(dotdot);
765 dotdot[slashes] = '/';
766 }
767
768 n = n_save;
769 for(; *n; ++n, ++p) {
770 *p = *n;
771 if(*n == '/')
772 p_end = p+1, n_save = n+1;
773 }
774 *p = '\0';
775
776 if(p_end) {
777 *p_end = '\0';
778 if(chdir(p_save) == -1) {
779 if(!missdir(p_save)) {
780cd_err:
781 err("Cannot chdir\n");
782 abort();
783 } else if(chdir(p_save) == -1)
784 goto cd_err;
785 }
786 } else
787 *p_save = '\0';
788 return n_save;
789}