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