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