/[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.5, Sat Feb 24 00:35:13 2001 UTC revision 1.8, Sun Mar 4 03:19:18 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 68  Line 31 
31  #include <regex.h>  #include <regex.h>
32  #include <errno.h>  #include <errno.h>
33  #include <getopt.h>  #include <getopt.h>
34    #include <ctype.h>
35    
36  #include <gd.h>  #include <gd.h>
37  #include <gdfontt.h>  #include <gdfontt.h>
# Line 76  Line 40 
40  #include "cvsgraph.h"  #include "cvsgraph.h"
41  #include "utils.h"  #include "utils.h"
42  #include "readconf.h"  #include "readconf.h"
43    #include "rcs.h"
44    
45  #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)
46  # error No image output format available. Check libgd  # error No image output format available. Check libgd
# Line 84  Line 49 
49    
50  /*#define DEBUG         1*/  /*#define DEBUG         1*/
51    
 #define RLOGCMD         "/usr/bin/rlog"  
 #define DEVNULL         "/dev/null"  
52  #define CONFFILENAME    "cvsgraph.conf"  #define CONFFILENAME    "cvsgraph.conf"
53    
54  #ifndef ETCDIR  #ifndef ETCDIR
# Line 109  Line 72 
72  #define ALIGN_VB        0x20  #define ALIGN_VB        0x20
73  #define ALIGN_VX        0xf0  #define ALIGN_VX        0xf0
74    
 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;  
   
75  /*  /*
76   **************************************************************************   **************************************************************************
77   * Globals   * Globals
78   **************************************************************************   **************************************************************************
79   */   */
80    
 char *rlogcmd = RLOGCMD;  
 char *devnull = DEVNULL;  
   
81  config_t conf;  config_t conf;
82    int debuglevel;
83    
84  /*  /*
85   **************************************************************************   **************************************************************************
86   * Debug routines   * Dubug routines
87   **************************************************************************   **************************************************************************
88   */   */
89  #ifdef DEBUG  void dump_rev(char *p, rev_t *r)
 void dump_revid(const char *s, revid_t *r)  
90  {  {
91          fprintf(stderr, "%s.branch  : '%s'\n", s, r->branch);          printf("%s", p);
92          fprintf(stderr, "%s.rev     : '%s'\n", s, r->rev);          if(r)
93          fprintf(stderr, "%s.isbranch: %d\n", s, r->isbranch);                  printf("'%s', '%s', %d\n", r->rev, r->branch, r->isbranch);
94  }          else
95                    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]);  
96  }  }
97    
98  void dump_branch(branch_t *b)  void dump_id(char *p, char *d)
99  {  {
100          int i;          printf("%s", p);
101          fprintf(stderr, "Branch: '%s'\n", b->branch);          if(d)
102          if(b->tag)                  printf("'%s'\n", d);
103                  dump_tag("branchtag:", b->tag);          else
104          for(i = 0; i < b->nrevs; i++)                  printf("<null>\n");
                 fprintf(stderr, "Branch.Rev: '%s'\n", b->revs[i]->rev->rev);  
105  }  }
106    
107  void dump_log(rcsfilelog_t *r)  void dump_idrev(char *p, idrev_t *t)
108  {  {
109          int i;          printf("%s", p);
110            if(t)
         fprintf(stderr, "Path   : '%s'\n", r->path);  
         fprintf(stderr, "Name   : '%s'\n", r->name);  
         dump_revid("Head", r->head);  
         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]);  
 }  
 #endif  
   
 /*  
  **************************************************************************  
  * Retrieve the log entries  
  **************************************************************************  
  */  
 FILE *get_log(const char *cvsroot, const char *module, const char *file)  
 {  
         pid_t pid;  
         int nul;  
         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())  
         {  
         case -1:        /* Error */  
                 close(nul);  
                 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)  
111          {          {
112                  if(fseek(tmp, 0, SEEK_SET) != (off_t)-1)                  printf("'%s' -> ", t->id);
113                  {                  dump_rev("", t->rev);
                         return tmp;  
                 }  
                 else  
                 {  
                         fclose(tmp);  
                         return NULL;  
                 }  
114          }          }
115          else          else
116                  fclose(tmp);                  printf("<null>\n");
         return NULL;  
117  }  }
118    
119  /*  void dump_tag(char *p, tag_t *t)
  **************************************************************************  
  * Parse the log entries  
  **************************************************************************  
  */  
 char *strip_dup(const char *s)  
