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