/*
* GNU Make Receipter
* Copyright (c) 2008, Blue Static <http://www.bluestatic.org>
*
* This program is free software; you can redistribute it and/or modify it under the terms of the GNU
* General Public License as published by the Free Software Foundation; either version 2 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without
* even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with this program; if not,
* write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/
#include "receipt.h"
#include <stdio.h>
#include <string.h>
#include <dirent.h>
#include <stdlib.h>
#include <assert.h>
#include <sys/stat.h>
#include <unistd.h>
/**
* Writes out a record struct to the given file pointer
*/
void write_struct(struct record *rec, FILE *fp)
{
// lay down a record separator
fputc('\30', fp);
// write the legth of the string
int pathlen = strlen(rec->filename);
fwrite(&pathlen, sizeof(size_t), 1, fp);
// put in a unit separator
fputc('\31', fp);
// write the filename
fwrite(rec->filename, pathlen + 1, 1, fp);
// another unit separator
fputc('\31', fp);
// write the modification time
fwrite(&rec->modified, sizeof(struct timespec), 1, fp);
}
/**
* Recurses a given directory pointer, and writes data to the file
* pointer when it finds files
*/
void record_files(char *path, DIR *dp, FILE *fp)
{
struct dirent *ent;
while ((ent = readdir(dp)) != NULL)
{
// skip dot files
if (ent->d_name[0] != '.')
{
int pathsize = strlen(path) + ent->d_namlen + 2;
char *newpath = malloc(pathsize * sizeof(char));
snprintf(newpath, pathsize, "%s/%s", path, ent->d_name);
if (ent->d_type == DT_DIR)
{
DIR *ndp = opendir(newpath);
// we don't have permission, continue
if (ndp == NULL)
{
continue;
}
record_files(newpath, ndp, fp);
closedir(ndp);
}
else
{
struct stat info;
assert(stat(newpath, &info) == 0);
struct record rec;
rec.filename = newpath;
rec.modified = info.st_mtimespec;
write_struct(&rec, fp);
}
free(newpath);
}
}
}
/**
* Compares two lists of files and records the results into the master receipt
*/
void compare_records(FILE *rcpt, FILE *before, FILE *after)
{
struct record *rbef = malloc(sizeof(struct record));
struct record *raft = malloc(sizeof(struct record));
while (read_struct(raft, after) == 0)
{
assert(read_struct(rbef, before) >= 0);
// if there are new files, go until we're synced up
while (strcmp(raft->filename, rbef->filename) != 0)
{
write_struct(raft, rcpt);
free(raft->filename);
if (read_struct(raft, after) != 0)
{
free(rbef->filename);
goto cleanup;
}
}
// file was modified, so record it
if (raft->modified.tv_sec > rbef->modified.tv_sec)
{
write_struct(raft, rcpt);
}
free(raft->filename);
free(rbef->filename);
}
cleanup:
free(rbef);
free(raft);
}
/**
* Main method
*/
int main(int argc, char *argv[])
{
if (argc < 2)
{
printf("Usage: mkrcpt <location to watch>\n");
return -1;
}
DIR *dp = opendir(argv[1]);
if (dp == NULL)
{
printf("Could not open the directory\n");
return -1;
}
char *recpt = malloc(sizeof(char) * 1024);
snprintf(recpt, 1024, REPOS_ITEM, getenv("HOME"), "test");
FILE *fp = fopen(recpt, "w");
if (fp == NULL)
{
char *repospath = malloc(sizeof(char) * 256);
snprintf(repospath, 256, REPOS, getenv("HOME"));
// could not open the recipt, try making the directory go again
DIR *dest = opendir(repospath);
if (dest == NULL)
{
if (mkdir(repospath, 0700) != 0)
{
printf("Could not create receipts directory\n");
free(repospath);
return -2;
}
fp = fopen(recpt, "w");
if (ferror(fp))
{
printf("Failed to create receipt\n");
free(repospath);
return -2;
}
}
else
{
printf("Failed to create recipt\n");
free(repospath);
return -1;
}
}
fwrite(&HEADER, sizeof(HEADER), 1, fp);
time_t install_time;
assert(time(&install_time) != 0);
fwrite(&install_time, sizeof(time_t), 1, fp);
fputc('\2', fp);
// open the temporary before list
int pathsize = strlen(recpt) + 3;
char *temppath = malloc(pathsize * sizeof(char));
snprintf(temppath, pathsize, "%s.1", recpt);
FILE *befp = fopen(temppath, "w+");
record_files(argv[1], dp, befp);
fputc('\0', befp);
// install
system("make install");
// open the temporary after list
rewinddir(dp);
temppath[strlen(temppath) - 1] = '2';
FILE *affp = fopen(temppath, "w+");
record_files(argv[1], dp, affp);
fputc('\0', affp);
// compare the two file lists
rewind(befp);
rewind(affp);
compare_records(fp, befp, affp);
fclose(befp);
fclose(affp);
// remove the two temps
unlink(temppath);
temppath[strlen(temppath) - 1] = '1';
unlink(temppath);
free(temppath);
// finish cleaning up
fputc('\0', fp);
fclose(fp);
closedir(dp);
free(recpt);
return 0;
}