* Formatter now uses more of the available screen width
[citadel.git] / citadel / commands.c
1 /*
2  * $Id$
3  *
4  * This file contains functions which implement parts of the
5  * text-mode user interface.
6  *
7  */
8
9 #include "sysdep.h"
10 #include <stdlib.h>
11 #include <unistd.h>
12 #include <stdio.h>
13 #include <fcntl.h>
14 #include <ctype.h>
15 #include <string.h>
16 #include <sys/types.h>
17
18 #if TIME_WITH_SYS_TIME
19 # include <sys/time.h>
20 # include <time.h>
21 #else
22 # if HAVE_SYS_TIME_H
23 #  include <sys/time.h>
24 # else
25 #  include <time.h>
26 # endif
27 #endif
28
29 #ifdef HAVE_TERMIOS_H
30 #include <termios.h>
31 #else
32 #include <sgtty.h>
33 #endif
34
35 #ifdef HAVE_SYS_SELECT_H
36 #include <sys/select.h>
37 #endif
38
39 #ifdef THREADED_CLIENT
40 #include <pthread.h>
41 #endif
42
43 #include <signal.h>
44 #include <errno.h>
45 #include <stdarg.h>
46 #include "citadel.h"
47 #include "commands.h"
48 #include "messages.h"
49 #include "citadel_decls.h"
50 #include "routines.h"
51 #include "routines2.h"
52 #include "tools.h"
53 #include "rooms.h"
54 #include "client_chat.h"
55 #ifndef HAVE_SNPRINTF
56 #include "snprintf.h"
57 #endif
58 #include "screen.h"
59
60 struct citcmd {
61         struct citcmd *next;
62         int c_cmdnum;
63         int c_axlevel;
64         char c_keys[5][64];
65 };
66
67 #define IFNEXPERT if ((userflags&US_EXPERT)==0)
68
69
70 int rc_exp_beep;
71 char rc_exp_cmd[1024];
72 int rc_allow_attachments;
73 int rc_display_message_numbers;
74 int rc_force_mail_prompts;
75 int rc_remember_passwords;
76 int rc_ansi_color;
77 int num_urls = 0;
78 char urls[MAXURLS][SIZ];
79 char rc_url_cmd[SIZ];
80
81 char *gl_string;
82 int next_lazy_cmd = 5;
83
84 int lines_printed = 0;          /* line count for paginator */
85 extern int screenwidth, screenheight;
86 extern int termn8;
87
88 struct citcmd *cmdlist = NULL;
89
90
91 /* these variables are local to this module */
92 char keepalives_enabled = KA_YES;       /* send NOOPs to server when idle */
93 int ok_to_interrupt = 0;                /* print express msgs asynchronously */
94 time_t AnsiDetect;                      /* when did we send the detect code? */
95 int enable_color = 0;                   /* nonzero for ANSI color */
96
97
98
99
100 /*
101  * If an interesting key has been pressed, return its value, otherwise 0
102  */
103 char was_a_key_pressed(void) {
104         fd_set rfds;
105         struct timeval tv;
106         int the_character;
107         int retval;
108
109         FD_ZERO(&rfds);
110         FD_SET(0, &rfds);
111         tv.tv_sec = 0;
112         tv.tv_usec = 0;
113         retval = select(1, &rfds, NULL, NULL, &tv); 
114
115         /* Careful!  Disable keepalives during keyboard polling; we're probably
116          * in the middle of a data transfer from the server, in which case
117          * sending a NOOP would throw the client protocol out of sync.
118          */
119         if (FD_ISSET(0, &rfds)) {
120                 set_keepalives(KA_NO);
121                 the_character = inkey();
122                 set_keepalives(KA_YES);
123         }
124         else {
125                 the_character = 0;
126         }
127         return(the_character);
128 }
129
130
131
132
133
134 /*
135  * Check to see if we need to pause at the end of a screen.
136  * If we do, we have to disable server keepalives during the pause because
137  * we are probably in the middle of a server operation and the NOOP command
138  * would confuse everything.
139  */
140 int checkpagin(int lp, int pagin, int height)
141 {
142         int thekey;
143
144         if (sigcaught) return(lp);
145         thekey = was_a_key_pressed();
146         if (thekey == 'q' || thekey == 'Q' || thekey == 's' || thekey == 'S')
147                 thekey = STOP_KEY;
148         if (thekey == 'n' || thekey == 'N')
149                 thekey = NEXT_KEY;
150         if ( (thekey == NEXT_KEY) || (thekey == STOP_KEY)) sigcaught = thekey;
151         if (sigcaught) return(lp);
152
153         if (!pagin) return(0);
154         if (lp>=(height-1)) {
155                 set_keepalives(KA_NO);
156                 hit_any_key();
157                 set_keepalives(KA_YES);
158                 return(0);
159         }
160         return(lp);
161 }
162
163
164
165
166 /*
167  * pprintf()  ...   paginated version of printf()
168  */
169 void pprintf(const char *format, ...) {   
170         va_list arg_ptr;
171         static char buf[4096];  /* static for performance, change if needed */
172         int i;
173
174         /* If sigcaught is nonzero, a keypress has interrupted this and we
175          * should just drain output.
176          */
177         if (sigcaught) return;
178  
179         /* Otherwise, start spewing... */ 
180         va_start(arg_ptr, format);   
181         vsprintf(buf, format, arg_ptr);   
182         va_end(arg_ptr);   
183
184         for (i=0; i<strlen(buf); ++i) {
185                 scr_putc(buf[i]);
186                 if (buf[i]==10) {
187                         ++lines_printed;
188                         lines_printed = checkpagin(lines_printed,
189                                 (userflags & US_PAGINATOR),
190                                 screenheight);
191                 }
192         }
193 }   
194
195
196
197 /*
198  * print_express()  -  print express messages if there are any
199  */
200 void print_express(void)
201 {
202         char buf[1024];
203         FILE *outpipe;
204         time_t timestamp;
205         struct tm *stamp;
206         int flags = 0;
207         char sender[64];
208         char node[64];
209
210         if (express_msgs == 0)
211                 return;
212
213         if (rc_exp_beep) {
214                 scr_putc(7);
215         }
216         if (strlen(rc_exp_cmd) == 0) {
217                 color(BRIGHT_RED);
218                 scr_printf("\r---");
219         }
220         
221         while (express_msgs != 0) {
222                 serv_puts("GEXP");
223                 serv_gets(buf);
224                 if (buf[0] != '1')
225                         return;
226         
227                 express_msgs = extract_int(&buf[4], 0);
228                 timestamp = extract_long(&buf[4], 1);
229                 flags = extract_int(&buf[4], 2);
230                 extract(sender, &buf[4], 3);
231                 extract(node, &buf[4], 4);
232                 strcpy(last_paged, sender);
233         
234                 stamp = localtime(&timestamp);
235
236                 /* If the page is a Logoff Request, honor it. */
237                 if (flags & 2) {
238                         termn8 = 1;
239                         return;
240                 }
241         
242                 if (strlen(rc_exp_cmd) > 0) {
243                         outpipe = popen(rc_exp_cmd, "w");
244                         if (outpipe != NULL) {
245                                 /* Header derived from flags */
246                                 if (flags & 2)
247                                         fprintf(outpipe,
248                                                "Please log off now, as requested ");
249                                 else if (flags & 1)
250                                         fprintf(outpipe, "Broadcast message ");
251                                 else if (flags & 4)
252                                         fprintf(outpipe, "Chat request ");
253                                 else
254                                         fprintf(outpipe, "Message ");
255                                 /* Timestamp.  Can this be improved? */
256                                 if (stamp->tm_hour == 0 || stamp->tm_hour == 12)
257                                         fprintf(outpipe, "at 12:%02d%cm",
258                                                 stamp->tm_min, 
259                                                 stamp->tm_hour ? 'p' : 'a');
260                                 else if (stamp->tm_hour > 12)           /* pm */
261                                         fprintf(outpipe, "at %d:%02dpm",
262                                                 stamp->tm_hour - 12,
263                                                 stamp->tm_min);
264                                 else                                    /* am */
265                                         fprintf(outpipe, "at %d:%02dam",
266                                                 stamp->tm_hour, stamp->tm_min);
267                                 fprintf(outpipe, " from %s", sender);
268                                 if (strncmp(serv_info.serv_nodename, node, 32))
269                                         fprintf(outpipe, " @%s", node);
270                                 fprintf(outpipe, ":\n");
271                                 while (serv_gets(buf), strcmp(buf, "000")) {
272                                         fprintf(outpipe, "%s\n", buf);
273                                 }
274                                 pclose(outpipe);
275                                 if (express_msgs == 0)
276                                         return;
277                                 continue;
278                         }
279                 }
280                 /* fall back to built-in express message display */
281                 scr_printf("\n");
282                 lines_printed++;
283
284                 /* Header derived from flags */
285                 if (flags & 2)
286                         scr_printf("Please log off now, as requested ");
287                 else if (flags & 1)
288                         scr_printf("Broadcast message ");
289                 else if (flags & 4)
290                         scr_printf("Chat request ");
291                 else
292                         scr_printf("Message ");
293         
294                 /* Timestamp.  Can this be improved? */
295                 if (stamp->tm_hour == 0 || stamp->tm_hour == 12)/* 12am/12pm */
296                         scr_printf("at 12:%02d%cm", stamp->tm_min, 
297                                 stamp->tm_hour ? 'p' : 'a');
298                 else if (stamp->tm_hour > 12)                   /* pm */
299                         scr_printf("at %d:%02dpm",
300                                 stamp->tm_hour - 12, stamp->tm_min);
301                 else                                            /* am */
302                         scr_printf("at %d:%02dam", stamp->tm_hour, stamp->tm_min);
303                 
304                 /* Sender */
305                 scr_printf(" from %s", sender);
306         
307                 /* Remote node, if any */
308                 if (strncmp(serv_info.serv_nodename, node, 32))
309                         scr_printf(" @%s", node);
310         
311                 scr_printf(":\n");
312                 lines_printed++;
313                 fmout(screenwidth, NULL, NULL, 1, screenheight, -1, 0);
314         }
315         scr_printf("\n---\n");
316         color(BRIGHT_WHITE);
317
318
319 }
320
321
322 void set_keepalives(int s)
323 {
324         keepalives_enabled = (char) s;
325 }
326
327 /* 
328  * This loop handles the "keepalive" messages sent to the server when idling.
329  */
330
331 static time_t idlet = 0;
332 static void really_do_keepalive(void) {
333         char buf[1024];
334
335         time(&idlet);
336         if (keepalives_enabled == KA_YES) {
337                 serv_puts("NOOP");
338                 serv_gets(buf);
339                 if (buf[3] == '*') {
340                         express_msgs = 1;
341                         if (ok_to_interrupt == 1) {
342                                 scr_printf("\r%64s\r", "");
343                                 print_express();
344                                 scr_printf("%s%c ", room_name,
345                                        room_prompt(room_flags));
346                                 scr_flush();
347                         }
348                 }
349         }
350 }
351
352 /* threaded nonblocking keepalive stuff starts here. I'm going for a simple
353    encapsulated interface; in theory there should be no need to touch these
354    globals outside of the async_ka_* functions. */
355
356 #ifdef THREADED_CLIENT
357 static pthread_t ka_thr_handle;
358 static int ka_thr_active = 0;
359 static int async_ka_enabled = 0;
360
361 static void *ka_thread(void *arg)
362 {
363         really_do_keepalive();
364         pthread_detach(ka_thr_handle);
365         ka_thr_active = 0;
366         return NULL;
367 }
368
369 /* start up a thread to handle a keepalive in the background */
370 static void async_ka_exec(void)
371 {
372         if (!ka_thr_active) {
373                 ka_thr_active = 1;
374                 if (pthread_create(&ka_thr_handle, NULL, ka_thread, NULL)) {
375                         perror("pthread_create");
376                         exit(1);
377                 }
378         }
379 }
380 #endif /* THREADED_CLIENT */
381
382 static void do_keepalive(void)
383 {
384         time_t now;
385
386         time(&now);
387         if ((now - idlet) < ((long) S_KEEPALIVE))
388                 return;
389
390         /* Do a space-backspace to keep telnet sessions from idling out */
391         scr_printf(" %c", 8);
392         scr_flush();
393
394 #ifdef THREADED_CLIENT
395         if (async_ka_enabled)
396                 async_ka_exec();
397         else
398 #endif
399                 really_do_keepalive();
400 }
401
402
403 /* Now the actual async-keepalve API that we expose to higher levels:
404    async_ka_start() and async_ka_end(). These do nothing when we don't have
405    threading enabled, so we avoid sprinkling ifdef's throughout the code. */
406
407 /* wait for a background keepalive to complete. this must be done before
408    attempting any further server requests! */
409 void async_ka_end(void)
410 {
411 #ifdef THREADED_CLIENT
412         if (ka_thr_active)
413                 pthread_join(ka_thr_handle, NULL);
414
415         async_ka_enabled--;
416 #endif
417 }
418
419 /* tell do_keepalive() that keepalives are asynchronous. */
420 void async_ka_start(void)
421 {
422 #ifdef THREADED_CLIENT
423         async_ka_enabled++;
424 #endif
425 }
426
427
428 int inkey(void)
429 {                               /* get a character from the keyboard, with   */
430         int a;                  /* the watchdog timer in effect if necessary */
431         fd_set rfds;
432         struct timeval tv;
433         time_t start_time, now;
434
435         scr_flush();
436         lines_printed = 0;
437         time(&start_time);
438
439         do {
440
441                 /* This loop waits for keyboard input.  If the keepalive
442                  * timer expires, it sends a keepalive to the server if
443                  * necessary and then waits again.
444                  */
445                 do {
446                         do_keepalive();
447
448                         FD_ZERO(&rfds);
449                         FD_SET(0, &rfds);
450                         tv.tv_sec = S_KEEPALIVE;
451                         tv.tv_usec = 0;
452
453                         time(&now);
454                         select(1, &rfds, NULL, NULL, &tv);
455                 } while (!FD_ISSET(0, &rfds));
456
457
458
459
460                 /* At this point, there's input, so fetch it.
461                  * (There's a hole in the bucket...)
462                  */
463                 a = scr_getc();
464                 if (a == 127)
465                         a = 8;
466                 if (a > 126)
467                         a = 0;
468                 if (a == 13)
469                         a = 10;
470                 if (((a != 4) && (a != 10) && (a != 8) && (a != NEXT_KEY) && (a != STOP_KEY))
471                     && ((a < 32) || (a > 126)))
472                         a = 0;
473         } while (a == 0);
474         return (a);
475 }
476
477
478 int yesno(void)
479 {                               /* Returns 1 for yes, 0 for no */
480         int a;
481         while (1) {
482                 a = inkey();
483                 a = tolower(a);
484                 if (a == 'y') {
485                         scr_printf("Yes\n");
486                         return (1);
487                 }
488                 if (a == 'n') {
489                         scr_printf("No\n");
490                         return (0);
491                 }
492         }
493 }
494
495 /* Returns 1 for yes, 0 for no, arg is default value */
496 int yesno_d(int d)
497 {
498         int a;
499         while (1) {
500                 a = inkey();
501                 a = tolower(a);
502                 if (a == 10)
503                         a = (d ? 'y' : 'n');
504                 if (a == 'y') {
505                         scr_printf("Yes\n");
506                         return (1);
507                 }
508                 if (a == 'n') {
509                         scr_printf("No\n");
510                         return (0);
511                 }
512         }
513 }
514
515
516
517
518 /* Gets a line from the terminal */
519 /* string == Pointer to string buffer */
520 /* lim == Maximum length - if negative, no-show */
521 void getline(char *string, int lim) 
522 {
523         int a, b;
524         char flag = 0;
525
526         if (lim < 0) {
527                 lim = (0 - lim);
528                 flag = 1;
529         }
530         strcpy(string, "");
531         gl_string = string;
532         async_ka_start();
533       GLA:a = inkey();
534         a = (a & 127);
535         if ((a == 8) && (strlen(string) == 0))
536                 goto GLA;
537         if ((a != 10) && (a != 8) && (strlen(string) == lim))
538                 goto GLA;
539         if ((a == 8) && (string[0] != 0)) {
540                 string[strlen(string) - 1] = 0;
541                 scr_putc(8);
542                 scr_putc(32);
543                 scr_putc(8);
544                 goto GLA;
545         }
546         if ((a == 10)) {
547                 scr_putc(10);
548                 async_ka_end();
549                 return;
550         }
551         if (a < 32)
552                 a = '.';
553         b = strlen(string);
554         string[b] = a;
555         string[b + 1] = 0;
556         if (flag == 0)
557                 scr_putc(a);
558         if (flag == 1)
559                 scr_putc('*');
560         goto GLA;
561 }
562
563
564 /*
565  * strprompt()  -  prompt for a string, print the existing value and
566  *                 allow the user to press return to keep it...
567  */
568 void strprompt(char *prompt, char *str, int len)
569 {
570         char buf[128];
571         print_express();
572         color(DIM_WHITE);
573         scr_printf("%s ", prompt);
574         color(DIM_MAGENTA);
575         scr_printf("[");
576         color(BRIGHT_MAGENTA);
577         scr_printf("%s", str);
578         color(DIM_MAGENTA);
579         scr_printf("]");
580         color(DIM_WHITE);
581         scr_printf(": ");
582         color(BRIGHT_CYAN);
583         getline(buf, len);
584         if (buf[0] != 0)
585                 strcpy(str, buf);
586         color(DIM_WHITE);
587 }
588
589 /*
590  * boolprompt()  -  prompt for a yes/no, print the existing value and
591  *                  allow the user to press return to keep it...
592  */
593 int boolprompt(char *prompt, int prev_val)
594 {
595         int r;
596
597         color(DIM_WHITE);
598         scr_printf("%s ", prompt);
599         color(DIM_MAGENTA);
600         scr_printf(" [");
601         color(BRIGHT_MAGENTA);
602         scr_printf("%s", (prev_val ? "Yes" : "No"));
603         color(DIM_MAGENTA);
604         scr_printf("]: ");
605         color(BRIGHT_CYAN);
606         r = (yesno_d(prev_val));
607         color(DIM_WHITE);
608         return r;
609 }
610
611 /* 
612  * intprompt()  -  like strprompt(), except for an integer
613  *                 (note that it RETURNS the new value!)
614  */
615 int intprompt(char *prompt, int ival, int imin, int imax)
616 {
617         char buf[16];
618         int i;
619         int p;
620
621         do {
622                 i = ival;
623                 snprintf(buf, sizeof buf, "%d", i);
624                 strprompt(prompt, buf, 15);
625                 i = atoi(buf);
626                 for (p=0; p<strlen(buf); ++p) {
627                         if ( (!isdigit(buf[p]))
628                            && ( (buf[p]!='-') || (p!=0) )  )
629                                 i = imin - 1;
630                 }
631                 if (i < imin)
632                         scr_printf("*** Must be no less than %d.\n", imin);
633                 if (i > imax)
634                         scr_printf("*** Must be no more than %d.\n", imax);
635         } while ((i < imin) || (i > imax));
636         return (i);
637 }
638
639 /* 
640  * newprompt()  -  prompt for a string with no existing value
641  *                 (clears out string buffer first)
642  */
643 void newprompt(char *prompt, char *str, int len)
644 {
645         color(BRIGHT_MAGENTA);
646         scr_printf("%s", prompt);
647         color(DIM_MAGENTA);
648         getline(str, len);
649         color(DIM_WHITE);
650 }
651
652
653 int lkey(void)
654 {                               /* returns a lower case value */
655         int a;
656         a = inkey();
657         if (isupper(a))
658                 a = tolower(a);
659         return (a);
660 }
661
662 /*
663  * parse the citadel.rc file
664  */
665 void load_command_set(void)
666 {
667         FILE *ccfile;
668         char buf[1024];
669         struct citcmd *cptr;
670         struct citcmd *lastcmd = NULL;
671         int a, d;
672         int b = 0;
673
674
675         /* first, set up some defaults for non-required variables */
676
677         strcpy(editor_path, "");
678         strcpy(printcmd, "");
679         strcpy(rc_username, "");
680         strcpy(rc_password, "");
681         rc_floor_mode = 0;
682         rc_exp_beep = 1;
683         rc_allow_attachments = 0;
684         rc_remember_passwords = 0;
685         strcpy(rc_exp_cmd, "");
686         rc_display_message_numbers = 0;
687         rc_force_mail_prompts = 0;
688         rc_ansi_color = 0;
689         strcpy(rc_url_cmd, "");
690         rc_encrypt = RC_DEFAULT;
691         rc_screen = RC_DEFAULT;
692         rc_alt_semantics = 0;
693
694         /* now try to open the citadel.rc file */
695
696         ccfile = NULL;
697         if (getenv("HOME") != NULL) {
698                 snprintf(buf, sizeof buf, "%s/.citadelrc", getenv("HOME"));
699                 ccfile = fopen(buf, "r");
700         }
701         if (ccfile == NULL) {
702                 ccfile = fopen("/usr/local/lib/citadel.rc", "r");
703         }
704         if (ccfile == NULL) {
705                 snprintf(buf, sizeof buf, "%s/citadel.rc", BBSDIR);
706                 ccfile = fopen(buf, "r");
707         }
708         if (ccfile == NULL) {
709                 ccfile = fopen("./citadel.rc", "r");
710         }
711         if (ccfile == NULL) {
712                 perror("commands: cannot open citadel.rc");
713                 logoff(errno);
714         }
715         while (fgets(buf, sizeof buf, ccfile) != NULL) {
716                 while ((strlen(buf) > 0) ? (isspace(buf[strlen(buf) - 1])) : 0)
717                         buf[strlen(buf) - 1] = 0;
718
719                 if (!strncasecmp(buf, "encrypt=", 8)) {
720                         if (!strcasecmp(&buf[8], "yes"))
721                                 rc_encrypt = RC_YES;
722                         else if (!strcasecmp(&buf[8], "no"))
723                                 rc_encrypt = RC_NO;
724                         else if (!strcasecmp(&buf[8], "default"))
725                                 rc_encrypt = RC_DEFAULT;
726                 }
727
728                 if (!strncasecmp(buf, "fullscreen=", 11)) {
729                         if (!strcasecmp(&buf[11], "yes"))
730                                 rc_screen = RC_YES;
731                         else if (!strcasecmp(&buf[11], "no"))
732                                 rc_screen = RC_NO;
733                 }
734
735                 if (!strncasecmp(buf, "editor=", 7))
736                         strcpy(editor_path, &buf[7]);
737
738                 if (!strncasecmp(buf, "printcmd=", 9))
739                         strcpy(printcmd, &buf[9]);
740
741                 if (!strncasecmp(buf, "expcmd=", 7))
742                         strcpy(rc_exp_cmd, &buf[7]);
743
744                 if (!strncasecmp(buf, "local_screen_dimensions=", 24))
745                         have_xterm = (char) atoi(&buf[24]);
746
747                 if (!strncasecmp(buf, "use_floors=", 11)) {
748                         if (!strcasecmp(&buf[11], "yes"))
749                                 rc_floor_mode = RC_YES;
750                         if (!strcasecmp(&buf[11], "no"))
751                                 rc_floor_mode = RC_NO;
752                         if (!strcasecmp(&buf[11], "default"))
753                                 rc_floor_mode = RC_DEFAULT;
754                 }
755                 if (!strncasecmp(buf, "beep=", 5)) {
756                         rc_exp_beep = atoi(&buf[5]);
757                 }
758                 if (!strncasecmp(buf, "allow_attachments=", 18)) {
759                         rc_allow_attachments = atoi(&buf[18]);
760                 }
761                 if (!strncasecmp(buf, "remember_passwords=", 19)) {
762                         rc_remember_passwords = atoi(&buf[19]);
763                 }
764                 if (!strncasecmp(buf, "display_message_numbers=", 24)) {
765                         rc_display_message_numbers = atoi(&buf[24]);
766                 }
767                 if (!strncasecmp(buf, "force_mail_prompts=", 19)) {
768                         rc_force_mail_prompts = atoi(&buf[19]);
769                 }
770                 if (!strncasecmp(buf, "ansi_color=", 11)) {
771                         if (!strncasecmp(&buf[11], "on", 2))
772                                 rc_ansi_color = 1;
773                         if (!strncasecmp(&buf[11], "auto", 4))
774                                 rc_ansi_color = 2;      /* autodetect */
775                         if (!strncasecmp(&buf[11], "user", 4))
776                                 rc_ansi_color = 3;      /* user config */
777                 }
778                 if (!strncasecmp(buf, "username=", 9))
779                         strcpy(rc_username, &buf[9]);
780
781                 if (!strncasecmp(buf, "password=", 9))
782                         strcpy(rc_password, &buf[9]);
783
784                 if (!strncasecmp(buf, "urlcmd=", 7))
785                         strcpy(rc_url_cmd, &buf[7]);
786
787                 if (!strncasecmp(buf, "alternate_semantics=", 20)) {
788                         if (!strncasecmp(&buf[11], "yes", 3))
789                                 rc_alt_semantics = 1;
790                         if (!strncasecmp(&buf[11], "no", 2))
791                                 rc_alt_semantics = 0;
792                 }
793                 if (!strncasecmp(buf, "cmd=", 4)) {
794                         strcpy(buf, &buf[4]);
795
796                         cptr = (struct citcmd *) malloc(sizeof(struct citcmd));
797
798                         cptr->c_cmdnum = atoi(buf);
799                         for (d = strlen(buf); d >= 0; --d)
800                                 if (buf[d] == ',')
801                                         b = d;
802                         strcpy(buf, &buf[b + 1]);
803
804                         cptr->c_axlevel = atoi(buf);
805                         for (d = strlen(buf); d >= 0; --d)
806                                 if (buf[d] == ',')
807                                         b = d;
808                         strcpy(buf, &buf[b + 1]);
809
810                         for (a = 0; a < 5; ++a)
811                                 cptr->c_keys[a][0] = 0;
812
813                         a = 0;
814                         b = 0;
815                         buf[strlen(buf) + 1] = 0;
816                         while (strlen(buf) > 0) {
817                                 b = strlen(buf);
818                                 for (d = strlen(buf); d >= 0; --d)
819                                         if (buf[d] == ',')
820                                                 b = d;
821                                 strncpy(cptr->c_keys[a], buf, b);
822                                 cptr->c_keys[a][b] = 0;
823                                 if (buf[b] == ',')
824                                         strcpy(buf, &buf[b + 1]);
825                                 else
826                                         strcpy(buf, "");
827                                 ++a;
828                         }
829
830                         cptr->next = NULL;
831                         if (cmdlist == NULL)
832                                 cmdlist = cptr;
833                         else
834                                 lastcmd->next = cptr;
835                         lastcmd = cptr;
836                 }
837         }
838         fclose(ccfile);
839 }
840
841
842
843 /*
844  * return the key associated with a command
845  */
846 char keycmd(char *cmdstr)
847 {
848         int a;
849
850         for (a = 0; a < strlen(cmdstr); ++a)
851                 if (cmdstr[a] == '&')
852                         return (tolower(cmdstr[a + 1]));
853         return (0);
854 }
855
856
857 /*
858  * Output the string from a key command without the ampersand
859  * "mode" should be set to 0 for normal or 1 for <C>ommand key highlighting
860  */
861 char *cmd_expand(char *strbuf, int mode)
862 {
863         int a;
864         static char exp[64];
865         char buf[1024];
866
867         strcpy(exp, strbuf);
868
869         for (a = 0; a < strlen(exp); ++a) {
870                 if (strbuf[a] == '&') {
871
872                         if (mode == 0) {
873                                 strcpy(&exp[a], &exp[a + 1]);
874                         }
875                         if (mode == 1) {
876                                 exp[a] = '<';
877                                 strcpy(buf, &exp[a + 2]);
878                                 exp[a + 2] = '>';
879                                 exp[a + 3] = 0;
880                                 strcat(exp, buf);
881                         }
882                 }
883                 if (!strncmp(&exp[a], "^r", 2)) {
884                         strcpy(buf, exp);
885                         strcpy(&exp[a], room_name);
886                         strcat(exp, &buf[a + 2]);
887                 }
888                 if (!strncmp(&exp[a], "^c", 2)) {
889                         exp[a] = ',';
890                         strcpy(&exp[a + 1], &exp[a + 2]);
891                 }
892         }
893
894         return (exp);
895 }
896
897
898
899 /*
900  * Comparison function to determine if entered commands match a
901  * command loaded from the config file.
902  */
903 int cmdmatch(char *cmdbuf, struct citcmd *cptr, int ncomp)
904 {
905         int a;
906         int cmdax;
907
908         cmdax = 0;
909         if (is_room_aide)
910                 cmdax = 1;
911         if (axlevel >= 6)
912                 cmdax = 2;
913
914         for (a = 0; a < ncomp; ++a) {
915                 if ((tolower(cmdbuf[a]) != keycmd(cptr->c_keys[a]))
916                     || (cptr->c_axlevel > cmdax))
917                         return (0);
918         }
919         return (1);
920 }
921
922
923 /*
924  * This function returns 1 if a given command requires a string input
925  */
926 int requires_string(struct citcmd *cptr, int ncomp)
927 {
928         int a;
929         char buf[64];
930
931         strcpy(buf, cptr->c_keys[ncomp - 1]);
932         for (a = 0; a < strlen(buf); ++a) {
933                 if (buf[a] == ':')
934                         return (1);
935         }
936         return (0);
937 }
938
939
940 /*
941  * Input a command at the main prompt.
942  * This function returns an integer command number.  If the command prompts
943  * for a string then it is placed in the supplied buffer.
944  */
945 int getcmd(char *argbuf)
946 {
947         char cmdbuf[5];
948         int cmdspaces[5];
949         int cmdpos;
950         int ch;
951         int a;
952         int got;
953         int this_lazy_cmd;
954         struct citcmd *cptr;
955
956         /*
957          * Starting a new command now, so set sigcaught to 0.  This variable
958          * is set to nonzero (usually NEXT_KEY or STOP_KEY) if a command has
959          * been interrupted by a keypress.
960          */
961         sigcaught = 0;
962
963         /* Switch color support on or off if we're in user mode */
964         if (rc_ansi_color == 3) {
965                 if (userflags & US_COLOR)
966                         enable_color = 1;
967                 else
968                         enable_color = 0;
969         }
970         /* if we're running in idiot mode, display a cute little menu */
971         IFNEXPERT formout("mainmenu");
972
973         print_express();        /* print express messages if there are any */
974         strcpy(argbuf, "");
975         cmdpos = 0;
976         for (a = 0; a < 5; ++a)
977                 cmdbuf[a] = 0;
978         /* now the room prompt... */
979         ok_to_interrupt = 1;
980         color(BRIGHT_WHITE);
981         scr_printf("\n%s", room_name);
982         color(DIM_WHITE);
983         scr_printf("%c ", room_prompt(room_flags));
984         scr_flush();
985
986         while (1) {
987                 ch = inkey();
988                 ok_to_interrupt = 0;
989
990                 /* Handle the backspace key, but only if there's something
991                  * to backspace over...
992                  */
993                 if ((ch == 8) && (cmdpos > 0)) {
994                         back(cmdspaces[cmdpos - 1] + 1);
995                         cmdbuf[cmdpos] = 0;
996                         --cmdpos;
997                 }
998                 /* Spacebar invokes "lazy traversal" commands */
999                 if ((ch == 32) && (cmdpos == 0)) {
1000                         this_lazy_cmd = next_lazy_cmd;
1001                         if (this_lazy_cmd == 13)
1002                                 next_lazy_cmd = 5;
1003                         if (this_lazy_cmd == 5)
1004                                 next_lazy_cmd = 13;
1005                         for (cptr = cmdlist; cptr != NULL; cptr = cptr->next) {
1006                                 if (cptr->c_cmdnum == this_lazy_cmd) {
1007                                         for (a = 0; a < 5; ++a)
1008                                                 if (cptr->c_keys[a][0] != 0)
1009                                                         scr_printf("%s ", cmd_expand(
1010                                                                                         cptr->c_keys[a], 0));
1011                                         scr_printf("\n");
1012                                         return (this_lazy_cmd);
1013                                 }
1014                         }
1015                         scr_printf("\n");
1016                         return (this_lazy_cmd);
1017                 }
1018                 /* Otherwise, process the command */
1019                 cmdbuf[cmdpos] = tolower(ch);
1020
1021                 for (cptr = cmdlist; cptr != NULL; cptr = cptr->next) {
1022                         if (cmdmatch(cmdbuf, cptr, cmdpos + 1)) {
1023
1024                                 scr_printf("%s", cmd_expand(cptr->c_keys[cmdpos], 0));
1025                                 cmdspaces[cmdpos] = strlen(
1026                                     cmd_expand(cptr->c_keys[cmdpos], 0));
1027                                 if (cmdpos < 4)
1028                                         if ((cptr->c_keys[cmdpos + 1]) != 0)
1029                                                 scr_putc(' ');
1030                                 ++cmdpos;
1031                         }
1032                 }
1033
1034                 for (cptr = cmdlist; cptr != NULL; cptr = cptr->next) {
1035                         if (cmdmatch(cmdbuf, cptr, 5)) {
1036                                 /* We've found our command. */
1037                                 if (requires_string(cptr, cmdpos)) {
1038                                         getline(argbuf, 32);
1039                                 } else {
1040                                         scr_printf("\n");
1041                                 }
1042
1043                                 /* If this command is one that changes rooms,
1044                                  * then the next lazy-command (space bar)
1045                                  * should be "read new" instead of "goto"
1046                                  */
1047                                 if ((cptr->c_cmdnum == 5)
1048                                     || (cptr->c_cmdnum == 6)
1049                                     || (cptr->c_cmdnum == 47)
1050                                     || (cptr->c_cmdnum == 52)
1051                                     || (cptr->c_cmdnum == 16)
1052                                     || (cptr->c_cmdnum == 20))
1053                                         next_lazy_cmd = 13;
1054
1055                                 return (cptr->c_cmdnum);
1056
1057                         }
1058                 }
1059
1060                 if (ch == '?') {
1061                         pprintf("\rOne of ...                         \n");
1062                         for (cptr = cmdlist; cptr != NULL; cptr = cptr->next) {
1063                                 if (cmdmatch(cmdbuf, cptr, cmdpos)) {
1064                                         for (a = 0; a < 5; ++a) {
1065                                                 pprintf("%s ", cmd_expand(cptr->c_keys[a], 1));
1066                                         }
1067                                         pprintf("\n");
1068                                 }
1069                         }
1070
1071                         pprintf("\n%s%c ", room_name, room_prompt(room_flags));
1072                         got = 0;
1073                         for (cptr = cmdlist; cptr != NULL; cptr = cptr->next) {
1074                                 if ((got == 0) && (cmdmatch(cmdbuf, cptr, cmdpos))) {
1075                                         for (a = 0; a < cmdpos; ++a) {
1076                                                 pprintf("%s ",
1077                                                        cmd_expand(cptr->c_keys[a], 0));
1078                                         }
1079                                         got = 1;
1080                                 }
1081                         }
1082                 }
1083         }
1084
1085 }
1086
1087
1088
1089
1090
1091 /*
1092  * set tty modes.  commands are:
1093  * 
1094  * 01- set to bbs mode
1095  * 2 - save current settings for later restoral
1096  * 3 - restore saved settings
1097  */
1098 #ifdef HAVE_TERMIOS_H
1099 void sttybbs(int cmd)
1100 {                               /* SysV version of sttybbs() */
1101         struct termios live;
1102         static struct termios saved_settings;
1103         static int last_cmd = 0;
1104
1105         if (cmd == SB_LAST)
1106                 cmd = last_cmd;
1107         else
1108                 last_cmd = cmd;
1109
1110         if ((cmd == 0) || (cmd == 1)) {
1111                 tcgetattr(0, &live);
1112                 live.c_iflag = ISTRIP | IXON | IXANY;
1113                 live.c_oflag = OPOST | ONLCR;
1114                 live.c_lflag = ISIG | NOFLSH;
1115
1116                 live.c_cc[VINTR] = (-1);
1117                 live.c_cc[VQUIT] = (-1);
1118
1119 #ifdef hpux
1120                 live.c_cc[VMIN] = 0;
1121                 live.c_cc[VTIME] = 0;
1122 #endif
1123
1124                 /* do we even need this stuff anymore? */
1125                 /* live.c_line=0; */
1126                 live.c_cc[VERASE] = 8;
1127                 live.c_cc[VKILL] = 24;
1128                 live.c_cc[VEOF] = 1;
1129                 live.c_cc[VEOL] = 255;
1130                 live.c_cc[VEOL2] = 0;
1131                 live.c_cc[VSTART] = 0;
1132                 tcsetattr(0, TCSADRAIN, &live);
1133         }
1134         if (cmd == 2) {
1135                 tcgetattr(0, &saved_settings);
1136         }
1137         if (cmd == 3) {
1138                 tcsetattr(0, TCSADRAIN, &saved_settings);
1139         }
1140 }
1141 #else
1142 void sttybbs(int cmd)
1143 {                               /* BSD version of sttybbs() */
1144         struct sgttyb live;
1145         static struct sgttyb saved_settings;
1146         static int last_cmd = 0;
1147
1148         if (cmd == SB_LAST)
1149                 cmd = last_cmd;
1150         else
1151                 last_cmd = cmd;
1152
1153         if ((cmd == 0) || (cmd == 1)) {
1154                 gtty(0, &live);
1155                 live.sg_flags |= CBREAK;
1156                 live.sg_flags |= CRMOD;
1157                 live.sg_flags |= NL1;
1158                 live.sg_flags &= ~ECHO;
1159                 if (cmd == 1)
1160                         live.sg_flags |= NOFLSH;
1161                 stty(0, &live);
1162         }
1163         if (cmd == 2) {
1164                 gtty(0, &saved_settings);
1165         }
1166         if (cmd == 3) {
1167                 stty(0, &saved_settings);
1168         }
1169 }
1170 #endif
1171
1172
1173 /*
1174  * display_help()  -  help file viewer
1175  */
1176 void display_help(char *name)
1177 {
1178         formout(name);
1179 }
1180
1181
1182 /*
1183  * fmout()  -  Citadel text formatter and paginator
1184  */
1185 int fmout(
1186         int width,      /* screen width to use */
1187         FILE *fpin,     /* file to read from, or NULL to read from server */
1188         FILE *fpout,    /* File to write to, or NULL to write to screen */
1189         char pagin,     /* nonzero if we should use the paginator */
1190         int height,     /* screen height to use */
1191         int starting_lp,/* starting value for lines_printed, -1 for global */
1192         char subst)     /* nonzero if we should use hypertext mode */
1193 {
1194         int a, b, c, d, old;
1195         int real = (-1);
1196         char aaa[140];
1197         char buffer[512];
1198         int eof_flag = 0;
1199
1200         num_urls = 0;   /* Start with a clean slate of embedded URL's */
1201
1202         if (starting_lp >= 0) {
1203                 lines_printed = starting_lp;
1204         }
1205         strcpy(aaa, "");
1206         old = 255;
1207         strcpy(buffer, "");
1208         c = 1;                  /* c is the current pos */
1209
1210 FMTA:   while ((eof_flag == 0) && (strlen(buffer) < 126)) {
1211                 if (fpin != NULL) {     /* read from file */
1212                         if (feof(fpin))
1213                                 eof_flag = 1;
1214                         if (eof_flag == 0) {
1215                                 a = getc(fpin);
1216                                 buffer[strlen(buffer) + 1] = 0;
1217                                 buffer[strlen(buffer)] = a;
1218                         }
1219                 } else {        /* read from server */
1220                         d = strlen(buffer);
1221                         serv_gets(&buffer[d]);
1222                         while ((!isspace(buffer[d])) && (isspace(buffer[strlen(buffer) - 1])))
1223                                 buffer[strlen(buffer) - 1] = 0;
1224                         if (!strcmp(&buffer[d], "000")) {
1225                                 buffer[d] = 0;
1226                                 eof_flag = 1;
1227                                 while (isspace(buffer[strlen(buffer) - 1]))
1228                                         buffer[strlen(buffer) - 1] = 0;
1229                         }
1230                         d = strlen(buffer);
1231                         buffer[d] = 10;
1232                         buffer[d + 1] = 0;
1233                 }
1234         }
1235
1236         if ( (!strncasecmp(buffer, "http://", 7))
1237            || (!strncasecmp(buffer, "ftp://", 6)) ) {
1238                 safestrncpy(urls[num_urls], buffer, (SIZ-1));
1239                 for (a=0; a<strlen(urls[num_urls]); ++a) {
1240                         b = urls[num_urls][a];
1241                         if ( (b==' ') || (b==')') || (b=='>') || (b==10)
1242                            || (b==13) || (b==9) || (b=='\"') )
1243                                 urls[num_urls][a] = 0;
1244                 }
1245                 ++num_urls;
1246         }
1247
1248         buffer[strlen(buffer) + 1] = 0;
1249         a = buffer[0];
1250         strcpy(buffer, &buffer[1]);
1251
1252         old = real;
1253         real = a;
1254         if (a <= 0)
1255                 goto FMTEND;
1256
1257         if (((a == 13) || (a == 10)) && (old != 13) && (old != 10))
1258                 a = 32;
1259         if (((old == 13) || (old == 10)) && (isspace(real))) {
1260                 if (fpout) {
1261                         fprintf(fpout, "\n");
1262                 } else {
1263                         scr_printf("\n");
1264                         ++lines_printed;
1265                         lines_printed = checkpagin(lines_printed, pagin, height);
1266                 }
1267                 c = 1;
1268         }
1269         if (a > 126)
1270                 goto FMTA;
1271
1272         if (a > 32) {
1273                 if (((strlen(aaa) + c) > (width - 1)) && (strlen(aaa) > (width - 1))) {
1274                         if (fpout) {
1275                                 fprintf(fpout, "\n%s", aaa);
1276                         } else {
1277                                 scr_printf("\n%s", aaa);
1278                                 ++lines_printed;
1279                                 lines_printed = checkpagin(lines_printed, pagin, height);
1280                         }
1281                         c = strlen(aaa);
1282                         aaa[0] = 0;
1283                 }
1284                 b = strlen(aaa);
1285                 aaa[b] = a;
1286                 aaa[b + 1] = 0;
1287         }
1288         if (a == 32) {
1289                 if ((strlen(aaa) + c) > (width - 1)) {
1290                         c = 1;
1291                         if (fpout) {
1292                                 fprintf(fpout, "\n");
1293                         } else {
1294                                 scr_printf("\n");
1295                                 ++lines_printed;
1296                                 lines_printed = checkpagin(lines_printed, pagin, height);
1297                         }
1298                 }
1299                 if (fpout) {
1300                         fprintf(fpout, "%s ", aaa);
1301                 } else {
1302                         scr_printf("%s ", aaa);
1303                 }
1304                 ++c;
1305                 c = c + strlen(aaa);
1306                 strcpy(aaa, "");
1307                 goto FMTA;
1308         }
1309         if ((a == 13) || (a == 10)) {
1310                 if (fpout) {
1311                         fprintf(fpout, "%s\n", aaa);
1312                 } else {
1313                         scr_printf("%s\n", aaa);
1314                         ++lines_printed;
1315                         lines_printed = checkpagin(lines_printed, pagin, height);
1316                 }
1317                 c = 1;
1318                 if (sigcaught) goto OOPS;
1319                 strcpy(aaa, "");
1320                 goto FMTA;
1321         }
1322         goto FMTA;
1323
1324         /* keypress caught; drain the server */
1325 OOPS:   do {
1326                 serv_gets(aaa);
1327         } while (strcmp(aaa, "000"));
1328
1329 FMTEND:
1330         if (fpout) {
1331                 fprintf(fpout, "\n");
1332         } else {
1333                 scr_printf("\n");
1334                 ++lines_printed;
1335                 lines_printed = checkpagin(lines_printed, pagin, height);
1336         }
1337         return (sigcaught);
1338 }
1339
1340
1341 /*
1342  * support ANSI color if defined
1343  */
1344 void color(int colornum)
1345 {
1346         static int is_bold = 0;
1347         static int hold_color, current_color;
1348
1349         if (colornum == COLOR_PUSH) {
1350                 hold_color = current_color;
1351                 return;
1352         }
1353
1354         if (colornum == COLOR_POP) {
1355                 color(hold_color);
1356                 return;
1357         }
1358
1359         current_color = colornum;
1360         if (enable_color) {
1361 #ifdef HAVE_CURSES_H
1362                 if (scr_color(colornum))
1363                         return;
1364 #endif
1365                 /* When switching to dim white, actually output an 'original
1366                  * pair' sequence -- this looks better on black-on-white
1367                  * terminals.
1368                  */
1369                 if (colornum == DIM_WHITE)
1370                         printf("\033[39;49m");
1371                 else
1372                         printf("\033[3%d;40m", (colornum & 7));
1373
1374                 if ((colornum >= 8) && (is_bold == 0)) {
1375                         printf("\033[1m");
1376                         is_bold = 1;
1377                 } else if ((colornum < 8) && (is_bold == 1)) {
1378                         printf("\033[0m");
1379                         is_bold = 0;
1380                 }
1381                 scr_flush();
1382         }
1383 }
1384
1385 void cls(int colornum)
1386 {
1387         if (enable_color) {
1388                 printf("\033[4%dm\033[2J\033[H\033[0m", colornum);
1389                 scr_flush();
1390         }
1391 }
1392
1393
1394 /*
1395  * Detect whether ANSI color is available (answerback)
1396  */
1397 void send_ansi_detect(void)
1398 {
1399         if (rc_ansi_color == 2) {
1400                 printf("\033[c");
1401                 scr_flush();
1402                 time(&AnsiDetect);
1403         }
1404 }
1405
1406 void look_for_ansi(void)
1407 {
1408         fd_set rfds;
1409         struct timeval tv;
1410         char abuf[512];
1411         time_t now;
1412         int a;
1413
1414         if (rc_ansi_color == 0) {
1415                 enable_color = 0;
1416         } else if (rc_ansi_color == 1) {
1417                 enable_color = 1;
1418         } else if (rc_ansi_color == 2) {
1419
1420                 /* otherwise, do the auto-detect */
1421
1422                 strcpy(abuf, "");
1423
1424                 time(&now);
1425                 if ((now - AnsiDetect) < 2)
1426                         sleep(1);
1427
1428                 do {
1429                         FD_ZERO(&rfds);
1430                         FD_SET(0, &rfds);
1431                         tv.tv_sec = 0;
1432                         tv.tv_usec = 1;
1433
1434                         select(1, &rfds, NULL, NULL, &tv);
1435                         if (FD_ISSET(0, &rfds)) {
1436                                 abuf[strlen(abuf) + 1] = 0;
1437                                 read(0, &abuf[strlen(abuf)], 1);
1438                         }
1439                 } while (FD_ISSET(0, &rfds));
1440
1441                 for (a = 0; a < strlen(abuf); ++a) {
1442                         if ((abuf[a] == 27) && (abuf[a + 1] == '[')
1443                             && (abuf[a + 2] == '?')) {
1444                                 enable_color = 1;
1445                         }
1446                 }
1447         }
1448 }
1449
1450
1451 /*
1452  * Display key options (highlight hotkeys inside angle brackets)
1453  */
1454 void keyopt(char *buf) {
1455         int i;
1456
1457         color(DIM_WHITE);
1458         for (i=0; i<strlen(buf); ++i) {
1459                 if (buf[i]=='<') {
1460                         scr_putc(buf[i]);
1461                         color(BRIGHT_MAGENTA);
1462                 } else {
1463                         if (buf[i]=='>') {
1464                                 color(DIM_WHITE);
1465                         }
1466                         scr_putc(buf[i]);
1467                 }
1468         }
1469         color(DIM_WHITE);
1470 }
1471
1472
1473
1474 /*
1475  * Present a key-menu line choice type of thing
1476  */
1477 char keymenu(char *menuprompt, char *menustring) {
1478         int i, c, a;
1479         int choices;
1480         int do_prompt = 0;
1481         char buf[1024];
1482         int ch;
1483         int display_prompt = 1;
1484
1485         choices = num_tokens(menustring, '|');
1486
1487         if (menuprompt != NULL) do_prompt = 1;
1488         if (menuprompt != NULL) if (strlen(menuprompt)==0) do_prompt = 0;
1489
1490         while (1) {
1491                 if (display_prompt) {
1492                         if (do_prompt) {
1493                                 scr_printf("%s ", menuprompt);
1494                         } 
1495                         else {
1496                                 for (i=0; i<choices; ++i) {
1497                                         extract(buf, menustring, i);
1498                                         keyopt(buf);
1499                                         scr_printf(" ");
1500                                 }
1501                         }
1502                         scr_printf(" -> ");
1503                         display_prompt = 0;
1504                 }
1505                 ch = lkey();
1506         
1507                 if ( (do_prompt) && (ch=='?') ) {
1508                         scr_printf("\rOne of...                               ");
1509                         scr_printf("                                      \n");
1510                         for (i=0; i<choices; ++i) {
1511                                 extract(buf, menustring, i);
1512                                 scr_printf("   ");
1513                                 keyopt(buf);
1514                                 scr_printf("\n");
1515                         }
1516                         scr_printf("\n");
1517                         display_prompt = 1;
1518                 }
1519
1520                 for (i=0; i<choices; ++i) {
1521                         extract(buf, menustring, i);
1522                         for (c=1; c<strlen(buf); ++c) {
1523                                 if ( (ch == tolower(buf[c]))
1524                                    && (buf[c-1]=='<')
1525                                    && (buf[c+1]=='>') ) {
1526                                         for (a=0; a<strlen(buf); ++a) {
1527                                                 if ( (a!=(c-1)) && (a!=(c+1))) {
1528                                                         scr_putc(buf[a]);
1529                                                 }
1530                                         }
1531                                         scr_printf("\n\n");
1532                                         return ch;
1533                                 }
1534                         }
1535                 }
1536         }
1537 }