// // srecord - manipulate eprom load files // Copyright (C) 1998, 1999, 2002, 2003, 2006, 2007 Peter Miller // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 3 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program. If not, see // . // #include #include #include #include using namespace std; #include #include #include #include #include // Cygwin's mingw has the execvp prototype in the wrong place. #ifdef __MSVCRT__ #include #endif static const arglex::table_ty default_table[] = { { "-", arglex::token_stdio, }, { "-Help", arglex::token_help, }, { "-LICense", arglex::token_license, }, { "-Page_Length", arglex::token_page_length, }, { "-Page_Width", arglex::token_page_width, }, { "-TRACIng", arglex::token_tracing, }, { "-Verbose", arglex::token_verbose, }, { "-VERSion", arglex::token_version, }, ARGLEX_END_MARKER }; arglex::arglex() : usage_tail_(0) { table_set(default_table); } arglex::arglex(int ac, char **av) : usage_tail_(0) { progname_set(av[0]); for (int j = 1; j < ac; ++j) { if (av[j][0] == '@') read_arguments_file(av[j] + 1); else arguments.push_back(av[j]); } table_set(default_table); } void arglex::read_arguments_file(const char *filename) { FILE *fp = fopen(filename, "r"); if (!fp) quit_default.fatal_error_errno("open \"%s\"", filename); for (;;) { int sc = getc(fp); if (sc == EOF) break; unsigned char c = sc; // // Ignore white space between words. // if (isspace(c)) continue; // // Ignore comments // if (c == '#') { for (;;) { sc = getc(fp); if (sc == EOF || sc == '\n') break; } continue; } char buffer[1000]; char *bp = buffer; for (;;) { if (bp < buffer + sizeof(buffer) - 1) *bp++ = c; sc = getc(fp); if (sc == EOF) break; c = sc; if (isspace(c)) break; if (c == '#') { ungetc(c, fp); break; } } *bp = 0; if (buffer[0] == '@') read_arguments_file(buffer + 1); else arguments.push_back(std::string(buffer, bp - buffer)); } fclose(fp); } arglex::~arglex() { } void arglex::table_set(const table_ty *tp) { tables.push_back(tp); } // // NAME // arglex_compare // // SYNOPSIS // int arglex_compare(const char *formal, char *actual); // // DESCRIPTION // The arglex_compare function is used to compare // a command line string with a formal spec of the option, // to see if they compare equal. // // The actual is case-insensitive. Uppercase in the formal // means a mandatory character, while lower case means optional. // Any number of consecutive optional characters may be supplied // by actual, but none may be skipped, unless all are skipped to // the next non-lower-case letter. // // The underscore (_) is like a lower-case minus, // it matches "", "-" and "_". // // The "*" in a pattern matches everything to the end of the line, // anything after the "*" is ignored. The rest of the line is pointed // to by the "partial" variable as a side-effect (else it will be 0). // This rather ugly feature is to support "-I./dir" type options. // // A backslash in a pattern nominates an exact match required, // case must matche excatly here. // This rather ugly feature is to support "-I./dir" type options. // // For example: "-project" and "-P' both match "-Project", // as does "-proJ", but "-prj" does not. // // For example: "-devDir" and "-d_d' both match "-Development_Directory", // but "-dvlpmnt_drctry" does not. // // For example: to match include path specifications, use a pattern // such as "-\\I*", and the partial global variable will have the // path in it on return. // // ARGUMENTS // formal - the "pattern" for the option // actual - what the user supplied // // RETURNS // int; zero if no match, // non-zero if they do match. // static const char *partial; bool arglex_compare(const char *formal, const char *actual) { for (;;) { unsigned char ac = *actual++; if (isupper(ac)) ac = tolower(ac); unsigned char fc = *formal++; switch (fc) { case 0: return !ac; case '_': if (ac == '-') break; // fall through... case 'a': case 'b': case 'c': case 'd': case 'e': case 'f': case 'g': case 'h': case 'i': case 'j': case 'k': case 'l': case 'm': case 'n': case 'o': case 'p': case 'q': case 'r': case 's': case 't': case 'u': case 'v': case 'w': case 'x': case 'y': case 'z': // // optional characters // if (ac == fc && arglex_compare(formal, actual)) return true; // // skip forward to next // mandatory character, or after '_' // while (islower(*formal)) ++formal; if (*formal == '_') { ++formal; if (ac == '_' || ac == '-') ++actual; } --actual; break; case '*': // // This is a hack, it should really // check for a match match the stuff after // the '*', too, a la glob. // if (!ac) return false; partial = actual - 1; return true; case '\\': if (actual[-1] != *formal++) return false; break; case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': case 'G': case 'H': case 'I': case 'J': case 'K': case 'L': case 'M': case 'N': case 'O': case 'P': case 'Q': case 'R': case 'S': case 'T': case 'U': case 'V': case 'W': case 'X': case 'Y': case 'Z': fc = tolower(fc); // fall through... default: // // mandatory characters // if (fc != ac) return false; break; } } } // // NAME // is_a_number // // SYNOPSIS // int is_a_number(char *s); // // DESCRIPTION // The is_a_number function is used to determine if the // argument is a number. // // The value is placed in arglex_value.alv_number as // a side effect. // // Negative and positive signs are accepted. // The C conventions for decimal, octal and hexadecimal are understood. // // There may be no white space anywhere in the string, // and the string must end after the last digit. // Trailing garbage will be interpreted to mean it is not a string. // // ARGUMENTS // s - string to be tested and evaluated // // RETURNS // int; zero if not a number, // non-zero if is a number. // static int is_a_number(const char *s, long &n) { int sign; n = 0; switch (*s) { case '-': ++s; sign = -1; break; case '+': ++s; sign = 1; break; default: sign = 1; break; } switch (*s) { case '0': if ((s[1] == 'x' || s[1] == 'X') && s[2]) { s += 2; for (;;) { switch (*s) { case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': n = n * 16 + *s++ - '0'; continue; case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': n = n * 16 + *s++ - 'A' + 10; continue; case 'a': case 'b': case 'c': case 'd': case 'e': case 'f': n = n * 16 + *s++ - 'a' + 10; continue; } break; } } else { for (;;) { switch (*s) { case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': n = n * 8 + *s++ - '0'; continue; } break; } } break; case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': for (;;) { switch (*s) { case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': n = n * 10 + *s++ - '0'; continue; } break; } break; default: return 0; } if (*s) return 0; n *= sign; return 1; } // // NAME // arglex // // SYNOPSIS // int arglex::token_next(void); // // DESCRIPTION // The arglex function is used to perfom lexical analysis // on the command line arguments. // // Unrecognised options are returned as arglex_token_option // for anything starting with a '-', or // arglex_token_string otherwise. // // RETURNS // The next token in the token stream. // When the end is reached, arglex_token_eoln is returned forever. // // CAVEAT // Must call arglex_init befor this function is called. // int arglex::token_next() { const table_ty *tp; const table_ty *hit[20]; int nhit; std::string arg; if (!pushback.empty()) { // // the second half of a "-foo=bar" style argument. // arg = pushback.back(); pushback.pop_back(); } else { if (arguments.empty()) { value_string_ = ""; token = token_eoln; return token; } arg = arguments.front(); arguments.pop_front(); // // See if it looks like a GNU "-foo=bar" option. // Split it at the '=' to make it something the // rest of the code understands. // if (arg[0] == '-' && arg[1] != '=') { const char *eqp = strchr(arg.c_str(), '='); if (eqp) { pushback.push_back(eqp + 1); arg = std::string(arg.c_str(), eqp - arg.c_str()); } } // // Turn the GNU-style leading "--" // into "-" if necessary. // if ( arg.size() > 2 && arg[0] == '-' && arg[1] == '-' && !is_a_number(arg.c_str() + 1, value_number_) ) arg = std::string(arg.c_str() + 1); } value_string_ = arg; // // see if it is a number // if (is_a_number(arg.c_str(), value_number_)) { token = arglex::token_number; return token; } // // scan the tables to see what it matches // nhit = 0; partial = 0; for ( table_ptr_vec_t::iterator it = tables.begin(); it != tables.end(); ++it ) { for (tp = *it; tp->name; tp++) { if (arglex_compare(tp->name, arg.c_str())) hit[nhit++] = tp; } } // // deal with unknown or ambiguous options // switch (nhit) { case 0: // // not found in the tables // if (value_string_[0] == '-') token = arglex::token_option; else token = arglex::token_string; break; default: // // if all the hits are the same, it isn't ambiguous // { bool all_same = true; string possibilities = hit[0]->name; for (int k = 1; k < nhit; ++k) { if (hit[0]->token != hit[k]->token) all_same = false; possibilities += ", "; possibilities += hit[k]->name; } if (!all_same) { fatal_error ( "option \"%s\" is ambiguous, did you mean one of: %s?", value_string_.c_str(), possibilities.c_str() ); // NOTREACHED } } // fall through... case 1: if (partial) { pushback.push_back(partial); partial = 0; } value_string_ = hit[0]->name; token = hit[0]->token; break; } return token; } const char * arglex::token_name(int n) { switch (n) { case token_eoln: return "end of command line"; case token_number: return "number"; case token_option: return "option"; case token_stdio: return "standard input or output"; case token_string: return "string"; default: break; } for ( table_ptr_vec_t::iterator it = tables.begin(); it != tables.end(); ++it ) { for (const table_ty *tp = *it; tp->name; ++tp) { if (tp->token == n) return tp->name; } } return "unknown command line token"; } void arglex::help(const char *name) const { if (!name) name = progname_get(); const char *cmd[3] = { "man", name, 0 }; execvp(cmd[0], (char *const *)cmd); cerr << cmd[0] << ": " << strerror(errno) << endl; exit(1); } void arglex::version() const { cout << progname_get() << " version " << version_stamp() << endl; cout << "Copyright (C) " << copyright_years() << " Peter Miller" << endl; cout << endl; cout << "The " << progname_get() << " program comes with ABSOLUTELY NO WARRANTY;" << endl; cout << "for details use the '" << progname_get() << " -LICense' command." << endl; cout << "The " << progname_get() << " program is free software, and you are welcome" << endl; cout << "to redistribute it under certain conditions; for" << endl; cout << "details use the '" << progname_get() << " -LICense' command." << endl; exit(0); } void arglex::license() const { help("srec_license"); } void arglex::bad_argument() const { switch (token_cur()) { case token_string: cerr << "misplaced file name (\"" << value_string() << "\") on command line" << endl; break; case token_number: cerr << "misplaced number (" << value_string() << ") on command line" << endl; break; case token_option: cerr << "unknown \"" << value_string() << "\" option" << endl; break; case token_eoln: cerr << "command line too short" << endl; break; default: cerr << "misplaced \"" << value_string() << "\" option" << endl; break; } usage(); // NOTREACHED } int arglex::token_first() { switch (token_next()) { default: return token_cur(); case token_help: if (token_next() != token_eoln) bad_argument(); help(); break; case token_version: if (token_next() != token_eoln) bad_argument(); version(); break; case token_license: if (token_next() != token_eoln) bad_argument(); license(); break; } exit(0); } void arglex::usage_tail_set(const char *s) { usage_tail_ = s; } const char * arglex::usage_tail_get() const { if (!usage_tail_) usage_tail_ = "..."; return usage_tail_; } void arglex::usage() const { cerr << "Usage: " << progname_get() << " [