1

This is for a project for everyone's awareness. It's my first project in C and have a question regarding lseek() and moving the file pointer.

Right now I'm able to read the bitmap and DIB header of a bitmap file. I need to now traverse the pixels and manipulate them in certain ways. I have written out in pseudocode how I plan to tackle the manipulation. But I am having difficulty understanding how to properly use lseek, as I keep getting incompatible pointer to integer conversion... warnings with my code. I'll give a short example of my main method since this is for a project:

int main(int argc, const char * argv[]) {

    FILE *fp;

    if((fp = fopen(argv[1], "r+b")) == NULL){
        printf("Error:  Unable to open file.\n");
        exit(0);
    }

    fseek(fp, 0, SEEK_END);
    long fsize = ftell(fp);
    rewind(fp);

    char test;  // simply to test overwriting the current byte

    fread(&test, sizeof(char), 1, fp);
    test = ~test;
    lseek(fp, -1, SEEK_CUR); //produces error
    //also tried fseek prior to realizing I should be using lseek to move my pointer.
    fwrite(&test, 1, sizeof(char), fp);

    fclose(fp);
    return 0;
}

Again, not trying to provide too much of my code since this is a project. But I'd like help understanding how to properly use lseek please. I noticed that it returns an int value, so I know that it is because my file pointer is of type FILE. But what is the proper way to use this method? I saw one example that had opened the same file in both read and write mode, where write mode was using lseek. But for some reason I don't know if that is correct or not.

EDITED Based on two members response, I changed my code above to:

rewind(fp);
lseek(fileno(fp), hdr.offset, SEEK_CUR); //hdr.offset == 54 bytes
printf("FD FILENO:  %d\n", fileno(fp)); // prints 3???
printf("CURRENT POS: %p\n", fp);  //prints 0x7fffe7eae0b0 (I understand it's an address)
fread(&test, sizeof(char), 1, fp);
lseek(fileno(fp), -1, SEEK_CUR);
fwrite(&test, 1, sizeof(char), fp);
printf("CURRENT POS: %p\n", fp);  //prints the same address as above?

What am I not getting other than everything to do with C?

Pwrcdr87
  • 935
  • 3
  • 16
  • 36
  • Line 6: `fileno(fp)` instead `file(fp)` – YaatSuka Oct 03 '17 at 02:58
  • Why do you want to use `lseek()` instead of `fseek()`? –  Oct 03 '17 at 03:00
  • @duskwuff yes because I need to be able to move the pointer back and forth between bytes as I read and write them. – Pwrcdr87 Oct 03 '17 at 03:02
  • @YaatSuka fixed it both in my sample above and in my test. Same results. Addresses don't update and offset is '3' for some reason. – Pwrcdr87 Oct 03 '17 at 03:06
  • ok... so I figured out that part of my problem was I was not using `long long` type var to return `lseek` to. For example, `long long val = lseek(fileno(fp), offset, SEEK_CUR);`. When I print out `val` I get the correct offset. So now I need to understand how to read and write the value from `val` once I have it. – Pwrcdr87 Oct 03 '17 at 03:16
  • `lseek()` is not a standard C library function. – chux - Reinstate Monica Oct 03 '17 at 03:25
  • 1
    Tip: "I am having difficulty understanding how to properly use lseek" --> then why does code not check the return values of `fseek, rewind, fread,lseek, ...` to insure code is behaving as it should? Code nicely does so `fopen`. Much insight and code confidence can be gained by testing return values. – chux - Reinstate Monica Oct 03 '17 at 03:30
  • @chux I actually have been printing `size_t` in my test runs here which produce the expected result. I then open the file in a hex editor and the byte value has not been changing. Also, the address value of the file pointer has not updated as you'd expect when using `fread` and `fwrite`, which should be automatic. I should be worried about backtracking my file pointer after one of those calls are made to make the `fwrite` adjustments that I want to. Sorry for not entering in all my `printf` statements, but I thought my comments were sufficient. – Pwrcdr87 Oct 03 '17 at 03:52
  • 1
    do not mix `lseek` with `stdio` routines. The `FILE *` has internal buffering and wouldn't know that you did interveing `lseek`s. Use `fseek`. – Antti Haapala -- Слава Україні Oct 03 '17 at 04:33

2 Answers2

4

If you'd like to keep using FILE *fp, which is probably a good idea, you can get convert fp to its corresponding int file descriptor with int fileno(FILE *stream). In other words,

lseek(fileno(fp), -1, SEEK_CUR); 

Added

All works fine with fseek.

#include <stdio.h>

