Subversion Repositories planix.SVN

Rev

Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
2 - 1
#include <u.h>
2
#include <libc.h>
3
#include <bio.h>
4
#include <draw.h>
5
#include <memdraw.h>
6
#include <thread.h>
7
#include <cursor.h>
8
#include <mouse.h>
9
#include <keyboard.h>
10
#include <frame.h>
11
#include <plumb.h>
12
#include <html.h>
13
#include <regexp.h>
14
#include "dat.h"
15
#include "fns.h"
16
 
17
static	Point		prevmouse;
18
static	Window	*mousew;
19
 
20
int
21
min(int a, int b)
22
{
23
	if(a < b)
24
		return a;
25
	return b;
26
}
27
 
28
int
29
max(int a, int b)
30
{
31
	if(a > b)
32
		return a;
33
	return b;
34
}
35
 
36
void
37
cvttorunes(char *p, int n, Rune *r, int *nb, int *nr, int *nulls)
38
{
39
	uchar *q;
40
	Rune *s;
41
	int j, w;
42
 
43
	/*
44
	 * Always guaranteed that n bytes may be interpreted
45
	 * without worrying about partial runes.  This may mean
46
	 * reading up to UTFmax-1 more bytes than n; the caller
47
	 * knows this.  If n is a firm limit, the caller should
48
	 * set p[n] = 0.
49
	 */
50
	q = (uchar*)p;
51
	s = r;
52
	for(j=0; j<n; j+=w){
53
		if(*q < Runeself){
54
			w = 1;
55
			*s = *q++;
56
		}else{
57
			w = chartorune(s, (char*)q);
58
			q += w;
59
		}
60
		if(*s)
61
			s++;
62
		else if(nulls)
63
			*nulls = TRUE;
64
	}
65
	*nb = (char*)q-p;
66
	*nr = s-r;
67
}
68
 
69
void
70
bytetorunestr(char *s, Runestr *rs)
71
{
72
	Rune *r;
73
	int nb, nr;
74
 
75
	nb = strlen(s);
76
	r = runemalloc(nb+1);
77
	cvttorunes(s, nb, r, &nb, &nr, nil);
78
	r[nr] = '\0';
79
	rs->nr = nr;
80
	rs->r = r;
81
}
82
 
83
void
84
error(char *s)
85
{
86
	fprint(2, "abaco: %s: %r\n", s);
87
//	abort();
88
	threadexitsall(s);
89
}
90
 
91
void*
92
emalloc(ulong n)
93
{
94
	void *p;
95
 
96
	p = malloc(n);
97
	if(p == nil)
98
		error("malloc failed");
99
	setmalloctag(p, getcallerpc(&n));
100
	memset(p, 0, n);
101
	return p;
102
}
103
 
104
void*
105
erealloc(void *p, ulong n)
106
{
107
	p = realloc(p, n);
108
	if(p == nil)
109
		error("realloc failed");
110
	setmalloctag(p, getcallerpc(&n));
111
	return p;
112
}
113
 
114
Rune*
115
erunestrdup(Rune *r)
116
{
117
	void *p;
118
 
119
	if(r == nil)
120
		return nil;
121
	p = runestrdup(r);
122
	if(p == nil)
123
		error("runestrdup failed");
124
	setmalloctag(p, getcallerpc(&r));
125
	return p;
126
}
127
 
128
char*
129
estrdup(char *s)
130
{
131
	char *t;
132
 
133
	t = strdup(s);
134
	if(t == nil)
135
		error("strdup failed");
136
	setmalloctag(t, getcallerpc(&s));
137
	return t;
138
}
139
 
140
int
141
runestreq(Runestr a, Runestr b)
142
{
143
	return runeeq(a.r, a.nr, b.r, b.nr);
144
}
145
 
146
int
147
runeeq(Rune *s1, uint n1, Rune *s2, uint n2)
148
{
149
	if(n1 != n2)
150
		return FALSE;
151
	return memcmp(s1, s2, n1*sizeof(Rune)) == 0;
152
}
153
 
154
void
155
closerunestr(Runestr *rs)
156
{
157
 
158
	rs->nr = 0;
159
	if(rs->r)
160
		free(rs->r);
161
	rs->r = nil;
162
}
163
 
164
void
165
copyrunestr(Runestr *a, Runestr *b)
166
{
167
	closerunestr(a);
168
	a->r = runemalloc(b->nr+1);
169
	runemove(a->r, b->r, b->nr);
170
	a->r[b->nr] = 0;
171
	a->nr = b->nr;
172
}
173
 
174
int
175
isalnum(Rune c)
176
{
177
	/*
178
	 * Hard to get absolutely right.  Use what we know about ASCII
179
	 * and assume anything above the Latin control characters is
180
	 * potentially an alphanumeric.
181
	 */
182
	if(c <= ' ')
183
		return FALSE;
184
	if(0x7F<=c && c<=0xA0)
185
		return FALSE;
186
	if(utfrune("!\"#$%&'()*+,-./:;<=>?@[\\]^`{|}~", c))
187
		return FALSE;
188
	return TRUE;
189
}
190
 
