/* ** Copyright (C) 1991, 1997 Free Software Foundation, Inc. ** ** This file is part of TACK. ** ** TACK 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 2, or (at your option) ** any later version. ** ** TACK 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 TACK; see the file COPYING. If not, write to ** the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, ** Boston, MA 02110-1301, USA */ #include #if HAVE_SYS_TIME_H #include #endif MODULE_ID("$Id: control.c,v 1.9 2006/06/24 21:27:53 tom Exp $") /* terminfo test program control subroutines */ #if HAVE_GETTIMEOFDAY #define MY_TIMER struct timeval #else #define MY_TIMER time_t #endif /* globals */ int test_complete; /* counts number of tests completed */ char txt_longer_test_time[80]; /* +) use longer time */ char txt_shorter_test_time[80]; /* -) use shorter time */ static int pad_test_duration = 1; /* number of seconds for a pad test */ int auto_pad_mode; /* run the time tests */ int no_alarm_event; /* TRUE if the alarm has not gone off yet */ unsigned long usec_run_time; /* length of last test in microseconds */ static MY_TIMER stop_watch[MAX_TIMERS]; /* Hold the start timers */ char txt_longer_augment[80]; /* >) use bigger augment */ char txt_shorter_augment[80]; /* <) use smaller augment */ /* caps under test data base */ int tt_delay_max; /* max number of milliseconds we can delay */ int tt_delay_used; /* number of milliseconds consumed in delay */ const char *tt_cap[TT_MAX]; /* value of string */ int tt_affected[TT_MAX]; /* lines or columns effected (repetition factor) */ int tt_count[TT_MAX]; /* Number of times sent */ int tt_delay[TT_MAX]; /* Number of milliseconds delay */ int ttp; /* number of entries used */ /* Saved value of the above data base */ const char *tx_cap[TT_MAX]; /* value of string */ int tx_affected[TT_MAX]; /* lines or columns effected (repetition factor) */ int tx_count[TT_MAX]; /* Number of times sent */ int tx_index[TT_MAX]; /* String index */ int tx_delay[TT_MAX]; /* Number of milliseconds delay */ int txp; /* number of entries used */ int tx_characters; /* printing characters sent by test */ unsigned long tx_cps; /* characters per second */ static struct test_list *tx_source; /* The test that generated this data */ #define RESULT_BLOCK 1024 static int blocks; /* number of result blocks available */ static struct test_results *results; /* pointer to next available */ static struct test_results **pads; /* save pad results here */ static void alloc_arrays(void) { if (pads == 0) { pads = (struct test_results **)calloc(MAX_STRINGS, sizeof(struct test_results *)); } } /* ** event_start(number) ** ** Begin the stopwatch at the current time-of-day. */ void event_start(int n) { #if HAVE_GETTIMEOFDAY (void) gettimeofday(&stop_watch[n], (struct timezone *)0); #else stop_watch[n] = time((time_t *)0); #endif } /* ** event_time(number) ** ** Return the number of milliseconds since this stop watch began. */ long event_time(int n) { #if HAVE_GETTIMEOFDAY MY_TIMER current_time; (void) gettimeofday(¤t_time, (struct timezone *)0); return ((current_time.tv_sec - stop_watch[n].tv_sec) * 1000000) + current_time.tv_usec - stop_watch[n].tv_usec; #else return (time((time_t *)0) - stop_watch[n]) * 1000; #endif } /***************************************************************************** * * Execution control for string capability tests * *****************************************************************************/ /* ** get_next_block() ** ** Get a results block for pad test data. */ static struct test_results * get_next_block(void) { if (blocks <= 0) { results = (struct test_results *) malloc(sizeof(struct test_results) * RESULT_BLOCK); if (!results) { ptextln("Malloc failed"); return (struct test_results *) 0; } blocks = RESULT_BLOCK; } blocks--; return results++; } /* ** set_augment_txt() ** ** Initialize the augment menu selections */ void set_augment_txt(void) { sprintf(txt_longer_augment, ">) Change lines/characters effected to %d", augment << 1); sprintf(txt_shorter_augment, "<) Change lines/characters effected to %d", augment >> 1); } void control_init(void) { sprintf(txt_longer_test_time, "+) Change test time to %d seconds", pad_test_duration + 1); sprintf(txt_shorter_test_time, "-) Change test time to %d seconds", pad_test_duration - 1); set_augment_txt(); } /* ** msec_cost(cap, affected-count) ** ** Return the number of milliseconds delay needed by the cap. */ int msec_cost( const char *const cap, int affcnt) { int dec, value, total, star, ch; const char *cp; if (!cap) { return 0; } total = 0; for (cp = cap; *cp; cp++) { if (*cp == '$' && cp[1] == '<') { star = 1; value = dec = 0; for (cp += 2; (ch = *cp); cp++) { if (ch >= '0' && ch <= '9') { value = value * 10 + (ch - '0'); dec *= 10; } else if (ch == '.') { dec = 1; } else if (ch == '*') { star = affcnt; } else if (ch == '>') { break; } } if (dec > 1) { total += (value * star) / dec; } else { total += (value * star); } } } return total; } /* ** liberated(cap) ** ** Return the cap without padding */ char * liberated(char *cap) { static char cb[1024]; char *ts, *ls; cb[0] = '\0'; ls = NULL; if (cap) { for (ts = cb; (*ts = *cap); ++cap) { if (*cap == '$' && cap[1] == '<') { ls = ts; } ++ts; if (*cap == '>') { if (ls) { ts = ls; ls = NULL; } } } } return cb; } /* ** page_loop() ** ** send CR/LF or go home and bump letter */ void page_loop(void) { if (line_count + 2 >= lines) { NEXT_LETTER; go_home(); } else { put_crlf(); } } /* ** skip_pad_test(test-list-entry, state, ch, text) ** ** Print the start test line. Handle start up commands. ** Return TRUE if a return is requested. */ int skip_pad_test( struct test_list *test, int *state, int *ch, const char *text) { char rep_text[16]; while(1) { if (text) { ptext(text); } if ((test->flags & MENU_LC_MASK)) { sprintf(rep_text, " *%d", augment); ptext(rep_text); } ptext(" [n] > "); *ch = wait_here(); if (*ch == 's') { /* Skip is converted to next */ *ch = 'n'; return TRUE; } if (*ch == 'q') { /* Quit is converted to help */ *ch = '?'; return TRUE; } if (*ch == '\r' || *ch == '\n' || *ch == 'n' || *ch == 'r') { /* this is the only response that allows the test to run */ *ch = 0; } if (subtest_menu(pad_test_list, state, ch)) { continue; } return (*ch != 0); } } /* ** pad_done_message(test_list) ** ** Print the Done message and request input. */ void pad_done_message( struct test_list *test, int *state, int *ch) { int default_action = 0; char done_message[128]; char rep_text[16]; while (1) { if ((test->flags & MENU_LC_MASK)) { sprintf(rep_text, "*%d", augment); } else { rep_text[0] = '\0'; } if (test->caps_done) { sprintf(done_message, "(%s)%s Done ", test->caps_done, rep_text); ptext(done_message); } else { if (rep_text[0]) { ptext(rep_text); ptext(" "); } ptext("Done "); } if (debug_level & 2) { dump_test_stats(test, state, ch); } else { *ch = wait_here(); } if (*ch == '\r' || *ch == '\n') { *ch = default_action; return; } if (*ch == 's' || *ch == 'n') { *ch = 0; return; } if (strchr(pad_repeat_test, *ch)) { /* default action is now repeat */ default_action = 'r'; } if (subtest_menu(pad_test_list, state, ch)) { continue; } return; } } /* ** sliding_scale(dividend, factor, divisor) ** ** Return (dividend * factor) / divisor */ int sliding_scale( int dividend, int factor, unsigned long divisor) { double d = dividend; if (divisor) { d = (d * (double) factor) / (double) divisor; return (int) (d + 0.5); } return 0; } /* ** pad_test_startup() ** ** Do the stuff needed to begin a test. */ void pad_test_startup( int do_clear) { if (do_clear) { put_clear(); } repeats = augment; raw_characters_sent = 0; test_complete = ttp = char_count = tt_delay_used = 0; letter = letters[letter_number = 0]; if (pad_test_duration <= 0) { pad_test_duration = 1; } tt_delay_max = pad_test_duration * 1000; set_alarm_clock(pad_test_duration); event_start(TIME_TEST); } /* ** still_testing() ** ** This function is called to see if the test loop should be terminated. */ int still_testing(void) { fflush(stdout); test_complete++; return EXIT_CONDITION; } /* ** pad_test_shutdown() ** ** Do the stuff needed to end a test. */ void pad_test_shutdown( struct test_list *t, int crlf) { int i; int counts; /* total counts */ int ss; /* Save string index */ int cpo; /* characters per operation */ int delta; /* difference in characters */ int bogus; /* Time is inaccurate */ struct test_results *r; /* Results of current test */ int ss_index[TT_MAX]; /* String index */ alloc_arrays(); if (tty_can_sync == SYNC_TESTED) { bogus = tty_sync_error(); } else { bogus = 1; } usec_run_time = event_time(TIME_TEST); tx_source = t; tx_characters = raw_characters_sent; tx_cps = sliding_scale(tx_characters, 1000000, usec_run_time); /* save the data base */ for (txp = ss = counts = 0; txp < ttp; txp++) { tx_cap[txp] = tt_cap[txp]; tx_count[txp] = tt_count[txp]; tx_delay[txp] = tt_delay[txp]; tx_affected[txp] = tt_affected[txp]; tx_index[txp] = get_string_cap_byvalue(tt_cap[txp]); if (tx_index[txp] >= 0) { if (cap_match(t->caps_done, strnames[tx_index[txp]])) { ss_index[ss++] = txp; counts += tx_count[txp]; } } } if (crlf) { put_crlf(); } if (counts == 0 || tty_cps == 0 || bogus) { /* nothing to do */ return; } /* calculate the suggested pad times */ delta = usec_run_time - sliding_scale(tx_characters, 1000000, tty_cps); if (delta < 0) { /* probably should bump tx_characters */ delta = 0; } cpo = delta / counts; for (i = 0; i < ss; i++) { if (!(r = get_next_block())) { return; } r->next = pads[tx_index[ss_index[i]]]; pads[tx_index[ss_index[i]]] = r; r->test = t; r->reps = tx_affected[ss_index[i]]; r->delay = cpo; } } /* ** show_cap_results(index) ** ** Display the previous results */ static void show_cap_results( int x) { struct test_results *r; /* a result */ int delay; alloc_arrays(); if ((r = pads[x])) { sprintf(temp, "(%s)", strnames[x]); ptext(temp); while (r) { sprintf(temp, "$<%d>", r->delay / 1000); put_columns(temp, (int) strlen(temp), 10); r = r->next; } r = pads[x]; while (r) { if (r->reps > 1) { delay = r->delay / (r->reps * 100); sprintf(temp, "$<%d.%d*>", delay / 10, delay % 10); put_columns(temp, (int) strlen(temp), 10); } r = r->next; } put_crlf(); } } /* ** dump_test_stats(test_list, status, ch) ** ** Dump the statistics about the last test */ void dump_test_stats( struct test_list *t, int *state, int *ch) { int i, j; char tbuf[32]; int x[32]; put_crlf(); if (tx_source && tx_source->caps_done) { cap_index(tx_source->caps_done, x); if (x[0] >= 0) { sprintf(temp, "Caps summary for (%s)", tx_source->caps_done); ptextln(temp); for (i = 0; x[i] >= 0; i++) { show_cap_results(x[i]); } put_crlf(); } } sprintf(tbuf, "%011lu", usec_run_time); sprintf(temp, "Test time: %lu.%s, characters per second %lu, characters %d", usec_run_time / 1000000UL, &tbuf[5], tx_cps, tx_characters); ptextln(temp); for (i = 0; i < txp; i++) { if ((j = get_string_cap_byvalue(tx_cap[i])) >= 0) { sprintf(tbuf, "(%s)", strnames[j]); } else { strcpy(tbuf, "(?)"); } sprintf(temp, "%8d %3d $<%3d> %8s %s", tx_count[i], tx_affected[i], tx_delay[i], tbuf, expand(tx_cap[i])); putln(temp); } generic_done_message(t, state, ch); } /* ** longer_test_time(test_list, status, ch) ** ** Extend the number of seconds for each test. */ void longer_test_time( struct test_list *t GCC_UNUSED, int *state GCC_UNUSED, int *ch) { pad_test_duration += 1; sprintf(txt_longer_test_time, "+) Change test time to %d seconds", pad_test_duration + 1); sprintf(txt_shorter_test_time, "-) Change test time to %d seconds", pad_test_duration - 1); sprintf(temp, "Tests will run for %d seconds", pad_test_duration); ptext(temp); *ch = REQUEST_PROMPT; } /* ** shorter_test_time(test_list, status, ch) ** ** Shorten the number of seconds for each test. */ void shorter_test_time( struct test_list *t GCC_UNUSED, int *state GCC_UNUSED, int *ch) { if (pad_test_duration > 1) { pad_test_duration -= 1; sprintf(txt_longer_test_time, "+) Change test time to %d seconds", pad_test_duration + 1); sprintf(txt_shorter_test_time, "-) Change test time to %d seconds", pad_test_duration - 1); } sprintf(temp, "Tests will run for %d second%s", pad_test_duration, pad_test_duration > 1 ? "s" : ""); ptext(temp); *ch = REQUEST_PROMPT; } /* ** longer_augment(test_list, status, ch) ** ** Lengthen the number of lines/characters effected */ void longer_augment( struct test_list *t, int *state GCC_UNUSED, int *ch) { augment <<= 1; set_augment_txt(); if (augment_test) { t = augment_test; } sprintf(temp, "The pad tests will effect %d %s.", augment, ((t->flags & MENU_LC_MASK) == MENU_lines) ? "lines" : "characters"); ptextln(temp); *ch = REQUEST_PROMPT; } /* ** shorter_augment(test_list, status, ch) ** ** Shorten the number of lines/characters effected */ void shorter_augment( struct test_list *t, int *state GCC_UNUSED, int *ch) { if (augment > 1) { /* don't let the augment go to zero */ augment >>= 1; } set_augment_txt(); if (augment_test) { t = augment_test; } sprintf(temp, "The pad tests will effect %d %s.", augment, ((t->flags & MENU_LC_MASK) == MENU_lines) ? "lines" : "characters"); ptextln(temp); *ch = REQUEST_PROMPT; }