]> andersk Git - moira.git/blame - clients/moira/menu.c
[changes from poto]
[moira.git] / clients / moira / menu.c
CommitLineData
07c56447 1/*
2aaf83c2 2 * Copyright 1987 by the Massachusetts Institute of Technology.
3 * For copying and distribution information, see the file
4 * "mit-copyright.h".
5 *
6 * $Source$
7 * $Author$
8 * $Header$
9 * $Log$
f659480d 10 * Revision 1.8 1987-08-21 22:52:37 wesommer
11 * [changes from poto]
967b7c08 12 *
f659480d 13 * Revision 1.7 87/08/17 14:23:05 jtkohl
14 * add Password_input
15 *
27de3061 16 * Revision 1.6 87/08/17 11:55:23 jtkohl
17 * changes made by poto. Looks like cleanup
18 *
967b7c08 19 * Revision 1.5 87/08/07 18:09:46 poto
74e7b641 20 * will not enter menu if ->m_entry returns DM_QUIT;
21 * the command args from a submenu command will be passed on to ->m_entry();
967b7c08 22 *
74e7b641 23 * Revision 1.4 87/08/05 14:48:04 ambar
e9db439c 24 * added latest set of hackery, to fix missing
25 * newlines, and not being able to quit out of
26 * the pager.
74e7b641 27 *
e9db439c 28 * Revision 1.3 87/08/03 05:10:34 wesommer
29 * This one appears to work.
30 *
33a6b7f2 31 * Revision 1.2 87/08/03 04:16:51 wesommer
32 * Here's another, which is probably better.
33 *
2aaf83c2 34 * Revision 1.1 87/07/31 18:02:23 ambar
35 * Initial revision
36 *
37 *
07c56447 38 * Generic menu system module.
39 *
40 * Basically, we define an enormous tree structure which represents the
41 * menu. Some extra pieces (ml_command, ma_doc) get thrown in so we can
42 * also use the structure for a command-based system.
43 *
44 * By making the menu descriptions so general, we can ease porting to just
45 * about anything.
46 */
47
2aaf83c2 48#ifndef lint
49static char rcsid_menu_c[] = "$Header$";
e9db439c 50
2aaf83c2 51#endif lint
52
07c56447 53#include <stdio.h>
967b7c08 54#include <signal.h>
07c56447 55#include <curses.h>
2aaf83c2 56#include <ctype.h>
07c56447 57#include "menu.h"
58
967b7c08 59#define MAX(A,B) ((A) > (B) ? (A) : (B))
60#define MIN(A,B) ((A) < (B) ? (A) : (B))
61#define CTL(ch) ((ch) & 037)
07c56447 62
63#define MIN_INPUT 2 /* Minimum number of lines for input window */
64
2aaf83c2 65char *strcpy();
66char *strncpy();
e9db439c 67int more_flg = 1;
2aaf83c2 68
07c56447 69/* Structure for holding current displayed menu */
70struct menu_screen {
71 WINDOW *ms_screen; /* Window for this menu */
72 WINDOW *ms_title; /* Title subwindow */
73 WINDOW *ms_menu; /* Menu subwindow */
74 WINDOW *ms_input; /* Input subwindow */
75 int ms_input_y; /* Input subwindow reference coordinate */
e9db439c 76} *cur_ms;
07c56447 77
2aaf83c2 78#define NULLMS ((struct menu_screen *) 0)
79
80Menu *top_menu; /* Root for command search */
81
07c56447 82/*
83 * Start_menu takes a menu as an argument. It initializes curses u.s.w.,
84 * and a quit in any submenu should unwind back to here. (it might not,
85 * if user functions which run their own menus don't cooperate.)
86 * Start_menu should only be called once, at the start of the program.
87 */
88Start_menu(m)
e9db439c 89 Menu *m;
07c56447 90{
2aaf83c2 91 struct menu_screen *make_ms();
92
e9db439c 93 if (initscr() == ERR) {
07c56447 94 fputs("Can't initialize curses!\n", stderr);
2aaf83c2 95 Start_no_menu(m);
07c56447 96 }
97 raw(); /* We parse & print everything ourselves */
98 noecho();
967b7c08 99 cur_ms = make_ms(0); /* So we always have some current */
100 /* menu_screen */
2aaf83c2 101 top_menu = m;
74e7b641 102 /* Run the menu */
967b7c08 103 (void) Do_menu(m, -1, (char **) NULL);
104 Cleanup_menu();
105}
106
107Cleanup_menu()
108{
109 if (cur_ms) {
110 wclear(cur_ms->ms_screen);
111 wrefresh(cur_ms->ms_screen);
112 }
07c56447 113 endwin();
114}
967b7c08 115
07c56447 116
2aaf83c2 117/* Like Start_menu, except it doesn't print menus and doesn't use curses */
118Start_no_menu(m)
e9db439c 119 Menu *m;
2aaf83c2 120{
121 cur_ms = NULLMS;
122 top_menu = m;
74e7b641 123 /* Run the menu */
967b7c08 124 (void) Do_menu(m, -1, (char **) NULL);
2aaf83c2 125}
126
07c56447 127/*
128 * Create a new menu screen template with the specified menu length
129 * and return it.
130 */
e9db439c 131struct menu_screen *
132make_ms(length)
133 int length;
07c56447 134{
135 struct menu_screen *ms;
136 char *malloc();
137
e9db439c 138 if (MAX_TITLE + length + MIN_INPUT > LINES) {
07c56447 139 fputs("Menu too big!\n", stderr);
140 exit(2);
141 }
142
143 ms = (struct menu_screen *) malloc(sizeof(struct menu_screen));
144
145 ms->ms_screen = newwin(0, 0, 0, 0);
146 ms->ms_title = subwin(ms->ms_screen, MAX_TITLE, 0, 0, 0);
147 ms->ms_menu = subwin(ms->ms_screen,
148 length, 0, MAX_TITLE, 0);
149 ms->ms_input = subwin(ms->ms_screen, 0, 0,
150 ms->ms_input_y = MAX_TITLE + length,
151 0);
152
153 scrollok(ms->ms_input, TRUE);
2aaf83c2 154 (void) wmove(ms->ms_input, 0, 0);
155 (void) wclear(ms->ms_screen);
07c56447 156
e9db439c 157 return (ms);
07c56447 158}
159
160/*
161 * This routine destroys a menu_screen.
162 */
163destroy_ms(ms)
e9db439c 164 struct menu_screen *ms;
07c56447 165{
166 delwin(ms->ms_title);
167 delwin(ms->ms_menu);
168 delwin(ms->ms_input);
169 delwin(ms->ms_screen);
e9db439c 170 free((char *) ms);
07c56447 171}
172
173/*
174 * This guy actually puts up the menu
967b7c08 175 * Note: if margc < 0, no 'r' option will be displayed (i.e., on the
176 * top level menu)
07c56447 177 */
e9db439c 178int
74e7b641 179Do_menu(m, margc, margv)
e9db439c 180 Menu *m;
74e7b641 181 int margc;
182 char *margv[];
07c56447 183{
184 struct menu_screen *my_ms, *old_cur_ms;
185 char argvals[MAX_ARGC][MAX_ARGLEN]; /* This is where args are stored */
2aaf83c2 186 char buf[MAX_ARGC * MAX_ARGLEN];
07c56447 187 char *argv[MAX_ARGC];
188 int line;
2aaf83c2 189 int i;
190 struct menu_line *command, *Find_command();
07c56447 191 int argc;
967b7c08 192 int quitflag, is_topmenu = (margc < 0);
193
07c56447 194 /* Entry function gets called with old menu_screen still current */
e9db439c 195 if (m->m_entry != NULLFUNC)
74e7b641 196 if (m->m_entry(m, margc, margv) == DM_QUIT)
197 return DM_NORMAL;
07c56447 198
2aaf83c2 199 /* The following get run only in curses mode */
e9db439c 200 if (cur_ms != NULLMS) {
2aaf83c2 201 /* Get a menu_screen */
202 old_cur_ms = cur_ms;
967b7c08 203 cur_ms = my_ms = make_ms(m->m_length + 1 + (is_topmenu?0:1));
07c56447 204
2aaf83c2 205 /* Now print the title and the menu */
e9db439c 206 (void) wclear(my_ms->ms_menu);
207 (void) wmove(my_ms->ms_title, 0, MAX(0, (COLS -
208 strlen(m->m_title)) >> 1));
2aaf83c2 209 (void) wstandout(my_ms->ms_title);
210 (void) waddstr(my_ms->ms_title, m->m_title);
211 (void) wstandend(my_ms->ms_title);
212
e9db439c 213 for (line = 0; line < m->m_length; line++) {
2aaf83c2 214 (void) wmove(my_ms->ms_menu, line, 0);
e9db439c 215 (void) wprintw(my_ms->ms_menu, "%2d. (%-12s) %s.", line + 1,
216 m->m_lines[line].ml_command,
217 m->m_lines[line].ml_doc);
2aaf83c2 218 }
219 (void) wmove(my_ms->ms_menu, line++, 0);
967b7c08 220 if (!is_topmenu) {
221 (void) waddstr(my_ms->ms_menu,
222 " r. (return ) Return to previous menu.");
223 (void) wmove(my_ms->ms_menu, line, 0);
224 }
2aaf83c2 225 (void) waddstr(my_ms->ms_menu, " q. (quit ) Quit.");
07c56447 226
07c56447 227 }
07c56447 228
e9db439c 229 for (;;) {
07c56447 230 /* This will be set by a return val from func or submenu */
231 quitflag = DM_NORMAL;
2aaf83c2 232 /* This is here because we may be coming from another menu */
e9db439c 233 if (cur_ms != NULL)
234 touchwin(my_ms->ms_screen);
07c56447 235 /* Get a command */
967b7c08 236 if (!Prompt_input("Command: ", buf, sizeof(buf)))
237 continue;
2aaf83c2 238 /* Parse it into the argument list */
239 /* If there's nothing there, try again */
240 /* Initialize argv */
e9db439c 241 for (argc = 0; argc < MAX_ARGC; argc++)
242 argv[argc] = argvals[argc];
243
244 if ((argc = Parse_words(buf, argv, MAX_ARGLEN)) == 0)
245 continue;
246 if ((line = atoi(argv[0])) > 0 && line <= m->m_length) {
247 command = &m->m_lines[line - 1];
248 }
967b7c08 249 else if ((!is_topmenu &&
250 (!strcmp(argv[0], "r")
251 || !strcmp(argv[0], "return")))
e9db439c 252 || !strcmp(argv[0], "q")
e9db439c 253 || !strcmp(argv[0], "quit")) {
07c56447 254 /* here if it's either return or quit */
e9db439c 255 if (cur_ms != NULLMS) {
2aaf83c2 256 cur_ms = old_cur_ms;
257 destroy_ms(my_ms);
258 }
e9db439c 259 if (m->m_exit != NULLFUNC)
260 m->m_exit(m);
261 return (*argv[0] == 'r' ? DM_NORMAL : DM_QUIT);
262 /* finally, try to find it using Find_command */
263 }
264 else if ((command = Find_command(argvals[0])) ==
265 (struct menu_line *) 0) {
266 Put_message("Command not recognized");
267 continue;
268 }
2aaf83c2 269 /* If we got to here, command is a valid menu_line */
270 /* Send the offical command name into the argv */
271 (void) strcpy(argvals[0], command->ml_command);
272 /* Show that we're working on it */
273 Put_message(command->ml_doc);
274 /* Print args that we've already got */
e9db439c 275 for (i = 1; i < argc; i++) {
276 if (command->ml_args[i].ma_prompt == NULL)
277 break;
2aaf83c2 278 (void) sprintf(buf, "%s%s", command->ml_args[i].ma_prompt,
279 argv[i]);
280 Put_message(buf);
281 }
282 /* Get remaining arguments, if any */
e9db439c 283 for (; argc < command->ml_argc; argc++) {
967b7c08 284 if (!Prompt_input(command->ml_args[argc].ma_prompt,
285 argvals[argc], sizeof(argvals[argc])))
286 goto punt_command;
2aaf83c2 287 }
e9db439c 288 if (command->ml_function != NULLFUNC) {
2aaf83c2 289 /* If it's got a function, call it */
290 quitflag = command->ml_function(argc, argv);
e9db439c 291 }
292 else if (command->ml_submenu != NULLMENU) {
2aaf83c2 293 /* Else see if it is a submenu */
74e7b641 294 quitflag = Do_menu(command->ml_submenu, argc, argv);
e9db439c 295 }
296 else {
2aaf83c2 297 /* If it's got neither, something is wrong */
298 Put_message("*INTERNAL ERROR: NO FUNCTION OR MENU FOR LINE*");
07c56447 299 }
e9db439c 300 if (quitflag == DM_QUIT) {
301 if (cur_ms != NULLMS) {
2aaf83c2 302 cur_ms = old_cur_ms;
303 destroy_ms(my_ms);
304 }
e9db439c 305 if (m->m_exit != NULLFUNC)
306 m->m_exit(m);
307 return (DM_QUIT);
07c56447 308 }
967b7c08 309 punt_command:
310 ;
07c56447 311 }
312}
313
314/* Prompt the user for input in the input window of cur_ms */
967b7c08 315int Prompt_input(prompt, buf, buflen)
e9db439c 316 char *prompt;
317 char *buf;
318 int buflen;
07c56447 319{
320 int c;
321 char *p;
322 int y, x, oldx;
323
e9db439c 324 if (cur_ms != NULLMS) {
325 more_flg = 1;
326 getyx(cur_ms->ms_input, y, x);
327 (void) wmove(cur_ms->ms_input, y, 0);
328
329 touchwin(cur_ms->ms_screen);
330 refresh_ms(cur_ms);
2aaf83c2 331 (void) waddstr(cur_ms->ms_input, prompt);
332 getyx(cur_ms->ms_input, y, x);
e9db439c 333
2aaf83c2 334 oldx = x;
f659480d 335 p = buf;
336 while(1) {
337/* for (p = buf; p - buf < buflen;) { */
2aaf83c2 338 (void) wmove(cur_ms->ms_input, y, x);
339 (void) wclrtoeol(cur_ms->ms_input);
340 refresh_ms(cur_ms);
341 c = getchar();
342 switch (c) {
967b7c08 343 case CTL('C'):
344 return 0;
345 case CTL('Z'):
346 kill(getpid(), SIGTSTP);
347 touchwin(curscr);
348 break;
349 case CTL('L'):
e9db439c 350 (void) wclear(cur_ms->ms_input);
351 (void) waddstr(cur_ms->ms_input, prompt);
967b7c08 352 wrefresh(curscr);
e9db439c 353 getyx(cur_ms->ms_input, y, x);
2aaf83c2 354 break;
e9db439c 355 case '\n':
356 case '\r':
f659480d 357 goto end_input;
2aaf83c2 358 case '\b':
359 case '\177':
360 if (p > buf) {
361 p--;
362 x--;
363 }
364 break;
967b7c08 365 case CTL('U'):
366 case CTL('G'):
367 case CTL('['):
2aaf83c2 368 x = oldx;
e9db439c 369 p = buf;
2aaf83c2 370 break;
371 default:
f659480d 372 if (isprint(c) && (p - buf < buflen)) {
967b7c08 373 (void) waddch(cur_ms->ms_input, c);
374 *p++ = c;
375 x++;
376 } else
377 putchar(CTL('G'));
2aaf83c2 378 break;
07c56447 379 }
07c56447 380 }
f659480d 381 end_input:
382 (void) waddch(cur_ms->ms_input, '\n');
383 (void) waddch(cur_ms->ms_input, '\r');
384
385 (void) wclrtoeol(cur_ms->ms_input);
386 refresh_ms(cur_ms);
387 *p = '\0';
388 Start_paging();
389 return 1;
390 } else {
2aaf83c2 391 printf("%s", prompt);
967b7c08 392 if (gets(buf) == NULL)
393 return 0;
2aaf83c2 394 Start_paging();
967b7c08 395 return 1;
07c56447 396 }
397}
398
27de3061 399/* Prompt the user for input in the input window of cur_ms, but don't echo
400 and allow some control characters */
401int Password_input(prompt, buf, buflen)
402 char *prompt;
403 char *buf;
404 int buflen;
405{
406 int c;
407 char *p;
408 int y, x, oldx;
409
410 if (cur_ms != NULLMS) {
411 more_flg = 1;
412 getyx(cur_ms->ms_input, y, x);
413 (void) wmove(cur_ms->ms_input, y, 0);
414
415 touchwin(cur_ms->ms_screen);
416 refresh_ms(cur_ms);
417 (void) waddstr(cur_ms->ms_input, prompt);
418 getyx(cur_ms->ms_input, y, x);
419
420 oldx = x;
421 for (p = buf; p - buf < buflen;) {
422 (void) wmove(cur_ms->ms_input, y, x);
423 (void) wclrtoeol(cur_ms->ms_input);
424 refresh_ms(cur_ms);
425 c = getchar();
426 switch (c) {
427 case CTL('C'):
428 return 0;
429 case CTL('Z'):
430 kill(getpid(), SIGTSTP);
431 touchwin(curscr);
432 break;
433 case CTL('L'):
434 (void) wclear(cur_ms->ms_input);
435 (void) waddstr(cur_ms->ms_input, prompt);
436 wrefresh(curscr);
437 getyx(cur_ms->ms_input, y, x);
438 break;
439 case '\n':
440 case '\r':
441 (void) waddch(cur_ms->ms_input, '\n');
442 (void) waddch(cur_ms->ms_input, '\r');
443
444 (void) wclrtoeol(cur_ms->ms_input);
445 refresh_ms(cur_ms);
446 *p = '\0';
447 Start_paging();
448 return 1;
449 case '\b':
450 case '\177':
451 if (p > buf) {
452 p--;
453 x--;
454 }
455 break;
456 case CTL('U'):
457 x = oldx;
458 p = buf;
459 break;
460 default:
461 *p++ = c;
462 break;
463 }
464 }
465 }
466 else {
467 struct sgttyb ttybuf, nttybuf;
468 printf("%s", prompt);
469 /* turn off echoing */
470 (void) ioctl(0, TIOCGETP, &ttybuf);
471 nttybuf = ttybuf;
472 nttybuf.sg_flags &= ~ECHO;
473 (void)ioctl(0, TIOCSETP, &nttybuf);
474 if (gets(buf) == NULL) {
475 (void) ioctl(0, TIOCSETP, &ttybuf);
476 putchar('\n');
477 return 0;
478 }
479 putchar('\n');
480 (void) ioctl(0, TIOCSETP, &ttybuf);
481 Start_paging();
482 return 1;
483 }
484}
485
2aaf83c2 486int lines_left;
487
488/* Start paging */
489/* This routine will cause the most recently put message to be the
490 one at the top of the screen when a ---More--- prompt is displayed */
491Start_paging()
492{
e9db439c 493 if (cur_ms != NULLMS) {
2aaf83c2 494 lines_left = LINES - cur_ms->ms_input_y - 1;
e9db439c 495 }
496 else {
2aaf83c2 497 lines_left = 23;
498 }
499}
500
501/* Turn off paging */
502Stop_paging()
503{
504 lines_left = -1;
505}
506
f659480d 507/* Print a message in the input window of cur_ms. */
07c56447 508Put_message(msg)
f659480d 509char *msg;
510{
511 char *copy, *line, *s;
512
513 copy = (char *) malloc(COLS);
514 s = line = msg;
515 while(*s++) {
516 if (s - line >= COLS-1) {
517 strncpy(copy, line, COLS-1);
518 line += COLS-1;
519 } else if (*s == '\n') {
520 *s = '\0';
521 strcpy(copy, line);
522 line = ++s;
523 } else
524 continue;
525 Put_line(copy);
526 }
527 Put_line(line);
528 free(copy);
529}
530
531/* Will be truncated to COLS characters. */
532Put_line(msg)
533char *msg;
07c56447 534{
e9db439c 535 int y, x, i;
536 char *msg1, chr;
537
538 if (!more_flg)
539 return;
2aaf83c2 540
e9db439c 541 if (lines_left >= 0) {
542 if (--lines_left == 0) {
2aaf83c2 543 /* Give the user a more prompt */
e9db439c 544 if (cur_ms != NULLMS) {
2aaf83c2 545 (void) wstandout(cur_ms->ms_input);
546 (void) wprintw(cur_ms->ms_input, "---More---");
547 (void) wstandend(cur_ms->ms_input);
548 refresh_ms(cur_ms);
e9db439c 549 chr = getchar();/* We do care what it is */
550 if (chr == 'q' || chr == 'Q') {
551 more_flg = 0;
552 return;
553 }
2aaf83c2 554 getyx(cur_ms->ms_input, y, x);
555 (void) wmove(cur_ms->ms_input, y, 0);
556 (void) wclrtoeol(cur_ms->ms_input);
e9db439c 557 }
558 else {
2aaf83c2 559 printf("---More (hit return)---");
560 (void) getchar();
561 }
562 Start_paging(); /* Reset lines_left */
563 }
564 }
e9db439c 565
566 if (cur_ms != NULLMS) {
567 msg1 = (char *) calloc(COLS, 1);
f659480d 568 (void) strncpy(msg1, msg, COLS-1);
569 for (i = strlen(msg1); i < COLS - 1; i++)
e9db439c 570 msg1[i] = ' ';
571 (void) wprintw(cur_ms->ms_input, "%s\n", msg1);
2aaf83c2 572/* refresh_ms(cur_ms); */
e9db439c 573 }
574 else {
2aaf83c2 575 puts(msg);
576 }
07c56447 577}
578
579/* Refresh a menu_screen onto the real screen */
580refresh_ms(ms)
e9db439c 581 struct menu_screen *ms;
07c56447 582{
583 int y, x;
584
585 getyx(ms->ms_input, y, x);
2aaf83c2 586 (void) wmove(ms->ms_screen, y + ms->ms_input_y, x);
587 (void) wrefresh(ms->ms_screen);
588}
589
590/* Parse buf into a list of words, which will be placed in strings specified by
591 argv. Space for these strings must have already been allocated.
592 Only the first n characters of each word will be copied */
593Parse_words(buf, argv, n)
e9db439c 594 char *buf;
595 char *argv[];
2aaf83c2 596int n;
e9db439c 597
2aaf83c2 598{
599 char *start, *end; /* For sausage machine */
600 int argc;
601
602 start = buf;
e9db439c 603 for (argc = 0; argc < MAX_ARGC; argc++) {
604 while (isspace(*start))
605 start++; /* Kill whitespace */
606 if (*start == '\0')
607 break; /* Nothing left */
2aaf83c2 608 /* Now find the end of the word */
e9db439c 609 for (end = start; *end != '\0' && !isspace(*end); end++);
610 (void) strncpy(argv[argc], start, MIN(end - start, n)); /* Copy it */
611 argv[argc][MIN(end - start, n - 1)] = '\0'; /* Terminate */
2aaf83c2 612 start = end;
613 }
e9db439c 614 return (argc);
2aaf83c2 615}
616
617/* This is the internal form of Find_command, which recursively searches
618 for a menu_line with command command in the specified menu */
619/* It will search to a maximum depth of d */
e9db439c 620struct menu_line *
621find_command_from(c, m, d)
622 char *c;
623 struct menu *m;
624 int d;
2aaf83c2 625{
626 int line;
627 struct menu_line *maybe;
628
e9db439c 629 if (d < 0)
630 return ((struct menu_line *) 0); /* Too deep! */
631 for (line = 0; line < m->m_length; line++) {
632 if (!strcmp(c, m->m_lines[line].ml_command)) {
633 return (&m->m_lines[line]);
634 }
635 else if (m->m_lines[line].ml_submenu != NULLMENU
636 && (maybe =
637 find_command_from(c, m->m_lines[line].ml_submenu, d - 1))
638 != (struct menu_line *) 0) {
639 return (maybe);
640 }
2aaf83c2 641 }
642 /* If we got to here, nothing works */
e9db439c 643 return ((struct menu_line *) 0);
07c56447 644}
2aaf83c2 645
646/* Find_command searches down the current menu tree */
647/* And returns a pointer to a menu_line with the specified command name */
648/* It returns (struct menu_line *) 0 if none is found */
e9db439c 649struct menu_line *
650Find_command(command)
651 char *command;
2aaf83c2 652{
e9db439c 653 if (top_menu == NULLMENU) {
654 return ((struct menu_line *) 0);
655 }
656 else {
657 return (find_command_from(command, top_menu, MAX_MENU_DEPTH));
2aaf83c2 658 }
659}
This page took 0.246096 seconds and 5 git commands to generate.