root/trunk/txlib/txparse2.c

Revision 11, 25.7 kB (checked in by jvw, 3 years ago)

DFSee 8.01 level; fix crash on huge PATH; History-popup improved (4OS2 compatible)

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, section 2
34//
35// 19-08-2005 JvW Initial version, split off for modularity
36
37#include <txlib.h>                              // TxLib interface
38#include <txwpriv.h>                            // TXW private interfaces
39#include <txtpriv.h>                            // TXT private interfaces
40
41
42// Parse next item in string, skip leading spaces, honor embedded strings
43static char *txaParseNextItem                   // RET   next item or NULL
44(
45   char              **start,                   // INOUT start position
46   int                *length                   // OUT   length of item
47);
48
49/*****************************************************************************/
50// Create new level of command options for a given command string
51/*****************************************************************************/
52ULONG TxaParseCommandString                     // RET   result
53(
54   char               *cmd,                     // IN    command string
55   BOOL                freeform,                // IN    free format options
56   TXHANDLE           *txh                      // OUT   TXA handle (optional)
57)
58{
59   ULONG               rc;
60   TXA_ELEMENT        *txa;
61   char               *item;                    // parsed item
62   char               *pos;                     // next start position
63   int                 len;                     // length of item
64
65   ENTER();
66
67   TRACES(( "Command: '%s'\n", cmd));
68
69   if ((rc = TxaNewParseElement( &txa)) == NO_ERROR)
70   {
71      pos = cmd;
72      while (((item = txaParseNextItem( &pos, &len)) != NULL) && (rc == NO_ERROR))
73      {
74         TRACES(( "Rest of cmd : ¯%s®\n", item));
75         if (*item != ';')
76         {
77            rc = txaReadAndStoreItem( item, len, freeform, FALSE, txa);
78         }
79         else                                   // copy rest as comment
80         {
81            TRACES(( "add comment: '%s'\n", item +1));
82            strncpy( txa->comment, item +1, TXMAXLN);
83            break;                              // and break the loop
84         }
85      }
86   }
87   if (txh != NULL)
88   {
89      *txh = (TXHANDLE) txa;
90   }
91   TRINIT(30);
92   {
93      TxaShowParsedCommand( FALSE);             // show parse result
94   }
95   TREXIT();
96   RETURN( rc);
97}                                               // end 'TxaParseCommandString'
98/*---------------------------------------------------------------------------*/
99
100
101/*****************************************************************************/
102// Add one item (argument or option) to the specified TXA handle
103/*****************************************************************************/
104ULONG TxaSetItem                                // RET   result
105(
106   TXHANDLE            txh,                     // IN    TXA handle
107   char               *item                     // IN    new item
108)
109{
110   ULONG               rc = TX_INVALID_HANDLE;
111   TXA_ELEMENT        *txa;
112
113   ENTER();
114
115   TRACES(( "Additional item: '%s'\n", item));
116
117   if ((txa = txaHandle2Element( txh)) != NULL)
118   {
119      rc = txaReadAndStoreItem( item, strlen(item), TRUE, FALSE, txa);
120   }
121   TRINIT(30);
122   {
123      TxaShowParsedCommand( FALSE);             // show parse result
124   }
125   TREXIT();
126   RETURN( rc);
127}                                               // end 'TxaSetItem'
128/*---------------------------------------------------------------------------*/
129
130
131/*****************************************************************************/
132// Replace current level of command options for a new command string
133/*****************************************************************************/
134ULONG TxaReParseCommand                         // RET   result
135(
136   char               *cmd                      // IN    new command string
137)
138{
139   ULONG               rc;
140
141   ENTER();
142   TRARGS(("ReParse new cmd: '%s%s%s'\n", CBC, cmd, CNN));
143
144   TxaDropParsedCommand( FALSE);                // drop current
145   rc = TxaParseCommandString(cmd, TRUE, NULL); // reparse, free-format
146   RETURN( rc);
147}                                               // end 'TxaReParseCommand'
148/*---------------------------------------------------------------------------*/
149
150
151/*****************************************************************************/
152// Get string value for specified option, convert to string for any type
153/*****************************************************************************/
154char *TxaOptionAsString                         // RET   option String value
155(
156   TXHANDLE            txh,                     // IN    TXA handle
157   char                opt,                     // IN    option character
158   int                 size,                    // IN    size of buffer
159   char               *str                      // OUT   string buffer, also
160)                                               // default when not specified
161{
162   char               *rc = str;
163   TXA_OPTION         *o;
164
165   ENTER();
166
167   if ((o = TxaGetOption( txh, opt)) != NULL)   // specified, use or convert
168   {
169      if (o->type == TXA_STRING)
170      {
171         TxCopy( str, o->value.string, size);
172      }
173      else                                      // other option type
174      {
175         sprintf( str, "%lu", o->value.number);
176      }
177   }
178   TRACES(("Option string: '%s'\n", rc));
179   RETURN( rc);
180}                                               // end 'TxaOptionAsString'
181/*---------------------------------------------------------------------------*/
182
183
184/*****************************************************************************/
185// Get verbosity value from Option 'o'
186/*****************************************************************************/
187ULONG TxaOutputVerbosity                        // RET   output verbosity value
188(
189   char                optchar                  // IN    option character
190)
191{
192   ULONG               rc = TXAO_NORMAL;        // default normal output
193   TXA_OPTION         *opt;                     // option pointer
194
195   ENTER();
196
197   if ((opt = TxaOptValue( optchar)) != NULL)   // specific verbosity
198   {
199      if (opt->type == TXA_STRING)
200      {
201         if      (strchr( "vVyY", opt->value.string[0]) != NULL)
202         {
203            rc = TXAO_VERBOSE;                  // verbose output
204         }
205         else if (strchr( "qQ", opt->value.string[0]) != NULL)
206         {
207            rc = TXAO_QUIET;                    // quiet, no output
208         }
209         else if (strchr( "xX", opt->value.string[0]) != NULL)
210         {
211            rc = TXAO_EXTREME;                  // extreme output
212         }
213         else if (strchr( "mM", opt->value.string[0]) != NULL)
214         {
215            rc = TXAO_MAXIMUM;                  // maximum output
216         }
217      }
218      else
219      {
220         rc = opt->value.number;
221      }
222   }
223   RETURN (rc);
224}                                               // end 'TxaOutputVerbosity'
225/*---------------------------------------------------------------------------*/
226
227
228/*****************************************************************************/
229// Get errorStrategy value from Option 'E' and batch-mode indicator 'quit'
230/*****************************************************************************/
231int TxaErrorStrategy                            // RET   error strategy value
232(
233   char                optchar,                 // IN    option character
234   BOOL                quit                     // IN    quit as default
235)
236{
237   int                 rc = (quit) ? 'q' : 'c'; // function return
238   TXA_OPTION         *opt;                     // option pointer
239
240   ENTER();
241
242   if ((opt = TxaOptValue( optchar)) != NULL)   // error handling specified
243   {
244      switch (opt->type)
245      {
246         case TXA_STRING: rc = tolower( opt->value.string[0]); break;
247         default:
248            if (opt->value.number != 0)
249            {
250               rc = 'i';                        // ignore errors on -E
251            }
252            break;
253      }
254   }
255   RETURN (rc);
256}                                               // end 'TxaErrorStrategy'
257/*---------------------------------------------------------------------------*/
258
259
260/*****************************************************************************/
261// Set radix class bits for input like ParseNumber, numeric options; 1 = HEX
262/*****************************************************************************/
263void TxSetNumberRadix
264(
265   ULONG               radix                    // IN    number radix
266)
267{
268   TRACES(( "Default number radix set from %lu to %lu\n",
269             txwa->radixclass, radix));
270   txwa->radixclass = radix;
271}                                               // end 'TxSetNumberRadix'
272/*---------------------------------------------------------------------------*/
273
274
275/*****************************************************************************/
276// Get radix class bits for input like ParseNumber, numeric options; 1 = HEX
277/*****************************************************************************/
278ULONG TxGetNumberRadix                          // RET   default number radix
279(
280   void
281)
282{
283   return( txwa->radixclass);
284}                                               // end 'TxGetNumberRadix'
285/*---------------------------------------------------------------------------*/
286
287
288/*****************************************************************************/
289// Parse a boolean value from a string (empty string returns TRUE)
290/*****************************************************************************/
291BOOL TxaParseBool                               // RET   boolean representation
292(
293   char               *param                    // IN    pragma parameter
294)
295{
296   BOOL                rc = TRUE;               // function return
297
298   ENTER();
299
300   if ((*param == '0') || (strnicmp( param, "N",     1) == 0) ||
301       (*param == '-') || (strnicmp( param, "OFF",   3) == 0) ||
302                          (strnicmp( param, "F",     1) == 0)  )
303   {
304      rc = FALSE;
305   }
306   BRETURN (rc);
307}                                               // end 'TxaParseBool'
308/*---------------------------------------------------------------------------*/
309
310
311/*****************************************************************************/
312// Build space separated concatenated string of arguments (and options)
313// Clips output with no warning when buffer too small!
314// Supports embedded spaces in the arguments, using single/double quoting
315/*****************************************************************************/
316char *TxaGetArgString                           // RET   argument str or NULL
317(
318   TXHANDLE            txh,                     // IN    TXA handle
319   int                 first,                   // IN    first argument to copy
320   int                 last,                    // IN    last arg, _ALL or _OPT
321   int                 bufsize,                 // IN    size of result buffer
322   char               *args                     // OUT   argument string
323)
324{
325   char               *rc = NULL;
326   TXA_ELEMENT        *txa;                     // specified TXA instance
327   int                 i;
328   int                 limit;
329   BOOL                clipped = FALSE;         // output exceeds buffer
330   TXLN                str;                     // element string
331
332   ENTER();
333
334   if ((txa = txaHandle2Element( txh)) != NULL)
335   {
336      if ((last == TXA_ALL) || (last == TXA_OPT))
337      {
338         limit = txa->argc;
339      }
340      else
341      {
342         limit = last;
343      }
344      if (first < txa->argc)                    // only when available
345      {
346         strcpy( args, txa->argv[first]);
347         for (i = first +1; (i < limit) && !clipped; i++)
348         {
349            if (strchr(txa->argv[i], ' ') == NULL)
350            {
351               strcpy( str, txa->argv[i]);      // straight copy
352            }
353            else                                // embedded spaces
354            {
355               if (strchr(txa->argv[i], '"') == NULL)
356               {
357                  sprintf( str, "\"%s\"", txa->argv[i]);
358               }
359               else                             // use single quoting
360               {
361                  sprintf( str, "'%s'", txa->argv[i]);
362               }
363            }
364            if ((strlen(args) + strlen(str) +2) < bufsize)
365            {
366               strcat( args, " ");              // spacing between arguments
367               strcat( args, str);              // possibly quoted string
368            }
369            else
370            {
371               clipped = TRUE;
372            }
373         }
374      }
375      else if ((first == TXA_OPT) && (txa->argc != 0)) // argv[0] + options
376      {
377         strcpy( args, txa->argv[0]);
378      }
379      else
380      {
381         strcpy( args, "");                     // empty string
382      }
383      if ((last == TXA_OPT) && !clipped)        // include the options too
384      {
385         TXTS     unit;
386
387         for (i = 0; (i < TXA_SIZE) && !clipped; i++)
388         {
389            switch (txa->opt[i].type)
390            {
391               case TXA_NUMBER:
392                  sprintf( str, " -%c:%lu",
393                           (char) (i + TXA_BASE),
394                           txa->opt[i].value.number);
395                  if (txa->opt[i].unit != ' ')
396                  {
397                     sprintf( unit, ",%c", txa->opt[i].unit);
398                     strcat( str, unit);
399                  }
400                  break;
401
402               case TXA_STRING:
403                  if (strchr(txa->opt[i].value.string, ' ') == NULL)
404                  {
405                     sprintf( str, " -%c:%s",
406                              (char) (i + TXA_BASE),
407                              txa->opt[i].value.string);
408                  }
409                  else                          // embedded spaces
410                  {
411                     if (strchr(txa->opt[i].value.string, '"') == NULL)
412                     {
413                        sprintf( str, " -%c:\"%s\"",
414                                 (char) (i + TXA_BASE),
415                                 txa->opt[i].value.string);
416                     }
417                     else                       // use single quoting
418                     {
419                        sprintf( str, " -%c:'%s'",
420                                 (char) (i + TXA_BASE),
421                                 txa->opt[i].value.string);
422                     }
423                  }
424                  break;
425
426               case TXA_NO_VAL:
427                  sprintf( str, " -%c", (char) (i + TXA_BASE));
428                  break;
429
430               default:                         // not set at all
431                  strcpy( str, "");
432                  break;
433            }
434            if ((strlen(args) + strlen(str) +1) < bufsize)
435            {
436               strcat( args, str);
437            }
438            else
439            {
440               clipped = TRUE;
441            }
442         }
443      }
444      rc = args;
445      TRACES(("arguments+options: '%s'\n", args));
446   }
447   RETURN( rc);
448}                                               // end 'TxaGetArgString'
449/*---------------------------------------------------------------------------*/
450
451
452/*****************************************************************************/
453// Return comment part of command, if any
454/*****************************************************************************/
455char *TxaGetComment                             // RET   comment  str or NULL
456(
457   TXHANDLE            txh,                     // IN    TXA handle
458   char               *comment                  // OUT   comment string
459)
460{
461   char               *rc = NULL;
462   TXA_ELEMENT        *txa;                     // specified TXA instance
463
464   ENTER();
465
466   if ((txa = txaHandle2Element( txh)) != NULL)
467   {
468      if (comment && strlen(txa->comment) != 0)
469      {
470         strcpy( comment, txa->comment);
471         rc = comment;
472      }
473   }
474   RETURN( rc);
475}                                               // end 'TxaGetComment'
476/*---------------------------------------------------------------------------*/
477
478
479/*****************************************************************************/
480// Test for mutual exclusive options and specified condition; handle error msg
481/*****************************************************************************/
482BOOL TxaMutualX
483(
484   TXHANDLE            txh,                     // IN    TXA handle
485   BOOL                cond,                    // IN    other  exclusive cond
486   char               *opts,                    // IN    mutual exclusive opts
487   char               *condmsg,                 // IN    msg if cond TRUE /NULL
488   char               *msgtext                  // OUT   message text     /NULL
489)                                               //       (NULL ==> TxPrint)
490{
491   BOOL                rc;                      // function return
492   int                 xo = 0;                  // exclusive option count
493   TXA_ELEMENT        *txa;                     // specified TXA instance
494
495   if ((txa = txaHandle2Element( txh)) != NULL)
496   {
497      int              i;
498      char            *o;
499
500      for (o = opts; *o; o++)                   // check each option
501      {
502         if ((*o >= TXA_BASE) && (*o < (TXA_BASE + TXA_SIZE)))
503         {
504            i = *o - TXA_BASE;                  // make it 0 based
505            if (txa->opt[ i].type != TXA_NONE)
506            {
507               xo++;
508            }
509         }
510      }
511   }
512   if (( cond && (xo == 0)) ||                  // cond but no options, or
513       (!cond && (xo <= 1))  )                  // no cond and one option,
514   {                                            // or nothing at all; OK
515      rc = TRUE;
516   }
517   else                                         // invalid combination
518   {
519      if (condmsg)
520      {
521         TXLN          error;
522         TXLN          options;
523         TXTS          nextopt;
524         int           i;
525
526         sprintf( options, "'-%c'", opts[0]);
527         for (i = 1; i < strlen(opts); i++)
528         {
529            sprintf( nextopt, "%s'-%c'",
530                     (i < (strlen(opts) -1)) ? ", " : " or ", opts[i]);
531            strcat(  options, nextopt);
532         }
533         if (cond)                              // option(s) AND condition
534         {
535            sprintf( error, "You can't use the %s%s option %s",
536                     options, (i > 4)   ? "\n " : "",
537                              (condmsg) ? condmsg : "now");
538         }
539         else                                   // non-exclusive options
540         {
541            sprintf( error, "You can only use one of the %s%s options "
542                            "at the same time", options, (i > 4) ? "\n " : "");
543         }
544         if (msgtext)                           // return the error text
545         {
546            strcpy( msgtext, error);
547         }
548         else                                   // direct TxPrint output
549         {
550            TxPrint( "\n* %s!", error);
551         }
552      }
553      rc = FALSE;
554   }
555   TRACES(( "MUTUAL cond. : %s + opts '%s' result: %s msg: '%s'\n",
556          (cond)    ? "TRUE " : "FALSE", opts,
557          (rc)      ? "TRUE " : "FALSE",
558          (condmsg) ? condmsg : "none"));
559   return (rc);
560}                                               // end 'TxaMutualX'
561/*---------------------------------------------------------------------------*/
562
563
564/*****************************************************************************/
565// Show contents of one or all instances of the TXA abstract-data-type
566/*****************************************************************************/
567void TxaShowParsedCommand                       // RET   more instances left
568(
569   BOOL                whole_stack              // IN    show all levels
570)
571{
572   TXA_ELEMENT        *txa = txacur;
573   ULONG               level = 0;
574   int                 i;
575
576
577   ENTER();
578
579   do
580   {
581      if (txa != NULL)
582      {
583         TxPrint( "\nTXA level: %lu  nr of options: %d  nr of arguments: %d\n",
584                         level, txa->optc, txa->argc);
585         if (strlen( txa->comment) != 0)
586         {
587            TxPrint(" Comment : '%s'\n", txa->comment);
588         }
589         for (i = 0; i < TXA_SIZE; i++)
590         {
591            switch (txa->opt[i].type)
592            {
593               case TXA_NUMBER:
594                  TxPrint( " opt. -%c : % -10lu = %8.8lx  Unit: '%c'\n",
595                           (char) (i + TXA_BASE),
596                           txa->opt[i].value.number,
597                           txa->opt[i].value.number,
598                           txa->opt[i].unit);
599                  break;
600
601               case TXA_STRING:
602                  TxPrint( " opt. -%c : '%s'\n",
603                           (char) (i + TXA_BASE),
604                           txa->opt[i].value.string);
605                  break;
606
607               case TXA_NO_VAL:
608                  TxPrint( " opt. -%c : no value\n",
609                           (char) (i + TXA_BASE));
610                  break;
611
612               default:
613                  break;
614            }
615         }
616         for (i = 0; i < txa->argc; i++)
617         {
618            TxPrint( " argv[% 2hu]: '%s'\n", i, txa->argv[i]);
619         }
620         TxPrint( " argc    : % 2hu\n", txa->argc);
621         txa = txa->prev;                       // to prev level
622         level++;
623      }
624   } while (txa && whole_stack);
625   TxPrint("\n");
626
627   VRETURN();
628}                                               // end 'TxaShowParsedCommand'
629/*---------------------------------------------------------------------------*/
630
631
632/*****************************************************************************/
633// Parse next item in string, skip leading spaces, honor embedded strings
634/*****************************************************************************/
635static char *txaParseNextItem                   // RET   next item or NULL
636(
637   char              **start,                   // INOUT start position
638   int                *length                   // OUT   length of item
639)
640{
641   char               *rc = NULL;               // function return
642   char               *s;
643   int                 l;
644   char                delim  = ' ';            // default delimitter
645   BOOL                escape = FALSE;
646
647   ENTER();
648
649   TRACES(("start:% -55.55s\n", *start));
650
651   if (*start != NULL)
652   {
653      for (rc = *start; *rc == ' '; rc++)       // skip leading spaces
654      {
655      }
656      if ((*rc) && (*rc != ';'))                // if not at end of command
657      {
658         for (s = rc, l = 0; *s; s++, l++)
659         {
660//          TRACES(( "*s:'%c', esc:%lu, delim:'%c'\n", *s, escape, delim));
661            if (*s == delim)
662            {
663               if (delim == ' ')                // end of item reached
664               {
665                  break;
666               }
667               else if (!escape)                // end-of string ...
668               {
669                  delim = ' ';                  // continue
670               }
671            }
672            else if (((*s == '"') || (*s == '\'')) &&
673                     ((!escape) && (delim == ' '))  )
674            {
675               delim = *s;                      // to end of string
676            }
677            else if ( (*s     == '\\') &&       // possible escape
678                     ((*(s+1) == ';')  ||       // for comment
679                      (*(s+1) == '"')  ||       // for double quote
680                      (*(s+1) == '\'') ))       // or single quote
681            {
682               escape = TRUE;                   // escape next char
683               continue;
684            }
685            else if ((*s == ';') && (!escape) && (delim == ' '))
686            {
687               break;
688            }
689            escape = FALSE;
690         }
691         *start  = s;                           // next parse start
692         *length = l;                           // length current item
693      }
694      else if (*rc != ';')
695      {
696         rc = NULL;                             // end of string reached
697      }
698   }
699   RETURN (rc);
700}                                               // end 'txaParseNextItem'
701/*---------------------------------------------------------------------------*/
Note: See TracBrowser for help on using the browser.