Join with aliencode
[ntk/apt.git] / apt-pkg / contrib / cmndline.cc
1 // -*- mode: cpp; mode: fold -*-
2 // Description /*{{{*/
3 // $Id: cmndline.cc,v 1.11 2001/02/20 07:03:17 jgg Exp $
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>
16 #include <apt-pkg/strutl.h>
17
18 #include <apti18n.h>
19 /*}}}*/
20
21 // CommandLine::CommandLine - Constructor /*{{{*/
22 // ---------------------------------------------------------------------
23 /* */
24 CommandLine::CommandLine(Args *AList,Configuration *Conf) : ArgList(AList),
25 Conf(Conf), FileList(0)
26 {
27 }
28 /*}}}*/
29 // CommandLine::~CommandLine - Destructor /*{{{*/
30 // ---------------------------------------------------------------------
31 /* */
32 CommandLine::~CommandLine()
33 {
34 delete [] FileList;
35 }
36 /*}}}*/
37 // CommandLine::Parse - Main action member /*{{{*/
38 // ---------------------------------------------------------------------
39 /* */
40 bool CommandLine::Parse(int argc,const char **argv)
41 {
42 delete [] FileList;
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)
73 return _error->Error(_("Command line option '%c' [from %s] is not known."),*Opt,argv[I]);
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)
93 bool PreceedMatch = false;
94 if (A->end() == true)
95 {
96 for (; Opt != OptEnd && *Opt != '-'; Opt++);
97
98 if (Opt == OptEnd)
99 return _error->Error(_("Command line option %s is not understood"),argv[I]);
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)
107 return _error->Error(_("Command line option %s is not understood"),argv[I]);
108
109 // The option could be a single letter option prefixed by a no-..
110 if (A->end() == true)
111 {
112 for (A = ArgList; A->end() == false && A->ShortOpt != *Opt; A++);
113
114 if (A->end() == true)
115 return _error->Error(_("Command line option %s is not understood"),argv[I]);
116 }
117
118 // The option is not boolean
119 if (A->IsBoolean() == false)
120 return _error->Error(_("Command line option %s is not boolean"),argv[I]);
121 PreceedMatch = true;
122 }
123
124 // Deal with it.
125 OptEnd--;
126 if (HandleOpt(I,argc,argv,OptEnd,A,PreceedMatch) == false)
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] */
143 bool CommandLine::HandleOpt(int &I,int argc,const char *argv[],
144 const char *&Opt,Args *A,bool PreceedMatch)
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)
159 return _error->Error(_("Option %s requires an argument."),argv[I]);
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 }
175
176 // Option is an argument set
177 if ((A->Flags & HasArg) == HasArg)
178 {
179 if (Argument == 0)
180 return _error->Error(_("Option %s requires an argument."),argv[I]);
181 Opt += strlen(Opt);
182 I += IncI;
183
184 // Parse a configuration file
185 if ((A->Flags & ConfigFile) == ConfigFile)
186 return ReadConfigFile(*Conf,Argument);
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)
194 return _error->Error(_("Option %s: Configuration item sepecification must have an =<val>."),argv[I]);
195
196 // = is trailing
197 if (J[1] == 0)
198 {
199 if (I+1 >= argc)
200 return _error->Error(_("Option %s: Configuration item sepecification must have an =<val>."),argv[I]);
201 Conf->Set(string(Argument,J-Argument),string(argv[I++ +1]));
202 }
203 else
204 Conf->Set(string(Argument,J-Argument),string(J+1));
205
206 return true;
207 }
208
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
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)
230 return _error->Error(_("Option %s requires an integer argument, not '%s'"),argv[I],Argument);
231
232 // Conversion was ok, set the value and return
233 if (EndPtr != 0 && EndPtr != Argument && *EndPtr == 0)
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 {
257 if (PreceedMatch == false)
258 break;
259
260 if (strlen(argv[I]) >= sizeof(Buffer))
261 return _error->Error(_("Option '%s' is too long"),argv[I]);
262
263 // Skip the leading dash
264 const char *J = argv[I];
265 for (; *J != 0 && *J == '-'; J++);
266
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
280 // Check for boolean
281 Sense = StringToBool(Argument);
282 if (Sense >= 0)
283 {
284 // Eat the argument
285 if (Argument != Buffer)
286 {
287 Opt += strlen(Opt);
288 I += IncI;
289 }
290 break;
291 }
292
293 if (CertainArg == true)
294 return _error->Error(_("Sense %s is not understood, try true or false."),Argument);
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 /*}}}*/
312 // CommandLine::FileSize - Count the number of filenames /*{{{*/
313 // ---------------------------------------------------------------------
314 /* */
315 unsigned 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 /*}}}*/
323 // CommandLine::DispatchArg - Do something with the first arg /*{{{*/
324 // ---------------------------------------------------------------------
325 /* */
326 bool CommandLine::DispatchArg(Dispatch *Map,bool NoMatch)
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)
342 {
343 if (NoMatch == true)
344 _error->Error(_("Invalid operation %s"),FileList[0]);
345 }
346
347 return false;
348 }
349 /*}}}*/