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);
Related
I am trying to open a directory and read the files and folders and add '/' at the end of if it is a folder. This is my current code.
#include <dirent.h>
#include <sys/stat.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
int isDir(const char* target)
{
struct stat statbuf;
stat(target, &statbuf);
return S_ISDIR(statbuf.st_mode);
}
int main(int argc, char * argv[])
{
puts("");
struct stat sfile;
struct dirent *dp;
char * dir;
if(argc != 2)
dir = getenv("PWD"); //default
else
dir = argv[1];
DIR *dirp = opendir(dir);
dp = readdir(dirp);
while ( dp != NULL )
{
stat(dp->d_name,&sfile);
//opendir(dp->d_name);
printf("Size : %ld ",sfile.st_size);
if (isDir(dp->d_name))
{
printf("%s/\n",dp->d_name);
}
else
{
printf("%s \n",dp->d_name);
}
dp = readdir(dirp);
}
printf("\n");
closedir(dirp);
}
This works when I pass no arguments, ie, it is reading files and folders in the current working directory. When I pass a directory as argument, it fails to detect further folders in that as directories. Curiously, when tried using errno when I try to open using opendir(), all the contents are detected as directories.
int isDir2(const char* target)
{
opendir(target);
if(errno == ENOTDIR)
{
puts("Not directory");
return 0;
}
else
{
puts("Directory"); //only this is getting printed
return 1;
}
}
In either case, when both '.' and '..' are detected as directories but the rest as either directories or files depending on whether I am using opendir()'s errno or S_ISDIR.
Your isDir function isn't actually checking the return value from stat to validate that it succeeded. And since your statbuf variable is uninitialized, it's undefined behavior.
Better:
int isDir(const char* target)
{
struct stat statbuf = {0};
int result = 0;
if (stat(target, &statbuf) != -1)
{
result = S_ISDIR(statbuf.st_mode);
}
return result;
}
Further, you are invoking opendir without assigning it's return value:
while ( dp != NULL )
{
stat(dp->d_name,&sfile);
opendir(dp->d_name); // WHERE'S THE RESULT?
You need to rethink your logic in this while loop with regards to invoking opendir, readdir, and don't forget to invoke closedir.
So the issue was of working directories. When I checked what the stat function in the isDir() function was returning, it was returning -1. It wasn't able to find the file/folder because it's current working directory was not the folder it was given, rather the cwd where the program was run. What I did was change the directory to the given folder and it works.
The code now:
#include <sys/stat.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
int isDir(const char* target)
{
struct stat statbuf = {0};
int result = 0;
int err = stat(target, &statbuf);
if (err != -1)
{
result = S_ISDIR(statbuf.st_mode);
}
return result;
}
int main(int argc, char * argv[])
{
puts("");
struct stat sfile;
struct dirent *dp;
char * dir;
if(argc != 2)
dir = getenv("PWD"); //default
else
dir = argv[1];
DIR *dirp = opendir(dir);
dp = readdir(dirp);
chdir(dir);
while ( dp != NULL )
{
stat(dp->d_name,&sfile);
printf("Size : %ld ",sfile.st_size);
if (isDir(dp->d_name))
{
printf("%s/\n",dp->d_name);
}
else
{
printf("%s \n",dp->d_name);
}
dp = readdir(dirp);
}
printf("\n");
closedir(dirp);
}
My code prints the files/directory names in a given path(user enters it as a command-line argument). When executing with a given path in the directory, it just works fine but it is supposed to do the same for the current working directory if user does not provide any command-line argument.
I am getting seg fault if I just run as: ./a.out
It works when I run as: ./a.out /path
Please fix my code by providing the necessary code fragment
I have tried to do debugging and found out that it gives the error right after it executes the line following line in the depthFirst function
printf("%s\n", sd->d_name);
My faulty code:
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <dirent.h>
#include <sys/stat.h>
#include <limits.h>
void depthFirst(char * path){
struct dirent *sd;
DIR *dir;
//char path[PATH_MAX];
dir = opendir(path);
if(dir == NULL){
printf("Error, unable to open\n");
exit(1);
}
while( (sd = readdir(dir)) != NULL){
if(strcmp(sd->d_name, ".") != 0 && strcmp(sd->d_name, "..") != 0){
printf("%s\n", sd->d_name);
realpath(sd->d_name,path);
if(isdirectory(path)){
printf("\t");
depthFirst(sd->d_name);
}
}
}
closedir(dir);
}
int isdirectory(char *path) {
struct stat statbuf;
if (stat(path, &statbuf) == -1)
return 0;
else
return S_ISDIR(statbuf.st_mode);
}
int main(int argc, char *argv[]){
char * path;
char * currentDirectory;
if(argc<2){
currentDirectory = ".";
depthFirst(currentDirectory);
}
else{
path = argv[1];
depthFirst(path);
}
return 0;
}
The output is shown below:
.git
Segmentation fault
Jonathan beat me to it in the comments, but this change prevents the problem.
## -9,7 +9,7 ##
void depthFirst(char * path){
struct dirent *sd;
DIR *dir;
- //char path[PATH_MAX];
+ char rpath[PATH_MAX];
dir = opendir(path);
## -22,8 +22,8 ##
while( (sd = readdir(dir)) != NULL){
if(strcmp(sd->d_name, ".") != 0 && strcmp(sd->d_name, "..") != 0){
printf("%s\n", sd->d_name);
- realpath(sd->d_name,path);
- if(isdirectory(path)){
+ realpath(sd->d_name,rpath);
+ if(isdirectory(rpath)){
printf("\t");
depthFirst(sd->d_name);
As another comment pointed out, you cannot reuse the char* path because it is stored in a page of memory that is not writable by your program. Therefore, realpath() will crash upon attempting to write to it.
The canonical way of determining a file's type is to use the commented
out code in this snippet:
// Return the number of files in dirName. Ignore directories and links.
#include <sys/types.h>
#include <dirent.h>
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char** argv) {
int fCt = 0;
struct dirent *dir;
DIR *d;
d = opendir(argv[1]);
if (d == NULL) {
printf("%s was not opened!\n", argv[1]);
exit(0);
}
// Count all of the files.
while ((dir = readdir(d)) != NULL) {
// struct stat buf;
// stat(dir->d_name, &buf);
// if (S_ISREG(buf.st_mode)) { fCt++; }
if (dir->d_type == 8) { fCt++; }
}
return fCt;
}
The element buf.st_mode returns 41ED (hex), 16877 (decimal) for both directories and regular files. S_ISREG fails to find the right bit set for both types.
Note that the line:
if (dir->d_type == 8) { fCt++; }
returns an accurate file count.
Why did the commented out method fail?
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
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;
}