2 * Copyright 1987 by the Massachusetts Institute of Technology.
3 * For copying and distribution information, see the file
10 * Revision 1.9 1987-08-21 23:31:37 wesommer
11 * Now passes lint with minimal difficulties.
13 * Revision 1.8 87/08/21 22:52:37 wesommer
16 * Revision 1.7 87/08/17 14:23:05 jtkohl
19 * Revision 1.6 87/08/17 11:55:23 jtkohl
20 * changes made by poto. Looks like cleanup
22 * Revision 1.5 87/08/07 18:09:46 poto
23 * will not enter menu if ->m_entry returns DM_QUIT;
24 * the command args from a submenu command will be passed on to ->m_entry();
26 * Revision 1.4 87/08/05 14:48:04 ambar
27 * added latest set of hackery, to fix missing
28 * newlines, and not being able to quit out of
31 * Revision 1.3 87/08/03 05:10:34 wesommer
32 * This one appears to work.
34 * Revision 1.2 87/08/03 04:16:51 wesommer
35 * Here's another, which is probably better.
37 * Revision 1.1 87/07/31 18:02:23 ambar
41 * Generic menu system module.
43 * Basically, we define an enormous tree structure which represents the
44 * menu. Some extra pieces (ml_command, ma_doc) get thrown in so we can
45 * also use the structure for a command-based system.
47 * By making the menu descriptions so general, we can ease porting to just
52 static char rcsid_menu_c[] = "$Header$";
56 #include <sys/types.h>
64 #define MAX(A,B) ((A) > (B) ? (A) : (B))
65 #define MIN(A,B) ((A) < (B) ? (A) : (B))
66 #define CTL(ch) ((ch) & 037)
68 #define MIN_INPUT 2 /* Minimum number of lines for input window */
70 extern char *calloc();
73 /* Structure for holding current displayed menu */
75 WINDOW *ms_screen; /* Window for this menu */
76 WINDOW *ms_title; /* Title subwindow */
77 WINDOW *ms_menu; /* Menu subwindow */
78 WINDOW *ms_input; /* Input subwindow */
79 int ms_input_y; /* Input subwindow reference coordinate */
82 #define NULLMS ((struct menu_screen *) 0)
84 Menu *top_menu; /* Root for command search */
87 * Start_menu takes a menu as an argument. It initializes curses u.s.w.,
88 * and a quit in any submenu should unwind back to here. (it might not,
89 * if user functions which run their own menus don't cooperate.)
90 * Start_menu should only be called once, at the start of the program.
95 struct menu_screen *make_ms();
97 if (initscr() == ERR) {
98 fputs("Can't initialize curses!\n", stderr);
101 (void) raw(); /* We parse & print everything ourselves */
103 cur_ms = make_ms(0); /* So we always have some current */
107 (void) Do_menu(m, -1, (char **) NULL);
114 (void) wclear(cur_ms->ms_screen);
115 (void) wrefresh(cur_ms->ms_screen);
121 /* Like Start_menu, except it doesn't print menus and doesn't use curses */
128 (void) Do_menu(m, -1, (char **) NULL);
132 * Create a new menu screen template with the specified menu length
139 struct menu_screen *ms;
142 if (MAX_TITLE + length + MIN_INPUT > LINES) {
143 fputs("Menu too big!\n", stderr);
147 ms = (struct menu_screen *) malloc(sizeof(struct menu_screen));
149 ms->ms_screen = newwin(0, 0, 0, 0);
150 ms->ms_title = subwin(ms->ms_screen, MAX_TITLE, 0, 0, 0);
151 ms->ms_menu = subwin(ms->ms_screen,
152 length, 0, MAX_TITLE, 0);
153 ms->ms_input = subwin(ms->ms_screen, 0, 0,
154 ms->ms_input_y = MAX_TITLE + length,
157 scrollok(ms->ms_input, TRUE);
158 (void) wmove(ms->ms_input, 0, 0);
159 (void) wclear(ms->ms_screen);
165 * This routine destroys a menu_screen.
168 struct menu_screen *ms;
170 delwin(ms->ms_title);
172 delwin(ms->ms_input);
173 delwin(ms->ms_screen);
178 * This guy actually puts up the menu
179 * Note: if margc < 0, no 'r' option will be displayed (i.e., on the
183 Do_menu(m, margc, margv)
188 struct menu_screen *my_ms, *old_cur_ms;
189 char argvals[MAX_ARGC][MAX_ARGLEN]; /* This is where args are stored */
190 char buf[MAX_ARGC * MAX_ARGLEN];
191 char *argv[MAX_ARGC];
194 struct menu_line *command, *Find_command();
196 int quitflag, is_topmenu = (margc < 0);
198 /* Entry function gets called with old menu_screen still current */
199 if (m->m_entry != NULLFUNC)
200 if (m->m_entry(m, margc, margv) == DM_QUIT)
203 /* The following get run only in curses mode */
204 if (cur_ms != NULLMS) {
205 /* Get a menu_screen */
207 cur_ms = my_ms = make_ms(m->m_length + 1 + (is_topmenu?0:1));
209 /* Now print the title and the menu */
210 (void) wclear(my_ms->ms_menu);
211 (void) wmove(my_ms->ms_title, 0, MAX(0, (COLS -
212 strlen(m->m_title)) >> 1));
213 (void) wstandout(my_ms->ms_title);
214 (void) waddstr(my_ms->ms_title, m->m_title);
215 (void) wstandend(my_ms->ms_title);
217 for (line = 0; line < m->m_length; line++) {
218 (void) wmove(my_ms->ms_menu, line, 0);
219 (void) wprintw(my_ms->ms_menu, "%2d. (%-12s) %s.", line + 1,
220 m->m_lines[line].ml_command,
221 m->m_lines[line].ml_doc);
223 (void) wmove(my_ms->ms_menu, line++, 0);
225 (void) waddstr(my_ms->ms_menu,
226 " r. (return ) Return to previous menu.");
227 (void) wmove(my_ms->ms_menu, line, 0);
229 (void) waddstr(my_ms->ms_menu, " q. (quit ) Quit.");
234 /* This will be set by a return val from func or submenu */
235 quitflag = DM_NORMAL;
236 /* This is here because we may be coming from another menu */
238 touchwin(my_ms->ms_screen);
240 if (!Prompt_input("Command: ", buf, sizeof(buf)))
242 /* Parse it into the argument list */
243 /* If there's nothing there, try again */
244 /* Initialize argv */
245 for (argc = 0; argc < MAX_ARGC; argc++)
246 argv[argc] = argvals[argc];
248 if ((argc = Parse_words(buf, argv, MAX_ARGLEN)) == 0)
250 if ((line = atoi(argv[0])) > 0 && line <= m->m_length) {
251 command = &m->m_lines[line - 1];
253 else if ((!is_topmenu &&
254 (!strcmp(argv[0], "r")
255 || !strcmp(argv[0], "return")))
256 || !strcmp(argv[0], "q")
257 || !strcmp(argv[0], "quit")) {
258 /* here if it's either return or quit */
259 if (cur_ms != NULLMS) {
263 if (m->m_exit != NULLFUNC)
265 return (*argv[0] == 'r' ? DM_NORMAL : DM_QUIT);
266 /* finally, try to find it using Find_command */
268 else if ((command = Find_command(argvals[0])) ==
269 (struct menu_line *) 0) {
270 Put_message("Command not recognized");
273 /* If we got to here, command is a valid menu_line */
274 /* Send the offical command name into the argv */
275 (void) strcpy(argvals[0], command->ml_command);
276 /* Show that we're working on it */
277 Put_message(command->ml_doc);
278 /* Print args that we've already got */
279 for (i = 1; i < argc; i++) {
280 if (command->ml_args[i].ma_prompt == NULL)
282 (void) sprintf(buf, "%s%s", command->ml_args[i].ma_prompt,
286 /* Get remaining arguments, if any */
287 for (; argc < command->ml_argc; argc++) {
288 if (!Prompt_input(command->ml_args[argc].ma_prompt,
289 argvals[argc], sizeof(argvals[argc])))
292 if (command->ml_function != NULLFUNC) {
293 /* If it's got a function, call it */
294 quitflag = command->ml_function(argc, argv);
296 else if (command->ml_submenu != NULLMENU) {
297 /* Else see if it is a submenu */
298 quitflag = Do_menu(command->ml_submenu, argc, argv);
301 /* If it's got neither, something is wrong */
302 Put_message("*INTERNAL ERROR: NO FUNCTION OR MENU FOR LINE*");
304 if (quitflag == DM_QUIT) {
305 if (cur_ms != NULLMS) {
309 if (m->m_exit != NULLFUNC)
318 /* Prompt the user for input in the input window of cur_ms */
319 int Prompt_input(prompt, buf, buflen)
328 if (cur_ms != NULLMS) {
330 getyx(cur_ms->ms_input, y, x);
331 (void) wmove(cur_ms->ms_input, y, 0);
333 touchwin(cur_ms->ms_screen);
335 (void) waddstr(cur_ms->ms_input, prompt);
336 getyx(cur_ms->ms_input, y, x);
341 /* for (p = buf; p - buf < buflen;) { */
342 (void) wmove(cur_ms->ms_input, y, x);
343 (void) wclrtoeol(cur_ms->ms_input);
350 (void) kill(getpid(), SIGTSTP);
354 (void) wclear(cur_ms->ms_input);
355 (void) waddstr(cur_ms->ms_input, prompt);
356 (void) wrefresh(curscr);
357 getyx(cur_ms->ms_input, y, x);
376 if (isprint(c) && (p - buf < buflen)) {
377 (void) waddch(cur_ms->ms_input, c);
386 (void) waddch(cur_ms->ms_input, '\n');
387 (void) waddch(cur_ms->ms_input, '\r');
389 (void) wclrtoeol(cur_ms->ms_input);
395 printf("%s", prompt);
396 if (gets(buf) == NULL)
403 /* Prompt the user for input in the input window of cur_ms, but don't echo
404 and allow some control characters */
405 int Password_input(prompt, buf, buflen)
414 if (cur_ms != NULLMS) {
416 getyx(cur_ms->ms_input, y, x);
417 (void) wmove(cur_ms->ms_input, y, 0);
419 touchwin(cur_ms->ms_screen);
421 (void) waddstr(cur_ms->ms_input, prompt);
422 getyx(cur_ms->ms_input, y, x);
425 for (p = buf; p - buf < buflen;) {
426 (void) wmove(cur_ms->ms_input, y, x);
427 (void) wclrtoeol(cur_ms->ms_input);
434 (void) kill(getpid(), SIGTSTP);
438 (void) wclear(cur_ms->ms_input);
439 (void) waddstr(cur_ms->ms_input, prompt);
440 (void) wrefresh(curscr);
441 getyx(cur_ms->ms_input, y, x);
445 (void) waddch(cur_ms->ms_input, '\n');
446 (void) waddch(cur_ms->ms_input, '\r');
448 (void) wclrtoeol(cur_ms->ms_input);
471 struct sgttyb ttybuf, nttybuf;
472 printf("%s", prompt);
473 /* turn off echoing */
474 (void) ioctl(0, TIOCGETP, (char *)&ttybuf);
476 nttybuf.sg_flags &= ~ECHO;
477 (void)ioctl(0, TIOCSETP, (char *)&nttybuf);
478 if (gets(buf) == NULL) {
479 (void) ioctl(0, TIOCSETP, (char *)&ttybuf);
484 (void) ioctl(0, TIOCSETP, (char *)&ttybuf);
494 /* This routine will cause the most recently put message to be the
495 one at the top of the screen when a ---More--- prompt is displayed */
498 if (cur_ms != NULLMS) {
499 lines_left = LINES - cur_ms->ms_input_y - 1;
506 /* Turn off paging */
512 /* Print a message in the input window of cur_ms. */
516 char *copy, *line, *s;
518 copy = (char *) malloc((u_int)COLS);
521 if (s - line >= COLS-1) {
522 (void) strncpy(copy, line, COLS-1);
524 } else if (*s == '\n') {
526 (void) strcpy(copy, line);
536 /* Will be truncated to COLS characters. */
546 if (lines_left >= 0) {
547 if (--lines_left == 0) {
548 /* Give the user a more prompt */
549 if (cur_ms != NULLMS) {
550 (void) wstandout(cur_ms->ms_input);
551 (void) wprintw(cur_ms->ms_input, "---More---");
552 (void) wstandend(cur_ms->ms_input);
554 chr = getchar();/* We do care what it is */
555 if (chr == 'q' || chr == 'Q') {
559 getyx(cur_ms->ms_input, y, x);
560 /* x is a bitbucket; avoid lint problems */
562 (void) wmove(cur_ms->ms_input, y, 0);
563 (void) wclrtoeol(cur_ms->ms_input);
566 printf("---More (hit return)---");
569 Start_paging(); /* Reset lines_left */
573 if (cur_ms != NULLMS) {
574 msg1 = (char *) calloc((u_int)COLS, 1);
575 (void) strncpy(msg1, msg, COLS-1);
576 for (i = strlen(msg1); i < COLS - 1; i++)
578 (void) wprintw(cur_ms->ms_input, "%s\n", msg1);
579 /* refresh_ms(cur_ms); */
586 /* Refresh a menu_screen onto the real screen */
588 struct menu_screen *ms;
592 getyx(ms->ms_input, y, x);
593 (void) wmove(ms->ms_screen, y + ms->ms_input_y, x);
594 (void) wrefresh(ms->ms_screen);
597 /* Parse buf into a list of words, which will be placed in strings specified by
598 argv. Space for these strings must have already been allocated.
599 Only the first n characters of each word will be copied */
600 Parse_words(buf, argv, n)
606 char *start, *end; /* For sausage machine */
610 for (argc = 0; argc < MAX_ARGC; argc++) {
611 while (isspace(*start))
612 start++; /* Kill whitespace */
614 break; /* Nothing left */
615 /* Now find the end of the word */
616 for (end = start; *end != '\0' && !isspace(*end); end++);
617 (void) strncpy(argv[argc], start, MIN(end - start, n)); /* Copy it */
618 argv[argc][MIN(end - start, n - 1)] = '\0'; /* Terminate */
624 /* This is the internal form of Find_command, which recursively searches
625 for a menu_line with command command in the specified menu */
626 /* It will search to a maximum depth of d */
628 find_command_from(c, m, d)
634 struct menu_line *maybe;
637 return ((struct menu_line *) 0); /* Too deep! */
638 for (line = 0; line < m->m_length; line++) {
639 if (!strcmp(c, m->m_lines[line].ml_command)) {
640 return (&m->m_lines[line]);
642 else if (m->m_lines[line].ml_submenu != NULLMENU
644 find_command_from(c, m->m_lines[line].ml_submenu, d - 1))
645 != (struct menu_line *) 0) {
649 /* If we got to here, nothing works */
650 return ((struct menu_line *) 0);
653 /* Find_command searches down the current menu tree */
654 /* And returns a pointer to a menu_line with the specified command name */
655 /* It returns (struct menu_line *) 0 if none is found */
657 Find_command(command)
660 if (top_menu == NULLMENU) {
661 return ((struct menu_line *) 0);
664 return (find_command_from(command, top_menu, MAX_MENU_DEPTH));