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