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