2 * Copyright 1987 by the Massachusetts Institute of Technology.
3 * For copying and distribution information, see the file
10 * Revision 1.3 1987-08-03 05:10:34 wesommer
11 * This one appears to work.
13 * Revision 1.2 87/08/03 04:16:51 wesommer
14 * Here's another, which is probably better.
16 * Revision 1.1 87/07/31 18:02:23 ambar
20 * Generic menu system module.
22 * Basically, we define an enormous tree structure which represents the
23 * menu. Some extra pieces (ml_command, ma_doc) get thrown in so we can
24 * also use the structure for a command-based system.
26 * By making the menu descriptions so general, we can ease porting to just
31 static char rcsid_menu_c[] = "$Header$";
39 #define MAX(A,B) ((A) > (B) ? (A) : (B))
40 #define MIN(A,B) ((A) < (B) ? (A) : (B))
42 #define MIN_INPUT 2 /* Minimum number of lines for input window */
47 /* Structure for holding current displayed menu */
49 WINDOW *ms_screen; /* Window for this menu */
50 WINDOW *ms_title; /* Title subwindow */
51 WINDOW *ms_menu; /* Menu subwindow */
52 WINDOW *ms_input; /* Input subwindow */
53 int ms_input_y; /* Input subwindow reference coordinate */
56 #define NULLMS ((struct menu_screen *) 0)
58 Menu *top_menu; /* Root for command search */
61 * Start_menu takes a menu as an argument. It initializes curses u.s.w.,
62 * and a quit in any submenu should unwind back to here. (it might not,
63 * if user functions which run their own menus don't cooperate.)
64 * Start_menu should only be called once, at the start of the program.
69 struct menu_screen *make_ms();
71 if(initscr() == ERR) {
72 fputs("Can't initialize curses!\n", stderr);
75 raw(); /* We parse & print everything ourselves */
77 cur_ms = make_ms(0); /* So we always have some current menu_screen */
79 (void) Do_menu(m); /* Run the menu */
83 /* Like Start_menu, except it doesn't print menus and doesn't use curses */
93 * Create a new menu screen template with the specified menu length
96 struct menu_screen *make_ms(length)
99 struct menu_screen *ms;
102 if(MAX_TITLE + length + MIN_INPUT > LINES) {
103 fputs("Menu too big!\n", stderr);
107 ms = (struct menu_screen *) malloc(sizeof(struct menu_screen));
109 ms->ms_screen = newwin(0, 0, 0, 0);
110 ms->ms_title = subwin(ms->ms_screen, MAX_TITLE, 0, 0, 0);
111 ms->ms_menu = subwin(ms->ms_screen,
112 length, 0, MAX_TITLE, 0);
113 ms->ms_input = subwin(ms->ms_screen, 0, 0,
114 ms->ms_input_y = MAX_TITLE + length,
117 scrollok(ms->ms_input, TRUE);
118 (void) wmove(ms->ms_input, 0, 0);
119 (void) wclear(ms->ms_screen);
125 * This routine destroys a menu_screen.
128 struct menu_screen *ms;
130 delwin(ms->ms_title);
132 delwin(ms->ms_input);
133 delwin(ms->ms_screen);
138 * This guy actually puts up the menu
143 struct menu_screen *my_ms, *old_cur_ms;
144 char argvals[MAX_ARGC][MAX_ARGLEN]; /* This is where args are stored */
145 char buf[MAX_ARGC * MAX_ARGLEN];
146 char *argv[MAX_ARGC];
149 struct menu_line *command, *Find_command();
153 /* Entry function gets called with old menu_screen still current */
154 if(m->m_entry != NULLFUNC) m->m_entry(m);
156 /* The following get run only in curses mode */
157 if(cur_ms != NULLMS) {
158 /* Get a menu_screen */
160 cur_ms = my_ms = make_ms(m->m_length + 2);
162 /* Now print the title and the menu */
163 (void) wmove(my_ms->ms_title, 0, MAX(0, (COLS - strlen(m->m_title))>>1));
164 (void) wstandout(my_ms->ms_title);
165 (void) waddstr(my_ms->ms_title, m->m_title);
166 (void) wstandend(my_ms->ms_title);
168 for(line = 0; line < m->m_length; line++) {
169 (void) wmove(my_ms->ms_menu, line, 0);
170 (void) wprintw(my_ms->ms_menu, "%2d. (%-12s) %s.", line+1,
171 m->m_lines[line].ml_command,
172 m->m_lines[line].ml_doc);
174 (void) wmove(my_ms->ms_menu, line++, 0);
175 (void) waddstr(my_ms->ms_menu, " r. (return ) Return to previous menu.");
176 (void) wmove(my_ms->ms_menu, line, 0);
177 (void) waddstr(my_ms->ms_menu, " q. (quit ) Quit.");
182 /* This will be set by a return val from func or submenu */
183 quitflag = DM_NORMAL;
184 /* This is here because we may be coming from another menu */
185 if(cur_ms != NULL) touchwin(my_ms->ms_screen);
187 Prompt_input("Command: ", buf, sizeof(buf));
188 /* Parse it into the argument list */
189 /* If there's nothing there, try again */
190 /* Initialize argv */
191 for(argc = 0; argc < MAX_ARGC; argc++) argv[argc] = argvals[argc];
193 if((argc = Parse_words(buf, argv, MAX_ARGLEN)) == 0) continue;
194 if((line = atoi(argv[0])) > 0 && line <= m->m_length) {
195 command = &m->m_lines[line-1];
196 } else if(!strcmp(argv[0], "r")
197 || !strcmp(argv[0], "q")
198 || !strcmp(argv[0], "return")
199 || !strcmp(argv[0], "quit")) {
200 /* here if it's either return or quit */
201 if(cur_ms != NULLMS) {
205 if(m->m_exit != NULLFUNC) m->m_exit(m);
206 return(*argv[0] == 'r' ? DM_NORMAL : DM_QUIT);
207 /* finally, try to find it using Find_command */
208 } else if ((command = Find_command (argvals[0])) ==
209 (struct menu_line *) 0) {
210 Put_message("Command not recognized");
213 /* If we got to here, command is a valid menu_line */
214 /* Send the offical command name into the argv */
215 (void) strcpy(argvals[0], command->ml_command);
216 /* Show that we're working on it */
217 Put_message(command->ml_doc);
218 /* Print args that we've already got */
219 for(i = 1; i < argc; i++) {
220 if(command->ml_args[i].ma_prompt == NULL) break;
221 (void) sprintf(buf, "%s%s", command->ml_args[i].ma_prompt,
225 /* Get remaining arguments, if any */
226 for(; argc < command->ml_argc; argc++) {
227 Prompt_input(command->ml_args[argc].ma_prompt,
228 argvals[argc], sizeof(argvals[argc]));
230 if(command->ml_function != NULLFUNC) {
231 /* If it's got a function, call it */
232 quitflag = command->ml_function(argc, argv);
233 } else if(command->ml_submenu != NULLMENU) {
234 /* Else see if it is a submenu */
235 quitflag = Do_menu(command->ml_submenu);
237 /* If it's got neither, something is wrong */
238 Put_message("*INTERNAL ERROR: NO FUNCTION OR MENU FOR LINE*");
240 if(quitflag == DM_QUIT) {
241 if(cur_ms != NULLMS) {
245 if(m->m_exit != NULLFUNC) m->m_exit(m);
251 /* Prompt the user for input in the input window of cur_ms */
252 Prompt_input(prompt, buf, buflen)
261 if(cur_ms != NULLMS) {
262 (void) waddstr(cur_ms->ms_input, prompt);
263 getyx(cur_ms->ms_input, y, x);
265 for (p = buf; p - buf < buflen; ) {
266 (void) wmove(cur_ms->ms_input, y, x);
267 (void) wclrtoeol(cur_ms->ms_input);
272 touchwin(cur_ms->ms_screen);
274 case '\n': case '\r':
275 (void) waddch(cur_ms->ms_input, '\n');
286 case 'U' & 037: case '\007': case '\033':
290 (void) waddch(cur_ms->ms_input, c);
297 printf("%s", prompt);
307 /* This routine will cause the most recently put message to be the
308 one at the top of the screen when a ---More--- prompt is displayed */
311 if(cur_ms != NULLMS) {
312 lines_left = LINES - cur_ms->ms_input_y - 1;
318 /* Turn off paging */
324 /* Print a message in the input window of cur_ms */
330 if(lines_left >= 0) {
331 if(--lines_left == 0) {
332 /* Give the user a more prompt */
333 if(cur_ms != NULLMS) {
334 (void) wstandout(cur_ms->ms_input);
335 (void) wprintw(cur_ms->ms_input, "---More---");
336 (void) wstandend(cur_ms->ms_input);
338 (void) getchar(); /* We don't care what it is */
339 getyx(cur_ms->ms_input, y, x);
340 (void) wmove(cur_ms->ms_input, y, 0);
341 (void) wclrtoeol(cur_ms->ms_input);
343 printf("---More (hit return)---");
346 Start_paging(); /* Reset lines_left */
350 if(cur_ms != NULLMS) {
351 (void) wprintw(cur_ms->ms_input, "%s\n", msg);
352 /* refresh_ms(cur_ms); */
358 /* Refresh a menu_screen onto the real screen */
360 struct menu_screen *ms;
364 getyx(ms->ms_input, y, x);
365 (void) wmove(ms->ms_screen, y + ms->ms_input_y, x);
366 (void) wrefresh(ms->ms_screen);
369 /* Parse buf into a list of words, which will be placed in strings specified by
370 argv. Space for these strings must have already been allocated.
371 Only the first n characters of each word will be copied */
372 Parse_words(buf, argv, n)
377 char *start, *end; /* For sausage machine */
381 for(argc = 0; argc < MAX_ARGC; argc++) {
382 while(isspace(*start)) start++; /* Kill whitespace */
383 if(*start == '\0') break; /* Nothing left */
384 /* Now find the end of the word */
385 for(end = start; *end != '\0' && !isspace(*end); end++);
386 (void) strncpy(argv[argc], start, MIN(end - start, n)); /* Copy it */
387 argv[argc][MIN(end - start, n-1)] = '\0'; /* Terminate */
393 /* This is the internal form of Find_command, which recursively searches
394 for a menu_line with command command in the specified menu */
395 /* It will search to a maximum depth of d */
396 struct menu_line *find_command_from(c, m, d)
402 struct menu_line *maybe;
404 if(d < 0) return((struct menu_line *) 0); /* Too deep! */
405 for(line = 0; line < m->m_length; line++) {
406 if(!strcmp(c, m->m_lines[line].ml_command)) {
407 return(&m->m_lines[line]);
408 } else if(m->m_lines[line].ml_submenu != NULLMENU
410 find_command_from(c, m->m_lines[line].ml_submenu, d-1))
411 != (struct menu_line *) 0) {
415 /* If we got to here, nothing works */
416 return((struct menu_line *) 0);
419 /* Find_command searches down the current menu tree */
420 /* And returns a pointer to a menu_line with the specified command name */
421 /* It returns (struct menu_line *) 0 if none is found */
422 struct menu_line *Find_command(command)
425 if(top_menu == NULLMENU) {
426 return((struct menu_line *) 0);
428 return(find_command_from(command, top_menu, MAX_MENU_DEPTH));
436 * c-continued-statement-offset: 4
438 * c-argdecl-indent: 4