#endif
#include "port_after.h"
#include "html.h"
typedef struct
{
HTMLBox box;
unsigned int width;
int rowspan, colspan;
} HData;
typedef struct
{
bool original;
int rowspan, colspan;
HData *ds;
int rowspan2, colspan2;
HData *ds2;
} HCell;
typedef struct
{
GList datas;
int colcount;
HCell *ca;
} HRow;
typedef struct
{
MemPool mp;
unsigned int border; /* table border width */
HTMLBox box; /* table box */
HTMLBox dbox; /* current data box */
MLElement p; /* table tag element */
GList grid; /* grid of rows and data */
GList klist; /* list of kids */
int rowcount, colcount; /* nearly always greater than real */
int pass;
unsigned int *cwidth, *rheight; /* column widths, row heights */
unsigned int *swidth; /* scaled columns widths */
unsigned int max_width; /* width available for table */
unsigned int inf_width; /* width with unbounded table size */
} HTable;
/*
* Private functions
*/
static void DestroyTable _ArgProto((HTMLInfo, HTMLBox));
static void SetupTable _ArgProto((HTMLInfo, HTMLBox));
static void RenderTable _ArgProto((HTMLInfo, HTMLBox, Region));
static void TablePosition1 _ArgProto((HTable *));
static void TablePosition2 _ArgProto((HTable *));
static void FillSpans _ArgProto((HTable *));
/*
* SetupTable
*
* Called when the position of the table is known. Now set the location
* of the child boxes and arrange to have their setup function called.
*/
static void
SetupTable(li, box)
HTMLInfo li;
HTMLBox box;
{
int i, j;
HRow *rs;
HData *ds;
int bx, by;
HTable *ts = (HTable *)box->closure;
bx = 0;
by = 0;
for (j = 0, rs = (HRow *)GListGetHead(ts->grid); rs != NULL;
rs = (HRow *)GListGetNext(ts->grid), j++)
{
if (rs->ca == NULL) continue;
for (i = 0; i < ts->colcount; i++)
{
if (rs->ca[i].ds != NULL && rs->ca[i].original)
{
ds = rs->ca[i].ds;
ds->box->x = bx + box->x;
ds->box->y = by + box->y;
HTMLSetupBox(li, ds->box);
bx += ts->cwidth[i];
}
else
{
bx += ts->cwidth[i];
}
}
bx = 0;
by += ts->rheight[j];
}
return;
}
/*
* RenderTable
*
* Called when the table has been exposed. Since the child boxes
* will handle their own refresh only need to refresh the border if
* there is one.
*/
static void
RenderTable(li, box, r)
HTMLInfo li;
HTMLBox box;
Region r;
{
HTable *ts = (HTable *)box->closure;
HTMLBox c;
for (c = (HTMLBox)GListGetHead(ts->klist); c != NULL;
c = (HTMLBox)GListGetNext(ts->klist))
{
HTMLRenderBox(li, r, c);
}
return;
}
/*
* DestroyTable
*/
static void
DestroyTable(li, box)
HTMLInfo li;
HTMLBox box;
{
HTable *ts = (HTable *)box->closure;
HTMLBox c;
while ((c = (HTMLBox)GListPop(ts->klist)) != NULL)
{
HTMLDestroyBox(li, c);
}
return;
}
/*
* HTMLTableAddBox
*/
void
HTMLTableAddBox(li, env, box)
HTMLInfo li;
HTMLEnv env;
HTMLBox box;
{
return;
}
/*
* HTMLTDAddBox
*/
void
HTMLTDAddBox(li, env, box)
HTMLInfo li;
HTMLEnv env;
HTMLBox box;
{
HTable *ts = (HTable *)env->closure;
HTMLLayoutBox(li, ts->dbox, box);
return;
}
/*
* HTMLTDBegin
*
* Called when a tag is encountered
*/
void
HTMLTDBegin(li, env, p)
HTMLInfo li;
HTMLEnv env;
MLElement p;
{
HTable *ts;
HRow *rs;
HData *ds;
ts = (HTable *)env->penv->closure;
env->closure = ts;
if (ts->pass == 0)
{
rs = (HRow *)GListGetTail(ts->grid);
myassert(rs != NULL, "Could not find row data (1).");
ds = (HData *)MPCGet(ts->mp, sizeof(HData));
if (p == NULL)
{
ds->colspan = 1;
ds->rowspan = 1;
}
else
{
if ((ds->colspan = MLAttributeToInt(p, "colspan")) < 1) ds->colspan = 1;
if ((ds->rowspan = MLAttributeToInt(p, "rowspan")) < 1) ds->rowspan = 1;
}
GListAddTail(rs->datas, ds);
rs->colcount += ds->colspan;
if (rs->colcount > ts->colcount) ts->colcount = rs->colcount;
/* this is more than the real count but that's ok. */
ts->rowcount += ds->rowspan;
ts->dbox = ds->box = HTMLCreateFlowBox(li, env, li->tableCellInfinity);
}
else
{
rs = (HRow *)GListGetCurrent(ts->grid);
myassert(rs != NULL, "Could not find row data (2).");
ds = (HData *)GListGetCurrent(rs->datas);
myassert(ds != NULL, "Could not find cell data (2).");
ts->dbox = ds->box = HTMLCreateFlowBox(li, env, ds->width);
}
GListAddHead(ts->klist, ds->box);
return;
}
/*
* HTMLTDEnd
*
* Called when a | tag is encountered
*/
void
HTMLTDEnd(li, env, p)
HTMLInfo li;
HTMLEnv env;
MLElement p;
{
HTable *ts = (HTable *)env->closure;
HRow *rs;
HTMLFinishFlowBox(li, ts->dbox);
if (ts->pass > 0)
{
rs = (HRow *)GListGetCurrent(ts->grid);
GListGetNext(rs->datas);
}
return;
}
/*
* HTMLTRBegin
*/
void
HTMLTRBegin(li, env, p)
HTMLInfo li;
HTMLEnv env;
MLElement p;
{
HTable *ts;
HRow *rs;
ts = (HTable *)env->penv->closure;
env->closure = ts;
if (ts->pass == 0)
{
rs = (HRow *)MPCGet(ts->mp, sizeof(HRow));
GListAddTail(ts->grid, rs);
rs->datas = GListCreateX(ts->mp);
}
else
{
rs = (HRow *)GListGetCurrent(ts->grid);
GListGetHead(rs->datas);
}
return;
}
/*
* HTMLTREnd
*/
void
HTMLTREnd(li, env, p)
HTMLInfo li;
HTMLEnv env;
MLElement p;
{
HTable *ts;
ts = (HTable *)env->penv->closure;
env->closure = ts;
if (ts->pass > 0) GListGetNext(ts->grid);
return;
}
/*
* HTMLTableBegin
*
* Called when is encountered. Has to deal with two passes.
*/
void
HTMLTableBegin(li, env, p)
HTMLInfo li;
HTMLEnv env;
MLElement p;
{
HTMLBox box;
HTable *ts;
char *value, *cp;
int x;
int border;
unsigned int width, maxwidth;
maxwidth = width = HTMLGetMaxWidth(li, env->penv);
env->ff = FLOW_LEFT_JUSTIFY;
env->anchor = NULL;
if (env->closure == NULL)
{
ts = (HTable *)MPCGet(li->mp, sizeof(HTable));
ts->mp = li->mp;
ts->p = p;
env->closure = ts;
if ((border = MLAttributeToInt(p, "border")) < 0) border = 0;
ts->border = border;
ts->grid = GListCreateX(ts->mp);
ts->klist = GListCreateX(li->mp);
if ((value = MLFindAttribute(p, "width")) != NULL)
{
if (isdigit(value[0]))
{
x = atoi(value);
for (cp = value; ; cp++)
{
if (!isdigit(*cp)) break;
}
if (*cp != '\0')
{
if (*cp == '%')
{
if (x >= 10) width = width * x / 100;
}
else if (x >= 100) width = x;
}
else if (x >= 100) width = x;
}
}
if (width > 0) ts->max_width = width;
else ts->max_width = 0;
}
else
{
ts = (HTable *)env->closure;
GListGetHead(ts->grid);
GListClear(ts->klist);
}
ts->pass = env->pass;
/*
* Create the table box
*/
ts->box = box = HTMLCreateBox(li, env);
box->setup = SetupTable;
box->render = RenderTable;
box->destroy = DestroyTable;
box->closure = ts;
return;
}
/*
* HTMLTableEnd
*
* Called when is encountered. Has to deal with two passes.
*/
void
HTMLTableEnd(li, env, p)
HTMLInfo li;
HTMLEnv env;
MLElement p;
{
HTable *ts = (HTable *)env->closure;
if (ts->colcount == 0) return;
if (ts->pass == 0)
{
/*
* Do first pass size calculations.
*/
FillSpans(ts);
TablePosition1(ts);
/*
* Now destroy the table box and its children since we need to rebuild
* from scratch.
*/
HTMLDestroyBox(li, ts->box);
ts->box = NULL;
}
else
{
TablePosition2(ts);
HTMLEnvAddBox(li, env->penv, ts->box);
}
return;
}
/*
* FillSpans
*/
static void
FillSpans(ts)
HTable *ts;
{
HRow *rs;
HData *ds;
int i, j, k;
HCell *pca;
ts->colcount *= 2;
/*
* Allocate an array for each row to keep track of the data grid.
*/
for (k = 0, rs = (HRow *)GListGetHead(ts->grid); rs != NULL;
rs = (HRow *)GListGetNext(ts->grid), k++)
{
rs->ca = (HCell *)MPCGet(ts->mp, sizeof(HCell) * ts->colcount);
}
/*
* Its possible rowspan has pushed things down a bit so add rows as
* necessary. Overkill is OK.
*/
for (; k < ts->rowcount; k++)
{
rs = (HRow *)MPCGet(ts->mp, sizeof(HRow));
GListAddTail(ts->grid, rs);
rs->datas = GListCreateX(ts->mp);
rs->ca = (HCell *)MPCGet(ts->mp, sizeof(HCell) * ts->colcount);
}
/*
* Put the data into the grids according to rowspan and colspan.
*/
pca = NULL;
for (rs = (HRow *)GListGetHead(ts->grid); rs != NULL;
rs = (HRow *)GListGetNext(ts->grid))
{
/*
* If there is a previous row then look at the HCell's above and
* see if they need to be extended. We know the HCell above needs to
* be extended down if rowspan > 1. Move rowspan - 1 down to the
* current row. Also, move the colspan
* value down (its not used, though) and the HData pointer.
*/
if (pca != NULL)
{
for (i = 0; i < ts->colcount; i++)
{
if (pca[i].ds != NULL && pca[i].rowspan > 1)
{
myassert(rs->ca[i].ds == NULL, "Cell visitation unexpected!");
rs->ca[i].rowspan = pca[i].rowspan - 1;
rs->ca[i].colspan = pca[i].colspan;
rs->ca[i].ds = pca[i].ds;
}
if (pca[i].ds2 != NULL && pca[i].rowspan2 > 1)
{
myassert(rs->ca[i].ds == NULL, "Cell visitation unexpected! (2)");
rs->ca[i].rowspan = pca[i].rowspan2 - 1;
rs->ca[i].colspan = pca[i].colspan2;
rs->ca[i].ds = pca[i].ds2;
}
}
}
/*
* Now extend HData to the right depending on the colspan.
*/
for (ds = (HData *)GListGetHead(rs->datas); ds != NULL;
ds = (HData *)GListGetNext(rs->datas))
{
/*
* Scan to the right and look for the first empty HCell. This needs
* to be done because an HData above could have spanned down to fill
* in the beginning of the row.
*
* This has to be done for each data section because a row that
* was extended down may have used up space in the middle of the
* row.
*/
for (i = 0; i < ts->colcount; i++)
{
if (rs->ca[i].ds == NULL) break;
}
myassert(ts->colcount != i, "Table column count confused.");
rs->ca[i].original = true;
for (j = 0; j < ds->colspan; j++, i++)
{
if (rs->ca[i].ds == NULL)
{
rs->ca[i].ds = ds;
rs->ca[i].colspan = ds->colspan - j;
rs->ca[i].rowspan = ds->rowspan;
}
else
{
rs->ca[i].ds2 = ds;
rs->ca[i].colspan2 = ds->colspan - j;
rs->ca[i].rowspan2 = ds->rowspan;
}
}
}
pca = rs->ca;
}
/*
* Test code. Print out patterns to see if the internal representation
* looks reasonable.
*/
/*
for (rs = (HRow *)GListGetHead(ts->grid); rs != NULL;
rs = (HRow *)GListGetNext(ts->grid))
{
for (i = 0; i < ts->colcount; i++)
{
if (rs->ca[i].ds2 != NULL) printf ("o");
else if (rs->ca[i].ds != NULL && rs->ca[i].original)
{
printf ("%c", (char )((int)('A') + i));
}
else if (rs->ca[i].ds != NULL) printf ("%c", (char )((int)('a') + i));
else printf ("x");
}
printf("\n");
}
*/
return;
}
/*
* TablePosition1
*/
static void
TablePosition1(ts)
HTable *ts;
{
int i, j;
HRow *rs;
HData *ds;
unsigned int twidth, width;
/*
* Allocate arrays to hold the column widths and row heights.
*
* cwidth = column width
* swidth = scaled column width
* rheight = row height
*/
ts->cwidth = (unsigned int *)MPCGet(ts->mp, sizeof(unsigned int) *
ts->colcount);
ts->swidth = (unsigned int *)MPCGet(ts->mp, sizeof(unsigned int) *
ts->colcount);
ts->rheight = (unsigned int *)MPCGet(ts->mp, sizeof(unsigned int) *
ts->rowcount);
/*
* Figure out the unrestrained widths of the columns. Needed a little
* later to determine the width of the table.
*/
twidth = 0;
for (rs = (HRow *)GListGetHead(ts->grid); rs != NULL;
rs = (HRow *)GListGetNext(ts->grid))
{
for (i = 0; i < ts->colcount; i++)
{
if (rs->ca[i].ds != NULL)
{
ds = rs->ca[i].ds;
width = ds->box->width / ds->colspan;
if (width > ts->cwidth[i]) ts->cwidth[i] = width;
}
if (rs->ca[i].ds2 != NULL)
{
ds = rs->ca[i].ds2;
width = ds->box->width / ds->colspan;
if (width > ts->cwidth[i]) ts->cwidth[i] = width;
}
}
}
/*
* Determine the width of the unrestrained table.
*/
twidth = 0;
for (i = 0; i < ts->colcount; i++)
{
twidth += ts->cwidth[i];
}
if (twidth > 0 && twidth > ts->max_width)
{
/*
* Now scale the columns to fit in the space available attempting to keep
* things in proportion.
*/
for (i = 0; i < ts->colcount; i++)
{
ts->swidth[i] = ts->max_width * ts->cwidth[i] / twidth;
}
}
else
{
/*
* No size problems so just copy the widths as-is.
*/
for (i = 0; i < ts->colcount; i++)
{
ts->swidth[i] = ts->cwidth[i];
}
}
/*
* Finally, figure out the widths of individual cells.
*/
for (rs = (HRow *)GListGetHead(ts->grid); rs != NULL;
rs = (HRow *)GListGetNext(ts->grid))
{
for (i = 0; i < ts->colcount; i++)
{
if (rs->ca[i].ds != NULL && rs->ca[i].original)
{
/*
* Add in the widths of all the columns for a column spanning
* cell.
*/
ds = rs->ca[i].ds;
for (j = 0; j + i < ts->colcount && j < ds->colspan; j++)
{
ds->width += ts->swidth[i + j];
}
}
}
}
return;
}
/*
* TablePosition2
*/
static void
TablePosition2(ts)
HTable *ts;
{
int i, j;
unsigned int width, height;
HRow *rs;
HData *ds;
memset(ts->cwidth, 0, ts->colcount * sizeof(unsigned int));
memset(ts->rheight, 0, ts->rowcount * sizeof(unsigned int));
/*
* Get the columns widths and row heights for the restrained table
* (second pass).
*/
for (j = 0, rs = (HRow *)GListGetHead(ts->grid); rs != NULL;
rs = (HRow *)GListGetNext(ts->grid), j++)
{
for (i = 0; i < ts->colcount; i++)
{
if (rs->ca[i].ds != NULL)
{
ds = rs->ca[i].ds;
width = ds->box->width / ds->colspan;
if (width > ts->cwidth[i]) ts->cwidth[i] = width;
height = ds->box->height / ds->rowspan;
if (height > ts->rheight[j]) ts->rheight[j] = height;
}
if (rs->ca[i].ds2 != NULL)
{
ds = rs->ca[i].ds2;
width = ds->box->width / ds->colspan;
if (width > ts->cwidth[i]) ts->cwidth[i] = width;
height = ds->box->height / ds->rowspan;
if (height > ts->rheight[j]) ts->rheight[j] = height;
}
}
}
/*
* Find the final width and height of the table.
*/
width = 0;
height = 0;
for (i = 0; i < ts->colcount; i++)
{
width += ts->cwidth[i];
}
for (i = 0; i < ts->rowcount; i++)
{
height += ts->rheight[i];
}
ts->box->width = width;
ts->box->height = height;
return;
}
/*
* HTMLTableAccept
*/
bool
HTMLTableAccept(li, obj)
HTMLInfo li;
HTMLObject obj;
{
if (obj->type != HTML_ENV) return(false);
if (HTMLTagToID(obj->o.env->tag) != TAG_TR) return(false);
return(true);
}
/*
* HTMLTRAccept
*/
bool
HTMLTRAccept(li, obj)
HTMLInfo li;
HTMLObject obj;
{
HTMLTagID tagid;
if (obj->type != HTML_ENV) return(false);
tagid = HTMLTagToID(obj->o.env->tag);
if (tagid != TAG_TD && tagid != TAG_TH) return(false);
return(true);
}
/*
* HTMLTDInsert
*/
HTMLInsertStatus
HTMLTDInsert(li, env, p)
HTMLInfo li;
HTMLEnv env;
MLElement p;
{
HTMLEnv c;
HTMLTagID tid;
for (c = (HTMLEnv)GListGetHead(li->envstack); c != NULL;
c = (HTMLEnv)GListGetNext(li->envstack))
{
tid = HTMLTagToID(c->tag);
if (tid == TAG_TABLE || tid == TAG_TR) break;
}
if (c == NULL) return(HTMLInsertReject);
else if (tid == TAG_TABLE) HTMLStartEnv(li, TAG_TR, NULL);
else HTMLPopEnv(li, TAG_TR);
return(HTMLInsertOK);
}
/*
* HTMLTDClamp
*/
bool
HTMLTDClamp(li, env)
HTMLInfo li;
HTMLEnv env;
{
HTMLEnv c;
HTMLTagID tid;
for (c = (HTMLEnv)GListGetHead(li->envstack); c != NULL;
c = (HTMLEnv)GListGetNext(li->envstack))
{
tid = HTMLTagToID(c->tag);
if (tid == TAG_TABLE || tid == TAG_TD) break;
}
if (c == NULL) return(false);
else if (tid == TAG_TABLE) return(false);
return(true);
}
/*
* HTMLTRInsert
*/
HTMLInsertStatus
HTMLTRInsert(li, env, p)
HTMLInfo li;
HTMLEnv env;
MLElement p;
{
HTMLEnv tenv;
if ((tenv = HTMLFindEnv(li, TAG_TABLE)) == NULL) return(HTMLInsertReject);
HTMLPopEnv(li, TAG_TABLE);
return(HTMLInsertOK);
}
/*
* HTMLTRClamp
*/
bool
HTMLTRClamp(li, env)
HTMLInfo li;
HTMLEnv env;
{
HTMLEnv c;
HTMLTagID tid;
for (c = (HTMLEnv)GListGetHead(li->envstack); c != NULL;
c = (HTMLEnv)GListGetNext(li->envstack))
{
tid = HTMLTagToID(c->tag);
if (tid == TAG_TABLE || tid == TAG_TR) break;
}
if (c == NULL) return(false);
else if (tid == TAG_TABLE) return(false);
return(true);
}
/*
* HTMLTDWidth
*/
unsigned int
HTMLTDWidth(li, env)
HTMLInfo li;
HTMLEnv env;
{
HTable *ts = (HTable *)env->closure;
return(HTMLGetBoxWidth(li, ts->dbox));
}
chimera-2.0a19/html/css.c 100600 11610 11610 35636 6724334152 13460 0 ustar john john /*
* css.c
*
* Copyright (c) 1998, John Kilburg
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include "port_before.h"
#include
#include
#ifdef HAVE_STRING_H
#include
#endif
#ifdef HAVE_STDLIB_H
#include
#endif
#include "port_after.h"
#include "common.h"
#include "Chimera.h"
#include "css.h"
struct CSSPropertyP
{
CSSRule cr;
char *name;
char *value;
};
struct CSSSelectorP
{
char *tag;
char *id;
char *class;
char *pclass;
};
struct CSSRuleP
{
GList selectors; /* list of lists of CSSSelector's */
GList properties; /* list of CSSProperty's */
};
/*
* Used to keep track of which buffer to take input from. Since new
* text can be inserted at any time have to be able to store and restore
* buffer context.
*/
struct CSSInputP
{
char *b;
size_t blen;
char *cp;
char *ep;
};
struct CSSContextP
{
MemPool mp;
ChimeraContext cs;
/* parser bookkeeping */
GList inputs;
/* The Result: a list of rules and properties */
GList rules;
GList props;
/* callback information */
CSSProc proc;
void *closure;
ChimeraTask task;
};
typedef struct CSSInputP *CSSInput;
static char *ParseSpace _ArgProto((char *, char *));
static CSSSelector ParseSelector2 _ArgProto((MemPool, char *, size_t));
static GList ParseSelector1 _ArgProto((MemPool, char *, size_t));
static GList ParseSelector _ArgProto((MemPool, char *, size_t));
static CSSProperty ParseProperty _ArgProto((MemPool, char *, size_t));
static GList ParseProperties _ArgProto((MemPool, char *, size_t));
static bool ParseRule _ArgProto((CSSContext, CSSInput));
static bool ParseAt _ArgProto((CSSContext, CSSInput));
static void ParseCSS _ArgProto((CSSContext));
static int scompare _ArgProto((char *, char *));
static bool SelectorListMatch _ArgProto((GList, GList));
static char *ParseToChar _ArgProto((char *, char *, int, bool));
/*
* ParseToChar
*
* Search for an expected character. Deals with escaped characters,
* quoted strings, and pairs of braces/parens/brackets.
*/
static char *
ParseToChar(cp, ep, c, chkws)
char *cp, *ep;
int c;
bool chkws;
{
bool sq, dq, esc;
sq = false; /* in single quote? */
dq = false; /* in double quote? */
esc = false; /* escape mode? */
while (cp < ep)
{
if (esc) esc = false;
else if (*cp == '\\') esc = true;
else if (dq && *cp != '"') dq = false;
else if (sq && *cp != '\'') sq = false;
else if (*cp == '"') dq = true;
else if (*cp == '\'') sq = true;
else if (*cp == c) return(cp);
else if (chkws && isspace8(*cp)) return(cp);
else if (*cp == '{')
{
if ((cp = ParseToChar(cp + 1, ep, '}', false)) == NULL) return(NULL);
}
else if (*cp == '(')
{
if ((cp = ParseToChar(cp + 1, ep, ')', false)) == NULL) return(NULL);
}
else if (*cp == '[')
{
if ((cp = ParseToChar(cp + 1, ep, ']', false)) == NULL) return(NULL);
}
cp++;
}
return(NULL);
}
/*
* ParseAt
*
* This is lame. Handle @ statements later.
*
* Return false if @import occured. Return true if not.
*/
static bool
ParseAt(css, ci)
CSSContext css;
CSSInput ci;
{
while (ci->cp < ci->ep)
{
if (*ci->cp == '{')
{
if ((ci->cp = ParseToChar(ci->cp + 1, ci->ep, '}', false)) == NULL)
{
ci->cp = ci->ep;
}
return(true);
}
else if (*ci->cp == ';')
{
ci->cp++;
return(false);
}
ci->cp++;
}
return(true);
}
/*
* ParseSpace
*
* Look for non-space and deal with comments.
*/
static char *
ParseSpace(s, e)
char *s, *e;
{
bool sc, ec, ic;
sc = false; /* possible start comment? */
ec = false; /* possible end comment? */
ic = false; /* in comment? */
while (s < e)
{
if (ic)
{
if (ec && *s == '/') ic = false;
else if (*s == '*') ec = true;
else ec = false;
}
else if (sc && *s == '*') ic = true;
else if (*s == '/') sc = true;
else if (!isspace8(*s)) return(s);
else sc = false;
s++;
}
return(NULL);
}
static CSSProperty
ParseProperty(mp, s, slen)
MemPool mp;
char *s;
size_t slen;
{
char *ep, *colon, *x;
CSSProperty prop;
ep = s + slen;
/* get rid of space in front of the declaration */
if ((s = ParseSpace(s, ep)) == NULL) return(NULL);
/* search for the required colon or white space */
if ((colon = ParseToChar(s, ep, ':', true)) == NULL) return(NULL);
prop = (CSSProperty)MPCGet(mp, sizeof(struct CSSPropertyP));
prop->name = (char *)MPGet(mp, colon - s + 1);
strncpy(prop->name, s, colon - s);
prop->name[colon - s] = '\0';
/* May have only reached white space before */
if (*colon != ':')
{
/* search for the required colon */
if ((colon = ParseToChar(colon, ep, ':', false)) == NULL) return(NULL);
}
/* remove space in front of the value */
if ((s = ParseSpace(colon + 1, ep)) == NULL) return(NULL);
prop->value = (char *)MPGet(mp, ep - s + 1);
/* Copy the value until end of input or whitespace is seen */
x = prop->value;
while (s < ep)
{
if (isspace8(*s)) break;
*x++ = *s++;
}
*x = '\0';
if (strlen(prop->name) == 0 || strlen(prop->value) == 0) return(NULL);
return(prop);
}
static GList
ParseProperties(mp, s, slen)
MemPool mp;
char *s;
size_t slen;
{
char *last;
char *ep;
GList properties;
CSSProperty prop;
properties = GListCreateX(mp);
ep = s + slen;
while (s < ep)
{
last = s;
s = ParseToChar(s, ep, ';', false);
if (s == NULL) s = ep;
if (last < s && (prop = ParseProperty(mp, last, s - last)) != NULL)
{
GListAddTail(properties, prop);
}
s++;
}
if (GListGetHead(properties) == NULL) return(NULL);
return(properties);
}
static CSSSelector
ParseSelector2(mp, s, slen)
MemPool mp;
char *s;
size_t slen;
{
CSSSelector cs;
char *tag = NULL;
char *id = NULL;
char *class = NULL;
char *pclass = NULL;
char *ep;
char *t;
cs = (CSSSelector)MPCGet(mp, sizeof(struct CSSSelectorP));
t = MPGet(mp, slen + 1);
strncpy(t, s, slen);
t[slen] = '\0';
s = t;
if (*s == '#') id = s + 1;
else if (*s == '.') class = s + 1;
else if (*s == ':') pclass = s + 1;
else
{
tag = s;
ep = s + slen;
while (s < ep)
{
if (*s == '#') { id = s + 1; *s = '\0'; break; }
else if (*s == '.') { class = s + 1; *s = '\0'; break; }
else if (*s == ':') { pclass = s + 1; *s = '\0'; break; }
else s++;
}
}
cs->tag = tag;
cs->id = id;
cs->class = class;
cs->pclass = pclass;
return(cs);
}
static GList
ParseSelector1(mp, s, slen)
MemPool mp;
char *s;
size_t slen;
{
char *ep = s + slen;
char *cp;
char *last;
CSSSelector cs;
GList selectors;
if ((s = ParseSpace(s, ep)) == NULL) return(NULL);
selectors = GListCreateX(mp);
last = s;
cp = s;
while (cp < ep)
{
if (isspace8(*cp))
{
if ((cs = ParseSelector2(mp, last, cp - last)) != NULL)
{
GListAddTail(selectors, cs);
}
if ((cp = ParseSpace(cp, ep)) == NULL) break;
last = cp;
}
else cp++;
}
if (last < cp)
{
if ((cs = ParseSelector2(mp, last, cp - last)) != NULL)
{
GListAddTail(selectors, cs);
}
}
if (GListGetHead(selectors) == NULL) return(NULL);
return(selectors);
}
/*
* ParseSelector
*/
static GList
ParseSelector(mp, s, slen)
MemPool mp;
char *s;
size_t slen;
{
GList slist, xlist;
char *ep;
char *last;
slist = GListCreateX(mp);
ep = s + slen;
while (s < ep)
{
last = s;
s = ParseToChar(s, ep, ',', false);
if (s == NULL) s = ep;
if (last < s && (xlist = ParseSelector1(mp, last, s - last)) != NULL)
{
GListAddTail(slist, xlist);
}
s++;
}
if (GListGetHead(slist) == NULL) return(NULL);
return(slist);
}
/*
* scompare
*
* do the sort of string comparison that we need here
*/
static int
scompare(s1, s2)
char *s1, *s2;
{
if (s1 == s2) return(0);
if (s1 == NULL || s2 == NULL) return(-1);
if (strlen(s1) != strlen(s2)) return(-1);
return(strcasecmp(s1, s2));
}
/*
* ParseRule
*
* Parse a single rule. Return false if parsing error that leads to EOF.
*/
static bool
ParseRule(css, ci)
CSSContext css;
CSSInput ci;
{
char *last;
GList slist;
GList plist;
CSSRule cr;
slist = GListCreateX(css->mp);
last = ci->cp;
if ((ci->cp = ParseToChar(ci->cp, ci->ep, '{', false)) == NULL)
{
ci->cp = ci->ep;
return(false);
}
slist = ParseSelector(css->mp, last, ci->cp - last);
ci->cp++; /* need to get past the '{' from above */
last = ci->cp;
if ((ci->cp = ParseToChar(ci->cp, ci->ep, '}', false)) == NULL)
{
ci->cp = ci->ep;
return(false);
}
plist = ParseProperties(css->mp, last, ci->cp - last);
ci->cp++;
/*
* Return true because parse was successful.
*/
if (plist == NULL || GListGetHead(plist) == NULL ||
slist == NULL || GListGetHead(slist) == NULL) return(true);
cr = (CSSRule)MPCGet(css->mp, sizeof(struct CSSRuleP));
cr->selectors = slist;
cr->properties = plist;
GListAddTail(css->rules, cr);
return(true);
}
/*
* ParseCSS
*
* Toplevel parser loop.
*/
static void
ParseCSS(css)
CSSContext css;
{
CSSInput ci = (CSSInput)GListGetHead(css->inputs);
if (ci == NULL)
{
if (css->proc == NULL) return;
/*
* Should call callback here because this means all CSS text including
* remote text has been parsed. Callback must be made from a toplevel
* task so create a task first.
*/
return;
}
ci->cp = ParseSpace(ci->cp, ci->ep);
while (ci->cp != NULL && ci->cp < ci->ep)
{
if (*ci->cp == '@')
{
if (!ParseAt(css, ci)) return;
}
else if (!ParseRule(css, ci)) return;
ci->cp = ParseSpace(ci->cp, ci->ep);
}
GListPop(css->inputs);
ParseCSS(css);
return;
}
CSSContext
CSSParseBuffer(cs, b, blen, proc, closure)
ChimeraContext cs;
char *b;
size_t blen;
CSSProc proc;
void *closure;
{
CSSContext css;
MemPool mp;
CSSInput ci;
mp = MPCreate();
css = (CSSContext)MPCGet(mp, sizeof(struct CSSContextP));
css->mp = mp;
css->cs = cs;
css->rules = GListCreateX(mp);
css->props = GListCreateX(mp);
css->inputs = GListCreateX(mp);
css->proc = proc;
css->closure = closure;
ci = (CSSInput)MPCGet(mp, sizeof(struct CSSInputP));
ci->b = b;
ci->blen = blen;
ci->cp = b;
ci->ep = b + blen;
GListAddHead(css->inputs, ci);
ParseCSS(css);
return(css);
}
/*
* CSSDestroyContext
*/
void
CSSDestroyContext(css)
CSSContext css;
{
MPDestroy(css->mp);
return;
}
static bool
SelectorListMatch(slist1, slist2)
GList slist1, slist2;
{
CSSSelector cs, ncs;
/*
* This code needs attention. It compares two selector lists.
*/
for (cs = (CSSSelector)GListGetHead(slist1),
ncs = (CSSSelector)GListGetHead(slist2);
cs != NULL && ncs != NULL;
cs = (CSSSelector)GListGetNext(slist1),
ncs = (CSSSelector)GListGetNext(slist2))
{
if (scompare(ncs->tag, cs->tag) != 0 ||
scompare(ncs->id, cs->id) != 0 ||
scompare(ncs->class, cs->class) != 0 ||
scompare(ncs->pclass, cs->pclass) != 0)
{
return(false);
}
}
if (ncs != NULL || cs != NULL) return(false);
return(true);
}
/*
* CSSCreateSelector
*/
CSSSelector
CSSCreateSelector(mp)
MemPool mp;
{
return((CSSSelector)MPCGet(mp, sizeof(struct CSSSelectorP)));
}
/*
* CSSSetSelector
*/
void
CSSSetSelector(cs, tag, id, class, pclass)
CSSSelector cs;
char *tag, *id, *class, *pclass;
{
cs->tag = tag;
cs->id = id;
cs->class = class;
cs->pclass = pclass;
return;
}
/*
* CSSFindProperty
*/
char *
CSSFindProperty(css, selectors, name)
CSSContext css;
GList selectors;
char *name;
{
CSSProperty p;
GList slist, xlist;
CSSSelector h;
if ((h = GListGetHead(selectors)) != GListGetTail(selectors))
{
slist = GListCreate();
GListAddHead(slist, h);
}
else slist = NULL;
for (p = (CSSProperty)GListGetHead(css->props); p != NULL;
p = (CSSProperty)GListGetNext(css->props))
{
if (scompare(p->name, name) == 0)
{
for (xlist = (GList)GListGetHead(p->cr->selectors); xlist != NULL;
xlist = (GList)GListGetNext(p->cr->selectors))
{
if ((slist != NULL && SelectorListMatch(xlist, slist)) ||
SelectorListMatch(xlist, selectors))
{
if (slist != NULL) GListDestroy(slist);
return(p->value);
}
}
}
}
if (slist != NULL) GListDestroy(slist);
return(NULL);
}
/*
* CSSPrintSelectorList
*/
void
CSSPrintSelectorList(slist)
GList slist;
{
CSSSelector s;
for (s = (CSSSelector)GListGetHead(slist); s != NULL;
s = (CSSSelector)GListGetNext(slist))
{
if (s->tag != NULL) fprintf (stderr, "%s ", s->tag);
if (s->id != NULL) fprintf (stderr, "%s ", s->id);
if (s->class != NULL) fprintf (stderr, "%s ", s->class);
if (s->pclass != NULL) fprintf (stderr, "%s ", s->pclass);
fprintf (stderr, "\n");
}
return;
}
/*
* CSSPrint
*/
void
CSSPrint(css)
CSSContext css;
{
CSSRule cr;
CSSSelector cs;
GList slist;
CSSProperty prop;
for (cr = (CSSRule)GListGetHead(css->rules); cr != NULL;
cr = (CSSRule)GListGetNext(css->rules))
{
for (slist = (GList)GListGetHead(cr->selectors); slist != NULL;
slist = (GList)GListGetNext(cr->selectors))
{
for (cs = (CSSSelector)GListGetHead(slist); cs != NULL;
cs = (CSSSelector)GListGetNext(slist))
{
printf ("%s ", cs->tag);
}
printf (", ");
}
printf ("{\n");
for (prop = (CSSProperty)GListGetHead(cr->properties); prop != NULL;
prop = (CSSProperty)GListGetNext(cr->properties))
{
printf ("\t%s : %s;\n", prop->name, prop->value);
}
printf ("}\n");
}
}
#ifdef CSSDEBUG
#include
#include
main(argc, argv)
int argc;
char *argv[];
{
CSSContext css;
FILE *fp;
char *b;
char *name;
struct stat st;
GList slist;
CSSSelector cs;
MemPool mp;
if (argc < 2) exit(1);
if (stat(argv[1], &st) != 0) exit(1);
if ((b = (char *)alloc_mem(st.st_size)) == NULL) exit(1);
if ((fp = fopen(argv[1], "r")) == NULL) exit(1);
fread(b, 1, st.st_size, fp);
fclose(fp);
css = CSSParseBuffer(NULL, b, st.st_size, NULL, NULL);
mp = MPCreate();
slist = GListCreate();
cs = CSSCreateSelector(mp);
CSSSetSelector(cs, "H5", NULL, NULL, NULL);
GListAddHead(slist, cs);
name = "white-space";
if ((b = CSSFindProperty(css, slist, name)) != NULL)
{
printf ("%s == %s\n", name, b);
}
else printf ("%s not found\n", name);
CSSPrint(css);
CSSDestroyContext(css);
exit(0);
}
#endif
chimera-2.0a19/html/css.h 100600 11610 11610 2612 6677561563 13450 0 ustar john john /*
* css.h
*
* Copyright (c) 1998, John Kilburg
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
typedef struct CSSPropertyP *CSSProperty;
typedef struct CSSSelectorP *CSSSelector;
typedef struct CSSRuleP *CSSRule;
typedef struct CSSContextP *CSSContext;
typedef void (*CSSProc) _ArgProto((void *));
void CSSDestroyContext _ArgProto((CSSContext));
CSSContext CSSParseBuffer _ArgProto((ChimeraContext,
char *, size_t, CSSProc, void *));
CSSSelector CSSCreateSelector _ArgProto((MemPool));
void CSSSetSelector _ArgProto((CSSSelector,
char *, char *, char *, char *));
char *CSSFindProperty _ArgProto((CSSContext, GList, char *));
void CSSPrintSelectorList _ArgProto((GList));
void CSSPrint _ArgProto((CSSContext));
chimera-2.0a19/html/csstest 100711 11610 11610 243754 6677561616 14202 0 ustar john john ELF @À 4 D¤ 4 ( 4 @ 4 @ 4 4 @4 @4 p H @H @H € € p È @È @È à @à @à D D p @ @ ` ` ` ` ` /usr/lib/libc.so.1 óÿÿþ ÿÿÿÿ âð 0 óÿÿþ ÿÿÿÿ âð @ @œ @
c
p p dp @ p
p ( p ' p p p bp p +p & |