?? 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 <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 + -