#include "xcp.h"
#include <limits.h>
#include <unistd.h>
#include <X11/Intrinsic.h>

extern head_path_struct *file_list_head;
extern int prefs, uidlength, grplength, sortby, side;
extern uidname *uidlist, *grplist;
extern struct commands {
       char name[20], instruction[50];
     } command_list[17];
extern struct pathstruct {
       char name[MAXBUTTONLENGTH], expand[MAXPATHLEN];
     } top_button_list[8];
extern char left_name[MAXPATHLEN], right_name[MAXPATHLEN], 
       new_left_name[MAXPATHLEN], new_right_name[MAXPATHLEN], *another_settings;
extern index_struct left_index, right_index;
extern XtAppContext app;
extern Widget left_path, right_path;

/* function declarations */
extern void make_selected_file_list();
extern XmString make_correct();
extern file_list_struct *map_name_to_file();
int idcompare();
void getuidlist(), getgrplist(), make_sorted_list(), add_file_to_dir(),
     getvalue(), remove_item_dir(), remove_item(), dtrt(char *path), got_stuff(),
     getuname(), write_settings(), read_settings(), get_home();
head_path_struct *add_to_list();
/*  done with functions */

/*  global pipes for IPC  */
int pipefd[2] = { -1, -1 };  /*  doesn't exist yet  */
/* done with globals  */

head_path_struct *add_to_list(new)
char *new;
{
  head_path_struct *tail, *list = file_list_head;
  file_list_struct *back=NULL, *front;  /*  grrr.. i hate variable names!!!!   */
  DIR *directory;
  struct dirent *current;
  struct stat buf;
  char link_nm[MAXPATHLEN], name_buf[MAXPATHLEN];
  int count, dir_length;

/* to make sure the path is resolved and the same as in list  */

  if (chdir(new))   /* chdir failed  */
    return(NULL);
  getwd(new);
  tail=list;
  while (list)
    if (!strcmp(list->pathname, new))
      break;  /*  found it!!   */
    else{
      tail=list;
      list=list->next_path;
    }
  if (!list){   /*  item not found.. needs to be added */
    if (!(list = (head_path_struct *)malloc(sizeof(head_path_struct)))){
      perror("malloc:");
      exit(1);
    }
    /* assign file_list_head if it is NULL  */
    if (!file_list_head)
      file_list_head=list;
    if ((tail !=list ) && (tail != NULL))
      tail->next_path=list;
    list->next_path=NULL;
    strcpy(list->pathname, new);
    list->first_file=NULL;
    list->total_files=0;
    strcpy(name_buf, new);
    dir_length = strlen(new);
    if (name_buf[dir_length-1] != '/'){
      name_buf[dir_length] = '/';
      name_buf[dir_length] = '\0';
      dir_length++;
    }
    if ((directory = opendir(new))==NULL){
      perror("opendir");
      exit(1);
    }
    else{
      while (current=readdir(directory))
	if ((current->d_ino) && (strcmp(current->d_name, ".")) && 
	    (strcmp(current->d_name, ".."))){
	  working();
	  (list->total_files)++;
	  front=(file_list_struct *)malloc(sizeof(file_list_struct));
	  if (back==NULL)
	    list->first_file=front;
	  else
	    back->next_file=front;
	  back=front;
	  front->next_file=NULL;
	  strcpy(front->filename, current->d_name);
	  front->link_info=NOTHING;
	  lstat(front->filename, &buf);
	  memcpy(&front->stats, &buf, sizeof(struct stat));  /* store stats on file */
	  if ((front->stats.st_mode & S_IFMT) == S_IFLNK) {
	    if(!stat(front->filename, &buf)){
	      if ((buf.st_mode & S_IFMT) == S_IFDIR)
		front->link_info = IS_DIRECTORY | IS_LINK;
	      else
		front->link_info = IS_FILE | IS_LINK;
	      count=readlink (front->filename, link_nm, MAXPATHLEN-1);
	      front->fullname = (char *) malloc ((count+1) * sizeof(char));
	      strncpy(front->fullname, link_nm, count);
	      front->fullname[count]='\0';
	    }
	    else{  /* stat failed.. maybe a symlink to a nonexistent place?  */
	      if ((count=readlink (front->filename, link_nm, MAXPATHLEN-1)) != -1){
		front->link_info = IS_FILE | IS_LINK;
		front->fullname = (char *) malloc ((count+1) * sizeof(char));
		strncpy (front->fullname, link_nm, count);
		front->fullname[count]='\0';
	      }
	      else{  /*pretend its a silly file  */
		front->link_info = IS_FILE;
		name_buf[dir_length-1] = '/';
		strcpy((name_buf + dir_length*sizeof(char)), front->filename);
		front->fullname = (char *)malloc((strlen(name_buf)+1) * sizeof(char));
		strcpy (front->fullname, name_buf);
	      }
	    }
	  }
	  else{
	    if ((front->stats.st_mode & S_IFMT) == S_IFDIR)
	      front->link_info = IS_DIRECTORY;
	    else
	      front->link_info = IS_FILE;
	    name_buf[dir_length-1] = '/';
	    strcpy((name_buf + dir_length*sizeof(char)), front->filename);
	    front->fullname = (char *)malloc((strlen(name_buf)+1) * sizeof(char));
	    strcpy (front->fullname, name_buf);
	  }
	  front->name_str = (XmString) make_correct(front, 0);  /* makes short  */
	  front->fullname_str = (XmString) make_correct(front, 1);  /* makes long  */
	}
      closedir(directory);
      return(list);
    }
  }
  else    /*  item found  */
    return(list);
}

