Back to index

citadel  8.12
commands.c
Go to the documentation of this file.
00001 /*
00002  * This file contains functions which implement parts of the
00003  * text-mode user interface.
00004  *
00005  * Copyright (c) 1987-2012 by the citadel.org team
00006  *
00007  * This program is open source software; you can redistribute it and/or modify
00008  * it under the terms of the GNU General Public License version 3.
00009  *
00010  * This program is distributed in the hope that it will be useful,
00011  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00012  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00013  * GNU General Public License for more details.
00014  */
00015 
00016 #include "sysdep.h"
00017 #include <stdlib.h>
00018 #include <unistd.h>
00019 #include <stdio.h>
00020 #include <fcntl.h>
00021 #include <ctype.h>
00022 #include <string.h>
00023 #include <sys/types.h>
00024 
00025 #if TIME_WITH_SYS_TIME
00026 # include <sys/time.h>
00027 # include <time.h>
00028 #else
00029 # if HAVE_SYS_TIME_H
00030 #  include <sys/time.h>
00031 # else
00032 #  include <time.h>
00033 # endif
00034 #endif
00035 
00036 #ifdef HAVE_TERMIOS_H
00037 #include <termios.h>
00038 #else
00039 #include <sgtty.h>
00040 #endif
00041 
00042 #ifdef HAVE_SYS_SELECT_H
00043 #include <sys/select.h>
00044 #endif
00045 
00046 #ifdef THREADED_CLIENT
00047 #include <pthread.h>
00048 #endif
00049 
00050 #include <signal.h>
00051 #include <errno.h>
00052 #include <stdarg.h>
00053 #include <libcitadel.h>
00054 #include "citadel.h"
00055 #include "citadel_ipc.h"
00056 #include "commands.h"
00057 #include "messages.h"
00058 #include "citadel_decls.h"
00059 #include "routines.h"
00060 #include "routines2.h"
00061 #include "rooms.h"
00062 #include "client_chat.h"
00063 #include "citadel_dirs.h"
00064 #include "tuiconfig.h"
00065 #ifndef HAVE_SNPRINTF
00066 #include "snprintf.h"
00067 #endif
00068 #include "screen.h"
00069 #include "ecrash.h"
00070 
00071 struct citcmd {
00072        struct citcmd *next;
00073        int c_cmdnum;
00074        int c_axlevel;
00075        char c_keys[5][64];
00076 };
00077 
00078 #define IFNEXPERT if ((userflags&US_EXPERT)==0)
00079 
00080 
00081 int rc_exp_beep;
00082 char rc_exp_cmd[1024];
00083 int rc_allow_attachments;
00084 int rc_display_message_numbers;
00085 int rc_force_mail_prompts;
00086 int rc_remember_passwords;
00087 int rc_ansi_color;
00088 int rc_color_use_bg;
00089 int rc_prompt_control = 0;
00090 time_t rc_idle_threshold = (time_t)900;
00091 char rc_url_cmd[SIZ];
00092 char rc_open_cmd[SIZ];
00093 char rc_gotmail_cmd[SIZ];
00094 
00095 int next_lazy_cmd = 5;
00096 
00097 extern int screenwidth, screenheight;
00098 extern int termn8;
00099 extern CtdlIPC *ipc_for_signal_handlers;  /* KLUDGE cover your eyes */
00100 
00101 struct citcmd *cmdlist = NULL;
00102 
00103 
00104 /* these variables are local to this module */
00105 char keepalives_enabled = KA_YES;  /* send NOOPs to server when idle */
00106 int ok_to_interrupt = 0;           /* print instant msgs asynchronously */
00107 time_t AnsiDetect;                 /* when did we send the detect code? */
00108 int enable_color = 0;                     /* nonzero for ANSI color */
00109 
00110 
00111 
00112 
00113 /*
00114  * If an interesting key has been pressed, return its value, otherwise 0
00115  */
00116 char was_a_key_pressed(void) {
00117        fd_set rfds;
00118        struct timeval tv;
00119        int the_character;
00120        int retval;
00121 
00122        FD_ZERO(&rfds);
00123        FD_SET(0, &rfds);
00124        tv.tv_sec = 0;
00125        tv.tv_usec = 0;
00126        retval = select(1, &rfds, NULL, NULL, &tv); 
00127 
00128        /* Careful!  Disable keepalives during keyboard polling; we're probably
00129         * in the middle of a data transfer from the server, in which case
00130         * sending a NOOP would throw the client protocol out of sync.
00131         */
00132        if ((retval > 0) && FD_ISSET(0, &rfds)) {
00133               set_keepalives(KA_NO);
00134               the_character = inkey();
00135               set_keepalives(KA_YES);
00136        }
00137        else {
00138               the_character = 0;
00139        }
00140        return(the_character);
00141 }
00142 
00143 
00144 
00145 
00146 
00147 /*
00148  * print_instant()  -  print instant messages if there are any
00149  */
00150 void print_instant(void)
00151 {
00152        char buf[1024];
00153        FILE *outpipe;
00154        time_t timestamp;
00155        struct tm stamp;
00156        int flags = 0;
00157        char sender[64];
00158        char node[64];
00159        char *listing = NULL;
00160        int r;               /* IPC result code */
00161 
00162        if (instant_msgs == 0)
00163               return;
00164 
00165        if (rc_exp_beep) {
00166               ctdl_beep();
00167        }
00168        if (IsEmptyStr(rc_exp_cmd)) {
00169               color(BRIGHT_RED);
00170               scr_printf("\r---");
00171        }
00172        
00173        while (instant_msgs != 0) {
00174               r = CtdlIPCGetInstantMessage(ipc_for_signal_handlers, &listing, buf);
00175               if (r / 100 != 1)
00176                      return;
00177        
00178               instant_msgs = extract_int(buf, 0);
00179               timestamp = extract_long(buf, 1);
00180               flags = extract_int(buf, 2);
00181               extract_token(sender, buf, 3, '|', sizeof sender);
00182               extract_token(node, buf, 4, '|', sizeof node);
00183               strcpy(last_paged, sender);
00184        
00185               localtime_r(&timestamp, &stamp);
00186 
00187               /* If the page is a Logoff Request, honor it. */
00188               if (flags & 2) {
00189                      termn8 = 1;
00190                      return;
00191               }
00192        
00193               if (!IsEmptyStr(rc_exp_cmd)) {
00194                      outpipe = popen(rc_exp_cmd, "w");
00195                      if (outpipe != NULL) {
00196                             /* Header derived from flags */
00197                             if (flags & 2)
00198                                    fprintf(outpipe,
00199                                           "Please log off now, as requested ");
00200                             else if (flags & 1)
00201                                    fprintf(outpipe, "Broadcast message ");
00202                             else if (flags & 4)
00203                                    fprintf(outpipe, "Chat request ");
00204                             else
00205                                    fprintf(outpipe, "Message ");
00206                             /* Timestamp.  Can this be improved? */
00207                             if (stamp.tm_hour == 0 || stamp.tm_hour == 12)
00208                                    fprintf(outpipe, "at 12:%02d%cm",
00209                                           stamp.tm_min, 
00210                                           stamp.tm_hour ? 'p' : 'a');
00211                             else if (stamp.tm_hour > 12)              /* pm */
00212                                    fprintf(outpipe, "at %d:%02dpm",
00213                                           stamp.tm_hour - 12,
00214                                           stamp.tm_min);
00215                             else                               /* am */
00216                                    fprintf(outpipe, "at %d:%02dam",
00217                                           stamp.tm_hour, stamp.tm_min);
00218                             fprintf(outpipe, " from %s", sender);
00219                             if (strncmp(ipc_for_signal_handlers->ServInfo.nodename, node, 32))
00220                                    fprintf(outpipe, " @%s", node);
00221                             fprintf(outpipe, ":\n%s\n", listing);
00222                             pclose(outpipe);
00223                             if (instant_msgs == 0)
00224                                    return;
00225                             continue;
00226                      }
00227               }
00228               /* fall back to built-in instant message display */
00229               scr_printf("\n");
00230 
00231               /* Header derived from flags */
00232               if (flags & 2)
00233                      scr_printf("Please log off now, as requested ");
00234               else if (flags & 1)
00235                      scr_printf("Broadcast message ");
00236               else if (flags & 4)
00237                      scr_printf("Chat request ");
00238               else
00239                      scr_printf("Message ");
00240        
00241               /* Timestamp.  Can this be improved? */
00242               if (stamp.tm_hour == 0 || stamp.tm_hour == 12)/* 12am/12pm */
00243                      scr_printf("at 12:%02d%cm", stamp.tm_min, 
00244                             stamp.tm_hour ? 'p' : 'a');
00245               else if (stamp.tm_hour > 12)                     /* pm */
00246                      scr_printf("at %d:%02dpm",
00247                             stamp.tm_hour - 12, stamp.tm_min);
00248               else                                      /* am */
00249                      scr_printf("at %d:%02dam", stamp.tm_hour, stamp.tm_min);
00250               
00251               /* Sender */
00252               scr_printf(" from %s", sender);
00253        
00254               /* Remote node, if any */
00255               if (strncmp(ipc_for_signal_handlers->ServInfo.nodename, node, 32))
00256                      scr_printf(" @%s", node);
00257        
00258               scr_printf(":\n");
00259               fmout(screenwidth, NULL, listing, NULL, 0);
00260               free(listing);
00261 
00262        }
00263        scr_printf("\n---\n");
00264        color(BRIGHT_WHITE);
00265 
00266 
00267 }
00268 
00269 
00270 void set_keepalives(int s)
00271 {
00272        keepalives_enabled = (char) s;
00273 }
00274 
00275 /* 
00276  * This loop handles the "keepalive" messages sent to the server when idling.
00277  */
00278 
00279 static time_t idlet = 0;
00280 static void really_do_keepalive(void) {
00281 
00282        time(&idlet);
00283 
00284        /* This may sometimes get called before we are actually connected
00285         * to the server.  Don't do anything if we aren't connected. -IO
00286         */
00287        if (!ipc_for_signal_handlers)
00288               return;
00289 
00290        /* If full keepalives are enabled, send a NOOP to the server and
00291         * wait for a response.
00292         */
00293        if (keepalives_enabled == KA_YES) {
00294               CtdlIPCNoop(ipc_for_signal_handlers);
00295               if (instant_msgs > 0) {
00296                      if (ok_to_interrupt == 1) {
00297                             scr_printf("\r%64s\r", "");
00298                             print_instant();
00299                             scr_printf("%s%c ", room_name,
00300                                    room_prompt(room_flags));
00301                             scr_flush();
00302                      }
00303               }
00304        }
00305 
00306        /* If half keepalives are enabled, send a QNOP to the server (if the
00307         * server supports it) and then do nothing.
00308         */
00309        if ( (keepalives_enabled == KA_HALF)
00310           && (ipc_for_signal_handlers->ServInfo.supports_qnop > 0) ) {
00311               CtdlIPC_chat_send(ipc_for_signal_handlers, "QNOP");
00312        }
00313 }
00314 
00315 /* threaded nonblocking keepalive stuff starts here. I'm going for a simple
00316    encapsulated interface; in theory there should be no need to touch these
00317    globals outside of the async_ka_* functions. */
00318 
00319 #ifdef THREADED_CLIENT
00320 static pthread_t ka_thr_handle;
00321 static int ka_thr_active = 0;
00322 static int async_ka_enabled = 0;
00323 
00324 static void *ka_thread(void *arg)
00325 {
00326 #ifdef HAVE_BACKTRACE
00327        char threadName[256];
00328 
00329        // Set up our name
00330        sprintf(threadName, "ka_Thread n");
00331 
00332        // Register for tracing
00333        eCrash_RegisterThread(threadName, 0);
00334 #endif
00335        really_do_keepalive();
00336        pthread_detach(ka_thr_handle);
00337        ka_thr_active = 0;
00338        
00339 #ifdef HAVE_BACKTRACE
00340        eCrash_UnregisterThread();
00341 #endif
00342        return NULL;
00343 }
00344 
00345 /* start up a thread to handle a keepalive in the background */
00346 static void async_ka_exec(void)
00347 {
00348        if (!ka_thr_active) {
00349               ka_thr_active = 1;
00350               if (pthread_create(&ka_thr_handle, NULL, ka_thread, NULL)) {
00351                      perror("pthread_create");
00352                      exit(1);
00353               }
00354        }
00355 }
00356 #endif /* THREADED_CLIENT */
00357 
00358 /* I changed this from static to not because I need to call it from
00359    screen.c, either that or make something in screen.c not static.
00360    Fix it how you like. Why all the staticness? stu */
00361    
00362 void do_keepalive(void)
00363 {
00364        time_t now;
00365 
00366        time(&now);
00367        if ((now - idlet) < ((long) S_KEEPALIVE))
00368               return;
00369 
00370        /* Do a space-backspace to keep telnet sessions from idling out */
00371        scr_printf(" %c", 8);
00372        scr_flush();
00373 
00374 #ifdef THREADED_CLIENT
00375        if (async_ka_enabled)
00376               async_ka_exec();
00377        else
00378 #endif
00379               really_do_keepalive();
00380 }
00381 
00382 
00383 /* Now the actual async-keepalve API that we expose to higher levels:
00384    async_ka_start() and async_ka_end(). These do nothing when we don't have
00385    threading enabled, so we avoid sprinkling ifdef's throughout the code. */
00386 
00387 /* wait for a background keepalive to complete. this must be done before
00388    attempting any further server requests! */
00389 void async_ka_end(void)
00390 {
00391 #ifdef THREADED_CLIENT
00392        if (ka_thr_active)
00393               pthread_join(ka_thr_handle, NULL);
00394 
00395        async_ka_enabled--;
00396 #endif
00397 }
00398 
00399 /* tell do_keepalive() that keepalives are asynchronous. */
00400 void async_ka_start(void)
00401 {
00402 #ifdef THREADED_CLIENT
00403        async_ka_enabled++;
00404 #endif
00405 }
00406 
00407 
00408 int inkey(void)
00409 {                           /* get a character from the keyboard, with   */
00410        int a;               /* the watchdog timer in effect if necessary */
00411        fd_set rfds;
00412        struct timeval tv;
00413        time_t start_time;
00414 
00415        scr_flush();
00416        time(&start_time);
00417 
00418        do {
00419               /* This loop waits for keyboard input.  If the keepalive
00420                * timer expires, it sends a keepalive to the server if
00421                * necessary and then waits again.
00422                */
00423               do {
00424                      do_keepalive();
00425 
00426                      FD_ZERO(&rfds);
00427                      FD_SET(0, &rfds);
00428                      tv.tv_sec = S_KEEPALIVE;
00429                      tv.tv_usec = 0;
00430 
00431                      select(1, &rfds, NULL, NULL, &tv);
00432               } while (!FD_ISSET(0, &rfds));
00433 
00434               /* At this point, there's input, so fetch it.
00435                * (There's a hole in the bucket...)
00436                */
00437               a = scr_getc(SCR_BLOCK);
00438               if (a == 127) {
00439                      a = 8;
00440               }
00441               if (a == 13) {
00442                      a = 10;
00443               }
00444        } while (a == 0);
00445        return (a);
00446 }
00447 
00448 
00449 int yesno(void)
00450 {                           /* Returns 1 for yes, 0 for no */
00451        int a;
00452        while (1) {
00453               a = inkey();
00454               a = tolower(a);
00455               if (a == 'y') {
00456                      scr_printf("Yes\n");
00457                      return (1);
00458               }
00459               if (a == 'n') {
00460                      scr_printf("No\n");
00461                      return (0);
00462               }
00463        }
00464 }
00465 
00466 /* Returns 1 for yes, 0 for no, arg is default value */
00467 int yesno_d(int d)
00468 {
00469        int a;
00470        while (1) {
00471               a = inkey();
00472               a = tolower(a);
00473               if (a == 10)
00474                      a = (d ? 'y' : 'n');
00475               if (a == 'y') {
00476                      scr_printf("Yes\n");
00477                      return (1);
00478               }
00479               if (a == 'n') {
00480                      scr_printf("No\n");
00481                      return (0);
00482               }
00483        }
00484 }
00485 
00486 
00487 
00488 
00489 /*
00490  * Function to read a line of text from the terminal.
00491  *
00492  * string            Pointer to string buffer
00493  * lim               Maximum length
00494  * noshow            Echo asterisks instead of keystrokes?
00495  * bs                Allow backspacing out of the prompt? (returns -1 if this happens)
00496  *
00497  * returns: string length
00498  */
00499 int ctdl_getline(char *string, int lim, int noshow, int bs)
00500 {
00501        int pos = strlen(string);
00502        int ch;
00503 
00504        async_ka_start();
00505        if (noshow && !IsEmptyStr(string)) {
00506               int num_stars = strlen(string);
00507               while (num_stars--) {
00508                      scr_putc('*');
00509               }
00510        }
00511        else {
00512               scr_printf("%s", string);
00513        }
00514 
00515        while(1) {
00516               ch = inkey();
00517 
00518               if ((ch == 8)  && (pos > 0)) {                          /* backspace */
00519                      --pos;
00520                      scr_putc(8); scr_putc(32); scr_putc(8);
00521               }
00522 
00523               else if ((ch == 8) && (pos == 0) && (bs)) {             /* backspace out of the prompt */
00524                      async_ka_end();
00525                      return(-1);
00526               }
00527 
00528               else if ((ch == 23) && (pos > 0)) {                     /* Ctrl-W deletes a word */
00529                      while ((pos > 0) && !isspace(string[pos])) {
00530                             --pos;
00531                             scr_putc(8); scr_putc(32); scr_putc(8);
00532                      }
00533                      while ((pos > 0) && !isspace(string[pos-1])) {
00534                             --pos;
00535                             scr_putc(8); scr_putc(32); scr_putc(8);
00536                      }
00537               }
00538 
00539               else if (ch == 10) {                             /* return */
00540                      string[pos] = 0;
00541                      scr_printf("\n");
00542                      async_ka_end();
00543                      return(pos);
00544               }
00545 
00546               else if (isprint(ch)) {                                 /* payload characters */
00547                      scr_putc((noshow ? '*' : ch));
00548                      string[pos] = ch;
00549                      ++pos;
00550               }
00551        }
00552 }
00553 
00554 
00555 /* 
00556  * newprompt()              prompt for a string, print the existing value, and
00557  *                   allow the user to press return to keep it...
00558  *                   If len is negative, pass the "noshow" flag to ctdl_getline()
00559  */
00560 void strprompt(char *prompt, char *str, int len)
00561 {
00562        print_instant();
00563        color(DIM_WHITE);
00564        scr_printf("%s", prompt);
00565        color(DIM_WHITE);
00566        scr_printf(": ");
00567        color(BRIGHT_CYAN);
00568        ctdl_getline(str, abs(len), (len<0), 0);
00569        color(DIM_WHITE);
00570 }
00571 
00572 /*
00573  * boolprompt()  -  prompt for a yes/no, print the existing value and
00574  *                  allow the user to press return to keep it...
00575  */
00576 int boolprompt(char *prompt, int prev_val)
00577 {
00578        int r;
00579 
00580        color(DIM_WHITE);
00581        scr_printf("%s ", prompt);
00582        color(DIM_MAGENTA);
00583        scr_printf("[");
00584        color(BRIGHT_MAGENTA);
00585        scr_printf("%s", (prev_val ? "Yes" : "No"));
00586        color(DIM_MAGENTA);
00587        scr_printf("]: ");
00588        color(BRIGHT_CYAN);
00589        r = (yesno_d(prev_val));
00590        color(DIM_WHITE);
00591        return r;
00592 }
00593 
00594 /* 
00595  * intprompt()  -  like strprompt(), except for an integer
00596  *                 (note that it RETURNS the new value!)
00597  */
00598 int intprompt(char *prompt, int ival, int imin, int imax)
00599 {
00600        char buf[16];
00601        int i;
00602        int p;
00603 
00604        do {
00605               i = ival;
00606               snprintf(buf, sizeof buf, "%d", i);
00607               strprompt(prompt, buf, 15);
00608               i = atoi(buf);
00609               for (p=0; !IsEmptyStr(&buf[p]); ++p) {
00610                      if ( (!isdigit(buf[p]))
00611                         && ( (buf[p]!='-') || (p!=0) )  )
00612                             i = imin - 1;
00613               }
00614               if (i < imin)
00615                      scr_printf("*** Must be no less than %d.\n", imin);
00616               if (i > imax)
00617                      scr_printf("*** Must be no more than %d.\n", imax);
00618        } while ((i < imin) || (i > imax));
00619        return (i);
00620 }
00621 
00622 /* 
00623  * newprompt()              prompt for a string with no existing value
00624  *                   (clears out string buffer first)
00625  *                   If len is negative, pass the "noshow" flag to ctdl_getline()
00626  */
00627 void newprompt(char *prompt, char *str, int len)
00628 {
00629        str[0] = 0;
00630        color(BRIGHT_MAGENTA);
00631        scr_printf("%s", prompt);
00632        color(DIM_MAGENTA);
00633        ctdl_getline(str, abs(len), (len<0), 0);
00634        color(DIM_WHITE);
00635 }
00636 
00637 
00638 int lkey(void)
00639 {                           /* returns a lower case value */
00640        int a;
00641        a = inkey();
00642        if (isupper(a))
00643               a = tolower(a);
00644        return (a);
00645 }
00646 
00647 /*
00648  * parse the citadel.rc file
00649  */
00650 void load_command_set(void)
00651 {
00652        FILE *ccfile;
00653        char buf[1024];
00654        struct citcmd *cptr;
00655        struct citcmd *lastcmd = NULL;
00656        int a, d;
00657        int b = 0;
00658 
00659        /* first, set up some defaults for non-required variables */
00660 
00661        strcpy(editor_path, "");
00662        strcpy(printcmd, "");
00663        strcpy(imagecmd, "");
00664        strcpy(rc_username, "");
00665        strcpy(rc_password, "");
00666        rc_floor_mode = 0;
00667        rc_exp_beep = 1;
00668        rc_allow_attachments = 0;
00669        rc_remember_passwords = 0;
00670        strcpy(rc_exp_cmd, "");
00671        rc_display_message_numbers = 0;
00672        rc_force_mail_prompts = 0;
00673        rc_ansi_color = 0;
00674        rc_color_use_bg = 0;
00675        strcpy(rc_url_cmd, "");
00676        strcpy(rc_open_cmd, "");
00677        strcpy(rc_gotmail_cmd, "");
00678 #ifdef HAVE_OPENSSL
00679        rc_encrypt = RC_DEFAULT;
00680 #endif
00681 
00682        /* now try to open the citadel.rc file */
00683 
00684        ccfile = NULL;
00685        if (getenv("HOME") != NULL) {
00686               snprintf(buf, sizeof buf, "%s/.citadelrc", getenv("HOME"));
00687               ccfile = fopen(buf, "r");
00688        }
00689        if (ccfile == NULL) {
00690               ccfile = fopen(file_citadel_rc, "r");
00691        }
00692        if (ccfile == NULL) {
00693               ccfile = fopen("/etc/citadel.rc", "r");
00694        }
00695        if (ccfile == NULL) {
00696               ccfile = fopen("./citadel.rc", "r");
00697        }
00698        if (ccfile == NULL) {
00699               perror("commands: cannot open citadel.rc");
00700               logoff(NULL, 3);
00701        }
00702        while (fgets(buf, sizeof buf, ccfile) != NULL) {
00703               while ((!IsEmptyStr(buf)) ? (isspace(buf[strlen(buf) - 1])) : 0)
00704                      buf[strlen(buf) - 1] = 0;
00705 
00706               if (!strncasecmp(buf, "encrypt=", 8)) {
00707                      if (!strcasecmp(&buf[8], "yes")) {
00708 #ifdef HAVE_OPENSSL
00709                             rc_encrypt = RC_YES;
00710 #else
00711                             fprintf(stderr, "citadel.rc requires encryption support but citadel is not compiled with OpenSSL");
00712                             logoff(NULL, 3);
00713 #endif
00714                      }
00715 #ifdef HAVE_OPENSSL
00716                      else if (!strcasecmp(&buf[8], "no")) {
00717                             rc_encrypt = RC_NO;
00718                      }
00719                      else if (!strcasecmp(&buf[8], "default")) {
00720                             rc_encrypt = RC_DEFAULT;
00721                      }
00722 #endif
00723               }
00724 
00725               if (!strncasecmp(buf, "editor=", 7)) {
00726                      strcpy(editor_path, &buf[7]);
00727               }
00728 
00729               if (!strncasecmp(buf, "printcmd=", 9))
00730                      strcpy(printcmd, &buf[9]);
00731 
00732               if (!strncasecmp(buf, "imagecmd=", 9))
00733                      strcpy(imagecmd, &buf[9]);
00734 
00735               if (!strncasecmp(buf, "expcmd=", 7))
00736                      strcpy(rc_exp_cmd, &buf[7]);
00737 
00738               if (!strncasecmp(buf, "use_floors=", 11)) {
00739                      if (!strcasecmp(&buf[11], "yes"))
00740                             rc_floor_mode = RC_YES;
00741                      if (!strcasecmp(&buf[11], "no"))
00742                             rc_floor_mode = RC_NO;
00743                      if (!strcasecmp(&buf[11], "default"))
00744                             rc_floor_mode = RC_DEFAULT;
00745               }
00746               if (!strncasecmp(buf, "beep=", 5)) {
00747                      rc_exp_beep = atoi(&buf[5]);
00748               }
00749               if (!strncasecmp(buf, "allow_attachments=", 18)) {
00750                      rc_allow_attachments = atoi(&buf[18]);
00751               }
00752               if (!strncasecmp(buf, "idle_threshold=", 15)) {
00753                      rc_idle_threshold = atol(&buf[15]);
00754               }
00755               if (!strncasecmp(buf, "remember_passwords=", 19)) {
00756                      rc_remember_passwords = atoi(&buf[19]);
00757               }
00758               if (!strncasecmp(buf, "display_message_numbers=", 24)) {
00759                      rc_display_message_numbers = atoi(&buf[24]);
00760               }
00761               if (!strncasecmp(buf, "force_mail_prompts=", 19)) {
00762                      rc_force_mail_prompts = atoi(&buf[19]);
00763               }
00764               if (!strncasecmp(buf, "ansi_color=", 11)) {
00765                      if (!strncasecmp(&buf[11], "on", 2))
00766                             rc_ansi_color = 1;
00767                      if (!strncasecmp(&buf[11], "auto", 4))
00768                             rc_ansi_color = 2;   /* autodetect */
00769                      if (!strncasecmp(&buf[11], "user", 4))
00770                             rc_ansi_color = 3;   /* user config */
00771               }
00772               if (!strncasecmp(buf, "status_line=", 12)) {
00773                      if (!strncasecmp(&buf[12], "on", 2))
00774                             enable_status_line = 1;
00775               }
00776               if (!strncasecmp(buf, "use_background=", 15)) {
00777                      if (!strncasecmp(&buf[15], "on", 2))
00778                             rc_color_use_bg = 9;
00779               }
00780               if (!strncasecmp(buf, "prompt_control=", 15)) {
00781                      if (!strncasecmp(&buf[15], "on", 2))
00782                             rc_prompt_control = 1;
00783                      if (!strncasecmp(&buf[15], "user", 4))
00784                             rc_prompt_control = 3;      /* user config */
00785               }
00786               if (!strncasecmp(buf, "username=", 9))
00787                      strcpy(rc_username, &buf[9]);
00788 
00789               if (!strncasecmp(buf, "password=", 9))
00790                      strcpy(rc_password, &buf[9]);
00791 
00792               if (!strncasecmp(buf, "urlcmd=", 7))
00793                      strcpy(rc_url_cmd, &buf[7]);
00794 
00795               if (!strncasecmp(buf, "opencmd=", 7))
00796                      strcpy(rc_open_cmd, &buf[8]);
00797 
00798               if (!strncasecmp(buf, "gotmailcmd=", 11))
00799                      strcpy(rc_gotmail_cmd, &buf[11]);
00800 
00801               if (!strncasecmp(buf, "cmd=", 4)) {
00802                      strcpy(buf, &buf[4]);
00803 
00804                      cptr = (struct citcmd *) malloc(sizeof(struct citcmd));
00805 
00806                      cptr->c_cmdnum = atoi(buf);
00807                      for (d = strlen(buf); d >= 0; --d)
00808                             if (buf[d] == ',')
00809                                    b = d;
00810                      strcpy(buf, &buf[b + 1]);
00811 
00812                      cptr->c_axlevel = atoi(buf);
00813                      for (d = strlen(buf); d >= 0; --d)
00814                             if (buf[d] == ',')
00815                                    b = d;
00816                      strcpy(buf, &buf[b + 1]);
00817 
00818                      for (a = 0; a < 5; ++a)
00819                             cptr->c_keys[a][0] = 0;
00820 
00821                      a = 0;
00822                      b = 0;
00823                      buf[strlen(buf) + 1] = 0;
00824                      while (!IsEmptyStr(buf)) {
00825                             b = strlen(buf);
00826                             for (d = strlen(buf); d >= 0; --d)
00827                                    if (buf[d] == ',')
00828                                           b = d;
00829                             strncpy(cptr->c_keys[a], buf, b);
00830                             cptr->c_keys[a][b] = 0;
00831                             if (buf[b] == ',')
00832                                    strcpy(buf, &buf[b + 1]);
00833                             else
00834                                    strcpy(buf, "");
00835                             ++a;
00836                      }
00837 
00838                      cptr->next = NULL;
00839                      if (cmdlist == NULL)
00840                             cmdlist = cptr;
00841                      else
00842                             lastcmd->next = cptr;
00843                      lastcmd = cptr;
00844               }
00845        }
00846        fclose(ccfile);
00847 }
00848 
00849 
00850 
00851 /*
00852  * return the key associated with a command
00853  */
00854 char keycmd(char *cmdstr)
00855 {
00856        int a;
00857 
00858        for (a = 0; !IsEmptyStr(&cmdstr[a]); ++a)
00859               if (cmdstr[a] == '&')
00860                      return (tolower(cmdstr[a + 1]));
00861        return (0);
00862 }
00863 
00864 
00865 /*
00866  * Output the string from a key command without the ampersand
00867  * "mode" should be set to 0 for normal or 1 for <C>ommand key highlighting
00868  */
00869 char *cmd_expand(char *strbuf, int mode)
00870 {
00871        int a;
00872        static char exp[64];
00873        char buf[1024];
00874 
00875        strcpy(exp, strbuf);
00876 
00877        for (a = 0; exp[a]; ++a) {
00878               if (strbuf[a] == '&') {
00879 
00880                      /* dont echo these non mnemonic command keys */
00881                      int noecho = strbuf[a+1] == '<' || strbuf[a+1] == '>' || strbuf[a+1] == '+' || strbuf[a+1] == '-';
00882 
00883                      if (mode == 0) {
00884                             strcpy(&exp[a], &exp[a + 1 + noecho]);
00885                      }
00886                      if (mode == 1) {
00887                             exp[a] = '<';
00888                             strcpy(buf, &exp[a + 2]);
00889                             exp[a + 2] = '>';
00890                             exp[a + 3] = 0;
00891                             strcat(exp, buf);
00892                      }
00893               }
00894               if (!strncmp(&exp[a], "^r", 2)) {
00895                      strcpy(buf, exp);
00896                      strcpy(&exp[a], room_name);
00897                      strcat(exp, &buf[a + 2]);
00898               }
00899               if (!strncmp(&exp[a], "^c", 2)) {
00900                      exp[a] = ',';
00901                      strcpy(&exp[a + 1], &exp[a + 2]);
00902               }
00903        }
00904 
00905        return (exp);
00906 }
00907 
00908 
00909 
00910 /*
00911  * Comparison function to determine if entered commands match a
00912  * command loaded from the config file.
00913  */
00914 int cmdmatch(char *cmdbuf, struct citcmd *cptr, int ncomp)
00915 {
00916        int a;
00917        int cmdax;
00918 
00919        cmdax = 0;
00920        if (is_room_aide)
00921               cmdax = 1;
00922        if (axlevel >= 6)
00923               cmdax = 2;
00924 
00925        for (a = 0; a < ncomp; ++a) {
00926               if ((tolower(cmdbuf[a]) != keycmd(cptr->c_keys[a]))
00927                   || (cptr->c_axlevel > cmdax))
00928                      return (0);
00929        }
00930        return (1);
00931 }
00932 
00933 
00934 /*
00935  * This function returns 1 if a given command requires a string input
00936  */
00937 int requires_string(struct citcmd *cptr, int ncomp)
00938 {
00939        int a;
00940        char buf[64];
00941 
00942        strcpy(buf, cptr->c_keys[ncomp - 1]);
00943        for (a = 0; !IsEmptyStr(&buf[a]); ++a) {
00944               if (buf[a] == ':')
00945                      return (1);
00946        }
00947        return (0);
00948 }
00949 
00950 
00951 /*
00952  * Input a command at the main prompt.
00953  * This function returns an integer command number.  If the command prompts
00954  * for a string then it is placed in the supplied buffer.
00955  */
00956 int getcmd(CtdlIPC *ipc, char *argbuf)
00957 {
00958        char cmdbuf[5];
00959        int cmdspaces[5];
00960        int cmdpos;
00961        int ch;
00962        int a;
00963        int got;
00964        int this_lazy_cmd;
00965        struct citcmd *cptr;
00966 
00967        /*
00968         * Starting a new command now, so set sigcaught to 0.  This variable
00969         * is set to nonzero (usually NEXT_KEY or STOP_KEY) if a command has
00970         * been interrupted by a keypress.
00971         */
00972        sigcaught = 0;
00973 
00974        /* Switch color support on or off if we're in user mode */
00975        if (rc_ansi_color == 3) {
00976               if (userflags & US_COLOR)
00977                      enable_color = 1;
00978               else
00979                      enable_color = 0;
00980        }
00981        /* if we're running in idiot mode, display a cute little menu */
00982        IFNEXPERT formout(ipc, "mainmenu");
00983 
00984        print_instant();
00985        strcpy(argbuf, "");
00986        cmdpos = 0;
00987        for (a = 0; a < 5; ++a)
00988               cmdbuf[a] = 0;
00989        /* now the room prompt... */
00990        ok_to_interrupt = 1;
00991        color(BRIGHT_WHITE);
00992        scr_printf("\n%s", room_name);
00993        color(DIM_WHITE);
00994        scr_printf("%c ", room_prompt(room_flags));
00995 
00996        while (1) {
00997               ch = inkey();
00998               ok_to_interrupt = 0;
00999 
01000               /* Handle the backspace key, but only if there's something
01001                * to backspace over...
01002                */
01003               if ((ch == 8) && (cmdpos > 0)) {
01004                      back(cmdspaces[cmdpos - 1] + 1);
01005                      cmdbuf[cmdpos] = 0;
01006                      --cmdpos;
01007               }
01008               /* Spacebar invokes "lazy traversal" commands */
01009               if ((ch == 32) && (cmdpos == 0)) {
01010                      this_lazy_cmd = next_lazy_cmd;
01011                      if (this_lazy_cmd == 13)
01012                             next_lazy_cmd = 5;
01013                      if (this_lazy_cmd == 5)
01014                             next_lazy_cmd = 13;
01015                      for (cptr = cmdlist; cptr != NULL; cptr = cptr->next) {
01016                             if (cptr->c_cmdnum == this_lazy_cmd) {
01017                                    for (a = 0; a < 5; ++a)
01018                                           if (cptr->c_keys[a][0] != 0)
01019                                                  scr_printf("%s ", cmd_expand(
01020                                                                              cptr->c_keys[a], 0));
01021                                    scr_printf("\n");
01022                                    return (this_lazy_cmd);
01023                             }
01024                      }
01025                      scr_printf("\n");
01026                      return (this_lazy_cmd);
01027               }
01028               /* Otherwise, process the command */
01029               cmdbuf[cmdpos] = tolower(ch);
01030 
01031               for (cptr = cmdlist; cptr != NULL; cptr = cptr->next) {
01032                      if (cmdmatch(cmdbuf, cptr, cmdpos + 1)) {
01033 
01034                             scr_printf("%s", cmd_expand(cptr->c_keys[cmdpos], 0));
01035                             cmdspaces[cmdpos] = strlen(
01036                                 cmd_expand(cptr->c_keys[cmdpos], 0));
01037                             if (cmdpos < 4)
01038                                    if ((cptr->c_keys[cmdpos + 1]) != 0)
01039                                           scr_putc(' ');
01040                             ++cmdpos;
01041                      }
01042               }
01043 
01044               for (cptr = cmdlist; cptr != NULL; cptr = cptr->next) {
01045                      if (cmdmatch(cmdbuf, cptr, 5)) {
01046                             /* We've found our command. */
01047                             if (requires_string(cptr, cmdpos)) {
01048                                    argbuf[0] = 0;
01049                                    ctdl_getline(argbuf, 64, 0, 0);
01050                             } else {
01051                                    scr_printf("\n");
01052                             }
01053 
01054                             /* If this command is one that changes rooms,
01055                              * then the next lazy-command (space bar)
01056                              * should be "read new" instead of "goto"
01057                              */
01058                             if ((cptr->c_cmdnum == 5)
01059                                 || (cptr->c_cmdnum == 6)
01060                                 || (cptr->c_cmdnum == 47)
01061                                 || (cptr->c_cmdnum == 52)
01062                                 || (cptr->c_cmdnum == 16)
01063                                 || (cptr->c_cmdnum == 20))
01064                                    next_lazy_cmd = 13;
01065 
01066                             /* If this command is "read new"
01067                              * then the next lazy-command (space bar)
01068                              * should be "goto"
01069                              */
01070                             if (cptr->c_cmdnum == 13)
01071                                    next_lazy_cmd = 5;
01072 
01073                             return (cptr->c_cmdnum);
01074 
01075                      }
01076               }
01077 
01078               if (ch == '?') {
01079                      scr_printf("\rOne of ...                         \n");
01080                      for (cptr = cmdlist; cptr != NULL; cptr = cptr->next) {
01081                             if (cmdmatch(cmdbuf, cptr, cmdpos)) {
01082                                    for (a = 0; a < 5; ++a) {
01083                                       keyopt(cmd_expand(cptr->c_keys[a], 1));
01084                                scr_printf(" ");
01085                                    }
01086                                    scr_printf("\n");
01087                             }
01088                      }
01089               sigcaught = 0;
01090 
01091                      scr_printf("\n%s%c ", room_name, room_prompt(room_flags));
01092                      got = 0;
01093                      for (cptr = cmdlist; cptr != NULL; cptr = cptr->next) {
01094                             if ((got == 0) && (cmdmatch(cmdbuf, cptr, cmdpos))) {
01095                                    for (a = 0; a < cmdpos; ++a) {
01096                                           scr_printf("%s ",
01097                                                  cmd_expand(cptr->c_keys[a], 0));
01098                                    }
01099                                    got = 1;
01100                             }
01101                      }
01102               }
01103        }
01104 
01105 }
01106 
01107 
01108 
01109 
01110 
01111 /*
01112  * set tty modes.  commands are:
01113  * 
01114  * 01- set to Citadel mode
01115  * 2 - save current settings for later restoral
01116  * 3 - restore saved settings
01117  */
01118 #ifdef HAVE_TERMIOS_H
01119 void stty_ctdl(int cmd)
01120 {                           /* SysV version of stty_ctdl() */
01121        struct termios live;
01122        static struct termios saved_settings;
01123        static int last_cmd = 0;
01124 
01125        if (cmd == SB_LAST)
01126               cmd = last_cmd;
01127        else
01128               last_cmd = cmd;
01129 
01130        if ((cmd == 0) || (cmd == 1)) {
01131               tcgetattr(0, &live);
01132               live.c_iflag = ISTRIP | IXON | IXANY;
01133               live.c_oflag = OPOST | ONLCR;
01134               live.c_lflag = ISIG | NOFLSH;
01135 
01136               live.c_cc[VINTR] = 0;
01137               live.c_cc[VQUIT] = 0;
01138 
01139 #ifdef hpux
01140               live.c_cc[VMIN] = 0;
01141               live.c_cc[VTIME] = 0;
01142 #endif
01143 
01144               /* do we even need this stuff anymore? */
01145               /* live.c_line=0; */
01146               live.c_cc[VERASE] = 8;
01147               live.c_cc[VKILL] = 24;
01148               live.c_cc[VEOF] = 1;
01149               live.c_cc[VEOL] = 255;
01150               live.c_cc[VEOL2] = 0;
01151               live.c_cc[VSTART] = 0;
01152               tcsetattr(0, TCSADRAIN, &live);
01153        }
01154        if (cmd == 2) {
01155               tcgetattr(0, &saved_settings);
01156        }
01157        if (cmd == 3) {
01158               tcsetattr(0, TCSADRAIN, &saved_settings);
01159        }
01160 
01161 }
01162 #else
01163 void stty_ctdl(int cmd)
01164 {                           /* BSD version of stty_ctdl() */
01165        struct sgttyb live;
01166        static struct sgttyb saved_settings;
01167        static int last_cmd = 0;
01168 
01169        if (cmd == SB_LAST)
01170               cmd = last_cmd;
01171        else
01172               last_cmd = cmd;
01173 
01174        if ((cmd == 0) || (cmd == 1)) {
01175               gtty(0, &live);
01176               live.sg_flags |= CBREAK;
01177               live.sg_flags |= CRMOD;
01178               live.sg_flags |= NL1;
01179               live.sg_flags &= ~ECHO;
01180               if (cmd == 1)
01181                      live.sg_flags |= NOFLSH;
01182               stty(0, &live);
01183        }
01184        if (cmd == 2) {
01185               gtty(0, &saved_settings);
01186        }
01187        if (cmd == 3) {
01188               stty(0, &saved_settings);
01189        }
01190 }
01191 #endif
01192 
01193 
01194 /*
01195  * display_help()  -  help file viewer
01196  */
01197 void display_help(CtdlIPC *ipc, char *name)
01198 {
01199        formout(ipc, name);
01200 }
01201 
01202 
01203 /*
01204  * fmout() - Citadel text formatter and paginator
01205  */
01206 int fmout(
01207        int width,    /* screen width to use */
01208        FILE *fpin,   /* file to read from, or NULL to format given text */
01209        char *text,   /* text to be formatted (when fpin is NULL */
01210        FILE *fpout,  /* file to write to, or NULL to write to screen */
01211        int subst)    /* nonzero if we should use hypertext mode */
01212 {
01213        char *buffer = NULL; /* The current message */
01214        char *word = NULL;   /* What we are about to actually print */
01215        char *e;             /* Pointer to position in text */
01216        char old = 0;        /* The previous character */
01217        int column = 0;             /* Current column */
01218        size_t i;            /* Generic counter */
01219 
01220        /* Space for a single word, which can be at most screenwidth */
01221        word = (char *)calloc(1, width);
01222        if (!word) {
01223               scr_printf("Can't alloc memory to print message: %s!\n",
01224                             strerror(errno));
01225               logoff(NULL, 3);
01226        }
01227 
01228        /* Read the entire message body into memory */
01229        if (fpin) {
01230               buffer = load_message_from_file(fpin);
01231               if (!buffer) {
01232                      scr_printf("Can't print message: %s!\n",
01233                                    strerror(errno));
01234                      logoff(NULL, 3);
01235               }
01236        } else {
01237               buffer = text;
01238        }
01239        e = buffer;
01240 
01241        /* Run the message body */
01242        while (*e) {
01243               /* Catch characters that shouldn't be there at all */
01244               if (*e == '\r') {
01245                      e++;
01246                      continue;
01247               }
01248               /* First, are we looking at a newline? */
01249               if (*e == '\n') {
01250                      e++;
01251                      if (*e == ' ') {     /* Paragraph */
01252                             if (fpout) {
01253                                    fprintf(fpout, "\n");
01254                             } else {
01255                                    scr_printf("\n");
01256                             }
01257                             column = 0;
01258                      } else if (old != ' ') {/* Don't print two spaces */
01259                             if (fpout) {
01260                                    fprintf(fpout, " ");
01261                             } else {
01262                                    scr_printf(" ");
01263                             }
01264                             column++;
01265                      }
01266                      old = '\n';
01267                      continue;
01268               }
01269 
01270               /* Are we looking at a nonprintable?
01271                * (This section is now commented out because we could be displaying
01272                * a character set like UTF-8 or ISO-8859-1.)
01273               if ( (*e < 32) || (*e > 126) ) {
01274                      e++;
01275                      continue;
01276               } */
01277 
01278               /* Or are we looking at a space? */
01279               if (*e == ' ') {
01280                      e++;
01281                      if (column >= width - 1) {
01282                             /* Are we in the rightmost column? */
01283                             if (fpout) {
01284                                    fprintf(fpout, "\n");
01285                             } else {
01286                                    scr_printf("\n");
01287                             }
01288                             column = 0;
01289                      } else if (!(column == 0 && old == ' ')) {
01290                             /* Eat only the first space on a line */
01291                             if (fpout) {
01292                                    fprintf(fpout, " ");
01293                             } else {
01294                                    scr_printf(" ");
01295                             }
01296                             column++;
01297                      }
01298                      /* ONLY eat the FIRST space on a line */
01299                      old = ' ';
01300                      continue;
01301               }
01302               old = *e;
01303 
01304               /* Read a word, slightly messy */
01305               i = 0;
01306               while (e[i]) {
01307                      if (!isprint(e[i]) && !isspace(e[i]))
01308                             e[i] = ' ';
01309                      if (isspace(e[i]))
01310                             break;
01311                      i++;
01312               }
01313 
01314               /* We should never see these, but... slightly messy */
01315               if (e[i] == '\t' || e[i] == '\f' || e[i] == '\v')
01316                      e[i] = ' ';
01317 
01318               /* Break up really long words */
01319               /* TODO: auto-hyphenation someday? */
01320               if (i >= width)      
01321                      i = width - 1;
01322               strncpy(word, e, i);
01323               word[i] = 0;
01324 
01325               /* Decide where to print the word */
01326               if (column + i >= width) {
01327                      /* Wrap to the next line */
01328                      if (fpout) {
01329                             fprintf(fpout, "\n");
01330                      } else {
01331                             scr_printf("\n");
01332                      }
01333                      column = 0;
01334               }
01335 
01336               /* Print the word */
01337               if (fpout) {
01338                      fprintf(fpout, "%s", word);
01339               } else {
01340                      scr_printf("%s", word);
01341               }
01342               column += i;
01343               e += i;              /* Start over with the whitepsace! */
01344        }
01345 
01346        free(word);
01347        if (fpin)            /* We allocated this, remember? */
01348               free(buffer);
01349 
01350        /* Is this necessary?  It makes the output kind of spacey. */
01351        if (fpout) {
01352               fprintf(fpout, "\n");
01353        } else {
01354               scr_printf("\n");
01355        }
01356 
01357        return sigcaught;
01358 }
01359 
01360 
01361 /*
01362  * support ANSI color if defined
01363  */
01364 void color(int colornum)
01365 {
01366        static int hold_color;
01367        static int current_color;
01368 
01369        if (colornum == COLOR_PUSH) {
01370               hold_color = current_color;
01371               return;
01372        }
01373 
01374        if (colornum == COLOR_POP) {
01375               color(hold_color);
01376               return;
01377        }
01378 
01379        current_color = colornum;
01380        if (enable_color) {
01381               /* When switching to dim white, actually output an 'original
01382                * pair' sequence -- this looks better on black-on-white
01383                * terminals. - Changed to ORIGINAL_PAIR as this actually
01384                * wound up looking horrible on black-on-white terminals, not
01385                * to mention transparent terminals.
01386                */
01387               if (colornum == ORIGINAL_PAIR)
01388                      printf("\033[0;39;49m");
01389               else
01390                      printf("\033[%d;3%d;4%dm", 
01391                                    (colornum & 8) ? 1 : 0,
01392                                    (colornum & 7),
01393                                    rc_color_use_bg);
01394 
01395        }
01396 }
01397 
01398 void cls(int colornum)
01399 {
01400        if (enable_color) {
01401               printf("\033[4%dm\033[2J\033[H\033[0m",
01402                             colornum ? colornum : rc_color_use_bg);
01403        }
01404 }
01405 
01406 
01407 /*
01408  * Detect whether ANSI color is available (answerback)
01409  */
01410 void send_ansi_detect(void)
01411 {
01412        if (rc_ansi_color == 2) {
01413               printf("\033[c");
01414               scr_flush();
01415               time(&AnsiDetect);
01416        }
01417 }
01418 
01419 void look_for_ansi(void)
01420 {
01421        fd_set rfds;
01422        struct timeval tv;
01423        char abuf[512];
01424        time_t now;
01425        int a, rv;
01426 
01427        if (rc_ansi_color == 0) {
01428               enable_color = 0;
01429        } else if (rc_ansi_color == 1) {
01430               enable_color = 1;
01431        } else if (rc_ansi_color == 2) {
01432 
01433               /* otherwise, do the auto-detect */
01434 
01435               strcpy(abuf, "");
01436 
01437               time(&now);
01438               if ((now - AnsiDetect) < 2)
01439                      sleep(1);
01440 
01441               do {
01442                      FD_ZERO(&rfds);
01443                      FD_SET(0, &rfds);
01444                      tv.tv_sec = 0;
01445                      tv.tv_usec = 1;
01446 
01447                      select(1, &rfds, NULL, NULL, &tv);
01448                      if (FD_ISSET(0, &rfds)) {
01449                             abuf[strlen(abuf) + 1] = 0;
01450                             rv = read(0, &abuf[strlen(abuf)], 1);
01451                             if (rv < 0) {
01452                                    scr_printf("failed to read after select: %s", 
01453                                              strerror(errno));
01454                                    break;
01455                             }
01456                      }
01457               } while (FD_ISSET(0, &rfds));
01458 
01459               for (a = 0; !IsEmptyStr(&abuf[a]); ++a) {
01460                      if ((abuf[a] == 27) && (abuf[a + 1] == '[')
01461                          && (abuf[a + 2] == '?')) {
01462                             enable_color = 1;
01463                      }
01464               }
01465        }
01466 }
01467 
01468 
01469 /*
01470  * Display key options (highlight hotkeys inside angle brackets)
01471  */
01472 void keyopt(char *buf) {
01473        int i;
01474 
01475        color(DIM_WHITE);
01476        for (i=0; !IsEmptyStr(&buf[i]); ++i) {
01477               if (buf[i]=='<') {
01478                      scr_printf("%c", buf[i]);
01479                      color(BRIGHT_MAGENTA);
01480               } else {
01481                      if (buf[i]=='>'&& buf[i+1] != '>') {
01482                             color(DIM_WHITE);
01483                      }
01484                      scr_printf("%c", buf[i]);
01485               }
01486        }
01487        color(DIM_WHITE);
01488 }
01489 
01490 
01491 
01492 /*
01493  * Present a key-menu line choice type of thing
01494  */
01495 char keymenu(char *menuprompt, char *menustring) {
01496        int i, c, a;
01497        int choices;
01498        int do_prompt = 0;
01499        char buf[1024];
01500        int ch;
01501        int display_prompt = 1;
01502 
01503        choices = num_tokens(menustring, '|');
01504 
01505        if (menuprompt != NULL) do_prompt = 1;
01506        if ((menuprompt != NULL) && (IsEmptyStr(menuprompt))) do_prompt = 0;
01507 
01508        while (1) {
01509               if (display_prompt) {
01510                      if (do_prompt) {
01511                             scr_printf("%s ", menuprompt);
01512                      } 
01513                      else {
01514                             for (i=0; i<choices; ++i) {
01515                                    extract_token(buf, menustring, i, '|', sizeof buf);
01516                                    keyopt(buf);
01517                                    scr_printf(" ");
01518                             }
01519                      }
01520                      scr_printf("-> ");
01521                      display_prompt = 0;
01522               }
01523               ch = lkey();
01524        
01525               if ( (do_prompt) && (ch=='?') ) {
01526                      scr_printf("\rOne of...                               ");
01527                      scr_printf("                                      \n");
01528                      for (i=0; i<choices; ++i) {
01529                             extract_token(buf, menustring, i, '|', sizeof buf);
01530                             scr_printf("   ");
01531                             keyopt(buf);
01532                             scr_printf("\n");
01533                      }
01534                      scr_printf("\n");
01535                      display_prompt = 1;
01536               }
01537 
01538               for (i=0; i<choices; ++i) {
01539                      extract_token(buf, menustring, i, '|', sizeof buf);
01540                      for (c=1; !IsEmptyStr(&buf[c]); ++c) {
01541                             if ( (ch == tolower(buf[c]))
01542                                && (buf[c-1]=='<')
01543                                && (buf[c+1]=='>') ) {
01544                                    for (a=0; !IsEmptyStr(&buf[a]); ++a) {
01545                                           if ( (a!=(c-1)) && (a!=(c+1))) {
01546                                                  scr_putc(buf[a]);
01547                                           }
01548                                    }
01549                                    scr_printf("\n");
01550                                    return ch;
01551                             }
01552                      }
01553               }
01554        }
01555 }