static HParseResult* parse_difference(void *env, HParseState *state) { HTwoParsers *parsers = (HTwoParsers*)env; // cache the initial state of the input stream HInputStream start_state = state->input_stream; HParseResult *r1 = h_do_parse(parsers->p1, state); // if p1 failed, bail out early if (NULL == r1) { return NULL; } // cache the state after parse #1, since we might have to back up to it HInputStream after_p1_state = state->input_stream; state->input_stream = start_state; HParseResult *r2 = h_do_parse(parsers->p2, state); // TODO(mlp): I'm pretty sure the input stream state should be the post-p1 state in all cases state->input_stream = after_p1_state; // if p2 failed, restore post-p1 state and bail out early if (NULL == r2) { return r1; } size_t r1len = token_length(r1); size_t r2len = token_length(r2); // if both match but p1's text is shorter than p2's, fail if (r1len < r2len) { return NULL; } else { return r1; } }
void scan_sections(struct test *test, scanstate *scanner, void (*parseproc)(struct test *test, int sec, const char *datap, int len, void *refcon), void *refcon) { // if the testfile is already at its eof, it means that // it didn't have any sections. therefore, we'll assume // defaults for all values. we're done. if(scan_is_finished(scanner)) { return; } do { int tokno = scan_next_token(scanner); if(tokno < 0) { test_abort(test, "scan_sections error %d pulling status tokens: " "%s\n", tokno, strerror(errno)); } else if(tokno == 0) { break; } (*parseproc)(test, tokno, token_start(scanner), token_length(scanner), refcon); } while(!scan_is_finished(scanner)); // give the parser an eof token so it can finalize things. (*parseproc)(test, 0, NULL, 0, refcon); }
void test_command_copy(struct test *test, FILE *fp) { int oldline; do { oldline = test->testscanner.line; int tokno = scan_next_token(&test->testscanner); if(tokno < 0) { test_abort(test, "Error %d pulling status tokens: %s\n", tokno, strerror(errno)); } else if(tokno == 0) { // if the test file is totally empty. break; } if(tokno != exCOMMAND) { // now we attempt to push the token back on the stream... scan_pushback(&test->testscanner); test->testscanner.line = oldline; // The pushback reset the stream, and I restored the line number, // but the scanner is still in a different state. // We need it to be in a COMMAND state, so that when it feeds // the new SECTION token it marks it NEW. Reattaching resets // the state to a command state, so we can just do that. tfscan_attach(&test->testscanner); // Now we're done dumping the command and the scanner // is poised to return the correct section start to the // next client. break; } // print the modified data to the output stream. rewrite_command_section(test, tokno, token_start(&test->testscanner), token_length(&test->testscanner)); if(fp) { // print the unmodified data to the command script. fwrite(token_start(&test->testscanner), token_length(&test->testscanner), 1, fp); } } while(!scan_is_finished(&test->testscanner)); rewrite_command_section(test, 0, NULL, 0); }
void scan_status_file(struct test *test) { char lastfile[PATH_MAX]; int lastfile_good = 0; char buf[BUFSIZ]; scanstate ss; int tok; int state = 0; // first rewind the status file if(lseek(test->statusfd, 0, SEEK_SET) < 0) { test_abort(test, "read_file lseek on status file: %s\n", strerror(errno)); } // then create our scanner scanstate_init(&ss, buf, sizeof(buf)); readfd_attach(&ss, test->statusfd); stscan_attach(&ss); // now, if we see the token "CBRUNNING" in the token stream, // it means that we attempted to start the test. If not, // then the test bailed early. do { tok = scan_next_token(&ss); // look for errors... if(tok < 0) { test_abort(test, "Error %d pulling status tokens: %s\n", tok, strerror(errno)); } else if(tok == stGARBAGE) { fprintf(stderr, "Garbage on line %d in the status file: '%.*s'\n", ss.line, (int)token_length(&ss)-1, token_start(&ss)); } else { state = tok; } switch(tok) { case stSTART: // nothing to do break; case stCONFIG: if(test->status == test_pending) { test->num_config_files += 1; if(copy_status_arg(token_start(&ss), token_end(&ss), lastfile, sizeof(lastfile))) { lastfile_good = 1; } else { fprintf(stderr, "CONFIG needs arg on line %d of the status file: '%.*s'\n", ss.line, (int)token_length(&ss)-1, token_start(&ss)); } } else { fprintf(stderr, "CONFIG but status (%d) wasn't pending on line %d of the status file: '%.*s'\n", test->status, ss.line, (int)token_length(&ss)-1, token_start(&ss)); } break; case stPREPARE: // nothing to do break; case stRUNNING: if(test->status == test_pending) { test->status = test_was_started; if(strlen(test->testfile) < sizeof(lastfile)) { strcpy(lastfile, test->testfile); lastfile_good = 1; } else { fprintf(stderr, "RUNNING lastfile is not big enough for %s", test->testfile); } } else { fprintf(stderr, "RUNNING but status (%d) wasn't pending on line %d of the status file: '%.*s'\n", test->status, ss.line, (int)token_length(&ss)-1, token_start(&ss)); } break; case stDONE: if(test->status == test_was_started) { test->status = test_was_completed; } else { fprintf(stderr, "DONE but status (%d) wasn't RUNNING on line %d of the status file: '%.*s'\n", test->status, ss.line, (int)token_length(&ss)-1, token_start(&ss)); } break; case stABORTED: test->status = (test->status >= test_was_started ? test_was_aborted : config_was_aborted); test->status_reason = dup_status_arg(token_start(&ss), token_end(&ss)); break; case stDISABLED: test->status = (test->status >= test_was_started ? test_was_disabled : config_was_disabled); test->status_reason = dup_status_arg(token_start(&ss), token_end(&ss)); break; default: fprintf(stderr, "Unknown token (%d) on line %d of the status file: '%.*s'\n", tok, ss.line, (int)token_length(&ss)-1, token_start(&ss)); } } while(!scan_is_finished(&ss)); if(lastfile_good) { test->last_file_processed = strdup(lastfile); } }
bool has_alpha(int t_id) { return (t_id > 255) && ((token_length((token_id)t_id) > 3) || alpha_with_2_or_3_letters(t_id)); }