/[CvsGraph]/cvsgraph/cvsgraph.c
ViewVC logotype

Annotate of /cvsgraph/cvsgraph.c

Parent Directory Parent Directory | Revision Log Revision Log | View Revision Graph Revision Graph


Revision 1.14 - (hide annotations)
Wed Nov 21 08:25:04 2001 UTC (15 years, 10 months ago) by bertho
Branch: MAIN
Changes since 1.13: +6 -2 lines
File MIME type: text/plain
Update configure to check for getopt.h which does not exist on Solaris 8.
1 bertho 1.1 /*
2     * CvsGraph graphical representation generator of brances and revisions
3     * of a file in cvs/rcs.
4     *
5     * Copyright (C) 2001 B. Stultiens
6     *
7     * This program is free software; you can redistribute it and/or modify
8     * it under the terms of the GNU General Public License as published by
9     * the Free Software Foundation; either version 2 of the License, or
10     * (at your option) any later version.
11     *
12     * This program is distributed in the hope that it will be useful,
13     * but WITHOUT ANY WARRANTY; without even the implied warranty of
14     * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15     * GNU General Public License for more details.
16     *
17     * You should have received a copy of the GNU General Public License
18     * along with this program; if not, write to the Free Software
19     * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20     */
21    
22 bertho 1.14 #include "config.h"
23    
24 bertho 1.1 #include <stdio.h>
25     #include <stdlib.h>
26     #include <unistd.h>
27     #include <string.h>
28     #include <assert.h>
29     #include <sys/types.h>
30     #include <sys/stat.h>
31     #include <sys/wait.h>
32     #include <fcntl.h>
33     #include <regex.h>
34     #include <errno.h>
35 bertho 1.8 #include <ctype.h>
36 bertho 1.9 #include <time.h>
37    
38 bertho 1.14 #ifdef HAVE_GETOPT_H
39     # include <getopt.h>
40     #endif
41    
42 bertho 1.1 #include <gd.h>
43     #include <gdfontt.h>
44    
45     #include "cvsgraph.h"
46     #include "utils.h"
47     #include "readconf.h"
48 bertho 1.7 #include "rcs.h"
49 bertho 1.1
50 bertho 1.5 #if !defined(HAVE_IMAGE_GIF) && !defined(HAVE_IMAGE_PNG) && !defined(HAVE_IMAGE_JPEG)
51     # error No image output format available. Check libgd
52     #endif
53    
54    
55 bertho 1.1 /*#define DEBUG 1*/
56    
57     #define CONFFILENAME "cvsgraph.conf"
58    
59     #ifndef ETCDIR
60     # define ETCDIR "/usr/local/etc"
61     #endif
62    
63     #ifndef MAX
64     # define MAX(a,b) ((a) > (b) ? (a) : (b))
65     #endif
66    
67     #ifndef MIN
68     # define MIN(a,b) ((a) < (b) ? (a) : (b))
69     #endif
70    
71     #define ALIGN_HL 0x00
72     #define ALIGN_HC 0x01
73     #define ALIGN_HR 0x02
74 bertho 1.9 #define ALIGN_HX 0x0f
75 bertho 1.1 #define ALIGN_VT 0x00
76     #define ALIGN_VC 0x10
77     #define ALIGN_VB 0x20
78     #define ALIGN_VX 0xf0
79 bertho 1.9
80 bertho 1.1 /*
81     **************************************************************************
82     * Globals
83     **************************************************************************
84     */
85    
86     config_t conf;
87 bertho 1.7 int debuglevel;
88 bertho 1.9 color_t white_color = {255, 255, 255, 0};
89     color_t black_color = {0, 0, 0, 0};
90    
91 bertho 1.1
92     /*
93     **************************************************************************
94 bertho 1.7 * Dubug routines
95 bertho 1.1 **************************************************************************
96     */
97 bertho 1.7 void dump_rev(char *p, rev_t *r)
98 bertho 1.1 {
99 bertho 1.7 printf("%s", p);
100     if(r)
101     printf("'%s', '%s', %d\n", r->rev, r->branch, r->isbranch);
102     else
103     printf("<null>\n");
104 bertho 1.1 }
105    
106 bertho 1.7 void dump_id(char *p, char *d)
107 bertho 1.1 {
108 bertho 1.7 printf("%s", p);
109     if(d)
110     printf("'%s'\n", d);
111     else
112     printf("<null>\n");
113 bertho 1.1 }
114    
115 bertho 1.7 void dump_idrev(char *p, idrev_t *t)
116 bertho 1.1 {
117 bertho 1.7 printf("%s", p);
118     if(t)
119 bertho 1.1 {
120 bertho 1.7 printf("'%s' -> ", t->id);
121     dump_rev("", t->rev);
122 bertho 1.1 }
123     else
124 bertho 1.7 printf("<null>\n");
125 bertho 1.1 }
126    
127 bertho 1.7 void dump_tag(char *p, tag_t *t)
128 bertho 1.1 {
129 bertho 1.7 printf("%s", p);
130     if(t)
131 bertho 1.1 {
132 bertho 1.7 printf("'%s' -> ", t->tag);
133     dump_rev("", t->rev);
134 bertho 1.1 }
135 bertho 1.7 else
136     printf("<null>\n");
137 bertho 1.1 }
138    
139 bertho 1.7 void dump_delta(char *p, delta_t *d)
140 bertho 1.1 {
141 bertho 1.7 int i;
142     printf("%sdelta.rev : ", p);
143     dump_rev("", d->rev);
144     printf("%sdelta.date : %s\n", p, d->date);
145     printf("%sdelta.author: %s\n", p, d->author);
146     printf("%sdelta.state : %s\n", p, d->state);
147     for(i = 0; d->branches && i < d->branches->nrevs; i++)
148 bertho 1.1 {
149 bertho 1.7 printf("%sdelta.branch: ", p);
150     dump_rev("", d->branches->revs[i]);
151 bertho 1.1 }
152 bertho 1.7 printf("%sdelta.next : ", p);
153     dump_rev("", d->next);
154     printf("\n");
155 bertho 1.1 }
156    
157 bertho 1.7 void dump_dtext(char *p, dtext_t *d)
158 bertho 1.1 {
159 bertho 1.7 printf("%sdtext.rev : ", p);
160     dump_rev("", d->rev);
161     printf("%sdtext.log : %d bytes\n", p, d->log ? strlen(d->log) : -1);
162     printf("%sdtext.text : %d bytes\n", p, d->text ? strlen(d->text) : -1);
163     printf("\n");
164 bertho 1.1 }
165    
166 bertho 1.7 void dump_rcsfile(rcsfile_t *rcs)
167 bertho 1.1 {
168 bertho 1.7 int i;
169     printf("root : '%s'\n", rcs->root);
170     printf("module : '%s'\n", rcs->module);
171     printf("file : '%s'\n", rcs->file);
172     dump_rev("head : ", rcs->head);
173     dump_rev("branch : ", rcs->branch);
174     printf("access :\n");
175     for(i = 0; rcs->access && i < rcs->access->nids; i++)
176     dump_id("\t", rcs->access->ids[i]);
177     printf("tags :\n");
178     for(i = 0; rcs->tags && i < rcs->tags->ntags; i++)
179     dump_tag("\t", rcs->tags->tags[i]);
180     printf("locks :%s\n", rcs->strict ? " (strict)" : "");
181     for(i = 0; rcs->locks && i < rcs->locks->nidrevs; i++)
182     dump_idrev("\t", rcs->locks->idrevs[i]);
183     printf("comment: '%s'\n", rcs->comment);
184     printf("expand : '%s'\n", rcs->expand ? rcs->expand : "(default -kv)");
185     printf("deltas :\n");
186     for(i = 0; rcs->deltas && i < rcs->deltas->ndeltas; i++)
187     dump_delta("\t", rcs->deltas->deltas[i]);
188     printf("desc : '%s'\n", rcs->desc);
189     printf("dtexts :\n");
190     for(i = 0; rcs->dtexts && i < rcs->dtexts->ndtexts; i++)
191     dump_dtext("\t", rcs->dtexts->dtexts[i]);
192    
193     fflush(stdout);
194 bertho 1.1 }
195    
196 bertho 1.7 /*
197     **************************************************************************
198     * Read the rcs file
199     **************************************************************************
200     */
201     rcsfile_t *get_rcsfile(const char *cvsroot, const char *module, const char *file)
202 bertho 1.1 {
203 bertho 1.7 char *cmd = NULL;
204     int rv;
205 bertho 1.1
206 bertho 1.9 cmd = xmalloc(strlen(cvsroot) + strlen(module) + strlen(file) + 1);
207     sprintf(cmd, "%s%s%s", cvsroot, module, file);
208 bertho 1.7 if(!(rcsin = fopen(cmd, "r")))
209 bertho 1.9 {
210     perror(cmd);
211 bertho 1.1 return NULL;
212 bertho 1.9 }
213 bertho 1.7 input_file = cmd;
214     line_number = 1;
215     rv = rcsparse();
216     fclose(rcsin);
217     if(rv)
218 bertho 1.1 return NULL;
219 bertho 1.7 xfree(cmd);
220     input_file = NULL;
221     rcsfile->root = xstrdup(cvsroot);
222     rcsfile->module = xstrdup(module);
223     rcsfile->file = xstrdup(file);
224     return rcsfile;
225 bertho 1.1 }
226    
227     /*
228     **************************************************************************
229     * Sort and find helpers
230     **************************************************************************
231     */
232 bertho 1.5 int count_dots(const char *s)
233     {
234     int i;
235     for(i = 0; *s; s++)
236     {
237     if(*s == '.')
238     i++;
239     }
240     return i;
241     }
242    
243 bertho 1.7 int compare_rev(int bcmp, const rev_t *r1, const rev_t *r2)
244 bertho 1.5 {
245     int d1, d2;
246 bertho 1.7 char *c1, *c2;
247 bertho 1.5 char *v1, *v2;
248     char *s1, *s2;
249     int retval = 0;
250     assert(r1 != NULL);
251     assert(r2 != NULL);
252 bertho 1.7 if(bcmp)
253     {
254     assert(r1->branch != NULL);
255     assert(r2->branch != NULL);
256     c1 = r1->branch;
257     c2 = r2->branch;
258     }
259     else
260     {
261     assert(r1->rev != NULL);
262     assert(r2->rev != NULL);
263     c1 = r1->rev;
264     c2 = r2->rev;
265     }
266 bertho 1.5
267 bertho 1.7 d1 = count_dots(c1);
268     d2 = count_dots(c2);
269 bertho 1.5 if(d1 != d2)
270     {
271     return d1 - d2;
272     }
273    
274 bertho 1.7 s1 = v1 = xstrdup(c1);
275     s2 = v2 = xstrdup(c2);
276 bertho 1.5 while(1)
277     {
278     char *vc1 = strchr(s1, '.');
279     char *vc2 = strchr(s2, '.');
280     if(vc1 && vc2)
281     *vc1 = *vc2 = '\0';
282     if(*s1 && *s2)
283     {
284     d1 = atoi(s1);
285     d2 = atoi(s2);
286     if(d1 != d2)
287     {
288     retval = d1 - d2;
289     break;
290     }
291     }
292     if(!vc1 || !vc2)
293     break;
294     s1 = vc1 + 1;
295     s2 = vc2 + 1;
296     }
297     xfree(v1);
298     xfree(v2);
299     return retval;
300     }
301    
302 bertho 1.7 /*
303     **************************************************************************
304     * Reorganise the rcsfile for the branches
305     *
306     * Basically, we have a list of deltas (i.e. administrative info on
307     * revisions) and a list of delta text (the actual logs and diffs).
308     * The deltas are linked through the 'next' and the 'branches' fields
309     * which describe the tree-structure of revisions.
310     * The reorganisation means that we put each delta and corresponding
311     * delta text in a revision structure and assign it to a specific
312     * branch. This is required because we want to be able to calculate
313     * the bounding boxes of each branch. The revisions expand vertically
314     * and the branches expand horizontally.
315     * The reorganisation is performed in these steps:
316     * 1 - sort deltas and detla text on revision number for quick lookup
317     * 2 - start at the denoted head revision:
318     * * create a branch structure and add this revision
319     * * for each 'branches' in the delta do:
320     * - walk all 'branches' of the delta and recursively goto 2
321     * with the denoted branch delta as new head
322     * - backlink the newly create sub-branch to the head revision
323     * so that we can draw them recursively
324     * * set head to the 'next' field and goto 2 until no next is
325     * available
326     * 3 - update the administration
327     **************************************************************************
328     */
329     int sort_delta(const void *d1, const void *d2)
330     {
331     return compare_rev(0, (*(delta_t **)d1)->rev, (*(delta_t **)d2)->rev);
332     }
333    
334     int search_delta(const void *r, const void *d)
335 bertho 1.1 {
336 bertho 1.7 return compare_rev(0, (rev_t *)r, (*(delta_t **)d)->rev);
337 bertho 1.1 }
338    
339 bertho 1.7 delta_t *find_delta(delta_t **dl, int n, rev_t *r)
340 bertho 1.1 {
341 bertho 1.7 delta_t **d;
342     d = bsearch(r, dl, n, sizeof(*dl), search_delta);
343     if(!d)
344     return NULL;
345     return *d;
346 bertho 1.1 }
347    
348 bertho 1.7 int sort_dtext(const void *d1, const void *d2)
349 bertho 1.1 {
350 bertho 1.7 return compare_rev(0, (*(dtext_t **)d1)->rev, (*(dtext_t **)d2)->rev);
351 bertho 1.1 }
352    
353 bertho 1.7 int search_dtext(const void *r, const void *d)
354 bertho 1.1 {
355 bertho 1.7 return compare_rev(0, (rev_t *)r, (*(dtext_t **)d)->rev);
356 bertho 1.1 }
357    
358 bertho 1.7 dtext_t *find_dtext(dtext_t **dl, int n, rev_t *r)
359 bertho 1.1 {
360 bertho 1.7 dtext_t **d;
361     d = bsearch(r, dl, n, sizeof(*dl), search_dtext);
362     if(!d)
363 bertho 1.1 return NULL;
364 bertho 1.7 return *d;
365     }
366    
367     rev_t *dup_rev(const rev_t *r)
368     {
369     rev_t *t = xmalloc(sizeof(*t));
370     t->rev = xstrdup(r->rev);
371     t->branch = xstrdup(r->branch);
372     t->isbranch = r->isbranch;
373     return t;
374     }
375    
376     branch_t *new_branch(delta_t *d, dtext_t *t)
377     {
378     branch_t *b = xmalloc(sizeof(*b));
379     revision_t *r = xmalloc(sizeof(*r));
380     r->delta = d;
381     r->dtext = t;
382     r->rev = d->rev;
383     r->branch = b;
384     b->branch = dup_rev(d->rev);
385     b->branch->isbranch = 1;
386     b->nrevs = 1;
387     b->revs = xmalloc(sizeof(b->revs[0]));
388     b->revs[0] = r;
389     return b;
390     }
391    
392     revision_t *add_to_branch(branch_t *b, delta_t *d, dtext_t *t)
393     {
394     revision_t *r = xmalloc(sizeof(*r));
395     r->delta = d;
396     r->dtext = t;
397     r->rev = d->rev;
398     r->branch = b;
399     b->revs = xrealloc(b->revs, (b->nrevs+1) * sizeof(b->revs[0]));
400     b->revs[b->nrevs] = r;
401     b->nrevs++;
402     return r;
403     }
404    
405     void build_branch(branch_t ***bl, int *nbl, delta_t **sdl, int nsdl, dtext_t **sdt, int nsdt, delta_t *head)
406     {
407     branch_t *b;
408     dtext_t *text;
409     revision_t *currev;
410    
411     assert(head != NULL);
412    
413     if(head->flag)
414     {
415     fprintf(stderr, "Circular reference on '%s' in branchpoint\n", head->rev->rev);
416     return;
417     }
418     head->flag++;
419     text = find_dtext(sdt, nsdt, head->rev);
420    
421     /* Create a new branch for this head */
422     b = new_branch(head, text);
423     *bl = xrealloc(*bl, (*nbl+1)*sizeof((*bl)[0]));
424     (*bl)[*nbl] = b;
425     (*nbl)++;
426     currev = b->revs[0];
427     while(1)
428     {
429     /* Process all sub-branches */
430     if(head->branches)
431     {
432     int i;
433     for(i = 0; i < head->branches->nrevs; i++)
434     {
435     delta_t *d = find_delta(sdl, nsdl, head->branches->revs[i]);
436     int btag = *nbl;
437     if(!d)
438     continue;
439     build_branch(bl, nbl, sdl, nsdl, sdt, nsdt, d);
440    
441     /* Set the new branch's origin */
442     (*bl)[btag]->branchpoint = currev;
443    
444     /* Add branch to this revision */
445     currev->branches = xrealloc(currev->branches, (currev->nbranches+1)*sizeof(currev->branches[0]));
446     currev->branches[currev->nbranches] = (*bl)[btag];
447     currev->nbranches++;
448     }
449     }
450    
451     /* Walk through the next list */
452     if(!head->next)
453     return;
454 bertho 1.9
455 bertho 1.7 head = find_delta(sdl, nsdl, head->next);
456     if(!head)
457     {
458     fprintf(stderr, "Next revision (%s) not found in deltalist\n", head->next->rev);
459     return;
460     }
461     if(head->flag)
462     {
463     fprintf(stderr, "Circular reference on '%s'\n", head->rev->rev);
464     return;
465     }
466     head->flag++;
467     text = find_dtext(sdt, nsdt, head->rev);
468     currev = add_to_branch(b, head, text);
469     }
470     }
471    
472     int reorganise_branches(rcsfile_t *rcs)
473     {
474     delta_t **sdelta;
475     int nsdelta;
476     dtext_t **sdtext;
477     int nsdtext;
478     delta_t *head;
479     branch_t **bl;
480     int nbl;
481     int i;
482    
483     assert(rcs->deltas != NULL);
484     assert(rcs->head != NULL);
485    
486     /* Make a new list for quick lookup */
487     nsdelta = rcs->deltas->ndeltas;
488     sdelta = xmalloc(nsdelta * sizeof(sdelta[0]));
489     memcpy(sdelta, rcs->deltas->deltas, nsdelta * sizeof(sdelta[0]));
490     qsort(sdelta, nsdelta, sizeof(sdelta[0]), sort_delta);
491    
492     /* Do the same for the delta text */
493     nsdtext = rcs->dtexts->ndtexts;
494     sdtext = xmalloc(nsdtext * sizeof(sdtext[0]));
495     memcpy(sdtext, rcs->dtexts->dtexts, nsdtext * sizeof(sdtext[0]));
496     qsort(sdtext, nsdtext, sizeof(sdtext[0]), sort_dtext);
497    
498     /* Start from the head of the trunk */
499     head = find_delta(sdelta, nsdelta, rcs->head);
500     if(!head)
501     {
502     fprintf(stderr, "Head revision (%s) not found in deltalist\n", rcs->head->rev);
503     return 0;
504     }
505     bl = NULL;
506     nbl = 0;
507     build_branch(&bl, &nbl, sdelta, nsdelta, sdtext, nsdtext, head);
508    
509     /* Reverse the head branch */
510     for(i = 0; i < bl[0]->nrevs/2; i++)
511     {
512     revision_t *r;
513     r = bl[0]->revs[i];
514     bl[0]->revs[i] = bl[0]->revs[bl[0]->nrevs-i-1];
515     bl[0]->revs[bl[0]->nrevs-i-1] = r;
516     }
517    
518     /* Update the branch-number of the head because it was reversed */
519     xfree(bl[0]->branch->branch);
520     bl[0]->branch->branch = xstrdup(bl[0]->revs[0]->rev->branch);
521    
522     /* Keep the admin */
523     rcs->branches = bl;
524     rcs->nbranches = nbl;
525     rcs->sdelta = sdelta;
526     rcs->nsdelta = nsdelta;
527     rcs->sdtext = sdtext;
528     rcs->nsdtext = nsdtext;
529     rcs->active = bl[0];
530     return 1;
531     }
532    
533     /*
534     **************************************************************************
535     * Assign the symbolic tags to the revisions and branches
536     *
537     * The tags point to revision numbers somewhere in the tree structure
538     * of branches and revisions. First we make a sorted list of all
539     * revisions and then we assign each tag to the proper revision.
540     **************************************************************************
541     */
542     int sort_revision(const void *r1, const void *r2)
543     {
544     return compare_rev(0, (*(revision_t **)r1)->delta->rev, (*(revision_t **)r2)->delta->rev);
545     }
546    
547     int search_revision(const void *t, const void *r)
548     {
549     return compare_rev(0, (rev_t *)t, (*(revision_t **)r)->delta->rev);
550     }
551    
552     int sort_branch(const void *b1, const void *b2)
553     {
554     return compare_rev(1, (*(branch_t **)b1)->branch, (*(branch_t **)b2)->branch);
555 bertho 1.1 }
556    
557 bertho 1.7 int search_branch(const void *t, const void *b)
558 bertho 1.1 {
559 bertho 1.7 return compare_rev(1, (rev_t *)t, (*(branch_t **)b)->branch);
560 bertho 1.1 }
561    
562 bertho 1.7 char *previous_rev(const char *c)
563 bertho 1.1 {
564 bertho 1.7 int dots = count_dots(c);
565     char *cptr;
566     char *r;
567     if(!dots)
568 bertho 1.9 {
569     fprintf(stderr, "FIXME: previous_rev(\"%s\"): Cannot determine parent branch revision\n", c);
570 bertho 1.7 return xstrdup("1.0"); /* FIXME: don't know what the parent is */
571 bertho 1.9 }
572 bertho 1.7 if(dots & 1)
573     {
574     /* Is is a revision we want the parent of */
575     r = xstrdup(c);
576     cptr = strrchr(r, '.');
577     assert(cptr != NULL);
578     if(dots == 1)
579     {
580 bertho 1.9 fprintf(stderr, "FIXME: previous_rev(\"%s\"): Going beyond top-level?\n", c);
581 bertho 1.7 /* FIXME: What is the parent of 1.1? */
582     cptr[1] = '\0';
583     strcat(r, "0");
584     return r;
585     }
586     /* Here we have a "x.x[.x.x]+" case */
587     *cptr = '\0';
588     cptr = strrchr(r, '.');
589     assert(cptr != NULL);
590     *cptr = '\0';
591     return r;
592     }
593     /* It is a branch we want the parent of */
594     r = xstrdup(c);
595     cptr = strrchr(r, '.');
596     assert(cptr != NULL);
597     *cptr = '\0';
598     return r;
599 bertho 1.1 }
600    
601 bertho 1.7 int assign_tags(rcsfile_t *rcs)
602 bertho 1.1 {
603     int i;
604 bertho 1.7 int nr;
605    
606     for(i = nr = 0; i < rcs->nbranches; i++)
607     nr += rcs->branches[i]->nrevs;
608    
609     rcs->srev = xmalloc(nr * sizeof(rcs->srev[0]));
610     rcs->nsrev = nr;
611     for(i = nr = 0; i < rcs->nbranches; i++)
612     {
613     memcpy(&rcs->srev[nr], rcs->branches[i]->revs, rcs->branches[i]->nrevs * sizeof(rcs->branches[i]->revs[0]));
614     nr += rcs->branches[i]->nrevs;
615     }
616    
617     qsort(rcs->srev, rcs->nsrev, sizeof(rcs->srev[0]), sort_revision);
618     qsort(rcs->branches, rcs->nbranches, sizeof(rcs->branches[0]), sort_branch);
619    
620     if(!rcs->branch)
621     {
622     /* The main trunk is the active trunk */
623     rcs->tags->tags = xrealloc(rcs->tags->tags, (rcs->tags->ntags+1)*sizeof(rcs->tags->tags[0]));
624     rcs->tags->tags[rcs->tags->ntags] = xmalloc(sizeof(tag_t));
625     rcs->tags->tags[rcs->tags->ntags]->tag = xstrdup("MAIN");
626     rcs->tags->tags[rcs->tags->ntags]->rev = xmalloc(sizeof(rev_t));
627     rcs->tags->tags[rcs->tags->ntags]->rev->rev = xstrdup(rcs->active->branch->rev);
628     rcs->tags->tags[rcs->tags->ntags]->rev->branch = xstrdup(rcs->active->branch->branch);
629     rcs->tags->tags[rcs->tags->ntags]->rev->isbranch = 1;
630     rcs->tags->ntags++;
631     }
632    
633     /* We should have at least two tags (HEAD and MAIN) */
634     assert(rcs->tags != 0);
635    
636     for(i = 0; i < rcs->tags->ntags; i++)
637     {
638     tag_t *t = rcs->tags->tags[i];
639     if(t->rev->isbranch)
640     {
641     branch_t **b;
642     add_btag:
643     b = bsearch(t->rev, rcs->branches, rcs->nbranches, sizeof(rcs->branches[0]), search_branch);
644     if(!b)
645     {
646     rev_t rev;
647     revision_t **r;
648     /* This happens for the magic branch numbers if there are
649 bertho 1.12 * no commits within the new branch yet. So, we add the
650     * branch and try to continue.
651 bertho 1.7 */
652     rev.rev = previous_rev(t->rev->branch);
653     rev.branch = NULL;
654     rev.isbranch = 0;
655     r = bsearch(&rev, rcs->srev, rcs->nsrev, sizeof(rcs->srev[0]), search_revision);
656     xfree(rev.rev);
657     if(!r)
658     {
659 bertho 1.12 if(!quiet)
660     fprintf(stderr, "No branch found for tag '%s:%s'\n", t->tag, t->rev->branch);
661 bertho 1.7 }
662     else
663     {
664     rcs->branches = xrealloc(rcs->branches, (rcs->nbranches+1)*sizeof(rcs->branches[0]));
665     rcs->branches[rcs->nbranches] = xmalloc(sizeof(branch_t));
666     rcs->branches[rcs->nbranches]->branch = dup_rev(t->rev);
667     rcs->branches[rcs->nbranches]->branchpoint = *r;
668     (*r)->branches = xrealloc((*r)->branches, ((*r)->nbranches+1)*sizeof((*r)->branches[0]));
669     (*r)->branches[(*r)->nbranches] = rcs->branches[rcs->nbranches];
670     (*r)->nbranches++;
671     rcs->nbranches++;
672     /* Resort the branches */
673     qsort(rcs->branches, rcs->nbranches, sizeof(rcs->branches[0]), sort_branch);
674     goto add_btag;
675     }
676     }
677     else
678     {
679     branch_t *bb = *b;
680     bb->tags = xrealloc(bb->tags, (bb->ntags+1)*sizeof(bb->tags[0]));
681     bb->tags[bb->ntags] = t;
682     bb->ntags++;
683     }
684     }
685     else
686     {
687     revision_t **r = bsearch(t->rev, rcs->srev, rcs->nsrev, sizeof(rcs->srev[0]), search_revision);
688     if(!r)
689 bertho 1.12 {
690     if(!quiet)
691     fprintf(stderr, "No revision found for tag '%s:%s'\n", t->tag, t->rev->rev);
692     }
693 bertho 1.7 else
694     {
695     revision_t *rr = *r;
696     rr->tags = xrealloc(rr->tags, (rr->ntags+1)*sizeof(rr->tags[0]));
697     rr->tags[rr->ntags] = t;
698     rr->ntags++;
699     }
700     }
701     }
702    
703     /* We need to reset the first in the list of branches to the
704     * active branch to ensure the drawing of all
705     */
706     if(rcs->active != rcs->branches[0])
707 bertho 1.1 {
708 bertho 1.7 branch_t **b = bsearch(rcs->active->branch, rcs->branches, rcs->nbranches, sizeof(rcs->branches[0]), search_branch);
709     branch_t *t;
710     assert(b != NULL);
711     t = *b;
712     *b = rcs->branches[0];
713     rcs->branches[0] = t;
714 bertho 1.1 }
715 bertho 1.7 return 1;
716 bertho 1.1 }
717    
718     /*
719     **************************************************************************
720 bertho 1.8 * String expansion
721     **************************************************************************
722     */
723     static char *_string;
724     static int _nstring;
725     static int _nastring;
726    
727     void add_string_str(const char *s)
728     {
729     int l = strlen(s) + 1;
730     if(_nstring + l > _nastring)
731     {
732     _nastring += 128;
733     _string = xrealloc(_string, _nastring * sizeof(_string[0]));
734     }
735     memcpy(_string+_nstring, s, l);
736     _nstring += l-1;
737     }
738    
739     void add_string_ch(int ch)
740     {
741     char buf[2];
742     buf[0] = ch;
743     buf[1] = '\0';
744     add_string_str(buf);
745     }
746    
747 bertho 1.9 void add_string_date(const char *d)
748     {
749     struct tm tm, *tmp;
750     int n;
751     time_t t;
752     char *buf;
753     int nbuf;
754    
755     memset(&tm, 0, sizeof(tm));
756     n = sscanf(d, "%d.%d.%d.%d.%d.%d",
757     &tm.tm_year, &tm.tm_mon, &tm.tm_mday,
758     &tm.tm_hour, &tm.tm_min, &tm.tm_sec);
759     tm.tm_mon--;
760     if(tm.tm_year > 1900)
761     tm.tm_year -= 1900;
762     t = mktime(&tm);
763     if(n != 6 || t == (time_t)(-1))
764     {
765     add_string_str("<invalid date>");
766     return;
767     }
768    
769     tmp = localtime(&t);
770     nbuf = strlen(conf.date_format) * 16; /* Should be enough to hold all types of expansions */
771     buf = xmalloc(nbuf);
772     strftime(buf, nbuf, conf.date_format, tmp);
773     add_string_str(buf);
774     xfree(buf);
775     }
776    
777     char *expand_string(const char *s, rcsfile_t *rcs, revision_t *r, rev_t *rev, rev_t *prev, tag_t *tag)
778 bertho 1.8 {
779     char nb[32];
780     char nr[32];
781     char *base;
782    
783     if(!s)
784     return xstrdup("");
785    
786     _nstring = 0;
787     if(_string)
788     _string[0] = '\0';
789    
790     sprintf(nb, "%d", rcs->nbranches);
791     sprintf(nr, "%d", rcs->nsrev);
792     for(; *s; s++)
793     {
794     if(*s == '%')
795     {
796     switch(*++s)
797     {
798 bertho 1.12 case 'c':
799     case 'C':
800     add_string_str(conf.cvsroot);
801     if(*s == 'C' && conf.cvsroot[0] && conf.cvsroot[strlen(conf.cvsroot)-1] == '/')
802     {
803     /* Strip the trailing '/' */
804     _nstring--;
805     _string[_nstring] = '\0';
806     }
807     break;
808 bertho 1.8 case 'f':
809     case 'F':
810     base = strrchr(rcs->file, '/');
811     if(!base)
812     add_string_str(rcs->file);
813     else
814     add_string_str(base+1);
815     if(*s == 'F' && _string[_nstring-1] == 'v' && _string[_nstring-2] == ',')
816     {
817     _nstring -= 2;
818     _string[_nstring] = '\0';
819     }
820     break;
821     case 'p':
822     base = strrchr(rcs->file, '/');
823     if(base)
824     {
825     char *c = xstrdup(rcs->file);
826     base = strrchr(c, '/');
827     assert(base != NULL);
828     base[1] = '\0';
829     add_string_str(c);
830     xfree(c);
831     }
832 bertho 1.9 /*
833     * We should not add anything here because we can encounter
834     * a completely empty path, in which case we do not want
835     * to add any slash. This prevents a inadvertent root redirect.
836     *
837     * else
838     * add_string_str("/");
839     */
840 bertho 1.8 break;
841 bertho 1.12 case 'm':
842     case 'M':
843     add_string_str(conf.cvsmodule);
844     if(*s == 'M' && conf.cvsmodule[0] && conf.cvsmodule[strlen(conf.cvsmodule)-1] == '/')
845     {
846     /* Strip the trailing '/' */
847     _nstring--;
848     _string[_nstring] = '\0';
849     }
850     break;
851 bertho 1.8 case 'r': add_string_str(nr); break;
852     case 'b': add_string_str(nb); break;
853     case '%': add_string_ch('%'); break;
854     case '0': if(conf.expand[0]) add_string_str(conf.expand[0]); break;
855     case '1': if(conf.expand[1]) add_string_str(conf.expand[1]); break;
856     case '2': if(conf.expand[2]) add_string_str(conf.expand[2]); break;
857     case '3': if(conf.expand[3]) add_string_str(conf.expand[3]); break;
858     case '4': if(conf.expand[4]) add_string_str(conf.expand[4]); break;
859     case '5': if(conf.expand[5]) add_string_str(conf.expand[5]); break;
860     case '6': if(conf.expand[6]) add_string_str(conf.expand[6]); break;
861     case '7': if(conf.expand[7]) add_string_str(conf.expand[7]); break;
862     case '8': if(conf.expand[8]) add_string_str(conf.expand[8]); break;
863     case '9': if(conf.expand[9]) add_string_str(conf.expand[9]); break;
864     case 'R': if(rev && rev->rev) add_string_str(rev->rev); break;
865 bertho 1.9 case 'P': if(prev && prev->rev) add_string_str(prev->rev); break;
866 bertho 1.8 case 'B': if(rev && rev->branch) add_string_str(rev->branch); break;
867     case 't': if(tag && tag->tag) add_string_str(tag->tag); break;
868 bertho 1.9 case 'd': if(r && r->delta && r->delta->date) add_string_date(r->delta->date); break;
869     case 's': if(r && r->delta && r->delta->state) add_string_str(r->delta->state); break;
870     case 'a': if(r && r->delta && r->delta->author) add_string_str(r->delta->author); break;
871 bertho 1.8 default:
872     add_string_ch('%');
873     add_string_ch(*s);
874     break;
875     }
876     }
877     else
878     add_string_ch(*s);
879     }
880     return xstrdup(_string);
881     }
882    
883     /*
884     **************************************************************************
885 bertho 1.1 * Drawing routines
886     **************************************************************************
887     */
888     int get_swidth(const char *s, font_t *f)
889     {
890 bertho 1.9 int n;
891     int m;
892     if(!s || !*s)
893 bertho 1.1 return 0;
894 bertho 1.9 for(n = m = 0; *s; n++, s++)
895     {
896     if(*s == '\n')
897     {
898     if(n > m)
899     m = n;
900     n = 0;
901     }
902     }
903     if(n > m)
904     m = n;
905     return m * (*f)->w;
906 bertho 1.1 }
907    
908     int get_sheight(const char *s, font_t *f)
909     {
910     int nl;
911 bertho 1.9 if(!s || !*s)
912 bertho 1.1 return 0;
913     for(nl = 1; *s; s++)
914     {
915     if(*s == '\n' && s[1])
916     nl++;
917     }
918     return nl * (*f)->h;
919     }
920    
921 bertho 1.9 void draw_rbox(gdImagePtr im, int x1, int y1, int x2, int y2, int r, color_t *color, color_t *bgcolor)
922 bertho 1.1 {
923     int r2 = 2*r;
924     gdImageLine(im, x1+r, y1, x2-r, y1, color->id);
925     gdImageLine(im, x1+r, y2, x2-r, y2, color->id);
926     gdImageLine(im, x1, y1+r, x1, y2-r, color->id);
927     gdImageLine(im, x2, y1+r, x2, y2-r, color->id);
928 bertho 1.9 if(conf.box_shadow)
929     {
930     gdImageLine(im, x1+r+1, y2+1, x2-r, y2+1, black_color.id);
931     gdImageLine(im, x2+1, y1+r+1, x2+1, y2-r, black_color.id);
932     }
933 bertho 1.1 if(r)
934     {
935     gdImageArc(im, x1+r, y1+r, r2, r2, 180, 270, color->id);
936     gdImageArc(im, x2-r, y1+r, r2, r2, 270, 360, color->id);
937     gdImageArc(im, x1+r, y2-r, r2, r2, 90, 180, color->id);
938     gdImageArc(im, x2-r, y2-r, r2, r2, 0, 90, color->id);
939 bertho 1.9 if(conf.box_shadow)
940     {
941     /* FIXME: Pixelization is not correct here */
942     gdImageArc(im, x2-r+1, y2-r+1, r2, r2, 0, 90, black_color.id);
943     }
944 bertho 1.1 }
945 bertho 1.9 gdImageFillToBorder(im, (x1+x2)/2, (y1+y2)/2, color->id, bgcolor->id);
946 bertho 1.1 }
947    
948     void draw_string(gdImagePtr im, char *s, font_t *f, int x, int y, int align, color_t *c)
949     {
950     int xx, yy;
951     switch(align & ALIGN_HX)
952     {
953     default:
954     case ALIGN_HL: xx = 0; break;
955     case ALIGN_HC: xx = -get_swidth(s, f)/2; break;
956     case ALIGN_HR: xx = -get_swidth(s, f); break;
957     }
958     switch(align & ALIGN_VX)
959     {
960     default:
961     case ALIGN_VT: yy = 0; break;
962     case ALIGN_VC: yy = -get_sheight(s, f)/2; break;
963     case ALIGN_VB: yy = -get_sheight(s, f); break;
964     }
965     gdImageString(im, *f, x+xx+1, y+yy, s, c->id);
966     }
967    
968 bertho 1.9 void draw_stringnl(gdImagePtr im, char *s, font_t *f, int x, int y, int align, color_t *c)
969     {
970     char *t;
971     char *d;
972     d = s = xstrdup(s);
973     do
974     {
975     t = strchr(s, '\n');
976     if(t)
977     *t = '\0';
978     draw_string(im, s, f, x, y, align, c);
979     y += get_sheight(s, f);
980     s = t+1;
981     } while(t);
982     xfree(d);
983     }
984    
985 bertho 1.1 void draw_rev(gdImagePtr im, int cx, int ty, revision_t *r)
986     {
987     int lx = cx - r->w/2;
988     int rx = lx + r->w;
989     int i;
990 bertho 1.9 draw_rbox(im, lx, ty, rx, ty+r->h, 0, &conf.rev_color, &conf.rev_bgcolor);
991 bertho 1.1 ty += conf.rev_tspace;
992     draw_string(im, r->rev->rev, &conf.rev_font, cx, ty, ALIGN_HC, &conf.rev_color);
993     ty += get_sheight(r->rev->rev, &conf.rev_font);
994 bertho 1.9 draw_stringnl(im, r->revtext, &conf.rev_text_font, cx, ty, ALIGN_HC, &conf.rev_text_color);
995     ty += get_sheight(r->revtext, &conf.rev_text_font);
996 bertho 1.1 for(i = 0; i < r->ntags; i++)
997     {
998     draw_string(im, r->tags[i]->tag, &conf.tag_font, cx, ty, ALIGN_HC, &conf.tag_color);
999     ty += get_sheight(r->tags[i]->tag, &conf.tag_font);
1000     }
1001     }
1002    
1003     void draw_branch(gdImagePtr im, int cx, int ty, branch_t *b)
1004     {
1005     int lx = cx - b->w/2;
1006     int rx = lx + b->w;
1007     int yy;
1008     int i;
1009 bertho 1.7 /*draw_rbox(im, cx-b->tw/2-1, ty-1, cx+b->tw/2+1, ty+b->th+1, 0, &conf.title_color);*/
1010 bertho 1.9 draw_rbox(im, lx, ty, rx, ty+b->h, 5, &conf.branch_color, &conf.branch_bgcolor);
1011 bertho 1.1 yy = conf.branch_tspace;
1012 bertho 1.7 draw_string(im, b->branch->branch, &conf.branch_font, cx, ty+yy, ALIGN_HC, &conf.branch_color);
1013     yy += get_sheight(b->branch->branch, &conf.branch_font);
1014     for(i = 0; i < b->ntags; i++)
1015 bertho 1.1 {
1016 bertho 1.7 draw_string(im, b->tags[i]->tag, &conf.branch_font, cx, ty+yy, ALIGN_HC, &conf.branch_color);
1017     yy += get_sheight(b->tags[i]->tag, &conf.branch_font);
1018 bertho 1.1 }
1019    
1020     ty += b->h;
1021     for(i = 0; i < b->nrevs; i++)
1022     {
1023     gdImageLine(im, cx, ty, cx, ty+conf.rev_minline, conf.rev_color.id);
1024     ty += conf.rev_minline;
1025     draw_rev(im, cx, ty, b->revs[i]);
1026     ty += b->revs[i]->h;
1027     }
1028     }
1029    
1030     void draw_connector(gdImagePtr im, branch_t *b)
1031     {
1032     revision_t *r = b->branchpoint;
1033 bertho 1.7 int x1 = r->cx + r->w/2 + 2;
1034 bertho 1.1 int y1 = r->y + r->h/2;
1035 bertho 1.7 int x2 = b->cx;
1036 bertho 1.1 int y2 = b->y;
1037     gdImageLine(im, x1, y1, x2, y1, conf.branch_color.id);
1038     gdImageLine(im, x2, y1, x2, y2, conf.branch_color.id);
1039     }
1040    
1041 bertho 1.9 void alloc_color(gdImagePtr im, color_t *c)
1042     {
1043     c->id = gdImageColorAllocate(im, c->r, c->g, c->b);
1044     }
1045    
1046 bertho 1.7 gdImagePtr make_image(rcsfile_t *rcs)
1047 bertho 1.1 {
1048     gdImagePtr im;
1049     int i;
1050 bertho 1.8 char *cptr;
1051 bertho 1.1
1052     im = gdImageCreate(rcs->tw+conf.margin_left+conf.margin_right, rcs->th+conf.margin_top+conf.margin_bottom);
1053 bertho 1.9 alloc_color(im, &conf.color_bg);
1054     alloc_color(im, &conf.tag_color);
1055     alloc_color(im, &conf.rev_color);
1056     alloc_color(im, &conf.rev_bgcolor);
1057     alloc_color(im, &conf.rev_text_color);
1058     alloc_color(im, &conf.branch_color);
1059     alloc_color(im, &conf.branch_bgcolor);
1060     alloc_color(im, &conf.title_color);
1061     alloc_color(im, &black_color);
1062     alloc_color(im, &white_color);
1063 bertho 1.1
1064     for(i = 0; i < rcs->nbranches; i++)
1065 bertho 1.7 draw_branch(im, rcs->branches[i]->cx, rcs->branches[i]->y, rcs->branches[i]);
1066 bertho 1.1 for(i = 0; i < rcs->nbranches; i++)
1067     {
1068     if(rcs->branches[i]->branchpoint)
1069     draw_connector(im, rcs->branches[i]);
1070     }
1071 bertho 1.9 cptr = expand_string(conf.title, rcs, NULL, NULL, NULL, NULL);
1072     draw_stringnl(im, cptr, &conf.title_font, conf.title_x, conf.title_y, conf.title_align, &conf.title_color);
1073 bertho 1.8 xfree(cptr);
1074 bertho 1.1
1075     return im;
1076     }
1077    
1078 bertho 1.7 /*
1079     **************************************************************************
1080     * Layout routines
1081     **************************************************************************
1082     */
1083 bertho 1.1 void move_branch(branch_t *b, int x, int y)
1084     {
1085     int i;
1086 bertho 1.7 b->cx += x;
1087 bertho 1.1 b->y += y;
1088     for(i = 0; i < b->nrevs; i++)
1089     {
1090 bertho 1.7 b->revs[i]->cx += x;
1091 bertho 1.1 b->revs[i]->y += y;
1092     }
1093     }
1094    
1095 bertho 1.6 void reposition_branch(revision_t *r, int *x, int *w)
1096     {
1097     int i, j;
1098     for(j = 0; j < r->nbranches; j++)
1099     {
1100     branch_t *b = r->branches[j];
1101 bertho 1.7 *x += *w + conf.rev_minline + b->tw/2 - b->cx;
1102 bertho 1.6 *w = b->tw/2;
1103 bertho 1.7 move_branch(b, *x, r->y + r->h/2 + conf.branch_connect);
1104     *x = b->cx;
1105 bertho 1.6 /* Recurse to move branches of branched revisions */
1106     for(i = b->nrevs-1; i >= 0; i--)
1107     {
1108     reposition_branch(b->revs[i], x, w);
1109     }
1110     }
1111     }
1112    
1113 bertho 1.1 void rect_union(int *x, int *y, int *w, int *h, branch_t *b)
1114     {
1115     int x1 = *x;
1116     int x2 = x1 + *w;
1117     int y1 = *y;
1118     int y2 = y1 + *h;
1119 bertho 1.7 int xx1 = b->cx - b->tw/2;
1120 bertho 1.1 int xx2 = xx1 + b->tw;
1121     int yy1 = b->y;
1122     int yy2 = yy1 + b->th;
1123     x1 = MIN(x1, xx1);
1124     x2 = MAX(x2, xx2);
1125     y1 = MIN(y1, yy1);
1126     y2 = MAX(y2, yy2);
1127     *x = x1;
1128     *y = y1;
1129     *w = x2 - x1;
1130     *h = y2 - y1;
1131     }
1132    
1133 bertho 1.7 int branch_intersects(int top, int bottom, int left, branch_t *b)
1134     {
1135     int br = b->cx + b->tw/2;
1136     int bt = b->y - conf.branch_connect - conf.branch_margin/2;
1137     int bb = b->y + b->th + conf.branch_margin/2;
1138     return !(bt > bottom || bb < top || br >= left);
1139     }
1140    
1141     int kern_branch(rcsfile_t *rcs, branch_t *b)
1142     {
1143     int left = b->cx - b->tw/2;
1144     int top = b->y - conf.branch_connect - conf.branch_margin/2;
1145     int bottom = b->y + b->th + conf.branch_margin/2;
1146     int i;
1147     int xpos = 0;
1148    
1149     for(i = 0; i < rcs->nbranches; i++)
1150     {
1151     branch_t *bp = rcs->branches[i];
1152     if(bp == b)
1153     continue;
1154     if(branch_intersects(top, bottom, left, bp))
1155     {
1156     int m = bp->cx + bp->tw/2 + conf.branch_margin;
1157     if(m > xpos)
1158     xpos = m;
1159     }
1160     }
1161     if(xpos && (b->cx - b->tw/2) - xpos > 0)
1162     {
1163     move_branch(b, xpos - (b->cx - b->tw/2), 0);
1164     return 1;
1165     }
1166     return 0;
1167     }
1168    
1169     void make_layout(rcsfile_t *rcs)
1170 bertho 1.1 {
1171     int i, j;
1172     int x, y;
1173     int w, h;
1174     int w2;
1175 bertho 1.7 int moved;
1176 bertho 1.1
1177     /* Calculate the box-sizes of the revisions */
1178 bertho 1.7 for(i = 0; i < rcs->nsrev; i++)
1179 bertho 1.1 {
1180     revision_t *rp;
1181     int w;
1182     int h;
1183 bertho 1.7 rp = rcs->srev[i];
1184 bertho 1.9 rp->revtext = expand_string(conf.rev_text, rcs, rp, rp->rev, NULL, rp->ntags ? rp->tags[0] : NULL);
1185     w = get_swidth(rp->revtext, &conf.rev_text_font);
1186     j = get_swidth(rp->rev->rev, &conf.rev_font);
1187     if(j > w)
1188     w = j;
1189     h = get_sheight(rp->revtext, &conf.rev_text_font) + get_sheight(rp->rev->rev, &conf.rev_font);
1190 bertho 1.1 for(j = 0; j < rp->ntags; j++)
1191     {
1192     int ww = get_swidth(rp->tags[j]->tag, &conf.tag_font);
1193     if(ww > w) w = ww;
1194     h += get_sheight(rp->tags[j]->tag, &conf.tag_font) + conf.rev_separator;
1195     }
1196     rp->w = w + conf.rev_lspace + conf.rev_rspace;
1197     rp->h = h + conf.rev_tspace + conf.rev_bspace;
1198     }
1199    
1200     /* Calculate the box-sizes of the branches */
1201     for(i = 0; i < rcs->nbranches; i++)
1202     {
1203     branch_t *bp = rcs->branches[i];
1204     int w;
1205     int h;
1206 bertho 1.7 w = get_swidth(bp->branch->branch, &conf.branch_font);
1207     h = get_sheight(bp->branch->branch, &conf.branch_font);
1208     for(j = 0; j < bp->ntags; j++)
1209 bertho 1.1 {
1210 bertho 1.7 int ww = get_swidth(bp->tags[j]->tag, &conf.branch_font);
1211 bertho 1.1 if(ww > w) w = ww;
1212 bertho 1.7 h += get_sheight(bp->tags[j]->tag, &conf.branch_font);
1213 bertho 1.1 }
1214     w += conf.branch_lspace + conf.branch_rspace;
1215     h += conf.branch_tspace + conf.branch_bspace;
1216     bp->w = w;
1217     bp->h = h;
1218     for(j = 0; j < bp->nrevs; j++)
1219     {
1220     if(bp->revs[j]->w > w)
1221     w = bp->revs[j]->w;
1222     h += bp->revs[j]->h + conf.rev_minline;
1223     }
1224     bp->th = h;
1225     bp->tw = w;
1226     }
1227    
1228     /* Calculate the relative positions of revs in a branch */
1229     for(i = 0; i < rcs->nbranches; i++)
1230     {
1231     branch_t *b = rcs->branches[i];
1232     x = b->tw/2;
1233     y = b->h;
1234 bertho 1.7 b->cx = x;
1235 bertho 1.1 b->y = 0;
1236     for(j = 0; j < b->nrevs; j++)
1237     {
1238     y += conf.rev_minline;
1239 bertho 1.7 b->revs[j]->cx = x;
1240 bertho 1.1 b->revs[j]->y = y;
1241     y += b->revs[j]->h;
1242     }
1243     }
1244    
1245 bertho 1.6 /* Reposition the branches */
1246 bertho 1.7 x = rcs->branches[0]->cx;
1247 bertho 1.1 w2 = rcs->branches[0]->tw / 2;
1248     for(i = rcs->branches[0]->nrevs-1; i >= 0; i--)
1249     {
1250 bertho 1.6 reposition_branch(rcs->branches[0]->revs[i], &x, &w2);
1251 bertho 1.1 }
1252    
1253 bertho 1.7 /* Try to move branches left if there is room (kerning) */
1254     for(moved = 1; moved; )
1255     {
1256     moved = 0;
1257     for(i = 1; i < rcs->nbranches; i++)
1258     {
1259     moved += kern_branch(rcs, rcs->branches[i]);
1260     }
1261     }
1262    
1263     /* Move everything w.r.t. the top-left margin */
1264 bertho 1.1 for(i = 0; i < rcs->nbranches; i++)
1265     move_branch(rcs->branches[i], conf.margin_left, conf.margin_top);
1266    
1267     /* Calculate overall image size */
1268 bertho 1.7 x = rcs->branches[0]->cx - rcs->branches[0]->tw/2;
1269 bertho 1.1 y = rcs->branches[0]->y;
1270     w = rcs->branches[0]->tw;
1271     h = rcs->branches[0]->th;
1272     for(i = 1; i < rcs->nbranches; i++)
1273     rect_union(&x, &y, &w, &h, rcs->branches[i]);
1274     rcs->tw = w;
1275     rcs->th = h;
1276     }
1277    
1278     /*
1279     **************************************************************************
1280 bertho 1.6 * Imagemap functions
1281     **************************************************************************
1282     */
1283 bertho 1.7 void make_imagemap(rcsfile_t *rcs, FILE *fp)
1284 bertho 1.6 {
1285     int i, j;
1286 bertho 1.8 char *href;
1287     char *alt;
1288 bertho 1.6 fprintf(fp, "<map name=\"%s\">\n", conf.map_name);
1289     for(i = 0; i < rcs->nbranches; i++)
1290     {
1291     branch_t *b = rcs->branches[i];
1292 bertho 1.8 tag_t *tag = b->ntags ? b->tags[0] : NULL;
1293 bertho 1.9 href = expand_string(conf.map_branch_href, rcs, NULL, b->branch, NULL, tag);
1294     alt = expand_string(conf.map_branch_alt, rcs, NULL, b->branch, NULL, tag);
1295 bertho 1.8 fprintf(fp, "\t<area shape=\"rect\" %s coords=\"%d,%d,%d,%d\" %s>\n",
1296     href,
1297 bertho 1.7 b->cx - b->w/2, b->y, b->cx + b->w/2, b->y + b->h,
1298 bertho 1.8 alt);
1299     xfree(href);
1300     xfree(alt);
1301 bertho 1.6 for(j = 0; j < b->nrevs; j++)
1302     {
1303     revision_t *r = b->revs[j];
1304 bertho 1.13 revision_t* r1;
1305     int xoff;
1306     int x1;
1307     int x2;
1308     int y1;
1309    
1310 bertho 1.8 tag = r->ntags ? r->tags[0] : NULL;
1311 bertho 1.9 href = expand_string(conf.map_rev_href, rcs, r, r->rev, NULL, tag);
1312     alt = expand_string(conf.map_rev_alt, rcs, r, r->rev, NULL, tag);
1313 bertho 1.8 fprintf(fp, "\t<area shape=\"rect\" %s coords=\"%d,%d,%d,%d\" %s>\n",
1314     href,
1315 bertho 1.7 r->cx - r->w/2, r->y, r->cx + r->w/2, r->y + r->h,
1316 bertho 1.8 alt);
1317     xfree(href);
1318     xfree(alt);
1319 bertho 1.13 if ( j > 0 || b->branchpoint )
1320 bertho 1.9 {
1321 bertho 1.13 if ( j > 0 )
1322     {
1323     r1 = b->revs[j-1];
1324     xoff = MIN(r->w, r1->w)/4;
1325     y1 = r1->y + r1->h;
1326     }
1327     else
1328     {
1329     r1 = b->branchpoint;
1330     xoff = MIN(r->w, b->w)/4;
1331     y1 = b->y + b->h;
1332     }
1333     x1 = r->cx - xoff;
1334     x2 = r->cx + xoff;
1335    
1336     href = expand_string(conf.map_diff_href, rcs, r, r->rev, r1->rev, tag);
1337     alt = expand_string(conf.map_diff_alt, rcs, r, r->rev, r1->rev, tag);
1338     fprintf(fp, "\t<area shape=\"rect\" %s coords=\"%d,%d,%d,%d\" %s>\n",
1339     href,
1340     x1, y1 + 1, x2, r->y - 1,
1341     alt);
1342     xfree(href);
1343     xfree(alt);
1344 bertho 1.9 }
1345 bertho 1.6 }
1346     }
1347     fprintf(fp, "</map>\n");
1348     }
1349    
1350     /*
1351     **************************************************************************
1352 bertho 1.1 * Configuration
1353     **************************************************************************
1354     */
1355     int read_config(const char *path)
1356     {
1357     FILE *fp;
1358     int r;
1359 bertho 1.7
1360 bertho 1.1 if(path)
1361     {
1362     if((fp = fopen(path, "r")) == NULL)
1363     {
1364     return 0;
1365     }
1366 bertho 1.7 else
1367     input_file = path;
1368 bertho 1.1 }
1369     else
1370     {
1371     if((fp = fopen("./" CONFFILENAME, "r")) == NULL)
1372     {
1373     if((fp = fopen(ETCDIR "/" CONFFILENAME, "r")) == NULL)
1374     {
1375     return 0;
1376     }
1377 bertho 1.7 else
1378     input_file = ETCDIR "/" CONFFILENAME;
1379 bertho 1.1 }
1380 bertho 1.7 else
1381     input_file = "./" CONFFILENAME;
1382 bertho 1.1 }
1383    
1384     yyin = fp;
1385     r = yyparse();
1386     fclose(fp);
1387 bertho 1.7 input_file = NULL;
1388 bertho 1.1 return r == 0;
1389     }
1390    
1391     /*
1392     **************************************************************************
1393     * Program entry
1394     **************************************************************************
1395     */
1396     static const char usage_str[] =
1397     "Usage: cvsgraph [options] <file>\n"
1398 bertho 1.8 " -c <file> Read alternative config from <file>\n"
1399     " -d <level> Enable debug mode at <level>\n"
1400     " -h This message\n"
1401     " -i Generate an imagemap instead of image\n"
1402     " -M <name> Use <name> as imagemap name\n"
1403     " -m <mod> Use <mod> as cvs module\n"
1404     " -o <file> Output to <file>\n"
1405     " -q Be quiet (i.e. no warnings)\n"
1406     " -r <path> Use <path> as cvsroot path\n"
1407     " -V Print version and exit\n"
1408     " -[0-9] <txt> Use <txt> for expansion\n"
1409 bertho 1.1 ;
1410    
1411 bertho 1.12 #define VERSION_STR "1.1.2"
1412 bertho 1.1 #define NOTICE_STR "Copyright (c) 2001 B.Stultiens"
1413    
1414 bertho 1.9 void append_slash(char **path)
1415     {
1416     int l;
1417     assert(path != NULL);
1418     assert(*path != NULL);
1419     l = strlen(*path);
1420     if(!l || (*path)[l-1] == '/')
1421     return;
1422     *path = xrealloc(*path, l+2);
1423     strcat(*path, "/");
1424     }
1425    
1426 bertho 1.1 int main(int argc, char *argv[])
1427     {
1428 bertho 1.7 extern int yy_flex_debug;
1429     extern int rcs_flex_debug;
1430     extern int yydebug;
1431     extern int rcsdebug;
1432 bertho 1.1 int optc;
1433     char *confpath = NULL;
1434     char *outfile = NULL;
1435     char *cvsroot = NULL;
1436     char *cvsmodule = NULL;
1437 bertho 1.7 int imagemap = 0;
1438     char *imgmapname = NULL;
1439 bertho 1.1 int lose = 0;
1440     FILE *fp;
1441 bertho 1.7 rcsfile_t *rcs;
1442 bertho 1.1 gdImagePtr im;
1443    
1444 bertho 1.8 while((optc = getopt(argc, argv, "0:1:2:3:4:5:6:7:8:9:c:d:hiM:m:o:qr:V")) != EOF)
1445 bertho 1.1 {
1446     switch(optc)
1447     {
1448     case 'c':
1449     confpath = xstrdup(optarg);
1450     break;
1451 bertho 1.7 case 'd':
1452     debuglevel = strtol(optarg, NULL, 0);
1453     break;
1454 bertho 1.6 case 'i':
1455 bertho 1.7 imagemap = 1;
1456     break;
1457     case 'M':
1458     imgmapname = xstrdup(optarg);
1459 bertho 1.6 break;
1460 bertho 1.1 case 'm':
1461     cvsmodule = xstrdup(optarg);
1462     break;
1463     case 'o':
1464     outfile = xstrdup(optarg);
1465     break;
1466 bertho 1.7 case 'q':
1467     quiet = 1;
1468     break;
1469 bertho 1.1 case 'r':
1470     cvsroot = xstrdup(optarg);
1471     break;
1472     case 'V':
1473     fprintf(stdout, "cvsgraph v%s, %s\n", VERSION_STR, NOTICE_STR);
1474     return 0;
1475     case 'h':
1476     fprintf(stdout, "%s", usage_str);
1477     return 0;
1478     default:
1479 bertho 1.8 if(isdigit(optc))
1480     {
1481     conf.expand[optc-'0'] = xstrdup(optarg);
1482     }
1483     else
1484     lose++;
1485 bertho 1.1 }
1486     }
1487    
1488     if(optind >= argc)
1489     {
1490     fprintf(stderr, "Missing inputfile\n");
1491     lose++;
1492     }
1493    
1494     if(lose)
1495     {
1496     fprintf(stderr, "%s", usage_str);
1497     return 1;
1498     }
1499    
1500 bertho 1.7 if(debuglevel)
1501     {
1502     setvbuf(stdout, NULL, 0, _IONBF);
1503     setvbuf(stderr, NULL, 0, _IONBF);
1504     }
1505     yy_flex_debug = (debuglevel & DEBUG_CONF_LEX) != 0;
1506     rcs_flex_debug = (debuglevel & DEBUG_RCS_LEX) != 0;
1507     yydebug = (debuglevel & DEBUG_CONF_YACC) != 0;
1508     rcsdebug = (debuglevel & DEBUG_RCS_YACC) != 0;
1509    
1510 bertho 1.1 /* Set defaults */
1511 bertho 1.9 conf.tag_font = gdFontTiny;
1512     conf.rev_font = gdFontTiny;
1513     conf.branch_font = gdFontTiny;
1514     conf.title_font = gdFontTiny;
1515     conf.rev_text_font = gdFontTiny;
1516    
1517     conf.cvsroot = xstrdup("");
1518     conf.cvsmodule = xstrdup("");
1519     conf.date_format = xstrdup("%d-%b-%Y %H:%M:%S");
1520     conf.title = xstrdup("");
1521     conf.map_name = xstrdup("CvsGraphImageMap");
1522     conf.map_branch_href = xstrdup("href=\"unset: conf.map_branch_href\"");
1523     conf.map_branch_alt = xstrdup("alt=\"%B\"");
1524     conf.map_rev_href = xstrdup("href=\"unset: conf.map_rev_href\"");
1525     conf.map_rev_alt = xstrdup("alt=\"%R\"");
1526     conf.map_diff_href = xstrdup("href=\"unset: conf.map_diff_href\"");
1527 bertho 1.12 conf.map_diff_alt = xstrdup("alt=\"%P &lt;-&gt; %R\"");
1528 bertho 1.10 conf.rev_text = xstrdup("%d");
1529 bertho 1.9
1530     conf.color_bg = white_color;
1531     conf.branch_bgcolor = white_color;
1532     conf.branch_color = black_color;
1533     conf.rev_color = black_color;
1534     conf.rev_bgcolor = white_color;
1535     conf.tag_color = black_color;
1536     conf.title_color = black_color;
1537     conf.rev_text_color = black_color;
1538 bertho 1.10
1539     conf.image_quality = 100;
1540 bertho 1.1
1541     if(!read_config(confpath))
1542     {
1543     fprintf(stderr, "Error reading config file\n");
1544     return 1;
1545     }
1546    
1547     /* Set overrides */
1548 bertho 1.7 if(cvsroot) conf.cvsroot = cvsroot;
1549     if(cvsmodule) conf.cvsmodule = cvsmodule;
1550     if(imgmapname) conf.map_name = imgmapname;
1551 bertho 1.1
1552 bertho 1.9 append_slash(&conf.cvsroot);
1553     append_slash(&conf.cvsmodule);
1554    
1555 bertho 1.7 rcs = get_rcsfile(conf.cvsroot, conf.cvsmodule, argv[optind]);
1556 bertho 1.1 if(!rcs)
1557     return 1;
1558    
1559 bertho 1.7 if(debuglevel & DEBUG_RCS_FILE)
1560     dump_rcsfile(rcs);
1561 bertho 1.1
1562 bertho 1.7 if(!reorganise_branches(rcs))
1563     return 1;
1564 bertho 1.1
1565 bertho 1.7 if(!assign_tags(rcs))
1566     return 1;
1567 bertho 1.1
1568     if(outfile)
1569     {
1570     if((fp = fopen(outfile, "w")) == NULL)
1571     {
1572     perror(outfile);
1573     return 1;
1574     }
1575     }
1576     else
1577     fp = stdout;
1578 bertho 1.5
1579 bertho 1.7 make_layout(rcs);
1580    
1581     if(!imagemap)
1582 bertho 1.5 {
1583 bertho 1.7 /* Create an image */
1584     im = make_image(rcs);
1585    
1586     switch(conf.image_type)
1587     {
1588 bertho 1.5 #ifdef HAVE_IMAGE_GIF
1589 bertho 1.7 # ifndef HAVE_IMAGE_PNG
1590     default:
1591     # endif
1592     case IMAGE_GIF:
1593     gdImageGif(im, fp);
1594     break;
1595 bertho 1.5 #endif
1596     #ifdef HAVE_IMAGE_PNG
1597 bertho 1.7 default:
1598     case IMAGE_PNG:
1599     gdImagePng(im, fp);
1600     break;
1601 bertho 1.5 #endif
1602     #ifdef HAVE_IMAGE_JPEG
1603     # if !defined(HAVE_IMAGE_GIF) && !defined(HAVE_IMAGE_PNG)
1604 bertho 1.7 default:
1605 bertho 1.5 # endif
1606 bertho 1.7 case IMAGE_JPEG:
1607     gdImageJpeg(im, fp, conf.image_quality);
1608     break;
1609 bertho 1.5 #endif
1610 bertho 1.7 }
1611    
1612     gdImageDestroy(im);
1613     }
1614     else
1615     {
1616     /* Create an imagemap */
1617     make_imagemap(rcs, fp);
1618 bertho 1.5 }
1619    
1620 bertho 1.1 if(outfile)
1621     fclose(fp);
1622 bertho 1.6
1623 bertho 1.1 return 0;
1624     }
1625    

  ViewVC Help
Powered by ViewVC 1.1.0 with CvsGraph 1.7.0