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