1//===-- OptionGroupFormat.cpp -----------------------------------*- C++ -*-===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8
9#include "lldb/Interpreter/OptionGroupFormat.h"
10
11#include "lldb/Host/OptionParser.h"
12#include "lldb/Interpreter/CommandInterpreter.h"
13#include "lldb/Target/ExecutionContext.h"
14#include "lldb/Target/Target.h"
15
16using namespace lldb;
17using namespace lldb_private;
18
19OptionGroupFormat::OptionGroupFormat(lldb::Format default_format,
20                                     uint64_t default_byte_size,
21                                     uint64_t default_count)
22    : m_format(default_format, default_format),
23      m_byte_size(default_byte_size, default_byte_size),
24      m_count(default_count, default_count), m_prev_gdb_format('x'),
25      m_prev_gdb_size('w') {}
26
27OptionGroupFormat::~OptionGroupFormat() {}
28
29static constexpr OptionDefinition g_option_table[] = {
30    {LLDB_OPT_SET_1, false, "format", 'f', OptionParser::eRequiredArgument,
31     nullptr, {}, 0, eArgTypeFormat,
32     "Specify a format to be used for display."},
33    {LLDB_OPT_SET_2, false, "gdb-format", 'G', OptionParser::eRequiredArgument,
34     nullptr, {}, 0, eArgTypeGDBFormat,
35     "Specify a format using a GDB format specifier string."},
36    {LLDB_OPT_SET_3, false, "size", 's', OptionParser::eRequiredArgument,
37     nullptr, {}, 0, eArgTypeByteSize,
38     "The size in bytes to use when displaying with the selected format."},
39    {LLDB_OPT_SET_4, false, "count", 'c', OptionParser::eRequiredArgument,
40     nullptr, {}, 0, eArgTypeCount,
41     "The number of total items to display."},
42};
43
44llvm::ArrayRef<OptionDefinition> OptionGroupFormat::GetDefinitions() {
45  auto result = llvm::makeArrayRef(g_option_table);
46  if (m_byte_size.GetDefaultValue() < UINT64_MAX) {
47    if (m_count.GetDefaultValue() < UINT64_MAX)
48      return result;
49    else
50      return result.take_front(3);
51  }
52  return result.take_front(2);
53}
54
55Status OptionGroupFormat::SetOptionValue(uint32_t option_idx,
56                                         llvm::StringRef option_arg,
57                                         ExecutionContext *execution_context) {
58  Status error;
59  const int short_option = g_option_table[option_idx].short_option;
60
61  switch (short_option) {
62  case 'f':
63    error = m_format.SetValueFromString(option_arg);
64    break;
65
66  case 'c':
67    if (m_count.GetDefaultValue() == 0) {
68      error.SetErrorString("--count option is disabled");
69    } else {
70      error = m_count.SetValueFromString(option_arg);
71      if (m_count.GetCurrentValue() == 0)
72        error.SetErrorStringWithFormat("invalid --count option value '%s'",
73                                       option_arg.str().c_str());
74    }
75    break;
76
77  case 's':
78    if (m_byte_size.GetDefaultValue() == 0) {
79      error.SetErrorString("--size option is disabled");
80    } else {
81      error = m_byte_size.SetValueFromString(option_arg);
82      if (m_byte_size.GetCurrentValue() == 0)
83        error.SetErrorStringWithFormat("invalid --size option value '%s'",
84                                       option_arg.str().c_str());
85    }
86    break;
87
88  case 'G': {
89    uint64_t count = 0;
90    llvm::StringRef gdb_format_str = option_arg;
91    gdb_format_str.consumeInteger(0, count);
92
93    Format format = eFormatDefault;
94    uint32_t byte_size = 0;
95
96    while (!gdb_format_str.empty() &&
97           ParserGDBFormatLetter(execution_context, gdb_format_str[0], format,
98                                 byte_size)) {
99      gdb_format_str = gdb_format_str.drop_front();
100    }
101
102    // We the first character of the "gdb_format_str" is not the
103    // NULL terminator, we didn't consume the entire string and
104    // something is wrong. Also, if none of the format, size or count was
105    // specified correctly, then abort.
106    if (!gdb_format_str.empty() ||
107        (format == eFormatInvalid && byte_size == 0 && count == 0)) {
108      // Nothing got set correctly
109      error.SetErrorStringWithFormat("invalid gdb format string '%s'",
110                                     option_arg.str().c_str());
111      return error;
112    }
113
114    // At least one of the format, size or count was set correctly. Anything
115    // that wasn't set correctly should be set to the previous default
116    if (format == eFormatInvalid)
117      ParserGDBFormatLetter(execution_context, m_prev_gdb_format, format,
118                            byte_size);
119
120    const bool byte_size_enabled = m_byte_size.GetDefaultValue() < UINT64_MAX;
121    const bool count_enabled = m_count.GetDefaultValue() < UINT64_MAX;
122    if (byte_size_enabled) {
123      // Byte size is enabled
124      if (byte_size == 0)
125        ParserGDBFormatLetter(execution_context, m_prev_gdb_size, format,
126                              byte_size);
127    } else {
128      // Byte size is disabled, make sure it wasn't specified but if this is an
129      // address, it's actually necessary to specify one so don't error out
130      if (byte_size > 0 && format != lldb::eFormatAddressInfo) {
131        error.SetErrorString(
132            "this command doesn't support specifying a byte size");
133        return error;
134      }
135    }
136
137    if (count_enabled) {
138      // Count is enabled and was not set, set it to the default for gdb format
139      // statements (which is 1).
140      if (count == 0)
141        count = 1;
142    } else {
143      // Count is disabled, make sure it wasn't specified
144      if (count > 0) {
145        error.SetErrorString("this command doesn't support specifying a count");
146        return error;
147      }
148    }
149
150    m_format.SetCurrentValue(format);
151    m_format.SetOptionWasSet();
152    if (byte_size_enabled) {
153      m_byte_size.SetCurrentValue(byte_size);
154      m_byte_size.SetOptionWasSet();
155    }
156    if (count_enabled) {
157      m_count.SetCurrentValue(count);
158      m_count.SetOptionWasSet();
159    }
160  } break;
161
162  default:
163    llvm_unreachable("Unimplemented option");
164  }
165
166  return error;
167}
168
169bool OptionGroupFormat::ParserGDBFormatLetter(
170    ExecutionContext *execution_context, char format_letter, Format &format,
171    uint32_t &byte_size) {
172  m_has_gdb_format = true;
173  switch (format_letter) {
174  case 'o':
175    format = eFormatOctal;
176    m_prev_gdb_format = format_letter;
177    return true;
178  case 'x':
179    format = eFormatHex;
180    m_prev_gdb_format = format_letter;
181    return true;
182  case 'd':
183    format = eFormatDecimal;
184    m_prev_gdb_format = format_letter;
185    return true;
186  case 'u':
187    format = eFormatUnsigned;
188    m_prev_gdb_format = format_letter;
189    return true;
190  case 't':
191    format = eFormatBinary;
192    m_prev_gdb_format = format_letter;
193    return true;
194  case 'f':
195    format = eFormatFloat;
196    m_prev_gdb_format = format_letter;
197    return true;
198  case 'a':
199    format = eFormatAddressInfo;
200    {
201      TargetSP target_sp =
202          execution_context ? execution_context->GetTargetSP() : TargetSP();
203      if (target_sp)
204        byte_size = target_sp->GetArchitecture().GetAddressByteSize();
205      m_prev_gdb_format = format_letter;
206      return true;
207    }
208  case 'i':
209    format = eFormatInstruction;
210    m_prev_gdb_format = format_letter;
211    return true;
212  case 'c':
213    format = eFormatChar;
214    m_prev_gdb_format = format_letter;
215    return true;
216  case 's':
217    format = eFormatCString;
218    m_prev_gdb_format = format_letter;
219    return true;
220  case 'T':
221    format = eFormatOSType;
222    m_prev_gdb_format = format_letter;
223    return true;
224  case 'A':
225    format = eFormatHexFloat;
226    m_prev_gdb_format = format_letter;
227    return true;
228
229  case 'b':
230  case 'h':
231  case 'w':
232  case 'g':
233    {
234      // Size isn't used for printing instructions, so if a size is specified,
235      // and the previous format was 'i', then we should reset it to the
236      // default ('x').  Otherwise we'll continue to print as instructions,
237      // which isn't expected.
238      if (format_letter == 'b')
239          byte_size = 1;
240      else if (format_letter == 'h')
241          byte_size = 2;
242      else if (format_letter == 'w')
243          byte_size = 4;
244      else if (format_letter == 'g')
245          byte_size = 8;
246
247        m_prev_gdb_size = format_letter;
248        if (m_prev_gdb_format == 'i')
249          m_prev_gdb_format = 'x';
250        return true;
251    }
252    break;
253  default:
254    break;
255  }
256
257
258  return false;
259}
260
261void OptionGroupFormat::OptionParsingStarting(
262    ExecutionContext *execution_context) {
263  m_format.Clear();
264  m_byte_size.Clear();
265  m_count.Clear();
266  m_has_gdb_format = false;
267}
268