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