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