/*
* 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>...%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);
}