util.c revision 1.15 1 /* $NetBSD: util.c,v 1.15 2003/05/30 18:14:13 kristerw Exp $ */
2 #include <sys/cdefs.h>
3 #ifndef lint
4 __RCSID("$NetBSD: util.c,v 1.15 2003/05/30 18:14:13 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 size_t 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 size_t 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 * realloc with result test.
186 */
187 void *
188 xrealloc(void *ptr, size_t size)
189 {
190 void *p;
191
192 if ((p = realloc(ptr, size)) == NULL)
193 fatal("out of memory\n");
194 return p;
195 }
196
197 /*
198 * strdup with result test.
199 */
200 char *
201 xstrdup(const char *s)
202 {
203 char *p;
204
205 if ((p = strdup(s)) == NULL)
206 fatal("out of memory\n");
207 return p;
208 }
209
210 /*
211 * Vanilla terminal output.
212 */
213 void
214 say(const char *pat, ...)
215 {
216 va_list ap;
217 va_start(ap, pat);
218
219 vfprintf(stderr, pat, ap);
220 va_end(ap);
221 Fflush(stderr);
222 }
223
224 /*
225 * Terminal output, pun intended.
226 */
227 void /* very void */
228 fatal(const char *pat, ...)
229 {
230 va_list ap;
231 va_start(ap, pat);
232
233 fprintf(stderr, "patch: **** ");
234 vfprintf(stderr, pat, ap);
235 va_end(ap);
236 my_exit(1);
237 }
238
239 /*
240 * Say something from patch, something from the system, then silence...
241 */
242 void /* very void */
243 pfatal(const char *pat, ...)
244 {
245 va_list ap;
246 int errnum = errno;
247 va_start(ap, pat);
248
249 fprintf(stderr, "patch: **** ");
250 vfprintf(stderr, pat, ap);
251 fprintf(stderr, ": %s\n", strerror(errnum));
252 va_end(ap);
253 my_exit(1);
254 }
255
256 /*
257 * Get a response from the user, somehow or other.
258 */
259 void
260 ask(const char *pat, ...)
261 {
262 int ttyfd;
263 int r;
264 bool tty2 = isatty(2);
265 va_list ap;
266 va_start(ap, pat);
267
268 (void)vsprintf(buf, pat, ap);
269 va_end(ap);
270 Fflush(stderr);
271 write(2, buf, strlen(buf));
272 if (tty2) { /* might be redirected to a file */
273 r = read(2, buf, sizeof buf);
274 } else if (isatty(1)) { /* this may be new file output */
275 Fflush(stdout);
276 write(1, buf, strlen(buf));
277 r = read(1, buf, sizeof buf);
278 } else if ((ttyfd = open("/dev/tty", 2)) >= 0 && isatty(ttyfd)) {
279 /* might be deleted or unwritable */
280 write(ttyfd, buf, strlen(buf));
281 r = read(ttyfd, buf, sizeof buf);
282 Close(ttyfd);
283 } else if (isatty(0)) { /* this is probably patch input */
284 Fflush(stdin);
285 write(0, buf, strlen(buf));
286 r = read(0, buf, sizeof buf);
287 } else { /* no terminal at all--default it */
288 buf[0] = '\n';
289 r = 1;
290 }
291 if (r <= 0)
292 buf[0] = 0;
293 else
294 buf[r] = '\0';
295 if (!tty2)
296 say("%s", buf);
297 }
298
299 /*
300 * How to handle certain events when not in a critical region.
301 */
302 void
303 set_signals(int reset)
304 {
305 static void (*hupval)(int);
306 static void (*intval)(int);
307
308 if (!reset) {
309 hupval = signal(SIGHUP, SIG_IGN);
310 if (hupval != SIG_IGN)
311 hupval = my_exit;
312 intval = signal(SIGINT, SIG_IGN);
313 if (intval != SIG_IGN)
314 intval = my_exit;
315 }
316 Signal(SIGHUP, hupval);
317 Signal(SIGINT, intval);
318 }
319
320 /*
321 * How to handle certain events when in a critical region.
322 */
323 void
324 ignore_signals()
325 {
326 Signal(SIGHUP, SIG_IGN);
327 Signal(SIGINT, SIG_IGN);
328 }
329
330 /*
331 * Make sure we'll have the directories to create a file.
332 * If `striplast' is TRUE, ignore the last element of `filename'.
333 */
334 void
335 makedirs(char *filename, bool striplast)
336 {
337 char tmpbuf[MAXPATHLEN];
338 char *s = tmpbuf;
339 char *dirv[MAXPATHLEN]; /* Point to the NULs between elements. */
340 int i;
341 int dirvp = 0; /* Number of finished entries in dirv. */
342
343 /*
344 * Copy `filename' into `tmpbuf' with a NUL instead of a slash
345 * between the directories.
346 */
347 while (*filename) {
348 if (*filename == '/') {
349 filename++;
350 dirv[dirvp++] = s;
351 *s++ = '\0';
352 } else {
353 *s++ = *filename++;
354 }
355 }
356 *s = '\0';
357 dirv[dirvp] = s;
358 if (striplast)
359 dirvp--;
360 if (dirvp < 0)
361 return;
362
363 strcpy(buf, "mkdir");
364 s = buf;
365 for (i = 0; i <= dirvp; i++) {
366 struct stat sbuf;
367
368 if (stat(tmpbuf, &sbuf) && errno == ENOENT) {
369 while (*s)
370 s++;
371 *s++ = ' ';
372 strcpy(s, tmpbuf);
373 }
374 *dirv[i] = '/';
375 }
376 if (s != buf)
377 system(buf);
378 }
379
380 /*
381 * Make filenames more reasonable.
382 */
383 char *
384 fetchname(char *at, int strip_leading, int assume_exists)
385 {
386 char *fullname;
387 char *name;
388 char *t;
389 char tmpbuf[MAXPATHLEN];
390 int sleading = strip_leading;
391
392 if (!at)
393 return NULL;
394 while (isspace((unsigned char)*at))
395 at++;
396 #ifdef DEBUGGING
397 if (debug & 128)
398 say("fetchname %s %d %d\n", at, strip_leading, assume_exists);
399 #endif
400 filename_is_dev_null = FALSE;
401 if (strnEQ(at, "/dev/null", 9)) {
402 /* So files can be created by diffing against /dev/null. */
403 filename_is_dev_null = TRUE;
404 return NULL;
405 }
406 name = fullname = t = xstrdup(at);
407
408 /* Strip off up to `sleading' leading slashes and null terminate. */
409 for (; *t && !isspace((unsigned char)*t); t++)
410 if (*t == '/')
411 if (--sleading >= 0)
412 name = t + 1;
413 *t = '\0';
414
415 /*
416 * If no -p option was given (957 is the default value!),
417 * we were given a relative pathname,
418 * and the leading directories that we just stripped off all exist,
419 * put them back on.
420 */
421 if (strip_leading == 957 && name != fullname && *fullname != '/') {
422 name[-1] = '\0';
423 if (stat(fullname, &filestat) == 0 &&
424 S_ISDIR(filestat.st_mode)) {
425 name[-1] = '/';
426 name = fullname;
427 }
428 }
429
430 name = xstrdup(name);
431 free(fullname);
432
433 if (stat(name, &filestat) && !assume_exists) {
434 char *filebase = basename(name);
435 size_t pathlen = filebase - name;
436
437 /* Put any leading path into `tmpbuf'. */
438 strncpy(tmpbuf, name, pathlen);
439
440 #define try(f, a1, a2) \
441 (Sprintf(tmpbuf + pathlen, f, a1, a2), stat(tmpbuf, &filestat) == 0)
442 #define try1(f, a1) \
443 (Sprintf(tmpbuf + pathlen, f, a1), stat(tmpbuf, &filestat) == 0)
444 if (try("RCS/%s%s", filebase, RCSSUFFIX) ||
445 try1("RCS/%s" , filebase) ||
446 try( "%s%s", filebase, RCSSUFFIX) ||
447 try("SCCS/%s%s", SCCSPREFIX, filebase) ||
448 try( "%s%s", SCCSPREFIX, filebase))
449 return name;
450 free(name);
451 name = NULL;
452 }
453
454 return name;
455 }
456