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