1/* 2** Copyright (C) 1991, 1997 Free Software Foundation, Inc. 3** 4** This file is part of TACK. 5** 6** TACK is free software; you can redistribute it and/or modify 7** it under the terms of the GNU General Public License as published by 8** the Free Software Foundation; either version 2, or (at your option) 9** any later version. 10** 11** TACK is distributed in the hope that it will be useful, 12** but WITHOUT ANY WARRANTY; without even the implied warranty of 13** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14** GNU General Public License for more details. 15** 16** You should have received a copy of the GNU General Public License 17** along with TACK; see the file COPYING. If not, write to 18** the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 19** Boston, MA 02110-1301, USA 20*/ 21 22#include <tack.h> 23 24#if HAVE_SYS_TIME_H 25#include <sys/time.h> 26#endif 27 28MODULE_ID("$Id: control.c,v 1.8 2005/09/17 19:49:16 tom Exp $") 29 30/* terminfo test program control subroutines */ 31 32#if HAVE_GETTIMEOFDAY 33#define MY_TIMER struct timeval 34#else 35#define MY_TIMER time_t 36#endif 37 38/* globals */ 39int test_complete; /* counts number of tests completed */ 40 41char txt_longer_test_time[80]; /* +) use longer time */ 42char txt_shorter_test_time[80]; /* -) use shorter time */ 43static int pad_test_duration = 1; /* number of seconds for a pad test */ 44int auto_pad_mode; /* run the time tests */ 45int no_alarm_event; /* TRUE if the alarm has not gone off yet */ 46unsigned long usec_run_time; /* length of last test in microseconds */ 47static MY_TIMER stop_watch[MAX_TIMERS]; /* Hold the start timers */ 48 49char txt_longer_augment[80]; /* >) use bigger augment */ 50char txt_shorter_augment[80]; /* <) use smaller augment */ 51 52/* caps under test data base */ 53int tt_delay_max; /* max number of milliseconds we can delay */ 54int tt_delay_used; /* number of milliseconds consumed in delay */ 55const char *tt_cap[TT_MAX]; /* value of string */ 56int tt_affected[TT_MAX]; /* lines or columns effected (repetition factor) */ 57int tt_count[TT_MAX]; /* Number of times sent */ 58int tt_delay[TT_MAX]; /* Number of milliseconds delay */ 59int ttp; /* number of entries used */ 60 61/* Saved value of the above data base */ 62const char *tx_cap[TT_MAX]; /* value of string */ 63int tx_affected[TT_MAX]; /* lines or columns effected (repetition factor) */ 64int tx_count[TT_MAX]; /* Number of times sent */ 65int tx_index[TT_MAX]; /* String index */ 66int tx_delay[TT_MAX]; /* Number of milliseconds delay */ 67int txp; /* number of entries used */ 68int tx_characters; /* printing characters sent by test */ 69unsigned long tx_cps; /* characters per second */ 70static struct test_list *tx_source; /* The test that generated this data */ 71 72#define RESULT_BLOCK 1024 73static int blocks; /* number of result blocks available */ 74static struct test_results *results; /* pointer to next available */ 75static struct test_results *pads[STRCOUNT]; /* save pad results here */ 76 77/* 78** event_start(number) 79** 80** Begin the stopwatch at the current time-of-day. 81*/ 82void 83event_start(int n) 84{ 85#if HAVE_GETTIMEOFDAY 86 (void) gettimeofday(&stop_watch[n], (struct timezone *)0); 87#else 88 stop_watch[n] = time((time_t *)0); 89#endif 90} 91 92/* 93** event_time(number) 94** 95** Return the number of milliseconds since this stop watch began. 96*/ 97long 98event_time(int n) 99{ 100#if HAVE_GETTIMEOFDAY 101 MY_TIMER current_time; 102 103 (void) gettimeofday(¤t_time, (struct timezone *)0); 104 return ((current_time.tv_sec - stop_watch[n].tv_sec) * 1000000) 105 + current_time.tv_usec - stop_watch[n].tv_usec; 106#else 107 return (time((time_t *)0) - stop_watch[n]) * 1000; 108#endif 109} 110 111/***************************************************************************** 112 * 113 * Execution control for string capability tests 114 * 115 *****************************************************************************/ 116 117/* 118** get_next_block() 119** 120** Get a results block for pad test data. 121*/ 122static struct test_results * 123get_next_block(void) 124{ 125 if (blocks <= 0) { 126 results = (struct test_results *) 127 malloc(sizeof(struct test_results) * RESULT_BLOCK); 128 if (!results) { 129 ptextln("Malloc failed"); 130 return (struct test_results *) 0; 131 } 132 blocks = RESULT_BLOCK; 133 } 134 blocks--; 135 return results++; 136} 137 138/* 139** set_augment_txt() 140** 141** Initialize the augment menu selections 142*/ 143void 144set_augment_txt(void) 145{ 146 sprintf(txt_longer_augment, 147 ">) Change lines/characters effected to %d", augment << 1); 148 sprintf(txt_shorter_augment, 149 "<) Change lines/characters effected to %d", augment >> 1); 150} 151 152void 153control_init(void) 154{ 155 sprintf(txt_longer_test_time, "+) Change test time to %d seconds", 156 pad_test_duration + 1); 157 sprintf(txt_shorter_test_time, "-) Change test time to %d seconds", 158 pad_test_duration - 1); 159 set_augment_txt(); 160} 161 162/* 163** msec_cost(cap, affected-count) 164** 165** Return the number of milliseconds delay needed by the cap. 166*/ 167int 168msec_cost( 169 const char *const cap, 170 int affcnt) 171{ 172 int dec, value, total, star, ch; 173 const char *cp; 174 175 if (!cap) { 176 return 0; 177 } 178 total = 0; 179 for (cp = cap; *cp; cp++) { 180 if (*cp == '$' && cp[1] == '<') { 181 star = 1; 182 value = dec = 0; 183 for (cp += 2; (ch = *cp); cp++) { 184 if (ch >= '0' && ch <= '9') { 185 value = value * 10 + (ch - '0'); 186 dec *= 10; 187 } else 188 if (ch == '.') { 189 dec = 1; 190 } else 191 if (ch == '*') { 192 star = affcnt; 193 } else 194 if (ch == '>') { 195 break; 196 } 197 } 198 if (dec > 1) { 199 total += (value * star) / dec; 200 } else { 201 total += (value * star); 202 } 203 } 204 } 205 return total; 206} 207 208/* 209** liberated(cap) 210** 211** Return the cap without padding 212*/ 213char * 214liberated(char *cap) 215{ 216 static char cb[1024]; 217 char *ts, *ls; 218 219 cb[0] = '\0'; 220 ls = NULL; 221 if (cap) { 222 for (ts = cb; (*ts = *cap); ++cap) { 223 if (*cap == '$' && cap[1] == '<') { 224 ls = ts; 225 } 226 ++ts; 227 if (*cap == '>') { 228 if (ls) { 229 ts = ls; 230 ls = NULL; 231 } 232 } 233 } 234 } 235 return cb; 236} 237 238/* 239** page_loop() 240** 241** send CR/LF or go home and bump letter 242*/ 243void 244page_loop(void) 245{ 246 if (line_count + 2 >= lines) { 247 NEXT_LETTER; 248 go_home(); 249 } else { 250 put_crlf(); 251 } 252} 253 254/* 255** skip_pad_test(test-list-entry, state, ch, text) 256** 257** Print the start test line. Handle start up commands. 258** Return TRUE if a return is requested. 259*/ 260int 261skip_pad_test( 262 struct test_list *test, 263 int *state, 264 int *ch, 265 const char *text) 266{ 267 char rep_text[16]; 268 269 while(1) { 270 if (text) { 271 ptext(text); 272 } 273 if ((test->flags & MENU_LC_MASK)) { 274 sprintf(rep_text, " *%d", augment); 275 ptext(rep_text); 276 } 277 ptext(" [n] > "); 278 *ch = wait_here(); 279 if (*ch == 's') { 280 /* Skip is converted to next */ 281 *ch = 'n'; 282 return TRUE; 283 } 284 if (*ch == 'q') { 285 /* Quit is converted to help */ 286 *ch = '?'; 287 return TRUE; 288 } 289 if (*ch == '\r' || *ch == '\n' || *ch == 'n' || *ch == 'r') { 290 /* this is the only response that allows the test to run */ 291 *ch = 0; 292 } 293 if (subtest_menu(pad_test_list, state, ch)) { 294 continue; 295 } 296 return (*ch != 0); 297 } 298} 299 300/* 301** pad_done_message(test_list) 302** 303** Print the Done message and request input. 304*/ 305void 306pad_done_message( 307 struct test_list *test, 308 int *state, 309 int *ch) 310{ 311 int default_action = 0; 312 char done_message[128]; 313 char rep_text[16]; 314 315 while (1) { 316 if ((test->flags & MENU_LC_MASK)) { 317 sprintf(rep_text, "*%d", augment); 318 } else { 319 rep_text[0] = '\0'; 320 } 321 if (test->caps_done) { 322 sprintf(done_message, "(%s)%s Done ", test->caps_done, 323 rep_text); 324 ptext(done_message); 325 } else { 326 if (rep_text[0]) { 327 ptext(rep_text); 328 ptext(" "); 329 } 330 ptext("Done "); 331 } 332 if (debug_level & 2) { 333 dump_test_stats(test, state, ch); 334 } else { 335 *ch = wait_here(); 336 } 337 if (*ch == '\r' || *ch == '\n') { 338 *ch = default_action; 339 return; 340 } 341 if (*ch == 's' || *ch == 'n') { 342 *ch = 0; 343 return; 344 } 345 if (strchr(pad_repeat_test, *ch)) { 346 /* default action is now repeat */ 347 default_action = 'r'; 348 } 349 if (subtest_menu(pad_test_list, state, ch)) { 350 continue; 351 } 352 return; 353 } 354} 355 356/* 357** sliding_scale(dividend, factor, divisor) 358** 359** Return (dividend * factor) / divisor 360*/ 361int 362sliding_scale( 363 int dividend, 364 int factor, 365 unsigned long divisor) 366{ 367 double d = dividend; 368 369 if (divisor) { 370 d = (d * (double) factor) / (double) divisor; 371 return (int) (d + 0.5); 372 } 373 return 0; 374} 375 376/* 377** pad_test_startup() 378** 379** Do the stuff needed to begin a test. 380*/ 381void 382pad_test_startup( 383 int do_clear) 384{ 385 if (do_clear) { 386 put_clear(); 387 } 388 repeats = augment; 389 raw_characters_sent = 0; 390 test_complete = ttp = char_count = tt_delay_used = 0; 391 letter = letters[letter_number = 0]; 392 if (pad_test_duration <= 0) { 393 pad_test_duration = 1; 394 } 395 tt_delay_max = pad_test_duration * 1000; 396 set_alarm_clock(pad_test_duration); 397 event_start(TIME_TEST); 398} 399 400/* 401** still_testing() 402** 403** This function is called to see if the test loop should be terminated. 404*/ 405int 406still_testing(void) 407{ 408 fflush(stdout); 409 test_complete++; 410 return EXIT_CONDITION; 411} 412 413/* 414** pad_test_shutdown() 415** 416** Do the stuff needed to end a test. 417*/ 418void 419pad_test_shutdown( 420 struct test_list *t, 421 int crlf) 422{ 423 int i; 424 int counts; /* total counts */ 425 int ss; /* Save string index */ 426 int cpo; /* characters per operation */ 427 int delta; /* difference in characters */ 428 int bogus; /* Time is inaccurate */ 429 struct test_results *r; /* Results of current test */ 430 int ss_index[TT_MAX]; /* String index */ 431 432 if (tty_can_sync == SYNC_TESTED) { 433 bogus = tty_sync_error(); 434 } else { 435 bogus = 1; 436 } 437 usec_run_time = event_time(TIME_TEST); 438 tx_source = t; 439 tx_characters = raw_characters_sent; 440 tx_cps = sliding_scale(tx_characters, 1000000, usec_run_time); 441 442 /* save the data base */ 443 for (txp = ss = counts = 0; txp < ttp; txp++) { 444 tx_cap[txp] = tt_cap[txp]; 445 tx_count[txp] = tt_count[txp]; 446 tx_delay[txp] = tt_delay[txp]; 447 tx_affected[txp] = tt_affected[txp]; 448 tx_index[txp] = get_string_cap_byvalue(tt_cap[txp]); 449 if (tx_index[txp] >= 0) { 450 if (cap_match(t->caps_done, strnames[tx_index[txp]])) { 451 ss_index[ss++] = txp; 452 counts += tx_count[txp]; 453 } 454 } 455 } 456 457 if (crlf) { 458 put_crlf(); 459 } 460 if (counts == 0 || tty_cps == 0 || bogus) { 461 /* nothing to do */ 462 return; 463 } 464 /* calculate the suggested pad times */ 465 delta = usec_run_time - sliding_scale(tx_characters, 1000000, tty_cps); 466 if (delta < 0) { 467 /* probably should bump tx_characters */ 468 delta = 0; 469 } 470 cpo = delta / counts; 471 for (i = 0; i < ss; i++) { 472 if (!(r = get_next_block())) { 473 return; 474 } 475 r->next = pads[tx_index[ss_index[i]]]; 476 pads[tx_index[ss_index[i]]] = r; 477 r->test = t; 478 r->reps = tx_affected[ss_index[i]]; 479 r->delay = cpo; 480 } 481} 482 483/* 484** show_cap_results(index) 485** 486** Display the previous results 487*/ 488static void 489show_cap_results( 490 int x) 491{ 492 struct test_results *r; /* a result */ 493 int delay; 494 495 if ((r = pads[x])) { 496 sprintf(temp, "(%s)", strnames[x]); 497 ptext(temp); 498 while (r) { 499 sprintf(temp, "$<%d>", r->delay / 1000); 500 put_columns(temp, (int) strlen(temp), 10); 501 r = r->next; 502 } 503 r = pads[x]; 504 while (r) { 505 if (r->reps > 1) { 506 delay = r->delay / (r->reps * 100); 507 sprintf(temp, "$<%d.%d*>", delay / 10, delay % 10); 508 put_columns(temp, (int) strlen(temp), 10); 509 } 510 r = r->next; 511 } 512 put_crlf(); 513 } 514} 515 516/* 517** dump_test_stats(test_list, status, ch) 518** 519** Dump the statistics about the last test 520*/ 521void 522dump_test_stats( 523 struct test_list *t, 524 int *state, 525 int *ch) 526{ 527 int i, j; 528 char tbuf[32]; 529 int x[32]; 530 531 put_crlf(); 532 if (tx_source && tx_source->caps_done) { 533 cap_index(tx_source->caps_done, x); 534 if (x[0] >= 0) { 535 sprintf(temp, "Caps summary for (%s)", 536 tx_source->caps_done); 537 ptextln(temp); 538 for (i = 0; x[i] >= 0; i++) { 539 show_cap_results(x[i]); 540 } 541 put_crlf(); 542 } 543 } 544 sprintf(tbuf, "%011lu", usec_run_time); 545 sprintf(temp, "Test time: %lu.%s, characters per second %lu, characters %d", 546 usec_run_time / 1000000UL, &tbuf[5], tx_cps, tx_characters); 547 ptextln(temp); 548 for (i = 0; i < txp; i++) { 549 if ((j = get_string_cap_byvalue(tx_cap[i])) >= 0) { 550 sprintf(tbuf, "(%s)", strnames[j]); 551 } else { 552 strcpy(tbuf, "(?)"); 553 } 554 sprintf(temp, "%8d %3d $<%3d> %8s %s", 555 tx_count[i], tx_affected[i], tx_delay[i], 556 tbuf, expand(tx_cap[i])); 557 putln(temp); 558 } 559 generic_done_message(t, state, ch); 560} 561 562/* 563** longer_test_time(test_list, status, ch) 564** 565** Extend the number of seconds for each test. 566*/ 567void 568longer_test_time( 569 struct test_list *t GCC_UNUSED, 570 int *state GCC_UNUSED, 571 int *ch) 572{ 573 pad_test_duration += 1; 574 sprintf(txt_longer_test_time, "+) Change test time to %d seconds", 575 pad_test_duration + 1); 576 sprintf(txt_shorter_test_time, "-) Change test time to %d seconds", 577 pad_test_duration - 1); 578 sprintf(temp, "Tests will run for %d seconds", pad_test_duration); 579 ptext(temp); 580 *ch = REQUEST_PROMPT; 581} 582 583/* 584** shorter_test_time(test_list, status, ch) 585** 586** Shorten the number of seconds for each test. 587*/ 588void 589shorter_test_time( 590 struct test_list *t GCC_UNUSED, 591 int *state GCC_UNUSED, 592 int *ch) 593{ 594 if (pad_test_duration > 1) { 595 pad_test_duration -= 1; 596 sprintf(txt_longer_test_time, "+) Change test time to %d seconds", 597 pad_test_duration + 1); 598 sprintf(txt_shorter_test_time, "-) Change test time to %d seconds", 599 pad_test_duration - 1); 600 } 601 sprintf(temp, "Tests will run for %d second%s", pad_test_duration, 602 pad_test_duration > 1 ? "s" : ""); 603 ptext(temp); 604 *ch = REQUEST_PROMPT; 605} 606 607/* 608** longer_augment(test_list, status, ch) 609** 610** Lengthen the number of lines/characters effected 611*/ 612void 613longer_augment( 614 struct test_list *t, 615 int *state GCC_UNUSED, 616 int *ch) 617{ 618 augment <<= 1; 619 set_augment_txt(); 620 if (augment_test) { 621 t = augment_test; 622 } 623 sprintf(temp, "The pad tests will effect %d %s.", augment, 624 ((t->flags & MENU_LC_MASK) == MENU_lines) ? 625 "lines" : "characters"); 626 ptextln(temp); 627 *ch = REQUEST_PROMPT; 628} 629 630/* 631** shorter_augment(test_list, status, ch) 632** 633** Shorten the number of lines/characters effected 634*/ 635void 636shorter_augment( 637 struct test_list *t, 638 int *state GCC_UNUSED, 639 int *ch) 640{ 641 if (augment > 1) { 642 /* don't let the augment go to zero */ 643 augment >>= 1; 644 } 645 set_augment_txt(); 646 if (augment_test) { 647 t = augment_test; 648 } 649 sprintf(temp, "The pad tests will effect %d %s.", augment, 650 ((t->flags & MENU_LC_MASK) == MENU_lines) ? 651 "lines" : "characters"); 652 ptextln(temp); 653 *ch = REQUEST_PROMPT; 654} 655