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