]> git.saurik.com Git - apt.git/blame - apt-pkg/contrib/cmndline.cc
Merge 0.7.7 release
[apt.git] / apt-pkg / contrib / cmndline.cc
CommitLineData
08e8f724
AL
1// -*- mode: cpp; mode: fold -*-
2// Description /*{{{*/
bac2e715 3// $Id: cmndline.cc,v 1.15 2003/02/10 01:40:58 doogie Exp $
08e8f724
AL
4/* ######################################################################
5
6 Command Line Class - Sophisticated command line parser
7
7da2b375
AL
8 This source is placed in the Public Domain, do with it what you will
9 It was originally written by Jason Gunthorpe <jgg@debian.org>.
10
08e8f724
AL
11 ##################################################################### */
12 /*}}}*/
13// Include files /*{{{*/
08e8f724
AL
14#include <apt-pkg/cmndline.h>
15#include <apt-pkg/error.h>
cdcc6d34 16#include <apt-pkg/strutl.h>
b2e465d6
AL
17
18#include <apti18n.h>
08e8f724 19 /*}}}*/
584e4558 20using namespace std;
08e8f724
AL
21
22// CommandLine::CommandLine - Constructor /*{{{*/
23// ---------------------------------------------------------------------
24/* */
25CommandLine::CommandLine(Args *AList,Configuration *Conf) : ArgList(AList),
26 Conf(Conf), FileList(0)
27{
e1b74f61
AL
28}
29 /*}}}*/
30// CommandLine::~CommandLine - Destructor /*{{{*/
31// ---------------------------------------------------------------------
32/* */
33CommandLine::~CommandLine()
34{
35 delete [] FileList;
08e8f724
AL
36}
37 /*}}}*/
38// CommandLine::Parse - Main action member /*{{{*/
39// ---------------------------------------------------------------------
40/* */
41bool CommandLine::Parse(int argc,const char **argv)
42{
e1b74f61 43 delete [] FileList;
08e8f724
AL
44 FileList = new const char *[argc];
45 const char **Files = FileList;
46 int I;
47 for (I = 1; I != argc; I++)
48 {
49 const char *Opt = argv[I];
50
51 // It is not an option
52 if (*Opt != '-')
53 {
54 *Files++ = Opt;
55 continue;
56 }
57
58 Opt++;
59
60 // Double dash signifies the end of option processing
61 if (*Opt == '-' && Opt[1] == 0)
343bd48e
AL
62 {
63 I++;
08e8f724 64 break;
343bd48e 65 }
08e8f724
AL
66
67 // Single dash is a short option
68 if (*Opt != '-')
69 {
70 // Iterate over each letter
71 while (*Opt != 0)
72 {
73 // Search for the option
74 Args *A;
75 for (A = ArgList; A->end() == false && A->ShortOpt != *Opt; A++);
76 if (A->end() == true)
b2e465d6 77 return _error->Error(_("Command line option '%c' [from %s] is not known."),*Opt,argv[I]);
08e8f724
AL
78
79 if (HandleOpt(I,argc,argv,Opt,A) == false)
80 return false;
81 if (*Opt != 0)
82 Opt++;
83 }
84 continue;
85 }
86
87 Opt++;
88
89 // Match up to a = against the list
90 const char *OptEnd = Opt;
91 Args *A;
92 for (; *OptEnd != 0 && *OptEnd != '='; OptEnd++);
93 for (A = ArgList; A->end() == false &&
94 stringcasecmp(Opt,OptEnd,A->LongOpt) != 0; A++);
95
96 // Failed, look for a word after the first - (no-foo)
0d47bd08 97 bool PreceedMatch = false;
08e8f724
AL
98 if (A->end() == true)
99 {
100 for (; Opt != OptEnd && *Opt != '-'; Opt++);
101
102 if (Opt == OptEnd)
b2e465d6 103 return _error->Error(_("Command line option %s is not understood"),argv[I]);
08e8f724
AL
104 Opt++;
105
106 for (A = ArgList; A->end() == false &&
107 stringcasecmp(Opt,OptEnd,A->LongOpt) != 0; A++);
108
109 // Failed again..
110 if (A->end() == true && OptEnd - Opt != 1)
b2e465d6 111 return _error->Error(_("Command line option %s is not understood"),argv[I]);
0d47bd08 112
08e8f724 113 // The option could be a single letter option prefixed by a no-..
08e8f724 114 if (A->end() == true)
0d47bd08
AL
115 {
116 for (A = ArgList; A->end() == false && A->ShortOpt != *Opt; A++);
117
118 if (A->end() == true)
b2e465d6 119 return _error->Error(_("Command line option %s is not understood"),argv[I]);
0d47bd08 120 }
e1b74f61
AL
121
122 // The option is not boolean
123 if (A->IsBoolean() == false)
b2e465d6 124 return _error->Error(_("Command line option %s is not boolean"),argv[I]);
0d47bd08 125 PreceedMatch = true;
08e8f724
AL
126 }
127
128 // Deal with it.
129 OptEnd--;
0d47bd08 130 if (HandleOpt(I,argc,argv,OptEnd,A,PreceedMatch) == false)
08e8f724
AL
131 return false;
132 }
133
134 // Copy any remaining file names over
135 for (; I != argc; I++)
136 *Files++ = argv[I];
137 *Files = 0;
138
139 return true;
140}
141 /*}}}*/
142// CommandLine::HandleOpt - Handle a single option including all flags /*{{{*/
143// ---------------------------------------------------------------------
144/* This is a helper function for parser, it looks at a given argument
145 and looks for specific patterns in the string, it gets tokanized
146 -ruffly- like -*[yes|true|enable]-(o|longopt)[=][ ][argument] */
147bool CommandLine::HandleOpt(int &I,int argc,const char *argv[],
0d47bd08 148 const char *&Opt,Args *A,bool PreceedMatch)
08e8f724
AL
149{
150 const char *Argument = 0;
151 bool CertainArg = false;
152 int IncI = 0;
153
154 /* Determine the possible location of an option or 0 if their is
155 no option */
156 if (Opt[1] == 0 || (Opt[1] == '=' && Opt[2] == 0))
157 {
158 if (I + 1 < argc && argv[I+1][0] != '-')
159 Argument = argv[I+1];
160
161 // Equals was specified but we fell off the end!
162 if (Opt[1] == '=' && Argument == 0)
b2e465d6 163 return _error->Error(_("Option %s requires an argument."),argv[I]);
08e8f724
AL
164 if (Opt[1] == '=')
165 CertainArg = true;
166
167 IncI = 1;
168 }
169 else
170 {
171 if (Opt[1] == '=')
172 {
173 CertainArg = true;
174 Argument = Opt + 2;
175 }
176 else
177 Argument = Opt + 1;
178 }
0d47bd08 179
08e8f724
AL
180 // Option is an argument set
181 if ((A->Flags & HasArg) == HasArg)
182 {
183 if (Argument == 0)
b2e465d6 184 return _error->Error(_("Option %s requires an argument."),argv[I]);
08e8f724
AL
185 Opt += strlen(Opt);
186 I += IncI;
187
188 // Parse a configuration file
189 if ((A->Flags & ConfigFile) == ConfigFile)
190 return ReadConfigFile(*Conf,Argument);
e1b74f61
AL
191
192 // Arbitary item specification
193 if ((A->Flags & ArbItem) == ArbItem)
194 {
195 const char *J;
196 for (J = Argument; *J != 0 && *J != '='; J++);
197 if (*J == 0)
bac2e715 198 return _error->Error(_("Option %s: Configuration item specification must have an =<val>."),argv[I]);
e1b74f61 199
7e798dd7
AL
200 // = is trailing
201 if (J[1] == 0)
202 {
203 if (I+1 >= argc)
bac2e715 204 return _error->Error(_("Option %s: Configuration item specification must have an =<val>."),argv[I]);
7e798dd7
AL
205 Conf->Set(string(Argument,J-Argument),string(argv[I++ +1]));
206 }
207 else
208 Conf->Set(string(Argument,J-Argument),string(J+1));
e1b74f61
AL
209
210 return true;
211 }
08e8f724 212
7f25bdff
AL
213 const char *I = A->ConfName;
214 for (; *I != 0 && *I != ' '; I++);
215 if (*I == ' ')
216 Conf->Set(string(A->ConfName,0,I-A->ConfName),string(I+1) + Argument);
217 else
218 Conf->Set(A->ConfName,string(I) + Argument);
219
08e8f724
AL
220 return true;
221 }
222
223 // Option is an integer level
224 if ((A->Flags & IntLevel) == IntLevel)
225 {
226 // There might be an argument
227 if (Argument != 0)
228 {
229 char *EndPtr;
230 unsigned long Value = strtol(Argument,&EndPtr,10);
231
232 // Conversion failed and the argument was specified with an =s
233 if (EndPtr == Argument && CertainArg == true)
b2e465d6 234 return _error->Error(_("Option %s requires an integer argument, not '%s'"),argv[I],Argument);
08e8f724
AL
235
236 // Conversion was ok, set the value and return
9435cc9b 237 if (EndPtr != 0 && EndPtr != Argument && *EndPtr == 0)
08e8f724
AL
238 {
239 Conf->Set(A->ConfName,Value);
240 Opt += strlen(Opt);
241 I += IncI;
242 return true;
243 }
244 }
245
246 // Increase the level
247 Conf->Set(A->ConfName,Conf->FindI(A->ConfName)+1);
248 return true;
249 }
250
251 // Option is a boolean
252 int Sense = -1; // -1 is unspecified, 0 is yes 1 is no
253
254 // Look for an argument.
255 while (1)
256 {
257 // Look at preceeding text
258 char Buffer[300];
259 if (Argument == 0)
260 {
0d47bd08
AL
261 if (PreceedMatch == false)
262 break;
263
08e8f724 264 if (strlen(argv[I]) >= sizeof(Buffer))
b2e465d6 265 return _error->Error(_("Option '%s' is too long"),argv[I]);
0d47bd08
AL
266
267 // Skip the leading dash
08e8f724
AL
268 const char *J = argv[I];
269 for (; *J != 0 && *J == '-'; J++);
0d47bd08 270
08e8f724
AL
271 const char *JEnd = J;
272 for (; *JEnd != 0 && *JEnd != '-'; JEnd++);
273 if (*JEnd != 0)
274 {
275 strncpy(Buffer,J,JEnd - J);
276 Buffer[JEnd - J] = 0;
277 Argument = Buffer;
278 CertainArg = true;
279 }
280 else
281 break;
282 }
283
3b5421b4
AL
284 // Check for boolean
285 Sense = StringToBool(Argument);
286 if (Sense >= 0)
08e8f724 287 {
08e8f724
AL
288 // Eat the argument
289 if (Argument != Buffer)
290 {
291 Opt += strlen(Opt);
292 I += IncI;
293 }
294 break;
295 }
296
08e8f724 297 if (CertainArg == true)
b2e465d6 298 return _error->Error(_("Sense %s is not understood, try true or false."),Argument);
08e8f724
AL
299
300 Argument = 0;
301 }
302
303 // Indeterminate sense depends on the flag
304 if (Sense == -1)
305 {
306 if ((A->Flags & InvBoolean) == InvBoolean)
307 Sense = 0;
308 else
309 Sense = 1;
310 }
311
312 Conf->Set(A->ConfName,Sense);
313 return true;
314}
315 /*}}}*/
bc4af0b9 316// CommandLine::FileSize - Count the number of filenames /*{{{*/
e1b74f61
AL
317// ---------------------------------------------------------------------
318/* */
319unsigned int CommandLine::FileSize() const
320{
321 unsigned int Count = 0;
322 for (const char **I = FileList; I != 0 && *I != 0; I++)
323 Count++;
324 return Count;
325}
326 /*}}}*/
bc4af0b9
AL
327// CommandLine::DispatchArg - Do something with the first arg /*{{{*/
328// ---------------------------------------------------------------------
329/* */
b0b4efb9 330bool CommandLine::DispatchArg(Dispatch *Map,bool NoMatch)
bc4af0b9
AL
331{
332 int I;
333 for (I = 0; Map[I].Match != 0; I++)
334 {
335 if (strcmp(FileList[0],Map[I].Match) == 0)
336 {
337 bool Res = Map[I].Handler(*this);
338 if (Res == false && _error->PendingError() == false)
339 _error->Error("Handler silently failed");
340 return Res;
341 }
342 }
343
344 // No matching name
345 if (Map[I].Match == 0)
b0b4efb9
AL
346 {
347 if (NoMatch == true)
b2e465d6 348 _error->Error(_("Invalid operation %s"),FileList[0]);
b0b4efb9
AL
349 }
350
bc4af0b9
AL
351 return false;
352}
353 /*}}}*/