1*37280775Sroy /* $NetBSD: util.c,v 1.2 2020/12/13 00:04:40 roy Exp $ */
275f6d617Schristos
375f6d617Schristos /* Support routines for GNU DIFF.
475f6d617Schristos
575f6d617Schristos Copyright (C) 1988, 1989, 1992, 1993, 1994, 1995, 1998, 2001, 2002
675f6d617Schristos Free Software Foundation, Inc.
775f6d617Schristos
875f6d617Schristos This file is part of GNU DIFF.
975f6d617Schristos
1075f6d617Schristos GNU DIFF is free software; you can redistribute it and/or modify
1175f6d617Schristos it under the terms of the GNU General Public License as published by
1275f6d617Schristos the Free Software Foundation; either version 2, or (at your option)
1375f6d617Schristos any later version.
1475f6d617Schristos
1575f6d617Schristos GNU DIFF is distributed in the hope that it will be useful,
1675f6d617Schristos but WITHOUT ANY WARRANTY; without even the implied warranty of
1775f6d617Schristos MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1875f6d617Schristos GNU General Public License for more details.
1975f6d617Schristos
2075f6d617Schristos You should have received a copy of the GNU General Public License
2175f6d617Schristos along with this program; see the file COPYING.
2275f6d617Schristos If not, write to the Free Software Foundation,
2375f6d617Schristos 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
2475f6d617Schristos
2575f6d617Schristos #include "diff.h"
2675f6d617Schristos #include <dirname.h>
2775f6d617Schristos #include <error.h>
2875f6d617Schristos #include <quotesys.h>
2975f6d617Schristos #include <regex.h>
3075f6d617Schristos #include <xalloc.h>
3175f6d617Schristos
3275f6d617Schristos char const pr_program[] = PR_PROGRAM;
3375f6d617Schristos
3475f6d617Schristos /* Queue up one-line messages to be printed at the end,
3575f6d617Schristos when -l is specified. Each message is recorded with a `struct msg'. */
3675f6d617Schristos
3775f6d617Schristos struct msg
3875f6d617Schristos {
3975f6d617Schristos struct msg *next;
4075f6d617Schristos char args[1]; /* Format + 4 args, each '\0' terminated, concatenated. */
4175f6d617Schristos };
4275f6d617Schristos
4375f6d617Schristos /* Head of the chain of queues messages. */
4475f6d617Schristos
4575f6d617Schristos static struct msg *msg_chain;
4675f6d617Schristos
4775f6d617Schristos /* Tail of the chain of queues messages. */
4875f6d617Schristos
4975f6d617Schristos static struct msg **msg_chain_end = &msg_chain;
5075f6d617Schristos
5175f6d617Schristos /* Use when a system call returns non-zero status.
5275f6d617Schristos NAME should normally be the file name. */
5375f6d617Schristos
5475f6d617Schristos void
perror_with_name(char const * name)5575f6d617Schristos perror_with_name (char const *name)
5675f6d617Schristos {
5775f6d617Schristos error (0, errno, "%s", name);
5875f6d617Schristos }
5975f6d617Schristos
6075f6d617Schristos /* Use when a system call returns non-zero status and that is fatal. */
6175f6d617Schristos
6275f6d617Schristos void
pfatal_with_name(char const * name)6375f6d617Schristos pfatal_with_name (char const *name)
6475f6d617Schristos {
6575f6d617Schristos int e = errno;
6675f6d617Schristos print_message_queue ();
6775f6d617Schristos error (EXIT_TROUBLE, e, "%s", name);
6875f6d617Schristos abort ();
6975f6d617Schristos }
7075f6d617Schristos
7175f6d617Schristos /* Print an error message containing MSGID, then exit. */
7275f6d617Schristos
7375f6d617Schristos void
fatal(char const * msgid)7475f6d617Schristos fatal (char const *msgid)
7575f6d617Schristos {
7675f6d617Schristos print_message_queue ();
7775f6d617Schristos error (EXIT_TROUBLE, 0, "%s", _(msgid));
7875f6d617Schristos abort ();
7975f6d617Schristos }
8075f6d617Schristos
8175f6d617Schristos /* Like printf, except if -l in effect then save the message and print later.
8275f6d617Schristos This is used for things like "Only in ...". */
8375f6d617Schristos
8475f6d617Schristos void
message(char const * format_msgid,char const * arg1,char const * arg2)8575f6d617Schristos message (char const *format_msgid, char const *arg1, char const *arg2)
8675f6d617Schristos {
8775f6d617Schristos message5 (format_msgid, arg1, arg2, 0, 0);
8875f6d617Schristos }
8975f6d617Schristos
9075f6d617Schristos void
message5(char const * format_msgid,char const * arg1,char const * arg2,char const * arg3,char const * arg4)9175f6d617Schristos message5 (char const *format_msgid, char const *arg1, char const *arg2,
9275f6d617Schristos char const *arg3, char const *arg4)
9375f6d617Schristos {
9475f6d617Schristos if (paginate)
9575f6d617Schristos {
9675f6d617Schristos char *p;
9775f6d617Schristos char const *arg[5];
9875f6d617Schristos int i;
9975f6d617Schristos size_t size[5];
10075f6d617Schristos size_t total_size = offsetof (struct msg, args);
10175f6d617Schristos struct msg *new;
10275f6d617Schristos
10375f6d617Schristos arg[0] = format_msgid;
10475f6d617Schristos arg[1] = arg1;
10575f6d617Schristos arg[2] = arg2;
10675f6d617Schristos arg[3] = arg3 ? arg3 : "";
10775f6d617Schristos arg[4] = arg4 ? arg4 : "";
10875f6d617Schristos
10975f6d617Schristos for (i = 0; i < 5; i++)
11075f6d617Schristos total_size += size[i] = strlen (arg[i]) + 1;
11175f6d617Schristos
11275f6d617Schristos new = xmalloc (total_size);
11375f6d617Schristos
11475f6d617Schristos for (i = 0, p = new->args; i < 5; p += size[i++])
11575f6d617Schristos memcpy (p, arg[i], size[i]);
11675f6d617Schristos
11775f6d617Schristos *msg_chain_end = new;
11875f6d617Schristos new->next = 0;
11975f6d617Schristos msg_chain_end = &new->next;
12075f6d617Schristos }
12175f6d617Schristos else
12275f6d617Schristos {
12375f6d617Schristos if (sdiff_merge_assist)
12475f6d617Schristos putchar (' ');
12575f6d617Schristos printf (_(format_msgid), arg1, arg2, arg3, arg4);
12675f6d617Schristos }
12775f6d617Schristos }
12875f6d617Schristos
12975f6d617Schristos /* Output all the messages that were saved up by calls to `message'. */
13075f6d617Schristos
13175f6d617Schristos void
print_message_queue(void)13275f6d617Schristos print_message_queue (void)
13375f6d617Schristos {
13475f6d617Schristos char const *arg[5];
13575f6d617Schristos int i;
13675f6d617Schristos struct msg *m = msg_chain;
13775f6d617Schristos
13875f6d617Schristos while (m)
13975f6d617Schristos {
14075f6d617Schristos struct msg *next = m->next;
14175f6d617Schristos arg[0] = m->args;
14275f6d617Schristos for (i = 0; i < 4; i++)
14375f6d617Schristos arg[i + 1] = arg[i] + strlen (arg[i]) + 1;
14475f6d617Schristos printf (_(arg[0]), arg[1], arg[2], arg[3], arg[4]);
14575f6d617Schristos free (m);
14675f6d617Schristos m = next;
14775f6d617Schristos }
14875f6d617Schristos }
14975f6d617Schristos
15075f6d617Schristos /* Call before outputting the results of comparing files NAME0 and NAME1
15175f6d617Schristos to set up OUTFILE, the stdio stream for the output to go to.
15275f6d617Schristos
15375f6d617Schristos Usually, OUTFILE is just stdout. But when -l was specified
15475f6d617Schristos we fork off a `pr' and make OUTFILE a pipe to it.
15575f6d617Schristos `pr' then outputs to our stdout. */
15675f6d617Schristos
15775f6d617Schristos static char const *current_name0;
15875f6d617Schristos static char const *current_name1;
15975f6d617Schristos static bool currently_recursive;
16075f6d617Schristos
16175f6d617Schristos void
setup_output(char const * name0,char const * name1,bool recursive)16275f6d617Schristos setup_output (char const *name0, char const *name1, bool recursive)
16375f6d617Schristos {
16475f6d617Schristos current_name0 = name0;
16575f6d617Schristos current_name1 = name1;
16675f6d617Schristos currently_recursive = recursive;
16775f6d617Schristos outfile = 0;
16875f6d617Schristos }
16975f6d617Schristos
17075f6d617Schristos #if HAVE_WORKING_FORK || HAVE_WORKING_VFORK
17175f6d617Schristos static pid_t pr_pid;
17275f6d617Schristos #endif
17375f6d617Schristos
17475f6d617Schristos void
begin_output(void)17575f6d617Schristos begin_output (void)
17675f6d617Schristos {
17775f6d617Schristos char *name;
17875f6d617Schristos
17975f6d617Schristos if (outfile != 0)
18075f6d617Schristos return;
18175f6d617Schristos
18275f6d617Schristos /* Construct the header of this piece of diff. */
18375f6d617Schristos name = xmalloc (strlen (current_name0) + strlen (current_name1)
18475f6d617Schristos + strlen (switch_string) + 7);
18575f6d617Schristos
18675f6d617Schristos /* POSIX 1003.1-2001 specifies this format. But there are some bugs in
18775f6d617Schristos the standard: it says that we must print only the last component
18875f6d617Schristos of the pathnames, and it requires two spaces after "diff" if
18975f6d617Schristos there are no options. These requirements are silly and do not
19075f6d617Schristos match historical practice. */
19175f6d617Schristos sprintf (name, "diff%s %s %s", switch_string, current_name0, current_name1);
19275f6d617Schristos
19375f6d617Schristos if (paginate)
19475f6d617Schristos {
19575f6d617Schristos if (fflush (stdout) != 0)
19675f6d617Schristos pfatal_with_name (_("write failed"));
19775f6d617Schristos
19875f6d617Schristos /* Make OUTFILE a pipe to a subsidiary `pr'. */
19975f6d617Schristos {
20075f6d617Schristos #if HAVE_WORKING_FORK || HAVE_WORKING_VFORK
20175f6d617Schristos int pipes[2];
20275f6d617Schristos
20375f6d617Schristos if (pipe (pipes) != 0)
20475f6d617Schristos pfatal_with_name ("pipe");
20575f6d617Schristos
20675f6d617Schristos pr_pid = vfork ();
20775f6d617Schristos if (pr_pid < 0)
20875f6d617Schristos pfatal_with_name ("fork");
20975f6d617Schristos
21075f6d617Schristos if (pr_pid == 0)
21175f6d617Schristos {
21275f6d617Schristos close (pipes[1]);
21375f6d617Schristos if (pipes[0] != STDIN_FILENO)
21475f6d617Schristos {
21575f6d617Schristos if (dup2 (pipes[0], STDIN_FILENO) < 0)
21675f6d617Schristos pfatal_with_name ("dup2");
21775f6d617Schristos close (pipes[0]);
21875f6d617Schristos }
21975f6d617Schristos
220*37280775Sroy execl (pr_program, pr_program, "-h", name, NULL);
22175f6d617Schristos _exit (errno == ENOEXEC ? 126 : 127);
22275f6d617Schristos }
22375f6d617Schristos else
22475f6d617Schristos {
22575f6d617Schristos close (pipes[0]);
22675f6d617Schristos outfile = fdopen (pipes[1], "w");
22775f6d617Schristos if (!outfile)
22875f6d617Schristos pfatal_with_name ("fdopen");
22975f6d617Schristos }
23075f6d617Schristos #else
23175f6d617Schristos char *command = xmalloc (sizeof pr_program - 1 + 7
23275f6d617Schristos + quote_system_arg ((char *) 0, name) + 1);
23375f6d617Schristos char *p;
23475f6d617Schristos sprintf (command, "%s -f -h ", pr_program);
23575f6d617Schristos p = command + sizeof pr_program - 1 + 7;
23675f6d617Schristos p += quote_system_arg (p, name);
23775f6d617Schristos *p = 0;
23875f6d617Schristos errno = 0;
23975f6d617Schristos outfile = popen (command, "w");
24075f6d617Schristos if (!outfile)
24175f6d617Schristos pfatal_with_name (command);
24275f6d617Schristos free (command);
24375f6d617Schristos #endif
24475f6d617Schristos }
24575f6d617Schristos }
24675f6d617Schristos else
24775f6d617Schristos {
24875f6d617Schristos
24975f6d617Schristos /* If -l was not specified, output the diff straight to `stdout'. */
25075f6d617Schristos
25175f6d617Schristos outfile = stdout;
25275f6d617Schristos
25375f6d617Schristos /* If handling multiple files (because scanning a directory),
25475f6d617Schristos print which files the following output is about. */
25575f6d617Schristos if (currently_recursive)
25675f6d617Schristos printf ("%s\n", name);
25775f6d617Schristos }
25875f6d617Schristos
25975f6d617Schristos free (name);
26075f6d617Schristos
26175f6d617Schristos /* A special header is needed at the beginning of context output. */
26275f6d617Schristos switch (output_style)
26375f6d617Schristos {
26475f6d617Schristos case OUTPUT_CONTEXT:
26575f6d617Schristos print_context_header (files, 0);
26675f6d617Schristos break;
26775f6d617Schristos
26875f6d617Schristos case OUTPUT_UNIFIED:
26975f6d617Schristos print_context_header (files, 1);
27075f6d617Schristos break;
27175f6d617Schristos
27275f6d617Schristos default:
27375f6d617Schristos break;
27475f6d617Schristos }
27575f6d617Schristos }
27675f6d617Schristos
27775f6d617Schristos /* Call after the end of output of diffs for one file.
27875f6d617Schristos Close OUTFILE and get rid of the `pr' subfork. */
27975f6d617Schristos
28075f6d617Schristos void
finish_output(void)28175f6d617Schristos finish_output (void)
28275f6d617Schristos {
28375f6d617Schristos if (outfile != 0 && outfile != stdout)
28475f6d617Schristos {
28575f6d617Schristos int wstatus;
28675f6d617Schristos int werrno = 0;
28775f6d617Schristos if (ferror (outfile))
28875f6d617Schristos fatal ("write failed");
28975f6d617Schristos #if ! (HAVE_WORKING_FORK || HAVE_WORKING_VFORK)
29075f6d617Schristos wstatus = pclose (outfile);
29175f6d617Schristos if (wstatus == -1)
29275f6d617Schristos werrno = errno;
29375f6d617Schristos #else
29475f6d617Schristos if (fclose (outfile) != 0)
29575f6d617Schristos pfatal_with_name (_("write failed"));
29675f6d617Schristos if (waitpid (pr_pid, &wstatus, 0) < 0)
29775f6d617Schristos pfatal_with_name ("waitpid");
29875f6d617Schristos #endif
29975f6d617Schristos if (! werrno && WIFEXITED (wstatus) && WEXITSTATUS (wstatus) == 127)
30075f6d617Schristos error (EXIT_TROUBLE, 0, _("subsidiary program `%s' not found"),
30175f6d617Schristos pr_program);
30275f6d617Schristos if (wstatus != 0)
30375f6d617Schristos error (EXIT_TROUBLE, werrno, _("subsidiary program `%s' failed"),
30475f6d617Schristos pr_program);
30575f6d617Schristos }
30675f6d617Schristos
30775f6d617Schristos outfile = 0;
30875f6d617Schristos }
30975f6d617Schristos
31075f6d617Schristos /* Compare two lines (typically one from each input file)
31175f6d617Schristos according to the command line options.
31275f6d617Schristos For efficiency, this is invoked only when the lines do not match exactly
31375f6d617Schristos but an option like -i might cause us to ignore the difference.
31475f6d617Schristos Return nonzero if the lines differ. */
31575f6d617Schristos
31675f6d617Schristos bool
lines_differ(char const * s1,char const * s2)31775f6d617Schristos lines_differ (char const *s1, char const *s2)
31875f6d617Schristos {
31975f6d617Schristos register unsigned char const *t1 = (unsigned char const *) s1;
32075f6d617Schristos register unsigned char const *t2 = (unsigned char const *) s2;
32175f6d617Schristos size_t column = 0;
32275f6d617Schristos
32375f6d617Schristos while (1)
32475f6d617Schristos {
32575f6d617Schristos register unsigned char c1 = *t1++;
32675f6d617Schristos register unsigned char c2 = *t2++;
32775f6d617Schristos
32875f6d617Schristos /* Test for exact char equality first, since it's a common case. */
32975f6d617Schristos if (c1 != c2)
33075f6d617Schristos {
33175f6d617Schristos switch (ignore_white_space)
33275f6d617Schristos {
33375f6d617Schristos case IGNORE_ALL_SPACE:
33475f6d617Schristos /* For -w, just skip past any white space. */
33575f6d617Schristos while (ISSPACE (c1) && c1 != '\n') c1 = *t1++;
33675f6d617Schristos while (ISSPACE (c2) && c2 != '\n') c2 = *t2++;
33775f6d617Schristos break;
33875f6d617Schristos
33975f6d617Schristos case IGNORE_SPACE_CHANGE:
34075f6d617Schristos /* For -b, advance past any sequence of white space in
34175f6d617Schristos line 1 and consider it just one space, or nothing at
34275f6d617Schristos all if it is at the end of the line. */
34375f6d617Schristos if (ISSPACE (c1))
34475f6d617Schristos {
34575f6d617Schristos while (c1 != '\n')
34675f6d617Schristos {
34775f6d617Schristos c1 = *t1++;
34875f6d617Schristos if (! ISSPACE (c1))
34975f6d617Schristos {
35075f6d617Schristos --t1;
35175f6d617Schristos c1 = ' ';
35275f6d617Schristos break;
35375f6d617Schristos }
35475f6d617Schristos }
35575f6d617Schristos }
35675f6d617Schristos
35775f6d617Schristos /* Likewise for line 2. */
35875f6d617Schristos if (ISSPACE (c2))
35975f6d617Schristos {
36075f6d617Schristos while (c2 != '\n')
36175f6d617Schristos {
36275f6d617Schristos c2 = *t2++;
36375f6d617Schristos if (! ISSPACE (c2))
36475f6d617Schristos {
36575f6d617Schristos --t2;
36675f6d617Schristos c2 = ' ';
36775f6d617Schristos break;
36875f6d617Schristos }
36975f6d617Schristos }
37075f6d617Schristos }
37175f6d617Schristos
37275f6d617Schristos if (c1 != c2)
37375f6d617Schristos {
37475f6d617Schristos /* If we went too far when doing the simple test
37575f6d617Schristos for equality, go back to the first non-white-space
37675f6d617Schristos character in both sides and try again. */
37775f6d617Schristos if (c2 == ' ' && c1 != '\n'
37875f6d617Schristos && (unsigned char const *) s1 + 1 < t1
37975f6d617Schristos && ISSPACE (t1[-2]))
38075f6d617Schristos {
38175f6d617Schristos --t1;
38275f6d617Schristos continue;
38375f6d617Schristos }
38475f6d617Schristos if (c1 == ' ' && c2 != '\n'
38575f6d617Schristos && (unsigned char const *) s2 + 1 < t2
38675f6d617Schristos && ISSPACE (t2[-2]))
38775f6d617Schristos {
38875f6d617Schristos --t2;
38975f6d617Schristos continue;
39075f6d617Schristos }
39175f6d617Schristos }
39275f6d617Schristos
39375f6d617Schristos break;
39475f6d617Schristos
39575f6d617Schristos case IGNORE_TAB_EXPANSION:
39675f6d617Schristos if ((c1 == ' ' && c2 == '\t')
39775f6d617Schristos || (c1 == '\t' && c2 == ' '))
39875f6d617Schristos {
39975f6d617Schristos size_t column2 = column;
40075f6d617Schristos for (;; c1 = *t1++)
40175f6d617Schristos {
40275f6d617Schristos if (c1 == ' ')
40375f6d617Schristos column++;
40475f6d617Schristos else if (c1 == '\t')
40575f6d617Schristos column += TAB_WIDTH - column % TAB_WIDTH;
40675f6d617Schristos else
40775f6d617Schristos break;
40875f6d617Schristos }
40975f6d617Schristos for (;; c2 = *t2++)
41075f6d617Schristos {
41175f6d617Schristos if (c2 == ' ')
41275f6d617Schristos column2++;
41375f6d617Schristos else if (c2 == '\t')
41475f6d617Schristos column2 += TAB_WIDTH - column2 % TAB_WIDTH;
41575f6d617Schristos else
41675f6d617Schristos break;
41775f6d617Schristos }
41875f6d617Schristos if (column != column2)
41975f6d617Schristos return 1;
42075f6d617Schristos }
42175f6d617Schristos break;
42275f6d617Schristos
42375f6d617Schristos case IGNORE_NO_WHITE_SPACE:
42475f6d617Schristos break;
42575f6d617Schristos }
42675f6d617Schristos
42775f6d617Schristos /* Lowercase all letters if -i is specified. */
42875f6d617Schristos
42975f6d617Schristos if (ignore_case)
43075f6d617Schristos {
43175f6d617Schristos c1 = TOLOWER (c1);
43275f6d617Schristos c2 = TOLOWER (c2);
43375f6d617Schristos }
43475f6d617Schristos
43575f6d617Schristos if (c1 != c2)
43675f6d617Schristos break;
43775f6d617Schristos }
43875f6d617Schristos if (c1 == '\n')
43975f6d617Schristos return 0;
44075f6d617Schristos
44175f6d617Schristos column += c1 == '\t' ? TAB_WIDTH - column % TAB_WIDTH : 1;
44275f6d617Schristos }
44375f6d617Schristos
44475f6d617Schristos return 1;
44575f6d617Schristos }
44675f6d617Schristos
44775f6d617Schristos /* Find the consecutive changes at the start of the script START.
44875f6d617Schristos Return the last link before the first gap. */
44975f6d617Schristos
45075f6d617Schristos struct change *
find_change(struct change * start)45175f6d617Schristos find_change (struct change *start)
45275f6d617Schristos {
45375f6d617Schristos return start;
45475f6d617Schristos }
45575f6d617Schristos
45675f6d617Schristos struct change *
find_reverse_change(struct change * start)45775f6d617Schristos find_reverse_change (struct change *start)
45875f6d617Schristos {
45975f6d617Schristos return start;
46075f6d617Schristos }
46175f6d617Schristos
46275f6d617Schristos /* Divide SCRIPT into pieces by calling HUNKFUN and
46375f6d617Schristos print each piece with PRINTFUN.
46475f6d617Schristos Both functions take one arg, an edit script.
46575f6d617Schristos
46675f6d617Schristos HUNKFUN is called with the tail of the script
46775f6d617Schristos and returns the last link that belongs together with the start
46875f6d617Schristos of the tail.
46975f6d617Schristos
47075f6d617Schristos PRINTFUN takes a subscript which belongs together (with a null
47175f6d617Schristos link at the end) and prints it. */
47275f6d617Schristos
47375f6d617Schristos void
print_script(struct change * script,struct change * (* hunkfun)(struct change *),void (* printfun)(struct change *))47475f6d617Schristos print_script (struct change *script,
47575f6d617Schristos struct change * (*hunkfun) (struct change *),
47675f6d617Schristos void (*printfun) (struct change *))
47775f6d617Schristos {
47875f6d617Schristos struct change *next = script;
47975f6d617Schristos
48075f6d617Schristos while (next)
48175f6d617Schristos {
48275f6d617Schristos struct change *this, *end;
48375f6d617Schristos
48475f6d617Schristos /* Find a set of changes that belong together. */
48575f6d617Schristos this = next;
48675f6d617Schristos end = (*hunkfun) (next);
48775f6d617Schristos
48875f6d617Schristos /* Disconnect them from the rest of the changes,
48975f6d617Schristos making them a hunk, and remember the rest for next iteration. */
49075f6d617Schristos next = end->link;
49175f6d617Schristos end->link = 0;
49275f6d617Schristos #ifdef DEBUG
49375f6d617Schristos debug_script (this);
49475f6d617Schristos #endif
49575f6d617Schristos
49675f6d617Schristos /* Print this hunk. */
49775f6d617Schristos (*printfun) (this);
49875f6d617Schristos
49975f6d617Schristos /* Reconnect the script so it will all be freed properly. */
50075f6d617Schristos end->link = next;
50175f6d617Schristos }
50275f6d617Schristos }
50375f6d617Schristos
50475f6d617Schristos /* Print the text of a single line LINE,
50575f6d617Schristos flagging it with the characters in LINE_FLAG (which say whether
50675f6d617Schristos the line is inserted, deleted, changed, etc.). */
50775f6d617Schristos
50875f6d617Schristos void
print_1_line(char const * line_flag,char const * const * line)50975f6d617Schristos print_1_line (char const *line_flag, char const *const *line)
51075f6d617Schristos {
51175f6d617Schristos char const *base = line[0], *limit = line[1]; /* Help the compiler. */
51275f6d617Schristos FILE *out = outfile; /* Help the compiler some more. */
51375f6d617Schristos char const *flag_format = 0;
51475f6d617Schristos
51575f6d617Schristos /* If -T was specified, use a Tab between the line-flag and the text.
51675f6d617Schristos Otherwise use a Space (as Unix diff does).
51775f6d617Schristos Print neither space nor tab if line-flags are empty. */
51875f6d617Schristos
51975f6d617Schristos if (line_flag && *line_flag)
52075f6d617Schristos {
52175f6d617Schristos flag_format = initial_tab ? "%s\t" : "%s ";
52275f6d617Schristos fprintf (out, flag_format, line_flag);
52375f6d617Schristos }
52475f6d617Schristos
52575f6d617Schristos output_1_line (base, limit, flag_format, line_flag);
52675f6d617Schristos
52775f6d617Schristos if ((!line_flag || line_flag[0]) && limit[-1] != '\n')
52875f6d617Schristos fprintf (out, "\n\\ %s\n", _("No newline at end of file"));
52975f6d617Schristos }
53075f6d617Schristos
53175f6d617Schristos /* Output a line from BASE up to LIMIT.
53275f6d617Schristos With -t, expand white space characters to spaces, and if FLAG_FORMAT
53375f6d617Schristos is nonzero, output it with argument LINE_FLAG after every
53475f6d617Schristos internal carriage return, so that tab stops continue to line up. */
53575f6d617Schristos
53675f6d617Schristos void
output_1_line(char const * base,char const * limit,char const * flag_format,char const * line_flag)53775f6d617Schristos output_1_line (char const *base, char const *limit, char const *flag_format,
53875f6d617Schristos char const *line_flag)
53975f6d617Schristos {
54075f6d617Schristos if (!expand_tabs)
54175f6d617Schristos fwrite (base, limit - base, 1, outfile);
54275f6d617Schristos else
54375f6d617Schristos {
54475f6d617Schristos register FILE *out = outfile;
54575f6d617Schristos register unsigned char c;
54675f6d617Schristos register char const *t = base;
54775f6d617Schristos register unsigned int column = 0;
54875f6d617Schristos
54975f6d617Schristos while (t < limit)
55075f6d617Schristos switch ((c = *t++))
55175f6d617Schristos {
55275f6d617Schristos case '\t':
55375f6d617Schristos {
55475f6d617Schristos unsigned int spaces = TAB_WIDTH - column % TAB_WIDTH;
55575f6d617Schristos column += spaces;
55675f6d617Schristos do
55775f6d617Schristos putc (' ', out);
55875f6d617Schristos while (--spaces);
55975f6d617Schristos }
56075f6d617Schristos break;
56175f6d617Schristos
56275f6d617Schristos case '\r':
56375f6d617Schristos putc (c, out);
56475f6d617Schristos if (flag_format && t < limit && *t != '\n')
56575f6d617Schristos fprintf (out, flag_format, line_flag);
56675f6d617Schristos column = 0;
56775f6d617Schristos break;
56875f6d617Schristos
56975f6d617Schristos case '\b':
57075f6d617Schristos if (column == 0)
57175f6d617Schristos continue;
57275f6d617Schristos column--;
57375f6d617Schristos putc (c, out);
57475f6d617Schristos break;
57575f6d617Schristos
57675f6d617Schristos default:
57775f6d617Schristos if (ISPRINT (c))
57875f6d617Schristos column++;
57975f6d617Schristos putc (c, out);
58075f6d617Schristos break;
58175f6d617Schristos }
58275f6d617Schristos }
58375f6d617Schristos }
58475f6d617Schristos
58575f6d617Schristos char const change_letter[] = { 0, 'd', 'a', 'c' };
58675f6d617Schristos
58775f6d617Schristos /* Translate an internal line number (an index into diff's table of lines)
58875f6d617Schristos into an actual line number in the input file.
58975f6d617Schristos The internal line number is I. FILE points to the data on the file.
59075f6d617Schristos
59175f6d617Schristos Internal line numbers count from 0 starting after the prefix.
59275f6d617Schristos Actual line numbers count from 1 within the entire file. */
59375f6d617Schristos
59475f6d617Schristos lin
translate_line_number(struct file_data const * file,lin i)59575f6d617Schristos translate_line_number (struct file_data const *file, lin i)
59675f6d617Schristos {
59775f6d617Schristos return i + file->prefix_lines + 1;
59875f6d617Schristos }
59975f6d617Schristos
60075f6d617Schristos /* Translate a line number range. This is always done for printing,
60175f6d617Schristos so for convenience translate to long rather than lin, so that the
60275f6d617Schristos caller can use printf with "%ld" without casting. */
60375f6d617Schristos
60475f6d617Schristos void
translate_range(struct file_data const * file,lin a,lin b,long * aptr,long * bptr)60575f6d617Schristos translate_range (struct file_data const *file,
60675f6d617Schristos lin a, lin b,
60775f6d617Schristos long *aptr, long *bptr)
60875f6d617Schristos {
60975f6d617Schristos *aptr = translate_line_number (file, a - 1) + 1;
61075f6d617Schristos *bptr = translate_line_number (file, b + 1) - 1;
61175f6d617Schristos }
61275f6d617Schristos
61375f6d617Schristos /* Print a pair of line numbers with SEPCHAR, translated for file FILE.
61475f6d617Schristos If the two numbers are identical, print just one number.
61575f6d617Schristos
61675f6d617Schristos Args A and B are internal line numbers.
61775f6d617Schristos We print the translated (real) line numbers. */
61875f6d617Schristos
61975f6d617Schristos void
print_number_range(char sepchar,struct file_data * file,lin a,lin b)62075f6d617Schristos print_number_range (char sepchar, struct file_data *file, lin a, lin b)
62175f6d617Schristos {
62275f6d617Schristos long trans_a, trans_b;
62375f6d617Schristos translate_range (file, a, b, &trans_a, &trans_b);
62475f6d617Schristos
62575f6d617Schristos /* Note: we can have B < A in the case of a range of no lines.
62675f6d617Schristos In this case, we should print the line number before the range,
62775f6d617Schristos which is B. */
62875f6d617Schristos if (trans_b > trans_a)
62975f6d617Schristos fprintf (outfile, "%ld%c%ld", trans_a, sepchar, trans_b);
63075f6d617Schristos else
63175f6d617Schristos fprintf (outfile, "%ld", trans_b);
63275f6d617Schristos }
63375f6d617Schristos
63475f6d617Schristos /* Look at a hunk of edit script and report the range of lines in each file
63575f6d617Schristos that it applies to. HUNK is the start of the hunk, which is a chain
63675f6d617Schristos of `struct change'. The first and last line numbers of file 0 are stored in
63775f6d617Schristos *FIRST0 and *LAST0, and likewise for file 1 in *FIRST1 and *LAST1.
63875f6d617Schristos Note that these are internal line numbers that count from 0.
63975f6d617Schristos
64075f6d617Schristos If no lines from file 0 are deleted, then FIRST0 is LAST0+1.
64175f6d617Schristos
64275f6d617Schristos Return UNCHANGED if only ignorable lines are inserted or deleted,
64375f6d617Schristos OLD if lines of file 0 are deleted,
64475f6d617Schristos NEW if lines of file 1 are inserted,
64575f6d617Schristos and CHANGED if both kinds of changes are found. */
64675f6d617Schristos
64775f6d617Schristos enum changes
analyze_hunk(struct change * hunk,lin * first0,lin * last0,lin * first1,lin * last1)64875f6d617Schristos analyze_hunk (struct change *hunk,
64975f6d617Schristos lin *first0, lin *last0,
65075f6d617Schristos lin *first1, lin *last1)
65175f6d617Schristos {
65275f6d617Schristos struct change *next;
65375f6d617Schristos lin l0, l1;
65475f6d617Schristos lin show_from, show_to;
65575f6d617Schristos lin i;
65675f6d617Schristos bool trivial = ignore_blank_lines || ignore_regexp.fastmap;
65775f6d617Schristos size_t trivial_length = (int) ignore_blank_lines - 1;
65875f6d617Schristos /* If 0, ignore zero-length lines;
65975f6d617Schristos if SIZE_MAX, do not ignore lines just because of their length. */
66075f6d617Schristos
66175f6d617Schristos char const * const *linbuf0 = files[0].linbuf; /* Help the compiler. */
66275f6d617Schristos char const * const *linbuf1 = files[1].linbuf;
66375f6d617Schristos
66475f6d617Schristos show_from = show_to = 0;
66575f6d617Schristos
66675f6d617Schristos *first0 = hunk->line0;
66775f6d617Schristos *first1 = hunk->line1;
66875f6d617Schristos
66975f6d617Schristos next = hunk;
67075f6d617Schristos do
67175f6d617Schristos {
67275f6d617Schristos l0 = next->line0 + next->deleted - 1;
67375f6d617Schristos l1 = next->line1 + next->inserted - 1;
67475f6d617Schristos show_from += next->deleted;
67575f6d617Schristos show_to += next->inserted;
67675f6d617Schristos
67775f6d617Schristos for (i = next->line0; i <= l0 && trivial; i++)
67875f6d617Schristos {
67975f6d617Schristos char const *line = linbuf0[i];
68075f6d617Schristos size_t len = linbuf0[i + 1] - line - 1;
68175f6d617Schristos if (len != trivial_length
68275f6d617Schristos && (! ignore_regexp.fastmap
68375f6d617Schristos || re_search (&ignore_regexp, line, len, 0, len, 0) < 0))
68475f6d617Schristos trivial = 0;
68575f6d617Schristos }
68675f6d617Schristos
68775f6d617Schristos for (i = next->line1; i <= l1 && trivial; i++)
68875f6d617Schristos {
68975f6d617Schristos char const *line = linbuf1[i];
69075f6d617Schristos size_t len = linbuf1[i + 1] - line - 1;
69175f6d617Schristos if (len != trivial_length
69275f6d617Schristos && (! ignore_regexp.fastmap
69375f6d617Schristos || re_search (&ignore_regexp, line, len, 0, len, 0) < 0))
69475f6d617Schristos trivial = 0;
69575f6d617Schristos }
69675f6d617Schristos }
69775f6d617Schristos while ((next = next->link) != 0);
69875f6d617Schristos
69975f6d617Schristos *last0 = l0;
70075f6d617Schristos *last1 = l1;
70175f6d617Schristos
70275f6d617Schristos /* If all inserted or deleted lines are ignorable,
70375f6d617Schristos tell the caller to ignore this hunk. */
70475f6d617Schristos
70575f6d617Schristos if (trivial)
70675f6d617Schristos return UNCHANGED;
70775f6d617Schristos
70875f6d617Schristos return (show_from ? OLD : UNCHANGED) | (show_to ? NEW : UNCHANGED);
70975f6d617Schristos }
71075f6d617Schristos
71175f6d617Schristos /* Concatenate three strings, returning a newly malloc'd string. */
71275f6d617Schristos
71375f6d617Schristos char *
concat(char const * s1,char const * s2,char const * s3)71475f6d617Schristos concat (char const *s1, char const *s2, char const *s3)
71575f6d617Schristos {
71675f6d617Schristos char *new = xmalloc (strlen (s1) + strlen (s2) + strlen (s3) + 1);
71775f6d617Schristos sprintf (new, "%s%s%s", s1, s2, s3);
71875f6d617Schristos return new;
71975f6d617Schristos }
72075f6d617Schristos
72175f6d617Schristos /* Yield a new block of SIZE bytes, initialized to zero. */
72275f6d617Schristos
72375f6d617Schristos void *
zalloc(size_t size)72475f6d617Schristos zalloc (size_t size)
72575f6d617Schristos {
72675f6d617Schristos void *p = xmalloc (size);
72775f6d617Schristos memset (p, 0, size);
72875f6d617Schristos return p;
72975f6d617Schristos }
73075f6d617Schristos
73175f6d617Schristos /* Yield the newly malloc'd pathname
73275f6d617Schristos of the file in DIR whose filename is FILE. */
73375f6d617Schristos
73475f6d617Schristos char *
dir_file_pathname(char const * dir,char const * file)73575f6d617Schristos dir_file_pathname (char const *dir, char const *file)
73675f6d617Schristos {
73775f6d617Schristos char const *base = base_name (dir);
73875f6d617Schristos bool omit_slash = !*base || base[strlen (base) - 1] == '/';
73975f6d617Schristos return concat (dir, "/" + omit_slash, file);
74075f6d617Schristos }
74175f6d617Schristos
74275f6d617Schristos void
debug_script(struct change * sp)74375f6d617Schristos debug_script (struct change *sp)
74475f6d617Schristos {
74575f6d617Schristos fflush (stdout);
74675f6d617Schristos
74775f6d617Schristos for (; sp; sp = sp->link)
74875f6d617Schristos {
74975f6d617Schristos long line0 = sp->line0;
75075f6d617Schristos long line1 = sp->line1;
75175f6d617Schristos long deleted = sp->deleted;
75275f6d617Schristos long inserted = sp->inserted;
75375f6d617Schristos fprintf (stderr, "%3ld %3ld delete %ld insert %ld\n",
75475f6d617Schristos line0, line1, deleted, inserted);
75575f6d617Schristos }
75675f6d617Schristos
75775f6d617Schristos fflush (stderr);
75875f6d617Schristos }
759