2 * Copyright 2001, 2002, 2003 David Mansfield and Cobite, Inc.
3 * See COPYING file for license information
8 #include <tsearch/search.h>
9 char * strsep(char **str
, const char *delim
);
22 #include <cbtcommon/hash.h>
23 #include <cbtcommon/debug.h>
26 #include "cvsps_types.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
)
48 char repository
[PATH_MAX
];
52 prefix
= get_cvsps_dir();
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");
83 debug(DEBUG_APPMSG1
, "Unable to open \"%s\" mode \"%s\"", fname
, mode
);
86 debug(DEBUG_APPMSG1
, "Opened \"%s\" in mode \"%s\"", fname
, mode
);
92 /* ************ Reading ************ */
102 CACHE_NEED_PS_AUTHOR
,
104 CACHE_NEED_PS_TAG_FLAGS
,
105 CACHE_NEED_PS_BRANCH
,
106 CACHE_NEED_PS_BRANCH_ADD
,
109 CACHE_NEED_PS_MEMBERS
,
117 int state
= CACHE_NEED_FILE
;
119 PatchSet
* ps
= NULL
;
120 char datebuff
[20] = "";
121 char authbuff
[AUTH_STR_MAX
] = "";
122 char tagbuff
[LOG_STR_MAX
] = "";
124 char branchbuff
[LOG_STR_MAX
] = "";
126 char logbuff
[LOG_STR_MAX
] = "";
127 time_t cache_date
= -1;
130 if (!(fp
= cache_open("r")))
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");
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
);
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");
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
);
163 case CACHE_NEED_FILE
:
164 if (strncmp(buff
, "file:", 5) == 0)
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
;
176 state
= CACHE_NEED_PS
;
179 case CACHE_NEED_BRANCHES
:
184 tag
= strchr(buff
, ':');
190 cvs_file_add_branch(f
, buff
, tag
);
195 f
->have_branches
= 1;
196 state
= CACHE_NEED_SYMBOLS
;
199 case CACHE_NEED_SYMBOLS
:
204 rev
= strchr(buff
, ':');
210 cvs_file_add_symbol(f
, rev
, buff
);
215 state
= CACHE_NEED_REV
;
219 if (isdigit(buff
[0]))
221 char * p
= strchr(buff
, ' ');
224 CvsFileRevision
* rev
;
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
);
237 state
= CACHE_NEED_FILE
;
241 if (strncmp(buff
, "patchset:", 9) == 0)
242 state
= CACHE_NEED_PS_DATE
;
244 case CACHE_NEED_PS_DATE
:
245 if (strncmp(buff
, "date:", 5) == 0)
247 /* remove prefix "date: " and LF from len */
249 strzncpy(datebuff
, buff
+ 6, MIN(len
, sizeof(datebuff
)));
250 state
= CACHE_NEED_PS_AUTHOR
;
253 case CACHE_NEED_PS_AUTHOR
:
254 if (strncmp(buff
, "author:", 7) == 0)
256 /* remove prefix "author: " and LF from len */
258 strzncpy(authbuff
, buff
+ 8, MIN(len
, AUTH_STR_MAX
));
259 state
= CACHE_NEED_PS_TAG
;
262 case CACHE_NEED_PS_TAG
:
263 if (strncmp(buff
, "tag:", 4) == 0)
265 /* remove prefix "tag: " and LF from len */
267 strzncpy(tagbuff
, buff
+ 5, MIN(len
, LOG_STR_MAX
));
268 state
= CACHE_NEED_PS_TAG_FLAGS
;
271 case CACHE_NEED_PS_TAG_FLAGS
:
272 if (strncmp(buff
, "tag_flags:", 10) == 0)
274 /* remove prefix "tag_flags: " and LF from len */
276 tag_flags
= atoi(buff
+ 11);
277 state
= CACHE_NEED_PS_BRANCH
;
280 case CACHE_NEED_PS_BRANCH
:
281 if (strncmp(buff
, "branch:", 7) == 0)
283 /* remove prefix "branch: " and LF from len */
285 strzncpy(branchbuff
, buff
+ 8, MIN(len
, LOG_STR_MAX
));
286 state
= CACHE_NEED_PS_BRANCH_ADD
;
289 case CACHE_NEED_PS_BRANCH_ADD
:
290 if (strncmp(buff
, "branch_add:", 11) == 0)
292 /* remove prefix "branch_add: " and LF from len */
294 branch_add
= atoi(buff
+ 12);
295 state
= CACHE_NEED_PS_DESCR
;
298 case CACHE_NEED_PS_DESCR
:
299 if (strncmp(buff
, "descr:", 6) == 0)
300 state
= CACHE_NEED_PS_EOD
;
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
;
316 /* Make sure we have enough in the buffer */
317 if (strlen(logbuff
)+strlen(buff
)<LOG_STR_MAX
)
318 strcat(logbuff
, buff
);
321 case CACHE_NEED_PS_MEMBERS
:
322 if (strncmp(buff
, "members:", 8) == 0)
323 state
= CACHE_NEED_PS_EOM
;
325 case CACHE_NEED_PS_EOM
:
335 state
= CACHE_NEED_PS
;
339 PatchSetMember
* psm
= create_patch_set_member();
340 parse_cache_revision(psm
, buff
);
341 patch_set_add_member(ps
, psm
);
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
];
373 int state
= CR_FILENAME
;
377 strcpy(buff
, p_buff
);
379 while ((s
= strsep(&p
, ";")))
381 char * c
= strchr(s
, ':');
385 debug(DEBUG_APPERROR
, "invalid cache revision line '%s'|'%s'", p_buff
, s
);
405 case CR_BRANCH_POINT
:
412 psm
->file
= (CvsFile
*)get_hash_object(file_hash
, filename
);
416 debug(DEBUG_APPERROR
, "file '%s' not found in hash", filename
);
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
;
428 psm
->pre_rev
->pre_psm
= psm
;
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
;
444 if ((cache_fp
= cache_open("w")) == NULL
)
446 debug(DEBUG_SYSERROR
, "can't open cvsps.cache for write");
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
;
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
;
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
);
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
;
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
);
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
))
533 fprintf(fp
, "file:%s; pre_rev:%s; post_rev:%s; dead:%d; branch_point:%d\n",
535 psm
->pre_rev
? psm
->pre_rev
->rev
: "INITIAL", psm
->post_rev
->rev
,
536 psm
->post_rev
->dead
, bp
);
543 /* where's arithmetic?... */