summaryrefslogtreecommitdiffstats
path: root/contrib/groff/src/libs
diff options
context:
space:
mode:
authorru <ru@FreeBSD.org>2003-05-01 13:09:50 +0000
committerru <ru@FreeBSD.org>2003-05-01 13:09:50 +0000
commit06b40fe5ee58fd8a0b847795047930206cb3088b (patch)
tree252b1111c59af7f71db148c72c15e643186b9b86 /contrib/groff/src/libs
parent9fa30633edf6d37792cc22d6f97efbd6e19a6ad8 (diff)
parentc96557721be60d942f4d486b9ea7f9b7cbb034cc (diff)
downloadFreeBSD-src-06b40fe5ee58fd8a0b847795047930206cb3088b.zip
FreeBSD-src-06b40fe5ee58fd8a0b847795047930206cb3088b.tar.gz
This commit was generated by cvs2svn to compensate for changes in r114402,
which included commits to RCS files with non-trunk default branches.
Diffstat (limited to 'contrib/groff/src/libs')
-rw-r--r--contrib/groff/src/libs/libbib/Makefile.sub8
-rw-r--r--contrib/groff/src/libs/libbib/common.cpp38
-rw-r--r--contrib/groff/src/libs/libbib/index.cpp640
-rw-r--r--contrib/groff/src/libs/libbib/linear.cpp503
-rw-r--r--contrib/groff/src/libs/libbib/search.cpp133
-rw-r--r--contrib/groff/src/libs/libdriver/Makefile.sub4
-rw-r--r--contrib/groff/src/libs/libdriver/input.cpp1831
-rw-r--r--contrib/groff/src/libs/libdriver/printer.cpp214
-rw-r--r--contrib/groff/src/libs/libgroff/Makefile.sub74
-rw-r--r--contrib/groff/src/libs/libgroff/assert.cpp34
-rw-r--r--contrib/groff/src/libs/libgroff/change_lf.cpp37
-rw-r--r--contrib/groff/src/libs/libgroff/cmap.cpp56
-rw-r--r--contrib/groff/src/libs/libgroff/color.cpp427
-rw-r--r--contrib/groff/src/libs/libgroff/cset.cpp102
-rw-r--r--contrib/groff/src/libs/libgroff/device.cpp36
-rw-r--r--contrib/groff/src/libs/libgroff/errarg.cpp128
-rw-r--r--contrib/groff/src/libs/libgroff/error.cpp142
-rw-r--r--contrib/groff/src/libs/libgroff/fatal.cpp27
-rw-r--r--contrib/groff/src/libs/libgroff/filename.cpp2
-rw-r--r--contrib/groff/src/libs/libgroff/font.cpp1034
-rw-r--r--contrib/groff/src/libs/libgroff/fontfile.cpp67
-rw-r--r--contrib/groff/src/libs/libgroff/geometry.cpp286
-rw-r--r--contrib/groff/src/libs/libgroff/htmlhint.cpp59
-rw-r--r--contrib/groff/src/libs/libgroff/invalid.cpp60
-rw-r--r--contrib/groff/src/libs/libgroff/lf.cpp62
-rw-r--r--contrib/groff/src/libs/libgroff/lineno.cpp1
-rw-r--r--contrib/groff/src/libs/libgroff/macropath.cpp30
-rw-r--r--contrib/groff/src/libs/libgroff/maxfilename.cpp69
-rw-r--r--contrib/groff/src/libs/libgroff/mksdir.cpp34
-rw-r--r--contrib/groff/src/libs/libgroff/mkstemp.cpp34
-rw-r--r--contrib/groff/src/libs/libgroff/nametoindex.cpp117
-rw-r--r--contrib/groff/src/libs/libgroff/new.cpp71
-rw-r--r--contrib/groff/src/libs/libgroff/paper.cpp84
-rw-r--r--contrib/groff/src/libs/libgroff/prime.cpp26
-rw-r--r--contrib/groff/src/libs/libgroff/progname.cpp1
-rw-r--r--contrib/groff/src/libs/libgroff/ptable.cpp52
-rw-r--r--contrib/groff/src/libs/libgroff/searchpath.cpp132
-rw-r--r--contrib/groff/src/libs/libgroff/strcasecmp.c66
-rw-r--r--contrib/groff/src/libs/libgroff/string.cpp341
-rw-r--r--contrib/groff/src/libs/libgroff/strncasecmp.c2
-rw-r--r--contrib/groff/src/libs/libgroff/strsave.cpp31
-rw-r--r--contrib/groff/src/libs/libgroff/tmpfile.cpp172
-rw-r--r--contrib/groff/src/libs/libgroff/tmpname.cpp116
43 files changed, 7341 insertions, 42 deletions
diff --git a/contrib/groff/src/libs/libbib/Makefile.sub b/contrib/groff/src/libs/libbib/Makefile.sub
index 6e59d64..19dd439 100644
--- a/contrib/groff/src/libs/libbib/Makefile.sub
+++ b/contrib/groff/src/libs/libbib/Makefile.sub
@@ -6,9 +6,9 @@ OBJS=\
search.$(OBJEXT) \
map.$(OBJEXT)
CCSRCS=\
- $(srcdir)/common.cc \
- $(srcdir)/index.cc \
- $(srcdir)/linear.cc \
- $(srcdir)/search.cc
+ $(srcdir)/common.cpp \
+ $(srcdir)/index.cpp \
+ $(srcdir)/linear.cpp \
+ $(srcdir)/search.cpp
CSRCS=\
$(srcdir)/map.c
diff --git a/contrib/groff/src/libs/libbib/common.cpp b/contrib/groff/src/libs/libbib/common.cpp
new file mode 100644
index 0000000..4b2bcca
--- /dev/null
+++ b/contrib/groff/src/libs/libbib/common.cpp
@@ -0,0 +1,38 @@
+/* Copyright (C) 1989, 1990, 1991, 1992 Free Software Foundation, Inc.
+ Written by James Clark (jjc@jclark.com)
+
+This file is part of groff.
+
+groff is free software; you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free
+Software Foundation; either version 2, or (at your option) any later
+version.
+
+groff is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+for more details.
+
+You should have received a copy of the GNU General Public License along
+with groff; see the file COPYING. If not, write to the Free Software
+Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
+
+unsigned hash(const char *s, int len)
+{
+#if 0
+ unsigned h = 0, g;
+ while (*s != '\0') {
+ h <<= 4;
+ h += *s++;
+ if ((g = h & 0xf0000000) != 0) {
+ h ^= g >> 24;
+ h ^= g;
+ }
+ }
+#endif
+ unsigned h = 0;
+ while (--len >= 0)
+ h = *s++ + 65587*h;
+ return h;
+}
+
diff --git a/contrib/groff/src/libs/libbib/index.cpp b/contrib/groff/src/libs/libbib/index.cpp
new file mode 100644
index 0000000..3633df1
--- /dev/null
+++ b/contrib/groff/src/libs/libbib/index.cpp
@@ -0,0 +1,640 @@
+// -*- C++ -*-
+/* Copyright (C) 1989, 1990, 1991, 1992, 2001 Free Software Foundation, Inc.
+ Written by James Clark (jjc@jclark.com)
+
+This file is part of groff.
+
+groff is free software; you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free
+Software Foundation; either version 2, or (at your option) any later
+version.
+
+groff is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+for more details.
+
+You should have received a copy of the GNU General Public License along
+with groff; see the file COPYING. If not, write to the Free Software
+Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
+
+#include "lib.h"
+
+#include <stdlib.h>
+#include <errno.h>
+
+#include "posix.h"
+#include "cset.h"
+#include "cmap.h"
+#include "errarg.h"
+#include "error.h"
+
+#include "refid.h"
+#include "search.h"
+#include "index.h"
+#include "defs.h"
+
+#include "nonposix.h"
+
+// Interface to mmap.
+extern "C" {
+ void *mapread(int fd, int len);
+ int unmap(void *, int len);
+}
+
+#if 0
+const
+#endif
+int minus_one = -1;
+
+int verify_flag = 0;
+
+struct word_list;
+
+class index_search_item : public search_item {
+ search_item *out_of_date_files;
+ index_header header;
+ char *buffer;
+ void *map_addr;
+ int map_len;
+ tag *tags;
+ int *table;
+ int *lists;
+ char *pool;
+ char *key_buffer;
+ char *filename_buffer;
+ int filename_buflen;
+ char **common_words_table;
+ int common_words_table_size;
+ const char *ignore_fields;
+ time_t mtime;
+
+ const char *do_verify();
+ const int *search1(const char **pp, const char *end);
+ const int *search(const char *ptr, int length, int **temp_listp);
+ const char *munge_filename(const char *);
+ void read_common_words_file();
+ void add_out_of_date_file(int fd, const char *filename, int fid);
+public:
+ index_search_item(const char *, int);
+ ~index_search_item();
+ int load(int fd);
+ search_item_iterator *make_search_item_iterator(const char *);
+ int verify();
+ void check_files();
+ int next_filename_id() const;
+ friend class index_search_item_iterator;
+};
+
+class index_search_item_iterator : public search_item_iterator {
+ index_search_item *indx;
+ search_item_iterator *out_of_date_files_iter;
+ search_item *next_out_of_date_file;
+ const int *found_list;
+ int *temp_list;
+ char *buf;
+ int buflen;
+ linear_searcher searcher;
+ char *query;
+ int get_tag(int tagno, const linear_searcher &, const char **, int *,
+ reference_id *);
+public:
+ index_search_item_iterator(index_search_item *, const char *);
+ ~index_search_item_iterator();
+ int next(const linear_searcher &, const char **, int *, reference_id *);
+};
+
+
+index_search_item::index_search_item(const char *filename, int fid)
+: search_item(filename, fid), out_of_date_files(0), buffer(0), map_addr(0),
+ map_len(0), key_buffer(0), filename_buffer(0), filename_buflen(0),
+ common_words_table(0)
+{
+}
+
+index_search_item::~index_search_item()
+{
+ if (buffer)
+ free(buffer);
+ if (map_addr) {
+ if (unmap(map_addr, map_len) < 0)
+ error("unmap: %1", strerror(errno));
+ }
+ while (out_of_date_files) {
+ search_item *tem = out_of_date_files;
+ out_of_date_files = out_of_date_files->next;
+ delete tem;
+ }
+ a_delete filename_buffer;
+ a_delete key_buffer;
+ if (common_words_table) {
+ for (int i = 0; i < common_words_table_size; i++)
+ a_delete common_words_table[i];
+ a_delete common_words_table;
+ }
+}
+
+class file_closer {
+ int *fdp;
+public:
+ file_closer(int &fd) : fdp(&fd) { }
+ ~file_closer() { close(*fdp); }
+};
+
+// Tell the compiler that a variable is intentionally unused.
+inline void unused(void *) { }
+
+int index_search_item::load(int fd)
+{
+ file_closer fd_closer(fd); // close fd on return
+ unused(&fd_closer);
+ struct stat sb;
+ if (fstat(fd, &sb) < 0) {
+ error("can't fstat `%1': %2", name, strerror(errno));
+ return 0;
+ }
+ if (!S_ISREG(sb.st_mode)) {
+ error("`%1' is not a regular file", name);
+ return 0;
+ }
+ mtime = sb.st_mtime;
+ int size = int(sb.st_size);
+ char *addr;
+ map_addr = mapread(fd, size);
+ if (map_addr) {
+ addr = (char *)map_addr;
+ map_len = size;
+ }
+ else {
+ addr = buffer = (char *)malloc(size);
+ if (buffer == 0) {
+ error("can't allocate buffer for `%1'", name);
+ return 0;
+ }
+ char *ptr = buffer;
+ int bytes_to_read = size;
+ while (bytes_to_read > 0) {
+ int nread = read(fd, ptr, bytes_to_read);
+ if (nread == 0) {
+ error("unexpected EOF on `%1'", name);
+ return 0;
+ }
+ if (nread < 0) {
+ error("read error on `%1': %2", name, strerror(errno));
+ return 0;
+ }
+ bytes_to_read -= nread;
+ ptr += nread;
+ }
+ }
+ header = *(index_header *)addr;
+ if (header.magic != INDEX_MAGIC) {
+ error("`%1' is not an index file: wrong magic number", name);
+ return 0;
+ }
+ if (header.version != INDEX_VERSION) {
+ error("version number in `%1' is wrong: was %2, should be %3",
+ name, header.version, INDEX_VERSION);
+ return 0;
+ }
+ int sz = (header.tags_size * sizeof(tag)
+ + header.lists_size * sizeof(int)
+ + header.table_size * sizeof(int)
+ + header.strings_size
+ + sizeof(header));
+ if (sz != size) {
+ error("size of `%1' is wrong: was %2, should be %3",
+ name, size, sz);
+ return 0;
+ }
+ tags = (tag *)(addr + sizeof(header));
+ lists = (int *)(tags + header.tags_size);
+ table = (int *)(lists + header.lists_size);
+ pool = (char *)(table + header.table_size);
+ ignore_fields = strchr(strchr(pool, '\0') + 1, '\0') + 1;
+ key_buffer = new char[header.truncate];
+ read_common_words_file();
+ return 1;
+}
+
+const char *index_search_item::do_verify()
+{
+ if (tags == 0)
+ return "not loaded";
+ if (lists[header.lists_size - 1] >= 0)
+ return "last list element not negative";
+ int i;
+ for (i = 0; i < header.table_size; i++) {
+ int li = table[i];
+ if (li >= header.lists_size)
+ return "bad list index";
+ if (li >= 0) {
+ for (int *ptr = lists + li; *ptr >= 0; ptr++) {
+ if (*ptr >= header.tags_size)
+ return "bad tag index";
+ if (*ptr >= ptr[1] && ptr[1] >= 0)
+ return "list not ordered";
+ }
+ }
+ }
+ for (i = 0; i < header.tags_size; i++) {
+ if (tags[i].filename_index >= header.strings_size)
+ return "bad index in tags";
+ if (tags[i].length < 0)
+ return "bad length in tags";
+ if (tags[i].start < 0)
+ return "bad start in tags";
+ }
+ if (pool[header.strings_size - 1] != '\0')
+ return "last character in pool not nul";
+ return 0;
+}
+
+int index_search_item::verify()
+{
+ const char *reason = do_verify();
+ if (!reason)
+ return 1;
+ error("`%1' is bad: %2", name, reason);
+ return 0;
+}
+
+int index_search_item::next_filename_id() const
+{
+ return filename_id + header.strings_size + 1;
+}
+
+search_item_iterator *index_search_item::make_search_item_iterator(
+ const char *query)
+{
+ return new index_search_item_iterator(this, query);
+}
+
+search_item *make_index_search_item(const char *filename, int fid)
+{
+ char *index_filename = new char[strlen(filename) + sizeof(INDEX_SUFFIX)];
+ strcpy(index_filename, filename);
+ strcat(index_filename, INDEX_SUFFIX);
+ int fd = open(index_filename, O_RDONLY | O_BINARY);
+ if (fd < 0)
+ return 0;
+ index_search_item *item = new index_search_item(index_filename, fid);
+ a_delete index_filename;
+ if (!item->load(fd)) {
+ close(fd);
+ delete item;
+ return 0;
+ }
+ else if (verify_flag && !item->verify()) {
+ delete item;
+ return 0;
+ }
+ else {
+ item->check_files();
+ return item;
+ }
+}
+
+
+index_search_item_iterator::index_search_item_iterator(index_search_item *ind,
+ const char *q)
+: indx(ind), out_of_date_files_iter(0), next_out_of_date_file(0), temp_list(0),
+ buf(0), buflen(0),
+ searcher(q, strlen(q), ind->ignore_fields, ind->header.truncate),
+ query(strsave(q))
+{
+ found_list = indx->search(q, strlen(q), &temp_list);
+ if (!found_list) {
+ found_list = &minus_one;
+ warning("all keys would have been discarded in constructing index `%1'",
+ indx->name);
+ }
+}
+
+index_search_item_iterator::~index_search_item_iterator()
+{
+ a_delete temp_list;
+ a_delete buf;
+ a_delete query;
+ delete out_of_date_files_iter;
+}
+
+int index_search_item_iterator::next(const linear_searcher &,
+ const char **pp, int *lenp,
+ reference_id *ridp)
+{
+ if (found_list) {
+ for (;;) {
+ int tagno = *found_list;
+ if (tagno == -1)
+ break;
+ found_list++;
+ if (get_tag(tagno, searcher, pp, lenp, ridp))
+ return 1;
+ }
+ found_list = 0;
+ next_out_of_date_file = indx->out_of_date_files;
+ }
+ while (next_out_of_date_file) {
+ if (out_of_date_files_iter == 0)
+ out_of_date_files_iter
+ = next_out_of_date_file->make_search_item_iterator(query);
+ if (out_of_date_files_iter->next(searcher, pp, lenp, ridp))
+ return 1;
+ delete out_of_date_files_iter;
+ out_of_date_files_iter = 0;
+ next_out_of_date_file = next_out_of_date_file->next;
+ }
+ return 0;
+}
+
+int index_search_item_iterator::get_tag(int tagno,
+ const linear_searcher &searcher,
+ const char **pp, int *lenp,
+ reference_id *ridp)
+{
+ if (tagno < 0 || tagno >= indx->header.tags_size) {
+ error("bad tag number");
+ return 0;
+ }
+ tag *tp = indx->tags + tagno;
+ const char *filename = indx->munge_filename(indx->pool + tp->filename_index);
+ int fd = open(filename, O_RDONLY | O_BINARY);
+ if (fd < 0) {
+ error("can't open `%1': %2", filename, strerror(errno));
+ return 0;
+ }
+ struct stat sb;
+ if (fstat(fd, &sb) < 0) {
+ error("can't fstat: %1", strerror(errno));
+ close(fd);
+ return 0;
+ }
+ time_t mtime = sb.st_mtime;
+ if (mtime > indx->mtime) {
+ indx->add_out_of_date_file(fd, filename,
+ indx->filename_id + tp->filename_index);
+ return 0;
+ }
+ int res = 0;
+ FILE *fp = fdopen(fd, FOPEN_RB);
+ if (!fp) {
+ error("fdopen failed");
+ close(fd);
+ return 0;
+ }
+ if (tp->start != 0 && fseek(fp, long(tp->start), 0) < 0)
+ error("can't seek on `%1': %2", filename, strerror(errno));
+ else {
+ int length = tp->length;
+ int err = 0;
+ if (length == 0) {
+ struct stat sb;
+ if (fstat(fileno(fp), &sb) < 0) {
+ error("can't stat `%1': %2", filename, strerror(errno));
+ err = 1;
+ }
+ else if (!S_ISREG(sb.st_mode)) {
+ error("`%1' is not a regular file", filename);
+ err = 1;
+ }
+ else
+ length = int(sb.st_size);
+ }
+ if (!err) {
+ if (length + 2 > buflen) {
+ a_delete buf;
+ buflen = length + 2;
+ buf = new char[buflen];
+ }
+ if (fread(buf + 1, 1, length, fp) != (size_t)length)
+ error("fread on `%1' failed: %2", filename, strerror(errno));
+ else {
+ buf[0] = '\n';
+ // Remove the CR characters from CRLF pairs.
+ int sidx = 1, didx = 1;
+ for ( ; sidx < length + 1; sidx++, didx++)
+ {
+ if (buf[sidx] == '\r')
+ {
+ if (buf[++sidx] != '\n')
+ buf[didx++] = '\r';
+ else
+ length--;
+ }
+ if (sidx != didx)
+ buf[didx] = buf[sidx];
+ }
+ buf[length + 1] = '\n';
+ res = searcher.search(buf + 1, buf + 2 + length, pp, lenp);
+ if (res && ridp)
+ *ridp = reference_id(indx->filename_id + tp->filename_index,
+ tp->start);
+ }
+ }
+ }
+ fclose(fp);
+ return res;
+}
+
+const char *index_search_item::munge_filename(const char *filename)
+{
+ if (IS_ABSOLUTE(filename))
+ return filename;
+ const char *cwd = pool;
+ int need_slash = (cwd[0] != 0
+ && strchr(DIR_SEPS, strchr(cwd, '\0')[-1]) == 0);
+ int len = strlen(cwd) + strlen(filename) + need_slash + 1;
+ if (len > filename_buflen) {
+ a_delete filename_buffer;
+ filename_buflen = len;
+ filename_buffer = new char[len];
+ }
+ strcpy(filename_buffer, cwd);
+ if (need_slash)
+ strcat(filename_buffer, "/");
+ strcat(filename_buffer, filename);
+ return filename_buffer;
+}
+
+const int *index_search_item::search1(const char **pp, const char *end)
+{
+ while (*pp < end && !csalnum(**pp))
+ *pp += 1;
+ if (*pp >= end)
+ return 0;
+ const char *start = *pp;
+ while (*pp < end && csalnum(**pp))
+ *pp += 1;
+ int len = *pp - start;
+ if (len < header.shortest)
+ return 0;
+ if (len > header.truncate)
+ len = header.truncate;
+ int is_number = 1;
+ for (int i = 0; i < len; i++)
+ if (csdigit(start[i]))
+ key_buffer[i] = start[i];
+ else {
+ key_buffer[i] = cmlower(start[i]);
+ is_number = 0;
+ }
+ if (is_number && !(len == 4 && start[0] == '1' && start[1] == '9'))
+ return 0;
+ unsigned hc = hash(key_buffer, len);
+ if (common_words_table) {
+ for (int h = hc % common_words_table_size;
+ common_words_table[h];
+ --h) {
+ if (strlen(common_words_table[h]) == (size_t)len
+ && memcmp(common_words_table[h], key_buffer, len) == 0)
+ return 0;
+ if (h == 0)
+ h = common_words_table_size;
+ }
+ }
+ int li = table[int(hc % header.table_size)];
+ return li < 0 ? &minus_one : lists + li;
+}
+
+static void merge(int *result, const int *s1, const int *s2)
+{
+ for (; *s1 >= 0; s1++) {
+ while (*s2 >= 0 && *s2 < *s1)
+ s2++;
+ if (*s2 == *s1)
+ *result++ = *s2;
+ }
+ *result++ = -1;
+}
+
+const int *index_search_item::search(const char *ptr, int length,
+ int **temp_listp)
+{
+ const char *end = ptr + length;
+ if (*temp_listp) {
+ a_delete *temp_listp;
+ *temp_listp = 0;
+ }
+ const int *first_list = 0;
+ while (ptr < end && (first_list = search1(&ptr, end)) == 0)
+ ;
+ if (!first_list)
+ return 0;
+ if (*first_list < 0)
+ return first_list;
+ const int *second_list = 0;
+ while (ptr < end && (second_list = search1(&ptr, end)) == 0)
+ ;
+ if (!second_list)
+ return first_list;
+ if (*second_list < 0)
+ return second_list;
+ const int *p;
+ for (p = first_list; *p >= 0; p++)
+ ;
+ int len = p - first_list;
+ for (p = second_list; *p >= 0; p++)
+ ;
+ if (p - second_list < len)
+ len = p - second_list;
+ int *matches = new int[len + 1];
+ merge(matches, first_list, second_list);
+ while (ptr < end) {
+ const int *list = search1(&ptr, end);
+ if (list != 0) {
+ if (*list < 0) {
+ a_delete matches;
+ return list;
+ }
+ merge(matches, matches, list);
+ if (*matches < 0) {
+ a_delete matches;
+ return &minus_one;
+ }
+ }
+ }
+ *temp_listp = matches;
+ return matches;
+}
+
+void index_search_item::read_common_words_file()
+{
+ if (header.common <= 0)
+ return;
+ const char *common_words_file = munge_filename(strchr(pool, '\0') + 1);
+ errno = 0;
+ FILE *fp = fopen(common_words_file, "r");
+ if (!fp) {
+ error("can't open `%1': %2", common_words_file, strerror(errno));
+ return;
+ }
+ common_words_table_size = 2*header.common + 1;
+ while (!is_prime(common_words_table_size))
+ common_words_table_size++;
+ common_words_table = new char *[common_words_table_size];
+ for (int i = 0; i < common_words_table_size; i++)
+ common_words_table[i] = 0;
+ int count = 0;
+ int key_len = 0;
+ for (;;) {
+ int c = getc(fp);
+ while (c != EOF && !csalnum(c))
+ c = getc(fp);
+ if (c == EOF)
+ break;
+ do {
+ if (key_len < header.truncate)
+ key_buffer[key_len++] = cmlower(c);
+ c = getc(fp);
+ } while (c != EOF && csalnum(c));
+ if (key_len >= header.shortest) {
+ int h = hash(key_buffer, key_len) % common_words_table_size;
+ while (common_words_table[h]) {
+ if (h == 0)
+ h = common_words_table_size;
+ --h;
+ }
+ common_words_table[h] = new char[key_len + 1];
+ memcpy(common_words_table[h], key_buffer, key_len);
+ common_words_table[h][key_len] = '\0';
+ }
+ if (++count >= header.common)
+ break;
+ key_len = 0;
+ if (c == EOF)
+ break;
+ }
+ fclose(fp);
+}
+
+void index_search_item::add_out_of_date_file(int fd, const char *filename,
+ int fid)
+{
+ search_item **pp;
+ for (pp = &out_of_date_files; *pp; pp = &(*pp)->next)
+ if ((*pp)->is_named(filename))
+ return;
+ *pp = make_linear_search_item(fd, filename, fid);
+ warning("`%1' modified since `%2' created", filename, name);
+}
+
+void index_search_item::check_files()
+{
+ const char *pool_end = pool + header.strings_size;
+ for (const char *ptr = strchr(ignore_fields, '\0') + 1;
+ ptr < pool_end;
+ ptr = strchr(ptr, '\0') + 1) {
+ const char *path = munge_filename(ptr);
+ struct stat sb;
+ if (stat(path, &sb) < 0)
+ error("can't stat `%1': %2", path, strerror(errno));
+ else if (sb.st_mtime > mtime) {
+ int fd = open(path, O_RDONLY | O_BINARY);
+ if (fd < 0)
+ error("can't open `%1': %2", path, strerror(errno));
+ else
+ add_out_of_date_file(fd, path, filename_id + (ptr - pool));
+ }
+ }
+}
diff --git a/contrib/groff/src/libs/libbib/linear.cpp b/contrib/groff/src/libs/libbib/linear.cpp
new file mode 100644
index 0000000..1dd902b
--- /dev/null
+++ b/contrib/groff/src/libs/libbib/linear.cpp
@@ -0,0 +1,503 @@
+// -*- C++ -*-
+/* Copyright (C) 1989, 1990, 1991, 1992, 2000, 2001
+ Free Software Foundation, Inc.
+ Written by James Clark (jjc@jclark.com)
+
+This file is part of groff.
+
+groff is free software; you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free
+Software Foundation; either version 2, or (at your option) any later
+version.
+
+groff is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+for more details.
+
+You should have received a copy of the GNU General Public License along
+with groff; see the file COPYING. If not, write to the Free Software
+Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
+
+#include "lib.h"
+
+#include <stdlib.h>
+#include <assert.h>
+#include <errno.h>
+
+#include "posix.h"
+#include "errarg.h"
+#include "error.h"
+#include "cset.h"
+#include "cmap.h"
+#include "nonposix.h"
+
+#include "refid.h"
+#include "search.h"
+
+class file_buffer {
+ char *buffer;
+ char *bufend;
+public:
+ file_buffer();
+ ~file_buffer();
+ int load(int fd, const char *filename);
+ const char *get_start() const;
+ const char *get_end() const;
+};
+
+typedef unsigned char uchar;
+
+static uchar map[256];
+static uchar inv_map[256][3];
+
+struct map_init {
+ map_init();
+};
+
+static map_init the_map_init;
+
+map_init::map_init()
+{
+ int i;
+ for (i = 0; i < 256; i++)
+ map[i] = csalnum(i) ? cmlower(i) : '\0';
+ for (i = 0; i < 256; i++) {
+ if (cslower(i)) {
+ inv_map[i][0] = i;
+ inv_map[i][1] = cmupper(i);
+ inv_map[i][2] = '\0';
+ }
+ else if (csdigit(i)) {
+ inv_map[i][0] = i;
+ inv_map[i][1] = 0;
+ }
+ else
+ inv_map[i][0] = '\0';
+ }
+}
+
+
+class bmpattern {
+ char *pat;
+ int len;
+ int delta[256];
+public:
+ bmpattern(const char *pattern, int pattern_length);
+ ~bmpattern();
+ const char *search(const char *p, const char *end) const;
+ int length() const;
+};
+
+bmpattern::bmpattern(const char *pattern, int pattern_length)
+: len(pattern_length)
+{
+ pat = new char[len];
+ int i;
+ for (i = 0; i < len; i++)
+ pat[i] = map[uchar(pattern[i])];
+ for (i = 0; i < 256; i++)
+ delta[i] = len;
+ for (i = 0; i < len; i++)
+ for (const unsigned char *inv = inv_map[uchar(pat[i])]; *inv; inv++)
+ delta[*inv] = len - i - 1;
+}
+
+const char *bmpattern::search(const char *buf, const char *end) const
+{
+ int buflen = end - buf;
+ if (len > buflen)
+ return 0;
+ const char *strend;
+ if (buflen > len*4)
+ strend = end - len*4;
+ else
+ strend = buf;
+ const char *k = buf + len - 1;
+ const int *del = delta;
+ const char *pattern = pat;
+ for (;;) {
+ while (k < strend) {
+ int t = del[uchar(*k)];
+ if (!t)
+ break;
+ k += t;
+ k += del[uchar(*k)];
+ k += del[uchar(*k)];
+ }
+ while (k < end && del[uchar(*k)] != 0)
+ k++;
+ if (k == end)
+ break;
+ int j = len - 1;
+ const char *s = k;
+ for (;;) {
+ if (j == 0)
+ return s;
+ if (map[uchar(*--s)] != uchar(pattern[--j]))
+ break;
+ }
+ k++;
+ }
+ return 0;
+}
+
+bmpattern::~bmpattern()
+{
+ a_delete pat;
+}
+
+inline int bmpattern::length() const
+{
+ return len;
+}
+
+
+static const char *find_end(const char *bufend, const char *p);
+
+const char *linear_searcher::search_and_check(const bmpattern *key,
+ const char *buf, const char *bufend, const char **start) const
+{
+ assert(buf[-1] == '\n');
+ assert(bufend[-1] == '\n');
+ const char *ptr = buf;
+ for (;;) {
+ const char *found = key->search(ptr, bufend);
+ if (!found)
+ break;
+ if (check_match(buf, bufend, found, key->length(), &ptr, start))
+ return found;
+ }
+ return 0;
+}
+
+static const char *skip_field(const char *end, const char *p)
+{
+ for (;;)
+ if (*p++ == '\n') {
+ if (p == end || *p == '%')
+ break;
+ const char *q;
+ for (q = p; *q == ' ' || *q == '\t'; q++)
+ ;
+ if (*q == '\n')
+ break;
+ p = q + 1;
+ }
+ return p;
+}
+
+static const char *find_end(const char *bufend, const char *p)
+{
+ for (;;)
+ if (*p++ == '\n') {
+ if (p == bufend)
+ break;
+ const char *q;
+ for (q = p; *q == ' ' || *q == '\t'; q++)
+ ;
+ if (*q == '\n')
+ break;
+ p = q + 1;
+ }
+ return p;
+}
+
+
+int linear_searcher::check_match(const char *buf, const char *bufend,
+ const char *match, int matchlen,
+ const char **cont, const char **start) const
+{
+ *cont = match + 1;
+ // The user is required to supply only the first truncate_len characters
+ // of the key. If truncate_len <= 0, he must supply all the key.
+ if ((truncate_len <= 0 || matchlen < truncate_len)
+ && map[uchar(match[matchlen])] != '\0')
+ return 0;
+
+ // The character before the match must not be an alphanumeric
+ // character (unless the alphanumeric character follows one or two
+ // percent characters at the beginning of the line), nor must it be
+ // a percent character at the beginning of a line, nor a percent
+ // character following a percent character at the beginning of a
+ // line.
+
+ switch (match - buf) {
+ case 0:
+ break;
+ case 1:
+ if (match[-1] == '%' || map[uchar(match[-1])] != '\0')
+ return 0;
+ break;
+ case 2:
+ if (map[uchar(match[-1])] != '\0' && match[-2] != '%')
+ return 0;
+ if (match[-1] == '%'
+ && (match[-2] == '\n' || match[-2] == '%'))
+ return 0;
+ break;
+ default:
+ if (map[uchar(match[-1])] != '\0'
+ && !(match[-2] == '%'
+ && (match[-3] == '\n'
+ || (match[-3] == '%' && match[-4] == '\n'))))
+ return 0;
+ if (match[-1] == '%'
+ && (match[-2] == '\n'
+ || (match[-2] == '%' && match[-3] == '\n')))
+ return 0;
+ }
+
+ const char *p = match;
+ int had_percent = 0;
+ for (;;) {
+ if (*p == '\n') {
+ if (!had_percent && p[1] == '%') {
+ if (p[2] != '\0' && strchr(ignore_fields, p[2]) != 0) {
+ *cont = skip_field(bufend, match + matchlen);
+ return 0;
+ }
+ if (!start)
+ break;
+ had_percent = 1;
+ }
+ if (p <= buf) {
+ if (start)
+ *start = p + 1;
+ return 1;
+ }
+ const char *q;
+ for (q = p - 1; *q == ' ' || *q == '\t'; q--)
+ ;
+ if (*q == '\n') {
+ if (start)
+ *start = p + 1;
+ break;
+ }
+ p = q;
+ }
+ p--;
+ }
+ return 1;
+}
+
+file_buffer::file_buffer()
+: buffer(0), bufend(0)
+{
+}
+
+file_buffer::~file_buffer()
+{
+ a_delete buffer;
+}
+
+const char *file_buffer::get_start() const
+{
+ return buffer ? buffer + 4 : 0;
+}
+
+const char *file_buffer::get_end() const
+{
+ return bufend;
+}
+
+int file_buffer::load(int fd, const char *filename)
+{
+ struct stat sb;
+ if (fstat(fd, &sb) < 0)
+ error("can't fstat `%1': %2", filename, strerror(errno));
+ else if (!S_ISREG(sb.st_mode))
+ error("`%1' is not a regular file", filename);
+ else {
+ // We need one character extra at the beginning for an additional newline
+ // used as a sentinel. We get 4 instead so that the read buffer will be
+ // word-aligned. This seems to make the read slightly faster. We also
+ // need one character at the end also for an additional newline used as a
+ // sentinel.
+ int size = int(sb.st_size);
+ buffer = new char[size + 4 + 1];
+ int nread = read(fd, buffer + 4, size);
+ if (nread < 0)
+ error("error reading `%1': %2", filename, strerror(errno));
+ else if (nread != size)
+ error("size of `%1' decreased", filename);
+ else {
+ char c;
+ nread = read(fd, &c, 1);
+ if (nread != 0)
+ error("size of `%1' increased", filename);
+ else if (memchr(buffer + 4, '\0', size < 1024 ? size : 1024) != 0)
+ error("database `%1' is a binary file", filename);
+ else {
+ close(fd);
+ buffer[3] = '\n';
+ int sidx = 4, didx = 4;
+ for ( ; sidx < size + 4; sidx++, didx++)
+ {
+ if (buffer[sidx] == '\r')
+ {
+ if (buffer[++sidx] != '\n')
+ buffer[didx++] = '\r';
+ else
+ size--;
+ }
+ if (sidx != didx)
+ buffer[didx] = buffer[sidx];
+ }
+ bufend = buffer + 4 + size;
+ if (bufend[-1] != '\n')
+ *bufend++ = '\n';
+ return 1;
+ }
+ }
+ a_delete buffer;
+ buffer = 0;
+ }
+ close(fd);
+ return 0;
+}
+
+linear_searcher::linear_searcher(const char *query, int query_len,
+ const char *ign, int trunc)
+: ignore_fields(ign), truncate_len(trunc), keys(0), nkeys(0)
+{
+ const char *query_end = query + query_len;
+ int nk = 0;
+ const char *p;
+ for (p = query; p < query_end; p++)
+ if (map[uchar(*p)] != '\0'
+ && (p[1] == '\0' || map[uchar(p[1])] == '\0'))
+ nk++;
+ if (nk == 0)
+ return;
+ keys = new bmpattern*[nk];
+ p = query;
+ for (;;) {
+ while (p < query_end && map[uchar(*p)] == '\0')
+ p++;
+ if (p == query_end)
+ break;
+ const char *start = p;
+ while (p < query_end && map[uchar(*p)] != '\0')
+ p++;
+ keys[nkeys++] = new bmpattern(start, p - start);
+ }
+ assert(nkeys <= nk);
+ if (nkeys == 0) {
+ a_delete keys;
+ keys = 0;
+ }
+}
+
+linear_searcher::~linear_searcher()
+{
+ for (int i = 0; i < nkeys; i++)
+ delete keys[i];
+ a_delete keys;
+}
+
+int linear_searcher::search(const char *buffer, const char *bufend,
+ const char **startp, int *lengthp) const
+{
+ assert(bufend - buffer > 0);
+ assert(buffer[-1] == '\n');
+ assert(bufend[-1] == '\n');
+ if (nkeys == 0)
+ return 0;
+ for (;;) {
+ const char *refstart;
+ const char *found = search_and_check(keys[0], buffer, bufend, &refstart);
+ if (!found)
+ break;
+ const char *refend = find_end(bufend, found + keys[0]->length());
+ int i;
+ for (i = 1; i < nkeys; i++)
+ if (!search_and_check(keys[i], refstart, refend))
+ break;
+ if (i >= nkeys) {
+ *startp = refstart;
+ *lengthp = refend - refstart;
+ return 1;
+ }
+ buffer = refend;
+ }
+ return 0;
+}
+
+class linear_search_item : public search_item {
+ file_buffer fbuf;
+public:
+ linear_search_item(const char *filename, int fid);
+ ~linear_search_item();
+ int load(int fd);
+ search_item_iterator *make_search_item_iterator(const char *);
+ friend class linear_search_item_iterator;
+};
+
+class linear_search_item_iterator : public search_item_iterator {
+ linear_search_item *lsi;
+ int pos;
+public:
+ linear_search_item_iterator(linear_search_item *, const char *query);
+ ~linear_search_item_iterator();
+ int next(const linear_searcher &, const char **ptr, int *lenp,
+ reference_id *ridp);
+};
+
+search_item *make_linear_search_item(int fd, const char *filename, int fid)
+{
+ linear_search_item *item = new linear_search_item(filename, fid);
+ if (!item->load(fd)) {
+ delete item;
+ return 0;
+ }
+ else
+ return item;
+}
+
+linear_search_item::linear_search_item(const char *filename, int fid)
+: search_item(filename, fid)
+{
+}
+
+linear_search_item::~linear_search_item()
+{
+}
+
+int linear_search_item::load(int fd)
+{
+ return fbuf.load(fd, name);
+}
+
+search_item_iterator *linear_search_item::make_search_item_iterator(
+ const char *query)
+{
+ return new linear_search_item_iterator(this, query);
+}
+
+linear_search_item_iterator::linear_search_item_iterator(
+ linear_search_item *p, const char *)
+: lsi(p), pos(0)
+{
+}
+
+linear_search_item_iterator::~linear_search_item_iterator()
+{
+}
+
+int linear_search_item_iterator::next(const linear_searcher &searcher,
+ const char **startp, int *lengthp,
+ reference_id *ridp)
+{
+ const char *bufstart = lsi->fbuf.get_start();
+ const char *bufend = lsi->fbuf.get_end();
+ const char *ptr = bufstart + pos;
+ if (ptr < bufend && searcher.search(ptr, bufend, startp, lengthp)) {
+ pos = *startp + *lengthp - bufstart;
+ if (ridp)
+ *ridp = reference_id(lsi->filename_id, *startp - bufstart);
+ return 1;
+ }
+ else
+ return 0;
+}
diff --git a/contrib/groff/src/libs/libbib/search.cpp b/contrib/groff/src/libs/libbib/search.cpp
new file mode 100644
index 0000000..2223fb6
--- /dev/null
+++ b/contrib/groff/src/libs/libbib/search.cpp
@@ -0,0 +1,133 @@
+// -*- C++ -*-
+/* Copyright (C) 1989, 1990, 1991, 1992, 2000, 2001
+ Free Software Foundation, Inc.
+ Written by James Clark (jjc@jclark.com)
+
+This file is part of groff.
+
+groff is free software; you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free
+Software Foundation; either version 2, or (at your option) any later
+version.
+
+groff is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+for more details.
+
+You should have received a copy of the GNU General Public License along
+with groff; see the file COPYING. If not, write to the Free Software
+Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
+
+#include "lib.h"
+
+#include <stdlib.h>
+#include <assert.h>
+#include <errno.h>
+
+#include "posix.h"
+#include "errarg.h"
+#include "error.h"
+#include "nonposix.h"
+
+#include "refid.h"
+#include "search.h"
+
+int linear_truncate_len = 6;
+const char *linear_ignore_fields = "XYZ";
+
+search_list::search_list()
+: list(0), niterators(0), next_fid(1)
+{
+}
+
+search_list::~search_list()
+{
+ assert(niterators == 0);
+ while (list) {
+ search_item *tem = list->next;
+ delete list;
+ list = tem;
+ }
+}
+
+void search_list::add_file(const char *filename, int silent)
+{
+ search_item *p = make_index_search_item(filename, next_fid);
+ if (!p) {
+ int fd = open(filename, O_RDONLY | O_BINARY);
+ if (fd < 0) {
+ if (!silent)
+ error("can't open `%1': %2", filename, strerror(errno));
+ }
+ else
+ p = make_linear_search_item(fd, filename, next_fid);
+ }
+ if (p) {
+ search_item **pp;
+ for (pp = &list; *pp; pp = &(*pp)->next)
+ ;
+ *pp = p;
+ next_fid = p->next_filename_id();
+ }
+}
+
+int search_list::nfiles() const
+{
+ int n = 0;
+ for (search_item *ptr = list; ptr; ptr = ptr->next)
+ n++;
+ return n;
+}
+
+search_list_iterator::search_list_iterator(search_list *p, const char *q)
+: list(p), ptr(p->list), iter(0), query(strsave(q)),
+ searcher(q, strlen(q), linear_ignore_fields, linear_truncate_len)
+{
+ list->niterators += 1;
+}
+
+search_list_iterator::~search_list_iterator()
+{
+ list->niterators -= 1;
+ a_delete query;
+ delete iter;
+}
+
+int search_list_iterator::next(const char **pp, int *lenp, reference_id *ridp)
+{
+ while (ptr) {
+ if (iter == 0)
+ iter = ptr->make_search_item_iterator(query);
+ if (iter->next(searcher, pp, lenp, ridp))
+ return 1;
+ delete iter;
+ iter = 0;
+ ptr = ptr->next;
+ }
+ return 0;
+}
+
+search_item::search_item(const char *nm, int fid)
+: name(strsave(nm)), filename_id(fid), next(0)
+{
+}
+
+search_item::~search_item()
+{
+ a_delete name;
+}
+
+int search_item::is_named(const char *nm) const
+{
+ return strcmp(name, nm) == 0;
+}
+
+int search_item::next_filename_id() const
+{
+ return filename_id + 1;
+}
+
+search_item_iterator::~search_item_iterator()
+{
+}
diff --git a/contrib/groff/src/libs/libdriver/Makefile.sub b/contrib/groff/src/libs/libdriver/Makefile.sub
index 547b8c0..5d37e2a 100644
--- a/contrib/groff/src/libs/libdriver/Makefile.sub
+++ b/contrib/groff/src/libs/libdriver/Makefile.sub
@@ -3,5 +3,5 @@ OBJS=\
input.$(OBJEXT) \
printer.$(OBJEXT)
CCSRCS=\
- $(srcdir)/input.cc \
- $(srcdir)/printer.cc
+ $(srcdir)/input.cpp \
+ $(srcdir)/printer.cpp
diff --git a/contrib/groff/src/libs/libdriver/input.cpp b/contrib/groff/src/libs/libdriver/input.cpp
new file mode 100644
index 0000000..acb6f13
--- /dev/null
+++ b/contrib/groff/src/libs/libdriver/input.cpp
@@ -0,0 +1,1831 @@
+// -*- C++ -*-
+
+// <groff_src_dir>/src/libs/libdriver/input.cpp
+
+/* Copyright (C) 1989, 1990, 1991, 1992, 2001, 2002, 2003
+ Free Software Foundation, Inc.
+
+ Written by James Clark (jjc@jclark.com)
+ Major rewrite 2001 by Bernd Warken (bwarken@mayn.de)
+
+ Last update: 04 Apr 2003
+
+ This file is part of groff, the GNU roff text processing system.
+
+ groff is free software; you can redistribute it and/or modify it
+ under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ groff is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with groff; see the file COPYING. If not, write to the Free
+ Software Foundation, 59 Temple Place - Suite 330, Boston, MA
+ 02111-1307, USA.
+*/
+
+/* Description
+
+ This file implements the parser for the intermediate groff output,
+ see groff_out(5), and does the printout for the given device.
+
+ All parsed information is processed within the function do_file().
+ A device postprocessor just needs to fill in the methods for the class
+ `printer' (or rather a derived class) without having to worry about
+ the syntax of the intermediate output format. Consequently, the
+ programming of groff postprocessors is similar to the development of
+ device drivers.
+
+ The prototyping for this file is done in driver.h (and error.h).
+*/
+
+/* Changes of the 2001 rewrite of this file.
+
+ The interface to the outside and the handling of the global
+ variables was not changed, but internally many necessary changes
+ were performed.
+
+ The main aim for this rewrite is to provide a first step towards
+ making groff fully compatible with classical troff without pain.
+
+ Bugs fixed
+ - Unknown subcommands of `D' and `x' are now ignored like in the
+ classical case, but a warning is issued. This was also
+ implemented for the other commands.
+ - A warning is emitted if `x stop' is missing.
+ - `DC' and `DE' commands didn't position to the right end after
+ drawing (now they do), see discussion below.
+ - So far, `x stop' was ignored. Now it terminates the processing
+ of the current intermediate output file like the classical troff.
+ - The command `c' didn't check correctly on white-space.
+ - The environment stack wasn't suitable for the color extensions
+ (replaced by a class).
+ - The old groff parser could only handle a prologue with the first
+ 3 lines having a fixed structure, while classical troff specified
+ the sequence of the first 3 commands without further
+ restrictions. Now the parser is smart about additional
+ white space, comments, and empty lines in the prologue.
+ - The old parser allowed space characters only as syntactical
+ separators, while classical troff had tab characters as well.
+ Now any sequence of tabs and/or spaces is a syntactical
+ separator between commands and/or arguments.
+ - Range checks for numbers implemented.
+
+ New and improved features
+ - The color commands `m' and `DF' are added.
+ - The old color command `Df' is now converted and delegated to `DFg'.
+ - The command `F' is implemented as `use intended file name'. It
+ checks whether its argument agrees with the file name used so far,
+ otherwise a warning is issued. Then the new name is remembered
+ and used for the following error messages.
+ - For the positioning after drawing commands, an alternative, easier
+ scheme is provided, but not yet activated; it can be chosen by
+ undefining the preprocessor macro STUPID_DRAWING_POSITIONING.
+ It extends the rule of the classical troff output language in a
+ logical way instead of the rather strange actual positioning.
+ For details, see the discussion below.
+ - For the `D' commands that only set the environment, the calling of
+ pr->send_draw() was removed because this doesn't make sense for
+ the `DF' commands; the (changed) environment is sent with the
+ next command anyway.
+ - Error handling was clearly separated into warnings and fatal.
+ - The error behavior on additional arguments for `D' and `x'
+ commands with a fixed number of arguments was changed from being
+ ignored (former groff) to issue a warning and ignore (now), see
+ skip_line_x(). No fatal was chosen because both string and
+ integer arguments can occur.
+ - The gtroff program issues a trailing dummy integer argument for
+ some drawing commands with an odd number of arguments to make the
+ number of arguments even, e.g. the DC and Dt commands; this is
+ honored now.
+ - All D commands with a variable number of args expect an even
+ number of trailing integer arguments, so fatal on error was
+ implemented.
+ - Disable environment stack and the commands `{' and `}' by making
+ them conditional on macro USE_ENV_STACK; actually, this is
+ undefined by default. There isn't any known application for these
+ features.
+
+ Cosmetics
+ - Nested `switch' commands are avoided by using more functions.
+ Dangerous 'fall-through's avoided.
+ - Commands and functions are sorted alphabetically (where possible).
+ - Dynamic arrays/buffers are now implemented as container classes.
+ - Some functions had an ugly return structure; this has been
+ streamlined by using classes.
+ - Use standard C math functions for number handling, so getting rid
+ of differences to '0'.
+ - The macro `IntArg' has been created for an easier transition
+ to guaranteed 32 bits integers (`int' is enough for GNU, while
+ ANSI only guarantees `long int' to have a length of 32 bits).
+ - The many usages of type `int' are differentiated by using `Char',
+ `bool', and `IntArg' where appropriate.
+ - To ease the calls of the local utility functions, the parser
+ variables `current_file', `npages', and `current_env'
+ (formerly env) were made global to the file (formerly they were
+ local to the do_file() function)
+ - Various comments were added.
+
+ TODO
+ - Get rid of the stupid drawing positioning.
+ - Can the `Dt' command be completely handled by setting environment
+ within do_file() instead of sending to pr?
+ - Integer arguments must be >= 32 bits, use conditional #define.
+ - Add scaling facility for classical device independence and
+ non-groff devices. Classical troff output had a quasi device
+ independence by scaling the intermediate output to the resolution
+ of the postprocessor device if different from the one specified
+ with `x T', groff have not. So implement full quasi device
+ indepedence, including the mapping of the strange classical
+ devices to the postprocessor device (seems to be reasonably
+ easy).
+ - The external, global pointer variables are not optimally handled.
+ - The global variables `current_filename',
+ `current_source_filename', and `current_lineno' are only used for
+ error reporting. So implement a static class `Error'
+ (`::' calls).
+ - The global `device' is the name used during the formatting
+ process; there should be a new variable for the device name used
+ during the postprocessing.
+ - Implement the B-spline drawing `D~' for all graphical devices.
+ - Make `environment' a class with an overflow check for its members
+ and a delete method to get rid of delete_current_env().
+ - Implement the `EnvStack' to use `new' instead of `malloc'.
+ - The class definitions of this document could go into a new file.
+ - The comments in this section should go to a `Changelog' or some
+ `README' file in this directory.
+*/
+
+/*
+ Discussion of the positioning by drawing commands
+
+ There was some confusion about the positioning of the graphical
+ pointer at the printout after having executed a `D' command.
+ The classical troff manual of Osanna & Kernighan specified,
+
+ `The position after a graphical object has been drawn is
+ at its end; for circles and ellipses, the "end" is at the
+ right side.'
+
+ From this, it follows that
+ - all open figures (args, splines, and lines) should position at their
+ final point.
+ - all circles and ellipses should position at their right-most point
+ (as if 2 halves had been drawn).
+ - all closed figures apart from circles and ellipses shouldn't change
+ the position because they return to their origin.
+ - all setting commands should not change position because they do not
+ draw any graphical object.
+
+ In the case of the open figures, this means that the horizontal
+ displacement is the sum of all odd arguments and the vertical offset
+ the sum of all even arguments, called the alternate arguments sum
+ displacement in the following.
+
+ Unfortunately, groff did not implement this simple rule. The former
+ documentation in groff_out(5) differed from the source code, and
+ neither of them is compatible with the classical rule.
+
+ The former groff_out(5) specified to use the alternative arguments
+ sum displacement for calculating the drawing positioning of
+ non-classical commands, including the `Dt' command (setting-only)
+ and closed polygons. Applying this to the new groff color commands
+ will lead to disaster. For their arguments can take large values (>
+ 65000). On low resolution devices, the displacement of such large
+ values will corrupt the display or kill the printer. So the
+ nonsense specification has come to a natural end anyway.
+
+ The groff source code, however, had no positioning for the
+ setting-only commands (esp. `Dt'), the right-end positioning for
+ outlined circles and ellipses, and the alternative argument sum
+ displacement for all other commands (including filled circles and
+ ellipses).
+
+ The reason why no one seems to have suffered from this mayhem so
+ far is that the graphical objects are usually generated by
+ preprocessors like pic that do not depend on the automatic
+ positioning. When using the low level `\D' escape sequences or `D'
+ output commands, the strange positionings can be circumvented by
+ absolute positionings or by tricks like `\Z'.
+
+ So doing an exorcism on the strange, incompatible displacements might
+ not harm any existing documents, but will make the usage of the
+ graphical escape sequences and commands natural.
+
+ That's why the rewrite of this file returned to the reasonable,
+ classical specification with its clear end-of-drawing rule that is
+ suitable for all cases. But a macro STUPID_DRAWING_POSITIONING is
+ provided for testing the funny former behavior.
+
+ The new rule implies the following behavior.
+ - Setting commands (`Dt', `Df', `DF') and polygons (`Dp' and `DP')
+ do not change position now.
+ - Filled circles and ellipses (`DC' and `DE') position at their
+ most right point (outlined ones `Dc' and `De' did this anyway).
+ - As before, all open graphical objects position to their final
+ drawing point (alternate sum of the command arguments).
+
+*/
+
+#ifndef STUPID_DRAWING_POSITIONING
+// uncomment next line if all non-classical D commands shall position
+// to the strange alternate sum of args displacement
+#define STUPID_DRAWING_POSITIONING
+#endif
+
+// Decide whether the commands `{' and `}' for different environments
+// should be used.
+#undef USE_ENV_STACK
+
+#include "driver.h"
+#include "device.h"
+
+#include <stdlib.h>
+#include <errno.h>
+#include <ctype.h>
+#include <math.h>
+
+
+/**********************************************************************
+ local types
+ **********************************************************************/
+
+// integer type used in the fields of struct environment (see printer.h)
+typedef int EnvInt;
+
+// integer arguments of groff_out commands, must be >= 32 bits
+typedef int IntArg;
+
+// color components of groff_out color commands, must be >= 32 bits
+typedef unsigned int ColorArg;
+
+// Array for IntArg values.
+class IntArray {
+ size_t num_allocated;
+ size_t num_stored;
+ IntArg *data;
+public:
+ IntArray(void);
+ IntArray(const size_t);
+ ~IntArray(void);
+ const IntArg operator[](const size_t i) const
+ {
+ if (i >= num_stored)
+ fatal("index out of range");
+ return (IntArg) data[i];
+ }
+ void append(IntArg);
+ const IntArg * const
+ get_data(void) const { return (IntArg *) data; }
+ const size_t len(void) const { return num_stored; }
+};
+
+// Characters read from the input queue.
+class Char {
+ int data;
+public:
+ Char(void) : data('\0') {}
+ Char(const int c) : data(c) {}
+ bool operator==(char c) const { return (data == c) ? true : false; }
+ bool operator==(int c) const { return (data == c) ? true : false; }
+ bool operator==(const Char c) const
+ { return (data == c.data) ? true : false; }
+ bool operator!=(char c) const { return !(*this == c); }
+ bool operator!=(int c) const { return !(*this == c); }
+ bool operator!=(const Char c) const { return !(*this == c); }
+ operator int() const { return (int) data; }
+ operator unsigned char() const { return (unsigned char) data; }
+ operator char() const { return (char) data; }
+};
+
+// Buffer for string arguments (Char, not char).
+class StringBuf {
+ size_t num_allocated;
+ size_t num_stored;
+ Char *data; // not terminated by '\0'
+public:
+ StringBuf(void); // allocate without storing
+ ~StringBuf(void);
+ void append(const Char); // append character to `data'
+ char *make_string(void); // return new copy of `data' with '\0'
+ bool is_empty(void) { // true if none stored
+ return (num_stored > 0) ? false : true;
+ }
+ void reset(void); // set `num_stored' to 0
+};
+
+#ifdef USE_ENV_STACK
+class EnvStack {
+ environment **data;
+ size_t num_allocated;
+ size_t num_stored;
+public:
+ EnvStack(void);
+ ~EnvStack(void);
+ environment *pop(void);
+ void push(environment *e);
+};
+#endif // USE_ENV_STACK
+
+
+/**********************************************************************
+ external variables
+ **********************************************************************/
+
+// exported as extern by error.h (called from driver.h)
+// needed for error messages (see ../libgroff/error.cpp)
+const char *current_filename = 0; // printable name of the current file
+ // printable name of current source file
+const char *current_source_filename = 0;
+int current_lineno = 0; // current line number of printout
+
+// exported as extern by device.h;
+const char *device = 0; // cancel former init with literal
+
+printer *pr;
+
+// Note:
+//
+// We rely on an implementation of the `new' operator which aborts
+// gracefully if it can't allocate memory (e.g. from libgroff/new.cpp).
+
+
+/**********************************************************************
+ static local variables
+ **********************************************************************/
+
+FILE *current_file = 0; // current input stream for parser
+
+// npages: number of pages processed so far (including current page),
+// _not_ the page number in the printout (can be set with `p').
+int npages = 0;
+
+const ColorArg
+COLORARG_MAX = (ColorArg) 65536U; // == 0xFFFF + 1 == 0x10000
+
+const IntArg
+INTARG_MAX = (IntArg) 0x7FFFFFFF; // maximal signed 32 bits number
+
+// parser environment, created and deleted by each run of do_file()
+environment *current_env = 0;
+
+#ifdef USE_ENV_STACK
+const size_t
+envp_size = sizeof(environment *);
+#endif // USE_ENV_STACK
+
+
+/**********************************************************************
+ function declarations
+ **********************************************************************/
+
+// utility functions
+ColorArg color_from_Df_command(IntArg);
+ // transform old color into new
+void delete_current_env(void); // delete global var current_env
+void fatal_command(char); // abort for invalid command
+inline Char get_char(void); // read next character from input stream
+ColorArg get_color_arg(void); // read in argument for new color cmds
+IntArray *get_D_fixed_args(const size_t);
+ // read in fixed number of integer
+ // arguments
+IntArray *get_D_fixed_args_odd_dummy(const size_t);
+ // read in a fixed number of integer
+ // arguments plus optional dummy
+IntArray *get_D_variable_args(void);
+ // variable, even number of int args
+char *get_extended_arg(void); // argument for `x X' (several lines)
+IntArg get_integer_arg(void); // read in next integer argument
+IntArray *get_possibly_integer_args();
+ // 0 or more integer arguments
+char *get_string_arg(void); // read in next string arg, ended by WS
+inline bool is_space_or_tab(const Char);
+ // test on space/tab char
+Char next_arg_begin(void); // skip white space on current line
+Char next_command(void); // go to next command, evt. diff. line
+inline bool odd(const int); // test if integer is odd
+void position_to_end_of_args(const IntArray * const);
+ // positioning after drawing
+void remember_filename(const char *);
+ // set global current_filename
+void remember_source_filename(const char *);
+ // set global current_source_filename
+void send_draw(const Char, const IntArray * const);
+ // call pr->draw
+void skip_line(void); // unconditionally skip to next line
+bool skip_line_checked(void); // skip line, false if args are left
+void skip_line_fatal(void); // skip line, fatal if args are left
+void skip_line_warn(void); // skip line, warn if args are left
+void skip_line_D(void); // skip line in D commands
+void skip_line_x(void); // skip line in x commands
+void skip_to_end_of_line(void); // skip to the end of the current line
+inline void unget_char(const Char);
+ // restore character onto input
+
+// parser subcommands
+void parse_color_command(color *);
+ // color sub(sub)commands m and DF
+void parse_D_command(void); // graphical subcommands
+bool parse_x_command(void); // device controller subcommands
+
+
+/**********************************************************************
+ class methods
+ **********************************************************************/
+
+#ifdef USE_ENV_STACK
+EnvStack::EnvStack(void)
+{
+ num_allocated = 4;
+ // allocate pointer to array of num_allocated pointers to environment
+ data = (environment **) malloc(envp_size * num_allocated);
+ if (data == 0)
+ fatal("could not allocate environment data");
+ num_stored = 0;
+}
+
+EnvStack::~EnvStack(void)
+{
+ for (size_t i = 0; i < num_stored; i++)
+ delete data[i];
+ free(data);
+}
+
+// return top element from stack and decrease stack pointer
+//
+// the calling function must take care of properly deleting the result
+environment *
+EnvStack::pop(void)
+{
+ num_stored--;
+ environment *result = data[num_stored];
+ data[num_stored] = 0;
+ return result;
+}
+
+// copy argument and push this onto the stack
+void
+EnvStack::push(environment *e)
+{
+ environment *e_copy = new environment;
+ if (num_stored >= num_allocated) {
+ environment **old_data = data;
+ num_allocated *= 2;
+ data = (environment **) malloc(envp_size * num_allocated);
+ if (data == 0)
+ fatal("could not allocate data");
+ for (size_t i = 0; i < num_stored; i++)
+ data[i] = old_data[i];
+ free(old_data);
+ }
+ e_copy->col = new color;
+ e_copy->fill = new color;
+ *e_copy->col = *e->col;
+ *e_copy->fill = *e->fill;
+ e_copy->fontno = e->fontno;
+ e_copy->height = e->height;
+ e_copy->hpos = e->hpos;
+ e_copy->size = e->size;
+ e_copy->slant = e->slant;
+ e_copy->vpos = e->vpos;
+ data[num_stored] = e_copy;
+ num_stored++;
+}
+#endif // USE_ENV_STACK
+
+IntArray::IntArray(void)
+{
+ num_allocated = 4;
+ data = new IntArg[num_allocated];
+ num_stored = 0;
+}
+
+IntArray::IntArray(const size_t n)
+{
+ if (n <= 0)
+ fatal("number of integers to be allocated must be > 0");
+ num_allocated = n;
+ data = new IntArg[num_allocated];
+ num_stored = 0;
+}
+
+IntArray::~IntArray(void)
+{
+ a_delete data;
+}
+
+void
+IntArray::append(IntArg x)
+{
+ if (num_stored >= num_allocated) {
+ IntArg *old_data = data;
+ num_allocated *= 2;
+ data = new IntArg[num_allocated];
+ for (size_t i = 0; i < num_stored; i++)
+ data[i] = old_data[i];
+ a_delete old_data;
+ }
+ data[num_stored] = x;
+ num_stored++;
+}
+
+StringBuf::StringBuf(void)
+{
+ num_stored = 0;
+ num_allocated = 128;
+ data = new Char[num_allocated];
+}
+
+StringBuf::~StringBuf(void)
+{
+ a_delete data;
+}
+
+void
+StringBuf::append(const Char c)
+{
+ if (num_stored >= num_allocated) {
+ Char *old_data = data;
+ num_allocated *= 2;
+ data = new Char[num_allocated];
+ for (size_t i = 0; i < num_stored; i++)
+ data[i] = old_data[i];
+ a_delete old_data;
+ }
+ data[num_stored] = c;
+ num_stored++;
+}
+
+char *
+StringBuf::make_string(void)
+{
+ char *result = new char[num_stored + 1];
+ for (size_t i = 0; i < num_stored; i++)
+ result[i] = (char) data[i];
+ result[num_stored] = '\0';
+ return result;
+}
+
+void
+StringBuf::reset(void)
+{
+ num_stored = 0;
+}
+
+/**********************************************************************
+ utility functions
+ **********************************************************************/
+
+//////////////////////////////////////////////////////////////////////
+/* color_from_Df_command:
+ Process the gray shade setting command Df.
+
+ Transform Df style color into DF style color.
+ Df color: 0-1000, 0 is white
+ DF color: 0-65536, 0 is black
+
+ The Df command is obsoleted by command DFg, but kept for
+ compatibility.
+*/
+ColorArg
+color_from_Df_command(IntArg Df_gray)
+{
+ return ColorArg((1000-Df_gray) * COLORARG_MAX / 1000); // scaling
+}
+
+//////////////////////////////////////////////////////////////////////
+/* delete_current_env():
+ Delete global variable current_env and its pointer members.
+
+ This should be a class method of environment.
+*/
+void delete_current_env(void)
+{
+ delete current_env->col;
+ delete current_env->fill;
+ delete current_env;
+}
+
+//////////////////////////////////////////////////////////////////////
+/* fatal_command():
+ Emit error message about invalid command and abort.
+*/
+void
+fatal_command(char command)
+{
+ fatal("`%1' command invalid before first `p' command", command);
+}
+
+//////////////////////////////////////////////////////////////////////
+/* get_char():
+ Retrieve the next character from the input queue.
+
+ Return: The retrieved character (incl. EOF), converted to Char.
+*/
+inline Char
+get_char(void)
+{
+ return (Char) getc(current_file);
+}
+
+//////////////////////////////////////////////////////////////////////
+/* get_color_arg():
+ Retrieve an argument suitable for the color commands m and DF.
+
+ Return: The retrieved color argument.
+*/
+ColorArg
+get_color_arg(void)
+{
+ IntArg x = get_integer_arg();
+ if (x < 0 || x > (IntArg)COLORARG_MAX) {
+ error("color component argument out of range");
+ x = 0;
+ }
+ return (ColorArg) x;
+}
+
+//////////////////////////////////////////////////////////////////////
+/* get_D_fixed_args():
+ Get a fixed number of integer arguments for D commands.
+
+ Fatal if wrong number of arguments.
+ Too many arguments on the line raise a warning.
+ A line skip is done.
+
+ number: In-parameter, the number of arguments to be retrieved.
+ ignore: In-parameter, ignore next argument -- GNU troff always emits
+ pairs of parameters for `D' extensions added by groff.
+ Default is `false'.
+
+ Return: New IntArray containing the arguments.
+*/
+IntArray *
+get_D_fixed_args(const size_t number)
+{
+ if (number <= 0)
+ fatal("requested number of arguments must be > 0");
+ IntArray *args = new IntArray(number);
+ for (size_t i = 0; i < number; i++)
+ args->append(get_integer_arg());
+ skip_line_D();
+ return args;
+}
+
+//////////////////////////////////////////////////////////////////////
+/* get_D_fixed_args_odd_dummy():
+ Get a fixed number of integer arguments for D commands and optionally
+ ignore a dummy integer argument if the requested number is odd.
+
+ The gtroff program adds a dummy argument to some commands to get
+ an even number of arguments.
+ Error if the number of arguments differs from the scheme above.
+ A line skip is done.
+
+ number: In-parameter, the number of arguments to be retrieved.
+
+ Return: New IntArray containing the arguments.
+*/
+IntArray *
+get_D_fixed_args_odd_dummy(const size_t number)
+{
+ if (number <= 0)
+ fatal("requested number of arguments must be > 0");
+ IntArray *args = new IntArray(number);
+ for (size_t i = 0; i < number; i++)
+ args->append(get_integer_arg());
+ if (odd(number)) {
+ IntArray *a = get_possibly_integer_args();
+ if (a->len() > 1)
+ error("too many arguments");
+ delete a;
+ }
+ skip_line_D();
+ return args;
+}
+
+//////////////////////////////////////////////////////////////////////
+/* get_D_variable_args():
+ Get a variable even number of integer arguments for D commands.
+
+ Get as many integer arguments as possible from the rest of the
+ current line.
+ - The arguments are separated by an arbitrary sequence of space or
+ tab characters.
+ - A comment, a newline, or EOF indicates the end of processing.
+ - Error on non-digit characters different from these.
+ - A final line skip is performed (except for EOF).
+
+ Return: New IntArray of the retrieved arguments.
+*/
+IntArray *
+get_D_variable_args()
+{
+ IntArray *args = get_possibly_integer_args();
+ size_t n = args->len();
+ if (n <= 0)
+ error("no arguments found");
+ if (odd(n))
+ error("even number of arguments expected");
+ skip_line_D();
+ return args;
+}
+
+//////////////////////////////////////////////////////////////////////
+/* get_extended_arg():
+ Retrieve extended arg for `x X' command.
+
+ - Skip leading spaces and tabs, error on EOL or newline.
+ - Return everything before the next NL or EOF ('#' is not a comment);
+ as long as the following line starts with '+' this is returned
+ as well, with the '+' replaced by a newline.
+ - Final line skip is always performed.
+
+ Return: Allocated (new) string of retrieved text argument.
+*/
+char *
+get_extended_arg(void)
+{
+ StringBuf buf = StringBuf();
+ Char c = next_arg_begin();
+ while ((int) c != EOF) {
+ if ((int) c == '\n') {
+ current_lineno++;
+ c = get_char();
+ if ((int) c == '+')
+ buf.append((Char) '\n');
+ else {
+ unget_char(c); // first character of next line
+ break;
+ }
+ }
+ else
+ buf.append(c);
+ c = get_char();
+ }
+ return buf.make_string();
+}
+
+//////////////////////////////////////////////////////////////////////
+/* get_integer_arg(): Retrieve integer argument.
+
+ Skip leading spaces and tabs, collect an optional '-' and all
+ following decimal digits (at least one) up to the next non-digit,
+ which is restored onto the input queue.
+
+ Fatal error on all other situations.
+
+ Return: Retrieved integer.
+*/
+IntArg
+get_integer_arg(void)
+{
+ StringBuf buf = StringBuf();
+ Char c = next_arg_begin();
+ if ((int) c == '-') {
+ buf.append(c);
+ c = get_char();
+ }
+ if (!isdigit((int) c))
+ error("integer argument expected");
+ while (isdigit((int) c)) {
+ buf.append(c);
+ c = get_char();
+ }
+ // c is not a digit
+ unget_char(c);
+ char *s = buf.make_string();
+ errno = 0;
+ long int number = strtol(s, 0, 10);
+ if (errno != 0
+ || number > INTARG_MAX || number < -INTARG_MAX) {
+ error("integer argument too large");
+ number = 0;
+ }
+ a_delete s;
+ return (IntArg) number;
+}
+
+//////////////////////////////////////////////////////////////////////
+/* get_possibly_integer_args():
+ Parse the rest of the input line as a list of integer arguments.
+
+ Get as many integer arguments as possible from the rest of the
+ current line, even none.
+ - The arguments are separated by an arbitrary sequence of space or
+ tab characters.
+ - A comment, a newline, or EOF indicates the end of processing.
+ - Error on non-digit characters different from these.
+ - No line skip is performed.
+
+ Return: New IntArray of the retrieved arguments.
+*/
+IntArray *
+get_possibly_integer_args()
+{
+ bool done = false;
+ StringBuf buf = StringBuf();
+ Char c = get_char();
+ IntArray *args = new IntArray();
+ while (!done) {
+ buf.reset();
+ while (is_space_or_tab(c))
+ c = get_char();
+ if (c == '-') {
+ Char c1 = get_char();
+ if (isdigit((int) c1)) {
+ buf.append(c);
+ c = c1;
+ }
+ else
+ unget_char(c1);
+ }
+ while (isdigit((int) c)) {
+ buf.append(c);
+ c = get_char();
+ }
+ if (!buf.is_empty()) {
+ char *s = buf.make_string();
+ errno = 0;
+ long int x = strtol(s, 0, 10);
+ if (errno
+ || x > INTARG_MAX || x < -INTARG_MAX) {
+ error("invalid integer argument, set to 0");
+ x = 0;
+ }
+ args->append((IntArg) x);
+ a_delete s;
+ }
+ // Here, c is not a digit.
+ // Terminate on comment, end of line, or end of file, while
+ // space or tab indicate continuation; otherwise error.
+ switch((int) c) {
+ case '#':
+ skip_to_end_of_line();
+ done = true;
+ break;
+ case '\n':
+ done = true;
+ unget_char(c);
+ break;
+ case EOF:
+ done = true;
+ break;
+ case ' ':
+ case '\t':
+ break;
+ default:
+ error("integer argument expected");
+ break;
+ }
+ }
+ return args;
+}
+
+//////////////////////////////////////////////////////////////////////
+/* get_string_arg():
+ Retrieve string arg.
+
+ - Skip leading spaces and tabs; error on EOL or newline.
+ - Return all following characters before the next space, tab,
+ newline, or EOF character (in-word '#' is not a comment character).
+ - The terminating space, tab, newline, or EOF character is restored
+ onto the input queue, so no line skip.
+
+ Return: Retrieved string as char *, allocated by 'new'.
+*/
+char *
+get_string_arg(void)
+{
+ StringBuf buf = StringBuf();
+ Char c = next_arg_begin();
+ while (!is_space_or_tab(c)
+ && c != Char('\n') && c != Char(EOF)) {
+ buf.append(c);
+ c = get_char();
+ }
+ unget_char(c); // restore white space
+ return buf.make_string();
+}
+
+//////////////////////////////////////////////////////////////////////
+/* is_space_or_tab():
+ Test a character if it is a space or tab.
+
+ c: In-parameter, character to be tested.
+
+ Return: True, if c is a space or tab character, false otherwise.
+*/
+inline bool
+is_space_or_tab(const Char c)
+{
+ return (c == Char(' ') || c == Char('\t')) ? true : false;
+}
+
+//////////////////////////////////////////////////////////////////////
+/* next_arg_begin():
+ Return first character of next argument.
+
+ Skip space and tab characters; error on newline or EOF.
+
+ Return: The first character different from these (including '#').
+*/
+Char
+next_arg_begin(void)
+{
+ Char c;
+ while (1) {
+ c = get_char();
+ switch ((int) c) {
+ case ' ':
+ case '\t':
+ break;
+ case '\n':
+ case EOF:
+ error("missing argument");
+ break;
+ default: // first essential character
+ return c;
+ }
+ }
+}
+
+//////////////////////////////////////////////////////////////////////
+/* next_command():
+ Find the first character of the next command.
+
+ Skip spaces, tabs, comments (introduced by #), and newlines.
+
+ Return: The first character different from these (including EOF).
+*/
+Char
+next_command(void)
+{
+ Char c;
+ while (1) {
+ c = get_char();
+ switch ((int) c) {
+ case ' ':
+ case '\t':
+ break;
+ case '\n':
+ current_lineno++;
+ break;
+ case '#': // comment
+ skip_line();
+ break;
+ default: // EOF or first essential character
+ return c;
+ }
+ }
+}
+
+//////////////////////////////////////////////////////////////////////
+/* odd():
+ Test whether argument is an odd number.
+
+ n: In-parameter, the integer to be tested.
+
+ Return: True if odd, false otherwise.
+*/
+inline bool
+odd(const int n)
+{
+ return (n & 1 == 1) ? true : false;
+}
+
+//////////////////////////////////////////////////////////////////////
+/* position_to_end_of_args():
+ Move graphical pointer to end of drawn figure.
+
+ This is used by the D commands that draw open geometrical figures.
+ The algorithm simply sums up all horizontal displacements (arguments
+ with even number) for the horizontal component. Similarly, the
+ vertical component is the sum of the odd arguments.
+
+ args: In-parameter, the arguments of a former drawing command.
+*/
+void
+position_to_end_of_args(const IntArray * const args)
+{
+ size_t i;
+ const size_t n = args->len();
+ for (i = 0; i < n; i += 2)
+ current_env->hpos += (*args)[i];
+ for (i = 1; i < n; i += 2)
+ current_env->vpos += (*args)[i];
+}
+
+//////////////////////////////////////////////////////////////////////
+/* remember_filename():
+ Set global variable current_filename.
+
+ The actual filename is stored in current_filename. This is used by
+ the postprocessors, expecting the name "<standard input>" for stdin.
+
+ filename: In-out-parameter; is changed to the new value also.
+*/
+void
+remember_filename(const char *filename)
+{
+ char *fname;
+ if (strcmp(filename, "-") == 0)
+ fname = "<standard input>";
+ else
+ fname = (char *) filename;
+ size_t len = strlen(fname) + 1;
+ if (current_filename != 0)
+ free((char *)current_filename);
+ current_filename = (const char *) malloc(len);
+ if (current_filename == 0)
+ fatal("can't malloc space for filename");
+ strncpy((char *)current_filename, (char *)fname, len);
+}
+
+//////////////////////////////////////////////////////////////////////
+/* remember_source_filename():
+ Set global variable current_source_filename.
+
+ The actual filename is stored in current_filename. This is used by
+ the postprocessors, expecting the name "<standard input>" for stdin.
+
+ filename: In-out-parameter; is changed to the new value also.
+*/
+void
+remember_source_filename(const char *filename)
+{
+ char *fname;
+ if (strcmp(filename, "-") == 0)
+ fname = "<standard input>";
+ else
+ fname = (char *) filename;
+ size_t len = strlen(fname) + 1;
+ if (current_source_filename != 0)
+ free((char *)current_source_filename);
+ current_source_filename = (const char *) malloc(len);
+ if (current_source_filename == 0)
+ fatal("can't malloc space for filename");
+ strncpy((char *)current_source_filename, (char *)fname, len);
+}
+
+//////////////////////////////////////////////////////////////////////
+/* send_draw():
+ Call draw method of printer class.
+
+ subcmd: Letter of actual D subcommand.
+ args: Array of integer arguments of actual D subcommand.
+*/
+void
+send_draw(const Char subcmd, const IntArray * const args)
+{
+ EnvInt n = (EnvInt) args->len();
+ pr->draw((int) subcmd, (IntArg *) args->get_data(), n, current_env);
+}
+
+//////////////////////////////////////////////////////////////////////
+/* skip_line():
+ Go to next line within the input queue.
+
+ Skip the rest of the current line, including the newline character.
+ The global variable current_lineno is adjusted.
+ No errors are raised.
+*/
+void
+skip_line(void)
+{
+ Char c = get_char();
+ while (1) {
+ if (c == '\n') {
+ current_lineno++;
+ break;
+ }
+ if (c == EOF)
+ break;
+ c = get_char();
+ }
+}
+
+//////////////////////////////////////////////////////////////////////
+/* skip_line_checked ():
+ Check that there aren't any arguments left on the rest of the line,
+ then skip line.
+
+ Spaces, tabs, and a comment are allowed before newline or EOF.
+ All other characters raise an error.
+*/
+bool
+skip_line_checked(void)
+{
+ bool ok = true;
+ Char c = get_char();
+ while (is_space_or_tab(c))
+ c = get_char();
+ switch((int) c) {
+ case '#': // comment
+ skip_line();
+ break;
+ case '\n':
+ current_lineno++;
+ break;
+ case EOF:
+ break;
+ default:
+ ok = false;
+ skip_line();
+ break;
+ }
+ return ok;
+}
+
+//////////////////////////////////////////////////////////////////////
+/* skip_line_fatal ():
+ Fatal error if arguments left, otherwise skip line.
+
+ Spaces, tabs, and a comment are allowed before newline or EOF.
+ All other characters trigger the error.
+*/
+void
+skip_line_fatal(void)
+{
+ bool ok = skip_line_checked();
+ if (!ok) {
+ current_lineno--;
+ error("too many arguments");
+ current_lineno++;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////
+/* skip_line_warn ():
+ Skip line, but warn if arguments are left on actual line.
+
+ Spaces, tabs, and a comment are allowed before newline or EOF.
+ All other characters raise a warning
+*/
+void
+skip_line_warn(void)
+{
+ bool ok = skip_line_checked();
+ if (!ok) {
+ current_lineno--;
+ warning("too many arguments on current line");
+ current_lineno++;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////
+/* skip_line_D ():
+ Skip line in `D' commands.
+
+ Decide whether in case of an additional argument a fatal error is
+ raised (the documented classical behavior), only a warning is
+ issued, or the line is just skipped (former groff behavior).
+ Actually decided for the warning.
+*/
+void
+skip_line_D(void)
+{
+ skip_line_warn();
+ // or: skip_line_fatal();
+ // or: skip_line();
+}
+
+//////////////////////////////////////////////////////////////////////
+/* skip_line_x ():
+ Skip line in `x' commands.
+
+ Decide whether in case of an additional argument a fatal error is
+ raised (the documented classical behavior), only a warning is
+ issued, or the line is just skipped (former groff behavior).
+ Actually decided for the warning.
+*/
+void
+skip_line_x(void)
+{
+ skip_line_warn();
+ // or: skip_line_fatal();
+ // or: skip_line();
+}
+
+//////////////////////////////////////////////////////////////////////
+/* skip_to_end_of_line():
+ Go to the end of the current line.
+
+ Skip the rest of the current line, excluding the newline character.
+ The global variable current_lineno is not changed.
+ No errors are raised.
+*/
+void
+skip_to_end_of_line(void)
+{
+ Char c = get_char();
+ while (1) {
+ if (c == '\n') {
+ unget_char(c);
+ return;
+ }
+ if (c == EOF)
+ return;
+ c = get_char();
+ }
+}
+
+//////////////////////////////////////////////////////////////////////
+/* unget_char(c):
+ Restore character c onto input queue.
+
+ Write a character back onto the input stream.
+ EOF is gracefully handled.
+
+ c: In-parameter; character to be pushed onto the input queue.
+*/
+inline void
+unget_char(const Char c)
+{
+ if (c != EOF) {
+ int ch = (int) c;
+ if (ungetc(ch, current_file) == EOF)
+ fatal("could not unget character");
+ }
+}
+
+
+/**********************************************************************
+ parser subcommands
+ **********************************************************************/
+
+//////////////////////////////////////////////////////////////////////
+/* parse_color_command:
+ Process the commands m and DF, but not Df.
+
+ col: In-out-parameter; the color object to be set, must have
+ been initialized before.
+*/
+void
+parse_color_command(color *col)
+{
+ ColorArg gray = 0;
+ ColorArg red = 0, green = 0, blue = 0;
+ ColorArg cyan = 0, magenta = 0, yellow = 0, black = 0;
+ Char subcmd = next_arg_begin();
+ switch((int) subcmd) {
+ case 'c': // DFc or mc: CMY
+ cyan = get_color_arg();
+ magenta = get_color_arg();
+ yellow = get_color_arg();
+ col->set_cmy(cyan, magenta, yellow);
+ break;
+ case 'd': // DFd or md: set default color
+ col->set_default();
+ break;
+ case 'g': // DFg or mg: gray
+ gray = get_color_arg();
+ col->set_gray(gray);
+ break;
+ case 'k': // DFk or mk: CMYK
+ cyan = get_color_arg();
+ magenta = get_color_arg();
+ yellow = get_color_arg();
+ black = get_color_arg();
+ col->set_cmyk(cyan, magenta, yellow, black);
+ break;
+ case 'r': // DFr or mr: RGB
+ red = get_color_arg();
+ green = get_color_arg();
+ blue = get_color_arg();
+ col->set_rgb(red, green, blue);
+ break;
+ default:
+ error("invalid color scheme `%1'", (int) subcmd);
+ break;
+ } // end of color subcommands
+}
+
+//////////////////////////////////////////////////////////////////////
+/* parse_D_command():
+ Parse the subcommands of graphical command D.
+
+ This is the part of the do_file() parser that scans the graphical
+ subcommands.
+ - Error on lacking or wrong arguments.
+ - Warning on too many arguments.
+ - Line is always skipped.
+*/
+void
+parse_D_command()
+{
+ Char subcmd = next_arg_begin();
+ switch((int) subcmd) {
+ case '~': // D~: draw B-spline
+ // actually, this isn't available for some postprocessors
+ // fall through
+ default: // unknown options are passed to device
+ {
+ IntArray *args = get_D_variable_args();
+ send_draw(subcmd, args);
+ position_to_end_of_args(args);
+ delete args;
+ break;
+ }
+ case 'a': // Da: draw arc
+ {
+ IntArray *args = get_D_fixed_args(4);
+ send_draw(subcmd, args);
+ position_to_end_of_args(args);
+ delete args;
+ break;
+ }
+ case 'c': // Dc: draw circle line
+ {
+ IntArray *args = get_D_fixed_args(1);
+ send_draw(subcmd, args);
+ // move to right end
+ current_env->hpos += (*args)[0];
+ delete args;
+ break;
+ }
+ case 'C': // DC: draw solid circle
+ {
+ IntArray *args = get_D_fixed_args_odd_dummy(1);
+ send_draw(subcmd, args);
+ // move to right end
+ current_env->hpos += (*args)[0];
+ delete args;
+ break;
+ }
+ case 'e': // De: draw ellipse line
+ case 'E': // DE: draw solid ellipse
+ {
+ IntArray *args = get_D_fixed_args(2);
+ send_draw(subcmd, args);
+ // move to right end
+ current_env->hpos += (*args)[0];
+ delete args;
+ break;
+ }
+ case 'f': // Df: set fill gray; obsoleted by DFg
+ {
+ IntArg arg = get_integer_arg();
+ if ((arg >= 0) && (arg <= 1000)) {
+ // convert arg and treat it like DFg
+ ColorArg gray = color_from_Df_command(arg);
+ current_env->fill->set_gray(gray);
+ }
+ else {
+ // set fill color to the same value as the current outline color
+ delete current_env->fill;
+ current_env->fill = new color(current_env->col);
+ }
+ pr->change_fill_color(current_env);
+ // skip unused `vertical' component (\D'...' always emits pairs)
+ (void) get_integer_arg();
+# ifdef STUPID_DRAWING_POSITIONING
+ current_env->hpos += arg;
+# endif
+ skip_line_x();
+ break;
+ }
+ case 'F': // DF: set fill color, several formats
+ parse_color_command(current_env->fill);
+ pr->change_fill_color(current_env);
+ // no positioning (setting-only command)
+ skip_line_x();
+ break;
+ case 'l': // Dl: draw line
+ {
+ IntArray *args = get_D_fixed_args(2);
+ send_draw(subcmd, args);
+ position_to_end_of_args(args);
+ delete args;
+ break;
+ }
+ case 'p': // Dp: draw closed polygon line
+ case 'P': // DP: draw solid closed polygon
+ {
+ IntArray *args = get_D_variable_args();
+ send_draw(subcmd, args);
+# ifdef STUPID_DRAWING_POSITIONING
+ // final args positioning
+ position_to_end_of_args(args);
+# endif
+ delete args;
+ break;
+ }
+ case 't': // Dt: set line thickness
+ {
+ IntArray *args = get_D_fixed_args_odd_dummy(1);
+ send_draw(subcmd, args);
+# ifdef STUPID_DRAWING_POSITIONING
+ // final args positioning
+ position_to_end_of_args(args);
+# endif
+ delete args;
+ break;
+ }
+ } // end of D subcommands
+}
+
+//////////////////////////////////////////////////////////////////////
+/* parse_x_command():
+ Parse subcommands of the device control command x.
+
+ This is the part of the do_file() parser that scans the device
+ controlling commands.
+ - Error on duplicate prologue commands.
+ - Error on wrong or lacking arguments.
+ - Warning on too many arguments.
+ - Line is always skipped.
+
+ Globals:
+ - current_env: is set by many subcommands.
+ - npages: page counting variable
+
+ Return: boolean in the meaning of `stopped'
+ - true if parsing should be stopped (`x stop').
+ - false if parsing should continue.
+*/
+bool
+parse_x_command(void)
+{
+ bool stopped = false;
+ char *subcmd_str = get_string_arg();
+ char subcmd = subcmd_str[0];
+ switch (subcmd) {
+ case 'f': // x font: mount font
+ {
+ IntArg n = get_integer_arg();
+ char *name = get_string_arg();
+ pr->load_font(n, name);
+ a_delete name;
+ skip_line_x();
+ break;
+ }
+ case 'F': // x Filename: set filename for errors
+ {
+ char *str_arg = get_string_arg();
+ if (str_arg == 0)
+ warning("empty argument for `x F' command");
+ else {
+ remember_source_filename(str_arg);
+ a_delete str_arg;
+ }
+ break;
+ }
+ case 'H': // x Height: set character height
+ current_env->height = get_integer_arg();
+ if (current_env->height == current_env->size)
+ current_env->height = 0;
+ skip_line_x();
+ break;
+ case 'i': // x init: initialize device
+ error("duplicate `x init' command");
+ skip_line_x();
+ break;
+ case 'p': // x pause: pause device
+ skip_line_x();
+ break;
+ case 'r': // x res: set resolution
+ error("duplicate `x res' command");
+ skip_line_x();
+ break;
+ case 's': // x stop: stop device
+ stopped = true;
+ skip_line_x();
+ break;
+ case 'S': // x Slant: set slant
+ current_env->slant = get_integer_arg();
+ skip_line_x();
+ break;
+ case 't': // x trailer: generate trailer info
+ skip_line_x();
+ break;
+ case 'T': // x Typesetter: set typesetter
+ error("duplicate `x T' command");
+ skip_line();
+ break;
+ case 'u': // x underline: from .cu
+ {
+ char *str_arg = get_string_arg();
+ pr->special(str_arg, current_env, 'u');
+ a_delete str_arg;
+ skip_line_x();
+ break;
+ }
+ case 'X': // x X: send uninterpretedly to device
+ {
+ char *str_arg = get_extended_arg(); // includes line skip
+ if (npages <= 0)
+ error("`x X' command invalid before first `p' command");
+ else
+ pr->special(str_arg, current_env);
+ a_delete str_arg;
+ break;
+ }
+ default: // ignore unknown x commands, but warn
+ warning("unknown command `x %1'", subcmd);
+ skip_line();
+ }
+ a_delete subcmd_str;
+ return stopped;
+}
+
+
+/**********************************************************************
+ exported part (by driver.h)
+ **********************************************************************/
+
+////////////////////////////////////////////////////////////////////////
+/* do_file():
+ Parse and postprocess groff intermediate output.
+
+ filename: "-" for standard input, normal file name otherwise
+*/
+void
+do_file(const char *filename)
+{
+ Char command;
+ bool stopped = false; // terminating condition
+
+#ifdef USE_ENV_STACK
+ EnvStack env_stack = EnvStack();
+#endif // USE_ENV_STACK
+
+ // setup of global variables
+ npages = 0;
+ current_lineno = 1;
+ // `pr' is initialized after the prologue.
+ // `device' is set by the 1st prologue command.
+
+ if (filename[0] == '-' && filename[1] == '\0')
+ current_file = stdin;
+ else {
+ errno = 0;
+ current_file = fopen(filename, "r");
+ if (errno != 0 || current_file == 0) {
+ error("can't open file `%1'", filename);
+ return;
+ }
+ }
+ remember_filename(filename);
+
+ if (current_env != 0)
+ delete_current_env();
+ current_env = new environment;
+ current_env->col = new color;
+ current_env->fill = new color;
+ current_env->fontno = -1;
+ current_env->height = 0;
+ current_env->hpos = -1;
+ current_env->slant = 0;
+ current_env->size = 0;
+ current_env->vpos = -1;
+
+ // parsing of prologue (first 3 commands)
+ {
+ char *str_arg;
+ IntArg int_arg;
+
+ // 1st command `x T'
+ command = next_command();
+ if ((int) command == EOF)
+ return;
+ if ((int) command != 'x')
+ fatal("the first command must be `x T'");
+ str_arg = get_string_arg();
+ if (str_arg[0] != 'T')
+ fatal("the first command must be `x T'");
+ a_delete str_arg;
+ char *tmp_dev = get_string_arg();
+ if (pr == 0) { // note: `pr' initialized after prologue
+ device = tmp_dev;
+ if (!font::load_desc())
+ fatal("couldn't load DESC file, can't continue");
+ }
+ else {
+ if (device == 0 || strcmp(device, tmp_dev) != 0)
+ fatal("all files must use the same device");
+ a_delete tmp_dev;
+ }
+ skip_line_x(); // ignore further arguments
+ current_env->size = 10 * font::sizescale;
+
+ // 2nd command `x res'
+ command = next_command();
+ if ((int) command != 'x')
+ fatal("the second command must be `x res'");
+ str_arg = get_string_arg();
+ if (str_arg[0] != 'r')
+ fatal("the second command must be `x res'");
+ a_delete str_arg;
+ int_arg = get_integer_arg();
+ EnvInt font_res = font::res;
+ if (int_arg != font_res)
+ fatal("resolution does not match");
+ int_arg = get_integer_arg();
+ if (int_arg != font::hor)
+ fatal("minimum horizontal motion does not match");
+ int_arg = get_integer_arg();
+ if (int_arg != font::vert)
+ fatal("minimum vertical motion does not match");
+ skip_line_x(); // ignore further arguments
+
+ // 3rd command `x init'
+ command = next_command();
+ if (command != 'x')
+ fatal("the third command must be `x init'");
+ str_arg = get_string_arg();
+ if (str_arg[0] != 'i')
+ fatal("the third command must be `x init'");
+ a_delete str_arg;
+ skip_line_x();
+ }
+
+ // parsing of body
+ if (pr == 0)
+ pr = make_printer();
+ while (!stopped) {
+ command = next_command();
+ if (command == EOF)
+ break;
+ // spaces, tabs, comments, and newlines are skipped here
+ switch ((int) command) {
+ case '#': // #: comment, ignore up to end of line
+ skip_line();
+ break;
+#ifdef USE_ENV_STACK
+ case '{': // {: start a new environment (a copy)
+ env_stack.push(current_env);
+ break;
+ case '}': // }: pop previous env from stack
+ delete_current_env();
+ current_env = env_stack.pop();
+ break;
+#endif // USE_ENV_STACK
+ case '0': // ddc: obsolete jump and print command
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ { // expect 2 digits and a character
+ char s[3];
+ Char c = next_arg_begin();
+ if (npages <= 0)
+ fatal_command(command);
+ if (!isdigit((int) c)) {
+ error("digit expected");
+ c = 0;
+ }
+ s[0] = (char) command;
+ s[1] = (char) c;
+ s[2] = '\0';
+ errno = 0;
+ long int x = strtol(s, 0, 10);
+ if (errno != 0)
+ error("couldn't convert 2 digits");
+ EnvInt hor_pos = (EnvInt) x;
+ current_env->hpos += hor_pos;
+ c = next_arg_begin();
+ if ((int) c == '\n' || (int) c == EOF)
+ error("character argument expected");
+ else
+ pr->set_ascii_char((unsigned char) c, current_env);
+ break;
+ }
+ case 'c': // c: print ascii char without moving
+ {
+ if (npages <= 0)
+ fatal_command(command);
+ Char c = next_arg_begin();
+ if (c == '\n' || c == EOF)
+ error("missing argument to `c' command");
+ else
+ pr->set_ascii_char((unsigned char) c, current_env);
+ break;
+ }
+ case 'C': // C: print named special character
+ {
+ if (npages <= 0)
+ fatal_command(command);
+ char *str_arg = get_string_arg();
+ pr->set_special_char(str_arg, current_env);
+ a_delete str_arg;
+ break;
+ }
+ case 'D': // drawing commands
+ if (npages <= 0)
+ fatal_command(command);
+ parse_D_command();
+ break;
+ case 'f': // f: set font to number
+ current_env->fontno = get_integer_arg();
+ break;
+ case 'F': // F: obsolete, replaced by `x F'
+ {
+ char *str_arg = get_string_arg();
+ remember_source_filename(str_arg);
+ a_delete str_arg;
+ break;
+ }
+ case 'h': // h: relative horizontal move
+ current_env->hpos += (EnvInt) get_integer_arg();
+ break;
+ case 'H': // H: absolute horizontal positioning
+ current_env->hpos = (EnvInt) get_integer_arg();
+ break;
+ case 'm': // m: glyph color
+ parse_color_command(current_env->col);
+ pr->change_color(current_env);
+ break;
+ case 'n': // n: print end of line
+ // ignore two arguments (historically)
+ if (npages <= 0)
+ fatal_command(command);
+ pr->end_of_line();
+ (void) get_integer_arg();
+ (void) get_integer_arg();
+ break;
+ case 'N': // N: print char with given int code
+ if (npages <= 0)
+ fatal_command(command);
+ pr->set_numbered_char(get_integer_arg(), current_env);
+ break;
+ case 'p': // p: start new page with given number
+ if (npages > 0)
+ pr->end_page(current_env->vpos);
+ npages++; // increment # of processed pages
+ pr->begin_page(get_integer_arg());
+ current_env->vpos = 0;
+ break;
+ case 's': // s: set point size
+ current_env->size = get_integer_arg();
+ if (current_env->height == current_env->size)
+ current_env->height = 0;
+ break;
+ case 't': // t: print a text word
+ {
+ char c;
+ if (npages <= 0)
+ fatal_command(command);
+ char *str_arg = get_string_arg();
+ size_t i = 0;
+ while ((c = str_arg[i++]) != '\0') {
+ EnvInt w;
+ pr->set_ascii_char((unsigned char) c, current_env, &w);
+ current_env->hpos += w;
+ }
+ a_delete str_arg;
+ break;
+ }
+ case 'u': // u: print spaced word
+ {
+ char c;
+ if (npages <= 0)
+ fatal_command(command);
+ EnvInt kern = (EnvInt) get_integer_arg();
+ char *str_arg = get_string_arg();
+ size_t i = 0;
+ while ((c = str_arg[i++]) != '\0') {
+ EnvInt w;
+ pr->set_ascii_char((unsigned char) c, current_env, &w);
+ current_env->hpos += w + kern;
+ }
+ a_delete str_arg;
+ break;
+ }
+ case 'v': // v: relative vertical move
+ current_env->vpos += (EnvInt) get_integer_arg();
+ break;
+ case 'V': // V: absolute vertical positioning
+ current_env->vpos = (EnvInt) get_integer_arg();
+ break;
+ case 'w': // w: inform about paddable space
+ break;
+ case 'x': // device controlling commands
+ stopped = parse_x_command();
+ break;
+ default:
+ warning("unrecognized command `%1'", (unsigned char) command);
+ skip_line();
+ break;
+ } // end of switch
+ } // end of while
+
+ // end of file reached
+ if (npages > 0)
+ pr->end_page(current_env->vpos);
+ delete pr;
+ fclose(current_file);
+ // If `stopped' is not `true' here then there wasn't any `x stop'.
+ if (!stopped)
+ warning("no final `x stop' command");
+ delete_current_env();
+}
diff --git a/contrib/groff/src/libs/libdriver/printer.cpp b/contrib/groff/src/libs/libdriver/printer.cpp
new file mode 100644
index 0000000..179daf1
--- /dev/null
+++ b/contrib/groff/src/libs/libdriver/printer.cpp
@@ -0,0 +1,214 @@
+// -*- C++ -*-
+
+// <groff_src_dir>/src/libs/libdriver/printer.cpp
+
+/* Copyright (C) 1989, 1990, 1991, 1992, 2001, 2002, 2003
+ Free Software Foundation, Inc.
+ Written by James Clark (jjc@jclark.com)
+
+ Last update: 04 Apr 2003
+
+ This file is part of groff.
+
+ groff is free software; you can redistribute it and/or modify it
+ under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ groff is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with groff; see the file COPYING. If not, write to the Free
+ Software Foundation, 59 Temple Place - Suite 330, Boston, MA
+ 02111-1307, USA.
+*/
+
+#include "driver.h"
+
+font_pointer_list::font_pointer_list(font *f, font_pointer_list *fp)
+: p(f), next(fp)
+{
+}
+
+printer::printer()
+: font_list(0), font_table(0), nfonts(0)
+{
+}
+
+printer::~printer()
+{
+ a_delete font_table;
+ while (font_list) {
+ font_pointer_list *tem = font_list;
+ font_list = font_list->next;
+ delete tem->p;
+ delete tem;
+ }
+ if (ferror(stdout) || fflush(stdout) < 0)
+ fatal("output error");
+}
+
+void printer::load_font(int n, const char *nm)
+{
+ assert(n >= 0);
+ if (n >= nfonts) {
+ if (nfonts == 0) {
+ nfonts = 10;
+ if (nfonts <= n)
+ nfonts = n + 1;
+ font_table = new font *[nfonts];
+ for (int i = 0; i < nfonts; i++)
+ font_table[i] = 0;
+ }
+ else {
+ font **old_font_table = font_table;
+ int old_nfonts = nfonts;
+ nfonts *= 2;
+ if (n >= nfonts)
+ nfonts = n + 1;
+ font_table = new font *[nfonts];
+ int i;
+ for (i = 0; i < old_nfonts; i++)
+ font_table[i] = old_font_table[i];
+ for (i = old_nfonts; i < nfonts; i++)
+ font_table[i] = 0;
+ a_delete old_font_table;
+ }
+ }
+ font *f = find_font(nm);
+ font_table[n] = f;
+}
+
+font *printer::find_font(const char *nm)
+{
+ for (font_pointer_list *p = font_list; p; p = p->next)
+ if (strcmp(p->p->get_name(), nm) == 0)
+ return p->p;
+ font *f = make_font(nm);
+ if (!f)
+ fatal("sorry, I can't continue");
+ font_list = new font_pointer_list(f, font_list);
+ return f;
+}
+
+font *printer::make_font(const char *nm)
+{
+ return font::load_font(nm);
+}
+
+void printer::end_of_line()
+{
+}
+
+void printer::special(char *, const environment *, char)
+{
+}
+
+void printer::draw(int, int *, int, const environment *)
+{
+}
+
+void printer::change_color(const environment * const)
+{
+}
+
+void printer::change_fill_color(const environment * const)
+{
+}
+
+void printer::set_ascii_char(unsigned char c, const environment *env,
+ int *widthp)
+{
+ char buf[2];
+ int w;
+ font *f;
+
+ buf[0] = c;
+ buf[1] = '\0';
+
+ int i = set_char_and_width(buf, env, &w, &f);
+ set_char(i, f, env, w, 0);
+ if (widthp) {
+ *widthp = w;
+ }
+}
+
+void printer::set_special_char(const char *nm, const environment *env,
+ int *widthp)
+{
+ font *f;
+ int w;
+ int i = set_char_and_width(nm, env, &w, &f);
+ if (i != -1) {
+ set_char(i, f, env, w, nm);
+ if (widthp)
+ *widthp = w;
+ }
+}
+
+int printer::set_char_and_width(const char *nm, const environment *env,
+ int *widthp, font **f)
+{
+ int i = font::name_to_index(nm);
+ int fn = env->fontno;
+ if (fn < 0 || fn >= nfonts) {
+ error("bad font position `%1'", fn);
+ return(-1);
+ }
+ *f = font_table[fn];
+ if (*f == 0) {
+ error("no font mounted at `%1'", fn);
+ return(-1);
+ }
+ if (!(*f)->contains(i)) {
+ if (nm[0] != '\0' && nm[1] == '\0')
+ error("font `%1' does not contain ascii character `%2'",
+ (*f)->get_name(),
+ nm[0]);
+ else
+ error("font `%1' does not contain special character `%2'",
+ (*f)->get_name(),
+ nm);
+ return(-1);
+ }
+ int w = (*f)->get_width(i, env->size);
+ if (widthp)
+ *widthp = w;
+ return( i );
+}
+
+void printer::set_numbered_char(int num, const environment *env, int *widthp)
+{
+ int i = font::number_to_index(num);
+ int fn = env->fontno;
+ if (fn < 0 || fn >= nfonts) {
+ error("bad font position `%1'", fn);
+ return;
+ }
+ font *f = font_table[fn];
+ if (f == 0) {
+ error("no font mounted at `%1'", fn);
+ return;
+ }
+ if (!f->contains(i)) {
+ error("font `%1' does not contain numbered character %2",
+ f->get_name(),
+ num);
+ return;
+ }
+ int w = f->get_width(i, env->size);
+ if (widthp)
+ *widthp = w;
+ set_char(i, f, env, w, 0);
+}
+
+font *printer::get_font_from_index(int fontno)
+{
+ if ((fontno >= 0) && (fontno < nfonts))
+ return(font_table[fontno]);
+ else
+ return(0);
+}
diff --git a/contrib/groff/src/libs/libgroff/Makefile.sub b/contrib/groff/src/libs/libgroff/Makefile.sub
index ff6bd00..30277ab 100644
--- a/contrib/groff/src/libs/libgroff/Makefile.sub
+++ b/contrib/groff/src/libs/libgroff/Makefile.sub
@@ -39,39 +39,39 @@ OBJS=\
version.$(OBJEXT) \
$(LIBOBJS)
CCSRCS=\
- $(srcdir)/assert.cc \
- $(srcdir)/change_lf.cc \
- $(srcdir)/cmap.cc \
- $(srcdir)/color.cc \
- $(srcdir)/cset.cc \
- $(srcdir)/device.cc \
- $(srcdir)/errarg.cc \
- $(srcdir)/error.cc \
- $(srcdir)/fatal.cc \
- $(srcdir)/filename.cc \
- $(srcdir)/font.cc \
- $(srcdir)/fontfile.cc \
- $(srcdir)/geometry.cc \
- $(srcdir)/htmlhint.cc \
- $(srcdir)/invalid.cc \
- $(srcdir)/lf.cc \
- $(srcdir)/lineno.cc \
- $(srcdir)/macropath.cc \
- $(srcdir)/maxfilename.cc \
- $(srcdir)/mksdir.cc \
- $(srcdir)/mkstemp.cc \
- $(srcdir)/nametoindex.cc \
- $(srcdir)/new.cc \
- $(srcdir)/paper.cc \
- $(srcdir)/prime.cc \
- $(srcdir)/progname.cc \
- $(srcdir)/ptable.cc \
- $(srcdir)/searchpath.cc \
- $(srcdir)/string.cc \
- $(srcdir)/strsave.cc \
- $(srcdir)/tmpfile.cc \
- $(srcdir)/tmpname.cc \
- version.cc
+ $(srcdir)/assert.cpp \
+ $(srcdir)/change_lf.cpp \
+ $(srcdir)/cmap.cpp \
+ $(srcdir)/color.cpp \
+ $(srcdir)/cset.cpp \
+ $(srcdir)/device.cpp \
+ $(srcdir)/errarg.cpp \
+ $(srcdir)/error.cpp \
+ $(srcdir)/fatal.cpp \
+ $(srcdir)/filename.cpp \
+ $(srcdir)/font.cpp \
+ $(srcdir)/fontfile.cpp \
+ $(srcdir)/geometry.cpp \
+ $(srcdir)/htmlhint.cpp \
+ $(srcdir)/invalid.cpp \
+ $(srcdir)/lf.cpp \
+ $(srcdir)/lineno.cpp \
+ $(srcdir)/macropath.cpp \
+ $(srcdir)/maxfilename.cpp \
+ $(srcdir)/mksdir.cpp \
+ $(srcdir)/mkstemp.cpp \
+ $(srcdir)/nametoindex.cpp \
+ $(srcdir)/new.cpp \
+ $(srcdir)/paper.cpp \
+ $(srcdir)/prime.cpp \
+ $(srcdir)/progname.cpp \
+ $(srcdir)/ptable.cpp \
+ $(srcdir)/searchpath.cpp \
+ $(srcdir)/string.cpp \
+ $(srcdir)/strsave.cpp \
+ $(srcdir)/tmpfile.cpp \
+ $(srcdir)/tmpname.cpp \
+ version.cpp
CSRCS=\
$(srcdir)/fmod.c \
$(srcdir)/getcwd.c \
@@ -81,17 +81,19 @@ CSRCS=\
$(srcdir)/itoa.c \
$(srcdir)/matherr.c \
$(srcdir)/putenv.c \
+ $(srcdir)/strcasecmp.c \
$(srcdir)/strerror.c \
+ $(srcdir)/strncasecmp.c \
$(srcdir)/strtol.c \
$(srcdir)/../snprintf/snprintf.c
GENSRCS=\
- version.cc
+ version.cpp
src_version=`cat $(top_srcdir)/VERSION`
src_revision=`cat $(top_srcdir)/REVISION`
-version.cc: $(top_srcdir)/VERSION $(top_srcdir)/REVISION
- @echo Making version.cc
+version.cpp: $(top_srcdir)/VERSION $(top_srcdir)/REVISION
+ @echo Making version.cpp
@echo const char \*version_string = \"$(src_version)\"\; >$@
@echo const char \*revision_string = \"$(src_revision)\"\; >>$@
@echo extern \"C\" const char \*Version_string = \"$(src_version).$(src_revision)\"\; | \
diff --git a/contrib/groff/src/libs/libgroff/assert.cpp b/contrib/groff/src/libs/libgroff/assert.cpp
new file mode 100644
index 0000000..89742e3
--- /dev/null
+++ b/contrib/groff/src/libs/libgroff/assert.cpp
@@ -0,0 +1,34 @@
+/* Copyright (C) 1989, 1990, 1991, 1992 Free Software Foundation, Inc.
+ Written by James Clark (jjc@jclark.com)
+
+This file is part of groff.
+
+groff is free software; you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free
+Software Foundation; either version 2, or (at your option) any later
+version.
+
+groff is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+for more details.
+
+You should have received a copy of the GNU General Public License along
+with groff; see the file COPYING. If not, write to the Free Software
+Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include "assert.h"
+
+extern const char *program_name;
+
+void assertion_failed(int lineno, const char *filename)
+{
+ if (program_name != 0)
+ fprintf(stderr, "%s: ", program_name);
+ fprintf(stderr, "Failed assertion at line %d, file `%s'.\n",
+ lineno, filename);
+ fflush(stderr);
+ abort();
+}
diff --git a/contrib/groff/src/libs/libgroff/change_lf.cpp b/contrib/groff/src/libs/libgroff/change_lf.cpp
new file mode 100644
index 0000000..2e44af1
--- /dev/null
+++ b/contrib/groff/src/libs/libgroff/change_lf.cpp
@@ -0,0 +1,37 @@
+/* Copyright (C) 1989, 1990, 1991, 1992 Free Software Foundation, Inc.
+ Written by James Clark (jjc@jclark.com)
+
+This file is part of groff.
+
+groff is free software; you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free
+Software Foundation; either version 2, or (at your option) any later
+version.
+
+groff is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+for more details.
+
+You should have received a copy of the GNU General Public License along
+with groff; see the file COPYING. If not, write to the Free Software
+Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
+
+#include <string.h>
+
+extern char *strsave(const char *);
+
+extern const char *current_filename;
+extern int current_lineno;
+
+void change_filename(const char *f)
+{
+ if (current_filename != 0 && strcmp(current_filename, f) == 0)
+ return;
+ current_filename = strsave(f);
+}
+
+void change_lineno(int ln)
+{
+ current_lineno = ln;
+}
diff --git a/contrib/groff/src/libs/libgroff/cmap.cpp b/contrib/groff/src/libs/libgroff/cmap.cpp
new file mode 100644
index 0000000..4b75d06
--- /dev/null
+++ b/contrib/groff/src/libs/libgroff/cmap.cpp
@@ -0,0 +1,56 @@
+// -*- C++ -*-
+/* Copyright (C) 1989, 1990, 1991, 1992 Free Software Foundation, Inc.
+ Written by James Clark (jjc@jclark.com)
+
+This file is part of groff.
+
+groff is free software; you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free
+Software Foundation; either version 2, or (at your option) any later
+version.
+
+groff is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+for more details.
+
+You should have received a copy of the GNU General Public License along
+with groff; see the file COPYING. If not, write to the Free Software
+Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
+
+#include <ctype.h>
+#include "cmap.h"
+
+cmap cmlower(CMAP_BUILTIN);
+cmap cmupper(CMAP_BUILTIN);
+
+#ifdef isascii
+#define ISASCII(c) isascii(c)
+#else
+#define ISASCII(c) (1)
+#endif
+
+cmap::cmap()
+{
+ unsigned char *p = v;
+ for (int i = 0; i <= UCHAR_MAX; i++)
+ p[i] = i;
+}
+
+cmap::cmap(cmap_builtin)
+{
+ // these are initialised by cmap_init::cmap_init()
+}
+
+int cmap_init::initialised = 0;
+
+cmap_init::cmap_init()
+{
+ if (initialised)
+ return;
+ initialised = 1;
+ for (int i = 0; i <= UCHAR_MAX; i++) {
+ cmupper.v[i] = ISASCII(i) && islower(i) ? toupper(i) : i;
+ cmlower.v[i] = ISASCII(i) && isupper(i) ? tolower(i) : i;
+ }
+}
diff --git a/contrib/groff/src/libs/libgroff/color.cpp b/contrib/groff/src/libs/libgroff/color.cpp
new file mode 100644
index 0000000..da138dc
--- /dev/null
+++ b/contrib/groff/src/libs/libgroff/color.cpp
@@ -0,0 +1,427 @@
+// -*- C++ -*-
+
+/* <groff_src_dir>/src/libs/libgroff/color.cpp
+
+Last update: 13 Apr 2003
+
+Copyright (C) 2001, 2002, 2003 Free Software Foundation, Inc.
+ Written by Gaius Mulley <gaius@glam.ac.uk>
+
+This file is part of groff.
+
+groff is free software; you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free
+Software Foundation; either version 2, or (at your option) any later
+version.
+
+groff is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+for more details.
+
+You should have received a copy of the GNU General Public License along
+with groff; see the file COPYING. If not, write to the Free Software
+Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
+
+#include "color.h"
+#include "cset.h"
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#include <assert.h>
+#include <stdio.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include "errarg.h"
+#include "error.h"
+
+static inline unsigned int
+min(const unsigned int a, const unsigned int b)
+{
+ if (a < b)
+ return a;
+ else
+ return b;
+}
+
+color *color::free_list = 0;
+
+void *color::operator new(size_t n)
+{
+ assert(n == sizeof(color));
+ if (!free_list) {
+ const int BLOCK = 128;
+ free_list = (color *)new char[sizeof(color)*BLOCK];
+ for (int i = 0; i < BLOCK - 1; i++)
+ free_list[i].next = free_list + i + 1;
+ free_list[BLOCK-1].next = 0;
+ }
+ color *p = free_list;
+ free_list = (color *)(free_list->next);
+ p->next = 0;
+ return p;
+}
+
+void color::operator delete(void *p)
+{
+ if (p) {
+ ((color *)p)->next = free_list;
+ free_list = (color *)p;
+ }
+}
+
+color::color(const color * const c)
+{
+ scheme = c->scheme;
+ components[0] = c->components[0];
+ components[1] = c->components[1];
+ components[2] = c->components[2];
+ components[3] = c->components[3];
+}
+
+color::~color()
+{
+}
+
+int color::operator==(const color & c) const
+{
+ if (scheme != c.scheme)
+ return 0;
+ switch (scheme) {
+ case DEFAULT:
+ break;
+ case RGB:
+ if (Red != c.Red || Green != c.Green || Blue != c.Blue)
+ return 0;
+ break;
+ case CMYK:
+ if (Cyan != c.Cyan || Magenta != c.Magenta
+ || Yellow != c.Yellow || Black != c.Black)
+ return 0;
+ break;
+ case GRAY:
+ if (Gray != c.Gray)
+ return 0;
+ break;
+ case CMY:
+ if (Cyan != c.Cyan || Magenta != c.Magenta || Yellow != c.Yellow)
+ return 0;
+ break;
+ }
+ return 1;
+}
+
+int color::operator!=(const color & c) const
+{
+ return !(*this == c);
+}
+
+color_scheme color::get_components(unsigned int *c) const
+{
+#if 0
+ if (sizeof (c) < sizeof (unsigned int) * 4)
+ fatal("argument is not big enough to store 4 color components");
+#endif
+ c[0] = components[0];
+ c[1] = components[1];
+ c[2] = components[2];
+ c[3] = components[3];
+ return scheme;
+}
+
+void color::set_default()
+{
+ scheme = DEFAULT;
+}
+
+// (0, 0, 0) is black
+
+void color::set_rgb(const unsigned int r, const unsigned int g,
+ const unsigned int b)
+{
+ scheme = RGB;
+ Red = min(MAX_COLOR_VAL, r);
+ Green = min(MAX_COLOR_VAL, g);
+ Blue = min(MAX_COLOR_VAL, b);
+}
+
+// (0, 0, 0) is white
+
+void color::set_cmy(const unsigned int c, const unsigned int m,
+ const unsigned int y)
+{
+ scheme = CMY;
+ Cyan = min(MAX_COLOR_VAL, c);
+ Magenta = min(MAX_COLOR_VAL, m);
+ Yellow = min(MAX_COLOR_VAL, y);
+}
+
+// (0, 0, 0, 0) is white
+
+void color::set_cmyk(const unsigned int c, const unsigned int m,
+ const unsigned int y, const unsigned int k)
+{
+ scheme = CMYK;
+ Cyan = min(MAX_COLOR_VAL, c);
+ Magenta = min(MAX_COLOR_VAL, m);
+ Yellow = min(MAX_COLOR_VAL, y);
+ Black = min(MAX_COLOR_VAL, k);
+}
+
+// (0) is black
+
+void color::set_gray(const unsigned int g)
+{
+ scheme = GRAY;
+ Gray = min(MAX_COLOR_VAL, g);
+}
+
+/*
+ * atoh - computes the decimal value of a hexadecimal number string.
+ * `length' characters of `s' are read. Returns 1 if successful.
+ */
+
+static int atoh(unsigned int *result,
+ const char * const s, const size_t length)
+{
+ size_t i = 0;
+ unsigned int val = 0;
+ while ((i < length) && csxdigit(s[i])) {
+ if (csdigit(s[i]))
+ val = val*0x10 + (s[i]-'0');
+ else if (csupper(s[i]))
+ val = val*0x10 + (s[i]-'A') + 10;
+ else
+ val = val*0x10 + (s[i]-'a') + 10;
+ i++;
+ }
+ if (i != length)
+ return 0;
+ *result = val;
+ return 1;
+}
+
+/*
+ * read_encoding - set color from a hexadecimal color string.
+ *
+ * Use color scheme `cs' to parse `n' color components from string `s'.
+ * Returns 1 if successful.
+ */
+
+int color::read_encoding(const color_scheme cs, const char * const s,
+ const size_t n)
+{
+ size_t hex_length = 2;
+ scheme = cs;
+ char *p = (char *) s;
+ p++;
+ if (*p == '#') {
+ hex_length = 4;
+ p++;
+ }
+ for (size_t i = 0; i < n; i++) {
+ if (!atoh(&(components[i]), p, hex_length))
+ return 0;
+ if (hex_length == 2)
+ components[i] *= 0x101; // scale up -- 0xff should become 0xffff
+ p += hex_length;
+ }
+ return 1;
+}
+
+int color::read_rgb(const char * const s)
+{
+ return read_encoding(RGB, s, 3);
+}
+
+int color::read_cmy(const char * const s)
+{
+ return read_encoding(CMY, s, 3);
+}
+
+int color::read_cmyk(const char * const s)
+{
+ return read_encoding(CMYK, s, 4);
+}
+
+int color::read_gray(const char * const s)
+{
+ return read_encoding(GRAY, s, 1);
+}
+
+void
+color::get_rgb(unsigned int *r, unsigned int *g, unsigned int *b) const
+{
+ switch (scheme) {
+ case RGB:
+ *r = Red;
+ *g = Green;
+ *b = Blue;
+ break;
+ case CMY:
+ *r = MAX_COLOR_VAL - Cyan;
+ *g = MAX_COLOR_VAL - Magenta;
+ *b = MAX_COLOR_VAL - Yellow;
+ break;
+ case CMYK:
+ *r = MAX_COLOR_VAL
+ - min(MAX_COLOR_VAL,
+ Cyan * (MAX_COLOR_VAL - Black) / MAX_COLOR_VAL + Black);
+ *g = MAX_COLOR_VAL
+ - min(MAX_COLOR_VAL,
+ Magenta * (MAX_COLOR_VAL - Black) / MAX_COLOR_VAL + Black);
+ *b = MAX_COLOR_VAL
+ - min(MAX_COLOR_VAL,
+ Yellow * (MAX_COLOR_VAL - Black) / MAX_COLOR_VAL + Black);
+ break;
+ case GRAY:
+ *r = *g = *b = Gray;
+ break;
+ default:
+ assert(0);
+ break;
+ }
+}
+
+void
+color::get_cmy(unsigned int *c, unsigned int *m, unsigned int *y) const
+{
+ switch (scheme) {
+ case RGB:
+ *c = MAX_COLOR_VAL - Red;
+ *m = MAX_COLOR_VAL - Green;
+ *y = MAX_COLOR_VAL - Blue;
+ break;
+ case CMY:
+ *c = Cyan;
+ *m = Magenta;
+ *y = Yellow;
+ break;
+ case CMYK:
+ *c = min(MAX_COLOR_VAL,
+ Cyan * (MAX_COLOR_VAL - Black) / MAX_COLOR_VAL + Black);
+ *m = min(MAX_COLOR_VAL,
+ Magenta * (MAX_COLOR_VAL - Black) / MAX_COLOR_VAL + Black);
+ *y = min(MAX_COLOR_VAL,
+ Yellow * (MAX_COLOR_VAL - Black) / MAX_COLOR_VAL + Black);
+ break;
+ case GRAY:
+ *c = *m = *y = MAX_COLOR_VAL - Gray;
+ break;
+ default:
+ assert(0);
+ break;
+ }
+}
+
+void color::get_cmyk(unsigned int *c, unsigned int *m,
+ unsigned int *y, unsigned int *k) const
+{
+ switch (scheme) {
+ case RGB:
+ *k = min(MAX_COLOR_VAL - Red,
+ min(MAX_COLOR_VAL - Green, MAX_COLOR_VAL - Blue));
+ if (MAX_COLOR_VAL == *k) {
+ *c = MAX_COLOR_VAL;
+ *m = MAX_COLOR_VAL;
+ *y = MAX_COLOR_VAL;
+ }
+ else {
+ *c = (MAX_COLOR_VAL * (MAX_COLOR_VAL - Red - *k))
+ / (MAX_COLOR_VAL - *k);
+ *m = (MAX_COLOR_VAL * (MAX_COLOR_VAL - Green - *k))
+ / (MAX_COLOR_VAL - *k);
+ *y = (MAX_COLOR_VAL * (MAX_COLOR_VAL - Blue - *k))
+ / (MAX_COLOR_VAL - *k);
+ }
+ break;
+ case CMY:
+ *k = min(Cyan, min(Magenta, Yellow));
+ if (MAX_COLOR_VAL == *k) {
+ *c = MAX_COLOR_VAL;
+ *m = MAX_COLOR_VAL;
+ *y = MAX_COLOR_VAL;
+ }
+ else {
+ *c = (MAX_COLOR_VAL * (Cyan - *k)) / (MAX_COLOR_VAL - *k);
+ *m = (MAX_COLOR_VAL * (Magenta - *k)) / (MAX_COLOR_VAL - *k);
+ *y = (MAX_COLOR_VAL * (Yellow - *k)) / (MAX_COLOR_VAL - *k);
+ }
+ break;
+ case CMYK:
+ *c = Cyan;
+ *m = Magenta;
+ *y = Yellow;
+ *k = Black;
+ break;
+ case GRAY:
+ *c = *m = *y = 0;
+ *k = MAX_COLOR_VAL - Gray;
+ break;
+ default:
+ assert(0);
+ break;
+ }
+}
+
+// we use `0.222r + 0.707g + 0.071b' (this is the ITU standard)
+// as an approximation for gray
+
+void color::get_gray(unsigned int *g) const
+{
+ switch (scheme) {
+ case RGB:
+ *g = (222*Red + 707*Green + 71*Blue) / 1000;
+ break;
+ case CMY:
+ *g = MAX_COLOR_VAL - (222*Cyan + 707*Magenta + 71*Yellow) / 1000;
+ break;
+ case CMYK:
+ *g = (MAX_COLOR_VAL - (222*Cyan + 707*Magenta + 71*Yellow) / 1000)
+ * (MAX_COLOR_VAL - Black);
+ break;
+ case GRAY:
+ *g = Gray;
+ break;
+ default:
+ assert(0);
+ break;
+ }
+}
+
+char *color::print_color()
+{
+ char *s = new char[30];
+ switch (scheme) {
+ case DEFAULT:
+ sprintf(s, "default");
+ break;
+ case RGB:
+ sprintf(s, "rgb %.2ff %.2ff %.2ff",
+ double(Red) / MAX_COLOR_VAL,
+ double(Green) / MAX_COLOR_VAL,
+ double(Blue) / MAX_COLOR_VAL);
+ break;
+ case CMY:
+ sprintf(s, "cmy %.2ff %.2ff %.2ff",
+ double(Cyan) / MAX_COLOR_VAL,
+ double(Magenta) / MAX_COLOR_VAL,
+ double(Yellow) / MAX_COLOR_VAL);
+ break;
+ case CMYK:
+ sprintf(s, "cmyk %.2ff %.2ff %.2ff %.2ff",
+ double(Cyan) / MAX_COLOR_VAL,
+ double(Magenta) / MAX_COLOR_VAL,
+ double(Yellow) / MAX_COLOR_VAL,
+ double(Black) / MAX_COLOR_VAL);
+ break;
+ case GRAY:
+ sprintf(s, "gray %.2ff",
+ double(Gray) / MAX_COLOR_VAL);
+ break;
+ }
+ return s;
+}
+
+color default_color;
diff --git a/contrib/groff/src/libs/libgroff/cset.cpp b/contrib/groff/src/libs/libgroff/cset.cpp
new file mode 100644
index 0000000..e4845c1
--- /dev/null
+++ b/contrib/groff/src/libs/libgroff/cset.cpp
@@ -0,0 +1,102 @@
+// -*- C++ -*-
+/* Copyright (C) 1989, 1990, 1991, 1992 Free Software Foundation, Inc.
+ Written by James Clark (jjc@jclark.com)
+
+This file is part of groff.
+
+groff is free software; you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free
+Software Foundation; either version 2, or (at your option) any later
+version.
+
+groff is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+for more details.
+
+You should have received a copy of the GNU General Public License along
+with groff; see the file COPYING. If not, write to the Free Software
+Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
+
+#include <ctype.h>
+#include "cset.h"
+
+cset csalpha(CSET_BUILTIN);
+cset csupper(CSET_BUILTIN);
+cset cslower(CSET_BUILTIN);
+cset csdigit(CSET_BUILTIN);
+cset csxdigit(CSET_BUILTIN);
+cset csspace(CSET_BUILTIN);
+cset cspunct(CSET_BUILTIN);
+cset csalnum(CSET_BUILTIN);
+cset csprint(CSET_BUILTIN);
+cset csgraph(CSET_BUILTIN);
+cset cscntrl(CSET_BUILTIN);
+
+#ifdef isascii
+#define ISASCII(c) isascii(c)
+#else
+#define ISASCII(c) (1)
+#endif
+
+void cset::clear()
+{
+ char *p = v;
+ for (int i = 0; i <= UCHAR_MAX; i++)
+ p[i] = 0;
+}
+
+cset::cset()
+{
+ clear();
+}
+
+cset::cset(const char *s)
+{
+ clear();
+ while (*s)
+ v[(unsigned char)*s++] = 1;
+}
+
+cset::cset(const unsigned char *s)
+{
+ clear();
+ while (*s)
+ v[*s++] = 1;
+}
+
+cset::cset(cset_builtin)
+{
+ // these are initialised by cset_init::cset_init()
+}
+
+cset &cset::operator|=(const cset &cs)
+{
+ for (int i = 0; i <= UCHAR_MAX; i++)
+ if (cs.v[i])
+ v[i] = 1;
+ return *this;
+}
+
+
+int cset_init::initialised = 0;
+
+cset_init::cset_init()
+{
+ if (initialised)
+ return;
+ initialised = 1;
+ for (int i = 0; i <= UCHAR_MAX; i++) {
+ csalpha.v[i] = ISASCII(i) && isalpha(i);
+ csupper.v[i] = ISASCII(i) && isupper(i);
+ cslower.v[i] = ISASCII(i) && islower(i);
+ csdigit.v[i] = ISASCII(i) && isdigit(i);
+ csxdigit.v[i] = ISASCII(i) && isxdigit(i);
+ csspace.v[i] = ISASCII(i) && isspace(i);
+ cspunct.v[i] = ISASCII(i) && ispunct(i);
+ csalnum.v[i] = ISASCII(i) && isalnum(i);
+ csprint.v[i] = ISASCII(i) && isprint(i);
+ csgraph.v[i] = ISASCII(i) && isgraph(i);
+ cscntrl.v[i] = ISASCII(i) && iscntrl(i);
+ }
+}
diff --git a/contrib/groff/src/libs/libgroff/device.cpp b/contrib/groff/src/libs/libgroff/device.cpp
new file mode 100644
index 0000000..7efbfef
--- /dev/null
+++ b/contrib/groff/src/libs/libgroff/device.cpp
@@ -0,0 +1,36 @@
+// -*- C++ -*-
+/* Copyright (C) 1989, 1990, 1991, 1992 Free Software Foundation, Inc.
+ Written by James Clark (jjc@jclark.com)
+
+This file is part of groff.
+
+groff is free software; you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free
+Software Foundation; either version 2, or (at your option) any later
+version.
+
+groff is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+for more details.
+
+You should have received a copy of the GNU General Public License along
+with groff; see the file COPYING. If not, write to the Free Software
+Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
+
+#include <stdlib.h>
+#include "device.h"
+#include "defs.h"
+
+const char *device = DEVICE;
+
+struct device_init {
+ device_init();
+} _device_init;
+
+device_init::device_init()
+{
+ char *tem = getenv("GROFF_TYPESETTER");
+ if (tem)
+ device = tem;
+}
diff --git a/contrib/groff/src/libs/libgroff/errarg.cpp b/contrib/groff/src/libs/libgroff/errarg.cpp
new file mode 100644
index 0000000..2ddc0cc
--- /dev/null
+++ b/contrib/groff/src/libs/libgroff/errarg.cpp
@@ -0,0 +1,128 @@
+// -*- C++ -*-
+/* Copyright (C) 1989, 1990, 1991, 1992, 2000, 2002
+ Free Software Foundation, Inc.
+ Written by James Clark (jjc@jclark.com)
+
+This file is part of groff.
+
+groff is free software; you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free
+Software Foundation; either version 2, or (at your option) any later
+version.
+
+groff is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+for more details.
+
+You should have received a copy of the GNU General Public License along
+with groff; see the file COPYING. If not, write to the Free Software
+Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
+
+#include <stdio.h>
+#include "assert.h"
+#include "errarg.h"
+
+errarg::errarg(const char *p) : type(STRING)
+{
+ s = p ? p : "(null)";
+}
+
+errarg::errarg() : type(EMPTY)
+{
+}
+
+errarg::errarg(int nn) : type(INTEGER)
+{
+ n = nn;
+}
+
+errarg::errarg(unsigned int uu) : type(UNSIGNED_INTEGER)
+{
+ u = uu;
+}
+
+errarg::errarg(char cc) : type(CHAR)
+{
+ c = cc;
+}
+
+errarg::errarg(unsigned char cc) : type(CHAR)
+{
+ c = cc;
+}
+
+errarg::errarg(double dd) : type(DOUBLE)
+{
+ d = dd;
+}
+
+int errarg::empty() const
+{
+ return type == EMPTY;
+}
+
+extern "C" {
+ const char *i_to_a(int);
+ const char *ui_to_a(unsigned int);
+}
+
+void errarg::print() const
+{
+ switch (type) {
+ case INTEGER:
+ fputs(i_to_a(n), stderr);
+ break;
+ case UNSIGNED_INTEGER:
+ fputs(ui_to_a(u), stderr);
+ break;
+ case CHAR:
+ putc(c, stderr);
+ break;
+ case STRING:
+ fputs(s, stderr);
+ break;
+ case DOUBLE:
+ fprintf(stderr, "%g", d);
+ break;
+ case EMPTY:
+ break;
+ }
+}
+
+errarg empty_errarg;
+
+void errprint(const char *format,
+ const errarg &arg1,
+ const errarg &arg2,
+ const errarg &arg3)
+{
+ assert(format != 0);
+ char c;
+ while ((c = *format++) != '\0') {
+ if (c == '%') {
+ c = *format++;
+ switch(c) {
+ case '%':
+ fputc('%', stderr);
+ break;
+ case '1':
+ assert(!arg1.empty());
+ arg1.print();
+ break;
+ case '2':
+ assert(!arg2.empty());
+ arg2.print();
+ break;
+ case '3':
+ assert(!arg3.empty());
+ arg3.print();
+ break;
+ default:
+ assert(0);
+ }
+ }
+ else
+ putc(c, stderr);
+ }
+}
diff --git a/contrib/groff/src/libs/libgroff/error.cpp b/contrib/groff/src/libs/libgroff/error.cpp
new file mode 100644
index 0000000..f7d3ec0
--- /dev/null
+++ b/contrib/groff/src/libs/libgroff/error.cpp
@@ -0,0 +1,142 @@
+// -*- C++ -*-
+/* Copyright (C) 1989, 1990, 1991, 1992, 2003 Free Software Foundation, Inc.
+ Written by James Clark (jjc@jclark.com)
+
+This file is part of groff.
+
+groff is free software; you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free
+Software Foundation; either version 2, or (at your option) any later
+version.
+
+groff is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+for more details.
+
+You should have received a copy of the GNU General Public License along
+with groff; see the file COPYING. If not, write to the Free Software
+Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "errarg.h"
+#include "error.h"
+
+extern void fatal_error_exit();
+
+enum error_type { WARNING, ERROR, FATAL };
+
+static void do_error_with_file_and_line(const char *filename,
+ const char *source_filename,
+ int lineno,
+ error_type type,
+ const char *format,
+ const errarg &arg1,
+ const errarg &arg2,
+ const errarg &arg3)
+{
+ int need_space = 0;
+ if (program_name) {
+ fprintf(stderr, "%s:", program_name);
+ need_space = 1;
+ }
+ if (lineno >= 0 && filename != 0) {
+ if (strcmp(filename, "-") == 0)
+ filename = "<standard input>";
+ if (source_filename != 0)
+ fprintf(stderr, "%s (%s):%d:", filename, source_filename, lineno);
+ else
+ fprintf(stderr, "%s:%d:", filename, lineno);
+ need_space = 1;
+ }
+ switch (type) {
+ case FATAL:
+ fputs("fatal error:", stderr);
+ need_space = 1;
+ break;
+ case ERROR:
+ break;
+ case WARNING:
+ fputs("warning:", stderr);
+ need_space = 1;
+ break;
+ }
+ if (need_space)
+ fputc(' ', stderr);
+ errprint(format, arg1, arg2, arg3);
+ fputc('\n', stderr);
+ fflush(stderr);
+ if (type == FATAL)
+ fatal_error_exit();
+}
+
+
+static void do_error(error_type type,
+ const char *format,
+ const errarg &arg1,
+ const errarg &arg2,
+ const errarg &arg3)
+{
+ do_error_with_file_and_line(current_filename, current_source_filename,
+ current_lineno, type, format, arg1, arg2, arg3);
+}
+
+
+void error(const char *format,
+ const errarg &arg1,
+ const errarg &arg2,
+ const errarg &arg3)
+{
+ do_error(ERROR, format, arg1, arg2, arg3);
+}
+
+void warning(const char *format,
+ const errarg &arg1,
+ const errarg &arg2,
+ const errarg &arg3)
+{
+ do_error(WARNING, format, arg1, arg2, arg3);
+}
+
+void fatal(const char *format,
+ const errarg &arg1,
+ const errarg &arg2,
+ const errarg &arg3)
+{
+ do_error(FATAL, format, arg1, arg2, arg3);
+}
+
+void error_with_file_and_line(const char *filename,
+ int lineno,
+ const char *format,
+ const errarg &arg1,
+ const errarg &arg2,
+ const errarg &arg3)
+{
+ do_error_with_file_and_line(filename, 0, lineno,
+ ERROR, format, arg1, arg2, arg3);
+}
+
+void warning_with_file_and_line(const char *filename,
+ int lineno,
+ const char *format,
+ const errarg &arg1,
+ const errarg &arg2,
+ const errarg &arg3)
+{
+ do_error_with_file_and_line(filename, 0, lineno,
+ WARNING, format, arg1, arg2, arg3);
+}
+
+void fatal_with_file_and_line(const char *filename,
+ int lineno,
+ const char *format,
+ const errarg &arg1,
+ const errarg &arg2,
+ const errarg &arg3)
+{
+ do_error_with_file_and_line(filename, 0, lineno,
+ FATAL, format, arg1, arg2, arg3);
+}
diff --git a/contrib/groff/src/libs/libgroff/fatal.cpp b/contrib/groff/src/libs/libgroff/fatal.cpp
new file mode 100644
index 0000000..42560dc
--- /dev/null
+++ b/contrib/groff/src/libs/libgroff/fatal.cpp
@@ -0,0 +1,27 @@
+/* Copyright (C) 1989, 1990, 1991, 1992 Free Software Foundation, Inc.
+ Written by James Clark (jjc@jclark.com)
+
+This file is part of groff.
+
+groff is free software; you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free
+Software Foundation; either version 2, or (at your option) any later
+version.
+
+groff is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+for more details.
+
+You should have received a copy of the GNU General Public License along
+with groff; see the file COPYING. If not, write to the Free Software
+Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
+
+#include <stdlib.h>
+
+#define FATAL_ERROR_EXIT_CODE 3
+
+void fatal_error_exit()
+{
+ exit(FATAL_ERROR_EXIT_CODE);
+}
diff --git a/contrib/groff/src/libs/libgroff/filename.cpp b/contrib/groff/src/libs/libgroff/filename.cpp
new file mode 100644
index 0000000..6212705
--- /dev/null
+++ b/contrib/groff/src/libs/libgroff/filename.cpp
@@ -0,0 +1,2 @@
+const char *current_filename = 0;
+const char *current_source_filename = 0;
diff --git a/contrib/groff/src/libs/libgroff/font.cpp b/contrib/groff/src/libs/libgroff/font.cpp
new file mode 100644
index 0000000..c633cc8
--- /dev/null
+++ b/contrib/groff/src/libs/libgroff/font.cpp
@@ -0,0 +1,1034 @@
+// -*- C++ -*-
+/* Copyright (C) 1989, 1990, 1991, 1992, 2000, 2001, 2002, 2003
+ Free Software Foundation, Inc.
+ Written by James Clark (jjc@jclark.com)
+
+This file is part of groff.
+
+groff is free software; you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free
+Software Foundation; either version 2, or (at your option) any later
+version.
+
+groff is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+for more details.
+
+You should have received a copy of the GNU General Public License along
+with groff; see the file COPYING. If not, write to the Free Software
+Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
+
+#include "lib.h"
+
+#include <ctype.h>
+#include <assert.h>
+#include <math.h>
+#include <stdlib.h>
+#include "errarg.h"
+#include "error.h"
+#include "cset.h"
+#include "font.h"
+#include "paper.h"
+
+const char *const WS = " \t\n\r";
+
+struct font_char_metric {
+ char type;
+ int code;
+ int width;
+ int height;
+ int depth;
+ int pre_math_space;
+ int italic_correction;
+ int subscript_correction;
+ char *special_device_coding;
+};
+
+struct font_kern_list {
+ int i1;
+ int i2;
+ int amount;
+ font_kern_list *next;
+
+ font_kern_list(int, int, int, font_kern_list * = 0);
+};
+
+struct font_widths_cache {
+ font_widths_cache *next;
+ int point_size;
+ int *width;
+
+ font_widths_cache(int, int, font_widths_cache * = 0);
+ ~font_widths_cache();
+};
+
+/* text_file */
+
+struct text_file {
+ FILE *fp;
+ char *path;
+ int lineno;
+ int size;
+ int skip_comments;
+ char *buf;
+ text_file(FILE *fp, char *p);
+ ~text_file();
+ int next();
+ void error(const char *format,
+ const errarg &arg1 = empty_errarg,
+ const errarg &arg2 = empty_errarg,
+ const errarg &arg3 = empty_errarg);
+};
+
+text_file::text_file(FILE *p, char *s)
+: fp(p), path(s), lineno(0), size(0), skip_comments(1), buf(0)
+{
+}
+
+text_file::~text_file()
+{
+ a_delete buf;
+ a_delete path;
+ if (fp)
+ fclose(fp);
+}
+
+int text_file::next()
+{
+ if (fp == 0)
+ return 0;
+ if (buf == 0) {
+ buf = new char[128];
+ size = 128;
+ }
+ for (;;) {
+ int i = 0;
+ for (;;) {
+ int c = getc(fp);
+ if (c == EOF)
+ break;
+ if (invalid_input_char(c))
+ error("invalid input character code `%1'", int(c));
+ else {
+ if (i + 1 >= size) {
+ char *old_buf = buf;
+ buf = new char[size*2];
+ memcpy(buf, old_buf, size);
+ a_delete old_buf;
+ size *= 2;
+ }
+ buf[i++] = c;
+ if (c == '\n')
+ break;
+ }
+ }
+ if (i == 0)
+ break;
+ buf[i] = '\0';
+ lineno++;
+ char *ptr = buf;
+ while (csspace(*ptr))
+ ptr++;
+ if (*ptr != 0 && (!skip_comments || *ptr != '#'))
+ return 1;
+ }
+ return 0;
+}
+
+void text_file::error(const char *format,
+ const errarg &arg1,
+ const errarg &arg2,
+ const errarg &arg3)
+{
+ error_with_file_and_line(path, lineno, format, arg1, arg2, arg3);
+}
+
+
+/* font functions */
+
+font::font(const char *s)
+: ligatures(0), kern_hash_table(0), space_width(0), ch_index(0), nindices(0),
+ ch(0), ch_used(0), ch_size(0), special(0), widths_cache(0)
+{
+ name = new char[strlen(s) + 1];
+ strcpy(name, s);
+ internalname = 0;
+ slant = 0.0;
+ // load(); // for testing
+}
+
+font::~font()
+{
+ for (int i = 0; i < ch_used; i++)
+ if (ch[i].special_device_coding)
+ a_delete ch[i].special_device_coding;
+ a_delete ch;
+ a_delete ch_index;
+ if (kern_hash_table) {
+ for (int i = 0; i < KERN_HASH_TABLE_SIZE; i++) {
+ font_kern_list *kerns = kern_hash_table[i];
+ while (kerns) {
+ font_kern_list *tem = kerns;
+ kerns = kerns->next;
+ delete tem;
+ }
+ }
+ a_delete kern_hash_table;
+ }
+ a_delete name;
+ a_delete internalname;
+ while (widths_cache) {
+ font_widths_cache *tem = widths_cache;
+ widths_cache = widths_cache->next;
+ delete tem;
+ }
+}
+
+static int scale_round(int n, int x, int y)
+{
+ assert(x >= 0 && y > 0);
+ int y2 = y/2;
+ if (x == 0)
+ return 0;
+ if (n >= 0) {
+ if (n <= (INT_MAX - y2)/x)
+ return (n*x + y2)/y;
+ return int(n*double(x)/double(y) + .5);
+ }
+ else {
+ if (-(unsigned)n <= (-(unsigned)INT_MIN - y2)/x)
+ return (n*x - y2)/y;
+ return int(n*double(x)/double(y) - .5);
+ }
+}
+
+inline int font::scale(int w, int sz)
+{
+ return sz == unitwidth ? w : scale_round(w, sz, unitwidth);
+}
+
+int font::unit_scale(double *value, char unit)
+{
+ // we scale everything to inch
+ double divisor = 0;
+ switch (unit) {
+ case 'i':
+ divisor = 1;
+ break;
+ case 'p':
+ divisor = 72;
+ break;
+ case 'P':
+ divisor = 6;
+ break;
+ case 'c':
+ divisor = 2.54;
+ break;
+ default:
+ assert(0);
+ break;
+ }
+ if (divisor) {
+ *value /= divisor;
+ return 1;
+ }
+ return 0;
+}
+
+int font::get_skew(int c, int point_size, int sl)
+{
+ int h = get_height(c, point_size);
+ return int(h*tan((slant+sl)*PI/180.0) + .5);
+}
+
+int font::contains(int c)
+{
+ return c >= 0 && c < nindices && ch_index[c] >= 0;
+}
+
+int font::is_special()
+{
+ return special;
+}
+
+font_widths_cache::font_widths_cache(int ps, int ch_size,
+ font_widths_cache *p)
+: next(p), point_size(ps)
+{
+ width = new int[ch_size];
+ for (int i = 0; i < ch_size; i++)
+ width[i] = -1;
+}
+
+font_widths_cache::~font_widths_cache()
+{
+ a_delete width;
+}
+
+int font::get_width(int c, int point_size)
+{
+ assert(c >= 0 && c < nindices);
+ int i = ch_index[c];
+ assert(i >= 0);
+
+ if (point_size == unitwidth)
+ return ch[i].width;
+
+ if (!widths_cache)
+ widths_cache = new font_widths_cache(point_size, ch_size);
+ else if (widths_cache->point_size != point_size) {
+ font_widths_cache **p;
+ for (p = &widths_cache; *p; p = &(*p)->next)
+ if ((*p)->point_size == point_size)
+ break;
+ if (*p) {
+ font_widths_cache *tem = *p;
+ *p = (*p)->next;
+ tem->next = widths_cache;
+ widths_cache = tem;
+ }
+ else
+ widths_cache = new font_widths_cache(point_size, ch_size, widths_cache);
+ }
+ int &w = widths_cache->width[i];
+ if (w < 0)
+ w = scale(ch[i].width, point_size);
+ return w;
+}
+
+int font::get_height(int c, int point_size)
+{
+ assert(c >= 0 && c < nindices && ch_index[c] >= 0);
+ return scale(ch[ch_index[c]].height, point_size);
+}
+
+int font::get_depth(int c, int point_size)
+{
+ assert(c >= 0 && c < nindices && ch_index[c] >= 0);
+ return scale(ch[ch_index[c]].depth, point_size);
+}
+
+int font::get_italic_correction(int c, int point_size)
+{
+ assert(c >= 0 && c < nindices && ch_index[c] >= 0);
+ return scale(ch[ch_index[c]].italic_correction, point_size);
+}
+
+int font::get_left_italic_correction(int c, int point_size)
+{
+ assert(c >= 0 && c < nindices && ch_index[c] >= 0);
+ return scale(ch[ch_index[c]].pre_math_space, point_size);
+}
+
+int font::get_subscript_correction(int c, int point_size)
+{
+ assert(c >= 0 && c < nindices && ch_index[c] >= 0);
+ return scale(ch[ch_index[c]].subscript_correction, point_size);
+}
+
+int font::get_space_width(int point_size)
+{
+ return scale(space_width, point_size);
+}
+
+font_kern_list::font_kern_list(int c1, int c2, int n, font_kern_list *p)
+: i1(c1), i2(c2), amount(n), next(p)
+{
+}
+
+inline int font::hash_kern(int i1, int i2)
+{
+ int n = ((i1 << 10) + i2) % KERN_HASH_TABLE_SIZE;
+ return n < 0 ? -n : n;
+}
+
+void font::add_kern(int i1, int i2, int amount)
+{
+ if (!kern_hash_table) {
+ kern_hash_table = new font_kern_list *[int(KERN_HASH_TABLE_SIZE)];
+ for (int i = 0; i < KERN_HASH_TABLE_SIZE; i++)
+ kern_hash_table[i] = 0;
+ }
+ font_kern_list **p = kern_hash_table + hash_kern(i1, i2);
+ *p = new font_kern_list(i1, i2, amount, *p);
+}
+
+int font::get_kern(int i1, int i2, int point_size)
+{
+ if (kern_hash_table) {
+ for (font_kern_list *p = kern_hash_table[hash_kern(i1, i2)]; p; p = p->next)
+ if (i1 == p->i1 && i2 == p->i2)
+ return scale(p->amount, point_size);
+ }
+ return 0;
+}
+
+int font::has_ligature(int mask)
+{
+ return mask & ligatures;
+}
+
+int font::get_character_type(int c)
+{
+ assert(c >= 0 && c < nindices && ch_index[c] >= 0);
+ return ch[ch_index[c]].type;
+}
+
+int font::get_code(int c)
+{
+ assert(c >= 0 && c < nindices && ch_index[c] >= 0);
+ return ch[ch_index[c]].code;
+}
+
+const char *font::get_name()
+{
+ return name;
+}
+
+const char *font::get_internal_name()
+{
+ return internalname;
+}
+
+const char *font::get_special_device_encoding(int c)
+{
+ assert(c >= 0 && c < nindices && ch_index[c] >= 0);
+ return( ch[ch_index[c]].special_device_coding );
+}
+
+void font::alloc_ch_index(int index)
+{
+ if (nindices == 0) {
+ nindices = 128;
+ if (index >= nindices)
+ nindices = index + 10;
+ ch_index = new short[nindices];
+ for (int i = 0; i < nindices; i++)
+ ch_index[i] = -1;
+ }
+ else {
+ int old_nindices = nindices;
+ nindices *= 2;
+ if (index >= nindices)
+ nindices = index + 10;
+ short *old_ch_index = ch_index;
+ ch_index = new short[nindices];
+ memcpy(ch_index, old_ch_index, sizeof(short)*old_nindices);
+ for (int i = old_nindices; i < nindices; i++)
+ ch_index[i] = -1;
+ a_delete old_ch_index;
+ }
+}
+
+void font::extend_ch()
+{
+ if (ch == 0)
+ ch = new font_char_metric[ch_size = 16];
+ else {
+ int old_ch_size = ch_size;
+ ch_size *= 2;
+ font_char_metric *old_ch = ch;
+ ch = new font_char_metric[ch_size];
+ memcpy(ch, old_ch, old_ch_size*sizeof(font_char_metric));
+ a_delete old_ch;
+ }
+}
+
+void font::compact()
+{
+ int i;
+ for (i = nindices - 1; i >= 0; i--)
+ if (ch_index[i] >= 0)
+ break;
+ i++;
+ if (i < nindices) {
+ short *old_ch_index = ch_index;
+ ch_index = new short[i];
+ memcpy(ch_index, old_ch_index, i*sizeof(short));
+ a_delete old_ch_index;
+ nindices = i;
+ }
+ if (ch_used < ch_size) {
+ font_char_metric *old_ch = ch;
+ ch = new font_char_metric[ch_used];
+ memcpy(ch, old_ch, ch_used*sizeof(font_char_metric));
+ a_delete old_ch;
+ ch_size = ch_used;
+ }
+}
+
+void font::add_entry(int index, const font_char_metric &metric)
+{
+ assert(index >= 0);
+ if (index >= nindices)
+ alloc_ch_index(index);
+ assert(index < nindices);
+ if (ch_used + 1 >= ch_size)
+ extend_ch();
+ assert(ch_used + 1 < ch_size);
+ ch_index[index] = ch_used;
+ ch[ch_used++] = metric;
+}
+
+void font::copy_entry(int new_index, int old_index)
+{
+ assert(new_index >= 0 && old_index >= 0 && old_index < nindices);
+ if (new_index >= nindices)
+ alloc_ch_index(new_index);
+ ch_index[new_index] = ch_index[old_index];
+}
+
+font *font::load_font(const char *s, int *not_found)
+{
+ font *f = new font(s);
+ if (!f->load(not_found)) {
+ delete f;
+ return 0;
+ }
+ return f;
+}
+
+static char *trim_arg(char *p)
+{
+ if (!p)
+ return 0;
+ while (csspace(*p))
+ p++;
+ char *q = strchr(p, '\0');
+ while (q > p && csspace(q[-1]))
+ q--;
+ *q = '\0';
+ return p;
+}
+
+int font::scan_papersize(const char *p,
+ const char **size, double *length, double *width)
+{
+ double l, w;
+ char lu[2], wu[2];
+ const char *pp = p;
+ int test_file = 1;
+ char line[255];
+again:
+ if (csdigit(*pp)) {
+ if (sscanf(pp, "%lf%1[ipPc],%lf%1[ipPc]", &l, lu, &w, wu) == 4
+ && l > 0 && w > 0
+ && unit_scale(&l, lu[0]) && unit_scale(&w, wu[0])) {
+ if (length)
+ *length = l;
+ if (width)
+ *width = w;
+ if (size)
+ *size = "custom";
+ return 1;
+ }
+ }
+ else {
+ int i;
+ for (i = 0; i < NUM_PAPERSIZES; i++)
+ if (strcasecmp(papersizes[i].name, pp) == 0) {
+ if (length)
+ *length = papersizes[i].length;
+ if (width)
+ *width = papersizes[i].width;
+ if (size)
+ *size = papersizes[i].name;
+ return 1;
+ }
+ if (test_file) {
+ FILE *f = fopen(p, "r");
+ if (f) {
+ fgets(line, 254, f);
+ fclose(f);
+ test_file = 0;
+ char *linep = strchr(line, '\0');
+ // skip final newline, if any
+ if (*(--linep) == '\n')
+ *linep = '\0';
+ pp = line;
+ goto again;
+ }
+ }
+ }
+ return 0;
+}
+
+// If the font can't be found, then if not_found is non-NULL, it will be set
+// to 1 otherwise a message will be printed.
+
+int font::load(int *not_found)
+{
+ char *path;
+ FILE *fp;
+ if ((fp = open_file(name, &path)) == NULL) {
+ if (not_found)
+ *not_found = 1;
+ else
+ error("can't find font file `%1'", name);
+ return 0;
+ }
+ text_file t(fp, path);
+ t.skip_comments = 1;
+ char *p;
+ for (;;) {
+ if (!t.next()) {
+ t.error("missing charset command");
+ return 0;
+ }
+ p = strtok(t.buf, WS);
+ if (strcmp(p, "name") == 0) {
+ }
+ else if (strcmp(p, "spacewidth") == 0) {
+ p = strtok(0, WS);
+ int n;
+ if (p == 0 || sscanf(p, "%d", &n) != 1 || n <= 0) {
+ t.error("bad argument for spacewidth command");
+ return 0;
+ }
+ space_width = n;
+ }
+ else if (strcmp(p, "slant") == 0) {
+ p = strtok(0, WS);
+ double n;
+ if (p == 0 || sscanf(p, "%lf", &n) != 1 || n >= 90.0 || n <= -90.0) {
+ t.error("bad argument for slant command", p);
+ return 0;
+ }
+ slant = n;
+ }
+ else if (strcmp(p, "ligatures") == 0) {
+ for (;;) {
+ p = strtok(0, WS);
+ if (p == 0 || strcmp(p, "0") == 0)
+ break;
+ if (strcmp(p, "ff") == 0)
+ ligatures |= LIG_ff;
+ else if (strcmp(p, "fi") == 0)
+ ligatures |= LIG_fi;
+ else if (strcmp(p, "fl") == 0)
+ ligatures |= LIG_fl;
+ else if (strcmp(p, "ffi") == 0)
+ ligatures |= LIG_ffi;
+ else if (strcmp(p, "ffl") == 0)
+ ligatures |= LIG_ffl;
+ else {
+ t.error("unrecognised ligature `%1'", p);
+ return 0;
+ }
+ }
+ }
+ else if (strcmp(p, "internalname") == 0) {
+ p = strtok(0, WS);
+ if (!p) {
+ t.error("`internalname command requires argument");
+ return 0;
+ }
+ internalname = new char[strlen(p) + 1];
+ strcpy(internalname, p);
+ }
+ else if (strcmp(p, "special") == 0) {
+ special = 1;
+ }
+ else if (strcmp(p, "kernpairs") != 0 && strcmp(p, "charset") != 0) {
+ char *command = p;
+ p = strtok(0, "\n");
+ handle_unknown_font_command(command, trim_arg(p), t.path, t.lineno);
+ }
+ else
+ break;
+ }
+ char *command = p;
+ int had_charset = 0;
+ t.skip_comments = 0;
+ while (command) {
+ if (strcmp(command, "kernpairs") == 0) {
+ for (;;) {
+ if (!t.next()) {
+ command = 0;
+ break;
+ }
+ char *c1 = strtok(t.buf, WS);
+ if (c1 == 0)
+ continue;
+ char *c2 = strtok(0, WS);
+ if (c2 == 0) {
+ command = c1;
+ break;
+ }
+ p = strtok(0, WS);
+ if (p == 0) {
+ t.error("missing kern amount");
+ return 0;
+ }
+ int n;
+ if (sscanf(p, "%d", &n) != 1) {
+ t.error("bad kern amount `%1'", p);
+ return 0;
+ }
+ int i1 = name_to_index(c1);
+ if (i1 < 0) {
+ t.error("invalid character `%1'", c1);
+ return 0;
+ }
+ int i2 = name_to_index(c2);
+ if (i2 < 0) {
+ t.error("invalid character `%1'", c2);
+ return 0;
+ }
+ add_kern(i1, i2, n);
+ }
+ }
+ else if (strcmp(command, "charset") == 0) {
+ had_charset = 1;
+ int last_index = -1;
+ for (;;) {
+ if (!t.next()) {
+ command = 0;
+ break;
+ }
+ char *nm = strtok(t.buf, WS);
+ if (nm == 0)
+ continue; // I dont think this should happen
+ p = strtok(0, WS);
+ if (p == 0) {
+ command = nm;
+ break;
+ }
+ if (p[0] == '"') {
+ if (last_index == -1) {
+ t.error("first charset entry is duplicate");
+ return 0;
+ }
+ if (strcmp(nm, "---") == 0) {
+ t.error("unnamed character cannot be duplicate");
+ return 0;
+ }
+ int index = name_to_index(nm);
+ if (index < 0) {
+ t.error("invalid character `%1'", nm);
+ return 0;
+ }
+ copy_entry(index, last_index);
+ }
+ else {
+ font_char_metric metric;
+ metric.height = 0;
+ metric.depth = 0;
+ metric.pre_math_space = 0;
+ metric.italic_correction = 0;
+ metric.subscript_correction = 0;
+ int nparms = sscanf(p, "%d,%d,%d,%d,%d,%d",
+ &metric.width, &metric.height, &metric.depth,
+ &metric.italic_correction,
+ &metric.pre_math_space,
+ &metric.subscript_correction);
+ if (nparms < 1) {
+ t.error("bad width for `%1'", nm);
+ return 0;
+ }
+ p = strtok(0, WS);
+ if (p == 0) {
+ t.error("missing character type for `%1'", nm);
+ return 0;
+ }
+ int type;
+ if (sscanf(p, "%d", &type) != 1) {
+ t.error("bad character type for `%1'", nm);
+ return 0;
+ }
+ if (type < 0 || type > 255) {
+ t.error("character type `%1' out of range", type);
+ return 0;
+ }
+ metric.type = type;
+ p = strtok(0, WS);
+ if (p == 0) {
+ t.error("missing code for `%1'", nm);
+ return 0;
+ }
+ char *ptr;
+ metric.code = (int)strtol(p, &ptr, 0);
+ if (metric.code == 0 && ptr == p) {
+ t.error("bad code `%1' for character `%2'", p, nm);
+ return 0;
+ }
+ p = strtok(0, WS);
+ if ((p == NULL) || (strcmp(p, "--") == 0)) {
+ metric.special_device_coding = NULL;
+ }
+ else {
+ char *name = new char[strlen(p) + 1];
+ strcpy(name, p);
+ metric.special_device_coding = name;
+ }
+ if (strcmp(nm, "---") == 0) {
+ last_index = number_to_index(metric.code);
+ add_entry(last_index, metric);
+ }
+ else {
+ last_index = name_to_index(nm);
+ if (last_index < 0) {
+ t.error("invalid character `%1'", nm);
+ return 0;
+ }
+ add_entry(last_index, metric);
+ copy_entry(number_to_index(metric.code), last_index);
+ }
+ }
+ }
+ if (last_index == -1) {
+ t.error("I didn't seem to find any characters");
+ return 0;
+ }
+ }
+ else {
+ t.error("unrecognised command `%1' after `kernpairs' or `charset' command", command);
+ return 0;
+ }
+ }
+ if (!had_charset) {
+ t.error("missing charset command");
+ return 0;
+ }
+ if (space_width == 0)
+ space_width = scale_round(unitwidth, res, 72*3*sizescale);
+ compact();
+ return 1;
+}
+
+static struct {
+ const char *command;
+ int *ptr;
+} table[] = {
+ { "res", &font::res },
+ { "hor", &font::hor },
+ { "vert", &font::vert },
+ { "unitwidth", &font::unitwidth },
+ { "paperwidth", &font::paperwidth },
+ { "paperlength", &font::paperlength },
+ { "spare1", &font::biggestfont },
+ { "biggestfont", &font::biggestfont },
+ { "spare2", &font::spare2 },
+ { "sizescale", &font::sizescale }
+ };
+
+int font::load_desc()
+{
+ int nfonts = 0;
+ FILE *fp;
+ char *path;
+ if ((fp = open_file("DESC", &path)) == 0) {
+ error("can't find `DESC' file");
+ return 0;
+ }
+ text_file t(fp, path);
+ t.skip_comments = 1;
+ res = 0;
+ while (t.next()) {
+ char *p = strtok(t.buf, WS);
+ int found = 0;
+ unsigned int idx;
+ for (idx = 0; !found && idx < sizeof(table)/sizeof(table[0]); idx++)
+ if (strcmp(table[idx].command, p) == 0)
+ found = 1;
+ if (found) {
+ char *q = strtok(0, WS);
+ if (!q) {
+ t.error("missing value for command `%1'", p);
+ return 0;
+ }
+ //int *ptr = &(this->*(table[idx-1].ptr));
+ int *ptr = table[idx-1].ptr;
+ if (sscanf(q, "%d", ptr) != 1) {
+ t.error("bad number `%1'", q);
+ return 0;
+ }
+ }
+ else if (strcmp("family", p) == 0) {
+ p = strtok(0, WS);
+ if (!p) {
+ t.error("family command requires an argument");
+ return 0;
+ }
+ char *tem = new char[strlen(p)+1];
+ strcpy(tem, p);
+ family = tem;
+ }
+ else if (strcmp("fonts", p) == 0) {
+ p = strtok(0, WS);
+ if (!p || sscanf(p, "%d", &nfonts) != 1 || nfonts <= 0) {
+ t.error("bad number of fonts `%1'", p);
+ return 0;
+ }
+ font_name_table = (const char **)new char *[nfonts+1];
+ for (int i = 0; i < nfonts; i++) {
+ p = strtok(0, WS);
+ while (p == 0) {
+ if (!t.next()) {
+ t.error("end of file while reading list of fonts");
+ return 0;
+ }
+ p = strtok(t.buf, WS);
+ }
+ char *temp = new char[strlen(p)+1];
+ strcpy(temp, p);
+ font_name_table[i] = temp;
+ }
+ p = strtok(0, WS);
+ if (p != 0) {
+ t.error("font count does not match number of fonts");
+ return 0;
+ }
+ font_name_table[nfonts] = 0;
+ }
+ else if (strcmp("papersize", p) == 0) {
+ p = strtok(0, WS);
+ if (!p) {
+ t.error("papersize command requires an argument");
+ return 0;
+ }
+ int found_paper = 0;
+ while (p) {
+ double unscaled_paperwidth, unscaled_paperlength;
+ if (scan_papersize(p, &papersize, &unscaled_paperlength,
+ &unscaled_paperwidth)) {
+ paperwidth = int(unscaled_paperwidth * res + 0.5);
+ paperlength = int(unscaled_paperlength * res + 0.5);
+ found_paper = 1;
+ break;
+ }
+ p = strtok(0, WS);
+ }
+ if (!found_paper) {
+ t.error("bad paper size");
+ return 0;
+ }
+ }
+ else if (strcmp("pass_filenames", p) == 0)
+ pass_filenames = 1;
+ else if (strcmp("sizes", p) == 0) {
+ int n = 16;
+ sizes = new int[n];
+ int i = 0;
+ for (;;) {
+ p = strtok(0, WS);
+ while (p == 0) {
+ if (!t.next()) {
+ t.error("list of sizes must be terminated by `0'");
+ return 0;
+ }
+ p = strtok(t.buf, WS);
+ }
+ int lower, upper;
+ switch (sscanf(p, "%d-%d", &lower, &upper)) {
+ case 1:
+ upper = lower;
+ // fall through
+ case 2:
+ if (lower <= upper && lower >= 0)
+ break;
+ // fall through
+ default:
+ t.error("bad size range `%1'", p);
+ return 0;
+ }
+ if (i + 2 > n) {
+ int *old_sizes = sizes;
+ sizes = new int[n*2];
+ memcpy(sizes, old_sizes, n*sizeof(int));
+ n *= 2;
+ a_delete old_sizes;
+ }
+ sizes[i++] = lower;
+ if (lower == 0)
+ break;
+ sizes[i++] = upper;
+ }
+ if (i == 1) {
+ t.error("must have some sizes");
+ return 0;
+ }
+ }
+ else if (strcmp("styles", p) == 0) {
+ int style_table_size = 5;
+ style_table = (const char **)new char *[style_table_size];
+ int j;
+ for (j = 0; j < style_table_size; j++)
+ style_table[j] = 0;
+ int i = 0;
+ for (;;) {
+ p = strtok(0, WS);
+ if (p == 0)
+ break;
+ // leave room for terminating 0
+ if (i + 1 >= style_table_size) {
+ const char **old_style_table = style_table;
+ style_table_size *= 2;
+ style_table = (const char **)new char*[style_table_size];
+ for (j = 0; j < i; j++)
+ style_table[j] = old_style_table[j];
+ for (; j < style_table_size; j++)
+ style_table[j] = 0;
+ a_delete old_style_table;
+ }
+ char *tem = new char[strlen(p) + 1];
+ strcpy(tem, p);
+ style_table[i++] = tem;
+ }
+ }
+ else if (strcmp("tcommand", p) == 0)
+ tcommand = 1;
+ else if (strcmp("use_charnames_in_special", p) == 0)
+ use_charnames_in_special = 1;
+ else if (strcmp("charset", p) == 0)
+ break;
+ else if (unknown_desc_command_handler) {
+ char *command = p;
+ p = strtok(0, "\n");
+ (*unknown_desc_command_handler)(command, trim_arg(p), t.path, t.lineno);
+ }
+ }
+ if (res == 0) {
+ t.error("missing `res' command");
+ return 0;
+ }
+ if (unitwidth == 0) {
+ t.error("missing `unitwidth' command");
+ return 0;
+ }
+ if (font_name_table == 0) {
+ t.error("missing `fonts' command");
+ return 0;
+ }
+ if (sizes == 0) {
+ t.error("missing `sizes' command");
+ return 0;
+ }
+ if (sizescale < 1) {
+ t.error("bad `sizescale' value");
+ return 0;
+ }
+ if (hor < 1) {
+ t.error("bad `hor' value");
+ return 0;
+ }
+ if (vert < 1) {
+ t.error("bad `vert' value");
+ return 0;
+ }
+ return 1;
+}
+
+void font::handle_unknown_font_command(const char *, const char *,
+ const char *, int)
+{
+}
+
+FONT_COMMAND_HANDLER
+font::set_unknown_desc_command_handler(FONT_COMMAND_HANDLER func)
+{
+ FONT_COMMAND_HANDLER prev = unknown_desc_command_handler;
+ unknown_desc_command_handler = func;
+ return prev;
+}
diff --git a/contrib/groff/src/libs/libgroff/fontfile.cpp b/contrib/groff/src/libs/libgroff/fontfile.cpp
new file mode 100644
index 0000000..8502d12
--- /dev/null
+++ b/contrib/groff/src/libs/libgroff/fontfile.cpp
@@ -0,0 +1,67 @@
+// -*- C++ -*-
+/* Copyright (C) 1989, 1990, 1991, 1992, 2000, 2001, 2002
+ Free Software Foundation, Inc.
+ Written by James Clark (jjc@jclark.com)
+
+This file is part of groff.
+
+groff is free software; you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free
+Software Foundation; either version 2, or (at your option) any later
+version.
+
+groff is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+for more details.
+
+You should have received a copy of the GNU General Public License along
+with groff; see the file COPYING. If not, write to the Free Software
+Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
+
+#include "lib.h"
+
+#include <assert.h>
+#include <stdlib.h>
+#include <errno.h>
+#include "font.h"
+#include "searchpath.h"
+#include "device.h"
+#include "defs.h"
+
+const char *const FONT_ENV_VAR = "GROFF_FONT_PATH";
+
+static search_path font_path(FONT_ENV_VAR, FONTPATH, 0, 0);
+
+int font::res = 0;
+int font::hor = 1;
+int font::vert = 1;
+int font::unitwidth = 0;
+int font::paperwidth = 0;
+int font::paperlength = 0;
+const char *font::papersize = 0;
+int font::biggestfont = 0;
+int font::spare2 = 0;
+int font::sizescale = 1;
+int font::tcommand = 0;
+int font::pass_filenames = 0;
+int font::use_charnames_in_special = 0;
+const char **font::font_name_table = 0;
+int *font::sizes = 0;
+const char *font::family = 0;
+const char **font::style_table = 0;
+FONT_COMMAND_HANDLER font::unknown_desc_command_handler = 0;
+
+void font::command_line_font_dir(const char *dir)
+{
+ font_path.command_line_dir(dir);
+}
+
+FILE *font::open_file(const char *name, char **pathp)
+{
+ char *filename = new char[strlen(name) + strlen(device) + 5];
+ sprintf(filename, "dev%s/%s", device, name);
+ FILE *fp = font_path.open_file(filename, pathp);
+ a_delete filename;
+ return fp;
+}
diff --git a/contrib/groff/src/libs/libgroff/geometry.cpp b/contrib/groff/src/libs/libgroff/geometry.cpp
new file mode 100644
index 0000000..256d266
--- /dev/null
+++ b/contrib/groff/src/libs/libgroff/geometry.cpp
@@ -0,0 +1,286 @@
+// -*- C++ -*-
+/* Copyright (C) 1989, 1990, 1991, 1992, 2000, 2001, 2002, 2003
+ Free Software Foundation, Inc.
+ Written by Gaius Mulley <gaius@glam.ac.uk>
+ using adjust_arc_center() from printer.cpp, written by James Clark.
+
+This file is part of groff.
+
+groff is free software; you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free
+Software Foundation; either version 2, or (at your option) any later
+version.
+
+groff is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+for more details.
+
+You should have received a copy of the GNU General Public License along
+with groff; see the file COPYING. If not, write to the Free Software
+Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
+
+
+#include <stdio.h>
+#include <math.h>
+
+#undef MAX
+#define MAX(a, b) (((a) > (b)) ? (a) : (b))
+
+#undef MIN
+#define MIN(a, b) (((a) < (b)) ? (a) : (b))
+
+
+// This utility function adjusts the specified center of the
+// arc so that it is equidistant between the specified start
+// and end points. (p[0], p[1]) is a vector from the current
+// point to the center; (p[2], p[3]) is a vector from the
+// center to the end point. If the center can be adjusted,
+// a vector from the current point to the adjusted center is
+// stored in c[0], c[1] and 1 is returned. Otherwise 0 is
+// returned.
+
+#if 1
+int adjust_arc_center(const int *p, double *c)
+{
+ // We move the center along a line parallel to the line between
+ // the specified start point and end point so that the center
+ // is equidistant between the start and end point.
+ // It can be proved (using Lagrange multipliers) that this will
+ // give the point nearest to the specified center that is equidistant
+ // between the start and end point.
+
+ double x = p[0] + p[2]; // (x, y) is the end point
+ double y = p[1] + p[3];
+ double n = x*x + y*y;
+ if (n != 0) {
+ c[0]= double(p[0]);
+ c[1] = double(p[1]);
+ double k = .5 - (c[0]*x + c[1]*y)/n;
+ c[0] += k*x;
+ c[1] += k*y;
+ return 1;
+ }
+ else
+ return 0;
+}
+#else
+int printer::adjust_arc_center(const int *p, double *c)
+{
+ int x = p[0] + p[2]; // (x, y) is the end point
+ int y = p[1] + p[3];
+ // Start at the current point; go in the direction of the specified
+ // center point until we reach a point that is equidistant between
+ // the specified starting point and the specified end point. Place
+ // the center of the arc there.
+ double n = p[0]*double(x) + p[1]*double(y);
+ if (n > 0) {
+ double k = (double(x)*x + double(y)*y)/(2.0*n);
+ // (cx, cy) is our chosen center
+ c[0] = k*p[0];
+ c[1] = k*p[1];
+ return 1;
+ }
+ else {
+ // We would never reach such a point. So instead start at the
+ // specified end point of the arc. Go towards the specified
+ // center point until we reach a point that is equidistant between
+ // the specified start point and specified end point. Place
+ // the center of the arc there.
+ n = p[2]*double(x) + p[3]*double(y);
+ if (n > 0) {
+ double k = 1 - (double(x)*x + double(y)*y)/(2.0*n);
+ // (c[0], c[1]) is our chosen center
+ c[0] = p[0] + k*p[2];
+ c[1] = p[1] + k*p[3];
+ return 1;
+ }
+ else
+ return 0;
+ }
+}
+#endif
+
+
+/*
+ * check_output_arc_limits - works out the smallest box that will encompass
+ * an arc defined by an origin (x, y) and two
+ * vectors (p0, p1) and (p2, p3).
+ * (x1, y1) -> start of arc
+ * (x1, y1) + (xv1, yv1) -> center of circle
+ * (x1, y1) + (xv1, yv1) + (xv2, yv2) -> end of arc
+ *
+ * Works out in which quadrant the arc starts and
+ * stops, and from this it determines the x, y
+ * max/min limits. The arc is drawn clockwise.
+ *
+ * [I'm sure there is a better way to do this, but
+ * I don't know how. Please can someone let me
+ * know or "improve" this function.]
+ */
+
+void check_output_arc_limits(int x1, int y1,
+ int xv1, int yv1,
+ int xv2, int yv2,
+ double c0, double c1,
+ int *minx, int *maxx,
+ int *miny, int *maxy)
+{
+ int radius = (int)sqrt(c0*c0 + c1*c1);
+ int x2 = x1 + xv1 + xv2; // end of arc is (x2, y2)
+ int y2 = y1 + yv1 + yv2;
+
+ // firstly lets use the `circle' limitation
+ *minx = x1 + xv1 - radius;
+ *maxx = x1 + xv1 + radius;
+ *miny = y1 + yv1 - radius;
+ *maxy = y1 + yv1 + radius;
+
+ /* now to see which min/max can be reduced and increased for the limits of
+ * the arc
+ *
+ * Q2 | Q1
+ * -----+-----
+ * Q3 | Q4
+ *
+ *
+ * NB. (x1+xv1, y1+yv1) is at the origin
+ *
+ * below we ask a nested question
+ * (i) from which quadrant does the first vector start?
+ * (ii) into which quadrant does the second vector go?
+ * from the 16 possible answers we determine the limits of the arc
+ */
+ if (xv1 > 0 && yv1 > 0) {
+ // first vector in Q3
+ if (xv2 >= 0 && yv2 >= 0 ) {
+ // second in Q1
+ *maxx = x2;
+ *miny = y1;
+ }
+ else if (xv2 < 0 && yv2 >= 0) {
+ // second in Q2
+ *maxx = x2;
+ *miny = y1;
+ }
+ else if (xv2 >= 0 && yv2 < 0) {
+ // second in Q4
+ *miny = MIN(y1, y2);
+ }
+ else if (xv2 < 0 && yv2 < 0) {
+ // second in Q3
+ if (x1 >= x2) {
+ *minx = x2;
+ *maxx = x1;
+ *miny = MIN(y1, y2);
+ *maxy = MAX(y1, y2);
+ }
+ else {
+ // xv2, yv2 could all be zero?
+ }
+ }
+ }
+ else if (xv1 > 0 && yv1 < 0) {
+ // first vector in Q2
+ if (xv2 >= 0 && yv2 >= 0) {
+ // second in Q1
+ *maxx = MAX(x1, x2);
+ *minx = MIN(x1, x2);
+ *miny = y1;
+ }
+ else if (xv2 < 0 && yv2 >= 0) {
+ // second in Q2
+ if (x1 < x2) {
+ *maxx = x2;
+ *minx = x1;
+ *miny = MIN(y1, y2);
+ *maxy = MAX(y1, y2);
+ }
+ else {
+ // otherwise almost full circle anyway
+ }
+ }
+ else if (xv2 >= 0 && yv2 < 0) {
+ // second in Q4
+ *miny = y2;
+ *minx = x1;
+ }
+ else if (xv2 < 0 && yv2 < 0) {
+ // second in Q3
+ *minx = MIN(x1, x2);
+ }
+ }
+ else if (xv1 <= 0 && yv1 <= 0) {
+ // first vector in Q1
+ if (xv2 >= 0 && yv2 >= 0) {
+ // second in Q1
+ if (x1 < x2) {
+ *minx = x1;
+ *maxx = x2;
+ *miny = MIN(y1, y2);
+ *maxy = MAX(y1, y2);
+ }
+ else {
+ // nearly full circle
+ }
+ }
+ else if (xv2 < 0 && yv2 >= 0) {
+ // second in Q2
+ *maxy = MAX(y1, y2);
+ }
+ else if (xv2 >= 0 && yv2 < 0) {
+ // second in Q4
+ *miny = MIN(y1, y2);
+ *maxy = MAX(y1, y2);
+ *minx = MIN(x1, x2);
+ }
+ else if (xv2 < 0 && yv2 < 0) {
+ // second in Q3
+ *minx = x2;
+ *maxy = y1;
+ }
+ }
+ else if (xv1 <= 0 && yv1 > 0) {
+ // first vector in Q4
+ if (xv2 >= 0 && yv2 >= 0) {
+ // second in Q1
+ *maxx = MAX(x1, x2);
+ }
+ else if (xv2 < 0 && yv2 >= 0) {
+ // second in Q2
+ *maxy = MAX(y1, y2);
+ *maxx = MAX(x1, x2);
+ }
+ else if (xv2 >= 0 && yv2 < 0) {
+ // second in Q4
+ if (x1 >= x2) {
+ *miny = MIN(y1, y2);
+ *maxy = MAX(y1, y2);
+ *minx = MIN(x1, x2);
+ *maxx = MAX(x2, x2);
+ }
+ else {
+ // nearly full circle
+ }
+ }
+ else if (xv2 < 0 && yv2 < 0) {
+ // second in Q3
+ *maxy = MAX(y1, y2);
+ *minx = MIN(x1, x2);
+ *maxx = MAX(x1, x2);
+ }
+ }
+
+ // this should *never* happen but if it does it means a case above is wrong
+ // this code is only present for safety sake
+ if (*maxx < *minx) {
+ fprintf(stderr, "assert failed *minx > *maxx\n");
+ fflush(stderr);
+ *maxx = *minx;
+ }
+ if (*maxy < *miny) {
+ fprintf(stderr, "assert failed *miny > *maxy\n");
+ fflush(stderr);
+ *maxy = *miny;
+ }
+}
diff --git a/contrib/groff/src/libs/libgroff/htmlhint.cpp b/contrib/groff/src/libs/libgroff/htmlhint.cpp
new file mode 100644
index 0000000..3015767
--- /dev/null
+++ b/contrib/groff/src/libs/libgroff/htmlhint.cpp
@@ -0,0 +1,59 @@
+/* Copyright (C) 2000, 2001, 2002 Free Software Foundation, Inc.
+ Written by Gaius Mulley (gaius@glam.ac.uk)
+
+This file is part of groff.
+
+groff is free software; you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free
+Software Foundation; either version 2, or (at your option) any later
+version.
+
+groff is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+for more details.
+
+You should have received a copy of the GNU General Public License along
+with groff; see the file COPYING. If not, write to the Free Software
+Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
+
+#include "lib.h"
+
+#include <stddef.h>
+#include <stdlib.h>
+
+#include "nonposix.h"
+#include "stringclass.h"
+#include "html-strings.h"
+
+/*
+ * This file contains a very simple set of routines which might
+ * be shared by preprocessors. It allows a preprocessor to indicate
+ * when an inline image should be created.
+ * This string is intercepted by pre-grohtml and substituted for
+ * the image name and suppression escapes.
+ *
+ * pre-html runs troff twice, once with -Thtml and once with -Tps.
+ * troff -Thtml device driver emits a <src='image'.png> tag
+ * and the postscript device driver works out the min/max limits
+ * of the graphic region. These region limits are read by pre-html
+ * and an image is generated via troff -Tps -> gs -> png
+ */
+
+/*
+ * html_begin_suppress - emit a start of image tag which will be seen
+ * by pre-html.
+ */
+void html_begin_suppress()
+{
+ put_string(HTML_IMAGE_INLINE_BEGIN, stdout);
+}
+
+/*
+ * html_end_suppress - emit an end of image tag which will be seen
+ * by pre-html.
+ */
+void html_end_suppress()
+{
+ put_string(HTML_IMAGE_INLINE_END, stdout);
+}
diff --git a/contrib/groff/src/libs/libgroff/invalid.cpp b/contrib/groff/src/libs/libgroff/invalid.cpp
new file mode 100644
index 0000000..f36894a
--- /dev/null
+++ b/contrib/groff/src/libs/libgroff/invalid.cpp
@@ -0,0 +1,60 @@
+/* Copyright (C) 2000, 2002 Free Software Foundation, Inc.
+ Written by James Clark (jjc@jclark.com)
+
+This file is part of groff.
+
+groff is free software; you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free
+Software Foundation; either version 2, or (at your option) any later
+version.
+
+groff is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+for more details.
+
+You should have received a copy of the GNU General Public License along
+with groff; see the file COPYING. If not, write to the Free Software
+Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
+
+#include "lib.h"
+
+// Table of invalid input characters.
+
+char invalid_char_table[256]= {
+#ifndef IS_EBCDIC_HOST
+ 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+#else
+ 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 0, 1, 1, 1,
+ 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+#endif
+};
diff --git a/contrib/groff/src/libs/libgroff/lf.cpp b/contrib/groff/src/libs/libgroff/lf.cpp
new file mode 100644
index 0000000..34272c7
--- /dev/null
+++ b/contrib/groff/src/libs/libgroff/lf.cpp
@@ -0,0 +1,62 @@
+// -*- C++ -*-
+/* Copyright (C) 1989, 1990, 1991, 1992 Free Software Foundation, Inc.
+ Written by James Clark (jjc@jclark.com)
+
+This file is part of groff.
+
+groff is free software; you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free
+Software Foundation; either version 2, or (at your option) any later
+version.
+
+groff is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+for more details.
+
+You should have received a copy of the GNU General Public License along
+with groff; see the file COPYING. If not, write to the Free Software
+Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
+
+#include <string.h>
+#include <ctype.h>
+#include "cset.h"
+#include "stringclass.h"
+
+extern void change_filename(const char *);
+extern void change_lineno(int);
+
+int interpret_lf_args(const char *p)
+{
+ while (*p == ' ')
+ p++;
+ if (!csdigit(*p))
+ return 0;
+ int ln = 0;
+ do {
+ ln *= 10;
+ ln += *p++ - '0';
+ } while (csdigit(*p));
+ if (*p != ' ' && *p != '\n' && *p != '\0')
+ return 0;
+ while (*p == ' ')
+ p++;
+ if (*p == '\0' || *p == '\n') {
+ change_lineno(ln);
+ return 1;
+ }
+ const char *q;
+ for (q = p;
+ *q != '\0' && *q != ' ' && *q != '\n' && *q != '\\';
+ q++)
+ ;
+ string tem(p, q - p);
+ while (*q == ' ')
+ q++;
+ if (*q != '\n' && *q != '\0')
+ return 0;
+ tem += '\0';
+ change_filename(tem.contents());
+ change_lineno(ln);
+ return 1;
+}
diff --git a/contrib/groff/src/libs/libgroff/lineno.cpp b/contrib/groff/src/libs/libgroff/lineno.cpp
new file mode 100644
index 0000000..f7138db
--- /dev/null
+++ b/contrib/groff/src/libs/libgroff/lineno.cpp
@@ -0,0 +1 @@
+int current_lineno = 0;
diff --git a/contrib/groff/src/libs/libgroff/macropath.cpp b/contrib/groff/src/libs/libgroff/macropath.cpp
new file mode 100644
index 0000000..03c04cb
--- /dev/null
+++ b/contrib/groff/src/libs/libgroff/macropath.cpp
@@ -0,0 +1,30 @@
+// -*- C++ -*-
+/* Copyright (C) 1989, 1990, 1991, 1992, 2000 Free Software Foundation, Inc.
+ Written by James Clark (jjc@jclark.com)
+
+This file is part of groff.
+
+groff is free software; you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free
+Software Foundation; either version 2, or (at your option) any later
+version.
+
+groff is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+for more details.
+
+You should have received a copy of the GNU General Public License along
+with groff; see the file COPYING. If not, write to the Free Software
+Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
+
+#include "lib.h"
+#include "searchpath.h"
+#include "macropath.h"
+#include "defs.h"
+
+#define MACROPATH_ENVVAR "GROFF_TMAC_PATH"
+
+search_path macro_path(MACROPATH_ENVVAR, MACROPATH, 1, 1);
+search_path safer_macro_path(MACROPATH_ENVVAR, MACROPATH, 1, 0);
+search_path config_macro_path(MACROPATH_ENVVAR, MACROPATH, 0, 0);
diff --git a/contrib/groff/src/libs/libgroff/maxfilename.cpp b/contrib/groff/src/libs/libgroff/maxfilename.cpp
new file mode 100644
index 0000000..25f5549
--- /dev/null
+++ b/contrib/groff/src/libs/libgroff/maxfilename.cpp
@@ -0,0 +1,69 @@
+// -*- C++ -*-
+/* Copyright (C) 1992, 2001, 2003 Free Software Foundation, Inc.
+ Written by James Clark (jjc@jclark.com)
+
+This file is part of groff.
+
+groff is free software; you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free
+Software Foundation; either version 2, or (at your option) any later
+version.
+
+groff is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+for more details.
+
+You should have received a copy of the GNU General Public License along
+with groff; see the file COPYING. If not, write to the Free Software
+Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
+
+/* file_name_max(dir) does the same as pathconf(dir, _PC_NAME_MAX) */
+
+#include "lib.h"
+
+#include <sys/types.h>
+
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif /* HAVE_UNISTD_H */
+
+#ifdef _POSIX_VERSION
+
+size_t file_name_max(const char *fname)
+{
+ return pathconf(fname, _PC_NAME_MAX);
+}
+
+#else /* not _POSIX_VERSION */
+
+#ifdef HAVE_CC_LIMITS_H
+#include <limits.h>
+#endif /* HAVE_CC_LIMITS_H */
+
+#ifdef HAVE_DIRENT_H
+#include <dirent.h>
+#else /* not HAVE_DIRENT_H */
+#ifdef HAVE_SYS_DIR_H
+#include <sys/dir.h>
+#endif /* HAVE_SYS_DIR_H */
+#endif /* not HAVE_DIRENT_H */
+
+#ifndef NAME_MAX
+#ifdef MAXNAMLEN
+#define NAME_MAX MAXNAMLEN
+#else /* !MAXNAMLEN */
+#ifdef MAXNAMELEN
+#define NAME_MAX MAXNAMELEN
+#else /* !MAXNAMELEN */
+#define NAME_MAX 14
+#endif /* !MAXNAMELEN */
+#endif /* !MAXNAMLEN */
+#endif /* !NAME_MAX */
+
+size_t file_name_max(const char *)
+{
+ return NAME_MAX;
+}
+
+#endif /* not _POSIX_VERSION */
diff --git a/contrib/groff/src/libs/libgroff/mksdir.cpp b/contrib/groff/src/libs/libgroff/mksdir.cpp
new file mode 100644
index 0000000..bf4d300
--- /dev/null
+++ b/contrib/groff/src/libs/libgroff/mksdir.cpp
@@ -0,0 +1,34 @@
+/* Copyright (C) 2001 Free Software Foundation, Inc.
+ Written by Werner Lemberg (wl@gnu.org)
+
+This file is part of groff.
+
+groff is free software; you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free
+Software Foundation; either version 2, or (at your option) any later
+version.
+
+groff is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+for more details.
+
+You should have received a copy of the GNU General Public License along
+with groff; see the file COPYING. If not, write to the Free Software
+Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
+
+
+/* This file is heavily based on the file mkstemp.c which is part of the
+ fileutils package. */
+
+
+extern int gen_tempname(char *, int = 0);
+
+/* Generate a unique temporary directory name from TEMPLATE.
+ The last six characters of TEMPLATE must be "XXXXXX";
+ they are replaced with a string that makes the filename unique.
+ Then open the directory and return a fd. */
+int mksdir(char *tmpl)
+{
+ return gen_tempname(tmpl, 1);
+}
diff --git a/contrib/groff/src/libs/libgroff/mkstemp.cpp b/contrib/groff/src/libs/libgroff/mkstemp.cpp
new file mode 100644
index 0000000..cd2717c
--- /dev/null
+++ b/contrib/groff/src/libs/libgroff/mkstemp.cpp
@@ -0,0 +1,34 @@
+/* Copyright (C) 2001 Free Software Foundation, Inc.
+ Written by Werner Lemberg (wl@gnu.org)
+
+This file is part of groff.
+
+groff is free software; you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free
+Software Foundation; either version 2, or (at your option) any later
+version.
+
+groff is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+for more details.
+
+You should have received a copy of the GNU General Public License along
+with groff; see the file COPYING. If not, write to the Free Software
+Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
+
+
+/* This file is heavily based on the file mkstemp.c which is part of the
+ fileutils package. */
+
+
+extern int gen_tempname(char *, int);
+
+/* Generate a unique temporary file name from TEMPLATE.
+ The last six characters of TEMPLATE must be "XXXXXX";
+ they are replaced with a string that makes the filename unique.
+ Then open the file and return a fd. */
+int mkstemp(char *tmpl)
+{
+ return gen_tempname(tmpl, 0);
+}
diff --git a/contrib/groff/src/libs/libgroff/nametoindex.cpp b/contrib/groff/src/libs/libgroff/nametoindex.cpp
new file mode 100644
index 0000000..def26b3
--- /dev/null
+++ b/contrib/groff/src/libs/libgroff/nametoindex.cpp
@@ -0,0 +1,117 @@
+// -*- C++ -*-
+/* Copyright (C) 1989, 1990, 1991, 1992, 2000, 2001, 2002, 2003
+ Free Software Foundation, Inc.
+ Written by James Clark (jjc@jclark.com)
+
+This file is part of groff.
+
+groff is free software; you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free
+Software Foundation; either version 2, or (at your option) any later
+version.
+
+groff is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+for more details.
+
+You should have received a copy of the GNU General Public License along
+with groff; see the file COPYING. If not, write to the Free Software
+Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
+
+#include "lib.h"
+
+#include <ctype.h>
+#include <assert.h>
+#include <stdlib.h>
+#include "errarg.h"
+#include "error.h"
+#include "font.h"
+#include "ptable.h"
+
+declare_ptable(int)
+implement_ptable(int)
+
+class character_indexer {
+public:
+ character_indexer();
+ ~character_indexer();
+ int ascii_char_index(unsigned char);
+ int named_char_index(const char *);
+ int numbered_char_index(int);
+private:
+ enum { NSMALL = 256 };
+ int next_index;
+ int ascii_index[256];
+ int small_number_index[NSMALL];
+ PTABLE(int) table;
+};
+
+character_indexer::character_indexer()
+: next_index(0)
+{
+ int i;
+ for (i = 0; i < 256; i++)
+ ascii_index[i] = -1;
+ for (i = 0; i < NSMALL; i++)
+ small_number_index[i] = -1;
+}
+
+character_indexer::~character_indexer()
+{
+}
+
+int character_indexer::ascii_char_index(unsigned char c)
+{
+ if (ascii_index[c] < 0)
+ ascii_index[c] = next_index++;
+ return ascii_index[c];
+}
+
+int character_indexer::numbered_char_index(int n)
+{
+ if (n >= 0 && n < NSMALL) {
+ if (small_number_index[n] < 0)
+ small_number_index[n] = next_index++;
+ return small_number_index[n];
+ }
+ // Not the most efficient possible implementation.
+ char buf[INT_DIGITS + 3];
+ buf[0] = ' ';
+ strcpy(buf + 1, i_to_a(n));
+ return named_char_index(buf);
+}
+
+int character_indexer::named_char_index(const char *s)
+{
+ int *np = table.lookup(s);
+ if (!np) {
+ np = new int[1];
+ *np = next_index++;
+ table.define(s, np);
+ }
+ return *np;
+}
+
+static character_indexer indexer;
+
+int font::number_to_index(int n)
+{
+ return indexer.numbered_char_index(n);
+}
+
+int font::name_to_index(const char *s)
+{
+ assert(s != 0 && s[0] != '\0' && s[0] != ' ');
+ if (s[1] == '\0')
+ return indexer.ascii_char_index(s[0]);
+ /* char128 and \200 are synonyms */
+ if (s[0] == 'c' && s[1] == 'h' && s[2] == 'a' && s[3] == 'r') {
+ char *res;
+ long n = strtol(s + 4, &res, 10);
+ if (res != s + 4 && *res == '\0' && n >= 0 && n < 256)
+ return indexer.ascii_char_index((unsigned char)n);
+ }
+ return indexer.named_char_index(s);
+}
+
diff --git a/contrib/groff/src/libs/libgroff/new.cpp b/contrib/groff/src/libs/libgroff/new.cpp
new file mode 100644
index 0000000..739cffa
--- /dev/null
+++ b/contrib/groff/src/libs/libgroff/new.cpp
@@ -0,0 +1,71 @@
+/* Copyright (C) 1989, 1990, 1991, 1992, 2001, 2003
+ Free Software Foundation, Inc.
+ Written by James Clark (jjc@jclark.com)
+
+This file is part of groff.
+
+groff is free software; you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free
+Software Foundation; either version 2, or (at your option) any later
+version.
+
+groff is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+for more details.
+
+You should have received a copy of the GNU General Public License along
+with groff; see the file COPYING. If not, write to the Free Software
+Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
+
+#include "lib.h"
+
+#include <stddef.h>
+#include <stdlib.h>
+
+#include "posix.h"
+#include "nonposix.h"
+
+extern const char *program_name;
+
+static void ewrite(const char *s)
+{
+ write(2, s, strlen(s));
+}
+
+void *operator new(size_t size)
+{
+ // Avoid relying on the behaviour of malloc(0).
+ if (size == 0)
+ size++;
+#ifdef COOKIE_BUG
+ char *p = (char *)malloc(unsigned(size + 8));
+#else /* not COOKIE_BUG */
+ char *p = (char *)malloc(unsigned(size));
+#endif /* not COOKIE_BUG */
+ if (p == 0) {
+ if (program_name) {
+ ewrite(program_name);
+ ewrite(": ");
+ }
+ ewrite("out of memory\n");
+ _exit(-1);
+ }
+#ifdef COOKIE_BUG
+ ((unsigned *)p)[1] = 0;
+ return p + 8;
+#else /* not COOKIE_BUG */
+ return p;
+#endif /* not COOKIE_BUG */
+}
+
+void operator delete(void *p)
+{
+#ifdef COOKIE_BUG
+ if (p)
+ free((void *)((char *)p - 8));
+#else
+ if (p)
+ free(p);
+#endif /* COOKIE_BUG */
+}
diff --git a/contrib/groff/src/libs/libgroff/paper.cpp b/contrib/groff/src/libs/libgroff/paper.cpp
new file mode 100644
index 0000000..db8f5bc
--- /dev/null
+++ b/contrib/groff/src/libs/libgroff/paper.cpp
@@ -0,0 +1,84 @@
+// -*- C++ -*-
+/* Copyright (C) 2002, 2003
+ Free Software Foundation, Inc.
+ Written by Werner Lemberg (wl@gnu.org)
+
+This file is part of groff.
+
+groff is free software; you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free
+Software Foundation; either version 2, or (at your option) any later
+version.
+
+groff is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+for more details.
+
+You should have received a copy of the GNU General Public License along
+with groff; see the file COPYING. If not, write to the Free Software
+Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
+
+#include "lib.h"
+#include "paper.h"
+
+paper papersizes[NUM_PAPERSIZES];
+
+// length and width in mm
+static void add_iso_paper(char series, int offset,
+ int start_length, int start_width)
+{
+ int length = start_length;
+ int width = start_width;
+ for (int i = 0; i < 8; i++)
+ {
+ char *p = new char[3];
+ p[0] = series;
+ p[1] = '0' + i;
+ p[2] = '\0';
+ papersizes[offset + i].name = p;
+ // convert mm to inch
+ papersizes[offset + i].length = (double)length / 25.4;
+ papersizes[offset + i].width = (double)width / 25.4;
+ // after division by two, values must be rounded down to the next
+ // integer (as specified by ISO)
+ int tmp = length;
+ length = width;
+ width = tmp / 2;
+ }
+}
+
+// length and width in inch
+static void add_american_paper(const char *name, int index,
+ double length, double width )
+{
+ char *p = new char[strlen(name) + 1];
+ strcpy(p, name);
+ papersizes[index].name = p;
+ papersizes[index].length = length;
+ papersizes[index].width = width;
+}
+
+int papersize_init::initialised = 0;
+
+papersize_init::papersize_init()
+{
+ if (initialised)
+ return;
+ initialised = 1;
+ add_iso_paper('a', 0, 1189, 841);
+ add_iso_paper('b', 8, 1414, 1000);
+ add_iso_paper('c', 16, 1297, 917);
+ add_iso_paper('d', 24, 1090, 771);
+ add_american_paper("letter", 32, 11, 8.5);
+ add_american_paper("legal", 33, 14, 8.5);
+ add_american_paper("tabloid", 34, 17, 11);
+ add_american_paper("ledger", 35, 11, 17);
+ add_american_paper("statement", 36, 8.5, 5.5);
+ add_american_paper("executive", 37, 10, 7.5);
+ // the next three entries are for grolj4
+ add_american_paper("com10", 38, 9.5, 4.125);
+ add_american_paper("monarch", 39, 7.5, 3.875);
+ // this is an ISO format, but it easier to use add_american_paper
+ add_american_paper("dl", 40, 220/25.4, 110/25.4);
+}
diff --git a/contrib/groff/src/libs/libgroff/prime.cpp b/contrib/groff/src/libs/libgroff/prime.cpp
new file mode 100644
index 0000000..f0b1ead
--- /dev/null
+++ b/contrib/groff/src/libs/libgroff/prime.cpp
@@ -0,0 +1,26 @@
+#include <math.h>
+
+int is_prime(unsigned n)
+{
+ if (n <= 3)
+ return 1;
+ if (!(n & 1))
+ return 0;
+ if (n % 3 == 0)
+ return 0;
+ unsigned lim = unsigned(sqrt((double)n));
+ unsigned d = 5;
+ for (;;) {
+ if (d > lim)
+ break;
+ if (n % d == 0)
+ return 0;
+ d += 2;
+ if (d > lim)
+ break;
+ if (n % d == 0)
+ return 0;
+ d += 4;
+ }
+ return 1;
+}
diff --git a/contrib/groff/src/libs/libgroff/progname.cpp b/contrib/groff/src/libs/libgroff/progname.cpp
new file mode 100644
index 0000000..a70e341
--- /dev/null
+++ b/contrib/groff/src/libs/libgroff/progname.cpp
@@ -0,0 +1 @@
+const char *program_name = 0;
diff --git a/contrib/groff/src/libs/libgroff/ptable.cpp b/contrib/groff/src/libs/libgroff/ptable.cpp
new file mode 100644
index 0000000..76735c2
--- /dev/null
+++ b/contrib/groff/src/libs/libgroff/ptable.cpp
@@ -0,0 +1,52 @@
+/* Copyright (C) 1989, 1990, 1991, 1992 Free Software Foundation, Inc.
+ Written by James Clark (jjc@jclark.com)
+
+This file is part of groff.
+
+groff is free software; you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free
+Software Foundation; either version 2, or (at your option) any later
+version.
+
+groff is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+for more details.
+
+You should have received a copy of the GNU General Public License along
+with groff; see the file COPYING. If not, write to the Free Software
+Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
+
+#include "ptable.h"
+#include "errarg.h"
+#include "error.h"
+
+unsigned long hash_string(const char *s)
+{
+ assert(s != 0);
+ unsigned long h = 0, g;
+ while (*s != 0) {
+ h <<= 4;
+ h += *s++;
+ if ((g = h & 0xf0000000) != 0) {
+ h ^= g >> 24;
+ h ^= g;
+ }
+ }
+ return h;
+}
+
+static const unsigned table_sizes[] = {
+101, 503, 1009, 2003, 3001, 4001, 5003, 10007, 20011, 40009,
+80021, 160001, 500009, 1000003, 2000003, 4000037, 8000009,
+16000057, 32000011, 64000031, 128000003, 0
+};
+
+unsigned next_ptable_size(unsigned n)
+{
+ const unsigned *p;
+ for (p = table_sizes; *p <= n; p++)
+ if (*p == 0)
+ fatal("cannot expand table");
+ return *p;
+}
diff --git a/contrib/groff/src/libs/libgroff/searchpath.cpp b/contrib/groff/src/libs/libgroff/searchpath.cpp
new file mode 100644
index 0000000..1f8b233
--- /dev/null
+++ b/contrib/groff/src/libs/libgroff/searchpath.cpp
@@ -0,0 +1,132 @@
+// -*- C++ -*-
+/* Copyright (C) 1989, 1990, 1991, 1992, 2000, 2001
+ Free Software Foundation, Inc.
+ Written by James Clark (jjc@jclark.com)
+
+This file is part of groff.
+
+groff is free software; you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free
+Software Foundation; either version 2, or (at your option) any later
+version.
+
+groff is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+for more details.
+
+You should have received a copy of the GNU General Public License along
+with groff; see the file COPYING. If not, write to the Free Software
+Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
+
+#include "lib.h"
+
+#include <stdlib.h>
+#include <assert.h>
+
+#include "searchpath.h"
+#include "nonposix.h"
+
+search_path::search_path(const char *envvar, const char *standard,
+ int add_home, int add_current)
+{
+ char *home = 0;
+ if (add_home)
+ home = getenv("HOME");
+ char *e = 0;
+ if (envvar)
+ e = getenv(envvar);
+ dirs = new char[((e && *e) ? strlen(e) + 1 : 0)
+ + (add_current ? 1 + 1 : 0)
+ + ((home && *home) ? strlen(home) + 1 : 0)
+ + ((standard && *standard) ? strlen(standard) : 0)
+ + 1];
+ *dirs = '\0';
+ if (e && *e) {
+ strcat(dirs, e);
+ strcat(dirs, PATH_SEP);
+ }
+ if (add_current) {
+ strcat(dirs, ".");
+ strcat(dirs, PATH_SEP);
+ }
+ if (home && *home) {
+ strcat(dirs, home);
+ strcat(dirs, PATH_SEP);
+ }
+ if (standard && *standard)
+ strcat(dirs, standard);
+ init_len = strlen(dirs);
+}
+
+search_path::~search_path()
+{
+ // dirs is always allocated
+ a_delete dirs;
+}
+
+void search_path::command_line_dir(const char *s)
+{
+ char *old = dirs;
+ unsigned old_len = strlen(old);
+ unsigned slen = strlen(s);
+ dirs = new char[old_len + 1 + slen + 1];
+ memcpy(dirs, old, old_len - init_len);
+ char *p = dirs;
+ p += old_len - init_len;
+ if (init_len == 0)
+ *p++ = PATH_SEP[0];
+ memcpy(p, s, slen);
+ p += slen;
+ if (init_len > 0) {
+ *p++ = PATH_SEP[0];
+ memcpy(p, old + old_len - init_len, init_len);
+ p += init_len;
+ }
+ *p++ = '\0';
+ a_delete old;
+}
+
+FILE *search_path::open_file(const char *name, char **pathp)
+{
+ assert(name != 0);
+ if (IS_ABSOLUTE(name) || *dirs == '\0') {
+ FILE *fp = fopen(name, "r");
+ if (fp) {
+ if (pathp)
+ *pathp = strsave(name);
+ return fp;
+ }
+ else
+ return 0;
+ }
+ unsigned namelen = strlen(name);
+ char *p = dirs;
+ for (;;) {
+ char *end = strchr(p, PATH_SEP[0]);
+ if (!end)
+ end = strchr(p, '\0');
+ int need_slash = end > p && strchr(DIR_SEPS, end[-1]) == 0;
+ char *path = new char[(end - p) + need_slash + namelen + 1];
+ memcpy(path, p, end - p);
+ if (need_slash)
+ path[end - p] = '/';
+ strcpy(path + (end - p) + need_slash, name);
+#if 0
+ fprintf(stderr, "trying `%s'\n", path);
+#endif
+ FILE *fp = fopen(path, "r");
+ if (fp) {
+ if (pathp)
+ *pathp = path;
+ else
+ a_delete path;
+ return fp;
+ }
+ a_delete path;
+ if (*end == '\0')
+ break;
+ p = end + 1;
+ }
+ return 0;
+}
diff --git a/contrib/groff/src/libs/libgroff/strcasecmp.c b/contrib/groff/src/libs/libgroff/strcasecmp.c
new file mode 100644
index 0000000..ae7601d
--- /dev/null
+++ b/contrib/groff/src/libs/libgroff/strcasecmp.c
@@ -0,0 +1,66 @@
+/* strcasecmp.c -- case insensitive string comparator
+ Copyright (C) 1998, 1999 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software Foundation,
+ Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
+
+#if HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#ifdef LENGTH_LIMIT
+# define STRXCASECMP_FUNCTION strncasecmp
+# define STRXCASECMP_DECLARE_N , size_t n
+# define LENGTH_LIMIT_EXPR(Expr) Expr
+#else
+# define STRXCASECMP_FUNCTION strcasecmp
+# define STRXCASECMP_DECLARE_N /* empty */
+# define LENGTH_LIMIT_EXPR(Expr) 0
+#endif
+
+#include <sys/types.h>
+#include <ctype.h>
+
+#define TOLOWER(Ch) (isupper (Ch) ? tolower (Ch) : (Ch))
+
+/* Compare {{no more than N characters of }}strings S1 and S2,
+ ignoring case, returning less than, equal to or
+ greater than zero if S1 is lexicographically less
+ than, equal to or greater than S2. */
+
+int
+STRXCASECMP_FUNCTION (const char *s1, const char *s2 STRXCASECMP_DECLARE_N)
+{
+ register const unsigned char *p1 = (const unsigned char *) s1;
+ register const unsigned char *p2 = (const unsigned char *) s2;
+ unsigned char c1, c2;
+
+ if (p1 == p2 || LENGTH_LIMIT_EXPR (n == 0))
+ return 0;
+
+ do
+ {
+ c1 = TOLOWER (*p1);
+ c2 = TOLOWER (*p2);
+
+ if (LENGTH_LIMIT_EXPR (--n == 0) || c1 == '\0')
+ break;
+
+ ++p1;
+ ++p2;
+ }
+ while (c1 == c2);
+
+ return c1 - c2;
+}
diff --git a/contrib/groff/src/libs/libgroff/string.cpp b/contrib/groff/src/libs/libgroff/string.cpp
new file mode 100644
index 0000000..2ef547e
--- /dev/null
+++ b/contrib/groff/src/libs/libgroff/string.cpp
@@ -0,0 +1,341 @@
+// -*- C++ -*-
+/* Copyright (C) 1989, 1990, 1991, 1992, 2001, 2002
+ Free Software Foundation, Inc.
+ Written by James Clark (jjc@jclark.com)
+
+This file is part of groff.
+
+groff is free software; you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free
+Software Foundation; either version 2, or (at your option) any later
+version.
+
+groff is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+for more details.
+
+You should have received a copy of the GNU General Public License along
+with groff; see the file COPYING. If not, write to the Free Software
+Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
+
+#include "lib.h"
+
+#include "stringclass.h"
+
+static char *salloc(int len, int *sizep);
+static void sfree(char *ptr, int size);
+static char *sfree_alloc(char *ptr, int size, int len, int *sizep);
+static char *srealloc(char *ptr, int size, int oldlen, int newlen, int *sizep);
+
+static char *salloc(int len, int *sizep)
+{
+ if (len == 0) {
+ *sizep = 0;
+ return 0;
+ }
+ else
+ return new char[*sizep = len*2];
+}
+
+static void sfree(char *ptr, int)
+{
+ a_delete ptr;
+}
+
+static char *sfree_alloc(char *ptr, int oldsz, int len, int *sizep)
+{
+ if (oldsz >= len) {
+ *sizep = oldsz;
+ return ptr;
+ }
+ a_delete ptr;
+ if (len == 0) {
+ *sizep = 0;
+ return 0;
+ }
+ else
+ return new char[*sizep = len*2];
+}
+
+static char *srealloc(char *ptr, int oldsz, int oldlen, int newlen, int *sizep)
+{
+ if (oldsz >= newlen) {
+ *sizep = oldsz;
+ return ptr;
+ }
+ if (newlen == 0) {
+ a_delete ptr;
+ *sizep = 0;
+ return 0;
+ }
+ else {
+ char *p = new char[*sizep = newlen*2];
+ if (oldlen < newlen && oldlen != 0)
+ memcpy(p, ptr, oldlen);
+ a_delete ptr;
+ return p;
+ }
+}
+
+string::string() : ptr(0), len(0), sz(0)
+{
+}
+
+string::string(const char *p, int n) : len(n)
+{
+ assert(n >= 0);
+ ptr = salloc(n, &sz);
+ if (n != 0)
+ memcpy(ptr, p, n);
+}
+
+string::string(const char *p)
+{
+ if (p == 0) {
+ len = 0;
+ ptr = 0;
+ sz = 0;
+ }
+ else {
+ len = strlen(p);
+ ptr = salloc(len, &sz);
+ memcpy(ptr, p, len);
+ }
+}
+
+string::string(char c) : len(1)
+{
+ ptr = salloc(1, &sz);
+ *ptr = c;
+}
+
+string::string(const string &s) : len(s.len)
+{
+ ptr = salloc(len, &sz);
+ if (len != 0)
+ memcpy(ptr, s.ptr, len);
+}
+
+string::~string()
+{
+ sfree(ptr, sz);
+}
+
+string &string::operator=(const string &s)
+{
+ ptr = sfree_alloc(ptr, sz, s.len, &sz);
+ len = s.len;
+ if (len != 0)
+ memcpy(ptr, s.ptr, len);
+ return *this;
+}
+
+string &string::operator=(const char *p)
+{
+ if (p == 0) {
+ sfree(ptr, len);
+ len = 0;
+ ptr = 0;
+ sz = 0;
+ }
+ else {
+ int slen = strlen(p);
+ ptr = sfree_alloc(ptr, sz, slen, &sz);
+ len = slen;
+ memcpy(ptr, p, len);
+ }
+ return *this;
+}
+
+string &string::operator=(char c)
+{
+ ptr = sfree_alloc(ptr, sz, 1, &sz);
+ len = 1;
+ *ptr = c;
+ return *this;
+}
+
+void string::move(string &s)
+{
+ sfree(ptr, sz);
+ ptr = s.ptr;
+ len = s.len;
+ sz = s.sz;
+ s.ptr = 0;
+ s.len = 0;
+ s.sz = 0;
+}
+
+void string::grow1()
+{
+ ptr = srealloc(ptr, sz, len, len + 1, &sz);
+}
+
+string &string::operator+=(const char *p)
+{
+ if (p != 0) {
+ int n = strlen(p);
+ int newlen = len + n;
+ if (newlen > sz)
+ ptr = srealloc(ptr, sz, len, newlen, &sz);
+ memcpy(ptr + len, p, n);
+ len = newlen;
+ }
+ return *this;
+}
+
+string &string::operator+=(const string &s)
+{
+ if (s.len != 0) {
+ int newlen = len + s.len;
+ if (newlen > sz)
+ ptr = srealloc(ptr, sz, len, newlen, &sz);
+ memcpy(ptr + len, s.ptr, s.len);
+ len = newlen;
+ }
+ return *this;
+}
+
+void string::append(const char *p, int n)
+{
+ if (n > 0) {
+ int newlen = len + n;
+ if (newlen > sz)
+ ptr = srealloc(ptr, sz, len, newlen, &sz);
+ memcpy(ptr + len, p, n);
+ len = newlen;
+ }
+}
+
+string::string(const char *s1, int n1, const char *s2, int n2)
+{
+ assert(n1 >= 0 && n2 >= 0);
+ len = n1 + n2;
+ if (len == 0) {
+ sz = 0;
+ ptr = 0;
+ }
+ else {
+ ptr = salloc(len, &sz);
+ if (n1 == 0)
+ memcpy(ptr, s2, n2);
+ else {
+ memcpy(ptr, s1, n1);
+ if (n2 != 0)
+ memcpy(ptr + n1, s2, n2);
+ }
+ }
+}
+
+int operator<=(const string &s1, const string &s2)
+{
+ return (s1.len <= s2.len
+ ? s1.len == 0 || memcmp(s1.ptr, s2.ptr, s1.len) <= 0
+ : s2.len != 0 && memcmp(s1.ptr, s2.ptr, s2.len) < 0);
+}
+
+int operator<(const string &s1, const string &s2)
+{
+ return (s1.len < s2.len
+ ? s1.len == 0 || memcmp(s1.ptr, s2.ptr, s1.len) <= 0
+ : s2.len != 0 && memcmp(s1.ptr, s2.ptr, s2.len) < 0);
+}
+
+int operator>=(const string &s1, const string &s2)
+{
+ return (s1.len >= s2.len
+ ? s2.len == 0 || memcmp(s1.ptr, s2.ptr, s2.len) >= 0
+ : s1.len != 0 && memcmp(s1.ptr, s2.ptr, s1.len) > 0);
+}
+
+int operator>(const string &s1, const string &s2)
+{
+ return (s1.len > s2.len
+ ? s2.len == 0 || memcmp(s1.ptr, s2.ptr, s2.len) >= 0
+ : s1.len != 0 && memcmp(s1.ptr, s2.ptr, s1.len) > 0);
+}
+
+void string::set_length(int i)
+{
+ assert(i >= 0);
+ if (i > sz)
+ ptr = srealloc(ptr, sz, len, i, &sz);
+ len = i;
+}
+
+void string::clear()
+{
+ len = 0;
+}
+
+int string::search(char c) const
+{
+ char *p = ptr ? (char *)memchr(ptr, c, len) : NULL;
+ return p ? p - ptr : -1;
+}
+
+// we silently strip nuls
+
+char *string::extract() const
+{
+ char *p = ptr;
+ int n = len;
+ int nnuls = 0;
+ int i;
+ for (i = 0; i < n; i++)
+ if (p[i] == '\0')
+ nnuls++;
+ char *q = new char[n + 1 - nnuls];
+ char *r = q;
+ for (i = 0; i < n; i++)
+ if (p[i] != '\0')
+ *r++ = p[i];
+ *r = '\0';
+ return q;
+}
+
+void string::remove_spaces()
+{
+ int l = len - 1;
+ while (l >= 0 && ptr[l] == ' ')
+ l--;
+ char *p = ptr;
+ if (l > 0)
+ while (*p == ' ') {
+ p++;
+ l--;
+ }
+ if (len - 1 != l) {
+ if (l >= 0) {
+ len = l + 1;
+ char *tmp = new char[len];
+ memcpy(tmp, p, len);
+ a_delete ptr;
+ ptr = tmp;
+ }
+ else {
+ len = 0;
+ if (ptr) {
+ a_delete ptr;
+ ptr = 0;
+ }
+ }
+ }
+}
+
+void put_string(const string &s, FILE *fp)
+{
+ int len = s.length();
+ const char *ptr = s.contents();
+ for (int i = 0; i < len; i++)
+ putc(ptr[i], fp);
+}
+
+string as_string(int i)
+{
+ static char buf[INT_DIGITS + 2];
+ sprintf(buf, "%d", i);
+ return string(buf);
+}
+
diff --git a/contrib/groff/src/libs/libgroff/strncasecmp.c b/contrib/groff/src/libs/libgroff/strncasecmp.c
new file mode 100644
index 0000000..68d95aa
--- /dev/null
+++ b/contrib/groff/src/libs/libgroff/strncasecmp.c
@@ -0,0 +1,2 @@
+#define LENGTH_LIMIT
+#include "strcasecmp.c"
diff --git a/contrib/groff/src/libs/libgroff/strsave.cpp b/contrib/groff/src/libs/libgroff/strsave.cpp
new file mode 100644
index 0000000..dfd2b6f
--- /dev/null
+++ b/contrib/groff/src/libs/libgroff/strsave.cpp
@@ -0,0 +1,31 @@
+// -*- C++ -*-
+/* Copyright (C) 1989, 1990, 1991, 1992 Free Software Foundation, Inc.
+ Written by James Clark (jjc@jclark.com)
+
+This file is part of groff.
+
+groff is free software; you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free
+Software Foundation; either version 2, or (at your option) any later
+version.
+
+groff is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+for more details.
+
+You should have received a copy of the GNU General Public License along
+with groff; see the file COPYING. If not, write to the Free Software
+Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
+
+#include <string.h>
+
+char *strsave(const char *s)
+{
+ if (s == 0)
+ return 0;
+ char *p = new char[strlen(s) + 1];
+ strcpy(p, s);
+ return p;
+}
+
diff --git a/contrib/groff/src/libs/libgroff/tmpfile.cpp b/contrib/groff/src/libs/libgroff/tmpfile.cpp
new file mode 100644
index 0000000..41b7f06
--- /dev/null
+++ b/contrib/groff/src/libs/libgroff/tmpfile.cpp
@@ -0,0 +1,172 @@
+// -*- C++ -*-
+/* Copyright (C) 1989, 1990, 1991, 1992, 2000, 2001
+ Free Software Foundation, Inc.
+ Written by James Clark (jjc@jclark.com)
+
+This file is part of groff.
+
+groff is free software; you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free
+Software Foundation; either version 2, or (at your option) any later
+version.
+
+groff is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+for more details.
+
+You should have received a copy of the GNU General Public License along
+with groff; see the file COPYING. If not, write to the Free Software
+Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
+
+#include "lib.h"
+
+#include <errno.h>
+#include <stdlib.h>
+
+#include "posix.h"
+#include "errarg.h"
+#include "error.h"
+#include "nonposix.h"
+
+// If this is set, create temporary files there
+#define GROFF_TMPDIR_ENVVAR "GROFF_TMPDIR"
+// otherwise if this is set, create temporary files there
+#define TMPDIR_ENVVAR "TMPDIR"
+// otherwise if P_tmpdir is defined, create temporary files there
+#ifdef P_tmpdir
+# define DEFAULT_TMPDIR P_tmpdir
+#else
+// otherwise create temporary files here.
+# define DEFAULT_TMPDIR "/tmp"
+#endif
+// Use this as the prefix for temporary filenames.
+#define TMPFILE_PREFIX_SHORT ""
+#define TMPFILE_PREFIX_LONG "groff"
+
+char *tmpfile_prefix;
+size_t tmpfile_prefix_len;
+int use_short_postfix = 0;
+
+struct temp_init {
+ temp_init();
+ ~temp_init();
+} _temp_init;
+
+temp_init::temp_init()
+{
+ const char *tem = getenv(GROFF_TMPDIR_ENVVAR);
+ if (!tem) {
+ tem = getenv(TMPDIR_ENVVAR);
+ if (!tem)
+ tem = DEFAULT_TMPDIR;
+ }
+ size_t tem_len = strlen(tem);
+ const char *tem_end = tem + tem_len - 1;
+ int need_slash = strchr(DIR_SEPS, *tem_end) == NULL ? 1 : 0;
+ char *tem2 = new char[tem_len + need_slash + 1];
+ strcpy(tem2, tem);
+ if (need_slash)
+ strcat(tem2, "/");
+ const char *tem3 = TMPFILE_PREFIX_LONG;
+ if (file_name_max(tem2) <= 14) {
+ tem3 = TMPFILE_PREFIX_SHORT;
+ use_short_postfix = 1;
+ }
+ tmpfile_prefix_len = tem_len + need_slash + strlen(tem3);
+ tmpfile_prefix = new char[tmpfile_prefix_len + 1];
+ strcpy(tmpfile_prefix, tem2);
+ strcat(tmpfile_prefix, tem3);
+ a_delete tem2;
+}
+
+temp_init::~temp_init()
+{
+ a_delete tmpfile_prefix;
+}
+
+/*
+ * Generate a temporary name template with a postfix
+ * immediately after the TMPFILE_PREFIX.
+ * It uses the groff preferences for a temporary directory.
+ * Note that no file name is either created or opened,
+ * only the *template* is returned.
+ */
+
+char *xtmptemplate(const char *postfix_long, const char *postfix_short)
+{
+ const char *postfix = use_short_postfix ? postfix_short : postfix_long;
+ int postlen = 0;
+ if (postfix)
+ postlen = strlen(postfix);
+ char *templ = new char[tmpfile_prefix_len + postlen + 6 + 1];
+ strcpy(templ, tmpfile_prefix);
+ if (postlen > 0)
+ strcat(templ, postfix);
+ strcat(templ, "XXXXXX");
+ return templ;
+}
+
+// The trick with unlinking the temporary file while it is still in
+// use is not portable, it will fail on MS-DOS and most MS-Windows
+// filesystems. So it cannot be used on non-Posix systems.
+// Instead, we maintain a list of files to be deleted on exit.
+// This should be portable to all platforms.
+
+struct xtmpfile_list {
+ char *fname;
+ xtmpfile_list *next;
+ xtmpfile_list(char *fn) : fname(fn), next(0) {}
+};
+
+xtmpfile_list *xtmpfiles_to_delete = 0;
+
+struct xtmpfile_list_init {
+ ~xtmpfile_list_init();
+} _xtmpfile_list_init;
+
+xtmpfile_list_init::~xtmpfile_list_init()
+{
+ xtmpfile_list *x = xtmpfiles_to_delete;
+ while (x != 0) {
+ if (unlink(x->fname) < 0)
+ error("cannot unlink `%1': %2", x->fname, strerror(errno));
+ xtmpfile_list *tmp = x;
+ x = x->next;
+ a_delete tmp->fname;
+ delete tmp;
+ }
+}
+
+static void add_tmp_file(const char *name)
+{
+ char *s = new char[strlen(name)+1];
+ strcpy(s, name);
+ xtmpfile_list *x = new xtmpfile_list(s);
+ x->next = xtmpfiles_to_delete;
+ xtmpfiles_to_delete = x;
+}
+
+// Open a temporary file and with fatal error on failure.
+
+FILE *xtmpfile(char **namep,
+ const char *postfix_long, const char *postfix_short,
+ int do_unlink)
+{
+ char *templ = xtmptemplate(postfix_long, postfix_short);
+ errno = 0;
+ int fd = mkstemp(templ);
+ if (fd < 0)
+ fatal("cannot create temporary file: %1", strerror(errno));
+ errno = 0;
+ FILE *fp = fdopen(fd, FOPEN_RWB); // many callers of xtmpfile use binary I/O
+ if (!fp)
+ fatal("fdopen: %1", strerror(errno));
+ if (do_unlink)
+ add_tmp_file(templ);
+ if (namep)
+ *namep = templ;
+ else
+ a_delete templ;
+ return fp;
+}
diff --git a/contrib/groff/src/libs/libgroff/tmpname.cpp b/contrib/groff/src/libs/libgroff/tmpname.cpp
new file mode 100644
index 0000000..f867fe0
--- /dev/null
+++ b/contrib/groff/src/libs/libgroff/tmpname.cpp
@@ -0,0 +1,116 @@
+/* Copyright (C) 2001, 2003 Free Software Foundation, Inc.
+ Written by Werner Lemberg (wl@gnu.org)
+
+This file is part of groff.
+
+groff is free software; you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free
+Software Foundation; either version 2, or (at your option) any later
+version.
+
+groff is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+for more details.
+
+You should have received a copy of the GNU General Public License along
+with groff; see the file COPYING. If not, write to the Free Software
+Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
+
+
+/* This file is heavily based on the function __gen_tempname() in the
+ file tempname.c which is part of the fileutils package. */
+
+
+#include "lib.h"
+
+#include <stddef.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <time.h>
+
+#include "posix.h"
+#include "nonposix.h"
+
+#ifndef TMP_MAX
+# define TMP_MAX 238328
+#endif
+
+#if HAVE_SYS_TIME_H
+# include <sys/time.h>
+#endif
+
+#ifdef HAVE_GETTIMEOFDAY
+#ifdef NEED_DECLARATION_GETTIMEOFDAY
+extern "C" {
+ int gettimeofday(struct timeval *, void *);
+}
+#endif
+#endif
+
+#if HAVE_CC_STDINT_H
+# include <stdint.h>
+#endif
+
+/* Use the widest available unsigned type if uint64_t is not
+ available. The algorithm below extracts a number less than 62**6
+ (approximately 2**35.725) from uint64_t, so ancient hosts where
+ uintmax_t is only 32 bits lose about 3.725 bits of randomness,
+ which is better than not having mkstemp at all. */
+#if !defined UINT64_MAX && !defined uint64_t
+# define uint64_t uintmax_t
+#endif
+
+/* These are the characters used in temporary filenames. */
+static const char letters[] =
+"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
+
+int gen_tempname(char *tmpl, int dir)
+{
+ static uint64_t value;
+
+ size_t len = strlen(tmpl);
+ if (len < 6 || strcmp(&tmpl[len - 6], "XXXXXX"))
+ return -1; /* EINVAL */
+
+ /* This is where the Xs start. */
+ char *XXXXXX = &tmpl[len - 6];
+
+ /* Get some more or less random data. */
+#if HAVE_GETTIMEOFDAY
+ timeval tv;
+ gettimeofday(&tv, NULL);
+ uint64_t random_time_bits = ((uint64_t)tv.tv_usec << 16) ^ tv.tv_sec;
+#else
+ uint64_t random_time_bits = time(NULL);
+#endif
+ value += random_time_bits ^ getpid();
+
+ for (int count = 0; count < TMP_MAX; value += 7777, ++count) {
+ uint64_t v = value;
+
+ /* Fill in the random bits. */
+ XXXXXX[0] = letters[v % 62];
+ v /= 62;
+ XXXXXX[1] = letters[v % 62];
+ v /= 62;
+ XXXXXX[2] = letters[v % 62];
+ v /= 62;
+ XXXXXX[3] = letters[v % 62];
+ v /= 62;
+ XXXXXX[4] = letters[v % 62];
+ v /= 62;
+ XXXXXX[5] = letters[v % 62];
+
+ int fd = dir ? mkdir(tmpl, S_IRUSR | S_IWUSR | S_IXUSR)
+ : open(tmpl, O_RDWR | O_CREAT | O_EXCL, S_IRUSR | S_IWUSR);
+
+ if (fd >= 0)
+ return fd;
+ else if (errno != EEXIST)
+ return -1;
+ }
+
+ /* We got out of the loop because we ran out of combinations to try. */
+ return -1; /* EEXIST */
+}
OpenPOWER on IntegriCloud