stuffy/src/stuffy.c

301 lines
9.0 KiB
C

#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include "stuffy.h"
// Put a module header into a HeaderStruct and read (skip) module data
// Returns size of data read when reading a module header
int ReadSingleModuleFromArchive(int fd, ModuleStruct* module)
{
int readSize;
long long int moduleSize;
readSize = read(fd, &(module->moduleHeader), sizeof(module->moduleHeader));
moduleSize = (long long) module->moduleHeader.moduleInfo.st_size;
module->moduleData = malloc(moduleSize);
readSize = read(fd, module->moduleData, moduleSize);
return readSize;
}
// Write a single module into file
int WriteSingleModuleToArchive(int fd, ModuleStruct* module)
{
int writeSize;
long long int moduleSize;
moduleSize = (long long) module->moduleHeader.moduleInfo.st_size;
writeSize = write(fd, &(module->moduleHeader), sizeof(module->moduleHeader));
writeSize = write(fd, module->moduleData, moduleSize);
return writeSize;
}
void PrintModuleHeader(ModuleStruct* module)
{
printf("Name: %s | ", module->moduleHeader.moduleName);
printf("Size: %ld\n", module->moduleHeader.moduleInfo.st_size);
return;
}
int OpenArchive(char* archiveName, int flags)
{
int archiveFD = open(archiveName, flags, 0644);
if (archiveFD < 0)
{
perror("Archive");
return -1;
}
return archiveFD;
}
void LoadModuleFromFile(char* filename, ModuleStruct* module)
{
stat(filename, &(module->moduleHeader.moduleInfo));
long long int moduleSize;
moduleSize = (long long) module->moduleHeader.moduleInfo.st_size;
module->moduleData = malloc(moduleSize);
int fdInput = open(filename, O_RDONLY);
read(fdInput, module->moduleData, moduleSize);
close(fdInput);
return;
}
// Checks if operation went ok, if not then prints message and exits
void SafetyCheck(int status, char* message)
{
if (status)
{
fprintf(stderr, "%s\n", message);
exit(1);
}
return;
}
// Strip path from filename for adding to archive
char* StripFilename(char* filename)
{
int nameStartPosition = -1;
for (int i = 0; i < strlen(filename); i++)
if (filename[i] == '/')
nameStartPosition = i+1;
if (nameStartPosition == -1)
nameStartPosition = 0;
int copySize = strlen(filename) - nameStartPosition;
char* newFilename = malloc(copySize + 1);
strncpy(newFilename, &filename[nameStartPosition], copySize);
strcat(newFilename, "\0");
return newFilename;
}
// Overhead function for archive operations
void Stuffy(int argc, char* argv[])
{
int archiveAction = StuffyArgument(argc, argv);
if (archiveAction < 0)
{
fprintf(stderr, "Usage: stuffy.out [OPTION] [ARCHIVE] [FILE]");
exit(1);
}
StuffyAction(argc, argv, archiveAction);
return;
}
// Checks for argument, if argument is found then returns argument number.
// Returns -1 if not found
int StuffyArgument(int argc, char* argv[])
{
for (int i = 1; i < argc; i++)
{
if ((argv[i][0] == '-') && (argv[i][1] == 'a'))
return 0;
if ((argv[i][0] == '-') && (argv[i][1] == 'r'))
return 1;
if ((argv[i][0] == '-') && (argv[i][1] == 'l'))
return 2;
if ((argv[i][0] == '-') && (argv[i][1] == 'e'))
return 3;
}
return -1;
}
// Redirects program to necessary action function
void StuffyAction(int argc, char* argv[], int archiveAction)
{
if (archiveAction == 0)
AddToArchive(argv[2], argv[3]);
if (archiveAction == 1)
RemoveFromArchive(argv[2], argv[3]);
if (archiveAction == 2)
ListArchive(argv[2]);
if (archiveAction == 3)
ExtractArchive(argc, argv);
return;
}
// Checks if filename is found in a header of archive
int IsFileArchived(char* archiveName, char* filename)
{
ModuleStruct module;
ssize_t readSize;
char* newFilename = StripFilename(filename);
int archiveFD = OpenArchive(archiveName, O_RDONLY | O_CREAT);
do
{
readSize = ReadSingleModuleFromArchive(archiveFD, &(module));
free(module.moduleData);
if (strcmp(module.moduleHeader.moduleName, newFilename) == 0)
{
free(newFilename);
close(archiveFD);
return 1;
}
} while (readSize > 0);
free(newFilename);
close(archiveFD);
return 0;
}
// Check through archive for file, if in archive then inform user
// Adds file to archive and can be repeatedly used to add more to the archive
// If file is already in archive, then exit, let user know, and suggest removing
// file already inside the archive
void AddToArchive(char* archiveName, char* filename)
{
ModuleStruct module;
ssize_t readSize;
char* filenameCleaned = StripFilename(filename);
if (IsFileArchived(archiveName, filenameCleaned))
{
fprintf(stderr, "File already exists in archive, try removing file first.\n");
return;
}
if (access(filename, F_OK))
{
fprintf(stderr, "%s not found.\n", filename);
return;
}
int archiveFD = OpenArchive(archiveName, O_WRONLY | O_APPEND| O_CREAT);
LoadModuleFromFile(filename, &module);
strcpy(module.moduleHeader.moduleName, filenameCleaned);
WriteSingleModuleToArchive(archiveFD, &module);
free(filenameCleaned);
free(module.moduleData);
close(archiveFD);
return;
}
// Check through archive for file, if file exists with name, remove it
// If file not in archive, print "somefile was not found."
void RemoveFromArchive(char* archiveName, char* filename)
{
ssize_t readSize;
char* filenameCleaned = StripFilename(filename);
char tempArchiveName[strlen(archiveName)+5];
strcpy(tempArchiveName, archiveName);
strcat(tempArchiveName, ".tmp\0");
int archiveFile = open(archiveName, O_RDWR | O_CREAT, 0644);
SafetyCheck((archiveFile < 0), "Archive failed to open.");
if (!IsFileArchived(archiveName, filenameCleaned))
{
fprintf(stderr, "Failed to find %s\n", filenameCleaned);
return;
}
ModuleStruct module;
int newArchiveFD = OpenArchive(tempArchiveName, O_WRONLY | O_CREAT);
readSize = ReadSingleModuleFromArchive(archiveFile, &(module));
while (readSize > 0)
{
if (strcmp(module.moduleHeader.moduleName, filenameCleaned) != 0)
WriteSingleModuleToArchive(newArchiveFD, &module);
free(module.moduleData);
readSize = ReadSingleModuleFromArchive(archiveFile, &(module));
}
close(archiveFile);
close(newArchiveFD);
SafetyCheck(remove(archiveName), "Old archive failed removal.");
SafetyCheck(rename(tempArchiveName, archiveName),
"New archive failed name change.");
free(filenameCleaned);
return;
}
// List names and sizes of files stored in archive
// Last line prints total size of all files in archive
void ListArchive(char* archiveName)
{
int archiveFile = open(archiveName, O_RDONLY);
ssize_t readSize;
SafetyCheck((archiveFile < 0), "Archive failed to open.");
ModuleStruct module;
do
{
readSize = ReadSingleModuleFromArchive(archiveFile, &module);
if (readSize > 0)
{
PrintModuleHeader(&module);
free(module.moduleData);
}
} while (readSize > 0);
return;
}
int Test_ListArchive(char* archiveName)
{
struct stat statArchive;
int archiveFD = OpenArchive(archiveName, O_RDONLY);
ssize_t readSize;
long int sizeTotal = 0;
if (archiveFD < 0)
return -1;
ModuleStruct module;
stat(archiveName, &statArchive);
do
{
readSize = ReadSingleModuleFromArchive(archiveFD, &module);
if (readSize > 0)
{
sizeTotal += sizeof(module.moduleHeader);
sizeTotal += module.moduleHeader.moduleInfo.st_size;
}
free(module.moduleData);
} while (readSize > 0);
return sizeTotal;
}
// Extract data of name file from archive to stdout, which is then redirected
// Extracted data remains in archive
int _ExtractArchive(char* archiveName, char* filenameIn)
{
ssize_t readSize;
ssize_t writeSize = 0;
long long int moduleSize;
char* filenameCleaned = StripFilename(filenameIn);
int archiveFile = open(archiveName, O_RDWR | O_CREAT, 0644);
if (!IsFileArchived(archiveName, filenameCleaned))
{
fprintf(stderr, "Failed to find %s\n", filenameCleaned);
return -1;
}
ModuleStruct module;
readSize = ReadSingleModuleFromArchive(archiveFile, &(module));
while (readSize > 0)
{
if (strcmp(module.moduleHeader.moduleName, filenameCleaned) == 0)
writeSize += write(STDOUT_FILENO, module.moduleData, module.moduleHeader.moduleInfo.st_size);
free(module.moduleData);
readSize = ReadSingleModuleFromArchive(archiveFile, &(module));
}
close(archiveFile);
free(filenameCleaned);
return writeSize;
}
void ExtractArchive(int argc, char* argv[])
{
int writeSize;
writeSize = _ExtractArchive(argv[2], argv[3]);
return;
}