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