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(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");
84 /* ************ Reading ************ */
96 CACHE_NEED_PS_TAG_FLAGS
,
98 CACHE_NEED_PS_BRANCH_ADD
,
101 CACHE_NEED_PS_MEMBERS
,
109 int state
= CACHE_NEED_FILE
;
111 PatchSet
* ps
= NULL
;
112 char datebuff
[20] = "";
113 char authbuff
[AUTH_STR_MAX
] = "";
114 char tagbuff
[LOG_STR_MAX
] = "";
116 char branchbuff
[LOG_STR_MAX
] = "";
118 char logbuff
[LOG_STR_MAX
] = "";
119 time_t cache_date
= -1;
122 if (!(fp
= cache_open("r")))
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");
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
);
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");
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
);
155 case CACHE_NEED_FILE
:
156 if (strncmp(buff
, "file:", 5) == 0)
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
;
168 state
= CACHE_NEED_PS
;
171 case CACHE_NEED_BRANCHES
:
176 tag
= strchr(buff
, ':');
182 cvs_file_add_branch(f
, buff
, tag
);
187 f
->have_branches
= 1;
188 state
= CACHE_NEED_SYMBOLS
;
191 case CACHE_NEED_SYMBOLS
:
196 rev
= strchr(buff
, ':');
202 cvs_file_add_symbol(f
, rev
, buff
);
207 state
= CACHE_NEED_REV
;
211 if (isdigit(buff
[0]))
213 char * p
= strchr(buff
, ' ');
216 CvsFileRevision
* rev
;
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
);
229 state
= CACHE_NEED_FILE
;
233 if (strncmp(buff
, "patchset:", 9) == 0)
234 state
= CACHE_NEED_PS_DATE
;
236 case CACHE_NEED_PS_DATE
:
237 if (strncmp(buff
, "date:", 5) == 0)
239 /* remove prefix "date: " and LF from len */
241 strzncpy(datebuff
, buff
+ 6, MIN(len
, sizeof(datebuff
)));
242 state
= CACHE_NEED_PS_AUTHOR
;
245 case CACHE_NEED_PS_AUTHOR
:
246 if (strncmp(buff
, "author:", 7) == 0)
248 /* remove prefix "author: " and LF from len */
250 strzncpy(authbuff
, buff
+ 8, MIN(len
, AUTH_STR_MAX
));
251 state
= CACHE_NEED_PS_TAG
;
254 case CACHE_NEED_PS_TAG
:
255 if (strncmp(buff
, "tag:", 4) == 0)
257 /* remove prefix "tag: " and LF from len */
259 strzncpy(tagbuff
, buff
+ 5, MIN(len
, LOG_STR_MAX
));
260 state
= CACHE_NEED_PS_TAG_FLAGS
;
263 case CACHE_NEED_PS_TAG_FLAGS
:
264 if (strncmp(buff
, "tag_flags:", 10) == 0)
266 /* remove prefix "tag_flags: " and LF from len */
268 tag_flags
= atoi(buff
+ 11);
269 state
= CACHE_NEED_PS_BRANCH
;
272 case CACHE_NEED_PS_BRANCH
:
273 if (strncmp(buff
, "branch:", 7) == 0)
275 /* remove prefix "branch: " and LF from len */
277 strzncpy(branchbuff
, buff
+ 8, MIN(len
, LOG_STR_MAX
));
278 state
= CACHE_NEED_PS_BRANCH_ADD
;
281 case CACHE_NEED_PS_BRANCH_ADD
:
282 if (strncmp(buff
, "branch_add:", 11) == 0)
284 /* remove prefix "branch_add: " and LF from len */
286 branch_add
= atoi(buff
+ 12);
287 state
= CACHE_NEED_PS_DESCR
;
290 case CACHE_NEED_PS_DESCR
:
291 if (strncmp(buff
, "descr:", 6) == 0)
292 state
= CACHE_NEED_PS_EOD
;
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
;
308 /* Make sure we have enough in the buffer */
309 if (strlen(logbuff
)+strlen(buff
)<LOG_STR_MAX
)
310 strcat(logbuff
, buff
);
313 case CACHE_NEED_PS_MEMBERS
:
314 if (strncmp(buff
, "members:", 8) == 0)
315 state
= CACHE_NEED_PS_EOM
;
317 case CACHE_NEED_PS_EOM
:
327 state
= CACHE_NEED_PS
;
331 PatchSetMember
* psm
= create_patch_set_member();
332 parse_cache_revision(psm
, buff
);
333 patch_set_add_member(ps
, psm
);
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
];
365 int state
= CR_FILENAME
;
369 strcpy(buff
, p_buff
);
371 while ((s
= strsep(&p
, ";")))
373 char * c
= strchr(s
, ':');
377 debug(DEBUG_APPERROR
, "invalid cache revision line '%s'|'%s'", p_buff
, s
);
397 case CR_BRANCH_POINT
:
404 psm
->file
= (CvsFile
*)get_hash_object(file_hash
, filename
);
408 debug(DEBUG_APPERROR
, "file '%s' not found in hash", filename
);
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
;
420 psm
->pre_rev
->pre_psm
= psm
;
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
;
436 if ((cache_fp
= cache_open("w")) == NULL
)
438 debug(DEBUG_SYSERROR
, "can't open cvsps.cache for write");
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
;
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
;
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
);
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
;
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
);
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
))
525 fprintf(fp
, "file:%s; pre_rev:%s; post_rev:%s; dead:%d; branch_point:%d\n",
527 psm
->pre_rev
? psm
->pre_rev
->rev
: "INITIAL", psm
->post_rev
->rev
,
528 psm
->post_rev
->dead
, bp
);
535 /* where's arithmetic?... */