Dependency handling
[cvsps-yd.git] / cache.c
blob4c51cf7bf4e133bd49fcec997954502a2e7b00fe
1 /*
2 * Copyright 2001, 2002, 2003 David Mansfield and Cobite, Inc.
3 * See COPYING file for license information
4 */
6 #include <stdio.h>
7 #include <search.h>
8 #include <string.h>
9 #include <stdlib.h>
10 #include <limits.h>
11 #include <unistd.h>
12 #include <ctype.h>
13 #include <time.h>
15 #include <cbtcommon/hash.h>
16 #include <cbtcommon/debug.h>
18 #include "cache.h"
19 #include "cvsps_types.h"
20 #include "cvsps.h"
21 #include "util.h"
23 #define CACHE_DESCR_BOUNDARY "-=-END CVSPS DESCR-=-\n"
25 /* change this when making the on-disk cache-format invalid */
26 static int cache_version = 1;
28 /* the tree walk API pretty much requries use of globals :-( */
29 static FILE * cache_fp;
30 static int ps_counter;
32 static void write_patch_set_to_cache(PatchSet *);
33 static void parse_cache_revision(PatchSetMember *, const char *);
34 static void dump_patch_set(FILE *, PatchSet *);
36 static FILE *cache_open(char const *mode)
38 char *prefix;
39 char fname[PATH_MAX];
40 char root[PATH_MAX];
41 char repository[PATH_MAX];
42 FILE * fp;
44 /* Get the prefix */
45 prefix = get_cvsps_dir();
46 if (!prefix)
47 return NULL;
49 /* Generate the full path */
50 strcpy(root, root_path);
51 strcpy(repository, repository_path);
53 strrep(root, '/', '#');
54 strrep(repository, '/', '#');
56 snprintf(fname, PATH_MAX, "%s/%s#%s", prefix, root, repository);
58 if (!(fp = fopen(fname, mode)) && *mode == 'r')
60 if ((fp = fopen("CVS/cvsps.cache", mode)))
62 fprintf(stderr, "\n");
63 fprintf(stderr, "****WARNING**** Obsolete CVS/cvsps.cache file found.\n");
64 fprintf(stderr, " New file will be re-written in ~/%s/\n", CVSPS_PREFIX);
65 fprintf(stderr, " Old file will be ignored.\n");
66 fprintf(stderr, " Please manually remove the old file.\n");
67 fprintf(stderr, " Continuing in 5 seconds.\n");
68 sleep(5);
69 fclose(fp);
70 fp = NULL;
74 return fp;
77 /* ************ Reading ************ */
79 enum
81 CACHE_NEED_FILE,
82 CACHE_NEED_BRANCHES,
83 CACHE_NEED_SYMBOLS,
84 CACHE_NEED_REV,
85 CACHE_NEED_PS,
86 CACHE_NEED_PS_DATE,
87 CACHE_NEED_PS_AUTHOR,
88 CACHE_NEED_PS_TAG,
89 CACHE_NEED_PS_TAG_FLAGS,
90 CACHE_NEED_PS_BRANCH,
91 CACHE_NEED_PS_BRANCH_ADD,
92 CACHE_NEED_PS_DESCR,
93 CACHE_NEED_PS_EOD,
94 CACHE_NEED_PS_MEMBERS,
95 CACHE_NEED_PS_EOM
98 time_t read_cache()
100 FILE * fp;
101 char buff[BUFSIZ];
102 int state = CACHE_NEED_FILE;
103 CvsFile * f = NULL;
104 PatchSet * ps = NULL;
105 char datebuff[20] = "";
106 char authbuff[AUTH_STR_MAX] = "";
107 char tagbuff[LOG_STR_MAX] = "";
108 int tag_flags = 0;
109 char branchbuff[LOG_STR_MAX] = "";
110 int branch_add = 0;
111 char logbuff[LOG_STR_MAX] = "";
112 time_t cache_date = -1;
113 int read_version;
115 if (!(fp = cache_open("r")))
116 goto out;
118 /* first line is cache version format "cache version: %d\n" */
119 if (!fgets(buff, BUFSIZ, fp) || strncmp(buff, "cache version:", 14))
121 debug(DEBUG_APPERROR, "bad cvsps.cache file");
122 goto out_close;
125 if ((read_version = atoi(buff + 15)) != cache_version)
127 debug(DEBUG_APPERROR, "bad cvsps.cache version %d, expecting %d. ignoring cache",
128 read_version, cache_version);
129 goto out_close;
132 /* second line is date cache was created, format "cache date: %d\n" */
133 if (!fgets(buff, BUFSIZ, fp) || strncmp(buff, "cache date:", 11))
135 debug(DEBUG_APPERROR, "bad cvsps.cache file");
136 goto out_close;
139 cache_date = atoi(buff + 12);
140 debug(DEBUG_STATUS, "read cache_date %d", (int)cache_date);
142 while (fgets(buff, BUFSIZ, fp))
144 int len = strlen(buff);
146 switch(state)
148 case CACHE_NEED_FILE:
149 if (strncmp(buff, "file:", 5) == 0)
151 len -= 6;
152 f = create_cvsfile();
153 f->filename = xstrdup(buff + 6);
154 f->filename[len-1] = 0; /* Remove the \n at the end of line */
155 debug(DEBUG_STATUS, "read cache filename '%s'", f->filename);
156 put_hash_object_ex(file_hash, f->filename, f, HT_NO_KEYCOPY, NULL, NULL);
157 state = CACHE_NEED_BRANCHES;
159 else
161 state = CACHE_NEED_PS;
163 break;
164 case CACHE_NEED_BRANCHES:
165 if (buff[0] != '\n')
167 char * tag;
169 tag = strchr(buff, ':');
170 if (tag)
172 *tag = 0;
173 tag += 2;
174 buff[len - 1] = 0;
175 cvs_file_add_branch(f, buff, tag);
178 else
180 f->have_branches = 1;
181 state = CACHE_NEED_SYMBOLS;
183 break;
184 case CACHE_NEED_SYMBOLS:
185 if (buff[0] != '\n')
187 char * rev;
189 rev = strchr(buff, ':');
190 if (rev)
192 *rev = 0;
193 rev += 2;
194 buff[len - 1] = 0;
195 cvs_file_add_symbol(f, rev, buff);
198 else
200 state = CACHE_NEED_REV;
202 break;
203 case CACHE_NEED_REV:
204 if (isdigit(buff[0]))
206 char * p = strchr(buff, ' ');
207 if (p)
209 CvsFileRevision * rev;
210 *p++ = 0;
211 buff[len-1] = 0;
212 rev = cvs_file_add_revision(f, buff);
213 if (strcmp(rev->branch, p) != 0)
215 debug(DEBUG_APPERROR, "branch mismatch for %s:%s %s != %s",
216 rev->file->filename, rev->rev, rev->branch, p);
220 else
222 state = CACHE_NEED_FILE;
224 break;
225 case CACHE_NEED_PS:
226 if (strncmp(buff, "patchset:", 9) == 0)
227 state = CACHE_NEED_PS_DATE;
228 break;
229 case CACHE_NEED_PS_DATE:
230 if (strncmp(buff, "date:", 5) == 0)
232 /* remove prefix "date: " and LF from len */
233 len -= 6;
234 strzncpy(datebuff, buff + 6, MIN(len, sizeof(datebuff)));
235 state = CACHE_NEED_PS_AUTHOR;
237 break;
238 case CACHE_NEED_PS_AUTHOR:
239 if (strncmp(buff, "author:", 7) == 0)
241 /* remove prefix "author: " and LF from len */
242 len -= 8;
243 strzncpy(authbuff, buff + 8, MIN(len, AUTH_STR_MAX));
244 state = CACHE_NEED_PS_TAG;
246 break;
247 case CACHE_NEED_PS_TAG:
248 if (strncmp(buff, "tag:", 4) == 0)
250 /* remove prefix "tag: " and LF from len */
251 len -= 5;
252 strzncpy(tagbuff, buff + 5, MIN(len, LOG_STR_MAX));
253 state = CACHE_NEED_PS_TAG_FLAGS;
255 break;
256 case CACHE_NEED_PS_TAG_FLAGS:
257 if (strncmp(buff, "tag_flags:", 10) == 0)
259 /* remove prefix "tag_flags: " and LF from len */
260 len -= 11;
261 tag_flags = atoi(buff + 11);
262 state = CACHE_NEED_PS_BRANCH;
264 break;
265 case CACHE_NEED_PS_BRANCH:
266 if (strncmp(buff, "branch:", 7) == 0)
268 /* remove prefix "branch: " and LF from len */
269 len -= 8;
270 strzncpy(branchbuff, buff + 8, MIN(len, LOG_STR_MAX));
271 state = CACHE_NEED_PS_BRANCH_ADD;
273 break;
274 case CACHE_NEED_PS_BRANCH_ADD:
275 if (strncmp(buff, "branch_add:", 11) == 0)
277 /* remove prefix "branch_add: " and LF from len */
278 len -= 12;
279 branch_add = atoi(buff + 12);
280 state = CACHE_NEED_PS_DESCR;
282 break;
283 case CACHE_NEED_PS_DESCR:
284 if (strncmp(buff, "descr:", 6) == 0)
285 state = CACHE_NEED_PS_EOD;
286 break;
287 case CACHE_NEED_PS_EOD:
288 if (strcmp(buff, CACHE_DESCR_BOUNDARY) == 0)
290 debug(DEBUG_STATUS, "patch set %s %s %s %s", datebuff, authbuff, logbuff, branchbuff);
291 ps = get_patch_set(datebuff, logbuff, authbuff, branchbuff, NULL);
292 /* the tag and tag_flags will be assigned by the resolve_global_symbols code
293 * ps->tag = (strlen(tagbuff)) ? get_string(tagbuff) : NULL;
294 * ps->tag_flags = tag_flags;
296 ps->branch_add = branch_add;
297 state = CACHE_NEED_PS_MEMBERS;
299 else
301 /* Make sure we have enough in the buffer */
302 if (strlen(logbuff)+strlen(buff)<LOG_STR_MAX)
303 strcat(logbuff, buff);
305 break;
306 case CACHE_NEED_PS_MEMBERS:
307 if (strncmp(buff, "members:", 8) == 0)
308 state = CACHE_NEED_PS_EOM;
309 break;
310 case CACHE_NEED_PS_EOM:
311 if (buff[0] == '\n')
313 datebuff[0] = 0;
314 authbuff[0] = 0;
315 tagbuff[0] = 0;
316 tag_flags = 0;
317 branchbuff[0] = 0;
318 branch_add = 0;
319 logbuff[0] = 0;
320 state = CACHE_NEED_PS;
322 else
324 PatchSetMember * psm = create_patch_set_member();
325 parse_cache_revision(psm, buff);
326 patch_set_add_member(ps, psm);
328 break;
332 out_close:
333 fclose(fp);
334 out:
335 return cache_date;
338 enum
340 CR_FILENAME,
341 CR_PRE_REV,
342 CR_POST_REV,
343 CR_DEAD,
344 CR_BRANCH_POINT
347 static void parse_cache_revision(PatchSetMember * psm, const char * p_buff)
349 /* The format used to generate is:
350 * "file:%s; pre_rev:%s; post_rev:%s; dead:%d; branch_point:%d\n"
352 char filename[PATH_MAX];
353 char pre[REV_STR_MAX];
354 char post[REV_STR_MAX];
355 int dead = 0;
356 int bp = 0;
357 char buff[BUFSIZ];
358 int state = CR_FILENAME;
359 const char *s;
360 char * p = buff;
362 strcpy(buff, p_buff);
364 while ((s = strsep(&p, ";")))
366 char * c = strchr(s, ':');
368 if (!c)
370 debug(DEBUG_APPERROR, "invalid cache revision line '%s'|'%s'", p_buff, s);
371 exit(1);
374 *c++ = 0;
376 switch(state)
378 case CR_FILENAME:
379 strcpy(filename, c);
380 break;
381 case CR_PRE_REV:
382 strcpy(pre, c);
383 break;
384 case CR_POST_REV:
385 strcpy(post, c);
386 break;
387 case CR_DEAD:
388 dead = atoi(c);
389 break;
390 case CR_BRANCH_POINT:
391 bp = atoi(c);
392 break;
394 state++;
397 psm->file = (CvsFile*)get_hash_object(file_hash, filename);
399 if (!psm->file)
401 debug(DEBUG_APPERROR, "file '%s' not found in hash", filename);
402 exit(1);
405 psm->pre_rev = file_get_revision(psm->file, pre);
406 psm->post_rev = file_get_revision(psm->file, post);
407 psm->post_rev->dead = dead;
408 psm->post_rev->post_psm = psm;
410 if (!bp)
412 if (psm->pre_rev)
413 psm->pre_rev->pre_psm = psm;
415 else
417 list_add(&psm->post_rev->link, &psm->pre_rev->branch_children);
421 /************ Writing ************/
423 void write_cache(time_t cache_date)
425 struct hash_entry * file_iter;
427 ps_counter = 0;
429 if ((cache_fp = cache_open("w")) == NULL)
431 debug(DEBUG_SYSERROR, "can't open cvsps.cache for write");
432 return;
435 fprintf(cache_fp, "cache version: %d\n", cache_version);
436 fprintf(cache_fp, "cache date: %d\n", (int)cache_date);
438 reset_hash_iterator(file_hash);
440 while ((file_iter = next_hash_entry(file_hash)))
442 CvsFile * file = (CvsFile*)file_iter->he_obj;
443 struct hash_entry * rev_iter;
445 fprintf(cache_fp, "file: %s\n", file->filename);
447 reset_hash_iterator(file->branches);
448 while ((rev_iter = next_hash_entry(file->branches)))
450 char * rev = (char *)rev_iter->he_key;
451 char * tag = (char *)rev_iter->he_obj;
452 fprintf(cache_fp, "%s: %s\n", rev, tag);
455 fprintf(cache_fp, "\n");
457 reset_hash_iterator(file->symbols);
458 while ((rev_iter = next_hash_entry(file->symbols)))
460 char * tag = (char *)rev_iter->he_key;
461 CvsFileRevision * rev = (CvsFileRevision*)rev_iter->he_obj;
463 if (rev->present)
464 fprintf(cache_fp, "%s: %s\n", tag, rev->rev);
467 fprintf(cache_fp, "\n");
469 reset_hash_iterator(file->revisions);
470 while ((rev_iter = next_hash_entry(file->revisions)))
472 CvsFileRevision * rev = (CvsFileRevision*)rev_iter->he_obj;
473 if (rev->present)
474 fprintf(cache_fp, "%s %s\n", rev->rev, rev->branch);
477 fprintf(cache_fp, "\n");
480 fprintf(cache_fp, "\n");
481 walk_all_patch_sets(write_patch_set_to_cache);
482 fclose(cache_fp);
483 cache_fp = NULL;
486 static void write_patch_set_to_cache(PatchSet * ps)
488 dump_patch_set(cache_fp, ps);
491 static void dump_patch_set(FILE * fp, PatchSet * ps)
493 struct list_head * next = ps->members.next;
495 ps_counter++;
496 fprintf(fp, "patchset: %d\n", ps_counter);
497 fprintf(fp, "date: %d\n", (int)ps->date);
498 fprintf(fp, "author: %s\n", ps->author);
499 fprintf(fp, "tag: %s\n", ps->tag ? ps->tag : "");
500 fprintf(fp, "tag_flags: %d\n", ps->tag_flags);
501 fprintf(fp, "branch: %s\n", ps->branch);
502 fprintf(fp, "branch_add: %d\n", ps->branch_add);
503 fprintf(fp, "descr:\n%s", ps->descr); /* descr is guaranteed to end with LF */
504 fprintf(fp, CACHE_DESCR_BOUNDARY);
505 fprintf(fp, "members:\n");
507 while (next != &ps->members)
509 PatchSetMember * psm = list_entry(next, PatchSetMember, link);
510 int bp = 1;
512 /* this actually deduces if this revision is a branch point... */
513 if (!psm->pre_rev || (psm->pre_rev->pre_psm && psm->pre_rev->pre_psm == psm))
514 bp = 0;
516 fflush(fp);
518 fprintf(fp, "file:%s; pre_rev:%s; post_rev:%s; dead:%d; branch_point:%d\n",
519 psm->file->filename,
520 psm->pre_rev ? psm->pre_rev->rev : "INITIAL", psm->post_rev->rev,
521 psm->post_rev->dead, bp);
522 next = next->next;
525 fprintf(fp, "\n");
528 /* where's arithmetic?... */