util.c revision 1.24 1 1.24 joerg /*
2 1.24 joerg * $OpenBSD: util.c,v 1.32 2006/03/11 19:41:30 otto Exp $
3 1.24 joerg * $DragonFly: src/usr.bin/patch/util.c,v 1.9 2007/09/29 23:11:10 swildner Exp $
4 1.24 joerg * $NetBSD: util.c,v 1.24 2008/09/19 18:33:34 joerg Exp $
5 1.24 joerg */
6 1.16 itojun
7 1.16 itojun /*
8 1.24 joerg * patch - a program to apply diffs to original files
9 1.24 joerg *
10 1.24 joerg * Copyright 1986, Larry Wall
11 1.24 joerg *
12 1.16 itojun * Redistribution and use in source and binary forms, with or without
13 1.24 joerg * modification, are permitted provided that the following condition is met:
14 1.24 joerg * 1. Redistributions of source code must retain the above copyright notice,
15 1.24 joerg * this condition and the following disclaimer.
16 1.24 joerg *
17 1.24 joerg * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND ANY
18 1.24 joerg * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
19 1.24 joerg * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
20 1.24 joerg * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
21 1.24 joerg * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22 1.24 joerg * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
23 1.24 joerg * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
24 1.24 joerg * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25 1.16 itojun * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26 1.16 itojun * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27 1.16 itojun * SUCH DAMAGE.
28 1.24 joerg *
29 1.24 joerg * -C option added in 1998, original code by Marc Espie, based on FreeBSD
30 1.24 joerg * behaviour
31 1.16 itojun */
32 1.16 itojun
33 1.5 christos #include <sys/cdefs.h>
34 1.24 joerg __RCSID("$NetBSD: util.c,v 1.24 2008/09/19 18:33:34 joerg Exp $");
35 1.2 mycroft
36 1.12 kristerw #include <sys/param.h>
37 1.24 joerg #include <sys/stat.h>
38 1.24 joerg
39 1.24 joerg #include <ctype.h>
40 1.24 joerg #include <errno.h>
41 1.24 joerg #include <fcntl.h>
42 1.24 joerg #include <libgen.h>
43 1.24 joerg #include <paths.h>
44 1.24 joerg #include <signal.h>
45 1.24 joerg #include <stdarg.h>
46 1.24 joerg #include <stdlib.h>
47 1.24 joerg #include <stdio.h>
48 1.24 joerg #include <string.h>
49 1.24 joerg #include <unistd.h>
50 1.12 kristerw
51 1.1 cgd #include "common.h"
52 1.1 cgd #include "util.h"
53 1.1 cgd #include "backupfile.h"
54 1.24 joerg #include "pathnames.h"
55 1.10 kristerw
56 1.24 joerg /* Rename a file, copying it if necessary. */
57 1.1 cgd
58 1.1 cgd int
59 1.24 joerg move_file(const char *from, const char *to)
60 1.1 cgd {
61 1.24 joerg int fromfd;
62 1.24 joerg ssize_t i;
63 1.21 skd
64 1.11 kristerw /* to stdout? */
65 1.1 cgd
66 1.11 kristerw if (strEQ(to, "-")) {
67 1.1 cgd #ifdef DEBUGGING
68 1.11 kristerw if (debug & 4)
69 1.11 kristerw say("Moving %s to stdout.\n", from);
70 1.1 cgd #endif
71 1.24 joerg fromfd = open(from, O_RDONLY);
72 1.11 kristerw if (fromfd < 0)
73 1.11 kristerw pfatal("internal error, can't reopen %s", from);
74 1.24 joerg while ((i = read(fromfd, buf, buf_len)) > 0)
75 1.24 joerg if (write(STDOUT_FILENO, buf, i) != i)
76 1.11 kristerw pfatal("write failed");
77 1.24 joerg close(fromfd);
78 1.11 kristerw return 0;
79 1.11 kristerw }
80 1.24 joerg if (backup_file(to) < 0) {
81 1.24 joerg say("Can't backup %s, output is in %s: %s\n", to, from,
82 1.24 joerg strerror(errno));
83 1.24 joerg return -1;
84 1.1 cgd }
85 1.1 cgd #ifdef DEBUGGING
86 1.1 cgd if (debug & 4)
87 1.11 kristerw say("Moving %s to %s.\n", from, to);
88 1.1 cgd #endif
89 1.24 joerg if (rename(from, to) < 0) {
90 1.24 joerg if (errno != EXDEV || copy_file(from, to) < 0) {
91 1.11 kristerw say("Can't create %s, output is in %s: %s\n",
92 1.11 kristerw to, from, strerror(errno));
93 1.11 kristerw return -1;
94 1.11 kristerw }
95 1.1 cgd }
96 1.24 joerg return 0;
97 1.24 joerg }
98 1.24 joerg
99 1.24 joerg /* Backup the original file. */
100 1.24 joerg
101 1.24 joerg int
102 1.24 joerg backup_file(const char *orig)
103 1.24 joerg {
104 1.24 joerg struct stat filestat;
105 1.24 joerg char bakname[MAXPATHLEN], *s, *simplename;
106 1.24 joerg dev_t orig_device;
107 1.24 joerg ino_t orig_inode;
108 1.24 joerg
109 1.24 joerg if (backup_type == none || stat(orig, &filestat) != 0)
110 1.24 joerg return 0; /* nothing to do */
111 1.24 joerg /*
112 1.24 joerg * If the user used zero prefixes or suffixes, then
113 1.24 joerg * he doesn't want backups. Yet we have to remove
114 1.24 joerg * orig to break possible hardlinks.
115 1.24 joerg */
116 1.24 joerg if ((origprae && *origprae == 0) || *simple_backup_suffix == 0) {
117 1.24 joerg unlink(orig);
118 1.24 joerg return 0;
119 1.24 joerg }
120 1.24 joerg orig_device = filestat.st_dev;
121 1.24 joerg orig_inode = filestat.st_ino;
122 1.24 joerg
123 1.24 joerg if (origprae) {
124 1.24 joerg if (strlcpy(bakname, origprae, sizeof(bakname)) >= sizeof(bakname) ||
125 1.24 joerg strlcat(bakname, orig, sizeof(bakname)) >= sizeof(bakname))
126 1.24 joerg fatal("filename %s too long for buffer\n", origprae);
127 1.24 joerg } else {
128 1.24 joerg if ((s = find_backup_file_name(orig)) == NULL)
129 1.24 joerg fatal("out of memory\n");
130 1.24 joerg if (strlcpy(bakname, s, sizeof(bakname)) >= sizeof(bakname))
131 1.24 joerg fatal("filename %s too long for buffer\n", s);
132 1.24 joerg free(s);
133 1.24 joerg }
134 1.24 joerg
135 1.24 joerg if ((simplename = strrchr(bakname, '/')) != NULL)
136 1.24 joerg simplename = simplename + 1;
137 1.24 joerg else
138 1.24 joerg simplename = bakname;
139 1.24 joerg
140 1.24 joerg /*
141 1.24 joerg * Find a backup name that is not the same file. Change the
142 1.24 joerg * first lowercase char into uppercase; if that isn't
143 1.24 joerg * sufficient, chop off the first char and try again.
144 1.24 joerg */
145 1.24 joerg while (stat(bakname, &filestat) == 0 &&
146 1.24 joerg orig_device == filestat.st_dev && orig_inode == filestat.st_ino) {
147 1.24 joerg /* Skip initial non-lowercase chars. */
148 1.24 joerg for (s = simplename; *s && !islower((unsigned char)*s); s++)
149 1.24 joerg ;
150 1.24 joerg if (*s)
151 1.24 joerg *s = toupper((unsigned char)*s);
152 1.24 joerg else
153 1.24 joerg memmove(simplename, simplename + 1,
154 1.24 joerg strlen(simplename + 1) + 1);
155 1.24 joerg }
156 1.24 joerg #ifdef DEBUGGING
157 1.24 joerg if (debug & 4)
158 1.24 joerg say("Moving %s to %s.\n", orig, bakname);
159 1.24 joerg #endif
160 1.24 joerg if (rename(orig, bakname) < 0) {
161 1.24 joerg if (errno != EXDEV || copy_file(orig, bakname) < 0)
162 1.24 joerg return -1;
163 1.24 joerg }
164 1.11 kristerw return 0;
165 1.11 kristerw }
166 1.11 kristerw
167 1.11 kristerw /*
168 1.11 kristerw * Copy a file.
169 1.11 kristerw */
170 1.24 joerg int
171 1.24 joerg copy_file(const char *from, const char *to)
172 1.11 kristerw {
173 1.24 joerg int tofd, fromfd;
174 1.24 joerg ssize_t i;
175 1.11 kristerw
176 1.24 joerg tofd = open(to, O_CREAT|O_TRUNC|O_WRONLY, 0666);
177 1.11 kristerw if (tofd < 0)
178 1.24 joerg return -1;
179 1.24 joerg fromfd = open(from, O_RDONLY, 0);
180 1.1 cgd if (fromfd < 0)
181 1.11 kristerw pfatal("internal error, can't reopen %s", from);
182 1.24 joerg while ((i = read(fromfd, buf, buf_len)) > 0)
183 1.11 kristerw if (write(tofd, buf, i) != i)
184 1.11 kristerw pfatal("write to %s failed", to);
185 1.24 joerg close(fromfd);
186 1.24 joerg close(tofd);
187 1.24 joerg return 0;
188 1.12 kristerw }
189 1.12 kristerw
190 1.12 kristerw /*
191 1.24 joerg * Allocate a unique area for a string.
192 1.11 kristerw */
193 1.1 cgd char *
194 1.24 joerg savestr(const char *s)
195 1.1 cgd {
196 1.24 joerg char *rv;
197 1.1 cgd
198 1.24 joerg if (!s)
199 1.24 joerg s = "Oops";
200 1.24 joerg rv = strdup(s);
201 1.24 joerg if (rv == NULL) {
202 1.24 joerg if (using_plan_a)
203 1.24 joerg out_of_mem = true;
204 1.24 joerg else
205 1.24 joerg fatal("out of memory\n");
206 1.24 joerg }
207 1.24 joerg return rv;
208 1.1 cgd }
209 1.1 cgd
210 1.11 kristerw /*
211 1.24 joerg * Vanilla terminal output (buffered).
212 1.11 kristerw */
213 1.1 cgd void
214 1.24 joerg say(const char *fmt, ...)
215 1.1 cgd {
216 1.24 joerg va_list ap;
217 1.24 joerg
218 1.24 joerg va_start(ap, fmt);
219 1.24 joerg vfprintf(stderr, fmt, ap);
220 1.11 kristerw va_end(ap);
221 1.24 joerg fflush(stderr);
222 1.1 cgd }
223 1.1 cgd
224 1.11 kristerw /*
225 1.11 kristerw * Terminal output, pun intended.
226 1.11 kristerw */
227 1.24 joerg void
228 1.24 joerg fatal(const char *fmt, ...)
229 1.1 cgd {
230 1.24 joerg va_list ap;
231 1.24 joerg
232 1.24 joerg va_start(ap, fmt);
233 1.11 kristerw fprintf(stderr, "patch: **** ");
234 1.24 joerg vfprintf(stderr, fmt, ap);
235 1.11 kristerw va_end(ap);
236 1.24 joerg my_exit(2);
237 1.1 cgd }
238 1.1 cgd
239 1.11 kristerw /*
240 1.24 joerg * Say something from patch, something from the system, then silence . . .
241 1.11 kristerw */
242 1.24 joerg void
243 1.24 joerg pfatal(const char *fmt, ...)
244 1.1 cgd {
245 1.24 joerg va_list ap;
246 1.24 joerg int errnum = errno;
247 1.24 joerg
248 1.11 kristerw fprintf(stderr, "patch: **** ");
249 1.24 joerg va_start(ap, fmt);
250 1.24 joerg vfprintf(stderr, fmt, ap);
251 1.24 joerg va_end(ap);
252 1.11 kristerw fprintf(stderr, ": %s\n", strerror(errnum));
253 1.24 joerg my_exit(2);
254 1.1 cgd }
255 1.1 cgd
256 1.11 kristerw /*
257 1.24 joerg * Get a response from the user via /dev/tty
258 1.11 kristerw */
259 1.1 cgd void
260 1.24 joerg ask(const char *fmt, ...)
261 1.1 cgd {
262 1.24 joerg va_list ap;
263 1.24 joerg ssize_t nr = 0;
264 1.24 joerg static int ttyfd = -1;
265 1.11 kristerw
266 1.24 joerg va_start(ap, fmt);
267 1.24 joerg vfprintf(stdout, fmt, ap);
268 1.11 kristerw va_end(ap);
269 1.24 joerg fflush(stdout);
270 1.24 joerg if (ttyfd < 0)
271 1.24 joerg ttyfd = open(_PATH_TTY, O_RDONLY);
272 1.24 joerg if (ttyfd >= 0) {
273 1.24 joerg if ((nr = read(ttyfd, buf, buf_len)) > 0 &&
274 1.24 joerg buf[nr - 1] == '\n')
275 1.24 joerg buf[nr - 1] = '\0';
276 1.24 joerg }
277 1.24 joerg if (ttyfd < 0 || nr <= 0) {
278 1.24 joerg /* no tty or error reading, pretend user entered 'return' */
279 1.24 joerg putchar('\n');
280 1.24 joerg buf[0] = '\0';
281 1.11 kristerw }
282 1.1 cgd }
283 1.1 cgd
284 1.11 kristerw /*
285 1.11 kristerw * How to handle certain events when not in a critical region.
286 1.11 kristerw */
287 1.1 cgd void
288 1.9 kristerw set_signals(int reset)
289 1.1 cgd {
290 1.24 joerg static sig_t hupval, intval;
291 1.1 cgd
292 1.11 kristerw if (!reset) {
293 1.11 kristerw hupval = signal(SIGHUP, SIG_IGN);
294 1.11 kristerw if (hupval != SIG_IGN)
295 1.24 joerg hupval = my_exit;
296 1.11 kristerw intval = signal(SIGINT, SIG_IGN);
297 1.11 kristerw if (intval != SIG_IGN)
298 1.24 joerg intval = my_exit;
299 1.11 kristerw }
300 1.24 joerg signal(SIGHUP, hupval);
301 1.24 joerg signal(SIGINT, intval);
302 1.1 cgd }
303 1.1 cgd
304 1.11 kristerw /*
305 1.11 kristerw * How to handle certain events when in a critical region.
306 1.11 kristerw */
307 1.1 cgd void
308 1.24 joerg ignore_signals(void)
309 1.1 cgd {
310 1.24 joerg signal(SIGHUP, SIG_IGN);
311 1.24 joerg signal(SIGINT, SIG_IGN);
312 1.1 cgd }
313 1.1 cgd
314 1.11 kristerw /*
315 1.24 joerg * Make sure we'll have the directories to create a file. If `striplast' is
316 1.24 joerg * true, ignore the last element of `filename'.
317 1.11 kristerw */
318 1.24 joerg
319 1.1 cgd void
320 1.24 joerg makedirs(const char *filename, bool striplast)
321 1.1 cgd {
322 1.24 joerg char *tmpbuf;
323 1.24 joerg
324 1.24 joerg if ((tmpbuf = strdup(filename)) == NULL)
325 1.24 joerg fatal("out of memory\n");
326 1.11 kristerw
327 1.24 joerg if (striplast) {
328 1.24 joerg char *s = strrchr(tmpbuf, '/');
329 1.24 joerg if (s == NULL)
330 1.24 joerg return; /* nothing to be done */
331 1.24 joerg *s = '\0';
332 1.24 joerg }
333 1.24 joerg if (mkpath(tmpbuf) != 0)
334 1.24 joerg pfatal("creation of %s failed", tmpbuf);
335 1.24 joerg free(tmpbuf);
336 1.1 cgd }
337 1.1 cgd
338 1.11 kristerw /*
339 1.11 kristerw * Make filenames more reasonable.
340 1.11 kristerw */
341 1.1 cgd char *
342 1.24 joerg fetchname(const char *at, bool *exists, int strip_leading)
343 1.1 cgd {
344 1.24 joerg char *fullname, *name, *t;
345 1.24 joerg int sleading, tab;
346 1.24 joerg struct stat filestat;
347 1.11 kristerw
348 1.24 joerg if (at == NULL || *at == '\0')
349 1.11 kristerw return NULL;
350 1.11 kristerw while (isspace((unsigned char)*at))
351 1.11 kristerw at++;
352 1.1 cgd #ifdef DEBUGGING
353 1.11 kristerw if (debug & 128)
354 1.24 joerg say("fetchname %s %d\n", at, strip_leading);
355 1.1 cgd #endif
356 1.24 joerg /* So files can be created by diffing against /dev/null. */
357 1.24 joerg if (strnEQ(at, _PATH_DEVNULL, sizeof(_PATH_DEVNULL) - 1))
358 1.11 kristerw return NULL;
359 1.24 joerg name = fullname = t = savestr(at);
360 1.11 kristerw
361 1.24 joerg tab = strchr(t, '\t') != NULL;
362 1.24 joerg /* Strip off up to `strip_leading' path components and NUL terminate. */
363 1.24 joerg for (sleading = strip_leading; *t != '\0' && ((tab && *t != '\t') ||
364 1.24 joerg !isspace((unsigned char)*t)); t++) {
365 1.24 joerg if (t[0] == '/' && t[1] != '/' && t[1] != '\0')
366 1.11 kristerw if (--sleading >= 0)
367 1.11 kristerw name = t + 1;
368 1.24 joerg }
369 1.11 kristerw *t = '\0';
370 1.11 kristerw
371 1.11 kristerw /*
372 1.24 joerg * If no -p option was given (957 is the default value!), we were
373 1.24 joerg * given a relative pathname, and the leading directories that we
374 1.24 joerg * just stripped off all exist, put them back on.
375 1.11 kristerw */
376 1.11 kristerw if (strip_leading == 957 && name != fullname && *fullname != '/') {
377 1.11 kristerw name[-1] = '\0';
378 1.24 joerg if (stat(fullname, &filestat) == 0 && S_ISDIR(filestat.st_mode)) {
379 1.11 kristerw name[-1] = '/';
380 1.11 kristerw name = fullname;
381 1.11 kristerw }
382 1.11 kristerw }
383 1.24 joerg name = savestr(name);
384 1.24 joerg free(fullname);
385 1.24 joerg
386 1.24 joerg *exists = stat(name, &filestat) == 0;
387 1.24 joerg return name;
388 1.24 joerg }
389 1.24 joerg
390 1.24 joerg /*
391 1.24 joerg * Takes the name returned by fetchname and looks in RCS/SCCS directories
392 1.24 joerg * for a checked in version.
393 1.24 joerg */
394 1.24 joerg char *
395 1.24 joerg checked_in(char *file)
396 1.24 joerg {
397 1.24 joerg char *filebase, *filedir, tmpbuf[MAXPATHLEN];
398 1.24 joerg struct stat filestat;
399 1.11 kristerw
400 1.24 joerg filebase = basename(file);
401 1.24 joerg filedir = dirname(file);
402 1.24 joerg
403 1.24 joerg #define try(f, a1, a2, a3) \
404 1.24 joerg (snprintf(tmpbuf, sizeof tmpbuf, f, a1, a2, a3), stat(tmpbuf, &filestat) == 0)
405 1.24 joerg
406 1.24 joerg if (try("%s/RCS/%s%s", filedir, filebase, RCSSUFFIX) ||
407 1.24 joerg try("%s/RCS/%s%s", filedir, filebase, "") ||
408 1.24 joerg try("%s/%s%s", filedir, filebase, RCSSUFFIX) ||
409 1.24 joerg try("%s/SCCS/%s%s", filedir, SCCSPREFIX, filebase) ||
410 1.24 joerg try("%s/%s%s", filedir, SCCSPREFIX, filebase))
411 1.24 joerg return file;
412 1.24 joerg
413 1.24 joerg return NULL;
414 1.24 joerg }
415 1.11 kristerw
416 1.24 joerg void
417 1.24 joerg version(void)
418 1.24 joerg {
419 1.24 joerg fprintf(stderr, "Patch version 2.0-12u8-NetBSD\n");
420 1.24 joerg my_exit(EXIT_SUCCESS);
421 1.24 joerg }
422 1.1 cgd
423 1.24 joerg /*
424 1.24 joerg * Exit with cleanup.
425 1.24 joerg */
426 1.24 joerg void
427 1.24 joerg my_exit(int status)
428 1.24 joerg {
429 1.24 joerg unlink(TMPINNAME);
430 1.24 joerg if (!toutkeep)
431 1.24 joerg unlink(TMPOUTNAME);
432 1.24 joerg if (!trejkeep)
433 1.24 joerg unlink(TMPREJNAME);
434 1.24 joerg unlink(TMPPATNAME);
435 1.24 joerg exit(status);
436 1.1 cgd }
437