Get the full path of the files in C - c

Basically this piece of code gives me names of the files in the directory....But I need to get their paths instead.. I tried to use function realpath(). But I am using it wrong I guess(I showed in code where i wanted to use it). Any ideas how to fix it?. One more thing: It gives me only names of subdirectories, but basically I need to get paths of the their files too.Thanks.
#include <stdio.h>
#include <dirent.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <unistd.h>
int main (int c, char *v[]) {
int len, n;
struct dirent *pDirent;
DIR *pDir;
int ecode=0;
struct stat dbuf;
for (n=1; n<c; n++){
if (lstat(v[n], &dbuf) == -1){
perror (v[n]);
ecode ++;
}
else if(S_ISDIR(dbuf.st_mode)){
printf("%s is a directory/n ", v[n]);
}
else{
printf("%s is not a directory\n", v[n]);
}
}
if (c < 2) {
printf ("Usage: testprog <dirname>\n");
return 1;
}
pDir = opendir (v[1]);
if (pDir == NULL) {
printf ("Cannot open directory '%s'\n", v[1]);
return 1;
}
while ((pDirent = readdir(pDir)) != NULL) {
// here I tried to use realpath()
printf ("[%s]\n", realpath(pDirent->d_name));
}
closedir (pDir);
return 0;
}

All you need is to add the second argument to realpath, because it needs a buffer to write into. I recommend you take the line out of the printf statement and give it its own line. realpath() can return a char*, but it wasn't designed to.
#include <limits.h> //For PATH_MAX
char buf[PATH_MAX + 1];
while ((pDirent = readdir(pDir)) != NULL) {
realpath(pDirent->d_name, buf);
printf ("[%s]\n", buf);
}
This appears to display the full paths properly on my system.

Related

Listing files in a directory into array in C using strcpy and strncpy

#include <stdio.h>
#include <stdlib.h>
#include <dirent.h>
#include <string.h>
int main()
{
struct dirent *de;
char *filesList[258];
DIR *dr = opendir("C:\\Users\\Asus\\Desktop\\Training-Process-of-YOLO-architecture\\annotations");
if (dr == NULL)
{
printf("could not open folder");
return 0;
}
int i = 0;
while ((de = readdir(dr)) != NULL)
{
if (!strcmp(de->d_name, ".") || !strcmp(de->d_name, ".."))
{
}
else
{
filesList[i] = (char *)malloc(strlen(de->d_name) + 1);
strcpy(filesList[i], de->d_name); // strncpy add some unnecessary values at the end of some array elements
i++;
}
}
closedir(dr);
for (int i = 0; i < 256; i++)
{
printf("%s\n", filesList[i]);
}
return 0;
}
strncpy adds some junk values at the end of some array elements as follows.
2077.xml\ProgramΦ_U═≥
2080.xml$G
2083.xmlam FilesΦ_U═≥
2086.xmlll\ModulΦ_U═≥
2089.xmlstem32\WΦ_U═≥
2095.xml\v1.0\MoΦ_U═≥
2098.xml\ProgramΦ_U═≥
Here I'm reading xml files and adding them to an array. Above you can see some junk values are coming at the end of the elements. This doesn't happen with strcpy(). Can someone explain this behaviour ?
The posted code needs to pay attention to the fields:
/* Length of name without \0 */ size_t d_namlen;
/* File type */ int d_type;
Notice there is a 'name length' field that can be used when copying the directory name.
Notice the file type that can be used to determine if the current file is a 'normal' file or something else.

Find Argv[i] in /bin and /sbin in C

I cannot locate argv[i] in /bin (or /sbin), package only checks the directory it was run from. How do I check for argv[i] in /bin and /sbin?
I am currently working on my own package manager and I am writing it in pure C. I am currently writing the check to see if the package(s) passed (using argv[]) are already installed. The issue I am having is that when I run for the check my code only checks in the directory it was run from and I need it to check /bin and /sbin (I am going to handle to checking of /sbin) and I am trying to get it to check /bin, but am having zero luck. I cannot seem to get this to work, each time they just check the current working directory and I need them to check /bin. I can't figure this out, has anyone by chance done this in pure C before? Thank you
These are all the methods I have tried so far, none of them work...
using stat()
#include <stdio.h>
#include <sys/stat.h>
#include <stdlib.h>
#include <dirent.h>
int main(int argc, char *argv[])
{
struct dirent *de = calloc(1, sizeof(struct dirent));
DIR *dr = opendir("/bin"); /* directory to open */
short i;
struct stat *program = calloc(1, sizeof(struct stat));
if (dr == NULL) {
printf("directory could not be opened");
return 0;
}
while ((de = readdir(dr)) != NULL) {
for (i = 1; i < argc; i++) {
if (stat(argv[i], program) == 0) {
printf("found\n");
closedir(dr);
}
else {
printf("not found\n");
closedir(dr);
}
}
}
}
using realpath
#include <stdio.h>
#include <stdlib.h>
#include <dirent.h>
int main(int argc, char *argv[])
{
struct dirent *de = calloc(1, sizeof(struct dirent));
DIR *dr = opendir("/bin"); /* directory to open */
short i;
char *res = realpath(argv[i], NULL);
if (dr == NULL) {
printf("directory could not be opened");
return 0;
}
while ((de = readdir(dr)) != NULL) {
for (i = 1; i < argc; i++) {
if (res == NULL) {
printf("found\n");
closedir(dr);
}
else {
printf("not found\n");
closedir(dr);
}
}
}
}
using strcmp
#include <stdio.h>
#include <string.h>
#include <dirent.h>
int main(int argc, char *argv[])
{
struct dirent *de;
DIR *dr = opendir("/bin"); /* directory to open */
short i;
struct stat program;
if (dr == NULL) {
printf("directory could not be opened");
return 0;
}
while ((de = readdir(dr)) != NULL) {
for (i = 1; i < argc; i++) {
if (strcmp(de->d_name, argv[i]) == 0) {
printf("found\n");
closedir(dr);
}
else {
printf("not found\n");
closedir(dr);
}
}
}
}
I am expecting them all to work as follows:
check echo
// it would go to /bin and find echo and then print
found
but when I run them they only check the current working directory, so for example:
check connection.c
// finds connection.c in the same directory
found
Solution
Naha! I found a way to do it! So using the function chdir() I can run stat() in /bin like so:
#include <stdio.h>
#include <sys/stat.h>
#include <stdlib.h>
#include <unistd.h>
int main(int argc, char *argv[])
{
short i;
struct stat *program = calloc(1, sizeof(struct stat));
for (i = 1; i < argc; i++) {
chdir("/bin"); /* move to /bin */
if (chdir("/bin") != 0)
return 1;
if (stat(argv[i], program) == 0)
return 0;
else
return 1;
}
}
I tried simplifying your code just to output the contents of the folder, I get the contents of the '/bin' folder each time.
As a general rule of thumb, I like to get my code to compile without warnings, test it does what I expect it to, then work on the next bit.
The 'struct stat program' was causing the code not to compile, my guess is you were running an old version of the code.
#include <stdio.h>
#include <dirent.h>
#include <string.h>
int main(int argc, char *argv[])
{
struct dirent *de;
DIR *dr = opendir("/bin"); /* directory to open */
short i;
// struct stat program;
if (dr == NULL) {
printf("directory could not be opened");
return 0;
}
while ((de = readdir(dr)) != NULL) {
printf(de->d_name);
printf("\n");
}
}
If I understand you correctly then you’re confusing the semantics of opendir/readdir with those of chdir:
opendir does not change the working directory. This means that, e.g. realpath("echo", resolved_path) won’t resolve echo as /bin/echo but as $PWD/echo (where $PWD is the environment variable holding your current working directory).
That said, as a general rule you should not chdir inside your process. Instead, you can construct the path explicitly … e.g. via sprintf:
const char *binpath = "/bin";
char *fullpath = malloc(strlen(binpath) + 1 + strlen(argv[1]) + 1);
sprintf(fullpath, "%s/%s", binpath, argv[1]);
… and then stat the result (no need to dynamically allocate your struct stat either):
struct stat st;
if (stat(fullpath, &st) != 0) {
fprintf(stderr, "error in stat(): %s", strerror(errno));
}
printf("%s has inode %ju\n", fullpath, (unsigned long) st.st_ino);

How do I recursively go through folders and count total file size?

I am trying to make this work recursively so when it finds a folder it goes into the folder and finds the file size, then at the end print the total of all file sizes. However I cannot figure out how to get this to work recursively, I have tried many things. Also my count for total does not end up correctly even when I am not doing recursion. Any and all help is greatly appreciated.
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <dirent.h>
#include <string.h>
#include <stdlib.h>
void do_ls(char[]);
int total = 0;
int main(int ac, char *av[])
{
if (ac == 1)
do_ls(".");
else
{
while (--ac) {
printf("%s:\n", *++av);
do_ls(*av);
}
}
}
void do_ls(char dirname[])
{
DIR *dir_ptr;
struct dirent *direntp;
struct stat info;
if ((dir_ptr = opendir(dirname)) == NULL)
fprintf(stderr, "ls01: cannot opern %s\n", dirname);
else
{
while((direntp = readdir(dir_ptr)) != NULL) {
stat(direntp->d_name, &info);
if (S_ISDIR(info.st_mode))
printf("%s\n", direntp->d_name);
//I believe recursion goes here, I tried the following
//do_ls(direntp->d_name);
//do_ls(".");
//do_ls(dirname + '/' + direntp->d_name);
//none of these seemed to work.
else
printf("%d %s\n", (int)info.st_size, direntp->d_name);
total += (int)info.st_size;
}
closedir(dir_ptr);
}
printf("Your total is: %d \n", total);
}
When you have a directory entry to recurse into, you must construct the path by concatenating the directory and the entry name with a / separator and call do_ls recursively.
In order to compute file sizes, you can use the stat system call, but you will need the pathname too, so construct is before testing for the entry type (use malloc to allocate space for the concatenated string) and do not forget to free it after use.
Also ignore the . and .. entries and move the closedir() out of the while loop.
Here is an improved version that does not use a global variable for the
total size, but instead returns the cumulative size to the caller:
#include <dirent.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
long long do_ls(const char *name) {
DIR *dir_ptr;
struct dirent *direntp;
struct stat info;
long long total = 0;
int output = 1;
if (stat(name, &info)) {
fprintf(stderr, "ls01: cannot stat %s\n", name);
return 0;
}
if (S_ISDIR(info.st_mode)) {
if ((dir_ptr = opendir(name)) == NULL) {
fprintf(stderr, "ls01: cannot open directory %s\n", name);
} else {
while ((direntp = readdir(dir_ptr)) != NULL) {
char *pathname;
/* ignore current and parent directories */
if (!strcmp(direntp->d_name, ".") || !strcmp(direntp->d_name, ".."))
continue;
pathname = malloc(strlen(name) + 1 + strlen(direntp->d_name) + 1);
if (pathname == NULL) {
fprintf(stderr, "ls01: cannot allocated memory\n");
exit(1);
}
sprintf(pathname, "%s/%s", name, direntp->d_name);
total += do_ls(pathname);
free(pathname);
}
closedir(dir_ptr);
}
} else {
total = info.st_size;
}
printf("%10lld %s\n", total, name);
return total;
}
int main(int ac, char *av[]) {
int i = 1;
if (i >= ac) {
do_ls(".");
} else {
long long total = 0;
while (i < ac) {
total += do_ls(av[i++]);
}
printf("total is: %lld\n", total);
}
return 0;
}
When you have the filename building working as already advised, the global
int total = 0;
is inadequate for summing the size of files with
total += (int)info.st_size;
so I suggest
uint64_t total;
and then
total += info.st_size;
Besides that a global is poor practice, which can be improved by returning a value from the recursive function, when you have it working. The total then can be summed within the recursive function.
uint64_t do_ls(char[]);

Directory listing with wildcards in C

Is there a ready-made function in C that can list the contents of a directory using wildcards to filter out file names, for example, the equivalent of:
echo [!b]????
which shows the names of directory entries that are four characters long and do not start with "b"?
I know I can use scandir, but then, I need to provide my own filter function:
#include <stdio.h>
#include <stdlib.h>
#include <dirent.h>
int filter(const struct dirent *entry)
{
if (strlen(entry->d_name) == 4 && entry->d_name[0] != 'b') return 1;
else return 0;
}
void main(void)
{
struct dirent **list;
int count;
count=scandir(".", &list, filter, alphasort)-1;
if (count < 0)
puts("Cannot open directory");
else
for (; count >= 0; count--)
puts(list[count]->d_name);
free(list);
}
Honestly, I am seriously considering actually calling shell to do it for me:
#include <stdio.h>
#include <stdlib.h>
void main(void)
{
FILE *fp;
char buffer[1024];
fp=popen("echo [!b]???", "r");
if (fp == NULL)
puts("Failed to run command.");
else
while (fgets(buffer, sizeof(buffer), fp) != NULL)
puts(buffer);
pclose(fp);
}
As mentioned in the comments the glob() function would be pretty good for this:
#include <stdio.h>
#include <glob.h>
int
main (void)
{
int i=0;
glob_t globbuf;
if (!glob("[!b]????", 0, NULL, &globbuf)) {
for (i=0; i <globbuf.gl_pathc; i++) {
printf("%s ",globbuf.gl_pathv[i]);
}
printf("\n");
globfree(&globbuf);
} else
printf("Error: glob()\n");
}

how can I get this readdir code sample to search other directories

I am currently working with a code example that initially is designed to take an argument, then search for that argument in the current directory, I've tried to make it search another directory (/dev/shm to exact) by replacing the "." with "/dev/shm" but the code turns up nothing when i search for something* (notice the wildcard). The wild card search works fine in the current directory so I do not think it is the wild card that is the problem, If someone could help me out though I would really appreciate it, thanks!
#include <dirent.h>
#include <errno.h>
#include <stdio.h>
#include <string.h>
static void lookup(const char *arg)
{
DIR *dirp;
struct dirent *dp;
if ((dirp = opendir(".")) == NULL) {
perror("couldn't open '.'");
return;
}
do {
errno = 0;
if ((dp = readdir(dirp)) != NULL) {
if (strcmp(dp->d_name, arg) != 0)
continue;
(void) printf("found %s\n", arg);
(void) closedir(dirp);
return;
}
} while (dp != NULL);
if (errno != 0)
perror("error reading directory");
else
(void) printf("failed to find %s\n", arg);
(void) closedir(dirp);
return;
}
int main(int argc, char *argv[])
{
int i;
for (i = 1; i < argc; i++)
lookup(argv[i]);
return (0);
}
opendir doesn't handle wildcards. It expects a real directory path. I'm not sure what you mean when you say
wildcard search works in the current directory
If you mean it works in your shell, that's to be expected. The shell will first expand the wildcard and then perform the command you typed.
So how to solve this? Expand the wildcard yourself using glob before calling opendir.
Edit: sorry, I thought you were trying to match the wildcard in the directory name. It looks like you want to match directory contents using the wildcard. In that case simply replace
if (strcmp(dp->d_name, arg) != 0)
with
if (fnmatch(arg, dp->d_name, 0) != 0)
You could also use glob for this. It will actually replace the call to opendir and the loop. Here is an example for using glob:
#include <glob.h>
#include <stdio.h>
static void lookup(const char *root, const char *arg)
{
size_t n;
glob_t res;
char **p;
chdir(root);
glob(arg, 0, 0, &res);
n = res.gl_pathc;
if (n < 1) {
printf("failed to find %s\n", arg);
} else {
for (p = res.gl_pathv; n; p++, n--) {
printf("found %s\n", *p);
}
}
globfree(&res);
}
int main(int argc, char *argv[])
{
int i;
for (i = 2; i < argc; i++)
lookup(argv[1], argv[i]);
return (0);
}
I'm not sure what you are expecting. If your program is called lookup then if you call it in the 'current directory', where that directory holds files something.1, something.2 and something.3 like this:
lookup something*
the shell will expand it to
lookup something.1 something.2 something.3
and your program will see three command line args and will be able to find a match in the readdir loop.
If you change the opendir call to "/dev/shm" and call it from the original directory (the one that has something.[1-3]) then the shell will again expand the wildcard in the current directory. But unless the files something.1, something.2 and something.3 are also present in /dev/shm, the readdir loop will not see them.
Note that your lookup function is a bit odd. I would expect it to be more like this:
static int lookup(const char * dir, const char *arg)
{
DIR *dirp;
struct dirent *dp;
if ((dirp = opendir(dir)) == NULL) {
perror(dir);
return -1;
}
while ((dp = readdir(dirp)) != NULL) {
if (!strcmp(dp->d_name, arg)) {
break;
}
}
(void) closedir(dirp);
printf("%s %s\n", dp ? "found" : "failed to find", arg);
return 0;
}

Resources