sreplace.c
author viric@llimona
Thu, 31 May 2007 00:17:52 +0200
changeset 7 fcde17ef6af6
parent 5 18c4f565e6d8
child 8 4ecd557ebebf
child 11 0ef0d9c52f82
permissions -rw-r--r--
Separated backslash C parser library.

/*
    Stream Replace - replaces sequences of bytes in FILE streams
    Copyright (C) 2007  LluĂ­s Batlle i Rossell

    Please find the license in the provided COPYING file.
*/
#include <stdio.h>
#include <assert.h>
#include <string.h>

#include "sreplace.h"

enum {
  BUFFER_SIZE = 2048
};

static FILE * input, * output;

struct CmpState
{
  struct String *str;
  int matched;
};

struct String old_str;
struct String new_str;
struct CmpState cmp_state;

/* Buffer:
   [F] - read
   [F] - read_cmp
   [F]
   [F] - filled
   [ ]
   [ ]

   In the circular sense:
   read <= read_cmp < filled
*/
struct Buffer
{
  unsigned char *base;
  int filled;
  int read;
  int read_cmp;
  int size;
};

static int get_buffer_space(struct Buffer *b, int start, int end)
{
  int space;
  space = end - start + b->size;
  if (space > b->size)
    space -= b->size;

  return space;
}

static int min(int a, int b)
{
  if (a < b)
    return a;
  return b;
}

static int one_before(struct Buffer *b, int pos)
{
  if (pos == 0)
    return b->size - 1;

  return pos - 1;
}

/* This should be called without any exceeding size */
static int file_to_buffer(struct Buffer *b, int size)
{
  int blocksize;
  int res;

  blocksize = min(b->size - b->filled, size);
  if (blocksize > 0)
  {
    res = fread(b->base + b->filled, 1, blocksize, input);
    /*res = read(0, b->base + b->filled, blocksize);*/
    /*fprintf(stderr, "fread1 %i bytes (should %i).\n", res, blocksize);*/
    size -= res;
    b->filled = (b->filled + res) % b->size;

    /* Check EOF */
    if (res == 0)
      return 0;
  }

  if (size > 0 && res == blocksize)
  {
    blocksize = min(b->read - 1, size);
    res = fread(b->base, 1, blocksize, input);
    /*res = read(0, b->base, blocksize);*/
    /*fprintf(stderr, "fread2 %i bytes.\n", res);*/
    size -= res;
    b->filled = (b->filled + res) % b->size;
  }
  return 1;
}

static int file_read_more(struct Buffer *b)
{
  int can_read;
  int res;

  can_read = get_buffer_space(b, b->filled, b->read - 1);

  /*fprintf(stderr, "file_to_buffer %i\n", can_read);*/

  return file_to_buffer(b, can_read);
}

/* pos will be read or read_cmp, depending on
   what to read */
static int can_read_from_buffer(struct Buffer *b, int pos)
{
  /*
   In the circular sense:
   read <= read_cmp < filled
   */
  if (pos != b->filled)
    return 1;
  else
    return 0;
}

static int getc_real(struct Buffer *b)
{
  int a;

  if (!can_read_from_buffer(b, b->read))
    a = EOF;
  else
  {
    a = *(b->base + b->read);
    b->read += 1;
    if (b->read == b->size)
      b->read = 0;
  }
  /*fprintf(stderr, "getc_real %x\n", a);*/

  return a;
}

static int getc_cmp(struct Buffer *b)
{
  int a;
  if (!can_read_from_buffer(b, b->read_cmp))
    a = EOF;
  else
  {
    a = *(b->base + b->read_cmp);
    b->read_cmp += 1;
    if (b->read_cmp == b->size)
      b->read_cmp = 0;
  }
  /*fprintf(stderr, "getc_cmp %x\n", a);*/

  return a;
}

/* This will not be moved farer than read_cmp */
static void read_advance(struct Buffer *b, int adv)
{
  int i;
  for (i=0; i < adv; ++i)
    getc_real(b);
};

static void process_buffer(struct Buffer *b)
{
  int c;

  while (1)
  {
    c = getc_cmp(b);

    if (c == EOF)
      break;

    if (cmp_state.str->ptr[cmp_state.matched] == (char) c)
    {
      cmp_state.matched++;
      if (cmp_state.matched == cmp_state.str->length)
      {
        fwrite(new_str.ptr, 1, new_str.length, output);
        /*write(1, new_str.ptr, new_str.length);*/
        read_advance(b, cmp_state.matched);
        cmp_state.matched = 0;
      }
    } else
    {
      int c;
      c = getc_real(b); /* Will not be EOF. read_cmp is forwarder. */
      fputc(c, output);
      /*write(1, &c, 1);*/
      cmp_state.matched = 0;
      b->read_cmp = b->read;
    }
  }
}

static void end_buffer(struct Buffer *b)
{
  int c;
  while((c = getc_real(b)) != EOF)
      fputc(c, output);
      /*write(1, &c, 1);*/
}

static void loop()
{
  struct Buffer b;

  b.base = (unsigned char *) malloc(BUFFER_SIZE);
  b.read = 0;
  b.read_cmp = 0;
  b.filled = 0;
  b.size = BUFFER_SIZE;

  while(file_read_more(&b) > 0)
  {
    int c;
    process_buffer(&b);
  }

  end_buffer(&b);;
}

static void show_usage(const char *pname)
{
  printf("usage: %s OLD_STR NEW_STR\n", pname);
}

static void process_parameters(int argc, char **argv)
{
  if (argc != 3)
  {
    show_usage(argv[0]);
    exit(1);
  }

  old_str.ptr = argv[1];
  new_str.ptr = argv[2];

  old_str.length = parse_backslashes(old_str.ptr);
  fprintf(stderr, "OLD: ");
  print_hex(stderr, &old_str);
  fprintf(stderr, "\n");

  new_str.length = parse_backslashes(new_str.ptr);
  fprintf(stderr, "NEW: ");
  print_hex(stderr, &new_str);
  fprintf(stderr, "\n");
}

int main(int argc, char ** argv)
{
  process_parameters(argc, argv);
  
  cmp_state.str = &old_str;
  cmp_state.matched = 0;

  input = stdin;
  output = stdout;

  loop();
  return 0;
}