119370Spst/* Support for complaint handling during symbol reading in GDB.
219370Spst
3130803Smarcel   Copyright 1990, 1991, 1992, 1993, 1995, 1998, 1999, 2000, 2002 Free
4130803Smarcel   Software Foundation, Inc.
5130803Smarcel
698944Sobrien   This file is part of GDB.
719370Spst
898944Sobrien   This program is free software; you can redistribute it and/or modify
998944Sobrien   it under the terms of the GNU General Public License as published by
1098944Sobrien   the Free Software Foundation; either version 2 of the License, or
1198944Sobrien   (at your option) any later version.
1219370Spst
1398944Sobrien   This program is distributed in the hope that it will be useful,
1498944Sobrien   but WITHOUT ANY WARRANTY; without even the implied warranty of
1598944Sobrien   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
1698944Sobrien   GNU General Public License for more details.
1719370Spst
1898944Sobrien   You should have received a copy of the GNU General Public License
1998944Sobrien   along with this program; if not, write to the Free Software
2098944Sobrien   Foundation, Inc., 59 Temple Place - Suite 330,
2198944Sobrien   Boston, MA 02111-1307, USA.  */
2219370Spst
2319370Spst#include "defs.h"
2419370Spst#include "complaints.h"
25130803Smarcel#include "gdb_assert.h"
26130803Smarcel#include "command.h"
2719370Spst#include "gdbcmd.h"
2819370Spst
2998944Sobrienextern void _initialize_complaints (void);
3098944Sobrien
31130803Smarcel/* Should each complaint message be self explanatory, or should we assume that
32130803Smarcel   a series of complaints is being produced?  */
33130803Smarcel
34130803Smarcel/* case 1: First message of a series that must
35130803Smarcel   start off with explanation.  case 2: Subsequent message of a series
36130803Smarcel   that needs no explanation (the user already knows we have a problem
37130803Smarcel   so we can just state our piece).  */
38130803Smarcelenum complaint_series {
39130803Smarcel  /* Isolated self explanatory message.  */
40130803Smarcel  ISOLATED_MESSAGE,
41130803Smarcel  /* First message of a series, includes an explanation.  */
42130803Smarcel  FIRST_MESSAGE,
43130803Smarcel  /* First message of a series, but does not need to include any sort
44130803Smarcel     of explanation.  */
45130803Smarcel  SHORT_FIRST_MESSAGE,
46130803Smarcel  /* Subsequent message of a series that needs no explanation (the
47130803Smarcel     user already knows we have a problem so we can just state our
48130803Smarcel     piece).  */
49130803Smarcel  SUBSEQUENT_MESSAGE
50130803Smarcel};
51130803Smarcel
5219370Spst/* Structure to manage complaints about symbol file contents.  */
5319370Spst
54130803Smarcelstruct complain
5598944Sobrien{
56130803Smarcel  const char *file;
57130803Smarcel  int line;
58130803Smarcel  const char *fmt;
59130803Smarcel  int counter;
60130803Smarcel  struct complain *next;
6119370Spst};
6219370Spst
63130803Smarcel/* The explanatory message that should accompany the complaint.  The
64130803Smarcel   message is in two parts - pre and post - that are printed around
65130803Smarcel   the complaint text.  */
66130803Smarcelstruct explanation
67130803Smarcel{
68130803Smarcel  const char *prefix;
69130803Smarcel  const char *postfix;
70130803Smarcel};
7119370Spst
72130803Smarcelstruct complaints
73130803Smarcel{
74130803Smarcel  struct complain *root;
7519370Spst
76130803Smarcel  /* Should each complaint be self explanatory, or should we assume
77130803Smarcel     that a series of complaints is being produced?  case 0: Isolated
78130803Smarcel     self explanatory message.  case 1: First message of a series that
79130803Smarcel     must start off with explanation.  case 2: Subsequent message of a
80130803Smarcel     series that needs no explanation (the user already knows we have
81130803Smarcel     a problem so we can just state our piece).  */
82130803Smarcel  int series;
8319370Spst
84130803Smarcel  /* The explanatory messages that should accompany the complaint.
85130803Smarcel     NOTE: cagney/2002-08-14: In a desperate attempt at being vaguely
86130803Smarcel     i18n friendly, this is an array of two messages.  When present,
87130803Smarcel     the PRE and POST EXPLANATION[SERIES] are used to wrap the
88130803Smarcel     message.  */
89130803Smarcel  const struct explanation *explanation;
90130803Smarcel};
9119370Spst
92130803Smarcelstatic struct complain complaint_sentinel;
9319370Spst
94130803Smarcel/* The symbol table complaint table.  */
9519370Spst
96130803Smarcelstatic struct explanation symfile_explanations[] = {
97130803Smarcel  { "During symbol reading, ", "." },
98130803Smarcel  { "During symbol reading...", "..."},
99130803Smarcel  { "", "..."},
100130803Smarcel  { "", "..."},
101130803Smarcel  { NULL, NULL }
102130803Smarcel};
10319370Spst
104130803Smarcelstatic struct complaints symfile_complaint_book = {
105130803Smarcel  &complaint_sentinel,
106130803Smarcel  0,
107130803Smarcel  symfile_explanations
108130803Smarcel};
109130803Smarcelstruct complaints *symfile_complaints = &symfile_complaint_book;
110130803Smarcel
111130803Smarcel/* Wrapper function to, on-demand, fill in a complaints object.  */
112130803Smarcel
113130803Smarcelstatic struct complaints *
114130803Smarcelget_complaints (struct complaints **c)
11519370Spst{
116130803Smarcel  if ((*c) != NULL)
117130803Smarcel    return (*c);
118130803Smarcel  (*c) = XMALLOC (struct complaints);
119130803Smarcel  (*c)->root = &complaint_sentinel;
120130803Smarcel  (*c)->series = ISOLATED_MESSAGE;
121130803Smarcel  (*c)->explanation = NULL;
122130803Smarcel  return (*c);
123130803Smarcel}
12419370Spst
125130803Smarcelstatic struct complain *
126130803Smarcelfind_complaint (struct complaints *complaints, const char *file,
127130803Smarcel		int line, const char *fmt)
128130803Smarcel{
129130803Smarcel  struct complain *complaint;
130130803Smarcel
131130803Smarcel  /* Find the complaint in the table.  A more efficient search
132130803Smarcel     algorithm (based on hash table or something) could be used.  But
133130803Smarcel     that can wait until someone shows evidence that this lookup is
134130803Smarcel     a real bottle neck.  */
135130803Smarcel  for (complaint = complaints->root;
136130803Smarcel       complaint != NULL;
137130803Smarcel       complaint = complaint->next)
13819370Spst    {
139130803Smarcel      if (complaint->fmt == fmt
140130803Smarcel	  && complaint->file == file
141130803Smarcel	  && complaint->line == line)
142130803Smarcel	return complaint;
14319370Spst    }
144130803Smarcel
145130803Smarcel  /* Oops not seen before, fill in a new complaint.  */
146130803Smarcel  complaint = XMALLOC (struct complain);
147130803Smarcel  complaint->fmt = fmt;
148130803Smarcel  complaint->file = file;
149130803Smarcel  complaint->line = line;
150130803Smarcel  complaint->counter = 0;
151130803Smarcel  complaint->next = NULL;
152130803Smarcel
153130803Smarcel  /* File it, return it.  */
154130803Smarcel  complaint->next = complaints->root;
155130803Smarcel  complaints->root = complaint;
156130803Smarcel  return complaint;
157130803Smarcel}
158130803Smarcel
159130803Smarcel
160130803Smarcel/* How many complaints about a particular thing should be printed
161130803Smarcel   before we stop whining about it?  Default is no whining at all,
162130803Smarcel   since so many systems have ill-constructed symbol files.  */
163130803Smarcel
164130803Smarcelstatic unsigned int stop_whining = 0;
165130803Smarcel
166130803Smarcel/* Print a complaint, and link the complaint block into a chain for
167130803Smarcel   later handling.  */
168130803Smarcel
169130803Smarcelstatic void
170130803Smarcelvcomplaint (struct complaints **c, const char *file, int line, const char *fmt,
171130803Smarcel	    va_list args)
172130803Smarcel{
173130803Smarcel  struct complaints *complaints = get_complaints (c);
174130803Smarcel  struct complain *complaint = find_complaint (complaints, file, line, fmt);
175130803Smarcel  enum complaint_series series;
176130803Smarcel  gdb_assert (complaints != NULL);
177130803Smarcel
178130803Smarcel  complaint->counter++;
17998944Sobrien  if (complaint->counter > stop_whining)
180130803Smarcel    return;
181130803Smarcel
182130803Smarcel  if (info_verbose)
183130803Smarcel    series = SUBSEQUENT_MESSAGE;
184130803Smarcel  else
185130803Smarcel    series = complaints->series;
186130803Smarcel
187130803Smarcel  if (complaint->file != NULL)
188130803Smarcel    internal_vwarning (complaint->file, complaint->line, complaint->fmt, args);
189130803Smarcel  else if (warning_hook)
190130803Smarcel    (*warning_hook) (complaint->fmt, args);
191130803Smarcel  else
19219370Spst    {
193130803Smarcel      if (complaints->explanation == NULL)
194130803Smarcel	/* A [v]warning() call always appends a newline.  */
195130803Smarcel	vwarning (complaint->fmt, args);
196130803Smarcel      else
197130803Smarcel	{
198130803Smarcel	  char *msg;
199130803Smarcel	  struct cleanup *cleanups;
200130803Smarcel	  xvasprintf (&msg, complaint->fmt, args);
201130803Smarcel	  cleanups = make_cleanup (xfree, msg);
202130803Smarcel	  wrap_here ("");
203130803Smarcel	  if (series != SUBSEQUENT_MESSAGE)
204130803Smarcel	    begin_line ();
205130803Smarcel	  fprintf_filtered (gdb_stderr, "%s%s%s",
206130803Smarcel			    complaints->explanation[series].prefix, msg,
207130803Smarcel			    complaints->explanation[series].postfix);
208130803Smarcel	  /* Force a line-break after any isolated message.  For the
209130803Smarcel             other cases, clear_complaints() takes care of any missing
210130803Smarcel             trailing newline, the wrap_here() is just a hint.  */
211130803Smarcel	  if (series == ISOLATED_MESSAGE)
212130803Smarcel	    /* It would be really nice to use begin_line() here.
213130803Smarcel	       Unfortunately that function doesn't track GDB_STDERR and
214130803Smarcel	       consequently will sometimes supress a line when it
215130803Smarcel	       shouldn't.  */
216130803Smarcel	    fputs_filtered ("\n", gdb_stderr);
217130803Smarcel	  else
218130803Smarcel	    wrap_here ("");
219130803Smarcel	  do_cleanups (cleanups);
220130803Smarcel	}
22119370Spst    }
22219370Spst
223130803Smarcel  switch (series)
22419370Spst    {
225130803Smarcel    case ISOLATED_MESSAGE:
22698944Sobrien      break;
227130803Smarcel    case FIRST_MESSAGE:
228130803Smarcel      complaints->series = SUBSEQUENT_MESSAGE;
22998944Sobrien      break;
230130803Smarcel    case SUBSEQUENT_MESSAGE:
231130803Smarcel    case SHORT_FIRST_MESSAGE:
232130803Smarcel      complaints->series = SUBSEQUENT_MESSAGE;
233130803Smarcel      break;
234130803Smarcel    }
23519370Spst
236130803Smarcel  /* If GDB dumps core, we'd like to see the complaints first.
237130803Smarcel     Presumably GDB will not be sending so many complaints that this
238130803Smarcel     becomes a performance hog.  */
239130803Smarcel
24098944Sobrien  gdb_flush (gdb_stderr);
241130803Smarcel}
242130803Smarcel
243130803Smarcelvoid
244130803Smarcelcomplaint (struct complaints **complaints, const char *fmt, ...)
245130803Smarcel{
246130803Smarcel  va_list args;
247130803Smarcel  va_start (args, fmt);
248130803Smarcel  vcomplaint (complaints, NULL/*file*/, 0/*line*/, fmt, args);
24919370Spst  va_end (args);
25019370Spst}
25119370Spst
252130803Smarcelvoid
253130803Smarcelinternal_complaint (struct complaints **complaints, const char *file,
254130803Smarcel		    int line, const char *fmt, ...)
255130803Smarcel{
256130803Smarcel  va_list args;
257130803Smarcel  va_start (args, fmt);
258130803Smarcel  vcomplaint (complaints, file, line, fmt, args);
259130803Smarcel  va_end (args);
260130803Smarcel}
26119370Spst
262130803Smarcel/* Clear out / initialize all complaint counters that have ever been
263130803Smarcel   incremented.  If LESS_VERBOSE is 1, be less verbose about
264130803Smarcel   successive complaints, since the messages are appearing all
265130803Smarcel   together during a command that is reporting a contiguous block of
266130803Smarcel   complaints (rather than being interleaved with other messages).  If
267130803Smarcel   noisy is 1, we are in a noisy command, and our caller will print
268130803Smarcel   enough context for the user to figure it out.  */
269130803Smarcel
27019370Spstvoid
271130803Smarcelclear_complaints (struct complaints **c, int less_verbose, int noisy)
27219370Spst{
273130803Smarcel  struct complaints *complaints = get_complaints (c);
274130803Smarcel  struct complain *p;
27519370Spst
276130803Smarcel  for (p = complaints->root; p != NULL; p = p->next)
27719370Spst    {
27898944Sobrien      p->counter = 0;
27919370Spst    }
28019370Spst
281130803Smarcel  switch (complaints->series)
28219370Spst    {
283130803Smarcel    case FIRST_MESSAGE:
284130803Smarcel      /* Haven't yet printed anything.  */
285130803Smarcel      break;
286130803Smarcel    case SHORT_FIRST_MESSAGE:
287130803Smarcel      /* Haven't yet printed anything.  */
288130803Smarcel      break;
289130803Smarcel    case ISOLATED_MESSAGE:
290130803Smarcel      /* The code above, always forces a line-break.  No need to do it
291130803Smarcel         here.  */
292130803Smarcel      break;
293130803Smarcel    case SUBSEQUENT_MESSAGE:
294130803Smarcel      /* It would be really nice to use begin_line() here.
295130803Smarcel         Unfortunately that function doesn't track GDB_STDERR and
296130803Smarcel         consequently will sometimes supress a line when it shouldn't.  */
297130803Smarcel      fputs_unfiltered ("\n", gdb_stderr);
298130803Smarcel      break;
299130803Smarcel    default:
300130803Smarcel      internal_error (__FILE__, __LINE__, "bad switch");
30119370Spst    }
30219370Spst
303130803Smarcel  if (!less_verbose)
304130803Smarcel    complaints->series = ISOLATED_MESSAGE;
305130803Smarcel  else if (!noisy)
306130803Smarcel    complaints->series = FIRST_MESSAGE;
307130803Smarcel  else
308130803Smarcel    complaints->series = SHORT_FIRST_MESSAGE;
30919370Spst}
31019370Spst
31119370Spstvoid
31298944Sobrien_initialize_complaints (void)
31319370Spst{
314130803Smarcel  add_setshow_cmd ("complaints", class_support, var_zinteger,
315130803Smarcel		   &stop_whining,
316130803Smarcel		   "Set max number of complaints about incorrect symbols.",
317130803Smarcel		   "Show max number of complaints about incorrect symbols.",
318130803Smarcel		   NULL, NULL,
319130803Smarcel		   &setlist, &showlist);
32019370Spst
32119370Spst}
322