From owner-svn-src-head@FreeBSD.ORG Tue Jan 29 17:03:21 2013 Return-Path: Delivered-To: svn-src-head@freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2001:1900:2254:206a::19:1]) by hub.freebsd.org (Postfix) with ESMTP id AECB0E9F; Tue, 29 Jan 2013 17:03:21 +0000 (UTC) (envelope-from gabor@FreeBSD.org) Received: from svn.freebsd.org (svn.freebsd.org [IPv6:2001:1900:2254:2068::e6a:0]) by mx1.freebsd.org (Postfix) with ESMTP id 923B9949; Tue, 29 Jan 2013 17:03:21 +0000 (UTC) Received: from svn.freebsd.org ([127.0.1.70]) by svn.freebsd.org (8.14.5/8.14.5) with ESMTP id r0TH3L6g078266; Tue, 29 Jan 2013 17:03:21 GMT (envelope-from gabor@svn.freebsd.org) Received: (from gabor@localhost) by svn.freebsd.org (8.14.5/8.14.5/Submit) id r0TH3JaO078248; Tue, 29 Jan 2013 17:03:19 GMT (envelope-from gabor@svn.freebsd.org) Message-Id: <201301291703.r0TH3JaO078248@svn.freebsd.org> From: Gabor Kovesdan Date: Tue, 29 Jan 2013 17:03:19 +0000 (UTC) To: src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org Subject: svn commit: r246074 - in head: . gnu/usr.bin/patch share/mk tools/build/options usr.bin usr.bin/patch X-SVN-Group: head MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit X-BeenThere: svn-src-head@freebsd.org X-Mailman-Version: 2.1.14 Precedence: list List-Id: SVN commit messages for the src tree for head/-current List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Tue, 29 Jan 2013 17:03:21 -0000 Author: gabor Date: Tue Jan 29 17:03:18 2013 New Revision: 246074 URL: http://svnweb.freebsd.org/changeset/base/246074 Log: - Add a BSD-licensed patch, ported by Pedro F. Giffuni (pfg) from DragonflyBSD and install it as bsdpatch. WITH_BSD_PATCH makes it default and installs GNU patch as gnupatch. Submitted by: pfg Obtained from: The DragonflyBSD Project Added: head/tools/build/options/WITH_BSD_PATCH (contents, props changed) head/usr.bin/patch/ head/usr.bin/patch/Makefile (contents, props changed) head/usr.bin/patch/backupfile.c (contents, props changed) head/usr.bin/patch/backupfile.h (contents, props changed) head/usr.bin/patch/common.h (contents, props changed) head/usr.bin/patch/inp.c (contents, props changed) head/usr.bin/patch/inp.h (contents, props changed) head/usr.bin/patch/mkpath.c (contents, props changed) head/usr.bin/patch/patch.1 (contents, props changed) head/usr.bin/patch/patch.c (contents, props changed) head/usr.bin/patch/pathnames.h (contents, props changed) head/usr.bin/patch/pch.c (contents, props changed) head/usr.bin/patch/pch.h (contents, props changed) head/usr.bin/patch/util.c (contents, props changed) head/usr.bin/patch/util.h (contents, props changed) Modified: head/UPDATING head/gnu/usr.bin/patch/Makefile head/share/mk/bsd.own.mk head/usr.bin/Makefile Modified: head/UPDATING ============================================================================== --- head/UPDATING Tue Jan 29 16:00:43 2013 (r246073) +++ head/UPDATING Tue Jan 29 17:03:18 2013 (r246074) @@ -26,6 +26,13 @@ NOTE TO PEOPLE WHO THINK THAT FreeBSD 10 disable the most expensive debugging functionality run "ln -s 'abort:false,junk:false' /etc/malloc.conf".) +20130129: + A BSD-licensed patch(1) variant has been added and is installed + as bsdpatch, being the GNU version the default patch. + To inverse the logic and use the BSD-licensed one as default, + while having the GNU version installed as gnupatch, rebuild + ans install world with the WITH_BSD_PATCH knob set. + 20130118: The install(1) option -M has changed meaning and now takes an argument that is a file or path to append logs to. In the Modified: head/gnu/usr.bin/patch/Makefile ============================================================================== --- head/gnu/usr.bin/patch/Makefile Tue Jan 29 16:00:43 2013 (r246073) +++ head/gnu/usr.bin/patch/Makefile Tue Jan 29 17:03:18 2013 (r246074) @@ -1,6 +1,17 @@ # $FreeBSD$ -PROG= patch +.include + +.if ${MK_BSD_PATCH} == "yes" +PROG= gnupatch +CLEANFILES+= gnupatch.1 + +gnupatch.1: patch.1 + cp ${.ALLSRC} ${.TARGET} +.else +PROG= patch +.endif + SRCS= backupfile.c inp.c patch.c pch.c util.c version.c CFLAGS+=-DHAVE_CONFIG_H Modified: head/share/mk/bsd.own.mk ============================================================================== --- head/share/mk/bsd.own.mk Tue Jan 29 16:00:43 2013 (r246073) +++ head/share/mk/bsd.own.mk Tue Jan 29 17:03:18 2013 (r246074) @@ -354,6 +354,7 @@ __DEFAULT_YES_OPTIONS = \ __DEFAULT_NO_OPTIONS = \ ARM_EABI \ + BSD_PATCH \ BIND_IDN \ BIND_LARGE_FILE \ BIND_LIBS \ Added: head/tools/build/options/WITH_BSD_PATCH ============================================================================== --- /dev/null 00:00:00 1970 (empty, because file is newly added) +++ head/tools/build/options/WITH_BSD_PATCH Tue Jan 29 17:03:18 2013 (r246074) @@ -0,0 +1,2 @@ +.\" $FreeBSD$ +Install BSD-licensed patch as 'patch' instead of GNU patch. Modified: head/usr.bin/Makefile ============================================================================== --- head/usr.bin/Makefile Tue Jan 29 16:00:43 2013 (r246073) +++ head/usr.bin/Makefile Tue Jan 29 17:03:18 2013 (r246074) @@ -123,6 +123,7 @@ SUBDIR= alias \ pagesize \ passwd \ paste \ + patch \ pathchk \ perror \ pr \ Added: head/usr.bin/patch/Makefile ============================================================================== --- /dev/null 00:00:00 1970 (empty, because file is newly added) +++ head/usr.bin/patch/Makefile Tue Jan 29 17:03:18 2013 (r246074) @@ -0,0 +1,19 @@ +# $FreeBSD$ +# $OpenBSD: Makefile,v 1.4 2005/05/16 15:22:46 espie Exp $ +# $DragonFly: src/usr.bin/patch/Makefile,v 1.8 2008/08/10 23:50:12 joerg Exp $ + +.include + +.if ${MK_BSD_PATCH} == "yes" +PROG= patch +.else +PROG= bsdpatch +CLEANFILES+= bsdpatch.1 + +bsdpatch.1: patch.1 + cp ${.ALLSRC} ${.TARGET} +.endif + +SRCS= backupfile.c inp.c mkpath.c patch.c pch.c util.c + +.include Added: head/usr.bin/patch/backupfile.c ============================================================================== --- /dev/null 00:00:00 1970 (empty, because file is newly added) +++ head/usr.bin/patch/backupfile.c Tue Jan 29 17:03:18 2013 (r246074) @@ -0,0 +1,248 @@ +/* $FreeBSD$ */ +/*- + * + * Copyright (C) 1990 Free Software Foundation, Inc. + * + * This program is free software; you can redistribute it and/or modify it + * without restriction. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. + * + * backupfile.c -- make Emacs style backup file names + * + * David MacKenzie . Some algorithms adapted from GNU Emacs. + * + * $OpenBSD: backupfile.c,v 1.19 2006/03/11 19:41:30 otto Exp $ + * $DragonFly: src/usr.bin/patch/backupfile.c,v 1.5 2008/08/11 00:05:06 joerg Exp $ + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "backupfile.h" + + +#define ISDIGIT(c) (isascii ((unsigned char)c) && isdigit ((unsigned char)c)) + +/* Which type of backup file names are generated. */ +enum backup_type backup_type = none; + +/* + * The extension added to file names to produce a simple (as opposed to + * numbered) backup file name. + */ +const char *simple_backup_suffix = "~"; + +static char *concat(const char *, const char *); +static char *make_version_name(const char *, int); +static int max_backup_version(const char *, const char *); +static int version_number(const char *, const char *, size_t); +static int argmatch(const char *, const char **); +static void invalid_arg(const char *, const char *, int); + +/* + * Return the name of the new backup file for file FILE, allocated with + * malloc. Return 0 if out of memory. FILE must not end with a '/' unless it + * is the root directory. Do not call this function if backup_type == none. + */ +char * +find_backup_file_name(const char *file) +{ + char *dir, *base_versions, *tmp_file; + int highest_backup; + + if (backup_type == simple) + return concat(file, simple_backup_suffix); + tmp_file = strdup(file); + if (tmp_file == NULL) + return NULL; + base_versions = concat(basename(tmp_file), ".~"); + free(tmp_file); + if (base_versions == NULL) + return NULL; + tmp_file = strdup(file); + if (tmp_file == NULL) { + free(base_versions); + return NULL; + } + dir = dirname(tmp_file); + if (dir == NULL) { + free(base_versions); + free(tmp_file); + return NULL; + } + highest_backup = max_backup_version(base_versions, dir); + free(base_versions); + free(tmp_file); + if (backup_type == numbered_existing && highest_backup == 0) + return concat(file, simple_backup_suffix); + return make_version_name(file, highest_backup + 1); +} + +/* + * Return the number of the highest-numbered backup file for file FILE in + * directory DIR. If there are no numbered backups of FILE in DIR, or an + * error occurs reading DIR, return 0. FILE should already have ".~" appended + * to it. + */ +static int +max_backup_version(const char *file, const char *dir) +{ + DIR *dirp; + struct dirent *dp; + int highest_version, this_version; + size_t file_name_length; + + dirp = opendir(dir); + if (dirp == NULL) + return 0; + + highest_version = 0; + file_name_length = strlen(file); + + while ((dp = readdir(dirp)) != NULL) { + if (dp->d_namlen <= file_name_length) + continue; + + this_version = version_number(file, dp->d_name, file_name_length); + if (this_version > highest_version) + highest_version = this_version; + } + closedir(dirp); + return highest_version; +} + +/* + * Return a string, allocated with malloc, containing "FILE.~VERSION~". + * Return 0 if out of memory. + */ +static char * +make_version_name(const char *file, int version) +{ + char *backup_name; + + if (asprintf(&backup_name, "%s.~%d~", file, version) == -1) + return NULL; + return backup_name; +} + +/* + * If BACKUP is a numbered backup of BASE, return its version number; + * otherwise return 0. BASE_LENGTH is the length of BASE. BASE should + * already have ".~" appended to it. + */ +static int +version_number(const char *base, const char *backup, size_t base_length) +{ + int version; + const char *p; + + version = 0; + if (!strncmp(base, backup, base_length) && ISDIGIT(backup[base_length])) { + for (p = &backup[base_length]; ISDIGIT(*p); ++p) + version = version * 10 + *p - '0'; + if (p[0] != '~' || p[1]) + version = 0; + } + return version; +} + +/* + * Return the newly-allocated concatenation of STR1 and STR2. If out of + * memory, return 0. + */ +static char * +concat(const char *str1, const char *str2) +{ + char *newstr; + + if (asprintf(&newstr, "%s%s", str1, str2) == -1) + return NULL; + return newstr; +} + +/* + * If ARG is an unambiguous match for an element of the null-terminated array + * OPTLIST, return the index in OPTLIST of the matched element, else -1 if it + * does not match any element or -2 if it is ambiguous (is a prefix of more + * than one element). + */ +static int +argmatch(const char *arg, const char **optlist) +{ + int i; /* Temporary index in OPTLIST. */ + size_t arglen; /* Length of ARG. */ + int matchind = -1; /* Index of first nonexact match. */ + int ambiguous = 0; /* If nonzero, multiple nonexact match(es). */ + + arglen = strlen(arg); + + /* Test all elements for either exact match or abbreviated matches. */ + for (i = 0; optlist[i]; i++) { + if (!strncmp(optlist[i], arg, arglen)) { + if (strlen(optlist[i]) == arglen) + /* Exact match found. */ + return i; + else if (matchind == -1) + /* First nonexact match found. */ + matchind = i; + else + /* Second nonexact match found. */ + ambiguous = 1; + } + } + if (ambiguous) + return -2; + else + return matchind; +} + +/* + * Error reporting for argmatch. KIND is a description of the type of entity + * that was being matched. VALUE is the invalid value that was given. PROBLEM + * is the return value from argmatch. + */ +static void +invalid_arg(const char *kind, const char *value, int problem) +{ + fprintf(stderr, "patch: "); + if (problem == -1) + fprintf(stderr, "invalid"); + else /* Assume -2. */ + fprintf(stderr, "ambiguous"); + fprintf(stderr, " %s `%s'\n", kind, value); +} + +static const char *backup_args[] = { + "never", "simple", "nil", "existing", "t", "numbered", 0 +}; + +static enum backup_type backup_types[] = { + simple, simple, numbered_existing, + numbered_existing, numbered, numbered +}; + +/* + * Return the type of backup indicated by VERSION. Unique abbreviations are + * accepted. + */ +enum backup_type +get_version(const char *version) +{ + int i; + + if (version == NULL || *version == '\0') + return numbered_existing; + i = argmatch(version, backup_args); + if (i >= 0) + return backup_types[i]; + invalid_arg("version control type", version, i); + exit(2); +} Added: head/usr.bin/patch/backupfile.h ============================================================================== --- /dev/null 00:00:00 1970 (empty, because file is newly added) +++ head/usr.bin/patch/backupfile.h Tue Jan 29 17:03:18 2013 (r246074) @@ -0,0 +1,39 @@ +/* $FreeBSD$ */ +/*- + * backupfile.h -- declarations for making Emacs style backup file names + * Copyright (C) 1990 Free Software Foundation, Inc. + * + * This program is free software; you can redistribute it and/or modify it + * without restriction. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. + * + * $OpenBSD: backupfile.h,v 1.6 2003/07/28 18:35:36 otto Exp $ + * $DragonFly: src/usr.bin/patch/backupfile.h,v 1.3 2007/09/29 23:11:10 swildner Exp $ + */ + +/* When to make backup files. */ +enum backup_type { + /* Never make backups. */ + none, + + /* Make simple backups of every file. */ + simple, + + /* + * Make numbered backups of files that already have numbered backups, + * and simple backups of the others. + */ + numbered_existing, + + /* Make numbered backups of every file. */ + numbered +}; + +extern enum backup_type backup_type; +extern const char *simple_backup_suffix; + +char *find_backup_file_name(const char *file); +enum backup_type get_version(const char *version); Added: head/usr.bin/patch/common.h ============================================================================== --- /dev/null 00:00:00 1970 (empty, because file is newly added) +++ head/usr.bin/patch/common.h Tue Jan 29 17:03:18 2013 (r246074) @@ -0,0 +1,121 @@ +/* $FreeBSD$ */ +/*- + * + * Copyright 1986, Larry Wall + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following condition is met: + * 1. Redistributions of source code must retain the above copyright notice, + * this condition and the following disclaimer. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * patch - a program to apply diffs to original files + * + * -C option added in 1998, original code by Marc Espie, based on FreeBSD + * behaviour + * + * $OpenBSD: common.h,v 1.26 2006/03/11 19:41:30 otto Exp $ + * $DragonFly: src/usr.bin/patch/common.h,v 1.5 2008/08/10 23:50:12 joerg Exp $ + */ + +#include + +#include +#include + +#define DEBUGGING + +/* constants */ + +#define MAXHUNKSIZE 200000 /* is this enough lines? */ +#define INITHUNKMAX 125 /* initial dynamic allocation size */ +#define INITLINELEN 4096 +#define BUFFERSIZE 4096 + +#define SCCSPREFIX "s." +#define GET "get -e %s" +#define SCCSDIFF "get -p %s | diff - %s >/dev/null" + +#define RCSSUFFIX ",v" +#define CHECKOUT "co -l %s" +#define RCSDIFF "rcsdiff %s > /dev/null" + +#define ORIGEXT ".orig" +#define REJEXT ".rej" + +/* handy definitions */ + +#define strNE(s1,s2) (strcmp(s1, s2)) +#define strEQ(s1,s2) (!strcmp(s1, s2)) +#define strnNE(s1,s2,l) (strncmp(s1, s2, l)) +#define strnEQ(s1,s2,l) (!strncmp(s1, s2, l)) + +/* typedefs */ + +typedef long LINENUM; /* must be signed */ + +/* globals */ + +extern mode_t filemode; + +extern char *buf; /* general purpose buffer */ +extern size_t buf_size; /* size of general purpose buffer */ + +extern bool using_plan_a; /* try to keep everything in memory */ +extern bool out_of_mem; /* ran out of memory in plan a */ + +#define MAXFILEC 2 + +extern char *filearg[MAXFILEC]; +extern bool ok_to_create_file; +extern char *outname; +extern char *origprae; + +extern char *TMPOUTNAME; +extern char *TMPINNAME; +extern char *TMPREJNAME; +extern char *TMPPATNAME; +extern bool toutkeep; +extern bool trejkeep; + +#ifdef DEBUGGING +extern int debug; +#endif + +extern bool force; +extern bool batch; +extern bool verbose; +extern bool reverse; +extern bool noreverse; +extern bool skip_rest_of_patch; +extern int strippath; +extern bool canonicalize; +/* TRUE if -C was specified on command line. */ +extern bool check_only; +extern bool warn_on_invalid_line; +extern bool last_line_missing_eol; + + +#define CONTEXT_DIFF 1 +#define NORMAL_DIFF 2 +#define ED_DIFF 3 +#define NEW_CONTEXT_DIFF 4 +#define UNI_DIFF 5 + +extern int diff_type; +extern char *revision; /* prerequisite revision, if any */ +extern LINENUM input_lines; /* how long is input file in lines */ + +extern int posix; + Added: head/usr.bin/patch/inp.c ============================================================================== --- /dev/null 00:00:00 1970 (empty, because file is newly added) +++ head/usr.bin/patch/inp.c Tue Jan 29 17:03:18 2013 (r246074) @@ -0,0 +1,485 @@ +/* $FreeBSD$ */ +/*- + * + * Copyright 1986, Larry Wall + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following condition is met: + * 1. Redistributions of source code must retain the above copyright notice, + * this condition and the following disclaimer. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * patch - a program to apply diffs to original files + * + * -C option added in 1998, original code by Marc Espie, based on FreeBSD + * behaviour + * + * $OpenBSD: inp.c,v 1.34 2006/03/11 19:41:30 otto Exp $ + */ + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "common.h" +#include "util.h" +#include "pch.h" +#include "inp.h" + + +/* Input-file-with-indexable-lines abstract type */ + +static size_t i_size; /* size of the input file */ +static char *i_womp; /* plan a buffer for entire file */ +static char **i_ptr; /* pointers to lines in i_womp */ +static char empty_line[] = { '\0' }; + +static int tifd = -1; /* plan b virtual string array */ +static char *tibuf[2]; /* plan b buffers */ +static LINENUM tiline[2] = {-1, -1}; /* 1st line in each buffer */ +static LINENUM lines_per_buf; /* how many lines per buffer */ +static int tireclen; /* length of records in tmp file */ + +static bool rev_in_string(const char *); +static bool reallocate_lines(size_t *); + +/* returns false if insufficient memory */ +static bool plan_a(const char *); + +static void plan_b(const char *); + +/* New patch--prepare to edit another file. */ + +void +re_input(void) +{ + if (using_plan_a) { + free(i_ptr); + i_ptr = NULL; + if (i_womp != NULL) { + munmap(i_womp, i_size); + i_womp = NULL; + } + i_size = 0; + } else { + using_plan_a = true; /* maybe the next one is smaller */ + close(tifd); + tifd = -1; + free(tibuf[0]); + free(tibuf[1]); + tibuf[0] = tibuf[1] = NULL; + tiline[0] = tiline[1] = -1; + tireclen = 0; + } +} + +/* Construct the line index, somehow or other. */ + +void +scan_input(const char *filename) +{ + if (!plan_a(filename)) + plan_b(filename); + if (verbose) { + say("Patching file %s using Plan %s...\n", filename, + (using_plan_a ? "A" : "B")); + } +} + +static bool +reallocate_lines(size_t *lines_allocated) +{ + char **p; + size_t new_size; + + new_size = *lines_allocated * 3 / 2; + p = realloc(i_ptr, (new_size + 2) * sizeof(char *)); + if (p == NULL) { /* shucks, it was a near thing */ + munmap(i_womp, i_size); + i_womp = NULL; + free(i_ptr); + i_ptr = NULL; + *lines_allocated = 0; + return false; + } + *lines_allocated = new_size; + i_ptr = p; + return true; +} + +/* Try keeping everything in memory. */ + +static bool +plan_a(const char *filename) +{ + int ifd, statfailed; + char *p, *s, lbuf[INITLINELEN]; + struct stat filestat; + ptrdiff_t sz; + size_t i; + size_t iline, lines_allocated; + +#ifdef DEBUGGING + if (debug & 8) + return false; +#endif + + if (filename == NULL || *filename == '\0') + return false; + + statfailed = stat(filename, &filestat); + if (statfailed && ok_to_create_file) { + if (verbose) + say("(Creating file %s...)\n", filename); + + /* + * in check_patch case, we still display `Creating file' even + * though we're not. The rule is that -C should be as similar + * to normal patch behavior as possible + */ + if (check_only) + return true; + makedirs(filename, true); + close(creat(filename, 0666)); + statfailed = stat(filename, &filestat); + } + if (statfailed && check_only) + fatal("%s not found, -C mode, can't probe further\n", filename); + /* For nonexistent or read-only files, look for RCS or SCCS versions. */ + if (statfailed || + /* No one can write to it. */ + (filestat.st_mode & 0222) == 0 || + /* I can't write to it. */ + ((filestat.st_mode & 0022) == 0 && filestat.st_uid != getuid())) { + const char *cs = NULL, *filebase, *filedir; + struct stat cstat; + char *tmp_filename1, *tmp_filename2; + + tmp_filename1 = strdup(filename); + tmp_filename2 = strdup(filename); + if (tmp_filename1 == NULL || tmp_filename2 == NULL) + fatal("strdupping filename"); + filebase = basename(tmp_filename1); + filedir = dirname(tmp_filename2); + + /* Leave room in lbuf for the diff command. */ + s = lbuf + 20; + +#define try(f, a1, a2, a3) \ + (snprintf(s, buf_size - 20, f, a1, a2, a3), stat(s, &cstat) == 0) + + if (try("%s/RCS/%s%s", filedir, filebase, RCSSUFFIX) || + try("%s/RCS/%s%s", filedir, filebase, "") || + try("%s/%s%s", filedir, filebase, RCSSUFFIX)) { + snprintf(buf, buf_size, CHECKOUT, filename); + snprintf(lbuf, sizeof lbuf, RCSDIFF, filename); + cs = "RCS"; + } else if (try("%s/SCCS/%s%s", filedir, SCCSPREFIX, filebase) || + try("%s/%s%s", filedir, SCCSPREFIX, filebase)) { + snprintf(buf, buf_size, GET, s); + snprintf(lbuf, sizeof lbuf, SCCSDIFF, s, filename); + cs = "SCCS"; + } else if (statfailed) + fatal("can't find %s\n", filename); + + free(tmp_filename1); + free(tmp_filename2); + + /* + * else we can't write to it but it's not under a version + * control system, so just proceed. + */ + if (cs) { + if (!statfailed) { + if ((filestat.st_mode & 0222) != 0) + /* The owner can write to it. */ + fatal("file %s seems to be locked " + "by somebody else under %s\n", + filename, cs); + /* + * It might be checked out unlocked. See if + * it's safe to check out the default version + * locked. + */ + if (verbose) + say("Comparing file %s to default " + "%s version...\n", + filename, cs); + if (system(lbuf)) + fatal("can't check out file %s: " + "differs from default %s version\n", + filename, cs); + } + if (verbose) + say("Checking out file %s from %s...\n", + filename, cs); + if (system(buf) || stat(filename, &filestat)) + fatal("can't check out file %s from %s\n", + filename, cs); + } + } + filemode = filestat.st_mode; + if (!S_ISREG(filemode)) + fatal("%s is not a normal file--can't patch\n", filename); + if ((uint64_t)filestat.st_size > SIZE_MAX) { + say("block too large to mmap\n"); + return false; + } + i_size = (size_t)filestat.st_size; + if (out_of_mem) { + set_hunkmax(); /* make sure dynamic arrays are allocated */ + out_of_mem = false; + return false; /* force plan b because plan a bombed */ + } + if ((ifd = open(filename, O_RDONLY)) < 0) + pfatal("can't open file %s", filename); + + if (i_size) { + i_womp = mmap(NULL, i_size, PROT_READ, MAP_PRIVATE, ifd, 0); + if (i_womp == MAP_FAILED) { + perror("mmap failed"); + i_womp = NULL; + close(ifd); + return false; + } + } else { + i_womp = NULL; + } + + close(ifd); + if (i_size) + madvise(i_womp, i_size, MADV_SEQUENTIAL); + + /* estimate the number of lines */ + lines_allocated = i_size / 25; + if (lines_allocated < 100) + lines_allocated = 100; + + if (!reallocate_lines(&lines_allocated)) + return false; + + /* now scan the buffer and build pointer array */ + iline = 1; + i_ptr[iline] = i_womp; + /* test for NUL too, to maintain the behavior of the original code */ + for (s = i_womp, i = 0; i < i_size && *s != '\0'; s++, i++) { + if (*s == '\n') { + if (iline == lines_allocated) { + if (!reallocate_lines(&lines_allocated)) + return false; + } + /* these are NOT NUL terminated */ + i_ptr[++iline] = s + 1; + } + } + /* if the last line contains no EOL, append one */ + if (i_size > 0 && i_womp[i_size - 1] != '\n') { + last_line_missing_eol = true; + /* fix last line */ + sz = s - i_ptr[iline]; + p = malloc(sz + 1); + if (p == NULL) { + free(i_ptr); + i_ptr = NULL; + munmap(i_womp, i_size); + i_womp = NULL; + return false; + } + + memcpy(p, i_ptr[iline], sz); + p[sz] = '\n'; + i_ptr[iline] = p; + /* count the extra line and make it point to some valid mem */ + i_ptr[++iline] = empty_line; + } else + last_line_missing_eol = false; + + input_lines = iline - 1; + + /* now check for revision, if any */ + + if (revision != NULL) { + if (!rev_in_string(i_womp)) { + if (force) { + if (verbose) + say("Warning: this file doesn't appear " + "to be the %s version--patching anyway.\n", + revision); + } else if (batch) { + fatal("this file doesn't appear to be the " + "%s version--aborting.\n", + revision); + } else { + ask("This file doesn't appear to be the " + "%s version--patch anyway? [n] ", + revision); + if (*buf != 'y') + fatal("aborted\n"); + } + } else if (verbose) + say("Good. This file appears to be the %s version.\n", + revision); + } + return true; /* plan a will work */ +} + +/* Keep (virtually) nothing in memory. */ + +static void +plan_b(const char *filename) +{ + FILE *ifp; + size_t i = 0, j, maxlen = 1; + char *p; + bool found_revision = (revision == NULL); + + using_plan_a = false; + if ((ifp = fopen(filename, "r")) == NULL) + pfatal("can't open file %s", filename); + unlink(TMPINNAME); + if ((tifd = open(TMPINNAME, O_EXCL | O_CREAT | O_WRONLY, 0666)) < 0) + pfatal("can't open file %s", TMPINNAME); + while (fgets(buf, buf_size, ifp) != NULL) { + if (revision != NULL && !found_revision && rev_in_string(buf)) + found_revision = true; + if ((i = strlen(buf)) > maxlen) + maxlen = i; /* find longest line */ + } + last_line_missing_eol = i > 0 && buf[i - 1] != '\n'; + if (last_line_missing_eol && maxlen == i) + maxlen++; + + if (revision != NULL) { + if (!found_revision) { + if (force) { + if (verbose) + say("Warning: this file doesn't appear " + "to be the %s version--patching anyway.\n", + revision); + } else if (batch) { + fatal("this file doesn't appear to be the " + "%s version--aborting.\n", + revision); + } else { + ask("This file doesn't appear to be the %s " + "version--patch anyway? [n] ", + revision); + if (*buf != 'y') + fatal("aborted\n"); + } + } else if (verbose) + say("Good. This file appears to be the %s version.\n", + revision); + } + fseek(ifp, 0L, SEEK_SET); /* rewind file */ + lines_per_buf = BUFFERSIZE / maxlen; + tireclen = maxlen; + tibuf[0] = malloc(BUFFERSIZE + 1); + if (tibuf[0] == NULL) + fatal("out of memory\n"); + tibuf[1] = malloc(BUFFERSIZE + 1); + if (tibuf[1] == NULL) + fatal("out of memory\n"); + for (i = 1;; i++) { + p = tibuf[0] + maxlen * (i % lines_per_buf); + if (i % lines_per_buf == 0) /* new block */ + if (write(tifd, tibuf[0], BUFFERSIZE) < BUFFERSIZE) + pfatal("can't write temp file"); + if (fgets(p, maxlen + 1, ifp) == NULL) { + input_lines = i - 1; + if (i % lines_per_buf != 0) + if (write(tifd, tibuf[0], BUFFERSIZE) < BUFFERSIZE) + pfatal("can't write temp file"); + break; + } + j = strlen(p); + /* These are '\n' terminated strings, so no need to add a NUL */ + if (j == 0 || p[j - 1] != '\n') + p[j] = '\n'; + } + fclose(ifp); + close(tifd); + if ((tifd = open(TMPINNAME, O_RDONLY)) < 0) + pfatal("can't reopen file %s", TMPINNAME); +} + +/* + * Fetch a line from the input file, \n terminated, not necessarily \0. + */ +char * +ifetch(LINENUM line, int whichbuf) +{ + if (line < 1 || line > input_lines) { + if (warn_on_invalid_line) { + say("No such line %ld in input file, ignoring\n", line); + warn_on_invalid_line = false; + } + return NULL; + } + if (using_plan_a) + return i_ptr[line]; + else { + LINENUM offline = line % lines_per_buf; + LINENUM baseline = line - offline; + + if (tiline[0] == baseline) + whichbuf = 0; + else if (tiline[1] == baseline) + whichbuf = 1; + else { + tiline[whichbuf] = baseline; + + if (lseek(tifd, (off_t) (baseline / lines_per_buf * + BUFFERSIZE), SEEK_SET) < 0) + pfatal("cannot seek in the temporary input file"); + + if (read(tifd, tibuf[whichbuf], BUFFERSIZE) < 0) + pfatal("error reading tmp file %s", TMPINNAME); + } + return tibuf[whichbuf] + (tireclen * offline); + } +} + +/* + * True if the string argument contains the revision number we want. + */ +static bool +rev_in_string(const char *string) +{ + const char *s; + size_t patlen; + + if (revision == NULL) + return true; + patlen = strlen(revision); + if (strnEQ(string, revision, patlen) && isspace((unsigned char)string[patlen])) + return true; + for (s = string; *s; s++) { + if (isspace((unsigned char)*s) && strnEQ(s + 1, revision, patlen) && + isspace((unsigned char)s[patlen + 1])) { + return true; + } + } + return false; +} Added: head/usr.bin/patch/inp.h ============================================================================== --- /dev/null 00:00:00 1970 (empty, because file is newly added) +++ head/usr.bin/patch/inp.h Tue Jan 29 17:03:18 2013 (r246074) @@ -0,0 +1,35 @@ +/* + * $FreeBSD$ + * $OpenBSD: inp.h,v 1.8 2003/08/15 08:00:51 otto Exp $ + * $DragonFly: src/usr.bin/patch/inp.h,v 1.1 2004/09/24 18:44:28 joerg Exp $ + */ + +/* + * patch - a program to apply diffs to original files *** DIFF OUTPUT TRUNCATED AT 1000 LINES ***