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