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

Diff of /cvsgraph/cvsgraph.c

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

revision 1.43, Thu Aug 5 09:48:32 2004 UTC revision 1.65, Wed May 21 13:33:35 2008 UTC
# Line 2  Line 2 
2   * CvsGraph graphical representation generator of brances and revisions   * CvsGraph graphical representation generator of brances and revisions
3   * of a file in cvs/rcs.   * of a file in cvs/rcs.
4   *   *
5   * Copyright (C) 2001,2002,2003  B. Stultiens   * Copyright (C) 2001,2002,2003,2004,2005,2006  B. Stultiens
6   *   *
7   * This program is free software; you can redistribute it and/or modify   * 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   * it under the terms of the GNU General Public License as published by
# Line 23  Line 23 
23    
24  #include <stdio.h>  #include <stdio.h>
25  #include <stdlib.h>  #include <stdlib.h>
26    #include <stdarg.h>
27  #include <unistd.h>  #include <unistd.h>
28  #include <string.h>  #include <string.h>
29  #include <assert.h>  #include <assert.h>
30  #include <sys/types.h>  #include <sys/types.h>
31  #include <sys/stat.h>  #include <sys/stat.h>
32  #include <sys/wait.h>  #ifdef HAVE_SYS_WAIT_H
33    # include <sys/wait.h>
34    #endif
35  #include <fcntl.h>  #include <fcntl.h>
36  #include <regex.h>  #include <regex.h>
37  #include <errno.h>  #include <errno.h>
38  #include <ctype.h>  #include <ctype.h>
39  #include <time.h>  #include <time.h>
40  #include <limits.h>  #include <limits.h>
 #include <regex.h>  
