blob: 4a04a4639fb8f810614fd55c2d986a8aaf895206 [file] [log] [blame]
Chris Lattner24943d22010-06-08 16:52:24 +00001//===-- Options.cpp ---------------------------------------------*- C++ -*-===//
2//
3// The LLVM Compiler Infrastructure
4//
5// This file is distributed under the University of Illinois Open Source
6// License. See LICENSE.TXT for details.
7//
8//===----------------------------------------------------------------------===//
9
Jim Ingham84cdc152010-06-15 19:49:27 +000010#include "lldb/Interpreter/Options.h"
Chris Lattner24943d22010-06-08 16:52:24 +000011
12// C Includes
13// C++ Includes
Caroline Ticee5f18b02010-09-09 16:44:14 +000014#include <algorithm>
Greg Clayton5e342f52011-04-13 22:47:15 +000015#include <bitset>
16#include <set>
Chris Lattner24943d22010-06-08 16:52:24 +000017
18// Other libraries and framework includes
19// Project includes
20#include "lldb/Interpreter/CommandObject.h"
21#include "lldb/Interpreter/CommandReturnObject.h"
22#include "lldb/Interpreter/CommandCompletions.h"
23#include "lldb/Interpreter/CommandInterpreter.h"
24#include "lldb/Core/StreamString.h"
25#include "lldb/Target/Target.h"
26
27using namespace lldb;
28using namespace lldb_private;
29
30//-------------------------------------------------------------------------
31// Options
32//-------------------------------------------------------------------------
Greg Claytonf15996e2011-04-07 22:46:35 +000033Options::Options (CommandInterpreter &interpreter) :
34 m_interpreter (interpreter),
Chris Lattner24943d22010-06-08 16:52:24 +000035 m_getopt_table ()
36{
Jim Ingham34e9a982010-06-15 18:47:14 +000037 BuildValidOptionSets();
Chris Lattner24943d22010-06-08 16:52:24 +000038}
39
40Options::~Options ()
41{
42}
43
Chris Lattner24943d22010-06-08 16:52:24 +000044void
Greg Clayton143fcc32011-04-13 00:18:08 +000045Options::NotifyOptionParsingStarting ()
Chris Lattner24943d22010-06-08 16:52:24 +000046{
47 m_seen_options.clear();
Greg Clayton24bc5d92011-03-30 18:16:51 +000048 // Let the subclass reset its option values
Greg Clayton143fcc32011-04-13 00:18:08 +000049 OptionParsingStarting ();
50}
51
52Error
53Options::NotifyOptionParsingFinished ()
54{
55 return OptionParsingFinished ();
Chris Lattner24943d22010-06-08 16:52:24 +000056}
57
58void
59Options::OptionSeen (int option_idx)
60{
61 m_seen_options.insert ((char) option_idx);
62}
63
64// Returns true is set_a is a subset of set_b; Otherwise returns false.
65
66bool
67Options::IsASubset (const OptionSet& set_a, const OptionSet& set_b)
68{
69 bool is_a_subset = true;
70 OptionSet::const_iterator pos_a;
71 OptionSet::const_iterator pos_b;
72
73 // set_a is a subset of set_b if every member of set_a is also a member of set_b
74
75 for (pos_a = set_a.begin(); pos_a != set_a.end() && is_a_subset; ++pos_a)
76 {
77 pos_b = set_b.find(*pos_a);
78 if (pos_b == set_b.end())
79 is_a_subset = false;
80 }
81
82 return is_a_subset;
83}
84
85// Returns the set difference set_a - set_b, i.e. { x | ElementOf (x, set_a) && !ElementOf (x, set_b) }
86
87size_t
88Options::OptionsSetDiff (const OptionSet& set_a, const OptionSet& set_b, OptionSet& diffs)
89{
90 size_t num_diffs = 0;
91 OptionSet::const_iterator pos_a;
92 OptionSet::const_iterator pos_b;
93
94 for (pos_a = set_a.begin(); pos_a != set_a.end(); ++pos_a)
95 {
96 pos_b = set_b.find(*pos_a);
97 if (pos_b == set_b.end())
98 {
99 ++num_diffs;
100 diffs.insert(*pos_a);
101 }
102 }
103
104 return num_diffs;
105}
106
107// Returns the union of set_a and set_b. Does not put duplicate members into the union.
108
109void
110Options::OptionsSetUnion (const OptionSet &set_a, const OptionSet &set_b, OptionSet &union_set)
111{
112 OptionSet::const_iterator pos;
113 OptionSet::iterator pos_union;
114
115 // Put all the elements of set_a into the union.
116
117 for (pos = set_a.begin(); pos != set_a.end(); ++pos)
118 union_set.insert(*pos);
119
120 // Put all the elements of set_b that are not already there into the union.
121 for (pos = set_b.begin(); pos != set_b.end(); ++pos)
122 {
123 pos_union = union_set.find(*pos);
124 if (pos_union == union_set.end())
125 union_set.insert(*pos);
126 }
127}
128
129bool
130Options::VerifyOptions (CommandReturnObject &result)
131{
132 bool options_are_valid = false;
133
Jim Ingham8b9af1c2010-06-24 20:30:15 +0000134 int num_levels = GetRequiredOptions().size();
Chris Lattner24943d22010-06-08 16:52:24 +0000135 if (num_levels)
136 {
137 for (int i = 0; i < num_levels && !options_are_valid; ++i)
138 {
139 // This is the correct set of options if: 1). m_seen_options contains all of m_required_options[i]
140 // (i.e. all the required options at this level are a subset of m_seen_options); AND
141 // 2). { m_seen_options - m_required_options[i] is a subset of m_options_options[i] (i.e. all the rest of
142 // m_seen_options are in the set of optional options at this level.
143
144 // Check to see if all of m_required_options[i] are a subset of m_seen_options
Jim Ingham8b9af1c2010-06-24 20:30:15 +0000145 if (IsASubset (GetRequiredOptions()[i], m_seen_options))
Chris Lattner24943d22010-06-08 16:52:24 +0000146 {
147 // Construct the set difference: remaining_options = {m_seen_options} - {m_required_options[i]}
148 OptionSet remaining_options;
Jim Ingham8b9af1c2010-06-24 20:30:15 +0000149 OptionsSetDiff (m_seen_options, GetRequiredOptions()[i], remaining_options);
Chris Lattner24943d22010-06-08 16:52:24 +0000150 // Check to see if remaining_options is a subset of m_optional_options[i]
Jim Ingham8b9af1c2010-06-24 20:30:15 +0000151 if (IsASubset (remaining_options, GetOptionalOptions()[i]))
Chris Lattner24943d22010-06-08 16:52:24 +0000152 options_are_valid = true;
153 }
154 }
155 }
156 else
157 {
158 options_are_valid = true;
159 }
160
161 if (options_are_valid)
162 {
163 result.SetStatus (eReturnStatusSuccessFinishNoResult);
164 }
165 else
166 {
167 result.AppendError ("invalid combination of options for the given command");
168 result.SetStatus (eReturnStatusFailed);
169 }
170
171 return options_are_valid;
172}
173
Jim Ingham34e9a982010-06-15 18:47:14 +0000174// This is called in the Options constructor, though we could call it lazily if that ends up being
175// a performance problem.
176
Chris Lattner24943d22010-06-08 16:52:24 +0000177void
178Options::BuildValidOptionSets ()
179{
180 // Check to see if we already did this.
181 if (m_required_options.size() != 0)
182 return;
183
184 // Check to see if there are any options.
185 int num_options = NumCommandOptions ();
186 if (num_options == 0)
187 return;
188
Greg Claytond8a218d2011-10-29 00:57:28 +0000189 const OptionDefinition *opt_defs = GetDefinitions();
Chris Lattner24943d22010-06-08 16:52:24 +0000190 m_required_options.resize(1);
191 m_optional_options.resize(1);
Jim Ingham34e9a982010-06-15 18:47:14 +0000192
193 // First count the number of option sets we've got. Ignore LLDB_ALL_OPTION_SETS...
194
195 uint32_t num_option_sets = 0;
196
197 for (int i = 0; i < num_options; i++)
Chris Lattner24943d22010-06-08 16:52:24 +0000198 {
Greg Claytond8a218d2011-10-29 00:57:28 +0000199 uint32_t this_usage_mask = opt_defs[i].usage_mask;
Jim Ingham34e9a982010-06-15 18:47:14 +0000200 if (this_usage_mask == LLDB_OPT_SET_ALL)
Chris Lattner24943d22010-06-08 16:52:24 +0000201 {
Jim Ingham34e9a982010-06-15 18:47:14 +0000202 if (num_option_sets == 0)
203 num_option_sets = 1;
Chris Lattner24943d22010-06-08 16:52:24 +0000204 }
205 else
206 {
Jim Ingham34e9a982010-06-15 18:47:14 +0000207 for (int j = 0; j < LLDB_MAX_NUM_OPTION_SETS; j++)
208 {
Jim Ingham8b9af1c2010-06-24 20:30:15 +0000209 if (this_usage_mask & (1 << j))
Jim Ingham34e9a982010-06-15 18:47:14 +0000210 {
211 if (num_option_sets <= j)
212 num_option_sets = j + 1;
213 }
214 }
Chris Lattner24943d22010-06-08 16:52:24 +0000215 }
Jim Ingham34e9a982010-06-15 18:47:14 +0000216 }
Chris Lattner24943d22010-06-08 16:52:24 +0000217
Jim Ingham34e9a982010-06-15 18:47:14 +0000218 if (num_option_sets > 0)
219 {
220 m_required_options.resize(num_option_sets);
221 m_optional_options.resize(num_option_sets);
222
223 for (int i = 0; i < num_options; ++i)
224 {
225 for (int j = 0; j < num_option_sets; j++)
226 {
Greg Claytond8a218d2011-10-29 00:57:28 +0000227 if (opt_defs[i].usage_mask & 1 << j)
Jim Ingham34e9a982010-06-15 18:47:14 +0000228 {
Greg Claytond8a218d2011-10-29 00:57:28 +0000229 if (opt_defs[i].required)
230 m_required_options[j].insert(opt_defs[i].short_option);
Jim Ingham34e9a982010-06-15 18:47:14 +0000231 else
Greg Claytond8a218d2011-10-29 00:57:28 +0000232 m_optional_options[j].insert(opt_defs[i].short_option);
Jim Ingham34e9a982010-06-15 18:47:14 +0000233 }
234 }
235 }
Chris Lattner24943d22010-06-08 16:52:24 +0000236 }
237}
238
239uint32_t
240Options::NumCommandOptions ()
241{
Greg Claytond8a218d2011-10-29 00:57:28 +0000242 const OptionDefinition *opt_defs = GetDefinitions ();
243 if (opt_defs == NULL)
Jim Ingham34e9a982010-06-15 18:47:14 +0000244 return 0;
245
Chris Lattner24943d22010-06-08 16:52:24 +0000246 int i = 0;
247
Greg Claytond8a218d2011-10-29 00:57:28 +0000248 if (opt_defs != NULL)
Chris Lattner24943d22010-06-08 16:52:24 +0000249 {
Greg Claytond8a218d2011-10-29 00:57:28 +0000250 while (opt_defs[i].long_option != NULL)
Chris Lattner24943d22010-06-08 16:52:24 +0000251 ++i;
252 }
253
254 return i;
255}
256
257struct option *
258Options::GetLongOptions ()
259{
260 // Check to see if this has already been done.
261 if (m_getopt_table.empty())
262 {
263 // Check to see if there are any options.
264 const uint32_t num_options = NumCommandOptions();
265 if (num_options == 0)
266 return NULL;
267
268 uint32_t i;
269 uint32_t j;
Greg Claytond8a218d2011-10-29 00:57:28 +0000270 const OptionDefinition *opt_defs = GetDefinitions();
Chris Lattner24943d22010-06-08 16:52:24 +0000271
272 std::bitset<256> option_seen;
273
274 m_getopt_table.resize(num_options + 1);
275 for (i = 0, j = 0; i < num_options; ++i)
276 {
Greg Claytonec9c2d22012-11-30 19:05:35 +0000277 const char short_opt = opt_defs[i].short_option;
Chris Lattner24943d22010-06-08 16:52:24 +0000278
279 if (option_seen.test(short_opt) == false)
280 {
Greg Claytond8a218d2011-10-29 00:57:28 +0000281 m_getopt_table[j].name = opt_defs[i].long_option;
282 m_getopt_table[j].has_arg = opt_defs[i].option_has_arg;
Chris Lattner24943d22010-06-08 16:52:24 +0000283 m_getopt_table[j].flag = NULL;
Greg Claytonec9c2d22012-11-30 19:05:35 +0000284 m_getopt_table[j].val = short_opt;
Chris Lattner24943d22010-06-08 16:52:24 +0000285 option_seen.set(short_opt);
286 ++j;
287 }
Greg Claytonec9c2d22012-11-30 19:05:35 +0000288 else
289 {
290 assert (!"duplicate short option character");
291 }
Chris Lattner24943d22010-06-08 16:52:24 +0000292 }
293
294 //getopt_long requires a NULL final entry in the table:
295
296 m_getopt_table[j].name = NULL;
297 m_getopt_table[j].has_arg = 0;
298 m_getopt_table[j].flag = NULL;
299 m_getopt_table[j].val = 0;
300 }
301
Greg Clayton53d68e72010-07-20 22:52:08 +0000302 if (m_getopt_table.empty())
303 return NULL;
304
305 return &m_getopt_table.front();
Chris Lattner24943d22010-06-08 16:52:24 +0000306}
307
308
309// This function takes INDENT, which tells how many spaces to output at the front of each line; SPACES, which is
310// a string containing 80 spaces; and TEXT, which is the text that is to be output. It outputs the text, on
311// multiple lines if necessary, to RESULT, with INDENT spaces at the front of each line. It breaks lines on spaces,
312// tabs or newlines, shortening the line if necessary to not break in the middle of a word. It assumes that each
313// output line should contain a maximum of OUTPUT_MAX_COLUMNS characters.
314
315
316void
317Options::OutputFormattedUsageText
318(
319 Stream &strm,
320 const char *text,
321 uint32_t output_max_columns
322)
323{
324 int len = strlen (text);
325
326 // Will it all fit on one line?
327
328 if ((len + strm.GetIndentLevel()) < output_max_columns)
329 {
330 // Output it as a single line.
331 strm.Indent (text);
332 strm.EOL();
333 }
334 else
335 {
336 // We need to break it up into multiple lines.
337
338 int text_width = output_max_columns - strm.GetIndentLevel() - 1;
339 int start = 0;
340 int end = start;
341 int final_end = strlen (text);
342 int sub_len;
343
344 while (end < final_end)
345 {
346 // Don't start the 'text' on a space, since we're already outputting the indentation.
347 while ((start < final_end) && (text[start] == ' '))
348 start++;
349
350 end = start + text_width;
351 if (end > final_end)
352 end = final_end;
353 else
354 {
355 // If we're not at the end of the text, make sure we break the line on white space.
356 while (end > start
357 && text[end] != ' ' && text[end] != '\t' && text[end] != '\n')
358 end--;
359 }
360
361 sub_len = end - start;
362 if (start != 0)
363 strm.EOL();
364 strm.Indent();
365 assert (start < final_end);
366 assert (start + sub_len <= final_end);
367 strm.Write(text + start, sub_len);
368 start = end + 1;
369 }
370 strm.EOL();
371 }
372}
373
Greg Claytond8a218d2011-10-29 00:57:28 +0000374bool
375Options::SupportsLongOption (const char *long_option)
376{
377 if (long_option && long_option[0])
378 {
379 const OptionDefinition *opt_defs = GetDefinitions ();
380 if (opt_defs)
381 {
Greg Claytonb5169392011-10-31 23:51:19 +0000382 const char *long_option_name = long_option;
Greg Claytond8a218d2011-10-29 00:57:28 +0000383 if (long_option[0] == '-' && long_option[1] == '-')
384 long_option_name += 2;
Greg Claytond8a218d2011-10-29 00:57:28 +0000385
386 for (uint32_t i = 0; opt_defs[i].long_option; ++i)
387 {
388 if (strcmp(opt_defs[i].long_option, long_option_name) == 0)
389 return true;
390 }
391 }
392 }
393 return false;
394}
395
Chris Lattner24943d22010-06-08 16:52:24 +0000396void
397Options::GenerateOptionUsage
398(
399 Stream &strm,
Greg Clayton238c0a12010-09-18 01:14:36 +0000400 CommandObject *cmd
401)
Chris Lattner24943d22010-06-08 16:52:24 +0000402{
Greg Claytonf15996e2011-04-07 22:46:35 +0000403 const uint32_t screen_width = m_interpreter.GetDebugger().GetTerminalWidth();
Caroline Tice6e4c5ce2010-09-04 00:03:46 +0000404
Greg Claytond8a218d2011-10-29 00:57:28 +0000405 const OptionDefinition *opt_defs = GetDefinitions();
Chris Lattner24943d22010-06-08 16:52:24 +0000406 const uint32_t save_indent_level = strm.GetIndentLevel();
407 const char *name;
408
Caroline Ticefb355112010-10-01 17:46:38 +0000409 StreamString arguments_str;
410
Chris Lattner24943d22010-06-08 16:52:24 +0000411 if (cmd)
Caroline Ticefb355112010-10-01 17:46:38 +0000412 {
Greg Clayton238c0a12010-09-18 01:14:36 +0000413 name = cmd->GetCommandName();
Caroline Ticefb355112010-10-01 17:46:38 +0000414 cmd->GetFormattedCommandArguments (arguments_str);
415 }
Chris Lattner24943d22010-06-08 16:52:24 +0000416 else
Greg Clayton238c0a12010-09-18 01:14:36 +0000417 name = "";
Chris Lattner24943d22010-06-08 16:52:24 +0000418
419 strm.PutCString ("\nCommand Options Usage:\n");
420
421 strm.IndentMore(2);
422
423 // First, show each usage level set of options, e.g. <cmd> [options-for-level-0]
424 // <cmd> [options-for-level-1]
425 // etc.
426
Chris Lattner24943d22010-06-08 16:52:24 +0000427 const uint32_t num_options = NumCommandOptions();
Jim Ingham34e9a982010-06-15 18:47:14 +0000428 if (num_options == 0)
429 return;
430
Jim Ingham8b9af1c2010-06-24 20:30:15 +0000431 int num_option_sets = GetRequiredOptions().size();
Jim Ingham34e9a982010-06-15 18:47:14 +0000432
Chris Lattner24943d22010-06-08 16:52:24 +0000433 uint32_t i;
Jim Ingham34e9a982010-06-15 18:47:14 +0000434
435 for (uint32_t opt_set = 0; opt_set < num_option_sets; ++opt_set)
Chris Lattner24943d22010-06-08 16:52:24 +0000436 {
Jim Ingham34e9a982010-06-15 18:47:14 +0000437 uint32_t opt_set_mask;
438
439 opt_set_mask = 1 << opt_set;
440 if (opt_set > 0)
441 strm.Printf ("\n");
442 strm.Indent (name);
Caroline Ticee5f18b02010-09-09 16:44:14 +0000443
Johnny Chen6183fcc2012-02-08 01:13:31 +0000444 // Different option sets may require different args.
445 StreamString args_str;
Jim Ingham6f01c932012-10-12 17:34:26 +0000446 if (cmd)
447 cmd->GetFormattedCommandArguments(args_str, opt_set_mask);
Johnny Chen6183fcc2012-02-08 01:13:31 +0000448
Greg Claytonfe424a92010-09-18 03:37:20 +0000449 // First go through and print all options that take no arguments as
450 // a single string. If a command has "-a" "-b" and "-c", this will show
451 // up as [-abc]
452
453 std::set<char> options;
454 std::set<char>::const_iterator options_pos, options_end;
455 bool first;
456 for (i = 0, first = true; i < num_options; ++i)
457 {
Greg Claytond8a218d2011-10-29 00:57:28 +0000458 if (opt_defs[i].usage_mask & opt_set_mask)
Greg Claytonfe424a92010-09-18 03:37:20 +0000459 {
460 // Add current option to the end of out_stream.
461
Greg Claytond8a218d2011-10-29 00:57:28 +0000462 if (opt_defs[i].required == true &&
463 opt_defs[i].option_has_arg == no_argument)
Greg Claytonfe424a92010-09-18 03:37:20 +0000464 {
Greg Claytond8a218d2011-10-29 00:57:28 +0000465 options.insert (opt_defs[i].short_option);
Greg Claytonfe424a92010-09-18 03:37:20 +0000466 }
467 }
468 }
469
470 if (options.empty() == false)
471 {
472 // We have some required options with no arguments
473 strm.PutCString(" -");
474 for (i=0; i<2; ++i)
475 for (options_pos = options.begin(), options_end = options.end();
476 options_pos != options_end;
477 ++options_pos)
478 {
479 if (i==0 && ::isupper (*options_pos))
480 continue;
481 if (i==1 && ::islower (*options_pos))
482 continue;
483 strm << *options_pos;
484 }
485 }
486
487 for (i = 0, options.clear(); i < num_options; ++i)
488 {
Greg Claytond8a218d2011-10-29 00:57:28 +0000489 if (opt_defs[i].usage_mask & opt_set_mask)
Greg Claytonfe424a92010-09-18 03:37:20 +0000490 {
491 // Add current option to the end of out_stream.
492
Greg Claytond8a218d2011-10-29 00:57:28 +0000493 if (opt_defs[i].required == false &&
494 opt_defs[i].option_has_arg == no_argument)
Greg Claytonfe424a92010-09-18 03:37:20 +0000495 {
Greg Claytond8a218d2011-10-29 00:57:28 +0000496 options.insert (opt_defs[i].short_option);
Greg Claytonfe424a92010-09-18 03:37:20 +0000497 }
498 }
499 }
500
501 if (options.empty() == false)
502 {
503 // We have some required options with no arguments
504 strm.PutCString(" [-");
505 for (i=0; i<2; ++i)
506 for (options_pos = options.begin(), options_end = options.end();
507 options_pos != options_end;
508 ++options_pos)
509 {
510 if (i==0 && ::isupper (*options_pos))
511 continue;
512 if (i==1 && ::islower (*options_pos))
513 continue;
514 strm << *options_pos;
515 }
516 strm.PutChar(']');
517 }
518
Caroline Ticee5f18b02010-09-09 16:44:14 +0000519 // First go through and print the required options (list them up front).
Jim Ingham34e9a982010-06-15 18:47:14 +0000520
521 for (i = 0; i < num_options; ++i)
Chris Lattner24943d22010-06-08 16:52:24 +0000522 {
Greg Claytond8a218d2011-10-29 00:57:28 +0000523 if (opt_defs[i].usage_mask & opt_set_mask)
Chris Lattner24943d22010-06-08 16:52:24 +0000524 {
Jim Ingham34e9a982010-06-15 18:47:14 +0000525 // Add current option to the end of out_stream.
Greg Claytond8a218d2011-10-29 00:57:28 +0000526 CommandArgumentType arg_type = opt_defs[i].argument_type;
Caroline Tice4d6675c2010-10-01 19:59:14 +0000527
Greg Claytond8a218d2011-10-29 00:57:28 +0000528 if (opt_defs[i].required)
Jim Ingham34e9a982010-06-15 18:47:14 +0000529 {
Greg Claytond8a218d2011-10-29 00:57:28 +0000530 if (opt_defs[i].option_has_arg == required_argument)
Jim Ingham34e9a982010-06-15 18:47:14 +0000531 {
Caroline Tice4d6675c2010-10-01 19:59:14 +0000532 strm.Printf (" -%c <%s>",
Greg Claytond8a218d2011-10-29 00:57:28 +0000533 opt_defs[i].short_option,
Caroline Tice4d6675c2010-10-01 19:59:14 +0000534 CommandObject::GetArgumentName (arg_type));
Jim Ingham34e9a982010-06-15 18:47:14 +0000535 }
Greg Claytond8a218d2011-10-29 00:57:28 +0000536 else if (opt_defs[i].option_has_arg == optional_argument)
Jim Ingham34e9a982010-06-15 18:47:14 +0000537 {
Caroline Tice4d6675c2010-10-01 19:59:14 +0000538 strm.Printf (" -%c [<%s>]",
Greg Claytond8a218d2011-10-29 00:57:28 +0000539 opt_defs[i].short_option,
Caroline Tice4d6675c2010-10-01 19:59:14 +0000540 CommandObject::GetArgumentName (arg_type));
Jim Ingham34e9a982010-06-15 18:47:14 +0000541 }
Jim Ingham34e9a982010-06-15 18:47:14 +0000542 }
Caroline Ticee5f18b02010-09-09 16:44:14 +0000543 }
544 }
545
546 // Now go through again, and this time only print the optional options.
547
548 for (i = 0; i < num_options; ++i)
549 {
Greg Claytond8a218d2011-10-29 00:57:28 +0000550 if (opt_defs[i].usage_mask & opt_set_mask)
Caroline Ticee5f18b02010-09-09 16:44:14 +0000551 {
552 // Add current option to the end of out_stream.
553
Greg Claytond8a218d2011-10-29 00:57:28 +0000554 CommandArgumentType arg_type = opt_defs[i].argument_type;
Caroline Tice4d6675c2010-10-01 19:59:14 +0000555
Greg Claytond8a218d2011-10-29 00:57:28 +0000556 if (! opt_defs[i].required)
Jim Ingham34e9a982010-06-15 18:47:14 +0000557 {
Greg Claytond8a218d2011-10-29 00:57:28 +0000558 if (opt_defs[i].option_has_arg == required_argument)
559 strm.Printf (" [-%c <%s>]", opt_defs[i].short_option,
Caroline Tice4d6675c2010-10-01 19:59:14 +0000560 CommandObject::GetArgumentName (arg_type));
Greg Claytond8a218d2011-10-29 00:57:28 +0000561 else if (opt_defs[i].option_has_arg == optional_argument)
562 strm.Printf (" [-%c [<%s>]]", opt_defs[i].short_option,
Caroline Tice4d6675c2010-10-01 19:59:14 +0000563 CommandObject::GetArgumentName (arg_type));
Jim Ingham34e9a982010-06-15 18:47:14 +0000564 }
Chris Lattner24943d22010-06-08 16:52:24 +0000565 }
Chris Lattner24943d22010-06-08 16:52:24 +0000566 }
Sean Callanan9798d7b2012-01-04 19:11:25 +0000567
Johnny Chen6183fcc2012-02-08 01:13:31 +0000568 if (args_str.GetSize() > 0)
Sean Callanan9798d7b2012-01-04 19:11:25 +0000569 {
570 if (cmd->WantsRawCommandString())
571 strm.Printf(" --");
572
Johnny Chen6183fcc2012-02-08 01:13:31 +0000573 strm.Printf (" %s", args_str.GetData());
Sean Callanan9798d7b2012-01-04 19:11:25 +0000574 }
Chris Lattner24943d22010-06-08 16:52:24 +0000575 }
Sean Callanan9798d7b2012-01-04 19:11:25 +0000576
Jim Ingham6f01c932012-10-12 17:34:26 +0000577 if (cmd &&
578 cmd->WantsRawCommandString() &&
Sean Callanan9798d7b2012-01-04 19:11:25 +0000579 arguments_str.GetSize() > 0)
580 {
581 strm.PutChar('\n');
582 strm.Indent(name);
583 strm.Printf(" %s", arguments_str.GetData());
584 }
585
Chris Lattner24943d22010-06-08 16:52:24 +0000586 strm.Printf ("\n\n");
587
588 // Now print out all the detailed information about the various options: long form, short form and help text:
Greg Claytonfe424a92010-09-18 03:37:20 +0000589 // --long_name <argument> ( -short <argument> )
Chris Lattner24943d22010-06-08 16:52:24 +0000590 // help text
591
592 // This variable is used to keep track of which options' info we've printed out, because some options can be in
593 // more than one usage level, but we only want to print the long form of its information once.
594
595 OptionSet options_seen;
596 OptionSet::iterator pos;
597 strm.IndentMore (5);
598
Caroline Ticee5f18b02010-09-09 16:44:14 +0000599 std::vector<char> sorted_options;
600
601
602 // Put the unique command options in a vector & sort it, so we can output them alphabetically (by short_option)
603 // when writing out detailed help for each option.
604
Chris Lattner24943d22010-06-08 16:52:24 +0000605 for (i = 0; i < num_options; ++i)
606 {
Greg Claytond8a218d2011-10-29 00:57:28 +0000607 pos = options_seen.find (opt_defs[i].short_option);
Chris Lattner24943d22010-06-08 16:52:24 +0000608 if (pos == options_seen.end())
609 {
Greg Claytond8a218d2011-10-29 00:57:28 +0000610 options_seen.insert (opt_defs[i].short_option);
611 sorted_options.push_back (opt_defs[i].short_option);
Caroline Ticee5f18b02010-09-09 16:44:14 +0000612 }
613 }
Chris Lattner24943d22010-06-08 16:52:24 +0000614
Caroline Ticee5f18b02010-09-09 16:44:14 +0000615 std::sort (sorted_options.begin(), sorted_options.end());
616
617 // Go through the unique'd and alphabetically sorted vector of options, find the table entry for each option
618 // and write out the detailed help information for that option.
619
620 int first_option_printed = 1;
621 size_t end = sorted_options.size();
622 for (size_t j = 0; j < end; ++j)
623 {
624 char option = sorted_options[j];
625 bool found = false;
626 for (i = 0; i < num_options && !found; ++i)
627 {
Greg Claytond8a218d2011-10-29 00:57:28 +0000628 if (opt_defs[i].short_option == option)
Caroline Ticee5f18b02010-09-09 16:44:14 +0000629 {
630 found = true;
631 //Print out the help information for this option.
632
633 // Put a newline separation between arguments
634 if (first_option_printed)
635 first_option_printed = 0;
636 else
637 strm.EOL();
638
Greg Claytond8a218d2011-10-29 00:57:28 +0000639 CommandArgumentType arg_type = opt_defs[i].argument_type;
Caroline Tice4d6675c2010-10-01 19:59:14 +0000640
641 StreamString arg_name_str;
642 arg_name_str.Printf ("<%s>", CommandObject::GetArgumentName (arg_type));
643
Caroline Ticee5f18b02010-09-09 16:44:14 +0000644 strm.Indent ();
Greg Claytond8a218d2011-10-29 00:57:28 +0000645 strm.Printf ("-%c", opt_defs[i].short_option);
Caroline Tice4d6675c2010-10-01 19:59:14 +0000646 if (arg_type != eArgTypeNone)
647 strm.Printf (" <%s>", CommandObject::GetArgumentName (arg_type));
Greg Claytond8a218d2011-10-29 00:57:28 +0000648 strm.Printf (" ( --%s", opt_defs[i].long_option);
Caroline Tice4d6675c2010-10-01 19:59:14 +0000649 if (arg_type != eArgTypeNone)
650 strm.Printf (" <%s>", CommandObject::GetArgumentName (arg_type));
Greg Claytonfe424a92010-09-18 03:37:20 +0000651 strm.PutCString(" )\n");
Caroline Ticee5f18b02010-09-09 16:44:14 +0000652
653 strm.IndentMore (5);
654
Greg Claytond8a218d2011-10-29 00:57:28 +0000655 if (opt_defs[i].usage_text)
Chris Lattner24943d22010-06-08 16:52:24 +0000656 OutputFormattedUsageText (strm,
Greg Claytond8a218d2011-10-29 00:57:28 +0000657 opt_defs[i].usage_text,
Chris Lattner24943d22010-06-08 16:52:24 +0000658 screen_width);
Greg Claytond8a218d2011-10-29 00:57:28 +0000659 if (opt_defs[i].enum_values != NULL)
Chris Lattner24943d22010-06-08 16:52:24 +0000660 {
Caroline Ticee5f18b02010-09-09 16:44:14 +0000661 strm.Indent ();
662 strm.Printf("Values: ");
Greg Claytond8a218d2011-10-29 00:57:28 +0000663 for (int k = 0; opt_defs[i].enum_values[k].string_value != NULL; k++)
Caroline Ticee5f18b02010-09-09 16:44:14 +0000664 {
665 if (k == 0)
Greg Claytond8a218d2011-10-29 00:57:28 +0000666 strm.Printf("%s", opt_defs[i].enum_values[k].string_value);
Caroline Ticee5f18b02010-09-09 16:44:14 +0000667 else
Greg Claytond8a218d2011-10-29 00:57:28 +0000668 strm.Printf(" | %s", opt_defs[i].enum_values[k].string_value);
Caroline Ticee5f18b02010-09-09 16:44:14 +0000669 }
670 strm.EOL();
Chris Lattner24943d22010-06-08 16:52:24 +0000671 }
Caroline Ticee5f18b02010-09-09 16:44:14 +0000672 strm.IndentLess (5);
Chris Lattner24943d22010-06-08 16:52:24 +0000673 }
Chris Lattner24943d22010-06-08 16:52:24 +0000674 }
675 }
676
677 // Restore the indent level
678 strm.SetIndentLevel (save_indent_level);
679}
680
681// This function is called when we have been given a potentially incomplete set of
682// options, such as when an alias has been defined (more options might be added at
683// at the time the alias is invoked). We need to verify that the options in the set
684// m_seen_options are all part of a set that may be used together, but m_seen_options
685// may be missing some of the "required" options.
686
687bool
688Options::VerifyPartialOptions (CommandReturnObject &result)
689{
690 bool options_are_valid = false;
691
Jim Ingham8b9af1c2010-06-24 20:30:15 +0000692 int num_levels = GetRequiredOptions().size();
Chris Lattner24943d22010-06-08 16:52:24 +0000693 if (num_levels)
694 {
695 for (int i = 0; i < num_levels && !options_are_valid; ++i)
696 {
697 // In this case we are treating all options as optional rather than required.
698 // Therefore a set of options is correct if m_seen_options is a subset of the
699 // union of m_required_options and m_optional_options.
700 OptionSet union_set;
Jim Ingham8b9af1c2010-06-24 20:30:15 +0000701 OptionsSetUnion (GetRequiredOptions()[i], GetOptionalOptions()[i], union_set);
Chris Lattner24943d22010-06-08 16:52:24 +0000702 if (IsASubset (m_seen_options, union_set))
703 options_are_valid = true;
704 }
705 }
706
707 return options_are_valid;
708}
709
710bool
711Options::HandleOptionCompletion
712(
713 Args &input,
714 OptionElementVector &opt_element_vector,
715 int cursor_index,
716 int char_pos,
717 int match_start_point,
718 int max_return_elements,
Jim Ingham802f8b02010-06-30 05:02:46 +0000719 bool &word_complete,
Chris Lattner24943d22010-06-08 16:52:24 +0000720 lldb_private::StringList &matches
721)
722{
Jim Ingham802f8b02010-06-30 05:02:46 +0000723 word_complete = true;
724
Chris Lattner24943d22010-06-08 16:52:24 +0000725 // For now we just scan the completions to see if the cursor position is in
726 // an option or its argument. Otherwise we'll call HandleArgumentCompletion.
727 // In the future we can use completion to validate options as well if we want.
728
729 const OptionDefinition *opt_defs = GetDefinitions();
730
731 std::string cur_opt_std_str (input.GetArgumentAtIndex(cursor_index));
732 cur_opt_std_str.erase(char_pos);
733 const char *cur_opt_str = cur_opt_std_str.c_str();
734
735 for (int i = 0; i < opt_element_vector.size(); i++)
736 {
737 int opt_pos = opt_element_vector[i].opt_pos;
738 int opt_arg_pos = opt_element_vector[i].opt_arg_pos;
739 int opt_defs_index = opt_element_vector[i].opt_defs_index;
740 if (opt_pos == cursor_index)
741 {
742 // We're completing the option itself.
Jim Ingham8b9af1c2010-06-24 20:30:15 +0000743
744 if (opt_defs_index == OptionArgElement::eBareDash)
745 {
746 // We're completing a bare dash. That means all options are open.
747 // FIXME: We should scan the other options provided and only complete options
748 // within the option group they belong to.
749 char opt_str[3] = {'-', 'a', '\0'};
750
Greg Claytonbef15832010-07-14 00:18:15 +0000751 for (int j = 0 ; opt_defs[j].short_option != 0 ; j++)
Jim Ingham8b9af1c2010-06-24 20:30:15 +0000752 {
Greg Claytonbef15832010-07-14 00:18:15 +0000753 opt_str[1] = opt_defs[j].short_option;
Jim Ingham8b9af1c2010-06-24 20:30:15 +0000754 matches.AppendString (opt_str);
755 }
756 return true;
757 }
758 else if (opt_defs_index == OptionArgElement::eBareDoubleDash)
759 {
760 std::string full_name ("--");
Greg Claytonbef15832010-07-14 00:18:15 +0000761 for (int j = 0 ; opt_defs[j].short_option != 0 ; j++)
Jim Ingham8b9af1c2010-06-24 20:30:15 +0000762 {
763 full_name.erase(full_name.begin() + 2, full_name.end());
Greg Claytonbef15832010-07-14 00:18:15 +0000764 full_name.append (opt_defs[j].long_option);
Jim Ingham8b9af1c2010-06-24 20:30:15 +0000765 matches.AppendString (full_name.c_str());
766 }
767 return true;
768 }
769 else if (opt_defs_index != OptionArgElement::eUnrecognizedArg)
Chris Lattner24943d22010-06-08 16:52:24 +0000770 {
771 // We recognized it, if it an incomplete long option, complete it anyway (getopt_long is
772 // happy with shortest unique string, but it's still a nice thing to do.) Otherwise return
773 // The string so the upper level code will know this is a full match and add the " ".
774 if (cur_opt_str && strlen (cur_opt_str) > 2
775 && cur_opt_str[0] == '-' && cur_opt_str[1] == '-'
776 && strcmp (opt_defs[opt_defs_index].long_option, cur_opt_str) != 0)
777 {
778 std::string full_name ("--");
779 full_name.append (opt_defs[opt_defs_index].long_option);
780 matches.AppendString(full_name.c_str());
781 return true;
782 }
783 else
784 {
785 matches.AppendString(input.GetArgumentAtIndex(cursor_index));
786 return true;
787 }
788 }
789 else
790 {
791 // FIXME - not handling wrong options yet:
792 // Check to see if they are writing a long option & complete it.
793 // I think we will only get in here if the long option table has two elements
794 // that are not unique up to this point. getopt_long does shortest unique match
795 // for long options already.
796
797 if (cur_opt_str && strlen (cur_opt_str) > 2
798 && cur_opt_str[0] == '-' && cur_opt_str[1] == '-')
799 {
Greg Claytonbef15832010-07-14 00:18:15 +0000800 for (int j = 0 ; opt_defs[j].short_option != 0 ; j++)
Chris Lattner24943d22010-06-08 16:52:24 +0000801 {
Greg Claytonbef15832010-07-14 00:18:15 +0000802 if (strstr(opt_defs[j].long_option, cur_opt_str + 2) == opt_defs[j].long_option)
Chris Lattner24943d22010-06-08 16:52:24 +0000803 {
804 std::string full_name ("--");
Greg Claytonbef15832010-07-14 00:18:15 +0000805 full_name.append (opt_defs[j].long_option);
Chris Lattner24943d22010-06-08 16:52:24 +0000806 // The options definitions table has duplicates because of the
807 // way the grouping information is stored, so only add once.
808 bool duplicate = false;
Greg Claytonbef15832010-07-14 00:18:15 +0000809 for (int k = 0; k < matches.GetSize(); k++)
Chris Lattner24943d22010-06-08 16:52:24 +0000810 {
Greg Claytonbef15832010-07-14 00:18:15 +0000811 if (matches.GetStringAtIndex(k) == full_name)
Chris Lattner24943d22010-06-08 16:52:24 +0000812 {
813 duplicate = true;
814 break;
815 }
816 }
817 if (!duplicate)
818 matches.AppendString(full_name.c_str());
819 }
820 }
821 }
822 return true;
823 }
824
825
826 }
827 else if (opt_arg_pos == cursor_index)
828 {
829 // Okay the cursor is on the completion of an argument.
830 // See if it has a completion, otherwise return no matches.
831
832 if (opt_defs_index != -1)
833 {
Greg Claytonf15996e2011-04-07 22:46:35 +0000834 HandleOptionArgumentCompletion (input,
Greg Clayton63094e02010-06-23 01:19:29 +0000835 cursor_index,
836 strlen (input.GetArgumentAtIndex(cursor_index)),
837 opt_element_vector,
838 i,
839 match_start_point,
840 max_return_elements,
Jim Ingham802f8b02010-06-30 05:02:46 +0000841 word_complete,
Greg Clayton63094e02010-06-23 01:19:29 +0000842 matches);
Chris Lattner24943d22010-06-08 16:52:24 +0000843 return true;
844 }
845 else
846 {
847 // No completion callback means no completions...
848 return true;
849 }
850
851 }
852 else
853 {
854 // Not the last element, keep going.
855 continue;
856 }
857 }
858 return false;
859}
860
861bool
862Options::HandleOptionArgumentCompletion
863(
864 Args &input,
865 int cursor_index,
866 int char_pos,
867 OptionElementVector &opt_element_vector,
868 int opt_element_index,
869 int match_start_point,
870 int max_return_elements,
Jim Ingham802f8b02010-06-30 05:02:46 +0000871 bool &word_complete,
Chris Lattner24943d22010-06-08 16:52:24 +0000872 lldb_private::StringList &matches
873)
874{
875 const OptionDefinition *opt_defs = GetDefinitions();
876 std::auto_ptr<SearchFilter> filter_ap;
877
878 int opt_arg_pos = opt_element_vector[opt_element_index].opt_arg_pos;
879 int opt_defs_index = opt_element_vector[opt_element_index].opt_defs_index;
880
881 // See if this is an enumeration type option, and if so complete it here:
882
883 OptionEnumValueElement *enum_values = opt_defs[opt_defs_index].enum_values;
884 if (enum_values != NULL)
885 {
886 bool return_value = false;
887 std::string match_string(input.GetArgumentAtIndex (opt_arg_pos), input.GetArgumentAtIndex (opt_arg_pos) + char_pos);
888 for (int i = 0; enum_values[i].string_value != NULL; i++)
889 {
890 if (strstr(enum_values[i].string_value, match_string.c_str()) == enum_values[i].string_value)
891 {
892 matches.AppendString (enum_values[i].string_value);
893 return_value = true;
894 }
895 }
896 return return_value;
897 }
898
899 // If this is a source file or symbol type completion, and there is a
900 // -shlib option somewhere in the supplied arguments, then make a search filter
901 // for that shared library.
902 // FIXME: Do we want to also have an "OptionType" so we don't have to match string names?
903
Greg Clayton5e342f52011-04-13 22:47:15 +0000904 uint32_t completion_mask = opt_defs[opt_defs_index].completion_type;
905
906 if (completion_mask == 0)
907 {
908 lldb::CommandArgumentType option_arg_type = opt_defs[opt_defs_index].argument_type;
909 if (option_arg_type != eArgTypeNone)
910 {
911 CommandObject::ArgumentTableEntry *arg_entry = CommandObject::FindArgumentDataByType (opt_defs[opt_defs_index].argument_type);
912 if (arg_entry)
913 completion_mask = arg_entry->completion_type;
914 }
915 }
916
Chris Lattner24943d22010-06-08 16:52:24 +0000917 if (completion_mask & CommandCompletions::eSourceFileCompletion
918 || completion_mask & CommandCompletions::eSymbolCompletion)
919 {
920 for (int i = 0; i < opt_element_vector.size(); i++)
921 {
922 int cur_defs_index = opt_element_vector[i].opt_defs_index;
923 int cur_arg_pos = opt_element_vector[i].opt_arg_pos;
924 const char *cur_opt_name = opt_defs[cur_defs_index].long_option;
925
926 // If this is the "shlib" option and there was an argument provided,
927 // restrict it to that shared library.
928 if (strcmp(cur_opt_name, "shlib") == 0 && cur_arg_pos != -1)
929 {
930 const char *module_name = input.GetArgumentAtIndex(cur_arg_pos);
931 if (module_name)
932 {
Greg Clayton537a7a82010-10-20 20:54:39 +0000933 FileSpec module_spec(module_name, false);
Greg Claytonf15996e2011-04-07 22:46:35 +0000934 lldb::TargetSP target_sp = m_interpreter.GetDebugger().GetSelectedTarget();
Chris Lattner24943d22010-06-08 16:52:24 +0000935 // Search filters require a target...
Greg Clayton987c7eb2011-09-17 08:33:22 +0000936 if (target_sp)
Chris Lattner24943d22010-06-08 16:52:24 +0000937 filter_ap.reset (new SearchFilterByModule (target_sp, module_spec));
938 }
939 break;
940 }
941 }
942 }
943
Greg Claytonf15996e2011-04-07 22:46:35 +0000944 return CommandCompletions::InvokeCommonCompletionCallbacks (m_interpreter,
Greg Clayton63094e02010-06-23 01:19:29 +0000945 completion_mask,
946 input.GetArgumentAtIndex (opt_arg_pos),
947 match_start_point,
948 max_return_elements,
949 filter_ap.get(),
Jim Ingham802f8b02010-06-30 05:02:46 +0000950 word_complete,
Greg Clayton63094e02010-06-23 01:19:29 +0000951 matches);
952
Chris Lattner24943d22010-06-08 16:52:24 +0000953}
Greg Clayton143fcc32011-04-13 00:18:08 +0000954
955
Greg Clayton143fcc32011-04-13 00:18:08 +0000956void
Greg Clayton57b3c6b2011-04-27 22:04:39 +0000957OptionGroupOptions::Append (OptionGroup* group)
958{
959 const OptionDefinition* group_option_defs = group->GetDefinitions ();
960 const uint32_t group_option_count = group->GetNumDefinitions();
961 for (uint32_t i=0; i<group_option_count; ++i)
962 {
963 m_option_infos.push_back (OptionInfo (group, i));
964 m_option_defs.push_back (group_option_defs[i]);
965 }
966}
967
968void
Greg Clayton5e342f52011-04-13 22:47:15 +0000969OptionGroupOptions::Append (OptionGroup* group,
970 uint32_t src_mask,
971 uint32_t dst_mask)
Greg Clayton143fcc32011-04-13 00:18:08 +0000972{
Greg Clayton143fcc32011-04-13 00:18:08 +0000973 const OptionDefinition* group_option_defs = group->GetDefinitions ();
974 const uint32_t group_option_count = group->GetNumDefinitions();
975 for (uint32_t i=0; i<group_option_count; ++i)
976 {
Greg Clayton5e342f52011-04-13 22:47:15 +0000977 if (group_option_defs[i].usage_mask & src_mask)
978 {
979 m_option_infos.push_back (OptionInfo (group, i));
980 m_option_defs.push_back (group_option_defs[i]);
981 m_option_defs.back().usage_mask = dst_mask;
982 }
Greg Clayton143fcc32011-04-13 00:18:08 +0000983 }
984}
985
986void
987OptionGroupOptions::Finalize ()
988{
989 m_did_finalize = true;
990 OptionDefinition empty_option_def = { 0, false, NULL, 0, 0, NULL, 0, eArgTypeNone, NULL };
991 m_option_defs.push_back (empty_option_def);
992}
993
994Error
995OptionGroupOptions::SetOptionValue (uint32_t option_idx,
996 const char *option_value)
997{
998 // After calling OptionGroupOptions::Append(...), you must finalize the groups
999 // by calling OptionGroupOptions::Finlize()
1000 assert (m_did_finalize);
Greg Clayton5e342f52011-04-13 22:47:15 +00001001 assert (m_option_infos.size() + 1 == m_option_defs.size());
Greg Clayton143fcc32011-04-13 00:18:08 +00001002 Error error;
Greg Clayton5e342f52011-04-13 22:47:15 +00001003 if (option_idx < m_option_infos.size())
1004 {
1005 error = m_option_infos[option_idx].option_group->SetOptionValue (m_interpreter,
1006 m_option_infos[option_idx].option_index,
1007 option_value);
1008
1009 }
1010 else
1011 {
1012 error.SetErrorString ("invalid option index"); // Shouldn't happen...
1013 }
Greg Clayton143fcc32011-04-13 00:18:08 +00001014 return error;
1015}
1016
1017void
1018OptionGroupOptions::OptionParsingStarting ()
1019{
Greg Clayton5e342f52011-04-13 22:47:15 +00001020 std::set<OptionGroup*> group_set;
1021 OptionInfos::iterator pos, end = m_option_infos.end();
1022 for (pos = m_option_infos.begin(); pos != end; ++pos)
1023 {
1024 OptionGroup* group = pos->option_group;
1025 if (group_set.find(group) == group_set.end())
1026 {
1027 group->OptionParsingStarting (m_interpreter);
1028 group_set.insert(group);
1029 }
1030 }
Greg Clayton143fcc32011-04-13 00:18:08 +00001031}
1032Error
1033OptionGroupOptions::OptionParsingFinished ()
1034{
Greg Clayton5e342f52011-04-13 22:47:15 +00001035 std::set<OptionGroup*> group_set;
Greg Clayton143fcc32011-04-13 00:18:08 +00001036 Error error;
Greg Clayton5e342f52011-04-13 22:47:15 +00001037 OptionInfos::iterator pos, end = m_option_infos.end();
1038 for (pos = m_option_infos.begin(); pos != end; ++pos)
Greg Clayton143fcc32011-04-13 00:18:08 +00001039 {
Greg Clayton5e342f52011-04-13 22:47:15 +00001040 OptionGroup* group = pos->option_group;
1041 if (group_set.find(group) == group_set.end())
1042 {
1043 error = group->OptionParsingFinished (m_interpreter);
1044 group_set.insert(group);
1045 if (error.Fail())
1046 return error;
1047 }
Greg Clayton143fcc32011-04-13 00:18:08 +00001048 }
1049 return error;
1050}