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