I have to make a project which asks me to create a program that will compile a C project by recursively descending into directories and launching processes which compile a file of code by calling GCC and look through directories and launch a new process for every ".c" file in the current directory that process will call gcc on the .c file making a .o file.
I have written this code so far to list the directories first
I am having trouble checking what files are .c and how to convert them to .o
can someone help me with some relevant information/links that I can refer to?
void listdir(const char *name, int indent)
{
DIR *dir;
struct dirent *entry;
if (!(dir = opendir(name)))
return;
while ((entry = readdir(dir)) != NULL) {
if (entry->d_type == DT_DIR) {
char path[1024];
if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0)
continue;
snprintf(path, sizeof(path), "%s/%s", name, entry->d_name);
printf("%*s[%s]\n", indent, "", entry->d_name);
listdir(path, indent + 2);
} else {
printf("%*s- %s\n", indent, "", entry->d_name);
}
}
closedir(dir);
}
int main(void) {
listdir(".", 0);
return 0;
}
You will need to use fork() and exec() in order to get a .o file it should look something like this
if ((forkmefool[i] = fork()) < 0)
{
printf("no Procceses");
exit(0);
}
else if (forkmefool[i] == 0)
{
char *args[] = {"cc", entry->d_name, "-c", NULL};
execv("/usr/bin/cc", args);
}
take a note of exec()
Related
I have a C program that finds duplicate files within a directory. The program is executed on the command line and passed 2 arguments. One is the parent directory, and argument two is the file name. It is working code in c, but I have a GUI and other files for "microservices" written in c++.
How would one call this C code from a c++ file?
#include<stdio.h>
#include<dirent.h>
#include<sys/stat.h>
#include<errno.h>
#include<string.h>
#include<stdlib.h>
#include<fcntl.h>
//Compile: gcc dreamduplicatefinder.c -o dreamduplicatefinder.exe
//Run: ./dreamduplicateFinder.exe parent_dir filename...
#define false 0
#define true 1
int duplicateCount = 0;
int FindDuplicates(char* path, char* fileName);
int CompareFiles(char* originalFile, char* currFile);
int main(int argc, char *argv[])
{
//Two additional arguments are expected: Parent dir, file to find duplicates of...
if (argc != 3)
{
printf("Usage: %s 'Base Directory' 'File Name'\n", argv[0]);
return -1;
}
//argv[1] = base dir, argv[2] = file to find duplicates of; e.g argv[1] = /home,
//argv[2] = "file.txt"...
FindDuplicates(argv[1], argv[2]);
printf("\n\nFound %d duplicate(s)\n", duplicateCount);
return 0;
}
int FindDuplicates(char* path, char* fileName)
{
DIR *dir;
struct dirent *dp;
struct dirent *result;
struct stat statp;
char absoluteFilePath[255];
if ((dir = opendir(path)) == NULL)
{
//printf(dir); //error could becuase trying to open shortcut or corrupt folder.
printf("%s\n",path);
perror("Failed to open directory");
return -1;
}
while ((dp = readdir(dir)) != NULL)
{
//readdir returns . and .. which we should ignore...
if (strcmp(dp->d_name, ".") && strcmp(dp->d_name, ".."))
{
//find file full path, relative to base path. e.g, a /home/file.txt...
//copy path to absoluteFilePath...
strcpy(absoluteFilePath, path);
//append / at end...
strcat(absoluteFilePath, "/");
//append filename to path...
strcat(absoluteFilePath, dp->d_name);
//check if the current file is actually file or dir...
stat(absoluteFilePath, &statp);
if (S_ISDIR(statp.st_mode)) //is a directory...
{
//recurse through this dir...
FindDuplicates(absoluteFilePath, fileName);
}
else if (S_ISREG(statp.st_mode)) //is a file...
{
//check for duplicates here...
//compare current file with the file specified by user...
if (strcmp(fileName, absoluteFilePath))
{
if (CompareFiles(fileName, absoluteFilePath))
{
//yes, duplicate; print it...
printf("%s\n", absoluteFilePath);
duplicateCount++;
}
}
} //end else if (S_ISREG(statp.st_mode))...
} //if (strcmp(dp->d_name, ".") && strcmp(dp->d_name,".."))...
} //end while...
closedir(dir);
return 0;
}
int CompareFiles(char* originalFile, char* currFile)
{
//two step comparison: (1) first check size; if not same, return false.
//If equal, (2) compare file content.If equal, return true, false otherwise...
struct stat statOriginal, statCurr;
stat(originalFile, &statOriginal);
stat(currFile, &statCurr);
//Step 1...
if ((int)statOriginal.st_size != (int)statCurr.st_size) //size not same...
return false;
//Step 2...
//size matches, files can be same; confirm it by matching both file contents...
int fdOriginal = open(originalFile, O_RDONLY);
int fdCurr = open(currFile, O_RDONLY);
if (fdOriginal == -1 || fdCurr == -1)
return false; //error occurred, not sure if file is duplicate...
//we will read file in small chunks and compare...
int chunkSize = 1024, bytesRead;
char *bufferOriginal = (char*)malloc(chunkSize * sizeof(char));
char *bufferCurr = (char*)malloc(chunkSize * sizeof(char));
while (true)
{
//read file in chunk...
bytesRead = read(fdOriginal, bufferOriginal, chunkSize);
if (bytesRead <= 0)
break; //end of file...
bytesRead = read(fdCurr, bufferCurr, bytesRead);
//compare buffer...
if (strcmp(bufferOriginal, bufferCurr)) //if content not matching...
return false;
}
return true;
}
My errors include: (from compareFiles function)
2x 'open' identifier not found
2x 'read' identifier not found
The working code for those curious.
Thank you #MarcusMüller & #JesperJuhl
#include "stdafx.h" //there is nothing in this header
#include<stdio.h>
#include<dirent.h>
#include<sys/stat.h>
#include<errno.h>
#include<string.h>
#include<stdlib.h>
#include<fcntl.h>
#include <iostream>
#include <fstream>
#include <string>
using namespace std;
//Compile: gcc <name of this file>.cpp -o <nameOfThisFile>.exe
//Run: <nameOfThisFile> parent_dir filename...
#define false 0
#define true 1
int duplicateCount = 0;
int FindDuplicates(char* path, char* fileName);
int CompareFiles(char* originalFile, char* currFile);
int main(int argc, char *argv[])
{
//Two additional arguments are expected: Parent dir, file to find duplicates of...
if (argc != 3)
{
printf("Usage: %s 'Base Directory' 'File Name'\n", argv[0]);
return -1;
}
//argv[1] = base dir, argv[2] = file to find duplicates of; e.g argv[1] = /home,
//argv[2] = "file.txt"...
FindDuplicates(argv[1], argv[2]);
printf("\n\nFound %d duplicate(s)\n", duplicateCount);
return 0;
}
int FindDuplicates(char* path, char* fileName)
{
DIR *dir;
struct dirent *dp;
struct dirent *result;
struct stat statp;
char absoluteFilePath[255];
if ((dir = opendir(path)) == NULL)
{
//possibly trying to open shortcut or corrupt folder typically.
printf("Failed to open directory %s \n",path);
return -1;
}
while ((dp = readdir(dir)) != NULL)
{
//readdir returns . and .. which we should ignore...
if (strcmp(dp->d_name, ".") && strcmp(dp->d_name, ".."))
{
//find file full path, relative to base path. e.g, a /home/file.txt...
//copy path to absoluteFilePath...
strcpy(absoluteFilePath, path);
//append / at end...
strcat(absoluteFilePath, "/");
//append filename to path...
strcat(absoluteFilePath, dp->d_name);
//check if the current file is actually file or dir...
stat(absoluteFilePath, &statp);
if (S_ISDIR(statp.st_mode)) //is a directory...
{
//recurse through this dir...
FindDuplicates(absoluteFilePath, fileName);
}
else if (S_ISREG(statp.st_mode)) //is a file...
{
//check for duplicates here...
//compare current file with the file specified by user...
if (strcmp(fileName, absoluteFilePath))
{
if (CompareFiles(fileName, absoluteFilePath))
{
//yes, duplicate; print it...
printf("This is a duplicate! %s\n", absoluteFilePath);
duplicateCount++;
}
}
} //end else if (S_ISREG(statp.st_mode))...
} //if (strcmp(dp->d_name, ".") && strcmp(dp->d_name,".."))...
} //end while...
closedir(dir);
return 0;
}
int CompareFiles(char* originalFile, char* currFile)
{
//two step comparison: (1) first check size; if not same, return false.
//If equal, (2) compare file content.If equal, return true, false otherwise...
struct stat statOriginal, statCurr;
stat(originalFile, &statOriginal);
stat(currFile, &statCurr);
//Step 1...
if ((int)statOriginal.st_size != (int)statCurr.st_size) //size not same...
return false;
FILE* fdOriginal;
if (fdOriginal = fopen(originalFile, "r")) {
if (fdOriginal == NULL) { fputs("File error", stderr); return false; }
}
else return false; //error occurred, not sure if duplicate
FILE* fdCurr;
if (fdCurr = fopen(currFile, "r")) {
if (fdCurr == NULL) { fputs("File error", stderr); return false; }
}
else return false;
int chunkSize = 1024, objsRead;
char *bufferOriginal = (char*)malloc(chunkSize * sizeof(char));
if (bufferOriginal == NULL) { fputs("Memory error for buff orig", stderr); exit(2); }
char *bufferCurr = (char*)malloc(chunkSize * sizeof(char));
if (bufferCurr == NULL) { fputs("Memory error for buff curr", stderr); exit(2); }
while (true)
{
//read file in chunk...
//std::size_t fread( void* buffer, std::size_t size, std::size_t count, std::FILE* stream );
objsRead = fread(bufferOriginal, sizeof(char), chunkSize , fdOriginal);
if (objsRead <= 0)
break; //end of file...
objsRead = fread(bufferCurr, sizeof(char), objsRead, fdCurr);
//compare buffer...
if (strcmp(bufferOriginal, bufferCurr)) //if content not matching...
return false;
}
return true;
}
You usually just wouldn't do that. You'd wrap it in a C function, and compile it to an object file.
Then you'd include your C header with extern "C" {…}, and just call that function from C++.
When building your executable, you'd link in the object file containing your C function. Done!
Note: C isn't C++, and albeit your code not being illegal in C++ (as far as I can instantly tell), it does very "ugly" things (like #defineing true and false – ugh, that would already be a bad idea in C, to be honest). So, deal with it like you would deal with code in Fortran, or Java, or any other language that has a calling convention that you can use from C++ (which, usually, is the C calling convention): Just use it as an extern object.
Using ::open and ::read should cause the functions to be found.
You may also want to replace the C headers (like "string.h") with their C++ equivalent versions (like "cstring").
Your defines for true and false should also go. In C++ those are proper bools, not integers. This means the return type of CompareFiles should be changed to bool.
And you should wrap duplicateCount in an anonymous namespace - or return it from the function that updates it (either by returning a small struct with two ints, or by using a std::pair or std::tuple) - global variables are evil.
Trying to understand why unlink isn't working (not removing the file) in my code down below. The only thing I can imagine is that the program thinks I'm still interacting with the file so its not actually unlinking it since its still in use. The code is meant to be a copy of "rm"
void directorySearch(const char *dName)
{
DIR *dir;
struct dirent *ent;
if ((dir = opendir (dName)) != NULL)
{
while ((ent = readdir (dir)) != NULL)
{
if ( ent->d_type!=DT_DIR)
{
//Where the crazy happens
printf ("%s\n", ent->d_name);
char path[PATH_MAX];
const char * d_name = ent->d_name;
unlink(path);
}
if ( ent->d_type==DT_DIR && strcmp(ent->d_name, ".")!= 0 && strcmp(ent->d_name, "..") != 0)
{
int path_length;
char path[PATH_MAX];
const char * d_name = ent->d_name;
path_length = snprintf (path, PATH_MAX, "%s/%s", dName, d_name);
directorySearch(path);
}
}
closedir (dir);
}
else
{
cout << "error with "<< dName<< endl;
}
}
Edited with unlink instead of remove, although both don't work...
You have declared your path variable, but not actually copied anything into that variable. So that's a problem. Also, as a matter of course you should examine the return value from unlink, and if less than zero, examine errno to determine the exact nature of the error. (Typically permissions on no such file.)
For Example: .txt
C:\Duck\aa.txt
C:\Duck\Dog\Pig\cc.txt
C:\Duck\Dog\kk.txt
C:\Duck\Cat\zz.txt
C:\xx.txt
Return 5
You must use opendir() and readdir().
Example:
DIR *dir = opendir("."); // current dir
if (dir == NULL) {
perror("opendir: .");
return 1;
}
struct dirent *dirent;
while ((dirent = readdir(dir)) != NULL) {
printf("%s\n", dirent->d_name);
}
closedir(dir);
Please try it.
#include <sys/types.h>
#include <dirent.h>
#include <stdio.h>
#include <string.h>
int count = 0;
void listdir(const char *name, int level)
{
DIR *dir;
struct dirent *entry;
if (!(dir = opendir(name)))
return;
if (!(entry = readdir(dir)))
return;
do {
if (entry->d_type == DT_DIR) {
char path[1024];
int len = snprintf(path, sizeof(path)-1, "%s/%s", name, entry->d_name);
path[len] = 0;
if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0)
continue;
listdir(path, level + 1);
}
else {
const char *ext = strrchr(entry->d_name,'.');
if((!ext) || (ext == entry->d_name))
;
else {
if(strcmp(ext, ".txt") == 0) {
printf("%*s- %s\n", level * 2, "", entry->d_name);
count++;
}
}
}
} while (entry = readdir(dir));
closedir(dir);
}
int main(void)
{
listdir(".", 0);
printf("There are %d .txt files in this directory and its subdirectories\n", count);
return 0;
}
Test
./a.out
- hi.txt
- payfile.txt
- csis.txt
- CMakeCache.txt
- text.txt
- sorted.txt
- CMakeLists.txt
- ddd.txt
- duom.txt
- output.txt
- mmap_datafile.txt
- file_datafile.txt
- CMakeLists.txt
- link.txt
- TargetDirectories.txt
There are 15 .txt files in this directory and its subdirectories
Thank you all for the answers, the code you had tried:
WIN32_FIND_DATA fdata;
int counter = 0;
if (HANDLE first = FindFirstFile(L"C:\\duck\\*.txt", &fdata))
{
counter++;
do
{
counter++;
} while (FindNextFile(first, &fdata));
}
But as you can see by the same, I did not get the result I was looking for, which was to include the subdirectories.
Thanks to "Dac Saunders" and to all the others, it helped a lot.
I have a problem with read file in the subfolder. I have about 6000 different files in the difference folders. And I will read each file. But if I read about 2000 files, then application is not problem. When I read 6000 files which mean whole subfolders. The application will display problem "can not open file.". But if I only access the folder that is not open, then application is not problem. I don't know what is happen? I think maybe I read many files and memory not enough. Can you edit help me?
//This is code to access subforder
static int
find_directory(
const char *dirname)
{
DIR *dir;
char buffer[PATH_MAX + 2];
char *p = buffer;
const char *src;
const char* folder_dir;
char *end = &buffer[PATH_MAX];
int ok;
/* Copy directory name to buffer */
src = dirname;
printf("src=%s\n",src);
while (p < end && *src != '\0') {
*p++ = *src++;
}
*p = '\0';
/* Open directory stream */
dir = opendir (dirname);
if (dir != NULL) {
struct dirent *ent;
/* Print all files and directories within the directory */
while ((ent = readdir (dir)) != NULL) {
char *q = p;
char c;
/* Get final character of directory name */
if (buffer < q) {
c = q[-1];
} else {
c = ':';
}
/* Append directory separator if not already there */
if (c != ':' && c != '/' && c != '\\') {
*q++ = '/';
}
/* Append file name */
src = ent->d_name;
while (q < end && *src != '\0') {
*q++ = *src++;
}
*q = '\0';
/* Decide what to do with the directory entry */
switch (ent->d_type) {
case DT_REG:
/* Output file name with directory */
{
printf ("FILE=%s\n", buffer);
OFBool check= readfile(buffer)
}
break;
case DT_DIR:
/* Scan sub-directory recursively */
if (strcmp (ent->d_name, ".") != 0
&& strcmp (ent->d_name, "..") != 0) {
find_directory (buffer,opts);
}
break;
default:
/* Do not device entries */
/*NOP*/;
}
}
closedir (dir);
ok = 1;
} else {
/* Could not open directory */
printf ("Cannot open directory %s\n", dirname);
ok = 0;
}
return ok;
}
OFBool readfile(const char* filepath)
{
FILE *f=NULL;
OFBool ok = OFFalse;
if( ( f = fopen( filepath, "rb" ) ) == NULL ) // checks to see if file exists
{
ok = OFFalse;
cout<<"can not read file"<<filepath<<endl;
return ok;
}
else
{
ok = true;
cout<<" reading OK"<<endl;
fclose(f);
return ok;
}
}
How about something like this:
/*
* Recursively walk though a directory tree.
*
* Arguments:
* path - The root directory to start from
* hidden - If non-zero, include hidden files and directories
*/
void recurse_directory(const char *path, const int hidden)
{
DIR *dir = opendir(path);
if (dir == NULL)
{
perror("opendir");
return;
}
struct dirent *ent;
while ((ent = readdir(dir)) != NULL)
{
if (ent->d_type == DT_DIR)
{
if (strcmp(ent->d_name, ".") != 0 &&
strcmp(ent->d_name, "..") != 0 &&
(ent->d_name[0] != '.' || hidden))
{
char *newpath = malloc(strlen(path) + 1 + strlen(ent->d_name) + 1);
strcpy(newpath, path);
strcat(newpath, "/");
strcat(newpath, ent->d_name);
recurse_directory(newpath, hidden);
free(newpath);
}
}
else if (ent->d_type == DT_REG)
{
if (ent->d_name[0] != '.' || hidden)
{
char *newpath = malloc(strlen(path) + 1 + strlen(ent->d_name) + 1);
strcpy(newpath, path);
strcat(newpath, "/");
strcat(newpath, ent->d_name);
printf("File %s\n", newpath);
/* readfile(newpath); */
free(newpath);
}
}
}
}
Too add to the answer from Joachim Pileborg. If you are working with a large number of sub-directories you'd need to add closedir() call after while loop. This is because there is a limit on the number of directories you can have open at one time. The above example will work fine when working with a small folder, but when working with 2000+ sub-folders you'd need to close the directory before opening another.
/*
* Recursively walk though a directory tree.
*
* Arguments:
* path - The root directory to start from
* hidden - If non-zero, include hidden files and directories
*/
void recurse_directory(const char *path, const int hidden)
{
DIR *dir = opendir(path);
if (dir == NULL)
{
perror("opendir");
return;
}
struct dirent *ent;
while ((ent = readdir(dir)) != NULL)
{
if (ent->d_type == DT_DIR)
{
if (strcmp(ent->d_name, ".") != 0 &&
strcmp(ent->d_name, "..") != 0 &&
(ent->d_name[0] != '.' || hidden))
{
char *newpath = malloc(strlen(path) + 1 + strlen(ent->d_name) + 1);
strcpy(newpath, path);
strcat(newpath, "/");
strcat(newpath, ent->d_name);
recurse_directory(newpath, hidden);
free(newpath);
}
}
else if (ent->d_type == DT_REG)
{
if (ent->d_name[0] != '.' || hidden)
{
char *newpath = malloc(strlen(path) + 1 + strlen(ent->d_name) + 1);
strcpy(newpath, path);
strcat(newpath, "/");
strcat(newpath, ent->d_name);
printf("File %s\n", newpath);
/* readfile(newpath); */
free(newpath);
}
}
}
closedir(dir); /* Add this <--- */
}
This program is printing the directories at the root level
Directory_1
Directory_2
but I want to be able to print the directories within them too
Directory_1
Directory_1_2
Directory_1_3
Directory_2
Directory 2_1
Directory_2_1_1
Directory_4
I am trying to do it recursively but I am finding it hard to pass the Directory_1 as a root so it gets evaluated.. What am i missing?
Here is my output
..
.
Directory_1
Directory_2
Failed to open directory: No such file or directory
Code
#include <dirent.h>
#include <errno.h>
#include <stdio.h>
#include <sys/stat.h>
char *arg_temp;
int printDepthFirst(char *arg_tmp);
int main(int argc, char *argv[]) {
if (argc != 2) {
fprintf(stderr, "Usage: %s directory_name\n", argv[0]);
return 1;
}
arg_temp = argv[1];
printDepthFirst(arg_temp);
}
int printDepthFirst(char *arg_tmp)
{
struct dirent *direntp;
DIR *dirp;
if ((dirp = opendir(arg_tmp)) == NULL) {
perror ("Failed to open directory");
return 1;
}
while ((direntp = readdir(dirp)) != NULL)
{
printf("%s\n", direntp->d_name);
arg_tmp = direntp->d_name;
}
printDepthFirst(arg_tmp);
while ((closedir(dirp) == -1) && (errno == EINTR)) ;
return 0;
}
Now, I know some people get irritated when asking questions that they think I am expecting them to code this, you dont need to, if you can tell me theoretically what i need to do.. I will research it although if its a small programmatically fix and you can post that I would really appreaciate it.. but if not.. I would also love to hear about what needs to be done in words..
Thank you
Well this should help:
#define _XOPEN_SOURCE 500
#include <ftw.h>
#include <stdio.h>
static int display_info(const char *fpath, const struct stat *sb,
int tflag, struct FTW *ftwbuf)
{
switch(tflag)
{
case FTW_D:
case FTW_DP: puts(fpath); break;
}
return 0; /* To tell nftw() to continue */
}
int main(int argc, char *argv[])
{
if (argc != 2) {
fprintf(stderr, "Usage: %s directory_name\n", argv[0]);
return 1;
}
int flags = FTW_DEPTH | FTW_MOUNT | FTW_PHYS;
if (nftw(argv[1], display_info, 20, flags) == -1)
{
perror("nftw");
return 255;
}
return 0;
}
Have a look at what fields struct dirent contains.
The string dirent::d_name is a name of a directory, not it's full path. So, if your directory "C:\Alpha" contains directory "C:\Alpha\Beta", d_name would only contatin "Beta", not "C:\Alpha\Beta". You will have to assemble the full path yourself - appending slash/backslash to your arg_tmp and then appending new directory name, like this:
while ((direntp = readdir (dirp)) != NULL)
{
char *dirname = direntp->d_name;
// Only work with directories and avoid recursion on "." and "..":
if (direntp->d_type != DT_DIR || !strcmp (dirname, ".") || !strcmp (dirname, "..")) continue;
// Assemble full directory path:
char current [strlen (arg_tmp) + 2 + strlen (dirname)];
strcpy (current, arg_tmp);
strcat (current, "\\"); // Replace "\\" with "/" on *nix systems
strcat (current, dirname);
// Show it and continue:
printf ("%s\n", current);
printDepthFirst (current);
}
Also, you should call recursively inside the loop, not outside.
Inside your while loop inside printDepthFirst you might need something like:
if(direntp->d_type == DT_DIR)
printDepthFirst(directp->d_name);
You might perhaps have to worry about .. directories too.
Alternatively, I've found boost::filesystem to work quite well.