xref: /netbsd-src/usr.bin/make/util.c (revision 181254a7b1bdde6873432bffef2d2decc4b5c22f)
1 /*	$NetBSD: util.c,v 1.58 2020/08/01 14:47:49 rillig Exp $	*/
2 
3 /*
4  * Missing stuff from OS's
5  */
6 #if defined(__MINT__) || defined(__linux__)
7 #include <signal.h>
8 #endif
9 
10 #ifndef MAKE_NATIVE
11 static char rcsid[] = "$NetBSD: util.c,v 1.58 2020/08/01 14:47:49 rillig Exp $";
12 #else
13 #include <sys/cdefs.h>
14 #ifndef lint
15 __RCSID("$NetBSD: util.c,v 1.58 2020/08/01 14:47:49 rillig Exp $");
16 #endif
17 #endif
18 
19 #include <sys/param.h>
20 
21 #include <errno.h>
22 #include <stdio.h>
23 #include <time.h>
24 #include <signal.h>
25 
26 #include "make.h"
27 
28 #if !defined(MAKE_NATIVE) && !defined(HAVE_STRERROR)
29 extern int errno, sys_nerr;
30 extern char *sys_errlist[];
31 
32 char *
33 strerror(int e)
34 {
35     static char buf[100];
36     if (e < 0 || e >= sys_nerr) {
37 	snprintf(buf, sizeof(buf), "Unknown error %d", e);
38 	return buf;
39     }
40     else
41 	return sys_errlist[e];
42 }
43 #endif
44 
45 #if !defined(MAKE_NATIVE) && !defined(HAVE_SETENV)
46 extern char **environ;
47 
48 static char *
49 findenv(const char *name, int *offset)
50 {
51 	size_t i, len;
52 	char *p, *q;
53 
54 	len = strlen(name);
55 	for (i = 0; (q = environ[i]); i++) {
56 		p = strchr(q, '=');
57 		if (p == NULL || p - q != len)
58 			continue;
59 		if (strncmp(name, q, len) == 0) {
60 			*offset = i;
61 			return q + len + 1;
62 		}
63 	}
64 	*offset = i;
65 	return NULL;
66 }
67 
68 char *
69 getenv(const char *name)
70 {
71     int offset;
72 
73     return findenv(name, &offset);
74 }
75 
76 int
77 unsetenv(const char *name)
78 {
79 	char **p;
80 	int offset;
81 
82 	if (name == NULL || *name == '\0' || strchr(name, '=') != NULL) {
83 		errno = EINVAL;
84 		return -1;
85 	}
86 
87 	while (findenv(name, &offset))	{ /* if set multiple times */
88 		for (p = &environ[offset];; ++p)
89 			if (!(*p = *(p + 1)))
90 				break;
91 	}
92 	return 0;
93 }
94 
95 int
96 setenv(const char *name, const char *value, int rewrite)
97 {
98 	char *c, **newenv;
99 	const char *cc;
100 	size_t l_value, size;
101 	int offset;
102 
103 	if (name == NULL || value == NULL) {
104 		errno = EINVAL;
105 		return -1;
106 	}
107 
108 	if (*value == '=')			/* no `=' in value */
109 		++value;
110 	l_value = strlen(value);
111 
112 	/* find if already exists */
113 	if ((c = findenv(name, &offset))) {
114 		if (!rewrite)
115 			return 0;
116 		if (strlen(c) >= l_value)	/* old larger; copy over */
117 			goto copy;
118 	} else {					/* create new slot */
119 		size = sizeof(char *) * (offset + 2);
120 		if (savedEnv == environ) {		/* just increase size */
121 			if ((newenv = realloc(savedEnv, size)) == NULL)
122 				return -1;
123 			savedEnv = newenv;
124 		} else {				/* get new space */
125 			/*
126 			 * We don't free here because we don't know if
127 			 * the first allocation is valid on all OS's
128 			 */
129 			if ((savedEnv = malloc(size)) == NULL)
130 				return -1;
131 			(void)memcpy(savedEnv, environ, size - sizeof(char *));
132 		}
133 		environ = savedEnv;
134 		environ[offset + 1] = NULL;
135 	}
136 	for (cc = name; *cc && *cc != '='; ++cc)	/* no `=' in name */
137 		continue;
138 	size = cc - name;
139 	/* name + `=' + value */
140 	if ((environ[offset] = malloc(size + l_value + 2)) == NULL)
141 		return -1;
142 	c = environ[offset];
143 	(void)memcpy(c, name, size);
144 	c += size;
145 	*c++ = '=';
146 copy:
147 	(void)memcpy(c, value, l_value + 1);
148 	return 0;
149 }
150 
151 #ifdef TEST
152 int
153 main(int argc, char *argv[])
154 {
155 	setenv(argv[1], argv[2], 0);
156 	printf("%s\n", getenv(argv[1]));
157 	unsetenv(argv[1]);
158 	printf("%s\n", getenv(argv[1]));
159 	return 0;
160 }
161 #endif
162 
163 #endif
164 
165 #if defined(__hpux__) || defined(__hpux)
166 /* strrcpy():
167  *	Like strcpy, going backwards and returning the new pointer
168  */
169 static char *
170 strrcpy(char *ptr, char *str)
171 {
172     int len = strlen(str);
173 
174     while (len)
175 	*--ptr = str[--len];
176 
177     return ptr;
178 } /* end strrcpy */
179 
180 char    *sys_siglist[] = {
181 	"Signal 0",
182 	"Hangup",                       /* SIGHUP    */
183 	"Interrupt",                    /* SIGINT    */
184 	"Quit",                         /* SIGQUIT   */
185 	"Illegal instruction",          /* SIGILL    */
186 	"Trace/BPT trap",               /* SIGTRAP   */
187 	"IOT trap",                     /* SIGIOT    */
188 	"EMT trap",                     /* SIGEMT    */
189 	"Floating point exception",     /* SIGFPE    */
190 	"Killed",                       /* SIGKILL   */
191 	"Bus error",                    /* SIGBUS    */
192 	"Segmentation fault",           /* SIGSEGV   */
193 	"Bad system call",              /* SIGSYS    */
194 	"Broken pipe",                  /* SIGPIPE   */
195 	"Alarm clock",                  /* SIGALRM   */
196 	"Terminated",                   /* SIGTERM   */
197 	"User defined signal 1",        /* SIGUSR1   */
198 	"User defined signal 2",        /* SIGUSR2   */
199 	"Child exited",                 /* SIGCLD    */
200 	"Power-fail restart",           /* SIGPWR    */
201 	"Virtual timer expired",        /* SIGVTALRM */
202 	"Profiling timer expired",      /* SIGPROF   */
203 	"I/O possible",                 /* SIGIO     */
204 	"Window size changes",          /* SIGWINDOW */
205 	"Stopped (signal)",             /* SIGSTOP   */
206 	"Stopped",                      /* SIGTSTP   */
207 	"Continued",                    /* SIGCONT   */
208 	"Stopped (tty input)",          /* SIGTTIN   */
209 	"Stopped (tty output)",         /* SIGTTOU   */
210 	"Urgent I/O condition",         /* SIGURG    */
211 	"Remote lock lost (NFS)",       /* SIGLOST   */
212 	"Signal 31",                    /* reserved  */
213 	"DIL signal"                    /* SIGDIL    */
214 };
215 #endif /* __hpux__ || __hpux */
216 
217 #if defined(__hpux__) || defined(__hpux)
218 #include <sys/types.h>
219 #include <sys/syscall.h>
220 #include <sys/signal.h>
221 #include <sys/stat.h>
222 #include <dirent.h>
223 #include <sys/time.h>
224 #include <unistd.h>
225 
226 int
227 killpg(int pid, int sig)
228 {
229     return kill(-pid, sig);
230 }
231 
232 #if !defined(BSD) && !defined(d_fileno)
233 # define d_fileno d_ino
234 #endif
235 
236 #ifndef DEV_DEV_COMPARE
237 # define DEV_DEV_COMPARE(a, b) ((a) == (b))
238 #endif
239 #define ISDOT(c) ((c)[0] == '.' && (((c)[1] == '\0') || ((c)[1] == '/')))
240 #define ISDOTDOT(c) ((c)[0] == '.' && ISDOT(&((c)[1])))
241 
242 char *
243 getwd(char *pathname)
244 {
245     DIR    *dp;
246     struct dirent *d;
247     extern int errno;
248 
249     struct stat st_root, st_cur, st_next, st_dotdot;
250     char    pathbuf[MAXPATHLEN], nextpathbuf[MAXPATHLEN * 2];
251     char   *pathptr, *nextpathptr, *cur_name_add;
252 
253     /* find the inode of root */
254     if (stat("/", &st_root) == -1) {
255 	(void)sprintf(pathname,
256 			"getwd: Cannot stat \"/\" (%s)", strerror(errno));
257 	return NULL;
258     }
259     pathbuf[MAXPATHLEN - 1] = '\0';
260     pathptr = &pathbuf[MAXPATHLEN - 1];
261     nextpathbuf[MAXPATHLEN - 1] = '\0';
262     cur_name_add = nextpathptr = &nextpathbuf[MAXPATHLEN - 1];
263 
264     /* find the inode of the current directory */
265     if (lstat(".", &st_cur) == -1) {
266 	(void)sprintf(pathname,
267 			"getwd: Cannot stat \".\" (%s)", strerror(errno));
268 	return NULL;
269     }
270     nextpathptr = strrcpy(nextpathptr, "../");
271 
272     /* Descend to root */
273     for (;;) {
274 
275 	/* look if we found root yet */
276 	if (st_cur.st_ino == st_root.st_ino &&
277 	    DEV_DEV_COMPARE(st_cur.st_dev, st_root.st_dev)) {
278 	    (void)strcpy(pathname, *pathptr != '/' ? "/" : pathptr);
279 	    return pathname;
280 	}
281 
282 	/* open the parent directory */
283 	if (stat(nextpathptr, &st_dotdot) == -1) {
284 	    (void)sprintf(pathname,
285 			    "getwd: Cannot stat directory \"%s\" (%s)",
286 			    nextpathptr, strerror(errno));
287 	    return NULL;
288 	}
289 	if ((dp = opendir(nextpathptr)) == NULL) {
290 	    (void)sprintf(pathname,
291 			    "getwd: Cannot open directory \"%s\" (%s)",
292 			    nextpathptr, strerror(errno));
293 	    return NULL;
294 	}
295 
296 	/* look in the parent for the entry with the same inode */
297 	if (DEV_DEV_COMPARE(st_dotdot.st_dev, st_cur.st_dev)) {
298 	    /* Parent has same device. No need to stat every member */
299 	    for (d = readdir(dp); d != NULL; d = readdir(dp))
300 		if (d->d_fileno == st_cur.st_ino)
301 		    break;
302 	}
303 	else {
304 	    /*
305 	     * Parent has a different device. This is a mount point so we
306 	     * need to stat every member
307 	     */
308 	    for (d = readdir(dp); d != NULL; d = readdir(dp)) {
309 		if (ISDOT(d->d_name) || ISDOTDOT(d->d_name))
310 		    continue;
311 		(void)strcpy(cur_name_add, d->d_name);
312 		if (lstat(nextpathptr, &st_next) == -1) {
313 		    (void)sprintf(pathname,
314 			"getwd: Cannot stat \"%s\" (%s)",
315 			d->d_name, strerror(errno));
316 		    (void)closedir(dp);
317 		    return NULL;
318 		}
319 		/* check if we found it yet */
320 		if (st_next.st_ino == st_cur.st_ino &&
321 		    DEV_DEV_COMPARE(st_next.st_dev, st_cur.st_dev))
322 		    break;
323 	    }
324 	}
325 	if (d == NULL) {
326 	    (void)sprintf(pathname,
327 		"getwd: Cannot find \".\" in \"..\"");
328 	    (void)closedir(dp);
329 	    return NULL;
330 	}
331 	st_cur = st_dotdot;
332 	pathptr = strrcpy(pathptr, d->d_name);
333 	pathptr = strrcpy(pathptr, "/");
334 	nextpathptr = strrcpy(nextpathptr, "../");
335 	(void)closedir(dp);
336 	*cur_name_add = '\0';
337     }
338 } /* end getwd */
339 #endif /* __hpux */
340 
341 /* force posix signals */
342 void (*
343 bmake_signal(int s, void (*a)(int)))(int)
344 {
345     struct sigaction sa, osa;
346 
347     sa.sa_handler = a;
348     sigemptyset(&sa.sa_mask);
349     sa.sa_flags = SA_RESTART;
350 
351     if (sigaction(s, &sa, &osa) == -1)
352 	return SIG_ERR;
353     else
354 	return osa.sa_handler;
355 }
356 
357 #if !defined(MAKE_NATIVE) && !defined(HAVE_VSNPRINTF)
358 #include <stdarg.h>
359 
360 #if !defined(__osf__)
361 #ifdef _IOSTRG
362 #define STRFLAG	(_IOSTRG|_IOWRT)	/* no _IOWRT: avoid stdio bug */
363 #else
364 #if 0
365 #define STRFLAG	(_IOREAD)		/* XXX: Assume svr4 stdio */
366 #endif
367 #endif /* _IOSTRG */
368 #endif /* __osf__ */
369 
370 int
371 vsnprintf(char *s, size_t n, const char *fmt, va_list args)
372 {
373 #ifdef STRFLAG
374 	FILE fakebuf;
375 
376 	fakebuf._flag = STRFLAG;
377 	/*
378 	 * Some os's are char * _ptr, others are unsigned char *_ptr...
379 	 * We cast to void * to make everyone happy.
380 	 */
381 	fakebuf._ptr = (void *)s;
382 	fakebuf._cnt = n-1;
383 	fakebuf._file = -1;
384 	_doprnt(fmt, args, &fakebuf);
385 	fakebuf._cnt++;
386 	putc('\0', &fakebuf);
387 	if (fakebuf._cnt<0)
388 	    fakebuf._cnt = 0;
389 	return n-fakebuf._cnt-1;
390 #else
391 	(void)vsprintf(s, fmt, args);
392 	return strlen(s);
393 #endif
394 }
395 
396 int
397 snprintf(char *s, size_t n, const char *fmt, ...)
398 {
399 	va_list ap;
400 	int rv;
401 
402 	va_start(ap, fmt);
403 	rv = vsnprintf(s, n, fmt, ap);
404 	va_end(ap);
405 	return rv;
406 }
407 
408 #if !defined(MAKE_NATIVE) && !defined(HAVE_STRFTIME)
409 size_t
410 strftime(char *buf, size_t len, const char *fmt, const struct tm *tm)
411 {
412 	static char months[][4] = {
413 		"Jan", "Feb", "Mar", "Apr", "May", "Jun",
414 		"Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
415 	};
416 
417 	size_t s;
418 	char *b = buf;
419 
420 	while (*fmt) {
421 		if (len == 0)
422 			return buf - b;
423 		if (*fmt != '%') {
424 			*buf++ = *fmt++;
425 			len--;
426 			continue;
427 		}
428 		switch (*fmt++) {
429 		case '%':
430 			*buf++ = '%';
431 			len--;
432 			if (len == 0) return buf - b;
433 			/*FALLTHROUGH*/
434 		case '\0':
435 			*buf = '%';
436 			s = 1;
437 			break;
438 		case 'k':
439 			s = snprintf(buf, len, "%d", tm->tm_hour);
440 			break;
441 		case 'M':
442 			s = snprintf(buf, len, "%02d", tm->tm_min);
443 			break;
444 		case 'S':
445 			s = snprintf(buf, len, "%02d", tm->tm_sec);
446 			break;
447 		case 'b':
448 			if (tm->tm_mon >= 12)
449 				return buf - b;
450 			s = snprintf(buf, len, "%s", months[tm->tm_mon]);
451 			break;
452 		case 'd':
453 			s = snprintf(buf, len, "%02d", tm->tm_mday);
454 			break;
455 		case 'Y':
456 			s = snprintf(buf, len, "%d", 1900 + tm->tm_year);
457 			break;
458 		default:
459 			s = snprintf(buf, len, "Unsupported format %c",
460 			    fmt[-1]);
461 			break;
462 		}
463 		buf += s;
464 		len -= s;
465 	}
466 }
467 #endif
468 #endif
469