/*
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"
const int verbose = 0;
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);
if (verbose)
{
fprintf(stderr, "OLD: ");
print_hex(stderr, &old_str);
fprintf(stderr, "\n");
}
new_str.length = parse_backslashes(new_str.ptr);
if (verbose)
{
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;
}