util.c revision 1.22.12.1 1 /* $NetBSD: util.c,v 1.22.12.1 2007/11/06 23:36:10 matt Exp $ */
2
3 /*
4 * Copyright (c) 1988, Larry Wall
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following condition
8 * is met:
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this condition and the following disclaimer.
11 *
12 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
13 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
14 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
15 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
16 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
17 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
18 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
19 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
20 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
21 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
22 * SUCH DAMAGE.
23 */
24
25 #include <sys/cdefs.h>
26 #ifndef lint
27 __RCSID("$NetBSD: util.c,v 1.22.12.1 2007/11/06 23:36:10 matt Exp $");
28 #endif /* not lint */
29
30 #include <sys/param.h>
31
32 #include "EXTERN.h"
33 #include "common.h"
34 #include "INTERN.h"
35 #include "util.h"
36 #include "backupfile.h"
37
38 #include <stdarg.h>
39 #include <stdlib.h>
40 #include <unistd.h>
41 #include <fcntl.h>
42
43 /*
44 * Rename a file, copying it if necessary.
45 */
46 int
47 move_file(char *from, char *to)
48 {
49 char bakname[MAXPATHLEN];
50 char *s;
51 ssize_t i;
52 int fromfd;
53
54 if ( check_only == TRUE )
55 return(0);
56
57 /* to stdout? */
58
59 if (strEQ(to, "-")) {
60 #ifdef DEBUGGING
61 if (debug & 4)
62 say("Moving %s to stdout.\n", from);
63 #endif
64 fromfd = open(from, 0);
65 if (fromfd < 0)
66 pfatal("internal error, can't reopen %s", from);
67 while ((i = read(fromfd, buf, sizeof buf)) > 0)
68 if (write(1, buf, i) != 1)
69 pfatal("write failed");
70 Close(fromfd);
71 return 0;
72 }
73
74 if (origprae) {
75 strlcpy(bakname, origprae, sizeof(bakname));
76 strlcat(bakname, to, sizeof(bakname));
77 } else {
78 #ifndef NODIR
79 char *backupname = find_backup_file_name(to);
80 strlcpy(bakname, backupname, sizeof(bakname));
81 free(backupname);
82 #else /* NODIR */
83 strlcpy(bakname, to, sizeof(bakname));
84 strlcat(bakname, simple_backup_suffix, sizeof(bakname));
85 #endif /* NODIR */
86 }
87
88 if (stat(to, &filestat) == 0) { /* output file exists */
89 dev_t to_device = filestat.st_dev;
90 ino_t to_inode = filestat.st_ino;
91 char *simplename = bakname;
92
93 for (s = bakname; *s; s++) {
94 if (*s == '/')
95 simplename = s + 1;
96 }
97 /*
98 * Find a backup name that is not the same file.
99 * Change the first lowercase char into uppercase;
100 * if that isn't sufficient, chop off the first char
101 * and try again.
102 */
103 while (stat(bakname, &filestat) == 0 &&
104 to_device == filestat.st_dev &&
105 to_inode == filestat.st_ino) {
106 /* Skip initial non-lowercase chars. */
107 for (s = simplename;
108 *s && *s == toupper((unsigned char)*s);
109 s++)
110 ;
111 if (*s)
112 *s = toupper((unsigned char)*s);
113 else
114 strcpy(simplename, simplename + 1);
115 }
116 while (unlink(bakname) >= 0)
117 ; /* while() is for benefit of Eunice */
118 #ifdef DEBUGGING
119 if (debug & 4)
120 say("Moving %s to %s.\n", to, bakname);
121 #endif
122 if (link(to, bakname) < 0) {
123 /*
124 * Maybe `to' is a symlink into a different file
125 * system. Copying replaces the symlink with a file;
126 * using rename would be better.
127 */
128 int tofd;
129 int bakfd;
130
131 bakfd = creat(bakname, 0666);
132 if (bakfd < 0) {
133 say("Can't backup %s, output is in %s: %s\n",
134 to, from, strerror(errno));
135 return -1;
136 }
137 tofd = open(to, 0);
138 if (tofd < 0)
139 pfatal("internal error, can't open %s", to);
140 while ((i = read(tofd, buf, sizeof buf)) > 0)
141 if (write(bakfd, buf, i) != i)
142 pfatal("write failed");
143 Close(tofd);
144 Close(bakfd);
145 }
146 while (unlink(to) >= 0) ;
147 }
148 #ifdef DEBUGGING
149 if (debug & 4)
150 say("Moving %s to %s.\n", from, to);
151 #endif
152 if (link(from, to) < 0) { /* different file system? */
153 int tofd;
154
155 tofd = creat(to, 0666);
156 if (tofd < 0) {
157 say("Can't create %s, output is in %s: %s\n",
158 to, from, strerror(errno));
159 return -1;
160 }
161 fromfd = open(from, 0);
162 if (fromfd < 0)
163 pfatal("internal error, can't reopen %s", from);
164 while ((i = read(fromfd, buf, sizeof buf)) > 0)
165 if (write(tofd, buf, i) != i)
166 pfatal("write failed");
167 Close(fromfd);
168 Close(tofd);
169 }
170 Unlink(from);
171 return 0;
172 }
173
174 /*
175 * Copy a file.
176 */
177 void
178 copy_file(char *from, char *to)
179 {
180 int tofd;
181 int fromfd;
182 size_t i;
183
184 tofd = creat(to, 0666);
185 if (tofd < 0)
186 pfatal("can't create %s", to);
187 fromfd = open(from, 0);
188 if (fromfd < 0)
189 pfatal("internal error, can't reopen %s", from);
190 while ((i = read(fromfd, buf, sizeof buf)) > 0)
191 if (write(tofd, buf, i) != i)
192 pfatal("write to %s failed", to);
193 Close(fromfd);
194 Close(tofd);
195 }
196
197 /*
198 * malloc with result test.
199 */
200 void *
201 xmalloc(size_t size)
202 {
203 void *p;
204
205 if ((p = malloc(size)) == NULL)
206 fatal("out of memory\n");
207 return p;
208 }
209
210 /*
211 * realloc with result test.
212 */
213 void *
214 xrealloc(void *ptr, size_t size)
215 {
216 void *p;
217
218 if ((p = realloc(ptr, size)) == NULL)
219 fatal("out of memory\n");
220 return p;
221 }
222
223 /*
224 * strdup with result test.
225 */
226 char *
227 xstrdup(const char *s)
228 {
229 char *p;
230
231 if ((p = strdup(s)) == NULL)
232 fatal("out of memory\n");
233 return p;
234 }
235
236 /*
237 * Vanilla terminal output.
238 */
239 void
240 say(const char *pat, ...)
241 {
242 va_list ap;
243 va_start(ap, pat);
244
245 vfprintf(stderr, pat, ap);
246 va_end(ap);
247 Fflush(stderr);
248 }
249
250 /*
251 * Terminal output, pun intended.
252 */
253 void /* very void */
254 fatal(const char *pat, ...)
255 {
256 va_list ap;
257 va_start(ap, pat);
258
259 fprintf(stderr, "patch: **** ");
260 vfprintf(stderr, pat, ap);
261 va_end(ap);
262 my_exit(1);
263 }
264
265 /*
266 * Say something from patch, something from the system, then silence...
267 */
268 void /* very void */
269 pfatal(const char *pat, ...)
270 {
271 va_list ap;
272 int errnum = errno;
273 va_start(ap, pat);
274
275 fprintf(stderr, "patch: **** ");
276 vfprintf(stderr, pat, ap);
277 fprintf(stderr, ": %s\n", strerror(errnum));
278 va_end(ap);
279 my_exit(1);
280 }
281
282 /*
283 * Get a response from the user, somehow or other.
284 */
285 void
286 ask(const char *pat, ...)
287 {
288 int ttyfd;
289 int r;
290 bool tty2 = isatty(2);
291 va_list ap;
292 va_start(ap, pat);
293
294 (void)vsprintf(buf, pat, ap);
295 va_end(ap);
296 Fflush(stderr);
297 write(2, buf, strlen(buf));
298 if (tty2) { /* might be redirected to a file */
299 r = read(2, buf, sizeof buf);
300 } else if (isatty(1)) { /* this may be new file output */
301 Fflush(stdout);
302 write(1, buf, strlen(buf));
303 r = read(1, buf, sizeof buf);
304 } else if ((ttyfd = open("/dev/tty", 2)) >= 0 && isatty(ttyfd)) {
305 /* might be deleted or unwritable */
306 write(ttyfd, buf, strlen(buf));
307 r = read(ttyfd, buf, sizeof buf);
308 Close(ttyfd);
309 } else if (isatty(0)) { /* this is probably patch input */
310 Fflush(stdin);
311 write(0, buf, strlen(buf));
312 r = read(0, buf, sizeof buf);
313 } else { /* no terminal at all--default it */
314 buf[0] = '\n';
315 r = 1;
316 }
317 if (r <= 0)
318 buf[0] = 0;
319 else
320 buf[r] = '\0';
321 if (!tty2)
322 say("%s", buf);
323 }
324
325 /*
326 * How to handle certain events when not in a critical region.
327 */
328 void
329 set_signals(int reset)
330 {
331 static void (*hupval)(int);
332 static void (*intval)(int);
333
334 if (!reset) {
335 hupval = signal(SIGHUP, SIG_IGN);
336 if (hupval != SIG_IGN)
337 hupval = my_sig_exit;
338 intval = signal(SIGINT, SIG_IGN);
339 if (intval != SIG_IGN)
340 intval = my_sig_exit;
341 }
342 Signal(SIGHUP, hupval);
343 Signal(SIGINT, intval);
344 }
345
346 /*
347 * How to handle certain events when in a critical region.
348 */
349 void
350 ignore_signals()
351 {
352 Signal(SIGHUP, SIG_IGN);
353 Signal(SIGINT, SIG_IGN);
354 }
355
356 /*
357 * Make sure we'll have the directories to create a file.
358 * If `striplast' is TRUE, ignore the last element of `filename'.
359 */
360 void
361 makedirs(char *filename, bool striplast)
362 {
363 char tmpbuf[MAXPATHLEN];
364 char *s = tmpbuf;
365 char *dirv[MAXPATHLEN]; /* Point to the NULs between elements. */
366 int i;
367 int dirvp = 0; /* Number of finished entries in dirv. */
368
369 /*
370 * Copy `filename' into `tmpbuf' with a NUL instead of a slash
371 * between the directories.
372 */
373 while (*filename) {
374 if (*filename == '/') {
375 filename++;
376 dirv[dirvp++] = s;
377 *s++ = '\0';
378 } else {
379 *s++ = *filename++;
380 }
381 }
382 *s = '\0';
383 dirv[dirvp] = s;
384 if (striplast)
385 dirvp--;
386 if (dirvp < 0)
387 return;
388
389 strlcpy(buf, "mkdir", sizeof(buf));
390 s = buf;
391 for (i = 0; i <= dirvp; i++) {
392 struct stat sbuf;
393
394 if (stat(tmpbuf, &sbuf) && errno == ENOENT) {
395 while (*s)
396 s++;
397 *s++ = ' ';
398 strlcpy(s, tmpbuf, sizeof(buf) - (s - buf));
399 }
400 *dirv[i] = '/';
401 }
402 if (s != buf)
403 system(buf);
404 }
405
406 /*
407 * Make filenames more reasonable.
408 */
409 char *
410 fetchname(char *at, int strip_leading, int assume_exists)
411 {
412 char *fullname;
413 char *name;
414 char *t;
415 char tmpbuf[MAXPATHLEN];
416 int sleading = strip_leading;
417
418 if (!at)
419 return NULL;
420 while (isspace((unsigned char)*at))
421 at++;
422 #ifdef DEBUGGING
423 if (debug & 128)
424 say("fetchname %s %d %d\n", at, strip_leading, assume_exists);
425 #endif
426 filename_is_dev_null = FALSE;
427 if (strnEQ(at, "/dev/null", 9)) {
428 /* So files can be created by diffing against /dev/null. */
429 filename_is_dev_null = TRUE;
430 return NULL;
431 }
432 name = fullname = t = xstrdup(at);
433
434 /* Strip off up to `sleading' leading slashes and null terminate. */
435 for (; *t && !isspace((unsigned char)*t); t++)
436 if (*t == '/')
437 if (--sleading >= 0)
438 name = t + 1;
439 *t = '\0';
440
441 /*
442 * If no -p option was given (957 is the default value!),
443 * we were given a relative pathname,
444 * and the leading directories that we just stripped off all exist,
445 * put them back on.
446 */
447 if (strip_leading == 957 && name != fullname && *fullname != '/') {
448 name[-1] = '\0';
449 if (stat(fullname, &filestat) == 0 &&
450 S_ISDIR(filestat.st_mode)) {
451 name[-1] = '/';
452 name = fullname;
453 }
454 }
455
456 name = xstrdup(name);
457 free(fullname);
458
459 if (stat(name, &filestat) && !assume_exists) {
460 char *filebase = basename(name);
461 size_t pathlen = filebase - name;
462
463 /* Put any leading path into `tmpbuf'. */
464 if (pathlen >= sizeof(tmpbuf))
465 return NULL;
466 strncpy(tmpbuf, name, pathlen);
467 tmpbuf[pathlen] = '\0';
468
469 #define try(f, a1, a2) \
470 (snprintf(tmpbuf + pathlen, sizeof(tmpbuf) - pathlen, f, a1, a2), \
471 stat(tmpbuf, &filestat) == 0)
472 #define try1(f, a1) \
473 (snprintf(tmpbuf + pathlen, sizeof(tmpbuf) - pathlen, f, a1), \
474 stat(tmpbuf, &filestat) == 0)
475 if (try("RCS/%s%s", filebase, RCSSUFFIX) ||
476 try1("RCS/%s" , filebase) ||
477 try( "%s%s", filebase, RCSSUFFIX) ||
478 try("SCCS/%s%s", SCCSPREFIX, filebase) ||
479 try( "%s%s", SCCSPREFIX, filebase))
480 return name;
481 free(name);
482 name = NULL;
483 }
484
485 return name;
486 }
487