www.pudn.com > CMDL.zip > CMDL.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#include #include 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(); }