2 ** Copyright (C) 1997 Free Software Foundation, Inc.
4 ** This file is part of TACK.
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)
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.
16 ** You should have received a copy of the GNU General Public License
17 ** along with TACK; see the file COPYING. If not, write to
18 ** the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
19 ** Boston, MA 02111-1307, USA.
26 MODULE_ID("$Id: edit.c,v 1.8 2001/06/18 18:44:32 tom Exp $")
29 * Terminfo edit features
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 *);
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}
54 static char change_pad_text
[MAX_CHANGES
][80];
55 struct test_list change_pad_list
[MAX_CHANGES
] = {
56 {MENU_LAST
, 0, 0, 0, 0, 0, 0}
59 static void build_change_menu(struct test_menu
*);
60 static void change_one_entry(struct test_list
*, int *, int *);
62 struct test_menu change_pad_menu
= {
64 "Select cap name", "change", 0,
65 build_change_menu
, change_pad_list
, 0, 0, 0
68 extern struct test_results
*pads
[STRCOUNT
]; /* save pad results here */
70 static TERMTYPE original_term
; /* terminal type description */
72 static char flag_boolean
[BOOLCOUNT
]; /* flags for booleans */
73 static char flag_numerics
[NUMCOUNT
]; /* flags for numerics */
74 static char flag_strings
[STRCOUNT
]; /* flags for strings */
75 static int xon_index
; /* Subscript for (xon) */
78 static int start_display
; /* the display has just started */
79 static int display_lines
; /* number of lines displayed */
82 ** send_info_string(str)
84 ** Return the terminfo string prefixed by the correct separator
93 if (display_lines
== -1) {
97 if (len
+ char_count
+ 3 >= columns
) {
98 if (start_display
== 0) {
102 if (++display_lines
> lines
) {
103 ptext("-- more -- ");
111 if (len
>= columns
) {
112 /* if the terminal does not (am) then this loses */
114 display_lines
+= ((strlen(str
) + 3) / columns
) + 1;
123 if (start_display
== 0) {
133 ** show_info(test_list, status, ch)
135 ** Display the current terminfo
139 struct test_list
*t GCC_UNUSED
,
140 int *state GCC_UNUSED
,
148 for (i
= 0; i
< BOOLCOUNT
; i
++) {
149 if ((i
== xon_index
) ? xon_shadow
: CUR Booleans
[i
]) {
150 send_info_string(boolnames
[i
], ch
);
153 for (i
= 0; i
< NUMCOUNT
; i
++) {
154 if (CUR Numbers
[i
] >= 0) {
155 sprintf(buf
, "%s#%d", numnames
[i
], CUR Numbers
[i
]);
156 send_info_string(buf
, ch
);
159 for (i
= 0; i
< STRCOUNT
; i
++) {
160 if (CUR Strings
[i
]) {
161 sprintf(buf
, "%s=%s", strnames
[i
],
162 print_expand(CUR Strings
[i
]));
163 send_info_string(buf
, ch
);
167 *ch
= REQUEST_PROMPT
;
171 ** save_info_string(str, fp)
173 ** Write the terminfo string prefixed by the correct separator
183 if (len
+ display_lines
>= 77) {
184 if (display_lines
> 0) {
185 (void) fprintf(fp
, "\n\t");
189 if (display_lines
> 0) {
190 (void) fprintf(fp
, " ");
193 (void) fprintf(fp
, "\t");
196 (void) fprintf(fp
, "%s,", str
);
197 display_lines
+= len
+ 1;
201 ** save_info(test_list, status, ch)
203 ** Write the current terminfo to a file
216 if ((fp
= fopen(tty_basename
, "w")) == (FILE *) NULL
) {
217 (void) sprintf(temp
, "can't open: %s", tty_basename
);
219 generic_done_message(t
, state
, ch
);
223 /* Note: ctime() returns a newline at the end of the string */
224 (void) fprintf(fp
, "# Terminfo created by TACK for TERM=%s on %s",
225 tty_basename
, ctime(&now
));
226 (void) fprintf(fp
, "%s|%s,\n", tty_basename
, longname());
229 for (i
= 0; i
< BOOLCOUNT
; i
++) {
230 if (i
== xon_index
? xon_shadow
: CUR Booleans
[i
]) {
231 save_info_string(boolnames
[i
], fp
);
234 for (i
= 0; i
< NUMCOUNT
; i
++) {
235 if (CUR Numbers
[i
] >= 0) {
236 sprintf(buf
, "%s#%d", numnames
[i
], CUR Numbers
[i
]);
237 save_info_string(buf
, fp
);
240 for (i
= 0; i
< STRCOUNT
; i
++) {
241 if (CUR Strings
[i
]) {
242 sprintf(buf
, "%s=%s", strnames
[i
],
243 _nc_tic_expand(CUR Strings
[i
], TRUE
, TRUE
));
244 save_info_string(buf
, fp
);
247 (void) fprintf(fp
, "\n");
249 sprintf(temp
, "Terminfo saved as file: %s", tty_basename
);
254 ** show_value(test_list, status, ch)
256 ** Display the value of a selected cap
261 int *state GCC_UNUSED
,
264 struct name_table_entry
const *nt
;
270 ptext("enter name: ");
271 read_string(buf
, 80);
272 if (buf
[0] == '\0' || buf
[1] == '\0') {
276 if (line_count
+ 2 >= lines
) {
280 if ((nt
= _nc_find_entry(buf
, _nc_info_hash_table
))) {
281 switch (nt
->nte_type
) {
283 if (op
== SHOW_DELETE
) {
284 if (nt
->nte_index
== xon_index
) {
287 CUR Booleans
[nt
->nte_index
] = 0;
291 b
= nt
->nte_index
== xon_index
? xon_shadow
:
292 CUR Booleans
[nt
->nte_index
];
293 sprintf(temp
, "boolean %s %s", buf
,
294 b
? "True" : "False");
297 if (op
== SHOW_DELETE
) {
298 CUR Strings
[nt
->nte_index
] = (char *) 0;
301 if (CUR Strings
[nt
->nte_index
]) {
302 sprintf(temp
, "string %s %s", buf
,
303 expand(CUR Strings
[nt
->nte_index
]));
305 sprintf(temp
, "undefined string %s", buf
);
309 if (op
== SHOW_DELETE
) {
310 CUR Numbers
[nt
->nte_index
] = -1;
313 sprintf(temp
, "numeric %s %d", buf
,
314 CUR Numbers
[nt
->nte_index
]);
317 sprintf(temp
, "unknown");
322 sprintf(temp
, "Cap not found: %s", buf
);
326 if (op
!= SHOW_EDIT
) {
329 if (nt
->nte_type
== BOOLEAN
) {
330 ptextln("Value flipped");
331 if (nt
->nte_index
== xon_index
) {
332 xon_shadow
= !xon_shadow
;
334 CUR Booleans
[nt
->nte_index
] = !CUR Booleans
[nt
->nte_index
];
338 ptextln("Enter new value");
339 read_string(buf
, sizeof(buf
));
341 switch (nt
->nte_type
) {
343 _nc_reset_input((FILE *) 0, buf
);
344 _nc_trans_string(tmp
, tmp
+ sizeof(tmp
));
345 s
= (char *)malloc(strlen(tmp
) + 1);
347 CUR Strings
[nt
->nte_index
] = s
;
348 sprintf(temp
, "new string value %s", nt
->nte_name
);
350 ptextln(expand(CUR Strings
[nt
->nte_index
]));
353 if (sscanf(buf
, "%d", &n
) == 1) {
354 CUR Numbers
[nt
->nte_index
] = n
;
355 sprintf(temp
, "new numeric value %s %d",
359 sprintf(temp
, "Illegal number: %s", buf
);
369 ** get_string_cap_byname(name, long_name)
371 ** Given a cap name, find the value
372 ** Errors are quietly ignored.
375 get_string_cap_byname(
377 const char **long_name
)
379 struct name_table_entry
const *nt
;
381 if ((nt
= _nc_find_entry(name
, _nc_info_hash_table
))) {
382 if (nt
->nte_type
== STRING
) {
383 *long_name
= strfnames
[nt
->nte_index
];
384 return (CUR Strings
[nt
->nte_index
]);
392 ** get_string_cap_byvalue(value)
394 ** Given a capability string, find its position in the data base.
395 ** Return the index or -1 if not found.
398 get_string_cap_byvalue(
404 for (i
= 0; i
< STRCOUNT
; i
++) {
405 if (CUR Strings
[i
] == value
) {
409 /* search for translated strings */
410 for (i
= 0; i
< TM_last
; i
++) {
411 if (TM_string
[i
].value
== value
) {
412 return TM_string
[i
].index
;
420 ** show_changed(test_list, status, ch)
422 ** Display a list of caps that have been changed.
426 struct test_list
*t GCC_UNUSED
,
427 int *state GCC_UNUSED
,
430 int i
, header
= 1, v
;
433 static char title
[] = " old value cap new value";
436 for (i
= 0; i
< BOOLCOUNT
; i
++) {
437 v
= (i
== xon_index
) ? xon_shadow
: CUR Booleans
[i
];
438 if (original_term
.Booleans
[i
] != v
) {
443 sprintf(temp
, "%30d %6s %d",
444 original_term
.Booleans
[i
], boolnames
[i
], v
);
448 for (i
= 0; i
< NUMCOUNT
; i
++) {
449 if (original_term
.Numbers
[i
] != CUR Numbers
[i
]) {
454 sprintf(temp
, "%30d %6s %d",
455 original_term
.Numbers
[i
], numnames
[i
],
460 for (i
= 0; i
< STRCOUNT
; i
++) {
461 a
= original_term
.Strings
[i
] ? original_term
.Strings
[i
] : "";
462 b
= CUR Strings
[i
] ? CUR Strings
[i
] : "";
468 strcpy(abuf
, _nc_tic_expand(a
, TRUE
, TRUE
));
469 sprintf(temp
, "%30s %6s %s", abuf
, strnames
[i
],
470 _nc_tic_expand(b
, TRUE
, TRUE
));
475 ptextln("No changes");
478 *ch
= REQUEST_PROMPT
;
484 ** Return TRUE if the user has modified the terminfo
492 for (i
= 0; i
< BOOLCOUNT
; i
++) {
493 v
= (i
== xon_index
) ? xon_shadow
: CUR Booleans
[i
];
494 if (original_term
.Booleans
[i
] != v
) {
498 for (i
= 0; i
< NUMCOUNT
; i
++) {
499 if (original_term
.Numbers
[i
] != CUR Numbers
[i
]) {
503 for (i
= 0; i
< STRCOUNT
; i
++) {
504 a
= original_term
.Strings
[i
] ? original_term
.Strings
[i
] : "";
505 b
= CUR Strings
[i
] ? CUR Strings
[i
] : "";
513 /*****************************************************************************
515 * Maintain the list of capabilities that can be tested
517 *****************************************************************************/
520 ** mark_cap(name, flag)
522 ** Mark the cap data base with the flag provided.
529 struct name_table_entry
const *nt
;
531 if ((nt
= _nc_find_entry(name
, _nc_info_hash_table
))) {
532 switch (nt
->nte_type
) {
534 flag_boolean
[nt
->nte_index
] |= flag
;
537 flag_strings
[nt
->nte_index
] |= flag
;
540 flag_numerics
[nt
->nte_index
] |= flag
;
543 sprintf(temp
, "unknown cap type (%s)", name
);
548 sprintf(temp
, "Cap not found: %s", name
);
555 ** can_test(name-list, flags)
557 ** Scan the name list and get the names.
558 ** Enter each name into the can-test data base.
559 ** <space> ( and ) may be used as separators.
570 for (j
= 0; (name
[j
] = ch
= *s
); s
++) {
571 if (ch
== ' ' || ch
== ')' || ch
== '(') {
574 mark_cap(name
, flags
);
582 mark_cap(name
, flags
);
588 ** cap_index(name-list, index-list)
590 ** Scan the name list and return a list of indexes.
591 ** <space> ( and ) may be used as separators.
592 ** This list is terminated with -1.
599 struct name_table_entry
const *nt
;
606 if (ch
== ' ' || ch
== ')' || ch
== '(' || ch
== 0) {
609 if ((nt
= _nc_find_entry(name
,
610 _nc_info_hash_table
)) &&
611 (nt
->nte_type
== STRING
)) {
612 *inx
++ = nt
->nte_index
;
628 ** cap_match(name-list, cap)
630 ** Scan the name list and see if the cap is in the list.
631 ** Return TRUE if we find an exact match.
632 ** <space> ( and ) may be used as separators.
644 while ((s
= strstr(names
, cap
))) {
645 c
= (names
== s
) ? 0 : *(s
- 1);
647 if ((c
== 0 || c
== ' ' || c
== '(') &&
648 (t
== 0 || t
== ' ' || t
== ')')) {
661 ** show_report(test_list, status, ch)
663 ** Display a list of caps that can be tested
668 int *state GCC_UNUSED
,
673 const char *nx
[BOOLCOUNT
+ NUMCOUNT
+ STRCOUNT
];
675 flag
= t
->flags
& 255;
677 for (i
= 0; i
< BOOLCOUNT
; i
++) {
678 if (flag_boolean
[i
] & flag
) {
679 nx
[nc
++] = boolnames
[i
];
682 for (i
= 0; i
< NUMCOUNT
; i
++) {
683 if (flag_numerics
[i
] & flag
) {
684 nx
[nc
++] = numnames
[i
];
687 for (i
= 0; i
< STRCOUNT
; i
++) {
688 if (flag_strings
[i
] & flag
) {
689 nx
[nc
++] = strnames
[i
];
693 for (i
= 0; i
< nc
- 1; i
++) {
694 for (j
= i
+ 1; j
< nc
; j
++) {
695 if (strcmp(nx
[i
], nx
[j
]) > 0) {
702 if (flag
& FLAG_FUNCTION_KEY
) {
703 ptextln("The following function keys can be tested:");
705 if (flag
& FLAG_CAN_TEST
) {
706 ptextln("The following capabilities can be tested:");
708 if (flag
& FLAG_TESTED
) {
709 ptextln("The following capabilities have been tested:");
712 for (i
= 0; i
< nc
; i
++) {
713 sprintf(temp
, "%s ", nx
[i
]);
717 *ch
= REQUEST_PROMPT
;
721 ** show_untested(test_list, status, ch)
723 ** Display a list of caps that are defined but cannot be tested.
724 ** Don't bother to sort this list.
728 struct test_list
*t GCC_UNUSED
,
729 int *state GCC_UNUSED
,
734 ptextln("Caps that are defined but cannot be tested:");
735 for (i
= 0; i
< BOOLCOUNT
; i
++) {
736 if (flag_boolean
[i
] == 0 && CUR Booleans
[i
]) {
737 sprintf(temp
, "%s ", boolnames
[i
]);
741 for (i
= 0; i
< NUMCOUNT
; i
++) {
742 if (flag_numerics
[i
] == 0 && CUR Numbers
[i
] >= 0) {
743 sprintf(temp
, "%s ", numnames
[i
]);
747 for (i
= 0; i
< STRCOUNT
; i
++) {
748 if (flag_strings
[i
] == 0 && CUR Strings
[i
]) {
749 sprintf(temp
, "%s ", strnames
[i
]);
754 *ch
= REQUEST_PROMPT
;
760 ** Initialize the function key data base
767 struct name_table_entry
const *nt
;
768 int label_strings
[STRCOUNT
];
770 _nc_copy_termtype(&original_term
, &cur_term
->type
);
771 for (i
= 0; i
< BOOLCOUNT
; i
++) {
772 original_term
.Booleans
[i
] = CUR Booleans
[i
];
774 for (i
= 0; i
< NUMCOUNT
; i
++) {
775 original_term
.Numbers
[i
] = CUR Numbers
[i
];
777 /* scan for labels */
778 for (i
= lc
= 0; i
< STRCOUNT
; i
++) {
779 original_term
.Strings
[i
] = CUR Strings
[i
];
780 if (strncmp(strnames
[i
], "lf", 2) == 0) {
781 flag_strings
[i
] |= FLAG_LABEL
;
782 if (CUR Strings
[i
]) {
783 label_strings
[lc
++] = i
;
787 /* scan for function keys */
788 for (i
= 0; i
< STRCOUNT
; i
++) {
789 if ((strnames
[i
][0] == 'k') && strcmp(strnames
[i
], "kmous")) {
790 flag_strings
[i
] |= FLAG_FUNCTION_KEY
;
792 for (j
= 0; j
< lc
; j
++) {
793 if (!strcmp(&strnames
[i
][1],
794 &strnames
[label_strings
[j
]][1])) {
795 lab
= CUR Strings
[label_strings
[j
]];
799 enter_key(strnames
[i
], CUR Strings
[i
], lab
);
802 /* Lookup the translated strings */
803 for (i
= 0; i
< TM_last
; i
++) {
804 if ((nt
= _nc_find_entry(TM_string
[i
].name
,
805 _nc_info_hash_table
)) && (nt
->nte_type
== STRING
)) {
806 TM_string
[i
].index
= nt
->nte_index
;
808 sprintf(temp
, "TM_string lookup failed for: %s",
813 if ((nt
= _nc_find_entry("xon", _nc_info_hash_table
)) != 0) {
814 xon_index
= nt
->nte_index
;
816 xon_shadow
= xon_xoff
;
820 ** change_one_entry(test_list, status, ch)
822 ** Change the padding on the selected cap
826 struct test_list
*test
,
830 struct name_table_entry
const *nt
;
831 int i
, j
, x
, star
, slash
, v
, dot
, ch
;
834 const char *current_string
;
838 i
= test
->flags
& 255;
840 /* read the cap name from the user */
841 ptext("enter name: ");
842 read_string(pad
, 32);
843 if (pad
[0] == '\0' || pad
[1] == '\0') {
847 if ((nt
= _nc_find_entry(pad
, _nc_info_hash_table
)) &&
848 (nt
->nte_type
== STRING
)) {
850 current_string
= CUR Strings
[x
];
852 sprintf(temp
, "%s is not a string capability", pad
);
854 generic_done_message(test
, state
, chp
);
859 current_string
= tx_cap
[i
];
860 strcpy(pad
, strnames
[x
]);
862 if (!current_string
) {
863 ptextln("That string is not currently defined. Please enter a new value, including the padding delay:");
864 read_string(buf
, sizeof(buf
));
865 _nc_reset_input((FILE *) 0, buf
);
866 _nc_trans_string(pad
, pad
+ sizeof(pad
));
867 t
= (char *)malloc(strlen(pad
) + 1);
870 sprintf(temp
, "new string value %s", strnames
[x
]);
875 sprintf(buf
, "Current value: (%s) %s", pad
, _nc_tic_expand(current_string
, TRUE
, TRUE
));
877 ptextln("Enter new pad. 0 for no pad. CR for no change.");
878 read_string(buf
, 32);
879 if (buf
[0] == '\0' || (buf
[1] == '\0' && isalpha(UChar(buf
[0])))) {
883 star
= slash
= FALSE
;
884 for (j
= v
= dot
= 0; (ch
= buf
[j
]); j
++) {
885 if (ch
>= '0' && ch
<= '9') {
886 v
= ch
- '0' + v
* 10;
890 } else if (ch
== '*') {
892 } else if (ch
== '/') {
894 } else if (ch
== '.') {
897 sprintf(temp
, "Illegal character: %c", ch
);
899 ptext("General format: 99.9*/ ");
900 generic_done_message(test
, state
, chp
);
909 sprintf(pad
, "%d.%d%s%s", v
/ 10, v
% 10,
910 star
? "*" : "", slash
? "/" : "");
912 sprintf(pad
, "%d%s%s",
913 v
, star
? "*" : "", slash
? "/" : "");
917 for (v
= 0; (ch
= *t
= *s
++); t
++) {
918 if (v
== '$' && ch
== '<') {
919 while ((ch
= *s
++) && (ch
!= '>'));
920 for (p
= pad
; (*++t
= *p
++); );
922 while ((*t
++ = *s
++));
929 sprintf(t
, "$<%s>", pad
);
931 if ((t
= (char *)malloc(strlen(buf
) + 1))) {
938 generic_done_message(test
, state
, chp
);
942 ** build_change_menu(menu_list)
944 ** Build the change pad menu list
953 for (i
= j
= 0; i
< txp
; i
++) {
954 if ((k
= tx_index
[i
]) >= 0) {
955 s
= _nc_tic_expand(tx_cap
[i
], TRUE
, TRUE
);
957 sprintf(change_pad_text
[j
], "%c) (%s) %s",
958 'a' + j
, strnames
[k
], s
);
959 change_pad_list
[j
].flags
= i
;
960 change_pad_list
[j
].lines_needed
= 4;
961 change_pad_list
[j
].menu_entry
= change_pad_text
[j
];
962 change_pad_list
[j
].test_procedure
= change_one_entry
;
966 strcpy(change_pad_text
[j
], "z) enter name");
967 change_pad_list
[j
].flags
= 255;
968 change_pad_list
[j
].lines_needed
= 4;
969 change_pad_list
[j
].menu_entry
= change_pad_text
[j
];
970 change_pad_list
[j
].test_procedure
= change_one_entry
;
972 change_pad_list
[j
].flags
= MENU_LAST
;
975 ptextln(m
->menu_title
);