]> git.decadent.org.uk Git - dak.git/blob - tools/dsync-0.0/libdsync/contrib/cmndline.cc
Merge commit 'godog/master' into merge
[dak.git] / tools / dsync-0.0 / libdsync / contrib / cmndline.cc
1 // -*- mode: cpp; mode: fold -*-
2 // Description                                                          /*{{{*/
3 // $Id: cmndline.cc,v 1.6 1999/11/17 05:59:29 jgg Exp $
4 /* ######################################################################
5
6    Command Line Class - Sophisticated command line parser
7    
8    ##################################################################### */
9                                                                         /*}}}*/
10 // Include files                                                        /*{{{*/
11 #ifdef __GNUG__
12 #pragma implementation "dsync/cmndline.h"
13 #endif
14 #include <dsync/cmndline.h>
15 #include <dsync/error.h>
16 #include <dsync/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          
100          for (A = ArgList; A->end() == false &&
101               stringcasecmp(Opt,OptEnd,A->LongOpt) != 0; A++);
102
103          // Failed again..
104          if (A->end() == true && OptEnd - Opt != 1)
105             return _error->Error("Command line option %s is not understood",argv[I]);
106
107          // The option could be a single letter option prefixed by a no-..
108          if (A->end() == true)
109          {
110             for (A = ArgList; A->end() == false && A->ShortOpt != *Opt; A++);
111             
112             if (A->end() == true)
113                return _error->Error("Command line option %s is not understood",argv[I]);
114          }
115          
116          // The option is not boolean
117          if (A->IsBoolean() == false)
118             return _error->Error("Command line option %s is not boolean",argv[I]);
119          PreceedMatch = true;
120       }
121       
122       // Deal with it.
123       OptEnd--;
124       if (HandleOpt(I,argc,argv,OptEnd,A,PreceedMatch) == false)
125          return false;
126    }
127    
128    // Copy any remaining file names over
129    for (; I != argc; I++)
130       *Files++ = argv[I];
131    *Files = 0;
132    
133    return true;
134 }
135                                                                         /*}}}*/
136 // CommandLine::HandleOpt - Handle a single option including all flags  /*{{{*/
137 // ---------------------------------------------------------------------
138 /* This is a helper function for parser, it looks at a given argument
139    and looks for specific patterns in the string, it gets tokanized
140    -ruffly- like -*[yes|true|enable]-(o|longopt)[=][ ][argument] */
141 bool CommandLine::HandleOpt(int &I,int argc,const char *argv[],
142                             const char *&Opt,Args *A,bool PreceedMatch)
143 {
144    const char *Argument = 0;
145    bool CertainArg = false;
146    int IncI = 0;
147
148    /* Determine the possible location of an option or 0 if their is
149       no option */
150    if (Opt[1] == 0 || (Opt[1] == '=' && Opt[2] == 0))
151    {
152       if (I + 1 < argc && argv[I+1][0] != '-')
153          Argument = argv[I+1];
154       
155       // Equals was specified but we fell off the end!
156       if (Opt[1] == '=' && Argument == 0)
157          return _error->Error("Option %s requires an argument.",argv[I]);
158       if (Opt[1] == '=')
159          CertainArg = true;
160          
161       IncI = 1;
162    }
163    else
164    {
165       if (Opt[1] == '=')
166       {
167          CertainArg = true;
168          Argument = Opt + 2;
169       }      
170       else
171          Argument = Opt + 1;
172    }
173    
174    // Option is an argument set
175    if ((A->Flags & HasArg) == HasArg)
176    {
177       if (Argument == 0)
178          return _error->Error("Option %s requires an argument.",argv[I]);
179       Opt += strlen(Opt);
180       I += IncI;
181       
182       // Parse a configuration file
183       if ((A->Flags & ConfigFile) == ConfigFile)
184          return ReadConfigFile(*Conf,Argument);
185
186       // Arbitary item specification
187       if ((A->Flags & ArbItem) == ArbItem)
188       {
189          const char *J;
190          for (J = Argument; *J != 0 && *J != '='; J++);
191          if (*J == 0)
192             return _error->Error("Option %s: Configuration item sepecification must have an =<val>.",argv[I]);
193
194          // = is trailing
195          if (J[1] == 0)
196          {
197             if (I+1 >= argc)
198                return _error->Error("Option %s: Configuration item sepecification must have an =<val>.",argv[I]);
199             Conf->Set(string(Argument,J-Argument),string(argv[I++ +1]));
200          }
201          else
202             Conf->Set(string(Argument,J-Argument),string(J+1));
203          
204          return true;
205       }
206       
207       const char *I = A->ConfName;
208       for (; *I != 0 && *I != ' '; I++);
209       if (*I == ' ')
210          Conf->Set(string(A->ConfName,0,I-A->ConfName),string(I+1) + Argument);
211       else
212          Conf->Set(A->ConfName,string(I) + Argument);
213          
214       return true;
215    }
216    
217    // Option is an integer level
218    if ((A->Flags & IntLevel) == IntLevel)
219    {
220       // There might be an argument
221       if (Argument != 0)
222       {
223          char *EndPtr;
224          unsigned long Value = strtol(Argument,&EndPtr,10);
225          
226          // Conversion failed and the argument was specified with an =s
227          if (EndPtr == Argument && CertainArg == true)
228             return _error->Error("Option %s requires an integer argument, not '%s'",argv[I],Argument);
229
230          // Conversion was ok, set the value and return
231          if (EndPtr != 0 && EndPtr != Argument && *EndPtr == 0)
232          {
233             Conf->Set(A->ConfName,Value);
234             Opt += strlen(Opt);
235             I += IncI;
236             return true;
237          }       
238       }      
239       
240       // Increase the level
241       Conf->Set(A->ConfName,Conf->FindI(A->ConfName)+1);
242       return true;
243    }
244   
245    // Option is a boolean
246    int Sense = -1;  // -1 is unspecified, 0 is yes 1 is no
247
248    // Look for an argument.
249    while (1)
250    {
251       // Look at preceeding text
252       char Buffer[300];
253       if (Argument == 0)
254       {
255          if (PreceedMatch == false)
256             break;
257          
258          if (strlen(argv[I]) >= sizeof(Buffer))
259             return _error->Error("Option '%s' is too long",argv[I]);
260
261          // Skip the leading dash
262          const char *J = argv[I];
263          for (; *J != 0 && *J == '-'; J++);
264          
265          const char *JEnd = J;
266          for (; *JEnd != 0 && *JEnd != '-'; JEnd++);
267          if (*JEnd != 0)
268          {
269             strncpy(Buffer,J,JEnd - J);
270             Buffer[JEnd - J] = 0;
271             Argument = Buffer;
272             CertainArg = true;
273          }       
274          else
275             break;
276       }
277
278       // Check for boolean
279       Sense = StringToBool(Argument);
280       if (Sense >= 0)
281       {
282          // Eat the argument     
283          if (Argument != Buffer)
284          {
285             Opt += strlen(Opt);
286             I += IncI;
287          }       
288          break;
289       }
290
291       if (CertainArg == true)
292          return _error->Error("Sense %s is not understood, try true or false.",Argument);
293       
294       Argument = 0;
295    }
296       
297    // Indeterminate sense depends on the flag
298    if (Sense == -1)
299    {
300       if ((A->Flags & InvBoolean) == InvBoolean)
301          Sense = 0;
302       else
303          Sense = 1;
304    }
305    
306    Conf->Set(A->ConfName,Sense);
307    return true;
308 }
309                                                                         /*}}}*/
310 // CommandLine::FileSize - Count the number of filenames                /*{{{*/
311 // ---------------------------------------------------------------------
312 /* */
313 unsigned int CommandLine::FileSize() const
314 {
315    unsigned int Count = 0;
316    for (const char **I = FileList; I != 0 && *I != 0; I++)
317       Count++;
318    return Count;
319 }
320                                                                         /*}}}*/
321 // CommandLine::DispatchArg - Do something with the first arg           /*{{{*/
322 // ---------------------------------------------------------------------
323 /* */
324 bool CommandLine::DispatchArg(Dispatch *Map,bool NoMatch)
325 {
326    int I;
327    for (I = 0; Map[I].Match != 0; I++)
328    {
329       if (strcmp(FileList[0],Map[I].Match) == 0)
330       {
331          bool Res = Map[I].Handler(*this);
332          if (Res == false && _error->PendingError() == false)
333             _error->Error("Handler silently failed");
334          return Res;
335       }
336    }
337    
338    // No matching name
339    if (Map[I].Match == 0)
340    {
341       if (NoMatch == true)
342          _error->Error("Invalid operation %s",FileList[0]);
343    }
344    
345    return false;
346 }
347                                                                         /*}}}*/