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