Fix null pointer dereference in process_debug_info()
[binutils-gdb.git] / gdb / tid-parse.c
blob623f55e14b366d532167f73f5239f46c3cc8c6b8
1 /* TID parsing for GDB, the GNU debugger.
3 Copyright (C) 2015-2024 Free Software Foundation, Inc.
5 This file is part of GDB.
7 This program is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 3 of the License, or
10 (at your option) any later version.
12 This program is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
17 You should have received a copy of the GNU General Public License
18 along with this program. If not, see <http://www.gnu.org/licenses/>. */
20 #include "tid-parse.h"
21 #include "inferior.h"
22 #include "gdbthread.h"
23 #include <ctype.h>
25 /* See tid-parse.h. */
27 void ATTRIBUTE_NORETURN
28 invalid_thread_id_error (const char *string)
30 error (_("Invalid thread ID: %s"), string);
33 /* Wrapper for get_number_trailer that throws an error if we get back
34 a negative number. We'll see a negative value if the number is
35 stored in a negative convenience variable (e.g., $minus_one = -1).
36 STRING is the parser string to be used in the error message if we
37 do get back a negative number. */
39 static int
40 get_positive_number_trailer (const char **pp, int trailer, const char *string)
42 int num;
44 num = get_number_trailer (pp, trailer);
45 if (num < 0)
46 error (_("negative value: %s"), string);
47 return num;
50 /* See tid-parse.h. */
52 struct thread_info *
53 parse_thread_id (const char *tidstr, const char **end)
55 const char *number = tidstr;
56 const char *dot, *p1;
57 struct inferior *inf;
58 int thr_num;
59 int explicit_inf_id = 0;
61 dot = strchr (number, '.');
63 if (dot != NULL)
65 /* Parse number to the left of the dot. */
66 int inf_num;
68 p1 = number;
69 inf_num = get_positive_number_trailer (&p1, '.', number);
70 if (inf_num == 0)
71 invalid_thread_id_error (number);
73 inf = find_inferior_id (inf_num);
74 if (inf == NULL)
75 error (_("No inferior number '%d'"), inf_num);
77 explicit_inf_id = 1;
78 p1 = dot + 1;
80 else
82 inf = current_inferior ();
84 p1 = number;
87 thr_num = get_positive_number_trailer (&p1, 0, number);
88 if (thr_num == 0)
89 invalid_thread_id_error (number);
91 thread_info *tp = nullptr;
92 for (thread_info *it : inf->threads ())
93 if (it->per_inf_num == thr_num)
95 tp = it;
96 break;
99 if (tp == NULL)
101 if (show_inferior_qualified_tids () || explicit_inf_id)
102 error (_("Unknown thread %d.%d."), inf->num, thr_num);
103 else
104 error (_("Unknown thread %d."), thr_num);
107 if (end != NULL)
108 *end = p1;
110 return tp;
113 /* See tid-parse.h. */
115 tid_range_parser::tid_range_parser (const char *tidlist,
116 int default_inferior)
118 init (tidlist, default_inferior);
121 /* See tid-parse.h. */
123 void
124 tid_range_parser::init (const char *tidlist, int default_inferior)
126 m_state = STATE_INFERIOR;
127 m_cur_tok = tidlist;
128 m_inf_num = 0;
129 m_qualified = false;
130 m_default_inferior = default_inferior;
133 /* See tid-parse.h. */
135 bool
136 tid_range_parser::finished () const
138 switch (m_state)
140 case STATE_INFERIOR:
141 /* Parsing is finished when at end of string or null string,
142 or we are not in a range and not in front of an integer, negative
143 integer, convenience var or negative convenience var. */
144 return (*m_cur_tok == '\0'
145 || !(isdigit (*m_cur_tok)
146 || *m_cur_tok == '$'
147 || *m_cur_tok == '*'));
148 case STATE_THREAD_RANGE:
149 case STATE_STAR_RANGE:
150 return m_range_parser.finished ();
153 gdb_assert_not_reached ("unhandled state");
156 /* See tid-parse.h. */
158 const char *
159 tid_range_parser::cur_tok () const
161 switch (m_state)
163 case STATE_INFERIOR:
164 return m_cur_tok;
165 case STATE_THREAD_RANGE:
166 case STATE_STAR_RANGE:
167 return m_range_parser.cur_tok ();
170 gdb_assert_not_reached ("unhandled state");
173 void
174 tid_range_parser::skip_range ()
176 gdb_assert (m_state == STATE_THREAD_RANGE
177 || m_state == STATE_STAR_RANGE);
179 m_range_parser.skip_range ();
180 init (m_range_parser.cur_tok (), m_default_inferior);
183 /* See tid-parse.h. */
185 bool
186 tid_range_parser::tid_is_qualified () const
188 return m_qualified;
191 /* Helper for tid_range_parser::get_tid and
192 tid_range_parser::get_tid_range. Return the next range if THR_END
193 is non-NULL, return a single thread ID otherwise. */
195 bool
196 tid_range_parser::get_tid_or_range (int *inf_num,
197 int *thr_start, int *thr_end)
199 if (m_state == STATE_INFERIOR)
201 const char *p;
202 const char *space;
204 space = skip_to_space (m_cur_tok);
206 p = m_cur_tok;
207 while (p < space && *p != '.')
208 p++;
209 if (p < space)
211 const char *dot = p;
213 /* Parse number to the left of the dot. */
214 p = m_cur_tok;
215 m_inf_num = get_positive_number_trailer (&p, '.', m_cur_tok);
216 if (m_inf_num == 0)
217 return 0;
219 m_qualified = true;
220 p = dot + 1;
222 if (isspace (*p))
223 return false;
225 else
227 m_inf_num = m_default_inferior;
228 m_qualified = false;
229 p = m_cur_tok;
232 m_range_parser.init (p);
233 if (p[0] == '*' && (p[1] == '\0' || isspace (p[1])))
235 /* Setup the number range parser to return numbers in the
236 whole [1,INT_MAX] range. */
237 m_range_parser.setup_range (1, INT_MAX, skip_spaces (p + 1));
238 m_state = STATE_STAR_RANGE;
240 else
241 m_state = STATE_THREAD_RANGE;
244 *inf_num = m_inf_num;
245 *thr_start = m_range_parser.get_number ();
246 if (*thr_start < 0)
247 error (_("negative value: %s"), m_cur_tok);
248 if (*thr_start == 0)
250 m_state = STATE_INFERIOR;
251 return false;
254 /* If we successfully parsed a thread number or finished parsing a
255 thread range, switch back to assuming the next TID is
256 inferior-qualified. */
257 if (!m_range_parser.in_range ())
259 m_state = STATE_INFERIOR;
260 m_cur_tok = m_range_parser.cur_tok ();
262 if (thr_end != NULL)
263 *thr_end = *thr_start;
266 /* If we're midway through a range, and the caller wants the end
267 value, return it and skip to the end of the range. */
268 if (thr_end != NULL
269 && (m_state == STATE_THREAD_RANGE
270 || m_state == STATE_STAR_RANGE))
272 *thr_end = m_range_parser.end_value ();
274 skip_range ();
277 return (*inf_num != 0 && *thr_start != 0);
280 /* See tid-parse.h. */
282 bool
283 tid_range_parser::get_tid_range (int *inf_num,
284 int *thr_start, int *thr_end)
286 gdb_assert (inf_num != NULL && thr_start != NULL && thr_end != NULL);
288 return get_tid_or_range (inf_num, thr_start, thr_end);
291 /* See tid-parse.h. */
293 bool
294 tid_range_parser::get_tid (int *inf_num, int *thr_num)
296 gdb_assert (inf_num != NULL && thr_num != NULL);
298 return get_tid_or_range (inf_num, thr_num, NULL);
301 /* See tid-parse.h. */
303 bool
304 tid_range_parser::in_star_range () const
306 return m_state == STATE_STAR_RANGE;
309 bool
310 tid_range_parser::in_thread_range () const
312 return m_state == STATE_THREAD_RANGE;
315 /* See tid-parse.h. */
318 tid_is_in_list (const char *list, int default_inferior,
319 int inf_num, int thr_num)
321 if (list == NULL || *list == '\0')
322 return 1;
324 tid_range_parser parser (list, default_inferior);
325 if (parser.finished ())
326 invalid_thread_id_error (parser.cur_tok ());
327 while (!parser.finished ())
329 int tmp_inf, tmp_thr_start, tmp_thr_end;
331 if (!parser.get_tid_range (&tmp_inf, &tmp_thr_start, &tmp_thr_end))
332 invalid_thread_id_error (parser.cur_tok ());
333 if (tmp_inf == inf_num
334 && tmp_thr_start <= thr_num && thr_num <= tmp_thr_end)
335 return 1;
337 return 0;