386BSD 0.1 development
[unix-history] / usr / src / usr.bin / tar / gnu.c
CommitLineData
f87489ac
WJ
1#include <stdio.h>
2#include <sys/types.h>
3#include <sys/stat.h>
4#include <ctype.h>
5#include <errno.h>
6
7#ifdef BSD42
8#include <sys/dir.h>
9#else
10#ifdef __MSDOS__
11#include "msd_dir.h"
12#else
13#ifdef USG
14#ifdef NDIR
15#include <ndir.h>
16#else
17#include <dirent.h>
18#endif
19#ifndef DIRECT
20#define direct dirent
21#endif
22#define DP_NAMELEN(x) strlen((x)->d_name)
23#else
24/*
25 * FIXME: On other systems there is no standard place for the header file
26 * for the portable directory access routines. Change the #include line
27 * below to bring it in from wherever it is.
28 */
29#include "ndir.h"
30#endif
31#endif
32#endif
33
34#ifndef DP_NAMELEN
35#define DP_NAMELEN(x) (x)->d_namlen
36#endif
37
38#ifndef MAXPATHLEN
39#define MAXPATHLEN 1024
40#endif
41
42/*
43 * If there are no symbolic links, there is no lstat(). Use stat().
44 */
45#ifndef S_IFLNK
46#define lstat stat
47#endif
48
49#ifdef __STDC__
50#define VOIDSTAR void *
51#else
52#define VOIDSTAR char *
53#endif
54extern VOIDSTAR ck_malloc();
55extern VOIDSTAR ck_realloc();
56
57#ifndef S_IFLNK
58#define lstat stat
59#endif
60
61extern VOIDSTAR malloc();
62
63#include "tar.h"
64
65extern time_t new_time;
66extern FILE *msg_file;
67
68extern VOIDSTAR init_buffer();
69extern char *get_buffer();
70extern void add_buffer();
71extern void flush_buffer();
72
73extern char *new_name();
74
75static void add_dir_name();
76
77struct dirname {
78 struct dirname *next;
79 char *name;
80 char *dir_text;
81 int dev;
82 int ino;
83 int allnew;
84};
85static struct dirname *dir_list;
86static time_t this_time;
87
88void
89add_dir(name,dev,ino,text)
90char *name;
91char *text;
92{
93 struct dirname *dp;
94
95 dp=(struct dirname *)malloc(sizeof(struct dirname));
96 if(!dp)
97 abort();
98 dp->next=dir_list;
99 dir_list=dp;
100 dp->dev=dev;
101 dp->ino=ino;
102 dp->name=malloc(strlen(name)+1);
103 strcpy(dp->name,name);
104 dp->dir_text=text;
105 dp->allnew=0;
106}
107
108void
109read_dir_file()
110{
111 int dev;
112 int ino;
113 char *strp;
114 FILE *fp;
115 char buf[512];
116 extern int errno;
117 static char path[MAXPATHLEN];
118
119 time(&this_time);
120 if(gnu_dumpfile[0]!='/') {
121#if defined(MSDOS) || defined(USG)
122 int getcwd();
123
124 if(!getcwd(path,MAXPATHLEN))
125 msg("Couldn't get current directory.");
126 exit(EX_SYSTEM);
127#else
128 char *getwd();
129
130 if(!getwd(path)) {
131 msg("Couldn't get current directory: %s",path);
132 exit(EX_SYSTEM);
133 }
134#endif
135 /* If this doesn't fit, we're in serious trouble */
136 strcat(path,"/");
137 strcat(path,gnu_dumpfile);
138 gnu_dumpfile=path;
139 }
140 fp=fopen(gnu_dumpfile,"r");
141 if(fp==0 && errno!=ENOENT) {
142 msg_perror("Can't open %s",gnu_dumpfile);
143 return;
144 }
145 if(!fp)
146 return;
147 fgets(buf,sizeof(buf),fp);
148 if(!f_new_files) {
149 f_new_files++;
150 new_time=atol(buf);
151 }
152 while(fgets(buf,sizeof(buf),fp)) {
153 strp= &buf[strlen(buf)];
154 if(strp[-1]=='\n')
155 strp[-1]='\0';
156 strp=buf;
157 dev=atol(strp);
158 while(isdigit(*strp))
159 strp++;
160 ino=atol(strp);
161 while(isspace(*strp))
162 strp++;
163 while(isdigit(*strp))
164 strp++;
165 strp++;
166 add_dir(un_quote_string(strp),dev,ino,(char *)0);
167 }
168 fclose(fp);
169}
170
171void
172write_dir_file()
173{
174 FILE *fp;
175 struct dirname *dp;
176 char *str;
177 extern char *quote_copy_string();
178
179 fp=fopen(gnu_dumpfile,"w");
180 if(fp==0) {
181 msg_perror("Can't write to %s",gnu_dumpfile);
182 return;
183 }
184 fprintf(fp,"%lu\n",this_time);
185 for(dp=dir_list;dp;dp=dp->next) {
186 if(!dp->dir_text)
187 continue;
188 str=quote_copy_string(dp->name);
189 if(str) {
190 fprintf(fp,"%u %u %s\n",dp->dev,dp->ino,str);
191 free(str);
192 } else
193 fprintf(fp,"%u %u %s\n",dp->dev,dp->ino,dp->name);
194 }
195 fclose(fp);
196}
197
198struct dirname *
199get_dir(name)
200char *name;
201{
202 struct dirname *dp;
203
204 for(dp=dir_list;dp;dp=dp->next) {
205 if(!strcmp(dp->name,name))
206 return dp;
207 }
208 return 0;
209}
210
211
212/* Collect all the names from argv[] (or whatever), then expand them into
213 a directory tree, and put all the directories at the beginning. */
214collect_and_sort_names()
215{
216 struct name *n,*n_next;
217 int num_names;
218 struct stat statbuf;
219 int name_cmp();
220 char *merge_sort();
221
222 name_gather();
223
224 if(gnu_dumpfile)
225 read_dir_file();
226 if(!namelist) addname(".");
227 for(n=namelist;n;n=n_next) {
228 n_next=n->next;
229 if(n->found || n->dir_contents)
230 continue;
231 if(n->regexp) /* FIXME just skip regexps for now */
232 continue;
233 if(n->change_dir)
234 if(chdir(n->change_dir)<0) {
235 msg_perror("can't chdir to %s",n->change_dir);
236 continue;
237 }
238
239#ifdef AIX
240 if (statx (n->name, &statbuf, STATSIZE, STX_HIDDEN|STX_LINK))
241#else
242 if(lstat(n->name,&statbuf)<0) {
243#endif /* AIX */
244 msg_perror("can't stat %s",n->name);
245 continue;
246 }
247 if((statbuf.st_mode&S_IFMT)==S_IFDIR) {
248 n->found++;
249 add_dir_name(n->name,statbuf.st_dev);
250 }
251 }
252
253 num_names=0;
254 for(n=namelist;n;n=n->next)
255 num_names++;
256 namelist=(struct name *)merge_sort((VOIDSTAR)namelist,num_names,(char *)(&(namelist->next))-(char *)namelist,name_cmp);
257
258 for(n=namelist;n;n=n->next) {
259 n->found=0;
260 }
261 /* if(gnu_dumpfile)
262 write_dir_file(gnu_dumpfile); */
263}
264
265int
266name_cmp(n1,n2)
267struct name *n1,*n2;
268{
269 if(n1->found) {
270 if(n2->found)
271 return strcmp(n1->name,n2->name);
272 else
273 return -1;
274 } else if(n2->found)
275 return 1;
276 else
277 return strcmp(n1->name,n2->name);
278}
279
280int
281dirent_cmp(p1,p2)
282char **p1,**p2;
283{
284 char *frst,*scnd;
285
286 frst= (*p1)+1;
287 scnd= (*p2)+1;
288
289 return strcmp(frst,scnd);
290}
291
292char *
293get_dir_contents(p,device)
294char *p;
295int device;
296{
297 DIR *dirp;
298 register struct direct *d;
299 char *new_buf;
300 char *namebuf;
301 int bufsiz;
302 int len;
303 VOIDSTAR the_buffer;
304 char *buf;
305 int n_strs;
306 int n_size;
307 char *p_buf;
308 char **vec,**p_vec;
309
310 extern int errno;
311
312 errno=0;
313 dirp=opendir(p);
314 bufsiz=strlen(p)+NAMSIZ;
315 namebuf=ck_malloc(bufsiz+2);
316 if(!dirp) {
317 if(errno)
318 msg_perror("can't open directory %s",p);
319 else
320 msg("error opening directory %s",p);
321 new_buf="\0\0\0\0";
322 } else {
323 struct dirname *dp;
324 int all_children;
325
326 dp=get_dir(p);
327 all_children= dp ? dp->allnew : 0;
328 (void) strcpy(namebuf,p);
329 if(p[strlen(p)-1]!='/')
330 (void) strcat(namebuf,"/");
331 len=strlen(namebuf);
332
333 the_buffer=init_buffer();
334 while(d=readdir(dirp)) {
335 struct stat hs;
336
337 /* Skip . and .. */
338 if(is_dot_or_dotdot(d->d_name))
339 continue;
340 if(DP_NAMELEN(d) + len >=bufsiz) {
341 bufsiz+=NAMSIZ;
342 namebuf=ck_realloc(namebuf,bufsiz+2);
343 }
344 (void) strcpy(namebuf+len,d->d_name);
345#ifdef AIX
346 if (0 != f_follow_links?
347 statx(namebuf, &hs, STATSIZE, STX_HIDDEN):
348 statx(namebuf, &hs, STATSIZE, STX_HIDDEN|STX_LINK))
349#else
350 if (0 != f_follow_links? stat(namebuf, &hs): lstat(namebuf, &hs))
351#endif
352 {
353 msg_perror("can't stat %s",namebuf);
354 continue;
355 }
356 if( (f_local_filesys && device!=hs.st_dev)
357 || (f_exclude && check_exclude(namebuf)))
358 add_buffer(the_buffer,"N",1);
359#ifdef AIX
360 else if (S_ISHIDDEN (hs.st_mode)) {
361 add_buffer (the_buffer, "D", 1);
362 strcat (d->d_name, "A");
363 d->d_namlen++;
364 }
365#endif /* AIX */
366 else if((hs.st_mode&S_IFMT)==S_IFDIR) {
367 if(dp=get_dir(namebuf)) {
368 if( dp->dev!=hs.st_dev
369 || dp->ino!=hs.st_ino) {
370 if(f_verbose)
371 msg("directory %s has been renamed.",namebuf);
372 dp->allnew=1;
373 dp->dev=hs.st_dev;
374 dp->ino=hs.st_ino;
375 }
376 dp->dir_text="";
377 } else {
378 if(f_verbose)
379 msg("Directory %s is new",namebuf);
380 add_dir(namebuf,hs.st_dev,hs.st_ino,"");
381 dp=get_dir(namebuf);
382 dp->allnew=1;
383 }
384 if(all_children)
385 dp->allnew=1;
386
387 add_buffer(the_buffer,"D",1);
388 } else if( !all_children
389 && f_new_files
390 && new_time>hs.st_mtime
391 && ( f_new_files>1
392 || new_time>hs.st_ctime))
393 add_buffer(the_buffer,"N",1);
394 else
395 add_buffer(the_buffer,"Y",1);
396 add_buffer(the_buffer,d->d_name,(int)(DP_NAMELEN(d)+1));
397 }
398 add_buffer(the_buffer,"\000\000",2);
399 closedir(dirp);
400
401 /* Well, we've read in the contents of the dir, now sort them */
402 buf=get_buffer(the_buffer);
403 if(buf[0]=='\0') {
404 flush_buffer(the_buffer);
405 new_buf="\0\0\0\0";
406 } else {
407 n_strs=0;
408 for(p_buf=buf;*p_buf;) {
409 int tmp;
410
411 tmp=strlen(p_buf)+1;
412 n_strs++;
413 p_buf+=tmp;
414 }
415 vec=(char **)malloc(sizeof(char *)*(n_strs+1));
416 for(p_vec=vec,p_buf=buf;*p_buf;p_buf+=strlen(p_buf)+1)
417 *p_vec++= p_buf;
418 *p_vec= 0;
419 qsort((VOIDSTAR)vec,n_strs,sizeof(char *),dirent_cmp);
420 new_buf=(char *)malloc(p_buf-buf+2);
421 for(p_vec=vec,p_buf=new_buf;*p_vec;p_vec++) {
422 char *p_tmp;
423
424 for(p_tmp= *p_vec;*p_buf++= *p_tmp++;)
425 ;
426 }
427 *p_buf++='\0';
428 free(vec);
429 flush_buffer(the_buffer);
430 }
431 }
432 free(namebuf);
433 return new_buf;
434}
435
436/* p is a directory. Add all the files in P to the namelist. If any of the
437 files is a directory, recurse on the subdirectory. . . */
438static void
439add_dir_name(p,device)
440char *p;
441int device;
442{
443 char *new_buf;
444 char *p_buf;
445
446 char *namebuf;
447 int buflen;
448 register int len;
449 int sublen;
450
451 VOIDSTAR the_buffer;
452
453 char *buf;
454 char **vec,**p_vec;
455 int n_strs,n_size;
456
457 struct name *n;
458
459 int dirent_cmp();
460
461 new_buf=get_dir_contents(p,device);
462
463 for(n=namelist;n;n=n->next) {
464 if(!strcmp(n->name,p)) {
465 n->dir_contents = new_buf;
466 break;
467 }
468 }
469
470 len=strlen(p);
471 buflen= NAMSIZ<=len ? len + NAMSIZ : NAMSIZ;
472 namebuf= ck_malloc(buflen+1);
473
474 (void)strcpy(namebuf,p);
475 if(namebuf[len-1]!='/') {
476 namebuf[len++]='/';
477 namebuf[len]='\0';
478 }
479 for(p_buf=new_buf;*p_buf;p_buf+=sublen+1) {
480 sublen=strlen(p_buf);
481 if(*p_buf=='D') {
482 if(len+sublen>=buflen) {
483 buflen+=NAMSIZ;
484 namebuf= ck_realloc(namebuf,buflen+1);
485 }
486 (void)strcpy(namebuf+len,p_buf+1);
487 addname(namebuf);
488 add_dir_name(namebuf,device);
489 }
490 }
491 free(namebuf);
492}
493
494/* Returns non-zero if p is . or .. This could be a macro for speed. */
495is_dot_or_dotdot(p)
496char *p;
497{
498 return (p[0]=='.' && (p[1]=='\0' || (p[1]=='.' && p[2]=='\0')));
499}
500
501
502
503
504
505
506gnu_restore(skipcrud)
507int skipcrud;
508{
509 char *current_dir;
510/* int current_dir_length; */
511
512 char *archive_dir;
513/* int archive_dir_length; */
514 VOIDSTAR the_buffer;
515 char *p;
516 DIR *dirp;
517 struct direct *d;
518 char *cur,*arc;
519 extern struct stat hstat; /* Stat struct corresponding */
520 long size,copied;
521 char *from,*to;
522 extern union record *head;
523
524 dirp=opendir(skipcrud+head->header.name);
525
526 if(!dirp) {
527 /* The directory doesn't exist now. It'll be created.
528 In any case, we don't have to delete any files out
529 of it */
530 skip_file((long)hstat.st_size);
531 return;
532 }
533
534 the_buffer=init_buffer();
535 while(d=readdir(dirp)) {
536 if(is_dot_or_dotdot(d->d_name))
537 continue;
538
539 add_buffer(the_buffer,d->d_name,(int)(DP_NAMELEN(d)+1));
540 }
541 closedir(dirp);
542 add_buffer(the_buffer,"",1);
543
544 current_dir=get_buffer(the_buffer);
545 archive_dir=(char *)malloc(hstat.st_size);
546 if(archive_dir==0) {
547 msg("Can't allocate %d bytes for restore",hstat.st_size);
548 skip_file((long)hstat.st_size);
549 return;
550 }
551 to=archive_dir;
552 for(size=hstat.st_size;size>0;size-=copied) {
553 from=findrec()->charptr;
554 if(!from) {
555 msg("Unexpected EOF in archive\n");
556 break;
557 }
558 copied=endofrecs()->charptr - from;
559 if(copied>size)
560 copied=size;
561 bcopy((VOIDSTAR)from,(VOIDSTAR)to,(int)copied);
562 to+=copied;
563 userec((union record *)(from+copied-1));
564 }
565
566 for(cur=current_dir;*cur;cur+=strlen(cur)+1) {
567 for(arc=archive_dir;*arc;arc+=strlen(arc)+1) {
568 arc++;
569 if(!strcmp(arc,cur))
570 break;
571 }
572 if(*arc=='\0') {
573 p=new_name(skipcrud+head->header.name,cur);
574 if(f_confirm && !confirm("delete",p)) {
575 free(p);
576 continue;
577 }
578 if(f_verbose)
579 fprintf(msg_file,"%s: deleting %s\n",tar,p);
580 if(recursively_delete(p)) {
581 msg("%s: Error while deleting %s\n",tar,p);
582 }
583 free(p);
584 }
585
586 }
587 flush_buffer(the_buffer);
588 free(archive_dir);
589}
590
591recursively_delete(path)
592char *path;
593{
594 struct stat sbuf;
595 DIR *dirp;
596 struct direct *dp;
597 char *path_buf;
598 /* int path_len; */
599
600
601 if(lstat(path,&sbuf)<0)
602 return 1;
603 if((sbuf.st_mode &S_IFMT)==S_IFDIR) {
604
605 /* path_len=strlen(path); */
606 dirp=opendir(path);
607 if(dirp==0)
608 return 1;
609 while(dp=readdir(dirp)) {
610 if(is_dot_or_dotdot(dp->d_name))
611 continue;
612 path_buf=new_name(path,dp->d_name);
613 if(recursively_delete(path_buf)) {
614 free(path_buf);
615 closedir(dirp);
616 return 1;
617 }
618 free(path_buf);
619 }
620 closedir(dirp);
621
622 if(rmdir(path)<0)
623 return 1;
624 return 0;
625 }
626 if(unlink(path)<0)
627 return 1;
628 return 0;
629}
630