root/trunk/txlib/txparse.c

Revision 14, 33.5 kB (checked in by jvw, 2 years ago)

HEX/ASCII (sector) editor control added

Line 
1//
2//                     TxWin, Textmode Windowing Library
3//
4//   Original code Copyright (c) 1995-2005 Fsys Software and Jan van Wijk
5//
6// ==========================================================================
7//
8// This file contains Original Code and/or Modifications of Original Code as
9// defined in and that are subject to the GNU Lesser General Public License.
10// You may not use this file except in compliance with the License.
11// BY USING THIS FILE YOU AGREE TO ALL TERMS AND CONDITIONS OF THE LICENSE.
12// A copy of the License is provided with the Original Code and Modifications,
13// and is also available at http://www.dfsee.com/txwin/lgpl.htm
14//
15// This library is free software; you can redistribute it and/or modify
16// it under the terms of the GNU Lesser General Public License as published
17// by the Free Software Foundation; either version 2.1 of the License,
18// or (at your option) any later version.
19//
20// This library is distributed in the hope that it will be useful,
21// but WITHOUT ANY WARRANTY; without even the implied warranty of
22// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
23// See the GNU Lesser General Public License for more details.
24//
25// You should have received a copy of the GNU Lesser General Public License
26// along with this library; (lgpl.htm) if not, write to the Free Software
27// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
28//
29// Questions on TxWin licensing can be directed to: txwin@fsys.nl
30//
31// ==========================================================================
32//
33// TxLib command, argv/argc and number parsing
34//
35// 21-11-2004 JvW Allow explicit decimal prefix 0n and 0t   (DFSee 6.16)
36// 08-08-2003 JvW Long-option support and allow '/' switch  (DFSee 5.25)
37// 15-12-2001 JvW No string-value for -tValue type syntax   (DFSee 5.08)
38// 04-12-2001 JvW Make numeric 0 the default for option -o  (DFSee 4.11)
39// 17-11-2001 JvW Added '' as alternative string syntax     (DFSee 4.10)
40// 05-11-2001 JvW Added -tValue and -t- type of syntax      (DFSee 4.09)
41// 27-08-2001 JvW Added argv/argc as input alternative      (DFSee 4.03)
42// 13-08-2001 JvW First released version                    (DFSee 4.02)
43
44#include <txlib.h>                              // TxLib interface
45#include <txwpriv.h>                            // TXW private interfaces
46#include <txtpriv.h>                            // TXT private interfaces
47
48#define TXA_SIGNATURE   0xFACEBEEF              // valid magic signature value
49
50       TXA_ELEMENT  *txacur = NULL;             // current, top of stack
51       TXA_ELEMENT  *txa1st = NULL;             // first, bottom of stack
52
53typedef char TXA_LONGOPT[ TXA_O_LEN +1];
54
55static TXA_LONGOPT   txalongopt[TXA_LSIZE] =
56{
57   "query",                                     // predefined long names
58   "menu",
59   "debug",
60   "test",
61   "trace",
62   "auto",
63   "entry",
64   "color",
65   "scheme",
66   "lines",
67   "ini",
68   "config",
69   "style",
70   "keyb",
71   "mouse",
72   "simulate",
73   "list",
74   "screen",
75   "label",
76   "test1",
77   "test2",
78   "test3",
79   "test4",
80   "test5",
81   "",
82   "",
83   "",
84   "",
85   "",
86   ""
87};
88
89
90
91/*****************************************************************************/
92// Set or query value for a specific LONGNAME value (the LONG option name)
93/*****************************************************************************/
94char  *TxaOptionLongName                        // RET   resulting option name
95(
96   char                opt,                     // IN    TXA_O_ option value
97   char               *name                     // IN    long name for option
98)                                               //       or NULL to query
99{
100   char               *rc = NULL;               // function return
101   int                 lni;
102
103   ENTER();
104
105   if ((opt >= TXA_LBASE) && ((lni = (opt - TXA_LBASE)) < TXA_LSIZE))
106   {
107      if (name != NULL)
108      {
109         TxCopy( txalongopt[lni], name, TXA_O_LEN +1);
110      }
111      rc = &(txalongopt[lni][0]);
112      TRACES(("Name for APP%lu option set to '%s'\n", opt - TXA_O_APP0, rc));
113   }
114   RETURN (rc);
115}                                               // end 'TxaOptionLongName'
116/*---------------------------------------------------------------------------*/
117
118
119/*****************************************************************************/
120// Create new level of command options for a set of argc/argv variables
121/*****************************************************************************/
122ULONG TxaParseArgcArgv                          // RET   result
123(
124   int                 argc,                    // IN    argument count
125   char               *argv[],                  // IN    array of arguments
126   char               *exename,                 // IN    alternative argv[0]
127   BOOL                freeform,                // IN    free format options
128   TXHANDLE           *txh                      // OUT   TXA handle (optional)
129)
130{
131   ULONG               rc;
132   TXA_ELEMENT        *txa;
133   char               *item;                    // parsed item
134   int                 len;                     // length of item
135   int                 i;
136
137   ENTER();
138
139   if ((rc = TxaNewParseElement( &txa)) == NO_ERROR)
140   {
141      for (i = 0; (i < argc) && (rc == NO_ERROR); i++)
142      {
143         if ((i == 0) && (exename != NULL))
144         {
145            item = exename;                     // preserve alternative argv[0]
146         }
147         else
148         {
149            item = argv[i];
150         }
151         len  = strlen( item);
152
153         rc = txaReadAndStoreItem( item, len, freeform, TRUE, txa);
154      }
155   }
156   if (txh != NULL)
157   {
158      *txh = (TXHANDLE) txa;
159   }
160   RETURN( rc);
161}                                               // end 'TxaParseArgcArgv'
162/*---------------------------------------------------------------------------*/
163
164
165/*****************************************************************************/
166// Parse a decimal or hex number value from a string
167/*****************************************************************************/
168ULONG TxaParseNumber                            // RET   number value
169(
170   char               *value,                   // IN    value string with nr
171   ULONG               rclass,                  // IN    HEX/DEC radix class
172   BYTE               *unit                     // OUT   optional unit char, if
173)                                               //       not needed use NULL
174{
175   ULONG               rc = 0;                  // function return
176   BOOL                prefix     = TRUE;       // number parsed using prefix
177   int                 distance   = 0;          // distance to specific char
178   int                 radixclass = rclass;     // class to be used for default
179
180   TRLEVX(700,( "value: '%s'  rclass: 0x%lX\n", value, rclass));
181
182   if (unit != NULL)                            // read unit char suffix
183   {
184      if ((( distance = (int) strcspn( value, ", ")) > 0) &&
185          ( value[ distance] == ','))           // unit specifier present
186      {
187         *unit = value[ distance +1];           // unit character
188         if (strchr( "sS", *unit) != NULL)      // 's' unit
189         {
190            radixclass = TX_RADIX_UN_S_CLASS;   // use sector-unit class
191         }
192         else
193         {
194            radixclass = TX_RADIX_UNIT_CLASS;   // use 'other' unit class
195         }
196      }
197      else
198      {
199         *unit = TXA_DFUNIT;                    // assume default unit
200      }
201      TRLEVX(700,( "Unit for value: '%s' is: '%c'\n", value, *unit));
202   }
203   if (*value == '0')                           // possible hex/oct/dec prefix
204   {
205      switch (tolower(*(value +1)))
206      {
207         case 'x': sscanf( value +2, "%lx", &rc); break; // hexadecimal number
208         case 'o': sscanf( value +2, "%lo", &rc); break; // octal number base
209         case 'n':
210         case 't': sscanf( value +2, "%lu", &rc); break; // decimal number base
211         default:  prefix = FALSE;                break; // to be determined
212      }
213   }
214   else
215   {
216      prefix = FALSE;
217   }
218   if (prefix == FALSE)                         // no prefix, test HEX or DEC
219   {
220      TRLEVX(700,( "value has no prefix ...\n"));
221      if ( txIsValidHex( value) &&              // valid HEX upto separator
222          !txIsValidDec( value) )               // and has non-decimal digits
223      {
224         TRLEVX(700,( "auto-interpret as HEX!\n"));
225         sscanf( value, "%lx", &rc);            // interpret as HEX
226      }
227      else                                      // could be both ...
228      {
229         if (txwa->radixclass & radixclass)     // class set to HEX default
230         {
231            TRLEVX(700,( "Radix-selection HEX!\n"));
232            sscanf( value, "%lx", &rc);         // interpret as HEX
233         }
234         else
235         {
236            TRLEVX(700,( "Radix-selection DEC!\n"));
237            sscanf( value, "%lu", &rc);         // interpret as DECimal
238         }
239      }
240   }
241   else
242   {
243      TRLEVX(700,( "Selection by prefix '%2.2s'\n", value));
244   }
245   return (rc);
246}                                               // end 'TxaParseNumber'
247/*---------------------------------------------------------------------------*/
248
249
250/*****************************************************************************/
251// Read one item (option or argument) and store it in the txa element
252/*****************************************************************************/
253ULONG txaReadAndStoreItem                       // RET   result
254(
255   char               *item,                    // IN    item string
256   int                 len,                     // IN    length of item
257   BOOL                freeform,                // IN    free format options
258   BOOL                passthrough,             // IN    keep parameter quotes
259   TXA_ELEMENT        *txa                      // INOUT TXA element
260)
261{
262   ULONG               rc = NO_ERROR;
263   int                 arguments = txa->argc;   // current number of arguments
264
265   ENTER();
266
267   TRACES(( "Freeform: %s Passthrough: %s Args: %d  Len:%3.3u, Item: '%*.*s'\n",
268            (freeform) ? "YES" : "NO ", (passthrough) ? "YES" : "NO ",
269             arguments, len, len, len, item));
270   if   ((((txa      == txa1st)  &&             // switch on 1st level '/' only
271         (*(item)    == '/'))    ||
272         (*(item)    == '-'  ))  &&             // switch or option using  '-'
273         (*(item +1) != ' '  )   &&             // allowed at this position ?
274         (*(item +1) != '\0' )   &&
275         (arguments  > 0     )   &&             // never BEFORE 1st word (cmd)
276        ((arguments == 1) || freeform))         // possibly after params too
277   {
278      int              index;                   // index for the option char
279      char            *value;                   // ptr to value part of item
280      int              distance;                // distance to specific char
281      TXA_LONGOPT      lname = {0};             // empty longname to start with
282
283      index = *(item +1);
284      if ((index >= TXA_BASE) && (index < (TXA_BASE + TXA_SIZE)))
285      {
286         index -= TXA_BASE;                     // make it 0 based
287         TRACES(( "Direct option index:%3hu\n", index));
288
289         switch (*(item +2))
290         {
291            case '"': case '\'':                // quoted string
292            case '+': case '-':                 // boolean yes/no
293            case '0': case '1': case '2': case '3': case '4':
294            case '5': case '6': case '7': case '8': case '9':
295               value = item +2;
296               break;
297
298            default:
299               if ((distance = (int) strcspn( item, ": ")) < len)
300               {
301                  value = item + distance +1;   // skip the colon ...
302               }
303               else
304               {
305                  if ((len > 2) &&              // non-empty option name
306                      (item[len -1] == '-'))    // ending in a '-'
307                  {
308                     distance = len -1;         // length of the name
309                     value = item + distance;   // '-' is the value
310                  }
311                  else
312                  {
313                     value = item + len;        // no explicit value given
314                  }
315               }
316
317               if (distance > 2)                // non-trivial longname
318               {
319                  int  lni;
320
321                  strncpy( lname, item +1, distance -1);
322                  lname[distance -1] = 0;       // terminate the name
323                  TRACES(( "longname '%s' for: '%s'\n", lname, item));
324
325                  if (stricmp( "help", lname) == 0) // predefined, map to '?'
326                  {
327                     index = '?' - TXA_BASE;
328                  }
329                  else
330                  {
331                     for (lni = 0; lni < TXA_LSIZE; lni++)
332                     {
333                        if (strlen(txalongopt[lni]) != 0)
334                        {
335                           if (stricmp( txalongopt[lni], lname) == 0)
336                           {
337                              index = lni + TXA_LBASE - TXA_BASE;
338
339                              TRACES(("Matched defined longname '%s', index: %hu\n",
340                                       txalongopt[lni], index));
341                           }
342                        }
343                     }
344                  }
345               }
346               break;
347         }
348         TxFreeMem(  txa->opt[index].name);     // free existing name, if any
349         if (strlen( lname) != 0)               // new longname available ?
350         {
351            if ((txa->opt[ index].name = TxAlloc(1, strlen(lname) +1)) != NULL)
352            {
353               strcpy( txa->opt[ index].name, lname);
354            }
355         }
356
357         if (txa->opt[ index].type == TXA_STRING) // already has a string!
358         {
359            TRACES(("Free existing string value: '%s'\n",
360                       txa->opt[ index].value.string));
361            TxFreeMem( txa->opt[ index].value.string);
362         }
363         txa->opt[ index].type = TXA_NUMBER;
364         txa->opt[ index].unit = TXA_DFUNIT;    // default unit
365         switch (*value)
366         {
367            case TXk_SPACE: case '\0':
368               txa->opt[ index].type = TXA_NO_VAL;
369               txa->opt[ index].value.number = 0;
370               TRACES(("NoValueOpt (%3hu='%c')\n", index + TXA_BASE, index + TXA_BASE));
371               break;
372
373            case '+': txa->opt[ index].value.number = 1;      break;
374            case '-': txa->opt[ index].value.number = 0;      break;
375            case '0': case '1': case '2': case '3': case '4':
376            case '5': case '6': case '7': case '8': case '9':
377               txa->opt[ index].value.number =
378                  TxaParseNumber( value, TX_RADIX_STD_CLASS,
379                                  &(txa->opt[ index].unit));
380
381               TRACES(("NumericOpt (%3hu='%c'), value: %lu\n",
382                        index + TXA_BASE, index + TXA_BASE,
383                        txa->opt[ index].value.number));
384               break;
385
386            default:
387               txa->opt[ index].type = TXA_STRING;
388               if ((txa->opt[ index].value.string = TxAlloc(1, len +1)) != NULL)
389               {
390                  if (passthrough)              // straight copy
391                  {
392                     strcpy(      txa->opt[ index].value.string, value);
393                  }
394                  else                          // space delimited stuff!
395                  {
396                     txaCopyItem( txa->opt[ index].value.string, value, len);
397                  }
398
399                  TRACES(("String Opt (%3hu='%c') at %8.8lx, value: '%s'\n",
400                               index + TXA_BASE, index + TXA_BASE,
401                               txa->opt[ index].value.string,
402                               txa->opt[ index].value.string));
403               }
404               else
405               {
406                  rc = TX_ALLOC_ERROR;
407               }
408               break;
409         }
410         txa->optc++;                           // count the option
411      }
412      else                                      // option char out of range
413      {
414         rc = TX_BAD_OPTION_CHAR;
415      }
416   }
417   else                                         // argv 0 .. n
418   {
419      if (arguments < TXA_ARGC)                 // room for another ?
420      {
421         if ((txa->argv[ arguments] = TxAlloc(1, len +1)) != NULL)
422         {
423            if (passthrough)                    // straight copy
424            {
425               strcpy(      txa->argv[ arguments], item);
426            }
427            else                                // space delimited stuff!
428            {
429               txaCopyItem( txa->argv[ arguments], item, len);
430            }
431            TRACES(("Stored argv[%d] at %8.8lx, value: '%s'\n", arguments,
432                         txa->argv[ arguments], txa->argv[ arguments]));
433            arguments++;
434         }
435         else
436         {
437            rc = TX_ALLOC_ERROR;
438         }
439      }
440      else
441      {
442         rc = TX_TOO_MANY_ARGS;
443      }
444   }
445   txa->argc = arguments;
446   RETURN( rc);
447}                                               // end 'txaReadAndStoreItem'
448/*---------------------------------------------------------------------------*/
449
450
451/*****************************************************************************/
452// Create new level of command options for a set of argc/argv variables
453/*****************************************************************************/
454ULONG TxaNewParseElement                        // RET   result
455(
456   TXA_ELEMENT       **element                  // OUT   TXA element
457)
458{
459   ULONG               rc  = NO_ERROR;
460   TXA_ELEMENT        *txa = NULL;
461
462   if ((txa = TxAlloc(1, sizeof(TXA_ELEMENT))) != NULL)
463   {
464      txa->signature = TXA_SIGNATURE;
465      txa->prev      = txacur;
466
467      if (txacur == NULL)                       // first one ?
468      {
469         txa1st   = txa;
470      }
471      txacur      = txa;
472   }
473   else
474   {
475      rc = TX_ALLOC_ERROR;
476   }
477   *element = txa;
478   return( rc);
479}                                               // end 'TxaNewParseElement'
480/*---------------------------------------------------------------------------*/
481
482
483/*****************************************************************************/
484// Terminate use of current or all instances of the TXA abstract-data-type
485/*****************************************************************************/
486BOOL TxaDropParsedCommand                       // RET   more instances left
487(
488   BOOL                whole_stack              // IN    drop all, terminate
489)
490{
491   TXA_ELEMENT        *txa;
492   int                 i;
493
494   ENTER();
495
496   do
497   {
498      if ((txa = txacur) != NULL)
499      {
500         for (i = 0; i < TXA_SIZE; i++)         // free allocated options
501         {
502            if (txa->opt[i].type == TXA_STRING)
503            {
504               TxFreeMem( txa->opt[i].value.string);
505            }
506            TxFreeMem( txa->opt[i].name);
507         }
508         for (i = 0; i < txa->argc; i++)        // free argv storage
509         {
510            if (txa->argv[i] != NULL)
511            {
512               TxFreeMem( txa->argv[i]);
513            }
514         }
515         txacur = txa->prev;
516         if (txacur == NULL)                    // empty again ?
517         {
518            txa1st   = NULL;
519         }
520         TxFreeMem( txa);
521      }
522   } while (txacur && whole_stack);
523
524   BRETURN((txacur != NULL));
525}                                               // end 'TxaDropParsedCommand'
526/*---------------------------------------------------------------------------*/
527
528
529/*****************************************************************************/
530// Get reference to specified option data, or NULL if option not set
531/*****************************************************************************/
532TXA_OPTION *TxaGetOption                        // RET   option ptr or NULL
533(
534   TXHANDLE            txh,                     // IN    TXA handle
535   char                opt                      // IN    option character
536)
537{
538   TXA_OPTION         *rc = NULL;
539   TXA_ELEMENT        *txa;                     // specified TXA instance
540   int                 i;
541   TXA_OPTION         *o;
542
543   if ((txa = txaHandle2Element( txh)) != NULL)
544   {
545      if ((opt >= TXA_BASE) && (opt < (TXA_BASE + TXA_SIZE)))
546      {
547         i = opt - TXA_BASE;                    // make it 0 based
548         o = &txa->opt[ i];
549         if (o->type != TXA_NONE)
550         {
551            rc  = o;
552         }
553         TRARGS(("TXH (%s): %lx option: %3hu = '%c'  value: %lu '%s'\n",
554            (txh == TXA_1ST) ? "EXE switch" : "CMD option", txh, opt, opt,
555             (o->type == TXA_STRING) ? strlen(o->value.string) : o->value.number,
556             (o->type == TXA_NONE  ) ?       "---NONE---"      :
557             (o->type != TXA_STRING) ?       "--NUMBER--"      : o->value.string ));
558      }
559   }
560   return( rc);
561}                                               // end 'TxaGetOption'
562/*---------------------------------------------------------------------------*/
563
564
565/*****************************************************************************/
566// Get simple YES/NO status for specified option. Not set is "NO"
567/*****************************************************************************/
568BOOL TxaOptionYes                               // RET   option set to YES
569(
570   TXHANDLE            txh,                     // IN    TXA handle
571   char                opt                      // IN    option character
572)
573{
574   BOOL                rc = FALSE;
575   TXA_OPTION         *o;
576
577   if ((o = TxaGetOption( txh, opt)) != NULL)
578   {
579      switch (o->type)
580      {
581         case TXA_NUMBER:
582            if (o->value.number != 0)
583            {
584               rc = TRUE;
585            }
586            break;
587
588         case TXA_STRING:
589            if ((stricmp( o->value.string, "NO")    != 0) &&
590                (stricmp( o->value.string, "N")     != 0) &&
591                (stricmp( o->value.string, "0")     != 0) &&
592                (stricmp( o->value.string, "OFF")   != 0) &&
593                (stricmp( o->value.string, "FALSE") != 0)  )
594            {
595               rc = TRUE;
596            }
597            break;
598
599         default:                               // no value, but set
600            rc = TRUE;
601            break;
602      }
603   }
604   return( rc);
605}                                               // end 'TxaOptionYes'
606/*---------------------------------------------------------------------------*/
607
608
609/*****************************************************************************/
610// Get string value for specified option, use default if no string (and warn!)
611/*****************************************************************************/
612char *TxaOptionStr                              // RET   option String value
613(
614   TXHANDLE            txh,                     // IN    TXA handle
615   char                opt,                     // IN    option character
616   char               *error,                   // IN    error text or NULL
617   char               *deflt                    // IN    default value
618)
619{
620   char               *rc = deflt;
621   TXA_OPTION         *o;
622
623   if ((o = TxaGetOption( txh, opt)) != NULL)
624   {
625      if (o->type == TXA_STRING)
626      {
627         rc = o->value.string;
628      }
629      else                                      // other option type
630      {
631         if (error != NULL)                     // give error/warning
632         {
633            TxPrint( "\n%s %s: %s'-%c'%s has no STRING value, using "
634                     "default: '%s'\n",
635                     (txh != TXA_1ST) ? "Option" : "Switch",
636                      error, CBC, opt, CNN, deflt);
637         }
638      }
639   }
640   return( rc);
641}                                               // end 'TxaOptionStr'
642/*---------------------------------------------------------------------------*/
643
644
645/*****************************************************************************/
646// Get number value for specified option, use default if no number (and warn!)
647/*****************************************************************************/
648ULONG TxaOptionNum                              // RET   option Number value
649(
650   TXHANDLE            txh,                     // IN    TXA handle
651   char                opt,                     // IN    option character
652   char               *error,                   // IN    error text or NULL
653   ULONG               deflt                    // IN    default value
654)
655{
656   ULONG               rc = deflt;
657   TXA_OPTION         *o;
658
659   if ((o = TxaGetOption( txh, opt)) != NULL)
660   {
661      if (o->type == TXA_NUMBER)
662      {
663         rc = o->value.number;
664      }
665      else                                      // other option type
666      {
667         if (error != NULL)                     // give error/warning
668         {
669            TxPrint( "\n%s %s: %s'-%c'%s has no NUMBER value, using "
670                     "default: 0x%8.8lx = %lu\n",
671                     (txh != TXA_1ST) ? "Option" : "Switch",
672                      error, CBC, opt, CNN, deflt, deflt);
673         }
674      }
675   }
676   return( rc);
677}                                               // end 'TxaOptionNum'
678/*---------------------------------------------------------------------------*/
679
680
681/*****************************************************************************/
682// Get option value num/string, with bytes/kilo/mega/giga modifier and default
683/*****************************************************************************/
684ULONG TxaOptionBkmg                             // RET   number value in bytes
685(
686   TXHANDLE            txh,                     // IN    TXA handle
687   char                option,                  // IN    option character
688   ULONG               def,                     // IN    default value
689   BYTE                mod                      // IN    b,kb,mb,gb modifier
690)
691{
692   ULONG               rc = 0;                  // function return
693   BYTE                unit = TXA_DFUNIT;
694   TXA_OPTION         *opt;                     // option pointer
695
696   ENTER();
697   TRACES(("Option: '%c' default: %8.8lx mod:%2.2hx = '%c'\n",
698            option, def, mod, mod));
699
700   if ((opt = TxaOptValue( option)) != NULL)    // get the option details
701   {
702      switch (opt->type)
703      {
704         case TXA_STRING:
705            rc   = TxaParseNumber( opt->value.string, TX_RADIX_STD_CLASS, &unit);
706         case TXA_NO_VAL:
707            rc   = def;
708            break;
709
710         default:                               // convert, default is MiB!
711            rc   = opt->value.number;
712            unit = opt->unit;
713            break;
714      }
715   }
716   if (unit == TXA_DFUNIT)
717   {
718      unit = mod;
719   }
720   switch (tolower(unit))
721   {
722      case 'g':
723      if (rc >= 4)
724      {
725         rc = 0xffffffff;                       // limit at 4GiB -1
726         break;
727      }
728      else
729      {
730         rc *= 1024;                            // Giga
731