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