2

I'm studying low-level I/O in C for the first time and I'm trying to write a program that prints a file backwards, but it seems that this while loop doesn't work. Why does it happen?

#include <stdio.h>
#include <errno.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#define BUFFSIZE 4096

int main(){
    int n;
    int buf[BUFFSIZE];
    off_t currpos;

    int fd;
    if((fd = open("fileprova", O_RDWR)) < 0)
        perror("open error");

    if(lseek(fd, -1, SEEK_END) == -1)
        perror("seek error");

    while((n = read(fd, buf, 1)) > 0){
        if(write(STDOUT_FILENO, buf, n) != n)
            perror("write error");

        if(lseek(fd, -1, SEEK_CUR) == -1)
            perror("seek error");

        currpos = lseek(fd, 0, SEEK_CUR);
        printf("Current pos: %ld\n", currpos);
    }

    if(n < 0)
        perror("read error");

    return 0;

}
DevSolar
  • 67,862
  • 21
  • 134
  • 209
Gibser
  • 197
  • 8
  • 1
    C and C++ are separate languages. Pick one. Your example doesn't use any C++, so I removed that tag. – DevSolar Dec 15 '19 at 13:06
  • 2
    Read advances the pointer by 1, so seek -1 will always read the same character. – stark Dec 15 '19 at 13:09
  • You need to start at end-1 and subtract 2 – stark Dec 15 '19 at 13:10
  • @stark Oh okay, great, thank you! – Gibser Dec 15 '19 at 13:11
  • BTW, it would be vastly more efficient to read or `pread` into a `char buf[4096]` array in 4k chunks or so, reverse that in place with a loop, and write it out. So you have a factor of 2^10 fewer system calls, and run about that much faster. (Making 1 system call per byte is very inefficient.) – Peter Cordes Dec 15 '19 at 15:00
  • @PeterCordes Okay, thank you for the advice. However, it was just an educational exercise to better understand these system calls, it is one of the very first exercises I do relating to this topic. Thank you again! – Gibser Dec 15 '19 at 15:10

1 Answers1

3

The call read(fd, buf, 1), if successful, will read one byte of data and then move the file pointer forward by one byte! The call lseek(fd, -1, SEEK_CUR) will then move the file pointer backward by one byte!

Net result: your while loop will continue to read the same byte forever!

Solution: Inside your while loop use the following to set the file pointer to read the previous byte: lseek(fd, -2, SEEK_CUR) - and break out of the loop when that call returns -1.

Adrian Mole
  • 49,934
  • 160
  • 51
  • 83
  • 1
    Or on a POSIX system, use [`pread(int fildes, void *buf, size_t nbyte, off_t offset)`](http://man7.org/linux/man-pages/man2/pwrite.2.html) which takes an offset as an extra arg so you don't need to seek in between `read` calls. – Peter Cordes Dec 15 '19 at 14:57