void get_home(result)
char *result;
{
  char **cpp, home[MAXPATHLEN];
  struct passwd *pwd;
 
  if (strcpy(result,(char *)getenv("HOME")))
    return;
  if (pwd = getpwuid(getuid())){
    strcpy(result, pwd->pw_dir);
    return;
  }
#ifdef HESIOD
  if (cpp=(char **)hes_resolve(getlogin(),"filsys")){
    sscanf(*cpp,"AFS %s w %s",result,home);
    return;
  }
#endif
  else{
    strcpy(result, "/");
    return;
  }
}

void getname(name,x,y)
/*  returns the name of the button in row x, col y  */
char *name;
int x,y;
{  int num;

   num=y+6*x;
   strcpy(name,"emacs");
   if (num>9){
     name[5]='1';
     num-=10;
   }
   else
     name[5]='0';
   name[6]=(48+num);
   name[7]='\0';
   return;
}    

void make_sorted_list(items, traverse, list_length, current_side)
XmString *items;
file_list_struct *traverse;
int list_length, current_side;
{
  int x=0;
  int length=0;
  index_struct *index;

  if (current_side){
    right_index.total = list_length;
    right_index.file = (file_list_struct **)
      realloc(right_index.file, sizeof(file_list_struct) * list_length);
    index = &right_index;
  }
  else{
    left_index.total = list_length;
    left_index.file = (file_list_struct **)
      realloc(left_index.file, sizeof(file_list_struct) * list_length);
    index = &left_index;
  }
  
  while (traverse != NULL){
    x=0;
    while ((x != length) && (compare(index->file, traverse, x) > 0))
      x++;
    if (x!=length)
      memmove(&index->file[x+1], &index->file[x], ((length-x) * sizeof(file_list_struct *)));
    index->file[x] = traverse;
    length++;
    traverse=traverse->next_file;
  }
  for (x=0;x<length;x++)
    if (prefs & FULLNAME)
      items[x] = index->file[x]->fullname_str;
    else
      items[x] = index->file[x]->name_str;
}

int compare(list, item, y)
file_list_struct *list[], *item;
int y;
{
  if (prefs & DIR_FIRST){
    if (((((item->stats.st_mode) & S_IFMT)==S_IFDIR) ||
	 (item->link_info & IS_DIRECTORY)) &&
	((((list[y]->stats.st_mode) & S_IFMT) != S_IFDIR) ||
	 !(list[y]->link_info & IS_DIRECTORY)))
      return(0);
    if (((((item->stats.st_mode) & S_IFMT)!=S_IFDIR) ||
	 !(item->link_info & IS_DIRECTORY)) &&
	((((list[y]->stats.st_mode) &S_IFMT) == S_IFDIR) ||
	 (list[y]->link_info & IS_DIRECTORY)))
      return(1);
  }
  switch (sortby) {
  case BY_NAME:
    return (strcmp(item->filename, list[y]->filename));
  case BY_INODE:
    return ((int) (item->stats.st_ino - list[y]->stats.st_ino));
  case BY_SIZE:
    return ((int) (item->stats.st_size - list[y]->stats.st_size));
  case BY_DATE:
    return ((int) (item->stats.st_mtime - list[y]->stats.st_mtime));
  default:
    return (0);
  }
}