191
Rune*
192
skipbl(Rune *r, int n, int *np)
193
{
194
	while(n>0 && (*r==' ' || *r=='\t' || *r=='\n')){
195
		--n;
196
		r++;
197
	}
198
	*np = n;
199
	return r;
200
}
201
 
202
Rune*
203
findbl(Rune *r, int n, int *np)
204
{
205
	while(n>0 && *r!=' ' && *r!='\t' && *r!='\n'){
206
		--n;
207
		r++;
208
	}
209
	*np = n;
210
	return r;
211
}
212
 
213
int
214
istextfield(Item *i)
215
{
216
	Formfield *ff;
217
 
218
	ff = ((Iformfield *)i)->formfield;
219
	if(ff->ftype==Ftext || ff->ftype==Ftextarea || ff->ftype==Fpassword)
220
		return TRUE;
221
 
222
	return FALSE;
223
}
224
 
225
int
226
forceitem(Item *i)
227
{
228
	if(i->state&IFwrap && i->tag!=Iruletag && i->tag!=Itabletag)
229
		return FALSE;
230
 
231
	return TRUE;
232
}
233
 
234
int
235
dimwidth(Dimen d, int w)
236
{
237
	int s, k;
238
 
239
	k = dimenkind(d);
240
	if(k == Dnone)
241
		return w;
242
	s = dimenspec(d);
243
	if(k == Dpixels)
244
		 w = s;
245
	else if(k==Dpercent && s<100)
246
		w = s*w/100;
247
 
248
	return w;
249
}
250
 
251
void
252
frdims(Dimen *d, int n, int t, int **ret)
253
{
254
	int totpix, totpcnt, totrel;
255
	double spix, spcnt, relu, vd;
256
	int tt, trest, totpixrel, minrelu, i;
257
	int *x, *spec, *kind;
258
 
259
	if(n == 1){
260
		*ret = x = emalloc(sizeof(int));
261
		x[0] = t;
262
		return;
263
	}
264
	totpix = totpcnt = totrel = 0;
265
	spec = emalloc(n*sizeof(int));
266
	kind = emalloc(n*sizeof(int));
267
	for(i=0; i<n; i++){
268
		spec[i] = dimenspec(d[i]);
269
		if(spec[i] < 0)
270
			spec[i] = 0;
271
		kind[i] = dimenkind(d[i]);
272
		switch(kind[i]){
273
		case Dpixels:
274
			totpix += spec[i];
275
			break;
276
		case Dpercent:
277
			totpcnt += spec[i];
278
			break;
279
		case Drelative:
280
			totrel += spec[i];
281
			break;
282
		case Dnone:
283
			totrel++;
284
			break;
285
		}
286
	}
287
	spix = spcnt = 1.0;
288
	minrelu = 0;
289
	if(totrel > 0)
290
		minrelu = Scrollsize+Scrollgap;
291
	relu = (double)minrelu;
292
	tt = totpix + t*totpcnt/100 + totrel*minrelu;
293
	if(tt < t){
294
		if(totrel == 0){
295
			if(totpcnt != 0)
296
				spcnt = (double)((t-totpix)*100)/(double)(t*totpcnt);
297
			else
298
				spix = (double)t/(double)totpix;
299
		}else
300
			relu += (double)(t-tt)/(double)totrel;
301
	}else{
302
		totpixrel = totpix + totrel*minrelu;
303
		if(totpixrel < t)
304
			spcnt = (double)((t-totpixrel)*100)/(double)(t*totpcnt);
305
		else{
306
			trest = t - totrel*minrelu;
307
			if(trest > 0)
308
				spcnt = (double)trest/(double)(totpix + (t*totpcnt/100));
309
			else{
310
				spcnt = (double)t/(double)tt;
311
				relu = 0.0;
312
			}
313
			spix = spcnt;
314
		}
315
	}
316
	x = emalloc(n * sizeof(int));
317
	tt = 0;
318
	for(i=0; i<n-1; i++){
319
		vd = (double)spec[i];
320
		switch(kind[i]){
321
		case Dpixels:
322
			vd = vd*spix;
323
			break;
324
		case Dpercent:
325
			vd = vd*(double)t*spcnt/100.0;
326
			break;
327
		case Drelative:
328
			vd = vd*relu;
329
			break;
330
		case Dnone:
331
			vd = relu;
332
			break;
333
		}
334
		x[i] = (int)(vd+.5);
335
		tt += x[i];
336
	}
337
	x[n - 1] = t - tt;
338
	*ret = x;
339
	free(spec);
340
	free(kind);
341
}
342
 
