v24i018: GNU Diff, version 1.15, Part03/08
Rich Salz
rsalz at uunet.uu.net
Tue Feb 26 08:14:14 AEST 1991
Submitted-by: Paul Eggert <eggert at twinsun.com>
Posting-number: Volume 24, Issue 18
Archive-name: gnudiff1.15/part03
#! /bin/sh
# This is a shell archive. Remove anything before this line, then unpack
# it by saving it into a file and typing "sh file". To overwrite existing
# files, type "sh file -c". You can also feed this as standard input via
# unshar, or by typing "sh <file", e.g.. If this archive is complete, you
# will see the following message at the end:
# "End of archive 3 (of 8)."
# Contents: diff.c getopt.c util.c
# Wrapped by eggert at ata on Mon Jan 7 11:25:29 1991
PATH=/bin:/usr/bin:/usr/ucb ; export PATH
if test -f 'diff.c' -a "${1}" != "-c" ; then
echo shar: Will not clobber existing file \"'diff.c'\"
else
echo shar: Extracting \"'diff.c'\" \(17901 characters\)
sed "s/^X//" >'diff.c' <<'END_OF_FILE'
X/* GNU DIFF main routine.
X Copyright (C) 1988, 1989 Free Software Foundation, Inc.
X
XThis file is part of GNU DIFF.
X
XGNU DIFF is free software; you can redistribute it and/or modify
Xit under the terms of the GNU General Public License as published by
Xthe Free Software Foundation; either version 1, or (at your option)
Xany later version.
X
XGNU DIFF is distributed in the hope that it will be useful,
Xbut WITHOUT ANY WARRANTY; without even the implied warranty of
XMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
XGNU General Public License for more details.
X
XYou should have received a copy of the GNU General Public License
Xalong with GNU DIFF; see the file COPYING. If not, write to
Xthe Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
X
X/* GNU DIFF was written by Mike Haertel, David Hayes,
X Richard Stallman and Len Tower. */
X
X#define GDIFF_MAIN
X#include "regex.h"
X#include "diff.h"
X#include "getopt.h"
X
X
X/* Nonzero for -r: if comparing two directories,
X compare their common subdirectories recursively. */
X
Xint recursive;
X
X/* For debugging: don't do discard_confusing_lines. */
X
Xint no_discards;
X
X/* Return a string containing the command options with which diff was invoked.
X Spaces appear between what were separate ARGV-elements.
X There is a space at the beginning but none at the end.
X If there were no options, the result is an empty string.
X
X Arguments: OPTIONVEC, a vector containing separate ARGV-elements, and COUNT,
X the length of that vector. */
X
Xstatic char *
Xoption_list (optionvec, count)
X char **optionvec; /* Was `vector', but that collides on Alliant. */
X int count;
X{
X int i;
X int length = 0;
X char *result;
X
X for (i = 0; i < count; i++)
X length += strlen (optionvec[i]) + 1;
X
X result = (char *) xmalloc (length + 1);
X result[0] = 0;
X
X for (i = 0; i < count; i++)
X {
X strcat (result, " ");
X strcat (result, optionvec[i]);
X }
X
X return result;
X}
X
X/* The numbers 129 and 130 that appear in the fourth element
X for the context and unidiff entries are used as a way of
X telling the big switch in `main' how to process those options. */
X
Xstatic struct option longopts[] =
X{
X {"ignore-blank-lines", 0, 0, 'B'},
X {"context", 2, 0, 129},
X {"ifdef", 1, 0, 'D'},
X {"show-function-line", 1, 0, 'F'},
X {"speed-large-files", 0, 0, 'H'},
X {"ignore-matching-lines", 1, 0, 'I'},
X {"file-label", 1, 0, 'L'},
X {"entire-new-files", 0, 0, 'N'},
X {"new-files", 0, 0, 'N'},
X {"starting-file", 1, 0, 'S'},
X {"initial-tab", 0, 0, 'T'},
X {"text", 0, 0, 'a'},
X {"all-text", 0, 0, 'a'},
X {"ascii", 0, 0, 'a'},
X {"ignore-space-change", 0, 0, 'b'},
X {"minimal", 0, 0, 'd'},
X {"ed", 0, 0, 'e'},
X {"reversed-ed", 0, 0, 'f'},
X {"ignore-case", 0, 0, 'i'},
X {"print", 0, 0, 'l'},
X {"rcs", 0, 0, 'n'},
X {"show-c-function", 0, 0, 'p'},
X {"binary", 0, 0, 'q'},
X {"brief", 0, 0, 'q'},
X {"recursive", 0, 0, 'r'},
X {"report-identical-files", 0, 0, 's'},
X {"expand-tabs", 0, 0, 't'},
X {"ignore-all-space", 0, 0, 'w'},
X {"unified", 2, 0, 130},
X {"version", 0, 0, 'v'},
X {0, 0, 0, 0}
X};
X
Xmain (argc, argv)
X int argc;
X char *argv[];
X{
X int val;
X int c;
X int prev = -1;
X int longind;
X extern char *version_string;
X
X program = argv[0];
X
X /* Do our initializations. */
X output_style = OUTPUT_NORMAL;
X always_text_flag = FALSE;
X ignore_space_change_flag = FALSE;
X ignore_all_space_flag = FALSE;
X length_varies = FALSE;
X ignore_case_flag = FALSE;
X ignore_blank_lines_flag = FALSE;
X ignore_regexp = 0;
X function_regexp = 0;
X print_file_same_flag = FALSE;
X entire_new_file_flag = FALSE;
X no_details_flag = FALSE;
X context = -1;
X line_end_char = '\n';
X tab_align_flag = FALSE;
X tab_expand_flag = FALSE;
X recursive = FALSE;
X paginate_flag = FALSE;
X ifdef_string = NULL;
X heuristic = FALSE;
X dir_start_file = NULL;
X msg_chain = NULL;
X msg_chain_end = NULL;
X no_discards = 0;
X
X /* Decode the options. */
X
X while ((c = getopt_long (argc, argv,
X "0123456789abBcC:dD:efF:hHiI:lL:nNpqrsS:tTuvw",
X longopts, &longind)) != EOF)
X {
X if (c == 0) /* Long option. */
X c = longopts[longind].val;
X switch (c)
X {
X /* All digits combine in decimal to specify the context-size. */
X case '1':
X case '2':
X case '3':
X case '4':
X case '5':
X case '6':
X case '7':
X case '8':
X case '9':
X case '0':
X if (context == -1)
X context = 0;
X /* If a context length has already been specified,
X more digits allowed only if they follow right after the others.
X Reject two separate runs of digits, or digits after -C. */
X else if (prev < '0' || prev > '9')
X fatal ("context length specified twice");
X
X context = context * 10 + c - '0';
X break;
X
X case 'a':
X /* Treat all files as text files; never treat as binary. */
X always_text_flag = 1;
X break;
X
X case 'b':
X /* Ignore changes in amount of whitespace. */
X ignore_space_change_flag = 1;
X length_varies = 1;
X break;
X
X case 'B':
X /* Ignore changes affecting only blank lines. */
X ignore_blank_lines_flag = 1;
X break;
X
X case 'C':
X case 129: /* +context[=lines] */
X case 130: /* +unified[=lines] */
X if (optarg)
X {
X if (context >= 0)
X fatal ("context length specified twice");
X {
X char *p;
X for (p = optarg; *p; p++)
X if (*p < '0' || *p > '9')
X fatal ("invalid context length argument");
X }
X context = atoi (optarg);
X }
X
X /* Falls through. */
X case 'c':
X /* Make context-style output. */
X specify_style (c == 130 ? OUTPUT_UNIFIED : OUTPUT_CONTEXT);
X break;
X
X case 'd':
X /* Don't discard lines. This makes things slower (sometimes much
X slower) but will find a guaranteed minimal set of changes. */
X no_discards = 1;
X break;
X
X case 'D':
X /* Make merged #ifdef output. */
X specify_style (OUTPUT_IFDEF);
X ifdef_string = optarg;
X break;
X
X case 'e':
X /* Make output that is a valid `ed' script. */
X specify_style (OUTPUT_ED);
X break;
X
X case 'f':
X /* Make output that looks vaguely like an `ed' script
X but has changes in the order they appear in the file. */
X specify_style (OUTPUT_FORWARD_ED);
X break;
X
X case 'F':
X /* Show, for each set of changes, the previous line that
X matches the specified regexp. Currently affects only
X context-style output. */
X function_regexp = optarg;
X break;
X
X case 'h':
X /* Split the files into chunks of around 1500 lines
X for faster processing. Usually does not change the result.
X
X This currently has no effect. */
X break;
X
X case 'H':
X /* Turn on heuristics that speed processing of large files
X with a small density of changes. */
X heuristic = 1;
X break;
X
X case 'i':
X /* Ignore changes in case. */
X ignore_case_flag = 1;
X break;
X
X case 'I':
X /* Ignore changes affecting only lines that match the
X specified regexp. */
X ignore_regexp = optarg;
X break;
X
X case 'l':
X /* Pass the output through `pr' to paginate it. */
X paginate_flag = 1;
X break;
X
X case 'L':
X /* Specify file labels for `-c' output headers. */
X if (!file_label[0])
X file_label[0] = optarg;
X else if (!file_label[1])
X file_label[1] = optarg;
X else
X fatal ("too many file label options");
X break;
X
X case 'n':
X /* Output RCS-style diffs, like `-f' except that each command
X specifies the number of lines affected. */
X specify_style (OUTPUT_RCS);
X break;
X
X case 'N':
X /* When comparing directories, if a file appears only in one
X directory, treat it as present but empty in the other. */
X entire_new_file_flag = 1;
X break;
X
X case 'p':
X /* Make context-style output and show name of last C function. */
X specify_style (OUTPUT_CONTEXT);
X function_regexp = "^[_a-zA-Z]";
X break;
X
X case 'q':
X no_details_flag = 1;
X break;
X
X case 'r':
X /* When comparing directories,
X recursively compare any subdirectories found. */
X recursive = 1;
X break;
X
X case 's':
X /* Print a message if the files are the same. */
X print_file_same_flag = 1;
X break;
X
X case 'S':
X /* When comparing directories, start with the specified
X file name. This is used for resuming an aborted comparison. */
X dir_start_file = optarg;
X break;
X
X case 't':
X /* Expand tabs to spaces in the output so that it preserves
X the alignment of the input files. */
X tab_expand_flag = 1;
X break;
X
X case 'T':
X /* Use a tab in the output, rather than a space, before the
X text of an input line, so as to keep the proper alignment
X in the input line without changing the characters in it. */
X tab_align_flag = 1;
X break;
X
X case 'v':
X printf ("GNU diff version %s\n", version_string);
X break;
X
X case 'u':
X /* Output the context diff in unidiff format. */
X specify_style (OUTPUT_UNIFIED);
X break;
X
X case 'w':
X /* Ignore horizontal whitespace when comparing lines. */
X ignore_all_space_flag = 1;
X length_varies = 1;
X break;
X
X default:
X usage ();
X }
X prev = c;
X }
X
X if (optind != argc - 2)
X usage ();
X
X if (ignore_regexp)
X {
X char *val;
X bzero (&ignore_regexp_compiled, sizeof ignore_regexp_compiled);
X val = re_compile_pattern (ignore_regexp, strlen (ignore_regexp),
X &ignore_regexp_compiled);
X if (val != 0)
X error ("%s: %s", ignore_regexp, val);
X ignore_regexp_compiled.fastmap = (char *) xmalloc (256);
X }
X
X if (function_regexp)
X {
X char *val;
X bzero (&function_regexp_compiled, sizeof function_regexp_compiled);
X val = re_compile_pattern (function_regexp, strlen (function_regexp),
X &function_regexp_compiled);
X if (val != 0)
X error ("%s: %s", function_regexp, val);
X function_regexp_compiled.fastmap = (char *) xmalloc (256);
X }
X
X if (output_style != OUTPUT_CONTEXT && output_style != OUTPUT_UNIFIED)
X context = 0;
X else if (context == -1)
X /* Default amount of context for -c. */
X context = 3;
X
X switch_string = option_list (argv + 1, optind - 1);
X
X val = compare_files (0, argv[optind], 0, argv[optind + 1], 0);
X
X /* Print any messages that were saved up for last. */
X print_message_queue ();
X
X if (ferror (stdout) || fclose (stdout) != 0)
X fatal ("write error");
X exit (val);
X}
X
Xusage ()
X{
X fprintf (stderr, "\
XUsage: diff [-#] [-abBcdefhHilnNprstTuvw] [-C lines] [-F regexp] [-I regexp]\n\
X [-L label [-L label]] [-S file] [-D symbol] [+ignore-blank-lines]\n\
X [+context[=lines]] [+unified[=lines]] [+ifdef=symbol]\n\
X [+show-function-line=regexp]\n");
X fprintf (stderr, "\
X [+speed-large-files] [+ignore-matching-lines=regexp] [+new-file]\n\
X [+initial-tab] [+starting-file=file] [+text] [+all-text] [+ascii]\n\
X [+minimal] [+ignore-space-change] [+ed] [+reversed-ed] [+ignore-case]\n");
X fprintf (stderr, "\
X [+print] [+rcs] [+show-c-function] [+binary] [+brief] [+recursive]\n\
X [+report-identical-files] [+expand-tabs] [+ignore-all-space]\n\
X [+file-label=label [+file-label=label]] [+version] path1 path2\n");
X exit (2);
X}
X
Xspecify_style (style)
X enum output_style style;
X{
X if (output_style != OUTPUT_NORMAL
X && output_style != style)
X error ("conflicting specifications of output style");
X output_style = style;
X}
X
X/* Compare two files (or dirs) with specified names
X DIR0/NAME0 and DIR1/NAME1, at level DEPTH in directory recursion.
X (if DIR0 is 0, then the name is just NAME0, etc.)
X This is self-contained; it opens the files and closes them.
X
X Value is 0 if files are identical, 1 if different,
X 2 if there is a problem opening them. */
X
Xint
Xcompare_files (dir0, name0, dir1, name1, depth)
X char *dir0, *dir1;
X char *name0, *name1;
X int depth;
X{
X static char Standard_Input[] = "Standard Input";
X struct file_data inf[2];
X register int i;
X int val;
X int errorcount = 0;
X int stat_result[2];
X
X /* If this is directory comparison, perhaps we have a file
X that exists only in one of the directories.
X If so, just print a message to that effect. */
X
X if (! entire_new_file_flag && (name0 == 0 || name1 == 0))
X {
X char *name = name0 == 0 ? name1 : name0;
X char *dir = name0 == 0 ? dir1 : dir0;
X message ("Only in %s: %s\n", dir, name);
X /* Return 1 so that diff_dirs will return 1 ("some files differ"). */
X return 1;
X }
X
X /* Mark any nonexistent file with -1 in the desc field. */
X /* Mark unopened files (i.e. directories) with -2. */
X
X inf[0].desc = name0 == 0 ? -1 : -2;
X inf[1].desc = name1 == 0 ? -1 : -2;
X
X /* Now record the full name of each file, including nonexistent ones. */
X
X if (name0 == 0)
X name0 = name1;
X if (name1 == 0)
X name1 = name0;
X
X inf[0].name = dir0 == 0 ? name0 : concat (dir0, "/", name0);
X inf[1].name = dir1 == 0 ? name1 : concat (dir1, "/", name1);
X
X /* Stat the files. Record whether they are directories.
X Record in stat_result whether stat fails. */
X
X for (i = 0; i <= 1; i++)
X {
X bzero (&inf[i].stat, sizeof(struct stat));
X inf[i].dir_p = 0;
X stat_result[i] = 0;
X
X if (inf[i].desc != -1)
X {
X char *filename = inf[i].name;
X
X stat_result[i] =
X strcmp (filename, "-")
X ? stat (filename, &inf[i].stat)
X : fstat (0, &inf[i].stat);
X
X if (stat_result[i] < 0)
X {
X perror_with_name (filename);
X errorcount = 1;
X }
X else
X inf[i].dir_p =
X S_IFDIR == (inf[i].stat.st_mode & S_IFMT)
X && strcmp (filename, "-");
X }
X }
X
X /* See if the two named files are actually the same physical file.
X If so, we know they are identical without actually reading them. */
X
X if (output_style != OUTPUT_IFDEF
X && inf[0].stat.st_ino == inf[1].stat.st_ino
X && inf[0].stat.st_dev == inf[1].stat.st_dev
X && stat_result[0] == 0
X && stat_result[1] == 0)
X {
X val = 0;
X goto done;
X }
X
X if (name0 == 0)
X inf[0].dir_p = inf[1].dir_p;
X if (name1 == 0)
X inf[1].dir_p = inf[0].dir_p;
X
X /* Open the files and record their descriptors. */
X
X for (i = 0; i <= 1; i++)
X {
X if (inf[i].desc == -1)
X ;
X else if (!strcmp (inf[i].name, "-"))
X {
X inf[i].desc = 0;
X inf[i].name = Standard_Input;
X }
X /* Don't bother opening if stat already failed. */
X else if (stat_result[i] == 0 && ! inf[i].dir_p)
X {
X char *filename = inf[i].name;
X
X inf[i].desc = open (filename, O_RDONLY, 0);
X if (0 > inf[i].desc)
X {
X perror_with_name (filename);
X errorcount = 1;
X }
X }
X }
X
X if (errorcount)
X {
X
X /* If either file should exist but fails to be opened, return 2. */
X
X val = 2;
X
X }
X else if (inf[0].dir_p && inf[1].dir_p)
X {
X if (output_style == OUTPUT_IFDEF)
X fatal ("-D option not supported with directories");
X
X /* If both are directories, compare the files in them. */
X
X if (depth > 0 && !recursive)
X {
X /* But don't compare dir contents one level down
X unless -r was specified. */
X message ("Common subdirectories: %s and %s\n",
X inf[0].name, inf[1].name);
X val = 0;
X }
X else
X {
X val = diff_dirs (inf[0].name, inf[1].name,
X compare_files, depth, 0, 0);
X }
X
X }
X else if (depth == 0 && (inf[0].dir_p || inf[1].dir_p))
X {
X
X /* If only one is a directory, and it was specified in the command line,
X use the file in that dir whose basename matches the other file. */
X
X int dir_arg = (inf[0].dir_p ? 0 : 1);
X int fnm_arg = (inf[0].dir_p ? 1 : 0);
X char *p = rindex (inf[fnm_arg].name, '/');
X char *filename = concat (inf[dir_arg].name, "/",
X (p ? p+1 : inf[fnm_arg].name));
X
X if (inf[fnm_arg].name == Standard_Input)
X fatal ("can't compare - to a directory");
X
X inf[dir_arg].desc = open (filename, O_RDONLY, 0);
X
X if (0 > inf[dir_arg].desc)
X {
X perror_with_name (filename);
X val = 2;
X }
X else
X {
X /* JF: patch from the net to check and make sure we can really free
X this. If it's from argv[], freeing it is a *really* bad idea */
X if (0 != (dir_arg ? dir1 : dir0))
X free (inf[dir_arg].name);
X inf[dir_arg].name = filename;
X if (fstat (inf[dir_arg].desc, &inf[dir_arg].stat) < 0)
X pfatal_with_name (inf[dir_arg].name);
X
X inf[dir_arg].dir_p
X = (S_IFDIR == (inf[dir_arg].stat.st_mode & S_IFMT));
X if (inf[dir_arg].dir_p)
X {
X error ("%s is a directory but %s is not",
X inf[dir_arg].name, inf[fnm_arg].name);
X val = 1;
X }
X else
X val = diff_2_files (inf, depth);
X }
X
X }
X else if (depth > 0 && (inf[0].dir_p || inf[1].dir_p))
X {
X /* Perhaps we have a subdirectory that exists only in one directory.
X If so, just print a message to that effect. */
X
X if (inf[0].desc == -1 || inf[1].desc == -1)
X {
X if (entire_new_file_flag && recursive)
X val = diff_dirs (inf[0].name, inf[1].name, compare_files, depth,
X inf[0].desc == -1, inf[1].desc == -1);
X else
X {
X char *dir = (inf[0].desc == -1) ? dir1 : dir0;
X message ("Only in %s: %s\n", dir, name0);
X val = 1;
X }
X }
X else
X {
X /* We have a subdirectory in one directory
X and a file in the other. */
X
X if (inf[0].dir_p)
X message ("%s is a directory but %s is not\n",
X inf[0].name, inf[1].name);
X else
X message ("%s is a directory but %s is not\n",
X inf[1].name, inf[0].name);
X /* This is a difference. */
X val = 1;
X }
X }
X else
X {
X
X /* Both exist and both are ordinary files. */
X
X val = diff_2_files (inf, depth);
X
X }
X
X /* Now the comparison has been done, if no error prevented it,
X and VAL is the value this function will return. */
X
X if (inf[0].desc >= 0)
X close (inf[0].desc);
X if (inf[1].desc >= 0)
X close (inf[1].desc);
X
X done:
X if (val == 0 && !inf[0].dir_p)
X {
X if (print_file_same_flag)
X message ("Files %s and %s are identical\n",
X inf[0].name, inf[1].name);
X }
X else
X fflush (stdout);
X
X if (dir0 != 0)
X free (inf[0].name);
X if (dir1 != 0)
X free (inf[1].name);
X
X return val;
X}
END_OF_FILE
if test 17901 -ne `wc -c <'diff.c'`; then
echo shar: \"'diff.c'\" unpacked with wrong size!
fi
# end of 'diff.c'
fi
if test -f 'getopt.c' -a "${1}" != "-c" ; then
echo shar: Will not clobber existing file \"'getopt.c'\"
else
echo shar: Extracting \"'getopt.c'\" \(16740 characters\)
sed "s/^X//" >'getopt.c' <<'END_OF_FILE'
X/* Getopt for GNU.
X Copyright (C) 1987, 1989, 1990 Free Software Foundation, Inc.
X
X This program is free software; you can redistribute it and/or modify
X it under the terms of the GNU General Public License as published by
X the Free Software Foundation; either version 1, or (at your option)
X any later version.
X
X This program is distributed in the hope that it will be useful,
X but WITHOUT ANY WARRANTY; without even the implied warranty of
X MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
X GNU General Public License for more details.
X
X You should have received a copy of the GNU General Public License
X along with this program; if not, write to the Free Software
X Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
X
X#ifdef __STDC__
X#define CONST const
X#else
X#define CONST
X#endif
X
X/* This version of `getopt' appears to the caller like standard Unix `getopt'
X but it behaves differently for the user, since it allows the user
X to intersperse the options with the other arguments.
X
X As `getopt' works, it permutes the elements of `argv' so that,
X when it is done, all the options precede everything else. Thus
X all application programs are extended to handle flexible argument order.
X
X Setting the environment variable _POSIX_OPTION_ORDER disables permutation.
X Then the behavior is completely standard.
X
X GNU application programs can use a third alternative mode in which
X they can distinguish the relative order of options and other arguments. */
X
X#include <stdio.h>
X
X/* If compiled with GNU C, use the built-in alloca */
X#ifdef __GNUC__
X#define alloca __builtin_alloca
X#else /* not __GNUC__ */
X#ifdef sparc
X#include <alloca.h>
X#else
Xchar *alloca ();
X#endif
X#endif /* not __GNUC__ */
X
X#if defined(STDC_HEADERS) || defined(__GNU_LIBRARY__)
X#include <stdlib.h>
X#include <string.h>
X#define bcopy(s, d, n) memcpy ((d), (s), (n))
X#define index strchr
X#else
X
X#ifdef USG
X#include <string.h>
X#define bcopy(s, d, n) memcpy ((d), (s), (n))
X#define index strchr
X#else
X#ifdef VMS
X#include <string.h>
X#else
X#include <strings.h>
X#endif
Xvoid bcopy ();
X#endif
X
Xchar *getenv ();
Xchar *malloc ();
X#endif
X
X/* For communication from `getopt' to the caller.
X When `getopt' finds an option that takes an argument,
X the argument value is returned here.
X Also, when `ordering' is RETURN_IN_ORDER,
X each non-option ARGV-element is returned here. */
X
Xchar *optarg = 0;
X
X/* Index in ARGV of the next element to be scanned.
X This is used for communication to and from the caller
X and for communication between successive calls to `getopt'.
X
X On entry to `getopt', zero means this is the first call; initialize.
X
X When `getopt' returns EOF, this is the index of the first of the
X non-option elements that the caller should itself scan.
X
X Otherwise, `optind' communicates from one call to the next
X how much of ARGV has been scanned so far. */
X
Xint optind = 0;
X
X/* The next char to be scanned in the option-element
X in which the last option character we returned was found.
X This allows us to pick up the scan where we left off.
X
X If this is zero, or a null string, it means resume the scan
X by advancing to the next ARGV-element. */
X
Xstatic char *nextchar;
X
X/* Callers store zero here to inhibit the error message
X for unrecognized options. */
X
Xint opterr = 1;
X
X/* Describe how to deal with options that follow non-option ARGV-elements.
X
X If the caller did not specify anything,
X the default is REQUIRE_ORDER if the environment variable
X _POSIX_OPTION_ORDER is defined, PERMUTE otherwise.
X
X REQUIRE_ORDER means don't recognize them as options;
X stop option processing when the first non-option is seen.
X This is what Unix does.
X This mode of operation is selected by either setting the environment
X variable _POSIX_OPTION_ORDER, or using `+' as the first character
X of the list of option characters.
X
X PERMUTE is the default. We permute the contents of ARGV as we scan,
X so that eventually all the non-options are at the end. This allows options
X to be given in any order, even with programs that were not written to
X expect this.
X
X RETURN_IN_ORDER is an option available to programs that were written
X to expect options and other ARGV-elements in any order and that care about
X the ordering of the two. We describe each non-option ARGV-element
X as if it were the argument of an option with character code 1.
X Using `-' as the first character of the list of option characters
X selects this mode of operation.
X
X The special argument `--' forces an end of option-scanning regardless
X of the value of `ordering'. In the case of RETURN_IN_ORDER, only
X `--' can cause `getopt' to return EOF with `optind' != ARGC. */
X
Xstatic enum
X{
X REQUIRE_ORDER, PERMUTE, RETURN_IN_ORDER
X} ordering;
X
X/* Describe the long-named options requested by the application.
X _GETOPT_LONG_OPTIONS is a vector of `struct option' terminated by an
X element containing a name which is zero.
X The field `has_arg' is 1 if the option takes an argument,
X 2 if it takes an optional argument. */
X
Xstruct option
X{
X char *name;
X int has_arg;
X int *flag;
X int val;
X};
X
XCONST struct option *_getopt_long_options;
X
Xint _getopt_long_only = 0;
X
X/* Index in _GETOPT_LONG_OPTIONS of the long-named option actually found.
X Only valid when a long-named option was found. */
X
Xint option_index;
X
X/* Handle permutation of arguments. */
X
X/* Describe the part of ARGV that contains non-options that have
X been skipped. `first_nonopt' is the index in ARGV of the first of them;
X `last_nonopt' is the index after the last of them. */
X
Xstatic int first_nonopt;
Xstatic int last_nonopt;
X
X/* Exchange two adjacent subsequences of ARGV.
X One subsequence is elements [first_nonopt,last_nonopt)
X which contains all the non-options that have been skipped so far.
X The other is elements [last_nonopt,optind), which contains all
X the options processed since those non-options were skipped.
X
X `first_nonopt' and `last_nonopt' are relocated so that they describe
X the new indices of the non-options in ARGV after they are moved. */
X
Xstatic void
Xexchange (argv)
X char **argv;
X{
X int nonopts_size = (last_nonopt - first_nonopt) * sizeof (char *);
X char **temp = (char **) alloca (nonopts_size);
X
X /* Interchange the two blocks of data in ARGV. */
X
X bcopy (&argv[first_nonopt], temp, nonopts_size);
X bcopy (&argv[last_nonopt], &argv[first_nonopt],
X (optind - last_nonopt) * sizeof (char *));
X bcopy (temp, &argv[first_nonopt + optind - last_nonopt], nonopts_size);
X
X /* Update records for the slots the non-options now occupy. */
X
X first_nonopt += (optind - last_nonopt);
X last_nonopt = optind;
X}
X
X/* Scan elements of ARGV (whose length is ARGC) for option characters
X given in OPTSTRING.
X
X If an element of ARGV starts with '-', and is not exactly "-" or "--",
X then it is an option element. The characters of this element
X (aside from the initial '-') are option characters. If `getopt'
X is called repeatedly, it returns successively each of the option characters
X from each of the option elements.
X
X If `getopt' finds another option character, it returns that character,
X updating `optind' and `nextchar' so that the next call to `getopt' can
X resume the scan with the following option character or ARGV-element.
X
X If there are no more option characters, `getopt' returns `EOF'.
X Then `optind' is the index in ARGV of the first ARGV-element
X that is not an option. (The ARGV-elements have been permuted
X so that those that are not options now come last.)
X
X OPTSTRING is a string containing the legitimate option characters.
X If an option character is seen that is not listed in OPTSTRING,
X return '?' after printing an error message. If you set `opterr' to
X zero, the error message is suppressed but we still return '?'.
X
X If a char in OPTSTRING is followed by a colon, that means it wants an arg,
X so the following text in the same ARGV-element, or the text of the following
X ARGV-element, is returned in `optarg'. Two colons mean an option that
X wants an optional arg; if there is text in the current ARGV-element,
X it is returned in `optarg', otherwise `optarg' is set to zero.
X
X If OPTSTRING starts with `-' or `+', it requests different methods of
X handling the non-option ARGV-elements.
X See the comments about RETURN_IN_ORDER and REQUIRE_ORDER, above.
X
X Long-named options begin with `+' instead of `-'.
X Their names may be abbreviated as long as the abbreviation is unique
X or is an exact match for some defined option. If they have an
X argument, it follows the option name in the same ARGV-element, separated
X from the option name by a `=', or else the in next ARGV-element.
X When `getopt' finds a long-named option, it returns 0 if that option's
X `flag' field is nonzero, the value of the option's `val' field
X otherwise. */
X
Xint
Xgetopt (argc, argv, optstring)
X int argc;
X char **argv;
X CONST char *optstring;
X{
X optarg = 0;
X
X /* Initialize the internal data when the first call is made.
X Start processing options with ARGV-element 1 (since ARGV-element 0
X is the program name); the sequence of previously skipped
X non-option ARGV-elements is empty. */
X
X if (optind == 0)
X {
X first_nonopt = last_nonopt = optind = 1;
X
X nextchar = 0;
X
X /* Determine how to handle the ordering of options and nonoptions. */
X
X if (optstring[0] == '-')
X {
X ordering = RETURN_IN_ORDER;
X ++optstring;
X }
X else if (optstring[0] == '+')
X {
X ordering = REQUIRE_ORDER;
X ++optstring;
X }
X else if (getenv ("_POSIX_OPTION_ORDER") != 0)
X ordering = REQUIRE_ORDER;
X else
X ordering = PERMUTE;
X }
X
X if (nextchar == 0 || *nextchar == 0)
X {
X if (ordering == PERMUTE)
X {
X /* If we have just processed some options following some non-options,
X exchange them so that the options come first. */
X
X if (first_nonopt != last_nonopt && last_nonopt != optind)
X exchange (argv);
X else if (last_nonopt != optind)
X first_nonopt = optind;
X
X /* Now skip any additional non-options
X and extend the range of non-options previously skipped. */
X
X while (optind < argc
X && (argv[optind][0] != '-'
X || argv[optind][1] == 0)
X && (_getopt_long_options == 0
X || argv[optind][0] != '+'
X || argv[optind][1] == 0))
X optind++;
X last_nonopt = optind;
X }
X
X /* Special ARGV-element `--' means premature end of options.
X Skip it like a null option,
X then exchange with previous non-options as if it were an option,
X then skip everything else like a non-option. */
X
X if (optind != argc && !strcmp (argv[optind], "--"))
X {
X optind++;
X
X if (first_nonopt != last_nonopt && last_nonopt != optind)
X exchange (argv);
X else if (first_nonopt == last_nonopt)
X first_nonopt = optind;
X last_nonopt = argc;
X
X optind = argc;
X }
X
X /* If we have done all the ARGV-elements, stop the scan
X and back over any non-options that we skipped and permuted. */
X
X if (optind == argc)
X {
X /* Set the next-arg-index to point at the non-options
X that we previously skipped, so the caller will digest them. */
X if (first_nonopt != last_nonopt)
X optind = first_nonopt;
X return EOF;
X }
X
X /* If we have come to a non-option and did not permute it,
X either stop the scan or describe it to the caller and pass it by. */
X
X if ((argv[optind][0] != '-' || argv[optind][1] == 0)
X && (_getopt_long_options == 0
X || argv[optind][0] != '+' || argv[optind][1] == 0))
X {
X if (ordering == REQUIRE_ORDER)
X return EOF;
X optarg = argv[optind++];
X return 1;
X }
X
X /* We have found another option-ARGV-element.
X Start decoding its characters. */
X
X nextchar = argv[optind] + 1;
X }
X
X if (_getopt_long_options != 0
X && (argv[optind][0] == '+'
X || (_getopt_long_only && argv[optind][0] == '-'))
X )
X {
X CONST struct option *p;
X char *s = nextchar;
X int exact = 0;
X int ambig = 0;
X CONST struct option *pfound = 0;
X int indfound;
X
X while (*s && *s != '=')
X s++;
X
X /* Test all options for either exact match or abbreviated matches. */
X for (p = _getopt_long_options, option_index = 0; p->name;
X p++, option_index++)
X if (!strncmp (p->name, nextchar, s - nextchar))
X {
X if (s - nextchar == strlen (p->name))
X {
X /* Exact match found. */
X pfound = p;
X indfound = option_index;
X exact = 1;
X break;
X }
X else if (pfound == 0)
X {
X /* First nonexact match found. */
X pfound = p;
X indfound = option_index;
X }
X else
X /* Second nonexact match found. */
X ambig = 1;
X }
X
X if (ambig && !exact)
X {
X fprintf (stderr, "%s: option `%s' is ambiguous\n",
X argv[0], argv[optind]);
X nextchar += strlen (nextchar);
X optind++;
X return '?';
X }
X
X if (pfound != 0)
X {
X option_index = indfound;
X optind++;
X if (*s)
X {
X if (pfound->has_arg > 0)
X optarg = s + 1;
X else
X {
X fprintf (stderr,
X "%s: option `%c%s' doesn't allow an argument\n",
X argv[0], argv[optind - 1][0], pfound->name);
X nextchar += strlen (nextchar);
X return '?';
X }
X }
X else if (pfound->has_arg == 1)
X {
X if (optind < argc)
X optarg = argv[optind++];
X else
X {
X fprintf (stderr, "%s: option `%s' requires an argument\n",
X argv[0], argv[optind - 1]);
X nextchar += strlen (nextchar);
X return '?';
X }
X }
X nextchar += strlen (nextchar);
X if (pfound->flag)
X {
X *(pfound->flag) = pfound->val;
X return 0;
X }
X return pfound->val;
X }
X /* Can't find it as a long option. If this is getopt_long_only,
X and the option starts with '-' and is a valid short
X option, then interpret it as a short option. Otherwise it's
X an error. */
X if (_getopt_long_only == 0 || argv[optind][0] == '+' ||
X index (optstring, *nextchar) == 0)
X {
X if (opterr != 0)
X fprintf (stderr, "%s: unrecognized option `%c%s'\n",
X argv[0], argv[optind][0], nextchar);
X nextchar += strlen (nextchar);
X optind++;
X return '?';
X }
X }
X
X /* Look at and handle the next option-character. */
X
X {
X char c = *nextchar++;
X char *temp = index (optstring, c);
X
X /* Increment `optind' when we start to process its last character. */
X if (*nextchar == 0)
X optind++;
X
X if (temp == 0 || c == ':')
X {
X if (opterr != 0)
X {
X if (c < 040 || c >= 0177)
X fprintf (stderr, "%s: unrecognized option, character code 0%o\n",
X argv[0], c);
X else
X fprintf (stderr, "%s: unrecognized option `-%c'\n",
X argv[0], c);
X }
X return '?';
X }
X if (temp[1] == ':')
X {
X if (temp[2] == ':')
X {
X /* This is an option that accepts an argument optionally. */
X if (*nextchar != 0)
X {
X optarg = nextchar;
X optind++;
X }
X else
X optarg = 0;
X nextchar = 0;
X }
X else
X {
X /* This is an option that requires an argument. */
X if (*nextchar != 0)
X {
X optarg = nextchar;
X /* If we end this ARGV-element by taking the rest as an arg,
X we must advance to the next element now. */
X optind++;
X }
X else if (optind == argc)
X {
X if (opterr != 0)
X fprintf (stderr, "%s: option `-%c' requires an argument\n",
X argv[0], c);
X c = '?';
X }
X else
X /* We already incremented `optind' once;
X increment it again when taking next ARGV-elt as argument. */
X optarg = argv[optind++];
X nextchar = 0;
X }
X }
X return c;
X }
X}
X
X#ifdef TEST
X
X/* Compile with -DTEST to make an executable for use in testing
X the above definition of `getopt'. */
X
Xint
Xmain (argc, argv)
X int argc;
X char **argv;
X{
X int c;
X int digit_optind = 0;
X
X while (1)
X {
X int this_option_optind = optind ? optind : 1;
X
X c = getopt (argc, argv, "abc:d:0123456789");
X if (c == EOF)
X break;
X
X switch (c)
X {
X case '0':
X case '1':
X case '2':
X case '3':
X case '4':
X case '5':
X case '6':
X case '7':
X case '8':
X case '9':
X if (digit_optind != 0 && digit_optind != this_option_optind)
X printf ("digits occur in two different argv-elements.\n");
X digit_optind = this_option_optind;
X printf ("option %c\n", c);
X break;
X
X case 'a':
X printf ("option a\n");
X break;
X
X case 'b':
X printf ("option b\n");
X break;
X
X case 'c':
X printf ("option c with value `%s'\n", optarg);
X break;
X
X case '?':
X break;
X
X default:
X printf ("?? getopt returned character code 0%o ??\n", c);
X }
X }
X
X if (optind < argc)
X {
X printf ("non-option ARGV-elements: ");
X while (optind < argc)
X printf ("%s ", argv[optind++]);
X printf ("\n");
X }
X
X exit (0);
X}
X
X#endif /* TEST */
END_OF_FILE
if test 16740 -ne `wc -c <'getopt.c'`; then
echo shar: \"'getopt.c'\" unpacked with wrong size!
fi
# end of 'getopt.c'
fi
if test -f 'util.c' -a "${1}" != "-c" ; then
echo shar: Will not clobber existing file \"'util.c'\"
else
echo shar: Extracting \"'util.c'\" \(14888 characters\)
sed "s/^X//" >'util.c' <<'END_OF_FILE'
X/* Support routines for GNU DIFF.
X Copyright (C) 1988, 1989 Free Software Foundation, Inc.
X
XThis file is part of GNU DIFF.
X
XGNU DIFF is free software; you can redistribute it and/or modify
Xit under the terms of the GNU General Public License as published by
Xthe Free Software Foundation; either version 1, or (at your option)
Xany later version.
X
XGNU DIFF is distributed in the hope that it will be useful,
Xbut WITHOUT ANY WARRANTY; without even the implied warranty of
XMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
XGNU General Public License for more details.
X
XYou should have received a copy of the GNU General Public License
Xalong with GNU DIFF; see the file COPYING. If not, write to
Xthe Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
X
X#include "diff.h"
X
X/* Use when a system call returns non-zero status.
X TEXT should normally be the file name. */
X
Xvoid
Xperror_with_name (text)
X char *text;
X{
X fprintf (stderr, "%s: ", program);
X perror (text);
X}
X
X/* Use when a system call returns non-zero status and that is fatal. */
X
Xvoid
Xpfatal_with_name (text)
X char *text;
X{
X print_message_queue ();
X fprintf (stderr, "%s: ", program);
X perror (text);
X exit (2);
X}
X
X/* Print an error message from the format-string FORMAT
X with args ARG1 and ARG2. */
X
Xvoid
Xerror (format, arg, arg1)
X char *format;
X char *arg;
X char *arg1;
X{
X fprintf (stderr, "%s: ", program);
X fprintf (stderr, format, arg, arg1);
X fprintf (stderr, "\n");
X}
X
X/* Print an error message containing the string TEXT, then exit. */
X
Xvoid
Xfatal (message)
X char *message;
X{
X print_message_queue ();
X error (message, "");
X exit (2);
X}
X
X/* Like printf, except if -l in effect then save the message and print later.
X This is used for things like "binary files differ" and "Only in ...". */
X
Xvoid
Xmessage (format, arg1, arg2)
X char *format, *arg1, *arg2;
X{
X if (paginate_flag)
X {
X struct msg *new = (struct msg *) xmalloc (sizeof (struct msg));
X if (msg_chain_end == 0)
X msg_chain = msg_chain_end = new;
X else
X {
X msg_chain_end->next = new;
X msg_chain_end = new;
X }
X new->format = format;
X new->arg1 = concat (arg1, "", "");
X new->arg2 = concat (arg2, "", "");
X new->next = 0;
X }
X else
X printf (format, arg1, arg2);
X}
X
X/* Output all the messages that were saved up by calls to `message'. */
X
Xvoid
Xprint_message_queue ()
X{
X struct msg *m;
X
X for (m = msg_chain; m; m = m->next)
X printf (m->format, m->arg1, m->arg2);
X}
X
X/* Call before outputting the results of comparing files NAME0 and NAME1
X to set up OUTFILE, the stdio stream for the output to go to.
X
X Usually, OUTFILE is just stdout. But when -l was specified
X we fork off a `pr' and make OUTFILE a pipe to it.
X `pr' then outputs to our stdout. */
X
Xvoid
Xsetup_output (name0, name1, depth)
X char *name0, *name1;
X int depth;
X{
X char *name;
X
X /* Construct the header of this piece of diff. */
X name = (char *) xmalloc (strlen (name0) + strlen (name1)
X + strlen (switch_string) + 15);
X
X strcpy (name, "diff");
X strcat (name, switch_string);
X strcat (name, " ");
X strcat (name, name0);
X strcat (name, " ");
X strcat (name, name1);
X
X if (paginate_flag)
X {
X int pipes[2];
X int desc;
X
X /* For a `pr' and make OUTFILE a pipe to it. */
X if (pipe (pipes) < 0)
X pfatal_with_name ("pipe");
X
X fflush (stdout);
X
X desc = vfork ();
X if (desc < 0)
X pfatal_with_name ("vfork");
X
X if (desc == 0)
X {
X close (pipes[1]);
X if (pipes[0] != fileno (stdin))
X {
X if (dup2 (pipes[0], fileno (stdin)) < 0)
X pfatal_with_name ("dup2");
X close (pipes[0]);
X }
X
X if (execl (PR_FILE_NAME, PR_FILE_NAME, "-f", "-h", name, 0) < 0)
X pfatal_with_name (PR_FILE_NAME);
X }
X else
X {
X close (pipes[0]);
X outfile = fdopen (pipes[1], "w");
X }
X }
X else
X {
X
X /* If -l was not specified, output the diff straight to `stdout'. */
X
X outfile = stdout;
X
X /* If handling multiple files (because scanning a directory),
X print which files the following output is about. */
X if (depth > 0)
X printf ("%s\n", name);
X }
X
X free (name);
X}
X
X/* Call after the end of output of diffs for one file.
X Close OUTFILE and get rid of the `pr' subfork. */
X
Xvoid
Xfinish_output ()
X{
X if (outfile != stdout)
X {
X fclose (outfile);
X wait (0);
X }
X}
X
X/* Compare two lines (typically one from each input file)
X according to the command line options.
X Each line is described by a `struct line_def'.
X Return 1 if the lines differ, like `bcmp'. */
X
Xint
Xline_cmp (s1, s2)
X struct line_def *s1, *s2;
X{
X register char *t1, *t2;
X register char end_char = line_end_char;
X int savechar;
X
X /* Check first for exact identity.
X If that is true, return 0 immediately.
X This detects the common case of exact identity
X faster than complete comparison would. */
X
X t1 = s1->text;
X t2 = s2->text;
X
X /* Alter the character following line 2 so it doesn't
X match that following line 1.
X (We used to alter the character after line 1,
X but that caused trouble if line 2 directly follows line 1.) */
X savechar = s2->text[s2->length];
X s2->text[s2->length] = s1->text[s1->length] + 1;
X
X /* Now find the first mismatch; this won't go past the
X character we just changed. */
X while (*t1++ == *t2++);
X
X /* Undo the alteration. */
X s2->text[s2->length] = savechar;
X
X /* If the comparison stopped at the alteration,
X the two lines are identical. */
X if (t2 == s2->text + s2->length + 1)
X return 0;
X
X /* Not exactly identical, but perhaps they match anyway
X when case or whitespace is ignored. */
X
X if (ignore_case_flag || ignore_space_change_flag || ignore_all_space_flag)
X {
X t1 = s1->text;
X t2 = s2->text;
X
X while (1)
X {
X register char c1 = *t1++;
X register char c2 = *t2++;
X
X /* Ignore horizontal whitespace if -b or -w is specified. */
X
X if (ignore_all_space_flag)
X {
X /* For -w, just skip past any spaces or tabs. */
X while (c1 == ' ' || c1 == '\t') c1 = *t1++;
X while (c2 == ' ' || c2 == '\t') c2 = *t2++;
X }
X else if (ignore_space_change_flag)
X {
X /* For -b, advance past any sequence of whitespace in line 1
X and consider it just one Space, or nothing at all
X if it is at the end of the line. */
X if (c1 == ' ' || c1 == '\t')
X {
X while (1)
X {
X c1 = *t1++;
X if (c1 == end_char)
X break;
X if (c1 != ' ' && c1 != '\t')
X {
X --t1;
X c1 = ' ';
X break;
X }
X }
X }
X
X /* Likewise for line 2. */
X if (c2 == ' ' || c2 == '\t')
X {
X while (1)
X {
X c2 = *t2++;
X if (c2 == end_char)
X break;
X if (c2 != ' ' && c2 != '\t')
X {
X --t2;
X c2 = ' ';
X break;
X }
X }
X }
X }
X
X /* Upcase all letters if -i is specified. */
X
X if (ignore_case_flag)
X {
X if (islower (c1))
X c1 = toupper (c1);
X if (islower (c2))
X c2 = toupper (c2);
X }
X
X if (c1 != c2)
X break;
X if (c1 == end_char)
X return 0;
X }
X }
X
X return (1);
X}
X
X/* Find the consecutive changes at the start of the script START.
X Return the last link before the first gap. */
X
Xstruct change *
Xfind_change (start)
X struct change *start;
X{
X return start;
X}
X
Xstruct change *
Xfind_reverse_change (start)
X struct change *start;
X{
X return start;
X}
X
X/* Divide SCRIPT into pieces by calling HUNKFUN and
X print each piece with PRINTFUN.
X Both functions take one arg, an edit script.
X
X HUNKFUN is called with the tail of the script
X and returns the last link that belongs together with the start
X of the tail.
X
X PRINTFUN takes a subscript which belongs together (with a null
X link at the end) and prints it. */
X
Xvoid
Xprint_script (script, hunkfun, printfun)
X struct change *script;
X struct change * (*hunkfun) ();
X void (*printfun) ();
X{
X struct change *next = script;
X
X while (next)
X {
X struct change *this, *end;
X
X /* Find a set of changes that belong together. */
X this = next;
X end = (*hunkfun) (next);
X
X /* Disconnect them from the rest of the changes,
X making them a hunk, and remember the rest for next iteration. */
X next = end->link;
X end->link = NULL;
X#ifdef DEBUG
X debug_script (this);
X#endif
X
X /* Print this hunk. */
X (*printfun) (this);
X
X /* Reconnect the script so it will all be freed properly. */
X end->link = next;
X }
X}
X
X/* Print the text of a single line LINE,
X flagging it with the characters in LINE_FLAG (which say whether
X the line is inserted, deleted, changed, etc.). */
X
Xvoid
Xprint_1_line (line_flag, line)
X char *line_flag;
X struct line_def *line;
X{
X int length = line->length; /* must be nonzero */
X const char *text = line->text; /* Help the compiler. */
X FILE *out = outfile; /* Help the compiler some more. */
X
X /* If -T was specified, use a Tab between the line-flag and the text.
X Otherwise use a Space (as Unix diff does).
X Print neither space nor tab if line-flags are empty. */
X
X if (line_flag != NULL && line_flag[0] != 0)
X fprintf (out, tab_align_flag ? "%s\t" : "%s ", line_flag);
X
X /* Now output the contents of the line.
X If -t was specified, expand tabs to spaces.
X Otherwise output verbatim. */
X
X if (tab_expand_flag)
X {
X register int column = 0;
X register int i;
X for (i = 0; i < line->length; i++)
X {
X register char c = line->text[i];
X switch (c)
X {
X case '\t':
X column++;
X while (column & 7)
X {
X putc (' ', out);
X column++;
X }
X c = ' ';
X break;
X case '\b':
X column--;
X break;
X default:
X column++;
X break;
X }
X putc (c, out);
X }
X }
X else
X fwrite (text, sizeof (char), length, out);
X if ((line_flag == NULL || line_flag[0] != 0) && text[length - 1] != '\n'
X && line_end_char == '\n')
X fprintf (out, "\n\\ No newline at end of file\n");
X}
X
Xchange_letter (inserts, deletes)
X int inserts, deletes;
X{
X if (!inserts)
X return 'd';
X else if (!deletes)
X return 'a';
X else
X return 'c';
X}
X
X/* Translate an internal line number (an index into diff's table of lines)
X into an actual line number in the input file.
X The internal line number is LNUM. FILE points to the data on the file.
X
X Internal line numbers count from 0 within the current chunk.
X Actual line numbers count from 1 within the entire file;
X in addition, they include lines ignored for comparison purposes.
X
X The `ltran' feature is no longer in use. */
X
Xint
Xtranslate_line_number (file, lnum)
X struct file_data *file;
X int lnum;
X{
X return lnum + 1;
X}
X
Xvoid
Xtranslate_range (file, a, b, aptr, bptr)
X struct file_data *file;
X int a, b;
X int *aptr, *bptr;
X{
X *aptr = translate_line_number (file, a - 1) + 1;
X *bptr = translate_line_number (file, b + 1) - 1;
X}
X
X/* Print a pair of line numbers with SEPCHAR, translated for file FILE.
X If the two numbers are identical, print just one number.
X
X Args A and B are internal line numbers.
X We print the translated (real) line numbers. */
X
Xvoid
Xprint_number_range (sepchar, file, a, b)
X char sepchar;
X struct file_data *file;
X int a, b;
X{
X int trans_a, trans_b;
X translate_range (file, a, b, &trans_a, &trans_b);
X
X /* Note: we can have B < A in the case of a range of no lines.
X In this case, we should print the line number before the range,
X which is B. */
X if (trans_b > trans_a)
X fprintf (outfile, "%d%c%d", trans_a, sepchar, trans_b);
X else
X fprintf (outfile, "%d", trans_b);
X}
X
X/* Look at a hunk of edit script and report the range of lines in each file
X that it applies to. HUNK is the start of the hunk, which is a chain
X of `struct change'. The first and last line numbers of file 0 are stored in
X *FIRST0 and *LAST0, and likewise for file 1 in *FIRST1 and *LAST1.
X Note that these are internal line numbers that count from 0.
X
X If no lines from file 0 are deleted, then FIRST0 is LAST0+1.
X
X Also set *DELETES nonzero if any lines of file 0 are deleted
X and set *INSERTS nonzero if any lines of file 1 are inserted.
X If only ignorable lines are inserted or deleted, both are
X set to 0. */
X
Xvoid
Xanalyze_hunk (hunk, first0, last0, first1, last1, deletes, inserts)
X struct change *hunk;
X int *first0, *last0, *first1, *last1;
X int *deletes, *inserts;
X{
X int f0, l0, f1, l1, show_from, show_to;
X int i;
X int nontrivial = !(ignore_blank_lines_flag || ignore_regexp);
X struct change *next;
X
X show_from = show_to = 0;
X
X f0 = hunk->line0;
X f1 = hunk->line1;
X
X for (next = hunk; next; next = next->link)
X {
X l0 = next->line0 + next->deleted - 1;
X l1 = next->line1 + next->inserted - 1;
X show_from += next->deleted;
X show_to += next->inserted;
X
X for (i = next->line0; i <= l0 && ! nontrivial; i++)
X if ((!ignore_blank_lines_flag || files[0].linbuf[i].length > 1)
X && (!ignore_regexp
X || 0 > re_search (&ignore_regexp_compiled,
X files[0].linbuf[i].text,
X files[0].linbuf[i].length, 0,
X files[0].linbuf[i].length, 0)))
X nontrivial = 1;
X
X for (i = next->line1; i <= l1 && ! nontrivial; i++)
X if ((!ignore_blank_lines_flag || files[1].linbuf[i].length > 1)
X && (!ignore_regexp
X || 0 > re_search (&ignore_regexp_compiled,
X files[1].linbuf[i].text,
X files[1].linbuf[i].length, 0,
X files[1].linbuf[i].length, 0)))
X nontrivial = 1;
X }
X
X *first0 = f0;
X *last0 = l0;
X *first1 = f1;
X *last1 = l1;
X
X /* If all inserted or deleted lines are ignorable,
X tell the caller to ignore this hunk. */
X
X if (!nontrivial)
X show_from = show_to = 0;
X
X *deletes = show_from;
X *inserts = show_to;
X}
X
X/* malloc a block of memory, with fatal error message if we can't do it. */
X
XVOID *
Xxmalloc (size)
X unsigned size;
X{
X register VOID *value;
X
X if (size == 0)
X size = 1;
X
X value = (VOID *) malloc (size);
X
X if (!value)
X fatal ("virtual memory exhausted");
X return value;
X}
X
X/* realloc a block of memory, with fatal error message if we can't do it. */
X
XVOID *
Xxrealloc (old, size)
X VOID *old;
X unsigned int size;
X{
X register VOID *value;
X
X if (size == 0)
X size = 1;
X
X value = (VOID *) realloc (old, size);
X
X if (!value)
X fatal ("virtual memory exhausted");
X return value;
X}
X
X/* Concatenate three strings, returning a newly malloc'd string. */
X
Xchar *
Xconcat (s1, s2, s3)
X char *s1, *s2, *s3;
X{
X int len = strlen (s1) + strlen (s2) + strlen (s3);
X char *new = (char *) xmalloc (len + 1);
X strcpy (new, s1);
X strcat (new, s2);
X strcat (new, s3);
X return new;
X}
X
Xdebug_script (sp)
X struct change *sp;
X{
X fflush (stdout);
X for (; sp; sp = sp->link)
X fprintf (stderr, "%3d %3d delete %d insert %d\n",
X sp->line0, sp->line1, sp->deleted, sp->inserted);
X fflush (stderr);
X}
END_OF_FILE
if test 14888 -ne `wc -c <'util.c'`; then
echo shar: \"'util.c'\" unpacked with wrong size!
fi
# end of 'util.c'
fi
echo shar: End of archive 3 \(of 8\).
cp /dev/null ark3isdone
MISSING=""
for I in 1 2 3 4 5 6 7 8 ; do
if test ! -f ark${I}isdone ; then
MISSING="${MISSING} ${I}"
fi
done
if test "${MISSING}" = "" ; then
echo You have unpacked all 8 archives.
rm -f ark[1-9]isdone
else
echo You still need to unpack the following archives:
echo " " ${MISSING}
fi
## End of shell archive.
exit 0
exit 0 # Just in case...
--
Please send comp.sources.unix-related mail to rsalz at uunet.uu.net.
Use a domain-based address or give alternate paths, or you may lose out.
More information about the Comp.sources.unix
mailing list