summaryrefslogtreecommitdiff
path: root/cache.c
diff options
context:
space:
mode:
Diffstat (limited to 'cache.c')
-rw-r--r--cache.c528
1 files changed, 528 insertions, 0 deletions
diff --git a/cache.c b/cache.c
new file mode 100644
index 0000000..4c51cf7
--- /dev/null
+++ b/cache.c
@@ -0,0 +1,528 @@
+/*
+ * Copyright 2001, 2002, 2003 David Mansfield and Cobite, Inc.
+ * See COPYING file for license information
+ */
+
+#include <stdio.h>
+#include <search.h>
+#include <string.h>
+#include <stdlib.h>
+#include <limits.h>
+#include <unistd.h>
+#include <ctype.h>
+#include <time.h>
+
+#include <cbtcommon/hash.h>
+#include <cbtcommon/debug.h>
+
+#include "cache.h"
+#include "cvsps_types.h"
+#include "cvsps.h"
+#include "util.h"
+
+#define CACHE_DESCR_BOUNDARY "-=-END CVSPS DESCR-=-\n"
+
+/* change this when making the on-disk cache-format invalid */
+static int cache_version = 1;
+
+/* the tree walk API pretty much requries use of globals :-( */
+static FILE * cache_fp;
+static int ps_counter;
+
+static void write_patch_set_to_cache(PatchSet *);
+static void parse_cache_revision(PatchSetMember *, const char *);
+static void dump_patch_set(FILE *, PatchSet *);
+
+static FILE *cache_open(char const *mode)
+{
+ char *prefix;
+ char fname[PATH_MAX];
+ char root[PATH_MAX];
+ char repository[PATH_MAX];
+ FILE * fp;
+
+ /* Get the prefix */
+ prefix = get_cvsps_dir();
+ if (!prefix)
+ return NULL;
+
+ /* Generate the full path */
+ strcpy(root, root_path);
+ strcpy(repository, repository_path);
+
+ strrep(root, '/', '#');
+ strrep(repository, '/', '#');
+
+ snprintf(fname, PATH_MAX, "%s/%s#%s", prefix, root, repository);
+
+ if (!(fp = fopen(fname, mode)) && *mode == 'r')
+ {
+ if ((fp = fopen("CVS/cvsps.cache", mode)))
+ {
+ fprintf(stderr, "\n");
+ fprintf(stderr, "****WARNING**** Obsolete CVS/cvsps.cache file found.\n");
+ fprintf(stderr, " New file will be re-written in ~/%s/\n", CVSPS_PREFIX);
+ fprintf(stderr, " Old file will be ignored.\n");
+ fprintf(stderr, " Please manually remove the old file.\n");
+ fprintf(stderr, " Continuing in 5 seconds.\n");
+ sleep(5);
+ fclose(fp);
+ fp = NULL;
+ }
+ }
+
+ return fp;
+}
+
+/* ************ Reading ************ */
+
+enum
+{
+ CACHE_NEED_FILE,
+ CACHE_NEED_BRANCHES,
+ CACHE_NEED_SYMBOLS,
+ CACHE_NEED_REV,
+ CACHE_NEED_PS,
+ CACHE_NEED_PS_DATE,
+ CACHE_NEED_PS_AUTHOR,
+ CACHE_NEED_PS_TAG,
+ CACHE_NEED_PS_TAG_FLAGS,
+ CACHE_NEED_PS_BRANCH,
+ CACHE_NEED_PS_BRANCH_ADD,
+ CACHE_NEED_PS_DESCR,
+ CACHE_NEED_PS_EOD,
+ CACHE_NEED_PS_MEMBERS,
+ CACHE_NEED_PS_EOM
+};
+
+time_t read_cache()
+{
+ FILE * fp;
+ char buff[BUFSIZ];
+ int state = CACHE_NEED_FILE;
+ CvsFile * f = NULL;
+ PatchSet * ps = NULL;
+ char datebuff[20] = "";
+ char authbuff[AUTH_STR_MAX] = "";
+ char tagbuff[LOG_STR_MAX] = "";
+ int tag_flags = 0;
+ char branchbuff[LOG_STR_MAX] = "";
+ int branch_add = 0;
+ char logbuff[LOG_STR_MAX] = "";
+ time_t cache_date = -1;
+ int read_version;
+
+ if (!(fp = cache_open("r")))
+ goto out;
+
+ /* first line is cache version format "cache version: %d\n" */
+ if (!fgets(buff, BUFSIZ, fp) || strncmp(buff, "cache version:", 14))
+ {
+ debug(DEBUG_APPERROR, "bad cvsps.cache file");
+ goto out_close;
+ }
+
+ if ((read_version = atoi(buff + 15)) != cache_version)
+ {
+ debug(DEBUG_APPERROR, "bad cvsps.cache version %d, expecting %d. ignoring cache",
+ read_version, cache_version);
+ goto out_close;
+ }
+
+ /* second line is date cache was created, format "cache date: %d\n" */
+ if (!fgets(buff, BUFSIZ, fp) || strncmp(buff, "cache date:", 11))
+ {
+ debug(DEBUG_APPERROR, "bad cvsps.cache file");
+ goto out_close;
+ }
+
+ cache_date = atoi(buff + 12);
+ debug(DEBUG_STATUS, "read cache_date %d", (int)cache_date);
+
+ while (fgets(buff, BUFSIZ, fp))
+ {
+ int len = strlen(buff);
+
+ switch(state)
+ {
+ case CACHE_NEED_FILE:
+ if (strncmp(buff, "file:", 5) == 0)
+ {
+ len -= 6;
+ f = create_cvsfile();
+ f->filename = xstrdup(buff + 6);
+ f->filename[len-1] = 0; /* Remove the \n at the end of line */
+ debug(DEBUG_STATUS, "read cache filename '%s'", f->filename);
+ put_hash_object_ex(file_hash, f->filename, f, HT_NO_KEYCOPY, NULL, NULL);
+ state = CACHE_NEED_BRANCHES;
+ }
+ else
+ {
+ state = CACHE_NEED_PS;
+ }
+ break;
+ case CACHE_NEED_BRANCHES:
+ if (buff[0] != '\n')
+ {
+ char * tag;
+
+ tag = strchr(buff, ':');
+ if (tag)
+ {
+ *tag = 0;
+ tag += 2;
+ buff[len - 1] = 0;
+ cvs_file_add_branch(f, buff, tag);
+ }
+ }
+ else
+ {
+ f->have_branches = 1;
+ state = CACHE_NEED_SYMBOLS;
+ }
+ break;
+ case CACHE_NEED_SYMBOLS:
+ if (buff[0] != '\n')
+ {
+ char * rev;
+
+ rev = strchr(buff, ':');
+ if (rev)
+ {
+ *rev = 0;
+ rev += 2;
+ buff[len - 1] = 0;
+ cvs_file_add_symbol(f, rev, buff);
+ }
+ }
+ else
+ {
+ state = CACHE_NEED_REV;
+ }
+ break;
+ case CACHE_NEED_REV:
+ if (isdigit(buff[0]))
+ {
+ char * p = strchr(buff, ' ');
+ if (p)
+ {
+ CvsFileRevision * rev;
+ *p++ = 0;
+ buff[len-1] = 0;
+ rev = cvs_file_add_revision(f, buff);
+ if (strcmp(rev->branch, p) != 0)
+ {
+ debug(DEBUG_APPERROR, "branch mismatch for %s:%s %s != %s",
+ rev->file->filename, rev->rev, rev->branch, p);
+ }
+ }
+ }
+ else
+ {
+ state = CACHE_NEED_FILE;
+ }
+ break;
+ case CACHE_NEED_PS:
+ if (strncmp(buff, "patchset:", 9) == 0)
+ state = CACHE_NEED_PS_DATE;
+ break;
+ case CACHE_NEED_PS_DATE:
+ if (strncmp(buff, "date:", 5) == 0)
+ {
+ /* remove prefix "date: " and LF from len */
+ len -= 6;
+ strzncpy(datebuff, buff + 6, MIN(len, sizeof(datebuff)));
+ state = CACHE_NEED_PS_AUTHOR;
+ }
+ break;
+ case CACHE_NEED_PS_AUTHOR:
+ if (strncmp(buff, "author:", 7) == 0)
+ {
+ /* remove prefix "author: " and LF from len */
+ len -= 8;
+ strzncpy(authbuff, buff + 8, MIN(len, AUTH_STR_MAX));
+ state = CACHE_NEED_PS_TAG;
+ }
+ break;
+ case CACHE_NEED_PS_TAG:
+ if (strncmp(buff, "tag:", 4) == 0)
+ {
+ /* remove prefix "tag: " and LF from len */
+ len -= 5;
+ strzncpy(tagbuff, buff + 5, MIN(len, LOG_STR_MAX));
+ state = CACHE_NEED_PS_TAG_FLAGS;
+ }
+ break;
+ case CACHE_NEED_PS_TAG_FLAGS:
+ if (strncmp(buff, "tag_flags:", 10) == 0)
+ {
+ /* remove prefix "tag_flags: " and LF from len */
+ len -= 11;
+ tag_flags = atoi(buff + 11);
+ state = CACHE_NEED_PS_BRANCH;
+ }
+ break;
+ case CACHE_NEED_PS_BRANCH:
+ if (strncmp(buff, "branch:", 7) == 0)
+ {
+ /* remove prefix "branch: " and LF from len */
+ len -= 8;
+ strzncpy(branchbuff, buff + 8, MIN(len, LOG_STR_MAX));
+ state = CACHE_NEED_PS_BRANCH_ADD;
+ }
+ break;
+ case CACHE_NEED_PS_BRANCH_ADD:
+ if (strncmp(buff, "branch_add:", 11) == 0)
+ {
+ /* remove prefix "branch_add: " and LF from len */
+ len -= 12;
+ branch_add = atoi(buff + 12);
+ state = CACHE_NEED_PS_DESCR;
+ }
+ break;
+ case CACHE_NEED_PS_DESCR:
+ if (strncmp(buff, "descr:", 6) == 0)
+ state = CACHE_NEED_PS_EOD;
+ break;
+ case CACHE_NEED_PS_EOD:
+ if (strcmp(buff, CACHE_DESCR_BOUNDARY) == 0)
+ {
+ debug(DEBUG_STATUS, "patch set %s %s %s %s", datebuff, authbuff, logbuff, branchbuff);
+ ps = get_patch_set(datebuff, logbuff, authbuff, branchbuff, NULL);
+ /* the tag and tag_flags will be assigned by the resolve_global_symbols code
+ * ps->tag = (strlen(tagbuff)) ? get_string(tagbuff) : NULL;
+ * ps->tag_flags = tag_flags;
+ */
+ ps->branch_add = branch_add;
+ state = CACHE_NEED_PS_MEMBERS;
+ }
+ else
+ {
+ /* Make sure we have enough in the buffer */
+ if (strlen(logbuff)+strlen(buff)<LOG_STR_MAX)
+ strcat(logbuff, buff);
+ }
+ break;
+ case CACHE_NEED_PS_MEMBERS:
+ if (strncmp(buff, "members:", 8) == 0)
+ state = CACHE_NEED_PS_EOM;
+ break;
+ case CACHE_NEED_PS_EOM:
+ if (buff[0] == '\n')
+ {
+ datebuff[0] = 0;
+ authbuff[0] = 0;
+ tagbuff[0] = 0;
+ tag_flags = 0;
+ branchbuff[0] = 0;
+ branch_add = 0;
+ logbuff[0] = 0;
+ state = CACHE_NEED_PS;
+ }
+ else
+ {
+ PatchSetMember * psm = create_patch_set_member();
+ parse_cache_revision(psm, buff);
+ patch_set_add_member(ps, psm);
+ }
+ break;
+ }
+ }
+
+ out_close:
+ fclose(fp);
+ out:
+ return cache_date;
+}
+
+enum
+{
+ CR_FILENAME,
+ CR_PRE_REV,
+ CR_POST_REV,
+ CR_DEAD,
+ CR_BRANCH_POINT
+};
+
+static void parse_cache_revision(PatchSetMember * psm, const char * p_buff)
+{
+ /* The format used to generate is:
+ * "file:%s; pre_rev:%s; post_rev:%s; dead:%d; branch_point:%d\n"
+ */
+ char filename[PATH_MAX];
+ char pre[REV_STR_MAX];
+ char post[REV_STR_MAX];
+ int dead = 0;
+ int bp = 0;
+ char buff[BUFSIZ];
+ int state = CR_FILENAME;
+ const char *s;
+ char * p = buff;
+
+ strcpy(buff, p_buff);
+
+ while ((s = strsep(&p, ";")))
+ {
+ char * c = strchr(s, ':');
+
+ if (!c)
+ {
+ debug(DEBUG_APPERROR, "invalid cache revision line '%s'|'%s'", p_buff, s);
+ exit(1);
+ }
+
+ *c++ = 0;
+
+ switch(state)
+ {
+ case CR_FILENAME:
+ strcpy(filename, c);
+ break;
+ case CR_PRE_REV:
+ strcpy(pre, c);
+ break;
+ case CR_POST_REV:
+ strcpy(post, c);
+ break;
+ case CR_DEAD:
+ dead = atoi(c);
+ break;
+ case CR_BRANCH_POINT:
+ bp = atoi(c);
+ break;
+ }
+ state++;
+ }
+
+ psm->file = (CvsFile*)get_hash_object(file_hash, filename);
+
+ if (!psm->file)
+ {
+ debug(DEBUG_APPERROR, "file '%s' not found in hash", filename);
+ exit(1);
+ }
+
+ psm->pre_rev = file_get_revision(psm->file, pre);
+ psm->post_rev = file_get_revision(psm->file, post);
+ psm->post_rev->dead = dead;
+ psm->post_rev->post_psm = psm;
+
+ if (!bp)
+ {
+ if (psm->pre_rev)
+ psm->pre_rev->pre_psm = psm;
+ }
+ else
+ {
+ list_add(&psm->post_rev->link, &psm->pre_rev->branch_children);
+ }
+}
+
+/************ Writing ************/
+
+void write_cache(time_t cache_date)
+{
+ struct hash_entry * file_iter;
+
+ ps_counter = 0;
+
+ if ((cache_fp = cache_open("w")) == NULL)
+ {
+ debug(DEBUG_SYSERROR, "can't open cvsps.cache for write");
+ return;
+ }
+
+ fprintf(cache_fp, "cache version: %d\n", cache_version);
+ fprintf(cache_fp, "cache date: %d\n", (int)cache_date);
+
+ reset_hash_iterator(file_hash);
+
+ while ((file_iter = next_hash_entry(file_hash)))
+ {
+ CvsFile * file = (CvsFile*)file_iter->he_obj;
+ struct hash_entry * rev_iter;
+
+ fprintf(cache_fp, "file: %s\n", file->filename);
+
+ reset_hash_iterator(file->branches);
+ while ((rev_iter = next_hash_entry(file->branches)))
+ {
+ char * rev = (char *)rev_iter->he_key;
+ char * tag = (char *)rev_iter->he_obj;
+ fprintf(cache_fp, "%s: %s\n", rev, tag);
+ }
+
+ fprintf(cache_fp, "\n");
+
+ reset_hash_iterator(file->symbols);
+ while ((rev_iter = next_hash_entry(file->symbols)))
+ {
+ char * tag = (char *)rev_iter->he_key;
+ CvsFileRevision * rev = (CvsFileRevision*)rev_iter->he_obj;
+
+ if (rev->present)
+ fprintf(cache_fp, "%s: %s\n", tag, rev->rev);
+ }
+
+ fprintf(cache_fp, "\n");
+
+ reset_hash_iterator(file->revisions);
+ while ((rev_iter = next_hash_entry(file->revisions)))
+ {
+ CvsFileRevision * rev = (CvsFileRevision*)rev_iter->he_obj;
+ if (rev->present)
+ fprintf(cache_fp, "%s %s\n", rev->rev, rev->branch);
+ }
+
+ fprintf(cache_fp, "\n");
+ }
+
+ fprintf(cache_fp, "\n");
+ walk_all_patch_sets(write_patch_set_to_cache);
+ fclose(cache_fp);
+ cache_fp = NULL;
+}
+
+static void write_patch_set_to_cache(PatchSet * ps)
+{
+ dump_patch_set(cache_fp, ps);
+}
+
+static void dump_patch_set(FILE * fp, PatchSet * ps)
+{
+ struct list_head * next = ps->members.next;
+
+ ps_counter++;
+ fprintf(fp, "patchset: %d\n", ps_counter);
+ fprintf(fp, "date: %d\n", (int)ps->date);
+ fprintf(fp, "author: %s\n", ps->author);
+ fprintf(fp, "tag: %s\n", ps->tag ? ps->tag : "");
+ fprintf(fp, "tag_flags: %d\n", ps->tag_flags);
+ fprintf(fp, "branch: %s\n", ps->branch);
+ fprintf(fp, "branch_add: %d\n", ps->branch_add);
+ fprintf(fp, "descr:\n%s", ps->descr); /* descr is guaranteed to end with LF */
+ fprintf(fp, CACHE_DESCR_BOUNDARY);
+ fprintf(fp, "members:\n");
+
+ while (next != &ps->members)
+ {
+ PatchSetMember * psm = list_entry(next, PatchSetMember, link);
+ int bp = 1;
+
+ /* this actually deduces if this revision is a branch point... */
+ if (!psm->pre_rev || (psm->pre_rev->pre_psm && psm->pre_rev->pre_psm == psm))
+ bp = 0;
+
+ fflush(fp);
+
+ fprintf(fp, "file:%s; pre_rev:%s; post_rev:%s; dead:%d; branch_point:%d\n",
+ psm->file->filename,
+ psm->pre_rev ? psm->pre_rev->rev : "INITIAL", psm->post_rev->rev,
+ psm->post_rev->dead, bp);
+ next = next->next;
+ }
+
+ fprintf(fp, "\n");
+}
+
+/* where's arithmetic?... */