void getuname(result,uid)
char *result;
uid_t uid;
{
  int left=0, right=uidlength-1, temp;

  if (left>right){    /*  hmm... error in passwd??  */
    sprintf(result, "%-8d", uid);
    return;
  }

  while (left != right){
    if (uidlist[right].id == uid)
      break;
    if (uidlist[temp=((left+right)/2)].id < uid)
      left = temp + 1;
    else 
      right = temp;
  }
  if (uidlist[right].id == uid){
    sprintf(result,"%-8s", uidlist[right].name);
    return;
  }
  else {
    sprintf(result, "%-8d", uid);
    return;
  }
}

void getgrname(result,uid)
char *result;
uid_t uid;
{
  int left=0, right=grplength-1, temp;

  if (left>right){    /*  hmm... error in passwd??  */
    sprintf(result, "%-8d", uid);
    return;
  }

  while (left != right){
    if (grplist[right].id == uid)
      break;
    if (grplist[temp=((left+right)/2)].id < uid)
      left = temp + 1;
    else 
      right = temp;
}
  if (grplist[right].id == uid){
    sprintf(result,"%-8s", grplist[right].name);
    return;
  }
  else {
    sprintf(result, "%-8d", uid);
    return;
    }
}

void getuidlist()
{
  struct passwd *pwd;
  int x=0,y=50;
  
  uidlist = (uidname *)malloc(50 * sizeof(uidname));
  while(pwd=getpwent()){
    if (x==y){
      y+=50;
      uidlist = (uidname *)realloc(uidlist, y * (sizeof(uidname)));
    }
    uidlist[x].id = pwd->pw_uid;
    strncpy(uidlist[x++].name, pwd->pw_name, 9);
  }
  /* clean up the array */
  uidlist = (uidname *)realloc(uidlist, x * (sizeof(uidname)));
  uidlength = x;
  qsort(uidlist, uidlength, sizeof(uidname), idcompare);
}

void getgrplist()
{
  struct group *grp;
  int x=0,y=50;
  
  grplist = (uidname *)malloc(50 * sizeof(uidname));
  while(grp=getgrent()){
    if (x==y){
      y+=50;
      grplist = (uidname *)realloc(grplist, y * (sizeof(uidname)));
    }
    grplist[x].id = grp->gr_gid;
    strncpy(grplist[x++].name, grp->gr_name, 9); 
  }
  /* clean up the array */
  grplist = (uidname *)realloc(grplist, x * (sizeof(uidname)));
  grplength = x;
  qsort(grplist, grplength, sizeof(uidname), idcompare);
}

int idcompare(a, b)
uidname *a, *b;
{
  return ((a->id) - (b->id));
}


void write_settings()
{
  FILE *settings;
  char string1[MAXPATHLEN], *env_name;
  char result[MAXPATHLEN], *temp;
  int x;

  if (another_settings)
    settings = fopen(another_settings, "w");
  else{
    get_home(string1);
    strcat(string1, "/.xcprc");
    settings = fopen(string1, "w");
  }
  fprintf(settings, "prompt = %s\n", prefs&PROMPT ? "True" : "False");
  fprintf(settings, "prompt_quit = %s\n", prefs&PROMPT_QUIT ? "True" : "False");
  fprintf(settings, "dir_first = %s\n", prefs&DIR_FIRST ? "True" : "False");
  fprintf(settings, "fullname = %s\n", prefs&FULLNAME ? "True" : "False");
  fprintf(settings, "overwrite = %s\n", prefs&OVERWRITE ? "True" : "False");
  fprintf(settings, "sortby = ");
  switch(sortby){
  case BY_NAME:
    fprintf(settings, "NAME\n");
    break;
  case BY_INODE:
    fprintf(settings, "INODE\n");
    break;
  case BY_SIZE:
    fprintf(settings, "SIZE\n");
    break;
  case BY_DATE:
    fprintf(settings, "DATE\n");
    break;
  default:   /*  WTF!!!???  */
    fprintf(settings, "NAME\n");
    break;
  }
  get_home(result);
  fprintf(settings, "dir1 = ");
  temp=XmTextGetString(left_path);
  if (!strcmp(result, temp))
    fprintf(settings, "[HOME]\n");
  else
    fprintf(settings, "%s\n", temp);
  XtFree(temp);
  temp=XmTextGetString(right_path);
  fprintf(settings, "dir2 = ");
  if (!strcmp(result, temp))
    fprintf(settings, "[HOME]\n");
  else
    fprintf(settings, "%s\n", temp);
  XtFree(temp);
  for (x=0;x<4;x++){
    fprintf(settings,"top_left-%d = ",x+1);
    if (!strcmp(result, top_button_list[x].expand))
      fprintf(settings, "[HOME]\n");
    else
      fprintf(settings, "%s\n", top_button_list[x].expand);
    fprintf(settings,"top_left-%d_text = %s\n", x+1, top_button_list[x].name);
  }
  for (x=4;x<8;x++){
    fprintf(settings,"top_right-%d = ",x+1);
    if (!strcmp(result, top_button_list[x].expand))
      fprintf(settings, "[HOME]\n");
    else
      fprintf(settings, "%s\n", top_button_list[x].expand);
    fprintf(settings,"top_right-%d_text = %s\n", x+1, top_button_list[x].name);
  }
  fclose(settings);
}

