home *** CD-ROM | disk | FTP | other *** search
- /* Support routines for GNU DIFF.
- Copyright (C) 1988, 1989 Free Software Foundation, Inc.
-
- This file is part of GNU DIFF.
-
- GNU DIFF is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 1, or (at your option)
- any later version.
-
- GNU DIFF 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. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with GNU DIFF; see the file COPYING. If not, write to
- the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
-
- /* MS-DOS port (c) 1990 by Thorsten Ohl, ohl@gnu.ai.mit.edu
- This port is also distributed under the terms of the GNU General
- Public License as published by the Free Software Foundation.
-
- Please note that this file is not identical to the original GNU release,
- you should have received this code as patch to the official release.
-
- $Header: e:/gnu/diff/RCS/util.c 1.15.0.2 91/03/12 17:06:46 tho Exp $ */
-
- #include "diff.h"
- #include "regex.h" /* was missing [tho] */
-
- #ifdef MSDOS
- #include <conio.h>
- static char *_pipe_file (void);
- static void cleanup_pipe (void);
- #endif /* MSDOS */
-
- /* Use when a system call returns non-zero status.
- TEXT should normally be the file name. */
-
- void
- perror_with_name (text)
- char *text;
- {
- fprintf (stderr, "%s: ", program);
- perror (text);
- }
-
- /* Use when a system call returns non-zero status and that is fatal. */
-
- void
- pfatal_with_name (text)
- char *text;
- {
- print_message_queue ();
- fprintf (stderr, "%s: ", program);
- perror (text);
- exit (2);
- }
-
- /* Print an error message from the format-string FORMAT
- with args ARG1 and ARG2. */
-
- void
- error (format, arg, arg1)
- char *format;
- char *arg;
- char *arg1;
- {
- fprintf (stderr, "%s: ", program);
- fprintf (stderr, format, arg, arg1);
- fprintf (stderr, "\n");
- }
-
- /* Print an error message containing the string TEXT, then exit. */
-
- void
- fatal (message)
- char *message;
- {
- print_message_queue ();
- error (message, "");
- exit (2);
- }
-
- #ifdef MSDOS
- void
- fatal_with_name (char *message,char *name)
- {
- print_message_queue ();
- error ("%s: %s", name, message);
- exit (2);
- }
- #endif
-
- /* Like printf, except if -l in effect then save the message and print later.
- This is used for things like "binary files differ" and "Only in ...". */
-
- void
- message (format, arg1, arg2)
- char *format, *arg1, *arg2;
- {
- if (paginate_flag)
- {
- struct msg *new = (struct msg *) xmalloc (sizeof (struct msg));
- if (msg_chain_end == 0)
- msg_chain = msg_chain_end = new;
- else
- {
- msg_chain_end->next = new;
- msg_chain_end = new;
- }
- new->format = format;
- new->arg1 = concat (arg1, "", "");
- new->arg2 = concat (arg2, "", "");
- new->next = 0;
- }
- else
- printf (format, arg1, arg2);
- }
-
- /* Output all the messages that were saved up by calls to `message'. */
-
- void
- print_message_queue ()
- {
- struct msg *m;
-
- for (m = msg_chain; m; m = m->next)
- printf (m->format, m->arg1, m->arg2);
- }
-
- /* Call before outputting the results of comparing files NAME0 and NAME1
- to set up OUTFILE, the stdio stream for the output to go to.
-
- Usually, OUTFILE is just stdout. But when -l was specified
- we fork off a `pr' and make OUTFILE a pipe to it.
- `pr' then outputs to our stdout. */
-
- void
- setup_output (name0, name1, depth)
- char *name0, *name1;
- int depth;
- {
- char *name;
-
- /* Construct the header of this piece of diff. */
- name = (char *) xmalloc (strlen (name0) + strlen (name1)
- + strlen (switch_string) + 15);
-
- strcpy (name, "diff");
- strcat (name, switch_string);
- strcat (name, " ");
- strcat (name, name0);
- strcat (name, " ");
- strcat (name, name1);
-
- if (paginate_flag)
- {
-
- #ifdef MSDOS
-
- if ( !(outfile = fopen(_pipe_file (), "w")) )
- {
- error ("can't pipe to more, using stdout instead.");
- outfile = stdout;
- }
-
- #else
-
- int pipes[2];
- int desc;
-
- /* For a `pr' and make OUTFILE a pipe to it. */
- if (pipe (pipes) < 0)
- pfatal_with_name ("pipe");
-
- fflush (stdout);
-
- desc = vfork ();
- if (desc < 0)
- pfatal_with_name ("vfork");
-
- if (desc == 0)
- {
- close (pipes[1]);
- if (pipes[0] != fileno (stdin))
- {
- if (dup2 (pipes[0], fileno (stdin)) < 0)
- pfatal_with_name ("dup2");
- close (pipes[0]);
- }
-
- if (execl (PR_FILE_NAME, PR_FILE_NAME, "-f", "-h", name, 0) < 0)
- pfatal_with_name (PR_FILE_NAME);
- }
- else
- {
- close (pipes[0]);
- outfile = fdopen (pipes[1], "w");
- }
-
- #endif /* MSDOS */
-
- }
- else
- {
-
- /* If -l was not specified, output the diff straight to `stdout'. */
-
- outfile = stdout;
-
- /* If handling multiple files (because scanning a directory),
- print which files the following output is about. */
- if (depth > 0)
- printf ("%s\n", name);
- }
-
- free (name);
- }
-
- /* Call after the end of output of diffs for one file.
- Close OUTFILE and get rid of the `pr' subfork. */
-
- void
- finish_output ()
- {
- if (outfile != stdout)
- {
- #ifdef MSDOS
-
- #ifndef PAGER
- #define PAGER "more"
- #endif
- #ifndef PAGER_ARGS
- #define PAGER_ARGS NULL
- #endif
-
- int diff_stdin;
- int pager_stdin;
-
- fclose (outfile);
-
- diff_stdin = dup (0);
- pager_stdin = open (_pipe_file (), O_RDONLY|O_TEXT);
- dup2 (pager_stdin, 0);
-
- spawnlp (P_WAIT, PAGER, PAGER, PAGER_ARGS, NULL);
-
- dup2 (diff_stdin, 0);
- close (diff_stdin);
- close (pager_stdin);
-
- printf ("hit any key to continue...");
- while (!kbhit ())
- ;
-
- printf ("\n");
-
- #else /* not MSDOS */
-
- fclose (outfile);
- wait (0);
-
- #endif /* not MSDOS */
- }
- }
-
- /* Compare two lines (typically one from each input file)
- according to the command line options.
- Each line is described by a `struct line_def'.
- Return 1 if the lines differ, like `bcmp'. */
-
- int
- line_cmp (s1, s2)
- struct line_def *s1, *s2;
- {
- register char _huge *t1, _huge *t2;
- register char end_char = line_end_char;
- #ifdef MSDOS
- char savechar;
- #else /* not MSDOS */
- int savechar;
- #endif /* not MSDOS */
-
- /* Check first for exact identity.
- If that is true, return 0 immediately.
- This detects the common case of exact identity
- faster than complete comparison would. */
-
- t1 = s1->text;
- t2 = s2->text;
-
- /* Alter the character following line 2 so it doesn't
- match that following line 1.
- (We used to alter the character after line 1,
- but that caused trouble if line 2 directly follows line 1.) */
- savechar = s2->text[s2->length];
- s2->text[s2->length] = s1->text[s1->length] + 1;
-
- /* Now find the first mismatch; this won't go past the
- character we just changed. */
- while (*t1++ == *t2++);
-
- /* Undo the alteration. */
- s2->text[s2->length] = savechar;
-
- /* If the comparison stopped at the alteration,
- the two lines are identical. */
- if (t2 == s2->text + s2->length + 1)
- return 0;
-
- /* Not exactly identical, but perhaps they match anyway
- when case or whitespace is ignored. */
-
- if (ignore_case_flag || ignore_space_change_flag || ignore_all_space_flag)
- {
- t1 = s1->text;
- t2 = s2->text;
-
- while (1)
- {
- register char c1 = *t1++;
- register char c2 = *t2++;
-
- /* Ignore horizontal whitespace if -b or -w is specified. */
-
- if (ignore_all_space_flag)
- {
- /* For -w, just skip past any spaces or tabs. */
- while (c1 == ' ' || c1 == '\t') c1 = *t1++;
- while (c2 == ' ' || c2 == '\t') c2 = *t2++;
- }
- else if (ignore_space_change_flag)
- {
- /* For -b, advance past any sequence of whitespace in line 1
- and consider it just one Space, or nothing at all
- if it is at the end of the line. */
- if (c1 == ' ' || c1 == '\t')
- {
- while (1)
- {
- c1 = *t1++;
- if (c1 == end_char)
- break;
- if (c1 != ' ' && c1 != '\t')
- {
- --t1;
- c1 = ' ';
- break;
- }
- }
- }
-
- /* Likewise for line 2. */
- if (c2 == ' ' || c2 == '\t')
- {
- while (1)
- {
- c2 = *t2++;
- if (c2 == end_char)
- break;
- if (c2 != ' ' && c2 != '\t')
- {
- --t2;
- c2 = ' ';
- break;
- }
- }
- }
- }
-
- /* Upcase all letters if -i is specified. */
-
- if (ignore_case_flag)
- {
- if (islower (c1))
- c1 = toupper (c1);
- if (islower (c2))
- c2 = toupper (c2);
- }
-
- if (c1 != c2)
- break;
- if (c1 == end_char)
- return 0;
- }
- }
-
- return (1);
- }
-
- /* Find the consecutive changes at the start of the script START.
- Return the last link before the first gap. */
-
- struct change *
- find_change (start)
- struct change *start;
- {
- return start;
- }
-
- struct change *
- find_reverse_change (start)
- struct change *start;
- {
- return start;
- }
-
- /* Divide SCRIPT into pieces by calling HUNKFUN and
- print each piece with PRINTFUN.
- Both functions take one arg, an edit script.
-
- HUNKFUN is called with the tail of the script
- and returns the last link that belongs together with the start
- of the tail.
-
- PRINTFUN takes a subscript which belongs together (with a null
- link at the end) and prints it. */
-
- #ifdef MSDOS
- void
- print_script ( struct change *script,
- struct change * (*hunkfun) (struct change *),
- void (*printfun) (struct change *) )
- #else
- void
- print_script (script, hunkfun, printfun)
- struct change *script;
- struct change * (*hunkfun) ();
- void (*printfun) ();
- #endif /* MSDOS */
- {
- struct change *next = script;
-
- while (next)
- {
- struct change *this, *end;
-
- /* Find a set of changes that belong together. */
- this = next;
- end = (*hunkfun) (next);
-
- /* Disconnect them from the rest of the changes,
- making them a hunk, and remember the rest for next iteration. */
- next = end->link;
- end->link = NULL;
- #ifdef DEBUG
- debug_script (this);
- #endif
-
- /* Print this hunk. */
- (*printfun) (this);
-
- /* Reconnect the script so it will all be freed properly. */
- end->link = next;
- }
- }
-
- /* Print the text of a single line LINE,
- flagging it with the characters in LINE_FLAG (which say whether
- the line is inserted, deleted, changed, etc.). */
-
- void
- print_1_line (line_flag, line)
- char *line_flag;
- struct line_def *line;
- {
- int length = line->length; /* must be nonzero */
- const char *text = line->text; /* Help the compiler. */
- FILE *out = outfile; /* Help the compiler some more. */
-
- /* If -T was specified, use a Tab between the line-flag and the text.
- Otherwise use a Space (as Unix diff does).
- Print neither space nor tab if line-flags are empty. */
-
- if (line_flag != NULL && line_flag[0] != 0)
- fprintf (out, tab_align_flag ? "%s\t" : "%s ", line_flag);
-
- /* Now output the contents of the line.
- If -t was specified, expand tabs to spaces.
- Otherwise output verbatim. */
-
- if (tab_expand_flag)
- {
- register int column = 0;
- register int i;
- for (i = 0; i < line->length; i++)
- {
- register char c = line->text[i];
- switch (c)
- {
- case '\t':
- column++;
- while (column & 7)
- {
- putc (' ', out);
- column++;
- }
- c = ' ';
- break;
- case '\b':
- column--;
- break;
- default:
- column++;
- break;
- }
- putc (c, out);
- }
- }
- else
- fwrite (text, sizeof (char), length, out);
- if ((line_flag == NULL || line_flag[0] != 0) && text[length - 1] != '\n'
- && line_end_char == '\n')
- fprintf (out, "\n\\ No newline at end of file\n");
- }
-
- change_letter (inserts, deletes)
- int inserts, deletes;
- {
- if (!inserts)
- return 'd';
- else if (!deletes)
- return 'a';
- else
- return 'c';
- }
-
- /* Translate an internal line number (an index into diff's table of lines)
- into an actual line number in the input file.
- The internal line number is LNUM. FILE points to the data on the file.
-
- Internal line numbers count from 0 within the current chunk.
- Actual line numbers count from 1 within the entire file;
- in addition, they include lines ignored for comparison purposes.
-
- The `ltran' feature is no longer in use. */
-
- int
- translate_line_number (file, lnum)
- struct file_data *file;
- int lnum;
- {
- return lnum + 1;
- }
-
- void
- translate_range (file, a, b, aptr, bptr)
- struct file_data *file;
- int a, b;
- int *aptr, *bptr;
- {
- *aptr = translate_line_number (file, a - 1) + 1;
- *bptr = translate_line_number (file, b + 1) - 1;
- }
-
- /* Print a pair of line numbers with SEPCHAR, translated for file FILE.
- If the two numbers are identical, print just one number.
-
- Args A and B are internal line numbers.
- We print the translated (real) line numbers. */
-
- void
- print_number_range (sepchar, file, a, b)
- char sepchar;
- struct file_data *file;
- int a, b;
- {
- int trans_a, trans_b;
- translate_range (file, a, b, &trans_a, &trans_b);
-
- /* Note: we can have B < A in the case of a range of no lines.
- In this case, we should print the line number before the range,
- which is B. */
- if (trans_b > trans_a)
- fprintf (outfile, "%d%c%d", trans_a, sepchar, trans_b);
- else
- fprintf (outfile, "%d", trans_b);
- }
-
- /* Look at a hunk of edit script and report the range of lines in each file
- that it applies to. HUNK is the start of the hunk, which is a chain
- of `struct change'. The first and last line numbers of file 0 are stored in
- *FIRST0 and *LAST0, and likewise for file 1 in *FIRST1 and *LAST1.
- Note that these are internal line numbers that count from 0.
-
- If no lines from file 0 are deleted, then FIRST0 is LAST0+1.
-
- Also set *DELETES nonzero if any lines of file 0 are deleted
- and set *INSERTS nonzero if any lines of file 1 are inserted.
- If only ignorable lines are inserted or deleted, both are
- set to 0. */
-
- void
- analyze_hunk (hunk, first0, last0, first1, last1, deletes, inserts)
- struct change *hunk;
- int *first0, *last0, *first1, *last1;
- int *deletes, *inserts;
- {
- int f0, l0, f1, l1, show_from, show_to;
- int i;
- int nontrivial = !(ignore_blank_lines_flag || ignore_regexp);
- struct change *next;
-
- show_from = show_to = 0;
-
- f0 = hunk->line0;
- f1 = hunk->line1;
-
- for (next = hunk; next; next = next->link)
- {
- l0 = next->line0 + next->deleted - 1;
- l1 = next->line1 + next->inserted - 1;
- show_from += next->deleted;
- show_to += next->inserted;
-
- for (i = next->line0; i <= l0 && ! nontrivial; i++)
- if ((!ignore_blank_lines_flag || files[0].linbuf[i].length > 1)
- && (!ignore_regexp
- || 0 > re_search (&ignore_regexp_compiled,
- files[0].linbuf[i].text,
- files[0].linbuf[i].length, 0,
- files[0].linbuf[i].length, 0)))
- nontrivial = 1;
-
- for (i = next->line1; i <= l1 && ! nontrivial; i++)
- if ((!ignore_blank_lines_flag || files[1].linbuf[i].length > 1)
- && (!ignore_regexp
- || 0 > re_search (&ignore_regexp_compiled,
- files[1].linbuf[i].text,
- files[1].linbuf[i].length, 0,
- files[1].linbuf[i].length, 0)))
- nontrivial = 1;
- }
-
- *first0 = f0;
- *last0 = l0;
- *first1 = f1;
- *last1 = l1;
-
- /* If all inserted or deleted lines are ignorable,
- tell the caller to ignore this hunk. */
-
- if (!nontrivial)
- show_from = show_to = 0;
-
- *deletes = show_from;
- *inserts = show_to;
- }
-
- /* malloc a block of memory, with fatal error message if we can't do it. */
-
- #ifdef MSDOS
- #include <malloc.h>
- #include <dos.h>
-
- void _huge *
- xhalloc (long size)
- {
- void _huge *value = (void _huge *) halloc (size, (size_t)1 );
-
- if (!value)
- fatal ("virtual memory exhausted");
- return value;
- }
-
- /* Here we do a huge "realloc" by allocating a new block and
- moving the old block afterwards. This is *slow*, but should
- be reliable. */
-
- void _huge *
- xhrealloc (void _huge *ptr, long new_size, long old_size)
- {
- void _huge *value = (void _huge *) halloc (new_size, (size_t)1 );
-
- if (!value)
- fatal ("virtual memory exhausted");
- else
- {
- char _huge *dest = value;
- char _huge *src = ptr;
-
- while (old_size > 0L)
- {
- unsigned int bytes = (unsigned int) min (0x8000L, old_size);
- memcpy (dest, src, bytes);
- old_size -= (long) bytes;
- dest += (long) bytes;
- src += (long) bytes;
- }
- }
- hfree (ptr);
- return value;
- }
-
- long /* doesn't belong here, but is also a 'huge' pointer kludge */
- hread (int fd, void _huge *buffer, long bytes)
- {
- long bytes_read = 0L;
-
- while (1)
- {
- int n = read (fd, buffer, (unsigned int) min (0x4000L, bytes));
-
- if (n > 0)
- {
- bytes_read += (long) n;
- bytes -= (long) n;
- /* we can't say buffer += n here, because we have to make
- sure that the offset of BUFFER doesn't overflow during
- the read() system call. Therefore we add what we read
- to the segment of BUFFER. */
- FP_SEG(buffer) += (n >> 4);
- FP_OFF(buffer) += (n & 0x0F);
- }
- else if (n == 0)
- return bytes_read; /* done */
- else
- pfatal_with_name ("error while reading input");
- }
- }
-
- #endif /* MSDOS */
-
- VOID *
- xmalloc (size)
- unsigned size;
- {
- register VOID *value;
-
- if (size == 0)
- size = 1;
-
- value = (VOID *) malloc (size);
-
- if (!value)
- fatal ("virtual memory exhausted");
- return value;
- }
-
- /* realloc a block of memory, with fatal error message if we can't do it. */
-
- VOID *
- xrealloc (old, size)
- VOID *old;
- unsigned int size;
- {
- register VOID *value;
-
- if (size == 0)
- size = 1;
-
- value = (VOID *) realloc (old, size);
-
- if (!value)
- fatal ("virtual memory exhausted");
- return value;
- }
-
- /* Concatenate three strings, returning a newly malloc'd string. */
-
- char *
- concat (s1, s2, s3)
- char *s1, *s2, *s3;
- {
- int len = strlen (s1) + strlen (s2) + strlen (s3);
- char *new = (char *) xmalloc (len + 1);
- strcpy (new, s1);
- strcat (new, s2);
- strcat (new, s3);
- return new;
- }
-
- #ifdef MSDOS
- void
- #endif /* MSDOS */
- debug_script (sp)
- struct change *sp;
- {
- fflush (stdout);
- for (; sp; sp = sp->link)
- fprintf (stderr, "%3d %3d delete %d insert %d\n",
- sp->line0, sp->line1, sp->deleted, sp->inserted);
- fflush (stderr);
- }
-
- #ifdef MSDOS
-
- static char *pipe_file = NULL;
- static char *tmpdir = NULL;
- static int tmpdirlen;
-
- /* Return the name of a temporary file. */
- char *
- _pipe_file (void)
- {
- if (!pipe_file)
- {
- if (!tmpdir)
- {
- /* Initialize. */
-
- atexit (cleanup_pipe);
-
- tmpdir = getenv ("TMP");
-
- if (tmpdir)
- {
- tmpdirlen = strlen (tmpdir);
- if (tmpdir[tmpdirlen - 1] == '/'
- || tmpdir[tmpdirlen - 1] == '\\')
- tmpdir[tmpdirlen - 1] = '\0';
- }
- else
- {
- tmpdir = ".";
- tmpdirlen = 1;
- }
- }
-
- pipe_file = (char *) xmalloc (tmpdirlen + 14);
- sprintf (pipe_file, "%s/diff%04x", tmpdir, getpid ());
- }
-
- return pipe_file;
- }
-
-
- /* Clean up after we are done. */
-
- void
- cleanup_pipe (void)
- {
- unlink (pipe_file);
- }
-
- #endif /* MSDOS */
-
-