/* This software may only be used by you under license from AT&T Corp. ("AT&T"). A copy of AT&T's Source Code Agreement is available at AT&T's Internet website having the URL: If you received this software without first entering into a license with AT&T, you have an infringing copy of this software and cannot use it without violating AT&T's intellectual property rights. */ #pragma prototyped /* * graphics code generator */ #include "render.h" #include "xbuf.h" #include "utils.h" int Obj; static int N_pages = 1; /* w.r.t. unrotated coords */ static int Page; /* w.r.t. unrotated coords */ static int Layer,Nlayers; static char **LayerID; static point First,Major,Minor; static point Pages; static box PB; /* drawable region in device coords */ static pointf GP; /* graph page size, in graph coords */ static box CB; /* current page box, in graph coords */ static point PFC; /* device page box for centering */ static double Deffontsize; static char *Deffontname; static char Layerdelims[] = ":\t "; static attrsym_t* G_peripheries; static int write_edge_test(Agraph_t *g, Agedge_t *e) { Agraph_t *subg; int c; for(c = 1; c <= GD_n_cluster(g); c++) { subg = GD_clust(g)[c]; if(agcontains(subg, e)) return FALSE; } return TRUE; } static int write_node_test(Agraph_t *g, Agnode_t *n) { Agraph_t *subg; int c; for(c = 1; c <= GD_n_cluster(g); c++) { subg = GD_clust(g)[c]; if(agcontains(subg, n)) return FALSE; } return TRUE; } void emit_reset(Agraph_t* g) { Agnode_t *n; N_pages = 1; Page = 0; Layer = Nlayers = 0; LayerID = (char **) 0; First.x = First.y = 0; Major.x = Major.y = 0; Minor.x = Minor.y = 0; Pages.x = Pages.y = 0; PB.LL.x = PB.LL.y = PB.UR.x = PB.UR.y = 0; GP.x = GP.y = 0; CB.LL.x = CB.LL.y = CB.UR.x = CB.UR.y = 0; PFC.x = PFC.y = 0; Deffontsize = 0; Deffontname = (char *) 0; for (n = agfstnode(g); n; n = agnxtnode(g,n)) { ND_state(n) = 0; } if(CodeGen) CodeGen->reset(); } void emit_graph(graph_t* g, int flags) { point curpage; graph_t *subg; node_t *n; edge_t *e; int c; char *str; G_peripheries = agfindattr(g,"peripheries"); emit_header(g); if (flags & EMIT_COLORS) { CodeGen->set_fillcolor(DEFAULT_FILL); if (((str = agget(g,"bgcolor")) != 0) && str[0]) CodeGen->set_fillcolor(str); if (((str = agget(g,"fontcolor")) != 0) && str[0]) CodeGen->set_pencolor(str); for (c = 1; c <= GD_n_cluster(g); c++) { subg = GD_clust(g)[c]; if (((str = agget(subg,"color")) != 0) && str[0]) CodeGen->set_pencolor(str); if (((str = agget(subg,"fillcolor")) != 0) && str[0]) CodeGen->set_fillcolor(str); if (((str = agget(subg,"fontcolor")) != 0) && str[0]) CodeGen->set_pencolor(str); } for (n = agfstnode(g); n; n = agnxtnode(g,n)) { if (((str = agget(n,"color")) != 0) && str[0]) CodeGen->set_pencolor(str); if (((str = agget(n,"fillcolor")) != 0) && str[0]) CodeGen->set_fillcolor(str); if (((str = agget(n,"fontcolor")) != 0) && str[0]) CodeGen->set_pencolor(str); for (e = agfstout(g,n); e; e = agnxtout(g,e)) { if (((str = agget(e,"color")) != 0) && str[0]) CodeGen->set_pencolor(str); if (((str = agget(e,"fontcolor")) != 0) && str[0]) CodeGen->set_pencolor(str); } } } Layer = 1; do { if (Nlayers > 0) emit_layer(Layer); for (curpage = First; validpage(curpage); curpage = pageincr(curpage)) { Obj = NONE; setup_page(g,curpage); if (GD_label(g)) emit_label(GD_label(g),g); Obj = CLST; /* when drawing, lay clusters down before nodes and edges */ if (!(flags & EMIT_CLUSTERS_LAST)) { emit_clusters(g,flags); } if (flags & EMIT_SORTED) { /* output all nodes, then all edges */ Obj = NODE; CodeGen->begin_nodes(); for (n = agfstnode(g); n; n = agnxtnode(g,n)) { emit_node(n); } CodeGen->end_nodes(); Obj = EDGE; CodeGen->begin_edges(); for (n = agfstnode(g); n; n = agnxtnode(g,n)) { for (e = agfstout(g,n); e; e = agnxtout(g,e)) { emit_edge(e); } } CodeGen->end_edges(); } else if (flags & EMIT_EDGE_SORTED) { /* output all edges, then all nodes */ Obj = EDGE; CodeGen->begin_edges(); for (n = agfstnode(g); n; n = agnxtnode(g,n)) { for (e = agfstout(g,n); e; e = agnxtout(g,e)) { emit_edge(e); } } CodeGen->end_edges(); Obj = NODE; CodeGen->begin_nodes(); for (n = agfstnode(g); n; n = agnxtnode(g,n)) { emit_node(n); } CodeGen->end_nodes(); } else if(flags & EMIT_PREORDER) { Obj = NODE; CodeGen->begin_nodes(); for(n = agfstnode(g); n; n = agnxtnode(g, n)) { if(write_node_test(g, n)) emit_node(n); } CodeGen->end_nodes(); Obj = EDGE; CodeGen->begin_edges(); for(n = agfstnode(g); n; n = agnxtnode(g, n)) { for(e = agfstout(g, n); e; e = agnxtout(g, e)) { if(write_edge_test(g, e)) emit_edge(e); } } CodeGen->end_edges(); } else { /* output in breadth first graph walk order */ for (n = agfstnode(g); n; n = agnxtnode(g,n)) { Obj = NODE; emit_node(n); for (e = agfstout(g,n); e; e = agnxtout(g,e)) { Obj = NODE; emit_node(e->head); Obj = EDGE; emit_edge(e); } } } /* when mapping, detect events on clusters after nodes and edges */ if (flags & EMIT_CLUSTERS_LAST) { emit_clusters(g,flags); } Obj = NONE; CodeGen->end_page(); } Layer++; } while (Layer <= Nlayers); emit_trailer(); } void emit_eof(void) { if (Page > 0) CodeGen->end_job(); } void emit_clusters(graph_t* g, int flags) { int i,c,filled; graph_t *subg; point A[4]; char *str,**style; node_t *n; edge_t *e; for (c = 1; c <= GD_n_cluster(g); c++) { subg = GD_clust(g)[c]; if (clust_in_layer(subg) == FALSE) continue; /* when mapping, detect events on clusters after sub_clusters */ if (flags & EMIT_CLUSTERS_LAST) { emit_clusters(subg,flags); } Obj = CLST; CodeGen->begin_cluster(subg); CodeGen->begin_context(); filled = FALSE; if (((str = agget(subg,"style")) != 0) && str[0]) { CodeGen->set_style(style = parse_style(str)); for (i = 0; style[i]; i++) if (strcmp(style[i],"filled")==0) {filled = TRUE; break;} } if (((str = agget(subg,"pencolor")) != 0) && str[0]) CodeGen->set_pencolor(str); else if (((str = agget(subg,"color")) != 0) && str[0]) CodeGen->set_pencolor(str); /* bgcolor is supported for backward compatability */ else if (((str = agget(subg,"bgcolor")) != 0) && str[0]) CodeGen->set_pencolor(str); str = 0; if (((str = agget(subg,"fillcolor")) != 0) && str[0]) CodeGen->set_fillcolor(str); else if (((str = agget(subg,"color")) != 0) && str[0]) CodeGen->set_fillcolor(str); /* bgcolor is supported for backward compatability */ else if (((str = agget(subg,"bgcolor")) != 0) && str[0]) { filled = TRUE; CodeGen->set_fillcolor(str); } A[0] = GD_bb(subg).LL; A[2] = GD_bb(subg).UR; A[1].x = A[2].x; A[1].y = A[0].y; A[3].x = A[0].x; A[3].y = A[2].y; if (late_int(subg,G_peripheries,1,0)) { CodeGen->polygon(A,4,filled); } else if (filled) { CodeGen->set_pencolor(str); CodeGen->polygon(A,4,filled); } if (GD_label(subg)) emit_label(GD_label(subg),subg); if(flags & EMIT_PREORDER) { for(n = agfstnode(subg); n; n = agnxtnode(subg, n)) { Obj = NODE; emit_node(n); for(e = agfstout(subg, n); e; e = agnxtout(subg, e)) { Obj = EDGE; emit_edge(e); } } Obj = NONE; } CodeGen->end_context(); CodeGen->end_cluster(); /* when drawing, lay down clusters before sub_clusters */ if (!(flags & EMIT_CLUSTERS_LAST)) { emit_clusters(subg,flags); } } } void emit_node(node_t* n) { if (ND_shape(n) == NULL) return; if (node_in_layer(n->graph,n) && node_in_CB(n) && (ND_state(n) != Page)) { ND_shape(n)->codefn(n); ND_state(n) = Page; } } void emit_edge(edge_t* e) { int i; char *color,*style; char **styles = 0; char **sp; bezier bz; boolean saved = FALSE; double scale; double theta; char *p; if ((edge_in_CB(e) == FALSE) || (edge_in_layer(e->head->graph,e) == FALSE)) return; CodeGen->begin_edge(e); style = late_string(e,E_style,""); /* We shortcircuit drawing an invisible edge because the arrowhead * code resets the style to solid, and most of the code generators * (except PostScript) won't honor a previous style of invis. */ if (style[0]) { styles = parse_style(style); sp = styles; while ((p = *sp++)) { if (streq(p, "invis")) { CodeGen->end_edge(); return; } } } color = late_string(e,E_color,""); scale = late_double(e,E_arrowsz,1.0,0.0); if (color[0] || styles) { CodeGen->begin_context(); if (styles) CodeGen->set_style(styles); if (color[0]) { CodeGen->set_pencolor(color); CodeGen->set_fillcolor(color); } saved = TRUE; } if (ED_spl(e)) { for (i = 0; i < ED_spl(e)->size; i++) { bz = ED_spl(e)->list[i]; if (NOT(codegen_bezier_has_arrows())) { CodeGen->beziercurve(bz.list,bz.size,FALSE,FALSE); /* CodeGen->beziercurve(bz.list,bz.size,bz.sflag,bz.eflag); */ /* vladimir: added sflag/eflag, arrowheads are made here */ if (bz.sflag) { /* dds: open style has sp == list[0] */ if ((bz.sflag == ARR_OPEN) || (bz.sflag == ARR_HALFOPEN)) theta = atan2pt(bz.list[1],bz.list[0]); else theta = atan2pt(bz.list[0],bz.sp); arrow_gen(bz.sp, DEGREES(theta), scale, bz.sflag); } if (bz.eflag) { /* dds: open style has ep == list[size-1] */ if ((bz.eflag == ARR_OPEN) || (bz.eflag == ARR_HALFOPEN)) theta = atan2pt(bz.list[bz.size-2],bz.list[bz.size-1]); else theta = atan2pt(bz.list[bz.size-1],bz.ep); arrow_gen(bz.ep, DEGREES(theta), scale, bz.eflag); } } else CodeGen->beziercurve(bz.list,bz.size,bz.sflag,bz.eflag); } } if (ED_label(e)) { emit_label(ED_label(e),e->tail->graph); if (mapbool(late_string(e,E_decorate,"false")) && ED_spl(e)) emit_attachment(ED_label(e),ED_spl(e)); } if (ED_head_label(e)) emit_label(ED_head_label(e),e->tail->graph); /* vladimir */ if (ED_tail_label(e)) emit_label(ED_tail_label(e),e->tail->graph); /* vladimir */ if (saved) CodeGen->end_context(); CodeGen->end_edge(); } int node_in_CB(node_t* n) { box nb; if (N_pages == 1) return TRUE; nb.LL.x = ND_coord_i(n).x - ND_lw_i(n); nb.LL.y = ND_coord_i(n).y - ND_ht_i(n)/2; nb.UR.x = ND_coord_i(n).x + ND_rw_i(n); nb.UR.y = ND_coord_i(n).y + ND_ht_i(n)/2; return rect_overlap(CB,nb); } int node_in_layer(graph_t* g, node_t* n) { char *pn,*pe; edge_t *e; if (Nlayers <= 0) return TRUE; pn = late_string(n,N_layer,""); if (selectedlayer(pn)) return TRUE; if (pn[0]) return FALSE; /* Only check edges if pn = "" */ if ((e = agfstedge(g,n)) == NULL) return TRUE; for (e = agfstedge(g,n); e; e = agnxtedge(g,e,n)) { pe = late_string(e,E_layer,""); if ((pe[0] == '\0') || selectedlayer(pe)) return TRUE; } return FALSE; } int edge_in_layer(graph_t* g, edge_t* e) { char *pe,*pn; int cnt; if (Nlayers <= 0) return TRUE; pe = late_string(e,E_layer,""); if (selectedlayer(pe)) return TRUE; if (pe[0]) return FALSE; for (cnt = 0; cnt < 2; cnt++) { pn = late_string(cnt < 1? e->tail:e->head,N_layer,""); if ((pn[0] == '\0') || selectedlayer(pn)) return TRUE; } return FALSE; } int clust_in_layer(graph_t* subg) { char *pg; node_t *n; if (Nlayers <= 0) return TRUE; pg = late_string(subg,agfindattr(subg,"layer"),""); if (selectedlayer(pg)) return TRUE; if (pg[0]) return FALSE; for (n = agfstnode(subg); n; n = agnxtnode(subg,n)) if (node_in_layer(subg,n)) return TRUE; return FALSE; } int edge_in_CB(edge_t* e) { int i,j,np; bezier bz; point *p,pp,sz; box b; textlabel_t *lp; if (N_pages == 1) return TRUE; if (ED_spl(e) == NULL) return FALSE; for (i = 0; i < ED_spl(e)->size; i++) { bz = ED_spl(e)->list[i]; np = bz.size; p = bz.list; pp = p[0]; for (j = 0; j < np; j++) { if (rect_overlap(CB,mkbox(pp,p[j]))) return TRUE; pp = p[j]; } } if ((lp = ED_label(e)) == NULL) return FALSE; sz = cvt2pt(lp->dimen); b.LL.x = lp->p.x - sz.x / 2; b.UR.x = lp->p.x + sz.x / 2; b.LL.y = lp->p.y - sz.y / 2; b.UR.y = lp->p.y + sz.y / 2; return rect_overlap(CB,b); } void emit_attachment(textlabel_t* lp, splines* spl) { point sz,A[3]; char *s; for (s = lp->text; *s; s++) if (isspace(*s) == FALSE) break; if (*s == 0) return; sz = cvt2pt(lp->dimen); A[0] = pointof(lp->p.x + sz.x/2,lp->p.y - sz.y/2); A[1] = pointof(A[0].x - sz.x, A[0].y); A[2] = dotneato_closest(spl,lp->p); CodeGen->polyline(A,3); } void emit_header(graph_t* g) { char *user; setup_graph(g); if (Page == 0) { user = username(); CodeGen->begin_job(Output_file,g,Lib,user,Info,Pages); } CodeGen->begin_graph(g,PB,PFC); } void emit_trailer(void) { CodeGen->end_graph(); } point pagecode(char c) { point rv; rv.x = rv.y = 0; switch(c) { case 'T': First.y = Pages.y - 1; rv.y = -1; break; case 'B': rv.y = 1; break; case 'L': rv.x = 1; break; case 'R': First.x = Pages.x - 1; rv.x = -1; break; } return rv; } static void set_pagedir(graph_t* g) { char *str; Major.x = Major.y = Minor.x = Minor.y = 0; str = agget(g,"pagedir"); if (str && str[0]) { Major = pagecode(str[0]); Minor = pagecode(str[1]); } if ((abs(Major.x + Minor.x) != 1) || (abs(Major.y + Minor.y) != 1)) { Major.x = 0; Major.y = 1; Minor.x = 1; Minor.y = 0; First.x = First.y = 0; if (str) agerr (AGWARN, "pagedir=%s ignored\n",str); } } int validpage(point page) { return ((page.x >= 0) && (page.x < Pages.x) && (page.y >= 0) && (page.y < Pages.y)); } int layerindex(char* tok) { int i; for (i = 1; i <= Nlayers; i++) if (streq(tok,LayerID[i])) return i; return -1; } int is_natural_number(char* str) { while (*str) if (NOT(isdigit(*str++))) return FALSE; return TRUE; } int layer_index(char* str, int all) { int i; if (streq(str,"all")) return all; if (is_natural_number(str)) return atoi(str); if (LayerID) for (i = 1; i <= Nlayers; i++) if (streq(str,LayerID[i])) return i; return -1; } int selectedlayer(char* spec) { int n0,n1; unsigned char buf[SMALLBUF]; char *w0, *w1; xbuf xb; int rval = FALSE; xbinit (&xb, SMALLBUF, buf); xbput(&xb,spec); w1 = w0 = strtok(xbuse(&xb),Layerdelims); if (w0) w1 = strtok(NULL,Layerdelims); switch((w0 != NULL) + (w1 != NULL)) { case 0: rval = FALSE; break; case 1: n0 = layer_index(w0,Layer); rval = (n0 == Layer); break; case 2: n0 = layer_index(w0,0); n1 = layer_index(w1,Nlayers); if ((n0 < 0) || (n1 < 0)) rval = TRUE; else if (n0 > n1) { int t = n0; n0 = n1; n1 = t;} rval = BETWEEN(n0,Layer,n1); break; } xbfree (&xb); return rval; } point pageincr(point page) { page = add_points(page,Minor); if (validpage(page) == FALSE) { if (Major.y) page.x = First.x; else page.y = First.y; page = add_points(page,Major); } return page; } static point exch_xy(point p) { int t; t = p.x; p.x = p.y; p.y = t; return p; } static pointf exch_xyf(pointf p) { double t; t = p.x; p.x = p.y; p.y = t; return p; } /* this isn't a pretty sight... */ void setup_graph(graph_t* g) { double xscale,yscale,scale; char *p; point PFCLM; /* page for centering less margins */ point DS; /* device drawable region for a page of the graph */ assert((GD_bb(g).LL.x == 0) && (GD_bb(g).LL.y == 0)); if (LayerID) free(LayerID); if ((p = agget(g,"layers")) != 0) Nlayers = parse_layers(p); else {LayerID = NULL; Nlayers = 0;} /* determine final drawing size and scale to apply. */ /* N.B. magnification could be allowed someday in the next conditional */ /* N.B. size given by user is not rotated by landscape mode */ if ((GD_drawing(g)->size.x > 0) /* was given by user... */ && ((GD_drawing(g)->size.x < GD_bb(g).UR.x) /* drawing is too big... */ || (GD_drawing(g)->size.y < GD_bb(g).UR.y))) { xscale = ((double)GD_drawing(g)->size.x) / GD_bb(g).UR.x; yscale = ((double)GD_drawing(g)->size.y) / GD_bb(g).UR.y; scale = MIN(xscale,yscale); GD_drawing(g)->scale = scale; GD_drawing(g)->size.x = scale * GD_bb(g).UR.x; GD_drawing(g)->size.y = scale * GD_bb(g).UR.y; } else { /* go with "natural" size of layout */ GD_drawing(g)->size = GD_bb(g).UR; scale = GD_drawing(g)->scale = 1.0; } /* determine pagination */ PB.LL = GD_drawing(g)->margin; if ((GD_drawing(g)->page.x > 0) && (GD_drawing(g)->page.y > 0)) { /* page was set by user */ point tp; PFC = GD_drawing(g)->page; PFCLM.x = PFC.x - 2*PB.LL.x; PFCLM.y = PFC.y - 2*PB.LL.y; GP.x = PFCLM.x ; GP.y = PFCLM.y; /* convert to double */ if (GD_drawing(g)->landscape) GP = exch_xyf(GP); GP.x = GP.x / scale; GP.y = GP.y / scale; /* we don't want graph page to exceed its bounding box */ GP.x = MIN(GP.x,GD_bb(g).UR.x); GP.y = MIN(GP.y,GD_bb(g).UR.y); Pages.x = (GP.x > 0) ? ceil( ((double)GD_bb(g).UR.x) / GP.x) : 1; Pages.y = (GP.y > 0) ? ceil( ((double)GD_bb(g).UR.y) / GP.y) : 1; N_pages = Pages.x * Pages.y; /* find the drawable size in device coords */ tp = GD_drawing(g)->size; if (GD_drawing(g)->landscape) tp = exch_xy(tp); DS.x = MIN(tp.x,PFCLM.x); DS.y = MIN(tp.y,PFCLM.y); } else { /* page not set by user, assume default when centering, but allow infinite page for any other interpretation */ GP.x = GD_bb(g).UR.x; GP.y = GD_bb(g).UR.y; PFC.x = DEFAULT_PAGEWD; PFC.y = DEFAULT_PAGEHT; PFCLM.x = PFC.x - 2*PB.LL.x; PFCLM.y = PFC.y - 2*PB.LL.y; DS = GD_drawing(g)->size; if (GD_drawing(g)->landscape) DS = exch_xy(DS); Pages.x = Pages.y = N_pages = 1; } set_pagedir(g); /* determine page box including centering */ if (GD_drawing(g)->centered) { point extra; if ((extra.x = PFCLM.x - DS.x) < 0) extra.x = 0; if ((extra.y = PFCLM.y - DS.y) < 0) extra.y = 0; PB.LL.x += extra.x / 2; PB.LL.y += extra.y / 2; } PB.UR = add_points(PB.LL,DS); Deffontname = late_nnstring(g->proto->n,N_fontname,DEFAULT_FONTNAME); Deffontsize = late_double(g->proto->n,N_fontsize,DEFAULT_FONTSIZE,MIN_FONTSIZE); } void emit_background(graph_t* g, point LL, point UR) { char *str; point A[4]; if (((str = agget(g,"bgcolor")) != 0) && str[0]) { A[0].x = A[1].x = LL.x - GD_drawing(g)->margin.x; A[2].x = A[3].x = UR.x + GD_drawing(g)->margin.x; A[1].y = A[2].y = UR.y + GD_drawing(g)->margin.y; A[3].y = A[0].y = LL.y - GD_drawing(g)->margin.y; CodeGen->set_fillcolor(str); CodeGen->set_pencolor(str); CodeGen->polygon(A,4,TRUE); /* filled */ } } /* even if this makes you cringe, at least it's short */ void setup_page(graph_t* g, point page) { point offset; int theta; Page++; theta = (GD_drawing(g)->landscape ? 90 : 0); /* establish current box in graph coordinates */ CB.LL.x = page.x * GP.x; CB.LL.y = page.y * GP.y; CB.UR.x = CB.LL.x + GP.x; CB.UR.y = CB.LL.y + GP.y; /* establish offset to be applied, in graph coordinates */ if (GD_drawing(g)->landscape == FALSE) offset = pointof(-CB.LL.x,-CB.LL.y); else { offset.x = (page.y + 1) * GP.y; offset.y = -page.x * GP.x; } CodeGen->begin_page(g,page,GD_drawing(g)->scale,theta,offset); emit_background(g, CB.LL, CB.UR); emit_defaults(g); } void emit_label(textlabel_t* lp, Agraph_t *g) { int i, linespacing, left_x, center_x, right_x, width_x; point p; /* make sure that there is something to do */ if (lp->nlines < 1) return; g = g->root; /* dimensions of box for label */ width_x = ROUND(POINTS(lp->dimen.x)); center_x = lp->p.x; left_x = center_x - width_x / 2; right_x = center_x + width_x / 2; /* set linespacing */ linespacing = (int)(lp->fontsize * LINESPACING); /* position for first line */ p.y = lp->p.y + (linespacing * (lp->nlines -1) / 2) /* cl of topline */ - lp->fontsize / 3.0 ; /* cl to baseline */ CodeGen->begin_context(); CodeGen->set_pencolor(lp->fontcolor); CodeGen->set_font(lp->fontname,lp->fontsize*GD_drawing(g)->font_scale_adj); for (i = 0; i < lp->nlines; i++) { switch(lp->line[i].just) { case 'l': p.x = left_x; break; case 'r': p.x = right_x; break; default: case 'n': p.x = center_x; break; } CodeGen->textline(p,&(lp->line[i])); /* position for next line */ p.y -= linespacing; } CodeGen->end_context(); } void emit_defaults(graph_t* g) { CodeGen->set_pencolor(DEFAULT_COLOR); CodeGen->set_fillcolor(DEFAULT_COLOR); CodeGen->set_font(Deffontname,Deffontsize); } int parse_layers(char* p) { int ntok,c; char *pcopy,*tok; ntok = strccnt(p,':')+1; pcopy = strdup(p); if (LayerID) free(LayerID); LayerID = N_NEW(ntok+2,char*); c = 1; for (tok = strtok(pcopy,Layerdelims); tok; tok = strtok(NULL,Layerdelims)) LayerID[c++] = tok; return ntok; } void emit_layer(int n) { char buf[BUFSIZ],*fake[2]; char null = 0; if (LayerID) { /* create compatible char array structure for set_style call */ sprintf(buf,"setlayer%c%d%c%d%c",null,n,null,Nlayers,null); fake[0] = buf; fake[1] = NULL; CodeGen->set_style(fake); } } static int style_delim(int c) { switch (c) { case '(': case ')': case ',': case '\0': return TRUE; default : return FALSE; } } #define SID 1 static int style_token(char** s, xbuf *xb) { char* p = *s; int token; char c; while (*p && (isspace(*p) || (*p ==','))) p++; switch (*p) { case '\0': token = 0; break; case '(': case ')': token = *p++; break; default: token = SID; while (!style_delim(c = *p)) { xbputc(xb,c); p++; } } *s = p; return token; } #define FUNLIMIT 64 static unsigned char outbuf[SMALLBUF]; static xbuf ps_xb; static void cleanup() { xbfree(&ps_xb); } char ** parse_style(char* s) { static char* parse[FUNLIMIT]; static int first = 1; int fun = 0; boolean in_parens = FALSE; unsigned char buf[SMALLBUF]; char* p; int c; xbuf xb; if (first) { xbinit (&ps_xb, SMALLBUF, outbuf); atexit(cleanup); first = 0; } xbinit (&xb, SMALLBUF, buf); p = s; while ((c = style_token(&p,&xb)) != 0) { switch (c) { case '(': if (in_parens) { agerr(AGERR, "nesting not allowed in style: %s\n",s); parse[0] = (char*)0; xbfree (&xb); return parse; } in_parens = TRUE; break; case ')': if (in_parens == FALSE) { agerr(AGERR, "unmatched ')' in style: %s\n",s); parse[0] = (char*)0; xbfree (&xb); return parse; } in_parens = FALSE; break; default: if (in_parens == FALSE) { if (fun == FUNLIMIT-1) { agerr(AGWARN, "truncating style '%s'\n", s); parse[fun] = (char*)0; xbfree (&xb); return parse; } xbputc(&ps_xb, '\0'); /* terminate previous */ parse[fun++] = xbnext(&ps_xb); } xbput (&ps_xb, xbuse(&xb)); xbputc(&ps_xb, '\0'); } } if (in_parens) { agerr(AGERR, "unmatched '(' in style: %s\n",s); parse[0] = (char*)0; xbfree (&xb); return parse; } parse[fun] = (char*)0; xbfree (&xb); xbuse (&ps_xb); /* adds final '\0' to buffer */ return parse; } static struct cg_s { codegen_t *cg; char *name; int id; } gens[] = { {&PS_CodeGen,"ps",POSTSCRIPT}, {&PS_CodeGen,"ps2",PDF}, {&HPGL_CodeGen,"hpgl",HPGL}, {&HPGL_CodeGen,"pcl",PCL}, {&MIF_CodeGen,"mif",MIF}, {&PIC_CodeGen,"pic",PIC_format}, {&GD_CodeGen,"gd",GD}, #ifdef HAVE_LIBZ {&GD_CodeGen,"gd2",GD2}, #endif #ifdef WITH_GIF {&GD_CodeGen,"gif",GIF}, #endif #ifdef HAVE_LIBJPEG {&GD_CodeGen,"jpg",JPEG}, {&GD_CodeGen,"jpeg",JPEG}, #endif #ifdef HAVE_LIBPNG #ifdef HAVE_LIBZ {&GD_CodeGen,"png",PNG}, #endif #endif {&GD_CodeGen,"wbmp",WBMP}, #ifdef HAVE_LIBXPM {&GD_CodeGen,"xbm",XBM}, #endif {&ISMAP_CodeGen,"ismap",ISMAP}, {&IMAP_CodeGen,"imap",IMAP}, {&CMAP_CodeGen,"cmap",CMAP}, #ifdef HAVE_LIBPNG {&VRML_CodeGen,"vrml",VRML}, #endif {&VTX_CodeGen,"vtx",VTX}, {&MP_CodeGen,"mp",METAPOST}, {&FIG_CodeGen,"fig",FIG}, {&SVG_CodeGen,"svg",SVG}, #ifdef HAVE_LIBZ {&SVG_CodeGen,"svgz",SVGZ}, {&DIA_CodeGen,"dia",DIA}, #endif {(codegen_t*)0,"dot",ATTRIBUTED_DOT}, {(codegen_t*)0,"canon",CANONICAL_DOT}, {(codegen_t*)0,"plain",PLAIN}, {(codegen_t*)0,"plain-ext",PLAIN_EXT}, {&XDot_CodeGen,"xdot",EXTENDED_DOT}, {(codegen_t*)0,(char*)0,0} }; int lang_select(char* str, int warn) { struct cg_s *p; for (p = gens; p->name; p++) { if (strcasecmp(str,p->name) == 0) { CodeGen = p->cg; return p->id; } } if (warn) { agerr(AGWARN, "language %s not recognized, use one of:\n",str); for (p = gens; p->name; p++) agerr(AGPREV, " %s",p->name); agerr(AGPREV, "\n"); } return ATTRIBUTED_DOT; } FILE * file_select(char* str) { FILE *rv; rv = fopen(str,"wb"); if (rv == NULL) { perror(str); exit(1); } return rv; } void use_library(char* name) { static int cnt = 0; if (name) { Lib = ALLOC(cnt+2,Lib,char*); Lib[cnt++] = name; Lib[cnt] = NULL; } }