-
What is happening here?
This one has got me stumped. I cannot make heads or tails of it with a debugger, or by looking at it.. I have been looking for a long time..
The code is altered from the original (first example) found here.
Here is the changed code that I am playing with:
Code:
#include <sys/types.h>
#include <sys/stat.h>
#include <dirent.h>
#include <sys/unistd.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
#include <pwd.h>
#include <grp.h>
#include <stdio.h>
#include <string.h>
#include <time.h>
#include <stdlib.h>
#define BUFSZ 1024
int main(void)
{
DIR *dp;
struct dirent *dirp;
struct stat st;
struct passwd *pw;
struct group *grp;
char buf[BUFSZ], *bp, *ftime;
memset(buf, '\0', BUFSZ);
if ((dp = opendir(".")) == NULL) {
printf("Cannot open this directory\n");
perror("");
exit(EXIT_FAILURE);
}
while ((dirp = readdir(dp)) != NULL) {
bp = buf;
do {
if (dirp->d_reclen != 0) {
if (strcmp(dirp->d_name, ".") == 0 ||
strcmp(dirp->d_name, "..") == 0) {
break; /* Don't want to list dot and dot-dot */
}
stat(dirp->d_name, &st);
ftime = ctime(&st.st_mtime);
ftime[16] = '\0';
ftime += 4;
pw = getpwuid(st.st_uid);
grp = getgrgid(st.st_gid);
printf("%3zu %-8s %-7s %9zu %s %s\n",
st.st_nlink, pw->pw_name, grp->gr_name,
st.st_size, ftime, dirp->d_name);
}
bp += dirp->d_reclen;
dirp = (struct dirent *) (bp);
} while (dirp->d_ino != 0);
memset(buf, '\0', BUFSZ);
}
closedir(dp);
return 0;
}
The particular bits I don't get are in red. Why make a buffer, set it to zero, make bp point to it, then add the size of d_reclen to it, then turn around cast it as a struct dirent* and assign that to dirp? I am obviously missing something here. I tried doing things like
Code:
// bp += dirp->d_reclen;
// dirp = (struct dirent *) (bp);
dirp += dirp->d_reclen;
or
Code:
// bp += dirp->d_reclen;
// dirp = (struct dirent *) (bp);
dirp += dirp->d_off;
But they both segfault on me. Why the empty buffer? This thing has got me completely flummoxed.
-
If you want to move forward by bytes, you have to have a char* -- dirp += dirp->d_reclen moves forward, not eighty bytes, but eighty "struct dirents". I would have expected, not bp = buf, but bp = dirp.
-
And what is the point of the
do ... while (dirp->d_ino != 0);
loop?
readdir doesn't return a pointer to a number of directory entries. If you want the next entry, call readdir() again, and just drop the hooky pointer arithmetic assuming there is an array of them.
Ah, but I see you're using readdir(), where the original example used getdents()
There's a big difference
http://linuxreviews.org/man/readdir/
-
>readdir doesn't return a pointer to a number of directory entries. If you want the next >entry, call readdir() again, and just drop the hooky pointer arithmetic assuming there is an >array of them.
Best advice I could have gotten I think. I was trying to convert this thing, to make it workable, and I should have scrapped more of the code than I did. I ditched the do..while loop, and the pointer arithemetic, and wonder of wonders, it worked nicely.
I am having trouble confirming this, but from my reading, it seems that getdents was never truly a Linux system call, but came over from one of the BSDs. Salem, can you tell me if you always had to call it (getdents) with syscall, or did it at one time work under Linux like in the original program that I posted before it was modified? I am just a bit curious about the history of getdents.
Anyway, thanks for your suggestion. I need to learn that sometimes, just because something 'works,' (or seems to work) it is not necessarily worth learning.
-
I've always used readdir()
I wasn't aware of anything called getdents until just now, so I can't say where it came from, or anything.