13

Depending on command-line arguments, I'm setting a file pointer to point either towards a specified file or stdin (for the purpose of piping). I then pass this pointer around to a number of different functions to read from the file. Here is the function for getting the file pointer:

FILE *getFile(int argc, char *argv[]) {
    FILE *myFile = NULL;
    if (argc == 2) {
        myFile = fopen(argv[1], "r");
        if (myFile == NULL)
           fprintf(stderr, "File \"%s\" not found\n", argv[1]);
    }
    else
        myFile = stdin;
    return myFile;
}

When it's pointing to stdin, fseek does not seem to work. By that, I mean I use it and then use fgetc and I get unexpected results. Is this expected behavior, and if so, how do I move to different locations in the stream?

For example:

int main(int argc, char *argv[]) {
    FILE *myFile = getFile(argc, argv); // assume pointer is set to stdin
    int x = fgetc(myFile); // expected result
    int y = fgetc(myFile); // expected result
    int z = fgetc(myFile); // expected result

    int foo = bar(myFile); // unexpected result

    return 0;
}

int bar(FILE *myFile) {
    fseek(myFile, 4, 0);
    return fgetc(myFile);
}
Tyler Treat
  • 14,640
  • 15
  • 80
  • 115
  • your example code looks fine for me. (except when the file not exist, but this is unrelated to your problem) – J-16 SDiZ Feb 07 '11 at 03:14
  • seems right to me. what compiler is it? you might try to print, inside the bar() function both pointers (stdin and myFile) to check they are the same. – leonbloy Feb 07 '11 at 03:21
  • @leonbloy: I have discovered that the problem is actually with `fseek()`. Apparently it does not work when the pointer is pointing to stdin? Any thoughts on this? (Updated the question) – Tyler Treat Feb 07 '11 at 03:23
  • related: https://stackoverflow.com/questions/2502489/ftell-stdin-causes-illegal-seek-error | https://stackoverflow.com/questions/40063488/how-do-i-seek-in-stdin – Ciro Santilli OurBigBook.com Nov 17 '19 at 20:52

3 Answers3

16

Yes, it's perfectly normal that fseek won't work on stdin -- it'll normally only work on a disk file, or something reasonably similar.

Though it's really a POSIX thing, you can typically use if (isatty(fileno(myFile))) to get at least a pretty good idea of whether seeking will work in a particular file. In some cases, isatty and/or fileno will have a leading underscore (e.g., IIRC the versions provided with Microsoft's compilers do).

Jerry Coffin
  • 476,176
  • 80
  • 629
  • 1,111
  • Then I guess my question is -- and this might be stupid -- how would I go about moving to a different index in the stream? I would think there would be a more elegant solution than calling `fgetc(myFile)` in a loop n times? – Tyler Treat Feb 07 '11 at 03:32
  • 6
    @Tyler Treat: There isn't - this the fundamental difference between streams and files. Conceptually, the input from a stream isn't stored anywhere - a stream is ephemeral, generated as you consume it. If you need to seek around in the data from a stream, you should read it into memory and seek around there (or, if it's particularly large, read it into a temporary file). – caf Feb 07 '11 at 03:40
  • 2
    I do not find support in the C11 spec concerning "it's perfectly normal that fseek won't work on stdin". Do you have C specification to support that when `stdin` is a text stream? – chux - Reinstate Monica Sep 10 '15 at 15:39
  • 2
    Change the incorrect "fseek won't work on stdin" to "fseek won't work on pipes, sockets, or FIFOs". Here is an example where `fseek` works perfectly well on `stdin` because `stdin` is coming from a (seekable) file: `./a.out – Ian D. Allen Nov 06 '21 at 17:01
2

Fseek() is based on lseek(), and the lseek man page discusses possible errors, including:

 [ESPIPE]           Fildes is associated with a pipe, socket, or FIFO.

If stdin is connected to a pseudo tty, I believe it will have socket behavior.

DigitalRoss
  • 143,651
  • 25
  • 248
  • 329
0

Here is the relevant entry in the ANSI standard concerning the fseek function:

For a text stream, either offset shall be zero, or offset shall be a value returned by an earlier successful call to the ftell function on a stream associated with the same file and whence shall be SEEK_SET

So, possible but with some limitations

cegfault
  • 6,442
  • 3
  • 27
  • 49
Jorge
  • 9
  • 1