|
#include <limits.h> |
|
#include <stdio.h> |
|
#include <string.h> |
|
#include <sys/stat.h> |
|
#include <time.h> |
|
#include <stdlib.h> |
|
#include <unistd.h> |
|
#include <dirent.h> |
|
|
|
#define VCS_DIR ".dvn" |
|
#define METADATA ".dvn.metadata" |
|
|
|
void usage(); |
|
|
|
void cmd_init(); |
|
void cmd_commit(char *message, int files_count, char *files[]); |
|
void cmd_log(); |
|
|
|
int copy(char *source_name, char *destination_name); |
|
|
|
int main (int argc, char *argv[]) { |
|
if (argc == 1) { |
|
usage(); |
|
return 0; |
|
} |
|
|
|
const char *action = argv[1]; |
|
if (strcmp(action, "init") == 0) { |
|
cmd_init(); |
|
} else if (strcmp(action, "commit") == 0 && argc >= 4) { |
|
int file_argc = argc - 3; |
|
char *files[file_argc]; |
|
int j = 3; |
|
for (int i = 0; i < file_argc; i++) { |
|
files[i] = argv[j]; |
|
j++; |
|
} |
|
cmd_commit(argv[2], file_argc, files); |
|
} else if (strcmp(action, "log") == 0) { |
|
cmd_log(); |
|
} else { |
|
usage(); |
|
} |
|
|
|
return 0; |
|
} |
|
|
|
void usage() { |
|
printf("dvn - Date version control\n\n"); |
|
printf("Usage:\n"); |
|
printf("\tdvn init\n"); |
|
printf("\tdvn commit [message] [file]\n"); |
|
printf("\tdvn log\n"); |
|
} |
|
|
|
void cmd_init() { |
|
if (mkdir(VCS_DIR, 0755) == 0) { |
|
printf("Initialized dvn repository.\n"); |
|
printf("Recommend: Initial commit `dvn commit 'Initial' *`\n"); |
|
} else { |
|
perror(VCS_DIR); |
|
} |
|
} |
|
|
|
void cmd_commit(char *message, int files_count, char *files[]) { |
|
const int DATE_LENGTH = 25; |
|
char date[DATE_LENGTH]; |
|
time_t now = time(NULL); |
|
strftime(date, DATE_LENGTH, "%FT%T%z", localtime(&now)); |
|
|
|
char commit_dir[PATH_MAX] = VCS_DIR; |
|
sprintf(commit_dir, "%s/%s", VCS_DIR, date); |
|
|
|
int error = mkdir(commit_dir, 0755); |
|
if (error != 0) { |
|
perror(commit_dir); |
|
exit(EXIT_FAILURE); |
|
} |
|
|
|
for (int i = 0; i < files_count; i++) { |
|
char commit_file[PATH_MAX]; |
|
char *file = files[i]; |
|
|
|
sprintf(commit_file, "%s/%s", commit_dir, file); |
|
|
|
error = copy(file, commit_file); |
|
if (error != 0) { |
|
rmdir(commit_dir); |
|
perror(commit_dir); |
|
exit(EXIT_FAILURE); |
|
} |
|
} |
|
|
|
char metadata_path[PATH_MAX]; |
|
sprintf(metadata_path, "%s/%s", commit_dir, METADATA); |
|
FILE *metadata = fopen(metadata_path, "w"); |
|
fputs(message, metadata); |
|
fclose(metadata); |
|
|
|
printf("Commit %s: %s\n", date, message); |
|
} |
|
|
|
void cmd_log() { |
|
DIR *dir; |
|
struct dirent *entry; |
|
|
|
if ((dir = opendir(VCS_DIR)) == NULL) { |
|
perror("Commit dir"); |
|
exit(EXIT_FAILURE); |
|
} |
|
for (entry = readdir(dir); entry != NULL; entry = readdir(dir)){ |
|
char *name = entry->d_name; |
|
|
|
if (strcmp(name, ".") == 0 || strcmp(name, "..") == 0) { |
|
continue; |
|
} |
|
|
|
char metadata_path[PATH_MAX]; |
|
char commit_msg[256]; |
|
sprintf(metadata_path, "%s/%s/%s", VCS_DIR, name, METADATA); |
|
FILE *metadata = fopen(metadata_path, "r"); |
|
fgets(commit_msg, sizeof(commit_msg), metadata); |
|
fclose(metadata); |
|
printf("%s: %s\n", name, commit_msg); |
|
} |
|
closedir(dir); |
|
} |
|
|
|
int copy(char *source_name, char *destination_name) { |
|
struct stat path_stat; |
|
stat(source_name, &path_stat); |
|
|
|
if (S_ISDIR(path_stat.st_mode)) { |
|
DIR *dir = opendir(source_name); |
|
struct dirent *entry; |
|
|
|
mkdir(destination_name, 0755); |
|
|
|
for (entry = readdir(dir); entry != NULL; entry = readdir(dir)) { |
|
char *name = entry->d_name; |
|
|
|
if (strcmp(name, ".") == 0 || strcmp(name, "..") == 0) { |
|
continue; |
|
} |
|
|
|
char source[PATH_MAX]; |
|
char destination[PATH_MAX]; |
|
sprintf(source, "%s/%s", source_name, name); |
|
sprintf(destination, "%s/%s", destination_name, name); |
|
copy(source, destination); |
|
} |
|
closedir(dir); |
|
|
|
return 0; |
|
} |
|
|
|
FILE *source = fopen(source_name, "rb"); |
|
if (source == NULL) { |
|
fclose(source); |
|
return 1; |
|
} |
|
|
|
FILE *destination = fopen(destination_name, "wb"); |
|
if (destination == NULL) { |
|
fclose(destination); |
|
return 1; |
|
} |
|
|
|
int i; |
|
for (i = getc(source); i != EOF; i = getc(source)) { |
|
putc(i, destination); |
|
} |
|
fclose(destination); |
|
fclose(source); |
|
|
|
return 0; |
|
} |