mingw-compile.p
[cvsps/4msysgit.git] / cache.c
blobcf8b56d6fac3ee826a8de420c8af6a55d91f8346
1 /*
2 * Copyright 2001, 2002, 2003 David Mansfield and Cobite, Inc.
3 * See COPYING file for license information
4 */
6 #include <stdio.h>
7 #ifdef __MINGW32__
8 #include <tsearch/search.h>
9 char * strsep(char **str, const char *delim);
10 #include <windows.h>
11 #define sleep Sleep
12 #else
13 #include <search.h>
14 #endif
15 #include <string.h>
16 #include <stdlib.h>
17 #include <limits.h>
18 #include <unistd.h>
19 #include <ctype.h>
20 #include <time.h>
22 #include <cbtcommon/hash.h>
23 #include <cbtcommon/debug.h>
25 #include "cache.h"
26 #include "cvsps_types.h"
27 #include "cvsps.h"
28 #include "util.h"
30 #define CACHE_DESCR_BOUNDARY "-=-END CVSPS DESCR-=-\n"
32 /* change this when making the on-disk cache-format invalid */
33 static int cache_version = 1;
35 /* the tree walk API pretty much requries use of globals :-( */
36 static FILE * cache_fp;
37 static int ps_counter;
39 static void write_patch_set_to_cache(PatchSet *);
40 static void parse_cache_revision(PatchSetMember *, const char *);
41 static void dump_patch_set(FILE *, PatchSet *);
43 static FILE *cache_open(char const *mode)
45 char *prefix;
46 char fname[PATH_MAX];
47 char root[PATH_MAX];
48 char repository[PATH_MAX];
49 FILE * fp;
51 /* Get the prefix */
52 prefix = get_cvsps_dir();
53 if (!prefix)
54 return NULL;
56 /* Generate the full path */
57 strcpy(root, root_path);
58 strcpy(repository, repository_path);
60 strrep(root, '/', '#');
61 strrep(repository, '/', '#');
63 snprintf(fname, PATH_MAX, "%s/%s#%s", prefix, root, repository);
65 if (!(fp = fopen(fname, mode)) && *mode == 'r')
67 if ((fp = fopen("CVS/cvsps.cache", mode)))
69 fprintf(stderr, "\n");
70 fprintf(stderr, "****WARNING**** Obsolete CVS/cvsps.cache file found.\n");
71 fprintf(stderr, " New file will be re-written in ~/%s/\n", CVSPS_PREFIX);
72 fprintf(stderr, " Old file will be ignored.\n");
73 fprintf(stderr, " Please manually remove the old file.\n");
74 fprintf(stderr, " Continuing in 5 seconds.\n");
75 sleep(5);
76 fclose(fp);
77 fp = NULL;
81 return fp;
84 /* ************ Reading ************ */
86 enum
88 CACHE_NEED_FILE,
89 CACHE_NEED_BRANCHES,
90 CACHE_NEED_SYMBOLS,
91 CACHE_NEED_REV,
92 CACHE_NEED_PS,
93 CACHE_NEED_PS_DATE,
94 CACHE_NEED_PS_AUTHOR,
95 CACHE_NEED_PS_TAG,
96 CACHE_NEED_PS_TAG_FLAGS,
97 CACHE_NEED_PS_BRANCH,
98 CACHE_NEED_PS_BRANCH_ADD,
99 CACHE_NEED_PS_DESCR,
100 CACHE_NEED_PS_EOD,
101 CACHE_NEED_PS_MEMBERS,
102 CACHE_NEED_PS_EOM
105 time_t read_cache()
107 FILE * fp;
108 char buff[BUFSIZ];
109 int state = CACHE_NEED_FILE;
110 CvsFile * f = NULL;
111 PatchSet * ps = NULL;
112 char datebuff[20] = "";
113 char authbuff[AUTH_STR_MAX] = "";
114 char tagbuff[LOG_STR_MAX] = "";
115 int tag_flags = 0;
116 char branchbuff[LOG_STR_MAX] = "";
117 int branch_add = 0;
118 char logbuff[LOG_STR_MAX] = "";
119 time_t cache_date = -1;
120 int read_version;
122 if (!(fp = cache_open("r")))
123 goto out;
125 /* first line is cache version format "cache version: %d\n" */
126 if (!fgets(buff, BUFSIZ, fp) || strncmp(buff, "cache version:", 14))
128 debug(DEBUG_APPERROR, "bad cvsps.cache file");
129 goto out_close;
132 if ((read_version = atoi(buff + 15)) != cache_version)
134 debug(DEBUG_APPERROR, "bad cvsps.cache version %d, expecting %d. ignoring cache",
135 read_version, cache_version);
136 goto out_close;
139 /* second line is date cache was created, format "cache date: %d\n" */
140 if (!fgets(buff, BUFSIZ, fp) || strncmp(buff, "cache date:", 11))
142 debug(DEBUG_APPERROR, "bad cvsps.cache file");
143 goto out_close;
146 cache_date = atoi(buff + 12);
147 debug(DEBUG_STATUS, "read cache_date %d", (int)cache_date);
149 while (fgets(buff, BUFSIZ, fp))
151 int len = strlen(buff);
153 switch(state)
155 case CACHE_NEED_FILE:
156 if (strncmp(buff, "file:", 5) == 0)
158 len -= 6;
159 f = create_cvsfile();
160 f->filename = xstrdup(buff + 6);
161 f->filename[len-1] = 0; /* Remove the \n at the end of line */
162 debug(DEBUG_STATUS, "read cache filename '%s'", f->filename);
163 put_hash_object_ex(file_hash, f->filename, f, HT_NO_KEYCOPY, NULL, NULL);
164 state = CACHE_NEED_BRANCHES;
166 else
168 state = CACHE_NEED_PS;
170 break;
171 case CACHE_NEED_BRANCHES:
172 if (buff[0] != '\n')
174 char * tag;
176 tag = strchr(buff, ':');
177 if (tag)
179 *tag = 0;
180 tag += 2;
181 buff[len - 1] = 0;
182 cvs_file_add_branch(f, buff, tag);
185 else
187 f->have_branches = 1;
188 state = CACHE_NEED_SYMBOLS;
190 break;
191 case CACHE_NEED_SYMBOLS:
192 if (buff[0] != '\n')
194 char * rev;
196 rev = strchr(buff, ':');
197 if (rev)
199 *rev = 0;
200 rev += 2;
201 buff[len - 1] = 0;
202 cvs_file_add_symbol(f, rev, buff);
205 else
207 state = CACHE_NEED_REV;
209 break;
210 case CACHE_NEED_REV:
211 if (isdigit(buff[0]))
213 char * p = strchr(buff, ' ');
214 if (p)
216 CvsFileRevision * rev;
217 *p++ = 0;
218 buff[len-1] = 0;
219 rev = cvs_file_add_revision(f, buff);
220 if (strcmp(rev->branch, p) != 0)
222 debug(DEBUG_APPERROR, "branch mismatch for %s:%s %s != %s",
223 rev->file->filename, rev->rev, rev->branch, p);
227 else
229 state = CACHE_NEED_FILE;
231 break;
232 case CACHE_NEED_PS:
233 if (strncmp(buff, "patchset:", 9) == 0)
234 state = CACHE_NEED_PS_DATE;
235 break;
236 case CACHE_NEED_PS_DATE:
237 if (strncmp(buff, "date:", 5) == 0)
239 /* remove prefix "date: " and LF from len */
240 len -= 6;
241 strzncpy(datebuff, buff + 6, MIN(len, sizeof(datebuff)));
242 state = CACHE_NEED_PS_AUTHOR;
244 break;
245 case CACHE_NEED_PS_AUTHOR:
246 if (strncmp(buff, "author:", 7) == 0)
248 /* remove prefix "author: " and LF from len */
249 len -= 8;
250 strzncpy(authbuff, buff + 8, MIN(len, AUTH_STR_MAX));
251 state = CACHE_NEED_PS_TAG;
253 break;
254 case CACHE_NEED_PS_TAG:
255 if (strncmp(buff, "tag:", 4) == 0)
257 /* remove prefix "tag: " and LF from len */
258 len -= 5;
259 strzncpy(tagbuff, buff + 5, MIN(len, LOG_STR_MAX));
260 state = CACHE_NEED_PS_TAG_FLAGS;
262 break;
263 case CACHE_NEED_PS_TAG_FLAGS:
264 if (strncmp(buff, "tag_flags:", 10) == 0)
266 /* remove prefix "tag_flags: " and LF from len */
267 len -= 11;
268 tag_flags = atoi(buff + 11);
269 state = CACHE_NEED_PS_BRANCH;
271 break;
272 case CACHE_NEED_PS_BRANCH:
273 if (strncmp(buff, "branch:", 7) == 0)
275 /* remove prefix "branch: " and LF from len */
276 len -= 8;
277 strzncpy(branchbuff, buff + 8, MIN(len, LOG_STR_MAX));
278 state = CACHE_NEED_PS_BRANCH_ADD;
280 break;
281 case CACHE_NEED_PS_BRANCH_ADD:
282 if (strncmp(buff, "branch_add:", 11) == 0)
284 /* remove prefix "branch_add: " and LF from len */
285 len -= 12;
286 branch_add = atoi(buff + 12);
287 state = CACHE_NEED_PS_DESCR;
289 break;
290 case CACHE_NEED_PS_DESCR:
291 if (strncmp(buff, "descr:", 6) == 0)
292 state = CACHE_NEED_PS_EOD;
293 break;
294 case CACHE_NEED_PS_EOD:
295 if (strcmp(buff, CACHE_DESCR_BOUNDARY) == 0)
297 debug(DEBUG_STATUS, "patch set %s %s %s %s", datebuff, authbuff, logbuff, branchbuff);
298 ps = get_patch_set(datebuff, logbuff, authbuff, branchbuff, NULL);
299 /* the tag and tag_flags will be assigned by the resolve_global_symbols code
300 * ps->tag = (strlen(tagbuff)) ? get_string(tagbuff) : NULL;
301 * ps->tag_flags = tag_flags;
303 ps->branch_add = branch_add;
304 state = CACHE_NEED_PS_MEMBERS;
306 else
308 /* Make sure we have enough in the buffer */
309 if (strlen(logbuff)+strlen(buff)<LOG_STR_MAX)
310 strcat(logbuff, buff);
312 break;
313 case CACHE_NEED_PS_MEMBERS:
314 if (strncmp(buff, "members:", 8) == 0)
315 state = CACHE_NEED_PS_EOM;
316 break;
317 case CACHE_NEED_PS_EOM:
318 if (buff[0] == '\n')
320 datebuff[0] = 0;
321 authbuff[0] = 0;
322 tagbuff[0] = 0;
323 tag_flags = 0;
324 branchbuff[0] = 0;
325 branch_add = 0;
326 logbuff[0] = 0;
327 state = CACHE_NEED_PS;
329 else
331 PatchSetMember * psm = create_patch_set_member();
332 parse_cache_revision(psm, buff);
333 patch_set_add_member(ps, psm);
335 break;
339 out_close:
340 fclose(fp);
341 out:
342 return cache_date;
345 enum
347 CR_FILENAME,
348 CR_PRE_REV,
349 CR_POST_REV,
350 CR_DEAD,
351 CR_BRANCH_POINT
354 static void parse_cache_revision(PatchSetMember * psm, const char * p_buff)
356 /* The format used to generate is:
357 * "file:%s; pre_rev:%s; post_rev:%s; dead:%d; branch_point:%d\n"
359 char filename[PATH_MAX];
360 char pre[REV_STR_MAX];
361 char post[REV_STR_MAX];
362 int dead = 0;
363 int bp = 0;
364 char buff[BUFSIZ];
365 int state = CR_FILENAME;
366 const char *s;
367 char * p = buff;
369 strcpy(buff, p_buff);
371 while ((s = strsep(&p, ";")))
373 char * c = strchr(s, ':');
375 if (!c)
377 debug(DEBUG_APPERROR, "invalid cache revision line '%s'|'%s'", p_buff, s);
378 exit(1);
381 *c++ = 0;
383 switch(state)
385 case CR_FILENAME:
386 strcpy(filename, c);
387 break;
388 case CR_PRE_REV:
389 strcpy(pre, c);
390 break;
391 case CR_POST_REV:
392 strcpy(post, c);
393 break;
394 case CR_DEAD:
395 dead = atoi(c);
396 break;
397 case CR_BRANCH_POINT:
398 bp = atoi(c);
399 break;
401 state++;
404 psm->file = (CvsFile*)get_hash_object(file_hash, filename);
406 if (!psm->file)
408 debug(DEBUG_APPERROR, "file '%s' not found in hash", filename);
409 exit(1);
412 psm->pre_rev = file_get_revision(psm->file, pre);
413 psm->post_rev = file_get_revision(psm->file, post);
414 psm->post_rev->dead = dead;
415 psm->post_rev->post_psm = psm;
417 if (!bp)
419 if (psm->pre_rev)
420 psm->pre_rev->pre_psm = psm;
422 else
424 list_add(&psm->post_rev->link, &psm->pre_rev->branch_children);
428 /************ Writing ************/
430 void write_cache(time_t cache_date)
432 struct hash_entry * file_iter;
434 ps_counter = 0;
436 if ((cache_fp = cache_open("w")) == NULL)
438 debug(DEBUG_SYSERROR, "can't open cvsps.cache for write");
439 return;
442 fprintf(cache_fp, "cache version: %d\n", cache_version);
443 fprintf(cache_fp, "cache date: %d\n", (int)cache_date);
445 reset_hash_iterator(file_hash);
447 while ((file_iter = next_hash_entry(file_hash)))
449 CvsFile * file = (CvsFile*)file_iter->he_obj;
450 struct hash_entry * rev_iter;
452 fprintf(cache_fp, "file: %s\n", file->filename);
454 reset_hash_iterator(file->branches);
455 while ((rev_iter = next_hash_entry(file->branches)))
457 char * rev = (char *)rev_iter->he_key;
458 char * tag = (char *)rev_iter->he_obj;
459 fprintf(cache_fp, "%s: %s\n", rev, tag);
462 fprintf(cache_fp, "\n");
464 reset_hash_iterator(file->symbols);
465 while ((rev_iter = next_hash_entry(file->symbols)))
467 char * tag = (char *)rev_iter->he_key;
468 CvsFileRevision * rev = (CvsFileRevision*)rev_iter->he_obj;
470 if (rev->present)
471 fprintf(cache_fp, "%s: %s\n", tag, rev->rev);
474 fprintf(cache_fp, "\n");
476 reset_hash_iterator(file->revisions);
477 while ((rev_iter = next_hash_entry(file->revisions)))
479 CvsFileRevision * rev = (CvsFileRevision*)rev_iter->he_obj;
480 if (rev->present)
481 fprintf(cache_fp, "%s %s\n", rev->rev, rev->branch);
484 fprintf(cache_fp, "\n");
487 fprintf(cache_fp, "\n");
488 walk_all_patch_sets(write_patch_set_to_cache);
489 fclose(cache_fp);
490 cache_fp = NULL;
493 static void write_patch_set_to_cache(PatchSet * ps)
495 dump_patch_set(cache_fp, ps);
498 static void dump_patch_set(FILE * fp, PatchSet * ps)
500 struct list_head * next = ps->members.next;
502 ps_counter++;
503 fprintf(fp, "patchset: %d\n", ps_counter);
504 fprintf(fp, "date: %d\n", (int)ps->date);
505 fprintf(fp, "author: %s\n", ps->author);
506 fprintf(fp, "tag: %s\n", ps->tag ? ps->tag : "");
507 fprintf(fp, "tag_flags: %d\n", ps->tag_flags);
508 fprintf(fp, "branch: %s\n", ps->branch);
509 fprintf(fp, "branch_add: %d\n", ps->branch_add);
510 fprintf(fp, "descr:\n%s", ps->descr); /* descr is guaranteed to end with LF */
511 fprintf(fp, CACHE_DESCR_BOUNDARY);
512 fprintf(fp, "members:\n");
514 while (next != &ps->members)
516 PatchSetMember * psm = list_entry(next, PatchSetMember, link);
517 int bp = 1;
519 /* this actually deduces if this revision is a branch point... */
520 if (!psm->pre_rev || (psm->pre_rev->pre_psm && psm->pre_rev->pre_psm == psm))
521 bp = 0;
523 fflush(fp);
525 fprintf(fp, "file:%s; pre_rev:%s; post_rev:%s; dead:%d; branch_point:%d\n",
526 psm->file->filename,
527 psm->pre_rev ? psm->pre_rev->rev : "INITIAL", psm->post_rev->rev,
528 psm->post_rev->dead, bp);
529 next = next->next;
532 fprintf(fp, "\n");
535 /* where's arithmetic?... */