element handler
- */
-int
-html_address(hd, ch, cmd)
- HANDLER_S *hd;
- int ch, cmd;
-{
- int j;
-#define HTML_ADD_INDENT 2
-
- if(cmd == GF_DATA){
- html_handoff(hd, ch);
- }
- else if(cmd == GF_RESET){
- /*
- * A typical rendering might be a slight extra left and
- * right indent, and/or italic font. The Blockquote element
- * causes a paragraph break, and typically provides space
- * above and below the quote.
- */
- html_indent(hd->html_data, HTML_ADD_INDENT, HTML_ID_INC);
- j = HD(hd->html_data)->wrapstate;
- HD(hd->html_data)->wrapstate = 0;
- html_blank(hd->html_data, 1);
- HD(hd->html_data)->wrapstate = j;
- }
- else if(cmd == GF_EOD){
- html_blank(hd->html_data, 1);
-
- j = HD(hd->html_data)->wrapstate;
- HD(hd->html_data)->wrapstate = 0;
- html_indent(hd->html_data, -(HTML_ADD_INDENT), HTML_ID_INC);
- HD(hd->html_data)->wrapstate = j;
- }
-
- return(1); /* get linked */
-}
-
-
-/*
- * HTML (Preformatted Text) element handler
- */
-int
-html_pre(hd, ch, cmd)
- HANDLER_S *hd;
- int ch, cmd;
-{
- if(cmd == GF_DATA){
- /*
- * remove CRLF after '>' in element.
- * We see CRLF because wrapstate is off.
- */
- switch(hd->y){
- case 2 :
- if(ch == '\012'){
- hd->y = 3;
- return(1);
- }
- else
- html_handoff(hd, '\015');
-
- break;
-
- case 1 :
- if(ch == '\015'){
- hd->y = 2;
- return(1);
- }
-
- default :
- hd->y = 0;
- break;
- }
-
- html_handoff(hd, ch);
- }
- else if(cmd == GF_RESET){
- html_blank(hd->html_data, 1);
- hd->x = HD(hd->html_data)->wrapstate;
- HD(hd->html_data)->wrapstate = 0;
- hd->y = 1;
- }
- else if(cmd == GF_EOD){
- HD(hd->html_data)->wrapstate = (hd->x != 0);
- html_blank(hd->html_data, 0);
- }
-
- return(1);
-}
-
-
-
-
-/*
- * HTML (Centerd Text) element handler
- */
-int
-html_center(hd, ch, cmd)
- HANDLER_S *hd;
- int ch, cmd;
-{
- if(cmd == GF_DATA){
- html_handoff(hd, ch);
- }
- else if(cmd == GF_RESET){
- /* turn ON the centered bit */
- CENTER_BIT(hd->html_data) = 1;
- }
- else if(cmd == GF_EOD){
- /* turn OFF the centered bit */
- CENTER_BIT(hd->html_data) = 0;
- }
-
- return(1);
-}
-
-
-
-/*
- * HTML (Document Divisions) element handler
- */
-int
-html_div(hd, ch, cmd)
- HANDLER_S *hd;
- int ch, cmd;
-{
- if(cmd == GF_DATA){
- html_handoff(hd, ch);
- }
- else if(cmd == GF_RESET){
- PARAMETER *p;
-
- for(p = HD(hd->html_data)->el_data->attribs;
- p && p->attribute;
- p = p->next)
- if(!strucmp(p->attribute, "ALIGN")){
- if(p->value){
- /* remember previous values */
- hd->x = CENTER_BIT(hd->html_data);
- hd->y = html_indent(hd->html_data, 0, HTML_ID_GET);
-
- html_blank(hd->html_data, 0);
- CENTER_BIT(hd->html_data) = !strucmp(p->value, "CENTER");
- html_indent(hd->html_data, 0, HTML_ID_SET);
- /* NOTE: "RIGHT" not supported yet */
- }
- }
- }
- else if(cmd == GF_EOD){
- /* restore centered bit and indentiousness */
- CENTER_BIT(hd->html_data) = hd->y;
- html_indent(hd->html_data, hd->y, HTML_ID_SET);
- html_blank(hd->html_data, 0);
- }
-
- return(1);
-}
-
-
-
-/*
- * return the function associated with the given element name
- */
-html_f
-html_element_func(el_name)
- char *el_name;
-{
- register int i;
-
- for(i = 0; element_table[i].element; i++)
- if(!strucmp(el_name, element_table[i].element))
- return(element_table[i].handler);
-
- return(NULL);
-}
-
-
-/*
- * collect element's name and any attribute/value pairs then
- * dispatch to the appropriate handler.
- *
- * Returns 1 : got what we wanted
- * 0 : we need more data
- * -1 : bogus input
- */
-int
-html_element_collector(fd, ch)
- FILTER_S *fd;
- int ch;
-{
- if(ch == '>'){
- if(ED(fd)->overrun){
- /*
- * If problem processing, don't bother doing anything
- * internally, just return such that none of what we've
- * digested is displayed.
- */
- HTML_DEBUG_EL("too long", ED(fd));
- return(1); /* Let it go, Jim */
- }
- else if(ED(fd)->mkup_decl){
- if(ED(fd)->badform){
- dprint(2, (debugfile, "-- html queueout init stuff below.
- */
-#define GF_QUE_START(F) (&(F)->queue[0])
-#define GF_QUE_END(F) (&(F)->queue[GF_MAXBUF - 1])
-
-#define GF_IP_INIT(F) ip = (F) ? &(F)->queue[(F)->queuein] : NULL
-#define GF_EIB_INIT(F) eib = (F) ? GF_QUE_END(F) : NULL
-#define GF_OP_INIT(F) op = (F) ? &(F)->queue[(F)->queueout] : NULL
-#define GF_EOB_INIT(F) eob = (F) ? &(F)->queue[(F)->queuein] : NULL
-
-#define GF_IP_END(F) (F)->queuein = ip - GF_QUE_START(F)
-#define GF_OP_END(F) (F)->queueout = op - GF_QUE_START(F)
-
-#define GF_INIT(FI, FO) register unsigned char *GF_OP_INIT(FI); \
- register unsigned char *GF_EOB_INIT(FI); \
- register unsigned char *GF_IP_INIT(FO); \
- register unsigned char *GF_EIB_INIT(FO);
-
-#define GF_CH_RESET(F) (op = eob = GF_QUE_START(F), \
- (F)->queueout = (F)->queuein = 0)
-
-#define GF_END(FI, FO) (GF_OP_END(FI), GF_IP_END(FO))
-
-#define GF_FLUSH(F) ((int)(GF_IP_END(F), (*(F)->f)((F), GF_DATA), \
- GF_IP_INIT(F), GF_EIB_INIT(F)))
-
-#define GF_PUTC(F, C) ((int)(*ip++ = (C), (ip >= eib) ? GF_FLUSH(F) : 1))
-
-#define GF_GETC(F, C) ((op < eob) ? (((C) = *op++), 1) : GF_CH_RESET(F))
-
-
-/*
- * Generalized getc and putc routines. provided here so they don't
- * need to be re-done elsewhere to
- */
-
-/*
- * pointers to objects to be used by the generic getc and putc
- * functions
- */
-static struct gf_io_struct {
- FILE *file;
- char *txtp;
- unsigned long n;
-} gf_in, gf_out;
-
-
-#define GF_SO_STACK struct gf_so_stack
-static GF_SO_STACK {
- STORE_S *so;
- GF_SO_STACK *next;
-} *gf_so_in, *gf_so_out;
-
-
-/*
- * setup to use and return a pointer to the generic
- * getc function
- */
-void
-gf_set_readc(gc, txt, len, src)
- gf_io_t *gc;
- void *txt;
- unsigned long len;
- SourceType src;
-{
- gf_in.n = len;
- if(src == FileStar){
- gf_in.file = (FILE *)txt;
- fseek(gf_in.file, 0L, 0);
- *gc = gf_freadc;
- }
- else{
- gf_in.txtp = (char *)txt;
- *gc = gf_sreadc;
- }
-}
-
-
-/*
- * setup to use and return a pointer to the generic
- * putc function
- */
-void
-gf_set_writec(pc, txt, len, src)
- gf_io_t *pc;
- void *txt;
- unsigned long len;
- SourceType src;
-{
- gf_out.n = len;
- if(src == FileStar){
- gf_out.file = (FILE *)txt;
- *pc = gf_fwritec;
- }
- else{
- gf_out.txtp = (char *)txt;
- *pc = gf_swritec;
- }
-}
-
-
-/*
- * setup to use and return a pointer to the generic
- * getc function
- */
-void
-gf_set_so_readc(gc, so)
- gf_io_t *gc;
- STORE_S *so;
-{
- GF_SO_STACK *sp = (GF_SO_STACK *) fs_get(sizeof(GF_SO_STACK));
-
- sp->so = so;
- sp->next = gf_so_in;
- gf_so_in = sp;
- *gc = gf_so_readc;
-}
-
-
-void
-gf_clear_so_readc(so)
- STORE_S *so;
-{
- GF_SO_STACK *sp;
-
- if(sp = gf_so_in){
- if(so == sp->so){
- gf_so_in = gf_so_in->next;
- fs_give((void **) &sp);
- }
- else
- panic("Programmer botch: Can't unstack store readc");
- }
- else
- panic("Programmer botch: NULL store clearing store readc");
-}
-
-
-/*
- * setup to use and return a pointer to the generic
- * putc function
- */
-void
-gf_set_so_writec(pc, so)
- gf_io_t *pc;
- STORE_S *so;
-{
- GF_SO_STACK *sp = (GF_SO_STACK *) fs_get(sizeof(GF_SO_STACK));
-
- sp->so = so;
- sp->next = gf_so_out;
- gf_so_out = sp;
- *pc = gf_so_writec;
-}
-
-
-void
-gf_clear_so_writec(so)
- STORE_S *so;
-{
- GF_SO_STACK *sp;
-
- if(sp = gf_so_out){
- if(so == sp->so){
- gf_so_out = gf_so_out->next;
- fs_give((void **) &sp);
- }
- else
- panic("Programmer botch: Can't unstack store writec");
- }
- else
- panic("Programmer botch: NULL store clearing store writec");
-}
-
-
-/*
- * put the character to the object previously defined
- */
-int
-gf_so_writec(c)
-int c;
-{
- return(so_writec(c, gf_so_out->so));
-}
-
-
-/*
- * get a character from an object previously defined
- */
-int
-gf_so_readc(c)
-unsigned char *c;
-{
- return(so_readc(c, gf_so_in->so));
-}
-
-
-/* get a character from a file */
-/* assumes gf_out struct is filled in */
-int
-gf_freadc(c)
-unsigned char *c;
-{
- int rv = 0;
-
- do {
- errno = 0;
- clearerr(gf_in.file);
- rv = fread(c, sizeof(unsigned char), (size_t)1, gf_in.file);
- } while(!rv && ferror(gf_in.file) && errno == EINTR);
-
- return(rv);
-}
-
-
-/* put a character to a file */
-/* assumes gf_out struct is filled in */
-int
-gf_fwritec(c)
- int c;
-{
- unsigned char ch = (unsigned char)c;
- int rv = 0;
-
- do
- rv = fwrite(&ch, sizeof(unsigned char), (size_t)1, gf_out.file);
- while(!rv && ferror(gf_out.file) && errno == EINTR);
-
- return(rv);
-}
-
-
-/* get a character from a string, return nonzero if things OK */
-/* assumes gf_out struct is filled in */
-int
-gf_sreadc(c)
-unsigned char *c;
-{
- return((gf_in.n) ? *c = *(gf_in.txtp)++, gf_in.n-- : 0);
-}
-
-
-/* put a character into a string, return nonzero if things OK */
-/* assumes gf_out struct is filled in */
-int
-gf_swritec(c)
- int c;
-{
- return((gf_out.n) ? *(gf_out.txtp)++ = c, gf_out.n-- : 0);
-}
-
-
-/*
- * output the given string with the given function
- */
-int
-gf_puts(s, pc)
- register char *s;
- gf_io_t pc;
-{
- while(*s != '\0')
- if(!(*pc)((unsigned char)*s++))
- return(0); /* ERROR putting char ! */
-
- return(1);
-}
-
-
-/*
- * output the given string with the given function
- */
-int
-gf_nputs(s, n, pc)
- register char *s;
- long n;
- gf_io_t pc;
-{
- while(n--)
- if(!(*pc)((unsigned char)*s++))
- return(0); /* ERROR putting char ! */
-
- return(1);
-}
-
-
-/*
- * Start of generalized filter routines
- */
-
-/*
- * initializing function to make sure list of filters is empty.
- */
-void
-gf_filter_init()
-{
- FILTER_S *flt, *fltn = gf_master;
-
- while((flt = fltn) != NULL){ /* free list of old filters */
- fltn = flt->next;
- fs_give((void **)&flt);
- }
-
- gf_master = NULL;
- gf_error_string = NULL; /* clear previous errors */
- gf_byte_count = 0L; /* reset counter */
-}
-
-
-
-/*
- * link the given filter into the filter chain
- */
-void
-gf_link_filter(f, data)
- filter_t f;
- void *data;
-{
- FILTER_S *new, *tail;
-
-#ifdef CRLF_NEWLINES
- /*
- * If the system's native EOL convention is CRLF, then there's no
- * point in passing data thru a filter that's not doing anything
- */
- if(f == gf_nvtnl_local || f == gf_local_nvtnl)
- return;
-#endif
-
- new = (FILTER_S *)fs_get(sizeof(FILTER_S));
- memset(new, 0, sizeof(FILTER_S));
-
- new->f = f; /* set the function pointer */
- new->opt = data; /* set any optional parameter data */
- (*f)(new, GF_RESET); /* have it setup initial state */
-
- if(tail = gf_master){ /* or add it to end of existing */
- while(tail->next) /* list */
- tail = tail->next;
-
- tail->next = new;
- }
- else /* attach new struct to list */
- gf_master = new; /* start a new list */
-}
-
-
-/*
- * terminal filter, doesn't call any other filters, typically just does
- * something with the output
- */
-void
-gf_terminal(f, flg)
- FILTER_S *f;
- int flg;
-{
- if(flg == GF_DATA){
- GF_INIT(f, f);
-
- while(op < eob)
- if((*last_filter)(*op++) <= 0) /* generic terminal filter */
- gf_error(errno ? error_description(errno) : "Error writing pipe");
-
- GF_CH_RESET(f);
- }
- else if(flg == GF_RESET)
- errno = 0; /* prepare for problems */
-}
-
-
-/*
- * set some outside gf_io_t function to the terminal function
- * for example: a function to write a char to a file or into a buffer
- */
-void
-gf_set_terminal(f) /* function to set generic filter */
- gf_io_t f;
-{
- last_filter = f;
-}
-
-
-/*
- * common function for filter's to make it known that an error
- * has occurred. Jumps back to gf_pipe with error message.
- */
-void
-gf_error(s)
- char *s;
-{
- /* let the user know the error passed in s */
- gf_error_string = s;
- longjmp(gf_error_state, 1);
-}
-
-
-/*
- * The routine that shoves each byte through the chain of
- * filters. It sets up error handling, and the terminal function.
- * Then loops getting bytes with the given function, and passing
- * it on to the first filter in the chain.
- */
-char *
-gf_pipe(gc, pc)
- gf_io_t gc, pc; /* how to get a character */
-{
- unsigned char c;
-
-#if defined(DOS) && !defined(_WINDOWS)
- MoveCursor(0, 1);
- StartInverse();
-#endif
-
- dprint(4, (debugfile, "-- gf_pipe: "));
-
- /*
- * set up for any errors a filter may encounter
- */
- if(setjmp(gf_error_state)){
-#if defined(DOS) && !defined(_WINDOWS)
- ibmputc(' ');
- EndInverse();
-#endif
- dprint(4, (debugfile, "ERROR: %s\n",
- gf_error_string ? gf_error_string : "NULL"));
- return(gf_error_string); /* */
- }
-
- /*
- * set and link in the terminal filter
- */
- gf_set_terminal(pc);
- gf_link_filter(gf_terminal, NULL);
-
- /*
- * while there are chars to process, send them thru the pipe.
- * NOTE: it's necessary to enclose the loop below in a block
- * as the GF_INIT macro calls some automatic var's into
- * existence. It can't be placed at the start of gf_pipe
- * because its useful for us to be called without filters loaded
- * when we're just being used to copy bytes between storage
- * objects.
- */
- {
- GF_INIT(gf_master, gf_master);
-
- while((*gc)(&c)){
- gf_byte_count++;
-#ifdef DOS
- if(!(gf_byte_count & 0x3ff))
-#ifdef _WINDOWS
- /* Under windows we yeild to allow event processing.
- * Progress display is handled throught the alarm()
- * mechinism.
- */
- mswin_yeild ();
-#else
- /* Poor PC still needs spinning bar */
- ibmputc("/-\\|"[((int) gf_byte_count >> 10) % 4]);
- MoveCursor(0, 1);
-#endif
-#endif
-
- GF_PUTC(gf_master, c & 0xff);
- }
-
- /*
- * toss an end-of-data marker down the pipe to give filters
- * that have any buffered data the opportunity to dump it
- */
- GF_FLUSH(gf_master);
- (*gf_master->f)(gf_master, GF_EOD);
- }
-
-#if defined(DOS) && !defined(_WINDOWS)
- ibmputc(' ');
- EndInverse();
-#endif
-
- dprint(1, (debugfile, "done.\n"));
- return(NULL); /* everything went OK */
-}
-
-
-/*
- * return the number of bytes piped so far
- */
-long
-gf_bytes_piped()
-{
- return(gf_byte_count);
-}
-
-
-/*
- * filter the given input with the given command
- *
- * Args: cmd -- command string to execute
- * prepend -- string to prepend to filtered input
- * source_so -- storage object containing data to be filtered
- * pc -- function to write filtered output with
- * aux_filters -- additional filters to pass data thru after "cmd"
- *
- * Returns: NULL on sucess, reason for failure (not alloc'd!) on error
- */
-char *
-gf_filter(cmd, prepend, source_so, pc, aux_filters)
- char *cmd, *prepend;
- STORE_S *source_so;
- gf_io_t pc;
- FILTLIST_S *aux_filters;
-{
- unsigned char c;
- int flags;
- char *errstr = NULL, buf[MAILTMPLEN], *rfile = NULL;
- PIPE_S *fpipe;
-
- dprint(4, (debugfile, "so_filter: \"%s\"\n", cmd));
-
- gf_filter_init();
- for( ; aux_filters && aux_filters->filter; aux_filters++)
- gf_link_filter(aux_filters->filter, aux_filters->data);
-
- gf_set_terminal(pc);
- gf_link_filter(gf_terminal, NULL);
-
- /*
- * Spawn filter feeding it data, and reading what it writes.
- */
- so_seek(source_so, 0L, 0);
-#ifdef NO_PIPE
- /*
- * When there're no pipes for IPC, use an output file to collect
- * the result...
- */
- flags = PIPE_WRITE | PIPE_NOSHELL | PIPE_RESET;
- rfile = temp_nam(NULL, "pf");
-#else
- flags = PIPE_WRITE | PIPE_READ | PIPE_NOSHELL | PIPE_RESET;
-#endif
-
- if(fpipe = open_system_pipe(cmd, rfile ? &rfile : NULL, NULL, flags, 0)){
-#ifdef NO_PIPE
- if(prepend && (fputs(prepend, fpipe->out.f) == EOF
- || fputc('\n', fpipe->out.f) == EOF))
- errstr = error_description(errno);
-
- /*
- * Write the output, and deal with the result later...
- */
- while(!errstr && so_readc(&c, source_so))
- if(fputc(c, fpipe->out.f) == EOF)
- errstr = error_description(errno);
-#else
-#ifdef NON_BLOCKING_IO
- int n;
-
- if(fcntl(fileno(fpipe->in.f), F_SETFL, NON_BLOCKING_IO) == -1)
- errstr = "Can't set up non-blocking IO";
-
- if(prepend && (fputs(prepend, fpipe->out.f) == EOF
- || fputc('\n', fpipe->out.f) == EOF))
- errstr = error_description(errno);
-
- while(!errstr){
- /* if the pipe can't hold a K we're sunk (too bad PIPE_MAX
- * isn't ubiquitous ;).
- */
- for(n = 0; !errstr && fpipe->out.f && n < 1024; n++)
- if(!so_readc(&c, source_so)){
- fclose(fpipe->out.f);
- fpipe->out.f = NULL;
- }
- else if(fputc(c, fpipe->out.f) == EOF)
- errstr = error_description(errno);
-
- /*
- * Note: We clear errno here and test below, before ferror,
- * because *some* stdio implementations consider
- * EAGAIN and EWOULDBLOCK equivalent to EOF...
- */
- errno = 0;
- clearerr(fpipe->in.f); /* fix from
*/
-
- while(!errstr && fgets(buf, MAILTMPLEN, fpipe->in.f))
- errstr = gf_filter_puts(buf);
-
- /* then fgets failed! */
- if(!errstr && !(errno == EAGAIN || errno == EWOULDBLOCK)){
- if(feof(fpipe->in.f)) /* nothing else interesting! */
- break;
- else if(ferror(fpipe->in.f)) /* bummer. */
- errstr = error_description(errno);
- }
- else if(errno == EAGAIN || errno == EWOULDBLOCK)
- clearerr(fpipe->in.f);
- }
-#else
- if(prepend && (fputs(prepend, fpipe->out.f) == EOF
- || fputc('\n', fpipe->out.f) == EOF))
- errstr = error_description(errno);
-
- /*
- * Well, do the best we can, and hope the pipe we're writing
- * doesn't fill up before we start reading...
- */
- while(!errstr && so_readc(&c, source_so))
- if(fputc(c, fpipe->out.f) == EOF)
- errstr = error_description(errno);
-
- fclose(fpipe->out.f);
- fpipe->out.f = NULL;
- while(!errstr && fgets(buf, MAILTMPLEN, fpipe->in.f))
- errstr = gf_filter_puts(buf);
-#endif /* NON_BLOCKING */
-#endif /* NO_PIPE */
-
- gf_filter_eod();
-
- if(close_system_pipe(&fpipe) && !errstr)
- errstr = "Pipe command returned error.";
-
-#ifdef NO_PIPE
- /*
- * retrieve filters result...
- */
- {
- FILE *fp;
-
- if(fp = fopen(rfile, STDIO_READ)){
- while(!errstr && fgets(buf, MAILTMPLEN, fp))
- errstr = gf_filter_puts(buf);
-
- fclose(fp);
- }
-
- fs_give((void **)&rfile);
- }
-#endif
- }
-
- return(errstr);
-}
-
-
-/*
- * gf_filter_puts - write the given string down the filter's pipe
- */
-char *
-gf_filter_puts(s)
- register char *s;
-{
- GF_INIT(gf_master, gf_master);
-
- /*
- * set up for any errors a filter may encounter
- */
- if(setjmp(gf_error_state)){
- dprint(4, (debugfile, "ERROR: gf_filter_puts: %s\n",
- gf_error_string ? gf_error_string : "NULL"));
- return(gf_error_string);
- }
-
- while(*s)
- GF_PUTC(gf_master, (*s++) & 0xff);
-
- GF_END(gf_master, gf_master);
- return(NULL);
-}
-
-
-/*
- * gf_filter_eod - flush pending data filter's input queue and deliver
- * the GF_EOD marker.
- */
-void
-gf_filter_eod()
-{
- GF_INIT(gf_master, gf_master);
- GF_FLUSH(gf_master);
- (*gf_master->f)(gf_master, GF_EOD);
-}
-
-
-
-
-/*
- * END OF PIPE SUPPORT ROUTINES, BEGINNING OF FILTERS
- *
- * Filters MUST use the specified interface (pointer to filter
- * structure, the unsigned character buffer in that struct, and a
- * cmd flag), and pass each resulting octet to the next filter in the
- * chain. Only the terminal filter need not call another filter.
- * As a result, filters share a pretty general structure.
- * Typically three main conditionals separate initialization from
- * data from end-of-data command processing.
- *
- * Lastly, being character-at-a-time, they're a little more complex
- * to write than filters operating on buffers because some state
- * must typically be kept between characters. However, for a
- * little bit of complexity here, much convenience is gained later
- * as they can be arbitrarily chained together at run time and
- * consume few resources (especially memory or disk) as they work.
- * (NOTE 951005: even less cpu now that data between filters is passed
- * via a vector.)
- *
- * A few notes about implementing filters:
- *
- * - A generic filter template looks like:
- *
- * void
- * gf_xxx_filter(f, flg)
- * FILTER_S *f;
- * int flg;
- * {
- * GF_INIT(f, f->next); // def's var's to speed queue drain
- *
- * if(flg == GF_DATA){
- * register unsigned char c;
- *
- * while(GF_GETC(f, c)){ // macro taking data off input queue
- * // operate on c and pass it on here
- * GF_PUTC(f->next, c); // macro writing output queue
- * }
- *
- * GF_END(f, f->next); // macro to sync pointers/offsets
- * //WARNING: DO NOT RETURN BEFORE ALL INCOMING DATA'S PROCESSED
- * }
- * else if(flg == GF_EOD){
- * // process any buffered data here and pass it on
- * GF_FLUSH(f->next); // flush pending data to next filter
- * (*f->next->f)(f->next, GF_EOD);
- * }
- * else if(flg == GF_RESET){
- * // initialize any data in the struct here
- * }
- * }
- *
- * - Any free storage allocated during initialization (typically tied
- * to the "line" pointer in FILTER_S) is the filter's responsibility
- * to clean up when the GF_EOD command comes through.
- *
- * - Filter's must pass GF_EOD they receive on to the next
- * filter in the chain so it has the opportunity to flush
- * any buffered data.
- *
- * - All filters expect NVT end-of-lines. The idea is to prepend
- * or append either the gf_local_nvtnl or gf_nvtnl_local
- * os-dependant filters to the data on the appropriate end of the
- * pipe for the task at hand.
- *
- * - NOTE: As of 951004, filters no longer take their input as a single
- * char argument, but rather get data to operate on via a vector
- * representing the input queue in the FILTER_S structure.
- *
- */
-
-
-
-/*
- * BASE64 TO BINARY encoding and decoding routines below
- */
-
-
-/*
- * BINARY to BASE64 filter (encoding described in rfc1341)
- */
-void
-gf_binary_b64(f, flg)
- FILTER_S *f;
- int flg;
-{
- static char *v =
- "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
- GF_INIT(f, f->next);
-
- if(flg == GF_DATA){
- register unsigned char c;
- register unsigned char t = f->t;
- register long n = f->n;
-
- while(GF_GETC(f, c)){
-
- switch(n++){
- case 0 : case 3 : case 6 : case 9 : case 12: case 15: case 18:
- case 21: case 24: case 27: case 30: case 33: case 36: case 39:
- case 42: case 45:
- GF_PUTC(f->next, v[c >> 2]);
- /* byte 1: high 6 bits (1) */
- t = c << 4; /* remember high 2 bits for next */
- break;
-
- case 1 : case 4 : case 7 : case 10: case 13: case 16: case 19:
- case 22: case 25: case 28: case 31: case 34: case 37: case 40:
- case 43:
- GF_PUTC(f->next, v[(t|(c>>4)) & 0x3f]);
- t = c << 2;
- break;
-
- case 2 : case 5 : case 8 : case 11: case 14: case 17: case 20:
- case 23: case 26: case 29: case 32: case 35: case 38: case 41:
- case 44:
- GF_PUTC(f->next, v[(t|(c >> 6)) & 0x3f]);
- GF_PUTC(f->next, v[c & 0x3f]);
- break;
- }
-
- if(n == 45){ /* start a new line? */
- GF_PUTC(f->next, '\015');
- GF_PUTC(f->next, '\012');
- n = 0L;
- }
- }
-
- f->n = n;
- f->t = t;
- GF_END(f, f->next);
- }
- else if(flg == GF_EOD){ /* no more data */
- switch (f->n % 3) { /* handle trailing bytes */
- case 0: /* no trailing bytes */
- break;
-
- case 1:
- GF_PUTC(f->next, v[(f->t) & 0x3f]);
- GF_PUTC(f->next, '='); /* byte 3 */
- GF_PUTC(f->next, '='); /* byte 4 */
- break;
-
- case 2:
- GF_PUTC(f->next, v[(f->t) & 0x3f]);
- GF_PUTC(f->next, '='); /* byte 4 */
- break;
- }
-
- GF_FLUSH(f->next);
- (*f->next->f)(f->next, GF_EOD);
- }
- else if(flg == GF_RESET){
- dprint(9, (debugfile, "-- gf_reset binary_b64\n"));
- f->n = 0L;
- }
-}
-
-
-
-/*
- * BASE64 to BINARY filter (encoding described in rfc1341)
- */
-void
-gf_b64_binary(f, flg)
- FILTER_S *f;
- int flg;
-{
- static char v[] = {65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,
- 65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,65,
- 65,65,65,65,65,65,65,65,65,65,65,62,65,65,65,63,
- 52,53,54,55,56,57,58,59,60,61,62,65,65,64,65,65,
- 65, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14,
- 15,16,17,18,19,20,21,22,23,24,25,65,65,65,65,65,
- 65,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,
- 41,42,43,44,45,46,47,48,49,50,51,65,65,65,65,65};
- GF_INIT(f, f->next);
-
- if(flg == GF_DATA){
- register unsigned char c;
- register unsigned char t = f->t;
- register int n = (int) f->n;
- register int state = f->f1;
-
- while(GF_GETC(f, c)){
-
- if(state){
- state = 0;
- if (c != '=') {
- gf_error("Illegal '=' in base64 text");
- /* NO RETURN */
- }
- }
-
- /* in range, and a valid value? */
- if((c & ~0x7f) || (c = v[c]) > 63){
- if(c == 64){
- switch (n++) { /* check quantum position */
- case 2:
- state++; /* expect an equal as next char */
- break;
-
- case 3:
- n = 0L; /* restart quantum */
- break;
-
- default: /* impossible quantum position */
- gf_error("Internal base64 decoder error");
- /* NO RETURN */
- }
- }
- }
- else{
- switch (n++) { /* install based on quantum position */
- case 0: /* byte 1: high 6 bits */
- t = c << 2;
- break;
-
- case 1: /* byte 1: low 2 bits */
- GF_PUTC(f->next, (t|(c >> 4)));
- t = c << 4; /* byte 2: high 4 bits */
- break;
-
- case 2: /* byte 2: low 4 bits */
- GF_PUTC(f->next, (t|(c >> 2)));
- t = c << 6; /* byte 3: high 2 bits */
- break;
-
- case 3:
- GF_PUTC(f->next, t | c);
- n = 0L; /* reinitialize mechanism */
- break;
- }
- }
- }
-
- f->f1 = state;
- f->t = t;
- f->n = n;
- GF_END(f, f->next);
- }
- else if(flg == GF_EOD){
- GF_FLUSH(f->next);
- (*f->next->f)(f->next, GF_EOD);
- }
- else if(flg == GF_RESET){
- dprint(9, (debugfile, "-- gf_reset b64_binary\n"));
- f->n = 0L; /* quantum position */
- f->f1 = 0; /* state holder: equal seen? */
- }
-}
-
-
-
-
-/*
- * QUOTED-PRINTABLE ENCODING AND DECODING filters below.
- * encoding described in rfc1341
- */
-
-#define GF_MAXLINE 80 /* good buffer size */
-
-/*
- * default action for QUOTED-PRINTABLE to 8BIT decoder
- */
-#define GF_QP_DEFAULT(f, c) { \
- if((c) == ' '){ \
- state = WSPACE; \
- /* reset white space! */ \
- (f)->linep = (f)->line; \
- *((f)->linep)++ = ' '; \
- } \
- else if((c) == '='){ \
- state = EQUAL; \
- } \
- else \
- GF_PUTC((f)->next, (c)); \
- }
-
-
-/*
- * QUOTED-PRINTABLE to 8BIT filter
- */
-void
-gf_qp_8bit(f, flg)
- FILTER_S *f;
- int flg;
-{
- GF_INIT(f, f->next);
-
- if(flg == GF_DATA){
- register unsigned char c;
- register int state = f->f1;
-
- while(GF_GETC(f, c)){
-
- switch(state){
- case DFL : /* default case */
- default:
- GF_QP_DEFAULT(f, c);
- break;
-
- case CCR : /* non-significant space */
- state = DFL;
- if(c == '\012')
- continue; /* go on to next char */
-
- GF_QP_DEFAULT(f, c);
- break;
-
- case EQUAL :
- if(c == '\015'){ /* "=\015" is a soft EOL */
- state = CCR;
- break;
- }
-
- if(c == '='){ /* compatibility clause for old guys */
- GF_PUTC(f->next, '=');
- state = DFL;
- break;
- }
-
- if(!isxdigit((unsigned char)c)){ /* must be hex! */
- fs_give((void **)&(f->line));
- gf_error("Non-hexadecimal character in QP encoding");
- /* NO RETURN */
- }
-
- if (isdigit ((unsigned char)c))
- f->t = c - '0';
- else
- f->t = c - (isupper((unsigned char)c) ? 'A' - 10 : 'a' - 10);
-
- state = HEX;
- break;
-
- case HEX :
- state = DFL;
- if(!isxdigit((unsigned char)c)){ /* must be hex! */
- fs_give((void **)&(f->line));
- gf_error("Non-hexadecimal character in QP encoding");
- /* NO RETURN */
- }
-
- if (isdigit((unsigned char)c))
- c -= '0';
- else
- c -= (isupper((unsigned char)c) ? 'A' - 10 : 'a' - 10);
-
- GF_PUTC(f->next, c + (f->t << 4));
- break;
-
- case WSPACE :
- if(c == ' '){ /* toss it in with other spaces */
- if(f->linep - f->line < GF_MAXLINE)
- *(f->linep)++ = ' ';
- break;
- }
-
- state = DFL;
- if(c == '\015'){ /* not our white space! */
- f->linep = f->line; /* reset buffer */
- GF_PUTC(f->next, '\015');
- break;
- }
-
- /* the spaces are ours, write 'em */
- f->n = f->linep - f->line;
- while((f->n)--)
- GF_PUTC(f->next, ' ');
-
- GF_QP_DEFAULT(f, c); /* take care of 'c' in default way */
- break;
- }
- }
-
- f->f1 = state;
- GF_END(f, f->next);
- }
- else if(flg == GF_EOD){
- fs_give((void **)&(f->line));
- GF_FLUSH(f->next);
- (*f->next->f)(f->next, GF_EOD);
- }
- else if(flg == GF_RESET){
- dprint(9, (debugfile, "-- gf_reset qp_8bit\n"));
- f->f1 = DFL;
- f->linep = f->line = (char *)fs_get(GF_MAXLINE * sizeof(char));
- }
-}
-
-
-
-/*
- * USEFUL MACROS TO HELP WITH QP ENCODING
- */
-
-#define QP_MAXL 75 /* 76th place only for continuation */
-
-/*
- * Macro to test and wrap long quoted printable lines
- */
-#define GF_8BIT_WRAP(f) { \
- GF_PUTC((f)->next, '='); \
- GF_PUTC((f)->next, '\015'); \
- GF_PUTC((f)->next, '\012'); \
- }
-
-/*
- * write a quoted octet in QUOTED-PRINTABLE encoding, adding soft
- * line break if needed.
- */
-#define GF_8BIT_PUT_QUOTE(f, c) { \
- if(((f)->n += 3) > QP_MAXL){ \
- GF_8BIT_WRAP(f); \
- (f)->n = 3; /* set line count */ \
- } \
- GF_PUTC((f)->next, '='); \
- GF_PUTC((f)->next, HEX_CHAR1(c)); \
- GF_PUTC((f)->next, HEX_CHAR2(c)); \
- }
-
-/*
- * just write an ordinary octet in QUOTED-PRINTABLE, wrapping line
- * if needed.
- */
-#define GF_8BIT_PUT(f, c) { \
- if((++(f->n)) > QP_MAXL){ \
- GF_8BIT_WRAP(f); \
- f->n = 1L; \
- } \
- if(f->n == 1L && c == '.'){ \
- GF_8BIT_PUT_QUOTE(f, c); \
- f->n = 3; \
- } \
- else \
- GF_PUTC(f->next, c); \
- }
-
-
-/*
- * default action for 8bit to quoted printable encoder
- */
-#define GF_8BIT_DEFAULT(f, c) if((c) == ' '){ \
- state = WSPACE; \
- } \
- else if(c == '\015'){ \
- state = CCR; \
- } \
- else if(iscntrl(c & 0x7f) || (c == 0x7f) \
- || (c & 0x80) || (c == '=')){ \
- GF_8BIT_PUT_QUOTE(f, c); \
- } \
- else{ \
- GF_8BIT_PUT(f, c); \
- }
-
-
-/*
- * 8BIT to QUOTED-PRINTABLE filter
- */
-void
-gf_8bit_qp(f, flg)
- FILTER_S *f;
- int flg;
-{
- short dummy_dots = 0, dummy_dmap = 1;
- GF_INIT(f, f->next);
-
- if(flg == GF_DATA){
- register unsigned char c;
- register int state = f->f1;
-
- while(GF_GETC(f, c)){
-
- /* keep track of "^JFrom " */
- Find_Froms(f->t, dummy_dots, f->f2, dummy_dmap, c);
-
- switch(state){
- case DFL : /* handle ordinary case */
- GF_8BIT_DEFAULT(f, c);
- break;
-
- case CCR : /* true line break? */
- state = DFL;
- if(c == '\012'){
- GF_PUTC(f->next, '\015');
- GF_PUTC(f->next, '\012');
- f->n = 0L;
- }
- else{ /* nope, quote the CR */
- GF_8BIT_PUT_QUOTE(f, '\015');
- GF_8BIT_DEFAULT(f, c); /* and don't forget about c! */
- }
- break;
-
- case WSPACE:
- state = DFL;
- if(c == '\015' || f->t){ /* handle the space */
- GF_8BIT_PUT_QUOTE(f, ' ');
- f->t = 0; /* reset From flag */
- }
- else
- GF_8BIT_PUT(f, ' ');
-
- GF_8BIT_DEFAULT(f, c); /* handle 'c' in the default way */
- break;
- }
- }
-
- f->f1 = state;
- GF_END(f, f->next);
- }
- else if(flg == GF_EOD){
- switch(f->f1){
- case CCR :
- GF_8BIT_PUT_QUOTE(f, '\015'); /* write the last cr */
- break;
-
- case WSPACE :
- GF_8BIT_PUT_QUOTE(f, ' '); /* write the last space */
- break;
- }
-
- GF_FLUSH(f->next);
- (*f->next->f)(f->next, GF_EOD);
- }
- else if(flg == GF_RESET){
- dprint(9, (debugfile, "-- gf_reset 8bit_qp\n"));
- f->f1 = DFL; /* state from last character */
- f->f2 = 1; /* state of "^NFrom " bitmap */
- f->t = 0;
- f->n = 0L; /* number of chars in current line */
- }
-}
-
-
-
-/*
- * RICHTEXT-TO-PLAINTEXT filter
- */
-
-/*
- * option to be used by rich2plain (NOTE: if this filter is ever
- * used more than once in a pipe, all instances will have the same
- * option value)
- */
-
-
-/*----------------------------------------------------------------------
- richtext to plaintext filter
-
- Args: f --
- flg --
-
- This basically removes all richtext formatting. A cute hack is used
- to get bold and underlining to work.
- Further work could be done to handle things like centering and right
- and left flush, but then it could no longer be done in place. This
- operates on text *with* CRLF's.
-
- WARNING: does not wrap lines!
- ----*/
-void
-gf_rich2plain(f, flg)
- FILTER_S *f;
- int flg;
-{
-/* BUG: qoute incoming \255 values */
- GF_INIT(f, f->next);
-
- if(flg == GF_DATA){
- register unsigned char c;
- register int state = f->f1;
-
- while(GF_GETC(f, c)){
-
- switch(state){
- case TOKEN : /* collect a richtext token */
- if(c == '>'){ /* what should we do with it? */
- state = DFL; /* return to default next time */
- *(f->linep) = '\0'; /* cap off token */
- if(f->line[0] == 'l' && f->line[1] == 't'){
- GF_PUTC(f->next, '<'); /* literal '<' */
- }
- else if(f->line[0] == 'n' && f->line[1] == 'l'){
- GF_PUTC(f->next, '\015');/* newline! */
- GF_PUTC(f->next, '\012');
- }
- else if(!strcmp("comment", f->line)){
- (f->f2)++;
- }
- else if(!strcmp("/comment", f->line)){
- f->f2 = 0;
- }
- else if(!strcmp("/paragraph", f->line)) {
- GF_PUTC(f->next, '\r');
- GF_PUTC(f->next, '\n');
- GF_PUTC(f->next, '\r');
- GF_PUTC(f->next, '\n');
- }
- else if(!f->opt /* gf_rich_plain */){
- if(!strcmp(f->line, "bold")) {
- GF_PUTC(f->next, TAG_EMBED);
- GF_PUTC(f->next, TAG_BOLDON);
- } else if(!strcmp(f->line, "/bold")) {
- GF_PUTC(f->next, TAG_EMBED);
- GF_PUTC(f->next, TAG_BOLDOFF);
- } else if(!strcmp(f->line, "italic")) {
- GF_PUTC(f->next, TAG_EMBED);
- GF_PUTC(f->next, TAG_ULINEON);
- } else if(!strcmp(f->line, "/italic")) {
- GF_PUTC(f->next, TAG_EMBED);
- GF_PUTC(f->next, TAG_ULINEOFF);
- } else if(!strcmp(f->line, "underline")) {
- GF_PUTC(f->next, TAG_EMBED);
- GF_PUTC(f->next, TAG_ULINEON);
- } else if(!strcmp(f->line, "/underline")) {
- GF_PUTC(f->next, TAG_EMBED);
- GF_PUTC(f->next, TAG_ULINEOFF);
- }
- }
- /* else we just ignore the token! */
-
- f->linep = f->line; /* reset token buffer */
- }
- else{ /* add char to token */
- if(f->linep - f->line > 40){
- /* What? rfc1341 says 40 char tokens MAX! */
- fs_give((void **)&(f->line));
- gf_error("Richtext token over 40 characters");
- /* NO RETURN */
- }
-
- *(f->linep)++ = isupper((unsigned char)c) ? c-'A'+'a' : c;
- }
- break;
-
- case CCR :
- state = DFL; /* back to default next time */
- if(c == '\012'){ /* treat as single space? */
- GF_PUTC(f->next, ' ');
- break;
- }
- /* fall thru to process c */
-
- case DFL :
- default:
- if(c == '<')
- state = TOKEN;
- else if(c == '\015')
- state = CCR;
- else if(!f->f2) /* not in comment! */
- GF_PUTC(f->next, c);
-
- break;
- }
- }
-
- f->f1 = state;
- GF_END(f, f->next);
- }
- else if(flg == GF_EOD){
- if(f->f1 = (f->linep != f->line)){
- /* incomplete token!! */
- gf_error("Incomplete token in richtext");
- /* NO RETURN */
- }
-
- fs_give((void **)&(f->line));
- GF_FLUSH(f->next);
- (*f->next->f)(f->next, GF_EOD);
- }
- else if(flg == GF_RESET){
- dprint(9, (debugfile, "-- gf_reset rich2plain\n"));
- f->f1 = DFL; /* state */
- f->f2 = 0; /* set means we're in a comment */
- f->linep = f->line = (char *)fs_get(45 * sizeof(char));
- }
-}
-
-
-/*
- * function called from the outside to set
- * richtext filter's options
- */
-void *
-gf_rich2plain_opt(plain)
- int plain;
-{
- return((void *) plain);
-}
-
-
-
-/*
- * ENRICHED-TO-PLAIN text filter
- */
-
-#define TEF_QUELL 0x01
-#define TEF_NOFILL 0x02
-
-
-
-/*----------------------------------------------------------------------
- enriched text to plain text filter (ala rfc1523)
-
- Args: f -- state and input data
- flg --
-
- This basically removes all enriched formatting. A cute hack is used
- to get bold and underlining to work.
-
- Further work could be done to handle things like centering and right
- and left flush, but then it could no longer be done in place. This
- operates on text *with* CRLF's.
-
- WARNING: does not wrap lines!
- ----*/
-void
-gf_enriched2plain(f, flg)
- FILTER_S *f;
- int flg;
-{
-/* BUG: qoute incoming \255 values */
- GF_INIT(f, f->next);
-
- if(flg == GF_DATA){
- register unsigned char c;
- register int state = f->f1;
-
- while(GF_GETC(f, c)){
-
- switch(state){
- case TOKEN : /* collect a richtext token */
- if(c == '>'){ /* what should we do with it? */
- int off = *f->line == '/';
- char *token = f->line + (off ? 1 : 0);
- state = DFL;
- *f->linep = '\0';
- if(!strcmp("param", token)){
- if(off)
- f->f2 &= ~TEF_QUELL;
- else
- f->f2 |= TEF_QUELL;
- }
- else if(!strcmp("nofill", token)){
- if(off)
- f->f2 &= ~TEF_NOFILL;
- else
- f->f2 |= TEF_NOFILL;
- }
- else if(!f->opt /* gf_enriched_plain */){
- /* Following is a cute hack or two to get
- bold and underline on the screen.
- See Putline0n() where these codes are
- interpreted */
- if(!strcmp("bold", token)) {
- GF_PUTC(f->next, TAG_EMBED);
- GF_PUTC(f->next, off ? TAG_BOLDOFF : TAG_BOLDON);
- } else if(!strcmp("italic", token)) {
- GF_PUTC(f->next, TAG_EMBED);
- GF_PUTC(f->next, off ? TAG_ULINEOFF : TAG_ULINEON);
- } else if(!strcmp("underline", token)) {
- GF_PUTC(f->next, TAG_EMBED);
- GF_PUTC(f->next, off ? TAG_ULINEOFF : TAG_ULINEON);
- }
- }
- /* else we just ignore the token! */
-
- f->linep = f->line; /* reset token buffer */
- }
- else if(c == '<'){ /* literal '<'? */
- if(f->linep == f->line){
- GF_PUTC(f->next, '<');
- state = DFL;
- }
- else{
- fs_give((void **)&(f->line));
- gf_error("Malformed Enriched text: unexpected '<'");
- /* NO RETURN */
- }
- }
- else{ /* add char to token */
- if(f->linep - f->line > 60){ /* rfc1523 says 60 MAX! */
- fs_give((void **)&(f->line));
- gf_error("Malformed Enriched text: token too long");
- /* NO RETURN */
- }
-
- *(f->linep)++ = isupper((unsigned char)c) ? c-'A'+'a' : c;
- }
- break;
-
- case CCR :
- if(c != '\012'){ /* treat as single space? */
- state = DFL; /* lone cr? */
- f->f2 &= ~TEF_QUELL;
- GF_PUTC(f->next, '\015');
- goto df;
- }
-
- state = CLF;
- break;
-
- case CLF :
- if(c == '\015'){ /* treat as single space? */
- state = CCR; /* repeat crlf's mean real newlines */
- f->f2 |= TEF_QUELL;
- GF_PUTC(f->next, '\r');
- GF_PUTC(f->next, '\n');
- break;
- }
- else{
- state = DFL;
- if(!((f->f2) & TEF_QUELL))
- GF_PUTC(f->next, ' ');
-
- f->f2 &= ~TEF_QUELL;
- }
-
- /* fall thru to take care of 'c' */
-
- case DFL :
- default :
- df :
- if(c == '<')
- state = TOKEN;
- else if(c == '\015' && (!((f->f2) & TEF_NOFILL)))
- state = CCR;
- else if(!((f->f2) & TEF_QUELL))
- GF_PUTC(f->next, c);
-
- break;
- }
- }
-
- f->f1 = state;
- GF_END(f, f->next);
- }
- else if(flg == GF_EOD){
- if(f->f1 = (f->linep != f->line)){
- /* incomplete token!! */
- gf_error("Incomplete token in richtext");
- /* NO RETURN */
- }
-
- /* Make sure we end with a newline so everything gets flushed */
- GF_PUTC(f->next, '\015');
- GF_PUTC(f->next, '\012');
-
- fs_give((void **)&(f->line));
-
- GF_FLUSH(f->next);
- (*f->next->f)(f->next, GF_EOD);
- }
- else if(flg == GF_RESET){
- dprint(9, (debugfile, "-- gf_reset enriched2plain\n"));
- f->f1 = DFL; /* state */
- f->f2 = 0; /* set means we're in a comment */
- f->linep = f->line = (char *)fs_get(65 * sizeof(char));
- }
-}
-
-
-/*
- * function called from the outside to set
- * richtext filter's options
- */
-void *
-gf_enriched2plain_opt(plain)
- int plain;
-{
- return((void *) plain);
-}
-
-
-
-/*
- * HTML-TO-PLAIN text filter
- */
-
-
-/* OK, here's the plan:
-
- * a universal output function handles writing chars and worries
- * about wrapping.
-
- * a unversal element collector reads chars and collects params
- * and dispatches the appropriate element handler.
-
- * element handlers are stacked. The most recently dispatched gets
- * first crack at the incoming character stream. It passes bytes it's
- * done with or not interested in to the next
-
- * installs that handler as the current one collecting data...
-
- * stacked handlers take their params from the element collector and
- * accept chars or do whatever they need to do. Sort of a vertical
- * piping? recursion-like? hmmm.
-
- * at least I think this is how it'll work. tres simple, non?
-
- */
-
-
-/*
- * Some important constants
- */
-#define HTML_BUF_LEN 1024 /* max scratch buffer length */
-#define MAX_ENTITY 20 /* maximum length of an entity */
-#define MAX_ELEMENT 72 /* maximum length of an element */
-#define HTML_BADVALUE 0x0100 /* good data, but bad entity value */
-#define HTML_BADDATA 0x0200 /* bad data found looking for entity */
-#define HTML_LITERAL 0x0400 /* Literal character value */
-#define HTML_NEWLINE 0x010A /* hard newline */
-#define HTML_DOBOLD 0x0400 /* Start Bold display */
-#define HTML_ID_GET 0 /* indent func: return current val */
-#define HTML_ID_SET 1 /* indent func: set to absolute val */
-#define HTML_ID_INC 2 /* indent func: increment by val */
-#define HTML_HX_CENTER 0x0001
-#define HTML_HX_ULINE 0x0002
-
-
-/*
- * Types used to manage HTML parsing
- */
-typedef int (*html_f) PROTO(());
-
-/*
- * Handler data, state information including function that uses it
- */
-typedef struct handler_s {
- FILTER_S *html_data;
- struct handler_s *below;
- html_f f;
- long x, y, z;
- unsigned char *s;
-} HANDLER_S;
-
-
-/*
- * to help manage line wrapping.
- */
-typedef struct _wrap_line {
- char *buf; /* buf to collect wrapped text */
- int used, /* number of chars in buf */
- width, /* text's width as displayed */
- len; /* length of allocated buf */
-} WRAPLINE_S;
-
-
-/*
- * to help manage centered text
- */
-typedef struct _center_s {
- WRAPLINE_S line; /* buf to assembled centered text */
- WRAPLINE_S word; /* word being to append to Line */
- int anchor;
- short embedded;
- short space;
-} CENTER_S;
-
-
-/*
- * Collector data and state information
- */
-typedef struct collector_s {
- char buf[HTML_BUF_LEN]; /* buffer to collect data */
- int len; /* length of that buffer */
- unsigned end_tag:1; /* collecting a closing tag */
- unsigned hit_equal:1; /* collecting right half of attrib */
- unsigned mkup_decl:1; /* markup declaration */
- unsigned start_comment:1; /* markup declaration comment */
- unsigned end_comment:1; /* legit comment format */
- unsigned hyphen:1; /* markup hyphen read */
- unsigned badform:1; /* malformed markup element */
- unsigned overrun:1; /* Overran buf above */
- char quoted; /* quoted element param value */
- char *element; /* element's collected name */
- PARAMETER *attribs; /* element's collected attributes */
- PARAMETER *cur_attrib; /* attribute now being collected */
-} CLCTR_S;
-
-
-/*
- * State information for all element handlers
- */
-typedef struct html_data {
- HANDLER_S *h_stack; /* handler list */
- CLCTR_S *el_data; /* element collector data */
- CENTER_S *centered; /* struct to manage centered text */
- int (*token) PROTO((FILTER_S *, int));
- char quoted; /* quoted, by either ' or ", text */
- short indent_level; /* levels of indention */
- int in_anchor; /* text now being written to anchor */
- int blanks; /* Consecutive blank line count */
- int wrapcol; /* column to wrap lines on */
- int *prefix; /* buffer containing Anchor prefix */
- int prefix_used;
- COLOR_PAIR *color;
- unsigned wrapstate:1; /* whether or not to wrap output */
- unsigned li_pending:1; /* next token expected */
- unsigned de_pending:1; /* or next token expected */
- unsigned bold_on:1; /* currently bolding text */
- unsigned uline_on:1; /* currently underlining text */
- unsigned center:1; /* center output text */
- unsigned bitbucket:1; /* Ignore input */
- unsigned head:1; /* In doc's HEAD */
- unsigned alt_entity:1; /* use alternative entity values */
-} HTML_DATA_S;
-
-
-/*
- * HTML filter options
- */
-typedef struct _html_opts {
- char *base; /* Base URL for this html file */
- int columns; /* Display columns */
- unsigned strip:1; /* Hilite TAGs allowed */
- unsigned handles:1; /* Anchors as handles requested? */
- unsigned handles_loc:1; /* Local handles requested? */
-} HTML_OPT_S;
-
-
-/*
- * Some macros to make life a little easier
- */
-#define WRAP_COLS(X) ((X)->opt ? ((HTML_OPT_S *)(X)->opt)->columns : 80)
-#define HTML_BASE(X) ((X)->opt ? ((HTML_OPT_S *)(X)->opt)->base : NULL)
-#define STRIP(X) ((X)->opt && ((HTML_OPT_S *)(X)->opt)->strip)
-#define HANDLES(X) ((X)->opt && ((HTML_OPT_S *)(X)->opt)->handles)
-#define HANDLES_LOC(X) ((X)->opt && ((HTML_OPT_S *)(X)->opt)->handles_loc)
-#define MAKE_LITERAL(C) (HTML_LITERAL | ((C) & 0xff))
-#define IS_LITERAL(C) (HTML_LITERAL & (C))
-#define HD(X) ((HTML_DATA_S *)(X)->data)
-#define ED(X) (HD(X)->el_data)
-#define HTML_ISSPACE(C) (IS_LITERAL(C) == 0 && isspace((unsigned char) (C)))
-#define NEW_CLCTR(X) { \
- ED(X) = (CLCTR_S *)fs_get(sizeof(CLCTR_S)); \
- memset(ED(X), 0, sizeof(CLCTR_S)); \
- HD(X)->token = html_element_collector; \
- }
-
-#define FREE_CLCTR(X) { \
- if(ED(X)->attribs){ \
- PARAMETER *p; \
- while(p = ED(X)->attribs){ \
- ED(X)->attribs = ED(X)->attribs->next; \
- if(p->attribute) \
- fs_give((void **)&p->attribute); \
- if(p->value) \
- fs_give((void **)&p->value); \
- fs_give((void **)&p); \
- } \
- } \
- if(ED(X)->element) \
- fs_give((void **) &ED(X)->element); \
- fs_give((void **) &ED(X)); \
- HD(X)->token = NULL; \
- }
-#define HANDLERS(X) (HD(X)->h_stack)
-#define BOLD_BIT(X) (HD(X)->bold_on)
-#define ULINE_BIT(X) (HD(X)->uline_on)
-#define CENTER_BIT(X) (HD(X)->center)
-#define HTML_FLUSH(X) { \
- html_write(X, (X)->line, (X)->linep - (X)->line); \
- (X)->linep = (X)->line; \
- (X)->f2 = 0L; \
- }
-#define HTML_BOLD(X, S) if(! STRIP(X)){ \
- if(S){ \
- html_output((X), TAG_EMBED); \
- html_output((X), TAG_BOLDON); \
- } \
- else if(!(S)){ \
- html_output((X), TAG_EMBED); \
- html_output((X), TAG_BOLDOFF); \
- } \
- }
-#define HTML_ULINE(X, S) \
- if(! STRIP(X)){ \
- if(S){ \
- html_output((X), TAG_EMBED); \
- html_output((X), TAG_ULINEON); \
- } \
- else if(!(S)){ \
- html_output((X), TAG_EMBED); \
- html_output((X), TAG_ULINEOFF); \
- } \
- }
-#define WRAPPED_LEN(X) ((HD(f)->centered) \
- ? (HD(f)->centered->line.width \
- + HD(f)->centered->word.width \
- + ((HD(f)->centered->line.width \
- && HD(f)->centered->word.width) \
- ? 1 : 0)) \
- : 0)
-#define HTML_DUMP_LIT(F, S, L) { \
- int i, c; \
- for(i = 0; i < (L); i++){ \
- c = isspace((S)[i]) \
- ? (S)[i] \
- : MAKE_LITERAL((S)[i]); \
- HTML_TEXT(F, c); \
- } \
- }
-#define HTML_PROC(F, C) { \
- if(HD(F)->token){ \
- int i; \
- if(i = (*(HD(F)->token))(F, C)){ \
- if(i < 0){ \
- HTML_DUMP_LIT(F, "<", 1); \
- if(HD(F)->el_data->element){ \
- HTML_DUMP_LIT(F, \
- HD(F)->el_data->element, \
- strlen(HD(F)->el_data->element));\
- } \
- if(HD(F)->el_data->len){ \
- HTML_DUMP_LIT(F, \
- HD(F)->el_data->buf, \
- HD(F)->el_data->len); \
- } \
- HTML_TEXT(F, C); \
- } \
- FREE_CLCTR(F); \
- } \
- } \
- else if((C) == '<'){ \
- NEW_CLCTR(F); \
- } \
- else \
- HTML_TEXT(F, C); \
- }
-#define HTML_TEXT(F, C) switch((F)->f1){ \
- case WSPACE : \
- if(HTML_ISSPACE(C)) /* ignore repeated WS */ \
- break; \
- HTML_TEXT_OUT(F, ' '); \
- (F)->f1 = DFL;/* stop sending chars here */ \
- /* fall thru to process 'c' */ \
- case DFL: \
- if(HD(F)->bitbucket) \
- (F)->f1 = DFL; /* no op */ \
- else if(HTML_ISSPACE(C) && HD(F)->wrapstate) \
- (F)->f1 = WSPACE;/* coalesce white space */ \
- else HTML_TEXT_OUT(F, C); \
- break; \
- }
-#define HTML_TEXT_OUT(F, C) if(HANDLERS(F)) /* let handlers see C */ \
- (*HANDLERS(F)->f)(HANDLERS(F),(C),GF_DATA); \
- else \
- html_output(F, C);
-#ifdef DEBUG
-#define HTML_DEBUG_EL(S, D) { \
- dprint(2, (debugfile, "-- html %s: %s\n", \
- S, (D)->element \
- ? (D)->element : "NULL")); \
- if(debug > 5){ \
- PARAMETER *p; \
- for(p = (D)->attribs; \
- p && p->attribute; \
- p = p->next) \
- dprint(6, (debugfile, \
- " PARM: %s%s%s\n", \
- p->attribute \
- ? p->attribute : "NULL",\
- p->value ? "=" : "", \
- p->value ? p->value : ""));\
- } \
- }
-#else
-#define HTML_DEBUG_EL(S, D)
-#endif
-
-
-/*
- * Protos for Tag handlers
- */
-int html_head PROTO((HANDLER_S *, int, int));
-int html_base PROTO((HANDLER_S *, int, int));
-int html_title PROTO((HANDLER_S *, int, int));
-int html_a PROTO((HANDLER_S *, int, int));
-int html_br PROTO((HANDLER_S *, int, int));
-int html_hr PROTO((HANDLER_S *, int, int));
-int html_p PROTO((HANDLER_S *, int, int));
-int html_tr PROTO((HANDLER_S *, int, int));
-int html_td PROTO((HANDLER_S *, int, int));
-int html_b PROTO((HANDLER_S *, int, int));
-int html_i PROTO((HANDLER_S *, int, int));
-int html_img PROTO((HANDLER_S *, int, int));
-int html_form PROTO((HANDLER_S *, int, int));
-int html_ul PROTO((HANDLER_S *, int, int));
-int html_ol PROTO((HANDLER_S *, int, int));
-int html_menu PROTO((HANDLER_S *, int, int));
-int html_dir PROTO((HANDLER_S *, int, int));
-int html_li PROTO((HANDLER_S *, int, int));
-int html_h1 PROTO((HANDLER_S *, int, int));
-int html_h2 PROTO((HANDLER_S *, int, int));
-int html_h3 PROTO((HANDLER_S *, int, int));
-int html_h4 PROTO((HANDLER_S *, int, int));
-int html_h5 PROTO((HANDLER_S *, int, int));
-int html_h6 PROTO((HANDLER_S *, int, int));
-int html_blockquote PROTO((HANDLER_S *, int, int));
-int html_address PROTO((HANDLER_S *, int, int));
-int html_pre PROTO((HANDLER_S *, int, int));
-int html_center PROTO((HANDLER_S *, int, int));
-int html_div PROTO((HANDLER_S *, int, int));
-int html_dl PROTO((HANDLER_S *, int, int));
-int html_dt PROTO((HANDLER_S *, int, int));
-int html_dd PROTO((HANDLER_S *, int, int));
-
-/*
- * Proto's for support routines
- */
-void html_pop PROTO((FILTER_S *, html_f));
-void html_push PROTO((FILTER_S *, html_f));
-int html_element_collector PROTO((FILTER_S *, int));
-int html_element_flush PROTO((CLCTR_S *));
-void html_element_comment PROTO((FILTER_S *, char *));
-void html_element_output PROTO((FILTER_S *, int));
-int html_entity_collector PROTO((FILTER_S *, int, char **));
-void html_a_prefix PROTO((FILTER_S *));
-void html_a_finish PROTO((HANDLER_S *));
-void html_a_output_prefix PROTO((FILTER_S *, int));
-void html_a_relative PROTO((char *, char *, HANDLE_S *));
-int html_indent PROTO((FILTER_S *, int, int));
-void html_blank PROTO((FILTER_S *, int));
-void html_newline PROTO((FILTER_S *));
-void html_output PROTO((FILTER_S *, int));
-void html_output_flush PROTO((FILTER_S *));
-void html_output_centered PROTO((FILTER_S *, int));
-void html_centered_handle PROTO((int *, char *, int));
-void html_centered_putc PROTO((WRAPLINE_S *, int));
-void html_centered_flush PROTO((FILTER_S *));
-void html_centered_flush_line PROTO((FILTER_S *));
-void html_write_anchor PROTO((FILTER_S *, int));
-void html_write_newline PROTO((FILTER_S *));
-void html_write_indent PROTO((FILTER_S *, int));
-void html_write PROTO((FILTER_S *, char *, int));
-void html_putc PROTO((FILTER_S *, int));
-
-
-/*
- * Named entity table -- most from HTML 2.0 (rfc1866) plus some from
- * W3C doc "Additional named entities for HTML"
- */
-static struct html_entities {
- char *name; /* entity name */
- unsigned char value; /* entity value */
- char *plain; /* plain text representation */
-} entity_tab[] = {
- {"quot", 042}, /* Double quote sign */
- {"amp", 046}, /* Ampersand */
- {"bull", 052}, /* Bullet */
- {"ndash", 055}, /* Dash */
- {"mdash", 055}, /* Dash */
- {"lt", 074}, /* Less than sign */
- {"gt", 076}, /* Greater than sign */
- {"nbsp", 0240, " "}, /* no-break space */
- {"iexcl", 0241}, /* inverted exclamation mark */
- {"cent", 0242}, /* cent sign */
- {"pound", 0243}, /* pound sterling sign */
- {"curren", 0244, "CUR"}, /* general currency sign */
- {"yen", 0245}, /* yen sign */
- {"brvbar", 0246, "|"}, /* broken (vertical) bar */
- {"sect", 0247}, /* section sign */
- {"uml", 0250, "\""}, /* umlaut (dieresis) */
- {"copy", 0251, "(C)"}, /* copyright sign */
- {"ordf", 0252, "a"}, /* ordinal indicator, feminine */
- {"laquo", 0253, "<<"}, /* angle quotation mark, left */
- {"not", 0254, "NOT"}, /* not sign */
- {"shy", 0255, "-"}, /* soft hyphen */
- {"reg", 0256, "(R)"}, /* registered sign */
- {"macr", 0257}, /* macron */
- {"deg", 0260, "DEG"}, /* degree sign */
- {"plusmn", 0261, "+/-"}, /* plus-or-minus sign */
- {"sup2", 0262}, /* superscript two */
- {"sup3", 0263}, /* superscript three */
- {"acute", 0264, "'"}, /* acute accent */
- {"micro", 0265}, /* micro sign */
- {"para", 0266}, /* pilcrow (paragraph sign) */
- {"middot", 0267}, /* middle dot */
- {"cedil", 0270}, /* cedilla */
- {"sup1", 0271}, /* superscript one */
- {"ordm", 0272, "o"}, /* ordinal indicator, masculine */
- {"raquo", 0273, ">>"}, /* angle quotation mark, right */
- {"frac14", 0274, " 1/4"}, /* fraction one-quarter */
- {"frac12", 0275, " 1/2"}, /* fraction one-half */
- {"frac34", 0276, " 3/4"}, /* fraction three-quarters */
- {"iquest", 0277}, /* inverted question mark */
- {"Agrave", 0300, "A"}, /* capital A, grave accent */
- {"Aacute", 0301, "A"}, /* capital A, acute accent */
- {"Acirc", 0302, "A"}, /* capital A, circumflex accent */
- {"Atilde", 0303, "A"}, /* capital A, tilde */
- {"Auml", 0304, "AE"}, /* capital A, dieresis or umlaut mark */
- {"Aring", 0305, "A"}, /* capital A, ring */
- {"AElig", 0306, "AE"}, /* capital AE diphthong (ligature) */
- {"Ccedil", 0307, "C"}, /* capital C, cedilla */
- {"Egrave", 0310, "E"}, /* capital E, grave accent */
- {"Eacute", 0311, "E"}, /* capital E, acute accent */
- {"Ecirc", 0312, "E"}, /* capital E, circumflex accent */
- {"Euml", 0313, "E"}, /* capital E, dieresis or umlaut mark */
- {"Igrave", 0314, "I"}, /* capital I, grave accent */
- {"Iacute", 0315, "I"}, /* capital I, acute accent */
- {"Icirc", 0316, "I"}, /* capital I, circumflex accent */
- {"Iuml", 0317, "I"}, /* capital I, dieresis or umlaut mark */
- {"ETH", 0320, "DH"}, /* capital Eth, Icelandic */
- {"Ntilde", 0321, "N"}, /* capital N, tilde */
- {"Ograve", 0322, "O"}, /* capital O, grave accent */
- {"Oacute", 0323, "O"}, /* capital O, acute accent */
- {"Ocirc", 0324, "O"}, /* capital O, circumflex accent */
- {"Otilde", 0325, "O"}, /* capital O, tilde */
- {"Ouml", 0326, "OE"}, /* capital O, dieresis or umlaut mark */
- {"times", 0327, "x"}, /* multiply sign */
- {"Oslash", 0330, "O"}, /* capital O, slash */
- {"Ugrave", 0331, "U"}, /* capital U, grave accent */
- {"Uacute", 0332, "U"}, /* capital U, acute accent */
- {"Ucirc", 0333, "U"}, /* capital U, circumflex accent */
- {"Uuml", 0334, "UE"}, /* capital U, dieresis or umlaut mark */
- {"Yacute", 0335, "Y"}, /* capital Y, acute accent */
- {"THORN", 0336, "P"}, /* capital THORN, Icelandic */
- {"szlig", 0337, "ss"}, /* small sharp s, German (sz ligature) */
- {"agrave", 0340, "a"}, /* small a, grave accent */
- {"aacute", 0341, "a"}, /* small a, acute accent */
- {"acirc", 0342, "a"}, /* small a, circumflex accent */
- {"atilde", 0343, "a"}, /* small a, tilde */
- {"auml", 0344, "ae"}, /* small a, dieresis or umlaut mark */
- {"aring", 0345, "a"}, /* small a, ring */
- {"aelig", 0346, "ae"}, /* small ae diphthong (ligature) */
- {"ccedil", 0347, "c"}, /* small c, cedilla */
- {"egrave", 0350, "e"}, /* small e, grave accent */
- {"eacute", 0351, "e"}, /* small e, acute accent */
- {"ecirc", 0352, "e"}, /* small e, circumflex accent */
- {"euml", 0353, "e"}, /* small e, dieresis or umlaut mark */
- {"igrave", 0354, "i"}, /* small i, grave accent */
- {"iacute", 0355, "i"}, /* small i, acute accent */
- {"icirc", 0356, "i"}, /* small i, circumflex accent */
- {"iuml", 0357, "i"}, /* small i, dieresis or umlaut mark */
- {"eth", 0360, "dh"}, /* small eth, Icelandic */
- {"ntilde", 0361, "n"}, /* small n, tilde */
- {"ograve", 0362, "o"}, /* small o, grave accent */
- {"oacute", 0363, "o"}, /* small o, acute accent */
- {"ocirc", 0364, "o"}, /* small o, circumflex accent */
- {"otilde", 0365, "o"}, /* small o, tilde */
- {"ouml", 0366, "oe"}, /* small o, dieresis or umlaut mark */
- {"divide", 0367, "/"}, /* divide sign */
- {"oslash", 0370, "o"}, /* small o, slash */
- {"ugrave", 0371, "u"}, /* small u, grave accent */
- {"uacute", 0372, "u"}, /* small u, acute accent */
- {"ucirc", 0373, "u"}, /* small u, circumflex accent */
- {"uuml", 0374, "ue"}, /* small u, dieresis or umlaut mark */
- {"yacute", 0375, "y"}, /* small y, acute accent */
- {"thorn", 0376, "p"}, /* small thorn, Icelandic */
- {"yuml", 0377, "y"}, /* small y, dieresis or umlaut mark */
- {NULL, 0}
-};
-
-
-/*
- * Table of supported elements and corresponding handlers
- */
-static struct element_table {
- char *element;
- int (*handler) PROTO(());
-} element_table[] = {
- {"HTML", NULL}, /* HTML ignore if seen? */
- {"HEAD", html_head}, /* slurp until ? */
- {"TITLE", html_title}, /* Document Title */
- {"BASE", html_base}, /* HREF base */
- {"BODY", NULL}, /* (NO OP) */
- {"A", html_a}, /* Anchor */
- {"IMG", html_img}, /* Image */
- {"HR", html_hr}, /* Horizontal Rule */
- {"BR", html_br}, /* Line Break */
- {"P", html_p}, /* Paragraph */
- {"OL", html_ol}, /* Ordered List */
- {"UL", html_ul}, /* Unordered List */
- {"MENU", html_menu}, /* Menu List */
- {"DIR", html_dir}, /* Directory List */
- {"LI", html_li}, /* ... List Item */
- {"DL", html_dl}, /* Definition List */
- {"DT", html_dt}, /* ... Def. Term */
- {"DD", html_dd}, /* ... Def. Definition */
- {"I", html_i}, /* Italic Text */
- {"EM", html_i}, /* Typographic Emphasis */
- {"STRONG", html_i}, /* STRONG Typo Emphasis */
- {"VAR", html_i}, /* Variable Name */
- {"B", html_b}, /* Bold Text */
- {"BLOCKQUOTE", html_blockquote}, /* Blockquote */
- {"ADDRESS", html_address}, /* Address */
- {"CENTER", html_center}, /* Centered Text v3.2 */
- {"DIV", html_div}, /* Document Division 3.2 */
- {"H1", html_h1}, /* Headings... */
- {"H2", html_h2},
- {"H3", html_h3},
- {"H4", html_h4},
- {"H5", html_h5},
- {"H6", html_h6},
- {"PRE", html_pre}, /* Preformatted Text */
- {"KBD", NULL}, /* Keyboard Input (NO OP) */
- {"TT", NULL}, /* Typetype (NO OP) */
- {"SAMP", NULL}, /* Sample Text (NO OP) */
-
-/*----- Handlers below are NOT DONE OR CHECKED OUT YET -----*/
-
- {"CITE", NULL}, /* Citation */
- {"CODE", NULL}, /* Code Text */
-
-/*----- Handlers below UNIMPLEMENTED (and won't until later) -----*/
-
- {"FORM", html_form}, /* form within a document */
- {"INPUT", NULL}, /* One input field, options */
- {"OPTION", NULL}, /* One option within Select */
- {"SELECT", NULL}, /* Selection from a set */
- {"TEXTAREA", NULL}, /* A multi-line input field */
-
-/*----- Handlers below provide limited support for RFC 1942 Tables -----*/
-
- {"CAPTION", html_center}, /* Table Caption */
- {"TR", html_tr}, /* Table Table Row */
- {"TD", html_td}, /* Table Table Data */
-
- {NULL, NULL}
-};
-
-
-
-/*
- * Initialize the given handler, and add it to the stack if it
- * requests it.
- */
-void
-html_push(fd, hf)
- FILTER_S *fd;
- html_f hf;
-{
- HANDLER_S *new;
-
- new = (HANDLER_S *)fs_get(sizeof(HANDLER_S));
- memset(new, 0, sizeof(HANDLER_S));
- new->html_data = fd;
- new->f = hf;
- if((*hf)(new, 0, GF_RESET)){ /* stack the handler? */
- new->below = HANDLERS(fd);
- HANDLERS(fd) = new; /* push */
- }
- else
- fs_give((void **) &new);
-}
-
-
-/*
- * Remove the most recently installed the given handler
- * after letting it accept its demise.
- */
-void
-html_pop(fd, hf)
- FILTER_S *fd;
- html_f hf;
-{
- HANDLER_S *tp;
-
- for(tp = HANDLERS(fd); tp && hf != tp->f; tp = tp->below)
- ;
-
- if(tp){
- (*tp->f)(tp, 0, GF_EOD); /* may adjust handler list */
- if(tp != HANDLERS(fd)){
- HANDLER_S *p;
-
- for(p = HANDLERS(fd); p->below != tp; p = p->below)
- ;
-
- if(p)
- p->below = tp->below; /* remove from middle of stack */
- /* BUG: else programming botch and we should die */
- }
- else
- HANDLERS(fd) = tp->below; /* pop */
-
- fs_give((void **)&tp);
- }
- else if(hf == html_p || hf == html_li || hf == html_dt || hf == html_dd){
- /*
- * Possible "special case" tag handling here.
- * It's for such tags as Paragraph (`'), List Item
- * (`'), Definition Term (`'), and Definition Description
- * (`') elements, which may be omitted...
- */
- HANDLER_S hd;
-
- memset(&hd, 0, sizeof(HANDLER_S));
- hd.html_data = fd;
- hd.f = hf;
-
- (*hf)(&hd, 0, GF_EOD);
- }
- /* BUG: else, we should bitch */
-}
-
-
-/*
- * Deal with data passed a hander in its GF_DATA state
- */
-html_handoff(hd, ch)
- HANDLER_S *hd;
- int ch;
-{
- if(hd->below)
- (*hd->below->f)(hd->below, ch, GF_DATA);
- else
- html_output(hd->html_data, ch);
-}
-
-
-/*
- * HTML
element handler
- */
-int
-html_br(hd, ch, cmd)
- HANDLER_S *hd;
- int ch, cmd;
-{
- if(cmd == GF_RESET)
- html_output(hd->html_data, HTML_NEWLINE);
-
- return(0); /* don't get linked */
-}
-
-
-/*
- * HTML
(Horizontal Rule) element handler
- */
-int
-html_hr(hd, ch, cmd)
- HANDLER_S *hd;
- int ch, cmd;
-{
- if(cmd == GF_RESET){
- int i, old_wrap, width, align;
- PARAMETER *p;
-
- width = WRAP_COLS(hd->html_data);
- align = 0;
- for(p = HD(hd->html_data)->el_data->attribs;
- p && p->attribute;
- p = p->next)
- if(p->value){
- if(!strucmp(p->attribute, "ALIGN")){
- if(!strucmp(p->value, "LEFT"))
- align = 1;
- else if(!strucmp(p->value, "RIGHT"))
- align = 2;
- }
- else if(!strucmp(p->attribute, "WIDTH")){
- char *cp;
-
- width = 0;
- for(cp = p->value; *cp; cp++)
- if(*cp == '%'){
- width = (WRAP_COLS(hd->html_data)*min(100,width))/100;
- break;
- }
- else if(isdigit((unsigned char) *cp))
- width = (width * 10) + (*cp - '0');
-
- width = min(width, WRAP_COLS(hd->html_data));
- }
- }
-
- html_blank(hd->html_data, 1); /* at least one blank line */
-
- old_wrap = HD(hd->html_data)->wrapstate;
- HD(hd->html_data)->wrapstate = 0;
- if((i = max(0, WRAP_COLS(hd->html_data) - width))
- && ((align == 0) ? i /= 2 : (align == 2)))
- for(; i > 0; i--)
- html_output(hd->html_data, ' ');
-
- for(i = 0; i < width; i++)
- html_output(hd->html_data, '_');
-
- html_blank(hd->html_data, 1);
- HD(hd->html_data)->wrapstate = old_wrap;
- }
-
- return(0); /* don't get linked */
-}
-
-
-/*
- * HTML (paragraph) element handler
- */
-int
-html_p(hd, ch, cmd)
- HANDLER_S *hd;
- int ch, cmd;
-{
- if(cmd == GF_RESET){
- /* Make sure there's at least 1 blank line */
- html_blank(hd->html_data, 1);
-
- /* adjust indent level if needed */
- if(HD(hd->html_data)->li_pending){
- html_indent(hd->html_data, 4, HTML_ID_INC);
- HD(hd->html_data)->li_pending = 0;
- }
- }
- else if(cmd == GF_EOD)
- /* Make sure there's at least 1 blank line */
- html_blank(hd->html_data, 1);
-
- return(0); /* don't get linked */
-}
-
-
-/*
- * HTML Table
(paragraph) table row
- */
-int
-html_tr(hd, ch, cmd)
- HANDLER_S *hd;
- int ch, cmd;
-{
- if(cmd == GF_RESET || cmd == GF_EOD)
- /* Make sure there's at least 1 blank line */
- html_blank(hd->html_data, 0);
-
- return(0); /* don't get linked */
-}
-
-
-/*
- * HTML Table (paragraph) table data
- */
-int
-html_td(hd, ch, cmd)
- HANDLER_S *hd;
- int ch, cmd;
-{
- if(cmd == GF_RESET){
- PARAMETER *p;
-
- for(p = HD(hd->html_data)->el_data->attribs;
- p && p->attribute;
- p = p->next)
- if(!strucmp(p->attribute, "nowrap")
- && (hd->html_data->f2 || hd->html_data->n)){
- HTML_DUMP_LIT(hd->html_data, " | ", 3);
- break;
- }
- }
-
- return(0); /* don't get linked */
-}
-
-
-/*
- * HTML (italic text) element handler
- */
-int
-html_i(hd, ch, cmd)
- HANDLER_S *hd;
- int ch, cmd;
-{
- if(cmd == GF_DATA){
- /* include LITERAL in spaceness test! */
- if(hd->x && !isspace((unsigned char) (ch & 0xff))){
- HTML_ULINE(hd->html_data, 1);
- hd->x = 0;
- }
-
- html_handoff(hd, ch);
- }
- else if(cmd == GF_RESET){
- hd->x = 1;
- }
- else if(cmd == GF_EOD){
- if(!hd->x)
- HTML_ULINE(hd->html_data, 0);
- }
-
- return(1); /* get linked */
-}
-
-
-/*
- * HTML (Bold text) element handler
- */
-int
-html_b(hd, ch, cmd)
- HANDLER_S *hd;
- int ch, cmd;
-{
- if(cmd == GF_DATA){
- /* include LITERAL in spaceness test! */
- if(hd->x && !isspace((unsigned char) (ch & 0xff))){
- HTML_ULINE(hd->html_data, 1);
- hd->x = 0;
- }
-
- html_handoff(hd, ch);
- }
- else if(cmd == GF_RESET){
- hd->x = 1;
- }
- else if(cmd == GF_EOD){
- if(!hd->x)
- HTML_ULINE(hd->html_data, 0);
- }
-
- return(1); /* get linked */
-}
-
-
-/*
- * HTML element handler
- */
-int
-html_img(hd, ch, cmd)
- HANDLER_S *hd;
- int ch, cmd;
-{
- if(cmd == GF_RESET){
- PARAMETER *p;
- char *s = NULL;
-
- for(p = HD(hd->html_data)->el_data->attribs;
- p && p->attribute;
- p = p->next)
- if(!strucmp(p->attribute, "alt")){
- if(p->value && p->value[0]){
- HTML_DUMP_LIT(hd->html_data, p->value, strlen(p->value));
- HTML_TEXT(hd->html_data, ' ');
- }
-
- return(0);
- }
-
- for(p = HD(hd->html_data)->el_data->attribs;
- p && p->attribute;
- p = p->next)
- if(!strucmp(p->attribute, "src") && p->value)
- if((s = strrindex(p->value, '/')) && *++s != '\0'){
- HTML_TEXT(hd->html_data, '[');
- HTML_DUMP_LIT(hd->html_data, s, strlen(s));
- HTML_TEXT(hd->html_data, ']');
- HTML_TEXT(hd->html_data, ' ');
- return(0);
- }
-
- HTML_DUMP_LIT(hd->html_data, "[IMAGE] ", 7);
- }
-
- return(0); /* don't get linked */
-}
-
-
-/*
- * HTML |