Directory Structure for a unix like file system - c++

I am currently in a Systems Software class and our final project is to implement implement a simple unix like shell environment and file system with a hierarchical directory structure. We have done the easy part of asking the user for a command like 'cd xxx' or 'ls'. And once each command is called, it goes to a function. I know I need a tree like data structure for the directories and files, but I just don't know where to start. I know that a parent can only be a directory. The directory has a name and can take other directories and files. A file only has a name. How do I go about implementing this kind of code? Here is all I have right now:
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<iostream>
void makeFS(){
printf("You created and formatted a new filesystem.\n");
}
void listDir(){
printf("Listing all entries in current directory...\n");
}
void exitShell(){
printf("Adios amigo.\n");
exit(0);
}
void makeDir(char name[50]){
printf("Directory [%s] created at !\n", name);
}
void remDir(char cmd[50]){
printf("You entered %s \n", cmd);
}
void changeDir(char *nextName){
printf("Changed directory path to %s\n", nextName);
}
void status(char cmd[50]){
printf("You entered %s \n", cmd);
}
void makeFile(char cmd[50]){
printf("You entered %s \n", cmd);
}
void remFile(char cmd[50]){
printf("You entered %s \n", cmd);
}
int main(int argc, char *argv[]){
char cmd[50];
const char spc[50] = " \n";
char *file, *dir;
char *tok, *nextName;
while (1){
printf("Russ_John_Shell> ");
fgets(cmd, 50, stdin);
//Tokenizes string to determine what command was inputed as well as any file/directory name needed
tok = strtok(cmd, spc);
nextName = strtok(NULL, spc);
//Checks to see whether the string has a file/directory name after the command
if (nextName == NULL){
//mkfs command
if (strcmp(cmd, "mkfs") == 0){
makeFS();
}
//exit command
else if (strcmp(cmd, "exit") == 0){
exitShell();
}
//ls command
else if (strcmp(cmd, "ls") == 0){
listDir();
}
//command not recognized at all
else {
printf("Command not recognized.\n");
}
}
else {
//mkdir command
if (strcmp(cmd, "mkdir") == 0){
makeDir(nextName);
}
//rmdir command
else if (strcmp(cmd, "rmdir") == 0){
remDir(cmd);
}
//cd command
else if (strcmp(cmd, "cd") == 0){
changeDir(nextName);
}
//stat command
else if (strcmp(cmd, "stat") == 0){
status(cmd);
}
//mkfile command
else if (strcmp(cmd, "mkfile") == 0){
makeFile(cmd);
}
//rmfile command
else if (strcmp(cmd, "rmfile") == 0){
remFile(cmd);
}
//command not recognized at all
else {
printf("Command not recognized.\n");
}
}
}
}

Why don't you use boost.filesystem?
http://www.boost.org/doc/libs/1_62_0/libs/filesystem/doc/index.htm

Related

converting .c file to .o using processes

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()

Enabling C code to run as C++ code

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.

Linux - Shell does not redirect outputs

I created a simple shell in Linux using fork() and execvp(). It works fine with cat, ls etc. but when I try to redirect its output like ./hello.o > output.txt it doesn't work.
I am guessing I didn't provide the write path to look for the definitions. My shell is currently searching on /bin/ path where most of the commands are stored.
Here is my code:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#define ARG_SIZE 100 // MAX LENGTH FOR ARGUMENTS
#define PATH "/bin/" // PATH FOR ARGUMENTS
int main() {
char inputLine[BUFSIZ];
char *argv[ARG_SIZE];
// for path + argv
char programPath[200];
while (1) {
printf("myshell> ");
// check if ctrl + D is pressed
if (fgets(inputLine, BUFSIZ, stdin) == NULL)
break;
inputLine[strlen(inputLine) - 1] = '\0';
// check if exit is typed
if (strcmp(inputLine, "exit") == 0)
break;
int i = 0;
argv[0] = strtok(inputLine, " \n");
for (i = 0; argv[i] && i < ARG_SIZE-1; ++i)
argv[++i] = strtok(NULL, " \n");
// create a fork call
pid_t pid = fork();
if (pid == -1) {
perror("fork");
exit(1);
}
// parent
if (pid != 0) {
wait();
// child
} else {
strcat(programPath, argv[0]);
// will not return unless it fails
execvp(programPath, argv);
perror("execvp");
exit(1);
}
}
return 0;
}

Inotify - event->name gives subdirectories, not the main directories