343
Image *
344
getbg(Page *p)
345
{
346
	Docinfo *d;
347
	Cimage *ci;
348
	Image *bg;
349
 
350
	d = p->doc;
351
	if(d->backgrounditem){
352
		if(d->backgrounditem->aux){
353
			ci = d->backgrounditem->aux;
354
			if(ci->mi)
355
				getimage(ci, d->backgrounditem->altrep);
356
			bg = ci->i;
357
		}else
358
			bg = display->white;
359
	}else
360
		bg = getcolor(d->background.color);
361
 
362
	return bg;
363
}
364
 
365
Rune *
366
getbase(Page *p)
367
{
368
	if(p->doc)
369
		return p->doc->base;
370
	if(p->url->act.r)
371
		return p->url->act.r;
372
	return p->url->src.r;
373
}
374
 
375
Image *
376
eallocimage(Display *d, Rectangle r, ulong chan, int repl, int col)
377
{
378
	Image *i;
379
 
380
	i = allocimage(d, r, chan, repl, col);
381
	if(i == nil)
382
		error("allocimage failed");
383
	return i;
384
}
385
 
386
void
387
rect3d(Image *im, Rectangle r, int i, Image **c, Point sp)
388
{
389
	Point p[6];
390
 
391
	if(i < 0){
392
		r = insetrect(r, i);
393
		sp = addpt(sp, Pt(i,i));
394
		i = -i;
395
	}
396
	draw(im, Rect(r.min.x+i, r.min.y+i, r.max.x-i, r.max.y-i), c[2], nil, sp);
397
	p[0] = r.min;
398
	p[1] = Pt(r.min.x, r.max.y);
399
	p[2] = Pt(r.min.x+i, r.max.y-i);
400
	p[3] = Pt(r.min.x+i, r.min.y+i);
401
	p[4] = Pt(r.max.x-i, r.min.y+i);
402
	p[5] = Pt(r.max.x, r.min.y);
403
	fillpoly(im, p, 6, 0, c[0], sp);
404
	p[0] = r.max;
405
	p[1] = Pt(r.min.x, r.max.y);
406
	p[2] = Pt(r.min.x+i, r.max.y-i);
407
	p[3] = Pt(r.max.x-i, r.max.y-i);
408
	p[4] = Pt(r.max.x-i, r.min.y+i);
409
	p[5] = Pt(r.max.x, r.min.y);
410
	fillpoly(im, p, 6, 0, c[1], sp);
411
}
412
 
413
void
414
ellipse3d(Image *im, Point p, int rad, int i, Image **c, Point sp)
415
{
416
	fillarc(im, p, rad, rad, c[0], sp, 45, 180);
417
	fillarc(im, p, rad, rad, c[1], sp,  45, -180);
418
	fillellipse(im, p, rad-i, rad-i, c[2], sp);
419
}
420
 
421
void
422
colarray(Image **c, Image *c0, Image *c1, Image *c2, int checked)
423
{
424
	if(checked){
425
		c[0] = c0;
426
		c[1] = c1;
427
	}else{
428
		c[0] = c1;
429
		c[1] = c0;
430
	}
431
	c[2] = c2;
432
}
433
 
434
static char *deffontpaths[] = {
435
#include "fonts.h"
436
};
437
 
438
static char *fontpaths[NumFnt];
439
static Font *fonts[NumFnt];
440
 
441
void
442
initfontpaths(void)
443
{
444
	Biobufhdr *bp;
445
	char buf[128];
446
	int i;
447
 
448
	/* we don't care if getenv(2) fails */
449
	snprint(buf, sizeof(buf)-1, "%s/lib/abaco.fonts", getenv("home"));
450
	if((bp=Bopen(buf, OREAD)) == nil)
451
		goto Default;
452
 
453
	for(i=0; i<NumFnt; i++)
454
		if((fontpaths[i]=Brdstr(bp, '\n', 1)) == nil)
455
			goto Error;
456
 
457
	Bterm(bp);
458
	return;
459
Error:
460
	fprint(2, "abaco: not enough fontpaths in '%s'\n", buf);
461
	Bterm(bp);
462
	for(i--; i>=0; i--)
463
		free(fontpaths[i]);
464
Default:
465
	for(i=0; i<NumFnt; i++)
466
		fontpaths[i] = deffontpaths[i];
467
}
468
 
469
Font *
470
getfont(int i)
471
{
472
	if(fonts[i] == nil){
473
		fonts[i] = openfont(display, fontpaths[i]);
474
		if(fonts[i] == nil)
475
			error("can't open font file");
476
	}
477
	return fonts[i];
478
}
479
 
480
typedef struct Color Color;
481
 
482
struct Color {
483
	int	rgb;
484
	Image	*i;
485
	Color	*next;
486
};
487
 
488
enum {
489
	NHASH = 19,
490
};
491
 
492
static Color *colortab[NHASH];
493
 
