]> andersk Git - moira.git/blob - clients/moira/menu.c
Fix typo.
[moira.git] / clients / moira / menu.c
1 /* $Id$
2  *
3  * Generic menu system module.
4  *
5  * Copyright (C) 1987-1998 by the Massachusetts Institute of Technology.
6  * For copying and distribution information, see the file
7  * <mit-copyright.h>.
8  */
9
10 #include <mit-copyright.h>
11 #include <moira.h>
12 #include "menu.h"
13
14 #include <ctype.h>
15 #ifdef HAVE_CURSES
16 #include <curses.h>
17 #endif
18 #include <signal.h>
19 #include <stdio.h>
20 #include <stdlib.h>
21 #include <string.h>
22 #include <unistd.h>
23
24 RCSID("$Header$");
25
26 #ifdef MAX
27 #undef MAX
28 #undef MIN
29 #endif
30 #define MAX(A, B)       ((A) > (B) ? (A) : (B))
31 #define MIN(A, B)       ((A) < (B) ? (A) : (B))
32 #define CTL(ch)         ((ch) & 037)
33
34 #define MIN_INPUT 2             /* Minimum number of lines for input window */
35
36 extern int interrupt;           /* will be set if ^C is received */
37 extern char *whoami;
38
39 FILE *log_file = (FILE *) NULL;         /* file stream of log file */
40 int more_flg = 1;
41
42 /* Structure for holding current displayed menu */
43 struct menu_screen {
44 #ifdef HAVE_CURSES
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 #endif /* HAVE_CURSES */
50   int ms_input_y;               /* Input subwindow reference coordinate */
51 } *cur_ms;
52
53 #ifndef HAVE_CURSES
54 int COLS;
55 #endif
56
57 #define NULLMS ((struct menu_screen *) 0)
58
59 Menu *top_menu;                 /* Root for command search */
60 int parsed_argc;                /* used by extern routines to get additional */
61 char **parsed_argv;             /*   comand line input */
62
63 int Parse_words(char *buf, char *argv[], int n);
64 void refresh_ms(struct menu_screen *ms);
65 void Put_line(char *msg);
66 void menu_com_err_hook(const char *who, long code,
67                        const char *fmt, va_list ap);
68 struct menu_screen *make_ms(int length);
69 void destroy_ms(struct menu_screen *ms);
70 struct menu_line *find_command_from(char *c, struct menu *m, int d);
71 struct menu_line *Find_command(Menu *m, char *command);
72 int toggle_logging(int argc, char *argv[]);
73
74 /*
75  * Hook function to cause error messages to be printed through
76  * curses instead of around it.
77  */
78
79 void menu_com_err_hook(const char *who, long code, const char *fmt, va_list ap)
80 {
81   char buf[BUFSIZ], *cp;
82
83   if (who)
84     {
85       strcpy(buf, who);
86       for (cp = buf; *cp; cp++)
87         ;
88       *cp++ = ':';
89       *cp++ = ' ';
90     }
91   else
92     {
93       cp = buf;
94       *cp = '\0';
95     }
96   if (code)
97     {
98       strcpy(cp, error_message(code));
99       while (*cp)
100         cp++;
101     }
102   vsprintf(cp, fmt, ap);
103   Put_message(buf);
104 }
105
106 #ifdef HAVE_CURSES
107 /*
108  * Start_menu takes a menu as an argument.  It initializes curses u.s.w.,
109  * and a quit in any submenu should unwind back to here.  (it might not,
110  * if user functions which run their own menus don't cooperate.)
111  * Start_menu should only be called once, at the start of the program.
112  */
113 void Start_menu(Menu *m)
114 {
115   void (*old_hook)(const char *, long, const char *, va_list) =
116     set_com_err_hook(menu_com_err_hook);
117 #ifdef CURSES_HAS_NEWTERM
118   SCREEN *scrn = newterm(NULL, stdout, stdin);
119 #else
120   WINDOW *scrn = initscr();
121 #endif
122   if (!scrn)
123     {
124       fputs("Can't initialize curses!\nReverting to -nomenu mode\n\n", stderr);
125       Start_no_menu(m);
126     }
127   else
128     {
129 #ifdef CURSES_HAS_NEWTERM
130       set_term(scrn);
131       endwin();
132       initscr();
133 #endif
134       raw();            /* We parse & print everything ourselves */
135       noecho();
136       cur_ms = make_ms(0);      /* So we always have some current */
137                                 /* menu_screen */
138       /* Run the menu */
139       Do_menu(m, -1, NULL);
140       Cleanup_menu();
141     }
142   set_com_err_hook(old_hook);
143 }
144
145 void Cleanup_menu(void)
146 {
147   if (cur_ms)
148     {
149       wclear(cur_ms->ms_screen);
150       wrefresh(cur_ms->ms_screen);
151       endwin();
152     }
153 }
154
155 /*
156  * Create a new menu screen template with the specified menu length
157  * and return it.
158  */
159 struct menu_screen *make_ms(int length)
160 {
161   struct menu_screen *ms;
162
163   if (MAX_TITLE + length + MIN_INPUT > LINES)
164     {
165       fputs("Menu too big!\n", stderr);
166       exit(2);
167     }
168
169   ms = malloc(sizeof(struct menu_screen));
170
171   ms->ms_screen = newwin(0, 0, 0, 0);
172   ms->ms_title = subwin(ms->ms_screen, MAX_TITLE, 0, 0, 0);
173   ms->ms_menu = subwin(ms->ms_screen, length, 0, MAX_TITLE, 0);
174   ms->ms_input = subwin(ms->ms_screen, 0, 0,
175                         ms->ms_input_y = MAX_TITLE + length,
176                         0);
177
178   scrollok(ms->ms_input, TRUE);
179   wmove(ms->ms_input, 0, 0);
180   wclear(ms->ms_screen);
181
182   return ms;
183 }
184
185 /*
186  * This routine destroys a menu_screen.
187  */
188 void destroy_ms(struct menu_screen *ms)
189 {
190   delwin(ms->ms_title);
191   delwin(ms->ms_menu);
192   delwin(ms->ms_input);
193   delwin(ms->ms_screen);
194   free(ms);
195 }
196 #endif /* HAVE_CURSES */
197
198 /* Like Start_menu, except it doesn't print menus and doesn't use curses */
199 void Start_no_menu(Menu *m)
200 {
201   cur_ms = NULLMS;
202   COLS = 80;
203   /* Run the menu */
204   Do_menu(m, -1, NULL);
205 }
206
207 /*
208  * This guy actually puts up the menu
209  * Note: if margc < 0, no 'r' option will be displayed (i.e., on the
210  * top level menu)
211  */
212 int Do_menu(Menu *m, int margc, char *margv[])
213 {
214   struct menu_screen *my_ms = NULLMS, *old_cur_ms = NULLMS;
215   char argvals[MAX_ARGC][MAX_ARGLEN];   /* This is where args are stored */
216   char buf[MAX_ARGC * MAX_ARGLEN];
217   char *argv[MAX_ARGC];
218   int line;
219   int i;
220   struct menu_line *command;
221   int argc;
222   int quitflag, is_topmenu = (margc < 0);
223
224   /* Entry function gets called with old menu_screen still current */
225   if (m->m_entry != NULLFUNC)
226     {
227       if (m->m_entry(m, margc, margv) == DM_QUIT)
228         return DM_NORMAL;
229       if (parsed_argc > 0)
230         {
231           margc = parsed_argc + 1;
232           margv = --parsed_argv;
233         }
234       else
235         {
236           margc--;
237           margv++;
238         }
239     }
240
241   parsed_argc = 0;
242
243 #ifdef HAVE_CURSES
244   /* The following get run only in curses mode */
245   if (cur_ms != NULLMS)
246     {
247       /* Get a menu_screen */
248       old_cur_ms = cur_ms;
249       /* 2 is for the 2 obligatory lines; quit and toggle */
250       cur_ms = my_ms = make_ms(m->m_length + 2 + (is_topmenu ? 0 : 1));
251
252       /* Now print the title and the menu */
253       wclear(my_ms->ms_screen);
254       wrefresh(my_ms->ms_screen);
255       wmove(my_ms->ms_title, 0, MAX(0, (COLS - (int)strlen(m->m_title)) >> 1));
256       wstandout(my_ms->ms_title);
257       waddstr(my_ms->ms_title, m->m_title);
258       wstandend(my_ms->ms_title);
259
260       for (line = 0; line < m->m_length; line++)
261         {
262           int len = strlen(m->m_lines[line].ml_command);
263           if (len > 12)
264             len = 12;
265
266           wmove(my_ms->ms_menu, line, 0);
267
268           wprintw(my_ms->ms_menu, "%2d. (%s)%*s %s.", line + 1,
269                   m->m_lines[line].ml_command, 12 - len, "",
270                   m->m_lines[line].ml_doc);
271         }
272       wmove(my_ms->ms_menu, line++, 0);
273       if (!is_topmenu)
274         {
275           waddstr(my_ms->ms_menu,
276                   " r. (return)       Return to previous menu.");
277           wmove(my_ms->ms_menu, line++, 0);
278         }
279       waddstr(my_ms->ms_menu, " t. (toggle)       Toggle logging on and off.");
280       wmove(my_ms->ms_menu, line, 0);
281       waddstr(my_ms->ms_menu, " q. (quit)         Quit.");
282     }
283   else
284 #endif /* HAVE_CURSES */
285     {
286       Put_message(m->m_title);
287       for (line = 0; line < m->m_length; line++)
288         {
289           sprintf(buf, "%2d. (%s)%*s %s.", line + 1,
290                   m->m_lines[line].ml_command,
291                   12 - strlen(m->m_lines[line].ml_command), "",
292                   m->m_lines[line].ml_doc);
293           Put_message(buf);
294         }
295       if (!is_topmenu)
296         Put_message(" r. (return)       Return to previous menu.");
297       Put_message(" t. (toggle)       Toggle logging on and off.");
298       Put_message(" q. (quit)         Quit.");
299       Put_message(" ?.                Print this information.");
300     }
301
302   for (;;)
303     {
304       /* This will be set by a return val from func or submenu */
305       quitflag = DM_NORMAL;
306 #ifdef HAVE_CURSES
307       /* This is here because we may be coming from another menu */
308       if (cur_ms != NULL)
309         {
310           touchwin(my_ms->ms_screen);
311           wrefresh(my_ms->ms_screen);
312         }
313 #endif /* HAVE_CURSES */
314       if (margc > 1)
315         {
316           /* Initialize argv */
317           for (argc = 0; argc < MAX_ARGC; argc++)
318             argv[argc] = argvals[argc];
319           argc = margc - 1;
320           for (i = 1; i < margc; i++)
321             strcpy(argvals[i - 1], margv[i]);
322           margc = 0;
323         }
324       else
325         {
326           /* Get a command */
327           if (!Prompt_input("Command: ", buf, sizeof(buf)))
328             {
329               if (cur_ms == NULLMS && feof(stdin))
330                 sprintf(buf, "quit");
331               else
332                 continue;
333             }
334           /* Parse it into the argument list */
335           /* If there's nothing there, try again */
336           /* Initialize argv */
337           for (argc = 0; argc < MAX_ARGC; argc++)
338             argv[argc] = argvals[argc];
339
340           if ((argc = Parse_words(buf, argv, MAX_ARGLEN)) == 0)
341             continue;
342         }
343       if ((line = atoi(argv[0])) > 0 && line <= m->m_length)
344         command = &m->m_lines[line - 1];
345       else if ((!is_topmenu &&
346                 (!strcmp(argv[0], "r") || !strcmp(argv[0], "return"))) ||
347                !strcmp(argv[0], "q") || !strcmp(argv[0], "quit"))
348         {
349           /* here if it's either return or quit */
350 #ifdef HAVE_CURSES
351           if (cur_ms != NULLMS)
352             {
353               cur_ms = old_cur_ms;
354               destroy_ms(my_ms);
355             }
356 #endif /* HAVE_CURSES */
357           if (m->m_exit != NULLFUNC)
358             m->m_exit(m);
359           return *argv[0] == 'r' ? DM_NORMAL : DM_QUIT;
360         }
361       else if (argv[0][0] == '?')
362         {
363           for (line = 0; line < m->m_length; line++)
364             {
365               sprintf(buf, "%2d. (%s)%*s %s.", line + 1,
366                       m->m_lines[line].ml_command,
367                       12 - strlen(m->m_lines[line].ml_command), "",
368                       m->m_lines[line].ml_doc);
369               Put_message(buf);
370             }
371           if (!is_topmenu)
372             Put_message(" r. (return)       Return to previous menu.");
373           Put_message(" t. (toggle)       Toggle logging on and off.");
374           Put_message(" q. (quit)         Quit.");
375           continue;
376         }
377       else if (!strcmp(argv[0], "t") || !strcmp(argv[0], "toggle"))
378         {
379           toggle_logging(argc, argv);
380           continue;
381         }
382       /* finally, try to find it using Find_command */
383       else if (!(command = Find_command(m, argvals[0])))
384         {
385           sprintf(buf, "Command not recognized: %s\n", argvals[0]);
386           Put_message(buf);
387           continue;
388         }
389       /* If we got to here, command is a valid menu_line */
390       /* Send the offical command name into the argv */
391       strcpy(argvals[0], command->ml_command);
392       /* Show that we're working on it */
393       Put_message(command->ml_doc);
394       /* Print args that we've already got */
395       for (i = 1; i < argc; i++)
396         {
397           if (!command->ml_args[i].ma_prompt)
398             break;
399           sprintf(buf, "%s%s", command->ml_args[i].ma_prompt, argv[i]);
400           Put_message(buf);
401         }
402       /* Get remaining arguments, if any */
403       for (; argc < command->ml_argc; argc++)
404         {
405           if (!Prompt_input(command->ml_args[argc].ma_prompt,
406                             argvals[argc], sizeof(argvals[argc])))
407             goto punt_command;
408         }
409       parsed_argc = argc - command->ml_argc;
410       parsed_argv = &(argv[command->ml_argc]);
411       if (command->ml_function != NULLFUNC)
412         {
413           /* If it's got a function, call it */
414           quitflag = command->ml_function(argc, argv);
415         }
416       else if (command->ml_submenu != NULLMENU)
417         {
418           /* Else see if it is a submenu */
419           quitflag = Do_menu(command->ml_submenu, argc, argv);
420         }
421       else
422         {
423           /* If it's got neither, something is wrong */
424           Put_message("*INTERNAL ERROR: NO FUNCTION OR MENU FOR LINE*");
425         }
426       if (quitflag == DM_QUIT)
427         {
428 #ifdef HAVE_CURSES
429           if (cur_ms != NULLMS)
430             {
431               cur_ms = old_cur_ms;
432               destroy_ms(my_ms);
433             }
434 #endif /* HAVE_CURSES */
435           if (m->m_exit != NULLFUNC)
436             m->m_exit(m);
437           parsed_argc = 0;
438           return DM_QUIT;
439         }
440     punt_command:
441       parsed_argc = 0;
442     }
443 }
444
445 void refresh_screen(void)
446 {
447 #ifdef HAVE_CURSES
448   if (cur_ms != NULLMS)
449     {
450       touchwin(cur_ms->ms_screen);
451       refresh_ms(cur_ms);
452     }
453 #endif /* HAVE_CURSES */
454 }
455
456
457 /* Prompt the user for input in the input window of cur_ms */
458 int Prompt_input(char *prompt, char *buf, int buflen)
459 {
460   int c;
461   char *p;
462   int y, x, oldx, oldy;
463
464 #ifdef HAVE_CURSES
465   if (cur_ms != NULLMS)
466     {
467       more_flg = 1;
468       getyx(cur_ms->ms_input, y, x);
469       wmove(cur_ms->ms_input, y, 0);
470
471       refresh_screen();
472       waddstr(cur_ms->ms_input, prompt);
473       getyx(cur_ms->ms_input, y, x);
474
475       oldx = x;
476       oldy = y;
477       p = buf;
478       while (1)
479         {
480           wmove(cur_ms->ms_input, y, x);
481           touchwin(cur_ms->ms_screen);
482           wclrtoeol(cur_ms->ms_input);
483           wrefresh(cur_ms->ms_input);
484           c = getchar() & 0x7f;
485           switch (c)
486             {
487             case CTL('C'):
488               *p = '\0';
489               return 0;
490             case CTL('Z'):
491               kill(getpid(), SIGTSTP);
492               touchwin(cur_ms->ms_screen);
493               break;
494             case CTL('L'):
495               wclear(cur_ms->ms_input);
496               wmove(cur_ms->ms_input, 0, 0);
497               waddstr(cur_ms->ms_input, prompt);
498               touchwin(cur_ms->ms_input);
499               wrefresh(cur_ms->ms_screen);
500               getyx(cur_ms->ms_input, y, x);
501               oldy = y;
502               oldx = x;
503               p = buf;
504               break;
505
506             case '\n':
507             case '\r':
508               goto end_input;
509               /* these should be obtained by doing ioctl() on tty */
510             case '\b':
511             case '\177':
512               if (p > buf)
513                 {
514                   p--;
515                   x--;
516                   if (x < 0)
517                     {
518                       wmove(cur_ms->ms_input, y, 0);
519                       wclrtoeol(cur_ms->ms_input);
520                       y--;
521                       x = getmaxx(cur_ms->ms_input) - 1;
522                     }
523                 }
524               break;
525             case CTL('U'):
526             case CTL('G'):
527             case CTL('['):
528               x = oldx;
529               y = oldy;
530               p = buf;
531               break;
532             default:
533               /* (buflen - 1) leaves room for the \0 */
534               if (isprint(c) && (p - buf < buflen - 1))
535                 {
536                   waddch(cur_ms->ms_input, c);
537                   *p++ = c;
538                   x++;
539                   if (x >= getmaxx(cur_ms->ms_input))
540                     {
541                       x = 0;
542                       y++;
543                     }
544                 }
545               else
546                 putchar(CTL('G'));
547               break;
548             }
549         }
550     end_input:
551       waddch(cur_ms->ms_input, '\n');
552
553       wclrtoeol(cur_ms->ms_input);
554       refresh_ms(cur_ms);
555       *p = '\0';
556       Start_paging();
557       strcpy(buf, strtrim(buf));
558       return 1;
559     }
560   else
561 #endif /* HAVE_CURSES */
562     {
563       char bigbuf[BUFSIZ];
564
565       printf("%s", prompt);
566       if (!fgets(bigbuf, BUFSIZ, stdin))
567         return 0;
568       if (interrupt)
569         {
570           interrupt = 0;
571           return 0;
572         }
573       Start_paging();
574       strncpy(buf, strtrim(bigbuf), buflen);
575       if (strchr(buf, '\n'))
576         *strchr(buf, '\n') = '\0';
577       else
578         buf[buflen - 1] = '\0';
579       return 1;
580     }
581 }
582
583 int lines_left;
584
585 /* Start paging */
586 /* This routine will cause the most recently put message to be the
587    one at the top of the screen when a ---More--- prompt is displayed */
588 void Start_paging(void)
589 {
590 #ifdef HAVE_CURSES
591   if (cur_ms != NULLMS)
592     lines_left = LINES - cur_ms->ms_input_y - 1;
593   else
594 #endif /* HAVE_CURSES */
595     lines_left = 23;
596 }
597
598 /* Turn off paging */
599 void Stop_paging(void)
600 {
601   lines_left = -1;
602 }
603
604 /* Print a message in the input window of cur_ms.  */
605 void Put_message(char *msg)
606 {
607   char *copy, *line, *s;
608
609   copy = malloc(COLS);
610   s = line = msg;
611   if (log_file)
612     {
613       /* if we're doing logging; we assume that the file has already
614          been opened. */
615       fprintf(log_file, "%s\n", msg);
616       fflush(log_file);
617     }
618
619   while (*s++)
620     {
621       if (s - line >= COLS - 1)
622         {
623           strncpy(copy, line, COLS - 1);
624           copy[COLS - 1] = '\0';
625           line += COLS - 1;
626         }
627       else if (*s == '\n')
628         {
629           *s = '\0';
630           strcpy(copy, line);
631           line = ++s;
632         }
633       else
634         continue;
635       Put_line(copy);
636     }
637   Put_line(line);
638   free(copy);
639 }
640
641 /* Will be truncated to COLS characters.  */
642 void Put_line(char *msg)
643 {
644   int y, x, i;
645   char *msg1, chr;
646
647   if (!more_flg)
648     return;
649
650   if (lines_left >= 0)
651     {
652       if (--lines_left == 0)
653         {
654           /* Give the user a more prompt */
655 #ifdef HAVE_CURSES
656           if (cur_ms != NULLMS)
657             {
658               wstandout(cur_ms->ms_input);
659               wprintw(cur_ms->ms_input, "---More---");
660               wstandend(cur_ms->ms_input);
661               refresh_ms(cur_ms);
662               chr = getchar() & 0x7f;/* We do care what it is */
663               if (chr == 'q' || chr == 'Q' || chr == 3 /* ^C */)
664                 {
665                   more_flg = 0;
666                   return;
667                 }
668               getyx(cur_ms->ms_input, y, x);
669               /* x is a bitbucket; avoid lint problems */
670               x = x;
671               wmove(cur_ms->ms_input, y, 0);
672               wclrtoeol(cur_ms->ms_input);
673             }
674           else
675 #endif /* HAVE_CURSES */
676             {
677               printf("---More (hit return)---");
678               getchar();
679             }
680           Start_paging();       /* Reset lines_left */
681         }
682     }
683
684 #ifdef HAVE_CURSES
685   if (cur_ms != NULLMS)
686     {
687       msg1 = calloc(COLS, 1);
688       strncpy(msg1, msg, COLS - 1);
689       for (i = strlen(msg1); i < COLS - 1; i++)
690         msg1[i] = ' ';
691       wprintw(cur_ms->ms_input, "%s\n", msg1);
692     }
693   else
694 #endif /* HAVE_CURSES */
695     puts(msg);
696 }
697
698 #ifdef HAVE_CURSES
699 /* Refresh a menu_screen onto the real screen */
700 void refresh_ms(struct menu_screen *ms)
701 {
702   wrefresh(ms->ms_title);
703   wrefresh(ms->ms_menu);
704   wrefresh(ms->ms_input);
705 }
706 #endif /* HAVE_CURSES */
707
708 /* Parse buf into a list of words, which will be placed in strings specified by
709    argv.  Space for these strings must have already been allocated.
710    Only the first n characters of each word will be copied */
711 int Parse_words(char *buf, char *argv[], int n)
712 {
713   char *start, *end;            /* For sausage machine */
714   int argc;
715
716   start = buf;
717   for (argc = 0; argc < MAX_ARGC; argc++)
718     {
719       while (isspace(*start))
720         start++;                /* Kill whitespace */
721       if (*start == '\0')
722         break;          /* Nothing left */
723       /* Now find the end of the word */
724       for (end = start; *end != '\0' && !isspace(*end); end++)
725         ;
726       strncpy(argv[argc], start, MIN(end - start, n));  /* Copy it */
727       argv[argc][MIN(end - start, n - 1)] = '\0';       /* Terminate */
728       start = end;
729     }
730   return argc;
731 }
732
733 /* This is the internal form of Find_command, which recursively searches
734    for a menu_line with command command in the specified menu */
735 /* It will search to a maximum depth of d */
736 struct menu_line *find_command_from(char *c, struct menu *m, int d)
737 {
738   int line;
739   struct menu_line *maybe;
740
741   if (d < 0)
742     return NULL;        /* Too deep! */
743   for (line = 0; line < m->m_length; line++)
744     {
745       if (!strcmp(c, m->m_lines[line].ml_command))
746         return &m->m_lines[line];
747     }
748   for (line = 0; line < m->m_length; line++)
749     {
750       if (m->m_lines[line].ml_submenu != NULLMENU &&
751           (maybe = find_command_from(c, m->m_lines[line].ml_submenu, d - 1)))
752         return maybe;
753     }
754   /* If we got to here, nothing works */
755   return NULL;
756 }
757
758 /* Find_command searches down the current menu tree */
759 /* And returns a pointer to a menu_line with the specified command name */
760 /* It returns (struct menu_line *) 0 if none is found */
761 struct menu_line *Find_command(Menu *m, char *command)
762 {
763   if (m == NULLMENU)
764     return NULL;
765   else
766     return find_command_from(command, m, MAX_MENU_DEPTH);
767 }
768
769 int toggle_logging(int argc, char *argv[])
770 {
771   char buf[BUFSIZ];
772
773   if (!log_file)
774     {
775       sprintf(buf, "/var/tmp/%s-log.%ld", whoami, (long)getpid());
776
777       /* open the file */
778       log_file = fopen(buf, "a");
779
780       if (!log_file)
781         Put_message("Open of log file failed.  Logging is not on.");
782       else
783         Put_message("Log file successfully opened.");
784     }
785   else
786     { /* log_file is a valid pointer; turn off logging. */
787       fflush(log_file);
788       fclose(log_file);
789       log_file = NULL;
790       Put_message("Logging off.");
791     }
792   return DM_NORMAL;
793 }
This page took 0.100199 seconds and 5 git commands to generate.