nakedmudv3.3/
nakedmudv3.3/lib/
nakedmudv3.3/lib/logs/
nakedmudv3.3/lib/misc/
nakedmudv3.3/lib/players/
nakedmudv3.3/lib/txt/
nakedmudv3.3/lib/world/
nakedmudv3.3/lib/world/examples/
nakedmudv3.3/lib/world/examples/mproto/
nakedmudv3.3/lib/world/examples/oproto/
nakedmudv3.3/lib/world/examples/reset/
nakedmudv3.3/lib/world/examples/rproto/
nakedmudv3.3/lib/world/examples/trigger/
nakedmudv3.3/lib/world/limbo/
nakedmudv3.3/lib/world/limbo/room/
nakedmudv3.3/lib/world/limbo/rproto/
nakedmudv3.3/src/alias/
nakedmudv3.3/src/char_vars/
nakedmudv3.3/src/editor/
nakedmudv3.3/src/example_module/
nakedmudv3.3/src/help/
nakedmudv3.3/src/set_val/
nakedmudv3.3/src/socials/
nakedmudv3.3/src/time/
//*****************************************************************************
//
// buffer.c
//
// The buffer datastructure is a string-like thing of infinite length. Contains
// some basic utilities for interacting with it.
//
//*****************************************************************************

#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "mud.h"
#include "utils.h"
#include "buffer.h"


struct buffer_data {
  char *data;   // what we're holding
  int  maxlen;  // what's the maximum storage capacity of data
  int  len;     // what's the current content length of data
};

BUFFER    *newBuffer   (int start_capacity) {
  BUFFER *buf = malloc(sizeof(BUFFER));
  if(start_capacity <= 0) start_capacity = 1;
  buf->data   = malloc(sizeof(char) * start_capacity);
  *buf->data  = '\0';
  buf->maxlen = start_capacity;
  buf->len    = 0;
  return buf;
}

void        deleteBuffer(BUFFER *buf) {
  if(buf->data) free(buf->data);
  free(buf);
}

void        bufferExpand(BUFFER *buf, int newsize) {
  char *newdata = malloc(sizeof(char) * newsize);
  strcpy(newdata, buf->data);
  free(buf->data);
  buf->maxlen = newsize;
  buf->data = newdata;
}

void        bufferCat   (BUFFER *buf, const char *txt) {
  int txtlen = strlen(txt);
  // see if we need to expand the size of the buffer
  if(txtlen + buf->len >= buf->maxlen)
    bufferExpand(buf, ((txtlen + buf->len) * 5) / 4 + 20); 
  // copy the new text over
  strcat(buf->data+buf->len, txt);
  buf->len += txtlen;
}

void        bufferClear (BUFFER *buf) {
  *buf->data = '\0';
  buf->len = 0;
}

BUFFER    *bufferCopy  (BUFFER *buf) {
  BUFFER *newbuf = newBuffer(1);
  bufferCopyTo(buf, newbuf);
  return newbuf;
}

void        bufferCopyTo(BUFFER *from, BUFFER *to) {
  bufferClear(to);
  bufferCat(to, bufferString(from));
}

const char *bufferString(BUFFER *buf) {
  return buf->data;
}

int         bufferLength(BUFFER *buf) {
  return buf->len;
}

int bprintf(BUFFER *buf, char *fmt, ...) {  
  static int printsize = 8192;
  char buftmp[printsize];
  va_list va;
  int res;

  va_start(va, fmt);
  res = vsnprintf(buftmp, printsize, fmt, va);
  va_end(va);
    
  if (res >= printsize - 1)
    *buftmp = '\0';
  else
    bufferCat(buf, buftmp);
   
  return res;
}

int bufferReplace(BUFFER *buf, const char *a, const char *b, int all) {
  // first, check if we'll need to expand the size of the buffer
  int a_len = strlen(a), b_len = strlen(b);
  int len_needed = buf->maxlen;
  int replaced = 0;

  if(b_len - a_len > 0) {
    int to_replace = (all ? 
		      count_occurences(buf->data, a) : 
		      strstr(buf->data, a) != NULL);

    // if we don't have any to replace, do nothing
    if(to_replace == 0)
      return 0;

    // if we won't have enough room, do the expansion
    if(to_replace * (b_len - a_len) + buf->len > buf->maxlen)
      len_needed = ((buf->maxlen + to_replace*(b_len-a_len))*5)/4 + 20;
  }

  int buf_i, tmp_i;
  char buftmp[len_needed];

  // go through and replace all the occurences we need to
  for(buf_i = tmp_i = 0; buf->data[buf_i] != '\0';) {
    // we found a match
    if(!strncmp(buf->data+buf_i, a, a_len)) {
      replaced++;
      strcpy(buftmp+tmp_i, b);
      buf_i += a_len;
      tmp_i += b_len;
      // exit if we only need to replace the first occurence
      if(all == 0) break;
    }
    else {
      buftmp[tmp_i] = buf->data[buf_i];
      tmp_i++; buf_i++;
    }
  }

  // continue filling up the tmpbuf (if we only replaced 1, and not all)
  for(; buf->data[buf_i] != '\0'; buf_i++, tmp_i++)
    buftmp[tmp_i] = buf->data[buf_i];

  // make sure the end of string marker is in place
  buftmp[tmp_i] = '\0';

  // copy over all the changes
  if(buf->maxlen >= len_needed) {
    strcpy(buf->data, buftmp);
    buf->len = tmp_i;
  }
  else {
    free(buf->data);
    buf->data = malloc(len_needed);
    strcpy(buf->data, buftmp);
    buf->maxlen = len_needed;
    buf->len    = tmp_i;
  }

  return replaced;
}


