xref: /freebsd/usr.bin/patch/util.c (revision afe61c15161c324a7af299a9b8457aba5afc92db)
1 #include "EXTERN.h"
2 #include "common.h"
3 #include "INTERN.h"
4 #include "util.h"
5 
6 /* Rename a file, copying it if necessary. */
7 
8 int
9 move_file(from,to)
10 char *from, *to;
11 {
12     char bakname[512];
13     Reg1 char *s;
14     Reg2 int i;
15     Reg3 int fromfd;
16 
17     /* to stdout? */
18 
19     if (strEQ(to, "-")) {
20 #ifdef DEBUGGING
21 	if (debug & 4)
22 	    say2("Moving %s to stdout.\n", from);
23 #endif
24 	fromfd = open(from, 0);
25 	if (fromfd < 0)
26 	    fatal2("patch: internal error, can't reopen %s\n", from);
27 	while ((i=read(fromfd, buf, sizeof buf)) > 0)
28 	    if (write(1, buf, i) != 1)
29 		fatal1("patch: write failed\n");
30 	Close(fromfd);
31 	return 0;
32     }
33 
34     Strcpy(bakname, to);
35     Strcat(bakname, origext?origext:ORIGEXT);
36     if (stat(to, &filestat) >= 0) {	/* output file exists */
37 	dev_t to_device = filestat.st_dev;
38 	ino_t to_inode  = filestat.st_ino;
39 	char *simplename = bakname;
40 
41 	for (s=bakname; *s; s++) {
42 	    if (*s == '/')
43 		simplename = s+1;
44 	}
45 	/* find a backup name that is not the same file */
46 	while (stat(bakname, &filestat) >= 0 &&
47 		to_device == filestat.st_dev && to_inode == filestat.st_ino) {
48 	    for (s=simplename; *s && !islower(*s); s++) ;
49 	    if (*s)
50 		*s = toupper(*s);
51 	    else
52 		Strcpy(simplename, simplename+1);
53 	}
54 	while (unlink(bakname) >= 0) ;	/* while() is for benefit of Eunice */
55 #ifdef DEBUGGING
56 	if (debug & 4)
57 	    say3("Moving %s to %s.\n", to, bakname);
58 #endif
59 	if (link(to, bakname) < 0) {
60 	    say3("patch: can't backup %s, output is in %s\n",
61 		to, from);
62 	    return -1;
63 	}
64 	while (unlink(to) >= 0) ;
65     }
66 #ifdef DEBUGGING
67     if (debug & 4)
68 	say3("Moving %s to %s.\n", from, to);
69 #endif
70     if (link(from, to) < 0) {		/* different file system? */
71 	Reg4 int tofd;
72 
73 	tofd = creat(to, 0666);
74 	if (tofd < 0) {
75 	    say3("patch: can't create %s, output is in %s.\n",
76 	      to, from);
77 	    return -1;
78 	}
79 	fromfd = open(from, 0);
80 	if (fromfd < 0)
81 	    fatal2("patch: internal error, can't reopen %s\n", from);
82 	while ((i=read(fromfd, buf, sizeof buf)) > 0)
83 	    if (write(tofd, buf, i) != i)
84 		fatal1("patch: write failed\n");
85 	Close(fromfd);
86 	Close(tofd);
87     }
88     Unlink(from);
89     return 0;
90 }
91 
92 /* Copy a file. */
93 
94 void
95 copy_file(from,to)
96 char *from, *to;
97 {
98     Reg3 int tofd;
99     Reg2 int fromfd;
100     Reg1 int i;
101 
102     tofd = creat(to, 0666);
103     if (tofd < 0)
104 	fatal2("patch: can't create %s.\n", to);
105     fromfd = open(from, 0);
106     if (fromfd < 0)
107 	fatal2("patch: internal error, can't reopen %s\n", from);
108     while ((i=read(fromfd, buf, sizeof buf)) > 0)
109 	if (write(tofd, buf, i) != i)
110 	    fatal2("patch: write (%s) failed\n", to);
111     Close(fromfd);
112     Close(tofd);
113 }
114 
115 /* Allocate a unique area for a string. */
116 
117 char *
118 savestr(s)
119 Reg1 char *s;
120 {
121     Reg3 char *rv;
122     Reg2 char *t;
123 
124     if (!s)
125 	s = "Oops";
126     t = s;
127     while (*t++);
128     rv = malloc((MEM) (t - s));
129     if (rv == Nullch) {
130 	if (using_plan_a)
131 	    out_of_mem = TRUE;
132 	else
133 	    fatal1("patch: out of memory (savestr)\n");
134     }
135     else {
136 	t = rv;
137 	while (*t++ = *s++);
138     }
139     return rv;
140 }
141 
142 #if defined(lint) && defined(CANVARARG)
143 
144 /*VARARGS ARGSUSED*/
145 say(pat) char *pat; { ; }
146 /*VARARGS ARGSUSED*/
147 fatal(pat) char *pat; { ; }
148 /*VARARGS ARGSUSED*/
149 ask(pat) char *pat; { ; }
150 
151 #else
152 
153 /* Vanilla terminal output (buffered). */
154 
155 void
156 say(pat,arg1,arg2,arg3)
157 char *pat;
158 int arg1,arg2,arg3;
159 {
160     fprintf(stderr, pat, arg1, arg2, arg3);
161     Fflush(stderr);
162 }
163 
164 /* Terminal output, pun intended. */
165 
166 void				/* very void */
167 fatal(pat,arg1,arg2,arg3)
168 char *pat;
169 int arg1,arg2,arg3;
170 {
171     void my_exit();
172 
173     say(pat, arg1, arg2, arg3);
174     my_exit(1);
175 }
176 
177 /* Get a response from the user, somehow or other. */
178 
179 void
180 ask(pat,arg1,arg2,arg3)
181 char *pat;
182 int arg1,arg2,arg3;
183 {
184     int ttyfd;
185     int r;
186     bool tty2 = isatty(2);
187 
188     Sprintf(buf, pat, arg1, arg2, arg3);
189     Fflush(stderr);
190     write(2, buf, strlen(buf));
191     if (tty2) {				/* might be redirected to a file */
192 	r = read(2, buf, sizeof buf);
193     }
194     else if (isatty(1)) {		/* this may be new file output */
195 	Fflush(stdout);
196 	write(1, buf, strlen(buf));
197 	r = read(1, buf, sizeof buf);
198     }
199     else if ((ttyfd = open("/dev/tty", 2)) >= 0 && isatty(ttyfd)) {
200 					/* might be deleted or unwriteable */
201 	write(ttyfd, buf, strlen(buf));
202 	r = read(ttyfd, buf, sizeof buf);
203 	Close(ttyfd);
204     }
205     else if (isatty(0)) {		/* this is probably patch input */
206 	Fflush(stdin);
207 	write(0, buf, strlen(buf));
208 	r = read(0, buf, sizeof buf);
209     }
210     else {				/* no terminal at all--default it */
211 	buf[0] = '\n';
212 	r = 1;
213     }
214     if (r <= 0)
215 	buf[0] = 0;
216     else
217 	buf[r] = '\0';
218     if (!tty2)
219 	say1(buf);
220 }
221 #endif lint
222 
223 /* How to handle certain events when not in a critical region. */
224 
225 void
226 set_signals()
227 {
228     void my_exit();
229 
230 #ifndef lint
231     if (signal(SIGHUP, SIG_IGN) != SIG_IGN)
232 	Signal(SIGHUP, my_exit);
233     if (signal(SIGINT, SIG_IGN) != SIG_IGN)
234 	Signal(SIGINT, my_exit);
235 #endif
236 }
237 
238 /* How to handle certain events when in a critical region. */
239 
240 void
241 ignore_signals()
242 {
243 #ifndef lint
244     Signal(SIGHUP, SIG_IGN);
245     Signal(SIGINT, SIG_IGN);
246 #endif
247 }
248 
249 /* Make sure we'll have the directories to create a file. */
250 
251 void
252 makedirs(filename,striplast)
253 Reg1 char *filename;
254 bool striplast;
255 {
256     char tmpbuf[256];
257     Reg2 char *s = tmpbuf;
258     char *dirv[20];
259     Reg3 int i;
260     Reg4 int dirvp = 0;
261 
262     while (*filename) {
263 	if (*filename == '/') {
264 	    filename++;
265 	    dirv[dirvp++] = s;
266 	    *s++ = '\0';
267 	}
268 	else {
269 	    *s++ = *filename++;
270 	}
271     }
272     *s = '\0';
273     dirv[dirvp] = s;
274     if (striplast)
275 	dirvp--;
276     if (dirvp < 0)
277 	return;
278     strcpy(buf, "mkdir");
279     s = buf;
280     for (i=0; i<=dirvp; i++) {
281 	while (*s) s++;
282 	*s++ = ' ';
283 	strcpy(s, tmpbuf);
284 	*dirv[i] = '/';
285     }
286     system(buf);
287 }
288 
289 /* Make filenames more reasonable. */
290 
291 char *
292 fetchname(at,strip_leading,assume_exists)
293 char *at;
294 int strip_leading;
295 int assume_exists;
296 {
297     char *s;
298     char *name;
299     Reg1 char *t;
300     char tmpbuf[200];
301 
302     if (!at)
303 	return Nullch;
304     s = savestr(at);
305     for (t=s; isspace(*t); t++) ;
306     name = t;
307 #ifdef DEBUGGING
308     if (debug & 128)
309 	say4("fetchname %s %d %d\n",name,strip_leading,assume_exists);
310 #endif
311     if (strnEQ(name, "/dev/null", 9))	/* so files can be created by diffing */
312 	return Nullch;			/*   against /dev/null. */
313     for (; *t && !isspace(*t); t++)
314 	if (*t == '/')
315 	    if (--strip_leading >= 0)
316 		name = t+1;
317     *t = '\0';
318     if (name != s && *s != '/') {
319 	name[-1] = '\0';
320 	if (stat(s, &filestat) && filestat.st_mode & S_IFDIR) {
321 	    name[-1] = '/';
322 	    name=s;
323 	}
324     }
325     name = savestr(name);
326     Sprintf(tmpbuf, "RCS/%s", name);
327     free(s);
328     if (stat(name, &filestat) < 0 && !assume_exists) {
329 	Strcat(tmpbuf, RCSSUFFIX);
330 	if (stat(tmpbuf, &filestat) < 0 && stat(tmpbuf+4, &filestat) < 0) {
331 	    Sprintf(tmpbuf, "SCCS/%s%s", SCCSPREFIX, name);
332 	    if (stat(tmpbuf, &filestat) < 0 && stat(tmpbuf+5, &filestat) < 0) {
333 		free(name);
334 		name = Nullch;
335 	    }
336 	}
337     }
338     return name;
339 }
340