My goal is to try to get the name, size and last modified of a folder. I have used dirent.h to get the first two types. I don't know what the best way of getting the last modified value is, but the current way I am using does not seem very useful. I am getting type errors when trying to get my path as the dirent folder that I am opening. I'm not sure if there is an easier way to do this or but any guidance would be much appreciated. I found an example of how to get the last modified on stackoverflow and was trying to use that... below is my code:
#include <dirent.h>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <sys/stat.h>
#include <sys/types.h>
void getFileCreationTime(char *path) {
struct stat attr;
stat(path, &attr);
printf("Last modified time: %s", ctime(&attr.st_mtime));
}
int main()
{
DIR *folder;
struct dirent *entry;
int files = 0;
folder = opendir(".");
if(folder == NULL)
{
perror("Unable to read directory");
return(1);
}
while( (entry=readdir(folder)) )
{
files++;
printf("File %3d: %s\n",files, entry->d_name);
printf("File %3d: %i\n",files, entry->d_reclen);
getFileCreationTime(folder);
}
closedir(folder);
return(0);
}
With error:
main.cpp: In function ‘int main()’:
main.cpp:36:35: error: cannot convert ‘DIR* {aka __dirstream*}’ to ‘char*’ for argument ‘1’ to ‘void getFileCreationTime(char*)’
getFileCreationTime(folder);
Again, I am trying to be able to get the last modified date of each folder that I am getting the name and size of using dirent.
folder is the return value of opendir(".");. opendir returns either a DIR* structure or NULL. Not the directory path expressed using characters.
When you call getFileCreationTime(folder); you are sending a DIR* to your function. But you are expecting a char* path. So do not pass a DIR*, pass it the characters representing your directory. In your case "." (as used in your opendir() statement above).
Obviously, your directory name could be stored in a variable.
[...]
while( (entry=readdir(folder)) )
{
files++;
printf("File %3d: %s\n",files, entry->d_name);
printf("File %3d: %i\n",files, entry->d_reclen);
getFileCreationTime(".");
}
[...]
Using this, you will be able to read the time value. The name you already have.
For directory size, you will have to loop through the directory objects and add their size. You will have to decide what to do with sub-directories. Go down recursively or just take local objects? The source code of du would offer great insight (although it might be too complex for your need).
Related
This question already has answers here:
stat() error 'No such file or directory' when file name is returned by readdir()
(2 answers)
Closed 2 years ago.
I am writing a simple c program which outputs all the contents within a given directory. Files are printed in green, executables are printed in red, and directories are printed in blue. Files and executables have their size printed as well. The code works properly when I open the current directory with either a relative path (".") or an absolute path. However, when I open other directories, all contents are identified as directories and printed in blue. I am unsure why the behavior changes when opening different directories.
#include <stdio.h>
#include <stdlib.h>
#include <dirent.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <fcntl.h>
int main(int argc, char *argv[]){
DIR* dir;
struct dirent* sd;
dir = opendir(".");
if(dir == NULL){
exit(1);
}
int fd;
int size;
while((sd=readdir(dir)) != NULL){
struct stat statstruct;
stat(sd->d_name, &statstruct);
if(S_ISREG(statstruct.st_mode)){
fd = open(sd->d_name, O_RDONLY);
size = lseek(fd, 0, SEEK_END);
if(statstruct.st_mode & S_IXUSR){
printf("\033[1;31m%s %d bytes\n\033[0m", sd->d_name, size);
}
else{
printf("\033[0;32m%s %d bytes\n\033[0m", sd->d_name, size);
}
}
else if(S_ISDIR(statstruct.st_mode)){
printf("\033[0;34m./%s\n\033[0m", sd->d_name);
}
}
closedir(dir);
}
stat(sd->d_name, &statstruct);
stat opens relative to current directory. So the file will be parsed relative to current directory. Instead open the file relative to the directory you are interested in.
int err = fstatat(dirfd(dir), sd->d_name, &statstruct, 0);
if (err) abort(); // yes, handle errors
Basically, this should be a simple piece of code that opens a directory stream and looks for symbolic links. Whenever a symbolic link is found, it should print ("Symbolic link found");
However, the lstat(dirp->d_name,&bufcall always returns a value < 0, and I don't know why.
I created the two symbolic link opening the file folder, opening a terminal window inside the folder and running
ln -s ciao.txt link1 and
ln -s ciao2.txt link2
I know I should call closedir() later in my code, please don't care about this.
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <dirent.h>
#include <unistd.h>
void main (int argc, char* argv[])
{
char buffer[100],dir[100];
struct stat buf;
int x;
DIR *dp;
struct dirent *dirp;
if((dp=opendir(argv[1]))==NULL)
{
printf("\nError opening directory stream, now exiting...\n");
exit(-1);
}
while((dirp=readdir(dp))!=NULL)
{
lstat(dirp->d_name,&buf);
if(S_ISLNK(buf.st_mode))
printf("\n%s Is a symbolic link\n",dirp->d_name);
else
printf("\n%s Is not a symbolic link\n",dirp->d_name);
}
}
Some help would be appreciated. Thanks.
d_name is the file name in the directory, not a full path name. You must eather chdir into the directory you are looking at, or construct full path names for the files.
The simplest solution is to add this line just before your while loop:
chdir(argv[1]);
I am new to this forum, so please bear with me.
I am currently writing a program that will back up specific files and each time it is backed up a date and time stamp are added to the file name. At this stage I am writing a function that will identify all the files in the present working directory and will then read it into an array and will later on back up each of those files. The backing up works fine.
The problem I am having is that each time I attempt to pass a folder name to my function (as a string) I get a segmentation fault the debugger gives this error:
Program received signal SIGSEGV, Segmentation fault.
0x0000000000400b48 in backupAllFiles (
dirname=) at processing.c:52
52 {
The backupAllFiles() function is written inside the processing.c source file (it is a multi-source file project). Here is a snippet of the code that is the main concern:
#include <stdbool.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <sys/param.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <dirent.h>
#include <time.h>
#include <getopt.h>
void backupAllFiles(char *dirname)
{
int numfiles = 0;
char allFiles[MAXPATHLEN][MAXPATHLEN];
DIR *dirp;
struct dirent *dp;
dirp = opendir(dirname);
if(dirp == NULL)
{
fprintf(stderr, "Could not open: %s.\n",dirname);
exit(EXIT_FAILURE);
}
while((dp = readdir(dirp)) != NULL)
{
struct stat stat_buffer;
if(stat(allFiles[numfiles], &stat_buffer) != 0)
{
fprintf(stderr,"An error occurred while reading the directory: %s\n",dirname);
}
else if(S_ISREG(stat_buffer.st_mode))
{
sprintf(allFiles[numfiles], "%s%s", dirname, dp->d_name);
}
numfiles++;
}
for(int i = 0; i < numfiles; i++)
{
fprintf(stderr,"All the files are (%i) %s.\n",i ,allFiles[i]);
}
}
int main(int argc, char *argv[])
{
fprintf(stderr, "Entered main, passing argument %s to backupAllFiles()\n",argv[1]);
backupAllFiles(argv[1]);
return 0;
}
Please note this is not part of the main program yet, this is merely a separated segment which is used for individual testing.
I am really stumped as to why the segfault occurs and I have tried strdup for argv[1], however to no avail.
I appreciate all and any help. :)
P.S. There will be extra inclusions that are not necessary, this is for the rest of the program, I was too lazy to find which would exclusively apply to this function.
Just to recap some of the discussion into an answer: there are at least two problems with this code.
Declaring char allFiles[MAXPATHLEN][MAXPATHLEN] on the stack tries to allocate more memory than the system is willing to permit, which is why the segmentation fault is raised as soon as the code enters backupAllFiles.
The stat call in backupAllFiles accesses the allFiles[numfiles] element before initializing it. This should probably be
if(stat(dp->d_name, &stat_buffer) != 0)
rather than
if(stat(allFiles[numfiles], &stat_buffer) != 0)
Thanks to #wildplasser and #Carl Norum for identifying that the underlying problem here was, literally, stack overflow.
I'm trying to write a program to open an archive file from Unix, read the files in and print what files are inside the archive and just the filename. Below is my code -- it compiles, but I got some weird output in the terminal -- e.g. ?;?U?. It should just display 2 txt file names. Can someone take a look at my code and give me some guidance on what I'm missing? Thank you!
EDIT: I made some changes to my code, but it's still not working. Any suggestion or help is greatly appreciated.
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <errno.h>
#include <sys/utsname.h>
#include <ctype.h>
#include <string.h>
#include <ar.h>
int main (int argc, char **argv)
{
int input_fd;
//char buffer[25];
struct ar_hdr my_ar;
if (argc != 2) {
printf("Error", argv[0]);
exit(EXIT_FAILURE);
}
//open the archive file (e.g., hw.a)
input_fd = open(argv[1], O_RDONLY);
if (input_fd == -1)
{
perror("Can't open input file\n");
exit(-1);
}
while (read(input_fd, my_ar.ar_name, sizeof(buffer)) > 0) {
printf("%s\n", my_ar.ar_name);
}
close(input_fd);
return 0;
}
I don't see anywhere that you are defining what a my_hdr is, yet you are printing one of its supposed members here:
printf("%s\n", my_ar.ar_name);
You also never set ar_name to anything either.
I see you read some data into a variable named buffer, but you never actually copy that buffer into ar_name which I am assuming is your intent.
while (read(input_fd, buffer, sizeof(buffer)) > 0) {
printf("%s\n", my_ar.ar_name);
}
The POSIX documentation explicitly states that the archive file format is not described, on the grounds that several incompatible such formats already exist, so you'll definitely need to study the documentation provided by your OS if you're going to use <ar.h> instead of the ar command-line utility.
Here are some details that might help, based on the Solaris implementation:
An archive file starts with a magic cookie string which you are neither verifying nor skipping,
Each header object is followed by the content of the actual object in the archive, which you are not stepping over,
The first object in the archive is an unnamed object containing the archive's symbol table,
If an object's name is more than fifteen bytes long, it will be stored in the archive's string table, which appears to be part of the symbol table.
Note that these points, or details thereof, may vary on your OS. As I said at the beginning, study the documentation provided by your OS.
I'm trying to list files and their sizes in "first" folder but I am getting weird repetition of every two files.
#include <stdlib.h>
#include <stdio.h>
#include <dirent.h>
#include <sys/stat.h>
struct dirent *dirPtr;
struct stat st;
void main()
{
DIR * dirp;
if((dirp=opendir("first"))==NULL)
{
printf("There has been an error");
}
while(dirPtr=readdir(dirp))
{
printf("%s - ", dirPtr->d_name);
stat(dirPtr->d_name, &st);
printf(" file size: %lu\n", st.st_size);
}
closedir(dirp);
}
This is my output:
Does anyone know why this is happening?
For starters, you should probably check the return result from "stat()", and print an error # (instead of file size) if stat() fails.
Also: "opendir()" is reading files from some arbitrary directory.
Q: Does "dirPtr->d_name" contain a full filepath ... or just a filename? In other words, are you trying to "stat()" from the wrong directory?
You are basically calling stat("test.c", &st). How is stat supposed to know what directory test.c is in? I don't think it can.
I think you should try doing stat("./first/test.c", &st) or stat("/full/path/to/first/test.c", &st). Better yet, you should find some function like stat that takes a struct dirent as the argument, so you don't have to worry about concatenating strings.
I'll see if there is a such a function and edit my answer if I find one.
Every directory has at least two entries, the current directory - a single dot . - and the parent directory - two dots ..