494
Image *
495
getcolor(int rgb)
496
{
497
	Color *c;
498
	int h;
499
 
500
	if(rgb == 0xFFFFFF)
501
		return display->white;
502
	else if(rgb == 0x000000)
503
		return display->black;
504
 
505
	h = rgb%NHASH;
506
	for(c=colortab[h]; c!=nil; c=c->next)
507
		if(c->rgb == rgb){
508
			flushimage(display, 0);	/* BUG? */
509
			return c->i;
510
		}
511
	c = emalloc(sizeof(Color));
512
	c->i = eallocimage(display, Rect(0,0,1,1), screen->chan, 1, (rgb<<8)|0xFF);
513
	c->rgb = rgb;
514
	c->next = colortab[h];
515
	colortab[h] = c;
516
 
517
	return c->i;
518
}
519
 
520
int
521
plumbrunestr(Runestr *rs, char *attr)
522
{
523
	Plumbmsg *m;
524
	int i;
525
 
526
	i = -1;
527
	if(plumbsendfd >= 0){
528
		m = emalloc(sizeof(Plumbmsg));
529
		m->src = estrdup("abaco");
530
		m->dst = nil;
531
		m->wdir = estrdup("/tmp");
532
		m->type = estrdup("text");
533
		if(attr)
534
			m->attr = plumbunpackattr(attr);
535
		else
536
			m->attr = nil;
537
		m->data = smprint("%.*S", rs->nr, rs->r);
538
		m->ndata = -1;
539
		i = plumbsend(plumbsendfd, m);
540
		plumbfree(m);
541
	}
542
	return i;
543
}
544
 
545
int
546
hexdigit(int v)
547
{
548
	if(0<=v && v<=9)
549
		return '0' + v;
550
	else
551
		return 'A' + v - 10;
552
}
553
 
554
static int
555
inclass(char c, Rune* cl)
556
{
557
	int n, ans, negate, i;
558
 
559
	n = runestrlen(cl);
560
	if(n == 0)
561
		return 0;
562
	ans = 0;
563
	negate = 0;
564
	if(cl[0] == '^'){
565
		negate = 1;
566
		cl++;
567
		n--;
568
	}
569
	for(i=0; i<n; i++){
570
		if(cl[i]=='-' && i>0 && i<n-1){
571
			if(c>=cl[i - 1] && c<=cl[i+1]){
572
				ans = 1;
573
				break;
574
			}
575
			i++;
576
		}
577
		else if(c == cl[i]){
578
			ans = 1;
579
			break;
580
		}
581
	}
582
	if(negate)
583
		ans = !ans;
584
	return ans;
585
}
586
 
587
Rune*
588
ucvt(Rune* s)
589
{
590
	Rune* u;
591
	char *t;
592
	int i, c, n, j, len;
593
 
594
	t = smprint("%S", s);
595
	n = strlen(t);
596
	len = 0;
597
	for(i=0; i<n; i++){
598
		c = t[i];
599
		if(inclass(c, L"- /$_@.!*'(),a-zA-Z0-9"))
600
			len++;
601
		else
602
			len += 3;
603
	}
604
	u = runemalloc(len+1);
605
	j = 0;
606
 
607
	for(i=0; i<n; i++){
608
		c = t[i];
609
		if(inclass(c, L"-/$_@.!*'(),a-zA-Z0-9"))
610
			u[j++] = c;
611
		else if(c == ' ')
612
			u[j++] = '+';
613
		else {
614
			u[j++] = '%';
615
			u[j++] = hexdigit((c >> 4)&15);
616
			u[j++] = hexdigit(c&15);
617
		}
618
	}
619
	u[j] = 0;
620
	free(t);
621
	return u;
622
}
623
 
624
void
625
reverseimages(Iimage **head)
626
{
627
	Iimage *r, *c, *n;
628
 
629
	r = nil;
630
	for(c=*head; c!=nil; c=n){
631
		n = c->nextimage;
632
		c->nextimage = r;
633
		r = c;
634
	}
635
	*head = r;
636
}
637
 
638
char urlexpr[] = "^(https?|ftp|file|gopher|mailto|news|nntp|telnet|wais|"
639
	"prospero)://([a-zA-Z0-9_@\\-]+([.:][a-zA-Z0-9_@\\-]+)*)";
640
Reprog	*urlprog;
641
 
642
int
643
validurl(Rune *r)
644
{
645
	Resub rs[10];
646
 
647
	if(urlprog == nil){
648
		urlprog = regcomp(urlexpr);
649
		if(urlprog == nil)
650
			error("regcomp");
651
	}
652
	memset(rs, 0, sizeof(rs));
653
	if(rregexec(urlprog, r, rs, nelem(rs)) == 0)
654
		return FALSE;
655
	return TRUE;
656
}
657
 
