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