120  {  {
121          int l = strlen(s);          printf("%s", p);
122          char *c = xmalloc(l+1);          if(t)
   
         strcpy(c, s);  
         while(*c == ' ' || *c == '\t')  
123          {          {
124                  memmove(c, c+1, l--);                  printf("'%s' -> ", t->tag);
125                    dump_rev("", t->rev);
126          }          }
127          while(l && strchr(" \t\r\n", c[l]))          else
128                  c[l--] = '\0';                  printf("<null>\n");
         return c;  
129  }  }
130    
131  revid_t *make_revid(const char *s)  void dump_delta(char *p, delta_t *d)
132  {  {
133          char *c = strip_dup(s);          int i;
134          char *cptr;          printf("%sdelta.rev   : ", p);
135          int dots = 0;          dump_rev("", d->rev);
136          revid_t *r = xmalloc(sizeof(*r));          printf("%sdelta.date  : %s\n", p, d->date);
137          for(cptr = c; *cptr; cptr++)          printf("%sdelta.author: %s\n", p, d->author);
138          {          printf("%sdelta.state : %s\n", p, d->state);
139                  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  
140          {          {
141                  r->isbranch = 1;                  printf("%sdelta.branch: ", p);
142                  r->branch = c;                  dump_rev("", d->branches->revs[i]);
                 r->rev = xmalloc(strlen(c) + 3);  
                 strcpy(r->rev, c);  
                 strcat(r->rev, ".?");  
143          }          }
144          return r;          printf("%sdelta.next  : ", p);
145            dump_rev("", d->next);
146            printf("\n");
147  }  }
148    
149  char *add_comment(char *c, const char *n)  void dump_dtext(char *p, dtext_t *d)
150  {  {
151          int l;          printf("%sdtext.rev  : ", p);
152          char *r;          dump_rev("", d->rev);
153          assert(n != NULL);          printf("%sdtext.log  : %d bytes\n", p, d->log ? strlen(d->log) : -1);
154          l = strlen(n);          printf("%sdtext.text : %d bytes\n", p, d->text ? strlen(d->text) : -1);
155          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;  
156  }  }
157    
158  int get_line(FILE *fp, char *buf, int maxlen)  void dump_rcsfile(rcsfile_t *rcs)
159  {  {
160          int n;          int i;
161          int seennl;          printf("root   : '%s'\n", rcs->root);
162  retry:          printf("module : '%s'\n", rcs->module);
163          seennl = 0;          printf("file   : '%s'\n", rcs->file);
164          if(!fgets(buf, maxlen, fp))          dump_rev("head   : ", rcs->head);
165                  return feof(fp) ? 0 : -1;          dump_rev("branch : ", rcs->branch);
166          n = strlen(buf);          printf("access :\n");
167          while(n && buf[n-1] == '\n')          for(i = 0; rcs->access && i < rcs->access->nids; i++)
168          {                  dump_id("\t", rcs->access->ids[i]);
169                  seennl = 1;          printf("tags   :\n");
170                  buf[--n] = '\0';          for(i = 0; rcs->tags && i < rcs->tags->ntags; i++)
171          }                  dump_tag("\t", rcs->tags->tags[i]);
172          if(!n)          printf("locks  :%s\n", rcs->strict ? " (strict)" : "");
173                  goto retry;          for(i = 0; rcs->locks && i < rcs->locks->nidrevs; i++)
174          if(!seennl)                  dump_idrev("\t", rcs->locks->idrevs[i]);
175          {          printf("comment: '%s'\n", rcs->comment);
176                  while(fgetc(fp) != '\n')          printf("expand : '%s'\n", rcs->expand ? rcs->expand : "(default -kv)");
177                          ;          printf("deltas :\n");
178          }          for(i = 0; rcs->deltas && i < rcs->deltas->ndeltas; i++)
179          return n;                  dump_delta("\t", rcs->deltas->deltas[i]);
180            printf("desc   : '%s'\n", rcs->desc);
181            printf("dtexts :\n");
182            for(i = 0; rcs->dtexts && i < rcs->dtexts->ndtexts; i++)
183                    dump_dtext("\t", rcs->dtexts->dtexts[i]);
184    
185            fflush(stdout);
186  }  }
187    
188  rcsfilelog_t *parse_log(FILE *fp)  /*
189     **************************************************************************
190     * Read the rcs file
191     **************************************************************************
192     */
193    rcsfile_t *get_rcsfile(const char *cvsroot, const char *module, const char *file)
194  {  {
195          rcsfilelog_t *p;          char *cmd = NULL;
196          int state = 0;          int rv;
         regex_t rerev;  
         regex_t reinfo;  
197    
198          if(regcomp(&rerev, "^revision[ \\t]*[0-9]+(\\.[0-9]+)*", REG_EXTENDED))          cmd = xmalloc(strlen(cvsroot) + strlen(module) + strlen(file) + 2 + 1);
199            sprintf(cmd, "%s/%s/%s", cvsroot, module, file);
200            if(!(rcsin = fopen(cmd, "r")))
201                  return NULL;                  return NULL;
202          if(regcomp(&reinfo, "^date:[^;]*;[ \\t]*author:[^;]*;[ \\t]+state:", REG_EXTENDED))          input_file = cmd;
203          {          line_number = 1;
204                  regfree(&rerev);          rv = rcsparse();
205            fclose(rcsin);
206            if(rv)
207                  return NULL;                  return NULL;
208          }          xfree(cmd);
209          p = xmalloc(sizeof(*p));          input_file = NULL;
210          while(state != 4)          rcsfile->root = xstrdup(cvsroot);
211          {          rcsfile->module = xstrdup(module);
212                  char buf[256];          rcsfile->file = xstrdup(file);
213                  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;  
214  }  }
215    
216  /*  /*
# Line 612  Line 229 
229          return i;          return i;
230  }  }
231    
232  int compare_revid(const revid_t *r1, const revid_t *r2)  int compare_rev(int bcmp, const rev_t *r1, const rev_t *r2)
233  {  {
234          int d1, d2;          int d1, d2;
235            char *c1, *c2;
236          char *v1, *v2;          char *v1, *v2;
237          char *s1, *s2;          char *s1, *s2;
238          int retval = 0;          int retval = 0;
239          assert(r1 != NULL);          assert(r1 != NULL);
         assert(r1->rev != NULL);  
240          assert(r2 != NULL);          assert(r2 != NULL);
241          assert(r2->rev != NULL);          if(bcmp)
242            {
243                    assert(r1->branch != NULL);
244                    assert(r2->branch != NULL);
245                    c1 = r1->branch;
246                    c2 = r2->branch;
247            }
248            else
249            {
250                    assert(r1->rev != NULL);
251                    assert(r2->rev != NULL);
252                    c1 = r1->rev;
253                    c2 = r2->rev;
254            }
255    
256          d1 = count_dots(r1->rev);          d1 = count_dots(c1);
257          d2 = count_dots(r2->rev);          d2 = count_dots(c2);
258          if(d1 != d2)          if(d1 != d2)
259          {          {
260                  return d1 - d2;                  return d1 - d2;
261          }          }
262    
263          s1 = v1 = xstrdup(r1->rev);          s1 = v1 = xstrdup(c1);
264          s2 = v2 = xstrdup(r2->rev);          s2 = v2 = xstrdup(c2);
265          while(1)          while(1)
266          {          {
267                  char *vc1 = strchr(s1, '.');                  char *vc1 = strchr(s1, '.');
# Line 658  Line 288 
288          return retval;          return retval;
289  }  }
290    
291  int tag_sort(const void *t1, const void *t2)  /*
292     **************************************************************************
293     * Reorganise the rcsfile for the branches
294     *
295     * Basically, we have a list of deltas (i.e. administrative info on
296     * revisions) and a list of delta text (the actual logs and diffs).
297     * The deltas are linked through the 'next' and the 'branches' fields
298     * which describe the tree-structure of revisions.
299     * The reorganisation means that we put each delta and corresponding
300     * delta text in a revision structure and assign it to a specific
301     * branch. This is required because we want to be able to calculate
302     * the bounding boxes of each branch. The revisions expand vertically
303     * and the branches expand horizontally.
304     * The reorganisation is performed in these steps:
305     * 1 - sort deltas and detla text on revision number for quick lookup
306     * 2 - start at the denoted head revision:
307     *      * create a branch structure and add this revision
308     *      * for each 'branches' in the delta do:
309     *              - walk all 'branches' of the delta and recursively goto 2
310     *                with the denoted branch delta as new head
311     *              - backlink the newly create sub-branch to the head revision
312     *                so that we can draw them recursively
313     *      * set head to the 'next' field and goto 2 until no next is
314     *        available
315     * 3 - update the administration
316     **************************************************************************
317     */
318    int sort_delta(const void *d1, const void *d2)
319  {  {
320  #define TAGPTR(t)       (*((tag_t **)t))          return compare_rev(0, (*(delta_t **)d1)->rev, (*(delta_t **)d2)->rev);
         return strcmp(TAGPTR(t1)->rev->rev, TAGPTR(t2)->rev->rev);  
 #undef TAGPTR  
321  }  }
322    
323  int rev_sort(const void *v1, const void *v2)  int search_delta(const void *r, const void *d)
324  {  {
325  #define REVPTR(t)       (*((revision_t **)t))          return compare_rev(0, (rev_t *)r, (*(delta_t **)d)->rev);
         return compare_revid(REVPTR(v1)->rev, REVPTR(v2)->rev);  
 #undef REVPTR  
326  }  }
327    
328  int branch_sort(const void *b1, const void *b2)  delta_t *find_delta(delta_t **dl, int n, rev_t *r)
329  {  {
330  #define BPTR(t) (*((branch_t **)t))          delta_t **d;
331          return strcmp(BPTR(b1)->branch, BPTR(b2)->branch);          d = bsearch(r, dl, n, sizeof(*dl), search_delta);
332  #undef BPTR          if(!d)
333                    return NULL;
334            return *d;
335  }  }
336    
337  int rev_cmp(const void *id, const void *v)  int sort_dtext(const void *d1, const void *d2)
338  {  {
339  #define REVPTR(t)       (*((revision_t **)t))          return compare_rev(0, (*(dtext_t **)d1)->rev, (*(dtext_t **)d2)->rev);
         return compare_revid((revid_t *)id, REVPTR(v)->rev);  
 #undef REVPTR  
340  }  }
341    
342  revision_t *find_revision(rcsfilelog_t * rcs, revid_t *id)  int search_dtext(const void *r, const void *d)
343  {  {
344          revision_t **r;          return compare_rev(0, (rev_t *)r, (*(dtext_t **)d)->rev);
345          if(id->isbranch)  }
346                  return NULL;  
347          r = bsearch(id, rcs->revs, rcs->nrevs, sizeof(rcs->revs[0]), rev_cmp);  dtext_t *find_dtext(dtext_t **dl, int n, rev_t *r)
348          if(!r)  {
349            dtext_t **d;
350            d = bsearch(r, dl, n, sizeof(*dl), search_dtext);
351            if(!d)
352                  return NULL;                  return NULL;
353          else          return *d;
                 return *r;  
354  }  }
355    
356  int branch_cmp(const void *s, const void *b)  rev_t *dup_rev(const rev_t *r)
357  {  {
358          return strcmp((const char *)s, (*((branch_t **)b))->branch);          rev_t *t = xmalloc(sizeof(*t));
359            t->rev = xstrdup(r->rev);
360            t->branch = xstrdup(r->branch);
361            t->isbranch = r->isbranch;
362            return t;
363    }
364    
365    branch_t *new_branch(delta_t *d, dtext_t *t)
366    {
367            branch_t *b = xmalloc(sizeof(*b));
368            revision_t *r = xmalloc(sizeof(*r));
369            r->delta = d;
370            r->dtext = t;
371            r->rev = d->rev;
372            r->branch = b;
373            b->branch = dup_rev(d->rev);
374            b->branch->isbranch = 1;
375            b->nrevs = 1;
376            b->revs = xmalloc(sizeof(b->revs[0]));
377            b->revs[0] = r;
378            return b;
379    }
380    
381    revision_t *add_to_branch(branch_t *b, delta_t *d, dtext_t *t)
382    {
383            revision_t *r = xmalloc(sizeof(*r));
384            r->delta = d;
385            r->dtext = t;
386            r->rev = d->rev;
387            r->branch = b;
388            b->revs = xrealloc(b->revs, (b->nrevs+1) * sizeof(b->revs[0]));
389            b->revs[b->nrevs] = r;
390            b->nrevs++;
391            return r;
392  }  }
393    
394  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)
395  {  {
396          branch_t **b;          branch_t *b;
397          b = bsearch(id, rcs->branches, rcs->nbranches, sizeof(rcs->branches[0]), branch_cmp);          dtext_t *text;
398          if(!b)          revision_t *currev;
399                  return NULL;  
400          else          assert(head != NULL);
401                  return *b;  
402            if(head->flag)
403            {
404                    fprintf(stderr, "Circular reference on '%s' in branchpoint\n", head->rev->rev);
405                    return;
406            }
407            head->flag++;
408            text = find_dtext(sdt, nsdt, head->rev);
409    
410            /* Create a new branch for this head */
411            b = new_branch(head, text);
412            *bl = xrealloc(*bl, (*nbl+1)*sizeof((*bl)[0]));
413            (*bl)[*nbl] = b;
414            (*nbl)++;
415            currev = b->revs[0];
416            while(1)
417            {
418                    /* Process all sub-branches */
419                    if(head->branches)
420                    {
421                            int i;
422                            for(i = 0; i < head->branches->nrevs; i++)
423                            {
424                                    delta_t *d = find_delta(sdl, nsdl, head->branches->revs[i]);
425                                    int btag = *nbl;
426                                    if(!d)
427                                            continue;
428                                    build_branch(bl, nbl, sdl, nsdl, sdt, nsdt, d);
429    
430                                    /* Set the new branch's origin */
431                                    (*bl)[btag]->branchpoint = currev;
432    
433                                    /* Add branch to this revision */
434                                    currev->branches = xrealloc(currev->branches, (currev->nbranches+1)*sizeof(currev->branches[0]));
435                                    currev->branches[currev->nbranches] = (*bl)[btag];
436                                    currev->nbranches++;
437                            }
438                    }
439    
440                    /* Walk through the next list */
441                    if(!head->next)
442                            return;
443    
444                    head = find_delta(sdl, nsdl, head->next);
445                    if(!head)
446                    {
447                            fprintf(stderr, "Next revision (%s) not found in deltalist\n", head->next->rev);
448                            return;
449                    }
450                    if(head->flag)
451                    {
452                            fprintf(stderr, "Circular reference on '%s'\n", head->rev->rev);
453                            return;
454                    }
455                    head->flag++;
456                    text = find_dtext(sdt, nsdt, head->rev);
457                    currev = add_to_branch(b, head, text);
458            }
459  }  }
460    
461  tag_t *find_branchtag(rcsfilelog_t * rcs, const char *id)  int reorganise_branches(rcsfile_t *rcs)
462  {  {
463            delta_t **sdelta;
464            int nsdelta;
465            dtext_t **sdtext;
466            int nsdtext;
467            delta_t *head;
468            branch_t **bl;
469            int nbl;
470          int i;          int i;
471          for(i = 0; i < rcs->ntags; i++)  
472            assert(rcs->deltas != NULL);
473            assert(rcs->head != NULL);
474    
475            /* Make a new list for quick lookup */
476            nsdelta = rcs->deltas->ndeltas;
477            sdelta = xmalloc(nsdelta * sizeof(sdelta[0]));
478            memcpy(sdelta, rcs->deltas->deltas, nsdelta * sizeof(sdelta[0]));
479            qsort(sdelta, nsdelta, sizeof(sdelta[0]), sort_delta);
480    
481            /* Do the same for the delta text */
482            nsdtext = rcs->dtexts->ndtexts;
483            sdtext = xmalloc(nsdtext * sizeof(sdtext[0]));
484            memcpy(sdtext, rcs->dtexts->dtexts, nsdtext * sizeof(sdtext[0]));
485            qsort(sdtext, nsdtext, sizeof(sdtext[0]), sort_dtext);
486    
487            /* Start from the head of the trunk */
488            head = find_delta(sdelta, nsdelta, rcs->head);
489            if(!head)
490          {          {
491                  if(!rcs->tags[i]->rev->isbranch)                  fprintf(stderr, "Head revision (%s) not found in deltalist\n", rcs->head->rev);
492                          continue;                  return 0;
493                  if(!strcmp(id, rcs->tags[i]->rev->branch))          }
494                          return rcs->tags[i];          bl = NULL;
495            nbl = 0;
496            build_branch(&bl, &nbl, sdelta, nsdelta, sdtext, nsdtext, head);
497    
498            /* Reverse the head branch */
499            for(i = 0; i < bl[0]->nrevs/2; i++)
500            {
501                    revision_t *r;
502                    r = bl[0]->revs[i];
503                    bl[0]->revs[i] = bl[0]->revs[bl[0]->nrevs-i-1];
504                    bl[0]->revs[bl[0]->nrevs-i-1] = r;
505            }
506    
507            /* Update the branch-number of the head because it was reversed */
508            xfree(bl[0]->branch->branch);
509            bl[0]->branch->branch = xstrdup(bl[0]->revs[0]->rev->branch);
510    
511            /* Keep the admin */
512            rcs->branches = bl;
513            rcs->nbranches = nbl;
514            rcs->sdelta = sdelta;
515            rcs->nsdelta = nsdelta;
516            rcs->sdtext = sdtext;
517            rcs->nsdtext = nsdtext;
518            rcs->active = bl[0];
519            return 1;
520    }
521    
522    /*
523     **************************************************************************
524     * Assign the symbolic tags to the revisions and branches
525     *
526     * The tags point to revision numbers somewhere in the tree structure
527     * of branches and revisions. First we make a sorted list of all
528     * revisions and then we assign each tag to the proper revision.
529     **************************************************************************
530     */
531    int sort_revision(const void *r1, const void *r2)
532    {
533            return compare_rev(0, (*(revision_t **)r1)->delta->rev, (*(revision_t **)r2)->delta->rev);
534    }
535    
536    int search_revision(const void *t, const void *r)
537    {
538            return compare_rev(0, (rev_t *)t, (*(revision_t **)r)->delta->rev);
539    }
540    
541    int sort_branch(const void *b1, const void *b2)
542    {
543            return compare_rev(1, (*(branch_t **)b1)->branch, (*(branch_t **)b2)->branch);
544    }
545    
546    int search_branch(const void *t, const void *b)
547    {
548            return compare_rev(1, (rev_t *)t, (*(branch_t **)b)->branch);
549    }
550    
551    char *previous_rev(const char *c)
552    {
553            int dots = count_dots(c);
554            char *cptr;
555            char *r;
556            if(!dots)
557                    return xstrdup("1.0");  /* FIXME: don't know what the parent is */
558            if(dots & 1)
559            {
560                    /* Is is a revision we want the parent of */
561                    r = xstrdup(c);
562                    cptr = strrchr(r, '.');
563                    assert(cptr != NULL);
564                    if(dots == 1)
565                    {
566                            /* FIXME: What is the parent of 1.1? */
567                            cptr[1] = '\0';
568                            strcat(r, "0");
569                            return r;
570                    }
571                    /* Here we have a "x.x[.x.x]+" case */
572                    *cptr = '\0';
573                    cptr = strrchr(r, '.');
574                    assert(cptr != NULL);
575                    *cptr = '\0';
576                    return r;
577            }
578            /* It is a branch we want the parent of */
579            r = xstrdup(c);
580            cptr = strrchr(r, '.');
581            assert(cptr != NULL);
582            *cptr = '\0';
583            return r;
584    }
585    
586    int assign_tags(rcsfile_t *rcs)
587    {
588            int i;
589            int nr;
590    
591            for(i = nr = 0; i < rcs->nbranches; i++)
592                    nr += rcs->branches[i]->nrevs;
593    
594            rcs->srev = xmalloc(nr * sizeof(rcs->srev[0]));
595            rcs->nsrev = nr;
596            for(i = nr = 0; i < rcs->nbranches; i++)
597            {
598                    memcpy(&rcs->srev[nr], rcs->branches[i]->revs, rcs->branches[i]->nrevs * sizeof(rcs->branches[i]->revs[0]));
599                    nr += rcs->branches[i]->nrevs;
600            }
601    
602            qsort(rcs->srev, rcs->nsrev, sizeof(rcs->srev[0]), sort_revision);
603            qsort(rcs->branches, rcs->nbranches, sizeof(rcs->branches[0]), sort_branch);
604    
605            if(!rcs->branch)
606            {
607                    /* The main trunk is the active trunk */
608                    rcs->tags->tags = xrealloc(rcs->tags->tags, (rcs->tags->ntags+1)*sizeof(rcs->tags->tags[0]));
609                    rcs->tags->tags[rcs->tags->ntags] = xmalloc(sizeof(tag_t));
610                    rcs->tags->tags[rcs->tags->ntags]->tag = xstrdup("MAIN");
611                    rcs->tags->tags[rcs->tags->ntags]->rev = xmalloc(sizeof(rev_t));
612                    rcs->tags->tags[rcs->tags->ntags]->rev->rev = xstrdup(rcs->active->branch->rev);
613                    rcs->tags->tags[rcs->tags->ntags]->rev->branch = xstrdup(rcs->active->branch->branch);
614                    rcs->tags->tags[rcs->tags->ntags]->rev->isbranch = 1;
615                    rcs->tags->ntags++;
616            }
617    
618            /* We should have at least two tags (HEAD and MAIN) */
619            assert(rcs->tags != 0);
620    
621            for(i = 0; i < rcs->tags->ntags; i++)
622            {
623                    tag_t *t = rcs->tags->tags[i];
624                    if(t->rev->isbranch)
625                    {
626                            branch_t **b;
627    add_btag:
628                            b = bsearch(t->rev, rcs->branches, rcs->nbranches, sizeof(rcs->branches[0]), search_branch);
629                            if(!b)
630                            {
631                                    rev_t rev;
632                                    revision_t **r;
633                                    /* This happens for the magic branch numbers if there are
634                                     * no commits withing the new branch yet. So, we add the
635                                     * branch and try continue.
636                                     */
637                                    rev.rev = previous_rev(t->rev->branch);
638                                    rev.branch = NULL;
639                                    rev.isbranch = 0;
640                                    r = bsearch(&rev, rcs->srev, rcs->nsrev, sizeof(rcs->srev[0]), search_revision);
641                                    xfree(rev.rev);
642                                    if(!r)
643                                    {
644                                            fprintf(stderr, "No branch found for tag '%s:%s'\n", t->tag, t->rev->branch);
645                                    }
646                                    else
647                                    {
648                                            rcs->branches = xrealloc(rcs->branches, (rcs->nbranches+1)*sizeof(rcs->branches[0]));
649                                            rcs->branches[rcs->nbranches] = xmalloc(sizeof(branch_t));
650                                            rcs->branches[rcs->nbranches]->branch = dup_rev(t->rev);
651                                            rcs->branches[rcs->nbranches]->branchpoint = *r;
652                                            (*r)->branches = xrealloc((*r)->branches, ((*r)->nbranches+1)*sizeof((*r)->branches[0]));
653                                            (*r)->branches[(*r)->nbranches] = rcs->branches[rcs->nbranches];
654                                            (*r)->nbranches++;
655                                            rcs->nbranches++;
656                                            /* Resort the branches */
657                                            qsort(rcs->branches, rcs->nbranches, sizeof(rcs->branches[0]), sort_branch);
658                                            goto add_btag;
659                                    }
660                            }
661                            else
662                            {
663                                    branch_t *bb = *b;
664                                    bb->tags = xrealloc(bb->tags, (bb->ntags+1)*sizeof(bb->tags[0]));
665                                    bb->tags[bb->ntags] = t;
666                                    bb->ntags++;
667                            }
668                    }
669                    else
670                    {
671                            revision_t **r = bsearch(t->rev, rcs->srev, rcs->nsrev, sizeof(rcs->srev[0]), search_revision);
672                            if(!r)
673                                    fprintf(stderr, "No revision found for tag '%s:%s'\n", t->tag, t->rev->rev);
674                            else
675                            {
676                                    revision_t *rr = *r;
677                                    rr->tags = xrealloc(rr->tags, (rr->ntags+1)*sizeof(rr->tags[0]));
678                                    rr->tags[rr->ntags] = t;
679                                    rr->ntags++;
680                            }
681                    }
682            }
683    
684            /* We need to reset the first in the list of branches to the
685             * active branch to ensure the drawing of all
686             */
687            if(rcs->active != rcs->branches[0])
688            {
689                    branch_t **b = bsearch(rcs->active->branch, rcs->branches, rcs->nbranches, sizeof(rcs->branches[0]), search_branch);
690                    branch_t *t;
691                    assert(b != NULL);
692                    t = *b;
693                    *b = rcs->branches[0];
694                    rcs->branches[0] = t;
695            }
696            return 1;
697    }
698    
699    /*
700     **************************************************************************
701     * String expansion
702     **************************************************************************
703     */
704    static char *_string;
705    static int _nstring;
706    static int _nastring;
707    
708    void add_string_str(const char *s)
709    {
710            int l = strlen(s) + 1;
711            if(_nstring + l > _nastring)
712            {
713                    _nastring += 128;
714                    _string = xrealloc(_string, _nastring * sizeof(_string[0]));
715            }
716            memcpy(_string+_nstring, s, l);
717            _nstring += l-1;
718    }
719    
720    void add_string_ch(int ch)
721    {
722            char buf[2];
723            buf[0] = ch;
724            buf[1] = '\0';
725            add_string_str(buf);
726    }
727    
728    char *expand_string(const char *s, rcsfile_t *rcs, rev_t *rev, tag_t *tag)
729    {
730            char nb[32];
731            char nr[32];
732            char *base;
733    
734            if(!s)
735                    return xstrdup("");
736    
737            _nstring = 0;
738            if(_string)
739                    _string[0] = '\0';
740    
741            sprintf(nb, "%d", rcs->nbranches);
742            sprintf(nr, "%d", rcs->nsrev);
743            for(; *s; s++)
744            {
745                    if(*s == '%')
746                    {
747                            switch(*++s)
748                            {
749                            case 'c': add_string_str(conf.cvsroot); break;
750                            case 'f':
751                            case 'F':
752                                    base = strrchr(rcs->file, '/');
753                                    if(!base)
754                                            add_string_str(rcs->file);
755                                    else
756                                            add_string_str(base+1);
757                                    if(*s == 'F' && _string[_nstring-1] == 'v' && _string[_nstring-2] == ',')
758                                    {
759                                            _nstring -= 2;
760                                            _string[_nstring] = '\0';
761                                    }
762                                    break;
763                            case 'p':
764                                    base = strrchr(rcs->file, '/');
765                                    if(base)
766                                    {
767                                            char *c = xstrdup(rcs->file);
768                                            base = strrchr(c, '/');
769                                            assert(base != NULL);
770                                            base[1] = '\0';
771                                            add_string_str(c);
772                                            xfree(c);
773                                    }
774                                    else
775                                            add_string_str("/");
776                                    break;
777                            case 'm': add_string_str(conf.cvsmodule); break;
778                            case 'r': add_string_str(nr); break;
779                            case 'b': add_string_str(nb); break;
780                            case '%': add_string_ch('%'); break;
781                            case '0': if(conf.expand[0]) add_string_str(conf.expand[0]); break;
782                            case '1': if(conf.expand[1]) add_string_str(conf.expand[1]); break;
783                            case '2': if(conf.expand[2]) add_string_str(conf.expand[2]); break;
784                            case '3': if(conf.expand[3]) add_string_str(conf.expand[3]); break;
785                            case '4': if(conf.expand[4]) add_string_str(conf.expand[4]); break;
786                            case '5': if(conf.expand[5]) add_string_str(conf.expand[5]); break;
787                            case '6': if(conf.expand[6]) add_string_str(conf.expand[6]); break;
788                            case '7': if(conf.expand[7]) add_string_str(conf.expand[7]); break;
789                            case '8': if(conf.expand[8]) add_string_str(conf.expand[8]); break;
790                            case '9': if(conf.expand[9]) add_string_str(conf.expand[9]); break;
791                            case 'R': if(rev && rev->rev) add_string_str(rev->rev); break;
792                            case 'B': if(rev && rev->branch) add_string_str(rev->branch); break;
793                            case 't': if(tag && tag->tag) add_string_str(tag->tag); break;
794                            default:
795                                    add_string_ch('%');
796                                    add_string_ch(*s);
797                                    break;
798                            }
799                    }
800                    else
801                            add_string_ch(*s);
802          }          }
803          return NULL;          return xstrdup(_string);
804  }  }
805    
806  /*  /*
# Line 809  Line 886 
886          int rx = lx + b->w;          int rx = lx + b->w;
887          int yy;          int yy;
888          int i;          int i;
889            /*draw_rbox(im, cx-b->tw/2-1, ty-1, cx+b->tw/2+1, ty+b->th+1, 0, &conf.title_color);*/
890          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);
891          yy = conf.branch_tspace;          yy = conf.branch_tspace;
892          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);
893          yy += get_sheight(b->branch, &conf.branch_font);          yy += get_sheight(b->branch->branch, &conf.branch_font);
894          if(b->tag)          for(i = 0; i < b->ntags; i++)
895          {          {
896                  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);
897                    yy += get_sheight(b->tags[i]->tag, &conf.branch_font);
898          }          }
899    
900          ty += b->h;          ty += b->h;
# Line 828  Line 907 
907          }          }
908  }  }
909    
 static char *_title;  
 static int _ntitle;  
 static int _natitle;  
   
 void add_title_str(const char *s)  
 {  
         int l = strlen(s) + 1;  
         if(_ntitle + l > _natitle)  
         {  
                 _natitle += 128;  
                 _title = xrealloc(_title, _natitle * sizeof(_title[0]));  
         }  
         memcpy(_title+_ntitle, s, l);  
         _ntitle += l-1;  
 }  
   
 void add_title_ch(int ch)  
 {  
         char buf[2];  
         buf[0] = ch;  
         buf[1] = '\0';  
         add_title_str(buf);  
 }  
   
 char *expand_title(rcsfilelog_t *rcs)  
 {  
         char nb[32];  
         char nr[32];  
         char *cptr;  
   
         sprintf(nb, "%d", rcs->nbranches);  
         sprintf(nr, "%d", rcs->nrevs);  
         for(cptr = conf.title; *cptr; cptr++)  
         {  
                 if(*cptr == '%')  
                 {  
                         switch(*++cptr)  
                         {  
                         case 'c': add_title_str(conf.cvsroot); break;  
                         case 'f': add_title_str(rcs->name); break;  
                         case 'm': add_title_str(conf.cvsmodule); break;  
                         case 'r': add_title_str(nr); break;  
                         case 'b': add_title_str(nb); break;  
                         case '%': add_title_ch('%'); break;  
                         default:  
                                 add_title_ch('%');  
                                 add_title_ch(*cptr);  
                                 break;  
                         }  
                 }  
                 else  
                         add_title_ch(*cptr);  
         }  
         return _title;  
 }  
   
