Im trying to create program for building archives using libzip and C. Function below must create archive from files. Name of directory with files and name of output file passed as a command line arguments. Code works without any errors. The main problem that program does not create a .zip file, and I don`t understend why.
#include <zip.h>
#include <stdio.h>
#include <stdlib.h>
#include <dirent.h>
#include <sys/stat.h>
#include <stdbool.h>
#include <string.h>
static bool is_modified (char *filename);
static void create_zip (char *source_name, char *output_filename, char *dir_name)
{
int err = 0;
DIR *dir = opendir(dir_name);//open directory
struct dirent *entry;
if(!dir){
fprintf(stderr, "Cannot read directory\n");
exit(1);
}
fprintf(stdout, "DIR - [%s]\n", dir_name);
zip_t *archive_zip = zip_open(output_filename, ZIP_CREATE, &err);//create zip
if(archive_zip == NULL)
{
zip_error_t ziperr;
zip_error_init_with_code(&ziperr, err);
fprintf(stderr, "Failed to create archive, error -[%i]\n", err);
zip_close(archive_zip);
exit(1);
}else
printf("Archive [%s] created\n", output_filename);
while((entry = readdir(dir)) != NULL)// walking through directory
{
if(strcmp(entry->d_name, ".") != 0 && strcmp(entry->d_name, "..") != 0 && strcmp(entry->d_name, source_name) !=0)
{
zip_source_t *source = zip_source_file(archive_zip, entry->d_name, 0, 0);//creating source for every file in dir
if(source == NULL)
{
fprintf(stderr, "Cant create source file [%s]\n", entry->d_name);
}
if(zip_file_add(archive_zip, entry->d_name, source, ZIP_FL_OVERWRITE) < 0)// adding it to archive with overwrite flag
{
zip_source_free(source);
fprintf(stderr, "Cant add file [%s]\n", entry->d_name);
}else
fprintf(stderr, "File [%s] added\n", entry->d_name);
}
}
zip_close(archive_zip);
closedir(dir);
}
int main(int argc, char **argv)
{
if (argc != 3) {
fprintf(stderr, "Usage: %s <output_file> <input_dir_name>\n", argv[0]);
return 1;
}
create_zip(argv[0],argv[1], argv[2]);
return 0;
}
Output looks like
DIR - [test_dir]
Archive [test] created
File [f1.txt] added
File [f2.txt] added
Thank you for help).
Related
WHAT I HAVE TO DO
I have to find, starting from a directory, a file located in one of all directories that have as root the directory passed in input.
Something as shell command find.
INPUT/OUTPUT
Having this in input:
./myfind /home/claudio/Scrivania file.txt
I need something as this in output, absolute path and last modify date ecc:
/home/claudio/Scrivania/SistemiOperativi/file.txt Tue Mar 30 19:51:54 2021
MY CODE
It doesn't print anything.
#include <stdio.h>
#include <sys/types.h>
#include <dirent.h>
#include <sys/stat.h>
#include <unistd.h>
#include <string.h>
#include <time.h>
#include <stdlib.h>
#include <errno.h>
#if !defined(NAME_MAX)
#define NAME_MAX 256
#endif
void find(const char* passed_dir_name, const char* passed_file_name) {
if (chdir(passed_dir_name) == -1) {
perror("FATAL ERROR CHANGING DIRECTORY");
exit(EXIT_FAILURE);
}
DIR* current_directory;
if ((current_directory = opendir(".")) == NULL) {
perror("FATAL ERROR OPENING CURRENT WORKING DIRECTORY");
exit(EXIT_FAILURE);
}
char* buf;
if ((buf = calloc(NAME_MAX, sizeof(char))) == NULL) {
perror("FATAL ERROR ALLOCATING MEMORY");
exit(EXIT_FAILURE);
}
struct dirent* dir;
while ((dir = readdir(current_directory)) != NULL) {
struct stat statbuf;
stat(dir->d_name, &statbuf);
if (S_ISDIR(statbuf.st_mode)) {
if (strncmp(dir->d_name, ".", 1) != 0 && strncmp(dir->d_name, "..", 2) != 0) {
find(dir->d_name, passed_file_name);
}
} else {
if (strncmp(dir->d_name, passed_file_name, strlen(passed_file_name) == 0)) {
if (getcwd(buf, NAME_MAX) == NULL) {
perror("FATAL ERROR");
exit(EXIT_FAILURE);
}
fprintf(stdout, "%s/%s %s", buf, dir->d_name, ctime(&statbuf.st_mtime));
}
}
}
if (closedir(current_directory) == -1) {
perror("FATAL ERROR CLOSING CURRENT WORKING DIRECTORY");
exit(EXIT_FAILURE);
}
chdir("..");
free(buf);
}
int main(int argc, char** argv) {
if (argc != 3) {
fprintf(stderr, "ERROR: RUn as ./myfind directory\n");
exit(EXIT_FAILURE);
}
const char* dir = argv[1];
const char* file = argv[2];
struct stat statbuf;
stat(dir, &statbuf);
if(!S_ISDIR(statbuf.st_mode)) {
fprintf(stderr, "FATAL ERROR: %s IS NOT A DIRECTORY\n", dir);
exit(EXIT_FAILURE);
}
find(dir, file);
exit(EXIT_SUCCESS);
}
Your parentheses are wrong in:
if (strncmp(dir->d_name, passed_file_name, strlen(passed_file_name) == 0))
You need to write:
if (strncmp(dir->d_name, passed_file_name, strlen(passed_file_name)) == 0)
Since strncmp(x, y, 0) will always return 0, the condition is never being met.
But note that there's no point in using strncmp here at all. strncmp is only needed if you don't know that one of your entries is a null terminated string. You have a guarante that d_name is null-terminated, and if passed_file_name is not, then strlen is going to be problematic. You might as well just write strcmp(dir->d_name, passed_file_name).
EXAMPLE
Imagine I have a directory called Alpha and I want it as root.
Alpha contains: some files and other two directories Beta and Gamma,
Beta contains: some files and another directory called Theta,
Gamma contains: some files,
Theta contains: some files.
INPUT/OUTPUT
Using input as: ./myfind Alpha
I'll want as output:
Alpha
Beta
Gamma
Theta
(I don't care about the order).
MY CODE
I tried with this code but it doesn't work. I'll want to do a recursive function to do it and i can't use POSIX.
#include <stdio.h>
#include <sys/types.h>
#include <dirent.h>
#include <sys/stat.h>
#include <unistd.h>
#include <string.h>
#include <time.h>
#include <stdlib.h>
#include <errno.h>
#if !defined(NAME_MAX)
#define NAME_MAX 256
#endif
int find(const char *passed_dir_name) {
if (chdir(passed_dir_name) == -1) {
perror("FATAL ERROR CHANGING DIRECTORY");
return -1;
}
DIR *current_directory;
if ((current_directory = opendir(".")) == NULL) {
perror("FATAL ERROR OPENING CURRENT WORKING DIRECTORY");
return -1;
}
struct dirent *dir;
while ((dir = readdir(current_directory)) != NULL) {
struct stat statbuf;
stat(dir->d_name, &statbuf);
if (S_ISDIR(statbuf.st_mode)) {
fprintf(stdout, "%s\n", dir->d_name);
find(dir->d_name);
}
}
if (closedir(current_directory) == -1) {
perror("FATAL ERROR CLOSING CURRENT WORKING DIRECTORY");
exit(EXIT_FAILURE);
}
}
int main(int argc, char **argv) {
if (argc != 2) {
fprintf(stderr, "ERROR: Run as ./myfind directory\n");
exit(EXIT_FAILURE);
}
const char *dir = argv[1];
struct stat statbuf;
stat(dir, &statbuf);
if (!S_ISDIR(statbuf.st_mode)) {
fprintf(stderr, "FATAL ERROR: %s IS NOT A DIRECTORY\n", dir);
exit(EXIT_FAILURE);
}
find(dir);
exit(EXIT_SUCCESS);
}
The problem is you change the current directory when you recurse into a subdirectory but you do not change back to the parent directory when returning from the recursive function.
You could add a chdir(".."); at the end of the find function, but it might not work in all cases:
if a directory has more than 2 hard links
if you traverse symbolic links
It is preferable to compute the path of the destination directory for the recursive call to find() by concatenating the passed_dir_name, a / and dir->d_name and avoid changing the current directory.
Here is a modified version of find() for the simplistic approach:
int find(const char *passed_dir_name) {
if (chdir(passed_dir_name) == -1) {
perror("FATAL ERROR CHANGING DIRECTORY");
return -1;
}
DIR *current_directory;
if ((current_directory = opendir(".")) == NULL) {
perror("FATAL ERROR OPENING CURRENT WORKING DIRECTORY");
chdir("..");
return -1;
}
struct dirent *dir;
while ((dir = readdir(current_directory)) != NULL) {
struct stat statbuf;
stat(dir->d_name, &statbuf);
if (S_ISDIR(statbuf.st_mode)) {
fprintf(stdout, "%s\n", dir->d_name);
find(dir->d_name);
}
}
if (closedir(current_directory) == -1) {
perror("FATAL ERROR CLOSING CURRENT WORKING DIRECTORY");
exit(EXIT_FAILURE);
}
chdir("..");
}
I want to read all files that I can find in the folder where my executable is, except the runnable file that I'm running. I code the following code but, although this list correctly the files that I have in my folder, I cannot open them with fopen because fopen prints that the file doesn't exists. If I do gedit "path of the file obtained from my program in c" then it opens perfectly from the term. Where is the bug?
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <dirent.h>
#include <unistd.h>
int main (int argc, char **argv) {
//Determining the number of files we have.
//We call to a bash command http://stackoverflow.com/questions/646241/c-run-a-system-command-and-get-output
FILE *fp, *fin;
char path[1035], cwd[1024];
int scanned = 0;
/* Open the command for reading. */
//https://askubuntu.com/questions/370697/how-to-count-number-of-files-in-a-directory-but-not-recursively
//This count soft and hard links also (I think)
fp = popen("ls -F |grep -v /", "r");
if (fp == NULL) {
printf("Failed to run command\n" );
exit(1);
}
/* Read the output a line at a time - output it. */
//Loop for each file. Be careful! if the exe is inside, it will also be counted!
while (fgets(path, sizeof(path)-1, fp) != NULL) {
printf("Reading file: %s\n", path);
fin=fopen(path,"r");
scanned = 0;
printf("caa");
if (fin != NULL){
printf("AA\n");
fclose(fin);
}
if (!fin)perror("fopen");
printf("Done! \n");
}
/* close */
pclose(fp);
printf("end");
return 0;
}
There are 2 bugs in your code:
when the code updates the "path" variable in your code. It has a newline at the end which needs to be corrected to NUL. This gives an incorrect path.
Something like below can be appended to your code:
while (fgets(path, sizeof(path)-1, fp) != NULL) {
len=strlen(path);
path[len-1]='\0';
Use 'ls -A1', since 'ls -F' adds a '*' in binary name:
fp = popen("ls -A1 |grep -v /", "r");
ok so just in case someone else needs a better approach, I redid the code with the comments I had. Here I let you the new code.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <dirent.h>
#include <sys/types.h>
#include <sys/stat.h>
int isDirectory(const char *path) {
struct stat statbuf;
if (stat(path, &statbuf) != 0)
return 0;
return S_ISDIR(statbuf.st_mode);
}
int main (int argc, char **argv) {
FILE *fp, *fin;
char path[1035], cwd[1024];
int scanned = 0;
int ints;
DIR *dir;
struct dirent *ent;
//getcwd prints directory where the app ran.
if ((dir = opendir (getcwd(cwd, sizeof(cwd)))) != NULL) {
/* print all the files and directories within directory */
while ((ent = readdir (dir)) != NULL) {
/*Skips . and ..*/
if (strcmp(ent->d_name, ".") == 0 || strcmp(ent->d_name, "..") == 0) continue;
if (isDirectory(ent->d_name) != 0) continue;
printf ("Reading file: %s\n", ent->d_name);
scanned = 0;
fin=fopen(ent->d_name,"r");
if (fin != NULL){
while ((scanned = fscanf(fin, "%d", ints)) != EOF) {
if(scanned == 1){
printf("%d\n", ints);
}else {
printf("Whoops! Input format is incorrect!\n");
break;
}
} //LOOP: reading file
fclose(fin);
}
if (!fin)perror("fopen");
printf("Done! \n");
}//LOOP: while opendir
closedir (dir);
} else {
/* could not open directory */
perror ("opendir");
return EXIT_FAILURE;
}
return 0;
}
I'm stuck on unix system programming and am beginner about it. Assume that there is a directory which is X. There are a file(text1.txt) and another directory which is Y in X. Last, there are two files(text2.noExtension) and text3.noExtension) and another directory which is Z in Y. My goal is that read files and enter directories until there is no directory. Candidly, I really don't have any idea how to go on.
#include <dirent.h>
#include <errno.h>
#include <stdio.h>
int main(int argc, char *argv[]) {
struct dirent *direntp;
DIR *dirp;
if (argc != 2) {
fprintf(stderr, "Usage: %s directory_name\n", argv[0]);
return 1; }
if ((dirp = opendir(argv[1])) == NULL)
{
perror ("Failed to open directory");
return 1;
}
while ((direntp = readdir(dirp)) != NULL)
printf("%s\n", direntp->d_name);
while ((closedir(dirp) == -1) && (errno == EINTR)) ;
return 0;
}
If you want to list all directories and sub-directories, try something recursive. E.g.:
#include <dirent.h>
#include <errno.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
void listOfDir(char * dirname, int level)
{
struct dirent *direntp;
DIR *dirp;
char *subdirname;
if ((dirp = opendir(dirname)) == NULL)
{
return;
}
while ((direntp = readdir(dirp)) != NULL)
{
if(strcmp(direntp->d_name, ".")==0 || strcmp(direntp->d_name, "..")==0)
continue; // skip current and parent directories
printf("%*c%s\n", level, '>', direntp->d_name);
if( direntp->d_type == 4)
{
// build child dir name and call listOfDir
subdirname = (char*)malloc(strlen(direntp->d_name) + strlen(dirname) + 2);
strcpy(subdirname, dirname);
strcat(subdirname, "/");
strcat(subdirname, direntp->d_name);
listOfDir(subdirname, level+1);
free(subdirname);
}
}
closedir(dirp);
}
int main(int argc, char *argv[]) {
struct dirent *direntp;
DIR *dirp;
if (argc != 2) {
fprintf(stderr, "Usage: %s directory_name\n", argv[0]);
return 1;
}
listOfDir(argv[1], 1);
return 0;
}
Expression like printf("%*c", level, '>') just make indents for elach level of nesting
I am trying to simulate linux command ls using linux api from c. Looking at the code it does make sense, but when I run it I get "stat error: No such file or directory". I have checked that opendir is working ok. I think the problem is in stat, which is returning -1 even though I think it should return 0.
What am I missing?
Thanks for your help.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <dirent.h>
#include <sys/stat.h>
#include <errno.h>
int main(int argc, char *argv[])
{
DIR *dirp;
struct dirent *direntp;
struct stat stat_buf;
char *str;
if (argc != 2)
{
fprintf( stderr, "Usage: %s dir_name\n", argv[0]);
exit(1);
}
if ((dirp = opendir( argv[1])) == NULL)
{
perror(argv[1]);
exit(2);
}
while ((direntp = readdir( dirp)) != NULL)
{
if (stat(direntp->d_name, &stat_buf)==-1)
{
perror("stat ERROR");
exit(3);
}
if (S_ISREG(stat_buf.st_mode)) str = "regular";
else if (S_ISDIR(stat_buf.st_mode)) str = "directory";
else str = "other";
printf("%-25s - %s\n", direntp->d_name, str);
}
closedir(dirp);
exit(0);
}
It's because you aren't stating the actual file. It's in a different directory. If you want the real filename, combine argv[1] and direntp->d_name with a '/' between them.
Also, hungarian naming is icky, even the minor bit like 'p' on the end. If you have so many variables you need to keep track of their types in their names you're doing something wrong.
Here is a revised version of your program:
#include <stdio.h>
#include <stdlib.h>
#include <stddef.h>
#include <string.h>
#include <dirent.h>
#include <sys/stat.h>
#include <errno.h>
#include <limits.h>
#include <sys/param.h>
int main(int argc, char *argv[])
{
DIR *dirp;
struct dirent *direntp;
struct stat stat_buf;
char *str;
char fullpath[MAXPATHLEN + 1];
size_t dirnamelen;
if (argc != 2)
{
fprintf( stderr, "Usage: %s dir_name\n", argv[0]);
exit(1);
}
strncpy(fullpath, argv[1], MAXPATHLEN - 1); /* account for trailing '/' */
fullpath[MAXPATHLEN - 1] = '\0';
dirnamelen = strlen(fullpath);
if (strlen(argv[1]) > dirnamelen) {
fprintf( stderr, "Directory name is too long: %s", argv[1] );
exit(2);
}
fullpath[dirnamelen++] = '/';
fullpath[dirnamelen] = '\0';
if ((dirp = opendir( argv[1])) == NULL)
{
perror(argv[1]);
exit(2);
}
while ((direntp = readdir( dirp)) != NULL)
{
fullpath[dirnamelen] = '\0';
if ((dirnamelen + strlen(direntp->d_name)) > MAXPATHLEN) {
fprintf(stderr, "File %s + directory %s is too long.", direntp->d_name, fullpath);
continue;
} else {
/* strncpy is mild overkill because the if statement has verified that
there's enough space. */
strncpy(fullpath + dirnamelen, direntp->d_name, MAXPATHLEN - dirnamelen);
fullpath[MAXPATHLEN] = '\0';
}
if (stat(fullpath, &stat_buf)==-1)
{
perror("stat ERROR");
exit(3);
}
if (S_ISREG(stat_buf.st_mode)) str = "regular";
else if (S_ISDIR(stat_buf.st_mode)) str = "directory";
else str = "other";
printf("%-25s - %s\n", direntp->d_name, str);
}
closedir(dirp);
exit(0);
}
Note that I use MAXPATHLEN (from <limits.h>) and carefully check to make sure there aren't any buffer overflows. You should do the same in your code.
Edit: Changed code to use strn family functions for added safety.
Add
#include <unistd.h>
...
chdir(argv[1]);
or call stat with the full pathname like this
...
char fullpath[MAXPATHLEN];
snprintf(fullpath, sizeof(fullpath), "%s/%s", argv[1], direntp->d_name);
if (stat(fullpath, &stat_buf) == -1)
...
Others have suggested building a full path for stat(), or using chdir(). Both those will work (although they are subject to a race condition, if the directory is renamed while you are in the middle of reading it).
An alternative, which is not subject to the race condition, and is therefore arguably more "correct", is to use fstatat(). Just replace your existing stat() call with:
fstatat(dirfd(dirp), direntp->d_name, &stat_buf, 0)
(The chdir() method can be made race-condition-free too: either by using fchdir(dirfd(dirp)) instead of chdir(), or by changing directory to argv[1] and then opening "." with opendir(). The pathname construction method can't be made race-condition-free).
Why dont you try this? Just give the path to argv[1] like this /home/sabri/Desktop/Test
int main(int argc, char *argv[])
{
struct dirent *direntp;
DIR *dirp;
if (argc != 2)
{
fprintf(stderr, "Usage: %s directory_name\n", argv[0]);
return 1;
}
if ((dirp = opendir(argv[1])) == NULL)
{
perror ("Failed to open directory");
return 1;
}
while ((direntp = readdir(dirp)) != NULL)
printf("%s\n", direntp->d_name);
while ((closedir(dirp) == -1) && (errno == EINTR)) ;
return 0;
}
If you are using on unix, then you may use the system command.
system("ls -ltr | grep -d");