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