2 * Copyright 2001, 2002, 2003 David Mansfield and Cobite, Inc.
3 * See COPYING file for license information
15 #include <cbtcommon/hash.h>
16 #include <cbtcommon/debug.h>
19 #include "cvsps_types.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
)
41 char repository
[PATH_MAX
];
45 prefix
= get_cvsps_dir();
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");
77 /* ************ Reading ************ */
89 CACHE_NEED_PS_TAG_FLAGS
,
91 CACHE_NEED_PS_BRANCH_ADD
,
94 CACHE_NEED_PS_MEMBERS
,
102 int state
= CACHE_NEED_FILE
;
104 PatchSet
* ps
= NULL
;
105 char datebuff
[20] = "";
106 char authbuff
[AUTH_STR_MAX
] = "";
107 char tagbuff
[LOG_STR_MAX
] = "";
109 char branchbuff
[LOG_STR_MAX
] = "";
111 char logbuff
[LOG_STR_MAX
] = "";
112 time_t cache_date
= -1;
115 if (!(fp
= cache_open("r")))
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");
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
);
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");
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
);
148 case CACHE_NEED_FILE
:
149 if (strncmp(buff
, "file:", 5) == 0)
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
;
161 state
= CACHE_NEED_PS
;
164 case CACHE_NEED_BRANCHES
:
169 tag
= strchr(buff
, ':');
175 cvs_file_add_branch(f
, buff
, tag
);
180 f
->have_branches
= 1;
181 state
= CACHE_NEED_SYMBOLS
;
184 case CACHE_NEED_SYMBOLS
:
189 rev
= strchr(buff
, ':');
195 cvs_file_add_symbol(f
, rev
, buff
);
200 state
= CACHE_NEED_REV
;
204 if (isdigit(buff
[0]))
206 char * p
= strchr(buff
, ' ');
209 CvsFileRevision
* rev
;
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
);
222 state
= CACHE_NEED_FILE
;
226 if (strncmp(buff
, "patchset:", 9) == 0)
227 state
= CACHE_NEED_PS_DATE
;
229 case CACHE_NEED_PS_DATE
:
230 if (strncmp(buff
, "date:", 5) == 0)
232 /* remove prefix "date: " and LF from len */
234 strzncpy(datebuff
, buff
+ 6, MIN(len
, sizeof(datebuff
)));
235 state
= CACHE_NEED_PS_AUTHOR
;
238 case CACHE_NEED_PS_AUTHOR
:
239 if (strncmp(buff
, "author:", 7) == 0)
241 /* remove prefix "author: " and LF from len */
243 strzncpy(authbuff
, buff
+ 8, MIN(len
, AUTH_STR_MAX
));
244 state
= CACHE_NEED_PS_TAG
;
247 case CACHE_NEED_PS_TAG
:
248 if (strncmp(buff
, "tag:", 4) == 0)
250 /* remove prefix "tag: " and LF from len */
252 strzncpy(tagbuff
, buff
+ 5, MIN(len
, LOG_STR_MAX
));
253 state
= CACHE_NEED_PS_TAG_FLAGS
;
256 case CACHE_NEED_PS_TAG_FLAGS
:
257 if (strncmp(buff
, "tag_flags:", 10) == 0)
259 /* remove prefix "tag_flags: " and LF from len */
261 tag_flags
= atoi(buff
+ 11);
262 state
= CACHE_NEED_PS_BRANCH
;
265 case CACHE_NEED_PS_BRANCH
:
266 if (strncmp(buff
, "branch:", 7) == 0)
268 /* remove prefix "branch: " and LF from len */
270 strzncpy(branchbuff
, buff
+ 8, MIN(len
, LOG_STR_MAX
));
271 state
= CACHE_NEED_PS_BRANCH_ADD
;
274 case CACHE_NEED_PS_BRANCH_ADD
:
275 if (strncmp(buff
, "branch_add:", 11) == 0)
277 /* remove prefix "branch_add: " and LF from len */
279 branch_add
= atoi(buff
+ 12);
280 state
= CACHE_NEED_PS_DESCR
;
283 case CACHE_NEED_PS_DESCR
:
284 if (strncmp(buff
, "descr:", 6) == 0)
285 state
= CACHE_NEED_PS_EOD
;
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
;
301 /* Make sure we have enough in the buffer */
302 if (strlen(logbuff
)+strlen(buff
)<LOG_STR_MAX
)
303 strcat(logbuff
, buff
);
306 case CACHE_NEED_PS_MEMBERS
:
307 if (strncmp(buff
, "members:", 8) == 0)
308 state
= CACHE_NEED_PS_EOM
;
310 case CACHE_NEED_PS_EOM
:
320 state
= CACHE_NEED_PS
;
324 PatchSetMember
* psm
= create_patch_set_member();
325 parse_cache_revision(psm
, buff
);
326 patch_set_add_member(ps
, psm
);
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
];
358 int state
= CR_FILENAME
;
362 strcpy(buff
, p_buff
);
364 while ((s
= strsep(&p
, ";")))
366 char * c
= strchr(s
, ':');
370 debug(DEBUG_APPERROR
, "invalid cache revision line '%s'|'%s'", p_buff
, s
);
390 case CR_BRANCH_POINT
:
397 psm
->file
= (CvsFile
*)get_hash_object(file_hash
, filename
);
401 debug(DEBUG_APPERROR
, "file '%s' not found in hash", filename
);
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
;
413 psm
->pre_rev
->pre_psm
= psm
;
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
;
429 if ((cache_fp
= cache_open("w")) == NULL
)
431 debug(DEBUG_SYSERROR
, "can't open cvsps.cache for write");
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
;
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
;
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
);
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
;
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
);
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
))
518 fprintf(fp
, "file:%s; pre_rev:%s; post_rev:%s; dead:%d; branch_point:%d\n",
520 psm
->pre_rev
? psm
->pre_rev
->rev
: "INITIAL", psm
->post_rev
->rev
,
521 psm
->post_rev
->dead
, bp
);
528 /* where's arithmetic?... */