More & names and numbers.

This commit is contained in:
rsc 2005-09-30 17:45:40 +00:00
parent 01a1c31a7d
commit 431e32de9b

View file

@ -333,7 +333,9 @@ AsciiInt _chartab[] = {
{"kappa", 954}, {"kappa", 954},
{"lambda", 955}, {"lambda", 955},
{"laquo", 171}, {"laquo", 171},
{"ldquo", 8220},
{"ldots", 8230}, {"ldots", 8230},
{"lsquo", 8216},
{"lt", 60}, {"lt", 60},
{"macr", 175}, {"macr", 175},
{"mdash", 8212}, {"mdash", 8212},
@ -364,8 +366,10 @@ AsciiInt _chartab[] = {
{"quad", 8193}, {"quad", 8193},
{"quot", 34}, {"quot", 34},
{"raquo", 187}, {"raquo", 187},
{"rdquo", 8221},
{"reg", 174}, {"reg", 174},
{"rho", 961}, {"rho", 961},
{"rsquo", 8217},
{"sect", 167}, {"sect", 167},
{"shy", 173}, {"shy", 173},
{"sigma", 963}, {"sigma", 963},
@ -492,9 +496,9 @@ _gettoks(uchar* data, int datalen, int chset, int mtype, int* plen)
ai = 0; ai = 0;
if(dbglex) if(dbglex)
fprint(2, "_gettoks starts, ts.i=%d, ts.edata=%d\n", ts->i, ts->edata); fprint(2, "_gettoks starts, ts.i=%d, ts.edata=%d\n", ts->i, ts->edata);
if(ts->mtype == TextHtml) { if(ts->mtype == TextHtml){
for(;;) { for(;;){
if(ai == alen) { if(ai == alen){
a = (Token*)erealloc(a, (alen+ToksChunk)*sizeof(Token)); a = (Token*)erealloc(a, (alen+ToksChunk)*sizeof(Token));
alen += ToksChunk; alen += ToksChunk;
} }
@ -502,9 +506,9 @@ _gettoks(uchar* data, int datalen, int chset, int mtype, int* plen)
c = getchar(ts); c = getchar(ts);
if(c < 0) if(c < 0)
break; break;
if(c == '<') { if(c == '<'){
tag = gettag(ts, starti, a, &ai); tag = gettag(ts, starti, a, &ai);
if(tag == Tscript) { if(tag == Tscript){
// special rules for getting Data after.... // special rules for getting Data after....
starti = ts->i; starti = ts->i;
c = getchar(ts); c = getchar(ts);
@ -521,8 +525,8 @@ _gettoks(uchar* data, int datalen, int chset, int mtype, int* plen)
} }
else { else {
// plain text (non-html) tokens // plain text (non-html) tokens
for(;;) { for(;;){
if(ai == alen) { if(ai == alen){
a = (Token*)erealloc(a, (alen+ToksChunk)*sizeof(Token)); a = (Token*)erealloc(a, (alen+ToksChunk)*sizeof(Token));
alen += ToksChunk; alen += ToksChunk;
} }
@ -560,14 +564,14 @@ getplaindata(TokenSource* ts, Token* a, int* pai)
s = nil; s = nil;
j = 0; j = 0;
starti = ts->i; starti = ts->i;
for(c = getchar(ts); c >= 0; c = getchar(ts)) { for(c = getchar(ts); c >= 0; c = getchar(ts)){
if(c < ' ') { if(c < ' '){
if(isspace(c)) { if(isspace(c)){
if(c == '\r') { if(c == '\r'){
// ignore it unless no following '\n', // ignore it unless no following '\n',
// in which case treat it like '\n' // in which case treat it like '\n'
c = getchar(ts); c = getchar(ts);
if(c != '\n') { if(c != '\n'){
if(c >= 0) if(c >= 0)
ungetchar(ts, c); ungetchar(ts, c);
c = '\n'; c = '\n';
@ -577,9 +581,9 @@ getplaindata(TokenSource* ts, Token* a, int* pai)
else else
c = 0; c = 0;
} }
if(c != 0) { if(c != 0){
buf[j++] = c; buf[j++] = c;
if(j == sizeof(buf)-1) { if(j == sizeof(buf)-1){
s = buftostr(s, buf, j); s = buftostr(s, buf, j);
j = 0; j = 0;
} }
@ -627,19 +631,19 @@ getdata(TokenSource* ts, int firstc, int starti, Token* a, int* pai)
s = nil; s = nil;
j = 0; j = 0;
c = firstc; c = firstc;
while(c >= 0) { while(c >= 0){
if(c == '&') { if(c == '&'){
c = ampersand(ts); c = ampersand(ts);
if(c < 0) if(c < 0)
break; break;
} }
else if(c < ' ') { else if(c < ' '){
if(isspace(c)) { if(isspace(c)){
if(c == '\r') { if(c == '\r'){
// ignore it unless no following '\n', // ignore it unless no following '\n',
// in which case treat it like '\n' // in which case treat it like '\n'
c = getchar(ts); c = getchar(ts);
if(c != '\n') { if(c != '\n'){
if(c >= 0) if(c >= 0)
ungetchar(ts, c); ungetchar(ts, c);
c = '\n'; c = '\n';
@ -652,13 +656,13 @@ getdata(TokenSource* ts, int firstc, int starti, Token* a, int* pai)
c = 0; c = 0;
} }
} }
else if(c == '<') { else if(c == '<'){
ungetchar(ts, c); ungetchar(ts, c);
break; break;
} }
if(c != 0) { if(c != 0){
buf[j++] = c; buf[j++] = c;
if(j == BIGBUFSIZE-1) { if(j == BIGBUFSIZE-1){
s = buftostr(s, buf, j); s = buftostr(s, buf, j);
j = 0; j = 0;
} }
@ -696,12 +700,12 @@ getscriptdata(TokenSource* ts, int firstc, int starti, Token* a, int* pai)
tstarti = starti; tstarti = starti;
c = firstc; c = firstc;
done = 0; done = 0;
while(c >= 0) { while(c >= 0){
if(c == '<') { if(c == '<'){
// other browsers ignore stuff to end of line after <! // other browsers ignore stuff to end of line after <!
savei = ts->i; savei = ts->i;
c = getchar(ts); c = getchar(ts);
if(c == '!') { if(c == '!'){
while(c >= 0 && c != '\n' && c != '\r') while(c >= 0 && c != '\n' && c != '\r')
c = getchar(ts); c = getchar(ts);
if(c == '\r') if(c == '\r')
@ -709,7 +713,7 @@ getscriptdata(TokenSource* ts, int firstc, int starti, Token* a, int* pai)
if(c == '\n') if(c == '\n')
c = getchar(ts); c = getchar(ts);
} }
else if(c >= 0) { else if(c >= 0){
backup(ts, savei); backup(ts, savei);
tag = gettag(ts, tstarti, a, pai); tag = gettag(ts, tstarti, a, pai);
if(tag == -1) if(tag == -1)
@ -717,7 +721,7 @@ getscriptdata(TokenSource* ts, int firstc, int starti, Token* a, int* pai)
if(tag != Comment) if(tag != Comment)
(*pai)--; (*pai)--;
backup(ts, tstarti); backup(ts, tstarti);
if(tag == Tscript + RBRA) { if(tag == Tscript + RBRA){
done = 1; done = 1;
break; break;
} }
@ -727,9 +731,9 @@ getscriptdata(TokenSource* ts, int firstc, int starti, Token* a, int* pai)
} }
if(c < 0) if(c < 0)
break; break;
if(c != 0) { if(c != 0){
buf[j++] = c; buf[j++] = c;
if(j == BIGBUFSIZE-1) { if(j == BIGBUFSIZE-1){
s = buftostr(s, buf, j); s = buftostr(s, buf, j);
j = 0; j = 0;
} }
@ -737,7 +741,7 @@ getscriptdata(TokenSource* ts, int firstc, int starti, Token* a, int* pai)
tstarti = ts->i; tstarti = ts->i;
c = getchar(ts); c = getchar(ts);
} }
if(done || ts->i == ts->edata) { if(done || ts->i == ts->edata){
s = buftostr(s, buf, j); s = buftostr(s, buf, j);
tok = &a[(*pai)++]; tok = &a[(*pai)++];
tok->tag = Data; tok->tag = Data;
@ -784,15 +788,15 @@ gettag(TokenSource* ts, int starti, Token* a, int* pai)
tok->attr = nil; tok->attr = nil;
tok->starti = starti; tok->starti = starti;
c = getchar(ts); c = getchar(ts);
if(c == '/') { if(c == '/'){
rbra = RBRA; rbra = RBRA;
c = getchar(ts); c = getchar(ts);
} }
if(c < 0) if(c < 0)
goto eob_done; goto eob_done;
if(c >= 256 || !isalpha(c)) { if(c >= 256 || !isalpha(c)){
// not a tag // not a tag
if(c == '!') { if(c == '!'){
ans = comment(ts); ans = comment(ts);
if(ans != -1) if(ans != -1)
return ans; return ans;
@ -809,7 +813,7 @@ gettag(TokenSource* ts, int starti, Token* a, int* pai)
// c starts a tagname // c starts a tagname
buf[0] = c; buf[0] = c;
i = 1; i = 1;
while(1) { for(;;){
c = getchar(ts); c = getchar(ts);
if(c < 0) if(c < 0)
goto eob_done; goto eob_done;
@ -826,34 +830,34 @@ gettag(TokenSource* ts, int starti, Token* a, int* pai)
// attribute gathering loop // attribute gathering loop
al = nil; al = nil;
while(1) { for(;;){
// look for "ws name" or "ws name ws = ws val" (ws=whitespace) // look for "ws name" or "ws name ws = ws val" (ws=whitespace)
// skip whitespace // skip whitespace
attrloop_continue: attrloop_continue:
while(c < 256 && isspace(c)) { while(c < 256 && isspace(c)){
c = getchar(ts); c = getchar(ts);
if(c < 0) if(c < 0)
goto eob_done; goto eob_done;
} }
if(c == '>') if(c == '>')
goto attrloop_done; goto attrloop_done;
if(c == '<') { if(c == '<'){
if(warn) if(warn)
fprint(2, "warning: unclosed tag\n"); fprint(2, "warning: unclosed tag\n");
ungetchar(ts, c); ungetchar(ts, c);
goto attrloop_done; goto attrloop_done;
} }
if(c >= 256 || !isalpha(c)) { if(c >= 256 || !isalpha(c)){
if(warn) if(warn)
fprint(2, "warning: expected attribute name\n"); fprint(2, "warning: expected attribute name\n");
// skipt to next attribute name // skipt to next attribute name
while(1) { for(;;){
c = getchar(ts); c = getchar(ts);
if(c < 0) if(c < 0)
goto eob_done; goto eob_done;
if(c < 256 && isalpha(c)) if(c < 256 && isalpha(c))
goto attrloop_continue; goto attrloop_continue;
if(c == '<') { if(c == '<'){
if(warn) if(warn)
fprint(2, "warning: unclosed tag\n"); fprint(2, "warning: unclosed tag\n");
ungetchar(ts, 60); ungetchar(ts, 60);
@ -866,7 +870,7 @@ attrloop_continue:
// gather attribute name // gather attribute name
buf[0] = c; buf[0] = c;
i = 1; i = 1;
while(1) { for(;;){
c = getchar(ts); c = getchar(ts);
if(c < 0) if(c < 0)
goto eob_done; goto eob_done;
@ -876,23 +880,23 @@ attrloop_continue:
buf[i++] = c; buf[i++] = c;
} }
afnd = _lookup(attrtable, Numattrs, buf, i, &attid); afnd = _lookup(attrtable, Numattrs, buf, i, &attid);
if(warn && !afnd) { if(warn && !afnd){
buf[i] = 0; buf[i] = 0;
fprint(2, "warning: unknown attribute name %S\n", buf); fprint(2, "warning: unknown attribute name %S\n", buf);
} }
// skip whitespace // skip whitespace
while(c < 256 && isspace(c)) { while(c < 256 && isspace(c)){
c = getchar(ts); c = getchar(ts);
if(c < 0) if(c < 0)
goto eob_done; goto eob_done;
} }
if(c != '=') { if(c != '='){
if(afnd) if(afnd)
al = newattr(attid, nil, al); al = newattr(attid, nil, al);
goto attrloop_continue; goto attrloop_continue;
} }
//# c is '=' here; skip whitespace //# c is '=' here; skip whitespace
while(1) { for(;;){
c = getchar(ts); c = getchar(ts);
if(c < 0) if(c < 0)
goto eob_done; goto eob_done;
@ -900,7 +904,7 @@ attrloop_continue:
break; break;
} }
quote = 0; quote = 0;
if(c == '\'' || c == '"') { if(c == '\'' || c == '"'){
quote = c; quote = c;
c = getchar(ts); c = getchar(ts);
if(c < 0) if(c < 0)
@ -908,31 +912,31 @@ attrloop_continue:
} }
val = nil; val = nil;
nv = 0; nv = 0;
while(1) { for(;;){
valloop_continue: valloop_continue:
if(c < 0) if(c < 0)
goto eob_done; goto eob_done;
if(c == '>') { if(c == '>'){
if(quote) { if(quote){
// c might be part of string (though not good style) // c might be part of string (though not good style)
// but if line ends before close quote, assume // but if line ends before close quote, assume
// there was an unmatched quote // there was an unmatched quote
ti = ts->i; ti = ts->i;
while(1) { for(;;){
c = getchar(ts); c = getchar(ts);
if(c < 0) if(c < 0)
goto eob_done; goto eob_done;
if(c == quote) { if(c == quote){
backup(ts, ti); backup(ts, ti);
buf[nv++] = '>'; buf[nv++] = '>';
if(nv == BIGBUFSIZE-1) { if(nv == BIGBUFSIZE-1){
val = buftostr(val, buf, nv); val = buftostr(val, buf, nv);
nv = 0; nv = 0;
} }
c = getchar(ts); c = getchar(ts);
goto valloop_continue; goto valloop_continue;
} }
if(c == '\n') { if(c == '\n'){
if(warn) if(warn)
fprint(2, "warning: apparent unmatched quote\n"); fprint(2, "warning: apparent unmatched quote\n");
backup(ts, ti); backup(ts, ti);
@ -944,14 +948,14 @@ valloop_continue:
else else
goto valloop_done; goto valloop_done;
} }
if(quote) { if(quote){
if(c == quote) { if(c == quote){
c = getchar(ts); c = getchar(ts);
if(c < 0) if(c < 0)
goto eob_done; goto eob_done;
goto valloop_done; goto valloop_done;
} }
if(c == '\r') { if(c == '\r'){
c = getchar(ts); c = getchar(ts);
goto valloop_continue; goto valloop_continue;
} }
@ -962,20 +966,20 @@ valloop_continue:
if(c < 256 && isspace(c)) if(c < 256 && isspace(c))
goto valloop_done; goto valloop_done;
} }
if(c == '&') { if(c == '&'){
c = ampersand(ts); c = ampersand(ts);
if(c == -1) if(c == -1)
goto eob_done; goto eob_done;
} }
buf[nv++] = c; buf[nv++] = c;
if(nv == BIGBUFSIZE-1) { if(nv == BIGBUFSIZE-1){
val = buftostr(val, buf, nv); val = buftostr(val, buf, nv);
nv = 0; nv = 0;
} }
c = getchar(ts); c = getchar(ts);
} }
valloop_done: valloop_done:
if(afnd) { if(afnd){
val = buftostr(val, buf, nv); val = buftostr(val, buf, nv);
al = newattr(attid, val, al); al = newattr(attid, val, al);
} }
@ -1017,19 +1021,19 @@ comment(TokenSource* ts)
nexti = ts->i; nexti = ts->i;
havecomment = 0; havecomment = 0;
c = getchar(ts); c = getchar(ts);
if(c == '-') { if(c == '-'){
c = getchar(ts); c = getchar(ts);
if(c == '-') { if(c == '-'){
if(findstr(ts, L(Larrow))) if(findstr(ts, L(Larrow)))
havecomment = 1; havecomment = 1;
else else
backup(ts, nexti); backup(ts, nexti);
} }
} }
if(!havecomment) { if(!havecomment){
if(c == '>') if(c == '>')
havecomment = 1; havecomment = 1;
else if(c >= 0) { else if(c >= 0){
if(findstr(ts, L(Lgt))) if(findstr(ts, L(Lgt)))
havecomment = 1; havecomment = 1;
} }
@ -1053,15 +1057,15 @@ findstr(TokenSource* ts, Rune* s)
c0 = s[0]; c0 = s[0];
n = runestrlen(s); n = runestrlen(s);
while(1) { for(;;){
c = getchar(ts); c = getchar(ts);
if(c < 0) if(c < 0)
break; break;
if(c == c0) { if(c == c0){
if(n == 1) if(n == 1)
return 1; return 1;
nexti = ts->i; nexti = ts->i;
for(i = 1; i < n; i++) { for(i = 1; i < n; i++){
c = getchar(ts); c = getchar(ts);
if(c < 0) if(c < 0)
goto mainloop_done; goto mainloop_done;
@ -1077,6 +1081,18 @@ mainloop_done:
return 0; return 0;
} }
static int
xdigit(int c)
{
if('0' <= c && c <= '9')
return c-'0';
if('a' <= c && c <= 'f')
return c-'a'+10;
if('A' <= c && c <= 'F')
return c-'A'+10;
return -1;
}
// We've just read an '&'; look for an entity reference // We've just read an '&'; look for an entity reference
// name, and if found, return translated char. // name, and if found, return translated char.
// if there is a complete entity name but it isn't known, // if there is a complete entity name but it isn't known,
@ -1100,36 +1116,42 @@ ampersand(TokenSource* ts)
c = getchar(ts); c = getchar(ts);
fnd = 0; fnd = 0;
ans = -1; ans = -1;
if(c == '#') { if(c == '#'){
c = getchar(ts); c = getchar(ts);
v = 0; v = 0;
while(c >= 0) { if(c == 'x'){
if(!(c < 256 && isdigit(c)))
break;
v = v*10 + c - 48;
c = getchar(ts); c = getchar(ts);
while((i=xdigit(c)) != -1){
v = v*16 + i;
c = getchar(ts);
}
}else{
while('0' <= c && c <= '9'){
v = v*10 + c - '0';
c = getchar(ts);
}
} }
if(c >= 0) { if(c >= 0){
if(!(c == ';' || c == '\n' || c == '\r')) if(!(c == ';' || c == '\n' || c == '\r'))
ungetchar(ts, c); ungetchar(ts, c);
c = v; c = v;
if(c == 160) if(c == 160)
c = 160; c = 160;
if(c >= Winstart && c <= Winend) { if(c >= Winstart && c <= Winend){
c = winchars[c - Winstart]; c = winchars[c - Winstart];
} }
ans = c; ans = c;
fnd = 1; fnd = 1;
} }
} }
else if(c < 256 && isalpha(c)) { else if(c < 256 && isalpha(c)){
buf[0] = c; buf[0] = c;
k = 1; k = 1;
while(1) { for(;;){
c = getchar(ts); c = getchar(ts);
if(c < 0) if(c < 0)
break; break;
if(ISNAMCHAR(c)) { if(ISNAMCHAR(c)){
if(k < SMALLBUFSIZE-1) if(k < SMALLBUFSIZE-1)
buf[k++] = c; buf[k++] = c;
} }
@ -1139,17 +1161,17 @@ ampersand(TokenSource* ts)
break; break;
} }
} }
if(c >= 0) { if(c >= 0){
fnd = _lookup(chartab, NCHARTAB, buf, k, &ans); fnd = _lookup(chartab, NCHARTAB, buf, k, &ans);
if(!fnd) { if(!fnd){
// Try prefixes of s // Try prefixes of s
if(c == ';' || c == '\n' || c == '\r') if(c == ';' || c == '\n' || c == '\r')
ungetchar(ts, c); ungetchar(ts, c);
i = k; i = k;
while(--k > 0) { while(--k > 0){
fnd = _lookup(chartab, NCHARTAB, buf, k, &ans); fnd = _lookup(chartab, NCHARTAB, buf, k, &ans);
if(fnd) { if(fnd){
while(i > k) { while(i > k){
i--; i--;
ungetchar(ts, buf[i]); ungetchar(ts, buf[i]);
} }
@ -1159,7 +1181,7 @@ ampersand(TokenSource* ts)
} }
} }
} }
if(!fnd) { if(!fnd){
backup(ts, savei); backup(ts, savei);
ans = '&'; ans = '&';
} }
@ -1181,14 +1203,14 @@ getchar(TokenSource* ts)
return -1; return -1;
buf = ts->data; buf = ts->data;
c = buf[ts->i]; c = buf[ts->i];
switch(ts->chset) { switch(ts->chset){
case ISO_8859_1: case ISO_8859_1:
if(c >= Winstart && c <= Winend) if(c >= Winstart && c <= Winend)
c = winchars[c - Winstart]; c = winchars[c - Winstart];
ts->i++; ts->i++;
break; break;
case US_Ascii: case US_Ascii:
if(c > 127) { if(c > 127){
if(warn) if(warn)
fprint(2, "non-ascii char (%x) when US-ASCII specified\n", c); fprint(2, "non-ascii char (%x) when US-ASCII specified\n", c);
} }
@ -1197,7 +1219,7 @@ getchar(TokenSource* ts)
case UTF_8: case UTF_8:
ok = fullrune((char*)(buf+ts->i), ts->edata-ts->i); ok = fullrune((char*)(buf+ts->i), ts->edata-ts->i);
n = chartorune(&r, (char*)(buf+ts->i)); n = chartorune(&r, (char*)(buf+ts->i));
if(ok) { if(ok){
if(warn && c == 0x80) if(warn && c == 0x80)
fprint(2, "warning: invalid utf-8 sequence (starts with %x)\n", ts->data[ts->i]); fprint(2, "warning: invalid utf-8 sequence (starts with %x)\n", ts->data[ts->i]);
ts->i += n; ts->i += n;
@ -1210,7 +1232,7 @@ getchar(TokenSource* ts)
} }
break; break;
case Unicode: case Unicode:
if(ts->i < ts->edata - 1) { if(ts->i < ts->edata - 1){
//standards say most-significant byte first //standards say most-significant byte first
c = (c << 8)|(buf[ts->i + 1]); c = (c << 8)|(buf[ts->i + 1]);
ts->i += 2; ts->i += 2;
@ -1235,9 +1257,9 @@ ungetchar(TokenSource* ts, int c)
char a[UTFmax]; char a[UTFmax];
n = 1; n = 1;
switch(ts->chset) { switch(ts->chset){
case UTF_8: case UTF_8:
if(c >= 128) { if(c >= 128){
r = c; r = c;
n = runetochar(a, &r); n = runetochar(a, &r);
} }
@ -1273,8 +1295,8 @@ _tokaval(Token* t, int attid, Rune** pans, int xfer)
Attr* attr; Attr* attr;
attr = t->attr; attr = t->attr;
while(attr != nil) { while(attr != nil){
if(attr->attid == attid) { if(attr->attid == attid){
if(pans != nil) if(pans != nil)
*pans = attr->value; *pans = attr->value;
if(xfer) if(xfer)
@ -1308,12 +1330,12 @@ Tconv(Fmt *f)
if(dbglex > 1) if(dbglex > 1)
i = snprint(buf, sizeof(buf), "[%d]", t->starti); i = snprint(buf, sizeof(buf), "[%d]", t->starti);
tag = t->tag; tag = t->tag;
if(tag == Data) { if(tag == Data){
i += snprint(buf+i, sizeof(buf)-i-1, "'%S'", t->text); i += snprint(buf+i, sizeof(buf)-i-1, "'%S'", t->text);
} }
else { else {
srbra = ""; srbra = "";
if(tag >= RBRA) { if(tag >= RBRA){
tag -= RBRA; tag -= RBRA;
srbra = "/"; srbra = "/";
} }
@ -1321,7 +1343,7 @@ Tconv(Fmt *f)
if(tag == Notfound) if(tag == Notfound)
tname = L(Lquestion); tname = L(Lquestion);
i += snprint(buf+i, sizeof(buf)-i-1, "<%s%S", srbra, tname); i += snprint(buf+i, sizeof(buf)-i-1, "<%s%S", srbra, tname);
for(a = t->attr; a != nil; a = a->next) { for(a = t->attr; a != nil; a = a->next){
aname = attrnames[a->attid]; aname = attrnames[a->attid];
i += snprint(buf+i, sizeof(buf)-i-1, " %S", aname); i += snprint(buf+i, sizeof(buf)-i-1, " %S", aname);
if(a->value != nil) if(a->value != nil)
@ -1356,7 +1378,7 @@ freeattrs(Attr* ahead)
Attr* nexta; Attr* nexta;
a = ahead; a = ahead;
while(a != nil) { while(a != nil){
nexta = a->next; nexta = a->next;
free(a->value); free(a->value);
free(a); free(a);
@ -1377,7 +1399,7 @@ _freetokens(Token* tarray, int n)
if(tarray == nil) if(tarray == nil)
return; return;
for(i = 0; i < n; i++) { for(i = 0; i < n; i++){
t = &tarray[i]; t = &tarray[i];
free(t->text); free(t->text);
freeattrs(t->attr); freeattrs(t->attr);