wrap ungetc() without puts() gets() and streams in general

104 Views Asked by At

I'm porting net-snmp to an embedded platform that only has limited access to the filesystem and I stumbled upon a big problem. There's a part of the core code that uses the ungetc() function, which I don't have. There are of course 2 solutions:

A) write my own ungetc() using what I have

B) modify net-snmp code in order to achieve the same result without ungetc()

Solution (B) will be eventually discussed in the net-snmp coders mailing list since requires deep understanding of the library's internals, so let's please focus on feasibility of (A)

What I have on my embedded system is:

fopen()
fclose()
fcreate()
fwrite()
fread()
fdelete()
fclear()
fcopy()
ffindfirst()
ffindnext()
frename()
fgetsize()
ftell()
fseek()
fgetc()
fgets()

The main difference is that my file functions work with INT32* file handles instead of FILE* types. I don't have the FILE* type.

What the ungetc() function does is to basically "put back the char in the stream" , either the char that it just read or another one.

In the first case the solution is easy, I rewind the pointer with fseek() one position backwards.

But in the second case I have a problem. I would be modifying the stream and not the file, except I don't have streams! I'm reading the file directly.

With ungetc() you can do something like

FILE *fp = fopen("file.txt", "r");
int c = getc (fp);
if( c == 'a' ) ungetc ('b', fp);

If "file.txt" contains "abcdefghi", a subsequent read with gets() will read "bbcdefghi" and not "abcdefghi" because the content IN THE STREAM has been changed, but not the file!

How can I replicate this behavior if I don't have "streams" ? My getc() and gets() read from an INT32* file handle and I don't have a puts() or putc() equivalent.

I can only write with fwrite() but that alters the content on the NV memory.

Thank you for your insight

1

There are 1 best solutions below

0
mfloris On

Here is how I solved it. I created a more complex struct for the file handle that contains not only the handle itself but also the file name, the file size and a buffer that holds the whole content of the file. It should only load the part of the file that I need but mine is an embedded application and I know I won't be opening big files so I didn't bother.

Then once you have the "stream" it's trivial to pop chars in and out.

typedef struct _myfile {
      _FS_HANDLE        handle; /* file descriptor */
      CHAR*             fname;  /* file name */
      UINT32            fsize;  /* file size */
      CHAR*             buffer; /* file buffer */
  } *my_FILE;

int my_ungetc(int c, my_FILE stream)
{
    if (stream)
    {
        UINT32 pointer = _fs_tell(stream->handle);
        if (pointer > 0)
        {
            _fs_seek(stream->handle,pointer - 1);
            stream->buffer[pointer - 1] = c;
            return c;
        }
    }
    else
    {
        printf("ERROR! stream is NULL!\r\n");
    }
    return EOF;
}

void *my_fopen(const char *filename, const char *mode)
{
    my_FILE fp = _mem_alloc(sizeof(struct _myfile));
    fp->fname = strdup(filename);
    if (mode == "r")
    {
        fp->handle = _fs_open((CHAR*)filename, OPEN_READ);
        if (fp->handle) fp->fsize = _get_size_with_handle(fp->handle);
        if (fp->fsize)
        {
            fp->buffer = _mem_alloc(fp->fsize);
            if (fp->buffer)
            {
                if (_fs_read(fp->handle,fp->buffer,fp->fsize))
                {
                    _fs_seek(fp->handle,0);
                }
                else
                {
                    printf("ERROR: unable to read %d bytes from %s\r\n",fp->fsize,filename);
                }
            }
            else
            {
                printf("ERROR in my_fopen(\"%s\",\"r\"): could not alloc %d bytes for buffer\r\n",filename,fp->fsize);
            }
        }
        else
        {
            fp->buffer = NULL;
            printf("File \"%s\" is empty\r\n");
        }
        return fp;
    }
    else if (mode == "w")
    {
        fp->handle = _fs_open((CHAR*)filename, OPEN_WRITE);
        if (fp->handle) fp->fsize = _get_size_with_handle(fp->handle);
        fp->buffer = NULL;
        return fp;
    }
    else
    {
        printf("File open mode %s not supported\r\n",mode);
        return NULL;
    }
}