910  void draw_title(gdImagePtr im, char *title)  void draw_title(gdImagePtr im, char *title)
911  {  {
912          char *t;          char *t;
# Line 904  Line 927 
927  void draw_connector(gdImagePtr im, branch_t *b)  void draw_connector(gdImagePtr im, branch_t *b)
928  {  {
929          revision_t *r = b->branchpoint;          revision_t *r = b->branchpoint;
930          int x1 = r->x + r->w/2 + 2;          int x1 = r->cx + r->w/2 + 2;
931          int y1 = r->y + r->h/2;          int y1 = r->y + r->h/2;
932          int x2 = b->x;          int x2 = b->cx;
933          int y2 = b->y;          int y2 = b->y;
934          gdImageLine(im, x1, y1, x2, y1, conf.branch_color.id);          gdImageLine(im, x1, y1, x2, y1, conf.branch_color.id);
935          gdImageLine(im, x2, y1, x2, y2, conf.branch_color.id);          gdImageLine(im, x2, y1, x2, y2, conf.branch_color.id);
936  }  }
937    
938  gdImagePtr make_image(rcsfilelog_t *rcs)  gdImagePtr make_image(rcsfile_t *rcs)
939  {  {
940          gdImagePtr im;          gdImagePtr im;
941          int i;          int i;
942            char *cptr;
943    
944          im = gdImageCreate(rcs->tw+conf.margin_left+conf.margin_right, rcs->th+conf.margin_top+conf.margin_bottom);          im = gdImageCreate(rcs->tw+conf.margin_left+conf.margin_right, rcs->th+conf.margin_top+conf.margin_bottom);
945          conf.color_bg.id = gdImageColorAllocate(im, conf.color_bg.r, conf.color_bg.g, conf.color_bg.b);          conf.color_bg.id = gdImageColorAllocate(im, conf.color_bg.r, conf.color_bg.g, conf.color_bg.b);
# Line 926  Line 950 
950          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);
951    
952          for(i = 0; i < rcs->nbranches; i++)          for(i = 0; i < rcs->nbranches; i++)
953                  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]);
954          for(i = 0; i < rcs->nbranches; i++)          for(i = 0; i < rcs->nbranches; i++)
955          {          {
956                  if(rcs->branches[i]->branchpoint)                  if(rcs->branches[i]->branchpoint)
957                          draw_connector(im, rcs->branches[i]);                          draw_connector(im, rcs->branches[i]);
958          }          }
959          draw_title(im, expand_title(rcs));          cptr = expand_string(conf.title, rcs, NULL, NULL);
960            draw_title(im, cptr);
961            xfree(cptr);
962    
963          return im;          return im;
964  }  }
965    
966    /*
967     **************************************************************************
968     * Layout routines
969     **************************************************************************
970     */
971  void move_branch(branch_t *b, int x, int y)  void move_branch(branch_t *b, int x, int y)
972  {  {
973          int i;          int i;
974          b->x += x;          b->cx += x;
975          b->y += y;          b->y += y;
976          for(i = 0; i < b->nrevs; i++)          for(i = 0; i < b->nrevs; i++)
977          {          {
978                  b->revs[i]->x += x;                  b->revs[i]->cx += x;
979                  b->revs[i]->y += y;                  b->revs[i]->y += y;
980          }          }
981  }  }
982    
983    void reposition_branch(revision_t *r, int *x, int *w)
984    {
985            int i, j;
986            for(j = 0; j < r->nbranches; j++)
987            {
988                    branch_t *b = r->branches[j];
989                    *x += *w + conf.rev_minline + b->tw/2 - b->cx;
990                    *w = b->tw/2;
991                    move_branch(b, *x, r->y + r->h/2 + conf.branch_connect);
992                    *x = b->cx;
993                    /* Recurse to move branches of branched revisions */
994                    for(i = b->nrevs-1; i >= 0; i--)
995                    {
996                            reposition_branch(b->revs[i], x, w);
997                    }
998            }
999    }
1000    
1001  void rect_union(int *x, int *y, int *w, int *h, branch_t *b)  void rect_union(int *x, int *y, int *w, int *h, branch_t *b)
1002  {  {
1003          int x1 = *x;          int x1 = *x;
1004          int x2 = x1 + *w;          int x2 = x1 + *w;
1005          int y1 = *y;          int y1 = *y;
1006          int y2 = y1 + *h;          int y2 = y1 + *h;
1007          int xx1 = b->x - b->tw/2;          int xx1 = b->cx - b->tw/2;
1008          int xx2 = xx1 + b->tw;          int xx2 = xx1 + b->tw;
1009          int yy1 = b->y;          int yy1 = b->y;
1010          int yy2 = yy1 + b->th;          int yy2 = yy1 + b->th;
# Line 969  Line 1018 
1018          *h = y2 - y1;          *h = y2 - y1;
1019  }  }
1020    
1021  void make_layout(rcsfilelog_t *rcs)  int branch_intersects(int top, int bottom, int left, branch_t *b)
1022    {
1023            int br = b->cx + b->tw/2;
1024            int bt = b->y - conf.branch_connect - conf.branch_margin/2;
1025            int bb = b->y + b->th + conf.branch_margin/2;
1026            return !(bt > bottom || bb < top || br >= left);
1027    }
1028    
1029    int kern_branch(rcsfile_t *rcs, branch_t *b)
1030    {
1031            int left = b->cx - b->tw/2;
1032            int top = b->y - conf.branch_connect - conf.branch_margin/2;
1033            int bottom = b->y + b->th + conf.branch_margin/2;
1034            int i;
1035            int xpos = 0;
1036    
1037            for(i = 0; i < rcs->nbranches; i++)
1038            {
1039                    branch_t *bp = rcs->branches[i];
1040                    if(bp == b)
1041                            continue;
1042                    if(branch_intersects(top, bottom, left, bp))
1043                    {
1044                            int m = bp->cx + bp->tw/2 + conf.branch_margin;
1045                            if(m > xpos)
1046                                    xpos = m;
1047                    }
1048            }
1049            if(xpos && (b->cx - b->tw/2) - xpos > 0)
1050            {
1051                    move_branch(b, xpos - (b->cx - b->tw/2), 0);
1052                    return 1;
1053            }
1054            return 0;
1055    }
1056    
1057    void make_layout(rcsfile_t *rcs)
1058  {  {
1059          int i, j;          int i, j;
1060          int x, y;          int x, y;
1061          int w, h;          int w, h;
1062          int w2;          int w2;
1063            int moved;
1064    
1065          /* Calculate the box-sizes of the revisions */          /* Calculate the box-sizes of the revisions */
1066          for(i = 0; i < rcs->nrevs; i++)          for(i = 0; i < rcs->nsrev; i++)
1067          {          {
1068                  revision_t *rp;                  revision_t *rp;
1069                  int w;                  int w;
1070                  int h;                  int h;
1071                  rp = rcs->revs[i];                  rp = rcs->srev[i];
1072                  w = get_swidth(rp->rev->rev, &conf.rev_font);                  w = get_swidth(rp->rev->rev, &conf.rev_font);
1073                  h = get_sheight(rp->rev->rev, &conf.rev_font);                  h = get_sheight(rp->rev->rev, &conf.rev_font);
1074                  for(j = 0; j < rp->ntags; j++)                  for(j = 0; j < rp->ntags; j++)
# Line 1001  Line 1087 
1087                  branch_t *bp = rcs->branches[i];                  branch_t *bp = rcs->branches[i];
1088                  int w;                  int w;
1089                  int h;                  int h;
1090                  w = get_swidth(bp->branch, &conf.branch_font);                  w = get_swidth(bp->branch->branch, &conf.branch_font);
1091                  h = get_sheight(bp->branch, &conf.branch_font);                  h = get_sheight(bp->branch->branch, &conf.branch_font);
1092                  if(bp->tag)                  for(j = 0; j < bp->ntags; j++)
1093                  {                  {
1094                          int ww = get_swidth(bp->tag->tag, &conf.branch_font);                          int ww = get_swidth(bp->tags[j]->tag, &conf.branch_font);
1095                          if(ww > w) w = ww;                          if(ww > w) w = ww;
1096                          h += get_sheight(bp->tag->tag, &conf.branch_font) + conf.branch_separator;                          h += get_sheight(bp->tags[j]->tag, &conf.branch_font);
1097                  }                  }
1098                  w += conf.branch_lspace + conf.branch_rspace;                  w += conf.branch_lspace + conf.branch_rspace;
1099                  h += conf.branch_tspace + conf.branch_bspace;                  h += conf.branch_tspace + conf.branch_bspace;
# Line 1029  Line 1115 
1115                  branch_t *b = rcs->branches[i];                  branch_t *b = rcs->branches[i];
1116                  x = b->tw/2;                  x = b->tw/2;
1117                  y = b->h;                  y = b->h;
1118                  b->x = x;                  b->cx = x;
1119                  b->y = 0;                  b->y = 0;
1120                  for(j = 0; j < b->nrevs; j++)                  for(j = 0; j < b->nrevs; j++)
1121                  {                  {
1122                          y += conf.rev_minline;                          y += conf.rev_minline;
1123                          b->revs[j]->x = x;                          b->revs[j]->cx = x;
1124                          b->revs[j]->y = y;                          b->revs[j]->y = y;
1125                          y += b->revs[j]->h;                          y += b->revs[j]->h;
1126                  }                  }
1127          }          }
1128    
1129          /* Reposition the branches FIXME: Should be recursive on branchpoint revisions within branches... */          /* Reposition the branches */
1130          x = rcs->branches[0]->x;          x = rcs->branches[0]->cx;
1131          w2 = rcs->branches[0]->tw / 2;          w2 = rcs->branches[0]->tw / 2;
1132          for(i = rcs->branches[0]->nrevs-1; i >= 0; i--)          for(i = rcs->branches[0]->nrevs-1; i >= 0; i--)
1133          {          {
1134                  revision_t *r = rcs->branches[0]->revs[i];                  reposition_branch(rcs->branches[0]->revs[i], &x, &w2);
1135                  for(j = 0; j < r->nbranches; j++)          }
1136    
1137            /* Try to move branches left if there is room (kerning) */
1138            for(moved = 1; moved; )
1139            {
1140                    moved = 0;
1141                    for(i = 1; i < rcs->nbranches; i++)
1142                  {                  {
1143                          branch_t *b = r->branches[j];                          moved += kern_branch(rcs, rcs->branches[i]);
                         x += w2 + conf.rev_minline + b->tw/2 - b->x;  
                         w2 = b->tw/2;  
                         move_branch(b, x, r->y + r->h);  
                         x = b->x;  
1144                  }                  }
1145          }          }
1146    
1147            /* Move everything w.r.t. the top-left margin */
1148          for(i = 0; i < rcs->nbranches; i++)          for(i = 0; i < rcs->nbranches; i++)
1149                  move_branch(rcs->branches[i], conf.margin_left, conf.margin_top);                  move_branch(rcs->branches[i], conf.margin_left, conf.margin_top);
1150    
1151          /* Calculate overall image size */          /* Calculate overall image size */
1152          x = rcs->branches[0]->x - rcs->branches[0]->tw/2;          x = rcs->branches[0]->cx - rcs->branches[0]->tw/2;
1153          y = rcs->branches[0]->y;          y = rcs->branches[0]->y;
1154          w = rcs->branches[0]->tw;          w = rcs->branches[0]->tw;
1155          h = rcs->branches[0]->th;          h = rcs->branches[0]->th;
# Line 1072  Line 1161 
1161    
1162  /*  /*
1163   **************************************************************************   **************************************************************************
1164     * Imagemap functions
1165     **************************************************************************
1166     */
1167    void make_imagemap(rcsfile_t *rcs, FILE *fp)
1168    {
1169            int i, j;
1170            char *href;
1171            char *alt;
1172            fprintf(fp, "<map name=\"%s\">\n", conf.map_name);
1173            for(i = 0; i < rcs->nbranches; i++)
1174            {
1175                    branch_t *b = rcs->branches[i];
1176                    tag_t *tag = b->ntags ? b->tags[0] : NULL;
1177                    href = expand_string(conf.map_branch_href, rcs, b->branch, tag);
1178                    alt = expand_string(conf.map_branch_alt, rcs, b->branch, tag);
1179                    fprintf(fp, "\t<area shape=\"rect\" %s coords=\"%d,%d,%d,%d\" %s>\n",
1180                                    href,
1181                                    b->cx - b->w/2, b->y, b->cx + b->w/2, b->y + b->h,
1182                                    alt);
1183                    xfree(href);
1184                    xfree(alt);
1185                    for(j = 0; j < b->nrevs; j++)
1186                    {
1187                            revision_t *r = b->revs[j];
1188                            tag = r->ntags ? r->tags[0] : NULL;
1189                            href = expand_string(conf.map_rev_href, rcs, r->rev, tag);
1190                            alt = expand_string(conf.map_rev_alt, rcs, r->rev, tag);
1191                            fprintf(fp, "\t<area shape=\"rect\" %s coords=\"%d,%d,%d,%d\" %s>\n",
1192                                    href,
1193                                    r->cx - r->w/2, r->y, r->cx + r->w/2, r->y + r->h,
1194                                    alt);
1195                            xfree(href);
1196                            xfree(alt);
1197                    }
1198            }
1199            fprintf(fp, "</map>\n");
1200    }
1201    
1202    /*
1203     **************************************************************************
1204   * Configuration   * Configuration
1205   **************************************************************************   **************************************************************************
1206   */   */
# Line 1079  Line 1208 
1208  {  {
1209          FILE *fp;          FILE *fp;
1210          int r;          int r;
1211    
1212          if(path)          if(path)
1213          {          {
1214                  if((fp = fopen(path, "r")) == NULL)                  if((fp = fopen(path, "r")) == NULL)
1215                  {                  {
1216                          return 0;                          return 0;
1217                  }                  }
1218                    else
1219                            input_file = path;
1220          }          }
1221          else          else
1222          {          {
# Line 1094  Line 1226 
1226                          {                          {
1227                                  return 0;                                  return 0;
1228                          }                          }
1229                            else
1230                                    input_file = ETCDIR "/" CONFFILENAME;
1231                  }                  }
1232                    else
1233                            input_file = "./" CONFFILENAME;
1234          }          }
1235    
1236          yyin = fp;          yyin = fp;
1237          r = yyparse();          r = yyparse();
1238          fclose(fp);          fclose(fp);
1239            input_file = NULL;
1240          return r == 0;          return r == 0;
1241  }  }
1242    
# Line 1110  Line 1247 
1247   */   */
1248  static const char usage_str[] =  static const char usage_str[] =
1249          "Usage: cvsgraph [options] <file>\n"          "Usage: cvsgraph [options] <file>\n"
1250          "  -c <file>  Read alternative config from <file>\n"          "  -c <file>    Read alternative config from <file>\n"
1251          "  -h         This message\n"          "  -d <level>   Enable debug mode at <level>\n"
1252          "  -m <mod>   Use <mod> as cvs module\n"          "  -h           This message\n"
1253          "  -o <file>  Output to <file>\n"          "  -i           Generate an imagemap instead of image\n"
1254          "  -r <path>  Use <path> as cvsroot path\n"          "  -M <name>    Use <name> as imagemap name\n"
1255          "  -V         Print version and exit\n"          "  -m <mod>     Use <mod> as cvs module\n"
1256            "  -o <file>    Output to <file>\n"
1257            "  -q           Be quiet (i.e. no warnings)\n"
1258            "  -r <path>    Use <path> as cvsroot path\n"
1259            "  -V           Print version and exit\n"
1260            "  -[0-9] <txt> Use <txt> for expansion\n"
1261          ;          ;
1262    
1263  #define VERSION_STR     "1.0.0"  #define VERSION_STR     "1.1.0"
1264  #define NOTICE_STR      "Copyright (c) 2001 B.Stultiens"  #define NOTICE_STR      "Copyright (c) 2001 B.Stultiens"
1265    
 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++;  
 }  
   
