/[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.6, Mon Feb 26 00:09:20 2001 UTC revision 1.7, Sun Mar 4 01:38:23 2001 UTC
# Line 19  Line 19 
19   * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA   * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
20   */   */
21    
 /*  
  * Approx. layout of a cvs/rcs log:  
  *  
  * ws           ::= "[ \t]*"  
  * rev_nr       ::= "[:digit:]+(\.[:digit:]+)*"  
  * path_name    ::= "/?(([^\n/]*)/)+"  
  * file_name    ::= "[^\n]+"  
  * file_path    ::= "{file_path}{file_name}"  
  * tag          ::= "[^,.$@:;\0-\037]+"  
  * number       ::= "[:digit:]+"  
  * separator    ::= "(-{28})|(={78)\n"  
  *  
  * The header is identified with this snippet until  
  * a {separator} is encountered:  
  *      "RCS file:{ws}{file_path}"  
  *      "Working file:{ws}{file_name}"  
  *      "head:{ws}{rev_nr}"  
  *      "branch:{ws}{rev_nr}?"  
  *      "locks:{ws}[^\n]*"  
  *      "access list:{ws}[^\n]*"  
  *      "symbolic names:"  
  *      "(\t{tag}:{rev_nr}\n)*"  
  *      "keyword substitution:{ws}[^\n]*"  
  *      "total revisions:{ws}{number};{ws}selected revisions:{ws}{number}"  
  *      "description:"  
  *      "<any text you can imagine until a separator>"  
  *  
  * Each revision is identiefied with:  
  *      "{separator}"  
  *      "revision{ws}{rev_nr}"  
  *      "date: 2001/02/15 20:17:37;  author: bertho;  state: Exp;  lines: +2 -0  
  *      "any text as a comment until a separator>"  
  *  
  * The last revision has the "={78}" separator. Eventually, a next file may be  
  * appended.  
  */  
   
22  #include <stdio.h>  #include <stdio.h>
23  #include <stdlib.h>  #include <stdlib.h>
24  #include <unistd.h>  #include <unistd.h>
# Line 76  Line 39 
39  #include "cvsgraph.h"  #include "cvsgraph.h"
40  #include "utils.h"  #include "utils.h"
41  #include "readconf.h"  #include "readconf.h"
42    #include "rcs.h"
43    
44  #if !defined(HAVE_IMAGE_GIF) && !defined(HAVE_IMAGE_PNG) && !defined(HAVE_IMAGE_JPEG)  #if !defined(HAVE_IMAGE_GIF) && !defined(HAVE_IMAGE_PNG) && !defined(HAVE_IMAGE_JPEG)
45  # error No image output format available. Check libgd  # error No image output format available. Check libgd
# Line 84  Line 48 
48    
49  /*#define DEBUG         1*/  /*#define DEBUG         1*/
50    
 #define RLOGCMD         "/usr/bin/rlog"  
 #define DEVNULL         "/dev/null"  
51  #define CONFFILENAME    "cvsgraph.conf"  #define CONFFILENAME    "cvsgraph.conf"
52    
53  #ifndef ETCDIR  #ifndef ETCDIR
# Line 109  Line 71 
71  #define ALIGN_VB        0x20  #define ALIGN_VB        0x20
72  #define ALIGN_VX        0xf0  #define ALIGN_VX        0xf0
73    
 typedef struct __revid_t  
 {  
         char    *branch;  
         char    *rev;  
         int     isbranch;  
 } revid_t;  
   
 typedef struct __tag_t  
 {  
         char    *tag;  
         revid_t *rev;  
 } tag_t;  
   
 struct __branch_t;  
   
 typedef struct __revision_t  
 {  
         revid_t         *rev;  
         char            *info;  
         char            *comment;  
         tag_t           **tags;  
         int             ntags;  
         struct __branch_t       **branches;  
         int             nbranches;  
         int             w, h;  
         int             x, y;  
 } revision_t;  
   
 typedef struct __branch_t  
 {  
         char            *branch;  
         revision_t      *branchpoint;  
         tag_t           *tag;  
         revision_t      **revs;  
         int             nrevs;  
         int             tw, th;  
         int             w, h;  
         int             x, y;  
 } branch_t;  
   
 typedef struct __rcsfilelog_t  
 {  
         char            *path;  
         char            *name;  
         revid_t         *head;  
         char            *branch;  
         char            *locks;  
         char            *access;  
         char            *keyword;  
         char            *totalrevs;  
         char            *comment;  
         tag_t           **tags;  
         int             ntags;  
         revision_t      **revs;  
         int             nrevs;  
         branch_t        **branches;  
         int             nbranches;  
         int             tw, th;  
 } rcsfilelog_t;  
   
74  /*  /*
75   **************************************************************************   **************************************************************************
76   * Globals   * Globals
77   **************************************************************************   **************************************************************************
78   */   */
79    
 char *rlogcmd = RLOGCMD;  
 char *devnull = DEVNULL;  
   
80  config_t conf;  config_t conf;
81    int debuglevel;
82    
83  /*  /*
84   **************************************************************************   **************************************************************************
85   * Debug routines   * Dubug routines
86   **************************************************************************   **************************************************************************
87   */   */
88  #ifdef DEBUG  void dump_rev(char *p, rev_t *r)
 void dump_revid(const char *s, revid_t *r)  
89  {  {
90          fprintf(stderr, "%s.branch  : '%s'\n", s, r->branch);          printf("%s", p);
91          fprintf(stderr, "%s.rev     : '%s'\n", s, r->rev);          if(r)
92          fprintf(stderr, "%s.isbranch: %d\n", s, r->isbranch);                  printf("'%s', '%s', %d\n", r->rev, r->branch, r->isbranch);
93  }          else
94                    printf("<null>\n");
 void dump_tag(const char *s, tag_t *t)  
 {  
         fprintf(stderr, "%s", s);  
         dump_revid(t->tag, t->rev);  
 }  
   
 void dump_rev(revision_t *r)  
 {  
         int i;  
         dump_revid("Revision", r->rev);  
         fprintf(stderr, "Revision.Info   : '%s'\n", r->info);  
         fprintf(stderr, "Revision.Comment: '%s'\n", r->comment);  
         for(i = 0; i < r->ntags; i++)  
                 dump_tag("Revision.Tag: ", r->tags[i]);  
 }  
   
 void dump_branch(branch_t *b)  
 {  
         int i;  
         fprintf(stderr, "Branch: '%s'\n", b->branch);  
         if(b->tag)  
                 dump_tag("branchtag:", b->tag);  
         for(i = 0; i < b->nrevs; i++)  
                 fprintf(stderr, "Branch.Rev: '%s'\n", b->revs[i]->rev->rev);  
95  }  }
96    
97  void dump_log(rcsfilelog_t *r)  void dump_id(char *p, char *d)
98  {  {
99          int i;          printf("%s", p);
100            if(d)
101          fprintf(stderr, "Path   : '%s'\n", r->path);                  printf("'%s'\n", d);
102          fprintf(stderr, "Name   : '%s'\n", r->name);          else
103          dump_revid("Head", r->head);                  printf("<null>\n");
         fprintf(stderr, "Branch : '%s'\n", r->branch);  
         fprintf(stderr, "Locks  : '%s'\n", r->locks);  
         fprintf(stderr, "Access : '%s'\n", r->access);  
         fprintf(stderr, "Keyword: '%s'\n", r->keyword);  
         fprintf(stderr, "Total  : '%s'\n", r->totalrevs);  
         fprintf(stderr, "Comment: '%s'\n", r->comment);  
         for(i = 0; i < r->ntags; i++)  
                 dump_tag("", r->tags[i]);  
         for(i = 0; i < r->nrevs; i++)  
                 dump_rev(r->revs[i]);  
         for(i = 0; i < r->nbranches; i++)  
                 dump_branch(r->branches[i]);  
104  }  }
 #endif  
105    
106  /*  void dump_idrev(char *p, idrev_t *t)
  **************************************************************************  
  * Retrieve the log entries  
  **************************************************************************  
  */  
 FILE *get_log(const char *cvsroot, const char *module, const char *file)  
107  {  {
108          pid_t pid;          printf("%s", p);
109          int nul;          if(t)
         FILE *tmp;  
         char *cmd = NULL;  
         int status;  
         mode_t um;  
   
         if((nul = open(devnull, O_RDWR, S_IRUSR|S_IWUSR)) == -1)  
                 return NULL;  
   
         um = umask(0177);       /* Set tempfiles to max 0600 permissions */  
         if((tmp = tmpfile()) == NULL)  
         {  
                 close(nul);  
                 return NULL;  
         }  
         umask(um);  
   
         cmd = xmalloc(strlen(cvsroot) + strlen(module) + strlen(file) + 2 + 1);  
         sprintf(cmd, "%s/%s/%s", cvsroot, module, file);  
   
         switch(pid = fork())  
110          {          {
111          case -1:        /* Error */                  printf("'%s' -> ", t->id);
112                  close(nul);                  dump_rev("", t->rev);
                 fclose(tmp);  
                 xfree(cmd);  
                 return NULL;  
         case 0:         /* Child */  
                 if((dup2(nul, STDIN_FILENO)) == -1)     exit(126);  
                 if((dup2(fileno(tmp), STDOUT_FILENO)) == -1)    exit(126);  
                 if((dup2(nul, STDERR_FILENO)) == -1)    exit(126);  
                 close(nul);  
                 fclose(tmp);  
                 execl(rlogcmd, rlogcmd, cmd, NULL);  
                 exit(127);  
                 break;  
         default:        /* Parent */  
                 close(nul);  
                 xfree(cmd);  
                 while(1)  
                 {  
                         if(waitpid(pid, &status, 0) == -1)  
                         {  
                                 if(errno != EINTR)  
                                 {  
                                         fclose(tmp);  
                                         return NULL;  
                                 }  
                         }  
                         else  
                                 break;  
                 }  
                 break;  
         }  
   
         if(WIFEXITED(status) && WEXITSTATUS(status) == 0)  
         {  
                 if(fseek(tmp, 0, SEEK_SET) != (off_t)-1)  
                 {  
                         return tmp;  
                 }  
                 else  
                 {  
                         fclose(tmp);  
                         return NULL;  
                 }  
113          }          }
114          else          else
115                  fclose(tmp);                  printf("<null>\n");
         return NULL;  
116  }  }
117    
118  /*  void dump_tag(char *p, tag_t *t)
  **************************************************************************  
  * Parse the log entries  
  **************************************************************************  
  */  
 char *strip_dup(const char *s)  
119  {  {
120          int l = strlen(s);          printf("%s", p);
121          char *c = xmalloc(l+1);          if(t)
   
         strcpy(c, s);  
         while(*c == ' ' || *c == '\t')  
122          {          {
123                  memmove(c, c+1, l--);                  printf("'%s' -> ", t->tag);
124                    dump_rev("", t->rev);
125          }          }
126          while(l && strchr(" \t\r\n", c[l]))          else
127                  c[l--] = '\0';                  printf("<null>\n");
         return c;  
128  }  }
129    
130  revid_t *make_revid(const char *s)  void dump_delta(char *p, delta_t *d)
131  {  {
132          char *c = strip_dup(s);          int i;
133          char *cptr;          printf("%sdelta.rev   : ", p);
134          int dots = 0;          dump_rev("", d->rev);
135          revid_t *r = xmalloc(sizeof(*r));          printf("%sdelta.date  : %s\n", p, d->date);
136          for(cptr = c; *cptr; cptr++)          printf("%sdelta.author: %s\n", p, d->author);
137          {          printf("%sdelta.state : %s\n", p, d->state);
138                  if(*cptr == '.')          for(i = 0; d->branches && i < d->branches->nrevs; i++)
                         dots++;  
         }  
         if(!dots)  
         {  
                 r->rev = xstrdup("");  
                 r->branch = xstrdup(s);  
                 r->isbranch = 1;  
         }  
         else if(!*c)  
         {  
                 r->rev = xstrdup("?.?");  
                 r->branch = xstrdup("?");  
         }  
         else if(dots & 1)  
         {  
                 char *t;  
                 r->rev = c;  
                 r->branch = xstrdup(c);  
                 cptr = strrchr(r->branch, '.');  
                 assert(cptr != NULL);  
                 *cptr = '\0';  
                 t = strrchr(r->branch, '.');  
                 if((t&& !strcmp(t+1, "0")) || (!t && !strcmp(r->branch, "0")))  
                 {  
                         /* Magic branch numbers "x.x.0.x" */  
                         r->isbranch = 1;  
                         if(t)  
                                 strcpy(t+1, cptr+1);  
                         else  
                                 strcpy(r->branch, cptr+1);  
                 }  
         }  
         else  
139          {          {
140                  r->isbranch = 1;                  printf("%sdelta.branch: ", p);
141                  r->branch = c;                  dump_rev("", d->branches->revs[i]);
                 r->rev = xmalloc(strlen(c) + 3);  
                 strcpy(r->rev, c);  
                 strcat(r->rev, ".?");  
142          }          }
143          return r;          printf("%sdelta.next  : ", p);
144            dump_rev("", d->next);
145            printf("\n");
146  }  }
147    
148  char *add_comment(char *c, const char *n)  void dump_dtext(char *p, dtext_t *d)
149  {  {
150          int l;          printf("%sdtext.rev  : ", p);
151          char *r;          dump_rev("", d->rev);
152          assert(n != NULL);          printf("%sdtext.log  : %d bytes\n", p, d->log ? strlen(d->log) : -1);
153          l = strlen(n);          printf("%sdtext.text : %d bytes\n", p, d->text ? strlen(d->text) : -1);
154          if(!c)          printf("\n");
         {  
                 r = xmalloc(l+1);  
                 strcpy(r, n);  
         }  
         else  
         {  
                 r = xmalloc(l+strlen(c)+1+1);  
                 strcpy(r, c);  
                 strcat(r, "\n");  
                 strcat(r, n);  
         }  
         return r;  
155  }  }
156    
157  int get_line(FILE *fp, char *buf, int maxlen)  void dump_rcsfile(rcsfile_t *rcs)
158  {  {
159          int n;          int i;
160          int seennl;          printf("root   : '%s'\n", rcs->root);
161  retry:          printf("module : '%s'\n", rcs->module);
162          seennl = 0;          printf("file   : '%s'\n", rcs->file);
163          if(!fgets(buf, maxlen, fp))          dump_rev("head   : ", rcs->head);
164                  return feof(fp) ? 0 : -1;          dump_rev("branch : ", rcs->branch);
165          n = strlen(buf);          printf("access :\n");
166          while(n && buf[n-1] == '\n')          for(i = 0; rcs->access && i < rcs->access->nids; i++)
167          {                  dump_id("\t", rcs->access->ids[i]);
168                  seennl = 1;          printf("tags   :\n");
169                  buf[--n] = '\0';          for(i = 0; rcs->tags && i < rcs->tags->ntags; i++)
170          }                  dump_tag("\t", rcs->tags->tags[i]);
171          if(!n)          printf("locks  :%s\n", rcs->strict ? " (strict)" : "");
172                  goto retry;          for(i = 0; rcs->locks && i < rcs->locks->nidrevs; i++)
173          if(!seennl)                  dump_idrev("\t", rcs->locks->idrevs[i]);
174          {          printf("comment: '%s'\n", rcs->comment);
175                  while(fgetc(fp) != '\n')          printf("expand : '%s'\n", rcs->expand ? rcs->expand : "(default -kv)");
176                          ;          printf("deltas :\n");
177          }          for(i = 0; rcs->deltas && i < rcs->deltas->ndeltas; i++)
178          return n;                  dump_delta("\t", rcs->deltas->deltas[i]);
179            printf("desc   : '%s'\n", rcs->desc);
180            printf("dtexts :\n");
181            for(i = 0; rcs->dtexts && i < rcs->dtexts->ndtexts; i++)
182                    dump_dtext("\t", rcs->dtexts->dtexts[i]);
183    
184            fflush(stdout);
185  }  }
186    
187  rcsfilelog_t *parse_log(FILE *fp)  /*
188     **************************************************************************
189     * Read the rcs file
190     **************************************************************************
191     */
192    rcsfile_t *get_rcsfile(const char *cvsroot, const char *module, const char *file)
193  {  {
194          rcsfilelog_t *p;          char *cmd = NULL;
195          int state = 0;          int rv;
         regex_t rerev;  
         regex_t reinfo;  
196    
197          if(regcomp(&rerev, "^revision[ \\t]*[0-9]+(\\.[0-9]+)*", REG_EXTENDED))          cmd = xmalloc(strlen(cvsroot) + strlen(module) + strlen(file) + 2 + 1);
198            sprintf(cmd, "%s/%s/%s", cvsroot, module, file);
199            if(!(rcsin = fopen(cmd, "r")))
200                  return NULL;                  return NULL;
201          if(regcomp(&reinfo, "^date:[^;]*;[ \\t]*author:[^;]*;[ \\t]+state:", REG_EXTENDED))          input_file = cmd;
202          {          line_number = 1;
203                  regfree(&rerev);          rv = rcsparse();
204            fclose(rcsin);
205            if(rv)
206                  return NULL;                  return NULL;
207          }          xfree(cmd);
208          p = xmalloc(sizeof(*p));          input_file = NULL;
209          while(state != 4)          rcsfile->root = xstrdup(cvsroot);
210          {          rcsfile->module = xstrdup(module);
211                  char buf[256];          rcsfile->file = xstrdup(file);
212                  int n;          return rcsfile;
                 n = get_line(fp, buf, sizeof(buf));  
                 if(!n)  
                         break;  
                 if(n == -1)  
                 {  
                         perror("tempfile read");  
                         xfree(p);  
                         regfree(&rerev);  
                         regfree(&reinfo);  
                         return NULL;  
                 }  
                 switch(state)  
                 {  
                 case 0: /* Prologue */  
 more_prologue:  
                         if(!strncmp(buf, "RCS file:", 9))  
                         {  
                                 p->path = strip_dup(buf+9);  
                         }  
                         else if(!strncmp(buf, "Working file:", 13))  
                         {  
                                 p->name = strip_dup(buf+13);  
                         }  
                         else if(!strncmp(buf, "head:", 5))  
                         {  
                                 p->head = make_revid(buf+5);  
                         }  
                         else if(!strncmp(buf, "branch:", 7))  
                         {  
                                 p->branch = strip_dup(buf+7);  
                         }  
                         else if(!strncmp(buf, "locks:", 6))  
                         {  
                                 p->locks = strip_dup(buf+6);  
                         }  
                         else if(!strncmp(buf, "access list:", 12))  
                         {  
                                 p->access = strip_dup(buf+12);  
                         }  
                         else if(!strncmp(buf, "keyword substitution:", 21))  
                         {  
                                 p->keyword = strip_dup(buf+21);  
                         }  
                         else if(!strncmp(buf, "total revisions:", 16))  
                         {  
                                 p->totalrevs = strip_dup(buf+16);  
                         }  
                         else if(!strncmp(buf, "description:", 12))  
                         {  
                                 state = 2;  
                         }  
                         else if(!strncmp(buf, "symbolic names:", 15))  
                         {  
                                 state = 1;  
                         }  
                         else  
                         {  
                                 fprintf(stderr, "Unknown keyword(s) in line '%s' (state=%d)\n", buf, state);  
                                 xfree(p);  
                                 return NULL;  
                         }  
                         break;  
                 case 1: /* Tags */  
                         if(*buf != '\t')  
                         {  
                                 state = 0;  
                                 goto more_prologue;  
                         }  
                         else  
                         {  
                                 char *rev = strrchr(buf, ':');  
                                 tag_t *t;  
                                 if(!rev)  
                                 {  
                                         state = 2;  
                                         goto more_prologue;  
                                 }  
                                 *rev = '\0';  
                                 t = xmalloc(sizeof(*t));  
                                 t->tag = strip_dup(buf);  
                                 t->rev = make_revid(rev+1);  
                                 p->tags = xrealloc(p->tags, (p->ntags+1) * sizeof(p->tags[0]));  
                                 p->tags[p->ntags] = t;  
                                 p->ntags++;  
                         }  
                         break;  
                 case 2: /* Description */  
 add_description:  
                         if(!strcmp(buf, "----------------------------"))  
                         {  
                                 /* End of description */  
                                 state = 3;  
                                 break;  
                         }  
                         else if(!strcmp(buf, "============================================================================="))  
                         {  
                                 /* End of log */  
                                 state = 4;  
                                 break;  
                         }  
                         if(!p->nrevs)  
                                 p->comment = add_comment(p->comment, buf);  
                         else  
                                 p->revs[p->nrevs-1]->comment = add_comment(p->revs[p->nrevs-1]->comment, buf);  
                         break;  
                 case 3:  
                         if(!regexec(&rerev, buf, 0, NULL, 0))  
                         {  
                                 revision_t *r = xmalloc(sizeof(*r));  
                                 p->revs = xrealloc(p->revs, (p->nrevs+1) * sizeof(p->revs[0]));  
                                 p->revs[p->nrevs] = r;  
                                 p->nrevs++;  
                                 r->rev = make_revid(buf+8);  
                         }  
                         else if(!regexec(&reinfo, buf, 0, NULL, 0))  
                         {  
                                 assert(p->nrevs > 0);  
                                 p->revs[p->nrevs-1]->info = strip_dup(buf);  
                         }  
                         else  
                         {  
                                 /* No match means the description/comment */  
                                 state = 2;  
                                 goto add_description;  
                         }  
                         break;  
                 default:  
                         fprintf(stderr, "Illegal state (%d) in parser\n", state);  
                         xfree(p);  
                         regfree(&rerev);  
                         regfree(&reinfo);  
                         return NULL;  
                 }  
         }  
         regfree(&rerev);  
         regfree(&reinfo);  
         return p;  
213  }  }
214    
215  /*  /*
# Line 612  Line 228 
228          return i;          return i;
229  }  }
230    
231  int compare_revid(const revid_t *r1, const revid_t *r2)  int compare_rev(int bcmp, const rev_t *r1, const rev_t *r2)
232  {  {
233          int d1, d2;          int d1, d2;
234            char *c1, *c2;
235          char *v1, *v2;          char *v1, *v2;
236          char *s1, *s2;          char *s1, *s2;
237          int retval = 0;          int retval = 0;
238          assert(r1 != NULL);          assert(r1 != NULL);
         assert(r1->rev != NULL);  
239          assert(r2 != NULL);          assert(r2 != NULL);
240          assert(r2->rev != NULL);          if(bcmp)
241            {
242                    assert(r1->branch != NULL);
243                    assert(r2->branch != NULL);
244                    c1 = r1->branch;
245                    c2 = r2->branch;
246            }
247            else
248            {
249                    assert(r1->rev != NULL);
250                    assert(r2->rev != NULL);
251                    c1 = r1->rev;
252                    c2 = r2->rev;
253            }
254    
255          d1 = count_dots(r1->rev);          d1 = count_dots(c1);
256          d2 = count_dots(r2->rev);          d2 = count_dots(c2);
257          if(d1 != d2)          if(d1 != d2)
258          {          {
259                  return d1 - d2;                  return d1 - d2;
260          }          }
261    
262          s1 = v1 = xstrdup(r1->rev);          s1 = v1 = xstrdup(c1);
263          s2 = v2 = xstrdup(r2->rev);          s2 = v2 = xstrdup(c2);
264          while(1)          while(1)
265          {          {
266                  char *vc1 = strchr(s1, '.');                  char *vc1 = strchr(s1, '.');
# Line 658  Line 287 
287          return retval;          return retval;
288  }  }
289    
290  int tag_sort(const void *t1, const void *t2)  /*
291     **************************************************************************
292     * Reorganise the rcsfile for the branches
293     *
294     * Basically, we have a list of deltas (i.e. administrative info on
295     * revisions) and a list of delta text (the actual logs and diffs).
296     * The deltas are linked through the 'next' and the 'branches' fields
297     * which describe the tree-structure of revisions.
298     * The reorganisation means that we put each delta and corresponding
299     * delta text in a revision structure and assign it to a specific
300     * branch. This is required because we want to be able to calculate
301     * the bounding boxes of each branch. The revisions expand vertically
302     * and the branches expand horizontally.
303     * The reorganisation is performed in these steps:
304     * 1 - sort deltas and detla text on revision number for quick lookup
305     * 2 - start at the denoted head revision:
306     *      * create a branch structure and add this revision
307     *      * for each 'branches' in the delta do:
308     *              - walk all 'branches' of the delta and recursively goto 2
309     *                with the denoted branch delta as new head
310     *              - backlink the newly create sub-branch to the head revision
311     *                so that we can draw them recursively
312     *      * set head to the 'next' field and goto 2 until no next is
313     *        available
314     * 3 - update the administration
315     **************************************************************************
316     */
317    int sort_delta(const void *d1, const void *d2)
318    {
319            return compare_rev(0, (*(delta_t **)d1)->rev, (*(delta_t **)d2)->rev);
320    }
321    
322    int search_delta(const void *r, const void *d)
323  {  {
324  #define TAGPTR(t)       (*((tag_t **)t))          return compare_rev(0, (rev_t *)r, (*(delta_t **)d)->rev);
         return strcmp(TAGPTR(t1)->rev->rev, TAGPTR(t2)->rev->rev);  
 #undef TAGPTR  
325  }  }
326    
327  int rev_sort(const void *v1, const void *v2)  delta_t *find_delta(delta_t **dl, int n, rev_t *r)
328  {  {
329  #define REVPTR(t)       (*((revision_t **)t))          delta_t **d;
330          return compare_revid(REVPTR(v1)->rev, REVPTR(v2)->rev);          d = bsearch(r, dl, n, sizeof(*dl), search_delta);
331  #undef REVPTR          if(!d)
332                    return NULL;
333            return *d;
334  }  }
335    
336  int branch_sort(const void *b1, const void *b2)  int sort_dtext(const void *d1, const void *d2)
337  {  {
338  #define BPTR(t) (*((branch_t **)t))          return compare_rev(0, (*(dtext_t **)d1)->rev, (*(dtext_t **)d2)->rev);
         return strcmp(BPTR(b1)->branch, BPTR(b2)->branch);  
 #undef BPTR  
339  }  }
340    
341  int rev_cmp(const void *id, const void *v)  int search_dtext(const void *r, const void *d)
342  {  {
343  #define REVPTR(t)       (*((revision_t **)t))          return compare_rev(0, (rev_t *)r, (*(dtext_t **)d)->rev);
         return compare_revid((revid_t *)id, REVPTR(v)->rev);  
 #undef REVPTR  
344  }  }
345    
346  revision_t *find_revision(rcsfilelog_t * rcs, revid_t *id)  dtext_t *find_dtext(dtext_t **dl, int n, rev_t *r)
347  {  {
348          revision_t **r;          dtext_t **d;
349          if(id->isbranch)          d = bsearch(r, dl, n, sizeof(*dl), search_dtext);
350                  return NULL;          if(!d)
         r = bsearch(id, rcs->revs, rcs->nrevs, sizeof(rcs->revs[0]), rev_cmp);  
         if(!r)  
351                  return NULL;                  return NULL;
352          else          return *d;
                 return *r;  
353  }  }
354    
355  int branch_cmp(const void *s, const void *b)  rev_t *dup_rev(const rev_t *r)
356  {  {
357          return strcmp((const char *)s, (*((branch_t **)b))->branch);          rev_t *t = xmalloc(sizeof(*t));
358            t->rev = xstrdup(r->rev);
359            t->branch = xstrdup(r->branch);
360            t->isbranch = r->isbranch;
361            return t;
362    }
363    
364    branch_t *new_branch(delta_t *d, dtext_t *t)
365    {
366            branch_t *b = xmalloc(sizeof(*b));
367            revision_t *r = xmalloc(sizeof(*r));
368            r->delta = d;
369            r->dtext = t;
370            r->rev = d->rev;
371            r->branch = b;
372            b->branch = dup_rev(d->rev);
373            b->branch->isbranch = 1;
374            b->nrevs = 1;
375            b->revs = xmalloc(sizeof(b->revs[0]));
376            b->revs[0] = r;
377            return b;
378    }
379    
380    revision_t *add_to_branch(branch_t *b, delta_t *d, dtext_t *t)
381    {
382            revision_t *r = xmalloc(sizeof(*r));
383            r->delta = d;
384            r->dtext = t;
385            r->rev = d->rev;
386            r->branch = b;
387            b->revs = xrealloc(b->revs, (b->nrevs+1) * sizeof(b->revs[0]));
388            b->revs[b->nrevs] = r;
389            b->nrevs++;
390            return r;
391  }  }
392    
393  branch_t *find_branch(rcsfilelog_t *rcs, const char *id)  void build_branch(branch_t ***bl, int *nbl, delta_t **sdl, int nsdl, dtext_t **sdt, int nsdt, delta_t *head)
394  {  {
395          branch_t **b;          branch_t *b;
396          b = bsearch(id, rcs->branches, rcs->nbranches, sizeof(rcs->branches[0]), branch_cmp);          dtext_t *text;
397          if(!b)          revision_t *currev;
398                  return NULL;  
399          else          assert(head != NULL);
400                  return *b;  
401            if(head->flag)
402            {
403                    fprintf(stderr, "Circular reference on '%s' in branchpoint\n", head->rev->rev);
404                    return;
405            }
406            head->flag++;
407            text = find_dtext(sdt, nsdt, head->rev);
408    
409            /* Create a new branch for this head */
410            b = new_branch(head, text);
411            *bl = xrealloc(*bl, (*nbl+1)*sizeof((*bl)[0]));
412            (*bl)[*nbl] = b;
413            (*nbl)++;
414            currev = b->revs[0];
415            while(1)
416            {
417                    /* Process all sub-branches */
418                    if(head->branches)
419                    {
420                            int i;
421                            for(i = 0; i < head->branches->nrevs; i++)
422                            {
423                                    delta_t *d = find_delta(sdl, nsdl, head->branches->revs[i]);
424                                    int btag = *nbl;
425                                    if(!d)
426                                            continue;
427                                    build_branch(bl, nbl, sdl, nsdl, sdt, nsdt, d);
428    
429                                    /* Set the new branch's origin */
430                                    (*bl)[btag]->branchpoint = currev;
431    
432                                    /* Add branch to this revision */
433                                    currev->branches = xrealloc(currev->branches, (currev->nbranches+1)*sizeof(currev->branches[0]));
434                                    currev->branches[currev->nbranches] = (*bl)[btag];
435                                    currev->nbranches++;
436                            }
437                    }
438    
439                    /* Walk through the next list */
440                    if(!head->next)
441                            return;
442    
443                    head = find_delta(sdl, nsdl, head->next);
444                    if(!head)
445                    {
446                            fprintf(stderr, "Next revision (%s) not found in deltalist\n", head->next->rev);
447                            return;
448                    }
449                    if(head->flag)
450                    {
451                            fprintf(stderr, "Circular reference on '%s'\n", head->rev->rev);
452                            return;
453                    }
454                    head->flag++;
455                    text = find_dtext(sdt, nsdt, head->rev);
456                    currev = add_to_branch(b, head, text);
457            }
458  }  }
459    
460  tag_t *find_branchtag(rcsfilelog_t * rcs, const char *id)  int reorganise_branches(rcsfile_t *rcs)
461  {  {
462            delta_t **sdelta;
463            int nsdelta;
464            dtext_t **sdtext;
465            int nsdtext;
466            delta_t *head;
467            branch_t **bl;
468            int nbl;
469          int i;          int i;
470          for(i = 0; i < rcs->ntags; i++)  
471            assert(rcs->deltas != NULL);
472            assert(rcs->head != NULL);
473    
474            /* Make a new list for quick lookup */
475            nsdelta = rcs->deltas->ndeltas;
476            sdelta = xmalloc(nsdelta * sizeof(sdelta[0]));
477            memcpy(sdelta, rcs->deltas->deltas, nsdelta * sizeof(sdelta[0]));
478            qsort(sdelta, nsdelta, sizeof(sdelta[0]), sort_delta);
479    
480            /* Do the same for the delta text */
481            nsdtext = rcs->dtexts->ndtexts;
482            sdtext = xmalloc(nsdtext * sizeof(sdtext[0]));
483            memcpy(sdtext, rcs->dtexts->dtexts, nsdtext * sizeof(sdtext[0]));
484            qsort(sdtext, nsdtext, sizeof(sdtext[0]), sort_dtext);
485    
486            /* Start from the head of the trunk */
487            head = find_delta(sdelta, nsdelta, rcs->head);
488            if(!head)
489          {          {
490                  if(!rcs->tags[i]->rev->isbranch)                  fprintf(stderr, "Head revision (%s) not found in deltalist\n", rcs->head->rev);
491                          continue;                  return 0;
492                  if(!strcmp(id, rcs->tags[i]->rev->branch))          }
493                          return rcs->tags[i];          bl = NULL;
494            nbl = 0;
495            build_branch(&bl, &nbl, sdelta, nsdelta, sdtext, nsdtext, head);
496    
497            /* Reverse the head branch */
498            for(i = 0; i < bl[0]->nrevs/2; i++)
499            {
500                    revision_t *r;
501                    r = bl[0]->revs[i];
502                    bl[0]->revs[i] = bl[0]->revs[bl[0]->nrevs-i-1];
503                    bl[0]->revs[bl[0]->nrevs-i-1] = r;
504            }
505    
506            /* Update the branch-number of the head because it was reversed */
507            xfree(bl[0]->branch->branch);
508            bl[0]->branch->branch = xstrdup(bl[0]->revs[0]->rev->branch);
509    
510            /* Keep the admin */
511            rcs->branches = bl;
512            rcs->nbranches = nbl;
513            rcs->sdelta = sdelta;
514            rcs->nsdelta = nsdelta;
515            rcs->sdtext = sdtext;
516            rcs->nsdtext = nsdtext;
517            rcs->active = bl[0];
518            return 1;
519    }
520    
521    /*
522     **************************************************************************
523     * Assign the symbolic tags to the revisions and branches
524     *
525     * The tags point to revision numbers somewhere in the tree structure
526     * of branches and revisions. First we make a sorted list of all
527     * revisions and then we assign each tag to the proper revision.
528     **************************************************************************
529     */
530    int sort_revision(const void *r1, const void *r2)
531    {
532            return compare_rev(0, (*(revision_t **)r1)->delta->rev, (*(revision_t **)r2)->delta->rev);
533    }
534    
535    int search_revision(const void *t, const void *r)
536    {
537            return compare_rev(0, (rev_t *)t, (*(revision_t **)r)->delta->rev);
538    }
539    
540    int sort_branch(const void *b1, const void *b2)
541    {
542            return compare_rev(1, (*(branch_t **)b1)->branch, (*(branch_t **)b2)->branch);
543    }
544    
545    int search_branch(const void *t, const void *b)
546    {
547            return compare_rev(1, (rev_t *)t, (*(branch_t **)b)->branch);
548    }
549    
550    char *previous_rev(const char *c)
551    {
552            int dots = count_dots(c);
553            char *cptr;
554            char *r;
555            if(!dots)
556                    return xstrdup("1.0");  /* FIXME: don't know what the parent is */
557            if(dots & 1)
558            {
559                    /* Is is a revision we want the parent of */
560                    r = xstrdup(c);
561                    cptr = strrchr(r, '.');
562                    assert(cptr != NULL);
563                    if(dots == 1)
564                    {
565                            /* FIXME: What is the parent of 1.1? */
566                            cptr[1] = '\0';
567                            strcat(r, "0");
568                            return r;
569                    }
570                    /* Here we have a "x.x[.x.x]+" case */
571                    *cptr = '\0';
572                    cptr = strrchr(r, '.');
573                    assert(cptr != NULL);
574                    *cptr = '\0';
575                    return r;
576          }          }
577          return NULL;          /* It is a branch we want the parent of */
578            r = xstrdup(c);
579            cptr = strrchr(r, '.');
580            assert(cptr != NULL);
581            *cptr = '\0';
582            return r;
583    }
584    
585    int assign_tags(rcsfile_t *rcs)
586    {
587            int i;
588            int nr;
589    
590            for(i = nr = 0; i < rcs->nbranches; i++)
591                    nr += rcs->branches[i]->nrevs;
592    
593            rcs->srev = xmalloc(nr * sizeof(rcs->srev[0]));
594            rcs->nsrev = nr;
595            for(i = nr = 0; i < rcs->nbranches; i++)
596            {
597                    memcpy(&rcs->srev[nr], rcs->branches[i]->revs, rcs->branches[i]->nrevs * sizeof(rcs->branches[i]->revs[0]));
598                    nr += rcs->branches[i]->nrevs;
599            }
600    
601            qsort(rcs->srev, rcs->nsrev, sizeof(rcs->srev[0]), sort_revision);
602            qsort(rcs->branches, rcs->nbranches, sizeof(rcs->branches[0]), sort_branch);
603    
604            if(!rcs->branch)
605            {
606                    /* The main trunk is the active trunk */
607                    rcs->tags->tags = xrealloc(rcs->tags->tags, (rcs->tags->ntags+1)*sizeof(rcs->tags->tags[0]));
608                    rcs->tags->tags[rcs->tags->ntags] = xmalloc(sizeof(tag_t));
609                    rcs->tags->tags[rcs->tags->ntags]->tag = xstrdup("MAIN");
610                    rcs->tags->tags[rcs->tags->ntags]->rev = xmalloc(sizeof(rev_t));
611                    rcs->tags->tags[rcs->tags->ntags]->rev->rev = xstrdup(rcs->active->branch->rev);
612                    rcs->tags->tags[rcs->tags->ntags]->rev->branch = xstrdup(rcs->active->branch->branch);
613                    rcs->tags->tags[rcs->tags->ntags]->rev->isbranch = 1;
614                    rcs->tags->ntags++;
615            }
616    
617            /* We should have at least two tags (HEAD and MAIN) */
618            assert(rcs->tags != 0);
619    
620            for(i = 0; i < rcs->tags->ntags; i++)
621            {
622                    tag_t *t = rcs->tags->tags[i];
623                    if(t->rev->isbranch)
624                    {
625                            branch_t **b;
626    add_btag:
627                            b = bsearch(t->rev, rcs->branches, rcs->nbranches, sizeof(rcs->branches[0]), search_branch);
628                            if(!b)
629                            {
630                                    rev_t rev;
631                                    revision_t **r;
632                                    /* This happens for the magic branch numbers if there are
633                                     * no commits withing the new branch yet. So, we add the
634                                     * branch and try continue.
635                                     */
636                                    rev.rev = previous_rev(t->rev->branch);
637                                    rev.branch = NULL;
638                                    rev.isbranch = 0;
639                                    r = bsearch(&rev, rcs->srev, rcs->nsrev, sizeof(rcs->srev[0]), search_revision);
640                                    xfree(rev.rev);
641                                    if(!r)
642                                    {
643                                            fprintf(stderr, "No branch found for tag '%s:%s'\n", t->tag, t->rev->branch);
644                                    }
645                                    else
646                                    {
647                                            rcs->branches = xrealloc(rcs->branches, (rcs->nbranches+1)*sizeof(rcs->branches[0]));
648                                            rcs->branches[rcs->nbranches] = xmalloc(sizeof(branch_t));
649                                            rcs->branches[rcs->nbranches]->branch = dup_rev(t->rev);
650                                            rcs->branches[rcs->nbranches]->branchpoint = *r;
651                                            (*r)->branches = xrealloc((*r)->branches, ((*r)->nbranches+1)*sizeof((*r)->branches[0]));
652                                            (*r)->branches[(*r)->nbranches] = rcs->branches[rcs->nbranches];
653                                            (*r)->nbranches++;
654                                            rcs->nbranches++;
655                                            /* Resort the branches */
656                                            qsort(rcs->branches, rcs->nbranches, sizeof(rcs->branches[0]), sort_branch);
657                                            goto add_btag;
658                                    }
659                            }
660                            else
661                            {
662                                    branch_t *bb = *b;
663                                    bb->tags = xrealloc(bb->tags, (bb->ntags+1)*sizeof(bb->tags[0]));
664                                    bb->tags[bb->ntags] = t;
665                                    bb->ntags++;
666                            }
667                    }
668                    else
669                    {
670                            revision_t **r = bsearch(t->rev, rcs->srev, rcs->nsrev, sizeof(rcs->srev[0]), search_revision);
671                            if(!r)
672                                    fprintf(stderr, "No revision found for tag '%s:%s'\n", t->tag, t->rev->rev);
673                            else
674                            {
675                                    revision_t *rr = *r;
676                                    rr->tags = xrealloc(rr->tags, (rr->ntags+1)*sizeof(rr->tags[0]));
677                                    rr->tags[rr->ntags] = t;
678                                    rr->ntags++;
679                            }
680                    }
681            }
682    
683            /* We need to reset the first in the list of branches to the
684             * active branch to ensure the drawing of all
685             */
686            if(rcs->active != rcs->branches[0])
687            {
688                    branch_t **b = bsearch(rcs->active->branch, rcs->branches, rcs->nbranches, sizeof(rcs->branches[0]), search_branch);
689                    branch_t *t;
690                    assert(b != NULL);
691                    t = *b;
692                    *b = rcs->branches[0];
693                    rcs->branches[0] = t;
694            }
695            return 1;
696  }  }
697    
698  /*  /*
# Line 809  Line 778 
778          int rx = lx + b->w;          int rx = lx + b->w;
779          int yy;          int yy;
780          int i;          int i;
781            /*draw_rbox(im, cx-b->tw/2-1, ty-1, cx+b->tw/2+1, ty+b->th+1, 0, &conf.title_color);*/
782          draw_rbox(im, lx, ty, rx, ty+b->h, 5, &conf.branch_color);          draw_rbox(im, lx, ty, rx, ty+b->h, 5, &conf.branch_color);
783          yy = conf.branch_tspace;          yy = conf.branch_tspace;
784          draw_string(im, b->branch, &conf.branch_font, cx, ty+yy, ALIGN_HC, &conf.branch_color);          draw_string(im, b->branch->branch, &conf.branch_font, cx, ty+yy, ALIGN_HC, &conf.branch_color);
785          yy += get_sheight(b->branch, &conf.branch_font);          yy += get_sheight(b->branch->branch, &conf.branch_font);
786          if(b->tag)          for(i = 0; i < b->ntags; i++)
787          {          {
788                  draw_string(im, b->tag->tag, &conf.branch_font, cx, ty+yy, ALIGN_HC, &conf.branch_color);                  draw_string(im, b->tags[i]->tag, &conf.branch_font, cx, ty+yy, ALIGN_HC, &conf.branch_color);
789                    yy += get_sheight(b->tags[i]->tag, &conf.branch_font);
790          }          }
791    
792          ty += b->h;          ty += b->h;
# Line 852  Line 823 
823          add_title_str(buf);          add_title_str(buf);
824  }  }
825    
826  char *expand_title(rcsfilelog_t *rcs)  char *expand_title(rcsfile_t *rcs)
827  {  {
828          char nb[32];          char nb[32];
829          char nr[32];          char nr[32];
830          char *cptr;          char *cptr;
831    
832          sprintf(nb, "%d", rcs->nbranches);          sprintf(nb, "%d", rcs->nbranches);
833          sprintf(nr, "%d", rcs->nrevs);          sprintf(nr, "%d", rcs->nsrev);
834          for(cptr = conf.title; *cptr; cptr++)          for(cptr = conf.title; *cptr; cptr++)
835          {          {
836                  if(*cptr == '%')                  if(*cptr == '%')
# Line 867  Line 838 
838                          switch(*++cptr)                          switch(*++cptr)
839                          {                          {
840                          case 'c': add_title_str(conf.cvsroot); break;                          case 'c': add_title_str(conf.cvsroot); break;
841                          case 'f': add_title_str(rcs->name); break;                          case 'f': add_title_str(rcs->file); break;
842                          case 'm': add_title_str(conf.cvsmodule); break;                          case 'm': add_title_str(conf.cvsmodule); break;
843                          case 'r': add_title_str(nr); break;                          case 'r': add_title_str(nr); break;
844                          case 'b': add_title_str(nb); break;                          case 'b': add_title_str(nb); break;
# Line 904  Line 875 
875  void draw_connector(gdImagePtr im, branch_t *b)  void draw_connector(gdImagePtr im, branch_t *b)
876  {  {
877          revision_t *r = b->branchpoint;          revision_t *r = b->branchpoint;
878          int x1 = r->x + r->w/2 + 2;          int x1 = r->cx + r->w/2 + 2;
879          int y1 = r->y + r->h/2;          int y1 = r->y + r->h/2;
880          int x2 = b->x;          int x2 = b->cx;
881          int y2 = b->y;          int y2 = b->y;
882          gdImageLine(im, x1, y1, x2, y1, conf.branch_color.id);          gdImageLine(im, x1, y1, x2, y1, conf.branch_color.id);
883          gdImageLine(im, x2, y1, x2, y2, conf.branch_color.id);          gdImageLine(im, x2, y1, x2, y2, conf.branch_color.id);
884  }  }
885    
886  gdImagePtr make_image(rcsfilelog_t *rcs)  gdImagePtr make_image(rcsfile_t *rcs)
887  {  {
888          gdImagePtr im;          gdImagePtr im;
889          int i;          int i;
# Line 926  Line 897 
897          conf.title_color.id = gdImageColorAllocate(im, conf.title_color.r, conf.title_color.g, conf.title_color.b);          conf.title_color.id = gdImageColorAllocate(im, conf.title_color.r, conf.title_color.g, conf.title_color.b);
898    
899          for(i = 0; i < rcs->nbranches; i++)          for(i = 0; i < rcs->nbranches; i++)
900                  draw_branch(im, rcs->branches[i]->x, rcs->branches[i]->y, rcs->branches[i]);                  draw_branch(im, rcs->branches[i]->cx, rcs->branches[i]->y, rcs->branches[i]);
901          for(i = 0; i < rcs->nbranches; i++)          for(i = 0; i < rcs->nbranches; i++)
902          {          {
903                  if(rcs->branches[i]->branchpoint)                  if(rcs->branches[i]->branchpoint)
# Line 937  Line 908 
908          return im;          return im;
909  }  }
910    
911    /*
912     **************************************************************************
913     * Layout routines
914     **************************************************************************
915     */
916  void move_branch(branch_t *b, int x, int y)  void move_branch(branch_t *b, int x, int y)
917  {  {
918          int i;          int i;
919          b->x += x;          b->cx += x;
920          b->y += y;          b->y += y;
921          for(i = 0; i < b->nrevs; i++)          for(i = 0; i < b->nrevs; i++)
922          {          {
923                  b->revs[i]->x += x;                  b->revs[i]->cx += x;
924                  b->revs[i]->y += y;                  b->revs[i]->y += y;
925          }          }
926  }  }
# Line 955  Line 931 
931          for(j = 0; j < r->nbranches; j++)          for(j = 0; j < r->nbranches; j++)
932          {          {
933                  branch_t *b = r->branches[j];                  branch_t *b = r->branches[j];
934                  *x += *w + conf.rev_minline + b->tw/2 - b->x;                  *x += *w + conf.rev_minline + b->tw/2 - b->cx;
935                  *w = b->tw/2;                  *w = b->tw/2;
936                  move_branch(b, *x, r->y + r->h);                  move_branch(b, *x, r->y + r->h/2 + conf.branch_connect);
937                  *x = b->x;                  *x = b->cx;
938                  /* Recurse to move branches of branched revisions */                  /* Recurse to move branches of branched revisions */
939                  for(i = b->nrevs-1; i >= 0; i--)                  for(i = b->nrevs-1; i >= 0; i--)
940                  {                  {
# Line 973  Line 949 
949          int x2 = x1 + *w;          int x2 = x1 + *w;
950          int y1 = *y;          int y1 = *y;
951          int y2 = y1 + *h;          int y2 = y1 + *h;
952          int xx1 = b->x - b->tw/2;          int xx1 = b->cx - b->tw/2;
953          int xx2 = xx1 + b->tw;          int xx2 = xx1 + b->tw;
954          int yy1 = b->y;          int yy1 = b->y;
955          int yy2 = yy1 + b->th;          int yy2 = yy1 + b->th;
# Line 987  Line 963 
963          *h = y2 - y1;          *h = y2 - y1;
964  }  }
965    
966  void make_layout(rcsfilelog_t *rcs)  int branch_intersects(int top, int bottom, int left, branch_t *b)
967    {
968            int br = b->cx + b->tw/2;
969            int bt = b->y - conf.branch_connect - conf.branch_margin/2;
970            int bb = b->y + b->th + conf.branch_margin/2;
971            return !(bt > bottom || bb < top || br >= left);
972    }
973    
974    int kern_branch(rcsfile_t *rcs, branch_t *b)
975    {
976            int left = b->cx - b->tw/2;
977            int top = b->y - conf.branch_connect - conf.branch_margin/2;
978            int bottom = b->y + b->th + conf.branch_margin/2;
979            int i;
980            int xpos = 0;
981    
982            for(i = 0; i < rcs->nbranches; i++)
983            {
984                    branch_t *bp = rcs->branches[i];
985                    if(bp == b)
986                            continue;
987                    if(branch_intersects(top, bottom, left, bp))
988                    {
989                            int m = bp->cx + bp->tw/2 + conf.branch_margin;
990                            if(m > xpos)
991                                    xpos = m;
992                    }
993            }
994            if(xpos && (b->cx - b->tw/2) - xpos > 0)
995            {
996                    move_branch(b, xpos - (b->cx - b->tw/2), 0);
997                    return 1;
998            }
999            return 0;
1000    }
1001    
1002    void make_layout(rcsfile_t *rcs)
1003  {  {
1004          int i, j;          int i, j;
1005          int x, y;          int x, y;
1006          int w, h;          int w, h;
1007          int w2;          int w2;
1008            int moved;
1009    
1010          /* Calculate the box-sizes of the revisions */          /* Calculate the box-sizes of the revisions */
1011          for(i = 0; i < rcs->nrevs; i++)          for(i = 0; i < rcs->nsrev; i++)
1012          {          {
1013                  revision_t *rp;                  revision_t *rp;
1014                  int w;                  int w;
1015                  int h;                  int h;
1016                  rp = rcs->revs[i];                  rp = rcs->srev[i];
1017                  w = get_swidth(rp->rev->rev, &conf.rev_font);                  w = get_swidth(rp->rev->rev, &conf.rev_font);
1018                  h = get_sheight(rp->rev->rev, &conf.rev_font);                  h = get_sheight(rp->rev->rev, &conf.rev_font);
1019                  for(j = 0; j < rp->ntags; j++)                  for(j = 0; j < rp->ntags; j++)
# Line 1019  Line 1032 
1032                  branch_t *bp = rcs->branches[i];                  branch_t *bp = rcs->branches[i];
1033                  int w;                  int w;
1034                  int h;                  int h;
1035                  w = get_swidth(bp->branch, &conf.branch_font);                  w = get_swidth(bp->branch->branch, &conf.branch_font);
1036                  h = get_sheight(bp->branch, &conf.branch_font);                  h = get_sheight(bp->branch->branch, &conf.branch_font);
1037                  if(bp->tag)                  for(j = 0; j < bp->ntags; j++)
1038                  {                  {
1039                          int ww = get_swidth(bp->tag->tag, &conf.branch_font);                          int ww = get_swidth(bp->tags[j]->tag, &conf.branch_font);
1040                          if(ww > w) w = ww;                          if(ww > w) w = ww;
1041                          h += get_sheight(bp->tag->tag, &conf.branch_font) + conf.branch_separator;                          h += get_sheight(bp->tags[j]->tag, &conf.branch_font);
1042                  }                  }
1043                  w += conf.branch_lspace + conf.branch_rspace;                  w += conf.branch_lspace + conf.branch_rspace;
1044                  h += conf.branch_tspace + conf.branch_bspace;                  h += conf.branch_tspace + conf.branch_bspace;
# Line 1047  Line 1060 
1060                  branch_t *b = rcs->branches[i];                  branch_t *b = rcs->branches[i];
1061                  x = b->tw/2;                  x = b->tw/2;
1062                  y = b->h;                  y = b->h;
1063                  b->x = x;                  b->cx = x;
1064                  b->y = 0;                  b->y = 0;
1065                  for(j = 0; j < b->nrevs; j++)                  for(j = 0; j < b->nrevs; j++)
1066                  {                  {
1067                          y += conf.rev_minline;                          y += conf.rev_minline;
1068                          b->revs[j]->x = x;                          b->revs[j]->cx = x;
1069                          b->revs[j]->y = y;                          b->revs[j]->y = y;
1070                          y += b->revs[j]->h;                          y += b->revs[j]->h;
1071                  }                  }
1072          }          }
1073    
1074          /* Reposition the branches */          /* Reposition the branches */
1075          x = rcs->branches[0]->x;          x = rcs->branches[0]->cx;
1076          w2 = rcs->branches[0]->tw / 2;          w2 = rcs->branches[0]->tw / 2;
1077          for(i = rcs->branches[0]->nrevs-1; i >= 0; i--)          for(i = rcs->branches[0]->nrevs-1; i >= 0; i--)
1078          {          {
1079                  reposition_branch(rcs->branches[0]->revs[i], &x, &w2);                  reposition_branch(rcs->branches[0]->revs[i], &x, &w2);
1080          }          }
1081    
1082            /* Try to move branches left if there is room (kerning) */
1083            for(moved = 1; moved; )
1084            {
1085                    moved = 0;
1086                    for(i = 1; i < rcs->nbranches; i++)
1087                    {
1088                            moved += kern_branch(rcs, rcs->branches[i]);
1089                    }
1090            }
1091    
1092            /* Move everything w.r.t. the top-left margin */
1093          for(i = 0; i < rcs->nbranches; i++)          for(i = 0; i < rcs->nbranches; i++)
1094                  move_branch(rcs->branches[i], conf.margin_left, conf.margin_top);                  move_branch(rcs->branches[i], conf.margin_left, conf.margin_top);
1095    
1096          /* Calculate overall image size */          /* Calculate overall image size */
1097          x = rcs->branches[0]->x - rcs->branches[0]->tw/2;          x = rcs->branches[0]->cx - rcs->branches[0]->tw/2;
1098          y = rcs->branches[0]->y;          y = rcs->branches[0]->y;
1099          w = rcs->branches[0]->tw;          w = rcs->branches[0]->tw;
1100          h = rcs->branches[0]->th;          h = rcs->branches[0]->th;
# Line 1085  Line 1109 
1109   * Imagemap functions   * Imagemap functions
1110   **************************************************************************   **************************************************************************
1111   */   */
1112  void make_imagemap(rcsfilelog_t *rcs, FILE *fp)  void make_imagemap(rcsfile_t *rcs, FILE *fp)
1113  {  {
1114          int i, j;          int i, j;
1115          fprintf(fp, "<map name=\"%s\">\n", conf.map_name);          fprintf(fp, "<map name=\"%s\">\n", conf.map_name);
# Line 1093  Line 1117 
1117          {          {
1118                  branch_t *b = rcs->branches[i];                  branch_t *b = rcs->branches[i];
1119                  fprintf(fp, "<area shape=\"rect\" href=\"branch_%s\" coords=\"%d,%d,%d,%d\" alt=\"Branch %s\">\n",                  fprintf(fp, "<area shape=\"rect\" href=\"branch_%s\" coords=\"%d,%d,%d,%d\" alt=\"Branch %s\">\n",
1120                                  b->branch,                                  b->branch->branch,
1121                                  b->x - b->w/2, b->y, b->x + b->w/2, b->y + b->h,                                  b->cx - b->w/2, b->y, b->cx + b->w/2, b->y + b->h,
1122                                  b->branch);                                  b->branch->branch);
1123                  for(j = 0; j < b->nrevs; j++)                  for(j = 0; j < b->nrevs; j++)
1124                  {                  {
1125                          revision_t *r = b->revs[j];                          revision_t *r = b->revs[j];
1126                          fprintf(fp, "<area shape=\"rect\" href=\"rev_%s\" coords=\"%d,%d,%d,%d\" alt=\"Revision %s\">\n",                          fprintf(fp, "<area shape=\"rect\" href=\"rev_%s\" coords=\"%d,%d,%d,%d\" alt=\"Revision %s\">\n",
1127                                  r->rev->rev,                                  r->rev->rev,
1128                                  r->x - r->w/2, r->y, r->x + r->w/2, r->y + r->h,                                  r->cx - r->w/2, r->y, r->cx + r->w/2, r->y + r->h,
1129                                  r->rev->rev);                                  r->rev->rev);
1130                  }                  }
1131          }          }
# Line 1117  Line 1141 
1141  {  {
1142          FILE *fp;          FILE *fp;
1143          int r;          int r;
1144    
1145          if(path)          if(path)
1146          {          {
1147                  if((fp = fopen(path, "r")) == NULL)                  if((fp = fopen(path, "r")) == NULL)
1148                  {                  {
1149                          return 0;                          return 0;
1150                  }                  }
1151                    else
1152                            input_file = path;
1153          }          }
1154          else          else
1155          {          {
# Line 1132  Line 1159 
1159                          {                          {
1160                                  return 0;                                  return 0;
1161                          }                          }
1162                            else
1163                                    input_file = ETCDIR "/" CONFFILENAME;
1164                  }                  }
1165                    else
1166                            input_file = "./" CONFFILENAME;
1167          }          }
1168    
1169          yyin = fp;          yyin = fp;
1170          r = yyparse();          r = yyparse();
1171          fclose(fp);          fclose(fp);
1172            input_file = NULL;
1173          return r == 0;          return r == 0;
1174  }  }
1175    
# Line 1149  Line 1181 
1181  static const char usage_str[] =  static const char usage_str[] =
1182          "Usage: cvsgraph [options] <file>\n"          "Usage: cvsgraph [options] <file>\n"
1183          "  -c <file>  Read alternative config from <file>\n"          "  -c <file>  Read alternative config from <file>\n"
1184            "  -d <level> Enable debug mode at <level>\n"
1185          "  -h         This message\n"          "  -h         This message\n"
1186          "  -i <file>  Write an imagamap to <file>\n"          "  -i         Generate an imagemap instead of image\n"
1187            "  -M <name>  Use <name> as imagemap name\n"
1188          "  -m <mod>   Use <mod> as cvs module\n"          "  -m <mod>   Use <mod> as cvs module\n"
1189          "  -o <file>  Output to <file>\n"          "  -o <file>  Output to <file>\n"
1190            "  -q         Be quiet (i.e. no warnings)\n"
1191          "  -r <path>  Use <path> as cvsroot path\n"          "  -r <path>  Use <path> as cvsroot path\n"
1192          "  -V         Print version and exit\n"          "  -V         Print version and exit\n"
1193          ;          ;
1194    
1195  #define VERSION_STR     "1.0.1"  #define VERSION_STR     "1.1.0"
1196  #define NOTICE_STR      "Copyright (c) 2001 B.Stultiens"  #define NOTICE_STR      "Copyright (c) 2001 B.Stultiens"
1197    
 void add_tag(rcsfilelog_t *rcs, const char *tag, const char *rev)  
 {  
         rcs->tags = xrealloc(rcs->tags, (rcs->ntags+1)*sizeof(rcs->tags[0]));  
         rcs->tags[rcs->ntags] = xmalloc(sizeof(tag_t));  
         rcs->tags[rcs->ntags]->tag = strip_dup(tag);  
         rcs->tags[rcs->ntags]->rev = make_revid(rev);  
         rcs->ntags++;  
 }  
   
1198  int main(int argc, char *argv[])  int main(int argc, char *argv[])
1199  {  {
1200            extern int yy_flex_debug;
1201            extern int rcs_flex_debug;
1202            extern int yydebug;
1203            extern int rcsdebug;
1204          int optc;          int optc;
1205          char *confpath = NULL;          char *confpath = NULL;
1206          char *outfile = NULL;          char *outfile = NULL;
1207          char *cvsroot = NULL;          char *cvsroot = NULL;
1208          char *cvsmodule = NULL;          char *cvsmodule = NULL;
1209          char *imagemap = NULL;          int imagemap = 0;
1210            char *imgmapname = NULL;
1211          int lose = 0;          int lose = 0;
1212          FILE *fp;          FILE *fp;
1213          int n;          rcsfile_t *rcs;
         rcsfilelog_t *rcs;  
1214          gdImagePtr im;          gdImagePtr im;
1215    
1216  #ifdef DEBUG          while((optc = getopt(argc, argv, "c:d:hiM:m:o:qr:V")) != EOF)
         setvbuf(stdout, NULL, 0, _IONBF);  
         setvbuf(stderr, NULL, 0, _IONBF);  
 #endif  
   
         while((optc = getopt(argc, argv, "c:hi:m:o:r:V")) != EOF)  
1217          {          {
1218                  switch(optc)                  switch(optc)
1219                  {                  {
1220                  case 'c':                  case 'c':
1221                          confpath = xstrdup(optarg);                          confpath = xstrdup(optarg);
1222                          break;                          break;
1223                    case 'd':
1224                            debuglevel = strtol(optarg, NULL, 0);
1225                            break;
1226                  case 'i':                  case 'i':
1227                          imagemap = xstrdup(optarg);                          imagemap = 1;
1228                            break;
1229                    case 'M':
1230                            imgmapname = xstrdup(optarg);
1231                          break;                          break;
1232                  case 'm':                  case 'm':
1233                          cvsmodule = xstrdup(optarg);                          cvsmodule = xstrdup(optarg);
# Line 1204  Line 1235 
1235                  case 'o':                  case 'o':
1236                          outfile = xstrdup(optarg);                          outfile = xstrdup(optarg);
1237                          break;                          break;
1238                    case 'q':
1239                            quiet = 1;
1240                            break;
1241                  case 'r':                  case 'r':
1242                          cvsroot = xstrdup(optarg);                          cvsroot = xstrdup(optarg);
1243                          break;                          break;
# Line 1230  Line 1264 
1264                  return 1;                  return 1;
1265          }          }
1266    
1267            if(debuglevel)
1268            {
1269                    setvbuf(stdout, NULL, 0, _IONBF);
1270                    setvbuf(stderr, NULL, 0, _IONBF);
1271            }
1272            yy_flex_debug = (debuglevel & DEBUG_CONF_LEX) != 0;
1273            rcs_flex_debug = (debuglevel & DEBUG_RCS_LEX) != 0;
1274            yydebug = (debuglevel & DEBUG_CONF_YACC) != 0;
1275            rcsdebug = (debuglevel & DEBUG_RCS_YACC) != 0;
1276    
1277          /* Set defaults */          /* Set defaults */
1278          if(!conf.tag_font)      conf.tag_font = gdFontTiny;          if(!conf.tag_font)      conf.tag_font = gdFontTiny;
1279          if(!conf.rev_font)      conf.rev_font = gdFontTiny;          if(!conf.rev_font)      conf.rev_font = gdFontTiny;
# Line 1243  Line 1287 
1287          }          }
1288    
1289          /* Set overrides */          /* Set overrides */
1290          if(cvsroot)             conf.cvsroot = cvsroot;          if(cvsroot)     conf.cvsroot = cvsroot;
1291          if(cvsmodule)           conf.cvsmodule = cvsmodule;          if(cvsmodule)   conf.cvsmodule = cvsmodule;
1292            if(imgmapname)  conf.map_name = imgmapname;
1293    
1294          if((fp = get_log(conf.cvsroot, conf.cvsmodule, argv[optind])) == NULL)          rcs = get_rcsfile(conf.cvsroot, conf.cvsmodule, argv[optind]);
         {  
                 fprintf(stderr, "Error getting log for '%s'\n", argv[optind]);  
                 return 1;  
         }  
   
         rcs = parse_log(fp);  
1295          if(!rcs)          if(!rcs)
1296          {          {
1297                  fprintf(stderr, "Error parsing log\n");                  fprintf(stderr, "Error reading rcs-file\n");
1298                  return 1;                  return 1;
1299          }          }
         fclose(fp);  
1300    
1301          /* Add implicit tags */          if(debuglevel & DEBUG_RCS_FILE)
1302          add_tag(rcs, "HEAD", rcs->head->rev);                  dump_rcsfile(rcs);
         add_tag(rcs, "MAIN", "1");  
   
         /* We now have the log. Sort and reorganize a little */  
         qsort(rcs->tags, rcs->ntags, sizeof(rcs->tags[0]), tag_sort);  
         qsort(rcs->revs, rcs->nrevs, sizeof(rcs->revs[0]), rev_sort);  
1303    
1304          /* Assign tags to revisions */          if(!reorganise_branches(rcs))
1305          for(n = 0; n < rcs->ntags; n++)                  return 1;
         {  
                 revision_t *r = find_revision(rcs, rcs->tags[n]->rev);  
                 if(!r)  
                         continue;  
                 r->tags = xrealloc(r->tags, (r->ntags+1) * sizeof(r->tags[0]));  
                 r->tags[r->ntags] = rcs->tags[n];  
                 r->ntags++;  
         }  
   
         /* Isolate the branches */  
         for(n = 0; n < rcs->nrevs; n++)  
         {  
                 branch_t *b = find_branch(rcs, rcs->revs[n]->rev->branch);  
                 if(!b)  
                 {  
                         rcs->branches = xrealloc(rcs->branches, (rcs->nbranches+1) * sizeof(rcs->branches[0]));  
                         b = xmalloc(sizeof(*b));  
                         b->branch = xstrdup(rcs->revs[n]->rev->branch);  
                         b->tag = find_branchtag(rcs, rcs->revs[n]->rev->branch);  
                         rcs->branches[rcs->nbranches] = b;  
                         rcs->nbranches++;  
                         qsort(rcs->branches, rcs->nbranches, sizeof(rcs->branches[0]), branch_sort);  
                 }  
                 b->revs = xrealloc(b->revs, (b->nrevs+1) * sizeof(b->revs[0]));  
                 b->revs[b->nrevs] = rcs->revs[n];  
                 b->nrevs++;  
         }  
   
         /* Find the branchpoints */  
         for(n = 0; n < rcs->nbranches; n++)  
         {  
                 char *prev = xstrdup(rcs->branches[n]->branch);  
                 char *cptr = strrchr(prev, '.');  
                 revision_t *r;  
                 revid_t rid;  
                 if(!cptr)       /* Main branch number */  
                         continue;  
                 *cptr = '\0';  
                 rid.isbranch = 0;  
                 rid.branch = "";  
                 rid.rev = prev;  
                 r = find_revision(rcs, &rid);  
                 if(!r)  
                 {  
                         /* Hm, disjoint branch... */  
                         fprintf(stderr, "Hm, don't know how to handle disjoint branches (%s)\n", prev);  
                         assert(r != NULL);  
                 }  
                 rcs->branches[n]->branchpoint = r;  
                 r->branches = xrealloc(r->branches, (r->nbranches+1)*sizeof(r->branches[0]));  
                 r->branches[r->nbranches] = rcs->branches[n];  
                 r->nbranches++;  
         }  
1306    
1307          make_layout(rcs);          if(!assign_tags(rcs))
1308                    return 1;
1309    
 #ifdef DEBUG  
         dump_log(rcs);  
 #endif  
         im = make_image(rcs);  
1310          if(outfile)          if(outfile)
1311          {          {
1312                  if((fp = fopen(outfile, "w")) == NULL)                  if((fp = fopen(outfile, "w")) == NULL)
# Line 1341  Line 1318 
1318          else          else
1319                  fp = stdout;                  fp = stdout;
1320    
1321          switch(conf.image_type)          make_layout(rcs);
1322    
1323            if(!imagemap)
1324          {          {
1325                    /* Create an image */
1326                    im = make_image(rcs);
1327    
1328                    switch(conf.image_type)
1329                    {
1330  #ifdef HAVE_IMAGE_GIF  #ifdef HAVE_IMAGE_GIF
1331          default:  # ifndef HAVE_IMAGE_PNG
1332          case IMAGE_GIF:                  default:
1333                  gdImageGif(im, fp);  # endif
1334                  break;                  case IMAGE_GIF:
1335                            gdImageGif(im, fp);
1336                            break;
1337  #endif  #endif
1338  #ifdef HAVE_IMAGE_PNG  #ifdef HAVE_IMAGE_PNG
1339  # ifndef HAVE_IMAGE_GIF                  default:
1340          default:                  case IMAGE_PNG:
1341  # endif                          gdImagePng(im, fp);
1342          case IMAGE_PNG:                          break;
                 gdImagePng(im, fp);  
                 break;  
1343  #endif  #endif
1344  #ifdef HAVE_IMAGE_JPEG  #ifdef HAVE_IMAGE_JPEG
1345  # if !defined(HAVE_IMAGE_GIF) && !defined(HAVE_IMAGE_PNG)  # if !defined(HAVE_IMAGE_GIF) && !defined(HAVE_IMAGE_PNG)
1346          default:                  default:
1347  # endif  # endif
1348          case IMAGE_JPEG:                  case IMAGE_JPEG:
1349                  gdImageJpeg(im, fp, conf.image_quality);                          gdImageJpeg(im, fp, conf.image_quality);
1350                  break;                          break;
1351  #endif  #endif
1352          }                  }
   
         if(outfile)  
                 fclose(fp);  
         gdImageDestroy(im);  
1353    
1354          if(imagemap)                  gdImageDestroy(im);
1355            }
1356            else
1357          {          {
1358                  if((fp = fopen(imagemap, "w")) == NULL)                  /* Create an imagemap */
                 {  
                         perror(imagemap);  
                         return 1;  
                 }  
1359                  make_imagemap(rcs, fp);                  make_imagemap(rcs, fp);
                 fclose(fp);  
1360          }          }
1361    
1362            if(outfile)
1363                    fclose(fp);
1364    
1365          return 0;          return 0;
1366  }  }
1367    

Legend:
Removed from v.1.6  
changed lines
  Added in v.1.7

  ViewVC Help
Powered by ViewVC 1.1.0 with CvsGraph 1.7.0