ncurses 5.5
[ncurses.git] / tack / control.c
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
28 MODULE_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 */
39 int test_complete;              /* counts number of tests completed */
40
41 char txt_longer_test_time[80];  /* +) use longer time */
42 char txt_shorter_test_time[80]; /* -) use shorter time */
43 static int pad_test_duration = 1;       /* number of seconds for a pad test */
44 int auto_pad_mode;              /* run the time tests */
45 int no_alarm_event;             /* TRUE if the alarm has not gone off yet */
46 unsigned long usec_run_time;    /* length of last test in microseconds */
47 static MY_TIMER stop_watch[MAX_TIMERS]; /* Hold the start timers */
48
49 char txt_longer_augment[80];    /* >) use bigger augment */
50 char txt_shorter_augment[80];   /* <) use smaller augment */
51
52 /* caps under test data base */
53 int tt_delay_max;               /* max number of milliseconds we can delay */
54 int tt_delay_used;              /* number of milliseconds consumed in delay */
55 const char *tt_cap[TT_MAX];     /* value of string */
56 int tt_affected[TT_MAX];        /* lines or columns effected (repetition factor) */
57 int tt_count[TT_MAX];           /* Number of times sent */
58 int tt_delay[TT_MAX];           /* Number of milliseconds delay */
59 int ttp;                        /* number of entries used */
60
61 /* Saved value of the above data base */
62 const char *tx_cap[TT_MAX];     /* value of string */
63 int tx_affected[TT_MAX];        /* lines or columns effected (repetition factor) */
64 int tx_count[TT_MAX];           /* Number of times sent */
65 int tx_index[TT_MAX];           /* String index */
66 int tx_delay[TT_MAX];           /* Number of milliseconds delay */
67 int txp;                        /* number of entries used */
68 int tx_characters;              /* printing characters sent by test */
69 unsigned long tx_cps;           /* characters per second */
70 static struct test_list *tx_source;     /* The test that generated this data */
71
72 #define RESULT_BLOCK            1024
73 static int blocks;              /* number of result blocks available */
74 static struct test_results *results;    /* pointer to next available */
75 static 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 */
82 void
83 event_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 */
97 long
98 event_time(int n)
99 {
100 #if HAVE_GETTIMEOFDAY
101         MY_TIMER current_time;
102
103         (void) gettimeofday(&current_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 */
122 static struct test_results *
123 get_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 */
143 void
144 set_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
152 void
153 control_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 */
167 int
168 msec_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 */
213 char *
214 liberated(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 */
243 void
244 page_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 */
260 int
261 skip_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 */
305 void
306 pad_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 */
361 int
362 sliding_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 */
381 void
382 pad_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 */
405 int
406 still_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 */
418 void
419 pad_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 */
488 static void
489 show_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 */
521 void
522 dump_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 */
567 void
568 longer_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 */
588 void
589 shorter_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 */
612 void
613 longer_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 */
635 void
636 shorter_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 }