Browse Source

We have working ANSI mangle, doors menu works.

The doors menu had a problem with the number
of MAX captured ANSI REGEX.  (If we have more
then we have room for, it fails // the ansi gets
mangled.)  Ouch!
bugz 4 years ago
parent
commit
617010c795
2 changed files with 794 additions and 652 deletions
  1. 6 0
      README.md
  2. 788 652
      mystic.c

+ 6 - 0
README.md

@@ -7,4 +7,10 @@ sudo apt install cmake
 cmake .
 make
 
+# Also:
+
+codium clang-format extension
+
+apt install clang-format
+
 

+ 788 - 652
mystic.c

@@ -2,66 +2,59 @@
 #include <string.h>
 #include <unistd.h> // usleep()
 
+#include <fcntl.h>
 #include <pty.h>
 #include <termios.h>
-#include <fcntl.h>
 
 #include <sys/select.h>
 #include <sys/wait.h>
 
-#include <signal.h>     // handle Ctrl-C/SIGINT
+#include <signal.h> // handle Ctrl-C/SIGINT
 
-#include <time.h>       // usleep(), nanonsleep() ? 
-#include <strings.h>    // strcasecmp
+#include <strings.h> // strcasecmp
+#include <time.h>    // usleep(), nanonsleep() ?
 
-#include <stdlib.h>     // random()
 #include <ctype.h>
+#include <stdlib.h> // random()
 
 #include <regex.h>
 
 // LOGGING with file output
 
+#include "zf_log.h"
 #include <stdio.h>
 #include <stdlib.h>
-#include "zf_log.h"
 
 FILE *g_log_file;
 
