1/*	$NetBSD: util.c,v 1.78 2021/12/15 12:58:01 rillig Exp $	*/
2
3/*
4 * Missing stuff from OS's
5 *
6 *	$Id: util.c,v 1.52 2024/01/04 00:27:30 sjg Exp $
7 */
8
9#include <sys/param.h>
10#include <errno.h>
11#include <time.h>
12#include <signal.h>
13
14#include "make.h"
15
16MAKE_RCSID("$NetBSD: util.c,v 1.78 2021/12/15 12:58:01 rillig Exp $");
17
18#if !defined(MAKE_NATIVE) && !defined(HAVE_STRERROR)
19extern int errno, sys_nerr;
20extern char *sys_errlist[];
21
22char *
23strerror(int e)
24{
25	static char buf[100];
26	if (e < 0 || e >= sys_nerr) {
27		snprintf(buf, sizeof buf, "Unknown error %d", e);
28		return buf;
29	} else
30		return sys_errlist[e];
31}
32#endif
33
34#if !defined(HAVE_GETENV) || !defined(HAVE_SETENV) || !defined(HAVE_UNSETENV)
35extern char **environ;
36
37static char *
38findenv(const char *name, int *offset)
39{
40	size_t i, len;
41	char *p, *q;
42
43	len = strlen(name);
44	for (i = 0; (q = environ[i]); i++) {
45		p = strchr(q, '=');
46		if (p == NULL || p - q != len)
47			continue;
48		if (strncmp(name, q, len) == 0) {
49			*offset = i;
50			return q + len + 1;
51		}
52	}
53	*offset = i;
54	return NULL;
55}
56
57char *
58getenv(const char *name)
59{
60	int offset;
61
62	return findenv(name, &offset);
63}
64
65int
66unsetenv(const char *name)
67{
68	char **p;
69	int offset;
70
71	if (name == NULL || *name == '\0' || strchr(name, '=') != NULL) {
72		errno = EINVAL;
73		return -1;
74	}
75
76	while (findenv(name, &offset)) {	/* if set multiple times */
77		for (p = &environ[offset];; p++)
78			if (!(*p = *(p + 1)))
79				break;
80	}
81	return 0;
82}
83
84int
85setenv(const char *name, const char *value, int rewrite)
86{
87	char *c, **newenv;
88	const char *cc;
89	size_t l_value, size;
90	int offset;
91
92	if (name == NULL || value == NULL) {
93		errno = EINVAL;
94		return -1;
95	}
96
97	if (*value == '=')	/* no `=' in value */
98		value++;
99	l_value = strlen(value);
100
101	/* find if already exists */
102	if ((c = findenv(name, &offset))) {
103		if (!rewrite)
104			return 0;
105		if (strlen(c) >= l_value)	/* old larger; copy over */
106			goto copy;
107	} else {					/* create new slot */
108		size = sizeof(char *) * (offset + 2);
109		if (savedEnv == environ) {		/* just increase size */
110			if ((newenv = realloc(savedEnv, size)) == NULL)
111				return -1;
112			savedEnv = newenv;
113		} else {				/* get new space */
114			/*
115			 * We don't free here because we don't know if
116			 * the first allocation is valid on all OS's
117			 */
118			if ((savedEnv = malloc(size)) == NULL)
119				return -1;
120			(void)memcpy(savedEnv, environ, size - sizeof(char *));
121		}
122		environ = savedEnv;
123		environ[offset + 1] = NULL;
124	}
125	for (cc = name; *cc && *cc != '='; cc++)	/* no `=' in name */
126		continue;
127	size = cc - name;
128	/* name + `=' + value */
129	if ((environ[offset] = malloc(size + l_value + 2)) == NULL)
130		return -1;
131	c = environ[offset];
132	(void)memcpy(c, name, size);
133	c += size;
134	*c++ = '=';
135copy:
136	(void)memcpy(c, value, l_value + 1);
137	return 0;
138}
139
140#ifdef TEST
141int
142main(int argc, char *argv[])
143{
144	setenv(argv[1], argv[2], 0);
145	printf("%s\n", getenv(argv[1]));
146	unsetenv(argv[1]);
147	printf("%s\n", getenv(argv[1]));
148	return 0;
149}
150#endif
151
152#endif
153
154
155#if defined(__hpux__) || defined(__hpux)
156/*
157 * strrcpy():
158 *	Like strcpy, going backwards and returning the new pointer
159 */
160static char *
161strrcpy(char *ptr, char *str)
162{
163	int len = strlen(str);
164
165	while (len != 0)
166		*--ptr = str[--len];
167
168	return ptr;
169}
170
171char *sys_siglist[] = {
172	"Signal 0",
173	"Hangup",			/* SIGHUP    */
174	"Interrupt",			/* SIGINT    */
175	"Quit",				/* SIGQUIT   */
176	"Illegal instruction",		/* SIGILL    */
177	"Trace/BPT trap",		/* SIGTRAP   */
178	"IOT trap",			/* SIGIOT    */
179	"EMT trap",			/* SIGEMT    */
180	"Floating point exception",	/* SIGFPE    */
181	"Killed",			/* SIGKILL   */
182	"Bus error",			/* SIGBUS    */
183	"Segmentation fault",		/* SIGSEGV   */
184	"Bad system call",		/* SIGSYS    */
185	"Broken pipe",			/* SIGPIPE   */
186	"Alarm clock",			/* SIGALRM   */
187	"Terminated",			/* SIGTERM   */
188	"User defined signal 1",	/* SIGUSR1   */
189	"User defined signal 2",	/* SIGUSR2   */
190	"Child exited",			/* SIGCLD    */
191	"Power-fail restart",		/* SIGPWR    */
192	"Virtual timer expired",	/* SIGVTALRM */
193	"Profiling timer expired",	/* SIGPROF   */
194	"I/O possible",			/* SIGIO     */
195	"Window size changes",		/* SIGWINDOW */
196	"Stopped (signal)",		/* SIGSTOP   */
197	"Stopped",			/* SIGTSTP   */
198	"Continued",			/* SIGCONT   */
199	"Stopped (tty input)",		/* SIGTTIN   */
200	"Stopped (tty output)",		/* SIGTTOU   */
201	"Urgent I/O condition",		/* SIGURG    */
202	"Remote lock lost (NFS)",	/* SIGLOST   */
203	"Signal 31",			/* reserved  */
204	"DIL signal"			/* SIGDIL    */
205};
206#endif /* __hpux__ || __hpux */
207
208#if defined(__hpux__) || defined(__hpux)
209#include <sys/types.h>
210#include <sys/syscall.h>
211#include <sys/signal.h>
212#include <sys/stat.h>
213#include <dirent.h>
214#include <sys/time.h>
215#include <unistd.h>
216
217int
218killpg(int pid, int sig)
219{
220	return kill(-pid, sig);
221}
222
223#if !defined(BSD) && !defined(d_fileno)
224# define d_fileno d_ino
225#endif
226
227#ifndef DEV_DEV_COMPARE
228# define DEV_DEV_COMPARE(a, b) ((a) == (b))
229#endif
230#define ISDOT(c) ((c)[0] == '.' && (((c)[1] == '\0') || ((c)[1] == '/')))
231#define ISDOTDOT(c) ((c)[0] == '.' && ISDOT(&((c)[1])))
232
233char *
234getwd(char *pathname)
235{
236    DIR    *dp;
237    struct dirent *d;
238    extern int errno;
239
240    struct stat st_root, st_cur, st_next, st_dotdot;
241    char    pathbuf[MAXPATHLEN], nextpathbuf[MAXPATHLEN * 2];
242    char   *pathptr, *nextpathptr, *cur_name_add;
243
244    /* find the inode of root */
245    if (stat("/", &st_root) == -1) {
246	(void)sprintf(pathname,
247			"getwd: Cannot stat \"/\" (%s)", strerror(errno));
248	return NULL;
249    }
250    pathbuf[MAXPATHLEN - 1] = '\0';
251    pathptr = &pathbuf[MAXPATHLEN - 1];
252    nextpathbuf[MAXPATHLEN - 1] = '\0';
253    cur_name_add = nextpathptr = &nextpathbuf[MAXPATHLEN - 1];
254
255    /* find the inode of the current directory */
256    if (lstat(".", &st_cur) == -1) {
257	(void)sprintf(pathname,
258			"getwd: Cannot stat \".\" (%s)", strerror(errno));
259	return NULL;
260    }
261    nextpathptr = strrcpy(nextpathptr, "../");
262
263    /* Descend to root */
264    for (;;) {
265
266	/* look if we found root yet */
267	if (st_cur.st_ino == st_root.st_ino &&
268	    DEV_DEV_COMPARE(st_cur.st_dev, st_root.st_dev)) {
269	    (void)strcpy(pathname, *pathptr != '/' ? "/" : pathptr);
270	    return pathname;
271	}
272
273	/* open the parent directory */
274	if (stat(nextpathptr, &st_dotdot) == -1) {
275	    (void)sprintf(pathname,
276			    "getwd: Cannot stat directory \"%s\" (%s)",
277			    nextpathptr, strerror(errno));
278	    return NULL;
279	}
280	if ((dp = opendir(nextpathptr)) == NULL) {
281	    (void)sprintf(pathname,
282			    "getwd: Cannot open directory \"%s\" (%s)",
283			    nextpathptr, strerror(errno));
284	    return NULL;
285	}
286
287	/* look in the parent for the entry with the same inode */
288	if (DEV_DEV_COMPARE(st_dotdot.st_dev, st_cur.st_dev)) {
289	    /* Parent has same device. No need to stat every member */
290	    for (d = readdir(dp); d != NULL; d = readdir(dp))
291		if (d->d_fileno == st_cur.st_ino)
292		    break;
293	} else {
294	    /*
295	     * Parent has a different device. This is a mount point so we
296	     * need to stat every member
297	     */
298	    for (d = readdir(dp); d != NULL; d = readdir(dp)) {
299		if (ISDOT(d->d_name) || ISDOTDOT(d->d_name))
300		    continue;
301		(void)strcpy(cur_name_add, d->d_name);
302		if (lstat(nextpathptr, &st_next) == -1) {
303		    (void)sprintf(pathname,
304			"getwd: Cannot stat \"%s\" (%s)",
305			d->d_name, strerror(errno));
306		    (void)closedir(dp);
307		    return NULL;
308		}
309		/* check if we found it yet */
310		if (st_next.st_ino == st_cur.st_ino &&
311		    DEV_DEV_COMPARE(st_next.st_dev, st_cur.st_dev))
312		    break;
313	    }
314	}
315	if (d == NULL) {
316	    (void)sprintf(pathname,
317		"getwd: Cannot find \".\" in \"..\"");
318	    (void)closedir(dp);
319	    return NULL;
320	}
321	st_cur = st_dotdot;
322	pathptr = strrcpy(pathptr, d->d_name);
323	pathptr = strrcpy(pathptr, "/");
324	nextpathptr = strrcpy(nextpathptr, "../");
325	(void)closedir(dp);
326	*cur_name_add = '\0';
327    }
328} /* end getwd */
329
330#endif /* __hpux */
331
332#if !defined(HAVE_GETCWD)
333char *
334getcwd(path, sz)
335     char *path;
336     int sz;
337{
338	return getwd(path);
339}
340#endif
341
342#if !defined(HAVE_SIGACTION)
343#include "sigact.h"
344#endif
345
346/* force posix signals */
347SignalProc
348bmake_signal(int s, SignalProc a)
349{
350	struct sigaction sa, osa;
351
352	sa.sa_handler = a;
353	sigemptyset(&sa.sa_mask);
354	sa.sa_flags = SA_RESTART;
355
356	if (sigaction(s, &sa, &osa) == -1)
357		return SIG_ERR;
358	else
359		return osa.sa_handler;
360}
361
362#if !defined(HAVE_VSNPRINTF) || !defined(HAVE_VASPRINTF)
363#include <stdarg.h>
364#endif
365
366#if !defined(HAVE_VSNPRINTF)
367#if !defined(__osf__)
368#ifdef _IOSTRG
369#define STRFLAG	(_IOSTRG|_IOWRT)	/* no _IOWRT: avoid stdio bug */
370#else
371#if 0
372#define STRFLAG	(_IOREAD)		/* XXX: Assume svr4 stdio */
373#endif
374#endif /* _IOSTRG */
375#endif /* __osf__ */
376
377int
378vsnprintf(char *s, size_t n, const char *fmt, va_list args)
379{
380#ifdef STRFLAG
381	FILE fakebuf;
382
383	fakebuf._flag = STRFLAG;
384	/*
385	 * Some os's are char * _ptr, others are unsigned char *_ptr...
386	 * We cast to void * to make everyone happy.
387	 */
388	fakebuf._ptr = (void *)s;
389	fakebuf._cnt = n - 1;
390	fakebuf._file = -1;
391	_doprnt(fmt, args, &fakebuf);
392	fakebuf._cnt++;
393	putc('\0', &fakebuf);
394	if (fakebuf._cnt < 0)
395		fakebuf._cnt = 0;
396	return n - fakebuf._cnt - 1;
397#else
398#ifndef _PATH_DEVNULL
399# define _PATH_DEVNULL "/dev/null"
400#endif
401	/*
402	 * Rats... we don't want to clobber anything...
403	 * do a printf to /dev/null to see how much space we need.
404	 */
405	static FILE *nullfp;
406	int need = 0;			/* XXX what's a useful error return? */
407
408	if (!nullfp)
409		nullfp = fopen(_PATH_DEVNULL, "w");
410	if (nullfp) {
411		need = vfprintf(nullfp, fmt, args);
412		if (need < n)
413			(void)vsprintf(s, fmt, args);
414	}
415	return need;
416#endif
417}
418#endif
419
420#if !defined(HAVE_SNPRINTF)
421int
422snprintf(char *s, size_t n, const char *fmt, ...)
423{
424	va_list ap;
425	int rv;
426
427	va_start(ap, fmt);
428	rv = vsnprintf(s, n, fmt, ap);
429	va_end(ap);
430	return rv;
431}
432#endif
433
434#if !defined(HAVE_STRFTIME) || defined(FORCE_BMAKE_STRFTIME)
435/* we only implement enough to pass our unit-tests */
436size_t
437strftime(char *buf, size_t len, const char *fmt, const struct tm *tm)
438{
439	static const char *months[] = {
440		"January", "February", "March",
441		"April", "May", "June",
442		"July", "August", "September",
443		"October", "November", "December"
444	};
445	static const char *days[] = {
446		"Sunday", "Monday", "Tuesday", "Wednesday",
447		"Thursday", "Friday", "Saturday"
448	};
449	int i;
450	size_t s;
451	char *b = buf;
452	char *cp;
453
454	if (fmt == NULL || *fmt == '\0')
455		fmt = "%c";
456	while (*fmt) {
457		if (len == 0)
458			return buf - b;
459		if (*fmt != '%') {
460			*buf++ = *fmt++;
461			len--;
462			continue;
463		}
464		fmt++;
465		switch (*fmt++) {
466		case '%':
467			*buf++ = '%';
468			len--;
469			if (len == 0) return buf - b;
470			/*FALLTHROUGH*/
471		case '\0':
472			*buf = '%';
473			s = 1;
474			break;
475		case 'A':
476			s = snprintf(buf, len, "%s", days[tm->tm_wday]);
477			break;
478		case 'a':
479			s = snprintf(buf, len, "%.3s", days[tm->tm_wday]);
480			break;
481		case 'B':
482			if (tm->tm_mon >= 12)
483				return buf - b;
484			s = snprintf(buf, len, "%s", months[tm->tm_mon]);
485			break;
486		case 'b':
487			if (tm->tm_mon >= 12)
488				return buf - b;
489			s = snprintf(buf, len, "%.3s", months[tm->tm_mon]);
490			break;
491		case 'c':
492			s = strftime(buf, len, "%a %b %e %H:%M:%S %Y", tm);
493			break;
494		case 'd':
495			s = snprintf(buf, len, "%02d", tm->tm_mday);
496			break;
497		case 'e':
498			s = snprintf(buf, len, "%2d", tm->tm_mday);
499			break;
500		case 'F':
501			s = strftime(buf, len, "%y-%m-%d", tm);
502			break;
503		case 'H':
504			s = snprintf(buf, len, "%02d", tm->tm_hour);
505			break;
506		case 'I':
507			if ((i = tm->tm_hour) == 0)
508				i = 24;
509			s = snprintf(buf, len, "%02d", (i > 12) ? (i - 12) : i);
510			break;
511		case 'j':
512			s = snprintf(buf, len, "%03d", tm->tm_yday + 1);
513			break;
514		case 'k':
515			s = snprintf(buf, len, "%d", tm->tm_hour);
516			break;
517		case 'M':
518			s = snprintf(buf, len, "%02d", tm->tm_min);
519			break;
520		case 'm':
521			s = snprintf(buf, len, "%02d", 1 + tm->tm_mon);
522			break;
523		case 'S':
524			s = snprintf(buf, len, "%02d", tm->tm_sec);
525			break;
526		case 's':
527			s = snprintf(buf, len, "%ld", (long)time(NULL));
528			break;
529		case 'T':
530			s = strftime(buf, len, "%H:%M:%S", tm);
531			break;
532		case 'w':
533			s = snprintf(buf, len, "%02d", tm->tm_wday);
534			break;
535		case 'Y':
536			s = snprintf(buf, len, "%d", 1900 + tm->tm_year);
537			break;
538		case 'y':
539			s = snprintf(buf, len, "%02d", tm->tm_year % 100);
540			break;
541		case 'Z':
542			if ((cp = getenv("TZ")) != NULL) {
543				char tz[20];
544
545				i = snprintf(tz, sizeof(tz), "%s", cp);
546				if (i > 5) {
547					cp = &tz[i - 3];
548					tz[3] = '\0';
549				} else
550					cp = tz;
551				s = snprintf(buf, len, "%s",
552				    tm->tm_isdst ? cp : tz);
553			} else
554				s = 0;
555			break;
556		default:
557			s = snprintf(buf, len, "Unsupported format %c",
558			    fmt[-1]);
559			break;
560		}
561		buf += s;
562		len -= s;
563	}
564	return buf - b;
565}
566#endif
567
568#if !defined(HAVE_KILLPG)
569#if !defined(__hpux__) && !defined(__hpux)
570int
571killpg(int pid, int sig)
572{
573    return kill(-pid, sig);
574}
575#endif
576#endif
577
578#if !defined(HAVE_WARNX)
579static void
580vwarnx(const char *fmt, va_list args)
581{
582	fprintf(stderr, "%s: ", progname);
583	if ((fmt)) {
584		vfprintf(stderr, fmt, args);
585		fprintf(stderr, ": ");
586	}
587}
588#endif
589
590#if !defined(HAVE_WARN)
591static void
592vwarn(const char *fmt, va_list args)
593{
594	vwarnx(fmt, args);
595	fprintf(stderr, "%s\n", strerror(errno));
596}
597#endif
598
599#if !defined(HAVE_ERR)
600static void
601verr(int eval, const char *fmt, va_list args)
602{
603	vwarn(fmt, args);
604	exit(eval);
605}
606#endif
607
608#if !defined(HAVE_ERRX)
609static void
610verrx(int eval, const char *fmt, va_list args)
611{
612	vwarnx(fmt, args);
613	exit(eval);
614}
615#endif
616
617#if !defined(HAVE_ERR)
618void
619err(int eval, const char *fmt, ...)
620{
621        va_list ap;
622
623        va_start(ap, fmt);
624        verr(eval, fmt, ap);
625        va_end(ap);
626}
627#endif
628
629#if !defined(HAVE_ERRX)
630void
631errx(int eval, const char *fmt, ...)
632{
633        va_list ap;
634
635        va_start(ap, fmt);
636        verrx(eval, fmt, ap);
637        va_end(ap);
638}
639#endif
640
641#if !defined(HAVE_WARN)
642void
643warn(const char *fmt, ...)
644{
645        va_list ap;
646
647        va_start(ap, fmt);
648        vwarn(fmt, ap);
649        va_end(ap);
650}
651#endif
652
653#if !defined(HAVE_WARNX)
654void
655warnx(const char *fmt, ...)
656{
657        va_list ap;
658
659        va_start(ap, fmt);
660        vwarnx(fmt, ap);
661        va_end(ap);
662}
663#endif
664
665#ifdef HAVE_INTTYPES_H
666#include <inttypes.h>
667#elif defined(HAVE_STDINT_H)
668#include <stdint.h>
669#endif
670#ifdef HAVE_LIMITS_H
671#include <limits.h>
672#endif
673
674#ifndef NUM_TYPE
675# ifdef HAVE_LONG_LONG_INT
676#   define NUM_TYPE long long
677# elif defined(_INT64_T_DECLARED) || defined(int64_t)
678#   define NUM_TYPE int64_t
679# endif
680#endif
681
682#ifdef NUM_TYPE
683#if !defined(HAVE_STRTOLL)
684#define BCS_ONLY
685#define _FUNCNAME strtoll
686#define __INT NUM_TYPE
687#undef __INT_MIN
688#undef __INT_MAX
689#ifdef LLONG_MAX
690# define __INT_MIN LLONG_MIN
691# define __INT_MAX LLONG_MAX
692#elif defined(INT64_MAX)
693# define __INT_MIN INT64_MIN
694# define __INT_MAX INT64_MAX
695#endif
696#ifndef _DIAGASSERT
697# define _DIAGASSERT(e)
698#endif
699#ifndef __UNCONST
700# define __UNCONST(a)      ((void *)(unsigned long)(const void *)(a))
701#endif
702#include "_strtol.h"
703#endif
704
705#endif
706
707#if !defined(HAVE_STRTOL)
708#define BCS_ONLY
709#define _FUNCNAME strtol
710#define __INT long
711#undef __INT_MIN
712#undef __INT_MAX
713#define __INT_MIN LONG_MIN
714#define __INT_MAX LONG_MAX
715#ifndef _DIAGASSERT
716# define _DIAGASSERT(e)
717#endif
718#ifndef __UNCONST
719# define __UNCONST(a)      ((void *)(unsigned long)(const void *)(a))
720#endif
721#include "_strtol.h"
722#endif
723
724#if !defined(HAVE_STRTOUL)
725#define BCS_ONLY
726#define _FUNCNAME strtoul
727#define __INT unsigned long
728#undef __INT_MIN
729#undef __INT_MAX
730#define __INT_MIN 0
731#define __INT_MAX ULONG_MAX
732#ifndef _DIAGASSERT
733# define _DIAGASSERT(e)
734#endif
735#ifndef __UNCONST
736# define __UNCONST(a)      ((void *)(unsigned long)(const void *)(a))
737#endif
738#include "_strtol.h"
739#endif
740