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 <sys/types.h>
35 #define MAX(A,B) ((A) > (B) ? (A) : (B))
36 #define MIN(A,B) ((A) < (B) ? (A) : (B))
37 #define CTL(ch) ((ch) & 037)
39 #define MIN_INPUT 2 /* Minimum number of lines for input window */
41 extern char *calloc();
44 /* Structure for holding current displayed menu */
46 WINDOW *ms_screen; /* Window for this menu */
47 WINDOW *ms_title; /* Title subwindow */
48 WINDOW *ms_menu; /* Menu subwindow */
49 WINDOW *ms_input; /* Input subwindow */
50 int ms_input_y; /* Input subwindow reference coordinate */
53 #define NULLMS ((struct menu_screen *) 0)
55 Menu *top_menu; /* Root for command search */
58 * Hook function to cause error messages to be printed through
59 * curses instead of around it.
63 menu_com_err_hook(who, code, fmt, args)
69 char buf[BUFSIZ], *cp;
73 (void) strcpy(buf, who);
74 for (cp = buf; *cp; cp++);
78 (void) strcpy(cp, error_message(code));
82 _strbuf._flag = _IOWRT + _IOSTRG;
84 _strbuf._cnt = BUFSIZ - (cp - buf);
85 _doprnt(fmt, args, &_strbuf);
86 (void) putc('\0', &_strbuf);
91 * Start_menu takes a menu as an argument. It initializes curses u.s.w.,
92 * and a quit in any submenu should unwind back to here. (it might not,
93 * if user functions which run their own menus don't cooperate.)
94 * Start_menu should only be called once, at the start of the program.
99 struct menu_screen *make_ms();
100 register int (*old_hook)() = set_com_err_hook(menu_com_err_hook);
102 if (initscr() == ERR) {
103 fputs("Can't initialize curses!\n", stderr);
106 (void) raw(); /* We parse & print everything ourselves */
108 cur_ms = make_ms(0); /* So we always have some current */
112 (void) Do_menu(m, -1, (char **) NULL);
114 (void) set_com_err_hook(old_hook);
121 (void) wclear(cur_ms->ms_screen);
122 (void) wrefresh(cur_ms->ms_screen);
128 /* Like Start_menu, except it doesn't print menus and doesn't use curses */
135 (void) Do_menu(m, -1, (char **) NULL);
139 * Create a new menu screen template with the specified menu length
146 struct menu_screen *ms;
149 if (MAX_TITLE + length + MIN_INPUT > LINES) {
150 fputs("Menu too big!\n", stderr);
154 ms = (struct menu_screen *) malloc(sizeof(struct menu_screen));
156 ms->ms_screen = newwin(0, 0, 0, 0);
157 ms->ms_title = subwin(ms->ms_screen, MAX_TITLE, 0, 0, 0);
158 ms->ms_menu = subwin(ms->ms_screen,
159 length, 0, MAX_TITLE, 0);
160 ms->ms_input = subwin(ms->ms_screen, 0, 0,
161 ms->ms_input_y = MAX_TITLE + length,
164 scrollok(ms->ms_input, TRUE);
165 (void) wmove(ms->ms_input, 0, 0);
166 (void) wclear(ms->ms_screen);
172 * This routine destroys a menu_screen.
175 struct menu_screen *ms;
177 delwin(ms->ms_title);
179 delwin(ms->ms_input);
180 delwin(ms->ms_screen);
185 * This guy actually puts up the menu
186 * Note: if margc < 0, no 'r' option will be displayed (i.e., on the
190 Do_menu(m, margc, margv)
195 struct menu_screen *my_ms, *old_cur_ms;
196 char argvals[MAX_ARGC][MAX_ARGLEN]; /* This is where args are stored */
197 char buf[MAX_ARGC * MAX_ARGLEN];
198 char *argv[MAX_ARGC];
201 struct menu_line *command, *Find_command();
203 int quitflag, is_topmenu = (margc < 0);
205 /* Entry function gets called with old menu_screen still current */
206 if (m->m_entry != NULLFUNC)
207 if (m->m_entry(m, margc, margv) == DM_QUIT)
210 /* The following get run only in curses mode */
211 if (cur_ms != NULLMS) {
212 /* Get a menu_screen */
214 cur_ms = my_ms = make_ms(m->m_length + 1 + (is_topmenu?0:1));
216 /* Now print the title and the menu */
217 (void) wclear(my_ms->ms_menu);
218 (void) wmove(my_ms->ms_title, 0, MAX(0, (COLS -
219 strlen(m->m_title)) >> 1));
220 (void) wstandout(my_ms->ms_title);
221 (void) waddstr(my_ms->ms_title, m->m_title);
222 (void) wstandend(my_ms->ms_title);
224 for (line = 0; line < m->m_length; line++) {
225 int len = strlen(m->m_lines[line].ml_command);
226 if (len > 12) len=12;
228 (void) wmove(my_ms->ms_menu, line, 0);
230 (void) wprintw(my_ms->ms_menu, "%2d. (%s)%*s %s.", line + 1,
231 m->m_lines[line].ml_command,
233 m->m_lines[line].ml_doc);
235 (void) wmove(my_ms->ms_menu, line++, 0);
237 (void) waddstr(my_ms->ms_menu,
238 " r. (return) Return to previous menu.");
239 (void) wmove(my_ms->ms_menu, line, 0);
241 (void) waddstr(my_ms->ms_menu, " q. (quit) Quit.");
245 /* This will be set by a return val from func or submenu */
246 quitflag = DM_NORMAL;
247 /* This is here because we may be coming from another menu */
249 touchwin(my_ms->ms_screen);
251 if (!Prompt_input("Command: ", buf, sizeof(buf)))
253 /* Parse it into the argument list */
254 /* If there's nothing there, try again */
255 /* Initialize argv */
256 for (argc = 0; argc < MAX_ARGC; argc++)
257 argv[argc] = argvals[argc];
259 if ((argc = Parse_words(buf, argv, MAX_ARGLEN)) == 0)
261 if ((line = atoi(argv[0])) > 0 && line <= m->m_length) {
262 command = &m->m_lines[line - 1];
264 else if ((!is_topmenu &&
265 (!strcmp(argv[0], "r")
266 || !strcmp(argv[0], "return")))
267 || !strcmp(argv[0], "q")
268 || !strcmp(argv[0], "quit")) {
269 /* here if it's either return or quit */
270 if (cur_ms != NULLMS) {
274 if (m->m_exit != NULLFUNC)
276 return (*argv[0] == 'r' ? DM_NORMAL : DM_QUIT);
277 /* finally, try to find it using Find_command */
279 else if ((command = Find_command(argvals[0])) ==
280 (struct menu_line *) 0) {
281 Put_message("Command not recognized");
284 /* If we got to here, command is a valid menu_line */
285 /* Send the offical command name into the argv */
286 (void) strcpy(argvals[0], command->ml_command);
287 /* Show that we're working on it */
288 Put_message(command->ml_doc);
289 /* Print args that we've already got */
290 for (i = 1; i < argc; i++) {
291 if (command->ml_args[i].ma_prompt == NULL)
293 (void) sprintf(buf, "%s%s", command->ml_args[i].ma_prompt,
297 /* Get remaining arguments, if any */
298 for (; argc < command->ml_argc; argc++) {
299 if (!Prompt_input(command->ml_args[argc].ma_prompt,
300 argvals[argc], sizeof(argvals[argc])))
303 if (command->ml_function != NULLFUNC) {
304 /* If it's got a function, call it */
305 quitflag = command->ml_function(argc, argv);
307 else if (command->ml_submenu != NULLMENU) {
308 /* Else see if it is a submenu */
309 quitflag = Do_menu(command->ml_submenu, argc, argv);
312 /* If it's got neither, something is wrong */
313 Put_message("*INTERNAL ERROR: NO FUNCTION OR MENU FOR LINE*");
315 if (quitflag == DM_QUIT) {
316 if (cur_ms != NULLMS) {
320 if (m->m_exit != NULLFUNC)
329 /* Prompt the user for input in the input window of cur_ms */
330 int Prompt_input(prompt, buf, buflen)
339 if (cur_ms != NULLMS) {
341 getyx(cur_ms->ms_input, y, x);
342 (void) wmove(cur_ms->ms_input, y, 0);
344 touchwin(cur_ms->ms_screen);
346 (void) waddstr(cur_ms->ms_input, prompt);
347 getyx(cur_ms->ms_input, y, x);
352 (void) wmove(cur_ms->ms_input, y, x);
353 (void) wclrtoeol(cur_ms->ms_input);
360 (void) kill(getpid(), SIGTSTP);
364 (void) wclear(cur_ms->ms_input);
365 (void) waddstr(cur_ms->ms_input, prompt);
366 (void) touchwin(cur_ms->ms_screen);
368 (void) wrefresh(curscr);
370 getyx(cur_ms->ms_input, y, x);
376 /* these should be obtained by doing ioctl() on tty */
391 if (isprint(c) && (p - buf < buflen)) {
392 (void) waddch(cur_ms->ms_input, c);
401 (void) waddch(cur_ms->ms_input, '\n');
402 (void) waddch(cur_ms->ms_input, '\r');
404 (void) wclrtoeol(cur_ms->ms_input);
410 printf("%s", prompt);
411 if (gets(buf) == NULL)
417 strcpy(buf, strtrim(buf));
421 /* Prompt the user for input in the input window of cur_ms, but don't echo
422 and allow some control characters */
423 int Password_input(prompt, buf, buflen)
432 if (cur_ms != NULLMS) {
434 getyx(cur_ms->ms_input, y, x);
435 (void) wmove(cur_ms->ms_input, y, 0);
437 touchwin(cur_ms->ms_screen);
439 (void) waddstr(cur_ms->ms_input, prompt);
440 getyx(cur_ms->ms_input, y, x);
443 for (p = buf; p - buf < buflen;) {
444 (void) wmove(cur_ms->ms_input, y, x);
445 (void) wclrtoeol(cur_ms->ms_input);
452 (void) kill(getpid(), SIGTSTP);
456 (void) wclear(cur_ms->ms_input);
457 (void) waddstr(cur_ms->ms_input, prompt);
459 getyx(cur_ms->ms_input, y, x);
463 (void) waddch(cur_ms->ms_input, '\n');
464 (void) waddch(cur_ms->ms_input, '\r');
466 (void) wclrtoeol(cur_ms->ms_input);
489 struct sgttyb ttybuf, nttybuf;
490 printf("%s", prompt);
491 /* turn off echoing */
492 (void) ioctl(0, TIOCGETP, (char *)&ttybuf);
494 nttybuf.sg_flags &= ~ECHO;
495 (void)ioctl(0, TIOCSETP, (char *)&nttybuf);
496 if (gets(buf) == NULL) {
497 (void) ioctl(0, TIOCSETP, (char *)&ttybuf);
502 (void) ioctl(0, TIOCSETP, (char *)&ttybuf);
512 /* This routine will cause the most recently put message to be the
513 one at the top of the screen when a ---More--- prompt is displayed */
516 if (cur_ms != NULLMS) {
517 lines_left = LINES - cur_ms->ms_input_y - 1;
524 /* Turn off paging */
530 /* Print a message in the input window of cur_ms. */
534 char *copy, *line, *s;
536 copy = (char *) malloc((u_int)COLS);
539 if (s - line >= COLS-1) {
540 (void) strncpy(copy, line, COLS-1);
542 } else if (*s == '\n') {
544 (void) strcpy(copy, line);
554 /* Will be truncated to COLS characters. */
564 if (lines_left >= 0) {
565 if (--lines_left == 0) {
566 /* Give the user a more prompt */
567 if (cur_ms != NULLMS) {
568 (void) wstandout(cur_ms->ms_input);
569 (void) wprintw(cur_ms->ms_input, "---More---");
570 (void) wstandend(cur_ms->ms_input);
572 chr = getchar();/* We do care what it is */
573 if (chr == 'q' || chr == 'Q') {
577 getyx(cur_ms->ms_input, y, x);
578 /* x is a bitbucket; avoid lint problems */
580 (void) wmove(cur_ms->ms_input, y, 0);
581 (void) wclrtoeol(cur_ms->ms_input);
584 printf("---More (hit return)---");
587 Start_paging(); /* Reset lines_left */
591 if (cur_ms != NULLMS) {
592 msg1 = (char *) calloc((u_int)COLS, 1);
593 (void) strncpy(msg1, msg, COLS-1);
594 for (i = strlen(msg1); i < COLS - 1; i++)
596 (void) wprintw(cur_ms->ms_input, "%s\n", msg1);
597 /* refresh_ms(cur_ms); */
604 /* Refresh a menu_screen onto the real screen */
606 struct menu_screen *ms;
610 getyx(ms->ms_input, y, x);
611 (void) wmove(ms->ms_screen, y + ms->ms_input_y, x);
612 (void) wrefresh(ms->ms_screen);
615 /* Parse buf into a list of words, which will be placed in strings specified by
616 argv. Space for these strings must have already been allocated.
617 Only the first n characters of each word will be copied */
618 Parse_words(buf, argv, n)
624 char *start, *end; /* For sausage machine */
628 for (argc = 0; argc < MAX_ARGC; argc++) {
629 while (isspace(*start))
630 start++; /* Kill whitespace */
632 break; /* Nothing left */
633 /* Now find the end of the word */
634 for (end = start; *end != '\0' && !isspace(*end); end++);
635 (void) strncpy(argv[argc], start, MIN(end - start, n)); /* Copy it */
636 argv[argc][MIN(end - start, n - 1)] = '\0'; /* Terminate */
642 /* This is the internal form of Find_command, which recursively searches
643 for a menu_line with command command in the specified menu */
644 /* It will search to a maximum depth of d */
646 find_command_from(c, m, d)
652 struct menu_line *maybe;
655 return ((struct menu_line *) 0); /* Too deep! */
656 for (line = 0; line < m->m_length; line++) {
657 if (!strcmp(c, m->m_lines[line].ml_command)) {
658 return (&m->m_lines[line]);
660 else if (m->m_lines[line].ml_submenu != NULLMENU
662 find_command_from(c, m->m_lines[line].ml_submenu, d - 1))
663 != (struct menu_line *) 0) {
667 /* If we got to here, nothing works */
668 return ((struct menu_line *) 0);
671 /* Find_command searches down the current menu tree */
672 /* And returns a pointer to a menu_line with the specified command name */
673 /* It returns (struct menu_line *) 0 if none is found */
675 Find_command(command)
678 if (top_menu == NULLMENU) {
679 return ((struct menu_line *) 0);
682 return (find_command_from(command, top_menu, MAX_MENU_DEPTH));
690 * c-continued-statement-offset: 4
692 * c-argdecl-indent: 4