1/* TID parsing for GDB, the GNU debugger.
2
3   Copyright (C) 2015-2020 Free Software Foundation, Inc.
4
5   This file is part of GDB.
6
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.
11
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.
16
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/>.  */
19
20#include "defs.h"
21#include "tid-parse.h"
22#include "inferior.h"
23#include "gdbthread.h"
24#include <ctype.h>
25
26/* See tid-parse.h.  */
27
28void ATTRIBUTE_NORETURN
29invalid_thread_id_error (const char *string)
30{
31  error (_("Invalid thread ID: %s"), string);
32}
33
34/* Wrapper for get_number_trailer that throws an error if we get back
35   a negative number.  We'll see a negative value if the number is
36   stored in a negative convenience variable (e.g., $minus_one = -1).
37   STRING is the parser string to be used in the error message if we
38   do get back a negative number.  */
39
40static int
41get_positive_number_trailer (const char **pp, int trailer, const char *string)
42{
43  int num;
44
45  num = get_number_trailer (pp, trailer);
46  if (num < 0)
47    error (_("negative value: %s"), string);
48  return num;
49}
50
51/* See tid-parse.h.  */
52
53struct thread_info *
54parse_thread_id (const char *tidstr, const char **end)
55{
56  const char *number = tidstr;
57  const char *dot, *p1;
58  struct inferior *inf;
59  int thr_num;
60  int explicit_inf_id = 0;
61
62  dot = strchr (number, '.');
63
64  if (dot != NULL)
65    {
66      /* Parse number to the left of the dot.  */
67      int inf_num;
68
69      p1 = number;
70      inf_num = get_positive_number_trailer (&p1, '.', number);
71      if (inf_num == 0)
72	invalid_thread_id_error (number);
73
74      inf = find_inferior_id (inf_num);
75      if (inf == NULL)
76	error (_("No inferior number '%d'"), inf_num);
77
78      explicit_inf_id = 1;
79      p1 = dot + 1;
80    }
81  else
82    {
83      inf = current_inferior ();
84
85      p1 = number;
86    }
87
88  thr_num = get_positive_number_trailer (&p1, 0, number);
89  if (thr_num == 0)
90    invalid_thread_id_error (number);
91
92  thread_info *tp = nullptr;
93  for (thread_info *it : inf->threads ())
94    if (it->per_inf_num == thr_num)
95      {
96	tp = it;
97	break;
98      }
99
100  if (tp == NULL)
101    {
102      if (show_inferior_qualified_tids () || explicit_inf_id)
103	error (_("Unknown thread %d.%d."), inf->num, thr_num);
104      else
105	error (_("Unknown thread %d."), thr_num);
106    }
107
108  if (end != NULL)
109    *end = p1;
110
111  return tp;
112}
113
114/* See tid-parse.h.  */
115
116tid_range_parser::tid_range_parser (const char *tidlist,
117				    int default_inferior)
118{
119  init (tidlist, default_inferior);
120}
121
122/* See tid-parse.h.  */
123
124void
125tid_range_parser::init (const char *tidlist, int default_inferior)
126{
127  m_state = STATE_INFERIOR;
128  m_cur_tok = tidlist;
129  m_inf_num = 0;
130  m_qualified = false;
131  m_default_inferior = default_inferior;
132}
133
134/* See tid-parse.h.  */
135
136bool
137tid_range_parser::finished () const
138{
139  switch (m_state)
140    {
141    case STATE_INFERIOR:
142      /* Parsing is finished when at end of string or null string,
143	 or we are not in a range and not in front of an integer, negative
144	 integer, convenience var or negative convenience var.  */
145      return (*m_cur_tok == '\0'
146	      || !(isdigit (*m_cur_tok)
147		   || *m_cur_tok == '$'
148		   || *m_cur_tok == '*'));
149    case STATE_THREAD_RANGE:
150    case STATE_STAR_RANGE:
151      return m_range_parser.finished ();
152    }
153
154  gdb_assert_not_reached (_("unhandled state"));
155}
156
157/* See tid-parse.h.  */
158
159const char *
160tid_range_parser::cur_tok () const
161{
162  switch (m_state)
163    {
164    case STATE_INFERIOR:
165      return m_cur_tok;
166    case STATE_THREAD_RANGE:
167    case STATE_STAR_RANGE:
168      return m_range_parser.cur_tok ();
169    }
170
171  gdb_assert_not_reached (_("unhandled state"));
172}
173
174void
175tid_range_parser::skip_range ()
176{
177  gdb_assert (m_state == STATE_THREAD_RANGE
178	      || m_state == STATE_STAR_RANGE);
179
180  m_range_parser.skip_range ();
181  init (m_range_parser.cur_tok (), m_default_inferior);
182}
183
184/* See tid-parse.h.  */
185
186bool
187tid_range_parser::tid_is_qualified () const
188{
189  return m_qualified;
190}
191
192/* Helper for tid_range_parser::get_tid and
193   tid_range_parser::get_tid_range.  Return the next range if THR_END
194   is non-NULL, return a single thread ID otherwise.  */
195
196bool
197tid_range_parser::get_tid_or_range (int *inf_num,
198				    int *thr_start, int *thr_end)
199{
200  if (m_state == STATE_INFERIOR)
201    {
202      const char *p;
203      const char *space;
204
205      space = skip_to_space (m_cur_tok);
206
207      p = m_cur_tok;
208      while (p < space && *p != '.')
209	p++;
210      if (p < space)
211	{
212	  const char *dot = p;
213
214	  /* Parse number to the left of the dot.  */
215	  p = m_cur_tok;
216	  m_inf_num = get_positive_number_trailer (&p, '.', m_cur_tok);
217	  if (m_inf_num == 0)
218	    return 0;
219
220	  m_qualified = true;
221	  p = dot + 1;
222
223	  if (isspace (*p))
224	    return false;
225	}
226      else
227	{
228	  m_inf_num = m_default_inferior;
229	  m_qualified = false;
230	  p = m_cur_tok;
231	}
232
233      m_range_parser.init (p);
234      if (p[0] == '*' && (p[1] == '\0' || isspace (p[1])))
235	{
236	  /* Setup the number range parser to return numbers in the
237	     whole [1,INT_MAX] range.  */
238	  m_range_parser.setup_range (1, INT_MAX, skip_spaces (p + 1));
239	  m_state = STATE_STAR_RANGE;
240	}
241      else
242	m_state = STATE_THREAD_RANGE;
243    }
244
245  *inf_num = m_inf_num;
246  *thr_start = m_range_parser.get_number ();
247  if (*thr_start < 0)
248    error (_("negative value: %s"), m_cur_tok);
249  if (*thr_start == 0)
250    {
251      m_state = STATE_INFERIOR;
252      return false;
253    }
254
255  /* If we successfully parsed a thread number or finished parsing a
256     thread range, switch back to assuming the next TID is
257     inferior-qualified.  */
258  if (!m_range_parser.in_range ())
259    {
260      m_state = STATE_INFERIOR;
261      m_cur_tok = m_range_parser.cur_tok ();
262
263      if (thr_end != NULL)
264	*thr_end = *thr_start;
265    }
266
267  /* If we're midway through a range, and the caller wants the end
268     value, return it and skip to the end of the range.  */
269  if (thr_end != NULL
270      && (m_state == STATE_THREAD_RANGE
271	  || m_state == STATE_STAR_RANGE))
272    {
273      *thr_end = m_range_parser.end_value ();
274
275      skip_range ();
276    }
277
278  return (*inf_num != 0 && *thr_start != 0);
279}
280
281/* See tid-parse.h.  */
282
283bool
284tid_range_parser::get_tid_range (int *inf_num,
285				 int *thr_start, int *thr_end)
286{
287  gdb_assert (inf_num != NULL && thr_start != NULL && thr_end != NULL);
288
289  return get_tid_or_range (inf_num, thr_start, thr_end);
290}
291
292/* See tid-parse.h.  */
293
294bool
295tid_range_parser::get_tid (int *inf_num, int *thr_num)
296{
297  gdb_assert (inf_num != NULL && thr_num != NULL);
298
299  return get_tid_or_range (inf_num, thr_num, NULL);
300}
301
302/* See tid-parse.h.  */
303
304bool
305tid_range_parser::in_star_range () const
306{
307  return m_state == STATE_STAR_RANGE;
308}
309
310bool
311tid_range_parser::in_thread_range () const
312{
313  return m_state == STATE_THREAD_RANGE;
314}
315
316/* See tid-parse.h.  */
317
318int
319tid_is_in_list (const char *list, int default_inferior,
320		int inf_num, int thr_num)
321{
322  if (list == NULL || *list == '\0')
323    return 1;
324
325  tid_range_parser parser (list, default_inferior);
326  if (parser.finished ())
327    invalid_thread_id_error (parser.cur_tok ());
328  while (!parser.finished ())
329    {
330      int tmp_inf, tmp_thr_start, tmp_thr_end;
331
332      if (!parser.get_tid_range (&tmp_inf, &tmp_thr_start, &tmp_thr_end))
333	invalid_thread_id_error (parser.cur_tok ());
334      if (tmp_inf == inf_num
335	  && tmp_thr_start <= thr_num && thr_num <= tmp_thr_end)
336	return 1;
337    }
338  return 0;
339}
340