void read_settings()
{ /* did i really write this crap??  !!!  */

  int x=0;
  char result[MAXPATHLEN], temp[20];

/* get the CD out of the way  */
  prefs=0;
  getvalue(result,"dir1");
  if (!strcmp(result, "[CD]")){
    getwd(new_left_name);
    getwd(left_name);
  }
  else if (!strcmp(result, "[HOME]")) {   
    get_home(left_name);
    strcpy(new_left_name,left_name);
  }
  else{
    strcpy(left_name, result);
    strcat(left_name, "/");
    strcpy(new_left_name, left_name);
  }
  getvalue(result, "dir2");
  if (!strcmp(result, "[CD]")){
    getwd(right_name);
    strcpy(new_right_name, right_name);
  }
  else if (!strcmp(result, "[HOME]")) {   
    get_home(right_name);
    strcpy(new_right_name,right_name);
  }
  else{
    strcpy(right_name, result);
    strcat(right_name, "/");
    strcpy(new_right_name, right_name);
  }
  /* prefs : */
  prefs = 0;
  getvalue(result,"prompt");
  if ((!strcmp(result, "True")) || (!strcmp(result, "TRUE")) || (!strcmp(result, "true")))
    prefs |=  PROMPT;
  getvalue(result,"prompt_quit");
  if ((!strcmp(result, "True")) || (!strcmp(result, "TRUE")) || (!strcmp(result, "true")))
    prefs |=  PROMPT_QUIT;
  getvalue(result,"dir_first");
  if ((!strcmp(result, "True")) || (!strcmp(result, "TRUE")) || (!strcmp(result, "true")))
    prefs |=  DIR_FIRST;
  getvalue(result,"fullname");
  if ((!strcmp(result, "True")) || (!strcmp(result, "TRUE")) || (!strcmp(result, "true")))
    prefs |=  FULLNAME;
  getvalue(result,"overwrite");
  if ((!strcmp(result, "True")) || (!strcmp(result, "TRUE")) || (!strcmp(result, "true")))
    prefs |=  OVERWRITE;

/* sort by : */
  getvalue(result,"sortby");
  if ((!strcmp(result, "BY_NAME")) || (!strcmp(result, "NAME")) || (!strcmp(result, "by_name")) 
      || (!strcmp(result, "name")))
    sortby =  BY_NAME;
  if ((!strcmp(result, "BY_INODE")) || (!strcmp(result, "INODE")) || (!strcmp(result, "by_inode")) 
      || (!strcmp(result, "inode")))
    sortby =  BY_INODE;
  if ((!strcmp(result, "BY_SIZE")) || (!strcmp(result, "SIZE")) || (!strcmp(result, "by_size")) 
      || (!strcmp(result, "size")))
    sortby =  BY_SIZE;
  if ((!strcmp(result, "BY_DATE")) || (!strcmp(result, "DATE")) || (!strcmp(result, "by_date")) 
      || (!strcmp(result, "date")))
    sortby =  BY_DATE;

/*  get the lower buttons  */
  for(x=0;x<=16;x++){
    sprintf(temp,"button-%d",x);
    getvalue(command_list[x].name, temp);
    sprintf(temp,"button-command-%d",x);
    getvalue(command_list[x].instruction, temp);
  }
/*  fine.. now get the directory buttons...  */
  for (x=0;x<4;x++){
    sprintf(temp,"top_left-%d",x+1);
    getvalue(top_button_list[x].expand, temp);
    if (!strcmp("[HOME]", top_button_list[x].expand))
      strcpy(top_button_list[x].expand, getenv("HOME"));
    if (!strcmp("[PARENT]",  top_button_list[x].expand))
      strcpy(top_button_list[x].expand, "../");
    strcat(temp,"_text");
    getvalue(top_button_list[x].name, temp);
  }
  for (x=4;x<8;x++){
    sprintf(temp,"top_right-%d",x-3);
    getvalue(top_button_list[x].expand, temp);
    if (!strcmp("[HOME]", top_button_list[x].expand))
      strcpy(top_button_list[x].expand, getenv("HOME"));
    if (!strcmp("[PARENT]",  top_button_list[x].expand))
      strcpy(top_button_list[x].expand, "../");
    strcat(temp,"_text");
    getvalue(top_button_list[x].name, temp);
  }
  getvalue(NULL, "!close!");
  if (uidlength==0)
    getuidlist();
  if (grplength==0)
    getgrplist();
  return;
}

