Split code out into separate files and remove some global variables to
[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  * 
305   Robust unlink: some OS'es (HPUX) refuse to unlink busy files, so
306   rename to <path>/.rsyncNNN instead.
307   
308   Note that successive rsync runs will shuffle the filenames around a
309   bit as long as the file is still busy; this is because this function
310   does not know if the unlink call is due to a new file coming in, or
311   --delete trying to remove old .rsyncNNN files, hence it renames it
312   each time.
313 */
314 int robust_unlink(char *fname)
315 {
316 #ifndef ETXTBSY
317         return do_unlink(fname);
318 #else
319         static int counter = 1;
320         int rc, pos, start;
321         char path[MAXPATHLEN];
322
323         rc = do_unlink(fname);
324         if ((rc == 0) || (errno != ETXTBSY))
325                 return rc;
326
327         strlcpy(path, fname, MAXPATHLEN);
328
329         pos = strlen(path);
330         while((path[--pos] != '/') && (pos >= 0))
331                 ;
332         ++pos;
333         strlcpy(&path[pos], ".rsync", MAXPATHLEN-pos);
334         pos += sizeof(".rsync")-1;
335
336         if (pos > (MAXPATHLEN-MAX_RENAMES_DIGITS-1)) {
337                 errno = ETXTBSY;
338                 return -1;
339         }
340
341         /* start where the last one left off to reduce chance of clashes */
342         start = counter;
343         do {
344                 sprintf(&path[pos], "%03d", counter);
345                 if (++counter >= MAX_RENAMES)
346                         counter = 1;
347         } while (((rc = access(path, 0)) == 0) && (counter != start));
348
349         if (verbose > 0)
350                 rprintf(FINFO,"renaming %s to %s because of text busy\n",
351                                             fname, path);
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
463 static void glob_expand_one(char *s, char **argv, int *argc, int maxargs)
464 {
465 #if !(defined(HAVE_GLOB) && defined(HAVE_GLOB_H))
466         if (!*s) s = ".";
467         argv[*argc] = strdup(s);
468         (*argc)++;
469         return;
470 #else
471         extern int sanitize_paths;
472         glob_t globbuf;
473         int i;
474
475         if (!*s) s = ".";
476
477         argv[*argc] = strdup(s);
478         if (sanitize_paths) {
479                 sanitize_path(argv[*argc], NULL);
480         }
481
482         memset(&globbuf, 0, sizeof(globbuf));
483         glob(argv[*argc], 0, NULL, &globbuf);
484         if (globbuf.gl_pathc == 0) {
485                 (*argc)++;
486                 globfree(&globbuf);
487                 return;
488         }
489         for (i=0; i<(maxargs - (*argc)) && i < (int) globbuf.gl_pathc;i++) {
490                 if (i == 0) free(argv[*argc]);
491                 argv[(*argc) + i] = strdup(globbuf.gl_pathv[i]);
492                 if (!argv[(*argc) + i]) out_of_memory("glob_expand");
493         }
494         globfree(&globbuf);
495         (*argc) += i;
496 #endif
497 }
498
499 void glob_expand(char *base1, char **argv, int *argc, int maxargs)
500 {
501         char *s = argv[*argc];
502         char *p, *q;
503         char *base = base1;
504
505         if (!s || !*s) return;
506
507         if (strncmp(s, base, strlen(base)) == 0) {
508                 s += strlen(base);
509         }
510
511         s = strdup(s);
512         if (!s) out_of_memory("glob_expand");
513
514         if (asprintf(&base," %s/", base1) <= 0) out_of_memory("glob_expand");
515
516         q = s;
517         while ((p = strstr(q,base)) && ((*argc) < maxargs)) {
518                 /* split it at this point */
519                 *p = 0;
520                 glob_expand_one(q, argv, argc, maxargs);
521                 q = p+strlen(base);
522         }
523
524         if (*q && (*argc < maxargs)) glob_expand_one(q, argv, argc, maxargs);
525
526         free(s);
527         free(base);
528 }
529
530 /**
531  * Convert a string to lower case
532  **/
533 void strlower(char *s)
534 {
535         while (*s) {
536                 if (isupper(*s)) *s = tolower(*s);
537                 s++;
538         }
539 }
540
541 void *Realloc(void *p, int size)
542 {
543         if (!p) return (void *)malloc(size);
544         return (void *)realloc(p, size);
545 }
546
547
548 void clean_fname(char *name)
549 {
550         char *p;
551         int l;
552         int modified = 1;
553
554         if (!name) return;
555
556         while (modified) {
557                 modified = 0;
558
559                 if ((p=strstr(name,"/./"))) {
560                         modified = 1;
561                         while (*p) {
562                                 p[0] = p[2];
563                                 p++;
564                         }
565                 }
566
567                 if ((p=strstr(name,"//"))) {
568                         modified = 1;
569                         while (*p) {
570                                 p[0] = p[1];
571                                 p++;
572                         }
573                 }
574
575                 if (strncmp(p=name,"./",2) == 0) {      
576                         modified = 1;
577                         do {
578                                 p[0] = p[2];
579                         } while (*p++);
580                 }
581
582                 l = strlen(p=name);
583                 if (l > 1 && p[l-1] == '/') {
584                         modified = 1;
585                         p[l-1] = 0;
586                 }
587         }
588 }
589
590 /**
591  * Make path appear as if a chroot had occurred:
592  *
593  *    1. remove leading "/" (or replace with "." if at end)
594  *    2. remove leading ".." components (except those allowed by "reldir")
595  *    3. delete any other "<dir>/.." (recursively)
596  *
597  * Can only shrink paths, so sanitizes in place.
598  *
599  * While we're at it, remove double slashes and "." components like
600  *   clean_fname does(), but DON'T remove a trailing slash because that
601  *   is sometimes significant on command line arguments.
602  *
603  * If "reldir" is non-null, it is a sanitized directory that the path will be
604  *    relative to, so allow as many ".." at the beginning of the path as
605  *    there are components in reldir.  This is used for symbolic link targets.
606  *    If reldir is non-null and the path began with "/", to be completely like
607  *    a chroot we should add in depth levels of ".." at the beginning of the
608  *    path, but that would blow the assumption that the path doesn't grow and
609  *    it is not likely to end up being a valid symlink anyway, so just do
610  *    the normal removal of the leading "/" instead.
611  *
612  * Contributed by Dave Dykstra <dwd@bell-labs.com>
613  */
614 void sanitize_path(char *p, char *reldir)
615 {
616         char *start, *sanp;
617         int depth = 0;
618         int allowdotdot = 0;
619
620         if (reldir) {
621                 depth++;
622                 while (*reldir) {
623                         if (*reldir++ == '/') {
624                                 depth++;
625                         }
626                 }
627         }
628         start = p;
629         sanp = p;
630         while (*p == '/') {
631                 /* remove leading slashes */
632                 p++;
633         }
634         while (*p != '\0') {
635                 /* this loop iterates once per filename component in p.
636                  * both p (and sanp if the original had a slash) should
637                  * always be left pointing after a slash
638                  */
639                 if ((*p == '.') && ((*(p+1) == '/') || (*(p+1) == '\0'))) {
640                         /* skip "." component */
641                         while (*++p == '/') {
642                                 /* skip following slashes */
643                                 ;
644                         }
645                         continue;
646                 }
647                 allowdotdot = 0;
648                 if ((*p == '.') && (*(p+1) == '.') &&
649                             ((*(p+2) == '/') || (*(p+2) == '\0'))) {
650                         /* ".." component followed by slash or end */
651                         if ((depth > 0) && (sanp == start)) {
652                                 /* allow depth levels of .. at the beginning */
653                                 --depth;
654                                 allowdotdot = 1;
655                         } else {
656                                 p += 2;
657                                 if (*p == '/')
658                                         p++;
659                                 if (sanp != start) {
660                                         /* back up sanp one level */
661                                         --sanp; /* now pointing at slash */
662                                         while ((sanp > start) && (*(sanp - 1) != '/')) {
663                                                 /* skip back up to slash */
664                                                 sanp--;
665                                         }
666                                 }
667                                 continue;
668                         }
669                 }
670                 while (1) {
671                         /* copy one component through next slash */
672                         *sanp++ = *p++;
673                         if ((*p == '\0') || (*(p-1) == '/')) {
674                                 while (*p == '/') {
675                                         /* skip multiple slashes */
676                                         p++;
677                                 }
678                                 break;
679                         }
680                 }
681                 if (allowdotdot) {
682                         /* move the virtual beginning to leave the .. alone */
683                         start = sanp;
684                 }
685         }
686         if ((sanp == start) && !allowdotdot) {
687                 /* ended up with nothing, so put in "." component */
688                 /*
689                  * note that the !allowdotdot doesn't prevent this from
690                  *  happening in all allowed ".." situations, but I didn't
691                  *  think it was worth putting in an extra variable to ensure
692                  *  it since an extra "." won't hurt in those situations.
693                  */
694                 *sanp++ = '.';
695         }
696         *sanp = '\0';
697 }
698
699
700 static char curr_dir[MAXPATHLEN];
701
702 /** like chdir() but can be reversed with pop_dir() if save is set. It
703    is also much faster as it remembers where we have been */
704 char *push_dir(char *dir, int save)
705 {
706         char *ret = curr_dir;
707         static int initialised;
708
709         if (!initialised) {
710                 initialised = 1;
711                 getcwd(curr_dir, sizeof(curr_dir)-1);
712         }
713
714         if (!dir) return NULL; /* this call was probably just to initialize */
715
716         if (chdir(dir)) return NULL;
717
718         if (save) {
719                 ret = strdup(curr_dir);
720         }
721
722         if (*dir == '/') {
723                 strlcpy(curr_dir, dir, sizeof(curr_dir));
724         } else {
725                 strlcat(curr_dir,"/", sizeof(curr_dir));
726                 strlcat(curr_dir,dir, sizeof(curr_dir));
727         }
728
729         clean_fname(curr_dir);
730
731         return ret;
732 }
733
734 /** Reverse a push_dir call */
735 int pop_dir(char *dir)
736 {
737         int ret;
738
739         ret = chdir(dir);
740         if (ret) {
741                 free(dir);
742                 return ret;
743         }
744
745         strlcpy(curr_dir, dir, sizeof(curr_dir));
746
747         free(dir);
748
749         return 0;
750 }
751
752 /** We need to supply our own strcmp function for file list comparisons
753    to ensure that signed/unsigned usage is consistent between machines. */
754 int u_strcmp(const char *cs1, const char *cs2)
755 {
756         const uchar *s1 = (const uchar *)cs1;
757         const uchar *s2 = (const uchar *)cs2;
758
759         while (*s1 && *s2 && (*s1 == *s2)) {
760                 s1++; s2++;
761         }
762         
763         return (int)*s1 - (int)*s2;
764 }
765
766
767
768 /**
769  * Determine if a symlink points outside the current directory tree.
770  * This is considered "unsafe" because e.g. when mirroring somebody
771  * else's machine it might allow them to establish a symlink to
772  * /etc/passwd, and then read it through a web server.
773  *
774  * @param dest Target of the symlink in question.
775  *
776  * @param src Top source directory currently applicable.  Basically this
777  * is the first parameter to rsync in a simple invocation, but it's
778  * modified by flist.c in slightly complex ways.
779  *
780  * @retval True if unsafe
781  * @retval False is unsafe
782  **/
783 int unsafe_symlink(char *dest, char *src)
784 {
785         char *tok;
786         int depth = 0;
787
788         /* all absolute and null symlinks are unsafe */
789         if (!dest || !(*dest) || (*dest == '/')) return 1;
790
791         src = strdup(src);
792         if (!src) out_of_memory("unsafe_symlink");
793
794         /* find out what our safety margin is */
795         for (tok=strtok(src,"/"); tok; tok=strtok(NULL,"/")) {
796                 if (strcmp(tok,"..") == 0) {
797                         depth=0;
798                 } else if (strcmp(tok,".") == 0) {
799                         /* nothing */
800                 } else {
801                         depth++;
802                 }
803         }
804         free(src);
805
806         /* drop by one to account for the filename portion */
807         depth--;
808
809         dest = strdup(dest);
810         if (!dest) out_of_memory("unsafe_symlink");
811
812         for (tok=strtok(dest,"/"); tok; tok=strtok(NULL,"/")) {
813                 if (strcmp(tok,"..") == 0) {
814                         depth--;
815                 } else if (strcmp(tok,".") == 0) {
816                         /* nothing */
817                 } else {
818                         depth++;
819                 }
820                 /* if at any point we go outside the current directory then
821                    stop - it is unsafe */
822                 if (depth < 0) break;
823         }
824
825         free(dest);
826         return (depth < 0);
827 }
828
829
830 /**
831    Return the date and time as a string
832 */
833 char *timestring(time_t t)
834 {
835         static char TimeBuf[200];
836         struct tm *tm = localtime(&t);
837
838 #ifdef HAVE_STRFTIME
839         strftime(TimeBuf,sizeof(TimeBuf)-1,"%Y/%m/%d %T",tm);
840 #else
841         strlcpy(TimeBuf, asctime(tm), sizeof(TimeBuf));
842 #endif
843
844         if (TimeBuf[strlen(TimeBuf)-1] == '\n') {
845                 TimeBuf[strlen(TimeBuf)-1] = 0;
846         }
847
848         return(TimeBuf);
849 }
850
851
852 /**
853  * Sleep for a specified number of milliseconds.
854  *
855  * Always returns TRUE.  (In the future it might return FALSE if
856  * interrupted.)
857  **/
858 int msleep(int t)
859 {
860         int tdiff=0;
861         struct timeval tval,t1,t2;  
862
863         gettimeofday(&t1, NULL);
864         gettimeofday(&t2, NULL);
865   
866         while (tdiff < t) {
867                 tval.tv_sec = (t-tdiff)/1000;
868                 tval.tv_usec = 1000*((t-tdiff)%1000);
869  
870                 errno = 0;
871                 select(0,NULL,NULL, NULL, &tval);
872
873                 gettimeofday(&t2, NULL);
874                 tdiff = (t2.tv_sec - t1.tv_sec)*1000 + 
875                         (t2.tv_usec - t1.tv_usec)/1000;
876         }
877
878         return True;
879 }
880
881
882 /**
883  * Determine if two file modification times are equivalent (either
884  * exact or in the modification timestamp window established by
885  * --modify-window).
886  *
887  * @retval 0 if the times should be treated as the same
888  *
889  * @retval +1 if the first is later
890  *
891  * @retval -1 if the 2nd is later
892  **/
893 int cmp_modtime(time_t file1, time_t file2)
894 {
895         extern int modify_window;
896
897         if (file2 > file1) {
898                 if (file2 - file1 <= modify_window) return 0;
899                 return -1;
900         }
901         if (file1 - file2 <= modify_window) return 0;
902         return 1;
903 }
904
905
906 #ifdef __INSURE__XX
907 #include <dlfcn.h>
908
909 /**
910    This routine is a trick to immediately catch errors when debugging
911    with insure. A xterm with a gdb is popped up when insure catches
912    a error. It is Linux specific.
913 **/
914 int _Insure_trap_error(int a1, int a2, int a3, int a4, int a5, int a6)
915 {
916         static int (*fn)();
917         int ret;
918         char *cmd;
919
920         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'", 
921                 getpid(), getpid(), getpid());
922
923         if (!fn) {
924                 static void *h;
925                 h = dlopen("/usr/local/parasoft/insure++lite/lib.linux2/libinsure.so", RTLD_LAZY);
926                 fn = dlsym(h, "_Insure_trap_error");
927         }
928
929         ret = fn(a1, a2, a3, a4, a5, a6);
930
931         system(cmd);
932
933         free(cmd);
934
935         return ret;
936 }
937 #endif