original netbsd patch
This commit is contained in:
		
							parent
							
								
									6657c0e58e
								
							
						
					
					
						commit
						f50f1bf7d6
					
				
							
								
								
									
										8
									
								
								commands/patch/Makefile
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										8
									
								
								commands/patch/Makefile
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,8 @@
 | 
			
		||||
#	$NetBSD: Makefile,v 1.9 2008/09/19 18:33:34 joerg Exp $
 | 
			
		||||
#	$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 $
 | 
			
		||||
 | 
			
		||||
PROG=	patch
 | 
			
		||||
SRCS=	patch.c pch.c inp.c util.c backupfile.c mkpath.c
 | 
			
		||||
 | 
			
		||||
.include <bsd.prog.mk>
 | 
			
		||||
							
								
								
									
										254
									
								
								commands/patch/backupfile.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										254
									
								
								commands/patch/backupfile.c
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,254 @@
 | 
			
		||||
/*
 | 
			
		||||
 * $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 $
 | 
			
		||||
 * $NetBSD: backupfile.c,v 1.14 2008/09/19 18:33:34 joerg Exp $
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * backupfile.c -- make 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.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * David MacKenzie <djm@ai.mit.edu>. Some algorithms adapted from GNU Emacs.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include <sys/cdefs.h>
 | 
			
		||||
__RCSID("$NetBSD: backupfile.c,v 1.14 2008/09/19 18:33:34 joerg Exp $");
 | 
			
		||||
 | 
			
		||||
#include <ctype.h>
 | 
			
		||||
#include <dirent.h>
 | 
			
		||||
#include <libgen.h>
 | 
			
		||||
#include <stdio.h>
 | 
			
		||||
#include <stdlib.h>
 | 
			
		||||
#include <string.h>
 | 
			
		||||
#include <unistd.h>
 | 
			
		||||
 | 
			
		||||
#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);
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										42
									
								
								commands/patch/backupfile.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										42
									
								
								commands/patch/backupfile.h
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,42 @@
 | 
			
		||||
/*
 | 
			
		||||
 * $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 $
 | 
			
		||||
 * $NetBSD: backupfile.h,v 1.6 2008/09/19 18:33:34 joerg Exp $
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * 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.
 | 
			
		||||
 * 
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
/* 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);
 | 
			
		||||
							
								
								
									
										122
									
								
								commands/patch/common.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										122
									
								
								commands/patch/common.h
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,122 @@
 | 
			
		||||
/*
 | 
			
		||||
 * $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 $
 | 
			
		||||
 * $NetBSD: common.h,v 1.19 2008/09/19 18:33:34 joerg Exp $
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * patch - a program to apply diffs to original files
 | 
			
		||||
 * 
 | 
			
		||||
 * 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.
 | 
			
		||||
 * 
 | 
			
		||||
 * -C option added in 1998, original code by Marc Espie, based on FreeBSD
 | 
			
		||||
 * behaviour
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include <sys/types.h>
 | 
			
		||||
 | 
			
		||||
#include <stdbool.h>
 | 
			
		||||
#include <stdint.h>
 | 
			
		||||
 | 
			
		||||
#define DEBUGGING
 | 
			
		||||
 | 
			
		||||
/* constants */
 | 
			
		||||
 | 
			
		||||
#define MAXHUNKSIZE 100000	/* is this enough lines? */
 | 
			
		||||
#define INITHUNKMAX 125		/* initial dynamic allocation size */
 | 
			
		||||
#define MAXLINELEN 8192
 | 
			
		||||
#define BUFFERSIZE 1024
 | 
			
		||||
 | 
			
		||||
#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[MAXLINELEN];/* general purpose buffer */
 | 
			
		||||
extern size_t	buf_len;
 | 
			
		||||
 | 
			
		||||
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;
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										486
									
								
								commands/patch/inp.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										486
									
								
								commands/patch/inp.c
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,486 @@
 | 
			
		||||
/*
 | 
			
		||||
 * $OpenBSD: inp.c,v 1.34 2006/03/11 19:41:30 otto Exp $
 | 
			
		||||
 * $DragonFly: src/usr.bin/patch/inp.c,v 1.6 2007/09/29 23:11:10 swildner Exp $
 | 
			
		||||
 * $NetBSD: inp.c,v 1.19 2008/09/19 18:33:34 joerg Exp $
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * patch - a program to apply diffs to original files
 | 
			
		||||
 * 
 | 
			
		||||
 * 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.
 | 
			
		||||
 * 
 | 
			
		||||
 * -C option added in 1998, original code by Marc Espie, based on FreeBSD
 | 
			
		||||
 * behaviour
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include <sys/cdefs.h>
 | 
			
		||||
__RCSID("$NetBSD: inp.c,v 1.19 2008/09/19 18:33:34 joerg Exp $");
 | 
			
		||||
 | 
			
		||||
#include <sys/types.h>
 | 
			
		||||
#include <sys/file.h>
 | 
			
		||||
#include <sys/stat.h>
 | 
			
		||||
#include <sys/mman.h>
 | 
			
		||||
 | 
			
		||||
#include <ctype.h>
 | 
			
		||||
#include <libgen.h>
 | 
			
		||||
#include <limits.h>
 | 
			
		||||
#include <stddef.h>
 | 
			
		||||
#include <stdio.h>
 | 
			
		||||
#include <stdlib.h>
 | 
			
		||||
#include <string.h>
 | 
			
		||||
#include <unistd.h>
 | 
			
		||||
 | 
			
		||||
#include "common.h"
 | 
			
		||||
#include "util.h"
 | 
			
		||||
#include "pch.h"
 | 
			
		||||
#include "inp.h"
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/* Input-file-with-indexable-lines abstract type */
 | 
			
		||||
 | 
			
		||||
static off_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) {
 | 
			
		||||
		i_size = 0;
 | 
			
		||||
		free(i_ptr);
 | 
			
		||||
		i_ptr = NULL;
 | 
			
		||||
		if (i_womp != NULL) {
 | 
			
		||||
			munmap(i_womp, i_size);
 | 
			
		||||
			i_womp = NULL;
 | 
			
		||||
		}
 | 
			
		||||
	} 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[MAXLINELEN];
 | 
			
		||||
	struct stat	filestat;
 | 
			
		||||
	off_t		i;
 | 
			
		||||
	ptrdiff_t	sz;
 | 
			
		||||
	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, sizeof lbuf - 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_len, 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_len, 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);
 | 
			
		||||
	i_size = 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 (i_size > SIZE_MAX) {
 | 
			
		||||
		say("block too large to mmap\n");
 | 
			
		||||
		return false;
 | 
			
		||||
	}
 | 
			
		||||
	if ((ifd = open(filename, O_RDONLY)) < 0)
 | 
			
		||||
		pfatal("can't open file %s", filename);
 | 
			
		||||
 | 
			
		||||
	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;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	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_len, 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;
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										35
									
								
								commands/patch/inp.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										35
									
								
								commands/patch/inp.h
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,35 @@
 | 
			
		||||
/*
 | 
			
		||||
 * $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 $
 | 
			
		||||
 * $NetBSD: inp.h,v 1.10 2008/09/19 18:33:34 joerg Exp $
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * patch - a program to apply diffs to original files
 | 
			
		||||
 * 
 | 
			
		||||
 * 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.
 | 
			
		||||
 * 
 | 
			
		||||
 * -C option added in 1998, original code by Marc Espie, based on FreeBSD
 | 
			
		||||
 * behaviour
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
void		re_input(void);
 | 
			
		||||
void		scan_input(const char *);
 | 
			
		||||
char		*ifetch(LINENUM, int);
 | 
			
		||||
							
								
								
									
										84
									
								
								commands/patch/mkpath.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										84
									
								
								commands/patch/mkpath.c
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,84 @@
 | 
			
		||||
/*
 | 
			
		||||
 *	$OpenBSD: mkpath.c,v 1.2 2005/06/20 07:14:06 otto Exp $
 | 
			
		||||
 *	$DragonFly: src/usr.bin/patch/mkpath.c,v 1.1 2007/09/29 23:11:10 swildner Exp $
 | 
			
		||||
 *	$NetBSD: mkpath.c,v 1.1 2008/09/19 18:33:34 joerg Exp $
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Copyright (c) 1983, 1992, 1993
 | 
			
		||||
 *	The Regents of the University of California.  All rights reserved.
 | 
			
		||||
 *
 | 
			
		||||
 * Redistribution and use in source and binary forms, with or without
 | 
			
		||||
 * modification, are permitted provided that the following conditions
 | 
			
		||||
 * are met:
 | 
			
		||||
 * 1. Redistributions of source code must retain the above copyright
 | 
			
		||||
 *    notice, this list of conditions and the following disclaimer.
 | 
			
		||||
 * 2. Redistributions in binary form must reproduce the above copyright
 | 
			
		||||
 *    notice, this list of conditions and the following disclaimer in the
 | 
			
		||||
 *    documentation and/or other materials provided with the distribution.
 | 
			
		||||
 * 3. Neither the name of the University nor the names of its contributors
 | 
			
		||||
 *    may be used to endorse or promote products derived from this software
 | 
			
		||||
 *    without specific prior written permission.
 | 
			
		||||
 *
 | 
			
		||||
 * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include <sys/cdefs.h>
 | 
			
		||||
__RCSID("$NetBSD: mkpath.c,v 1.1 2008/09/19 18:33:34 joerg Exp $");
 | 
			
		||||
 | 
			
		||||
#include <sys/types.h>
 | 
			
		||||
#include <sys/stat.h>
 | 
			
		||||
#include <err.h>
 | 
			
		||||
#include <errno.h>
 | 
			
		||||
#include <string.h>
 | 
			
		||||
 | 
			
		||||
int	mkpath(char *);
 | 
			
		||||
 | 
			
		||||
/* Code taken directly from mkdir(1).
 | 
			
		||||
 | 
			
		||||
 * mkpath -- create directories.
 | 
			
		||||
 *	path     - path
 | 
			
		||||
 */
 | 
			
		||||
