]> andersk Git - moira.git/blob - clients/moira/menu.c
5da0c3e34d40199bdb8853f0525898be36915e8c
[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  *
10  * Generic menu system module.
11  *
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.
15  *
16  * By making the menu descriptions so general, we can ease porting to just
17  * about anything.
18  */
19
20 #ifndef lint
21 static char rcsid_menu_c[] = "$Header$";
22
23 #endif lint
24
25 #include <sys/types.h>
26 #include <stdio.h>
27 #include <signal.h>
28 #include <curses.h>
29 #include <ctype.h>
30 #include <strings.h>
31 #include <varargs.h>
32 #include "menu.h"
33
34 #define MAX(A,B)        ((A) > (B) ? (A) : (B))
35 #define MIN(A,B)        ((A) < (B) ? (A) : (B))
36 #define CTL(ch)         ((ch) & 037)
37
38 #define MIN_INPUT 2             /* Minimum number of lines for input window */
39
40 extern char *calloc();
41 int more_flg = 1;
42
43 /* Structure for holding current displayed menu */
44 struct menu_screen {
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 */
50 }          *cur_ms;
51
52 #define NULLMS ((struct menu_screen *) 0)
53
54 Menu *top_menu;                 /* Root for command search */
55
56 /*
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.
61  */
62 Start_menu(m)
63     Menu *m;
64 {
65     struct menu_screen *make_ms();
66
67     if (initscr() == ERR) {
68         fputs("Can't initialize curses!\n", stderr);
69         Start_no_menu(m);
70     }
71     (void) raw();               /* We parse & print everything ourselves */
72     (void) noecho();
73     cur_ms = make_ms(0);        /* So we always have some current */
74                                 /* menu_screen */ 
75     top_menu = m;
76     /* Run the menu */
77     (void) Do_menu(m, -1, (char **) NULL);
78     Cleanup_menu();
79 }
80
81 Cleanup_menu()
82 {
83     if (cur_ms) {
84         (void) wclear(cur_ms->ms_screen);
85         (void) wrefresh(cur_ms->ms_screen);
86     }
87     endwin();
88 }
89     
90
91 /* Like Start_menu, except it doesn't print menus and doesn't use curses */
92 Start_no_menu(m)
93     Menu *m;
94 {
95     cur_ms = NULLMS;
96     top_menu = m;
97     /* Run the menu */
98     (void) Do_menu(m, -1, (char **) NULL);
99 }
100
101 /*
102  * Create a new menu screen template with the specified menu length
103  * and return it.
104  */
105 struct menu_screen *
106 make_ms(length)
107     int length;
108 {
109     struct menu_screen *ms;
110     char *malloc();
111
112     if (MAX_TITLE + length + MIN_INPUT > LINES) {
113         fputs("Menu too big!\n", stderr);
114         exit(2);
115     }
116
117     ms = (struct menu_screen *) malloc(sizeof(struct menu_screen));
118
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,
125                           0);
126
127     scrollok(ms->ms_input, TRUE);
128     (void) wmove(ms->ms_input, 0, 0);
129     (void) wclear(ms->ms_screen);
130
131     return (ms);
132 }
133
134 /*
135  * This routine destroys a menu_screen.
136  */
137 destroy_ms(ms)
138     struct menu_screen *ms;
139 {
140     delwin(ms->ms_title);
141     delwin(ms->ms_menu);
142     delwin(ms->ms_input);
143     delwin(ms->ms_screen);
144     free((char *) ms);
145 }
146
147 /*
148  * This guy actually puts up the menu
149  * Note: if margc < 0, no 'r' option will be displayed (i.e., on the
150  * top level menu)
151  */
152 int 
153 Do_menu(m, margc, margv)
154     Menu *m;
155     int margc;
156     char *margv[];
157 {
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];
162     int line;
163     int i;
164     struct menu_line *command, *Find_command();
165     int argc;
166     int quitflag, is_topmenu = (margc < 0);
167     
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)
171             return DM_NORMAL;
172
173     /* The following get run only in curses mode */
174     if (cur_ms != NULLMS) {
175         /* Get a menu_screen */
176         old_cur_ms = cur_ms;
177         cur_ms = my_ms = make_ms(m->m_length + 1 + (is_topmenu?0:1));
178
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);
186
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);
192         }
193         (void) wmove(my_ms->ms_menu, line++, 0);
194         if (!is_topmenu) {
195             (void) waddstr(my_ms->ms_menu,
196                            " r. (return      ) Return to previous menu.");
197             (void) wmove(my_ms->ms_menu, line, 0);
198         }
199         (void) waddstr(my_ms->ms_menu, " q. (quit        ) Quit.");
200
201     }
202
203     for (;;) {
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 */
207         if (cur_ms != NULL)
208             touchwin(my_ms->ms_screen);
209         /* Get a command */
210         if (!Prompt_input("Command: ", buf, sizeof(buf)))
211             continue;
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];
217
218         if ((argc = Parse_words(buf, argv, MAX_ARGLEN)) == 0)
219             continue;
220         if ((line = atoi(argv[0])) > 0 && line <= m->m_length) {
221             command = &m->m_lines[line - 1];
222         }
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) {
230                 cur_ms = old_cur_ms;
231                 destroy_ms(my_ms);
232             }
233             if (m->m_exit != NULLFUNC)
234                 m->m_exit(m);
235             return (*argv[0] == 'r' ? DM_NORMAL : DM_QUIT);
236             /* finally, try to find it using Find_command */
237         }
238         else if ((command = Find_command(argvals[0])) ==
239                  (struct menu_line *) 0) {
240             Put_message("Command not recognized");
241             continue;
242         }
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)
251                 break;
252             (void) sprintf(buf, "%s%s", command->ml_args[i].ma_prompt,
253                            argv[i]);
254             Put_message(buf);
255         }
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])))
260                 goto punt_command;
261         }
262         if (command->ml_function != NULLFUNC) {
263             /* If it's got a function, call it */
264             quitflag = command->ml_function(argc, argv);
265         }
266         else if (command->ml_submenu != NULLMENU) {
267             /* Else see if it is a submenu */
268             quitflag = Do_menu(command->ml_submenu, argc, argv);
269         }
270         else {
271             /* If it's got neither, something is wrong */
272             Put_message("*INTERNAL ERROR: NO FUNCTION OR MENU FOR LINE*");
273         }
274         if (quitflag == DM_QUIT) {
275             if (cur_ms != NULLMS) {
276                 cur_ms = old_cur_ms;
277                 destroy_ms(my_ms);
278             }
279             if (m->m_exit != NULLFUNC)
280                 m->m_exit(m);
281             return (DM_QUIT);
282         }
283     punt_command:
284         ;
285     }
286 }
287
288 /* Prompt the user for input in the input window of cur_ms */
289 int Prompt_input(prompt, buf, buflen)
290     char *prompt;
291     char *buf;
292     int buflen;
293 {
294     int c;
295     char *p;
296     int y, x, oldx;
297
298     if (cur_ms != NULLMS) {
299         more_flg = 1;
300         getyx(cur_ms->ms_input, y, x);
301         (void) wmove(cur_ms->ms_input, y, 0);
302
303         touchwin(cur_ms->ms_screen);
304         refresh_ms(cur_ms);
305         (void) waddstr(cur_ms->ms_input, prompt);
306         getyx(cur_ms->ms_input, y, x);
307
308         oldx = x;
309         p = buf;
310         while(1) {
311 /*      for (p = buf; p - buf < buflen;) { */
312             (void) wmove(cur_ms->ms_input, y, x);
313             (void) wclrtoeol(cur_ms->ms_input);
314             refresh_ms(cur_ms);
315             c = getchar();
316             switch (c) {
317             case CTL('C'):
318                 return 0;
319             case CTL('Z'):
320                 (void) kill(getpid(), SIGTSTP);
321                 touchwin(curscr);
322                 break;
323             case CTL('L'):
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);
328                 break;
329             case '\n':
330             case '\r':
331                 goto end_input;
332             case '\b':
333             case '\177':
334                 if (p > buf) {
335                     p--;
336                     x--;
337                 }
338                 break;
339             case CTL('U'):
340             case CTL('G'):
341             case CTL('['):
342                 x = oldx;
343                 p = buf;
344                 break;
345             default:
346                 if (isprint(c) && (p - buf < buflen)) {
347                     (void) waddch(cur_ms->ms_input, c);
348                     *p++ = c;
349                     x++;
350                 } else
351                     putchar(CTL('G'));
352                 break;
353             }
354         }
355     end_input:
356         (void) waddch(cur_ms->ms_input, '\n');
357         (void) waddch(cur_ms->ms_input, '\r');
358         
359         (void) wclrtoeol(cur_ms->ms_input);
360         refresh_ms(cur_ms);
361         *p = '\0';
362         Start_paging();
363         return 1;
364     } else {
365         printf("%s", prompt);
366         if (gets(buf) == NULL)
367             return 0;
368         Start_paging();
369         return 1;
370     }
371 }
372
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)
376     char *prompt;
377     char *buf;
378     int buflen;
379 {
380     int c;
381     char *p;
382     int y, x, oldx;
383
384     if (cur_ms != NULLMS) {
385         more_flg = 1;
386         getyx(cur_ms->ms_input, y, x);
387         (void) wmove(cur_ms->ms_input, y, 0);
388
389         touchwin(cur_ms->ms_screen);
390         refresh_ms(cur_ms);
391         (void) waddstr(cur_ms->ms_input, prompt);
392         getyx(cur_ms->ms_input, y, x);
393
394         oldx = x;
395         for (p = buf; p - buf < buflen;) {
396             (void) wmove(cur_ms->ms_input, y, x);
397             (void) wclrtoeol(cur_ms->ms_input);
398             refresh_ms(cur_ms);
399             c = getchar();
400             switch (c) {
401             case CTL('C'):
402                 return 0;
403             case CTL('Z'):
404                 (void) kill(getpid(), SIGTSTP);
405                 touchwin(curscr);
406                 break;
407             case CTL('L'):
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);
412                 break;
413             case '\n':
414             case '\r':
415                 (void) waddch(cur_ms->ms_input, '\n');
416                 (void) waddch(cur_ms->ms_input, '\r');
417
418                 (void) wclrtoeol(cur_ms->ms_input);
419                 refresh_ms(cur_ms);
420                 *p = '\0';
421                 Start_paging();
422                 return 1;
423             case '\b':
424             case '\177':
425                 if (p > buf) {
426                     p--;
427                     x--;
428                 }
429                 break;
430             case CTL('U'):
431                 x = oldx;
432                 p = buf;
433                 break;
434             default:
435                 *p++ = c;
436                 break;
437             }
438         }
439     }
440     else {
441         struct sgttyb ttybuf, nttybuf;
442         printf("%s", prompt);
443         /* turn off echoing */
444         (void) ioctl(0, TIOCGETP, (char *)&ttybuf);
445         nttybuf = 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);
450             putchar('\n');
451             return 0;
452         }
453         putchar('\n');
454         (void) ioctl(0, TIOCSETP, (char *)&ttybuf);
455         Start_paging();
456         return 1;
457     }
458     return 0;
459 }
460
461 int lines_left;
462
463 /* Start paging */
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 */
466 Start_paging()
467 {
468     if (cur_ms != NULLMS) {
469         lines_left = LINES - cur_ms->ms_input_y - 1;
470     }
471     else {
472         lines_left = 23;
473     }
474 }
475
476 /* Turn off paging */
477 Stop_paging()
478 {
479     lines_left = -1;
480 }
481
482 /* Print a message in the input window of cur_ms.  */
483 Put_message(msg)
484 char *msg;
485 {
486     char *copy, *line, *s;
487
488     copy = (char *) malloc((u_int)COLS);
489     s = line = msg;
490     while(*s++) {
491         if (s - line >= COLS-1) {
492             (void) strncpy(copy, line, COLS-1);
493             line += COLS-1;
494         } else if (*s == '\n') {
495             *s = '\0';
496             (void) strcpy(copy, line);
497             line = ++s;
498         } else
499             continue;
500         Put_line(copy);
501     }
502     Put_line(line);
503     free(copy);
504 }
505
506 /* Will be truncated to COLS characters.  */
507 Put_line(msg)
508 char *msg;
509 {
510     int y, x, i;
511     char *msg1, chr;
512
513     if (!more_flg)
514         return;
515
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);
523                 refresh_ms(cur_ms);
524                 chr = getchar();/* We do care what it is */
525                 if (chr == 'q' || chr == 'Q') {
526                     more_flg = 0;
527                     return;
528                 }
529                 getyx(cur_ms->ms_input, y, x);
530                 /* x is a bitbucket; avoid lint problems */
531                 x=x;
532                 (void) wmove(cur_ms->ms_input, y, 0);
533                 (void) wclrtoeol(cur_ms->ms_input);
534             }
535             else {
536                 printf("---More (hit return)---");
537                 (void) getchar();
538             }
539             Start_paging();     /* Reset lines_left */
540         }
541     }
542
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++)
547             msg1[i] = ' ';
548         (void) wprintw(cur_ms->ms_input, "%s\n", msg1);
549 /*      refresh_ms(cur_ms); */
550     }
551     else {
552         puts(msg);
553     }
554 }
555
556 /* Refresh a menu_screen onto the real screen */
557 refresh_ms(ms)
558     struct menu_screen *ms;
559 {
560     int y, x;
561
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);
565 }
566
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)
571     char *buf;
572     char *argv[];
573 int n;
574
575 {
576     char *start, *end;          /* For sausage machine */
577     int argc;
578
579     start = buf;
580     for (argc = 0; argc < MAX_ARGC; argc++) {
581         while (isspace(*start))
582             start++;            /* Kill whitespace */
583         if (*start == '\0')
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 */
589         start = end;
590     }
591     return (argc);
592 }
593
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 */
597 struct menu_line *
598 find_command_from(c, m, d)
599     char *c;
600     struct menu *m;
601     int d;
602 {
603     int line;
604     struct menu_line *maybe;
605
606     if (d < 0)
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]);
611         }
612         else if (m->m_lines[line].ml_submenu != NULLMENU
613                  && (maybe =
614                    find_command_from(c, m->m_lines[line].ml_submenu, d - 1))
615                  != (struct menu_line *) 0) {
616             return (maybe);
617         }
618     }
619     /* If we got to here, nothing works */
620     return ((struct menu_line *) 0);
621 }
622
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 */
626 struct menu_line *
627 Find_command(command)
628     char *command;
629 {
630     if (top_menu == NULLMENU) {
631         return ((struct menu_line *) 0);
632     }
633     else {
634         return (find_command_from(command, top_menu, MAX_MENU_DEPTH));
635     }
636 }
637
638 void 
639 sms_com_err_hook(who, code, fmt, args)
640     char *who;
641     int code;
642     char *fmt;
643     va_list args;
644 {
645     char buf[BUFSIZ], *cp;
646
647     FILE _strbuf;
648
649     (void) strcpy(buf, who);
650     for (cp = buf; *cp; cp++);
651     *cp++ = ':';
652     *cp++ = ' ';
653     if (code) {
654         (void) strcpy(cp, error_message(code));
655         while (*cp)
656             cp++;
657     }
658     _strbuf._flag = _IOWRT + _IOSTRG;
659     _strbuf._ptr = cp;
660     _strbuf._cnt = BUFSIZ - (cp - buf);
661     _doprnt(fmt, args, &_strbuf);
662     (void) putc('\0', &_strbuf);
663     Put_message(buf);
664 }
665
666 /*
667  * Local Variables:
668  * mode: c
669  * c-indent-level: 4
670  * c-continued-statement-offset: 4
671  * c-brace-offset: -4
672  * c-argdecl-indent: 4
673  * c-label-offset: -4
674  * End:
675  */
This page took 0.138204 seconds and 3 git commands to generate.