/* * markup.c: * Process general markup in htmlise. * * Copyright (c) 2003 Chris Lightfoot. All rights reserved. * Email: chris@ex-parrot.com; WWW: http://www.ex-parrot.com/~chris/ * */ static const char rcsid[] = "$Id: markup.c,v 1.3 2003/02/07 00:30:29 chris Exp $"; #include #include #include #include "htmlise.h" #if 0 void dump_list(struct paragraph *p, size_t offs) { #define ind(i) do { size_t jjj; for (jjj = 0; jjj < offs; ++jjj) fprintf(stderr, "%c", !(jjj % 8) ? '|' : ' '); } while (0) for (; p; p = p->next) { if (p->nlines > 0) { ind(offs); fprintf(stderr, "[%u] `%s...'\n", p->indent, p->lines[0]); } if (p->container) { ind(offs); fprintf(stderr, "[%u] <%s>...\n", p->indent, p->container, p->container); dump_list(p->contents, offs + 8); } ind(i); fprintf(stderr, "\n"); if (p->next) assert(p->next->prev == p); } } #endif /* paragraph_is_start_of_list PARAGRAPH * Is PARAGRAPH the start of a bulleted or numbered list? */ static int paragraph_is_start_of_list(const struct paragraph *P) { return (P->type == bullet || P->type == number) && (!P->prev || P->indent > P->prev->indent); } /* paragraph_is_part_of_list PARAGRAPH FIRST * Is PARAGRAPH part of the list with the given FIRST paragraph? */ static int paragraph_is_part_of_list(const struct paragraph *P, const struct paragraph *first) { return (P->indent > first->indent) || ((P->type == none || P->type == first->type) && P->indent == first->indent); } /* paragraph_is_part_of_block PARAGRAPH FIRST * Is PARAGRAPH part of the block with the given FIRST paragraph? */ static int paragraph_is_part_of_block(const struct paragraph *P, const struct paragraph *first) { return (P->indent > first->indent) || (P->type == none && P->indent == first->indent); } /* paragraph_is_start_of_block_quote PARAGRAPH * Is PARAGRAPH the start of an indented block quote? */ static int paragraph_is_start_of_block_quote(const struct paragraph *P) { return P->type == none && ((!P->prev && P->indent > 0) || (P->prev && P->indent > P->prev->indent)); } /* create_container TYPE FIRST LAST UNINDENT * Replace the paragraphs from FIRST to LAST inclusive with a container * of the given TYPE containing those paragraphs. FIRST is not modified. If * UNINDENT is true, the indent of the contained paragraphs is reduced by * the indent of FIRST. */ struct paragraph *create_container(char *type, struct paragraph *first, struct paragraph *last, const int unindent) { struct paragraph *newfirst, *P; size_t origindent; origindent = first->indent; /* Make a copy of first. */ alloc_struct(paragraph, newfirst); *newfirst = *first; /* Make the original first into a container object. */ first->nlines = 0; first->lines = NULL; first->container = type; first->contents = newfirst; /* Skip over items in the list up to last. */ first->next = last->next; if (first->next) first->next->prev = first; if (last == first) newfirst->next = newfirst->prev = NULL; else { newfirst->next->prev = newfirst; last->next = newfirst->prev = NULL; } if (unindent) /* Indent the contents left. */ for (P = first->contents; P; P = P->next) { assert(P->indent >= origindent); P->indent -= origindent; if (P->prev) assert(P->prev->next == P); if (P->next) assert(P->next->prev == P); } assert(!first->next || first->next->prev == first); return first; } /* process_lists PARAGRAPHS * Go through PARAGRAPHS, identifying extents which form part of a list and * inserting suitable container objects. */ void process_lists(struct paragraph *paras) { struct paragraph *P; for (P = paras; P; ) { if (P->contents) process_lists(P->contents); else if (paragraph_is_start_of_list(P)) { struct paragraph *first, *last, *listcontainer; first = P; /* Find the last paragraph which is part of this list. */ for (last = first; last->next && paragraph_is_part_of_list(last->next, first); last = last->next); /* Make the list container. */ listcontainer = create_container(first->type == bullet ? "ul" : "ol", first, last, 1); /* Now we need to go through the individual contained paragraphs * and break them up into
  • elements. While we do this, also * remove their leaders and process the contents of each container * recursively, to deal with lists-in-lists and so forth. */ for (first = listcontainer->contents; first; ) { struct paragraph *Q, *icontainer; for (last = first; last->next && paragraph_is_part_of_block(last->next, first); last = last->next); Q = last->next; icontainer = create_container("li", first, last, 1); icontainer->contents->type = none; /* get rid of bullet/number on leading para */ process_lists(icontainer->contents); first = Q; } } P = P->next; } } /* process_block_quotes PARAGRAPHS * Go through PARAGRAPHS, identifying extents which form part of a block quote * and inserting suitable container objects. We don't create block quotes * which contain only tables. */ void process_block_quotes(struct paragraph *paras) { struct paragraph *P; for (P = paras; P; ) { if (P->contents) process_block_quotes(P->contents); else if (paragraph_is_start_of_block_quote(P)) { struct paragraph *first, *last; int all_tables = 0; first = P; all_tables = first->container && strcmp(first->container, "table") == 0; for (last = first; last->next && paragraph_is_part_of_block(last->next, first); last = last->next) if (all_tables && (!last->container || strcmp(last->container, "table"))) all_tables = 0; if (!all_tables) { create_container("blockquote", first, last, 1); process_block_quotes(P->contents); } } P = P->next; } } /* process_markup PARAGRAPHS * Process markup in PARAGRAPHS. We classify individual paragraphs and put * them inside their proper containers. */ void process_markup(struct paragraph *paras) { /* First, break paragraphs up into tables, if appropriate. */ process_tables(paras); /* Now generate containers for lists. */ process_lists(paras); /* And finally do block quotes. */ process_block_quotes(paras); }