]>
git.saurik.com Git - apt.git/blob - apt-pkg/contrib/cmndline.cc
34e90da207c0578cf02d4368f4de066f0b4fa33e
1 // -*- mode: cpp; mode: fold -*-
3 // $Id: cmndline.cc,v 1.15 2003/02/10 01:40:58 doogie Exp $
4 /* ######################################################################
6 Command Line Class - Sophisticated command line parser
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>.
11 ##################################################################### */
13 // Include files /*{{{*/
16 #include <apt-pkg/cmndline.h>
17 #include <apt-pkg/error.h>
18 #include <apt-pkg/strutl.h>
24 // CommandLine::CommandLine - Constructor /*{{{*/
25 // ---------------------------------------------------------------------
27 CommandLine::CommandLine(Args
*AList
,Configuration
*Conf
) : ArgList(AList
),
28 Conf(Conf
), FileList(0)
32 // CommandLine::~CommandLine - Destructor /*{{{*/
33 // ---------------------------------------------------------------------
35 CommandLine::~CommandLine()
40 // CommandLine::Parse - Main action member /*{{{*/
41 // ---------------------------------------------------------------------
43 bool CommandLine::Parse(int argc
,const char **argv
)
46 FileList
= new const char *[argc
];
47 const char **Files
= FileList
;
49 for (I
= 1; I
!= argc
; I
++)
51 const char *Opt
= argv
[I
];
53 // It is not an option
62 // Double dash signifies the end of option processing
63 if (*Opt
== '-' && Opt
[1] == 0)
69 // Single dash is a short option
72 // Iterate over each letter
75 // Search for the option
77 for (A
= ArgList
; A
->end() == false && A
->ShortOpt
!= *Opt
; A
++);
79 return _error
->Error(_("Command line option '%c' [from %s] is not known."),*Opt
,argv
[I
]);
81 if (HandleOpt(I
,argc
,argv
,Opt
,A
) == false)
91 // Match up to a = against the list
92 const char *OptEnd
= Opt
;
94 for (; *OptEnd
!= 0 && *OptEnd
!= '='; OptEnd
++);
95 for (A
= ArgList
; A
->end() == false &&
96 stringcasecmp(Opt
,OptEnd
,A
->LongOpt
) != 0; A
++);
98 // Failed, look for a word after the first - (no-foo)
99 bool PreceedMatch
= false;
100 if (A
->end() == true)
102 for (; Opt
!= OptEnd
&& *Opt
!= '-'; Opt
++);
105 return _error
->Error(_("Command line option %s is not understood"),argv
[I
]);
108 for (A
= ArgList
; A
->end() == false &&
109 stringcasecmp(Opt
,OptEnd
,A
->LongOpt
) != 0; A
++);
112 if (A
->end() == true && OptEnd
- Opt
!= 1)
113 return _error
->Error(_("Command line option %s is not understood"),argv
[I
]);
115 // The option could be a single letter option prefixed by a no-..
116 if (A
->end() == true)
118 for (A
= ArgList
; A
->end() == false && A
->ShortOpt
!= *Opt
; A
++);
120 if (A
->end() == true)
121 return _error
->Error(_("Command line option %s is not understood"),argv
[I
]);
124 // The option is not boolean
125 if (A
->IsBoolean() == false)
126 return _error
->Error(_("Command line option %s is not boolean"),argv
[I
]);
132 if (HandleOpt(I
,argc
,argv
,OptEnd
,A
,PreceedMatch
) == false)
136 // Copy any remaining file names over
137 for (; I
!= argc
; I
++)
141 SaveInConfig(argc
, argv
);
146 // CommandLine::HandleOpt - Handle a single option including all flags /*{{{*/
147 // ---------------------------------------------------------------------
148 /* This is a helper function for parser, it looks at a given argument
149 and looks for specific patterns in the string, it gets tokanized
150 -ruffly- like -*[yes|true|enable]-(o|longopt)[=][ ][argument] */
151 bool CommandLine::HandleOpt(int &I
,int argc
,const char *argv
[],
152 const char *&Opt
,Args
*A
,bool PreceedMatch
)
154 const char *Argument
= 0;
155 bool CertainArg
= false;
158 /* Determine the possible location of an option or 0 if their is
160 if (Opt
[1] == 0 || (Opt
[1] == '=' && Opt
[2] == 0))
162 if (I
+ 1 < argc
&& argv
[I
+1][0] != '-')
163 Argument
= argv
[I
+1];
165 // Equals was specified but we fell off the end!
166 if (Opt
[1] == '=' && Argument
== 0)
167 return _error
->Error(_("Option %s requires an argument."),argv
[I
]);
184 // Option is an argument set
185 if ((A
->Flags
& HasArg
) == HasArg
)
188 return _error
->Error(_("Option %s requires an argument."),argv
[I
]);
192 // Parse a configuration file
193 if ((A
->Flags
& ConfigFile
) == ConfigFile
)
194 return ReadConfigFile(*Conf
,Argument
);
196 // Arbitrary item specification
197 if ((A
->Flags
& ArbItem
) == ArbItem
)
200 for (J
= Argument
; *J
!= 0 && *J
!= '='; J
++);
202 return _error
->Error(_("Option %s: Configuration item specification must have an =<val>."),argv
[I
]);
208 return _error
->Error(_("Option %s: Configuration item specification must have an =<val>."),argv
[I
]);
209 Conf
->Set(string(Argument
,J
-Argument
),string(argv
[I
++ +1]));
212 Conf
->Set(string(Argument
,J
-Argument
),string(J
+1));
217 const char *I
= A
->ConfName
;
218 for (; *I
!= 0 && *I
!= ' '; I
++);
220 Conf
->Set(string(A
->ConfName
,0,I
-A
->ConfName
),string(I
+1) + Argument
);
222 Conf
->Set(A
->ConfName
,string(I
) + Argument
);
227 // Option is an integer level
228 if ((A
->Flags
& IntLevel
) == IntLevel
)
230 // There might be an argument
234 unsigned long Value
= strtol(Argument
,&EndPtr
,10);
236 // Conversion failed and the argument was specified with an =s
237 if (EndPtr
== Argument
&& CertainArg
== true)
238 return _error
->Error(_("Option %s requires an integer argument, not '%s'"),argv
[I
],Argument
);
240 // Conversion was ok, set the value and return
241 if (EndPtr
!= 0 && EndPtr
!= Argument
&& *EndPtr
== 0)
243 Conf
->Set(A
->ConfName
,Value
);
250 // Increase the level
251 Conf
->Set(A
->ConfName
,Conf
->FindI(A
->ConfName
)+1);
255 // Option is a boolean
256 int Sense
= -1; // -1 is unspecified, 0 is yes 1 is no
258 // Look for an argument.
261 // Look at preceeding text
265 if (PreceedMatch
== false)
268 if (strlen(argv
[I
]) >= sizeof(Buffer
))
269 return _error
->Error(_("Option '%s' is too long"),argv
[I
]);
271 // Skip the leading dash
272 const char *J
= argv
[I
];
273 for (; *J
!= 0 && *J
== '-'; J
++);
275 const char *JEnd
= J
;
276 for (; *JEnd
!= 0 && *JEnd
!= '-'; JEnd
++);
279 strncpy(Buffer
,J
,JEnd
- J
);
280 Buffer
[JEnd
- J
] = 0;
289 Sense
= StringToBool(Argument
);
293 if (Argument
!= Buffer
)
301 if (CertainArg
== true)
302 return _error
->Error(_("Sense %s is not understood, try true or false."),Argument
);
307 // Indeterminate sense depends on the flag
310 if ((A
->Flags
& InvBoolean
) == InvBoolean
)
316 Conf
->Set(A
->ConfName
,Sense
);
320 // CommandLine::FileSize - Count the number of filenames /*{{{*/
321 // ---------------------------------------------------------------------
323 unsigned int CommandLine::FileSize() const
325 unsigned int Count
= 0;
326 for (const char **I
= FileList
; I
!= 0 && *I
!= 0; I
++)
331 // CommandLine::DispatchArg - Do something with the first arg /*{{{*/
332 // ---------------------------------------------------------------------
334 bool CommandLine::DispatchArg(Dispatch
*Map
,bool NoMatch
)
337 for (I
= 0; Map
[I
].Match
!= 0; I
++)
339 if (strcmp(FileList
[0],Map
[I
].Match
) == 0)
341 bool Res
= Map
[I
].Handler(*this);
342 if (Res
== false && _error
->PendingError() == false)
343 _error
->Error("Handler silently failed");
349 if (Map
[I
].Match
== 0)
352 _error
->Error(_("Invalid operation %s"),FileList
[0]);
358 // CommandLine::SaveInConfig - for output later in a logfile or so /*{{{*/
359 // ---------------------------------------------------------------------
360 /* We save the commandline here to have it around later for e.g. logging.
361 It feels a bit like a hack here and isn't bulletproof, but it is better
362 than nothing after all. */
363 void CommandLine::SaveInConfig(unsigned int const &argc
, char const * const * const argv
)
365 char cmdline
[100 + argc
* 50];
366 unsigned int length
= 0;
367 bool lastWasOption
= false;
368 bool closeQuote
= false;
369 for (unsigned int i
= 0; i
< argc
&& length
< sizeof(cmdline
); ++i
, ++length
)
371 for (unsigned int j
= 0; argv
[i
][j
] != '\0' && length
< sizeof(cmdline
)-1; ++j
, ++length
)
373 cmdline
[length
] = argv
[i
][j
];
374 if (lastWasOption
== true && argv
[i
][j
] == '=')
376 // That is possibly an option: Quote it if it includes spaces,
377 // the benefit is that this will eliminate also most false positives
378 const char* c
= &argv
[i
][j
+1];
379 for (; *c
!= '\0' && *c
!= ' '; ++c
);
380 if (*c
== '\0') continue;
381 cmdline
[++length
] = '"';
385 if (closeQuote
== true)
386 cmdline
[length
++] = '"';
387 // Problem: detects also --hello
388 if (cmdline
[length
-1] == 'o')
389 lastWasOption
= true;
390 cmdline
[length
] = ' ';
392 cmdline
[--length
] = '\0';
393 _config
->Set("CommandLine::AsString", cmdline
);