/
dirt31/
dirt31/bin/
#include "kernel.h"
#include "writer.h"
#include "bprintf.h"

/*
 * MUD writer
 *
 * Note that the mud writer is reentrant, you may start one writer
 * while another is active, the writer will save the previous writer's state
 * prompt et.c the writer tries to save all info about the previous
 * input handler, cprompt is also saved.
 * This means that for example while doing 'mail xyzzy' in mud
 * you may do '*mail zyxxy' inside there, to mail another person.
 * by typing '**' you will then terminate the last mail and you will
 * get back to the previous mail and yet another ** will terminate your
 * mail to xyzzy and get you back to whereever you were when you did
 * mail xyzzy.
 *
 *
 * Also note that it is not an editor, you may write text and the text
 * can replace old text or may be appended to old text, but you don't have
 * the old text available while inside the writer.
 *
 *
 * The MUD writer is used like this:
 *
 * example a simple mail writer for mud.
 *
 * mailcom(void)
 * {
 *    if (brkword() != -1) { /* send mail
 *        start_writer( "Write your mail, terminate with '**'",
 *                      "MAIL>",
 *                      wordbuf,  /* The name who we want to send mail to
 *                      strlen(wordbuf) + 1, /* length of argument
 *                      mail_handler,
 *                      WR_CMD|'*',          /* allow commands
 *                      500);                /* Max 500 lines
 *
 *        return;
 *    } else { /* read mail
 *      read_mail();
 *   }
 * }
 *
 * mail_handler(void *w,void *ad, int adlen)
 * {
 *    FILE *f;
 *    char b[100];
 *    char a[100];
 *
 *    strcpy(b,"MAIL/");
 *    strcpy(a,ad); /* save the address, wgets will destroy ad
 *    strcat(b,a);
 *    if ((f = fopen(b,"a")) == NULL) {
 *       progerror(b);
 *       terminate_writer(w);
 *       return;
 *    } else {
 *       while (wgets(b,sizeof(b),w) != NULL) {
 *           fputs(b,f);
 *       }
 *       /* notify the person he has mail
 *       notify_mail(a); /* can't use ad here as it is destroyed by wgets
 *    }
 * }
 *
 */

typedef struct _wr_line {
  struct _wr_line *next;
  char            *s;
} WrLine;

typedef struct {
  void              *previous;
  char              *prevprompt;
  void              (*exit_handler)(void *w,void *arg,int arglen);
  void              (*old_inp_h)(char *str);
  char              *prompt;
  void              *arg;
  int		    arglen;
  int               flags;
  int               max_lines;
  int               num_lines;
  WrLine            *first, *last;
} WrHead;

static void write_handler(char *line);

void start_writer(char *h,char *p,void *arg,int arglen,
		  void handler(void *x,void *arg,int arglen),
		  int flags,int max_lines)
{
  WrHead *w;

  if (handler == NULL || h == NULL || p == NULL || max_lines <= 0) return;
  w = NEW(WrHead,1);
  w->previous      = cur_player->writer;
  w->prevprompt    = COPY(cur_player->cprompt);
  w->exit_handler  = handler;
  w->old_inp_h     = cur_player->inp_handler->inp_handler;
  w->prompt        = COPY(p);
  w->arg           = BCOPY(arg,arglen);
  w->arglen        = arglen;
  w->flags         = flags;
  w->max_lines     = max_lines;
  w->num_lines     = 0;
  w->first         = NULL;
  w->last          = NULL;
  cur_player->writer = w;
  strcpy(cur_player->cprompt,p);
  bprintf( "%s\n\n", h );
  write_handler(NULL);
}
  