-static void file_output_callback(const zf_log_message *msg, void *arg)
-{
-	(void)arg;
-	*msg->p = '\n';
-	fwrite(msg->buf, msg->p - msg->buf + 1, 1, g_log_file);
-	fflush(g_log_file);
+static void file_output_callback(const zf_log_message *msg, void *arg) {
+  (void)arg;
+  *msg->p = '\n';
+  fwrite(msg->buf, msg->p - msg->buf + 1, 1, g_log_file);
+  fflush(g_log_file);
 }
 
-static void file_output_close(void)
-{
-	fclose(g_log_file);
-}
+static void file_output_close(void) { fclose(g_log_file); }
 
-static void file_output_open(const char *const log_path)
-{
-	g_log_file = fopen(log_path, "a");
-	if (!g_log_file)
-	{
-		ZF_LOGW("Failed to open log file %s", log_path);
-		return;
-	}
-	atexit(file_output_close);
-	zf_log_set_output_v(ZF_LOG_PUT_STD, 0, file_output_callback);
+static void file_output_open(const char *const log_path) {
+  g_log_file = fopen(log_path, "a");
+  if (!g_log_file) {
+    ZF_LOGW("Failed to open log file %s", log_path);
+    return;
+  }
+  atexit(file_output_close);
+  zf_log_set_output_v(ZF_LOG_PUT_STD, 0, file_output_callback);
 }
 
-
 // END LOGGING
 
-/* 
+/*
 What is the name of the actual, real Mystic executable
 that we'll be executing and mangling?
 */
 
 #define TARGET "./mySTIC"
-// Size of our input and output buffers. 
+// Size of our input and output buffers.
 #define BSIZE 128
 
 /*
@@ -87,377 +80,447 @@ void slow_write(int fd, int speed, char * buffer, int len) {
 }
 */
 
-const char * repr( const char * data) {
-    static char buffer[4096];
-    char * cp;
-
-    strcpy( buffer, data );
-    cp = buffer;
-    while ( *cp != 0) {
-        char c = *cp;
-
-        if (isspace(c)) {
-            if (c == ' ') {
-                cp++;
-                continue;
-            };
-            /* Ok, it's form-feed ('\f'), newline ('\n'), carriage return ('\r'),  horizontal tab ('\t'), and vertical tab ('\v') */
-            memmove( cp + 1, cp, strlen(cp) + 1);
-            *cp = '\\';
-            cp++;
-            switch(c) {
-                case '\f':
-                    *cp = 'f';
-                    cp++;
-                    break;
-                case '\n':
-                    *cp = 'n';
-                    cp++;
-                    break;
-                case '\r':
-                    *cp = 'r';
-                    cp++;
-                    break;
-                case '\t':
-                    *cp = 't';
-                    cp++;
-                    break;
-                case '\v':
-                    *cp = 'v';
-                    cp++;
-                    break;
-                default:
-                    *cp = '?';
-                    cp++;
-                    break;
-            }
-            continue;
-        }
+/**
+ * Display a repr of the given string.
+ *
+ * This converts most \n\r\v\f\t codes,
+ * defaults to \xHH (hex value).
+ */
+const char *repr(const char *data) {
+  static char buffer[4096];
+  char *cp;
 
-        if (isprint(c)) {
-            cp++;
-            continue;
-        };
-        
-        // Ok, default to \xHH output.
-        memmove( cp + 3, cp, strlen(cp) + 1);
-        *cp = '\\'; cp++;
-        *cp = 'x'; cp++;
-        char buffer[3];
-        sprintf(buffer, "%02x", (int)c & 0xff);
-        *cp = buffer[0]; cp++;
-        *cp = buffer[1]; cp++;
+  strcpy(buffer, data);
+  cp = buffer;
+  while (*cp != 0) {
+    char c = *cp;
+
+    if (isspace(c)) {
+      if (c == ' ') {
+        cp++;
         continue;
+      };
+      /* Ok, it's form-feed ('\f'), newline ('\n'), carriage return ('\r'),
+       * horizontal tab ('\t'), and vertical tab ('\v') */
+      memmove(cp + 1, cp, strlen(cp) + 1);
+      *cp = '\\';
+      cp++;
+      switch (c) {
+      case '\f':
+        *cp = 'f';
+        cp++;
+        break;
+      case '\n':
+        *cp = 'n';
+        cp++;
+        break;
+      case '\r':
+        *cp = 'r';
+        cp++;
+        break;
+      case '\t':
+        *cp = 't';
+        cp++;
+        break;
+      case '\v':
+        *cp = 'v';
+        cp++;
+        break;
+      default:
+        *cp = '?';
+        cp++;
+        break;
+      }
+      continue;
     }
 
-    /*
-    int len = strlen(buffer);
-    if (len > 100 ) {
-        ZF_LOGW("len is %d, resetting to 100.", len);
-        buffer[100] = 0;
+    if (isprint(c)) {
+      cp++;
+      continue;
     };
-    */
 
-    return buffer;
-}
+    // Ok, default to \xHH output.
+    memmove(cp + 3, cp, strlen(cp) + 1);
+    *cp = '\\';
+    cp++;
+    *cp = 'x';
+    cp++;
+    char buffer[3];
+    sprintf(buffer, "%02x", (int)c & 0xff);
+    *cp = buffer[0];
+    cp++;
+    *cp = buffer[1];
+    cp++;
+    continue;
+  }
 
+  return buffer;
+}
 
 struct render {
-    int speed;
-    int effect;
+  int speed;
+  int effect;
 } current_render;
 
 int render_overlimit = 0;
 
 void reset_render(void) {
-    current_render.speed = 0;
-    current_render.effect = 0;
-    render_overlimit = 0;
+  current_render.speed = 0;
+  current_render.effect = 0;
+  render_overlimit = 0;
 }
 
-
 #define TRIGGER "^"
 // Max limit we'll sleep before ignoring effects/speed.
 #define SLEEP_LIMIT 30
 
 int ms_sleep(unsigned int ms) {
-    int result = 0;
-    struct timespec ts = {
-        ms / 1000, 
-        (ms % 1000) * 1000000L
-    };
-
-    do {
-        struct timespec ts_sleep = ts;
-        result = nanosleep(&ts_sleep, &ts);
-    } while ( (-1 == result));
-    return result;
+  int result = 0;
+  struct timespec ts = {ms / 1000, (ms % 1000) * 1000000L};
+
+  do {
+    struct timespec ts_sleep = ts;
+    result = nanosleep(&ts_sleep, &ts);
+  } while ((-1 == result));
+  return result;
 }
 
 void render_sleep(void) {
-    if (render_overlimit)
-        return;
+  if (render_overlimit)
+    return;
 
-    if (current_render.speed) { // * 100 still too slow.
-        ms_sleep(current_render.speed * 10);
-    }
+  if (current_render.speed) { // * 100 still too slow.
+    ms_sleep(current_render.speed * 10);
+  }
 }
 
 // This needs work.
 void write_color(int fd, int color) {
-    char buffer[10];
-    sprintf(buffer, "\x1b[%dm", color);
-    write(fd, buffer, strlen(buffer));
+  char buffer[10];
+  sprintf(buffer, "\x1b[%dm", color);
+  write(fd, buffer, strlen(buffer));
 }
 
-const char * process_trigger(int fd, const char * cp) {
-    char ch;
-    int i, x, y;
-    ch = toupper(*cp);
-    cp++;
+/**
+ * process_trigger( fd, *cp )
+ *
+ * This process a command trigger.
+ * It has seen TRIGGER, and now it is
+ * processing whatever comes after it.
+ * It will perform the process, and
+ * return the char * of whatever is next.
+ */
+const char *process_trigger(int fd, const char *cp) {
+  char ch;
+  int i, x, y;
+  ch = toupper(*cp);
+  cp++;
+
+  switch (ch) {
+  case 'D':
+    i = 0;
+    if (isdigit(*cp)) {
+      i = (*cp) - '0';
+      cp++;
+    };
+    if (isdigit(*cp)) {
+      i *= 10;
+      i += (*cp) - '0';
+      cp++;
+    };
 
-    switch(ch) {
-        case 'D':
-            i = 0;
-            if (isdigit(*cp)) {
-                i = (*cp) - '0';
-                cp++;
-            };
-            if (isdigit(*cp)) {
-                i *= 10;
-                i += (*cp) - '0';
-                cp++;
-            };
-
-            if ((i > 0) && (i < 80)) {
-                ZF_LOGI("DEL %02d", i);
-
-                for (x = 0; x < i; x++ ) {
-                    write(fd, "\b \b", 3);
-                }
-            };
-            break;
-
-        case 'C':
-            i = 0;
-            if (isdigit(*cp)) {
-                i = (*cp) - '0';
-                cp++;
-            };
-            if (isdigit(*cp)) {
-                i *= 10;
-                i += (*cp) - '0';
-                cp++;
-            };
-            write_color(fd, i);
-            break;
-
-        case 'G':
-            x = 0;
-            if (isdigit(*cp)) {
-                x = (*cp) - '0';
-                cp++;
-            };
-            if (isdigit(*cp)) {
-                x *= 10;
-                x += (*cp) - '0';
-                cp++;
-            };
-            y = 0;
-            if (isdigit(*cp)) {
-                y = (*cp) - '0';
-                cp++;
-            };
-            if (isdigit(*cp)) {
-                y *= 10;
-                y += (*cp) - '0';
-                cp++;
-            };
-            break;
-
-        case 'R':
-            i = 0;
-            if (isdigit(*cp)) {
-                i = (*cp) - '0';
-                cp++;
-            };
-            if ( ( i > 0 ) && ( i < 10) ) {
-                ZF_LOGI("RENDER %d", i);
-                current_render.effect = i;
-            } else {
-                current_render.effect = 0;
-            }
-            break;
-        case 'S':
-            i = 0;
-            if (isdigit(*cp)) {
-                i = (*cp) - '0';
-                cp++;
-            };
-            if ( ( i > 0 ) && ( i < 10) ) {
-                ZF_LOGI( "SPEED %d", i);
-                current_render.speed = i;
-            } else {
-                current_render.speed = 0;
-            }
-            break;
-        case 'P':
-            i = 0;
-            if (isdigit(*cp)) {
-                i = (*cp) - '0';
-                cp++;
-            };
-            if ( ( i > 0 ) && ( i < 10) ) {
-                ZF_LOGI( "PAWS %d", i);
-                // sleep(i);
-                if (!render_overlimit) {
-                    sleep(i);
-                };
-            }
-            break;
-        
+    if ((i > 0) && (i < 80)) {
+      ZF_LOGI("DEL %02d", i);
+
+      for (x = 0; x < i; x++) {
+        write(fd, "\b \b", 3);
+      }
+    };
+    break;
+
+  case 'C':
+    i = 0;
+    if (isdigit(*cp)) {
+      i = (*cp) - '0';
+      cp++;
+    };
+    if (isdigit(*cp)) {
+      i *= 10;
+      i += (*cp) - '0';
+      cp++;
+    };
+    write_color(fd, i);
+    break;
+
+  case 'G':
+    x = 0;
+    if (isdigit(*cp)) {
+      x = (*cp) - '0';
+      cp++;
+    };
+    if (isdigit(*cp)) {
+      x *= 10;
+      x += (*cp) - '0';
+      cp++;
+    };
+    y = 0;
+    if (isdigit(*cp)) {
+      y = (*cp) - '0';
+      cp++;
+    };
+    if (isdigit(*cp)) {
+      y *= 10;
+      y += (*cp) - '0';
+      cp++;
+    };
+    break;
+
+  case 'R':
+    i = 0;
+    if (isdigit(*cp)) {
+      i = (*cp) - '0';
+      cp++;
+    };
+    if ((i > 0) && (i < 10)) {
+      ZF_LOGI("RENDER %d", i);
+      current_render.effect = i;
+    } else {
+      current_render.effect = 0;
+    }
+    break;
+  case 'S':
+    i = 0;
+    if (isdigit(*cp)) {
+      i = (*cp) - '0';
+      cp++;
+    };
+    if ((i > 0) && (i < 10)) {
+      ZF_LOGI("SPEED %d", i);
+      current_render.speed = i;
+    } else {
+      current_render.speed = 0;
+    }
+    break;
+  case 'P':
+    i = 0;
+    if (isdigit(*cp)) {
+      i = (*cp) - '0';
+      cp++;
+    };
+    if ((i > 0) && (i < 10)) {
+      ZF_LOGI("PAWS %d", i);
+      // sleep(i);
+      if (!render_overlimit) {
+        sleep(i);
+      };
     }
-    return cp;
+    break;
+  }
+  return cp;
 }
 
+/**
+ * render_effect( fd, ch )
+ *
+ * Displays the given character with whatever
+ * rendering effect is currently active.
+ * (If any).
+ */
 void render_effect(int fd, char ch) {
-    int effect = current_render.effect;
-    int l;
-    char space = ' ';
-    char bs = '\b';
-
-    switch (effect) {
-        case 1:
-            // CHAR + SPC + BS
-            render_sleep();
-            write(fd, &ch, 1);
-            render_sleep(); // Maybe extra sleep here?  This is hard to see.
-            write(fd, &space, 1);
-            render_sleep();
-            write(fd, &bs, 1);
-            break;
-        case 2:
-            // CHAR + 8 spaces + 8 BS
-            render_sleep();
-            write(fd, &ch, 1);
-            for(l = 0; l < 8; l++) {
-                render_sleep();
-                write(fd, &space, 1);
-            }
-            for(l = 0; l < 8; l++) {
-                render_sleep();
-                write(fd, &bs, 1);
-            }
-            break;
-        case 0: 
-        default:
-            // NORMAL
-            render_sleep();
-            write(fd, &ch, 1); 
-            break;
+  int effect = current_render.effect;
+  int l;
+  char space = ' ';
+  char bs = '\b';
+
+  switch (effect) {
+  case 1:
+    // CHAR + SPC + BS
+    render_sleep();
+    write(fd, &ch, 1);
+    render_sleep();
+    write(fd, &space, 1);
+    render_sleep();
+    render_sleep();
+    write(fd, &bs, 1);
+    break;
+  case 2:
+    // CHAR + 8 spaces + 8 BS
+    render_sleep();
+    write(fd, &ch, 1);
+    for (l = 0; l < 8; l++) {
+      render_sleep();
+      write(fd, &space, 1);
     }
+    for (l = 0; l < 8; l++) {
+      render_sleep();
+      write(fd, &bs, 1);
+    }
+    break;
+  case 0:
+  default:
+    // NORMAL
+    render_sleep();
+    write(fd, &ch, 1);
+    break;
+  }
 }
 
-void render( int fd, const char * string_out ) {
-    const char * cp = string_out;
-    const char * trigger = cp;
-    time_t start = time(NULL);
-    int elapsed;
-    int over = 0;
+/**
+ * render( fd, string_out )
+ *
+ * Render an entire string.
+ * Handles TRIGGER.
+ * Renders with effects.
+ */
+void render(int fd, const char *string_out) {
+  const char *cp = string_out;
+  const char *trigger = cp;
+  time_t start = time(NULL);
+  int elapsed;
+  int over = 0;
+
+  reset_render();
+
+  ZF_LOGD("render(%d, %s)", fd, repr(string_out));
+
+  // Check our time from time to time.
+  // If we start running long, disable sleeps.
+
+  while ((trigger = strstr(cp, TRIGGER)) != NULL) {
+    // There is special things to handle in here.
+    while (cp != trigger) {
+      elapsed = time(NULL) - start;
+      if (elapsed > SLEEP_LIMIT) {
+        render_overlimit = 1;
+        current_render.speed = 0;
+      };
+
+      // write(fd, cp, 1 );
+      render_effect(fd, *cp);
+      cp++;
+    };
 
-    reset_render();
+    // ZF_LOGI( "at trigger: (%s)", cp);
+    cp += strlen(TRIGGER);
 
-    ZF_LOGD( "render(%d, %s)", fd, string_out);
+    // Ok, we're pointing at the trigger -- do something.
+    cp = process_trigger(fd, cp);
 
-    // Check our time from time to time.
-    // If we start running long, kill sleeps.
+    // ZF_LOGI( "after trigger: (%s)", cp);
+  };
 
-    while ( (trigger = strstr(cp, TRIGGER)) != NULL) {
-        // There is special things to handle in here.
-        while ( cp != trigger ) {
-            elapsed = time(NULL)-start;
-            if (elapsed > SLEEP_LIMIT) {
-                render_overlimit = 1;
-                current_render.speed = 0;
-            };
+  // We still might be under a rendering effect.
+  while (*cp != 0) {
+    elapsed = time(NULL) - start;
+    if (elapsed > SLEEP_LIMIT) {
+      render_overlimit = 1;
+      current_render.speed = 0;
+    };
+    // write(fd, cp, 1);
+    render_effect(fd, *cp);
+    cp++;
+  }
+}
 
-            // write(fd, cp, 1 );
-            render_effect(fd, *cp);
-            cp++;
-        };
+// Beanzilla'a no repeats
 
-        // ZF_LOGI( "at trigger: (%s)", cp);
-        cp += strlen(TRIGGER);
+/**
+ * have_seen( list, len, item )
+ *
+ * Returns 1 (true) if item is in the list.
+ * Rotates the list [x] = [x+1], and
+ * list[len-1] = item, return 0 (false)
+ */
+int have_seen(int *list, int len, int item) {
+  int x;
 
-        // Ok, we're pointing at the trigger -- do something.
-        cp = process_trigger(fd, cp);
+  for (x = 0; x < len; x++) {
+    if (list[x] == item) {
+      return 1;
+    }
+  };
+
+  // Ok, it is something different
+  for (x = 0; x < len - 1; x++) {
+    list[x] = list[x + 1];
+  };
+  list[x] = item;
+  return 0;
+}
 
-        // ZF_LOGI( "after trigger: (%s)", cp);
-    };
+/**
+ * init_have_seen( list, len )
+ *
+ * Initialize the have_seen list with -1.
+ * (-1 isn't a valid index, so we start
+ * out with all invalid.)
+ */
+void init_have_seen(int *list, int len) {
+  int x;
 
-    // We still might be under a rendering effect.
-    while (*cp != 0) {
-        elapsed = time(NULL)-start;
-        if (elapsed > SLEEP_LIMIT) {
-            render_overlimit = 1;
-            current_render.speed = 0;
-        };
-        // write(fd, cp, 1);
-        render_effect(fd, *cp);
-        cp++;
-    }
+  for (x = 0; x < len; x++) {
+    list[x] = -1;
+  }
 }
 
 /*
     These are harry "timeout" events.
 
     These happen when we've been sitting around awhile.
-    
+
  */
+
+#define MAX_HARRY_EVENT_DUPS 2
+int last_seen_harry_event[MAX_HARRY_EVENT_DUPS];
+
 void harry_event(int fd) {
-    // Make something happen
-    char buffer[100];
-    int r = random() % 5;
-
-    // This is no where near finished, BUT!
-    // TO FIX: Remember the last phrase used,
-    // and don't repeat (the last two)!
-
-    const char * phrases[] = 
-    {
-        "Hahaha",
-        "Snicker, snicker",
-        "Boo!",
-        "MeOW",
-        "I see U"
-    };
-    const char * cp;
+  // Make something happen
+  char buffer[100];
+  int r;
+  // This is no where near finished, BUT!
+  const char *phrases[] = {"Hahaha", "Snicker, snicker", "Boo!", "MeOW",
+                           "I see U"};
+  const char *cp;
 
-    cp = phrases[r];
+  // Remember the last phrase used,
+  // and don't repeat (the last two)!
 
-    sprintf(buffer, "^S2%s^P2^D%02d", cp, (int)strlen(cp));
+  do {
+    r = random() % 5;
+  } while (have_seen(last_seen_harry_event, MAX_HARRY_EVENT_DUPS, r));
 
-    ZF_LOGD( "harry_event: render(%d, \"%s\")", fd, buffer);
+  ZF_LOGD("%d => %d %d", r, last_seen_harry_event[0], last_seen_harry_event[1]);
 
-    render(fd, buffer);
+  cp = phrases[r];
+
+  sprintf(buffer, "^S2%s^P2^D%02d", cp, (int)strlen(cp));
+
+  ZF_LOGD("harry_event: render(%d, \"%s\")", fd, buffer);
+
+  render(fd, buffer);
 }
 
-/* 
+void init_harry() {
+  init_have_seen(last_seen_harry_event, MAX_HARRY_EVENT_DUPS);
+  ZF_LOGD("init => %d %d", last_seen_harry_event[0], last_seen_harry_event[1]);
+}
+
+/*
 NOTE: Logging is single file, don't use in production!
 It won't handle multiple writers.
 */
-char * username = NULL;
-char * fullname = NULL;
+char *username = NULL;
+char *fullname = NULL;
 
 /*
 Pascal String Copy.  Copy from pascal string, to C String.
 
-First char is pascal string length.  (Max 255). 
+First char is pascal string length.  (Max 255).
  */
-void pcopy(char * pstring, char * str) {
-    int len = (int)*pstring;
-    strncpy( str, pstring+1, len );
-    str[len] = 0;
+void pcopy(char *pstring, char *str) {
+  int len = (int)*pstring;
+  strncpy(str, pstring + 1, len);
+  str[len] = 0;
 }
 
 /*
@@ -465,384 +528,457 @@ This only works for those few idiots that use the
 horribly broken SSH crap that Mystic uses.
  */
 int locate_user(const char *alias) {
-    FILE * user;
-    char buffer[0x600];
-    char temp[100];
-
-    user = fopen("data/users.dat", "rb");
-    if (user == NULL)
-        return 0;
-
-    // Carry on!
-    while (fread(buffer, 0x600, 1, user) == 1) {
-        pcopy( buffer + 0x6d, temp );
-        if ( strcasecmp( temp, username) == 0) {
-            pcopy(buffer + 0x8c, temp );
-            fullname = strdup(temp);
-            break;
-        }
-        /*
-        printf("Alias: %s\n", temp);
-        pcopy(buffer + 0x8c, temp );
-        printf("Full Name: %s\n", temp );
-        */
+  FILE *user;
+  char buffer[0x600];
+  char temp[100];
+
+  user = fopen("data/users.dat", "rb");
+  if (user == NULL)
+    return 0;
+
+  // Carry on!
+  while (fread(buffer, 0x600, 1, user) == 1) {
+    pcopy(buffer + 0x6d, temp);
+    if (strcasecmp(temp, username) == 0) {
+      pcopy(buffer + 0x8c, temp);
+      fullname = strdup(temp);
+      break;
     }
-    fclose(user);
-    return 1;
+    /*
+    printf("Alias: %s\n", temp);
+    pcopy(buffer + 0x8c, temp );
+    printf("Full Name: %s\n", temp );
+    */
+  }
+  fclose(user);
+  return 1;
 }
 
 // Buffers are BSIZE + 1, so a buffer that size can strcpy safely.
 
-
 regex_t ANSI;
 regex_t WORDS;
 regex_t WORD;
 
 int init_regex(void) {
-    int ret;
-    char ansi[] = "\x1b\[[0-9]+(;[0-9]+)*?[a-zA-Z]";
-    char words[] = "[a-zA-Z]+( [a-zA-Z]+)+";
-    char word[] = "[a-zA-Z]+";
-    char errorbuf[100];
-
-    if ( ret = regcomp( &ANSI, ansi, REG_EXTENDED|REG_NEWLINE ) ) {
-        regerror( ret, &ANSI, errorbuf, sizeof(errorbuf));
-        ZF_LOGW( "Regex %s failed to compile: %s", ansi, errorbuf);
-        return 0;
-    };
+  int ret;
+  char ansi[] = "\x1b\[[0-9]+(;[0-9]+)*?[a-zA-Z]";
+  char words[] = "[a-zA-Z]+( [a-zA-Z]+)+";
+  char word[] = "[a-zA-Z]+";
+  char errorbuf[100];
+
+  if (ret = regcomp(&ANSI, ansi, REG_EXTENDED | REG_NEWLINE)) {
+    regerror(ret, &ANSI, errorbuf, sizeof(errorbuf));
+    ZF_LOGW("Regex %s failed to compile: %s", ansi, errorbuf);
+    return 0;
+  };
 
-    if ( ret = regcomp( &WORDS, words, REG_EXTENDED|REG_NEWLINE ) ) {
-        regerror( ret, &WORDS, errorbuf, sizeof(errorbuf));
-        ZF_LOGW( "Regex %s failed to compile: %s", words, errorbuf);
-        return 0;
-    };
+  if (ret = regcomp(&WORDS, words, REG_EXTENDED | REG_NEWLINE)) {
+    regerror(ret, &WORDS, errorbuf, sizeof(errorbuf));
+    ZF_LOGW("Regex %s failed to compile: %s", words, errorbuf);
+    return 0;
+  };
 
-    if ( ret = regcomp( &WORD, word, REG_EXTENDED|REG_NEWLINE ) ) {
-        regerror( ret, &WORD, errorbuf, sizeof(errorbuf));
-        ZF_LOGW( "Regex %s failed to compile: %s", word, errorbuf);
-        return 0;
-    };
+  if (ret = regcomp(&WORD, word, REG_EXTENDED | REG_NEWLINE)) {
+    regerror(ret, &WORD, errorbuf, sizeof(errorbuf));
+    ZF_LOGW("Regex %s failed to compile: %s", word, errorbuf);
+    return 0;
+  };
 
-    return 1;
+  return 1;
 }
 
-int regmatch( regex_t *preg, const char * string, size_t nmatch, regmatch_t pmatch[], int eflags) {
-    // returns number of matches found.  (Max nmatch)
-    int matches = 0;
-    int offset = 0;
-    int ret;
-
-    while (matches < nmatch) {
-        ret = regexec( preg, string + offset, nmatch - matches, pmatch + matches, eflags);
-        if (!ret) {
-            int current = offset;
-            offset += pmatch[matches].rm_eo;
-            pmatch[matches].rm_so += current;
-            pmatch[matches].rm_eo += current;
-            matches++;
-        } else if (ret == REG_NOMATCH ) {
-            break;
-        } else {
-            break;
-        }
+int regmatch(regex_t *preg, const char *string, size_t nmatch,
+             regmatch_t pmatch[], int eflags) {
+  // returns number of matches found.  (Max nmatch)
+  int matches = 0;
+  int offset = 0;
+  int ret;
+
+  while (matches < nmatch) {
+    ret = regexec(preg, string + offset, nmatch - matches, pmatch + matches,
+                  eflags);
+    if (!ret) {
+      int current = offset;
+      offset += pmatch[matches].rm_eo;
+      pmatch[matches].rm_so += current;
+      pmatch[matches].rm_eo += current;
+      matches++;
+    } else if (ret == REG_NOMATCH) {
+      break;
+    } else {
+      break;
     }
-    return matches;
-} 
+  }
+  return matches;
+}
 
-#define MAX_MATCH 10
-regmatch_t rxmatch[ MAX_MATCH ];
+#define MAX_MATCH 32
+regmatch_t rxmatch[MAX_MATCH];
 
-int rx_match(regex_t * regex, const char * buffer) {
-    int ret;
+int rx_match(regex_t *regex, const char *buffer) {
+  int ret;
 
-    ret = regmatch(regex, buffer, MAX_MATCH, rxmatch, 0);
-    for( int i = 0; i < ret; i++ ) {
-        ZF_LOGI("%d : (%d-%d)", i, rxmatch[i].rm_so, rxmatch[i].rm_eo);
+  ret = regmatch(regex, buffer, MAX_MATCH, rxmatch, 0);
+  if (0) {
+    for (int i = 0; i < ret; i++) {
+      ZF_LOGI("%d : (%d-%d)", i, rxmatch[i].rm_so, rxmatch[i].rm_eo);
     }
-    return ret;
+  }
+  return ret;
 }
 
-int mangle( int fd, char * buffer ) {
-    int x, i;
-    int m = 0;
-
-    // Ok, we want to look for words to:
-    // MaNGlE , or transpose (both?!)
-    // or possibly transpose words
-    char work[ BSIZE + 1];
+/*
+Terminal processing section.
 
-    ZF_LOGI("mangle: %s", repr(buffer));
-    strcpy( work, buffer );
+Monitor lines, position, color.
 
-    /* 
-    NOTE:  We copy the buffer, so we can clear out ANSI codes, etc.  
-    Otherwise we might mess some ANSI up!
-    */
-
-    /*
-     (random) Look for ANSI CLS and:
+What screen size do I want to emulate?
 
-        display random spooky texts around, with delays ... then CLS.
+ANSI codes.
 
-        display ANSI graphic file, with delays ... then CLS
-     */
+Do I need this??
 
-    /* work -- clear out ANSI so we don't mangle ANSI codes. */
-    x = rx_match(&ANSI, work );
-    if ( x > 0) {
-        ZF_LOGD("found %d ANSI", x);
-        for( i = 0; i < x; i++ ) {
-            memset(work + rxmatch[i].rm_so, '-', rxmatch[i].rm_eo - rxmatch[i].rm_so);
-            ZF_LOGD( "Now %d : %s", i, repr(work));
-        };
-    }
+*/
 
-    ZF_LOGI("mangle: %s", repr(work));
+int random_activate(int w) {
+  int r = random() % 100;
+  if (r <= (w * 10)) {
+    return 1;
+  };
+  return 0;
+}
 
-    /*
-     (random) Locate words (in work), and possibly flip them around.
-     Transpose words.  Transpose case.  Transpose letters.
-     */
-    x = rx_match(&WORDS, work );
-    ZF_LOGD("found %d WORDS", x );    
-
-    if ( x > 0 ) {    
-        for( i = 0; i < x; i++ ) {
-            // Do things here.
-            if ( i % 3 == 0 ) {
-                for( int p = rxmatch[i].rm_so; p < rxmatch[i].rm_eo; p++ ) {
-                    buffer[p] = tolower(buffer[p]);
-                    m++;
-                }
-            } else {
-                if ( i % 3 == 1 ) {
-                    for( int p = rxmatch[i].rm_so; p < rxmatch[i].rm_eo; p++ ) {
-                        buffer[p] = toupper(buffer[p]);
-                        m++;
-                    }
-                }
-            }
+/*
+ * The buffer that we've been given is much larger now.
+ *
+ */
+int mangle(int fd, char *buffer) {
+  int x, i;
+  int need_render = 0; // changing word case around doesn't need the render
+  int mangled = 0;
+
+  char *cp;
+
+  // Ok, we want to look for words to:
+  // MaNGlE , or transpose (both?!)
+  // or possibly transpose words
+  char work[(BSIZE * 4) + 1];
+
+  ZF_LOGI("mangle(%s)", repr(buffer));
+  strcpy(work, buffer);
+
+  /*
+  NOTE:  We copy the buffer, so we can clear out ANSI codes, etc.
+  Otherwise we might mess some ANSI up in the manglying
+  process.
+  */
+
+  /*
+   (random) Look for ANSI CLS and:
+
+      display random spooky texts around, with delays ... then CLS.
+
+      display ANSI graphic file, with delays ... then CLS
+   */
+  const char *ANSI_CLS = "\x1b[2J";
+  cp = strstr(buffer, ANSI_CLS);
+
+  if (cp != NULL) {
+    ZF_LOGI("seen: ANSI_CLS");
+
+    if (random_activate(9)) {
+      char display[100] = "";
+
+      ZF_LOGI("mangle(ANSI_CLS)");
+      // sprintf( display, "^P2...");
+      // This string actually screws up ANSI detection (takes too long)
+      // strcpy(display, "^P2^S501234567890^P1abcdef^P2g^P3h^P4i^S0^P2");
+
+      strcpy(display, "^P2^S301234^P15^S0^P2");
+      // Move the buffer so there's room for the display string.
+      memmove(cp + strlen(display), cp, strlen(cp) + 1);
+      strncpy(cp, display, strlen(display));
+      ZF_LOGI("mangle(ANSI_CLS): (%d) %s", (int)strlen(buffer), repr(buffer));
+      need_render = 1;
+
+      /*
+      Copy the new buffer over, but hide our "render" code
+      from the remaining mangler steps.
+      */
+      strcpy(work, buffer);
+      i = cp - buffer;
+      // find offset into "buffer"
+      // apply to work.
+      memset(work + i, ' ', strlen(display));
+    };
+  }
+
+  /* work -- clear out ANSI so we don't mangle ANSI codes. */
+  x = rx_match(&ANSI, work);
+  char replace_with = ' ';
+  if (x > 0) {
+    ZF_LOGD("found %d ANSI", x);
+    for (i = 0; i < x; i++) {
+      memset(work + rxmatch[i].rm_so, replace_with,
+             rxmatch[i].rm_eo - rxmatch[i].rm_so);
+    };
+    ZF_LOGD("Work Now : (%d) %s", (int)strlen(work), repr(work));
+  }
+
+  // ZF_LOGI("mangle: %s", repr(work));
+
+  /*
+   (random) Locate words (in work), and possibly flip them around.
+   Transpose words.  Transpose case.  Transpose letters.
+   */
+  x = rx_match(&WORDS, work);
+  ZF_LOGD("found %d WORDS", x);
+
+  if (x > 0) {
+    for (i = 0; i < x; i++) {
+      // Do things here.
+      if (i % 3 == 0) {
+        for (int p = rxmatch[i].rm_so; p < rxmatch[i].rm_eo; p++) {
+          buffer[p] = tolower(buffer[p]);
+          mangled++;
         }
+      } else {
+        if (i % 3 == 1) {
+          for (int p = rxmatch[i].rm_so; p < rxmatch[i].rm_eo; p++) {
+            buffer[p] = toupper(buffer[p]);
+            mangled++;
+          }
+        }
+      }
     }
-
-    /*
-     (random) Locate single words, and transpose them.  Transpose case.
-     Transpose letters. 
-     */
-
-    /*
-     (random) Display up to certain point.  Delay.
-     Print some characters slowly.  Delay.
-     */
-
-    if (m) {
-        ZF_LOGD("HH %d : %s", m, repr(buffer));
+  }
+
+  /*
+   (random) Locate single words, and transpose them.  Transpose case.
+   Transpose letters.
+   */
+
+  /*
+   (random) Display up to certain point.  Delay.
+   Print some characters slowly.  Delay.
+   */
+
+  if (need_render) {
+    ZF_LOGD("HH %d : (%d) %s", need_render, (int)strlen(buffer), repr(buffer));
+  } else {
+    if (mangled) {
+      ZF_LOGD("Mangled %d : %s", mangled, repr(buffer));
     }
+  }
 
+  if (need_render) {
+    render(fd, buffer);
+  } else {
     write(fd, buffer, strlen(buffer));
-    return m;
+  };
+  return need_render && mangled;
 }
 
-int harry_happens( time_t *last_event, int wakeup ) {
-    time_t now = time(NULL);
-    int elapsed = now - *last_event;
+int harry_happens(time_t *last_event, int wakeup) {
+  time_t now = time(NULL);
+  int elapsed = now - *last_event;
 
-    if ( elapsed > wakeup ) {
-        // Ok!  It's been too long since we've done something.
-        *last_event = now;
-        return 1;
-    }
-    return 0;
+  if (elapsed > wakeup) {
+    // Ok!  It's been too long since we've done something.
+    *last_event = now;
+    return 1;
+  }
+  return 0;
 }
 
-int main(int argc, char * argv[])
-{
-    int master;
-    pid_t pid;
-    int node = -1;
-
-    file_output_open("horrible_harry.log");
-
-    srandom(time(NULL));
+int main(int argc, char *argv[]) {
+  int master;
+  pid_t pid;
+  int node = -1;
 
-    // ./mystic -TID7 -IP192.168.0.1 -HOSTUnknown -ML1 -SL0 -ST2 -CUnknown -Ubugz -PUWISHPASSWORD
-    // ./mystic -TID7 -IP192.168.0.1 -HOSTUnknown -ML0 -SL0 -ST0 -CUnknown
+  file_output_open("horrible_harry.log");
+  init_harry();
 
-    // ./mystic -TID7 -IP192.168.0.1 -HOSTUnknown -ML1 -SL0 -ST2 -CUnknown -Ubugz -PUP2LAT3
-    // ./mystic -TID7 -IP192.168.0.1 -HOSTUnknown -ML0 -SL0 -ST0 -CUnknown
+  srandom(time(NULL));
 
-    // ./mystic -TID7 -IP192.168.0.1 -HOSTUnknown -ML0 -SL0 -ST0 -CUnknown
-    // ./mystic -TID9 -IP192.168.0.1 -HOSTUnknown -ML0 -SL1 -ST0 -CUnknown
+  // ./mystic -TID7 -IP192.168.0.1 -HOSTUnknown -ML1 -SL0 -ST2 -CUnknown -Ubugz
+  // -PUWISHPASSWORD
+  // ./mystic -TID7 -IP192.168.0.1 -HOSTUnknown -ML0 -SL0 -ST0 -CUnknown
 
-    // ./mystic -TID7 -IP192.168.0.1 -HOSTUnknown -ML1 -SL0 -ST2 -CUnknown -Ubugz -PUP2LAT3
-    // ./mystic -TID9 -IP192.168.0.1 -HOSTUnknown -ML1 -SL1 -ST2 -CUnknown -Ubugz -PUP2LAT3
+  // ./mystic -TID7 -IP192.168.0.1 -HOSTUnknown -ML1 -SL0 -ST2 -CUnknown -Ubugz
+  // -PUP2LAT3
+  // ./mystic -TID7 -IP192.168.0.1 -HOSTUnknown -ML0 -SL0 -ST0 -CUnknown
 
-    // SSH:  -ML1 -ST2
-    // Telnet: -ML0 -ST0
+  // ./mystic -TID7 -IP192.168.0.1 -HOSTUnknown -ML0 -SL0 -ST0 -CUnknown
+  // ./mystic -TID9 -IP192.168.0.1 -HOSTUnknown -ML0 -SL1 -ST0 -CUnknown
 
+  // ./mystic -TID7 -IP192.168.0.1 -HOSTUnknown -ML1 -SL0 -ST2 -CUnknown -Ubugz
+  // -PUP2LAT3
+  // ./mystic -TID9 -IP192.168.0.1 -HOSTUnknown -ML1 -SL1 -ST2 -CUnknown -Ubugz
+  // -PUP2LAT3
 
-    // Locate username (if given) in the command line
-    // -U<username>
-    for (int x = 0; x < argc; x++) {
-        if (strncmp("-U", argv[x], 2) == 0) {
-            username = argv[x] + 2;
-            ZF_LOGI( "Username: [%s]", username);
-        };
-        if (strncmp("-SL", argv[x], 3) == 0) {
-            node = argv[x][3] - '0' + 1;
-            ZF_LOGI( "Node: %d", node);
-        }
-    }
+  // SSH:  -ML1 -ST2
+  // Telnet: -ML0 -ST0
 
-    if (username != NULL) {
-        locate_user(username);
-        ZF_LOGD( "Username: [%s] A.K.A. [%s]", username, fullname);
-    }
-
-    if ( !init_regex() )
-        return 2;
-
-    // With IGNBRK  I don't think I need this anymore.  (Nope!)
-    // signal(SIGINT, SIG_IGN);
-
-    pid = forkpty(&master, NULL, NULL, NULL);
-
-    // impossible to fork
-    if (pid < 0) {
-        return 1;
-    }
-
-    // child
-    else if (pid == 0) {
-        char *args[20];     // max 20 args
-        int x;
-        args[0] = TARGET;
-
-        for ( x = 1; x < argc; x++ ) {
-            args[x] = argv[x];
-        };
-        args[x] = NULL;
-        // run Mystic, run!
-        execvp( TARGET, args);
+  // Locate username (if given) in the command line
+  // -U<username>
+  for (int x = 0; x < argc; x++) {
+    if (strncmp("-U", argv[x], 2) == 0) {
+      username = argv[x] + 2;
+      ZF_LOGI("Username: [%s]", username);
+    };
+    if (strncmp("-SL", argv[x], 3) == 0) {
+      node = argv[x][3] - '0' + 1;
+      ZF_LOGI("Node: %d", node);
     }
+  }
 
-    // parent
-    else {
-        // remove the echo
-        // ICANON - raw mode.  No more line buffering!
-        struct termios tios, orig1;
-        struct timeval timeout;
-        time_t last_event = 0; // time(NULL);
-
-        ZF_LOGD("starting");
+  if (username != NULL) {
+    locate_user(username);
+    ZF_LOGD("Username: [%s] A.K.A. [%s]", username, fullname);
+  }
 
-        tcgetattr(master, &tios);
-        tios.c_lflag &= ~(ECHO | ECHONL | ICANON );
-        tcsetattr(master, TCSAFLUSH, &tios);
+  if (!init_regex())
+    return 2;
 
-        tcgetattr(1, &orig1);
-        tios = orig1; 
-        tios.c_iflag &= ~(ICRNL | IXON);  // Disable software flow control
+  // With IGNBRK  I don't think I need this anymore.  (Nope!)
+  // signal(SIGINT, SIG_IGN);
 
-        tios.c_lflag &= ~(ECHO | ECHONL | ICANON | ISIG | IEXTEN ); 
+  pid = forkpty(&master, NULL, NULL, NULL);
 
-        // https://viewsourcecode.org/snaptoken/kilo/02.enteringRawMode.html
-        // ISIG should be Ctrl-C and Ctrl-Z IGNBRK + 
-        tcsetattr(1, TCSAFLUSH, &tios);
-
-
-        for (;;) {
-
-            // define estruturas para o select, que serve para verificar qual
-            // se tornou "pronto pra uso"
-            fd_set          read_fd;
-            fd_set          write_fd;
-            fd_set          except_fd;
-
-            // inicializa as estruturas
-            FD_ZERO(&read_fd);
-            FD_ZERO(&write_fd);
-            FD_ZERO(&except_fd);
-
-            // atribui o descritor master, obtido pelo forkpty, ao read_fd
-            FD_SET(master, &read_fd);
-            // atribui o stdin ao read_fd
-            FD_SET(STDIN_FILENO, &read_fd);
-
-            // o descritor tem que ser unico para o programa, a documentacao
-            // recomenda um calculo entre os descritores sendo usados + 1
-
-            /*
-             TODO:  Figure out how this would work.
-
-             I'm thinking something like timeouts 30-50 seconds?
-             And as we get closer, 15-25 seconds.
-            */
-
-            // we're in luck!  The last parameter is time interval/timeout.  :D
-            timeout.tv_sec = 10;
-            timeout.tv_usec = 0;
+  // impossible to fork
+  if (pid < 0) {
+    return 1;
+  }
 
-            // select(master+1, &read_fd, &write_fd, &except_fd, NULL);
-            if ( select(master+1, &read_fd, &write_fd, &except_fd, &timeout) == 0 ) {
-                // This means timeout!
-                ZF_LOGI("TIMEOUT");                
-                harry_event(STDOUT_FILENO);
-            }
+  // child
+  else if (pid == 0) {
+    char *args[20]; // max 20 args
+    int x;
+    args[0] = TARGET;
 
-            char input[BSIZE + 1];
-            char output[BSIZE + 1];
-            int total;
-
-            // read_fd esta atribuido com read_fd?
-            if (FD_ISSET(master, &read_fd))
-            {
-                // leia o que bc esta mandando
-                if ((total = read(master, &output, BSIZE)) != -1) {
-                    // e escreva isso na saida padrao
-                    output[total] = 0;
-
-                    if (harry_happens( &last_event, 5)) {
-                        ZF_LOGI( "harry_happens");
-                        if ( mangle( STDOUT_FILENO, output ) == 0 ) {
-                            // failed, so.  Try again.
-                            last_event = 0;
-                        }
-                    } else {
-                        write(STDOUT_FILENO, &output, total);
-                        // This is OUTPUT from the BBS
-                        // ZF_LOGI( ">> %s", repr(output));
-                        ZF_LOGI( ">> %d chars", (int)strlen(output));
-                        //   I think repr is flipping out here.  :(
-                        // ZF_LOGI( ">> %d", (int)strlen(repr(output)));
-                    }
-
-                } else
-                    break;
+    for (x = 1; x < argc; x++) {
+      args[x] = argv[x];
+    };
+    args[x] = NULL;
+    // run Mystic, run!
+    execvp(TARGET, args);
+  }
+
+  // parent
+  else {
+    // remove the echo
+    // ICANON - raw mode.  No more line buffering!
+    struct termios tios, orig1;
+    struct timeval timeout;
+    time_t last_event = 0; // time(NULL);
+
+    ZF_LOGD("starting");
+
+    tcgetattr(master, &tios);
+    tios.c_lflag &= ~(ECHO | ECHONL | ICANON);
+    tcsetattr(master, TCSAFLUSH, &tios);
+
+    tcgetattr(1, &orig1);
+    tios = orig1;
+    tios.c_iflag &= ~(ICRNL | IXON); // Disable software flow control
+
+    tios.c_lflag &= ~(ECHO | ECHONL | ICANON | ISIG | IEXTEN);
+
+    // https://viewsourcecode.org/snaptoken/kilo/02.enteringRawMode.html
+    // ISIG should be Ctrl-C and Ctrl-Z IGNBRK +
+    tcsetattr(1, TCSAFLUSH, &tios);
+
+    for (;;) {
+
+      // define estruturas para o select, que serve para verificar qual
+      // se tornou "pronto pra uso"
+      fd_set read_fd;
+      fd_set write_fd;
+      fd_set except_fd;
+
+      // inicializa as estruturas
+      FD_ZERO(&read_fd);
+      FD_ZERO(&write_fd);
+      FD_ZERO(&except_fd);
+
+      // atribui o descritor master, obtido pelo forkpty, ao read_fd
+      FD_SET(master, &read_fd);
+      // atribui o stdin ao read_fd
+      FD_SET(STDIN_FILENO, &read_fd);
+
+      // o descritor tem que ser unico para o programa, a documentacao
+      // recomenda um calculo entre os descritores sendo usados + 1
+
+      /*
+       TODO:  Figure out how this would work.
+
+       I'm thinking something like timeouts 30-50 seconds?
+       And as we get closer, 15-25 seconds.
+      */
+
+      // we're in luck!  The last parameter is time interval/timeout.  :D
+      timeout.tv_sec = 10;
+      timeout.tv_usec = 0;
+
+      // select(master+1, &read_fd, &write_fd, &except_fd, NULL);
+      if (select(master + 1, &read_fd, &write_fd, &except_fd, &timeout) == 0) {
+        // This means timeout!
+        ZF_LOGI("TIMEOUT");
+        harry_event(STDOUT_FILENO);
+      }
+
+      char input[BSIZE + 1];
+      static char output[(BSIZE * 4) + 1];
+      int total;
+
+      // read_fd esta atribuido com read_fd?
+      if (FD_ISSET(master, &read_fd)) {
+        // leia o que bc esta mandando
+        if ((total = read(master, &output, BSIZE)) != -1) {
+          // e escreva isso na saida padrao
+          output[total] = 0;
+
+          // if ( harry_happens( &last_event, 5)) {
+          if (1) {
+            ZF_LOGI("harry_happens");
+            if (mangle(STDOUT_FILENO, output) == 0) {
+              // failed, so.  Try again.
+              last_event = 0;
             }
-
-            // read_fd esta atribuido com a entrada padrao?
-            if (FD_ISSET(STDIN_FILENO, &read_fd))
-            {
-                // leia a entrada padrao
-                total = read(STDIN_FILENO, &input, BSIZE);
-                input[total] = 0;
-                // e escreva no bc
-                ZF_LOGI( "<< %s", repr(input));
-
-                write(master, &input, total);
-
-                // This is INPUT from the USER
-                // ZF_LOGI_MEM( input, strlen(input), "<< ");
-                
-             }
-        }
-
-        // Restore terminal
-        tcsetattr(1, TCSAFLUSH, &orig1);
-        ZF_LOGD("exit");
+          } else {
+            write(STDOUT_FILENO, &output, total);
+            // This is OUTPUT from the BBS
+            // ZF_LOGI( ">> %s", repr(output));
+            ZF_LOGI(">> %d chars", (int)strlen(output));
+            //   I think repr is flipping out here.  :(
+            // ZF_LOGI( ">> %d", (int)strlen(repr(output)));
+          }
+
+        } else
+          break;
+      }
+
+      // read_fd esta atribuido com a entrada padrao?
+      if (FD_ISSET(STDIN_FILENO, &read_fd)) {
+        // leia a entrada padrao
+        total = read(STDIN_FILENO, &input, BSIZE);
+        input[total] = 0;
+        // e escreva no bc
+        ZF_LOGI("<< %s", repr(input));
+
+        write(master, &input, total);
+
+        // This is INPUT from the USER
+        // ZF_LOGI_MEM( input, strlen(input), "<< ");
+      }
     }
 
-    return 0;
-}
+    // Restore terminal
+    tcsetattr(1, TCSAFLUSH, &orig1);
+    ZF_LOGD("exit");
+  }
 
+  return 0;
+}