988 lines
23 KiB
C
Executable File
988 lines
23 KiB
C
Executable File
#ident "@(#)patch.c 1.2 95/09/07 SMI"
|
|
/*
|
|
* Copyright (c) 1995, Sun Microsystems, Inc.
|
|
* All Rights Reserved.
|
|
*
|
|
* This module contains IBM CONFIDENTIAL code. -- (IBM
|
|
* Confidential Restricted when combined with the aggregated
|
|
* modules for this product)
|
|
* OBJECT CODE ONLY SOURCE MATERIALS
|
|
* (C) COPYRIGHT International Business Machines Corp. 1993
|
|
* All Rights Reserved
|
|
*
|
|
* US Government Users Restricted Rights - Use, duplication or
|
|
* disclosure restricted by GSA ADP Schedule Contract with IBM Corp.
|
|
*
|
|
* (c) Copyright 1990, 1991, 1992, 1993 OPEN SOFTWARE FOUNDATION, INC.
|
|
* ALL RIGHTS RESERVED
|
|
*
|
|
* OSF/1 1.2
|
|
*
|
|
* Copyright 1986, Larry Wall
|
|
*
|
|
* This program may be copied as long as you don't try to make any
|
|
* money off of it, or pretend that you wrote it.
|
|
*/
|
|
|
|
/*
|
|
* File: patch.c
|
|
* Date: Sun Feb 12 18:48:18 PST 1995
|
|
*
|
|
* Description:
|
|
*
|
|
* patch - a program to apply diffs to original files
|
|
*
|
|
* Modifications:
|
|
* $Log$
|
|
*/
|
|
|
|
/*
|
|
* Include files:
|
|
*/
|
|
|
|
#include "common.h"
|
|
|
|
/*
|
|
* Global variables
|
|
*/
|
|
|
|
struct stat filestat; /* file statistics area */
|
|
|
|
/* Temp buffers for character conversion */
|
|
char *buf; /* general purpose buffer */
|
|
wchar_t *wbuf; /* general purpose buffer */
|
|
|
|
/* Flags */
|
|
bool verbose = TRUE; /* Tell all */
|
|
bool reverse = FALSE; /* Reverse patch files */
|
|
bool skip_rest_of_patch = FALSE; /* Set on error */
|
|
int strippath = INT_MAX; /* # path components to strip */
|
|
int cdiff_type = 0; /* diff type from cmd line */
|
|
int diff_type = 0; /* working diff type */
|
|
int dont_sync = FALSE; /* Assume we sync files */
|
|
long max_input;
|
|
|
|
/*
|
|
* Local variables.
|
|
*/
|
|
|
|
static LINENUM last_offset = 0;
|
|
static bool force = FALSE;
|
|
static bool noreverse = FALSE;
|
|
static bool canonicalize = FALSE;
|
|
static bool saveorig = FALSE;
|
|
static wchar_t if_defined[128] = { 0 }; /* #ifdef xyzzy */
|
|
static wchar_t if_ndefined[128] = { 0 }; /* #ifndef xyzzy */
|
|
static wchar_t else_defined[] = { L"#else\n" }; /* #else */
|
|
static wchar_t end_defined[128] = { 0 }; /* #endif xyzzy */
|
|
|
|
static char *patch_file = NULL;
|
|
static char *output_file = NULL;
|
|
static char *reject_file = NULL;
|
|
static char *input_file = NULL;
|
|
|
|
static LINENUM delta;
|
|
static int reject_written = 0;
|
|
|
|
/* To keep the compiler quiet... */
|
|
void main(int, char **);
|
|
|
|
|
|
/*
|
|
* Function: void usage(char *)
|
|
*
|
|
* Description:
|
|
*
|
|
* Print problem and usage message on stderr and exit with error.
|
|
*
|
|
* Inputs:
|
|
* problem -> Message to print.
|
|
*/
|
|
|
|
static void
|
|
usage(char *problem)
|
|
{
|
|
|
|
say("patch: %s.\n", problem);
|
|
fatal(gettext("Usage:\tpatch [-blNR] [-c|-e|-n] [-d dir]"
|
|
" [-D define] [-i patchfile]\\\n"
|
|
"\t [-o outfile] [-p num] [-r rejectfile] [file]\n"));
|
|
exit(ABORT_EXIT_VALUE);
|
|
}
|
|
|
|
|
|
/*
|
|
* Function: void reinitialize(void)
|
|
*
|
|
* Description:
|
|
*
|
|
* Prepare to find the next patch to do in the patch file.
|
|
*/
|
|
|
|
static void
|
|
reinitialize(void)
|
|
{
|
|
last_offset = 0;
|
|
delta = 0;
|
|
|
|
reverse = FALSE;
|
|
skip_rest_of_patch = FALSE;
|
|
}
|
|
|
|
|
|
/*
|
|
* Function: void process_arguments(int, char **)
|
|
*
|
|
* Description:
|
|
*
|
|
* Process commmand line arguments.
|
|
*/
|
|
|
|
static void
|
|
process_arguments(int argc, char **argv)
|
|
{
|
|
int c;
|
|
|
|
while ((c = getopt(argc, argv, ":bcd:D:ei:lnNo:p:r:Rs")) != -1) {
|
|
switch (c) {
|
|
case 'b': /* Save copy of originals */
|
|
saveorig = TRUE;
|
|
break;
|
|
|
|
case 'c': /* Interpret patch as context diff */
|
|
if (cdiff_type && cdiff_type != CONTEXT_DIFF) {
|
|
usage(gettext("-c, -e, and -n are mutually"
|
|
" exclusive options"));
|
|
/* NOTREACHED */
|
|
}
|
|
cdiff_type = CONTEXT_DIFF;
|
|
break;
|
|
|
|
case 'd': /* Chdir to optarg before processing */
|
|
if (chdir(optarg) < 0) {
|
|
fatal(gettext("cd to path %s failed.\n"),
|
|
optarg);
|
|
}
|
|
break;
|
|
|
|
case 'D': /* #Ifdef changes */
|
|
if (!isalpha(*optarg)) {
|
|
fatal(gettext("Argument to -D"
|
|
" is not an identifier.\n"));
|
|
}
|
|
(void) mbstowcs(wbuf, optarg,
|
|
sizeof (wbuf) / sizeof (wchar_t));
|
|
wcscpy(if_defined, L"#ifdef ");
|
|
(void) mbstowcs(&if_defined[wcslen(if_defined)],
|
|
optarg, 120);
|
|
wcscat(if_defined, L"\n");
|
|
|
|
wcscpy(if_ndefined, L"#ifndef ");
|
|
(void) mbstowcs(&if_ndefined[wcslen(if_ndefined)],
|
|
optarg, 120);
|
|
wcscat(if_ndefined, L"\n");
|
|
|
|
wcscpy(end_defined, L"#endif /* ");
|
|
(void) mbstowcs(&end_defined[wcslen(end_defined)],
|
|
optarg, 115);
|
|
wcscat(end_defined, L" */\n");
|
|
break;
|
|
|
|
case 'e': /* Interpret patch as ed script */
|
|
if (cdiff_type && cdiff_type != ED_DIFF) {
|
|
usage(gettext("-c, -e, and -n are mutually"
|
|
" exclusive options"));
|
|
/* NOTREACHED */
|
|
}
|
|
cdiff_type = ED_DIFF;
|
|
break;
|
|
|
|
case 'i': /* Read patch from file rather than stdin */
|
|
patch_file = optarg; /* patch input file */
|
|
break;
|
|
|
|
case 'l': /* Cause any blank sequences to match */
|
|
canonicalize = TRUE;
|
|
break;
|
|
|
|
case 'n': /* Interpret patch as normal diff */
|
|
if (cdiff_type && cdiff_type != NORMAL_DIFF) {
|
|
usage(gettext("-c, -e, and -n are mutually"
|
|
" exclusive options"));
|
|
/* NOTREACHED */
|
|
}
|
|
cdiff_type = NORMAL_DIFF;
|
|
break;
|
|
|
|
case 'N': /* Ignore previously applied patches */
|
|
noreverse = TRUE;
|
|
break;
|
|
|
|
case 'o': /* Output to named file */
|
|
/*
|
|
* If an empty string is passed I.E 'patch -o ""'
|
|
* we accept this to mean stdout too.
|
|
*/
|
|
if (*optarg == NULL)
|
|
output_file = "-";
|
|
else
|
|
output_file = optarg;
|
|
break;
|
|
|
|
case 'p': /* delete specified # components from paths */
|
|
if (!isdigit(*optarg)) {
|
|
strippath = 0;
|
|
optind--;
|
|
} else {
|
|
strippath = atoi(optarg);
|
|
}
|
|
break;
|
|
|
|
case 'r': /* Send rejects to named file */
|
|
reject_file = optarg;
|
|
break;
|
|
|
|
case 'R': /* reverse patch */
|
|
reverse = TRUE;
|
|
break;
|
|
|
|
case 's': /* Silent operation */
|
|
verbose = FALSE;
|
|
break;
|
|
|
|
case ':':
|
|
if (optopt == 'p') {
|
|
strippath = 0;
|
|
break;
|
|
}
|
|
|
|
default: /* None of the above */
|
|
usage(gettext("Invalid options"));
|
|
break;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Get file name if specified. Otherwise name is derived from
|
|
* patch file.
|
|
*/
|
|
if (argv[optind] != NULL) {
|
|
(void) mbstowcs(wbuf, argv[optind], max_input);
|
|
if (argv[optind + 1] != NULL) {
|
|
usage(gettext("Too many file arguments"));
|
|
}
|
|
if ((input_file = fetchname(wbuf, 0, 0)) == NULL) {
|
|
fatal(gettext("patch: Could not open \n"),
|
|
argv[optind]);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
* Function: void similar(wchar_t *, wchar_t *)
|
|
*
|
|
* Description:
|
|
*
|
|
* Do two lines match with canonicalized white space?
|
|
*
|
|
* Inputs:
|
|
* a -> A pointer to the first wide character string.
|
|
* b -> A pointer to the second wide character string.
|
|
*
|
|
* Returns:
|
|
* TRUE -> Lines compare favorably.
|
|
* FALSE -> Lines do not compare.
|
|
*/
|
|
|
|
static bool
|
|
similar(wchar_t *a, wchar_t *b)
|
|
{
|
|
int len = wcslen(b);
|
|
|
|
while (len) {
|
|
if (iswspace(*b)) { /* whitespace (or \n) to match? */
|
|
/* no corresponding whitespace? */
|
|
if (!iswspace(*a))
|
|
return (FALSE);
|
|
|
|
/* skip pattern whitespace */
|
|
while (len && iswspace(*b) && *b != '\n') {
|
|
b++;
|
|
len--;
|
|
}
|
|
|
|
/* skip target whitespace */
|
|
while (iswspace(*a) && *a != '\n')
|
|
a++;
|
|
|
|
/* should end in sync */
|
|
if (*a == '\n' || *b == '\n')
|
|
return (*a == *b);
|
|
|
|
/*
|
|
* Othewise start matching non-whitespace
|
|
* characters again (if any characters
|
|
* are left.
|
|
*/
|
|
continue;
|
|
} else if (*a++ != *b++) {
|
|
/*
|
|
* Non-whitespace chars did not match
|
|
* so lines are not similar.
|
|
*/
|
|
return (FALSE);
|
|
}
|
|
/*
|
|
* Non-whitespace chars matched
|
|
* so lines are still similar.
|
|
*/
|
|
len--;
|
|
}
|
|
|
|
/* Lines match */
|
|
return (TRUE);
|
|
}
|
|
|
|
|
|
/*
|
|
* Function: bool patch_match(file_info *, hunk_info *, LINENUM)
|
|
*
|
|
* Description:
|
|
*
|
|
* Does the hunk patterns match starting at line offset?
|
|
*
|
|
* Inputs:
|
|
* winfo -> A pointer to the file descriptor.
|
|
* hunk -> A pointer to the patch descriptor.
|
|
* offset -> Offset to start checking.
|
|
*/
|
|
|
|
static bool
|
|
patch_match(file_info *winfo, hunk_info *hunk, LINENUM offset)
|
|
{
|
|
wchar_t *patch_line, *input_line;
|
|
LINENUM pline;
|
|
|
|
for (pline = 0; pline < hunk->file1_lines; pline++) {
|
|
patch_line = hunk->lines[pline] + 2;
|
|
if ((input_line = fetch_line(winfo, offset + pline)) == NULL)
|
|
return (FALSE);
|
|
if (canonicalize) {
|
|
if (!similar(input_line, patch_line))
|
|
return (FALSE);
|
|
} else if (wcsncmp(input_line, patch_line, wcslen(patch_line)))
|
|
return (FALSE);
|
|
}
|
|
return (TRUE);
|
|
}
|
|
|
|
|
|
/*
|
|
* Function: LINENUM locate_hunk(file_info *, hunk_info *, LINENUM)
|
|
*
|
|
* Description:
|
|
*
|
|
* Attempt to find the right place to apply this hunk of patch.
|
|
* Fuzz is the current extent of the search. Most the time this
|
|
* is 0, but for misplaced patches our caller keeps expanding the
|
|
* search by one line. We take advantage of this and only check
|
|
* the extremes since we know every position in the middle has already
|
|
* failed.
|
|
*
|
|
* Inputs:
|
|
* winfo -> A pointer to the file descriptor.
|
|
* hunk -> A pointer to the patch descriptor.
|
|
* fuzz -> +/- Offset to check at.
|
|
*
|
|
* Returns:
|
|
* line number of match or -1 if no match was found yet.
|
|
*/
|
|
|
|
static LINENUM
|
|
locate_hunk(file_info *winfo, hunk_info *hunk, LINENUM fuzz)
|
|
{
|
|
LINENUM first_guess, max_pos_offset, max_neg_offset;
|
|
|
|
first_guess = hunk->file1_start + delta + last_offset - 1;
|
|
|
|
/* null range matches always */
|
|
if (hunk->file1_lines == 0)
|
|
return (first_guess);
|
|
|
|
max_pos_offset = first_guess + fuzz;
|
|
max_neg_offset = first_guess - fuzz;
|
|
|
|
/* do not try lines < 0 */
|
|
if (max_neg_offset < 0)
|
|
max_neg_offset = 0;
|
|
if (max_pos_offset < 0)
|
|
max_pos_offset = 0;
|
|
|
|
/* do not try lines > number of lines in file */
|
|
if ((max_neg_offset + hunk->file1_lines) > winfo->line_count)
|
|
max_neg_offset = winfo->line_count - hunk->file1_lines;
|
|
if ((max_pos_offset + hunk->file1_lines) > winfo->line_count)
|
|
max_pos_offset = winfo->line_count - hunk->file1_lines;
|
|
|
|
/*
|
|
* because of the way searches are expanded we only need
|
|
* to check the extremes... If fuzz is 0 then we don't
|
|
* even need to check both since they are identical.
|
|
*/
|
|
|
|
if (fuzz && patch_match(winfo, hunk, max_neg_offset)) {
|
|
last_offset = delta + hunk->file1_start - max_neg_offset - 1;
|
|
return (max_neg_offset);
|
|
}
|
|
|
|
if (patch_match(winfo, hunk, max_pos_offset)) {
|
|
last_offset = delta + hunk->file1_start - max_pos_offset - 1;
|
|
return (max_pos_offset);
|
|
}
|
|
return (-1);
|
|
}
|
|
|
|
|
|
/*
|
|
* Function: LINENUM check_hunk_location(file_info *, hunk_info *, int)
|
|
*
|
|
* Description:
|
|
*
|
|
*
|
|
* Inputs:
|
|
* winfo -> A pointer to the file descriptor.
|
|
* hunk -> A pointer to the patch descriptor.
|
|
* hunk_count -> Current hunk number.
|
|
*
|
|
* Returns:
|
|
* line number of match or -1 if no match was found.
|
|
*/
|
|
|
|
static LINENUM
|
|
check_hunk_location(file_info *winfo, hunk_info *hunk, int hunk_count)
|
|
{
|
|
LINENUM where, fuzz;
|
|
|
|
fuzz = 0;
|
|
do {
|
|
where = locate_hunk(winfo, hunk, fuzz);
|
|
if (hunk_count == 1 && where == -1 && !force) {
|
|
/*
|
|
* dwim for reversed patch?
|
|
* for normal diffs swapping a pure delete will always
|
|
* work and this is not what we want...
|
|
*/
|
|
if (diff_type == NORMAL_DIFF && hunk->file2_lines == 0)
|
|
continue;
|
|
|
|
pch_swap(hunk);
|
|
reverse = !reverse;
|
|
|
|
/* try again */
|
|
where = locate_hunk(winfo, hunk, fuzz);
|
|
|
|
/* didn't find it swapped */
|
|
if (where == -1) {
|
|
pch_swap(hunk);
|
|
reverse = !reverse;
|
|
} else if (noreverse) {
|
|
pch_swap(hunk);
|
|
reverse = !reverse;
|
|
say(gettext("Ignoring previously applied "
|
|
"(or reversed) patch.\n"));
|
|
skip_rest_of_patch = TRUE;
|
|
} else {
|
|
if (reverse) {
|
|
ask(gettext("Reversed (or previously "
|
|
"applied) patch detected! "
|
|
"Assume -R [%s] "),
|
|
nl_langinfo(YESSTR));
|
|
} else {
|
|
ask(gettext(
|
|
"Unreversed (or previously "
|
|
"applied) patch detected! "
|
|
"Ignore -R? [%s] "),
|
|
nl_langinfo(YESSTR));
|
|
}
|
|
if (rpmatch(buf) == 0) {
|
|
/* no */
|
|
ask(gettext("Apply anyway? [%s] "),
|
|
nl_langinfo(NOSTR));
|
|
if (rpmatch(buf) != 1) {
|
|
skip_rest_of_patch = TRUE;
|
|
}
|
|
where = NULL;
|
|
reverse = !reverse;
|
|
pch_swap(hunk);
|
|
}
|
|
}
|
|
}
|
|
} while (!skip_rest_of_patch && where == -1 && ++fuzz <= MAXFUZZ);
|
|
|
|
return (where);
|
|
}
|
|
|
|
|
|
/*
|
|
* Function: void abort_hunk(hunk_info *, file_info *)
|
|
*
|
|
* Description:
|
|
*
|
|
* We did not find the pattern, dump out the hunk so the user
|
|
* can handle it.
|
|
*
|
|
* Inputs:
|
|
* hunk -> A pointer to the patch descriptor.
|
|
* reject_info -> A pointer to the file descriptor.
|
|
*/
|
|
|
|
static void
|
|
abort_hunk(hunk_info *hunk, file_info *reject_info)
|
|
{
|
|
LINENUM i;
|
|
|
|
reject_written = 1;
|
|
insert_line(reject_info, L"***************\n", reject_info->line_count);
|
|
|
|
(void) sprintf(buf, "*** %ld,%ld ****\n", hunk->file1_start,
|
|
hunk->file1_start + hunk->file1_lines - 1);
|
|
(void) mbstowcs(wbuf, buf, max_input);
|
|
insert_line(reject_info, wbuf, reject_info->line_count);
|
|
|
|
for (i = 0; i < hunk->file1_lines; i++) {
|
|
insert_line(reject_info, hunk->lines[i],
|
|
reject_info->line_count);
|
|
}
|
|
|
|
(void) sprintf(buf, "--- %ld,%ld ----\n", hunk->file2_start,
|
|
hunk->file2_start + hunk->file2_lines - 1);
|
|
(void) mbstowcs(wbuf, buf, max_input);
|
|
insert_line(reject_info, wbuf, reject_info->line_count);
|
|
|
|
for (; i < hunk->line_count; i++) {
|
|
insert_line(reject_info, hunk->lines[i],
|
|
reject_info->line_count);
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
* Function: void apply_hunk(file_info *, hunk_info *, LINENUM)
|
|
*
|
|
* Description:
|
|
*
|
|
* We found where to apply it (we hope), so do it.
|
|
*
|
|
* Hunk has already been error checked so we don't
|
|
* check for malformed expressions here.
|
|
*
|
|
* The hunk is applied in 2 phases:
|
|
* 1) Process the original lines (these include deletions, context lines,
|
|
* and change lines) we simply delete deletions and change lines.
|
|
* 2) Process the new lines (these include, additions, context lines,
|
|
* and change lines) we simply add additions and change lines.
|
|
*
|
|
* The tricky part here is to properly account for #ifdef lines and
|
|
* lines that would normally be deleted when ifdefs are required and
|
|
* to not account so if they are not. The array deltas is used to
|
|
* accomplish this. It tracks the number of additional lines that need
|
|
* to be added to the line count at each sync point (context line) for
|
|
* each context line in the second phase of application.
|
|
*
|
|
* Inputs:
|
|
* winfo -> A pointer to the file descriptor.
|
|
* hunk -> A pointer to the patch descriptor.
|
|
* offset -> Offset to start checking.
|
|
*/
|
|
|
|
static void
|
|
apply_hunk(file_info *winfo, hunk_info *hunk, LINENUM where)
|
|
{
|
|
LINENUM i, wh;
|
|
wchar_t *line;
|
|
wchar_t in_define = 0;
|
|
LINENUM *deltas;
|
|
LINENUM context_line;
|
|
|
|
deltas = allocate(sizeof (LINENUM) * (hunk->line_count + 1));
|
|
|
|
/* file1 processing */
|
|
wh = where;
|
|
context_line = 0;
|
|
for (i = 0; i < hunk->file1_lines; i++) {
|
|
line = hunk->lines[i];
|
|
switch (*line) {
|
|
case L' ': /* Context line */
|
|
if (*if_defined) {
|
|
/* Put out #endif if needed */
|
|
if (in_define) {
|
|
insert_line(winfo, end_defined, wh++);
|
|
deltas[context_line]++;
|
|
delta++;
|
|
in_define = 0;
|
|
}
|
|
context_line++;
|
|
}
|
|
wh++;
|
|
break;
|
|
|
|
case L'-': /* Delete line */
|
|
if (*if_defined) {
|
|
/* Put out #ifndef if needed */
|
|
if (!in_define) {
|
|
in_define = *line;
|
|
insert_line(winfo, if_ndefined, wh++);
|
|
deltas[context_line]++;
|
|
delta++;
|
|
}
|
|
wh++;
|
|
} else {
|
|
delete_line(winfo, wh);
|
|
delta--;
|
|
}
|
|
break;
|
|
|
|
case L'!': /* Change line */
|
|
if (*if_defined) {
|
|
/* Put out #else if needed */
|
|
if (!in_define) {
|
|
in_define = *line;
|
|
insert_line(winfo, else_defined, wh++);
|
|
deltas[context_line]++;
|
|
delta++;
|
|
}
|
|
/*
|
|
* Deltas are accounted for at matching change
|
|
*/
|
|
wh++;
|
|
} else {
|
|
delete_line(winfo, wh);
|
|
delta--;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* Put out #endif if needed */
|
|
if (*if_defined && in_define) {
|
|
insert_line(winfo, end_defined, wh++);
|
|
deltas[context_line]++;
|
|
delta++;
|
|
in_define = 0;
|
|
}
|
|
|
|
/* Finish adding lines */
|
|
wh = where;
|
|
context_line = 0;
|
|
for (i = hunk->file1_lines; i < hunk->line_count; i++) {
|
|
line = hunk->lines[i];
|
|
switch (*line) {
|
|
case L' ': /* Context line */
|
|
wh += deltas[context_line++];
|
|
if (*if_defined && in_define == L'+') {
|
|
/* Put out #endif */
|
|
insert_line(winfo, end_defined, wh++);
|
|
delta++;
|
|
in_define = 0;
|
|
}
|
|
wh++;
|
|
break;
|
|
|
|
case L'+': /* Add line */
|
|
if (*if_defined) {
|
|
/* Put out #ifdef if needed */
|
|
if (!in_define) {
|
|
in_define = *line;
|
|
insert_line(winfo, if_defined, wh++);
|
|
delta++;
|
|
}
|
|
}
|
|
insert_line(winfo, line + 2, wh++);
|
|
delta++;
|
|
break;
|
|
|
|
case L'!': /* Change line */
|
|
if (*if_defined) {
|
|
/* Put out #ifdef if needed */
|
|
if (!in_define) {
|
|
in_define = *line;
|
|
insert_line(winfo, if_defined, wh++);
|
|
delta++;
|
|
}
|
|
}
|
|
insert_line(winfo, line + 2, wh++);
|
|
delta++;
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* Put out final #endif if needed */
|
|
if (*if_defined && in_define == L'+') {
|
|
insert_line(winfo, end_defined, wh++);
|
|
delta++;
|
|
}
|
|
in_define = 0;
|
|
|
|
/* Free up our temp space */
|
|
free(deltas);
|
|
}
|
|
|
|
|
|
/*
|
|
* Function: void cleanup(void)
|
|
*
|
|
* Description:
|
|
*
|
|
* Called on exit. Writes out any files that have not already
|
|
* been written to disk.
|
|
*
|
|
* Inputs:
|
|
* info -> A pointer to the file descriptor.
|
|
* data -> A pointer to the wide character string to add.
|
|
*/
|
|
|
|
void
|
|
cleanup(void)
|
|
{
|
|
unsigned long i;
|
|
|
|
if (dont_sync == FALSE) {
|
|
for (i = 0; i < opened_file_descriptors; i++) {
|
|
file_info *info;
|
|
|
|
if (opened_files[i]->flags & UPDATE_ON_EXIT) {
|
|
(void) open_file(opened_files[i]->name, 0);
|
|
sync_file(opened_files[i], NULL);
|
|
close_file(opened_files[i]);
|
|
}
|
|
}
|
|
}
|
|
for (i = 0; i < opened_file_descriptors; i++) {
|
|
unlink(opened_files[i]->temp_file);
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
* Function: void ask_for_name(void)
|
|
*
|
|
* Description:
|
|
*
|
|
* Ask for a file to patch since none could be intuited.
|
|
*
|
|
* Returns:
|
|
*
|
|
* Name of a file that exists.
|
|
*/
|
|
|
|
static char *
|
|
ask_for_name(void)
|
|
{
|
|
struct stat statbuf;
|
|
char *name = NULL;
|
|
|
|
while (name == NULL) {
|
|
ask(gettext("File to patch: "));
|
|
if (*buf != '\n') {
|
|
(void) mbstowcs(wbuf, buf, max_input);
|
|
/* Process potential file name */
|
|
name = fetchname(wbuf, 0, FALSE);
|
|
}
|
|
if (name == NULL || lstat(name, &statbuf) == -1) {
|
|
ask(gettext("No file found -- skip this patch? [%s] "),
|
|
nl_langinfo(NOSTR));
|
|
if (rpmatch(buf) != 1) { /* not yes */
|
|
continue;
|
|
}
|
|
if (verbose)
|
|
say(gettext("Skipping patch...\n"));
|
|
|
|
skip_rest_of_patch = TRUE;
|
|
return (NULL);
|
|
}
|
|
}
|
|
return (savestr(name));
|
|
}
|
|
|
|
|
|
/*
|
|
* Function: void main(int, char **)
|
|
*
|
|
* Description:
|
|
*
|
|
* Process arguments and apply a set of diffs as appropriate.
|
|
*/
|
|
|
|
|
|
void
|
|
main(int argc, char **argv)
|
|
{
|
|
int winfo, pinfo, reject_info;
|
|
hunk_info *hunk;
|
|
LINENUM where;
|
|
struct stat statbuf;
|
|
char *workname, *reject_name;
|
|
int hunk_count;
|
|
int failed = 0;
|
|
int failtotal = 0;
|
|
|
|
/* Set locale environment variables local definitions */
|
|
(void) setlocale(LC_ALL, "");
|
|
#if !defined(TEXT_DOMAIN) /* Should be defined by cc -D */
|
|
#define TEXT_DOMAIN "SYS_TEST" /* Use this only if it wasn't */
|
|
#endif
|
|
(void) textdomain(TEXT_DOMAIN);
|
|
|
|
/*
|
|
* setup common buffers
|
|
*/
|
|
max_input = MAX_INPUT;
|
|
buf = allocate((max_input + 1) * sizeof (char));
|
|
wbuf = allocate((max_input + 1) * sizeof (wchar_t));
|
|
|
|
/* parse switches */
|
|
process_arguments(argc, argv);
|
|
|
|
/* make sure we clean up /tmp and sync files in case of disaster */
|
|
set_signals();
|
|
(void) atexit(cleanup);
|
|
|
|
/* For each patch in file open file, apply patch */
|
|
for (pinfo = open_patch_file(patch_file);
|
|
there_is_another_patch(opened_files[pinfo], &workname);
|
|
reinitialize()) {
|
|
if (!skip_rest_of_patch) {
|
|
if (input_file == NULL) {
|
|
/*
|
|
* No file was specified on command line so we
|
|
* take the file name from patch file (if_any)
|
|
*/
|
|
if (workname == NULL) {
|
|
/* No file name in patch file either! */
|
|
input_file = ask_for_name();
|
|
if (input_file == NULL)
|
|
continue;
|
|
workname = input_file;
|
|
}
|
|
} else {
|
|
/*
|
|
* file name was specified on command line or
|
|
* user was prompted for it so the name never
|
|
* changes
|
|
*/
|
|
workname = input_file;
|
|
}
|
|
winfo = open_file(workname, 0);
|
|
opened_files[winfo]->flags |= UPDATE_ON_EXIT;
|
|
if (saveorig && (output_file == 0))
|
|
opened_files[winfo]->flags |= SAVE_ORIGINAL;
|
|
}
|
|
|
|
/* for ed script just up and do it and exit */
|
|
if (diff_type == ED_DIFF) {
|
|
if (!skip_rest_of_patch)
|
|
do_ed_script(opened_files[winfo],
|
|
opened_files[pinfo]);
|
|
goto next;
|
|
}
|
|
|
|
/* Open up reject file */
|
|
if (reject_file != NULL) {
|
|
reject_name = reject_file;
|
|
} else {
|
|
(void) sprintf(buf, "%s.rej", workname);
|
|
reject_name = savestr(buf);
|
|
}
|
|
|
|
/* get an empty file */
|
|
reject_info = open_file(reject_name, 1);
|
|
|
|
/* apply each hunk of patch to file */
|
|
failed = hunk_count = 0;
|
|
while ((hunk = another_hunk(opened_files[pinfo])) != NULL) {
|
|
hunk_count++;
|
|
where = check_hunk_location(opened_files[winfo], hunk, hunk_count);
|
|
if (skip_rest_of_patch) {
|
|
abort_hunk(hunk, opened_files[reject_info]);
|
|
failed++;
|
|
if (verbose) {
|
|
say(gettext("Hunk #%d ignored at "
|
|
"line %ld.\n"), hunk_count, where);
|
|
}
|
|
} else if (where == (LINENUM) -1) {
|
|
abort_hunk(hunk, opened_files[reject_info]);
|
|
failed++;
|
|
if (verbose) {
|
|
say(gettext("Hunk #%d failed at "
|
|
"line %ld.\n"), hunk_count,
|
|
hunk->file1_start);
|
|
}
|
|
} else {
|
|
apply_hunk(opened_files[winfo], hunk, where);
|
|
if (verbose) {
|
|
if (last_offset == 1L) {
|
|
say(gettext("Hunk #%d succeeded"
|
|
" at line %ld (offset 1"
|
|
" line)\n"), hunk_count,
|
|
where - delta);
|
|
} else if (last_offset > 1L) {
|
|
say(gettext("Hunk #%d succeeded"
|
|
" at %ld (offset %ld lines)"
|
|
"\n"), hunk_count,
|
|
where - delta,
|
|
last_offset);
|
|
}
|
|
}
|
|
}
|
|
free_hunk(hunk);
|
|
}
|
|
|
|
/* and put the output where desired */
|
|
ignore_signals();
|
|
|
|
/* If patch failed write out stats */
|
|
if (failed) {
|
|
opened_files[reject_info]->flags |= UPDATE_ON_EXIT;
|
|
failtotal = FAIL_EXIT_VALUE;
|
|
if (skip_rest_of_patch) {
|
|
say(gettext("%d out of %d hunks "
|
|
"ignored: saving rejects to %s\n"),
|
|
failed, hunk_count, reject_name);
|
|
} else {
|
|
say(gettext("%d out of %d hunks "
|
|
"failed: saving rejects to %s\n"),
|
|
failed, hunk_count, reject_name);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* If an output has been specified then all output goes to
|
|
* that file and the original files are not touched. We also
|
|
* Flush out the intermediate results after each patch in this
|
|
* case since the spec says so...
|
|
*/
|
|
close_file(opened_files[reject_info]);
|
|
next: if (output_file) {
|
|
/*
|
|
* We also need to create a .orig file if asked to
|
|
* and file exists already...
|
|
*/
|
|
if (saveorig && (strcmp(output_file, "-") != 0)) {
|
|
saveorig = 0;
|
|
if (lstat(output_file, &statbuf) != -1) {
|
|
(void) sprintf(buf, "%s.orig",
|
|
output_file);
|
|
(void) rename(output_file, buf);
|
|
}
|
|
}
|
|
opened_files[winfo]->flags &= ~UPDATE_ON_EXIT;
|
|
sync_file(opened_files[winfo], output_file);
|
|
}
|
|
close_file(opened_files[winfo]);
|
|
}
|
|
if (reject_written)
|
|
exit(REJECT_EXIT_VALUE);
|
|
exit(failtotal);
|
|
}
|