41  #include <math.h>  #include <math.h>
42    
43  #ifdef HAVE_GETOPT_H  #ifdef HAVE_GETOPT_H
# Line 50  Line 52 
52  #include "readconf.h"  #include "readconf.h"
53  #include "rcs.h"  #include "rcs.h"
54    
55  #if !defined(HAVE_IMAGE_GIF) && !defined(HAVE_IMAGE_PNG) && !defined(HAVE_IMAGE_JPEG)  #if !defined(HAVE_GD_GIF) && !defined(HAVE_GD_PNG) && !defined(HAVE_GD_JPEG)
56  # error No image output format available. Check libgd  # error No image output format available. Check libgd
57  #endif  #endif
58    
# Line 94  Line 96 
96    
97  config_t conf;  config_t conf;
98  int debuglevel;  int debuglevel;
99  color_t white_color = {255, 255, 255, 0};  
100  color_t black_color = {0, 0, 0, 0};  static color_t white_color = {255, 255, 255, 0, NULL};
101    static color_t black_color = {0, 0, 0, 0, NULL};
102    
103    static branch_t *subtree_branch = NULL;         /* Set to the (first) subtree branch that we want to show */
104    static revision_t *subtree_rev = NULL;          /* Set to the subtree revision which branches we want to show */
105    
106    static msg_stack_t *msg_stack = NULL;           /* Messages that would otherwise be sent to stderr goto the image */
107    static int nmsg_stack = 0;
108    
109  /*  /*
110   **************************************************************************   **************************************************************************
# Line 110  Line 119 
119  static void add_string_str_html(const char *s, int maxlen);  static void add_string_str_html(const char *s, int maxlen);
120  static void add_string_str_len(const char *s, int maxlen);  static void add_string_str_len(const char *s, int maxlen);
121    
122    static void calc_subtree_size(branch_t *b, int *x, int *y, int *w, int *h);
123    
124  /*  /*
125   **************************************************************************   **************************************************************************
126   * Debug routines   * Debug routines
# Line 216  Line 227 
227    
228  /*  /*
229   **************************************************************************   **************************************************************************
230     * Error/Warning Message helpers
231     **************************************************************************
232     */
233    #define MSGBUFSIZE      256
234    void stack_msg(int severity, const char *fmt, ...)
235    {
236            va_list va;
237            int i;
238            char *buf = xmalloc(MSGBUFSIZE);
239            switch(severity)
240            {
241            case MSG_WARN:  sprintf(buf, "Warning: "); break;
242            case MSG_ERR:   sprintf(buf, "Error: "); break;
243            default:        sprintf(buf, "Unqualified error: "); break;
244            }
245            i = strlen(buf);
246            assert(i < MSGBUFSIZE);
247            va_start(va, fmt);
248            vsnprintf(buf+i, MSGBUFSIZE-i, fmt, va);
249            va_end(va);
250            if(!msg_stack)
251                    msg_stack = xmalloc(sizeof(*msg_stack));
252            else
253            {
254                    msg_stack = xrealloc(msg_stack, (nmsg_stack+1)*sizeof(*msg_stack));
255            }
256            msg_stack[nmsg_stack].msg = buf;
257            msg_stack[nmsg_stack].severity = severity;
258            nmsg_stack++;
259    }
260    
261    /*
262     **************************************************************************
263   * Read the rcs file   * Read the rcs file
264   **************************************************************************   **************************************************************************
265   */   */
266  rcsfile_t *get_rcsfile(const char *cvsroot, const char *module, const char *file)  static rcsfile_t *get_rcsfile(const char *cvsroot, const char *module, const char *file)
267  {  {
268          char *cmd = NULL;          char *cmd = NULL;
269          int rv;          int rv;
# Line 228  Line 272 
272          {          {
273                  cmd = xmalloc(strlen(cvsroot) + strlen(module) + strlen(file) + 1);                  cmd = xmalloc(strlen(cvsroot) + strlen(module) + strlen(file) + 1);
274                  sprintf(cmd, "%s%s%s", cvsroot, module, file);                  sprintf(cmd, "%s%s%s", cvsroot, module, file);
275                  if(!(rcsin = fopen(cmd, "rb")))                  if(!(yyin = fopen(cmd, "rb")))
276                  {                  {
277                          perror(cmd);                          perror(cmd);
278                          return NULL;                          return NULL;
# Line 237  Line 281 
281          }          }
282          else          else
283          {          {
284                  rcsin = stdin;                  yyin = stdin;
285                  input_file = "<stdin>";                  input_file = "<stdin>";
286          }          }
287          line_number = 1;          line_number = 1;
288          rv = rcsparse();          rv = yyparse();
289          if(file)          if(file)
290          {          {
291                  fclose(rcsin);                  fclose(yyin);
292                  xfree(cmd);                  xfree(cmd);
293          }          }
294          if(rv)          if(rv)
# Line 270  Line 314 
314   * Sort and find helpers   * Sort and find helpers
315   **************************************************************************   **************************************************************************
316   */   */
317  int count_dots(const char *s)  static int count_dots(const char *s)
318  {  {
319          int i;          int i;
320          for(i = 0; *s; s++)          for(i = 0; *s; s++)
# Line 281  Line 325 
325          return i;          return i;
326  }  }
327    
328  int compare_rev(int bcmp, const rev_t *r1, const rev_t *r2)  static int compare_rev(int bcmp, const rev_t *r1, const rev_t *r2)
329  {  {
330          int d1, d2;          int d1, d2;
331          char *c1, *c2;          char *c1, *c2;
# Line 447  Line 491 
491          return r;          return r;
492  }  }
493    
494  void build_branch(branch_t ***bl, int *nbl, delta_t **sdl, int nsdl, dtext_t **sdt, int nsdt, delta_t *head)  static int sort_branch_height(const void *b1, const void *b2)
495    {
496            return (*(branch_t **)b1)->nrevs - (*(branch_t **)b2)->nrevs;
497    }
498    
499    static void build_branch(branch_t ***bl, int *nbl, delta_t **sdl, int nsdl, dtext_t **sdt, int nsdt, delta_t *head)
500  {  {
501          branch_t *b;          branch_t *b;
502          dtext_t *text;          dtext_t *text;
# Line 457  Line 506 
506    
507          if(head->flag)          if(head->flag)
508          {          {
509                  fprintf(stderr, "Circular reference on '%s' in branchpoint\n", head->rev->rev);                  stack_msg(MSG_ERR, "Circular reference on '%s' in branchpoint\n", head->rev->rev);
510                  return;                  return;
511          }          }
512          head->flag++;          head->flag++;
# Line 491  Line 540 
540                                  currev->branches[currev->nbranches] = (*bl)[btag];                                  currev->branches[currev->nbranches] = (*bl)[btag];
541                                  currev->nbranches++;                                  currev->nbranches++;
542                          }                          }
543                            if(conf.branch_resort)
544                                    qsort(currev->branches, currev->nbranches, sizeof(currev->branches[0]), sort_branch_height);
545                  }                  }
546    
547                  /* Walk through the next list */                  /* Walk through the next list */
# Line 500  Line 551 
551                  head = find_delta(sdl, nsdl, head->next);                  head = find_delta(sdl, nsdl, head->next);
552                  if(!head)                  if(!head)
553                  {                  {
554                          fprintf(stderr, "Next revision (%s) not found in deltalist\n", head->next->rev);                          stack_msg(MSG_ERR, "Next revision (%s) not found in deltalist\n", head->next->rev);
555                          return;                          return;
556                  }                  }
557                  if(head->flag)                  if(head->flag)
558                  {                  {
559                          fprintf(stderr, "Circular reference on '%s'\n", head->rev->rev);                          stack_msg(MSG_ERR, "Circular reference on '%s'\n", head->rev->rev);
560                          return;                          return;
561                  }                  }
562                  head->flag++;                  head->flag++;
# Line 552  Line 603 
603          head = find_delta(sdelta, nsdelta, rcs->head);          head = find_delta(sdelta, nsdelta, rcs->head);
604          if(!head)          if(!head)
605          {          {
606                  fprintf(stderr, "Head revision (%s) not found in deltalist\n", rcs->head->rev);                  stack_msg(MSG_ERR, "Head revision (%s) not found in deltalist\n", rcs->head->rev);
607                  return 0;                  return 0;
608          }          }
609          bl = NULL;          bl = NULL;
# Line 619  Line 670 
670          char *r;          char *r;
671          if(!dots)          if(!dots)
672          {          {
673                  fprintf(stderr, "FIXME: previous_rev(\"%s\"): Cannot determine parent branch revision\n", c);                  stack_msg(MSG_ERR, "FIXME: previous_rev(\"%s\"): Cannot determine parent branch revision\n", c);
674                  return xstrdup("1.0");  /* FIXME: don't know what the parent is */                  return xstrdup("1.0");  /* FIXME: don't know what the parent is */
675          }          }
676          if(dots & 1)          if(dots & 1)
# Line 630  Line 681 
681                  assert(cptr != NULL);                  assert(cptr != NULL);
682                  if(dots == 1)                  if(dots == 1)
683                  {                  {
684                          fprintf(stderr, "FIXME: previous_rev(\"%s\"): Going beyond top-level?\n", c);                          stack_msg(MSG_ERR, "FIXME: previous_rev(\"%s\"): Going beyond top-level?\n", c);
685                          /* FIXME: What is the parent of 1.1? */                          /* FIXME: What is the parent of 1.1? */
686                          cptr[1] = '\0';                          cptr[1] = '\0';
687                          strcat(r, "0");                          strcat(r, "0");
# Line 651  Line 702 
702          return r;          return r;
703  }  }
704    
705  static char *build_regex(size_t n, regmatch_t *m, const char *ms)  static char *build_regex(size_t n, regmatch_t *m, const char *ms, int idx)
706  {  {
707          char *cptr;          char *cptr;
708          int i;          int i;
709    
710          if(!conf.merge_to || !conf.merge_to[0])          if(!conf.merge_to.strs[idx])
711                  return NULL;                  return NULL;
712    
713          zap_string();          zap_string();
714          for(cptr = conf.merge_to; *cptr; cptr++)          for(cptr = conf.merge_to.strs[idx]; *cptr; cptr++)
715          {          {
716                  if(*cptr == '%')                  if(*cptr == '%')
717                  {                  {
# Line 688  Line 739 
739          return dup_string();          return dup_string();
740  }  }
741    
742  static void find_merges(rcsfile_t *rcs)  static void find_merges_cvsnt(rcsfile_t *rcs)
743  {  {
744          int i;          int i;
745    
746            if(!conf.merge_cvsnt)
747                    return;
748    
749            for(i = 0; i < rcs->nsrev; i++)
750            {
751                    revision_t **r;
752    
753                    if(!rcs->srev[i]->delta->mergepoint)
754                            continue;
755    
756                    r = bsearch(rcs->srev[i]->delta->mergepoint->rev, rcs->srev, rcs->nsrev, sizeof(rcs->srev[0]), search_revision);
757                    if(!r)
758                            continue;
759                    rcs->merges = xrealloc(rcs->merges, sizeof(rcs->merges[0]) * (rcs->nmerges+1));
760                    rcs->merges[rcs->nmerges].type = TR_REVISION;
761                    rcs->merges[rcs->nmerges].from.rev = *r;
762                    rcs->merges[rcs->nmerges].to.rev = rcs->srev[i];
763                    rcs->merges[rcs->nmerges].clr = -1;
764                    rcs->nmerges++;
765                    (*r)->mergetarget = 1;
766                    rcs->srev[i]->mergetarget = 1;
767            }
768    }
769    
770    static void find_merges(rcsfile_t *rcs)
771    {
772            int i, j;
773          int err;          int err;
774          int rcflags = REG_EXTENDED | (conf.merge_nocase ? REG_ICASE : 0);          int rcflags = REG_EXTENDED | (conf.merge_nocase ? REG_ICASE : 0);
775          regex_t *refrom = NULL;          regex_t *refrom = NULL;
776          regex_t *reto = NULL;          regex_t *reto = NULL;
777          regmatch_t *matchfrom = NULL;          regmatch_t *matchfrom = NULL;
778    
779          if(!conf.merge_from || !conf.merge_from[0] || !conf.merge_to || !conf.merge_to[0])          if(!conf.merge_from.n || !conf.merge_to.n)
780                  return;                  return;
781    
782          refrom = xmalloc(sizeof(*refrom));          for(j = 0; j < conf.merge_from.n; j++)
         reto = xmalloc(sizeof(*reto));  
   
         /* Compile the 'from' regex match for merge identification */  
         err = regcomp(refrom, conf.merge_from, rcflags);  
         if(err)  
783          {          {
784                  if(!quiet)                  if(!conf.merge_from.strs[0] || !conf.merge_to.strs[0])
785                            continue;
786    
787                    refrom = xmalloc(sizeof(*refrom));
788                    reto = xmalloc(sizeof(*reto));
789    
790                    /* Compile the 'from' regex match for merge identification */
791                    err = regcomp(refrom, conf.merge_from.strs[j], rcflags);
792                    if(err)
793                  {                  {
794                          char *msg;                          char *msg;
795                          i = regerror(err, refrom, NULL, 0);                          i = regerror(err, refrom, NULL, 0);
796                          msg = xmalloc(i+1);                          msg = xmalloc(i+1);
797                          regerror(err, refrom, msg, i+1);                          regerror(err, refrom, msg, i+1);
798                          fprintf(stderr, "%s\n", msg);                          stack_msg(MSG_WARN, "%s", msg);
799                          xfree(msg);                          xfree(msg);
800                            xfree(refrom);
801                            xfree(reto);
802                            return;
803                  }                  }
804                  xfree(refrom);                  else
805                  xfree(reto);                          matchfrom = xmalloc((refrom->re_nsub+1) * sizeof(*matchfrom));
                 return;  
         }  
         else  
                 matchfrom = xmalloc((refrom->re_nsub+1) * sizeof(*matchfrom));  
   
         for(i = 0; i < rcs->tags->ntags; i++)  
         {  
                 tag_t *t = rcs->tags->tags[i];  
   
                 /* Must be revision tags and not detached */  
                 if(t->rev->isbranch || !t->logrev)  
                         continue;  
806    
807                  /* Try to find merge tag matches */                  for(i = 0; i < rcs->tags->ntags; i++)
                 if(!regexec(refrom, t->tag, refrom->re_nsub+1, matchfrom, 0))  
808                  {                  {
809                          int n;                          tag_t *t = rcs->tags->tags[i];
810                          char *to;  
811                            /* Must be revision tags and not detached */
812                            if(t->rev->isbranch || !t->logrev)
813                                    continue;
814    
815                          to = build_regex(refrom->re_nsub+1, matchfrom, t->tag);                          /* Try to find merge tag matches */
816                          if(to)                          if(!regexec(refrom, t->tag, refrom->re_nsub+1, matchfrom, 0))
817                          {                          {
818                                  err = regcomp(reto, to, rcflags);                                  int n;
819                                  if(err && !quiet)                                  char *to;
820                                  {  
821                                          char *msg;                                  to = build_regex(refrom->re_nsub+1, matchfrom, t->tag, j);
822                                          i = regerror(err, reto, NULL, 0);                                  if(to)
                                         msg = xmalloc(i+1);  
                                         regerror(err, reto, msg, i+1);  
                                         fprintf(stderr, "%s\n", msg);  
                                 }  
                                 else if(!err)  
823                                  {                                  {
824                                          for(n = 0; n < rcs->tags->ntags; n++)                                          err = regcomp(reto, to, rcflags);
825                                            if(err)
826                                          {                                          {
827                                                  tag_t *nt = rcs->tags->tags[n];                                                  char *msg;
828                                                  /* From and To never should match the same tag or belong to a branch */                                                  i = regerror(err, reto, NULL, 0);
829                                                  if(n == i || nt->rev->isbranch || !nt->logrev)                                                  msg = xmalloc(i+1);
830                                                          continue;                                                  regerror(err, reto, msg, i+1);
831                                                    stack_msg(MSG_WARN, "%s", msg);
832                                                  if(!regexec(reto, nt->tag, 0, NULL, 0))                                                  xfree(msg);
833                                            }
834                                            else if(!err)
835                                            {
836                                                    for(n = 0; n < rcs->tags->ntags; n++)
837                                                  {                                                  {
838                                                          /* Tag matches */                                                          tag_t *nt = rcs->tags->tags[n];
839                                                          rcs->merges = xrealloc(rcs->merges,                                                          /* From and To never should match the same tag or belong to a branch */
840                                                                          sizeof(rcs->merges[0]) * (rcs->nmerges+1));                                                          if(n == i || nt->rev->isbranch || !nt->logrev)
841                                                          rcs->merges[rcs->nmerges].to = nt;                                                                  continue;
842                                                          rcs->merges[rcs->nmerges].from = t;  
843                                                          rcs->nmerges++;                                                          if(!regexec(reto, nt->tag, 0, NULL, 0))
844                                                          /* Merges cannot be ignored tags */                                                          {
845                                                          nt->ignore = 0;                                                                  /* Tag matches */
846                                                          t->ignore = 0;                                                                  rcs->merges = xrealloc(rcs->merges,
847                                                          /* We cannot (should not) match multiple times */                                                                                  sizeof(rcs->merges[0]) * (rcs->nmerges+1));
848                                                          break;                                                                  rcs->merges[rcs->nmerges].type = TR_TAG;
849                                                                    rcs->merges[rcs->nmerges].to.tag = nt;
850                                                                    rcs->merges[rcs->nmerges].from.tag = t;
851                                                                    rcs->merges[rcs->nmerges].clr = j;
852                                                                    rcs->nmerges++;
853                                                                    if(!conf.tag_ignore_merge)
854                                                                    {
855                                                                            nt->ignore = 0;
856                                                                            t->ignore = 0;
857                                                                    }
858                                                                    /* We cannot (should not) match multiple times */
859                                                                    if(!conf.merge_findall)
860                                                                            break;
861                                                            }
862                                                  }                                                  }
863                                                    regfree(reto);
864                                          }                                          }
865                                          regfree(reto);                                          xfree(to);
866                                  }                                  }
                                 xfree(to);  
867                          }                          }
868                  }                  }
869                    if(matchfrom)   xfree(matchfrom);
870                    if(refrom)      { regfree(refrom); xfree(refrom); }
871                    if(reto)        xfree(reto);
872                    refrom = NULL;
873                    reto = NULL;
874                    matchfrom = NULL;
875          }          }
         if(matchfrom)   xfree(matchfrom);  
         if(refrom)      { regfree(refrom); xfree(refrom); }  
         if(reto)        xfree(reto);  
876  }  }
877    
878  static void assign_tags(rcsfile_t *rcs)  static void assign_tags(rcsfile_t *rcs)
# Line 797  Line 888 
888                  err = regcomp(regextag, conf.tag_ignore, REG_EXTENDED | REG_NOSUB | (conf.tag_nocase ? REG_ICASE : 0));                  err = regcomp(regextag, conf.tag_ignore, REG_EXTENDED | REG_NOSUB | (conf.tag_nocase ? REG_ICASE : 0));
889                  if(err)                  if(err)
890                  {                  {
891                          if(!quiet)                          char *msg;
892                          {                          i = regerror(err, regextag, NULL, 0);
893                                  char *msg;                          msg = xmalloc(i+1);
894                                  i = regerror(err, regextag, NULL, 0);                          regerror(err, regextag, msg, i+1);
895                                  msg = xmalloc(i+1);                          stack_msg(MSG_WARN, "%s", msg);
896                                  regerror(err, regextag, msg, i+1);                          xfree(msg);
                                 fprintf(stderr, "%s\n", msg);  
                                 xfree(msg);  
                         }  
897                          xfree(regextag);                          xfree(regextag);
898                          regextag = NULL;                          regextag = NULL;
899                  }                  }
# Line 839  Line 927 
927          }          }
928    
929          /* We should have at least two tags (HEAD and MAIN) */          /* We should have at least two tags (HEAD and MAIN) */
930          assert(rcs->tags != 0);          assert(rcs->tags != NULL);
931    
932          for(i = 0; i < rcs->tags->ntags; i++)          for(i = 0; i < rcs->tags->ntags; i++)
933          {          {
# Line 864  Line 952 
952                                  xfree(rev.rev);                                  xfree(rev.rev);
953                                  if(!r)                                  if(!r)
954                                  {                                  {
955                                          if(!quiet)                                          stack_msg(MSG_WARN, "No branch found for tag '%s:%s'", t->tag, t->rev->branch);
                                                 fprintf(stderr, "No branch found for tag '%s:%s'\n", t->tag, t->rev->branch);  
956                                  }                                  }
957                                  else                                  else
958                                  {                                  {
# Line 895  Line 982 
982                          revision_t **r = bsearch(t->rev, rcs->srev, rcs->nsrev, sizeof(rcs->srev[0]), search_revision);                          revision_t **r = bsearch(t->rev, rcs->srev, rcs->nsrev, sizeof(rcs->srev[0]), search_revision);
983                          if(!r)                          if(!r)
984                          {                          {
985                                  if(!quiet)                                  stack_msg(MSG_WARN, "No revision found for tag '%s:%s'\n", t->tag, t->rev->rev);
                                         fprintf(stderr, "No revision found for tag '%s:%s'\n", t->tag, t->rev->rev);  
986                          }                          }
987                          else                          else
988                          {                          {
# Line 1001  Line 1087 
1087          time_t t;          time_t t;
1088          char *buf;          char *buf;
1089          int nbuf;          int nbuf;
1090            char *env;
1091    
1092          memset(&tm, 0, sizeof(tm));          memset(&tm, 0, sizeof(tm));
1093          n = sscanf(d, "%d.%d.%d.%d.%d.%d",          n = sscanf(d, "%d.%d.%d.%d.%d.%d",
# Line 1009  Line 1096 
1096          tm.tm_mon--;          tm.tm_mon--;
1097          if(tm.tm_year > 1900)          if(tm.tm_year > 1900)
1098                  tm.tm_year -= 1900;                  tm.tm_year -= 1900;
1099          t = mktime(&tm) - timezone;  
1100            env = getenv("TZ");
1101            putenv("TZ=UTC0");
1102            t = mktime(&tm);
1103            if(env)
1104            {
1105                    char *c = xmalloc(strlen(env) + 3 + 1); /* Extra space for TZ and = */
1106                    sprintf(c, "TZ=%s", env);
1107                    putenv(c);
1108                    xfree(c);
1109            }
1110            else
1111                    putenv("TZ");
1112    
1113          if(n != 6 || t == (time_t)(-1))          if(n != 6 || t == (time_t)(-1))
1114          {          {
1115                  add_string_str("<invalid date>");                  add_string_str("<invalid date>");
# Line 1017  Line 1117 
1117          }          }
1118    
1119          tmp = localtime(&t);          tmp = localtime(&t);
1120          nbuf = strlen(conf.date_format) * 16;   /* Should be enough to hold all types of expansions */          nbuf = (strlen(conf.date_format)+1) * 16;       /* Should be enough to hold all types of expansions */
1121          buf = xmalloc(nbuf);          buf = xmalloc(nbuf);
1122          strftime(buf, nbuf, conf.date_format, tmp);          strftime(buf, nbuf, conf.date_format, tmp);
1123          add_string_str(buf);          add_string_str(buf);
# Line 1076  Line 1176 
1176          xfree(str);          xfree(str);
1177  }  }
1178    
1179  char *expand_string(const char *s, rcsfile_t *rcs, revision_t *r, rev_t *rev, rev_t *prev, tag_t *tag)  static char *expand_string(const char *s, rcsfile_t *rcs, revision_t *r, rev_t *rev, rev_t *prev, tag_t *tag)
1180  {  {
1181          char nb[32];          char nb[32];
1182          char nr[32];          char nr[32];
# Line 1090  Line 1190 
1190    
1191          zap_string();          zap_string();
1192    
1193          sprintf(nb, "%d", rcs->nbranches);          sprintf(nb, "%d", rcs->nbranches + rcs->nfolds);
1194          sprintf(nr, "%d", rcs->nsrev);          sprintf(nr, "%d", rcs->nsrev);
1195          for(; *s; s++)          for(; *s; s++)
1196          {          {
# Line 1218  Line 1318 
1318                                  if(!*s)                                  if(!*s)
1319                                  {                                  {
1320                                          s--;    /* To end outer loop */                                          s--;    /* To end outer loop */
1321                                          if(!quiet)                                          stack_msg(MSG_WARN, "string expand: Missing %%) in expansion");
                                                 fprintf(stderr, "string expand: Missing %%) in expansion\n");  
1322                                  }                                  }
1323                                  break;                                  break;
1324                          case ')':                          case ')':
# Line 1241  Line 1340 
1340   * Drawing routines   * Drawing routines
1341   **************************************************************************   **************************************************************************
1342   */   */
1343    static color_t *clr_id = NULL;
1344    static int nclr_id = 0;
1345    
1346    static int rexpr_eval(const char *key, const char *content, int flags)
1347    {
1348            int res;
1349            regex_t re;
1350            if(regcomp(&re, content, flags | REG_EXTENDED | REG_NOSUB))
1351                    return 0;
1352            res = regexec(&re, key, 0, NULL, 0);
1353            regfree(&re);
1354            return res == 0;
1355    }
1356    
1357    static int expr_eval(const char *key, int op, const char *content)
1358    {
1359            switch(op)
1360            {
1361            case OP_CONTAINED:      return rexpr_eval(key, content, 0);
1362            case OP_CONTAINEDI:     return rexpr_eval(key, content, REG_ICASE);
1363            case OP_NCONTAINED:     return !rexpr_eval(key, content, 0);
1364            case OP_NCONTAINEDI:    return !rexpr_eval(key, content, REG_ICASE);
1365            case OP_EQ:     return strcmp(key, content) == 0;
1366            case OP_NE:     return strcmp(key, content) != 0;
1367            case OP_GE:     return strcmp(key, content) >= 0;
1368            case OP_GT:     return strcmp(key, content) > 0;
1369            case OP_LE:     return strcmp(key, content) <= 0;
1370            case OP_LT:     return strcmp(key, content) < 0;
1371            }
1372            return 0;
1373    }
1374    
1375    static char *eval_string(node_t *node, revision_t *r)
1376    {
1377            int i;
1378            assert(node != NULL);
1379            switch(node->key)
1380            {
1381            default:
1382            case TYPE_COLOR:
1383                    return "";      /* This should not happen */
1384            case TYPE_STRING:
1385                    return node->value.str;
1386            case KEY_STATE:
1387                    if(r && expr_eval(r->delta->state, node->op, node->content))
1388                            return eval_string(node->tcase, r);
1389                    else
1390                            return eval_string(node->fcase, r);
1391            case KEY_AUTHOR:
1392                    if(r && expr_eval(r->delta->author, node->op, node->content))
1393                            return eval_string(node->tcase, r);
1394                    else
1395                            return eval_string(node->fcase, r);
1396            case KEY_TAG:
1397                    for(i = 0; r && i < r->ntags; i++)
1398                    {
1399                            if(expr_eval(r->tags[i]->tag, node->op, node->content))
1400                                    return eval_string(node->tcase, r);
1401                    }
1402                    return eval_string(node->fcase, r);
1403            case KEY_DATE:
1404                    if(r && expr_eval(r->delta->date, node->op, node->content))
1405                            return eval_string(node->tcase, r);
1406                    else
1407                            return eval_string(node->fcase, r);
1408            case KEY_REV:
1409                    if(r && expr_eval(r->rev->rev, node->op, node->content))
1410                            return eval_string(node->tcase, r);
1411                    else
1412                            return eval_string(node->fcase, r);
1413            }
1414            return "";
1415    }
1416    static color_t *eval_color(node_t *node, revision_t *r, branch_t *b)
1417    {
1418            int i;
1419            assert(node != NULL);
1420            switch(node->key)
1421            {
1422            default:
1423            case TYPE_STRING:
1424                    return &black_color;    /* This should not happen */
1425            case TYPE_COLOR:
1426                    return &node->value.clr;
1427            case KEY_STATE:
1428                    if(r && expr_eval(r->delta->state, node->op, node->content))
1429                            return eval_color(node->tcase, r, b);
1430                    else
1431                            return eval_color(node->fcase, r, b);
1432            case KEY_AUTHOR:
1433                    if(r && expr_eval(r->delta->author, node->op, node->content))
1434                            return eval_color(node->tcase, r, b);
1435                    else
1436                            return eval_color(node->fcase, r, b);
1437            case KEY_TAG:
1438                    for(i = 0; r && i < r->ntags; i++)
1439                    {
1440                            if(expr_eval(r->tags[i]->tag, node->op, node->content))
1441                                    return eval_color(node->tcase, r, b);
1442                    }
1443                    return eval_color(node->fcase, r, b);
1444            case KEY_DATE:
1445                    if(r && expr_eval(r->delta->date, node->op, node->content))
1446                            return eval_color(node->tcase, r, b);
1447                    else
1448                            return eval_color(node->fcase, r, b);
1449            case KEY_REV:
1450                    if(r && expr_eval(r->rev->rev, node->op, node->content))
1451                            return eval_color(node->tcase, r, b);
1452                    if(b && expr_eval(b->branch->branch, node->op, node->content))
1453                            return eval_color(node->tcase, r, b);
1454                    return eval_color(node->fcase, r, b);
1455            }
1456            return &black_color;
1457    }
1458    
1459    static color_t *clr(gdImagePtr im, const char *s, revision_t *r, branch_t *b, int idx)
1460    {
1461            int i;
1462            color_t *c = get_colorref(s, idx);
1463            if(!c)
1464                    c = &black_color;
1465            if(c->node)
1466                    c = eval_color(c->node, r, b);
1467            for(i = 0; i < nclr_id; i++)
1468            {
1469                    if(c->r == clr_id[i].r && c->g == clr_id[i].g && c->b == clr_id[i].b)
1470                            return &clr_id[i];
1471            }
1472            clr_id = xrealloc(clr_id, (nclr_id+1) * sizeof(*clr_id));
1473            clr_id[nclr_id] = *c;
1474            clr_id[nclr_id].id = gdImageColorAllocate(im, c->r, c->g, c->b);
1475            return &clr_id[nclr_id++];
1476    }
1477    
1478    static void zap_clr(void)
1479    {
1480            if(clr_id)
1481                    xfree(clr_id);
1482            clr_id = NULL;
1483            nclr_id = 0;
1484    }
1485    
1486  static int get_swidth(const char *s, font_t *f)  static int get_swidth(const char *s, font_t *f)
1487  {  {
1488          int n;          int n;
# Line 1248  Line 1490 
1490          if(!s || !*s)          if(!s || !*s)
1491                  return 0;                  return 0;
1492    
1493  #if defined(HAVE_GDIMAGESTRINGFT) || defined(HAVE_GDIMAGESTRINGTTF)  #if defined(HAVE_GD_STRINGFT) || defined(HAVE_GD_STRINGTTF)
1494          if(conf.use_ttf && f->ttfont)          if(conf.use_ttf && f->ttfont)
1495          {          {
1496                  int bb[8];                  int bb[8];
1497                  char *e;                  char *e;
1498  #ifdef HAVE_GDIMAGESTRINGFT  #ifdef HAVE_GD_STRINGFT
1499                  e = gdImageStringFT(NULL, bb, 0, f->ttfont, f->ttsize, 0.0, 0, 0, (char *)s);                  e = gdImageStringFT(NULL, bb, 0, f->ttfont, f->ttsize, 0.0, 0, 0, (char *)s);
1500  #else  #else
1501                  e = gdImageStringTTF(NULL, bb, 0, f->ttfont, f->ttsize, 0.0, 0, 0, (char *)s);                  e = gdImageStringTTF(NULL, bb, 0, f->ttfont, f->ttsize, 0.0, 0, 0, (char *)s);
# Line 1282  Line 1524 
1524          if(!s || !*s)          if(!s || !*s)
1525                  return 0;                  return 0;
1526    
1527  #if defined(HAVE_GDIMAGESTRINGFT) || defined(HAVE_GDIMAGESTRINGTTF)  #if defined(HAVE_GD_STRINGFT) || defined(HAVE_GD_STRINGTTF)
1528          if(conf.use_ttf && f->ttfont)          if(conf.use_ttf && f->ttfont)
1529          {          {
1530                  int bb[8];                  int bb[8];
1531                  char *e;                  char *e;
1532  #ifdef HAVE_GDIMAGESTRINGFT  #ifdef HAVE_GD_STRINGFT
1533                  e = gdImageStringFT(NULL, bb, 0, f->ttfont, f->ttsize, 0.0, 0, 0, (char *)s);                  e = gdImageStringFT(NULL, bb, 0, f->ttfont, f->ttsize, 0.0, 0, 0, (char *)s);
1534  #else  #else
1535                  e = gdImageStringTTF(NULL, bb, 0, f->ttfont, f->ttsize, 0.0, 0, 0, (char *)s);                  e = gdImageStringTTF(NULL, bb, 0, f->ttfont, f->ttsize, 0.0, 0, 0, (char *)s);
# Line 1307  Line 1549 
1549  static void draw_rbox(gdImagePtr im, int x1, int y1, int x2, int y2, int r, color_t *color, color_t *bgcolor)  static void draw_rbox(gdImagePtr im, int x1, int y1, int x2, int y2, int r, color_t *color, color_t *bgcolor)
1550  {  {
1551          int r2 = 2*r;          int r2 = 2*r;
1552            if(!r)
1553                    gdImageFilledRectangle(im, x1, y1, x2, y2, bgcolor->id);
1554    #ifdef HAVE_GD_FILLEDARC
1555            else
1556            {
1557                    gdImageFilledArc(im, x1+r, y1+r, r2, r2, 180, 270, bgcolor->id, gdArc);
1558                    gdImageFilledArc(im, x2-r, y1+r, r2, r2, 270, 360, bgcolor->id, gdArc);
1559                    gdImageFilledArc(im, x1+r, y2-r, r2, r2,  90, 180, bgcolor->id, gdArc);
1560                    gdImageFilledArc(im, x2-r, y2-r, r2, r2,   0,  90, bgcolor->id, gdArc);
1561                    gdImageFilledRectangle(im, x1+r, y1, x2-r, y1+r, bgcolor->id);
1562                    gdImageFilledRectangle(im, x1, y1+r, x2, y2-r, bgcolor->id);
1563                    gdImageFilledRectangle(im, x1+r, y2-r, x2-r, y2, bgcolor->id);
1564            }
1565    #endif
1566          gdImageLine(im, x1+r, y1, x2-r, y1, color->id);          gdImageLine(im, x1+r, y1, x2-r, y1, color->id);
1567          gdImageLine(im, x1+r, y2, x2-r, y2, color->id);          gdImageLine(im, x1+r, y2, x2-r, y2, color->id);
1568          gdImageLine(im, x1, y1+r, x1, y2-r, color->id);          gdImageLine(im, x1, y1+r, x1, y2-r, color->id);
1569          gdImageLine(im, x2, y1+r, x2, y2-r, color->id);          gdImageLine(im, x2, y1+r, x2, y2-r, color->id);
1570          if(conf.box_shadow)          if(conf.box_shadow)
1571          {          {
1572                  gdImageLine(im, x1+r+1, y2+1, x2-r, y2+1, black_color.id);                  gdImageLine(im, x1+r+1, y2+1, x2-r, y2+1, clr(im, NULL, NULL, NULL, 0)->id);
1573                  gdImageLine(im, x2+1, y1+r+1, x2+1, y2-r, black_color.id);                  gdImageLine(im, x2+1, y1+r+1, x2+1, y2-r, clr(im, NULL, NULL, NULL, 0)->id);
1574          }          }
1575          if(r)          if(r)
1576          {          {
# Line 1324  Line 1580 
1580                  gdImageArc(im, x1+r, y2-r, r2, r2,  90, 180, color->id);                  gdImageArc(im, x1+r, y2-r, r2, r2,  90, 180, color->id);
1581                  if(conf.box_shadow)                  if(conf.box_shadow)
1582                  {                  {
1583                          gdImageArc(im, x2-r+1, y2-r+1, r2, r2,   0,  90, black_color.id);                          gdImageArc(im, x2-r+1, y2-r+1, r2, r2,   0,  90, clr(im, NULL, NULL, NULL, 0)->id);
1584                          gdImageArc(im, x2-r+1, y2-r, r2, r2,   0,  90, black_color.id);                          gdImageArc(im, x2-r+1, y2-r, r2, r2,   0,  90, clr(im, NULL, NULL, NULL, 0)->id);
1585                          gdImageArc(im, x2-r, y2-r+1, r2, r2,   0,  90, black_color.id);                          gdImageArc(im, x2-r, y2-r+1, r2, r2,   0,  90, clr(im, NULL, NULL, NULL, 0)->id);
1586                  }                  }
1587                  gdImageArc(im, x2-r, y2-r, r2, r2,   0,  90, color->id);                  gdImageArc(im, x2-r, y2-r, r2, r2,   0,  90, color->id);
1588          }  #if !defined(NOGDFILL) && !defined(HAVE_GD_FILLEDARC)
1589  #ifndef NOGDFILL                  /* BUG: We clip manually because libgd segfaults on out of bound values */
1590          gdImageFillToBorder(im, (x1+x2)/2, (y1+y2)/2, color->id, bgcolor->id);                  if((x1+x2)/2 >= 0 && (x1+x2)/2 < gdImageSX(im) && (y1+y2)/2 >= 0 && (y1+y2)/2 < gdImageSY(im))
1591                            gdImageFillToBorder(im, (x1+x2)/2, (y1+y2)/2, color->id, bgcolor->id);
1592  #endif  #endif
1593            }
1594  }  }
1595    
1596  static void draw_string(gdImagePtr im, char *s, font_t *f, int x, int y, int align, color_t *c)  static void draw_string(gdImagePtr im, char *s, font_t *f, int x, int y, int align, color_t *c)
# Line 1353  Line 1611 
1611          case ALIGN_VC: yy = h/2; break;          case ALIGN_VC: yy = h/2; break;
1612          case ALIGN_VB: yy = h; break;          case ALIGN_VB: yy = h; break;
1613          }          }
1614  #if defined(HAVE_GDIMAGESTRINGFT) || defined(HAVE_GDIMAGESTRINGTTF)  #if defined(HAVE_GD_STRINGFT) || defined(HAVE_GD_STRINGTTF)
1615          if(conf.use_ttf && f->ttfont)          if(conf.use_ttf && f->ttfont)
1616          {          {
1617                  int bb[8];                  int bb[8];
1618                  char *e;                  char *e;
1619                  int cid = conf.anti_alias ? c->id : -c->id;                  int cid = conf.anti_alias ? c->id : -c->id;
1620  #ifdef HAVE_GDIMAGESTRINGFT  #ifdef HAVE_GD_STRINGFT
1621                  e = gdImageStringFT(im, bb, cid, f->ttfont, f->ttsize, 0.0, x+xx, y+yy+h-2, (char *)s);                  e = gdImageStringFT(im, bb, cid, f->ttfont, f->ttsize, 0.0, x+xx, y+yy+h-2, (char *)s);
1622  #else  #else
1623                  e = gdImageStringTTF(im, bb, cid, f->ttfont, f->ttsize, 0.0, x+xx, y+yy+h-2, (char *)s);                  e = gdImageStringTTF(im, bb, cid, f->ttfont, f->ttsize, 0.0, x+xx, y+yy+h-2, (char *)s);
# Line 1369  Line 1627 
1627          }          }
1628  #endif  #endif
1629          yy = -yy;          yy = -yy;
1630          gdImageString(im, f->gdfont, x+xx+1, y+yy, s, c->id);          gdImageString(im, f->gdfont, x+xx+1, y+yy, (unsigned char *)s, c->id);
1631  }  }
1632    
1633  static void draw_stringnl(gdImagePtr im, char *s, font_t *f, int x, int y, int align, color_t *c)  static void draw_stringnl(gdImagePtr im, char *s, font_t *f, int x, int y, int align, color_t *c)
# Line 1411  Line 1669 
1669                  ty = r->y;                  ty = r->y;
1670                  x2 = r->cx;                  x2 = r->cx;
1671          }          }
1672          draw_rbox(im, lx, ty, rx, ty+r->h, 0, &conf.rev_color, &conf.rev_bgcolor);          draw_rbox(im, lx, ty, rx, ty+r->h, 0, clr(im, "rev_color", r, NULL, 0), clr(im, "rev_bgcolor", r, NULL, 0));
1673          ty += conf.rev_tspace;          ty += conf.rev_tspace;
1674          draw_string(im, r->rev->rev, &conf.rev_font, x2, ty, ALIGN_HC, &conf.rev_color);          if(!conf.rev_hidenumber)
1675          ty += get_sheight(r->rev->rev, &conf.rev_font);          {
1676          draw_stringnl(im, r->revtext, &conf.rev_text_font, x2, ty, ALIGN_HC, &conf.rev_text_color);                  draw_string(im, r->revidtext, &conf.rev_font, x2, ty, ALIGN_HC, clr(im, "rev_color", r, NULL, 0));
1677                    ty += get_sheight(r->revidtext, &conf.rev_font);
1678            }
1679            draw_stringnl(im, r->revtext, &conf.rev_text_font, x2, ty, ALIGN_HC, clr(im, "rev_text_color", r, NULL, 0));
1680          ty += get_sheight(r->revtext, &conf.rev_text_font);          ty += get_sheight(r->revtext, &conf.rev_text_font);
1681          for(i = 0; i < r->ntags; i++)          for(i = 0; i < r->ntags; i++)
1682          {          {
1683                  draw_string(im, r->tags[i]->tag, &conf.tag_font, x2, ty, ALIGN_HC, &conf.tag_color);                  draw_string(im, r->tags[i]->tag, &conf.tag_font, x2, ty, ALIGN_HC, clr(im, "tag_color", r, NULL, 0));
1684                  ty += get_sheight(r->tags[i]->tag, &conf.tag_font) + conf.rev_separator;                  ty += get_sheight(r->tags[i]->tag, &conf.tag_font) + conf.rev_separator;
1685          }          }
1686  }  }
# Line 1444  Line 1705 
1705                  rx = lx + b->w;                  rx = lx + b->w;
1706                  x2 = b->cx;                  x2 = b->cx;
1707          }          }
1708          draw_rbox(im, lx+xp, yp, rx+xp, yp+b->h, 5, &conf.branch_color, &conf.branch_bgcolor);          draw_rbox(im, lx+xp, yp, rx+xp, yp+b->h, 5, clr(im, "branch_color", NULL, b, 0), clr(im, "branch_bgcolor", NULL, b, 0));
1709          yy = conf.branch_tspace;          yy = conf.branch_tspace;
1710          if(!b->nfolds)          if(!b->nfolds)
1711          {          {
1712                  draw_string(im, b->branch->branch, &conf.branch_font, x2+xp, yp+yy, ALIGN_HC, &conf.branch_color);                  if(!conf.rev_hidenumber)
1713                  yy += get_sheight(b->branch->branch, &conf.branch_font);                  {
1714                            draw_string(im, b->branch->branch, &conf.branch_font, x2+xp, yp+yy, ALIGN_HC, clr(im, "branch_color", NULL, b, 0));
1715                            yy += get_sheight(b->branch->branch, &conf.branch_font);
1716                    }
1717                  for(i = 0; i < b->ntags; i++)                  for(i = 0; i < b->ntags; i++)
1718                  {                  {
1719                          draw_string(im, b->tags[i]->tag, &conf.branch_tag_font, x2+xp, yp+yy, ALIGN_HC, &conf.branch_tag_color);                          draw_string(im, b->tags[i]->tag, &conf.branch_tag_font, x2+xp, yp+yy, ALIGN_HC, clr(im, "branch_tag_color", NULL, b, 0));
1720                          yy += get_sheight(b->tags[i]->tag, &conf.branch_tag_font);                          yy += get_sheight(b->tags[i]->tag, &conf.branch_tag_font);
1721                  }                  }
1722          }          }
# Line 1461  Line 1725 
1725                  int y1, y2;                  int y1, y2;
1726                  int tx = lx + b->fw + conf.branch_lspace;                  int tx = lx + b->fw + conf.branch_lspace;
1727                  int nx = tx - get_swidth(" ", &conf.branch_font);                  int nx = tx - get_swidth(" ", &conf.branch_font);
1728                  draw_string(im, b->branch->branch, &conf.branch_font, nx+xp, yp+yy, ALIGN_HR, &conf.branch_color);                  draw_string(im, b->branch->branch, &conf.branch_font, nx+xp, yp+yy, ALIGN_HR, clr(im, "branch_color", NULL, b, 0));
1729                  y1 = get_sheight(b->branch->branch, &conf.branch_font);                  y1 = get_sheight(b->branch->branch, &conf.branch_font);
1730                  draw_string(im, b->tags[0]->tag, &conf.branch_tag_font, tx+xp, yp+yy, ALIGN_HL, &conf.branch_tag_color);                  draw_string(im, b->tags[0]->tag, &conf.branch_tag_font, tx+xp, yp+yy, ALIGN_HL, clr(im, "branch_tag_color", NULL, b, 0));
1731                  y2 = get_sheight(b->tags[0]->tag, &conf.branch_font);                  y2 = get_sheight(b->tags[0]->tag, &conf.branch_font);
1732                  yy += MAX(y1, y2);                  yy += MAX(y1, y2);
1733                  for(i = 0; i < b->nfolds; i++)                  for(i = 0; i < b->nfolds; i++)
1734                  {                  {
1735                          draw_string(im, b->folds[i]->branch->branch, &conf.branch_font, nx+xp, yp+yy, ALIGN_HR, &conf.branch_color);                          draw_string(im, b->folds[i]->branch->branch, &conf.branch_font, nx+xp, yp+yy, ALIGN_HR, clr(im, "branch_color", NULL, b, 0));
1736                          y1 = get_sheight(b->folds[i]->branch->branch, &conf.branch_font);                          y1 = get_sheight(b->folds[i]->branch->branch, &conf.branch_font);
1737                          draw_string(im, b->folds[i]->tags[0]->tag, &conf.branch_tag_font, tx+xp, yp+yy, ALIGN_HL, &conf.branch_tag_color);                          draw_string(im, b->folds[i]->tags[0]->tag, &conf.branch_tag_font, tx+xp, yp+yy, ALIGN_HL, clr(im, "branch_tag_color", NULL, b, 0));
1738                          y2 = get_sheight(b->folds[i]->tags[0]->tag, &conf.branch_tag_font);                          y2 = get_sheight(b->folds[i]->tags[0]->tag, &conf.branch_tag_font);
1739                          yy += MAX(y1, y2);                          yy += MAX(y1, y2);
1740                  }                  }
# Line 1485  Line 1749 
1749          int l;          int l;
1750          int sign;          int sign;
1751    
1752          line[0] = conf.rev_color.id;          line[1] = line[2] = gdTransparent;
1753          line[1] = gdTransparent;  
1754          line[1] = gdTransparent;          /* Trivial clip the branch */
1755          line[3] = conf.rev_color.id;          if(conf.left_right)
1756            {
1757                    if(b->cx > gdImageSX(im) || b->cx+b->tw < 0 || b->y-b->th/2 > gdImageSY(im) || b->y+b->th/2 < 0)
1758                            return;
1759            }
1760            else
1761            {
1762                    if(b->cx-b->tw/2 > gdImageSX(im) || b->cx+b->tw/2 < 0 || b->y > gdImageSY(im) || b->y+b->th < 0)
1763                            return;
1764            }
1765    
1766          draw_branch_box(im, b, 0, conf.left_right ? b->y - b->h/2 : b->y);          draw_branch_box(im, b, 0, conf.left_right ? b->y - b->h/2 : b->y);
1767    
# Line 1500  Line 1773 
1773                          for(i = 0; i < b->nrevs; i++)                          for(i = 0; i < b->nrevs; i++)
1774                          {                          {
1775                                  revision_t *r = b->revs[i];                                  revision_t *r = b->revs[i];
1776                                  gdImageSetStyle(im, line, r->stripped ? 4 : 1);                                  line[0] = line[3] = clr(im, "rev_color", r, b, 0)->id;
1777                                    gdImageSetStyle(im, line, r->stripped > 0 ? 4 : 1);
1778                                  gdImageLine(im, xx, r->y, r->cx+r->w, r->y, gdStyled);                                  gdImageLine(im, xx, r->y, r->cx+r->w, r->y, gdStyled);
1779                                  for(sign = l = 1; l < conf.thick_lines; l++)                                  for(sign = l = 1; l < conf.thick_lines; l++)
1780                                  {                                  {
# Line 1514  Line 1788 
1788                          if(conf.branch_dupbox && b->nrevs)                          if(conf.branch_dupbox && b->nrevs)
1789                          {                          {
1790                                  i = b->cx - b->tw + b->w;                                  i = b->cx - b->tw + b->w;
1791                                  gdImageLine(im, xx, b->y, i+b->w, b->y, conf.rev_color.id);                                  gdImageLine(im, xx, b->y, i+b->w, b->y, clr(im, "rev_color", NULL, b, 0)->id);
1792                                  for(sign = l = 1; l < conf.thick_lines; l++)                                  for(sign = l = 1; l < conf.thick_lines; l++)
1793                                  {                                  {
1794                                          int pp = (l+1)/2*sign;                                          int pp = (l+1)/2*sign;
1795                                          gdImageLine(im, xx, b->y+pp, i+b->w, b->y+pp, conf.rev_color.id);                                          gdImageLine(im, xx, b->y+pp, i+b->w, b->y+pp, clr(im, "rev_color", NULL, b, 0)->id);
1796                                          sign *= -1;                                          sign *= -1;
1797                                  }                                  }
1798                                  draw_branch_box(im, b, i - b->cx, b->y - b->h/2);                                  draw_branch_box(im, b, i - b->cx, b->y - b->h/2);
# Line 1530  Line 1804 
1804                          for(i = 0; i < b->nrevs; i++)                          for(i = 0; i < b->nrevs; i++)
1805                          {                          {
1806                                  revision_t *r = b->revs[i];                                  revision_t *r = b->revs[i];
1807                                  gdImageSetStyle(im, line, r->stripped ? 4 : 1);                                  line[0] = line[3] = clr(im, "rev_color", r, b, 0)->id;
1808                                    gdImageSetStyle(im, line, r->stripped > 0 ? 4 : 1);
1809                                  gdImageLine(im, xx, r->y, r->cx, r->y, gdStyled);                                  gdImageLine(im, xx, r->y, r->cx, r->y, gdStyled);
1810                                  for(sign = l = 1; l < conf.thick_lines; l++)                                  for(sign = l = 1; l < conf.thick_lines; l++)
1811                                  {                                  {
# Line 1544  Line 1819 
1819                          if(conf.branch_dupbox && b->nrevs)                          if(conf.branch_dupbox && b->nrevs)
1820                          {                          {
1821                                  i = b->cx + b->tw - b->w;                                  i = b->cx + b->tw - b->w;
1822                                  gdImageLine(im, xx, b->y, i, b->y, conf.rev_color.id);                                  gdImageLine(im, xx, b->y, i, b->y, clr(im, "rev_color", NULL, b, 0)->id);
1823                                  for(sign = l = 1; l < conf.thick_lines; l++)                                  for(sign = l = 1; l < conf.thick_lines; l++)
1824                                  {                                  {
1825                                          int pp = (l+1)/2*sign;                                          int pp = (l+1)/2*sign;
1826                                          gdImageLine(im, xx, b->y+pp, i, b->y+pp, conf.rev_color.id);                                          gdImageLine(im, xx, b->y+pp, i, b->y+pp, clr(im, "rev_color", NULL, b, 0)->id);
1827                                          sign *= -1;                                          sign *= -1;
1828                                  }                                  }
1829                                  draw_branch_box(im, b, i - b->cx, b->y - b->h/2);                                  draw_branch_box(im, b, i - b->cx, b->y - b->h/2);
# Line 1563  Line 1838 
1838                          for(i = 0; i < b->nrevs; i++)                          for(i = 0; i < b->nrevs; i++)
1839                          {                          {
1840                                  revision_t *r = b->revs[i];                                  revision_t *r = b->revs[i];
1841                                  gdImageSetStyle(im, line, r->stripped ? 4 : 1);                                  line[0] = line[3] = clr(im, "rev_color", r, b, 0)->id;
1842                                    gdImageSetStyle(im, line, r->stripped > 0 ? 4 : 1);
1843                                  gdImageLine(im, r->cx, yy, r->cx, r->y+r->h, gdStyled);                                  gdImageLine(im, r->cx, yy, r->cx, r->y+r->h, gdStyled);
1844                                  for(sign = l = 1; l < conf.thick_lines; l++)                                  for(sign = l = 1; l < conf.thick_lines; l++)
1845                                  {                                  {
# Line 1577  Line 1853 
1853                          if(conf.branch_dupbox && b->nrevs)                          if(conf.branch_dupbox && b->nrevs)
1854                          {                          {
1855                                  i = b->y - b->th + b->h;                                  i = b->y - b->th + b->h;
1856                                  gdImageLine(im, b->cx, yy, b->cx, i, conf.rev_color.id);                                  gdImageLine(im, b->cx, yy, b->cx, i, clr(im, "rev_color", NULL, b, 0)->id);
1857                                  for(sign = l = 1; l < conf.thick_lines; l++)                                  for(sign = l = 1; l < conf.thick_lines; l++)
1858                                  {                                  {
1859                                          int pp = (l+1)/2*sign;                                          int pp = (l+1)/2*sign;
1860                                          gdImageLine(im, b->cx+pp, yy, b->cx+pp, i, conf.rev_color.id);                                          gdImageLine(im, b->cx+pp, yy, b->cx+pp, i, clr(im, "rev_color", NULL, b, 0)->id);
1861                                          sign *= -1;                                          sign *= -1;
1862                                  }                                  }
1863                                  draw_branch_box(im, b, 0, i);                                  draw_branch_box(im, b, 0, i);
# Line 1593  Line 1869 
1869                          for(i = 0; i < b->nrevs; i++)                          for(i = 0; i < b->nrevs; i++)
1870                          {                          {
1871                                  revision_t *r = b->revs[i];                                  revision_t *r = b->revs[i];
1872                                  gdImageSetStyle(im, line, r->stripped ? 4 : 1);                                  line[0] = line[3] = clr(im, "rev_color", r, b, 0)->id;
1873                                    gdImageSetStyle(im, line, r->stripped > 0 ? 4 : 1);
1874                                  gdImageLine(im, r->cx, yy, r->cx, r->y, gdStyled);                                  gdImageLine(im, r->cx, yy, r->cx, r->y, gdStyled);
1875                                  for(sign = l = 1; l < conf.thick_lines; l++)                                  for(sign = l = 1; l < conf.thick_lines; l++)
1876                                  {                                  {
# Line 1607  Line 1884 
1884                          if(conf.branch_dupbox && b->nrevs)                          if(conf.branch_dupbox && b->nrevs)
1885                          {                          {
1886                                  i = b->y + b->th - b->h;                                  i = b->y + b->th - b->h;
1887                                  gdImageLine(im, b->cx, yy, b->cx, i, conf.rev_color.id);                                  gdImageLine(im, b->cx, yy, b->cx, i, clr(im, "rev_color", NULL, b, 0)->id);
1888                                  for(sign = l = 1; l < conf.thick_lines; l++)                                  for(sign = l = 1; l < conf.thick_lines; l++)
1889                                  {                                  {
1890                                          int pp = (l+1)/2*sign;                                          int pp = (l+1)/2*sign;
1891                                          gdImageLine(im, b->cx+pp, yy, b->cx+pp, i, conf.rev_color.id);                                          gdImageLine(im, b->cx+pp, yy, b->cx+pp, i, clr(im, "rev_color", NULL, b, 0)->id);
1892                                          sign *= -1;                                          sign *= -1;
1893                                  }                                  }
1894                                  draw_branch_box(im, b, 0, i);                                  draw_branch_box(im, b, 0, i);
# Line 1648  Line 1925 
1925                  if(conf.upside_down)                  if(conf.upside_down)
1926                          y2 += b->h;                          y2 += b->h;
1927          }          }
1928          gdImageLine(im, x1, y1, x2, y1, conf.branch_color.id);          gdImageLine(im, x1, y1, x2, y1, clr(im, "branch_color", NULL, b, 0)->id);
1929          gdImageLine(im, x2, y1, x2, y2, conf.branch_color.id);          gdImageLine(im, x2, y1, x2, y2, clr(im, "branch_color", NULL, b, 0)->id);
1930          for(sign = l = 1; l < conf.thick_lines; l++)          for(sign = l = 1; l < conf.thick_lines; l++)
1931          {          {
1932                  int pp = (l+1)/2*sign;                  int pp = (l+1)/2*sign;
1933                  gdImageLine(im, x1, y1+pp, x2, y1+pp, conf.branch_color.id);                  gdImageLine(im, x1, y1+pp, x2, y1+pp, clr(im, "branch_color", NULL, b, 0)->id);
1934                  gdImageLine(im, x2+pp, y1, x2+pp, y2, conf.branch_color.id);                  gdImageLine(im, x2+pp, y1, x2+pp, y2, clr(im, "branch_color", NULL, b, 0)->id);
1935                  sign *= -1;                  sign *= -1;
1936          }          }
1937  }  }
1938    
1939  static void draw_merges(gdImagePtr im, rcsfile_t *rcs, int dot)  
1940    static void calc_merge_coords(merge_t *mt, revision_t *fr, revision_t *tr, int *x1, int *x2, int *y1, int *y2, int *sx1, int *sx2, int *sy1, int *sy2)
1941  {  {
1942          int i;          int shadow = conf.box_shadow ? 1 : 0;
1943          for(i = 0; i < rcs->nmerges; i++)          assert(mt != NULL);
1944          {          assert(fr != NULL);
1945                  revision_t *fr = rcs->merges[i].from->logrev;          assert(tr != NULL);
1946                  revision_t *tr = rcs->merges[i].to->logrev;          assert(x1 != NULL);
1947                  int x1, x2, y1, y2;          assert(x2 != NULL);
1948                  if(!fr || !tr || fr == tr)          assert(y1 != NULL);
1949                          continue;       /* This can happen with detached tags and self-references */          assert(y2 != NULL);
1950                  if(conf.left_right)          assert(sx1 != NULL);
1951            assert(sx2 != NULL);
1952            assert(sy1 != NULL);
1953            assert(sy2 != NULL);
1954            if(conf.left_right && !conf.merge_on_tag)
1955            {
1956                    if(fr->branch == tr->branch)
1957                    {
1958                            *y1 = fr->y - (fr->h+1)/2;
1959                            *y2 = tr->y - (tr->h+1)/2;
1960                            *sy1 = *sy2 = -1;
1961                    }
1962                    else
1963                  {                  {
1964                          if(fr->branch == tr->branch)                          /* See comment below on shortest path */
1965                            int y1a = fr->y + (fr->h+1)/2 + shadow;
1966                            int y1b = fr->y - (fr->h+1)/2;
1967                            int y2a = tr->y + (tr->h+1)/2 + shadow;
1968                            int y2b = tr->y - (tr->h+1)/2;
1969                            int laa = abs(y2a - y1a);
1970                            int lba = abs(y2a - y1b);
1971                            int lab = abs(y2b - y1a);
1972                            int lbb = abs(y2b - y1b);
1973                            if(laa < lab)
1974                          {                          {
1975                                  y1 = fr->y - fr->h/2;                                  if(laa < lba)
1976                                  y2 = tr->y - tr->h/2;                                  {
1977                                            if(laa < lbb)
1978                                            {
1979                                                    *y1 = y1a;
1980                                                    *y2 = y2a;
1981                                                    *sy1 = *sy2 = 1;
1982                                            }
1983                                            else
1984                                            {
1985    ybb:
1986                                                    *y1 = y1b;
1987                                                    *y2 = y2b;
1988                                                    *sy1 = *sy2 = -1;
1989                                            }
1990                                    }
1991                                    else
1992                                    {
1993    yba:
1994                                            if(lba < lbb)
1995                                            {
1996                                                    *y1 = y1b;
1997                                                    *y2 = y2a;
1998                                                    *sy1 = -1;
1999                                                    *sy2 = 1;
2000                                            }
2001                                            else
2002                                                    goto ybb;
2003                                    }
2004                          }                          }
2005                          else                          else
2006                          {                          {
2007                                  if(fr->y < tr->y)                                  if(lab < lba)
2008                                  {                                  {
2009                                          y1 = fr->y + fr->h/2;                                          if(lab < lbb)
2010                                          y2 = tr->y - tr->h/2;                                          {
2011                                                    *y1 = y1a;
2012                                                    *y2 = y2b;
2013                                                    *sy1 = 1;
2014                                                    *sy2 = -1;
2015                                            }
2016                                            else
2017                                                    goto ybb;
2018                                  }                                  }
2019                                  else                                  else
2020                                  {                                          goto yba;
                                         y1 = fr->y - fr->h/2;  
                                         y2 = tr->y + tr->h/2;  
                                 }  
2021                          }                          }
2022                          x1 = fr->cx + fr->w/2;                  }
2023                          x2 = tr->cx + tr->w/2;                  *x1 = fr->cx + fr->w/2;
2024                    *x2 = tr->cx + tr->w/2;
2025                    *sx1 = *sx2 = 1;
2026            }
2027            else
2028            {
2029                    if(fr->branch == tr->branch)
2030                    {
2031                            /* Line on same branch always on left side */
2032                            *x1 = fr->cx - fr->w/2;
2033                            *x2 = tr->cx - tr->w/2;
2034                            *sx1 = *sx2 = -1;
2035                  }                  }
2036                  else                  else
2037                  {                  {
2038                          if(fr->branch == tr->branch)                          /* Find the shortest route from the two revisions
2039                             * to determine which sides of the revision box
2040                             * should be used. The basics are:
2041                             * l = (x2 -x1)^2 + (y2 - y1)^2
2042                             * However, (y2 -y1) is constant and hence the
2043                             * determination of the shortest path is already
2044                             * clear from |x2 -x1| for each permutation of left
2045                             * and right x.
2046                             * This strategy is still not perfect because it can
2047                             * happen that a source/destination is overlayed by
2048                             * the revision box. To prevent this, we need to do
2049                             * a very deep analysis and I'm not prepared to do
2050                             * that right now...
2051                             */
2052                            int x1a = fr->cx + (fr->w+1)/2 + shadow;
2053                            int x1b = fr->cx - (fr->w+1)/2;
2054                            int x2a = tr->cx + (tr->w+1)/2 + shadow;
2055                            int x2b = tr->cx - (tr->w+1)/2;
2056                            int laa = abs(x2a - x1a);
2057                            int lba = abs(x2a - x1b);
2058                            int lab = abs(x2b - x1a);
2059                            int lbb = abs(x2b - x1b);
2060                            if(laa < lab)
2061                          {                          {
2062                                  x1 = fr->cx - fr->w/2;                                  if(laa < lba)
2063                                  x2 = tr->cx - tr->w/2;                                  {
2064                                            if(laa < lbb)
2065                                            {
2066                                                    *x1 = x1a;
2067                                                    *x2 = x2a;
2068                                                    *sx1 = *sx2 = 1;
2069                                            }
2070                                            else
2071                                            {
2072    xbb:
2073                                                    *x1 = x1b;
2074                                                    *x2 = x2b;
2075                                                    *sx1 = *sx2 = -1;
2076                                            }
2077                                    }
2078                                    else
2079                                    {
2080    xba:
2081                                            if(lba < lbb)
2082                                            {
2083                                                    *x1 = x1b;
2084                                                    *x2 = x2a;
2085                                                    *sx1 = -1;
2086                                                    *sx2 = 1;
2087                                            }
2088                                            else
2089                                                    goto xbb;
2090                                    }
2091                          }                          }
2092                          else                          else
2093                          {                          {
2094                                  if(fr->cx < tr->cx)                                  if(lab < lba)
2095                                  {                                  {
2096                                          x1 = fr->cx + fr->w/2;                                          if(lab < lbb)
2097                                          x2 = tr->cx - tr->w/2;                                          {
2098                                                    *x1 = x1a;
2099                                                    *x2 = x2b;
2100                                                    *sx1 = 1;
2101                                                    *sx2 = -1;
2102                                            }
2103                                            else
2104                                                    goto xbb;
2105                                  }                                  }
2106                                  else                                  else
2107                                  {                                          goto xba;
                                         x1 = fr->cx - fr->w/2;  
                                         x2 = tr->cx + tr->w/2;  
                                 }  
2108                          }                          }
                         y1 = fr->y + rcs->merges[i].from->yofs;  
                         y2 = tr->y + rcs->merges[i].to->yofs;  
2109                  }                  }
2110                    if(mt->type == TR_TAG)
2111                    {
2112                            *y1 = fr->y + mt->from.tag->yofs;
2113                            *y2 = tr->y + mt->to.tag->yofs;
2114                            if(conf.left_right && conf.merge_on_tag)
2115                            {
2116                                    *y1 -= fr->h/2;
2117                                    *y2 -= tr->h/2;
2118                            }
2119                    }
2120                    else
2121                    {
2122                            *y1 = fr->y + fr->h/2;
2123                            *y2 = tr->y + tr->h/2;
2124                    }
2125                    *sy1 = *sy2 = 1;
2126                    if(conf.left_right && conf.merge_on_tag)
2127                    {
2128                            *x1 += fr->w/2;
2129                            *x2 += tr->w/2;
2130                    }
2131            }
2132    }
2133    
2134    static void draw_merges(gdImagePtr im, rcsfile_t *rcs, int dot)
2135    {
2136            int i;
2137            for(i = 0; i < rcs->nmerges; i++)
2138            {
2139                    revision_t *fr;
2140                    revision_t *tr;
2141                    int colorid;
2142                    int x1, x2, y1, y2;             /* Edge position on revision box */
2143                    int sx1, sx2, sy1, sy2;         /* Direction for mergeline -1 = left/up, +1 = right/down */
2144                    switch(rcs->merges[i].type)
2145                    {
2146                    case TR_TAG:
2147                            fr = rcs->merges[i].from.tag->logrev;
2148                            tr = rcs->merges[i].to.tag->logrev;
2149                            colorid = clr(im, "merge_color", NULL, NULL, rcs->merges[i].clr)->id;
2150                            break;
2151                    case TR_REVISION:
2152                            fr = rcs->merges[i].from.rev;
2153                            tr = rcs->merges[i].to.rev;
2154                            colorid = clr(im, "merge_cvsnt_color", NULL, NULL, 0)->id;
2155                            break;
2156                    default:
2157                            continue;
2158                    }
2159                    if(!fr || !tr || fr == tr)
2160                            continue;       /* This can happen with detached tags and self-references */
2161    
2162                    calc_merge_coords(&rcs->merges[i], fr, tr, &x1, &x2, &y1, &y2, &sx1, &sx2, &sy1, &sy2);
2163    
2164                  if(dot && !conf.merge_arrows)                  if(dot && !conf.merge_arrows)
2165                  {                  {
2166                          int o = conf.left_right ? 1 : 0;                          int o = conf.left_right ? 1 : 0;
2167                          gdImageArc(im, x2, y2+o, 8, 8, 0, 360, conf.merge_color.id);                          gdImageArc(im, x2, y2+o, 8, 8, 0, 360, colorid);
2168                          gdImageFillToBorder(im, x2+1, y2+o+1, conf.merge_color.id, conf.merge_color.id);                          /* BUG: We clip manually because libgd segfaults on out of bound values */
2169                            if(x2+1 >= 0 && x2+1 < gdImageSX(im) && y2+o+1 >= 0 && y2+o+1 < gdImageSY(im))
2170                                    gdImageFillToBorder(im, x2+1, y2+o+1, colorid, colorid);
2171                  }                  }
2172                  else if(dot && conf.merge_arrows)                  else if(dot && conf.merge_arrows)
2173                  {                  {
# Line 1735  Line 2183 
2183    
2184                          sx = x1; sy = y1;                          sx = x1; sy = y1;
2185                          ex = x2; ey = y2;                          ex = x2; ey = y2;
2186                          if(conf.left_right)                          if(conf.left_right && !conf.merge_on_tag)
2187                          {                          {
2188                                  if(fr->branch == tr->branch)                                  if(fr->branch == tr->branch)
2189                                  {                                  {
# Line 1745  Line 2193 
2193                                  }                                  }
2194                                  else                                  else
2195                                  {                                  {
2196                                          if(y1 > y2)                                          sy = y1 + 3 * sy1;
2197                                          {                                          ey = y2 + 3 * sy2;
                                                 /* line from (x1,y1-3) to (x2,y2+3+1) */  
                                                 sy = y1-3;  
                                                 ey = y2+3+1;  
                                         }  
                                         else  
                                         {  
                                                 /* line from (x1,y1+3+1) to (x2,y2-3) */  
                                                 sy = y1+3+1;  
                                                 ey = y2-3;  
                                         }  
2198                                  }                                  }
2199                          }                          }
2200                          else                          else
# Line 1769  Line 2207 
2207                                  }                                  }
2208                                  else                                  else
2209                                  {                                  {
2210                                          if(x1 > x2)                                          sx = x1 + 3 * sx1;
2211                                          {                                          ex = x2 + 3 * sx2;
                                                 /* line from (x1-3,y1) to (x2+3,y2) */  
                                                 sx = x1-3;  
                                                 ex = x2+3;  
                                         }  
                                         else  
                                         {  
                                                 /* line from (x1+3,y1) to (x2-3,y2) */  
                                                 sx = x1+3;  
                                                 ex = x2-3;  
                                         }  
2212                                  }                                  }
2213                          }                          }
2214                          /*                          /*
# Line 1801  Line 2229 
2229                          p[2].x = ROUND(ex + u1 + u2);                          p[2].x = ROUND(ex + u1 + u2);
2230                          p[2].y = ROUND(ey + v1 + v2);                          p[2].y = ROUND(ey + v1 + v2);
2231                          /* draw the polygon (triangle) */                          /* draw the polygon (triangle) */
2232                          gdImageFilledPolygon(im, p, 3, conf.merge_color.id);                          gdImageFilledPolygon(im, p, 3, colorid);
2233                  }                  }
2234                  else                  else
2235                  {                  {
2236                          if(conf.left_right)                          if(conf.left_right && !conf.merge_on_tag)
2237                          {                          {
2238                                  if(fr->branch == tr->branch)                                  if(fr->branch == tr->branch)
2239                                  {                                  {
2240                                          int yy = (y1 < y2 ? y1 : y2) - 5;                                          int yy = (y1 < y2 ? y1 : y2) - 5;
2241                                          gdImageLine(im, x1, y1, x1, yy, conf.merge_color.id);                                          gdImageLine(im, x1, y1, x1, yy, colorid);
2242                                          gdImageLine(im, x2, y2, x2, yy, conf.merge_color.id);                                          gdImageLine(im, x2, y2, x2, yy, colorid);
2243                                          gdImageLine(im, x1, yy, x2, yy, conf.merge_color.id);                                          gdImageLine(im, x1, yy, x2, yy, colorid);
2244                                  }                                  }
2245                                  else                                  else
2246                                  {                                  {
2247                                          if(y1 > y2)                                          gdImageLine(im, x1, y1, x1, y1+3*sy1, colorid);
2248                                          {                                          gdImageLine(im, x2, y2, x2, y2+3*sy2, colorid);
2249                                                  gdImageLine(im, x1, y1, x1, y1-3, conf.merge_color.id);                                          gdImageLine(im, x1, y1+3*sy1, x2, y2+3*sy2, colorid);
                                                 gdImageLine(im, x2, y2+1, x2, y2+3+1, conf.merge_color.id);  
                                                 gdImageLine(im, x1, y1-3, x2, y2+3+1, conf.merge_color.id);  
                                         }  
                                         else  
                                         {  
                                                 gdImageLine(im, x1, y1+1, x1, y1+3+1, conf.merge_color.id);  
                                                 gdImageLine(im, x2, y2, x2, y2-3, conf.merge_color.id);  
                                                 gdImageLine(im, x1, y1+3+1, x2, y2-3, conf.merge_color.id);  
                                         }  
2250                                  }                                  }
2251                          }                          }
2252                          else                          else
# Line 1835  Line 2254 
2254                                  if(fr->branch == tr->branch)                                  if(fr->branch == tr->branch)
2255                                  {                                  {
2256                                          int xx = (x1 < x2 ? x1 : x2) - 5;                                          int xx = (x1 < x2 ? x1 : x2) - 5;
2257                                          gdImageLine(im, xx, y1, x1, y1, conf.merge_color.id);                                          gdImageLine(im, xx, y1, x1, y1, colorid);
2258                                          gdImageLine(im, xx, y2, x2, y2, conf.merge_color.id);                                          gdImageLine(im, xx, y2, x2, y2, colorid);
2259                                          gdImageLine(im, xx, y1, xx, y2, conf.merge_color.id);                                          gdImageLine(im, xx, y1, xx, y2, colorid);
2260                                  }                                  }
2261                                  else                                  else
2262                                  {                                  {
2263                                          if(x1 > x2)                                          gdImageLine(im, x1, y1, x1+3*sx1, y1, colorid);
2264                                          {                                          gdImageLine(im, x2, y2, x2+3*sx2, y2, colorid);
2265                                                  gdImageLine(im, x1, y1, x1-3, y1, conf.merge_color.id);                                          gdImageLine(im, x1+3*sx1, y1, x2+3*sx2, y2, colorid);
                                                 gdImageLine(im, x2, y2, x2+3, y2, conf.merge_color.id);  
                                                 gdImageLine(im, x1-3, y1, x2+3, y2, conf.merge_color.id);  
                                         }  
                                         else  
                                         {  
                                                 gdImageLine(im, x1, y1, x1+3, y1, conf.merge_color.id);  
                                                 gdImageLine(im, x2, y2, x2-3, y2, conf.merge_color.id);  
                                                 gdImageLine(im, x1+3, y1, x2-3, y2, conf.merge_color.id);  
                                         }  
2266                                  }                                  }
2267                          }                          }
2268                  }                  }
2269          }          }
2270  }  }
2271    
2272  static void alloc_color(gdImagePtr im, color_t *c)  static void draw_messages(gdImagePtr im, int offset)
2273  {  {
2274          c->id = gdImageColorAllocate(im, c->r, c->g, c->b);          int i;
2275    
2276            for(i = 0; i < nmsg_stack; i++)
2277            {
2278                    draw_stringnl(im, msg_stack[i].msg, &conf.msg_font, conf.margin_left, offset, ALIGN_HL|ALIGN_VT, clr(im, "msg_color", NULL, NULL, 0));
2279                    offset += msg_stack[i].h;
2280            }
2281  }  }
2282    
2283  gdImagePtr make_image(rcsfile_t *rcs)  static gdImagePtr make_image(rcsfile_t *rcs)
2284  {  {
2285          gdImagePtr im;          gdImagePtr im;
2286          int i;          int i;
2287            int bgid;
2288          char *cptr;          char *cptr;
2289            int w, h;
2290            int subx = 0, suby = 0;
2291            int msgh = 0;
2292    
2293            if(subtree_branch)
2294            {
2295                    w = 0;
2296                    h = 0;
2297                    if(subtree_rev)
2298                    {
2299                            for(i = 0; i < subtree_rev->nbranches; i++)
2300                                    calc_subtree_size(subtree_rev->branches[i], &subx, &suby, &w, &h);
2301                    }
2302                    else
2303                            calc_subtree_size(subtree_branch, &subx, &suby, &w, &h);
2304            }
2305            else
2306            {
2307                    w = rcs->tw;
2308                    h = rcs->th;
2309            }
2310    
2311          cptr = expand_string(conf.title, rcs, NULL, NULL, NULL, NULL);          cptr = expand_string(conf.title, rcs, NULL, NULL, NULL, NULL);
2312          i = get_swidth(cptr, &conf.title_font);          i = get_swidth(cptr, &conf.title_font);
2313          if(rcs->tw+conf.margin_left+conf.margin_right > i)          if(i > w)
2314                  i = rcs->tw+conf.margin_left+conf.margin_right;                  w = i;
2315          im = gdImageCreate(i, rcs->th+conf.margin_top+conf.margin_bottom);  
2316          alloc_color(im, &conf.color_bg);          if(!quiet && nmsg_stack)
2317          alloc_color(im, &conf.tag_color);          {
2318          alloc_color(im, &conf.rev_color);                  int msgw = 0;
2319          alloc_color(im, &conf.rev_bgcolor);                  for(i = 0; i < nmsg_stack; i++)
2320          alloc_color(im, &conf.rev_text_color);                  {
2321          alloc_color(im, &conf.branch_color);                          int ww = msg_stack[i].w = get_swidth(msg_stack[i].msg, &conf.msg_font);
2322          alloc_color(im, &conf.branch_tag_color);                          int hh = msg_stack[i].h = get_sheight(msg_stack[i].msg, &conf.msg_font);
2323          alloc_color(im, &conf.branch_bgcolor);                          msgh += hh;
2324          alloc_color(im, &conf.title_color);                          h += hh;
2325          alloc_color(im, &conf.merge_color);                          if(ww > msgw)
2326          alloc_color(im, &black_color);                                  msgw = ww;
2327          alloc_color(im, &white_color);                  }
2328                    if(msgw > w)
2329                            w = msgw;
2330            }
2331    
2332            w += conf.margin_left + conf.margin_right;
2333            h += conf.margin_top + conf.margin_bottom;
2334    
2335            im = gdImageCreate(w, h);
2336            bgid = clr(im, "color_bg", NULL, NULL, 0)->id;  /* The background is always a unique color, */
2337            zap_clr();                                      /* so clear the color ref table */
2338            clr(im, NULL, NULL, NULL, 0);
2339    
2340          if(conf.transparent_bg)          if(conf.transparent_bg)
2341                  gdImageColorTransparent(im, conf.color_bg.id);                  gdImageColorTransparent(im, bgid);
2342    
2343          if(!conf.merge_front)          if(!conf.merge_front)
2344                  draw_merges(im, rcs, 0);                  draw_merges(im, rcs, 0);
2345    
2346          for(i = 0; i < rcs->nbranches; i++)          for(i = 0; i < rcs->nbranches; i++)
2347          {          {
2348                  if(!rcs->branches[i]->folded)                  if(!rcs->branches[i]->folded && !(subtree_branch && !rcs->branches[i]->subtree_draw))
2349                          draw_branch(im, rcs->branches[i]);                          draw_branch(im, rcs->branches[i]);
2350          }          }
2351    
# Line 1907  Line 2356 
2356                  if(rcs->branches[i]->branchpoint)                  if(rcs->branches[i]->branchpoint)
2357                          draw_connector(im, rcs->branches[i]);                          draw_connector(im, rcs->branches[i]);
2358          }          }
2359          draw_stringnl(im, cptr, &conf.title_font, conf.title_x, conf.title_y, conf.title_align, &conf.title_color);  
2360            /* Clear the margins if we have a partial tree */
2361            if(subtree_branch)
2362            {
2363                    gdImageFilledRectangle(im, 0, 0, w-1, conf.margin_top-1, bgid);
2364                    gdImageFilledRectangle(im, 0, 0, conf.margin_left-1, h-1, bgid);
2365                    gdImageFilledRectangle(im, 0, h-conf.margin_bottom, w-1, h-1, bgid);
2366                    gdImageFilledRectangle(im, w-conf.margin_right, 0, w-1, h-1, bgid);
2367            }
2368    
2369            draw_stringnl(im, cptr, &conf.title_font, conf.title_x, conf.title_y, conf.title_align, clr(im, "title_color", NULL, NULL, 0));
2370          xfree(cptr);          xfree(cptr);
2371    
2372          if(conf.merge_front)          if(conf.merge_front)
2373                  draw_merges(im, rcs, 0);                  draw_merges(im, rcs, 0);
2374    
2375            if(!quiet)
2376                    draw_messages(im, h - conf.margin_bottom/2 - msgh);
2377    
2378          return im;          return im;
2379  }  }
2380    
# Line 2038  Line 2500 
2500          *h = y2 - y1;          *h = y2 - y1;
2501  }  }
2502    
2503    static void calc_subtree_size(branch_t *b, int *x, int *y, int *w, int *h)
2504    {
2505            int i, j;
2506    
2507            rect_union(x, y, w, h, b);
2508    
2509            for(i = 0; i < b->nrevs; i++)
2510            {
2511                    for(j = 0; j < b->revs[i]->nbranches; j++)
2512                            calc_subtree_size(b->revs[i]->branches[j], x, y, w, h);
2513            }
2514    }
2515    
2516  static int branch_intersects(int top, int bottom, int left, branch_t *b)  static int branch_intersects(int top, int bottom, int left, branch_t *b)
2517  {  {
2518          int br = b->cx + b->tw/2;          int br = b->cx + b->tw/2;
# Line 2131  Line 2606 
2606                  fprintf(stderr, "kern_tree: moved=%d\n", moved);                  fprintf(stderr, "kern_tree: moved=%d\n", moved);
2607  #endif  #endif
2608          }          }
2609          if(!quiet && !safeguard)          if(!safeguard)
2610                  fprintf(stderr, "kern_tree: safeguard terminated possible infinite loop; please report.\n");                  stack_msg(MSG_WARN, "kern_tree: safeguard terminated possible infinite loop; please report.");
2611          return totalmoved;          return totalmoved;
2612  }  }
2613    
# Line 2145  Line 2620 
2620                  if(r == b->revs[i])                  if(r == b->revs[i])
2621                          return i;                          return i;
2622          }          }
2623          fprintf(stderr, "index_of_revision: Cannot find revision in branch\n");          stack_msg(MSG_ERR, "index_of_revision: Cannot find revision in branch\n");
2624          return 0;          return 0;
2625  }  }
2626    
# Line 2306  Line 2781 
2781    
2782          if(!tagbr->branchpoint || !colbr->branchpoint)          if(!tagbr->branchpoint || !colbr->branchpoint)
2783          {          {
2784                  if(!quiet)                  stack_msg(MSG_WARN, "space_available: Trying to stretch the top?");
                         fprintf(stderr, "space_available: Trying to stretch the top?\n");  
2785                  return 0;                  return 0;
2786          }          }
2787    
# Line 2352  Line 2826 
2826                          branchpoint = ancestor->branchpoint;                          branchpoint = ancestor->branchpoint;
2827                          if(!branchpoint)                          if(!branchpoint)
2828                          {                          {
2829                                  if(!quiet)                                  stack_msg(MSG_WARN, "space_available: No common ancestor?");
                                         fprintf(stderr, "space_available: No common ancestor?\n");  
2830                                  return 0;                                  return 0;
2831                          }                          }
2832                          ancestor = branchpoint->branch;                          ancestor = branchpoint->branch;
# Line 2379  Line 2852 
2852          branch_t *b;          branch_t *b;
2853          int i;          int i;
2854          int space;          int space;
2855          int nlinks;          int nlinks = 0;
2856          int dy;          int dy;
2857          int rest;          int rest;
2858    
# Line 2454  Line 2927 
2927          return col;          return col;
2928  }  }
2929    
2930  void auto_stretch(rcsfile_t *rcs)  static void auto_stretch(rcsfile_t *rcs)
2931  {  {
2932          int i;          int i;
2933          int safeguard;          int safeguard;
# Line 2507  Line 2980 
2980                          }                          }
2981                  }                  }
2982          }          }
2983          if(!quiet && !safeguard)          if(!safeguard)
2984                  fprintf(stderr, "auto_stretch: safeguard terminated possible infinite loop; please report.\n");                  stack_msg(MSG_ERR, "auto_stretch: safeguard terminated possible infinite loop; please report.");
2985  }  }
2986    
2987  static void fold_branch(rcsfile_t *rcs, revision_t *r)  static void fold_branch(rcsfile_t *rcs, revision_t *r)
# Line 2516  Line 2989 
2989          int i, j;          int i, j;
2990          branch_t *btag = NULL;          branch_t *btag = NULL;
2991    
         if(r->nbranches < 2)  
                 return;         /* Should not happen... */  
   
2992          for(i = 0; i < r->nbranches; i++)          for(i = 0; i < r->nbranches; i++)
2993          {          {
2994                  branch_t *b = r->branches[i];                  branch_t *b = r->branches[i];
# Line 2531  Line 3001 
3001                          {                          {
3002                                  /* We have consecutive empty branches, fold */                                  /* We have consecutive empty branches, fold */
3003                                  b->folded = 1;                                  b->folded = 1;
3004                                    b->folded_to = btag;
3005                                  for(j = 0; j < rcs->nbranches; j++)                                  for(j = 0; j < rcs->nbranches; j++)
3006                                  {                                  {
3007                                          if(b == rcs->branches[j])                                          if(b == rcs->branches[j])
# Line 2540  Line 3011 
3011                                                          &rcs->branches[j+1],                                                          &rcs->branches[j+1],
3012                                                          (rcs->nbranches - j - 1)*sizeof(rcs->branches[0]));                                                          (rcs->nbranches - j - 1)*sizeof(rcs->branches[0]));
3013                                                  rcs->nbranches--;                                                  rcs->nbranches--;
3014                                                    rcs->nfolds++;
3015                                                  break;                                                  break;
3016                                          }                                          }
3017    
# Line 2565  Line 3037 
3037          }          }
3038  }  }
3039    
3040  void make_layout(rcsfile_t *rcs)  static void mark_subtree(branch_t *b)
3041    {
3042            int i, j;
3043            b->subtree_draw = 1;
3044            for(i = 0; i < b->nrevs; i++)
3045            {
3046                    for(j = 0; j < b->revs[i]->nbranches; j++)
3047                            mark_subtree(b->revs[i]->branches[j]);
3048            }
3049    }
3050    
3051    static void make_layout(rcsfile_t *rcs)
3052  {  {
3053          int i, j;          int i, j;
3054          int x, y;          int x, y;
# Line 2581  Line 3064 
3064                          branch_t *bp = rcs->branches[i];                          branch_t *bp = rcs->branches[i];
3065                          for(j = fr; j < bp->nrevs-1; j++)                          for(j = fr; j < bp->nrevs-1; j++)
3066                          {                          {
3067                                  if(!bp->revs[j]->ntags && !bp->revs[j]->nbranches)                                  if(!bp->revs[j]->ntags && bp->revs[j]->stripped >= 0 && !bp->revs[j]->mergetarget && !bp->revs[j]->nbranches)
3068                                  {                                  {
3069                                            bp->revs[j+1]->stripped = 1;
3070                                          memmove(&bp->revs[j], &bp->revs[j+1], (bp->nrevs-j-1) * sizeof(bp->revs[0]));                                          memmove(&bp->revs[j], &bp->revs[j+1], (bp->nrevs-j-1) * sizeof(bp->revs[0]));
3071                                          bp->nrevs--;                                          bp->nrevs--;
                                         bp->revs[j]->stripped = 1;  
3072                                          j--;                                          j--;
3073                                  }                                  }
3074                          }                          }
3075                  }                  }
3076          }          }
3077    
3078          /* Fold all empty branched in one box on the same branchpoint */          /* Find the sub-tree(s) we want to see */
3079            if(conf.branch_subtree && conf.branch_subtree[0])
3080            {
3081                    branch_t **b;
3082                    revision_t **r;
3083                    rev_t rev;
3084                    int k;
3085                    char *tag = conf.branch_subtree;
3086    
3087                    /* First translate any symbolic tag to a real branch/revision number */
3088                    if(rcs->tags)
3089                    {
3090                            for(k = 0; k < rcs->tags->ntags; k++)
3091                            {
3092                                    if(!strcmp(conf.branch_subtree, rcs->tags->tags[k]->tag))
3093                                    {
3094                                            if(rcs->tags->tags[k]->rev->isbranch)
3095                                                    tag = rcs->tags->tags[k]->rev->branch;
3096                                            else
3097                                                    tag = rcs->tags->tags[k]->rev->rev;
3098                                            break;
3099                                    }
3100                            }
3101                    }
3102    
3103                    /* Find the corresponding branch */
3104                    rev.branch = tag;
3105                    rev.rev = NULL;
3106                    rev.isbranch = 1;
3107                    b = bsearch(&rev, rcs->branches, rcs->nbranches, sizeof(rcs->branches[0]), search_branch);
3108                    if(b)
3109                    {
3110                            if((*b)->branchpoint)
3111                            {
3112                                    subtree_branch = *b;
3113                                    for(k = 0; k < (*b)->branchpoint->nbranches; k++)
3114                                            mark_subtree((*b)->branchpoint->branches[k]);
3115                            }
3116                            /*
3117                             * else -> we want everything.
3118                             * This happens for the top level branch because it has no
3119                             * branchpoint. We do not set the subtree_branch, which then
3120                             * results in drawing the whole tree as if we did not select a
3121                             * particular branch.
3122                             */
3123                    }
3124                    else
3125                    {
3126                            /* Maybe it is a revision we want all subtrees from */
3127                            rev.rev = tag;
3128                            rev.branch = NULL;
3129                            rev.isbranch = 0;
3130                            r = bsearch(&rev, rcs->srev, rcs->nsrev, sizeof(rcs->srev[0]), search_revision);
3131                            if(r)
3132                            {
3133                                    if((*r)->nbranches)
3134                                    {
3135                                            subtree_branch = (*r)->branches[0];
3136                                            subtree_rev = *r;
3137                                            for(k = 0; k < (*r)->nbranches; k++)
3138                                                    mark_subtree((*r)->branches[k]);
3139                                    }
3140                                    /*
3141                                     * else -> we select everything.
3142                                     * This happens for the any revision that has no branches.
3143                                     * We do not set the subtree_branch, which then results in
3144                                     * drawing the whole tree as if we did not select a
3145                                     * particular revision's branches.
3146                                     */
3147                            }
3148                    }
3149            }
3150    
3151            /* Fold all empty branches in one box on the same branchpoint */
3152          if(conf.branch_fold)          if(conf.branch_fold)
3153          {          {
3154                  for(i = 0; i < rcs->branches[0]->nrevs; i++)                  for(i = 0; i < rcs->branches[0]->nrevs; i++)
3155                  {                  {
3156                          if(rcs->branches[0]->revs[i]->nbranches > 1)                          if(rcs->branches[0]->revs[i]->nbranches > 0)
3157                                  fold_branch(rcs, rcs->branches[0]->revs[i]);                                  fold_branch(rcs, rcs->branches[0]->revs[i]);
3158                  }                  }
3159          }          }
# Line 2629  Line 3185 
3185                  int w;                  int w;
3186                  int h;                  int h;
3187                  rp = rcs->srev[i];                  rp = rcs->srev[i];
3188                  rp->revtext = expand_string(conf.rev_text, rcs, rp, rp->rev, NULL, rp->ntags ? rp->tags[0] : NULL);                  rp->revtext = expand_string(conf.rev_text.node ? eval_string(conf.rev_text.node, rp) : conf.rev_text.str, rcs, rp, rp->rev, NULL, rp->ntags ? rp->tags[0] : NULL);
3189                    rp->revidtext = expand_string(conf.rev_idtext.node ? eval_string(conf.rev_idtext.node, rp) : conf.rev_idtext.str, rcs, rp, rp->rev, NULL, rp->ntags ? rp->tags[0] : NULL);
3190                  w = get_swidth(rp->revtext, &conf.rev_text_font);                  w = get_swidth(rp->revtext, &conf.rev_text_font);
3191                  j = get_swidth(rp->rev->rev, &conf.rev_font);                  j = get_swidth(rp->revidtext, &conf.rev_font);
3192                  if(j > w)                  if(j > w)
3193                          w = j;                          w = j;
3194                  h = get_sheight(rp->revtext, &conf.rev_text_font) + get_sheight(rp->rev->rev, &conf.rev_font);                  h = get_sheight(rp->revtext, &conf.rev_text_font);
3195                    if(!conf.rev_hidenumber)
3196                            h += get_sheight(rp->revidtext, &conf.rev_font);
3197                  for(j = 0; j < rp->ntags; j++)                  for(j = 0; j < rp->ntags; j++)
3198                  {                  {
3199                          int ww = get_swidth(rp->tags[j]->tag, &conf.tag_font);                          int ww = get_swidth(rp->tags[j]->tag, &conf.tag_font);
# Line 2657  Line 3216 
3216                  if(!bp->nfolds)                  if(!bp->nfolds)
3217                  {                  {
3218                          w = get_swidth(bp->branch->branch, &conf.branch_font);                          w = get_swidth(bp->branch->branch, &conf.branch_font);
3219                          h = get_sheight(bp->branch->branch, &conf.branch_font);                          if(conf.rev_hidenumber)
3220                                    h = 0;
3221                            else
3222                                    h = get_sheight(bp->branch->branch, &conf.branch_font);
3223                          for(j = 0; j < bp->ntags; j++)                          for(j = 0; j < bp->ntags; j++)
3224                          {                          {
3225                                  int ww = get_swidth(bp->tags[j]->tag, &conf.branch_tag_font);                                  int ww = get_swidth(bp->tags[j]->tag, &conf.branch_tag_font);
# Line 2788  Line 3350 
3350          if(conf.auto_stretch && !conf.left_right)          if(conf.auto_stretch && !conf.left_right)
3351                  auto_stretch(rcs);                  auto_stretch(rcs);
3352    
         /* Move everything w.r.t. the top-left margin */  
         for(i = 0; i < rcs->nbranches; i++)  
                 move_branch(rcs->branches[i], conf.margin_left, conf.margin_top);  
   
3353          /* Calculate overall image size */          /* Calculate overall image size */
3354          if(conf.left_right)          if(conf.left_right)
3355          {          {
# Line 2822  Line 3380 
3380                                  for(j = 0; j < b->nrevs; j++)                                  for(j = 0; j < b->nrevs; j++)
3381                                  {                                  {
3382                                          revision_t *r = b->revs[j];                                          revision_t *r = b->revs[j];
3383                                          r->cx = x - r->cx - r->w + conf.margin_left;                                          r->cx = x - r->cx - r->w;
3384                                  }                                  }
3385                                  b->cx = x - b->cx - b->w + conf.margin_left;                                  b->cx = x - b->cx - b->w;
3386                          }                          }
3387                  }                  }
3388                  else                  else
# Line 2836  Line 3394 
3394                                  for(j = 0; j < b->nrevs; j++)                                  for(j = 0; j < b->nrevs; j++)
3395                                  {                                  {
3396                                          revision_t *r = b->revs[j];                                          revision_t *r = b->revs[j];
3397                                          r->y = y - r->y - r->h + conf.margin_top;                                          r->y = y - r->y - r->h;
3398                                  }                                  }
3399                                  b->y = y - b->y - b->h + conf.margin_top;                                  b->y = y - b->y - b->h;
3400                          }                          }
3401                  }                  }
3402          }          }
3403    
3404            /* Relocate the lot if we only draw a sub-tree */
3405            if(subtree_branch)
3406            {
3407                    int xx, yy;
3408    
3409                    if(subtree_branch->folded)      /* Fix the reference if the branch got folded */
3410                            subtree_branch = subtree_branch->folded_to;
3411    
3412                    xx = conf.left_right ? subtree_branch->cx : subtree_branch->cx - subtree_branch->tw/2;
3413                    yy = conf.left_right ? subtree_branch->y - subtree_branch->th/2 : subtree_branch->y;
3414                    if(subtree_branch != rcs->branches[0])
3415                    {
3416                            if(conf.left_right)
3417                                    xx -= conf.branch_connect;
3418                            else
3419                                    yy -= conf.branch_connect;
3420                    }
3421                    for(i = 0; i < rcs->nbranches; i++)
3422                            move_branch(rcs->branches[i], -xx, -yy);
3423            }
3424    
3425            /* Move everything w.r.t. the top-left margin */
3426            for(i = 0; i < rcs->nbranches; i++)
3427                    move_branch(rcs->branches[i], conf.margin_left, conf.margin_top);
3428  }  }
3429    
3430  /*  /*
# Line 2849  Line 3432 
3432   * Imagemap functions   * Imagemap functions
3433   **************************************************************************   **************************************************************************
3434   */   */
3435  void make_imagemap(rcsfile_t *rcs, FILE *fp, gdImagePtr im)  static void map_merge_box(rcsfile_t *rcs, FILE *fp, revision_t *fr, revision_t *tr, gdImagePtr im, int x1, int y1, int x2, int y2)
3436    {
3437            char *href = expand_string(conf.map_merge_href, rcs, tr, tr->rev, fr->rev, NULL);
3438            char *alt = expand_string(conf.map_merge_alt, rcs, tr, tr->rev, fr->rev, NULL);
3439            const char *htp = conf.html_level == HTMLLEVEL_X ? " /" : "";
3440    
3441            if(x1 > 0 && x2 > 0 && y1 > 0 && y2 > 0)
3442                    fprintf(fp, "\t<area shape=\"rect\" %s coords=\"%d,%d,%d,%d\" %s%s>\n",
3443                                            href, x1, y1, x2, y2, alt, htp);
3444            xfree(alt);
3445            xfree(href);
3446    
3447            if(im)
3448            {
3449                    gdImageFilledRectangle(im, x1-2, y1-2, x1+2, y1+2, clr(im, "title_color", NULL, NULL, 0)->id);
3450                    gdImageFilledRectangle(im, x2-2, y2-2, x2+2, y2+2, clr(im, "tag_color.id", NULL, NULL, 0)->id);
3451                    gdImageLine(im, x1, y1, x2, y2, clr(im, "title_color", NULL, NULL, 0)->id);
3452            }
3453    }
3454    
3455    static void map_merges(rcsfile_t *rcs, FILE *fp, gdImagePtr im)
3456    {
3457            int i;
3458            int tagh2 = get_sheight("Hg", &conf.tag_font) / 2;
3459            int bm = conf.branch_margin / 2;
3460    
3461            for(i = 0; i < rcs->nmerges; i++)
3462            {
3463                    revision_t *fr;
3464                    revision_t *tr;
3465                    int x1, x2, y1, y2;             /* Edge position on revision box */
3466                    int sx1, sx2, sy1, sy2;         /* Direction for mergeline -1 = left/up, +1 = right/down */
3467                    switch(rcs->merges[i].type)
3468                    {
3469                    case TR_TAG:
3470                            fr = rcs->merges[i].from.tag->logrev;
3471                            tr = rcs->merges[i].to.tag->logrev;
3472                            break;
3473                    case TR_REVISION:
3474                            fr = rcs->merges[i].from.rev;
3475                            tr = rcs->merges[i].to.rev;
3476                            break;
3477                    default:
3478                            continue;
3479                    }
3480                    if(!fr || !tr || fr == tr)
3481                            continue;       /* This can happen with detached tags and self-references */
3482    
3483                    calc_merge_coords(&rcs->merges[i], fr, tr, &x1, &x2, &y1, &y2, &sx1, &sx2, &sy1, &sy2);
3484    
3485                    if(conf.left_right && !conf.merge_on_tag)
3486                    {
3487                            if(fr->branch == tr->branch)
3488                            {
3489                                    map_merge_box(rcs, fp, fr, tr, im, x1-bm, y1-bm, x1+bm, y1);
3490                                    map_merge_box(rcs, fp, fr, tr, im, x2-bm, y2-bm, x2+bm, y2);
3491                            }
3492                            else
3493                            {
3494                                    if(sy1 < 0)
3495                                            map_merge_box(rcs, fp, fr, tr, im, x1-bm, y1-bm, x1+bm, y1);
3496                                    else
3497                                            map_merge_box(rcs, fp, fr, tr, im, x1-bm, y1, x1+bm, y1+bm);
3498                                    if(sy2 < 0)
3499                                            map_merge_box(rcs, fp, fr, tr, im, x2-bm, y2-bm, x2+bm, y2);
3500                                    else
3501                                            map_merge_box(rcs, fp, fr, tr, im, x2-bm, y2, x2+bm, y2+bm);
3502                            }
3503                    }
3504                    else
3505                    {
3506                            if(fr->branch == tr->branch)
3507                            {
3508                                    map_merge_box(rcs, fp, fr, tr, im, x1-bm, y1-tagh2, x1, y1+tagh2);
3509                                    map_merge_box(rcs, fp, fr, tr, im, x2-bm, y2-tagh2, x2, y2+tagh2);
3510                            }
3511                            else
3512                            {
3513                                    if(sx1 < 0)
3514                                            map_merge_box(rcs, fp, fr, tr, im, x1-bm, y1-tagh2, x1, y1+tagh2);
3515                                    else
3516                                            map_merge_box(rcs, fp, fr, tr, im, x1, y1-tagh2, x1+bm, y1+tagh2);
3517                                    if(sx2 < 0)
3518                                            map_merge_box(rcs, fp, fr, tr, im, x2-bm, y2-tagh2, x2, y2+tagh2);
3519                                    else
3520                                            map_merge_box(rcs, fp, fr, tr, im, x2, y2-tagh2, x2+bm, y2+tagh2);
3521                            }
3522                    }
3523            }
3524    }
3525    
3526    static void make_imagemap(rcsfile_t *rcs, FILE *fp, gdImagePtr im)
3527  {  {
3528          int i, j;          int i, j;
3529          const char *htp = conf.html_level == HTMLLEVEL_X ? " /" : "";          const char *htp = conf.html_level == HTMLLEVEL_X ? " /" : "";
# Line 2870  Line 3544 
3544          {          {
3545                  branch_t *b = rcs->branches[i];                  branch_t *b = rcs->branches[i];
3546                  tag_t *tag = b->ntags ? b->tags[0] : NULL;                  tag_t *tag = b->ntags ? b->tags[0] : NULL;
3547                  char *bhref = expand_string(conf.map_branch_href, rcs, NULL, b->branch, NULL, tag);                  char *bhref;
3548                  char *balt = expand_string(conf.map_branch_alt, rcs, NULL, b->branch, NULL, tag);                  char *balt;
3549                  int x1;                  int x1;
3550                  int x2;                  int x2;
3551                  int y1;                  int y1;
3552                  int y2;                  int y2;
3553    
3554                    if(subtree_branch && !b->subtree_draw)
3555                            continue;
3556    
3557                    bhref = expand_string(conf.map_branch_href, rcs, NULL, b->branch, NULL, tag);
3558                    balt = expand_string(conf.map_branch_alt, rcs, NULL, b->branch, NULL, tag);
3559    
3560                  if(!b->nfolds)                  if(!b->nfolds)
3561                  {                  {
3562                          if(conf.left_right)                          if(conf.left_right)
# Line 2897  Line 3577 
3577                                          bhref, x1, y1, x2, y2, balt, htp);                                          bhref, x1, y1, x2, y2, balt, htp);
3578                          if(im)                          if(im)
3579                          {                          {
3580                                  gdImageFilledRectangle(im, x1-2, y1-2, x1+2, y1+2, conf.title_color.id);                                  gdImageFilledRectangle(im, x1-2, y1-2, x1+2, y1+2, clr(im, "title_color", NULL, NULL, 0)->id);
3581                                  gdImageFilledRectangle(im, x2-2, y2-2, x2+2, y2+2, conf.tag_color.id);                                  gdImageFilledRectangle(im, x2-2, y2-2, x2+2, y2+2, clr(im, "tag_color", NULL, NULL, 0)->id);
3582                                  gdImageLine(im, x1, y1, x2, y2, conf.title_color.id);                                  gdImageLine(im, x1, y1, x2, y2, clr(im, "title_color", NULL, NULL, 0)->id);
3583                          }                          }
3584                  }                  }
3585                  else                  else
# Line 2974  Line 3654 
3654                                  href, x1, y1, x2, y2, alt, htp);                                  href, x1, y1, x2, y2, alt, htp);
3655                          if(im)                          if(im)
3656                          {                          {
3657                                  gdImageFilledRectangle(im, x1-2, y1-2, x1+2, y1+2, conf.title_color.id);                                  gdImageFilledRectangle(im, x1-2, y1-2, x1+2, y1+2, clr(im, "title_color", NULL, NULL, 0)->id);
3658                                  gdImageFilledRectangle(im, x2-2, y2-2, x2+2, y2+2, conf.tag_color.id);                                  gdImageFilledRectangle(im, x2-2, y2-2, x2+2, y2+2, clr(im, "tag_color", NULL, NULL, 0)->id);
3659                                  gdImageLine(im, x1, y1, x2, y2, conf.title_color.id);                                  gdImageLine(im, x1, y1, x2, y2, clr(im, "title_color", NULL, NULL, 0)->id);
3660                          }                          }
3661                          xfree(href);                          xfree(href);
3662                          xfree(alt);                          xfree(alt);
# Line 3044  Line 3724 
3724                                          alt, htp);                                          alt, htp);
3725                                  if(im)                                  if(im)
3726                                  {                                  {
3727                                          gdImageFilledRectangle(im, x1-2, y1-2, x1+2, y1+2, conf.title_color.id);                                          gdImageFilledRectangle(im, x1-2, y1-2, x1+2, y1+2, clr(im, "title_color", NULL, NULL, 0)->id);
3728                                          gdImageFilledRectangle(im, x2-2, y2-2, x2+2, y2+2, conf.tag_color.id);                                          gdImageFilledRectangle(im, x2-2, y2-2, x2+2, y2+2, clr(im, "tag_color", NULL, NULL, 0)->id);
3729                                          gdImageLine(im, x1, y1, x2, y2, conf.title_color.id);                                          gdImageLine(im, x1, y1, x2, y2, clr(im, "title_color", NULL, NULL, 0)->id);
3730                                  }                                  }
3731                                  xfree(href);                                  xfree(href);
3732                                  xfree(alt);                                  xfree(alt);
# Line 3072  Line 3752 
3752                                          bhref, x1, y1, x2, y2, balt, htp);                                          bhref, x1, y1, x2, y2, balt, htp);
3753                          if(im)                          if(im)
3754                          {                          {
3755                                  gdImageFilledRectangle(im, x1-2, y1-2, x1+2, y1+2, conf.title_color.id);                                  gdImageFilledRectangle(im, x1-2, y1-2, x1+2, y1+2, clr(im, "title_color", NULL, NULL, 0)->id);
3756                                  gdImageFilledRectangle(im, x2-2, y2-2, x2+2, y2+2, conf.tag_color.id);                                  gdImageFilledRectangle(im, x2-2, y2-2, x2+2, y2+2, clr(im, "tag_color", NULL, NULL, 0)->id);
3757                                  gdImageLine(im, x1, y1, x2, y2, conf.title_color.id);                                  gdImageLine(im, x1, y1, x2, y2, clr(im, "title_color", NULL, NULL, 0)->id);
3758                          }                          }
3759                  }                  }
3760                  xfree(bhref);                  xfree(bhref);
3761                  xfree(balt);                  xfree(balt);
3762          }          }
3763    
3764            map_merges(rcs, fp, im);
3765    
3766          fprintf(fp, "</map>\n");          fprintf(fp, "</map>\n");
3767  }  }
3768    
# Line 3111  Line 3794 
3794          "  -[0-9] <txt> Use <txt> for expansion\n"          "  -[0-9] <txt> Use <txt> for expansion\n"
3795          ;          ;
3796    
3797  #define VERSION_STR     "1.4.2"  #define VERSION_STR     VERSION
3798  #define NOTICE_STR      "Copyright (c) 2001,2002,2003,2004 B.Stultiens"  #define NOTICE_STR      "Copyright (c) 2001-2008 B.Stultiens"
3799    
3800  static void append_slash(char **path)  static void append_slash(char **path)
3801  {  {
# Line 3128  Line 3811 
3811    
3812  int main(int argc, char *argv[])  int main(int argc, char *argv[])
3813  {  {
3814          extern int rcs_flex_debug;          extern int yy_flex_debug;
3815          extern int rcsdebug;          extern int yydebug;
3816          int optc;          int optc;
3817          char *confpath = NULL;          char *confpath = NULL;
3818          char *outfile = NULL;          char *outfile = NULL;
# Line 3243  Line 3926 
3926                  setvbuf(stdout, NULL, 0, _IONBF);                  setvbuf(stdout, NULL, 0, _IONBF);
3927                  setvbuf(stderr, NULL, 0, _IONBF);                  setvbuf(stderr, NULL, 0, _IONBF);
3928          }          }
3929          rcs_flex_debug = (debuglevel & DEBUG_RCS_LEX) != 0;          yy_flex_debug = (debuglevel & DEBUG_RCS_LEX) != 0;
3930          rcsdebug = (debuglevel & DEBUG_RCS_YACC) != 0;          yydebug = (debuglevel & DEBUG_RCS_YACC) != 0;
3931    
3932          /* Set defaults */          /* Set defaults */
3933          conf.tag_font.gdfont            = gdFontTiny;          conf.tag_font.gdfont            = gdFontTiny;
# Line 3253  Line 3936 
3936          conf.branch_tag_font.gdfont     = gdFontTiny;          conf.branch_tag_font.gdfont     = gdFontTiny;
3937          conf.title_font.gdfont          = gdFontTiny;          conf.title_font.gdfont          = gdFontTiny;
3938          conf.rev_text_font.gdfont       = gdFontTiny;          conf.rev_text_font.gdfont       = gdFontTiny;
3939            conf.msg_font.gdfont            = gdFontTiny;
3940    
3941          conf.anti_alias         = 1;          conf.anti_alias         = 1;
3942          conf.thick_lines        = 1;          conf.thick_lines        = 1;
# Line 3269  Line 3953 
3953          conf.map_rev_alt        = xstrdup("alt=\"%R\"");          conf.map_rev_alt        = xstrdup("alt=\"%R\"");
3954          conf.map_diff_href      = xstrdup("href=\"unset: conf.map_diff_href\"");          conf.map_diff_href      = xstrdup("href=\"unset: conf.map_diff_href\"");
3955          conf.map_diff_alt       = xstrdup("alt=\"%P &lt;-&gt; %R\"");          conf.map_diff_alt       = xstrdup("alt=\"%P &lt;-&gt; %R\"");
3956          conf.rev_text           = xstrdup("%d");          conf.map_merge_href     = xstrdup("href=\"unset: conf.map_merge_href\"");
3957          conf.merge_from         = xstrdup("");          conf.map_merge_alt      = xstrdup("alt=\"%P &lt;-&gt; %R\"");
3958          conf.merge_to           = xstrdup("");          conf.rev_text.str       = xstrdup("%d");
3959            conf.rev_idtext.str     = xstrdup("%R");
3960            conf.branch_subtree     = xstrdup("");
3961            conf.tag_ignore         = xstrdup("");
3962            conf.merge_from.n       = 0;
3963            conf.merge_from.strs    = NULL;
3964            conf.merge_to.n         = 0;
3965            conf.merge_to.strs      = NULL;
3966          conf.merge_arrows       = 1;          conf.merge_arrows       = 1;
3967            conf.merge_cvsnt        = 1;
3968          conf.arrow_width        = ARROW_WIDTH;          conf.arrow_width        = ARROW_WIDTH;
3969          conf.arrow_length       = ARROW_LENGTH;          conf.arrow_length       = ARROW_LENGTH;
3970    
# Line 3282  Line 3974 
3974          conf.branch_tag_color   = black_color;          conf.branch_tag_color   = black_color;
3975          conf.rev_color          = black_color;          conf.rev_color          = black_color;
3976          conf.rev_bgcolor        = white_color;          conf.rev_bgcolor        = white_color;
3977          conf.merge_color        = black_color;          conf.merge_color.n      = 0;
3978            conf.merge_color.clrs   = NULL;
3979            conf.merge_cvsnt_color  = black_color;
3980          conf.tag_color          = black_color;          conf.tag_color          = black_color;
3981          conf.title_color        = black_color;          conf.title_color        = black_color;
3982          conf.rev_text_color     = black_color;          conf.rev_text_color     = black_color;
3983            conf.msg_color          = black_color;
3984    
3985          conf.image_quality      = 100;          conf.image_quality      = 100;
3986            conf.image_compress     = -1;   /* Use default zlib setting */
3987          conf.rev_maxline        = -1;   /* Checked later to set to default */          conf.rev_maxline        = -1;   /* Checked later to set to default */
3988    
3989          read_config(confpath);          read_config(confpath);
# Line 3307  Line 4003 
4003    
4004          if(conf.rev_minline >= conf.rev_maxline)          if(conf.rev_minline >= conf.rev_maxline)
4005          {          {
4006                  if(conf.auto_stretch && !quiet)                  if(conf.auto_stretch)
4007                          fprintf(stderr, "Auto stretch is only possible if rev_minline < rev_maxline\n");                          stack_msg(MSG_WARN, "Auto stretch is only possible if rev_minline < rev_maxline");
4008                  conf.auto_stretch = 0;                  conf.auto_stretch = 0;
4009          }          }
4010    
# Line 3317  Line 4013 
4013          if(conf.thick_lines > 11)          if(conf.thick_lines > 11)
4014                  conf.thick_lines = 11;                  conf.thick_lines = 11;
4015    
4016            if(conf.image_quality < 0 || conf.image_quality > 100)
4017            {
4018                    stack_msg(MSG_WARN, "JPEG quality (image_quality) must be between 0 and 100");
4019                    conf.image_quality = 100;
4020            }
4021    
4022            if(conf.image_compress < -1 || conf.image_compress > 9)
4023            {
4024                    stack_msg(MSG_WARN, "PNG compression (image_compress) must be between -1 and 9");
4025                    conf.image_compress = -1;
4026            }
4027    
4028          append_slash(&conf.cvsroot);          append_slash(&conf.cvsroot);
4029          append_slash(&conf.cvsmodule);          append_slash(&conf.cvsmodule);
4030    
# Line 3347  Line 4055 
4055    
4056          assign_tags(rcs);          assign_tags(rcs);
4057          find_merges(rcs);          find_merges(rcs);
4058            find_merges_cvsnt(rcs);
4059    
4060          if(outfile)          if(outfile)
4061          {          {
# Line 3375  Line 4084 
4084          {          {
4085                  /* Create an image */                  /* Create an image */
4086                  im = make_image(rcs);                  im = make_image(rcs);
4087    
4088                    if(conf.image_interlace)
4089                            gdImageInterlace(im, 1);
4090    
4091  #ifdef DEBUG_IMAGEMAP  #ifdef DEBUG_IMAGEMAP
4092                  {                  {
4093                          FILE *nulfile = fopen("/dev/null", "w");                          FILE *nulfile = fopen("/dev/null", "w");
# Line 3384  Line 4097 
4097  #endif  #endif
4098                  switch(conf.image_type)                  switch(conf.image_type)
4099                  {                  {
4100  #ifdef HAVE_IMAGE_GIF  #ifdef HAVE_GD_GIF
4101  # ifndef HAVE_IMAGE_PNG  # ifndef HAVE_GD_PNG
4102                  default:                  default:
4103  # endif  # endif
4104                  case IMAGE_GIF:                  case IMAGE_GIF:
4105                          gdImageGif(im, fp);                          gdImageGif(im, fp);
4106                          break;                          break;
4107  #endif  #endif
4108  #ifdef HAVE_IMAGE_PNG  #ifdef HAVE_GD_PNG
4109                  default:                  default:
4110                  case IMAGE_PNG:                  case IMAGE_PNG:
4111    #ifdef HAVE_GD_PNGEX
4112                            gdImagePngEx(im, fp, conf.image_compress);
4113    #else
4114                          gdImagePng(im, fp);                          gdImagePng(im, fp);
4115    #endif
4116                          break;                          break;
4117  #endif  #endif
4118  #ifdef HAVE_IMAGE_JPEG  #ifdef HAVE_GD_JPEG
4119  # if !defined(HAVE_IMAGE_GIF) && !defined(HAVE_IMAGE_PNG)  # if !defined(HAVE_GD_GIF) && !defined(HAVE_GD_PNG)
4120                  default:                  default:
4121  # endif  # endif
4122                  case IMAGE_JPEG:                  case IMAGE_JPEG:

Legend:
Removed from v.1.43  
changed lines
  Added in v.1.65

  ViewVC Help
Powered by ViewVC 1.1.0 with CvsGraph 1.7.0