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