void getvalue(result,name)
char *result;
char *name;
{ /* if name is "!close!" it will close the files */
   static FILE *settings=NULL, *defaults=NULL;
   FILE *use;
   char input[240], desc[240], *temp;
   int length, okay = 1;

   if (!strcmp(name,"!close!")){
     if ((settings!=(FILE *)-1) && (settings != NULL))
       fclose(settings);
     if (defaults != NULL)
       fclose(defaults);
     settings=defaults=NULL;
     return;
   }
   if (defaults==NULL)
     if ((defaults=fopen(DEFAULT_FILE, "r")) == NULL){
       fprintf(stderr, "There is no default file!!:  %s\n", DEFAULT_FILE);
       exit(0);
     }
   if (settings==NULL){
     struct stat buf;
     char default_file[MAXNAMLEN], *env_name;
     
     if (another_settings)
       strcpy(default_file, another_settings);
     else{
       env_name=getenv("HOME");
       if (env_name)
	 strcpy(default_file,env_name);
       else
	 getwd(default_file);
       strcat(default_file, "/.xcprc");
     }
     if ((stat(default_file, &buf)) == -1)
       settings=(FILE *)-1;
     else
       settings = fopen(default_file, "rw");
   }
   for (okay = 1; okay>=0 ; okay--){
     if ((settings!=(FILE *)-1) && okay)
       use=settings;
     else
       use=defaults;
     rewind(use);
     while(!feof(use)){
       fgets(input, 239, use);
       length=strlen(input)-1;
       if (length > 0){
	 input[length]='\0';
	 sscanf(input,"%s = ",desc);
	 if (!strcmp(name,desc)) {
	   temp = strchr(input, '=');
	   while ((*temp=='=') || (*temp==' '))
	     temp++;
	   strcpy(result,temp);
	   return;
	 }
       }
     }
   }
 }
       
void remove_item(directory, index_file)
     char *directory;
     file_list_struct *index_file;
{
  head_path_struct *temp_head = file_list_head;
  file_list_struct *temp_file, *temp_file_prev;

  while (temp_head)
    if (!strcmp(temp_head->pathname, directory))
      break;
    else
      temp_head=temp_head->next_path;
  if (temp_head){
    temp_file = temp_file_prev = temp_head->first_file;
    while (temp_file){
      if (temp_file == index_file){
	if (temp_file==temp_file_prev){
	  temp_head->first_file = temp_file->next_file;
	  XmStringFree(temp_file->name_str);
	  XmStringFree(temp_file->fullname_str);
	  free(temp_file);
	  (temp_head->total_files)--;
	  return;
	}
	else{
	  temp_file_prev->next_file=temp_file->next_file;
	  XmStringFree(temp_file->name_str);
	  XmStringFree(temp_file->fullname_str);
	  free (temp_file);
	  (temp_head->total_files)--;
	  return;
	}
      }
      temp_file_prev=temp_file;
      temp_file=temp_file->next_file;
    }
  }
}

