⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 cmdl.cpp

📁 c++编写的命令行解释器
💻 CPP
字号:
/***************************************************************
File: CMDL.CPP                Copyright 1992 by Dlugosz Software
part of the CMDL package for command-line parsing
This version may be used freely, with attribution.
***************************************************************/

#include "usual.h"
#include "cmdl.h"
#include "scanner.h"
#include <stdio.h>
#include <string.h>
#include <stdlib.h>

cmdl* cmdl::head= 0;
cmdl* cmdl::last= 0;
#ifdef VERSION21
 cmdl::errval cmdl::error= OK;
#else
 errval cmdl::error= OK;
#endif
int cmdl::count= 0;
char* cmdl::signon_string= 0;

/* /\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\ */
/*     parseing and processing              */
/* /\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\ */

cmdl* cmdl::find_switch (char c)
{  //find switch matching char name
for (cmdl* p= head;  p;  p=p->next) {
   if (p->iskeyword()) continue;
   if (!p->ischar()) continue;
   // filtered out the ones I don't care about,
   // now match the name
   if (c == p->cname) return p;  //found it!
   }
return 0;  //found nothing.
}

/* /\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\ */

cmdl* cmdl::find_keyword (char c)
{  //find keyword matching char name (no leading switchchar)
for (cmdl* p= head;  p;  p=p->next) {
   if (!p->iskeyword()) continue;
   if (!p->ischar()) continue;
   // filtered out the ones I don't care about,
   // now match the name
   if (c == p->cname) return p;  //found it!
   }
return 0;  //found nothing.
}

/* /\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\ */

cmdl* cmdl::find_switch (char* s)
{  //find switch matching string name
for (cmdl* p= head;  p;  p=p->next) {
   if (p->iskeyword()) continue;
   if (p->ischar()) continue;
   // filtered out the ones I don't care about,
   // now match the name
   if (!strcmp (s,p->sname)) return p;  //found it!
   }
return 0;  //found nothing.
}

/* /\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\ */

cmdl* cmdl::find_keyword (char* s)
{  //find keyword matching string name (no leading switchchar)
for (cmdl* p= head;  p;  p=p->next) {
   if (!p->iskeyword()) continue;
   if (p->ischar()) continue;
   // filtered out the ones I don't care about,
   // now match the name
   if (!strcmp (s,p->sname)) return p;  //found it!
   }
return 0;  //found nothing.
}

/* /\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\ */

bool cmdl::parse_switches (cmdlscan& s, unsigned /*flags*/)
{
/* this matches the next thing in the input stream `s' against
   all the switch entries.  It handles cascaded single-character
   switches in one gulp.
   Flags not used yet... I'll think of something.
*/
int passcount= 0;  //how many switches were processed
++s;  //skip over the switch character
for (;;) {  //handle cascaded single character switches
   char c= s.thischar();
   cmdl* sw= find_switch (c);
   if (sw) {
      passcount++;
      count++;  //how many total
      ++s;  //advance beyond the name
      if (!sw->scan (s)) {
         sw->report_error();
         return FALSE;
         }
      }
   else break;  //did not find another switch
   }
if (passcount == 0) {  //try a word switch
   const wordsize= 50;  //longest identifier possible
   char word[wordsize];
   s.extract_word (word,wordsize);
   cmdl* sw= find_switch (word);
   if (sw) {  //found one
      count++;
      sw->scan (s);
      }
   else {
      // switch not found
      output ("error: switch -");
      output (word);
      output ("\ninvalid switch.\n");
      return FALSE;
      }
   }
return TRUE;
}

/* /\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\ */

bool cmdl::parse_keywords (cmdlscan& s, unsigned fl)
{
/* this matches the next thing input stream `s' against
   non-switch items.  It handles positional parameters, which are
   syntactically anonomous keyword parameters
*/
int oldpos= s.mark();
const wordsize= 50;  //longest identifier possible
char word[wordsize];
s.extract_word (word,wordsize);
cmdl* sw= find_keyword (word);
if (sw) {  //found it!
   count++;
   sw->scan (s);
   return TRUE;
   }
// else check for positional parameters
s.restore (oldpos);  //rewind the input
sw= find_positional (fl);
if (sw) {
   count++;
   sw->scan (s);
   return TRUE;
   }
// if it gets to here, is an error.
output ("bad parameter: ");
output (word);
output ("\n");
return FALSE;
}

