Why mkdir doesn't work with GString? - c

If I use just "/home/user/.some/qwe" instead of homedir->str, it works.
struct stat st = {0};
GString* homedir = g_string_new(NULL);
g_string_append(homedir, getenv("HOME"));
g_string_append(homedir, ".some/qwe");
printf("%s", homedir->str);
if (stat(homedir->str, &st) == -1) {
int res= mkdir(homedir->str, 0777);
g_string_free(homedir, TRUE);
}

You would be able to avoid this bug and always build valid path names by using g_build_filename():
char *homedir = g_build_filename(getenv("HOME"), ".some", "qwe", NULL);
printf("%s, homedir);
if (stat(homedir, &st) == -1) {
int res = mkdir(homedir, 0777);
}
g_free(homedir);

Normally, $HOME does not end with /. So, for your case, HOME = /home/user, and string for mkdir would look like: /home/user.some/qwe (notice missing /).
Try changing your code to add that missing slash, for example use:
g_string_append(homedir, "/.some/qwe");
With that said, all you really needed is to know how to debug - just printing value of your string to stdout would make it immediately obvious.

Related

Implementing parsing with the ls command in C

I'm trying to implement the ls command in C with a few parameters like -a, -l... or -la, but I'm having issues with the parsing, when I use the input I get Segmentation Fault, this is an example of the -a parameter:
int comparator(char *av) {
int i = 0;
if (my_strcmp((av[i]), "-a") == 0)
return 0;
else
return 1;
}
int my_ls_a(char *path) {
int comp = comparator(path);
DIR *pdirec = opendir(".");
struct dirent *direc;
direc = readdir(pdirec);
while (direc != NULL || comp == 0) {
my_printf("%s ", direc->d_name);
direc = readdir(pdirec);
}
if ((path = readdir(pdirec)) == NULL)
my_printf("\n");
if (pdirec == NULL)
return (84);
closedir(pdirec);
return (0);
}
And this is my main:
int main(int ac, char *av[]) {
if (ac == 1)
my_ls_a(av[0]);
return 0;
}
I already have all the #include in a .h by the way.
When I only use the main function it works but not when I add the parameter -a.
It's probably better to use getopt() for parameter parsing instead of writing your own parser.
You have undefined behavior in the function comparator in my_strcmp((av[i]), "-a") because av is defined as a char * so you are passing a character where my_strcmp probably expects a pointer.
You should compile with -Wall -Werror to avoid such silly mistakes.
It is unclear why you pass only a single argument to my_ls_a. You should pass both ac and the argument array av and iterate on the arguments to parse the options.

Searching through all sub directories within a directory

So I had help and now I have a program that searches through the current directory and prints out the file if it exists, now how would I make it so that it goes through a different directory and searches for the file in all the sub directories of it?
I thought if I replaced the line "." with "..", it would go back to the previous directory and go thought all the sub directories, but it only looks for the file in the directory without going in the subs.
//headers here
char *FINDME=NULL;
int filter (const struct dirent *p){
int retval=0;
if (fnmatch(FINDME,p->d_name, 0) == 0)
retval = 1;
return retval;
}
int main(int argc, char **argv){
struct dirent **namelist;
int i = 0;
FINDME = (argc > 1) ? argv[1] : "testfilename";
i = scandir("..", &namelist, filter, alphasort);
if(i < 0){
perror("scandir");
exit(1);
}
while(i--){
printf("%s\n", namelist[i]->d_name);
free(namelist[i]);
}
free(namelist);
return 0;
}
Travelling through a filesystem is actually travelling through a tree (excluding hard and symlinks), so you can use the recursive way:
The following pseudocode will give you an idea:
Function TravelDirectory (String dirname, String filename)
Foreach item=Element in dirname Do
If item.type is File AND item.name==filename
Print item
Else If item.type is Directory
TravelDirectory (item.name, filename)
EndFor
EndFunction
To implement this in C under Linux, for example, you can use the opendir()and readdir() functions instead of scandir(). readdir() will serve you as an iterator for the Foreach part.
Im not 100% certain but have you tried a wildcard or adding a trailing slash
You have ..
Try ../
OR
../*

Regularize Non-existent path in C on Linux

I am writing a utility function for a c program which creates directories, but will also create any intermediate directories which do not exist. I have that code working, but I wanted to turn my attention to improve user input formatting. Namely, I want to regularize the input path before I process it, especially to remove // which will break my current implementation.
I am aware of realpath(3), but my main issue is that it will fail if any of the directories on the path do not already exist. The command line function realpath(1) has the -m option, which seems to do what I want, but I don't want to invoke a shell if I can avoid it (Otherwise I could do mkdir -p and be done with it). Gnu findutils/canonicalize.h has canonicalize_filename_mode, but I don't know how to reference that short of copying the source directly (not out of the question). Thoughts, suggestions?
I am tied to my development environment gcc 4.7.7and rhel 6.6.
Below is my current implementation.
static int do_mkdir(const char *path, mode_t mode) {
struct stat st;
int status = 0;
if ( stat(path, &st) != 0 ) {
errno = 0;
if (mkdir(path, mode) != 0 && errno != EEXIST)
status = -1;
} else if ( !S_ISDIR(st.st_mode) ) {
errno = ENOTDIR;
status = -1;
}
return(status);
}
int mkpath(const char *path, mode_t mode) {
char *pp;
char *sp;
int status;
char copypath[PATH_MAX+1];
copypath = realpath(path, copypath);
status = 0;
pp = copypath;
if(copypath == NULL)
status = -1;
while ( status == 0 && (sp = strchr(pp, '/')) != 0 ) {
if (sp != pp) {
*sp = '\0';
status = do_mkdir(copypath, mode);
*sp = '/';
}
pp = sp + 1;
}
if ( status == 0 )
status = do_mkdir(path, mode);
return (status);
}
It might not fully answer your issue but here is an alternative:
You could process the path string with strtok, setting delim argument to '/'.
By doing so, strtok will return you intermediate directory names (null-terminated: you can get rid off the \0 tweak) and it will gracefully ignore //.
You can then add your "sanitize" checks (look for invalid characters, path name is too long, etc...)
I found a workaround that I believe covers all cases. What I did, was to move the call to realpath into the do_mkdir function so that way it will only have at most one non-existent element, replacing it with a strdup in the mkpath function. I can't think of a situation where performing the canonicalization on a partial path will break anything either.

Execv failing even when command is available

I'm trying to call execv after manually saerching for the program to execute.
In my case,
c is a struct which has args as an array of strings having the arguments passed while receiving input. nargs is the number of arguments.
c->args[0] would contain "ls","cat" etc.
I tried printing the value of the args[0], fullPath etc. in my child process. They all show values like "/bin/ls","/bin/cat" etc. But when I call execv, it returns -1 with an errno of 2, which I understand is the error for "No such file or directory". But I'm sure the file is there because thats what my PathResolver is returning after checking all permissions.
Can anyone point where I might have made a mistake.
//The part happening inside child
char *fullPath = PathResolver(c->args[0],1,&permission);
printf("FullPath: %s -- Permission: %d\n",fullPath,permission);
if(permission==0)
{
fprintf(stderr, "%s: Command not found\n",c->args[0]);
}
else if(permission==-1)
{
fprintf(stderr, "%s: Permission denied\n",c->args[0]);
}
else
{
char* args[c->nargs+1];
int m=0;
for(m=0;m<c->nargs;m++)
{
strcpy(args[m],c->args[m]);
}
args[c->nargs] = NULL;
printf("%d\n",execv(args[0], args));
printf("errno: %d\n",errno);
}
PathResolver function
char* PathResolver(char *command, int ResolverMode, int *Permission)
{
*Permission = 0;
char *returnString;
returnString = malloc((sizeof(char)));
char *strPath = getenv("PATH");
char *del = ":";
char *strToken = strtok(strPath,del);
FILE *f;
while(strToken)
{
char filePath[100];
sprintf(filePath,"%s/%s",strToken,command);
if(access(filePath,F_OK)>=0)
{
if(access(filePath,X_OK)>=0)
{
*Permission = 1;
sprintf(returnString,"%s%s ",returnString,filePath);
if(ResolverMode == 1)
break;
}
else
{
*Permission = -1;
}
}
strToken = strtok(NULL,del);
}
sprintf(returnString,"%s\b",returnString);
return returnString;
}
strcpy(args[m],c->args[m]); is undefined behaviour, because args[m] is not a pointer to valid memory.
The following might be simpler:
char * args[c->nargs + 1];
for (size_t m = 0; m != c->nargs; ++m)
{
args[m] = c->args[m];
}
args[c->nargs] = NULL;
There's no need to copy the strings.
(This may not be your actual problem, but it certainly prevents your program from being correct.)
execv() expects the program name to be prefixed by a full path as 1st parameter.
To have PATH searched instead of providing a path use execvp().
Update:
Also this line
returnString = malloc((sizeof(char)));
does only allocate 1 byte to returnString, which is way to few for how you use returnString.

getenv() Linux/Ubuntu returns NULL

I am trying to get a users home directory using getenv("$HOME"), but it is returning NULL. What am I doing wrong?
int main(void)
{
char * path;
path = getenv("$HOME");
printf ("The current path is: %s",path);
return 0;
}
Leave the $ off the environment variable name. When used in the shell the $ is not part of the name, but signals the shell that a variable name follows and it should substitute its value.
getenv("PATH"); // This is what you really want
And, optionally, compile with -Wall and end up with something like this. (Tested...)
#include <stdio.h>
#include <stdlib.h>
int main(void) {
char *path;
path = getenv("PATH");
if(path)
printf("The current path is: %s\n", path);
return 0;
}
Shouldn't that be getenv("PATH")?
For the home directory, you could use
char* homedir = getenv("HOME");
or you could use
char* homedir = NULL;
struct passwd *pw = getpwuid(getuid());
if (pw)
homedir = pw->pw_dir;
For the PATH used by execvp use getenv("PATH")
Since HOME is an environment variable you shouldn't prefix the $ sign to it.
char *value,name[20];
scanf("%s",name);
value=getenv(name);
if(value == NULL)
printf("Not found");
else
print("value = %s",value);
Ensure that you have included unistd.h and all concerned header files.

Resources