Use regex.[ch] from msysGit's /git
[cvsps/4msysgit.git] / cache.c
blobcafd9234e58a4edcb92cc0aa1107892c41cd4ded
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(root, ':', '.'); /* Windows does not like ':' in file names */
62 strrep(repository, '/', '#');
64 snprintf(fname, PATH_MAX, "%s/%s#%s", prefix, root, repository);
66 if (!(fp = fopen(fname, mode)) && *mode == 'r')
68 if ((fp = fopen("CVS/cvsps.cache", mode)))
70 fprintf(stderr, "\n");
71 fprintf(stderr, "****WARNING**** Obsolete CVS/cvsps.cache file found.\n");
72 fprintf(stderr, " New file will be re-written in ~/%s/\n", CVSPS_PREFIX);
73 fprintf(stderr, " Old file will be ignored.\n");
74 fprintf(stderr, " Please manually remove the old file.\n");
75 fprintf(stderr, " Continuing in 5 seconds.\n");
76 sleep(5);
77 fclose(fp);
78 fp = NULL;
82 if (!fp) {
83 debug(DEBUG_APPMSG1, "Unable to open \"%s\" mode \"%s\"", fname, mode);
85 else {
86 debug(DEBUG_APPMSG1, "Opened \"%s\" in mode \"%s\"", fname, mode);
89 return fp;
92 /* ************ Reading ************ */
94 enum
96 CACHE_NEED_FILE,
97 CACHE_NEED_BRANCHES,
98 CACHE_NEED_SYMBOLS,
99 CACHE_NEED_REV,
100 CACHE_NEED_PS,
101 CACHE_NEED_PS_DATE,
102 CACHE_NEED_PS_AUTHOR,
103 CACHE_NEED_PS_TAG,
104 CACHE_NEED_PS_TAG_FLAGS,
105 CACHE_NEED_PS_BRANCH,
106 CACHE_NEED_PS_BRANCH_ADD,
107 CACHE_NEED_PS_DESCR,
108 CACHE_NEED_PS_EOD,
109 CACHE_NEED_PS_MEMBERS,
110 CACHE_NEED_PS_EOM
113 time_t read_cache()
115 FILE * fp;
116 char buff[BUFSIZ];
117 int state = CACHE_NEED_FILE;
118 CvsFile * f = NULL;
119 PatchSet * ps = NULL;
120 char datebuff[20] = "";
121 char authbuff[AUTH_STR_MAX] = "";
122 char tagbuff[LOG_STR_MAX] = "";
123 int tag_flags = 0;
124 char branchbuff[LOG_STR_MAX] = "";
125 int branch_add = 0;
126 char logbuff[LOG_STR_MAX] = "";
127 time_t cache_date = -1;
128 int read_version;
130 if (!(fp = cache_open("r")))
131 goto out;
133 /* first line is cache version format "cache version: %d\n" */
134 if (!fgets(buff, BUFSIZ, fp) || strncmp(buff, "cache version:", 14))
136 debug(DEBUG_APPERROR, "bad cvsps.cache file");
137 goto out_close;
140 if ((read_version = atoi(buff + 15)) != cache_version)
142 debug(DEBUG_APPERROR, "bad cvsps.cache version %d, expecting %d. ignoring cache",
143 read_version, cache_version);
144 goto out_close;
147 /* second line is date cache was created, format "cache date: %d\n" */
148 if (!fgets(buff, BUFSIZ, fp) || strncmp(buff, "cache date:", 11))
150 debug(DEBUG_APPERROR, "bad cvsps.cache file");
151 goto out_close;
154 cache_date = atoi(buff + 12);
155 debug(DEBUG_STATUS, "read cache_date %d", (int)cache_date);
157 while (fgets(buff, BUFSIZ, fp))
159 int len = strlen(buff);
161 switch(state)
163 case CACHE_NEED_FILE:
164 if (strncmp(buff, "file:", 5) == 0)
166 len -= 6;
167 f = create_cvsfile();
168 f->filename = xstrdup(buff + 6);
169 f->filename[len-1] = 0; /* Remove the \n at the end of line */
170 debug(DEBUG_STATUS, "read cache filename '%s'", f->filename);
171 put_hash_object_ex(file_hash, f->filename, f, HT_NO_KEYCOPY, NULL, NULL);
172 state = CACHE_NEED_BRANCHES;
174 else
176 state = CACHE_NEED_PS;
178 break;
179 case CACHE_NEED_BRANCHES:
180 if (buff[0] != '\n')
182 char * tag;
184 tag = strchr(buff, ':');
185 if (tag)
187 *tag = 0;
188 tag += 2;
189 buff[len - 1] = 0;
190 cvs_file_add_branch(f, buff, tag);
193 else
195 f->have_branches = 1;
196 state = CACHE_NEED_SYMBOLS;
198 break;
199 case CACHE_NEED_SYMBOLS:
200 if (buff[0] != '\n')
202 char * rev;
204 rev = strchr(buff, ':');
205 if (rev)
207 *rev = 0;
208 rev += 2;
209 buff[len - 1] = 0;
210 cvs_file_add_symbol(f, rev, buff);
213 else
215 state = CACHE_NEED_REV;
217 break;
218 case CACHE_NEED_REV:
219 if (isdigit(buff[0]))
221 char * p = strchr(buff, ' ');
222 if (p)
224 CvsFileRevision * rev;
225 *p++ = 0;
226 buff[len-1] = 0;
227 rev = cvs_file_add_revision(f, buff);
228 if (strcmp(rev->branch, p) != 0)
230 debug(DEBUG_APPERROR, "branch mismatch for %s:%s %s != %s",
231 rev->file->filename, rev->rev, rev->branch, p);
235 else
237 state = CACHE_NEED_FILE;
239 break;
240 case CACHE_NEED_PS:
241 if (strncmp(buff, "patchset:", 9) == 0)
242 state = CACHE_NEED_PS_DATE;
243 break;
244 case CACHE_NEED_PS_DATE:
245 if (strncmp(buff, "date:", 5) == 0)
247 /* remove prefix "date: " and LF from len */
248 len -= 6;
249 strzncpy(datebuff, buff + 6, MIN(len, sizeof(datebuff)));
250 state = CACHE_NEED_PS_AUTHOR;
252 break;
253 case CACHE_NEED_PS_AUTHOR:
254 if (strncmp(buff, "author:", 7) == 0)
256 /* remove prefix "author: " and LF from len */
257 len -= 8;
258 strzncpy(authbuff, buff + 8, MIN(len, AUTH_STR_MAX));
259 state = CACHE_NEED_PS_TAG;
261 break;
262 case CACHE_NEED_PS_TAG:
263 if (strncmp(buff, "tag:", 4) == 0)
265 /* remove prefix "tag: " and LF from len */
266 len -= 5;
267 strzncpy(tagbuff, buff + 5, MIN(len, LOG_STR_MAX));
268 state = CACHE_NEED_PS_TAG_FLAGS;
270 break;
271 case CACHE_NEED_PS_TAG_FLAGS:
272 if (strncmp(buff, "tag_flags:", 10) == 0)
274 /* remove prefix "tag_flags: " and LF from len */
275 len -= 11;
276 tag_flags = atoi(buff + 11);
277 state = CACHE_NEED_PS_BRANCH;
279 break;
280 case CACHE_NEED_PS_BRANCH:
281 if (strncmp(buff, "branch:", 7) == 0)
283 /* remove prefix "branch: " and LF from len */
284 len -= 8;
285 strzncpy(branchbuff, buff + 8, MIN(len, LOG_STR_MAX));
286 state = CACHE_NEED_PS_BRANCH_ADD;
288 break;
289 case CACHE_NEED_PS_BRANCH_ADD:
290 if (strncmp(buff, "branch_add:", 11) == 0)
292 /* remove prefix "branch_add: " and LF from len */
293 len -= 12;
294 branch_add = atoi(buff + 12);
295 state = CACHE_NEED_PS_DESCR;
297 break;
298 case CACHE_NEED_PS_DESCR:
299 if (strncmp(buff, "descr:", 6) == 0)
300 state = CACHE_NEED_PS_EOD;
301 break;
302 case CACHE_NEED_PS_EOD:
303 if (strcmp(buff, CACHE_DESCR_BOUNDARY) == 0)
305 debug(DEBUG_STATUS, "patch set %s %s %s %s", datebuff, authbuff, logbuff, branchbuff);
306 ps = get_patch_set(datebuff, logbuff, authbuff, branchbuff, NULL);
307 /* the tag and tag_flags will be assigned by the resolve_global_symbols code
308 * ps->tag = (strlen(tagbuff)) ? get_string(tagbuff) : NULL;
309 * ps->tag_flags = tag_flags;
311 ps->branch_add = branch_add;
312 state = CACHE_NEED_PS_MEMBERS;
314 else
316 /* Make sure we have enough in the buffer */
317 if (strlen(logbuff)+strlen(buff)<LOG_STR_MAX)
318 strcat(logbuff, buff);
320 break;
321 case CACHE_NEED_PS_MEMBERS:
322 if (strncmp(buff, "members:", 8) == 0)
323 state = CACHE_NEED_PS_EOM;
324 break;
325 case CACHE_NEED_PS_EOM:
326 if (buff[0] == '\n')
328 datebuff[0] = 0;
329 authbuff[0] = 0;
330 tagbuff[0] = 0;
331 tag_flags = 0;
332 branchbuff[0] = 0;
333 branch_add = 0;
334 logbuff[0] = 0;
335 state = CACHE_NEED_PS;
337 else
339 PatchSetMember * psm = create_patch_set_member();
340 parse_cache_revision(psm, buff);
341 patch_set_add_member(ps, psm);
343 break;
347 out_close:
348 fclose(fp);
349 out:
350 return cache_date;
353 enum
355 CR_FILENAME,
356 CR_PRE_REV,
357 CR_POST_REV,
358 CR_DEAD,
359 CR_BRANCH_POINT
362 static void parse_cache_revision(PatchSetMember * psm, const char * p_buff)
364 /* The format used to generate is:
365 * "file:%s; pre_rev:%s; post_rev:%s; dead:%d; branch_point:%d\n"
367 char filename[PATH_MAX];
368 char pre[REV_STR_MAX];
369 char post[REV_STR_MAX];
370 int dead = 0;
371 int bp = 0;
372 char buff[BUFSIZ];
373 int state = CR_FILENAME;
374 const char *s;
375 char * p = buff;
377 strcpy(buff, p_buff);
379 while ((s = strsep(&p, ";")))
381 char * c = strchr(s, ':');
383 if (!c)
385 debug(DEBUG_APPERROR, "invalid cache revision line '%s'|'%s'", p_buff, s);
386 exit(1);
389 *c++ = 0;
391 switch(state)
393 case CR_FILENAME:
394 strcpy(filename, c);
395 break;
396 case CR_PRE_REV:
397 strcpy(pre, c);
398 break;
399 case CR_POST_REV:
400 strcpy(post, c);
401 break;
402 case CR_DEAD:
403 dead = atoi(c);
404 break;
405 case CR_BRANCH_POINT:
406 bp = atoi(c);
407 break;
409 state++;
412 psm->file = (CvsFile*)get_hash_object(file_hash, filename);
414 if (!psm->file)
416 debug(DEBUG_APPERROR, "file '%s' not found in hash", filename);
417 exit(1);
420 psm->pre_rev = file_get_revision(psm->file, pre);
421 psm->post_rev = file_get_revision(psm->file, post);
422 psm->post_rev->dead = dead;
423 psm->post_rev->post_psm = psm;
425 if (!bp)
427 if (psm->pre_rev)
428 psm->pre_rev->pre_psm = psm;
430 else
432 list_add(&psm->post_rev->link, &psm->pre_rev->branch_children);
436 /************ Writing ************/
438 void write_cache(time_t cache_date)
440 struct hash_entry * file_iter;
442 ps_counter = 0;
444 if ((cache_fp = cache_open("w")) == NULL)
446 debug(DEBUG_SYSERROR, "can't open cvsps.cache for write");
447 return;
450 fprintf(cache_fp, "cache version: %d\n", cache_version);
451 fprintf(cache_fp, "cache date: %d\n", (int)cache_date);
453 reset_hash_iterator(file_hash);
455 while ((file_iter = next_hash_entry(file_hash)))
457 CvsFile * file = (CvsFile*)file_iter->he_obj;
458 struct hash_entry * rev_iter;
460 fprintf(cache_fp, "file: %s\n", file->filename);
462 reset_hash_iterator(file->branches);
463 while ((rev_iter = next_hash_entry(file->branches)))
465 char * rev = (char *)rev_iter->he_key;
466 char * tag = (char *)rev_iter->he_obj;
467 fprintf(cache_fp, "%s: %s\n", rev, tag);
470 fprintf(cache_fp, "\n");
472 reset_hash_iterator(file->symbols);
473 while ((rev_iter = next_hash_entry(file->symbols)))
475 char * tag = (char *)rev_iter->he_key;
476 CvsFileRevision * rev = (CvsFileRevision*)rev_iter->he_obj;
478 if (rev->present)
479 fprintf(cache_fp, "%s: %s\n", tag, rev->rev);
482 fprintf(cache_fp, "\n");
484 reset_hash_iterator(file->revisions);
485 while ((rev_iter = next_hash_entry(file->revisions)))
487 CvsFileRevision * rev = (CvsFileRevision*)rev_iter->he_obj;
488 if (rev->present)
489 fprintf(cache_fp, "%s %s\n", rev->rev, rev->branch);
492 fprintf(cache_fp, "\n");
495 fprintf(cache_fp, "\n");
496 walk_all_patch_sets(write_patch_set_to_cache);
497 fclose(cache_fp);
498 cache_fp = NULL;
501 static void write_patch_set_to_cache(PatchSet * ps)
503 dump_patch_set(cache_fp, ps);
506 static void dump_patch_set(FILE * fp, PatchSet * ps)
508 struct list_head * next = ps->members.next;
510 ps_counter++;
511 fprintf(fp, "patchset: %d\n", ps_counter);
512 fprintf(fp, "date: %d\n", (int)ps->date);
513 fprintf(fp, "author: %s\n", ps->author);
514 fprintf(fp, "tag: %s\n", ps->tag ? ps->tag : "");
515 fprintf(fp, "tag_flags: %d\n", ps->tag_flags);
516 fprintf(fp, "branch: %s\n", ps->branch);
517 fprintf(fp, "branch_add: %d\n", ps->branch_add);
518 fprintf(fp, "descr:\n%s", ps->descr); /* descr is guaranteed to end with LF */
519 fprintf(fp, CACHE_DESCR_BOUNDARY);
520 fprintf(fp, "members:\n");
522 while (next != &ps->members)
524 PatchSetMember * psm = list_entry(next, PatchSetMember, link);
525 int bp = 1;
527 /* this actually deduces if this revision is a branch point... */
528 if (!psm->pre_rev || (psm->pre_rev->pre_psm && psm->pre_rev->pre_psm == psm))
529 bp = 0;
531 fflush(fp);
533 fprintf(fp, "file:%s; pre_rev:%s; post_rev:%s; dead:%d; branch_point:%d\n",
534 psm->file->filename,
535 psm->pre_rev ? psm->pre_rev->rev : "INITIAL", psm->post_rev->rev,
536 psm->post_rev->dead, bp);
537 next = next->next;
540 fprintf(fp, "\n");
543 /* where's arithmetic?... */