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 int logbufflen
= LOG_STR_MAX
+ 1;
112 char * logbuff
= malloc(logbufflen
);
113 time_t cache_date
= -1;
118 debug(DEBUG_SYSERROR
, "could not malloc %d bytes for logbuff in read_cache", logbufflen
);
124 if (!(fp
= cache_open("r")))
127 /* first line is cache version format "cache version: %d\n" */
128 if (!fgets(buff
, BUFSIZ
, fp
) || strncmp(buff
, "cache version:", 14))
130 debug(DEBUG_APPERROR
, "bad cvsps.cache file");
134 if ((read_version
= atoi(buff
+ 15)) != cache_version
)
136 debug(DEBUG_APPERROR
, "bad cvsps.cache version %d, expecting %d. ignoring cache",
137 read_version
, cache_version
);
141 /* second line is date cache was created, format "cache date: %d\n" */
142 if (!fgets(buff
, BUFSIZ
, fp
) || strncmp(buff
, "cache date:", 11))
144 debug(DEBUG_APPERROR
, "bad cvsps.cache file");
148 cache_date
= atoi(buff
+ 12);
149 debug(DEBUG_STATUS
, "read cache_date %d", (int)cache_date
);
151 while (fgets(buff
, BUFSIZ
, fp
))
153 int len
= strlen(buff
);
157 case CACHE_NEED_FILE
:
158 if (strncmp(buff
, "file:", 5) == 0)
161 f
= create_cvsfile();
162 f
->filename
= xstrdup(buff
+ 6);
163 f
->filename
[len
-1] = 0; /* Remove the \n at the end of line */
164 debug(DEBUG_STATUS
, "read cache filename '%s'", f
->filename
);
165 put_hash_object_ex(file_hash
, f
->filename
, f
, HT_NO_KEYCOPY
, NULL
, NULL
);
166 state
= CACHE_NEED_BRANCHES
;
170 state
= CACHE_NEED_PS
;
173 case CACHE_NEED_BRANCHES
:
178 tag
= strchr(buff
, ':');
184 cvs_file_add_branch(f
, buff
, tag
);
189 f
->have_branches
= 1;
190 state
= CACHE_NEED_SYMBOLS
;
193 case CACHE_NEED_SYMBOLS
:
198 rev
= strchr(buff
, ':');
204 cvs_file_add_symbol(f
, rev
, buff
);
209 state
= CACHE_NEED_REV
;
213 if (isdigit(buff
[0]))
215 char * p
= strchr(buff
, ' ');
218 CvsFileRevision
* rev
;
221 rev
= cvs_file_add_revision(f
, buff
);
222 if (strcmp(rev
->branch
, p
) != 0)
224 debug(DEBUG_APPERROR
, "branch mismatch for %s:%s %s != %s",
225 rev
->file
->filename
, rev
->rev
, rev
->branch
, p
);
231 state
= CACHE_NEED_FILE
;
235 if (strncmp(buff
, "patchset:", 9) == 0)
236 state
= CACHE_NEED_PS_DATE
;
238 case CACHE_NEED_PS_DATE
:
239 if (strncmp(buff
, "date:", 5) == 0)
241 /* remove prefix "date: " and LF from len */
243 strzncpy(datebuff
, buff
+ 6, MIN(len
, sizeof(datebuff
)));
244 state
= CACHE_NEED_PS_AUTHOR
;
247 case CACHE_NEED_PS_AUTHOR
:
248 if (strncmp(buff
, "author:", 7) == 0)
250 /* remove prefix "author: " and LF from len */
252 strzncpy(authbuff
, buff
+ 8, MIN(len
, AUTH_STR_MAX
));
253 state
= CACHE_NEED_PS_TAG
;
256 case CACHE_NEED_PS_TAG
:
257 if (strncmp(buff
, "tag:", 4) == 0)
259 /* remove prefix "tag: " and LF from len */
261 strzncpy(tagbuff
, buff
+ 5, MIN(len
, LOG_STR_MAX
));
262 state
= CACHE_NEED_PS_TAG_FLAGS
;
265 case CACHE_NEED_PS_TAG_FLAGS
:
266 if (strncmp(buff
, "tag_flags:", 10) == 0)
268 /* remove prefix "tag_flags: " and LF from len */
270 tag_flags
= atoi(buff
+ 11);
271 state
= CACHE_NEED_PS_BRANCH
;
274 case CACHE_NEED_PS_BRANCH
:
275 if (strncmp(buff
, "branch:", 7) == 0)
277 /* remove prefix "branch: " and LF from len */
279 strzncpy(branchbuff
, buff
+ 8, MIN(len
, LOG_STR_MAX
));
280 state
= CACHE_NEED_PS_BRANCH_ADD
;
283 case CACHE_NEED_PS_BRANCH_ADD
:
284 if (strncmp(buff
, "branch_add:", 11) == 0)
286 /* remove prefix "branch_add: " and LF from len */
288 branch_add
= atoi(buff
+ 12);
289 state
= CACHE_NEED_PS_DESCR
;
292 case CACHE_NEED_PS_DESCR
:
293 if (strncmp(buff
, "descr:", 6) == 0)
294 state
= CACHE_NEED_PS_EOD
;
296 case CACHE_NEED_PS_EOD
:
297 if (strcmp(buff
, CACHE_DESCR_BOUNDARY
) == 0)
299 debug(DEBUG_STATUS
, "patch set %s %s %s %s", datebuff
, authbuff
, logbuff
, branchbuff
);
300 ps
= get_patch_set(datebuff
, logbuff
, authbuff
, branchbuff
, NULL
);
301 /* the tag and tag_flags will be assigned by the resolve_global_symbols code
302 * ps->tag = (strlen(tagbuff)) ? get_string(tagbuff) : NULL;
303 * ps->tag_flags = tag_flags;
305 ps
->branch_add
= branch_add
;
306 state
= CACHE_NEED_PS_MEMBERS
;
310 /* Make sure we have enough in the buffer */
311 int len
= strlen(buff
);
312 if (strlen(logbuff
) + len
>= LOG_STR_MAX
)
314 logbufflen
+= (len
>= LOG_STR_MAX
? (len
+1) : LOG_STR_MAX
);
315 char * newlogbuff
= realloc(logbuff
, logbufflen
);
316 if (newlogbuff
== NULL
)
318 debug(DEBUG_SYSERROR
, "could not realloc %d bytes for logbuff in read_cache", logbufflen
);
321 logbuff
= newlogbuff
;
323 strcat(logbuff
, buff
);
326 case CACHE_NEED_PS_MEMBERS
:
327 if (strncmp(buff
, "members:", 8) == 0)
328 state
= CACHE_NEED_PS_EOM
;
330 case CACHE_NEED_PS_EOM
:
340 state
= CACHE_NEED_PS
;
344 PatchSetMember
* psm
= create_patch_set_member();
345 parse_cache_revision(psm
, buff
);
346 patch_set_add_member(ps
, psm
);
368 static void parse_cache_revision(PatchSetMember
* psm
, const char * p_buff
)
370 /* The format used to generate is:
371 * "file:%s; pre_rev:%s; post_rev:%s; dead:%d; branch_point:%d\n"
373 char filename
[PATH_MAX
];
374 char pre
[REV_STR_MAX
];
375 char post
[REV_STR_MAX
];
379 int state
= CR_FILENAME
;
383 strcpy(buff
, p_buff
);
385 while ((s
= strsep(&p
, ";")))
387 char * c
= strchr(s
, ':');
391 debug(DEBUG_APPERROR
, "invalid cache revision line '%s'|'%s'", p_buff
, s
);
411 case CR_BRANCH_POINT
:
418 psm
->file
= (CvsFile
*)get_hash_object(file_hash
, filename
);
422 debug(DEBUG_APPERROR
, "file '%s' not found in hash", filename
);
426 psm
->pre_rev
= file_get_revision(psm
->file
, pre
);
427 psm
->post_rev
= file_get_revision(psm
->file
, post
);
428 psm
->post_rev
->dead
= dead
;
429 psm
->post_rev
->post_psm
= psm
;
434 psm
->pre_rev
->pre_psm
= psm
;
438 list_add(&psm
->post_rev
->link
, &psm
->pre_rev
->branch_children
);
442 /************ Writing ************/
444 void write_cache(time_t cache_date
)
446 struct hash_entry
* file_iter
;
450 if ((cache_fp
= cache_open("w")) == NULL
)
452 debug(DEBUG_SYSERROR
, "can't open cvsps.cache for write");
456 fprintf(cache_fp
, "cache version: %d\n", cache_version
);
457 fprintf(cache_fp
, "cache date: %d\n", (int)cache_date
);
459 reset_hash_iterator(file_hash
);
461 while ((file_iter
= next_hash_entry(file_hash
)))
463 CvsFile
* file
= (CvsFile
*)file_iter
->he_obj
;
464 struct hash_entry
* rev_iter
;
466 fprintf(cache_fp
, "file: %s\n", file
->filename
);
468 reset_hash_iterator(file
->branches
);
469 while ((rev_iter
= next_hash_entry(file
->branches
)))
471 char * rev
= (char *)rev_iter
->he_key
;
472 char * tag
= (char *)rev_iter
->he_obj
;
473 fprintf(cache_fp
, "%s: %s\n", rev
, tag
);
476 fprintf(cache_fp
, "\n");
478 reset_hash_iterator(file
->symbols
);
479 while ((rev_iter
= next_hash_entry(file
->symbols
)))
481 char * tag
= (char *)rev_iter
->he_key
;
482 CvsFileRevision
* rev
= (CvsFileRevision
*)rev_iter
->he_obj
;
485 fprintf(cache_fp
, "%s: %s\n", tag
, rev
->rev
);
488 fprintf(cache_fp
, "\n");
490 reset_hash_iterator(file
->revisions
);
491 while ((rev_iter
= next_hash_entry(file
->revisions
)))
493 CvsFileRevision
* rev
= (CvsFileRevision
*)rev_iter
->he_obj
;
495 fprintf(cache_fp
, "%s %s\n", rev
->rev
, rev
->branch
);
498 fprintf(cache_fp
, "\n");
501 fprintf(cache_fp
, "\n");
502 walk_all_patch_sets(write_patch_set_to_cache
);
507 static void write_patch_set_to_cache(PatchSet
* ps
)
509 dump_patch_set(cache_fp
, ps
);
512 static void dump_patch_set(FILE * fp
, PatchSet
* ps
)
514 struct list_head
* next
= ps
->members
.next
;
517 fprintf(fp
, "patchset: %d\n", ps_counter
);
518 fprintf(fp
, "date: %d\n", (int)ps
->date
);
519 fprintf(fp
, "author: %s\n", ps
->author
);
520 fprintf(fp
, "tag: %s\n", ps
->tag
? ps
->tag
: "");
521 fprintf(fp
, "tag_flags: %d\n", ps
->tag_flags
);
522 fprintf(fp
, "branch: %s\n", ps
->branch
);
523 fprintf(fp
, "branch_add: %d\n", ps
->branch_add
);
524 fprintf(fp
, "descr:\n%s", ps
->descr
); /* descr is guaranteed to end with LF */
525 fprintf(fp
, CACHE_DESCR_BOUNDARY
);
526 fprintf(fp
, "members:\n");
528 while (next
!= &ps
->members
)
530 PatchSetMember
* psm
= list_entry(next
, PatchSetMember
, link
);
533 /* this actually deduces if this revision is a branch point... */
534 if (!psm
->pre_rev
|| (psm
->pre_rev
->pre_psm
&& psm
->pre_rev
->pre_psm
== psm
))
539 fprintf(fp
, "file:%s; pre_rev:%s; post_rev:%s; dead:%d; branch_point:%d\n",
541 psm
->pre_rev
? psm
->pre_rev
->rev
: "INITIAL", psm
->post_rev
->rev
,
542 psm
->post_rev
->dead
, bp
);
549 /* where's arithmetic?... */