/* keyparm.c Keyword Parameter Library Copyright 2004 Eric L. Michelsen. All rights reserved. This file contains the library for processing keyword cmd line parameters. Keywords must be named from A-Za-z_ (as C identifiers), and are case *sensitive*. 'long double' supported only to 'double' precision Author: Eric L. Michelsen Design Doc: Sadly, none. Date Created: 9/21/04 ============================================================================*/ // ---------------- Open Issues (do not remove) --------------- // 'long double' supported only to 'double' precision // ---------------- System Includes (do not remove) --------------- #include // fprintf() #include // strtoll() #include // strchr(), etc. #include // isspace() #ifdef __BORLANDC__ #define strtoll strtol // do what ya gotta do: Borland has no equiv to strtoll() #endif // ---------------- Local Includes (do not remove) --------------- #include "comtypes.h" #include "gi_keyparm.h" // ---------------- Constants (do not remove) --------------- #define WHITE " \t\n\r" PRIVATE const char keychars[] = // valid keyword characters "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_"; // ---------------- Structures/Types (do not remove) --------------- // ---------------- Public Variables (do not remove) --------------- // Define these sparingly. PUBLIC char kp_errorstr[320]="none"; // ---------------- Private Variables (do not remove) --------------- // ---------------- Private Prototypes/Macros (do not remove) ------------- #define SETERR(s) \ sprintf(kp_errorstr, "%s.%d: %s", __func__, __LINE__, s) #define SETERR2(s,x) \ sprintf(kp_errorstr, "%s.%d: " s, __func__, __LINE__, x) #define SETERR3(s,x,y) \ sprintf(kp_errorstr, "%s.%d: " s, __func__, __LINE__, x, y) /*---------------------------------------------------------------------------- keyword_parameters(): Processes keyword parameters from the argv[] list. Strips out all the recognized parameters from argv[] and argc, so caller can follow up with positional parameter processing. Integer parameters can be decimal, octal, or hex, with usual syntax of strtol(). ----------------------------------------------------------------------------*/ PUBLIC int32 keyword_parameters( // # of parameters recognized, < 0 on error const keyentry klist[], // list of parameters to recognize INOUT int * argc_ptr, // updated with post-processing value INOUT char * argv[]) // recognized parms removed { int largc = *argc_ptr; // local value of argc int32 found, nfound = 0, val; // Process keyword parameter from last to first (just 'cuz it's easier) while(--largc >= 1) // don't process argv[0] { if( (found = keyword_scan(klist, argv[largc])) > 0) { nfound += found; // strip parameter from argv[] val = largc; // move subsequent parameters left 1 position do { argv[val] = argv[val+1]; val++; } while(argv[val]); (*argc_ptr)--; // decrement caller's argc } } // while(--largc ... return nfound; } // keyword_parameters() /*---------------------------------------------------------------------------- keyword_convert1() Finds the first valid keyword parameter in a string. Processes it, and returns the keyentry, and start & end chars, so caller can choose how to deal with the result. The string is not modified, but the caller can easily do so. Integer parameters can be in decimal, octal, or hex, with syntax of strtol() 'long double' is supported, but only to 'double' precision by strtod(). String types may be delimited by space, quotes, or apostrphes. On return, *sptr and *endptr are as follows: xyz = 1234 sptr^ ^endptr ----------------------------------------------------------------------------*/ PUBLIC int32 keyword_convert1( // # parameters recognized (0 or 1), < 0 on error const keyentry klist[], // list of parameters to recognize INOUT char *ptr, // string to scan for parameters const keyentry **found, // the matching keyentry, NULL if none OUT char **sptr, // start of keyword OUT char **endptr) // one past end of keyword value { char *eqptr, *cptr, *eptr, termc; int nfound=0, error=0, key; int64 val; double fval; *found = NULL; // assume the worst: nothing found // "tokens" are white-space separated, optional space around '=' while(ptr) // more stuff in string { // skip forward to alphabetic (i.e. ignore stuff we don't recognize) ptr = strpbrk(ptr, keychars); if(!ptr) break; // no more parameters eqptr = strchr(ptr, '='); if(!eqptr) break; // no more parameters for(cptr = eqptr+1; isspace(*cptr); cptr++) ;// skip spaces // Find end of keyword (allow space between keyword and '=') eptr = strpbrk(ptr, WHITE "="); // keyword ends on white or '=' //printf("ptr'%s' eptr'%s' eqptr'%s' cptr'%s'\n", ptr, eptr, eqptr, cptr); for(key = 0; klist[key].typeb != KP_END; key++) { if(strlen(klist[key].name) == (size_t)(eptr - ptr) && strncmp(ptr, klist[key].name, eptr-ptr) == 0) break; } if(klist[key].typeb == KP_END) // not found { ptr = strpbrk(eqptr, WHITE); // find end of this "value" error = 1; continue; // look for more } //printf("Found %s\n", klist[key].name); if(klist[key].typeb & KP_READONLY) // not writeable { ptr = strpbrk(eqptr, WHITE); // find end of this "value" error = 1; SETERR("keyword is readonly"); continue; // look for more } // We recognized a keyword: ptr -> keyword, eqptr -> '=' // Now we must set eptr -> just past end of value if(found) *found = &klist[key]; // tell caller what we found switch(klist[key].typeb & KP_TYPE_MASK) { case KP_INT: val = strtoll(cptr, &eptr, 0);// supports decimal, octal, & hex if(klist[key].size == sizeof(int64) ) *(int64 *)klist[key].adrs = val; if(klist[key].size == sizeof(int32) ) *(int32 *)klist[key].adrs = val; if(klist[key].size == sizeof(int16) ) *(int16 *)klist[key].adrs = val; if(klist[key].size == sizeof(int8) ) *(int8 *)klist[key].adrs = val; nfound++; break; case KP_FLOAT: fval = strtod(cptr, &eptr);// supports various flt pt formats if(klist[key].size == sizeof(float) ) *(float *)klist[key].adrs = fval; if(klist[key].size == sizeof(double) ) *(double *)klist[key].adrs = fval; if(klist[key].size == sizeof(long double) ) *(long double *)klist[key].adrs = fval; nfound++; break; case KP_STRING: // We skip leading space, allow ", ', or no delimiter termc = ' '; // assume we terminate on ' ' if(*cptr == '"' || *cptr == '\'') termc = *cptr++; // Find terminating character, and truncate there eptr = strchr(cptr, termc); if(!eptr) eptr = strchr(cptr, '\0'); // backstop val = eptr - cptr; // string length eptr++; // skip over terminating char if(val >= klist[key].size-1) { fprintf(stderr, "String too long (max %d chars): '%s'\n", klist[key].size-1, cptr); val = klist[key].size - 1; } strncpy(klist[key].adrs, cptr, val); // Be sure string is properly nul terminated ((char *)(klist[key].adrs))[val] = '\0'; nfound++; break; default: fprintf(stderr, __FILE__ ":%s: Unrecognized type %d\n", klist[key].name, klist[key].typeb); ptr = strpbrk(cptr, WHITE); // attempt another assignment error = 1; continue; } // return edges of keyword parameter if(!eptr) eptr = strchr(cptr, '\0'); // safety: must be end of string if(sptr) *sptr = ptr; if(endptr) *endptr = eptr; break; } // while(ptr) if(error) return -1; return nfound; } // keyword_convert1() /*---------------------------------------------------------------------------- keyword_scan() Processes keyword parameters from a string, and deletes the recognized parameters from it. Integer parameters can be in decimal, octal, or hex, with syntax of strtol() 'long double' is supported, but only to 'double' precision by strtod(). String types may be delimited by space, quotes, or apostrphes. ----------------------------------------------------------------------------*/ PUBLIC int32 keyword_scan( // # of parameters recognized, < 0 on error const keyentry klist[], // list of parameters to recognize INOUT char *ptr) // string to scan for parameters { char *sptr, *endptr; int error = 0, nfound = 0; const keyentry *found; // "tokens" are white-space separated, optional space around '=' while(1) // more stuff in string { if(keyword_convert1(klist, ptr, &found, &sptr, &endptr) < 0) error = 1; if(!found) break; // remove parameter from string memmove(sptr, endptr, strlen(endptr) + 1); nfound++; } if(error) return -1; return nfound; } // keyword_scan() /*---------------------------------------------------------------------------- keyword_find() Find a keyword entry from a keyword struct array. ----------------------------------------------------------------------------*/ PUBLIC keyentry *keyword_find( // ptr to klist entry const keyentry klist[], // list of parameters to recognize const char *keyword) // string to scan for parameters { int32 key; for(key = 0; klist[key].typeb != KP_END; key++) { if(strcmp(keyword, klist[key].name) == 0) break; } if(klist[key].typeb == KP_END) // not found return NULL; return (keyentry *)&klist[key]; } // keyword_find() // ---------------------------------------------------------------------------- // Formats the value of a keyword as a string // 'long double' supported only to 'double' precision // WARNING: *str must be big enough for result PUBLIC char * keyword_format( // returns 'str' const keyentry *kptr, // keyword entry to format OUT char *str) // string for formatted output { int64 val; double fval; switch(kptr->typeb & KP_TYPE_MASK) { case KP_INT: if(kptr->size == sizeof(int64)) val = *(int64 *)kptr->adrs; if(kptr->size == sizeof(int32)) val = *(int32 *)kptr->adrs; if(kptr->size == sizeof(int16)) val = *(int16 *)kptr->adrs; if(kptr->size == sizeof(int8)) val = *(int8 *)kptr->adrs; if(kptr->typeb & KP_HEX) sprintf(str, "0x%llX", val); else sprintf(str, "%lld", val); break; case KP_FLOAT: if(kptr->size == sizeof(float) ) fval = *(float *)kptr->adrs; if(kptr->size == sizeof(double) ) fval = *(double *)kptr->adrs; if(kptr->size == sizeof(long double) ) fval = *(long double *)kptr->adrs; sprintf(str, "%.16g", fval); if(!strchr(str, '.')) strcat(str, "."); // floats always have "." break; case KP_STRING: sprintf(str, "\"%s\"", (char *)kptr->adrs); break; default: sprintf(str, "Unknown keyword type %d", (int)kptr->typeb); break; } return str; } // keyword_format() // ---------------------------------------------------------------------------- // Formats the name and value of a keyword as an assignment string // 'long double' supported only to 'double' precision // WARNING: *str must be big enough for result PUBLIC char * keyname_format_as( // returns 'str' const keyentry klist[], // list of parameters to recognize const char *kname, // keyword name to format OUT char *str) // string for formatted output { char locstr[80]; // temp buffer for value keyentry *kptr = keyword_find(klist, kname); if(kptr) sprintf(str, "%s=%s ", kptr->name, keyword_format(kptr, locstr) ); else str[0] = '\0'; // return empty string return str; } // keyname_format_as() // ---------------------------------------------------------------------------- PUBLIC void keyword_fprint( FILE *fp, // file pointer to print to const keyentry klist[], // list of parameters to print bool comments) // true to print comments (long form) { int key; int64 val; double fval; char str[80], *sptr; // string to hold formatted output for(key = 0; key < 1000; key++) // safety limit { if(klist[key].typeb == KP_END) break; if(comments) { fprintf(fp, "%16s =", klist[key].name); switch(klist[key].typeb & KP_TYPE_MASK) { case KP_INT: if(klist[key].size == sizeof(int64)) val = *(int64 *)klist[key].adrs; if(klist[key].size == sizeof(int32)) val = *(int32 *)klist[key].adrs; if(klist[key].size == sizeof(int16)) val = *(int16 *)klist[key].adrs; if(klist[key].size == sizeof(int8)) val = *(int8 *)klist[key].adrs; fprintf(fp, " %-16lld ", val); break; case KP_FLOAT: if(klist[key].size == sizeof(float) ) fval = *(float *)klist[key].adrs; if(klist[key].size == sizeof(double) ) fval = *(double *)klist[key].adrs; if(klist[key].size == sizeof(long double) ) fval = *(long double *)klist[key].adrs; sprintf(str, "%.10g", fval); if(!strchr(str, '.')) strcat(str, "."); // floats always have "." fprintf(fp, " %-16s ", str); break; case KP_STRING: sptr = (char *)klist[key].adrs; fprintf(fp, "\"%s\" ", sptr); for(val = strlen(sptr); val < 15; val++) fprintf(fp, " "); break; default: fprintf(fp, "Unknown type"); break; } fprintf(fp, "%s\n", klist[key].comment); } else // print short form { fprintf(fp, "%s=%s ", klist[key].name, keyword_format(&klist[key], str) ); } } if(!comments) fprintf(fp, "\n"); } // keyword_fprint()