report exit code when failing a test
[rsync/rsync.git] / util.c
1 /* 
2    Copyright (C) Andrew Tridgell 1996
3    Copyright (C) Paul Mackerras 1996
4    
5    This program is free software; you can redistribute it and/or modify
6    it under the terms of the GNU General Public License as published by
7    the Free Software Foundation; either version 2 of the License, or
8    (at your option) any later version.
9    
10    This program is distributed in the hope that it will be useful,
11    but WITHOUT ANY WARRANTY; without even the implied warranty of
12    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13    GNU General Public License for more details.
14    
15    You should have received a copy of the GNU General Public License
16    along with this program; if not, write to the Free Software
17    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
18 */
19
20 /*
21   Utilities used in rsync 
22
23   tridge, June 1996
24   */
25 #include "rsync.h"
26
27 extern int verbose;
28
29 /* this is taken from CVS */
30 int piped_child(char **command,int *f_in,int *f_out)
31 {
32   int pid;
33   int to_child_pipe[2];
34   int from_child_pipe[2];
35
36   if (pipe(to_child_pipe) < 0 ||
37       pipe(from_child_pipe) < 0) {
38     rprintf(FERROR,"pipe: %s\n",strerror(errno));
39     exit_cleanup(RERR_IPC);
40   }
41
42
43   pid = do_fork();
44   if (pid < 0) {
45     rprintf(FERROR,"fork: %s\n",strerror(errno));
46     exit_cleanup(RERR_IPC);
47   }
48
49   if (pid == 0)
50     {
51       extern int orig_umask;
52       if (dup2(to_child_pipe[0], STDIN_FILENO) < 0 ||
53           close(to_child_pipe[1]) < 0 ||
54           close(from_child_pipe[0]) < 0 ||
55           dup2(from_child_pipe[1], STDOUT_FILENO) < 0) {
56         rprintf(FERROR,"Failed to dup/close : %s\n",strerror(errno));
57         exit_cleanup(RERR_IPC);
58       }
59       if (to_child_pipe[0] != STDIN_FILENO) close(to_child_pipe[0]);
60       if (from_child_pipe[1] != STDOUT_FILENO) close(from_child_pipe[1]);
61       umask(orig_umask);
62       execvp(command[0], command);
63       rprintf(FERROR,"Failed to exec %s : %s\n",
64               command[0],strerror(errno));
65       exit_cleanup(RERR_IPC);
66     }
67
68   if (close(from_child_pipe[1]) < 0 ||
69       close(to_child_pipe[0]) < 0) {
70     rprintf(FERROR,"Failed to close : %s\n",strerror(errno));   
71     exit_cleanup(RERR_IPC);
72   }
73
74   *f_in = from_child_pipe[0];
75   *f_out = to_child_pipe[1];
76
77   return pid;
78 }
79
80 int local_child(int argc, char **argv,int *f_in,int *f_out)
81 {
82         int pid;
83         int to_child_pipe[2];
84         int from_child_pipe[2];
85
86         if (pipe(to_child_pipe) < 0 ||
87             pipe(from_child_pipe) < 0) {
88                 rprintf(FERROR,"pipe: %s\n",strerror(errno));
89                 exit_cleanup(RERR_IPC);
90         }
91
92
93         pid = do_fork();
94         if (pid < 0) {
95                 rprintf(FERROR,"fork: %s\n",strerror(errno));
96                 exit_cleanup(RERR_IPC);
97         }
98
99         if (pid == 0) {
100                 extern int am_sender;
101                 extern int am_server;
102
103                 am_sender = !am_sender;
104                 am_server = 1;          
105
106                 if (dup2(to_child_pipe[0], STDIN_FILENO) < 0 ||
107                     close(to_child_pipe[1]) < 0 ||
108                     close(from_child_pipe[0]) < 0 ||
109                     dup2(from_child_pipe[1], STDOUT_FILENO) < 0) {
110                         rprintf(FERROR,"Failed to dup/close : %s\n",strerror(errno));
111                         exit_cleanup(RERR_IPC);
112                 }
113                 if (to_child_pipe[0] != STDIN_FILENO) close(to_child_pipe[0]);
114                 if (from_child_pipe[1] != STDOUT_FILENO) close(from_child_pipe[1]);
115                 start_server(STDIN_FILENO, STDOUT_FILENO, argc, argv);
116         }
117
118         if (close(from_child_pipe[1]) < 0 ||
119             close(to_child_pipe[0]) < 0) {
120                 rprintf(FERROR,"Failed to close : %s\n",strerror(errno));   
121                 exit_cleanup(RERR_IPC);
122         }
123
124         *f_in = from_child_pipe[0];
125         *f_out = to_child_pipe[1];
126   
127         return pid;
128 }
129
130
131
132 void out_of_memory(char *str)
133 {
134   rprintf(FERROR,"ERROR: out of memory in %s\n",str);
135   exit_cleanup(RERR_MALLOC);
136 }
137
138 void overflow(char *str)
139 {
140   rprintf(FERROR,"ERROR: buffer overflow in %s\n",str);
141   exit_cleanup(RERR_MALLOC);
142 }
143
144
145
146 int set_modtime(char *fname,time_t modtime)
147 {
148         extern int dry_run;
149         if (dry_run) return 0;
150         {
151 #ifdef HAVE_UTIMBUF
152                 struct utimbuf tbuf;  
153                 tbuf.actime = time(NULL);
154                 tbuf.modtime = modtime;
155                 return utime(fname,&tbuf);
156 #elif defined(HAVE_UTIME)
157                 time_t t[2];
158                 t[0] = time(NULL);
159                 t[1] = modtime;
160                 return utime(fname,t);
161 #else
162                 struct timeval t[2];
163                 t[0].tv_sec = time(NULL);
164                 t[0].tv_usec = 0;
165                 t[1].tv_sec = modtime;
166                 t[1].tv_usec = 0;
167                 return utimes(fname,t);
168 #endif
169         }
170 }
171
172
173 /****************************************************************************
174 create any necessary directories in fname. Unfortunately we don't know
175 what perms to give the directory when this is called so we need to rely
176 on the umask
177 ****************************************************************************/
178 int create_directory_path(char *fname)
179 {
180         extern int orig_umask;
181         char *p;
182
183         while (*fname == '/') fname++;
184         while (strncmp(fname,"./",2)==0) fname += 2;
185
186         p = fname;
187         while ((p=strchr(p,'/'))) {
188                 *p = 0;
189                 do_mkdir(fname,0777 & ~orig_umask); 
190                 *p = '/';
191                 p++;
192         }
193         return 0;
194 }
195
196
197 /* Write LEN bytes at PTR to descriptor DESC, retrying if interrupted.
198    Return LEN upon success, write's (negative) error code otherwise.  
199
200    derived from GNU C's cccp.c.
201 */
202 static int full_write(int desc, char *ptr, int len)
203 {
204         int total_written;
205         
206         total_written = 0;
207         while (len > 0) {
208                 int written = write (desc, ptr, len);
209                 if (written < 0)  {
210 #ifdef EINTR
211                         if (errno == EINTR)
212                                 continue;
213 #endif
214                         return written;
215                 }
216                 total_written += written;
217                 ptr += written;
218                 len -= written;
219         }
220         return total_written;
221 }
222
223 /* Read LEN bytes at PTR from descriptor DESC, retrying if interrupted.
224    Return the actual number of bytes read, zero for EOF, or negative
225    for an error.  
226
227    derived from GNU C's cccp.c. */
228 static int safe_read(int desc, char *ptr, int len)
229 {
230         int n_chars;
231  
232         if (len <= 0)
233                 return len;
234  
235 #ifdef EINTR
236         do {
237                 n_chars = read(desc, ptr, len);
238         } while (n_chars < 0 && errno == EINTR);
239 #else
240         n_chars = read(desc, ptr, len);
241 #endif
242  
243         return n_chars;
244 }
245
246
247 /* copy a file - this is used in conjunction with the --temp-dir option */
248 int copy_file(char *source, char *dest, mode_t mode)
249 {
250         int ifd;
251         int ofd;
252         char buf[1024 * 8];
253         int len;   /* Number of bytes read into `buf'. */
254
255         ifd = do_open(source, O_RDONLY, 0);
256         if (ifd == -1) {
257                 rprintf(FERROR,"open %s: %s\n",
258                         source,strerror(errno));
259                 return -1;
260         }
261
262         if (robust_unlink(dest) && errno != ENOENT) {
263                 rprintf(FERROR,"unlink %s: %s\n",
264                         dest,strerror(errno));
265                 return -1;
266         }
267
268         ofd = do_open(dest, O_WRONLY | O_CREAT | O_TRUNC | O_EXCL, mode);
269         if (ofd == -1) {
270                 rprintf(FERROR,"open %s: %s\n",
271                         dest,strerror(errno));
272                 close(ifd);
273                 return -1;
274         }
275
276         while ((len = safe_read(ifd, buf, sizeof(buf))) > 0) {
277                 if (full_write(ofd, buf, len) < 0) {
278                         rprintf(FERROR,"write %s: %s\n",
279                                 dest,strerror(errno));
280                         close(ifd);
281                         close(ofd);
282                         return -1;
283                 }
284         }
285
286         close(ifd);
287         close(ofd);
288
289         if (len < 0) {
290                 rprintf(FERROR,"read %s: %s\n",
291                         source,strerror(errno));
292                 return -1;
293         }
294
295         return 0;
296 }
297
298 /*
299   Robust unlink: some OS'es (HPUX) refuse to unlink busy files, so
300   rename to <path>/.rsyncNNN instead. Note that successive rsync runs
301   will shuffle the filenames around a bit as long as the file is still
302   busy; this is because this function does not know if the unlink call
303   is due to a new file coming in, or --delete trying to remove old
304   .rsyncNNN files, hence it renames it each time.
305 */
306 /* MAX_RENAMES should be 10**MAX_RENAMES_DIGITS */
307 #define MAX_RENAMES_DIGITS 3
308 #define MAX_RENAMES 1000
309
310 int robust_unlink(char *fname)
311 {
312 #ifndef ETXTBSY
313         return do_unlink(fname);
314 #else
315         static int counter = 1;
316         int rc, pos, start;
317         char path[MAXPATHLEN];
318
319         rc = do_unlink(fname);
320         if ((rc == 0) || (errno != ETXTBSY))
321                 return rc;
322
323         strlcpy(path, fname, MAXPATHLEN);
324
325         pos = strlen(path);
326         while((path[--pos] != '/') && (pos >= 0))
327                 ;
328         ++pos;
329         strlcpy(&path[pos], ".rsync", MAXPATHLEN-pos);
330         pos += sizeof(".rsync")-1;
331
332         if (pos > (MAXPATHLEN-MAX_RENAMES_DIGITS-1)) {
333                 errno = ETXTBSY;
334                 return -1;
335         }
336
337         /* start where the last one left off to reduce chance of clashes */
338         start = counter;
339         do {
340                 sprintf(&path[pos], "%03d", counter);
341                 if (++counter >= MAX_RENAMES)
342                         counter = 1;
343         } while (((rc = access(path, 0)) == 0) && (counter != start));
344
345         if (verbose > 0)
346                 rprintf(FINFO,"renaming %s to %s because of text busy\n",
347                                             fname, path);
348
349         /* maybe we should return rename()'s exit status? Nah. */
350         if (do_rename(fname, path) != 0) {
351                 errno = ETXTBSY;
352                 return -1;
353         }
354         return 0;
355 #endif
356 }
357
358 int robust_rename(char *from, char *to)
359 {
360 #ifndef ETXTBSY
361         return do_rename(from, to);
362 #else
363         int rc = do_rename(from, to);
364         if ((rc == 0) || (errno != ETXTBSY))
365                 return rc;
366         if (robust_unlink(to) != 0)
367                 return -1;
368         return do_rename(from, to);
369 #endif
370 }
371
372
373 static pid_t all_pids[10];
374 static int num_pids;
375
376 /* fork and record the pid of the child */
377 pid_t do_fork(void)
378 {
379         pid_t newpid = fork();
380         
381         if (newpid) {
382                 all_pids[num_pids++] = newpid;
383         }
384         return newpid;
385 }
386
387 /* kill all children */
388 void kill_all(int sig)
389 {
390         int i;
391         for (i=0;i<num_pids;i++) {
392                 if (all_pids[i] != getpid())
393                         kill(all_pids[i], sig);
394         }
395 }
396
397 /* turn a user name into a uid */
398 int name_to_uid(char *name, uid_t *uid)
399 {
400         struct passwd *pass;
401         if (!name || !*name) return 0;
402         pass = getpwnam(name);
403         if (pass) {
404                 *uid = pass->pw_uid;
405                 return 1;
406         }
407         return 0;
408 }
409
410 /* turn a group name into a gid */
411 int name_to_gid(char *name, gid_t *gid)
412 {
413         struct group *grp;
414         if (!name || !*name) return 0;
415         grp = getgrnam(name);
416         if (grp) {
417                 *gid = grp->gr_gid;
418                 return 1;
419         }
420         return 0;
421 }
422
423
424 /* lock a byte range in a open file */
425 int lock_range(int fd, int offset, int len)
426 {
427         struct flock lock;
428
429         lock.l_type = F_WRLCK;
430         lock.l_whence = SEEK_SET;
431         lock.l_start = offset;
432         lock.l_len = len;
433         lock.l_pid = 0;
434         
435         return fcntl(fd,F_SETLK,&lock) == 0;
436 }
437
438
439 static void glob_expand_one(char *s, char **argv, int *argc, int maxargs)
440 {
441 #if !(defined(HAVE_GLOB) && defined(HAVE_GLOB_H))
442         if (!*s) s = ".";
443         argv[*argc] = strdup(s);
444         (*argc)++;
445         return;
446 #else
447         extern int sanitize_paths;
448         glob_t globbuf;
449         int i;
450
451         if (!*s) s = ".";
452
453         argv[*argc] = strdup(s);
454         if (sanitize_paths) {
455                 sanitize_path(argv[*argc], NULL);
456         }
457
458         memset(&globbuf, 0, sizeof(globbuf));
459         glob(argv[*argc], 0, NULL, &globbuf);
460         if (globbuf.gl_pathc == 0) {
461                 (*argc)++;
462                 globfree(&globbuf);
463                 return;
464         }
465         for (i=0; i<(maxargs - (*argc)) && i<globbuf.gl_pathc;i++) {
466                 if (i == 0) free(argv[*argc]);
467                 argv[(*argc) + i] = strdup(globbuf.gl_pathv[i]);
468                 if (!argv[(*argc) + i]) out_of_memory("glob_expand");
469         }
470         globfree(&globbuf);
471         (*argc) += i;
472 #endif
473 }
474
475 void glob_expand(char *base1, char **argv, int *argc, int maxargs)
476 {
477         char *s = argv[*argc];
478         char *p, *q;
479         char *base = base1;
480
481         if (!s || !*s) return;
482
483         if (strncmp(s, base, strlen(base)) == 0) {
484                 s += strlen(base);
485         }
486
487         s = strdup(s);
488         if (!s) out_of_memory("glob_expand");
489
490         base = (char *)malloc(strlen(base1)+3);
491         if (!base) out_of_memory("glob_expand");
492
493         sprintf(base," %s/", base1);
494
495         q = s;
496         while ((p = strstr(q,base)) && ((*argc) < maxargs)) {
497                 /* split it at this point */
498                 *p = 0;
499                 glob_expand_one(q, argv, argc, maxargs);
500                 q = p+strlen(base);
501         }
502
503         if (*q && (*argc < maxargs)) glob_expand_one(q, argv, argc, maxargs);
504
505         free(s);
506         free(base);
507 }
508
509 /*******************************************************************
510   convert a string to lower case
511 ********************************************************************/
512 void strlower(char *s)
513 {
514         while (*s) {
515                 if (isupper(*s)) *s = tolower(*s);
516                 s++;
517         }
518 }
519
520 /* this is like vsnprintf but it always null terminates, so you
521    can fit at most n-1 chars in */
522 int vslprintf(char *str, int n, const char *format, va_list ap)
523 {
524         int ret = vsnprintf(str, n, format, ap);
525         if (ret >= n || ret < 0) {
526                 str[n-1] = 0;
527                 return -1;
528         }
529         str[ret] = 0;
530         return ret;
531 }
532
533
534 /* like snprintf but always null terminates */
535 int slprintf(char *str, int n, char *format, ...)
536 {
537         va_list ap;  
538         int ret;
539
540         va_start(ap, format);
541         ret = vslprintf(str,n,format,ap);
542         va_end(ap);
543         return ret;
544 }
545
546
547 void *Realloc(void *p, int size)
548 {
549         if (!p) return (void *)malloc(size);
550         return (void *)realloc(p, size);
551 }
552
553
554 void clean_fname(char *name)
555 {
556         char *p;
557         int l;
558         int modified = 1;
559
560         if (!name) return;
561
562         while (modified) {
563                 modified = 0;
564
565                 if ((p=strstr(name,"/./"))) {
566                         modified = 1;
567                         while (*p) {
568                                 p[0] = p[2];
569                                 p++;
570                         }
571                 }
572
573                 if ((p=strstr(name,"//"))) {
574                         modified = 1;
575                         while (*p) {
576                                 p[0] = p[1];
577                                 p++;
578                         }
579                 }
580
581                 if (strncmp(p=name,"./",2) == 0) {      
582                         modified = 1;
583                         do {
584                                 p[0] = p[2];
585                         } while (*p++);
586                 }
587
588                 l = strlen(p=name);
589                 if (l > 1 && p[l-1] == '/') {
590                         modified = 1;
591                         p[l-1] = 0;
592                 }
593         }
594 }
595
596 /*
597  * Make path appear as if a chroot had occurred:
598  *    1. remove leading "/" (or replace with "." if at end)
599  *    2. remove leading ".." components (except those allowed by "reldir")
600  *    3. delete any other "<dir>/.." (recursively)
601  * Can only shrink paths, so sanitizes in place.
602  * While we're at it, remove double slashes and "." components like
603  *   clean_fname does(), but DON'T remove a trailing slash because that
604  *   is sometimes significant on command line arguments.
605  * If "reldir" is non-null, it is a sanitized directory that the path will be
606  *    relative to, so allow as many ".." at the beginning of the path as
607  *    there are components in reldir.  This is used for symbolic link targets.
608  *    If reldir is non-null and the path began with "/", to be completely like
609  *    a chroot we should add in depth levels of ".." at the beginning of the
610  *    path, but that would blow the assumption that the path doesn't grow and
611  *    it is not likely to end up being a valid symlink anyway, so just do
612  *    the normal removal of the leading "/" instead.
613  * Contributed by Dave Dykstra <dwd@bell-labs.com>
614  */
615
616 void sanitize_path(char *p, char *reldir)
617 {
618         char *start, *sanp;
619         int depth = 0;
620         int allowdotdot = 0;
621
622         if (reldir) {
623                 depth++;
624                 while (*reldir) {
625                         if (*reldir++ == '/') {
626                                 depth++;
627                         }
628                 }
629         }
630         start = p;
631         sanp = p;
632         while (*p == '/') {
633                 /* remove leading slashes */
634                 p++;
635         }
636         while (*p != '\0') {
637                 /* this loop iterates once per filename component in p.
638                  * both p (and sanp if the original had a slash) should
639                  * always be left pointing after a slash
640                  */
641                 if ((*p == '.') && ((*(p+1) == '/') || (*(p+1) == '\0'))) {
642                         /* skip "." component */
643                         while (*++p == '/') {
644                                 /* skip following slashes */
645                                 ;
646                         }
647                         continue;
648                 }
649                 allowdotdot = 0;
650                 if ((*p == '.') && (*(p+1) == '.') &&
651                             ((*(p+2) == '/') || (*(p+2) == '\0'))) {
652                         /* ".." component followed by slash or end */
653                         if ((depth > 0) && (sanp == start)) {
654                                 /* allow depth levels of .. at the beginning */
655                                 --depth;
656                                 allowdotdot = 1;
657                         } else {
658                                 p += 2;
659                                 if (*p == '/')
660                                         p++;
661                                 if (sanp != start) {
662                                         /* back up sanp one level */
663                                         --sanp; /* now pointing at slash */
664                                         while ((sanp > start) && (*(sanp - 1) != '/')) {
665                                                 /* skip back up to slash */
666                                                 sanp--;
667                                         }
668                                 }
669                                 continue;
670                         }
671                 }
672                 while (1) {
673                         /* copy one component through next slash */
674                         *sanp++ = *p++;
675                         if ((*p == '\0') || (*(p-1) == '/')) {
676                                 while (*p == '/') {
677                                         /* skip multiple slashes */
678                                         p++;
679                                 }
680                                 break;
681                         }
682                 }
683                 if (allowdotdot) {
684                         /* move the virtual beginning to leave the .. alone */
685                         start = sanp;
686                 }
687         }
688         if ((sanp == start) && !allowdotdot) {
689                 /* ended up with nothing, so put in "." component */
690                 /*
691                  * note that the !allowdotdot doesn't prevent this from
692                  *  happening in all allowed ".." situations, but I didn't
693                  *  think it was worth putting in an extra variable to ensure
694                  *  it since an extra "." won't hurt in those situations.
695                  */
696                 *sanp++ = '.';
697         }
698         *sanp = '\0';
699 }
700
701
702 static char curr_dir[MAXPATHLEN];
703
704 /* like chdir() but can be reversed with pop_dir() if save is set. It
705    is also much faster as it remembers where we have been */
706 char *push_dir(char *dir, int save)
707 {
708         char *ret = curr_dir;
709         static int initialised;
710
711         if (!initialised) {
712                 initialised = 1;
713                 getcwd(curr_dir, sizeof(curr_dir)-1);
714         }
715
716         if (!dir) return NULL; /* this call was probably just to initialize */
717
718         if (chdir(dir)) return NULL;
719
720         if (save) {
721                 ret = strdup(curr_dir);
722         }
723
724         if (*dir == '/') {
725                 strlcpy(curr_dir, dir, sizeof(curr_dir));
726         } else {
727                 strlcat(curr_dir,"/", sizeof(curr_dir));
728                 strlcat(curr_dir,dir, sizeof(curr_dir));
729         }
730
731         clean_fname(curr_dir);
732
733         return ret;
734 }
735
736 /* reverse a push_dir call */
737 int pop_dir(char *dir)
738 {
739         int ret;
740
741         ret = chdir(dir);
742         if (ret) {
743                 free(dir);
744                 return ret;
745         }
746
747         strlcpy(curr_dir, dir, sizeof(curr_dir));
748
749         free(dir);
750
751         return 0;
752 }
753
754 /* we need to supply our own strcmp function for file list comparisons
755    to ensure that signed/unsigned usage is consistent between machines. */
756 int u_strcmp(const char *cs1, const char *cs2)
757 {
758         const uchar *s1 = (const uchar *)cs1;
759         const uchar *s2 = (const uchar *)cs2;
760
761         while (*s1 && *s2 && (*s1 == *s2)) {
762                 s1++; s2++;
763         }
764         
765         return (int)*s1 - (int)*s2;
766 }
767
768 static OFF_T last_ofs;
769
770 void end_progress(OFF_T size)
771 {
772         extern int do_progress, am_server;
773
774         if (do_progress && !am_server) {
775                 rprintf(FINFO,"%.0f (100%%)\n", (double)size);
776         }
777         last_ofs = 0;
778 }
779
780 void show_progress(OFF_T ofs, OFF_T size)
781 {
782         extern int do_progress, am_server;
783
784         if (do_progress && !am_server) {
785                 if (ofs > last_ofs + 1000) {
786                         int pct = (int)((100.0*ofs)/size);
787                         rprintf(FINFO,"%.0f (%d%%)\r", (double)ofs, pct);
788                         last_ofs = ofs;
789                 }
790         }
791 }
792
793 /* determine if a symlink points outside the current directory tree */
794 int unsafe_symlink(char *dest, char *src)
795 {
796         char *tok;
797         int depth = 0;
798
799         /* all absolute and null symlinks are unsafe */
800         if (!dest || !(*dest) || (*dest == '/')) return 1;
801
802         src = strdup(src);
803         if (!src) out_of_memory("unsafe_symlink");
804
805         /* find out what our safety margin is */
806         for (tok=strtok(src,"/"); tok; tok=strtok(NULL,"/")) {
807                 if (strcmp(tok,"..") == 0) {
808                         depth=0;
809                 } else if (strcmp(tok,".") == 0) {
810                         /* nothing */
811                 } else {
812                         depth++;
813                 }
814         }
815         free(src);
816
817         /* drop by one to account for the filename portion */
818         depth--;
819
820         dest = strdup(dest);
821         if (!dest) out_of_memory("unsafe_symlink");
822
823         for (tok=strtok(dest,"/"); tok; tok=strtok(NULL,"/")) {
824                 if (strcmp(tok,"..") == 0) {
825                         depth--;
826                 } else if (strcmp(tok,".") == 0) {
827                         /* nothing */
828                 } else {
829                         depth++;
830                 }
831                 /* if at any point we go outside the current directory then
832                    stop - it is unsafe */
833                 if (depth < 0) break;
834         }
835
836         free(dest);
837         return (depth < 0);
838 }
839
840
841 /****************************************************************************
842   return the date and time as a string
843 ****************************************************************************/
844 char *timestring(time_t t)
845 {
846         static char TimeBuf[200];
847         struct tm *tm = localtime(&t);
848
849 #ifdef HAVE_STRFTIME
850         strftime(TimeBuf,sizeof(TimeBuf)-1,"%Y/%m/%d %T",tm);
851 #else
852         strlcpy(TimeBuf, asctime(tm), sizeof(TimeBuf));
853 #endif
854
855         if (TimeBuf[strlen(TimeBuf)-1] == '\n') {
856                 TimeBuf[strlen(TimeBuf)-1] = 0;
857         }
858
859         return(TimeBuf);
860 }
861
862
863 /****************************************************************************
864  like waitpid but does the WEXITSTATUS
865 ****************************************************************************/
866 #ifndef WEXITSTATUS
867 #define WEXITSTATUS(stat)       ((int)(((stat)>>8)&0xFF))
868 #endif
869 void wait_process(pid_t pid, int *status)
870 {
871         waitpid(pid, status, 0);
872         *status = WEXITSTATUS(*status);
873 }