658
void
659
execproc(void *v)
660
{
661
	Channel *sync;
662
	Exec *e;
663
	int p[2], q[2];
664
	char *cmd;
665
 
666
	threadsetname("execproc");
667
	e = v;
668
	p[0] = e->p[0];
669
	p[1] = e->p[1];
670
	q[0] = e->q[0];
671
	q[1] = e->q[1];
672
	cmd = e->cmd;
673
	sync = e->sync;
674
	rfork(RFFDG);
675
	free(e);
676
	dup(p[0], 0);
677
	close(p[0]);
678
	close(p[1]);
679
	if(q[0]){
680
		dup(q[1], 1);
681
		close(q[0]);
682
		close(q[1]);
683
	}
684
	if(!procstderr)
685
		close(2);
686
	procexecl(sync, "/bin/rc", "rc", "-c", cmd, 0);
687
	error("can't exec");
688
}
689
 
690
static void
691
writeproc(void *v)
692
{
693
	Channel *sync;
694
	void **a;
695
	char *s;
696
	long np;
697
	int fd, i, n;
698
 
699
	threadsetname("writeproc");
700
	a = v;
701
	sync = a[0];
702
	fd = (uintptr)a[1];
703
	s = a[2];
704
	np =(uintptr)a[3];
705
	free(a);
706
 
707
	for(i=0; i<np; i+=n){
708
		n = np-i;
709
		if(n > BUFSIZE)
710
			n = BUFSIZE;
711
		if(write(fd, s+i, n) != n)
712
			break;
713
	}
714
	close(fd);
715
	sendul(sync, i);
716
}
717
 
718
struct {
719
	char *mime;
720
	char *tcs;
721
}tcstab[] = {
722
 
723
#include "tcs.h"
724
 
725
	/* not generated by the script */
726
	"euc_jp", "jis",
727
	"euc_kr", "euc-k",
728
	"windows-874", "tis",
729
	nil,	nil,
730
};
731
 
732
enum {
733
	Winstart = 127,
734
	Winend = 159
735
};
736
 
737
static int winchars[] = {
738
	 8226,	/* 8226 is a bullet */
739
	8226, 8226, 8218, 402, 8222, 8230, 8224, 8225,
740
	710, 8240, 352, 8249, 338, 8226, 8226, 8226,
741
	8226, 8216, 8217, 8220, 8221, 8226, 8211, 8212,
742
	732, 8482, 353, 8250, 339, 8226, 8226, 376
743
};
744
 
745
char *
746
tcs(char *cs, char *s, long *np)
747
{
748
	Channel *sync;
749
	Exec *e;
750
	Rune r;
751
	long i, n;
752
	void **a;
753
	uchar *us;
754
	char buf[BUFSIZE], cmd[50];
755
	char *t, *u;
756
	int p[2], q[2];
757
 
758
 
759
	if(s==nil || *s=='\0' || *np==0){
760
		werrstr("tcs failed: no data");
761
		return s;
762
	}
763
 
764
	if(cs == nil){
765
		werrstr("tcs failed: no charset");
766
		return s;
767
	}
768
 
769
	if(cistrncmp(cs, "utf-8", 5)==0 || cistrncmp(cs, "utf8", 4)==0)
770
		return s;
771
 
772
	for(i=0; tcstab[i].mime!=nil; i++)
773
		if(cistrncmp(cs, tcstab[i].mime, strlen(tcstab[i].mime)) == 0)
774
			break;
775
 
776
	if(tcstab[i].mime == nil){
777
		fprint(2, "abaco: charset: %s not supported\n", cs);
778
		goto latin1;
779
	}
780
	if(cistrcmp(tcstab[i].tcs, "8859-1")==0 || cistrcmp(tcstab[i].tcs, "ascii")==0){
781
latin1:
782
		n = 0;
783
		for(us=(uchar*)s; *us; us++)
784
			n += runelen(*us);
785
		n++;
786
		t = emalloc(n);
787
		for(us=(uchar*)s, u=t; *us; us++){
788
			if(*us>=Winstart && *us<=Winend)
789
				*u++ = winchars[*us-Winstart];
790
			else{
791
				r = *us;
792
				u += runetochar(u, &r);
793
			}
794
		}
795
		*u = 0;
796
		free(s);
797
		return t;
798
	}
799
 
800
	if(pipe(p)<0 || pipe(q)<0)
801
		error("can't create pipe");
802
 
803
	sync = chancreate(sizeof(ulong), 0);
804
	if(sync == nil)
805
		error("can't create channel");
806
 
807
	snprint(cmd, sizeof cmd, "tcs -f %s", tcstab[i].tcs);
808
	e = emalloc(sizeof(Exec));
809
	e->p[0] = p[0];
810
	e->p[1] = p[1];
811
	e->q[0] = q[0];
812
	e->q[1] = q[1];
813
	e->cmd = cmd;
814
	e->sync = sync;
815
	proccreate(execproc, e, STACK);
816
	recvul(sync);
817
	chanfree(sync);
818
	close(p[0]);
819
	close(q[1]);
820
 
821
	/* in case tcs fails */
822
	t = s;
823
	sync = chancreate(sizeof(ulong), 0);
824
	if(sync == nil)
825
		error("can't create channel");
826
 
827
	a = emalloc(4*sizeof(void *));
828
	a[0] = sync;
829
	a[1] = (void *)p[1];
830
	a[2] = s;
831
	a[3] = (void *)*np;
832
	proccreate(writeproc, a, STACK);
833
 
834
	s = nil;
835
	while((n = read(q[0], buf, sizeof(buf))) > 0){
836
		s = erealloc(s, i+n+1);
837
		memmove(s+i, buf, n);
838
		i += n;
839
		s[i] = '\0';
840
	}
841
	n = recvul(sync);
842
	if(n != *np)
843
		fprint(2, "tcs: did not write %ld; wrote %uld\n", *np, n);
844
 
845
	*np = i;
846
	chanfree(sync);
847
	close(q[0]);
848
 
849
	if(s == nil){
850
		fprint(2, "tcs failed: can't convert charset=%s to %s\n", cs, tcstab[i].tcs);
851
		return t;
852
	}
853
	free(t);
854
 
855
	return s;
856
}
857
 
