Richard Bos wrote:
"anachronic_individual" <an******************@gmail.comwrote:
Is there a standard library function to insert an array of characters
at a particular point in a text stream without overwriting the existing
content, such that the following data in appropriately moved further
down?
<http://c-faq.com/osdep/insdelrec.html>.
Thanks for the pointer. Next time I check the FAQ before I post.
Nevertheless I went ahead and wrote a function to insert arbitrary text
into a text stream. It compiles with gcc -W -Wall -std=c99 -pedantic
with one warning about comparision between a signed and unsigned
object. It also appears to work as intended.
I'll be greatful if the group can review the code for possible errors
or non-portable assumptions or anything else. I admit the code is not
elegant, but this is actually my third try, and I was getting a little
desperate. The code follows:
#include <stdio.h>
#include <stdlib.h>
#include <stddef.h>
#include <string.h>
#define BUFFER_SIZE 512
int s_insert(FILE *s, char *is, size_t isl, char *buf, size_t bufl);
int main(void) {
FILE *fp;
char *buffer, *ins;
char insstr[] = { 'I', 'n', 's', 'e', 'r', 't', '!' };
char *base_text = "This is the\nbase text in\nthe file.\n";
char *testfile = "s_insert_t.txt";
char *openmode = "w+";
size_t isl, bufl;
int rv, ct;
/* initialise variables */
ins = insstr;
isl = sizeof(insstr);
bufl = BUFFER_SIZE;
printf("isl = %u\tbufl = %u\n", isl, bufl);
/* allocate buffer */
buffer = malloc(bufl);
if(buffer == NULL) { puts("malloc() failed."); return EXIT_FAILURE; }
/* open test file and write the base text to it */
fp = fopen(testfile, openmode);
if(fp == NULL) { puts("fopen() failed."); return EXIT_FAILURE; }
else {
if(fputs(base_text, fp) == EOF) {
puts("fputs() failed.");
exit(EXIT_FAILURE);
}
else {
if(fflush(fp) == EOF) {
puts("fflush() failed.");
exit(EXIT_FAILURE);
}
if(fseek(fp, 0, SEEK_SET) != 0) {
puts("fseek() failed.");
exit(EXIT_FAILURE);
}
}
}
/* display the file contents & string to be inserted */
puts("File before insert:");
while((rv = fgetc(fp)) != EOF)
putc(rv, stdout);
putc('\n', stdout);
if(fseek(fp, 0, SEEK_SET) != 0) {
puts("fseek() failed.");
exit(EXIT_FAILURE);
}
puts("\nText to be inserted:");
for(rv = 0; rv < isl; rv++)
putc(ins[rv], stdout);
putc('\n', stdout);
/* insert the text and display the new file contents */
for(ct = 0; ct < 10; ct++) {
rv = s_insert(fp, ins, isl, buffer, bufl);
if(rv == 0) {
puts("s_insert() returned zero.");
exit(EXIT_FAILURE);
}
else {
if(fflush(fp) == EOF) {
puts("fflush failed.");
exit(EXIT_FAILURE);
}
if(fseek(fp, 0, SEEK_SET) != 0) {
puts("fseek() failed.");
exit(EXIT_FAILURE);
}
puts("File after insert:");
while((rv = fgetc(fp)) != EOF)
putc(rv, stdout);
putc('\n', stdout);
if(fseek(fp, 0, SEEK_SET) != 0) {
puts("fseek() failed.");
exit(EXIT_FAILURE);
}
}
}
if(fclose(fp) == EOF) {
puts("fclose() failed.");
exit(EXIT_FAILURE);
}
return EXIT_SUCCESS;
}
int s_insert(FILE *s, char *is, size_t isl, char *buf, size_t bufl) {
char *sbuf;
int cc, ss, bufi, sbufi;
size_t bufc, sbufc, halfb, cnt;
fpos_t pos0, pos1, *crpos, *cwpos;
/* initialise variables */
ss = 1;
sbufi = 1;
sbufc = isl;
halfb = bufl / 2;
sbuf = buf + halfb;
bufi = 0;
bufc = 0;
crpos = &pos0;
cwpos = &pos1;
/* get current file position */
if(fgetpos(s, crpos) != 0 || fgetpos(s, cwpos) != 0)
return 0;
/* copy argument to be inserted into secondary buffer */
memcpy(sbuf, is, sbufc);
/* main insert loop */
for( ;; ) {
/* if primary buffer is empty and secondary buffer is
* not, copy secondary buffer to primary buffer...
*/
if(bufi == 0 && sbufi == 1) {
memcpy(buf, sbuf, sbufc);
bufi = 1;
bufc = sbufc;
sbufi = 0;
sbufc = 0;
}
else
break;
/* try to fill secondary buffer, if empty, from the
* current reading position of stream, and update the
* former...
*/
if(sbufi == 0 && ss == 1) {
if(fsetpos(s, crpos) != 0)
return 0;
for(sbufc = 0; sbufc < halfb; sbufc++) {
cc = getc(s);
if(cc == EOF) {
if(ferror(s))
return 0;
else {
ss = 0;
if(sbufc == 0)
sbufi = 0;
else
sbufi = 1;
break;
}
}
else {
sbuf[sbufc] = cc;
sbufi = 1;
}
}
/* update stream reading pos. if not at EOF */
if(ss != 0) {
if(fgetpos(s, crpos) != 0)
return 0;
}
}
/* if primary buffer is not empty, write it to stream
* at latter's current writing offset and update it...
*/
if(bufi == 1) {
if(fsetpos(s, cwpos) != 0)
return 0;
for(cnt = 0; cnt < bufc; cnt++) {
cc = putc(buf[cnt], s);
if(cc == EOF)
return 0;
}
if(fgetpos(s, cwpos) != 0)
return 0;
else {
bufi = 0;
bufc = 0;
}
}
}
return 1;
}