util.c revision 1.3 1 #ifndef lint
2 static char rcsid[] = "$Id: util.c,v 1.3 1993/12/07 10:36:16 mycroft 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