 /* reply.c
    =======
   
 This file is part of Gerolf Markup Shredder,
 written by G. D. Brettschneider (1999-2006). All rights reserved.
 Send corrections to: MarkupShredder(at)Gerolf.org (www.Gerolf.org)
 Subject: Reply shell script interface */
   
 #define GMSdateREPLY "20061102"
 #define GMSversionREPLY "0.05a"
   
 /* ======================================================================== */
 
 /* Required libraries: */
 
 #ifdef CONIO
   #include <conio.h>
   #include <dos.h>
 #endif
 
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
 #include <time.h>
 
 /* Console routines for compatibility with conio.h: ----------------------- */
 
 /* Get single character from keyboard (without waiting for "enter"): */ 
 #ifndef CONIO
   static char getch (void) {
     static unsigned char old_c = 0;
     unsigned char new_c = 0;
  /* Return special key (which has been backed up from previous call): */
     if (old_c) {new_c = old_c; old_c = 0; return new_c;}
  /* Get a new character (which may be a multi-byte escape sequence): */
     scanf ("%c", &new_c); old_c = 0;
     if (new_c == 27) {scanf ("%c", &new_c); scanf ("%c", &new_c); 
    /* Mask arrow keys: */
       if (new_c == 'A') {old_c = 'H';}
       else if (new_c == 'B') {old_c = 'P';}
       else if (new_c == 'C') {old_c = 'M';}
       else if (new_c == 'D') {old_c = 'K';}
       else {old_c = 0;}
     new_c = 0;}
   return new_c;}
 #endif
 
 /* Clear screen: */ 
 #ifndef CONIO
   static void clrscr (void) {printf ("\033[2J");}
 #endif
 
 /* Normvideo: */ 
 #ifndef CONIO
   static void normvideo (void) {
   printf ("\033[0m");}
 #endif
 
 /* Set cursor absolutely: */ 
 #ifndef CONIO
   static void gotoxy (int x, int y) {printf ("\033[%d;%dH", y, x);}
 #endif
 
 /* Set cursor relatively (one column or row): */ 
 static void cursor_up (void) {
   #ifdef CONIO
     gotoxy (wherex (), (wherey () - 1));
   #else
     printf ("\033[1A");
   #endif
   }
 static void cursor_down (void) {
   #ifdef CONIO
     gotoxy (wherex (), (wherey () + 1));
   #else
     printf ("\033[1B");
   #endif
   }
 static void cursor_right (void) {
   #ifdef CONIO
     gotoxy (wherex () + 1, (wherey ()));
   #else
     printf ("\033[1C");
   #endif
   }
 static void cursor_left (void) {
   #ifdef CONIO
     gotoxy (wherex () - 1, (wherey ()));
   #else
     printf ("\033[1D");
   #endif
   }
   
 /* Set textbackground and textcolor: */ 
 /* Background (black ... lightgray): */ 
 static char my_background [8] [5] = {
   "0;40", // 0 black
   "0;44", // 1 blue
   "0;42", // 2 green
   "0;46", // 3 cyan
   "0;41", // 4 red
   "0;45", // 5 magenta
   "0;43", // 6 brown
   "0;47"}; // 7 lightgray 
 /*Foreground (black ... white): */ 
 static char my_foreground [16][5] = {
   "30", // 0 black
   "34", // 1 blue
   "32", // 2 green
   "36", // 3 cyan
   "31", // 4 red
   "35", // 5 magenta
   "33", // 6 brown
   "37", // 7 lightgray
   "1;30", // 8 darkgray
   "1;34", // 9 lightblue
   "1;32", // 10 lightgreen
   "1;36", // 11 lightcyan
   "1;31", // 12 lightred
   "1;35", // 13 lightmagenta
   "1;33", // 14 yellow
   "1;37"}; // 15 white
 
 /* Text background: */ 
 #ifndef CONIO
   static void textbackground (int color) {
   printf ("\033[%sm", my_background [color]);}
 #endif 
 /* Text color: */ 
 #ifndef CONIO
   static void textcolor (int color) {
   printf ("\033[%sm", my_foreground [color]);}
 #endif
  
 /* Colorize: */ 
 static void colorize (int back, int fore) {
   textbackground (back); textcolor (fore);}
 
 /* Sleep one second: */ 
 #ifdef CONIO 
   static void sleep_ (void) {delay (1000);} 
 #endif
  
 /* Global variables: ------------------------------------------------------ */
 
 #define line_length 80
 #define lines 24
 
 typedef unsigned char uchar;
 typedef unsigned char boolean;
 
 static int error = 0;
 static int line_number = 0;
 static int filler = 0;
 
 static int box_width = 24;
 static int box_offset = 30;
 
 static int item_number = 0;
 static int item_amount = 5;
 static int item_index = 0;
 
 static char item_hotkey = '\0';
 static char item_next = '\0';
 
 static boolean item_background = 0;
 static boolean item_question = 0;
 static boolean item_selected = 0;
 static boolean item_measurement = 0;
 
 static char *mode = "-help";
 
 static char item_hotstr [line_length + 1];
 static char item_entry [line_length + 1];
 static char line [line_length + 1];
 static char item_ [line_length + 1];
 
 /* Background colors: */ 
 static int color_back; // desktop
 static int color_banner;
 static int color_shadow;
 
 /* Foreground colors: */ 
 static int color_fore; // pattern
 static int color_text;
 static int color_hotkey;
 
 /* Time measurement: */
 static long after;
 static long before;
 
 /* Environment variables: ------------------------------------------------- */
 
 static char *REPLY_ITEMS;
 static char *REPLY_OFFSET;
 static char *REPLY_SIZE;
 static char *REPLY_MODULE;
 static char *REPLY_ACTION;
 static char *REPLY_HOT;
 static char *REPLY_COLD;
 static char *REPLY_TEMP;
 static char *REPLY_TEXT;
 
 /* Background colors: */ 
 static char *REPLY_DESKTOP;
 static char *REPLY_BANNER;
 static char *REPLY_SHADE;
 
 /* Foreground colors: */ 
 static char *REPLY_PATTERN;
 static char *REPLY_TEXT;
 static char *REPLY_HOTKEY;
 
 /* Time measurement: */
 static char *REPLY_BEFORE;
   
 /*Get environment variables: -----------------------------------------------*/
   
 #define reply_error "REPLY error: Environment"
 #define need_num "must be set to a number"
 
 static void get_environment (void) {
 /* Name of menu-building script: */
   REPLY_MODULE = getenv ("REPLY_MODULE");
   REPLY_ACTION = getenv ("REPLY_ACTION");
 /* Name of temporary answering script: */
   REPLY_TEMP = getenv ("REPLY_TEMP");
 /* Width of dialog box, or height of background: */
   REPLY_SIZE = getenv ("REPLY_SIZE"); 
   if ((REPLY_SIZE != NULL) && (sscanf 
     ((char *) REPLY_SIZE, "%d", &box_width) == 0))
       printf (" %s REPLY_SIZE %s. ", reply_error, need_num);
 /* Horizontal offset of dialog box: */
   REPLY_OFFSET = getenv ("REPLY_OFFSET"); 
   if ((REPLY_OFFSET != NULL) && (sscanf
     ((char *) REPLY_OFFSET, "%d", &box_offset) == 0))
       printf (" %s REPLY_OFFSET %s. ", reply_error, need_num); 
 /* Number of menu items: */
   REPLY_ITEMS = getenv ("REPLY_ITEMS");
   if ((REPLY_ITEMS != NULL) && (sscanf
     ((char *) REPLY_ITEMS, "%d", &item_amount) == 0))
       printf (" %s REPLY_ITEMS %s. ", reply_error, need_num);
 /* Number of desktop color (0...7): */
   REPLY_DESKTOP = getenv ("REPLY_DESKTOP"); 
   if ((REPLY_DESKTOP !=NULL) && (sscanf
     ((char *) REPLY_DESKTOP, "%d", &color_back) == 0))
       printf (" %s REPLY_DESKTOP %s. ", reply_error, need_num);
 /* Number of pattern color (0..15): */
   REPLY_PATTERN = getenv ("REPLY_PATTERN"); 
   if ((REPLY_PATTERN !=NULL) && (sscanf
     ((char *) REPLY_PATTERN, "%d", &color_fore) == 0))
       printf (" %s REPLY_PATTERN %s. ", reply_error, need_num);
 /* Number of banner color (0...7): */
   REPLY_BANNER = getenv ("REPLY_BANNER");
   if ((REPLY_BANNER != NULL) && (sscanf
     ((char *) REPLY_BANNER, "%d", &color_banner) == 0))
       printf (" %s REPLY_BANNER %s. ", reply_error, need_num);
 /* Number of text color (0..15): */
   REPLY_TEXT = getenv ("REPLY_TEXT");
   if ((REPLY_TEXT != NULL) && (sscanf
     ((char *) REPLY_TEXT, "%d", &color_text) == 0))
       printf (" %s REPLY_TEXT %s. ", reply_error, need_num);
 /* Number of shadow color (0...7): */
   REPLY_SHADE = getenv ("REPLY_SHADE");
   if ((REPLY_SHADE != NULL) && (sscanf
     ((char *) REPLY_SHADE, "%d", &color_shadow) == 0))
       printf (" %s REPLY_SHADE %s. ", reply_error, need_num);
 /* Number of hotkey color (0..15): */
   REPLY_HOTKEY = getenv ("REPLY_HOTKEY"); 
   if ((REPLY_HOTKEY != NULL) && (sscanf
     ((char *) REPLY_HOTKEY, "%d", &color_hotkey) == 0))
       printf (" %s REPLY_HOTKEY %s. ", reply_error, need_num);
 /* Time measurement: */
   REPLY_BEFORE = getenv ("REPLY_BEFORE"); 
   if ((REPLY_BEFORE != NULL) && (sscanf
     ((char *) REPLY_BEFORE, "%ld", &before) == 0))
       printf (" %s REPLY_BEFORE %s. ", reply_error, need_num);}       
   
 #undef need_num 
   
 /* Command line parameters: ----------------------------------------------- */
 
 static char **P_argv;
 static int Argc;
 
 /* (1) Get mode: */ 
 static void get_mode (void) {
   if (Argc < 2) return;
   mode = P_argv [1];}
 
 /* (n) Get number parameter: */ 
 #define msg1 "REPLY error: Number ex" 
 #define msg2 "pected. Parameter number "
 #define msg3 "must be the "
 #define msg4 ", not "   
 static int get_number (int par_number, 
   char *par_name) {
   int i, error;
   if (Argc < par_number + 1) return (0);
   error = (sscanf ((char *) P_argv [par_number], "%d", &i) == 0);
   if ((strcmp (P_argv [par_number], "")) || (error == 0)) return (i);
   printf (" %s%s%d %s%s%s\"%s\".", msg1, msg2, par_number,
     msg3, par_name, msg4, P_argv [par_number]);
   return (0);} 
 #undef msg1
 #undef msg2
 #undef msg3
 #undef msg4
   
 /* (5) Get text for banner, menu item, default answer, fill sign: */ 
 static void get_text (void) {
   int n, limit;
   if (Argc < 4) return;
   strcpy (item_, P_argv [3]);
   limit = strlen (P_argv [3]);
   for (n = 0; n < limit; n++) {if (item_[n] == '=') item_[n] = ' ';}}
 
 /* (6) Get item hotkey: */ 
 static void get_item_hotkey (void) {
   if (Argc < 5) return;
   strcpy (item_hotstr, P_argv [4]);
   if (*item_hotstr != '\0') item_hotkey = item_hotstr [0];
   else item_hotkey = '\0';
   item_selected = (strlen (item_hotstr) > 1);}
 
 /* Get command line parameters: */ 
 static void get_parameters (int argc, char *argv []) {
   Argc = argc;
   P_argv = argv;
   get_mode (); // 1
   line_number = get_number (2, "line number");
   get_text (); // 3
   get_item_hotkey (); // 4
   item_number = get_number (5, "item number");}
 
 /* Answering file handling: ----------------------------------------------- */
 
 /*Check whether file exists: */ 
 static boolean file_exists (char *name) {
   FILE *p_file = NULL;
   if ((p_file = fopen (name, "rt")) == NULL) return 0; else return 1;}
 
 /* Write file error message: */ 
 static void file_error (char *action,char *name) {
   error = 1;
   gotoxy (1, 1);
   printf (" REPLY error: Cannot %s %s", action, name);
   getch ();}
 
 /* Write replytmp or replytmp.bat: */ 
 static void write_replytmp (char *first, char *second) {
   int n;
   char *my_str = item_;
   FILE *replytmp = NULL;
   /* Search old file: */
   if (file_exists (REPLY_TEMP)) {
     file_error ("overwrite", REPLY_TEMP); return;}
   /* Rewrite file: */
   if ((replytmp = fopen (REPLY_TEMP, "wt")) == NULL) {
     file_error ("rewrite", REPLY_TEMP); return;}
   /* Write content (1: header line): */
   if (fprintf (replytmp, "%s\n", second) <= 0) {
     file_error ("write header line to", REPLY_TEMP); return;}
   /* Okay... omit error check for the next lines: */
   /* Write content (2: hot and cold state, if not background): */
   if (!item_background) {
     if ((int) item_next < 32) {
       fprintf (replytmp, "%s REPLY_HOT=%d\n", first, item_next);}
     else {fprintf (replytmp, "%s REPLY_HOT=%c\n", first, item_next);}
     if ((int) item_hotkey < 32) {
       fprintf (replytmp, "%s REPLY_COLD=%d\n", first, item_hotkey);}
     else {fprintf (replytmp, "%s REPLY_COLD=%c\n", first, item_hotkey);}}
   /* Write content (3: data (if question): */
   if (item_question) {
     fprintf (replytmp, "%s REPLY_DATA=%s\n", first, my_str);}
   /* Write content (4: color palette (if background): */
   if (item_background) {
     fprintf (replytmp, "%s REPLY_DESKTOP=%d\n", first, color_back);
     fprintf (replytmp, "%s REPLY_PATTERN=%d\n", first, color_fore);
     fprintf (replytmp, "%s REPLY_BANNER=%d\n", first, color_banner);
     fprintf (replytmp, "%s REPLY_TEXT=%d\n", first, color_text);
     fprintf (replytmp, "%s REPLY_SHADE=%d\n", first, color_shadow);
     fprintf (replytmp, "%s REPLY_HOTKEY=%d\n", first, color_hotkey);
     fprintf (replytmp, "%s REPLY_LETTER=%d\n", first, filler);}
   if (item_measurement) {
     fprintf (replytmp, "%s REPLY_AFTER=%ld\n", first, after);}
   /* Close file: */
   if (fclose (replytmp) != 0) {
     file_error ("close", REPLY_TEMP); return;}}
 
 /* Write user input to temporary files in Unix and Dos shell script styles: */ 
 static void write_user_input (void) {
   char *bat = ".bat"; char *Bat = ".Bat"; char *BAT = ".BAT";
   if (strstr (REPLY_TEMP, bat) != NULL
     || strstr (REPLY_TEMP, Bat) != NULL
     || strstr (REPLY_TEMP, BAT) != NULL)
       write_replytmp ("set", "@echo off");
   else write_replytmp ("export", "#!/bin/sh");}
   
 /* Line drawing functions: ------------------------------------------------ */
 
 /* Fill line: */   
 static void fill_line (int yoffset) {
   int i;
   gotoxy (1 + box_offset, line_number + yoffset);
   colorize (color_back, color_fore);
   for (i = 0; i < box_width + 2; i++)
   #ifdef CONIO
     cprintf ("%c", filler);
   #else
     printf ("%c", filler);
   #endif
   return;}   
    
 /* Draw definite background: */   
 static void definite_background (void) {
   int n; char *tab = "\t";
   char col_back [3]; char col_fore [3];
   char col_banner [3]; char col_text [3];
   char col_shadow [3]; char col_hotkey [3];
   char char_filler [3];
   sprintf (col_back, "%d", color_back);
   sprintf (col_fore, "%d", color_fore);
   sprintf (col_banner, "%d", color_banner);
   sprintf (col_text, "%d", color_text);
   sprintf (col_shadow, "%d", color_shadow);
   sprintf (col_hotkey, "%d", color_hotkey);
   sprintf (char_filler,"%d", filler);
   colorize (color_back, color_fore);
   /* box_width here means background height: */
   for (n = 0; n < box_width; n++) fill_line (n);
   /* Save background information to temp file: */
   item_background = 1; item_next = ' '; item_hotkey = ' ';
 //strcpy (item_, "-g_palet");
   strcat (item_, tab); strcat (item_, col_back);
   strcat (item_, tab); strcat (item_, col_fore);
   strcat (item_, tab); strcat (item_, col_banner);
   strcat (item_, tab); strcat (item_, col_text);
   strcat (item_, tab); strcat (item_, col_shadow);
   strcat (item_, tab); strcat (item_, col_hotkey);
   strcat (item_, tab); strcat (item_, char_filler);
   write_user_input ();}

 /* Color pairs that have enough contrast to be readable ("rainbow"): */
 // 0  1  2  3  4  5  6  7
 // BK BL GN CY RD MA BN LGR
 static int pairs [16] [8] = {
 #ifdef CONIO
   {0, 0, 1, 1, 0, 0, 1, 1}, // 0 BK
   {0, 0, 0, 0, 0, 0, 0, 1}, // 1 BL
   {0, 0, 0, 0, 0, 0, 0, 0}, // 2 GN
   {0, 0, 0, 0, 0, 0, 0, 0}, // 3 CY

   {0, 0, 1, 1, 0, 0, 0, 1}, // 4 RD
   {0, 0, 1, 1, 0, 0, 0, 1}, // 5 MA
   {0, 0, 0, 0, 0, 0, 0, 0}, // 6 BN
   {1, 1, 0, 0, 1, 1, 0, 0}, // 7 LGR

   {0, 0, 0, 0, 0, 0, 0, 0}, // 8 DGR
   {0, 0, 0, 0, 0, 0, 0, 1}, // 9 LBL
   {1, 1, 0, 0, 1, 1, 1, 0}, //10 LGN
   {1, 1, 0, 0, 1, 1, 0, 0}, //11 LCY

   {0, 0, 1, 1, 0, 0, 0, 1}, //12 LRD
   {0, 0, 0, 0, 0, 0, 0, 0}, //13 LMA
   {1, 1, 0, 0, 1, 1, 1, 0}, //14 YE
   {1, 1, 0, 0, 1, 1, 1, 0}  //15 WH
 #else
   {0, 0, 1, 1, 0, 0, 1, 1}, // 0 BK
   {0, 0, 0, 0, 0, 0, 0, 1}, // 1 BL
   {0, 0, 0, 0, 0, 0, 0, 0}, // 2 GN
   {0, 0, 0, 0, 0, 0, 0, 0}, // 3 CY 

   {0, 0, 1, 1, 0, 0, 1, 1}, // 4 RD
   {0, 0, 0, 0, 0, 0, 0, 1}, // 5 MA
   {0, 0, 0, 0, 0, 0, 0, 0}, // 6 BN
   {0, 0, 0, 0, 0, 0, 0, 0}, // 7 LGR

   {0, 0, 1, 1, 0, 0, 1, 1}, // 8 DGR
   {0, 0, 1, 1, 0, 0, 1, 1}, // 9 LBL
   {1, 1, 0, 0, 1, 1, 0, 0}, //10 LGN
   {1, 1, 0, 0, 1, 1, 0, 0}, //11 LCY
 
   {0, 0, 1, 1, 0, 0, 1, 1}, //12 LRD
   {0, 0, 0, 0, 0, 0, 1, 1}, //13 LMA
   {1, 1, 0, 0, 1, 1, 0, 0}, //14 YE
   {1, 1, 0, 0, 1, 1, 0, 0}  //15 WH   
 #endif
   };
   
 /* Draw random background: */ 
 static void random_back (void) {
   int i; struct tm *time_now; time_t secs_now;
   char weekday [2]; // BK, BL, CY, GN, BN, RD, LGR
   int color_of_day [8] = {0, 1, 3, 2, 6, 4, 7};
   /* Randomize: */
   srand ((unsigned) time (&secs_now));
   /* Get time: */
   tzset (); time (&secs_now); time_now = localtime (&secs_now);
   strftime (weekday, 2, "%w", time_now);
   /* Get three different background colors: */
   /* Background color: */
   color_back = color_of_day [weekday [0] - 48];
   i = 1000;
   do {i = 0;
     /* Banner color: */
     do color_banner = ( 8.0 * rand () / (RAND_MAX + 1.0));
     while (color_banner == color_back);
     /* Shadow color: */
     do color_shadow = ( 8.0 * rand () / (RAND_MAX + 1.0));
     while ((color_shadow == color_back) || (color_shadow == color_banner));
     /* Get readable paired foreground colors: */
     /* Foreground color: */
     do color_fore = (16.0 * rand () / (RAND_MAX + 1.0));
     while (!pairs [color_fore] [color_back]);
     /* Text color: */
     do color_text = (16.0 * rand () / (RAND_MAX + 1.0));
     while (!pairs [color_text] [color_banner]);
     /* Hotkey color: */
     do {i ++; // avoid hang-up
       color_hotkey = (16.0 * rand () / (RAND_MAX + 1.0));}
     while (((!pairs [color_hotkey] [color_back])
       || (!pairs [color_hotkey] [color_banner])
       || (!pairs [color_hotkey] [color_shadow])) && (i < 1000));}
   while (i > 999);
   /* Fill pattern character: */
   filler = (95.0 * rand () / (RAND_MAX + 1.0)) + 32.0;
   definite_background ();}
   
 /* Time measurement: */
 static void measure_time (void) {
   struct tm *time_now; time_t t;
   t = time (NULL);
   item_measurement = 1;
   after = t - before;}
 
 /* Write text of item and whitespace: ------------------------------------- */

 /* Write single character: */ 
 static void write_char (char character, uchar back, uchar fore) {
   colorize (back, fore);
   #ifdef CONIO
     cprintf ("%c", character);
   #else
     printf ("%c", character);
   #endif
 }
 /* Fill up with whitespace or fill character, if text fits into box: */ 
 static void write_aftertext (char character, uchar back, uchar fore) {
   long i, limit;
   colorize (back, fore);
   limit = box_width - 4;
   for (i = strlen(item_) + 1; i <= limit; i++)
   #ifdef CONIO
     cprintf ("%c", character);
   #else
     printf ("%c", character);
   #endif
 } 
 /* Write single whitespace or character fill character: */ 
 static void write_gap (char character, uchar back, uchar fore) {
   colorize (back, fore);
   if (*item_ != '\0') {
   #ifdef CONIO
     cprintf ("%c", character);
   #else
     printf ("%c", character);
   #endif
   } else {
   #ifdef CONIO
     cprintf ("%c", character);
   #else
     printf ("%c", character);
   #endif
   }} 
 /* Write text of item: */ 
 static void write_item_text (uchar back, uchar fore, 
   uchar hotback, uchar hotfore) {
     long i, limit; int found = 0;
     colorize (back, fore);
     limit = strlen (item_);
     for (i = 0; i < limit; i++) {
       if (item_[i] == item_hotkey && found == 0 ) {
         found = 1;
         /* Colorize hotkey: */
         colorize (hotback, hotfore);
         #ifdef CONIO
         cprintf ("%c", item_[i]);
         #else
         printf ("%c", item_[i]);
         #endif
         colorize (back, fore);}
       else {
         /* Write other letters of item text: */
         #ifdef CONIO
         cprintf ("%c", item_[i]);
         #else
         printf ("%c", item_[i]);
         #endif
       }}}   
 /* Write static line: */ 
 static void write_static_line (char character, uchar back, uchar fore, 
   uchar hotback, uchar hotfore) {
     if (strlen (item_) < box_width - 3) {
       write_gap (character, back, fore);
       write_item_text (back, fore, hotback, hotfore);
       write_gap (character, back, fore);} 
     else
       write_item_text (back, fore, hotback, hotfore);
       write_aftertext (character, back, hotfore);}
   
 /* Edit text of item: ----------------------------------------------------- */
 
 /* Cursor movement: */
    
 /* Move cursor left: */ 
 static void move_left (void) {
   if (item_index > 0) {cursor_left ();item_index --;}} 
 /* Move cursor right: */ 
 static void move_right (void) {
   if (item_index < strlen (item_)) {cursor_right (); item_index ++;}} 
 /* Move cursor up: */ 
 static void move_up (void) {
   if (item_number == 1) item_number = item_amount; else item_number --;
   item_next = (char) item_number;}      
 /* Move cursor down: */ 
 static void move_down (void) {
   if (item_number == item_amount) item_number = 1; else item_number ++;
   item_next = (char) item_number;}
   
 /* Text deletion and insertion: */
   
 /* Move cursor back (and delete what is there): */ 
 static void move_back (uchar back, uchar fore) {
   int i;
   if (item_index > 0) {
     /* Set cursor and index: */
     cursor_left (); item_index --;
     /* Move right and shift letters: */
     for (i = item_index; i < strlen (item_); i++) {
       item_ [i] = item_ [i + 1]; write_char (item_ [i], back, fore);}
     /* Overwrite last letter: */
     write_char (' ', back, fore);
     /* Move left to index position: */
     for (i = strlen (item_); i >= item_index; i--) cursor_left ();}}
      
 /* Move cursor forth (and insert new text): */ 
 static void move_forth (char character, uchar back, uchar fore) {
   int i;
   if ((strlen (item_) < box_width - 4)
   && (character > 31) && (character != 127)) {
     /* Move right to end of text: */
     for (i = item_index; i <= strlen (item_); i++) cursor_right ();
     /* Move left and shift letters: */
     for (i = strlen (item_); i > item_index; i--) {
       item_ [i] = item_ [i - 1]; cursor_left ();
       write_char (item_ [i], back, fore); cursor_left ();}
     /* Set cursor and index: */
     cursor_left (); item_index ++;
     /* Insert new letter: */
     item_ [item_index - 1] = character;
     write_char (character, back, fore);}}
 
 /* Edit item text: */ 
 static void edit_item_text (uchar back, uchar fore) {
   unsigned char c = '\0'; unsigned char d = '\0';
   colorize (back, fore);
   do {c = getch ();
     if (!c) {/* It's a special character: */
       d = getch ();
       /* Unmask arrow keys: */
       if (d == 'K') move_left ();
       else if (d == 'M') move_right ();
       else if (d == 'H') {move_up (); break;}
       else if (d == 'P') {move_down (); break;}}
     else if (c == 8 && item_index > 0) {
       move_back (back, fore);
       #ifdef CONIO
         cursor_left ();
       #endif
       }
     else if (c == 127) move_back (back, fore);
     else move_forth (c, back, fore);}
   while (1);}
 
 /* Edge below line needs special care for 3D effects: */
 
 /* Goto edge below (to set cursor at a nice place): */ 
 static void goto_edge_below (void) {cursor_left (); cursor_down ();}   
 /* Draw edge below: */ 
 static void draw_edge_below (void) {
   cursor_left (); cursor_down (); cursor_left ();
   write_char ('\\', color_shadow, color_hotkey);}   
 /* Remove edge below: */ 
 static void remove_edge_below (void) {
   if (!item_selected) {return;}
   cursor_left (); 
   write_char (' ', color_shadow, color_hotkey); 
   cursor_left ();} 
 /* Goto start point: */ 
 static void goto_start (void) {remove_edge_below ();
   gotoxy ((int) (box_offset + 1), (int) line_number);}
 
 /* Distinguish different line types: -------------------------------------- */
 
 /* Line definition to fixed colors and fill character: */ 
 #define msg1 "REPLY error (-background," 
 #define msg2 " -stripe): Number expected."
 #define par0 "Parameter number"
 #define par3 "the foreground color"
 #define par4 "an ASCII code" 
 static void fix_color_and_filler (void) {
   int i, err3, err4;
   /* Take parameter 3 as color index: */
   err3 = (sscanf (item_, "%d", &color_fore) == 0);
   if (!strcmp (item_, "") || err3) {
     printf (" %s%s ", msg1, msg2);
     printf ("%s 3 must be %s", par0, par3);
     printf (", not \"%s\". ", item_); return;}
   /* Take parameter 4 as fill character code: */
   err4 = (sscanf (item_hotstr,"%d", &filler) == 0);
   if (!strcmp (item_hotstr, "") || err4) {
     printf (" %s%s ", msg1, msg2);
     printf ("%s 4 must be %s", par0, par4);
     printf (", not \"%s\". ", item_hotstr); return;}}   
 #undef msg1
 #undef msg2
 #undef par0
 #undef par3
 #undef par4
 
 /* Stripe: */ 
 static void stripe (void) {
   item_selected = 0; fix_color_and_filler (); fill_line (0);}
   
 /* Clear: */ 
 static void clear (void) {
   item_selected = 0; filler = ' ';
   #ifdef CONIO
     color_back =  0; // black DOS
     color_fore = 15; // white
   #else
     color_back = 15; // white Unix
     color_fore =  0; // black
   #endif
   normvideo (); fill_line (0);}
 
 /* Background: */ 
 static void background (void) {
   item_selected = 0; fix_color_and_filler (); definite_background ();}
   
 /* Banner: */ 
 static void banner (void) {goto_start (); 
   write_static_line (' ',  color_banner, color_text, 
     color_banner, color_text);}
   
 /* Top of box: */ 
 static void top (void) {goto_start ();
   write_char ('+', color_banner, color_hotkey);
   write_static_line ('-', color_banner, color_hotkey, 
     color_banner, color_hotkey);
   write_char ('+', color_banner, color_hotkey);
   write_char ('\\', color_back, color_hotkey);
   cursor_right (); 
   goto_edge_below ();}
 
 /* Item box: */ 
 static void item (void) {goto_start ();
   if (item_selected) {
     write_char ('\\', color_back, color_hotkey);
     write_char (' ', color_shadow, color_hotkey);
     write_static_line (' ', color_shadow, color_hotkey,
       color_banner, color_text);
     write_char ('\\', color_shadow, color_hotkey);
     write_char ('|', color_back, color_hotkey);
     draw_edge_below (); 
     return;}
   write_char ('|', color_banner, color_hotkey);
   write_static_line (' ', color_banner, color_text,
     color_shadow, color_hotkey);
   write_char ('|', color_banner, color_hotkey);
   write_char (' ', color_shadow, color_banner);
   write_char ('|', color_back, color_hotkey);
   goto_edge_below ();}
 
 /* Question box: */ 
 static void question (void) {
   int i; item_question = 1; goto_start ();
   if (item_selected) {
     write_char ('\\', color_back, color_hotkey);
     write_char (' ', color_shadow, color_hotkey);
     write_static_line (' ', color_shadow, color_hotkey,
       color_banner, color_text);
     write_char ('\\', color_shadow, color_hotkey);
     write_char ('|', color_back, color_hotkey);
     draw_edge_below (); cursor_up ();
     item_index = strlen (item_);
     for (i = item_index + 1; i <= box_width - 2; i++) {cursor_left ();}
     edit_item_text (color_shadow, color_hotkey); return;}
   write_char ('|', color_banner, color_hotkey);
   write_static_line (' ', color_banner, color_text,
     color_shadow, color_hotkey);
   write_char ('|', color_banner, color_hotkey);
   write_char (' ', color_shadow, color_banner);
   write_char ('|', color_back, color_hotkey);
   goto_edge_below ();}
 
 /* Bottom of box: */ 
 static void bottom (void) {goto_start ();
   write_char ('+',  color_banner, color_hotkey);
   write_static_line ('-', color_banner, color_hotkey,
     color_banner, color_hotkey);
   write_char ('+', color_banner, color_hotkey);
   write_char (' ', color_shadow, color_banner);
   write_char ('|', color_back, color_hotkey);
   goto_edge_below ();}
 
 /* Shadow of box: */ 
 static void shadow (void) {goto_start ();
   write_char ('\\', color_back, color_hotkey);
   write_char ('_', color_shadow, color_hotkey);
   write_char ('_', color_shadow, color_hotkey);
   write_char ('_', color_shadow, color_hotkey);
   write_aftertext ('_', color_shadow, color_hotkey);
   write_char ('\\', color_shadow, color_hotkey);
   write_char ('|', color_back, color_hotkey);
   goto_edge_below ();}
   
 /* Main program: ---------------------------------------------------------- */
 
 /* Analyze mode and draw background or line: */
 static void draw_screen (void) {
   /* strcmp returns 0 if strings are equal: */ 
   if (!strcmp (mode, "-banner")) banner ();
   else if (!strcmp (mode, "-clear")) clear ();
   #ifdef CONIO
   else if (!strcmp (mode, "-sleep")) sleep_ ();
   #endif
   else if (!strcmp (mode, "-background")) background ();
   else if (!strcmp (mode, "-time")) measure_time ();
   else if (!strcmp (mode, "-random")) random_back ();
   else if (!strcmp (mode, "-top")) top ();
   else if (!strcmp (mode, "-item")) item ();
   else if (!strcmp (mode, "-question")) question ();
   else if (!strcmp (mode, "-bottom")) bottom ();
   else if (!strcmp (mode, "-shadow")) shadow ();
   else if (!strcmp (mode, "-stripe")) stripe ();
   else
   #define msg1 "This is REPLY Version"
   #define msg2 "the tiny shell script user"
   #define msg3 "interface for DOS and UNIX"
   #define msg4 "written by G. D. Brettschneider. Refer"
   #define msg5 "to Gerolf Markup Shredder"
   #define msg6 "for examples of usage. -- www.Gerolf.org"
   #ifdef CONIO
     printf ("\r\n    %s %s (%s), %s\r\n    %s, %s\r\n    %s %s\r\n\r\n",
     msg1, GMSversionREPLY, GMSdateREPLY, msg2, msg3, msg4, msg5, msg6);
   #else 
     printf ("\n    %s %s (%s), %s\n    %s, %s\n    %s %s\n\n",
     msg1, GMSversionREPLY, GMSdateREPLY, msg2, msg3, msg4, msg5, msg6);
   #endif
   #undef msg1
   #undef msg2
   #undef msg3
   #undef msg4
   }
   
 /* Get user input: */ 
 static void read_keyboard (void) {
   char d = '\0';
   item_next = getch (); 
   if (item_next == 0) {/* It's a special sign, but which one? */
     d = getch (); 
     if (d == 'H') move_up (); 
     else if (d == 'P') move_down ();}
   else if ((item_next == '\t') // Tabulator
     || (item_next == '\r')) // Enter
       item_next = (char) item_number;}
   
 /* Execute: */ 
 int main (int argc, char *argv []) {
   get_parameters (argc, argv);
   get_environment (); 
   draw_screen (); 
   /* Get user input: */
   if (item_selected) {
     if (!item_question) {
       #ifdef CONIO
       _setcursortype (_NOCURSOR);
       #endif
        read_keyboard ();
       #ifdef CONIO
       _setcursortype (_NORMALCURSOR);
       #endif
       }
     write_user_input ();}
   if (item_measurement) { write_user_input ();}
   normvideo (); return (error);}
   
 /* end */