858
static
859
int
860
isspace(char c)
861
{
862
	return c==' ' || c== '\t' || c=='\r' || c=='\n';
863
}
864
 
865
static
866
int
867
findctype(char *b, int l, char *keyword, char *s)
868
{
869
	char *p, *e;
870
	int i;
871
 
872
	p = cistrstr(s, keyword);
873
	if(!p)
874
		return -1;
875
	p += strlen(keyword);
876
	while(*p && isspace(*p))
877
		p++;
878
	if(*p != '=')
879
		return -1;
880
	p++;
881
	while(*p && isspace(*p))
882
		p++;
883
	if(!*p)
884
		return -1;
885
	if(*p == '"'){
886
		p++;
887
		e = strchr(p, '"');
888
		if(!e)
889
			return -1;
890
	}else
891
		for(e = p; *e < 127 && *e > ' ' ; e++)
892
			;
893
	i = e-p;
894
	if(i < 1)
895
		return -1;
896
	snprint(b, l, "%.*s", i, p);
897
	return 0;
898
}
899
 
900
static
901
int
902
finddocctype(char *b, int l, char *s)
903
{
904
	char *p, *e;
905
 
906
	p = cistrstr(s, "<meta");
907
	if(!p)
908
		return -1;
909
	p += 5;
910
	e = strchr(s, '>');
911
	if(!e)
912
		return -1;
913
	snprint(b, l, "%.*s", (int)(e-p), p);
914
	return 0;
915
}
916
 
917
static
918
int
919
findxmltype(char *b, int l, char *s)
920
{
921
	char *p, *e;
922
 
923
	p = cistrstr(s, "<?xml ");
924
	if(!p)
925
		return -1;
926
 
927
	p += 6;
928
	e = strstr(p, "?>");
929
	if(!e)
930
		return -1;
931
	snprint(b, l, "%.*s", (int)(e-p), p);
932
 
933
	return 0;
934
}
935
 
936
/*
937
 * servers can lie about lie about the charset,
938
 * so we use the charset based on the priority.
939
 */
940
char *
941
convert(Runestr ctype, char *s, long *np)
942
{
943
	char t[25], buf[256];
944
 
945
	*t = '\0';
946
	if(ctype.nr){
947
		snprint(buf, sizeof(buf), "%.*S", ctype.nr, ctype.r);
948
		findctype(t, sizeof(t), "charset", buf);
949
	}
950
	if(findxmltype(buf, sizeof(buf), s)==0)
951
		findctype(t, sizeof(t), "encoding", buf);
952
	if(finddocctype(buf, sizeof(buf), s) == 0)
953
		findctype(t, sizeof(t), "charset", buf);
954
 
955
	if(*t == '\0')
956
		strcpy(t, charset);
957
	return tcs(t, s, np);
958
}
959
 
960
int
961
xtofchar(Rune *s, Font *f, long p)
962
{
963
	Rune *r;
964
	int q;
965
 
966
	if(p == 0)
967
		return 0;
968
 
969
	q = 0;
970
	for(r=s; *r!=L'\0'; r++){
971
		p -= runestringnwidth(f, r, 1);
972
		if(p < 0)
973
			break;
974
		q++;
975
	}
976
	return q;
977
}
978
 
979
int
980
istextsel(Page *p, Rectangle r, int *q0, int *q1, Rune *s, Font *f)
981
{
982
	int topinr, botinr;
983
 
984
	*q0 = *q1 = 0;
985
	topinr= ptinrect(p->top, r);
986
	if(topinr || (r.min.y>p->top.y && r.max.y<p->bot.y))
987
		p->selecting = TRUE;
988
	botinr = ptinrect(p->bot, r);
989
	if(botinr || r.min.y>p->bot.y)
990
		p->selecting = FALSE;
991
 
992
	if(topinr || botinr){
993
		if(topinr)
994
			*q0 = xtofchar(s, f, p->top.x-r.min.x);
995
		if(botinr)
996
			*q1 = xtofchar(s, f, p->bot.x-r.min.x);
997
		if(*q0!=0 || *q1!=0)
998
			return TRUE;
999
	}
1000
	return p->selecting;
1001
}
1002
 
