ncurses 5.6
[ncurses.git] / tack / edit.c
1 /*
2 ** Copyright (C) 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 #include <time.h>
24 #include <tic.h>
25
26 MODULE_ID("$Id: edit.c,v 1.11 2006/06/24 21:22:42 tom Exp $")
27
28 /*
29  * Terminfo edit features
30  */
31 static void show_info(struct test_list *, int *, int *);
32 static void show_value(struct test_list *, int *, int *);
33 static void show_untested(struct test_list *, int *, int *);
34 static void show_changed(struct test_list *, int *, int *);
35
36 #define SHOW_VALUE      1
37 #define SHOW_EDIT       2
38 #define SHOW_DELETE     3
39
40 struct test_list edit_test_list[] = {
41         {MENU_CLEAR, 0, 0, 0, "i) display current terminfo", show_info, 0},
42         {0, 0, 0, 0, "w) write the current terminfo to a file", save_info, 0},
43         {SHOW_VALUE, 3, 0, 0, "v) show value of a selected cap", show_value, 0},
44         {SHOW_EDIT, 4, 0, 0, "e) edit value of a selected cap", show_value, 0},
45         {SHOW_DELETE, 3, 0, 0, "d) delete string", show_value, 0},
46         {0, 3, 0, 0, "m) show caps that have been modified", show_changed, 0},
47         {MENU_CLEAR + FLAG_CAN_TEST, 0, 0, 0, "c) show caps that can be tested", show_report, 0},
48         {MENU_CLEAR + FLAG_TESTED, 0, 0, 0, "t) show caps that have been tested", show_report, 0},
49         {MENU_CLEAR + FLAG_FUNCTION_KEY, 0, 0, 0, "f) show a list of function keys", show_report, 0},
50         {MENU_CLEAR, 0, 0, 0, "u) show caps defined that can not be tested", show_untested, 0},
51         {MENU_LAST, 0, 0, 0, 0, 0, 0}
52 };
53
54 static char change_pad_text[MAX_CHANGES][80];
55 static struct test_list change_pad_list[MAX_CHANGES] = {
56         {MENU_LAST, 0, 0, 0, 0, 0, 0}
57 };
58
59 static void build_change_menu(struct test_menu *);
60 static void change_one_entry(struct test_list *, int *, int *);
61
62 struct test_menu change_pad_menu = {
63         0, 'q', 0,
64         "Select cap name", "change", 0,
65         build_change_menu, change_pad_list, 0, 0, 0
66 };
67
68 static TERMTYPE original_term;          /* terminal type description */
69
70 static char flag_boolean[BOOLCOUNT];    /* flags for booleans */
71 static char flag_numerics[NUMCOUNT];    /* flags for numerics */
72 static char *flag_strings;              /* flags for strings */
73 static int *label_strings;
74 static int xon_index;                   /* Subscript for (xon) */
75 static int xon_shadow;
76
77 static int start_display;               /* the display has just started */
78 static int display_lines;               /* number of lines displayed */
79
80 static void
81 alloc_arrays(void)
82 {
83         if (flag_strings == 0) {
84                 label_strings = (int *)calloc(MAX_STRINGS, sizeof(int));
85                 flag_strings = (char *)calloc(MAX_STRINGS, sizeof(char));
86         }
87 }
88
89 /*
90 **      send_info_string(str)
91 **
92 **      Return the terminfo string prefixed by the correct separator
93 */
94 static void
95 send_info_string(
96         const char *str,
97         int *ch)
98 {
99         int len;
100
101         if (display_lines == -1) {
102                 return;
103         }
104         len = strlen(str);
105         if (len + char_count + 3 >= columns) {
106                 if (start_display == 0) {
107                         put_str(",");
108                 }
109                 put_crlf();
110                 if (++display_lines > lines) {
111                         ptext("-- more -- ");
112                         *ch = wait_here();
113                         if (*ch == 'q') {
114                                 display_lines = -1;
115                                 return;
116                         }
117                         display_lines = 0;
118                 }
119                 if (len >= columns) {
120                         /* if the terminal does not (am) then this loses */
121                         if (columns) {
122                                 display_lines += ((strlen(str) + 3) / columns) + 1;
123                         }
124                         put_str("   ");
125                         put_str(str);
126                         start_display = 0;
127                         return;
128                 }
129                 ptext("   ");
130         } else
131         if (start_display == 0) {
132                 ptext(", ");
133         } else {
134                 ptext("   ");
135         }
136         ptext(str);
137         start_display = 0;
138 }
139
140 /*
141 **      show_info(test_list, status, ch)
142 **
143 **      Display the current terminfo
144 */
145 static void
146 show_info(
147         struct test_list *t GCC_UNUSED,
148         int *state GCC_UNUSED,
149         int *ch)
150 {
151         int i;
152         char buf[1024];
153
154         display_lines = 1;
155         start_display = 1;
156         for (i = 0; i < BOOLCOUNT; i++) {
157                 if ((i == xon_index) ? xon_shadow : CUR Booleans[i]) {
158                         send_info_string(boolnames[i], ch);
159                 }
160         }
161         for (i = 0; i < NUMCOUNT; i++) {
162                 if (CUR Numbers[i] >= 0) {
163                         sprintf(buf, "%s#%d", numnames[i], CUR Numbers[i]);
164                         send_info_string(buf, ch);
165                 }
166         }
167         for (i = 0; i < MAX_STRINGS; i++) {
168                 if (CUR Strings[i]) {
169                         sprintf(buf, "%s=%s", STR_NAME(i),
170                                 print_expand(CUR Strings[i]));
171                         send_info_string(buf, ch);
172                 }
173         }
174         put_newlines(2);
175         *ch = REQUEST_PROMPT;
176 }
177
178 /*
179 **      save_info_string(str, fp)
180 **
181 **      Write the terminfo string prefixed by the correct separator
182 */
183 static void
184 save_info_string(
185         const char *str,
186         FILE *fp)
187 {
188         int len;
189
190         len = strlen(str);
191         if (len + display_lines >= 77) {
192                 if (display_lines > 0) {
193                         (void) fprintf(fp, "\n\t");
194                 }
195                 display_lines = 8;
196         } else
197         if (display_lines > 0) {
198                 (void) fprintf(fp, " ");
199                 display_lines++;
200         } else {
201                 (void) fprintf(fp, "\t");
202                 display_lines = 8;
203         }
204         (void) fprintf(fp, "%s,", str);
205         display_lines += len + 1;
206 }
207
208 /*
209 **      save_info(test_list, status, ch)
210 **
211 **      Write the current terminfo to a file
212 */
213 void
214 save_info(
215         struct test_list *t,
216         int *state,
217         int *ch)
218 {
219         int i;
220         FILE *fp;
221         time_t now;
222         char buf[1024];
223
224         if ((fp = fopen(tty_basename, "w")) == (FILE *) NULL) {
225                 (void) sprintf(temp, "can't open: %s", tty_basename);
226                 ptextln(temp);
227                 generic_done_message(t, state, ch);
228                 return;
229         }
230         time(&now);
231         /* Note: ctime() returns a newline at the end of the string */
232         (void) fprintf(fp, "# Terminfo created by TACK for TERM=%s on %s",
233                 tty_basename, ctime(&now));
234         (void) fprintf(fp, "%s|%s,\n", tty_basename, longname());
235
236         display_lines = 0;
237         for (i = 0; i < BOOLCOUNT; i++) {
238                 if (i == xon_index ? xon_shadow : CUR Booleans[i]) {
239                         save_info_string(boolnames[i], fp);
240                 }
241         }
242         for (i = 0; i < NUMCOUNT; i++) {
243                 if (CUR Numbers[i] >= 0) {
244                         sprintf(buf, "%s#%d", numnames[i], CUR Numbers[i]);
245                         save_info_string(buf, fp);
246                 }
247         }
248         for (i = 0; i < MAX_STRINGS; i++) {
249                 if (CUR Strings[i]) {
250                         sprintf(buf, "%s=%s", STR_NAME(i),
251                                 _nc_tic_expand(CUR Strings[i], TRUE, TRUE));
252                         save_info_string(buf, fp);
253                 }
254         }
255         (void) fprintf(fp, "\n");
256         (void) fclose(fp);
257         sprintf(temp, "Terminfo saved as file: %s", tty_basename);
258         ptextln(temp);
259 }
260
261 /*
262 **      show_value(test_list, status, ch)
263 **
264 **      Display the value of a selected cap
265 */
266 static void
267 show_value(
268         struct test_list *t,
269         int *state GCC_UNUSED,
270         int *ch)
271 {
272         struct name_table_entry const *nt;
273         char *s;
274         int n, op, b;
275         char buf[1024];
276         char tmp[1024];
277
278         ptext("enter name: ");
279         read_string(buf, 80);
280         if (buf[0] == '\0' || buf[1] == '\0') {
281                 *ch = buf[0];
282                 return;
283         }
284         if (line_count + 2 >= lines) {
285                 put_clear();
286         }
287         op = t->flags & 255;
288         if ((nt = _nc_find_entry(buf, _nc_info_hash_table))) {
289                 switch (nt->nte_type) {
290                 case BOOLEAN:
291                         if (op == SHOW_DELETE) {
292                                 if (nt->nte_index == xon_index) {
293                                         xon_shadow = 0;
294                                 } else {
295                                         CUR Booleans[nt->nte_index] = 0;
296                                 }
297                                 return;
298                         }
299                         b = nt->nte_index == xon_index ? xon_shadow :
300                                 CUR Booleans[nt->nte_index];
301                         sprintf(temp, "boolean  %s %s", buf,
302                                 b ? "True" : "False");
303                         break;
304                 case STRING:
305                         if (op == SHOW_DELETE) {
306                                 CUR Strings[nt->nte_index] = (char *) 0;
307                                 return;
308                         }
309                         if (CUR Strings[nt->nte_index]) {
310                                 sprintf(temp, "string  %s %s", buf,
311                                         expand(CUR Strings[nt->nte_index]));
312                         } else {
313                                 sprintf(temp, "undefined string %s", buf);
314                         }
315                         break;
316                 case NUMBER:
317                         if (op == SHOW_DELETE) {
318                                 CUR Numbers[nt->nte_index] = -1;
319                                 return;
320                         }
321                         sprintf(temp, "numeric  %s %d", buf,
322                                 CUR Numbers[nt->nte_index]);
323                         break;
324                 default:
325                         sprintf(temp, "unknown");
326                         break;
327                 }
328                 ptextln(temp);
329         } else {
330                 sprintf(temp, "Cap not found: %s", buf);
331                 ptextln(temp);
332                 return;
333         }
334         if (op != SHOW_EDIT) {
335                 return;
336         }
337         if (nt->nte_type == BOOLEAN) {
338                 ptextln("Value flipped");
339                 if (nt->nte_index == xon_index) {
340                         xon_shadow = !xon_shadow;
341                 } else {
342                         CUR Booleans[nt->nte_index] = !CUR Booleans[nt->nte_index];
343                 }
344                 return;
345         }
346         ptextln("Enter new value");
347         read_string(buf, sizeof(buf));
348
349         switch (nt->nte_type) {
350         case STRING:
351                 _nc_reset_input((FILE *) 0, buf);
352                 _nc_trans_string(tmp, tmp + sizeof(tmp));
353                 s = (char *)malloc(strlen(tmp) + 1);
354                 strcpy(s, tmp);
355                 CUR Strings[nt->nte_index] = s;
356                 sprintf(temp, "new string value  %s", nt->nte_name);
357                 ptextln(temp);
358                 ptextln(expand(CUR Strings[nt->nte_index]));
359                 break;
360         case NUMBER:
361                 if (sscanf(buf, "%d", &n) == 1) {
362                         CUR Numbers[nt->nte_index] = n;
363                         sprintf(temp, "new numeric value  %s %d",
364                                 nt->nte_name, n);
365                         ptextln(temp);
366                 } else {
367                         sprintf(temp, "Illegal number: %s", buf);
368                         ptextln(temp);
369                 }
370                 break;
371         default:
372                 break;
373         }
374 }
375
376 /*
377 **      get_string_cap_byname(name, long_name)
378 **
379 **      Given a cap name, find the value
380 **      Errors are quietly ignored.
381 */
382 char *
383 get_string_cap_byname(
384         const char *name,
385         const char **long_name)
386 {
387         struct name_table_entry const *nt;
388
389         if ((nt = _nc_find_entry(name, _nc_info_hash_table))) {
390                 if (nt->nte_type == STRING) {
391                         *long_name = strfnames[nt->nte_index];
392                         return (CUR Strings[nt->nte_index]);
393                 }
394         }
395         *long_name = "??";
396         return (char *) 0;
397 }
398
399 /*
400 **      get_string_cap_byvalue(value)
401 **
402 **      Given a capability string, find its position in the data base.
403 **      Return the index or -1 if not found.
404 */
405 int
406 get_string_cap_byvalue(
407         const char *value)
408 {
409         int i;
410
411         if (value) {
412                 for (i = 0; i < MAX_STRINGS; i++) {
413                         if (CUR Strings[i] == value) {
414                                 return i;
415                         }
416                 }
417                 /* search for translated strings */
418                 for (i = 0; i < TM_last; i++) {
419                         if (TM_string[i].value == value) {
420                                 return TM_string[i].index;
421                         }
422                 }
423         }
424         return -1;
425 }
426
427 /*
428 **      show_changed(test_list, status, ch)
429 **
430 **      Display a list of caps that have been changed.
431 */
432 static void
433 show_changed(
434         struct test_list *t GCC_UNUSED,
435         int *state GCC_UNUSED,
436         int *ch)
437 {
438         int i, header = 1, v;
439         const char *a;
440         const char *b;
441         static char title[] = "                     old value   cap  new value";
442         char abuf[1024];
443
444         for (i = 0; i < BOOLCOUNT; i++) {
445                 v = (i == xon_index) ? xon_shadow : CUR Booleans[i];
446                 if (original_term.Booleans[i] != v) {
447                         if (header) {
448                                 ptextln(title);
449                                 header = 0;
450                         }
451                         sprintf(temp, "%30d %6s %d",
452                                 original_term.Booleans[i], boolnames[i], v);
453                         ptextln(temp);
454                 }
455         }
456         for (i = 0; i < NUMCOUNT; i++) {
457                 if (original_term.Numbers[i] != CUR Numbers[i]) {
458                         if (header) {
459                                 ptextln(title);
460                                 header = 0;
461                         }
462                         sprintf(temp, "%30d %6s %d",
463                                 original_term.Numbers[i], numnames[i],
464                                 CUR Numbers[i]);
465                         ptextln(temp);
466                 }
467         }
468         for (i = 0; i < MAX_STRINGS; i++) {
469                 a = original_term.Strings[i] ? original_term.Strings[i] : "";
470                 b = CUR Strings[i] ?  CUR Strings[i] : "";
471                 if (strcmp(a, b)) {
472                         if (header) {
473                                 ptextln(title);
474                                 header = 0;
475                         }
476                         strcpy(abuf, _nc_tic_expand(a, TRUE, TRUE));
477                         sprintf(temp, "%30s %6s %s", abuf, STR_NAME(i),
478                                 _nc_tic_expand(b, TRUE, TRUE));
479                         putln(temp);
480                 }
481         }
482         if (header) {
483                 ptextln("No changes");
484         }
485         put_crlf();
486         *ch = REQUEST_PROMPT;
487 }
488
489 /*
490 **      user_modified()
491 **
492 **      Return TRUE if the user has modified the terminfo
493 */
494 int
495 user_modified(void)
496 {
497         const char *a, *b;
498         int i, v;
499
500         for (i = 0; i < BOOLCOUNT; i++) {
501                 v = (i == xon_index) ? xon_shadow : CUR Booleans[i];
502                 if (original_term.Booleans[i] != v) {
503                         return TRUE;
504                 }
505         }
506         for (i = 0; i < NUMCOUNT; i++) {
507                 if (original_term.Numbers[i] != CUR Numbers[i]) {
508                         return TRUE;
509                 }
510         }
511         for (i = 0; i < MAX_STRINGS; i++) {
512                 a = original_term.Strings[i] ? original_term.Strings[i] : "";
513                 b = CUR Strings[i] ?  CUR Strings[i] : "";
514                 if (strcmp(a, b)) {
515                         return TRUE;
516                 }
517         }
518         return FALSE;
519 }
520
521 /*****************************************************************************
522  *
523  * Maintain the list of capabilities that can be tested
524  *
525  *****************************************************************************/
526
527 /*
528 **      mark_cap(name, flag)
529 **
530 **      Mark the cap data base with the flag provided.
531 */
532 static void
533 mark_cap(
534         char *name,
535         int flag)
536 {
537         struct name_table_entry const *nt;
538
539         alloc_arrays();
540         if ((nt = _nc_find_entry(name, _nc_info_hash_table))) {
541                 switch (nt->nte_type) {
542                 case BOOLEAN:
543                         flag_boolean[nt->nte_index] |= flag;
544                         break;
545                 case STRING:
546                         flag_strings[nt->nte_index] |= flag;
547                         break;
548                 case NUMBER:
549                         flag_numerics[nt->nte_index] |= flag;
550                         break;
551                 default:
552                         sprintf(temp, "unknown cap type (%s)", name);
553                         ptextln(temp);
554                         break;
555                 }
556         } else {
557                 sprintf(temp, "Cap not found: %s", name);
558                 ptextln(temp);
559                 (void) wait_here();
560         }
561 }
562
563 /*
564 **      can_test(name-list, flags)
565 **
566 **      Scan the name list and get the names.
567 **      Enter each name into the can-test data base.
568 **      <space> ( and ) may be used as separators.
569 */
570 void
571 can_test(
572         const char *s,
573         int flags)
574 {
575         int ch, j;
576         char name[32];
577
578         if (s) {
579                 for (j = 0; (name[j] = ch = *s); s++) {
580                         if (ch == ' ' || ch == ')' || ch == '(') {
581                                 if (j) {
582                                         name[j] = '\0';
583                                         mark_cap(name, flags);
584                                 }
585                                 j = 0;
586                         } else {
587                                 j++;
588                         }
589                 }
590                 if (j) {
591                         mark_cap(name, flags);
592                 }
593         }
594 }
595
596 /*
597 **      cap_index(name-list, index-list)
598 **
599 **      Scan the name list and return a list of indexes.
600 **      <space> ( and ) may be used as separators.
601 **      This list is terminated with -1.
602 */
603 void
604 cap_index(
605         const char *s,
606         int *inx)
607 {
608         struct name_table_entry const *nt;
609         int ch, j;
610         char name[32];
611
612         if (s) {
613                 for (j = 0; ; s++) {
614                         name[j] = ch = *s;
615                         if (ch == ' ' || ch == ')' || ch == '(' || ch == 0) {
616                                 if (j) {
617                                         name[j] = '\0';
618                                         if ((nt = _nc_find_entry(name,
619                                                 _nc_info_hash_table)) &&
620                                                 (nt->nte_type == STRING)) {
621                                                 *inx++ = nt->nte_index;
622                                         }
623                                 }
624                                 if (ch == 0) {
625                                         break;
626                                 }
627                                 j = 0;
628                         } else {
629                                 j++;
630                         }
631                 }
632         }
633         *inx = -1;
634 }
635
636 /*
637 **      cap_match(name-list, cap)
638 **
639 **      Scan the name list and see if the cap is in the list.
640 **      Return TRUE if we find an exact match.
641 **      <space> ( and ) may be used as separators.
642 */
643 int
644 cap_match(
645         const char *names,
646         const char *cap)
647 {
648         char *s;
649         int c, l, t;
650
651         if (names) {
652                 l = strlen(cap);
653                 while ((s = strstr(names, cap))) {
654                         c = (names == s) ? 0 : *(s - 1);
655                         t = s[l];
656                         if ((c == 0 || c == ' ' || c == '(') &&
657                                 (t == 0 || t == ' ' || t == ')')) {
658                                 return TRUE;
659                         }
660                         if (t == 0) {
661                                 break;
662                         }
663                         names = s + l;
664                 }
665         }
666         return FALSE;
667 }
668
669 /*
670 **      show_report(test_list, status, ch)
671 **
672 **      Display a list of caps that can be tested
673 */
674 void
675 show_report(
676         struct test_list *t,
677         int *state GCC_UNUSED,
678         int *ch)
679 {
680         int i, j, nc, flag;
681         const char *s;
682         const char **nx = malloc(BOOLCOUNT + NUMCOUNT + MAX_STRINGS);
683
684         alloc_arrays();
685         flag = t->flags & 255;
686         nc = 0;
687         for (i = 0; i < BOOLCOUNT; i++) {
688                 if (flag_boolean[i] & flag) {
689                         nx[nc++] = boolnames[i];
690                 }
691         }
692         for (i = 0; i < NUMCOUNT; i++) {
693                 if (flag_numerics[i] & flag) {
694                         nx[nc++] = numnames[i];
695                 }
696         }
697         for (i = 0; i < MAX_STRINGS; i++) {
698                 if (flag_strings[i] & flag) {
699                         nx[nc++] = STR_NAME(i);
700                 }
701         }
702         /* sort */
703         for (i = 0; i < nc - 1; i++) {
704                 for (j = i + 1; j < nc; j++) {
705                         if (strcmp(nx[i], nx[j]) > 0) {
706                                 s = nx[i];
707                                 nx[i] = nx[j];
708                                 nx[j] = s;
709                         }
710                 }
711         }
712         if (flag & FLAG_FUNCTION_KEY) {
713                 ptextln("The following function keys can be tested:");
714         } else
715         if (flag & FLAG_CAN_TEST) {
716                 ptextln("The following capabilities can be tested:");
717         } else
718         if (flag & FLAG_TESTED) {
719                 ptextln("The following capabilities have been tested:");
720         }
721         put_crlf();
722         for (i = 0; i < nc; i++) {
723                 sprintf(temp, "%s ", nx[i]);
724                 ptext(temp);
725         }
726         put_newlines(1);
727         *ch = REQUEST_PROMPT;
728         free (nx);
729 }
730
731 /*
732 **      show_untested(test_list, status, ch)
733 **
734 **      Display a list of caps that are defined but cannot be tested.
735 **      Don't bother to sort this list.
736 */
737 static void
738 show_untested(
739         struct test_list *t GCC_UNUSED,
740         int *state GCC_UNUSED,
741         int *ch)
742 {
743         int i;
744
745         alloc_arrays();
746         ptextln("Caps that are defined but cannot be tested:");
747         for (i = 0; i < BOOLCOUNT; i++) {
748                 if (flag_boolean[i] == 0 && CUR Booleans[i]) {
749                         sprintf(temp, "%s ", boolnames[i]);
750                         ptext(temp);
751                 }
752         }
753         for (i = 0; i < NUMCOUNT; i++) {
754                 if (flag_numerics[i] == 0 && CUR Numbers[i] >= 0) {
755                         sprintf(temp, "%s ", numnames[i]);
756                         ptext(temp);
757                 }
758         }
759         for (i = 0; i < MAX_STRINGS; i++) {
760                 if (flag_strings[i] == 0 && CUR Strings[i]) {
761                         sprintf(temp, "%s ", STR_NAME(i));
762                         ptext(temp);
763                 }
764         }
765         put_newlines(1);
766         *ch = REQUEST_PROMPT;
767 }
768
769 /*
770 **      edit_init()
771 **
772 **      Initialize the function key data base
773 */
774 void
775 edit_init(void)
776 {
777         int i, j, lc;
778         char *lab;
779         struct name_table_entry const *nt;
780
781         alloc_arrays();
782
783         _nc_copy_termtype(&original_term, &cur_term->type);
784         for (i = 0; i < BOOLCOUNT; i++) {
785                 original_term.Booleans[i] = CUR Booleans[i];
786         }
787         for (i = 0; i < NUMCOUNT; i++) {
788                 original_term.Numbers[i] = CUR Numbers[i];
789         }
790         /* scan for labels */
791         for (i = lc = 0; i < MAX_STRINGS; i++) {
792                 original_term.Strings[i] = CUR Strings[i];
793                 if (strncmp(STR_NAME(i), "lf", 2) == 0) {
794                         flag_strings[i] |= FLAG_LABEL;
795                         if (CUR Strings[i]) {
796                                 label_strings[lc++] = i;
797                         }
798                 }
799         }
800         /* scan for function keys */
801         for (i = 0; i < MAX_STRINGS; i++) {
802                 const char *this_name = STR_NAME(i);
803                 if ((this_name[0] == 'k') && strcmp(this_name, "kmous")) {
804                         flag_strings[i] |= FLAG_FUNCTION_KEY;
805                         lab = (char *) 0;
806                         for (j = 0; j < lc; j++) {
807                                 if (!strcmp(this_name,
808                                         STR_NAME(label_strings[j]))) {
809                                         lab = CUR Strings[label_strings[j]];
810                                         break;
811                                 }
812                         }
813                         enter_key(this_name, CUR Strings[i], lab);
814                 }
815         }
816         /* Lookup the translated strings */
817         for (i = 0; i < TM_last; i++) {
818                 if ((nt = _nc_find_entry(TM_string[i].name,
819                         _nc_info_hash_table)) && (nt->nte_type == STRING)) {
820                         TM_string[i].index = nt->nte_index;
821                 } else {
822                         sprintf(temp, "TM_string lookup failed for: %s",
823                                 TM_string[i].name);
824                         ptextln(temp);
825                 }
826         }
827         if ((nt = _nc_find_entry("xon", _nc_info_hash_table)) != 0) {
828                 xon_index = nt->nte_index;
829         }
830         xon_shadow = xon_xoff;
831         free(label_strings);
832 }
833
834 /*
835 **      change_one_entry(test_list, status, ch)
836 **
837 **      Change the padding on the selected cap
838 */
839 static void
840 change_one_entry(
841         struct test_list *test,
842         int *state,
843         int *chp)
844 {
845         struct name_table_entry const *nt;
846         int i, j, x, star, slash,  v, dot, ch;
847         const char *s;
848         char *t, *p;
849         const char *current_string;
850         char buf[1024];
851         char pad[1024];
852
853         i = test->flags & 255;
854         if (i == 255) {
855                 /* read the cap name from the user */
856                 ptext("enter name: ");
857                 read_string(pad, 32);
858                 if (pad[0] == '\0' || pad[1] == '\0') {
859                         *chp = pad[0];
860                         return;
861                 }
862                 if ((nt = _nc_find_entry(pad, _nc_info_hash_table)) &&
863                         (nt->nte_type == STRING)) {
864                         x = nt->nte_index;
865                         current_string = CUR Strings[x];
866                 } else {
867                         sprintf(temp, "%s is not a string capability", pad);
868                         ptext(temp);
869                         generic_done_message(test, state, chp);
870                         return;
871                 }
872         } else {
873                 x = tx_index[i];
874                 current_string = tx_cap[i];
875                 strcpy(pad, STR_NAME(x));
876         }
877         if (!current_string) {
878                 ptextln("That string is not currently defined.  Please enter a new value, including the padding delay:");
879                 read_string(buf, sizeof(buf));
880                 _nc_reset_input((FILE *) 0, buf);
881                 _nc_trans_string(pad, pad + sizeof(pad));
882                 t = (char *)malloc(strlen(pad) + 1);
883                 strcpy(t, pad);
884                 CUR Strings[x] = t;
885                 sprintf(temp, "new string value  %s", STR_NAME(x));
886                 ptextln(temp);
887                 ptextln(expand(t));
888                 return;
889         }
890         sprintf(buf, "Current value: (%s) %s", pad, _nc_tic_expand(current_string, TRUE, TRUE));
891         putln(buf);
892         ptextln("Enter new pad.  0 for no pad.  CR for no change.");
893         read_string(buf, 32);
894         if (buf[0] == '\0' || (buf[1] == '\0' && isalpha(UChar(buf[0])))) {
895                 *chp = buf[0];
896                 return;
897         }
898         star = slash = FALSE;
899         for (j = v = dot = 0; (ch = buf[j]); j++) {
900                 if (ch >= '0' && ch <= '9') {
901                         v = ch - '0' + v * 10;
902                         if (dot) {
903                                 dot++;
904                         }
905                 } else if (ch == '*') {
906                         star = TRUE;
907                 } else if (ch == '/') {
908                         slash = TRUE;
909                 } else if (ch == '.') {
910                         dot = 1;
911                 } else {
912                         sprintf(temp, "Illegal character: %c", ch);
913                         ptextln(temp);
914                         ptext("General format:  99.9*/  ");
915                         generic_done_message(test, state, chp);
916                         return;
917                 }
918         }
919         while (dot > 2) {
920                 v /= 10;
921                 dot--;
922         }
923         if (dot == 2) {
924                 sprintf(pad, "%d.%d%s%s", v / 10, v % 10,
925                                 star ? "*" : "", slash ? "/" : "");
926         } else {
927                 sprintf(pad, "%d%s%s",
928                         v, star ? "*" : "", slash ? "/" : "");
929         }
930         s = current_string;
931         t = buf;
932         for (v = 0; (ch = *t = *s++); t++) {
933                 if (v == '$' && ch == '<') {
934                         while ((ch = *s++) && (ch != '>'));
935                         for (p = pad; (*++t = *p++); );
936                         *t++ = '>';
937                         while ((*t++ = *s++));
938                         pad[0] = '\0';
939                         break;
940                 }
941                 v = ch;
942         }
943         if (pad[0]) {
944                 sprintf(t, "$<%s>", pad);
945         }
946         if ((t = (char *)malloc(strlen(buf) + 1))) {
947                 strcpy(t, buf);
948                 CUR Strings[x] = t;
949                 if (i != 255) {
950                         tx_cap[i] = t;
951                 }
952         }
953         generic_done_message(test, state, chp);
954 }
955
956 /*
957 **      build_change_menu(menu_list)
958 **
959 **      Build the change pad menu list
960 */
961 static void
962 build_change_menu(
963         struct test_menu *m)
964 {
965         int i, j, k;
966         char *s;
967
968         for (i = j = 0; i < txp; i++) {
969                 if ((k = tx_index[i]) >= 0) {
970                         s = _nc_tic_expand(tx_cap[i], TRUE, TRUE);
971                         s[40] = '\0';
972                         sprintf(change_pad_text[j], "%c) (%s) %s",
973                                 'a' + j, STR_NAME(k), s);
974                         change_pad_list[j].flags = i;
975                         change_pad_list[j].lines_needed = 4;
976                         change_pad_list[j].menu_entry = change_pad_text[j];
977                         change_pad_list[j].test_procedure = change_one_entry;
978                         j++;
979                 }
980         }
981         strcpy(change_pad_text[j], "z) enter name");
982         change_pad_list[j].flags = 255;
983         change_pad_list[j].lines_needed = 4;
984         change_pad_list[j].menu_entry = change_pad_text[j];
985         change_pad_list[j].test_procedure = change_one_entry;
986         j++;
987         change_pad_list[j].flags = MENU_LAST;
988         if (m->menu_title) {
989                 put_crlf();
990                 ptextln(m->menu_title);
991         }
992 }