/* /\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\ */

cmdl* cmdl::find_positional (unsigned /*fl*/)
{   // look for cmdl objects with no name
for (cmdl* p= head;  p;  p=p->next) {
   if (!(p->flags & noname)) continue;
   if (p->flags & used) continue;  //used that one already
   // got here, p is the one I want.
   return p;
   }
return 0;  //did not find one
}

/* /\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\ */

bool cmdl::parseit (cmdlscan& s, unsigned fl)
{
/* this scans the input described by `s', and processes all
   parameters it parses out.  It may terminate the program with
   an error.
*/
static int recursion_level= 0;
const recursion_limit= 10;
if (++recursion_level >= recursion_limit) {
   /* it is possible for a parameter to create more input.  I.e.
     an @ will read the contents of a file, and a % will read an
     environment variable.  None of this is implemented, but it is
     planned and provided for.  */
   output ("error: nesting level too deep.\n");
   recursion_level--;
   return FALSE;
   }
for (;;) {
   s.skipws();
   switch (s.thischar()) {
      case '\0': 
         recursion_level--;
         return TRUE;     //got through the whole string
      case '-':
      case '/':
         if (!parse_switches (s,fl)) {
            recursion_level--;
            return FALSE;
            }
         break;
      /* other things, such as the above-mentioned @ and % will
         go here as additional cases. */
      default:
         if (!parse_keywords (s,fl)) {
            recursion_level--;
            return FALSE;
            }
         break;
      }
   }
}

/* /\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\ */

bool cmdl::parseit (char* s, unsigned fl)
{
/* this parses an explicit string.  It creates the scanner object
   around the input string, and adds processing after the scan
   is complete.
*/
cmdlscan scan (s);
if (!parseit (scan, fl)) {
   // return FALSE;  //depending on fl
   exit (1);
   }
// otherwise look for more errors
if (count == 0) {
   show_usage();
   exit (1);
   }
for (cmdl* p= head;  p;  p=p->next)
   p->post_process (fl);
for (p= head;  p;  p=p->next)
   p->validate (fl);
return TRUE;
}

/* /\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\ */

bool cmdl::parseit (unsigned /*fl*/)
{
/* this is the form you'll normally see.  It looks up the command-line
   tail from DOS, and passes it to the previous form.
*/
static char string[257];
// locate the command line tail in the PSP
extern unsigned _psp;
char _seg * psp= (char _seg*) _psp;
const offset= 0x80;
char far* commandline= psp+offset;
int len= *commandline++;
for (int loop= 0;  loop < len;  loop++)
   string[loop]= commandline[loop];
   // _fmemcpy() not available in TC++1.01 (a.k.a. "second edition").  Bummer
string[len]= '\0';
return parseit (string);
}

/* /\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\ */
/*     constructors and helpers             */
/* /\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\ */

void cmdl::linkin()
{
/* this is how the parser knows about all command line objects.
   They are maintained in a linked list.  Note that they are never
   _removed_ from the list, so it is a real bad idea to let any
   command line object go out of scope before calling parseit().
   After parsing, the list is abandoned, never to be traversed again.
*/
next= 0;
if (!head) head= last= this;  //make a list of one.
else {
   // chain to end of list
   last->next= this;
   last= this;
   }
}

/* /\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\ */

cmdl::cmdl (const char* name, const char* helpstring, unsigned fl)
: helpstring(helpstring), flags(fl)
{     //STRING form
if (!name) flags |= noname;    //note positional parameters
else {
   int len= strlen (name);
   if (len == 0) {
      //note name as a char
      flags |= charname;
      cname= name[0];
      }
   else sname= name;   //note name as a string.
   }
linkin();
}

/* /\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\ */

cmdl::cmdl (char name, const char* helpstring, unsigned fl)
: helpstring(helpstring), flags(fl)
{     //CHAR form
// note name as a character
flags |= charname;
cname= name;
linkin();
}

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -