searching for a file name using only an extension - c

prev problem was not described well.
I need to retrieve a file name and assign that name to a variable. The only thing is that the file can have any name.
The thing that I know is the file extension and there can only be one file of this extension.
Is there any way to implement this in C?
Update #1
File extension is *.knb and target is Linux system.
#include <stdio.h>
#include <dirent.h>
#define LOG_() do{fprintf(stdout, " UNIT TESTING...\n");}while(0)
char* knbname;
void fName(void);
int main()
{
LOG_();
fName(void);
//do some more code
return 0;
}
void fName(void){
DIR *d;
struct dirent *dir;
d = opendir(".");
if (d)
{
//find file with *.knb extension and place name into knbname
}
closedir(d);
}
I don't know how to find for a *.knb file in the fName() function and how to assign name to knbname. Any help will be appreciated

You are reinventing POSIX's glob() function. Here is an example of how to use it (see the flags in manpage for more options):
#include <glob.h>
int main(void)
{
glob_t glob_res = { 0 };
glob("*.knb", 0, NULL, &glob_res);
if (glob_res.gl_pathc == 0) puts("No match.");
else printf("First match is %s\n", glob_res.gl_pathv[0]);
globfree(&glob_res);
return 0;
}

After Andrew pointed out some major flaw in the previous design, i will make another attempt for a solution...
int hasExtension(char *filename, char *extension);
int main(void)
{
DIR *directory;
struct dirent *entry;
if(NULL == (directory = opendir(".")))
{
perror("Failed to open directory\n");
exit(1);
}
while( NULL != (entry = readdir(directory)) )
{
if(DT_REG == entry->d_type) { // Regualar files only
if(hasExtension(entry->d_name, ".knb"))
{
printf("Match - %s\n", entry->d_name);
} else {
printf("No match - %s\n", entry->d_name);
}
}
}
closedir(directory);
return 0;
}
int hasExtension(char *filename, char *extension)
{
size_t filename_len, extension_len;
if((NULL == filename) || (NULL ==extension))
return 0;
filename_len = strlen(filename);
extension_len = strlen(extension);
if(filename_len < extension_len)
return 0;
return !strcmp(filename + filename_len - extension_len, extension);
}

Related

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 to count only the number of directories from a path

I am trying to count only the directories from a path, but it doesn't work. So, i don't want to number both files and directories, i want only the directories. Could you help me, please?
The code:
int listdir(char *dir) {
struct dirent *dp;
struct stat s;
DIR *fd;
int count = 0;
if ((fd = opendir(dir)) == NULL) {
fprintf(stderr, "listdir: can't open %s\n", dir);
}
while ((dp = readdir(fd)) != NULL) {
if (!strcmp(dp->d_name, ".") || !strcmp(dp->d_name, ".."))
continue;
stat(dp->d_name, &s);
if (S_ISDIR(s.st_mode))
count++;
}
closedir(fd);
return count;
}
your stat() call will fail, since you are not in the correct directory. This you can solve by either changing the current directory, or generate full paths and give to stat as argument.
Some Unixes, you can optimize away the stat call by looking at struct dirent, the d_type field
int listdir(char *dir) {
struct dirent *dp;
struct stat s;
DIR *fd;
int count = 0;
if ((fd = opendir(dir)) == NULL) {
fprintf(stderr, "listdir: can't open %s\n", dir);
}
chdir (dir); /* needed for stat to work */
while ((dp = readdir(fd)) != NULL) {
if (!strcmp(dp->d_name, ".") || !strcmp(dp->d_name, ".."))
continue;
#ifdef _DIRENT_HAVE_D_TYPE
switch (dp->d_type)
{
case DT_UNKNOWN:
stat(dp->d_name, &s);
if (S_ISDIR(s.st_mode)) count++;
break;
case DT_DIR:
count++;
break;
}
#else
stat(dp->d_name, &s);
if (S_ISDIR(s.st_mode)) count++;
#endif
}
closedir(fd);
return count;
}
I think you want...
written in C
count the number of directoriesfrom a path.
the counting function will returns int value.
I have no idea about your environment, so it is just a example solution.
If you can use glob, it is so easy to count the number of directories. that is: main.c
#include <stdio.h>
#include <glob.h>
#include <string.h>
#define MAX_PATH 1023
int countDirectories(char* dir)
{
char path[MAX_PATH] = "";
strcat(path, dir);
strcat(path, "/*");
glob_t globbuf;
long i, count = 0;
if (glob(path, GLOB_NOSORT | GLOB_ONLYDIR, NULL, &globbuf) == 0)
{
count = globbuf.gl_pathc;
for (i = 0; i < globbuf.gl_pathc; i++)
{
count += countDirectories(globbuf.gl_pathv[i]);
}
}
globfree(&globbuf);
return count;
}
int main(int argc, char* argv[])
{
int count;
if (argc > 1)
{
count = countDirectories(argv[1]);
}
else
{
count = countDirectories(".");
}
printf("there are %d directories.\n", count);
return 0;
}
and you can try this like:
> gcc main.c -o testglob
> ./testglob /path/to/target/dir
then you'll receive output like this:
path = /path/to/target/dir/*, there are N directories
thanks.

how to find the files with .done extension from a specific directory using c language

int main(int argc, char **argv)
{
char *dirFilename = "/home/sv5071184/sample";
DIR *directory = NULL;
directory = opendir (dirFilename);
if(directory == NULL)
return -1;
struct dirent *dirp;
while ((dirp = readdir (directory)) != NULL) {
if( ! (strcmp (dirp->d_name ,".done") == 0) )
{
printf ("%s\n", dirp->d_name);
}
}
if(closedir(directory) < 0)
return -1;
}
NOTE: i have updated the code . with this code am able to find all the files in a directory whereas i need to find only .done files
The function char *strstr(const char *haystack, const char *needle); give you the position of the substring 'needle' if exists. Then you just have to test if the substring is at the end of the file name.
EDIT:
If you want to keep your code as you did, in the while, change your if condition to:
strstr(dirp->d_name, ".done")!=NULL
Here is how you can find the extension using strrchr:
#include <string.h>
int main(int argc, char **argv)
{
char *dirFilename = "/home/sv5071184/sample";
DIR *directory = NULL;
directory = opendir (dirFilename);
if(directory == NULL)
return -1;
struct dirent *dirp;
while ((dirp = readdir (directory)) != NULL) {
char *dot = strrchr(dirp->d_name, '.'); /* Find last '.', if there is one */
if (dot && (strcmp(dot, ".done") == 0))
{
printf ("%s\n", dirp->d_name);
}
}
if(closedir(directory) < 0)
return -1;
}
Finding files that end in specific string (an extension) is very common thing to do with file names.
For the better or worse, under POSIX standard, file name is not separated into name and extension , and unfortunately C string library does not have a function that is like endsWith in Java or C#.
I think that you should write a simple utility function ends_with(char*, char*) that checks if one string ends with another and use it to check if ends_with(ent->d_name, ".done").
Hope this helps :)
#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdlib.h>
#include <dirent.h>
int main(int argc, char **argv)
{
char *dirFilename = "/home/sv5071184/sample";
DIR *directory = NULL;
directory = opendir (dirFilename);
if(directory == NULL)
return -1;
struct dirent *dirp;
while ((dirp = readdir (directory)) != NULL) {
if ( strstr(dirp->d_name , ".done" ))
{
printf( "found a .done file: %s\n", dirp->d_name );
}
}
if(closedir(directory) < 0)
return -1;
}
I have used this way and it is working.. thanks everyone

Getting full file path during recursive file walk C

I am doing a recursive walk through directories to make changes to files. My change file function needs the full path of the file to be able to do stuff. However, what my program is doing right now is just getting the name of the current file or folder but not the full path.
My approach is that I would make a string and keeps appending names to it until I get the full path. However, because I'm doing recursion, I'm having troubles passing the string around to append more strings to it.
This is my code:
#include <stdio.h>
#include <stdlib.h>
#include <regex.h>
#include <string.h>
#include <dirent.h>
#include <unistd.h>
#include <sys/types.h>
#include <errno.h>
void recursiveWalk(const char *pathName, char *fullPath, int level) {
DIR *dir;
struct dirent *entry;
if (!(dir = opendir(pathName))) {
fprintf(stderr, "Could not open directory\n");
return;
}
if (!(entry = readdir(dir))) {
fprintf(stderr, "Could not read directory\n");
return;
}
do {
if (entry->d_type == DT_DIR) { // found subdirectory
char path[1024];
int len = snprintf(path, sizeof(path)-1, "%s/%s", pathName, entry->d_name); // get depth
path[len] = 0;
// skip hidden paths
if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0) {
continue;
}
fprintf(stdout, "%*s[%s]\n", level*2, "", entry->d_name);
// Append fullPath to entry->d_name here
recursiveWalk(path, fullPath, level + 1);
}
else { // files
fprintf(stdout, "%*s- %s\n", level*2, "", entry->d_name);
//changeFile(fullPath);
}
} while (entry = readdir(dir));
closedir(dir);
}
int main(int argn, char *argv[]) {
int level = 0;
recursiveWalk(".", "", level);
return 0;
}
Well there are a number of little problems in your code.
you never use nor change fullPath in recursiveWalk
your formats are weird : you use level*2 to limit the number of characters printed from an empty string
you compute the actual path only when you have found a directory, while you say you need it to change a file.
you add path[len] = 0 after a snprintf when snprintf guarantees that but buffer is null terminated
But apart from that, you correctly pass the path to the analyzed dir append to the path passed in initial call, but in pathName variable, and computed as path.
So a possible fix for your code would be :
fix the formats for printf
remove the unused fullPath parameter from recursiveWalk
allways compute path and use it in the file branch
comment out the unnecessary path[len] = '\0'
I also replaced while (entry = readdir(dir)); with while ((entry = readdir(dir))); to explicitely tell the compiler that I want to set entry and then test its value - and remove the warning
Possible code:
#include <stdio.h>
#include <stdlib.h>
#include <regex.h>
#include <string.h>
#include <dirent.h>
#include <unistd.h>
#include <sys/types.h>
#include <errno.h>
void recursiveWalk(const char *pathName, int level) {
DIR *dir;
struct dirent *entry;
if (!(dir = opendir(pathName))) {
fprintf(stderr, "Could not open directory\n");
return;
}
if (!(entry = readdir(dir))) {
fprintf(stderr, "Could not read directory\n");
return;
}
do {
char path[1024];
int len = snprintf(path, sizeof(path)-1, "%s/%s", pathName, entry->d_name); // get depth
// path[len] = 0;
if (entry->d_type == DT_DIR) { // found subdirectory
// skip hidden paths
if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0) {
continue;
}
fprintf(stdout, "%s [%s] (%d)\n", pathName, entry->d_name, level);
// Append fullPath to entry->d_name here
recursiveWalk(path, level + 1);
}
else { // files
fprintf(stdout, "%s (%d)\n", path, level);
//changeFile(fullPath);
}
} while ((entry = readdir(dir)));
closedir(dir);
}
int main(int argn, char *argv[]) {
int level = 0;
recursiveWalk(".", level);
return 0;
}
Recursion is a succinct way to express things (especially walking directories), but actually you should normally avoid it in practice. If the directory tree is deep enough, it will crash your software.
Using a queue eliminates the need for recursion, and is generally an efficient way to traverse.
I'm including the code I use to process directory trees in a project...
static int on_dir(const char* const dir, struct duplicate** dp) {
bool r = opts.recurse;
DIR* d = opendir(dir);
if (!d)
return - 1;
struct dirent* de;
while ((de = readdir(d))) {
struct stat s;
size_t bs = strlen(dir) + strlen(de->d_name) + 2;
char b[bs];
const char* const a = strjoin(b, dir, de->d_name, '/');
if (lstat(a, &s)) {
print_error("unable to stat %s", d);
continue;
}
if (S_ISREG(s.st_mode))
if (on_file(a, &s, dp))
print_error("unable to process file %s/%s", dir, de->d_name);
}
if (!r) {
if (closedir(d))
on_fatal("unable to close directory %s", dir);
return 0;
}
rewinddir(d);
while ((de = readdir(d))) {
struct stat ds;
size_t bs = strlen(dir) + strlen(de->d_name) + 2;
char b[bs];
const char* const d = strjoin(b, dir, de->d_name, '/');
if (lstat(d, &ds)) {
print_error("unable to stat %s", d);
continue;
}
if (S_ISDIR(ds.st_mode)) {
const char* const dot = ".";
const char* const dotdot = "..";
if (!strcmp(dot, de->d_name) || !strcmp(dotdot, de->d_name))
continue;
struct path* p = path_create(strcpy(fmalloc(bs), d));
queue_add(&paths, &p->queue);
}
}
if (closedir(d))
print_error("unable to close directory %s", dir);
return 0;
}
and the code for strjoin
static inline char* strjoin(char* restrict const d, const char* restrict const a, const char* restrict const b, const char c) {
size_t na = strlen(a);
size_t nb = strlen(b);
memcpy(d, a, na);
d[na] = c;
memcpy(d + na + 1, b, nb);
d[na + nb + 1] = '\0';
return d;
}
I'm hoping this helps. Please feel free to use any of the code you find in the git repository.

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