1 /* Support routines for GNU DIFF.
3 Copyright (C) 1988-1989, 1992-1995, 1998, 2001-2002, 2004, 2006, 2009-2013,
4 2015-2025 Free Software Foundation, Inc.
6 This file is part of GNU DIFF.
8 This program is free software: you can redistribute it and/or modify
9 it under the terms of the GNU General Public License as published by
10 the Free Software Foundation, either version 3 of the License, or
11 (at your option) any later version.
13 This program is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
18 You should have received a copy of the GNU General Public License
19 along with this program. If not, see <http://www.gnu.org/licenses/>. */
27 #include <flexmember.h>
30 #include <system-quote.h>
36 /* Use SA_NOCLDSTOP as a proxy for whether the sigaction machinery is
39 # define SA_NOCLDSTOP 0
40 # define sigprocmask(How, Set, Oset) 0
41 # if ! HAVE_SIGINTERRUPT
42 # define siginterrupt(sig, flag) 0
56 char const pr_program
[] = PR_PROGRAM
;
58 /* Queue up one-line messages to be printed at the end,
59 when -l is specified. Each message is recorded with a 'struct msg'. */
65 /* Msgid of printf-style format. */
68 /* Number of bytes in ARGS. */
71 /* Arg strings, each '\0' terminated, concatenated. */
72 char args
[FLEXIBLE_ARRAY_MEMBER
];
75 /* Head of the chain of queues messages. */
77 static struct msg
*msg_chain
;
79 /* Tail of the chain of queues messages. */
81 static struct msg
**msg_chain_end
= &msg_chain
;
83 /* Use when a system call returns non-zero status.
84 NAME should normally be the file name. */
87 perror_with_name (char const *name
)
89 error (0, errno
, "%s", squote (0, name
));
92 /* Use when a system call returns non-zero status and that is fatal. */
95 pfatal_with_name (char const *name
)
98 print_message_queue ();
99 error (EXIT_TROUBLE
, e
, "%s", name
);
102 /* Print an error message containing MSGID, then exit. */
105 fatal (char const *msgid
)
107 print_message_queue ();
108 error (EXIT_TROUBLE
, 0, "%s", _(msgid
));
111 /* Like printf, except if -l in effect then save the message and print later.
112 Also, all arguments must be char * or char const *.
113 This is used for things like "Only in ...". */
116 message (char const *format_msgid
, ...)
119 va_start (ap
, format_msgid
);
125 for (char const *m
= format_msgid
; *m
; m
++)
131 argbytes
+= strlen (va_arg (ap
, char const *)) + 1;
135 struct msg
*new = xmalloc (FLEXSIZEOF (struct msg
, args
, argbytes
));
136 new->msgid
= format_msgid
;
137 new->argbytes
= argbytes
;
139 va_start (ap
, format_msgid
);
141 for (char const *m
= format_msgid
; *m
; m
++)
147 p
= stpcpy (p
, va_arg (ap
, char const *)) + 1;
150 *msg_chain_end
= new;
152 msg_chain_end
= &new->next
;
156 if (sdiff_merge_assist
)
158 vprintf (_(format_msgid
), ap
);
164 /* Output all the messages that were saved up by calls to 'message'. */
167 print_message_queue (void)
169 for (struct msg
*m
= msg_chain
; m
; )
171 /* Change this if diff ever has messages with more than 4 args. */
172 char const *p
= m
->args
;
173 char const *plim
= p
+ m
->argbytes
;
174 /* Unroll the loop to work around GCC 12 bug with
175 -Wanalyzer-use-of-uninitialized-value. */
176 char const *arg0
= p
; p
+= p
< plim
? strlen (p
) + 1 : 0;
177 char const *arg1
= p
; p
+= p
< plim
? strlen (p
) + 1 : 0;
178 char const *arg2
= p
; p
+= p
< plim
? strlen (p
) + 1 : 0;
179 char const *arg3
= p
; p
+= p
< plim
? strlen (p
) + 1 : 0;
180 printf (_(m
->msgid
), arg0
, arg1
, arg2
, arg3
);
182 struct msg
*next
= m
->next
;
188 /* Signal handling, needed for restoring default colors. */
191 xsigaddset (sigset_t
*set
, int sig
)
193 if (sigaddset (set
, sig
) != 0)
194 pfatal_with_name ("sigaddset");
198 xsigismember (sigset_t
const *set
, int sig
)
200 int mem
= sigismember (set
, sig
);
202 pfatal_with_name ("sigismember");
207 typedef void (*signal_handler
) (int);
208 static signal_handler
209 xsignal (int sig
, signal_handler func
)
211 signal_handler h
= signal (sig
, func
);
213 pfatal_with_name ("signal");
218 xsigprocmask (int how
, sigset_t
const *restrict set
, sigset_t
*restrict oset
)
220 if (sigprocmask (how
, set
, oset
) != 0)
221 pfatal_with_name ("sigprocmask");
224 /* If true, some signals are caught. This is separate from
225 'caught_signals' because POSIX doesn't require an all-zero sigset_t
227 static bool some_signals_caught
;
229 /* The set of signals that are caught. */
230 static sigset_t caught_signals
;
232 /* If nonzero, the value of the pending fatal signal. */
233 static sig_atomic_t volatile interrupt_signal
;
235 /* A count of the number of pending stop signals that have been received. */
236 static sig_atomic_t volatile stop_signal_count
;
238 /* An ordinary signal was received; arrange for the program to exit. */
244 signal (sig
, SIG_IGN
);
245 if (! interrupt_signal
)
246 interrupt_signal
= sig
;
249 /* A SIGTSTP was received; arrange for the program to suspend itself. */
252 stophandler (int sig
)
255 signal (sig
, stophandler
);
256 if (! interrupt_signal
)
259 /* Process any pending signals. If signals are caught, this function
260 should be called periodically. Ideally there should never be an
261 unbounded amount of time when signals are not being processed.
262 Signal handling can restore the default colors, so callers must
263 immediately change colors after invoking this function. */
266 process_signals (void)
268 while (interrupt_signal
| stop_signal_count
)
270 set_color_context (RESET_CONTEXT
);
274 xsigprocmask (SIG_BLOCK
, &caught_signals
, &oldset
);
276 /* Reload stop_signal_count and (if needed) interrupt_signal, in
277 case a new signal was handled before sigprocmask took effect. */
278 int stops
= stop_signal_count
, sig
;
280 /* SIGTSTP is special, since the application can receive that signal
281 more than once. In this case, don't set the signal handler to the
282 default. Instead, just raise the uncatchable SIGSTOP. */
285 stop_signal_count
= stops
- 1;
290 sig
= interrupt_signal
;
291 xsignal (sig
, SIG_DFL
);
294 /* Exit or suspend the program. */
295 if (raise (sig
) != 0)
296 pfatal_with_name ("raise");
297 xsigprocmask (SIG_SETMASK
, &oldset
, nullptr);
299 /* If execution reaches here, then the program has been
300 continued (after being suspended). */
304 /* The signals that can be caught, the number of such signals,
305 and which of them are actually caught. */
306 static int const sig
[] =
309 /* This one is handled specially; see is_tstp_index. */
313 /* The usual suspects. */
344 enum { nsigs
= sizeof (sig
) / sizeof *(sig
) };
346 /* True if sig[j] == SIGTSTP. */
348 is_tstp_index (int j
)
350 return SIGTSTP
&& j
== 0;
354 install_signal_handlers (void)
356 if (sigemptyset (&caught_signals
) != 0)
357 pfatal_with_name ("sigemptyset");
360 for (int j
= 0; j
< nsigs
; j
++)
362 struct sigaction actj
;
363 if (sigaction (sig
[j
], nullptr, &actj
) == 0 && actj
.sa_handler
!= SIG_IGN
)
364 xsigaddset (&caught_signals
, sig
[j
]);
367 struct sigaction act
;
368 act
.sa_mask
= caught_signals
;
369 act
.sa_flags
= SA_RESTART
;
371 for (int j
= 0; j
< nsigs
; j
++)
372 if (xsigismember (&caught_signals
, sig
[j
]))
374 act
.sa_handler
= is_tstp_index (j
) ? stophandler
: sighandler
;
375 if (sigaction (sig
[j
], &act
, nullptr) != 0)
376 pfatal_with_name ("sigaction");
377 some_signals_caught
= true;
380 for (int j
= 0; j
< nsigs
; j
++)
382 signal_handler h
= signal (sig
[j
], SIG_IGN
);
383 if (h
!= SIG_IGN
&& h
!= SIG_ERR
)
385 xsigaddset (&caught_signals
, sig
[j
]);
386 xsignal (sig
[j
], is_tstp_index (j
) ? stophandler
: sighandler
);
387 some_signals_caught
= true;
388 if (siginterrupt (sig
[j
], 0) != 0)
389 pfatal_with_name ("siginterrupt");
395 /* Clean up signal handlers just before exiting the program. Do this
396 by resetting signal actions back to default, and then processing
397 any signals that arrived before resetting. */
399 cleanup_signal_handlers (void)
401 if (some_signals_caught
)
403 for (int j
= 0; j
< nsigs
; j
++)
404 if (xsigismember (&caught_signals
, sig
[j
]))
405 xsignal (sig
[j
], SIG_DFL
);
410 static char const *current_name
[2];
411 static bool currently_recursive
;
412 static bool colors_enabled
;
416 idx_t len
; /* Number of bytes */
417 const char *string
; /* Pointer to the same */
420 /* Parse a string as part of the --palette argument; this may involve
421 decoding all kinds of escape characters. If equals_end is set an
422 unescaped equal sign ends the string, otherwise only a : or \0
423 does. Return true if successful.
425 The resulting string is *not* null-terminated, but may contain
428 *dest and *src may point into the same string, in which case *dest
429 must not exceed *src and the string is modified in place.
431 Note that both dest and src are char **; on return they point to
432 the first free byte after the array and the character that ended
433 the input string, respectively. */
436 get_funky_string (char **dest
, const char **src
, bool equals_end
)
439 ST_GND
, ST_BACKSLASH
, ST_OCTAL
, ST_HEX
, ST_CARET
, ST_END
, ST_ERROR
442 char const *p
= *src
; /* We don't want to double-indirect */
443 char *q
= *dest
; /* the whole darn time. */
445 char num
= 0; /* For numerical codes. */
447 while (state
< ST_END
)
451 case ST_GND
: /* Ground state (no escapes) */
456 state
= ST_END
; /* End of string */
459 state
= ST_BACKSLASH
; /* Backslash scape sequence */
463 state
= ST_CARET
; /* Caret escape */
469 state
= ST_END
; /* End */
479 case ST_BACKSLASH
: /* Backslash escaped character */
490 state
= ST_OCTAL
; /* Octal sequence */
495 state
= ST_HEX
; /* Hex sequence */
501 case 'b': /* Backspace */
504 case 'e': /* Escape */
507 case 'f': /* Form feed */
510 case 'n': /* Newline */
513 case 'r': /* Carriage return */
522 case '?': /* Delete */
525 case '_': /* Space */
528 case '\0': /* End of string */
529 state
= ST_ERROR
; /* Error! */
531 default: /* Escaped character like \ ^ : = */
535 if (state
== ST_BACKSLASH
)
543 case ST_OCTAL
: /* Octal sequence */
544 if (*p
< '0' || *p
> '7')
550 num
= (num
<< 3) + (*(p
++) - '0');
553 case ST_HEX
: /* Hex sequence */
566 num
= (num
<< 4) + (*(p
++) - '0');
574 num
= (num
<< 4) + (*(p
++) - 'a') + 10;
582 num
= (num
<< 4) + (*(p
++) - 'A') + 10;
591 case ST_CARET
: /* Caret escape */
592 state
= ST_GND
; /* Should be the next state... */
593 if (*p
>= '@' && *p
<= '~')
595 *(q
++) = *(p
++) & 037;
613 return state
!= ST_ERROR
;
626 #define LEN_STR_PAIR(s) sizeof (s) - 1, s
628 static struct bin_str color_indicator
[] =
630 { LEN_STR_PAIR ("\033[") }, /* lc: Left of color sequence */
631 { LEN_STR_PAIR ("m") }, /* rc: Right of color sequence */
632 { 0, nullptr }, /* ec: End color (replaces lc+rs+rc) */
633 { LEN_STR_PAIR ("0") }, /* rs: Reset to ordinary colors */
634 { LEN_STR_PAIR ("1") }, /* hd: Header */
635 { LEN_STR_PAIR ("32") }, /* ad: Add line */
636 { LEN_STR_PAIR ("31") }, /* de: Delete line */
637 { LEN_STR_PAIR ("36") }, /* ln: Line number */
640 static const char *const indicator_name
[] =
642 "lc", "rc", "ec", "rs", "hd", "ad", "de", "ln", nullptr
644 ARGMATCH_VERIFY (indicator_name
, color_indicator
);
646 static char *color_palette
;
648 /* Set the color palette to PALETTE, a string that set_color_context
651 set_color_palette (char *palette
)
653 color_palette
= palette
;
657 parse_diff_color (void)
659 /* Process color_palette into itself. This saves a bit of memory,
660 and pacifies Coverity. The output is no larger than the input. */
661 char *buf
= color_palette
;
663 if (p
== nullptr || *p
== '\0')
665 /* Do not process the color palette twice. */
666 color_palette
= nullptr;
668 char label
[] = "??"; /* Indicator label */
670 enum parse_state state
= PS_START
;
675 case PS_START
: /* First label character */
684 state
= get_funky_string (&buf
, &p
, true) ? PS_4
: PS_FAIL
;
688 state
= PS_DONE
; /* Done! */
691 default: /* Assume it is file type label */
698 case PS_2
: /* Second label character */
705 state
= PS_FAIL
; /* Error */
708 case PS_3
: /* Equal sign after indicator label */
709 state
= PS_FAIL
; /* Assume failure... */
710 if (*(p
++) == '=')/* It *should* be... */
712 for (int ind_no
= 0; indicator_name
[ind_no
] != nullptr; ind_no
++)
714 if (STREQ (label
, indicator_name
[ind_no
]))
717 if (get_funky_string (&buf
, &p
, false))
719 color_indicator
[ind_no
].string
= str
;
720 color_indicator
[ind_no
].len
= buf
- str
;
726 if (state
== PS_FAIL
)
727 error (0, 0, _("unrecognized prefix: %s"), label
);
731 case PS_4
: /* Equal sign after *.ext */
732 state
= (*p
++ == '=' && get_funky_string (&buf
, &p
, false)
733 ? PS_START
: PS_FAIL
);
745 if (state
== PS_FAIL
)
748 _("unparsable value for --palette"));
749 colors_enabled
= false;
754 check_color_output (bool is_pipe
)
758 if (! outfile
|| colors_style
== NEVER
)
761 output_is_tty
= presume_output_tty
|| (!is_pipe
&& isatty (fileno (outfile
)));
763 colors_enabled
= (colors_style
== ALWAYS
764 || (colors_style
== AUTO
&& output_is_tty
));
770 install_signal_handlers ();
773 /* Call before outputting the results of comparing files NAME0 and NAME1
774 to set up OUTFILE, the stdio stream for the output to go to.
776 Usually, OUTFILE is just stdout. But when -l was specified
777 we fork off a 'pr' and make OUTFILE a pipe to it.
778 'pr' then outputs to our stdout. */
781 setup_output (char const *name0
, char const *name1
, bool recursive
)
783 current_name
[0] = name0
;
784 current_name
[1] = name1
;
785 currently_recursive
= recursive
;
789 #if HAVE_WORKING_FORK
800 char const *names
[2];
801 for (int f
= 0; f
< 2; f
++)
802 names
[f
] = quotearg_n_style (f
,
803 (strchr (current_name
[f
], ' ')
804 ? c_quoting_style
: c_maybe_quoting_style
),
807 /* Construct the header of this piece of diff. */
808 /* POSIX 1003.1-2017 specifies this format. But there are some bugs in
809 the standard: it says that we must print only the last component
810 of the pathnames, and it requires two spaces after "diff" if
811 there are no options. These requirements are silly and do not
812 match historical practice. */
813 char *name
= xmalloc (sizeof "diff" + strlen (switch_string
)
814 + 1 + strlen (names
[0]) + 1 + strlen (names
[1]));
815 char *p
= stpcpy (name
, "diff");
816 p
= stpcpy (p
, switch_string
);
818 p
= stpcpy (p
, names
[0]);
820 strcpy (p
, names
[1]);
824 if (fflush (stdout
) != 0)
825 pfatal_with_name (_("write failed"));
827 char const *argv
[4] = {pr_program
, "-h", name
, nullptr };
829 /* Make OUTFILE a pipe to a subsidiary 'pr'. */
830 #if HAVE_WORKING_FORK
832 if (pipe (pipes
) != 0)
833 pfatal_with_name ("pipe");
837 pfatal_with_name ("fork");
842 if (pipes
[0] != STDIN_FILENO
)
844 if (dup2 (pipes
[0], STDIN_FILENO
) < 0)
845 pfatal_with_name ("dup2");
849 execv (pr_program
, (char **) argv
);
850 _exit (errno
== ENOENT
? 127 : 126);
855 outfile
= fdopen (pipes
[1], "w");
857 pfatal_with_name ("fdopen");
858 check_color_output (true);
861 char *command
= system_quote_argv (SCI_SYSTEM
, (char **) argv
);
863 outfile
= popen (command
, "w");
865 pfatal_with_name (command
);
866 check_color_output (true);
873 /* If -l was not specified, output the diff straight to 'stdout'. */
876 check_color_output (false);
878 /* If handling multiple files (because scanning a directory),
879 print which files the following output is about. */
880 if (currently_recursive
)
886 /* A special header is needed at the beginning of context output. */
887 if (output_style
== OUTPUT_CONTEXT
|| output_style
== OUTPUT_UNIFIED
)
888 print_context_header (curr
.file
, names
,
889 output_style
== OUTPUT_UNIFIED
);
892 /* Call after the end of output of diffs for one file.
893 Close OUTFILE and get rid of the 'pr' subfork. */
898 if (outfile
&& outfile
!= stdout
)
902 if (ferror (outfile
))
903 fatal ("write failed");
904 #if ! HAVE_WORKING_FORK
905 wstatus
= pclose (outfile
);
909 if (fclose (outfile
) != 0)
910 pfatal_with_name (_("write failed"));
911 if (waitpid (pr_pid
, &wstatus
, 0) < 0)
912 pfatal_with_name ("waitpid");
914 int status
= (! werrno
&& WIFEXITED (wstatus
)
915 ? WEXITSTATUS (wstatus
)
918 error (EXIT_TROUBLE
, werrno
,
920 ? "subsidiary program %s could not be invoked"
922 ? "subsidiary program %s not found"
924 ? "subsidiary program %s failed"
925 : "subsidiary program %s failed (exit status %d)"),
926 quote (pr_program
), status
);
932 /* Find the consecutive changes at the start of the script START.
933 Return the last link before the first gap. */
936 find_change (struct change
*script
)
941 /* Divide SCRIPT into pieces by calling HUNKFUN and
942 print each piece with PRINTFUN.
943 Both functions take one arg, an edit script.
945 HUNKFUN is called with the tail of the script
946 and returns the last link that belongs together with the start
949 PRINTFUN takes a subscript which belongs together (with a null
950 link at the end) and prints it. */
953 print_script (struct change
*script
,
954 struct change
* (*hunkfun
) (struct change
*),
955 void (*printfun
) (struct change
*))
957 struct change
*next
= script
;
961 /* Find a set of changes that belong together. */
962 struct change
*this = next
;
963 struct change
*end
= (*hunkfun
) (next
);
965 /* Disconnect them from the rest of the changes,
966 making them a hunk, and remember the rest for next iteration. */
973 /* Print this hunk. */
976 /* Reconnect the script so it will all be freed properly. */
981 /* Print the text of a single line LINE,
982 flagging it with the characters in LINE_FLAG (which say whether
983 the line is inserted, deleted, changed, etc.). LINE_FLAG must not
984 end in a blank, unless it is a single blank. */
987 print_1_line (char const *line_flag
, char const *const *line
)
989 print_1_line_nl (line_flag
, line
, false);
992 /* Print the text of a single line LINE,
993 flagging it with the characters in LINE_FLAG (which say whether
994 the line is inserted, deleted, changed, etc.). LINE_FLAG must not
995 end in a blank, unless it is a single blank. If SKIP_NL is set, then
996 the final '\n' is not printed. */
999 print_1_line_nl (char const *line_flag
, char const *const *line
, bool skip_nl
)
1001 char const *base
= line
[0], *limit
= line
[1]; /* Help the compiler. */
1002 FILE *out
= outfile
; /* Help the compiler some more. */
1003 char const *flag_format
= nullptr;
1005 /* If -T was specified, use a Tab between the line-flag and the text.
1006 Otherwise use a Space (as Unix diff does).
1007 Print neither space nor tab if line-flags are empty.
1008 But omit trailing blanks if requested. */
1010 if (line_flag
&& *line_flag
)
1012 char const *flag_format_1
= flag_format
= initial_tab
? "%s\t" : "%s ";
1013 char const *line_flag_1
= line_flag
;
1015 if (suppress_blank_empty
&& **line
== '\n')
1017 flag_format_1
= "%s";
1019 /* This hack to omit trailing blanks takes advantage of the
1020 fact that the only way that LINE_FLAG can end in a blank
1021 is when LINE_FLAG consists of a single blank. */
1022 line_flag_1
+= *line_flag_1
== ' ';
1025 fprintf (out
, flag_format_1
, line_flag_1
);
1028 output_1_line (base
, limit
- (skip_nl
&& limit
[-1] == '\n'), flag_format
, line_flag
);
1030 if ((!line_flag
|| line_flag
[0]) && limit
[-1] != '\n')
1032 set_color_context (RESET_CONTEXT
);
1033 fprintf (out
, "\n\\ %s\n", _("No newline at end of file"));
1037 /* Output a line from BASE up to LIMIT.
1038 With -t, expand white space characters to spaces, and if FLAG_FORMAT
1039 is nonzero, output it with argument LINE_FLAG after every
1040 internal carriage return, so that tab stops continue to line up. */
1043 output_1_line (char const *base
, char const *limit
, char const *flag_format
,
1044 char const *line_flag
)
1046 enum { MAX_CHUNK
= 1024 };
1049 idx_t left
= limit
- base
;
1052 idx_t to_write
= MIN (left
, MAX_CHUNK
);
1053 idx_t written
= fwrite (base
, sizeof (char), to_write
, outfile
);
1055 if (written
< to_write
)
1063 FILE *out
= outfile
;
1064 char const *t
= base
;
1065 intmax_t tab
= 0, column
= 0, tab_size
= tabsize
;
1066 int counter_proc_signals
= 0;
1070 counter_proc_signals
++;
1071 if (counter_proc_signals
== MAX_CHUNK
)
1074 counter_proc_signals
= 0;
1082 if (putc (' ', out
) < 0)
1084 while (++column
< tab_size
);
1092 if (putc ('\r', out
) < 0)
1094 if (flag_format
&& t
< limit
&& *t
!= '\n')
1095 if (fprintf (out
, flag_format
, line_flag
) < 0)
1107 column
= tab_size
- 1;
1111 if (putc ('\b', out
) < 0)
1116 mcel_t g
= mcel_scan (t
, limit
);
1117 column
+= g
.err
? 1 : c32isprint (g
.ch
) ? c32width (g
.ch
) : 0;
1118 tab
+= column
/ tab_size
;
1120 if (fwrite (t
, sizeof *t
, g
.len
, outfile
) != g
.len
)
1131 C_LEFT
, C_RIGHT
, C_END
, C_RESET
, C_HEADER
, C_ADD
, C_DELETE
, C_LINE
1135 put_indicator (const struct bin_str
*ind
)
1137 fwrite (ind
->string
, ind
->len
, 1, outfile
);
1140 static enum color_context last_context
= RESET_CONTEXT
;
1143 set_color_context (enum color_context color_context
)
1145 if (color_context
!= RESET_CONTEXT
)
1147 if (colors_enabled
&& last_context
!= color_context
)
1149 put_indicator (&color_indicator
[C_LEFT
]);
1150 switch (color_context
)
1152 case HEADER_CONTEXT
:
1153 put_indicator (&color_indicator
[C_HEADER
]);
1156 case LINE_NUMBER_CONTEXT
:
1157 put_indicator (&color_indicator
[C_LINE
]);
1161 put_indicator (&color_indicator
[C_ADD
]);
1164 case DELETE_CONTEXT
:
1165 put_indicator (&color_indicator
[C_DELETE
]);
1169 put_indicator (&color_indicator
[C_RESET
]);
1175 put_indicator (&color_indicator
[C_RIGHT
]);
1176 last_context
= color_context
;
1181 char const change_letter
[] = { '\0', 'd', 'a', 'c' };
1183 /* Translate an internal line number (an index into diff's table of lines)
1184 into an actual line number in the input file.
1185 The internal line number is I. FILE points to the data on the file.
1187 Internal line numbers count from 0 starting after the prefix.
1188 Actual line numbers count from 1 within the entire file. */
1191 translate_line_number (struct file_data
const *file
, lin i
)
1193 return i
+ file
->prefix_lines
+ 1;
1196 /* Translate a line number range. */
1199 translate_range (struct file_data
const *file
,
1201 lin
*aptr
, lin
*bptr
)
1203 *aptr
= translate_line_number (file
, a
- 1) + 1;
1204 *bptr
= translate_line_number (file
, b
+ 1) - 1;
1207 /* Print a pair of line numbers with SEPCHAR, translated for file FILE.
1208 If the two numbers are identical, print just one number.
1210 Args A and B are internal line numbers.
1211 We print the translated (real) line numbers. */
1214 print_number_range (char sepchar
, struct file_data
*file
, lin a
, lin b
)
1216 lin trans_a
, trans_b
;
1217 translate_range (file
, a
, b
, &trans_a
, &trans_b
);
1219 /* Note: we can have B < A in the case of a range of no lines.
1220 In this case, we should print the line number before the range,
1222 if (trans_b
> trans_a
)
1223 fprintf (outfile
, "%"pI
"d%c%"pI
"d", trans_a
, sepchar
, trans_b
);
1225 fprintf (outfile
, "%"pI
"d", trans_b
);
1228 /* Look at a hunk of edit script and report the range of lines in each file
1229 that it applies to. HUNK is the start of the hunk, which is a chain
1230 of 'struct change'. The first and last line numbers of file 0 are stored in
1231 *FIRST0 and *LAST0, and likewise for file 1 in *FIRST1 and *LAST1.
1232 Note that these are internal line numbers that count from 0.
1234 If no lines from file 0 are deleted, then FIRST0 is LAST0+1.
1236 Return UNCHANGED if only ignorable lines are inserted or deleted,
1237 OLD if lines of file 0 are deleted,
1238 NEW if lines of file 1 are inserted,
1239 and CHANGED if both kinds of changes are found. */
1242 analyze_hunk (struct change
*hunk
,
1243 lin
*first0
, lin
*last0
,
1244 lin
*first1
, lin
*last1
)
1246 bool trivial
= ignore_blank_lines
|| ignore_regexp
.fastmap
;
1247 int trivial_length
= ignore_blank_lines
- 1;
1248 /* If 0, ignore zero-length lines;
1249 if -1, do not ignore lines just because of their length. */
1251 bool skip_white_space
=
1252 ignore_blank_lines
&& IGNORE_TRAILING_SPACE
<= ignore_white_space
;
1253 bool skip_leading_white_space
=
1254 skip_white_space
&& IGNORE_SPACE_CHANGE
<= ignore_white_space
;
1256 /* Help the compiler. */
1257 char const *const *linbuf0
= curr
.file
[0].linbuf
;
1258 char const *const *linbuf1
= curr
.file
[1].linbuf
;
1260 lin show_from
= 0, show_to
= 0;
1262 *first0
= hunk
->line0
;
1263 *first1
= hunk
->line1
;
1265 struct change
*next
= hunk
;
1269 l0
= next
->line0
+ next
->deleted
- 1;
1270 l1
= next
->line1
+ next
->inserted
- 1;
1271 show_from
+= next
->deleted
;
1272 show_to
+= next
->inserted
;
1274 for (lin i
= next
->line0
; i
<= l0
&& trivial
; i
++)
1276 char const *line
= linbuf0
[i
];
1277 char const *lastbyte
= linbuf0
[i
+ 1] - 1;
1278 char const *newline
= lastbyte
+ (*lastbyte
!= '\n');
1279 idx_t len
= newline
- line
;
1280 char const *p
= line
;
1281 if (skip_white_space
)
1284 mcel_t g
= mcel_scan (p
, newline
);
1285 if (! c32isspace (g
.ch
))
1287 if (! skip_leading_white_space
)
1293 if (newline
- p
!= trivial_length
1294 && (! ignore_regexp
.fastmap
1295 || (re_search (&ignore_regexp
, line
, len
, 0, len
, nullptr)
1300 for (lin i
= next
->line1
; i
<= l1
&& trivial
; i
++)
1302 char const *line
= linbuf1
[i
];
1303 char const *lastbyte
= linbuf1
[i
+ 1] - 1;
1304 char const *newline
= lastbyte
+ (*lastbyte
!= '\n');
1305 idx_t len
= newline
- line
;
1306 char const *p
= line
;
1307 if (skip_white_space
)
1310 mcel_t g
= mcel_scan (p
, newline
);
1311 if (! c32isspace (g
.ch
))
1313 if (! skip_leading_white_space
)
1319 if (newline
- p
!= trivial_length
1320 && (! ignore_regexp
.fastmap
1321 || (re_search (&ignore_regexp
, line
, len
, 0, len
, nullptr)
1326 while ((next
= next
->link
));
1331 /* If all inserted or deleted lines are ignorable,
1332 tell the caller to ignore this hunk. */
1337 return (show_from
? OLD
: UNCHANGED
) | (show_to
? NEW
: UNCHANGED
);
1342 debug_script (struct change
*sp
)
1346 for (; sp
; sp
= sp
->link
)
1347 fprintf (stderr
, "%3"pI
"d %3"pI
"d delete %"pI
"d insert %"pI
"d\n",
1348 sp
->line0
, sp
->line1
, sp
->deleted
, sp
->inserted
);