Code:
#define __EXTENSIONS__
#include <stdio.h>
#include <time.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include "safe_utilities.h"
#define ARB_BUFF_SZ 100
#define VC_SUB_DIR "./vc/"
#define PATH_LNGTH 5
#define FIRST_LINE 8
#define CURVER "({VC_CURRENT_VERSION)}"
#define END_CURVER "({VC_END_CURRENT_VERSION)}"
#define DIFFS "({VC_VERSION_DIFFERENCES)}"
#define END_DIFFS "({VC_END_VERSION_DIFFERENCES)}"
#define DIFF_VER "({VC_VERSION)}"
#define END_DIFF_VER "({VC_END_VERSION)}"
#define LOG "({VC_ACTIVITY_LOG)}"
#define END_LOG "({VC_END_ACTIVITY_LOG)}"
#define SED "sed -n -e :a -e '/({VC_CURRENT_VERSION)}/bb' -e 'n;ba' -e :b -e 'n' -e :c -e '/({VC_END_CURRENT_VERSION)}/ba' -e 'p;n;bc'"
#define RED_FILE "vc/vc_temporary_file"
typedef enum {sucessful = 0, failed = 1} sucess_t;
char *diff_command(char *f1, char *f2)
{
int size = (5 + strlen(f1) + 1 + strlen(f2) + 1)*sizeof(char);
char *diff = malloc( size );
snprintf(diff, size, "diff %s %s", f1, f2);
return diff;
}
char *sed_command(char *redirect)
{
char *s = SED;
int size = (strlen(s) + 3 + strlen(redirect) + 1) * sizeof(char);
char *sedcom = malloc( size );
snprintf(sedcom, size, "%s > %s", s, redirect);
return sedcom;
}
char *rel_path(char *path)
{
int size = (PATH_LNGTH + strlen(path) + 1) * sizeof(char);
char *filename = malloc(size);
snprintf(filename, size, "%s%s", VC_SUB_DIR, path);
return filename;
}
int checkout(char* path)
{
time_t t; /* non-readable time from function call*/
char* sed; /* the sed command to isolate the current version from 'path' */
char *date; /* current date string */
char *file_content; /* string of 'file' content */
char *filename = rel_path(path); /* includes "./vc/" infront of 'path' */
char *locked = malloc(FIRST_LINE*sizeof(char)); /* contains "locked" or "unlocked" */
FILE *file = fopen(filename, "r"); /* file to be checked out */
FILE *command; /* sed command used to isolate newest version */
struct stat fstatus; /* status information of 'file' */
sucess_t sucess; /* indicates a successful run */
int sizeread; /* size of data already read from file */
fgets(locked, FIRST_LINE, file); /* get first line (indicates lock) */
sizeread = strlen(locked);
if( strcmp(locked, "locked\n") == 0) /* file is locked (previously checked out */
{
locked = realloc(locked, ARB_BUFF_SZ); /* reallocate memory to an arbitrary but safe length to obtain checkout info from next line */
fgets(locked, ARB_BUFF_SZ, file);
fprintf(stderr, "'%s' has been locked by %s", path, locked);
sucess = sucessful;
}
else if( strcmp(locked, "unlock\n") == 0) /* file is available for checkout */
{
stat_safe(filename, &fstatus); /* get file statistics (specifically file size) */
file_content = malloc(sizeof(char)*(fstatus.st_size-sizeread)); /* holds 'file' contents */
sed = sed_command(path); /* string form of sed command to isolate current version within 'file' */
command = popen(sed, "w"); /* command stream */
fread(file_content, sizeof(char), (fstatus.st_size-sizeread)*sizeof(char), file); /* read file into 'file_content' */
fputs(file_content, command); /* execute command with 'file' contents */
fclose(file); /* close file to be re-opened for writing */
/* re-write file with "locked" and user's checkout information */
file = fopen(filename, "w");
time(&t);
date = ctime(&t);
fprintf(file, "locked\n%s", date); /* write check out information */
fwrite(file_content, sizeof(char), strlen(file_content)*sizeof(char), file); /* write back whole file */
pclose(command);
free(sed);
free(file_content);
sucess = sucessful;
}
else{
fputs("Could not determine lock on target file.\n", stderr);
sucess = failed;
}
fclose(file);
free(filename);
free(locked);
return (int)sucess;
}
int checkin(char *path)
{
char *lock_version = malloc(ARB_BUFF_SZ*sizeof(char)); /* throw away lines */
int erased_size; /* size of erased data */
char *vcfilename = rel_path(path); /* relative path to 'path' in vc directory */
char *curversion; /* checked in version */
char *vcfile_content; /* content in the vc file */
char *username = malloc(ARB_BUFF_SZ*sizeof(char)); /* name of user checking in */
char *usernotes = malloc((ARB_BUFF_SZ+1)*sizeof(char)*3); /* user's notes on checkin */
char *sed = sed_command(RED_FILE); /* string form of sed command to isolate current version within 'file' */
char *diff = diff_command(RED_FILE, path); /* string form of the diff command of user version and vc's version */
char *diff_content; /* output from diff utility */
char *start; /* start of section being left from vc file re-write */
char *end; /* end of section being left from vc file re-write */
int writesize; /* end of section being left from vc file re-write */
FILE *vcfile = fopen(vcfilename, "a+"); /* version in vc directory */
FILE *ufile = fopen(path, "r"); /* version in user's directory */
FILE *sed_comm = popen(sed, "w"); /* piped shell command */
FILE *diff_comm; /* piped diff command */
struct stat vcfstat; /* targeted file's status info */
struct stat ufstat; /* user's file status info */
int version = 0; /* current version of file */
int buff;
stat_safe(path, &ufstat); /* user file stats */
stat_safe(vcfilename, &vcfstat); /* vc file stats */
/* file is NOT the first version being checked in */
if(vcfstat.st_size != 0)
{
rewind(vcfile); /* reposition file stream position to beginning */
fgets(lock_version, ARB_BUFF_SZ, vcfile); /* get lock information */
erased_size = strlen(lock_version); /* track file position */
/*file has not been checked out yet */
if(strcmp(lock_version, "unlock\n") == 0){
printf("'%s' has not been checked out.\n", path);
return sucessful;
}
/* OK to check in because file has been checked in */
else if(strcmp(lock_version, "locked\n") == 0){
fgets(lock_version, ARB_BUFF_SZ, vcfile); /* this line will contain the date of the checkout */
erased_size += strlen(lock_version); /* track file position */
}
/* file must have been tampered with and formatting changed */
else
{printf("could not determine lock on '%s'.\n", path);}
int ret = fscanf(vcfile, "%d", &version); /* get file version */
erased_size += ret; /* track file position */
curversion = malloc(ufstat.st_size * sizeof(char));
vcfile_content = malloc( (vcfstat.st_size-erased_size) * sizeof(char));
fread(vcfile_content, sizeof(char), (vcfstat.st_size-erased_size)*sizeof(char), vcfile); /* get vc's version of the file */
/*******************PRETTY SURE THIS IS THE PROBLEM**********/
fread(curversion, sizeof(char), ufstat.st_size*sizeof(char), ufile); /* get the user's version of the file */
/********************************************************/
fputs(vcfile_content, sed_comm); /* run sed command to isolate old version into temp file from vc's file */
sleep(1); /* pause or else sed command will not finish before diff call */
diff_comm = popen(diff, "r"); /* run diff on user file and temp file from sed command */
diff_content = malloc((ARB_BUFF_SZ+1)*sizeof(char));
buff = ARB_BUFF_SZ+1;
char c;
int index = 0;
/* get output from diff */
while( (c = fgetc(diff_comm)) != EOF)
{
if(index == buff-1)
{
diff_content = (char*)realloc(diff_content, 2*buff+1);
buff = buff*2 + 1;
}
diff_content[index] = c;
index++;
}
diff_content[index] = '\0';
pclose(diff_comm);
}
/* file was the first version */
else{
curversion = malloc(ufstat.st_size * sizeof(char));
fread(curversion, sizeof(char), ufstat.st_size*sizeof(char), ufile);
}
fclose(vcfile);
vcfile = fopen(vcfilename, "w");
fprintf(vcfile, "unlock\n%d\n", version+1); /* lock and version information */
fprintf(vcfile, "%s\n%s\n%s\n", CURVER, curversion, END_CURVER); /* updated version information */
if( version > 1 ) /* diff information is only present in versions 2 and above */
{
start = strstr(vcfile_content, DIFFS);
end = strstr(vcfile_content, END_DIFFS);
writesize = end - start;
fwrite(start, sizeof(char), writesize, vcfile);
fprintf(vcfile, "%s %d\n%s\n%s\n%s\n", DIFF_VER, version, diff_content, END_DIFF_VER, END_DIFFS); /* print diffs to file */
free(diff_content);
}
/* diff formatting is slightly different if this is the first diff */
if(version == 1){
fprintf(vcfile, "%s\n%s %d\n%s\n%s\n%s\n", DIFFS, DIFF_VER, version, diff_content, END_DIFF_VER, END_DIFFS);
free(diff_content);
}
if(version != 0) /* if first version there is no previous log */
{
start = strstr(vcfile_content, LOG);
end = strstr(vcfile_content, END_LOG);
writesize = end - start;
fwrite(start, sizeof(char), writesize, vcfile);
free(vcfile_content);
}
/* get username and description of checkin from developer */
printf("Enter username: ");
fgets(username, ARB_BUFF_SZ, stdin);
printf("Enter comments (300 characters or less):\n");
fgets(usernotes, ARB_BUFF_SZ*3, stdin);
username[ strlen(username)-1 ] = '\0';
if(version == 0)
{fprintf(vcfile, "%s\n", LOG);}
fprintf(vcfile, "%s: %s\n%s", username, usernotes, END_LOG);
remove(path);
remove(RED_FILE);
free(vcfilename);
free(lock_version);
free(username);
free(usernotes);
free(sed);
free(diff);
fclose(vcfile);
fclose(ufile);
pclose(sed_comm);
free(curversion);
return 0;
}