[BACK]Return to util.c CVS log [TXT][DIR] Up to [cvs.NetBSD.org] / src / usr.bin / patch

Annotation of src/usr.bin/patch/util.c, Revision 1.27

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

CVSweb <webmaster@jp.NetBSD.org>