1003
Point
1004
getpt(Page *p, Point xy)
1005
{
1006
	xy.x = xy.x-p->r.min.x+p->pos.x;
1007
	xy.y = xy.y-p->r.min.y+p->pos.y;
1008
 
1009
	return xy;
1010
}
1011
 
1012
void
1013
getimage(Cimage *ci, Rune *altr)
1014
{
1015
	Rectangle r;
1016
	Memimage *mi;
1017
	Image *i, *i2;
1018
	char buf[128];
1019
	uchar *bits;
1020
	int nbits;
1021
 
1022
	mi = ci->mi;
1023
	if(mi == nil){
1024
		snprint(buf, sizeof(buf), "[%S]", altr ? altr : L"IMG");
1025
		r.min = Pt(0, 0);
1026
		r.max.x = 2*Space + stringwidth(font, buf);
1027
		r.max.y = 2*Space + font->height;
1028
		ci->i = eallocimage(display, r, GREY1, 1, DBlack);
1029
		r.min.x += Space;
1030
		r.min.y += Space;
1031
		string(ci->i, r.min, display->white, ZP, font, buf);
1032
		return;
1033
	}
1034
	nbits = bytesperline(mi->r, mi->depth)*Dy(mi->r);
1035
	bits = emalloc(nbits);
1036
	unloadmemimage(mi, mi->r, bits, nbits);
1037
/*
1038
	/* get rid of alpha channel from transparent gif * /
1039
 
1040
	if(mi->depth == 16){
1041
		for(y=1; y<nbits; y+=2)
1042
			bits[y>>1] = bits[y];
1043
	}
1044
*/
1045
	i = eallocimage(display, mi->r, mi->chan, 0, DNofill);
1046
	loadimage(i, i->r, bits, nbits);
1047
	i2 = eallocimage(display, i->r, RGB24, 1, DNofill);
1048
	draw(i2, i2->r, display->black, nil, ZP);
1049
	draw(i2, i2->r, i, nil, i->r.min);
1050
	free(bits);
1051
	freememimage(mi);
1052
	freeimage(i);
1053
	ci->i = i2;
1054
	ci->mi = nil;
1055
}
1056
 
1057
static
1058
void
1059
fixtext1(Item **list)
1060
{
1061
	Itext *text, *ntext;
1062
	Item *it, *prev;
1063
	Rune *s, *s1, *s2;
1064
	int n;
1065
 
1066
	if(*list == nil)
1067
		return;
1068
 
1069
	prev = nil;
1070
	for(it=*list; it!=nil; it=prev->next){
1071
		if(it->tag!=Itexttag || forceitem(it))
1072
			goto Continue;
1073
 
1074
		text = (Itext *)it;
1075
		s = text->s;
1076
		while(*s && isspacerune(*s))
1077
			s++;
1078
		if(!*s){
1079
			if(prev == nil)
1080
				prev = *list = it->next;
1081
			else
1082
				prev->next = it->next;
1083
 
1084
			it->next = nil;
1085
			freeitems(it);
1086
			if(prev == nil)
1087
				return;
1088
			continue;
1089
		}
1090
		n = 0;
1091
		while(s[n] && !isspacerune(s[n]))
1092
			n++;
1093
 
1094
		if(!s[n])
1095
			goto Continue;
1096
 
1097
		s1 = runemalloc(n+1);
1098
		s1 = runemove(s1, s, n);
1099
		s1[n] = L'\0';
1100
		s += n;
1101
 
1102
		while(*s && isspacerune(*s))
1103
			s++;
1104
 
1105
		if(*s){
1106
			n = runestrlen(s);
1107
			s2 = runemalloc(n+1);
1108
			runemove(s2, s, n);
1109
			s2[n] = L'\0';
1110
			ntext = emalloc(sizeof(Itext));
1111
			ntext->s = s2;
1112
			ntext->ascent = text->ascent;
1113
			ntext->anchorid = text->anchorid;
1114
			ntext->state = text->state&~(IFbrk|IFbrksp|IFnobrk|IFcleft|IFcright);
1115
			ntext->tag = text->tag;
1116
			ntext->fnt = text->fnt;
1117
			ntext->fg = text->fg;
1118
			ntext->ul = text->ul;
1119
			ntext->next = (Item *)text->next;
1120
			text->next = (Item *)ntext;
1121
		}
1122
		free(text->s);
1123
		text->s = s1;
1124
    Continue:
1125
		prev = it;
1126
	}
1127
}
1128
 
1129
void
1130
fixtext(Page *p)
1131
{
1132
	Tablecell *c;
1133
	Table *t;
1134
 
1135
	fixtext1(&p->items);
1136
	for(t=p->doc->tables; t!=nil; t=t->next)
1137
		for(c=t->cells; c!=nil; c=c->next)
1138
			fixtext1(&c->content);
1139
}
1140
 