1266  int main(int argc, char *argv[])  int main(int argc, char *argv[])
1267  {  {
1268            extern int yy_flex_debug;
1269            extern int rcs_flex_debug;
1270            extern int yydebug;
1271            extern int rcsdebug;
1272          int optc;          int optc;
1273          char *confpath = NULL;          char *confpath = NULL;
1274          char *outfile = NULL;          char *outfile = NULL;
1275          char *cvsroot = NULL;          char *cvsroot = NULL;
1276          char *cvsmodule = NULL;          char *cvsmodule = NULL;
1277            int imagemap = 0;
1278            char *imgmapname = NULL;
1279          int lose = 0;          int lose = 0;
1280          FILE *fp;          FILE *fp;
1281          int n;          rcsfile_t *rcs;
         rcsfilelog_t *rcs;  
1282          gdImagePtr im;          gdImagePtr im;
1283    
1284  #ifdef DEBUG          while((optc = getopt(argc, argv, "0:1:2:3:4:5:6:7:8:9: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:hm:o:r:V")) != EOF)  
1285          {          {
1286                  switch(optc)                  switch(optc)
1287                  {                  {
1288                  case 'c':                  case 'c':
1289                          confpath = xstrdup(optarg);                          confpath = xstrdup(optarg);
1290                          break;                          break;
1291                    case 'd':
1292                            debuglevel = strtol(optarg, NULL, 0);
1293                            break;
1294                    case 'i':
1295                            imagemap = 1;
1296                            break;
1297                    case 'M':
1298                            imgmapname = xstrdup(optarg);
1299                            break;
1300                  case 'm':                  case 'm':
1301                          cvsmodule = xstrdup(optarg);                          cvsmodule = xstrdup(optarg);
1302                          break;                          break;
1303                  case 'o':                  case 'o':
1304                          outfile = xstrdup(optarg);                          outfile = xstrdup(optarg);
1305                          break;                          break;
1306                    case 'q':
1307                            quiet = 1;
1308                            break;
1309                  case 'r':                  case 'r':
1310                          cvsroot = xstrdup(optarg);                          cvsroot = xstrdup(optarg);
1311                          break;                          break;
# Line 1171  Line 1316 
1316                          fprintf(stdout, "%s", usage_str);                          fprintf(stdout, "%s", usage_str);
1317                          return 0;                          return 0;
1318                  default:                  default:
1319                          lose++;                          if(isdigit(optc))
1320                            {
1321                                    conf.expand[optc-'0'] = xstrdup(optarg);
1322                            }
1323                            else
1324                                    lose++;
1325                  }                  }
1326          }          }
1327    
# Line 1187  Line 1337 
1337                  return 1;                  return 1;
1338          }          }
1339    
1340            if(debuglevel)
1341            {
1342                    setvbuf(stdout, NULL, 0, _IONBF);
1343                    setvbuf(stderr, NULL, 0, _IONBF);
1344            }
1345            yy_flex_debug = (debuglevel & DEBUG_CONF_LEX) != 0;
1346            rcs_flex_debug = (debuglevel & DEBUG_RCS_LEX) != 0;
1347            yydebug = (debuglevel & DEBUG_CONF_YACC) != 0;
1348            rcsdebug = (debuglevel & DEBUG_RCS_YACC) != 0;
1349    
1350          /* Set defaults */          /* Set defaults */
1351          if(!conf.tag_font)      conf.tag_font = gdFontTiny;          if(!conf.tag_font)      conf.tag_font = gdFontTiny;
1352          if(!conf.rev_font)      conf.rev_font = gdFontTiny;          if(!conf.rev_font)      conf.rev_font = gdFontTiny;
# Line 1200  Line 1360 
1360          }          }
1361    
1362          /* Set overrides */          /* Set overrides */
1363          if(cvsroot)             conf.cvsroot = cvsroot;          if(cvsroot)     conf.cvsroot = cvsroot;
1364          if(cvsmodule)           conf.cvsmodule = cvsmodule;          if(cvsmodule)   conf.cvsmodule = cvsmodule;
1365            if(imgmapname)  conf.map_name = imgmapname;
         if((fp = get_log(conf.cvsroot, conf.cvsmodule, argv[optind])) == NULL)  
         {  
                 fprintf(stderr, "Error getting log for '%s'\n", argv[optind]);  
                 return 1;  
         }  
