#include <stdio.h>
#include <stdlib.h>
/* for ftruncate */
#include <unistd.h>
#include <sys/types.h>
size_t str_length(char *s)
{
size_t n = 0;
while (*s++ != '\0') {
n++;
}
return n;
}
int str_equal(char *a, char *b)
{
do {
if (*a++ != *b++) {
return 0;
}
} while (*b != '\0');
return 1;
}
long find_in_file(FILE *f, char *needle)
{
size_t nlen = str_length(needle);
long pos = 0;
char *str = malloc(nlen + 1);
while (!feof(f) || !ferror(f)) {
if (fread(str, 1, nlen, f) != nlen) {
break;
}
str[nlen] = '\0';
//printf("%ld: %s\n", ftell(f), str);
if (str_equal(str, needle)) {
return pos;
}
fseek(f, ++pos, SEEK_SET);
}
free(str);
return -1;
}
void move_char(FILE *f, long src, long dst)
{
int c;
fseek(f, src, SEEK_SET);
c = fgetc(f);
fseek(f, dst, SEEK_SET);
fputc(c, f);
}
void file_move_contents(FILE *f, long from, long to, long end)
{
int c;
long p, q;
if (from < to) {
// in case of moving to the right side,
// we start from right end and move backwards to the start
for (p = end, q = end + to - from; p >= from; p--, q--) {
move_char(f, p, q);
}
} else {
// otherwise, we start from the left and move towards
// the right
for (p = from, q = to; p <= end; p++, q++) {
move_char(f, p, q);
}
}
}
int main(int argc, char **argv)
{
FILE *f;
int fd;
long pos;
long olen, nlen, end;
if (argc < 4) {
printf("Usage: %s [file] [needle] [replacement]\n", argv[0]);
printf("Replaces needle in the contents of file with replacement.\n\n");
return 2;
}
if (argv[2][0] == '\0') {
printf("%s: Invalid needle: needle can not be empty.\n", argv[0]);
return 3;
}
f = fopen(argv[1], "r+");
if (f == NULL) {
printf("%s: Error: Can not open file `%s` for reading.\n",
argv[0], argv[1]);
return 1;
}
pos = find_in_file(f, argv[2]);
if (pos == -1) {
printf("The string \"%s\" is not found in file `%s`\n",
argv[2], argv[1]);
} else {
printf("The string \"%s\" is found in file `%s` at position %ld\n", argv[2], argv[1], pos);
fseek(f, 0L, SEEK_END);
end = ftell(f);
olen = str_length(argv[2]);
nlen = str_length(argv[3]);
file_move_contents(f, pos + olen, pos + nlen, end - 1);
// write down the replacement in the right place
fseek(f, pos, SEEK_SET);
fwrite(argv[3], 1, nlen, f);
fseek(f, 0L, SEEK_END);
end = ftell(f);
// truncate the file to the right size
fd = fileno(f);
ftruncate(fd, end + nlen - olen);
}
fclose(f);
return 0;
}