2 * Copyright 1987 by the Massachusetts Institute of Technology.
3 * For copying and distribution information, see the file
10 * Revision 1.2 1987-08-03 04:16:51 wesommer
11 * Here's another, which is probably better.
13 * Revision 1.1 87/07/31 18:02:23 ambar
17 * Generic menu system module.
19 * Basically, we define an enormous tree structure which represents the
20 * menu. Some extra pieces (ml_command, ma_doc) get thrown in so we can
21 * also use the structure for a command-based system.
23 * By making the menu descriptions so general, we can ease porting to just
28 static char rcsid_menu_c[] = "$Header$";
36 #define MAX(A,B) ((A) > (B) ? (A) : (B))
37 #define MIN(A,B) ((A) < (B) ? (A) : (B))
39 #define MIN_INPUT 2 /* Minimum number of lines for input window */
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 * Start_menu takes a menu as an argument. It initializes curses u.s.w.,
59 * and a quit in any submenu should unwind back to here. (it might not,
60 * if user functions which run their own menus don't cooperate.)
61 * Start_menu should only be called once, at the start of the program.
66 struct menu_screen *make_ms();
68 if(initscr() == ERR) {
69 fputs("Can't initialize curses!\n", stderr);
72 raw(); /* We parse & print everything ourselves */
74 cur_ms = make_ms(0); /* So we always have some current menu_screen */
76 (void) Do_menu(m); /* Run the menu */
80 /* Like Start_menu, except it doesn't print menus and doesn't use curses */
90 * Create a new menu screen template with the specified menu length
93 struct menu_screen *make_ms(length)
96 struct menu_screen *ms;
99 if(MAX_TITLE + length + MIN_INPUT > LINES) {
100 fputs("Menu too big!\n", stderr);
104 ms = (struct menu_screen *) malloc(sizeof(struct menu_screen));
106 ms->ms_screen = newwin(0, 0, 0, 0);
107 ms->ms_title = subwin(ms->ms_screen, MAX_TITLE, 0, 0, 0);
108 ms->ms_menu = subwin(ms->ms_screen,
109 length, 0, MAX_TITLE, 0);
110 ms->ms_input = subwin(ms->ms_screen, 0, 0,
111 ms->ms_input_y = MAX_TITLE + length,
114 scrollok(ms->ms_input, TRUE);
115 (void) wmove(ms->ms_input, 0, 0);
116 (void) wclear(ms->ms_screen);
122 * This routine destroys a menu_screen.
125 struct menu_screen *ms;
127 delwin(ms->ms_title);
129 delwin(ms->ms_input);
130 delwin(ms->ms_screen);
135 * This guy actually puts up the menu
140 struct menu_screen *my_ms, *old_cur_ms;
141 char argvals[MAX_ARGC][MAX_ARGLEN]; /* This is where args are stored */
142 char buf[MAX_ARGC * MAX_ARGLEN];
143 char *argv[MAX_ARGC];
146 struct menu_line *command, *Find_command();
150 /* Entry function gets called with old menu_screen still current */
151 if(m->m_entry != NULLFUNC) m->m_entry(m);
153 /* The following get run only in curses mode */
154 if(cur_ms != NULLMS) {
155 /* Get a menu_screen */
157 cur_ms = my_ms = make_ms(m->m_length + 2);
159 /* Now print the title and the menu */
160 (void) wmove(my_ms->ms_title, 0, MAX(0, (COLS - strlen(m->m_title))>>1));
161 (void) wstandout(my_ms->ms_title);
162 (void) waddstr(my_ms->ms_title, m->m_title);
163 (void) wstandend(my_ms->ms_title);
165 for(line = 0; line < m->m_length; line++) {
166 (void) wmove(my_ms->ms_menu, line, 0);
167 (void) wprintw(my_ms->ms_menu, "%2d. (%-12s) %s.", line+1,
168 m->m_lines[line].ml_command,
169 m->m_lines[line].ml_doc);
171 (void) wmove(my_ms->ms_menu, line++, 0);
172 (void) waddstr(my_ms->ms_menu, " r. (return ) Return to previous menu.");
173 (void) wmove(my_ms->ms_menu, line, 0);
174 (void) waddstr(my_ms->ms_menu, " q. (quit ) Quit.");
179 /* This will be set by a return val from func or submenu */
180 quitflag = DM_NORMAL;
181 /* This is here because we may be coming from another menu */
182 if(cur_ms != NULL) touchwin(my_ms->ms_screen);
184 Prompt_input("Command: ", buf, sizeof(buf));
185 /* Parse it into the argument list */
186 /* If there's nothing there, try again */
187 /* Initialize argv */
188 for(argc = 0; argc < MAX_ARGC; argc++) argv[argc] = argvals[argc];
190 if((argc = Parse_words(buf, argv, MAX_ARGLEN)) == 0) continue;
191 if((line = atoi(argv[0])) > 0 && line <= m->m_length) {
192 command = &m->m_lines[line-1];
193 } else if(!strcmp(argv[0], "r")
194 || !strcmp(argv[0], "q")
195 || !strcmp(argv[0], "return")
196 || !strcmp(argv[0], "quit")) {
197 /* here if it's either return or quit */
198 if(cur_ms != NULLMS) {
202 if(m->m_exit != NULLFUNC) m->m_exit(m);
203 return(*argv[0] == 'r' ? DM_NORMAL : DM_QUIT);
204 /* finally, try to find it using Find_command */
205 } else if ((command = Find_command (argvals[0])) ==
206 (struct menu_line *) 0) {
207 Put_message("Command not recognized");
210 /* If we got to here, command is a valid menu_line */
211 /* Send the offical command name into the argv */
212 (void) strcpy(argvals[0], command->ml_command);
213 /* Show that we're working on it */
214 Put_message(command->ml_doc);
215 /* Print args that we've already got */
216 for(i = 1; i < argc; i++) {
217 if(command->ml_args[i].ma_prompt == NULL) break;
218 (void) sprintf(buf, "%s%s", command->ml_args[i].ma_prompt,
222 /* Get remaining arguments, if any */
223 for(; argc < command->ml_argc; argc++) {
224 Prompt_input(command->ml_args[argc].ma_prompt,
225 argvals[argc], sizeof(argvals[argc]));
227 if(command->ml_function != NULLFUNC) {
228 /* If it's got a function, call it */
229 quitflag = command->ml_function(argc, argv);
230 } else if(command->ml_submenu != NULLMENU) {
231 /* Else see if it is a submenu */
232 quitflag = Do_menu(command->ml_submenu);
234 /* If it's got neither, something is wrong */
235 Put_message("*INTERNAL ERROR: NO FUNCTION OR MENU FOR LINE*");
237 if(quitflag == DM_QUIT) {
238 if(cur_ms != NULLMS) {
242 if(m->m_exit != NULLFUNC) m->m_exit(m);
248 /* Prompt the user for input in the input window of cur_ms */
249 Prompt_input(prompt, buf, buflen)
258 if(cur_ms != NULLMS) {
259 (void) waddstr(cur_ms->ms_input, prompt);
260 getyx(cur_ms->ms_input, y, x);
262 for (p = buf; p - buf < buflen; ) {
263 (void) wmove(cur_ms->ms_input, y, x);
264 (void) wclrtoeol(cur_ms->ms_input);
269 touchwin(cur_ms->ms_screen);
271 case '\n': case '\r':
272 (void) waddch(cur_ms->ms_input, '\n');
283 case 'U' & 037: case '\007': case '\033':
287 (void) waddch(cur_ms->ms_input, c);
294 printf("%s", prompt);
304 /* This routine will cause the most recently put message to be the
305 one at the top of the screen when a ---More--- prompt is displayed */
308 if(cur_ms != NULLMS) {
309 lines_left = LINES - cur_ms->ms_input_y - 1;
315 /* Turn off paging */
321 /* Print a message in the input window of cur_ms */
327 if(lines_left >= 0) {
328 if(--lines_left == 0) {
329 /* Give the user a more prompt */
330 if(cur_ms != NULLMS) {
331 (void) wstandout(cur_ms->ms_input);
332 (void) wprintw(cur_ms->ms_input, "---More---");
333 (void) wstandend(cur_ms->ms_input);
335 (void) getchar(); /* We don't care what it is */
336 getyx(cur_ms->ms_input, y, x);
337 (void) wmove(cur_ms->ms_input, y, 0);
338 (void) wclrtoeol(cur_ms->ms_input);
340 printf("---More (hit return)---");
343 Start_paging(); /* Reset lines_left */
347 if(cur_ms != NULLMS) {
348 (void) wprintw(cur_ms->ms_input, "%s\n", msg);
349 /* refresh_ms(cur_ms); */
355 /* Refresh a menu_screen onto the real screen */
357 struct menu_screen *ms;
361 getyx(ms->ms_input, y, x);
362 (void) wmove(ms->ms_screen, y + ms->ms_input_y, x);
363 (void) wrefresh(ms->ms_screen);
366 /* Parse buf into a list of words, which will be placed in strings specified by
367 argv. Space for these strings must have already been allocated.
368 Only the first n characters of each word will be copied */
369 Parse_words(buf, argv, n)
374 char *start, *end; /* For sausage machine */
378 for(argc = 0; argc < MAX_ARGC; argc++) {
379 while(isspace(*start)) start++; /* Kill whitespace */
380 if(*start == '\0') break; /* Nothing left */
381 /* Now find the end of the word */
382 for(end = start; *end != '\0' && !isspace(*end); end++);
383 (void) strncpy(argv[argc], start, MIN(end - start, n)); /* Copy it */
384 argv[argc][MIN(end - start, n-1)] = '\0'; /* Terminate */
390 /* This is the internal form of Find_command, which recursively searches
391 for a menu_line with command command in the specified menu */
392 /* It will search to a maximum depth of d */
393 struct menu_line *find_command_from(c, m, d)
399 struct menu_line *maybe;
401 if(d < 0) return((struct menu_line *) 0); /* Too deep! */
402 for(line = 0; line < m->m_length; line++) {
403 if(!strcmp(c, m->m_lines[line].ml_command)) {
404 return(&m->m_lines[line]);
405 } else if(m->m_lines[line].ml_submenu != NULLMENU
407 find_command_from(c, m->m_lines[line].ml_submenu, d-1))
408 != (struct menu_line *) 0) {
412 /* If we got to here, nothing works */
413 return((struct menu_line *) 0);
416 /* Find_command searches down the current menu tree */
417 /* And returns a pointer to a menu_line with the specified command name */
418 /* It returns (struct menu_line *) 0 if none is found */
419 struct menu_line *Find_command(command)
422 if(top_menu == NULLMENU) {
423 return((struct menu_line *) 0);
425 return(find_command_from(command, top_menu, MAX_MENU_DEPTH));
433 * c-continued-statement-offset: 4
435 * c-argdecl-indent: 4