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