2 * $OpenBSD: util.c,v 1.32 2006/03/11 19:41:30 otto Exp $
3 * $DragonFly: src/usr.bin/patch/util.c,v 1.9 2007/09/29 23:11:10 swildner Exp $
4 * $NetBSD: util.c,v 1.26 2010/10/02 19:31:14 wiz Exp $
8 * patch - a program to apply diffs to original files
10 * Copyright 1986, Larry Wall
12 * Redistribution and use in source and binary forms, with or without
13 * modification, are permitted provided that the following condition is met:
14 * 1. Redistributions of source code must retain the above copyright notice,
15 * this condition and the following disclaimer.
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND ANY
18 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
19 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
20 * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
21 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
23 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
24 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29 * -C option added in 1998, original code by Marc Espie, based on FreeBSD
33 #include <sys/cdefs.h>
34 __RCSID("$NetBSD: util.c,v 1.26 2010/10/02 19:31:14 wiz Exp $");
36 #include <sys/param.h>
53 #include "backupfile.h"
54 #include "pathnames.h"
56 /* Rename a file, copying it if necessary. */
59 move_file(const char *from
, const char *to
)
69 say("Moving %s to stdout.\n", from
);
71 fromfd
= open(from
, O_RDONLY
);
73 pfatal("internal error, can't reopen %s", from
);
74 while ((i
= read(fromfd
, buf
, buf_len
)) > 0)
75 if (write(STDOUT_FILENO
, buf
, i
) != i
)
76 pfatal("write failed");
80 if (backup_file(to
) < 0) {
81 say("Can't backup %s, output is in %s: %s\n", to
, from
,
87 say("Moving %s to %s.\n", from
, to
);
89 if (rename(from
, to
) < 0) {
90 if (errno
!= EXDEV
|| copy_file(from
, to
) < 0) {
91 say("Can't create %s, output is in %s: %s\n",
92 to
, from
, strerror(errno
));
99 /* Backup the original file. */
102 backup_file(const char *orig
)
104 struct stat filestat
;
105 char bakname
[MAXPATHLEN
], *s
, *simplename
;
109 if (backup_type
== none
|| stat(orig
, &filestat
) != 0)
110 return 0; /* nothing to do */
112 * If the user used zero prefixes or suffixes, then
113 * he doesn't want backups. Yet we have to remove
114 * orig to break possible hardlinks.
116 if ((origprae
&& *origprae
== 0) || *simple_backup_suffix
== 0) {
120 orig_device
= filestat
.st_dev
;
121 orig_inode
= filestat
.st_ino
;
124 if (strlcpy(bakname
, origprae
, sizeof(bakname
)) >= sizeof(bakname
) ||
125 strlcat(bakname
, orig
, sizeof(bakname
)) >= sizeof(bakname
))
126 fatal("filename %s too long for buffer\n", origprae
);
128 if ((s
= find_backup_file_name(orig
)) == NULL
)
129 fatal("out of memory\n");
130 if (strlcpy(bakname
, s
, sizeof(bakname
)) >= sizeof(bakname
))
131 fatal("filename %s too long for buffer\n", s
);
135 if ((simplename
= strrchr(bakname
, '/')) != NULL
)
136 simplename
= simplename
+ 1;
138 simplename
= bakname
;
141 * Find a backup name that is not the same file. Change the
142 * first lowercase char into uppercase; if that isn't
143 * sufficient, chop off the first char and try again.
145 while (stat(bakname
, &filestat
) == 0 &&
146 orig_device
== filestat
.st_dev
&& orig_inode
== filestat
.st_ino
) {
147 /* Skip initial non-lowercase chars. */
148 for (s
= simplename
; *s
&& !islower((unsigned char)*s
); s
++)
151 *s
= toupper((unsigned char)*s
);
153 memmove(simplename
, simplename
+ 1,
154 strlen(simplename
+ 1) + 1);
158 say("Moving %s to %s.\n", orig
, bakname
);
160 if (rename(orig
, bakname
) < 0) {
161 if (errno
!= EXDEV
|| copy_file(orig
, bakname
) < 0)
171 copy_file(const char *from
, const char *to
)
176 tofd
= open(to
, O_CREAT
|O_TRUNC
|O_WRONLY
, 0666);
179 fromfd
= open(from
, O_RDONLY
, 0);
181 pfatal("internal error, can't reopen %s", from
);
182 while ((i
= read(fromfd
, buf
, buf_len
)) > 0)
183 if (write(tofd
, buf
, i
) != i
)
184 pfatal("write to %s failed", to
);
191 * Allocate a unique area for a string.
194 savestr(const char *s
)
205 fatal("out of memory\n");
211 * Vanilla terminal output (buffered).
214 say(const char *fmt
, ...)
219 vfprintf(stderr
, fmt
, ap
);
225 * Terminal output, pun intended.
228 fatal(const char *fmt
, ...)
233 fprintf(stderr
, "patch: **** ");
234 vfprintf(stderr
, fmt
, ap
);
240 * Say something from patch, something from the system, then silence . . .
243 pfatal(const char *fmt
, ...)
248 fprintf(stderr
, "patch: **** ");
250 vfprintf(stderr
, fmt
, ap
);
252 fprintf(stderr
, ": %s\n", strerror(errnum
));
257 * Get a response from the user via /dev/tty
260 ask(const char *fmt
, ...)
264 static int ttyfd
= -1;
267 vfprintf(stdout
, fmt
, ap
);
271 ttyfd
= open(_PATH_TTY
, O_RDONLY
);
273 if ((nr
= read(ttyfd
, buf
, buf_len
)) > 0 &&
277 if (ttyfd
< 0 || nr
<= 0) {
278 /* no tty or error reading, pretend user entered 'return' */
285 * How to handle certain events when not in a critical region.
288 set_signals(int reset
)
290 static sig_t hupval
, intval
;
293 hupval
= signal(SIGHUP
, SIG_IGN
);
294 if (hupval
!= SIG_IGN
)
296 intval
= signal(SIGINT
, SIG_IGN
);
297 if (intval
!= SIG_IGN
)
300 signal(SIGHUP
, hupval
);
301 signal(SIGINT
, intval
);
305 * How to handle certain events when in a critical region.
310 signal(SIGHUP
, SIG_IGN
);
311 signal(SIGINT
, SIG_IGN
);
315 * Make sure we'll have the directories to create a file. If `striplast' is
316 * true, ignore the last element of `filename'.
320 makedirs(const char *filename
, bool striplast
)
324 if ((tmpbuf
= strdup(filename
)) == NULL
)
325 fatal("out of memory\n");
328 char *s
= strrchr(tmpbuf
, '/');
331 return; /* nothing to be done */
335 if (mkpath(tmpbuf
) != 0)
336 pfatal("creation of %s failed", tmpbuf
);
341 * Make filenames more reasonable.
344 fetchname(const char *at
, bool *exists
, int strip_leading
)
346 char *fullname
, *name
, *t
;
348 struct stat filestat
;
350 if (at
== NULL
|| *at
== '\0')
352 while (isspace((unsigned char)*at
))
356 say("fetchname %s %d\n", at
, strip_leading
);
358 /* So files can be created by diffing against /dev/null. */
359 if (strnEQ(at
, _PATH_DEVNULL
, sizeof(_PATH_DEVNULL
) - 1))
361 name
= fullname
= t
= savestr(at
);
363 tab
= strchr(t
, '\t') != NULL
;
364 /* Strip off up to `strip_leading' path components and NUL terminate. */
365 for (sleading
= strip_leading
; *t
!= '\0' && ((tab
&& *t
!= '\t') ||
366 !isspace((unsigned char)*t
)); t
++) {
367 if (t
[0] == '/' && t
[1] != '/' && t
[1] != '\0')
374 * If no -p option was given (957 is the default value!), we were
375 * given a relative pathname, and the leading directories that we
376 * just stripped off all exist, put them back on.
378 if (strip_leading
== 957 && name
!= fullname
&& *fullname
!= '/') {
380 if (stat(fullname
, &filestat
) == 0 && S_ISDIR(filestat
.st_mode
)) {
385 name
= savestr(name
);
388 *exists
= stat(name
, &filestat
) == 0;
393 * Takes the name returned by fetchname and looks in RCS/SCCS directories
394 * for a checked in version.
397 checked_in(char *file
)
399 char *filebase
, *filedir
, tmpbuf
[MAXPATHLEN
];
400 struct stat filestat
;
402 filebase
= basename(file
);
403 filedir
= dirname(file
);
405 #define try(f, a1, a2, a3) \
406 (snprintf(tmpbuf, sizeof tmpbuf, f, a1, a2, a3), stat(tmpbuf, &filestat) == 0)
408 if (try("%s/RCS/%s%s", filedir
, filebase
, RCSSUFFIX
) ||
409 try("%s/RCS/%s%s", filedir
, filebase
, "") ||
410 try("%s/%s%s", filedir
, filebase
, RCSSUFFIX
) ||
411 try("%s/SCCS/%s%s", filedir
, SCCSPREFIX
, filebase
) ||
412 try("%s/%s%s", filedir
, SCCSPREFIX
, filebase
))
421 printf("Patch version 2.0-12u8-NetBSD\n");
422 my_exit(EXIT_SUCCESS
);