1 /*-
2  ***********************************************************************
3  *
4  * $Id: options.c,v 1.7 2014/07/18 06:40:44 mavrik Exp $
5  *
6  ***********************************************************************
7  *
8  * Copyright 2006-2014 The FTimes Project, All Rights Reserved.
9  *
10  ***********************************************************************
11  */
12 #include "all-includes.h"
13 
14 /*-
15  ***********************************************************************
16  *
17  * OptionsFreeOptionsContext
18  *
19  ***********************************************************************
20  */
21 void
OptionsFreeOptionsContext(OPTIONS_CONTEXT * psOptionsContext)22 OptionsFreeOptionsContext(OPTIONS_CONTEXT *psOptionsContext)
23 {
24   if (psOptionsContext != NULL)
25   {
26     if (psOptionsContext->piArgumentTypes != NULL)
27     {
28       free(psOptionsContext->piArgumentTypes);
29     }
30     free(psOptionsContext);
31   }
32 }
33 
34 
35 /*-
36  ***********************************************************************
37  *
38  * OptionsGetArgumentIndex
39  *
40  ***********************************************************************
41  */
42 int
OptionsGetArgumentIndex(OPTIONS_CONTEXT * psOptionsContext)43 OptionsGetArgumentIndex(OPTIONS_CONTEXT *psOptionsContext)
44 {
45   return psOptionsContext->iArgumentIndex;
46 }
47 
48 
49 /*-
50  ***********************************************************************
51  *
52  * OptionsGetArgumentsLeft
53  *
54  ***********************************************************************
55  */
56 int
OptionsGetArgumentsLeft(OPTIONS_CONTEXT * psOptionsContext)57 OptionsGetArgumentsLeft(OPTIONS_CONTEXT *psOptionsContext)
58 {
59   return (psOptionsContext->iArgumentCount - (psOptionsContext->iArgumentIndex + 1));
60 }
61 
62 
63 /*-
64  ***********************************************************************
65  *
66  * OptionsGetCommand
67  *
68  ***********************************************************************
69  */
70 TCHAR *
OptionsGetCommand(OPTIONS_CONTEXT * psOptionsContext)71 OptionsGetCommand(OPTIONS_CONTEXT *psOptionsContext)
72 {
73   return psOptionsContext->pptcArgumentVector[0];
74 }
75 
76 
77 /*-
78  ***********************************************************************
79  *
80  * OptionsGetCurrentArgument
81  *
82  ***********************************************************************
83  */
84 TCHAR *
OptionsGetCurrentArgument(OPTIONS_CONTEXT * psOptionsContext)85 OptionsGetCurrentArgument(OPTIONS_CONTEXT *psOptionsContext)
86 {
87   TCHAR              *ptcArgument = NULL;
88 
89   if (psOptionsContext->iArgumentIndex < psOptionsContext->iArgumentCount)
90   {
91     ptcArgument = psOptionsContext->pptcArgumentVector[psOptionsContext->iArgumentIndex];
92   }
93 
94   return ptcArgument;
95 }
96 
97 
98 /*-
99  ***********************************************************************
100  *
101  * OptionsGetCurrentOperand
102  *
103  ***********************************************************************
104  */
105 TCHAR *
OptionsGetCurrentOperand(OPTIONS_CONTEXT * psOptionsContext)106 OptionsGetCurrentOperand(OPTIONS_CONTEXT *psOptionsContext)
107 {
108   TCHAR              *ptcOperand = NULL;
109 
110   if (psOptionsContext->iOperandIndex < psOptionsContext->iArgumentCount)
111   {
112     ptcOperand = psOptionsContext->pptcArgumentVector[psOptionsContext->iOperandIndex];
113   }
114 
115   return ptcOperand;
116 }
117 
118 
119 /*-
120  ***********************************************************************
121  *
122  * OptionsGetFirstArgument
123  *
124  ***********************************************************************
125  */
126 TCHAR *
OptionsGetFirstArgument(OPTIONS_CONTEXT * psOptionsContext)127 OptionsGetFirstArgument(OPTIONS_CONTEXT *psOptionsContext)
128 {
129   TCHAR              *ptcArgument = NULL;
130 
131   psOptionsContext->iArgumentIndex = 1;
132   if (psOptionsContext->iArgumentIndex < psOptionsContext->iArgumentCount)
133   {
134     ptcArgument = psOptionsContext->pptcArgumentVector[psOptionsContext->iArgumentIndex];
135   }
136 
137   return ptcArgument;
138 }
139 
140 
141 /*-
142  ***********************************************************************
143  *
144  * OptionsGetFirstOperand
145  *
146  ***********************************************************************
147  */
148 TCHAR *
OptionsGetFirstOperand(OPTIONS_CONTEXT * psOptionsContext)149 OptionsGetFirstOperand(OPTIONS_CONTEXT *psOptionsContext)
150 {
151   TCHAR              *ptcOperand = NULL;
152 
153   for (psOptionsContext->iOperandIndex = 0; psOptionsContext->iOperandIndex < psOptionsContext->iArgumentCount; psOptionsContext->iOperandIndex++)
154   {
155     if (psOptionsContext->piArgumentTypes[psOptionsContext->iOperandIndex] == OPTIONS_ARGUMENT_TYPE_OPERAND)
156     {
157       ptcOperand = psOptionsContext->pptcArgumentVector[psOptionsContext->iOperandIndex];
158       break;
159     }
160   }
161 
162   return ptcOperand;
163 }
164 
165 
166 /*-
167  ***********************************************************************
168  *
169  * OptionsGetNextArgument
170  *
171  ***********************************************************************
172  */
173 TCHAR *
OptionsGetNextArgument(OPTIONS_CONTEXT * psOptionsContext)174 OptionsGetNextArgument(OPTIONS_CONTEXT *psOptionsContext)
175 {
176   TCHAR              *ptcArgument = NULL;
177 
178   if ((psOptionsContext->iArgumentIndex + 1) < psOptionsContext->iArgumentCount)
179   {
180     ptcArgument = psOptionsContext->pptcArgumentVector[++psOptionsContext->iArgumentIndex];
181   }
182 
183   return ptcArgument;
184 }
185 
186 
187 /*-
188  ***********************************************************************
189  *
190  * OptionsGetNextOperand
191  *
192  ***********************************************************************
193  */
194 TCHAR *
OptionsGetNextOperand(OPTIONS_CONTEXT * psOptionsContext)195 OptionsGetNextOperand(OPTIONS_CONTEXT *psOptionsContext)
196 {
197   TCHAR              *ptcOperand = NULL;
198 
199   while ((psOptionsContext->iOperandIndex + 1) < psOptionsContext->iArgumentCount)
200   {
201     if (psOptionsContext->piArgumentTypes[++psOptionsContext->iOperandIndex] == OPTIONS_ARGUMENT_TYPE_OPERAND)
202     {
203       ptcOperand = psOptionsContext->pptcArgumentVector[psOptionsContext->iOperandIndex];
204       break;
205     }
206   }
207 
208   return ptcOperand;
209 }
210 
211 
212 /*-
213  ***********************************************************************
214  *
215  * OptionsGetOperandCount
216  *
217  ***********************************************************************
218  */
219 int
OptionsGetOperandCount(OPTIONS_CONTEXT * psOptionsContext)220 OptionsGetOperandCount(OPTIONS_CONTEXT *psOptionsContext)
221 {
222   int                 iOperandCount = 0;
223   int                 iOperandIndex = 0;
224 
225   for (iOperandIndex = 0; iOperandIndex < psOptionsContext->iArgumentCount; iOperandIndex++)
226   {
227     if (psOptionsContext->piArgumentTypes[iOperandIndex] == OPTIONS_ARGUMENT_TYPE_OPERAND)
228     {
229       iOperandCount++;
230     }
231   }
232 
233   return iOperandCount;
234 }
235 
236 
237 /*-
238  ***********************************************************************
239  *
240  * OptionsHaveRequiredOptions
241  *
242  ***********************************************************************
243  */
244 int
OptionsHaveRequiredOptions(OPTIONS_CONTEXT * psOptionsContext)245 OptionsHaveRequiredOptions(OPTIONS_CONTEXT *psOptionsContext)
246 {
247   int                 iIndex = 0;
248 
249   for (iIndex = 0; iIndex < psOptionsContext->iNOptions; iIndex++)
250   {
251     if (psOptionsContext->psOptions[iIndex].iRequired == 1 && psOptionsContext->psOptions[iIndex].iFound == 0)
252     {
253       return 0;
254     }
255   }
256 
257   return 1;
258 }
259 
260 
261 /*-
262  ***********************************************************************
263  *
264  * OptionsHaveSpecifiedOption
265  *
266  ***********************************************************************
267  */
268 int
OptionsHaveSpecifiedOption(OPTIONS_CONTEXT * psOptionsContext,int iId)269 OptionsHaveSpecifiedOption(OPTIONS_CONTEXT *psOptionsContext, int iId)
270 {
271   int                 iIndex = 0;
272 
273   for (iIndex = 0; iIndex < psOptionsContext->iNOptions; iIndex++)
274   {
275     if (iId == psOptionsContext->psOptions[iIndex].iId && psOptionsContext->psOptions[iIndex].iFound)
276     {
277       return 1;
278     }
279   }
280 
281   return 0;
282 }
283 
284 
285 /*-
286  ***********************************************************************
287  *
288  * OptionsNewOptionsContext
289  *
290  ***********************************************************************
291  */
292 OPTIONS_CONTEXT *
OptionsNewOptionsContext(int iArgumentCount,TCHAR * pptcArgumentVector[],TCHAR * ptcError)293 OptionsNewOptionsContext(int iArgumentCount, TCHAR *pptcArgumentVector[], TCHAR *ptcError)
294 {
295   OPTIONS_CONTEXT    *psOptionsContext = NULL;
296 
297   /*-
298    *********************************************************************
299    *
300    * Allocate and clear memory for the structure.
301    *
302    *********************************************************************
303    */
304   psOptionsContext = (OPTIONS_CONTEXT *) calloc(sizeof(OPTIONS_CONTEXT), 1);
305   if (psOptionsContext == NULL)
306   {
307     _sntprintf(ptcError, MESSAGE_SIZE, _T("OptionsNewOptionsContext(): calloc(): %s"), strerror(errno));
308     return NULL;
309   }
310 
311   /*-
312    *********************************************************************
313    *
314    * Allocate and clear memory for the argument types.
315    *
316    *********************************************************************
317    */
318   psOptionsContext->piArgumentTypes = (int *) calloc(sizeof(int), iArgumentCount);
319   if (psOptionsContext->piArgumentTypes == NULL)
320   {
321     _sntprintf(ptcError, MESSAGE_SIZE, _T("OptionsNewOptionsContext(): calloc(): %s"), strerror(errno));
322     return NULL;
323   }
324 
325   /*-
326    *********************************************************************
327    *
328    * Initialize members.
329    *
330    *********************************************************************
331    */
332   psOptionsContext->pptcArgumentVector = pptcArgumentVector;
333   psOptionsContext->iArgumentCount = iArgumentCount;
334   psOptionsContext->piArgumentTypes[0] = OPTIONS_ARGUMENT_TYPE_PROGRAM;
335 
336   return psOptionsContext;
337 }
338 
339 
340 /*-
341  ***********************************************************************
342  *
343  * OptionsProcessOptions
344  *
345  ***********************************************************************
346  */
347 int
OptionsProcessOptions(OPTIONS_CONTEXT * psOptionsContext,void * pvProperties,TCHAR * ptcError)348 OptionsProcessOptions(OPTIONS_CONTEXT *psOptionsContext, void *pvProperties, TCHAR *ptcError)
349 {
350   TCHAR               atcLocalError[MESSAGE_SIZE] = { 0 };
351   TCHAR              *ptcArgument = NULL;
352   int                 iError = 0;
353   int                 iIndex = 0;
354   int                 iStopIndex = psOptionsContext->iArgumentIndex;
355   int                 iStopIndexSaved = 0;
356   OPTIONS_TABLE      *psOptions = NULL;
357 
358   /*-
359    *********************************************************************
360    *
361    * Define local variables to make the code easier to read.
362    *
363    *********************************************************************
364    */
365   psOptions = psOptionsContext->psOptions;
366 
367   /*-
368    *********************************************************************
369    *
370    * Walk the argument list.
371    *
372    *********************************************************************
373    */
374   while ((ptcArgument = OptionsGetNextArgument(psOptionsContext)) != NULL)
375   {
376     /*-
377      *******************************************************************
378      *
379      * Check for the end of options token ("--"). If it is found, save
380      * the current argument index, classify the remaining arguments as
381      * operands, and break.
382      *
383      *******************************************************************
384      */
385     if (_tcscmp(ptcArgument, _T("--")) == 0)
386     {
387       OptionsSetArgumentType(psOptionsContext, OPTIONS_ARGUMENT_TYPE_END_OF_OPTIONS);
388       if (!iStopIndexSaved)
389       {
390         iStopIndex = psOptionsContext->iArgumentIndex;
391         iStopIndexSaved = 1;
392       }
393       while ((ptcArgument = OptionsGetNextArgument(psOptionsContext)) != NULL)
394       {
395         OptionsSetArgumentType(psOptionsContext, OPTIONS_ARGUMENT_TYPE_OPERAND);
396       }
397       break;
398     }
399 
400     /*-
401      *******************************************************************
402      *
403      * Scan the options table looking for a match on the nick name or
404      * the full name. If a match is found, pass the option and its
405      * argument, if any, to the designated handler.
406      *
407      *******************************************************************
408      */
409     for (iIndex = 0; iIndex < psOptionsContext->iNOptions; iIndex++)
410     {
411       if (
412            (psOptions[iIndex].atcNickName[0] && _tcscmp(ptcArgument, psOptions[iIndex].atcNickName) == 0) ||
413            (psOptions[iIndex].atcFullName[0] && _tcscmp(ptcArgument, psOptions[iIndex].atcFullName) == 0)
414          )
415       {
416         if (psOptions[iIndex].iAllowRepeats == 0 && psOptions[iIndex].iFound == 1)
417         {
418           _sntprintf(ptcError, MESSAGE_SIZE, _T("OptionsProcessOptions(): The %s option may not be specified more than once."), ptcArgument);
419           return OPTIONS_ER;
420         }
421         OptionsSetArgumentType(psOptionsContext, OPTIONS_ARGUMENT_TYPE_OPTION);
422 /* TODO: Modify this section (down to the break) to support multiple arguments per option. */
423         if (psOptions[iIndex].iNArguments > 0)
424         {
425           if (OptionsGetArgumentsLeft(psOptionsContext) < 1)
426           {
427             return OPTIONS_USAGE;
428           }
429           ptcArgument = OptionsGetNextArgument(psOptionsContext);
430           OptionsSetArgumentType(psOptionsContext, OPTIONS_ARGUMENT_TYPE_OPTION_ARGUMENT);
431         }
432         else
433         {
434           ptcArgument = NULL;
435         }
436         psOptions[iIndex].iFound = 1;
437         iError = psOptions[iIndex].piHandler(&psOptions[iIndex], ptcArgument, pvProperties, atcLocalError);
438         if (iError != OPTIONS_OK)
439         {
440           _sntprintf(ptcError, MESSAGE_SIZE, _T("OptionsProcessOptions(): %s"), atcLocalError);
441           return OPTIONS_ER;
442         }
443         break;
444       }
445     }
446 
447     /*-
448      *******************************************************************
449      *
450      * Reject unmatched arguments that look like options. Treat single
451      * hyphens ("-") as arguments/operands rather than options. Save
452      * the index of the first argument that looks like an operand. It
453      * will be needed to adjust the options context before returning
454      * to the caller. In older implementations of this routine, option
455      * processing stopped once the first operand was identified.
456      *
457      *******************************************************************
458      */
459     if (iIndex == psOptionsContext->iNOptions)
460     {
461       if (ptcArgument[0] == '-' && ptcArgument[1] != 0)
462       {
463         _sntprintf(ptcError, MESSAGE_SIZE, _T("OptionsProcessOptions(): option=[%s]: Unknown option."), ptcArgument);
464         return OPTIONS_ER;
465       }
466       else
467       {
468         OptionsSetArgumentType(psOptionsContext, OPTIONS_ARGUMENT_TYPE_OPERAND);
469         if (!iStopIndexSaved)
470         {
471           iStopIndex = psOptionsContext->iArgumentIndex - 1; /* Subtract one from the index so the next call to OptionsGetNextArgument() outside this routine will get the correct argument (i.e., the argument that was just checked). */
472           iStopIndexSaved = 1;
473         }
474       }
475     }
476   }
477 
478   /*-
479    *********************************************************************
480    *
481    * Set the argument index back to where it should have been if option
482    * processing stopped at the first operand.
483    *
484    *********************************************************************
485    */
486   psOptionsContext->iArgumentIndex = iStopIndex;
487 
488   return OPTIONS_OK;
489 }
490 
491 
492 /*-
493  ***********************************************************************
494  *
495  * OptionsSetArgumentType
496  *
497  ***********************************************************************
498  */
499 void
OptionsSetArgumentType(OPTIONS_CONTEXT * psOptionsContext,int iType)500 OptionsSetArgumentType(OPTIONS_CONTEXT *psOptionsContext, int iType)
501 {
502   psOptionsContext->piArgumentTypes[psOptionsContext->iArgumentIndex] = iType;
503 }
504 
505 
506 /*-
507  ***********************************************************************
508  *
509  * OptionsSetOptions
510  *
511  ***********************************************************************
512  */
513 void
OptionsSetOptions(OPTIONS_CONTEXT * psOptionsContext,OPTIONS_TABLE * psOptions,int iNOptions)514 OptionsSetOptions(OPTIONS_CONTEXT *psOptionsContext, OPTIONS_TABLE *psOptions, int iNOptions)
515 {
516   psOptionsContext->psOptions = psOptions;
517   psOptionsContext->iNOptions = iNOptions;
518 }
519