void remove_item_dir(path)
char *path;
{
  head_path_struct *temp_head, *temp_head_prev;
  file_list_struct *temp_file, *prev_file;

  temp_head = temp_head_prev = file_list_head;
  while (temp_head)
    if (!strcmp(temp_head->pathname, path))
      break;
    else{
      temp_head_prev = temp_head;
      temp_head = temp_head->next_path;
    }
  if (temp_head){
    temp_file = prev_file = temp_head->first_file;
    while (temp_file){
      temp_file=temp_file->next_file;
      XmStringFree(prev_file->name_str);
      XmStringFree(prev_file->fullname_str);
      free(prev_file->fullname);
      free(prev_file);
      prev_file = temp_file;
      }
    if (temp_head == file_list_head)
      file_list_head = temp_head->next_path;
    else
      temp_head_prev->next_path = temp_head->next_path;
    free(temp_head);
  }
}


void add_file_to_dir(dir, file)
char *dir;
file_list_struct *file;
{
  head_path_struct *list = file_list_head;
  file_list_struct *back=NULL;  /*  grrr.. i hate variable names!!!!   */
  
  while (list)
    if (!strcmp(list->pathname, dir))
      break;  /*  found it!!   */
    else
      list=list->next_path;
  if (!list)   /*  item not found.. needs to be added - no error: void */
    return;
  else{
    back=list->first_file;
    if (back)
      while (back->next_file)
	back=back->next_file;
    (list->total_files)++;
    if (back==NULL)
      list->first_file=file;
    else
      back->next_file=file;
    return;
  }
}


void dtrt(char *path)
{
/*  okay.. this will create one pipe for stdout/err from ALL children..
   therefore.. it never closes it.. even if there are no kiddies left,
   for the same reason, the parent can't close the write end.. oh well  */

  char *argv[3];
  int pid, flags;

  if ((argv[0] = (char *)malloc(sizeof(char) * (1 + strlen(DTRT_NAME)))) == NULL){
    inform("can't allocate memory\n");
    return;
  }
  get_filename(DTRT_NAME, argv[0]);
  if ((argv[1] = (char *)malloc(sizeof(char) * (1 + strlen(path)))) == NULL){
    inform("can't allocate memory\n");
    free(argv[0]);
    return;
  }  
  strcpy(argv[1], path);
  argv[2] = (char *)NULL;
  if (pipefd[0] == -1){
    if (pipe(pipefd) != 0){
      inform(" can't create the pipe.. information from the program\n\twill go to the tty\n");
      pipefd[0] = STDIN_FILENO;
      pipefd[1] = STDOUT_FILENO;
    }
    if ((flags=fcntl(pipefd[0], F_GETFL, 0)) == -1){  /* problem setting non-block */
      close(pipefd[0]);
      close(pipefd[1]);
      pipefd[0] = STDIN_FILENO;
      pipefd[1] = STDOUT_FILENO;
      }
      else 
	if ((fcntl(pipefd[0], F_SETFL, flags | O_NONBLOCK))== -1){
	  close(pipefd[0]);
	  close(pipefd[1]);
	  pipefd[0] = STDIN_FILENO;
	  pipefd[1] = STDOUT_FILENO;
	}
	else
	  XtAppAddInput(app, pipefd[0], (XtPointer)XtInputReadMask, got_stuff, NULL);
  }
  inform("Please be patient, i am trying to do the right thing (tm)\n");
  if ((pid = fork()) < 0){
    perror("fork");
    return;
  }
  if (pid==0){
    if (dup2(pipefd[1], STDOUT_FILENO) != STDOUT_FILENO)
      perror("dup2");
    if (dup2(pipefd[1], STDERR_FILENO) != STDERR_FILENO)
      perror("dup2");
    close(pipefd[0]);
    execv(DTRT_NAME, argv);
    inform("exec: can't find the program I need!!! (dtrt)...  send bug report to your administrator");
    exit(0);
    return;
  }
  else
    return;
}

void got_stuff()
{
  char stuff[PIPE_BUF+1];
  int num_read;

  while ((num_read = read(pipefd[0], stuff, PIPE_BUF)) != -1){ /* done  */
    stuff[num_read] = '\0';
    inform(stuff);
  }
}

