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