/[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.32 - (hide annotations)
Fri Mar 14 01:38:39 2003 UTC (14 years, 9 months ago) by bertho
Branch: MAIN
Changes since 1.31: +200 -44 lines
File MIME type: text/plain
- Fixed a bug in the initial placing of left to right displaying.
- Half-fixed a bug in the kerning code, where a loop-safeguard was triggered
  way too soon (at 100 iterations). This has now been changed to 10000, but
  should be dependent on the number of drawable branches, as the function is
  _at least_ order O(N^2). However, more analysis is required to ensure
  safe guarding under all circumstances.
- Implemented folding of empty branches. Many uses of CVS create many
  branches on the same revision but have no commits on them. This occurs
  often with stable files like .cvsignore files and the like. A new
  configuration option branch_fold enables imaging consequtive brances
  with no commits in the same branch-box. This reduces images by a huge
  factor (28000x1700 -> 2100x2300, i.e. a factor of 10). It also speeds up
  drawing considerably. The branch_fold option is *on* by default.
- Fix duplicate branch-boxes (branch_dupbox=true). No duplicates should be
  created if there are no commits on a specific branch. There is no reason
  for having two boxes on top of each other.
1 bertho 1.1 /*
2     * CvsGraph graphical representation generator of brances and revisions
3     * of a file in cvs/rcs.
4     *
5 bertho 1.29 * Copyright (C) 2001,2002,2003 B. Stultiens
6 bertho 1.1 *
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 bertho 1.18 #include <limits.h>
38 bertho 1.30 #include <regex.h>
39 bertho 1.9
40 bertho 1.14 #ifdef HAVE_GETOPT_H
41     # include <getopt.h>
42     #endif
43    
44 bertho 1.1 #include <gd.h>
45     #include <gdfontt.h>
46    
47     #include "cvsgraph.h"
48     #include "utils.h"
49     #include "readconf.h"
50 bertho 1.7 #include "rcs.h"
51 bertho 1.1
52 bertho 1.5 #if !defined(HAVE_IMAGE_GIF) && !defined(HAVE_IMAGE_PNG) && !defined(HAVE_IMAGE_JPEG)
53     # error No image output format available. Check libgd
54     #endif
55    
56    
57 bertho 1.21 /*#define DEBUG 1*/
58 bertho 1.19 /*#define NOGDFILL 1*/
59 bertho 1.26 /*#define DEBUG_IMAGEMAP 1*/
60 bertho 1.19
61 bertho 1.32 #define LOOPSAFEGUARD 10000 /* Max itterations in possible infinite loops */
62 bertho 1.1
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.30 /*
92     **************************************************************************
93     * Forwards
94     **************************************************************************
95     */
96     static void zap_string(void);
97     static char *dup_string(void);
98     static void add_string_str(const char *s);
99     static void add_string_ch(int ch);
100     static void add_string_date(const char *d);
101     static void add_string_str_html(const char *s, int maxlen);
102     static void add_string_str_len(const char *s, int maxlen);
103 bertho 1.1
104     /*
105     **************************************************************************
106 bertho 1.17 * Debug routines
107 bertho 1.1 **************************************************************************
108     */
109 bertho 1.19 static void dump_rev(char *p, rev_t *r)
110 bertho 1.1 {
111 bertho 1.7 printf("%s", p);
112     if(r)
113     printf("'%s', '%s', %d\n", r->rev, r->branch, r->isbranch);
114     else
115     printf("<null>\n");
116 bertho 1.1 }
117    
118 bertho 1.19 static void dump_id(char *p, char *d)
119 bertho 1.1 {
120 bertho 1.7 printf("%s", p);
121     if(d)
122     printf("'%s'\n", d);
123     else
124     printf("<null>\n");
125 bertho 1.1 }
126    
127 bertho 1.19 static void dump_idrev(char *p, idrev_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->id);
133     dump_rev("", t->rev);
134 bertho 1.1 }
135     else
136 bertho 1.7 printf("<null>\n");
137 bertho 1.1 }
138    
139 bertho 1.19 static void dump_tag(char *p, tag_t *t)
140 bertho 1.1 {
141 bertho 1.7 printf("%s", p);
142     if(t)
143 bertho 1.1 {
144 bertho 1.7 printf("'%s' -> ", t->tag);
145     dump_rev("", t->rev);
146 bertho 1.1 }
147 bertho 1.7 else
148     printf("<null>\n");
149 bertho 1.1 }
150    
151 bertho 1.19 static void dump_delta(char *p, delta_t *d)
152 bertho 1.1 {
153 bertho 1.7 int i;
154     printf("%sdelta.rev : ", p);
155     dump_rev("", d->rev);
156     printf("%sdelta.date : %s\n", p, d->date);
157     printf("%sdelta.author: %s\n", p, d->author);
158     printf("%sdelta.state : %s\n", p, d->state);
159     for(i = 0; d->branches && i < d->branches->nrevs; i++)
160 bertho 1.1 {
161 bertho 1.7 printf("%sdelta.branch: ", p);
162     dump_rev("", d->branches->revs[i]);
163 bertho 1.1 }
164 bertho 1.7 printf("%sdelta.next : ", p);
165     dump_rev("", d->next);
166     printf("\n");
167 bertho 1.1 }
168    
169 bertho 1.19 static void dump_dtext(char *p, dtext_t *d)
170 bertho 1.1 {
171 bertho 1.7 printf("%sdtext.rev : ", p);
172     dump_rev("", d->rev);
173     printf("%sdtext.log : %d bytes\n", p, d->log ? strlen(d->log) : -1);
174     printf("%sdtext.text : %d bytes\n", p, d->text ? strlen(d->text) : -1);
175     printf("\n");
176 bertho 1.1 }
177    
178 bertho 1.19 static void dump_rcsfile(rcsfile_t *rcs)
179 bertho 1.1 {
180 bertho 1.7 int i;
181     printf("root : '%s'\n", rcs->root);
182     printf("module : '%s'\n", rcs->module);
183     printf("file : '%s'\n", rcs->file);
184     dump_rev("head : ", rcs->head);
185     dump_rev("branch : ", rcs->branch);
186     printf("access :\n");
187     for(i = 0; rcs->access && i < rcs->access->nids; i++)
188     dump_id("\t", rcs->access->ids[i]);
189     printf("tags :\n");
190     for(i = 0; rcs->tags && i < rcs->tags->ntags; i++)
191     dump_tag("\t", rcs->tags->tags[i]);
192     printf("locks :%s\n", rcs->strict ? " (strict)" : "");
193     for(i = 0; rcs->locks && i < rcs->locks->nidrevs; i++)
194     dump_idrev("\t", rcs->locks->idrevs[i]);
195     printf("comment: '%s'\n", rcs->comment);
196     printf("expand : '%s'\n", rcs->expand ? rcs->expand : "(default -kv)");
197     printf("deltas :\n");
198     for(i = 0; rcs->deltas && i < rcs->deltas->ndeltas; i++)
199     dump_delta("\t", rcs->deltas->deltas[i]);
200     printf("desc : '%s'\n", rcs->desc);
201     printf("dtexts :\n");
202     for(i = 0; rcs->dtexts && i < rcs->dtexts->ndtexts; i++)
203     dump_dtext("\t", rcs->dtexts->dtexts[i]);
204    
205     fflush(stdout);
206 bertho 1.1 }
207    
208 bertho 1.7 /*
209     **************************************************************************
210     * Read the rcs file
211     **************************************************************************
212     */
213     rcsfile_t *get_rcsfile(const char *cvsroot, const char *module, const char *file)
214 bertho 1.1 {
215 bertho 1.7 char *cmd = NULL;
216     int rv;
217 bertho 1.1
218 bertho 1.18 if(file)
219 bertho 1.9 {
220 bertho 1.18 cmd = xmalloc(strlen(cvsroot) + strlen(module) + strlen(file) + 1);
221     sprintf(cmd, "%s%s%s", cvsroot, module, file);
222     if(!(rcsin = fopen(cmd, "rb")))
223     {
224     perror(cmd);
225     return NULL;
226     }
227     input_file = cmd;
228     }
229     else
230     {
231     rcsin = stdin;
232     input_file = "<stdin>";
233 bertho 1.9 }
234 bertho 1.7 line_number = 1;
235     rv = rcsparse();
236 bertho 1.18 if(file)
237     {
238     fclose(rcsin);
239     xfree(cmd);
240     }
241 bertho 1.7 if(rv)
242 bertho 1.1 return NULL;
243 bertho 1.7 input_file = NULL;
244 bertho 1.18 if(file)
245     {
246     rcsfile->root = xstrdup(cvsroot);
247     rcsfile->module = xstrdup(module);
248     rcsfile->file = xstrdup(file);
249     }
250     else
251     {
252     rcsfile->root = xstrdup("");
253     rcsfile->module = xstrdup("");
254     rcsfile->file = xstrdup("<stdin>");
255     }
256 bertho 1.7 return rcsfile;
257 bertho 1.1 }
258    
259     /*
260     **************************************************************************
261     * Sort and find helpers
262     **************************************************************************
263     */
264 bertho 1.5 int count_dots(const char *s)
265     {
266     int i;
267     for(i = 0; *s; s++)
268     {
269     if(*s == '.')
270     i++;
271     }
272     return i;
273     }
274    
275 bertho 1.7 int compare_rev(int bcmp, const rev_t *r1, const rev_t *r2)
276 bertho 1.5 {
277     int d1, d2;
278 bertho 1.7 char *c1, *c2;
279 bertho 1.5 char *v1, *v2;
280     char *s1, *s2;
281     int retval = 0;
282     assert(r1 != NULL);
283     assert(r2 != NULL);
284 bertho 1.7 if(bcmp)
285     {
286     assert(r1->branch != NULL);
287     assert(r2->branch != NULL);
288     c1 = r1->branch;
289     c2 = r2->branch;
290     }
291     else
292     {
293     assert(r1->rev != NULL);
294     assert(r2->rev != NULL);
295     c1 = r1->rev;
296     c2 = r2->rev;
297     }
298 bertho 1.5
299 bertho 1.7 d1 = count_dots(c1);
300     d2 = count_dots(c2);
301 bertho 1.5 if(d1 != d2)
302     {
303     return d1 - d2;
304     }
305    
306 bertho 1.7 s1 = v1 = xstrdup(c1);
307     s2 = v2 = xstrdup(c2);
308 bertho 1.5 while(1)
309     {
310     char *vc1 = strchr(s1, '.');
311     char *vc2 = strchr(s2, '.');
312     if(vc1 && vc2)
313     *vc1 = *vc2 = '\0';
314     if(*s1 && *s2)
315     {
316     d1 = atoi(s1);
317     d2 = atoi(s2);
318     if(d1 != d2)
319     {
320     retval = d1 - d2;
321     break;
322     }
323     }
324     if(!vc1 || !vc2)
325     break;
326     s1 = vc1 + 1;
327     s2 = vc2 + 1;
328     }
329     xfree(v1);
330     xfree(v2);
331     return retval;
332     }
333    
334 bertho 1.7 /*
335     **************************************************************************
336     * Reorganise the rcsfile for the branches
337     *
338     * Basically, we have a list of deltas (i.e. administrative info on
339     * revisions) and a list of delta text (the actual logs and diffs).
340     * The deltas are linked through the 'next' and the 'branches' fields
341     * which describe the tree-structure of revisions.
342     * The reorganisation means that we put each delta and corresponding
343     * delta text in a revision structure and assign it to a specific
344     * branch. This is required because we want to be able to calculate
345     * the bounding boxes of each branch. The revisions expand vertically
346     * and the branches expand horizontally.
347     * The reorganisation is performed in these steps:
348 bertho 1.17 * 1 - sort deltas and delta text on revision number for quick lookup
349 bertho 1.7 * 2 - start at the denoted head revision:
350     * * create a branch structure and add this revision
351     * * for each 'branches' in the delta do:
352     * - walk all 'branches' of the delta and recursively goto 2
353     * with the denoted branch delta as new head
354     * - backlink the newly create sub-branch to the head revision
355     * so that we can draw them recursively
356     * * set head to the 'next' field and goto 2 until no next is
357     * available
358     * 3 - update the administration
359     **************************************************************************
360     */
361 bertho 1.19 static int sort_delta(const void *d1, const void *d2)
362 bertho 1.7 {
363     return compare_rev(0, (*(delta_t **)d1)->rev, (*(delta_t **)d2)->rev);
364     }
365    
366 bertho 1.19 static int search_delta(const void *r, const void *d)
367 bertho 1.1 {
368 bertho 1.7 return compare_rev(0, (rev_t *)r, (*(delta_t **)d)->rev);
369 bertho 1.1 }
370    
371 bertho 1.19 static delta_t *find_delta(delta_t **dl, int n, rev_t *r)
372 bertho 1.1 {
373 bertho 1.7 delta_t **d;
374 bertho 1.23 if(!n)
375     return NULL;
376 bertho 1.7 d = bsearch(r, dl, n, sizeof(*dl), search_delta);
377     if(!d)
378     return NULL;
379     return *d;
380 bertho 1.1 }
381    
382 bertho 1.19 static int sort_dtext(const void *d1, const void *d2)
383 bertho 1.1 {
384 bertho 1.7 return compare_rev(0, (*(dtext_t **)d1)->rev, (*(dtext_t **)d2)->rev);
385 bertho 1.1 }
386    
387 bertho 1.19 static int search_dtext(const void *r, const void *d)
388 bertho 1.1 {
389 bertho 1.7 return compare_rev(0, (rev_t *)r, (*(dtext_t **)d)->rev);
390 bertho 1.1 }
391    
392 bertho 1.19 static dtext_t *find_dtext(dtext_t **dl, int n, rev_t *r)
393 bertho 1.1 {
394 bertho 1.7 dtext_t **d;
395 bertho 1.23 if(!n)
396     return NULL;
397 bertho 1.7 d = bsearch(r, dl, n, sizeof(*dl), search_dtext);
398     if(!d)
399 bertho 1.1 return NULL;
400 bertho 1.7 return *d;
401     }
402    
403 bertho 1.19 static rev_t *dup_rev(const rev_t *r)
404 bertho 1.7 {
405     rev_t *t = xmalloc(sizeof(*t));
406     t->rev = xstrdup(r->rev);
407     t->branch = xstrdup(r->branch);
408     t->isbranch = r->isbranch;
409     return t;
410     }
411    
412 bertho 1.19 static branch_t *new_branch(delta_t *d, dtext_t *t)
413 bertho 1.7 {
414     branch_t *b = xmalloc(sizeof(*b));
415     revision_t *r = xmalloc(sizeof(*r));
416     r->delta = d;
417     r->dtext = t;
418     r->rev = d->rev;
419     r->branch = b;
420     b->branch = dup_rev(d->rev);
421     b->branch->isbranch = 1;
422     b->nrevs = 1;
423     b->revs = xmalloc(sizeof(b->revs[0]));
424     b->revs[0] = r;
425     return b;
426     }
427    
428 bertho 1.19 static revision_t *add_to_branch(branch_t *b, delta_t *d, dtext_t *t)
429 bertho 1.7 {
430     revision_t *r = xmalloc(sizeof(*r));
431     r->delta = d;
432     r->dtext = t;
433     r->rev = d->rev;
434     r->branch = b;
435     b->revs = xrealloc(b->revs, (b->nrevs+1) * sizeof(b->revs[0]));
436     b->revs[b->nrevs] = r;
437     b->nrevs++;
438     return r;
439     }
440    
441     void build_branch(branch_t ***bl, int *nbl, delta_t **sdl, int nsdl, dtext_t **sdt, int nsdt, delta_t *head)
442     {
443     branch_t *b;
444     dtext_t *text;
445     revision_t *currev;
446    
447     assert(head != NULL);
448    
449     if(head->flag)
450     {
451     fprintf(stderr, "Circular reference on '%s' in branchpoint\n", head->rev->rev);
452     return;
453     }
454     head->flag++;
455     text = find_dtext(sdt, nsdt, head->rev);
456    
457     /* Create a new branch for this head */
458     b = new_branch(head, text);
459     *bl = xrealloc(*bl, (*nbl+1)*sizeof((*bl)[0]));
460     (*bl)[*nbl] = b;
461     (*nbl)++;
462     currev = b->revs[0];
463     while(1)
464     {
465     /* Process all sub-branches */
466     if(head->branches)
467     {
468     int i;
469     for(i = 0; i < head->branches->nrevs; i++)
470     {
471     delta_t *d = find_delta(sdl, nsdl, head->branches->revs[i]);
472     int btag = *nbl;
473     if(!d)
474     continue;
475     build_branch(bl, nbl, sdl, nsdl, sdt, nsdt, d);
476    
477     /* Set the new branch's origin */
478     (*bl)[btag]->branchpoint = currev;
479    
480     /* Add branch to this revision */
481     currev->branches = xrealloc(currev->branches, (currev->nbranches+1)*sizeof(currev->branches[0]));
482     currev->branches[currev->nbranches] = (*bl)[btag];
483     currev->nbranches++;
484     }
485     }
486    
487     /* Walk through the next list */
488     if(!head->next)
489     return;
490 bertho 1.9
491 bertho 1.7 head = find_delta(sdl, nsdl, head->next);
492     if(!head)
493     {
494     fprintf(stderr, "Next revision (%s) not found in deltalist\n", head->next->rev);
495     return;
496     }
497     if(head->flag)
498     {
499     fprintf(stderr, "Circular reference on '%s'\n", head->rev->rev);
500     return;
501     }
502     head->flag++;
503     text = find_dtext(sdt, nsdt, head->rev);
504     currev = add_to_branch(b, head, text);
505     }
506     }
507    
508     int reorganise_branches(rcsfile_t *rcs)
509     {
510     delta_t **sdelta;
511     int nsdelta;
512     dtext_t **sdtext;
513     int nsdtext;
514     delta_t *head;
515     branch_t **bl;
516     int nbl;
517     int i;
518    
519     assert(rcs->deltas != NULL);
520     assert(rcs->head != NULL);
521    
522     /* Make a new list for quick lookup */
523     nsdelta = rcs->deltas->ndeltas;
524     sdelta = xmalloc(nsdelta * sizeof(sdelta[0]));
525     memcpy(sdelta, rcs->deltas->deltas, nsdelta * sizeof(sdelta[0]));
526     qsort(sdelta, nsdelta, sizeof(sdelta[0]), sort_delta);
527    
528     /* Do the same for the delta text */
529 bertho 1.22 if(rcs->dtexts)
530     {
531     nsdtext = rcs->dtexts->ndtexts;
532     sdtext = xmalloc(nsdtext * sizeof(sdtext[0]));
533     memcpy(sdtext, rcs->dtexts->dtexts, nsdtext * sizeof(sdtext[0]));
534     qsort(sdtext, nsdtext, sizeof(sdtext[0]), sort_dtext);
535     }
536     else
537     {
538     nsdtext = 0;
539     sdtext = NULL;
540     }
541 bertho 1.7
542     /* Start from the head of the trunk */
543     head = find_delta(sdelta, nsdelta, rcs->head);
544     if(!head)
545     {
546     fprintf(stderr, "Head revision (%s) not found in deltalist\n", rcs->head->rev);
547     return 0;
548     }
549     bl = NULL;
550     nbl = 0;
551     build_branch(&bl, &nbl, sdelta, nsdelta, sdtext, nsdtext, head);
552    
553     /* Reverse the head branch */
554     for(i = 0; i < bl[0]->nrevs/2; i++)
555     {
556     revision_t *r;
557     r = bl[0]->revs[i];
558     bl[0]->revs[i] = bl[0]->revs[bl[0]->nrevs-i-1];
559     bl[0]->revs[bl[0]->nrevs-i-1] = r;
560     }
561    
562     /* Update the branch-number of the head because it was reversed */
563     xfree(bl[0]->branch->branch);
564     bl[0]->branch->branch = xstrdup(bl[0]->revs[0]->rev->branch);
565    
566     /* Keep the admin */
567     rcs->branches = bl;
568     rcs->nbranches = nbl;
569     rcs->sdelta = sdelta;
570     rcs->nsdelta = nsdelta;
571     rcs->sdtext = sdtext;
572     rcs->nsdtext = nsdtext;
573     rcs->active = bl[0];
574     return 1;
575     }
576    
577     /*
578     **************************************************************************
579     * Assign the symbolic tags to the revisions and branches
580     *
581     * The tags point to revision numbers somewhere in the tree structure
582     * of branches and revisions. First we make a sorted list of all
583     * revisions and then we assign each tag to the proper revision.
584     **************************************************************************
585     */
586 bertho 1.19 static int sort_revision(const void *r1, const void *r2)
587 bertho 1.7 {
588     return compare_rev(0, (*(revision_t **)r1)->delta->rev, (*(revision_t **)r2)->delta->rev);
589     }
590    
591 bertho 1.19 static int search_revision(const void *t, const void *r)
592 bertho 1.7 {
593     return compare_rev(0, (rev_t *)t, (*(revision_t **)r)->delta->rev);
594     }
595    
596 bertho 1.19 static int sort_branch(const void *b1, const void *b2)
597 bertho 1.7 {
598     return compare_rev(1, (*(branch_t **)b1)->branch, (*(branch_t **)b2)->branch);
599 bertho 1.1 }
600    
601 bertho 1.19 static int search_branch(const void *t, const void *b)
602 bertho 1.1 {
603 bertho 1.7 return compare_rev(1, (rev_t *)t, (*(branch_t **)b)->branch);
604 bertho 1.1 }
605    
606 bertho 1.19 static char *previous_rev(const char *c)
607 bertho 1.1 {
608 bertho 1.7 int dots = count_dots(c);
609     char *cptr;
610     char *r;
611     if(!dots)
612 bertho 1.9 {
613     fprintf(stderr, "FIXME: previous_rev(\"%s\"): Cannot determine parent branch revision\n", c);
614 bertho 1.7 return xstrdup("1.0"); /* FIXME: don't know what the parent is */
615 bertho 1.9 }
616 bertho 1.7 if(dots & 1)
617     {
618     /* Is is a revision we want the parent of */
619     r = xstrdup(c);
620     cptr = strrchr(r, '.');
621     assert(cptr != NULL);
622     if(dots == 1)
623     {
624 bertho 1.9 fprintf(stderr, "FIXME: previous_rev(\"%s\"): Going beyond top-level?\n", c);
625 bertho 1.7 /* FIXME: What is the parent of 1.1? */
626     cptr[1] = '\0';
627     strcat(r, "0");
628     return r;
629     }
630     /* Here we have a "x.x[.x.x]+" case */
631     *cptr = '\0';
632     cptr = strrchr(r, '.');
633     assert(cptr != NULL);
634     *cptr = '\0';
635     return r;
636     }
637     /* It is a branch we want the parent of */
638     r = xstrdup(c);
639     cptr = strrchr(r, '.');
640     assert(cptr != NULL);
641     *cptr = '\0';
642     return r;
643 bertho 1.1 }
644    
645 bertho 1.30 static char *build_regex(size_t n, regmatch_t *m, const char *ms)
646     {
647     char *cptr;
648     int i;
649    
650     if(!conf.merge_to || !conf.merge_to[0])
651     return NULL;
652    
653     zap_string();
654     for(cptr = conf.merge_to; *cptr; cptr++)
655     {
656     if(*cptr == '%')
657     {
658     if(cptr[1] >= '1' && cptr[1] <= '9')
659     {
660     int idx = cptr[1] - '0';
661     regmatch_t *p = &m[idx];
662     if(idx < n && !(p->rm_so == -1 || p->rm_so >= p->rm_eo))
663     {
664     for(i = p->rm_so; i < p->rm_eo; i++)
665     {
666     if(strchr("^$.*+\\[{()", ms[i]))
667     add_string_ch('\\');
668     add_string_ch(ms[i]);
669     }
670     }
671     cptr++;
672     }
673     else
674     add_string_ch('%');
675     }
676     else
677     add_string_ch(*cptr);
678     }
679     return dup_string();
680     }
681    
682 bertho 1.31 static void find_merges(rcsfile_t *rcs)
683 bertho 1.1 {
684     int i;
685 bertho 1.30 int err;
686 bertho 1.31 int rcflags = REG_EXTENDED | (conf.merge_nocase ? REG_ICASE : 0);
687 bertho 1.30 regex_t *refrom = NULL;
688     regex_t *reto = NULL;
689     regmatch_t *matchfrom = NULL;
690    
691 bertho 1.31 if(!conf.merge_from || !conf.merge_from[0] || !conf.merge_to || !conf.merge_to[0])
692     return;
693    
694     refrom = xmalloc(sizeof(*refrom));
695     reto = xmalloc(sizeof(*reto));
696    
697     /* Compile the 'from' regex match for merge identification */
698     err = regcomp(refrom, conf.merge_from, rcflags);
699     if(err)
700 bertho 1.30 {
701 bertho 1.31 if(!quiet)
702     {
703     char *msg;
704     i = regerror(err, refrom, NULL, 0);
705     msg = xmalloc(i+1);
706     regerror(err, refrom, msg, i+1);
707     fprintf(stderr, "%s\n", msg);
708     xfree(msg);
709     }
710     xfree(refrom);
711     xfree(reto);
712     return;
713     }
714     else
715     matchfrom = xmalloc((refrom->re_nsub+1) * sizeof(*matchfrom));
716 bertho 1.30
717 bertho 1.31 for(i = 0; i < rcs->tags->ntags; i++)
718     {
719     tag_t *t = rcs->tags->tags[i];
720    
721     /* Must be revision tags and not detached */
722     if(t->rev->isbranch || !t->logrev)
723     continue;
724    
725     /* Try to find merge tag matches */
726     if(!regexec(refrom, t->tag, refrom->re_nsub+1, matchfrom, 0))
727     {
728     int n;
729     char *to;
730    
731     to = build_regex(refrom->re_nsub+1, matchfrom, t->tag);
732     if(to)
733     {
734     err = regcomp(reto, to, rcflags);
735     if(err && !quiet)
736     {
737     char *msg;
738     i = regerror(err, reto, NULL, 0);
739     msg = xmalloc(i+1);
740     regerror(err, reto, msg, i+1);
741     fprintf(stderr, "%s\n", msg);
742     }
743     else if(!err)
744     {
745     for(n = 0; n < rcs->tags->ntags; n++)
746     {
747     tag_t *nt = rcs->tags->tags[n];
748     /* From and To never should match the same tag or belong to a branch */
749     if(n == i || nt->rev->isbranch || !nt->logrev)
750     continue;
751    
752     if(!regexec(reto, nt->tag, 0, NULL, REG_NOSUB))
753     {
754     /* Tag matches */
755     rcs->merges = xrealloc(rcs->merges,
756     sizeof(rcs->merges[0]) * (rcs->nmerges+1));
757     rcs->merges[rcs->nmerges].to = nt;
758     rcs->merges[rcs->nmerges].from = t;
759     rcs->nmerges++;
760     /* We cannot (should not) match multiple times */
761     break;
762     }
763     }
764     regfree(reto);
765     }
766     xfree(to);
767     }
768 bertho 1.30 }
769     }
770 bertho 1.31 if(matchfrom) xfree(matchfrom);
771     if(refrom) { regfree(refrom); xfree(refrom); }
772     if(reto) xfree(reto);
773     }
774    
775     static void assign_tags(rcsfile_t *rcs)
776     {
777     int i;
778     int nr;
779 bertho 1.7
780     for(i = nr = 0; i < rcs->nbranches; i++)
781     nr += rcs->branches[i]->nrevs;
782    
783     rcs->srev = xmalloc(nr * sizeof(rcs->srev[0]));
784     rcs->nsrev = nr;
785     for(i = nr = 0; i < rcs->nbranches; i++)
786     {
787     memcpy(&rcs->srev[nr], rcs->branches[i]->revs, rcs->branches[i]->nrevs * sizeof(rcs->branches[i]->revs[0]));
788     nr += rcs->branches[i]->nrevs;
789     }
790    
791     qsort(rcs->srev, rcs->nsrev, sizeof(rcs->srev[0]), sort_revision);
792     qsort(rcs->branches, rcs->nbranches, sizeof(rcs->branches[0]), sort_branch);
793    
794     if(!rcs->branch)
795     {
796     /* The main trunk is the active trunk */
797     rcs->tags->tags = xrealloc(rcs->tags->tags, (rcs->tags->ntags+1)*sizeof(rcs->tags->tags[0]));
798     rcs->tags->tags[rcs->tags->ntags] = xmalloc(sizeof(tag_t));
799     rcs->tags->tags[rcs->tags->ntags]->tag = xstrdup("MAIN");
800     rcs->tags->tags[rcs->tags->ntags]->rev = xmalloc(sizeof(rev_t));
801     rcs->tags->tags[rcs->tags->ntags]->rev->rev = xstrdup(rcs->active->branch->rev);
802     rcs->tags->tags[rcs->tags->ntags]->rev->branch = xstrdup(rcs->active->branch->branch);
803     rcs->tags->tags[rcs->tags->ntags]->rev->isbranch = 1;
804     rcs->tags->ntags++;
805     }
806    
807     /* We should have at least two tags (HEAD and MAIN) */
808     assert(rcs->tags != 0);
809    
810     for(i = 0; i < rcs->tags->ntags; i++)
811     {
812     tag_t *t = rcs->tags->tags[i];
813     if(t->rev->isbranch)
814     {
815     branch_t **b;
816     add_btag:
817     b = bsearch(t->rev, rcs->branches, rcs->nbranches, sizeof(rcs->branches[0]), search_branch);
818     if(!b)
819     {
820     rev_t rev;
821     revision_t **r;
822     /* This happens for the magic branch numbers if there are
823 bertho 1.12 * no commits within the new branch yet. So, we add the
824     * branch and try to continue.
825 bertho 1.7 */
826     rev.rev = previous_rev(t->rev->branch);
827     rev.branch = NULL;
828     rev.isbranch = 0;
829     r = bsearch(&rev, rcs->srev, rcs->nsrev, sizeof(rcs->srev[0]), search_revision);
830     xfree(rev.rev);
831     if(!r)
832     {
833 bertho 1.12 if(!quiet)
834     fprintf(stderr, "No branch found for tag '%s:%s'\n", t->tag, t->rev->branch);
835 bertho 1.7 }
836     else
837     {
838     rcs->branches = xrealloc(rcs->branches, (rcs->nbranches+1)*sizeof(rcs->branches[0]));
839     rcs->branches[rcs->nbranches] = xmalloc(sizeof(branch_t));
840     rcs->branches[rcs->nbranches]->branch = dup_rev(t->rev);
841     rcs->branches[rcs->nbranches]->branchpoint = *r;
842     (*r)->branches = xrealloc((*r)->branches, ((*r)->nbranches+1)*sizeof((*r)->branches[0]));
843     (*r)->branches[(*r)->nbranches] = rcs->branches[rcs->nbranches];
844     (*r)->nbranches++;
845     rcs->nbranches++;
846     /* Resort the branches */
847     qsort(rcs->branches, rcs->nbranches, sizeof(rcs->branches[0]), sort_branch);
848     goto add_btag;
849     }
850     }
851     else
852     {
853     branch_t *bb = *b;
854     bb->tags = xrealloc(bb->tags, (bb->ntags+1)*sizeof(bb->tags[0]));
855     bb->tags[bb->ntags] = t;
856     bb->ntags++;
857     }
858     }
859     else
860     {
861     revision_t **r = bsearch(t->rev, rcs->srev, rcs->nsrev, sizeof(rcs->srev[0]), search_revision);
862     if(!r)
863 bertho 1.12 {
864     if(!quiet)
865     fprintf(stderr, "No revision found for tag '%s:%s'\n", t->tag, t->rev->rev);
866     }
867 bertho 1.7 else
868     {
869     revision_t *rr = *r;
870 bertho 1.30 t->logrev = rr;
871 bertho 1.28 if(!conf.rev_maxtags || rr->ntags <= conf.rev_maxtags)
872     {
873     rr->tags = xrealloc(rr->tags, (rr->ntags+1)*sizeof(rr->tags[0]));
874     if(conf.rev_maxtags && rr->ntags == conf.rev_maxtags)
875     {
876     rr->tags[rr->ntags] = xmalloc(sizeof(tag_t));
877     rr->tags[rr->ntags]->tag = xstrdup("...");
878     rr->tags[rr->ntags]->rev = t->rev;
879     }
880     else
881     rr->tags[rr->ntags] = t;
882     rr->ntags++;
883     }
884 bertho 1.7 }
885     }
886     }
887    
888     /* We need to reset the first in the list of branches to the
889     * active branch to ensure the drawing of all
890     */
891     if(rcs->active != rcs->branches[0])
892 bertho 1.1 {
893 bertho 1.7 branch_t **b = bsearch(rcs->active->branch, rcs->branches, rcs->nbranches, sizeof(rcs->branches[0]), search_branch);
894     branch_t *t;
895     assert(b != NULL);
896     t = *b;
897     *b = rcs->branches[0];
898     rcs->branches[0] = t;
899 bertho 1.1 }
900     }
901    
902     /*
903     **************************************************************************
904 bertho 1.8 * String expansion
905     **************************************************************************
906     */
907     static char *_string;
908     static int _nstring;
909     static int _nastring;
910    
911 bertho 1.30 static void zap_string(void)
912     {
913     _nstring = 0;
914     if(_string)
915     _string[0] = '\0';
916     }
917    
918     static char *dup_string(void)
919     {
920     if(_string)
921     return xstrdup(_string);
922     else
923     return "";
924     }
925    
926 bertho 1.19 static void add_string_str(const char *s)
927 bertho 1.8 {
928     int l = strlen(s) + 1;
929     if(_nstring + l > _nastring)
930     {
931 bertho 1.24 _nastring += MAX(128, l);
932 bertho 1.8 _string = xrealloc(_string, _nastring * sizeof(_string[0]));
933     }
934     memcpy(_string+_nstring, s, l);
935     _nstring += l-1;
936     }
937    
938 bertho 1.19 static void add_string_ch(int ch)
939 bertho 1.8 {
940     char buf[2];
941     buf[0] = ch;
942     buf[1] = '\0';
943     add_string_str(buf);
944     }
945    
946 bertho 1.19 static void add_string_date(const char *d)
947 bertho 1.9 {
948     struct tm tm, *tmp;
949     int n;
950     time_t t;
951     char *buf;
952     int nbuf;
953    
954     memset(&tm, 0, sizeof(tm));
955     n = sscanf(d, "%d.%d.%d.%d.%d.%d",
956     &tm.tm_year, &tm.tm_mon, &tm.tm_mday,
957     &tm.tm_hour, &tm.tm_min, &tm.tm_sec);
958     tm.tm_mon--;
959     if(tm.tm_year > 1900)
960     tm.tm_year -= 1900;
961     t = mktime(&tm);
962     if(n != 6 || t == (time_t)(-1))
963     {
964     add_string_str("<invalid date>");
965     return;
966     }
967    
968     tmp = localtime(&t);
969     nbuf = strlen(conf.date_format) * 16; /* Should be enough to hold all types of expansions */
970     buf = xmalloc(nbuf);
971     strftime(buf, nbuf, conf.date_format, tmp);
972     add_string_str(buf);
973     xfree(buf);
974     }
975    
976 bertho 1.22 static void add_string_str_html(const char *s, int maxlen)
977     {
978     int l = 0;
979     char *str = xmalloc(6 * strlen(s) + 1); /* Should hold all char entity-expand */
980     char *cptr = str;
981     for(; *s; s++)
982     {
983     if(maxlen && l > abs(maxlen))
984     {
985     cptr += sprintf(cptr, "...");
986     break;
987     }
988     if(*s < 0x20)
989     {
990     if(*s == '\n')
991     {
992     if(maxlen < 0)
993     *cptr++ = ' ';
994     else
995     cptr += sprintf(cptr, "<br>");
996     }
997     }
998 bertho 1.30 else if(*s >= 0x7f || *s == '"')
999 bertho 1.22 cptr += sprintf(cptr, "&#%d;", (int)(unsigned char)*s);
1000     else if(*s == '<')
1001     cptr += sprintf(cptr, "&lt;");
1002     else if(*s == '>')
1003     cptr += sprintf(cptr, "&gt;");
1004     else
1005     *cptr++ = *s;
1006     l++;
1007     }
1008     *cptr = '\0';
1009     add_string_str(str);
1010     xfree(str);
1011     }
1012    
1013     static void add_string_str_len(const char *s, int maxlen)
1014     {
1015     int l = strlen(s);
1016     char *str = xmalloc(l + 1 + 3);
1017     strcpy(str, s);
1018     if(maxlen < l)
1019     sprintf(&str[maxlen], "...");
1020     add_string_str(str);
1021     xfree(str);
1022     }
1023    
1024 bertho 1.9 char *expand_string(const char *s, rcsfile_t *rcs, revision_t *r, rev_t *rev, rev_t *prev, tag_t *tag)
1025 bertho 1.8 {
1026     char nb[32];
1027     char nr[32];
1028     char *base;
1029 bertho 1.30 char *exp;
1030 bertho 1.22 int l;
1031     char ch;
1032 bertho 1.8
1033     if(!s)
1034     return xstrdup("");
1035    
1036 bertho 1.30 zap_string();
1037 bertho 1.8
1038     sprintf(nb, "%d", rcs->nbranches);
1039     sprintf(nr, "%d", rcs->nsrev);
1040     for(; *s; s++)
1041     {
1042     if(*s == '%')
1043     {
1044     switch(*++s)
1045     {
1046 bertho 1.12 case 'c':
1047     case 'C':
1048     add_string_str(conf.cvsroot);
1049     if(*s == 'C' && conf.cvsroot[0] && conf.cvsroot[strlen(conf.cvsroot)-1] == '/')
1050     {
1051     /* Strip the trailing '/' */
1052     _nstring--;
1053     _string[_nstring] = '\0';
1054     }
1055     break;
1056 bertho 1.8 case 'f':
1057     case 'F':
1058     base = strrchr(rcs->file, '/');
1059     if(!base)
1060     add_string_str(rcs->file);
1061     else
1062     add_string_str(base+1);
1063     if(*s == 'F' && _string[_nstring-1] == 'v' && _string[_nstring-2] == ',')
1064     {
1065     _nstring -= 2;
1066     _string[_nstring] = '\0';
1067     }
1068     break;
1069     case 'p':
1070     base = strrchr(rcs->file, '/');
1071     if(base)
1072     {
1073     char *c = xstrdup(rcs->file);
1074     base = strrchr(c, '/');
1075     assert(base != NULL);
1076     base[1] = '\0';
1077     add_string_str(c);
1078     xfree(c);
1079     }
1080 bertho 1.9 /*
1081     * We should not add anything here because we can encounter
1082     * a completely empty path, in which case we do not want
1083 bertho 1.17 * to add any slash. This prevents an inadvertent root redirect.
1084 bertho 1.9 *
1085     * else
1086     * add_string_str("/");
1087     */
1088 bertho 1.8 break;
1089 bertho 1.12 case 'm':
1090     case 'M':
1091     add_string_str(conf.cvsmodule);
1092     if(*s == 'M' && conf.cvsmodule[0] && conf.cvsmodule[strlen(conf.cvsmodule)-1] == '/')
1093     {
1094     /* Strip the trailing '/' */
1095     _nstring--;
1096     _string[_nstring] = '\0';
1097     }
1098     break;
1099 bertho 1.8 case 'r': add_string_str(nr); break;
1100     case 'b': add_string_str(nb); break;
1101     case '%': add_string_ch('%'); break;
1102     case '0': if(conf.expand[0]) add_string_str(conf.expand[0]); break;
1103     case '1': if(conf.expand[1]) add_string_str(conf.expand[1]); break;
1104     case '2': if(conf.expand[2]) add_string_str(conf.expand[2]); break;
1105     case '3': if(conf.expand[3]) add_string_str(conf.expand[3]); break;
1106     case '4': if(conf.expand[4]) add_string_str(conf.expand[4]); break;
1107     case '5': if(conf.expand[5]) add_string_str(conf.expand[5]); break;
1108     case '6': if(conf.expand[6]) add_string_str(conf.expand[6]); break;
1109     case '7': if(conf.expand[7]) add_string_str(conf.expand[7]); break;
1110     case '8': if(conf.expand[8]) add_string_str(conf.expand[8]); break;
1111     case '9': if(conf.expand[9]) add_string_str(conf.expand[9]); break;
1112     case 'R': if(rev && rev->rev) add_string_str(rev->rev); break;
1113 bertho 1.9 case 'P': if(prev && prev->rev) add_string_str(prev->rev); break;
1114 bertho 1.8 case 'B': if(rev && rev->branch) add_string_str(rev->branch); break;
1115     case 't': if(tag && tag->tag) add_string_str(tag->tag); break;
1116 bertho 1.9 case 'd': if(r && r->delta && r->delta->date) add_string_date(r->delta->date); break;
1117     case 's': if(r && r->delta && r->delta->state) add_string_str(r->delta->state); break;
1118     case 'a': if(r && r->delta && r->delta->author) add_string_str(r->delta->author); break;
1119 bertho 1.22 case 'L':
1120     case 'l':
1121     ch = *s;
1122     l = 0;
1123     if(s[1] == '[')
1124     {
1125     char *cptr = strchr(s, ']');
1126     char *eptr;
1127     if(cptr)
1128     {
1129     l = strtol(&s[2], &eptr, 10);
1130     if(eptr != cptr)
1131     l = 0;
1132     else
1133     s = cptr;
1134     }
1135     }
1136     if(!conf.parse_logs)
1137     add_string_str("N/A");
1138     else if(r && r->dtext && r->dtext->log)
1139     {
1140     if(ch == 'l')
1141     add_string_str_html(r->dtext->log, l);
1142     else
1143     add_string_str_len(r->dtext->log, abs(l));
1144     }
1145     break;
1146 bertho 1.30 case '(':
1147     base = dup_string();
1148     exp = expand_string(s+1, rcs, r, rev, prev, tag);
1149     zap_string();
1150     add_string_str(base);
1151     add_string_str_html(exp, 0);
1152     xfree(base);
1153     xfree(exp);
1154     /* Find the %) in this recursion level */
1155     for(; *s; s++)
1156     {
1157     if(*s == '%' && s[1] == ')')
1158     break;
1159     }
1160     if(!*s)
1161     {
1162     s--; /* To end outer loop */
1163     if(!quiet)
1164     fprintf(stderr, "string expand: Missing %%) in expansion\n");
1165     }
1166     break;
1167     case ')':
1168     return dup_string();
1169 bertho 1.8 default:
1170     add_string_ch('%');
1171     add_string_ch(*s);
1172     break;
1173     }
1174     }
1175     else
1176     add_string_ch(*s);
1177     }
1178 bertho 1.30 return dup_string();
1179 bertho 1.8 }
1180    
1181     /*
1182     **************************************************************************
1183 bertho 1.1 * Drawing routines
1184     **************************************************************************
1185     */
1186 bertho 1.19 static int get_swidth(const char *s, font_t *f)
1187 bertho 1.1 {
1188 bertho 1.9 int n;
1189     int m;
1190     if(!s || !*s)
1191 bertho 1.1 return 0;
1192 bertho 1.19
1193     #if defined(HAVE_GDIMAGESTRINGFT) || defined(HAVE_GDIMAGESTRINGTTF)
1194     if(conf.use_ttf && f->ttfont)
1195     {
1196     int bb[8];
1197     char *e;
1198     #ifdef HAVE_GDIMAGESTRINGFT
1199     e = gdImageStringFT(NULL, bb, 0, f->ttfont, f->ttsize, 0.0, 0, 0, (char *)s);
1200     #else
1201     e = gdImageStringTTF(NULL, bb, 0, f->ttfont, f->ttsize, 0.0, 0, 0, (char *)s);
1202     #endif
1203     if(!e)
1204     return bb[2] - bb[6];
1205     }
1206     #endif
1207 bertho 1.9 for(n = m = 0; *s; n++, s++)
1208     {
1209     if(*s == '\n')
1210     {
1211     if(n > m)
1212     m = n;
1213     n = 0;
1214     }
1215     }
1216     if(n > m)
1217     m = n;
1218 bertho 1.22 return f->gdfont ? m * f->gdfont->w : m;
1219 bertho 1.1 }
1220    
1221 bertho 1.19 static int get_sheight(const char *s, font_t *f)
1222 bertho 1.1 {
1223     int nl;
1224 bertho 1.9 if(!s || !*s)
1225 bertho 1.1 return 0;
1226 bertho 1.19
1227     #if defined(HAVE_GDIMAGESTRINGFT) || defined(HAVE_GDIMAGESTRINGTTF)
1228     if(conf.use_ttf && f->ttfont)
1229     {
1230     int bb[8];
1231     char *e;
1232     #ifdef HAVE_GDIMAGESTRINGFT
1233     e = gdImageStringFT(NULL, bb, 0, f->ttfont, f->ttsize, 0.0, 0, 0, (char *)s);
1234     #else
1235     e = gdImageStringTTF(NULL, bb, 0, f->ttfont, f->ttsize, 0.0, 0, 0, (char *)s);
1236     #endif
1237     if(!e)
1238     return bb[3] - bb[7] + 4;
1239     }
1240     #endif
1241 bertho 1.1 for(nl = 1; *s; s++)
1242     {
1243     if(*s == '\n' && s[1])
1244     nl++;
1245     }
1246 bertho 1.19 return nl * f->gdfont->h;
1247 bertho 1.1 }
1248    
1249 bertho 1.19 static void draw_rbox(gdImagePtr im, int x1, int y1, int x2, int y2, int r, color_t *color, color_t *bgcolor)
1250 bertho 1.1 {
1251     int r2 = 2*r;
1252     gdImageLine(im, x1+r, y1, x2-r, y1, color->id);
1253     gdImageLine(im, x1+r, y2, x2-r, y2, color->id);
1254     gdImageLine(im, x1, y1+r, x1, y2-r, color->id);
1255     gdImageLine(im, x2, y1+r, x2, y2-r, color->id);
1256 bertho 1.9 if(conf.box_shadow)
1257     {
1258     gdImageLine(im, x1+r+1, y2+1, x2-r, y2+1, black_color.id);
1259     gdImageLine(im, x2+1, y1+r+1, x2+1, y2-r, black_color.id);
1260     }
1261 bertho 1.1 if(r)
1262     {
1263 bertho 1.18 /* FIXME: Pixelization is not perfect */
1264 bertho 1.1 gdImageArc(im, x1+r, y1+r, r2, r2, 180, 270, color->id);
1265     gdImageArc(im, x2-r, y1+r, r2, r2, 270, 360, color->id);
1266     gdImageArc(im, x1+r, y2-r, r2, r2, 90, 180, color->id);
1267 bertho 1.9 if(conf.box_shadow)
1268     {
1269     gdImageArc(im, x2-r+1, y2-r+1, r2, r2, 0, 90, black_color.id);
1270 bertho 1.18 gdImageArc(im, x2-r+1, y2-r, r2, r2, 0, 90, black_color.id);
1271     gdImageArc(im, x2-r, y2-r+1, r2, r2, 0, 90, black_color.id);
1272 bertho 1.9 }
1273 bertho 1.18 gdImageArc(im, x2-r, y2-r, r2, r2, 0, 90, color->id);
1274 bertho 1.1 }
1275 bertho 1.19 #ifndef NOGDFILL
1276 bertho 1.9 gdImageFillToBorder(im, (x1+x2)/2, (y1+y2)/2, color->id, bgcolor->id);
1277 bertho 1.19 #endif
1278 bertho 1.1 }
1279    
1280 bertho 1.19 static void draw_string(gdImagePtr im, char *s, font_t *f, int x, int y, int align, color_t *c)
1281 bertho 1.1 {
1282 bertho 1.19 int h = get_sheight(s, f);
1283 bertho 1.1 int xx, yy;
1284     switch(align & ALIGN_HX)
1285     {
1286     default:
1287     case ALIGN_HL: xx = 0; break;
1288     case ALIGN_HC: xx = -get_swidth(s, f)/2; break;
1289     case ALIGN_HR: xx = -get_swidth(s, f); break;
1290     }
1291     switch(align & ALIGN_VX)
1292     {
1293     default:
1294     case ALIGN_VT: yy = 0; break;
1295 bertho 1.19 case ALIGN_VC: yy = h/2; break;
1296     case ALIGN_VB: yy = h; break;
1297 bertho 1.1 }
1298 bertho 1.19 #if defined(HAVE_GDIMAGESTRINGFT) || defined(HAVE_GDIMAGESTRINGTTF)
1299     if(conf.use_ttf && f->ttfont)
1300     {
1301     int bb[8];
1302     char *e;
1303     int cid = conf.anti_alias ? c->id : -c->id;
1304     #ifdef HAVE_GDIMAGESTRINGFT
1305     e = gdImageStringFT(im, bb, cid, f->ttfont, f->ttsize, 0.0, x+xx, y+yy+h-2, (char *)s);
1306     #else
1307     e = gdImageStringTTF(im, bb, cid, f->ttfont, f->ttsize, 0.0, x+xx, y+yy+h-2, (char *)s);
1308     #endif
1309     if(!e)
1310     return;
1311     }
1312     #endif
1313     yy = -yy;
1314     gdImageString(im, f->gdfont, x+xx+1, y+yy, s, c->id);
1315 bertho 1.1 }
1316    
1317 bertho 1.19 static void draw_stringnl(gdImagePtr im, char *s, font_t *f, int x, int y, int align, color_t *c)
1318 bertho 1.9 {
1319     char *t;
1320     char *d;
1321     d = s = xstrdup(s);
1322     do
1323     {
1324     t = strchr(s, '\n');
1325     if(t)
1326     *t = '\0';
1327     draw_string(im, s, f, x, y, align, c);
1328     y += get_sheight(s, f);
1329     s = t+1;
1330     } while(t);
1331     xfree(d);
1332     }
1333    
1334 bertho 1.19 static void draw_rev(gdImagePtr im, revision_t *r)
1335 bertho 1.1 {
1336 bertho 1.25 int lx;
1337     int rx;
1338     int x2;
1339 bertho 1.1 int i;
1340 bertho 1.25 int ty;
1341    
1342     if(conf.left_right)
1343     {
1344     lx = r->cx;
1345     rx = r->cx + r->w;
1346     ty = r->y - r->h/2;
1347     x2 = r->cx + r->w/2;
1348     }
1349     else
1350     {
1351     lx = r->cx - r->w/2;
1352     rx = lx + r->w;
1353     ty = r->y;
1354     x2 = r->cx;
1355     }
1356 bertho 1.9 draw_rbox(im, lx, ty, rx, ty+r->h, 0, &conf.rev_color, &conf.rev_bgcolor);
1357 bertho 1.1 ty += conf.rev_tspace;
1358 bertho 1.25 draw_string(im, r->rev->rev, &conf.rev_font, x2, ty, ALIGN_HC, &conf.rev_color);
1359 bertho 1.1 ty += get_sheight(r->rev->rev, &conf.rev_font);
1360 bertho 1.25 draw_stringnl(im, r->revtext, &conf.rev_text_font, x2, ty, ALIGN_HC, &conf.rev_text_color);
1361 bertho 1.9 ty += get_sheight(r->revtext, &conf.rev_text_font);
1362 bertho 1.1 for(i = 0; i < r->ntags; i++)
1363     {
1364 bertho 1.25 draw_string(im, r->tags[i]->tag, &conf.tag_font, x2, ty, ALIGN_HC, &conf.tag_color);
1365 bertho 1.19 ty += get_sheight(r->tags[i]->tag, &conf.tag_font) + conf.rev_separator;
1366 bertho 1.1 }
1367     }
1368    
1369 bertho 1.26 static void draw_branch_box(gdImagePtr im, branch_t *b, int xp, int yp)
1370 bertho 1.1 {
1371 bertho 1.25 int lx;
1372     int rx;
1373 bertho 1.16 int i;
1374 bertho 1.1 int yy;
1375 bertho 1.25 int x2;
1376 bertho 1.16
1377 bertho 1.25 if(conf.left_right)
1378     {
1379     lx = b->cx;
1380     rx = lx + b->w;
1381     x2 = b->cx + b->w/2;
1382     }
1383     else
1384     {
1385     lx = b->cx - b->w/2;
1386     rx = lx + b->w;
1387     x2 = b->cx;
1388     }
1389 bertho 1.26 draw_rbox(im, lx+xp, yp, rx+xp, yp+b->h, 5, &conf.branch_color, &conf.branch_bgcolor);
1390 bertho 1.1 yy = conf.branch_tspace;
1391 bertho 1.32 if(!b->nfolds)
1392 bertho 1.1 {
1393 bertho 1.32 draw_string(im, b->branch->branch, &conf.branch_font, x2+xp, yp+yy, ALIGN_HC, &conf.branch_color);
1394     yy += get_sheight(b->branch->branch, &conf.branch_font);
1395     for(i = 0; i < b->ntags; i++)
1396     {
1397     draw_string(im, b->tags[i]->tag, &conf.branch_tag_font, x2+xp, yp+yy, ALIGN_HC, &conf.branch_tag_color);
1398     yy += get_sheight(b->tags[i]->tag, &conf.branch_font);
1399     }
1400     }
1401     else
1402     {
1403     int y1, y2;
1404     int tx = lx + b->fw + conf.branch_lspace;
1405     int nx = tx - get_swidth(" ", &conf.branch_font);
1406     draw_string(im, b->branch->branch, &conf.branch_font, nx+xp, yp+yy, ALIGN_HR, &conf.branch_color);
1407     y1 = get_sheight(b->branch->branch, &conf.branch_font);
1408     draw_string(im, b->tags[0]->tag, &conf.branch_tag_font, tx+xp, yp+yy, ALIGN_HL, &conf.branch_tag_color);
1409     y2 = get_sheight(b->tags[0]->tag, &conf.branch_font);
1410     yy += MAX(y1, y2);
1411     for(i = 0; i < b->nfolds; i++)
1412     {
1413     draw_string(im, b->folds[i]->branch->branch, &conf.branch_font, nx+xp, yp+yy, ALIGN_HR, &conf.branch_color);
1414     y1 = get_sheight(b->folds[i]->branch->branch, &conf.branch_font);
1415     draw_string(im, b->folds[i]->tags[0]->tag, &conf.branch_tag_font, tx+xp, yp+yy, ALIGN_HL, &conf.branch_tag_color);
1416     y2 = get_sheight(b->folds[i]->tags[0]->tag, &conf.branch_font);
1417     yy += MAX(y1, y2);
1418     }
1419 bertho 1.1 }
1420 bertho 1.16 }
1421    
1422 bertho 1.19 static void draw_branch(gdImagePtr im, branch_t *b)
1423 bertho 1.16 {
1424 bertho 1.25 int yy, xx;
1425 bertho 1.16 int i;
1426 bertho 1.17 int line[4];
1427 bertho 1.20 int l;
1428     int sign;
1429 bertho 1.17
1430     line[0] = conf.rev_color.id;
1431     line[1] = gdTransparent;
1432     line[1] = gdTransparent;
1433     line[3] = conf.rev_color.id;
1434    
1435 bertho 1.26 draw_branch_box(im, b, 0, conf.left_right ? b->y - b->h/2 : b->y);
1436 bertho 1.1
1437 bertho 1.25 if(conf.left_right)
1438 bertho 1.16 {
1439 bertho 1.25 if(conf.upside_down)
1440 bertho 1.16 {
1441 bertho 1.25 xx = b->cx;
1442     for(i = 0; i < b->nrevs; i++)
1443 bertho 1.20 {
1444 bertho 1.25 revision_t *r = b->revs[i];
1445     gdImageSetStyle(im, line, r->stripped ? 4 : 1);
1446     gdImageLine(im, xx, r->y, r->cx+r->w, r->y, gdStyled);
1447     for(sign = l = 1; l < conf.thick_lines; l++)
1448     {
1449     int pp = (l+1)/2*sign;
1450     gdImageLine(im, xx, r->y+pp, r->cx+r->w, r->y+pp, gdStyled);
1451     sign *= -1;
1452     }
1453     draw_rev(im, r);
1454     xx = r->cx;
1455     }
1456 bertho 1.32 if(conf.branch_dupbox && b->nrevs)
1457 bertho 1.25 {
1458     i = b->cx - b->tw + b->w;
1459 bertho 1.26 gdImageLine(im, xx, b->y, i+b->w, b->y, conf.rev_color.id);
1460 bertho 1.25 for(sign = l = 1; l < conf.thick_lines; l++)
1461     {
1462     int pp = (l+1)/2*sign;
1463 bertho 1.26 gdImageLine(im, xx, b->y+pp, i+b->w, b->y+pp, conf.rev_color.id);
1464 bertho 1.25 sign *= -1;
1465     }
1466 bertho 1.26 draw_branch_box(im, b, i - b->cx, b->y - b->h/2);
1467 bertho 1.20 }
1468 bertho 1.16 }
1469 bertho 1.25 else
1470 bertho 1.16 {
1471 bertho 1.25 xx = b->cx + b->w;
1472     for(i = 0; i < b->nrevs; i++)
1473     {
1474     revision_t *r = b->revs[i];
1475     gdImageSetStyle(im, line, r->stripped ? 4 : 1);
1476     gdImageLine(im, xx, r->y, r->cx, r->y, gdStyled);
1477     for(sign = l = 1; l < conf.thick_lines; l++)
1478     {
1479     int pp = (l+1)/2*sign;
1480     gdImageLine(im, xx, r->y+pp, r->cx, r->y+pp, gdStyled);
1481     sign *= -1;
1482     }
1483     draw_rev(im, r);
1484     xx = r->cx + r->w;
1485     }
1486 bertho 1.32 if(conf.branch_dupbox && b->nrevs)
1487 bertho 1.20 {
1488 bertho 1.25 i = b->cx + b->tw - b->w;
1489     gdImageLine(im, xx, b->y, i, b->y, conf.rev_color.id);
1490     for(sign = l = 1; l < conf.thick_lines; l++)
1491     {
1492     int pp = (l+1)/2*sign;
1493     gdImageLine(im, xx, b->y+pp, i, b->y+pp, conf.rev_color.id);
1494     sign *= -1;
1495     }
1496 bertho 1.26 draw_branch_box(im, b, i - b->cx, b->y - b->h/2);
1497 bertho 1.20 }
1498 bertho 1.16 }
1499     }
1500     else
1501 bertho 1.1 {
1502 bertho 1.25 if(conf.upside_down)
1503 bertho 1.16 {
1504 bertho 1.25 yy = b->y;
1505     for(i = 0; i < b->nrevs; i++)
1506 bertho 1.20 {
1507 bertho 1.25 revision_t *r = b->revs[i];
1508     gdImageSetStyle(im, line, r->stripped ? 4 : 1);
1509     gdImageLine(im, r->cx, yy, r->cx, r->y+r->h, gdStyled);
1510     for(sign = l = 1; l < conf.thick_lines; l++)
1511     {
1512     int pp = (l+1)/2*sign;
1513     gdImageLine(im, r->cx+pp, yy, r->cx+pp, r->y+r->h, gdStyled);
1514     sign *= -1;
1515     }
1516     draw_rev(im, r);
1517     yy = r->y;
1518     }
1519 bertho 1.32 if(conf.branch_dupbox && b->nrevs)
1520 bertho 1.25 {
1521     i = b->y - b->th + b->h;
1522     gdImageLine(im, b->cx, yy, b->cx, i, conf.rev_color.id);
1523     for(sign = l = 1; l < conf.thick_lines; l++)
1524     {
1525     int pp = (l+1)/2*sign;
1526     gdImageLine(im, b->cx+pp, yy, b->cx+pp, i, conf.rev_color.id);
1527     sign *= -1;
1528     }
1529 bertho 1.26 draw_branch_box(im, b, 0, i);
1530 bertho 1.20 }
1531 bertho 1.16 }
1532 bertho 1.25 else
1533 bertho 1.16 {
1534 bertho 1.25 yy = b->y + b->h;
1535     for(i = 0; i < b->nrevs; i++)
1536     {
1537     revision_t *r = b->revs[i];
1538     gdImageSetStyle(im, line, r->stripped ? 4 : 1);
1539     gdImageLine(im, r->cx, yy, r->cx, r->y, gdStyled);
1540     for(sign = l = 1; l < conf.thick_lines; l++)
1541     {
1542     int pp = (l+1)/2*sign;
1543     gdImageLine(im, r->cx+pp, yy, r->cx+pp, r->y, gdStyled);
1544     sign *= -1;
1545     }
1546     draw_rev(im, r);
1547     yy = r->y + r->h;
1548     }
1549 bertho 1.32 if(conf.branch_dupbox && b->nrevs)
1550 bertho 1.20 {
1551 bertho 1.25 i = b->y + b->th - b->h;
1552     gdImageLine(im, b->cx, yy, b->cx, i, conf.rev_color.id);
1553     for(sign = l = 1; l < conf.thick_lines; l++)
1554     {
1555     int pp = (l+1)/2*sign;
1556     gdImageLine(im, b->cx+pp, yy, b->cx+pp, i, conf.rev_color.id);
1557     sign *= -1;
1558     }
1559 bertho 1.26 draw_branch_box(im, b, 0, i);
1560 bertho 1.20 }
1561 bertho 1.16 }
1562 bertho 1.1 }
1563     }
1564    
1565 bertho 1.19 static void draw_connector(gdImagePtr im, branch_t *b)
1566 bertho 1.1 {
1567 bertho 1.20 int l;
1568     int sign;
1569 bertho 1.1 revision_t *r = b->branchpoint;
1570 bertho 1.7 int x1 = r->cx + r->w/2 + 2;
1571 bertho 1.1 int y1 = r->y + r->h/2;
1572 bertho 1.7 int x2 = b->cx;
1573 bertho 1.1 int y2 = b->y;
1574 bertho 1.25
1575     if(conf.left_right)
1576     {
1577     x2 = r->cx + r->w/2;
1578 bertho 1.26 y2 = r->y + r->h/2 + 3;
1579 bertho 1.25 x1 = b->cx;
1580     y1 = b->y;
1581     if(conf.upside_down)
1582     x1 += b->w;
1583     }
1584     else
1585     {
1586     x1 = r->cx + r->w/2 + 2;
1587     y1 = r->y + r->h/2;
1588     x2 = b->cx;
1589     y2 = b->y;
1590     if(conf.upside_down)
1591     y2 += b->h;
1592     }
1593 bertho 1.1 gdImageLine(im, x1, y1, x2, y1, conf.branch_color.id);
1594     gdImageLine(im, x2, y1, x2, y2, conf.branch_color.id);
1595 bertho 1.20 for(sign = l = 1; l < conf.thick_lines; l++)
1596     {
1597     int pp = (l+1)/2*sign;
1598 bertho 1.26 gdImageLine(im, x1, y1+pp, x2, y1+pp, conf.branch_color.id);
1599     gdImageLine(im, x2+pp, y1, x2+pp, y2, conf.branch_color.id);
1600 bertho 1.20 sign *= -1;
1601     }
1602 bertho 1.1 }
1603    
1604 bertho 1.31 static void draw_merges(gdImagePtr im, rcsfile_t *rcs, int dot)
1605 bertho 1.30 {
1606     int i;
1607     for(i = 0; i < rcs->nmerges; i++)
1608     {
1609     revision_t *fr = rcs->merges[i].from->logrev;
1610     revision_t *tr = rcs->merges[i].to->logrev;
1611     int x1, x2, y1, y2;
1612 bertho 1.31 if(!fr || !tr || fr == tr)
1613     continue; /* This can happen with detached tags and self-references */
1614 bertho 1.30 if(conf.left_right)
1615     {
1616 bertho 1.32 if(fr->branch == tr->branch)
1617 bertho 1.30 {
1618 bertho 1.32 y1 = fr->y - fr->h/2;
1619 bertho 1.30 y2 = tr->y - tr->h/2;
1620     }
1621     else
1622     {
1623 bertho 1.32 if(fr->y < tr->y)
1624     {
1625     y1 = fr->y + fr->h/2;
1626     y2 = tr->y - tr->h/2;
1627     }
1628     else
1629     {
1630     y1 = fr->y - fr->h/2;
1631     y2 = tr->y + tr->h/2;
1632     }
1633 bertho 1.30 }
1634     x1 = fr->cx + fr->w/2;
1635     x2 = tr->cx + tr->w/2;
1636     }
1637     else
1638     {
1639 bertho 1.32 if(fr->branch == tr->branch)
1640 bertho 1.30 {
1641 bertho 1.32 x1 = fr->cx - fr->w/2;
1642 bertho 1.30 x2 = tr->cx - tr->w/2;
1643     }
1644     else
1645     {
1646 bertho 1.32 if(fr->cx < tr->cx)
1647     {
1648     x1 = fr->cx + fr->w/2;
1649     x2 = tr->cx - tr->w/2;
1650     }
1651     else
1652     {
1653     x1 = fr->cx - fr->w/2;
1654     x2 = tr->cx + tr->w/2;
1655     }
1656 bertho 1.30 }
1657 bertho 1.31 y1 = fr->y + rcs->merges[i].from->yofs;
1658     y2 = tr->y + rcs->merges[i].to->yofs;
1659 bertho 1.30 }
1660 bertho 1.31 if(dot)
1661 bertho 1.30 {
1662 bertho 1.31 int o = conf.left_right ? 1 : 0;
1663     gdImageArc(im, x2, y2+o, 8, 8, 0, 360, conf.merge_color.id);
1664     gdImageFillToBorder(im, x2+1, y2+o+1, conf.merge_color.id, conf.merge_color.id);
1665 bertho 1.30 }
1666     else
1667     {
1668 bertho 1.31 if(conf.left_right)
1669 bertho 1.30 {
1670 bertho 1.32 if(fr->branch == tr->branch)
1671 bertho 1.31 {
1672 bertho 1.32 int yy = (y1 < y2 ? y1 : y2) - 5;
1673     gdImageLine(im, x1, y1, x1, yy, conf.merge_color.id);
1674     gdImageLine(im, x2, y2, x2, yy, conf.merge_color.id);
1675     gdImageLine(im, x1, yy, x2, yy, conf.merge_color.id);
1676 bertho 1.31 }
1677     else
1678     {
1679 bertho 1.32 if(y1 > y2)
1680     {
1681     gdImageLine(im, x1, y1, x1, y1-3, conf.merge_color.id);
1682     gdImageLine(im, x2, y2+1, x2, y2+3+1, conf.merge_color.id);
1683     gdImageLine(im, x1, y1-3, x2, y2+3+1, conf.merge_color.id);
1684     }
1685     else
1686     {
1687     gdImageLine(im, x1, y1+1, x1, y1+3+1, conf.merge_color.id);
1688     gdImageLine(im, x2, y2, x2, y2-3, conf.merge_color.id);
1689     gdImageLine(im, x1, y1+3+1, x2, y2-3, conf.merge_color.id);
1690     }
1691 bertho 1.31 }
1692 bertho 1.30 }
1693     else
1694     {
1695 bertho 1.32 if(fr->branch == tr->branch)
1696 bertho 1.31 {
1697 bertho 1.32 int xx = (x1 < x2 ? x1 : x2) - 5;
1698     gdImageLine(im, xx, y1, x1, y1, conf.merge_color.id);
1699     gdImageLine(im, xx, y2, x2, y2, conf.merge_color.id);
1700     gdImageLine(im, xx, y1, xx, y2, conf.merge_color.id);
1701 bertho 1.31 }
1702     else
1703     {
1704 bertho 1.32 if(x1 > x2)
1705     {
1706     gdImageLine(im, x1, y1, x1-3, y1, conf.merge_color.id);
1707     gdImageLine(im, x2, y2, x2+3, y2, conf.merge_color.id);
1708     gdImageLine(im, x1-3, y1, x2+3, y2, conf.merge_color.id);
1709     }
1710     else
1711     {
1712     gdImageLine(im, x1, y1, x1+3, y1, conf.merge_color.id);
1713     gdImageLine(im, x2, y2, x2-3, y2, conf.merge_color.id);
1714     gdImageLine(im, x1+3, y1, x2-3, y2, conf.merge_color.id);
1715     }
1716 bertho 1.31 }
1717 bertho 1.30 }
1718     }
1719     }
1720     }
1721    
1722 bertho 1.19 static void alloc_color(gdImagePtr im, color_t *c)
1723 bertho 1.9 {
1724     c->id = gdImageColorAllocate(im, c->r, c->g, c->b);
1725     }
1726    
1727 bertho 1.7 gdImagePtr make_image(rcsfile_t *rcs)
1728 bertho 1.1 {
1729     gdImagePtr im;
1730     int i;
1731 bertho 1.8 char *cptr;
1732 bertho 1.1
1733 bertho 1.15 cptr = expand_string(conf.title, rcs, NULL, NULL, NULL, NULL);
1734     i = get_swidth(cptr, &conf.title_font);
1735     if(rcs->tw+conf.margin_left+conf.margin_right > i)
1736     i = rcs->tw+conf.margin_left+conf.margin_right;
1737     im = gdImageCreate(i, rcs->th+conf.margin_top+conf.margin_bottom);
1738 bertho 1.9 alloc_color(im, &conf.color_bg);
1739     alloc_color(im, &conf.tag_color);
1740     alloc_color(im, &conf.rev_color);
1741     alloc_color(im, &conf.rev_bgcolor);
1742     alloc_color(im, &conf.rev_text_color);
1743     alloc_color(im, &conf.branch_color);
1744 bertho 1.19 alloc_color(im, &conf.branch_tag_color);
1745 bertho 1.9 alloc_color(im, &conf.branch_bgcolor);
1746     alloc_color(im, &conf.title_color);
1747 bertho 1.30 alloc_color(im, &conf.merge_color);
1748 bertho 1.9 alloc_color(im, &black_color);
1749     alloc_color(im, &white_color);
1750 bertho 1.1
1751 bertho 1.19 if(conf.transparent_bg)
1752     gdImageColorTransparent(im, conf.color_bg.id);
1753    
1754 bertho 1.30 if(!conf.merge_front)
1755 bertho 1.31 draw_merges(im, rcs, 0);
1756 bertho 1.30
1757 bertho 1.1 for(i = 0; i < rcs->nbranches; i++)
1758 bertho 1.32 {
1759     if(!rcs->branches[i]->folded)
1760     draw_branch(im, rcs->branches[i]);
1761     }
1762 bertho 1.31
1763     draw_merges(im, rcs, 1); /* The dots of the merge dest */
1764    
1765 bertho 1.1 for(i = 0; i < rcs->nbranches; i++)
1766     {
1767     if(rcs->branches[i]->branchpoint)
1768     draw_connector(im, rcs->branches[i]);
1769     }
1770 bertho 1.9 draw_stringnl(im, cptr, &conf.title_font, conf.title_x, conf.title_y, conf.title_align, &conf.title_color);
1771 bertho 1.8 xfree(cptr);
1772 bertho 1.1
1773 bertho 1.30 if(conf.merge_front)
1774 bertho 1.31 draw_merges(im, rcs, 0);
1775 bertho 1.30
1776 bertho 1.1 return im;
1777     }
1778    
1779 bertho 1.7 /*
1780     **************************************************************************
1781     * Layout routines
1782 bertho 1.19 *
1783     * Branch BBox:
1784     * left = center_x - total_width / 2 (cx-tw)/2
1785     * right = center_x + total_width / 2 (cx+tw)/2
1786     * top = y_pos (y)
1787     * bottom = y_pos + total_height (y+th)
1788     *
1789     * Margins of branches:
1790     *
1791     * . .
1792     * . .
1793     * +--------------+
1794     * ^
1795     * | branch_margin .
1796     * v .
1797     * ----------------+ .
1798     * | ^ |
1799     * | | branch_connect |
1800     * | v |
1801     *..-+ +t-----+------+ +------+------+
1802     * | l | | |
1803     * | <--> | branch bbox | <--> | branch bbox |
1804     * | | | r | | |
1805     *..-+ | +------------b+ | +-------------+
1806     * | ^ branch_margin
1807     * | | branch_margin
1808     * | v
1809     * | +-------------+
1810     * | . .
1811     * | . .
1812     * |
1813     * branch_margin
1814     *
1815     * FIXME: There are probable som +/-1 errors in the code...
1816     * (notably shadows are not calculated in the margins)
1817 bertho 1.7 **************************************************************************
1818     */
1819 bertho 1.19 static void move_branch(branch_t *b, int x, int y)
1820 bertho 1.1 {
1821     int i;
1822 bertho 1.7 b->cx += x;
1823 bertho 1.1 b->y += y;
1824     for(i = 0; i < b->nrevs; i++)
1825     {
1826 bertho 1.7 b->revs[i]->cx += x;
1827 bertho 1.1 b->revs[i]->y += y;
1828     }
1829     }
1830    
1831 bertho 1.19 static void initial_reposition_branch(revision_t *r, int *x, int *w)
1832 bertho 1.6 {
1833     int i, j;
1834     for(j = 0; j < r->nbranches; j++)
1835     {
1836     branch_t *b = r->branches[j];
1837 bertho 1.7 *x += *w + conf.rev_minline + b->tw/2 - b->cx;
1838 bertho 1.6 *w = b->tw/2;
1839 bertho 1.7 move_branch(b, *x, r->y + r->h/2 + conf.branch_connect);
1840     *x = b->cx;
1841 bertho 1.6 /* Recurse to move branches of branched revisions */
1842     for(i = b->nrevs-1; i >= 0; i--)
1843     {
1844 bertho 1.18 initial_reposition_branch(b->revs[i], x, w);
1845 bertho 1.6 }
1846     }
1847     }
1848    
1849 bertho 1.25 static void initial_reposition_branch_lr(revision_t *r, int *y, int *h)
1850     {
1851     int i, j;
1852     for(j = 0; j < r->nbranches; j++)
1853     {
1854     branch_t *b = r->branches[j];
1855     *y += *h + conf.rev_minline + b->th/2 - b->y;
1856     *h = b->th/2;
1857     move_branch(b, r->cx + r->w/2 + conf.branch_connect, *y);
1858     *y = b->y;
1859     /* Recurse to move branches of branched revisions */
1860     for(i = b->nrevs-1; i >= 0; i--)
1861     {
1862 bertho 1.32 initial_reposition_branch_lr(b->revs[i], y, h);
1863 bertho 1.25 }
1864     }
1865     }
1866    
1867 bertho 1.19 static void rect_union(int *x, int *y, int *w, int *h, branch_t *b)
1868 bertho 1.1 {
1869     int x1 = *x;
1870     int x2 = x1 + *w;
1871     int y1 = *y;
1872     int y2 = y1 + *h;
1873 bertho 1.25 int xx1;
1874     int xx2;
1875     int yy1;
1876     int yy2;
1877    
1878     if(conf.left_right)
1879     {
1880     xx1 = b->cx;
1881     yy1 = b->y - b->th/2;
1882     }
1883     else
1884     {
1885     xx1 = b->cx - b->tw/2;
1886     yy1 = b->y;
1887     }
1888     xx2 = xx1 + b->tw;
1889     yy2 = yy1 + b->th;
1890    
1891 bertho 1.1 x1 = MIN(x1, xx1);
1892     x2 = MAX(x2, xx2);
1893     y1 = MIN(y1, yy1);
1894     y2 = MAX(y2, yy2);
1895     *x = x1;
1896     *y = y1;
1897     *w = x2 - x1;
1898     *h = y2 - y1;
1899     }
1900    
1901 bertho 1.19 static int branch_intersects(int top, int bottom, int left, branch_t *b)
1902 bertho 1.7 {
1903     int br = b->cx + b->tw/2;
1904     int bt = b->y - conf.branch_connect - conf.branch_margin/2;
1905     int bb = b->y + b->th + conf.branch_margin/2;
1906     return !(bt > bottom || bb < top || br >= left);
1907     }
1908    
1909 bertho 1.25 static int branch_intersects_lr(int left, int right, int top, branch_t *b)
1910     {
1911     int bt = b->y + b->th/2;
1912     int bl = b->cx - conf.branch_connect - conf.branch_margin/2;
1913     int br = b->cx + b->tw + conf.branch_margin/2;
1914     return !(bl > right || br < left || bt >= top);
1915     }
1916    
1917 bertho 1.19 static int kern_branch(rcsfile_t *rcs, branch_t *b)
1918 bertho 1.7 {
1919     int left = b->cx - b->tw/2;
1920     int top = b->y - conf.branch_connect - conf.branch_margin/2;
1921     int bottom = b->y + b->th + conf.branch_margin/2;
1922     int i;
1923     int xpos = 0;
1924    
1925     for(i = 0; i < rcs->nbranches; i++)
1926     {
1927     branch_t *bp = rcs->branches[i];
1928     if(bp == b)
1929     continue;
1930     if(branch_intersects(top, bottom, left, bp))
1931     {
1932     int m = bp->cx + bp->tw/2 + conf.branch_margin;
1933     if(m > xpos)
1934     xpos = m;
1935     }
1936     }
1937     if(xpos && (b->cx - b->tw/2) - xpos > 0)
1938     {
1939     move_branch(b, xpos - (b->cx - b->tw/2), 0);
1940     return 1;
1941     }
1942     return 0;
1943     }
1944    
1945 bertho 1.25 static int kern_branch_lr(rcsfile_t *rcs, branch_t *b)
1946     {
1947     int top = b->y - b->th/2;
1948     int left = b->cx - conf.branch_connect - conf.branch_margin/2;
1949     int right = b->cx + b->tw + conf.branch_margin/2;
1950     int i;
1951     int ypos = 0;
1952    
1953     for(i = 0; i < rcs->nbranches; i++)
1954     {
1955     branch_t *bp = rcs->branches[i];
1956     if(bp == b)
1957     continue;
1958     if(branch_intersects_lr(left, right, top, bp))
1959     {
1960     int m = bp->y + bp->th/2 + conf.branch_margin;
1961     if(m > ypos)
1962     ypos = m;
1963     }
1964     }
1965     if(ypos && (b->y - b->th/2) - ypos > 0)
1966     {
1967     move_branch(b, 0, ypos - (b->y - b->th/2));
1968     return 1;
1969     }
1970     return 0;
1971     }
1972    
1973 bertho 1.19 static int kern_tree(rcsfile_t *rcs)
1974 bertho 1.18 {
1975     int i;
1976     int moved;
1977 bertho 1.19 int safeguard;
1978     int totalmoved = 0;
1979     for(moved = 1, safeguard = LOOPSAFEGUARD; moved && safeguard; safeguard--)
1980 bertho 1.18 {
1981     moved = 0;
1982     for(i = 1; i < rcs->nbranches; i++)
1983     {
1984 bertho 1.25 if(conf.left_right)
1985     moved += kern_branch_lr(rcs, rcs->branches[i]);
1986     else
1987     moved += kern_branch(rcs, rcs->branches[i]);
1988 bertho 1.18 }
1989 bertho 1.19 totalmoved += moved;
1990 bertho 1.18 #ifdef DEBUG
1991     fprintf(stderr, "kern_tree: moved=%d\n", moved);
1992     #endif
1993     }
1994 bertho 1.19 if(!quiet && !safeguard)
1995     fprintf(stderr, "kern_tree: safeguard terminated possible infinite loop; please report.\n");
1996     return totalmoved;
1997     }
1998    
1999     static int index_of_revision(revision_t *r)
2000     {
2001     branch_t *b = r->branch;
2002     int i;
2003     for(i = 0; i < b->nrevs; i++)
2004     {
2005     if(r == b->revs[i])
2006     return i;
2007     }
2008     fprintf(stderr, "index_of_revision: Cannot find revision in branch\n");
2009     return 0;
2010 bertho 1.18 }
2011    
2012 bertho 1.19 static void branch_bbox(branch_t *br, int *l, int *r, int *t, int *b)
2013     {
2014     if(l) *l = br->cx - br->tw/2;
2015     if(r) *r = br->cx + br->tw/2;
2016     if(t) *t = br->y;
2017 bertho 1.32 if(b) *b = br->y + br->th + ((conf.branch_dupbox && br->nrevs) ? conf.rev_minline + br->h : 0);
2018 bertho 1.19 }
2019    
2020     static void branch_ext_bbox(branch_t *br, int *l, int *r, int *t, int *b)
2021     {
2022     int extra = conf.branch_margin & 1; /* Correct +/-1 error on div 2 */
2023     branch_bbox(br, l, r, t, b);
2024     if(l) *l -= conf.branch_margin/2;
2025     if(r) *r += conf.branch_margin/2 + extra;
2026     if(t) *t -= conf.branch_connect + conf.branch_margin/2;
2027     if(b) *b += conf.branch_margin/2 + extra;
2028     }
2029    
2030     static int branch_distance(branch_t *br1, branch_t *br2)
2031     {
2032     int l1, r1, t1, b1;
2033     int l2, r2, t2, b2;
2034     assert(br1 != NULL);
2035     assert(br2 != NULL);
2036     branch_bbox(br1, &l1, &r1, NULL, NULL);
2037     branch_bbox(br2, &l2, &r2, NULL, NULL);
2038     branch_ext_bbox(br1, NULL, NULL, &t1, &b1);
2039     branch_ext_bbox(br2, NULL, NULL, &t2, &b2);
2040     /* Return:
2041     * - 0 if branches have no horizontal overlap
2042     * - positive if b1 is left of b2
2043     * - negative if b2 is left of b1
2044     */
2045     if((t1 > t2 && t1 < b2) || (b1 > t2 && b1 < b2))
2046     return l1 < l2 ? l2 - r1 : -(l1 - r2);
2047     else
2048     return 0;
2049     }
2050    
2051     static int space_needed(branch_t *br1, branch_t *br2)
2052     {
2053     int t1, b1;
2054     int t2, b2;
2055     assert(br1 != NULL);
2056     assert(br2 != NULL);
2057     assert(br1->cx < br2->cx); /* br1 must be left of br2 */
2058     branch_ext_bbox(br1, NULL, NULL, &t1, &b1);
2059     branch_ext_bbox(br2, NULL, NULL, &t2, &b2);
2060     /* Return:
2061     * - positive if top br1 is located lower than br2
2062     * - negatve is top br2 is located lower than br1
2063     */
2064     if(t1 > t2)
2065     return -(t1 - b2);
2066     else
2067     return t2 - b1;
2068     }
2069    
2070     static void move_yr_branch(branch_t *b, int dy)
2071 bertho 1.18 {
2072     int i, j;
2073     #ifdef DEBUG
2074 bertho 1.23 /* fprintf(stderr, "move_yr_branch: b=%s, dy=%d\n", b->branch->branch, dy);*/
2075 bertho 1.18 #endif
2076 bertho 1.19 b->y += dy;
2077     for(i = 0; i < b->nrevs; i++)
2078 bertho 1.18 {
2079 bertho 1.19 b->revs[i]->y += dy;
2080     for(j = 0; j < b->revs[i]->nbranches; j++)
2081 bertho 1.18 {
2082 bertho 1.19 #ifdef DEBUG
2083 bertho 1.23 /* fprintf(stderr, ".");*/
2084 bertho 1.19 #endif
2085     move_yr_branch(b->revs[i]->branches[j], dy);
2086 bertho 1.18 }
2087     }
2088     }
2089    
2090 bertho 1.19 static void move_trunk(revision_t *r, int dy)
2091 bertho 1.18 {
2092 bertho 1.19 int i, j;
2093 bertho 1.18 branch_t *b = r->branch;
2094 bertho 1.19 b->th += dy;
2095     for(i = index_of_revision(r); i < b->nrevs; i++)
2096 bertho 1.18 {
2097 bertho 1.19 #ifdef DEBUG
2098     fprintf(stderr, "move_trunk: start %s, moving %s by %d (b's %d)\n", r->rev->rev, b->revs[i]->rev->rev, dy, b->revs[i]->nbranches);
2099     #endif
2100     b->revs[i]->y += dy;
2101     for(j = 0; j < b->revs[i]->nbranches; j++)
2102     {
2103     move_yr_branch(b->revs[i]->branches[j], dy);
2104     }
2105 bertho 1.18 }
2106     }
2107    
2108 bertho 1.19 static int space_below(rcsfile_t *rcs, revision_t *r)
2109 bertho 1.18 {
2110 bertho 1.19 int i, j;
2111     int bl, br, bb;
2112 bertho 1.18 int space = INT_MAX;
2113 bertho 1.19 branch_t *b = r->branch;
2114     branch_t *minb = NULL;
2115    
2116     branch_ext_bbox(b, &bl, &br, NULL, &bb);
2117 bertho 1.18 for(i = 0; i < rcs->nbranches; i++)
2118     {
2119 bertho 1.19 int tbl, tbr, tbt;
2120 bertho 1.18 branch_t *tb = rcs->branches[i];
2121 bertho 1.19 branch_ext_bbox(tb, &tbl, &tbr, &tbt, NULL);
2122 bertho 1.18 if(tb == b)
2123     continue;
2124 bertho 1.19 if(tbt > bb) /* Must be below our branch */
2125 bertho 1.18 {
2126 bertho 1.19 if(tb->branchpoint) /* Take account for the horiz connector */
2127     tbl = tb->branchpoint->cx + tb->branchpoint->branch->tw/2;
2128     if((bl >= tbl && bl <= tbr) || (br <= tbr && br >= tbl))
2129 bertho 1.18 {
2130 bertho 1.19 int s = tbt - bb - conf.branch_connect;
2131     if(s < space)
2132 bertho 1.18 {
2133 bertho 1.19 space = s;
2134     minb = tb;
2135 bertho 1.18 }
2136     }
2137     }
2138     }
2139 bertho 1.19 if(b->branchpoint)
2140     {
2141     for(i = index_of_revision(r); i < b->nrevs; i++)
2142     {
2143     for(j = 0; j < b->revs[i]->nbranches; j++)
2144     {
2145     int s = space_below(rcs, b->revs[i]->branches[j]->revs[0]);
2146     if(s < space)
2147     space = s;
2148     }
2149     }
2150     }
2151     #ifdef DEBUG
2152     fprintf(stderr, "space_below: from %s have %d to %s\n", b->branch->branch, space, minb ? minb->branch->branch : "<recursed>");
2153     #endif
2154 bertho 1.18 return space;
2155     }
2156    
2157 bertho 1.19 static int space_available(rcsfile_t *rcs, branch_t *colbr, branch_t *tagbr, int *nl, revision_t **bpcommon)
2158 bertho 1.18 {
2159 bertho 1.19 int i;
2160     int space = 0;
2161     int nlinks = 0;
2162     revision_t *r;
2163     branch_t *b;
2164     branch_t *ancestor;
2165     revision_t *branchpoint;
2166    
2167     if(!tagbr->branchpoint || !colbr->branchpoint)
2168 bertho 1.18 {
2169 bertho 1.19 if(!quiet)
2170     fprintf(stderr, "space_available: Trying to stretch the top?\n");
2171     return 0;
2172 bertho 1.18 }
2173 bertho 1.19
2174     r = colbr->branchpoint;
2175     b = r->branch;
2176     branchpoint = tagbr->branchpoint;
2177     ancestor = branchpoint->branch;
2178     assert(b != NULL);
2179     assert(ancestor != NULL);
2180    
2181     while(1)
2182 bertho 1.18 {
2183 bertho 1.19 int s;
2184     int rtag = b == ancestor ? index_of_revision(branchpoint)+1 : 0;
2185     for(i = index_of_revision(r); i >= rtag; i--)
2186 bertho 1.18 {
2187 bertho 1.19 if(i > 0)
2188     s = b->revs[i]->y - (b->revs[i-1]->y + b->revs[i-1]->h);
2189     else
2190     s = b->revs[i]->y - (b->y + b->h);
2191     if(s < conf.rev_maxline)
2192     {
2193     space += conf.rev_maxline - s;
2194     nlinks++;
2195     }
2196 bertho 1.18 }
2197 bertho 1.19 s = space_below(rcs, r);
2198     if(s < space)
2199     space = s;
2200     #ifdef DEBUG
2201     if(space < 0)
2202     return -1;
2203     #endif
2204     if(b == ancestor)
2205     break;
2206     r = b->branchpoint;
2207     if(!r)
2208     {
2209     /* Not a common ancestor */
2210     r = colbr->branchpoint;
2211     b = r->branch;
2212     branchpoint = ancestor->branchpoint;
2213     if(!branchpoint)
2214     {
2215     if(!quiet)
2216     fprintf(stderr, "space_available: No common ancestor?\n");
2217     return 0;
2218     }
2219     ancestor = branchpoint->branch;
2220     assert(ancestor != NULL);
2221     nlinks = 0;
2222     space = 0;
2223     continue; /* Restart with a new ancestor */
2224     }
2225     b = r->branch;
2226     }
2227     if(nl)
2228     *nl = nlinks; /* Return the number of links that can stretch */
2229     if(bpcommon)
2230     *bpcommon = branchpoint; /* Return the ancestral branchpoint on the common branch */
2231     return space;
2232 bertho 1.18 }
2233    
2234 bertho 1.19 static int stretch_branches(rcsfile_t *rcs, branch_t *br1, branch_t *br2, int totalstretch)
2235 bertho 1.18 {
2236 bertho 1.19 revision_t *r;
2237     revision_t *bpcommon = NULL;
2238     branch_t *ancestor = NULL;
2239     branch_t *b;
2240 bertho 1.18 int i;
2241 bertho 1.19 int space;
2242     int nlinks;
2243     int dy;
2244     int rest;
2245    
2246     space = space_available(rcs, br1, br2, &nlinks, &bpcommon);
2247     if(bpcommon)
2248     ancestor = bpcommon->branch;
2249    
2250     #ifdef DEBUG
2251     if(space == -1)
2252     return 0;
2253     fprintf(stderr, "stretch_branches: space available %d over %d links common %s\n", space, nlinks, ancestor->branch->branch);
2254     #endif
2255     if(space < totalstretch)
2256     return 0;
2257    
2258     dy = totalstretch / nlinks;
2259     rest = totalstretch - dy * nlinks;
2260    
2261     r = br1->branchpoint;
2262     b = r->branch;
2263     while(1)
2264 bertho 1.18 {
2265 bertho 1.19 int rtag = b == ancestor ? index_of_revision(bpcommon)+1 : 0;
2266     for(i = index_of_revision(r); i >= rtag; i--)
2267     {
2268     int s, q;
2269     if(i > 0)
2270     s = b->revs[i]->y - (b->revs[i-1]->y + b->revs[i-1]->h);
2271     else
2272     s = b->revs[i]->y - (b->y + b->h);
2273     q = conf.rev_maxline - s;
2274     if(q > 0)
2275     {
2276     int d = rest ? rest/nlinks+1 : 0;
2277     if(q >= dy+d)
2278     {
2279     move_trunk(b->revs[i], dy+d);
2280     }
2281     else
2282     {
2283     move_trunk(b->revs[i], q);
2284     rest += dy+d - q;
2285     }
2286     rest -= d;
2287     nlinks--;
2288     }
2289     }
2290     if(b == ancestor)
2291     break;
2292     r = b->branchpoint;
2293     assert(r != NULL); /* else 'space_available' wouldn't have returned positively */
2294     b = r->branch;
2295 bertho 1.18 }
2296 bertho 1.19 return 1;
2297 bertho 1.18 }
2298    
2299 bertho 1.19 static branch_t *find_collision_branch(rcsfile_t *rcs, branch_t *b)
2300 bertho 1.18 {
2301     int i;
2302 bertho 1.19 int dist = INT_MAX;
2303     branch_t *col = NULL;
2304    
2305 bertho 1.18 for(i = 0; i < rcs->nbranches; i++)
2306     {
2307 bertho 1.19 int t = branch_distance(rcs->branches[i], b);
2308     if(t > 0 && t < dist)
2309 bertho 1.18 {
2310 bertho 1.19 dist = t;
2311     col = rcs->branches[i];
2312 bertho 1.18 }
2313     }
2314 bertho 1.19 return col;
2315 bertho 1.18 }
2316    
2317 bertho 1.19 void auto_stretch(rcsfile_t *rcs)
2318 bertho 1.18 {
2319 bertho 1.19 int i;
2320     int safeguard;
2321 bertho 1.18
2322 bertho 1.19 for(i = 0, safeguard = LOOPSAFEGUARD; i < rcs->nbranches && safeguard; i++)
2323 bertho 1.18 {
2324 bertho 1.19 int bl, pr;
2325     branch_t *b = rcs->branches[i];
2326     if(!b->branchpoint)
2327     continue;
2328     branch_bbox(b, &bl, NULL, NULL, NULL);
2329     branch_bbox(b->branchpoint->branch, NULL, &pr, NULL, NULL);
2330     if(bl - conf.branch_margin - pr > 0)
2331 bertho 1.18 {
2332 bertho 1.19 branch_t *col;
2333 bertho 1.18 int spaceneeded;
2334 bertho 1.19 /* There is a potential to move branch b further left.
2335     * All branches obstructing this one from moving further
2336     * left must be originating from revisions below
2337     * b->branchpoint until a common ancester.
2338     * So, we search all branches for a branch that lies left
2339     * of b and is closest to b. This is then the collission
2340     * branch that needs to be moved.
2341     */
2342     col = find_collision_branch(rcs, b);
2343     if(!col)
2344 bertho 1.18 continue;
2345 bertho 1.19 spaceneeded = space_needed(col, b);
2346     if(spaceneeded < 0)
2347     continue;
2348     #ifdef DEBUG
2349     fprintf(stderr, "auto_stretch: %s collides %s need %d\n", b->branch->branch, col->branch->branch, spaceneeded);
2350     #endif
2351     /* Trace the collision branch back to find the common ancester
2352     * of both col and b. All revisions encountered while traversing
2353     * backwards must be stretched, including all revisions on the
2354     * common ancester from where the branches sprout.
2355     */
2356     if(stretch_branches(rcs, col, b, spaceneeded))
2357 bertho 1.18 {
2358 bertho 1.19 if(kern_tree(rcs))
2359     {
2360     /* Restart the process because movement can
2361     * cause more movement.
2362     */
2363     i = 0 - 1; /* -1 for the i++ of the loop */
2364     safeguard--; /* Prevent infinite loop, just in case */
2365     }
2366 bertho 1.23 /*return;*/
2367 bertho 1.18 }
2368     }
2369     }
2370 bertho 1.19 if(!quiet && !safeguard)
2371     fprintf(stderr, "auto_stretch: safeguard terminated possible infinite loop; please report.\n");
2372 bertho 1.18 }
2373    
2374 bertho 1.32 static void fold_branch(rcsfile_t *rcs, revision_t *r)
2375     {
2376     int i, j;
2377     branch_t *btag = NULL;
2378    
2379     if(r->nbranches < 2)
2380     return; /* Should not happen... */
2381    
2382     for(i = 0; i < r->nbranches; i++)
2383     {
2384     branch_t *b = r->branches[i];
2385     if(!b->nrevs && b->ntags < 2)
2386     {
2387     /* No commits in this branch and no duplicate tags */
2388     if(!btag)
2389     btag = b;
2390     else
2391     {
2392     /* We have consecutive empty branches, fold */
2393     b->folded = 1;
2394     for(j = 0; j < rcs->nbranches; j++)
2395     {
2396     if(b == rcs->branches[j])
2397     {
2398     /* Zap the branch from the admin */
2399     memmove(&rcs->branches[j],
2400     &rcs->branches[j+1],
2401     (rcs->nbranches - j - 1)*sizeof(rcs->branches[0]));
2402     rcs->nbranches--;
2403     break;
2404     }
2405    
2406     }
2407     memmove(&r->branches[i], &r->branches[i+1], (r->nbranches - i - 1)*sizeof(r->branches[0]));
2408     r->nbranches--;
2409     i--; /* We have one less now */
2410    
2411     /* Add to the fold-list */
2412     btag->folds = xrealloc(btag->folds, (btag->nfolds+1) * sizeof(btag->folds[0]));
2413     btag->folds[btag->nfolds] = b;
2414     btag->nfolds++;
2415     }
2416     }
2417     else
2418     {
2419     btag = NULL;
2420     /* Recursively fold sub-branches */
2421     for(j = 0; j < b->nrevs; j++)
2422     fold_branch(rcs, b->revs[j]);
2423     }
2424     }
2425     }
2426    
2427 bertho 1.7 void make_layout(rcsfile_t *rcs)
2428 bertho 1.1 {
2429     int i, j;
2430     int x, y;
2431     int w, h;
2432     int w2;
2433    
2434 bertho 1.16 /* Remove all unwanted revisions */
2435     if(conf.strip_untagged)
2436     {
2437 bertho 1.21 int fr = conf.strip_first_rev ? 0 : 1;
2438 bertho 1.16 for(i = 0; i < rcs->nbranches; i++)
2439     {
2440     branch_t *bp = rcs->branches[i];
2441 bertho 1.18 for(j = fr; j < bp->nrevs-1; j++)
2442 bertho 1.16 {
2443     if(!bp->revs[j]->ntags && !bp->revs[j]->nbranches)
2444     {
2445     memmove(&bp->revs[j], &bp->revs[j+1], (bp->nrevs-j-1) * sizeof(bp->revs[0]));
2446     bp->nrevs--;
2447 bertho 1.17 bp->revs[j]->stripped = 1;
2448 bertho 1.16 j--;
2449     }
2450     }
2451     }
2452     }
2453    
2454 bertho 1.32 /* Fold all empty branched in one box on the same branchpoint */
2455     if(conf.branch_fold)
2456     {
2457     for(i = 0; i < rcs->branches[0]->nrevs; i++)
2458     {
2459     if(rcs->branches[0]->revs[i]->nbranches > 1)
2460     fold_branch(rcs, rcs->branches[0]->revs[i]);
2461     }
2462     }
2463    
2464 bertho 1.1 /* Calculate the box-sizes of the revisions */
2465 bertho 1.7 for(i = 0; i < rcs->nsrev; i++)
2466 bertho 1.1 {
2467     revision_t *rp;
2468     int w;
2469     int h;
2470 bertho 1.7 rp = rcs->srev[i];
2471 bertho 1.9 rp->revtext = expand_string(conf.rev_text, rcs, rp, rp->rev, NULL, rp->ntags ? rp->tags[0] : NULL);
2472     w = get_swidth(rp->revtext, &conf.rev_text_font);
2473     j = get_swidth(rp->rev->rev, &conf.rev_font);
2474     if(j > w)
2475     w = j;
2476     h = get_sheight(rp->revtext, &conf.rev_text_font) + get_sheight(rp->rev->rev, &conf.rev_font);
2477 bertho 1.1 for(j = 0; j < rp->ntags; j++)
2478     {
2479     int ww = get_swidth(rp->tags[j]->tag, &conf.tag_font);
2480 bertho 1.31 int th;
2481 bertho 1.1 if(ww > w) w = ww;
2482 bertho 1.31 th = get_sheight(rp->tags[j]->tag, &conf.tag_font) + conf.rev_separator;
2483     rp->tags[j]->yofs = h + th/2 + conf.rev_tspace;
2484     h += th;
2485 bertho 1.1 }
2486     rp->w = w + conf.rev_lspace + conf.rev_rspace;
2487     rp->h = h + conf.rev_tspace + conf.rev_bspace;
2488     }
2489    
2490     /* Calculate the box-sizes of the branches */
2491     for(i = 0; i < rcs->nbranches; i++)
2492     {
2493     branch_t *bp = rcs->branches[i];
2494     int w;
2495     int h;
2496 bertho 1.32 if(!bp->nfolds)
2497     {
2498     w = get_swidth(bp->branch->branch, &conf.branch_font);
2499     h = get_sheight(bp->branch->branch, &conf.branch_font);
2500     for(j = 0; j < bp->ntags; j++)
2501     {
2502     int ww = get_swidth(bp->tags[j]->tag, &conf.branch_tag_font);
2503     if(ww > w) w = ww;
2504     h += get_sheight(bp->tags[j]->tag, &conf.branch_tag_font);
2505     }
2506     }
2507     else
2508 bertho 1.1 {
2509 bertho 1.32 int h1, h2;
2510     int w1, w2;
2511     int fw;
2512     w1 = get_swidth(bp->branch->branch, &conf.branch_font);
2513     w1 += get_swidth(" ", &conf.branch_font);
2514     w2 = get_swidth(bp->tags[0]->tag, &conf.branch_tag_font);
2515     fw = w1;
2516     w = w1 + w2;
2517     h1 = get_sheight(bp->branch->branch, &conf.branch_font);
2518     h2 = get_sheight(bp->tags[0]->tag, &conf.branch_tag_font);
2519     h = MAX(h1, h2);
2520     for(j = 0; j < bp->nfolds; j++)
2521     {
2522     w1 = get_swidth(bp->folds[j]->branch->branch, &conf.branch_font);
2523     w1 += get_swidth(" ", &conf.branch_font);
2524     w2 = get_swidth(bp->folds[j]->tags[0]->tag, &conf.branch_tag_font);
2525     if(w1 > fw)
2526     fw = w1;
2527     if(w1 + w2 > w)
2528     w = w1 + w2;
2529     h1 = get_sheight(bp->folds[j]->branch->branch, &conf.branch_font);
2530     h2 = get_sheight(bp->folds[j]->tags[0]->tag, &conf.branch_tag_font);
2531     h += MAX(h1, h2);
2532     }
2533     bp->fw = fw;
2534 bertho 1.1 }
2535     w += conf.branch_lspace + conf.branch_rspace;
2536     h += conf.branch_tspace + conf.branch_bspace;
2537     bp->w = w;
2538     bp->h = h;
2539 bertho 1.25 if(conf.left_right)
2540     {
2541     for(j = 0; j < bp->nrevs; j++)
2542     {
2543     if(bp->revs[j]->h > h)
2544     h = bp->revs[j]->h;
2545     w += bp->revs[j]->w + conf.rev_minline;
2546     }
2547 bertho 1.32 if(conf.branch_dupbox && bp->nrevs)
2548 bertho 1.25 w += bp->w + conf.rev_minline;
2549     }
2550     else
2551 bertho 1.1 {
2552 bertho 1.25 for(j = 0; j < bp->nrevs; j++)
2553     {
2554     if(bp->revs[j]->w > w)
2555     w = bp->revs[j]->w;
2556     h += bp->revs[j]->h + conf.rev_minline;
2557     }
2558 bertho 1.32 if(conf.branch_dupbox && bp->nrevs)
2559 bertho 1.25 h += bp->h + conf.rev_minline;
2560 bertho 1.1 }
2561     bp->th = h;
2562     bp->tw = w;
2563     }
2564    
2565     /* Calculate the relative positions of revs in a branch */
2566 bertho 1.25 if(conf.left_right)
2567     {
2568     for(i = 0; i < rcs->nbranches; i++)
2569     {
2570     branch_t *b = rcs->branches[i];
2571     y = b->th/2;
2572     x = b->w;
2573     b->y = y;
2574     b->cx = 0;
2575     for(j = 0; j < b->nrevs; j++)
2576     {
2577     x += conf.rev_minline;
2578     b->revs[j]->y = y;
2579     b->revs[j]->cx = x;
2580     x += b->revs[j]->w;
2581     }
2582     }
2583     }
2584     else
2585 bertho 1.1 {
2586 bertho 1.25 for(i = 0; i < rcs->nbranches; i++)
2587     {
2588     branch_t *b = rcs->branches[i];
2589     x = b->tw/2;
2590     y = b->h;
2591     b->cx = x;
2592     b->y = 0;
2593     for(j = 0; j < b->nrevs; j++)
2594     {
2595     y += conf.rev_minline;
2596     b->revs[j]->cx = x;
2597     b->revs[j]->y = y;
2598     y += b->revs[j]->h;
2599     }
2600 bertho 1.1 }
2601     }
2602    
2603 bertho 1.18 /* Initially reposition the branches from bottom to top progressively right */
2604 bertho 1.25 if(conf.left_right)
2605     {
2606     x = rcs->branches[0]->y;
2607     w2 = rcs->branches[0]->th / 2;
2608     for(i = rcs->branches[0]->nrevs-1; i >= 0; i--)
2609     {
2610     initial_reposition_branch_lr(rcs->branches[0]->revs[i], &x, &w2);
2611     }
2612     }
2613     else
2614 bertho 1.1 {
2615 bertho 1.25 x = rcs->branches[0]->cx;
2616     w2 = rcs->branches[0]->tw / 2;
2617     for(i = rcs->branches[0]->nrevs-1; i >= 0; i--)
2618     {
2619     initial_reposition_branch(rcs->branches[0]->revs[i], &x, &w2);
2620     }
2621 bertho 1.1 }
2622    
2623 bertho 1.19 /* Initially move branches left if there is room */
2624     kern_tree(rcs);
2625    
2626 bertho 1.18 /* Try to kern the branches more by expanding the inter-revision spacing */
2627 bertho 1.25 if(conf.auto_stretch && !conf.left_right)
2628 bertho 1.19 auto_stretch(rcs);
2629 bertho 1.7
2630     /* Move everything w.r.t. the top-left margin */
2631 bertho 1.1 for(i = 0; i < rcs->nbranches; i++)
2632     move_branch(rcs->branches[i], conf.margin_left, conf.margin_top);
2633    
2634     /* Calculate overall image size */
2635 bertho 1.25 if(conf.left_right)
2636     {
2637     x = rcs->branches[0]->cx;
2638     y = rcs->branches[0]->y - rcs->branches[0]->th/2;
2639     }
2640     else
2641     {
2642     x = rcs->branches[0]->cx - rcs->branches[0]->tw/2;
2643     y = rcs->branches[0]->y;
2644     }
2645 bertho 1.1 w = rcs->branches[0]->tw;
2646     h = rcs->branches[0]->th;
2647     for(i = 1; i < rcs->nbranches; i++)
2648     rect_union(&x, &y, &w, &h, rcs->branches[i]);
2649     rcs->tw = w;
2650     rcs->th = h;
2651 bertho 1.16
2652     /* Flip the entire tree */
2653     if(conf.upside_down)
2654     {
2655 bertho 1.25 if(conf.left_right)
2656     {
2657     x += rcs->tw;
2658     for(i = 0; i < rcs->nbranches; i++)
2659     {
2660     branch_t *b = rcs->branches[i];
2661     for(j = 0; j < b->nrevs; j++)
2662     {
2663     revision_t *r = b->revs[j];
2664 bertho 1.26 r->cx = x - r->cx - r->w + conf.margin_left;
2665 bertho 1.25 }
2666 bertho 1.26 b->cx = x - b->cx - b->w + conf.margin_left;
2667 bertho 1.25 }
2668     }
2669     else
2670 bertho 1.16 {
2671 bertho 1.25 y += rcs->th;
2672     for(i = 0; i < rcs->nbranches; i++)
2673 bertho 1.16 {
2674 bertho 1.25 branch_t *b = rcs->branches[i];
2675     for(j = 0; j < b->nrevs; j++)
2676     {
2677     revision_t *r = b->revs[j];
2678     r->y = y - r->y - r->h + conf.margin_top;
2679     }
2680     b->y = y - b->y - b->h + conf.margin_top;
2681 bertho 1.16 }
2682     }
2683     }
2684 bertho 1.1 }
2685    
2686     /*
2687     **************************************************************************
2688 bertho 1.6 * Imagemap functions
2689     **************************************************************************
2690     */
2691 bertho 1.26 void make_imagemap(rcsfile_t *rcs, FILE *fp, gdImagePtr im)
2692 bertho 1.6 {
2693     int i, j;
2694 bertho 1.29 const char *htp = conf.html_level == HTMLLEVEL_X ? " /" : "";
2695    
2696     switch(conf.html_level)
2697     {
2698     case HTMLLEVEL_4:
2699     fprintf(fp, "<map name=\"%s\" id=\"%s\">\n", conf.map_name, conf.map_name);
2700     break;
2701     case HTMLLEVEL_X:
2702     fprintf(fp, "<map id=\"%s\">\n", conf.map_name);
2703     break;
2704     default:
2705     fprintf(fp, "<map name=\"%s\">\n", conf.map_name);
2706     }
2707    
2708 bertho 1.6 for(i = 0; i < rcs->nbranches; i++)
2709     {
2710     branch_t *b = rcs->branches[i];
2711 bertho 1.8 tag_t *tag = b->ntags ? b->tags[0] : NULL;
2712 bertho 1.16 char *bhref = expand_string(conf.map_branch_href, rcs, NULL, b->branch, NULL, tag);
2713     char *balt = expand_string(conf.map_branch_alt, rcs, NULL, b->branch, NULL, tag);
2714 bertho 1.26 int x1;
2715     int x2;
2716     int y1;
2717     int y2;
2718    
2719     if(conf.left_right)
2720     {
2721     x1 = b->cx;
2722     y1 = b->y - b->h/2;
2723     x2 = b->cx + b->w;
2724     y2 = b->y + b->h/2;
2725     }
2726     else
2727     {
2728     x1 = b->cx - b->w/2;
2729     y1 = b->y;
2730     x2 = b->cx + b->w/2;
2731     y2 = b->y + b->h;
2732     }
2733 bertho 1.29 fprintf(fp, "\t<area shape=\"rect\" %s coords=\"%d,%d,%d,%d\" %s%s>\n",
2734     bhref, x1, y1, x2, y2, balt, htp);
2735 bertho 1.26 if(im)
2736     {
2737     gdImageFilledRectangle(im, x1-2, y1-2, x1+2, y1+2, conf.title_color.id);
2738     gdImageFilledRectangle(im, x2-2, y2-2, x2+2, y2+2, conf.tag_color.id);
2739     gdImageLine(im, x1, y1, x2, y2, conf.title_color.id);
2740     }
2741 bertho 1.6 for(j = 0; j < b->nrevs; j++)
2742     {
2743     revision_t *r = b->revs[j];
2744 bertho 1.13 revision_t* r1;
2745 bertho 1.26 int xoff = 1;
2746     int yoff = 1;
2747 bertho 1.16 char *href;
2748     char *alt;
2749 bertho 1.13
2750 bertho 1.8 tag = r->ntags ? r->tags[0] : NULL;
2751 bertho 1.9 href = expand_string(conf.map_rev_href, rcs, r, r->rev, NULL, tag);
2752     alt = expand_string(conf.map_rev_alt, rcs, r, r->rev, NULL, tag);
2753 bertho 1.26 if(conf.left_right)
2754     {
2755     x1 = r->cx;
2756     y1 = r->y - r->h/2;
2757     x2 = r->cx + r->w;
2758     y2 = r->y + r->h/2;
2759     }
2760     else
2761     {
2762     x1 = r->cx - r->w/2;
2763     y1 = r->y;
2764     x2 = r->cx + r->w/2;
2765     y2 = r->y + r->h;
2766     }
2767 bertho 1.29 fprintf(fp, "\t<area shape=\"rect\" %s coords=\"%d,%d,%d,%d\" %s%s>\n",
2768     href, x1, y1, x2, y2, alt, htp);
2769 bertho 1.26 if(im)
2770     {
2771     gdImageFilledRectangle(im, x1-2, y1-2, x1+2, y1+2, conf.title_color.id);
2772     gdImageFilledRectangle(im, x2-2, y2-2, x2+2, y2+2, conf.tag_color.id);
2773     gdImageLine(im, x1, y1, x2, y2, conf.title_color.id);
2774     }
2775 bertho 1.8 xfree(href);
2776     xfree(alt);
2777 bertho 1.16 if(j > 0 || b->branchpoint)
2778 bertho 1.9 {
2779 bertho 1.16 if(j > 0)
2780     {
2781     r1 = b->revs[j-1];
2782 bertho 1.26 if(conf.left_right)
2783     {
2784     yoff = MIN(r->h, r1->h)/4;
2785     x1 = conf.upside_down ? r1->cx : r1->cx + r1->w;
2786     }
2787     else
2788     {
2789     xoff = MIN(r->w, r1->w)/4;
2790     y1 = conf.upside_down ? r1->y : r1->y + r1->h;
2791     }
2792 bertho 1.16 }
2793     else
2794     {
2795     r1 = b->branchpoint;
2796 bertho 1.26 if(conf.left_right)
2797     {
2798     yoff = MIN(r->h, b->h)/4;
2799     x1 = conf.upside_down ? b->cx : b->cx + b->w;
2800     }
2801     else
2802     {
2803     xoff = MIN(r->w, b->w)/4;
2804     y1 = conf.upside_down ? b->y : b->y + b->h;
2805     }
2806     }
2807     if(conf.left_right)
2808     {
2809     y1 = r->y - yoff;
2810     y2 = r->y + yoff;
2811     x2 = conf.upside_down ? r->cx + r->w : r->cx;
2812     yoff = 0;
2813     }
2814     else
2815     {
2816     x1 = r->cx - xoff;
2817     x2 = r->cx + xoff;
2818     y2 = conf.upside_down ? r->y + r->h : r->y;
2819     xoff = 0;
2820     }
2821     if(x1 > x2)
2822     {
2823     int tt = x1;
2824     x1 = x2;
2825     x2 = tt;
2826     }
2827     if(y1 > y2)
2828     {
2829     int tt = y1;
2830     y1 = y2;
2831     y2 = tt;
2832 bertho 1.16 }
2833     href = expand_string(conf.map_diff_href, rcs, r, r->rev, r1->rev, tag);
2834     alt = expand_string(conf.map_diff_alt, rcs, r, r->rev, r1->rev, tag);
2835 bertho 1.29 fprintf(fp, "\t<area shape=\"rect\" %s coords=\"%d,%d,%d,%d\" %s%s>\n",
2836 bertho 1.16 href,
2837 bertho 1.26 x1+xoff, y1+yoff, x2-xoff, y2-yoff,
2838 bertho 1.29 alt, htp);
2839 bertho 1.26 if(im)
2840     {
2841     gdImageFilledRectangle(im, x1-2, y1-2, x1+2, y1+2, conf.title_color.id);
2842     gdImageFilledRectangle(im, x2-2, y2-2, x2+2, y2+2, conf.tag_color.id);
2843     gdImageLine(im, x1, y1, x2, y2, conf.title_color.id);
2844     }
2845 bertho 1.16 xfree(href);
2846     xfree(alt);
2847 bertho 1.9 }
2848 bertho 1.6 }
2849 bertho 1.32 if(conf.branch_dupbox && b->nrevs)
2850 bertho 1.16 {
2851 bertho 1.26 if(conf.left_right)
2852     {
2853     x1 = conf.upside_down ? b->cx + b->w - b->tw : b->cx - b->w + b->tw;
2854     y1 = b->y - b->h/2;
2855     x2 = x1 + b->w;
2856     y2 = b->y + b->h/2;
2857     }
2858 bertho 1.16 else
2859 bertho 1.26 {
2860     x1 = b->cx - b->w/2;
2861     y1 = conf.upside_down ? b->y + b->h - b->th : b->y - b->h + b->th;
2862     x2 = b->cx + b->w/2;
2863     y2 = y1 + b->h;
2864     }
2865 bertho 1.29 fprintf(fp, "\t<area shape=\"rect\" %s coords=\"%d,%d,%d,%d\" %s%s>\n",
2866     bhref, x1, y1, x2, y2, balt, htp);
2867 bertho 1.26 if(im)
2868     {
2869     gdImageFilledRectangle(im, x1-2, y1-2, x1+2, y1+2, conf.title_color.id);
2870     gdImageFilledRectangle(im, x2-2, y2-2, x2+2, y2+2, conf.tag_color.id);
2871     gdImageLine(im, x1, y1, x2, y2, conf.title_color.id);
2872     }
2873 bertho 1.16 }
2874     xfree(bhref);
2875     xfree(balt);
2876 bertho 1.6 }
2877     fprintf(fp, "</map>\n");
2878     }
2879    
2880     /*
2881     **************************************************************************
2882 bertho 1.1 * Program entry
2883     **************************************************************************
2884     */
2885     static const char usage_str[] =
2886     "Usage: cvsgraph [options] <file>\n"
2887 bertho 1.16 " -b Add a branch box at both sides of the trunk (config value is negated)\n"
2888 bertho 1.8 " -c <file> Read alternative config from <file>\n"
2889     " -d <level> Enable debug mode at <level>\n"
2890     " -h This message\n"
2891     " -i Generate an imagemap instead of image\n"
2892 bertho 1.18 " -I <file> Also write the imagemap to <file>\n"
2893 bertho 1.19 " -k Auto stretch the tree (config value is negated)\n"
2894 bertho 1.8 " -M <name> Use <name> as imagemap name\n"
2895     " -m <mod> Use <mod> as cvs module\n"
2896     " -o <file> Output to <file>\n"
2897 bertho 1.18 " -O <opt=val> Set option opt to value val\n"
2898 bertho 1.8 " -q Be quiet (i.e. no warnings)\n"
2899     " -r <path> Use <path> as cvsroot path\n"
2900 bertho 1.16 " -s Strip untagged revisions (config value is negated)\n"
2901 bertho 1.18 " -S Also strip the first revision (config value is negated)\n"
2902 bertho 1.16 " -u Upside down image (mirror vertically; config value is negated)\n"
2903 bertho 1.8 " -V Print version and exit\n"
2904 bertho 1.29 " -x [34x] Specify level of HTML 3.2 (default), 4.0 or XHTML\n"
2905 bertho 1.8 " -[0-9] <txt> Use <txt> for expansion\n"
2906 bertho 1.1 ;
2907    
2908 bertho 1.29 #define VERSION_STR "1.3.1"
2909     #define NOTICE_STR "Copyright (c) 2001,2002,2003 B.Stultiens"
2910 bertho 1.1
2911 bertho 1.19 static void append_slash(char **path)
2912 bertho 1.9 {
2913     int l;
2914     assert(path != NULL);
2915     assert(*path != NULL);
2916     l = strlen(*path);
2917     if(!l || (*path)[l-1] == '/')
2918     return;
2919     *path = xrealloc(*path, l+2);
2920     strcat(*path, "/");
2921     }
2922    
2923 bertho 1.1 int main(int argc, char *argv[])
2924     {
2925 bertho 1.7 extern int rcs_flex_debug;
2926     extern int rcsdebug;
2927 bertho 1.1 int optc;
2928     char *confpath = NULL;
2929     char *outfile = NULL;
2930     char *cvsroot = NULL;
2931     char *cvsmodule = NULL;
2932 bertho 1.7 int imagemap = 0;
2933 bertho 1.16 int upsidedown = 0;
2934 bertho 1.17 int bdupbox = 0;
2935 bertho 1.16 int stripuntag = 0;
2936 bertho 1.18 int stripfirst = 0;
2937 bertho 1.19 int autostretch = 0;
2938 bertho 1.29 int htmllevel = 0;
2939 bertho 1.7 char *imgmapname = NULL;
2940 bertho 1.18 char *imgmapfile = NULL;
2941 bertho 1.1 int lose = 0;
2942     FILE *fp;
2943 bertho 1.18 char *rcsfilename;
2944 bertho 1.7 rcsfile_t *rcs;
2945 bertho 1.1 gdImagePtr im;
2946    
2947 bertho 1.29 while((optc = getopt(argc, argv, "0:1:2:3:4:5:6:7:8:9:bc:d:hI:ikM:m:O:o:qr:SsuVx:")) != EOF)
2948 bertho 1.1 {
2949     switch(optc)
2950     {
2951 bertho 1.16 case 'b':
2952 bertho 1.17 bdupbox = 1;
2953 bertho 1.16 break;
2954 bertho 1.1 case 'c':
2955     confpath = xstrdup(optarg);
2956     break;
2957 bertho 1.7 case 'd':
2958     debuglevel = strtol(optarg, NULL, 0);
2959     break;
2960 bertho 1.18 case 'I':
2961     imgmapfile = xstrdup(optarg);
2962     break;
2963 bertho 1.6 case 'i':
2964 bertho 1.7 imagemap = 1;
2965     break;
2966 bertho 1.18 case 'k':
2967 bertho 1.19 autostretch = 1;
2968 bertho 1.18 break;
2969 bertho 1.7 case 'M':
2970     imgmapname = xstrdup(optarg);
2971 bertho 1.6 break;
2972 bertho 1.1 case 'm':
2973     cvsmodule = xstrdup(optarg);
2974     break;
2975 bertho 1.18 case 'O':
2976     stack_option(optarg);
2977     break;
2978 bertho 1.1 case 'o':
2979     outfile = xstrdup(optarg);
2980     break;
2981 bertho 1.7 case 'q':
2982     quiet = 1;
2983     break;
2984 bertho 1.1 case 'r':
2985     cvsroot = xstrdup(optarg);
2986     break;
2987 bertho 1.18 case 'S':
2988     stripfirst = 1;
2989     break;
2990 bertho 1.16 case 's':
2991     stripuntag = 1;
2992     break;
2993     case 'u':
2994     upsidedown = 1;
2995     break;
2996 bertho 1.1 case 'V':
2997     fprintf(stdout, "cvsgraph v%s, %s\n", VERSION_STR, NOTICE_STR);
2998     return 0;
2999 bertho 1.29 case 'x':
3000     switch(optarg[0])
3001     {
3002     case '3':
3003     htmllevel = HTMLLEVEL_3;
3004     break;
3005     case '4':
3006     htmllevel = HTMLLEVEL_4;
3007     break;
3008     case 'x':
3009     htmllevel = HTMLLEVEL_X;
3010     break;
3011     default:
3012     fprintf(stderr, "Invalid HTML level in -x\n");
3013     lose++;
3014     }
3015     break;
3016 bertho 1.1 case 'h':
3017     fprintf(stdout, "%s", usage_str);
3018     return 0;
3019     default:
3020 bertho 1.8 if(isdigit(optc))
3021     {
3022     conf.expand[optc-'0'] = xstrdup(optarg);
3023     }
3024     else
3025     lose++;
3026 bertho 1.1 }
3027     }
3028    
3029     if(lose)
3030     {
3031     fprintf(stderr, "%s", usage_str);
3032     return 1;
3033     }
3034    
3035 bertho 1.7 if(debuglevel)
3036     {
3037     setvbuf(stdout, NULL, 0, _IONBF);
3038     setvbuf(stderr, NULL, 0, _IONBF);
3039     }
3040     rcs_flex_debug = (debuglevel & DEBUG_RCS_LEX) != 0;
3041     rcsdebug = (debuglevel & DEBUG_RCS_YACC) != 0;
3042    
3043 bertho 1.1 /* Set defaults */
3044 bertho 1.21 conf.tag_font.gdfont = gdFontTiny;
3045     conf.rev_font.gdfont = gdFontTiny;
3046     conf.branch_font.gdfont = gdFontTiny;
3047     conf.branch_tag_font.gdfont = gdFontTiny;
3048     conf.title_font.gdfont = gdFontTiny;
3049     conf.rev_text_font.gdfont = gdFontTiny;
3050    
3051 bertho 1.19 conf.anti_alias = 1;
3052 bertho 1.20 conf.thick_lines = 1;
3053 bertho 1.32 conf.branch_fold = 1;
3054 bertho 1.9
3055     conf.cvsroot = xstrdup("");
3056     conf.cvsmodule = xstrdup("");
3057     conf.date_format = xstrdup("%d-%b-%Y %H:%M:%S");
3058     conf.title = xstrdup("");
3059     conf.map_name = xstrdup("CvsGraphImageMap");
3060     conf.map_branch_href = xstrdup("href=\"unset: conf.map_branch_href\"");
3061     conf.map_branch_alt = xstrdup("alt=\"%B\"");
3062     conf.map_rev_href = xstrdup("href=\"unset: conf.map_rev_href\"");
3063     conf.map_rev_alt = xstrdup("alt=\"%R\"");
3064     conf.map_diff_href = xstrdup("href=\"unset: conf.map_diff_href\"");
3065 bertho 1.12 conf.map_diff_alt = xstrdup("alt=\"%P &lt;-&gt; %R\"");
3066 bertho 1.10 conf.rev_text = xstrdup("%d");
3067 bertho 1.30 conf.merge_from = xstrdup("");
3068     conf.merge_to = xstrdup("");
3069 bertho 1.9
3070     conf.color_bg = white_color;
3071     conf.branch_bgcolor = white_color;
3072     conf.branch_color = black_color;
3073 bertho 1.19 conf.branch_tag_color = black_color;
3074 bertho 1.9 conf.rev_color = black_color;
3075     conf.rev_bgcolor = white_color;
3076 bertho 1.30 conf.merge_color = black_color;
3077 bertho 1.9 conf.tag_color = black_color;
3078     conf.title_color = black_color;
3079     conf.rev_text_color = black_color;
3080 bertho 1.10
3081     conf.image_quality = 100;
3082 bertho 1.18 conf.rev_maxline = -1; /* Checked later to set to default */
3083    
3084     read_config(confpath);
3085 bertho 1.1
3086 bertho 1.18 if(conf.rev_maxline == -1) conf.rev_maxline = 5 * conf.rev_minline;
3087 bertho 1.1
3088     /* Set overrides */
3089 bertho 1.7 if(cvsroot) conf.cvsroot = cvsroot;
3090     if(cvsmodule) conf.cvsmodule = cvsmodule;
3091     if(imgmapname) conf.map_name = imgmapname;
3092 bertho 1.16 if(upsidedown) conf.upside_down = !conf.upside_down;
3093 bertho 1.17 if(bdupbox) conf.branch_dupbox = !conf.branch_dupbox;
3094 bertho 1.16 if(stripuntag) conf.strip_untagged = !conf.strip_untagged;
3095 bertho 1.18 if(stripfirst) conf.strip_first_rev = !conf.strip_first_rev;
3096 bertho 1.19 if(autostretch) conf.auto_stretch = !conf.auto_stretch;
3097 bertho 1.29 if(htmllevel) conf.html_level = htmllevel;
3098 bertho 1.18
3099     if(conf.rev_minline >= conf.rev_maxline)
3100     {
3101 bertho 1.19 if(conf.auto_stretch && !quiet)
3102     fprintf(stderr, "Auto stretch is only possible if rev_minline < rev_maxline\n");
3103     conf.auto_stretch = 0;
3104 bertho 1.18 }
3105 bertho 1.20
3106     if(conf.thick_lines < 1)
3107     conf.thick_lines = 1;
3108     if(conf.thick_lines > 11)
3109     conf.thick_lines = 11;
3110 bertho 1.1
3111 bertho 1.9 append_slash(&conf.cvsroot);
3112     append_slash(&conf.cvsmodule);
3113    
3114 bertho 1.18 if(optind >= argc)
3115     {
3116     #ifdef __WIN32__
3117     /* Bad hack for DOS/Windows */
3118     if(setmode(fileno(stdin), O_BINARY) == -1)
3119     {
3120     perror("Set binary mode for stdin");
3121     return 1;
3122     }
3123     #endif
3124     rcsfilename = NULL;
3125     }
3126     else
3127     rcsfilename = argv[optind];
3128    
3129     rcs = get_rcsfile(conf.cvsroot, conf.cvsmodule, rcsfilename);
3130 bertho 1.1 if(!rcs)
3131     return 1;
3132    
3133 bertho 1.7 if(debuglevel & DEBUG_RCS_FILE)
3134     dump_rcsfile(rcs);
3135 bertho 1.1
3136 bertho 1.7 if(!reorganise_branches(rcs))
3137     return 1;
3138 bertho 1.1
3139 bertho 1.31 assign_tags(rcs);
3140     find_merges(rcs);
3141 bertho 1.1
3142     if(outfile)
3143     {
3144 bertho 1.15 if((fp = fopen(outfile, "wb")) == NULL)
3145 bertho 1.1 {
3146     perror(outfile);
3147     return 1;
3148     }
3149     }
3150     else
3151 bertho 1.15 {
3152 bertho 1.1 fp = stdout;
3153 bertho 1.15 #ifdef __WIN32__
3154     /* Bad hack for DOS/Windows */
3155     if(setmode(fileno(fp), O_BINARY) == -1)
3156     {
3157     perror("Set binary mode for stdout");
3158     return 1;
3159     }
3160     #endif
3161     }
3162 bertho 1.5
3163 bertho 1.7 make_layout(rcs);
3164    
3165     if(!imagemap)
3166 bertho 1.5 {
3167 bertho 1.7 /* Create an image */
3168     im = make_image(rcs);
3169 bertho 1.26 #ifdef DEBUG_IMAGEMAP
3170     {
3171     FILE *nulfile = fopen("/dev/null", "w");
3172     make_imagemap(rcs, nulfile, im);
3173     fclose(nulfile);
3174     }
3175     #endif
3176 bertho 1.7 switch(conf.image_type)
3177     {
3178 bertho 1.5 #ifdef HAVE_IMAGE_GIF
3179 bertho 1.7 # ifndef HAVE_IMAGE_PNG
3180     default:
3181     # endif
3182     case IMAGE_GIF:
3183     gdImageGif(im, fp);
3184     break;
3185 bertho 1.5 #endif
3186     #ifdef HAVE_IMAGE_PNG
3187 bertho 1.7 default:
3188     case IMAGE_PNG:
3189     gdImagePng(im, fp);
3190     break;
3191 bertho 1.5 #endif
3192     #ifdef HAVE_IMAGE_JPEG
3193     # if !defined(HAVE_IMAGE_GIF) && !defined(HAVE_IMAGE_PNG)
3194 bertho 1.7 default:
3195 bertho 1.5 # endif
3196 bertho 1.7 case IMAGE_JPEG:
3197     gdImageJpeg(im, fp, conf.image_quality);
3198     break;
3199 bertho 1.5 #endif
3200 bertho 1.7 }
3201    
3202     gdImageDestroy(im);
3203     }
3204     else
3205     {
3206     /* Create an imagemap */
3207 bertho 1.26 make_imagemap(rcs, fp, NULL);
3208 bertho 1.18 }
3209    
3210     /* Also create imagemap to file if requested */
3211     if(imgmapfile)
3212     {
3213     FILE *ifp = fopen(imgmapfile, "wb");
3214     if(!ifp)
3215     {
3216     perror(imgmapfile);
3217     return 1;
3218     }
3219 bertho 1.26 make_imagemap(rcs, ifp, NULL);
3220 bertho 1.18 fclose(ifp);
3221 bertho 1.5 }
3222    
3223 bertho 1.1 if(outfile)
3224     fclose(fp);
3225 bertho 1.6
3226 bertho 1.1 return 0;
3227     }
3228    

  ViewVC Help
Powered by ViewVC 1.1.0 with CvsGraph 1.7.0