2 * $OpenBSD: util.c,v 1.29 2004/11/19 20:00:57 otto Exp $
3 * $DragonFly: src/usr.bin/patch/util.c,v 1.8 2006/04/18 22:11:35 joerg Exp $
7 * patch - a program to apply diffs to original files
9 * Copyright 1986, Larry Wall
11 * Redistribution and use in source and binary forms, with or without
12 * modification, are permitted provided that the following condition is met:
13 * 1. Redistributions of source code must retain the above copyright notice,
14 * this condition and the following disclaimer.
16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND ANY
17 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19 * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
20 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
22 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
23 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28 * -C option added in 1998, original code by Marc Espie, based on FreeBSD
32 #include <sys/param.h>
48 #include "backupfile.h"
49 #include "pathnames.h"
52 /* Rename a file, copying it if necessary. */
55 move_file(const char *from
, const char *to
)
65 say("Moving %s to stdout.\n", from
);
67 fromfd
= open(from
, O_RDONLY
);
69 pfatal("internal error, can't reopen %s", from
);
70 while ((i
= read(fromfd
, buf
, buf_len
)) > 0)
71 if (write(STDOUT_FILENO
, buf
, i
) != i
)
72 pfatal("write failed");
76 if (backup_file(to
) < 0) {
77 say("Can't backup %s, output is in %s: %s\n", to
, from
,
83 say("Moving %s to %s.\n", from
, to
);
85 if (rename(from
, to
) < 0) {
86 if (errno
!= EXDEV
|| copy_file(from
, to
) < 0) {
87 say("Can't create %s, output is in %s: %s\n",
88 to
, from
, strerror(errno
));
95 /* Backup the original file. */
98 backup_file(const char *orig
)
100 struct stat filestat
;
101 char bakname
[MAXPATHLEN
], *s
, *simplename
;
105 if (backup_type
== none
|| stat(orig
, &filestat
) != 0)
106 return 0; /* nothing to do */
108 * If the user used zero prefixes or suffixes, then
109 * he doesn't want backups. Yet we have to remove
110 * orig to break possible hardlinks.
112 if ((origprae
&& *origprae
== 0) || *simple_backup_suffix
== 0) {
116 orig_device
= filestat
.st_dev
;
117 orig_inode
= filestat
.st_ino
;
120 if (strlcpy(bakname
, origprae
, sizeof(bakname
)) >= sizeof(bakname
) ||
121 strlcat(bakname
, orig
, sizeof(bakname
)) >= sizeof(bakname
))
122 fatal("filename %s too long for buffer\n", origprae
);
124 if ((s
= find_backup_file_name(orig
)) == NULL
)
125 fatal("out of memory\n");
126 if (strlcpy(bakname
, s
, sizeof(bakname
)) >= sizeof(bakname
))
127 fatal("filename %s too long for buffer\n", s
);
131 if ((simplename
= strrchr(bakname
, '/')) != NULL
)
132 simplename
= simplename
+ 1;
134 simplename
= bakname
;
137 * Find a backup name that is not the same file. Change the
138 * first lowercase char into uppercase; if that isn't
139 * sufficient, chop off the first char and try again.
141 while (stat(bakname
, &filestat
) == 0 &&
142 orig_device
== filestat
.st_dev
&& orig_inode
== filestat
.st_ino
) {
143 /* Skip initial non-lowercase chars. */
144 for (s
= simplename
; *s
&& !islower((unsigned char)*s
); s
++)
147 *s
= toupper((unsigned char)*s
);
149 memmove(simplename
, simplename
+ 1,
150 strlen(simplename
+ 1) + 1);
154 say("Moving %s to %s.\n", orig
, bakname
);
156 if (rename(orig
, bakname
) < 0) {
157 if (errno
!= EXDEV
|| copy_file(orig
, bakname
) < 0)
167 copy_file(const char *from
, const char *to
)
172 tofd
= open(to
, O_CREAT
|O_TRUNC
|O_WRONLY
, 0666);
175 fromfd
= open(from
, O_RDONLY
, 0);
177 pfatal("internal error, can't reopen %s", from
);
178 while ((i
= read(fromfd
, buf
, buf_len
)) > 0)
179 if (write(tofd
, buf
, i
) != i
)
180 pfatal("write to %s failed", to
);
187 * Allocate a unique area for a string.
190 savestr(const char *s
)
201 fatal("out of memory\n");
207 * Vanilla terminal output (buffered).
210 say(const char *fmt
, ...)
215 vfprintf(stderr
, fmt
, ap
);
221 * Terminal output, pun intended.
224 fatal(const char *fmt
, ...)
229 fprintf(stderr
, "patch: **** ");
230 vfprintf(stderr
, fmt
, ap
);
236 * Say something from patch, something from the system, then silence . . .
239 pfatal(const char *fmt
, ...)
244 fprintf(stderr
, "patch: **** ");
246 vfprintf(stderr
, fmt
, ap
);
248 fprintf(stderr
, ": %s\n", strerror(errnum
));
253 * Get a response from the user via /dev/tty
256 ask(const char *fmt
, ...)
260 static int ttyfd
= -1;
263 vfprintf(stdout
, fmt
, ap
);
267 ttyfd
= open(_PATH_TTY
, O_RDONLY
);
269 if ((nr
= read(ttyfd
, buf
, buf_len
)) > 0 &&
273 if (ttyfd
< 0 || nr
<= 0) {
274 /* no tty or error reading, pretend user entered 'return' */
281 * How to handle certain events when not in a critical region.
284 set_signals(int reset
)
286 static sig_t hupval
, intval
;
289 hupval
= signal(SIGHUP
, SIG_IGN
);
290 if (hupval
!= SIG_IGN
)
292 intval
= signal(SIGINT
, SIG_IGN
);
293 if (intval
!= SIG_IGN
)
296 signal(SIGHUP
, hupval
);
297 signal(SIGINT
, intval
);
301 * How to handle certain events when in a critical region.
306 signal(SIGHUP
, SIG_IGN
);
307 signal(SIGINT
, SIG_IGN
);
311 * Make sure we'll have the directories to create a file. If `striplast' is
312 * true, ignore the last element of `filename'.
316 makedirs(const char *filename
, bool striplast
)
320 if ((tmpbuf
= strdup(filename
)) == NULL
)
321 fatal("out of memory\n");
324 char *s
= strrchr(tmpbuf
, '/');
326 return; /* nothing to be done */
329 if (snprintf(buf
, buf_len
, "%s -p %s", _PATH_MKDIR
, tmpbuf
)
331 fatal("buffer too small to hold %.20s...\n", tmpbuf
);
334 pfatal("%.40s failed", buf
);
338 * Make filenames more reasonable.
341 fetchname(const char *at
, bool *exists
, int strip_leading
)
343 char *fullname
, *name
, *t
;
345 struct stat filestat
;
347 if (at
== NULL
|| *at
== '\0')
349 while (isspace((unsigned char)*at
))
353 say("fetchname %s %d\n", at
, strip_leading
);
355 /* So files can be created by diffing against /dev/null. */
356 if (strnEQ(at
, _PATH_DEVNULL
, sizeof(_PATH_DEVNULL
) - 1))
358 name
= fullname
= t
= savestr(at
);
360 tab
= strchr(t
, '\t') != NULL
;
361 /* Strip off up to `strip_leading' path components and NUL terminate. */
362 for (sleading
= strip_leading
; *t
!= '\0' && ((tab
&& *t
!= '\t') ||
363 !isspace((unsigned char)*t
)); t
++) {
364 if (t
[0] == '/' && t
[1] != '/' && t
[1] != '\0')
371 * If no -p option was given (957 is the default value!), we were
372 * given a relative pathname, and the leading directories that we
373 * just stripped off all exist, put them back on.
375 if (strip_leading
== 957 && name
!= fullname
&& *fullname
!= '/') {
377 if (stat(fullname
, &filestat
) == 0 && S_ISDIR(filestat
.st_mode
)) {
382 name
= savestr(name
);
385 *exists
= stat(name
, &filestat
) == 0;
390 * Takes the name returned by fetchname and looks in RCS/SCCS directories
391 * for a checked in version.
394 checked_in(char *file
)
396 char *filebase
, *filedir
, tmpbuf
[MAXPATHLEN
];
397 struct stat filestat
;
399 filebase
= basename(file
);
400 filedir
= dirname(file
);
402 #define try(f, a1, a2, a3) \
403 (snprintf(tmpbuf, sizeof tmpbuf, f, a1, a2, a3), stat(tmpbuf, &filestat) == 0)
405 if (try("%s/RCS/%s%s", filedir
, filebase
, RCSSUFFIX
) ||
406 try("%s/RCS/%s%s", filedir
, filebase
, "") ||
407 try("%s/%s%s", filedir
, filebase
, RCSSUFFIX
) ||
408 try("%s/SCCS/%s%s", filedir
, SCCSPREFIX
, filebase
) ||
409 try("%s/%s%s", filedir
, SCCSPREFIX
, filebase
))
418 fprintf(stderr
, "Patch version 2.0-12u8-OpenBSD\n");
419 my_exit(EXIT_SUCCESS
);