* Experimental: Alpine can pass an HTML message to an external web browser, by...
[alpine.git] / pith / filter.c
blob1e2db4d1f7405cf7d49df5419a44122116095d75
1 #if !defined(lint) && !defined(DOS)
2 static char rcsid[] = "$Id: filter.c 1266 2009-07-14 18:39:12Z hubert@u.washington.edu $";
3 #endif
5 /*
6 * ========================================================================
7 * Copyright 2013-2020 Eduardo Chappa
8 * Copyright 2006-2008 University of Washington
10 * Licensed under the Apache License, Version 2.0 (the "License");
11 * you may not use this file except in compliance with the License.
12 * You may obtain a copy of the License at
14 * http://www.apache.org/licenses/LICENSE-2.0
16 * ========================================================================
19 /*======================================================================
20 filter.c
22 This code provides a generalized, flexible way to allow
23 piping of data thru filters. Each filter is passed a structure
24 that it will use to hold its static data while it operates on
25 the stream of characters that are passed to it. After processing
26 it will either return or call the next filter in
27 the pipe with any character (or characters) it has ready to go. This
28 means some terminal type of filter has to be the last in the
29 chain (i.e., one that writes the passed char someplace, but doesn't
30 call another filter).
32 See below for more details.
34 The motivation is to handle MIME decoding, richtext conversion,
35 iso_code stripping and anything else that may come down the
36 pike (e.g., PEM) in an elegant fashion. mikes (920811)
38 TODO:
39 reasonable error handling
41 ====*/
44 #include "../pith/headers.h"
45 #include "../pith/filter.h"
46 #include "../pith/conf.h"
47 #include "../pith/store.h"
48 #include "../pith/color.h"
49 #include "../pith/escapes.h"
50 #include "../pith/pipe.h"
51 #include "../pith/status.h"
52 #include "../pith/string.h"
53 #include "../pith/util.h"
54 #include "../pith/url.h"
55 #include "../pith/init.h"
56 #include "../pith/help.h"
57 #include "../pico/keydefs.h"
59 #ifdef _WINDOWS
60 #include "../pico/osdep/mswin.h"
61 #endif
65 * Internal prototypes
67 int gf_so_writec(int);
68 int gf_so_readc(unsigned char *);
69 int gf_freadc(unsigned char *);
70 int gf_freadc_locale(unsigned char *);
71 int gf_freadc_getchar(unsigned char *, void *);
72 int gf_fwritec(int);
73 int gf_fwritec_locale(int);
74 #ifdef _WINDOWS
75 int gf_freadc_windows(unsigned char *);
76 #endif /* _WINDOWS */
77 int gf_preadc(unsigned char *);
78 int gf_preadc_locale(unsigned char *);
79 int gf_preadc_getchar(unsigned char *, void *);
80 int gf_pwritec(int);
81 int gf_pwritec_locale(int);
82 int gf_sreadc(unsigned char *);
83 int gf_sreadc_locale(unsigned char *);
84 int gf_sreadc_getchar(unsigned char *, void *);
85 int gf_swritec(int);
86 int gf_swritec_locale(int);
87 void gf_terminal(FILTER_S *, int);
88 void gf_error(char *);
89 char *gf_filter_puts(char *);
90 void gf_filter_eod(void);
92 void gf_8bit_put(FILTER_S *, int);
97 * System specific options
99 #ifdef _WINDOWS
100 #define CRLF_NEWLINES
101 #endif
105 * Hooks for callers to adjust behavior
107 char *(*pith_opt_pretty_var_name)(char *);
108 char *(*pith_opt_pretty_feature_name)(char *, int);
112 * pointer to first function in a pipe, and pointer to last filter
114 FILTER_S *gf_master = NULL;
115 static gf_io_t last_filter;
116 static char *gf_error_string;
117 static long gf_byte_count;
118 static jmp_buf gf_error_state;
121 #define GF_NOOP 0x01 /* flags used by generalized */
122 #define GF_EOD 0x02 /* filters */
123 #define GF_DATA 0x04 /* See filter.c for more */
124 #define GF_ERROR 0x08 /* details */
125 #define GF_RESET 0x10
129 * A list of states used by the various filters. Reused in many filters.
131 #define DFL 0
132 #define EQUAL 1
133 #define HEX 2
134 #define WSPACE 3
135 #define CCR 4
136 #define CLF 5
137 #define TOKEN 6
138 #define TAG 7
139 #define HANDLE 8
140 #define HDATA 9
141 #define ESC 10
142 #define ESCDOL 11
143 #define ESCPAR 12
144 #define EUC 13
145 #define BOL 14
146 #define FL_QLEV 15
147 #define FL_STF 16
148 #define FL_SIG 17
149 #define STOP_DECODING 18
150 #define SPACECR 19
155 * Macros to reduce function call overhead associated with calling
156 * each filter for each byte filtered, and to minimize filter structure
157 * dereferences. NOTE: "queuein" has to do with putting chars into the
158 * filter structs data queue. So, writing at the queuein offset is
159 * what a filter does to pass processed data out of itself. Ditto for
160 * queueout. This explains the FI --> queueout init stuff below.
162 #define GF_QUE_START(F) (&(F)->queue[0])
163 #define GF_QUE_END(F) (&(F)->queue[GF_MAXBUF - 1])
165 #define GF_IP_INIT(F) ip = (F) ? &(F)->queue[(F)->queuein] : NULL
166 #define GF_IP_INIT_GLO(F) (*ipp) = (F) ? &(F)->queue[(F)->queuein] : NULL
167 #define GF_EIB_INIT(F) eib = (F) ? GF_QUE_END(F) : NULL
168 #define GF_EIB_INIT_GLO(F) (*eibp) = (F) ? GF_QUE_END(F) : NULL
169 #define GF_OP_INIT(F) op = (F) ? &(F)->queue[(F)->queueout] : NULL
170 #define GF_EOB_INIT(F) eob = (F) ? &(F)->queue[(F)->queuein] : NULL
172 #define GF_IP_END(F) (F)->queuein = ip - GF_QUE_START(F)
173 #define GF_IP_END_GLO(F) (F)->queuein = (unsigned char *)(*ipp) - (unsigned char *)GF_QUE_START(F)
174 #define GF_OP_END(F) (F)->queueout = op - GF_QUE_START(F)
176 #define GF_INIT(FI, FO) unsigned char *GF_OP_INIT(FI); \
177 unsigned char *GF_EOB_INIT(FI); \
178 unsigned char *GF_IP_INIT(FO); \
179 unsigned char *GF_EIB_INIT(FO);
181 #define GF_CH_RESET(F) (op = eob = GF_QUE_START(F), \
182 (F)->queueout = (F)->queuein = 0)
184 #define GF_END(FI, FO) (GF_OP_END(FI), GF_IP_END(FO))
186 #define GF_FLUSH(F) ((GF_IP_END(F), (*(F)->f)((F), GF_DATA), \
187 GF_IP_INIT(F), GF_EIB_INIT(F)) ? 1 : 0)
188 #define GF_FLUSH_GLO(F) ((GF_IP_END_GLO(F), (*(F)->f)((F), GF_DATA), \
189 GF_IP_INIT_GLO(F), GF_EIB_INIT_GLO(F)) ? 1 : 0)
191 #define GF_PUTC(F, C) ((int)(*ip++ = (C), (ip >= eib) ? GF_FLUSH(F) : 1))
192 #define GF_PUTC_GLO(F, C) ((int)(*(*ipp)++ = (C), ((*ipp) >= (*eibp)) ? GF_FLUSH_GLO(F) : 1))
195 * Introducing the *_GLO macros for use in splitting the big macros out
196 * into functions (wrap_flush, wrap_eol). The reason we need a
197 * separate macro is because of the vars ip, eib, op, and eob, which are
198 * set up locally in a call to GF_INIT. To preserve these variables
199 * in the new functions, we now pass pointers to these four vars. Each
200 * of these new functions expects the presence of pointer vars
201 * ipp, eibp, opp, and eobp.
204 #define GF_GETC(F, C) ((op < eob) ? (((C) = *op++), 1) : GF_CH_RESET(F))
206 #define GF_COLOR_PUTC(F, C) { \
207 char *p; \
208 char cb[RGBLEN+1]; \
209 GF_PUTC_GLO((F)->next, TAG_EMBED); \
210 GF_PUTC_GLO((F)->next, TAG_FGCOLOR); \
211 strncpy(cb, color_to_asciirgb((C)->fg), sizeof(cb)); \
212 cb[sizeof(cb)-1] = '\0'; \
213 p = cb; \
214 for(; *p; p++) \
215 GF_PUTC_GLO((F)->next, *p); \
216 GF_PUTC_GLO((F)->next, TAG_EMBED); \
217 GF_PUTC_GLO((F)->next, TAG_BGCOLOR); \
218 strncpy(cb, color_to_asciirgb((C)->bg), sizeof(cb)); \
219 cb[sizeof(cb)-1] = '\0'; \
220 p = cb; \
221 for(; *p; p++) \
222 GF_PUTC_GLO((F)->next, *p); \
226 * Generalized getc and putc routines. provided here so they don't
227 * need to be re-done elsewhere to
231 * pointers to objects to be used by the generic getc and putc
232 * functions
234 static struct gf_io_struct {
235 FILE *file;
236 PIPE_S *pipe;
237 char *txtp;
238 unsigned long n;
239 int flags;
240 CBUF_S cb;
241 } gf_in, gf_out;
243 #define GF_SO_STACK struct gf_so_stack
244 static GF_SO_STACK {
245 STORE_S *so;
246 GF_SO_STACK *next;
247 } *gf_so_in, *gf_so_out;
252 * Returns 1 if pc will write into a PicoText object, 0 otherwise.
254 * The purpose of this routine is so that we can avoid setting SIGALARM
255 * when writing into a PicoText object, because that type of object uses
256 * unprotected malloc/free/realloc, which can't be interrupted.
259 pc_is_picotext(gf_io_t pc)
261 return(pc == gf_so_writec && gf_so_out && gf_so_out->so &&
262 gf_so_out->so->src == ExternalText);
268 * setup to use and return a pointer to the generic
269 * getc function
271 void
272 gf_set_readc(gf_io_t *gc, void *txt, long unsigned int len, SourceType src, int flags)
274 gf_in.n = len;
275 gf_in.flags = flags;
276 gf_in.cb.cbuf[0] = '\0';
277 gf_in.cb.cbufp = gf_in.cb.cbuf;
278 gf_in.cb.cbufend = gf_in.cb.cbuf;
280 if(src == FileStar){
281 gf_in.file = (FILE *)txt;
282 fseek(gf_in.file, 0L, 0);
283 #ifdef _WINDOWS
284 *gc = (flags & READ_FROM_LOCALE) ? gf_freadc_windows
285 : gf_freadc;
286 #else /* UNIX */
287 *gc = (flags & READ_FROM_LOCALE) ? gf_freadc_locale
288 : gf_freadc;
289 #endif /* UNIX */
291 else if(src == PipeStar){
292 gf_in.pipe = (PIPE_S *)txt;
293 *gc = gf_preadc;
294 *gc = (flags & READ_FROM_LOCALE) ? gf_preadc_locale
295 : gf_preadc;
297 else{
298 gf_in.txtp = (char *)txt;
299 *gc = (flags & READ_FROM_LOCALE) ? gf_sreadc_locale
300 : gf_sreadc;
306 * setup to use and return a pointer to the generic
307 * putc function
309 void
310 gf_set_writec(gf_io_t *pc, void *txt, long unsigned int len, SourceType src, int flags)
312 gf_out.n = len;
313 gf_out.flags = flags;
314 gf_out.cb.cbuf[0] = '\0';
315 gf_out.cb.cbufp = gf_out.cb.cbuf;
316 gf_out.cb.cbufend = gf_out.cb.cbuf;
318 if(src == FileStar){
319 gf_out.file = (FILE *)txt;
320 #ifdef _WINDOWS
321 *pc = gf_fwritec;
322 #else /* UNIX */
323 *pc = (flags & WRITE_TO_LOCALE) ? gf_fwritec_locale
324 : gf_fwritec;
325 #endif /* UNIX */
327 else if(src == PipeStar){
328 gf_out.pipe = (PIPE_S *)txt;
329 *pc = (flags & WRITE_TO_LOCALE) ? gf_pwritec_locale
330 : gf_pwritec;
332 else{
333 gf_out.txtp = (char *)txt;
334 *pc = (flags & WRITE_TO_LOCALE) ? gf_swritec_locale
335 : gf_swritec;
341 * setup to use and return a pointer to the generic
342 * getc function
344 void
345 gf_set_so_readc(gf_io_t *gc, STORE_S *so)
347 GF_SO_STACK *sp = (GF_SO_STACK *) fs_get(sizeof(GF_SO_STACK));
349 sp->so = so;
350 sp->next = gf_so_in;
351 gf_so_in = sp;
352 *gc = gf_so_readc;
356 void
357 gf_clear_so_readc(STORE_S *so)
359 GF_SO_STACK *sp;
361 if((sp = gf_so_in) != NULL){
362 if(so == sp->so){
363 gf_so_in = gf_so_in->next;
364 fs_give((void **) &sp);
366 else
367 alpine_panic("Programmer botch: Can't unstack store readc");
369 else
370 alpine_panic("Programmer botch: NULL store clearing store readc");
375 * setup to use and return a pointer to the generic
376 * putc function
378 void
379 gf_set_so_writec(gf_io_t *pc, STORE_S *so)
381 GF_SO_STACK *sp = (GF_SO_STACK *) fs_get(sizeof(GF_SO_STACK));
383 sp->so = so;
384 sp->next = gf_so_out;
385 gf_so_out = sp;
386 *pc = gf_so_writec;
390 void
391 gf_clear_so_writec(STORE_S *so)
393 GF_SO_STACK *sp;
395 if((sp = gf_so_out) != NULL){
396 if(so == sp->so){
397 gf_so_out = gf_so_out->next;
398 fs_give((void **) &sp);
400 else
401 alpine_panic("Programmer botch: Can't unstack store writec");
403 else
404 alpine_panic("Programmer botch: NULL store clearing store writec");
409 * put the character to the object previously defined
412 gf_so_writec(int c)
414 return(so_writec(c, gf_so_out->so));
419 * get a character from an object previously defined
422 gf_so_readc(unsigned char *c)
424 return(so_readc(c, gf_so_in->so));
428 /* get a character from a file */
429 /* assumes gf_out struct is filled in */
431 gf_freadc(unsigned char *c)
433 int rv = 0;
435 do {
436 errno = 0;
437 clearerr(gf_in.file);
438 rv = fread(c, sizeof(unsigned char), (size_t)1, gf_in.file);
439 } while(!rv && ferror(gf_in.file) && errno == EINTR);
441 return(rv);
446 gf_freadc_locale(unsigned char *c)
448 return(generic_readc_locale(c, gf_freadc_getchar, (void *) gf_in.file, &gf_in.cb));
453 * This is just to make it work with generic_readc_locale.
456 gf_freadc_getchar(unsigned char *c, void *extraarg)
458 FILE *file;
459 int rv = 0;
461 file = (FILE *) extraarg;
463 do {
464 errno = 0;
465 clearerr(file);
466 rv = fread(c, sizeof(unsigned char), (size_t)1, file);
467 } while(!rv && ferror(file) && errno == EINTR);
469 return(rv);
474 * Put a character to a file.
475 * Assumes gf_out struct is filled in.
476 * Returns 1 on success, <= 0 on failure.
479 gf_fwritec(int c)
481 unsigned char ch = (unsigned char)c;
482 int rv = 0;
485 rv = fwrite(&ch, sizeof(unsigned char), (size_t)1, gf_out.file);
486 while(!rv && ferror(gf_out.file) && errno == EINTR);
488 return(rv);
493 * The locale version converts from UTF-8 to user's locale charset
494 * before writing the characters.
497 gf_fwritec_locale(int c)
499 int rv = 1;
500 int i, outchars;
501 unsigned char obuf[MAX(MB_LEN_MAX,32)];
503 if((outchars = utf8_to_locale(c, &gf_out.cb, obuf, sizeof(obuf))) != 0){
504 for(i = 0; i < outchars; i++)
505 if(gf_fwritec(obuf[i]) != 1){
506 rv = 0;
507 break;
511 return(rv);
515 #ifdef _WINDOWS
517 * Read unicode characters from windows filesystem and return
518 * them as a stream of UTF-8 characters. The stream is assumed
519 * opened so that it will know how to put together the unicode.
521 * (This is totally untested, copied loosely from so_file_readc_windows
522 * which may or may not be appropriate.)
525 gf_freadc_windows(unsigned char *c)
527 int rv = 0;
528 UCS ucs;
530 /* already got some from previous call? */
531 if(gf_in.cb.cbufend > gf_in.cb.cbuf){
532 *c = *gf_in.cb.cbufp;
533 gf_in.cb.cbufp++;
534 rv++;
535 if(gf_in.cb.cbufp >= gf_in.cb.cbufend){
536 gf_in.cb.cbufend = gf_in.cb.cbuf;
537 gf_in.cb.cbufp = gf_in.cb.cbuf;
540 return(rv);
543 if(gf_in.file){
544 /* windows only so second arg is ignored */
545 ucs = read_a_wide_char(gf_in.file, NULL);
546 rv = (ucs == CCONV_EOF) ? 0 : 1;
549 if(rv){
551 * Now we need to convert the UCS character to UTF-8
552 * and dole out the UTF-8 one char at a time.
554 gf_in.cb.cbufend = utf8_put(gf_in.cb.cbuf, (unsigned long) ucs);
555 gf_in.cb.cbufp = gf_in.cb.cbuf;
556 if(gf_in.cb.cbufend > gf_in.cb.cbuf){
557 *c = *gf_in.cb.cbufp;
558 gf_in.cb.cbufp++;
559 if(gf_in.cb.cbufp >= gf_in.cb.cbufend){
560 gf_in.cb.cbufend = gf_in.cb.cbuf;
561 gf_in.cb.cbufp = gf_in.cb.cbuf;
564 else
565 *c = '?';
568 return(rv);
570 #endif /* _WINDOWS */
574 gf_preadc(unsigned char *c)
576 return(pipe_readc(c, gf_in.pipe));
581 gf_preadc_locale(unsigned char *c)
583 return(generic_readc_locale(c, gf_preadc_getchar, (void *) gf_in.pipe, &gf_in.cb));
588 * This is just to make it work with generic_readc_locale.
591 gf_preadc_getchar(unsigned char *c, void *extraarg)
593 PIPE_S *pipe;
595 pipe = (PIPE_S *) extraarg;
597 return(pipe_readc(c, pipe));
602 * Put a character to a pipe.
603 * Assumes gf_out struct is filled in.
604 * Returns 1 on success, <= 0 on failure.
607 gf_pwritec(int c)
609 return(pipe_writec(c, gf_out.pipe));
614 * The locale version converts from UTF-8 to user's locale charset
615 * before writing the characters.
618 gf_pwritec_locale(int c)
620 int rv = 1;
621 int i, outchars;
622 unsigned char obuf[MAX(MB_LEN_MAX,32)];
624 if((outchars = utf8_to_locale(c, &gf_out.cb, obuf, sizeof(obuf))) != 0){
625 for(i = 0; i < outchars; i++)
626 if(gf_pwritec(obuf[i]) != 1){
627 rv = 0;
628 break;
632 return(rv);
636 /* get a character from a string, return nonzero if things OK */
637 /* assumes gf_out struct is filled in */
639 gf_sreadc(unsigned char *c)
641 return((gf_in.n) ? *c = *(gf_in.txtp)++, gf_in.n-- : 0);
646 gf_sreadc_locale(unsigned char *c)
648 return(generic_readc_locale(c, gf_sreadc_getchar, NULL, &gf_in.cb));
653 gf_sreadc_getchar(unsigned char *c, void *extraarg)
656 * extraarg is ignored and gf_sreadc just uses globals instead.
657 * That's ok as long as we don't call it more than once at a time.
659 return(gf_sreadc(c));
664 * Put a character to a string.
665 * Assumes gf_out struct is filled in.
666 * Returns 1 on success, <= 0 on failure.
669 gf_swritec(int c)
671 return((gf_out.n) ? *(gf_out.txtp)++ = c, gf_out.n-- : 0);
676 * The locale version converts from UTF-8 to user's locale charset
677 * before writing the characters.
680 gf_swritec_locale(int c)
682 int rv = 1;
683 int i, outchars;
684 unsigned char obuf[MAX(MB_LEN_MAX,32)];
686 if((outchars = utf8_to_locale(c, &gf_out.cb, obuf, sizeof(obuf))) != 0){
687 for(i = 0; i < outchars; i++)
688 if(gf_swritec(obuf[i]) != 1){
689 rv = 0;
690 break;
694 return(rv);
699 * output the given string with the given function
702 gf_puts(register char *s, gf_io_t pc)
704 while(*s != '\0')
705 if(!(*pc)((unsigned char)*s++))
706 return(0); /* ERROR putting char ! */
708 return(1);
713 * output the given string with the given function
716 gf_nputs(register char *s, long int n, gf_io_t pc)
718 while(n--)
719 if(!(*pc)((unsigned char)*s++))
720 return(0); /* ERROR putting char ! */
722 return(1);
727 * Read a stream of multi-byte characters from the
728 * user's locale charset and return a stream of
729 * UTF-8 characters, one at a time. The input characters
730 * are obtained by using the get_a_char function.
732 * Args c -- the returned octet
733 * get_a_char -- function to get a single octet of the multibyte
734 * character. The first arg of that function is the
735 * returned value and the second arg is for the
736 * functions use. The second arg is replaced with
737 * extraarg when it is called.
738 * extraarg -- The second arg to get_a_char.
739 * cb -- Storage area for state between calls to this func.
742 generic_readc_locale(unsigned char *c,
743 int (*get_a_char)(unsigned char *, void *),
744 void *extraarg,
745 CBUF_S *cb)
747 unsigned long octets_so_far = 0, remaining_octets;
748 unsigned char *inputp;
749 unsigned char ch;
750 UCS ucs;
751 unsigned char inputbuf[20];
752 int rv = 0;
753 int got_one = 0;
755 /* already got some from previous call? */
756 if(cb->cbufend > cb->cbuf){
757 *c = *cb->cbufp;
758 cb->cbufp++;
759 rv++;
760 if(cb->cbufp >= cb->cbufend){
761 cb->cbufend = cb->cbuf;
762 cb->cbufp = cb->cbuf;
765 return(rv);
768 memset(inputbuf, 0, sizeof(inputbuf));
769 if((*get_a_char)(&ch, extraarg) == 0)
770 return(0);
772 inputbuf[octets_so_far++] = ch;
774 while(!got_one){
775 remaining_octets = octets_so_far;
776 inputp = inputbuf;
777 ucs = mbtow(ps_global->input_cs, &inputp, &remaining_octets);
778 switch(ucs){
779 case CCONV_BADCHAR:
780 return(rv);
782 case CCONV_NEEDMORE:
784 * Do we need to do something with the characters we've
785 * collected that don't form a valid UCS character?
786 * Probably need to try discarding them one at a time
787 * from the front instead of just throwing them all out.
789 if(octets_so_far >= sizeof(inputbuf))
790 return(rv);
792 if((*get_a_char)(&ch, extraarg) == 0)
793 return(rv);
795 inputbuf[octets_so_far++] = ch;
796 break;
798 default:
799 /* got a good UCS-4 character */
800 got_one++;
801 break;
806 * Now we need to convert the UCS character to UTF-8
807 * and dole out the UTF-8 one char at a time.
809 rv++;
810 cb->cbufend = utf8_put(cb->cbuf, (unsigned long) ucs);
811 cb->cbufp = cb->cbuf;
812 if(cb->cbufend > cb->cbuf){
813 *c = *cb->cbufp;
814 cb->cbufp++;
815 if(cb->cbufp >= cb->cbufend){
816 cb->cbufend = cb->cbuf;
817 cb->cbufp = cb->cbuf;
820 else
821 *c = '?';
823 return(rv);
828 * Start of generalized filter routines
832 * initializing function to make sure list of filters is empty.
834 void
835 gf_filter_init(void)
837 FILTER_S *flt, *fltn = gf_master;
839 while((flt = fltn) != NULL){ /* free list of old filters */
840 fltn = flt->next;
841 fs_give((void **)&flt);
844 gf_master = NULL;
845 gf_error_string = NULL; /* clear previous errors */
846 gf_byte_count = 0L; /* reset counter */
852 * link the given filter into the filter chain
854 void
855 gf_link_filter(filter_t f, void *data)
857 FILTER_S *new, *tail;
859 #ifdef CRLF_NEWLINES
861 * If the system's native EOL convention is CRLF, then there's no
862 * point in passing data thru a filter that's not doing anything
864 if(f == gf_nvtnl_local || f == gf_local_nvtnl)
865 return;
866 #endif
868 new = (FILTER_S *)fs_get(sizeof(FILTER_S));
869 memset(new, 0, sizeof(FILTER_S));
871 new->f = f; /* set the function pointer */
872 new->opt = data; /* set any optional parameter data */
873 (*f)(new, GF_RESET); /* have it setup initial state */
875 if((tail = gf_master) != NULL){ /* or add it to end of existing */
876 while(tail->next) /* list */
877 tail = tail->next;
879 tail->next = new;
881 else /* attach new struct to list */
882 gf_master = new; /* start a new list */
887 * terminal filter, doesn't call any other filters, typically just does
888 * something with the output
890 void
891 gf_terminal(FILTER_S *f, int flg)
893 if(flg == GF_DATA){
894 GF_INIT(f, f);
896 while(op < eob)
897 if((*last_filter)(*op++) <= 0) /* generic terminal filter */
898 gf_error(errno ? error_description(errno) : "Error writing pipe");
900 GF_CH_RESET(f);
902 else if(flg == GF_RESET)
903 errno = 0; /* prepare for problems */
908 * set some outside gf_io_t function to the terminal function
909 * for example: a function to write a char to a file or into a buffer
911 void
912 gf_set_terminal(gf_io_t f) /* function to set generic filter */
915 last_filter = f;
920 * common function for filter's to make it known that an error
921 * has occurred. Jumps back to gf_pipe with error message.
923 void
924 gf_error(char *s)
926 /* let the user know the error passed in s */
927 gf_error_string = s;
928 longjmp(gf_error_state, 1);
933 * The routine that shoves each byte through the chain of
934 * filters. It sets up error handling, and the terminal function.
935 * Then loops getting bytes with the given function, and passing
936 * it on to the first filter in the chain.
938 char *
939 gf_pipe(gf_io_t gc, gf_io_t pc)
940 /* how to get a character */
942 unsigned char c;
944 dprint((4, "-- gf_pipe: "));
947 * set up for any errors a filter may encounter
949 if(setjmp(gf_error_state)){
950 dprint((4, "ERROR: %s\n",
951 gf_error_string ? gf_error_string : "NULL"));
952 return(gf_error_string); /* */
956 * set and link in the terminal filter
958 gf_set_terminal(pc);
959 gf_link_filter(gf_terminal, NULL);
962 * while there are chars to process, send them thru the pipe.
963 * NOTE: it's necessary to enclose the loop below in a block
964 * as the GF_INIT macro calls some automatic var's into
965 * existence. It can't be placed at the start of gf_pipe
966 * because its useful for us to be called without filters loaded
967 * when we're just being used to copy bytes between storage
968 * objects.
971 GF_INIT(gf_master, gf_master);
973 while((*gc)(&c)){
974 gf_byte_count++;
976 #ifdef _WINDOWS
977 if(!(gf_byte_count & 0x3ff))
978 /* Under windows we yield to allow event processing.
979 * Progress display is handled through the alarm()
980 * mechanism.
982 mswin_yield ();
983 #endif
985 GF_PUTC(gf_master, c & 0xff);
989 * toss an end-of-data marker down the pipe to give filters
990 * that have any buffered data the opportunity to dump it
992 (void) GF_FLUSH(gf_master);
993 (*gf_master->f)(gf_master, GF_EOD);
996 dprint((4, "done.\n"));
997 return(NULL); /* everything went OK */
1002 * return the number of bytes piped so far
1004 long
1005 gf_bytes_piped(void)
1007 return(gf_byte_count);
1012 * filter the given input with the given command
1014 * Args: cmd -- command string to execute
1015 * prepend -- string to prepend to filtered input
1016 * source_so -- storage object containing data to be filtered
1017 * pc -- function to write filtered output with
1018 * aux_filters -- additional filters to pass data thru after "cmd"
1020 * Returns: NULL on success, reason for failure (not alloc'd!) on error
1022 char *
1023 gf_filter(char *cmd, char *prepend, STORE_S *source_so, gf_io_t pc,
1024 FILTLIST_S *aux_filters, int silent, int disable_reset,
1025 void (*pipecb_f)(PIPE_S *, int, void *))
1027 unsigned char c, obuf[MAX(MB_LEN_MAX,32)];
1028 int flags, outchars, i;
1029 char *errstr = NULL, buf[MAILTMPLEN];
1030 PIPE_S *fpipe;
1031 CBUF_S cb;
1032 #ifdef NON_BLOCKING_IO
1033 int n;
1034 #endif
1036 dprint((4, "so_filter: \"%s\"\n", cmd ? cmd : "?"));
1038 gf_filter_init();
1041 * After coming back from user's pipe command we need to convert
1042 * the output from the pipe back to UTF-8.
1044 if(ps_global->keyboard_charmap && strucmp("UTF-8", ps_global->keyboard_charmap))
1045 gf_link_filter(gf_utf8, gf_utf8_opt(ps_global->keyboard_charmap));
1047 for( ; aux_filters && aux_filters->filter; aux_filters++)
1048 gf_link_filter(aux_filters->filter, aux_filters->data);
1050 gf_set_terminal(pc);
1051 gf_link_filter(gf_terminal, NULL);
1053 cb.cbuf[0] = '\0';
1054 cb.cbufp = cb.cbuf;
1055 cb.cbufend = cb.cbuf;
1058 * Spawn filter feeding it data, and reading what it writes.
1060 so_seek(source_so, 0L, 0);
1061 flags = PIPE_WRITE | PIPE_READ | PIPE_NOSHELL
1062 | (silent ? PIPE_SILENT : 0)
1063 | (!disable_reset ? PIPE_RESET : 0);
1065 if((fpipe = open_system_pipe(cmd, NULL, NULL, flags, 0, pipecb_f, pipe_report_error)) != NULL){
1067 #ifdef NON_BLOCKING_IO
1069 if(fcntl(fileno(fpipe->in.f), F_SETFL, NON_BLOCKING_IO) == -1)
1070 errstr = "Can't set up non-blocking IO";
1072 if(prepend && (fputs(prepend, fpipe->out.f) == EOF
1073 || fputc('\n', fpipe->out.f) == EOF))
1074 errstr = error_description(errno);
1076 while(!errstr){
1077 /* if the pipe can't hold a K we're sunk (too bad PIPE_MAX
1078 * isn't ubiquitous ;).
1080 for(n = 0; !errstr && fpipe->out.f && n < 1024; n++)
1081 if(!so_readc(&c, source_so)){
1082 fclose(fpipe->out.f);
1083 fpipe->out.f = NULL;
1085 else{
1087 * Got a UTF-8 character from source_so.
1088 * We need to convert it to the user's locale charset
1089 * and then send the result to the pipe.
1091 if((outchars = utf8_to_locale((int) c, &cb, obuf, sizeof(obuf))) != 0)
1092 for(i = 0; i < outchars && !errstr; i++)
1093 if(fputc(obuf[i], fpipe->out.f) == EOF)
1094 errstr = error_description(errno);
1098 * Note: We clear errno here and test below, before ferror,
1099 * because *some* stdio implementations consider
1100 * EAGAIN and EWOULDBLOCK equivalent to EOF...
1102 errno = 0;
1103 clearerr(fpipe->in.f); /* fix from <cananian@cananian.mit.edu> */
1105 while(!errstr && fgets(buf, sizeof(buf), fpipe->in.f))
1106 errstr = gf_filter_puts(buf);
1108 /* then fgets failed! */
1109 if(!errstr && !(errno == EAGAIN || errno == EWOULDBLOCK)){
1110 if(feof(fpipe->in.f)) /* nothing else interesting! */
1111 break;
1112 else if(ferror(fpipe->in.f)) /* bummer. */
1113 errstr = error_description(errno);
1115 else if(errno == EAGAIN || errno == EWOULDBLOCK)
1116 clearerr(fpipe->in.f);
1119 #else /* !NON_BLOCKING_IO */
1121 if(prepend && (pipe_puts(prepend, fpipe) == EOF
1122 || pipe_putc('\n', fpipe) == EOF))
1123 errstr = error_description(errno);
1126 * Well, do the best we can, and hope the pipe we're writing
1127 * doesn't fill up before we start reading...
1129 while(!errstr && so_readc(&c, source_so))
1130 if((outchars = utf8_to_locale((int) c, &cb, obuf, sizeof(obuf))) != 0)
1131 for(i = 0; i < outchars && !errstr; i++)
1132 if(pipe_putc(obuf[i], fpipe) == EOF)
1133 errstr = error_description(errno);
1135 if(pipe_close_write(fpipe))
1136 errstr = _("Pipe command returned error.");
1138 while(!errstr && pipe_gets(buf, sizeof(buf), fpipe))
1139 errstr = gf_filter_puts(buf);
1141 #endif /* !NON_BLOCKING_IO */
1143 if(close_system_pipe(&fpipe, NULL, pipecb_f) && !errstr)
1144 errstr = _("Pipe command returned error.");
1146 gf_filter_eod();
1148 else
1149 errstr = _("Error setting up pipe command.");
1151 return(errstr);
1156 * gf_filter_puts - write the given string down the filter's pipe
1158 char *
1159 gf_filter_puts(register char *s)
1161 GF_INIT(gf_master, gf_master);
1164 * set up for any errors a filter may encounter
1166 if(setjmp(gf_error_state)){
1167 dprint((4, "ERROR: gf_filter_puts: %s\n",
1168 gf_error_string ? gf_error_string : "NULL"));
1169 return(gf_error_string);
1172 while(*s)
1173 GF_PUTC(gf_master, (*s++) & 0xff);
1175 GF_END(gf_master, gf_master);
1176 return(NULL);
1181 * gf_filter_eod - flush pending data filter's input queue and deliver
1182 * the GF_EOD marker.
1184 void
1185 gf_filter_eod(void)
1187 GF_INIT(gf_master, gf_master);
1188 (void) GF_FLUSH(gf_master);
1189 (*gf_master->f)(gf_master, GF_EOD);
1194 * END OF PIPE SUPPORT ROUTINES, BEGINNING OF FILTERS
1196 * Filters MUST use the specified interface (pointer to filter
1197 * structure, the unsigned character buffer in that struct, and a
1198 * cmd flag), and pass each resulting octet to the next filter in the
1199 * chain. Only the terminal filter need not call another filter.
1200 * As a result, filters share a pretty general structure.
1201 * Typically three main conditionals separate initialization from
1202 * data from end-of-data command processing.
1204 * Lastly, being character-at-a-time, they're a little more complex
1205 * to write than filters operating on buffers because some state
1206 * must typically be kept between characters. However, for a
1207 * little bit of complexity here, much convenience is gained later
1208 * as they can be arbitrarily chained together at run time and
1209 * consume few resources (especially memory or disk) as they work.
1210 * (NOTE 951005: even less cpu now that data between filters is passed
1211 * via a vector.)
1213 * A few notes about implementing filters:
1215 * - A generic filter template looks like:
1217 * void
1218 * gf_xxx_filter(f, flg)
1219 * FILTER_S *f;
1220 * int flg;
1222 * GF_INIT(f, f->next); // def's var's to speed queue drain
1224 * if(flg == GF_DATA){
1225 * register unsigned char c;
1227 * while(GF_GETC(f, c)){ // macro taking data off input queue
1228 * // operate on c and pass it on here
1229 * GF_PUTC(f->next, c); // macro writing output queue
1232 * GF_END(f, f->next); // macro to sync pointers/offsets
1233 * //WARNING: DO NOT RETURN BEFORE ALL INCOMING DATA'S PROCESSED
1235 * else if(flg == GF_EOD){
1236 * // process any buffered data here and pass it on
1237 * GF_FLUSH(f->next); // flush pending data to next filter
1238 * (*f->next->f)(f->next, GF_EOD);
1240 * else if(flg == GF_RESET){
1241 * // initialize any data in the struct here
1245 * - Any free storage allocated during initialization (typically tied
1246 * to the "line" pointer in FILTER_S) is the filter's responsibility
1247 * to clean up when the GF_EOD command comes through.
1249 * - Filter's must pass GF_EOD they receive on to the next
1250 * filter in the chain so it has the opportunity to flush
1251 * any buffered data.
1253 * - All filters expect NVT end-of-lines. The idea is to prepend
1254 * or append either the gf_local_nvtnl or gf_nvtnl_local
1255 * os-dependant filters to the data on the appropriate end of the
1256 * pipe for the task at hand.
1258 * - NOTE: As of 951004, filters no longer take their input as a single
1259 * char argument, but rather get data to operate on via a vector
1260 * representing the input queue in the FILTER_S structure.
1267 * BASE64 TO BINARY encoding and decoding routines below
1272 * BINARY to BASE64 filter (encoding described in rfc1341)
1274 void
1275 gf_binary_b64(FILTER_S *f, int flg)
1277 static char *v =
1278 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
1279 GF_INIT(f, f->next);
1281 if(flg == GF_DATA){
1282 register unsigned char c;
1283 register unsigned char t = f->t;
1284 register long n = f->n;
1286 while(GF_GETC(f, c)){
1288 switch(n++){
1289 case 0 : case 3 : case 6 : case 9 : case 12: case 15: case 18:
1290 case 21: case 24: case 27: case 30: case 33: case 36: case 39:
1291 case 42: case 45:
1292 GF_PUTC(f->next, v[c >> 2]);
1293 /* byte 1: high 6 bits (1) */
1294 t = c << 4; /* remember high 2 bits for next */
1295 break;
1297 case 1 : case 4 : case 7 : case 10: case 13: case 16: case 19:
1298 case 22: case 25: case 28: case 31: case 34: case 37: case 40:
1299 case 43:
1300 GF_PUTC(f->next, v[(t|(c>>4)) & 0x3f]);
1301 t = c << 2;
1302 break;
1304 case 2 : case 5 : case 8 : case 11: case 14: case 17: case 20:
1305 case 23: case 26: case 29: case 32: case 35: case 38: case 41:
1306 case 44:
1307 GF_PUTC(f->next, v[(t|(c >> 6)) & 0x3f]);
1308 GF_PUTC(f->next, v[c & 0x3f]);
1309 break;
1312 if(n == 45){ /* start a new line? */
1313 GF_PUTC(f->next, '\015');
1314 GF_PUTC(f->next, '\012');
1315 n = 0L;
1319 f->n = n;
1320 f->t = t;
1321 GF_END(f, f->next);
1323 else if(flg == GF_EOD){ /* no more data */
1324 switch (f->n % 3) { /* handle trailing bytes */
1325 case 0: /* no trailing bytes */
1326 break;
1328 case 1:
1329 GF_PUTC(f->next, v[(f->t) & 0x3f]);
1330 GF_PUTC(f->next, '='); /* byte 3 */
1331 GF_PUTC(f->next, '='); /* byte 4 */
1332 break;
1334 case 2:
1335 GF_PUTC(f->next, v[(f->t) & 0x3f]);
1336 GF_PUTC(f->next, '='); /* byte 4 */
1337 break;
1340 /* end with CRLF */
1341 if(f->n){
1342 GF_PUTC(f->next, '\015');
1343 GF_PUTC(f->next, '\012');
1346 (void) GF_FLUSH(f->next);
1347 (*f->next->f)(f->next, GF_EOD);
1349 else if(flg == GF_RESET){
1350 dprint((9, "-- gf_reset binary_b64\n"));
1351 f->n = 0L;
1358 * BASE64 to BINARY filter (encoding described in rfc1341)
1360 void
1361 gf_b64_binary(FILTER_S *f, int flg)
1363 static char v[] = {65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,
1364 65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,
1365 65,65,65,65,65,65,65,65,65,65,65,62,65,65,65,63,
1366 52,53,54,55,56,57,58,59,60,61,65,65,65,64,65,65,
1367 65, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14,
1368 15,16,17,18,19,20,21,22,23,24,25,65,65,65,65,65,
1369 65,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,
1370 41,42,43,44,45,46,47,48,49,50,51,65,65,65,65,65};
1371 GF_INIT(f, f->next);
1373 if(flg == GF_DATA){
1374 register unsigned char c;
1375 register unsigned char t = f->t;
1376 register int n = (int) f->n;
1377 register int state = f->f1;
1379 while(GF_GETC(f, c)){
1381 if(state){
1382 state = 0;
1383 if (c != '=') {
1384 gf_error("Illegal '=' in base64 text");
1385 /* NO RETURN */
1389 /* in range, and a valid value? */
1390 if((c & ~0x7f) || (c = v[c]) > 63){
1391 if(c == 64){
1392 switch (n++) { /* check quantum position */
1393 case 2:
1394 state++; /* expect an equal as next char */
1395 break;
1397 case 3:
1398 n = 0L; /* restart quantum */
1399 break;
1401 default: /* impossible quantum position */
1402 gf_error("Internal base64 decoder error");
1403 /* NO RETURN */
1407 else{
1408 switch (n++) { /* install based on quantum position */
1409 case 0: /* byte 1: high 6 bits */
1410 t = c << 2;
1411 break;
1413 case 1: /* byte 1: low 2 bits */
1414 GF_PUTC(f->next, (t|(c >> 4)));
1415 t = c << 4; /* byte 2: high 4 bits */
1416 break;
1418 case 2: /* byte 2: low 4 bits */
1419 GF_PUTC(f->next, (t|(c >> 2)));
1420 t = c << 6; /* byte 3: high 2 bits */
1421 break;
1423 case 3:
1424 GF_PUTC(f->next, t | c);
1425 n = 0L; /* reinitialize mechanism */
1426 break;
1431 f->f1 = state;
1432 f->t = t;
1433 f->n = n;
1434 GF_END(f, f->next);
1436 else if(flg == GF_EOD){
1437 (void) GF_FLUSH(f->next);
1438 (*f->next->f)(f->next, GF_EOD);
1440 else if(flg == GF_RESET){
1441 dprint((9, "-- gf_reset b64_binary\n"));
1442 f->n = 0L; /* quantum position */
1443 f->f1 = 0; /* state holder: equal seen? */
1451 * QUOTED-PRINTABLE ENCODING AND DECODING filters below.
1452 * encoding described in rfc1341
1455 #define GF_MAXLINE 80 /* good buffer size */
1458 * default action for QUOTED-PRINTABLE to 8BIT decoder
1460 #define GF_QP_DEFAULT(f, c) { \
1461 if((c) == ' '){ \
1462 state = WSPACE; \
1463 /* reset white space! */ \
1464 (f)->linep = (f)->line; \
1465 *((f)->linep)++ = ' '; \
1467 else if((c) == '='){ \
1468 state = EQUAL; \
1470 else \
1471 GF_PUTC((f)->next, (c)); \
1476 * QUOTED-PRINTABLE to 8BIT filter
1478 void
1479 gf_qp_8bit(FILTER_S *f, int flg)
1482 GF_INIT(f, f->next);
1484 if(flg == GF_DATA){
1485 register unsigned char c;
1486 register int state = f->f1;
1488 while(GF_GETC(f, c)){
1490 switch(state){
1491 case DFL : /* default case */
1492 default:
1493 GF_QP_DEFAULT(f, c);
1494 break;
1496 case CCR : /* non-significant space */
1497 state = DFL;
1498 if(c == '\012')
1499 continue; /* go on to next char */
1501 GF_QP_DEFAULT(f, c);
1502 break;
1504 case EQUAL :
1505 if(c == '\015'){ /* "=\015" is a soft EOL */
1506 state = CCR;
1507 break;
1510 if(c == '='){ /* compatibility clause for old guys */
1511 GF_PUTC(f->next, '=');
1512 state = DFL;
1513 break;
1516 if(!isxdigit((unsigned char)c)){ /* must be hex! */
1518 * First character after '=' not a hex digit.
1519 * This ain't right, but we're going to treat it as
1520 * plain old text instead of an '=' followed by hex.
1521 * In other words, they forgot to encode the '='.
1522 * Before 4.60 we just bailed with an error here, but now
1523 * we keep going as long as we are just displaying
1524 * the result (and not saving it or something).
1526 * Wait! The users don't like that. They want to be able
1527 * to use it even if it might be wrong. So just plow
1528 * ahead even if displaying.
1530 * Better have this be a constant string so that if we
1531 * get multiple instances of it in a single message we
1532 * can avoid the too many error messages problem. It
1533 * better be the same message as the one a few lines
1534 * below, as well.
1536 * Turn off decoding after encountering such an error and
1537 * just dump the rest of the text as is.
1539 state = STOP_DECODING;
1540 GF_PUTC(f->next, '=');
1541 GF_PUTC(f->next, c);
1542 q_status_message(SM_ORDER,3,3,
1543 _("Warning: Non-hexadecimal character in QP encoding!"));
1545 dprint((2, "gf_qp_8bit: warning: non-hex char in QP encoding: char \"%c\" (%d) follows =\n", c, c));
1546 break;
1549 if (isdigit ((unsigned char)c))
1550 f->t = c - '0';
1551 else
1552 f->t = c - (isupper((unsigned char)c) ? 'A' - 10 : 'a' - 10);
1554 f->f2 = c; /* store character in case we have to
1555 back out in !isxdigit below */
1557 state = HEX;
1558 break;
1560 case HEX :
1561 state = DFL;
1562 if(!isxdigit((unsigned char)c)){ /* must be hex! */
1563 state = STOP_DECODING;
1564 GF_PUTC(f->next, '=');
1565 GF_PUTC(f->next, f->f2);
1566 GF_PUTC(f->next, c);
1567 q_status_message(SM_ORDER,3,3,
1568 _("Warning: Non-hexadecimal character in QP encoding!"));
1570 dprint((2, "gf_qp_8bit: warning: non-hex char in QP encoding: char \"%c\" (%d) follows =%c\n", c, c, f->f2));
1571 break;
1574 if (isdigit((unsigned char)c))
1575 c -= '0';
1576 else
1577 c -= (isupper((unsigned char)c) ? 'A' - 10 : 'a' - 10);
1579 GF_PUTC(f->next, c + (f->t << 4));
1580 break;
1582 case WSPACE :
1583 if(c == ' '){ /* toss it in with other spaces */
1584 if(f->linep - f->line < GF_MAXLINE)
1585 *(f->linep)++ = ' ';
1586 break;
1589 state = DFL;
1590 if(c == '\015'){ /* not our white space! */
1591 f->linep = f->line; /* reset buffer */
1592 GF_PUTC(f->next, '\015');
1593 break;
1596 /* the spaces are ours, write 'em */
1597 f->n = f->linep - f->line;
1598 while((f->n)--)
1599 GF_PUTC(f->next, ' ');
1601 GF_QP_DEFAULT(f, c); /* take care of 'c' in default way */
1602 break;
1604 case STOP_DECODING :
1605 GF_PUTC(f->next, c);
1606 break;
1610 f->f1 = state;
1611 GF_END(f, f->next);
1613 else if(flg == GF_EOD){
1614 fs_give((void **)&(f->line));
1615 (void) GF_FLUSH(f->next);
1616 (*f->next->f)(f->next, GF_EOD);
1618 else if(flg == GF_RESET){
1619 dprint((9, "-- gf_reset qp_8bit\n"));
1620 f->f1 = DFL;
1621 f->linep = f->line = (char *)fs_get(GF_MAXLINE * sizeof(char));
1628 * USEFUL MACROS TO HELP WITH QP ENCODING
1631 #define QP_MAXL 75 /* 76th place only for continuation */
1634 * Macro to test and wrap long quoted printable lines
1636 #define GF_8BIT_WRAP(f) { \
1637 GF_PUTC((f)->next, '='); \
1638 GF_PUTC((f)->next, '\015'); \
1639 GF_PUTC((f)->next, '\012'); \
1643 * write a quoted octet in QUOTED-PRINTABLE encoding, adding soft
1644 * line break if needed.
1646 #define GF_8BIT_PUT_QUOTE(f, c) { \
1647 if(((f)->n += 3) > QP_MAXL){ \
1648 GF_8BIT_WRAP(f); \
1649 (f)->n = 3; /* set line count */ \
1651 GF_PUTC((f)->next, '='); \
1652 GF_PUTC((f)->next, HEX_CHAR1(c)); \
1653 GF_PUTC((f)->next, HEX_CHAR2(c)); \
1657 * just write an ordinary octet in QUOTED-PRINTABLE, wrapping line
1658 * if needed.
1660 #define GF_8BIT_PUT(f, c) { \
1661 if((++(f->n)) > QP_MAXL){ \
1662 GF_8BIT_WRAP(f); \
1663 f->n = 1L; \
1665 if(f->n == 1L && c == '.'){ \
1666 GF_8BIT_PUT_QUOTE(f, c); \
1667 f->n = 3; \
1669 else \
1670 GF_PUTC(f->next, c); \
1675 * default action for 8bit to quoted printable encoder
1677 #define GF_8BIT_DEFAULT(f, c) if((c) == ' '){ \
1678 state = WSPACE; \
1680 else if(c == '\015'){ \
1681 state = CCR; \
1683 else if(iscntrl(c & 0x7f) || (c == 0x7f) \
1684 || (c & 0x80) || (c == '=')){ \
1685 GF_8BIT_PUT_QUOTE(f, c); \
1687 else{ \
1688 GF_8BIT_PUT(f, c); \
1693 * 8BIT to QUOTED-PRINTABLE filter
1695 void
1696 gf_8bit_qp(FILTER_S *f, int flg)
1698 short dummy_dots = 0, dummy_dmap = 1;
1699 GF_INIT(f, f->next);
1701 if(flg == GF_DATA){
1702 register unsigned char c;
1703 register int state = f->f1;
1705 while(GF_GETC(f, c)){
1707 /* keep track of "^JFrom " */
1708 Find_Froms(f->t, dummy_dots, f->f2, dummy_dmap, c);
1710 switch(state){
1711 case DFL : /* handle ordinary case */
1712 GF_8BIT_DEFAULT(f, c);
1713 break;
1715 case CCR : /* true line break? */
1716 state = DFL;
1717 if(c == '\012'){
1718 GF_PUTC(f->next, '\015');
1719 GF_PUTC(f->next, '\012');
1720 f->n = 0L;
1722 else{ /* nope, quote the CR */
1723 GF_8BIT_PUT_QUOTE(f, '\015');
1724 GF_8BIT_DEFAULT(f, c); /* and don't forget about c! */
1726 break;
1728 case WSPACE:
1729 state = DFL;
1730 if(c == '\015' || f->t){ /* handle the space */
1731 GF_8BIT_PUT_QUOTE(f, ' ');
1732 f->t = 0; /* reset From flag */
1734 else
1735 GF_8BIT_PUT(f, ' ');
1737 GF_8BIT_DEFAULT(f, c); /* handle 'c' in the default way */
1738 break;
1742 f->f1 = state;
1743 GF_END(f, f->next);
1745 else if(flg == GF_EOD){
1746 switch(f->f1){
1747 case CCR :
1748 GF_8BIT_PUT_QUOTE(f, '\015'); /* write the last cr */
1749 break;
1751 case WSPACE :
1752 GF_8BIT_PUT_QUOTE(f, ' '); /* write the last space */
1753 break;
1756 (void) GF_FLUSH(f->next);
1757 (*f->next->f)(f->next, GF_EOD);
1759 else if(flg == GF_RESET){
1760 dprint((9, "-- gf_reset 8bit_qp\n"));
1761 f->f1 = DFL; /* state from last character */
1762 f->f2 = 1; /* state of "^NFrom " bitmap */
1763 f->t = 0;
1764 f->n = 0L; /* number of chars in current line */
1769 * This filter converts characters in one character set (the character
1770 * set of a message, for example) to another (the user's character set).
1772 void
1773 gf_convert_8bit_charset(FILTER_S *f, int flg)
1775 static unsigned char *conv_table = NULL;
1776 GF_INIT(f, f->next);
1778 if(flg == GF_DATA){
1779 register unsigned char c;
1781 while(GF_GETC(f, c)){
1782 GF_PUTC(f->next, conv_table ? conv_table[c] : c);
1785 GF_END(f, f->next);
1787 else if(flg == GF_EOD){
1788 (void) GF_FLUSH(f->next);
1789 (*f->next->f)(f->next, GF_EOD);
1791 else if(flg == GF_RESET){
1792 dprint((9, "-- gf_reset convert_8bit_charset\n"));
1793 conv_table = (f->opt) ? (unsigned char *) (f->opt) : NULL;
1799 typedef struct _utf8c_s {
1800 void *conv_table;
1801 int report_err;
1802 } UTF8C_S;
1806 * This filter converts characters in UTF-8 to an 8-bit or 16-bit charset.
1807 * Characters missing from the destination set, and invalid UTF-8 sequences,
1808 * will be converted to "?".
1810 void
1811 gf_convert_utf8_charset(FILTER_S *f, int flg)
1813 static unsigned short *conv_table = NULL;
1814 static int report_err = 0;
1815 register int more = f->f2;
1816 register long u = f->n;
1819 * "more" is the number of subsequent octets needed to complete a character,
1820 * it is stored in f->f2.
1821 * "u" is the accumulated Unicode character, it is stored in f->n
1824 GF_INIT(f, f->next);
1826 if(flg == GF_DATA){
1827 register unsigned char c;
1829 while(GF_GETC(f, c)){
1830 if(!conv_table){ /* can't do much if no conversion table */
1831 GF_PUTC(f->next, c);
1833 /* UTF-8 continuation? */
1834 else if((c > 0x7f) && (c < 0xc0)){
1835 if(more){
1836 u <<= 6; /* shift current value by 6 bits */
1837 u |= c & 0x3f;
1838 if (!--more){ /* last octet? */
1839 if(u >= 0xffff || (u = conv_table[u]) == NOCHAR){
1841 * non-BMP character or a UTF-8 character
1842 * which is not representable in the
1843 * charset we're converting to.
1845 c = '?';
1846 if(report_err){
1847 if(f->opt)
1848 fs_give((void **) &f->opt);
1850 /* TRANSLATORS: error while translating from one
1851 character set to another, for example from UTF-8
1852 to ISO-2022-JP or something like that. */
1853 gf_error(_("translation error"));
1854 /* NO RETURN */
1857 else{
1858 if(u > 0xff){
1859 c = (unsigned char) (u >> 8);
1860 GF_PUTC(f->next, c);
1863 c = (unsigned char) u & 0xff;
1866 GF_PUTC(f->next, c);
1869 else{ /* continuation when not in progress */
1870 GF_PUTC(f->next, '?');
1873 else{
1874 if(more){ /* incomplete UTF-8 character */
1875 GF_PUTC(f->next, '?');
1876 more = 0;
1878 if(c < 0x80){ /* U+0000 - U+007f */
1879 GF_PUTC(f->next, c);
1881 else if(c < 0xe0){ /* U+0080 - U+07ff */
1882 u = c & 0x1f; /* first 5 bits of 12 */
1883 more = 1;
1885 else if(c < 0xf0){ /* U+1000 - U+ffff */
1886 u = c & 0x0f; /* first 4 bits of 16 */
1887 more = 2;
1889 /* in case we ever support non-BMP Unicode */
1890 else if (c < 0xf8){ /* U+10000 - U+10ffff */
1891 u = c & 0x07; /* first 3 bits of 20.5 */
1892 more = 3;
1894 #if 0 /* ISO 10646 not in Unicode */
1895 else if (c < 0xfc){ /* ISO 10646 20000 - 3ffffff */
1896 u = c & 0x03; /* first 2 bits of 26 */
1897 more = 4;
1899 else if (c < 0xfe){ /* ISO 10646 4000000 - 7fffffff */
1900 u = c & 0x03; /* first 2 bits of 26 */
1901 more = 5;
1903 #endif
1904 else{ /* not in Unicode */
1905 GF_PUTC(f->next, '?');
1910 f->f2 = more;
1911 f->n = u;
1912 GF_END(f, f->next);
1914 else if(flg == GF_EOD){
1915 (void) GF_FLUSH(f->next);
1916 if(f->opt)
1917 fs_give((void **) &f->opt);
1918 (*f->next->f)(f->next, GF_EOD);
1920 else if(flg == GF_RESET){
1921 dprint((9, "-- gf_reset convert_utf8_charset\n"));
1922 conv_table = ((UTF8C_S *) f->opt)->conv_table;
1923 report_err = ((UTF8C_S *) f->opt)->report_err;
1924 f->f2 = 0;
1925 f->n = 0L;
1930 void *
1931 gf_convert_utf8_charset_opt(void *table, int report_err)
1933 UTF8C_S *utf8c;
1935 utf8c = (UTF8C_S *) fs_get(sizeof(UTF8C_S));
1936 utf8c->conv_table = table;
1937 utf8c->report_err = report_err;
1938 return((void *) utf8c);
1943 * ISO-2022-JP to EUC (on Unix) or Shift-JIS (on PC) filter
1945 * The routine is call ..._to_euc but it is really to either euc (unix Pine)
1946 * or to Shift-JIS (if PC-Pine).
1948 void
1949 gf_2022_jp_to_euc(FILTER_S *f, int flg)
1951 register unsigned char c;
1952 register int state = f->f1;
1955 * f->t lit means we're in middle of decoding a sequence of characters.
1956 * f->f2 keeps track of first character of pair for Shift-JIS.
1957 * f->f1 is the state.
1960 GF_INIT(f, f->next);
1962 if(flg == GF_DATA){
1963 while(GF_GETC(f, c)){
1964 switch(state){
1965 case ESC: /* saw ESC */
1966 if(!f->t && c == '$')
1967 state = ESCDOL;
1968 else if(f->t && c == '(')
1969 state = ESCPAR;
1970 else{
1971 GF_PUTC(f->next, '\033');
1972 GF_PUTC(f->next, c);
1973 state = DFL;
1976 break;
1978 case ESCDOL: /* saw ESC $ */
1979 if(c == 'B' || c == '@'){
1980 state = EUC;
1981 f->t = 1; /* filtering into euc */
1982 f->f2 = -1; /* first character of pair */
1984 else{
1985 GF_PUTC(f->next, '\033');
1986 GF_PUTC(f->next, '$');
1987 GF_PUTC(f->next, c);
1988 state = DFL;
1991 break;
1993 case ESCPAR: /* saw ESC ( */
1994 if(c == 'B' || c == 'J' || c == 'H'){
1995 state = DFL;
1996 f->t = 0; /* done filtering */
1998 else{
1999 GF_PUTC(f->next, '\033'); /* Don't set hibit for */
2000 GF_PUTC(f->next, '('); /* escape sequences, which */
2001 GF_PUTC(f->next, c); /* this appears to be. */
2004 break;
2006 case EUC: /* filtering into euc */
2007 if(c == '\033')
2008 state = ESC;
2009 else{
2010 #ifdef _WINDOWS /* Shift-JIS */
2011 c &= 0x7f; /* 8-bit can't win */
2012 if (f->f2 >= 0){ /* second of a pair? */
2013 int rowOffset = (f->f2 < 95) ? 112 : 176;
2014 int cellOffset = (f->f2 % 2) ? ((c > 95) ? 32 : 31)
2015 : 126;
2017 GF_PUTC(f->next, ((f->f2 + 1) >> 1) + rowOffset);
2018 GF_PUTC(f->next, c + cellOffset);
2019 f->f2 = -1; /* restart */
2021 else if(c > 0x20 && c < 0x7f)
2022 f->f2 = c; /* first of pair */
2023 else{
2024 GF_PUTC(f->next, c); /* write CTL as itself */
2025 f->f2 = -1;
2027 #else /* EUC */
2028 GF_PUTC(f->next, (c > 0x20 && c < 0x7f) ? c | 0x80 : c);
2029 #endif
2032 break;
2034 case DFL:
2035 default:
2036 if(c == '\033')
2037 state = ESC;
2038 else
2039 GF_PUTC(f->next, c);
2041 break;
2045 f->f1 = state;
2046 GF_END(f, f->next);
2048 else if(flg == GF_EOD){
2049 switch(state){
2050 case ESC:
2051 GF_PUTC(f->next, '\033');
2052 break;
2054 case ESCDOL:
2055 GF_PUTC(f->next, '\033');
2056 GF_PUTC(f->next, '$');
2057 break;
2059 case ESCPAR:
2060 GF_PUTC(f->next, '\033'); /* Don't set hibit for */
2061 GF_PUTC(f->next, '('); /* escape sequences. */
2062 break;
2065 (void) GF_FLUSH(f->next);
2066 (*f->next->f)(f->next, GF_EOD);
2068 else if(flg == GF_RESET){
2069 dprint((9, "-- gf_reset jp_to_euc\n"));
2070 f->f1 = DFL; /* state */
2071 f->t = 0; /* not translating to euc */
2077 * EUC (on Unix) or Shift-JIS (on PC) to ISO-2022-JP filter
2079 void
2080 gf_native8bitjapanese_to_2022_jp(FILTER_S *f, int flg)
2082 #ifdef _WINDOWS
2083 gf_sjis_to_2022_jp(f, flg);
2084 #else
2085 gf_euc_to_2022_jp(f, flg);
2086 #endif
2090 void
2091 gf_euc_to_2022_jp(FILTER_S *f, int flg)
2093 register unsigned char c;
2096 * f->t lit means we've sent the start esc seq but not the end seq.
2097 * f->f2 keeps track of first character of pair for Shift-JIS.
2100 GF_INIT(f, f->next);
2102 if(flg == GF_DATA){
2103 while(GF_GETC(f, c)){
2104 if(f->t){
2105 if(c & 0x80){
2106 GF_PUTC(f->next, c & 0x7f);
2108 else{
2109 GF_PUTC(f->next, '\033');
2110 GF_PUTC(f->next, '(');
2111 GF_PUTC(f->next, 'B');
2112 GF_PUTC(f->next, c);
2113 f->f2 = -1;
2114 f->t = 0;
2117 else{
2118 if(c & 0x80){
2119 GF_PUTC(f->next, '\033');
2120 GF_PUTC(f->next, '$');
2121 GF_PUTC(f->next, 'B');
2122 GF_PUTC(f->next, c & 0x7f);
2123 f->t = 1;
2125 else{
2126 GF_PUTC(f->next, c);
2131 GF_END(f, f->next);
2133 else if(flg == GF_EOD){
2134 if(f->t){
2135 GF_PUTC(f->next, '\033');
2136 GF_PUTC(f->next, '(');
2137 GF_PUTC(f->next, 'B');
2138 f->t = 0;
2139 f->f2 = -1;
2142 (void) GF_FLUSH(f->next);
2143 (*f->next->f)(f->next, GF_EOD);
2145 else if(flg == GF_RESET){
2146 dprint((9, "-- gf_reset euc_to_jp\n"));
2147 f->t = 0;
2148 f->f2 = -1;
2152 void
2153 gf_sjis_to_2022_jp(FILTER_S *f, int flg)
2155 register unsigned char c;
2158 * f->t lit means we've sent the start esc seq but not the end seq.
2159 * f->f2 keeps track of first character of pair for Shift-JIS.
2162 GF_INIT(f, f->next);
2164 if(flg == GF_DATA){
2165 while(GF_GETC(f, c)){
2166 if(f->t){
2167 if(f->f2 >= 0){ /* second of a pair? */
2168 int adjust = c < 159;
2169 int rowOffset = f->f2 < 160 ? 112 : 176;
2170 int cellOffset = adjust ? (c > 127 ? 32 : 31) : 126;
2172 GF_PUTC(f->next, ((f->f2 - rowOffset) << 1) - adjust);
2173 GF_PUTC(f->next, c - cellOffset);
2174 f->f2 = -1;
2176 else if(c & 0x80){
2177 f->f2 = c; /* remember first of pair */
2179 else{
2180 GF_PUTC(f->next, '\033');
2181 GF_PUTC(f->next, '(');
2182 GF_PUTC(f->next, 'B');
2183 GF_PUTC(f->next, c);
2184 f->f2 = -1;
2185 f->t = 0;
2188 else{
2189 if(c & 0x80){
2190 GF_PUTC(f->next, '\033');
2191 GF_PUTC(f->next, '$');
2192 GF_PUTC(f->next, 'B');
2193 f->f2 = c;
2194 f->t = 1;
2196 else{
2197 GF_PUTC(f->next, c);
2202 GF_END(f, f->next);
2204 else if(flg == GF_EOD){
2205 if(f->t){
2206 GF_PUTC(f->next, '\033');
2207 GF_PUTC(f->next, '(');
2208 GF_PUTC(f->next, 'B');
2209 f->t = 0;
2210 f->f2 = -1;
2213 (void) GF_FLUSH(f->next);
2214 (*f->next->f)(f->next, GF_EOD);
2216 else if(flg == GF_RESET){
2217 dprint((9, "-- gf_reset sjis_to_jp\n"));
2218 f->t = 0;
2219 f->f2 = -1;
2226 * Various charset to UTF-8 Translation filter
2230 * utf8 conversion options
2232 typedef struct _utf8_s {
2233 CHARSET *charset;
2234 unsigned long ucsc;
2235 } UTF8_S;
2237 #define UTF8_BLOCK 1024
2238 #define UTF8_EOB(f) ((f)->line + (f)->f2 - 1)
2239 #define UTF8_ADD(f, c) \
2241 if(p >= eobuf){ \
2242 f->f2 += UTF8_BLOCK; \
2243 fs_resize((void **)&f->line, \
2244 (size_t) f->f2 * sizeof(char)); \
2245 eobuf = UTF8_EOB(f); \
2246 p = eobuf - UTF8_BLOCK; \
2248 *p++ = c; \
2250 #define GF_UTF8_FLUSH(f) { \
2251 register long n; \
2252 SIZEDTEXT intext, outtext; \
2253 intext.data = (unsigned char *) f->line; \
2254 intext.size = p - f->line; \
2255 memset(&outtext, 0, sizeof(SIZEDTEXT)); \
2256 if(!((UTF8_S *) f->opt)->charset){ \
2257 for(n = 0; n < intext.size; n++) \
2258 GF_PUTC(f->next, (intext.data[n] & 0x80) ? '?' : intext.data[n]); \
2260 else if(utf8_text_cs(&intext, ((UTF8_S *) f->opt)->charset, &outtext, NULL, NULL)){ \
2261 for(n = 0; n < outtext.size; n++) \
2262 GF_PUTC(f->next, outtext.data[n]); \
2263 if(outtext.data && intext.data != outtext.data) \
2264 fs_give((void **) &outtext.data); \
2266 else{ \
2267 for(n = 0; n < intext.size; n++) \
2268 GF_PUTC(f->next, '?'); \
2274 * gf_utf8 - text in specified charset to to UTF-8 filter
2275 * Process line-at-a-time rather than character
2276 * because ISO-2022-JP. Call utf8_text_cs by hand
2277 * rather than utf8_text to reduce the cost of
2278 * utf8_charset() for each line.
2280 void
2281 gf_utf8(FILTER_S *f, int flg)
2283 register char *p = f->linep;
2284 register char *eobuf = UTF8_EOB(f);
2285 GF_INIT(f, f->next);
2287 if(flg == GF_DATA){
2288 register int state = f->f1;
2289 register unsigned char c;
2291 while(GF_GETC(f, c)){
2293 switch(state){
2294 case CCR :
2295 state = DFL;
2296 if(c == '\012'){
2297 GF_UTF8_FLUSH(f);
2298 p = f->line;
2299 GF_PUTC(f->next, '\015');
2300 GF_PUTC(f->next, '\012');
2302 else{
2303 UTF8_ADD(f, '\015');
2304 UTF8_ADD(f, c);
2307 break;
2309 default :
2310 if(c == '\015'){
2311 state = CCR;
2313 else
2314 UTF8_ADD(f, c);
2318 f->f1 = state;
2319 GF_END(f, f->next);
2321 else if(flg == GF_EOD){
2323 if(p != f->line)
2324 GF_UTF8_FLUSH(f);
2326 fs_give((void **) &f->line);
2327 fs_give((void **) &f->opt);
2328 (void) GF_FLUSH(f->next);
2329 (*f->next->f)(f->next, GF_EOD);
2331 else if(GF_RESET){
2332 dprint((9, "-- gf_reset utf8\n"));
2333 f->f1 = DFL;
2334 f->f2 = UTF8_BLOCK; /* input buffer length */
2335 f->line = p = (char *) fs_get(f->f2 * sizeof(char));
2338 f->linep = p;
2342 void *
2343 gf_utf8_opt(char *charset)
2345 UTF8_S *utf8;
2347 utf8 = (UTF8_S *) fs_get(sizeof(UTF8_S));
2349 utf8->charset = (CHARSET *) utf8_charset(charset);
2352 * When we get 8-bit non-ascii characters but it is supposed to
2353 * be ascii we want it to turn into question marks, not
2354 * just behave as if it is UTF-8 which is what happens
2355 * with ascii because there is no translation table.
2356 * So we need to catch the ascii special case here.
2358 if(utf8->charset && utf8->charset->type == CT_ASCII)
2359 utf8->charset = NULL;
2361 return((void *) utf8);
2366 * RICHTEXT-TO-PLAINTEXT filter
2370 * option to be used by rich2plain (NOTE: if this filter is ever
2371 * used more than once in a pipe, all instances will have the same
2372 * option value)
2376 /*----------------------------------------------------------------------
2377 richtext to plaintext filter
2379 Args: f --
2380 flg --
2382 This basically removes all richtext formatting. A cute hack is used
2383 to get bold and underlining to work.
2384 Further work could be done to handle things like centering and right
2385 and left flush, but then it could no longer be done in place. This
2386 operates on text *with* CRLF's.
2388 WARNING: does not wrap lines!
2389 ----*/
2390 void
2391 gf_rich2plain(FILTER_S *f, int flg)
2393 static int rich_bold_on = 0, rich_uline_on = 0;
2395 /* BUG: quote incoming \255 values */
2396 GF_INIT(f, f->next);
2398 if(flg == GF_DATA){
2399 register unsigned char c;
2400 register int state = f->f1;
2401 register int plain;
2403 plain = f->opt ? (*(int *) f->opt) : 0;
2405 while(GF_GETC(f, c)){
2407 switch(state){
2408 case TOKEN : /* collect a richtext token */
2409 if(c == '>'){ /* what should we do with it? */
2410 state = DFL; /* return to default next time */
2411 *(f->linep) = '\0'; /* cap off token */
2412 if(f->line[0] == 'l' && f->line[1] == 't'){
2413 GF_PUTC(f->next, '<'); /* literal '<' */
2415 else if(f->line[0] == 'n' && f->line[1] == 'l'){
2416 GF_PUTC(f->next, '\015');/* newline! */
2417 GF_PUTC(f->next, '\012');
2419 else if(!strcmp("comment", f->line)){
2420 (f->f2)++;
2422 else if(!strcmp("/comment", f->line)){
2423 f->f2 = 0;
2425 else if(!strcmp("/paragraph", f->line)) {
2426 GF_PUTC(f->next, '\r');
2427 GF_PUTC(f->next, '\n');
2428 GF_PUTC(f->next, '\r');
2429 GF_PUTC(f->next, '\n');
2431 else if(!plain /* gf_rich_plain */){
2432 if(!strcmp(f->line, "bold")) {
2433 GF_PUTC(f->next, TAG_EMBED);
2434 GF_PUTC(f->next, TAG_BOLDON);
2435 rich_bold_on = 1;
2436 } else if(!strcmp(f->line, "/bold")) {
2437 GF_PUTC(f->next, TAG_EMBED);
2438 GF_PUTC(f->next, TAG_BOLDOFF);
2439 rich_bold_on = 0;
2440 } else if(!strcmp(f->line, "italic")) {
2441 GF_PUTC(f->next, TAG_EMBED);
2442 GF_PUTC(f->next, TAG_ULINEON);
2443 rich_uline_on = 1;
2444 } else if(!strcmp(f->line, "/italic")) {
2445 GF_PUTC(f->next, TAG_EMBED);
2446 GF_PUTC(f->next, TAG_ULINEOFF);
2447 rich_uline_on = 0;
2448 } else if(!strcmp(f->line, "underline")) {
2449 GF_PUTC(f->next, TAG_EMBED);
2450 GF_PUTC(f->next, TAG_ULINEON);
2451 rich_uline_on = 1;
2452 } else if(!strcmp(f->line, "/underline")) {
2453 GF_PUTC(f->next, TAG_EMBED);
2454 GF_PUTC(f->next, TAG_ULINEOFF);
2455 rich_uline_on = 0;
2458 /* else we just ignore the token! */
2460 f->linep = f->line; /* reset token buffer */
2462 else{ /* add char to token */
2463 if(f->linep - f->line > 40){
2464 /* What? rfc1341 says 40 char tokens MAX! */
2465 fs_give((void **)&(f->line));
2466 gf_error("Richtext token over 40 characters");
2467 /* NO RETURN */
2470 *(f->linep)++ = isupper((unsigned char)c) ? c-'A'+'a' : c;
2472 break;
2474 case CCR :
2475 state = DFL; /* back to default next time */
2476 if(c == '\012'){ /* treat as single space? */
2477 GF_PUTC(f->next, ' ');
2478 break;
2480 /* fall thru to process c */
2482 case DFL :
2483 default:
2484 if(c == '<')
2485 state = TOKEN;
2486 else if(c == '\015')
2487 state = CCR;
2488 else if(!f->f2) /* not in comment! */
2489 GF_PUTC(f->next, c);
2491 break;
2495 f->f1 = state;
2496 GF_END(f, f->next);
2498 else if(flg == GF_EOD){
2499 if((f->f1 = (f->linep != f->line)) != 0){
2500 /* incomplete token!! */
2501 gf_error("Incomplete token in richtext");
2502 /* NO RETURN */
2505 if(rich_uline_on){
2506 GF_PUTC(f->next, TAG_EMBED);
2507 GF_PUTC(f->next, TAG_ULINEOFF);
2508 rich_uline_on = 0;
2510 if(rich_bold_on){
2511 GF_PUTC(f->next, TAG_EMBED);
2512 GF_PUTC(f->next, TAG_BOLDOFF);
2513 rich_bold_on = 0;
2516 fs_give((void **)&(f->line));
2517 (void) GF_FLUSH(f->next);
2518 (*f->next->f)(f->next, GF_EOD);
2520 else if(flg == GF_RESET){
2521 dprint((9, "-- gf_reset rich2plain\n"));
2522 f->f1 = DFL; /* state */
2523 f->f2 = 0; /* set means we're in a comment */
2524 f->linep = f->line = (char *)fs_get(45 * sizeof(char));
2530 * function called from the outside to set
2531 * richtext filter's options
2533 void *
2534 gf_rich2plain_opt(int *plain)
2536 return((void *) plain);
2542 * ENRICHED-TO-PLAIN text filter
2545 #define TEF_QUELL 0x01
2546 #define TEF_NOFILL 0x02
2550 /*----------------------------------------------------------------------
2551 enriched text to plain text filter (ala rfc1523)
2553 Args: f -- state and input data
2554 flg --
2556 This basically removes all enriched formatting. A cute hack is used
2557 to get bold and underlining to work.
2559 Further work could be done to handle things like centering and right
2560 and left flush, but then it could no longer be done in place. This
2561 operates on text *with* CRLF's.
2563 WARNING: does not wrap lines!
2564 ----*/
2565 void
2566 gf_enriched2plain(FILTER_S *f, int flg)
2568 static int enr_uline_on = 0, enr_bold_on = 0;
2570 /* BUG: quote incoming \255 values */
2571 GF_INIT(f, f->next);
2573 if(flg == GF_DATA){
2574 register unsigned char c;
2575 register int state = f->f1;
2576 register int plain;
2578 plain = f->opt ? (*(int *) f->opt) : 0;
2580 while(GF_GETC(f, c)){
2582 switch(state){
2583 case TOKEN : /* collect a richtext token */
2584 if(c == '>'){ /* what should we do with it? */
2585 int off = *f->line == '/';
2586 char *token = f->line + (off ? 1 : 0);
2587 state = DFL;
2588 *f->linep = '\0';
2589 if(!strcmp("param", token)){
2590 if(off)
2591 f->f2 &= ~TEF_QUELL;
2592 else
2593 f->f2 |= TEF_QUELL;
2595 else if(!strcmp("nofill", token)){
2596 if(off)
2597 f->f2 &= ~TEF_NOFILL;
2598 else
2599 f->f2 |= TEF_NOFILL;
2601 else if(!plain /* gf_enriched_plain */){
2602 /* Following is a cute hack or two to get
2603 bold and underline on the screen.
2604 See Putline0n() where these codes are
2605 interpreted */
2606 if(!strcmp("bold", token)) {
2607 GF_PUTC(f->next, TAG_EMBED);
2608 GF_PUTC(f->next, off ? TAG_BOLDOFF : TAG_BOLDON);
2609 enr_bold_on = off ? 0 : 1;
2610 } else if(!strcmp("italic", token)) {
2611 GF_PUTC(f->next, TAG_EMBED);
2612 GF_PUTC(f->next, off ? TAG_ULINEOFF : TAG_ULINEON);
2613 enr_uline_on = off ? 0 : 1;
2614 } else if(!strcmp("underline", token)) {
2615 GF_PUTC(f->next, TAG_EMBED);
2616 GF_PUTC(f->next, off ? TAG_ULINEOFF : TAG_ULINEON);
2617 enr_uline_on = off ? 0 : 1;
2620 /* else we just ignore the token! */
2622 f->linep = f->line; /* reset token buffer */
2624 else if(c == '<'){ /* literal '<'? */
2625 if(f->linep == f->line){
2626 GF_PUTC(f->next, '<');
2627 state = DFL;
2629 else{
2630 fs_give((void **)&(f->line));
2631 gf_error("Malformed Enriched text: unexpected '<'");
2632 /* NO RETURN */
2635 else{ /* add char to token */
2636 if(f->linep - f->line > 60){ /* rfc1523 says 60 MAX! */
2637 fs_give((void **)&(f->line));
2638 gf_error("Malformed Enriched text: token too long");
2639 /* NO RETURN */
2642 *(f->linep)++ = isupper((unsigned char)c) ? c-'A'+'a' : c;
2644 break;
2646 case CCR :
2647 if(c != '\012'){ /* treat as single space? */
2648 state = DFL; /* lone cr? */
2649 f->f2 &= ~TEF_QUELL;
2650 GF_PUTC(f->next, '\015');
2651 goto df;
2654 state = CLF;
2655 break;
2657 case CLF :
2658 if(c == '\015'){ /* treat as single space? */
2659 state = CCR; /* repeat crlf's mean real newlines */
2660 f->f2 |= TEF_QUELL;
2661 GF_PUTC(f->next, '\r');
2662 GF_PUTC(f->next, '\n');
2663 break;
2665 else{
2666 state = DFL;
2667 if(!((f->f2) & TEF_QUELL))
2668 GF_PUTC(f->next, ' ');
2670 f->f2 &= ~TEF_QUELL;
2673 /* fall thru to take care of 'c' */
2675 case DFL :
2676 default :
2677 df :
2678 if(c == '<')
2679 state = TOKEN;
2680 else if(c == '\015' && (!((f->f2) & TEF_NOFILL)))
2681 state = CCR;
2682 else if(!((f->f2) & TEF_QUELL))
2683 GF_PUTC(f->next, c);
2685 break;
2689 f->f1 = state;
2690 GF_END(f, f->next);
2692 else if(flg == GF_EOD){
2693 if((f->f1 = (f->linep != f->line)) != 0){
2694 /* incomplete token!! */
2695 gf_error("Incomplete token in richtext");
2696 /* NO RETURN */
2698 if(enr_uline_on){
2699 GF_PUTC(f->next, TAG_EMBED);
2700 GF_PUTC(f->next, TAG_ULINEOFF);
2701 enr_uline_on = 0;
2703 if(enr_bold_on){
2704 GF_PUTC(f->next, TAG_EMBED);
2705 GF_PUTC(f->next, TAG_BOLDOFF);
2706 enr_bold_on = 0;
2709 /* Make sure we end with a newline so everything gets flushed */
2710 GF_PUTC(f->next, '\015');
2711 GF_PUTC(f->next, '\012');
2713 fs_give((void **)&(f->line));
2715 (void) GF_FLUSH(f->next);
2716 (*f->next->f)(f->next, GF_EOD);
2718 else if(flg == GF_RESET){
2719 dprint((9, "-- gf_reset enriched2plain\n"));
2720 f->f1 = DFL; /* state */
2721 f->f2 = 0; /* set means we're in a comment */
2722 f->linep = f->line = (char *)fs_get(65 * sizeof(char));
2728 * function called from the outside to set
2729 * richtext filter's options
2731 void *
2732 gf_enriched2plain_opt(int *plain)
2734 return((void *) plain);
2740 * HTML-TO-PLAIN text filter
2744 /* OK, here's the plan:
2746 * a universal output function handles writing chars and worries
2747 * about wrapping.
2749 * a unversal element collector reads chars and collects params
2750 * and dispatches the appropriate element handler.
2752 * element handlers are stacked. The most recently dispatched gets
2753 * first crack at the incoming character stream. It passes bytes it's
2754 * done with or not interested in to the next
2756 * installs that handler as the current one collecting data...
2758 * stacked handlers take their params from the element collector and
2759 * accept chars or do whatever they need to do. Sort of a vertical
2760 * piping? recursion-like? hmmm.
2762 * at least I think this is how it'll work. tres simple, non?
2768 * Some important constants
2770 #define HTML_BUF_LEN 2048 /* max scratch buffer length */
2771 #define MAX_ENTITY 20 /* maximum length of an entity */
2772 #define MAX_ELEMENT 72 /* maximum length of an element */
2773 #define HTML_MOREDATA 0 /* expect more entity data */
2774 #define HTML_ENTITY 1 /* valid entity collected */
2775 #define HTML_BADVALUE 0x0100 /* good data, but bad entity value */
2776 #define HTML_BADDATA 0x0200 /* bad data found looking for entity */
2777 #define HTML_LITERAL 0x0400 /* Literal character value */
2778 #define HTML_NEWLINE 0x010A /* hard newline */
2779 #define HTML_DOBOLD 0x0400 /* Start Bold display */
2780 #define HTML_ID_GET 0 /* indent func: return current val */
2781 #define HTML_ID_SET 1 /* indent func: set to absolute val */
2782 #define HTML_ID_INC 2 /* indent func: increment by val */
2783 #define HTML_HX_CENTER 0x0001
2784 #define HTML_HX_ULINE 0x0002
2785 #define RSS_ITEM_LIMIT 20 /* RSS 2.0 ITEM depth limit */
2788 /* types of lists that we will support */
2789 #define LIST_DECIMAL (long) 0
2790 #define LIST_ALPHALO (long) 1
2791 #define LIST_ALPHAUP (long) 2
2792 #define LIST_ROMANLO (long) 3
2793 #define LIST_ROMANUP (long) 4
2794 #define LIST_UNKNOWN (long) 10
2797 * Handler data, state information including function that uses it
2799 typedef struct handler_s {
2800 FILTER_S *html_data;
2801 void *element;
2802 long x, y, z;
2803 void *dp;
2804 unsigned char *s;
2805 struct handler_s *below;
2806 } HANDLER_S;
2809 * Element Property structure
2811 typedef struct _element_properties {
2812 char *element;
2813 size_t len;
2814 int (*handler)(HANDLER_S *, int, int);
2815 unsigned blocklevel:1;
2816 unsigned alternate:1;
2817 } ELPROP_S;
2820 * Types used to manage HTML parsing
2822 static void html_handoff(HANDLER_S *, int);
2826 * to help manage line wrapping.
2828 typedef struct _wrap_line {
2829 char *buf; /* buf to collect wrapped text */
2830 int used, /* number of chars in buf */
2831 width, /* text's width as displayed */
2832 len; /* length of allocated buf */
2833 } WRAPLINE_S;
2837 * to help manage centered text
2839 typedef struct _center_s {
2840 WRAPLINE_S line; /* buf to assembled centered text */
2841 WRAPLINE_S word; /* word being to append to Line */
2842 int anchor;
2843 short space;
2844 } CENTER_S;
2848 * Collector data and state information
2850 typedef struct collector_s {
2851 char buf[HTML_BUF_LEN]; /* buffer to collect data */
2852 int len; /* length of that buffer */
2853 unsigned unquoted_data:1; /* parameter is not quoted... */
2854 unsigned end_tag:1; /* collecting a closing tag */
2855 unsigned hit_equal:1; /* collecting right half of attrib */
2856 unsigned mkup_decl:1; /* markup declaration */
2857 unsigned start_comment:1; /* markup declaration comment */
2858 unsigned end_comment:1; /* legit comment format */
2859 unsigned hyphen:1; /* markup hyphen read */
2860 unsigned badform:1; /* malformed markup element */
2861 unsigned overrun:1; /* Overran buf above */
2862 unsigned proc_inst:1; /* XML processing instructions */
2863 unsigned empty:1; /* empty element */
2864 unsigned was_quoted:1; /* basically to catch null string */
2865 char quoted; /* quoted element param value */
2866 char *element; /* element's collected name */
2867 PARAMETER *attribs; /* element's collected attributes */
2868 PARAMETER *cur_attrib; /* attribute now being collected */
2869 } CLCTR_S;
2873 * State information for all element handlers
2875 typedef struct html_data {
2876 HANDLER_S *h_stack; /* handler list */
2877 CLCTR_S *el_data; /* element collector data */
2878 CENTER_S *centered; /* struct to manage centered text */
2879 int (*token)(FILTER_S *, int);
2880 char quoted; /* quoted, by either ' or ", text */
2881 short indent_level; /* levels of indention */
2882 int in_anchor; /* text now being written to anchor */
2883 int blanks; /* Consecutive blank line count */
2884 int wrapcol; /* column to wrap lines on */
2885 int *prefix; /* buffer containing Anchor prefix */
2886 int prefix_used;
2887 long line_bufsize; /* current size of the line buffer */
2888 COLOR_PAIR *color;
2889 struct {
2890 int state; /* embedded data state */
2891 char *color; /* embedded color pointer */
2892 } embedded;
2893 CBUF_S cb; /* utf8->ucs4 conversion state */
2894 unsigned wrapstate:1; /* whether or not to wrap output */
2895 unsigned li_pending:1; /* <LI> next token expected */
2896 unsigned de_pending:1; /* <DT> or <DD> next token expected */
2897 unsigned bold_on:1; /* currently bolding text */
2898 unsigned uline_on:1; /* currently underlining text */
2899 unsigned center:1; /* center output text */
2900 unsigned bitbucket:1; /* Ignore input */
2901 unsigned head:1; /* In doc's HEAD */
2902 unsigned body:1; /* In doc's BODY */
2903 unsigned alt_entity:1; /* use alternative entity values */
2904 unsigned wrote:1; /* anything witten yet? */
2905 } HTML_DATA_S;
2909 * HTML filter options
2911 typedef struct _html_opts {
2912 char *base; /* Base URL for this html file */
2913 int columns, /* Display columns (excluding margins) */
2914 indent; /* Left margin */
2915 HANDLE_S **handlesp; /* Head of handles */
2916 htmlrisk_t warnrisk_f; /* Nasty link warning call */
2917 ELPROP_S *element_table; /* markup element table */
2918 RSS_FEED_S **feedp; /* hook for RSS feed response */
2919 unsigned strip:1; /* Hilite TAGs allowed */
2920 unsigned handles_loc:1; /* Local handles requested? */
2921 unsigned showserver:1; /* Display server after anchors */
2922 unsigned outputted:1; /* any */
2923 unsigned no_relative_links:1; /* Disable embedded relative links */
2924 unsigned related_content:1; /* Embedded related content */
2925 unsigned html:1; /* Output content in HTML */
2926 unsigned html_imgs:1; /* Output IMG tags in HTML content */
2927 } HTML_OPT_S;
2932 * Some macros to make life a little easier
2934 #define WRAP_COLS(X) ((X)->opt ? ((HTML_OPT_S *)(X)->opt)->columns : 80)
2935 #define HTML_INDENT(X) ((X)->opt ? ((HTML_OPT_S *)(X)->opt)->indent : 0)
2936 #define HTML_WROTE(X) (HD(X)->wrote)
2937 #define HTML_BASE(X) ((X)->opt ? ((HTML_OPT_S *)(X)->opt)->base : NULL)
2938 #define STRIP(X) ((X)->opt && ((HTML_OPT_S *)(X)->opt)->strip)
2939 #define PASS_HTML(X) ((X)->opt && ((HTML_OPT_S *)(X)->opt)->html)
2940 #define PASS_IMAGES(X) ((X)->opt && ((HTML_OPT_S *)(X)->opt)->html_imgs)
2941 #define HANDLESP(X) (((HTML_OPT_S *)(X)->opt)->handlesp)
2942 #define DO_HANDLES(X) ((X)->opt && HANDLESP(X))
2943 #define HANDLES_LOC(X) ((X)->opt && ((HTML_OPT_S *)(X)->opt)->handles_loc)
2944 #define SHOWSERVER(X) ((X)->opt && ((HTML_OPT_S *)(X)->opt)->showserver)
2945 #define NO_RELATIVE(X) ((X)->opt && ((HTML_OPT_S *)(X)->opt)->no_relative_links)
2946 #define RELATED_OK(X) ((X)->opt && ((HTML_OPT_S *)(X)->opt)->related_content)
2947 #define ELEMENTS(X) (((HTML_OPT_S *)(X)->opt)->element_table)
2948 #define RSS_FEED(X) (*(((HTML_OPT_S *)(X)->opt)->feedp))
2949 #define MAKE_LITERAL(C) (HTML_LITERAL | ((C) & 0xff))
2950 #define IS_LITERAL(C) (HTML_LITERAL & (C))
2951 #define HD(X) ((HTML_DATA_S *)(X)->data)
2952 #define ED(X) (HD(X)->el_data)
2953 #define EL(X) ((ELPROP_S *) (X)->element)
2954 #define ASCII_ISSPACE(C) ((C) < 0x80 && isspace((unsigned char) (C)))
2955 #define HTML_ISSPACE(C) (IS_LITERAL(C) == 0 && ((C) == HTML_NEWLINE || ASCII_ISSPACE(C)))
2956 #define NEW_CLCTR(X) { \
2957 ED(X) = (CLCTR_S *)fs_get(sizeof(CLCTR_S)); \
2958 memset(ED(X), 0, sizeof(CLCTR_S)); \
2959 HD(X)->token = html_element_collector; \
2962 #define FREE_CLCTR(X) { \
2963 if(ED(X)->attribs){ \
2964 PARAMETER *p; \
2965 while((p = ED(X)->attribs) != NULL){ \
2966 ED(X)->attribs = ED(X)->attribs->next; \
2967 if(p->attribute) \
2968 fs_give((void **)&p->attribute); \
2969 if(p->value) \
2970 fs_give((void **)&p->value); \
2971 fs_give((void **)&p); \
2974 if(ED(X)->element) \
2975 fs_give((void **) &ED(X)->element); \
2976 fs_give((void **) &ED(X)); \
2977 HD(X)->token = NULL; \
2979 #define HANDLERS(X) (HD(X)->h_stack)
2980 #define BOLD_BIT(X) (HD(X)->bold_on)
2981 #define ULINE_BIT(X) (HD(X)->uline_on)
2982 #define CENTER_BIT(X) (HD(X)->center)
2983 #define HTML_FLUSH(X) { \
2984 html_write(X, (X)->line, (X)->linep - (X)->line); \
2985 (X)->linep = (X)->line; \
2986 (X)->f2 = 0L; \
2988 #define HTML_BOLD(X, S) if(! STRIP(X)){ \
2989 if((S)){ \
2990 html_output((X), TAG_EMBED); \
2991 html_output((X), TAG_BOLDON); \
2993 else if(!(S)){ \
2994 html_output((X), TAG_EMBED); \
2995 html_output((X), TAG_BOLDOFF); \
2998 #define HTML_ULINE(X, S) \
2999 if(! STRIP(X)){ \
3000 if((S)){ \
3001 html_output((X), TAG_EMBED); \
3002 html_output((X), TAG_ULINEON); \
3004 else if(!(S)){ \
3005 html_output((X), TAG_EMBED); \
3006 html_output((X), TAG_ULINEOFF); \
3009 #define HTML_ITALIC(X, S) \
3010 if(! STRIP(X)){ \
3011 if(S){ \
3012 html_output((X), TAG_EMBED); \
3013 html_output((X), TAG_ITALICON); \
3015 else if(!(S)){ \
3016 html_output((X), TAG_EMBED); \
3017 html_output((X), TAG_ITALICOFF); \
3020 #define HTML_STRIKE(X, S) \
3021 if(! STRIP(X)){ \
3022 if(S){ \
3023 html_output((X), TAG_EMBED); \
3024 html_output((X), TAG_STRIKEON); \
3026 else if(!(S)){ \
3027 html_output((X), TAG_EMBED); \
3028 html_output((X), TAG_STRIKEOFF); \
3031 #define HTML_BIG(X, S) \
3032 if(! STRIP(X)){ \
3033 if(S){ \
3034 html_output((X), TAG_EMBED); \
3035 html_output((X), TAG_BIGON); \
3037 else if(!(S)){ \
3038 html_output((X), TAG_EMBED); \
3039 html_output((X), TAG_BIGOFF); \
3042 #define HTML_SMALL(X, S) \
3043 if(! STRIP(X)){ \
3044 if(S){ \
3045 html_output((X), TAG_EMBED); \
3046 html_output((X), TAG_SMALLON); \
3048 else if(!(S)){ \
3049 html_output((X), TAG_EMBED); \
3050 html_output((X), TAG_SMALLOFF); \
3053 #define WRAPPED_LEN(X) ((HD(f)->centered) \
3054 ? (HD(f)->centered->line.width \
3055 + HD(f)->centered->word.width \
3056 + ((HD(f)->centered->line.width \
3057 && HD(f)->centered->word.width) \
3058 ? 1 : 0)) \
3059 : 0)
3060 #define HTML_DUMP_LIT(F, S, L) { \
3061 int i, c; \
3062 for(i = 0; i < (L); i++){ \
3063 c = ASCII_ISSPACE((unsigned char)(S)[i]) \
3064 ? (S)[i] \
3065 : MAKE_LITERAL((S)[i]); \
3066 HTML_TEXT(F, c); \
3069 #define HTML_PROC(F, C) { \
3070 if(HD(F)->token){ \
3071 int i; \
3072 if((i = (*(HD(F)->token))(F, C)) != 0){ \
3073 if(i < 0){ \
3074 HTML_DUMP_LIT(F, "<", 1); \
3075 if(HD(F)->el_data->element){ \
3076 HTML_DUMP_LIT(F, \
3077 HD(F)->el_data->element, \
3078 strlen(HD(F)->el_data->element));\
3080 if(HD(F)->el_data->len){ \
3081 HTML_DUMP_LIT(F, \
3082 HD(F)->el_data->buf, \
3083 HD(F)->el_data->len); \
3085 HTML_TEXT(F, C); \
3087 FREE_CLCTR(F); \
3090 else if((C) == '<'){ \
3091 NEW_CLCTR(F); \
3093 else \
3094 HTML_TEXT(F, C); \
3096 #define HTML_LINEP_PUTC(F, C) { \
3097 if((F)->linep - (F)->line >= (HD(F)->line_bufsize - 1)){ \
3098 size_t offset = (F)->linep - (F)->line; \
3099 fs_resize((void **) &(F)->line, \
3100 (HD(F)->line_bufsize * 2) * sizeof(char)); \
3101 HD(F)->line_bufsize *= 2; \
3102 (F)->linep = &(F)->line[offset]; \
3104 *(F)->linep++ = (C); \
3106 #define HTML_TEXT(F, C) switch((F)->f1){ \
3107 case WSPACE : \
3108 if(HTML_ISSPACE(C)) /* ignore repeated WS */ \
3109 break; \
3110 HTML_TEXT_OUT(F, ' '); \
3111 (F)->f1 = DFL;/* stop sending chars here */ \
3112 /* fall thru to process 'c' */ \
3113 case DFL: \
3114 if(HD(F)->bitbucket) \
3115 (F)->f1 = DFL; /* no op */ \
3116 else if(HTML_ISSPACE(C) && HD(F)->wrapstate) \
3117 (F)->f1 = WSPACE;/* coalesce white space */ \
3118 else HTML_TEXT_OUT(F, C); \
3119 break; \
3121 #define HTML_TEXT_OUT(F, C) if(HANDLERS(F)) /* let handlers see C */ \
3122 (*EL(HANDLERS(F))->handler)(HANDLERS(F),(C),GF_DATA); \
3123 else \
3124 html_output(F, C);
3125 #ifdef DEBUG
3126 #define HTML_DEBUG_EL(S, D) { \
3127 dprint((5, "-- html %s: %s\n", \
3128 S ? S : "?", \
3129 (D)->element \
3130 ? (D)->element : "NULL")); \
3131 if(debug > 5){ \
3132 PARAMETER *p; \
3133 for(p = (D)->attribs; \
3134 p && p->attribute; \
3135 p = p->next) \
3136 dprint((6, \
3137 " PARM: %s%s%s\n", \
3138 p->attribute \
3139 ? p->attribute : "NULL",\
3140 p->value ? "=" : "", \
3141 p->value ? p->value : ""));\
3144 #else
3145 #define HTML_DEBUG_EL(S, D)
3146 #endif
3148 #ifndef SYSTEM_PINE_INFO_PATH
3149 #define SYSTEM_PINE_INFO_PATH "/usr/local/lib/pine.info"
3150 #endif
3151 #define CHTML_VAR_EXPAND(S) (!strcmp(S, "PINE_INFO_PATH") \
3152 ? SYSTEM_PINE_INFO_PATH : S)
3155 * Protos for Tag handlers
3157 int html_head(HANDLER_S *, int, int);
3158 int html_base(HANDLER_S *, int, int);
3159 int html_title(HANDLER_S *, int, int);
3160 int html_body(HANDLER_S *, int, int);
3161 int html_a(HANDLER_S *, int, int);
3162 int html_br(HANDLER_S *, int, int);
3163 int html_hr(HANDLER_S *, int, int);
3164 int html_p(HANDLER_S *, int, int);
3165 int html_table(HANDLER_S *, int, int);
3166 int html_caption(HANDLER_S *, int, int);
3167 int html_tr(HANDLER_S *, int, int);
3168 int html_td(HANDLER_S *, int, int);
3169 int html_th(HANDLER_S *, int, int);
3170 int html_thead(HANDLER_S *, int, int);
3171 int html_tbody(HANDLER_S *, int, int);
3172 int html_tfoot(HANDLER_S *, int, int);
3173 int html_col(HANDLER_S *, int, int);
3174 int html_colgroup(HANDLER_S *, int, int);
3175 int html_b(HANDLER_S *, int, int);
3176 int html_u(HANDLER_S *, int, int);
3177 int html_i(HANDLER_S *, int, int);
3178 int html_em(HANDLER_S *, int, int);
3179 int html_strong(HANDLER_S *, int, int);
3180 int html_s(HANDLER_S *, int, int);
3181 int html_big(HANDLER_S *, int, int);
3182 int html_small(HANDLER_S *, int, int);
3183 int html_font(HANDLER_S *, int, int);
3184 int html_img(HANDLER_S *, int, int);
3185 int html_map(HANDLER_S *, int, int);
3186 int html_area(HANDLER_S *, int, int);
3187 int html_form(HANDLER_S *, int, int);
3188 int html_input(HANDLER_S *, int, int);
3189 int html_option(HANDLER_S *, int, int);
3190 int html_optgroup(HANDLER_S *, int, int);
3191 int html_button(HANDLER_S *, int, int);
3192 int html_select(HANDLER_S *, int, int);
3193 int html_textarea(HANDLER_S *, int, int);
3194 int html_label(HANDLER_S *, int, int);
3195 int html_fieldset(HANDLER_S *, int, int);
3196 int html_ul(HANDLER_S *, int, int);
3197 int html_ol(HANDLER_S *, int, int);
3198 int html_menu(HANDLER_S *, int, int);
3199 int html_dir(HANDLER_S *, int, int);
3200 int html_li(HANDLER_S *, int, int);
3201 int html_h1(HANDLER_S *, int, int);
3202 int html_h2(HANDLER_S *, int, int);
3203 int html_h3(HANDLER_S *, int, int);
3204 int html_h4(HANDLER_S *, int, int);
3205 int html_h5(HANDLER_S *, int, int);
3206 int html_h6(HANDLER_S *, int, int);
3207 int html_blockquote(HANDLER_S *, int, int);
3208 int html_address(HANDLER_S *, int, int);
3209 int html_pre(HANDLER_S *, int, int);
3210 int html_center(HANDLER_S *, int, int);
3211 int html_div(HANDLER_S *, int, int);
3212 int html_span(HANDLER_S *, int, int);
3213 int html_dl(HANDLER_S *, int, int);
3214 int html_dt(HANDLER_S *, int, int);
3215 int html_dd(HANDLER_S *, int, int);
3216 int html_script(HANDLER_S *, int, int);
3217 int html_applet(HANDLER_S *, int, int);
3218 int html_style(HANDLER_S *, int, int);
3219 int html_kbd(HANDLER_S *, int, int);
3220 int html_dfn(HANDLER_S *, int, int);
3221 int html_var(HANDLER_S *, int, int);
3222 int html_tt(HANDLER_S *, int, int);
3223 int html_samp(HANDLER_S *, int, int);
3224 int html_sub(HANDLER_S *, int, int);
3225 int html_sup(HANDLER_S *, int, int);
3226 int html_cite(HANDLER_S *, int, int);
3227 int html_code(HANDLER_S *, int, int);
3228 int html_ins(HANDLER_S *, int, int);
3229 int html_del(HANDLER_S *, int, int);
3230 int html_abbr(HANDLER_S *, int, int);
3231 char *cid_tempfile_name(unsigned char *, long, int *);
3234 * Protos for RSS 2.0 Tag handlers
3236 int rss_rss(HANDLER_S *, int, int);
3237 int rss_channel(HANDLER_S *, int, int);
3238 int rss_title(HANDLER_S *, int, int);
3239 int rss_image(HANDLER_S *, int, int);
3240 int rss_link(HANDLER_S *, int, int);
3241 int rss_description(HANDLER_S *, int, int);
3242 int rss_ttl(HANDLER_S *, int, int);
3243 int rss_item(HANDLER_S *, int, int);
3246 * Proto's for support routines
3248 void html_pop(FILTER_S *, ELPROP_S *);
3249 int html_push(FILTER_S *, ELPROP_S *);
3250 int html_element_collector(FILTER_S *, int);
3251 int html_element_flush(CLCTR_S *);
3252 void html_element_comment(FILTER_S *, char *);
3253 void html_element_output(FILTER_S *, int);
3254 int html_entity_collector(FILTER_S *, int, UCS *, char **);
3255 void html_a_prefix(FILTER_S *);
3256 void html_a_finish(HANDLER_S *);
3257 void html_a_output_prefix(FILTER_S *, int);
3258 void html_a_output_info(HANDLER_S *);
3259 void html_a_relative(char *, char *, HANDLE_S *);
3260 int html_href_relative(char *);
3261 int html_indent(FILTER_S *, int, int);
3262 void html_blank(FILTER_S *, int);
3263 void html_newline(FILTER_S *);
3264 void html_output(FILTER_S *, int);
3265 void html_output_string(FILTER_S *, char *);
3266 void html_output_raw_tag(FILTER_S *, char *);
3267 void html_output_normal(FILTER_S *, int, int, int);
3268 void html_output_flush(FILTER_S *);
3269 void html_output_centered(FILTER_S *, int, int, int);
3270 void html_centered_handle(int *, char *, int);
3271 void html_centered_putc(WRAPLINE_S *, int);
3272 void html_centered_flush(FILTER_S *);
3273 void html_centered_flush_line(FILTER_S *);
3274 void html_write_anchor(FILTER_S *, int);
3275 void html_write_newline(FILTER_S *);
3276 void html_write_indent(FILTER_S *, int);
3277 void html_write(FILTER_S *, char *, int);
3278 void html_putc(FILTER_S *, int);
3279 int html_event_attribute(char *);
3280 char *rss_skip_whitespace(char *s);
3281 ELPROP_S *element_properties(FILTER_S *, char *);
3285 * Named entity table -- most from HTML 2.0 (rfc1866) plus some from
3286 * W3C doc "Additional named entities for HTML"
3288 static struct html_entities {
3289 char *name; /* entity name */
3290 UCS value; /* UCS entity value */
3291 char *plain; /* US-ASCII representation */
3292 } entity_tab[] = {
3293 {"quot", 0x0022}, /* 34 - quotation mark */
3294 {"amp", 0x0026}, /* 38 - ampersand */
3295 {"apos", 0x0027}, /* 39 - apostrophe */
3296 {"lt", 0x003C}, /* 60 - less-than sign */
3297 {"gt", 0x003E}, /* 62 - greater-than sign */
3298 {"nbsp", 0x00A0, " "}, /* 160 - no-break space */
3299 {"iexcl", 0x00A1}, /* 161 - inverted exclamation mark */
3300 {"cent", 0x00A2}, /* 162 - cent sign */
3301 {"pound", 0x00A3}, /* 163 - pound sign */
3302 {"curren", 0x00A4, "CUR"}, /* 164 - currency sign */
3303 {"yen", 0x00A5}, /* 165 - yen sign */
3304 {"brvbar", 0x00A6, "|"}, /* 166 - broken bar */
3305 {"sect", 0x00A7}, /* 167 - section sign */
3306 {"uml", 0x00A8, "\""}, /* 168 - diaeresis */
3307 {"copy", 0x00A9, "(C)"}, /* 169 - copyright sign */
3308 {"ordf", 0x00AA, "a"}, /* 170 - feminine ordinal indicator */
3309 {"laquo", 0x00AB, "<<"}, /* 171 - left-pointing double angle quotation mark */
3310 {"not", 0x00AC, "NOT"}, /* 172 - not sign */
3311 {"shy", 0x00AD, "-"}, /* 173 - soft hyphen */
3312 {"reg", 0x00AE, "(R)"}, /* 174 - registered sign */
3313 {"macr", 0x00AF}, /* 175 - macron */
3314 {"deg", 0x00B0, "DEG"}, /* 176 - degree sign */
3315 {"plusmn", 0x00B1, "+/-"}, /* 177 - plus-minus sign */
3316 {"sup2", 0x00B2}, /* 178 - superscript two */
3317 {"sup3", 0x00B3}, /* 179 - superscript three */
3318 {"acute", 0x00B4, "'"}, /* 180 - acute accent */
3319 {"micro", 0x00B5}, /* 181 - micro sign */
3320 {"para", 0x00B6}, /* 182 - pilcrow sign */
3321 {"middot", 0x00B7}, /* 183 - middle dot */
3322 {"cedil", 0x00B8}, /* 184 - cedilla */
3323 {"sup1", 0x00B9}, /* 185 - superscript one */
3324 {"ordm", 0x00BA, "o"}, /* 186 - masculine ordinal indicator */
3325 {"raquo", 0x00BB, ">>"}, /* 187 - right-pointing double angle quotation mark */
3326 {"frac14", 0x00BC, " 1/4"}, /* 188 - vulgar fraction one quarter */
3327 {"frac12", 0x00BD, " 1/2"}, /* 189 - vulgar fraction one half */
3328 {"frac34", 0x00BE, " 3/4"}, /* 190 - vulgar fraction three quarters */
3329 {"iquest", 0x00BF}, /* 191 - inverted question mark */
3330 {"Agrave", 0x00C0, "A"}, /* 192 - latin capital letter a with grave */
3331 {"Aacute", 0x00C1, "A"}, /* 193 - latin capital letter a with acute */
3332 {"Acirc", 0x00C2, "A"}, /* 194 - latin capital letter a with circumflex */
3333 {"Atilde", 0x00C3, "A"}, /* 195 - latin capital letter a with tilde */
3334 {"Auml", 0x00C4, "AE"}, /* 196 - latin capital letter a with diaeresis */
3335 {"Aring", 0x00C5, "A"}, /* 197 - latin capital letter a with ring above */
3336 {"AElig", 0x00C6, "AE"}, /* 198 - latin capital letter ae */
3337 {"Ccedil", 0x00C7, "C"}, /* 199 - latin capital letter c with cedilla */
3338 {"Egrave", 0x00C8, "E"}, /* 200 - latin capital letter e with grave */
3339 {"Eacute", 0x00C9, "E"}, /* 201 - latin capital letter e with acute */
3340 {"Ecirc", 0x00CA, "E"}, /* 202 - latin capital letter e with circumflex */
3341 {"Euml", 0x00CB, "E"}, /* 203 - latin capital letter e with diaeresis */
3342 {"Igrave", 0x00CC, "I"}, /* 204 - latin capital letter i with grave */
3343 {"Iacute", 0x00CD, "I"}, /* 205 - latin capital letter i with acute */
3344 {"Icirc", 0x00CE, "I"}, /* 206 - latin capital letter i with circumflex */
3345 {"Iuml", 0x00CF, "I"}, /* 207 - latin capital letter i with diaeresis */
3346 {"ETH", 0x00D0, "DH"}, /* 208 - latin capital letter eth */
3347 {"Ntilde", 0x00D1, "N"}, /* 209 - latin capital letter n with tilde */
3348 {"Ograve", 0x00D2, "O"}, /* 210 - latin capital letter o with grave */
3349 {"Oacute", 0x00D3, "O"}, /* 211 - latin capital letter o with acute */
3350 {"Ocirc", 0x00D4, "O"}, /* 212 - latin capital letter o with circumflex */
3351 {"Otilde", 0x00D5, "O"}, /* 213 - latin capital letter o with tilde */
3352 {"Ouml", 0x00D6, "O"}, /* 214 - latin capital letter o with diaeresis */
3353 {"times", 0x00D7, "x"}, /* 215 - multiplication sign */
3354 {"Oslash", 0x00D8, "O"}, /* 216 - latin capital letter o with stroke */
3355 {"Ugrave", 0x00D9, "U"}, /* 217 - latin capital letter u with grave */
3356 {"Uacute", 0x00DA, "U"}, /* 218 - latin capital letter u with acute */
3357 {"Ucirc", 0x00DB, "U"}, /* 219 - latin capital letter u with circumflex */
3358 {"Uuml", 0x00DC, "UE"}, /* 220 - latin capital letter u with diaeresis */
3359 {"Yacute", 0x00DD, "Y"}, /* 221 - latin capital letter y with acute */
3360 {"THORN", 0x00DE, "P"}, /* 222 - latin capital letter thorn */
3361 {"szlig", 0x00DF, "ss"}, /* 223 - latin small letter sharp s (German <a href="/wiki/Eszett" title="Eszett">Eszett</a>) */
3362 {"agrave", 0x00E0, "a"}, /* 224 - latin small letter a with grave */
3363 {"aacute", 0x00E1, "a"}, /* 225 - latin small letter a with acute */
3364 {"acirc", 0x00E2, "a"}, /* 226 - latin small letter a with circumflex */
3365 {"atilde", 0x00E3, "a"}, /* 227 - latin small letter a with tilde */
3366 {"auml", 0x00E4, "ae"}, /* 228 - latin small letter a with diaeresis */
3367 {"aring", 0x00E5, "a"}, /* 229 - latin small letter a with ring above */
3368 {"aelig", 0x00E6, "ae"}, /* 230 - latin lowercase ligature ae */
3369 {"ccedil", 0x00E7, "c"}, /* 231 - latin small letter c with cedilla */
3370 {"egrave", 0x00E8, "e"}, /* 232 - latin small letter e with grave */
3371 {"eacute", 0x00E9, "e"}, /* 233 - latin small letter e with acute */
3372 {"ecirc", 0x00EA, "e"}, /* 234 - latin small letter e with circumflex */
3373 {"euml", 0x00EB, "e"}, /* 235 - latin small letter e with diaeresis */
3374 {"igrave", 0x00EC, "i"}, /* 236 - latin small letter i with grave */
3375 {"iacute", 0x00ED, "i"}, /* 237 - latin small letter i with acute */
3376 {"icirc", 0x00EE, "i"}, /* 238 - latin small letter i with circumflex */
3377 {"iuml", 0x00EF, "i"}, /* 239 - latin small letter i with diaeresis */
3378 {"eth", 0x00F0, "dh"}, /* 240 - latin small letter eth */
3379 {"ntilde", 0x00F1, "n"}, /* 241 - latin small letter n with tilde */
3380 {"ograve", 0x00F2, "o"}, /* 242 - latin small letter o with grave */
3381 {"oacute", 0x00F3, "o"}, /* 243 - latin small letter o with acute */
3382 {"ocirc", 0x00F4, "o"}, /* 244 - latin small letter o with circumflex */
3383 {"otilde", 0x00F5, "o"}, /* 245 - latin small letter o with tilde */
3384 {"ouml", 0x00F6, "oe"}, /* 246 - latin small letter o with diaeresis */
3385 {"divide", 0x00F7, "/"}, /* 247 - division sign */
3386 {"oslash", 0x00F8, "o"}, /* 248 - latin small letter o with stroke */
3387 {"ugrave", 0x00F9, "u"}, /* 249 - latin small letter u with grave */
3388 {"uacute", 0x00FA, "u"}, /* 250 - latin small letter u with acute */
3389 {"ucirc", 0x00FB, "u"}, /* 251 - latin small letter u with circumflex */
3390 {"uuml", 0x00FC, "ue"}, /* 252 - latin small letter u with diaeresis */
3391 {"yacute", 0x00FD, "y"}, /* 253 - latin small letter y with acute */
3392 {"thorn", 0x00FE, "p"}, /* 254 - latin small letter thorn */
3393 {"yuml", 0x00FF, "y"}, /* 255 - latin small letter y with diaeresis */
3394 {"OElig", 0x0152, "OE"}, /* 338 - latin capital ligature oe */
3395 {"oelig", 0x0153, "oe"}, /* 339 - latin small ligature oe */
3396 {"Scaron", 0x0160, "S"}, /* 352 - latin capital letter s with caron */
3397 {"scaron", 0x0161, "s"}, /* 353 - latin small letter s with caron */
3398 {"Yuml", 0x0178, "Y"}, /* 376 - latin capital letter y with diaeresis */
3399 {"fnof", 0x0192, "f"}, /* 402 - latin small letter f with hook */
3400 {"circ", 0x02C6}, /* 710 - modifier letter circumflex accent */
3401 {"tilde", 0x02DC, "~"}, /* 732 - small tilde */
3402 {"Alpha", 0x0391}, /* 913 - greek capital letter alpha */
3403 {"Beta", 0x0392}, /* 914 - greek capital letter beta */
3404 {"Gamma", 0x0393}, /* 915 - greek capital letter gamma */
3405 {"Delta", 0x0394}, /* 916 - greek capital letter delta */
3406 {"Epsilon", 0x0395}, /* 917 - greek capital letter epsilon */
3407 {"Zeta", 0x0396}, /* 918 - greek capital letter zeta */
3408 {"Eta", 0x0397}, /* 919 - greek capital letter eta */
3409 {"Theta", 0x0398}, /* 920 - greek capital letter theta */
3410 {"Iota", 0x0399}, /* 921 - greek capital letter iota */
3411 {"Kappa", 0x039A}, /* 922 - greek capital letter kappa */
3412 {"Lambda", 0x039B}, /* 923 - greek capital letter lamda */
3413 {"Mu", 0x039C}, /* 924 - greek capital letter mu */
3414 {"Nu", 0x039D}, /* 925 - greek capital letter nu */
3415 {"Xi", 0x039E}, /* 926 - greek capital letter xi */
3416 {"Omicron", 0x039F}, /* 927 - greek capital letter omicron */
3417 {"Pi", 0x03A0}, /* 928 - greek capital letter pi */
3418 {"Rho", 0x03A1}, /* 929 - greek capital letter rho */
3419 {"Sigma", 0x03A3}, /* 931 - greek capital letter sigma */
3420 {"Tau", 0x03A4}, /* 932 - greek capital letter tau */
3421 {"Upsilon", 0x03A5}, /* 933 - greek capital letter upsilon */
3422 {"Phi", 0x03A6}, /* 934 - greek capital letter phi */
3423 {"Chi", 0x03A7}, /* 935 - greek capital letter chi */
3424 {"Psi", 0x03A8}, /* 936 - greek capital letter psi */
3425 {"Omega", 0x03A9}, /* 937 - greek capital letter omega */
3426 {"alpha", 0x03B1}, /* 945 - greek small letter alpha */
3427 {"beta", 0x03B2}, /* 946 - greek small letter beta */
3428 {"gamma", 0x03B3}, /* 947 - greek small letter gamma */
3429 {"delta", 0x03B4}, /* 948 - greek small letter delta */
3430 {"epsilon", 0x03B5}, /* 949 - greek small letter epsilon */
3431 {"zeta", 0x03B6}, /* 950 - greek small letter zeta */
3432 {"eta", 0x03B7}, /* 951 - greek small letter eta */
3433 {"theta", 0x03B8}, /* 952 - greek small letter theta */
3434 {"iota", 0x03B9}, /* 953 - greek small letter iota */
3435 {"kappa", 0x03BA}, /* 954 - greek small letter kappa */
3436 {"lambda", 0x03BB}, /* 955 - greek small letter lamda */
3437 {"mu", 0x03BC}, /* 956 - greek small letter mu */
3438 {"nu", 0x03BD}, /* 957 - greek small letter nu */
3439 {"xi", 0x03BE}, /* 958 - greek small letter xi */
3440 {"omicron", 0x03BF}, /* 959 - greek small letter omicron */
3441 {"pi", 0x03C0}, /* 960 - greek small letter pi */
3442 {"rho", 0x03C1}, /* 961 - greek small letter rho */
3443 {"sigmaf", 0x03C2}, /* 962 - greek small letter final sigma */
3444 {"sigma", 0x03C3}, /* 963 - greek small letter sigma */
3445 {"tau", 0x03C4}, /* 964 - greek small letter tau */
3446 {"upsilon", 0x03C5}, /* 965 - greek small letter upsilon */
3447 {"phi", 0x03C6}, /* 966 - greek small letter phi */
3448 {"chi", 0x03C7}, /* 967 - greek small letter chi */
3449 {"psi", 0x03C8}, /* 968 - greek small letter psi */
3450 {"omega", 0x03C9}, /* 969 - greek small letter omega */
3451 {"thetasym", 0x03D1}, /* 977 - greek theta symbol */
3452 {"upsih", 0x03D2}, /* 978 - greek upsilon with hook symbol */
3453 {"piv", 0x03D6}, /* 982 - greek pi symbol */
3454 {"ensp", 0x2002}, /* 8194 - en space */
3455 {"emsp", 0x2003}, /* 8195 - em space */
3456 {"thinsp", 0x2009}, /* 8201 - thin space */
3457 {"zwnj", 0x200C}, /* 8204 - zero width non-joiner */
3458 {"zwj", 0x200D}, /* 8205 - zero width joiner */
3459 {"lrm", 0x200E}, /* 8206 - left-to-right mark */
3460 {"rlm", 0x200F}, /* 8207 - right-to-left mark */
3461 {"ndash", 0x2013}, /* 8211 - en dash */
3462 {"mdash", 0x2014}, /* 8212 - em dash */
3463 {"#8213", 0x2015, "--"}, /* 2015 - horizontal bar */
3464 {"#8214", 0x2016, "||"}, /* 2016 - double vertical line */
3465 {"#8215", 0x2017, "__"}, /* 2017 - double low line */
3466 {"lsquo", 0x2018}, /* 8216 - left single quotation mark */
3467 {"rsquo", 0x2019}, /* 8217 - right single quotation mark */
3468 {"sbquo", 0x201A}, /* 8218 - single low-9 quotation mark */
3469 {"ldquo", 0x201C}, /* 8220 - left double quotation mark */
3470 {"rdquo", 0x201D}, /* 8221 - right double quotation mark */
3471 {"bdquo", 0x201E, ",,"}, /* 8222 - double low-9 quotation mark */
3472 {"#8223", 0x201F, "``"}, /* 201F - double high reversed-9 quotation mark */
3473 {"dagger", 0x2020}, /* 8224 - dagger */
3474 {"Dagger", 0x2021}, /* 8225 - double dagger */
3475 {"bull", 0x2022, "*"}, /* 8226 - bullet */
3476 {"hellip", 0x2026}, /* 8230 - horizontal ellipsis */
3477 {"permil", 0x2030}, /* 8240 - per mille sign */
3478 {"prime", 0x2032, "\'"}, /* 8242 - prime */
3479 {"Prime", 0x2033, "\'\'"}, /* 8243 - double prime */
3480 {"#8244", 0x2034, "\'\'\'"}, /* 2034 - triple prime */
3481 {"lsaquo", 0x2039}, /* 8249 - single left-pointing angle quotation mark */
3482 {"rsaquo", 0x203A}, /* 8250 - single right-pointing angle quotation mark */
3483 {"#8252", 0x203C, "!!"}, /* 203C - double exclamation mark */
3484 {"oline", 0x203E, "-"}, /* 8254 - overline */
3485 {"frasl", 0x2044}, /* 8260 - fraction slash */
3486 {"#8263", 0x2047, "??"}, /* 2047 - double question mark */
3487 {"#8264", 0x2048, "?!"}, /* 2048 - question exclamation mark */
3488 {"#8265", 0x2049, "!?"}, /* 2049 - exclamation question mark */
3489 {"#8279", 0x2057, "\'\'\'\'"}, /* 2057 - quad prime */
3490 {"euro", 0x20AC, "EUR"}, /* 8364 - euro sign */
3491 {"image", 0x2111}, /* 8465 - black-letter capital i */
3492 {"weierp", 0x2118}, /* 8472 - script capital p (<a href="/wiki/Weierstrass" title="Weierstrass">Weierstrass</a> p) */
3493 {"real", 0x211C}, /* 8476 - black-letter capital r */
3494 {"trade", 0x2122, "[tm]"}, /* 8482 - trademark sign */
3495 {"alefsym", 0x2135}, /* 8501 - alef symbol */
3496 {"larr", 0x2190}, /* 8592 - leftwards arrow */
3497 {"uarr", 0x2191}, /* 8593 - upwards arrow */
3498 {"rarr", 0x2192}, /* 8594 - rightwards arrow */
3499 {"darr", 0x2193}, /* 8595 - downwards arrow */
3500 {"harr", 0x2194}, /* 8596 - left right arrow */
3501 {"crarr", 0x21B5}, /* 8629 - downwards arrow with corner leftwards */
3502 {"lArr", 0x21D0}, /* 8656 - leftwards double arrow */
3503 {"uArr", 0x21D1}, /* 8657 - upwards double arrow */
3504 {"rArr", 0x21D2}, /* 8658 - rightwards double arrow */
3505 {"dArr", 0x21D3}, /* 8659 - downwards double arrow */
3506 {"hArr", 0x21D4}, /* 8660 - left right double arrow */
3507 {"forall", 0x2200}, /* 8704 - for all */
3508 {"part", 0x2202}, /* 8706 - partial differential */
3509 {"exist", 0x2203}, /* 8707 - there exists */
3510 {"empty", 0x2205}, /* 8709 - empty set */
3511 {"nabla", 0x2207}, /* 8711 - nabla */
3512 {"isin", 0x2208}, /* 8712 - element of */
3513 {"notin", 0x2209}, /* 8713 - not an element of */
3514 {"ni", 0x220B}, /* 8715 - contains as member */
3515 {"prod", 0x220F}, /* 8719 - n-ary product */
3516 {"sum", 0x2211}, /* 8721 - n-ary summation */
3517 {"minus", 0x2212}, /* 8722 - minus sign */
3518 {"lowast", 0x2217}, /* 8727 - asterisk operator */
3519 {"radic", 0x221A}, /* 8730 - square root */
3520 {"prop", 0x221D}, /* 8733 - proportional to */
3521 {"infin", 0x221E}, /* 8734 - infinity */
3522 {"ang", 0x2220}, /* 8736 - angle */
3523 {"and", 0x2227}, /* 8743 - logical and */
3524 {"or", 0x2228}, /* 8744 - logical or */
3525 {"cap", 0x2229}, /* 8745 - intersection */
3526 {"cup", 0x222A}, /* 8746 - union */
3527 {"int", 0x222B}, /* 8747 - integral */
3528 {"there4", 0x2234}, /* 8756 - therefore */
3529 {"sim", 0x223C}, /* 8764 - tilde operator */
3530 {"cong", 0x2245}, /* 8773 - congruent to */
3531 {"asymp", 0x2248}, /* 8776 - almost equal to */
3532 {"ne", 0x2260}, /* 8800 - not equal to */
3533 {"equiv", 0x2261}, /* 8801 - identical to (equivalent to) */
3534 {"le", 0x2264}, /* 8804 - less-than or equal to */
3535 {"ge", 0x2265}, /* 8805 - greater-than or equal to */
3536 {"sub", 0x2282}, /* 8834 - subset of */
3537 {"sup", 0x2283}, /* 8835 - superset of */
3538 {"nsub", 0x2284}, /* 8836 - not a subset of */
3539 {"sube", 0x2286}, /* 8838 - subset of or equal to */
3540 {"supe", 0x2287}, /* 8839 - superset of or equal to */
3541 {"oplus", 0x2295}, /* 8853 - circled plus */
3542 {"otimes", 0x2297}, /* 8855 - circled times */
3543 {"perp", 0x22A5}, /* 8869 - up tack */
3544 {"sdot", 0x22C5}, /* 8901 - dot operator */
3545 {"lceil", 0x2308}, /* 8968 - left ceiling */
3546 {"rceil", 0x2309}, /* 8969 - right ceiling */
3547 {"lfloor", 0x230A}, /* 8970 - left floor */
3548 {"rfloor", 0x230B}, /* 8971 - right floor */
3549 {"lang", 0x2329}, /* 9001 - left-pointing angle bracket */
3550 {"rang", 0x232A}, /* 9002 - right-pointing angle bracket */
3551 {"loz", 0x25CA}, /* 9674 - lozenge */
3552 {"spades", 0x2660}, /* 9824 - black spade suit */
3553 {"clubs", 0x2663}, /* 9827 - black club suit */
3554 {"hearts", 0x2665}, /* 9829 - black heart suit */
3555 {"diams", 0x2666} /* 9830 - black diamond suit */
3560 * Table of supported elements and corresponding handlers
3562 static ELPROP_S html_element_table[] = {
3563 {"HTML", 4}, /* HTML ignore if seen? */
3564 {"HEAD", 4, html_head}, /* slurp until <BODY> ? */
3565 {"TITLE", 5, html_title}, /* Document Title */
3566 {"BASE", 4, html_base}, /* HREF base */
3567 {"BODY", 4, html_body}, /* HTML BODY */
3568 {"A", 1, html_a}, /* Anchor */
3569 {"ABBR", 4, html_abbr}, /* Abbreviation */
3570 {"IMG", 3, html_img}, /* Image */
3571 {"MAP", 3, html_map}, /* Image Map */
3572 {"AREA", 4, html_area}, /* Image Map Area */
3573 {"HR", 2, html_hr, 1, 1}, /* Horizontal Rule */
3574 {"BR", 2, html_br, 0, 1}, /* Line Break */
3575 {"P", 1, html_p, 1}, /* Paragraph */
3576 {"OL", 2, html_ol, 1}, /* Ordered List */
3577 {"UL", 2, html_ul, 1}, /* Unordered List */
3578 {"MENU", 4, html_menu}, /* Menu List */
3579 {"DIR", 3, html_dir}, /* Directory List */
3580 {"LI", 2, html_li}, /* ... List Item */
3581 {"DL", 2, html_dl, 1}, /* Definition List */
3582 {"DT", 2, html_dt}, /* ... Def. Term */
3583 {"DD", 2, html_dd}, /* ... Def. Definition */
3584 {"I", 1, html_i}, /* Italic Text */
3585 {"EM", 2, html_em}, /* Typographic Emphasis */
3586 {"STRONG", 6, html_strong}, /* STRONG Typo Emphasis */
3587 {"VAR", 3, html_i}, /* Variable Name */
3588 {"B", 1, html_b}, /* Bold Text */
3589 {"U", 1, html_u}, /* Underline Text */
3590 {"S", 1, html_s}, /* Strike-Through Text */
3591 {"STRIKE", 6, html_s}, /* Strike-Through Text */
3592 {"BIG", 3, html_big}, /* Big Font Text */
3593 {"SMALL", 5, html_small}, /* Small Font Text */
3594 {"FONT", 4, html_font}, /* Font display directives */
3595 {"BLOCKQUOTE", 10, html_blockquote, 1}, /* Blockquote */
3596 {"ADDRESS", 7, html_address, 1}, /* Address */
3597 {"CENTER", 6, html_center}, /* Centered Text v3.2 */
3598 {"DIV", 3, html_div, 1}, /* Document Division 3.2 */
3599 {"SPAN", 4, html_span}, /* Text Span */
3600 {"H1", 2, html_h1, 1}, /* Headings... */
3601 {"H2", 2, html_h2, 1},
3602 {"H3", 2, html_h3,1},
3603 {"H4", 2, html_h4, 1},
3604 {"H5", 2, html_h5, 1},
3605 {"H6", 2, html_h6, 1},
3606 {"PRE", 3, html_pre, 1}, /* Preformatted Text */
3607 {"KBD", 3, html_kbd}, /* Keyboard Input (NO OP) */
3608 {"DFN", 3, html_dfn}, /* Definition (NO OP) */
3609 {"VAR", 3, html_var}, /* Variable (NO OP) */
3610 {"TT", 2, html_tt}, /* Typetype (NO OP) */
3611 {"SAMP", 4, html_samp}, /* Sample Text (NO OP) */
3612 {"CITE", 4, html_cite}, /* Citation (NO OP) */
3613 {"CODE", 4, html_code}, /* Code Text (NO OP) */
3614 {"INS", 3, html_ins}, /* Text Inserted (NO OP) */
3615 {"DEL", 3, html_del}, /* Text Deleted (NO OP) */
3616 {"SUP", 3, html_sup}, /* Text Superscript (NO OP) */
3617 {"SUB", 3, html_sub}, /* Text Superscript (NO OP) */
3618 {"STYLE", 5, html_style}, /* CSS Definitions */
3620 /*----- Handlers below UNIMPLEMENTED (and won't until later) -----*/
3622 {"FORM", 4, html_form, 1}, /* form within a document */
3623 {"INPUT", 5, html_input}, /* One input field, options */
3624 {"BUTTON", 6, html_button}, /* Push Button */
3625 {"OPTION", 6, html_option}, /* One option within Select */
3626 {"OPTION", 6, html_optgroup}, /* Option Group Definition */
3627 {"SELECT", 6, html_select}, /* Selection from a set */
3628 {"TEXTAREA", 8, html_textarea}, /* A multi-line input field */
3629 {"LABEL", 5, html_label}, /* Control Label */
3630 {"FIELDSET", 8, html_fieldset, 1}, /* Fieldset Control Group */
3632 /*----- Handlers below NEVER TO BE IMPLEMENTED -----*/
3633 {"SCRIPT", 6, html_script}, /* Embedded scripting statements */
3634 {"APPLET", 6, NULL}, /* Embedded applet statements */
3635 {"OBJECT", 6, NULL}, /* Embedded object statements */
3636 {"LINK", 4, NULL}, /* References to external data */
3637 {"PARAM", 5, NULL}, /* Applet/Object parameters */
3639 /*----- Handlers below provide limited support for RFC 1942 Tables -----*/
3641 {"TABLE", 5, html_table, 1}, /* Table */
3642 {"CAPTION", 7, html_caption}, /* Table Caption */
3643 {"TR", 2, html_tr}, /* Table Table Row */
3644 {"TD", 2, html_td}, /* Table Table Data */
3645 {"TH", 2, html_th}, /* Table Table Head */
3646 {"THEAD", 5, html_thead}, /* Table Table Head */
3647 {"TBODY", 5, html_tbody}, /* Table Table Body */
3648 {"TFOOT", 5, html_tfoot}, /* Table Table Foot */
3649 {"COL", 3, html_col}, /* Table Column Attributes */
3650 {"COLGROUP", 8, html_colgroup}, /* Table Column Group Attributes */
3652 {NULL, 0, NULL}
3657 * Table of supported RSS 2.0 elements
3659 static ELPROP_S rss_element_table[] = {
3660 {"RSS", 3, rss_rss}, /* RSS 2.0 version */
3661 {"CHANNEL", 7, rss_channel}, /* RSS 2.0 Channel */
3662 {"TITLE", 5, rss_title}, /* RSS 2.0 Title */
3663 {"IMAGE", 5, rss_image}, /* RSS 2.0 Channel Image */
3664 {"LINK", 4, rss_link}, /* RSS 2.0 Channel/Item Link */
3665 {"DESCRIPTION", 11, rss_description}, /* RSS 2.0 Channel/Item Description */
3666 {"ITEM", 4, rss_item}, /* RSS 2.0 Channel ITEM */
3667 {"TTL", 3, rss_ttl}, /* RSS 2.0 Item TTL */
3668 {NULL, 0, NULL}
3673 * Initialize the given handler, and add it to the stack if it
3674 * requests it.
3676 * Returns: 1 if handler chose to get pushed on stack
3677 * 0 if handler declined
3680 html_push(FILTER_S *fd, ELPROP_S *ep)
3682 HANDLER_S *new;
3684 new = (HANDLER_S *)fs_get(sizeof(HANDLER_S));
3685 memset(new, 0, sizeof(HANDLER_S));
3686 new->html_data = fd;
3687 new->element = ep;
3688 if((*ep->handler)(new, 0, GF_RESET)){ /* stack the handler? */
3689 new->below = HANDLERS(fd);
3690 HANDLERS(fd) = new; /* push */
3691 return(1);
3694 fs_give((void **) &new);
3695 return(0);
3700 * Remove the most recently installed the given handler
3701 * after letting it accept its demise.
3703 void
3704 html_pop(FILTER_S *fd, ELPROP_S *ep)
3706 HANDLER_S *tp;
3708 for(tp = HANDLERS(fd); tp && ep != EL(tp); tp = tp->below){
3709 HANDLER_S *tp2;
3711 dprint((3, "-- html error: bad nesting: given /%s expected /%s", ep->element, EL(tp)->element));
3712 /* if no evidence of opening tag, ignore given closing tag */
3713 for(tp2 = HANDLERS(fd); tp2 && ep != EL(tp2); tp2 = tp2->below)
3716 if(!tp2){
3717 dprint((3, "-- html error: no opening tag for given tag /%s", ep->element));
3718 return;
3721 (void) (*EL(tp)->handler)(tp, 0, GF_EOD);
3722 HANDLERS(fd) = tp->below;
3725 if(tp){
3726 (void) (*EL(tp)->handler)(tp, 0, GF_EOD); /* may adjust handler list */
3727 if(tp != HANDLERS(fd)){
3728 HANDLER_S *p;
3730 for(p = HANDLERS(fd); p->below != tp; p = p->below)
3733 if(p)
3734 p->below = tp->below; /* remove from middle of stack */
3735 /* BUG: else programming botch and we should die */
3737 else
3738 HANDLERS(fd) = tp->below; /* pop */
3740 fs_give((void **)&tp);
3742 else{
3743 /* BUG: should MAKE SURE NOT TO EMIT IT */
3744 dprint((3, "-- html error: end tag without a start: %s", ep->element));
3750 * Deal with data passed a handler in its GF_DATA state
3752 static void
3753 html_handoff(HANDLER_S *hd, int ch)
3755 if(hd->below)
3756 (void) (*EL(hd->below)->handler)(hd->below, ch, GF_DATA);
3757 else
3758 html_output(hd->html_data, ch);
3763 * HTML <BR> element handler
3766 html_br(HANDLER_S *hd, int ch, int cmd)
3768 if(cmd == GF_RESET){
3769 if(PASS_HTML(hd->html_data)){
3770 html_output_raw_tag(hd->html_data, "br");
3772 else{
3773 html_output(hd->html_data, HTML_NEWLINE);
3777 return(0); /* don't get linked */
3782 * HTML <HR> (Horizontal Rule) element handler
3785 html_hr(HANDLER_S *hd, int ch, int cmd)
3787 if(cmd == GF_RESET){
3788 if(PASS_HTML(hd->html_data)){
3789 html_output_raw_tag(hd->html_data, "hr");
3791 else{
3792 int i, old_wrap, width, align;
3793 PARAMETER *p;
3795 width = WRAP_COLS(hd->html_data);
3796 align = 0;
3797 for(p = HD(hd->html_data)->el_data->attribs;
3798 p && p->attribute;
3799 p = p->next)
3800 if(p->value){
3801 if(!strucmp(p->attribute, "ALIGN")){
3802 if(!strucmp(p->value, "LEFT"))
3803 align = 1;
3804 else if(!strucmp(p->value, "RIGHT"))
3805 align = 2;
3807 else if(!strucmp(p->attribute, "WIDTH")){
3808 char *cp;
3810 width = 0;
3811 for(cp = p->value; *cp; cp++)
3812 if(*cp == '%'){
3813 width = (WRAP_COLS(hd->html_data)*MIN(100,width))/100;
3814 break;
3816 else if(isdigit((unsigned char) *cp))
3817 width = (width * 10) + (*cp - '0');
3819 width = MIN(width, WRAP_COLS(hd->html_data));
3823 html_blank(hd->html_data, 1); /* at least one blank line */
3825 old_wrap = HD(hd->html_data)->wrapstate;
3826 HD(hd->html_data)->wrapstate = 0;
3827 if((i = MAX(0, WRAP_COLS(hd->html_data) - width))
3828 && ((align == 0) ? i /= 2 : (align == 2)))
3829 for(; i > 0; i--)
3830 html_output(hd->html_data, ' ');
3832 for(i = 0; i < width; i++)
3833 html_output(hd->html_data, '_');
3835 html_blank(hd->html_data, 1);
3836 HD(hd->html_data)->wrapstate = old_wrap;
3840 return(0); /* don't get linked */
3845 * HTML <P> (paragraph) element handler
3848 html_p(HANDLER_S *hd, int ch, int cmd)
3850 if(cmd == GF_DATA){
3851 html_handoff(hd, ch);
3853 else if(cmd == GF_RESET){
3854 if(PASS_HTML(hd->html_data)){
3855 html_output_raw_tag(hd->html_data, "p");
3857 else{
3858 /* Make sure there's at least 1 blank line */
3859 html_blank(hd->html_data, 1);
3861 /* adjust indent level if needed */
3862 if(HD(hd->html_data)->li_pending){
3863 html_indent(hd->html_data, 4, HTML_ID_INC);
3864 HD(hd->html_data)->li_pending = 0;
3868 else if(cmd == GF_EOD){
3869 if(PASS_HTML(hd->html_data)){
3870 html_output_string(hd->html_data, "</p>");
3872 else{
3873 /* Make sure there's at least 1 blank line */
3874 html_blank(hd->html_data, 1);
3878 return(1); /* GET linked */
3883 * HTML Table <TABLE> (paragraph) table row
3886 html_table(HANDLER_S *hd, int ch, int cmd)
3888 if(cmd == GF_DATA){
3889 if(PASS_HTML(hd->html_data)){
3890 html_handoff(hd, ch);
3893 else if(cmd == GF_RESET){
3894 if(PASS_HTML(hd->html_data)){
3895 html_output_raw_tag(hd->html_data, "table");
3897 else
3898 /* Make sure there's at least 1 blank line */
3899 html_blank(hd->html_data, 0);
3901 else if(cmd == GF_EOD){
3902 if(PASS_HTML(hd->html_data)){
3903 html_output_string(hd->html_data, "</table>");
3905 else
3906 /* Make sure there's at least 1 blank line */
3907 html_blank(hd->html_data, 0);
3909 return(PASS_HTML(hd->html_data)); /* maybe get linked */
3914 * HTML <CAPTION> (Table Caption) element handler
3917 html_caption(HANDLER_S *hd, int ch, int cmd)
3919 if(cmd == GF_DATA){
3920 html_handoff(hd, ch);
3922 else if(cmd == GF_RESET){
3923 if(PASS_HTML(hd->html_data)){
3924 html_output_raw_tag(hd->html_data, "caption");
3926 else{
3927 /* turn ON the centered bit */
3928 CENTER_BIT(hd->html_data) = 1;
3931 else if(cmd == GF_EOD){
3932 if(PASS_HTML(hd->html_data)){
3933 html_output_string(hd->html_data, "</caption>");
3935 else{
3936 /* turn OFF the centered bit */
3937 CENTER_BIT(hd->html_data) = 0;
3941 return(1);
3946 * HTML Table <TR> (paragraph) table row
3949 html_tr(HANDLER_S *hd, int ch, int cmd)
3951 if(cmd == GF_DATA){
3952 if(PASS_HTML(hd->html_data)){
3953 html_handoff(hd, ch);
3956 else if(cmd == GF_RESET){
3957 if(PASS_HTML(hd->html_data)){
3958 html_output_raw_tag(hd->html_data, "tr");
3960 else
3961 /* Make sure there's at least 1 blank line */
3962 html_blank(hd->html_data, 0);
3964 else if(cmd == GF_EOD){
3965 if(PASS_HTML(hd->html_data)){
3966 html_output_string(hd->html_data, "</tr>");
3968 else
3969 /* Make sure there's at least 1 blank line */
3970 html_blank(hd->html_data, 0);
3972 return(PASS_HTML(hd->html_data)); /* maybe get linked */
3977 * HTML Table <TD> (paragraph) table data
3980 html_td(HANDLER_S *hd, int ch, int cmd)
3982 if(cmd == GF_DATA){
3983 if(PASS_HTML(hd->html_data)){
3984 html_handoff(hd, ch);
3987 else if(cmd == GF_RESET){
3988 if(PASS_HTML(hd->html_data)){
3989 html_output_raw_tag(hd->html_data, "td");
3991 else{
3992 PARAMETER *p;
3994 for(p = HD(hd->html_data)->el_data->attribs;
3995 p && p->attribute;
3996 p = p->next)
3997 if(!strucmp(p->attribute, "nowrap")
3998 && (hd->html_data->f2 || hd->html_data->n)){
3999 HTML_DUMP_LIT(hd->html_data, " | ", 3);
4000 break;
4004 else if(cmd == GF_EOD){
4005 if(PASS_HTML(hd->html_data)){
4006 html_output_string(hd->html_data, "</td>");
4010 return(PASS_HTML(hd->html_data)); /* maybe get linked */
4015 * HTML Table <TH> (paragraph) table head
4018 html_th(HANDLER_S *hd, int ch, int cmd)
4020 if(cmd == GF_DATA){
4021 if(PASS_HTML(hd->html_data)){
4022 html_handoff(hd, ch);
4025 else if(cmd == GF_RESET){
4026 if(PASS_HTML(hd->html_data)){
4027 html_output_raw_tag(hd->html_data, "th");
4029 else{
4030 PARAMETER *p;
4032 for(p = HD(hd->html_data)->el_data->attribs;
4033 p && p->attribute;
4034 p = p->next)
4035 if(!strucmp(p->attribute, "nowrap")
4036 && (hd->html_data->f2 || hd->html_data->n)){
4037 HTML_DUMP_LIT(hd->html_data, " | ", 3);
4038 break;
4042 else if(cmd == GF_EOD){
4043 if(PASS_HTML(hd->html_data)){
4044 html_output_string(hd->html_data, "</th>");
4048 return(PASS_HTML(hd->html_data)); /* don't get linked */
4053 * HTML Table <THEAD> table head
4056 html_thead(HANDLER_S *hd, int ch, int cmd)
4058 if(PASS_HTML(hd->html_data)){
4059 if(cmd == GF_DATA){
4060 html_handoff(hd, ch);
4062 else if(cmd == GF_RESET){
4063 html_output_raw_tag(hd->html_data, "thead");
4065 else if(cmd == GF_EOD){
4066 html_output_string(hd->html_data, "</thead>");
4069 return(1); /* GET linked */
4072 return(0); /* don't get linked */
4077 * HTML Table <TBODY> table body
4080 html_tbody(HANDLER_S *hd, int ch, int cmd)
4082 if(PASS_HTML(hd->html_data)){
4083 if(cmd == GF_DATA){
4084 html_handoff(hd, ch);
4086 else if(cmd == GF_RESET){
4087 html_output_raw_tag(hd->html_data, "tbody");
4089 else if(cmd == GF_EOD){
4090 html_output_string(hd->html_data, "</tbody>");
4093 return(1); /* GET linked */
4096 return(0); /* don't get linked */
4101 * HTML Table <TFOOT> table body
4104 html_tfoot(HANDLER_S *hd, int ch, int cmd)
4106 if(PASS_HTML(hd->html_data)){
4107 if(cmd == GF_DATA){
4108 html_handoff(hd, ch);
4110 else if(cmd == GF_RESET){
4111 html_output_raw_tag(hd->html_data, "tfoot");
4113 else if(cmd == GF_EOD){
4114 html_output_string(hd->html_data, "</tfoot>");
4117 return(1); /* GET linked */
4120 return(0); /* don't get linked */
4125 * HTML <COL> (Table Column Attributes) element handler
4128 html_col(HANDLER_S *hd, int ch, int cmd)
4130 if(cmd == GF_RESET){
4131 if(PASS_HTML(hd->html_data)){
4132 html_output_raw_tag(hd->html_data, "col");
4136 return(0); /* don't get linked */
4141 * HTML Table <COLGROUP> table body
4144 html_colgroup(HANDLER_S *hd, int ch, int cmd)
4146 if(PASS_HTML(hd->html_data)){
4147 if(cmd == GF_DATA){
4148 html_handoff(hd, ch);
4150 else if(cmd == GF_RESET){
4151 html_output_raw_tag(hd->html_data, "colgroup");
4153 else if(cmd == GF_EOD){
4154 html_output_string(hd->html_data, "</colgroup>");
4157 return(1); /* GET linked */
4160 return(0); /* don't get linked */
4165 * HTML <I> (italic text) element handler
4168 html_i(HANDLER_S *hd, int ch, int cmd)
4170 if(cmd == GF_DATA){
4171 /* include LITERAL in spaceness test! */
4172 if(hd->x && !ASCII_ISSPACE((unsigned char) (ch & 0xff))){
4173 HTML_ITALIC(hd->html_data, 1);
4174 hd->x = 0;
4177 html_handoff(hd, ch);
4179 else if(cmd == GF_RESET){
4180 hd->x = 1;
4182 else if(cmd == GF_EOD){
4183 if(!hd->x)
4184 HTML_ITALIC(hd->html_data, 0);
4187 return(1); /* get linked */
4192 * HTML <EM> element handler
4195 html_em(HANDLER_S *hd, int ch, int cmd)
4197 if(cmd == GF_DATA){
4198 if(!PASS_HTML(hd->html_data)){
4199 /* include LITERAL in spaceness test! */
4200 if(hd->x && !ASCII_ISSPACE((unsigned char) (ch & 0xff))){
4201 HTML_ITALIC(hd->html_data, 1);
4202 hd->x = 0;
4206 html_handoff(hd, ch);
4208 else if(cmd == GF_RESET){
4209 if(PASS_HTML(hd->html_data)){
4210 html_output_raw_tag(hd->html_data, "em");
4212 else{
4213 hd->x = 1;
4216 else if(cmd == GF_EOD){
4217 if(PASS_HTML(hd->html_data)){
4218 html_output_string(hd->html_data, "</em>");
4220 else{
4221 if(!hd->x)
4222 HTML_ITALIC(hd->html_data, 0);
4226 return(1); /* get linked */
4231 * HTML <STRONG> element handler
4234 html_strong(HANDLER_S *hd, int ch, int cmd)
4236 if(cmd == GF_DATA){
4237 if(!PASS_HTML(hd->html_data)){
4238 /* include LITERAL in spaceness test! */
4239 if(hd->x && !ASCII_ISSPACE((unsigned char) (ch & 0xff))){
4240 HTML_ITALIC(hd->html_data, 1);
4241 hd->x = 0;
4245 html_handoff(hd, ch);
4247 else if(cmd == GF_RESET){
4248 if(PASS_HTML(hd->html_data)){
4249 html_output_raw_tag(hd->html_data, "strong");
4251 else{
4252 hd->x = 1;
4255 else if(cmd == GF_EOD){
4256 if(PASS_HTML(hd->html_data)){
4257 html_output_string(hd->html_data, "</strong>");
4259 else{
4260 if(!hd->x)
4261 HTML_ITALIC(hd->html_data, 0);
4265 return(1); /* get linked */
4270 * HTML <u> (Underline text) element handler
4273 html_u(HANDLER_S *hd, int ch, int cmd)
4275 if(PASS_HTML(hd->html_data)){
4276 if(cmd == GF_DATA){
4277 html_handoff(hd, ch);
4279 else if(cmd == GF_RESET){
4280 html_output_raw_tag(hd->html_data, "u");
4282 else if(cmd == GF_EOD){
4283 html_output_string(hd->html_data, "</u>");
4286 return(1); /* get linked */
4289 return(0); /* do NOT get linked */
4294 * HTML <b> (Bold text) element handler
4297 html_b(HANDLER_S *hd, int ch, int cmd)
4299 if(cmd == GF_DATA){
4300 if(!PASS_HTML(hd->html_data)){
4301 /* include LITERAL in spaceness test! */
4302 if(hd->x && !ASCII_ISSPACE((unsigned char) (ch & 0xff))){
4303 HTML_BOLD(hd->html_data, 1);
4304 hd->x = 0;
4308 html_handoff(hd, ch);
4310 else if(cmd == GF_RESET){
4311 if(PASS_HTML(hd->html_data)){
4312 html_output_raw_tag(hd->html_data, "b");
4314 else{
4315 hd->x = 1;
4318 else if(cmd == GF_EOD){
4319 if(PASS_HTML(hd->html_data)){
4320 html_output_string(hd->html_data, "</b>");
4322 else{
4323 if(!hd->x)
4324 HTML_BOLD(hd->html_data, 0);
4328 return(1); /* get linked */
4333 * HTML <s> (strike-through text) element handler
4336 html_s(HANDLER_S *hd, int ch, int cmd)
4338 if(cmd == GF_DATA){
4339 if(!PASS_HTML(hd->html_data)){
4340 /* include LITERAL in spaceness test! */
4341 if(hd->x && !ASCII_ISSPACE((unsigned char) (ch & 0xff))){
4342 HTML_STRIKE(hd->html_data, 1);
4343 hd->x = 0;
4347 html_handoff(hd, ch);
4349 else if(cmd == GF_RESET){
4350 if(PASS_HTML(hd->html_data)){
4351 html_output_raw_tag(hd->html_data, "s");
4353 else{
4354 hd->x = 1;
4357 else if(cmd == GF_EOD){
4358 if(PASS_HTML(hd->html_data)){
4359 html_output_string(hd->html_data, "</s>");
4361 else{
4362 if(!hd->x)
4363 HTML_STRIKE(hd->html_data, 0);
4367 return(1); /* get linked */
4372 * HTML <big> (BIG text) element handler
4375 html_big(HANDLER_S *hd, int ch, int cmd)
4377 if(cmd == GF_DATA){
4378 /* include LITERAL in spaceness test! */
4379 if(hd->x && !ASCII_ISSPACE((unsigned char) (ch & 0xff))){
4380 HTML_BIG(hd->html_data, 1);
4381 hd->x = 0;
4384 html_handoff(hd, ch);
4386 else if(cmd == GF_RESET){
4387 hd->x = 1;
4389 else if(cmd == GF_EOD){
4390 if(!hd->x)
4391 HTML_BIG(hd->html_data, 0);
4394 return(1); /* get linked */
4399 * HTML <small> (SMALL text) element handler
4402 html_small(HANDLER_S *hd, int ch, int cmd)
4404 if(cmd == GF_DATA){
4405 /* include LITERAL in spaceness test! */
4406 if(hd->x && !ASCII_ISSPACE((unsigned char) (ch & 0xff))){
4407 HTML_SMALL(hd->html_data, 1);
4408 hd->x = 0;
4411 html_handoff(hd, ch);
4413 else if(cmd == GF_RESET){
4414 hd->x = 1;
4416 else if(cmd == GF_EOD){
4417 if(!hd->x)
4418 HTML_SMALL(hd->html_data, 0);
4421 return(1); /* get linked */
4426 * HTML <FONT> element handler
4429 html_font(HANDLER_S *hd, int ch, int cmd)
4431 if(PASS_HTML(hd->html_data)){
4432 if(cmd == GF_DATA){
4433 html_handoff(hd, ch);
4435 else if(cmd == GF_RESET){
4436 html_output_raw_tag(hd->html_data, "font");
4438 else if(cmd == GF_EOD){
4439 html_output_string(hd->html_data, "</font>");
4442 return(1); /* get linked */
4445 return(0);
4450 * HTML <IMG> element handler
4453 html_img(HANDLER_S *hd, int ch, int cmd)
4455 PARAMETER *p;
4456 char *alt = NULL, *src = NULL, *s;
4458 if(cmd == GF_RESET){
4459 if(PASS_HTML(hd->html_data)){
4460 html_output_raw_tag(hd->html_data, "img");
4462 else{
4463 for(p = HD(hd->html_data)->el_data->attribs;
4464 p && p->attribute;
4465 p = p->next)
4466 if(p->value && p->value[0]){
4467 if(!strucmp(p->attribute, "alt"))
4468 alt = p->value;
4469 if(!strucmp(p->attribute, "src"))
4470 src = p->value;
4474 * Multipart/Related Content ID pointer
4475 * ONLY attached messages are recognized
4476 * if we ever decide web bugs aren't a problem
4477 * anymore then we might expand the scope
4479 if(src
4480 && DO_HANDLES(hd->html_data)
4481 && RELATED_OK(hd->html_data)
4482 && struncmp(src, "cid:", 4) == 0){
4483 char buf[32];
4484 int i, n;
4485 HANDLE_S *h = new_handle(HANDLESP(hd->html_data));
4487 h->type = IMG;
4488 h->h.img.src = cpystr(src + 4);
4489 h->h.img.alt = cpystr((alt) ? alt : "Attached Image");
4491 HTML_TEXT(hd->html_data, TAG_EMBED);
4492 HTML_TEXT(hd->html_data, TAG_HANDLE);
4494 sprintf(buf, "%d", h->key);
4495 n = strlen(buf);
4496 HTML_TEXT(hd->html_data, n);
4497 for(i = 0; i < n; i++){
4498 unsigned int uic = buf[i];
4499 HTML_TEXT(hd->html_data, uic);
4502 return(0);
4504 else if(alt && strlen(alt) < 256){ /* arbitrary "reasonable" limit */
4505 HTML_DUMP_LIT(hd->html_data, alt, strlen(alt));
4506 HTML_TEXT(hd->html_data, ' ');
4507 return(0);
4509 else if(src
4510 && (s = strrindex(src, '/'))
4511 && *++s != '\0'){
4512 HTML_TEXT(hd->html_data, '[');
4513 HTML_DUMP_LIT(hd->html_data, s, strlen(s));
4514 HTML_TEXT(hd->html_data, ']');
4515 HTML_TEXT(hd->html_data, ' ');
4516 return(0);
4519 /* text filler of last resort */
4520 HTML_DUMP_LIT(hd->html_data, "[IMAGE] ", 7);
4524 return(0); /* don't get linked */
4529 * HTML <MAP> (Image Map) element handler
4532 html_map(HANDLER_S *hd, int ch, int cmd)
4534 if(PASS_HTML(hd->html_data) && PASS_IMAGES(hd->html_data)){
4535 if(cmd == GF_DATA){
4536 html_handoff(hd, ch);
4538 else if(cmd == GF_RESET){
4539 html_output_raw_tag(hd->html_data, "map");
4541 else if(cmd == GF_EOD){
4542 html_output_string(hd->html_data, "</map>");
4545 return(1);
4548 return(0);
4553 * HTML <AREA> (Image Map Area) element handler
4556 html_area(HANDLER_S *hd, int ch, int cmd)
4558 if(PASS_HTML(hd->html_data) && PASS_IMAGES(hd->html_data)){
4559 if(cmd == GF_DATA){
4560 html_handoff(hd, ch);
4562 else if(cmd == GF_RESET){
4563 html_output_raw_tag(hd->html_data, "area");
4565 else if(cmd == GF_EOD){
4566 html_output_string(hd->html_data, "</area>");
4569 return(1);
4572 return(0);
4577 * HTML <FORM> (Form) element handler
4580 html_form(HANDLER_S *hd, int ch, int cmd)
4582 if(PASS_HTML(hd->html_data)){
4583 if(cmd == GF_DATA){
4584 html_handoff(hd, ch);
4586 else if(cmd == GF_RESET){
4587 PARAMETER **pp;
4589 /* SECURITY: make sure to redirect to new browser instance */
4590 for(pp = &(HD(hd->html_data)->el_data->attribs);
4591 *pp && (*pp)->attribute;
4592 pp = &(*pp)->next)
4593 if(!strucmp((*pp)->attribute, "target")){
4594 if((*pp)->value)
4595 fs_give((void **) &(*pp)->value);
4597 (*pp)->value = cpystr("_blank");
4600 if(!*pp){
4601 *pp = (PARAMETER *)fs_get(sizeof(PARAMETER));
4602 memset(*pp, 0, sizeof(PARAMETER));
4603 (*pp)->attribute = cpystr("target");
4604 (*pp)->value = cpystr("_blank");
4607 html_output_raw_tag(hd->html_data, "form");
4609 else if(cmd == GF_EOD){
4610 html_output_string(hd->html_data, "</form>");
4613 else{
4614 if(cmd == GF_RESET){
4615 html_blank(hd->html_data, 0);
4616 HTML_DUMP_LIT(hd->html_data, "[FORM]", 6);
4617 html_blank(hd->html_data, 0);
4621 return(PASS_HTML(hd->html_data)); /* maybe get linked */
4626 * HTML <INPUT> (Form) element handler
4629 html_input(HANDLER_S *hd, int ch, int cmd)
4631 if(PASS_HTML(hd->html_data)){
4632 if(cmd == GF_RESET){
4633 html_output_raw_tag(hd->html_data, "input");
4637 return(0); /* don't get linked */
4642 * HTML <BUTTON> (Form) element handler
4645 html_button(HANDLER_S *hd, int ch, int cmd)
4647 if(PASS_HTML(hd->html_data)){
4648 if(cmd == GF_DATA){
4649 html_handoff(hd, ch);
4651 else if(cmd == GF_RESET){
4652 html_output_raw_tag(hd->html_data, "button");
4654 else if(cmd == GF_EOD){
4655 html_output_string(hd->html_data, "</button>");
4658 return(1); /* get linked */
4661 return(0);
4666 * HTML <OPTION> (Form) element handler
4669 html_option(HANDLER_S *hd, int ch, int cmd)
4671 if(PASS_HTML(hd->html_data)){
4672 if(cmd == GF_DATA){
4673 html_handoff(hd, ch);
4675 else if(cmd == GF_RESET){
4676 html_output_raw_tag(hd->html_data, "option");
4678 else if(cmd == GF_EOD){
4679 html_output_string(hd->html_data, "</option>");
4682 return(1); /* get linked */
4685 return(0);
4690 * HTML <OPTGROUP> (Form) element handler
4693 html_optgroup(HANDLER_S *hd, int ch, int cmd)
4695 if(PASS_HTML(hd->html_data)){
4696 if(cmd == GF_DATA){
4697 html_handoff(hd, ch);
4699 else if(cmd == GF_RESET){
4700 html_output_raw_tag(hd->html_data, "optgroup");
4702 else if(cmd == GF_EOD){
4703 html_output_string(hd->html_data, "</optgroup>");
4706 return(1); /* get linked */
4709 return(0);
4714 * HTML <SELECT> (Form) element handler
4717 html_select(HANDLER_S *hd, int ch, int cmd)
4719 if(PASS_HTML(hd->html_data)){
4720 if(cmd == GF_DATA){
4721 html_handoff(hd, ch);
4723 else if(cmd == GF_RESET){
4724 html_output_raw_tag(hd->html_data, "select");
4726 else if(cmd == GF_EOD){
4727 html_output_string(hd->html_data, "</select>");
4730 return(1); /* get linked */
4733 return(0);
4738 * HTML <TEXTAREA> (Form) element handler
4741 html_textarea(HANDLER_S *hd, int ch, int cmd)
4743 if(PASS_HTML(hd->html_data)){
4744 if(cmd == GF_DATA){
4745 html_handoff(hd, ch);
4747 else if(cmd == GF_RESET){
4748 html_output_raw_tag(hd->html_data, "textarea");
4750 else if(cmd == GF_EOD){
4751 html_output_string(hd->html_data, "</textarea>");
4754 return(1); /* get linked */
4757 return(0);
4762 * HTML <LABEL> (Form) element handler
4765 html_label(HANDLER_S *hd, int ch, int cmd)
4767 if(PASS_HTML(hd->html_data)){
4768 if(cmd == GF_DATA){
4769 html_handoff(hd, ch);
4771 else if(cmd == GF_RESET){
4772 html_output_raw_tag(hd->html_data, "label");
4774 else if(cmd == GF_EOD){
4775 html_output_string(hd->html_data, "</label>");
4778 return(1); /* get linked */
4781 return(0);
4786 * HTML <FIELDSET> (Form) element handler
4789 html_fieldset(HANDLER_S *hd, int ch, int cmd)
4791 if(PASS_HTML(hd->html_data)){
4792 if(cmd == GF_DATA){
4793 html_handoff(hd, ch);
4795 else if(cmd == GF_RESET){
4796 html_output_raw_tag(hd->html_data, "fieldset");
4798 else if(cmd == GF_EOD){
4799 html_output_string(hd->html_data, "</fieldset>");
4802 return(1); /* get linked */
4805 return(0);
4810 * HTML <HEAD> element handler
4813 html_head(HANDLER_S *hd, int ch, int cmd)
4815 if(cmd == GF_DATA){
4816 html_handoff(hd, ch);
4818 else if(cmd == GF_RESET){
4819 HD(hd->html_data)->head = 1;
4821 else if(cmd == GF_EOD){
4822 HD(hd->html_data)->head = 0;
4825 return(1); /* get linked */
4830 * HTML <BASE> element handler
4833 html_base(HANDLER_S *hd, int ch, int cmd)
4835 if(cmd == GF_RESET){
4836 if(HD(hd->html_data)->head && !HTML_BASE(hd->html_data)){
4837 PARAMETER *p;
4839 for(p = HD(hd->html_data)->el_data->attribs;
4840 p && p->attribute && strucmp(p->attribute, "HREF");
4841 p = p->next)
4844 if(p && p->value && !((HTML_OPT_S *)(hd->html_data)->opt)->base)
4845 ((HTML_OPT_S *)(hd->html_data)->opt)->base = cpystr(p->value);
4849 return(0); /* DON'T get linked */
4854 * HTML <TITLE> element handler
4857 html_title(HANDLER_S *hd, int ch, int cmd)
4859 if(cmd == GF_DATA){
4860 if(hd->x + 1 >= hd->y){
4861 hd->y += 80;
4862 fs_resize((void **)&hd->s, (size_t)hd->y * sizeof(unsigned char));
4865 hd->s[hd->x++] = (unsigned char) ch;
4867 else if(cmd == GF_RESET){
4868 hd->x = 0L;
4869 hd->y = 80L;
4870 hd->s = (unsigned char *)fs_get((size_t)hd->y * sizeof(unsigned char));
4872 else if(cmd == GF_EOD){
4873 /* Down the road we probably want to give these bytes to
4874 * someone...
4876 hd->s[hd->x] = '\0';
4877 fs_give((void **)&hd->s);
4880 return(1); /* get linked */
4885 * HTML <BODY> element handler
4888 html_body(HANDLER_S *hd, int ch, int cmd)
4890 if(cmd == GF_DATA){
4891 html_handoff(hd, ch);
4893 else if(cmd == GF_RESET){
4894 if(PASS_HTML(hd->html_data)){
4895 PARAMETER *p, *tp;
4896 char **style = NULL, *text = NULL, *bgcolor = NULL, *pcs;
4898 /* modify any attributes in a useful way? */
4899 for(p = HD(hd->html_data)->el_data->attribs;
4900 p && p->attribute;
4901 p = p->next)
4902 if(p->value){
4903 if(!strucmp(p->attribute, "style"))
4904 style = &p->value;
4905 else if(!strucmp(p->attribute, "text"))
4906 text = p->value;
4908 * bgcolor NOT passed since user setting takes precedence
4910 else if(!strucmp(p->attribute, "bgcolor"))
4911 bgcolor = p->value;
4915 /* colors pretty much it */
4916 if(text || bgcolor){
4917 if(!style){
4918 tp = (PARAMETER *)fs_get(sizeof(PARAMETER));
4919 memset(tp, 0, sizeof(PARAMETER));
4920 tp->next = HD(hd->html_data)->el_data->attribs;
4921 HD(hd->html_data)->el_data->attribs = tp;
4922 tp->attribute = cpystr("style");
4924 tmp_20k_buf[0] = '\0';
4925 style = &tp->value;
4926 pcs = "%s%s%s%s%s";
4928 else{
4929 snprintf(tmp_20k_buf, SIZEOF_20KBUF, "%s", *style);
4930 fs_give((void **) style);
4931 pcs = "; %s%s%s%s%s";
4934 snprintf(tmp_20k_buf + strlen(tmp_20k_buf),
4935 SIZEOF_20KBUF - strlen(tmp_20k_buf),
4936 pcs,
4937 (text) ? "color: " : "", (text) ? text : "",
4938 (text && bgcolor) ? ";" : "",
4939 (bgcolor) ? "background-color: " : "", (bgcolor) ? bgcolor : "");
4940 *style = cpystr(tmp_20k_buf);
4943 html_output_raw_tag(hd->html_data, "div");
4946 HD(hd->html_data)->body = 1;
4948 else if(cmd == GF_EOD){
4949 if(PASS_HTML(hd->html_data)){
4950 html_output_string(hd->html_data, "</div>");
4953 HD(hd->html_data)->body = 0;
4956 return(1); /* get linked */
4961 * HTML <A> (Anchor) element handler
4964 html_a(HANDLER_S *hd, int ch, int cmd)
4966 if(cmd == GF_DATA){
4967 html_handoff(hd, ch);
4969 if(hd->dp) /* remember text within anchor tags */
4970 so_writec(ch, (STORE_S *) hd->dp);
4972 else if(cmd == GF_RESET){
4973 int i, n, x;
4974 char buf[256];
4975 HANDLE_S *h;
4976 PARAMETER *p, *href = NULL, *name = NULL;
4979 * Pending Anchor!?!?
4980 * space insertion/line breaking that's yet to get done...
4982 if(HD(hd->html_data)->prefix){
4983 dprint((2, "-- html error: nested or unterminated anchor\n"));
4984 html_a_finish(hd);
4988 * Look for valid Anchor data vis the filter installer's parms
4989 * (e.g., Only allow references to our internal URLs if asked)
4991 for(p = HD(hd->html_data)->el_data->attribs;
4992 p && p->attribute;
4993 p = p->next)
4994 if(!strucmp(p->attribute, "HREF")
4995 && p->value
4996 && (HANDLES_LOC(hd->html_data)
4997 || struncmp(p->value, "x-alpine-", 9)
4998 || struncmp(p->value, "x-pine-help", 11)
4999 || p->value[0] == '#'))
5000 href = p;
5001 else if(!strucmp(p->attribute, "NAME"))
5002 name = p;
5004 if(DO_HANDLES(hd->html_data) && (href || name)){
5005 h = new_handle(HANDLESP(hd->html_data));
5008 * Enhancement: we might want to get fancier and parse the
5009 * href a bit further such that we can launch images using
5010 * our image viewer, or browse local files or directories
5011 * with our internal tools. Of course, having the jump-off
5012 * point into text/html always be the defined "web-browser",
5013 * just might be the least confusing UI-wise...
5015 h->type = URL;
5017 if(name && name->value)
5018 h->h.url.name = cpystr(name->value);
5021 * Prepare to build embedded prefix...
5023 HD(hd->html_data)->prefix = (int *) fs_get(64 * sizeof(int));
5024 x = 0;
5027 * Is this something that looks like a URL? If not and
5028 * we were giving some "base" string, proceed ala RFC1808...
5030 if(href){
5031 if(HTML_BASE(hd->html_data) && !rfc1738_scan(href->value, &n)){
5032 html_a_relative(HTML_BASE(hd->html_data), href->value, h);
5034 else if(!(NO_RELATIVE(hd->html_data) && html_href_relative(href->value)))
5035 h->h.url.path = cpystr(href->value);
5037 if(pico_usingcolor()){
5038 char *fg = NULL, *bg = NULL, *q;
5040 if(ps_global->VAR_SLCTBL_FORE_COLOR
5041 && colorcmp(ps_global->VAR_SLCTBL_FORE_COLOR,
5042 ps_global->VAR_NORM_FORE_COLOR))
5043 fg = ps_global->VAR_SLCTBL_FORE_COLOR;
5045 if(ps_global->VAR_SLCTBL_BACK_COLOR
5046 && colorcmp(ps_global->VAR_SLCTBL_BACK_COLOR,
5047 ps_global->VAR_NORM_BACK_COLOR))
5048 bg = ps_global->VAR_SLCTBL_BACK_COLOR;
5050 if(fg || bg){
5051 COLOR_PAIR *tmp;
5054 * The blacks are just known good colors for testing
5055 * whether the other color is good.
5057 tmp = new_color_pair(fg ? fg : colorx(COL_BLACK),
5058 bg ? bg : colorx(COL_BLACK));
5059 if(pico_is_good_colorpair(tmp)){
5060 q = color_embed(fg, bg);
5062 for(i = 0; q[i]; i++)
5063 HD(hd->html_data)->prefix[x++] = q[i];
5066 if(tmp)
5067 free_color_pair(&tmp);
5070 if(F_OFF(F_SLCTBL_ITEM_NOBOLD, ps_global))
5071 HD(hd->html_data)->prefix[x++] = HTML_DOBOLD;
5073 else
5074 HD(hd->html_data)->prefix[x++] = HTML_DOBOLD;
5077 HD(hd->html_data)->prefix[x++] = TAG_EMBED;
5078 HD(hd->html_data)->prefix[x++] = TAG_HANDLE;
5080 snprintf(buf, sizeof(buf), "%ld", hd->x = h->key);
5081 HD(hd->html_data)->prefix[x++] = n = strlen(buf);
5082 for(i = 0; i < n; i++)
5083 HD(hd->html_data)->prefix[x++] = buf[i];
5085 HD(hd->html_data)->prefix_used = x;
5087 hd->dp = (void *) so_get(CharStar, NULL, EDIT_ACCESS);
5090 else if(cmd == GF_EOD){
5091 html_a_finish(hd);
5094 return(1); /* get linked */
5098 void
5099 html_a_prefix(FILTER_S *f)
5101 int *prefix, n;
5103 /* Do this so we don't visit from html_output... */
5104 prefix = HD(f)->prefix;
5105 HD(f)->prefix = NULL;
5107 for(n = 0; n < HD(f)->prefix_used; n++)
5108 html_a_output_prefix(f, prefix[n]);
5110 fs_give((void **) &prefix);
5115 * html_a_finish - house keeping associated with end of link tag
5117 void
5118 html_a_finish(HANDLER_S *hd)
5120 if(DO_HANDLES(hd->html_data)){
5121 if(HD(hd->html_data)->prefix){
5122 if(!PASS_HTML(hd->html_data)){
5123 char *empty_link = "[LINK]";
5124 int i;
5126 html_a_prefix(hd->html_data);
5127 for(i = 0; empty_link[i]; i++)
5128 html_output(hd->html_data, empty_link[i]);
5132 if(pico_usingcolor()){
5133 char *fg = NULL, *bg = NULL, *p;
5134 int i;
5136 if(ps_global->VAR_SLCTBL_FORE_COLOR
5137 && colorcmp(ps_global->VAR_SLCTBL_FORE_COLOR,
5138 ps_global->VAR_NORM_FORE_COLOR))
5139 fg = ps_global->VAR_NORM_FORE_COLOR;
5141 if(ps_global->VAR_SLCTBL_BACK_COLOR
5142 && colorcmp(ps_global->VAR_SLCTBL_BACK_COLOR,
5143 ps_global->VAR_NORM_BACK_COLOR))
5144 bg = ps_global->VAR_NORM_BACK_COLOR;
5146 if(F_OFF(F_SLCTBL_ITEM_NOBOLD, ps_global))
5147 HTML_BOLD(hd->html_data, 0); /* turn OFF bold */
5149 if(fg || bg){
5150 COLOR_PAIR *tmp;
5153 * The blacks are just known good colors for testing
5154 * whether the other color is good.
5156 tmp = new_color_pair(fg ? fg : colorx(COL_BLACK),
5157 bg ? bg : colorx(COL_BLACK));
5158 if(pico_is_good_colorpair(tmp)){
5159 p = color_embed(fg, bg);
5161 for(i = 0; p[i]; i++)
5162 html_output(hd->html_data, p[i]);
5165 if(tmp)
5166 free_color_pair(&tmp);
5169 else
5170 HTML_BOLD(hd->html_data, 0); /* turn OFF bold */
5172 html_output(hd->html_data, TAG_EMBED);
5173 html_output(hd->html_data, TAG_HANDLEOFF);
5175 html_a_output_info(hd);
5181 * html_output_a_prefix - dump Anchor prefix data
5183 void
5184 html_a_output_prefix(FILTER_S *f, int c)
5186 switch(c){
5187 case HTML_DOBOLD :
5188 HTML_BOLD(f, 1);
5189 break;
5191 default :
5192 html_output(f, c);
5193 break;
5200 * html_a_output_info - dump possibly deceptive link info into text.
5201 * phark the phishers.
5203 void
5204 html_a_output_info(HANDLER_S *hd)
5206 int l, risky = 0, hl = 0, tl;
5207 char *url = NULL, *hn = NULL, *txt;
5208 HANDLE_S *h;
5210 /* find host anchor references */
5211 if((h = get_handle(*HANDLESP(hd->html_data), (int) hd->x)) != NULL
5212 && h->h.url.path != NULL
5213 && (hn = rfc1738_scan(rfc1738_str(url = cpystr(h->h.url.path)), &l)) != NULL
5214 && (hn = srchstr(hn,"://")) != NULL){
5216 for(hn += 3, hl = 0; hn[hl] && hn[hl] != '/' && hn[hl] != '?'; hl++)
5220 if(hn && hl){
5222 * look over anchor's text to see if there's a
5223 * mismatch between href target and url-ish
5224 * looking text. throw a red flag if so.
5225 * similarly, toss one if the target's referenced
5226 * by a
5228 if(hd->dp){
5229 so_writec('\0', (STORE_S *) hd->dp);
5231 if((txt = (char *) so_text((STORE_S *) hd->dp)) != NULL
5232 && (txt = rfc1738_scan(txt, &tl)) != NULL
5233 && (txt = srchstr(txt,"://")) != NULL){
5235 for(txt += 3, tl = 0; txt[tl] && txt[tl] != '/' && txt[tl] != '?'; tl++)
5238 if(tl != hl)
5239 risky++;
5240 else
5241 /* look for non matching text */
5242 for(l = 0; l < tl && l < hl; l++)
5243 if(tolower((unsigned char) txt[l]) != tolower((unsigned char) hn[l])){
5244 risky++;
5245 break;
5249 so_give((STORE_S **) &hd->dp);
5252 /* look for literal IP, anything possibly encoded or auth specifier */
5253 if(!risky){
5254 int digits = 1;
5256 for(l = 0; l < hl; l++){
5257 if(hn[l] == '@' || hn[l] == '%'){
5258 risky++;
5259 break;
5261 else if(!(hn[l] == '.' || isdigit((unsigned char) hn[l])))
5262 digits = 0;
5265 if(digits)
5266 risky++;
5269 /* Insert text of link's domain */
5270 if(SHOWSERVER(hd->html_data)){
5271 char *q;
5272 COLOR_PAIR *col = NULL, *colnorm = NULL;
5274 html_output(hd->html_data, ' ');
5275 html_output(hd->html_data, '[');
5277 if(pico_usingcolor()
5278 && ps_global->VAR_METAMSG_FORE_COLOR
5279 && ps_global->VAR_METAMSG_BACK_COLOR
5280 && (col = new_color_pair(ps_global->VAR_METAMSG_FORE_COLOR,
5281 ps_global->VAR_METAMSG_BACK_COLOR))){
5282 if(!pico_is_good_colorpair(col))
5283 free_color_pair(&col);
5285 if(col){
5286 q = color_embed(col->fg, col->bg);
5288 for(l = 0; q[l]; l++)
5289 html_output(hd->html_data, q[l]);
5293 for(l = 0; l < hl; l++)
5294 html_output(hd->html_data, hn[l]);
5296 if(col){
5297 if(ps_global->VAR_NORM_FORE_COLOR
5298 && ps_global->VAR_NORM_BACK_COLOR
5299 && (colnorm = new_color_pair(ps_global->VAR_NORM_FORE_COLOR,
5300 ps_global->VAR_NORM_BACK_COLOR))){
5301 if(!pico_is_good_colorpair(colnorm))
5302 free_color_pair(&colnorm);
5304 if(colnorm){
5305 q = color_embed(colnorm->fg, colnorm->bg);
5306 free_color_pair(&colnorm);
5308 for(l = 0; q[l]; l++)
5309 html_output(hd->html_data, q[l]);
5313 free_color_pair(&col);
5316 html_output(hd->html_data, ']');
5321 * if things look OK so far, make sure nothing within
5322 * the url looks too fishy...
5324 while(!risky && hn
5325 && (hn = rfc1738_scan(hn, &l)) != NULL
5326 && (hn = srchstr(hn,"://")) != NULL){
5327 int digits = 1;
5329 for(hn += 3, hl = 0; hn[hl] && hn[hl] != '/' && hn[hl] != '?'; hl++){
5331 * auth spec, encoded characters, or possibly non-standard port
5332 * should raise a red flag
5334 if(hn[hl] == '@' || hn[hl] == '%' || hn[hl] == ':'){
5335 risky++;
5336 break;
5338 else if(!(hn[hl] == '.' || isdigit((unsigned char) hn[hl])))
5339 digits = 0;
5342 /* dotted-dec/raw-int address should cause suspicion as well */
5343 if(digits)
5344 risky++;
5347 if(risky && ((HTML_OPT_S *) hd->html_data->opt)->warnrisk_f)
5348 (*((HTML_OPT_S *) hd->html_data->opt)->warnrisk_f)();
5350 if(hd->dp)
5351 so_give((STORE_S **) &hd->dp);
5354 fs_give((void **) &url);
5360 * relative_url - put full url path in h based on base and relative url
5362 void
5363 html_a_relative(char *base_url, char *rel_url, HANDLE_S *h)
5365 size_t len;
5366 char tmp[MAILTMPLEN], *p, *q;
5367 char *scheme = NULL, *net = NULL, *path = NULL,
5368 *parms = NULL, *query = NULL, *frag = NULL,
5369 *base_scheme = NULL, *base_net_loc = NULL,
5370 *base_path = NULL, *base_parms = NULL,
5371 *base_query = NULL, *base_frag = NULL,
5372 *rel_scheme = NULL, *rel_net_loc = NULL,
5373 *rel_path = NULL, *rel_parms = NULL,
5374 *rel_query = NULL, *rel_frag = NULL;
5376 /* Rough parse of base URL */
5377 rfc1808_tokens(base_url, &base_scheme, &base_net_loc, &base_path,
5378 &base_parms, &base_query, &base_frag);
5380 /* Rough parse of this URL */
5381 rfc1808_tokens(rel_url, &rel_scheme, &rel_net_loc, &rel_path,
5382 &rel_parms, &rel_query, &rel_frag);
5384 scheme = rel_scheme; /* defaults */
5385 net = rel_net_loc;
5386 path = rel_path;
5387 parms = rel_parms;
5388 query = rel_query;
5389 frag = rel_frag;
5390 if(!scheme && base_scheme){
5391 scheme = base_scheme;
5392 if(!net){
5393 net = base_net_loc;
5394 if(path){
5395 if(*path != '/'){
5396 if(base_path){
5397 for(p = q = base_path; /* Drop base path's tail */
5398 (p = strchr(p, '/'));
5399 q = ++p)
5402 len = q - base_path;
5404 else
5405 len = 0;
5407 if(len + strlen(rel_path) < sizeof(tmp)-1){
5408 if(len)
5409 snprintf(path = tmp, sizeof(tmp), "%.*s", (int) len, base_path);
5411 strncpy(tmp + len, rel_path, sizeof(tmp)-len);
5412 tmp[sizeof(tmp)-1] = '\0';
5414 /* Follow RFC 1808 "Step 6" */
5415 for(p = tmp; (p = strchr(p, '.')); )
5416 switch(*(p+1)){
5418 * a) All occurrences of "./", where "." is a
5419 * complete path segment, are removed.
5421 case '/' :
5422 if(p > tmp)
5423 for(q = p; (*q = *(q+2)) != '\0'; q++)
5425 else
5426 p++;
5428 break;
5431 * b) If the path ends with "." as a
5432 * complete path segment, that "." is
5433 * removed.
5435 case '\0' :
5436 if(p == tmp || *(p-1) == '/')
5437 *p = '\0';
5438 else
5439 p++;
5441 break;
5444 * c) All occurrences of "<segment>/../",
5445 * where <segment> is a complete path
5446 * segment not equal to "..", are removed.
5447 * Removal of these path segments is
5448 * performed iteratively, removing the
5449 * leftmost matching pattern on each
5450 * iteration, until no matching pattern
5451 * remains.
5453 * d) If the path ends with "<segment>/..",
5454 * where <segment> is a complete path
5455 * segment not equal to "..", that
5456 * "<segment>/.." is removed.
5458 case '.' :
5459 if(p > tmp + 1){
5460 for(q = p - 2; q > tmp && *q != '/'; q--)
5463 if(*q == '/')
5464 q++;
5466 if(q + 1 == p /* no "//.." */
5467 || (*q == '.' /* and "../.." */
5468 && *(q+1) == '.'
5469 && *(q+2) == '/')){
5470 p += 2;
5471 break;
5474 switch(*(p+2)){
5475 case '/' :
5476 len = (p - q) + 3;
5477 p = q;
5478 for(; (*q = *(q+len)) != '\0'; q++)
5481 break;
5483 case '\0':
5484 *(p = q) = '\0';
5485 break;
5487 default:
5488 p += 2;
5489 break;
5492 else
5493 p += 2;
5495 break;
5497 default :
5498 p++;
5499 break;
5502 else
5503 path = ""; /* lame. */
5506 else{
5507 path = base_path;
5508 if(!parms){
5509 parms = base_parms;
5510 if(!query)
5511 query = base_query;
5517 len = (scheme ? strlen(scheme) : 0) + (net ? strlen(net) : 0)
5518 + (path ? strlen(path) : 0) + (parms ? strlen(parms) : 0)
5519 + (query ? strlen(query) : 0) + (frag ? strlen(frag ) : 0) + 8;
5521 h->h.url.path = (char *) fs_get(len * sizeof(char));
5522 snprintf(h->h.url.path, len, "%s%s%s%s%s%s%s%s%s%s%s%s",
5523 scheme ? scheme : "", scheme ? ":" : "",
5524 net ? "//" : "", net ? net : "",
5525 (path && *path == '/') ? "" : ((path && net) ? "/" : ""),
5526 path ? path : "",
5527 parms ? ";" : "", parms ? parms : "",
5528 query ? "?" : "", query ? query : "",
5529 frag ? "#" : "", frag ? frag : "");
5531 if(base_scheme)
5532 fs_give((void **) &base_scheme);
5534 if(base_net_loc)
5535 fs_give((void **) &base_net_loc);
5537 if(base_path)
5538 fs_give((void **) &base_path);
5540 if(base_parms)
5541 fs_give((void **) &base_parms);
5543 if(base_query)
5544 fs_give((void **) &base_query);
5546 if(base_frag)
5547 fs_give((void **) &base_frag);
5549 if(rel_scheme)
5550 fs_give((void **) &rel_scheme);
5552 if(rel_net_loc)
5553 fs_give((void **) &rel_net_loc);
5555 if(rel_parms)
5556 fs_give((void **) &rel_parms);
5558 if(rel_query)
5559 fs_give((void **) &rel_query);
5561 if(rel_frag)
5562 fs_give((void **) &rel_frag);
5564 if(rel_path)
5565 fs_give((void **) &rel_path);
5570 * html_href_relative - href
5573 html_href_relative(char *url)
5575 int i;
5577 if(url)
5578 for(i = 0; i < 32 && url[i]; i++)
5579 if(!(isalpha((unsigned char) url[i]) || url[i] == '_' || url[i] == '-')){
5580 if(url[i] == ':')
5581 return(FALSE);
5582 else
5583 break;
5586 return(TRUE);
5591 * HTML <UL> (Unordered List) element handler
5594 html_ul(HANDLER_S *hd, int ch, int cmd)
5596 if(cmd == GF_DATA){
5597 html_handoff(hd, ch);
5599 else if(cmd == GF_RESET){
5600 if(PASS_HTML(hd->html_data)){
5601 html_output_raw_tag(hd->html_data, "ul");
5603 else{
5604 HD(hd->html_data)->li_pending = 1;
5605 html_blank(hd->html_data, 0);
5608 else if(cmd == GF_EOD){
5609 if(PASS_HTML(hd->html_data)){
5610 html_output_string(hd->html_data, "</ul>");
5612 else{
5613 html_blank(hd->html_data, 0);
5615 if(!HD(hd->html_data)->li_pending)
5616 html_indent(hd->html_data, -4, HTML_ID_INC);
5617 else
5618 HD(hd->html_data)->li_pending = 0;
5622 return(1); /* get linked */
5627 * HTML <OL> (Ordered List) element handler
5630 html_ol(HANDLER_S *hd, int ch, int cmd)
5632 if(cmd == GF_DATA){
5633 html_handoff(hd, ch);
5635 else if(cmd == GF_RESET){
5636 if(PASS_HTML(hd->html_data)){
5637 html_output_raw_tag(hd->html_data, "ol");
5639 else{
5640 PARAMETER *p;
5642 * Signal that we're expecting to see <LI> as our next element
5643 * and set the the initial ordered count.
5645 hd->x = 1L; /* set default */
5646 hd->y = LIST_DECIMAL; /* set default */
5647 for(p = HD(hd->html_data)->el_data->attribs;
5648 p && p->attribute;
5649 p = p->next)
5650 if(p->value){
5651 if(!strucmp(p->attribute, "TYPE")){
5652 if(!strucmp(p->value, "a")) /* alpha, lowercase */
5653 hd->y = LIST_ALPHALO;
5654 else if(!strucmp(p->value, "A")) /* alpha, uppercase */
5655 hd->y = LIST_ALPHAUP;
5656 else if(!strucmp(p->value, "i")) /* roman, lowercase */
5657 hd->y = LIST_ROMANLO;
5658 else if(!strucmp(p->value, "I")) /* roman, uppercase */
5659 hd->y = LIST_ROMANUP;
5660 else if(strucmp(p->value, "1")) /* decimal, the default */
5661 hd->y = LIST_UNKNOWN;
5663 else if(!strucmp(p->attribute, "START"))
5664 hd->x = atol(p->value);
5665 // else ADD SUPPORT FOR OTHER ATTRIBUTES... LATER
5666 // this is not so simple. The main missing support
5667 // is for the STYLE attribute, but implementing that
5668 // correctly will take time, so will be implemented
5669 // after version 2.21 is released.
5671 HD(hd->html_data)->li_pending = 1;
5672 html_blank(hd->html_data, 0);
5675 else if(cmd == GF_EOD){
5676 if(PASS_HTML(hd->html_data)){
5677 html_output_string(hd->html_data, "</ol>");
5679 else{
5680 html_blank(hd->html_data, 0);
5682 if(!HD(hd->html_data)->li_pending)
5683 html_indent(hd->html_data, -4, HTML_ID_INC);
5684 else
5685 HD(hd->html_data)->li_pending = 0;
5689 return(1); /* get linked */
5694 * HTML <MENU> (Menu List) element handler
5697 html_menu(HANDLER_S *hd, int ch, int cmd)
5699 if(cmd == GF_DATA){
5700 html_handoff(hd, ch);
5702 else if(cmd == GF_RESET){
5703 if(PASS_HTML(hd->html_data)){
5704 html_output_raw_tag(hd->html_data, "menu");
5706 else{
5707 HD(hd->html_data)->li_pending = 1;
5710 else if(cmd == GF_EOD){
5711 if(PASS_HTML(hd->html_data)){
5712 html_output_string(hd->html_data, "</menu>");
5714 else{
5715 html_blank(hd->html_data, 0);
5717 if(!HD(hd->html_data)->li_pending)
5718 html_indent(hd->html_data, -4, HTML_ID_INC);
5719 else
5720 HD(hd->html_data)->li_pending = 0;
5724 return(1); /* get linked */
5729 * HTML <DIR> (Directory List) element handler
5732 html_dir(HANDLER_S *hd, int ch, int cmd)
5734 if(cmd == GF_DATA){
5735 html_handoff(hd, ch);
5737 else if(cmd == GF_RESET){
5738 if(PASS_HTML(hd->html_data)){
5739 html_output_raw_tag(hd->html_data, "dir");
5741 else{
5742 HD(hd->html_data)->li_pending = 1;
5745 else if(cmd == GF_EOD){
5746 if(PASS_HTML(hd->html_data)){
5747 html_output_string(hd->html_data, "</dir>");
5749 else{
5750 html_blank(hd->html_data, 0);
5752 if(!HD(hd->html_data)->li_pending)
5753 html_indent(hd->html_data, -4, HTML_ID_INC);
5754 else
5755 HD(hd->html_data)->li_pending = 0;
5759 return(1); /* get linked */
5764 * HTML <LI> (List Item) element handler
5767 html_li(HANDLER_S *hd, int ch, int cmd)
5769 if(cmd == GF_DATA){
5770 if(PASS_HTML(hd->html_data)){
5771 html_handoff(hd, ch);
5774 else if(cmd == GF_RESET){
5775 HANDLER_S *p, *found = NULL;
5778 * There better be a an unordered list, ordered list,
5779 * Menu or Directory handler installed
5780 * or else we crap out...
5782 for(p = HANDLERS(hd->html_data); p; p = p->below)
5783 if(EL(p)->handler == html_ul
5784 || EL(p)->handler == html_ol
5785 || EL(p)->handler == html_menu
5786 || EL(p)->handler == html_dir){
5787 found = p;
5788 break;
5791 if(found){
5792 if(PASS_HTML(hd->html_data)){
5794 else{
5795 char buf[16], tmp[16], *p;
5796 int wrapstate;
5798 /* Start a new line */
5799 html_blank(hd->html_data, 0);
5801 /* adjust indent level if needed */
5802 if(HD(hd->html_data)->li_pending){
5803 html_indent(hd->html_data, 4, HTML_ID_INC);
5804 HD(hd->html_data)->li_pending = 0;
5807 if(EL(found)->handler == html_ul){
5808 int l = html_indent(hd->html_data, 0, HTML_ID_GET);
5810 strncpy(buf, " ", sizeof(buf));
5811 buf[1] = (l < 5) ? '*' : (l < 9) ? '+' : (l < 17) ? 'o' : '#';
5813 else if(EL(found)->handler == html_ol){
5814 if(found->y == LIST_DECIMAL || found->y == LIST_UNKNOWN)
5815 snprintf(tmp, sizeof(tmp), "%ld", found->x++);
5816 else if(found->y == LIST_ALPHALO)
5817 convert_decimal_to_alpha(tmp, sizeof(tmp), found->x++, 'a');
5818 else if(found->y == LIST_ALPHAUP)
5819 convert_decimal_to_alpha(tmp, sizeof(tmp), found->x++, 'A');
5820 else if(found->y == LIST_ROMANLO)
5821 convert_decimal_to_roman(tmp, sizeof(tmp), found->x++, 'i');
5822 else if(found->y == LIST_ROMANUP)
5823 convert_decimal_to_roman(tmp, sizeof(tmp), found->x++, 'I');
5824 snprintf(buf, sizeof(buf), " %s.", tmp);
5825 buf[sizeof(buf)-1] = '\0';
5827 else if(EL(found)->handler == html_menu){
5828 strncpy(buf, " ->", sizeof(buf));
5829 buf[sizeof(buf)-1] = '\0';
5832 html_indent(hd->html_data, -4, HTML_ID_INC);
5834 /* So we don't munge whitespace */
5835 wrapstate = HD(hd->html_data)->wrapstate;
5836 HD(hd->html_data)->wrapstate = 0;
5838 html_write_indent(hd->html_data, HD(hd->html_data)->indent_level);
5839 for(p = buf; *p; p++)
5840 html_output(hd->html_data, (int) *p);
5841 HD(hd->html_data)->wrapstate = wrapstate;
5842 html_indent(hd->html_data, 4, HTML_ID_INC);
5844 /* else BUG: should really bitch about this */
5847 if(PASS_HTML(hd->html_data)){
5848 html_output_raw_tag(hd->html_data, "li");
5849 return(1); /* get linked */
5852 else if(cmd == GF_EOD){
5853 if(PASS_HTML(hd->html_data)){
5854 html_output_string(hd->html_data, "</li>");
5858 return(PASS_HTML(hd->html_data)); /* DON'T get linked */
5863 * HTML <DL> (Definition List) element handler
5866 html_dl(HANDLER_S *hd, int ch, int cmd)
5868 if(cmd == GF_DATA){
5869 html_handoff(hd, ch);
5871 else if(cmd == GF_RESET){
5872 if(PASS_HTML(hd->html_data)){
5873 html_output_raw_tag(hd->html_data, "dl");
5875 else{
5877 * Set indention level for definition terms and definitions...
5879 hd->x = html_indent(hd->html_data, 0, HTML_ID_GET);
5880 hd->y = hd->x + 2;
5881 hd->z = hd->y + 4;
5884 else if(cmd == GF_EOD){
5885 if(PASS_HTML(hd->html_data)){
5886 html_output_string(hd->html_data, "</dl>");
5888 else{
5889 html_indent(hd->html_data, (int) hd->x, HTML_ID_SET);
5890 html_blank(hd->html_data, 1);
5894 return(1); /* get linked */
5899 * HTML <DT> (Definition Term) element handler
5902 html_dt(HANDLER_S *hd, int ch, int cmd)
5904 if(PASS_HTML(hd->html_data)){
5905 if(cmd == GF_DATA){
5906 html_handoff(hd, ch);
5908 else if(cmd == GF_RESET){
5909 html_output_raw_tag(hd->html_data, "dt");
5911 else if(cmd == GF_EOD){
5912 html_output_string(hd->html_data, "</dt>");
5915 return(1); /* get linked */
5918 if(cmd == GF_RESET){
5919 HANDLER_S *p;
5922 * There better be a Definition Handler installed
5923 * or else we crap out...
5925 for(p = HANDLERS(hd->html_data); p && EL(p)->handler != html_dl; p = p->below)
5928 if(p){ /* adjust indent level if needed */
5929 html_indent(hd->html_data, (int) p->y, HTML_ID_SET);
5930 html_blank(hd->html_data, 1);
5932 /* BUG: else should really bitch about this */
5935 return(0); /* DON'T get linked */
5940 * HTML <DD> (Definition Definition) element handler
5943 html_dd(HANDLER_S *hd, int ch, int cmd)
5945 if(PASS_HTML(hd->html_data)){
5946 if(cmd == GF_DATA){
5947 html_handoff(hd, ch);
5949 else if(cmd == GF_RESET){
5950 html_output_raw_tag(hd->html_data, "dd");
5952 else if(cmd == GF_EOD){
5953 html_output_string(hd->html_data, "</dd>");
5956 return(1); /* get linked */
5959 if(cmd == GF_RESET){
5960 HANDLER_S *p;
5963 * There better be a Definition Handler installed
5964 * or else we crap out...
5966 for(p = HANDLERS(hd->html_data); p && EL(p)->handler != html_dl; p = p->below)
5969 if(p){ /* adjust indent level if needed */
5970 html_indent(hd->html_data, (int) p->z, HTML_ID_SET);
5971 html_blank(hd->html_data, 0);
5973 /* BUG: should really bitch about this */
5976 return(0); /* DON'T get linked */
5981 * HTML <H1> (Headings 1) element handler.
5983 * Bold, very-large font, CENTERED. One or two blank lines
5984 * above and below. For our silly character cell's that
5985 * means centered and ALL CAPS...
5988 html_h1(HANDLER_S *hd, int ch, int cmd)
5990 if(cmd == GF_DATA){
5991 html_handoff(hd, ch);
5993 else if(cmd == GF_RESET){
5994 if(PASS_HTML(hd->html_data)){
5995 html_output_raw_tag(hd->html_data, "h1");
5997 else{
5998 /* turn ON the centered bit */
5999 CENTER_BIT(hd->html_data) = 1;
6002 else if(cmd == GF_EOD){
6003 if(PASS_HTML(hd->html_data)){
6004 html_output_string(hd->html_data, "</h1>");
6006 else{
6007 /* turn OFF the centered bit, add blank line */
6008 CENTER_BIT(hd->html_data) = 0;
6009 html_blank(hd->html_data, 1);
6013 return(1); /* get linked */
6018 * HTML <H2> (Headings 2) element handler
6021 html_h2(HANDLER_S *hd, int ch, int cmd)
6023 if(cmd == GF_DATA){
6024 if(PASS_HTML(hd->html_data)){
6025 html_handoff(hd, ch);
6027 else{
6028 if((hd->x & HTML_HX_ULINE) && !ASCII_ISSPACE((unsigned char) (ch & 0xff))){
6029 HTML_ULINE(hd->html_data, 1);
6030 hd->x ^= HTML_HX_ULINE; /* only once! */
6033 html_handoff(hd, (ch < 128 && islower((unsigned char) ch))
6034 ? toupper((unsigned char) ch) : ch);
6037 else if(cmd == GF_RESET){
6038 if(PASS_HTML(hd->html_data)){
6039 html_output_raw_tag(hd->html_data, "h2");
6041 else{
6043 * Bold, large font, flush-left. One or two blank lines
6044 * above and below.
6046 if(CENTER_BIT(hd->html_data)) /* stop centering for now */
6047 hd->x = HTML_HX_CENTER;
6048 else
6049 hd->x = 0;
6051 hd->x |= HTML_HX_ULINE;
6053 CENTER_BIT(hd->html_data) = 0;
6054 hd->y = html_indent(hd->html_data, 0, HTML_ID_SET);
6055 hd->z = HD(hd->html_data)->wrapcol;
6056 HD(hd->html_data)->wrapcol = WRAP_COLS(hd->html_data) - 8;
6057 html_blank(hd->html_data, 1);
6060 else if(cmd == GF_EOD){
6061 if(PASS_HTML(hd->html_data)){
6062 html_output_string(hd->html_data, "</h2>");
6064 else{
6066 * restore previous centering, and indent level
6068 if(!(hd->x & HTML_HX_ULINE))
6069 HTML_ULINE(hd->html_data, 0);
6071 html_indent(hd->html_data, hd->y, HTML_ID_SET);
6072 html_blank(hd->html_data, 1);
6073 CENTER_BIT(hd->html_data) = (hd->x & HTML_HX_CENTER) != 0;
6074 HD(hd->html_data)->wrapcol = hd->z;
6078 return(1); /* get linked */
6083 * HTML <H3> (Headings 3) element handler
6086 html_h3(HANDLER_S *hd, int ch, int cmd)
6088 if(cmd == GF_DATA){
6089 if(!PASS_HTML(hd->html_data)){
6090 if((hd->x & HTML_HX_ULINE) && !ASCII_ISSPACE((unsigned char) (ch & 0xff))){
6091 HTML_ULINE(hd->html_data, 1);
6092 hd->x ^= HTML_HX_ULINE; /* only once! */
6096 html_handoff(hd, ch);
6098 else if(cmd == GF_RESET){
6099 if(PASS_HTML(hd->html_data)){
6100 html_output_raw_tag(hd->html_data, "h3");
6102 else{
6104 * Italic, large font, slightly indented from the left
6105 * margin. One or two blank lines above and below.
6107 if(CENTER_BIT(hd->html_data)) /* stop centering for now */
6108 hd->x = HTML_HX_CENTER;
6109 else
6110 hd->x = 0;
6112 hd->x |= HTML_HX_ULINE;
6113 CENTER_BIT(hd->html_data) = 0;
6114 hd->y = html_indent(hd->html_data, 2, HTML_ID_SET);
6115 hd->z = HD(hd->html_data)->wrapcol;
6116 HD(hd->html_data)->wrapcol = WRAP_COLS(hd->html_data) - 8;
6117 html_blank(hd->html_data, 1);
6120 else if(cmd == GF_EOD){
6121 if(PASS_HTML(hd->html_data)){
6122 html_output_string(hd->html_data, "</h3>");
6124 else{
6126 * restore previous centering, and indent level
6128 if(!(hd->x & HTML_HX_ULINE))
6129 HTML_ULINE(hd->html_data, 0);
6131 html_indent(hd->html_data, hd->y, HTML_ID_SET);
6132 html_blank(hd->html_data, 1);
6133 CENTER_BIT(hd->html_data) = (hd->x & HTML_HX_CENTER) != 0;
6134 HD(hd->html_data)->wrapcol = hd->z;
6138 return(1); /* get linked */
6143 * HTML <H4> (Headings 4) element handler
6146 html_h4(HANDLER_S *hd, int ch, int cmd)
6148 if(cmd == GF_DATA){
6149 html_handoff(hd, ch);
6151 else if(cmd == GF_RESET){
6152 if(PASS_HTML(hd->html_data)){
6153 html_output_raw_tag(hd->html_data, "h4");
6155 else{
6157 * Bold, normal font, indented more than H3. One blank line
6158 * above and below.
6160 hd->x = CENTER_BIT(hd->html_data); /* stop centering for now */
6161 CENTER_BIT(hd->html_data) = 0;
6162 hd->y = html_indent(hd->html_data, 4, HTML_ID_SET);
6163 hd->z = HD(hd->html_data)->wrapcol;
6164 HD(hd->html_data)->wrapcol = WRAP_COLS(hd->html_data) - 8;
6165 html_blank(hd->html_data, 1);
6168 else if(cmd == GF_EOD){
6169 if(PASS_HTML(hd->html_data)){
6170 html_output_string(hd->html_data, "</h4>");
6172 else{
6174 * restore previous centering, and indent level
6176 html_indent(hd->html_data, (int) hd->y, HTML_ID_SET);
6177 html_blank(hd->html_data, 1);
6178 CENTER_BIT(hd->html_data) = hd->x;
6179 HD(hd->html_data)->wrapcol = hd->z;
6183 return(1); /* get linked */
6188 * HTML <H5> (Headings 5) element handler
6191 html_h5(HANDLER_S *hd, int ch, int cmd)
6193 if(cmd == GF_DATA){
6194 html_handoff(hd, ch);
6196 else if(cmd == GF_RESET){
6197 if(PASS_HTML(hd->html_data)){
6198 html_output_raw_tag(hd->html_data, "h5");
6200 else{
6202 * Italic, normal font, indented as H4. One blank line
6203 * above.
6205 hd->x = CENTER_BIT(hd->html_data); /* stop centering for now */
6206 CENTER_BIT(hd->html_data) = 0;
6207 hd->y = html_indent(hd->html_data, 6, HTML_ID_SET);
6208 hd->z = HD(hd->html_data)->wrapcol;
6209 HD(hd->html_data)->wrapcol = WRAP_COLS(hd->html_data) - 8;
6210 html_blank(hd->html_data, 1);
6213 else if(cmd == GF_EOD){
6214 if(PASS_HTML(hd->html_data)){
6215 html_output_string(hd->html_data, "</h5>");
6217 else{
6219 * restore previous centering, and indent level
6221 html_indent(hd->html_data, (int) hd->y, HTML_ID_SET);
6222 html_blank(hd->html_data, 1);
6223 CENTER_BIT(hd->html_data) = hd->x;
6224 HD(hd->html_data)->wrapcol = hd->z;
6228 return(1); /* get linked */
6233 * HTML <H6> (Headings 6) element handler
6236 html_h6(HANDLER_S *hd, int ch, int cmd)
6238 if(cmd == GF_DATA){
6239 html_handoff(hd, ch);
6241 else if(cmd == GF_RESET){
6242 if(PASS_HTML(hd->html_data)){
6243 html_output_raw_tag(hd->html_data, "h6");
6245 else{
6247 * Bold, indented same as normal text, more than H5. One
6248 * blank line above.
6250 hd->x = CENTER_BIT(hd->html_data); /* stop centering for now */
6251 CENTER_BIT(hd->html_data) = 0;
6252 hd->y = html_indent(hd->html_data, 8, HTML_ID_SET);
6253 hd->z = HD(hd->html_data)->wrapcol;
6254 HD(hd->html_data)->wrapcol = WRAP_COLS(hd->html_data) - 8;
6255 html_blank(hd->html_data, 1);
6258 else if(cmd == GF_EOD){
6259 if(PASS_HTML(hd->html_data)){
6260 html_output_string(hd->html_data, "</h6>");
6262 else{
6264 * restore previous centering, and indent level
6266 html_indent(hd->html_data, (int) hd->y, HTML_ID_SET);
6267 html_blank(hd->html_data, 1);
6268 CENTER_BIT(hd->html_data) = hd->x;
6269 HD(hd->html_data)->wrapcol = hd->z;
6273 return(1); /* get linked */
6278 * HTML <BlockQuote> element handler
6281 html_blockquote(HANDLER_S *hd, int ch, int cmd)
6283 int j;
6284 #define HTML_BQ_INDENT 6
6286 if(cmd == GF_DATA){
6287 html_handoff(hd, ch);
6289 else if(cmd == GF_RESET){
6290 if(PASS_HTML(hd->html_data)){
6291 html_output_raw_tag(hd->html_data, "blockquote");
6293 else{
6295 * A typical rendering might be a slight extra left and
6296 * right indent, and/or italic font. The Blockquote element
6297 * causes a paragraph break, and typically provides space
6298 * above and below the quote.
6300 html_indent(hd->html_data, HTML_BQ_INDENT, HTML_ID_INC);
6301 j = HD(hd->html_data)->wrapstate;
6302 HD(hd->html_data)->wrapstate = 0;
6303 html_blank(hd->html_data, 1);
6304 HD(hd->html_data)->wrapstate = j;
6305 HD(hd->html_data)->wrapcol -= HTML_BQ_INDENT;
6308 else if(cmd == GF_EOD){
6309 if(PASS_HTML(hd->html_data)){
6310 html_output_string(hd->html_data, "</blockquote>");
6312 else{
6313 html_blank(hd->html_data, 1);
6315 j = HD(hd->html_data)->wrapstate;
6316 HD(hd->html_data)->wrapstate = 0;
6317 html_indent(hd->html_data, -(HTML_BQ_INDENT), HTML_ID_INC);
6318 HD(hd->html_data)->wrapstate = j;
6319 HD(hd->html_data)->wrapcol += HTML_BQ_INDENT;
6323 return(1); /* get linked */
6328 * HTML <Address> element handler
6331 html_address(HANDLER_S *hd, int ch, int cmd)
6333 int j;
6334 #define HTML_ADD_INDENT 2
6336 if(cmd == GF_DATA){
6337 html_handoff(hd, ch);
6339 else if(cmd == GF_RESET){
6340 if(PASS_HTML(hd->html_data)){
6341 html_output_raw_tag(hd->html_data, "address");
6343 else{
6345 * A typical rendering might be a slight extra left and
6346 * right indent, and/or italic font. The Blockquote element
6347 * causes a paragraph break, and typically provides space
6348 * above and below the quote.
6350 html_indent(hd->html_data, HTML_ADD_INDENT, HTML_ID_INC);
6351 j = HD(hd->html_data)->wrapstate;
6352 HD(hd->html_data)->wrapstate = 0;
6353 html_blank(hd->html_data, 1);
6354 HD(hd->html_data)->wrapstate = j;
6357 else if(cmd == GF_EOD){
6358 if(PASS_HTML(hd->html_data)){
6359 html_output_string(hd->html_data, "</address>");
6361 else{
6362 html_blank(hd->html_data, 1);
6364 j = HD(hd->html_data)->wrapstate;
6365 HD(hd->html_data)->wrapstate = 0;
6366 html_indent(hd->html_data, -(HTML_ADD_INDENT), HTML_ID_INC);
6367 HD(hd->html_data)->wrapstate = j;
6371 return(1); /* get linked */
6376 * HTML <PRE> (Preformatted Text) element handler
6379 html_pre(HANDLER_S *hd, int ch, int cmd)
6381 if(cmd == GF_DATA){
6383 * remove CRLF after '>' in element.
6384 * We see CRLF because wrapstate is off.
6386 switch(hd->y){
6387 case 2 :
6388 if(ch == '\012'){
6389 hd->y = 3;
6390 return(1);
6392 else
6393 html_handoff(hd, '\015');
6395 break;
6397 case 1 :
6398 if(ch == '\015'){
6399 hd->y = 2;
6400 return(1);
6403 case 3 :
6404 /* passing tags? replace CRLF with <BR> to make
6405 * sure hard newline survives in the end...
6407 if(PASS_HTML(hd->html_data))
6408 hd->y = 4; /* keep looking for CRLF */
6409 else
6410 hd->y = 0; /* stop looking */
6412 break;
6414 case 4 :
6415 if(ch == '\015'){
6416 hd->y = 5;
6417 return(1);
6420 break;
6422 case 5 :
6423 hd->y = 4;
6424 if(ch == '\012'){
6425 html_output_string(hd->html_data, "<br />");
6426 return(1);
6428 else
6429 html_handoff(hd, '\015'); /* not CRLF, pass raw CR */
6431 break;
6433 default : /* zero case */
6434 break;
6437 html_handoff(hd, ch);
6439 else if(cmd == GF_RESET){
6440 hd->y = 1;
6441 if(PASS_HTML(hd->html_data)){
6442 html_output_raw_tag(hd->html_data, "pre");
6444 else{
6445 if(hd->html_data)
6446 hd->html_data->f1 = DFL; \
6448 html_blank(hd->html_data, 1);
6449 hd->x = HD(hd->html_data)->wrapstate;
6450 HD(hd->html_data)->wrapstate = 0;
6453 else if(cmd == GF_EOD){
6454 if(PASS_HTML(hd->html_data)){
6455 html_output_string(hd->html_data, "</pre>");
6457 else{
6458 HD(hd->html_data)->wrapstate = (hd->x != 0);
6459 html_blank(hd->html_data, 0);
6463 return(1);
6468 * HTML <CENTER> (Centered Text) element handler
6471 html_center(HANDLER_S *hd, int ch, int cmd)
6473 if(cmd == GF_DATA){
6474 html_handoff(hd, ch);
6476 else if(cmd == GF_RESET){
6477 if(PASS_HTML(hd->html_data)){
6478 html_output_raw_tag(hd->html_data, "center");
6480 else{
6481 /* turn ON the centered bit */
6482 CENTER_BIT(hd->html_data) = 1;
6485 else if(cmd == GF_EOD){
6486 if(PASS_HTML(hd->html_data)){
6487 html_output_string(hd->html_data, "</center>");
6489 else{
6490 /* turn OFF the centered bit */
6491 CENTER_BIT(hd->html_data) = 0;
6495 return(1);
6500 * HTML <DIV> (Document Divisions) element handler
6503 html_div(HANDLER_S *hd, int ch, int cmd)
6505 if(cmd == GF_DATA){
6506 html_handoff(hd, ch);
6508 else if(cmd == GF_RESET){
6509 if(PASS_HTML(hd->html_data)){
6510 html_output_raw_tag(hd->html_data, "div");
6512 else{
6513 PARAMETER *p;
6515 for(p = HD(hd->html_data)->el_data->attribs;
6516 p && p->attribute;
6517 p = p->next)
6518 if(!strucmp(p->attribute, "ALIGN")){
6519 if(p->value){
6520 /* remember previous values */
6521 hd->x = CENTER_BIT(hd->html_data);
6522 hd->y = html_indent(hd->html_data, 0, HTML_ID_GET);
6524 html_blank(hd->html_data, 0);
6525 CENTER_BIT(hd->html_data) = !strucmp(p->value, "CENTER");
6526 html_indent(hd->html_data, 0, HTML_ID_SET);
6527 /* NOTE: "RIGHT" not supported yet */
6532 else if(cmd == GF_EOD){
6533 if(PASS_HTML(hd->html_data)){
6534 html_output_string(hd->html_data, "</div>");
6536 else{
6537 /* restore centered bit and indentiousness */
6538 CENTER_BIT(hd->html_data) = hd->y;
6539 html_indent(hd->html_data, hd->y, HTML_ID_SET);
6540 html_blank(hd->html_data, 0);
6544 return(1);
6549 * HTML <SPAN> (Text Span) element handler
6552 html_span(HANDLER_S *hd, int ch, int cmd)
6554 if(PASS_HTML(hd->html_data)){
6555 if(cmd == GF_DATA){
6556 html_handoff(hd, ch);
6558 else if(cmd == GF_RESET){
6559 html_output_raw_tag(hd->html_data, "span");
6561 else if(cmd == GF_EOD){
6562 html_output_string(hd->html_data, "</span>");
6565 return(1);
6568 return(0);
6573 * HTML <KBD> (Text Kbd) element handler
6576 html_kbd(HANDLER_S *hd, int ch, int cmd)
6578 if(PASS_HTML(hd->html_data)){
6579 if(cmd == GF_DATA){
6580 html_handoff(hd, ch);
6582 else if(cmd == GF_RESET){
6583 html_output_raw_tag(hd->html_data, "kbd");
6585 else if(cmd == GF_EOD){
6586 html_output_string(hd->html_data, "</kbd>");
6589 return(1);
6592 return(0);
6597 * HTML <DFN> (Text Definition) element handler
6600 html_dfn(HANDLER_S *hd, int ch, int cmd)
6602 if(PASS_HTML(hd->html_data)){
6603 if(cmd == GF_DATA){
6604 html_handoff(hd, ch);
6606 else if(cmd == GF_RESET){
6607 html_output_raw_tag(hd->html_data, "dfn");
6609 else if(cmd == GF_EOD){
6610 html_output_string(hd->html_data, "</dfn>");
6613 return(1);
6616 return(0);
6621 * HTML <TT> (Text Tt) element handler
6624 html_tt(HANDLER_S *hd, int ch, int cmd)
6626 if(PASS_HTML(hd->html_data)){
6627 if(cmd == GF_DATA){
6628 html_handoff(hd, ch);
6630 else if(cmd == GF_RESET){
6631 html_output_raw_tag(hd->html_data, "tt");
6633 else if(cmd == GF_EOD){
6634 html_output_string(hd->html_data, "</tt>");
6637 return(1);
6640 return(0);
6645 * HTML <VAR> (Text Var) element handler
6648 html_var(HANDLER_S *hd, int ch, int cmd)
6650 if(PASS_HTML(hd->html_data)){
6651 if(cmd == GF_DATA){
6652 html_handoff(hd, ch);
6654 else if(cmd == GF_RESET){
6655 html_output_raw_tag(hd->html_data, "var");
6657 else if(cmd == GF_EOD){
6658 html_output_string(hd->html_data, "</var>");
6661 return(1);
6664 return(0);
6669 * HTML <SAMP> (Text Samp) element handler
6672 html_samp(HANDLER_S *hd, int ch, int cmd)
6674 if(PASS_HTML(hd->html_data)){
6675 if(cmd == GF_DATA){
6676 html_handoff(hd, ch);
6678 else if(cmd == GF_RESET){
6679 html_output_raw_tag(hd->html_data, "samp");
6681 else if(cmd == GF_EOD){
6682 html_output_string(hd->html_data, "</samp>");
6685 return(1);
6688 return(0);
6693 * HTML <SUP> (Text Superscript) element handler
6696 html_sup(HANDLER_S *hd, int ch, int cmd)
6698 if(PASS_HTML(hd->html_data)){
6699 if(cmd == GF_DATA){
6700 html_handoff(hd, ch);
6702 else if(cmd == GF_RESET){
6703 html_output_raw_tag(hd->html_data, "sup");
6705 else if(cmd == GF_EOD){
6706 html_output_string(hd->html_data, "</sup>");
6709 return(1);
6712 return(0);
6717 * HTML <SUB> (Text Subscript) element handler
6720 html_sub(HANDLER_S *hd, int ch, int cmd)
6722 if(PASS_HTML(hd->html_data)){
6723 if(cmd == GF_DATA){
6724 html_handoff(hd, ch);
6726 else if(cmd == GF_RESET){
6727 html_output_raw_tag(hd->html_data, "sub");
6729 else if(cmd == GF_EOD){
6730 html_output_string(hd->html_data, "</sub>");
6733 return(1);
6736 return(0);
6741 * HTML <CITE> (Text Citation) element handler
6744 html_cite(HANDLER_S *hd, int ch, int cmd)
6746 if(PASS_HTML(hd->html_data)){
6747 if(cmd == GF_DATA){
6748 html_handoff(hd, ch);
6750 else if(cmd == GF_RESET){
6751 html_output_raw_tag(hd->html_data, "cite");
6753 else if(cmd == GF_EOD){
6754 html_output_string(hd->html_data, "</cite>");
6757 return(1);
6760 return(0);
6765 * HTML <CODE> (Text Code) element handler
6768 html_code(HANDLER_S *hd, int ch, int cmd)
6770 if(PASS_HTML(hd->html_data)){
6771 if(cmd == GF_DATA){
6772 html_handoff(hd, ch);
6774 else if(cmd == GF_RESET){
6775 html_output_raw_tag(hd->html_data, "code");
6777 else if(cmd == GF_EOD){
6778 html_output_string(hd->html_data, "</code>");
6781 return(1);
6784 return(0);
6789 * HTML <INS> (Text Inserted) element handler
6792 html_ins(HANDLER_S *hd, int ch, int cmd)
6794 if(PASS_HTML(hd->html_data)){
6795 if(cmd == GF_DATA){
6796 html_handoff(hd, ch);
6798 else if(cmd == GF_RESET){
6799 html_output_raw_tag(hd->html_data, "ins");
6801 else if(cmd == GF_EOD){
6802 html_output_string(hd->html_data, "</ins>");
6805 return(1);
6808 return(0);
6813 * HTML <DEL> (Text Deleted) element handler
6816 html_del(HANDLER_S *hd, int ch, int cmd)
6818 if(PASS_HTML(hd->html_data)){
6819 if(cmd == GF_DATA){
6820 html_handoff(hd, ch);
6822 else if(cmd == GF_RESET){
6823 html_output_raw_tag(hd->html_data, "del");
6825 else if(cmd == GF_EOD){
6826 html_output_string(hd->html_data, "</del>");
6829 return(1);
6832 return(0);
6837 * HTML <ABBR> (Text Abbreviation) element handler
6840 html_abbr(HANDLER_S *hd, int ch, int cmd)
6842 if(PASS_HTML(hd->html_data)){
6843 if(cmd == GF_DATA){
6844 html_handoff(hd, ch);
6846 else if(cmd == GF_RESET){
6847 html_output_raw_tag(hd->html_data, "abbr");
6849 else if(cmd == GF_EOD){
6850 html_output_string(hd->html_data, "</abbr>");
6853 return(1);
6856 return(0);
6861 * HTML <SCRIPT> element handler
6864 html_script(HANDLER_S *hd, int ch, int cmd)
6866 /* Link in and drop everything within on the floor */
6867 return(1);
6872 * HTML <APPLET> element handler
6875 html_applet(HANDLER_S *hd, int ch, int cmd)
6877 /* Link in and drop everything within on the floor */
6878 return(1);
6883 * HTML <STYLE> CSS element handler
6886 html_style(HANDLER_S *hd, int ch, int cmd)
6888 static STORE_S *css_stuff ;
6890 if(PASS_HTML(hd->html_data)){
6891 if(cmd == GF_DATA){
6892 /* collect style settings */
6893 so_writec(ch, css_stuff);
6895 else if(cmd == GF_RESET){
6896 if(css_stuff)
6897 so_give(&css_stuff);
6899 css_stuff = so_get(CharStar, NULL, EDIT_ACCESS);
6901 else if(cmd == GF_EOD){
6903 * TODO: strip anything mischievous and pass on
6906 so_give(&css_stuff);
6910 return(1);
6914 * RSS 2.0 <RSS> version
6917 rss_rss(HANDLER_S *hd, int ch, int cmd)
6919 if(cmd == GF_RESET){
6920 PARAMETER *p;
6922 for(p = HD(hd->html_data)->el_data->attribs;
6923 p && p->attribute;
6924 p = p->next)
6925 if(!strucmp(p->attribute, "VERSION")){
6926 if(p->value && !strucmp(p->value,"2.0"))
6927 return(0); /* do not link in */
6930 gf_error("Incompatible RSS version");
6931 /* NO RETURN */
6934 return(0); /* not linked or error means we never get here */
6938 * RSS 2.0 <CHANNEL>
6941 rss_channel(HANDLER_S *hd, int ch, int cmd)
6943 if(cmd == GF_DATA){
6944 html_handoff(hd, ch);
6946 else if(cmd == GF_RESET){
6947 RSS_FEED_S *feed;
6949 feed = RSS_FEED(hd->html_data) = fs_get(sizeof(RSS_FEED_S));
6950 memset(feed, 0, sizeof(RSS_FEED_S));
6953 return(1); /* link in */
6957 * RSS 2.0 <TITLE>
6960 rss_title(HANDLER_S *hd, int ch, int cmd)
6962 static STORE_S *title_so;
6964 if(cmd == GF_DATA){
6965 /* collect data */
6966 if(title_so){
6967 so_writec(ch, title_so);
6970 else if(cmd == GF_RESET){
6971 if(RSS_FEED(hd->html_data)){
6972 /* prepare for data */
6973 if(title_so)
6974 so_give(&title_so);
6976 title_so = so_get(CharStar, NULL, EDIT_ACCESS);
6979 else if(cmd == GF_EOD){
6980 if(title_so){
6981 RSS_FEED_S *feed = RSS_FEED(hd->html_data);
6982 RSS_ITEM_S *rip;
6984 if(feed){
6985 if((rip = feed->items) != NULL){
6986 for(; rip->next; rip = rip->next)
6989 if(rip->title)
6990 fs_give((void **) &rip->title);
6992 rip->title = cpystr(rss_skip_whitespace(so_text(title_so)));
6994 else{
6995 if(feed->title)
6996 fs_give((void **) &feed->title);
6998 feed->title = cpystr(rss_skip_whitespace(so_text(title_so)));
7002 so_give(&title_so);
7006 return(1); /* link in */
7010 * RSS 2.0 <IMAGE>
7013 rss_image(HANDLER_S *hd, int ch, int cmd)
7015 static STORE_S *img_so;
7017 if(cmd == GF_DATA){
7018 /* collect data */
7019 if(img_so){
7020 so_writec(ch, img_so);
7023 else if(cmd == GF_RESET){
7024 if(RSS_FEED(hd->html_data)){
7025 /* prepare to collect data */
7026 if(img_so)
7027 so_give(&img_so);
7029 img_so = so_get(CharStar, NULL, EDIT_ACCESS);
7032 else if(cmd == GF_EOD){
7033 if(img_so){
7034 RSS_FEED_S *feed = RSS_FEED(hd->html_data);
7036 if(feed){
7037 if(feed->image)
7038 fs_give((void **) &feed->image);
7040 feed->image = cpystr(rss_skip_whitespace(so_text(img_so)));
7043 so_give(&img_so);
7047 return(1); /* link in */
7051 * RSS 2.0 <LINK>
7054 rss_link(HANDLER_S *hd, int ch, int cmd)
7056 static STORE_S *link_so;
7058 if(cmd == GF_DATA){
7059 /* collect data */
7060 if(link_so){
7061 so_writec(ch, link_so);
7064 else if(cmd == GF_RESET){
7065 if(RSS_FEED(hd->html_data)){
7066 /* prepare to collect data */
7067 if(link_so)
7068 so_give(&link_so);
7070 link_so = so_get(CharStar, NULL, EDIT_ACCESS);
7073 else if(cmd == GF_EOD){
7074 if(link_so){
7075 RSS_FEED_S *feed = RSS_FEED(hd->html_data);
7076 RSS_ITEM_S *rip;
7078 if(feed){
7079 if((rip = feed->items) != NULL){
7080 for(; rip->next; rip = rip->next)
7083 if(rip->link)
7084 fs_give((void **) &rip->link);
7086 rip->link = cpystr(rss_skip_whitespace(so_text(link_so)));
7088 else{
7089 if(feed->link)
7090 fs_give((void **) &feed->link);
7092 feed->link = cpystr(rss_skip_whitespace(so_text(link_so)));
7096 so_give(&link_so);
7100 return(1); /* link in */
7104 * RSS 2.0 <DESCRIPTION>
7107 rss_description(HANDLER_S *hd, int ch, int cmd)
7109 static STORE_S *desc_so;
7111 if(cmd == GF_DATA){
7112 /* collect data */
7113 if(desc_so){
7114 so_writec(ch, desc_so);
7117 else if(cmd == GF_RESET){
7118 if(RSS_FEED(hd->html_data)){
7119 /* prepare to collect data */
7120 if(desc_so)
7121 so_give(&desc_so);
7123 desc_so = so_get(CharStar, NULL, EDIT_ACCESS);
7126 else if(cmd == GF_EOD){
7127 if(desc_so){
7128 RSS_FEED_S *feed = RSS_FEED(hd->html_data);
7129 RSS_ITEM_S *rip;
7131 if(feed){
7132 if((rip = feed->items) != NULL){
7133 for(; rip->next; rip = rip->next)
7136 if(rip->description)
7137 fs_give((void **) &rip->description);
7139 rip->description = cpystr(rss_skip_whitespace(so_text(desc_so)));
7141 else{
7142 if(feed->description)
7143 fs_give((void **) &feed->description);
7145 feed->description = cpystr(rss_skip_whitespace(so_text(desc_so)));
7149 so_give(&desc_so);
7153 return(1); /* link in */
7157 * RSS 2.0 <TTL> (in minutes)
7160 rss_ttl(HANDLER_S *hd, int ch, int cmd)
7162 RSS_FEED_S *feed = RSS_FEED(hd->html_data);
7164 if(cmd == GF_DATA){
7165 if(isdigit((unsigned char) ch))
7166 feed->ttl = ((feed->ttl * 10) + (ch - '0'));
7168 else if(cmd == GF_RESET){
7169 /* prepare to collect data */
7170 feed->ttl = 0;
7172 else if(cmd == GF_EOD){
7175 return(1); /* link in */
7179 * RSS 2.0 <ITEM>
7182 rss_item(HANDLER_S *hd, int ch, int cmd)
7184 /* BUG: verify no ITEM nesting? */
7185 if(cmd == GF_RESET){
7186 RSS_FEED_S *feed;
7188 if((feed = RSS_FEED(hd->html_data)) != NULL){
7189 RSS_ITEM_S **rip;
7190 int n = 0;
7192 for(rip = &feed->items; *rip; rip = &(*rip)->next)
7193 if(++n > RSS_ITEM_LIMIT)
7194 return(0);
7196 *rip = fs_get(sizeof(RSS_ITEM_S));
7197 memset(*rip, 0, sizeof(RSS_ITEM_S));
7201 return(0); /* don't link in */
7205 char *
7206 rss_skip_whitespace(char *s)
7208 for(; *s && isspace((unsigned char) *s); s++)
7211 return(s);
7216 * return the function associated with the given element name
7218 ELPROP_S *
7219 element_properties(FILTER_S *fd, char *el_name)
7221 register ELPROP_S *el_table = ELEMENTS(fd);
7222 size_t len_name = strlen(el_name);
7224 for(; el_table->element; el_table++)
7225 if(!strucmp(el_name, el_table->element)
7226 || (el_table->alternate
7227 && len_name == el_table->len + 1
7228 && el_name[el_table->len] == '/'
7229 && !struncmp(el_name, el_table->element, el_table->len)))
7230 return(el_table);
7232 return(NULL);
7237 * collect element's name and any attribute/value pairs then
7238 * dispatch to the appropriate handler.
7240 * Returns 1 : got what we wanted
7241 * 0 : we need more data
7242 * -1 : bogus input
7245 html_element_collector(FILTER_S *fd, int ch)
7247 if(ch == '>'){
7248 if(ED(fd)->overrun){
7250 * If problem processing, don't bother doing anything
7251 * internally, just return such that none of what we've
7252 * digested is displayed.
7254 HTML_DEBUG_EL("too long", ED(fd));
7255 return(1); /* Let it go, Jim */
7257 else if(ED(fd)->mkup_decl){
7258 if(ED(fd)->badform){
7259 dprint((2, "-- html error: bad form: %.*s\n",
7260 ED(fd)->len, ED(fd)->buf ? ED(fd)->buf : "?"));
7262 * Invalid comment -- make some guesses as
7263 * to whether we should stop with this greater-than...
7265 if(ED(fd)->buf[0] != '-'
7266 || ED(fd)->len < 4
7267 || (ED(fd)->buf[1] == '-'
7268 && ED(fd)->buf[ED(fd)->len - 1] == '-'
7269 && ED(fd)->buf[ED(fd)->len - 2] == '-'))
7270 return(1);
7272 else{
7273 dprint((5, "-- html: OK: %.*s\n",
7274 ED(fd)->len, ED(fd)->buf ? ED(fd)->buf : "?"));
7275 if(ED(fd)->start_comment == ED(fd)->end_comment){
7276 if(ED(fd)->len > 10){
7277 ED(fd)->buf[ED(fd)->len - 2] = '\0';
7278 html_element_comment(fd, ED(fd)->buf + 2);
7281 return(1);
7283 /* else keep collecting comment below */
7286 else if(ED(fd)->proc_inst){
7287 return(1); /* return without display... */
7289 else if(!ED(fd)->quoted || ED(fd)->badform){
7290 ELPROP_S *ep;
7293 * We either have the whole thing or all that we could
7294 * salvage from it. Try our best...
7297 if(HD(fd)->bitbucket)
7298 return(1); /* element inside chtml clause! */
7300 if(!ED(fd)->badform && html_element_flush(ED(fd)))
7301 return(1); /* return without display... */
7304 * If we ran into an empty tag or we don't know how to deal
7305 * with it, just go on, ignoring it...
7307 if(ED(fd)->element && (ep = element_properties(fd, ED(fd)->element))){
7308 if(ep->handler){
7309 /* dispatch the element's handler */
7310 HTML_DEBUG_EL(ED(fd)->end_tag ? "POP" : "PUSH", ED(fd));
7311 if(ED(fd)->end_tag){
7312 html_pop(fd, ep); /* remove it's handler */
7314 else{
7315 /* if a block element, pop any open <p>'s */
7316 if(ep->blocklevel){
7317 HANDLER_S *tp;
7319 for(tp = HANDLERS(fd); tp && EL(tp)->handler == html_p; tp = tp->below){
7320 HTML_DEBUG_EL("Unclosed <P>", ED(fd));
7321 html_pop(fd, EL(tp));
7322 break;
7326 /* enforce table nesting */
7327 if(!strucmp(ep->element, "tr")){
7328 if(!HANDLERS(fd) || (strucmp(EL(HANDLERS(fd))->element, "table") && strucmp(EL(HANDLERS(fd))->element, "tbody") && strucmp(EL(HANDLERS(fd))->element, "thead"))){
7329 dprint((2, "-- html error: bad nesting for <TR>, GOT %s\n", (HANDLERS(fd)) ? EL(HANDLERS(fd))->element : "NO-HANDLERS"));
7330 if(HANDLERS(fd) && !strucmp(EL(HANDLERS(fd))->element,"tr")){
7331 dprint((2, "-- html error: bad nesting popping previous <TR>"));
7332 html_pop(fd, EL(HANDLERS(fd)));
7334 else{
7335 dprint((2, "-- html error: bad nesting pusing <TABLE>"));
7336 html_push(fd, element_properties(fd, "table"));
7340 else if(!strucmp(ep->element, "td") || !strucmp(ep->element, "th")){
7341 if(!HANDLERS(fd)){
7342 dprint((2, "-- html error: bad nesting: NO HANDLERS before <TD>"));
7343 html_push(fd, element_properties(fd, "table"));
7344 html_push(fd, element_properties(fd, "tr"));
7346 else if(strucmp(EL(HANDLERS(fd))->element, "tr")){
7347 dprint((2, "-- html error: bad nesting for <TD>, GOT %s\n", EL(HANDLERS(fd))->element));
7348 html_push(fd, element_properties(fd, "tr"));
7350 else if(!strucmp(EL(HANDLERS(fd))->element, "td")){
7351 dprint((2, "-- html error: bad nesting popping <TD>"));
7352 html_pop(fd, EL(HANDLERS(fd)));
7356 /* add it's handler */
7357 if(html_push(fd, ep)){
7358 if(ED(fd)->empty){
7359 /* remove empty element */
7360 html_pop(fd, ep);
7365 else {
7366 HTML_DEBUG_EL("IGNORED", ED(fd));
7369 else{ /* else, empty or unrecognized */
7370 HTML_DEBUG_EL("?", ED(fd));
7373 return(1); /* all done! see, that didn't hurt */
7376 else if(ch == '/' && ED(fd)->element && ED(fd)->len){
7377 ED(fd)->empty = 1;
7379 else
7380 ED(fd)->empty = 0;
7382 if(ED(fd)->mkup_decl){
7383 if((ch &= 0xff) == '-'){
7384 if(ED(fd)->hyphen){
7385 ED(fd)->hyphen = 0;
7386 if(ED(fd)->start_comment)
7387 ED(fd)->end_comment = 1;
7388 else
7389 ED(fd)->start_comment = 1;
7391 else
7392 ED(fd)->hyphen = 1;
7394 else{
7395 if(ED(fd)->end_comment)
7396 ED(fd)->start_comment = ED(fd)->end_comment = 0;
7399 * no "--" after ! or non-whitespace between comments - bad
7401 if(ED(fd)->len < 2 || (!ED(fd)->start_comment
7402 && !ASCII_ISSPACE((unsigned char) ch)))
7403 ED(fd)->badform = 1; /* non-comment! */
7405 ED(fd)->hyphen = 0;
7409 * Remember the comment for possible later processing, if
7410 * it gets too long, remember first and last few chars
7411 * so we know when to terminate (and throw some garbage
7412 * in between when we toss out what's between.
7414 if(ED(fd)->len == HTML_BUF_LEN){
7415 ED(fd)->buf[2] = ED(fd)->buf[3] = 'X';
7416 ED(fd)->buf[4] = ED(fd)->buf[ED(fd)->len - 2];
7417 ED(fd)->buf[5] = ED(fd)->buf[ED(fd)->len - 1];
7418 ED(fd)->len = 6;
7421 ED(fd)->buf[(ED(fd)->len)++] = ch;
7422 return(0); /* comments go in the bit bucket */
7424 else if(ED(fd)->overrun || ED(fd)->badform){
7425 return(0); /* swallow char's until next '>' */
7427 else if(!ED(fd)->element && !ED(fd)->len){
7428 if(ch == '/'){ /* validate leading chars */
7429 ED(fd)->end_tag = 1;
7430 return(0);
7432 else if(ch == '!'){
7433 ED(fd)->mkup_decl = 1;
7434 return(0);
7436 else if(ch == '?'){
7437 ED(fd)->proc_inst = 1;
7438 return(0);
7440 else if(!isalpha((unsigned char) ch))
7441 return(-1); /* can't be a tag! */
7443 else if(ch == '\"' || ch == '\''){
7444 if(!ED(fd)->hit_equal){
7445 ED(fd)->badform = 1; /* quote in element name?!? */
7446 return(0);
7449 if(ED(fd)->quoted){
7450 if(ED(fd)->quoted == (char) ch){
7451 /* end of a quoted value */
7452 ED(fd)->quoted = 0;
7453 if(ED(fd)->len && html_element_flush(ED(fd)))
7454 ED(fd)->badform = 1;
7456 return(0); /* continue collecting chars */
7458 /* ELSE fall thru writing other quoting char */
7460 else{
7461 ED(fd)->quoted = (char) ch;
7462 ED(fd)->was_quoted = 1;
7463 return(0); /* need more data */
7466 else if (ASCII_ISSPACE((unsigned char) ch))
7467 ED(fd)->unquoted_data = 0;
7468 else if (ED(fd)->hit_equal)
7469 ED(fd)->unquoted_data = 1;
7471 ch &= 0xff; /* strip any "literal" high bits */
7472 if(ED(fd)->quoted
7473 || ED(fd)->unquoted_data
7474 || isalnum(ch)
7475 || strchr("#-.!", ch)){
7476 if(ED(fd)->len < ((ED(fd)->element || !ED(fd)->hit_equal)
7477 ? HTML_BUF_LEN:MAX_ELEMENT)){
7478 ED(fd)->buf[(ED(fd)->len)++] = ch;
7480 else
7481 ED(fd)->overrun = 1; /* flag it broken */
7483 else if(ASCII_ISSPACE((unsigned char) ch) || ch == '='){
7484 if((ED(fd)->len || ED(fd)->was_quoted) && html_element_flush(ED(fd))){
7485 ED(fd)->badform = 1;
7486 return(0); /* else, we ain't done yet */
7489 if(!ED(fd)->hit_equal)
7490 ED(fd)->hit_equal = (ch == '=');
7492 else if(ch == '/' && ED(fd)->len && !ED(fd)->element){
7493 ELPROP_S *ep;
7494 ep = element_properties(fd, ED(fd)->buf);
7495 if(ep){
7496 if(!ep->alternate)
7497 ED(fd)->badform = 1;
7498 else{
7499 if(ED(fd)->len < ((ED(fd)->element || !ED(fd)->hit_equal)
7500 ? HTML_BUF_LEN:MAX_ELEMENT)){
7501 ED(fd)->buf[(ED(fd)->len)++] = ch; /* add this exception */
7503 else
7504 ED(fd)->overrun = 1;
7507 else
7508 ED(fd)->badform = 1;
7510 else
7511 ED(fd)->badform = 1; /* unrecognized data?? */
7513 return(0); /* keep collecting */
7518 * Element collector found complete string, integrate it and reset
7519 * internal collection buffer.
7521 * Returns zero if element collection buffer flushed, error flag otherwise
7524 html_element_flush(CLCTR_S *el_data)
7526 int rv = 0;
7528 if(el_data->hit_equal){ /* adding a value */
7529 el_data->hit_equal = 0;
7530 if(el_data->cur_attrib){
7531 if(!el_data->cur_attrib->value){
7532 el_data->cur_attrib->value = cpystr(el_data->len
7533 ? el_data->buf : "");
7535 else{
7536 dprint((2, "** element: unexpected value: %.10s...\n",
7537 (el_data->len && el_data->buf) ? el_data->buf : "\"\""));
7538 rv = 1;
7541 else{
7542 dprint((2, "** element: missing attribute name: %.10s...\n",
7543 (el_data->len && el_data->buf) ? el_data->buf : "\"\""));
7544 rv = 2;
7547 else if(el_data->len){
7548 if(!el_data->element){
7549 el_data->element = cpystr(el_data->buf);
7551 else{
7552 PARAMETER *p = (PARAMETER *)fs_get(sizeof(PARAMETER));
7553 memset(p, 0, sizeof(PARAMETER));
7554 if(el_data->attribs){
7555 el_data->cur_attrib->next = p;
7556 el_data->cur_attrib = p;
7558 else
7559 el_data->attribs = el_data->cur_attrib = p;
7561 p->attribute = cpystr(el_data->buf);
7566 el_data->was_quoted = 0; /* reset collector buf and state */
7567 el_data->len = 0;
7568 memset(el_data->buf, 0, HTML_BUF_LEN);
7569 return(rv); /* report whatever happened above */
7574 * html_element_comment - "Special" comment handling here
7576 void
7577 html_element_comment(FILTER_S *f, char *s)
7579 char *p;
7581 while(*s && ASCII_ISSPACE((unsigned char) *s))
7582 s++;
7585 * WARNING: "!--chtml" denotes "Conditional HTML", a UW-ism.
7587 if(!struncmp(s, "chtml ", 6)){
7588 s += 6;
7589 if(!struncmp(s, "if ", 3)){
7590 HD(f)->bitbucket = 1; /* default is failure! */
7591 switch(*(s += 3)){
7592 case 'P' :
7593 case 'p' :
7594 if(!struncmp(s + 1, "inemode=", 8)){
7595 if(!strucmp(s = removing_quotes(s + 9), "function_key")
7596 && F_ON(F_USE_FK, ps_global))
7597 HD(f)->bitbucket = 0;
7598 else if(!strucmp(s, "running"))
7599 HD(f)->bitbucket = 0;
7600 #ifdef _WINDOWS
7601 else if(!strucmp(s, "os_windows"))
7602 HD(f)->bitbucket = 0;
7603 #endif
7606 break;
7608 case '[' : /* test */
7609 if((p = strindex(++s, ']')) != NULL){
7610 *p = '\0'; /* tie off test string */
7611 removing_leading_white_space(s);
7612 removing_trailing_white_space(s);
7613 if(*s == '-' && *(s+1) == 'r'){ /* readable file? */
7614 for(s += 2; *s && ASCII_ISSPACE((unsigned char) *s); s++)
7618 HD(f)->bitbucket = (can_access(CHTML_VAR_EXPAND(removing_quotes(s)),
7619 READ_ACCESS) != 0);
7623 break;
7625 default :
7626 break;
7629 else if(!strucmp(s, "else")){
7630 HD(f)->bitbucket = !HD(f)->bitbucket;
7632 else if(!strucmp(s, "endif")){
7633 /* Clean up after chtml here */
7634 HD(f)->bitbucket = 0;
7637 else if(!HD(f)->bitbucket){
7638 if(!struncmp(s, "#include ", 9)){
7639 char buf[MAILTMPLEN], *bufp;
7640 int len, end_of_line;
7641 FILE *fp;
7643 /* Include the named file */
7644 if(!struncmp(s += 9, "file=", 5)
7645 && (fp = our_fopen(CHTML_VAR_EXPAND(removing_quotes(s+5)), "r"))){
7646 html_element_output(f, HTML_NEWLINE);
7648 while(fgets(buf, sizeof(buf), fp)){
7649 if((len = strlen(buf)) && buf[len-1] == '\n'){
7650 end_of_line = 1;
7651 buf[--len] = '\0';
7653 else
7654 end_of_line = 0;
7656 for(bufp = buf; len; bufp++, len--)
7657 html_element_output(f, (int) *bufp);
7659 if(end_of_line)
7660 html_element_output(f, HTML_NEWLINE);
7663 fclose(fp);
7664 html_element_output(f, HTML_NEWLINE);
7665 HD(f)->blanks = 0;
7666 if(f->f1 == WSPACE)
7667 f->f1 = DFL;
7670 else if(!struncmp(s, "#echo ", 6)){
7671 if(!struncmp(s += 6, "var=", 4)){
7672 char *p, buf[MAILTMPLEN];
7673 ADDRESS *adr;
7674 extern char datestamp[];
7676 if(!strcmp(s = removing_quotes(s + 4), "ALPINE_VERSION")){
7677 p = ALPINE_VERSION;
7679 else if(!strcmp(s, "ALPINE_REVISION")){
7680 p = get_alpine_revision_string(buf, sizeof(buf));
7682 else if(!strcmp(s, "C_CLIENT_VERSION")){
7683 p = CCLIENTVERSION;
7685 else if(!strcmp(s, "ALPINE_COMPILE_DATE")){
7686 p = datestamp;
7688 else if(!strcmp(s, "ALPINE_TODAYS_DATE")){
7689 rfc822_date(p = buf);
7691 else if(!strcmp(s, "_LOCAL_FULLNAME_")){
7692 p = (ps_global->VAR_LOCAL_FULLNAME
7693 && ps_global->VAR_LOCAL_FULLNAME[0])
7694 ? ps_global->VAR_LOCAL_FULLNAME
7695 : "Local Support";
7697 else if(!strcmp(s, "_LOCAL_ADDRESS_")){
7698 p = (ps_global->VAR_LOCAL_ADDRESS
7699 && ps_global->VAR_LOCAL_ADDRESS[0])
7700 ? ps_global->VAR_LOCAL_ADDRESS
7701 : "postmaster";
7702 adr = rfc822_parse_mailbox(&p, ps_global->maildomain);
7703 snprintf(p = buf, sizeof(buf), "%s@%s", adr->mailbox, adr->host);
7704 mail_free_address(&adr);
7706 else if(!strcmp(s, "_BUGS_FULLNAME_")){
7707 p = (ps_global->VAR_BUGS_FULLNAME
7708 && ps_global->VAR_BUGS_FULLNAME[0])
7709 ? ps_global->VAR_BUGS_FULLNAME
7710 : "Place to report Alpine Bugs";
7712 else if(!strcmp(s, "_BUGS_ADDRESS_")){
7713 p = (ps_global->VAR_BUGS_ADDRESS
7714 && ps_global->VAR_BUGS_ADDRESS[0])
7715 ? ps_global->VAR_BUGS_ADDRESS : "postmaster";
7716 adr = rfc822_parse_mailbox(&p, ps_global->maildomain);
7717 snprintf(p = buf, sizeof(buf), "%s@%s", adr->mailbox, adr->host);
7718 mail_free_address(&adr);
7720 else if(!strcmp(s, "CURRENT_DIR")){
7721 getcwd(p = buf, sizeof(buf));
7723 else if(!strcmp(s, "HOME_DIR")){
7724 p = ps_global->home_dir;
7726 else if(!strcmp(s, "PINE_CONF_PATH")){
7727 #if defined(_WINDOWS) || !defined(SYSTEM_PINERC)
7728 p = "/usr/local/lib/pine.conf";
7729 #else
7730 p = SYSTEM_PINERC;
7731 #endif
7733 else if(!strcmp(s, "PINE_CONF_FIXED_PATH")){
7734 #ifdef SYSTEM_PINERC_FIXED
7735 p = SYSTEM_PINERC_FIXED;
7736 #else
7737 p = "/usr/local/lib/pine.conf.fixed";
7738 #endif
7740 else if(!strcmp(s, "PINE_INFO_PATH")){
7741 p = SYSTEM_PINE_INFO_PATH;
7743 else if(!strcmp(s, "MAIL_SPOOL_PATH")){
7744 p = sysinbox();
7746 else if(!strcmp(s, "MAIL_SPOOL_LOCK_PATH")){
7747 /* Don't put the leading /tmp/. */
7748 int i, j;
7750 p = sysinbox();
7751 if(p){
7752 for(j = 0, i = 0; p[i] && j < MAILTMPLEN - 1; i++){
7753 if(p[i] == '/')
7754 buf[j++] = '\\';
7755 else
7756 buf[j++] = p[i];
7758 buf[j++] = '\0';
7759 p = buf;
7762 else if(!struncmp(s, "VAR_", 4)){
7763 p = s+4;
7764 if(pith_opt_pretty_var_name)
7765 p = (*pith_opt_pretty_var_name)(p);
7767 else if(!struncmp(s, "FEAT_", 5)){
7768 p = s+5;
7769 if(pith_opt_pretty_feature_name)
7770 p = (*pith_opt_pretty_feature_name)(p, -1);
7772 else
7773 p = NULL;
7775 if(p){
7776 if(f->f1 == WSPACE){
7777 html_element_output(f, ' ');
7778 f->f1 = DFL; /* clear it */
7781 while(*p)
7782 html_element_output(f, (int) *p++);
7790 void
7791 html_element_output(FILTER_S *f, int ch)
7793 if(HANDLERS(f))
7794 (*EL(HANDLERS(f))->handler)(HANDLERS(f), ch, GF_DATA);
7795 else
7796 html_output(f, ch);
7799 #define ISHEX_DIGIT(X) (isdigit((X)) || \
7800 ((X) >= 'a' && (X) <= 'f') || \
7801 ((X) >= 'A' && (X) <= 'F'))
7804 * collect html entity and return its UCS value when done.
7806 * Returns HTML_MOREDATA : we need more data
7807 * HTML_ENTITY : entity collected
7808 * HTML_BADVALUE : good data, but no named match or out of range
7809 * HTML_BADDATA : invalid input
7811 * NOTES:
7812 * - entity format is "'&' tag ';'" and represents a literal char
7813 * - named entities are CASE SENSITIVE.
7814 * - numeric char references (where the tag is prefixed with a '#')
7815 * are a char with that numbers value
7816 * - numeric vals are 0-255 except for the ranges: 0-8, 11-31, 127-159.
7819 html_entity_collector(FILTER_S *f, int ch, UCS *ucs, char **alt)
7821 static int len = 0;
7822 static char buf[MAX_ENTITY+2];
7823 int rv, i;
7825 if(len == MAX_ENTITY){
7826 rv = HTML_BADDATA;
7828 else if((len == 0)
7829 ? (isalpha((unsigned char) ch) || ch == '#')
7830 : ((isdigit((unsigned char) ch)
7831 || (len == 1 && (unsigned char) ch == 'x')
7832 || (len == 1 &&(unsigned char) ch == 'X')
7833 || (len > 1 && isxdigit((unsigned char) ch))
7834 || (isalpha((unsigned char) ch) && buf[0] != '#')))){
7835 buf[len++] = ch;
7836 return(HTML_MOREDATA);
7838 else if(ch == ';' || ASCII_ISSPACE((unsigned char) ch)){
7839 buf[len] = '\0'; /* got something! */
7840 if(buf[0] == '#'){
7841 if(buf[1] == 'x' || buf[1] == 'X')
7842 *ucs = (UCS) strtoul(&buf[2], NULL, 16);
7843 else
7844 *ucs = (UCS) strtoul(&buf[1], NULL, 10);
7846 if(alt){
7847 *alt = NULL;
7848 for(i = 0; i < sizeof(entity_tab)/sizeof(struct html_entities); i++)
7849 if(entity_tab[i].value == *ucs){
7850 *alt = entity_tab[i].plain;
7851 break;
7855 len = 0;
7856 return(HTML_ENTITY);
7858 else{
7859 rv = HTML_BADVALUE; /* in case of no match */
7860 for(i = 0; i < sizeof(entity_tab)/sizeof(struct html_entities); i++)
7861 if(strcmp(entity_tab[i].name, buf) == 0){
7862 *ucs = entity_tab[i].value;
7863 if(alt)
7864 *alt = entity_tab[i].plain;
7866 len = 0;
7867 return(HTML_ENTITY);
7871 else
7872 rv = HTML_BADDATA; /* bogus input! */
7874 if(alt){
7875 buf[len] = '\0';
7876 *alt = buf;
7879 len = 0;
7880 return(rv);
7884 /*----------------------------------------------------------------------
7885 HTML text to plain text filter
7887 This basically tries to do the best it can with HTML 2.0 (RFC1866)
7888 with bits of RFC 1942 (plus some HTML 3.2 thrown in as well) text
7889 formatting.
7891 ----*/
7892 void
7893 gf_html2plain(FILTER_S *f, int flg)
7895 /* BUG: quote incoming \255 values (see "yuml" above!) */
7896 if(flg == GF_DATA){
7897 register int c;
7898 GF_INIT(f, f->next);
7900 if(!HTML_WROTE(f)){
7901 int ii;
7903 for(ii = HTML_INDENT(f); ii > 0; ii--)
7904 html_putc(f, ' ');
7906 HTML_WROTE(f) = 1;
7909 while(GF_GETC(f, c)){
7911 * First we have to collect any literal entities...
7912 * that is, IF we're not already collecting one
7913 * AND we're not in element's text or, if we are, we're
7914 * not in quoted text. Whew.
7916 if(f->t){
7917 char *alt = NULL;
7918 UCS ucs;
7920 switch(html_entity_collector(f, c, &ucs, &alt)){
7921 case HTML_MOREDATA: /* more data required? */
7922 continue; /* go get another char */
7924 case HTML_BADVALUE :
7925 case HTML_BADDATA :
7926 /* if supplied, process bogus data */
7927 HTML_PROC(f, '&');
7928 for(; *alt; alt++){
7929 unsigned int uic = *alt;
7930 HTML_PROC(f, uic);
7933 if(c == '&' && !HD(f)->quoted){
7934 f->t = '&';
7935 continue;
7937 else
7938 f->t = 0; /* don't come back next time */
7940 break;
7942 default : /* thing to process */
7943 f->t = 0; /* don't come back */
7946 * do something with UCS codepoint. If it's
7947 * not displayable then use the alt version
7948 * otherwise
7949 * cvt UCS to UTF-8 and toss into next filter.
7951 if(ucs > 127 && wcellwidth(ucs) < 0){
7952 if(alt){
7953 for(; *alt; alt++){
7954 c = MAKE_LITERAL(*alt);
7955 HTML_PROC(f, c);
7958 continue;
7960 else
7961 c = MAKE_LITERAL('?');
7963 else{
7964 unsigned char utf8buf[8], *p1, *p2;
7966 p2 = utf8_put(p1 = (unsigned char *) utf8buf, (unsigned long) ucs);
7967 for(; p1 < p2; p1++){
7968 c = MAKE_LITERAL(*p1);
7969 HTML_PROC(f, c);
7972 continue;
7975 break;
7978 else if(!PASS_HTML(f) && c == '&' && !HD(f)->quoted){
7979 f->t = '&';
7980 continue;
7984 * then we process whatever we got...
7987 HTML_PROC(f, c);
7990 GF_OP_END(f); /* clean up our input pointers */
7992 else if(flg == GF_EOD){
7993 while(HANDLERS(f)){
7994 dprint((2, "-- html error: no closing tag for %s",EL(HANDLERS(f))->element));
7995 html_pop(f, EL(HANDLERS(f)));
7998 html_output(f, HTML_NEWLINE);
7999 if(ULINE_BIT(f))
8000 HTML_ULINE(f, ULINE_BIT(f) = 0);
8002 if(BOLD_BIT(f))
8003 HTML_BOLD(f, BOLD_BIT(f) = 0);
8005 HTML_FLUSH(f);
8006 fs_give((void **)&f->line);
8007 if(HD(f)->color)
8008 free_color_pair(&HD(f)->color);
8010 fs_give(&f->data);
8011 if(f->opt){
8012 if(((HTML_OPT_S *)f->opt)->base)
8013 fs_give((void **) &((HTML_OPT_S *)f->opt)->base);
8015 fs_give(&f->opt);
8018 (*f->next->f)(f->next, GF_DATA);
8019 (*f->next->f)(f->next, GF_EOD);
8021 else if(flg == GF_RESET){
8022 dprint((9, "-- gf_reset html2plain\n"));
8023 f->data = (HTML_DATA_S *) fs_get(sizeof(HTML_DATA_S));
8024 memset(f->data, 0, sizeof(HTML_DATA_S));
8025 /* start with flowing text */
8026 HD(f)->wrapstate = !PASS_HTML(f);
8027 HD(f)->wrapcol = WRAP_COLS(f);
8028 f->f1 = DFL; /* state */
8029 f->f2 = 0; /* chars in wrap buffer */
8030 f->n = 0L; /* chars on line so far */
8031 f->linep = f->line = (char *)fs_get(HTML_BUF_LEN * sizeof(char));
8032 HD(f)->line_bufsize = HTML_BUF_LEN; /* initial bufsize of line */
8033 HD(f)->alt_entity = (!ps_global->display_charmap
8034 || strucmp(ps_global->display_charmap, "iso-8859-1"));
8035 HD(f)->cb.cbufp = HD(f)->cb.cbufend = HD(f)->cb.cbuf;
8042 * html_indent - do the requested indent level function with appropriate
8043 * flushing and such.
8045 * Returns: indent level prior to set/increment
8048 html_indent(FILTER_S *f, int val, int func)
8050 int old = HD(f)->indent_level;
8052 /* flush pending data at old indent level */
8053 switch(func){
8054 case HTML_ID_INC :
8055 html_output_flush(f);
8056 if((HD(f)->indent_level += val) < 0)
8057 HD(f)->indent_level = 0;
8059 break;
8061 case HTML_ID_SET :
8062 html_output_flush(f);
8063 HD(f)->indent_level = val;
8064 break;
8066 default :
8067 break;
8070 return(old);
8076 * html_blanks - Insert n blank lines into output
8078 void
8079 html_blank(FILTER_S *f, int n)
8081 /* Cap off any flowing text, and then write blank lines */
8082 if(f->f2 || f->n || CENTER_BIT(f) || HD(f)->centered || WRAPPED_LEN(f))
8083 html_output(f, HTML_NEWLINE);
8085 if(HD(f)->wrapstate)
8086 while(HD(f)->blanks < n) /* blanks inc'd by HTML_NEWLINE */
8087 html_output(f, HTML_NEWLINE);
8093 * html_newline -- insert a newline mindful of embedded tags
8095 void
8096 html_newline(FILTER_S *f)
8098 html_write_newline(f); /* commit an actual newline */
8100 if(f->n){ /* and keep track of blank lines */
8101 HD(f)->blanks = 0;
8102 f->n = 0L;
8104 else
8105 HD(f)->blanks++;
8110 * output the given char, handling any requested wrapping.
8111 * It's understood that all whitespace handed us is written. In other
8112 * words, junk whitespace is weeded out before it's given to us here.
8115 void
8116 html_output(FILTER_S *f, int ch)
8118 UCS uc;
8119 int width;
8120 void (*o_f)(FILTER_S *, int, int, int) = CENTER_BIT(f) ? html_output_centered : html_output_normal;
8123 * if ch is a control token, just pass it on, else, collect
8124 * utf8-encoded characters to determine width,then feed into
8125 * output routines
8127 if(ch == TAG_EMBED || HD(f)->embedded.state || (ch > 0xff && IS_LITERAL(ch) == 0)){
8128 (*o_f)(f, ch, 1, 0);
8130 else if(utf8_to_ucs4_oneatatime(ch & 0xff, &(HD(f)->cb), &uc, &width)){
8131 unsigned char *cp;
8133 for(cp = HD(f)->cb.cbuf; cp <= HD(f)->cb.cbufend; cp++){
8134 (*o_f)(f, *cp, width, HD(f)->cb.cbufend - cp);
8135 width = 0; /* only count it once */
8138 HD(f)->cb.cbufp = HD(f)->cb.cbufend = HD(f)->cb.cbuf;
8140 else
8141 HD(f)->cb.cbufend = HD(f)->cb.cbufp;
8142 /* else do nothing until we have a full character */
8146 void
8147 html_output_string(FILTER_S *f, char *s)
8149 for(; *s; s++)
8150 html_output(f, *s);
8154 void
8155 html_output_raw_tag(FILTER_S *f, char *tag)
8157 PARAMETER *p;
8158 char *vp;
8159 int i;
8161 html_output(f, '<');
8162 html_output_string(f, tag);
8163 for(p = HD(f)->el_data->attribs;
8164 p && p->attribute;
8165 p = p->next){
8166 /* SECURITY: no javascript */
8167 /* PRIVACY: no img src without permission */
8168 /* BUGS: no class collisions since <head> ignored */
8169 if(html_event_attribute(p->attribute)
8170 || !strucmp(p->attribute, "class")
8171 || (!PASS_IMAGES(f) && !strucmp(tag, "img") && !strucmp(p->attribute, "src")))
8172 continue;
8174 /* PRIVACY: sniff out background images */
8175 if(p->value && !PASS_IMAGES(f)){
8176 if(!strucmp(p->attribute, "style")){
8177 if((vp = srchstr(p->value, "background-image")) != NULL){
8178 /* neuter in place */
8179 vp[11] = vp[12] = vp[13] = vp[14] = vp[15] = 'X';
8181 else{
8182 for(vp = p->value; (vp = srchstr(vp, "background")) != NULL; vp++)
8183 if(vp[10] == ' ' || vp[10] == ':')
8184 for(i = 11; vp[i] && vp[i] != ';'; i++)
8185 if((vp[i] == 'u' && vp[i+1] == 'r' && vp[i+2] == 'l' && vp[i+3] == '(')
8186 || vp[i] == ':' || vp[i] == '/' || vp[i] == '.')
8187 vp[0] = 'X';
8190 else if(!strucmp(p->attribute, "background")){
8191 char *ip;
8193 for(ip = p->value; *ip && !(*ip == ':' || *ip == '/' || *ip == '.'); ip++)
8196 if(ip)
8197 continue;
8201 html_output(f, ' ');
8202 html_output_string(f, p->attribute);
8203 if(p->value){
8204 html_output(f, '=');
8205 html_output(f, '\"');
8206 html_output_string(f, p->value);
8207 html_output(f, '\"');
8211 /* append warning to form submission */
8212 if(!strucmp(tag, "form")){
8213 html_output_string(f, " onsubmit=\"return window.confirm('This form is submitting information to an outside server.\\nAre you sure?');\"");
8216 if(ED(f)->end_tag){
8217 html_output(f, ' ');
8218 html_output(f, '/');
8221 html_output(f, '>');
8226 html_event_attribute(char *attr)
8228 int i;
8229 static char *events[] = {
8230 "onabort", "onblur", "onchange", "onclick", "ondblclick", "ondragdrop",
8231 "onerror", "onfocus", "onkeydown", "onkeypress", "onkeyup", "onload",
8232 "onmousedown", "onmousemove", "onmouseout", "onmouseover", "onmouseup", "onmove",
8233 "onreset", "onresize", "onselec", "onsubmit", "onunload"
8236 if((attr[0] == 'o' || attr[0] == 'O') && (attr[1] == 'n' || attr[1] == 'N'))
8237 for(i = 0; i < sizeof(events)/sizeof(events[0]); i++)
8238 if(!strucmp(attr, events[i]))
8239 return(TRUE);
8241 return(FALSE);
8245 void
8246 html_output_normal(FILTER_S *f, int ch, int width, int remaining)
8248 static int written = 0;
8249 static int cwidth;
8251 if(HD(f)->centered){
8252 html_centered_flush(f);
8253 fs_give((void **) &HD(f)->centered->line.buf);
8254 fs_give((void **) &HD(f)->centered->word.buf);
8255 fs_give((void **) &HD(f)->centered);
8258 if(HD(f)->wrapstate){
8259 if(ch == HTML_NEWLINE){ /* hard newline */
8260 html_output_flush(f);
8261 html_newline(f);
8263 else
8264 HD(f)->blanks = 0; /* reset blank line counter */
8266 if(ch == TAG_EMBED){ /* takes up no space */
8267 HD(f)->embedded.state = -5;
8268 HTML_LINEP_PUTC(f, TAG_EMBED);
8270 else if(HD(f)->embedded.state){ /* ditto */
8271 if(HD(f)->embedded.state == -5){
8272 /* looking for specially handled tags following TAG_EMBED */
8273 if(ch == TAG_HANDLE)
8274 HD(f)->embedded.state = -1; /* next ch is length */
8275 else if(ch == TAG_FGCOLOR || ch == TAG_BGCOLOR){
8276 if(!HD(f)->color)
8277 HD(f)->color = new_color_pair(NULL, NULL);
8279 if(ch == TAG_FGCOLOR)
8280 HD(f)->embedded.color = HD(f)->color->fg;
8281 else
8282 HD(f)->embedded.color = HD(f)->color->bg;
8284 HD(f)->embedded.state = RGBLEN;
8286 else
8287 HD(f)->embedded.state = 0; /* non-special */
8289 else if(HD(f)->embedded.state > 0){
8290 /* collecting up an RGBLEN color or length, ignore tags */
8291 (HD(f)->embedded.state)--;
8292 if(HD(f)->embedded.color)
8293 *HD(f)->embedded.color++ = ch;
8295 if(HD(f)->embedded.state == 0 && HD(f)->embedded.color){
8296 *HD(f)->embedded.color = '\0';
8297 HD(f)->embedded.color = NULL;
8300 else if(HD(f)->embedded.state < 0){
8301 HD(f)->embedded.state = ch; /* number of embedded chars */
8303 else{
8304 (HD(f)->embedded.state)--;
8305 if(HD(f)->embedded.color)
8306 *HD(f)->embedded.color++ = ch;
8308 if(HD(f)->embedded.state == 0 && HD(f)->embedded.color){
8309 *HD(f)->embedded.color = '\0';
8310 HD(f)->embedded.color = NULL;
8314 HTML_LINEP_PUTC(f, ch);
8316 else if(HTML_ISSPACE(ch)){
8317 html_output_flush(f);
8319 else{
8320 if(HD(f)->prefix)
8321 html_a_prefix(f);
8323 if(written == 0)
8324 cwidth = width;
8326 if(f->f2 + cwidth + 1 >= WRAP_COLS(f)){
8327 HTML_LINEP_PUTC(f, ch & 0xff);
8328 written++;
8329 if(remaining == 0){
8330 HTML_FLUSH(f);
8331 html_newline(f);
8333 if(HD(f)->in_anchor)
8334 html_write_anchor(f, HD(f)->in_anchor);
8336 else{
8337 HTML_LINEP_PUTC(f, ch & 0xff);
8338 written++;
8341 if(remaining == 0){
8342 written = 0;
8343 f->f2 += cwidth;
8347 else{
8348 if(HD(f)->prefix)
8349 html_a_prefix(f);
8351 html_output_flush(f);
8353 switch(HD(f)->embedded.state){
8354 case 0 :
8355 switch(ch){
8356 default :
8358 * It's difficult to both preserve whitespace and wrap at the
8359 * same time so we'll do a dumb wrap at the edge of the screen.
8360 * Since this shouldn't come up much in real life we'll hope
8361 * it is good enough.
8363 if(!PASS_HTML(f) && (f->n + width) > WRAP_COLS(f))
8364 html_newline(f);
8366 f->n += width; /* inc displayed char count */
8367 HD(f)->blanks = 0; /* reset blank line counter */
8368 html_putc(f, ch & 0xff);
8369 break;
8371 case TAG_EMBED : /* takes up no space */
8372 html_putc(f, TAG_EMBED);
8373 HD(f)->embedded.state = -2;
8374 break;
8376 case HTML_NEWLINE : /* newline handling */
8377 if(!f->n)
8378 break;
8380 case '\n' :
8381 html_newline(f);
8383 case '\r' :
8384 break;
8387 break;
8389 case -2 :
8390 HD(f)->embedded.state = 0;
8391 switch(ch){
8392 case TAG_HANDLE :
8393 HD(f)->embedded.state = -1; /* next ch is length */
8394 break;
8396 case TAG_BOLDON :
8397 BOLD_BIT(f) = 1;
8398 break;
8400 case TAG_BOLDOFF :
8401 BOLD_BIT(f) = 0;
8402 break;
8404 case TAG_ULINEON :
8405 ULINE_BIT(f) = 1;
8406 break;
8408 case TAG_ULINEOFF :
8409 ULINE_BIT(f) = 0;
8410 break;
8412 case TAG_FGCOLOR :
8413 if(!HD(f)->color)
8414 HD(f)->color = new_color_pair(NULL, NULL);
8416 HD(f)->embedded.color = HD(f)->color->fg;
8417 HD(f)->embedded.state = 11;
8418 break;
8420 case TAG_BGCOLOR :
8421 if(!HD(f)->color)
8422 HD(f)->color = new_color_pair(NULL, NULL);
8424 HD(f)->embedded.color = HD(f)->color->bg;
8425 HD(f)->embedded.state = 11;
8426 break;
8428 case TAG_HANDLEOFF :
8429 ch = TAG_INVOFF;
8430 HD(f)->in_anchor = 0;
8431 break;
8433 default :
8434 break;
8437 html_putc(f, ch);
8438 break;
8440 case -1 :
8441 HD(f)->embedded.state = ch; /* number of embedded chars */
8442 html_putc(f, ch);
8443 break;
8445 default :
8446 HD(f)->embedded.state--;
8447 if(HD(f)->embedded.color)
8448 *HD(f)->embedded.color++ = ch;
8450 if(HD(f)->embedded.state == 0 && HD(f)->embedded.color){
8451 *HD(f)->embedded.color = '\0';
8452 HD(f)->embedded.color = NULL;
8455 html_putc(f, ch);
8456 break;
8463 * flush any buffered chars waiting for wrapping.
8465 void
8466 html_output_flush(FILTER_S *f)
8468 if(f->f2){
8469 if(f->n && ((int) f->n) + 1 + f->f2 > HD(f)->wrapcol)
8470 html_newline(f); /* wrap? */
8472 if(f->n){ /* text already on the line? */
8473 html_putc(f, ' ');
8474 f->n++; /* increment count */
8476 else{
8477 /* write at start of new line */
8478 html_write_indent(f, HD(f)->indent_level);
8480 if(HD(f)->in_anchor)
8481 html_write_anchor(f, HD(f)->in_anchor);
8484 f->n += f->f2;
8485 HTML_FLUSH(f);
8492 * html_output_centered - managed writing centered text
8494 void
8495 html_output_centered(FILTER_S *f, int ch, int width, int remaining)
8497 static int written;
8498 static int cwidth;
8500 if(!HD(f)->centered){ /* new text? */
8501 html_output_flush(f);
8502 if(f->n) /* start on blank line */
8503 html_newline(f);
8505 HD(f)->centered = (CENTER_S *) fs_get(sizeof(CENTER_S));
8506 memset(HD(f)->centered, 0, sizeof(CENTER_S));
8507 /* and grab a buf to start collecting centered text */
8508 HD(f)->centered->line.len = WRAP_COLS(f);
8509 HD(f)->centered->line.buf = (char *) fs_get(HD(f)->centered->line.len
8510 * sizeof(char));
8511 HD(f)->centered->line.used = HD(f)->centered->line.width = 0;
8512 HD(f)->centered->word.len = 32;
8513 HD(f)->centered->word.buf = (char *) fs_get(HD(f)->centered->word.len
8514 * sizeof(char));
8515 HD(f)->centered->word.used = HD(f)->centered->word.width = 0;
8518 if(ch == HTML_NEWLINE){ /* hard newline */
8519 html_centered_flush(f);
8521 else if(ch == TAG_EMBED){ /* takes up no space */
8522 HD(f)->embedded.state = -5;
8523 html_centered_putc(&HD(f)->centered->word, TAG_EMBED);
8525 else if(HD(f)->embedded.state){
8526 if(HD(f)->embedded.state == -5){
8527 /* looking for specially handled tags following TAG_EMBED */
8528 if(ch == TAG_HANDLE)
8529 HD(f)->embedded.state = -1; /* next ch is length */
8530 else if(ch == TAG_FGCOLOR || ch == TAG_BGCOLOR){
8531 if(!HD(f)->color)
8532 HD(f)->color = new_color_pair(NULL, NULL);
8534 if(ch == TAG_FGCOLOR)
8535 HD(f)->embedded.color = HD(f)->color->fg;
8536 else
8537 HD(f)->embedded.color = HD(f)->color->bg;
8539 HD(f)->embedded.state = RGBLEN;
8541 else
8542 HD(f)->embedded.state = 0; /* non-special */
8544 else if(HD(f)->embedded.state > 0){
8545 /* collecting up an RGBLEN color or length, ignore tags */
8546 (HD(f)->embedded.state)--;
8547 if(HD(f)->embedded.color)
8548 *HD(f)->embedded.color++ = ch;
8550 if(HD(f)->embedded.state == 0 && HD(f)->embedded.color){
8551 *HD(f)->embedded.color = '\0';
8552 HD(f)->embedded.color = NULL;
8555 else if(HD(f)->embedded.state < 0){
8556 HD(f)->embedded.state = ch; /* number of embedded chars */
8558 else{
8559 (HD(f)->embedded.state)--;
8560 if(HD(f)->embedded.color)
8561 *HD(f)->embedded.color++ = ch;
8563 if(HD(f)->embedded.state == 0 && HD(f)->embedded.color){
8564 *HD(f)->embedded.color = '\0';
8565 HD(f)->embedded.color = NULL;
8569 html_centered_putc(&HD(f)->centered->word, ch);
8571 else if(ASCII_ISSPACE((unsigned char) ch)){
8572 if(!HD(f)->centered->space++){ /* end of a word? flush! */
8573 int i;
8575 if(WRAPPED_LEN(f) > HD(f)->wrapcol){
8576 html_centered_flush_line(f);
8577 /* fall thru to put current "word" on blank "line" */
8579 else if(HD(f)->centered->line.width){
8580 /* put space char between line and appended word */
8581 html_centered_putc(&HD(f)->centered->line, ' ');
8582 HD(f)->centered->line.width++;
8585 for(i = 0; i < HD(f)->centered->word.used; i++)
8586 html_centered_putc(&HD(f)->centered->line,
8587 HD(f)->centered->word.buf[i]);
8589 HD(f)->centered->line.width += HD(f)->centered->word.width;
8590 HD(f)->centered->word.used = 0;
8591 HD(f)->centered->word.width = 0;
8594 else{
8595 if(HD(f)->prefix)
8596 html_a_prefix(f);
8598 /* ch is start of next word */
8599 HD(f)->centered->space = 0;
8600 if(HD(f)->centered->word.width >= WRAP_COLS(f))
8601 html_centered_flush(f);
8603 html_centered_putc(&HD(f)->centered->word, ch);
8605 if(written == 0)
8606 cwidth = width;
8608 written++;
8610 if(remaining == 0){
8611 written = 0;
8612 HD(f)->centered->word.width += cwidth;
8619 * html_centered_putc -- add given char to given WRAPLINE_S
8621 void
8622 html_centered_putc(WRAPLINE_S *wp, int ch)
8624 if(wp->used + 1 >= wp->len){
8625 wp->len += 64;
8626 fs_resize((void **) &wp->buf, wp->len * sizeof(char));
8629 wp->buf[wp->used++] = ch;
8635 * html_centered_flush - finish writing any pending centered output
8637 void
8638 html_centered_flush(FILTER_S *f)
8640 int i;
8643 * If word present (what about line?) we need to deal with
8644 * appending it...
8646 if(HD(f)->centered->word.width && WRAPPED_LEN(f) > HD(f)->wrapcol)
8647 html_centered_flush_line(f);
8649 if(WRAPPED_LEN(f)){
8650 /* figure out how much to indent */
8651 if((i = (WRAP_COLS(f) - WRAPPED_LEN(f))/2) > 0)
8652 html_write_indent(f, i);
8654 if(HD(f)->centered->anchor)
8655 html_write_anchor(f, HD(f)->centered->anchor);
8657 html_centered_handle(&HD(f)->centered->anchor,
8658 HD(f)->centered->line.buf,
8659 HD(f)->centered->line.used);
8660 html_write(f, HD(f)->centered->line.buf, HD(f)->centered->line.used);
8662 if(HD(f)->centered->word.used){
8663 if(HD(f)->centered->line.width)
8664 html_putc(f, ' ');
8666 html_centered_handle(&HD(f)->centered->anchor,
8667 HD(f)->centered->word.buf,
8668 HD(f)->centered->word.used);
8669 html_write(f, HD(f)->centered->word.buf,
8670 HD(f)->centered->word.used);
8673 HD(f)->centered->line.used = HD(f)->centered->word.used = 0;
8674 HD(f)->centered->line.width = HD(f)->centered->word.width = 0;
8676 else{
8677 if(HD(f)->centered->word.used){
8678 html_write(f, HD(f)->centered->word.buf,
8679 HD(f)->centered->word.used);
8680 HD(f)->centered->line.used = HD(f)->centered->word.used = 0;
8681 HD(f)->centered->line.width = HD(f)->centered->word.width = 0;
8683 HD(f)->blanks++; /* advance the blank line counter */
8686 html_newline(f); /* finish the line */
8691 * html_centered_handle - scan the line for embedded handles
8693 void
8694 html_centered_handle(int *h, char *line, int len)
8696 int n;
8698 while(len-- > 0)
8699 if(*line++ == TAG_EMBED && len-- > 0)
8700 switch(*line++){
8701 case TAG_HANDLE :
8702 if((n = *line++) >= --len){
8703 *h = 0;
8704 len -= n;
8705 while(n--)
8706 *h = (*h * 10) + (*line++ - '0');
8708 break;
8710 case TAG_HANDLEOFF :
8711 case TAG_INVOFF :
8712 *h = 0; /* assumption 23,342: inverse off ends tags */
8713 break;
8715 default :
8716 break;
8723 * html_centered_flush_line - flush the centered "line" only
8725 void
8726 html_centered_flush_line(FILTER_S *f)
8728 if(HD(f)->centered->line.used){
8729 int i, j;
8731 /* hide "word" from flush */
8732 i = HD(f)->centered->word.used;
8733 j = HD(f)->centered->word.width;
8734 HD(f)->centered->word.used = 0;
8735 HD(f)->centered->word.width = 0;
8736 html_centered_flush(f);
8738 HD(f)->centered->word.used = i;
8739 HD(f)->centered->word.width = j;
8745 * html_write_indent - write indention mindful of display attributes
8747 void
8748 html_write_indent(FILTER_S *f, int indent)
8750 if(! STRIP(f)){
8751 if(BOLD_BIT(f)){
8752 html_putc(f, TAG_EMBED);
8753 html_putc(f, TAG_BOLDOFF);
8756 if(ULINE_BIT(f)){
8757 html_putc(f, TAG_EMBED);
8758 html_putc(f, TAG_ULINEOFF);
8762 f->n = indent;
8763 while(indent-- > 0)
8764 html_putc(f, ' '); /* indent as needed */
8767 * Resume any previous embedded state
8769 if(! STRIP(f)){
8770 if(BOLD_BIT(f)){
8771 html_putc(f, TAG_EMBED);
8772 html_putc(f, TAG_BOLDON);
8775 if(ULINE_BIT(f)){
8776 html_putc(f, TAG_EMBED);
8777 html_putc(f, TAG_ULINEON);
8786 void
8787 html_write_anchor(FILTER_S *f, int anchor)
8789 char buf[256];
8790 int i;
8792 html_putc(f, TAG_EMBED);
8793 html_putc(f, TAG_HANDLE);
8794 snprintf(buf, sizeof(buf), "%d", anchor);
8795 html_putc(f, (int) strlen(buf));
8797 for(i = 0; buf[i]; i++)
8798 html_putc(f, buf[i]);
8803 * html_write_newline - write a newline mindful of display attributes
8805 void
8806 html_write_newline(FILTER_S *f)
8808 int i;
8810 if(! STRIP(f)){ /* First tie, off any embedded state */
8811 if(HD(f)->in_anchor){
8812 html_putc(f, TAG_EMBED);
8813 html_putc(f, TAG_INVOFF);
8816 if(BOLD_BIT(f)){
8817 html_putc(f, TAG_EMBED);
8818 html_putc(f, TAG_BOLDOFF);
8821 if(ULINE_BIT(f)){
8822 html_putc(f, TAG_EMBED);
8823 html_putc(f, TAG_ULINEOFF);
8826 if(HD(f)->color && (HD(f)->color->fg[0] || HD(f)->color->bg[0])){
8827 char *p;
8828 int i;
8830 p = color_embed(ps_global->VAR_NORM_FORE_COLOR,
8831 ps_global->VAR_NORM_BACK_COLOR);
8832 for(i = 0; i < 2 * (RGBLEN + 2); i++)
8833 html_putc(f, p[i]);
8837 html_write(f, "\015\012", 2);
8838 for(i = HTML_INDENT(f); i > 0; i--)
8839 html_putc(f, ' ');
8841 if(! STRIP(f)){ /* First tie, off any embedded state */
8842 if(BOLD_BIT(f)){
8843 html_putc(f, TAG_EMBED);
8844 html_putc(f, TAG_BOLDON);
8847 if(ULINE_BIT(f)){
8848 html_putc(f, TAG_EMBED);
8849 html_putc(f, TAG_ULINEON);
8852 if(HD(f)->color && (HD(f)->color->fg[0] || HD(f)->color->bg[0])){
8853 char *p, *tfg, *tbg;
8854 int i;
8855 COLOR_PAIR *tmp;
8857 tfg = HD(f)->color->fg;
8858 tbg = HD(f)->color->bg;
8859 tmp = new_color_pair(tfg[0] ? tfg
8860 : color_to_asciirgb(ps_global->VAR_NORM_FORE_COLOR),
8861 tbg[0] ? tbg
8862 : color_to_asciirgb(ps_global->VAR_NORM_BACK_COLOR));
8863 if(pico_is_good_colorpair(tmp)){
8864 p = color_embed(tfg[0] ? tfg
8865 : ps_global->VAR_NORM_FORE_COLOR,
8866 tbg[0] ? tbg
8867 : ps_global->VAR_NORM_BACK_COLOR);
8868 for(i = 0; i < 2 * (RGBLEN + 2); i++)
8869 html_putc(f, p[i]);
8872 if(tmp)
8873 free_color_pair(&tmp);
8880 * html_write - write given n-length string to next filter
8882 void
8883 html_write(FILTER_S *f, char *s, int n)
8885 GF_INIT(f, f->next);
8887 while(n-- > 0){
8888 /* keep track of attribute state? Not if last char! */
8889 if(!STRIP(f) && *s == TAG_EMBED && n-- > 0){
8890 GF_PUTC(f->next, TAG_EMBED);
8891 switch(*++s){
8892 case TAG_BOLDON :
8893 BOLD_BIT(f) = 1;
8894 break;
8895 case TAG_BOLDOFF :
8896 BOLD_BIT(f) = 0;
8897 break;
8898 case TAG_ULINEON :
8899 ULINE_BIT(f) = 1;
8900 break;
8901 case TAG_ULINEOFF :
8902 ULINE_BIT(f) = 0;
8903 break;
8904 case TAG_HANDLEOFF :
8905 HD(f)->in_anchor = 0;
8906 GF_PUTC(f->next, TAG_INVOFF);
8907 s++;
8908 continue;
8909 case TAG_HANDLE :
8910 if(n-- > 0){
8911 int i = *++s;
8913 GF_PUTC(f->next, TAG_HANDLE);
8914 if(i <= n){
8915 int anum = 0;
8916 HANDLE_S *h;
8918 n -= i;
8919 GF_PUTC(f->next, i);
8920 while(1){
8921 anum = (anum * 10) + (*++s - '0');
8922 if(--i)
8923 GF_PUTC(f->next, *s);
8924 else
8925 break;
8928 if(DO_HANDLES(f)
8929 && (h = get_handle(*HANDLESP(f), anum)) != NULL
8930 && (h->type == URL || h->type == Attach)){
8931 HD(f)->in_anchor = anum;
8936 break;
8937 default:
8938 break;
8942 GF_PUTC(f->next, (*s++) & 0xff);
8945 GF_IP_END(f->next); /* clean up next's input pointers */
8950 * html_putc -- actual work of writing to next filter.
8951 * NOTE: Small opt not using full GF_END since our input
8952 * pointers don't need adjusting.
8954 void
8955 html_putc(FILTER_S *f, int ch)
8957 GF_INIT(f, f->next);
8958 GF_PUTC(f->next, ch & 0xff);
8959 GF_IP_END(f->next); /* clean up next's input pointers */
8965 * Only current option is to turn on embedded data stripping for text
8966 * bound to a printer or composer.
8968 void *
8969 gf_html2plain_opt(char *base,
8970 int columns,
8971 int *margin,
8972 HANDLE_S **handlesp,
8973 htmlrisk_t risk_f,
8974 int flags)
8976 HTML_OPT_S *op;
8977 int margin_l, margin_r;
8979 op = (HTML_OPT_S *) fs_get(sizeof(HTML_OPT_S));
8981 op->base = cpystr(base);
8982 margin_l = (margin) ? margin[0] : 0;
8983 margin_r = (margin) ? margin[1] : 0;
8984 op->indent = margin_l;
8985 op->columns = columns - (margin_l + margin_r);
8986 op->strip = ((flags & GFHP_STRIPPED) == GFHP_STRIPPED);
8987 op->handlesp = handlesp;
8988 op->handles_loc = ((flags & GFHP_LOCAL_HANDLES) == GFHP_LOCAL_HANDLES);
8989 op->showserver = ((flags & GFHP_SHOW_SERVER) == GFHP_SHOW_SERVER);
8990 op->warnrisk_f = risk_f;
8991 op->no_relative_links = ((flags & GFHP_NO_RELATIVE) == GFHP_NO_RELATIVE);
8992 op->related_content = ((flags & GFHP_RELATED_CONTENT) == GFHP_RELATED_CONTENT);
8993 op->html = ((flags & GFHP_HTML) == GFHP_HTML);
8994 op->html_imgs = ((flags & GFHP_HTML_IMAGES) == GFHP_HTML_IMAGES);
8995 op->element_table = html_element_table;
8996 return((void *) op);
9000 void *
9001 gf_html2plain_rss_opt(RSS_FEED_S **feedp, int flags)
9003 HTML_OPT_S *op;
9005 op = (HTML_OPT_S *) fs_get(sizeof(HTML_OPT_S));
9006 memset(op, 0, sizeof(HTML_OPT_S));
9008 op->base = cpystr("");
9009 op->element_table = rss_element_table;
9010 *(op->feedp = feedp) = NULL;
9011 return((void *) op);
9014 void
9015 gf_html2plain_rss_free(RSS_FEED_S **feedp)
9017 if(feedp && *feedp){
9018 if((*feedp)->title)
9019 fs_give((void **) &(*feedp)->title);
9021 if((*feedp)->link)
9022 fs_give((void **) &(*feedp)->link);
9024 if((*feedp)->description)
9025 fs_give((void **) &(*feedp)->description);
9027 if((*feedp)->source)
9028 fs_give((void **) &(*feedp)->source);
9030 if((*feedp)->image)
9031 fs_give((void **) &(*feedp)->image);
9033 gf_html2plain_rss_free_items(&((*feedp)->items));
9034 fs_give((void **) feedp);
9038 void
9039 gf_html2plain_rss_free_items(RSS_ITEM_S **itemp)
9041 if(itemp && *itemp){
9042 if((*itemp)->title)
9043 fs_give((void **) &(*itemp)->title);
9045 if((*itemp)->link)
9046 fs_give((void **) &(*itemp)->link);
9048 if((*itemp)->description)
9049 fs_give((void **) &(*itemp)->description);
9051 if((*itemp)->source)
9052 fs_give((void **) &(*itemp)->source);
9054 gf_html2plain_rss_free_items(&(*itemp)->next);
9055 fs_give((void **) itemp);
9059 char *
9060 cid_tempfile_name(unsigned char *line, long n, int *is_cidp)
9062 int f2 = 0;
9063 int i, found;
9064 char *s, *t = NULL, *u;
9065 char imgfile[1024];
9066 char *extp = NULL;
9069 s = NULL;
9070 *is_cidp = 0;
9071 if(n > 0){
9072 if (line[0] == '\"') f2 = 1;
9073 if (n - f2 > 3){
9074 if (!struncmp(line+f2, "cid:", 4)){
9075 *is_cidp = 1;
9076 f2 += 4;
9077 s = fs_get((n - f2 + 4)*sizeof(char));
9078 snprintf(s, n - f2 + 2, "<%s", line+f2);
9079 if (s[strlen(s)-1] == '\"')
9080 s[strlen(s)-1] = '>';
9081 else{
9082 i = strlen(s);
9083 s[i] = '>';
9084 s[i + 1] = '\0';
9086 /* find the tmpdir where all these files will be saved to */
9087 if(t == NULL){
9088 for(i = 0; ps_global->atmts[i].tmpdir == NULL && ps_global->atmts[i].description != NULL; i++);
9089 t = ps_global->atmts[i].description ? ps_global->atmts[i].tmpdir : NULL;
9092 /* now we need to look for s in the list of attachments */
9093 for (i = 0, found = 0; found == 0 && ps_global->atmts[i].description != NULL; i++)
9094 if (ps_global->atmts[i].body
9095 && ps_global->atmts[i].body->type == TYPEIMAGE
9096 && strcmp(ps_global->atmts[i].body->id, s) == 0){
9097 found++;
9098 break;
9101 fs_give((void **) &s);
9102 if(found && ps_global->atmts[i].cid_tmpfile == NULL){
9103 PARAMETER *param;
9104 if (ps_global->atmts[i].cid_tmpfile == NULL){
9105 for(param = ps_global->atmts[i].body->parameter; param ; param = param->next){
9106 if (!strucmp(param->attribute, "NAME")){
9107 strncpy(imgfile, param->value, sizeof(imgfile));
9108 imgfile[sizeof(imgfile)-1] = '\0';
9109 extp = strrchr(imgfile, '.');
9110 if(extp) extp++;
9113 ps_global->atmts[i].cid_tmpfile = temp_nam_ext(t, "tmp-img-", extp);
9116 if(found && ps_global->atmts[i].cid_tmpfile != NULL)
9117 s = strstr(ps_global->atmts[i].cid_tmpfile, "tmp-img-");
9121 return s;
9124 #define COLLECT(X, C) { \
9125 if((X)->n == buflen){ \
9126 fs_resize((void **) &((X)->line), buflen + 1024); \
9127 (X)->linep = (X)->line + buflen; \
9128 buflen += 1024; \
9130 *((X)->linep)++ = (C); \
9131 (X)->n = (X)->linep - (X)->line; \
9134 #define RESET_FILTER(X) { \
9135 (X)->linep = (X)->line; \
9136 (X)->n = 0L; \
9139 void
9140 gf_html_cid2file(FILTER_S *f, int cmd)
9142 register char *p;
9143 register unsigned char c;
9144 static long buflen = 0L;
9146 GF_INIT(f, f->next);
9148 if(cmd == GF_DATA){
9149 register int state = f->f1;
9151 while(GF_GETC(f, c)){
9153 if(state == 0){ /* look for "<img " */
9154 if (c == '<') f->f2 = 1;
9155 else if(f->f2 > 0){
9156 if (f->f2 == 1 && (c == 'i' || c == 'I')) f->f2 = 2;
9157 else if (f->f2 == 2 && (c == 'm' || c == 'M')) f->f2 = 3;
9158 else if (f->f2 == 3 && (c == 'g' || c == 'G')) f->f2 = 4;
9159 else if (f->f2 == 4 && HTML_ISSPACE(c)){ f->f2 = 0; state = 1; }
9160 else f->f2 = 0;
9163 else if(state == 1){ /* look for "src=" */
9164 if (c == 's' || c == 'S') f->f2 = 1;
9165 else if (f->f2 == 1 && (c == 'r' || c == 'R')) f->f2 = 2;
9166 else if (f->f2 == 2 && (c == 'c' || c == 'C')) f->f2 = 3;
9167 else if (f->f2 == 3 && c == '='){ GF_PUTC(f->next, c); state = 2; }
9168 else if (f->f2 == 3 && !HTML_ISSPACE(c)) f->f2 = 0;
9169 else f->f2 = 0;
9171 else if (state == 2){ /* collect all data */
9172 if(HTML_ISSPACE(c) || c == '>'){
9173 long n;
9174 int is_cid;
9175 if(f->n > 0){
9176 char *s = cid_tempfile_name(f->line, f->n, &is_cid);
9177 if(is_cid){
9178 RESET_FILTER(f);
9179 if(s != NULL)
9180 for(; *s != '\0'; s++)
9181 COLLECT(f, *s);
9184 GF_PUTC(f->next, '\"');
9185 if(is_cid || f->t){
9186 for(p = f->line; f->n; f->n--, p++){
9187 if(*p == '\"') continue;
9188 GF_PUTC(f->next, *p);
9191 else f->n = 0;
9192 GF_PUTC(f->next, '\"');
9193 GF_PUTC(f->next, c);
9194 state = HTML_ISSPACE(c) ? 1 : 0;
9195 RESET_FILTER(f);
9197 else COLLECT(f, c); /* collect this data */
9200 p = f->line;
9201 if(state < 2)
9202 GF_PUTC(f->next, c);
9205 f->f1 = state;
9206 GF_END(f, f->next);
9208 else if(cmd == GF_EOD){
9209 if(f->f1 == 2){
9210 char *s = cid_tempfile_name(f->line, f->n, &f->f2);
9211 GF_PUTC(f->next, '\"');
9212 if (f->f2 || f->t){
9213 for(p = s; *p; p++){
9214 if(*p == '\"') continue;
9215 GF_PUTC(f->next, *p);
9218 GF_PUTC(f->next, '\"');
9219 GF_PUTC(f->next, '>');
9222 buflen = 0;
9223 fs_give((void **)&(f->line)); /* free temp line buffer */
9224 (void) GF_FLUSH(f->next);
9225 (*f->next->f)(f->next, GF_EOD);
9227 else if(cmd == GF_RESET){
9228 dprint((9, "-- gf_reset cid2file\n"));
9229 f->n = 0L; /* number of bytes in buffer */
9230 f->f1 = 0; /* state */
9231 f->f2 = 0; /* total number of bytes read that match pattern */
9232 f->t = *(char *)f->opt;
9236 /* END OF HTML-TO-PLAIN text filter */
9239 * ESCAPE CODE FILTER - remove unknown and possibly dangerous escape codes
9240 * from the text stream.
9243 #define MAX_ESC_LEN 5
9246 * the simple filter, removes unknown escape codes from the stream
9248 void
9249 gf_escape_filter(FILTER_S *f, int flg)
9251 register char *p;
9252 GF_INIT(f, f->next);
9254 if(flg == GF_DATA){
9255 register unsigned char c;
9256 register int state = f->f1;
9258 while(GF_GETC(f, c)){
9260 if(state){
9261 if(c == '\033' || f->n == MAX_ESC_LEN){
9262 f->line[f->n] = '\0';
9263 f->n = 0L;
9264 if(!match_escapes(f->line)){
9265 GF_PUTC(f->next, '^');
9266 GF_PUTC(f->next, '[');
9268 else
9269 GF_PUTC(f->next, '\033');
9271 p = f->line;
9272 while(*p)
9273 GF_PUTC(f->next, *p++);
9275 if(c == '\033')
9276 continue;
9277 else
9278 state = 0; /* fall thru */
9280 else{
9281 f->line[f->n++] = c; /* collect */
9282 continue;
9286 if(c == '\033')
9287 state = 1;
9288 else
9289 GF_PUTC(f->next, c);
9292 f->f1 = state;
9293 GF_END(f, f->next);
9295 else if(flg == GF_EOD){
9296 if(f->f1){
9297 if(!match_escapes(f->line)){
9298 GF_PUTC(f->next, '^');
9299 GF_PUTC(f->next, '[');
9301 else
9302 GF_PUTC(f->next, '\033');
9305 for(p = f->line; f->n; f->n--, p++)
9306 GF_PUTC(f->next, *p);
9308 fs_give((void **)&(f->line)); /* free temp line buffer */
9309 (void) GF_FLUSH(f->next);
9310 (*f->next->f)(f->next, GF_EOD);
9312 else if(flg == GF_RESET){
9313 dprint((9, "-- gf_reset escape\n"));
9314 f->f1 = 0;
9315 f->n = 0L;
9316 f->linep = f->line = (char *)fs_get((MAX_ESC_LEN + 1) * sizeof(char));
9323 * CONTROL CHARACTER FILTER - transmogrify control characters into their
9324 * corresponding string representations (you know, ^blah and such)...
9328 * the simple filter transforms unknown control characters in the stream
9329 * into harmless strings.
9331 void
9332 gf_control_filter(FILTER_S *f, int flg)
9334 GF_INIT(f, f->next);
9336 if(flg == GF_DATA){
9337 register unsigned char c;
9338 register int filt_only_c0;
9340 filt_only_c0 = f->opt ? (*(int *) f->opt) : 0;
9342 while(GF_GETC(f, c)){
9344 if(((c < 0x20 || c == 0x7f)
9345 || (c >= 0x80 && c < 0xA0 && !filt_only_c0))
9346 && !(ASCII_ISSPACE((unsigned char) c)
9347 || c == '\016' || c == '\017' || c == '\033')){
9348 GF_PUTC(f->next, c >= 0x80 ? '~' : '^');
9349 GF_PUTC(f->next, (c == 0x7f) ? '?' : (c & 0x1f) + '@');
9351 else
9352 GF_PUTC(f->next, c);
9355 GF_END(f, f->next);
9357 else if(flg == GF_EOD){
9358 (void) GF_FLUSH(f->next);
9359 (*f->next->f)(f->next, GF_EOD);
9365 * function called from the outside to set
9366 * control filter's option, which says to filter C0 control characters
9367 * but not C1 control chars. We don't call it at all if we don't want
9368 * to filter C0 chars either.
9370 void *
9371 gf_control_filter_opt(int *filt_only_c0)
9373 return((void *) filt_only_c0);
9378 * TAG FILTER - quote all TAG_EMBED characters by doubling them.
9379 * This prevents the possibility of embedding other tags.
9380 * We assume that this filter should only be used for something
9381 * that is eventually writing to a display, which has the special
9382 * knowledge of quoted TAG_EMBEDs.
9384 void
9385 gf_tag_filter(FILTER_S *f, int flg)
9387 GF_INIT(f, f->next);
9389 if(flg == GF_DATA){
9390 register unsigned char c;
9392 while(GF_GETC(f, c)){
9394 if((c & 0xff) == (TAG_EMBED & 0xff)){
9395 GF_PUTC(f->next, TAG_EMBED);
9396 GF_PUTC(f->next, c);
9398 else
9399 GF_PUTC(f->next, c);
9402 GF_END(f, f->next);
9404 else if(flg == GF_EOD){
9405 (void) GF_FLUSH(f->next);
9406 (*f->next->f)(f->next, GF_EOD);
9412 * LINEWRAP FILTER - insert CRLF's at end of nearest whitespace before
9413 * specified line width
9417 typedef struct wrap_col_s {
9418 unsigned bold:1;
9419 unsigned uline:1;
9420 unsigned inverse:1;
9421 unsigned tags:1;
9422 unsigned do_indent:1;
9423 unsigned on_comma:1;
9424 unsigned flowed:1;
9425 unsigned delsp:1;
9426 unsigned quoted:1;
9427 unsigned allwsp:1;
9428 unsigned hard_nl:1;
9429 unsigned leave_flowed:1;
9430 unsigned use_color:1;
9431 unsigned hdr_color:1;
9432 unsigned for_compose:1;
9433 unsigned handle_soft_hyphen:1;
9434 unsigned saw_soft_hyphen:1;
9435 unsigned trailing_space:1;
9436 unsigned char utf8buf[7];
9437 unsigned char *utf8bufp;
9438 COLOR_PAIR *color;
9439 STORE_S *spaces;
9440 short embedded,
9441 space_len;
9442 char *lineendp;
9443 int anchor,
9444 prefbrk,
9445 prefbrkn,
9446 quote_depth,
9447 quote_count,
9448 sig,
9449 state,
9450 wrap_col,
9451 wrap_max,
9452 margin_l,
9453 margin_r,
9454 indent;
9455 char special[256];
9456 } WRAP_S;
9458 #define WRAP_MARG_L(F) (((WRAP_S *)(F)->opt)->margin_l)
9459 #define WRAP_MARG_R(F) (((WRAP_S *)(F)->opt)->margin_r)
9460 #define WRAP_COL(F) (((WRAP_S *)(F)->opt)->wrap_col - WRAP_MARG_R(F) - ((((WRAP_S *)(F)->opt)->leave_flowed) ? 1 : 0))
9461 #define WRAP_MAX_COL(F) (((WRAP_S *)(F)->opt)->wrap_max - WRAP_MARG_R(F) - ((((WRAP_S *)(F)->opt)->leave_flowed) ? 1 : 0))
9462 #define WRAP_INDENT(F) (((WRAP_S *)(F)->opt)->indent)
9463 #define WRAP_DO_IND(F) (((WRAP_S *)(F)->opt)->do_indent)
9464 #define WRAP_COMMA(F) (((WRAP_S *)(F)->opt)->on_comma)
9465 #define WRAP_FLOW(F) (((WRAP_S *)(F)->opt)->flowed)
9466 #define WRAP_DELSP(F) (((WRAP_S *)(F)->opt)->delsp)
9467 #define WRAP_FL_QD(F) (((WRAP_S *)(F)->opt)->quote_depth)
9468 #define WRAP_FL_QC(F) (((WRAP_S *)(F)->opt)->quote_count)
9469 #define WRAP_FL_SIG(F) (((WRAP_S *)(F)->opt)->sig)
9470 #define WRAP_HARD(F) (((WRAP_S *)(F)->opt)->hard_nl)
9471 #define WRAP_LV_FLD(F) (((WRAP_S *)(F)->opt)->leave_flowed)
9472 #define WRAP_USE_CLR(F) (((WRAP_S *)(F)->opt)->use_color)
9473 #define WRAP_HDR_CLR(F) (((WRAP_S *)(F)->opt)->hdr_color)
9474 #define WRAP_FOR_CMPS(F) (((WRAP_S *)(F)->opt)->for_compose)
9475 #define WRAP_HANDLE_SOFT_HYPHEN(F) (((WRAP_S *)(F)->opt)->handle_soft_hyphen)
9476 #define WRAP_SAW_SOFT_HYPHEN(F) (((WRAP_S *)(F)->opt)->saw_soft_hyphen)
9477 #define WRAP_UTF8BUF(F, C) (((WRAP_S *)(F)->opt)->utf8buf[C])
9478 #define WRAP_UTF8BUFP(F) (((WRAP_S *)(F)->opt)->utf8bufp)
9479 #define WRAP_STATE(F) (((WRAP_S *)(F)->opt)->state)
9480 #define WRAP_QUOTED(F) (((WRAP_S *)(F)->opt)->quoted)
9481 #define WRAP_TAGS(F) (((WRAP_S *)(F)->opt)->tags)
9482 #define WRAP_BOLD(F) (((WRAP_S *)(F)->opt)->bold)
9483 #define WRAP_ULINE(F) (((WRAP_S *)(F)->opt)->uline)
9484 #define WRAP_INVERSE(F) (((WRAP_S *)(F)->opt)->inverse)
9485 #define WRAP_LASTC(F) (((WRAP_S *)(F)->opt)->lineendp)
9486 #define WRAP_EMBED(F) (((WRAP_S *)(F)->opt)->embedded)
9487 #define WRAP_ANCHOR(F) (((WRAP_S *)(F)->opt)->anchor)
9488 #define WRAP_PB_OFF(F) (((WRAP_S *)(F)->opt)->prefbrk)
9489 #define WRAP_PB_LEN(F) (((WRAP_S *)(F)->opt)->prefbrkn)
9490 #define WRAP_ALLWSP(F) (((WRAP_S *)(F)->opt)->allwsp)
9491 #define WRAP_SPC_LEN(F) (((WRAP_S *)(F)->opt)->space_len)
9492 #define WRAP_TRL_SPC(F) (((WRAP_S *)(F)->opt)->trailing_space)
9493 #define WRAP_SPEC(F, C) ((WRAP_S *) (F)->opt)->special[C]
9494 #define WRAP_COLOR(F) (((WRAP_S *)(F)->opt)->color)
9495 #define WRAP_COLOR_SET(F) ((WRAP_COLOR(F)) && (WRAP_COLOR(F)->fg[0]))
9496 #define WRAP_SPACES(F) (((WRAP_S *)(F)->opt)->spaces)
9497 #define WRAP_PUTC(F,C,W) { \
9498 if((F)->linep == WRAP_LASTC(F)){ \
9499 size_t offset = (F)->linep - (F)->line; \
9500 fs_resize((void **) &(F)->line, \
9501 (2 * offset) * sizeof(char)); \
9502 (F)->linep = &(F)->line[offset]; \
9503 WRAP_LASTC(F) = &(F)->line[2*offset-1]; \
9505 *(F)->linep++ = (C); \
9506 (F)->f2 += (W); \
9509 #define WRAP_EMBED_PUTC(F,C) { \
9510 if((F)->f2){ \
9511 WRAP_PUTC((F), C, 0); \
9513 else \
9514 so_writec(C, WRAP_SPACES(F)); \
9517 #define WRAP_COLOR_UNSET(F) { \
9518 if(WRAP_COLOR_SET(F)){ \
9519 WRAP_COLOR(F)->fg[0] = '\0'; \
9524 * wrap_flush_embed flags
9526 #define WFE_NONE 0 /* Nothing special */
9527 #define WFE_CNT_HANDLE 1 /* account for/don't write handles */
9530 int wrap_flush(FILTER_S *, unsigned char **, unsigned char **, unsigned char **, unsigned char **);
9531 int wrap_flush_embed(FILTER_S *, unsigned char **, unsigned char **,
9532 unsigned char **, unsigned char **);
9533 int wrap_flush_s(FILTER_S *,char *, int, int, unsigned char **, unsigned char **,
9534 unsigned char **, unsigned char **, int);
9535 int wrap_eol(FILTER_S *, int, unsigned char **, unsigned char **,
9536 unsigned char **, unsigned char **);
9537 int wrap_bol(FILTER_S *, int, int, unsigned char **,
9538 unsigned char **, unsigned char **, unsigned char **);
9539 int wrap_quote_insert(FILTER_S *, unsigned char **, unsigned char **,
9540 unsigned char **, unsigned char **);
9543 * the no longer simple filter, breaks lines at end of white space nearest
9544 * to global "gf_wrap_width" in length
9545 * It also supports margins, indents (inverse indenting, really) and
9546 * flowed text (ala RFC 3676)
9549 void
9550 gf_wrap(FILTER_S *f, int flg)
9552 register long i;
9553 GF_INIT(f, f->next);
9556 * f->f1 state
9557 * f->line buffer where next "word" being considered is stored
9558 * f->f2 width in screen cells of f->line stuff
9559 * f->n width in screen cells of the part of this line committed to next
9560 * filter so far
9563 if(flg == GF_DATA){
9564 register unsigned char c;
9565 register int state = f->f1;
9566 int width, full_character;
9568 while(GF_GETC(f, c)){
9570 switch(state){
9571 case CCR : /* CRLF or CR in text ? */
9572 state = BOL; /* either way, handle start */
9574 if(WRAP_FLOW(f)){
9575 /* wrapped line? */
9576 if(f->f2 == 0 && WRAP_SPC_LEN(f) && WRAP_TRL_SPC(f)){
9578 * whack trailing space char, but be aware
9579 * of embeds in space buffer. grok them just
9580 * in case they contain a 0x20 value
9582 if(WRAP_DELSP(f)){
9583 char *sb, *sbp, *scp = NULL;
9584 int x;
9586 for(sb = sbp = (char *)so_text(WRAP_SPACES(f)); *sbp; sbp++){
9587 switch(*sbp){
9588 case ' ' :
9589 scp = sbp;
9590 break;
9592 case TAG_EMBED :
9593 sbp++;
9594 switch (*sbp++){
9595 case TAG_HANDLE :
9596 x = (int) *sbp++;
9597 if(strlen(sbp) >= x)
9598 sbp += (x - 1);
9600 break;
9602 case TAG_FGCOLOR :
9603 case TAG_BGCOLOR :
9604 if(strlen(sbp) >= RGBLEN)
9605 sbp += (RGBLEN - 1);
9607 break;
9609 default :
9610 break;
9613 break;
9615 default :
9616 break;
9620 /* replace space buf without trailing space char */
9621 if(scp){
9622 STORE_S *ns = so_get(CharStar, NULL, EDIT_ACCESS);
9624 *scp++ = '\0';
9625 WRAP_SPC_LEN(f)--;
9626 WRAP_TRL_SPC(f) = 0;
9628 so_puts(ns, sb);
9629 so_puts(ns, scp);
9631 so_give(&WRAP_SPACES(f));
9632 WRAP_SPACES(f) = ns;
9636 else{ /* fixed line */
9637 WRAP_HARD(f) = 1;
9638 wrap_flush(f, &ip, &eib, &op, &eob);
9639 wrap_eol(f, 0, &ip, &eib, &op, &eob);
9642 * When we get to a real end of line, we don't need to
9643 * remember what the special color was anymore because
9644 * we aren't going to be changing back to it. We unset it
9645 * so that we don't keep resetting the color to normal.
9647 WRAP_COLOR_UNSET(f);
9650 if(c == '\012'){ /* get c following LF */
9651 break;
9653 /* else c is first char of new line, fall thru */
9655 else{
9656 wrap_flush(f, &ip, &eib, &op, &eob);
9657 wrap_eol(f, 0, &ip, &eib, &op, &eob);
9658 WRAP_COLOR_UNSET(f); /* see note above */
9659 if(c == '\012'){
9660 break;
9662 /* else fall thru to deal with beginning of line */
9665 case BOL :
9666 if(WRAP_FLOW(f)){
9667 if(c == '>'){
9668 WRAP_FL_QC(f) = 1; /* init it */
9669 state = FL_QLEV; /* go collect it */
9671 else {
9672 /* if EMBEDed, process it and return here */
9673 if(c == (unsigned char) TAG_EMBED){
9674 WRAP_EMBED_PUTC(f, TAG_EMBED);
9675 WRAP_STATE(f) = state;
9676 state = TAG;
9677 continue;
9680 /* quote level change implies new paragraph */
9681 if(WRAP_FL_QD(f)){
9682 WRAP_FL_QD(f) = 0;
9683 if(WRAP_HARD(f) == 0){
9684 WRAP_HARD(f) = 1;
9685 wrap_flush(f, &ip, &eib, &op, &eob);
9686 wrap_eol(f, 0, &ip, &eib, &op, &eob);
9687 WRAP_COLOR_UNSET(f); /* see note above */
9691 if(WRAP_HARD(f)){
9692 wrap_bol(f, 0, 1, &ip, &eib, &op,
9693 &eob); /* write quoting prefix */
9694 WRAP_HARD(f) = 0;
9697 switch (c) {
9698 case '\015' : /* a blank line? */
9699 wrap_flush(f, &ip, &eib, &op, &eob);
9700 state = CCR; /* go collect it */
9701 break;
9703 case ' ' : /* space stuffed */
9704 state = FL_STF; /* just eat it */
9705 break;
9707 case '-' : /* possible sig-dash */
9708 WRAP_FL_SIG(f) = 1; /* init state */
9709 state = FL_SIG; /* go collect it */
9710 break;
9712 default :
9713 state = DFL; /* go back to normal */
9714 goto case_dfl; /* handle c like DFL case */
9718 else{
9719 state = DFL;
9720 if(WRAP_COMMA(f) && c == TAB){
9721 wrap_bol(f, 1, 0, &ip, &eib, &op,
9722 &eob); /* convert to normal indent */
9723 break;
9726 wrap_bol(f,0,0, &ip, &eib, &op, &eob);
9727 goto case_dfl; /* handle c like DFL case */
9730 break;
9732 case FL_QLEV :
9733 if(c == '>'){ /* another level */
9734 WRAP_FL_QC(f)++;
9736 else {
9737 /* if EMBEDed, process it and return here */
9738 if(c == (unsigned char) TAG_EMBED){
9739 WRAP_EMBED_PUTC(f, TAG_EMBED);
9740 WRAP_STATE(f) = state;
9741 state = TAG;
9742 continue;
9745 /* quote level change signals new paragraph */
9746 if(WRAP_FL_QC(f) != WRAP_FL_QD(f)){
9747 WRAP_FL_QD(f) = WRAP_FL_QC(f);
9748 if(WRAP_HARD(f) == 0){ /* add hard newline */
9749 WRAP_HARD(f) = 1; /* hard newline */
9750 wrap_flush(f, &ip, &eib, &op, &eob);
9751 wrap_eol(f, 0, &ip, &eib, &op, &eob);
9752 WRAP_COLOR_UNSET(f); /* see note above */
9756 if(WRAP_HARD(f)){
9757 wrap_bol(f,0,1, &ip, &eib, &op, &eob);
9758 WRAP_HARD(f) = 0;
9761 switch (c) {
9762 case '\015' : /* a blank line? */
9763 wrap_flush(f, &ip, &eib, &op, &eob);
9764 state = CCR; /* go collect it */
9765 break;
9767 case ' ' : /* space-stuffed! */
9768 state = FL_STF; /* just eat it */
9769 break;
9771 case '-' : /* sig dash? */
9772 WRAP_FL_SIG(f) = 1;
9773 state = FL_SIG;
9774 break;
9776 default : /* something else */
9777 state = DFL;
9778 goto case_dfl; /* handle c like DFL */
9782 break;
9784 case FL_STF : /* space stuffed */
9785 switch (c) {
9786 case '\015' : /* a blank line? */
9787 wrap_flush(f, &ip, &eib, &op, &eob);
9788 state = CCR; /* go collect it */
9789 break;
9791 case (unsigned char) TAG_EMBED : /* process TAG data */
9792 WRAP_EMBED_PUTC(f, TAG_EMBED);
9793 WRAP_STATE(f) = state; /* and return */
9794 state = TAG;
9795 continue;
9797 case '-' : /* sig dash? */
9798 WRAP_FL_SIG(f) = 1;
9799 WRAP_ALLWSP(f) = 0;
9800 state = FL_SIG;
9801 break;
9803 default : /* something else */
9804 state = DFL;
9805 goto case_dfl; /* handle c like DFL */
9808 break;
9810 case FL_SIG : /* sig-dash collector */
9811 switch (WRAP_FL_SIG(f)){ /* possible sig-dash? */
9812 case 1 :
9813 if(c != '-'){ /* not a sigdash */
9814 if((f->n + WRAP_SPC_LEN(f) + 1) > WRAP_COL(f)){
9815 wrap_flush_embed(f, &ip, &eib, &op,
9816 &eob); /* note any embedded*/
9817 wrap_eol(f, 1, &ip, &eib,
9818 &op, &eob); /* plunk down newline */
9819 wrap_bol(f, 1, 1, &ip, &eib,
9820 &op, &eob); /* write any prefix */
9823 WRAP_PUTC(f,'-', 1); /* write what we got */
9825 WRAP_FL_SIG(f) = 0;
9826 state = DFL;
9827 goto case_dfl;
9830 /* don't put anything yet until we know to wrap or not */
9831 WRAP_FL_SIG(f) = 2;
9832 break;
9834 case 2 :
9835 if(c != ' '){ /* not a sigdash */
9836 WRAP_PUTC(f, '-', 1);
9837 if((f->n + WRAP_SPC_LEN(f) + 2) > WRAP_COL(f)){
9838 wrap_flush_embed(f, &ip, &eib, &op,
9839 &eob); /* note any embedded*/
9840 wrap_eol(f, 1, &ip, &eib,
9841 &op, &eob); /* plunk down newline */
9842 wrap_bol(f, 1, 1, &ip, &eib, &op,
9843 &eob); /* write any prefix */
9846 WRAP_PUTC(f,'-', 1); /* write what we got */
9848 WRAP_FL_SIG(f) = 0;
9849 state = DFL;
9850 goto case_dfl;
9853 /* don't put anything yet until we know to wrap or not */
9854 WRAP_FL_SIG(f) = 3;
9855 break;
9857 case 3 :
9858 if(c == '\015'){ /* success! */
9859 /* known sigdash, newline if soft nl */
9860 if(WRAP_SPC_LEN(f)){
9861 wrap_flush(f, &ip, &eib, &op, &eob);
9862 wrap_eol(f, 0, &ip, &eib, &op, &eob);
9863 wrap_bol(f, 0, 1, &ip, &eib, &op, &eob);
9865 WRAP_PUTC(f,'-',1);
9866 WRAP_PUTC(f,'-',1);
9867 WRAP_PUTC(f,' ',1);
9869 state = CCR;
9870 break;
9872 else{
9873 WRAP_FL_SIG(f) = 4; /* possible success */
9876 case 4 :
9877 switch(c){
9878 case (unsigned char) TAG_EMBED :
9880 * At this point we're almost 100% sure that we've got
9881 * a sigdash. Putc it (adding newline if previous
9882 * was a soft nl) so we get it the right color
9883 * before we store this new embedded stuff
9885 if(WRAP_SPC_LEN(f)){
9886 wrap_flush(f, &ip, &eib, &op, &eob);
9887 wrap_eol(f, 0, &ip, &eib, &op, &eob);
9888 wrap_bol(f, 0, 1, &ip, &eib, &op, &eob);
9890 WRAP_PUTC(f,'-',1);
9891 WRAP_PUTC(f,'-',1);
9892 WRAP_PUTC(f,' ',1);
9894 WRAP_FL_SIG(f) = 5;
9895 break;
9897 case '\015' : /* success! */
9899 * We shouldn't get here, but in case we do, we have
9900 * not yet put the sigdash
9902 if(WRAP_SPC_LEN(f)){
9903 wrap_flush(f, &ip, &eib, &op, &eob);
9904 wrap_eol(f, 0, &ip, &eib, &op, &eob);
9905 wrap_bol(f, 0, 1, &ip, &eib, &op, &eob);
9907 WRAP_PUTC(f,'-',1);
9908 WRAP_PUTC(f,'-',1);
9909 WRAP_PUTC(f,' ',1);
9911 state = CCR;
9912 break;
9914 default : /* that's no sigdash! */
9915 /* write what we got but didn't put yet */
9916 WRAP_PUTC(f,'-', 1);
9917 WRAP_PUTC(f,'-', 1);
9918 WRAP_PUTC(f,' ', 1);
9920 WRAP_FL_SIG(f) = 0;
9921 wrap_flush(f, &ip, &eib, &op, &eob);
9922 WRAP_SPC_LEN(f) = 1;
9923 state = DFL; /* set normal state */
9924 goto case_dfl; /* and go do "c" */
9927 break;
9929 case 5 :
9930 WRAP_STATE(f) = FL_SIG; /* come back here */
9931 WRAP_FL_SIG(f) = 6; /* and seek EOL */
9932 WRAP_EMBED_PUTC(f, TAG_EMBED);
9933 state = TAG; /* process embed */
9934 goto case_tag;
9936 case 6 :
9938 * at this point we've already putc the sigdash in case 4
9940 switch(c){
9941 case (unsigned char) TAG_EMBED :
9942 WRAP_FL_SIG(f) = 5;
9943 break;
9945 case '\015' : /* success! */
9946 state = CCR;
9947 break;
9949 default : /* that's no sigdash! */
9951 * probably never reached (fake sigdash with embedded
9952 * stuff) but if this did get reached, then we
9953 * might have accidentally disobeyed a soft nl
9955 WRAP_FL_SIG(f) = 0;
9956 wrap_flush(f, &ip, &eib, &op, &eob);
9957 WRAP_SPC_LEN(f) = 1;
9958 state = DFL; /* set normal state */
9959 goto case_dfl; /* and go do "c" */
9962 break;
9965 default :
9966 dprint((2, "-- gf_wrap: BROKEN FLOW STATE: %d\n",
9967 WRAP_FL_SIG(f)));
9968 WRAP_FL_SIG(f) = 0;
9969 state = DFL; /* set normal state */
9970 goto case_dfl; /* and go process "c" */
9973 break;
9975 case_dfl :
9976 case DFL :
9978 * This was just if(WRAP_SPEC(f, c)) before the change to add
9979 * the == 0 test. This isn't quite right, either. We should really
9980 * be looking for special characters in the UCS characters, not
9981 * in the incoming stream of UTF-8. It is not right to
9982 * call this on bytes that are in the middle of a UTF-8 character,
9983 * hence the == 0 test which restricts it to the first byte
9984 * of a character. This isn't right, either, but it's closer.
9985 * Also change the definition of WRAP_SPEC so that isspace only
9986 * matches ascii characters, which will never be in the middle
9987 * of a UTF-8 multi-byte character.
9989 if((WRAP_UTF8BUFP(f) - &WRAP_UTF8BUF(f, 0)) == 0 && WRAP_SPEC(f, c)){
9990 WRAP_SAW_SOFT_HYPHEN(f) = 0;
9991 switch(c){
9992 default :
9993 if(WRAP_QUOTED(f))
9994 break;
9996 if(f->f2){ /* any non-lwsp to flush? */
9997 if(WRAP_COMMA(f)){
9998 /* remember our second best break point */
9999 WRAP_PB_OFF(f) = f->linep - f->line;
10000 WRAP_PB_LEN(f) = f->f2;
10001 break;
10003 else
10004 wrap_flush(f, &ip, &eib, &op, &eob);
10007 switch(c){ /* remember separator */
10008 case ' ' :
10009 WRAP_SPC_LEN(f)++;
10010 WRAP_TRL_SPC(f) = 1;
10011 so_writec(' ',WRAP_SPACES(f));
10012 break;
10014 case TAB :
10016 int i = (int) f->n + WRAP_SPC_LEN(f);
10019 WRAP_SPC_LEN(f)++;
10020 while(++i & 0x07);
10022 so_writec(TAB,WRAP_SPACES(f));
10023 WRAP_TRL_SPC(f) = 0;
10026 break;
10028 default : /* some control char? */
10029 WRAP_SPC_LEN(f) += 2;
10030 WRAP_TRL_SPC(f) = 0;
10031 break;
10034 continue;
10036 case '\"' :
10037 WRAP_QUOTED(f) = !WRAP_QUOTED(f);
10038 break;
10040 case '\015' : /* already has newline? */
10041 state = CCR;
10042 continue;
10044 case '\012' : /* bare LF in text? */
10045 wrap_flush(f, &ip, &eib, &op, &eob); /* they must've */
10046 wrap_eol(f, 0, &ip, &eib, &op, &eob); /* meant */
10047 wrap_bol(f,1,1, &ip, &eib, &op, &eob); /* newline... */
10048 continue;
10050 case (unsigned char) TAG_EMBED :
10051 WRAP_EMBED_PUTC(f, TAG_EMBED);
10052 WRAP_STATE(f) = state;
10053 state = TAG;
10054 continue;
10056 case ',' :
10057 if(!WRAP_QUOTED(f)){
10058 /* handle this special case in general code below */
10059 if(f->n + WRAP_SPC_LEN(f) + f->f2 + 1 > WRAP_MAX_COL(f)
10060 && WRAP_ALLWSP(f) && WRAP_PB_OFF(f))
10061 break;
10063 if(f->n + WRAP_SPC_LEN(f) + f->f2 + 1 > WRAP_COL(f)){
10064 if(WRAP_ALLWSP(f)) /* if anything visible */
10065 wrap_flush(f, &ip, &eib, &op,
10066 &eob); /* ... blat buf'd chars */
10068 wrap_eol(f, 1, &ip, &eib, &op,
10069 &eob); /* plunk down newline */
10070 wrap_bol(f, 1, 1, &ip, &eib, &op,
10071 &eob); /* write any prefix */
10074 WRAP_PUTC(f, ',', 1); /* put out comma */
10075 wrap_flush(f, &ip, &eib, &op,
10076 &eob); /* write buf'd chars */
10077 continue;
10080 break;
10083 else if(WRAP_HANDLE_SOFT_HYPHEN(f)
10084 && (WRAP_UTF8BUFP(f) - &WRAP_UTF8BUF(f, 0)) == 1
10085 && WRAP_UTF8BUF(f, 0) == 0xC2 && c == 0xAD){
10087 * This is a soft hyphen. If there is enough space for
10088 * a real hyphen to fit on the line here then we can
10089 * flush everything up to before the soft hyphen,
10090 * and simply remember that we saw a soft hyphen.
10091 * If it turns out that we can't fit the next piece in
10092 * then wrap_eol will append a real hyphen to the line.
10093 * If we can fit another piece in it will be because we've
10094 * reached the next break point. At that point we'll flush
10095 * everything but won't include the unneeded hyphen. We erase
10096 * the fact that we saw this soft hyphen because it have
10097 * become irrelevant.
10099 * If the hyphen is the character that puts us over the edge
10100 * we go through the else case.
10103 /* erase this soft hyphen character from buffer */
10104 WRAP_UTF8BUFP(f) = &WRAP_UTF8BUF(f, 0);
10106 if((f->n + WRAP_SPC_LEN(f) + f->f2 + 1) <= WRAP_COL(f)){
10107 if(f->f2) /* any non-lwsp to flush? */
10108 wrap_flush(f, &ip, &eib, &op, &eob);
10110 /* remember that we saw the soft hyphen */
10111 WRAP_SAW_SOFT_HYPHEN(f) = 1;
10113 else{
10115 * Everything up to the hyphen fits, otherwise it
10116 * would have already been flushed the last time
10117 * through the loop. But the hyphen won't fit. So
10118 * we need to go back to the last line break and
10119 * break there instead. Then start a new line with
10120 * the buffered up characters and the soft hyphen.
10122 wrap_flush_embed(f, &ip, &eib, &op, &eob);
10123 wrap_eol(f, 1, &ip, &eib, &op,
10124 &eob); /* plunk down newline */
10125 wrap_bol(f,1,1, &ip, &eib, &op,
10126 &eob); /* write any prefix */
10129 * Now we're in the same situation as we would have
10130 * been above except we're on a new line. Try to
10131 * flush out the characters seen up to the hyphen.
10133 if((f->n + WRAP_SPC_LEN(f) + f->f2 + 1) <= WRAP_COL(f)){
10134 if(f->f2) /* any non-lwsp to flush? */
10135 wrap_flush(f, &ip, &eib, &op, &eob);
10137 /* remember that we saw the soft hyphen */
10138 WRAP_SAW_SOFT_HYPHEN(f) = 1;
10140 else
10141 WRAP_SAW_SOFT_HYPHEN(f) = 0;
10144 continue;
10147 full_character = 0;
10150 unsigned char *inputp;
10151 unsigned long remaining_octets;
10152 UCS ucs;
10154 if(WRAP_UTF8BUFP(f) < &WRAP_UTF8BUF(f, 0) + 6){ /* always true */
10156 *WRAP_UTF8BUFP(f)++ = c;
10157 remaining_octets = WRAP_UTF8BUFP(f) - &WRAP_UTF8BUF(f, 0);
10158 if(remaining_octets == 1 && isascii(WRAP_UTF8BUF(f, 0))){
10159 full_character++;
10160 if(c == TAB){
10161 int i = (int) f->n;
10163 while(i & 0x07)
10164 i++;
10166 width = i - f->n;
10168 else if(c < 0x80 && iscntrl((unsigned char) c))
10169 width = 2;
10170 else
10171 width = 1;
10173 else{
10174 inputp = &WRAP_UTF8BUF(f, 0);
10175 ucs = (UCS) utf8_get(&inputp, &remaining_octets);
10176 switch(ucs){
10177 case U8G_ENDSTRG: /* incomplete character, wait */
10178 case U8G_ENDSTRI: /* incomplete character, wait */
10179 width = 0;
10180 break;
10182 default:
10183 if(ucs & U8G_ERROR || ucs == UBOGON){
10185 * None of these cases is supposed to happen. If it
10186 * does happen then the input stream isn't UTF-8
10187 * so something is wrong. Writechar will treat
10188 * each octet in the input buffer as a separate
10189 * error character and print a '?' for each,
10190 * so the width will be the number of octets.
10192 width = WRAP_UTF8BUFP(f) - &WRAP_UTF8BUF(f, 0);
10193 full_character++;
10195 else{
10196 /* got a character */
10197 width = wcellwidth(ucs);
10198 full_character++;
10200 if(width < 0){
10202 * This happens when we have a UTF-8 character that
10203 * we aren't able to print in our locale. For example,
10204 * if the locale is setup with the terminal
10205 * expecting ISO-8859-1 characters then there are
10206 * lots of UTF-8 characters that can't be printed.
10207 * Print a '?' instead.
10209 width = 1;
10213 break;
10217 else{
10219 * This cannot happen because an error would have
10220 * happened at least by character #6. So if we get
10221 * here there is a bug in utf8_get().
10223 if(WRAP_UTF8BUFP(f) == &WRAP_UTF8BUF(f, 0) + 6){
10224 *WRAP_UTF8BUFP(f)++ = c;
10228 * We could possibly do some more sophisticated
10229 * resynchronization here, but we aren't doing
10230 * anything in Writechar so it wouldn't match up
10231 * with that anyway. Just figure each character will
10232 * end up being printed as a ? character.
10234 width = WRAP_UTF8BUFP(f) - &WRAP_UTF8BUF(f, 0);
10235 full_character++;
10239 if(WRAP_ALLWSP(f)){
10241 * Nothing is visible yet but the first word may be too long
10242 * all by itself. We need to break early.
10244 if(f->n + WRAP_SPC_LEN(f) + f->f2 + width > WRAP_MAX_COL(f)){
10246 * A little reaching behind the curtain here.
10247 * if there's at least a preferable break point, use
10248 * it and stuff what's left back into the wrap buffer.
10249 * The "nwsp" latch is used to skip leading whitespace
10250 * The second half of the test prevents us from wrapping
10251 * at the preferred break point in the case that it
10252 * is so early in the line that it doesn't help.
10253 * That is, the width of the indent is even more than
10254 * the width of the first part before the preferred
10255 * break point. An example would be breaking after
10256 * "To:" when the indent is 4 which is > 3.
10258 if(WRAP_PB_OFF(f) && WRAP_PB_LEN(f) >= WRAP_INDENT(f)){
10259 char *p1 = f->line + WRAP_PB_OFF(f);
10260 char *p2 = f->linep;
10261 char c2;
10262 int nwsp = 0, left_after_wrap;
10264 left_after_wrap = f->f2 - WRAP_PB_LEN(f);
10266 f->f2 = WRAP_PB_LEN(f);
10267 f->linep = p1;
10269 wrap_flush(f, &ip, &eib, &op, &eob); /* flush shortened buf */
10271 /* put back rest of characters */
10272 while(p1 < p2){
10273 c2 = *p1++;
10274 if(!(c2 == ' ' || c2 == '\t') || nwsp){
10275 WRAP_PUTC(f, c2, 0);
10276 nwsp = 1;
10278 else
10279 left_after_wrap--; /* wrong if a tab! */
10282 f->f2 = MAX(left_after_wrap, 0);
10284 wrap_eol(f, 1, &ip, &eib, &op,
10285 &eob); /* plunk down newline */
10286 wrap_bol(f,1,1, &ip, &eib, &op,
10287 &eob); /* write any prefix */
10290 * What's this for?
10291 * If we do the less preferable break point at
10292 * the space we don't want to lose the fact that
10293 * we might be able to break at this comma for
10294 * the next one.
10296 if(full_character && c == ','){
10297 WRAP_PUTC(f, c, 1);
10298 wrap_flush(f, &ip, &eib, &op, &eob);
10299 WRAP_UTF8BUFP(f) = &WRAP_UTF8BUF(f, 0);
10302 else{
10303 wrap_flush(f, &ip, &eib, &op, &eob);
10305 wrap_eol(f, 1, &ip, &eib, &op,
10306 &eob); /* plunk down newline */
10307 wrap_bol(f,1,1, &ip, &eib, &op,
10308 &eob); /* write any prefix */
10312 else if((f->n + WRAP_SPC_LEN(f) + f->f2 + width) > WRAP_COL(f)){
10313 wrap_flush_embed(f, &ip, &eib, &op, &eob);
10314 wrap_eol(f, 1, &ip, &eib, &op,
10315 &eob); /* plunk down newline */
10316 wrap_bol(f,1,1, &ip, &eib, &op,
10317 &eob); /* write any prefix */
10321 * Commit entire multibyte UTF-8 character at once
10322 * instead of writing partial characters into the
10323 * buffer.
10325 if(full_character){
10326 unsigned char *q;
10328 for(q = &WRAP_UTF8BUF(f, 0); q < WRAP_UTF8BUFP(f); q++){
10329 WRAP_PUTC(f, *q, width);
10330 width = 0;
10333 WRAP_UTF8BUFP(f) = &WRAP_UTF8BUF(f, 0);
10336 break;
10338 case_tag :
10339 case TAG :
10340 WRAP_EMBED_PUTC(f, c);
10341 switch(c){
10342 case TAG_HANDLE :
10343 WRAP_EMBED(f) = -1;
10344 state = HANDLE;
10345 break;
10347 case TAG_FGCOLOR :
10348 case TAG_BGCOLOR :
10349 WRAP_EMBED(f) = RGBLEN;
10350 state = HDATA;
10351 break;
10353 default :
10354 state = WRAP_STATE(f);
10355 break;
10358 break;
10360 case HANDLE :
10361 WRAP_EMBED_PUTC(f, c);
10362 WRAP_EMBED(f) = c;
10363 state = HDATA;
10364 break;
10366 case HDATA :
10367 if(f->f2){
10368 WRAP_PUTC(f, c, 0);
10370 else
10371 so_writec(c, WRAP_SPACES(f));
10373 if(!(WRAP_EMBED(f) -= 1)){
10374 state = WRAP_STATE(f);
10377 break;
10381 f->f1 = state;
10382 GF_END(f, f->next);
10384 else if(flg == GF_EOD){
10385 wrap_flush(f, &ip, &eib, &op, &eob);
10386 if(WRAP_COLOR(f))
10387 free_color_pair(&WRAP_COLOR(f));
10389 fs_give((void **) &f->line); /* free temp line buffer */
10390 so_give(&WRAP_SPACES(f));
10391 fs_give((void **) &f->opt); /* free wrap widths struct */
10392 (void) GF_FLUSH(f->next);
10393 (*f->next->f)(f->next, GF_EOD);
10395 else if(flg == GF_RESET){
10396 dprint((9, "-- gf_reset wrap\n"));
10397 f->f1 = BOL;
10398 f->n = 0L; /* displayed length of line so far */
10399 f->f2 = 0; /* displayed length of buffered chars */
10400 WRAP_HARD(f) = 1; /* starting at beginning of line */
10401 if(! (WRAP_S *) f->opt)
10402 f->opt = gf_wrap_filter_opt(75, 80, NULL, 0, 0);
10404 while(WRAP_INDENT(f) >= WRAP_MAX_COL(f))
10405 WRAP_INDENT(f) /= 2;
10407 f->line = (char *) fs_get(WRAP_MAX_COL(f) * sizeof(char));
10408 f->linep = f->line;
10409 WRAP_LASTC(f) = &f->line[WRAP_MAX_COL(f) - 1];
10411 for(i = 0; i < 256; i++)
10412 ((WRAP_S *) f->opt)->special[i] = ((i == '\"' && WRAP_COMMA(f))
10413 || i == '\015'
10414 || i == '\012'
10415 || (i == (unsigned char) TAG_EMBED
10416 && WRAP_TAGS(f))
10417 || (i == ',' && WRAP_COMMA(f)
10418 && !WRAP_QUOTED(f))
10419 || ASCII_ISSPACE(i));
10420 WRAP_SPACES(f) = so_get(CharStar, NULL, EDIT_ACCESS);
10421 WRAP_UTF8BUFP(f) = &WRAP_UTF8BUF(f, 0);
10426 wrap_flush(FILTER_S *f, unsigned char **ipp, unsigned char **eibp,
10427 unsigned char **opp, unsigned char **eobp)
10429 register char *s;
10430 register int n;
10432 s = (char *)so_text(WRAP_SPACES(f));
10433 n = so_tell(WRAP_SPACES(f));
10434 so_seek(WRAP_SPACES(f), 0L, 0);
10435 wrap_flush_s(f, s, n, WRAP_SPC_LEN(f), ipp, eibp, opp, eobp, WFE_NONE);
10436 so_truncate(WRAP_SPACES(f), 0L);
10437 WRAP_SPC_LEN(f) = 0;
10438 WRAP_TRL_SPC(f) = 0;
10439 s = f->line;
10440 n = f->linep - f->line;
10441 wrap_flush_s(f, s, n, f->f2, ipp, eibp, opp, eobp, WFE_NONE);
10442 f->f2 = 0;
10443 f->linep = f->line;
10444 WRAP_PB_OFF(f) = 0;
10445 WRAP_PB_LEN(f) = 0;
10447 return 0;
10451 wrap_flush_embed(FILTER_S *f, unsigned char **ipp, unsigned char **eibp, unsigned char **opp, unsigned char **eobp)
10453 register char *s;
10454 register int n;
10455 s = (char *)so_text(WRAP_SPACES(f));
10456 n = so_tell(WRAP_SPACES(f));
10457 so_seek(WRAP_SPACES(f), 0L, 0);
10458 wrap_flush_s(f, s, n, 0, ipp, eibp, opp, eobp, WFE_CNT_HANDLE);
10459 so_truncate(WRAP_SPACES(f), 0L);
10460 WRAP_SPC_LEN(f) = 0;
10461 WRAP_TRL_SPC(f) = 0;
10463 return 0;
10467 wrap_flush_s(FILTER_S *f, char *s, int n, int w, unsigned char **ipp,
10468 unsigned char **eibp, unsigned char **opp, unsigned char **eobp, int flags)
10470 f->n += w;
10472 for(; n > 0; n--,s++){
10473 if(*s == TAG_EMBED){
10474 if(n-- > 0){
10475 switch(*++s){
10476 case TAG_BOLDON :
10477 GF_PUTC_GLO(f->next,TAG_EMBED);
10478 GF_PUTC_GLO(f->next,TAG_BOLDON);
10479 WRAP_BOLD(f) = 1;
10480 break;
10481 case TAG_BOLDOFF :
10482 GF_PUTC_GLO(f->next,TAG_EMBED);
10483 GF_PUTC_GLO(f->next,TAG_BOLDOFF);
10484 WRAP_BOLD(f) = 0;
10485 break;
10486 case TAG_ULINEON :
10487 GF_PUTC_GLO(f->next,TAG_EMBED);
10488 GF_PUTC_GLO(f->next,TAG_ULINEON);
10489 WRAP_ULINE(f) = 1;
10490 break;
10491 case TAG_ULINEOFF :
10492 GF_PUTC_GLO(f->next,TAG_EMBED);
10493 GF_PUTC_GLO(f->next,TAG_ULINEOFF);
10494 WRAP_ULINE(f) = 0;
10495 break;
10496 case TAG_INVOFF :
10497 GF_PUTC_GLO(f->next,TAG_EMBED);
10498 GF_PUTC_GLO(f->next,TAG_INVOFF);
10499 WRAP_ANCHOR(f) = 0;
10500 break;
10501 case TAG_HANDLE :
10502 if((flags & WFE_CNT_HANDLE) == 0)
10503 GF_PUTC_GLO(f->next,TAG_EMBED);
10505 if(n-- > 0){
10506 int i = *++s;
10508 if((flags & WFE_CNT_HANDLE) == 0)
10509 GF_PUTC_GLO(f->next, TAG_HANDLE);
10511 if(i <= n){
10512 n -= i;
10514 if((flags & WFE_CNT_HANDLE) == 0)
10515 GF_PUTC_GLO(f->next, i);
10517 WRAP_ANCHOR(f) = 0;
10518 while(i-- > 0){
10519 WRAP_ANCHOR(f) = (WRAP_ANCHOR(f) * 10) + (*++s-'0');
10521 if((flags & WFE_CNT_HANDLE) == 0)
10522 GF_PUTC_GLO(f->next,*s);
10527 break;
10528 case TAG_FGCOLOR :
10529 if(pico_usingcolor() && n >= RGBLEN){
10530 int i;
10531 GF_PUTC_GLO(f->next,TAG_EMBED);
10532 GF_PUTC_GLO(f->next,TAG_FGCOLOR);
10533 if(!WRAP_COLOR(f))
10534 WRAP_COLOR(f)=new_color_pair(NULL,NULL);
10535 strncpy(WRAP_COLOR(f)->fg, s+1, RGBLEN);
10536 WRAP_COLOR(f)->fg[RGBLEN]='\0';
10537 i = RGBLEN;
10538 n -= i;
10539 while(i-- > 0)
10540 GF_PUTC_GLO(f->next,
10541 (*++s) & 0xff);
10543 break;
10544 case TAG_BGCOLOR :
10545 if(pico_usingcolor() && n >= RGBLEN){
10546 int i;
10547 GF_PUTC_GLO(f->next,TAG_EMBED);
10548 GF_PUTC_GLO(f->next,TAG_BGCOLOR);
10549 if(!WRAP_COLOR(f))
10550 WRAP_COLOR(f)=new_color_pair(NULL,NULL);
10551 strncpy(WRAP_COLOR(f)->bg, s+1, RGBLEN);
10552 WRAP_COLOR(f)->bg[RGBLEN]='\0';
10553 i = RGBLEN;
10554 n -= i;
10555 while(i-- > 0)
10556 GF_PUTC_GLO(f->next,
10557 (*++s) & 0xff);
10559 break;
10560 default :
10561 break;
10565 else if(w){
10567 if(f->n <= WRAP_MAX_COL(f)){
10568 GF_PUTC_GLO(f->next, (*s) & 0xff);
10570 else{
10571 dprint((2, "-- gf_wrap: OVERRUN: %c\n", (*s) & 0xff));
10574 WRAP_ALLWSP(f) = 0;
10578 return 0;
10582 wrap_eol(FILTER_S *f, int c, unsigned char **ipp, unsigned char **eibp,
10583 unsigned char **opp, unsigned char **eobp)
10585 if(WRAP_SAW_SOFT_HYPHEN(f)){
10586 WRAP_SAW_SOFT_HYPHEN(f) = 0;
10587 GF_PUTC_GLO(f->next, '-'); /* real hyphen */
10590 if(c && WRAP_LV_FLD(f))
10591 GF_PUTC_GLO(f->next, ' ');
10593 if(WRAP_BOLD(f)){
10594 GF_PUTC_GLO(f->next, TAG_EMBED);
10595 GF_PUTC_GLO(f->next, TAG_BOLDOFF);
10598 if(WRAP_ULINE(f)){
10599 GF_PUTC_GLO(f->next, TAG_EMBED);
10600 GF_PUTC_GLO(f->next, TAG_ULINEOFF);
10603 if(WRAP_INVERSE(f) || WRAP_ANCHOR(f)){
10604 GF_PUTC_GLO(f->next, TAG_EMBED);
10605 GF_PUTC_GLO(f->next, TAG_INVOFF);
10608 if(WRAP_COLOR_SET(f)){
10609 char *p;
10610 char cb[RGBLEN+1];
10611 GF_PUTC_GLO(f->next, TAG_EMBED);
10612 GF_PUTC_GLO(f->next, TAG_FGCOLOR);
10613 strncpy(cb, color_to_asciirgb(ps_global->VAR_NORM_FORE_COLOR), sizeof(cb));
10614 cb[sizeof(cb)-1] = '\0';
10615 p = cb;
10616 for(; *p; p++)
10617 GF_PUTC_GLO(f->next, *p);
10618 GF_PUTC_GLO(f->next, TAG_EMBED);
10619 GF_PUTC_GLO(f->next, TAG_BGCOLOR);
10620 strncpy(cb, color_to_asciirgb(ps_global->VAR_NORM_BACK_COLOR), sizeof(cb));
10621 cb[sizeof(cb)-1] = '\0';
10622 p = cb;
10623 for(; *p; p++)
10624 GF_PUTC_GLO(f->next, *p);
10627 GF_PUTC_GLO(f->next, '\015');
10628 GF_PUTC_GLO(f->next, '\012');
10629 f->n = 0L;
10630 so_truncate(WRAP_SPACES(f), 0L);
10631 WRAP_SPC_LEN(f) = 0;
10632 WRAP_TRL_SPC(f) = 0;
10634 return 0;
10638 wrap_bol(FILTER_S *f, int ivar, int q, unsigned char **ipp, unsigned char **eibp,
10639 unsigned char **opp, unsigned char **eobp)
10641 int n = WRAP_MARG_L(f) + (ivar ? WRAP_INDENT(f) : 0);
10643 if(WRAP_HDR_CLR(f)){
10644 char *p;
10645 char cbuf[RGBLEN+1];
10646 int k;
10648 if((k = WRAP_MARG_L(f)) > 0)
10649 while(k-- > 0){
10650 n--;
10651 f->n++;
10652 GF_PUTC_GLO(f->next, ' ');
10655 GF_PUTC_GLO(f->next, TAG_EMBED);
10656 GF_PUTC_GLO(f->next, TAG_FGCOLOR);
10657 strncpy(cbuf,
10658 color_to_asciirgb(ps_global->VAR_HEADER_GENERAL_FORE_COLOR),
10659 sizeof(cbuf));
10660 cbuf[sizeof(cbuf)-1] = '\0';
10661 p = cbuf;
10662 for(; *p; p++)
10663 GF_PUTC_GLO(f->next, *p);
10664 GF_PUTC_GLO(f->next, TAG_EMBED);
10665 GF_PUTC_GLO(f->next, TAG_BGCOLOR);
10666 strncpy(cbuf,
10667 color_to_asciirgb(ps_global->VAR_HEADER_GENERAL_BACK_COLOR),
10668 sizeof(cbuf));
10669 cbuf[sizeof(cbuf)-1] = '\0';
10670 p = cbuf;
10671 for(; *p; p++)
10672 GF_PUTC_GLO(f->next, *p);
10675 while(n-- > 0){
10676 f->n++;
10677 GF_PUTC_GLO(f->next, ' ');
10680 WRAP_ALLWSP(f) = 1;
10682 if(q)
10683 wrap_quote_insert(f, ipp, eibp, opp, eobp);
10685 if(WRAP_BOLD(f)){
10686 GF_PUTC_GLO(f->next, TAG_EMBED);
10687 GF_PUTC_GLO(f->next, TAG_BOLDON);
10689 if(WRAP_ULINE(f)){
10690 GF_PUTC_GLO(f->next, TAG_EMBED);
10691 GF_PUTC_GLO(f->next, TAG_ULINEON);
10693 if(WRAP_INVERSE(f)){
10694 GF_PUTC_GLO(f->next, TAG_EMBED);
10695 GF_PUTC_GLO(f->next, TAG_INVON);
10697 if(WRAP_COLOR_SET(f)){
10698 char *p;
10699 if(WRAP_COLOR(f)->fg[0]){
10700 char cb[RGBLEN+1];
10701 GF_PUTC_GLO(f->next, TAG_EMBED);
10702 GF_PUTC_GLO(f->next, TAG_FGCOLOR);
10703 strncpy(cb, color_to_asciirgb(WRAP_COLOR(f)->fg), sizeof(cb));
10704 cb[sizeof(cb)-1] = '\0';
10705 p = cb;
10706 for(; *p; p++)
10707 GF_PUTC_GLO(f->next, *p);
10709 if(WRAP_COLOR(f)->bg[0]){
10710 char cb[RGBLEN+1];
10711 GF_PUTC_GLO(f->next, TAG_EMBED);
10712 GF_PUTC_GLO(f->next, TAG_BGCOLOR);
10713 strncpy(cb, color_to_asciirgb(WRAP_COLOR(f)->bg), sizeof(cb));
10714 cb[sizeof(cb)-1] = '\0';
10715 p = cb;
10716 for(; *p; p++)
10717 GF_PUTC_GLO(f->next, *p);
10720 if(WRAP_ANCHOR(f)){
10721 char buf[64]; int i;
10722 GF_PUTC_GLO(f->next, TAG_EMBED);
10723 GF_PUTC_GLO(f->next, TAG_HANDLE);
10724 snprintf(buf, sizeof(buf), "%d", WRAP_ANCHOR(f));
10725 GF_PUTC_GLO(f->next, (int) strlen(buf));
10726 for(i = 0; buf[i]; i++)
10727 GF_PUTC_GLO(f->next, buf[i]);
10730 return 0;
10734 wrap_quote_insert(FILTER_S *f, unsigned char **ipp, unsigned char **eibp,
10735 unsigned char **opp, unsigned char **eobp)
10737 int j, i;
10738 COLOR_PAIR *col = NULL;
10739 char *prefix = NULL, *last_prefix = NULL;
10741 if(ps_global->VAR_QUOTE_REPLACE_STRING){
10742 get_pair(ps_global->VAR_QUOTE_REPLACE_STRING, &prefix, &last_prefix, 0, 0);
10743 if(!prefix && last_prefix){
10744 prefix = last_prefix;
10745 last_prefix = NULL;
10749 for(j = 0; j < WRAP_FL_QD(f); j++){
10750 if(WRAP_USE_CLR(f)){
10751 if((j % 3) == 0
10752 && ps_global->VAR_QUOTE1_FORE_COLOR
10753 && ps_global->VAR_QUOTE1_BACK_COLOR
10754 && (col = new_color_pair(ps_global->VAR_QUOTE1_FORE_COLOR,
10755 ps_global->VAR_QUOTE1_BACK_COLOR))
10756 && pico_is_good_colorpair(col)){
10757 GF_COLOR_PUTC(f, col);
10759 else if((j % 3) == 1
10760 && ps_global->VAR_QUOTE2_FORE_COLOR
10761 && ps_global->VAR_QUOTE2_BACK_COLOR
10762 && (col = new_color_pair(ps_global->VAR_QUOTE2_FORE_COLOR,
10763 ps_global->VAR_QUOTE2_BACK_COLOR))
10764 && pico_is_good_colorpair(col)){
10765 GF_COLOR_PUTC(f, col);
10767 else if((j % 3) == 2
10768 && ps_global->VAR_QUOTE3_FORE_COLOR
10769 && ps_global->VAR_QUOTE3_BACK_COLOR
10770 && (col = new_color_pair(ps_global->VAR_QUOTE3_FORE_COLOR,
10771 ps_global->VAR_QUOTE3_BACK_COLOR))
10772 && pico_is_good_colorpair(col)){
10773 GF_COLOR_PUTC(f, col);
10775 if(col){
10776 free_color_pair(&col);
10777 col = NULL;
10781 if(!WRAP_LV_FLD(f)){
10782 if(!WRAP_FOR_CMPS(f) && ps_global->VAR_QUOTE_REPLACE_STRING && prefix){
10783 for(i = 0; prefix[i]; i++)
10784 GF_PUTC_GLO(f->next, prefix[i]);
10785 f->n += utf8_width(prefix);
10787 else if(ps_global->VAR_REPLY_STRING
10788 && (!strcmp(ps_global->VAR_REPLY_STRING, ">")
10789 || !strcmp(ps_global->VAR_REPLY_STRING, "\">\""))){
10790 GF_PUTC_GLO(f->next, '>');
10791 f->n += 1;
10793 else{
10794 GF_PUTC_GLO(f->next, '>');
10795 GF_PUTC_GLO(f->next, ' ');
10796 f->n += 2;
10799 else{
10800 GF_PUTC_GLO(f->next, '>');
10801 f->n += 1;
10804 if(j && WRAP_LV_FLD(f)){
10805 GF_PUTC_GLO(f->next, ' ');
10806 f->n++;
10808 else if(j && last_prefix){
10809 for(i = 0; last_prefix[i]; i++)
10810 GF_PUTC_GLO(f->next, last_prefix[i]);
10811 f->n += utf8_width(last_prefix);
10814 if(prefix)
10815 fs_give((void **)&prefix);
10816 if(last_prefix)
10817 fs_give((void **)&last_prefix);
10819 return 0;
10824 * function called from the outside to set
10825 * wrap filter's width option
10827 void *
10828 gf_wrap_filter_opt(int width, int width_max, int *margin, int indent, int flags)
10830 WRAP_S *wrap;
10832 /* NOTE: variables MUST be sanity checked before they get here */
10833 wrap = (WRAP_S *) fs_get(sizeof(WRAP_S));
10834 memset(wrap, 0, sizeof(WRAP_S));
10835 wrap->wrap_col = width;
10836 wrap->wrap_max = width_max;
10837 wrap->indent = indent;
10838 wrap->margin_l = (margin) ? margin[0] : 0;
10839 wrap->margin_r = (margin) ? margin[1] : 0;
10840 wrap->tags = (GFW_HANDLES & flags) == GFW_HANDLES;
10841 wrap->on_comma = (GFW_ONCOMMA & flags) == GFW_ONCOMMA;
10842 wrap->flowed = (GFW_FLOWED & flags) == GFW_FLOWED;
10843 wrap->leave_flowed = (GFW_FLOW_RESULT & flags) == GFW_FLOW_RESULT;
10844 wrap->delsp = (GFW_DELSP & flags) == GFW_DELSP;
10845 wrap->use_color = (GFW_USECOLOR & flags) == GFW_USECOLOR;
10846 wrap->hdr_color = (GFW_HDRCOLOR & flags) == GFW_HDRCOLOR;
10847 wrap->for_compose = (GFW_FORCOMPOSE & flags) == GFW_FORCOMPOSE;
10848 wrap->handle_soft_hyphen = (GFW_SOFTHYPHEN & flags) == GFW_SOFTHYPHEN;
10850 return((void *) wrap);
10854 void *
10855 gf_url_hilite_opt(URL_HILITE_S *uh, HANDLE_S **handlesp, int flags)
10857 if(uh){
10858 memset(uh, 0, sizeof(URL_HILITE_S));
10859 uh->handlesp = handlesp;
10860 uh->hdr_color = (URH_HDRCOLOR & flags) == URH_HDRCOLOR;
10863 return((void *) uh);
10867 #define PF_QD(F) (((PREFLOW_S *)(F)->opt)->quote_depth)
10868 #define PF_QC(F) (((PREFLOW_S *)(F)->opt)->quote_count)
10869 #define PF_SIG(F) (((PREFLOW_S *)(F)->opt)->sig)
10871 typedef struct preflow_s {
10872 int quote_depth,
10873 quote_count,
10874 sig;
10875 } PREFLOW_S;
10878 * This would normally be handled in gf_wrap. If there is a possibility
10879 * that a url we want to recognize is cut in half by a soft newline we
10880 * want to fix that up by putting the halves back together. We do that
10881 * by deleting the soft newline and putting it all in one line. It will
10882 * still get wrapped later in gf_wrap. It isn't pretty with all the
10883 * goto's, but whatta ya gonna do?
10885 void
10886 gf_preflow(FILTER_S *f, int flg)
10888 GF_INIT(f, f->next);
10890 if(flg == GF_DATA){
10891 register unsigned char c;
10892 register int state = f->f1;
10893 register int pending = f->f2;
10895 while(GF_GETC(f, c)){
10896 switch(state){
10897 case DFL:
10898 default_case:
10899 switch(c){
10900 case ' ':
10901 state = WSPACE;
10902 break;
10904 case '\015':
10905 state = CCR;
10906 break;
10908 default:
10909 GF_PUTC(f->next, c);
10910 break;
10913 break;
10915 case CCR:
10916 switch(c){
10917 case '\012':
10918 pending = 1;
10919 state = BOL;
10920 break;
10922 default:
10923 GF_PUTC(f->next, '\012');
10924 state = DFL;
10925 goto default_case;
10926 break;
10929 break;
10931 case WSPACE:
10932 switch(c){
10933 case '\015':
10934 state = SPACECR;
10935 break;
10937 default:
10938 GF_PUTC(f->next, ' ');
10939 state = DFL;
10940 goto default_case;
10941 break;
10944 break;
10946 case SPACECR:
10947 switch(c){
10948 case '\012':
10949 pending = 2;
10950 state = BOL;
10951 break;
10953 default:
10954 GF_PUTC(f->next, ' ');
10955 GF_PUTC(f->next, '\012');
10956 state = DFL;
10957 goto default_case;
10958 break;
10961 break;
10963 case BOL:
10964 PF_QC(f) = 0;
10965 if(c == '>'){ /* count quote level */
10966 PF_QC(f)++;
10967 state = FL_QLEV;
10969 else{
10970 done_counting_quotes:
10971 if(c == ' '){ /* eat stuffed space */
10972 state = FL_STF;
10973 break;
10976 done_with_stuffed_space:
10977 if(c == '-'){ /* look for signature */
10978 PF_SIG(f) = 1;
10979 state = FL_SIG;
10980 break;
10983 done_with_sig:
10984 if(pending == 2){
10985 if(PF_QD(f) == PF_QC(f) && PF_SIG(f) < 4){
10986 /* delete pending */
10988 PF_QD(f) = PF_QC(f);
10990 /* suppress quotes, too */
10991 PF_QC(f) = 0;
10993 else{
10995 * This should have been a hard new line
10996 * instead so leave out the trailing space.
10998 GF_PUTC(f->next, '\015');
10999 GF_PUTC(f->next, '\012');
11001 PF_QD(f) = PF_QC(f);
11004 else if(pending == 1){
11005 GF_PUTC(f->next, '\015');
11006 GF_PUTC(f->next, '\012');
11007 PF_QD(f) = PF_QC(f);
11009 else{
11010 PF_QD(f) = PF_QC(f);
11013 pending = 0;
11014 state = DFL;
11015 while(PF_QC(f)-- > 0)
11016 GF_PUTC(f->next, '>');
11018 switch(PF_SIG(f)){
11019 case 0:
11020 default:
11021 break;
11023 case 1:
11024 GF_PUTC(f->next, '-');
11025 break;
11027 case 2:
11028 GF_PUTC(f->next, '-');
11029 GF_PUTC(f->next, '-');
11030 break;
11032 case 3:
11033 case 4:
11034 GF_PUTC(f->next, '-');
11035 GF_PUTC(f->next, '-');
11036 GF_PUTC(f->next, ' ');
11037 break;
11040 PF_SIG(f) = 0;
11041 goto default_case; /* to handle c */
11044 break;
11046 case FL_QLEV: /* count quote level */
11047 if(c == '>')
11048 PF_QC(f)++;
11049 else
11050 goto done_counting_quotes;
11052 break;
11054 case FL_STF: /* eat stuffed space */
11055 goto done_with_stuffed_space;
11056 break;
11058 case FL_SIG: /* deal with sig indicator */
11059 switch(PF_SIG(f)){
11060 case 1: /* saw '-' */
11061 if(c == '-')
11062 PF_SIG(f) = 2;
11063 else
11064 goto done_with_sig;
11066 break;
11068 case 2: /* saw '--' */
11069 if(c == ' ')
11070 PF_SIG(f) = 3;
11071 else
11072 goto done_with_sig;
11074 break;
11076 case 3: /* saw '-- ' */
11077 if(c == '\015')
11078 PF_SIG(f) = 4; /* it really is a sig line */
11080 goto done_with_sig;
11081 break;
11084 break;
11088 f->f1 = state;
11089 f->f2 = pending;
11090 GF_END(f, f->next);
11092 else if(flg == GF_EOD){
11093 fs_give((void **) &f->opt);
11094 (void) GF_FLUSH(f->next);
11095 (*f->next->f)(f->next, GF_EOD);
11097 else if(flg == GF_RESET){
11098 PREFLOW_S *pf;
11100 pf = (PREFLOW_S *) fs_get(sizeof(*pf));
11101 memset(pf, 0, sizeof(*pf));
11102 f->opt = (void *) pf;
11104 f->f1 = BOL; /* state */
11105 f->f2 = 0; /* pending */
11106 PF_QD(f) = 0; /* quote depth */
11107 PF_QC(f) = 0; /* quote count */
11108 PF_SIG(f) = 0; /* sig level */
11116 * LINE PREFIX FILTER - insert given text at beginning of each
11117 * line
11121 #define GF_PREFIX_WRITE(s) { \
11122 register char *p; \
11123 if((p = (s)) != NULL) \
11124 while(*p) \
11125 GF_PUTC(f->next, *p++); \
11130 * the simple filter, prepends each line with the requested prefix.
11131 * if prefix is null, does nothing, and as with all filters, assumes
11132 * NVT end of lines.
11134 void
11135 gf_prefix(FILTER_S *f, int flg)
11137 GF_INIT(f, f->next);
11139 if(flg == GF_DATA){
11140 register unsigned char c;
11141 register int state = f->f1;
11142 register int first = f->f2;
11144 while(GF_GETC(f, c)){
11146 if(first){ /* write initial prefix!! */
11147 first = 0; /* but just once */
11148 GF_PREFIX_WRITE((char *) f->opt);
11152 * State == 0 is the starting state and the usual state.
11153 * State == 1 means we saw a CR and haven't acted on it yet.
11154 * We are looking for a LF to get the CRLF end of line.
11155 * However, we also treat bare CR and bare LF as if they
11156 * were CRLF sequences. What else could it mean in text?
11157 * This filter is only used for text so that is probably
11158 * a reasonable interpretation of the bad input.
11160 if(c == '\015'){ /* CR */
11161 if(state){ /* Treat pending CR as endofline, */
11162 GF_PUTC(f->next, '\015'); /* and remain in saw-a-CR state. */
11163 GF_PUTC(f->next, '\012');
11164 GF_PREFIX_WRITE((char *) f->opt);
11166 else{
11167 state = 1;
11170 else if(c == '\012'){ /* LF */
11171 GF_PUTC(f->next, '\015'); /* Got either a CRLF or a bare LF, */
11172 GF_PUTC(f->next, '\012'); /* treat both as if a CRLF. */
11173 GF_PREFIX_WRITE((char *) f->opt);
11174 state = 0;
11176 else{ /* any other character */
11177 if(state){
11178 GF_PUTC(f->next, '\015'); /* Treat pending CR as endofline. */
11179 GF_PUTC(f->next, '\012');
11180 GF_PREFIX_WRITE((char *) f->opt);
11181 state = 0;
11184 GF_PUTC(f->next, c);
11188 f->f1 = state; /* save state for next chunk of data */
11189 f->f2 = first;
11190 GF_END(f, f->next);
11192 else if(flg == GF_EOD){
11193 (void) GF_FLUSH(f->next);
11194 (*f->next->f)(f->next, GF_EOD);
11196 else if(flg == GF_RESET){
11197 dprint((9, "-- gf_reset prefix\n"));
11198 f->f1 = 0;
11199 f->f2 = 1; /* nothing written yet */
11205 * function called from the outside to set
11206 * prefix filter's prefix string
11208 void *
11209 gf_prefix_opt(char *prefix)
11211 return((void *) prefix);
11216 * LINE TEST FILTER - accumulate lines and offer each to the provided
11217 * test function.
11220 typedef struct _linetest_s {
11221 linetest_t f;
11222 void *local;
11223 } LINETEST_S;
11226 /* accumulator growth increment */
11227 #define LINE_TEST_BLOCK 1024
11229 #define GF_LINE_TEST_EOB(f) \
11230 ((f)->line + ((f)->f2 - 1))
11232 #define GF_LINE_TEST_ADD(f, c) \
11234 if(p >= eobuf){ \
11235 f->f2 += LINE_TEST_BLOCK; \
11236 fs_resize((void **)&f->line, \
11237 (size_t) f->f2 * sizeof(char)); \
11238 eobuf = GF_LINE_TEST_EOB(f); \
11239 p = eobuf - LINE_TEST_BLOCK; \
11241 *p++ = c; \
11244 #define GF_LINE_TEST_TEST(F, D) \
11246 unsigned char c; \
11247 register char *cp; \
11248 register int l; \
11249 LT_INS_S *ins = NULL, *insp; \
11250 *p = '\0'; \
11251 (D) = (*((LINETEST_S *) (F)->opt)->f)((F)->n++, \
11252 (F)->line, &ins, \
11253 ((LINETEST_S *) (F)->opt)->local); \
11254 if((D) < 2){ \
11255 if((D) < 0){ \
11256 if((F)->line) \
11257 fs_give((void **) &(F)->line); \
11258 if((F)->opt) \
11259 fs_give((void **) &(F)->opt); \
11260 gf_error(_("translation error")); \
11261 /* NO RETURN */ \
11263 for(insp = ins, cp = (F)->line; cp < p; ){ \
11264 if(insp && cp == insp->where){ \
11265 if(insp->len > 0){ \
11266 for(l = 0; l < insp->len; l++){ \
11267 c = (unsigned char) insp->text[l]; \
11268 GF_PUTC((F)->next, c); \
11270 insp = insp->next; \
11271 continue; \
11272 } else if(insp->len < 0){ \
11273 cp -= insp->len; \
11274 insp = insp->next; \
11275 continue; \
11278 GF_PUTC((F)->next, *cp); \
11279 cp++; \
11281 while(insp){ \
11282 for(l = 0; l < insp->len; l++){ \
11283 c = (unsigned char) insp->text[l]; \
11284 GF_PUTC((F)->next, c); \
11286 insp = insp->next; \
11288 gf_line_test_free_ins(&ins); \
11295 * this simple filter accumulates characters until a newline, offers it
11296 * to the provided test function, and then passes it on. It assumes
11297 * NVT EOLs.
11299 void
11300 gf_line_test(FILTER_S *f, int flg)
11302 register char *p = f->linep;
11303 register char *eobuf = GF_LINE_TEST_EOB(f);
11304 GF_INIT(f, f->next);
11306 if(flg == GF_DATA){
11307 register unsigned char c;
11308 register int state = f->f1;
11310 while(GF_GETC(f, c)){
11312 if(state){
11313 state = 0;
11314 if(c == '\012'){
11315 int done;
11317 GF_LINE_TEST_TEST(f, done);
11319 p = (f)->line;
11321 if(done == 2) /* skip this line! */
11322 continue;
11324 GF_PUTC(f->next, '\015');
11325 GF_PUTC(f->next, '\012');
11327 * if the line tester returns TRUE, it's
11328 * telling us its seen enough and doesn't
11329 * want to see any more. Remove ourself
11330 * from the pipeline...
11332 if(done){
11333 if(gf_master == f){
11334 gf_master = f->next;
11336 else{
11337 FILTER_S *fprev;
11339 for(fprev = gf_master;
11340 fprev && fprev->next != f;
11341 fprev = fprev->next)
11344 if(fprev) /* wha??? */
11345 fprev->next = f->next;
11346 else
11347 continue;
11350 while(GF_GETC(f, c)) /* pass input */
11351 GF_PUTC(f->next, c);
11353 (void) GF_FLUSH(f->next); /* and drain queue */
11354 fs_give((void **)&f->line);
11355 fs_give((void **)&f); /* wax our data */
11356 return;
11358 else
11359 continue;
11361 else /* add CR to buffer */
11362 GF_LINE_TEST_ADD(f, '\015');
11363 } /* fall thru to handle 'c' */
11365 if(c == '\015') /* newline? */
11366 state = 1;
11367 else
11368 GF_LINE_TEST_ADD(f, c);
11371 f->f1 = state;
11372 GF_END(f, f->next);
11374 else if(flg == GF_EOD){
11375 int i;
11377 GF_LINE_TEST_TEST(f, i); /* examine remaining data */
11378 fs_give((void **) &f->line); /* free line buffer */
11379 fs_give((void **) &f->opt); /* free test struct */
11380 (void) GF_FLUSH(f->next);
11381 (*f->next->f)(f->next, GF_EOD);
11383 else if(flg == GF_RESET){
11384 dprint((9, "-- gf_reset line_test\n"));
11385 f->f1 = 0; /* state */
11386 f->n = 0L; /* line number */
11387 f->f2 = LINE_TEST_BLOCK; /* size of alloc'd line */
11388 f->line = p = (char *) fs_get(f->f2 * sizeof(char));
11391 f->linep = p;
11396 * function called from the outside to operate on accumulated line.
11398 void *
11399 gf_line_test_opt(linetest_t test_f, void *local)
11401 LINETEST_S *ltp;
11403 ltp = (LINETEST_S *) fs_get(sizeof(LINETEST_S));
11404 memset(ltp, 0, sizeof(LINETEST_S));
11405 ltp->f = test_f;
11406 ltp->local = local;
11407 return((void *) ltp);
11412 LT_INS_S **
11413 gf_line_test_new_ins(LT_INS_S **ins, char *p, char *s, int n)
11415 *ins = (LT_INS_S *) fs_get(sizeof(LT_INS_S));
11416 if(((*ins)->len = n) > 0)
11417 strncpy((*ins)->text = (char *) fs_get(n * sizeof(char)), s, n);
11418 else
11419 (*ins)->text = NULL;
11421 (*ins)->where = p;
11422 (*ins)->next = NULL;
11423 return(&(*ins)->next);
11427 void
11428 gf_line_test_free_ins(LT_INS_S **ins)
11430 if(ins && *ins){
11431 if((*ins)->next)
11432 gf_line_test_free_ins(&(*ins)->next);
11434 if((*ins)->text)
11435 fs_give((void **) &(*ins)->text);
11437 fs_give((void **) ins);
11443 * PREPEND EDITORIAL FILTER - conditionally prepend output text
11444 * with editorial comment
11447 typedef struct _preped_s {
11448 prepedtest_t f;
11449 char *text;
11450 } PREPED_S;
11454 * gf_prepend_editorial - accumulate filtered text and prepend its
11455 * output with given text
11459 void
11460 gf_prepend_editorial(FILTER_S *f, int flg)
11462 GF_INIT(f, f->next);
11464 if(flg == GF_DATA){
11465 register unsigned char c;
11467 while(GF_GETC(f, c)){
11468 so_writec(c, (STORE_S *) f->data);
11471 GF_END(f, f->next);
11473 else if(flg == GF_EOD){
11474 unsigned char c;
11476 if(!((PREPED_S *)(f)->opt)->f || (*((PREPED_S *)(f)->opt)->f)()){
11477 char *p = ((PREPED_S *)(f)->opt)->text;
11479 for( ; p && *p; p++)
11480 GF_PUTC(f->next, *p);
11483 so_seek((STORE_S *) f->data, 0L, 0);
11484 while(so_readc(&c, (STORE_S *) f->data)){
11485 GF_PUTC(f->next, c);
11488 so_give((STORE_S **) &f->data);
11489 fs_give((void **) &f->opt);
11490 (void) GF_FLUSH(f->next);
11491 (*f->next->f)(f->next, GF_EOD);
11493 else if(flg == GF_RESET){
11494 dprint((9, "-- gf_reset line_test\n"));
11495 f->data = (void *) so_get(CharStar, NULL, EDIT_ACCESS);
11501 * function called from the outside to setup prepending editorial
11502 * to output text
11504 void *
11505 gf_prepend_editorial_opt(prepedtest_t test_f, char *text)
11507 PREPED_S *pep;
11509 pep = (PREPED_S *) fs_get(sizeof(PREPED_S));
11510 memset(pep, 0, sizeof(PREPED_S));
11511 pep->f = test_f;
11512 pep->text = text;
11513 return((void *) pep);
11518 * Network virtual terminal to local newline convention filter
11520 void
11521 gf_nvtnl_local(FILTER_S *f, int flg)
11523 GF_INIT(f, f->next);
11525 if(flg == GF_DATA){
11526 register unsigned char c;
11527 register int state = f->f1;
11529 while(GF_GETC(f, c)){
11530 if(state){
11531 state = 0;
11532 if(c == '\012'){
11533 GF_PUTC(f->next, '\012');
11534 continue;
11536 else
11537 GF_PUTC(f->next, '\015');
11538 /* fall thru to deal with 'c' */
11541 if(c == '\015')
11542 state = 1;
11543 else
11544 GF_PUTC(f->next, c);
11547 f->f1 = state;
11548 GF_END(f, f->next);
11550 else if(flg == GF_EOD){
11551 (void) GF_FLUSH(f->next);
11552 (*f->next->f)(f->next, GF_EOD);
11554 else if(flg == GF_RESET){
11555 dprint((9, "-- gf_reset nvtnl_local\n"));
11556 f->f1 = 0;
11562 * local to network newline convention filter
11564 void
11565 gf_local_nvtnl(FILTER_S *f, int flg)
11567 GF_INIT(f, f->next);
11569 if(flg == GF_DATA){
11570 register unsigned char c;
11572 while(GF_GETC(f, c)){
11573 if(c == '\012'){
11574 GF_PUTC(f->next, '\015');
11575 GF_PUTC(f->next, '\012');
11577 else if(c != '\015') /* do not copy isolated \015 into source */
11578 GF_PUTC(f->next, c);
11581 GF_END(f, f->next);
11583 else if(flg == GF_EOD){
11584 (void) GF_FLUSH(f->next);
11585 (*f->next->f)(f->next, GF_EOD);
11587 else if(GF_RESET){
11588 dprint((9, "-- gf_reset local_nvtnl\n"));
11589 /* no op */
11594 void
11595 free_filter_module_globals(void)
11597 FILTER_S *flt, *fltn = gf_master;
11599 while((flt = fltn) != NULL){ /* free list of old filters */
11600 fltn = flt->next;
11601 fs_give((void **)&flt);