]> andersk Git - moira.git/blame_incremental - clients/moira/menu.c
forgot needed include file...
[moira.git] / clients / moira / menu.c
... / ...
CommitLineData
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
21static 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
40extern char *calloc();
41int more_flg = 1;
42
43/* Structure for holding current displayed menu */
44struct 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
54Menu *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 */
62Start_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
81Cleanup_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 */
92Start_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 */
105struct menu_screen *
106make_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 */
137destroy_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 */
152int
153Do_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 */
289int 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 */
375int 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
461int 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 */
466Start_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 */
477Stop_paging()
478{
479 lines_left = -1;
480}
481
482/* Print a message in the input window of cur_ms. */
483Put_message(msg)
484char *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. */
507Put_line(msg)
508char *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 */
557refresh_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 */
570Parse_words(buf, argv, n)
571 char *buf;
572 char *argv[];
573int 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 */
597struct menu_line *
598find_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 */
626struct menu_line *
627Find_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
638void
639sms_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.075701 seconds and 5 git commands to generate.