1 /* $Id: buffer.c,v 1.30 2010/07/18 14:10:09 htrb Exp $ */
2 #include "fm.h"
3
4 #ifdef USE_MOUSE
5 #ifdef USE_GPM
6 #include <gpm.h>
7 #endif
8 #if defined(USE_GPM) || defined(USE_SYSMOUSE)
9 extern int do_getch();
10 #define getch() do_getch()
11 #endif /* USE_GPM */
12 #endif /* USE_MOUSE */
13
14 #ifdef __EMX__
15 #include <sys/kbdscan.h>
16 #include <strings.h>
17 #endif
18 char *NullLine = "";
19 Lineprop NullProp[] = { 0 };
20
21 /*
22 * Buffer creation
23 */
24 Buffer *
newBuffer(int width)25 newBuffer(int width)
26 {
27 Buffer *n;
28
29 n = New(Buffer);
30 if (n == NULL)
31 return NULL;
32 bzero((void *)n, sizeof(Buffer));
33 n->width = width;
34 n->COLS = COLS;
35 n->LINES = LASTLINE;
36 n->currentURL.scheme = SCM_UNKNOWN;
37 n->baseURL = NULL;
38 n->baseTarget = NULL;
39 n->buffername = "";
40 n->bufferprop = BP_NORMAL;
41 n->clone = New(int);
42 *n->clone = 1;
43 n->trbyte = 0;
44 #ifdef USE_SSL
45 n->ssl_certificate = NULL;
46 #endif
47 #ifdef USE_M17N
48 n->auto_detect = WcOption.auto_detect;
49 #endif
50 n->check_url = MarkAllPages; /* use default from -o mark_all_pages */
51 n->need_reshape = 1; /* always reshape new buffers to mark URLs */
52 return n;
53 }
54
55 /*
56 * Create null buffer
57 */
58 Buffer *
nullBuffer(void)59 nullBuffer(void)
60 {
61 Buffer *b;
62
63 b = newBuffer(COLS);
64 b->buffername = "*Null*";
65 return b;
66 }
67
68 /*
69 * clearBuffer: clear buffer content
70 */
71 void
clearBuffer(Buffer * buf)72 clearBuffer(Buffer *buf)
73 {
74 buf->firstLine = buf->topLine = buf->currentLine = buf->lastLine = NULL;
75 buf->allLine = 0;
76 }
77
78 /*
79 * discardBuffer: free buffer structure
80 */
81
82 void
discardBuffer(Buffer * buf)83 discardBuffer(Buffer *buf)
84 {
85 int i;
86 Buffer *b;
87
88 #ifdef USE_IMAGE
89 deleteImage(buf);
90 #endif
91 clearBuffer(buf);
92 for (i = 0; i < MAX_LB; i++) {
93 b = buf->linkBuffer[i];
94 if (b == NULL)
95 continue;
96 b->linkBuffer[REV_LB[i]] = NULL;
97 }
98 if (buf->savecache)
99 unlink(buf->savecache);
100 if (--(*buf->clone))
101 return;
102 if (buf->pagerSource)
103 ISclose(buf->pagerSource);
104 if (buf->sourcefile &&
105 (!buf->real_type || strncasecmp(buf->real_type, "image/", 6))) {
106 if (buf->real_scheme != SCM_LOCAL || buf->bufferprop & BP_FRAME)
107 unlink(buf->sourcefile);
108 }
109 if (buf->header_source)
110 unlink(buf->header_source);
111 if (buf->mailcap_source)
112 unlink(buf->mailcap_source);
113 while (buf->frameset) {
114 deleteFrameSet(buf->frameset);
115 buf->frameset = popFrameTree(&(buf->frameQ));
116 }
117 }
118
119 /*
120 * namedBuffer: Select buffer which have specified name
121 */
122 Buffer *
namedBuffer(Buffer * first,char * name)123 namedBuffer(Buffer *first, char *name)
124 {
125 Buffer *buf;
126
127 if (!strcmp(first->buffername, name)) {
128 return first;
129 }
130 for (buf = first; buf->nextBuffer != NULL; buf = buf->nextBuffer) {
131 if (!strcmp(buf->nextBuffer->buffername, name)) {
132 return buf->nextBuffer;
133 }
134 }
135 return NULL;
136 }
137
138 /*
139 * deleteBuffer: delete buffer
140 */
141 Buffer *
deleteBuffer(Buffer * first,Buffer * delbuf)142 deleteBuffer(Buffer *first, Buffer *delbuf)
143 {
144 Buffer *buf, *b;
145
146 if (first == delbuf && first->nextBuffer != NULL) {
147 buf = first->nextBuffer;
148 discardBuffer(first);
149 return buf;
150 }
151 if ((buf = prevBuffer(first, delbuf)) != NULL) {
152 b = buf->nextBuffer;
153 buf->nextBuffer = b->nextBuffer;
154 discardBuffer(b);
155 }
156 return first;
157 }
158
159 /*
160 * replaceBuffer: replace buffer
161 */
162 Buffer *
replaceBuffer(Buffer * first,Buffer * delbuf,Buffer * newbuf)163 replaceBuffer(Buffer *first, Buffer *delbuf, Buffer *newbuf)
164 {
165 Buffer *buf;
166
167 if (delbuf == NULL) {
168 newbuf->nextBuffer = first;
169 return newbuf;
170 }
171 if (first == delbuf) {
172 newbuf->nextBuffer = delbuf->nextBuffer;
173 discardBuffer(delbuf);
174 return newbuf;
175 }
176 if (delbuf && (buf = prevBuffer(first, delbuf))) {
177 buf->nextBuffer = newbuf;
178 newbuf->nextBuffer = delbuf->nextBuffer;
179 discardBuffer(delbuf);
180 return first;
181 }
182 newbuf->nextBuffer = first;
183 return newbuf;
184 }
185
186 Buffer *
nthBuffer(Buffer * firstbuf,int n)187 nthBuffer(Buffer *firstbuf, int n)
188 {
189 int i;
190 Buffer *buf = firstbuf;
191
192 if (n < 0)
193 return firstbuf;
194 for (i = 0; i < n; i++) {
195 if (buf == NULL)
196 return NULL;
197 buf = buf->nextBuffer;
198 }
199 return buf;
200 }
201
202 static void
writeBufferName(Buffer * buf,int n)203 writeBufferName(Buffer *buf, int n)
204 {
205 Str msg;
206 int all;
207
208 all = buf->allLine;
209 if (all == 0 && buf->lastLine != NULL)
210 all = buf->lastLine->linenumber;
211 move(n, 0);
212 /* FIXME: gettextize? */
213 msg = Sprintf("<%s> [%d lines]", buf->buffername, all);
214 if (buf->filename != NULL) {
215 switch (buf->currentURL.scheme) {
216 case SCM_LOCAL:
217 case SCM_LOCAL_CGI:
218 if (strcmp(buf->currentURL.file, "-")) {
219 Strcat_char(msg, ' ');
220 Strcat_charp(msg, conv_from_system(buf->currentURL.real_file));
221 }
222 break;
223 case SCM_UNKNOWN:
224 case SCM_MISSING:
225 break;
226 default:
227 Strcat_char(msg, ' ');
228 Strcat(msg, parsedURL2Str(&buf->currentURL));
229 break;
230 }
231 }
232 addnstr_sup(msg->ptr, COLS - 1);
233 }
234
235
236 /*
237 * gotoLine: go to line number
238 */
239 void
gotoLine(Buffer * buf,int n)240 gotoLine(Buffer *buf, int n)
241 {
242 char msg[32];
243 Line *l = buf->firstLine;
244
245 if (l == NULL)
246 return;
247 if (buf->pagerSource && !(buf->bufferprop & BP_CLOSE)) {
248 if (buf->lastLine->linenumber < n)
249 getNextPage(buf, n - buf->lastLine->linenumber);
250 while ((buf->lastLine->linenumber < n) &&
251 (getNextPage(buf, 1) != NULL)) ;
252 }
253 if (l->linenumber > n) {
254 /* FIXME: gettextize? */
255 sprintf(msg, "First line is #%ld", l->linenumber);
256 set_delayed_message(msg);
257 buf->topLine = buf->currentLine = l;
258 return;
259 }
260 if (buf->lastLine->linenumber < n) {
261 l = buf->lastLine;
262 /* FIXME: gettextize? */
263 sprintf(msg, "Last line is #%ld", buf->lastLine->linenumber);
264 set_delayed_message(msg);
265 buf->currentLine = l;
266 buf->topLine = lineSkip(buf, buf->currentLine, -(buf->LINES - 1),
267 FALSE);
268 return;
269 }
270 for (; l != NULL; l = l->next) {
271 if (l->linenumber >= n) {
272 buf->currentLine = l;
273 if (n < buf->topLine->linenumber ||
274 buf->topLine->linenumber + buf->LINES <= n)
275 buf->topLine = lineSkip(buf, l, -(buf->LINES + 1) / 2, FALSE);
276 break;
277 }
278 }
279 }
280
281 /*
282 * gotoRealLine: go to real line number
283 */
284 void
gotoRealLine(Buffer * buf,int n)285 gotoRealLine(Buffer *buf, int n)
286 {
287 char msg[32];
288 Line *l = buf->firstLine;
289
290 if (l == NULL)
291 return;
292 if (buf->pagerSource && !(buf->bufferprop & BP_CLOSE)) {
293 if (buf->lastLine->real_linenumber < n)
294 getNextPage(buf, n - buf->lastLine->real_linenumber);
295 while ((buf->lastLine->real_linenumber < n) &&
296 (getNextPage(buf, 1) != NULL)) ;
297 }
298 if (l->real_linenumber > n) {
299 /* FIXME: gettextize? */
300 sprintf(msg, "First line is #%ld", l->real_linenumber);
301 set_delayed_message(msg);
302 buf->topLine = buf->currentLine = l;
303 return;
304 }
305 if (buf->lastLine->real_linenumber < n) {
306 l = buf->lastLine;
307 /* FIXME: gettextize? */
308 sprintf(msg, "Last line is #%ld", buf->lastLine->real_linenumber);
309 set_delayed_message(msg);
310 buf->currentLine = l;
311 buf->topLine = lineSkip(buf, buf->currentLine, -(buf->LINES - 1),
312 FALSE);
313 return;
314 }
315 for (; l != NULL; l = l->next) {
316 if (l->real_linenumber >= n) {
317 buf->currentLine = l;
318 if (n < buf->topLine->real_linenumber ||
319 buf->topLine->real_linenumber + buf->LINES <= n)
320 buf->topLine = lineSkip(buf, l, -(buf->LINES + 1) / 2, FALSE);
321 break;
322 }
323 }
324 }
325
326
327 static Buffer *
listBuffer(Buffer * top,Buffer * current)328 listBuffer(Buffer *top, Buffer *current)
329 {
330 int i, c = 0;
331 Buffer *buf = top;
332
333 move(0, 0);
334 #ifdef USE_COLOR
335 if (useColor) {
336 setfcolor(basic_color);
337 #ifdef USE_BG_COLOR
338 setbcolor(bg_color);
339 #endif /* USE_BG_COLOR */
340 }
341 #endif /* USE_COLOR */
342 clrtobotx();
343 for (i = 0; i < LASTLINE; i++) {
344 if (buf == current) {
345 c = i;
346 standout();
347 }
348 writeBufferName(buf, i);
349 if (buf == current) {
350 standend();
351 clrtoeolx();
352 move(i, 0);
353 toggle_stand();
354 }
355 else
356 clrtoeolx();
357 if (buf->nextBuffer == NULL) {
358 move(i + 1, 0);
359 clrtobotx();
360 break;
361 }
362 buf = buf->nextBuffer;
363 }
364 standout();
365 /* FIXME: gettextize? */
366 message("Buffer selection mode: SPC for select / D for delete buffer", 0,
367 0);
368 standend();
369 /*
370 * move(LASTLINE, COLS - 1); */
371 move(c, 0);
372 refresh();
373 return buf->nextBuffer;
374 }
375
376
377 /*
378 * Select buffer visually
379 */
380 Buffer *
selectBuffer(Buffer * firstbuf,Buffer * currentbuf,char * selectchar)381 selectBuffer(Buffer *firstbuf, Buffer *currentbuf, char *selectchar)
382 {
383 int i, cpoint, /* Current Buffer Number */
384 spoint, /* Current Line on Screen */
385 maxbuf, sclimit = LASTLINE; /* Upper limit of line * number in
386 * the * screen */
387 Buffer *buf, *topbuf;
388 char c;
389
390 i = cpoint = 0;
391 for (buf = firstbuf; buf != NULL; buf = buf->nextBuffer) {
392 if (buf == currentbuf)
393 cpoint = i;
394 i++;
395 }
396 maxbuf = i;
397
398 if (cpoint >= sclimit) {
399 spoint = sclimit / 2;
400 topbuf = nthBuffer(firstbuf, cpoint - spoint);
401 }
402 else {
403 topbuf = firstbuf;
404 spoint = cpoint;
405 }
406 listBuffer(topbuf, currentbuf);
407
408 for (;;) {
409 if ((c = getch()) == ESC_CODE) {
410 if ((c = getch()) == '[' || c == 'O') {
411 switch (c = getch()) {
412 case 'A':
413 c = 'k';
414 break;
415 case 'B':
416 c = 'j';
417 break;
418 case 'C':
419 c = ' ';
420 break;
421 case 'D':
422 c = 'B';
423 break;
424 }
425 }
426 }
427 #ifdef __EMX__
428 else if (!c)
429 switch (getch()) {
430 case K_UP:
431 c = 'k';
432 break;
433 case K_DOWN:
434 c = 'j';
435 break;
436 case K_RIGHT:
437 c = ' ';
438 break;
439 case K_LEFT:
440 c = 'B';
441 }
442 #endif
443 switch (c) {
444 case CTRL_N:
445 case 'j':
446 if (spoint < sclimit - 1) {
447 if (currentbuf->nextBuffer == NULL)
448 continue;
449 writeBufferName(currentbuf, spoint);
450 currentbuf = currentbuf->nextBuffer;
451 cpoint++;
452 spoint++;
453 standout();
454 writeBufferName(currentbuf, spoint);
455 standend();
456 move(spoint, 0);
457 toggle_stand();
458 }
459 else if (cpoint < maxbuf - 1) {
460 topbuf = currentbuf;
461 currentbuf = currentbuf->nextBuffer;
462 cpoint++;
463 spoint = 1;
464 listBuffer(topbuf, currentbuf);
465 }
466 break;
467 case CTRL_P:
468 case 'k':
469 if (spoint > 0) {
470 writeBufferName(currentbuf, spoint);
471 currentbuf = nthBuffer(topbuf, --spoint);
472 cpoint--;
473 standout();
474 writeBufferName(currentbuf, spoint);
475 standend();
476 move(spoint, 0);
477 toggle_stand();
478 }
479 else if (cpoint > 0) {
480 i = cpoint - sclimit;
481 if (i < 0)
482 i = 0;
483 cpoint--;
484 spoint = cpoint - i;
485 currentbuf = nthBuffer(firstbuf, cpoint);
486 topbuf = nthBuffer(firstbuf, i);
487 listBuffer(topbuf, currentbuf);
488 }
489 break;
490 default:
491 *selectchar = c;
492 return currentbuf;
493 }
494 /*
495 * move(LASTLINE, COLS - 1);
496 */
497 move(spoint, 0);
498 refresh();
499 }
500 }
501
502 /*
503 * Reshape HTML buffer
504 */
505 void
reshapeBuffer(Buffer * buf)506 reshapeBuffer(Buffer *buf)
507 {
508 URLFile f;
509 Buffer sbuf;
510 #ifdef USE_M17N
511 wc_uint8 old_auto_detect = WcOption.auto_detect;
512 #endif
513
514 if (!buf->need_reshape)
515 return;
516 buf->need_reshape = FALSE;
517 buf->width = INIT_BUFFER_WIDTH;
518 if (buf->sourcefile == NULL)
519 return;
520 init_stream(&f, SCM_LOCAL, NULL);
521 examineFile(buf->mailcap_source ? buf->mailcap_source : buf->sourcefile,
522 &f);
523 if (f.stream == NULL)
524 return;
525 copyBuffer(&sbuf, buf);
526 clearBuffer(buf);
527 while (buf->frameset) {
528 deleteFrameSet(buf->frameset);
529 buf->frameset = popFrameTree(&(buf->frameQ));
530 }
531
532 buf->href = NULL;
533 buf->name = NULL;
534 buf->img = NULL;
535 buf->formitem = NULL;
536 buf->formlist = NULL;
537 buf->linklist = NULL;
538 buf->maplist = NULL;
539 if (buf->hmarklist)
540 buf->hmarklist->nmark = 0;
541 if (buf->imarklist)
542 buf->imarklist->nmark = 0;
543
544 if (buf->header_source) {
545 if (buf->currentURL.scheme != SCM_LOCAL ||
546 buf->mailcap_source || !strcmp(buf->currentURL.file, "-")) {
547 URLFile h;
548 init_stream(&h, SCM_LOCAL, NULL);
549 examineFile(buf->header_source, &h);
550 if (h.stream) {
551 readHeader(&h, buf, TRUE, NULL);
552 UFclose(&h);
553 }
554 }
555 else if (buf->search_header) /* -m option */
556 readHeader(&f, buf, TRUE, NULL);
557 }
558
559 #ifdef USE_M17N
560 WcOption.auto_detect = WC_OPT_DETECT_OFF;
561 UseContentCharset = FALSE;
562 #endif
563 if (is_html_type(buf->type))
564 loadHTMLBuffer(&f, buf);
565 else
566 loadBuffer(&f, buf);
567 UFclose(&f);
568 #ifdef USE_M17N
569 WcOption.auto_detect = old_auto_detect;
570 UseContentCharset = TRUE;
571 #endif
572
573 buf->height = LASTLINE + 1;
574 if (buf->firstLine && sbuf.firstLine) {
575 Line *cur = sbuf.currentLine;
576 int n;
577
578 buf->pos = sbuf.pos + cur->bpos;
579 while (cur->bpos && cur->prev)
580 cur = cur->prev;
581 if (cur->real_linenumber > 0)
582 gotoRealLine(buf, cur->real_linenumber);
583 else
584 gotoLine(buf, cur->linenumber);
585 n = (buf->currentLine->linenumber - buf->topLine->linenumber)
586 - (cur->linenumber - sbuf.topLine->linenumber);
587 if (n) {
588 buf->topLine = lineSkip(buf, buf->topLine, n, FALSE);
589 if (cur->real_linenumber > 0)
590 gotoRealLine(buf, cur->real_linenumber);
591 else
592 gotoLine(buf, cur->linenumber);
593 }
594 buf->pos -= buf->currentLine->bpos;
595 if (FoldLine && !is_html_type(buf->type))
596 buf->currentColumn = 0;
597 else
598 buf->currentColumn = sbuf.currentColumn;
599 arrangeCursor(buf);
600 }
601 if (buf->check_url & CHK_URL)
602 chkURLBuffer(buf);
603 #ifdef USE_NNTP
604 if (buf->check_url & CHK_NMID)
605 chkNMIDBuffer(buf);
606 if (buf->real_scheme == SCM_NNTP || buf->real_scheme == SCM_NEWS)
607 reAnchorNewsheader(buf);
608 #endif
609 formResetBuffer(buf, sbuf.formitem);
610 }
611
612 /* shallow copy */
613 void
copyBuffer(Buffer * a,Buffer * b)614 copyBuffer(Buffer *a, Buffer *b)
615 {
616 readBufferCache(b);
617 bcopy((void *)b, (void *)a, sizeof(Buffer));
618 }
619
620 Buffer *
prevBuffer(Buffer * first,Buffer * buf)621 prevBuffer(Buffer *first, Buffer *buf)
622 {
623 Buffer *b;
624
625 for (b = first; b != NULL && b->nextBuffer != buf; b = b->nextBuffer) ;
626 return b;
627 }
628
629 #define fwrite1(d, f) (fwrite(&d, sizeof(d), 1, f)==0)
630 #define fread1(d, f) (fread(&d, sizeof(d), 1, f)==0)
631
632 int
writeBufferCache(Buffer * buf)633 writeBufferCache(Buffer *buf)
634 {
635 Str tmp;
636 FILE *cache = NULL;
637 Line *l;
638 #ifdef USE_ANSI_COLOR
639 int colorflag;
640 #endif
641
642 if (buf->savecache)
643 return -1;
644
645 if (buf->firstLine == NULL)
646 goto _error1;
647
648 tmp = tmpfname(TMPF_CACHE, NULL);
649 buf->savecache = tmp->ptr;
650 cache = fopen(buf->savecache, "w");
651 if (!cache)
652 goto _error1;
653
654 if (fwrite1(buf->currentLine->linenumber, cache) ||
655 fwrite1(buf->topLine->linenumber, cache))
656 goto _error;
657
658 for (l = buf->firstLine; l; l = l->next) {
659 if (fwrite1(l->real_linenumber, cache) ||
660 fwrite1(l->usrflags, cache) ||
661 fwrite1(l->width, cache) ||
662 fwrite1(l->len, cache) ||
663 fwrite1(l->size, cache) ||
664 fwrite1(l->bpos, cache) || fwrite1(l->bwidth, cache))
665 goto _error;
666 if (l->bpos == 0) {
667 if (fwrite(l->lineBuf, 1, l->size, cache) < l->size ||
668 fwrite(l->propBuf, sizeof(Lineprop), l->size, cache) < l->size)
669 goto _error;
670 }
671 #ifdef USE_ANSI_COLOR
672 colorflag = l->colorBuf ? 1 : 0;
673 if (fwrite1(colorflag, cache))
674 goto _error;
675 if (colorflag) {
676 if (l->bpos == 0) {
677 if (fwrite(l->colorBuf, sizeof(Linecolor), l->size, cache) <
678 l->size)
679 goto _error;
680 }
681 }
682 #endif
683 }
684
685 fclose(cache);
686 return 0;
687 _error:
688 fclose(cache);
689 unlink(buf->savecache);
690 _error1:
691 buf->savecache = NULL;
692 return -1;
693 }
694
695 int
readBufferCache(Buffer * buf)696 readBufferCache(Buffer *buf)
697 {
698 FILE *cache;
699 Line *l = NULL, *prevl = NULL, *basel = NULL;
700 long lnum = 0, clnum, tlnum;
701 #ifdef USE_ANSI_COLOR
702 int colorflag;
703 #endif
704
705 if (buf->savecache == NULL)
706 return -1;
707
708 cache = fopen(buf->savecache, "r");
709 if (cache == NULL || fread1(clnum, cache) || fread1(tlnum, cache)) {
710 fclose(cache);
711 buf->savecache = NULL;
712 return -1;
713 }
714
715 while (!feof(cache)) {
716 lnum++;
717 prevl = l;
718 l = New(Line);
719 l->prev = prevl;
720 if (prevl)
721 prevl->next = l;
722 else
723 buf->firstLine = l;
724 l->linenumber = lnum;
725 if (lnum == clnum)
726 buf->currentLine = l;
727 if (lnum == tlnum)
728 buf->topLine = l;
729 if (fread1(l->real_linenumber, cache) ||
730 fread1(l->usrflags, cache) ||
731 fread1(l->width, cache) ||
732 fread1(l->len, cache) ||
733 fread1(l->size, cache) ||
734 fread1(l->bpos, cache) || fread1(l->bwidth, cache))
735 break;
736 if (l->bpos == 0) {
737 basel = l;
738 l->lineBuf = NewAtom_N(char, l->size + 1);
739 fread(l->lineBuf, 1, l->size, cache);
740 l->lineBuf[l->size] = '\0';
741 l->propBuf = NewAtom_N(Lineprop, l->size);
742 fread(l->propBuf, sizeof(Lineprop), l->size, cache);
743 }
744 else if (basel) {
745 l->lineBuf = basel->lineBuf + l->bpos;
746 l->propBuf = basel->propBuf + l->bpos;
747 }
748 else
749 break;
750 #ifdef USE_ANSI_COLOR
751 if (fread1(colorflag, cache))
752 break;
753 if (colorflag) {
754 if (l->bpos == 0) {
755 l->colorBuf = NewAtom_N(Linecolor, l->size);
756 fread(l->colorBuf, sizeof(Linecolor), l->size, cache);
757 }
758 else
759 l->colorBuf = basel->colorBuf + l->bpos;
760 }
761 else {
762 l->colorBuf = NULL;
763 }
764 #endif
765 }
766 if (prevl) {
767 buf->lastLine = prevl;
768 buf->lastLine->next = NULL;
769 }
770 fclose(cache);
771 unlink(buf->savecache);
772 buf->savecache = NULL;
773 return 0;
774 }
775