I have drafted a code snippet that is to emulate the operation of ls -all in a custom shell named My$HELL
The main shell process invokes this code(by calling its executable through execlp).
Following is the code of the executable myls which is to do the work:-
myls.c
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <dirent.h>
#include <sys/stat.h>
#include <time.h>
void search_dir(const char * arg);
void main(int argc, char *argv[])
{
int i;
if (argc==1)
search_dir(".");
for(i=1;i<argc;i++)
search_dir(argv[i]);
}
void search_dir(const char *arg)//Function to read directories and file attributes//
{
DIR *dirp;
struct dirent *dp;//Dirent structure used to read and store directory attributes//
char file_name[256];
char time[50]={"\0"};
struct tm *timeinfo;
struct stat prop_file;//stat function for accessing file attributes//
char type;
if((dirp=opendir(arg))==NULL)
{
perror("opendir");
return;
}
printf("\n\nDirectory \tTime last Modified\tSize\t\t Name\n");
while((dp=readdir(dirp))!=NULL) // Navigates the directory structure
{
if ( stat(dp->d_name,&prop_file)!=0) //Reading file attributes//
{
printf("\n%s:Error in reading the file attributes", dp->d_name );
continue;
}
if ( dp->d_type==8 )
{
type = '-';
}
else
{
type = 'd';
}
timeinfo=localtime(&(prop_file.st_mtime));
strftime(time,20,"%b %d %H:%M", timeinfo);
printf("\n %c\t\t %s\t\t%d\t\t %s",type,time,(int)prop_file.st_size,dp->d_name); //Printing ile attributes//
}
printf("\n");
}
Irrespective of the contents in the directory, the process displays certain fields after which the calling process terminates with a segmentation fault.
A GDB run is also of a little help (for being vague) and search on the error yields little result. Following is the debugged output:-
[~pbox/working/trial]<My$HELL>myls
Executing myls
Directory Time last Modified Size Name
d Aug 14 19:22 4096 ..
d Aug 14 18:42 4096 .
[~pbox/working/trial]<My$HELL>
Program received signal SIGSEGV, Segmentation fault.
strlen () at ../sysdeps/x86_64/strlen.S:106
106 ../sysdeps/x86_64/strlen.S: No such file or directory.
(gdb) Quit
From what I could understand such error are results of illegal variable/pointer assignments. Any help in pointing out the bug is highly appreciated.
Am also appending code segment of the main process from where myls is being called
main.c
.
.
else if(strcmp(command[0],"myls")==0) //command of type char ** stores the user input command check if the first field is 'myls'//
{
printf("Executing myls\n");
strcat(path,"/myls"); //path stores the directory path
result=execvp(path,command); //result of type int
exit(0);
}
.
.
Cheers and thanks in anticipation!!
the following code :
1) cleanly compiles
2) handles errors in an appropriate manner
3) does the job correctly
4) does not follow symbolic links
5) does not display the proper file type for every file
6) when accessing directories that are (in any way) protected
from casual reading, will output an error message
7) properly builds the path+filename before calling stat()
8) properly declares main() function and proper return
9) does not handle any options that are passed in.
10)does not seg fault
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <dirent.h>
#include <sys/stat.h>
#include <time.h>
#include <string.h>
void search_dir(const char * arg);
int main(int argc, char *argv[])
{
int i;
char dirBuf[128] = {'\0'};
char * newline = NULL;
if (argc==1)
search_dir(".");
else
{
for(i=1;i<argc;i++)
{
newline = strcpy( dirBuf, argv[i] );
if( strstr( dirBuf, "\n") )
{
*newline = '\0';
}
search_dir(dirBuf);
}
}
return 0;
}
void search_dir(const char *arg)//Function to read directories and file attributes//
{
DIR *dirp;
struct dirent *dp;//Dirent structure used to read and store directory attributes//
char fileName[256];
char fileTime[50]={"\0"};
struct tm *timeinfo;
struct stat prop_file;//stat function for accessing file attributes//
char type;
printf( "%s\n", arg);
if( NULL == (dirp=opendir(arg)) )
{
perror("opendir failed");
return;
}
// implied else, opendir successful
printf("\n\nDirectory \tTime last Modified\tSize\t\t Name\n");
while( NULL != (dp=readdir(dirp)) ) // gets next entry in current directory,
{
strcpy(fileName, arg);
strcat(fileName, "/");
strcat(fileName, dp->d_name);
printf( "\nfileName: %s", fileName);
if ( stat(fileName,&prop_file) ) //Reading file attributes//
{
perror( "stat failed" );
printf("\n%s:Error in reading the file attributes", dp->d_name );
continue;
}
#ifdef _DIRENT_HAVE_D_OFF
// following if/else needs expansion
if ( dp->d_type==8 )
{
type = '-';
}
else
{
type = 'd';
}
#else
type = '?';
#endif
timeinfo=localtime(&(prop_file.st_mtime));
strftime(fileTime, 49, "%b %d %H:%M", timeinfo);
printf("\n %c\t\t %s\t\t%d\t\t %s",
type,
fileTime,
(int)prop_file.st_size,
dp->d_name); //Printing file attributes//
}
printf("\n");
closedir( dirp );
}
Related
I tried to write a program in C that checks if a certain file is executable or not, if it is a shell script or a binary
<apue.h> is a header from the book Advanced Programming in the UNIX Environment
I don't think the approach is exactly right (if it's a shell script or binary). I think there is a more efficient solution. Which one do you think?
In addition, what other problems does the code have?
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <apue.h>
void checkIfFileExists(const char *fileName);
int main (int argc,char *argv[])
{
char *fileName = argv[1];
if (argc < 2 )
{
err_quit("File to check not specified\n");
return 0;
}
if (argc > 2 )
{
err_quit("Too many arguments\n");
return 0;
}
checkIfFileExists(fileName);
return 0;
}
void checkIfFileExists(const char *fileName)
{
if(!access( fileName, F_OK ))
{
if(!access( fileName, X_OK ))
{
printf("The file %s is an executable\n",fileName);
//check if the file is binary or shell script
}
else
{
printf("The file %s is not an executable\n",fileName);
}
}
else
{
err_quit("The file %s was not found\n",fileName);
}
}
You can use the stat methods,
#include <sys/stat.h>
...
struct stat myf;
stat(<file>,&mf);
if( mf.st_mode & S_IXUSR )
printf("execute");
The output is a bitmap from which you can extract the file permission info
look at https://www.gnu.org/software/libc/manual/html_node/Permission-Bits.html
for the permission define available.
I was wondering if someone could tell me what I'm doing wrong. This code is supposed to walk though all the directories and files and print them out exactly the same way the UNIX utility FIND does. But for some reason I cant get chdir to change the working directory. I'm trying to limit the number of file descriptors im using.
MAIN
#include <stdio.h>
#include "sfind.h"
#include <unistd.h>
#include <dirent.h>
int main(int argv, char *argc[]){
char cwd[1024]; /* current working directory limit*/
char *path = NULL;
DIR *dp = NULL;
if (getcwd(cwd, sizeof(cwd)) != NULL){ /*allow us to grab the current working directory*/
fprintf(stdout, "Current working dir: %s\n", cwd);
}
else{
perror("getcwd() error");
}
dp = opendir(cwd);
path = ".";
directoryList(dp,path);
return 0;
}
Directory Method Definition
#include <stdio.h>
#include <stdlib.h>
#include "sfind.h"
#include <unistd.h>
#include <limits.h>
#include <dirent.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
void directoryList(DIR *dp, char *path){
char newPath[PATH_MAX] = {0};/*To store new path*/
struct dirent *element; /*get file name*/
struct stat statbuf;/*determine type of file*/
int status = 0; /*My base case should be once the directory I'm in runs outs out of files I should return;*/
if(dp == NULL){
fprintf(stderr,"FILE DID NOT OPEN!");
exit(-1);
}
/*change the current file directory even if its the first one*/
if((status = chdir(path)) == -1){
printf("ERROOR!");
}/*change the current working directory whether that the same on or not*/
while((element = readdir(dp)) != NULL) /*while current file directory pointer is not equal to zero*/{
/* from here we only have two cases once were reading from the directory either is a file or directory!*/
/*using lstat*/
lstat(element->d_name,&statbuf);
if((S_ISDIR(statbuf.st_mode))) /*is of type directory*/{
if((strcmp(".",element->d_name) == 0) || (strcmp("..",element->d_name) == 0))
continue;
/*create new directory name*/
newPath[0] = '\0';
strcat(newPath,path);/* this will give us the "."*/
strcat(newPath,"/");
strcat(newPath,element->d_name);
printf("%s\n", newPath);
directoryList(dp,newPath); /*recursion*/
file*/
}
else /*Its a file!*/{
printf("%s/%s\n",path,element->d_name);
}
}
}
The issue seems to be with the call to readdir(dp)...
Even though you change the current working directory, you don't update the dp pointer to open the new folder.
Here's a poor-man's working example (I wouldn't do it this way, but it works for small trees).
#include <dirent.h>
#include <limits.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
void directoryList(DIR *dp, char *path) {
char newPath[PATH_MAX] = {0}; /*To store new path*/
struct dirent *element; /*get file name*/
struct stat statbuf; /*determine type of file*/
int status = 0; /*My base case should be once the directory I'm in runs outs
out of files I should return;*/
DIR *dp_tmp;
if (dp == NULL) {
fprintf(stderr, "FILE DID NOT OPEN!");
exit(-1);
}
while ((element = readdir(dp)) !=
NULL) /*while current file directory pointer is not equal to zero*/ {
/* from here we only have two cases once were reading from the directory
* either is a file or directory!*/
/*using lstat*/
lstat(element->d_name, &statbuf);
if ((S_ISDIR(statbuf.st_mode))) /*is of type directory*/ {
if ((strcmp(".", element->d_name) == 0) ||
(strcmp("..", element->d_name) == 0))
continue;
/*create new directory name*/
newPath[0] = '\0';
strcat(newPath, path); /* this will give us the "."*/
strcat(newPath, "/");
strcat(newPath, element->d_name);
printf("%s\n", newPath);
if ((dp_tmp = opendir(newPath)) == NULL) {
perror("hmm?! ");
exit(1);
}
directoryList(dp_tmp, newPath); /*recursion*/
} else /*Its a file!*/ {
printf("* %s/%s\n", path, element->d_name);
}
}
closedir(dp);
}
int main(void) {
char cwd[1024]; /* current working directory limit*/
char *path = NULL;
DIR *dp = NULL;
if (getcwd(cwd, sizeof(cwd)) !=
NULL) { /*allow us to grab the current working directory*/
fprintf(stdout, "Current working dir: %s\n", cwd);
} else {
perror("getcwd() error");
}
dp = opendir(cwd);
path = ".";
directoryList(dp, path);
return 0;
}
EDIT:
To answer the question in the comment...
Open directories (that should be closed using closedir) are different (and quite unrelated) to the current working directory.
The current working directory is mostly used to resolve the path to any file/folder you're referencing.
Open directory pointers (DIR *) are just pointers to data in the memory. That data relates to a specific directory and you can open a number of directories at the same time.
EDIT2:
A few people in the comments recommended nftw (file tree walk) which is a great alternative to doing it yourself.
If this isn't a learning project, I would recommend it's use.
However, note that POSIX.1-2008 marks ftw as obsolete, so make sure to use the nftw flavor.
Is your goal to learn to implement this yourself, or do you just want results? Because you should take a look at fts.h if you want some very powerful stuff to implement something like find.
I have for homework to write a C program, which is acting like the Linux "ls -al" command. I know that there are a lot of example programs over the internet, which are doing the thing that I need, but I have a specific problem, to which I can't find a solution. I also want to mention, that I am new to C programming. Here is my code :
#include <stdio.h>
#include <stdlib.h>
#include <dirent.h>
#include <errno.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <time.h>
#include <pwd.h>
int list_dir(const char *dirname) {
struct dirent* current_directory;
struct stat my_stat;
struct tm lt;
struct passwd *pwd; // For User-ID
DIR* directory = opendir(dirname);
if(directory == NULL) {
printf("list_dir : %s : %s \n", dirname, strerror(errno));
return 0;
}
printf("Directory : %s\n", dirname);
printf("\n");
while( (current_directory = readdir(directory) ) ) {
stat(current_directory->d_name, &my_stat);
if ( (stat(current_directory->d_name, &my_stat) ) == 0 ) {
pwd = getpwuid(my_stat.st_uid); // Get User-ID
}
// Last Modified
time_t t = my_stat.st_mtime;
localtime_r(&t, <);
char timebuf[80];
strftime(timebuf, sizeof(timebuf), "%c", <);
if (pwd != 0) {
printf("%s \t %ld \t %s \t %s", pwd->pw_name, (long)my_stat.st_size, timebuf, current_directory->d_name);
printf("\n");
} else {
printf("%d \t %ld \t %s \t %s", my_stat.st_uid, (long)my_stat.st_size, timebuf, current_directory->d_name);
printf("\n");
}
}
closedir(directory);
return 0;
}
int main(int argc, char* argv[]) {
if ( argc == 1 ) {
return list_dir ( "." );
} else {
int ret = 0;
for (int i = 1; i < argc; i += 1 ) {
if ( list_dir ( argv[i] ) != 0 ) {
ret = 1;
}
}
return ret;
}
}
The program has to display the same things (without the permissions) as "ls -al". So far so good, if I compile it with "gcc -std=gnu99 -o list_dir list_dir.c" and execute the program with "./list_dir" I am getting the same result as "ls -al" and it looks like this :
username 1599 Fri May 1 20:43:57 2015 list_dir.c
However if I run the program with something like : "./list_dir /home/username/Downloads/" I am getting this :
32727 0 Sun May 8 07:09:04 4461391 selection_sort.c
As you can see, the program can't get the right information about the Username, the size of the file and the year. Also this information is appearing thanks to the else case of the if(pwd != 0) statement. If I don't have the else case, the program is printing out only the files, for which it can get the correct information. Also if I remove this if statement and also the if statement :
if ( (stat(current_directory->d_name, &my_stat) ) == 0 )
I am getting a segmentation fault.
So my questions are :
1.What am I doing wrong. I know that I am doing something wrong, because as a hint for the homework I have an example run of the program and also a hint that I can use "stat, lstat, readlink, getpwnam, getpwuid, strftime".
2.Is there any way to get the username with stat() and with the User-ID only, or it's only possible with getpwuid?
Here,
if ( (stat(current_directory->d_name, &my_stat) ) == 0 ) {
pwd = getpwuid(my_stat.st_uid); // Get User-ID
}
What happens if stat() fails? pwd would have uninitialized value or incorrect value set from previous iteration.
The reason why stat would fail, current_directory->d_name contains only the file name whereas stat expects a fullpath. So you need to prepend the directory name to the file and pass it to stat(). You are only passing the filename right now.
Something like:
char buf[1024];
errno = 0;
snprintf(buf, sizeof buf, "%s/%s", dirname, current_directory->d_name);
if ( (stat(buf, &my_stat) ) == 0 ) {
...
}
else {
printf("%s: %s\n", buf, strerror(errno));
// exit here or continue to the next entry in the directory
//depending how you wish to handle failure
}
and handle the error in case stat() fails.
You also have one other stat() inside the loop which doesn't do anything.
i am currently trying to delete directories from system call using c and i am facing a weird problem. In my deleteFunction() after using the char * path to open the directory. the value of path changes
here is part of the code:
#include <dirent.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
void deleteDir(char *path){
//opening the directory
printf("BEFORE %s\n",path );
DIR *p = opendir(path);
if (p == NULL){
printf("Directory not Opened!\n");
exit(2);
}
printf("AFTER %s\n",path );
}
void main(int argc,char *argv[]){
if (argc != 2){
printf("Not enough Arguments!\n");
exit(1);
}
//creating the path
char * currentDir = getcwd(NULL,0);
strcat(currentDir,"/");
strcat(currentDir,argv[1]);
//deleting the directory
deleteDir(currentDir);
exit(0);
}
the output produced is:
BEFORE /home/tarounen/test/newfolder
AFTER /home/tarounen/test/!�
note: i am only taking the directory name as parameter
The getcwd function is probably allocating just enough space to hold the current path, so adding more characters with strcat overflows the buffer, and results in undefined behavior. Try this
char path[MAXPATHLEN];
getcwd( path, MAXPATHLEN );
strcat( path, "/" );
strcat( path, argv[1] );
getcwd only allocates enough memory to hold the directory when you pass NULL.
Concatenating to its result has undefined behaviour.
If you want to use strcat, you need to provide your own buffer with enough space:
char buffer[MAXPATHLEN] = {0};
if (getcwd(buffer, sizeof(buffer)))
{
strcat(buffer, "/");
strcat(buffer, argv[1]);
deleteDir(buffer);
}
I ran a simulation for some data y1, y2,..yn and generate vectors w, mu. At each simulation these results are stored into a file, let us say (normally w and mu are very long vectors 10,000 entries)
/home/carlos/Documents/Results/w.txt
/home/carlos/Documents/Results/mu.txt
But if I want to run my algorithm with other data set, and do not want to lose the previous results, I have to go directly into my C code and change (or move the w.txt, mu.txt to other file)
/home/carlos/Documents/Results/OtherData/w.txt
/home/carlos/Documents/Results/OtherData/mu.txt
I do not want to go every time into my C code to change the address(or move again and again w.txt, mu.txt), I would like to just create a new folder with a name: OtherData and store the data there just giving the address
/home/carlos/Documents/Results/OtherData/
as an input for the code
I did a very simplified example but it does not work, could somebody give me a hand?
#include <stdio.h>
#include <string.h>
void main(char *dir){
char dir_happy[100] = *dir, dir_sad[100]=*dir;
FILE *ffile_happy, *ffile_sad;
strcat(dir_happy, "/happy.txt");
strcat(dir_sad, "/sad.txt");
ffile_happy = fopen("dir_happy.txt", "w");
ffile_sad = fopen("dir_sad.txt", "w");
fprintf(ffile_happy, "Hello!, happy world\n");
fprintf(ffile_sad, "Hello!, sad world\n");
fclose(ffile_happy);
fclose(ffile_sad);
}
You have the arguments to main() wrong. The proper prototype is:
int main(int argc, char *argv[]);
Where argc is the number of arguments given, and argv is a vector holding each argument. The first argument (in argv[0]) is generally the program's name.
Untested. Have fun.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAX_FILENAME_LENGTH 100
#define DEFAULT_DIR "."
#define HAPPY_NAME "/happy.txt"
#define SAD_NAME "/sad.txt"
int main(int argc, char **argv) {
char name1[MAX_FILENAME_LENGTH+1], name2[MAX_FILENAME_LENGTH+1];
size_t dirlen;
char *dir = DEFAULT_DIR;
FILE *ffile_happy, *ffile_sad;
if (argc == 2) {
dir = argv[1];
}
dirlen = strlen(dir);
if (len + strlen(HAPPY_NAME) > MAX_FILENAME_LENGTH) {
fprintf(stderr, "Directory name too long. Program aborted.\n");
exit(EXIT_FAILURE);
}
if (len + strlen(SAD_NAME) > MAX_FILENAME_LENGTH) {
fprintf(stderr, "Directory name too long. Program aborted.\n");
exit(EXIT_FAILURE);
}
strcpy(name1, dir); strcat(name1, HAPPY_NAME);
strcpy(name2, dir); strcat(name2, SAD_NAME);
ffile_happy = fopen(name1, "w");
if (ffile_happy == NULL) {
fprintf(stderr, "Unable to open file \"%s\" for writing. Program aborted.\n", name1);
exit(EXIT_FAILURE);
}
ffile_sad = fopen(name2, "w");
if (ffile_sad == NULL) {
fprintf(stderr, "Unable to open file \"%s\" for writing. Program aborted.\n", name2);
fclose(ffile_happy);
exit(EXIT_FAILURE);
}
/* use files */
fclose(ffile_happy);
fclose(ffile_sad);
return 0;
}
void main(char *dir) is the problem.
Main takes 2 args int/void main(int argc, char *argv[])
argc is the number of arguments to the executable.
argv[0] is the filename of the executable.
argv[1..n] are the arguments passed (normally space separated, with quotes allowed)
So /a.out Hello "Look at me" would parse as
argv[0] => './a.out'
argv[1] => 'Hello'
argv[2] => 'Look at me'