Sync with Michael
[ntk/apt.git] / apt-pkg / contrib / cmndline.cc
1 // -*- mode: cpp; mode: fold -*-
2 // Description /*{{{*/
3 // $Id: cmndline.cc,v 1.15 2003/02/10 01:40:58 doogie Exp $
4 /* ######################################################################
5
6 Command Line Class - Sophisticated command line parser
7
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
11 ##################################################################### */
12 /*}}}*/
13 // Include files /*{{{*/
14 #include <apt-pkg/cmndline.h>
15 #include <apt-pkg/error.h>
16 #include <apt-pkg/strutl.h>
17
18 #include <apti18n.h>
19 /*}}}*/
20 using namespace std;
21
22 // CommandLine::CommandLine - Constructor /*{{{*/
23 // ---------------------------------------------------------------------
24 /* */
25 CommandLine::CommandLine(Args *AList,Configuration *Conf) : ArgList(AList),
26 Conf(Conf), FileList(0)
27 {
28 }
29 /*}}}*/
30 // CommandLine::~CommandLine - Destructor /*{{{*/
31 // ---------------------------------------------------------------------
32 /* */
33 CommandLine::~CommandLine()
34 {
35 delete [] FileList;
36 }
37 /*}}}*/
38 // CommandLine::Parse - Main action member /*{{{*/
39 // ---------------------------------------------------------------------
40 /* */
41 bool CommandLine::Parse(int argc,const char **argv)
42 {
43 delete [] FileList;
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)
62 {
63 I++;
64 break;
65 }
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)
77 return _error->Error(_("Command line option '%c' [from %s] is not known."),*Opt,argv[I]);
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)
97 bool PreceedMatch = false;
98 if (A->end() == true)
99 {
100 for (; Opt != OptEnd && *Opt != '-'; Opt++);
101
102 if (Opt == OptEnd)
103 return _error->Error(_("Command line option %s is not understood"),argv[I]);
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)
111 return _error->Error(_("Command line option %s is not understood"),argv[I]);
112
113 // The option could be a single letter option prefixed by a no-..
114 if (A->end() == true)
115 {
116 for (A = ArgList; A->end() == false && A->ShortOpt != *Opt; A++);
117
118 if (A->end() == true)
119 return _error->Error(_("Command line option %s is not understood"),argv[I]);
120 }
121
122 // The option is not boolean
123 if (A->IsBoolean() == false)
124 return _error->Error(_("Command line option %s is not boolean"),argv[I]);
125 PreceedMatch = true;
126 }
127
128 // Deal with it.
129 OptEnd--;
130 if (HandleOpt(I,argc,argv,OptEnd,A,PreceedMatch) == false)
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] */
147 bool CommandLine::HandleOpt(int &I,int argc,const char *argv[],
148 const char *&Opt,Args *A,bool PreceedMatch)
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)
163 return _error->Error(_("Option %s requires an argument."),argv[I]);
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 }
179
180 // Option is an argument set
181 if ((A->Flags & HasArg) == HasArg)
182 {
183 if (Argument == 0)
184 return _error->Error(_("Option %s requires an argument."),argv[I]);
185 Opt += strlen(Opt);
186 I += IncI;
187
188 // Parse a configuration file
189 if ((A->Flags & ConfigFile) == ConfigFile)
190 return ReadConfigFile(*Conf,Argument);
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)
198 return _error->Error(_("Option %s: Configuration item specification must have an =<val>."),argv[I]);
199
200 // = is trailing
201 if (J[1] == 0)
202 {
203 if (I+1 >= argc)
204 return _error->Error(_("Option %s: Configuration item specification must have an =<val>."),argv[I]);
205 Conf->Set(string(Argument,J-Argument),string(argv[I++ +1]));
206 }
207 else
208 Conf->Set(string(Argument,J-Argument),string(J+1));
209
210 return true;
211 }
212
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
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)
234 return _error->Error(_("Option %s requires an integer argument, not '%s'"),argv[I],Argument);
235
236 // Conversion was ok, set the value and return
237 if (EndPtr != 0 && EndPtr != Argument && *EndPtr == 0)
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 {
261 if (PreceedMatch == false)
262 break;
263
264 if (strlen(argv[I]) >= sizeof(Buffer))
265 return _error->Error(_("Option '%s' is too long"),argv[I]);
266
267 // Skip the leading dash
268 const char *J = argv[I];
269 for (; *J != 0 && *J == '-'; J++);
270
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
284 // Check for boolean
285 Sense = StringToBool(Argument);
286 if (Sense >= 0)
287 {
288 // Eat the argument
289 if (Argument != Buffer)
290 {
291 Opt += strlen(Opt);
292 I += IncI;
293 }
294 break;
295 }
296
297 if (CertainArg == true)
298 return _error->Error(_("Sense %s is not understood, try true or false."),Argument);
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 /*}}}*/
316 // CommandLine::FileSize - Count the number of filenames /*{{{*/
317 // ---------------------------------------------------------------------
318 /* */
319 unsigned 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 /*}}}*/
327 // CommandLine::DispatchArg - Do something with the first arg /*{{{*/
328 // ---------------------------------------------------------------------
329 /* */
330 bool CommandLine::DispatchArg(Dispatch *Map,bool NoMatch)
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)
346 {
347 if (NoMatch == true)
348 _error->Error(_("Invalid operation %s"),FileList[0]);
349 }
350
351 return false;
352 }
353 /*}}}*/