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