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>
34 #define MAX(A,B) ((A) > (B) ? (A) : (B))
35 #define MIN(A,B) ((A) < (B) ? (A) : (B))
36 #define CTL(ch) ((ch) & 037)
38 #define MIN_INPUT 2 /* Minimum number of lines for input window */
40 extern char *calloc();
43 /* Structure for holding current displayed menu */
45 WINDOW *ms_screen; /* Window for this menu */
46 WINDOW *ms_title; /* Title subwindow */
47 WINDOW *ms_menu; /* Menu subwindow */
48 WINDOW *ms_input; /* Input subwindow */
49 int ms_input_y; /* Input subwindow reference coordinate */
52 #define NULLMS ((struct menu_screen *) 0)
54 Menu *top_menu; /* Root for command search */
57 * Start_menu takes a menu as an argument. It initializes curses u.s.w.,
58 * and a quit in any submenu should unwind back to here. (it might not,
59 * if user functions which run their own menus don't cooperate.)
60 * Start_menu should only be called once, at the start of the program.
65 struct menu_screen *make_ms();
67 if (initscr() == ERR) {
68 fputs("Can't initialize curses!\n", stderr);
71 (void) raw(); /* We parse & print everything ourselves */
73 cur_ms = make_ms(0); /* So we always have some current */
77 (void) Do_menu(m, -1, (char **) NULL);
84 (void) wclear(cur_ms->ms_screen);
85 (void) wrefresh(cur_ms->ms_screen);
91 /* Like Start_menu, except it doesn't print menus and doesn't use curses */
98 (void) Do_menu(m, -1, (char **) NULL);
102 * Create a new menu screen template with the specified menu length
109 struct menu_screen *ms;
112 if (MAX_TITLE + length + MIN_INPUT > LINES) {
113 fputs("Menu too big!\n", stderr);
117 ms = (struct menu_screen *) malloc(sizeof(struct menu_screen));
119 ms->ms_screen = newwin(0, 0, 0, 0);
120 ms->ms_title = subwin(ms->ms_screen, MAX_TITLE, 0, 0, 0);
121 ms->ms_menu = subwin(ms->ms_screen,
122 length, 0, MAX_TITLE, 0);
123 ms->ms_input = subwin(ms->ms_screen, 0, 0,
124 ms->ms_input_y = MAX_TITLE + length,
127 scrollok(ms->ms_input, TRUE);
128 (void) wmove(ms->ms_input, 0, 0);
129 (void) wclear(ms->ms_screen);
135 * This routine destroys a menu_screen.
138 struct menu_screen *ms;
140 delwin(ms->ms_title);
142 delwin(ms->ms_input);
143 delwin(ms->ms_screen);
148 * This guy actually puts up the menu
149 * Note: if margc < 0, no 'r' option will be displayed (i.e., on the
153 Do_menu(m, margc, margv)
158 struct menu_screen *my_ms, *old_cur_ms;
159 char argvals[MAX_ARGC][MAX_ARGLEN]; /* This is where args are stored */
160 char buf[MAX_ARGC * MAX_ARGLEN];
161 char *argv[MAX_ARGC];
164 struct menu_line *command, *Find_command();
166 int quitflag, is_topmenu = (margc < 0);
168 /* Entry function gets called with old menu_screen still current */
169 if (m->m_entry != NULLFUNC)
170 if (m->m_entry(m, margc, margv) == DM_QUIT)
173 /* The following get run only in curses mode */
174 if (cur_ms != NULLMS) {
175 /* Get a menu_screen */
177 cur_ms = my_ms = make_ms(m->m_length + 1 + (is_topmenu?0:1));
179 /* Now print the title and the menu */
180 (void) wclear(my_ms->ms_menu);
181 (void) wmove(my_ms->ms_title, 0, MAX(0, (COLS -
182 strlen(m->m_title)) >> 1));
183 (void) wstandout(my_ms->ms_title);
184 (void) waddstr(my_ms->ms_title, m->m_title);
185 (void) wstandend(my_ms->ms_title);
187 for (line = 0; line < m->m_length; line++) {
188 (void) wmove(my_ms->ms_menu, line, 0);
189 (void) wprintw(my_ms->ms_menu, "%2d. (%-12s) %s.", line + 1,
190 m->m_lines[line].ml_command,
191 m->m_lines[line].ml_doc);
193 (void) wmove(my_ms->ms_menu, line++, 0);
195 (void) waddstr(my_ms->ms_menu,
196 " r. (return ) Return to previous menu.");
197 (void) wmove(my_ms->ms_menu, line, 0);
199 (void) waddstr(my_ms->ms_menu, " q. (quit ) Quit.");
204 /* This will be set by a return val from func or submenu */
205 quitflag = DM_NORMAL;
206 /* This is here because we may be coming from another menu */
208 touchwin(my_ms->ms_screen);
210 if (!Prompt_input("Command: ", buf, sizeof(buf)))
212 /* Parse it into the argument list */
213 /* If there's nothing there, try again */
214 /* Initialize argv */
215 for (argc = 0; argc < MAX_ARGC; argc++)
216 argv[argc] = argvals[argc];
218 if ((argc = Parse_words(buf, argv, MAX_ARGLEN)) == 0)
220 if ((line = atoi(argv[0])) > 0 && line <= m->m_length) {
221 command = &m->m_lines[line - 1];
223 else if ((!is_topmenu &&
224 (!strcmp(argv[0], "r")
225 || !strcmp(argv[0], "return")))
226 || !strcmp(argv[0], "q")
227 || !strcmp(argv[0], "quit")) {
228 /* here if it's either return or quit */
229 if (cur_ms != NULLMS) {
233 if (m->m_exit != NULLFUNC)
235 return (*argv[0] == 'r' ? DM_NORMAL : DM_QUIT);
236 /* finally, try to find it using Find_command */
238 else if ((command = Find_command(argvals[0])) ==
239 (struct menu_line *) 0) {
240 Put_message("Command not recognized");
243 /* If we got to here, command is a valid menu_line */
244 /* Send the offical command name into the argv */
245 (void) strcpy(argvals[0], command->ml_command);
246 /* Show that we're working on it */
247 Put_message(command->ml_doc);
248 /* Print args that we've already got */
249 for (i = 1; i < argc; i++) {
250 if (command->ml_args[i].ma_prompt == NULL)
252 (void) sprintf(buf, "%s%s", command->ml_args[i].ma_prompt,
256 /* Get remaining arguments, if any */
257 for (; argc < command->ml_argc; argc++) {
258 if (!Prompt_input(command->ml_args[argc].ma_prompt,
259 argvals[argc], sizeof(argvals[argc])))
262 if (command->ml_function != NULLFUNC) {
263 /* If it's got a function, call it */
264 quitflag = command->ml_function(argc, argv);
266 else if (command->ml_submenu != NULLMENU) {
267 /* Else see if it is a submenu */
268 quitflag = Do_menu(command->ml_submenu, argc, argv);
271 /* If it's got neither, something is wrong */
272 Put_message("*INTERNAL ERROR: NO FUNCTION OR MENU FOR LINE*");
274 if (quitflag == DM_QUIT) {
275 if (cur_ms != NULLMS) {
279 if (m->m_exit != NULLFUNC)
288 /* Prompt the user for input in the input window of cur_ms */
289 int Prompt_input(prompt, buf, buflen)
298 if (cur_ms != NULLMS) {
300 getyx(cur_ms->ms_input, y, x);
301 (void) wmove(cur_ms->ms_input, y, 0);
303 touchwin(cur_ms->ms_screen);
305 (void) waddstr(cur_ms->ms_input, prompt);
306 getyx(cur_ms->ms_input, y, x);
311 /* for (p = buf; p - buf < buflen;) { */
312 (void) wmove(cur_ms->ms_input, y, x);
313 (void) wclrtoeol(cur_ms->ms_input);
320 (void) kill(getpid(), SIGTSTP);
324 (void) wclear(cur_ms->ms_input);
325 (void) waddstr(cur_ms->ms_input, prompt);
326 (void) wrefresh(curscr);
327 getyx(cur_ms->ms_input, y, x);
346 if (isprint(c) && (p - buf < buflen)) {
347 (void) waddch(cur_ms->ms_input, c);
356 (void) waddch(cur_ms->ms_input, '\n');
357 (void) waddch(cur_ms->ms_input, '\r');
359 (void) wclrtoeol(cur_ms->ms_input);
365 printf("%s", prompt);
366 if (gets(buf) == NULL)
373 /* Prompt the user for input in the input window of cur_ms, but don't echo
374 and allow some control characters */
375 int Password_input(prompt, buf, buflen)
384 if (cur_ms != NULLMS) {
386 getyx(cur_ms->ms_input, y, x);
387 (void) wmove(cur_ms->ms_input, y, 0);
389 touchwin(cur_ms->ms_screen);
391 (void) waddstr(cur_ms->ms_input, prompt);
392 getyx(cur_ms->ms_input, y, x);
395 for (p = buf; p - buf < buflen;) {
396 (void) wmove(cur_ms->ms_input, y, x);
397 (void) wclrtoeol(cur_ms->ms_input);
404 (void) kill(getpid(), SIGTSTP);
408 (void) wclear(cur_ms->ms_input);
409 (void) waddstr(cur_ms->ms_input, prompt);
410 (void) wrefresh(curscr);
411 getyx(cur_ms->ms_input, y, x);
415 (void) waddch(cur_ms->ms_input, '\n');
416 (void) waddch(cur_ms->ms_input, '\r');
418 (void) wclrtoeol(cur_ms->ms_input);
441 struct sgttyb ttybuf, nttybuf;
442 printf("%s", prompt);
443 /* turn off echoing */
444 (void) ioctl(0, TIOCGETP, (char *)&ttybuf);
446 nttybuf.sg_flags &= ~ECHO;
447 (void)ioctl(0, TIOCSETP, (char *)&nttybuf);
448 if (gets(buf) == NULL) {
449 (void) ioctl(0, TIOCSETP, (char *)&ttybuf);
454 (void) ioctl(0, TIOCSETP, (char *)&ttybuf);
464 /* This routine will cause the most recently put message to be the
465 one at the top of the screen when a ---More--- prompt is displayed */
468 if (cur_ms != NULLMS) {
469 lines_left = LINES - cur_ms->ms_input_y - 1;
476 /* Turn off paging */
482 /* Print a message in the input window of cur_ms. */
486 char *copy, *line, *s;
488 copy = (char *) malloc((u_int)COLS);
491 if (s - line >= COLS-1) {
492 (void) strncpy(copy, line, COLS-1);
494 } else if (*s == '\n') {
496 (void) strcpy(copy, line);
506 /* Will be truncated to COLS characters. */
516 if (lines_left >= 0) {
517 if (--lines_left == 0) {
518 /* Give the user a more prompt */
519 if (cur_ms != NULLMS) {
520 (void) wstandout(cur_ms->ms_input);
521 (void) wprintw(cur_ms->ms_input, "---More---");
522 (void) wstandend(cur_ms->ms_input);
524 chr = getchar();/* We do care what it is */
525 if (chr == 'q' || chr == 'Q') {
529 getyx(cur_ms->ms_input, y, x);
530 /* x is a bitbucket; avoid lint problems */
532 (void) wmove(cur_ms->ms_input, y, 0);
533 (void) wclrtoeol(cur_ms->ms_input);
536 printf("---More (hit return)---");
539 Start_paging(); /* Reset lines_left */
543 if (cur_ms != NULLMS) {
544 msg1 = (char *) calloc((u_int)COLS, 1);
545 (void) strncpy(msg1, msg, COLS-1);
546 for (i = strlen(msg1); i < COLS - 1; i++)
548 (void) wprintw(cur_ms->ms_input, "%s\n", msg1);
549 /* refresh_ms(cur_ms); */
556 /* Refresh a menu_screen onto the real screen */
558 struct menu_screen *ms;
562 getyx(ms->ms_input, y, x);
563 (void) wmove(ms->ms_screen, y + ms->ms_input_y, x);
564 (void) wrefresh(ms->ms_screen);
567 /* Parse buf into a list of words, which will be placed in strings specified by
568 argv. Space for these strings must have already been allocated.
569 Only the first n characters of each word will be copied */
570 Parse_words(buf, argv, n)
576 char *start, *end; /* For sausage machine */
580 for (argc = 0; argc < MAX_ARGC; argc++) {
581 while (isspace(*start))
582 start++; /* Kill whitespace */
584 break; /* Nothing left */
585 /* Now find the end of the word */
586 for (end = start; *end != '\0' && !isspace(*end); end++);
587 (void) strncpy(argv[argc], start, MIN(end - start, n)); /* Copy it */
588 argv[argc][MIN(end - start, n - 1)] = '\0'; /* Terminate */
594 /* This is the internal form of Find_command, which recursively searches
595 for a menu_line with command command in the specified menu */
596 /* It will search to a maximum depth of d */
598 find_command_from(c, m, d)
604 struct menu_line *maybe;
607 return ((struct menu_line *) 0); /* Too deep! */
608 for (line = 0; line < m->m_length; line++) {
609 if (!strcmp(c, m->m_lines[line].ml_command)) {
610 return (&m->m_lines[line]);
612 else if (m->m_lines[line].ml_submenu != NULLMENU
614 find_command_from(c, m->m_lines[line].ml_submenu, d - 1))
615 != (struct menu_line *) 0) {
619 /* If we got to here, nothing works */
620 return ((struct menu_line *) 0);
623 /* Find_command searches down the current menu tree */
624 /* And returns a pointer to a menu_line with the specified command name */
625 /* It returns (struct menu_line *) 0 if none is found */
627 Find_command(command)
630 if (top_menu == NULLMENU) {
631 return ((struct menu_line *) 0);
634 return (find_command_from(command, top_menu, MAX_MENU_DEPTH));
639 sms_com_err_hook(who, code, fmt, args)
645 char buf[BUFSIZ], *cp;
649 (void) strcpy(buf, who);
650 for (cp = buf; *cp; cp++);
654 (void) strcpy(cp, error_message(code));
658 _strbuf._flag = _IOWRT + _IOSTRG;
660 _strbuf._cnt = BUFSIZ - (cp - buf);
661 _doprnt(fmt, args, &_strbuf);
662 (void) putc('\0', &_strbuf);
670 * c-continued-statement-offset: 4
672 * c-argdecl-indent: 4