I have been writing a program to print the name of the file being accessed and the time the file was accessed. Basically, instead of getting the file path name that I type in, I am getting the names of all the subdirectories of the file. I strongly suspect that it has something to do with the event->name that I am using toward the end of the program. Is there some other function I should be using instead?
The code is as follows:
string filename;
printf("Please enter the file path name: ");
getline(cin, filename);
/*Create a List<string> for multiple files to be entered*/
list<string> stringlist;
list<string>::iterator it;
stringlist.push_back(filename);
it = stringlist.begin();
while(filename != "exit")
{
/*Check whether files exists. Prompt user to re-enter filename*/
while(FileExists(filename) == false )
{
printf("File %s does not exist. Try again.\n", filename.c_str());
printf("Please enter the file path name: ");
getline(cin, filename);
}
printf("Please enter the next file: ");
getline(cin, filename);
if(filename != "exit")
stringlist.push_back(filename);
}
int index = 0;
wd = 0;
length = read(fd, buffer, BUF_LEN);
while(i < length) {
struct inotify_event* event = (struct inotify_event * ) &buffer[i];
if(event->mask & IN_ACCESS)
{
log.WriteFile(event->name);
}
i+= EVENT_SIZE + event->len;
}

Using 'fgets' to read in a char is just skipping when running

the issue is in the read_in function when i use fgets to read in the users input for a band/singer it will just skip this line. I cant work out why this is happening? I did use scanf for this before but the issue is that if you do that when you enter a band name with a space e.g 'foo fighters' it will skip the next print name of reference line. Anyone know how to fix this?
#include <stdio.h>
#include <conio.h>
#define MAX 1000`enter code here`
int exit = 0; // Declaring a global variable
struct data { // using struct to put the data in an arrays
char band [48], cd [48], ref [16];
float cost;
int year;
} cata [MAX]; // declaring a struct variable
int read_in ( void )
{
int i = 0;
printf("\n");
while ( exit != 2 ) // while exit != 2 then ask the user to enter the information
{
printf("\nPlease Enter the Name of Band/Singer: ");
fgets(cata[i].band, 48, stdin);
printf("\nPlease Enter the Name of CD: ");
scanf("%s", &cata[i].cd);
printf("\nPlease Enter the Name of the Reference: ");
scanf("%s", &cata[i].ref);
printf("\nPlease Enter the cost: ");
scanf("%f", &cata[i].cost);
printf("\nPlease Enter the Year of Release: ");
scanf("%d", &cata[i].year);
printf("\nWrite another CD: [1] \nMain Menu: [2]\nYour choice: "); // Asking him a choice either write another one or go back to menu
scanf("%d", &exit);
i++;
}
exit = 0;
return i;
}
void print_out (void)
{
FILE *file_cata;
int i = 0;
printf("\n");
while ( exit != 2 )
{
file_cata = fopen ("catalogue.txt", "r"); // open the file and read
if (file_cata == NULL)
{
printf("Error: can't open files.\n");
}
else
{
while (!feof (file_cata))
{
fgets(cata[i].band, MAX , file_cata); // it will scanf the file and get the string and then print it out on screen
printf("%s", cata[i].band);
fgets(cata[i].cd, MAX, file_cata);
printf("%s", cata[i].cd);
fgets(cata[i].ref, MAX, file_cata);
printf("%s", cata[i].ref);
fscanf(file_cata, "%.2f" , cata[i].cost);
fscanf(file_cata, "%d", cata[i].year);
i++;
}
}
fclose (file_cata); // close file
printf("Read it again: [1] \nMain Menu: [2]\nYour choice: ");
scanf("%d", &exit);
}
exit = 0;
}
void save ( int num )
{
FILE *file_cata;
int i = 0;
printf("\n");
while ( exit != 2 )
{
file_cata = fopen ("catalogue.txt", "a"); // file append, so it will write at the end of the file
if (file_cata == NULL)
{
printf("Error: can't open files. \n");
}
else
{
while (i != num)
{
fprintf( file_cata, "\n");
fprintf( file_cata, "The name of Band/Singer: %s \n", cata[i].band);
fprintf( file_cata, "The name of CD: %s \n", cata[i].cd);
fprintf( file_cata, "The name of Reference: %s\n", cata[i].ref);
fprintf( file_cata, "The Cost: %.2f \n", cata[i].cost);
fprintf( file_cata, "The Year of Release: %d \n", cata[i].year);
i++;
}
}
fprintf( file_cata, "\n");
fclose (file_cata);
printf("Your data has been saved to the catalogue.\n\n");
printf("Save it again: [1] \nMain Menu: [2]\nYour Choice: ");
scanf("%d", &exit);
}
exit = 0;
}
void main ()
{
int num1 = 0;
int option = 0;
while ( option != 4 )
{
printf("The following options are availlable: \n");
printf("Read in data [1] \n");
printf("Print out catalogue to screen [2] \n");
printf("Save data to file [3] \n");
printf("Exit Program [4] \n");
printf("Enter your choice now: ");
scanf( "%d", &option);
if (option == 1)
num1 = read_in ();
if (option == 2)
print_out ();
if (option == 3)
save(num1);
printf("\n");
}
printf("\n*** Your program will end when you press ANY button. ***\n");
_getch();
}
The scanf is leaving the newline in the input buffer. Related question with a very good answer (Hint: don't use scanf; use fgets always, plus sscanf if you must convert that way.): Replacement of fflush(stdin)
This flushes the input buffer, which you should do before attempting to read another line (stolen from answer above):
while((c = getchar()) != '\n' && c != EOF)