0

I want(ed) to implement POSIX tail in C language. If the input is file (filename received from argv), then I used fseek to get at the end of file, so implementation was somehow easy.

But when the input is stdin, I can't use fseek. I somehow figured out that i can do:

FILE *f = stdin;

Then I can use stdin as file and fseek, everything works as intended (with some little work :D).

My question is only, is this okay ? I mean (for me) it's kinda unusual to use stdin like that. Wouldn't be there any 'security' or another errors with this ? I tested my tail a lot and it looks like it works good even with edge cases.

HorseCZ
  • 11
  • 1
  • 1
    It's fine, this is how programs that process either named files or stdin normally work. – Barmar Apr 14 '20 at 22:39
  • Is `fseek` relevant with `stdin`? See [Using fseek with a file pointer that points to stdin](https://stackoverflow.com/questions/4917801/using-fseek-with-a-file-pointer-that-points-to-stdin) – Weather Vane Apr 14 '20 at 22:43
  • @WeatherVane If `stdin` is redirected to a file, why wouldn't it be relevant? – Barmar Apr 14 '20 at 22:45
  • That question is talking about the default connection of stdin to the terminal, but if you run `tail < filename` you can use `fseek()`. – Barmar Apr 14 '20 at 22:46
  • @Barmar yes, `fseek` does work with a file redirected to `stdin`. But when you type something, my code waits for more input (using `fseek` to the beginning). – Weather Vane Apr 14 '20 at 22:50
  • @WeatherVane It doesn't make sense to use a program like `tail` with interactive input. You need to check for an error from `fseek()` to know if you can use it. – Barmar Apr 14 '20 at 22:54
  • 1
    @Barmar indeed `0` was not returned. *On devices incapable of seeking, the return value is undefined.* – Weather Vane Apr 14 '20 at 22:57
  • @Barmar *if you run `tail < filename` you can use `fseek()`* Not if `filename` is a named pipe. And probably a few others types, too. – Andrew Henle Apr 15 '20 at 00:22
  • @AndrewHenle Of course. It's for plain files. The whole point is that `tail filename` and `tail < filename` should work the same -- it doesn't matter whether you open the file by name or get it by `stdin`. – Barmar Apr 15 '20 at 02:01

1 Answers1

3

It is indeed a weird thing to do because it doesn't help. Whether fseek works or not isn't affected by the name of the variable used as an argument.

It can succeed if the handle is for a plain file.
It can't succeed if the handle isn't for a plain file.

This is true for fseek(stdin, ...). (Code below.)

$ ./fseek_stdin <file
fghij

$ cat file | ./fseek_stdin
fseek: Illegal seek

This is true for fseek(f, ...). (Code below.)

$ ./fseek_f <file
fghij

$ cat file | ./fseek_f
fseek: Illegal seek

But there is also no harm in assigning stdin to another variable. For example, you might do

FILE *f;
if (...) {
   f = stdin;
} else {
   f = fopen(...);
}

Or you might do

void some_func(FILE *f) {
   ...
}

some_func(stdin);

These are both perfectly legitimate assignments of stdin to another variable.


Here are the files used in the earlier tests:

file:

abcdefghij

fseek_stdin.c:

#include <stdio.h>
#include <stdlib.h>

int main(void) {
   if (fseek(stdin, 5, SEEK_CUR) < 0) {
      perror("fseek");
      return EXIT_FAILURE;
   }

   char *line = NULL;
   size_t n = 0;
   if (getline(&line, &n, stdin) < 0) {
      perror("getline");
      free(line);
      return EXIT_FAILURE;
   }

   printf("%s", line);
   free(line);
   return EXIT_SUCCESS;
}

fseek_f.c:

#include <stdio.h>
#include <stdlib.h>

int main(void) {
   FILE *f = stdin;

   if (fseek(f, 5, SEEK_CUR) < 0) {
      perror("fseek");
      return EXIT_FAILURE;
   }

   char *line = NULL;
   size_t n = 0;
   if (getline(&line, &n, f) < 0) {
      perror("getline");
      free(line);
      return EXIT_FAILURE;
   }

   printf("%s", line);
   free(line);
   return EXIT_SUCCESS;
}

A diff of the two programs (slightly massaged for readability):

$ diff -y fseek_{stdin,f}.c
#include <stdio.h>                               #include <stdio.h>
#include <stdlib.h>                              #include <stdlib.h>

int main(void) {                                 int main(void) {
                                               >    FILE *f = stdin;
                                               >
   if (fseek(stdin, 5, SEEK_CUR) < 0) {        |    if (fseek(f, 5, SEEK_CUR) < 0) {
      perror("fseek");                                 perror("fseek");
      return EXIT_FAILURE;                             return EXIT_FAILURE;
   }                                                }

   char *line = NULL;                               char *line = NULL;
   size_t n = 0;                                    size_t n = 0;
   if (getline(&line, &n, stdin) < 0) {        |    if (getline(&line, &n, f) < 0) {
      perror("getline");                               perror("getline");
      free(line);                                      free(line);
      return EXIT_FAILURE;                             return EXIT_FAILURE;
   }                                                }

   printf("%s", line);                              printf("%s", line);
   free(line);                                      free(line);
   return EXIT_SUCCESS;                             return EXIT_SUCCESS;
}                                                }
ikegami
  • 367,544
  • 15
  • 269
  • 518