static void write_handler(char *line)
{
  WrHead *w = (WrHead *)cur_player->writer;
  WrLine *l;
  int len;
  int k;

  if ((k = (w->flags & WR_CMDCH)) == 0) k = '*';
  if (line == NULL) {
    replace_input_handler(write_handler);
  } else if (*line == k) {
    if (line[1] == k && line[2] == '\0') {
      /* Exit writer. */
      replace_input_handler(w->old_inp_h); /* Back to old handler */
      free(w->prompt);         /* Don't need the prompt any more */
      if (w->first == NULL) {  /* Let it point to text instead */
	w->prompt = NULL;
      } else {
	w->prompt = w->first->s;
      }
      cur_player->writer = w->previous;
      strcpy(cur_player->cprompt,w->prevprompt);
      free(w->prevprompt);

/* Don't need max lines anymore, use it for unget buffer */

      w->max_lines = EOF;
      w->exit_handler(w,w->arg,w->arglen);
      bprintf( "\r%s", cur_player->cprompt);
      return; /* Don't increment num_lines. */
    } else if ((w->flags & WR_CMD) != 0) {
      gamecom(line+1,True);
    } else {
      bprintf( "You cannot execute any commands now.\n");
    }
  } else if (w->num_lines == w->max_lines) {
    bprintf( "You cannot type in more lines.\n" );
  } else {
    l = NEW(WrLine,1);
    l->s = COPY(line);
    l->next = NULL;
    if (w->first == NULL) { /* First line. */
      w->first = w->last = l;
    } else {
      w->last->next = l;
      w->last = l;
    }
    ++w->num_lines;
  }
  bprintf( "\r%s", cur_player->cprompt);
}

int wnum_lines(void *x)
{
   WrHead *w = x;

   return w == 0 ? 0 : w -> num_lines;
}

int wnum_chars(void *x)
{
   WrHead *w = x;
   WrLine *n;
   int k = 0;

   if (w == 0) return 0;
   for (n = w -> first; n != 0; n = n -> next) k += strlen(n -> s);
   return k;
}

int wgetc(void *x)
{
  WrHead *w = x;
  WrLine *l;
  WrLine *n;
  char *s;
  int k;

  if (w == NULL) return EOF;
  if ((k = w->max_lines) != EOF) {
    w->max_lines = EOF;
    return k; /* return unget character */
  }
  if (w->prompt == NULL) {
    free(w->arg);
    free(w);
    return EOF;
  }
  if ((k = *w->prompt++) == '\0') {
    /* End of this line, kill it and return '\n' */
    l = w->first;
    if ((n = w->first = l->next) == NULL) {
      w->prompt = NULL;
    } else {
      w->prompt = n->s;
    }
    w->num_lines--;
    free(l->s);
    free(l);
    return '\n';
  } else {
    return k;
  }
}

int wungetc(int c, void *x)
{
  WrHead *w;
  WrLine *l;
  char *s;

  if ((w = x) == NULL) return EOF;
  if (c == EOF) {
    while ((l = w->first) != NULL) {
      w->first = l->next;
      free(l->s);
      free(l);
    }
    w->prompt = NULL;
    w->num_lines = 0;
    return EOF;
  }
  if ((l = w->first) != NULL && w->prompt > l->s) {
    *--(w->prompt) = c;
    return c;
  }
  /* Try to put it into max_lines */
  if (w->max_lines != EOF) return EOF; /* Unable to push character back */
  w->max_lines = c;
  return c;
}

char *wgets(char *buf,int buflen,void *w)
{
  int i, k;
  char *s = buf;

  for (i = 1; i < buflen; i++) {
    if ((k = wgetc(w)) == EOF) {
      *s = 0;
      return NULL;
    }
    if ((*s++ = k) == '\n') break;
  }
  *s = 0;
  return buf;
}

void terminate_writer(void *w)
{

  if (w != NULL) {
    (void)wungetc(EOF,w);
    (void)wgetc(w);
  }
}

void terminate_all_writers(int plx)
{
  WrHead *w;
  WrHead *x;

  if (plx >= 0 && plx < max_players) {
    w = players[plx].writer;
    while ((x = w) != NULL) {
      w = w->previous;
      terminate_writer(x);
    }
  }
}