1/* Map logical line numbers to (source file, line number) pairs.
2   Copyright (C) 2001, 2003, 2004
3   Free Software Foundation, Inc.
4
5This program is free software; you can redistribute it and/or modify it
6under the terms of the GNU General Public License as published by the
7Free Software Foundation; either version 2, or (at your option) any
8later version.
9
10This program is distributed in the hope that it will be useful,
11but WITHOUT ANY WARRANTY; without even the implied warranty of
12MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13GNU General Public License for more details.
14
15You should have received a copy of the GNU General Public License
16along with this program; if not, write to the Free Software
17Foundation, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
18
19 In other words, you are welcome to use, share and improve this program.
20 You are forbidden to forbid anyone else to use, share and improve
21 what you give them.   Help stamp out software-hoarding!  */
22
23#include "config.h"
24#include "system.h"
25#include "line-map.h"
26
27static void trace_include (const struct line_maps *, const struct line_map *);
28
29/* Initialize a line map set.  */
30
31void
32linemap_init (struct line_maps *set)
33{
34  set->maps = NULL;
35  set->allocated = 0;
36  set->used = 0;
37  set->last_listed = -1;
38  set->trace_includes = false;
39  set->depth = 0;
40  set->cache = 0;
41  set->highest_location = 0;
42  set->highest_line = 0;
43  set->max_column_hint = 0;
44}
45
46/* Check for and warn about line_maps entered but not exited.  */
47
48void
49linemap_check_files_exited (struct line_maps *set)
50{
51  struct line_map *map;
52  /* Depending upon whether we are handling preprocessed input or
53     not, this can be a user error or an ICE.  */
54  for (map = &set->maps[set->used - 1]; ! MAIN_FILE_P (map);
55       map = INCLUDED_FROM (set, map))
56    fprintf (stderr, "line-map.c: file \"%s\" entered but not left\n",
57	     map->to_file);
58}
59
60/* Free a line map set.  */
61
62void
63linemap_free (struct line_maps *set)
64{
65  if (set->maps)
66    {
67      linemap_check_files_exited (set);
68
69      free (set->maps);
70    }
71}
72
73/* Add a mapping of logical source line to physical source file and
74   line number.
75
76   The text pointed to by TO_FILE must have a lifetime
77   at least as long as the final call to lookup_line ().  An empty
78   TO_FILE means standard input.  If reason is LC_LEAVE, and
79   TO_FILE is NULL, then TO_FILE, TO_LINE and SYSP are given their
80   natural values considering the file we are returning to.
81
82   FROM_LINE should be monotonic increasing across calls to this
83   function.  A call to this function can relocate the previous set of
84   A call to this function can relocate the previous set of
85   maps, so any stored line_map pointers should not be used.  */
86
87const struct line_map *
88linemap_add (struct line_maps *set, enum lc_reason reason,
89	     unsigned int sysp, const char *to_file, unsigned int to_line)
90{
91  struct line_map *map;
92  source_location start_location = set->highest_location + 1;
93
94  if (set->used && start_location < set->maps[set->used - 1].start_location)
95    abort ();
96
97  if (set->used == set->allocated)
98    {
99      set->allocated = 2 * set->allocated + 256;
100      set->maps = XRESIZEVEC (struct line_map, set->maps, set->allocated);
101    }
102
103  map = &set->maps[set->used];
104
105  if (to_file && *to_file == '\0')
106    to_file = "<stdin>";
107
108  /* If we don't keep our line maps consistent, we can easily
109     segfault.  Don't rely on the client to do it for us.  */
110  if (set->depth == 0)
111    reason = LC_ENTER;
112  else if (reason == LC_LEAVE)
113    {
114      struct line_map *from;
115      bool error;
116
117      if (MAIN_FILE_P (map - 1))
118	{
119	  if (to_file == NULL)
120	    {
121	      set->depth--;
122	      return NULL;
123	    }
124	  error = true;
125          reason = LC_RENAME;
126          from = map - 1;
127	}
128      else
129	{
130	  from = INCLUDED_FROM (set, map - 1);
131	  error = to_file && strcmp (from->to_file, to_file);
132	}
133
134      /* Depending upon whether we are handling preprocessed input or
135	 not, this can be a user error or an ICE.  */
136      if (error)
137	fprintf (stderr, "line-map.c: file \"%s\" left but not entered\n",
138		 to_file);
139
140      /* A TO_FILE of NULL is special - we use the natural values.  */
141      if (error || to_file == NULL)
142	{
143	  to_file = from->to_file;
144	  to_line = SOURCE_LINE (from, from[1].start_location);
145	  sysp = from->sysp;
146	}
147    }
148
149  map->reason = reason;
150  map->sysp = sysp;
151  map->start_location = start_location;
152  map->to_file = to_file;
153  map->to_line = to_line;
154  set->cache = set->used++;
155  map->column_bits = 0;
156  set->highest_location = start_location;
157  set->highest_line = start_location;
158  set->max_column_hint = 0;
159
160  if (reason == LC_ENTER)
161    {
162      map->included_from = set->depth == 0 ? -1 : (int) (set->used - 2);
163      set->depth++;
164      if (set->trace_includes)
165	trace_include (set, map);
166    }
167  else if (reason == LC_RENAME)
168    map->included_from = map[-1].included_from;
169  else if (reason == LC_LEAVE)
170    {
171      set->depth--;
172      map->included_from = INCLUDED_FROM (set, map - 1)->included_from;
173    }
174
175  return map;
176}
177
178source_location
179linemap_line_start (struct line_maps *set, unsigned int to_line,
180		    unsigned int max_column_hint)
181{
182  struct line_map *map = &set->maps[set->used - 1];
183  source_location highest = set->highest_location;
184  source_location r;
185  unsigned int last_line = SOURCE_LINE (map, set->highest_line);
186  int line_delta = to_line - last_line;
187  bool add_map = false;
188  if (line_delta < 0
189      || (line_delta > 10 && line_delta * map->column_bits > 1000)
190      || (max_column_hint >= (1U << map->column_bits))
191      || (max_column_hint <= 80 && map->column_bits >= 10))
192    {
193      add_map = true;
194    }
195  else
196    max_column_hint = set->max_column_hint;
197  if (add_map)
198    {
199      int column_bits;
200      if (max_column_hint > 100000 || highest > 0xC0000000)
201	{
202	  /* If the column number is ridiculous or we've allocated a huge
203	     number of source_locations, give up on column numbers. */
204	  max_column_hint = 0;
205	  if (highest >0xF0000000)
206	    return 0;
207	  column_bits = 0;
208	}
209      else
210	{
211	  column_bits = 7;
212	  while (max_column_hint >= (1U << column_bits))
213	    column_bits++;
214	  max_column_hint = 1U << column_bits;
215	}
216      /* Allocate the new line_map.  However, if the current map only has a
217	 single line we can sometimes just increase its column_bits instead. */
218      if (line_delta < 0
219	  || last_line != map->to_line
220	  || SOURCE_COLUMN (map, highest) >= (1U << column_bits))
221	map = (struct line_map*) linemap_add (set, LC_RENAME, map->sysp,
222				      map->to_file, to_line);
223      map->column_bits = column_bits;
224      r = map->start_location + ((to_line - map->to_line) << column_bits);
225    }
226  else
227    r = highest - SOURCE_COLUMN (map, highest)
228      + (line_delta << map->column_bits);
229  set->highest_line = r;
230  if (r > set->highest_location)
231    set->highest_location = r;
232  set->max_column_hint = max_column_hint;
233  return r;
234}
235
236source_location
237linemap_position_for_column (struct line_maps *set, unsigned int to_column)
238{
239  source_location r = set->highest_line;
240  if (to_column >= set->max_column_hint)
241    {
242      if (r >= 0xC000000 || to_column > 100000)
243	{
244	  /* Running low on source_locations - disable column numbers.  */
245	  return r;
246	}
247      else
248	{
249	  struct line_map *map = &set->maps[set->used - 1];
250	  r = linemap_line_start (set, SOURCE_LINE (map, r), to_column + 50);
251	}
252    }
253  r = r + to_column;
254  if (r >= set->highest_location)
255    set->highest_location = r;
256  return r;
257}
258
259/* Given a logical line, returns the map from which the corresponding
260   (source file, line) pair can be deduced.  Since the set is built
261   chronologically, the logical lines are monotonic increasing, and so
262   the list is sorted and we can use a binary search.  */
263
264const struct line_map *
265linemap_lookup (struct line_maps *set, source_location line)
266{
267  unsigned int md, mn, mx;
268  const struct line_map *cached;
269
270  mn = set->cache;
271  mx = set->used;
272
273  cached = &set->maps[mn];
274  /* We should get a segfault if no line_maps have been added yet.  */
275  if (line >= cached->start_location)
276    {
277      if (mn + 1 == mx || line < cached[1].start_location)
278	return cached;
279    }
280  else
281    {
282      mx = mn;
283      mn = 0;
284    }
285
286  while (mx - mn > 1)
287    {
288      md = (mn + mx) / 2;
289      if (set->maps[md].start_location > line)
290	mx = md;
291      else
292	mn = md;
293    }
294
295  set->cache = mn;
296  return &set->maps[mn];
297}
298
299/* Print the file names and line numbers of the #include commands
300   which led to the map MAP, if any, to stderr.  Nothing is output if
301   the most recently listed stack is the same as the current one.  */
302
303void
304linemap_print_containing_files (struct line_maps *set,
305				const struct line_map *map)
306{
307  if (MAIN_FILE_P (map) || set->last_listed == map->included_from)
308    return;
309
310  set->last_listed = map->included_from;
311  map = INCLUDED_FROM (set, map);
312
313  fprintf (stderr,  _("In file included from %s:%u"),
314	   map->to_file, LAST_SOURCE_LINE (map));
315
316  while (! MAIN_FILE_P (map))
317    {
318      map = INCLUDED_FROM (set, map);
319      /* Translators note: this message is used in conjunction
320	 with "In file included from %s:%ld" and some other
321	 tricks.  We want something like this:
322
323	 | In file included from sys/select.h:123,
324	 |                  from sys/types.h:234,
325	 |                  from userfile.c:31:
326	 | bits/select.h:45: <error message here>
327
328	 with all the "from"s lined up.
329	 The trailing comma is at the beginning of this message,
330	 and the trailing colon is not translated.  */
331      fprintf (stderr, _(",\n                 from %s:%u"),
332	       map->to_file, LAST_SOURCE_LINE (map));
333    }
334
335  fputs (":\n", stderr);
336}
337
338/* Print an include trace, for e.g. the -H option of the preprocessor.  */
339
340static void
341trace_include (const struct line_maps *set, const struct line_map *map)
342{
343  unsigned int i = set->depth;
344
345  while (--i)
346    putc ('.', stderr);
347  fprintf (stderr, " %s\n", map->to_file);
348}
349