int
 | 
			
		||||
mkpath(char *path)
 | 
			
		||||
{
 | 
			
		||||
	struct stat sb;
 | 
			
		||||
	char *slash;
 | 
			
		||||
	int done = 0;
 | 
			
		||||
 | 
			
		||||
	slash = path;
 | 
			
		||||
 | 
			
		||||
	while (!done) {
 | 
			
		||||
		slash += strspn(slash, "/");
 | 
			
		||||
		slash += strcspn(slash, "/");
 | 
			
		||||
 | 
			
		||||
		done = (*slash == '\0');
 | 
			
		||||
		*slash = '\0';
 | 
			
		||||
 | 
			
		||||
		if (stat(path, &sb)) {
 | 
			
		||||
			if (errno != ENOENT || (mkdir(path, 0777) &&
 | 
			
		||||
			    errno != EEXIST)) {
 | 
			
		||||
				warn("%s", path);
 | 
			
		||||
				return (-1);
 | 
			
		||||
			}
 | 
			
		||||
		} else if (!S_ISDIR(sb.st_mode)) {
 | 
			
		||||
			warnx("%s: %s", path, strerror(ENOTDIR));
 | 
			
		||||
			return (-1);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		*slash = '/';
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return (0);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										700
									
								
								commands/patch/patch.1
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										700
									
								
								commands/patch/patch.1
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,700 @@
 | 
			
		||||
.\"	$OpenBSD: patch.1,v 1.22 2008/06/06 20:44:00 jmc Exp $
 | 
			
		||||
.\"	$DragonFly: src/usr.bin/patch/patch.1,v 1.10 2008/08/18 19:15:55 joerg Exp $
 | 
			
		||||
.\"	$NetBSD: patch.1,v 1.13 2008/09/19 18:33:34 joerg Exp $
 | 
			
		||||
.\" 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.
 | 
			
		||||
.\"
 | 
			
		||||
.Dd August 18, 2008
 | 
			
		||||
.Dt PATCH 1
 | 
			
		||||
.Os
 | 
			
		||||
.Sh NAME
 | 
			
		||||
.Nm patch
 | 
			
		||||
.Nd apply a diff file to an original
 | 
			
		||||
.Sh SYNOPSIS
 | 
			
		||||
.Nm
 | 
			
		||||
.Bk -words
 | 
			
		||||
.Op Fl bCcEeflNnRstuv
 | 
			
		||||
.Op Fl B Ar backup-prefix
 | 
			
		||||
.Op Fl D Ar symbol
 | 
			
		||||
.Op Fl d Ar directory
 | 
			
		||||
.Op Fl F Ar max-fuzz
 | 
			
		||||
.Op Fl i Ar patchfile
 | 
			
		||||
.Op Fl o Ar out-file
 | 
			
		||||
.Op Fl p Ar strip-count
 | 
			
		||||
.Op Fl r Ar rej-name
 | 
			
		||||
.Op Fl V Cm t | nil | never
 | 
			
		||||
.Op Fl x Ar number
 | 
			
		||||
.Op Fl z Ar backup-ext
 | 
			
		||||
.Op Fl Fl posix
 | 
			
		||||
.Op Ar origfile Op Ar patchfile
 | 
			
		||||
.Ek
 | 
			
		||||
.Nm
 | 
			
		||||
.Pf \*(Lt Ar patchfile
 | 
			
		||||
.Sh DESCRIPTION
 | 
			
		||||
.Nm
 | 
			
		||||
will take a patch file containing any of the four forms of difference
 | 
			
		||||
listing produced by the
 | 
			
		||||
.Xr diff 1
 | 
			
		||||
program and apply those differences to an original file,
 | 
			
		||||
producing a patched version.
 | 
			
		||||
If
 | 
			
		||||
.Ar patchfile
 | 
			
		||||
is omitted, or is a hyphen, the patch will be read from the standard input.
 | 
			
		||||
.Pp
 | 
			
		||||
.Nm
 | 
			
		||||
will attempt to determine the type of the diff listing, unless over-ruled by a
 | 
			
		||||
.Fl c ,
 | 
			
		||||
.Fl e ,
 | 
			
		||||
.Fl n ,
 | 
			
		||||
or
 | 
			
		||||
.Fl u
 | 
			
		||||
option.
 | 
			
		||||
Context diffs (old-style, new-style, and unified) and
 | 
			
		||||
normal diffs are applied directly by the
 | 
			
		||||
.Nm
 | 
			
		||||
program itself, whereas ed diffs are simply fed to the
 | 
			
		||||
.Xr ed 1
 | 
			
		||||
editor via a pipe.
 | 
			
		||||
.Pp
 | 
			
		||||
If the
 | 
			
		||||
.Ar patchfile
 | 
			
		||||
contains more than one patch,
 | 
			
		||||
.Nm
 | 
			
		||||
will try to apply each of them as if they came from separate patch files.
 | 
			
		||||
This means, among other things, that it is assumed that the name of the file
 | 
			
		||||
to patch must be determined for each diff listing, and that the garbage before
 | 
			
		||||
each diff listing will be examined for interesting things such as file names
 | 
			
		||||
and revision level (see the section on
 | 
			
		||||
.Sx Filename Determination
 | 
			
		||||
below).
 | 
			
		||||
.Pp
 | 
			
		||||
The options are as follows:
 | 
			
		||||
.Bl -tag -width Ds
 | 
			
		||||
.It Xo
 | 
			
		||||
.Fl B Ar backup-prefix ,
 | 
			
		||||
.Fl Fl prefix Ar backup-prefix
 | 
			
		||||
.Xc
 | 
			
		||||
Causes the next argument to be interpreted as a prefix to the backup file
 | 
			
		||||
name.
 | 
			
		||||
If this argument is specified, any argument to
 | 
			
		||||
.Fl z
 | 
			
		||||
will be ignored.
 | 
			
		||||
.It Fl b , Fl Fl backup
 | 
			
		||||
Save a backup copy of the file before it is modified.
 | 
			
		||||
By default the original file is saved with a backup extension of
 | 
			
		||||
.Qq .orig
 | 
			
		||||
unless the file already has a numbered backup, in which case a numbered
 | 
			
		||||
backup is made.
 | 
			
		||||
This is equivalent to specifying
 | 
			
		||||
.Qo Fl V Cm existing Qc .
 | 
			
		||||
This option is currently the default, unless
 | 
			
		||||
.Fl -posix
 | 
			
		||||
is specified.
 | 
			
		||||
.It Fl C , Fl Fl check
 | 
			
		||||
Checks that the patch would apply cleanly, but does not modify anything.
 | 
			
		||||
.It Fl c , Fl Fl context
 | 
			
		||||
Forces
 | 
			
		||||
.Nm
 | 
			
		||||
to interpret the patch file as a context diff.
 | 
			
		||||
.It Xo
 | 
			
		||||
.Fl D Ar symbol ,
 | 
			
		||||
.Fl Fl ifdef Ar symbol
 | 
			
		||||
.Xc
 | 
			
		||||
Causes
 | 
			
		||||
.Nm
 | 
			
		||||
to use the
 | 
			
		||||
.Qq #ifdef...#endif
 | 
			
		||||
construct to mark changes.
 | 
			
		||||
The argument following will be used as the differentiating symbol.
 | 
			
		||||
Note that, unlike the C compiler, there must be a space between the
 | 
			
		||||
.Fl D
 | 
			
		||||
and the argument.
 | 
			
		||||
.It Xo
 | 
			
		||||
.Fl d Ar directory ,
 | 
			
		||||
.Fl Fl directory Ar directory
 | 
			
		||||
.Xc
 | 
			
		||||
Causes
 | 
			
		||||
.Nm
 | 
			
		||||
to interpret the next argument as a directory,
 | 
			
		||||
and change the working directory to it before doing anything else.
 | 
			
		||||
.It Fl E , Fl Fl remove-empty-files
 | 
			
		||||
Causes
 | 
			
		||||
.Nm
 | 
			
		||||
to remove output files that are empty after the patches have been applied.
 | 
			
		||||
This option is useful when applying patches that create or remove files.
 | 
			
		||||
.It Fl e , Fl Fl ed
 | 
			
		||||
Forces
 | 
			
		||||
.Nm
 | 
			
		||||
to interpret the patch file as an
 | 
			
		||||
.Xr ed 1
 | 
			
		||||
script.
 | 
			
		||||
.It Xo
 | 
			
		||||
.Fl F Ar max-fuzz ,
 | 
			
		||||
.Fl Fl fuzz Ar max-fuzz
 | 
			
		||||
.Xc
 | 
			
		||||
Sets the maximum fuzz factor.
 | 
			
		||||
This option only applies to context diffs, and causes
 | 
			
		||||
.Nm
 | 
			
		||||
to ignore up to that many lines in looking for places to install a hunk.
 | 
			
		||||
Note that a larger fuzz factor increases the odds of a faulty patch.
 | 
			
		||||
The default fuzz factor is 2, and it may not be set to more than
 | 
			
		||||
the number of lines of context in the context diff, ordinarily 3.
 | 
			
		||||
.It Fl f , Fl Fl force
 | 
			
		||||
Forces
 | 
			
		||||
.Nm
 | 
			
		||||
to assume that the user knows exactly what he or she is doing, and to not
 | 
			
		||||
ask any questions.
 | 
			
		||||
It assumes the following:
 | 
			
		||||
skip patches for which a file to patch can't be found;
 | 
			
		||||
patch files even though they have the wrong version for the
 | 
			
		||||
.Qq Prereq:
 | 
			
		||||
line in the patch;
 | 
			
		||||
and assume that patches are not reversed even if they look like they are.
 | 
			
		||||
This option does not suppress commentary; use
 | 
			
		||||
.Fl s
 | 
			
		||||
for that.
 | 
			
		||||
.It Xo
 | 
			
		||||
.Fl i Ar patchfile ,
 | 
			
		||||
.Fl Fl input Ar patchfile
 | 
			
		||||
.Xc
 | 
			
		||||
Causes the next argument to be interpreted as the input file name
 | 
			
		||||
(i.e. a patchfile).
 | 
			
		||||
This option may be specified multiple times.
 | 
			
		||||
.It Fl l , Fl Fl ignore-whitespace
 | 
			
		||||
Causes the pattern matching to be done loosely, in case the tabs and
 | 
			
		||||
spaces have been munged in your input file.
 | 
			
		||||
Any sequence of whitespace in the pattern line will match any sequence
 | 
			
		||||
in the input file.
 | 
			
		||||
Normal characters must still match exactly.
 | 
			
		||||
Each line of the context must still match a line in the input file.
 | 
			
		||||
.It Fl N , Fl Fl forward
 | 
			
		||||
Causes
 | 
			
		||||
.Nm
 | 
			
		||||
to ignore patches that it thinks are reversed or already applied.
 | 
			
		||||
See also
 | 
			
		||||
.Fl R .
 | 
			
		||||
.It Fl n , Fl Fl normal
 | 
			
		||||
Forces
 | 
			
		||||
.Nm
 | 
			
		||||
to interpret the patch file as a normal diff.
 | 
			
		||||
.It Xo
 | 
			
		||||
.Fl o Ar out-file ,
 | 
			
		||||
.Fl Fl output Ar out-file
 | 
			
		||||
.Xc
 | 
			
		||||
Causes the next argument to be interpreted as the output file name.
 | 
			
		||||
.It Xo
 | 
			
		||||
.Fl p Ar strip-count ,
 | 
			
		||||
.Fl Fl strip Ar strip-count
 | 
			
		||||
.Xc
 | 
			
		||||
Sets the pathname strip count,
 | 
			
		||||
which controls how pathnames found in the patch file are treated,
 | 
			
		||||
in case you keep your files in a different directory than the person who sent
 | 
			
		||||
out the patch.
 | 
			
		||||
The strip count specifies how many slashes are to be stripped from
 | 
			
		||||
the front of the pathname.
 | 
			
		||||
(Any intervening directory names also go away.)
 | 
			
		||||
For example, supposing the file name in the patch file was
 | 
			
		||||
.Pa /u/howard/src/blurfl/blurfl.c :
 | 
			
		||||
.Pp
 | 
			
		||||
Setting
 | 
			
		||||
.Fl p Ns Ar 0
 | 
			
		||||
gives the entire pathname unmodified.
 | 
			
		||||
.Pp
 | 
			
		||||
.Fl p Ns Ar 1
 | 
			
		||||
gives
 | 
			
		||||
.Pp
 | 
			
		||||
.D1 Pa u/howard/src/blurfl/blurfl.c
 | 
			
		||||
.Pp
 | 
			
		||||
without the leading slash.
 | 
			
		||||
.Pp
 | 
			
		||||
.Fl p Ns Ar 4
 | 
			
		||||
gives
 | 
			
		||||
.Pp
 | 
			
		||||
.D1 Pa blurfl/blurfl.c
 | 
			
		||||
.Pp
 | 
			
		||||
Not specifying
 | 
			
		||||
.Fl p
 | 
			
		||||
at all just gives you
 | 
			
		||||
.Pa blurfl.c ,
 | 
			
		||||
unless all of the directories in the leading path
 | 
			
		||||
.Pq Pa u/howard/src/blurfl
 | 
			
		||||
exist and that path is relative,
 | 
			
		||||
in which case you get the entire pathname unmodified.
 | 
			
		||||
Whatever you end up with is looked for either in the current directory,
 | 
			
		||||
or the directory specified by the
 | 
			
		||||
.Fl d
 | 
			
		||||
option.
 | 
			
		||||
.It Fl R , Fl Fl reverse
 | 
			
		||||
Tells
 | 
			
		||||
.Nm
 | 
			
		||||
that this patch was created with the old and new files swapped.
 | 
			
		||||
(Yes, I'm afraid that does happen occasionally, human nature being what it
 | 
			
		||||
is.)
 | 
			
		||||
.Nm
 | 
			
		||||
will attempt to swap each hunk around before applying it.
 | 
			
		||||
Rejects will come out in the swapped format.
 | 
			
		||||
The
 | 
			
		||||
.Fl R
 | 
			
		||||
option will not work with ed diff scripts because there is too little
 | 
			
		||||
information to reconstruct the reverse operation.
 | 
			
		||||
.Pp
 | 
			
		||||
If the first hunk of a patch fails,
 | 
			
		||||
.Nm
 | 
			
		||||
will reverse the hunk to see if it can be applied that way.
 | 
			
		||||
If it can, you will be asked if you want to have the
 | 
			
		||||
.Fl R
 | 
			
		||||
option set.
 | 
			
		||||
If it can't, the patch will continue to be applied normally.
 | 
			
		||||
(Note: this method cannot detect a reversed patch if it is a normal diff
 | 
			
		||||
and if the first command is an append (i.e. it should have been a delete)
 | 
			
		||||
since appends always succeed, due to the fact that a null context will match
 | 
			
		||||
anywhere.
 | 
			
		||||
Luckily, most patches add or change lines rather than delete them, so most
 | 
			
		||||
reversed normal diffs will begin with a delete, which will fail, triggering
 | 
			
		||||
the heuristic.)
 | 
			
		||||
.It Xo
 | 
			
		||||
.Fl r Ar rej-name ,
 | 
			
		||||
.Fl Fl reject-file Ar rej-name
 | 
			
		||||
.Xc
 | 
			
		||||
Causes the next argument to be interpreted as the reject file name.
 | 
			
		||||
.It Xo
 | 
			
		||||
.Fl s , Fl Fl quiet ,
 | 
			
		||||
.Fl Fl silent
 | 
			
		||||
.Xc
 | 
			
		||||
Makes
 | 
			
		||||
.Nm
 | 
			
		||||
do its work silently, unless an error occurs.
 | 
			
		||||
.It Fl t , Fl Fl batch
 | 
			
		||||
Similar to
 | 
			
		||||
.Fl f ,
 | 
			
		||||
in that it suppresses questions, but makes some different assumptions:
 | 
			
		||||
skip patches for which a file to patch can't be found (the same as
 | 
			
		||||
.Fl f ) ;
 | 
			
		||||
skip patches for which the file has the wrong version for the
 | 
			
		||||
.Qq Prereq:
 | 
			
		||||
line in the patch;
 | 
			
		||||
and assume that patches are reversed if they look like they are.
 | 
			
		||||
.It Fl u , Fl Fl unified
 | 
			
		||||
Forces
 | 
			
		||||
.Nm
 | 
			
		||||
to interpret the patch file as a unified context diff (a unidiff).
 | 
			
		||||
.It Xo
 | 
			
		||||
.Fl V Cm t | nil | never ,
 | 
			
		||||
.Fl Fl version-control Cm t | nil | never
 | 
			
		||||
.Xc
 | 
			
		||||
Causes the next argument to be interpreted as a method for creating
 | 
			
		||||
backup file names.
 | 
			
		||||
The type of backups made can also be given in the
 | 
			
		||||
.Ev PATCH_VERSION_CONTROL
 | 
			
		||||
or
 | 
			
		||||
.Ev VERSION_CONTROL
 | 
			
		||||
environment variables, which are overridden by this option.
 | 
			
		||||
The
 | 
			
		||||
.Fl B
 | 
			
		||||
option overrides this option, causing the prefix to always be used for
 | 
			
		||||
making backup file names.
 | 
			
		||||
The values of the
 | 
			
		||||
.Ev PATCH_VERSION_CONTROL
 | 
			
		||||
and
 | 
			
		||||
.Ev VERSION_CONTROL
 | 
			
		||||
environment variables and the argument to the
 | 
			
		||||
.Fl V
 | 
			
		||||
option are like the GNU Emacs
 | 
			
		||||
.Dq version-control
 | 
			
		||||
variable; they also recognize synonyms that are more descriptive.
 | 
			
		||||
The valid values are (unique abbreviations are accepted):
 | 
			
		||||
.Bl -tag -width Ds -offset indent
 | 
			
		||||
.It Cm t , numbered
 | 
			
		||||
Always make numbered backups.
 | 
			
		||||
.It Cm nil , existing
 | 
			
		||||
Make numbered backups of files that already have them,
 | 
			
		||||
simple backups of the others.
 | 
			
		||||
.It Cm never , simple
 | 
			
		||||
Always make simple backups.
 | 
			
		||||
.El
 | 
			
		||||
.It Fl v , Fl Fl version
 | 
			
		||||
Causes
 | 
			
		||||
.Nm
 | 
			
		||||
to print out its revision header and patch level.
 | 
			
		||||
.It Xo
 | 
			
		||||
.Fl x Ar number ,
 | 
			
		||||
.Fl Fl debug Ar number
 | 
			
		||||
.Xc
 | 
			
		||||
Sets internal debugging flags, and is of interest only to
 | 
			
		||||
.Nm
 | 
			
		||||
patchers.
 | 
			
		||||
.It Xo
 | 
			
		||||
.Fl z Ar backup-ext ,
 | 
			
		||||
.Fl Fl suffix Ar backup-ext
 | 
			
		||||
.Xc
 | 
			
		||||
Causes the next argument to be interpreted as the backup extension, to be
 | 
			
		||||
used in place of
 | 
			
		||||
.Qq .orig .
 | 
			
		||||
.It Fl Fl posix
 | 
			
		||||
Enables strict
 | 
			
		||||
.St -p1003.1-2004
 | 
			
		||||
conformance, specifically:
 | 
			
		||||
.Bl -enum
 | 
			
		||||
.It
 | 
			
		||||
Backup files are not created unless the
 | 
			
		||||
.Fl b
 | 
			
		||||
option is specified.
 | 
			
		||||
.It
 | 
			
		||||
If unspecified, the file name used is the first of the old, new and
 | 
			
		||||
index files that exists.
 | 
			
		||||
.El
 | 
			
		||||
.El
 | 
			
		||||
.Ss Patch Application
 | 
			
		||||
.Nm
 | 
			
		||||
will try to skip any leading garbage, apply the diff,
 | 
			
		||||
and then skip any trailing garbage.
 | 
			
		||||
Thus you could feed an article or message containing a
 | 
			
		||||
diff listing to
 | 
			
		||||
.Nm ,
 | 
			
		||||
and it should work.
 | 
			
		||||
If the entire diff is indented by a consistent amount,
 | 
			
		||||
this will be taken into account.
 | 
			
		||||
.Pp
 | 
			
		||||
With context diffs, and to a lesser extent with normal diffs,
 | 
			
		||||
.Nm
 | 
			
		||||
can detect when the line numbers mentioned in the patch are incorrect,
 | 
			
		||||
and will attempt to find the correct place to apply each hunk of the patch.
 | 
			
		||||
As a first guess, it takes the line number mentioned for the hunk, plus or
 | 
			
		||||
minus any offset used in applying the previous hunk.
 | 
			
		||||
If that is not the correct place,
 | 
			
		||||
.Nm
 | 
			
		||||
will scan both forwards and backwards for a set of lines matching the context
 | 
			
		||||
given in the hunk.
 | 
			
		||||
First
 | 
			
		||||
.Nm
 | 
			
		||||
looks for a place where all lines of the context match.
 | 
			
		||||
If no such place is found, and it's a context diff, and the maximum fuzz factor
 | 
			
		||||
is set to 1 or more, then another scan takes place ignoring the first and last
 | 
			
		||||
line of context.
 | 
			
		||||
If that fails, and the maximum fuzz factor is set to 2 or more,
 | 
			
		||||
the first two and last two lines of context are ignored,
 | 
			
		||||
and another scan is made.
 | 
			
		||||
.Pq The default maximum fuzz factor is 2.
 | 
			
		||||
.Pp
 | 
			
		||||
If
 | 
			
		||||
.Nm
 | 
			
		||||
cannot find a place to install that hunk of the patch, it will put the hunk
 | 
			
		||||
out to a reject file, which normally is the name of the output file plus
 | 
			
		||||
.Qq .rej .
 | 
			
		||||
(Note that the rejected hunk will come out in context diff form whether the
 | 
			
		||||
input patch was a context diff or a normal diff.
 | 
			
		||||
If the input was a normal diff, many of the contexts will simply be null.)
 | 
			
		||||
The line numbers on the hunks in the reject file may be different than
 | 
			
		||||
in the patch file: they reflect the approximate location patch thinks the
 | 
			
		||||
failed hunks belong in the new file rather than the old one.
 | 
			
		||||
.Pp
 | 
			
		||||
As each hunk is completed, you will be told whether the hunk succeeded or
 | 
			
		||||
failed, and which line (in the new file)
 | 
			
		||||
.Nm
 | 
			
		||||
thought the hunk should go on.
 | 
			
		||||
If this is different from the line number specified in the diff,
 | 
			
		||||
you will be told the offset.
 | 
			
		||||
A single large offset MAY be an indication that a hunk was installed in the
 | 
			
		||||
wrong place.
 | 
			
		||||
You will also be told if a fuzz factor was used to make the match, in which
 | 
			
		||||
case you should also be slightly suspicious.
 | 
			
		||||
.Ss Filename Determination
 | 
			
		||||
If no original file is specified on the command line,
 | 
			
		||||
.Nm
 | 
			
		||||
will try to figure out from the leading garbage what the name of the file
 | 
			
		||||
to edit is.
 | 
			
		||||
When checking a prospective file name, pathname components are stripped
 | 
			
		||||
as specified by the
 | 
			
		||||
.Fl p
 | 
			
		||||
option and the file's existence and writability are checked relative
 | 
			
		||||
to the current working directory (or the directory specified by the
 | 
			
		||||
.Fl d
 | 
			
		||||
option).
 | 
			
		||||
.Pp
 | 
			
		||||
If the diff is a context or unified diff,
 | 
			
		||||
.Nm
 | 
			
		||||
is able to determine the old and new file names from the diff header.
 | 
			
		||||
For context diffs, the
 | 
			
		||||
.Dq old
 | 
			
		||||
file is specified in the line beginning with
 | 
			
		||||
.Qq ***
 | 
			
		||||
and the
 | 
			
		||||
.Dq new
 | 
			
		||||
file is specified in the line beginning with
 | 
			
		||||
.Qq --- .
 | 
			
		||||
For a unified diff, the
 | 
			
		||||
.Dq old
 | 
			
		||||
file is specified in the line beginning with
 | 
			
		||||
.Qq ---
 | 
			
		||||
and the
 | 
			
		||||
.Dq new
 | 
			
		||||
file is specified in the line beginning with
 | 
			
		||||
.Qq +++ .
 | 
			
		||||
If there is an
 | 
			
		||||
.Qq Index:
 | 
			
		||||
line in the leading garbage (regardless of the diff type),
 | 
			
		||||
.Nm
 | 
			
		||||
will use the file name from that line as the
 | 
			
		||||
.Dq index
 | 
			
		||||
file.
 | 
			
		||||
.Pp
 | 
			
		||||
.Nm
 | 
			
		||||
will choose the file name by performing the following steps, with the first
 | 
			
		||||
match used:
 | 
			
		||||
.Bl -enum
 | 
			
		||||
.It
 | 
			
		||||
If
 | 
			
		||||
.Nm
 | 
			
		||||
is operating in strict
 | 
			
		||||
.St -p1003.1-2004
 | 
			
		||||
mode, the first of the
 | 
			
		||||
.Dq old ,
 | 
			
		||||
.Dq new
 | 
			
		||||
and
 | 
			
		||||
.Dq index
 | 
			
		||||
file names that exist is used.
 | 
			
		||||
Otherwise,
 | 
			
		||||
.Nm
 | 
			
		||||
will examine either the
 | 
			
		||||
.Dq old
 | 
			
		||||
and
 | 
			
		||||
.Dq new
 | 
			
		||||
file names or, for a non-context diff, the
 | 
			
		||||
.Dq index
 | 
			
		||||
file name, and choose the file name with the fewest path components,
 | 
			
		||||
the shortest basename, and the shortest total file name length (in that order).
 | 
			
		||||
.It
 | 
			
		||||
If no file exists,
 | 
			
		||||
.Nm
 | 
			
		||||
checks for the existence of the files in an SCCS or RCS directory
 | 
			
		||||
(using the appropriate prefix or suffix) using the criteria specified
 | 
			
		||||
above.
 | 
			
		||||
If found,
 | 
			
		||||
.Nm
 | 
			
		||||
will attempt to get or check out the file.
 | 
			
		||||
.It
 | 
			
		||||
If no suitable file was found to patch, the patch file is a context or
 | 
			
		||||
unified diff, and the old file was zero length, the new file name is
 | 
			
		||||
created and used.
 | 
			
		||||
.It
 | 
			
		||||
If the file name still cannot be determined,
 | 
			
		||||
.Nm
 | 
			
		||||
will prompt the user for the file name to use.
 | 
			
		||||
.El
 | 
			
		||||
.Pp
 | 
			
		||||
Additionally, if the leading garbage contains a
 | 
			
		||||
.Qq Prereq:\ \&
 | 
			
		||||
line,
 | 
			
		||||
.Nm
 | 
			
		||||
will take the first word from the prerequisites line (normally a version
 | 
			
		||||
number) and check the input file to see if that word can be found.
 | 
			
		||||
If not,
 | 
			
		||||
.Nm
 | 
			
		||||
will ask for confirmation before proceeding.
 | 
			
		||||
.Pp
 | 
			
		||||
The upshot of all this is that you should be able to say, while in a news
 | 
			
		||||
interface, the following:
 | 
			
		||||
.Pp
 | 
			
		||||
.Dl | patch -d /usr/src/local/blurfl
 | 
			
		||||
.Pp
 | 
			
		||||
and patch a file in the blurfl directory directly from the article containing
 | 
			
		||||
the patch.
 | 
			
		||||
.Ss Backup Files
 | 
			
		||||
By default, the patched version is put in place of the original, with
 | 
			
		||||
the original file backed up to the same name with the extension
 | 
			
		||||
.Qq .orig ,
 | 
			
		||||
or as specified by the
 | 
			
		||||
.Fl B ,
 | 
			
		||||
.Fl V ,
 | 
			
		||||
or
 | 
			
		||||
.Fl z
 | 
			
		||||
options.
 | 
			
		||||
The extension used for making backup files may also be specified in the
 | 
			
		||||
.Ev SIMPLE_BACKUP_SUFFIX
 | 
			
		||||
environment variable, which is overridden by the options above.
 | 
			
		||||
.Pp
 | 
			
		||||
If the backup file is a symbolic or hard link to the original file,
 | 
			
		||||
.Nm
 | 
			
		||||
creates a new backup file name by changing the first lowercase letter
 | 
			
		||||
in the last component of the file's name into uppercase.
 | 
			
		||||
If there are no more lowercase letters in the name,
 | 
			
		||||
it removes the first character from the name.
 | 
			
		||||
It repeats this process until it comes up with a
 | 
			
		||||
backup file that does not already exist or is not linked to the original file.
 | 
			
		||||
.Pp
 | 
			
		||||
You may also specify where you want the output to go with the
 | 
			
		||||
.Fl o
 | 
			
		||||
option; if that file already exists, it is backed up first.
 | 
			
		||||
.Ss Notes For Patch Senders
 | 
			
		||||
There are several things you should bear in mind if you are going to
 | 
			
		||||
be sending out patches:
 | 
			
		||||
.Pp
 | 
			
		||||
First, you can save people a lot of grief by keeping a
 | 
			
		||||
.Pa patchlevel.h
 | 
			
		||||
file which is patched to increment the patch level as the first diff in the
 | 
			
		||||
patch file you send out.
 | 
			
		||||
If you put a
 | 
			
		||||
.Qq Prereq:
 | 
			
		||||
line in with the patch, it won't let them apply
 | 
			
		||||
patches out of order without some warning.
 | 
			
		||||
.Pp
 | 
			
		||||
Second, make sure you've specified the file names right, either in a
 | 
			
		||||
context diff header, or with an
 | 
			
		||||
.Qq Index:
 | 
			
		||||
line.
 | 
			
		||||
If you are patching something in a subdirectory, be sure to tell the patch
 | 
			
		||||
user to specify a
 | 
			
		||||
.Fl p
 | 
			
		||||
option as needed.
 | 
			
		||||
.Pp
 | 
			
		||||
Third, you can create a file by sending out a diff that compares a
 | 
			
		||||
null file to the file you want to create.
 | 
			
		||||
This will only work if the file you want to create doesn't exist already in
 | 
			
		||||
the target directory.
 | 
			
		||||
.Pp
 | 
			
		||||
Fourth, take care not to send out reversed patches, since it makes people wonder
 | 
			
		||||
whether they already applied the patch.
 | 
			
		||||
.Pp
 | 
			
		||||
Fifth, while you may be able to get away with putting 582 diff listings into
 | 
			
		||||
one file, it is probably wiser to group related patches into separate files in
 | 
			
		||||
case something goes haywire.
 | 
			
		||||
.Sh ENVIRONMENT
 | 
			
		||||
.Bl -tag -width "PATCH_VERSION_CONTROL" -compact
 | 
			
		||||
.It Ev POSIXLY_CORRECT
 | 
			
		||||
When set,
 | 
			
		||||
.Nm
 | 
			
		||||
behaves as if the
 | 
			
		||||
.Fl Fl posix
 | 
			
		||||
option has been specified.
 | 
			
		||||
.It Ev SIMPLE_BACKUP_SUFFIX
 | 
			
		||||
Extension to use for backup file names instead of
 | 
			
		||||
.Qq .orig .
 | 
			
		||||
.It Ev TMPDIR
 | 
			
		||||
Directory to put temporary files in; default is
 | 
			
		||||
.Pa /tmp .
 | 
			
		||||
.It Ev PATCH_VERSION_CONTROL
 | 
			
		||||
Selects when numbered backup files are made.
 | 
			
		||||
.It Ev VERSION_CONTROL
 | 
			
		||||
Same as
 | 
			
		||||
.Ev PATCH_VERSION_CONTROL .
 | 
			
		||||
.El
 | 
			
		||||
.Sh FILES
 | 
			
		||||
.Bl -tag -width "$TMPDIR/patch*" -compact
 | 
			
		||||
.It Pa $TMPDIR/patch*
 | 
			
		||||
.Nm
 | 
			
		||||
temporary files
 | 
			
		||||
.It Pa /dev/tty
 | 
			
		||||
used to read input when
 | 
			
		||||
.Nm
 | 
			
		||||
prompts the user
 | 
			
		||||
.El
 | 
			
		||||
.Sh DIAGNOSTICS
 | 
			
		||||
Too many to list here, but generally indicative that
 | 
			
		||||
.Nm
 | 
			
		||||
couldn't parse your patch file.
 | 
			
		||||
.Pp
 | 
			
		||||
The message
 | 
			
		||||
.Qq Hmm...
 | 
			
		||||
indicates that there is unprocessed text in the patch file and that
 | 
			
		||||
.Nm
 | 
			
		||||
is attempting to intuit whether there is a patch in that text and, if so,
 | 
			
		||||
what kind of patch it is.
 | 
			
		||||
.Pp
 | 
			
		||||
The
 | 
			
		||||
.Nm
 | 
			
		||||
utility exits with one of the following values:
 | 
			
		||||
.Pp
 | 
			
		||||
.Bl -tag -width Ds -compact -offset indent
 | 
			
		||||
.It \&0
 | 
			
		||||
Successful completion.
 | 
			
		||||
.It \&1
 | 
			
		||||
One or more lines were written to a reject file.
 | 
			
		||||
.It \*[Gt]\&1
 | 
			
		||||
An error occurred.
 | 
			
		||||
.El
 | 
			
		||||
.Pp
 | 
			
		||||
When applying a set of patches in a loop it behooves you to check this
 | 
			
		||||
exit status so you don't apply a later patch to a partially patched file.
 | 
			
		||||
.Sh SEE ALSO
 | 
			
		||||
.Xr diff 1
 | 
			
		||||
.Sh STANDARDS
 | 
			
		||||
The
 | 
			
		||||
.Nm
 | 
			
		||||
utility is compliant with the
 | 
			
		||||
.St -p1003.1-2004
 | 
			
		||||
specification
 | 
			
		||||
(except as detailed above for the
 | 
			
		||||
.Fl -posix
 | 
			
		||||
option),
 | 
			
		||||
though the presence of
 | 
			
		||||
.Nm
 | 
			
		||||
itself is optional.
 | 
			
		||||
.Pp
 | 
			
		||||
The flags
 | 
			
		||||
.Op Fl CEfstuvBFVxz
 | 
			
		||||
and
 | 
			
		||||
.Op Fl -posix
 | 
			
		||||
are extensions to that specification.
 | 
			
		||||
.Sh AUTHORS
 | 
			
		||||
.An Larry Wall
 | 
			
		||||
with many other contributors.
 | 
			
		||||
.Sh CAVEATS
 | 
			
		||||
.Nm
 | 
			
		||||
cannot tell if the line numbers are off in an ed script, and can only detect
 | 
			
		||||
bad line numbers in a normal diff when it finds a
 | 
			
		||||
.Qq change
 | 
			
		||||
or a
 | 
			
		||||
.Qq delete
 | 
			
		||||
command.
 | 
			
		||||
A context diff using fuzz factor 3 may have the same problem.
 | 
			
		||||
Until a suitable interactive interface is added, you should probably do
 | 
			
		||||
a context diff in these cases to see if the changes made sense.
 | 
			
		||||
Of course, compiling without errors is a pretty good indication that the patch
 | 
			
		||||
worked, but not always.
 | 
			
		||||
.Pp
 | 
			
		||||
.Nm
 | 
			
		||||
usually produces the correct results, even when it has to do a lot of
 | 
			
		||||
guessing.
 | 
			
		||||
However, the results are guaranteed to be correct only when the patch is
 | 
			
		||||
applied to exactly the same version of the file that the patch was
 | 
			
		||||
generated from.
 | 
			
		||||
.Sh BUGS
 | 
			
		||||
Could be smarter about partial matches, excessively deviant offsets and
 | 
			
		||||
swapped code, but that would take an extra pass.
 | 
			
		||||
.Pp
 | 
			
		||||
Check patch mode
 | 
			
		||||
.Pq Fl C
 | 
			
		||||
will fail if you try to check several patches in succession that build on
 | 
			
		||||
each other.
 | 
			
		||||
The entire
 | 
			
		||||
.Nm
 | 
			
		||||
code would have to be restructured to keep temporary files around so that it
 | 
			
		||||
can handle this situation.
 | 
			
		||||
.Pp
 | 
			
		||||
If code has been duplicated (for instance with #ifdef OLDCODE ... #else ...
 | 
			
		||||
#endif),
 | 
			
		||||
.Nm
 | 
			
		||||
is incapable of patching both versions, and, if it works at all, will likely
 | 
			
		||||
patch the wrong one, and tell you that it succeeded to boot.
 | 
			
		||||
.Pp
 | 
			
		||||
If you apply a patch you've already applied,
 | 
			
		||||
.Nm
 | 
			
		||||
will think it is a reversed patch, and offer to un-apply the patch.
 | 
			
		||||
This could be construed as a feature.
 | 
			
		||||
							
								
								
									
										1064
									
								
								commands/patch/patch.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1064
									
								
								commands/patch/patch.c
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										14
									
								
								commands/patch/pathnames.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										14
									
								
								commands/patch/pathnames.h
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,14 @@
 | 
			
		||||
/*
 | 
			
		||||
 * $OpenBSD: pathnames.h,v 1.1 2003/07/29 20:10:17 millert Exp $
 | 
			
		||||
 * $DragonFly: src/usr.bin/patch/pathnames.h,v 1.2 2008/08/11 00:04:12 joerg Exp $
 | 
			
		||||
 * $NetBSD: pathnames.h,v 1.1 2008/09/19 18:33:34 joerg Exp $
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Placed in the public domain by Todd C. Miller <Todd.Miller@courtesan.com>
 | 
			
		||||
 * on July 29, 2003.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include <paths.h>
 | 
			
		||||
 | 
			
		||||
#define	_PATH_ED		"/bin/ed"
 | 
			
		||||
							
								
								
									
										1555
									
								
								commands/patch/pch.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1555
									
								
								commands/patch/pch.c
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										59
									
								
								commands/patch/pch.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										59
									
								
								commands/patch/pch.h
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,59 @@
 | 
			
		||||
/*
 | 
			
		||||
 * $OpenBSD: pch.h,v 1.9 2003/10/31 20:20:45 millert Exp $
 | 
			
		||||
 * $DragonFly: src/usr.bin/patch/pch.h,v 1.1 2004/09/24 18:44:28 joerg Exp $
 | 
			
		||||
 * $NetBSD: pch.h,v 1.10 2008/09/19 18:33:34 joerg Exp $
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * patch - a program to apply diffs to original files
 | 
			
		||||
 * 
 | 
			
		||||
 * 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.
 | 
			
		||||
 * 
 | 
			
		||||
 * -C option added in 1998, original code by Marc Espie, based on FreeBSD
 | 
			
		||||
 * behaviour
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#define OLD_FILE	0
 | 
			
		||||
#define NEW_FILE	1
 | 
			
		||||
#define INDEX_FILE	2
 | 
			
		||||
#define MAX_FILE	3
 | 
			
		||||
 | 
			
		||||
struct file_name {
 | 
			
		||||
	char *path;
 | 
			
		||||
	bool exists;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
void		re_patch(void);
 | 
			
		||||
void		open_patch_file(const char *);
 | 
			
		||||
void		set_hunkmax(void);
 | 
			
		||||
bool		there_is_another_patch(void);
 | 
			
		||||
bool		another_hunk(void);
 | 
			
		||||
bool		pch_swap(void);
 | 
			
		||||
char		*pfetch(LINENUM);
 | 
			
		||||
short		pch_line_len(LINENUM);
 | 
			
		||||
LINENUM		pch_first(void);
 | 
			
		||||
LINENUM		pch_ptrn_lines(void);
 | 
			
		||||
LINENUM		pch_newfirst(void);
 | 
			
		||||
LINENUM		pch_repl_lines(void);
 | 
			
		||||
LINENUM		pch_end(void);
 | 
			
		||||
LINENUM		pch_context(void);
 | 
			
		||||
LINENUM		pch_hunk_beg(void);
 | 
			
		||||
char		pch_char(LINENUM);
 | 
			
		||||
void		do_ed_script(void);
 | 
			
		||||
							
								
								
									
										436
									
								
								commands/patch/util.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										436
									
								
								commands/patch/util.c
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,436 @@
 | 
			
		||||
/*
 | 
			
		||||
 * $OpenBSD: util.c,v 1.32 2006/03/11 19:41:30 otto Exp $
 | 
			
		||||
 * $DragonFly: src/usr.bin/patch/util.c,v 1.9 2007/09/29 23:11:10 swildner Exp $
 | 
			
		||||
 * $NetBSD: util.c,v 1.24 2008/09/19 18:33:34 joerg Exp $
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * patch - a program to apply diffs to original files
 | 
			
		||||
 * 
 | 
			
		||||
 * 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.
 | 
			
		||||
 * 
 | 
			
		||||
 * -C option added in 1998, original code by Marc Espie, based on FreeBSD
 | 
			
		||||
 * behaviour
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include <sys/cdefs.h>
 | 
			
		||||
__RCSID("$NetBSD: util.c,v 1.24 2008/09/19 18:33:34 joerg Exp $");
 | 
			
		||||
 | 
			
		||||
#include <sys/param.h>
 | 
			
		||||
#include <sys/stat.h>
 | 
			
		||||
 | 
			
		||||
#include <ctype.h>
 | 
			
		||||
#include <errno.h>
 | 
			
		||||
#include <fcntl.h>
 | 
			
		||||
#include <libgen.h>
 | 
			
		||||
#include <paths.h>
 | 
			
		||||
#include <signal.h>
 | 
			
		||||
#include <stdarg.h>
 | 
			
		||||
#include <stdlib.h>
 | 
			
		||||
#include <stdio.h>
 | 
			
		||||
#include <string.h>
 | 
			
		||||
#include <unistd.h>
 | 
			
		||||
 | 
			
		||||
#include "common.h"
 | 
			
		||||
#include "util.h"
 | 
			
		||||
#include "backupfile.h"
 | 
			
		||||
#include "pathnames.h"
 | 
			
		||||
 | 
			
		||||
/* Rename a file, copying it if necessary. */
 | 
			
		||||
 | 
			
		||||
int
 | 
			
		||||
move_file(const char *from, const char *to)
 | 
			
		||||
{
 | 
			
		||||
	int	fromfd;
 | 
			
		||||
	ssize_t	i;
 | 
			
		||||
 | 
			
		||||
	/* to stdout? */
 | 
			
		||||
 | 
			
		||||
	if (strEQ(to, "-")) {
 | 
			
		||||
#ifdef DEBUGGING
 | 
			
		||||
		if (debug & 4)
 | 
			
		||||
			say("Moving %s to stdout.\n", from);
 | 
			
		||||
#endif
 | 
			
		||||
		fromfd = open(from, O_RDONLY);
 | 
			
		||||
		if (fromfd < 0)
 | 
			
		||||
			pfatal("internal error, can't reopen %s", from);
 | 
			
		||||
		while ((i = read(fromfd, buf, buf_len)) > 0)
 | 
			
		||||
			if (write(STDOUT_FILENO, buf, i) != i)
 | 
			
		||||
				pfatal("write failed");
 | 
			
		||||
		close(fromfd);
 | 
			
		||||
		return 0;
 | 
			
		||||
	}
 | 
			
		||||
	if (backup_file(to) < 0) {
 | 
			
		||||
		say("Can't backup %s, output is in %s: %s\n", to, from,
 | 
			
		||||
		    strerror(errno));
 | 
			
		||||
		return -1;
 | 
			
		||||
	}
 | 
			
		||||
#ifdef DEBUGGING
 | 
			
		||||
	if (debug & 4)
 | 
			
		||||
		say("Moving %s to %s.\n", from, to);
 | 
			
		||||
#endif
 | 
			
		||||
	if (rename(from, to) < 0) {
 | 
			
		||||
		if (errno != EXDEV || copy_file(from, to) < 0) {
 | 
			
		||||
			say("Can't create %s, output is in %s: %s\n",
 | 
			
		||||
			    to, from, strerror(errno));
 | 
			
		||||
			return -1;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Backup the original file.  */
 | 
			
		||||
 | 
			
		||||
int
 | 
			
		||||
backup_file(const char *orig)
 | 
			
		||||
{
 | 
			
		||||
	struct stat	filestat;
 | 
			
		||||
	char		bakname[MAXPATHLEN], *s, *simplename;
 | 
			
		||||
	dev_t		orig_device;
 | 
			
		||||
	ino_t		orig_inode;
 | 
			
		||||
 | 
			
		||||
	if (backup_type == none || stat(orig, &filestat) != 0)
 | 
			
		||||
		return 0;			/* nothing to do */
 | 
			
		||||
	/*
 | 
			
		||||
	 * If the user used zero prefixes or suffixes, then
 | 
			
		||||
	 * he doesn't want backups.  Yet we have to remove
 | 
			
		||||
	 * orig to break possible hardlinks.
 | 
			
		||||
	 */
 | 
			
		||||
	if ((origprae && *origprae == 0) || *simple_backup_suffix == 0) {
 | 
			
		||||
		unlink(orig);
 | 
			
		||||
		return 0;
 | 
			
		||||
	}
 | 
			
		||||
	orig_device = filestat.st_dev;
 | 
			
		||||
	orig_inode = filestat.st_ino;
 | 
			
		||||
 | 
			
		||||
	if (origprae) {
 | 
			
		||||
		if (strlcpy(bakname, origprae, sizeof(bakname)) >= sizeof(bakname) ||
 | 
			
		||||
		    strlcat(bakname, orig, sizeof(bakname)) >= sizeof(bakname))
 | 
			
		||||
			fatal("filename %s too long for buffer\n", origprae);
 | 
			
		||||
	} else {
 | 
			
		||||
		if ((s = find_backup_file_name(orig)) == NULL)
 | 
			
		||||
			fatal("out of memory\n");
 | 
			
		||||
		if (strlcpy(bakname, s, sizeof(bakname)) >= sizeof(bakname))
 | 
			
		||||
			fatal("filename %s too long for buffer\n", s);
 | 
			
		||||
		free(s);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if ((simplename = strrchr(bakname, '/')) != NULL)
 | 
			
		||||
		simplename = simplename + 1;
 | 
			
		||||
	else
 | 
			
		||||
		simplename = bakname;
 | 
			
		||||
 | 
			
		||||
	/*
 | 
			
		||||
	 * Find a backup name that is not the same file. Change the
 | 
			
		||||
	 * first lowercase char into uppercase; if that isn't
 | 
			
		||||
	 * sufficient, chop off the first char and try again.
 | 
			
		||||
	 */
 | 
			
		||||
	while (stat(bakname, &filestat) == 0 &&
 | 
			
		||||
	    orig_device == filestat.st_dev && orig_inode == filestat.st_ino) {
 | 
			
		||||
		/* Skip initial non-lowercase chars.  */
 | 
			
		||||
		for (s = simplename; *s && !islower((unsigned char)*s); s++)
 | 
			
		||||
			;
 | 
			
		||||
		if (*s)
 | 
			
		||||
			*s = toupper((unsigned char)*s);
 | 
			
		||||
		else
 | 
			
		||||
			memmove(simplename, simplename + 1,
 | 
			
		||||
			    strlen(simplename + 1) + 1);
 | 
			
		||||
	}
 | 
			
		||||
#ifdef DEBUGGING
 | 
			
		||||
	if (debug & 4)
 | 
			
		||||
		say("Moving %s to %s.\n", orig, bakname);
 | 
			
		||||
#endif
 | 
			
		||||
	if (rename(orig, bakname) < 0) {
 | 
			
		||||
		if (errno != EXDEV || copy_file(orig, bakname) < 0)
 | 
			
		||||
			return -1;
 | 
			
		||||
	}
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Copy a file.
 | 
			
		||||
 */
 | 
			
		||||
int
 | 
			
		||||
copy_file(const char *from, const char *to)
 | 
			
		||||
{
 | 
			
		||||
	int	tofd, fromfd;
 | 
			
		||||
	ssize_t	i;
 | 
			
		||||
 | 
			
		||||
	tofd = open(to, O_CREAT|O_TRUNC|O_WRONLY, 0666);
 | 
			
		||||
	if (tofd < 0)
 | 
			
		||||
		return -1;
 | 
			
		||||
	fromfd = open(from, O_RDONLY, 0);
 | 
			
		||||
	if (fromfd < 0)
 | 
			
		||||
		pfatal("internal error, can't reopen %s", from);
 | 
			
		||||
	while ((i = read(fromfd, buf, buf_len)) > 0)
 | 
			
		||||
		if (write(tofd, buf, i) != i)
 | 
			
		||||
			pfatal("write to %s failed", to);
 | 
			
		||||
	close(fromfd);
 | 
			
		||||
	close(tofd);
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Allocate a unique area for a string.
 | 
			
		||||
 */
 | 
			
		||||
char *
 | 
			
		||||
savestr(const char *s)
 | 
			
		||||
{
 | 
			
		||||
	char	*rv;
 | 
			
		||||
 | 
			
		||||
	if (!s)
 | 
			
		||||
		s = "Oops";
 | 
			
		||||
	rv = strdup(s);
 | 
			
		||||
	if (rv == NULL) {
 | 
			
		||||
		if (using_plan_a)
 | 
			
		||||
			out_of_mem = true;
 | 
			
		||||
		else
 | 
			
		||||
			fatal("out of memory\n");
 | 
			
		||||
	}
 | 
			
		||||
	return rv;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Vanilla terminal output (buffered).
 | 
			
		||||
 */
 | 
			
		||||
void
 | 
			
		||||
say(const char *fmt, ...)
 | 
			
		||||
{
 | 
			
		||||
	va_list	ap;
 | 
			
		||||
 | 
			
		||||
	va_start(ap, fmt);
 | 
			
		||||
	vfprintf(stderr, fmt, ap);
 | 
			
		||||
	va_end(ap);
 | 
			
		||||
	fflush(stderr);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Terminal output, pun intended.
 | 
			
		||||
 */
 | 
			
		||||
void
 | 
			
		||||
fatal(const char *fmt, ...)
 | 
			
		||||
{
 | 
			
		||||
	va_list	ap;
 | 
			
		||||
 | 
			
		||||
	va_start(ap, fmt);
 | 
			
		||||
	fprintf(stderr, "patch: **** ");
 | 
			
		||||
	vfprintf(stderr, fmt, ap);
 | 
			
		||||
	va_end(ap);
 | 
			
		||||
	my_exit(2);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Say something from patch, something from the system, then silence . . .
 | 
			
		||||
 */
 | 
			
		||||
void
 | 
			
		||||
pfatal(const char *fmt, ...)
 | 
			
		||||
{
 | 
			
		||||
	va_list	ap;
 | 
			
		||||
	int	errnum = errno;
 | 
			
		||||
 | 
			
		||||
	fprintf(stderr, "patch: **** ");
 | 
			
		||||
	va_start(ap, fmt);
 | 
			
		||||
	vfprintf(stderr, fmt, ap);
 | 
			
		||||
	va_end(ap);
 | 
			
		||||
	fprintf(stderr, ": %s\n", strerror(errnum));
 | 
			
		||||
	my_exit(2);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Get a response from the user via /dev/tty
 | 
			
		||||
 */
 | 
			
		||||
void
 | 
			
		||||
ask(const char *fmt, ...)
 | 
			
		||||
{
 | 
			
		||||
	va_list	ap;
 | 
			
		||||
	ssize_t	nr = 0;
 | 
			
		||||
	static	int ttyfd = -1;
 | 
			
		||||
 | 
			
		||||
	va_start(ap, fmt);
 | 
			
		||||
	vfprintf(stdout, fmt, ap);
 | 
			
		||||
	va_end(ap);
 | 
			
		||||
	fflush(stdout);
 | 
			
		||||
	if (ttyfd < 0)
 | 
			
		||||
		ttyfd = open(_PATH_TTY, O_RDONLY);
 | 
			
		||||
	if (ttyfd >= 0) {
 | 
			
		||||
		if ((nr = read(ttyfd, buf, buf_len)) > 0 &&
 | 
			
		||||
		    buf[nr - 1] == '\n')
 | 
			
		||||
			buf[nr - 1] = '\0';
 | 
			
		||||
	}
 | 
			
		||||
	if (ttyfd < 0 || nr <= 0) {
 | 
			
		||||
		/* no tty or error reading, pretend user entered 'return' */
 | 
			
		||||
		putchar('\n');
 | 
			
		||||
		buf[0] = '\0';
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * How to handle certain events when not in a critical region.
 | 
			
		||||
 */
 | 
			
		||||
void
 | 
			
		||||
set_signals(int reset)
 | 
			
		||||
{
 | 
			
		||||
	static sig_t	hupval, intval;
 | 
			
		||||
 | 
			
		||||
	if (!reset) {
 | 
			
		||||
		hupval = signal(SIGHUP, SIG_IGN);
 | 
			
		||||
		if (hupval != SIG_IGN)
 | 
			
		||||
			hupval = my_exit;
 | 
			
		||||
		intval = signal(SIGINT, SIG_IGN);
 | 
			
		||||
		if (intval != SIG_IGN)
 | 
			
		||||
			intval = my_exit;
 | 
			
		||||
	}
 | 
			
		||||
	signal(SIGHUP, hupval);
 | 
			
		||||
	signal(SIGINT, intval);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * How to handle certain events when in a critical region.
 | 
			
		||||
 */
 | 
			
		||||
void
 | 
			
		||||
ignore_signals(void)
 | 
			
		||||
{
 | 
			
		||||
	signal(SIGHUP, SIG_IGN);
 | 
			
		||||
	signal(SIGINT, SIG_IGN);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Make sure we'll have the directories to create a file. If `striplast' is
 | 
			
		||||
 * true, ignore the last element of `filename'.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
makedirs(const char *filename, bool striplast)
 | 
			
		||||
{
 | 
			
		||||
	char	*tmpbuf;
 | 
			
		||||
 | 
			
		||||
	if ((tmpbuf = strdup(filename)) == NULL)
 | 
			
		||||
		fatal("out of memory\n");
 | 
			
		||||
 | 
			
		||||
	if (striplast) {
 | 
			
		||||
		char	*s = strrchr(tmpbuf, '/');
 | 
			
		||||
		if (s == NULL)
 | 
			
		||||
			return;	/* nothing to be done */
 | 
			
		||||
		*s = '\0';
 | 
			
		||||
	}
 | 
			
		||||
	if (mkpath(tmpbuf) != 0)
 | 
			
		||||
		pfatal("creation of %s failed", tmpbuf);
 | 
			
		||||
	free(tmpbuf);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Make filenames more reasonable.
 | 
			
		||||
 */
 | 
			
		||||
char *
 | 
			
		||||
fetchname(const char *at, bool *exists, int strip_leading)
 | 
			
		||||
{
 | 
			
		||||
	char		*fullname, *name, *t;
 | 
			
		||||
	int		sleading, tab;
 | 
			
		||||
	struct stat	filestat;
 | 
			
		||||
 | 
			
		||||
	if (at == NULL || *at == '\0')
 | 
			
		||||
		return NULL;
 | 
			
		||||
	while (isspace((unsigned char)*at))
 | 
			
		||||
		at++;
 | 
			
		||||
#ifdef DEBUGGING
 | 
			
		||||
	if (debug & 128)
 | 
			
		||||
		say("fetchname %s %d\n", at, strip_leading);
 | 
			
		||||
#endif
 | 
			
		||||
	/* So files can be created by diffing against /dev/null.  */
 | 
			
		||||
	if (strnEQ(at, _PATH_DEVNULL, sizeof(_PATH_DEVNULL) - 1))
 | 
			
		||||
		return NULL;
 | 
			
		||||
	name = fullname = t = savestr(at);
 | 
			
		||||
 | 
			
		||||
	tab = strchr(t, '\t') != NULL;
 | 
			
		||||
	/* Strip off up to `strip_leading' path components and NUL terminate. */
 | 
			
		||||
	for (sleading = strip_leading; *t != '\0' && ((tab && *t != '\t') ||
 | 
			
		||||
	    !isspace((unsigned char)*t)); t++) {
 | 
			
		||||
		if (t[0] == '/' && t[1] != '/' && t[1] != '\0')
 | 
			
		||||
			if (--sleading >= 0)
 | 
			
		||||
				name = t + 1;
 | 
			
		||||
	}
 | 
			
		||||
	*t = '\0';
 | 
			
		||||
 | 
			
		||||
	/*
 | 
			
		||||
	 * If no -p option was given (957 is the default value!), we were
 | 
			
		||||
	 * given a relative pathname, and the leading directories that we
 | 
			
		||||
	 * just stripped off all exist, put them back on.
 | 
			
		||||
	 */
 | 
			
		||||
	if (strip_leading == 957 && name != fullname && *fullname != '/') {
 | 
			
		||||
		name[-1] = '\0';
 | 
			
		||||
		if (stat(fullname, &filestat) == 0 && S_ISDIR(filestat.st_mode)) {
 | 
			
		||||
			name[-1] = '/';
 | 
			
		||||
			name = fullname;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	name = savestr(name);
 | 
			
		||||
	free(fullname);
 | 
			
		||||
 | 
			
		||||
	*exists = stat(name, &filestat) == 0;
 | 
			
		||||
	return name;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Takes the name returned by fetchname and looks in RCS/SCCS directories
 | 
			
		||||
 * for a checked in version.
 | 
			
		||||
 */
 | 
			
		||||
char *
 | 
			
		||||
checked_in(char *file)
 | 
			
		||||
{
 | 
			
		||||
	char		*filebase, *filedir, tmpbuf[MAXPATHLEN];
 | 
			
		||||
	struct stat	filestat;
 | 
			
		||||
 | 
			
		||||
	filebase = basename(file);
 | 
			
		||||
	filedir = dirname(file);
 | 
			
		||||
 | 
			
		||||
#define try(f, a1, a2, a3) \
 | 
			
		||||
(snprintf(tmpbuf, sizeof tmpbuf, f, a1, a2, a3), stat(tmpbuf, &filestat) == 0)
 | 
			
		||||
 | 
			
		||||
	if (try("%s/RCS/%s%s", filedir, filebase, RCSSUFFIX) ||
 | 
			
		||||
	    try("%s/RCS/%s%s", filedir, filebase, "") ||
 | 
			
		||||
	    try("%s/%s%s", filedir, filebase, RCSSUFFIX) ||
 | 
			
		||||
	    try("%s/SCCS/%s%s", filedir, SCCSPREFIX, filebase) ||
 | 
			
		||||
	    try("%s/%s%s", filedir, SCCSPREFIX, filebase))
 | 
			
		||||
		return file;
 | 
			
		||||
 | 
			
		||||
	return NULL;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
version(void)
 | 
			
		||||
{
 | 
			
		||||
	fprintf(stderr, "Patch version 2.0-12u8-NetBSD\n");
 | 
			
		||||
	my_exit(EXIT_SUCCESS);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Exit with cleanup.
 | 
			
		||||
 */
 | 
			
		||||
void
 | 
			
		||||
my_exit(int status)
 | 
			
		||||
{
 | 
			
		||||
	unlink(TMPINNAME);
 | 
			
		||||
	if (!toutkeep)
 | 
			
		||||
		unlink(TMPOUTNAME);
 | 
			
		||||
	if (!trejkeep)
 | 
			
		||||
		unlink(TMPREJNAME);
 | 
			
		||||
	unlink(TMPPATNAME);
 | 
			
		||||
	exit(status);
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										54
									
								
								commands/patch/util.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										54
									
								
								commands/patch/util.h
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,54 @@
 | 
			
		||||
/*
 | 
			
		||||
 * $OpenBSD: util.h,v 1.15 2005/06/20 07:14:06 otto Exp $
 | 
			
		||||
 * $DragonFly: src/usr.bin/patch/util.h,v 1.2 2007/09/29 23:11:10 swildner Exp $
 | 
			
		||||
 * $NetBSD: util.h,v 1.11 2008/09/19 18:33:34 joerg Exp $
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * patch - a program to apply diffs to original files
 | 
			
		||||
 * 
 | 
			
		||||
 * 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.
 | 
			
		||||
 * 
 | 
			
		||||
 * -C option added in 1998, original code by Marc Espie, based on FreeBSD
 | 
			
		||||
 * behaviour
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
char		*fetchname(const char *, bool *, int);
 | 
			
		||||
char		*checked_in(char *);
 | 
			
		||||
int		backup_file(const char *);
 | 
			
		||||
int		move_file(const char *, const char *);
 | 
			
		||||
int		copy_file(const char *, const char *);
 | 
			
		||||
void		say(const char *, ...)
 | 
			
		||||
		    __attribute__((__format__(__printf__, 1, 2)));
 | 
			
		||||
void		fatal(const char *, ...)
 | 
			
		||||
		    __attribute__((__format__(__printf__, 1, 2)));
 | 
			
		||||
void		pfatal(const char *, ...)
 | 
			
		||||
		    __attribute__((__format__(__printf__, 1, 2)));
 | 
			
		||||
void		ask(const char *, ...)
 | 
			
		||||
		    __attribute__((__format__(__printf__, 1, 2)));
 | 
			
		||||
char		*savestr(const char *);
 | 
			
		||||
void		set_signals(int);
 | 
			
		||||
void		ignore_signals(void);
 | 
			
		||||
void		makedirs(const char *, bool);
 | 
			
		||||
void		version(void);
 | 
			
		||||
void		my_exit(int) __attribute__((noreturn));
 | 
			
		||||
 | 
			
		||||
/* in mkpath.c */
 | 
			
		||||
extern int mkpath(char *);
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user