xref: /netbsd-src/external/gpl2/diffutils/dist/src/context.c (revision f8c23a2b94243924f9b7311eb0ad24bf23d5c657)
1*f8c23a2bSchristos /*	$NetBSD: context.c,v 1.2 2016/01/13 03:39:28 christos Exp $	*/
275f6d617Schristos 
375f6d617Schristos /* Context-format output routines for GNU DIFF.
475f6d617Schristos 
575f6d617Schristos    Copyright (C) 1988, 1989, 1991, 1992, 1993, 1994, 1995, 1998, 2001,
675f6d617Schristos    2002 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 <inttostr.h>
2775f6d617Schristos #include <regex.h>
2875f6d617Schristos 
2975f6d617Schristos #ifdef ST_MTIM_NSEC
3075f6d617Schristos # define TIMESPEC_NS(timespec) ((timespec).ST_MTIM_NSEC)
3175f6d617Schristos #else
3275f6d617Schristos # define TIMESPEC_NS(timespec) 0
3375f6d617Schristos #endif
3475f6d617Schristos 
3575f6d617Schristos size_t nstrftime (char *, size_t, char const *, struct tm const *, int, int);
3675f6d617Schristos 
3775f6d617Schristos static char const *find_function (char const * const *, lin);
3875f6d617Schristos static struct change *find_hunk (struct change *);
3975f6d617Schristos static void mark_ignorable (struct change *);
4075f6d617Schristos static void pr_context_hunk (struct change *);
4175f6d617Schristos static void pr_unidiff_hunk (struct change *);
4275f6d617Schristos 
4375f6d617Schristos /* Last place find_function started searching from.  */
4475f6d617Schristos static lin find_function_last_search;
4575f6d617Schristos 
4675f6d617Schristos /* The value find_function returned when it started searching there.  */
4775f6d617Schristos static lin find_function_last_match;
4875f6d617Schristos 
4975f6d617Schristos /* Print a label for a context diff, with a file name and date or a label.  */
5075f6d617Schristos 
5175f6d617Schristos static void
print_context_label(char const * mark,struct file_data * inf,char const * label)5275f6d617Schristos print_context_label (char const *mark,
5375f6d617Schristos 		     struct file_data *inf,
5475f6d617Schristos 		     char const *label)
5575f6d617Schristos {
5675f6d617Schristos   if (label)
5775f6d617Schristos     fprintf (outfile, "%s %s\n", mark, label);
5875f6d617Schristos   else
5975f6d617Schristos     {
6075f6d617Schristos       char buf[MAX (INT_STRLEN_BOUND (int) + 32,
6175f6d617Schristos 		    INT_STRLEN_BOUND (time_t) + 11)];
6275f6d617Schristos       struct tm const *tm = localtime (&inf->stat.st_mtime);
6375f6d617Schristos       int nsec = TIMESPEC_NS (inf->stat.st_mtim);
6475f6d617Schristos       if (! (tm && nstrftime (buf, sizeof buf, time_format, tm, 0, nsec)))
6575f6d617Schristos 	{
66*f8c23a2bSchristos 	  long long sec = inf->stat.st_mtime;
6775f6d617Schristos 	  verify (info_preserved, sizeof inf->stat.st_mtime <= sizeof sec);
68*f8c23a2bSchristos 	  sprintf (buf, "%lld.%.9d", sec, nsec);
6975f6d617Schristos 	}
7075f6d617Schristos       fprintf (outfile, "%s %s\t%s\n", mark, inf->name, buf);
7175f6d617Schristos     }
7275f6d617Schristos }
7375f6d617Schristos 
7475f6d617Schristos /* Print a header for a context diff, with the file names and dates.  */
7575f6d617Schristos 
7675f6d617Schristos void
print_context_header(struct file_data inf[],bool unidiff)7775f6d617Schristos print_context_header (struct file_data inf[], bool unidiff)
7875f6d617Schristos {
7975f6d617Schristos   if (unidiff)
8075f6d617Schristos     {
8175f6d617Schristos       print_context_label ("---", &inf[0], file_label[0]);
8275f6d617Schristos       print_context_label ("+++", &inf[1], file_label[1]);
8375f6d617Schristos     }
8475f6d617Schristos   else
8575f6d617Schristos     {
8675f6d617Schristos       print_context_label ("***", &inf[0], file_label[0]);
8775f6d617Schristos       print_context_label ("---", &inf[1], file_label[1]);
8875f6d617Schristos     }
8975f6d617Schristos }
9075f6d617Schristos 
9175f6d617Schristos /* Print an edit script in context format.  */
9275f6d617Schristos 
9375f6d617Schristos void
print_context_script(struct change * script,bool unidiff)9475f6d617Schristos print_context_script (struct change *script, bool unidiff)
9575f6d617Schristos {
9675f6d617Schristos   if (ignore_blank_lines || ignore_regexp.fastmap)
9775f6d617Schristos     mark_ignorable (script);
9875f6d617Schristos   else
9975f6d617Schristos     {
10075f6d617Schristos       struct change *e;
10175f6d617Schristos       for (e = script; e; e = e->link)
10275f6d617Schristos 	e->ignore = 0;
10375f6d617Schristos     }
10475f6d617Schristos 
10575f6d617Schristos   find_function_last_search = - files[0].prefix_lines;
10675f6d617Schristos   find_function_last_match = LIN_MAX;
10775f6d617Schristos 
10875f6d617Schristos   if (unidiff)
10975f6d617Schristos     print_script (script, find_hunk, pr_unidiff_hunk);
11075f6d617Schristos   else
11175f6d617Schristos     print_script (script, find_hunk, pr_context_hunk);
11275f6d617Schristos }
11375f6d617Schristos 
11475f6d617Schristos /* Print a pair of line numbers with a comma, translated for file FILE.
11575f6d617Schristos    If the second number is not greater, use the first in place of it.
11675f6d617Schristos 
11775f6d617Schristos    Args A and B are internal line numbers.
11875f6d617Schristos    We print the translated (real) line numbers.  */
11975f6d617Schristos 
12075f6d617Schristos static void
print_context_number_range(struct file_data const * file,lin a,lin b)12175f6d617Schristos print_context_number_range (struct file_data const *file, lin a, lin b)
12275f6d617Schristos {
12375f6d617Schristos   long trans_a, trans_b;
12475f6d617Schristos   translate_range (file, a, b, &trans_a, &trans_b);
12575f6d617Schristos 
12675f6d617Schristos   /* We can have B <= A in the case of a range of no lines.
12775f6d617Schristos      In this case, we should print the line number before the range,
12875f6d617Schristos      which is B.
12975f6d617Schristos 
13075f6d617Schristos      POSIX 1003.1-2001 requires two line numbers separated by a comma
13175f6d617Schristos      even if the line numbers are the same.  However, this does not
13275f6d617Schristos      match existing practice and is surely an error in the
13375f6d617Schristos      specification.  */
13475f6d617Schristos 
13575f6d617Schristos   if (trans_b <= trans_a)
13675f6d617Schristos     fprintf (outfile, "%ld", trans_b);
13775f6d617Schristos   else
13875f6d617Schristos     fprintf (outfile, "%ld,%ld", trans_a, trans_b);
13975f6d617Schristos }
14075f6d617Schristos 
14175f6d617Schristos /* Print FUNCTION in a context header.  */
14275f6d617Schristos static void
print_context_function(FILE * out,char const * function)14375f6d617Schristos print_context_function (FILE *out, char const *function)
14475f6d617Schristos {
14575f6d617Schristos   int i;
14675f6d617Schristos   putc (' ', out);
14775f6d617Schristos   for (i = 0; i < 40 && function[i] != '\n'; i++)
14875f6d617Schristos     continue;
14975f6d617Schristos   fwrite (function, 1, i, out);
15075f6d617Schristos }
15175f6d617Schristos 
15275f6d617Schristos /* Print a portion of an edit script in context format.
15375f6d617Schristos    HUNK is the beginning of the portion to be printed.
15475f6d617Schristos    The end is marked by a `link' that has been nulled out.
15575f6d617Schristos 
15675f6d617Schristos    Prints out lines from both files, and precedes each
15775f6d617Schristos    line with the appropriate flag-character.  */
15875f6d617Schristos 
15975f6d617Schristos static void
pr_context_hunk(struct change * hunk)16075f6d617Schristos pr_context_hunk (struct change *hunk)
16175f6d617Schristos {
16275f6d617Schristos   lin first0, last0, first1, last1, i;
16375f6d617Schristos   char const *prefix;
16475f6d617Schristos   char const *function;
16575f6d617Schristos   FILE *out;
16675f6d617Schristos 
16775f6d617Schristos   /* Determine range of line numbers involved in each file.  */
16875f6d617Schristos 
16975f6d617Schristos   enum changes changes = analyze_hunk (hunk, &first0, &last0, &first1, &last1);
17075f6d617Schristos   if (! changes)
17175f6d617Schristos     return;
17275f6d617Schristos 
17375f6d617Schristos   /* Include a context's width before and after.  */
17475f6d617Schristos 
17575f6d617Schristos   i = - files[0].prefix_lines;
17675f6d617Schristos   first0 = MAX (first0 - context, i);
17775f6d617Schristos   first1 = MAX (first1 - context, i);
17875f6d617Schristos   if (last0 < files[0].valid_lines - context)
17975f6d617Schristos     last0 += context;
18075f6d617Schristos   else
18175f6d617Schristos     last0 = files[0].valid_lines - 1;
18275f6d617Schristos   if (last1 < files[1].valid_lines - context)
18375f6d617Schristos     last1 += context;
18475f6d617Schristos   else
18575f6d617Schristos     last1 = files[1].valid_lines - 1;
18675f6d617Schristos 
18775f6d617Schristos   /* If desired, find the preceding function definition line in file 0.  */
18875f6d617Schristos   function = 0;
18975f6d617Schristos   if (function_regexp.fastmap)
19075f6d617Schristos     function = find_function (files[0].linbuf, first0);
19175f6d617Schristos 
19275f6d617Schristos   begin_output ();
19375f6d617Schristos   out = outfile;
19475f6d617Schristos 
19575f6d617Schristos   fprintf (out, "***************");
19675f6d617Schristos 
19775f6d617Schristos   if (function)
19875f6d617Schristos     print_context_function (out, function);
19975f6d617Schristos 
20075f6d617Schristos   fprintf (out, "\n*** ");
20175f6d617Schristos   print_context_number_range (&files[0], first0, last0);
20275f6d617Schristos   fprintf (out, " ****\n");
20375f6d617Schristos 
20475f6d617Schristos   if (changes & OLD)
20575f6d617Schristos     {
20675f6d617Schristos       struct change *next = hunk;
20775f6d617Schristos 
20875f6d617Schristos       for (i = first0; i <= last0; i++)
20975f6d617Schristos 	{
21075f6d617Schristos 	  /* Skip past changes that apply (in file 0)
21175f6d617Schristos 	     only to lines before line I.  */
21275f6d617Schristos 
21375f6d617Schristos 	  while (next && next->line0 + next->deleted <= i)
21475f6d617Schristos 	    next = next->link;
21575f6d617Schristos 
21675f6d617Schristos 	  /* Compute the marking for line I.  */
21775f6d617Schristos 
21875f6d617Schristos 	  prefix = " ";
21975f6d617Schristos 	  if (next && next->line0 <= i)
22075f6d617Schristos 	    /* The change NEXT covers this line.
22175f6d617Schristos 	       If lines were inserted here in file 1, this is "changed".
22275f6d617Schristos 	       Otherwise it is "deleted".  */
22375f6d617Schristos 	    prefix = (next->inserted > 0 ? "!" : "-");
22475f6d617Schristos 
22575f6d617Schristos 	  print_1_line (prefix, &files[0].linbuf[i]);
22675f6d617Schristos 	}
22775f6d617Schristos     }
22875f6d617Schristos 
22975f6d617Schristos   fprintf (out, "--- ");
23075f6d617Schristos   print_context_number_range (&files[1], first1, last1);
23175f6d617Schristos   fprintf (out, " ----\n");
23275f6d617Schristos 
23375f6d617Schristos   if (changes & NEW)
23475f6d617Schristos     {
23575f6d617Schristos       struct change *next = hunk;
23675f6d617Schristos 
23775f6d617Schristos       for (i = first1; i <= last1; i++)
23875f6d617Schristos 	{
23975f6d617Schristos 	  /* Skip past changes that apply (in file 1)
24075f6d617Schristos 	     only to lines before line I.  */
24175f6d617Schristos 
24275f6d617Schristos 	  while (next && next->line1 + next->inserted <= i)
24375f6d617Schristos 	    next = next->link;
24475f6d617Schristos 
24575f6d617Schristos 	  /* Compute the marking for line I.  */
24675f6d617Schristos 
24775f6d617Schristos 	  prefix = " ";
24875f6d617Schristos 	  if (next && next->line1 <= i)
24975f6d617Schristos 	    /* The change NEXT covers this line.
25075f6d617Schristos 	       If lines were deleted here in file 0, this is "changed".
25175f6d617Schristos 	       Otherwise it is "inserted".  */
25275f6d617Schristos 	    prefix = (next->deleted > 0 ? "!" : "+");
25375f6d617Schristos 
25475f6d617Schristos 	  print_1_line (prefix, &files[1].linbuf[i]);
25575f6d617Schristos 	}
25675f6d617Schristos     }
25775f6d617Schristos }
25875f6d617Schristos 
25975f6d617Schristos /* Print a pair of line numbers with a comma, translated for file FILE.
26075f6d617Schristos    If the second number is smaller, use the first in place of it.
26175f6d617Schristos    If the numbers are equal, print just one number.
26275f6d617Schristos 
26375f6d617Schristos    Args A and B are internal line numbers.
26475f6d617Schristos    We print the translated (real) line numbers.  */
26575f6d617Schristos 
26675f6d617Schristos static void
print_unidiff_number_range(struct file_data const * file,lin a,lin b)26775f6d617Schristos print_unidiff_number_range (struct file_data const *file, lin a, lin b)
26875f6d617Schristos {
26975f6d617Schristos   long trans_a, trans_b;
27075f6d617Schristos   translate_range (file, a, b, &trans_a, &trans_b);
27175f6d617Schristos 
27275f6d617Schristos   /* We can have B < A in the case of a range of no lines.
27375f6d617Schristos      In this case, we should print the line number before the range,
27475f6d617Schristos      which is B.  */
27575f6d617Schristos   if (trans_b <= trans_a)
27675f6d617Schristos     fprintf (outfile, trans_b < trans_a ? "%ld,0" : "%ld", trans_b);
27775f6d617Schristos   else
27875f6d617Schristos     fprintf (outfile, "%ld,%ld", trans_a, trans_b - trans_a + 1);
27975f6d617Schristos }
28075f6d617Schristos 
28175f6d617Schristos /* Print a portion of an edit script in unidiff format.
28275f6d617Schristos    HUNK is the beginning of the portion to be printed.
28375f6d617Schristos    The end is marked by a `link' that has been nulled out.
28475f6d617Schristos 
28575f6d617Schristos    Prints out lines from both files, and precedes each
28675f6d617Schristos    line with the appropriate flag-character.  */
28775f6d617Schristos 
28875f6d617Schristos static void
pr_unidiff_hunk(struct change * hunk)28975f6d617Schristos pr_unidiff_hunk (struct change *hunk)
29075f6d617Schristos {
29175f6d617Schristos   lin first0, last0, first1, last1;
29275f6d617Schristos   lin i, j, k;
29375f6d617Schristos   struct change *next;
29475f6d617Schristos   char const *function;
29575f6d617Schristos   FILE *out;
29675f6d617Schristos 
29775f6d617Schristos   /* Determine range of line numbers involved in each file.  */
29875f6d617Schristos 
29975f6d617Schristos   if (! analyze_hunk (hunk, &first0, &last0, &first1, &last1))
30075f6d617Schristos     return;
30175f6d617Schristos 
30275f6d617Schristos   /* Include a context's width before and after.  */
30375f6d617Schristos 
30475f6d617Schristos   i = - files[0].prefix_lines;
30575f6d617Schristos   first0 = MAX (first0 - context, i);
30675f6d617Schristos   first1 = MAX (first1 - context, i);
30775f6d617Schristos   if (last0 < files[0].valid_lines - context)
30875f6d617Schristos     last0 += context;
30975f6d617Schristos   else
31075f6d617Schristos     last0 = files[0].valid_lines - 1;
31175f6d617Schristos   if (last1 < files[1].valid_lines - context)
31275f6d617Schristos     last1 += context;
31375f6d617Schristos   else
31475f6d617Schristos     last1 = files[1].valid_lines - 1;
31575f6d617Schristos 
31675f6d617Schristos   /* If desired, find the preceding function definition line in file 0.  */
31775f6d617Schristos   function = 0;
31875f6d617Schristos   if (function_regexp.fastmap)
31975f6d617Schristos     function = find_function (files[0].linbuf, first0);
32075f6d617Schristos 
32175f6d617Schristos   begin_output ();
32275f6d617Schristos   out = outfile;
32375f6d617Schristos 
32475f6d617Schristos   fprintf (out, "@@ -");
32575f6d617Schristos   print_unidiff_number_range (&files[0], first0, last0);
32675f6d617Schristos   fprintf (out, " +");
32775f6d617Schristos   print_unidiff_number_range (&files[1], first1, last1);
32875f6d617Schristos   fprintf (out, " @@");
32975f6d617Schristos 
33075f6d617Schristos   if (function)
33175f6d617Schristos     print_context_function (out, function);
33275f6d617Schristos 
33375f6d617Schristos   putc ('\n', out);
33475f6d617Schristos 
33575f6d617Schristos   next = hunk;
33675f6d617Schristos   i = first0;
33775f6d617Schristos   j = first1;
33875f6d617Schristos 
33975f6d617Schristos   while (i <= last0 || j <= last1)
34075f6d617Schristos     {
34175f6d617Schristos 
34275f6d617Schristos       /* If the line isn't a difference, output the context from file 0. */
34375f6d617Schristos 
34475f6d617Schristos       if (!next || i < next->line0)
34575f6d617Schristos 	{
34675f6d617Schristos 	  putc (initial_tab ? '\t' : ' ', out);
34775f6d617Schristos 	  print_1_line (0, &files[0].linbuf[i++]);
34875f6d617Schristos 	  j++;
34975f6d617Schristos 	}
35075f6d617Schristos       else
35175f6d617Schristos 	{
35275f6d617Schristos 	  /* For each difference, first output the deleted part. */
35375f6d617Schristos 
35475f6d617Schristos 	  k = next->deleted;
35575f6d617Schristos 	  while (k--)
35675f6d617Schristos 	    {
35775f6d617Schristos 	      putc ('-', out);
35875f6d617Schristos 	      if (initial_tab)
35975f6d617Schristos 		putc ('\t', out);
36075f6d617Schristos 	      print_1_line (0, &files[0].linbuf[i++]);
36175f6d617Schristos 	    }
36275f6d617Schristos 
36375f6d617Schristos 	  /* Then output the inserted part. */
36475f6d617Schristos 
36575f6d617Schristos 	  k = next->inserted;
36675f6d617Schristos 	  while (k--)
36775f6d617Schristos 	    {
36875f6d617Schristos 	      putc ('+', out);
36975f6d617Schristos 	      if (initial_tab)
37075f6d617Schristos 		putc ('\t', out);
37175f6d617Schristos 	      print_1_line (0, &files[1].linbuf[j++]);
37275f6d617Schristos 	    }
37375f6d617Schristos 
37475f6d617Schristos 	  /* We're done with this hunk, so on to the next! */
37575f6d617Schristos 
37675f6d617Schristos 	  next = next->link;
37775f6d617Schristos 	}
37875f6d617Schristos     }
37975f6d617Schristos }
38075f6d617Schristos 
38175f6d617Schristos /* Scan a (forward-ordered) edit script for the first place that more than
38275f6d617Schristos    2*CONTEXT unchanged lines appear, and return a pointer
38375f6d617Schristos    to the `struct change' for the last change before those lines.  */
38475f6d617Schristos 
38575f6d617Schristos static struct change *
find_hunk(struct change * start)38675f6d617Schristos find_hunk (struct change *start)
38775f6d617Schristos {
38875f6d617Schristos   struct change *prev;
38975f6d617Schristos   lin top0, top1;
39075f6d617Schristos   lin thresh;
39175f6d617Schristos 
39275f6d617Schristos   /* Threshold distance is 2 * CONTEXT + 1 between two non-ignorable
39375f6d617Schristos      changes, but only CONTEXT if one is ignorable.  Watch out for
39475f6d617Schristos      integer overflow, though.  */
39575f6d617Schristos   lin non_ignorable_threshold =
39675f6d617Schristos     (LIN_MAX - 1) / 2 < context ? LIN_MAX : 2 * context + 1;
39775f6d617Schristos   lin ignorable_threshold = context;
39875f6d617Schristos 
39975f6d617Schristos   do
40075f6d617Schristos     {
40175f6d617Schristos       /* Compute number of first line in each file beyond this changed.  */
40275f6d617Schristos       top0 = start->line0 + start->deleted;
40375f6d617Schristos       top1 = start->line1 + start->inserted;
40475f6d617Schristos       prev = start;
40575f6d617Schristos       start = start->link;
40675f6d617Schristos       thresh = (prev->ignore || (start && start->ignore)
40775f6d617Schristos 		? ignorable_threshold
40875f6d617Schristos 		: non_ignorable_threshold);
40975f6d617Schristos       /* It is not supposed to matter which file we check in the end-test.
41075f6d617Schristos 	 If it would matter, crash.  */
41175f6d617Schristos       if (start && start->line0 - top0 != start->line1 - top1)
41275f6d617Schristos 	abort ();
41375f6d617Schristos     } while (start
41475f6d617Schristos 	     /* Keep going if less than THRESH lines
41575f6d617Schristos 		elapse before the affected line.  */
41675f6d617Schristos 	     && start->line0 - top0 < thresh);
41775f6d617Schristos 
41875f6d617Schristos   return prev;
41975f6d617Schristos }
42075f6d617Schristos 
42175f6d617Schristos /* Set the `ignore' flag properly in each change in SCRIPT.
42275f6d617Schristos    It should be 1 if all the lines inserted or deleted in that change
42375f6d617Schristos    are ignorable lines.  */
42475f6d617Schristos 
42575f6d617Schristos static void
mark_ignorable(struct change * script)42675f6d617Schristos mark_ignorable (struct change *script)
42775f6d617Schristos {
42875f6d617Schristos   while (script)
42975f6d617Schristos     {
43075f6d617Schristos       struct change *next = script->link;
43175f6d617Schristos       lin first0, last0, first1, last1;
43275f6d617Schristos 
43375f6d617Schristos       /* Turn this change into a hunk: detach it from the others.  */
43475f6d617Schristos       script->link = 0;
43575f6d617Schristos 
43675f6d617Schristos       /* Determine whether this change is ignorable.  */
43775f6d617Schristos       script->ignore = ! analyze_hunk (script,
43875f6d617Schristos 				       &first0, &last0, &first1, &last1);
43975f6d617Schristos 
44075f6d617Schristos       /* Reconnect the chain as before.  */
44175f6d617Schristos       script->link = next;
44275f6d617Schristos 
44375f6d617Schristos       /* Advance to the following change.  */
44475f6d617Schristos       script = next;
44575f6d617Schristos     }
44675f6d617Schristos }
44775f6d617Schristos 
44875f6d617Schristos /* Find the last function-header line in LINBUF prior to line number LINENUM.
44975f6d617Schristos    This is a line containing a match for the regexp in `function_regexp'.
45075f6d617Schristos    Return the address of the text, or 0 if no function-header is found.  */
45175f6d617Schristos 
45275f6d617Schristos static char const *
find_function(char const * const * linbuf,lin linenum)45375f6d617Schristos find_function (char const * const *linbuf, lin linenum)
45475f6d617Schristos {
45575f6d617Schristos   lin i = linenum;
45675f6d617Schristos   lin last = find_function_last_search;
45775f6d617Schristos   find_function_last_search = i;
45875f6d617Schristos 
45975f6d617Schristos   while (last <= --i)
46075f6d617Schristos     {
46175f6d617Schristos       /* See if this line is what we want.  */
46275f6d617Schristos       char const *line = linbuf[i];
46375f6d617Schristos       size_t linelen = linbuf[i + 1] - line - 1;
46475f6d617Schristos 
46575f6d617Schristos       /* FIXME: re_search's size args should be size_t, not int.  */
46675f6d617Schristos       int len = MIN (linelen, INT_MAX);
46775f6d617Schristos 
46875f6d617Schristos       if (0 <= re_search (&function_regexp, line, len, 0, len, 0))
46975f6d617Schristos 	{
47075f6d617Schristos 	  find_function_last_match = i;
47175f6d617Schristos 	  return line;
47275f6d617Schristos 	}
47375f6d617Schristos     }
47475f6d617Schristos   /* If we search back to where we started searching the previous time,
47575f6d617Schristos      find the line we found last time.  */
47675f6d617Schristos   if (find_function_last_match != LIN_MAX)
47775f6d617Schristos     return linbuf[find_function_last_match];
47875f6d617Schristos 
47975f6d617Schristos   return 0;
48075f6d617Schristos }
481