int bufferInsert(BUFFER *buf, const char *newline, int line) {
  // first, check if we'll need to expand the size of the buffer
  int line_len = strlen(newline);
  if(line_len + buf->len + 2 >= buf->maxlen) // +2 for \r\n
    bufferExpand(buf, ((line_len + buf->len + 2) * 5)/4 + 20);

  // insert it in
  char *start = line_start(buf->data, line);
  if(start != NULL) {
    char tmpbuf[buf->maxlen];
    sprintf(tmpbuf, "%s", start);
    sprintf(start, "%s\r\n", newline);
    strcpy(start+line_len+2, tmpbuf);
    buf->len = buf->len + line_len + 2;
    return TRUE;
  }

  return FALSE;
}

int bufferRemove(BUFFER *buf, int line) {
  char *start = line_start(buf->data, line);
  if(start == NULL) 
    return FALSE;

  int i;
  // copy over the line with everything from the end of the line onwards
  for(i = 0; start[i] != '\0'; i++) {
    if(start[i] == '\n') {
      i++;
      break;
    }
  }
  strcpy(start, start+i);
  buf->len = strlen(buf->data);
  return TRUE;
}

int bufferReplaceLine(BUFFER *buf, const char *newline, int line) {
  if(bufferRemove(buf, line))
    return bufferInsert(buf, newline, line);
  else
    return 0;
}

void bufferFormat(BUFFER *buf, int max_width, int indent) {
  char formatted[(buf->len * 3)/2];
  bool needs_capital = TRUE, needs_indent = FALSE;
  int fmt_i = 0, buf_i = 0, col = 0, next_space = 0;

  // put in our indent
  if(indent > 0) {
    char fmt[20];
    sprintf(fmt, "%%%ds", indent);
    sprintf(formatted, fmt, " ");
    fmt_i += indent;
    col   += indent;

    // skip the leading spaces
    while(isspace(buf->data[buf_i]) && buf->data[buf_i] != '\0')
      buf_i++;
  }

  for(; buf->data[buf_i] != '\0'; buf_i++) {
    // we have to put a newline in because the word won't fit on the line
    next_space = next_space_in(buf->data + buf_i);
    if(next_space == -1)
      next_space = buf->len - buf_i;
    if(col + next_space > max_width-1) {
      formatted[fmt_i] = '\r'; fmt_i++;
      formatted[fmt_i] = '\n'; fmt_i++;
      col = 0;
    }

    char ch = buf->data[buf_i];

    // no spaces on newlines or ends of lines
    if(isspace(ch) && (col == 0 || col == max_width-1))
      continue;
    // we will do our own sentance formatting
    else if(needs_capital && isspace(ch))
      continue;
    // delete multiple spaces
    else if(isspace(ch) && fmt_i > 0 && isspace(formatted[fmt_i-1]))
      continue;
    // treat newlines as spaces (to separate words on different lines), 
    // since we are creating our own newlines
    else if(ch == '\r' || ch == '\n') {
      // we've already spaced
      if(col == 0)
	continue;
      formatted[fmt_i] = ' ';
      col++;
    }
    // if someone is putting more than 1 sentence delimiter, we
    // need to catch it so we will still capitalize the next word
    else if(strchr("?!.", ch) && isspace(buf->data[buf_i + 1])) {
      needs_capital = TRUE;
      needs_indent  = TRUE;
      formatted[fmt_i] = ch;
      col++;
    }
    // see if we are the first letter after the end of a sentence
    else if(needs_capital) {
      // check if indenting will make it so we don't
      // have enough room to print the word. If that's the
      // case, then skip down to a new line instead
      next_space = next_space_in(buf->data + buf_i);
      if(next_space == -1)
	next_space = buf->len - buf_i;
      if(col + 2 + next_space > max_width-1) {
	formatted[fmt_i] = '\r'; fmt_i++;
	formatted[fmt_i] = '\n'; fmt_i++;
	col = 0;
      }
      // indent two spaces if we're not at the start of a line 
      else if(needs_indent && col != 0) {
	formatted[fmt_i] = ' '; fmt_i++;
	formatted[fmt_i] = ' '; fmt_i++;
	col += 2;
      }
      // capitalize the first letter on the new word
      formatted[fmt_i] = toupper(ch);
      needs_capital = FALSE;
      needs_indent  = FALSE;
      col++;
    }
    else {
      formatted[fmt_i] = ch;
     
      // if we're adding a { or our last character was an {, don't increase
      // our columns, because these are colour codes
      if(ch != '{' && (fmt_i == 0 || formatted[fmt_i-1] != '{'))
	col++;
    }

    fmt_i++;
  }

  // tag a newline onto the end of the string if there isn't one already
  if(fmt_i > 0 && formatted[fmt_i-1] != '\n') {
    formatted[fmt_i++] = '\r';
    formatted[fmt_i++] = '\n';
  }

  formatted[fmt_i] = '\0';

  // if all we have are spaces and newlines, erase it all
  if(fmt_i == 2 + indent) {
    formatted[0] = '\0';
    fmt_i = 0;
  }

  // make sure we have enough room to copy everything over
  if(fmt_i >= buf->maxlen)
    bufferExpand(buf, (fmt_i * 5)/4 + 20);
  
  // copy over our changes
  strcpy(buf->data, formatted);
  buf->len = fmt_i;
}