1366    
1367          rcs = parse_log(fp);          rcs = get_rcsfile(conf.cvsroot, conf.cvsmodule, argv[optind]);
1368          if(!rcs)          if(!rcs)
1369          {          {
1370                  fprintf(stderr, "Error parsing log\n");                  fprintf(stderr, "Error reading rcs-file\n");
1371                  return 1;                  return 1;
1372          }          }
         fclose(fp);  
   
         /* Add implicit tags */  
         add_tag(rcs, "HEAD", rcs->head->rev);  
         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);  
   
         /* Assign tags to revisions */  
         for(n = 0; n < rcs->ntags; n++)  
         {  
                 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++;  
         }  
1373    
1374          /* Isolate the branches */          if(debuglevel & DEBUG_RCS_FILE)
1375          for(n = 0; n < rcs->nrevs; n++)                  dump_rcsfile(rcs);
         {  
                 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++;  
         }  
1376    
1377          /* Find the branchpoints */          if(!reorganise_branches(rcs))
1378          for(n = 0; n < rcs->nbranches; n++)                  return 1;
         {  
                 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++;  
         }  
1379    
1380          make_layout(rcs);          if(!assign_tags(rcs))
1381                    return 1;
1382    
 #ifdef DEBUG  
         dump_log(rcs);  
 #endif  
         im = make_image(rcs);  
