1 /* @(#)util.c	1.42 21/07/22 2011-2020 J. Schilling */
2 #include <schily/mconfig.h>
3 #ifndef lint
4 static	UConst char sccsid[] =
5 	"@(#)util.c	1.42 21/07/22 2011-2020 J. Schilling";
6 #endif
7 /*
8  *	Copyright (c) 1986 Larry Wall
9  *	Copyright (c) 2011-2020 J. Schilling
10  *
11  * Redistribution and use in source and binary forms, with or without
12  * modification, are permitted provided that the following condition is met:
13  *
14  * 1. Redistributions of source code must retain the above copyright notice,
15  * this condition and the following disclaimer.
16  *
17  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND ANY
18  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
19  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
20  * DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
21  * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
23  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
24  * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27  * SUCH DAMAGE.
28  */
29 
30 #define	EXT	extern
31 #include "common.h"
32 #undef	EXT
33 #define	EXT
34 #include "util.h"
35 #include <schily/varargs.h>
36 #include <schily/errno.h>
37 #include <schily/wait.h>
38 #define	VMS_VFORK_OK
39 #include <schily/vfork.h>
40 
41 #ifndef	HAVE_STRERROR
42 #define	strerror	errmsgstr
43 #endif
44 
45 static	int	fetchtime	__PR((char *t, dtime_t *));
46 static	void	sig_exit	__PR((int signo));
47 static	int	wday		__PR((char *d));
48 static	int	month		__PR((char *m));
49 static	int	fetchtz		__PR((char *t));
50 static	int	fetchctime	__PR((char *t, dtime_t *));
51 
52 /* Rename a file, copying it if necessary. */
53 
54 int
move_file(from,to)55 move_file(from, to)
56 	char	*from;
57 	char	*to;
58 {
59 	char bakname[512];
60 	char *s;
61 	int i;
62 	int fromfd;
63 
64 	/* to stdout? */
65 
66 	if (from && strEQ(to, "-")) {
67 #ifdef DEBUGGING
68 		if (debug & 4)
69 			say(_("Moving %s to stdout.\n"), from);
70 #endif
71 		fromfd = open(from, O_RDONLY);
72 		if (fromfd < 0) {
73 			pfatal(_("internal error, can't reopen %s\n"),
74 				from);
75 		}
76 		/*
77 		 * For file copy operations, we use the minimal buf size that
78 		 * is better aligned.
79 		 */
80 		while ((i = read(fromfd, buf, BUFFERSIZE)) > 0)
81 			if (write(STDOUT_FILENO, buf, i) != i)
82 				pfatal(_("write failed\n"));
83 		Close(fromfd);
84 		return (0);
85 	}
86 
87 	if (origprae) {
88 		Strcpy(bakname, origprae);
89 		Strcat(bakname, to);
90 	} else {
91 		Strcpy(bakname, to);
92 		Strcat(bakname, origext?origext:ORIGEXT);
93 	}
94 	if (do_backup &&
95 	    stat(to, &file_stat) >= 0) {	/* output file exists */
96 		dev_t to_device = file_stat.st_dev;
97 		ino_t to_inode  = file_stat.st_ino;
98 		char *simplename = bakname;
99 
100 		for (s = bakname; *s; s++) {
101 			if (*s == '/')
102 				simplename = s+1;
103 		}
104 		/* find a backup name that is not the same file */
105 		file_stat.st_ctime = (time_t)0;
106 		while (stat(bakname, &file_stat) >= 0 &&
107 			to_device == file_stat.st_dev &&
108 			to_inode == file_stat.st_ino) {
109 			if (file_stat.st_ctime >= starttime) /* mult patches */
110 				goto backup_done;
111 			for (s = simplename; *s && !islower(UCH *s); s++) {
112 				;
113 				/* LINTED */
114 			}
115 			if (*s)
116 				*s = toupper(*s);
117 			else
118 				Strcpy(simplename, simplename+1);
119 		}
120 		if (file_stat.st_ctime >= starttime) /* mult patches */
121 			goto backup_done;
122 		while (unlink(bakname) >= 0) {	/* while() is for Eunice */
123 			;
124 			/* LINTED */
125 		}
126 #ifdef DEBUGGING
127 		if (debug & 4)
128 			say(_("Moving %s to %s.\n"), to, bakname);
129 #endif
130 		if (rename(to, bakname) < 0) {
131 			say(_("patch: can't backup %s, output is in %s\n"),
132 			    to, from);
133 			return (-1);
134 		}
135 	}
136 backup_done:
137 	if (from == NULL) {
138 		if (!do_backup) {
139 			if (debug & 4)
140 				say(_("Removing file %s\n"), to);
141 			if (unlink(to) != 0 && errno != ENOENT)
142 				pfatal(_("Can't remove file %s\n"), to);
143 		}
144 		return (0);
145 	}
146 #ifdef DEBUGGING
147 	if (debug & 4)
148 		say(_("Moving %s to %s.\n"), from, to);
149 #endif
150 	if (rename(from, to) < 0) {		/* different file system? */
151 		int tofd;
152 
153 		unlink(to);			/* to may be a readonly file */
154 		tofd = creat(to, 0666);
155 		if (tofd < 0) {
156 			say(_("patch: can't create %s, output is in %s.\n"),
157 			    to, from);
158 			return (-1);
159 		}
160 		fromfd = open(from, O_RDONLY);
161 		if (fromfd < 0) {
162 			pfatal(_("internal error, can't reopen %s\n"),
163 				from);
164 		}
165 		/*
166 		 * For file copy operations, we use the minimal buf size that
167 		 * is better aligned.
168 		 */
169 		while ((i = read(fromfd, buf, BUFFERSIZE)) > 0)
170 			if (write(tofd, buf, i) != i)
171 				pfatal(_("write failed\n"));
172 		Close(fromfd);
173 		Close(tofd);
174 	}
175 	Unlink(from);
176 	return (0);
177 }
178 
179 void
removedirs(path)180 removedirs(path)
181 	char	*path;
182 {
183 	char	*p = strrchr(path, '/');
184 
185 	if (p == NULL)
186 		return;
187 	while (p > path) {
188 		*p = '\0';
189 		if (rmdir(path) == 0 && verbose)
190 			say(_("Removed empty directory %s\n"),
191 				path);
192 		*p = '/';
193 		while (--p > path) {
194 			if (*p == '/')
195 				break;
196 		}
197 	}
198 }
199 
200 /* Copy a file. */
201 
202 void
copy_file(from,to)203 copy_file(from, to)
204 	char	*from;
205 	char	*to;
206 {
207 	int tofd;
208 	int fromfd;
209 	int i;
210 
211 	tofd = creat(to, 0666);
212 	if (tofd < 0)
213 		pfatal(_("can't create %s.\n"), to);
214 	fromfd = open(from, O_RDONLY);
215 	if (fromfd < 0)
216 		pfatal(_("internal error, can't reopen %s\n"), from);
217 	/*
218 	 * For file copy operations, we use the minimal buf size that
219 	 * is better aligned.
220 	 */
221 	while ((i = read(fromfd, buf, BUFFERSIZE)) > 0)
222 		if (write(tofd, buf, i) != i)
223 			pfatal(_("write (%s) failed\n"), to);
224 	Close(fromfd);
225 	Close(tofd);
226 }
227 
228 /* Allocate a unique area for a string. */
229 
230 char *
savestr(s)231 savestr(s)
232 	char *s;
233 {
234 	char *rv;
235 
236 	if (!s)
237 		s = "Oops";
238 	rv = strdup(s);
239 	if (rv == Nullch) {
240 		if (using_plan_a) {
241 			out_of_mem = TRUE;
242 		} else {
243 			pfatal(_("out of memory (savestr)\n"));
244 			/* NOTREACHED */
245 		}
246 	}
247 	return (rv);
248 }
249 
250 /* Vanilla terminal output (buffered). */
251 
252 /* VARARGS1 */
253 #ifdef	PROTOTYPES
254 EXPORT void
say(const char * fmt,...)255 say(const char *fmt, ...)
256 #else
257 EXPORT void
258 say(fmt, va_alist)
259 	char	*fmt;
260 	va_dcl
261 #endif
262 {
263 	va_list	args;
264 
265 #ifdef	PROTOTYPES
266 	va_start(args, fmt);
267 #else
268 	va_start(args);
269 #endif
270 #ifdef	USE_VPRINTF
271 	(void) vfprintf(stderr, fmt, args);
272 #else
273 	(void) js_fprintf(stderr, "%r", fmt, args);
274 #endif
275 	va_end(args);
276 	Fflush(stderr);
277 }
278 
279 /* Terminal output, pun intended. */
280 
281 /* VARARGS1 */
282 #ifdef	PROTOTYPES
283 EXPORT void
fatal(const char * fmt,...)284 fatal(const char *fmt, ...)
285 #else
286 EXPORT void
287 fatal(fmt, va_alist)
288 	char	*fmt;
289 	va_dcl
290 #endif
291 {
292 	va_list	args;
293 
294 #ifdef	PROTOTYPES
295 	va_start(args, fmt);
296 #else
297 	va_start(args);
298 #endif
299 #ifdef	USE_VPRINTF
300 	(void) vfprintf(stderr, fmt, args);
301 #else
302 	(void) js_fprintf(stderr, "%r", fmt, args);
303 #endif
304 	va_end(args);
305 	my_exit(EXIT_FAIL);
306 }
307 
308 
309 /* VARARGS1 */
310 #ifdef	PROTOTYPES
311 EXPORT void
pfatal(const char * fmt,...)312 pfatal(const char *fmt, ...)
313 #else
314 EXPORT void
315 pfatal(fmt, va_alist)
316 	char	*fmt;
317 	va_dcl
318 #endif
319 {
320 	va_list	args;
321 	int	errsav = errno;
322 	char	*err;
323 	char	errbuf[32];
324 
325 	err = strerror(errsav);
326 	if (err == NULL) {
327 		sprintf(errbuf, "Error %d", errsav);
328 		err = errbuf;
329 	}
330 #ifdef	PROTOTYPES
331 	va_start(args, fmt);
332 #else
333 	va_start(args);
334 #endif
335 #ifdef	USE_VPRINTF
336 	(void) fprintf(stderr, "patch: %s. ", err);
337 	(void) vfprintf(stderr, fmt, args);
338 #else
339 	(void) js_fprintf(stderr, "patch: %s. %r", err, fmt, args);
340 #endif
341 	va_end(args);
342 	my_exit(EXIT_FAIL);
343 }
344 
345 
346 /* Get a response from the user, somehow or other. */
347 
348 /* VARARGS1 */
349 #ifdef	PROTOTYPES
350 EXPORT void
ask(const char * fmt,...)351 ask(const char *fmt, ...)
352 #else
353 EXPORT void
354 ask(fmt, va_alist)
355 	char	*fmt;
356 	va_dcl
357 #endif
358 {
359 	va_list	args;
360 	int	ttyfd;
361 	int	r;
362 	bool	tty2 = isatty(STDERR_FILENO);
363 
364 #ifdef	PROTOTYPES
365 	va_start(args, fmt);
366 #else
367 	va_start(args);
368 #endif
369 #ifdef	USE_VPRINTF
370 	(void) vsprintf(buf, fmt, args);
371 #else
372 	(void) js_sprintf(buf, "%r", fmt, args);
373 #endif
374 	va_end(args);
375 	Fflush(stderr);
376 	write(STDERR_FILENO, buf, strlen(buf));
377 	if (tty2) {			/* might be redirected to a file */
378 		r = read(STDERR_FILENO, buf, bufsize);
379 	} else if (isatty(STDOUT_FILENO)) {
380 					/* this may be new file output */
381 		Fflush(stdout);
382 		write(STDOUT_FILENO, buf, strlen(buf));
383 		r = read(STDOUT_FILENO, buf, bufsize);
384 	} else if ((ttyfd = open("/dev/tty", O_RDWR)) >= 0 && isatty(ttyfd)) {
385 					/* might be deleted or unwriteable */
386 		write(ttyfd, buf, strlen(buf));
387 		r = read(ttyfd, buf, bufsize);
388 		Close(ttyfd);
389 	} else if (isatty(STDIN_FILENO)) {
390 					/* this is probably patch input */
391 		Fflush(stdin);
392 		write(STDIN_FILENO, buf, strlen(buf));
393 		r = read(STDIN_FILENO, buf, bufsize);
394 	} else {			/* no terminal at all--default it */
395 		buf[0] = '\n';
396 		r = 1;
397 	}
398 	if (r <= 0)
399 		buf[0] = 0;
400 	else
401 		buf[r] = '\0';
402 	if (!tty2)
403 		say("%s", buf);
404 }
405 
406 /* How to handle certain events when not in a critical region. */
407 
408 void
set_signals(reset)409 set_signals(reset)
410 	int	reset;
411 {
412 	static RETSIGTYPE (*hupval) __PR((int)), (*intval) __PR((int));
413 
414 	if (!reset) {
415 #ifdef	SIGHUP
416 		hupval = signal(SIGHUP, SIG_IGN);
417 		if (hupval != SIG_IGN)
418 			hupval = (RETSIGTYPE(*) __PR((int)))sig_exit;
419 #endif
420 		intval = signal(SIGINT, SIG_IGN);
421 		if (intval != SIG_IGN)
422 			intval = (RETSIGTYPE(*) __PR((int)))sig_exit;
423 	}
424 #ifdef	SIGHUP
425 	Signal(SIGHUP, hupval);
426 #endif
427 	Signal(SIGINT, intval);
428 }
429 
430 static void
sig_exit(signo)431 sig_exit(signo)
432 	int	signo;
433 {
434 	my_exit(EXIT_SIGNAL);
435 }
436 
437 /* How to handle certain events when in a critical region. */
438 
439 void
ignore_signals()440 ignore_signals()
441 {
442 #ifdef	SIGHUP
443 	Signal(SIGHUP, SIG_IGN);
444 #endif
445 	Signal(SIGINT, SIG_IGN);
446 }
447 
448 /* Make sure we'll have the directories to create a file. */
449 
450 #ifndef	_SCHILY_SCHILY_H
451 #ifdef	PROTOTYPES
452 int
makedirs(char * filename,mode_t mode,bool striplast)453 makedirs(char *filename, mode_t mode, bool striplast)
454 #else
455 int
456 makedirs(filename, mode, striplast)
457 	char *filename;
458 	mode_t mode;
459 	bool striplast;
460 #endif
461 {
462 	char tmpbuf[256];
463 	char *s = tmpbuf;
464 	char *dirv[20];
465 	int i;
466 	int dirvp = 0;
467 
468 	while (*filename) {
469 		if (*filename == '/') {
470 			filename++;
471 			dirv[dirvp++] = s;
472 			*s++ = '\0';
473 		} else {
474 			*s++ = *filename++;
475 		}
476 	}
477 	*s = '\0';
478 	dirv[dirvp] = s;
479 	if (striplast)
480 		dirvp--;
481 	if (dirvp < 0)
482 		return (0);
483 #if 1
484 	for (i = 0; i <= dirvp; i++) {
485 		mkdir(tmpbuf, mode);
486 #if 0
487 			S_IRUSR|S_IWUSR|S_IXUSR
488 			|S_IRGRP|S_IWGRP|S_IXGRP
489 			|S_IROTH|S_IWOTH|S_IXOTH);
490 #endif
491 		*dirv[i] = '/';
492 	}
493 #else
494 	strcpy(buf, "mkdir");
495 	s = buf;
496 	for (i = 0; i <= dirvp; i++) {
497 		while (*s) s++;
498 		*s++ = ' ';
499 		strcpy(s, tmpbuf);
500 		*dirv[i] = '/';
501 	}
502 	system(buf);
503 #endif
504 	return (0);
505 }
506 #endif	/* _SCHILY_SCHILY_H */
507 
508 /* Make filenames more reasonable. */
509 
510 char *
fetchname(at,strip_leading,assume_exists,dtp)511 fetchname(at, strip_leading, assume_exists, dtp)
512 	char *at;
513 	int strip_leading;
514 	int assume_exists;
515 	dtime_t	*dtp;
516 {
517 	char *s;
518 	char *sn;
519 	char *name;
520 	char *t;
521 	char tmpbuf[512];
522 	dtime_t	dt;
523 
524 	if (dtp == NULL)
525 		dtp = &dt;
526 	dtp->dt_sec = -1;		/* Mark as illegal time */
527 	dtp->dt_nsec = 0;		/* Default to no nsecs  */
528 	dtp->dt_zone = DT_NO_ZONE;	/* No timezone seen yet */
529 
530 	if (!at)
531 		return (Nullch);
532 	s = savestr(at);
533 	for (t = s; isspace(UCH *t); t++) {
534 		;
535 		/* LINTED */
536 	}
537 	sn = name = t;
538 #ifdef DEBUGGING
539 	if (debug & 128)
540 		say(_("fetchname %s %d %d\n"),
541 			name, strip_leading, assume_exists);
542 #endif
543 	for (; *t && !isspace(UCH *t); t++)
544 		if (*t == '/')
545 			if (--strip_leading >= 0)
546 				name = t+1;
547 	*t = '\0';
548 	if (strEQ(sn, "/dev/null")) {	/* files can be created by diffing */
549 		dtp->dt_sec = 0;	/* against /dev/null. */
550 		free(s);
551 		return (Nullch);
552 	}
553 	fetchtime(t, dtp);
554 
555 	/*
556 	 * If we could not strip the full number of directories and
557 	 * if we stripped off existing leading directories from a relative path
558 	 * use the unstipped full name.
559 	 */
560 	if (strip_leading > 0 && name != s && *s != '/') {
561 		name[-1] = '\0';
562 		if (stat(s, &file_stat) == 0 && S_ISDIR(file_stat.st_mode)) {
563 			name[-1] = '/';
564 			name = s;
565 		}
566 	}
567 	name = savestr(name);
568 	Snprintf(tmpbuf, sizeof (tmpbuf), "RCS/%s", name);
569 	free(s);
570 	if (stat(name, &file_stat) < 0 && !assume_exists) {
571 		Strcat(tmpbuf, RCSSUFFIX);
572 		if (stat(tmpbuf, &file_stat) < 0 &&
573 		    stat(tmpbuf+4, &file_stat) < 0) {
574 			Snprintf(tmpbuf, sizeof (tmpbuf), "SCCS/%s%s",
575 			    SCCSPREFIX, name);
576 			if (stat(tmpbuf, &file_stat) < 0 &&
577 			    stat(tmpbuf+5, &file_stat) < 0) {
578 				free(name);
579 				name = Nullch;
580 			}
581 		}
582 	}
583 	return (name);
584 }
585 
586 static int
wday(d)587 wday(d)
588 	char	*d;
589 {
590 	char	*days = "SunMonTueWedThuFriSat";
591 	char	day[4];
592 	char	*p;
593 
594 	strlcpy(day, d, 4);
595 	p = strstr(days, day);
596 	if (p == NULL)
597 		return (-1);
598 	return ((p - days) / 3);
599 }
600 
601 static int
month(m)602 month(m)
603 	char	*m;
604 {
605 	char	*months = "JanFebMarAprMayJunJulAugSepOctNovDec";
606 	char	mon[4];
607 	char	*p;
608 
609 	strlcpy(mon, m, 4);
610 	p = strstr(months, mon);
611 	if (p == NULL)
612 		return (-1);
613 	return ((p - months) / 3);
614 }
615 
616 static int
fetchtz(t)617 fetchtz(t)
618 	char	*t;
619 {
620 	int	n;
621 	int	h;
622 	int	m;
623 
624 	/*
625 	 * Skip until until the timezone offset field
626 	 */
627 	for (; *t != '\0' && isspace(UCH *t); t++)
628 		;
629 	/*
630 	 * Scan timezone hours and minutes
631 	 */
632 	n = sscanf(t, "%3d%2d", &h, &m);
633 	if (n != 2)
634 		return (-1);
635 	if (h < -24 || h > 25)
636 		return (-1);
637 	if (m < 0 || m > 59)
638 		return (-1);
639 	m += 60 * h;
640 	m *= 60;
641 	return (m);
642 }
643 
644 /*
645  * Fetch a time that is in ctime() syntax.
646  * This is a format like: LC_ALL=C date "+%a %b %e %T %Y"
647  */
648 static int
fetchctime(t,dtp)649 fetchctime(t, dtp)
650 	char	*t;
651 	dtime_t	*dtp;
652 {
653 	char	y[10];
654 	Llong	d = -1;
655 	struct tm tm;
656 	int	n;
657 	char	*ep = t + strlen(t);
658 
659 	for (; *t != '\0' && isspace(UCH *t); t++)
660 		;
661 	n = wday(t);
662 	if (n < 0)
663 		return (-1);
664 	tm.tm_wday = n;
665 	t += 3;
666 	if (t >= ep)
667 		return (-1);
668 	for (; *t != '\0' && isspace(UCH *t); t++)
669 		;
670 	n = month(t);
671 	if (n < 0)
672 		return (-1);
673 	tm.tm_mon = n;
674 	t += 3;
675 	if (t >= ep)
676 		return (-1);
677 	for (; *t != '\0' && isspace(UCH *t); t++)
678 		;
679 	if (!isdigit(UCH *t))
680 		return (-1);
681 	n = atoi(t);
682 	if (n < 1 || n > 31)
683 		return (-1);
684 	tm.tm_mday = n;
685 	for (; *t != '\0' && isdigit(UCH *t); t++)
686 		;
687 	for (; *t != '\0' && isspace(UCH *t); t++)
688 		;
689 	n = sscanf(t, "%2d:%2d:%2d %4d",
690 			&tm.tm_hour, &tm.tm_min, &tm.tm_sec,
691 			&tm.tm_year);
692 	if (n != 4)
693 		return (-1);
694 
695 	/*
696 	 * Now check whether there is a timezone field.
697 	 * This may be present in case the diff has been created by
698 	 * a "hg export" call.
699 	 */
700 	sprintf(y, " %d", tm.tm_year);
701 	ep = strstr(t, y);
702 	if (ep) {
703 		ep += strlen(y);
704 		n = fetchtz(ep);
705 	} else {
706 		n = -1;
707 	}
708 	tm.tm_year -= 1900;
709 
710 	tm.tm_isdst = -1;
711 	if (n != -1) {		/* Timezone string seen */
712 		d = mklgmtime(&tm);
713 		d -= n;		/* Add timezone offset */
714 		dtp->dt_zone = n;
715 	} else if (touch_gmt) {
716 		d = mklgmtime(&tm);
717 	} else {
718 		d = mktime(&tm);
719 	}
720 	dtp->dt_sec = d;
721 	return (0);
722 }
723 
724 
725 static int mul[10] = {	0, 100000000, 10000000,
726 			1000000, 100000, 10000,
727 			1000, 100, 10,
728 			1
729 };
730 
731 static int
fetchtime(t,dtp)732 fetchtime(t, dtp)
733 	char	*t;
734 	dtime_t	*dtp;
735 {
736 	Llong	d = -1;
737 	struct tm tm;
738 	int	n;
739 	int	m;
740 
741 	for (++t; *t != '\0' && isspace(UCH *t); t++)
742 		;
743 	/*
744 	 * Check format from: LC_ALL=C date "+%a %b %e %T %Y"
745 	 */
746 	if (wday(t) >= 0) {
747 		return (fetchctime(t, dtp));
748 	}
749 	/*
750 	 * Parse format from: date '+%Y-%m-%d %H:%M:%S'
751 	 */
752 	n = sscanf(t, "%4d-%2d-%2d %2d:%2d:%2d",
753 			&tm.tm_year, &tm.tm_mon, &tm.tm_mday,
754 			&tm.tm_hour, &tm.tm_min, &tm.tm_sec);
755 	tm.tm_year -= 1900;
756 	tm.tm_mon -= 1;
757 
758 	if (n != 6)
759 		return (-1);
760 
761 	if (strlen(t) <= 19)	/* Assume time >= y1000 */
762 		return (-1);
763 
764 	t += 19;		/* Skip minimum length */
765 
766 	if (*t == '.') {
767 		Llong	frac = 0;
768 		int	dig = -1;
769 
770 		/*
771 		 * Convert fractions of a second.
772 		 */
773 		for (++t; ++dig < 9 && *t != '\0' && isdigit(UCH *t); t++) {
774 			frac *= 10;
775 			frac += *t - '0';
776 		}
777 		frac *= mul[dig];
778 		if (frac < 0 || frac > 999999999)
779 			frac = 0;
780 		dtp->dt_nsec = frac;
781 	}
782 	/*
783 	 * Skip rest of fractions of a second
784 	 */
785 	for (; *t != '\0' && !isspace(UCH *t); t++)
786 		;
787 
788 	m = fetchtz(t);
789 	if (m == -1)		/* The diff -u time format requires a TZ */
790 		return (m);	/* Without, the whole format is invalid	 */
791 	d = mklgmtime(&tm);
792 	d -= m;			/* Add timezone offset */
793 	dtp->dt_sec = d;
794 	dtp->dt_zone = m;
795 	return (0);
796 }
797 
798 int
settime(name,idx,failed)799 settime(name, idx, failed)
800 	char	*name;
801 	int	idx;
802 	int	failed;
803 {
804 	int		ret;
805 	time_t		t;
806 	struct timespec	tp[2];
807 
808 	/*
809 	 * Silently return if we have no "new" time.
810 	 */
811 	if (file_times[idx].dt_sec == -1)
812 		return (0);
813 
814 	/*
815 	 * If there is no old time stamp in the patch file, we do not verify
816 	 * the old time stamp.
817 	 */
818 	t = file_times[!idx].dt_sec;
819 	if (!force && t != -1 && filetime.tv_sec != t) {
820 		say("Not setting time of file %s (time mismatch)\n", name);
821 		return (-1);
822 	}
823 
824 	/*
825 	 * We always verify the content.
826 	 */
827 	if (!force && failed) {
828 		say("Not setting time of file %s (contents mismatch)\n", name);
829 		return (-1);
830 	}
831 
832 	tp[0].tv_sec = file_times[idx].dt_sec;
833 	tp[0].tv_nsec = file_times[idx].dt_nsec;
834 	tp[1].tv_sec = file_times[idx].dt_sec;
835 	tp[1].tv_nsec = file_times[idx].dt_nsec;
836 
837 	ret = utimensat(AT_FDCWD, name, tp, AT_SYMLINK_NOFOLLOW);
838 	if (ret)
839 		pfatal("Can't set timestamp on file %s", name);
840 	return (ret);
841 }
842 
843 typedef	RETSIGTYPE	(*sigtype) __PR((int));
844 
845 int
pspawn(av)846 pspawn(av)
847 	char	*av[];
848 {
849 	pid_t	pid;
850 	pid_t	p;
851 	int	status;
852 	sigtype	sint;
853 	sigtype	squit;
854 #ifdef	SIGCHLD
855 	sigtype	schld;
856 #endif
857 
858 	if ((pid = vfork()) == 0) {
859 		execvp(av[0], av);
860 		_exit(127);
861 	}
862 
863 	sint = signal(SIGINT, SIG_IGN);
864 	squit = signal(SIGQUIT, SIG_IGN);
865 #ifdef	SIGCHLD
866 	schld = signal(SIGCHLD, SIG_DFL);
867 #endif
868 	while ((p = wait(&status)) != pid && p != (pid_t)-1)
869 		;
870 	(void) signal(SIGINT, sint);
871 	(void) signal(SIGQUIT, squit);
872 #ifdef	SIGCHLD
873 	(void) signal(SIGCHLD, schld);
874 #endif
875 
876 	if (p == (pid_t)-1)
877 		return (-1);
878 	return (status);
879 }
880