]> andersk Git - moira.git/blob - clients/moira/menu.c
Now passes lint with minimal difficulties.
[moira.git] / clients / moira / menu.c
1 /*
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$
10  * Revision 1.9  1987-08-21 23:31:37  wesommer
11  * Now passes lint with minimal difficulties.
12  *
13  * Revision 1.8  87/08/21  22:52:37  wesommer
14  * [changes from poto]
15  * 
16  * Revision 1.7  87/08/17  14:23:05  jtkohl
17  * add Password_input
18  * 
19  * Revision 1.6  87/08/17  11:55:23  jtkohl
20  * changes made by poto. Looks like cleanup
21  * 
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();
25  * 
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
29  * the pager.
30  * 
31  * Revision 1.3  87/08/03  05:10:34  wesommer
32  * This one appears to work.
33  * 
34  * Revision 1.2  87/08/03  04:16:51  wesommer
35  * Here's another, which is probably better.
36  * 
37  * Revision 1.1  87/07/31  18:02:23  ambar
38  * Initial revision
39  * 
40  *
41  * Generic menu system module.
42  *
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.
46  *
47  * By making the menu descriptions so general, we can ease porting to just
48  * about anything.
49  */
50
51 #ifndef lint
52 static char rcsid_menu_c[] = "$Header$";
53
54 #endif lint
55
56 #include <sys/types.h>
57 #include <stdio.h>
58 #include <signal.h>
59 #include <curses.h>
60 #include <ctype.h>
61 #include <strings.h>
62 #include "menu.h"
63
64 #define MAX(A,B)        ((A) > (B) ? (A) : (B))
65 #define MIN(A,B)        ((A) < (B) ? (A) : (B))
66 #define CTL(ch)         ((ch) & 037)
67
68 #define MIN_INPUT 2             /* Minimum number of lines for input window */
69
70 extern char *calloc();
71 int more_flg = 1;
72
73 /* Structure for holding current displayed menu */
74 struct menu_screen {
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 */
80 }          *cur_ms;
81
82 #define NULLMS ((struct menu_screen *) 0)
83
84 Menu *top_menu;                 /* Root for command search */
85
86 /*
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.
91  */
92 Start_menu(m)
93     Menu *m;
94 {
95     struct menu_screen *make_ms();
96
97     if (initscr() == ERR) {
98         fputs("Can't initialize curses!\n", stderr);
99         Start_no_menu(m);
100     }
101     (void) raw();               /* We parse & print everything ourselves */
102     (void) noecho();
103     cur_ms = make_ms(0);        /* So we always have some current */
104                                 /* menu_screen */ 
105     top_menu = m;
106     /* Run the menu */
107     (void) Do_menu(m, -1, (char **) NULL);
108     Cleanup_menu();
109 }
110
111 Cleanup_menu()
112 {
113     if (cur_ms) {
114         (void) wclear(cur_ms->ms_screen);
115         (void) wrefresh(cur_ms->ms_screen);
116     }
117     endwin();
118 }
119     
120
121 /* Like Start_menu, except it doesn't print menus and doesn't use curses */
122 Start_no_menu(m)
123     Menu *m;
124 {
125     cur_ms = NULLMS;
126     top_menu = m;
127     /* Run the menu */
128     (void) Do_menu(m, -1, (char **) NULL);
129 }
130
131 /*
132  * Create a new menu screen template with the specified menu length
133  * and return it.
134  */
135 struct menu_screen *
136 make_ms(length)
137     int length;
138 {
139     struct menu_screen *ms;
140     char *malloc();
141
142     if (MAX_TITLE + length + MIN_INPUT > LINES) {
143         fputs("Menu too big!\n", stderr);
144         exit(2);
145     }
146
147     ms = (struct menu_screen *) malloc(sizeof(struct menu_screen));
148
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,
155                           0);
156
157     scrollok(ms->ms_input, TRUE);
158     (void) wmove(ms->ms_input, 0, 0);
159     (void) wclear(ms->ms_screen);
160
161     return (ms);
162 }
163
164 /*
165  * This routine destroys a menu_screen.
166  */
167 destroy_ms(ms)
168     struct menu_screen *ms;
169 {
170     delwin(ms->ms_title);
171     delwin(ms->ms_menu);
172     delwin(ms->ms_input);
173     delwin(ms->ms_screen);
174     free((char *) ms);
175 }
176
177 /*
178  * This guy actually puts up the menu
179  * Note: if margc < 0, no 'r' option will be displayed (i.e., on the
180  * top level menu)
181  */
182 int 
183 Do_menu(m, margc, margv)
184     Menu *m;
185     int margc;
186     char *margv[];
187 {
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];
192     int line;
193     int i;
194     struct menu_line *command, *Find_command();
195     int argc;
196     int quitflag, is_topmenu = (margc < 0);
197     
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)
201             return DM_NORMAL;
202
203     /* The following get run only in curses mode */
204     if (cur_ms != NULLMS) {
205         /* Get a menu_screen */
206         old_cur_ms = cur_ms;
207         cur_ms = my_ms = make_ms(m->m_length + 1 + (is_topmenu?0:1));
208
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);
216
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);
222         }
223         (void) wmove(my_ms->ms_menu, line++, 0);
224         if (!is_topmenu) {
225             (void) waddstr(my_ms->ms_menu,
226                            " r. (return      ) Return to previous menu.");
227             (void) wmove(my_ms->ms_menu, line, 0);
228         }
229         (void) waddstr(my_ms->ms_menu, " q. (quit        ) Quit.");
230
231     }
232
233     for (;;) {
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 */
237         if (cur_ms != NULL)
238             touchwin(my_ms->ms_screen);
239         /* Get a command */
240         if (!Prompt_input("Command: ", buf, sizeof(buf)))
241             continue;
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];
247
248         if ((argc = Parse_words(buf, argv, MAX_ARGLEN)) == 0)
249             continue;
250         if ((line = atoi(argv[0])) > 0 && line <= m->m_length) {
251             command = &m->m_lines[line - 1];
252         }
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) {
260                 cur_ms = old_cur_ms;
261                 destroy_ms(my_ms);
262             }
263             if (m->m_exit != NULLFUNC)
264                 m->m_exit(m);
265             return (*argv[0] == 'r' ? DM_NORMAL : DM_QUIT);
266             /* finally, try to find it using Find_command */
267         }
268         else if ((command = Find_command(argvals[0])) ==
269                  (struct menu_line *) 0) {
270             Put_message("Command not recognized");
271             continue;
272         }
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)
281                 break;
282             (void) sprintf(buf, "%s%s", command->ml_args[i].ma_prompt,
283                            argv[i]);
284             Put_message(buf);
285         }
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])))
290                 goto punt_command;
291         }
292         if (command->ml_function != NULLFUNC) {
293             /* If it's got a function, call it */
294             quitflag = command->ml_function(argc, argv);
295         }
296         else if (command->ml_submenu != NULLMENU) {
297             /* Else see if it is a submenu */
298             quitflag = Do_menu(command->ml_submenu, argc, argv);
299         }
300         else {
301             /* If it's got neither, something is wrong */
302             Put_message("*INTERNAL ERROR: NO FUNCTION OR MENU FOR LINE*");
303         }
304         if (quitflag == DM_QUIT) {
305             if (cur_ms != NULLMS) {
306                 cur_ms = old_cur_ms;
307                 destroy_ms(my_ms);
308             }
309             if (m->m_exit != NULLFUNC)
310                 m->m_exit(m);
311             return (DM_QUIT);
312         }
313     punt_command:
314         ;
315     }
316 }
317
318 /* Prompt the user for input in the input window of cur_ms */
319 int Prompt_input(prompt, buf, buflen)
320     char *prompt;
321     char *buf;
322     int buflen;
323 {
324     int c;
325     char *p;
326     int y, x, oldx;
327
328     if (cur_ms != NULLMS) {
329         more_flg = 1;
330         getyx(cur_ms->ms_input, y, x);
331         (void) wmove(cur_ms->ms_input, y, 0);
332
333         touchwin(cur_ms->ms_screen);
334         refresh_ms(cur_ms);
335         (void) waddstr(cur_ms->ms_input, prompt);
336         getyx(cur_ms->ms_input, y, x);
337
338         oldx = x;
339         p = buf;
340         while(1) {
341 /*      for (p = buf; p - buf < buflen;) { */
342             (void) wmove(cur_ms->ms_input, y, x);
343             (void) wclrtoeol(cur_ms->ms_input);
344             refresh_ms(cur_ms);
345             c = getchar();
346             switch (c) {
347             case CTL('C'):
348                 return 0;
349             case CTL('Z'):
350                 (void) kill(getpid(), SIGTSTP);
351                 touchwin(curscr);
352                 break;
353             case CTL('L'):
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);
358                 break;
359             case '\n':
360             case '\r':
361                 goto end_input;
362             case '\b':
363             case '\177':
364                 if (p > buf) {
365                     p--;
366                     x--;
367                 }
368                 break;
369             case CTL('U'):
370             case CTL('G'):
371             case CTL('['):
372                 x = oldx;
373                 p = buf;
374                 break;
375             default:
376                 if (isprint(c) && (p - buf < buflen)) {
377                     (void) waddch(cur_ms->ms_input, c);
378                     *p++ = c;
379                     x++;
380                 } else
381                     putchar(CTL('G'));
382                 break;
383             }
384         }
385     end_input:
386         (void) waddch(cur_ms->ms_input, '\n');
387         (void) waddch(cur_ms->ms_input, '\r');
388         
389         (void) wclrtoeol(cur_ms->ms_input);
390         refresh_ms(cur_ms);
391         *p = '\0';
392         Start_paging();
393         return 1;
394     } else {
395         printf("%s", prompt);
396         if (gets(buf) == NULL)
397             return 0;
398         Start_paging();
399         return 1;
400     }
401 }
402
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)
406     char *prompt;
407     char *buf;
408     int buflen;
409 {
410     int c;
411     char *p;
412     int y, x, oldx;
413
414     if (cur_ms != NULLMS) {
415         more_flg = 1;
416         getyx(cur_ms->ms_input, y, x);
417         (void) wmove(cur_ms->ms_input, y, 0);
418
419         touchwin(cur_ms->ms_screen);
420         refresh_ms(cur_ms);
421         (void) waddstr(cur_ms->ms_input, prompt);
422         getyx(cur_ms->ms_input, y, x);
423
424         oldx = x;
425         for (p = buf; p - buf < buflen;) {
426             (void) wmove(cur_ms->ms_input, y, x);
427             (void) wclrtoeol(cur_ms->ms_input);
428             refresh_ms(cur_ms);
429             c = getchar();
430             switch (c) {
431             case CTL('C'):
432                 return 0;
433             case CTL('Z'):
434                 (void) kill(getpid(), SIGTSTP);
435                 touchwin(curscr);
436                 break;
437             case CTL('L'):
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);
442                 break;
443             case '\n':
444             case '\r':
445                 (void) waddch(cur_ms->ms_input, '\n');
446                 (void) waddch(cur_ms->ms_input, '\r');
447
448                 (void) wclrtoeol(cur_ms->ms_input);
449                 refresh_ms(cur_ms);
450                 *p = '\0';
451                 Start_paging();
452                 return 1;
453             case '\b':
454             case '\177':
455                 if (p > buf) {
456                     p--;
457                     x--;
458                 }
459                 break;
460             case CTL('U'):
461                 x = oldx;
462                 p = buf;
463                 break;
464             default:
465                 *p++ = c;
466                 break;
467             }
468         }
469     }
470     else {
471         struct sgttyb ttybuf, nttybuf;
472         printf("%s", prompt);
473         /* turn off echoing */
474         (void) ioctl(0, TIOCGETP, (char *)&ttybuf);
475         nttybuf = 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);
480             putchar('\n');
481             return 0;
482         }
483         putchar('\n');
484         (void) ioctl(0, TIOCSETP, (char *)&ttybuf);
485         Start_paging();
486         return 1;
487     }
488     return 0;
489 }
490
491 int lines_left;
492
493 /* Start paging */
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 */
496 Start_paging()
497 {
498     if (cur_ms != NULLMS) {
499         lines_left = LINES - cur_ms->ms_input_y - 1;
500     }
501     else {
502         lines_left = 23;
503     }
504 }
505
506 /* Turn off paging */
507 Stop_paging()
508 {
509     lines_left = -1;
510 }
511
512 /* Print a message in the input window of cur_ms.  */
513 Put_message(msg)
514 char *msg;
515 {
516     char *copy, *line, *s;
517
518     copy = (char *) malloc((u_int)COLS);
519     s = line = msg;
520     while(*s++) {
521         if (s - line >= COLS-1) {
522             (void) strncpy(copy, line, COLS-1);
523             line += COLS-1;
524         } else if (*s == '\n') {
525             *s = '\0';
526             (void) strcpy(copy, line);
527             line = ++s;
528         } else
529             continue;
530         Put_line(copy);
531     }
532     Put_line(line);
533     free(copy);
534 }
535
536 /* Will be truncated to COLS characters.  */
537 Put_line(msg)
538 char *msg;
539 {
540     int y, x, i;
541     char *msg1, chr;
542
543     if (!more_flg)
544         return;
545
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);
553                 refresh_ms(cur_ms);
554                 chr = getchar();/* We do care what it is */
555                 if (chr == 'q' || chr == 'Q') {
556                     more_flg = 0;
557                     return;
558                 }
559                 getyx(cur_ms->ms_input, y, x);
560                 /* x is a bitbucket; avoid lint problems */
561                 x=x;
562                 (void) wmove(cur_ms->ms_input, y, 0);
563                 (void) wclrtoeol(cur_ms->ms_input);
564             }
565             else {
566                 printf("---More (hit return)---");
567                 (void) getchar();
568             }
569             Start_paging();     /* Reset lines_left */
570         }
571     }
572
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++)
577             msg1[i] = ' ';
578         (void) wprintw(cur_ms->ms_input, "%s\n", msg1);
579 /*      refresh_ms(cur_ms); */
580     }
581     else {
582         puts(msg);
583     }
584 }
585
586 /* Refresh a menu_screen onto the real screen */
587 refresh_ms(ms)
588     struct menu_screen *ms;
589 {
590     int y, x;
591
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);
595 }
596
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)
601     char *buf;
602     char *argv[];
603 int n;
604
605 {
606     char *start, *end;          /* For sausage machine */
607     int argc;
608
609     start = buf;
610     for (argc = 0; argc < MAX_ARGC; argc++) {
611         while (isspace(*start))
612             start++;            /* Kill whitespace */
613         if (*start == '\0')
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 */
619         start = end;
620     }
621     return (argc);
622 }
623
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 */
627 struct menu_line *
628 find_command_from(c, m, d)
629     char *c;
630     struct menu *m;
631     int d;
632 {
633     int line;
634     struct menu_line *maybe;
635
636     if (d < 0)
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]);
641         }
642         else if (m->m_lines[line].ml_submenu != NULLMENU
643                  && (maybe =
644                    find_command_from(c, m->m_lines[line].ml_submenu, d - 1))
645                  != (struct menu_line *) 0) {
646             return (maybe);
647         }
648     }
649     /* If we got to here, nothing works */
650     return ((struct menu_line *) 0);
651 }
652
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 */
656 struct menu_line *
657 Find_command(command)
658     char *command;
659 {
660     if (top_menu == NULLMENU) {
661         return ((struct menu_line *) 0);
662     }
663     else {
664         return (find_command_from(command, top_menu, MAX_MENU_DEPTH));
665     }
666 }
This page took 0.084654 seconds and 5 git commands to generate.