replaced stdio in many parts of samba with a XFILE. XFILE is a cut-down
[kai/samba.git] / source3 / client / clitar.c
1 /* 
2    Unix SMB/Netbios implementation.
3    Version 1.9.
4    Tar Extensions
5    Copyright (C) Ricky Poulten 1995-1998
6    Copyright (C) Richard Sharpe 1998
7    
8    This program is free software; you can redistribute it and/or modify
9    it under the terms of the GNU General Public License as published by
10    the Free Software Foundation; either version 2 of the License, or
11    (at your option) any later version.
12    
13    This program is distributed in the hope that it will be useful,
14    but WITHOUT ANY WARRANTY; without even the implied warranty of
15    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16    GNU General Public License for more details.
17    
18    You should have received a copy of the GNU General Public License
19    along with this program; if not, write to the Free Software
20    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
21 */
22 /* The following changes developed by Richard Sharpe for Canon Information
23    Systems Research Australia (CISRA)
24
25    1. Restore can now restore files with long file names
26    2. Save now saves directory information so that we can restore 
27       directory creation times
28    3. tar now accepts both UNIX path names and DOS path names. I prefer
29       those lovely /'s to those UGLY \'s :-)
30    4. the files to exclude can be specified as a regular expression by adding
31       an r flag to the other tar flags. Eg:
32
33          -TcrX file.tar "*.(obj|exe)"
34
35       will skip all .obj and .exe files
36 */
37
38
39 #include "includes.h"
40 #include "clitar.h"
41
42 static int clipfind(char **aret, int ret, char *tok);
43
44 typedef struct file_info_struct file_info2;
45
46 struct file_info_struct
47 {
48   size_t size;
49   uint16 mode;
50   int uid;
51   int gid;
52   /* These times are normally kept in GMT */
53   time_t mtime;
54   time_t atime;
55   time_t ctime;
56   char *name;     /* This is dynamically allocate */
57
58   file_info2 *next, *prev;  /* Used in the stack ... */
59
60 };
61
62 typedef struct
63 {
64   file_info2 *top;
65   int items;
66
67 } stack;
68
69 stack dir_stack = {NULL, 0}; /* Want an empty stack */
70
71 #define SEPARATORS " \t\n\r"
72 extern int DEBUGLEVEL;
73 extern struct cli_state *cli;
74 extern XFILE *dbf;
75
76 /* These defines are for the do_setrattr routine, to indicate
77  * setting and reseting of file attributes in the function call */
78 #define ATTRSET 1
79 #define ATTRRESET 0
80
81 static uint16 attribute = aDIR | aSYSTEM | aHIDDEN;
82
83 #ifndef CLIENT_TIMEOUT
84 #define CLIENT_TIMEOUT (30*1000)
85 #endif
86
87 static char *tarbuf, *buffer_p;
88 static int tp, ntarf, tbufsiz;
89 static double ttarf;
90 /* Incremental mode */
91 BOOL tar_inc=False;
92 /* Reset archive bit */
93 BOOL tar_reset=False;
94 /* Include / exclude mode (true=include, false=exclude) */
95 BOOL tar_excl=True;
96 /* use regular expressions for search on file names */
97 BOOL tar_re_search=False;
98 #ifdef HAVE_REGEX_H
99 regex_t *preg;
100 #endif
101 /* Do not dump anything, just calculate sizes */
102 BOOL dry_run=False;
103 /* Dump files with System attribute */
104 BOOL tar_system=True;
105 /* Dump files with Hidden attribute */
106 BOOL tar_hidden=True;
107 /* Be noisy - make a catalogue */
108 BOOL tar_noisy=True;
109 BOOL tar_real_noisy=False;  /* Don't want to be really noisy by default */
110
111 char tar_type='\0';
112 static char **cliplist=NULL;
113 static int clipn=0;
114 static BOOL must_free_cliplist = False;
115
116 extern file_info def_finfo;
117 extern BOOL lowercase;
118 extern uint16 cnum;
119 extern BOOL readbraw_supported;
120 extern int max_xmit;
121 extern pstring cur_dir;
122 extern int get_total_time_ms;
123 extern int get_total_size;
124
125 int blocksize=20;
126 int tarhandle;
127
128 static void writetarheader(int f,  char *aname, int size, time_t mtime,
129                            char *amode, unsigned char ftype);
130 static void do_atar(char *rname,char *lname,file_info *finfo1);
131 static void do_tar(file_info *finfo);
132 static void oct_it(long value, int ndgs, char *p);
133 static void fixtarname(char *tptr, char *fp, int l);
134 static int dotarbuf(int f, char *b, int n);
135 static void dozerobuf(int f, int n);
136 static void dotareof(int f);
137 static void initarbuf(void);
138
139 /* restore functions */
140 static long readtarheader(union hblock *hb, file_info2 *finfo, char *prefix);
141 static long unoct(char *p, int ndgs);
142 static void do_tarput(void);
143 static void unfixtarname(char *tptr, char *fp, int l, BOOL first);
144
145 /*
146  * tar specific utitlities
147  */
148
149 /*******************************************************************
150 Create  a string of size size+1 (for the null)
151 *******************************************************************/
152 static char *string_create_s(int size)
153 {
154   char *tmp;
155
156   tmp = (char *)malloc(size+1);
157
158   if (tmp == NULL) {
159
160     DEBUG(0, ("Out of memory in string_create_s\n"));
161
162   }
163
164   return(tmp);
165
166 }
167
168 /****************************************************************************
169 Write a tar header to buffer
170 ****************************************************************************/
171 static void writetarheader(int f,  char *aname, int size, time_t mtime,
172                            char *amode, unsigned char ftype)
173 {
174   union hblock hb;
175   int i, chk, l;
176   char *jp;
177
178   DEBUG(5, ("WriteTarHdr, Type = %c, Size= %i, Name = %s\n", ftype, size, aname));
179
180   memset(hb.dummy, 0, sizeof(hb.dummy));
181   
182   l=strlen(aname);
183   if (l >= NAMSIZ - 1) {
184           /* write a GNU tar style long header */
185           char *b;
186           b = (char *)malloc(l+TBLOCK+100);
187           if (!b) {
188                   DEBUG(0,("out of memory\n"));
189                   exit(1);
190           }
191           writetarheader(f, "/./@LongLink", l+2, 0, "     0 \0", 'L');
192           memset(b, 0, l+TBLOCK+100);
193           fixtarname(b, aname, l);
194           i = strlen(b)+1;
195           DEBUG(5, ("File name in tar file: %s, size=%d, \n", b, (int)strlen(b)));
196           dotarbuf(f, b, TBLOCK*(((i-1)/TBLOCK)+1));
197           free(b);
198   }
199
200   /* use l + 1 to do the null too */
201   fixtarname(hb.dbuf.name, aname, (l >= NAMSIZ) ? NAMSIZ : l + 1);
202
203   if (lowercase)
204     strlower(hb.dbuf.name);
205
206   /* write out a "standard" tar format header */
207
208   hb.dbuf.name[NAMSIZ-1]='\0';
209   safe_strcpy(hb.dbuf.mode, amode, strlen(amode));
210   oct_it(0L, 8, hb.dbuf.uid);
211   oct_it(0L, 8, hb.dbuf.gid);
212   oct_it((long) size, 13, hb.dbuf.size);
213   oct_it((long) mtime, 13, hb.dbuf.mtime);
214   memcpy(hb.dbuf.chksum, "        ", sizeof(hb.dbuf.chksum));
215   memset(hb.dbuf.linkname, 0, NAMSIZ);
216   hb.dbuf.linkflag=ftype;
217   
218   for (chk=0, i=sizeof(hb.dummy), jp=hb.dummy; --i>=0;) chk+=(0xFF & *jp++);
219
220   oct_it((long) chk, 8, hb.dbuf.chksum);
221   hb.dbuf.chksum[6] = '\0';
222
223   (void) dotarbuf(f, hb.dummy, sizeof(hb.dummy));
224 }
225
226 /****************************************************************************
227 Read a tar header into a hblock structure, and validate
228 ***************************************************************************/
229 static long readtarheader(union hblock *hb, file_info2 *finfo, char *prefix)
230 {
231   long chk, fchk;
232   int i;
233   char *jp;
234
235   /*
236    * read in a "standard" tar format header - we're not that interested
237    * in that many fields, though
238    */
239
240   /* check the checksum */
241   for (chk=0, i=sizeof(hb->dummy), jp=hb->dummy; --i>=0;) chk+=(0xFF & *jp++);
242
243   if (chk == 0)
244     return chk;
245
246   /* compensate for blanks in chksum header */
247   for (i=sizeof(hb->dbuf.chksum), jp=hb->dbuf.chksum; --i>=0;)
248     chk-=(0xFF & *jp++);
249
250   chk += ' ' * sizeof(hb->dbuf.chksum);
251
252   fchk=unoct(hb->dbuf.chksum, sizeof(hb->dbuf.chksum));
253
254   DEBUG(5, ("checksum totals chk=%ld fchk=%ld chksum=%s\n",
255             chk, fchk, hb->dbuf.chksum));
256
257   if (fchk != chk)
258     {
259       DEBUG(0, ("checksums don't match %ld %ld\n", fchk, chk));
260       dump_data(5, (char *)hb - TBLOCK, TBLOCK *3);
261       return -1;
262     }
263
264   if ((finfo->name = string_create_s(strlen(prefix) + strlen(hb -> dbuf.name) + 3)) == NULL) {
265
266     DEBUG(0, ("Out of space creating file_info2 for %s\n", hb -> dbuf.name));
267     return(-1);
268
269   }
270
271   safe_strcpy(finfo->name, prefix, strlen(prefix) + strlen(hb -> dbuf.name) + 3);
272
273   /* use l + 1 to do the null too; do prefix - prefcnt to zap leading slash */
274   unfixtarname(finfo->name + strlen(prefix), hb->dbuf.name,
275                strlen(hb->dbuf.name) + 1, True);
276
277   /* can't handle some links at present */
278   if ((hb->dbuf.linkflag != '0') && (hb -> dbuf.linkflag != '5')) {
279     if (hb->dbuf.linkflag == 0) {
280       DEBUG(6, ("Warning: NULL link flag (gnu tar archive ?) %s\n",
281                 finfo->name));
282     } else { 
283       if (hb -> dbuf.linkflag == 'L') { /* We have a longlink */
284          /* Do nothing here at the moment. do_tarput will handle this
285             as long as the longlink gets back to it, as it has to advance 
286             the buffer pointer, etc */
287
288       } else {
289         DEBUG(0, ("this tar file appears to contain some kind of link other than a GNUtar Longlink - ignoring\n"));
290         return -2;
291       }
292     }
293   }
294     
295   if ((unoct(hb->dbuf.mode, sizeof(hb->dbuf.mode)) & S_IFDIR)
296     || (*(finfo->name+strlen(finfo->name)-1) == '\\'))
297     {
298       finfo->mode=aDIR;
299     }
300   else
301     finfo->mode=0; /* we don't care about mode at the moment, we'll
302                     * just make it a regular file */
303   /*
304    * Bug fix by richard@sj.co.uk
305    *
306    * REC: restore times correctly (as does tar)
307    * We only get the modification time of the file; set the creation time
308    * from the mod. time, and the access time to current time
309    */
310   finfo->mtime = finfo->ctime = strtol(hb->dbuf.mtime, NULL, 8);
311   finfo->atime = time(NULL);
312   finfo->size = unoct(hb->dbuf.size, sizeof(hb->dbuf.size));
313
314   return True;
315 }
316
317 /****************************************************************************
318 Write out the tar buffer to tape or wherever
319 ****************************************************************************/
320 static int dotarbuf(int f, char *b, int n)
321 {
322   int fail=1, writ=n;
323
324   if (dry_run) {
325     return writ;
326   }
327   /* This routine and the next one should be the only ones that do write()s */
328   if (tp + n >= tbufsiz)
329     {
330       int diff;
331
332       diff=tbufsiz-tp;
333       memcpy(tarbuf + tp, b, diff);
334       fail=fail && (1+write(f, tarbuf, tbufsiz));
335       n-=diff;
336       b+=diff;
337       tp=0;
338
339       while (n >= tbufsiz)
340         {
341           fail=fail && (1 + write(f, b, tbufsiz));
342           n-=tbufsiz;
343           b+=tbufsiz;
344         }
345     }
346   if (n>0) {
347     memcpy(tarbuf+tp, b, n);
348     tp+=n;
349   }
350
351   return(fail ? writ : 0);
352 }
353
354 /****************************************************************************
355 Write zeros to buffer / tape
356 ****************************************************************************/
357 static void dozerobuf(int f, int n)
358 {
359   /* short routine just to write out n zeros to buffer -
360    * used to round files to nearest block
361    * and to do tar EOFs */
362
363   if (dry_run)
364     return;
365   
366   if (n+tp >= tbufsiz)
367     {
368       memset(tarbuf+tp, 0, tbufsiz-tp);
369
370       write(f, tarbuf, tbufsiz);
371       memset(tarbuf, 0, (tp+=n-tbufsiz));
372     }
373   else
374     {
375       memset(tarbuf+tp, 0, n);
376       tp+=n;
377     }
378 }
379
380 /****************************************************************************
381 Malloc tape buffer
382 ****************************************************************************/
383 static void initarbuf(void)
384 {
385   /* initialize tar buffer */
386   tbufsiz=blocksize*TBLOCK;
387   tarbuf=malloc(tbufsiz);      /* FIXME: We might not get the buffer */
388
389   /* reset tar buffer pointer and tar file counter and total dumped */
390   tp=0; ntarf=0; ttarf=0;
391 }
392
393 /****************************************************************************
394 Write two zero blocks at end of file
395 ****************************************************************************/
396 static void dotareof(int f)
397 {
398   SMB_STRUCT_STAT stbuf;
399   /* Two zero blocks at end of file, write out full buffer */
400
401   if (dry_run)
402     return;
403
404   (void) dozerobuf(f, TBLOCK);
405   (void) dozerobuf(f, TBLOCK);
406
407   if (sys_fstat(f, &stbuf) == -1)
408     {
409       DEBUG(0, ("Couldn't stat file handle\n"));
410       return;
411     }
412
413   /* Could be a pipe, in which case S_ISREG should fail,
414    * and we should write out at full size */
415   if (tp > 0) write(f, tarbuf, S_ISREG(stbuf.st_mode) ? tp : tbufsiz);
416 }
417
418 /****************************************************************************
419 (Un)mangle DOS pathname, make nonabsolute
420 ****************************************************************************/
421 static void fixtarname(char *tptr, char *fp, int l)
422 {
423         /* add a '.' to start of file name, convert from ugly dos \'s in path
424          * to lovely unix /'s :-} */
425         *tptr++='.';
426
427         safe_strcpy(tptr, fp, l);
428         string_replace(tptr, '\\', '/');
429 }
430
431 /****************************************************************************
432 Convert from decimal to octal string
433 ****************************************************************************/
434 static void oct_it (long value, int ndgs, char *p)
435 {
436   /* Converts long to octal string, pads with leading zeros */
437
438   /* skip final null, but do final space */
439   --ndgs;
440   p[--ndgs] = ' ';
441  
442   /* Loop does at least one digit */
443   do {
444       p[--ndgs] = '0' + (char) (value & 7);
445       value >>= 3;
446     }
447   while (ndgs > 0 && value != 0);
448  
449   /* Do leading zeros */
450   while (ndgs > 0)
451     p[--ndgs] = '0';
452 }
453
454 /****************************************************************************
455 Convert from octal string to long
456 ***************************************************************************/
457 static long unoct(char *p, int ndgs)
458 {
459   long value=0;
460   /* Converts octal string to long, ignoring any non-digit */
461
462   while (--ndgs)
463     {
464       if (isdigit((int)*p))
465         value = (value << 3) | (long) (*p - '0');
466
467       p++;
468     }
469
470   return value;
471 }
472
473 /****************************************************************************
474 Compare two strings in a slash insensitive way, allowing s1 to match s2 
475 if s1 is an "initial" string (up to directory marker).  Thus, if s2 is 
476 a file in any subdirectory of s1, declare a match.
477 ***************************************************************************/
478 static int strslashcmp(char *s1, char *s2)
479 {
480   char *s1_0=s1;
481
482   while(*s1 && *s2 &&
483         (*s1 == *s2
484          || tolower(*s1) == tolower(*s2)
485          || (*s1 == '\\' && *s2=='/')
486          || (*s1 == '/' && *s2=='\\'))) {
487           s1++; s2++;
488   }
489
490   /* if s1 has a trailing slash, it compared equal, so s1 is an "initial" 
491      string of s2.
492    */
493   if (!*s1 && s1 != s1_0 && (*(s1-1) == '/' || *(s1-1) == '\\')) return 0;
494
495   /* ignore trailing slash on s1 */
496   if (!*s2 && (*s1 == '/' || *s1 == '\\') && !*(s1+1)) return 0;
497
498   /* check for s1 is an "initial" string of s2 */
499   if (*s2 == '/' || *s2 == '\\') return 0;
500
501   return *s1-*s2;
502 }
503
504
505 /****************************************************************************
506 Ensure a remote path exists (make if necessary)
507 ***************************************************************************/
508 static BOOL ensurepath(char *fname)
509 {
510   /* *must* be called with buffer ready malloc'ed */
511   /* ensures path exists */
512
513   char *partpath, *ffname;
514   char *p=fname, *basehack;
515
516   DEBUG(5, ( "Ensurepath called with: %s\n", fname));
517
518   partpath = string_create_s(strlen(fname));
519   ffname = string_create_s(strlen(fname));
520
521   if ((partpath == NULL) || (ffname == NULL)){
522
523     DEBUG(0, ("Out of memory in ensurepath: %s\n", fname));
524     return(False);
525
526   }
527
528   *partpath = 0;
529
530   /* fname copied to ffname so can strtok */
531
532   safe_strcpy(ffname, fname, strlen(fname));
533
534   /* do a `basename' on ffname, so don't try and make file name directory */
535   if ((basehack=strrchr_m(ffname, '\\')) == NULL)
536     return True;
537   else
538     *basehack='\0';
539
540   p=strtok(ffname, "\\");
541
542   while (p)
543     {
544       safe_strcat(partpath, p, strlen(fname) + 1);
545
546       if (!cli_chkpath(cli, partpath)) {
547         if (!cli_mkdir(cli, partpath))
548           {
549             DEBUG(0, ("Error mkdirhiering\n"));
550             return False;
551           }
552         else
553           DEBUG(3, ("mkdirhiering %s\n", partpath));
554
555       }
556
557       safe_strcat(partpath, "\\", strlen(fname) + 1);
558       p = strtok(NULL,"/\\");
559     }
560
561     return True;
562 }
563
564 static int padit(char *buf, int bufsize, int padsize)
565 {
566         int berr= 0;
567         int bytestowrite;
568   
569         DEBUG(5, ("Padding with %d zeros\n", padsize));
570         memset(buf, 0, bufsize);
571         while( !berr && padsize > 0 ) {
572                 bytestowrite= MIN(bufsize, padsize);
573                 berr = dotarbuf(tarhandle, buf, bytestowrite) != bytestowrite;
574                 padsize -= bytestowrite;
575         }
576   
577         return berr;
578 }
579
580
581 static void do_setrattr(char *name, uint16 attr, int set)
582 {
583         uint16 oldattr;
584
585         if (!cli_getatr(cli, name, &oldattr, NULL, NULL)) return;
586
587         if (set == ATTRSET) {
588                 attr |= oldattr;
589         } else {
590                 attr = oldattr & ~attr;
591         }
592
593         if (!cli_setatr(cli, name, attr, 0)) {
594                 DEBUG(1,("setatr failed: %s\n", cli_errstr(cli)));
595         }
596 }
597
598
599 /****************************************************************************
600 append one remote file to the tar file
601 ***************************************************************************/
602 static void do_atar(char *rname,char *lname,file_info *finfo1)
603 {
604   int fnum;
605   uint32 nread=0;
606   char ftype;
607   file_info2 finfo;
608   BOOL close_done = False;
609   BOOL shallitime=True;
610   char data[65520];
611   int read_size = 65520;
612   int datalen=0;
613
614   struct timeval tp_start;
615   GetTimeOfDay(&tp_start);
616
617   ftype = '0'; /* An ordinary file ... */
618
619   if (finfo1) {
620     finfo.size  = finfo1 -> size;
621     finfo.mode  = finfo1 -> mode;
622     finfo.uid   = finfo1 -> uid;
623     finfo.gid   = finfo1 -> gid;
624     finfo.mtime = finfo1 -> mtime;
625     finfo.atime = finfo1 -> atime;
626     finfo.ctime = finfo1 -> ctime;
627   }
628   else {
629     finfo.size  = def_finfo.size;
630     finfo.mode  = def_finfo.mode;
631     finfo.uid   = def_finfo.uid;
632     finfo.gid   = def_finfo.gid;
633     finfo.mtime = def_finfo.mtime;
634     finfo.atime = def_finfo.atime;
635     finfo.ctime = def_finfo.ctime;
636   }
637
638   if (dry_run)
639     {
640       DEBUG(3,("skipping file %s of size %d bytes\n",
641                finfo.name,
642                (int)finfo.size));
643       shallitime=0;
644       ttarf+=finfo.size + TBLOCK - (finfo.size % TBLOCK);
645       ntarf++;
646       return;
647     }
648
649   fnum = cli_open(cli, rname, O_RDONLY, DENY_NONE);
650
651   dos_clean_name(rname);
652
653   if (fnum == -1) {
654           DEBUG(0,("%s opening remote file %s (%s)\n",
655                    cli_errstr(cli),rname, cur_dir));
656           return;
657   }
658
659   finfo.name = string_create_s(strlen(rname));
660   if (finfo.name == NULL) {
661           DEBUG(0, ("Unable to allocate space for finfo.name in do_atar\n"));
662           return;
663   }
664
665   safe_strcpy(finfo.name,rname, strlen(rname));
666   if (!finfo1) {
667           if (!cli_getattrE(cli, fnum, &finfo.mode, &finfo.size, NULL, &finfo.atime, &finfo.mtime)) {
668                   DEBUG(0, ("getattrE: %s\n", cli_errstr(cli)));
669                   return;
670           }
671           finfo.ctime = finfo.mtime;
672   }
673
674   DEBUG(3,("file %s attrib 0x%X\n",finfo.name,finfo.mode));
675
676   if (tar_inc && !(finfo.mode & aARCH))
677     {
678       DEBUG(4, ("skipping %s - archive bit not set\n", finfo.name));
679       shallitime=0;
680     }
681   else if (!tar_system && (finfo.mode & aSYSTEM))
682     {
683       DEBUG(4, ("skipping %s - system bit is set\n", finfo.name));
684       shallitime=0;
685     }
686   else if (!tar_hidden && (finfo.mode & aHIDDEN))
687     {
688       DEBUG(4, ("skipping %s - hidden bit is set\n", finfo.name));
689       shallitime=0;
690     }
691   else
692     {
693       DEBUG(3,("getting file %s of size %d bytes as a tar file %s",
694                finfo.name,
695                (int)finfo.size,
696                lname));
697       
698       /* write a tar header, don't bother with mode - just set to 100644 */
699       writetarheader(tarhandle, rname, finfo.size, finfo.mtime, "100644 \0", ftype);
700
701       while (nread < finfo.size && !close_done) {
702               
703               DEBUG(3,("nread=%d\n",nread));
704               
705               datalen = cli_read(cli, fnum, data, nread, read_size);
706               
707               if (datalen == -1) {
708                       DEBUG(0,("Error reading file %s : %s\n", rname, cli_errstr(cli)));
709                       break;
710               }
711               
712                   nread += datalen;
713
714                   /* if file size has increased since we made file size query, truncate
715                         read so tar header for this file will be correct.
716                    */
717
718                   if (nread > finfo.size) {
719                         datalen -= nread - finfo.size;
720                         DEBUG(0,("File size change - truncating %s to %d bytes\n", finfo.name, (int)finfo.size));
721                   }
722
723               /* add received bits of file to buffer - dotarbuf will
724                * write out in 512 byte intervals */
725               if (dotarbuf(tarhandle,data,datalen) != datalen) {
726                       DEBUG(0,("Error writing to tar file - %s\n", strerror(errno)));
727                       break;
728               }
729               
730               if (datalen == 0) {
731                       DEBUG(0,("Error reading file %s. Got 0 bytes\n", rname));
732                       break;
733               }
734
735               datalen=0;
736       }
737
738       /* pad tar file with zero's if we couldn't get entire file */
739       if (nread < finfo.size) {
740               DEBUG(0, ("Didn't get entire file. size=%d, nread=%d\n", (int)finfo.size, (int)nread));
741               if (padit(data, sizeof(data), finfo.size - nread))
742                       DEBUG(0,("Error writing tar file - %s\n", strerror(errno)));
743       }
744
745       /* round tar file to nearest block */
746       if (finfo.size % TBLOCK)
747         dozerobuf(tarhandle, TBLOCK - (finfo.size % TBLOCK));
748       
749       ttarf+=finfo.size + TBLOCK - (finfo.size % TBLOCK);
750       ntarf++;
751     }
752   
753   cli_close(cli, fnum);
754
755   if (shallitime)
756     {
757       struct timeval tp_end;
758       int this_time;
759
760       /* if shallitime is true then we didn't skip */
761       if (tar_reset && !dry_run)
762         (void) do_setrattr(finfo.name, aARCH, ATTRRESET);
763       
764       GetTimeOfDay(&tp_end);
765       this_time = 
766         (tp_end.tv_sec - tp_start.tv_sec)*1000 +
767           (tp_end.tv_usec - tp_start.tv_usec)/1000;
768       get_total_time_ms += this_time;
769       get_total_size += finfo.size;
770
771       if (tar_noisy)
772         {
773           DEBUG(0, ("%10d (%7.1f kb/s) %s\n",
774                (int)finfo.size, finfo.size / MAX(0.001, (1.024*this_time)),
775                finfo.name));
776         }
777
778       /* Thanks to Carel-Jan Engel (ease@mail.wirehub.nl) for this one */
779       DEBUG(3,("(%g kb/s) (average %g kb/s)\n",
780                finfo.size / MAX(0.001, (1.024*this_time)),
781                get_total_size / MAX(0.001, (1.024*get_total_time_ms))));
782     }
783 }
784
785 /****************************************************************************
786 Append single file to tar file (or not)
787 ***************************************************************************/
788 static void do_tar(file_info *finfo)
789 {
790   pstring rname;
791
792   if (strequal(finfo->name,"..") || strequal(finfo->name,"."))
793     return;
794
795   /* Is it on the exclude list ? */
796   if (!tar_excl && clipn) {
797     pstring exclaim;
798
799     DEBUG(5, ("Excl: strlen(cur_dir) = %d\n", (int)strlen(cur_dir)));
800
801     safe_strcpy(exclaim, cur_dir, sizeof(pstring));
802     *(exclaim+strlen(exclaim)-1)='\0';
803
804     safe_strcat(exclaim, "\\", sizeof(pstring));
805     safe_strcat(exclaim, finfo->name, sizeof(exclaim));
806
807     DEBUG(5, ("...tar_re_search: %d\n", tar_re_search));
808
809     if ((!tar_re_search && clipfind(cliplist, clipn, exclaim)) ||
810 #ifdef HAVE_REGEX_H
811         (tar_re_search && !regexec(preg, exclaim, 0, NULL, 0))) {
812 #else
813         (tar_re_search && mask_match(exclaim, cliplist[0], True))) {
814 #endif
815       DEBUG(3,("Skipping file %s\n", exclaim));
816       return;
817     }
818   }
819
820   if (finfo->mode & aDIR)
821     {
822       pstring saved_curdir;
823       pstring mtar_mask;
824
825       safe_strcpy(saved_curdir, cur_dir, sizeof(saved_curdir));
826
827       DEBUG(5, ("Sizeof(cur_dir)=%d, strlen(cur_dir)=%d, strlen(finfo->name)=%d\nname=%s,cur_dir=%s\n", (int)sizeof(cur_dir), (int)strlen(cur_dir), (int)strlen(finfo->name), finfo->name, cur_dir));
828
829       safe_strcat(cur_dir,finfo->name, sizeof(cur_dir));
830       safe_strcat(cur_dir,"\\", sizeof(cur_dir));
831
832       DEBUG(5, ("Writing a dir, Name = %s\n", cur_dir));
833
834       /* write a tar directory, don't bother with mode - just set it to
835        * 40755 */
836       writetarheader(tarhandle, cur_dir, 0, finfo->mtime, "040755 \0", '5');
837       if (tar_noisy) {
838           DEBUG(0,("                directory %s\n", cur_dir));
839       }
840       ntarf++;  /* Make sure we have a file on there */
841       safe_strcpy(mtar_mask,cur_dir, sizeof(pstring));
842       safe_strcat(mtar_mask,"*", sizeof(pstring));
843       DEBUG(5, ("Doing list with mtar_mask: %s\n", mtar_mask));
844       do_list(mtar_mask, attribute, do_tar, False, True);
845       safe_strcpy(cur_dir,saved_curdir, sizeof(pstring));
846     }
847   else
848     {
849       safe_strcpy(rname,cur_dir, sizeof(pstring));
850       safe_strcat(rname,finfo->name, sizeof(pstring));
851       do_atar(rname,finfo->name,finfo);
852     }
853 }
854
855 /****************************************************************************
856 Convert from UNIX to DOS file names
857 ***************************************************************************/
858 static void unfixtarname(char *tptr, char *fp, int l, BOOL first)
859 {
860         /* remove '.' from start of file name, convert from unix /'s to
861          * dos \'s in path. Kill any absolute path names. But only if first!
862          */
863
864         DEBUG(5, ("firstb=%lX, secondb=%lX, len=%i\n", (long)tptr, (long)fp, l));
865
866         if (first) {
867                 if (*fp == '.') {
868                         fp++;
869                         l--;
870                 }
871                 if (*fp == '\\' || *fp == '/') {
872                         fp++;
873                         l--;
874                 }
875         }
876
877         safe_strcpy(tptr, fp, l);
878         string_replace(tptr, '/', '\\');
879 }
880
881
882 /****************************************************************************
883 Move to the next block in the buffer, which may mean read in another set of
884 blocks. FIXME, we should allow more than one block to be skipped.
885 ****************************************************************************/
886 static int next_block(char *ltarbuf, char **bufferp, int bufsiz)
887 {
888   int bufread, total = 0;
889
890   DEBUG(5, ("Advancing to next block: %0lx\n", (unsigned long)*bufferp));
891   *bufferp += TBLOCK;
892   total = TBLOCK;
893
894   if (*bufferp >= (ltarbuf + bufsiz)) {
895
896     DEBUG(5, ("Reading more data into ltarbuf ...\n"));
897
898     /*
899      * Bugfix from Bob Boehmer <boehmer@worldnet.att.net>
900      * Fixes bug where read can return short if coming from
901      * a pipe.
902      */
903
904     bufread = read(tarhandle, ltarbuf, bufsiz);
905     total = bufread;
906
907     while (total < bufsiz) {
908       if (bufread < 0) { /* An error, return false */
909         return (total > 0 ? -2 : bufread);
910       }
911       if (bufread == 0) {
912         if (total <= 0) {
913             return -2;
914         }
915         break;
916       }
917       bufread = read(tarhandle, &ltarbuf[total], bufsiz - total);
918       total += bufread;
919     }
920
921     DEBUG(5, ("Total bytes read ... %i\n", total));
922
923     *bufferp = ltarbuf;
924
925   }
926
927   return(total);
928
929 }
930
931 /* Skip a file, even if it includes a long file name? */
932 static int skip_file(int skipsize)
933 {
934   int dsize = skipsize;
935
936   DEBUG(5, ("Skiping file. Size = %i\n", skipsize));
937
938   /* FIXME, we should skip more than one block at a time */
939
940   while (dsize > 0) {
941
942     if (next_block(tarbuf, &buffer_p, tbufsiz) <= 0) {
943
944         DEBUG(0, ("Empty file, short tar file, or read error: %s\n", strerror(errno)));
945         return(False);
946
947     }
948
949     dsize -= TBLOCK;
950
951   }
952
953   return(True);
954 }
955
956 /*************************************************************
957  Get a file from the tar file and store it.
958  When this is called, tarbuf already contains the first
959  file block. This is a bit broken & needs fixing.
960 **************************************************************/
961
962 static int get_file(file_info2 finfo)
963 {
964   int fnum = -1, pos = 0, dsize = 0, rsize = 0, bpos = 0;
965
966   DEBUG(5, ("get_file: file: %s, size %i\n", finfo.name, (int)finfo.size));
967
968   if (ensurepath(finfo.name) && 
969       (fnum=cli_open(cli, finfo.name, O_RDWR|O_CREAT|O_TRUNC, DENY_NONE)) == -1) {
970       DEBUG(0, ("abandoning restore\n"));
971       return(False);
972   }
973
974   /* read the blocks from the tar file and write to the remote file */
975
976   rsize = finfo.size;  /* This is how much to write */
977
978   while (rsize > 0) {
979
980     /* We can only write up to the end of the buffer */
981
982     dsize = MIN(tbufsiz - (buffer_p - tarbuf) - bpos, 65520); /* Calculate the size to write */
983     dsize = MIN(dsize, rsize);  /* Should be only what is left */
984     DEBUG(5, ("writing %i bytes, bpos = %i ...\n", dsize, bpos));
985
986     if (cli_write(cli, fnum, 0, buffer_p + bpos, pos, dsize) != dsize) {
987             DEBUG(0, ("Error writing remote file\n"));
988             return 0;
989     }
990
991     rsize -= dsize;
992     pos += dsize;
993
994     /* Now figure out how much to move in the buffer */
995
996     /* FIXME, we should skip more than one block at a time */
997
998     /* First, skip any initial part of the part written that is left over */
999     /* from the end of the first TBLOCK                                   */
1000
1001     if ((bpos) && ((bpos + dsize) >= TBLOCK)) {
1002
1003       dsize -= (TBLOCK - bpos);  /* Get rid of the end of the first block */
1004       bpos = 0;
1005
1006       if (next_block(tarbuf, &buffer_p, tbufsiz) <=0) {  /* and skip the block */
1007         DEBUG(0, ("Empty file, short tar file, or read error: %s\n", strerror(errno)));
1008         return False;
1009
1010       }
1011
1012     }
1013
1014     /*
1015      * Bugfix from Bob Boehmer <boehmer@worldnet.att.net>.
1016      * If the file being extracted is an exact multiple of
1017      * TBLOCK bytes then we don't want to extract the next
1018      * block from the tarfile here, as it will be done in
1019      * the caller of get_file().
1020      */
1021
1022     while (((rsize != 0) && (dsize >= TBLOCK)) ||
1023          ((rsize == 0) && (dsize > TBLOCK))) {
1024
1025       if (next_block(tarbuf, &buffer_p, tbufsiz) <=0) {
1026         DEBUG(0, ("Empty file, short tar file, or read error: %s\n", strerror(errno)));
1027         return False;
1028       }
1029
1030       dsize -= TBLOCK;
1031     }
1032
1033     bpos = dsize;
1034
1035   }
1036
1037   /* Now close the file ... */
1038
1039   if (!cli_close(cli, fnum)) {
1040           DEBUG(0, ("Error closing remote file\n"));
1041           return(False);
1042   }
1043
1044   /* Now we update the creation date ... */
1045
1046   DEBUG(5, ("Updating creation date on %s\n", finfo.name));
1047
1048   if (!cli_setatr(cli, finfo.name, finfo.mode, finfo.mtime)) {
1049           if (tar_real_noisy) {
1050                   DEBUG(0, ("Could not set time on file: %s\n", finfo.name));
1051                   /*return(False); */ /* Ignore, as Win95 does not allow changes */
1052           }
1053   }
1054
1055   ntarf++;
1056
1057   DEBUG(0, ("restore tar file %s of size %d bytes\n", finfo.name, (int)finfo.size));
1058   
1059   return(True);
1060 }
1061
1062 /* Create a directory.  We just ensure that the path exists and return as there
1063    is no file associated with a directory 
1064 */
1065 static int get_dir(file_info2 finfo)
1066 {
1067
1068   DEBUG(0, ("restore directory %s\n", finfo.name));
1069
1070   if (!ensurepath(finfo.name)) {
1071
1072     DEBUG(0, ("Problems creating directory\n"));
1073     return(False);
1074
1075   }
1076
1077   ntarf++;
1078   return(True);
1079
1080 }
1081 /* Get a file with a long file name ... first file has file name, next file 
1082    has the data. We only want the long file name, as the loop in do_tarput
1083    will deal with the rest.
1084 */
1085 static char * get_longfilename(file_info2 finfo)
1086 {
1087   int namesize = finfo.size + strlen(cur_dir) + 2;
1088   char *longname = malloc(namesize);
1089   int offset = 0, left = finfo.size;
1090   BOOL first = True;
1091
1092   DEBUG(5, ("Restoring a long file name: %s\n", finfo.name));
1093   DEBUG(5, ("Len = %d\n", (int)finfo.size));
1094
1095   if (longname == NULL) {
1096
1097     DEBUG(0, ("could not allocate buffer of size %d for longname\n", 
1098               (int)(finfo.size + strlen(cur_dir) + 2)));
1099     return(NULL);
1100   }
1101
1102   /* First, add cur_dir to the long file name */
1103
1104   if (strlen(cur_dir) > 0) {
1105     strncpy(longname, cur_dir, namesize);
1106     offset = strlen(cur_dir);
1107   }
1108
1109   /* Loop through the blocks picking up the name */
1110
1111   while (left > 0) {
1112
1113     if (next_block(tarbuf, &buffer_p, tbufsiz) <= 0) {
1114
1115       DEBUG(0, ("Empty file, short tar file, or read error: %s\n", strerror(errno)));
1116       return(NULL);
1117
1118     }
1119
1120     unfixtarname(longname + offset, buffer_p, MIN(TBLOCK, finfo.size), first--);
1121     DEBUG(5, ("UnfixedName: %s, buffer: %s\n", longname, buffer_p));
1122
1123     offset += TBLOCK;
1124     left -= TBLOCK;
1125
1126   }
1127
1128   return(longname);
1129
1130 }
1131
1132 static void do_tarput(void)
1133 {
1134   file_info2 finfo;
1135   struct timeval tp_start;
1136   char *longfilename = NULL, linkflag;
1137   int skip = False;
1138
1139   GetTimeOfDay(&tp_start);
1140
1141   DEBUG(5, ("RJS do_tarput called ...\n"));
1142
1143   buffer_p = tarbuf + tbufsiz;  /* init this to force first read */
1144
1145   /* Now read through those files ... */
1146
1147   while (True) {
1148
1149     /* Get us to the next block, or the first block first time around */
1150
1151     if (next_block(tarbuf, &buffer_p, tbufsiz) <= 0) {
1152
1153       DEBUG(0, ("Empty file, short tar file, or read error: %s\n", strerror(errno)));
1154
1155       return;
1156
1157     }
1158
1159     DEBUG(5, ("Reading the next header ...\n"));
1160
1161     switch (readtarheader((union hblock *) buffer_p, &finfo, cur_dir)) {
1162
1163     case -2:    /* Hmm, not good, but not fatal */
1164       DEBUG(0, ("Skipping %s...\n", finfo.name));
1165       if ((next_block(tarbuf, &buffer_p, tbufsiz) <= 0) &&
1166           !skip_file(finfo.size)) {
1167
1168         DEBUG(0, ("Short file, bailing out...\n"));
1169         return;
1170
1171       }
1172
1173       break;
1174
1175     case -1:
1176       DEBUG(0, ("abandoning restore, -1 from read tar header\n"));
1177       return;
1178
1179     case 0: /* chksum is zero - looks like an EOF */
1180       DEBUG(0, ("tar: restored %d files and directories\n", ntarf));
1181       return;        /* Hmmm, bad here ... */
1182
1183     default: 
1184       /* No action */
1185
1186       break;
1187
1188     }
1189
1190     /* Now, do we have a long file name? */
1191
1192     if (longfilename != NULL) {
1193
1194       free(finfo.name);   /* Free the space already allocated */
1195       finfo.name = longfilename;
1196       longfilename = NULL;
1197
1198     }
1199
1200     /* Well, now we have a header, process the file ...            */
1201
1202     /* Should we skip the file? We have the long name as well here */
1203
1204     skip = clipn &&
1205       ((!tar_re_search && clipfind(cliplist, clipn, finfo.name) ^ tar_excl)
1206 #ifdef HAVE_REGEX_H
1207       || (tar_re_search && !regexec(preg, finfo.name, 0, NULL, 0)));
1208 #else
1209       || (tar_re_search && mask_match(finfo.name, cliplist[0], True)));
1210 #endif
1211
1212   DEBUG(5, ("Skip = %i, cliplist=%s, file=%s\n", skip, (cliplist?cliplist[0]:NULL), finfo.name));
1213
1214   if (skip) {
1215
1216     skip_file(finfo.size);
1217     continue;
1218
1219   }
1220
1221     /* We only get this far if we should process the file */
1222   linkflag = ((union hblock *)buffer_p) -> dbuf.linkflag;
1223
1224     switch (linkflag) {
1225
1226     case '0':  /* Should use symbolic names--FIXME */
1227
1228       /* 
1229        * Skip to the next block first, so we can get the file, FIXME, should
1230        * be in get_file ...
1231        * The 'finfo.size != 0' fix is from Bob Boehmer <boehmer@worldnet.att.net>
1232        * Fixes bug where file size in tarfile is zero.
1233        */
1234
1235       if ((finfo.size != 0) && next_block(tarbuf, &buffer_p, tbufsiz) <=0) {
1236         DEBUG(0, ("Short file, bailing out...\n"));
1237         return;
1238       }
1239       if (!get_file(finfo)) {
1240         DEBUG(0, ("Abandoning restore\n"));
1241         return;
1242
1243       }
1244       break;
1245
1246     case '5':
1247       if (!get_dir(finfo)) {
1248         DEBUG(0, ("Abandoning restore \n"));
1249         return;
1250       }
1251       break;
1252
1253     case 'L':
1254       longfilename = get_longfilename(finfo);
1255       if (!longfilename) {
1256         DEBUG(0, ("abandoning restore\n"));
1257         return;
1258
1259       }
1260       DEBUG(5, ("Long file name: %s\n", longfilename));
1261       break;
1262
1263     default:
1264       skip_file(finfo.size);  /* Don't handle these yet */
1265       break;
1266
1267     }
1268
1269   }
1270
1271
1272 }
1273
1274
1275 /*
1276  * samba interactive commands
1277  */
1278
1279 /****************************************************************************
1280 Blocksize command
1281 ***************************************************************************/
1282 void cmd_block(void)
1283 {
1284   fstring buf;
1285   int block;
1286
1287   if (!next_token_nr(NULL,buf,NULL,sizeof(buf)))
1288     {
1289       DEBUG(0, ("blocksize <n>\n"));
1290       return;
1291     }
1292
1293   block=atoi(buf);
1294   if (block < 0 || block > 65535)
1295     {
1296       DEBUG(0, ("blocksize out of range"));
1297       return;
1298     }
1299
1300   blocksize=block;
1301   DEBUG(2,("blocksize is now %d\n", blocksize));
1302 }
1303
1304 /****************************************************************************
1305 command to set incremental / reset mode
1306 ***************************************************************************/
1307 void cmd_tarmode(void)
1308 {
1309   fstring buf;
1310
1311   while (next_token_nr(NULL,buf,NULL,sizeof(buf))) {
1312     if (strequal(buf, "full"))
1313       tar_inc=False;
1314     else if (strequal(buf, "inc"))
1315       tar_inc=True;
1316     else if (strequal(buf, "reset"))
1317       tar_reset=True;
1318     else if (strequal(buf, "noreset"))
1319       tar_reset=False;
1320     else if (strequal(buf, "system"))
1321       tar_system=True;
1322     else if (strequal(buf, "nosystem"))
1323       tar_system=False;
1324     else if (strequal(buf, "hidden"))
1325       tar_hidden=True;
1326     else if (strequal(buf, "nohidden"))
1327       tar_hidden=False;
1328     else if (strequal(buf, "verbose") || strequal(buf, "noquiet"))
1329       tar_noisy=True;
1330     else if (strequal(buf, "quiet") || strequal(buf, "noverbose"))
1331       tar_noisy=False;
1332     else DEBUG(0, ("tarmode: unrecognised option %s\n", buf));
1333   }
1334
1335   DEBUG(0, ("tarmode is now %s, %s, %s, %s, %s\n",
1336             tar_inc ? "incremental" : "full",
1337             tar_system ? "system" : "nosystem",
1338             tar_hidden ? "hidden" : "nohidden",
1339             tar_reset ? "reset" : "noreset",
1340             tar_noisy ? "verbose" : "quiet"));
1341
1342 }
1343
1344 /****************************************************************************
1345 Feeble attrib command
1346 ***************************************************************************/
1347 void cmd_setmode(void)
1348 {
1349   char *q;
1350   fstring buf;
1351   pstring fname;
1352   uint16 attra[2];
1353   int direct=1;
1354
1355   attra[0] = attra[1] = 0;
1356
1357   if (!next_token_nr(NULL,buf,NULL,sizeof(buf)))
1358     {
1359       DEBUG(0, ("setmode <filename> <[+|-]rsha>\n"));
1360       return;
1361     }
1362
1363   safe_strcpy(fname, cur_dir, sizeof(pstring));
1364   safe_strcat(fname, buf, sizeof(pstring));
1365
1366   while (next_token_nr(NULL,buf,NULL,sizeof(buf))) {
1367     q=buf;
1368
1369     while(*q)
1370       switch (*q++) {
1371       case '+': direct=1;
1372         break;
1373       case '-': direct=0;
1374         break;
1375       case 'r': attra[direct]|=aRONLY;
1376         break;
1377       case 'h': attra[direct]|=aHIDDEN;
1378         break;
1379       case 's': attra[direct]|=aSYSTEM;
1380         break;
1381       case 'a': attra[direct]|=aARCH;
1382         break;
1383       default: DEBUG(0, ("setmode <filename> <perm=[+|-]rsha>\n"));
1384         return;
1385       }
1386   }
1387
1388   if (attra[ATTRSET]==0 && attra[ATTRRESET]==0)
1389     {
1390       DEBUG(0, ("setmode <filename> <[+|-]rsha>\n"));
1391       return;
1392     }
1393
1394   DEBUG(2, ("\nperm set %d %d\n", attra[ATTRSET], attra[ATTRRESET]));
1395   do_setrattr(fname, attra[ATTRSET], ATTRSET);
1396   do_setrattr(fname, attra[ATTRRESET], ATTRRESET);
1397 }
1398
1399 /****************************************************************************
1400 Principal command for creating / extracting
1401 ***************************************************************************/
1402 void cmd_tar(void)
1403 {
1404   fstring buf;
1405   char **argl;
1406   int argcl;
1407
1408   if (!next_token_nr(NULL,buf,NULL,sizeof(buf)))
1409     {
1410       DEBUG(0,("tar <c|x>[IXbgan] <filename>\n"));
1411       return;
1412     }
1413
1414   argl=toktocliplist(&argcl, NULL);
1415   if (!tar_parseargs(argcl, argl, buf, 0))
1416     return;
1417
1418   process_tar();
1419
1420   free(argl);
1421 }
1422
1423 /****************************************************************************
1424 Command line (option) version
1425 ***************************************************************************/
1426 int process_tar(void)
1427 {
1428   initarbuf();
1429   switch(tar_type) {
1430   case 'x':
1431
1432 #if 0
1433     do_tarput2();
1434 #else
1435     do_tarput();
1436 #endif
1437     free(tarbuf);
1438     close(tarhandle);
1439     break;
1440   case 'r':
1441   case 'c':
1442     if (clipn && tar_excl) {
1443       int i;
1444       pstring tarmac;
1445
1446       for (i=0; i<clipn; i++) {
1447         DEBUG(5,("arg %d = %s\n", i, cliplist[i]));
1448
1449         if (*(cliplist[i]+strlen(cliplist[i])-1)=='\\') {
1450           *(cliplist[i]+strlen(cliplist[i])-1)='\0';
1451         }
1452         
1453         if (strrchr_m(cliplist[i], '\\')) {
1454           pstring saved_dir;
1455           
1456           safe_strcpy(saved_dir, cur_dir, sizeof(pstring));
1457           
1458           if (*cliplist[i]=='\\') {
1459             safe_strcpy(tarmac, cliplist[i], sizeof(pstring));
1460           } else {
1461             safe_strcpy(tarmac, cur_dir, sizeof(pstring));
1462             safe_strcat(tarmac, cliplist[i], sizeof(pstring));
1463           }
1464           safe_strcpy(cur_dir, tarmac, sizeof(pstring));
1465           *(strrchr_m(cur_dir, '\\')+1)='\0';
1466
1467           DEBUG(5, ("process_tar, do_list with tarmac: %s\n", tarmac));
1468           do_list(tarmac,attribute,do_tar, False, True);
1469           safe_strcpy(cur_dir,saved_dir, sizeof(pstring));
1470         } else {
1471           safe_strcpy(tarmac, cur_dir, sizeof(pstring));
1472           safe_strcat(tarmac, cliplist[i], sizeof(pstring));
1473           DEBUG(5, ("process_tar, do_list with tarmac: %s\n", tarmac));
1474           do_list(tarmac,attribute,do_tar, False, True);
1475         }
1476       }
1477     } else {
1478       pstring mask;
1479       safe_strcpy(mask,cur_dir, sizeof(pstring));
1480       DEBUG(5, ("process_tar, do_list with mask: %s\n", mask));
1481       safe_strcat(mask,"\\*", sizeof(pstring));
1482       do_list(mask,attribute,do_tar,False, True);
1483     }
1484     
1485     if (ntarf) dotareof(tarhandle);
1486     close(tarhandle);
1487     free(tarbuf);
1488     
1489     DEBUG(0, ("tar: dumped %d files and directories\n", ntarf));
1490     DEBUG(0, ("Total bytes written: %.0f\n", (double)ttarf));
1491     break;
1492   }
1493
1494   if (must_free_cliplist) {
1495     int i;
1496     for (i = 0; i < clipn; ++i) {
1497       free(cliplist[i]);
1498     }
1499     free(cliplist);
1500     cliplist = NULL;
1501     clipn = 0;
1502     must_free_cliplist = False;
1503   }
1504
1505   return(0);
1506 }
1507
1508 /****************************************************************************
1509 Find a token (filename) in a clip list
1510 ***************************************************************************/
1511 static int clipfind(char **aret, int ret, char *tok)
1512 {
1513   if (aret==NULL) return 0;
1514
1515   /* ignore leading slashes or dots in token */
1516   while(strchr_m("/\\.", *tok)) tok++;
1517
1518   while(ret--) {
1519     char *pkey=*aret++;
1520
1521     /* ignore leading slashes or dots in list */
1522     while(strchr_m("/\\.", *pkey)) pkey++;
1523
1524     if (!strslashcmp(pkey, tok)) return 1;
1525   }
1526
1527   return 0;
1528 }
1529
1530 /****************************************************************************
1531 Read list of files to include from the file and initialize cliplist
1532 accordingly.
1533 ***************************************************************************/
1534 static int read_inclusion_file(char *filename)
1535 {
1536   XFILE *inclusion = NULL;
1537   char buf[MAXPATHLEN + 1];
1538   char *inclusion_buffer = NULL;
1539   int inclusion_buffer_size = 0;
1540   int inclusion_buffer_sofar = 0;
1541   char *p;
1542   char *tmpstr;
1543   int i;
1544   int error = 0;
1545
1546   clipn = 0;
1547   buf[MAXPATHLEN] = '\0'; /* guarantee null-termination */
1548   if ((inclusion = x_fopen(filename, O_RDONLY, 0)) == NULL) {
1549     /* XXX It would be better to include a reason for failure, but without
1550      * autoconf, it's hard to use strerror, sys_errlist, etc.
1551      */
1552     DEBUG(0,("Unable to open inclusion file %s\n", filename));
1553     return 0;
1554   }
1555
1556   while ((! error) && (x_fgets(buf, sizeof(buf)-1, inclusion))) {
1557     if (inclusion_buffer == NULL) {
1558       inclusion_buffer_size = 1024;
1559       if ((inclusion_buffer = malloc(inclusion_buffer_size)) == NULL) {
1560         DEBUG(0,("failure allocating buffer to read inclusion file\n"));
1561         error = 1;
1562         break;
1563       }
1564     }
1565     
1566     if (buf[strlen(buf)-1] == '\n') {
1567       buf[strlen(buf)-1] = '\0';
1568     }
1569     
1570     if ((strlen(buf) + 1 + inclusion_buffer_sofar) >= inclusion_buffer_size) {
1571       char *ib;
1572       inclusion_buffer_size *= 2;
1573       ib = Realloc(inclusion_buffer,inclusion_buffer_size);
1574       if (! ib) {
1575         DEBUG(0,("failure enlarging inclusion buffer to %d bytes\n",
1576                  inclusion_buffer_size));
1577         error = 1;
1578         break;
1579       }
1580       else inclusion_buffer = ib;
1581     }
1582     
1583     safe_strcpy(inclusion_buffer + inclusion_buffer_sofar, buf, inclusion_buffer_size - inclusion_buffer_sofar);
1584     inclusion_buffer_sofar += strlen(buf) + 1;
1585     clipn++;
1586   }
1587   x_fclose(inclusion);
1588
1589   if (! error) {
1590     /* Allocate an array of clipn + 1 char*'s for cliplist */
1591     cliplist = malloc((clipn + 1) * sizeof(char *));
1592     if (cliplist == NULL) {
1593       DEBUG(0,("failure allocating memory for cliplist\n"));
1594       error = 1;
1595     } else {
1596       cliplist[clipn] = NULL;
1597       p = inclusion_buffer;
1598       for (i = 0; (! error) && (i < clipn); i++) {
1599         /* set current item to NULL so array will be null-terminated even if
1600          * malloc fails below. */
1601         cliplist[i] = NULL;
1602         if ((tmpstr = (char *)malloc(strlen(p)+1)) == NULL) {
1603           DEBUG(0, ("Could not allocate space for a cliplist item, # %i\n", i));
1604           error = 1;
1605         } else {
1606           unfixtarname(tmpstr, p, strlen(p) + 1, True);
1607           cliplist[i] = tmpstr;
1608           if ((p = strchr_m(p, '\000')) == NULL) {
1609             DEBUG(0,("INTERNAL ERROR: inclusion_buffer is of unexpected contents.\n"));
1610             abort();
1611           }
1612         }
1613         ++p;
1614       }
1615       must_free_cliplist = True;
1616     }
1617   }
1618
1619   if (inclusion_buffer) {
1620     free(inclusion_buffer);
1621   }
1622   if (error) {
1623     if (cliplist) {
1624       char **pp;
1625       /* We know cliplist is always null-terminated */
1626       for (pp = cliplist; *pp; ++pp) {
1627         free(*pp);
1628       }
1629       free(cliplist);
1630       cliplist = NULL;
1631       must_free_cliplist = False;
1632     }
1633     return 0;
1634   }
1635   
1636   /* cliplist and its elements are freed at the end of process_tar. */
1637   return 1;
1638 }
1639
1640 /****************************************************************************
1641 Parse tar arguments. Sets tar_type, tar_excl, etc.
1642 ***************************************************************************/
1643 int tar_parseargs(int argc, char *argv[], char *Optarg, int Optind)
1644 {
1645   char tar_clipfl='\0';
1646
1647   /* Reset back to defaults - could be from interactive version 
1648    * reset mode and archive mode left as they are though
1649    */
1650   tar_type='\0';
1651   tar_excl=True;
1652   dry_run=False;
1653
1654   while (*Optarg) 
1655     switch(*Optarg++) {
1656     case 'c':
1657       tar_type='c';
1658       break;
1659     case 'x':
1660       if (tar_type=='c') {
1661         printf("Tar must be followed by only one of c or x.\n");
1662         return 0;
1663       }
1664       tar_type='x';
1665       break;
1666     case 'b':
1667       if (Optind>=argc || !(blocksize=atoi(argv[Optind]))) {
1668         DEBUG(0,("Option b must be followed by valid blocksize\n"));
1669         return 0;
1670       } else {
1671         Optind++;
1672       }
1673       break;
1674     case 'g':
1675       tar_inc=True;
1676       break;
1677     case 'N':
1678       if (Optind>=argc) {
1679         DEBUG(0,("Option N must be followed by valid file name\n"));
1680         return 0;
1681       } else {
1682         SMB_STRUCT_STAT stbuf;
1683         extern time_t newer_than;
1684         
1685         if (sys_stat(argv[Optind], &stbuf) == 0) {
1686           newer_than = stbuf.st_mtime;
1687           DEBUG(1,("Getting files newer than %s",
1688                    asctime(LocalTime(&newer_than))));
1689           Optind++;
1690         } else {
1691           DEBUG(0,("Error setting newer-than time\n"));
1692           return 0;
1693         }
1694       }
1695       break;
1696     case 'a':
1697       tar_reset=True;
1698       break;
1699     case 'q':
1700       tar_noisy=False;
1701       break;
1702     case 'I':
1703       if (tar_clipfl) {
1704         DEBUG(0,("Only one of I,X,F must be specified\n"));
1705         return 0;
1706       }
1707       tar_clipfl='I';
1708       break;
1709     case 'X':
1710       if (tar_clipfl) {
1711         DEBUG(0,("Only one of I,X,F must be specified\n"));
1712         return 0;
1713       }
1714       tar_clipfl='X';
1715       break;
1716     case 'F':
1717       if (tar_clipfl) {
1718         DEBUG(0,("Only one of I,X,F must be specified\n"));
1719         return 0;
1720       }
1721       tar_clipfl='F';
1722       break;
1723     case 'r':
1724       DEBUG(0, ("tar_re_search set\n"));
1725       tar_re_search = True;
1726       break;
1727     case 'n':
1728       if (tar_type == 'c') {
1729         DEBUG(0, ("dry_run set\n"));
1730         dry_run = True;
1731       } else {
1732         DEBUG(0, ("n is only meaningful when creating a tar-file\n"));
1733         return 0;
1734       }
1735       break;
1736     default:
1737       DEBUG(0,("Unknown tar option\n"));
1738       return 0;
1739     }
1740
1741   if (!tar_type) {
1742     printf("Option T must be followed by one of c or x.\n");
1743     return 0;
1744   }
1745
1746   /* tar_excl is true if cliplist lists files to be included.
1747    * Both 'I' and 'F' mean include. */
1748   tar_excl=tar_clipfl!='X';
1749
1750   if (tar_clipfl=='F') {
1751     if (argc-Optind-1 != 1) {
1752       DEBUG(0,("Option F must be followed by exactly one filename.\n"));
1753       return 0;
1754     }
1755     if (! read_inclusion_file(argv[Optind+1])) {
1756       return 0;
1757     }
1758   } else if (Optind+1<argc && !tar_re_search) { /* For backwards compatibility */
1759     char *tmpstr;
1760     char **tmplist;
1761     int clipcount;
1762
1763     cliplist=argv+Optind+1;
1764     clipn=argc-Optind-1;
1765     clipcount = clipn;
1766
1767     if ((tmplist=malloc(clipn*sizeof(char *))) == NULL) {
1768       DEBUG(0, ("Could not allocate space to process cliplist, count = %i\n", 
1769                clipn)
1770            );
1771       return 0;
1772     }
1773
1774     for (clipcount = 0; clipcount < clipn; clipcount++) {
1775
1776       DEBUG(5, ("Processing an item, %s\n", cliplist[clipcount]));
1777
1778       if ((tmpstr = (char *)malloc(strlen(cliplist[clipcount])+1)) == NULL) {
1779         DEBUG(0, ("Could not allocate space for a cliplist item, # %i\n",
1780                  clipcount)
1781              );
1782         return 0;
1783       }
1784       unfixtarname(tmpstr, cliplist[clipcount], strlen(cliplist[clipcount]) + 1, True);
1785       tmplist[clipcount] = tmpstr;
1786       DEBUG(5, ("Processed an item, %s\n", tmpstr));
1787
1788       DEBUG(5, ("Cliplist is: %s\n", cliplist[0]));
1789     }
1790     cliplist = tmplist;
1791     must_free_cliplist = True;
1792   }
1793
1794   if (Optind+1<argc && tar_re_search) {  /* Doing regular expression seaches */
1795 #ifdef HAVE_REGEX_H
1796     int errcode;
1797
1798     if ((preg = (regex_t *)malloc(65536)) == NULL) {
1799
1800       DEBUG(0, ("Could not allocate buffer for regular expression search\n"));
1801       return;
1802
1803     }
1804
1805     if (errcode = regcomp(preg, argv[Optind + 1], REG_EXTENDED)) {
1806       char errstr[1024];
1807       size_t errlen;
1808
1809       errlen = regerror(errcode, preg, errstr, sizeof(errstr) - 1);
1810       
1811       DEBUG(0, ("Could not compile pattern buffer for re search: %s\n%s\n", argv[Optind + 1], errstr));
1812       return;
1813
1814     }
1815 #endif
1816
1817     clipn=argc-Optind-1;
1818     cliplist=argv+Optind+1;
1819
1820   }
1821
1822   if (Optind>=argc || !strcmp(argv[Optind], "-")) {
1823     /* Sets tar handle to either 0 or 1, as appropriate */
1824     tarhandle=(tar_type=='c');
1825     /*
1826      * Make sure that dbf points to stderr if we are using stdout for 
1827      * tar output
1828     */
1829     if (tarhandle == 1) 
1830       dbf = x_stderr;
1831   } else {
1832     if (tar_type=='c' && (dry_run || strcmp(argv[Optind], "/dev/null")==0))
1833       {
1834         if (!dry_run) {
1835           DEBUG(0,("Output is /dev/null, assuming dry_run"));
1836           dry_run = True;
1837         }
1838         tarhandle=-1;
1839       } else
1840     if ((tar_type=='x' && (tarhandle = sys_open(argv[Optind], O_RDONLY, 0)) == -1)
1841         || (tar_type=='c' && (tarhandle=sys_creat(argv[Optind], 0644)) < 0))
1842       {
1843         DEBUG(0,("Error opening local file %s - %s\n",
1844                  argv[Optind], strerror(errno)));
1845         return(0);
1846       }
1847   }
1848
1849   return 1;
1850 }