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