1141
typedef struct Refresh Refresh;
1142
 
1143
struct Refresh
1144
{
1145
	Page *p;
1146
	Refresh *next;
1147
};
1148
 
1149
static Refresh *refreshs = nil;
1150
static QLock refreshlock;
1151
 
1152
void
1153
addrefresh(Page *p, char *fmt, ...)
1154
{
1155
	Refresh *r;
1156
	Rune *s;
1157
	va_list arg;
1158
 
1159
	if(p->aborting)
1160
		return;
1161
 
1162
	va_start(arg, fmt);
1163
	s = runevsmprint(fmt, arg);
1164
	va_end(arg);
1165
	if(s == nil)
1166
		error("runevsmprint failed");
1167
 
1168
	if(p->status){
1169
		free(p->status);
1170
		p->status = nil;
1171
	}
1172
	p->status = s;
1173
	qlock(&refreshlock);
1174
	for(r=refreshs; r!=nil; r=r->next)
1175
		if(r->p == p)
1176
			goto Return;
1177
 
1178
	incref(p->w);				/* flushrefresh will decref */
1179
	r = emalloc(sizeof(Refresh));
1180
	r->p = p;
1181
	r->next = refreshs;
1182
	refreshs = r;
1183
 
1184
    Return:
1185
	nbsendp(crefresh, nil);
1186
	qunlock(&refreshlock);
1187
}
1188
 
1189
/* called while row is locked */
1190
void
1191
flushrefresh(void)
1192
{
1193
	Refresh *r, *next;
1194
	Page *p;
1195
 
1196
	qlock(&refreshlock);
1197
	for(r=refreshs; r!=nil; r=next){
1198
		p = r->p;
1199
		if(p->changed==TRUE && p->aborting==FALSE){
1200
			p->changed = FALSE;
1201
			if(p->parent==nil || p->loading==FALSE)
1202
				pagerender(p);
1203
			if(!p->refresh.t)
1204
				pagesetrefresh(p);
1205
		}
1206
		if(p->status){
1207
			winsetstatus(p->w, p->status);
1208
			free(p->status);
1209
			p->status = nil;
1210
		}
1211
		winseturl(p->w);
1212
		winsettag(p->w);
1213
		decref(p->w);
1214
		next = r->next;
1215
		free(r);
1216
	}
1217
	refreshs = nil;
1218
	qunlock(&refreshlock);
1219
}
1220
 
1221
void
1222
savemouse(Window *w)
1223
{
1224
	prevmouse = mouse->xy;
1225
	mousew = w;
1226
}
1227
 
1228
void
1229
restoremouse(Window *w)
1230
{
1231
	if(mousew!=nil && mousew==w)
1232
		moveto(mousectl, prevmouse);
1233
	mousew = nil;
1234
}
1235
 
1236
void
1237
clearmouse()
1238
{
1239
	mousew = nil;
1240
}
1241
 
1242
/*
1243
 * Heuristic city.
1244
 */
1245
Window*
1246
makenewwindow(Page *p)
1247
{
1248
	Column *c;
1249
	Window *w, *bigw, *emptyw;
1250
	Page *emptyp;
1251
	int i, y, el;
1252
 
1253
	if(activecol)
1254
		c = activecol;
1255
	else if(selpage && selpage->col)
1256
		c = selpage->col;
1257
	else if(p && p->col)
1258
		c = p->col;
1259
	else{
1260
		if(row.ncol==0 && rowadd(&row, nil, -1)==nil)
1261
			error("can't make column");
1262
		c = row.col[row.ncol-1];
1263
	}
1264
	activecol = c;
1265
	if(p==nil || p->w==nil || c->nw==0)
1266
		return coladd(c, nil, nil, -1);
1267
 
1268
	/* find biggest window and biggest blank spot */
1269
	emptyw = c->w[0];
1270
	bigw = emptyw;
1271
	for(i=1; i<c->nw; i++){
1272
		w = c->w[i];
1273
		/* use >= to choose one near bottom of screen */
1274
		if(Dy(w->page.all) >= Dy(bigw->page.all))
1275
			bigw = w;
1276
		if(w->page.lay==nil && Dy(w->page.all) >= Dy(emptyw->page.all))
1277
			emptyw = w;
1278
	}
1279
	emptyp = &emptyw->page;
1280
	el = Dy(emptyp->all);
1281
	/* if empty space is big, use it */
1282
	if(el>15 || (el>3 && el>(Dy(bigw->page.all)-1)/2))
1283
		y = emptyp->all.max.y;
1284
	else{
1285
		/* if this window is in column and isn't much smaller, split it */
1286
		if(p->col==c && Dy(p->w->r)>2*Dy(bigw->r)/3)
1287
			bigw = p->w;
1288
		y = (bigw->r.min.y + bigw->r.max.y)/2;
1289
	}
1290
	w = coladd(c, nil, nil, y);
1291
	colgrow(w->col, w, 1);
1292
	return w;
1293
}