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 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 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177
|
/*
* Routines to wrap lines and indent them.
*
* Copyright © 1998-2016 World Wide Web Consortium
* See http://www.w3.org/Consortium/Legal/copyright-software
*
* To do: count characters, not bytes
*
* Bert Bos
* Created 10 May 1998
* $Id: textwrap.c,v 1.32 2017/11/24 10:14:49 bbos Exp $
*/
#include "config.h"
#include <stdio.h>
#include <ctype.h>
#include <assert.h>
#if STDC_HEADERS
# include <string.h>
#else
# ifndef HAVE_STRCHR
# define strchr index
# define strrchr rindex
# endif
#endif
#include <stdlib.h>
#include <stdbool.h>
#include "export.h"
#include "types.e"
#include "errexit.e"
#include "heap.e"
/* To do: XML 1.1 allows , so the following isn't safe anymore */
#define NBSP 1 /* Marks non-break-space */
#define BREAKOP 2 /* '\002' marks a line break opportunity */
static unsigned char *buf = NULL;
static int buflen = 0; /* Size of buf */
static int len = 0; /* Length of buf */
static int bufchars = 0; /* Ditto in chars, not bytes */
static int linelen = 0; /* Length of printed line */
static int level = 0; /* Indentation level */
static int indent = 2; /* # of spaces per indent */
static int maxlinelen = 72; /* Desired line length */
static unsigned char prev = NBSP; /* Previously added char */
/* set_indent -- set the amount of indent per level */
EXPORT void set_indent(int n) {indent = n;}
/* set_linelen -- set the maximum length of a line */
EXPORT void set_linelen(int n) {maxlinelen = n;}
/* flush -- print word in buf */
EXPORT void flush()
{
int i, j, k;
assert(len <= buflen);
while (len != 0 && linelen + bufchars >= maxlinelen) { /* Need break */
/* Find last breakpoint i before maxlinelen, or first after it */
for (i = -1, j = 0, k = 0; j < len && (k < maxlinelen || i == -1); j++)
if (buf[j] == ' ') {i = j; k++;}
else if (buf[j] == BREAKOP) i = j; /* Breakpoint but no character */
else if ((buf[j] & 0xC0) != 0x80) k++; /* Start of a UTF-8 sequence */
if (i < 0) break; /* No breakpoint */
assert(i >= 0); /* Found a breakpoint at i */
assert(buf[i] == ' ' || buf[i]==BREAKOP);
/* Print up to breakpoint (removing non-break-space markers) */
for (j = 0; j < i; j++)
if (buf[j] != BREAKOP) {
putchar(buf[j] == NBSP ? ' ' : buf[j]);
if ((buf[j] & 0xC0) != 0x80) bufchars--;
}
putchar('\n'); /* Break line */
linelen = 0;
assert(level >= 0);
assert(len >= 0);
assert(i <= len);
i++; /* Skip the breakpoint */
len -= i;
if (len != 0) { /* If anything left, insert the indent */
memmove(buf + level * indent, buf + i, len);
for (j = 0; j < level * indent; j++) buf[j] = NBSP; /* Indent */
len += level * indent;
bufchars += level * indent;
}
}
/* Print rest, if any (removing non-break-space markers) */
/* First remove spaces at end of line */
while (len > 0 && buf[len-1] == ' ') {len--; bufchars--;}
for (j = 0; j < len; j++)
if (buf[j] == BREAKOP) /* skip */;
else if (buf[j] == '\n' || buf[j] == '\r') {putchar(buf[j]); linelen = 0;}
else if (buf[j] == NBSP) {putchar(' '); linelen++;}
else if ((buf[j] & 0xC0) != 0x80) {putchar(buf[j]); linelen++;}
else putchar(buf[j]);
bufchars = 0;
len = 0;
}
/* outc -- add one character to output buffer */
EXPORT void outc(char c, bool preformatted)
{
if (c == '\n' && !preformatted) c = ' '; /* Newline is just a space */
if (c == '\r' && !preformatted) c = ' '; /* CR is just a space */
if (c == '\t' && !preformatted) c = ' '; /* Tab is just a space */
if (c == '\f' && !preformatted) c = ' '; /* Formfeed is just a space */
if (c == ' ' && preformatted) c = NBSP; /* Non-break-space marker */
if (c == ' ' && prev == ' ') return; /* Don't add another space */
if ((c == ' ' || c == BREAKOP) && linelen + bufchars >= maxlinelen) flush();
if (c == '\n' || c == '\r' || c == '\f') flush(); /* Empty the buf */
if (c == ' ' && linelen + len == 0) return; /* No ins at BOL */
while (level * indent >= buflen) {buflen += 1024; renewarray(buf, buflen);}
if (linelen + len == 0 && !preformatted)
while (len < level * indent) {buf[len++] = NBSP; bufchars++;}
if (c == ' ' && len && buf[len-1] == ' ') return; /* Skip multiple spaces */
while (len >= buflen) {buflen += 1024; renewarray(buf, buflen);}
if ((c & 0xC0) != 0x80) bufchars++; /* Character */
buf[len++] = c; /* Finally, insert c */
prev = c; /* Remember for next round */
}
/* out -- add text to current output line, print line if getting too long */
EXPORT void out(string s, bool preformatted)
{
if (s) for (; *s; s++) outc(*s, preformatted);
}
/* outn -- add n chars to current output, print line if getting too long */
EXPORT void outn(string s, size_t n, bool preformatted)
{
size_t i;
for (i = 0; i < n; i++) outc(s[i], preformatted);
}
/* outln -- add string to output buffer, followed by '\n' */
EXPORT void outln(char *s, bool preformatted)
{
out(s, preformatted);
flush();
assert(len == 0);
assert(bufchars == 0);
putchar('\n');
linelen = 0;
}
/* outbreak -- conditional new line; make sure next text starts on new line */
EXPORT void outbreak(void)
{
flush();
assert(len == 0);
assert(bufchars == 0);
if (linelen != 0) {
putchar('\n');
linelen = 0;
}
}
/* outbreakpoint -- mark a possible line break point */
EXPORT void outbreakpoint(void)
{
outc(BREAKOP, false);
}
/* inc_indent -- increase indentation level by 1 */
EXPORT void inc_indent(void)
{
flush();
level++;
}
/* dec_indent -- decrease indentation level by 1 */
EXPORT void dec_indent(void)
{
flush();
level--;
}
|