1383          if(outfile)          if(outfile)
1384          {          {
1385                  if((fp = fopen(outfile, "w")) == NULL)                  if((fp = fopen(outfile, "w")) == NULL)
# Line 1298  Line 1391 
1391          else          else
1392                  fp = stdout;                  fp = stdout;
1393    
1394          switch(conf.image_type)          make_layout(rcs);
1395    
1396            if(!imagemap)
1397          {          {
1398                    /* Create an image */
1399                    im = make_image(rcs);
1400    
1401                    switch(conf.image_type)
1402                    {
1403  #ifdef HAVE_IMAGE_GIF  #ifdef HAVE_IMAGE_GIF
1404          default:  # ifndef HAVE_IMAGE_PNG
1405          case IMAGE_GIF:                  default:
1406                  gdImageGif(im, fp);  # endif
1407                  break;                  case IMAGE_GIF:
1408                            gdImageGif(im, fp);
1409                            break;
1410  #endif  #endif
1411  #ifdef HAVE_IMAGE_PNG  #ifdef HAVE_IMAGE_PNG
1412  # ifndef HAVE_IMAGE_GIF                  default:
1413          default:                  case IMAGE_PNG:
1414  # endif                          gdImagePng(im, fp);
1415          case IMAGE_PNG:                          break;
                 gdImagePng(im, fp);  
                 break;  
1416  #endif  #endif
1417  #ifdef HAVE_IMAGE_JPEG  #ifdef HAVE_IMAGE_JPEG
1418  # if !defined(HAVE_IMAGE_GIF) && !defined(HAVE_IMAGE_PNG)  # if !defined(HAVE_IMAGE_GIF) && !defined(HAVE_IMAGE_PNG)
1419          default:                  default:
1420  # endif  # endif
1421          case IMAGE_JPEG:                  case IMAGE_JPEG:
1422                  gdImageJpeg(im, fp, conf.image_quality);                          gdImageJpeg(im, fp, conf.image_quality);
1423                  break;                          break;
1424  #endif  #endif
1425                    }
1426    
1427                    gdImageDestroy(im);
1428            }
1429            else
1430            {
1431                    /* Create an imagemap */
1432                    make_imagemap(rcs, fp);
1433          }          }
1434    
1435          if(outfile)          if(outfile)
1436                  fclose(fp);                  fclose(fp);
1437          gdImageDestroy(im);  
1438          return 0;          return 0;
1439  }  }
1440    

Legend:
Removed from v.1.5  
changed lines
  Added in v.1.8

  ViewVC Help
Powered by ViewVC 1.1.0 with CvsGraph 1.7.0