apt  2.2.4
About: Apt (Advanced Package Tool) is a management system for software packages (Debian/Ubuntu). Release series 2.2.
  Fossies Dox: apt-2.2.4.tar.gz  ("unofficial" and yet experimental doxygen-generated source code documentation)  

cmndline.cc
Go to the documentation of this file.
1 // -*- mode: cpp; mode: fold -*-
2 // Description /*{{{*/
3 /* ######################################################################
4 
5  Command Line Class - Sophisticated command line parser
6 
7  This source is placed in the Public Domain, do with it what you will
8  It was originally written by Jason Gunthorpe <jgg@debian.org>.
9 
10  ##################################################################### */
11  /*}}}*/
12 // Include files /*{{{*/
13 #include <config.h>
14 
15 #include <apt-pkg/cmndline.h>
16 #include <apt-pkg/configuration.h>
17 #include <apt-pkg/error.h>
18 #include <apt-pkg/strutl.h>
19 
20 #include <string>
21 #include <stddef.h>
22 #include <stdlib.h>
23 #include <string.h>
24 
25 #include <apti18n.h>
26  /*}}}*/
27 using namespace std;
28 
29 // CommandLine::CommandLine - Constructor /*{{{*/
30 // ---------------------------------------------------------------------
31 /* */
32 CommandLine::CommandLine(Args *AList,Configuration *Conf) : ArgList(AList),
33  Conf(Conf), FileList(0)
34 {
35 }
36 CommandLine::CommandLine() : ArgList(NULL), Conf(NULL), FileList(0)
37 {
38 }
39  /*}}}*/
40 // CommandLine::~CommandLine - Destructor /*{{{*/
41 // ---------------------------------------------------------------------
42 /* */
44 {
45  delete [] FileList;
46 }
47  /*}}}*/
48 // CommandLine::GetCommand - return the first non-option word /*{{{*/
49 char const * CommandLine::GetCommand(Dispatch const * const Map,
50  unsigned int const argc, char const * const * const argv)
51 {
52  // if there is a -- on the line there must be the word we search for either
53  // before it (as -- marks the end of the options) or right after it (as we can't
54  // decide if the command is actually an option, given that in theory, you could
55  // have parameters named like commands)
56  for (size_t i = 1; i < argc; ++i)
57  {
58  if (strcmp(argv[i], "--") != 0)
59  continue;
60  // check if command is before --
61  for (size_t k = 1; k < i; ++k)
62  for (size_t j = 0; Map[j].Match != NULL; ++j)
63  if (strcmp(argv[k], Map[j].Match) == 0)
64  return Map[j].Match;
65  // see if the next token after -- is the command
66  ++i;
67  if (i < argc)
68  for (size_t j = 0; Map[j].Match != NULL; ++j)
69  if (strcmp(argv[i], Map[j].Match) == 0)
70  return Map[j].Match;
71  // we found a --, but not a command
72  return NULL;
73  }
74  // no --, so search for the first word matching a command
75  // FIXME: How like is it that an option parameter will be also a valid Match ?
76  for (size_t i = 1; i < argc; ++i)
77  {
78  if (*(argv[i]) == '-')
79  continue;
80  for (size_t j = 0; Map[j].Match != NULL; ++j)
81  if (strcmp(argv[i], Map[j].Match) == 0)
82  return Map[j].Match;
83  }
84  return NULL;
85 }
86  /*}}}*/
87 // CommandLine::Parse - Main action member /*{{{*/
88 // ---------------------------------------------------------------------
89 /* */
90 bool CommandLine::Parse(int argc,const char **argv)
91 {
92  delete [] FileList;
93  FileList = new const char *[argc];
94  const char **Files = FileList;
95  int I;
96  for (I = 1; I != argc; I++)
97  {
98  const char *Opt = argv[I];
99 
100  // It is not an option
101  if (*Opt != '-')
102  {
103  *Files++ = Opt;
104  continue;
105  }
106 
107  Opt++;
108 
109  // Double dash signifies the end of option processing
110  if (*Opt == '-' && Opt[1] == 0)
111  {
112  I++;
113  break;
114  }
115 
116  // Single dash is a short option
117  if (*Opt != '-')
118  {
119  // Iterate over each letter
120  while (*Opt != 0)
121  {
122  // Search for the option
123  Args *A;
124  for (A = ArgList; A->end() == false && A->ShortOpt != *Opt; A++);
125  if (A->end() == true)
126  return _error->Error(_("Command line option '%c' [from %s] is not understood in combination with the other options."),*Opt,argv[I]);
127 
128  if (HandleOpt(I,argc,argv,Opt,A) == false)
129  return false;
130  if (*Opt != 0)
131  Opt++;
132  }
133  continue;
134  }
135 
136  Opt++;
137 
138  // Match up to a = against the list
139  Args *A;
140  const char *OptEnd = strchrnul(Opt, '=');
141  for (A = ArgList; A->end() == false &&
142  (A->LongOpt == 0 || stringcasecmp(Opt,OptEnd,A->LongOpt) != 0);
143  ++A);
144 
145  // Failed, look for a word after the first - (no-foo)
146  bool PreceedMatch = false;
147  if (A->end() == true)
148  {
149  Opt = (const char*) memchr(Opt, '-', OptEnd - Opt);
150  if (Opt == NULL)
151  return _error->Error(_("Command line option %s is not understood in combination with the other options"),argv[I]);
152  Opt++;
153 
154  for (A = ArgList; A->end() == false &&
155  (A->LongOpt == 0 || stringcasecmp(Opt,OptEnd,A->LongOpt) != 0);
156  ++A);
157 
158  // Failed again..
159  if (A->end() == true && OptEnd - Opt != 1)
160  return _error->Error(_("Command line option %s is not understood in combination with the other options"),argv[I]);
161 
162  // The option could be a single letter option prefixed by a no-..
163  if (A->end() == true)
164  {
165  for (A = ArgList; A->end() == false && A->ShortOpt != *Opt; A++);
166 
167  if (A->end() == true)
168  return _error->Error(_("Command line option %s is not understood in combination with the other options"),argv[I]);
169  }
170 
171  // The option is not boolean
172  if (A->IsBoolean() == false)
173  return _error->Error(_("Command line option %s is not boolean"),argv[I]);
174  PreceedMatch = true;
175  }
176 
177  // Deal with it.
178  OptEnd--;
179  if (HandleOpt(I,argc,argv,OptEnd,A,PreceedMatch) == false)
180  return false;
181  }
182 
183  // Copy any remaining file names over
184  for (; I != argc; I++)
185  *Files++ = argv[I];
186  *Files = 0;
187 
188  SaveInConfig(argc, argv);
189 
190  return true;
191 }
192  /*}}}*/
193 // CommandLine::HandleOpt - Handle a single option including all flags /*{{{*/
194 // ---------------------------------------------------------------------
195 /* This is a helper function for parser, it looks at a given argument
196  and looks for specific patterns in the string, it gets tokanized
197  -ruffly- like -*[yes|true|enable]-(o|longopt)[=][ ][argument] */
198 bool CommandLine::HandleOpt(int &I,int argc,const char *argv[],
199  const char *&Opt,Args *A,bool PreceedMatch)
200 {
201  const char *Argument = 0;
202  bool CertainArg = false;
203  int IncI = 0;
204 
205  /* Determine the possible location of an option or 0 if their is
206  no option */
207  if (Opt[1] == 0)
208  {
209  if (I + 1 < argc && argv[I+1][0] != '-')
210  Argument = argv[I+1];
211 
212  IncI = 1;
213  }
214  else
215  {
216  if (Opt[1] == '=')
217  {
218  CertainArg = true;
219  Argument = Opt + 2;
220  }
221  else
222  Argument = Opt + 1;
223  }
224 
225  // Option is an argument set
226  if ((A->Flags & HasArg) == HasArg)
227  {
228  if (Argument == 0)
229  return _error->Error(_("Option %s requires an argument."),argv[I]);
230  Opt += strlen(Opt);
231  I += IncI;
232 
233  // Parse a configuration file
234  if ((A->Flags & ConfigFile) == ConfigFile)
235  return ReadConfigFile(*Conf,Argument);
236 
237  // Arbitrary item specification
238  if ((A->Flags & ArbItem) == ArbItem)
239  {
240  const char * const J = strchr(Argument, '=');
241  if (J == nullptr)
242  return _error->Error(_("Option %s: Configuration item specification must have an =<val>."),argv[I]);
243 
244  Conf->Set(string(Argument,J-Argument), J+1);
245  return true;
246  }
247 
248  const char *I = strchrnul(A->ConfName, ' ');
249  if (*I == ' ')
250  Conf->Set(string(A->ConfName,0,I-A->ConfName),string(I+1) + Argument);
251  else
252  Conf->Set(A->ConfName,string(I) + Argument);
253 
254  return true;
255  }
256 
257  // Option is an integer level
258  if ((A->Flags & IntLevel) == IntLevel)
259  {
260  // There might be an argument
261  if (Argument != 0)
262  {
263  char *EndPtr;
264  unsigned long Value = strtol(Argument,&EndPtr,10);
265 
266  // Conversion failed and the argument was specified with an =s
267  if (EndPtr == Argument && CertainArg == true)
268  return _error->Error(_("Option %s requires an integer argument, not '%s'"),argv[I],Argument);
269 
270  // Conversion was ok, set the value and return
271  if (EndPtr != 0 && EndPtr != Argument && *EndPtr == 0)
272  {
273  Conf->Set(A->ConfName,Value);
274  Opt += strlen(Opt);
275  I += IncI;
276  return true;
277  }
278  }
279 
280  // Increase the level
281  Conf->Set(A->ConfName,Conf->FindI(A->ConfName)+1);
282  return true;
283  }
284 
285  // Option is a boolean
286  int Sense = -1; // -1 is unspecified, 0 is yes 1 is no
287 
288  // Look for an argument.
289  while (1)
290  {
291  // Look at preceding text
292  char Buffer[300];
293  if (Argument == 0)
294  {
295  if (PreceedMatch == false)
296  break;
297 
298  if (strlen(argv[I]) >= sizeof(Buffer))
299  return _error->Error(_("Option '%s' is too long"),argv[I]);
300 
301  // Skip the leading dash
302  const char *J = argv[I];
303  for (; *J == '-'; J++)
304  ;
305 
306  const char *JEnd = strchr(J, '-');
307  if (JEnd != NULL)
308  {
309  strncpy(Buffer,J,JEnd - J);
310  Buffer[JEnd - J] = 0;
311  Argument = Buffer;
312  CertainArg = true;
313  }
314  else
315  break;
316  }
317 
318  // Check for boolean
319  Sense = StringToBool(Argument);
320  if (Sense >= 0)
321  {
322  // Eat the argument
323  if (Argument != Buffer)
324  {
325  Opt += strlen(Opt);
326  I += IncI;
327  }
328  break;
329  }
330 
331  if (CertainArg == true)
332  return _error->Error(_("Sense %s is not understood, try true or false."),Argument);
333 
334  Argument = 0;
335  }
336 
337  // Indeterminate sense depends on the flag
338  if (Sense == -1)
339  {
340  if ((A->Flags & InvBoolean) == InvBoolean)
341  Sense = 0;
342  else
343  Sense = 1;
344  }
345 
346  Conf->Set(A->ConfName,Sense);
347  return true;
348 }
349  /*}}}*/
350 // CommandLine::FileSize - Count the number of filenames /*{{{*/
351 // ---------------------------------------------------------------------
352 /* */
353 unsigned int CommandLine::FileSize() const
354 {
355  unsigned int Count = 0;
356  for (const char **I = FileList; I != 0 && *I != 0; I++)
357  Count++;
358  return Count;
359 }
360  /*}}}*/
361 // CommandLine::DispatchArg - Do something with the first arg /*{{{*/
362 bool CommandLine::DispatchArg(Dispatch const * const Map,bool NoMatch)
363 {
364  int I;
365  for (I = 0; Map[I].Match != 0; I++)
366  {
367  if (strcmp(FileList[0],Map[I].Match) == 0)
368  {
369  bool Res = Map[I].Handler(*this);
370  if (Res == false && _error->PendingError() == false)
371  _error->Error("Handler silently failed");
372  return Res;
373  }
374  }
375 
376  // No matching name
377  if (Map[I].Match == 0)
378  {
379  if (NoMatch == true)
380  _error->Error(_("Invalid operation %s"),FileList[0]);
381  }
382 
383  return false;
384 }
385  /*}}}*/
386 // CommandLine::SaveInConfig - for output later in a logfile or so /*{{{*/
387 // ---------------------------------------------------------------------
388 /* We save the commandline here to have it around later for e.g. logging.
389  It feels a bit like a hack here and isn't bulletproof, but it is better
390  than nothing after all. */
391 void CommandLine::SaveInConfig(unsigned int const &argc, char const * const * const argv)
392 {
393  char cmdline[100 + argc * 50];
394  memset(cmdline, 0, sizeof(cmdline));
395  unsigned int length = 0;
396  bool lastWasOption = false;
397  bool closeQuote = false;
398  for (unsigned int i = 0; i < argc && length < sizeof(cmdline); ++i, ++length)
399  {
400  for (unsigned int j = 0; argv[i][j] != '\0' && length < sizeof(cmdline)-2; ++j)
401  {
402  // we can't really sensibly deal with quoting so skip it
403  if (strchr("\"\'\r\n", argv[i][j]) != nullptr)
404  continue;
405  cmdline[length++] = argv[i][j];
406  if (lastWasOption == true && argv[i][j] == '=')
407  {
408  // That is possibly an option: Quote it if it includes spaces,
409  // the benefit is that this will eliminate also most false positives
410  const char* c = strchr(&argv[i][j+1], ' ');
411  if (c == NULL) continue;
412  cmdline[length++] = '\'';
413  closeQuote = true;
414  }
415  }
416  if (closeQuote == true)
417  {
418  cmdline[length++] = '\'';
419  closeQuote = false;
420  }
421  // Problem: detects also --hello
422  if (cmdline[length-1] == 'o')
423  lastWasOption = true;
424  cmdline[length] = ' ';
425  }
426  cmdline[--length] = '\0';
427  _config->Set("CommandLine::AsString", cmdline);
428 }
429  /*}}}*/
430 CommandLine::Args CommandLine::MakeArgs(char ShortOpt, char const *LongOpt, char const *ConfName, unsigned long Flags)/*{{{*/
431 {
432  /* In theory, this should be a constructor for CommandLine::Args instead,
433  but this breaks compatibility as gcc thinks this is a c++11 initializer_list */
434  CommandLine::Args arg;
435  arg.ShortOpt = ShortOpt;
436  arg.LongOpt = LongOpt;
437  arg.ConfName = ConfName;
438  arg.Flags = Flags;
439  return arg;
440 }
441  /*}}}*/
static bool std::string const metaIndex const *const pkgAcqMetaClearSig *const pkgAcquire::Item *const I
Provide access methods to various configuration settings.
const char ** FileList
Definition: cmndline.h:78
unsigned int FileSize() const APT_PURE
Definition: cmndline.cc:353
bool Parse(int argc, const char **argv)
Definition: cmndline.cc:90
@ ConfigFile
Definition: cmndline.h:74
@ InvBoolean
Definition: cmndline.h:73
bool DispatchArg(Dispatch const *const List, bool NoMatch=true)
Definition: cmndline.cc:362
static void SaveInConfig(unsigned int const &argc, char const *const *const argv)
Definition: cmndline.cc:391
Args * ArgList
Definition: cmndline.h:56
Configuration * Conf
Definition: cmndline.h:61
static CommandLine::Args MakeArgs(char ShortOpt, char const *LongOpt, char const *ConfName, unsigned long Flags) APT_PURE
Definition: cmndline.cc:430
static char const * GetCommand(Dispatch const *const Map, unsigned int const argc, char const *const *const argv) APT_PURE
Definition: cmndline.cc:49
bool HandleOpt(int &I, int argc, const char *argv[], const char *&Opt, Args *A, bool PreceedeMatch=false)
Definition: cmndline.cc:198
~CommandLine()
Definition: cmndline.cc:43
bool ReadConfigFile(Configuration &Conf, const string &FName, bool const &AsSectional, unsigned const &Depth)
Configuration * _config
unsigned long Flags
Definition: cmndline.h:101
bool IsBoolean()
Definition: cmndline.h:104
const char * LongOpt
Definition: cmndline.h:99
const char * ConfName
Definition: cmndline.h:100
const char * Match
Definition: cmndline.h:109
bool(* Handler)(CommandLine &)
Definition: cmndline.h:110
int stringcasecmp(const char *A, const char *AEnd, const char *B, const char *BEnd)
Definition: strutl.cc:685
int StringToBool(const string &Text, int Default)
Definition: strutl.cc:820