Use %zd for size_t printf()s
[dragonfly.git] / usr.bin / make / util.c
1 /*-
2  * Copyright (c) 2002 Juli Mallett.  All rights reserved.
3  * Copyright (c) 1988, 1989, 1990, 1993
4  *      The Regents of the University of California.  All rights reserved.
5  * Copyright (c) 1989 by Berkeley Softworks
6  * All rights reserved.
7  *
8  * This code is derived from software contributed to Berkeley by
9  * Adam de Boor.
10  *
11  * Redistribution and use in source and binary forms, with or without
12  * modification, are permitted provided that the following conditions
13  * are met:
14  * 1. Redistributions of source code must retain the above copyright
15  *    notice, this list of conditions and the following disclaimer.
16  * 2. Redistributions in binary form must reproduce the above copyright
17  *    notice, this list of conditions and the following disclaimer in the
18  *    documentation and/or other materials provided with the distribution.
19  * 3. All advertising materials mentioning features or use of this software
20  *    must display the following acknowledgement:
21  *      This product includes software developed by the University of
22  *      California, Berkeley and its contributors.
23  * 4. Neither the name of the University nor the names of its contributors
24  *    may be used to endorse or promote products derived from this software
25  *    without specific prior written permission.
26  *
27  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
28  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
29  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
30  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
31  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
32  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
33  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
34  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
35  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
36  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
37  * SUCH DAMAGE.
38  *
39  * $FreeBSD: src/usr.bin/make/util.c,v 1.16 2005/02/04 13:23:39 harti Exp $
40  * $DragonFly: src/usr.bin/make/util.c,v 1.25 2006/07/27 00:41:10 corecode Exp $
41  */
42
43 /*-
44  * util.c --
45  *      General utilitarian routines for make(1).
46  */
47
48 #include <sys/types.h>
49 #include <sys/stat.h>
50 #include <err.h>
51 #include <errno.h>
52 #include <stdarg.h>
53 #include <stdlib.h>
54 #include <string.h>
55 #include <unistd.h>
56 #include <fcntl.h>
57
58 #include "globals.h"
59 #include "job.h"
60 #include "targ.h"
61 #include "util.h"
62
63 /*-
64  * Debug --
65  *      Print a debugging message given its format.
66  *
67  * Results:
68  *      None.
69  *
70  * Side Effects:
71  *      The message is printed.
72  */
73 /* VARARGS */
74 void
75 Debug(const char *fmt, ...)
76 {
77         va_list ap;
78
79         va_start(ap, fmt);
80         vfprintf(stderr, fmt, ap);
81         va_end(ap);
82         fflush(stderr);
83 }
84
85 /*-
86  * Print a debugging message given its format and append the current
87  * errno description. Terminate with a newline.
88  */
89 /* VARARGS */
90 void
91 DebugM(const char *fmt, ...)
92 {
93         va_list ap;
94         int e = errno;
95
96         va_start(ap, fmt);
97         vfprintf(stderr, fmt, ap);
98         fprintf(stderr, ": %s\n", strerror(e));
99         va_end(ap);
100         fflush(stderr);
101 }
102
103 /*-
104  * Error --
105  *      Print an error message given its format.
106  *
107  * Results:
108  *      None.
109  *
110  * Side Effects:
111  *      The message is printed.
112  */
113 /* VARARGS */
114 void
115 Error(const char *fmt, ...)
116 {
117         va_list ap;
118
119         va_start(ap, fmt);
120         vfprintf(stderr, fmt, ap);
121         va_end(ap);
122         fprintf(stderr, "\n");
123         fflush(stderr);
124 }
125
126 /*-
127  * Fatal --
128  *      Produce a Fatal error message. If jobs are running, waits for them
129  *      to finish.
130  *
131  * Results:
132  *      None
133  *
134  * Side Effects:
135  *      The program exits
136  */
137 /* VARARGS */
138 void
139 Fatal(const char *fmt, ...)
140 {
141         va_list ap;
142
143         va_start(ap, fmt);
144         if (jobsRunning)
145                 Job_Wait();
146
147         vfprintf(stderr, fmt, ap);
148         va_end(ap);
149         fprintf(stderr, "\n");
150         fflush(stderr);
151
152         if (DEBUG(GRAPH2))
153                 Targ_PrintGraph(2);
154         exit(2);                /* Not 1 so -q can distinguish error */
155 }
156
157 /*
158  * Punt --
159  *      Major exception once jobs are being created. Kills all jobs, prints
160  *      a message and exits.
161  *
162  * Results:
163  *      None
164  *
165  * Side Effects:
166  *      All children are killed indiscriminately and the program Lib_Exits
167  */
168 /* VARARGS */
169 void
170 Punt(const char *fmt, ...)
171 {
172         va_list ap;
173
174         va_start(ap, fmt);
175         fprintf(stderr, "make: ");
176         vfprintf(stderr, fmt, ap);
177         va_end(ap);
178         fprintf(stderr, "\n");
179         fflush(stderr);
180
181         DieHorribly();
182 }
183
184 /*-
185  * DieHorribly --
186  *      Exit without giving a message.
187  *
188  * Results:
189  *      None
190  *
191  * Side Effects:
192  *      A big one...
193  */
194 void
195 DieHorribly(void)
196 {
197         if (jobsRunning)
198                 Job_AbortAll();
199         if (DEBUG(GRAPH2))
200                 Targ_PrintGraph(2);
201         exit(2);                /* Not 1, so -q can distinguish error */
202 }
203
204 /*
205  * Finish --
206  *      Called when aborting due to errors in child shell to signal
207  *      abnormal exit, with the number of errors encountered in Make_Make.
208  *
209  * Results:
210  *      None
211  *
212  * Side Effects:
213  *      The program exits
214  */
215 void
216 Finish(int errors)
217 {
218
219         Fatal("%d error%s", errors, errors == 1 ? "" : "s");
220 }
221
222 /*
223  * emalloc --
224  *      malloc, but die on error.
225  */
226 void *
227 emalloc(size_t size)
228 {
229         void *p;
230
231         if ((p = malloc(size)) == NULL)
232                 err(2, "malloc(%zd)", size);
233         return (p);
234 }
235
236 /*
237  * estrdup --
238  *      strdup, but die on error.
239  */
240 char *
241 estrdup(const char *str)
242 {
243         char *p;
244
245         if ((p = strdup(str)) == NULL)
246                 err(2, "strdup(%s)", str);
247         return (p);
248 }
249
250 /*
251  * erealloc --
252  *      realloc, but die on error.
253  */
254 void *
255 erealloc(void *ptr, size_t size)
256 {
257         char *p;
258
259         if ((p = realloc(ptr, size)) == NULL)
260                 err(2, "realloc(%zd)", size);
261         return (p);
262 }
263
264 /*
265  * enunlink --
266  *      Remove a file carefully, avoiding directories.
267  */
268 int
269 eunlink(const char *file)
270 {
271         struct stat st;
272
273         if (lstat(file, &st) == -1)
274                 return (-1);
275
276         if (S_ISDIR(st.st_mode)) {
277                 errno = EISDIR;
278                 return (-1);
279         }
280         return (unlink(file));
281 }
282
283 /*
284  * Convert a flag word to a printable thing and print it
285  */
286 void
287 print_flags(FILE *fp, const struct flag2str *tab, u_int flags, int par)
288 {
289         int first = 1;
290
291         if (par)
292                 fprintf(fp, "(");
293         while (tab->str != NULL) {
294                 if (flags & tab->flag) {
295                         if (!first)
296                                 fprintf(fp, par ? "|" : " ");
297                         first = 0;
298                         fprintf(fp, "%s", tab->str);
299                 }
300                 tab++;
301         }
302         if (par)
303                 fprintf(fp, ")");
304 }
305
306 /**
307  * Create a fifo file with a uniq filename, and returns a file
308  * descriptor to that fifo.
309  */
310 int
311 mkfifotemp(char *template)
312 {
313         char *start;
314         char *pathend;
315         char *ptr;
316         const char padchar[] =
317             "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
318
319         if (template[0] == '\0') {
320                 errno = EINVAL; /* bad input string */
321                 return (-1);
322         }
323
324         /* Find end of template string. */
325         pathend = strchr(template, '\0');
326         ptr = pathend - 1;
327
328         /*
329          * Starting from the end of the template replace spaces with 'X' in
330          * them with random characters until there are no more 'X'.
331          */
332         while (ptr >= template && *ptr == 'X') {
333                 uint32_t rand_num = arc4random() % (sizeof(padchar) - 1);
334                 *ptr-- = padchar[rand_num];
335         }
336         start = ptr + 1;
337
338         /* Check the target directory. */
339         for (; ptr > template; --ptr) {
340                 if (*ptr == '/') {
341                         struct stat sbuf;
342
343                         *ptr = '\0';
344                         if (stat(template, &sbuf) != 0)
345                                 return (-1);
346
347                         if (!S_ISDIR(sbuf.st_mode)) {
348                                 errno = ENOTDIR;
349                                 return (-1);
350                         }
351                         *ptr = '/';
352                         break;
353                 }
354         }
355
356         for (;;) {
357                 if (mkfifo(template, 0600) == 0) {
358                         int fd;
359
360                         if ((fd = open(template, O_RDWR, 0600)) < 0) {
361                                 unlink(template);
362                                 return (-1);
363                         } else {
364                                 return (fd);
365                         }
366                 } else {
367                         if (errno != EEXIST) {
368                                 return (-1);
369                         }
370                 }
371
372                 /*
373                  * If we have a collision, cycle through the space of
374                  * filenames.
375                  */
376                 for (ptr = start;;) {
377                         char *pad;
378
379                         if (*ptr == '\0' || ptr == pathend)
380                                 return (-1);
381
382                         pad = strchr(padchar, *ptr);
383                         if (pad == NULL || *++pad == '\0') {
384                                 *ptr++ = padchar[0];
385                         } else {
386                                 *ptr++ = *pad;
387                                 break;
388                         }
389                 }
390         }
391         /*NOTREACHED*/
392 }
393