~sysman-one/starlet/main

« back to all changes in this revision

Viewing changes to cli_routines.c

  • Committer: Ruslan R. Laishev
  • Date: 2023-11-16 15:42:06 UTC
  • Revision ID: git-v1:123f82c758cbac11d4af5b4c317128070af798a3
[*] Added API for CLI (Command Line Interface)

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
#define __MODULE__      "CLI_ROUTINES"
 
2
#define __IDENT__       "X.00-01"
 
3
 
 
4
#ifdef  __GNUC__
 
5
        #ident                  __IDENT__
 
6
 
 
7
        #pragma GCC diagnostic ignored  "-Wparentheses"
 
8
        #pragma GCC diagnostic ignored  "-Wdate-time"
 
9
        #pragma GCC diagnostic ignored  "-Wunused-variable"
 
10
        #pragma GCC diagnostic ignored  "-Wmissing-braces"
 
11
#endif
 
12
 
 
13
#ifdef __cplusplus
 
14
    extern "C" {
 
15
#define __unknown_params ...
 
16
#define __optional_params ...
 
17
#else
 
18
#define __unknown_params
 
19
#define __optional_params ...
 
20
#endif
 
21
 
 
22
/*
 
23
**++
 
24
**
 
25
**  FACILITY:  Command Language Interface (CLI) Routines
 
26
**
 
27
**  ABSTRACT: A portable API to implement command line parameters parsing, primary syntax checking,
 
28
**      and dispatching of processing. This true story is based on the OpenVMS CDU/CLI facilities.
 
29
**
 
30
**  DESCRIPTION: Command Language Interface (CLI) Routines - an API is supposed to be used to parse and
 
31
**      dispatch processing.
 
32
**
 
33
**      This set of routines help to implement a follows syntax:
 
34
**
 
35
**      <verb> [p1 p2 ... p8] [/qualifiers[=<value> ...]
 
36
**
 
37
**      ...
 
38
**
 
39
**  DESIGN ISSUE:
 
40
**      {tbs}
 
41
**
 
42
**  AUTHORS: Ruslan R. Laishev (RRL)
 
43
**
 
44
**  CREATION DATE:  22-AUG-2018
 
45
**
 
46
**  MODIFICATION HISTORY:
 
47
**
 
48
**      15-OCT-2018     RRL     A main part of code has been wrote; split CLI API to .C and .H modules.
 
49
**
 
50
**      28-JUL-2021     RRL     Project reanimation.
 
51
**
 
52
**--
 
53
*/
 
54
 
 
55
#include        <string.h>
 
56
#include        <stdio.h>
 
57
#include        <stdlib.h>
 
58
#include        <errno.h>
 
59
#include        <time.h>
 
60
#include        <sys/stat.h>
 
61
#include        <arpa/inet.h>
 
62
 
 
63
/*
 
64
* Defines and includes for enable extend trace and logging
 
65
*/
 
66
#define         __FAC__ "CLI_RTNS"
 
67
#define         __TFAC__ __FAC__ ": "           /* Special prefix for $TRACE                    */
 
68
#include        "utility_routines.h"
 
69
#include        "cli_routines.h"
 
70
 
 
71
#define $SHOW_PARM(name, value, format) $IFTRACE(q_trace, ": " #name " = " format, (value))
 
72
#define $SHOW_PTR(var)                  $SHOW_PARM(var, var, "%p")
 
73
#define $SHOW_STR(var)                  $SHOW_PARM(var, (var ? var : "UNDEF(NULL)"), "'%s'")
 
74
#define $SHOW_INT(var)                  $SHOW_PARM(var, ((int) var), "%d")
 
75
#define $SHOW_UINT(var)                 $SHOW_PARM(var, ((unsigned) var), "%u")
 
76
#define $SHOW_ULL(var)                  $SHOW_PARM(var, ((unsigned long long) var), "%llu")
 
77
#define $SHOW_UNSIGNED(var)             $SHOW_PARM(var, var, "0x%08x")
 
78
#define $SHOW_BOOL(var)                 $SHOW_PARM(var, (var ? "ENABLED(TRUE)" : "DISABLED(FALSE)"), "%s");
 
79
 
 
80
 
 
81
static  char *cli$val_type      (
 
82
                        int     valtype
 
83
                )
 
84
{
 
85
        switch (valtype)
 
86
                {
 
87
                case    CLI$K_FILE:     return  "FILE (./filename.ext, device:\\path\\filename.ext)";
 
88
                case    CLI$K_DATE:     return  "DATE (dd-mm-yyyy[-hh:mm:ss])";
 
89
                case    CLI$K_NUM:      return  "DIGIT (decimal, octal, hex)";
 
90
                case    CLI$K_IPV4:     return  "IPV4 (aa.bb.cc.dd)";
 
91
                case    CLI$K_IPV6:     return  "IPV6";
 
92
                case    CLI$K_OPT:      return  "OPTION (no value)";
 
93
                case    CLI$K_QSTRING:  return  "ASCII string in double quotes";
 
94
                case    CLI$K_UUID:     return  "UUID ( ... )";
 
95
                case    CLI$K_KWD:      return  "KEYWORD";
 
96
                }
 
97
 
 
98
        return  "ILLEGAL";
 
99
}
 
100
 
 
101
/*
 
102
 *
 
103
 *  DESCRIPTION: Check a input value for the parameter/qualifier corresponding has been declared type
 
104
 
 
105
 *
 
106
 *  INPUT:
 
107
 *      clictx: CLI-context has been created by cli$parse()
 
108
 *      pqdesc: Parameter/Qualifier descriptor
 
109
 *      val:    an ASCIC string to be checked
 
110
 *
 
111
 *  RETURN:
 
112
 *      SS$_NORMAL, condition status
 
113
 *
 
114
 */
 
115
static  int     cli$val_check   (
 
116
                CLI_CTX         *clictx,
 
117
                CLI_PQDESC      *pqdesc,
 
118
                        ASC     *val
 
119
                                )
 
120
{
 
121
unsigned long long int  status = -1;
 
122
char    buf[NAME_MAX], *cp;
 
123
 
 
124
        switch (pqdesc->type)
 
125
                {
 
126
                case    CLI$K_IPV4:
 
127
                case    CLI$K_IPV6:
 
128
                        if ( 1 !=  inet_pton(pqdesc->type == CLI$K_IPV4 ? AF_INET : AF_INET6, $ASCPTR(val), buf) )
 
129
                                return  (clictx->opts & CLI$M_OPSIGNAL)
 
130
                                        ? $LOG(STS$K_ERROR, "Value '%.*s' cannot be converted, errno=%d", $ASC(val), errno)
 
131
                                        : STS$K_ERROR;
 
132
                        break;
 
133
 
 
134
                case    CLI$K_NUM:
 
135
                        strtoull($ASCPTR(val), &cp, 0);
 
136
 
 
137
                        if (errno == ERANGE )
 
138
                                return  (clictx->opts & CLI$M_OPSIGNAL)
 
139
                                        ? $LOG(STS$K_ERROR, "Value '%.*s' cannot be converted, errno=%d", $ASC(val), errno)
 
140
                                        : STS$K_ERROR;
 
141
 
 
142
                        break;
 
143
 
 
144
                case    CLI$K_DATE:
 
145
                        {
 
146
                        struct tm _tm = {0};
 
147
 
 
148
                        if ( 3 > (status = sscanf ($ASCPTR(val), "%2d-%2d-%4d%c%2d:%2d:%2d",
 
149
                                        &_tm.tm_mday, &_tm.tm_mon, &_tm.tm_year,
 
150
                                        (char *) &status,
 
151
                                        &_tm.tm_hour, &_tm.tm_min, &_tm.tm_sec)) )
 
152
                                return  (clictx->opts & CLI$M_OPSIGNAL)
 
153
                                        ? $LOG(STS$K_ERROR, "Illformed date/time value '%.*s'",  $ASC(val))
 
154
                                        : STS$K_ERROR;
 
155
                        break;
 
156
                        }
 
157
 
 
158
                case    CLI$K_DEVICE:
 
159
                        {
 
160
                        struct stat st = {0};
 
161
 
 
162
                        if ( !(cp = strstr($ASCPTR(val), "dev/")) )
 
163
                                sprintf(cp  = buf, "/dev/%.*s", $ASC(val));
 
164
                        else    cp = $ASCPTR(val);
 
165
 
 
166
                        if ( stat(cp, &st) )
 
167
                                return  (clictx->opts & CLI$M_OPSIGNAL)
 
168
                                        ? $LOG(STS$K_ERROR, "stat(%s), errno=%d", cp, errno)
 
169
                                        : STS$K_ERROR;
 
170
                        break;
 
171
                        }
 
172
 
 
173
                case    CLI$K_UUID:
 
174
                        {
 
175
                        if ( 6 != (status = sscanf ($ASCPTR(val), "%08x-%04x-%04x-%04x-%012x",
 
176
                                                   &status, &status, &status, &status, &status)) )
 
177
                                return  (clictx->opts & CLI$M_OPSIGNAL)
 
178
                                        ? $LOG(STS$K_ERROR, "Illformat UUID value '%.*s'", $ASC(val))
 
179
                                        : STS$K_ERROR;
 
180
 
 
181
                        break;
 
182
                        }
 
183
 
 
184
                default:
 
185
                        return  (clictx->opts & CLI$M_OPSIGNAL)
 
186
                                ? $LOG(STS$K_ERROR, "Unknown  type of value '%.*s'", $ASC(val))
 
187
                                : STS$K_ERROR;
 
188
 
 
189
 
 
190
                }
 
191
 
 
192
 
 
193
        return  STS$K_SUCCESS;
 
194
}
 
195
 
 
196
static  int     cli$add_item2ctx        (
 
197
                CLI_CTX         *clictx,
 
198
                int             type,
 
199
                void            *item,
 
200
                char            *val
 
201
                        )
 
202
{
 
203
CLI_ITEM        *avp, *avp2;
 
204
 
 
205
        /* Allocate memory for new CLI's param/qual value entry */
 
206
        if ( !(avp = calloc(1, sizeof(CLI_ITEM))) )
 
207
                {
 
208
                return  (clictx->opts & CLI$M_OPSIGNAL)
 
209
                        ? $LOG(STS$K_ERROR, "Insufficient memory, errno=%d", errno)
 
210
                        : STS$K_ERROR;
 
211
                }
 
212
 
 
213
        /* Store a given item: parameter or qualifier into the context */
 
214
        if ( val )
 
215
                __util$str2asc (val, &avp->val);
 
216
 
 
217
        if ( !type )
 
218
                {
 
219
                avp->verb = item;
 
220
 
 
221
                /* Run over list down to last item */
 
222
                for ( avp2 = clictx->vlist; avp2 && avp2->next; avp2 = avp2->next);
 
223
 
 
224
                /* Insert new item into the CLI context list */
 
225
                if ( avp2 )
 
226
                        avp2->next = avp;
 
227
                else    clictx->vlist = avp;
 
228
 
 
229
                return  STS$K_SUCCESS;
 
230
                }
 
231
 
 
232
        if ( CLI$K_QUAL == type )
 
233
                avp->pqdesc = item;
 
234
        else    avp->pqdesc =  item;
 
235
 
 
236
        /* Run over list down to last item */
 
237
        for ( avp2 = clictx->avlist; avp2 && avp2->next; avp2 = avp2->next);
 
238
 
 
239
        /* Insert new item into the CLI context list */
 
240
        if ( avp2 )
 
241
                avp2->next = avp;
 
242
        else    clictx->avlist = avp;
 
243
 
 
244
        return  STS$K_SUCCESS;
 
245
}
 
246
 
 
247
void    cli$show_verbs  (
 
248
        CLI_VERB        *verbs,
 
249
                int     level
 
250
                )
 
251
{
 
252
CLI_VERB *verb, *subverb;
 
253
CLI_PQDESC *par;
 
254
CLI_KEYWORD *kwd;
 
255
CLI_PQDESC *qual;
 
256
char    spaces [64];
 
257
 
 
258
        memset(spaces, ' ', sizeof(spaces));
 
259
 
 
260
        for ( verb = verbs; $ASCLEN(&verb->name); verb++)
 
261
                {
 
262
                if ( !level )
 
263
                        $LOG(STS$K_INFO, "Show verb '%.*s' : ", $ASC(&verb->name));
 
264
                else    $LOG(STS$K_INFO, " %.*s%.*s", level * 2, spaces, $ASC(&verb->name));
 
265
 
 
266
                /* Run over command's verbs list ... */
 
267
                for ( subverb = verb->next; subverb; subverb = subverb->next)
 
268
                        cli$show_verbs(verb->next, ++level);
 
269
 
 
270
                for ( par = verb->params; par && par->pn; par++)
 
271
                        {
 
272
                        $LOG(STS$K_INFO, "   P%d - '%.*s' (%s)", par->pn, $ASC(&par->name), cli$val_type (par->type));
 
273
 
 
274
                        if ( par->kwd )
 
275
                                {
 
276
                                for ( kwd = par->kwd; $ASCLEN(&kwd->name); kwd++)
 
277
                                        $LOG(STS$K_INFO, "      %.*s=%#x ", $ASC(&kwd->name), kwd->val);
 
278
                                }
 
279
                        }
 
280
 
 
281
 
 
282
                for ( qual = verb->quals; qual && $ASCLEN(&qual->name); qual++)
 
283
                        {
 
284
                        $LOG(STS$K_INFO, "   /%.*s (%s)", $ASC(&qual->name), cli$val_type (qual->type) );
 
285
 
 
286
                        if ( qual->kwd )
 
287
                                {
 
288
                                for ( kwd = qual->kwd; $ASCLEN(&kwd->name); kwd++)
 
289
                                        $LOG(STS$K_INFO, "      %.*s=%#x ", $ASC(&kwd->name), kwd->val);
 
290
                                }
 
291
                        }
 
292
 
 
293
                }
 
294
}
 
295
 
 
296
 
 
297
void    cli$show_ctx    (
 
298
                CLI_CTX *       clictx
 
299
                )
 
300
{
 
301
CLI_ITEM        *avp;
 
302
char    spaces [64];
 
303
int     splen = 0;
 
304
 
 
305
        memset(spaces, ' ', sizeof(spaces));
 
306
 
 
307
        $LOG(STS$K_INFO, "CLI-parser context area");
 
308
 
 
309
 
 
310
        /* Run over command's verbs list ... */
 
311
        for ( splen = 2, avp = clictx->vlist; avp; avp = avp->next, splen += 2)
 
312
                $LOG(STS$K_INFO, "%.*s %.*s  ('%.*s')", splen, spaces, $ASC(&avp->verb->name), $ASC(&avp->val));
 
313
 
 
314
        for ( avp = clictx->avlist; avp; avp = avp->next)
 
315
                {
 
316
                if ( avp->type )
 
317
                        $LOG(STS$K_INFO, "   P%d[0:%d]='%.*s'", avp->pqdesc->pn, $ASCLEN(&avp->val), $ASC(&avp->val));
 
318
                else    $LOG(STS$K_INFO, "   /%.*s[0:%d]='%.*s'", $ASC(&avp->pqdesc->name), $ASCLEN(&avp->val), $ASC(&avp->val));
 
319
                }
 
320
}
 
321
 
 
322
/*
 
323
 *
 
324
 *  DESCRIPTION: check a given string against a table of keywords,
 
325
 *      detect ambiguous input:
 
326
 *              ST      - is conflicted with START & STOP
 
327
 *      allow not full length-comparing:
 
328
 *              SH      - is matched to  SHOW
 
329
 *
 
330
 *  INPUT:
 
331
 *      sts:    a keyword string to be checked
 
332
 *      opts:   processing options, see CLI$M_OP*
 
333
 *      klist:  an address of the keyword table, null entry terminated
 
334
 *
 
335
 *  OUTPUT:
 
336
 *      kwd:    an address to accept a pointer to keyword's record
 
337
 *
 
338
 *  RETURN:
 
339
 *      SS$_NORMAL, condition status
 
340
 *
 
341
 */
 
342
static  int     cli$check_keyword       (
 
343
                        char    *sts,
 
344
                        int      len,
 
345
                int             opts,
 
346
                CLI_KEYWORD     *klist,
 
347
                CLI_KEYWORD **  kwd
 
348
                )
 
349
{
 
350
CLI_KEYWORD     *krun, *ksel;
 
351
int     qlog = opts & CLI$M_OPTRACE;
 
352
 
 
353
        *kwd = NULL;
 
354
 
 
355
        for ( krun = klist; $ASCLEN(&krun->name); krun++)
 
356
                {
 
357
                if ( len > $ASCLEN(&krun->name) )
 
358
                        continue;
 
359
 
 
360
                $IFTRACE(qlog, "Match [0:%d]='%.*s' against '%.*s'", len, len, sts, $ASC(&krun->name) );
 
361
 
 
362
                if ( !strncasecmp(sts, $ASCPTR(&krun->name), $MIN(len, $ASCLEN(&krun->name))) )
 
363
                        {
 
364
                        if ( ksel )
 
365
                                {
 
366
                                return  (opts & CLI$M_OPSIGNAL)
 
367
                                        ? $LOG(STS$K_FATAL, "Ambiguous input '%.*s' (matched to : '%.*s', '%.*s')", len, sts, $ASC(&krun->name), $ASC(&ksel->name))
 
368
                                        : STS$K_FATAL;
 
369
                                }
 
370
 
 
371
                        /* Safe has been matched keyword's record */
 
372
                        ksel = krun;
 
373
                        }
 
374
                }
 
375
 
 
376
        if ( ksel )
 
377
                {
 
378
                *kwd = ksel;
 
379
                return  STS$K_SUCCESS;
 
380
                }
 
381
 
 
382
        return  (opts & CLI$M_OPSIGNAL)
 
383
                ? $LOG(STS$K_ERROR, "Illegal or unrecognized keyword '%.*s'", len, sts)
 
384
                : STS$K_ERROR;
 
385
}
 
386
 
 
387
/*
 
388
 *
 
389
 *  DESCRIPTION: extract a params list from the command line corresponding to definition
 
390
 *              of the param list from the 'verb'
 
391
 *
 
392
 *  INPUT:
 
393
 *      verbs:  verb's definition structure
 
394
 *      opts:   processing options, see CLI$M_OP*
 
395
 *      argc:   arguments count
 
396
 *      argv:   arguments array
 
397
 *
 
398
 *  IMPLICIT OUTPUT:
 
399
 *      ctx:    A CLI-context to be created
 
400
 *
 
401
 *  RETURN:
 
402
 *      SS$_NORMAL, condition status
 
403
 *
 
404
 */
 
405
static  int     _cli$parse_quals(
 
406
        CLI_CTX *       clictx,
 
407
        CLI_VERB        *verb,
 
408
                int     argc,
 
409
                char ** argv
 
410
                        )
 
411
{
 
412
CLI_PQDESC      *qrun, *qsel = NULL;
 
413
int             status, len, i, qlog = clictx->opts & CLI$M_OPTRACE;
 
414
char            *aptr, *vptr;
 
415
 
 
416
        /*
 
417
         *  Run over arguments from command line
 
418
         */
 
419
        for ( qsel = NULL, aptr = argv[i = 0]; argc; argc--, i++,  aptr = argv[i] )
 
420
                {
 
421
                if ( (*aptr == '-') || (*aptr == '/') )
 
422
                        aptr++;
 
423
                else    continue;
 
424
 
 
425
                /* Is there '=' and value ? */
 
426
                if ( vptr = strchr(aptr, '=') )
 
427
                        len = vptr - aptr;
 
428
                else    len = strnlen(aptr, ASC$K_SZ);
 
429
 
 
430
                for ( qsel = NULL, qrun = verb->quals; $ASCLEN(&qrun->name); qrun++  )
 
431
                        {
 
432
                        if ( len > $ASCLEN(&qrun->name) )
 
433
                                continue;
 
434
 
 
435
//                      $IFTRACE(qlog, "Match [0:%d]='%.*s' against '%.*s'", len, len, aptr, $ASC(&qrun->name) );
 
436
 
 
437
                        if ( !strncasecmp(aptr, $ASCPTR(&qrun->name), len) )
 
438
                                {
 
439
                                if ( qsel )
 
440
                                        {
 
441
                                        return  (clictx->opts & CLI$M_OPSIGNAL)
 
442
                                                ? $LOG(STS$K_FATAL, "Ambiguous input '%.*s' (matched to : '%.*s', '%.*s')", len, aptr, $ASC(&qrun->name), $ASC(&qsel->name))
 
443
                                                : STS$K_FATAL;
 
444
                                        }
 
445
 
 
446
                                vptr    += (vptr != NULL);
 
447
                                $IFTRACE(qlog, "%.*s='%s'", $ASC(&qrun->name), vptr);
 
448
 
 
449
                                status = cli$add_item2ctx(clictx, CLI$K_QUAL, qrun, vptr);
 
450
 
 
451
                                qsel = qrun;
 
452
                                }
 
453
                        }
 
454
                }
 
455
 
 
456
        return  STS$K_SUCCESS;
 
457
}
 
458
 
 
459
/*
 
460
 *
 
461
 *  DESCRIPTION: extract a params list from the command line corresponding to definition
 
462
 *              of the param list from the 'verb, add param/value pair into the 'CLI-context' area.
 
463
 *
 
464
 *  INPUT:
 
465
 *      clictx: A CLI-context to be created
 
466
 *      opts:   processing options, see CLI$M_OP*
 
467
 *      argc:   arguments count
 
468
 *      argv:   arguments array
 
469
 *
 
470
 *  RETURN:
 
471
 *      SS$_NORMAL, condition status
 
472
 *
 
473
 */
 
474
static  int     _cli$parse_params(
 
475
        CLI_CTX *       clictx,
 
476
        CLI_VERB        *verb,
 
477
                int     argc,
 
478
                char ** argv
 
479
                        )
 
480
{
 
481
CLI_PQDESC      *param;
 
482
int             status, pi, qlog = clictx->opts & CLI$M_OPTRACE;
 
483
 
 
484
        /*
 
485
         * Run firstly over parameters list and extract values
 
486
         */
 
487
        for ( pi = 0, param = verb->params; param && (pi < argc) && param->pn; param++, pi++ )
 
488
                {
 
489
                $IFTRACE(qlog, "P%d(%.*s)='%s'", param->pn, $ASC(&param->name), argv[pi] );
 
490
 
 
491
                /* Put parameter's value into the CLI context */
 
492
                if ( !(1 & (status = cli$add_item2ctx (clictx, param->pn, param, argv[pi]))) )
 
493
                        return  (clictx->opts & CLI$M_OPSIGNAL) ? $LOG(STS$K_FATAL, "Error inserting P%d - %.*s into CLI context area", param->pn, $ASC(&param->name)) : STS$K_FATAL;
 
494
                }
 
495
 
 
496
        /* Did we get all parameters ? */
 
497
        if ( param && param->pn )
 
498
                return  (clictx->opts & CLI$M_OPSIGNAL) ? $LOG(STS$K_FATAL, "Missing P%d - %.*s !", param->pn, $ASC(&param->name)) : STS$K_FATAL;
 
499
 
 
500
        /* Now we can extract qualifiers ... */
 
501
        status = _cli$parse_quals(clictx, verb, argc - pi, argv + pi);
 
502
 
 
503
        return  status;
 
504
}
 
505
 
 
506
/*
 
507
 *
 
508
 *  DESCRIPTION: parsing input list of arguments by using a command's verbs definition is provided by 'verbs'
 
509
 *              syntax definition. Internaly performs command verb matching and calling other CLI-routines to parse 'parameters'
 
510
 *              and 'qualifiers'.
 
511
 *              Create a CLI-context area is supposed to be used by cli$get_value() routine to extract a parameter value.
 
512
 
 
513
 *  INPUT:
 
514
 *      verbs:  commands' verbs definition structure, null entry terminated
 
515
 *      opts:   processing options, see CLI$M_OP*
 
516
 *      argc:   arguments count
 
517
 *      argv:   arguments array
 
518
 *
 
519
 *  OUTPUT:
 
520
 *      ctx:    A CLI-context to be created
 
521
 *
 
522
 *  RETURN:
 
523
 *      SS$_NORMAL, condition status
 
524
 *
 
525
 */
 
526
static  int     _cli$parse_verb (
 
527
        CLI_CTX         *clictx,
 
528
        CLI_VERB *      verbs,
 
529
                int     argc,
 
530
                char ** argv
 
531
                        )
 
532
{
 
533
CLI_VERB        *vrun, *vsel;
 
534
int             status, len, qlog = clictx->opts & CLI$M_OPTRACE;
 
535
char            *pverb;
 
536
 
 
537
        $IFTRACE(qlog, "argc=%d", argc);
 
538
 
 
539
        /*
 
540
         * Sanity check for input arguments ...
 
541
         */
 
542
        if ( argc < 1 )
 
543
                return  (clictx->opts & CLI$M_OPSIGNAL) ? $LOG(STS$K_FATAL, "Too many arguments") : STS$K_FATAL;
 
544
 
 
545
 
 
546
        pverb = argv[0];
 
547
 
 
548
        /*
 
549
         * First at all we need to match  argv[1] in the verbs list
 
550
         */
 
551
        len = strnlen(pverb, CLI$S_MAXVERBL);
 
552
 
 
553
        $IFTRACE(qlog, "argv[1]='%s'->[0:%d]='%.*s'", pverb, len, len, pverb);
 
554
 
 
555
        for (vrun = verbs, vsel = NULL; $ASCLEN(&vrun->name); vrun++)
 
556
                {
 
557
                if ( len > $ASCLEN(&vrun->name) )
 
558
                        continue;
 
559
 
 
560
                $IFTRACE(qlog, "Matching '%.*s' vs '%.*s' ... ", len, pverb, $ASC(&vrun->name));
 
561
 
 
562
                /*
 
563
                 * Match verb from command line against given verbs table,
 
564
                 * we are comparing at minimal length, but we must checks for ambiguous verb's definitions like:
 
565
                 *
 
566
                 * SET will match verbs: SET & SETUP
 
567
                 * DEL will match: DELETE & DELIVERY
 
568
                 *
 
569
                 */
 
570
                if ( !strncasecmp(pverb, $ASCPTR(&vrun->name), $MIN(len, $ASCLEN(&vrun->name))) )
 
571
                        {
 
572
                        $IFTRACE(qlog, "Matched on length=%d '%.*s' := '%.*s' !", $MIN(len, $ASCLEN(&vrun->name)), len, pverb, $ASC(&vrun->name));
 
573
 
 
574
                        /* Check that there is not previous matches */
 
575
                        if ( vsel )
 
576
                                {
 
577
                                return  (clictx->opts & CLI$M_OPSIGNAL)
 
578
                                        ? $LOG(STS$K_FATAL, "Ambiguous input '%.*s' (matched to : '%.*s', '%.*s')", len, pverb, $ASC(&vrun->name), $ASC(&vsel->name))
 
579
                                        : STS$K_FATAL;
 
580
                                }
 
581
 
 
582
                        /* Safe matched verb for future checks */
 
583
                        vsel = vrun;
 
584
                        }
 
585
                }
 
586
 
 
587
        /* Found something ?*/
 
588
        if ( !vsel )
 
589
                return  (clictx->opts & CLI$M_OPSIGNAL) ? $LOG(STS$K_FATAL, "Unrecognized command verb '%.*s'", len, pverb) : STS$K_FATAL;
 
590
 
 
591
        /*
 
592
         * Ok, we has got in 'vsel' a legal verb, so
 
593
         * so we will now matching arguments from 'argv' against
 
594
         * the verb's parameters and qualifiers list
 
595
         */
 
596
        /* Insert new item into the CLI context list */
 
597
        cli$add_item2ctx (clictx, 0, vsel, pverb);
 
598
 
 
599
        /* Is there a next subverb ? */
 
600
        if ( vsel->next )
 
601
                status = _cli$parse_verb        (clictx, vsel, argc - 1, argv++);
 
602
        else    {
 
603
                if ( ( !vsel->params) && (!vsel->quals) )
 
604
                        return  STS$K_SUCCESS;
 
605
 
 
606
                status = _cli$parse_params(clictx, vsel, argc - 1, argv + 1);
 
607
                }
 
608
 
 
609
        return  status;
 
610
}
 
611
 
 
612
 
 
613
/*
 
614
 *
 
615
 *  DESCRIPTION: a top level routine - as main entry for the CLI parsing.
 
616
 *              Create a CLI-context area is supposed to be used by cli$get_value() routine to extract a parameter value.
 
617
 
 
618
 *  INPUT:
 
619
 *      verbs:  commands' verbs definition structure, null entry terminated
 
620
 *      opts:   processing options, see CLI$M_OP*
 
621
 *      argc:   arguments count
 
622
 *      argv:   arguments array
 
623
 *
 
624
 *  OUTPUT:
 
625
 *      ctx:    A CLI-context to be created
 
626
 *
 
627
 *  RETURN:
 
628
 *      SS$_NORMAL, condition status
 
629
 *
 
630
 */
 
631
int     cli$parse       (
 
632
        CLI_VERB *      verbs,
 
633
        int             opts,
 
634
                int     argc,
 
635
                char ** argv,
 
636
                void ** clictx
 
637
                        )
 
638
{
 
639
int     status, qlog = opts & CLI$M_OPTRACE;
 
640
CLI_CTX *ctx;
 
641
 
 
642
        $IFTRACE(qlog, "argc=%d, opts=%#x", argc, opts);
 
643
 
 
644
        /*
 
645
         * Sanity check for input arguments ...
 
646
         */
 
647
        if ( argc < 1 )
 
648
                return  (opts & CLI$M_OPSIGNAL) ? $LOG(STS$K_FATAL, "Too many arguments") : STS$K_FATAL;
 
649
 
 
650
        /* Create CLI-context area */
 
651
        if ( !(*clictx = calloc(1, sizeof(CLI_CTX))) )
 
652
                return  (opts & CLI$M_OPSIGNAL) ? $LOG(STS$K_FATAL, "Cannot allocate memory, errno=%d", errno) : STS$K_FATAL;
 
653
        ctx = *clictx;
 
654
        ctx->opts = opts;
 
655
 
 
656
 
 
657
        status = _cli$parse_verb(*clictx, verbs, argc, argv);
 
658
 
 
659
        return  STS$K_SUCCESS;
 
660
}
 
661
 
 
662
/*
 
663
 *
 
664
 *  DESCRIPTION: retreive a value of the parameter or qualifier from the CLI-context has been created and filled by cli$parse().
 
665
 
 
666
 *  INPUT:
 
667
 *      ctx:    A CLI-context has been created by cli$parse()
 
668
 *      pq:     A pointer to parameter/qualifier definition
 
669
 *      val:    A buffer to accept value
 
670
 *
 
671
 *  RETURN:
 
672
 *      STS$K_WARN      - value is zero length
 
673
 *      SS$_NORMAL, condition status
 
674
 *
 
675
 */
 
676
int     cli$get_value   (
 
677
        CLI_CTX         *clictx,
 
678
        CLI_PQDESC      *pq,
 
679
                ASC     *val
 
680
                        )
 
681
{
 
682
CLI_ITEM *item;
 
683
 
 
684
        /* Sanity check */
 
685
        if ( !clictx )
 
686
                return  $LOG(STS$K_FATAL, "CLI-context is empty");
 
687
 
 
688
        if ( !pq )
 
689
                return  $LOG(STS$K_FATAL, "Illegal parameter/qualifier definition");
 
690
 
 
691
        for ( item = clictx->avlist; item; item = item->next)
 
692
                {
 
693
                if ( pq  == item->pqdesc )
 
694
                        {
 
695
                        /* Do we need to return a qualifier value to caller ?*/
 
696
                        if ( val )
 
697
                                {
 
698
                                *val = item->val;
 
699
 
 
700
                                if ( !($ASCLEN(val)) )
 
701
                                        return  (clictx->opts & CLI$M_OPSIGNAL) ? $LOG(STS$K_WARN, "Zero length value") : STS$K_WARN;
 
702
                                }
 
703
 
 
704
                        return  STS$K_SUCCESS;
 
705
                        }
 
706
 
 
707
                }
 
708
 
 
709
        return  (clictx->opts & CLI$M_OPSIGNAL) ? $LOG(STS$K_ERROR, "No parameter/qualifier ('%.*s') is present in command line",
 
710
                                                       $ASC(&pq->name)) : STS$K_ERROR;
 
711
}
 
712
 
 
713
 
 
714
/*
 
715
 *
 
716
 *  DESCRIPTION: release resources has been allocated by cli$parse() routine.
 
717
 *
 
718
 *  INPUT:
 
719
 *      ctx:    A CLI-context has been created by cli$parse()
 
720
 *
 
721
 *  RETURN:
 
722
 *      SS$_NORMAL, condition status
 
723
 *
 
724
 */
 
725
int     cli$cleanup     (
 
726
                CLI_CTX *clictx
 
727
                )
 
728
{
 
729
 
 
730
CLI_ITEM        *avp, *avp2;
 
731
 
 
732
        /* Run over verb's items list and free has been alocated memory ...*/
 
733
        for (avp = clictx->vlist; avp; )
 
734
                {
 
735
                avp2 = avp;
 
736
                avp = avp->next;
 
737
                free(avp2);
 
738
                }
 
739
 
 
740
        /* Run over vlaue's items list and free has been alocated memory ...*/
 
741
        for (avp = clictx->avlist; avp; )
 
742
                {
 
743
                avp2 = avp;
 
744
                avp = avp->next;
 
745
                free(avp2);
 
746
                }
 
747
 
 
748
        /* Release CLI-context area */
 
749
        free(clictx);
 
750
 
 
751
        return  STS$K_SUCCESS;
 
752
}
 
753
 
 
754
 
 
755
/*
 
756
 *
 
757
 *  DESCRIPTION: dispatch processing to action routine has been defined for the last verb's keyword.
 
758
 *
 
759
 *  INPUT:
 
760
 *      ctx:    A CLI-context has been created by cli$parse()
 
761
 *
 
762
 *  RETURN:
 
763
 *      SS$_NORMAL, condition status
 
764
 *
 
765
 */
 
766
int     cli$dispatch    (
 
767
                CLI_CTX *clictx
 
768
                        )
 
769
{
 
770
CLI_ITEM        *item;
 
771
CLI_VERB        *verb;
 
772
 
 
773
        /* Run over verb's items to last element */
 
774
        for ( item = clictx->vlist; item && item->next; item = item->next);
 
775
 
 
776
        if ( !item )
 
777
                return  (clictx->opts & CLI$M_OPSIGNAL) ? $LOG(STS$K_FATAL, "No verb's item has been found in CLI-context") : STS$K_FATAL;
 
778
 
 
779
        if ( !(verb = item->verb)  )
 
780
                return  (clictx->opts & CLI$M_OPSIGNAL) ? $LOG(STS$K_FATAL, "No verb has been found in CLI-context") : STS$K_FATAL;
 
781
 
 
782
        $IFTRACE(clictx->opts & CLI$M_OPTRACE, "Action routine=%#x, argument=%#x", verb->act_rtn, verb->act_arg);
 
783
 
 
784
        if ( verb->act_rtn )
 
785
                return  verb->act_rtn(clictx, verb->act_arg);
 
786
 
 
787
        return  (clictx->opts & CLI$M_OPSIGNAL) ? $LOG(STS$K_WARN, "No action routine has been defined") : STS$K_WARN;
 
788
 
 
789
}
 
790
 
 
791
 
 
792
 
 
793
 
 
794
#ifdef  __CLI_DEBUG__
 
795
 
 
796
#include        <stdio.h>
 
797
#include        <errno.h>
 
798
 
 
799
 
 
800
/*
 
801
        SHOW    VOLUME  <p1-volume_name>/UUID=<uuid> /FULL
 
802
                USER    <p1-user-name>  /FULL /GROUP=<user_group_name>
 
803
                VM      <p1-VM-name>    /FULL /GROUP=<vm_group_name>
 
804
 
 
805
        DIFF    <p1-file-name> <p2-file-name> /BLOCK=(START=<lbn>, END=<lbn>, COUNT=<lbn>)
 
806
                        /IGNORE=(ERROR) /LOGGING=(FULL, TRACE, ERROR)
 
807
 
 
808
        SORT    <p1-file-name>
 
809
                /MERGE=(file-spec[,file-spec]))
 
810
                /KEY0=(<key-type>, <start-pos>[, <key-size>])
 
811
                ...
 
812
                /KEY8=(<key-type>, <start-pos>[, <key-size>])
 
813
                        key-type        := STRING, BYTE, WORD, LONG, QUAD,
 
814
                        start-post      := <digit>
 
815
                        key-size        := <digit>
 
816
 
 
817
                /ORDER=(ASCENDING | DESCENDING)
 
818
                /TRACE
 
819
                /OUTPUT=<file-spec>
 
820
*/
 
821
 
 
822
enum    {
 
823
        SORT$K_STRING = 0,
 
824
        SORT$K_BYTE,
 
825
        SORT$K_WORD,
 
826
        SORT$K_LONG,
 
827
        SORT$K_QUAD
 
828
};
 
829
 
 
830
 
 
831
enum    {
 
832
        SORT$K_ASCENDING = 0,
 
833
        SORT$K_DESCENDING,
 
834
        SORT$K_TRACE
 
835
};
 
836
 
 
837
 
 
838
CLI_PQDESC      sort_params [] = {
 
839
                        {.pn = CLI$K_P1, .type = CLI$K_FILE, .name = {$ASCINI("Input file")} },
 
840
                        {0}};
 
841
 
 
842
CLI_KEYWORD     sort_key_types[] = {
 
843
                        { .name = {$ASCINI("STRING")},  .val = SORT$K_STRING},
 
844
                        { .name = {$ASCINI("BYTE")},    .val = SORT$K_BYTE},
 
845
                        { .name = {$ASCINI("WORD")},    .val = SORT$K_WORD},
 
846
                        { .name = {$ASCINI("LONG")},    .val = SORT$K_LONG},
 
847
                        { .name = {$ASCINI("QUAD")},    .val = SORT$K_QUAD},
 
848
                        {0}};
 
849
 
 
850
CLI_KEYWORD     sort_orders [] = {
 
851
                        { .name = {$ASCINI("ASCENDING")}, .val = SORT$K_ASCENDING},
 
852
                        { .name = {$ASCINI("DESCENDING")}, .val = SORT$K_DESCENDING},
 
853
                        {0}};
 
854
 
 
855
CLI_KEYWORD     sort_key_opts [] = {
 
856
                        { .name = {$ASCINI("")}, .val = SORT$K_ASCENDING},
 
857
                        { .name = {$ASCINI("DESCENDING")}, .val = SORT$K_DESCENDING},
 
858
                        {0}};
 
859
 
 
860
 
 
861
 
 
862
 
 
863
CLI_PQDESC      sort_quals [] = {
 
864
                        { .name = {$ASCINI("ORDER")}, .type = CLI$K_KWD, .kwd = sort_orders},
 
865
                        { .name = {$ASCINI("START")},   .type = CLI$K_NUM},
 
866
                        { .name = {$ASCINI("END")},     .type = CLI$K_NUM},
 
867
                        { .name = {$ASCINI("COUNT")},   .type = CLI$K_NUM},
 
868
                        { .name = {$ASCINI("IGNORE")},  .type = CLI$K_OPT},
 
869
                        { .name = {$ASCINI("LOGGING")}, .type = CLI$K_KWD, .kwd = diff_log_opts},
 
870
                        {0}};
 
871
 
 
872
 
 
873
enum    {
 
874
        SHOW$K_VOLUME = 1,
 
875
        SHOW$K_USER,
 
876
        SHOW$K_VM
 
877
};
 
878
 
 
879
 
 
880
enum    {
 
881
        DIFF$K_FULL = 1 << 0,
 
882
        DIFF$K_TRACE = 1 << 1,
 
883
        DIFF$K_ERROR = 1 << 2,
 
884
};
 
885
 
 
886
CLI_KEYWORD     diff_log_opts[] = {
 
887
                        { .name = {$ASCINI("FULL")},    .val = DIFF$K_FULL},
 
888
                        { .name = {$ASCINI("TRACE")},   .val = DIFF$K_TRACE},
 
889
                        { .name = {$ASCINI("ERROR")},   .val = DIFF$K_ERROR},
 
890
                        {0}},
 
891
 
 
892
                show_what_opts [] = {
 
893
                        { .name = {$ASCINI("VOLUME")},  .val = SHOW$K_VOLUME},
 
894
                        { .name = {$ASCINI("USER")},    .val = SHOW$K_USER},
 
895
                        { .name = {$ASCINI("VM")},      .val = SHOW$K_VM},
 
896
                        {0}};
 
897
 
 
898
CLI_PQDESC      diff_quals [] = {
 
899
                        { .name = {$ASCINI("START")},   .type = CLI$K_NUM},
 
900
                        { .name = {$ASCINI("END")},     .type = CLI$K_NUM},
 
901
                        { .name = {$ASCINI("COUNT")},   .type = CLI$K_NUM},
 
902
                        { .name = {$ASCINI("IGNORE")},  .type = CLI$K_OPT},
 
903
                        { .name = {$ASCINI("LOGGING")}, .type = CLI$K_KWD, .kwd = diff_log_opts},
 
904
                        {0}},
 
905
 
 
906
                show_volume_quals [] = {
 
907
                        { .name = {$ASCINI("UUID")},    CLI$K_UUID},
 
908
                        { .name = {$ASCINI("FULL")},    CLI$K_OPT},
 
909
                        {0}},
 
910
                show_user_quals [] = {
 
911
                        { .name = {$ASCINI("GROUP")},   CLI$K_QSTRING},
 
912
                        { .name = {$ASCINI("FULL")},    CLI$K_OPT},
 
913
                        {0}},
 
914
                show_vm_quals [] = {
 
915
                        { .name = {$ASCINI("GROUP")},   CLI$K_QSTRING},
 
916
                        { .name = {$ASCINI("FULL")},    CLI$K_OPT},
 
917
                        {0}};
 
918
 
 
919
CLI_PQDESC      diff_params [] = {
 
920
                        {.pn = CLI$K_P1, .type = CLI$K_FILE, .name = {$ASCINI("Input file 1")} },
 
921
                        {.pn = CLI$K_P2, .type = CLI$K_FILE, .name = {$ASCINI("Input file 2")} },
 
922
                        {0}},
 
923
 
 
924
                show_volume_params [] = {
 
925
                        {.pn = CLI$K_P1, .type = CLI$K_DEVICE, .name = {$ASCINI("Volume name (eg: sdb, sdb1, sda5: )")} },
 
926
                        {0}},
 
927
 
 
928
                show_user_params [] = {
 
929
                        {.pn = CLI$K_P1, .type = CLI$K_QSTRING,  .name = {$ASCINI("User spec")}},
 
930
                        {0}},
 
931
 
 
932
                show_vm_params [] = {
 
933
                        {.pn = CLI$K_P1, .type = CLI$K_QSTRING,  .name = {$ASCINI("VM Id")}},
 
934
                        {0}};
 
935
 
 
936
 
 
937
int     diff_action     ( CLI_CTX *clictx, void *arg);
 
938
int     show_action     ( CLI_CTX *clictx, void *arg);
 
939
 
 
940
CLI_VERB        show_what []  = {
 
941
        { {$ASCINI("volume")},  .params = show_volume_params, .quals = show_volume_quals , .act_rtn = show_action, .act_arg = SHOW$K_VOLUME },
 
942
        { {$ASCINI("vm")},      .params = show_user_params, .quals = show_vm_quals,  .act_rtn = show_action, .act_arg = SHOW$K_VM  },
 
943
        { {$ASCINI("user")},    .params = show_vm_params, .quals = show_user_quals , .act_rtn = show_action, .act_arg = SHOW$K_USER  },
 
944
        {0}};
 
945
 
 
946
 
 
947
CLI_VERB        top_commands []  = {
 
948
        { .name = {$ASCINI("diff")}, .params = diff_params, .quals = diff_quals , .act_rtn = diff_action  },
 
949
        { .name = {$ASCINI("show")}, .next = show_what},
 
950
        { .name = {$ASCINI("sort")}, .params = sort_params, .quals = sort_quals , .act_rtn = sort_action  },
 
951
        {0}};
 
952
 
 
953
ASC     prompt = {$ASCINI("CRYPTORCP>")};
 
954
 
 
955
 
 
956
int     diff_action     (
 
957
                CLI_CTX         *clictx,
 
958
                        void    *arg
 
959
                        )
 
960
{
 
961
int     status;
 
962
ASC     fl1, fl2;
 
963
 
 
964
        $IFTRACE(clictx->opts & CLI$M_OPTRACE, "Action routine is just called!");
 
965
 
 
966
        status = cli$get_value(clictx, &diff_params, &fl1);
 
967
 
 
968
        status = cli$get_value(clictx, &diff_params, &fl2);
 
969
 
 
970
        $IFTRACE(clictx->opts & CLI$M_OPTRACE, "Comparing %.*s vs %.*s", $ASC(&fl1), $ASC(&fl2));
 
971
 
 
972
 
 
973
        $IFTRACE(clictx->opts & CLI$M_OPTRACE, "Action routine has been completed!");
 
974
 
 
975
        return  STS$K_SUCCESS;
 
976
}
 
977
 
 
978
int     show_action     ( CLI_CTX *clictx, void *arg)
 
979
{
 
980
int     what = (int) arg, status;
 
981
ASC     val;
 
982
 
 
983
 
 
984
        switch  ( what )
 
985
                {
 
986
                case    SHOW$K_VOLUME:
 
987
                        break;
 
988
 
 
989
                case    SHOW$K_USER:
 
990
                        break;
 
991
 
 
992
                case    SHOW$K_VM:
 
993
                        break;
 
994
 
 
995
                default:
 
996
                        $LOG(STS$K_FATAL, "Error processing SHOW command");
 
997
                }
 
998
 
 
999
        return  STS$K_SUCCESS;
 
1000
}
 
1001
 
 
1002
int main        (int    argc, char **argv)
 
1003
{
 
1004
int     status;
 
1005
void    *clictx = NULL;
 
1006
 
 
1007
        {
 
1008
 
 
1009
        struct tm _tm = {0};
 
1010
 
 
1011
        status = sscanf ("15-10-2018-15:17:13", "%2d-%2d-%4d%c%2d:%2d:%2d",
 
1012
                        &_tm.tm_mday, &_tm.tm_mon, &_tm.tm_year,
 
1013
                        (char *) &status,
 
1014
                        &_tm.tm_hour, &_tm.tm_min, &_tm.tm_sec);
 
1015
 
 
1016
        //return        0;
 
1017
        }
 
1018
 
 
1019
 
 
1020
 
 
1021
        /* Dump to screen verbs definitions */
 
1022
        cli$show_verbs (top_commands, 0);
 
1023
 
 
1024
        /* Process command line arguments */
 
1025
        if ( !(1 & (status = cli$parse (top_commands, CLI$M_OPTRACE | CLI$M_OPSIGNAL, argc - 1, argv + 1, &clictx))) )
 
1026
                return  -EINVAL;
 
1027
 
 
1028
        /* Show  a result of the parsing */
 
1029
        cli$show_ctx (clictx);
 
1030
 
 
1031
        /* Process command line arguments */
 
1032
        if ( !(1 & (status = cli$dispatch (clictx))) )
 
1033
                return  -EINVAL;
 
1034
 
 
1035
        /* Release hass been allocated resources */
 
1036
        cli$cleanup(clictx);
 
1037
 
 
1038
        return 0;
 
1039
}
 
1040
 
 
1041
 
 
1042
#ifdef __cplusplus
 
1043
    }
 
1044
#endif
 
1045
#endif  /* __CLI_DEBUG__ */