int main(int argc, const char *argv[]) {
  if (argc < 2) {
    fprintf(stderr, "Usage: %s FILENAME\n", argv[0]);
    return 1;
  }
  FILE *fp;
  if((fp = fopen(argv[1], "r+b")) == NULL){
    fprintf(stderr, "Error:  Unable to open fle '%s'.\n", argv[1]);
    return 1;
  }
  char buf[17];
  size_t n_read = fread(buf, sizeof(char), sizeof buf - 1, fp);
  buf[n_read] = '\0';
  printf("Initial contents: %s\n", buf);

  fseek(fp, 0, SEEK_END);
  long fsize = ftell(fp);
  printf("Position at end of file: %ld\n", fsize);

  // Read first character in file.
  rewind(fp);
  char test;
  fread(&test, sizeof(char), 1, fp);

  ++test;
  fseek(fp, -1, SEEK_CUR);
  fwrite(&test, sizeof(char), 1, fp);

  rewind(fp);
  n_read = fread(buf, sizeof(char), sizeof buf - 1, fp);
  buf[n_read] = '\0';
  printf("Final contents: %s\n", buf);

  fclose(fp);
  return 0;
}

And then

$ gcc foo.c -o foo
$ ./foo foo.c
Initial contents: #include <stdio.
Position at end of file: 881
Final contents: $include <stdio.
Gene
  • 46,253
  • 4
  • 58
  • 96
  • I've combined both you and @YaatSuka's advice and still am not getting the output I thought I'd get. Please see my edit and thanks for your advice! – Pwrcdr87 Oct 03 '17 at 02:54
  • @Pwrcdr87 You're getting expected results. File handles are small integers that on most C impls start at `3` because 0,1,2 are used for `stdin`, `stdout`, and `stderr`. If you opened a different file it would probably get `4`, etc. `fp` is a pointer to a `FILE` data structure in memory. It's not going to change. What are you expecting? Maybe you intend `ftell()` to see the current stream position. That's what's adjusted by calling any of the `seek` variants. But the C standard doesn't guarantee the `ftell` result is a byte offset; only that `fseek` on it will put file in same state. – Gene Oct 03 '17 at 17:34
  • Incidentally, what do you have against `fseek()` ? – Gene Oct 03 '17 at 17:39
  • I have nothing against `fseek()` personally, but I believe my problem was that I was not fully understanding its use as well as how to properly retrieve its current value. I remember my professor mentioning `lseek()` but didn't recall him saying whether or not it was usable. – Pwrcdr87 Oct 05 '17 at 20:30
1

Here fp is type of FILE *, but lseek take an int as first parameter.
So use open instead fopen:

int main(int argc, const char * argv[]) {

int fp;

// Open return the file descriptor
if((fp = open(argv[1], O_RDWR | O_WRONLY)) == -1){
    printf("Error:  Unable to open file.\n");
    exit(0);
}

// Rest of the function

lseek(fp, -1, SEEK_CUR);    

[EDIT]

Another issue is to use fileno() function.
fileno() take a FILE * as parameter and return the file_descriptor (int) that you need for lseek() function.
You can use it like this:

int main(int argc, const char * argv[]) {

FILE *fp;
int  fd;

// Open return the file descriptor
if((fp = fopen(argv[1], "r+b")) == NULL){
    printf("Error:  Unable to open file.\n");
    exit(0);
}
if ((fd = fileno(fp)) == -1){
    printf("Fileno Error\n");
    exit(0);
}

// Rest of the function

lseek(fd, -1, SEEK_CUR);

Here is the doc => https://linux.die.net/man/3/fileno

YaatSuka
  • 231
  • 1
  • 9
  • So I need to maintain (2) pointers in my file? Right now I'm currently using `fread` to populate values into my `struct`s with header information. Apologize for the probably stupid question, but are you stating that when I use `fopen` for my current `FILE *fp` I should also follow it up with another `int` pointer with `open`? I'll have to look up those arguments as well since those are new to me. – Pwrcdr87 Oct 03 '17 at 02:20
  • Thanks! Right after you posted that I found https://stackoverflow.com/questions/3167298/how-can-i-convert-a-file-pointer-file-fp-to-a-file-descriptor-int-fd of course. I didn't phrase my problem correctly leaving me confused. I'll give it a shot! – Pwrcdr87 Oct 03 '17 at 02:36
  • I've added the code you mentioned and don't see the response I'd expect. Please look at my edit above and don't be afraid to tell me how I'm doing something wrong in a very stern and critical way. – Pwrcdr87 Oct 03 '17 at 02:44