2 * Copyright 1987 by the Massachusetts Institute of Technology.
3 * For copying and distribution information, see the file
10 * Generic menu system module.
12 * Basically, we define an enormous tree structure which represents the
13 * menu. Some extra pieces (ml_command, ma_doc) get thrown in so we can
14 * also use the structure for a command-based system.
16 * By making the menu descriptions so general, we can ease porting to just
21 static char rcsid_menu_c[] = "$Header$";
25 #include <mit-copyright.h>
26 #include <sys/types.h>
36 #define MAX(A,B) ((A) > (B) ? (A) : (B))
37 #define MIN(A,B) ((A) < (B) ? (A) : (B))
38 #define CTL(ch) ((ch) & 037)
40 #define MIN_INPUT 2 /* Minimum number of lines for input window */
42 extern FILE *fdopen();
44 extern char *calloc();
47 FILE *log_file = (FILE *) NULL; /* file stream of log file */
50 /* Structure for holding current displayed menu */
52 WINDOW *ms_screen; /* Window for this menu */
53 WINDOW *ms_title; /* Title subwindow */
54 WINDOW *ms_menu; /* Menu subwindow */
55 WINDOW *ms_input; /* Input subwindow */
56 int ms_input_y; /* Input subwindow reference coordinate */
59 #define NULLMS ((struct menu_screen *) 0)
61 Menu *top_menu; /* Root for command search */
64 * Hook function to cause error messages to be printed through
65 * curses instead of around it.
69 menu_com_err_hook(who, code, fmt, args)
75 char buf[BUFSIZ], *cp;
79 (void) strcpy(buf, who);
80 for (cp = buf; *cp; cp++);
84 (void) strcpy(cp, error_message(code));
88 _strbuf._flag = _IOWRT + _IOSTRG;
90 _strbuf._cnt = BUFSIZ - (cp - buf);
91 _doprnt(fmt, args, &_strbuf);
92 (void) putc('\0', &_strbuf);
97 * Start_menu takes a menu as an argument. It initializes curses u.s.w.,
98 * and a quit in any submenu should unwind back to here. (it might not,
99 * if user functions which run their own menus don't cooperate.)
100 * Start_menu should only be called once, at the start of the program.
105 struct menu_screen *make_ms();
106 register int (*old_hook)() = set_com_err_hook(menu_com_err_hook);
108 if (initscr() == ERR) {
109 fputs("Can't initialize curses!\n", stderr);
112 (void) raw(); /* We parse & print everything ourselves */
114 cur_ms = make_ms(0); /* So we always have some current */
117 (void) Do_menu(m, -1, (char **) NULL);
119 (void) set_com_err_hook(old_hook);
126 (void) wclear(cur_ms->ms_screen);
127 (void) wrefresh(cur_ms->ms_screen);
133 /* Like Start_menu, except it doesn't print menus and doesn't use curses */
140 (void) Do_menu(m, -1, (char **) NULL);
144 * Create a new menu screen template with the specified menu length
151 struct menu_screen *ms;
154 if (MAX_TITLE + length + MIN_INPUT > LINES) {
155 fputs("Menu too big!\n", stderr);
159 ms = (struct menu_screen *) malloc(sizeof(struct menu_screen));
161 ms->ms_screen = newwin(0, 0, 0, 0);
162 ms->ms_title = subwin(ms->ms_screen, MAX_TITLE, 0, 0, 0);
163 ms->ms_menu = subwin(ms->ms_screen,
164 length, 0, MAX_TITLE, 0);
165 ms->ms_input = subwin(ms->ms_screen, 0, 0,
166 ms->ms_input_y = MAX_TITLE + length,
169 scrollok(ms->ms_input, TRUE);
170 (void) wmove(ms->ms_input, 0, 0);
171 (void) wclear(ms->ms_screen);
177 * This routine destroys a menu_screen.
180 struct menu_screen *ms;
182 delwin(ms->ms_title);
184 delwin(ms->ms_input);
185 delwin(ms->ms_screen);
190 * This guy actually puts up the menu
191 * Note: if margc < 0, no 'r' option will be displayed (i.e., on the
195 Do_menu(m, margc, margv)
200 struct menu_screen *my_ms, *old_cur_ms;
201 char argvals[MAX_ARGC][MAX_ARGLEN]; /* This is where args are stored */
202 char buf[MAX_ARGC * MAX_ARGLEN];
203 char *argv[MAX_ARGC];
206 struct menu_line *command, *Find_command();
208 int quitflag, is_topmenu = (margc < 0);
209 int toggle_logging();
211 /* Entry function gets called with old menu_screen still current */
212 if (m->m_entry != NULLFUNC)
213 if (m->m_entry(m, margc, margv) == DM_QUIT)
216 /* The following get run only in curses mode */
217 if (cur_ms != NULLMS) {
218 /* Get a menu_screen */
220 /* 2 is for the 2 obligatory lines; quit and toggle */
221 cur_ms = my_ms = make_ms(m->m_length + 2 + (is_topmenu ? 0 : 1));
223 /* Now print the title and the menu */
224 (void) wclear(my_ms->ms_menu);
225 (void) wmove(my_ms->ms_title, 0, MAX(0, (COLS -
226 strlen(m->m_title)) >> 1));
227 (void) wstandout(my_ms->ms_title);
228 (void) waddstr(my_ms->ms_title, m->m_title);
229 (void) wstandend(my_ms->ms_title);
231 for (line = 0; line < m->m_length; line++) {
232 int len = strlen(m->m_lines[line].ml_command);
233 if (len > 12) len=12;
235 (void) wmove(my_ms->ms_menu, line, 0);
237 (void) wprintw(my_ms->ms_menu, "%2d. (%s)%*s %s.", line + 1,
238 m->m_lines[line].ml_command,
240 m->m_lines[line].ml_doc);
242 (void) wmove(my_ms->ms_menu, line++, 0);
244 (void) waddstr(my_ms->ms_menu,
245 " r. (return) Return to previous menu.");
246 (void) wmove(my_ms->ms_menu, line++, 0);
248 (void) waddstr(my_ms->ms_menu,
249 " t. (toggle) Toggle logging on and off.");
250 (void) wmove(my_ms->ms_menu, line, 0);
251 (void) waddstr(my_ms->ms_menu, " q. (quit) Quit.");
253 Put_message(m->m_title);
254 for (line = 0; line < m->m_length; line++) {
255 sprintf(buf, "%2d. (%s)%*s %s.", line + 1,
256 m->m_lines[line].ml_command,
257 12 - strlen(m->m_lines[line].ml_command), "",
258 m->m_lines[line].ml_doc);
262 Put_message(" r. (return) Return to previous menu.");
263 Put_message(" t. (toggle) Toggle logging on and off.");
264 Put_message(" q. (quit) Quit.");
265 Put_message(" ?. Print this information.");
269 /* This will be set by a return val from func or submenu */
270 quitflag = DM_NORMAL;
271 /* This is here because we may be coming from another menu */
273 touchwin(my_ms->ms_screen);
275 if (!Prompt_input("Command: ", buf, sizeof(buf)))
277 /* Parse it into the argument list */
278 /* If there's nothing there, try again */
279 /* Initialize argv */
280 for (argc = 0; argc < MAX_ARGC; argc++)
281 argv[argc] = argvals[argc];
283 if ((argc = Parse_words(buf, argv, MAX_ARGLEN)) == 0)
285 if ((line = atoi(argv[0])) > 0 && line <= m->m_length) {
286 command = &m->m_lines[line - 1];
288 else if ((!is_topmenu &&
289 (!strcmp(argv[0], "r") || !strcmp(argv[0], "return")))
290 || !strcmp(argv[0], "q") || !strcmp(argv[0], "quit")) {
292 /* here if it's either return or quit */
293 if (cur_ms != NULLMS) {
297 if (m->m_exit != NULLFUNC)
299 return (*argv[0] == 'r' ? DM_NORMAL : DM_QUIT);
301 else if (argv[0][0] == '?') {
302 for (line = 0; line < m->m_length; line++) {
303 sprintf(buf, "%2d. (%s)%*s %s.", line + 1,
304 m->m_lines[line].ml_command,
305 12 - strlen(m->m_lines[line].ml_command), "",
306 m->m_lines[line].ml_doc);
310 Put_message(" r. (return) Return to previous menu.");
311 Put_message(" t. (toggle) Toggle logging on and off.");
312 Put_message(" q. (quit) Quit.");
315 else if (!strcmp(argv[0], "t") || !strcmp(argv[0], "toggle")) {
316 toggle_logging(argc, argv);
319 /* finally, try to find it using Find_command */
320 else if ((command = Find_command(m, argvals[0])) ==
321 (struct menu_line *) 0) {
322 Put_message("Command not recognized");
325 /* If we got to here, command is a valid menu_line */
326 /* Send the offical command name into the argv */
327 (void) strcpy(argvals[0], command->ml_command);
328 /* Show that we're working on it */
329 Put_message(command->ml_doc);
330 /* Print args that we've already got */
331 for (i = 1; i < argc; i++) {
332 if (command->ml_args[i].ma_prompt == NULL)
334 (void) sprintf(buf, "%s%s", command->ml_args[i].ma_prompt,
338 /* Get remaining arguments, if any */
339 for (; argc < command->ml_argc; argc++) {
340 if (!Prompt_input(command->ml_args[argc].ma_prompt,
341 argvals[argc], sizeof(argvals[argc])))
344 if (command->ml_function != NULLFUNC) {
345 /* If it's got a function, call it */
346 quitflag = command->ml_function(argc, argv);
348 else if (command->ml_submenu != NULLMENU) {
349 /* Else see if it is a submenu */
350 quitflag = Do_menu(command->ml_submenu, argc, argv);
353 /* If it's got neither, something is wrong */
354 Put_message("*INTERNAL ERROR: NO FUNCTION OR MENU FOR LINE*");
356 if (quitflag == DM_QUIT) {
357 if (cur_ms != NULLMS) {
361 if (m->m_exit != NULLFUNC)
370 /* Prompt the user for input in the input window of cur_ms */
371 int Prompt_input(prompt, buf, buflen)
380 if (cur_ms != NULLMS) {
382 getyx(cur_ms->ms_input, y, x);
383 (void) wmove(cur_ms->ms_input, y, 0);
385 touchwin(cur_ms->ms_screen);
387 (void) waddstr(cur_ms->ms_input, prompt);
388 getyx(cur_ms->ms_input, y, x);
393 (void) wmove(cur_ms->ms_input, y, x);
394 (void) touchwin(cur_ms->ms_screen);
395 (void) wclrtoeol(cur_ms->ms_input);
397 c = getchar() & 0x7f;
403 (void) kill(getpid(), SIGTSTP);
407 (void) wclear(cur_ms->ms_input);
408 (void) waddstr(cur_ms->ms_input, prompt);
409 (void) touchwin(cur_ms->ms_screen);
413 getyx(cur_ms->ms_input, y, x);
419 /* these should be obtained by doing ioctl() on tty */
434 /* (buflen - 1) leaves room for the \0 */
435 if (isprint(c) && (p - buf < buflen - 1)) {
436 (void) waddch(cur_ms->ms_input, c);
445 (void) waddch(cur_ms->ms_input, '\n');
446 (void) waddch(cur_ms->ms_input, '\r');
448 (void) wclrtoeol(cur_ms->ms_input);
454 printf("%s", prompt);
455 if (gets(buf) == NULL)
461 strcpy(buf, strtrim(buf));
465 /* Prompt the user for input in the input window of cur_ms, but don't echo
466 and allow some control characters */
467 int Password_input(prompt, buf, buflen)
476 if (cur_ms != NULLMS) {
478 getyx(cur_ms->ms_input, y, x);
479 (void) wmove(cur_ms->ms_input, y, 0);
481 touchwin(cur_ms->ms_screen);
483 (void) waddstr(cur_ms->ms_input, prompt);
484 getyx(cur_ms->ms_input, y, x);
487 for (p = buf; p - buf < buflen;) {
488 (void) wmove(cur_ms->ms_input, y, x);
489 (void) wclrtoeol(cur_ms->ms_input);
491 c = getchar() & 0x7f;
496 (void) kill(getpid(), SIGTSTP);
500 (void) wclear(cur_ms->ms_input);
501 (void) waddstr(cur_ms->ms_input, prompt);
503 getyx(cur_ms->ms_input, y, x);
507 (void) waddch(cur_ms->ms_input, '\n');
508 (void) waddch(cur_ms->ms_input, '\r');
510 (void) wclrtoeol(cur_ms->ms_input);
533 struct sgttyb ttybuf, nttybuf;
534 printf("%s", prompt);
535 /* turn off echoing */
536 (void) ioctl(0, TIOCGETP, (char *)&ttybuf);
538 nttybuf.sg_flags &= ~ECHO;
539 (void)ioctl(0, TIOCSETP, (char *)&nttybuf);
540 if (gets(buf) == NULL) {
541 (void) ioctl(0, TIOCSETP, (char *)&ttybuf);
546 (void) ioctl(0, TIOCSETP, (char *)&ttybuf);
556 /* This routine will cause the most recently put message to be the
557 one at the top of the screen when a ---More--- prompt is displayed */
560 if (cur_ms != NULLMS) {
561 lines_left = LINES - cur_ms->ms_input_y - 1;
568 /* Turn off paging */
574 /* Print a message in the input window of cur_ms. */
578 char *copy, *line, *s;
580 copy = (char *) malloc((u_int)COLS);
582 if (log_file) { /* if we're doing logging; we assume that the */
583 /* file has already been opened. */
584 (void) fprintf(log_file, "%s\n", msg);
589 if (s - line >= COLS-1) {
590 (void) strncpy(copy, line, COLS-1);
592 } else if (*s == '\n') {
594 (void) strcpy(copy, line);
604 /* Will be truncated to COLS characters. */
614 if (lines_left >= 0) {
615 if (--lines_left == 0) {
616 /* Give the user a more prompt */
617 if (cur_ms != NULLMS) {
618 (void) wstandout(cur_ms->ms_input);
619 (void) wprintw(cur_ms->ms_input, "---More---");
620 (void) wstandend(cur_ms->ms_input);
622 chr = getchar() & 0x7f;/* We do care what it is */
623 if (chr == 'q' || chr == 'Q') {
627 getyx(cur_ms->ms_input, y, x);
628 /* x is a bitbucket; avoid lint problems */
630 (void) wmove(cur_ms->ms_input, y, 0);
631 (void) wclrtoeol(cur_ms->ms_input);
634 printf("---More (hit return)---");
637 Start_paging(); /* Reset lines_left */
641 if (cur_ms != NULLMS) {
642 msg1 = (char *) calloc((u_int)COLS, 1);
643 (void) strncpy(msg1, msg, COLS-1);
644 for (i = strlen(msg1); i < COLS - 1; i++)
646 (void) wprintw(cur_ms->ms_input, "%s\n", msg1);
647 /* refresh_ms(cur_ms); */
654 /* Refresh a menu_screen onto the real screen */
656 struct menu_screen *ms;
660 getyx(ms->ms_input, y, x);
661 (void) wmove(ms->ms_screen, y + ms->ms_input_y, x);
662 (void) wrefresh(ms->ms_screen);
665 /* Parse buf into a list of words, which will be placed in strings specified by
666 argv. Space for these strings must have already been allocated.
667 Only the first n characters of each word will be copied */
668 Parse_words(buf, argv, n)
674 char *start, *end; /* For sausage machine */
678 for (argc = 0; argc < MAX_ARGC; argc++) {
679 while (isspace(*start))
680 start++; /* Kill whitespace */
682 break; /* Nothing left */
683 /* Now find the end of the word */
684 for (end = start; *end != '\0' && !isspace(*end); end++);
685 (void) strncpy(argv[argc], start, MIN(end - start, n)); /* Copy it */
686 argv[argc][MIN(end - start, n - 1)] = '\0'; /* Terminate */
692 /* This is the internal form of Find_command, which recursively searches
693 for a menu_line with command command in the specified menu */
694 /* It will search to a maximum depth of d */
696 find_command_from(c, m, d)
702 struct menu_line *maybe;
705 return ((struct menu_line *) 0); /* Too deep! */
706 for (line = 0; line < m->m_length; line++) {
707 if (!strcmp(c, m->m_lines[line].ml_command)) {
708 return (&m->m_lines[line]);
711 for (line = 0; line < m->m_length; line++) {
712 if (m->m_lines[line].ml_submenu != NULLMENU &&
713 (maybe = find_command_from(c, m->m_lines[line].ml_submenu, d - 1))
714 != (struct menu_line *) 0) {
718 /* If we got to here, nothing works */
719 return ((struct menu_line *) 0);
722 /* Find_command searches down the current menu tree */
723 /* And returns a pointer to a menu_line with the specified command name */
724 /* It returns (struct menu_line *) 0 if none is found */
726 Find_command(m, command)
731 return ((struct menu_line *) 0);
734 return (find_command_from(command, m, MAX_MENU_DEPTH));
740 toggle_logging(argc, argv)
747 if (log_file == (FILE *) NULL) {
751 Put_message("I've lost my SENSE of DIRECTION! I have no IDEA who I AM!");
752 Put_message("My God... You've turned him into a DEMOCRAT!!");
753 Put_message(" -- Doonesbury");
755 Put_message("translation: your log file can be found in \"/usr/tmp/a.out.pid\".");
757 (void) sprintf(buf, "/usr/tmp/%s-log.%d", whoami, pid);
760 (void) sprintf(buf, "/usr/tmp/%s-log.%d", whoami, pid);
763 log_file = fopen(buf,"a");
765 if (log_file == (FILE *) NULL)
766 Put_message("Open of log file failed. Logging is not on.");
768 Put_message("Log file successfully opened.");
770 else { /* log_file is a valid pointer; turn off logging. */
771 (void) fflush(log_file);
772 (void) fclose(log_file);
773 log_file = (FILE *) NULL;
774 Put_message("Logging off.");
783 * c-continued-statement-offset: 4
785 * c-argdecl-indent: 4