int Range::_parseSpec(RangeList *seq) { // Parse the top level TERM [; TERM] syntax // Have we seen a real preceding separator? 1=yes, -1=no, 0=maybe int seensep = 1, rc; DBGFPRINTF((stderr, "_parseSpec: at '%s'\n", pos)); do { // Read first term if ( (rc = _parseTerm(seq)) <= 0) { DBGFPRINTF((stderr, "_parseSpec:%s @ '%s'\n", errmsg, pos)); return rc; } seq = FindLastRangeList(seq); // If next chr is valid seq separator, try again char c = *pos; seensep = -1; if (c != '\0' && strchr(space, c)) { pos += strspn(pos, space); c = *pos; } if (c != '\0' && strchr(semis, c)) { seensep = 1; ++pos; c = *pos; } } while (seensep != -1); DBGFPRINTF((stderr, "_parseSpec:OK @ '%s'\n", pos)); return 1; }
int Range::_parseTerm(RangeList *seq) { // Parse the full SEQUENCE [/SEQUENCE] syntax RangeList positive = NULL, negative = NULL; int rc; DBGFPRINTF((stderr, "_parseTerm: at '%s'\n", pos)); // Skip leading WS pos += strspn(pos, space); // Read first sequence if ( (rc = _parseSequence(&positive)) <= 0) { DBGFPRINTF((stderr, "_parseTerm:%s @ '%s'\n", errmsg, pos)); return rc; } // Maybe a slash now? pos += strspn(pos, space); char c = *pos; if (c != '\0' && strchr(slashes, c)) { ++pos; // Read negative range if ( (rc = _parseSequence(&negative)) <= 0) { DBGFPRINTF((stderr, "_parseTerm:%s @ '%s'\n", errmsg, pos)); return rc; } } // Combine positive and negative sequences SubtractSeqs(&positive, negative); *seq = positive; FreeRangeList(negative); DBGFPRINTF((stderr, "_parseTerm:OK @ '%s'\n", pos)); return 1; }
int Range::_parseNumber(int *pval) { // Grab the next number from the string // ignore leading WS int fromEnd = 0; int scale = 1; int val = 0; int len = 0; DBGFPRINTF((stderr, "_parseNumber: at '%s'\n", pos)); pos += strspn(pos, space); char c = *pos; if (c == '^') { fromEnd = 1; ++pos; c = *pos; } if (c == '-') { scale = -1; ++pos; c = *pos; } if (c == '\0') { errmsg = "expecting number, hit EOS"; DBGFPRINTF((stderr, "_parseNumber:%s @ '%s'\n", errmsg, pos)); return 0; } while (c >= '0' && c <= '9') { val = 10*val + (c - '0'); ++len; ++pos; c = *pos; } if (len == 0) { errmsg = "number not found"; DBGFPRINTF((stderr, "_parseNumber:%s @ '%s'\n", errmsg, pos)); return 0; } val = scale * val; if (fromEnd) { if (max_val == RNG_VAL_BAD) { errmsg = "max_val not specified for ^ syntax"; DBGFPRINTF((stderr, "_parseNumber:%s @ '%s'\n", errmsg, pos)); return -1; } val = max_val - 1 - val; } DBGFPRINTF((stderr, "got number %d\n", val)); *pval = val; return 1; }
int Range::_parseSequence(RangeList *seq) { // Read a sequence of matlab-ranges, return as a linked list of new // RangeNodes int start, end, step, rc; DBGFPRINTF((stderr, "_parseSeq: at '%s'\n", pos)); // Have we seen a real preceding separator? 1=yes, -1=no, 0=maybe int seensep = 0; // Skip leading WS pos += strspn(pos, space); do { if ( (rc=_parseRange(&start, &step, &end)) < 0) { return rc; } else if (rc == 1) { // Found one, add it to the list *seq = new RangeNode(*seq, start, end, step); seq = &((*seq)->next); // OK for another sequence to follow without extra sep seensep = 0; } else if (seensep == 1) { errmsg = "sequence separator not followed by sequence"; DBGFPRINTF((stderr, "_parseSeq:%s @ '%s'\n", errmsg, pos)); return -1; } else { // No range seen, so don't expect another seensep = -1; } // If next chr is valid seq separator, try again char c = *pos; if (c != '\0' && strchr(space, c)) { seensep = 0; pos += strspn(pos, space); c = *pos; } if (c != '\0' && strchr(commas, c)) { seensep = 1; ++pos; c = *pos; } DBGFPRINTF((stderr, "seqLoop: seensep=%d @ '%s'\n", seensep, pos)); } while (seensep != -1); return 1; }
int Range::_parseRange(int *pstart, int *pstep, int *pend) { // Accept either a special-case token or a Matlab range // Special-case tokens int rc; int start, step, end; DBGFPRINTF((stderr, "_parseRange: at '%s'\n", pos)); // Skip leading WS pos += strspn(pos, space); int slen = strspn(pos, alpha); if (slen == 3 && strncmp(pos, "all", slen) == 0) { if (min_val == RNG_VAL_BAD) { errmsg = "min_val not specified for sequence \"all\""; DBGFPRINTF((stderr, "_parseRange:%s @ '%s'\n", errmsg, pos)); return -1; } start = min_val; if (max_val == RNG_VAL_BAD) { errmsg = "max_val not specified for sequence \"all\""; DBGFPRINTF((stderr, "_parseRange:%s @ '%s'\n", errmsg, pos)); return -1; } end = max_val - 1; step = 1; pos += slen; } else if ( (slen == 3 && strncmp(pos, "nil", slen) == 0) \ || (slen == 4 && strncmp(pos, "none", slen) == 0)) { // Construct the sequence empty but consume the token start = 0; step = 1; end = -1; pos += slen; } else { // None of the ascii tokens - let it be Matlab ranges if ( (rc=_parseMatlabRange(&start, &step, &end)) <= 0) { return rc; } } *pstart = start; *pstep = step; *pend = end; return 1; }
int cleExtractArgs(CLE_ENTRY *entries, int* pargc, char *argv[], int noExtraArgs) { /* parse a set of command line arguments according to a structure. If noExtraArgs is not set, allow for a variable number of arguments that we don't parse; return modified argc and argv pointing to the residue. Return value is number of actual error objected to. */ int i, j, errs = 0, anerr, rc, argUsed, tokDone, flaglen, toklen, minlen; int blankIndex = 0; /* use each blank index just once */ int blankCount; char *next, *arg, *tok; char tokbuf[CLE_STRLEN]; char flag[CLE_STRLEN]; CLE_ENTRY *ent; int outargc = 1; int argc = *pargc; char *pat; const char *state; int tokcontinued = 0; /* flag that reparsing remainder */ const char *programName = argv[0]; /* first, set all defaults - we can overwrite them later */ /* cleSetDefaults(entries); */ /* no, let program do this if it wants */ i = 0; while(i + 1 - tokcontinued<argc) { if (!tokcontinued) tok = argv[++i]; /* grab the next arg; preincrement ensures we skip argv[0] */ next = (i+1<argc)?argv[i+1]:NULL; ent = entries; blankCount = 0; argUsed = 0; tokDone = 0; anerr = 0; tokcontinued = 0; DBGFPRINTF((stderr, "cle(%d): testing '%s' (then '%s')\n", \ i, tok, next?next:"(NULL)")); while(!tokDone && ent->type != CLE_T_END) { state = NULL; while(!tokDone && ent->type != CLE_T_USAGE && \ (minlen = cleNextPattern(ent->flag, flag, &state)) > -1) { toklen = strlen(tok); flaglen = strlen(flag); DBGFPRINTF((stderr, "cle: trying '%s' ('%s', %d)\n", \ tok,flag,minlen)); if(flaglen == 0) ++blankCount; /* keep track of which blank field */ if( /* Allow empty flags (untagged args) to match any (new) string, but not if they start with '-' unless they are exactly "-" */ ( flaglen == 0 \ && blankCount > blankIndex \ && ( tok[0] != '-' || tok[1] == '\0' ) ) \ /* Match all the tok, at least up to minlen of the flag */ || ((toklen >= minlen) \ && (strncmp(tok, flag, toklen) == 0)) \ /* special case: 2-chr flags only need match first bit */ || ( flaglen == 2 \ && flag[0] == '-' \ && ( tok[0] == '-' && tok[1] == flag[1]) ) ) { /*** We matched a flag pattern ***/ tokDone = 1; if(flaglen == 0) { ++blankIndex; /* mark this blank field as used */ arg = tok; /* for blank flds, tok is arg */ } else if(flag[0]=='-' && flaglen==2 && strlen(tok)>flaglen) { /* simple "-X" flag was matched as prefix: separate out the remainder as an arg, or the next tok. */ arg = tok+flaglen; } else { arg = next; } switch(ent->type) { case CLE_T_INT: { int x; anerr = (arg==NULL) || (sscanf(arg, "%d", &x) != 1); if (!anerr) *(int *)(ent->where) = x; } argUsed = 1; break; case CLE_T_LONG: { long l; anerr = (arg==NULL) || (sscanf(arg, "%ld", &l) != 1); if(!anerr) *(int *)(ent->where) = l; } argUsed = 1; break; case CLE_T_FLOAT: { float f; anerr = (arg==NULL) || (sscanf(arg, "%f", &f) != 1); if(!anerr) *(float *)(ent->where) = f; } argUsed = 1; break; case CLE_T_TIME: { float f; anerr = (arg==NULL) || (cleScanTime(arg, &f) != 1); if(!anerr) *(float *)(ent->where) = f; } argUsed = 1; break; case CLE_T_STRING: if(arg==NULL) { anerr=1; } else { /* free any default value */ if (*(char **)ent->where) free(*(char **)ent->where); *(char **)(ent->where) = strdup(arg); argUsed = 1; } break; case CLE_T_BOOL: ++(*(int *)(ent->where)); break; case CLE_T_INVBOOL: --(*(int *)(ent->where)); break; case CLE_T_TOKEN: rc = cleTokenSetFn(arg,ent->where, tok, ent->specData); if(rc == 1) argUsed = 1; else if(rc != 0) anerr = 1; break; case CLE_T_SPECIAL: rc = (*(ent->specialfn))(arg, ent->where, tok, ent->specData); if(rc == 1) argUsed = 1; else if(rc != 0) anerr = 1; break; default: fprintf(stderr, "cleExtractArgs: unknown CLE_TYPE %d\n", ent->type); abort; } if(anerr) { fprintf(stderr, "%s: error reading '%s'(%s) as %s(%s)\n", programName, tok, (arg==NULL)?"NULL":arg, ent->flag, ent->usage); ++errs; } DBGFPRINTF((stderr, \ "cle(%d): matched %s %s as %s %s (%s %s)\n", \ i, tok, next?next:"(NULL)", flag, \ argUsed?arg:"", ent->flag, ent->usage)); } } ++ent; } if(!tokDone) { /* this token was not recognized at all */ float dummy; if(noExtraArgs \ || (tok[0] == '-' && strlen(tok) > 1 \ && sscanf(tok, "%f", &dummy) == 0) ) { /* report an error if we don't expect any unrecognized args - or even if we do, but the tok is of "-foo" form, *unless* it's a valid number (e.g. -123) */ fprintf(stderr, "%s: '%s' not recognized\n", programName, tok); ++errs; } else { /* just copy the missed tok to output */ argv[outargc] = tok; ++outargc; } } else if(!argUsed) { /* we recognized a token but it didn't use the arg - any arg is thus a candidate for the next token */ if(arg != next) { /* arg was not just the next token - so set it as a cont'n */ /* fakely prepend the '-' (or ...) */ tokbuf[0] = flag[0]; strcpy(tokbuf+1, arg); /* flag looptop not read next arg */ tokcontinued = 1; tok = tokbuf; } } else { /* tok used the arg. If it was the next tok, skip it */ if(arg == next) { ++i; } } } *pargc = outargc; /* if(errs) cleUsage(entries, argv[0]); */ return errs; }
int Range::_parseMatlabRange(int *pstart, int *pstep, int *pend) { // Parse a matlab-style [start][:step][:[end]] string, // which includes 1 1:5 1:2:10 :4 and : // Missing numbers take defaults from min_val and max_val int start = 0 , end = 0, step = 0, rc; DBGFPRINTF((stderr, "_parseRange: at '%s'\n", pos)); // Either the first character is a colon, or it's a number // .. but skip leading WS pos += strspn(pos, space); char c = *pos; if (c == '\0') { errmsg = "expecting Matlab range, hit EOS"; DBGFPRINTF((stderr, "_parseRange:%s @ '%s'\n", errmsg, pos)); return 0; } // Try to get first number, OK to fail if ( (rc=_parseNumber(&start)) < 0) { // fatal error in number return rc; } else if (rc == 0) { // Next chr *must* be colon c = *pos; if (c == '\0' || strchr(colons, c) == NULL) { errmsg = "Matlab range does not start with colon or number"; DBGFPRINTF((stderr, "_parseRange:%s @ '%s'\n", errmsg, pos)); return 0; } // It was a colon, but don't move over it, so we see it again blw //++pos; // default the first val if (min_val == RNG_VAL_BAD) { errmsg = "min_val not specified for default range"; DBGFPRINTF((stderr, "_parseRange:%s @ '%s'\n", errmsg, pos)); return -1; } start = min_val; } // Parse up to two subseqent ":number" terms int i; int read_end = 0; end = start; for (i = 0; i < 2; ++i) { // Skip ws pos += strspn(pos, space); // Do we have a colon? c = *pos; if (c != '\0' && strchr(colons, c)) { // We have a colon. If this is the second pass, push the // first pass to the step if (i > 0) { if (read_end == 0) { // I think this means "::" found - just ignore fprintf(stderr, "defaulted step (:: found?)\n"); } else { step = end; } } // Step over colon ++pos; // Either grab a number, or it defaults if ( (rc=_parseNumber(&end)) < 0) { return rc; } else if (rc == 0) { // Didn't find a number, so take default end if (max_val == RNG_VAL_BAD) { errmsg = "max_val not specified for default range"; DBGFPRINTF((stderr, "_parseRange:%s @ '%s'\n", errmsg, pos)); return -1; } end = max_val - 1; } else { read_end = 1; } } } // Maybe choose appropriate default step if (step == 0) { if (end < start) step = -1; else step = 1; } // Should now have it all *pstart = start; *pend = end; *pstep = step; DBGFPRINTF((stderr, "got mat range %d:%d:%d @ '%s'\n", start, step, end, pos)); return 1; }