summaryrefslogtreecommitdiffstats
path: root/src/libs
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-15 19:44:05 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-15 19:44:05 +0000
commitd318611dd6f23fcfedd50e9b9e24620b102ba96a (patch)
tree8b9eef82ca40fdd5a8deeabf07572074c236095d /src/libs
parentInitial commit. (diff)
downloadgroff-d318611dd6f23fcfedd50e9b9e24620b102ba96a.tar.xz
groff-d318611dd6f23fcfedd50e9b9e24620b102ba96a.zip
Adding upstream version 1.23.0.upstream/1.23.0upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/libs')
-rw-r--r--src/libs/libbib/common.cpp37
-rw-r--r--src/libs/libbib/index.cpp688
-rw-r--r--src/libs/libbib/libbib.am35
-rw-r--r--src/libs/libbib/linear.cpp506
-rw-r--r--src/libs/libbib/map.c86
-rw-r--r--src/libs/libbib/search.cpp136
-rw-r--r--src/libs/libdriver/input.cpp1841
-rw-r--r--src/libs/libdriver/libdriver.am31
-rw-r--r--src/libs/libdriver/printer.cpp268
-rw-r--r--src/libs/libgroff/assert.cpp38
-rw-r--r--src/libs/libgroff/change_lf.cpp37
-rw-r--r--src/libs/libgroff/cmap.cpp56
-rw-r--r--src/libs/libgroff/color.cpp404
-rwxr-xr-xsrc/libs/libgroff/config.charset684
-rw-r--r--src/libs/libgroff/cset.cpp104
-rw-r--r--src/libs/libgroff/curtime.cpp55
-rw-r--r--src/libs/libgroff/device.cpp39
-rw-r--r--src/libs/libgroff/errarg.cpp136
-rw-r--r--src/libs/libgroff/error.cpp185
-rw-r--r--src/libs/libgroff/fatal.cpp30
-rw-r--r--src/libs/libgroff/filename.cpp31
-rw-r--r--src/libs/libgroff/fmod.c27
-rw-r--r--src/libs/libgroff/font.cpp1321
-rw-r--r--src/libs/libgroff/fontfile.cpp80
-rw-r--r--src/libs/libgroff/geometry.cpp180
-rw-r--r--src/libs/libgroff/getcwd.c54
-rw-r--r--src/libs/libgroff/getopt.c1241
-rw-r--r--src/libs/libgroff/getopt1.c172
-rw-r--r--src/libs/libgroff/glyphuni.cpp523
-rw-r--r--src/libs/libgroff/htmlhint.cpp60
-rw-r--r--src/libs/libgroff/hypot.cpp34
-rw-r--r--src/libs/libgroff/iftoa.c76
-rw-r--r--src/libs/libgroff/invalid.cpp59
-rw-r--r--src/libs/libgroff/itoa.c67
-rw-r--r--src/libs/libgroff/lf.cpp78
-rw-r--r--src/libs/libgroff/libgroff.am182
-rw-r--r--src/libs/libgroff/lineno.cpp20
-rw-r--r--src/libs/libgroff/localcharset.c579
-rw-r--r--src/libs/libgroff/macropath.cpp29
-rwxr-xr-xsrc/libs/libgroff/make-uniuni162
-rw-r--r--src/libs/libgroff/matherr.c48
-rw-r--r--src/libs/libgroff/maxfilename.cpp75
-rw-r--r--src/libs/libgroff/maxpathname.cpp70
-rw-r--r--src/libs/libgroff/mksdir.cpp33
-rw-r--r--src/libs/libgroff/mkstemp.cpp33
-rw-r--r--src/libs/libgroff/nametoindex.cpp167
-rw-r--r--src/libs/libgroff/new.cpp75
-rw-r--r--src/libs/libgroff/paper.cpp82
-rw-r--r--src/libs/libgroff/prime.cpp55
-rw-r--r--src/libs/libgroff/progname.c18
-rw-r--r--src/libs/libgroff/ptable.cpp57
-rw-r--r--src/libs/libgroff/putenv.c97
-rw-r--r--src/libs/libgroff/quotearg.c213
-rw-r--r--src/libs/libgroff/ref-add.sin29
-rw-r--r--src/libs/libgroff/ref-del.sin24
-rw-r--r--src/libs/libgroff/relocatable.h19
-rw-r--r--src/libs/libgroff/relocate.cpp244
-rw-r--r--src/libs/libgroff/searchpath.cpp215
-rw-r--r--src/libs/libgroff/spawnvp.c120
-rw-r--r--src/libs/libgroff/strcasecmp.c65
-rw-r--r--src/libs/libgroff/strerror.c46
-rw-r--r--src/libs/libgroff/string.cpp354
-rw-r--r--src/libs/libgroff/strncasecmp.c19
-rw-r--r--src/libs/libgroff/strsave.cpp40
-rw-r--r--src/libs/libgroff/strtol.c131
-rw-r--r--src/libs/libgroff/symbol.cpp157
-rw-r--r--src/libs/libgroff/tmpfile.cpp188
-rw-r--r--src/libs/libgroff/tmpname.cpp117
-rw-r--r--src/libs/libgroff/unicode.cpp65
-rw-r--r--src/libs/libgroff/uniglyph.cpp497
-rw-r--r--src/libs/libgroff/uniuni.cpp2128
-rw-r--r--src/libs/libxutil/DviChar.c679
-rw-r--r--src/libs/libxutil/XFontName.c260
-rw-r--r--src/libs/libxutil/libxutil.am35
-rw-r--r--src/libs/libxutil/xmalloc.c28
75 files changed, 16854 insertions, 0 deletions
diff --git a/src/libs/libbib/common.cpp b/src/libs/libbib/common.cpp
new file mode 100644
index 0000000..05accd8
--- /dev/null
+++ b/src/libs/libbib/common.cpp
@@ -0,0 +1,37 @@
+/* Copyright (C) 1989-2020 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 3 of the License, 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 this program. If not, see <http://www.gnu.org/licenses/>. */
+
+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/src/libs/libbib/index.cpp b/src/libs/libbib/index.cpp
new file mode 100644
index 0000000..aebcbbf
--- /dev/null
+++ b/src/libs/libbib/index.cpp
@@ -0,0 +1,688 @@
+/* Copyright (C) 1989-2020 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 3 of the License, 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 this program. If not, see <http://www.gnu.org/licenses/>. */
+
+#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;
+
+bool do_verify = false;
+
+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 *get_invalidity_reason();
+ 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();
+ const char *check_header(index_header *, unsigned);
+ bool load(int fd);
+ search_item_iterator *make_search_item_iterator(const char *);
+ bool is_valid();
+ 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;
+ }
+ delete[] filename_buffer;
+ delete[] key_buffer;
+ if (common_words_table) {
+ for (int i = 0; i < common_words_table_size; i++)
+ delete[] common_words_table[i];
+ 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 *) { }
+
+// Validate the data reported in the header so that we don't overread on
+// the heap in the load() member function. Return null pointer if no
+// problems are detected.
+const char *index_search_item::check_header(index_header *file_header,
+ unsigned file_size)
+{
+ if (file_header->tags_size < 0)
+ return "tag list length negative";
+ if (file_header->lists_size < 0)
+ return "reference list length negative";
+ // The table and string pool sizes will not be zero, even in an empty
+ // index.
+ if (file_header->table_size < 1)
+ return "table size nonpositive";
+ if (file_header->strings_size < 1)
+ return "string pool size nonpositive";
+ size_t sz = (file_header->tags_size * sizeof(tag)
+ + file_header->lists_size * sizeof(int)
+ + file_header->table_size * sizeof(int)
+ + file_header->strings_size
+ + sizeof(file_header));
+ if (sz != file_size)
+ return("size mismatch between header and data");
+ unsigned size_remaining = file_size;
+ unsigned chunk_size = file_header->tags_size * sizeof(tag);
+ if (chunk_size > size_remaining)
+ return "claimed tag list length exceeds file size";
+ size_remaining -= chunk_size;
+ chunk_size = file_header->lists_size * sizeof(int);
+ if (chunk_size > size_remaining)
+ return "claimed reference list length exceeds file size";
+ size_remaining -= chunk_size;
+ chunk_size = file_header->table_size * sizeof(int);
+ if (chunk_size > size_remaining)
+ return "claimed table size exceeds file size";
+ size_remaining -= chunk_size;
+ chunk_size = file_header->strings_size;
+ if (chunk_size > size_remaining)
+ return "claimed string pool size exceeds file size";
+ return 0;
+}
+
+bool 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 index '%1': %2", name, strerror(errno));
+ return false;
+ }
+ if (!S_ISREG(sb.st_mode)) {
+ error("index '%1' is not a regular file", name);
+ return false;
+ }
+ mtime = sb.st_mtime;
+ unsigned size = unsigned(sb.st_size); // widening conversion
+ if (size == 0) {
+ error("index '%1' is an empty file", name);
+ return false;
+ }
+ 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 memory to process index '%1'", name);
+ return false;
+ }
+ 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 end-of-file while reading index '%1'", name);
+ return false;
+ }
+ if (nread < 0) {
+ error("read error on index '%1': %2", name, strerror(errno));
+ return false;
+ }
+ 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 false;
+ }
+ if (header.version != INDEX_VERSION) {
+ error("version number in index '%1' is wrong: was %2, should be %3",
+ name, header.version, INDEX_VERSION);
+ return false;
+ }
+ const char *problem = check_header(&header, size);
+ if (problem != 0) {
+ if (do_verify)
+ error("corrupt header in index file '%1': %2", name, problem);
+ else
+ error("corrupt header in index file '%1'", name);
+ return false;
+ }
+ 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 true;
+}
+
+const char *index_search_item::get_invalidity_reason()
+{
+ if (tags == 0)
+ return "not loaded";
+ if ((header.lists_size > 0) && (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 string pool is not null";
+ return 0;
+}
+
+bool index_search_item::is_valid()
+{
+ const char *reason = get_invalidity_reason();
+ if (!reason)
+ return true;
+ error("'%1' is bad: %2", name, reason);
+ return false;
+}
+
+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);
+ delete[] index_filename;
+ if (!item->load(fd)) {
+ close(fd);
+ delete item;
+ return 0;
+ }
+ else if (do_verify && !item->is_valid()) {
+ 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()
+{
+ delete[] temp_list;
+ delete[] buf;
+ 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 &searchr,
+ 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) {
+ 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) {
+ 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 = searchr.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) {
+ 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) {
+ 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) {
+ delete[] matches;
+ return list;
+ }
+ merge(matches, matches, list);
+ if (*matches < 0) {
+ 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 += 2;
+ 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 index '%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));
+ }
+ }
+}
+
+// Local Variables:
+// fill-column: 72
+// mode: C++
+// End:
+// vim: set cindent noexpandtab shiftwidth=2 textwidth=72:
diff --git a/src/libs/libbib/libbib.am b/src/libs/libbib/libbib.am
new file mode 100644
index 0000000..00e1d3b
--- /dev/null
+++ b/src/libs/libbib/libbib.am
@@ -0,0 +1,35 @@
+# Automake rules for 'libbib'
+#
+# Copyright (C) 2014-2020 Free Software Foundation, Inc.
+#
+# '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 of the License, 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 this program. If not, see
+# <http://www.gnu.org/licenses/gpl-2.0.html>.
+#
+########################################################################
+
+noinst_LIBRARIES += libbib.a
+libbib_a_SOURCES = \
+ src/libs/libbib/common.cpp \
+ src/libs/libbib/index.cpp \
+ src/libs/libbib/linear.cpp \
+ src/libs/libbib/search.cpp \
+ src/libs/libbib/map.c
+src/libs/libbib/index.$(OBJEXT): defs.h
+
+
+# Local Variables:
+# mode: makefile-automake
+# fill-column: 72
+# End:
+# vim: set autoindent filetype=automake textwidth=72:
diff --git a/src/libs/libbib/linear.cpp b/src/libs/libbib/linear.cpp
new file mode 100644
index 0000000..22e11d8
--- /dev/null
+++ b/src/libs/libbib/linear.cpp
@@ -0,0 +1,506 @@
+/* Copyright (C) 1989-2020 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 3 of the License, 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 this program. If not, see <http://www.gnu.org/licenses/>. */
+
+#include "lib.h"
+
+#include <assert.h>
+#include <stdlib.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()
+{
+ 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()
+{
+ 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;
+ }
+ }
+ 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) {
+ delete[] keys;
+ keys = 0;
+ }
+}
+
+linear_searcher::~linear_searcher()
+{
+ for (int i = 0; i < nkeys; i++)
+ delete keys[i];
+ 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;
+}
+
+// Local Variables:
+// fill-column: 72
+// mode: C++
+// End:
+// vim: set cindent noexpandtab shiftwidth=2 textwidth=72:
diff --git a/src/libs/libbib/map.c b/src/libs/libbib/map.c
new file mode 100644
index 0000000..9eae72b
--- /dev/null
+++ b/src/libs/libbib/map.c
@@ -0,0 +1,86 @@
+/* Copyright (C) 1989- 2014 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 3 of the License, 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 this program. If not, see <http://www.gnu.org/licenses/>. */
+
+#include <config.h>
+
+#include <stdlib.h>
+
+#ifdef HAVE_MMAP
+
+#include <sys/types.h>
+#include <sys/mman.h>
+
+/* The Net-2 man pages says that a MAP_FILE flag is required. */
+#ifndef MAP_FILE
+#define MAP_FILE 0
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* Prototypes */
+char *mapread(int, int);
+int unmap(char *, int);
+
+char *mapread(int fd, int nbytes)
+{
+ char *p = (char *)mmap((void *)0, (size_t)nbytes, PROT_READ,
+ MAP_FILE|MAP_PRIVATE, fd, (off_t)0);
+ if (p == MAP_FAILED)
+ return 0;
+ /* mmap() shouldn't return 0 since MAP_FIXED wasn't specified. */
+ if (p == 0)
+ abort();
+ return p;
+}
+
+int unmap(char *p, int len)
+{
+ return munmap((void *)p, len);
+}
+
+#ifdef __cplusplus
+}
+#endif
+
+#else /* not HAVE_MMAP */
+
+#include <errno.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+char *mapread(int fd, int nbytes)
+{
+ errno = ENODEV;
+ return 0;
+}
+
+int unmap(char *p, int len)
+{
+ errno = EINVAL;
+ return -1;
+}
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* not HAVE_MMAP */
diff --git a/src/libs/libbib/search.cpp b/src/libs/libbib/search.cpp
new file mode 100644
index 0000000..10867b6
--- /dev/null
+++ b/src/libs/libbib/search.cpp
@@ -0,0 +1,136 @@
+/* Copyright (C) 1989-2020 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 3 of the License, 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 this program. If not, see <http://www.gnu.org/licenses/>. */
+
+#include "lib.h"
+
+#include <assert.h>
+#include <stdlib.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;
+ 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()
+{
+ 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()
+{
+}
+
+// Local Variables:
+// fill-column: 72
+// mode: C++
+// End:
+// vim: set cindent noexpandtab shiftwidth=2 textwidth=72:
diff --git a/src/libs/libdriver/input.cpp b/src/libs/libdriver/input.cpp
new file mode 100644
index 0000000..821b526
--- /dev/null
+++ b/src/libs/libdriver/input.cpp
@@ -0,0 +1,1841 @@
+/* Copyright (C) 1989-2020 Free Software Foundation, Inc.
+
+ Written by James Clark (jjc@jclark.com)
+ Major rewrite 2001 by Bernd Warken <groff-bernd.warken-72@web.de>
+
+ 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 3 of the License, 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 this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+/* 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
+ independence, 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 Ossanna & 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);
+ IntArg operator[](const size_t i) const
+ {
+ if (i >= num_stored)
+ fatal("index out of range");
+ return (IntArg) data[i];
+ }
+ void append(IntArg);
+ IntArg *get_data(void) const { return (IntArg *)data; }
+ 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)
+{
+ 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];
+ 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)
+{
+ 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];
+ 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;
+ current_env = 0;
+}
+
+//////////////////////////////////////////////////////////////////////
+/* 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))
+ fatal("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;
+ }
+ 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);
+ 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");
+ done = true;
+ 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");
+ return c;
+ 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 = (char *)"<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 = (char *)"<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);
+ delete[] name;
+ skip_line_x();
+ break;
+ }
+ case 'F': // x Filename: set filename for errors
+ {
+ char *str_arg = get_extended_arg();
+ if (str_arg == 0)
+ warning("empty argument for 'x F' command");
+ else {
+ remember_source_filename(str_arg);
+ 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');
+ 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 if (str_arg && (strncmp(str_arg, "devtag:",
+ strlen("devtag:")) == 0))
+ pr->devtag(str_arg, current_env);
+ else
+ pr->special(str_arg, current_env);
+ delete[] str_arg;
+ break;
+ }
+ default: // ignore unknown x commands, but warn
+ warning("unknown command 'x %1'", subcmd);
+ skip_line();
+ }
+ 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'");
+ delete[] str_arg;
+ char *tmp_dev = get_string_arg();
+ if (pr == 0) { // note: 'pr' initialized after prologue
+ device = tmp_dev;
+ if (0 /* nullptr */ == font::load_desc())
+ fatal("cannot load description of '%1' device", tmp_dev);
+ }
+ else {
+ if (device == 0 || strcmp(device, tmp_dev) != 0)
+ fatal("all files must use the same device");
+ 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'");
+ 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'");
+ 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);
+ 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_extended_arg();
+ remember_source_filename(str_arg);
+ delete[] str_arg;
+ break;
+ }
+ case 'h': // h: relative horizontal move
+ if (npages <= 0)
+ fatal_command(command);
+ current_env->hpos += (EnvInt) get_integer_arg();
+ break;
+ case 'H': // H: absolute horizontal positioning
+ if (npages <= 0)
+ fatal_command(command);
+ 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;
+ }
+ 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;
+ }
+ delete[] str_arg;
+ break;
+ }
+ case 'v': // v: relative vertical move
+ if (npages <= 0)
+ fatal_command(command);
+ current_env->vpos += (EnvInt) get_integer_arg();
+ break;
+ case 'V': // V: absolute vertical positioning
+ if (npages <= 0)
+ fatal_command(command);
+ 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;
+ pr = 0;
+ 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();
+}
+
+// Local Variables:
+// fill-column: 72
+// mode: C++
+// End:
+// vim: set cindent noexpandtab shiftwidth=2 textwidth=72:
diff --git a/src/libs/libdriver/libdriver.am b/src/libs/libdriver/libdriver.am
new file mode 100644
index 0000000..51475d8
--- /dev/null
+++ b/src/libs/libdriver/libdriver.am
@@ -0,0 +1,31 @@
+# Automake rules for 'libdriver'
+#
+# Copyright (C) 2014-2020 Free Software Foundation, Inc.
+#
+# '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 of the License, 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 this program. If not, see
+# <http://www.gnu.org/licenses/gpl-2.0.html>.
+#
+########################################################################
+
+noinst_LIBRARIES += libdriver.a
+libdriver_a_SOURCES = \
+ src/libs/libdriver/input.cpp \
+ src/libs/libdriver/printer.cpp
+
+
+# Local Variables:
+# mode: makefile-automake
+# fill-column: 72
+# End:
+# vim: set autoindent filetype=automake textwidth=72:
diff --git a/src/libs/libdriver/printer.cpp b/src/libs/libdriver/printer.cpp
new file mode 100644
index 0000000..f89b2e9
--- /dev/null
+++ b/src/libs/libdriver/printer.cpp
@@ -0,0 +1,268 @@
+/* Copyright (C) 1989-2020 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 3 of the License, 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 this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <assert.h>
+
+#include "driver.h"
+
+/* If we are sending output to an onscreen pager (as is the normal case
+ when reading man pages), then we may get an error state on the output
+ stream, if the user does not read all the way to the end.
+
+ We normally expect to catch this, and clean up the error context,
+ when the pager exits, because we should get, and handle, a SIGPIPE.
+
+ However ...
+*/
+
+#if (defined(_MSC_VER) || defined(_WIN32)) \
+ && !defined(__CYGWIN__) && !defined(_UWIN)
+
+ /* Native MS-Windows doesn't know about SIGPIPE, so we cannot detect
+ the early exit from the pager, and therefore, cannot clean up the
+ error context; thus we use the following static function to
+ identify this particular error context, and so suppress unwanted
+ diagnostics.
+ */
+
+ static int
+ check_for_output_error (FILE* stream)
+ {
+ /* First, clean up any prior error context on the output stream */
+ if (ferror (stream))
+ clearerr (stream);
+ /* Clear errno, in case clearerr() and fflush() don't */
+ errno = 0;
+ /* Flush the output stream, so we can capture any error context,
+ other than the specific case we wish to suppress.
+
+ Microsoft doesn't document it, but the error code for the
+ specific context we are trying to suppress seems to be EINVAL --
+ a strange choice, since it is not normally associated with
+ fflush(); of course, it *should* be EPIPE, but this *definitely*
+ is not used, and *is* so documented.
+ */
+ return ((fflush(stream) < 0) && (errno != EINVAL));
+ }
+
+#else
+
+ /* For other systems, we simply assume that *any* output error context
+ is to be reported.
+ */
+# define check_for_output_error(stream) ferror(stream) || fflush(stream) < 0
+
+#endif
+
+
+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()
+{
+ delete[] font_table;
+ while (font_list) {
+ font_pointer_list *tem = font_list;
+ font_list = font_list->next;
+ delete tem->p;
+ delete tem;
+ }
+ if (check_for_output_error(stdout))
+ 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;
+ 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 (0 /* nullptr */ == f)
+ fatal("cannot find font '%1'", nm);
+ 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::devtag(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';
+
+ glyph *g = set_char_and_width(buf, env, &w, &f);
+
+ if (g != UNDEFINED_GLYPH) {
+ set_char(g, 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;
+ glyph *g = set_char_and_width(nm, env, &w, &f);
+ if (g != UNDEFINED_GLYPH) {
+ set_char(g, f, env, w, nm);
+ if (widthp)
+ *widthp = w;
+ }
+}
+
+glyph *printer::set_char_and_width(const char *nm,
+ const environment *env, int *widthp,
+ font **f)
+{
+ glyph *g = name_to_glyph(nm);
+ int fn = env->fontno;
+ if (fn < 0 || fn >= nfonts) {
+ error("invalid font position '%1'", fn);
+ return UNDEFINED_GLYPH;
+ }
+ *f = font_table[fn];
+ if (*f == 0) {
+ error("no font mounted at position %1", fn);
+ return UNDEFINED_GLYPH;
+ }
+ if (!(*f)->contains(g)) {
+ if (nm[0] != '\0' && nm[1] == '\0')
+ error("font '%1' does not contain ordinary character '%2'",
+ (*f)->get_name(), nm[0]);
+ else
+ error("font '%1' does not contain special character '%2'",
+ (*f)->get_name(), nm);
+ return UNDEFINED_GLYPH;
+ }
+ int w = (*f)->get_width(g, env->size);
+ if (widthp)
+ *widthp = w;
+ return g;
+}
+
+void printer::set_numbered_char(int num, const environment *env, int
+ *widthp)
+{
+ glyph *g = number_to_glyph(num);
+ int fn = env->fontno;
+ if (fn < 0 || fn >= nfonts) {
+ error("invalid font position '%1'", fn);
+ return;
+ }
+ font *f = font_table[fn];
+ if (f == 0) {
+ error("no font mounted at position %1", fn);
+ return;
+ }
+ if (!f->contains(g)) {
+ error("font '%1' does not contain numbered character %2",
+ f->get_name(), num);
+ return;
+ }
+ int w = f->get_width(g, env->size);
+ if (widthp)
+ *widthp = w;
+ set_char(g, f, env, w, 0);
+}
+
+font *printer::get_font_from_index(int fontno)
+{
+ if ((fontno >= 0) && (fontno < nfonts))
+ return font_table[fontno];
+ else
+ return 0;
+}
+
+// Local Variables:
+// fill-column: 72
+// mode: C++
+// End:
+// vim: set cindent noexpandtab shiftwidth=2 textwidth=72:
diff --git a/src/libs/libgroff/assert.cpp b/src/libs/libgroff/assert.cpp
new file mode 100644
index 0000000..9da3d46
--- /dev/null
+++ b/src/libs/libgroff/assert.cpp
@@ -0,0 +1,38 @@
+/* Copyright (C) 1989-2020 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 3 of the License, 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 this program. If not, see <http://www.gnu.org/licenses/>. */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <assert.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+extern "C" const char *program_name;
+
+void assertion_failed(int lineno, const char *filename,
+ const char *function, const char *msg)
+{
+ if (program_name != 0)
+ fprintf(stderr, "%s: ", program_name);
+ fprintf(stderr, "%s:%d: %s(): assertion failed: '%s'\n", filename,
+ lineno, function, msg);
+ fflush(stderr);
+ abort();
+}
diff --git a/src/libs/libgroff/change_lf.cpp b/src/libs/libgroff/change_lf.cpp
new file mode 100644
index 0000000..eb98766
--- /dev/null
+++ b/src/libs/libgroff/change_lf.cpp
@@ -0,0 +1,37 @@
+/* Copyright (C) 1989-2020 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 3 of the License, 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 this program. If not, see <http://www.gnu.org/licenses/>. */
+
+#include <config.h>
+#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/src/libs/libgroff/cmap.cpp b/src/libs/libgroff/cmap.cpp
new file mode 100644
index 0000000..3f2c0c3
--- /dev/null
+++ b/src/libs/libgroff/cmap.cpp
@@ -0,0 +1,56 @@
+// -*- C++ -*-
+/* Copyright (C) 1989-2020 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 3 of the License, 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 this program. If not, see <http://www.gnu.org/licenses/>. */
+
+#include <config.h>
+#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/src/libs/libgroff/color.cpp b/src/libs/libgroff/color.cpp
new file mode 100644
index 0000000..388c2ee
--- /dev/null
+++ b/src/libs/libgroff/color.cpp
@@ -0,0 +1,404 @@
+/* Copyright (C) 2001-2020 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 3 of the License, 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 this program. If not, see <http://www.gnu.org/licenses/>. */
+
+#include "lib.h"
+#include "color.h"
+#include "cset.h"
+
+#include <assert.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#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(const color * const c)
+{
+ nm = c->nm;
+ 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) / double(MAX_COLOR_VAL),
+ double(Green) / double(MAX_COLOR_VAL),
+ double(Blue) / double(MAX_COLOR_VAL));
+ break;
+ case CMY:
+ sprintf(s, "cmy %.2ff %.2ff %.2ff",
+ double(Cyan) / double(MAX_COLOR_VAL),
+ double(Magenta) / double(MAX_COLOR_VAL),
+ double(Yellow) / double(MAX_COLOR_VAL));
+ break;
+ case CMYK:
+ sprintf(s, "cmyk %.2ff %.2ff %.2ff %.2ff",
+ double(Cyan) / double(MAX_COLOR_VAL),
+ double(Magenta) / double(MAX_COLOR_VAL),
+ double(Yellow) / double(MAX_COLOR_VAL),
+ double(Black) / double(MAX_COLOR_VAL));
+ break;
+ case GRAY:
+ sprintf(s, "gray %.2ff",
+ double(Gray) / double(MAX_COLOR_VAL));
+ break;
+ }
+ return s;
+}
+
+color default_color;
+
+// Local Variables:
+// fill-column: 72
+// mode: C++
+// End:
+// vim: set cindent noexpandtab shiftwidth=2 textwidth=72:
diff --git a/src/libs/libgroff/config.charset b/src/libs/libgroff/config.charset
new file mode 100755
index 0000000..8fca485
--- /dev/null
+++ b/src/libs/libgroff/config.charset
@@ -0,0 +1,684 @@
+#! /bin/sh
+# Output a system dependent table of character encoding aliases.
+#
+# Copyright (C) 2000-2020 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, see <http://www.gnu.org/licenses/>.
+#
+# The table consists of lines of the form
+# ALIAS CANONICAL
+#
+# ALIAS is the (system dependent) result of "nl_langinfo (CODESET)".
+# ALIAS is compared in a case sensitive way.
+#
+# CANONICAL is the GNU canonical name for this character encoding.
+# It must be an encoding supported by libiconv. Support by GNU libc is
+# also desirable. CANONICAL is case insensitive. Usually an upper case
+# MIME charset name is preferred.
+# The current list of GNU canonical charset names is as follows.
+#
+# name MIME? used by which systems
+# (darwin = Mac OS X, woe32 = native Windows)
+#
+# ASCII, ANSI_X3.4-1968 glibc solaris freebsd netbsd darwin cygwin
+# ISO-8859-1 Y glibc aix hpux irix osf solaris freebsd netbsd openbsd darwin cygwin
+# ISO-8859-2 Y glibc aix hpux irix osf solaris freebsd netbsd openbsd darwin cygwin
+# ISO-8859-3 Y glibc solaris cygwin
+# ISO-8859-4 Y osf solaris freebsd netbsd openbsd darwin
+# ISO-8859-5 Y glibc aix hpux irix osf solaris freebsd netbsd openbsd darwin cygwin
+# ISO-8859-6 Y glibc aix hpux solaris cygwin
+# ISO-8859-7 Y glibc aix hpux irix osf solaris netbsd openbsd darwin cygwin
+# ISO-8859-8 Y glibc aix hpux osf solaris cygwin
+# ISO-8859-9 Y glibc aix hpux irix osf solaris darwin cygwin
+# ISO-8859-13 glibc netbsd openbsd darwin cygwin
+# ISO-8859-14 glibc cygwin
+# ISO-8859-15 glibc aix osf solaris freebsd netbsd openbsd darwin cygwin
+# KOI8-R Y glibc solaris freebsd netbsd openbsd darwin
+# KOI8-U Y glibc freebsd netbsd openbsd darwin cygwin
+# KOI8-T glibc
+# CP437 dos
+# CP775 dos
+# CP850 aix osf dos
+# CP852 dos
+# CP855 dos
+# CP856 aix
+# CP857 dos
+# CP861 dos
+# CP862 dos
+# CP864 dos
+# CP865 dos
+# CP866 freebsd netbsd openbsd darwin dos
+# CP869 dos
+# CP874 woe32 dos
+# CP922 aix
+# CP932 aix cygwin woe32 dos
+# CP943 aix
+# CP949 osf darwin woe32 dos
+# CP950 woe32 dos
+# CP1046 aix
+# CP1124 aix
+# CP1125 dos
+# CP1129 aix
+# CP1131 darwin
+# CP1250 woe32
+# CP1251 glibc solaris netbsd openbsd darwin cygwin woe32
+# CP1252 aix woe32
+# CP1253 woe32
+# CP1254 woe32
+# CP1255 glibc woe32
+# CP1256 woe32
+# CP1257 woe32
+# GB2312 Y glibc aix hpux irix solaris freebsd netbsd darwin
+# EUC-JP Y glibc aix hpux irix osf solaris freebsd netbsd darwin
+# EUC-KR Y glibc aix hpux irix osf solaris freebsd netbsd darwin cygwin
+# EUC-TW glibc aix hpux irix osf solaris netbsd
+# BIG5 Y glibc aix hpux osf solaris freebsd netbsd darwin cygwin
+# BIG5-HKSCS glibc solaris darwin
+# GBK glibc aix osf solaris darwin cygwin woe32 dos
+# GB18030 glibc solaris netbsd darwin
+# SHIFT_JIS Y hpux osf solaris freebsd netbsd darwin
+# JOHAB glibc solaris woe32
+# TIS-620 glibc aix hpux osf solaris cygwin
+# VISCII Y glibc
+# TCVN5712-1 glibc
+# ARMSCII-8 glibc darwin
+# GEORGIAN-PS glibc cygwin
+# PT154 glibc
+# HP-ROMAN8 hpux
+# HP-ARABIC8 hpux
+# HP-GREEK8 hpux
+# HP-HEBREW8 hpux
+# HP-TURKISH8 hpux
+# HP-KANA8 hpux
+# DEC-KANJI osf
+# DEC-HANYU osf
+# UTF-8 Y glibc aix hpux osf solaris netbsd darwin cygwin
+#
+# Note: Names which are not marked as being a MIME name should not be used in
+# Internet protocols for information interchange (mail, news, etc.).
+#
+# Note: ASCII and ANSI_X3.4-1968 are synonymous canonical names. Applications
+# must understand both names and treat them as equivalent.
+#
+# The first argument passed to this file is the canonical host specification,
+# CPU_TYPE-MANUFACTURER-OPERATING_SYSTEM
+# or
+# CPU_TYPE-MANUFACTURER-KERNEL-OPERATING_SYSTEM
+
+host="$1"
+os=`echo "$host" | sed -e 's/^[^-]*-[^-]*-\(.*\)$/\1/'`
+echo "# This file contains a table of character encoding aliases,"
+echo "# suitable for operating system '${os}'."
+echo "# It was automatically generated from config.charset."
+# List of references, updated during installation:
+echo "# Packages using this file: "
+case "$os" in
+ linux-gnulibc1*)
+ # Linux libc5 doesn't have nl_langinfo(CODESET); therefore
+ # localcharset.c falls back to using the full locale name
+ # from the environment variables.
+ echo "C ASCII"
+ echo "POSIX ASCII"
+ for l in af af_ZA ca ca_ES da da_DK de de_AT de_BE de_CH de_DE de_LU \
+ en en_AU en_BW en_CA en_DK en_GB en_IE en_NZ en_US en_ZA \
+ en_ZW es es_AR es_BO es_CL es_CO es_DO es_EC es_ES es_GT \
+ es_HN es_MX es_PA es_PE es_PY es_SV es_US es_UY es_VE et \
+ et_EE eu eu_ES fi fi_FI fo fo_FO fr fr_BE fr_CA fr_CH fr_FR \
+ fr_LU ga ga_IE gl gl_ES id id_ID in in_ID is is_IS it it_CH \
+ it_IT kl kl_GL nl nl_BE nl_NL no no_NO pt pt_BR pt_PT sv \
+ sv_FI sv_SE; do
+ echo "$l ISO-8859-1"
+ echo "$l.iso-8859-1 ISO-8859-1"
+ echo "$l.iso-8859-15 ISO-8859-15"
+ echo "$l.iso-8859-15@euro ISO-8859-15"
+ echo "$l@euro ISO-8859-15"
+ echo "$l.cp-437 CP437"
+ echo "$l.cp-850 CP850"
+ echo "$l.cp-1252 CP1252"
+ echo "$l.cp-1252@euro CP1252"
+ #echo "$l.atari-st ATARI-ST" # not a commonly used encoding
+ echo "$l.utf-8 UTF-8"
+ echo "$l.utf-8@euro UTF-8"
+ done
+ for l in cs cs_CZ hr hr_HR hu hu_HU pl pl_PL ro ro_RO sk sk_SK sl \
+ sl_SI sr sr_CS sr_YU; do
+ echo "$l ISO-8859-2"
+ echo "$l.iso-8859-2 ISO-8859-2"
+ echo "$l.cp-852 CP852"
+ echo "$l.cp-1250 CP1250"
+ echo "$l.utf-8 UTF-8"
+ done
+ for l in mk mk_MK ru ru_RU; do
+ echo "$l ISO-8859-5"
+ echo "$l.iso-8859-5 ISO-8859-5"
+ echo "$l.koi8-r KOI8-R"
+ echo "$l.cp-866 CP866"
+ echo "$l.cp-1251 CP1251"
+ echo "$l.utf-8 UTF-8"
+ done
+ for l in ar ar_SA; do
+ echo "$l ISO-8859-6"
+ echo "$l.iso-8859-6 ISO-8859-6"
+ echo "$l.cp-864 CP864"
+ #echo "$l.cp-868 CP868" # not a commonly used encoding
+ echo "$l.cp-1256 CP1256"
+ echo "$l.utf-8 UTF-8"
+ done
+ for l in el el_GR gr gr_GR; do
+ echo "$l ISO-8859-7"
+ echo "$l.iso-8859-7 ISO-8859-7"
+ echo "$l.cp-869 CP869"
+ echo "$l.cp-1253 CP1253"
+ echo "$l.cp-1253@euro CP1253"
+ echo "$l.utf-8 UTF-8"
+ echo "$l.utf-8@euro UTF-8"
+ done
+ for l in he he_IL iw iw_IL; do
+ echo "$l ISO-8859-8"
+ echo "$l.iso-8859-8 ISO-8859-8"
+ echo "$l.cp-862 CP862"
+ echo "$l.cp-1255 CP1255"
+ echo "$l.utf-8 UTF-8"
+ done
+ for l in tr tr_TR; do
+ echo "$l ISO-8859-9"
+ echo "$l.iso-8859-9 ISO-8859-9"
+ echo "$l.cp-857 CP857"
+ echo "$l.cp-1254 CP1254"
+ echo "$l.utf-8 UTF-8"
+ done
+ for l in lt lt_LT lv lv_LV; do
+ #echo "$l BALTIC" # not a commonly used encoding, wrong encoding name
+ echo "$l ISO-8859-13"
+ done
+ for l in ru_UA uk uk_UA; do
+ echo "$l KOI8-U"
+ done
+ for l in zh zh_CN; do
+ #echo "$l GB_2312-80" # not a commonly used encoding, wrong encoding name
+ echo "$l GB2312"
+ done
+ for l in ja ja_JP ja_JP.EUC; do
+ echo "$l EUC-JP"
+ done
+ for l in ko ko_KR; do
+ echo "$l EUC-KR"
+ done
+ for l in th th_TH; do
+ echo "$l TIS-620"
+ done
+ for l in fa fa_IR; do
+ #echo "$l ISIRI-3342" # a broken encoding
+ echo "$l.utf-8 UTF-8"
+ done
+ ;;
+ linux* | *-gnu*)
+ # With glibc-2.1 or newer, we don't need any canonicalization,
+ # because glibc has iconv and both glibc and libiconv support all
+ # GNU canonical names directly. Therefore, the Makefile does not
+ # need to install the alias file at all.
+ # The following applies only to glibc-2.0.x and older libcs.
+ echo "ISO_646.IRV:1983 ASCII"
+ ;;
+ aix*)
+ echo "ISO8859-1 ISO-8859-1"
+ echo "ISO8859-2 ISO-8859-2"
+ echo "ISO8859-5 ISO-8859-5"
+ echo "ISO8859-6 ISO-8859-6"
+ echo "ISO8859-7 ISO-8859-7"
+ echo "ISO8859-8 ISO-8859-8"
+ echo "ISO8859-9 ISO-8859-9"
+ echo "ISO8859-15 ISO-8859-15"
+ echo "IBM-850 CP850"
+ echo "IBM-856 CP856"
+ echo "IBM-921 ISO-8859-13"
+ echo "IBM-922 CP922"
+ echo "IBM-932 CP932"
+ echo "IBM-943 CP943"
+ echo "IBM-1046 CP1046"
+ echo "IBM-1124 CP1124"
+ echo "IBM-1129 CP1129"
+ echo "IBM-1252 CP1252"
+ echo "IBM-eucCN GB2312"
+ echo "IBM-eucJP EUC-JP"
+ echo "IBM-eucKR EUC-KR"
+ echo "IBM-eucTW EUC-TW"
+ echo "big5 BIG5"
+ echo "GBK GBK"
+ echo "TIS-620 TIS-620"
+ echo "UTF-8 UTF-8"
+ ;;
+ hpux*)
+ echo "iso88591 ISO-8859-1"
+ echo "iso88592 ISO-8859-2"
+ echo "iso88595 ISO-8859-5"
+ echo "iso88596 ISO-8859-6"
+ echo "iso88597 ISO-8859-7"
+ echo "iso88598 ISO-8859-8"
+ echo "iso88599 ISO-8859-9"
+ echo "iso885915 ISO-8859-15"
+ echo "roman8 HP-ROMAN8"
+ echo "arabic8 HP-ARABIC8"
+ echo "greek8 HP-GREEK8"
+ echo "hebrew8 HP-HEBREW8"
+ echo "turkish8 HP-TURKISH8"
+ echo "kana8 HP-KANA8"
+ echo "tis620 TIS-620"
+ echo "big5 BIG5"
+ echo "eucJP EUC-JP"
+ echo "eucKR EUC-KR"
+ echo "eucTW EUC-TW"
+ echo "hp15CN GB2312"
+ #echo "ccdc ?" # what is this?
+ echo "SJIS SHIFT_JIS"
+ echo "utf8 UTF-8"
+ ;;
+ irix*)
+ echo "ISO8859-1 ISO-8859-1"
+ echo "ISO8859-2 ISO-8859-2"
+ echo "ISO8859-5 ISO-8859-5"
+ echo "ISO8859-7 ISO-8859-7"
+ echo "ISO8859-9 ISO-8859-9"
+ echo "eucCN GB2312"
+ echo "eucJP EUC-JP"
+ echo "eucKR EUC-KR"
+ echo "eucTW EUC-TW"
+ ;;
+ osf*)
+ echo "ISO8859-1 ISO-8859-1"
+ echo "ISO8859-2 ISO-8859-2"
+ echo "ISO8859-4 ISO-8859-4"
+ echo "ISO8859-5 ISO-8859-5"
+ echo "ISO8859-7 ISO-8859-7"
+ echo "ISO8859-8 ISO-8859-8"
+ echo "ISO8859-9 ISO-8859-9"
+ echo "ISO8859-15 ISO-8859-15"
+ echo "cp850 CP850"
+ echo "big5 BIG5"
+ echo "dechanyu DEC-HANYU"
+ echo "dechanzi GB2312"
+ echo "deckanji DEC-KANJI"
+ echo "deckorean EUC-KR"
+ echo "eucJP EUC-JP"
+ echo "eucKR EUC-KR"
+ echo "eucTW EUC-TW"
+ echo "GBK GBK"
+ echo "KSC5601 CP949"
+ echo "sdeckanji EUC-JP"
+ echo "SJIS SHIFT_JIS"
+ echo "TACTIS TIS-620"
+ echo "UTF-8 UTF-8"
+ ;;
+ solaris*)
+ echo "646 ASCII"
+ echo "ISO8859-1 ISO-8859-1"
+ echo "ISO8859-2 ISO-8859-2"
+ echo "ISO8859-3 ISO-8859-3"
+ echo "ISO8859-4 ISO-8859-4"
+ echo "ISO8859-5 ISO-8859-5"
+ echo "ISO8859-6 ISO-8859-6"
+ echo "ISO8859-7 ISO-8859-7"
+ echo "ISO8859-8 ISO-8859-8"
+ echo "ISO8859-9 ISO-8859-9"
+ echo "ISO8859-15 ISO-8859-15"
+ echo "koi8-r KOI8-R"
+ echo "ansi-1251 CP1251"
+ echo "BIG5 BIG5"
+ echo "Big5-HKSCS BIG5-HKSCS"
+ echo "gb2312 GB2312"
+ echo "GBK GBK"
+ echo "GB18030 GB18030"
+ echo "cns11643 EUC-TW"
+ echo "5601 EUC-KR"
+ echo "ko_KR.johap92 JOHAB"
+ echo "eucJP EUC-JP"
+ echo "PCK SHIFT_JIS"
+ echo "TIS620.2533 TIS-620"
+ #echo "sun_eu_greek ?" # what is this?
+ echo "UTF-8 UTF-8"
+ ;;
+ freebsd* | os2*)
+ # FreeBSD 4.2 doesn't have nl_langinfo(CODESET); therefore
+ # localcharset.c falls back to using the full locale name
+ # from the environment variables.
+ # Likewise for OS/2. OS/2 has XFree86 just like FreeBSD. Just
+ # reuse FreeBSD's locale data for OS/2.
+ echo "C ASCII"
+ echo "US-ASCII ASCII"
+ for l in la_LN lt_LN; do
+ echo "$l.ASCII ASCII"
+ done
+ for l in da_DK de_AT de_CH de_DE en_AU en_CA en_GB en_US es_ES \
+ fi_FI fr_BE fr_CA fr_CH fr_FR is_IS it_CH it_IT la_LN \
+ lt_LN nl_BE nl_NL no_NO pt_PT sv_SE; do
+ echo "$l.ISO_8859-1 ISO-8859-1"
+ echo "$l.DIS_8859-15 ISO-8859-15"
+ done
+ for l in cs_CZ hr_HR hu_HU la_LN lt_LN pl_PL sl_SI; do
+ echo "$l.ISO_8859-2 ISO-8859-2"
+ done
+ for l in la_LN lt_LT; do
+ echo "$l.ISO_8859-4 ISO-8859-4"
+ done
+ for l in ru_RU ru_SU; do
+ echo "$l.KOI8-R KOI8-R"
+ echo "$l.ISO_8859-5 ISO-8859-5"
+ echo "$l.CP866 CP866"
+ done
+ echo "uk_UA.KOI8-U KOI8-U"
+ echo "zh_TW.BIG5 BIG5"
+ echo "zh_TW.Big5 BIG5"
+ echo "zh_CN.EUC GB2312"
+ echo "ja_JP.EUC EUC-JP"
+ echo "ja_JP.SJIS SHIFT_JIS"
+ echo "ja_JP.Shift_JIS SHIFT_JIS"
+ echo "ko_KR.EUC EUC-KR"
+ ;;
+ netbsd*)
+ echo "646 ASCII"
+ echo "ISO8859-1 ISO-8859-1"
+ echo "ISO8859-2 ISO-8859-2"
+ echo "ISO8859-4 ISO-8859-4"
+ echo "ISO8859-5 ISO-8859-5"
+ echo "ISO8859-7 ISO-8859-7"
+ echo "ISO8859-13 ISO-8859-13"
+ echo "ISO8859-15 ISO-8859-15"
+ echo "eucCN GB2312"
+ echo "eucJP EUC-JP"
+ echo "eucKR EUC-KR"
+ echo "eucTW EUC-TW"
+ echo "BIG5 BIG5"
+ echo "SJIS SHIFT_JIS"
+ ;;
+ openbsd*)
+ echo "646 ASCII"
+ echo "ISO8859-1 ISO-8859-1"
+ echo "ISO8859-2 ISO-8859-2"
+ echo "ISO8859-4 ISO-8859-4"
+ echo "ISO8859-5 ISO-8859-5"
+ echo "ISO8859-7 ISO-8859-7"
+ echo "ISO8859-13 ISO-8859-13"
+ echo "ISO8859-15 ISO-8859-15"
+ ;;
+ darwin[56]*)
+ # Darwin 6.8 doesn't have nl_langinfo(CODESET); therefore
+ # localcharset.c falls back to using the full locale name
+ # from the environment variables.
+ echo "C ASCII"
+ for l in en_AU en_CA en_GB en_US la_LN; do
+ echo "$l.US-ASCII ASCII"
+ done
+ for l in da_DK de_AT de_CH de_DE en_AU en_CA en_GB en_US es_ES \
+ fi_FI fr_BE fr_CA fr_CH fr_FR is_IS it_CH it_IT nl_BE \
+ nl_NL no_NO pt_PT sv_SE; do
+ echo "$l ISO-8859-1"
+ echo "$l.ISO8859-1 ISO-8859-1"
+ echo "$l.ISO8859-15 ISO-8859-15"
+ done
+ for l in la_LN; do
+ echo "$l.ISO8859-1 ISO-8859-1"
+ echo "$l.ISO8859-15 ISO-8859-15"
+ done
+ for l in cs_CZ hr_HR hu_HU la_LN pl_PL sl_SI; do
+ echo "$l.ISO8859-2 ISO-8859-2"
+ done
+ for l in la_LN lt_LT; do
+ echo "$l.ISO8859-4 ISO-8859-4"
+ done
+ for l in ru_RU; do
+ echo "$l.KOI8-R KOI8-R"
+ echo "$l.ISO8859-5 ISO-8859-5"
+ echo "$l.CP866 CP866"
+ done
+ for l in bg_BG; do
+ echo "$l.CP1251 CP1251"
+ done
+ echo "uk_UA.KOI8-U KOI8-U"
+ echo "zh_TW.BIG5 BIG5"
+ echo "zh_TW.Big5 BIG5"
+ echo "zh_CN.EUC GB2312"
+ echo "ja_JP.EUC EUC-JP"
+ echo "ja_JP.SJIS SHIFT_JIS"
+ echo "ko_KR.EUC EUC-KR"
+ ;;
+ darwin*)
+ # Darwin 7.5 has nl_langinfo(CODESET), but sometimes its value is
+ # useless:
+ # - It returns the empty string when LANG is set to a locale of the
+ # form ll_CC, although ll_CC/LC_CTYPE is a symlink to an UTF-8
+ # LC_CTYPE file.
+ # - The environment variables LANG, LC_CTYPE, LC_ALL are not set by
+ # the system; nl_langinfo(CODESET) returns "US-ASCII" in this case.
+ # - The documentation says:
+ # "... all code that calls BSD system routines should ensure
+ # that the const *char parameters of these routines are in UTF-8
+ # encoding. All BSD system functions expect their string
+ # parameters to be in UTF-8 encoding and nothing else."
+ # It also says
+ # "An additional caveat is that string parameters for files,
+ # paths, and other file-system entities must be in canonical
+ # UTF-8. In a canonical UTF-8 Unicode string, all decomposable
+ # characters are decomposed ..."
+ # but this is not true: You can pass non-decomposed UTF-8 strings
+ # to file system functions, and it is the OS which will convert
+ # them to decomposed UTF-8 before accessing the file system.
+ # - The Apple Terminal application displays UTF-8 by default.
+ # - However, other applications are free to use different encodings:
+ # - xterm uses ISO-8859-1 by default.
+ # - TextEdit uses MacRoman by default.
+ # We prefer UTF-8 over decomposed UTF-8-MAC because one should
+ # minimize the use of decomposed Unicode. Unfortunately, through the
+ # Darwin file system, decomposed UTF-8 strings are leaked into user
+ # space nevertheless.
+ # Then there are also the locales with encodings other than US-ASCII
+ # and UTF-8. These locales can be occasionally useful to users (e.g.
+ # when grepping through ISO-8859-1 encoded text files), when all their
+ # file names are in US-ASCII.
+ echo "ISO8859-1 ISO-8859-1"
+ echo "ISO8859-2 ISO-8859-2"
+ echo "ISO8859-4 ISO-8859-4"
+ echo "ISO8859-5 ISO-8859-5"
+ echo "ISO8859-7 ISO-8859-7"
+ echo "ISO8859-9 ISO-8859-9"
+ echo "ISO8859-13 ISO-8859-13"
+ echo "ISO8859-15 ISO-8859-15"
+ echo "KOI8-R KOI8-R"
+ echo "KOI8-U KOI8-U"
+ echo "CP866 CP866"
+ echo "CP949 CP949"
+ echo "CP1131 CP1131"
+ echo "CP1251 CP1251"
+ echo "eucCN GB2312"
+ echo "GB2312 GB2312"
+ echo "eucJP EUC-JP"
+ echo "eucKR EUC-KR"
+ echo "Big5 BIG5"
+ echo "Big5HKSCS BIG5-HKSCS"
+ echo "GBK GBK"
+ echo "GB18030 GB18030"
+ echo "SJIS SHIFT_JIS"
+ echo "ARMSCII-8 ARMSCII-8"
+ echo "PT154 PT154"
+ #echo "ISCII-DEV ?"
+ echo "* UTF-8"
+ ;;
+ beos* | haiku*)
+ # BeOS and Haiku have a single locale, and it has UTF-8 encoding.
+ echo "* UTF-8"
+ ;;
+ msdosdjgpp*)
+ # DJGPP 2.03 doesn't have nl_langinfo(CODESET); therefore
+ # localcharset.c falls back to using the full locale name
+ # from the environment variables.
+ echo "#"
+ echo "# The encodings given here may not all be correct."
+ echo "# If you find that the encoding given for your language and"
+ echo "# country is not the one your DOS machine actually uses, just"
+ echo "# correct it in this file, and send a mail to"
+ echo "# Juan Manuel Guerrero <juan.guerrero@gmx.de>"
+ echo "# and Bruno Haible <bruno@clisp.org>."
+ echo "#"
+ echo "C ASCII"
+ # ISO-8859-1 languages
+ echo "ca CP850"
+ echo "ca_ES CP850"
+ echo "da CP865" # not CP850 ??
+ echo "da_DK CP865" # not CP850 ??
+ echo "de CP850"
+ echo "de_AT CP850"
+ echo "de_CH CP850"
+ echo "de_DE CP850"
+ echo "en CP850"
+ echo "en_AU CP850" # not CP437 ??
+ echo "en_CA CP850"
+ echo "en_GB CP850"
+ echo "en_NZ CP437"
+ echo "en_US CP437"
+ echo "en_ZA CP850" # not CP437 ??
+ echo "es CP850"
+ echo "es_AR CP850"
+ echo "es_BO CP850"
+ echo "es_CL CP850"
+ echo "es_CO CP850"
+ echo "es_CR CP850"
+ echo "es_CU CP850"
+ echo "es_DO CP850"
+ echo "es_EC CP850"
+ echo "es_ES CP850"
+ echo "es_GT CP850"
+ echo "es_HN CP850"
+ echo "es_MX CP850"
+ echo "es_NI CP850"
+ echo "es_PA CP850"
+ echo "es_PY CP850"
+ echo "es_PE CP850"
+ echo "es_SV CP850"
+ echo "es_UY CP850"
+ echo "es_VE CP850"
+ echo "et CP850"
+ echo "et_EE CP850"
+ echo "eu CP850"
+ echo "eu_ES CP850"
+ echo "fi CP850"
+ echo "fi_FI CP850"
+ echo "fr CP850"
+ echo "fr_BE CP850"
+ echo "fr_CA CP850"
+ echo "fr_CH CP850"
+ echo "fr_FR CP850"
+ echo "ga CP850"
+ echo "ga_IE CP850"
+ echo "gd CP850"
+ echo "gd_GB CP850"
+ echo "gl CP850"
+ echo "gl_ES CP850"
+ echo "id CP850" # not CP437 ??
+ echo "id_ID CP850" # not CP437 ??
+ echo "is CP861" # not CP850 ??
+ echo "is_IS CP861" # not CP850 ??
+ echo "it CP850"
+ echo "it_CH CP850"
+ echo "it_IT CP850"
+ echo "lt CP775"
+ echo "lt_LT CP775"
+ echo "lv CP775"
+ echo "lv_LV CP775"
+ echo "nb CP865" # not CP850 ??
+ echo "nb_NO CP865" # not CP850 ??
+ echo "nl CP850"
+ echo "nl_BE CP850"
+ echo "nl_NL CP850"
+ echo "nn CP865" # not CP850 ??
+ echo "nn_NO CP865" # not CP850 ??
+ echo "no CP865" # not CP850 ??
+ echo "no_NO CP865" # not CP850 ??
+ echo "pt CP850"
+ echo "pt_BR CP850"
+ echo "pt_PT CP850"
+ echo "sv CP850"
+ echo "sv_SE CP850"
+ # ISO-8859-2 languages
+ echo "cs CP852"
+ echo "cs_CZ CP852"
+ echo "hr CP852"
+ echo "hr_HR CP852"
+ echo "hu CP852"
+ echo "hu_HU CP852"
+ echo "pl CP852"
+ echo "pl_PL CP852"
+ echo "ro CP852"
+ echo "ro_RO CP852"
+ echo "sk CP852"
+ echo "sk_SK CP852"
+ echo "sl CP852"
+ echo "sl_SI CP852"
+ echo "sq CP852"
+ echo "sq_AL CP852"
+ echo "sr CP852" # CP852 or CP866 or CP855 ??
+ echo "sr_CS CP852" # CP852 or CP866 or CP855 ??
+ echo "sr_YU CP852" # CP852 or CP866 or CP855 ??
+ # ISO-8859-3 languages
+ echo "mt CP850"
+ echo "mt_MT CP850"
+ # ISO-8859-5 languages
+ echo "be CP866"
+ echo "be_BE CP866"
+ echo "bg CP866" # not CP855 ??
+ echo "bg_BG CP866" # not CP855 ??
+ echo "mk CP866" # not CP855 ??
+ echo "mk_MK CP866" # not CP855 ??
+ echo "ru CP866"
+ echo "ru_RU CP866"
+ echo "uk CP1125"
+ echo "uk_UA CP1125"
+ # ISO-8859-6 languages
+ echo "ar CP864"
+ echo "ar_AE CP864"
+ echo "ar_DZ CP864"
+ echo "ar_EG CP864"
+ echo "ar_IQ CP864"
+ echo "ar_IR CP864"
+ echo "ar_JO CP864"
+ echo "ar_KW CP864"
+ echo "ar_MA CP864"
+ echo "ar_OM CP864"
+ echo "ar_QA CP864"
+ echo "ar_SA CP864"
+ echo "ar_SY CP864"
+ # ISO-8859-7 languages
+ echo "el CP869"
+ echo "el_GR CP869"
+ # ISO-8859-8 languages
+ echo "he CP862"
+ echo "he_IL CP862"
+ # ISO-8859-9 languages
+ echo "tr CP857"
+ echo "tr_TR CP857"
+ # Japanese
+ echo "ja CP932"
+ echo "ja_JP CP932"
+ # Chinese
+ echo "zh_CN GBK"
+ echo "zh_TW CP950" # not CP938 ??
+ # Korean
+ echo "kr CP949" # not CP934 ??
+ echo "kr_KR CP949" # not CP934 ??
+ # Thai
+ echo "th CP874"
+ echo "th_TH CP874"
+ # Other
+ echo "eo CP850"
+ echo "eo_EO CP850"
+ ;;
+esac
diff --git a/src/libs/libgroff/cset.cpp b/src/libs/libgroff/cset.cpp
new file mode 100644
index 0000000..6702237
--- /dev/null
+++ b/src/libs/libgroff/cset.cpp
@@ -0,0 +1,104 @@
+// -*- C++ -*-
+/* Copyright (C) 1989-2020 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 3 of the License, 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 this program. If not, see <http://www.gnu.org/licenses/>. */
+
+#include <config.h>
+#include <ctype.h>
+
+#include "lib.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/src/libs/libgroff/curtime.cpp b/src/libs/libgroff/curtime.cpp
new file mode 100644
index 0000000..34dbc5c
--- /dev/null
+++ b/src/libs/libgroff/curtime.cpp
@@ -0,0 +1,55 @@
+/* Copyright (C) 2015-2020 Free Software Foundation, Inc.
+
+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 of the License, 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.
+
+The GNU General Public License version 2 (GPL2) is available in the
+internet at <http://www.gnu.org/licenses/gpl-2.0.txt>. */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <errno.h>
+#include <limits.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+
+#include "errarg.h"
+#include "error.h"
+
+#ifdef LONG_FOR_TIME_T
+long
+#else
+time_t
+#endif
+current_time()
+{
+ char *source_date_epoch = getenv("SOURCE_DATE_EPOCH");
+
+ if (source_date_epoch) {
+ errno = 0;
+ char *endptr;
+ long epoch = strtol(source_date_epoch, &endptr, 10);
+
+ if ((errno == ERANGE && (epoch == LONG_MAX || epoch == LONG_MIN)) ||
+ (errno != 0 && epoch == 0))
+ fatal("$SOURCE_DATE_EPOCH: strtol: %1", strerror(errno));
+ if (endptr == source_date_epoch)
+ fatal("$SOURCE_DATE_EPOCH: no digits found: '%1'", endptr);
+ if (*endptr != '\0')
+ fatal("$SOURCE_DATE_EPOCH: trailing garbage: '%1'", endptr);
+ return epoch;
+ } else
+ return time(0);
+}
diff --git a/src/libs/libgroff/device.cpp b/src/libs/libgroff/device.cpp
new file mode 100644
index 0000000..6caeb8e
--- /dev/null
+++ b/src/libs/libgroff/device.cpp
@@ -0,0 +1,39 @@
+// -*- C++ -*-
+/* Copyright (C) 1989-2020 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 3 of the License, 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 this program. If not, see <http://www.gnu.org/licenses/>. */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#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 && tem[0])
+ device = tem;
+}
diff --git a/src/libs/libgroff/errarg.cpp b/src/libs/libgroff/errarg.cpp
new file mode 100644
index 0000000..99fc0fa
--- /dev/null
+++ b/src/libs/libgroff/errarg.cpp
@@ -0,0 +1,136 @@
+/* Copyright (C) 1989-2020 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 3 of the License, 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 this program. If not, see <http://www.gnu.org/licenses/>. */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <assert.h>
+#include <stdio.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 == "unsupported argument conversion (not in [%123])");
+ }
+ }
+ else
+ putc(c, stderr);
+ }
+}
+
+// Local Variables:
+// fill-column: 72
+// mode: C++
+// End:
+// vim: set cindent noexpandtab shiftwidth=2 textwidth=72:
diff --git a/src/libs/libgroff/error.cpp b/src/libs/libgroff/error.cpp
new file mode 100644
index 0000000..b1a4e61
--- /dev/null
+++ b/src/libs/libgroff/error.cpp
@@ -0,0 +1,185 @@
+/* Copyright (C) 1989-2020 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 3 of the License, 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 this program. If not, see <http://www.gnu.org/licenses/>. */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "errarg.h"
+#include "error.h"
+
+extern void fatal_error_exit();
+
+enum error_type { DEBUG, 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)
+{
+ bool need_space = false;
+ if (program_name != 0 /* nullptr */) {
+ fputs(program_name, stderr);
+ fputc(':', stderr);
+ need_space = true;
+ }
+ if (filename != 0 /* nullptr */) {
+ if (strcmp(filename, "-") == 0)
+ filename = "<standard input>";
+ fputs(filename, stderr);
+ if (source_filename != 0 /* nullptr */) {
+ fputs(":(", stderr);
+ fputs(source_filename, stderr);
+ fputc(')', stderr);
+ }
+ if (lineno > 0) {
+ fputc(':', stderr);
+ errprint("%1", lineno);
+ }
+ fputc(':', stderr);
+ need_space = true;
+ }
+ if (need_space)
+ fputc(' ', stderr);
+ switch (type) {
+ case FATAL:
+ fputs("fatal error", stderr);
+ break;
+ case ERROR:
+ fputs("error", stderr);
+ break;
+ case WARNING:
+ fputs("warning", stderr);
+ break;
+ case DEBUG:
+ fputs("debug", stderr);
+ break;
+ }
+ fputs(": ", 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 debug(const char *format,
+ const errarg &arg1,
+ const errarg &arg2,
+ const errarg &arg3)
+{
+ do_error(DEBUG, 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);
+}
+
+// Use the functions below when it's more costly to save and restore the
+// globals current_filename, current_source_filename, and current_lineno
+// than to specify additional arguments. For instance, a function that
+// would need to temporarily change their values and has multiple return
+// paths might prefer these to the simpler variants above.
+
+void debug_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 /* nullptr */, lineno,
+ DEBUG, 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 /* nullptr */, 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 /* nullptr */, 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 /* nullptr */, lineno,
+ FATAL, format, arg1, arg2, arg3);
+}
+
+// Local Variables:
+// fill-column: 72
+// mode: C++
+// End:
+// vim: set cindent noexpandtab shiftwidth=2 textwidth=72:
diff --git a/src/libs/libgroff/fatal.cpp b/src/libs/libgroff/fatal.cpp
new file mode 100644
index 0000000..1cbf3ef
--- /dev/null
+++ b/src/libs/libgroff/fatal.cpp
@@ -0,0 +1,30 @@
+/* Copyright (C) 1989-2020 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 3 of the License, 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 this program. If not, see <http://www.gnu.org/licenses/>. */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdlib.h>
+
+#define FATAL_ERROR_EXIT_CODE 3
+
+void fatal_error_exit()
+{
+ exit(FATAL_ERROR_EXIT_CODE);
+}
diff --git a/src/libs/libgroff/filename.cpp b/src/libs/libgroff/filename.cpp
new file mode 100644
index 0000000..ded9738
--- /dev/null
+++ b/src/libs/libgroff/filename.cpp
@@ -0,0 +1,31 @@
+/* Copyright (C) 2014-2022 Free Software Foundation, Inc.
+
+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 3 of the License, 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 this program. If not, see <http://www.gnu.org/licenses/>. */
+
+// This global stores the name of the input file being processed by
+// troff, an output driver, or other program.
+const char *current_filename = 0 /* nullptr */;
+
+// This global stores the name of the troff input file corresponding to
+// the part of a device-independent troff output being processed; it is
+// used only by output drivers.
+const char *current_source_filename = 0 /* nullptr */;
+
+// Local Variables:
+// fill-column: 72
+// mode: C++
+// End:
+// vim: set cindent noexpandtab shiftwidth=2 textwidth=72:
diff --git a/src/libs/libgroff/fmod.c b/src/libs/libgroff/fmod.c
new file mode 100644
index 0000000..45278f9
--- /dev/null
+++ b/src/libs/libgroff/fmod.c
@@ -0,0 +1,27 @@
+/* Copyright (C) 1989-2020 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 3 of the License, 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 this program. If not, see <http://www.gnu.org/licenses/>. */
+
+#include <config.h>
+#include <math.h>
+
+double fmod(x, y)
+ double x, y;
+{
+ double quot = x/y;
+ return x - (quot < 0.0 ? ceil(quot) : floor(quot)) * y;
+}
diff --git a/src/libs/libgroff/font.cpp b/src/libs/libgroff/font.cpp
new file mode 100644
index 0000000..c1af12c
--- /dev/null
+++ b/src/libs/libgroff/font.cpp
@@ -0,0 +1,1321 @@
+/* Copyright (C) 1989-2021 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 3 of the License, 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 this program. If not, see <http://www.gnu.org/licenses/>. */
+
+#include "lib.h"
+
+#include <assert.h>
+#include <ctype.h>
+#include <math.h>
+#include <stdlib.h>
+#include <wchar.h>
+
+#include "errarg.h"
+#include "error.h"
+#include "cset.h"
+#include "font.h"
+#include "unicode.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 {
+ glyph *glyph1;
+ glyph *glyph2;
+ int amount;
+ font_kern_list *next;
+
+ font_kern_list(glyph *, glyph *, 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 linebufsize;
+ bool recognize_comments;
+ bool silent;
+ char *buf;
+ text_file(FILE *fp, char *p);
+ ~text_file();
+ bool next_line();
+ void error(const char *format,
+ const errarg &arg1 = empty_errarg,
+ const errarg &arg2 = empty_errarg,
+ const errarg &arg3 = empty_errarg);
+ void fatal(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),
+ linebufsize(128), recognize_comments(true), silent(false), buf(0)
+{
+}
+
+text_file::~text_file()
+{
+ delete[] buf;
+ free(path);
+ if (fp)
+ fclose(fp);
+}
+
+bool text_file::next_line()
+{
+ if (fp == 0)
+ return false;
+ if (buf == 0)
+ buf = new char[linebufsize];
+ for (;;) {
+ lineno++;
+ int length = 0;
+ for (;;) {
+ int c = getc(fp);
+ if (c == EOF)
+ break;
+ if (is_invalid_input_char(c))
+ error("invalid input character code %1", int(c));
+ else {
+ if (length + 1 >= linebufsize) {
+ char *old_buf = buf;
+ buf = new char[linebufsize * 2];
+ memcpy(buf, old_buf, linebufsize);
+ delete[] old_buf;
+ linebufsize *= 2;
+ }
+ buf[length++] = c;
+ if (c == '\n')
+ break;
+ }
+ }
+ if (length == 0)
+ break;
+ buf[length] = '\0';
+ char *ptr = buf;
+ while (csspace(*ptr))
+ ptr++;
+ if (*ptr != 0 && (!recognize_comments || *ptr != '#'))
+ return true;
+ }
+ return false;
+}
+
+void text_file::error(const char *format,
+ const errarg &arg1,
+ const errarg &arg2,
+ const errarg &arg3)
+{
+ if (!silent)
+ error_with_file_and_line(path, lineno, format, arg1, arg2, arg3);
+}
+
+void text_file::fatal(const char *format,
+ const errarg &arg1,
+ const errarg &arg2,
+ const errarg &arg3)
+{
+ if (!silent)
+ fatal_with_file_and_line(path, lineno, format, arg1, arg2, arg3);
+}
+
+int glyph_to_unicode(glyph *g)
+{
+ const char *nm = glyph_to_name(g);
+ if (nm != 0) {
+ // ASCII character?
+ if (nm[0] == 'c' && nm[1] == 'h' && nm[2] == 'a' && nm[3] == 'r'
+ && (nm[4] >= '0' && nm[4] <= '9')) {
+ int n = (nm[4] - '0');
+ if (nm[5] == '\0')
+ return n;
+ if (n > 0 && (nm[5] >= '0' && nm[5] <= '9')) {
+ n = 10*n + (nm[5] - '0');
+ if (nm[6] == '\0')
+ return n;
+ if (nm[6] >= '0' && nm[6] <= '9') {
+ n = 10*n + (nm[6] - '0');
+ if (nm[7] == '\0' && n < 128)
+ return n;
+ }
+ }
+ }
+ // Unicode character?
+ if (check_unicode_name(nm)) {
+ char *ignore;
+ return (int)strtol(nm + 1, &ignore, 16);
+ }
+ // If 'nm' is a single letter 'x', the glyph name is '\x'.
+ char buf[] = { '\\', '\0', '\0' };
+ if (nm[1] == '\0') {
+ buf[1] = nm[0];
+ nm = buf;
+ }
+ // groff glyphs that map to Unicode?
+ const char *unicode = glyph_name_to_unicode(nm);
+ if (unicode != 0 && strchr(unicode, '_') == 0) {
+ char *ignore;
+ return (int)strtol(unicode, &ignore, 16);
+ }
+ }
+ return -1;
+}
+
+/* font functions */
+
+font::font(const char *s) : ligatures(0), kern_hash_table(0),
+ space_width(0), special(false), internalname(0), slant(0.0), zoom(0),
+ ch_index(0), nindices(0), ch(0), ch_used(0), ch_size(0),
+ widths_cache(0)
+{
+ name = new char[strlen(s) + 1];
+ strcpy(name, s);
+}
+
+font::~font()
+{
+ for (int i = 0; i < ch_used; i++)
+ if (ch[i].special_device_coding)
+ delete[] ch[i].special_device_coding;
+ delete[] ch;
+ 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;
+ }
+ }
+ delete[] kern_hash_table;
+ }
+ delete[] name;
+ 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);
+ }
+}
+
+static int scale_round(int n, int x, int y, int z)
+{
+ assert(x >= 0 && y > 0 && z > 0);
+ if (x == 0)
+ return 0;
+ if (n >= 0)
+ return int((n * double(x) / double(y)) * (double(z) / 1000.0) + .5);
+ else
+ return int((n * double(x) / double(y)) * (double(z) / 1000.0) - .5);
+}
+
+inline int font::scale(int w, int sz)
+{
+ if (zoom)
+ return scale_round(w, sz, unitwidth, zoom);
+ else
+ return sz == unitwidth ? w : scale_round(w, sz, unitwidth);
+}
+
+// Returns whether scaling by arguments was successful. Used for paper
+// size conversions.
+bool font::unit_scale(double *value, char unit)
+{
+ // Paper sizes are handled in inches.
+ 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 == "unit not in [cipP]");
+ break;
+ }
+ if (divisor) {
+ *value /= divisor;
+ return true;
+ }
+ return false;
+}
+
+int font::get_skew(glyph *g, int point_size, int sl)
+{
+ int h = get_height(g, point_size);
+ return int(h * tan((slant + sl) * PI / 180.0) + .5);
+}
+
+bool font::contains(glyph *g)
+{
+ int idx = glyph_to_index(g);
+ assert(idx >= 0);
+ // Explicitly enumerated glyph?
+ if (idx < nindices && ch_index[idx] >= 0)
+ return true;
+ if (is_unicode) {
+ // Unicode font
+ // ASCII or Unicode character, or groff glyph name that maps to Unicode?
+ if (glyph_to_unicode(g) >= 0)
+ return true;
+ // Numbered character?
+ if (glyph_to_number(g) >= 0)
+ return true;
+ }
+ return false;
+}
+
+bool 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()
+{
+ delete[] width;
+}
+
+int font::get_width(glyph *g, int point_size)
+{
+ int idx = glyph_to_index(g);
+ assert(idx >= 0);
+ int real_size;
+ if (zoom == 0) // 0 means "don't zoom"
+ real_size = point_size;
+ else
+ {
+ if (point_size <= (INT_MAX - 500) / zoom)
+ real_size = (point_size * zoom + 500) / 1000;
+ else
+ real_size = int(point_size * double(zoom) / 1000.0 + .5);
+ }
+ if (idx < nindices && ch_index[idx] >= 0) {
+ // Explicitly enumerated glyph
+ int i = ch_index[idx];
+ if (real_size == unitwidth || font::use_unscaled_charwidths)
+ return ch[i].width;
+
+ if (!widths_cache)
+ widths_cache = new font_widths_cache(real_size, ch_size);
+ else if (widths_cache->point_size != real_size) {
+ font_widths_cache **p;
+ for (p = &widths_cache; *p; p = &(*p)->next)
+ if ((*p)->point_size == real_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(real_size, ch_size,
+ widths_cache);
+ }
+ int &w = widths_cache->width[i];
+ if (w < 0)
+ w = scale(ch[i].width, point_size);
+ return w;
+ }
+ if (is_unicode) {
+ // Unicode font
+ int width = 24; // XXX: Add a request to override this.
+ int w = wcwidth(get_code(g));
+ if (w > 1)
+ width *= w;
+ if (real_size == unitwidth || font::use_unscaled_charwidths)
+ return width;
+ else
+ return scale(width, point_size);
+ }
+ assert(0 == "glyph is not indexed and device lacks Unicode support");
+ abort(); // -Wreturn-type
+}
+
+int font::get_height(glyph *g, int point_size)
+{
+ int idx = glyph_to_index(g);
+ assert(idx >= 0);
+ if (idx < nindices && ch_index[idx] >= 0) {
+ // Explicitly enumerated glyph
+ return scale(ch[ch_index[idx]].height, point_size);
+ }
+ if (is_unicode) {
+ // Unicode font
+ return 0;
+ }
+ assert(0 == "glyph is not indexed and device lacks Unicode support");
+ abort(); // -Wreturn-type
+}
+
+int font::get_depth(glyph *g, int point_size)
+{
+ int idx = glyph_to_index(g);
+ assert(idx >= 0);
+ if (idx < nindices && ch_index[idx] >= 0) {
+ // Explicitly enumerated glyph
+ return scale(ch[ch_index[idx]].depth, point_size);
+ }
+ if (is_unicode) {
+ // Unicode font
+ return 0;
+ }
+ assert(0 == "glyph is not indexed and device lacks Unicode support");
+ abort(); // -Wreturn-type
+}
+
+int font::get_italic_correction(glyph *g, int point_size)
+{
+ int idx = glyph_to_index(g);
+ assert(idx >= 0);
+ if (idx < nindices && ch_index[idx] >= 0) {
+ // Explicitly enumerated glyph
+ return scale(ch[ch_index[idx]].italic_correction, point_size);
+ }
+ if (is_unicode) {
+ // Unicode font
+ return 0;
+ }
+ assert(0 == "glyph is not indexed and device lacks Unicode support");
+ abort(); // -Wreturn-type
+}
+
+int font::get_left_italic_correction(glyph *g, int point_size)
+{
+ int idx = glyph_to_index(g);
+ assert(idx >= 0);
+ if (idx < nindices && ch_index[idx] >= 0) {
+ // Explicitly enumerated glyph
+ return scale(ch[ch_index[idx]].pre_math_space, point_size);
+ }
+ if (is_unicode) {
+ // Unicode font
+ return 0;
+ }
+ assert(0 == "glyph is not indexed and device lacks Unicode support");
+ abort(); // -Wreturn-type
+}
+
+int font::get_subscript_correction(glyph *g, int point_size)
+{
+ int idx = glyph_to_index(g);
+ assert(idx >= 0);
+ if (idx < nindices && ch_index[idx] >= 0) {
+ // Explicitly enumerated glyph
+ return scale(ch[ch_index[idx]].subscript_correction, point_size);
+ }
+ if (is_unicode) {
+ // Unicode font
+ return 0;
+ }
+ assert(0 == "glyph is not indexed and device lacks Unicode support");
+ abort(); // -Wreturn-type
+}
+
+void font::set_zoom(int factor)
+{
+ assert(factor >= 0);
+ if (factor == 1000)
+ zoom = 0;
+ else
+ zoom = factor;
+}
+
+int font::get_zoom()
+{
+ return zoom;
+}
+
+int font::get_space_width(int point_size)
+{
+ return scale(space_width, point_size);
+}
+
+font_kern_list::font_kern_list(glyph *g1, glyph *g2, int n, font_kern_list *p)
+: glyph1(g1), glyph2(g2), amount(n), next(p)
+{
+}
+
+inline int font::hash_kern(glyph *g1, glyph *g2)
+{
+ int n = ((glyph_to_index(g1) << 10) + glyph_to_index(g2))
+ % KERN_HASH_TABLE_SIZE;
+ return n < 0 ? -n : n;
+}
+
+void font::add_kern(glyph *g1, glyph *g2, 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(g1, g2);
+ *p = new font_kern_list(g1, g2, amount, *p);
+}
+
+int font::get_kern(glyph *g1, glyph *g2, int point_size)
+{
+ if (kern_hash_table) {
+ for (font_kern_list *p = kern_hash_table[hash_kern(g1, g2)]; p;
+ p = p->next)
+ if (g1 == p->glyph1 && g2 == p->glyph2)
+ return scale(p->amount, point_size);
+ }
+ return 0;
+}
+
+bool font::has_ligature(int mask)
+{
+ return (bool) (mask & ligatures);
+}
+
+int font::get_character_type(glyph *g)
+{
+ int idx = glyph_to_index(g);
+ assert(idx >= 0);
+ if (idx < nindices && ch_index[idx] >= 0) {
+ // Explicitly enumerated glyph
+ return ch[ch_index[idx]].type;
+ }
+ if (is_unicode) {
+ // Unicode font
+ return 0;
+ }
+ assert(0 == "glyph is not indexed and device lacks Unicode support");
+ abort(); // -Wreturn-type
+}
+
+int font::get_code(glyph *g)
+{
+ int idx = glyph_to_index(g);
+ assert(idx >= 0);
+ if (idx < nindices && ch_index[idx] >= 0) {
+ // Explicitly enumerated glyph
+ return ch[ch_index[idx]].code;
+ }
+ if (is_unicode) {
+ // Unicode font
+ // ASCII or Unicode character, or groff glyph name that maps to Unicode?
+ int uni = glyph_to_unicode(g);
+ if (uni >= 0)
+ return uni;
+ // Numbered character?
+ int n = glyph_to_number(g);
+ if (n >= 0)
+ return n;
+ }
+ // The caller must check 'contains(g)' before calling get_code(g).
+ assert(0 == "glyph is not indexed and device lacks Unicode support");
+ abort(); // -Wreturn-type
+}
+
+const char *font::get_name()
+{
+ return name;
+}
+
+const char *font::get_internal_name()
+{
+ return internalname;
+}
+
+const char *font::get_special_device_encoding(glyph *g)
+{
+ int idx = glyph_to_index(g);
+ assert(idx >= 0);
+ if (idx < nindices && ch_index[idx] >= 0) {
+ // Explicitly enumerated glyph
+ return ch[ch_index[idx]].special_device_coding;
+ }
+ if (is_unicode) {
+ // Unicode font
+ return 0;
+ }
+ assert(0 == "glyph is not indexed and device lacks Unicode support");
+ abort(); // -Wreturn-type
+}
+
+const char *font::get_image_generator()
+{
+ return image_generator;
+}
+
+void font::alloc_ch_index(int idx)
+{
+ if (nindices == 0) {
+ nindices = 128;
+ if (idx >= nindices)
+ nindices = idx + 10;
+ ch_index = new int[nindices];
+ for (int i = 0; i < nindices; i++)
+ ch_index[i] = -1;
+ }
+ else {
+ int old_nindices = nindices;
+ nindices *= 2;
+ if (idx >= nindices)
+ nindices = idx + 10;
+ int *old_ch_index = ch_index;
+ ch_index = new int[nindices];
+ memcpy(ch_index, old_ch_index, sizeof(int) * old_nindices);
+ for (int i = old_nindices; i < nindices; i++)
+ ch_index[i] = -1;
+ 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));
+ 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) {
+ int *old_ch_index = ch_index;
+ ch_index = new int[i];
+ memcpy(ch_index, old_ch_index, i*sizeof(int));
+ 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));
+ delete[] old_ch;
+ ch_size = ch_used;
+ }
+}
+
+void font::add_entry(glyph *g, const font_char_metric &metric)
+{
+ int idx = glyph_to_index(g);
+ assert(idx >= 0);
+ if (idx >= nindices)
+ alloc_ch_index(idx);
+ assert(idx < nindices);
+ if (ch_used + 1 >= ch_size)
+ extend_ch();
+ assert(ch_used + 1 < ch_size);
+ ch_index[idx] = ch_used;
+ ch[ch_used++] = metric;
+}
+
+void font::copy_entry(glyph *new_glyph, glyph *old_glyph)
+{
+ int new_index = glyph_to_index(new_glyph);
+ int old_index = glyph_to_index(old_glyph);
+ 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, bool load_header_only)
+{
+ font *f = new font(s);
+ if (!f->load(load_header_only)) {
+ delete f;
+ return 0;
+ }
+ return f;
+}
+
+static char *trim_arg(char *p)
+{
+ if (0 == p)
+ return 0;
+ while (csspace(*p))
+ p++;
+ char *q = strchr(p, '\0');
+ while (q > p && csspace(q[-1]))
+ q--;
+ *q = '\0';
+ return p;
+}
+
+bool 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;
+ bool attempt_file_open = true;
+ 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 true;
+ }
+ }
+ 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 true;
+ }
+ if (attempt_file_open) {
+ FILE *fp = fopen(p, "r");
+ if (fp != 0) {
+ if (fgets(line, 254, fp)) {
+ // Don't recurse on file names.
+ attempt_file_open = false;
+ char *linep = strchr(line, '\0');
+ // skip final newline, if any
+ if (*(--linep) == '\n')
+ *linep = '\0';
+ pp = line;
+ }
+ fclose(fp);
+ goto again;
+ }
+ }
+ }
+ return false;
+}
+
+bool font::load(bool load_header_only)
+{
+ FILE *fp;
+ char *path;
+ if ((fp = open_file(name, &path)) == 0)
+ return false;
+ text_file t(fp, path);
+ t.silent = load_header_only;
+ char *p = 0;
+ bool saw_name_directive = false;
+ while (t.next_line()) {
+ p = strtok(t.buf, WS);
+ if (strcmp(p, "name") == 0) {
+ p = strtok(0, WS);
+ if (0 == p) {
+ t.error("'name' directive requires an argument");
+ return false;
+ }
+ if (strcmp(p, name) != 0) {
+ t.error("font description file name '%1' does not match 'name'"
+ " argument '%2'", name, p);
+ return false;
+ }
+ saw_name_directive = true;
+ }
+ else if (strcmp(p, "spacewidth") == 0) {
+ p = strtok(0, WS);
+ int n;
+ if (0 == p) {
+ t.error("missing argument to 'spacewidth' directive");
+ return false;
+ }
+ if (sscanf(p, "%d", &n) != 1) {
+ t.error("invalid argument '%1' to 'spacewidth' directive", p);
+ return false;
+ }
+ if (n <= 0) {
+ t.error("'spacewidth' argument '%1' out of range", n);
+ return false;
+ }
+ space_width = n;
+ }
+ else if (strcmp(p, "slant") == 0) {
+ p = strtok(0, WS);
+ double n;
+ if (0 == p) {
+ t.error("missing argument to 'slant' directive");
+ return false;
+ }
+ if (sscanf(p, "%lf", &n) != 1) {
+ t.error("invalid argument '%1' to 'slant' directive", p);
+ return false;
+ }
+ if (n >= 90.0 || n <= -90.0) {
+ t.error("'slant' directive argument '%1' out of range", n);
+ return false;
+ }
+ slant = n;
+ }
+ else if (strcmp(p, "ligatures") == 0) {
+ for (;;) {
+ p = strtok(0, WS);
+ if (0 == p || 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("unrecognized ligature '%1'", p);
+ return false;
+ }
+ }
+ }
+ else if (strcmp(p, "internalname") == 0) {
+ p = strtok(0, WS);
+ if (0 == p) {
+ t.error("missing argument to 'internalname' directive");
+ return false;
+ }
+ internalname = new char[strlen(p) + 1];
+ strcpy(internalname, p);
+ }
+ else if (strcmp(p, "special") == 0) {
+ special = true;
+ }
+ else if (strcmp(p, "kernpairs") != 0 && strcmp(p, "charset") != 0) {
+ char *directive = p;
+ p = strtok(0, "\n");
+ handle_unknown_font_command(directive, trim_arg(p), t.path,
+ t.lineno);
+ }
+ else
+ break;
+ }
+ bool saw_charset_directive = false;
+ char *directive = p;
+ t.recognize_comments = false;
+ while (directive) {
+ if (strcmp(directive, "kernpairs") == 0) {
+ if (load_header_only)
+ return true;
+ for (;;) {
+ if (!t.next_line()) {
+ directive = 0;
+ break;
+ }
+ char *c1 = strtok(t.buf, WS);
+ if (0 == c1)
+ continue;
+ char *c2 = strtok(0, WS);
+ if (0 == c2) {
+ directive = c1;
+ break;
+ }
+ p = strtok(0, WS);
+ if (0 == p) {
+ t.error("missing kern amount for kerning pair '%1 %2'", c1,
+ c2);
+ return false;
+ }
+ int n;
+ if (sscanf(p, "%d", &n) != 1) {
+ t.error("invalid kern amount '%1' for kerning pair '%2 %3'",
+ p, c1, c2);
+ return false;
+ }
+ glyph *g1 = name_to_glyph(c1);
+ glyph *g2 = name_to_glyph(c2);
+ add_kern(g1, g2, n);
+ }
+ }
+ else if (strcmp(directive, "charset") == 0) {
+ if (load_header_only)
+ return true;
+ saw_charset_directive = true;
+ glyph *last_glyph = 0;
+ for (;;) {
+ if (!t.next_line()) {
+ directive = 0;
+ break;
+ }
+ char *nm = strtok(t.buf, WS);
+ assert(nm != 0);
+ p = strtok(0, WS);
+ if (0 == p) {
+ directive = nm;
+ break;
+ }
+ if (p[0] == '"') {
+ if (last_glyph == 0) {
+ t.error("the first entry ('%1') in 'charset' subsection"
+ " cannot be an alias", nm);
+ return false;
+ }
+ if (strcmp(nm, "---") == 0) {
+ t.error("an unnamed character ('---') cannot be an alias");
+ return false;
+ }
+ glyph *g = name_to_glyph(nm);
+ copy_entry(g, last_glyph);
+ }
+ 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("missing or invalid width for glyph '%1'", nm);
+ return false;
+ }
+ p = strtok(0, WS);
+ if (0 == p) {
+ t.error("missing character type for '%1'", nm);
+ return false;
+ }
+ int type;
+ if (sscanf(p, "%d", &type) != 1) {
+ t.error("invalid character type for '%1'", nm);
+ return false;
+ }
+ if (type < 0 || type > 255) {
+ t.error("character type '%1' out of range for '%2'", type,
+ nm);
+ return false;
+ }
+ metric.type = type;
+ p = strtok(0, WS);
+ if (0 == p) {
+ t.error("missing code for '%1'", nm);
+ return false;
+ }
+ char *ptr;
+ metric.code = (int)strtol(p, &ptr, 0);
+ if (metric.code == 0 && ptr == p) {
+ t.error("invalid code '%1' for character '%2'", p, nm);
+ return false;
+ }
+ if (is_unicode) {
+ int w = wcwidth(metric.code);
+ if (w > 1)
+ metric.width *= w;
+ }
+ p = strtok(0, WS);
+ if ((0 == p) || (strcmp(p, "--") == 0)) {
+ metric.special_device_coding = 0;
+ }
+ else {
+ char *nam = new char[strlen(p) + 1];
+ strcpy(nam, p);
+ metric.special_device_coding = nam;
+ }
+ if (strcmp(nm, "---") == 0) {
+ last_glyph = number_to_glyph(metric.code);
+ add_entry(last_glyph, metric);
+ }
+ else {
+ last_glyph = name_to_glyph(nm);
+ add_entry(last_glyph, metric);
+ copy_entry(number_to_glyph(metric.code), last_glyph);
+ }
+ }
+ }
+ if (0 == last_glyph) {
+ t.error("no glyphs defined in font description");
+ return false;
+ }
+ }
+ else {
+ t.error("unrecognized font description directive '%1' (missing"
+ " 'kernpairs' or 'charset'?)", directive);
+ return false;
+ }
+ }
+ compact();
+ t.lineno = 0;
+ if (!saw_name_directive) {
+ t.error("font description 'name' directive missing");
+ return false;
+ }
+ if (!is_unicode && !saw_charset_directive) {
+ t.error("font description 'charset' subsection missing");
+ return false;
+ }
+ if (space_width == 0) {
+ t.error("font description 'spacewidth' directive missing");
+ // _Don't_ return false; compute a typical one for Western glyphs.
+ if (zoom)
+ space_width = scale_round(unitwidth, res, 72 * 3 * sizescale,
+ zoom);
+ else
+ space_width = scale_round(unitwidth, res, 72 * 3 * sizescale);
+ }
+ return true;
+}
+
+static struct {
+ const char *numeric_directive;
+ 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 },
+ };
+
+// Return file specification of DESC file for selected output device if
+// it can be located and is valid, and a null pointer otherwise.
+const char *font::load_desc()
+{
+ int nfonts = 0;
+ FILE *fp;
+ char *path;
+ if ((fp = open_file("DESC", &path)) == 0)
+ return 0 /* nullptr */;
+ text_file t(fp, path);
+ while (t.next_line()) {
+ char *p = strtok(t.buf, WS);
+ assert(p != 0);
+ bool numeric_directive_found = false;
+ unsigned int idx;
+ for (idx = 0; !numeric_directive_found
+ && idx < sizeof(table) / sizeof(table[0]); idx++)
+ if (strcmp(table[idx].numeric_directive, p) == 0)
+ numeric_directive_found = true;
+ if (numeric_directive_found) {
+ char *q = strtok(0, WS);
+ if (0 == q) {
+ t.error("missing value for directive '%1'", p);
+ return 0 /* nullptr */;
+ }
+ int val;
+ if (sscanf(q, "%d", &val) != 1) {
+ t.error("'%1' directive given invalid number '%2'", p, q);
+ return 0 /* nullptr */;
+ }
+ if ((strcmp(p, "res") == 0
+ || strcmp(p, "hor") == 0
+ || strcmp(p, "vert") == 0
+ || strcmp(p, "unitwidth") == 0
+ || strcmp(p, "paperwidth") == 0
+ || strcmp(p, "paperlength") == 0
+ || strcmp(p, "sizescale") == 0)
+ && val < 1) {
+ t.error("expected argument to '%1' directive to be a"
+ " positive number, got '%2'", p, val);
+ return 0 /* nullptr */;
+ }
+ *(table[idx-1].ptr) = val;
+ }
+ else if (strcmp("family", p) == 0) {
+ p = strtok(0, WS);
+ if (0 == p) {
+ t.error("'family' directive requires an argument");
+ return 0 /* nullptr */;
+ }
+ char *tem = new char[strlen(p)+1];
+ strcpy(tem, p);
+ family = tem;
+ }
+ else if (strcmp("fonts", p) == 0) {
+ p = strtok(0, WS);
+ if (0 == p) {
+ t.error("'fonts' directive requires arguments");
+ return 0 /* nullptr */;
+ }
+ if (sscanf(p, "%d", &nfonts) != 1 || nfonts <= 0) {
+ t.error("expected first argument to 'fonts' directive to be a"
+ " non-negative number, got '%1'", p);
+ return 0 /* nullptr */;
+ }
+ font_name_table = (const char **)new char *[nfonts+1];
+ for (int i = 0; i < nfonts; i++) {
+ p = strtok(0, WS);
+ while (0 == p) {
+ if (!t.next_line()) {
+ t.error("unexpected end of file while reading font list");
+ return 0 /* nullptr */;
+ }
+ 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 declared number of fonts"
+ " ('%1')", nfonts);
+ return 0 /* nullptr */;
+ }
+ font_name_table[nfonts] = 0;
+ }
+ else if (strcmp("papersize", p) == 0) {
+ if (0 == res) {
+ t.error("'res' directive must precede 'papersize' in device"
+ " description file");
+ return 0 /* nullptr */;
+ }
+ p = strtok(0, WS);
+ if (0 == p) {
+ t.error("'papersize' directive requires an argument");
+ return 0 /* nullptr */;
+ }
+ bool found_paper = false;
+ char *savedp = strdup(p);
+ if (0 == savedp)
+ t.fatal("memory allocation failure while processing 'papersize'"
+ " directive");
+ 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 = true;
+ break;
+ }
+ p = strtok(0, WS);
+ }
+ assert(savedp != 0);
+ if (!found_paper) {
+ t.error("unable to determine a paper format from '%1'", savedp);
+ free(savedp);
+ return 0 /* nullptr */;
+ }
+ free(savedp);
+ }
+ else if (strcmp("unscaled_charwidths", p) == 0)
+ use_unscaled_charwidths = true;
+ else if (strcmp("pass_filenames", p) == 0)
+ pass_filenames = true;
+ else if (strcmp("sizes", p) == 0) {
+ int n = 16;
+ sizes = new int[n];
+ int i = 0;
+ for (;;) {
+ p = strtok(0, WS);
+ while (0 == p) {
+ if (!t.next_line()) {
+ t.error("list of sizes must be terminated by '0'");
+ return 0 /* nullptr */;
+ }
+ 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("invalid size range '%1'", p);
+ return 0 /* nullptr */;
+ }
+ if (i + 2 > n) {
+ int *old_sizes = sizes;
+ sizes = new int[n*2];
+ memcpy(sizes, old_sizes, n*sizeof(int));
+ n *= 2;
+ delete[] old_sizes;
+ }
+ sizes[i++] = lower;
+ if (lower == 0)
+ break;
+ sizes[i++] = upper;
+ }
+ if (i == 1) {
+ t.error("must have some sizes");
+ return 0 /* nullptr */;
+ }
+ }
+ 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 (0 == p)
+ 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;
+ delete[] old_style_table;
+ }
+ char *tem = new char[strlen(p) + 1];
+ strcpy(tem, p);
+ style_table[i++] = tem;
+ }
+ }
+ else if (strcmp("tcommand", p) == 0)
+ has_tcommand = true;
+ else if (strcmp("use_charnames_in_special", p) == 0)
+ use_charnames_in_special = true;
+ else if (strcmp("unicode", p) == 0)
+ is_unicode = true;
+ else if (strcmp("image_generator", p) == 0) {
+ p = strtok(0, WS);
+ if (0 == p) {
+ t.error("'image_generator' directive requires an argument");
+ return 0 /* nullptr */;
+ }
+ image_generator = strsave(p);
+ }
+ else if (strcmp("charset", p) == 0)
+ break;
+ else if (unknown_desc_command_handler) {
+ char *directive = p;
+ p = strtok(0, "\n");
+ (*unknown_desc_command_handler)(directive, trim_arg(p), t.path,
+ t.lineno);
+ }
+ }
+ t.lineno = 0;
+ if (res == 0) {
+ t.error("device description file missing 'res' directive");
+ return 0 /* nullptr */;
+ }
+ if (unitwidth == 0) {
+ t.error("device description file missing 'unitwidth' directive");
+ return 0 /* nullptr */;
+ }
+ if (font_name_table == 0) {
+ t.error("device description file missing 'fonts' directive");
+ return 0 /* nullptr */;
+ }
+ if (sizes == 0) {
+ t.error("device description file missing 'sizes' directive");
+ return 0 /* nullptr */;
+ }
+ return path;
+}
+
+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;
+}
+
+// Local Variables:
+// fill-column: 72
+// mode: C++
+// End:
+// vim: set cindent noexpandtab shiftwidth=2 textwidth=72:
diff --git a/src/libs/libgroff/fontfile.cpp b/src/libs/libgroff/fontfile.cpp
new file mode 100644
index 0000000..8987971
--- /dev/null
+++ b/src/libs/libgroff/fontfile.cpp
@@ -0,0 +1,80 @@
+/* Copyright (C) 1989-2020 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 3 of the License, 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 this program. If not, see <http://www.gnu.org/licenses/>. */
+
+#include "lib.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;
+bool font::has_tcommand = false;
+bool font::pass_filenames = false;
+bool font::use_unscaled_charwidths = false;
+bool font::use_charnames_in_special = false;
+bool font::is_unicode = false;
+const char *font::image_generator = 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 *nm, char **pathp)
+{
+ FILE *fp = 0 /* nullptr */;
+ // Do not traverse user-specified directories; Savannah #61424.
+ if (0 /* nullptr */ == strchr(nm, '/')) {
+ // Allocate enough for nm + device + 'dev' '/' '\0'.
+ int expected_size = strlen(nm) + strlen(device) + 5;
+ char *filename = new char[expected_size];
+ const int actual_size = sprintf(filename, "dev%s/%s", device, nm);
+ expected_size--; // sprintf() doesn't count the null terminator.
+ if (actual_size == expected_size)
+ fp = font_path.open_file(filename, pathp);
+ delete[] filename;
+ }
+ return fp;
+}
+
+// Local Variables:
+// fill-column: 72
+// mode: C++
+// End:
+// vim: set cindent noexpandtab shiftwidth=2 textwidth=72:
diff --git a/src/libs/libgroff/geometry.cpp b/src/libs/libgroff/geometry.cpp
new file mode 100644
index 0000000..c4665c4
--- /dev/null
+++ b/src/libs/libgroff/geometry.cpp
@@ -0,0 +1,180 @@
+// -*- C++ -*-
+/* Copyright (C) 1989-2020 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 3 of the License, 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 this program. If not, see <http://www.gnu.org/licenses/>. */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#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.
+ */
+
+void check_output_arc_limits(int x_1, int y_1,
+ int xv_1, int yv_1,
+ int xv_2, int yv_2,
+ double c_0, double c_1,
+ int *minx, int *maxx,
+ int *miny, int *maxy)
+{
+ int radius = (int)sqrt(c_0 * c_0 + c_1 * c_1);
+ // clockwise direction
+ int xcenter = x_1 + xv_1;
+ int ycenter = y_1 + yv_1;
+ int xend = xcenter + xv_2;
+ int yend = ycenter + yv_2;
+ // for convenience, transform to counterclockwise direction,
+ // centered at the origin
+ int xs = xend - xcenter;
+ int ys = yend - ycenter;
+ int xe = x_1 - xcenter;
+ int ye = y_1 - ycenter;
+ *minx = *maxx = xs;
+ *miny = *maxy = ys;
+ if (xe > *maxx)
+ *maxx = xe;
+ else if (xe < *minx)
+ *minx = xe;
+ if (ye > *maxy)
+ *maxy = ye;
+ else if (ye < *miny)
+ *miny = ye;
+ int qs, qe; // quadrants 0..3
+ if (xs >= 0)
+ qs = (ys >= 0) ? 0 : 3;
+ else
+ qs = (ys >= 0) ? 1 : 2;
+ if (xe >= 0)
+ qe = (ye >= 0) ? 0 : 3;
+ else
+ qe = (ye >= 0) ? 1 : 2;
+ // make qs always smaller than qe
+ if ((qs > qe)
+ || ((qs == qe) && (double(xs) * ye < double(xe) * ys)))
+ qe += 4;
+ for (int i = qs; i < qe; i++)
+ switch (i % 4) {
+ case 0:
+ *maxy = radius;
+ break;
+ case 1:
+ *minx = -radius;
+ break;
+ case 2:
+ *miny = -radius;
+ break;
+ case 3:
+ *maxx = radius;
+ break;
+ }
+ *minx += xcenter;
+ *maxx += xcenter;
+ *miny += ycenter;
+ *maxy += ycenter;
+}
diff --git a/src/libs/libgroff/getcwd.c b/src/libs/libgroff/getcwd.c
new file mode 100644
index 0000000..dd8b578
--- /dev/null
+++ b/src/libs/libgroff/getcwd.c
@@ -0,0 +1,54 @@
+/* Copyright (C) 2000-2020 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 3 of the License, 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 this program. If not, see <http://www.gnu.org/licenses/>. */
+
+/* Partial emulation of getcwd in terms of getwd. */
+
+#include <config.h>
+#include <sys/param.h>
+#include <string.h>
+#include <errno.h>
+
+char *getwd();
+
+char *getcwd(buf, size)
+ char *buf;
+ int size; /* POSIX says this should be size_t */
+{
+ if (size <= 0) {
+ errno = EINVAL;
+ return 0;
+ }
+ else {
+ char mybuf[MAXPATHLEN];
+ int saved_errno = errno;
+
+ errno = 0;
+ if (!getwd(mybuf)) {
+ if (errno == 0)
+ ; /* what to do? */
+ return 0;
+ }
+ errno = saved_errno;
+ if (strlen(mybuf) + 1 > size) {
+ errno = ERANGE;
+ return 0;
+ }
+ strcpy(buf, mybuf);
+ return buf;
+ }
+}
diff --git a/src/libs/libgroff/getopt.c b/src/libs/libgroff/getopt.c
new file mode 100644
index 0000000..6efa529
--- /dev/null
+++ b/src/libs/libgroff/getopt.c
@@ -0,0 +1,1241 @@
+/* Getopt for GNU.
+ NOTE: getopt is now part of the C library, so if you don't know what
+ "Keep this file name-space clean" means, talk to drepper@gnu.org
+ before changing it!
+ Copyright (C) 1987-2020 Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
+
+ 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 3 of the License, 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, see <http://www.gnu.org/licenses/>. */
+
+/* This tells Alpha OSF/1 not to define a getopt prototype in <stdio.h>.
+ Ditto for AIX 3.2 and <stdlib.h>. */
+#ifndef _NO_PROTO
+# define _NO_PROTO
+#endif
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include <stdio.h>
+
+/* This needs to come after some library #include
+ to get __GNU_LIBRARY__ defined. */
+#ifdef __GNU_LIBRARY__
+/* Don't include stdlib.h for non-GNU C libraries because some of them
+ contain conflicting prototypes for getopt. */
+# include <stdlib.h>
+# include <unistd.h>
+#endif /* GNU C library. */
+
+#include <string.h>
+
+#ifdef VMS
+# include <unixlib.h>
+#endif
+
+#ifdef _LIBC
+# include <libintl.h>
+#else
+# include "gettext.h"
+# define _(msgid) gettext (msgid)
+#endif
+
+#if defined _LIBC && defined USE_IN_LIBIO
+# include <wchar.h>
+#endif
+
+#ifndef attribute_hidden
+# define attribute_hidden
+#endif
+
+/* Unlike standard Unix 'getopt', functions like 'getopt_long'
+ let the user intersperse the options with the other arguments.
+
+ As 'getopt_long' works, it permutes the elements of ARGV so that,
+ when it is done, all the options precede everything else. Thus
+ all application programs are extended to handle flexible argument order.
+
+ Using 'getopt' or setting the environment variable POSIXLY_CORRECT
+ disables permutation.
+ Then the application's behavior is completely standard.
+
+ GNU application programs can use a third alternative mode in which
+ they can distinguish the relative order of options and other arguments. */
+
+#include "getopt.h"
+#include "getopt_int.h"
+
+/* For communication from 'getopt' to the caller.
+ When 'getopt' finds an option that takes an argument,
+ the argument value is returned here.
+ Also, when 'ordering' is RETURN_IN_ORDER,
+ each non-option ARGV-element is returned here. */
+
+char *optarg;
+
+/* Index in ARGV of the next element to be scanned.
+ This is used for communication to and from the caller
+ and for communication between successive calls to 'getopt'.
+
+ On entry to 'getopt', zero means this is the first call; initialize.
+
+ When 'getopt' returns -1, this is the index of the first of the
+ non-option elements that the caller should itself scan.
+
+ Otherwise, 'optind' communicates from one call to the next
+ how much of ARGV has been scanned so far. */
+
+/* 1003.2 says this must be 1 before any call. */
+int optind = 1;
+
+/* Callers store zero here to inhibit the error message
+ for unrecognized options. */
+
+int opterr = 1;
+
+/* Set to an option character which was unrecognized.
+ This must be initialized on some systems to avoid linking in the
+ system's own getopt implementation. */
+
+int optopt = '?';
+
+/* Keep a global copy of all internal members of getopt_data. */
+
+static struct _getopt_data getopt_data;
+
+
+#ifndef __GNU_LIBRARY__
+
+/* Avoid depending on library functions or files
+ whose names are inconsistent. */
+
+#ifndef getenv
+extern char *getenv ();
+#endif
+
+#endif /* not __GNU_LIBRARY__ */
+
+#ifdef _LIBC
+/* Stored original parameters.
+ XXX This is no good solution. We should rather copy the args so
+ that we can compare them later. But we must not use malloc(3). */
+extern int __libc_argc;
+extern char **__libc_argv;
+
+/* Bash 2.0 gives us an environment variable containing flags
+ indicating ARGV elements that should not be considered arguments. */
+
+# ifdef USE_NONOPTION_FLAGS
+/* Defined in getopt_init.c */
+extern char *__getopt_nonoption_flags;
+# endif
+
+# ifdef USE_NONOPTION_FLAGS
+# define SWAP_FLAGS(ch1, ch2) \
+ if (d->__nonoption_flags_len > 0) \
+ { \
+ char __tmp = __getopt_nonoption_flags[ch1]; \
+ __getopt_nonoption_flags[ch1] = __getopt_nonoption_flags[ch2]; \
+ __getopt_nonoption_flags[ch2] = __tmp; \
+ }
+# else
+# define SWAP_FLAGS(ch1, ch2)
+# endif
+#else /* !_LIBC */
+# define SWAP_FLAGS(ch1, ch2)
+#endif /* _LIBC */
+
+/* Exchange two adjacent subsequences of ARGV.
+ One subsequence is elements [first_nonopt,last_nonopt)
+ which contains all the non-options that have been skipped so far.
+ The other is elements [last_nonopt,optind), which contains all
+ the options processed since those non-options were skipped.
+
+ 'first_nonopt' and 'last_nonopt' are relocated so that they describe
+ the new indices of the non-options in ARGV after they are moved. */
+
+static void
+exchange (char **argv, struct _getopt_data *d)
+{
+ int bottom = d->__first_nonopt;
+ int middle = d->__last_nonopt;
+ int top = d->optind;
+ char *tem;
+
+ /* Exchange the shorter segment with the far end of the longer segment.
+ That puts the shorter segment into the right place.
+ It leaves the longer segment in the right place overall,
+ but it consists of two parts that need to be swapped next. */
+
+#if defined _LIBC && defined USE_NONOPTION_FLAGS
+ /* First make sure the handling of the '__getopt_nonoption_flags'
+ string can work normally. Our top argument must be in the range
+ of the string. */
+ if (d->__nonoption_flags_len > 0 && top >= d->__nonoption_flags_max_len)
+ {
+ /* We must extend the array. The user plays games with us and
+ presents new arguments. */
+ char *new_str = malloc (top + 1);
+ if (new_str == NULL)
+ d->__nonoption_flags_len = d->__nonoption_flags_max_len = 0;
+ else
+ {
+ memset (__mempcpy (new_str, __getopt_nonoption_flags,
+ d->__nonoption_flags_max_len),
+ '\0', top + 1 - d->__nonoption_flags_max_len);
+ d->__nonoption_flags_max_len = top + 1;
+ __getopt_nonoption_flags = new_str;
+ }
+ }
+#endif
+
+ while (top > middle && middle > bottom)
+ {
+ if (top - middle > middle - bottom)
+ {
+ /* Bottom segment is the short one. */
+ int len = middle - bottom;
+ register int i;
+
+ /* Swap it with the top part of the top segment. */
+ for (i = 0; i < len; i++)
+ {
+ tem = argv[bottom + i];
+ argv[bottom + i] = argv[top - (middle - bottom) + i];
+ argv[top - (middle - bottom) + i] = tem;
+ SWAP_FLAGS (bottom + i, top - (middle - bottom) + i);
+ }
+ /* Exclude the moved bottom segment from further swapping. */
+ top -= len;
+ }
+ else
+ {
+ /* Top segment is the short one. */
+ int len = top - middle;
+ register int i;
+
+ /* Swap it with the bottom part of the bottom segment. */
+ for (i = 0; i < len; i++)
+ {
+ tem = argv[bottom + i];
+ argv[bottom + i] = argv[middle + i];
+ argv[middle + i] = tem;
+ SWAP_FLAGS (bottom + i, middle + i);
+ }
+ /* Exclude the moved top segment from further swapping. */
+ bottom += len;
+ }
+ }
+
+ /* Update records for the slots the non-options now occupy. */
+
+ d->__first_nonopt += (d->optind - d->__last_nonopt);
+ d->__last_nonopt = d->optind;
+}
+
+/* Initialize the internal data when the first call is made. */
+
+static const char *
+_getopt_initialize (__attribute__((__unused__)) int argc,
+ __attribute__((__unused__)) char **argv,
+ const char *optstring, int posixly_correct,
+ struct _getopt_data *d)
+{
+ /* Start processing options with ARGV-element 1 (since ARGV-element 0
+ is the program name); the sequence of previously skipped
+ non-option ARGV-elements is empty. */
+
+ d->__first_nonopt = d->__last_nonopt = d->optind;
+
+ d->__nextchar = NULL;
+
+ d->__posixly_correct = posixly_correct || !!getenv ("POSIXLY_CORRECT");
+
+ /* Determine how to handle the ordering of options and nonoptions. */
+
+ if (optstring[0] == '-')
+ {
+ d->__ordering = RETURN_IN_ORDER;
+ ++optstring;
+ }
+ else if (optstring[0] == '+')
+ {
+ d->__ordering = REQUIRE_ORDER;
+ ++optstring;
+ }
+ else if (d->__posixly_correct)
+ d->__ordering = REQUIRE_ORDER;
+ else
+ d->__ordering = PERMUTE;
+
+#if defined _LIBC && defined USE_NONOPTION_FLAGS
+ if (!d->__posixly_correct
+ && argc == __libc_argc && argv == __libc_argv)
+ {
+ if (d->__nonoption_flags_max_len == 0)
+ {
+ if (__getopt_nonoption_flags == NULL
+ || __getopt_nonoption_flags[0] == '\0')
+ d->__nonoption_flags_max_len = -1;
+ else
+ {
+ const char *orig_str = __getopt_nonoption_flags;
+ int len = d->__nonoption_flags_max_len = strlen (orig_str);
+ if (d->__nonoption_flags_max_len < argc)
+ d->__nonoption_flags_max_len = argc;
+ __getopt_nonoption_flags =
+ (char *) malloc (d->__nonoption_flags_max_len);
+ if (__getopt_nonoption_flags == NULL)
+ d->__nonoption_flags_max_len = -1;
+ else
+ memset (__mempcpy (__getopt_nonoption_flags, orig_str, len),
+ '\0', d->__nonoption_flags_max_len - len);
+ }
+ }
+ d->__nonoption_flags_len = d->__nonoption_flags_max_len;
+ }
+ else
+ d->__nonoption_flags_len = 0;
+#endif
+
+ return optstring;
+}
+
+/* Scan elements of ARGV (whose length is ARGC) for option characters
+ given in OPTSTRING.
+
+ If an element of ARGV starts with '-', and is not exactly "-" or "--",
+ then it is an option element. The characters of this element
+ (aside from the initial '-') are option characters. If 'getopt'
+ is called repeatedly, it returns successively each of the option characters
+ from each of the option elements.
+
+ If 'getopt' finds another option character, it returns that character,
+ updating 'optind' and 'nextchar' so that the next call to 'getopt' can
+ resume the scan with the following option character or ARGV-element.
+
+ If there are no more option characters, 'getopt' returns -1.
+ Then 'optind' is the index in ARGV of the first ARGV-element
+ that is not an option. (The ARGV-elements have been permuted
+ so that those that are not options now come last.)
+
+ OPTSTRING is a string containing the legitimate option characters.
+ If an option character is seen that is not listed in OPTSTRING,
+ return '?' after printing an error message. If you set 'opterr' to
+ zero, the error message is suppressed but we still return '?'.
+
+ If a char in OPTSTRING is followed by a colon, that means it wants an arg,
+ so the following text in the same ARGV-element, or the text of the following
+ ARGV-element, is returned in 'optarg'. Two colons mean an option that
+ wants an optional arg; if there is text in the current ARGV-element,
+ it is returned in 'optarg', otherwise 'optarg' is set to zero.
+
+ If OPTSTRING starts with '-' or '+', it requests different methods of
+ handling the non-option ARGV-elements.
+ See the comments about RETURN_IN_ORDER and REQUIRE_ORDER, above.
+
+ Long-named options begin with '--' instead of '-'.
+ Their names may be abbreviated as long as the abbreviation is unique
+ or is an exact match for some defined option. If they have an
+ argument, it follows the option name in the same ARGV-element, separated
+ from the option name by a '=', or else the in next ARGV-element.
+ When 'getopt' finds a long-named option, it returns 0 if that option's
+ 'flag' field is nonzero, the value of the option's 'val' field
+ if the 'flag' field is zero.
+
+ LONGOPTS is a vector of 'struct option' terminated by an
+ element containing a name which is zero.
+
+ LONGIND returns the index in LONGOPT of the long-named option found.
+ It is only valid when a long-named option has been found by the most
+ recent call.
+
+ If LONG_ONLY is nonzero, '-' as well as '--' can introduce
+ long-named options.
+
+ If POSIXLY_CORRECT is nonzero, behave as if the POSIXLY_CORRECT
+ environment variable were set. */
+
+int
+_getopt_internal_r (int argc, char **argv, const char *optstring,
+ const struct option *longopts, int *longind,
+ int long_only, int posixly_correct, struct _getopt_data *d)
+{
+ int print_errors = d->opterr;
+ if (optstring[0] == ':')
+ print_errors = 0;
+
+ if (argc < 1)
+ return -1;
+
+ d->optarg = NULL;
+
+ if (d->optind == 0 || !d->__initialized)
+ {
+ if (d->optind == 0)
+ d->optind = 1; /* Don't scan ARGV[0], the program name. */
+ optstring = _getopt_initialize (argc, argv, optstring,
+ posixly_correct, d);
+ d->__initialized = 1;
+ }
+
+ /* Test whether ARGV[optind] points to a non-option argument.
+ Either it does not have option syntax, or there is an environment flag
+ from the shell indicating it is not an option. The later information
+ is only used when the used in the GNU libc. */
+#if defined _LIBC && defined USE_NONOPTION_FLAGS
+# define NONOPTION_P (argv[d->optind][0] != '-' || argv[d->optind][1] == '\0' \
+ || (d->optind < d->__nonoption_flags_len \
+ && __getopt_nonoption_flags[d->optind] == '1'))
+#else
+# define NONOPTION_P (argv[d->optind][0] != '-' || argv[d->optind][1] == '\0')
+#endif
+
+ if (d->__nextchar == NULL || *d->__nextchar == '\0')
+ {
+ /* Advance to the next ARGV-element. */
+
+ /* Give FIRST_NONOPT & LAST_NONOPT rational values if OPTIND has been
+ moved back by the user (who may also have changed the arguments). */
+ if (d->__last_nonopt > d->optind)
+ d->__last_nonopt = d->optind;
+ if (d->__first_nonopt > d->optind)
+ d->__first_nonopt = d->optind;
+
+ if (d->__ordering == PERMUTE)
+ {
+ /* If we have just processed some options following some non-options,
+ exchange them so that the options come first. */
+
+ if (d->__first_nonopt != d->__last_nonopt
+ && d->__last_nonopt != d->optind)
+ exchange ((char **) argv, d);
+ else if (d->__last_nonopt != d->optind)
+ d->__first_nonopt = d->optind;
+
+ /* Skip any additional non-options
+ and extend the range of non-options previously skipped. */
+
+ while (d->optind < argc && NONOPTION_P)
+ d->optind++;
+ d->__last_nonopt = d->optind;
+ }
+
+ /* The special ARGV-element '--' means premature end of options.
+ Skip it like a null option,
+ then exchange with previous non-options as if it were an option,
+ then skip everything else like a non-option. */
+
+ if (d->optind != argc && !strcmp (argv[d->optind], "--"))
+ {
+ d->optind++;
+
+ if (d->__first_nonopt != d->__last_nonopt
+ && d->__last_nonopt != d->optind)
+ exchange ((char **) argv, d);
+ else if (d->__first_nonopt == d->__last_nonopt)
+ d->__first_nonopt = d->optind;
+ d->__last_nonopt = argc;
+
+ d->optind = argc;
+ }
+
+ /* If we have done all the ARGV-elements, stop the scan
+ and back over any non-options that we skipped and permuted. */
+
+ if (d->optind == argc)
+ {
+ /* Set the next-arg-index to point at the non-options
+ that we previously skipped, so the caller will digest them. */
+ if (d->__first_nonopt != d->__last_nonopt)
+ d->optind = d->__first_nonopt;
+ return -1;
+ }
+
+ /* If we have come to a non-option and did not permute it,
+ either stop the scan or describe it to the caller and pass it by. */
+
+ if (NONOPTION_P)
+ {
+ if (d->__ordering == REQUIRE_ORDER)
+ return -1;
+ d->optarg = argv[d->optind++];
+ return 1;
+ }
+
+ /* We have found another option-ARGV-element.
+ Skip the initial punctuation. */
+
+ d->__nextchar = (argv[d->optind] + 1
+ + (longopts != NULL && argv[d->optind][1] == '-'));
+ }
+
+ /* Decode the current option-ARGV-element. */
+
+ /* Check whether the ARGV-element is a long option.
+
+ If long_only and the ARGV-element has the form "-f", where f is
+ a valid short option, don't consider it an abbreviated form of
+ a long option that starts with f. Otherwise there would be no
+ way to give the -f short option.
+
+ On the other hand, if there's a long option "fubar" and
+ the ARGV-element is "-fu", do consider that an abbreviation of
+ the long option, just like "--fu", and not "-f" with arg "u".
+
+ This distinction seems to be the most useful approach. */
+
+ if (longopts != NULL
+ && (argv[d->optind][1] == '-'
+ || (long_only && (argv[d->optind][2]
+ || !strchr (optstring, argv[d->optind][1])))))
+ {
+ char *nameend;
+ const struct option *p;
+ const struct option *pfound = NULL;
+ int exact = 0;
+ int ambig = 0;
+ int indfound = -1;
+ int option_index;
+
+ for (nameend = d->__nextchar; *nameend && *nameend != '='; nameend++)
+ /* Do nothing. */ ;
+
+ /* Test all long options for either exact match
+ or abbreviated matches. */
+ for (p = longopts, option_index = 0; p->name; p++, option_index++)
+ if (!strncmp (p->name, d->__nextchar, nameend - d->__nextchar))
+ {
+ if ((unsigned int) (nameend - d->__nextchar)
+ == (unsigned int) strlen (p->name))
+ {
+ /* Exact match found. */
+ pfound = p;
+ indfound = option_index;
+ exact = 1;
+ break;
+ }
+ else if (pfound == NULL)
+ {
+ /* First nonexact match found. */
+ pfound = p;
+ indfound = option_index;
+ }
+ else if (long_only
+ || pfound->has_arg != p->has_arg
+ || pfound->flag != p->flag
+ || pfound->val != p->val)
+ /* Second or later nonexact match found. */
+ ambig = 1;
+ }
+
+ if (ambig && !exact)
+ {
+ if (print_errors)
+ {
+#if defined _LIBC && defined USE_IN_LIBIO
+ char *buf;
+
+ if (__asprintf (&buf, _("%s: option '%s' is ambiguous\n"),
+ argv[0], argv[d->optind]) >= 0)
+ {
+ _IO_flockfile (stderr);
+
+ int old_flags2 = ((_IO_FILE *) stderr)->_flags2;
+ ((_IO_FILE *) stderr)->_flags2 |= _IO_FLAGS2_NOTCANCEL;
+
+ if (_IO_fwide (stderr, 0) > 0)
+ __fwprintf (stderr, L"%s", buf);
+ else
+ fputs (buf, stderr);
+
+ ((_IO_FILE *) stderr)->_flags2 = old_flags2;
+ _IO_funlockfile (stderr);
+
+ free (buf);
+ }
+#else
+ fprintf (stderr, _("%s: option '%s' is ambiguous\n"),
+ argv[0], argv[d->optind]);
+#endif
+ }
+ d->__nextchar += strlen (d->__nextchar);
+ d->optind++;
+ d->optopt = 0;
+ return '?';
+ }
+
+ if (pfound != NULL)
+ {
+ option_index = indfound;
+ d->optind++;
+ if (*nameend)
+ {
+ /* Don't test has_arg with >, because some C compilers don't
+ allow it to be used on enums. */
+ if (pfound->has_arg)
+ d->optarg = nameend + 1;
+ else
+ {
+ if (print_errors)
+ {
+#if defined _LIBC && defined USE_IN_LIBIO
+ char *buf;
+ int n;
+#endif
+
+ if (argv[d->optind - 1][1] == '-')
+ {
+ /* --option */
+#if defined _LIBC && defined USE_IN_LIBIO
+ n = __asprintf (&buf, _("\
+%s: option '--%s' doesn't allow an argument\n"),
+ argv[0], pfound->name);
+#else
+ fprintf (stderr, _("\
+%s: option '--%s' doesn't allow an argument\n"),
+ argv[0], pfound->name);
+#endif
+ }
+ else
+ {
+ /* +option or -option */
+#if defined _LIBC && defined USE_IN_LIBIO
+ n = __asprintf (&buf, _("\
+%s: option '%c%s' doesn't allow an argument\n"),
+ argv[0], argv[d->optind - 1][0],
+ pfound->name);
+#else
+ fprintf (stderr, _("\
+%s: option '%c%s' doesn't allow an argument\n"),
+ argv[0], argv[d->optind - 1][0],
+ pfound->name);
+#endif
+ }
+
+#if defined _LIBC && defined USE_IN_LIBIO
+ if (n >= 0)
+ {
+ _IO_flockfile (stderr);
+
+ int old_flags2 = ((_IO_FILE *) stderr)->_flags2;
+ ((_IO_FILE *) stderr)->_flags2
+ |= _IO_FLAGS2_NOTCANCEL;
+
+ if (_IO_fwide (stderr, 0) > 0)
+ __fwprintf (stderr, L"%s", buf);
+ else
+ fputs (buf, stderr);
+
+ ((_IO_FILE *) stderr)->_flags2 = old_flags2;
+ _IO_funlockfile (stderr);
+
+ free (buf);
+ }
+#endif
+ }
+
+ d->__nextchar += strlen (d->__nextchar);
+
+ d->optopt = pfound->val;
+ return '?';
+ }
+ }
+ else if (pfound->has_arg == 1)
+ {
+ if (d->optind < argc)
+ d->optarg = argv[d->optind++];
+ else
+ {
+ if (print_errors)
+ {
+#if defined _LIBC && defined USE_IN_LIBIO
+ char *buf;
+
+ if (__asprintf (&buf, _("\
+%s: option '%s' requires an argument\n"),
+ argv[0], argv[d->optind - 1]) >= 0)
+ {
+ _IO_flockfile (stderr);
+
+ int old_flags2 = ((_IO_FILE *) stderr)->_flags2;
+ ((_IO_FILE *) stderr)->_flags2
+ |= _IO_FLAGS2_NOTCANCEL;
+
+ if (_IO_fwide (stderr, 0) > 0)
+ __fwprintf (stderr, L"%s", buf);
+ else
+ fputs (buf, stderr);
+
+ ((_IO_FILE *) stderr)->_flags2 = old_flags2;
+ _IO_funlockfile (stderr);
+
+ free (buf);
+ }
+#else
+ fprintf (stderr,
+ _("%s: option '%s' requires an argument\n"),
+ argv[0], argv[d->optind - 1]);
+#endif
+ }
+ d->__nextchar += strlen (d->__nextchar);
+ d->optopt = pfound->val;
+ return optstring[0] == ':' ? ':' : '?';
+ }
+ }
+ d->__nextchar += strlen (d->__nextchar);
+ if (longind != NULL)
+ *longind = option_index;
+ if (pfound->flag)
+ {
+ *(pfound->flag) = pfound->val;
+ return 0;
+ }
+ return pfound->val;
+ }
+
+ /* Can't find it as a long option. If this is not getopt_long_only,
+ or the option starts with '--' or is not a valid short
+ option, then it's an error.
+ Otherwise interpret it as a short option. */
+ if (!long_only || argv[d->optind][1] == '-'
+ || strchr (optstring, *d->__nextchar) == NULL)
+ {
+ if (print_errors)
+ {
+#if defined _LIBC && defined USE_IN_LIBIO
+ char *buf;
+ int n;
+#endif
+
+ if (argv[d->optind][1] == '-')
+ {
+ /* --option */
+#if defined _LIBC && defined USE_IN_LIBIO
+ n = __asprintf (&buf, _("%s: unrecognized option '--%s'\n"),
+ argv[0], d->__nextchar);
+#else
+ fprintf (stderr, _("%s: unrecognized option '--%s'\n"),
+ argv[0], d->__nextchar);
+#endif
+ }
+ else
+ {
+ /* +option or -option */
+#if defined _LIBC && defined USE_IN_LIBIO
+ n = __asprintf (&buf, _("%s: unrecognized option '%c%s'\n"),
+ argv[0], argv[d->optind][0], d->__nextchar);
+#else
+ fprintf (stderr, _("%s: unrecognized option '%c%s'\n"),
+ argv[0], argv[d->optind][0], d->__nextchar);
+#endif
+ }
+
+#if defined _LIBC && defined USE_IN_LIBIO
+ if (n >= 0)
+ {
+ _IO_flockfile (stderr);
+
+ int old_flags2 = ((_IO_FILE *) stderr)->_flags2;
+ ((_IO_FILE *) stderr)->_flags2 |= _IO_FLAGS2_NOTCANCEL;
+
+ if (_IO_fwide (stderr, 0) > 0)
+ __fwprintf (stderr, L"%s", buf);
+ else
+ fputs (buf, stderr);
+
+ ((_IO_FILE *) stderr)->_flags2 = old_flags2;
+ _IO_funlockfile (stderr);
+
+ free (buf);
+ }
+#endif
+ }
+ d->__nextchar = (char *) "";
+ d->optind++;
+ d->optopt = 0;
+ return '?';
+ }
+ }
+
+ /* Look at and handle the next short option-character. */
+
+ {
+ char c = *d->__nextchar++;
+ char *temp = strchr (optstring, c);
+
+ /* Increment 'optind' when we start to process its last character. */
+ if (*d->__nextchar == '\0')
+ ++d->optind;
+
+ if (temp == NULL || c == ':')
+ {
+ if (print_errors)
+ {
+#if defined _LIBC && defined USE_IN_LIBIO
+ char *buf;
+ int n;
+#endif
+
+ if (d->__posixly_correct)
+ {
+ /* 1003.2 specifies the format of this message. */
+#if defined _LIBC && defined USE_IN_LIBIO
+ n = __asprintf (&buf, _("%s: illegal option -- %c\n"),
+ argv[0], c);
+#else
+ fprintf (stderr, _("%s: illegal option -- %c\n"), argv[0], c);
+#endif
+ }
+ else
+ {
+#if defined _LIBC && defined USE_IN_LIBIO
+ n = __asprintf (&buf, _("%s: invalid option -- %c\n"),
+ argv[0], c);
+#else
+ fprintf (stderr, _("%s: invalid option -- %c\n"), argv[0], c);
+#endif
+ }
+
+#if defined _LIBC && defined USE_IN_LIBIO
+ if (n >= 0)
+ {
+ _IO_flockfile (stderr);
+
+ int old_flags2 = ((_IO_FILE *) stderr)->_flags2;
+ ((_IO_FILE *) stderr)->_flags2 |= _IO_FLAGS2_NOTCANCEL;
+
+ if (_IO_fwide (stderr, 0) > 0)
+ __fwprintf (stderr, L"%s", buf);
+ else
+ fputs (buf, stderr);
+
+ ((_IO_FILE *) stderr)->_flags2 = old_flags2;
+ _IO_funlockfile (stderr);
+
+ free (buf);
+ }
+#endif
+ }
+ d->optopt = c;
+ return '?';
+ }
+ /* Convenience. Treat POSIX -W foo same as long option --foo */
+ if (temp[0] == 'W' && temp[1] == ';')
+ {
+ char *nameend;
+ const struct option *p;
+ const struct option *pfound = NULL;
+ int exact = 0;
+ int ambig = 0;
+ int indfound = 0;
+ int option_index;
+
+ /* This is an option that requires an argument. */
+ if (*d->__nextchar != '\0')
+ {
+ d->optarg = d->__nextchar;
+ /* If we end this ARGV-element by taking the rest as an arg,
+ we must advance to the next element now. */
+ d->optind++;
+ }
+ else if (d->optind == argc)
+ {
+ if (print_errors)
+ {
+ /* 1003.2 specifies the format of this message. */
+#if defined _LIBC && defined USE_IN_LIBIO
+ char *buf;
+
+ if (__asprintf (&buf,
+ _("%s: option requires an argument -- %c\n"),
+ argv[0], c) >= 0)
+ {
+ _IO_flockfile (stderr);
+
+ int old_flags2 = ((_IO_FILE *) stderr)->_flags2;
+ ((_IO_FILE *) stderr)->_flags2 |= _IO_FLAGS2_NOTCANCEL;
+
+ if (_IO_fwide (stderr, 0) > 0)
+ __fwprintf (stderr, L"%s", buf);
+ else
+ fputs (buf, stderr);
+
+ ((_IO_FILE *) stderr)->_flags2 = old_flags2;
+ _IO_funlockfile (stderr);
+
+ free (buf);
+ }
+#else
+ fprintf (stderr, _("%s: option requires an argument -- %c\n"),
+ argv[0], c);
+#endif
+ }
+ d->optopt = c;
+ if (optstring[0] == ':')
+ c = ':';
+ else
+ c = '?';
+ return c;
+ }
+ else
+ /* We already incremented 'd->optind' once;
+ increment it again when taking next ARGV-elt as argument. */
+ d->optarg = argv[d->optind++];
+
+ /* optarg is now the argument, see if it's in the
+ table of longopts. */
+
+ for (d->__nextchar = nameend = d->optarg; *nameend && *nameend != '=';
+ nameend++)
+ /* Do nothing. */ ;
+
+ /* Test all long options for either exact match
+ or abbreviated matches. */
+ for (p = longopts, option_index = 0; p->name; p++, option_index++)
+ if (!strncmp (p->name, d->__nextchar, nameend - d->__nextchar))
+ {
+ if ((unsigned int) (nameend - d->__nextchar) == strlen (p->name))
+ {
+ /* Exact match found. */
+ pfound = p;
+ indfound = option_index;
+ exact = 1;
+ break;
+ }
+ else if (pfound == NULL)
+ {
+ /* First nonexact match found. */
+ pfound = p;
+ indfound = option_index;
+ }
+ else
+ /* Second or later nonexact match found. */
+ ambig = 1;
+ }
+ if (ambig && !exact)
+ {
+ if (print_errors)
+ {
+#if defined _LIBC && defined USE_IN_LIBIO
+ char *buf;
+
+ if (__asprintf (&buf, _("%s: option '-W %s' is ambiguous\n"),
+ argv[0], argv[d->optind]) >= 0)
+ {
+ _IO_flockfile (stderr);
+
+ int old_flags2 = ((_IO_FILE *) stderr)->_flags2;
+ ((_IO_FILE *) stderr)->_flags2 |= _IO_FLAGS2_NOTCANCEL;
+
+ if (_IO_fwide (stderr, 0) > 0)
+ __fwprintf (stderr, L"%s", buf);
+ else
+ fputs (buf, stderr);
+
+ ((_IO_FILE *) stderr)->_flags2 = old_flags2;
+ _IO_funlockfile (stderr);
+
+ free (buf);
+ }
+#else
+ fprintf (stderr, _("%s: option '-W %s' is ambiguous\n"),
+ argv[0], argv[d->optind]);
+#endif
+ }
+ d->__nextchar += strlen (d->__nextchar);
+ d->optind++;
+ return '?';
+ }
+ if (pfound != NULL)
+ {
+ option_index = indfound;
+ if (*nameend)
+ {
+ /* Don't test has_arg with >, because some C compilers don't
+ allow it to be used on enums. */
+ if (pfound->has_arg)
+ d->optarg = nameend + 1;
+ else
+ {
+ if (print_errors)
+ {
+#if defined _LIBC && defined USE_IN_LIBIO
+ char *buf;
+
+ if (__asprintf (&buf, _("\
+%s: option '-W %s' doesn't allow an argument\n"),
+ argv[0], pfound->name) >= 0)
+ {
+ _IO_flockfile (stderr);
+
+ int old_flags2 = ((_IO_FILE *) stderr)->_flags2;
+ ((_IO_FILE *) stderr)->_flags2
+ |= _IO_FLAGS2_NOTCANCEL;
+
+ if (_IO_fwide (stderr, 0) > 0)
+ __fwprintf (stderr, L"%s", buf);
+ else
+ fputs (buf, stderr);
+
+ ((_IO_FILE *) stderr)->_flags2 = old_flags2;
+ _IO_funlockfile (stderr);
+
+ free (buf);
+ }
+#else
+ fprintf (stderr, _("\
+%s: option '-W %s' doesn't allow an argument\n"),
+ argv[0], pfound->name);
+#endif
+ }
+
+ d->__nextchar += strlen (d->__nextchar);
+ return '?';
+ }
+ }
+ else if (pfound->has_arg == 1)
+ {
+ if (d->optind < argc)
+ d->optarg = argv[d->optind++];
+ else
+ {
+ if (print_errors)
+ {
+#if defined _LIBC && defined USE_IN_LIBIO
+ char *buf;
+
+ if (__asprintf (&buf, _("\
+%s: option '%s' requires an argument\n"),
+ argv[0], argv[d->optind - 1]) >= 0)
+ {
+ _IO_flockfile (stderr);
+
+ int old_flags2 = ((_IO_FILE *) stderr)->_flags2;
+ ((_IO_FILE *) stderr)->_flags2
+ |= _IO_FLAGS2_NOTCANCEL;
+
+ if (_IO_fwide (stderr, 0) > 0)
+ __fwprintf (stderr, L"%s", buf);
+ else
+ fputs (buf, stderr);
+
+ ((_IO_FILE *) stderr)->_flags2 = old_flags2;
+ _IO_funlockfile (stderr);
+
+ free (buf);
+ }
+#else
+ fprintf (stderr,
+ _("%s: option '%s' requires an argument\n"),
+ argv[0], argv[d->optind - 1]);
+#endif
+ }
+ d->__nextchar += strlen (d->__nextchar);
+ return optstring[0] == ':' ? ':' : '?';
+ }
+ }
+ d->__nextchar += strlen (d->__nextchar);
+ if (longind != NULL)
+ *longind = option_index;
+ if (pfound->flag)
+ {
+ *(pfound->flag) = pfound->val;
+ return 0;
+ }
+ return pfound->val;
+ }
+ d->__nextchar = NULL;
+ return 'W'; /* Let the application handle it. */
+ }
+ if (temp[1] == ':')
+ {
+ if (temp[2] == ':')
+ {
+ /* This is an option that accepts an argument optionally. */
+ if (*d->__nextchar != '\0')
+ {
+ d->optarg = d->__nextchar;
+ d->optind++;
+ }
+ else
+ d->optarg = NULL;
+ d->__nextchar = NULL;
+ }
+ else
+ {
+ /* This is an option that requires an argument. */
+ if (*d->__nextchar != '\0')
+ {
+ d->optarg = d->__nextchar;
+ /* If we end this ARGV-element by taking the rest as an arg,
+ we must advance to the next element now. */
+ d->optind++;
+ }
+ else if (d->optind == argc)
+ {
+ if (print_errors)
+ {
+ /* 1003.2 specifies the format of this message. */
+#if defined _LIBC && defined USE_IN_LIBIO
+ char *buf;
+
+ if (__asprintf (&buf, _("\
+%s: option requires an argument -- %c\n"),
+ argv[0], c) >= 0)
+ {
+ _IO_flockfile (stderr);
+
+ int old_flags2 = ((_IO_FILE *) stderr)->_flags2;
+ ((_IO_FILE *) stderr)->_flags2 |= _IO_FLAGS2_NOTCANCEL;
+
+ if (_IO_fwide (stderr, 0) > 0)
+ __fwprintf (stderr, L"%s", buf);
+ else
+ fputs (buf, stderr);
+
+ ((_IO_FILE *) stderr)->_flags2 = old_flags2;
+ _IO_funlockfile (stderr);
+
+ free (buf);
+ }
+#else
+ fprintf (stderr,
+ _("%s: option requires an argument -- %c\n"),
+ argv[0], c);
+#endif
+ }
+ d->optopt = c;
+ if (optstring[0] == ':')
+ c = ':';
+ else
+ c = '?';
+ }
+ else
+ /* We already incremented 'optind' once;
+ increment it again when taking next ARGV-elt as argument. */
+ d->optarg = argv[d->optind++];
+ d->__nextchar = NULL;
+ }
+ }
+ return c;
+ }
+}
+
+int
+_getopt_internal (int argc, char **argv, const char *optstring,
+ const struct option *longopts, int *longind,
+ int long_only, int posixly_correct)
+{
+ int result;
+
+ getopt_data.optind = optind;
+ getopt_data.opterr = opterr;
+
+ result = _getopt_internal_r (argc, argv, optstring, longopts, longind,
+ long_only, posixly_correct, &getopt_data);
+
+ optind = getopt_data.optind;
+ optarg = getopt_data.optarg;
+ optopt = getopt_data.optopt;
+
+ return result;
+}
+
+/* glibc gets a LSB-compliant getopt.
+ Standalone applications get a POSIX-compliant getopt. */
+#if _LIBC
+enum { POSIXLY_CORRECT = 0 };
+#else
+enum { POSIXLY_CORRECT = 1 };
+#endif
+
+int
+getopt (int argc, char *const *argv, const char *optstring)
+{
+ return _getopt_internal (argc, (char **) argv, optstring, NULL, NULL, 0,
+ POSIXLY_CORRECT);
+}
+
+
+#ifdef TEST
+
+/* Compile with -DTEST to make an executable for use in testing
+ the above definition of 'getopt'. */
+
+int
+main (int argc, char **argv)
+{
+ int c;
+ int digit_optind = 0;
+
+ while (1)
+ {
+ int this_option_optind = optind ? optind : 1;
+
+ c = getopt (argc, argv, "abc:d:0123456789");
+ if (c == -1)
+ break;
+
+ switch (c)
+ {
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ if (digit_optind != 0 && digit_optind != this_option_optind)
+ printf ("digits occur in two different argv-elements.\n");
+ digit_optind = this_option_optind;
+ printf ("option %c\n", c);
+ break;
+
+ case 'a':
+ printf ("option a\n");
+ break;
+
+ case 'b':
+ printf ("option b\n");
+ break;
+
+ case 'c':
+ printf ("option c with value '%s'\n", optarg);
+ break;
+
+ case '?':
+ break;
+
+ default:
+ printf ("?? getopt returned character code 0%o ??\n", c);
+ }
+ }
+
+ if (optind < argc)
+ {
+ printf ("non-option ARGV-elements: ");
+ while (optind < argc)
+ printf ("%s ", argv[optind++]);
+ printf ("\n");
+ }
+
+ exit (0);
+}
+
+#endif /* TEST */
diff --git a/src/libs/libgroff/getopt1.c b/src/libs/libgroff/getopt1.c
new file mode 100644
index 0000000..374b3ce
--- /dev/null
+++ b/src/libs/libgroff/getopt1.c
@@ -0,0 +1,172 @@
+/* getopt_long and getopt_long_only entry points for GNU getopt.
+ Copyright (C) 1987-2020 Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
+
+ 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 3 of the License, 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, see <http://www.gnu.org/licenses/>. */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#ifdef _LIBC
+# include <getopt.h>
+#else
+# include "getopt.h"
+#endif
+#include "getopt_int.h"
+
+#include <stdio.h>
+
+/* This needs to come after some library #include
+ to get __GNU_LIBRARY__ defined. */
+#ifdef __GNU_LIBRARY__
+#include <stdlib.h>
+#endif
+
+#ifndef NULL
+#define NULL 0
+#endif
+
+int
+getopt_long (int argc, char *__getopt_argv_const *argv, const char *options,
+ const struct option *long_options, int *opt_index)
+{
+ return _getopt_internal (argc, (char **) argv, options, long_options,
+ opt_index, 0, 0);
+}
+
+int
+_getopt_long_r (int argc, char **argv, const char *options,
+ const struct option *long_options, int *opt_index,
+ struct _getopt_data *d)
+{
+ return _getopt_internal_r (argc, argv, options, long_options, opt_index,
+ 0, 0, d);
+}
+
+/* Like getopt_long, but '-' as well as '--' can indicate a long option.
+ If an option that starts with '-' (not '--') doesn't match a long option,
+ but does match a short option, it is parsed as a short option
+ instead. */
+
+int
+getopt_long_only (int argc, char *__getopt_argv_const *argv,
+ const char *options,
+ const struct option *long_options, int *opt_index)
+{
+ return _getopt_internal (argc, (char **) argv, options, long_options,
+ opt_index, 1, 0);
+}
+
+int
+_getopt_long_only_r (int argc, char **argv, const char *options,
+ const struct option *long_options, int *opt_index,
+ struct _getopt_data *d)
+{
+ return _getopt_internal_r (argc, argv, options, long_options, opt_index,
+ 1, 0, d);
+}
+
+
+#ifdef TEST
+
+#include <stdio.h>
+
+int
+main (int argc, char **argv)
+{
+ int c;
+ int digit_optind = 0;
+
+ while (1)
+ {
+ int this_option_optind = optind ? optind : 1;
+ int option_index = 0;
+ static struct option long_options[] =
+ {
+ {"add", 1, 0, 0},
+ {"append", 0, 0, 0},
+ {"delete", 1, 0, 0},
+ {"verbose", 0, 0, 0},
+ {"create", 0, 0, 0},
+ {"file", 1, 0, 0},
+ {0, 0, 0, 0}
+ };
+
+ c = getopt_long (argc, argv, "abc:d:0123456789",
+ long_options, &option_index);
+ if (c == -1)
+ break;
+
+ switch (c)
+ {
+ case 0:
+ printf ("option %s", long_options[option_index].name);
+ if (optarg)
+ printf (" with arg %s", optarg);
+ printf ("\n");
+ break;
+
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ if (digit_optind != 0 && digit_optind != this_option_optind)
+ printf ("digits occur in two different argv-elements.\n");
+ digit_optind = this_option_optind;
+ printf ("option %c\n", c);
+ break;
+
+ case 'a':
+ printf ("option a\n");
+ break;
+
+ case 'b':
+ printf ("option b\n");
+ break;
+
+ case 'c':
+ printf ("option c with value '%s'\n", optarg);
+ break;
+
+ case 'd':
+ printf ("option d with value '%s'\n", optarg);
+ break;
+
+ case '?':
+ break;
+
+ default:
+ printf ("?? getopt returned character code 0%o ??\n", c);
+ }
+ }
+
+ if (optind < argc)
+ {
+ printf ("non-option ARGV-elements: ");
+ while (optind < argc)
+ printf ("%s ", argv[optind++]);
+ printf ("\n");
+ }
+
+ exit (0);
+}
+
+#endif /* TEST */
diff --git a/src/libs/libgroff/glyphuni.cpp b/src/libs/libgroff/glyphuni.cpp
new file mode 100644
index 0000000..0d5a217
--- /dev/null
+++ b/src/libs/libgroff/glyphuni.cpp
@@ -0,0 +1,523 @@
+// -*- C++ -*-
+/* Copyright (C) 2002-2020 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 3 of the License, 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 this program. If not, see <http://www.gnu.org/licenses/>. */
+
+#include "lib.h"
+#include "stringclass.h"
+#include "ptable.h"
+
+#include "unicode.h"
+
+struct glyph_to_unicode {
+ char *value;
+};
+
+declare_ptable(glyph_to_unicode)
+implement_ptable(glyph_to_unicode)
+
+PTABLE(glyph_to_unicode) glyph_to_unicode_table;
+
+// The entries commented out in the table below can't be used in glyph
+// names.
+
+struct S {
+ const char *key;
+ const char *value;
+} glyph_to_unicode_list[] = {
+ { "!", "0021" },
+ { "\"", "0022" },
+ { "dq", "0022" },
+ { "#", "0023" },
+ { "sh", "0023" },
+ { "$", "0024" },
+ { "Do", "0024" },
+ { "%", "0025" },
+ { "&", "0026" },
+ { "aq", "0027" },
+ { "(", "0028" },
+ { ")", "0029" },
+ { "*", "002A" },
+ { "+", "002B" },
+ { "pl", "002B" },
+ { ",", "002C" },
+ { ".", "002E" },
+ { "/", "002F" },
+ { "sl", "002F" },
+ { "0", "0030" },
+ { "1", "0031" },
+ { "2", "0032" },
+ { "3", "0033" },
+ { "4", "0034" },
+ { "5", "0035" },
+ { "6", "0036" },
+ { "7", "0037" },
+ { "8", "0038" },
+ { "9", "0039" },
+ { ":", "003A" },
+ { ";", "003B" },
+ { "<", "003C" },
+ { "=", "003D" },
+ { "eq", "003D" },
+ { ">", "003E" },
+ { "?", "003F" },
+ { "@", "0040" },
+ { "at", "0040" },
+ { "A", "0041" },
+ { "B", "0042" },
+ { "C", "0043" },
+ { "D", "0044" },
+ { "E", "0045" },
+ { "F", "0046" },
+ { "G", "0047" },
+ { "H", "0048" },
+ { "I", "0049" },
+ { "J", "004A" },
+ { "K", "004B" },
+ { "L", "004C" },
+ { "M", "004D" },
+ { "N", "004E" },
+ { "O", "004F" },
+ { "P", "0050" },
+ { "Q", "0051" },
+ { "R", "0052" },
+ { "S", "0053" },
+ { "T", "0054" },
+ { "U", "0055" },
+ { "V", "0056" },
+ { "W", "0057" },
+ { "X", "0058" },
+ { "Y", "0059" },
+ { "Z", "005A" },
+//{ "[", "005B" },
+ { "lB", "005B" },
+//{ "\\", "005C" },
+ { "rs", "005C" },
+//{ "]", "005D" },
+ { "rB", "005D" },
+ { "a^", "005E" },
+ { "^", "005E" },
+ { "ha", "005E" },
+ { "_", "005F" },
+ { "ru", "005F" },
+ { "ul", "005F" },
+ { "ga", "0060" },
+ { "a", "0061" },
+ { "b", "0062" },
+ { "c", "0063" },
+ { "d", "0064" },
+ { "e", "0065" },
+ { "f", "0066" },
+ { "ff", "0066_0066" },
+ { "Fi", "0066_0066_0069" },
+ { "Fl", "0066_0066_006C" },
+ { "fi", "0066_0069" },
+ { "fl", "0066_006C" },
+ { "g", "0067" },
+ { "h", "0068" },
+ { "i", "0069" },
+ { "j", "006A" },
+ { "k", "006B" },
+ { "l", "006C" },
+ { "m", "006D" },
+ { "n", "006E" },
+ { "o", "006F" },
+ { "p", "0070" },
+ { "q", "0071" },
+ { "r", "0072" },
+ { "s", "0073" },
+ { "t", "0074" },
+ { "u", "0075" },
+ { "v", "0076" },
+ { "w", "0077" },
+ { "x", "0078" },
+ { "y", "0079" },
+ { "z", "007A" },
+ { "lC", "007B" },
+ { "{", "007B" },
+ { "ba", "007C" },
+ { "or", "007C" },
+ { "|", "007C" },
+ { "rC", "007D" },
+ { "}", "007D" },
+ { "a~", "007E" },
+ { "~", "007E" },
+ { "ti", "007E" },
+ { "r!", "00A1" },
+ { "ct", "00A2" },
+ { "Po", "00A3" },
+ { "Cs", "00A4" },
+ { "Ye", "00A5" },
+ { "bb", "00A6" },
+ { "sc", "00A7" },
+ { "ad", "00A8" },
+ { "co", "00A9" },
+ { "Of", "00AA" },
+ { "Fo", "00AB" },
+ { "no", "00AC" },
+ { "tno", "00AC" },
+ // The soft hyphen U+00AD is meaningful only in the input file,
+ // not in the output.
+ { "rg", "00AE" },
+ { "a-", "00AF" },
+ { "de", "00B0" },
+ { "+-", "00B1" },
+ { "t+-", "00B1" },
+ { "S2", "00B2" },
+ { "S3", "00B3" },
+ { "aa", "00B4" },
+ { "mc", "00B5" },
+ { "ps", "00B6" },
+ { "pc", "00B7" },
+ { "ac", "00B8" },
+ { "S1", "00B9" },
+ { "Om", "00BA" },
+ { "Fc", "00BB" },
+ { "14", "00BC" },
+ { "12", "00BD" },
+ { "34", "00BE" },
+ { "r?", "00BF" },
+ { "`A", "00C0" },
+ { "'A", "00C1" },
+ { "^A", "00C2" },
+ { "~A", "00C3" },
+ { ":A", "00C4" },
+ { "oA", "00C5" },
+ { "AE", "00C6" },
+ { ",C", "00C7" },
+ { "`E", "00C8" },
+ { "'E", "00C9" },
+ { "^E", "00CA" },
+ { ":E", "00CB" },
+ { "`I", "00CC" },
+ { "'I", "00CD" },
+ { "^I", "00CE" },
+ { ":I", "00CF" },
+ { "-D", "00D0" },
+ { "~N", "00D1" },
+ { "`O", "00D2" },
+ { "'O", "00D3" },
+ { "^O", "00D4" },
+ { "~O", "00D5" },
+ { ":O", "00D6" },
+ { "mu", "00D7" },
+ { "tmu", "00D7" },
+ { "/O", "00D8" },
+ { "`U", "00D9" },
+ { "'U", "00DA" },
+ { "^U", "00DB" },
+ { ":U", "00DC" },
+ { "'Y", "00DD" },
+ { "TP", "00DE" },
+ { "ss", "00DF" },
+ { "`a", "00E0" },
+ { "'a", "00E1" },
+ { "^a", "00E2" },
+ { "~a", "00E3" },
+ { ":a", "00E4" },
+ { "oa", "00E5" },
+ { "ae", "00E6" },
+ { ",c", "00E7" },
+ { "`e", "00E8" },
+ { "'e", "00E9" },
+ { "^e", "00EA" },
+ { ":e", "00EB" },
+ { "`i", "00EC" },
+ { "'i", "00ED" },
+ { "^i", "00EE" },
+ { ":i", "00EF" },
+ { "Sd", "00F0" },
+ { "~n", "00F1" },
+ { "`o", "00F2" },
+ { "'o", "00F3" },
+ { "^o", "00F4" },
+ { "~o", "00F5" },
+ { ":o", "00F6" },
+ { "di", "00F7" },
+ { "tdi", "00F7" },
+ { "/o", "00F8" },
+ { "`u", "00F9" },
+ { "'u", "00FA" },
+ { "^u", "00FB" },
+ { ":u", "00FC" },
+ { "'y", "00FD" },
+ { "Tp", "00FE" },
+ { ":y", "00FF" },
+ { "'C", "0106" },
+ { "'c", "0107" },
+ { ".i", "0131" },
+ { "IJ", "0132" },
+ { "ij", "0133" },
+ { "/L", "0141" },
+ { "/l", "0142" },
+ { "OE", "0152" },
+ { "oe", "0153" },
+ { "vS", "0160" },
+ { "vs", "0161" },
+ { ":Y", "0178" },
+ { "vZ", "017D" },
+ { "vz", "017E" },
+ { "Fn", "0192" },
+ { ".j", "0237" },
+ { "ah", "02C7" },
+ { "ab", "02D8" },
+ { "a.", "02D9" },
+ { "ao", "02DA" },
+ { "ho", "02DB" },
+ { "a\"", "02DD" },
+ { "*A", "0391" },
+ { "*B", "0392" },
+ { "*G", "0393" },
+ { "*D", "0394" },
+ { "*E", "0395" },
+ { "*Z", "0396" },
+ { "*Y", "0397" },
+ { "*H", "0398" },
+ { "*I", "0399" },
+ { "*K", "039A" },
+ { "*L", "039B" },
+ { "*M", "039C" },
+ { "*N", "039D" },
+ { "*C", "039E" },
+ { "*O", "039F" },
+ { "*P", "03A0" },
+ { "*R", "03A1" },
+ { "*S", "03A3" },
+ { "*T", "03A4" },
+ { "*U", "03A5" },
+ { "*F", "03A6" },
+ { "*X", "03A7" },
+ { "*Q", "03A8" },
+ { "*W", "03A9" },
+ { "*a", "03B1" },
+ { "*b", "03B2" },
+ { "*g", "03B3" },
+ { "*d", "03B4" },
+ { "*e", "03B5" },
+ { "*z", "03B6" },
+ { "*y", "03B7" },
+ { "*h", "03B8" },
+ { "*i", "03B9" },
+ { "*k", "03BA" },
+ { "*l", "03BB" },
+ { "*m", "03BC" },
+ { "*n", "03BD" },
+ { "*c", "03BE" },
+ { "*o", "03BF" },
+ { "*p", "03C0" },
+ { "*r", "03C1" },
+ { "ts", "03C2" },
+ { "*s", "03C3" },
+ { "*t", "03C4" },
+ { "*u", "03C5" },
+ // the curly phi variant
+ { "+f", "03C6" },
+ { "*x", "03C7" },
+ { "*q", "03C8" },
+ { "*w", "03C9" },
+ { "+h", "03D1" },
+ // the stroked phi variant
+ { "*f", "03D5" },
+ { "+p", "03D6" },
+ { "+e", "03F5" },
+ // '-' and 'hy' denote a HYPHEN, usually a glyph with a smaller width than
+ // the MINUS sign. Users who are viewing broken man pages that assume
+ // that '-' denotes a U+002D character can either fix the broken man pages
+ // or apply the workaround described in the PROBLEMS file.
+ { "-", "2010" },
+ { "hy", "2010" },
+ { "en", "2013" },
+ { "em", "2014" },
+ { "`", "2018" },
+ { "oq", "2018" },
+ { "'", "2019" },
+ { "cq", "2019" },
+ { "bq", "201A" },
+ { "lq", "201C" },
+ { "rq", "201D" },
+ { "Bq", "201E" },
+ { "dg", "2020" },
+ { "dd", "2021" },
+ { "bu", "2022" },
+ { "%0", "2030" },
+ { "fm", "2032" },
+ { "sd", "2033" },
+ { "fo", "2039" },
+ { "fc", "203A" },
+ { "rn", "203E" },
+ { "f/", "2044" },
+ { "eu", "20AC" },
+ { "Eu", "20AC" },
+ { "-h", "210F" },
+ { "hbar", "210F" },
+ { "Im", "2111" },
+ { "wp", "2118" },
+ { "Re", "211C" },
+ { "tm", "2122" },
+ { "Ah", "2135" },
+ { "18", "215B" },
+ { "38", "215C" },
+ { "58", "215D" },
+ { "78", "215E" },
+ { "<-", "2190" },
+ { "ua", "2191" },
+ { "->", "2192" },
+ { "da", "2193" },
+ { "<>", "2194" },
+ { "va", "2195" },
+ { "CR", "21B5" },
+ { "lA", "21D0" },
+ { "uA", "21D1" },
+ { "rA", "21D2" },
+ { "dA", "21D3" },
+ { "hA", "21D4" },
+ { "vA", "21D5" },
+ { "fa", "2200" },
+ { "pd", "2202" },
+ { "te", "2203" },
+ { "es", "2205" },
+ { "gr", "2207" },
+ { "mo", "2208" },
+ { "nm", "2209" },
+ { "st", "220B" },
+ { "product", "220F" },
+ { "coproduct", "2210" },
+ { "sum", "2211" },
+ // 'mi' and '\-' represent a MINUS sign. But it is used in many man pages
+ // to denote the U+002D character that introduces a command-line option.
+ // For devices that support copy&paste, such as devhtml and devutf8, the
+ // user can apply the workaround described in the PROBLEMS file.
+ { "\\-", "2212" },
+ { "mi", "2212" },
+ { "-+", "2213" },
+ { "**", "2217" },
+ { "sqrt", "221A" },
+ { "sr", "221A" },
+ { "pt", "221D" },
+ { "if", "221E" },
+ { "/_", "2220" },
+ { "AN", "2227" },
+ { "OR", "2228" },
+ { "ca", "2229" },
+ { "cu", "222A" },
+ { "is", "222B" },
+ { "integral", "222B" },
+ { "tf", "2234" },
+ { "3d", "2234" },
+ { "ap", "223C" },
+ { "|=", "2243" },
+ { "=~", "2245" },
+ { "~~", "2248" },
+ { "~=", "2248" },
+ { "!=", "2260" },
+ { "==", "2261" },
+ { "ne", "2262" },
+ { "<=", "2264" },
+ { ">=", "2265" },
+ { "<<", "226A" },
+ { ">>", "226B" },
+ { "sb", "2282" },
+ { "sp", "2283" },
+ { "nb", "2284" },
+ { "nc", "2285" },
+ { "ib", "2286" },
+ { "ip", "2287" },
+ { "c+", "2295" },
+ { "c*", "2297" },
+ { "pp", "22A5" },
+ { "md", "22C5" },
+ { "lc", "2308" },
+ { "rc", "2309" },
+ { "lf", "230A" },
+ { "rf", "230B" },
+ { "parenlefttp", "239B" },
+ { "parenleftex", "239C" },
+ { "parenleftbt", "239D" },
+ { "parenrighttp", "239E" },
+ { "parenrightex", "239F" },
+ { "parenrightbt", "23A0" },
+ { "bracketlefttp", "23A1" },
+ { "bracketleftex", "23A2" },
+ { "bracketleftbt", "23A3" },
+ { "bracketrighttp", "23A4" },
+ { "bracketrightex", "23A5" },
+ { "bracketrightbt", "23A6" },
+ { "lt", "23A7" },
+ { "bracelefttp", "23A7" },
+ { "lk", "23A8" },
+ { "braceleftmid", "23A8" },
+ { "lb", "23A9" },
+ { "braceleftbt", "23A9" },
+ { "bv", "23AA" },
+ { "braceex", "23AA" },
+ { "braceleftex", "23AA" },
+ { "bracerightex", "23AA" },
+ { "rt", "23AB" },
+ { "bracerighttp", "23AB" },
+ { "rk", "23AC" },
+ { "bracerightmid", "23AC" },
+ { "rb", "23AD" },
+ { "bracerightbt", "23AD" },
+ { "an", "23AF" },
+ { "br", "2502" },
+ { "sq", "25A1" },
+ { "lz", "25CA" },
+ { "ci", "25CB" },
+ { "lh", "261C" },
+ { "rh", "261E" },
+ { "SP", "2660" },
+ { "CL", "2663" },
+ { "HE", "2665" },
+ { "DI", "2666" },
+ { "OK", "2713" },
+ // The 'left angle bracket' and 'right angle bracket' could be mapped to
+ // either U+2329,U+232A or U+3008,U+3009 or U+27E8,U+27E9. But the first
+ // and second possibility are double-width characters (see Unicode's
+ // 'DerivedEastAsianWidth.txt' file) and are therefore not suitable for
+ // general use, whereas the third possibility is single-width.
+ //
+ // The devhtml device overrides this mapping, because
+ //
+ // http://www.w3.org/TR/html401/sgml/entities.html
+ //
+ // says that in HTML, '&lang;' and '&rang;' are U+2329,U+232A,
+ // respectively.
+ { "la", "27E8" },
+ { "ra", "27E9" },
+};
+
+// global constructor
+static struct glyph_to_unicode_init {
+ glyph_to_unicode_init();
+} _glyph_to_unicode_init;
+
+glyph_to_unicode_init::glyph_to_unicode_init()
+{
+ for (unsigned int i = 0;
+ i < sizeof(glyph_to_unicode_list)/sizeof(glyph_to_unicode_list[0]);
+ i++) {
+ glyph_to_unicode *gtu = new glyph_to_unicode[1];
+ gtu->value = (char *)glyph_to_unicode_list[i].value;
+ glyph_to_unicode_table.define(glyph_to_unicode_list[i].key, gtu);
+ }
+}
+
+const char *glyph_name_to_unicode(const char *s)
+{
+ glyph_to_unicode *result = glyph_to_unicode_table.lookup(s);
+ return result ? result->value : 0;
+}
diff --git a/src/libs/libgroff/htmlhint.cpp b/src/libs/libgroff/htmlhint.cpp
new file mode 100644
index 0000000..8ebb84e
--- /dev/null
+++ b/src/libs/libgroff/htmlhint.cpp
@@ -0,0 +1,60 @@
+/* Copyright (C) 2000-2020 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 3 of the License, 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 this program. If not, see <http://www.gnu.org/licenses/>. */
+
+#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 (or -Txhtml) and once
+ * with -Tps. 'troff -Thtml' (and 'troff -Txhtml') 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/src/libs/libgroff/hypot.cpp b/src/libs/libgroff/hypot.cpp
new file mode 100644
index 0000000..fa0977d
--- /dev/null
+++ b/src/libs/libgroff/hypot.cpp
@@ -0,0 +1,34 @@
+/* Copyright (C) 2005-2020 Free Software Foundation, Inc.
+This file is part of the GNU C Library.
+
+The GNU C Library is free software; you can redistribute it and/or
+modify it under the terms of the GNU Library General Public License as
+published by the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+The GNU C Library 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
+Library General Public License for more details.
+
+You should have received a copy of the GNU Library General Public License
+along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <math.h>
+
+double groff_hypot(double x, double y)
+{
+ double result = hypot(x, y);
+
+#ifdef __INTERIX
+ /* hypot() on Interix is broken */
+ if (isnan(result) && !isnan(x) && !isnan(y))
+ return 0.0;
+#endif
+
+ return result;
+}
diff --git a/src/libs/libgroff/iftoa.c b/src/libs/libgroff/iftoa.c
new file mode 100644
index 0000000..87aec58
--- /dev/null
+++ b/src/libs/libgroff/iftoa.c
@@ -0,0 +1,76 @@
+/* Copyright (C) 1989-2020 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 3 of the License, 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 this program. If not, see <http://www.gnu.org/licenses/>. */
+
+#include "lib.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+const char *if_to_a(int i, int decimal_point)
+{
+ static char buf[INT_DIGITS + 3]; // INT_DIGITS + '-', '.', '\0'
+ char *p = buf + INT_DIGITS + 2;
+ int point = 0;
+ buf[INT_DIGITS + 2] = '\0';
+ /* assert(decimal_point <= INT_DIGITS); */
+ if (i >= 0) {
+ do {
+ *--p = '0' + (i % 10);
+ i /= 10;
+ if (++point == decimal_point)
+ *--p = '.';
+ } while (i != 0 || point < decimal_point);
+ }
+ else { /* i < 0 */
+ do {
+ *--p = '0' - (i % 10);
+ i /= 10;
+ if (++point == decimal_point)
+ *--p = '.';
+ } while (i != 0 || point < decimal_point);
+ *--p = '-';
+ }
+ if (decimal_point > 0) {
+ char *q;
+ /* there must be a dot, so this will terminate */
+ for (q = buf + INT_DIGITS + 2; q[-1] == '0'; --q)
+ ;
+ if (q[-1] == '.') {
+ if (q - 1 == p) {
+ q[-1] = '0';
+ q[0] = '\0';
+ }
+ else
+ q[-1] = '\0';
+ }
+ else
+ *q = '\0';
+ }
+ return p;
+}
+
+#ifdef __cplusplus
+}
+#endif
+
+// Local Variables:
+// fill-column: 72
+// mode: C++
+// End:
+// vim: set cindent noexpandtab shiftwidth=2 textwidth=72:
diff --git a/src/libs/libgroff/invalid.cpp b/src/libs/libgroff/invalid.cpp
new file mode 100644
index 0000000..59e4619
--- /dev/null
+++ b/src/libs/libgroff/invalid.cpp
@@ -0,0 +1,59 @@
+/* Copyright (C) 2000-2020 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 3 of the License, 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 this program. If not, see <http://www.gnu.org/licenses/>. */
+
+#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/src/libs/libgroff/itoa.c b/src/libs/libgroff/itoa.c
new file mode 100644
index 0000000..a573b2c
--- /dev/null
+++ b/src/libs/libgroff/itoa.c
@@ -0,0 +1,67 @@
+/* Copyright (C) 1989-2020 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 3 of the License, 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 this program. If not, see <http://www.gnu.org/licenses/>. */
+
+#include "lib.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+const char *i_to_a(int i)
+{
+ /* Room for INT_DIGITS digits, - and '\0' */
+ static char buf[INT_DIGITS + 2];
+ char *p = buf + INT_DIGITS + 1; /* points to terminating '\0' */
+ if (i >= 0) {
+ do {
+ *--p = '0' + (i % 10);
+ i /= 10;
+ } while (i != 0);
+ return p;
+ }
+ else { /* i < 0 */
+ do {
+ *--p = '0' - (i % 10);
+ i /= 10;
+ } while (i != 0);
+ *--p = '-';
+ }
+ return p;
+}
+
+const char *ui_to_a(unsigned int i)
+{
+ /* Room for UINT_DIGITS digits and '\0' */
+ static char buf[UINT_DIGITS + 1];
+ char *p = buf + UINT_DIGITS; /* points to terminating '\0' */
+ do {
+ *--p = '0' + (i % 10);
+ i /= 10;
+ } while (i != 0);
+ return p;
+}
+
+#ifdef __cplusplus
+}
+#endif
+
+// Local Variables:
+// fill-column: 72
+// mode: C++
+// End:
+// vim: set cindent noexpandtab shiftwidth=2 textwidth=72:
diff --git a/src/libs/libgroff/lf.cpp b/src/libs/libgroff/lf.cpp
new file mode 100644
index 0000000..9c255fb
--- /dev/null
+++ b/src/libs/libgroff/lf.cpp
@@ -0,0 +1,78 @@
+// -*- C++ -*-
+/* Copyright (C) 1989-2020 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 3 of the License, 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 this program. If not, see <http://www.gnu.org/licenses/>. */
+
+#include "lib.h"
+#include "cset.h"
+#include "stringclass.h"
+#include "lf.h"
+
+#include <ctype.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;
+}
+
+#if defined(__MSDOS__) || (defined(_WIN32) && !defined(__CYGWIN__))
+void normalize_for_lf (string &fn)
+{
+ int fnlen = fn.length();
+ for (int i = 0; i < fnlen; i++) {
+ if (fn[i] == '\\')
+ fn[i] = '/';
+ }
+}
+#else
+void normalize_for_lf (string &)
+{
+}
+#endif
diff --git a/src/libs/libgroff/libgroff.am b/src/libs/libgroff/libgroff.am
new file mode 100644
index 0000000..be6d9e2
--- /dev/null
+++ b/src/libs/libgroff/libgroff.am
@@ -0,0 +1,182 @@
+# Copyright (C) 2014-2020 Free Software Foundation, Inc.
+#
+# 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 3 of the License, 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 this program. If not, see <http://www.gnu.org/licenses/>.
+
+libgroff_srcdir = $(top_srcdir)/src/libs/libgroff
+noinst_LIBRARIES += libgroff.a
+libgroff_a_CPPFLAGS = \
+ $(AM_CPPFLAGS) \
+ -D__GETOPT_PREFIX=groff_ \
+ -DENABLE_RELOCATABLE=1 \
+ -DLIBDIR=\"$(libdir)\"
+
+# Build from OBJS
+libgroff_a_SOURCES = \
+ src/libs/libgroff/assert.cpp \
+ src/libs/libgroff/change_lf.cpp \
+ src/libs/libgroff/cmap.cpp \
+ src/libs/libgroff/color.cpp \
+ src/libs/libgroff/cset.cpp\
+ src/libs/libgroff/curtime.cpp \
+ src/libs/libgroff/device.cpp \
+ src/libs/libgroff/errarg.cpp \
+ src/libs/libgroff/error.cpp \
+ src/libs/libgroff/fatal.cpp \
+ src/libs/libgroff/filename.cpp \
+ src/libs/libgroff/font.cpp \
+ src/libs/libgroff/fontfile.cpp \
+ src/libs/libgroff/geometry.cpp \
+ src/libs/libgroff/getopt.c \
+ src/libs/libgroff/getopt1.c \
+ src/libs/libgroff/glyphuni.cpp \
+ src/libs/libgroff/htmlhint.cpp \
+ src/libs/libgroff/hypot.cpp \
+ src/libs/libgroff/iftoa.c \
+ src/libs/libgroff/invalid.cpp \
+ src/libs/libgroff/itoa.c \
+ src/libs/libgroff/lf.cpp \
+ src/libs/libgroff/lineno.cpp \
+ src/libs/libgroff/localcharset.c \
+ src/libs/libgroff/macropath.cpp \
+ src/libs/libgroff/matherr.c \
+ src/libs/libgroff/maxfilename.cpp \
+ src/libs/libgroff/maxpathname.cpp \
+ src/libs/libgroff/mksdir.cpp \
+ src/libs/libgroff/nametoindex.cpp \
+ src/libs/libgroff/paper.cpp \
+ src/libs/libgroff/prime.cpp \
+ src/libs/libgroff/progname.c \
+ src/libs/libgroff/ptable.cpp \
+ src/libs/libgroff/quotearg.c \
+ src/libs/libgroff/relocate.cpp \
+ src/libs/libgroff/searchpath.cpp \
+ src/libs/libgroff/spawnvp.c \
+ src/libs/libgroff/string.cpp \
+ src/libs/libgroff/strsave.cpp \
+ src/libs/libgroff/symbol.cpp \
+ src/libs/libgroff/tmpfile.cpp \
+ src/libs/libgroff/tmpname.cpp \
+ src/libs/libgroff/unicode.cpp \
+ src/libs/libgroff/uniglyph.cpp \
+ src/libs/libgroff/uniuni.cpp \
+ src/libs/libgroff/relocatable.h
+if USE_GROFF_ALLOCATOR
+libgroff_a_SOURCES += \
+ src/libs/libgroff/new.cpp
+endif
+nodist_libgroff_a_SOURCES = src/libs/libgroff/version.cpp
+
+# TODO: these .c files could be removed (use gnulib instead).
+EXTRA_DIST += \
+ src/libs/libgroff/mkstemp.cpp \
+ src/libs/libgroff/fmod.c \
+ src/libs/libgroff/getcwd.c \
+ src/libs/libgroff/putenv.c \
+ src/libs/libgroff/strcasecmp.c \
+ src/libs/libgroff/strerror.c \
+ src/libs/libgroff/strncasecmp.c \
+ src/libs/libgroff/strtol.c \
+ src/libs/libgroff/config.charset \
+ src/libs/libgroff/ref-add.sin \
+ src/libs/libgroff/ref-del.sin \
+ src/libs/libgroff/make-uniuni
+
+CLEANFILES += \
+ src/libs/libgroff/version.cpp \
+ charset.alias \
+ ref-add.sed \
+ ref-del.sed
+
+# .o files have a 'libgroff_a-' prefix because we set
+# libgroff_a_CPPFLAGS.
+src/libs/libgroff/libgroff_a-device.$(OBJEXT): defs.h
+src/libs/libgroff/libgroff_a-fontfile.$(OBJEXT): defs.h
+src/libs/libgroff/libgroff_a-macropath.$(OBJEXT): defs.h
+src/libs/libgroff/libgroff_a-relocate.$(OBJEXT): defs.h
+
+src/libs/libgroff/version.cpp: $(top_srcdir)/.version
+ $(AM_V_at)printf 'const char *version_string = "%s.%s";\n' \
+ $(MAJOR_VERSION) $(MINOR_VERSION) > $@.tmp
+ $(AM_V_at)printf 'const char *revision_string = "%s";\n' \
+ $(REVISION) >> $@.tmp
+ $(AM_V_at)printf \
+ 'extern "C" {\nconst char *Version_string = "%s";\n}\n' \
+ $(VERSION) >> $@.tmp
+ $(AM_V_GEN)mv $@.tmp $@
+
+# Data for localcharset.c. Taken from libiconv/libcharset.
+
+LIBGROFF_PACKAGE = groff
+
+all: charset.alias ref-add.sed ref-del.sed
+
+charset.alias: $(libgroff_srcdir)/config.charset
+ $(AM_V_GEN)$(SHELL) $(libgroff_srcdir)/config.charset \
+ '$(HOST)' > t-$@ \
+ && mv t-$@ $@
+
+ref-add.sed: $(libgroff_srcdir)/ref-add.sin
+ $(AM_V_GEN)sed -e '/^#/d' \
+ -e 's/@''PACKAGE''@/$(LIBGROFF_PACKAGE)/g' \
+ $(libgroff_srcdir)/ref-add.sin > t-$@ \
+ && mv t-$@ $@
+
+ref-del.sed: $(libgroff_srcdir)/ref-del.sin
+ $(AM_V_GEN)sed -e '/^#/d' \
+ -e 's/@''PACKAGE''@/$(LIBGROFF_PACKAGE)/g' \
+ $(libgroff_srcdir)/ref-del.sin > t-$@ \
+ && mv t-$@ $@
+
+install-data-local: install_charset_data
+install_charset_data:
+ -test $(GLIBC21) != no || $(mkinstalldirs) $(DESTDIR)$(libdir)
+ if test -f $(DESTDIR)$(libdir)/charset.alias; then \
+ sed -f ref-add.sed $(DESTDIR)$(libdir)/charset.alias \
+ > $(DESTDIR)$(libdir)/t-charset.alias; \
+ $(INSTALL_DATA) $(DESTDIR)$(libdir)/t-charset.alias \
+ $(DESTDIR)$(libdir)/charset.alias; \
+ rm -f $(DESTDIR)$(libdir)/t-charset.alias; \
+ else \
+ if test $(GLIBC21) = no; then \
+ sed -f ref-add.sed charset.alias \
+ > $(DESTDIR)$(libdir)/t-charset.alias; \
+ $(INSTALL_DATA) $(DESTDIR)$(libdir)/t-charset.alias \
+ $(DESTDIR)$(libdir)/charset.alias; \
+ rm -f $(DESTDIR)$(libdir)/t-charset.alias; \
+ fi; \
+ fi
+
+uninstall-local: uninstall_charset_data
+uninstall_charset_data:
+ -if test -f $(DESTDIR)$(libdir)/charset.alias; then \
+ sed -f ref-del.sed $(DESTDIR)$(libdir)/charset.alias \
+ > $(DESTDIR)$(libdir)/t-charset.alias; \
+ if grep '^# Packages using this file: $$' \
+ $(DESTDIR)$(libdir)/t-charset.alias > /dev/null; then \
+ rm -f $(DESTDIR)$(libdir)/charset.alias; \
+ else \
+ $(INSTALL_DATA) $(DESTDIR)$(libdir)/t-charset.alias \
+ $(DESTDIR)$(libdir)/charset.alias; \
+ fi; \
+ rm -f $(DESTDIR)$(libdir)/t-charset.alias; \
+ fi
+
+
+# Local Variables:
+# mode: makefile-automake
+# fill-column: 72
+# End:
+# vim: set autoindent filetype=automake textwidth=72:
diff --git a/src/libs/libgroff/lineno.cpp b/src/libs/libgroff/lineno.cpp
new file mode 100644
index 0000000..6e356c7
--- /dev/null
+++ b/src/libs/libgroff/lineno.cpp
@@ -0,0 +1,20 @@
+/* Copyright (C) 2014-2020 Free Software Foundation, Inc.
+
+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 of the License, 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.
+
+The GNU General Public License version 2 (GPL2) is available in the
+internet at <http://www.gnu.org/licenses/gpl-2.0.txt>. */
+
+// This global stores the line number of the input file being
+// processed by troff, an output driver, or other program.
+int current_lineno = 0;
diff --git a/src/libs/libgroff/localcharset.c b/src/libs/libgroff/localcharset.c
new file mode 100644
index 0000000..1bc07a4
--- /dev/null
+++ b/src/libs/libgroff/localcharset.c
@@ -0,0 +1,579 @@
+/* Determine a canonical name for the current locale's character encoding.
+
+ Copyright (C) 2000-2020 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, see <http://www.gnu.org/licenses/>. */
+
+/* Written by Bruno Haible <bruno@clisp.org>. */
+
+#include <config.h>
+
+/* Specification. */
+#include "localcharset.h"
+
+#include <fcntl.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+
+#if defined __APPLE__ && defined __MACH__ && HAVE_LANGINFO_CODESET
+# define DARWIN7 /* Darwin 7 or newer, i.e. Mac OS X 10.3 or newer */
+#endif
+
+#if defined _WIN32 || defined __WIN32__
+# define WINDOWS_NATIVE
+# include <locale.h>
+#endif
+
+#if defined __EMX__
+/* Assume EMX program runs on OS/2, even if compiled under DOS. */
+# ifndef OS2
+# define OS2
+# endif
+#endif
+
+#if !defined WINDOWS_NATIVE
+# include <unistd.h>
+# if HAVE_LANGINFO_CODESET
+# include <langinfo.h>
+# else
+# if 0 /* see comment below */
+# include <locale.h>
+# endif
+# endif
+# ifdef __CYGWIN__
+# define WIN32_LEAN_AND_MEAN
+# include <windows.h>
+# endif
+#elif defined WINDOWS_NATIVE
+# define WIN32_LEAN_AND_MEAN
+# include <windows.h>
+#endif
+#if defined OS2
+# define INCL_DOS
+# include <os2.h>
+#endif
+
+/* For MB_CUR_MAX_L */
+#if defined DARWIN7
+# include <xlocale.h>
+#endif
+
+#if ENABLE_RELOCATABLE
+# include "relocatable.h"
+#else
+# define relocate(pathname) (pathname)
+#endif
+
+/* Get LIBDIR. */
+#ifndef LIBDIR
+# include "configmake.h"
+#endif
+
+/* Define O_NOFOLLOW to 0 on platforms where it does not exist. */
+#ifndef O_NOFOLLOW
+# define O_NOFOLLOW 0
+#endif
+
+#if defined _WIN32 || defined __WIN32__ || defined __CYGWIN__ || defined __EMX__ || defined __DJGPP__
+ /* Native Windows, Cygwin, OS/2, DOS */
+# define ISSLASH(C) ((C) == '/' || (C) == '\\')
+#endif
+
+#ifndef DIRECTORY_SEPARATOR
+# define DIRECTORY_SEPARATOR '/'
+#endif
+
+#ifndef ISSLASH
+# define ISSLASH(C) ((C) == DIRECTORY_SEPARATOR)
+#endif
+
+#if HAVE_DECL_GETC_UNLOCKED
+# undef getc
+# define getc getc_unlocked
+#endif
+
+/* The following static variable is declared 'volatile' to avoid a
+ possible multithread problem in the function get_charset_aliases. If we
+ are running in a threaded environment, and if two threads initialize
+ 'charset_aliases' simultaneously, both will produce the same value,
+ and everything will be ok if the two assignments to 'charset_aliases'
+ are atomic. But I don't know what will happen if the two assignments mix. */
+#if __STDC__ != 1
+# define volatile /* empty */
+#endif
+/* Pointer to the contents of the charset.alias file, if it has already been
+ read, else NULL. Its format is:
+ ALIAS_1 '\0' CANONICAL_1 '\0' ... ALIAS_n '\0' CANONICAL_n '\0' '\0' */
+static const char * volatile charset_aliases;
+
+/* Return a pointer to the contents of the charset.alias file. */
+static const char *
+get_charset_aliases (void)
+{
+ const char *cp;
+
+ cp = charset_aliases;
+ if (cp == NULL)
+ {
+#if !(defined DARWIN7 || defined VMS || defined WINDOWS_NATIVE || defined __CYGWIN__)
+ const char *dir;
+ const char *base = "charset.alias";
+ char *file_name;
+
+ /* Make it possible to override the charset.alias location. This is
+ necessary for running the testsuite before "make install". */
+ dir = getenv ("CHARSETALIASDIR");
+ if (dir == NULL || dir[0] == '\0')
+ dir = relocate (LIBDIR);
+
+ /* Concatenate dir and base into freshly allocated file_name. */
+ {
+ size_t dir_len = strlen (dir);
+ size_t base_len = strlen (base);
+ int add_slash = (dir_len > 0 && !ISSLASH (dir[dir_len - 1]));
+ file_name = (char *) malloc (dir_len + add_slash + base_len + 1);
+ if (file_name != NULL)
+ {
+ memcpy (file_name, dir, dir_len);
+ if (add_slash)
+ file_name[dir_len] = DIRECTORY_SEPARATOR;
+ memcpy (file_name + dir_len + add_slash, base, base_len + 1);
+ }
+ }
+
+ if (file_name == NULL)
+ /* Out of memory. Treat the file as empty. */
+ cp = "";
+ else
+ {
+ int fd;
+
+ /* Open the file. Reject symbolic links on platforms that support
+ O_NOFOLLOW. This is a security feature. Without it, an attacker
+ could retrieve parts of the contents (namely, the tail of the
+ first line that starts with "* ") of an arbitrary file by placing
+ a symbolic link to that file under the name "charset.alias" in
+ some writable directory and defining the environment variable
+ CHARSETALIASDIR to point to that directory. */
+ fd = open (file_name,
+ O_RDONLY | (HAVE_WORKING_O_NOFOLLOW ? O_NOFOLLOW : 0));
+ if (fd < 0)
+ /* File not found. Treat it as empty. */
+ cp = "";
+ else
+ {
+ FILE *fp;
+
+ fp = fdopen (fd, "r");
+ if (fp == NULL)
+ {
+ /* Out of memory. Treat the file as empty. */
+ close (fd);
+ cp = "";
+ }
+ else
+ {
+ /* Parse the file's contents. */
+ char *res_ptr = NULL;
+ size_t res_size = 0;
+
+ for (;;)
+ {
+ int c;
+ char buf1[50+1];
+ char buf2[50+1];
+ size_t l1, l2;
+ char *old_res_ptr;
+
+ c = getc (fp);
+ if (c == EOF)
+ break;
+ if (c == '\n' || c == ' ' || c == '\t')
+ continue;
+ if (c == '#')
+ {
+ /* Skip comment, to end of line. */
+ do
+ c = getc (fp);
+ while (!(c == EOF || c == '\n'));
+ if (c == EOF)
+ break;
+ continue;
+ }
+ ungetc (c, fp);
+ if (fscanf (fp, "%50s %50s", buf1, buf2) < 2)
+ break;
+ l1 = strlen (buf1);
+ l2 = strlen (buf2);
+ old_res_ptr = res_ptr;
+ if (res_size == 0)
+ {
+ res_size = l1 + 1 + l2 + 1;
+ res_ptr = (char *) malloc (res_size + 1);
+ }
+ else
+ {
+ res_size += l1 + 1 + l2 + 1;
+ res_ptr = (char *) realloc (res_ptr, res_size + 1);
+ }
+ if (res_ptr == NULL)
+ {
+ /* Out of memory. */
+ res_size = 0;
+ free (old_res_ptr);
+ break;
+ }
+ strcpy (res_ptr + res_size - (l2 + 1) - (l1 + 1), buf1);
+ strcpy (res_ptr + res_size - (l2 + 1), buf2);
+ }
+ fclose (fp);
+ if (res_size == 0)
+ cp = "";
+ else
+ {
+ *(res_ptr + res_size) = '\0';
+ cp = res_ptr;
+ }
+ }
+ }
+
+ free (file_name);
+ }
+
+#else
+
+# if defined DARWIN7
+ /* To avoid the trouble of installing a file that is shared by many
+ GNU packages -- many packaging systems have problems with this --,
+ simply inline the aliases here. */
+ cp = "ISO8859-1" "\0" "ISO-8859-1" "\0"
+ "ISO8859-2" "\0" "ISO-8859-2" "\0"
+ "ISO8859-4" "\0" "ISO-8859-4" "\0"
+ "ISO8859-5" "\0" "ISO-8859-5" "\0"
+ "ISO8859-7" "\0" "ISO-8859-7" "\0"
+ "ISO8859-9" "\0" "ISO-8859-9" "\0"
+ "ISO8859-13" "\0" "ISO-8859-13" "\0"
+ "ISO8859-15" "\0" "ISO-8859-15" "\0"
+ "KOI8-R" "\0" "KOI8-R" "\0"
+ "KOI8-U" "\0" "KOI8-U" "\0"
+ "CP866" "\0" "CP866" "\0"
+ "CP949" "\0" "CP949" "\0"
+ "CP1131" "\0" "CP1131" "\0"
+ "CP1251" "\0" "CP1251" "\0"
+ "eucCN" "\0" "GB2312" "\0"
+ "GB2312" "\0" "GB2312" "\0"
+ "eucJP" "\0" "EUC-JP" "\0"
+ "eucKR" "\0" "EUC-KR" "\0"
+ "Big5" "\0" "BIG5" "\0"
+ "Big5HKSCS" "\0" "BIG5-HKSCS" "\0"
+ "GBK" "\0" "GBK" "\0"
+ "GB18030" "\0" "GB18030" "\0"
+ "SJIS" "\0" "SHIFT_JIS" "\0"
+ "ARMSCII-8" "\0" "ARMSCII-8" "\0"
+ "PT154" "\0" "PT154" "\0"
+ /*"ISCII-DEV" "\0" "?" "\0"*/
+ "*" "\0" "UTF-8" "\0";
+# endif
+
+# if defined VMS
+ /* To avoid the troubles of an extra file charset.alias_vms in the
+ sources of many GNU packages, simply inline the aliases here. */
+ /* The list of encodings is taken from the OpenVMS 7.3-1 documentation
+ "Compaq C Run-Time Library Reference Manual for OpenVMS systems"
+ section 10.7 "Handling Different Character Sets". */
+ cp = "ISO8859-1" "\0" "ISO-8859-1" "\0"
+ "ISO8859-2" "\0" "ISO-8859-2" "\0"
+ "ISO8859-5" "\0" "ISO-8859-5" "\0"
+ "ISO8859-7" "\0" "ISO-8859-7" "\0"
+ "ISO8859-8" "\0" "ISO-8859-8" "\0"
+ "ISO8859-9" "\0" "ISO-8859-9" "\0"
+ /* Japanese */
+ "eucJP" "\0" "EUC-JP" "\0"
+ "SJIS" "\0" "SHIFT_JIS" "\0"
+ "DECKANJI" "\0" "DEC-KANJI" "\0"
+ "SDECKANJI" "\0" "EUC-JP" "\0"
+ /* Chinese */
+ "eucTW" "\0" "EUC-TW" "\0"
+ "DECHANYU" "\0" "DEC-HANYU" "\0"
+ "DECHANZI" "\0" "GB2312" "\0"
+ /* Korean */
+ "DECKOREAN" "\0" "EUC-KR" "\0";
+# endif
+
+# if defined WINDOWS_NATIVE || defined __CYGWIN__
+ /* To avoid the troubles of installing a separate file in the same
+ directory as the DLL and of retrieving the DLL's directory at
+ runtime, simply inline the aliases here. */
+
+ cp = "CP936" "\0" "GBK" "\0"
+ "CP1361" "\0" "JOHAB" "\0"
+ "CP20127" "\0" "ASCII" "\0"
+ "CP20866" "\0" "KOI8-R" "\0"
+ "CP20936" "\0" "GB2312" "\0"
+ "CP21866" "\0" "KOI8-RU" "\0"
+ "CP28591" "\0" "ISO-8859-1" "\0"
+ "CP28592" "\0" "ISO-8859-2" "\0"
+ "CP28593" "\0" "ISO-8859-3" "\0"
+ "CP28594" "\0" "ISO-8859-4" "\0"
+ "CP28595" "\0" "ISO-8859-5" "\0"
+ "CP28596" "\0" "ISO-8859-6" "\0"
+ "CP28597" "\0" "ISO-8859-7" "\0"
+ "CP28598" "\0" "ISO-8859-8" "\0"
+ "CP28599" "\0" "ISO-8859-9" "\0"
+ "CP28605" "\0" "ISO-8859-15" "\0"
+ "CP38598" "\0" "ISO-8859-8" "\0"
+ "CP51932" "\0" "EUC-JP" "\0"
+ "CP51936" "\0" "GB2312" "\0"
+ "CP51949" "\0" "EUC-KR" "\0"
+ "CP51950" "\0" "EUC-TW" "\0"
+ "CP54936" "\0" "GB18030" "\0"
+ "CP65001" "\0" "UTF-8" "\0";
+# endif
+#endif
+
+ charset_aliases = cp;
+ }
+
+ return cp;
+}
+
+/* Determine the current locale's character encoding, and canonicalize it
+ into one of the canonical names listed in config.charset.
+ The result must not be freed; it is statically allocated.
+ If the canonical name cannot be determined, the result is a non-canonical
+ name. */
+
+#ifdef STATIC
+STATIC
+#endif
+const char *
+locale_charset (void)
+{
+ const char *codeset;
+ const char *aliases;
+
+#if !(defined WINDOWS_NATIVE || defined OS2)
+
+# if HAVE_LANGINFO_CODESET
+
+ /* Most systems support nl_langinfo (CODESET) nowadays. */
+ codeset = nl_langinfo (CODESET);
+
+# ifdef __CYGWIN__
+ /* Cygwin < 1.7 does not have locales. nl_langinfo (CODESET) always
+ returns "US-ASCII". Return the suffix of the locale name from the
+ environment variables (if present) or the codepage as a number. */
+ if (codeset != NULL && strcmp (codeset, "US-ASCII") == 0)
+ {
+ const char *locale;
+ static char buf[2 + 10 + 1];
+
+ locale = getenv ("LC_ALL");
+ if (locale == NULL || locale[0] == '\0')
+ {
+ locale = getenv ("LC_CTYPE");
+ if (locale == NULL || locale[0] == '\0')
+ locale = getenv ("LANG");
+ }
+ if (locale != NULL && locale[0] != '\0')
+ {
+ /* If the locale name contains an encoding after the dot, return
+ it. */
+ const char *dot = strchr (locale, '.');
+
+ if (dot != NULL)
+ {
+ const char *modifier;
+
+ dot++;
+ /* Look for the possible @... trailer and remove it, if any. */
+ modifier = strchr (dot, '@');
+ if (modifier == NULL)
+ return dot;
+ if (modifier - dot < sizeof (buf))
+ {
+ memcpy (buf, dot, modifier - dot);
+ buf [modifier - dot] = '\0';
+ return buf;
+ }
+ }
+ }
+
+ /* The Windows API has a function returning the locale's codepage as a
+ number: GetACP(). This encoding is used by Cygwin, unless the user
+ has set the environment variable CYGWIN=codepage:oem (which very few
+ people do).
+ Output directed to console windows needs to be converted (to
+ GetOEMCP() if the console is using a raster font, or to
+ GetConsoleOutputCP() if it is using a TrueType font). Cygwin does
+ this conversion transparently (see winsup/cygwin/fhandler_console.cc),
+ converting to GetConsoleOutputCP(). This leads to correct results,
+ except when SetConsoleOutputCP has been called and a raster font is
+ in use. */
+ sprintf (buf, "CP%u", GetACP ());
+ codeset = buf;
+ }
+# endif
+
+# else
+
+ /* On old systems which lack it, use setlocale or getenv. */
+ const char *locale = NULL;
+
+ /* But most old systems don't have a complete set of locales. Some
+ (like SunOS 4 or DJGPP) have only the C locale. Therefore we don't
+ use setlocale here; it would return "C" when it doesn't support the
+ locale name the user has set. */
+# if 0
+ locale = setlocale (LC_CTYPE, NULL);
+# endif
+ if (locale == NULL || locale[0] == '\0')
+ {
+ locale = getenv ("LC_ALL");
+ if (locale == NULL || locale[0] == '\0')
+ {
+ locale = getenv ("LC_CTYPE");
+ if (locale == NULL || locale[0] == '\0')
+ locale = getenv ("LANG");
+ }
+ }
+
+ /* On some old systems, one used to set locale = "iso8859_1". On others,
+ you set it to "language_COUNTRY.charset". In any case, we resolve it
+ through the charset.alias file. */
+ codeset = locale;
+
+# endif
+
+#elif defined WINDOWS_NATIVE
+
+ static char buf[2 + 10 + 1];
+
+ /* The Windows API has a function returning the locale's codepage as
+ a number, but the value doesn't change according to what the
+ 'setlocale' call specified. So we use it as a last resort, in
+ case the string returned by 'setlocale' doesn't specify the
+ codepage. */
+ char *current_locale = setlocale (LC_ALL, NULL);
+ char *pdot;
+
+ /* If they set different locales for different categories,
+ 'setlocale' will return a semi-colon separated list of locale
+ values. To make sure we use the correct one, we choose LC_CTYPE. */
+ if (strchr (current_locale, ';'))
+ current_locale = setlocale (LC_CTYPE, NULL);
+
+ pdot = strrchr (current_locale, '.');
+ if (pdot)
+ sprintf (buf, "CP%s", pdot + 1);
+ else
+ {
+ /* The Windows API has a function returning the locale's codepage as a
+ number: GetACP().
+ When the output goes to a console window, it needs to be provided in
+ GetOEMCP() encoding if the console is using a raster font, or in
+ GetConsoleOutputCP() encoding if it is using a TrueType font.
+ But in GUI programs and for output sent to files and pipes, GetACP()
+ encoding is the best bet. */
+ sprintf (buf, "CP%u", GetACP ());
+ }
+ codeset = buf;
+
+#elif defined OS2
+
+ const char *locale;
+ static char buf[2 + 10 + 1];
+ ULONG cp[3];
+ ULONG cplen;
+
+ /* Allow user to override the codeset, as set in the operating system,
+ with standard language environment variables. */
+ locale = getenv ("LC_ALL");
+ if (locale == NULL || locale[0] == '\0')
+ {
+ locale = getenv ("LC_CTYPE");
+ if (locale == NULL || locale[0] == '\0')
+ locale = getenv ("LANG");
+ }
+ if (locale != NULL && locale[0] != '\0')
+ {
+ /* If the locale name contains an encoding after the dot, return it. */
+ const char *dot = strchr (locale, '.');
+
+ if (dot != NULL)
+ {
+ const char *modifier;
+
+ dot++;
+ /* Look for the possible @... trailer and remove it, if any. */
+ modifier = strchr (dot, '@');
+ if (modifier == NULL)
+ return dot;
+ if (modifier - dot < sizeof (buf))
+ {
+ memcpy (buf, dot, modifier - dot);
+ buf [modifier - dot] = '\0';
+ return buf;
+ }
+ }
+
+ /* Resolve through the charset.alias file. */
+ codeset = locale;
+ }
+ else
+ {
+ /* OS/2 has a function returning the locale's codepage as a number. */
+ if (DosQueryCp (sizeof (cp), cp, &cplen))
+ codeset = "";
+ else
+ {
+ sprintf (buf, "CP%u", cp[0]);
+ codeset = buf;
+ }
+ }
+
+#endif
+
+ if (codeset == NULL)
+ /* The canonical name cannot be determined. */
+ codeset = "";
+
+ /* Resolve alias. */
+ for (aliases = get_charset_aliases ();
+ *aliases != '\0';
+ aliases += strlen (aliases) + 1, aliases += strlen (aliases) + 1)
+ if (strcmp (codeset, aliases) == 0
+ || (aliases[0] == '*' && aliases[1] == '\0'))
+ {
+ codeset = aliases + strlen (aliases) + 1;
+ break;
+ }
+
+ /* Don't return an empty string. GNU libc and GNU libiconv interpret
+ the empty string as denoting "the locale's character encoding",
+ thus GNU libiconv would call this function a second time. */
+ if (codeset[0] == '\0')
+ codeset = "ASCII";
+
+#ifdef DARWIN7
+ /* Mac OS X sets MB_CUR_MAX to 1 when LC_ALL=C, and "UTF-8"
+ (the default codeset) does not work when MB_CUR_MAX is 1. */
+ if (strcmp (codeset, "UTF-8") == 0 && MB_CUR_MAX_L (uselocale (NULL)) <= 1)
+ codeset = "ASCII";
+#endif
+
+ return codeset;
+}
diff --git a/src/libs/libgroff/macropath.cpp b/src/libs/libgroff/macropath.cpp
new file mode 100644
index 0000000..a9c9b35
--- /dev/null
+++ b/src/libs/libgroff/macropath.cpp
@@ -0,0 +1,29 @@
+// -*- C++ -*-
+/* Copyright (C) 1989-2020 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 3 of the License, 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 this program. If not, see <http://www.gnu.org/licenses/>. */
+
+#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/src/libs/libgroff/make-uniuni b/src/libs/libgroff/make-uniuni
new file mode 100755
index 0000000..386eacd
--- /dev/null
+++ b/src/libs/libgroff/make-uniuni
@@ -0,0 +1,162 @@
+#! /bin/sh
+#
+# make-uniuni -- script for creating the file uniuni.cpp
+#
+# Copyright (C) 2005-2020 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 3 of the License, 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 this program. If not, see <http://www.gnu.org/licenses/>.
+
+#
+# usage:
+#
+# make-uniuni <version-string> < UnicodeData.txt > uniuni.cpp
+#
+# 'UnicodeData.txt' is the central database file from the Unicode standard.
+# Unfortunately, it doesn't contain a version number which must be thus
+# provided manually as a parameter to the filter.
+#
+# This program needs a C preprocessor.
+#
+
+CPP=cpp
+
+prog="$0"
+
+if test $# -ne 1; then
+ echo "usage: $0 <version-string> < UnicodeData.txt > uniuni.cpp"
+ exit 1
+fi
+
+version_string="$1"
+
+# Remove ranges and control characters,
+# then extract the decomposition field,
+# then remove lines without decomposition,
+# then remove all compatibility decompositions.
+sed -e '/^[^;]*;</d' \
+| sed -e 's/;[^;]*;[^;]*;[^;]*;[^;]*;\([^;]*\);.*$/;\1/' \
+| sed -e '/^[^;]*;$/d' \
+| sed -e '/^[^;]*;</d' > $$1
+
+# Prepare input for running cpp.
+cat $$1 \
+| sed -e 's/^\([^;]*\);/#define \1 /' \
+ -e 's/ / u/g' > $$2
+cat $$1 \
+| sed -e 's/^\([^;]*\);.*$/\1 u\1/' >> $$2
+
+# Run C preprocessor to recursively decompose.
+$CPP $$2 $$3
+
+# Convert it back to original format.
+cat $$3 \
+| sed -e '/#/d' \
+ -e '/^$/d' \
+ -e 's/ \+/ /g' \
+ -e 's/ *$//' \
+ -e 's/u//g' \
+ -e 's/^\([^ ]*\) /\1;/' > $$4
+
+# Write preamble.
+cat <<END
+// -*- C++ -*-
+/* Copyright (C) 2002-2014 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 3 of the License, 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 this program. If not, see <http://www.gnu.org/licenses/>. */
+
+// This code has been algorithmically derived from the file
+// UnicodeData.txt, version $version_string, available from unicode.org,
+// on `date '+%Y-%m-%d'`.
+
+#include "lib.h"
+#include "stringclass.h"
+#include "ptable.h"
+
+#include "unicode.h"
+
+struct unicode_decompose {
+ char *value;
+};
+
+declare_ptable(unicode_decompose)
+implement_ptable(unicode_decompose)
+
+PTABLE(unicode_decompose) unicode_decompose_table;
+
+// the first digit in the composite string gives the number of composites
+
+struct S {
+ const char *key;
+ const char *value;
+} unicode_decompose_list[] = {
+END
+
+# Emit Unicode data.
+cat $$4 \
+| sed -e 's/ /_/g' \
+ -e 's/\(.*\);\(.*_.*_.*_.*\)$/ { "\1", "4\2" },/' \
+ -e 's/\(.*\);\(.*_.*_.*\)$/ { "\1", "3\2" },/' \
+ -e 's/\(.*\);\(.*_.*\)$/ { "\1", "2\2" },/' \
+ -e 's/\(.*\);\(.*\)$/ { "\1", "1\2" },/'
+
+# Write postamble.
+cat <<END
+};
+
+// global constructor
+
+static struct unicode_decompose_init {
+ unicode_decompose_init();
+} _unicode_decompose_init;
+
+unicode_decompose_init::unicode_decompose_init()
+{
+ for (unsigned int i = 0;
+ i < sizeof(unicode_decompose_list)/sizeof(unicode_decompose_list[0]);
+ i++) {
+ unicode_decompose *dec = new unicode_decompose[1];
+ dec->value = (char *)unicode_decompose_list[i].value;
+ unicode_decompose_table.define(unicode_decompose_list[i].key, dec);
+ }
+}
+
+const char *decompose_unicode(const char *s)
+{
+ unicode_decompose *result = unicode_decompose_table.lookup(s);
+ return result ? result->value : 0;
+}
+END
+
+
+# Remove temporary files.
+rm $$1 $$2 $$3 $$4
+
+# EOF
diff --git a/src/libs/libgroff/matherr.c b/src/libs/libgroff/matherr.c
new file mode 100644
index 0000000..a1adbf8
--- /dev/null
+++ b/src/libs/libgroff/matherr.c
@@ -0,0 +1,48 @@
+/* Copyright (C) 1989-2020 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 3 of the License, 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 this program. If not, see <http://www.gnu.org/licenses/>. */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <math.h>
+#include <errno.h>
+
+#ifdef HAVE_STRUCT_EXCEPTION
+#ifdef TLOSS
+
+int matherr(exc)
+struct exception *exc;
+{
+ switch (exc->type) {
+ case SING:
+ case DOMAIN:
+ errno = EDOM;
+ break;
+ case OVERFLOW:
+ case UNDERFLOW:
+ case TLOSS:
+ case PLOSS:
+ errno = ERANGE;
+ break;
+ }
+ return 1;
+}
+
+#endif /* TLOSS */
+#endif /* HAVE_STRUCT_EXCEPTION */
diff --git a/src/libs/libgroff/maxfilename.cpp b/src/libs/libgroff/maxfilename.cpp
new file mode 100644
index 0000000..5e1defe
--- /dev/null
+++ b/src/libs/libgroff/maxfilename.cpp
@@ -0,0 +1,75 @@
+// -*- C++ -*-
+/* Copyright (C) 1992-2020 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 3 of the License, 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 this program. If not, see <http://www.gnu.org/licenses/>. */
+
+/* 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_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
+#endif
+#endif
+
+#ifndef NAME_MAX
+#ifdef MAXNAMELEN
+#define NAME_MAX MAXNAMELEN
+#endif
+#endif
+
+#ifndef NAME_MAX
+#include <stdio.h>
+#ifdef FILENAME_MAX
+#define NAME_MAX FILENAME_MAX
+#endif
+#endif
+
+#ifndef NAME_MAX
+#define NAME_MAX 14
+#endif
+
+size_t file_name_max(const char *)
+{
+ return NAME_MAX;
+}
+
+#endif /* not _POSIX_VERSION */
diff --git a/src/libs/libgroff/maxpathname.cpp b/src/libs/libgroff/maxpathname.cpp
new file mode 100644
index 0000000..8041eeb
--- /dev/null
+++ b/src/libs/libgroff/maxpathname.cpp
@@ -0,0 +1,70 @@
+// -*- C++ -*-
+/* Copyright (C) 2005-2020 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 3 of the License, 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 this program. If not, see <http://www.gnu.org/licenses/>. */
+
+/* path_name_max(dir) does the same as pathconf(dir, _PC_PATH_MAX) */
+
+#include "lib.h"
+
+#include <sys/types.h>
+
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif /* HAVE_UNISTD_H */
+
+#ifdef _POSIX_VERSION
+
+size_t path_name_max()
+{
+ return pathconf("/", _PC_PATH_MAX) < 1 ? 1024 : pathconf("/",_PC_PATH_MAX);
+}
+
+#else /* not _POSIX_VERSION */
+
+#include <stdlib.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 PATH_MAX
+# ifdef MAXPATHLEN
+# define PATH_MAX MAXPATHLEN
+# else /* !MAXPATHLEN */
+# ifdef MAX_PATH
+# define PATH_MAX MAX_PATH
+# else /* !MAX_PATH */
+# ifdef _MAX_PATH
+# define PATH_MAX _MAX_PATH
+# else /* !_MAX_PATH */
+# define PATH_MAX 255
+# endif /* !_MAX_PATH */
+# endif /* !MAX_PATH */
+# endif /* !MAXPATHLEN */
+#endif /* !PATH_MAX */
+
+size_t path_name_max()
+{
+ return PATH_MAX;
+}
+
+#endif /* not _POSIX_VERSION */
diff --git a/src/libs/libgroff/mksdir.cpp b/src/libs/libgroff/mksdir.cpp
new file mode 100644
index 0000000..5fa6d28
--- /dev/null
+++ b/src/libs/libgroff/mksdir.cpp
@@ -0,0 +1,33 @@
+/* Copyright (C) 2001-2020 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 3 of the License, 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 this program. If not, see <http://www.gnu.org/licenses/>. */
+
+
+/* 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/src/libs/libgroff/mkstemp.cpp b/src/libs/libgroff/mkstemp.cpp
new file mode 100644
index 0000000..089a10a
--- /dev/null
+++ b/src/libs/libgroff/mkstemp.cpp
@@ -0,0 +1,33 @@
+/* Copyright (C) 2001-2020 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 3 of the License, 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 this program. If not, see <http://www.gnu.org/licenses/>. */
+
+
+/* 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/src/libs/libgroff/nametoindex.cpp b/src/libs/libgroff/nametoindex.cpp
new file mode 100644
index 0000000..095a432
--- /dev/null
+++ b/src/libs/libgroff/nametoindex.cpp
@@ -0,0 +1,167 @@
+/* Copyright (C) 1989-2020 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 3 of the License, 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 this program. If not, see <http://www.gnu.org/licenses/>. */
+
+#include "lib.h"
+
+#include <assert.h>
+#include <ctype.h>
+#include <stdlib.h>
+
+#include "errarg.h"
+#include "error.h"
+#include "font.h"
+#include "ptable.h"
+#include "itable.h"
+
+// Every glyphinfo is actually a charinfo.
+class charinfo : glyph {
+public:
+ const char *name; // The glyph name, or a null pointer.
+ friend class character_indexer;
+};
+
+// PTABLE(charinfo) is a hash table mapping 'const char *' to 'charinfo *'.
+declare_ptable(charinfo)
+implement_ptable(charinfo)
+
+// ITABLE(charinfo) is a hash table mapping 'int >= 0' to 'charinfo *'.
+declare_itable(charinfo)
+implement_itable(charinfo)
+
+// This class is as a registry storing all named and numbered glyphs known
+// so far, and assigns a unique index to each glyph.
+class character_indexer {
+public:
+ character_indexer();
+ ~character_indexer();
+ // --------------------- Lookup or creation of a glyph.
+ glyph *ascii_char_glyph(unsigned char);
+ glyph *named_char_glyph(const char *);
+ glyph *numbered_char_glyph(int);
+private:
+ int next_index; // Number of glyphs already allocated.
+ PTABLE(charinfo) table; // Table mapping name to glyph.
+ glyph *ascii_glyph[256]; // Shorthand table for looking up "charNNN"
+ // glyphs.
+ ITABLE(charinfo) ntable; // Table mapping number to glyph.
+ enum { NSMALL = 256 };
+ glyph *small_number_glyph[NSMALL]; // Shorthand table for looking up
+ // numbered glyphs with small numbers.
+};
+
+character_indexer::character_indexer()
+: next_index(0)
+{
+ int i;
+ for (i = 0; i < 256; i++)
+ ascii_glyph[i] = UNDEFINED_GLYPH;
+ for (i = 0; i < NSMALL; i++)
+ small_number_glyph[i] = UNDEFINED_GLYPH;
+}
+
+character_indexer::~character_indexer()
+{
+}
+
+glyph *character_indexer::ascii_char_glyph(unsigned char c)
+{
+ if (ascii_glyph[c] == UNDEFINED_GLYPH) {
+ char buf[4+3+1];
+ memcpy(buf, "char", 4);
+ strcpy(buf + 4, i_to_a(c));
+ charinfo *ci = new charinfo;
+ ci->index = next_index++;
+ ci->number = -1;
+ ci->name = strsave(buf);
+ ascii_glyph[c] = ci;
+ }
+ return ascii_glyph[c];
+}
+
+inline glyph *character_indexer::named_char_glyph(const char *s)
+{
+ // Glyphs with name 'charNNN' are only stored in ascii_glyph[], not
+ // in the table. Therefore treat them specially here.
+ if (s[0] == 'c' && s[1] == 'h' && s[2] == 'a' && s[3] == 'r') {
+ char *val;
+ long n = strtol(s + 4, &val, 10);
+ if (val != s + 4 && *val == '\0' && n >= 0 && n < 256)
+ return ascii_char_glyph((unsigned char)n);
+ }
+ charinfo *ci = table.lookupassoc(&s);
+ if (0 == ci) {
+ ci = new charinfo[1];
+ ci->index = next_index++;
+ ci->number = -1;
+ ci->name = table.define(s, ci);
+ }
+ return ci;
+}
+
+inline glyph *character_indexer::numbered_char_glyph(int n)
+{
+ if (n >= 0 && n < NSMALL) {
+ if (small_number_glyph[n] == UNDEFINED_GLYPH) {
+ charinfo *ci = new charinfo;
+ ci->index = next_index++;
+ ci->number = n;
+ ci->name = 0;
+ small_number_glyph[n] = ci;
+ }
+ return small_number_glyph[n];
+ }
+ charinfo *ci = ntable.lookup(n);
+ if (0 == ci) {
+ ci = new charinfo[1];
+ ci->index = next_index++;
+ ci->number = n;
+ ci->name = 0;
+ ntable.define(n, ci);
+ }
+ return ci;
+}
+
+static character_indexer indexer;
+
+glyph *number_to_glyph(int n)
+{
+ return indexer.numbered_char_glyph(n);
+}
+
+// troff overrides this function with its own version.
+
+glyph *name_to_glyph(const char *s)
+{
+ assert(s != 0 && s[0] != '\0' && s[0] != ' ');
+ if (s[1] == '\0')
+ // \200 and char128 are synonyms
+ return indexer.ascii_char_glyph(s[0]);
+ return indexer.named_char_glyph(s);
+}
+
+const char *glyph_to_name(glyph *g)
+{
+ charinfo *ci = (charinfo *)g; // Every glyph is actually a charinfo.
+ return ci->name;
+}
+
+// Local Variables:
+// fill-column: 72
+// mode: C++
+// End:
+// vim: set cindent noexpandtab shiftwidth=2 textwidth=72:
diff --git a/src/libs/libgroff/new.cpp b/src/libs/libgroff/new.cpp
new file mode 100644
index 0000000..2af976c
--- /dev/null
+++ b/src/libs/libgroff/new.cpp
@@ -0,0 +1,75 @@
+/* Copyright (C) 1989-2020 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 3 of the License, 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 this program. If not, see <http://www.gnu.org/licenses/>. */
+
+#include "lib.h"
+
+#include <stddef.h>
+#include <stdlib.h>
+
+#include "posix.h"
+#include "nonposix.h"
+
+extern "C" 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++;
+ char *p = (char *)malloc(unsigned(size));
+ if (p == 0) {
+ if (program_name) {
+ ewrite(program_name);
+ ewrite(": ");
+ }
+ ewrite("out of memory\n");
+ _exit(-1);
+ }
+ return p;
+}
+
+void operator delete(void *p) throw()
+{
+ if (p)
+ free(p);
+}
+
+void operator delete(void *p,
+ __attribute__((__unused__)) long unsigned int size)
+{
+ // It's ugly to duplicate the code from delete(void *) above, but if
+ // we don't, g++ 6.3 can't figure out we're calling through it to
+ // free().
+ //
+ // In function 'void operator delete(void*, long unsigned int)':
+ // warning: deleting 'void*' is undefined [-Wdelete-incomplete]
+ //delete p;
+ if (p)
+ free(p);
+}
+
+// Local Variables:
+// fill-column: 72
+// mode: C++
+// End:
+// vim: set cindent noexpandtab shiftwidth=2 textwidth=72:
diff --git a/src/libs/libgroff/paper.cpp b/src/libs/libgroff/paper.cpp
new file mode 100644
index 0000000..842f369
--- /dev/null
+++ b/src/libs/libgroff/paper.cpp
@@ -0,0 +1,82 @@
+// -*- C++ -*-
+/* Copyright (C) 2002-2020 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 3 of the License, 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 this program. If not, see <http://www.gnu.org/licenses/>. */
+
+#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 idx,
+ double length, double width )
+{
+ char *p = new char[strlen(name) + 1];
+ strcpy(p, name);
+ papersizes[idx].name = p;
+ papersizes[idx].length = length;
+ papersizes[idx].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/src/libs/libgroff/prime.cpp b/src/libs/libgroff/prime.cpp
new file mode 100644
index 0000000..5ae068d
--- /dev/null
+++ b/src/libs/libgroff/prime.cpp
@@ -0,0 +1,55 @@
+/* Copyright (C) 2014-2020 Free Software Foundation, Inc.
+
+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 of the License, 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.
+
+The GNU General Public License version 2 (GPL2) is available in the
+internet at <http://www.gnu.org/licenses/gpl-2.0.txt>. */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <assert.h>
+#include <math.h>
+
+bool is_prime(unsigned n)
+{
+ assert(n > 1);
+ if (n <= 3)
+ return true;
+ if (!(n & 1))
+ return false;
+ if (n % 3 == 0)
+ return false;
+ unsigned lim = unsigned(sqrt((double)n));
+ unsigned d = 5;
+ for (;;) {
+ if (d > lim)
+ break;
+ if (n % d == 0)
+ return false;
+ d += 2;
+ if (d > lim)
+ break;
+ if (n % d == 0)
+ return false;
+ d += 4;
+ }
+ return true;
+}
+
+// Local Variables:
+// fill-column: 72
+// mode: C++
+// End:
+// vim: set cindent noexpandtab shiftwidth=2 textwidth=72:
diff --git a/src/libs/libgroff/progname.c b/src/libs/libgroff/progname.c
new file mode 100644
index 0000000..f4320c1
--- /dev/null
+++ b/src/libs/libgroff/progname.c
@@ -0,0 +1,18 @@
+/* Copyright (C) 2014-2020 Free Software Foundation, Inc.
+
+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 of the License, 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.
+
+The GNU General Public License version 2 (GPL2) is available in the
+internet at <http://www.gnu.org/licenses/gpl-2.0.txt>. */
+
+const char *program_name = 0;
diff --git a/src/libs/libgroff/ptable.cpp b/src/libs/libgroff/ptable.cpp
new file mode 100644
index 0000000..52d09f8
--- /dev/null
+++ b/src/libs/libgroff/ptable.cpp
@@ -0,0 +1,57 @@
+/* Copyright (C) 1989-2020 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 3 of the License, 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 this program. If not, see <http://www.gnu.org/licenses/>. */
+
+#include <config.h>
+
+#include "ptable.h"
+#include "errarg.h"
+#include "error.h"
+
+unsigned long hash_string(const char *s)
+{
+ // This is the mythical Aho-Hopcroft-Ullman hash function.
+ // TODO: Improve. See http://www.haible.de/bruno/hashfunc.html
+ 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;
+}
+
+// end of ptable.cpp
diff --git a/src/libs/libgroff/putenv.c b/src/libs/libgroff/putenv.c
new file mode 100644
index 0000000..692a126
--- /dev/null
+++ b/src/libs/libgroff/putenv.c
@@ -0,0 +1,97 @@
+/* Copyright (C) 1991-2020 Free Software Foundation, Inc.
+This file is part of the GNU C Library.
+
+The GNU C Library is free software; you can redistribute it and/or
+modify it under the terms of the GNU Library General Public License as
+published by the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+The GNU C Library 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
+Library General Public License for more details.
+
+You should have received a copy of the GNU Library General Public License
+along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+/* Hacked slightly by jjc@jclark.com for groff. */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <string.h>
+
+#ifdef __STDC__
+#include <stddef.h>
+typedef void *PTR;
+typedef size_t SIZE_T;
+#else /* not __STDC__ */
+typedef char *PTR;
+typedef int SIZE_T;
+#endif /* not __STDC__ */
+
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#else /* not HAVE_STDLIB_H */
+PTR malloc();
+#endif /* not HAVE_STDLIB_H */
+
+#ifndef NULL
+#define NULL 0
+#endif
+
+extern char **environ;
+
+/* Put STRING, which is of the form 'NAME=VALUE', in the environment. */
+
+int putenv(const char *string)
+{
+ char *name_end = strchr(string, '=');
+ SIZE_T size;
+ char **ep;
+
+ if (name_end == NULL)
+ {
+ /* Remove the variable from the environment. */
+ size = strlen(string);
+ for (ep = environ; *ep != NULL; ++ep)
+ if (!strncmp(*ep, string, size) && (*ep)[size] == '=')
+ {
+ while (ep[1] != NULL)
+ {
+ ep[0] = ep[1];
+ ++ep;
+ }
+ *ep = NULL;
+ return 0;
+ }
+ }
+
+ size = 0;
+ for (ep = environ; *ep != NULL; ++ep)
+ if (!strncmp(*ep, string, name_end - string)
+ && (*ep)[name_end - string] == '=')
+ break;
+ else
+ ++size;
+
+ if (*ep == NULL)
+ {
+ static char **last_environ = NULL;
+ char **new_environ = (char **) malloc((size + 2) * sizeof(char *));
+ if (new_environ == NULL)
+ return -1;
+ (void) memcpy((PTR) new_environ, (PTR) environ, size * sizeof(char *));
+ new_environ[size] = (char *) string;
+ new_environ[size + 1] = NULL;
+ if (last_environ != NULL)
+ free((PTR) last_environ);
+ last_environ = new_environ;
+ environ = new_environ;
+ }
+ else
+ *ep = (char *) string;
+
+ return 0;
+}
diff --git a/src/libs/libgroff/quotearg.c b/src/libs/libgroff/quotearg.c
new file mode 100644
index 0000000..75eaca8
--- /dev/null
+++ b/src/libs/libgroff/quotearg.c
@@ -0,0 +1,213 @@
+/* Copyright (C) 2004-2020 Free Software Foundation, Inc.
+ Written by: Jeff Conrad (jeff_conrad@msn.com)
+ and Keith Marshall (keith.d.marshall@ntlworld.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 3 of the License, 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 this program. If not, see <http://www.gnu.org/licenses/>. */
+
+#include <config.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <limits.h>
+
+/* Define the default mechanism, and messages, for error reporting
+ * (user may substitute a preferred alternative, by defining his own
+ * implementation of the macros REPORT_ERROR, QUOTE_ARG_MALLOC_FAILED
+ * and QUOTE_ARG_REALLOC_FAILED, in the header file 'nonposix.h').
+ */
+
+#include "nonposix.h"
+
+#ifndef REPORT_ERROR
+# define REPORT_ERROR(WHY) fprintf(stderr, "%s:%s\n", program_name, WHY)
+#endif
+#ifndef QUOTE_ARG_MALLOC_ERROR
+# define QUOTE_ARG_MALLOC_ERROR "malloc: Buffer allocation failed"
+#endif
+#ifndef QUOTE_ARG_REALLOC_ERROR
+# define QUOTE_ARG_REALLOC_ERROR "realloc: Buffer resize failed"
+#endif
+
+extern char *program_name; /* main program must define this */
+
+/* Prototypes */
+char *quote_arg(char *);
+void purge_quoted_args(char **);
+
+#undef FALSE
+#undef TRUE
+#define FALSE 0
+#define TRUE 1
+
+static int
+needs_quoting(const char *string)
+{
+ /* Scan 'string' to see whether it needs quoting for MSVC 'spawn'/'exec'
+ * (i.e., whether it contains whitespace or embedded quotes).
+ */
+
+ if (string == NULL) /* ignore NULL strings */
+ return FALSE;
+
+ if (*string == '\0') /* explicit arguments of zero length */
+ return TRUE; /* need quoting, so they aren't discarded */
+
+ while (*string) {
+ /* Scan non-NULL strings, up to '\0' terminator,
+ * returning 'TRUE' if quote or white space found.
+ */
+
+ if (*string == '"' || isspace(*string))
+ return TRUE;
+
+ /* otherwise, continue scanning to end of string */
+
+ ++string;
+ }
+
+ /* Fall through, if no quotes or white space found,
+ * in which case, return 'FALSE'.
+ */
+
+ return FALSE;
+}
+
+char *
+quote_arg(char *string)
+{
+ /* Enclose arguments in double quotes so that the parsing done in the
+ * MSVC runtime startup code doesn't split them at whitespace. Escape
+ * embedded double quotes so that they emerge intact from the parsing.
+ */
+
+ int backslashes;
+ char *quoted, *p, *q;
+
+ if (needs_quoting(string)) {
+ /* Need to create a quoted copy of 'string';
+ * maximum buffer space needed is twice the original length,
+ * plus two enclosing quotes and one '\0' terminator.
+ */
+
+ if ((quoted = (char *)malloc(2 * strlen(string) + 3)) == NULL) {
+ /* Couldn't get a buffer for the quoted string,
+ * so complain, and bail out gracefully.
+ */
+
+ REPORT_ERROR(QUOTE_ARG_MALLOC_ERROR);
+ exit(1);
+ }
+
+ /* Ok to proceed:
+ * insert the opening quote, then copy the source string,
+ * adding escapes as required.
+ */
+
+ *quoted = '"';
+ for (backslashes = 0, p = string, q = quoted; *p; p++) {
+ if (*p == '\\') {
+ /* Just count backslashes when we find them.
+ * We will copy them out later, when we know if the count
+ * needs to be adjusted, to escape an embedded quote.
+ */
+
+ ++backslashes;
+ }
+ else if (*p == '"') {
+ /* This embedded quote character must be escaped,
+ * but first double up any immediately preceding backslashes,
+ * with one extra, as the escape character.
+ */
+
+ for (backslashes += backslashes + 1; backslashes; backslashes--)
+ *++q = '\\';
+
+ /* and now, add the quote character itself */
+
+ *++q = '"';
+ }
+ else {
+ /* Any other character is simply copied,
+ * but first, if we have any pending backslashes,
+ * we must now insert them, without any count adjustment.
+ */
+
+ while (backslashes) {
+ *++q = '\\';
+ --backslashes;
+ }
+
+ /* and then, copy the current character */
+
+ *++q = *p;
+ }
+ }
+
+ /* At end of argument:
+ * If any backslashes remain to be copied out, append them now,
+ * doubling the actual count to protect against reduction by MSVC,
+ * as a consequence of the immediately following closing quote.
+ */
+
+ for (backslashes += backslashes; backslashes; backslashes--)
+ *++q = '\\';
+
+ /* Finally,
+ * add the closing quote, terminate the quoted string,
+ * and adjust its size to what was actually required,
+ * ready for return.
+ */
+
+ *++q = '"';
+ *++q = '\0';
+ if ((string = (char *)realloc(quoted, strlen(quoted) + 1)) == NULL) {
+ /* but bail out gracefully, on error */
+
+ REPORT_ERROR(QUOTE_ARG_REALLOC_ERROR);
+ exit(1);
+ }
+ }
+
+ /* 'string' now refers to the argument,
+ * quoted and escaped, as required.
+ */
+
+ return string;
+}
+
+void
+purge_quoted_args(char **argv)
+{
+ /* To avoid memory leaks,
+ * free all memory previously allocated by 'quoted_arg()',
+ * within the scope of the referring argument vector, 'argv'.
+ */
+
+ if (argv)
+ while (*argv) {
+ /* Any argument beginning with a double quote
+ * SHOULD have been allocated by 'quoted_arg()'.
+ */
+
+ if (**argv == '"')
+ free( *argv ); /* so free its allocation */
+ ++argv; /* and continue to the next argument */
+ }
+}
+
+/* quotearg.c: end of file */
diff --git a/src/libs/libgroff/ref-add.sin b/src/libs/libgroff/ref-add.sin
new file mode 100644
index 0000000..50aa7d1
--- /dev/null
+++ b/src/libs/libgroff/ref-add.sin
@@ -0,0 +1,29 @@
+# Add this package to a list of references stored in a text file.
+#
+# Copyright (C) 2000-2020 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, see <http://www.gnu.org/licenses/>.
+#
+# Written by Bruno Haible <haible@clisp.cons.org>.
+#
+/^# Packages using this file: / {
+ s/# Packages using this file://
+ ta
+ :a
+ s/ @PACKAGE@ / @PACKAGE@ /
+ tb
+ s/ $/ @PACKAGE@ /
+ :b
+ s/^/# Packages using this file:/
+}
diff --git a/src/libs/libgroff/ref-del.sin b/src/libs/libgroff/ref-del.sin
new file mode 100644
index 0000000..1601469
--- /dev/null
+++ b/src/libs/libgroff/ref-del.sin
@@ -0,0 +1,24 @@
+# Remove this package from a list of references stored in a text file.
+#
+# Copyright (C) 2000-2020 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, see <http://www.gnu.org/licenses/>.
+#
+# Written by Bruno Haible <haible@clisp.cons.org>.
+#
+/^# Packages using this file: / {
+ s/# Packages using this file://
+ s/ @PACKAGE@ / /
+ s/^/# Packages using this file:/
+}
diff --git a/src/libs/libgroff/relocatable.h b/src/libs/libgroff/relocatable.h
new file mode 100644
index 0000000..f152194
--- /dev/null
+++ b/src/libs/libgroff/relocatable.h
@@ -0,0 +1,19 @@
+/* Copyright (C) 2014-2020 Free Software Foundation, Inc.
+
+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 of the License, 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.
+
+The GNU General Public License version 2 (GPL2) is available in the
+internet at <http://www.gnu.org/licenses/gpl-2.0.txt>. */
+
+#include "relocate.h"
+#define relocate(path) relocatep(path)
diff --git a/src/libs/libgroff/relocate.cpp b/src/libs/libgroff/relocate.cpp
new file mode 100644
index 0000000..32a0e2e
--- /dev/null
+++ b/src/libs/libgroff/relocate.cpp
@@ -0,0 +1,244 @@
+/* Provide relocation for macro and font files.
+ Copyright (C) 2005-2020 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Library General Public License as published
+ by the Free Software Foundation, either version 3 of the License, 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
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+// Made after relocation code in kpathsea and gettext.
+
+#include "lib.h"
+
+#include <assert.h>
+#include <errno.h>
+#include <stdlib.h>
+
+#include "defs.h"
+#include "posix.h"
+#include "nonposix.h"
+#include "relocate.h"
+
+#if defined _WIN32
+# define WIN32_LEAN_AND_MEAN
+# include <windows.h>
+#endif
+
+#define INSTALLPATHLEN (sizeof(INSTALLPATH) - 1)
+#ifndef DEBUG
+# define DEBUG 0
+#endif
+
+extern "C" const char *program_name;
+
+// The prefix (parent directory) corresponding to the binary.
+char *curr_prefix = 0;
+size_t curr_prefix_len = 0;
+
+// Return the directory part of a filename, or '.' if no path separators.
+char *xdirname(char *s)
+{
+ static const char dot[] = ".";
+ if (!s)
+ return 0;
+ // DIR_SEPS[] are possible directory separator characters, see nonposix.h.
+ // We want the rightmost separator of all possible ones.
+ // Example: d:/foo\\bar.
+ char *p = strrchr(s, DIR_SEPS[0]);
+ const char *sep = &DIR_SEPS[1];
+ while (*sep) {
+ char *p1 = strrchr(s, *sep);
+ if (p1 && (!p || p1 > p))
+ p = p1;
+ sep++;
+ }
+ if (p)
+ *p = '\0';
+ else
+ s = (char *)dot;
+ return s;
+}
+
+// Return the full path of NAME along the path PATHP.
+// Adapted from search_path::open_file in searchpath.cpp.
+char *searchpath(const char *name, const char *pathp)
+{
+ char *path;
+ if (!name || !*name)
+ return 0;
+#if DEBUG
+ fprintf(stderr, "searchpath: pathp: '%s'\n", pathp);
+ fprintf(stderr, "searchpath: trying '%s'\n", name);
+#endif
+ // Try first NAME as such; success if NAME is an absolute filename,
+ // or if NAME is found in the current directory.
+ if (!access (name, F_OK)) {
+ path = new char[path_name_max()];
+#ifdef _WIN32
+ path = _fullpath(path, name, path_name_max());
+#else
+ path = realpath(name, path);
+#endif
+#if DEBUG
+ fprintf(stderr, "searchpath: found '%s'\n", path);
+#endif
+ return path;
+ }
+ // Secondly, try the current directory.
+ // Now search along PATHP.
+ size_t namelen = strlen(name);
+ char *p = (char *)pathp;
+ for (;;) {
+ char *end = strchr(p, PATH_SEP_CHAR);
+ if (!end)
+ end = strchr(p, '\0');
+ int need_slash = end > p && strchr(DIR_SEPS, end[-1]) == 0;
+ 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 DEBUG
+ fprintf(stderr, "searchpath: trying '%s'\n", path);
+#endif
+ if (!access(path, F_OK)) {
+#if DEBUG
+ fprintf(stderr, "searchpath: found '%s'\n", name);
+#endif
+ return path;
+ }
+ delete[] path;
+ if (*end == '\0')
+ break;
+ p = end + 1;
+ }
+ return 0;
+}
+
+// Search NAME along PATHP with the elements of PATHEXT in turn added.
+char *searchpathext(const char *name, const char *pathext, const char *pathp)
+{
+ char *found = 0;
+ char *tmpathext = strsave(pathext); // strtok modifies this string,
+ // so make a copy
+ char *ext = strtok(tmpathext, PATH_SEP);
+ while (ext) {
+ char *namex = new char[strlen(name) + strlen(ext) + 1];
+ strcpy(namex, name);
+ strcat(namex, ext);
+ found = searchpath(namex, pathp);
+ delete[] namex;
+ if (found)
+ break;
+ ext = strtok(0, PATH_SEP);
+ }
+ delete[] tmpathext;
+ return found;
+}
+
+// Convert an MS path to a POSIX path.
+char *msw2posixpath(char *path)
+{
+ char *s = path;
+ while (*s) {
+ if (*s == '\\')
+ *s = '/';
+ s++;
+ }
+ return path;
+}
+
+// Compute the current prefix.
+void set_current_prefix()
+{
+ // Obtain the full path of the current binary;
+ // using GetModuleFileName on MS-Windows,
+ // and searching along PATH on other systems.
+#ifdef _WIN32
+ char *pathextstr;
+ curr_prefix = new char[path_name_max()];
+ int len = GetModuleFileName(0, curr_prefix, path_name_max());
+ if (len)
+ len = GetShortPathName(curr_prefix, curr_prefix, path_name_max());
+# if DEBUG
+ fprintf(stderr, "curr_prefix: %s\n", curr_prefix);
+# endif /* DEBUG */
+ if (!curr_prefix && !strchr(program_name, '.')) { // try with extensions
+ pathextstr = strsave(getenv("PATHEXT"));
+ if (!pathextstr)
+ pathextstr = strsave(PATH_EXT);
+ curr_prefix = searchpathext(program_name, pathextstr, getenv("PATH"));
+ delete[] pathextstr;
+ }
+#else /* !_WIN32 */
+ curr_prefix = searchpath(program_name, getenv("PATH"));
+ if (!curr_prefix)
+ return;
+#endif /* !_WIN32 */
+ msw2posixpath(curr_prefix);
+#if DEBUG
+ fprintf(stderr, "curr_prefix: %s\n", curr_prefix);
+#endif
+ curr_prefix = xdirname(curr_prefix); // directory of executable
+ curr_prefix = xdirname(curr_prefix); // parent directory of executable
+ curr_prefix_len = strlen(curr_prefix);
+#if DEBUG
+ fprintf(stderr, "curr_prefix: %s\n", curr_prefix);
+ fprintf(stderr, "curr_prefix_len: %d\n", curr_prefix_len);
+#endif
+}
+
+// Strip the installation prefix and replace it
+// with the current installation prefix; return the relocated path.
+char *relocatep(const char *path)
+{
+#if DEBUG
+ fprintf(stderr, "relocatep: path = %s\n", path);
+ fprintf(stderr, "relocatep: INSTALLPATH = %s\n", INSTALLPATH);
+ fprintf(stderr, "relocatep: INSTALLPATHLEN = %d\n", INSTALLPATHLEN);
+#endif
+ if (!curr_prefix)
+ set_current_prefix();
+ if (strncmp(INSTALLPATH, path, INSTALLPATHLEN))
+ return strsave(path);
+ char *relative_path = (char *)path + INSTALLPATHLEN;
+ size_t relative_path_len = strlen(relative_path);
+ char *relocated_path = (char *)malloc(curr_prefix_len
+ + relative_path_len + 1);
+ assert(0 != curr_prefix);
+ strcpy(relocated_path, curr_prefix);
+ strcat(relocated_path, relative_path);
+#if DEBUG
+ fprintf(stderr, "relocated_path: %s\n", relocated_path);
+#endif /* DEBUG */
+ return relocated_path;
+}
+
+// Return the original pathname if it exists;
+// otherwise return the relocated path.
+char *relocate(const char *path)
+{
+ char *p;
+ if (access(path, F_OK))
+ p = relocatep(path);
+ else
+ p = strsave(path);
+#if DEBUG
+ fprintf (stderr, "relocate: %s\n", p);
+#endif
+ return p;
+}
+
+// Local Variables:
+// fill-column: 72
+// mode: C++
+// End:
+// vim: set cindent noexpandtab shiftwidth=2 textwidth=72:
diff --git a/src/libs/libgroff/searchpath.cpp b/src/libs/libgroff/searchpath.cpp
new file mode 100644
index 0000000..6062e8d
--- /dev/null
+++ b/src/libs/libgroff/searchpath.cpp
@@ -0,0 +1,215 @@
+/* Copyright (C) 1989-2020 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 3 of the License, 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 this program. If not, see <http://www.gnu.org/licenses/>. */
+
+#include "lib.h"
+
+#include <assert.h>
+#include <errno.h>
+#include <stdlib.h>
+
+#include "searchpath.h"
+#include "nonposix.h"
+
+#ifdef _WIN32
+# include "relocate.h"
+#else
+# define relocate(path) strsave(path)
+#endif
+
+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
+ 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_CHAR;
+ memcpy(p, s, slen);
+ p += slen;
+ if (init_len > 0) {
+ *p++ = PATH_SEP_CHAR;
+ memcpy(p, old + old_len - init_len, init_len);
+ p += init_len;
+ }
+ *p++ = '\0';
+ 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_CHAR);
+ if (!end)
+ end = strchr(p, '\0');
+ int need_slash = end > p && strchr(DIR_SEPS, end[-1]) == 0;
+ char *origpath = new char[(end - p) + need_slash + namelen + 1];
+ memcpy(origpath, p, end - p);
+ if (need_slash)
+ origpath[end - p] = '/';
+ strcpy(origpath + (end - p) + need_slash, name);
+#if 0
+ fprintf(stderr, "origpath '%s'\n", origpath);
+#endif
+ char *path = relocate(origpath);
+ delete[] origpath;
+#if 0
+ fprintf(stderr, "trying '%s'\n", path);
+#endif
+ FILE *fp = fopen(path, "r");
+ int err = errno;
+ if (fp) {
+ if (pathp)
+ *pathp = path;
+ else {
+ free(path);
+ errno = err;
+ }
+ return fp;
+ }
+ free(path);
+ errno = err;
+ if (*end == '\0')
+ break;
+ p = end + 1;
+ }
+ return 0;
+}
+
+FILE *search_path::open_file_cautious(const char *name, char **pathp,
+ const char *mode)
+{
+ if (!mode)
+ mode = "r";
+ bool reading = (strchr(mode, 'r') != 0);
+ if (name == 0 || strcmp(name, "-") == 0) {
+ if (pathp)
+ *pathp = strsave(reading ? "stdin" : "stdout");
+ return (reading ? stdin : stdout);
+ }
+ if (!reading || IS_ABSOLUTE(name) || *dirs == '\0') {
+ FILE *fp = fopen(name, mode);
+ 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_CHAR);
+ if (!end)
+ end = strchr(p, '\0');
+ int need_slash = end > p && strchr(DIR_SEPS, end[-1]) == 0;
+ char *origpath = new char[(end - p) + need_slash + namelen + 1];
+ memcpy(origpath, p, end - p);
+ if (need_slash)
+ origpath[end - p] = '/';
+ strcpy(origpath + (end - p) + need_slash, name);
+#if 0
+ fprintf(stderr, "origpath '%s'\n", origpath);
+#endif
+ char *path = relocate(origpath);
+ delete[] origpath;
+#if 0
+ fprintf(stderr, "trying '%s'\n", path);
+#endif
+ FILE *fp = fopen(path, mode);
+ int err = errno;
+ if (fp) {
+ if (pathp)
+ *pathp = path;
+ else {
+ free(path);
+ errno = err;
+ }
+ return fp;
+ }
+ free(path);
+ errno = err;
+ if (err != ENOENT)
+ return 0;
+ if (*end == '\0')
+ break;
+ p = end + 1;
+ }
+ errno = ENOENT;
+ return 0;
+}
+
+// Local Variables:
+// fill-column: 72
+// mode: C++
+// End:
+// vim: set cindent noexpandtab shiftwidth=2 textwidth=72:
diff --git a/src/libs/libgroff/spawnvp.c b/src/libs/libgroff/spawnvp.c
new file mode 100644
index 0000000..1fffa2b
--- /dev/null
+++ b/src/libs/libgroff/spawnvp.c
@@ -0,0 +1,120 @@
+/* Copyright (C) 2004-2020 Free Software Foundation, Inc.
+ Written by: Keith Marshall (keith.d.marshall@ntlworld.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 3 of the License, 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 this program. If not, see <http://www.gnu.org/licenses/>. */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#ifdef HAVE_PROCESS_H
+# include <process.h>
+#endif
+
+#if defined(__MSDOS__) \
+ || (defined(_WIN32) && !defined(_UWIN) && !defined(__CYGWIN__)) \
+ || defined(__EMX__)
+
+#define SPAWN_FUNCTION_WRAPPERS 1
+
+/* Define the default mechanism, and messages, for error reporting
+ * (user may substitute a preferred alternative, by defining his own
+ * implementation of the macros REPORT_ERROR and ARGV_MALLOC_ERROR,
+ * in the header file 'nonposix.h').
+ */
+
+#include "nonposix.h"
+
+#ifndef REPORT_ERROR
+# define REPORT_ERROR(WHY) fprintf(stderr, "%s:%s\n", program_name, WHY)
+#endif
+#ifndef ARGV_MALLOC_ERROR
+# define ARGV_MALLOC_ERROR "malloc: Allocation for 'argv' failed"
+#endif
+
+extern char *program_name;
+
+extern char *quote_arg(char *string);
+extern void purge_quoted_args(char **argv);
+
+int
+spawnvp_wrapper(int mode, char *path, char **argv)
+{
+ /* Invoke the system 'spawnvp' service
+ * enclosing the passed arguments in double quotes, as required,
+ * so that the (broken) default parsing in the MSVC runtime doesn't
+ * split them at whitespace. */
+
+ char **quoted_argv; /* used to build a quoted local copy of 'argv' */
+
+ int i; /* used as an index into 'argv' or 'quoted_argv' */
+ int status = -1; /* initialise return code, in case we fail */
+ int argc = 0; /* initialise argument count; may be none */
+
+ /* First count the number of arguments
+ * which are actually present in the passed 'argv'. */
+
+ if (argv)
+ for (quoted_argv = argv; *quoted_argv; ++argc, ++quoted_argv)
+ ;
+
+ /* If we do not now have an argument count,
+ * then we must fall through and fail. */
+
+ if (argc) {
+ /* We do have at least one argument:
+ * We will use a copy of the 'argv', in which to do the quoting,
+ * so we must allocate space for it. */
+
+ if ((quoted_argv = (char **)malloc(++argc * sizeof(char **))) == NULL) {
+ /* If we didn't get enough space,
+ * then complain, and bail out gracefully. */
+
+ REPORT_ERROR(ARGV_MALLOC_ERROR);
+ exit(1);
+ }
+
+ /* Now copy the passed 'argv' into our new vector,
+ * quoting its contents as required. */
+
+ for (i = 0; i < argc; i++)
+ quoted_argv[i] = quote_arg(argv[i]);
+
+ /* Invoke the MSVC 'spawnvp' service
+ * passing our now appropriately quoted copy of 'argv'. */
+
+ status = spawnvp(mode, path, quoted_argv);
+
+ /* Clean up our memory allocations
+ * for the quoted copy of 'argv', which is no longer required. */
+
+ purge_quoted_args(quoted_argv);
+ free(quoted_argv);
+ }
+
+ /* Finally,
+ * return the status code returned by 'spawnvp',
+ * or a failure code if we fell through. */
+
+ return status;
+}
+
+#endif /* __MSDOS__ || _WIN32 */
+
+/* spawnvp.c: end of file */
diff --git a/src/libs/libgroff/strcasecmp.c b/src/libs/libgroff/strcasecmp.c
new file mode 100644
index 0000000..22c0997
--- /dev/null
+++ b/src/libs/libgroff/strcasecmp.c
@@ -0,0 +1,65 @@
+/* strcasecmp.c -- case insensitive string comparator
+ Copyright (C) 1998-2020 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 3 of the License, 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, see <http://www.gnu.org/licenses/>. */
+
+#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 <stddef.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/src/libs/libgroff/strerror.c b/src/libs/libgroff/strerror.c
new file mode 100644
index 0000000..a8887e5
--- /dev/null
+++ b/src/libs/libgroff/strerror.c
@@ -0,0 +1,46 @@
+/* Copyright (C) 1989-2020 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 3 of the License, 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 this program. If not, see <http://www.gnu.org/licenses/>. */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <errno.h>
+#include <stdlib.h> /* for MinGW */
+
+#define INT_DIGITS 19 /* enough for 64 bit integer */
+
+#ifndef HAVE_SYS_NERR
+extern int sys_nerr;
+#endif
+#ifndef HAVE_SYS_ERRLIST
+extern char *sys_errlist[];
+#endif
+
+char *strerror(n)
+ int n;
+{
+ static char buf[sizeof("Error ") + 1 + INT_DIGITS];
+ if (n >= 0 && n < sys_nerr && sys_errlist[n] != 0)
+ return sys_errlist[n];
+ else {
+ sprintf(buf, "Error %d", n);
+ return buf;
+ }
+}
diff --git a/src/libs/libgroff/string.cpp b/src/libs/libgroff/string.cpp
new file mode 100644
index 0000000..33c0565
--- /dev/null
+++ b/src/libs/libgroff/string.cpp
@@ -0,0 +1,354 @@
+/* Copyright (C) 1989-2020 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 3 of the License, 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 this program. If not, see <http://www.gnu.org/licenses/>. */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdlib.h>
+
+#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)
+{
+ delete[] ptr;
+}
+
+static char *sfree_alloc(char *ptr, int oldsz, int len, int *sizep)
+{
+ if (oldsz >= len) {
+ *sizep = oldsz;
+ return ptr;
+ }
+ 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) {
+ delete[] ptr;
+ *sizep = 0;
+ return 0;
+ }
+ else {
+ char *p = new char[*sizep = newlen*2];
+ if (oldlen < newlen && oldlen != 0)
+ memcpy(p, ptr, oldlen);
+ 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);
+ if (len != 0)
+ 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;
+ if (len != 0)
+ 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) : 0;
+ 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 =(char*)malloc(n + 1 - nnuls);
+ if (q != 0 /* nullptr */) {
+ 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[sz];
+ memcpy(tmp, p, len);
+ delete[] ptr;
+ ptr = tmp;
+ }
+ else {
+ len = 0;
+ if (ptr) {
+ delete[] ptr;
+ ptr = 0;
+ sz = 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);
+}
+
+// Local Variables:
+// fill-column: 72
+// mode: C++
+// End:
+// vim: set cindent noexpandtab shiftwidth=2 textwidth=72:
diff --git a/src/libs/libgroff/strncasecmp.c b/src/libs/libgroff/strncasecmp.c
new file mode 100644
index 0000000..ec8aae2
--- /dev/null
+++ b/src/libs/libgroff/strncasecmp.c
@@ -0,0 +1,19 @@
+/* Copyright (C) 2014-2020 Free Software Foundation, Inc.
+
+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 of the License, 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.
+
+The GNU General Public License version 2 (GPL2) is available in the
+internet at <http://www.gnu.org/licenses/gpl-2.0.txt>. */
+
+#define LENGTH_LIMIT
+#include "strcasecmp.c"
diff --git a/src/libs/libgroff/strsave.cpp b/src/libs/libgroff/strsave.cpp
new file mode 100644
index 0000000..95a529b
--- /dev/null
+++ b/src/libs/libgroff/strsave.cpp
@@ -0,0 +1,40 @@
+/* Copyright (C) 1989-2020 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 3 of the License, 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 this program. If not, see <http://www.gnu.org/licenses/>. */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <string.h>
+#include <stdlib.h>
+
+char *strsave(const char *s)
+{
+ if (s == 0)
+ return 0;
+ char *p = (char*)malloc(strlen(s) + 1);
+ if (p != 0)
+ strcpy(p, s);
+ return p;
+}
+
+// Local Variables:
+// fill-column: 72
+// mode: C++
+// End:
+// vim: set cindent noexpandtab shiftwidth=2 textwidth=72:
diff --git a/src/libs/libgroff/strtol.c b/src/libs/libgroff/strtol.c
new file mode 100644
index 0000000..1820732
--- /dev/null
+++ b/src/libs/libgroff/strtol.c
@@ -0,0 +1,131 @@
+/* Copyright (C) 1989-2020 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 3 of the License, 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 this program. If not, see <http://www.gnu.org/licenses/>. */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <string.h>
+#include <ctype.h>
+#include <errno.h>
+
+#ifdef HAVE_LIMITS_H
+#include <limits.h>
+#endif
+
+#ifndef LONG_MAX
+#define LONG_MAX 2147483647
+#endif
+
+#ifndef LONG_MIN
+#define LONG_MIN (-LONG_MAX-1)
+#endif
+
+#ifdef isascii
+#define ISASCII(c) isascii(c)
+#else
+#define ISASCII(c) (1)
+#endif
+
+long strtol(str, ptr, base)
+ char *str, **ptr;
+ int base;
+{
+ char *start = str;
+ int neg = 0;
+ long val;
+ char *p;
+ static char digits[] = "0123456789abcdefghijklmnopqrstuvwxyz";
+
+ while (ISASCII((unsigned char)*str) && isspace((unsigned char)*str))
+ str++;
+
+ if (*str == '-') {
+ neg = 1;
+ str++;
+ }
+ if (base == 0) {
+ if (*str == '0') {
+ if (str[1] == 'x' || str[1] == 'X') {
+ str += 2;
+ base = 16;
+ }
+ else
+ base = 8;
+ }
+ else
+ base = 10;
+ }
+ if (base < 2 || base > 36)
+ base = 10;
+ else if (base == 16 && *str == '0' && (str[1] == 'x' || str[1] == 'X'))
+ str += 2;
+
+ p = strchr(digits, (ISASCII((unsigned char)*str)
+ && isupper((unsigned char)*str)
+ ? tolower((unsigned char)*str)
+ : *str));
+ if (p == 0 || (val = (p - digits)) >= base) {
+ if (base == 16 && str > start && (str[-1] == 'x' || str[-1] == 'X')) {
+ if (ptr)
+ *ptr = str - 1;
+ }
+ else {
+ if (ptr)
+ *ptr = start;
+ errno = ERANGE;
+ }
+ return 0;
+ }
+ if (neg)
+ val = -val;
+
+ while (*++str != '\0') {
+ int n;
+
+ p = strchr(digits, (ISASCII((unsigned char)*str)
+ && isupper((unsigned char)*str)
+ ? tolower((unsigned char)*str) : *str));
+ if (p == 0)
+ break;
+ n = p - digits;
+ if (n >= base)
+ break;
+ if (neg) {
+ if (-(unsigned long)val > (-(unsigned long)LONG_MIN - n)/base) {
+ val = LONG_MIN;
+ errno = ERANGE;
+ }
+ else
+ val = val*base - n;
+ }
+ else {
+ if (val > (LONG_MAX - n)/base) {
+ val = LONG_MAX;
+ errno = ERANGE;
+ }
+ else
+ val = val*base + n;
+ }
+ }
+
+ if (ptr)
+ *ptr = str;
+
+ return val;
+}
diff --git a/src/libs/libgroff/symbol.cpp b/src/libs/libgroff/symbol.cpp
new file mode 100644
index 0000000..4f50627
--- /dev/null
+++ b/src/libs/libgroff/symbol.cpp
@@ -0,0 +1,157 @@
+// -*- C++ -*-
+/* Copyright (C) 1989-2020 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 3 of the License, 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 this program. If not, see <http://www.gnu.org/licenses/>. */
+
+#include "lib.h"
+
+#include "errarg.h"
+#include "error.h"
+#include "symbol.h"
+
+const char **symbol::table = 0;
+int symbol::table_used = 0;
+int symbol::table_size = 0;
+char *symbol::block = 0;
+int symbol::block_size = 0;
+
+const symbol NULL_SYMBOL;
+const symbol EMPTY_SYMBOL("");
+
+#ifdef BLOCK_SIZE
+#undef BLOCK_SIZE
+#endif
+
+const int BLOCK_SIZE = 1024;
+// the table will increase in size as necessary
+// the size will be chosen from the following array
+// add some more if you want
+static const unsigned int table_sizes[] = {
+ 101, 503, 1009, 2003, 3001, 4001, 5003, 10007, 20011, 40009, 80021,
+ 160001, 500009, 1000003, 1500007, 2000003, 0
+};
+const double FULL_MAX = 0.3; // don't let the table get more than this full
+
+static unsigned int hash_string(const char *p)
+{
+ // compute a hash code; this assumes 32-bit unsigned ints
+ // see p436 of Compilers by Aho, Sethi & Ullman
+ // give special treatment to two-character names
+ unsigned int hc = 0, g;
+ if (*p != 0) {
+ hc = *p++;
+ if (*p != 0) {
+ hc <<= 7;
+ hc += *p++;
+ for (; *p != 0; p++) {
+ hc <<= 4;
+ hc += *p;
+ if ((g = (hc & 0xf0000000)) == 0) {
+ hc ^= g >> 24;
+ hc ^= g;
+ }
+ }
+ }
+ }
+ return hc;
+}
+
+// Tell compiler that a variable is intentionally unused.
+inline void unused(void *) { }
+
+symbol::symbol(const char *p, int how)
+{
+ if (p == 0) {
+ s = 0;
+ return;
+ }
+ if (*p == 0) {
+ s = "";
+ return;
+ }
+ if (table == 0) {
+ table_size = table_sizes[0];
+ table = (const char **)new char*[table_size];
+ for (int i = 0; i < table_size; i++)
+ table[i] = 0;
+ table_used = 0;
+ }
+ unsigned int hc = hash_string(p);
+ const char **pp;
+ for (pp = table + hc % table_size;
+ *pp != 0;
+ (pp == table ? pp = table + table_size - 1 : --pp))
+ if (strcmp(p, *pp) == 0) {
+ s = *pp;
+ return;
+ }
+ if (how == MUST_ALREADY_EXIST) {
+ s = 0;
+ return;
+ }
+ if (table_used >= table_size - 1 || table_used >= table_size*FULL_MAX) {
+ const char **old_table = table;
+ unsigned int old_table_size = table_size;
+ int i;
+ for (i = 1; table_sizes[i] <= old_table_size; i++)
+ if (table_sizes[i] == 0)
+ fatal("too many symbols");
+ table_size = table_sizes[i];
+ table_used = 0;
+ table = (const char **)new char*[table_size];
+ for (i = 0; i < table_size; i++)
+ table[i] = 0;
+ for (pp = old_table + old_table_size - 1;
+ pp >= old_table;
+ --pp) {
+ symbol temp(*pp, 1); /* insert it into the new table */
+ unused(&temp);
+ }
+ delete[] old_table;
+ for (pp = table + hc % table_size;
+ *pp != 0;
+ (pp == table ? pp = table + table_size - 1 : --pp))
+ ;
+ }
+ ++table_used;
+ if (how == DONT_STORE) {
+ s = *pp = p;
+ }
+ else {
+ int len = strlen(p)+1;
+ if (block == 0 || block_size < len) {
+ block_size = len > BLOCK_SIZE ? len : BLOCK_SIZE;
+ block = new char [block_size];
+ }
+ (void)strcpy(block, p);
+ s = *pp = block;
+ block += len;
+ block_size -= len;
+ }
+}
+
+symbol concat(symbol s1, symbol s2)
+{
+ char *buf = new char [strlen(s1.contents()) + strlen(s2.contents()) + 1];
+ strcpy(buf, s1.contents());
+ strcat(buf, s2.contents());
+ symbol res(buf);
+ delete[] buf;
+ return res;
+}
+
+symbol default_symbol("default");
diff --git a/src/libs/libgroff/tmpfile.cpp b/src/libs/libgroff/tmpfile.cpp
new file mode 100644
index 0000000..5e807ae
--- /dev/null
+++ b/src/libs/libgroff/tmpfile.cpp
@@ -0,0 +1,188 @@
+// -*- C++ -*-
+/* Copyright (C) 1989-2020 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 3 of the License, 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 this program. If not, see <http://www.gnu.org/licenses/>. */
+
+#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, on MS-DOS or MS-Windows ...
+#if defined(__MSDOS__) || defined(_WIN32)
+// if either of these is set, create temporary files there
+// (giving priority to WIN32_TMPDIR_ENVVAR)
+#define WIN32_TMPDIR_ENVVAR "TMP"
+#define MSDOS_TMPDIR_ENVVAR "TEMP"
+#endif
+// 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()
+{
+ // First, choose a location for creating temporary files...
+ const char *tem;
+ // using the first match for any of the environment specs in listed order.
+ if (
+ (tem = getenv(GROFF_TMPDIR_ENVVAR)) == 0
+ && (tem = getenv(TMPDIR_ENVVAR)) == 0
+#if defined(__MSDOS__) || defined(_WIN32)
+ // If we didn't find a match for either of the above
+ // (which are preferred, regardless of the host operating system),
+ // and we are hosted on either MS-Windows or MS-DOS,
+ // then try the Microsoft conventions.
+ && (tem = getenv(WIN32_TMPDIR_ENVVAR)) == 0
+ && (tem = getenv(MSDOS_TMPDIR_ENVVAR)) == 0
+#endif
+ )
+ // If we didn't find an environment spec fall back to this default.
+ 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) == 0) ? 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);
+ delete[] tem2;
+}
+
+temp_init::~temp_init()
+{
+ 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;
+ 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
+ delete[] templ;
+ return fp;
+}
diff --git a/src/libs/libgroff/tmpname.cpp b/src/libs/libgroff/tmpname.cpp
new file mode 100644
index 0000000..69dc9a4
--- /dev/null
+++ b/src/libs/libgroff/tmpname.cpp
@@ -0,0 +1,117 @@
+/* Copyright (C) 2001-2020 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 3 of the License, 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 this program. If not, see <http://www.gnu.org/licenses/>. */
+
+
+/* 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_INTTYPES_H
+# include <inttypes.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, 0);
+ uint64_t random_time_bits = ((uint64_t)tv.tv_usec << 16) ^ tv.tv_sec;
+#else
+ uint64_t random_time_bits = time(0);
+#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 | O_BINARY,
+ 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 */
+}
diff --git a/src/libs/libgroff/unicode.cpp b/src/libs/libgroff/unicode.cpp
new file mode 100644
index 0000000..29e80c7
--- /dev/null
+++ b/src/libs/libgroff/unicode.cpp
@@ -0,0 +1,65 @@
+// -*- C++ -*-
+/* Copyright (C) 2002-2020 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 3 of the License, 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 this program. If not, see <http://www.gnu.org/licenses/>. */
+
+#include "lib.h"
+#include "cset.h"
+#include "stringclass.h"
+
+#include "unicode.h"
+
+const char *check_unicode_name(const char *u)
+{
+ if (*u != 'u')
+ return 0;
+ const char *p = ++u;
+ for (;;) {
+ int val = 0;
+ const char *start = p;
+ for (;;) {
+ // only uppercase hex digits allowed
+ if (!csxdigit(*p))
+ return 0;
+ if (csdigit(*p))
+ val = val*0x10 + (*p-'0');
+ else if (csupper(*p))
+ val = val*0x10 + (*p-'A'+10);
+ else
+ return 0;
+ // biggest Unicode value is U+10FFFF
+ if (val > 0x10FFFF)
+ return 0;
+ p++;
+ if (*p == '\0' || *p == '_')
+ break;
+ }
+ // surrogates not allowed
+ if ((val >= 0xD800 && val <= 0xDBFF) || (val >= 0xDC00 && val <= 0xDFFF))
+ return 0;
+ if (val > 0xFFFF) {
+ if (*start == '0') // no leading zeros allowed if > 0xFFFF
+ return 0;
+ }
+ else if (p - start != 4) // otherwise, check for exactly 4 hex digits
+ return 0;
+ if (*p == '\0')
+ break;
+ p++;
+ }
+ return u;
+}
diff --git a/src/libs/libgroff/uniglyph.cpp b/src/libs/libgroff/uniglyph.cpp
new file mode 100644
index 0000000..bab2bc4
--- /dev/null
+++ b/src/libs/libgroff/uniglyph.cpp
@@ -0,0 +1,497 @@
+// -*- C++ -*-
+/* Copyright (C) 2002-2020 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 3 of the License, 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 this program. If not, see <http://www.gnu.org/licenses/>. */
+
+#include "lib.h"
+#include "stringclass.h"
+#include "ptable.h"
+
+#include "unicode.h"
+
+struct unicode_to_glyph {
+ char *value;
+};
+
+declare_ptable(unicode_to_glyph)
+implement_ptable(unicode_to_glyph)
+
+PTABLE(unicode_to_glyph) unicode_to_glyph_table;
+
+struct S {
+ const char *key;
+ const char *value;
+} unicode_to_glyph_list[] = {
+ { "0021", "!" },
+//{ "0022", "\"" },
+ { "0022", "dq" },
+//{ "0023", "#" },
+ { "0023", "sh" },
+//{ "0024", "$" },
+ { "0024", "Do" },
+ { "0025", "%" },
+ { "0026", "&" },
+ { "0027", "aq" },
+ { "0028", "(" },
+ { "0029", ")" },
+ { "002A", "*" },
+//{ "002B", "+" },
+ { "002B", "pl" },
+ { "002C", "," },
+ { "002E", "." },
+//{ "002F", "/" },
+ { "002F", "sl" },
+ { "0030", "0" },
+ { "0031", "1" },
+ { "0032", "2" },
+ { "0033", "3" },
+ { "0034", "4" },
+ { "0035", "5" },
+ { "0036", "6" },
+ { "0037", "7" },
+ { "0038", "8" },
+ { "0039", "9" },
+ { "003A", ":" },
+ { "003B", ";" },
+ { "003C", "<" },
+//{ "003D", "=" },
+ { "003D", "eq" },
+ { "003D_0338", "!=" },
+ { "003E", ">" },
+ { "003F", "?" },
+//{ "0040", "@" },
+ { "0040", "at" },
+ { "0041", "A" },
+ { "0041_0300", "`A" },
+ { "0041_0301", "'A" },
+ { "0041_0302", "^A" },
+ { "0041_0303", "~A" },
+ { "0041_0308", ":A" },
+ { "0041_030A", "oA" },
+ { "0042", "B" },
+ { "0043", "C" },
+ { "0043_0301", "'C" },
+ { "0043_0327", ",C" },
+ { "0044", "D" },
+ { "0045", "E" },
+ { "0045_0300", "`E" },
+ { "0045_0301", "'E" },
+ { "0045_0302", "^E" },
+ { "0045_0308", ":E" },
+ { "0046", "F" },
+ { "0047", "G" },
+ { "0048", "H" },
+ { "0049", "I" },
+ { "0049_0300", "`I" },
+ { "0049_0301", "'I" },
+ { "0049_0302", "^I" },
+ { "0049_0308", ":I" },
+ { "004A", "J" },
+ { "004B", "K" },
+ { "004C", "L" },
+ { "004D", "M" },
+ { "004E", "N" },
+ { "004E_0303", "~N" },
+ { "004F", "O" },
+ { "004F_0300", "`O" },
+ { "004F_0301", "'O" },
+ { "004F_0302", "^O" },
+ { "004F_0303", "~O" },
+ { "004F_0308", ":O" },
+ { "0050", "P" },
+ { "0051", "Q" },
+ { "0052", "R" },
+ { "0053", "S" },
+ { "0053_030C", "vS" },
+ { "0054", "T" },
+ { "0055", "U" },
+ { "0055_0300", "`U" },
+ { "0055_0301", "'U" },
+ { "0055_0302", "^U" },
+ { "0055_0308", ":U" },
+ { "0056", "V" },
+ { "0057", "W" },
+ { "0058", "X" },
+ { "0059", "Y" },
+ { "0059_0301", "'Y" },
+ { "0059_0308", ":Y" },
+ { "005A", "Z" },
+ { "005A_030C", "vZ" },
+ { "005B", "lB" },
+//{ "005B", "[" },
+ { "005C", "rs" },
+//{ "005C", "\\" },
+ { "005D", "rB" },
+//{ "005D", "]" },
+//{ "005E", "^" },
+//{ "005E", "a^" },
+ { "005E", "ha" },
+//{ "005F", "_" },
+//{ "005F", "ru" },
+ { "005F", "ul" },
+ { "0060", "ga" },
+ { "0061", "a" },
+ { "0061_0300", "`a" },
+ { "0061_0301", "'a" },
+ { "0061_0302", "^a" },
+ { "0061_0303", "~a" },
+ { "0061_0308", ":a" },
+ { "0061_030A", "oa" },
+ { "0062", "b" },
+ { "0063", "c" },
+ { "0063_0301", "'c" },
+ { "0063_0327", ",c" },
+ { "0064", "d" },
+ { "0065", "e" },
+ { "0065_0300", "`e" },
+ { "0065_0301", "'e" },
+ { "0065_0302", "^e" },
+ { "0065_0308", ":e" },
+ { "0066", "f" },
+ { "0066_0066", "ff" },
+ { "0066_0066_0069", "Fi" },
+ { "0066_0066_006C", "Fl" },
+ { "0066_0069", "fi" },
+ { "0066_006C", "fl" },
+ { "0067", "g" },
+ { "0068", "h" },
+ { "0069", "i" },
+ { "0069_0300", "`i" },
+ { "0069_0301", "'i" },
+ { "0069_0302", "^i" },
+ { "0069_0308", ":i" },
+ { "006A", "j" },
+ { "006B", "k" },
+ { "006C", "l" },
+ { "006D", "m" },
+ { "006E", "n" },
+ { "006E_0303", "~n" },
+ { "006F", "o" },
+ { "006F_0300", "`o" },
+ { "006F_0301", "'o" },
+ { "006F_0302", "^o" },
+ { "006F_0303", "~o" },
+ { "006F_0308", ":o" },
+ { "0070", "p" },
+ { "0071", "q" },
+ { "0072", "r" },
+ { "0073", "s" },
+ { "0073_030C", "vs" },
+ { "0074", "t" },
+ { "0075", "u" },
+ { "0075_0300", "`u" },
+ { "0075_0301", "'u" },
+ { "0075_0302", "^u" },
+ { "0075_0308", ":u" },
+ { "0076", "v" },
+ { "0077", "w" },
+ { "0078", "x" },
+ { "0079", "y" },
+ { "0079_0301", "'y" },
+ { "0079_0308", ":y" },
+ { "007A", "z" },
+ { "007A_030C", "vz" },
+ { "007B", "lC" },
+//{ "007B", "{" },
+ { "007C", "ba" },
+//{ "007C", "or" },
+//{ "007C", "|" },
+ { "007D", "rC" },
+//{ "007D", "}" },
+//{ "007E", "a~" },
+ { "007E", "ti" },
+//{ "007E", "~" },
+ { "00A1", "r!" },
+ { "00A2", "ct" },
+ { "00A3", "Po" },
+ { "00A4", "Cs" },
+ { "00A5", "Ye" },
+ { "00A6", "bb" },
+ { "00A7", "sc" },
+ { "00A8", "ad" },
+ { "00A9", "co" },
+ { "00AA", "Of" },
+ { "00AB", "Fo" },
+ { "00AC", "no" },
+//{ "00AC", "tno" },
+ // In groff, U+00AD is an input character only; it is not mapped to
+ // a glyph but to '\%'.
+ { "00AE", "rg" },
+ { "00AF", "a-" },
+ { "00B0", "de" },
+ { "00B1", "+-" },
+//{ "00B1", "t+-" },
+ { "00B2", "S2" },
+ { "00B3", "S3" },
+ { "00B4", "aa" },
+ { "00B5", "mc" },
+ { "00B6", "ps" },
+ { "00B7", "pc" },
+ { "00B8", "ac" },
+ { "00B9", "S1" },
+ { "00BA", "Om" },
+ { "00BB", "Fc" },
+ { "00BC", "14" },
+ { "00BD", "12" },
+ { "00BE", "34" },
+ { "00BF", "r?" },
+ { "00C6", "AE" },
+ { "00D0", "-D" },
+ { "00D7", "mu" },
+//{ "00D7", "tmu" },
+ { "00D8", "/O" },
+ { "00DE", "TP" },
+ { "00DF", "ss" },
+ { "00E6", "ae" },
+ { "00F0", "Sd" },
+ { "00F7", "di" },
+//{ "00F7", "tdi" },
+ { "00F8", "/o" },
+ { "00FE", "Tp" },
+ { "0131", ".i" },
+ { "0132", "IJ" },
+ { "0133", "ij" },
+ { "0141", "/L" },
+ { "0142", "/l" },
+ { "0152", "OE" },
+ { "0153", "oe" },
+ { "0192", "Fn" },
+ { "0237", ".j" },
+ { "02C7", "ah" },
+ { "02D8", "ab" },
+ { "02D9", "a." },
+ { "02DA", "ao" },
+ { "02DB", "ho" },
+ { "02DD", "a\"" },
+ { "0391", "*A" },
+ { "0392", "*B" },
+ { "0393", "*G" },
+ { "0394", "*D" },
+ { "0395", "*E" },
+ { "0396", "*Z" },
+ { "0397", "*Y" },
+ { "0398", "*H" },
+ { "0399", "*I" },
+ { "039A", "*K" },
+ { "039B", "*L" },
+ { "039C", "*M" },
+ { "039D", "*N" },
+ { "039E", "*C" },
+ { "039F", "*O" },
+ { "03A0", "*P" },
+ { "03A1", "*R" },
+ { "03A3", "*S" },
+ { "03A4", "*T" },
+ { "03A5", "*U" },
+ { "03A6", "*F" },
+ { "03A7", "*X" },
+ { "03A8", "*Q" },
+ { "03A9", "*W" },
+ { "03B1", "*a" },
+ { "03B2", "*b" },
+ { "03B3", "*g" },
+ { "03B4", "*d" },
+ { "03B5", "*e" },
+ { "03B6", "*z" },
+ { "03B7", "*y" },
+ { "03B8", "*h" },
+ { "03B9", "*i" },
+ { "03BA", "*k" },
+ { "03BB", "*l" },
+ { "03BC", "*m" },
+ { "03BD", "*n" },
+ { "03BE", "*c" },
+ { "03BF", "*o" },
+ { "03C0", "*p" },
+ { "03C1", "*r" },
+ { "03C2", "ts" },
+ { "03C3", "*s" },
+ { "03C4", "*t" },
+ { "03C5", "*u" },
+ { "03C6", "+f" },
+ { "03C7", "*x" },
+ { "03C8", "*q" },
+ { "03C9", "*w" },
+ { "03D1", "+h" },
+ { "03D5", "*f" },
+ { "03D6", "+p" },
+ { "03F5", "+e" },
+//{ "2010", "-" },
+ { "2010", "hy" },
+ { "2013", "en" },
+ { "2014", "em" },
+//{ "2018", "`" },
+ { "2018", "oq" },
+//{ "2019", "'" },
+ { "2019", "cq" },
+ { "201A", "bq" },
+ { "201C", "lq" },
+ { "201D", "rq" },
+ { "201E", "Bq" },
+ { "2020", "dg" },
+ { "2021", "dd" },
+ { "2022", "bu" },
+ { "2030", "%0" },
+ { "2032", "fm" },
+ { "2033", "sd" },
+ { "2039", "fo" },
+ { "203A", "fc" },
+ { "203E", "rn" },
+ { "2044", "f/" },
+ { "20AC", "Eu" },
+//{ "20AC", "eu" },
+ { "210F", "-h" },
+//{ "210F", "hbar" },
+ { "2111", "Im" },
+ { "2118", "wp" },
+ { "211C", "Re" },
+ { "2122", "tm" },
+ { "2135", "Ah" },
+ { "215B", "18" },
+ { "215C", "38" },
+ { "215D", "58" },
+ { "215E", "78" },
+ { "2190", "<-" },
+ { "2191", "ua" },
+ { "2192", "->" },
+ { "2193", "da" },
+ { "2194", "<>" },
+ { "2195", "va" },
+ { "21B5", "CR" },
+ { "21D0", "lA" },
+ { "21D1", "uA" },
+ { "21D2", "rA" },
+ { "21D3", "dA" },
+ { "21D4", "hA" },
+ { "21D5", "vA" },
+ { "2200", "fa" },
+ { "2202", "pd" },
+ { "2203", "te" },
+ { "2205", "es" },
+ { "2207", "gr" },
+ { "2208", "mo" },
+ { "2208_0338", "nm" },
+ { "220B", "st" },
+ { "220F", "product" },
+ { "2210", "coproduct" },
+ { "2211", "sum" },
+ { "2212", "mi" },
+//{ "2212", "\\-" },
+ { "2213", "-+" },
+ { "2217", "**" },
+ { "221A", "sr" },
+ { "221D", "pt" },
+ { "221E", "if" },
+ { "2220", "/_" },
+ { "2227", "AN" },
+ { "2228", "OR" },
+ { "2229", "ca" },
+ { "222A", "cu" },
+ { "222B", "is" },
+//{ "222B", "integral" },
+//{ "2234", "3d" },
+ { "2234", "tf" },
+ { "223C", "ap" },
+ { "2243", "|=" },
+ { "2245", "=~" },
+//{ "2248", "~=" },
+ { "2248", "~~" },
+ { "2261", "==" },
+ { "2261_0338", "ne" },
+ { "2264", "<=" },
+ { "2265", ">=" },
+ { "226A", ">>" },
+ { "226B", "<<" },
+ { "2282", "sb" },
+ { "2282_0338", "nb" },
+ { "2283", "sp" },
+ { "2283_0338", "nc" },
+ { "2286", "ib" },
+ { "2287", "ip" },
+ { "2295", "c+" },
+ { "2297", "c*" },
+ { "22A5", "pp" },
+ { "22C5", "md" },
+ { "2308", "lc" },
+ { "2309", "rc" },
+ { "230A", "lf" },
+ { "230B", "rf" },
+ { "239B", "parenlefttp" },
+ { "239C", "parenleftex" },
+ { "239D", "parenleftbt" },
+ { "239E", "parenrighttp" },
+ { "239F", "parenrightex" },
+ { "23A0", "parenrightbt" },
+//{ "23A1", "bracketlefttp" },
+ { "23A2", "bracketleftex" },
+//{ "23A3", "bracketleftbt" },
+//{ "23A4", "bracketrighttp" },
+ { "23A5", "bracketrightex" },
+//{ "23A6", "bracketrightbt" },
+ { "23A7", "lt" },
+//{ "23A7", "bracelefttp" },
+ { "23A8", "lk" },
+//{ "23A8", "braceleftmid" },
+ { "23A9", "lb" },
+//{ "23A9", "braceleftbt" },
+ { "23AA", "bv" },
+//{ "23AA", "braceex" },
+//{ "23AA", "braceleftex" },
+//{ "23AA", "bracerightex" },
+ { "23AB", "rt" },
+//{ "23AB", "bracerighttp" },
+ { "23AC", "rk" },
+//{ "23AC", "bracerightmid" },
+ { "23AD", "rb" },
+//{ "23AD", "bracerightbt" },
+ { "23AF", "an" },
+ { "2502", "br" },
+ { "25A1", "sq" },
+ { "25CA", "lz" },
+ { "25CB", "ci" },
+ { "261C", "lh" },
+ { "261E", "rh" },
+ { "2660", "SP" },
+ { "2663", "CL" },
+ { "2665", "HE" },
+ { "2666", "DI" },
+ { "2713", "OK" },
+ { "27E8", "la" },
+ { "27E9", "ra" },
+};
+
+// global constructor
+static struct unicode_to_glyph_init {
+ unicode_to_glyph_init();
+} _unicode_to_glyph_init;
+
+unicode_to_glyph_init::unicode_to_glyph_init()
+{
+ for (unsigned int i = 0;
+ i < sizeof(unicode_to_glyph_list)/sizeof(unicode_to_glyph_list[0]);
+ i++) {
+ unicode_to_glyph *utg = new unicode_to_glyph[1];
+ utg->value = (char *)unicode_to_glyph_list[i].value;
+ unicode_to_glyph_table.define(unicode_to_glyph_list[i].key, utg);
+ }
+}
+
+const char *unicode_to_glyph_name(const char *s)
+{
+ unicode_to_glyph *result = unicode_to_glyph_table.lookup(s);
+ return result ? result->value : 0;
+}
diff --git a/src/libs/libgroff/uniuni.cpp b/src/libs/libgroff/uniuni.cpp
new file mode 100644
index 0000000..e3b2708
--- /dev/null
+++ b/src/libs/libgroff/uniuni.cpp
@@ -0,0 +1,2128 @@
+// -*- C++ -*-
+/* Copyright (C) 2002-2020 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 3 of the License, 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 this program. If not, see <http://www.gnu.org/licenses/>. */
+
+// This code has been algorithmically derived from the file
+// UnicodeData.txt, version 7.0.0, available from unicode.org,
+// on 2014-12-16.
+
+#include "lib.h"
+#include "stringclass.h"
+#include "ptable.h"
+
+#include "unicode.h"
+
+struct unicode_decompose {
+ char *value;
+};
+
+declare_ptable(unicode_decompose)
+implement_ptable(unicode_decompose)
+
+PTABLE(unicode_decompose) unicode_decompose_table;
+
+// the first digit in the composite string gives the number of composites
+
+struct S {
+ const char *key;
+ const char *value;
+} unicode_decompose_list[] = {
+ { "00C0", "20041_0300" },
+ { "00C1", "20041_0301" },
+ { "00C2", "20041_0302" },
+ { "00C3", "20041_0303" },
+ { "00C4", "20041_0308" },
+ { "00C5", "20041_030A" },
+ { "00C7", "20043_0327" },
+ { "00C8", "20045_0300" },
+ { "00C9", "20045_0301" },
+ { "00CA", "20045_0302" },
+ { "00CB", "20045_0308" },
+ { "00CC", "20049_0300" },
+ { "00CD", "20049_0301" },
+ { "00CE", "20049_0302" },
+ { "00CF", "20049_0308" },
+ { "00D1", "2004E_0303" },
+ { "00D2", "2004F_0300" },
+ { "00D3", "2004F_0301" },
+ { "00D4", "2004F_0302" },
+ { "00D5", "2004F_0303" },
+ { "00D6", "2004F_0308" },
+ { "00D9", "20055_0300" },
+ { "00DA", "20055_0301" },
+ { "00DB", "20055_0302" },
+ { "00DC", "20055_0308" },
+ { "00DD", "20059_0301" },
+ { "00E0", "20061_0300" },
+ { "00E1", "20061_0301" },
+ { "00E2", "20061_0302" },
+ { "00E3", "20061_0303" },
+ { "00E4", "20061_0308" },
+ { "00E5", "20061_030A" },
+ { "00E7", "20063_0327" },
+ { "00E8", "20065_0300" },
+ { "00E9", "20065_0301" },
+ { "00EA", "20065_0302" },
+ { "00EB", "20065_0308" },
+ { "00EC", "20069_0300" },
+ { "00ED", "20069_0301" },
+ { "00EE", "20069_0302" },
+ { "00EF", "20069_0308" },
+ { "00F1", "2006E_0303" },
+ { "00F2", "2006F_0300" },
+ { "00F3", "2006F_0301" },
+ { "00F4", "2006F_0302" },
+ { "00F5", "2006F_0303" },
+ { "00F6", "2006F_0308" },
+ { "00F9", "20075_0300" },
+ { "00FA", "20075_0301" },
+ { "00FB", "20075_0302" },
+ { "00FC", "20075_0308" },
+ { "00FD", "20079_0301" },
+ { "00FF", "20079_0308" },
+ { "0100", "20041_0304" },
+ { "0101", "20061_0304" },
+ { "0102", "20041_0306" },
+ { "0103", "20061_0306" },
+ { "0104", "20041_0328" },
+ { "0105", "20061_0328" },
+ { "0106", "20043_0301" },
+ { "0107", "20063_0301" },
+ { "0108", "20043_0302" },
+ { "0109", "20063_0302" },
+ { "010A", "20043_0307" },
+ { "010B", "20063_0307" },
+ { "010C", "20043_030C" },
+ { "010D", "20063_030C" },
+ { "010E", "20044_030C" },
+ { "010F", "20064_030C" },
+ { "0112", "20045_0304" },
+ { "0113", "20065_0304" },
+ { "0114", "20045_0306" },
+ { "0115", "20065_0306" },
+ { "0116", "20045_0307" },
+ { "0117", "20065_0307" },
+ { "0118", "20045_0328" },
+ { "0119", "20065_0328" },
+ { "011A", "20045_030C" },
+ { "011B", "20065_030C" },
+ { "011C", "20047_0302" },
+ { "011D", "20067_0302" },
+ { "011E", "20047_0306" },
+ { "011F", "20067_0306" },
+ { "0120", "20047_0307" },
+ { "0121", "20067_0307" },
+ { "0122", "20047_0327" },
+ { "0123", "20067_0327" },
+ { "0124", "20048_0302" },
+ { "0125", "20068_0302" },
+ { "0128", "20049_0303" },
+ { "0129", "20069_0303" },
+ { "012A", "20049_0304" },
+ { "012B", "20069_0304" },
+ { "012C", "20049_0306" },
+ { "012D", "20069_0306" },
+ { "012E", "20049_0328" },
+ { "012F", "20069_0328" },
+ { "0130", "20049_0307" },
+ { "0134", "2004A_0302" },
+ { "0135", "2006A_0302" },
+ { "0136", "2004B_0327" },
+ { "0137", "2006B_0327" },
+ { "0139", "2004C_0301" },
+ { "013A", "2006C_0301" },
+ { "013B", "2004C_0327" },
+ { "013C", "2006C_0327" },
+ { "013D", "2004C_030C" },
+ { "013E", "2006C_030C" },
+ { "0143", "2004E_0301" },
+ { "0144", "2006E_0301" },
+ { "0145", "2004E_0327" },
+ { "0146", "2006E_0327" },
+ { "0147", "2004E_030C" },
+ { "0148", "2006E_030C" },
+ { "014C", "2004F_0304" },
+ { "014D", "2006F_0304" },
+ { "014E", "2004F_0306" },
+ { "014F", "2006F_0306" },
+ { "0150", "2004F_030B" },
+ { "0151", "2006F_030B" },
+ { "0154", "20052_0301" },
+ { "0155", "20072_0301" },
+ { "0156", "20052_0327" },
+ { "0157", "20072_0327" },
+ { "0158", "20052_030C" },
+ { "0159", "20072_030C" },
+ { "015A", "20053_0301" },
+ { "015B", "20073_0301" },
+ { "015C", "20053_0302" },
+ { "015D", "20073_0302" },
+ { "015E", "20053_0327" },
+ { "015F", "20073_0327" },
+ { "0160", "20053_030C" },
+ { "0161", "20073_030C" },
+ { "0162", "20054_0327" },
+ { "0163", "20074_0327" },
+ { "0164", "20054_030C" },
+ { "0165", "20074_030C" },
+ { "0168", "20055_0303" },
+ { "0169", "20075_0303" },
+ { "016A", "20055_0304" },
+ { "016B", "20075_0304" },
+ { "016C", "20055_0306" },
+ { "016D", "20075_0306" },
+ { "016E", "20055_030A" },
+ { "016F", "20075_030A" },
+ { "0170", "20055_030B" },
+ { "0171", "20075_030B" },
+ { "0172", "20055_0328" },
+ { "0173", "20075_0328" },
+ { "0174", "20057_0302" },
+ { "0175", "20077_0302" },
+ { "0176", "20059_0302" },
+ { "0177", "20079_0302" },
+ { "0178", "20059_0308" },
+ { "0179", "2005A_0301" },
+ { "017A", "2007A_0301" },
+ { "017B", "2005A_0307" },
+ { "017C", "2007A_0307" },
+ { "017D", "2005A_030C" },
+ { "017E", "2007A_030C" },
+ { "01A0", "2004F_031B" },
+ { "01A1", "2006F_031B" },
+ { "01AF", "20055_031B" },
+ { "01B0", "20075_031B" },
+ { "01CD", "20041_030C" },
+ { "01CE", "20061_030C" },
+ { "01CF", "20049_030C" },
+ { "01D0", "20069_030C" },
+ { "01D1", "2004F_030C" },
+ { "01D2", "2006F_030C" },
+ { "01D3", "20055_030C" },
+ { "01D4", "20075_030C" },
+ { "01D5", "30055_0308_0304" },
+ { "01D6", "30075_0308_0304" },
+ { "01D7", "30055_0308_0301" },
+ { "01D8", "30075_0308_0301" },
+ { "01D9", "30055_0308_030C" },
+ { "01DA", "30075_0308_030C" },
+ { "01DB", "30055_0308_0300" },
+ { "01DC", "30075_0308_0300" },
+ { "01DE", "30041_0308_0304" },
+ { "01DF", "30061_0308_0304" },
+ { "01E0", "30041_0307_0304" },
+ { "01E1", "30061_0307_0304" },
+ { "01E2", "200C6_0304" },
+ { "01E3", "200E6_0304" },
+ { "01E6", "20047_030C" },
+ { "01E7", "20067_030C" },
+ { "01E8", "2004B_030C" },
+ { "01E9", "2006B_030C" },
+ { "01EA", "2004F_0328" },
+ { "01EB", "2006F_0328" },
+ { "01EC", "3004F_0328_0304" },
+ { "01ED", "3006F_0328_0304" },
+ { "01EE", "201B7_030C" },
+ { "01EF", "20292_030C" },
+ { "01F0", "2006A_030C" },
+ { "01F4", "20047_0301" },
+ { "01F5", "20067_0301" },
+ { "01F8", "2004E_0300" },
+ { "01F9", "2006E_0300" },
+ { "01FA", "30041_030A_0301" },
+ { "01FB", "30061_030A_0301" },
+ { "01FC", "200C6_0301" },
+ { "01FD", "200E6_0301" },
+ { "01FE", "200D8_0301" },
+ { "01FF", "200F8_0301" },
+ { "0200", "20041_030F" },
+ { "0201", "20061_030F" },
+ { "0202", "20041_0311" },
+ { "0203", "20061_0311" },
+ { "0204", "20045_030F" },
+ { "0205", "20065_030F" },
+ { "0206", "20045_0311" },
+ { "0207", "20065_0311" },
+ { "0208", "20049_030F" },
+ { "0209", "20069_030F" },
+ { "020A", "20049_0311" },
+ { "020B", "20069_0311" },
+ { "020C", "2004F_030F" },
+ { "020D", "2006F_030F" },
+ { "020E", "2004F_0311" },
+ { "020F", "2006F_0311" },
+ { "0210", "20052_030F" },
+ { "0211", "20072_030F" },
+ { "0212", "20052_0311" },
+ { "0213", "20072_0311" },
+ { "0214", "20055_030F" },
+ { "0215", "20075_030F" },
+ { "0216", "20055_0311" },
+ { "0217", "20075_0311" },
+ { "0218", "20053_0326" },
+ { "0219", "20073_0326" },
+ { "021A", "20054_0326" },
+ { "021B", "20074_0326" },
+ { "021E", "20048_030C" },
+ { "021F", "20068_030C" },
+ { "0226", "20041_0307" },
+ { "0227", "20061_0307" },
+ { "0228", "20045_0327" },
+ { "0229", "20065_0327" },
+ { "022A", "3004F_0308_0304" },
+ { "022B", "3006F_0308_0304" },
+ { "022C", "3004F_0303_0304" },
+ { "022D", "3006F_0303_0304" },
+ { "022E", "2004F_0307" },
+ { "022F", "2006F_0307" },
+ { "0230", "3004F_0307_0304" },
+ { "0231", "3006F_0307_0304" },
+ { "0232", "20059_0304" },
+ { "0233", "20079_0304" },
+ { "0340", "10300" },
+ { "0341", "10301" },
+ { "0343", "10313" },
+ { "0344", "20308_0301" },
+ { "0374", "102B9" },
+ { "037E", "1003B" },
+ { "0385", "200A8_0301" },
+ { "0386", "20391_0301" },
+ { "0387", "100B7" },
+ { "0388", "20395_0301" },
+ { "0389", "20397_0301" },
+ { "038A", "20399_0301" },
+ { "038C", "2039F_0301" },
+ { "038E", "203A5_0301" },
+ { "038F", "203A9_0301" },
+ { "0390", "303B9_0308_0301" },
+ { "03AA", "20399_0308" },
+ { "03AB", "203A5_0308" },
+ { "03AC", "203B1_0301" },
+ { "03AD", "203B5_0301" },
+ { "03AE", "203B7_0301" },
+ { "03AF", "203B9_0301" },
+ { "03B0", "303C5_0308_0301" },
+ { "03CA", "203B9_0308" },
+ { "03CB", "203C5_0308" },
+ { "03CC", "203BF_0301" },
+ { "03CD", "203C5_0301" },
+ { "03CE", "203C9_0301" },
+ { "03D3", "203D2_0301" },
+ { "03D4", "203D2_0308" },
+ { "0400", "20415_0300" },
+ { "0401", "20415_0308" },
+ { "0403", "20413_0301" },
+ { "0407", "20406_0308" },
+ { "040C", "2041A_0301" },
+ { "040D", "20418_0300" },
+ { "040E", "20423_0306" },
+ { "0419", "20418_0306" },
+ { "0439", "20438_0306" },
+ { "0450", "20435_0300" },
+ { "0451", "20435_0308" },
+ { "0453", "20433_0301" },
+ { "0457", "20456_0308" },
+ { "045C", "2043A_0301" },
+ { "045D", "20438_0300" },
+ { "045E", "20443_0306" },
+ { "0476", "20474_030F" },
+ { "0477", "20475_030F" },
+ { "04C1", "20416_0306" },
+ { "04C2", "20436_0306" },
+ { "04D0", "20410_0306" },
+ { "04D1", "20430_0306" },
+ { "04D2", "20410_0308" },
+ { "04D3", "20430_0308" },
+ { "04D6", "20415_0306" },
+ { "04D7", "20435_0306" },
+ { "04DA", "204D8_0308" },
+ { "04DB", "204D9_0308" },
+ { "04DC", "20416_0308" },
+ { "04DD", "20436_0308" },
+ { "04DE", "20417_0308" },
+ { "04DF", "20437_0308" },
+ { "04E2", "20418_0304" },
+ { "04E3", "20438_0304" },
+ { "04E4", "20418_0308" },
+ { "04E5", "20438_0308" },
+ { "04E6", "2041E_0308" },
+ { "04E7", "2043E_0308" },
+ { "04EA", "204E8_0308" },
+ { "04EB", "204E9_0308" },
+ { "04EC", "2042D_0308" },
+ { "04ED", "2044D_0308" },
+ { "04EE", "20423_0304" },
+ { "04EF", "20443_0304" },
+ { "04F0", "20423_0308" },
+ { "04F1", "20443_0308" },
+ { "04F2", "20423_030B" },
+ { "04F3", "20443_030B" },
+ { "04F4", "20427_0308" },
+ { "04F5", "20447_0308" },
+ { "04F8", "2042B_0308" },
+ { "04F9", "2044B_0308" },
+ { "0622", "20627_0653" },
+ { "0623", "20627_0654" },
+ { "0624", "20648_0654" },
+ { "0625", "20627_0655" },
+ { "0626", "2064A_0654" },
+ { "06C0", "206D5_0654" },
+ { "06C2", "206C1_0654" },
+ { "06D3", "206D2_0654" },
+ { "0929", "20928_093C" },
+ { "0931", "20930_093C" },
+ { "0934", "20933_093C" },
+ { "0958", "20915_093C" },
+ { "0959", "20916_093C" },
+ { "095A", "20917_093C" },
+ { "095B", "2091C_093C" },
+ { "095C", "20921_093C" },
+ { "095D", "20922_093C" },
+ { "095E", "2092B_093C" },
+ { "095F", "2092F_093C" },
+ { "09CB", "209C7_09BE" },
+ { "09CC", "209C7_09D7" },
+ { "09DC", "209A1_09BC" },
+ { "09DD", "209A2_09BC" },
+ { "09DF", "209AF_09BC" },
+ { "0A33", "20A32_0A3C" },
+ { "0A36", "20A38_0A3C" },
+ { "0A59", "20A16_0A3C" },
+ { "0A5A", "20A17_0A3C" },
+ { "0A5B", "20A1C_0A3C" },
+ { "0A5E", "20A2B_0A3C" },
+ { "0B48", "20B47_0B56" },
+ { "0B4B", "20B47_0B3E" },
+ { "0B4C", "20B47_0B57" },
+ { "0B5C", "20B21_0B3C" },
+ { "0B5D", "20B22_0B3C" },
+ { "0B94", "20B92_0BD7" },
+ { "0BCA", "20BC6_0BBE" },
+ { "0BCB", "20BC7_0BBE" },
+ { "0BCC", "20BC6_0BD7" },
+ { "0C48", "20C46_0C56" },
+ { "0CC0", "20CBF_0CD5" },
+ { "0CC7", "20CC6_0CD5" },
+ { "0CC8", "20CC6_0CD6" },
+ { "0CCA", "20CC6_0CC2" },
+ { "0CCB", "30CC6_0CC2_0CD5" },
+ { "0D4A", "20D46_0D3E" },
+ { "0D4B", "20D47_0D3E" },
+ { "0D4C", "20D46_0D57" },
+ { "0DDA", "20DD9_0DCA" },
+ { "0DDC", "20DD9_0DCF" },
+ { "0DDD", "30DD9_0DCF_0DCA" },
+ { "0DDE", "20DD9_0DDF" },
+ { "0F43", "20F42_0FB7" },
+ { "0F4D", "20F4C_0FB7" },
+ { "0F52", "20F51_0FB7" },
+ { "0F57", "20F56_0FB7" },
+ { "0F5C", "20F5B_0FB7" },
+ { "0F69", "20F40_0FB5" },
+ { "0F73", "20F71_0F72" },
+ { "0F75", "20F71_0F74" },
+ { "0F76", "20FB2_0F80" },
+ { "0F78", "20FB3_0F80" },
+ { "0F81", "20F71_0F80" },
+ { "0F93", "20F92_0FB7" },
+ { "0F9D", "20F9C_0FB7" },
+ { "0FA2", "20FA1_0FB7" },
+ { "0FA7", "20FA6_0FB7" },
+ { "0FAC", "20FAB_0FB7" },
+ { "0FB9", "20F90_0FB5" },
+ { "1026", "21025_102E" },
+ { "1B06", "21B05_1B35" },
+ { "1B08", "21B07_1B35" },
+ { "1B0A", "21B09_1B35" },
+ { "1B0C", "21B0B_1B35" },
+ { "1B0E", "21B0D_1B35" },
+ { "1B12", "21B11_1B35" },
+ { "1B3B", "21B3A_1B35" },
+ { "1B3D", "21B3C_1B35" },
+ { "1B40", "21B3E_1B35" },
+ { "1B41", "21B3F_1B35" },
+ { "1B43", "21B42_1B35" },
+ { "1E00", "20041_0325" },
+ { "1E01", "20061_0325" },
+ { "1E02", "20042_0307" },
+ { "1E03", "20062_0307" },
+ { "1E04", "20042_0323" },
+ { "1E05", "20062_0323" },
+ { "1E06", "20042_0331" },
+ { "1E07", "20062_0331" },
+ { "1E08", "30043_0327_0301" },
+ { "1E09", "30063_0327_0301" },
+ { "1E0A", "20044_0307" },
+ { "1E0B", "20064_0307" },
+ { "1E0C", "20044_0323" },
+ { "1E0D", "20064_0323" },
+ { "1E0E", "20044_0331" },
+ { "1E0F", "20064_0331" },
+ { "1E10", "20044_0327" },
+ { "1E11", "20064_0327" },
+ { "1E12", "20044_032D" },
+ { "1E13", "20064_032D" },
+ { "1E14", "30045_0304_0300" },
+ { "1E15", "30065_0304_0300" },
+ { "1E16", "30045_0304_0301" },
+ { "1E17", "30065_0304_0301" },
+ { "1E18", "20045_032D" },
+ { "1E19", "20065_032D" },
+ { "1E1A", "20045_0330" },
+ { "1E1B", "20065_0330" },
+ { "1E1C", "30045_0327_0306" },
+ { "1E1D", "30065_0327_0306" },
+ { "1E1E", "20046_0307" },
+ { "1E1F", "20066_0307" },
+ { "1E20", "20047_0304" },
+ { "1E21", "20067_0304" },
+ { "1E22", "20048_0307" },
+ { "1E23", "20068_0307" },
+ { "1E24", "20048_0323" },
+ { "1E25", "20068_0323" },
+ { "1E26", "20048_0308" },
+ { "1E27", "20068_0308" },
+ { "1E28", "20048_0327" },
+ { "1E29", "20068_0327" },
+ { "1E2A", "20048_032E" },
+ { "1E2B", "20068_032E" },
+ { "1E2C", "20049_0330" },
+ { "1E2D", "20069_0330" },
+ { "1E2E", "30049_0308_0301" },
+ { "1E2F", "30069_0308_0301" },
+ { "1E30", "2004B_0301" },
+ { "1E31", "2006B_0301" },
+ { "1E32", "2004B_0323" },
+ { "1E33", "2006B_0323" },
+ { "1E34", "2004B_0331" },
+ { "1E35", "2006B_0331" },
+ { "1E36", "2004C_0323" },
+ { "1E37", "2006C_0323" },
+ { "1E38", "3004C_0323_0304" },
+ { "1E39", "3006C_0323_0304" },
+ { "1E3A", "2004C_0331" },
+ { "1E3B", "2006C_0331" },
+ { "1E3C", "2004C_032D" },
+ { "1E3D", "2006C_032D" },
+ { "1E3E", "2004D_0301" },
+ { "1E3F", "2006D_0301" },
+ { "1E40", "2004D_0307" },
+ { "1E41", "2006D_0307" },
+ { "1E42", "2004D_0323" },
+ { "1E43", "2006D_0323" },
+ { "1E44", "2004E_0307" },
+ { "1E45", "2006E_0307" },
+ { "1E46", "2004E_0323" },
+ { "1E47", "2006E_0323" },
+ { "1E48", "2004E_0331" },
+ { "1E49", "2006E_0331" },
+ { "1E4A", "2004E_032D" },
+ { "1E4B", "2006E_032D" },
+ { "1E4C", "3004F_0303_0301" },
+ { "1E4D", "3006F_0303_0301" },
+ { "1E4E", "3004F_0303_0308" },
+ { "1E4F", "3006F_0303_0308" },
+ { "1E50", "3004F_0304_0300" },
+ { "1E51", "3006F_0304_0300" },
+ { "1E52", "3004F_0304_0301" },
+ { "1E53", "3006F_0304_0301" },
+ { "1E54", "20050_0301" },
+ { "1E55", "20070_0301" },
+ { "1E56", "20050_0307" },
+ { "1E57", "20070_0307" },
+ { "1E58", "20052_0307" },
+ { "1E59", "20072_0307" },
+ { "1E5A", "20052_0323" },
+ { "1E5B", "20072_0323" },
+ { "1E5C", "30052_0323_0304" },
+ { "1E5D", "30072_0323_0304" },
+ { "1E5E", "20052_0331" },
+ { "1E5F", "20072_0331" },
+ { "1E60", "20053_0307" },
+ { "1E61", "20073_0307" },
+ { "1E62", "20053_0323" },
+ { "1E63", "20073_0323" },
+ { "1E64", "30053_0301_0307" },
+ { "1E65", "30073_0301_0307" },
+ { "1E66", "30053_030C_0307" },
+ { "1E67", "30073_030C_0307" },
+ { "1E68", "30053_0323_0307" },
+ { "1E69", "30073_0323_0307" },
+ { "1E6A", "20054_0307" },
+ { "1E6B", "20074_0307" },
+ { "1E6C", "20054_0323" },
+ { "1E6D", "20074_0323" },
+ { "1E6E", "20054_0331" },
+ { "1E6F", "20074_0331" },
+ { "1E70", "20054_032D" },
+ { "1E71", "20074_032D" },
+ { "1E72", "20055_0324" },
+ { "1E73", "20075_0324" },
+ { "1E74", "20055_0330" },
+ { "1E75", "20075_0330" },
+ { "1E76", "20055_032D" },
+ { "1E77", "20075_032D" },
+ { "1E78", "30055_0303_0301" },
+ { "1E79", "30075_0303_0301" },
+ { "1E7A", "30055_0304_0308" },
+ { "1E7B", "30075_0304_0308" },
+ { "1E7C", "20056_0303" },
+ { "1E7D", "20076_0303" },
+ { "1E7E", "20056_0323" },
+ { "1E7F", "20076_0323" },
+ { "1E80", "20057_0300" },
+ { "1E81", "20077_0300" },
+ { "1E82", "20057_0301" },
+ { "1E83", "20077_0301" },
+ { "1E84", "20057_0308" },
+ { "1E85", "20077_0308" },
+ { "1E86", "20057_0307" },
+ { "1E87", "20077_0307" },
+ { "1E88", "20057_0323" },
+ { "1E89", "20077_0323" },
+ { "1E8A", "20058_0307" },
+ { "1E8B", "20078_0307" },
+ { "1E8C", "20058_0308" },
+ { "1E8D", "20078_0308" },
+ { "1E8E", "20059_0307" },
+ { "1E8F", "20079_0307" },
+ { "1E90", "2005A_0302" },
+ { "1E91", "2007A_0302" },
+ { "1E92", "2005A_0323" },
+ { "1E93", "2007A_0323" },
+ { "1E94", "2005A_0331" },
+ { "1E95", "2007A_0331" },
+ { "1E96", "20068_0331" },
+ { "1E97", "20074_0308" },
+ { "1E98", "20077_030A" },
+ { "1E99", "20079_030A" },
+ { "1E9B", "2017F_0307" },
+ { "1EA0", "20041_0323" },
+ { "1EA1", "20061_0323" },
+ { "1EA2", "20041_0309" },
+ { "1EA3", "20061_0309" },
+ { "1EA4", "30041_0302_0301" },
+ { "1EA5", "30061_0302_0301" },
+ { "1EA6", "30041_0302_0300" },
+ { "1EA7", "30061_0302_0300" },
+ { "1EA8", "30041_0302_0309" },
+ { "1EA9", "30061_0302_0309" },
+ { "1EAA", "30041_0302_0303" },
+ { "1EAB", "30061_0302_0303" },
+ { "1EAC", "30041_0323_0302" },
+ { "1EAD", "30061_0323_0302" },
+ { "1EAE", "30041_0306_0301" },
+ { "1EAF", "30061_0306_0301" },
+ { "1EB0", "30041_0306_0300" },
+ { "1EB1", "30061_0306_0300" },
+ { "1EB2", "30041_0306_0309" },
+ { "1EB3", "30061_0306_0309" },
+ { "1EB4", "30041_0306_0303" },
+ { "1EB5", "30061_0306_0303" },
+ { "1EB6", "30041_0323_0306" },
+ { "1EB7", "30061_0323_0306" },
+ { "1EB8", "20045_0323" },
+ { "1EB9", "20065_0323" },
+ { "1EBA", "20045_0309" },
+ { "1EBB", "20065_0309" },
+ { "1EBC", "20045_0303" },
+ { "1EBD", "20065_0303" },
+ { "1EBE", "30045_0302_0301" },
+ { "1EBF", "30065_0302_0301" },
+ { "1EC0", "30045_0302_0300" },
+ { "1EC1", "30065_0302_0300" },
+ { "1EC2", "30045_0302_0309" },
+ { "1EC3", "30065_0302_0309" },
+ { "1EC4", "30045_0302_0303" },
+ { "1EC5", "30065_0302_0303" },
+ { "1EC6", "30045_0323_0302" },
+ { "1EC7", "30065_0323_0302" },
+ { "1EC8", "20049_0309" },
+ { "1EC9", "20069_0309" },
+ { "1ECA", "20049_0323" },
+ { "1ECB", "20069_0323" },
+ { "1ECC", "2004F_0323" },
+ { "1ECD", "2006F_0323" },
+ { "1ECE", "2004F_0309" },
+ { "1ECF", "2006F_0309" },
+ { "1ED0", "3004F_0302_0301" },
+ { "1ED1", "3006F_0302_0301" },
+ { "1ED2", "3004F_0302_0300" },
+ { "1ED3", "3006F_0302_0300" },
+ { "1ED4", "3004F_0302_0309" },
+ { "1ED5", "3006F_0302_0309" },
+ { "1ED6", "3004F_0302_0303" },
+ { "1ED7", "3006F_0302_0303" },
+ { "1ED8", "3004F_0323_0302" },
+ { "1ED9", "3006F_0323_0302" },
+ { "1EDA", "3004F_031B_0301" },
+ { "1EDB", "3006F_031B_0301" },
+ { "1EDC", "3004F_031B_0300" },
+ { "1EDD", "3006F_031B_0300" },
+ { "1EDE", "3004F_031B_0309" },
+ { "1EDF", "3006F_031B_0309" },
+ { "1EE0", "3004F_031B_0303" },
+ { "1EE1", "3006F_031B_0303" },
+ { "1EE2", "3004F_031B_0323" },
+ { "1EE3", "3006F_031B_0323" },
+ { "1EE4", "20055_0323" },
+ { "1EE5", "20075_0323" },
+ { "1EE6", "20055_0309" },
+ { "1EE7", "20075_0309" },
+ { "1EE8", "30055_031B_0301" },
+ { "1EE9", "30075_031B_0301" },
+ { "1EEA", "30055_031B_0300" },
+ { "1EEB", "30075_031B_0300" },
+ { "1EEC", "30055_031B_0309" },
+ { "1EED", "30075_031B_0309" },
+ { "1EEE", "30055_031B_0303" },
+ { "1EEF", "30075_031B_0303" },
+ { "1EF0", "30055_031B_0323" },
+ { "1EF1", "30075_031B_0323" },
+ { "1EF2", "20059_0300" },
+ { "1EF3", "20079_0300" },
+ { "1EF4", "20059_0323" },
+ { "1EF5", "20079_0323" },
+ { "1EF6", "20059_0309" },
+ { "1EF7", "20079_0309" },
+ { "1EF8", "20059_0303" },
+ { "1EF9", "20079_0303" },
+ { "1F00", "203B1_0313" },
+ { "1F01", "203B1_0314" },
+ { "1F02", "303B1_0313_0300" },
+ { "1F03", "303B1_0314_0300" },
+ { "1F04", "303B1_0313_0301" },
+ { "1F05", "303B1_0314_0301" },
+ { "1F06", "303B1_0313_0342" },
+ { "1F07", "303B1_0314_0342" },
+ { "1F08", "20391_0313" },
+ { "1F09", "20391_0314" },
+ { "1F0A", "30391_0313_0300" },
+ { "1F0B", "30391_0314_0300" },
+ { "1F0C", "30391_0313_0301" },
+ { "1F0D", "30391_0314_0301" },
+ { "1F0E", "30391_0313_0342" },
+ { "1F0F", "30391_0314_0342" },
+ { "1F10", "203B5_0313" },
+ { "1F11", "203B5_0314" },
+ { "1F12", "303B5_0313_0300" },
+ { "1F13", "303B5_0314_0300" },
+ { "1F14", "303B5_0313_0301" },
+ { "1F15", "303B5_0314_0301" },
+ { "1F18", "20395_0313" },
+ { "1F19", "20395_0314" },
+ { "1F1A", "30395_0313_0300" },
+ { "1F1B", "30395_0314_0300" },
+ { "1F1C", "30395_0313_0301" },
+ { "1F1D", "30395_0314_0301" },
+ { "1F20", "203B7_0313" },
+ { "1F21", "203B7_0314" },
+ { "1F22", "303B7_0313_0300" },
+ { "1F23", "303B7_0314_0300" },
+ { "1F24", "303B7_0313_0301" },
+ { "1F25", "303B7_0314_0301" },
+ { "1F26", "303B7_0313_0342" },
+ { "1F27", "303B7_0314_0342" },
+ { "1F28", "20397_0313" },
+ { "1F29", "20397_0314" },
+ { "1F2A", "30397_0313_0300" },
+ { "1F2B", "30397_0314_0300" },
+ { "1F2C", "30397_0313_0301" },
+ { "1F2D", "30397_0314_0301" },
+ { "1F2E", "30397_0313_0342" },
+ { "1F2F", "30397_0314_0342" },
+ { "1F30", "203B9_0313" },
+ { "1F31", "203B9_0314" },
+ { "1F32", "303B9_0313_0300" },
+ { "1F33", "303B9_0314_0300" },
+ { "1F34", "303B9_0313_0301" },
+ { "1F35", "303B9_0314_0301" },
+ { "1F36", "303B9_0313_0342" },
+ { "1F37", "303B9_0314_0342" },
+ { "1F38", "20399_0313" },
+ { "1F39", "20399_0314" },
+ { "1F3A", "30399_0313_0300" },
+ { "1F3B", "30399_0314_0300" },
+ { "1F3C", "30399_0313_0301" },
+ { "1F3D", "30399_0314_0301" },
+ { "1F3E", "30399_0313_0342" },
+ { "1F3F", "30399_0314_0342" },
+ { "1F40", "203BF_0313" },
+ { "1F41", "203BF_0314" },
+ { "1F42", "303BF_0313_0300" },
+ { "1F43", "303BF_0314_0300" },
+ { "1F44", "303BF_0313_0301" },
+ { "1F45", "303BF_0314_0301" },
+ { "1F48", "2039F_0313" },
+ { "1F49", "2039F_0314" },
+ { "1F4A", "3039F_0313_0300" },
+ { "1F4B", "3039F_0314_0300" },
+ { "1F4C", "3039F_0313_0301" },
+ { "1F4D", "3039F_0314_0301" },
+ { "1F50", "203C5_0313" },
+ { "1F51", "203C5_0314" },
+ { "1F52", "303C5_0313_0300" },
+ { "1F53", "303C5_0314_0300" },
+ { "1F54", "303C5_0313_0301" },
+ { "1F55", "303C5_0314_0301" },
+ { "1F56", "303C5_0313_0342" },
+ { "1F57", "303C5_0314_0342" },
+ { "1F59", "203A5_0314" },
+ { "1F5B", "303A5_0314_0300" },
+ { "1F5D", "303A5_0314_0301" },
+ { "1F5F", "303A5_0314_0342" },
+ { "1F60", "203C9_0313" },
+ { "1F61", "203C9_0314" },
+ { "1F62", "303C9_0313_0300" },
+ { "1F63", "303C9_0314_0300" },
+ { "1F64", "303C9_0313_0301" },
+ { "1F65", "303C9_0314_0301" },
+ { "1F66", "303C9_0313_0342" },
+ { "1F67", "303C9_0314_0342" },
+ { "1F68", "203A9_0313" },
+ { "1F69", "203A9_0314" },
+ { "1F6A", "303A9_0313_0300" },
+ { "1F6B", "303A9_0314_0300" },
+ { "1F6C", "303A9_0313_0301" },
+ { "1F6D", "303A9_0314_0301" },
+ { "1F6E", "303A9_0313_0342" },
+ { "1F6F", "303A9_0314_0342" },
+ { "1F70", "203B1_0300" },
+ { "1F71", "203B1_0301" },
+ { "1F72", "203B5_0300" },
+ { "1F73", "203B5_0301" },
+ { "1F74", "203B7_0300" },
+ { "1F75", "203B7_0301" },
+ { "1F76", "203B9_0300" },
+ { "1F77", "203B9_0301" },
+ { "1F78", "203BF_0300" },
+ { "1F79", "203BF_0301" },
+ { "1F7A", "203C5_0300" },
+ { "1F7B", "203C5_0301" },
+ { "1F7C", "203C9_0300" },
+ { "1F7D", "203C9_0301" },
+ { "1F80", "303B1_0313_0345" },
+ { "1F81", "303B1_0314_0345" },
+ { "1F82", "403B1_0313_0300_0345" },
+ { "1F83", "403B1_0314_0300_0345" },
+ { "1F84", "403B1_0313_0301_0345" },
+ { "1F85", "403B1_0314_0301_0345" },
+ { "1F86", "403B1_0313_0342_0345" },
+ { "1F87", "403B1_0314_0342_0345" },
+ { "1F88", "30391_0313_0345" },
+ { "1F89", "30391_0314_0345" },
+ { "1F8A", "40391_0313_0300_0345" },
+ { "1F8B", "40391_0314_0300_0345" },
+ { "1F8C", "40391_0313_0301_0345" },
+ { "1F8D", "40391_0314_0301_0345" },
+ { "1F8E", "40391_0313_0342_0345" },
+ { "1F8F", "40391_0314_0342_0345" },
+ { "1F90", "303B7_0313_0345" },
+ { "1F91", "303B7_0314_0345" },
+ { "1F92", "403B7_0313_0300_0345" },
+ { "1F93", "403B7_0314_0300_0345" },
+ { "1F94", "403B7_0313_0301_0345" },
+ { "1F95", "403B7_0314_0301_0345" },
+ { "1F96", "403B7_0313_0342_0345" },
+ { "1F97", "403B7_0314_0342_0345" },
+ { "1F98", "30397_0313_0345" },
+ { "1F99", "30397_0314_0345" },
+ { "1F9A", "40397_0313_0300_0345" },
+ { "1F9B", "40397_0314_0300_0345" },
+ { "1F9C", "40397_0313_0301_0345" },
+ { "1F9D", "40397_0314_0301_0345" },
+ { "1F9E", "40397_0313_0342_0345" },
+ { "1F9F", "40397_0314_0342_0345" },
+ { "1FA0", "303C9_0313_0345" },
+ { "1FA1", "303C9_0314_0345" },
+ { "1FA2", "403C9_0313_0300_0345" },
+ { "1FA3", "403C9_0314_0300_0345" },
+ { "1FA4", "403C9_0313_0301_0345" },
+ { "1FA5", "403C9_0314_0301_0345" },
+ { "1FA6", "403C9_0313_0342_0345" },
+ { "1FA7", "403C9_0314_0342_0345" },
+ { "1FA8", "303A9_0313_0345" },
+ { "1FA9", "303A9_0314_0345" },
+ { "1FAA", "403A9_0313_0300_0345" },
+ { "1FAB", "403A9_0314_0300_0345" },
+ { "1FAC", "403A9_0313_0301_0345" },
+ { "1FAD", "403A9_0314_0301_0345" },
+ { "1FAE", "403A9_0313_0342_0345" },
+ { "1FAF", "403A9_0314_0342_0345" },
+ { "1FB0", "203B1_0306" },
+ { "1FB1", "203B1_0304" },
+ { "1FB2", "303B1_0300_0345" },
+ { "1FB3", "203B1_0345" },
+ { "1FB4", "303B1_0301_0345" },
+ { "1FB6", "203B1_0342" },
+ { "1FB7", "303B1_0342_0345" },
+ { "1FB8", "20391_0306" },
+ { "1FB9", "20391_0304" },
+ { "1FBA", "20391_0300" },
+ { "1FBB", "20391_0301" },
+ { "1FBC", "20391_0345" },
+ { "1FBE", "103B9" },
+ { "1FC1", "200A8_0342" },
+ { "1FC2", "303B7_0300_0345" },
+ { "1FC3", "203B7_0345" },
+ { "1FC4", "303B7_0301_0345" },
+ { "1FC6", "203B7_0342" },
+ { "1FC7", "303B7_0342_0345" },
+ { "1FC8", "20395_0300" },
+ { "1FC9", "20395_0301" },
+ { "1FCA", "20397_0300" },
+ { "1FCB", "20397_0301" },
+ { "1FCC", "20397_0345" },
+ { "1FCD", "21FBF_0300" },
+ { "1FCE", "21FBF_0301" },
+ { "1FCF", "21FBF_0342" },
+ { "1FD0", "203B9_0306" },
+ { "1FD1", "203B9_0304" },
+ { "1FD2", "303B9_0308_0300" },
+ { "1FD3", "303B9_0308_0301" },
+ { "1FD6", "203B9_0342" },
+ { "1FD7", "303B9_0308_0342" },
+ { "1FD8", "20399_0306" },
+ { "1FD9", "20399_0304" },
+ { "1FDA", "20399_0300" },
+ { "1FDB", "20399_0301" },
+ { "1FDD", "21FFE_0300" },
+ { "1FDE", "21FFE_0301" },
+ { "1FDF", "21FFE_0342" },
+ { "1FE0", "203C5_0306" },
+ { "1FE1", "203C5_0304" },
+ { "1FE2", "303C5_0308_0300" },
+ { "1FE3", "303C5_0308_0301" },
+ { "1FE4", "203C1_0313" },
+ { "1FE5", "203C1_0314" },
+ { "1FE6", "203C5_0342" },
+ { "1FE7", "303C5_0308_0342" },
+ { "1FE8", "203A5_0306" },
+ { "1FE9", "203A5_0304" },
+ { "1FEA", "203A5_0300" },
+ { "1FEB", "203A5_0301" },
+ { "1FEC", "203A1_0314" },
+ { "1FED", "200A8_0300" },
+ { "1FEE", "200A8_0301" },
+ { "1FEF", "10060" },
+ { "1FF2", "303C9_0300_0345" },
+ { "1FF3", "203C9_0345" },
+ { "1FF4", "303C9_0301_0345" },
+ { "1FF6", "203C9_0342" },
+ { "1FF7", "303C9_0342_0345" },
+ { "1FF8", "2039F_0300" },
+ { "1FF9", "2039F_0301" },
+ { "1FFA", "203A9_0300" },
+ { "1FFB", "203A9_0301" },
+ { "1FFC", "203A9_0345" },
+ { "1FFD", "100B4" },
+ { "2000", "12002" },
+ { "2001", "12003" },
+ { "2126", "103A9" },
+ { "212A", "1004B" },
+ { "212B", "20041_030A" },
+ { "219A", "22190_0338" },
+ { "219B", "22192_0338" },
+ { "21AE", "22194_0338" },
+ { "21CD", "221D0_0338" },
+ { "21CE", "221D4_0338" },
+ { "21CF", "221D2_0338" },
+ { "2204", "22203_0338" },
+ { "2209", "22208_0338" },
+ { "220C", "2220B_0338" },
+ { "2224", "22223_0338" },
+ { "2226", "22225_0338" },
+ { "2241", "2223C_0338" },
+ { "2244", "22243_0338" },
+ { "2247", "22245_0338" },
+ { "2249", "22248_0338" },
+ { "2260", "2003D_0338" },
+ { "2262", "22261_0338" },
+ { "226D", "2224D_0338" },
+ { "226E", "2003C_0338" },
+ { "226F", "2003E_0338" },
+ { "2270", "22264_0338" },
+ { "2271", "22265_0338" },
+ { "2274", "22272_0338" },
+ { "2275", "22273_0338" },
+ { "2278", "22276_0338" },
+ { "2279", "22277_0338" },
+ { "2280", "2227A_0338" },
+ { "2281", "2227B_0338" },
+ { "2284", "22282_0338" },
+ { "2285", "22283_0338" },
+ { "2288", "22286_0338" },
+ { "2289", "22287_0338" },
+ { "22AC", "222A2_0338" },
+ { "22AD", "222A8_0338" },
+ { "22AE", "222A9_0338" },
+ { "22AF", "222AB_0338" },
+ { "22E0", "2227C_0338" },
+ { "22E1", "2227D_0338" },
+ { "22E2", "22291_0338" },
+ { "22E3", "22292_0338" },
+ { "22EA", "222B2_0338" },
+ { "22EB", "222B3_0338" },
+ { "22EC", "222B4_0338" },
+ { "22ED", "222B5_0338" },
+ { "2329", "13008" },
+ { "232A", "13009" },
+ { "2ADC", "22ADD_0338" },
+ { "304C", "2304B_3099" },
+ { "304E", "2304D_3099" },
+ { "3050", "2304F_3099" },
+ { "3052", "23051_3099" },
+ { "3054", "23053_3099" },
+ { "3056", "23055_3099" },
+ { "3058", "23057_3099" },
+ { "305A", "23059_3099" },
+ { "305C", "2305B_3099" },
+ { "305E", "2305D_3099" },
+ { "3060", "2305F_3099" },
+ { "3062", "23061_3099" },
+ { "3065", "23064_3099" },
+ { "3067", "23066_3099" },
+ { "3069", "23068_3099" },
+ { "3070", "2306F_3099" },
+ { "3071", "2306F_309A" },
+ { "3073", "23072_3099" },
+ { "3074", "23072_309A" },
+ { "3076", "23075_3099" },
+ { "3077", "23075_309A" },
+ { "3079", "23078_3099" },
+ { "307A", "23078_309A" },
+ { "307C", "2307B_3099" },
+ { "307D", "2307B_309A" },
+ { "3094", "23046_3099" },
+ { "309E", "2309D_3099" },
+ { "30AC", "230AB_3099" },
+ { "30AE", "230AD_3099" },
+ { "30B0", "230AF_3099" },
+ { "30B2", "230B1_3099" },
+ { "30B4", "230B3_3099" },
+ { "30B6", "230B5_3099" },
+ { "30B8", "230B7_3099" },
+ { "30BA", "230B9_3099" },
+ { "30BC", "230BB_3099" },
+ { "30BE", "230BD_3099" },
+ { "30C0", "230BF_3099" },
+ { "30C2", "230C1_3099" },
+ { "30C5", "230C4_3099" },
+ { "30C7", "230C6_3099" },
+ { "30C9", "230C8_3099" },
+ { "30D0", "230CF_3099" },
+ { "30D1", "230CF_309A" },
+ { "30D3", "230D2_3099" },
+ { "30D4", "230D2_309A" },
+ { "30D6", "230D5_3099" },
+ { "30D7", "230D5_309A" },
+ { "30D9", "230D8_3099" },
+ { "30DA", "230D8_309A" },
+ { "30DC", "230DB_3099" },
+ { "30DD", "230DB_309A" },
+ { "30F4", "230A6_3099" },
+ { "30F7", "230EF_3099" },
+ { "30F8", "230F0_3099" },
+ { "30F9", "230F1_3099" },
+ { "30FA", "230F2_3099" },
+ { "30FE", "230FD_3099" },
+ { "F900", "18C48" },
+ { "F901", "166F4" },
+ { "F902", "18ECA" },
+ { "F903", "18CC8" },
+ { "F904", "16ED1" },
+ { "F905", "14E32" },
+ { "F906", "153E5" },
+ { "F907", "19F9C" },
+ { "F908", "19F9C" },
+ { "F909", "15951" },
+ { "F90A", "191D1" },
+ { "F90B", "15587" },
+ { "F90C", "15948" },
+ { "F90D", "161F6" },
+ { "F90E", "17669" },
+ { "F90F", "17F85" },
+ { "F910", "1863F" },
+ { "F911", "187BA" },
+ { "F912", "188F8" },
+ { "F913", "1908F" },
+ { "F914", "16A02" },
+ { "F915", "16D1B" },
+ { "F916", "170D9" },
+ { "F917", "173DE" },
+ { "F918", "1843D" },
+ { "F919", "1916A" },
+ { "F91A", "199F1" },
+ { "F91B", "14E82" },
+ { "F91C", "15375" },
+ { "F91D", "16B04" },
+ { "F91E", "1721B" },
+ { "F91F", "1862D" },
+ { "F920", "19E1E" },
+ { "F921", "15D50" },
+ { "F922", "16FEB" },
+ { "F923", "185CD" },
+ { "F924", "18964" },
+ { "F925", "162C9" },
+ { "F926", "181D8" },
+ { "F927", "1881F" },
+ { "F928", "15ECA" },
+ { "F929", "16717" },
+ { "F92A", "16D6A" },
+ { "F92B", "172FC" },
+ { "F92C", "190CE" },
+ { "F92D", "14F86" },
+ { "F92E", "151B7" },
+ { "F92F", "152DE" },
+ { "F930", "164C4" },
+ { "F931", "16AD3" },
+ { "F932", "17210" },
+ { "F933", "176E7" },
+ { "F934", "18001" },
+ { "F935", "18606" },
+ { "F936", "1865C" },
+ { "F937", "18DEF" },
+ { "F938", "19732" },
+ { "F939", "19B6F" },
+ { "F93A", "19DFA" },
+ { "F93B", "1788C" },
+ { "F93C", "1797F" },
+ { "F93D", "17DA0" },
+ { "F93E", "183C9" },
+ { "F93F", "19304" },
+ { "F940", "19E7F" },
+ { "F941", "18AD6" },
+ { "F942", "158DF" },
+ { "F943", "15F04" },
+ { "F944", "17C60" },
+ { "F945", "1807E" },
+ { "F946", "17262" },
+ { "F947", "178CA" },
+ { "F948", "18CC2" },
+ { "F949", "196F7" },
+ { "F94A", "158D8" },
+ { "F94B", "15C62" },
+ { "F94C", "16A13" },
+ { "F94D", "16DDA" },
+ { "F94E", "16F0F" },
+ { "F94F", "17D2F" },
+ { "F950", "17E37" },
+ { "F951", "1964B" },
+ { "F952", "152D2" },
+ { "F953", "1808B" },
+ { "F954", "151DC" },
+ { "F955", "151CC" },
+ { "F956", "17A1C" },
+ { "F957", "17DBE" },
+ { "F958", "183F1" },
+ { "F959", "19675" },
+ { "F95A", "18B80" },
+ { "F95B", "162CF" },
+ { "F95C", "16A02" },
+ { "F95D", "18AFE" },
+ { "F95E", "14E39" },
+ { "F95F", "15BE7" },
+ { "F960", "16012" },
+ { "F961", "17387" },
+ { "F962", "17570" },
+ { "F963", "15317" },
+ { "F964", "178FB" },
+ { "F965", "14FBF" },
+ { "F966", "15FA9" },
+ { "F967", "14E0D" },
+ { "F968", "16CCC" },
+ { "F969", "16578" },
+ { "F96A", "17D22" },
+ { "F96B", "153C3" },
+ { "F96C", "1585E" },
+ { "F96D", "17701" },
+ { "F96E", "18449" },
+ { "F96F", "18AAA" },
+ { "F970", "16BBA" },
+ { "F971", "18FB0" },
+ { "F972", "16C88" },
+ { "F973", "162FE" },
+ { "F974", "182E5" },
+ { "F975", "163A0" },
+ { "F976", "17565" },
+ { "F977", "14EAE" },
+ { "F978", "15169" },
+ { "F979", "151C9" },
+ { "F97A", "16881" },
+ { "F97B", "17CE7" },
+ { "F97C", "1826F" },
+ { "F97D", "18AD2" },
+ { "F97E", "191CF" },
+ { "F97F", "152F5" },
+ { "F980", "15442" },
+ { "F981", "15973" },
+ { "F982", "15EEC" },
+ { "F983", "165C5" },
+ { "F984", "16FFE" },
+ { "F985", "1792A" },
+ { "F986", "195AD" },
+ { "F987", "19A6A" },
+ { "F988", "19E97" },
+ { "F989", "19ECE" },
+ { "F98A", "1529B" },
+ { "F98B", "166C6" },
+ { "F98C", "16B77" },
+ { "F98D", "18F62" },
+ { "F98E", "15E74" },
+ { "F98F", "16190" },
+ { "F990", "16200" },
+ { "F991", "1649A" },
+ { "F992", "16F23" },
+ { "F993", "17149" },
+ { "F994", "17489" },
+ { "F995", "179CA" },
+ { "F996", "17DF4" },
+ { "F997", "1806F" },
+ { "F998", "18F26" },
+ { "F999", "184EE" },
+ { "F99A", "19023" },
+ { "F99B", "1934A" },
+ { "F99C", "15217" },
+ { "F99D", "152A3" },
+ { "F99E", "154BD" },
+ { "F99F", "170C8" },
+ { "F9A0", "188C2" },
+ { "F9A1", "18AAA" },
+ { "F9A2", "15EC9" },
+ { "F9A3", "15FF5" },
+ { "F9A4", "1637B" },
+ { "F9A5", "16BAE" },
+ { "F9A6", "17C3E" },
+ { "F9A7", "17375" },
+ { "F9A8", "14EE4" },
+ { "F9A9", "156F9" },
+ { "F9AA", "15BE7" },
+ { "F9AB", "15DBA" },
+ { "F9AC", "1601C" },
+ { "F9AD", "173B2" },
+ { "F9AE", "17469" },
+ { "F9AF", "17F9A" },
+ { "F9B0", "18046" },
+ { "F9B1", "19234" },
+ { "F9B2", "196F6" },
+ { "F9B3", "19748" },
+ { "F9B4", "19818" },
+ { "F9B5", "14F8B" },
+ { "F9B6", "179AE" },
+ { "F9B7", "191B4" },
+ { "F9B8", "196B8" },
+ { "F9B9", "160E1" },
+ { "F9BA", "14E86" },
+ { "F9BB", "150DA" },
+ { "F9BC", "15BEE" },
+ { "F9BD", "15C3F" },
+ { "F9BE", "16599" },
+ { "F9BF", "16A02" },
+ { "F9C0", "171CE" },
+ { "F9C1", "17642" },
+ { "F9C2", "184FC" },
+ { "F9C3", "1907C" },
+ { "F9C4", "19F8D" },
+ { "F9C5", "16688" },
+ { "F9C6", "1962E" },
+ { "F9C7", "15289" },
+ { "F9C8", "1677B" },
+ { "F9C9", "167F3" },
+ { "F9CA", "16D41" },
+ { "F9CB", "16E9C" },
+ { "F9CC", "17409" },
+ { "F9CD", "17559" },
+ { "F9CE", "1786B" },
+ { "F9CF", "17D10" },
+ { "F9D0", "1985E" },
+ { "F9D1", "1516D" },
+ { "F9D2", "1622E" },
+ { "F9D3", "19678" },
+ { "F9D4", "1502B" },
+ { "F9D5", "15D19" },
+ { "F9D6", "16DEA" },
+ { "F9D7", "18F2A" },
+ { "F9D8", "15F8B" },
+ { "F9D9", "16144" },
+ { "F9DA", "16817" },
+ { "F9DB", "17387" },
+ { "F9DC", "19686" },
+ { "F9DD", "15229" },
+ { "F9DE", "1540F" },
+ { "F9DF", "15C65" },
+ { "F9E0", "16613" },
+ { "F9E1", "1674E" },
+ { "F9E2", "168A8" },
+ { "F9E3", "16CE5" },
+ { "F9E4", "17406" },
+ { "F9E5", "175E2" },
+ { "F9E6", "17F79" },
+ { "F9E7", "188CF" },
+ { "F9E8", "188E1" },
+ { "F9E9", "191CC" },
+ { "F9EA", "196E2" },
+ { "F9EB", "1533F" },
+ { "F9EC", "16EBA" },
+ { "F9ED", "1541D" },
+ { "F9EE", "171D0" },
+ { "F9EF", "17498" },
+ { "F9F0", "185FA" },
+ { "F9F1", "196A3" },
+ { "F9F2", "19C57" },
+ { "F9F3", "19E9F" },
+ { "F9F4", "16797" },
+ { "F9F5", "16DCB" },
+ { "F9F6", "181E8" },
+ { "F9F7", "17ACB" },
+ { "F9F8", "17B20" },
+ { "F9F9", "17C92" },
+ { "F9FA", "172C0" },
+ { "F9FB", "17099" },
+ { "F9FC", "18B58" },
+ { "F9FD", "14EC0" },
+ { "F9FE", "18336" },
+ { "F9FF", "1523A" },
+ { "FA00", "15207" },
+ { "FA01", "15EA6" },
+ { "FA02", "162D3" },
+ { "FA03", "17CD6" },
+ { "FA04", "15B85" },
+ { "FA05", "16D1E" },
+ { "FA06", "166B4" },
+ { "FA07", "18F3B" },
+ { "FA08", "1884C" },
+ { "FA09", "1964D" },
+ { "FA0A", "1898B" },
+ { "FA0B", "15ED3" },
+ { "FA0C", "15140" },
+ { "FA0D", "155C0" },
+ { "FA10", "1585A" },
+ { "FA12", "16674" },
+ { "FA15", "151DE" },
+ { "FA16", "1732A" },
+ { "FA17", "176CA" },
+ { "FA18", "1793C" },
+ { "FA19", "1795E" },
+ { "FA1A", "17965" },
+ { "FA1B", "1798F" },
+ { "FA1C", "19756" },
+ { "FA1D", "17CBE" },
+ { "FA1E", "17FBD" },
+ { "FA20", "18612" },
+ { "FA22", "18AF8" },
+ { "FA25", "19038" },
+ { "FA26", "190FD" },
+ { "FA2A", "198EF" },
+ { "FA2B", "198FC" },
+ { "FA2C", "19928" },
+ { "FA2D", "19DB4" },
+ { "FA2E", "190DE" },
+ { "FA2F", "196B7" },
+ { "FA30", "14FAE" },
+ { "FA31", "150E7" },
+ { "FA32", "1514D" },
+ { "FA33", "152C9" },
+ { "FA34", "152E4" },
+ { "FA35", "15351" },
+ { "FA36", "1559D" },
+ { "FA37", "15606" },
+ { "FA38", "15668" },
+ { "FA39", "15840" },
+ { "FA3A", "158A8" },
+ { "FA3B", "15C64" },
+ { "FA3C", "15C6E" },
+ { "FA3D", "16094" },
+ { "FA3E", "16168" },
+ { "FA3F", "1618E" },
+ { "FA40", "161F2" },
+ { "FA41", "1654F" },
+ { "FA42", "165E2" },
+ { "FA43", "16691" },
+ { "FA44", "16885" },
+ { "FA45", "16D77" },
+ { "FA46", "16E1A" },
+ { "FA47", "16F22" },
+ { "FA48", "1716E" },
+ { "FA49", "1722B" },
+ { "FA4A", "17422" },
+ { "FA4B", "17891" },
+ { "FA4C", "1793E" },
+ { "FA4D", "17949" },
+ { "FA4E", "17948" },
+ { "FA4F", "17950" },
+ { "FA50", "17956" },
+ { "FA51", "1795D" },
+ { "FA52", "1798D" },
+ { "FA53", "1798E" },
+ { "FA54", "17A40" },
+ { "FA55", "17A81" },
+ { "FA56", "17BC0" },
+ { "FA57", "17DF4" },
+ { "FA58", "17E09" },
+ { "FA59", "17E41" },
+ { "FA5A", "17F72" },
+ { "FA5B", "18005" },
+ { "FA5C", "181ED" },
+ { "FA5D", "18279" },
+ { "FA5E", "18279" },
+ { "FA5F", "18457" },
+ { "FA60", "18910" },
+ { "FA61", "18996" },
+ { "FA62", "18B01" },
+ { "FA63", "18B39" },
+ { "FA64", "18CD3" },
+ { "FA65", "18D08" },
+ { "FA66", "18FB6" },
+ { "FA67", "19038" },
+ { "FA68", "196E3" },
+ { "FA69", "197FF" },
+ { "FA6A", "1983B" },
+ { "FA6B", "16075" },
+ { "FA6C", "1242EE" },
+ { "FA6D", "18218" },
+ { "FA70", "14E26" },
+ { "FA71", "151B5" },
+ { "FA72", "15168" },
+ { "FA73", "14F80" },
+ { "FA74", "15145" },
+ { "FA75", "15180" },
+ { "FA76", "152C7" },
+ { "FA77", "152FA" },
+ { "FA78", "1559D" },
+ { "FA79", "15555" },
+ { "FA7A", "15599" },
+ { "FA7B", "155E2" },
+ { "FA7C", "1585A" },
+ { "FA7D", "158B3" },
+ { "FA7E", "15944" },
+ { "FA7F", "15954" },
+ { "FA80", "15A62" },
+ { "FA81", "15B28" },
+ { "FA82", "15ED2" },
+ { "FA83", "15ED9" },
+ { "FA84", "15F69" },
+ { "FA85", "15FAD" },
+ { "FA86", "160D8" },
+ { "FA87", "1614E" },
+ { "FA88", "16108" },
+ { "FA89", "1618E" },
+ { "FA8A", "16160" },
+ { "FA8B", "161F2" },
+ { "FA8C", "16234" },
+ { "FA8D", "163C4" },
+ { "FA8E", "1641C" },
+ { "FA8F", "16452" },
+ { "FA90", "16556" },
+ { "FA91", "16674" },
+ { "FA92", "16717" },
+ { "FA93", "1671B" },
+ { "FA94", "16756" },
+ { "FA95", "16B79" },
+ { "FA96", "16BBA" },
+ { "FA97", "16D41" },
+ { "FA98", "16EDB" },
+ { "FA99", "16ECB" },
+ { "FA9A", "16F22" },
+ { "FA9B", "1701E" },
+ { "FA9C", "1716E" },
+ { "FA9D", "177A7" },
+ { "FA9E", "17235" },
+ { "FA9F", "172AF" },
+ { "FAA0", "1732A" },
+ { "FAA1", "17471" },
+ { "FAA2", "17506" },
+ { "FAA3", "1753B" },
+ { "FAA4", "1761D" },
+ { "FAA5", "1761F" },
+ { "FAA6", "176CA" },
+ { "FAA7", "176DB" },
+ { "FAA8", "176F4" },
+ { "FAA9", "1774A" },
+ { "FAAA", "17740" },
+ { "FAAB", "178CC" },
+ { "FAAC", "17AB1" },
+ { "FAAD", "17BC0" },
+ { "FAAE", "17C7B" },
+ { "FAAF", "17D5B" },
+ { "FAB0", "17DF4" },
+ { "FAB1", "17F3E" },
+ { "FAB2", "18005" },
+ { "FAB3", "18352" },
+ { "FAB4", "183EF" },
+ { "FAB5", "18779" },
+ { "FAB6", "18941" },
+ { "FAB7", "18986" },
+ { "FAB8", "18996" },
+ { "FAB9", "18ABF" },
+ { "FABA", "18AF8" },
+ { "FABB", "18ACB" },
+ { "FABC", "18B01" },
+ { "FABD", "18AFE" },
+ { "FABE", "18AED" },
+ { "FABF", "18B39" },
+ { "FAC0", "18B8A" },
+ { "FAC1", "18D08" },
+ { "FAC2", "18F38" },
+ { "FAC3", "19072" },
+ { "FAC4", "19199" },
+ { "FAC5", "19276" },
+ { "FAC6", "1967C" },
+ { "FAC7", "196E3" },
+ { "FAC8", "19756" },
+ { "FAC9", "197DB" },
+ { "FACA", "197FF" },
+ { "FACB", "1980B" },
+ { "FACC", "1983B" },
+ { "FACD", "19B12" },
+ { "FACE", "19F9C" },
+ { "FACF", "12284A" },
+ { "FAD0", "122844" },
+ { "FAD1", "1233D5" },
+ { "FAD2", "13B9D" },
+ { "FAD3", "14018" },
+ { "FAD4", "14039" },
+ { "FAD5", "125249" },
+ { "FAD6", "125CD0" },
+ { "FAD7", "127ED3" },
+ { "FAD8", "19F43" },
+ { "FAD9", "19F8E" },
+ { "FB1D", "205D9_05B4" },
+ { "FB1F", "205F2_05B7" },
+ { "FB2A", "205E9_05C1" },
+ { "FB2B", "205E9_05C2" },
+ { "FB2C", "305E9_05BC_05C1" },
+ { "FB2D", "305E9_05BC_05C2" },
+ { "FB2E", "205D0_05B7" },
+ { "FB2F", "205D0_05B8" },
+ { "FB30", "205D0_05BC" },
+ { "FB31", "205D1_05BC" },
+ { "FB32", "205D2_05BC" },
+ { "FB33", "205D3_05BC" },
+ { "FB34", "205D4_05BC" },
+ { "FB35", "205D5_05BC" },
+ { "FB36", "205D6_05BC" },
+ { "FB38", "205D8_05BC" },
+ { "FB39", "205D9_05BC" },
+ { "FB3A", "205DA_05BC" },
+ { "FB3B", "205DB_05BC" },
+ { "FB3C", "205DC_05BC" },
+ { "FB3E", "205DE_05BC" },
+ { "FB40", "205E0_05BC" },
+ { "FB41", "205E1_05BC" },
+ { "FB43", "205E3_05BC" },
+ { "FB44", "205E4_05BC" },
+ { "FB46", "205E6_05BC" },
+ { "FB47", "205E7_05BC" },
+ { "FB48", "205E8_05BC" },
+ { "FB49", "205E9_05BC" },
+ { "FB4A", "205EA_05BC" },
+ { "FB4B", "205D5_05B9" },
+ { "FB4C", "205D1_05BF" },
+ { "FB4D", "205DB_05BF" },
+ { "FB4E", "205E4_05BF" },
+ { "1109A", "211099_110BA" },
+ { "1109C", "21109B_110BA" },
+ { "110AB", "2110A5_110BA" },
+ { "1112E", "211131_11127" },
+ { "1112F", "211132_11127" },
+ { "1134B", "211347_1133E" },
+ { "1134C", "211347_11357" },
+ { "114BB", "2114B9_114BA" },
+ { "114BC", "2114B9_114B0" },
+ { "114BE", "2114B9_114BD" },
+ { "115BA", "2115B8_115AF" },
+ { "115BB", "2115B9_115AF" },
+ { "1D15E", "21D157_1D165" },
+ { "1D15F", "21D158_1D165" },
+ { "1D160", "31D158_1D165_1D16E" },
+ { "1D161", "31D158_1D165_1D16F" },
+ { "1D162", "31D158_1D165_1D170" },
+ { "1D163", "31D158_1D165_1D171" },
+ { "1D164", "31D158_1D165_1D172" },
+ { "1D1BB", "21D1B9_1D165" },
+ { "1D1BC", "21D1BA_1D165" },
+ { "1D1BD", "31D1B9_1D165_1D16E" },
+ { "1D1BE", "31D1BA_1D165_1D16E" },
+ { "1D1BF", "31D1B9_1D165_1D16F" },
+ { "1D1C0", "31D1BA_1D165_1D16F" },
+ { "2F800", "14E3D" },
+ { "2F801", "14E38" },
+ { "2F802", "14E41" },
+ { "2F803", "120122" },
+ { "2F804", "14F60" },
+ { "2F805", "14FAE" },
+ { "2F806", "14FBB" },
+ { "2F807", "15002" },
+ { "2F808", "1507A" },
+ { "2F809", "15099" },
+ { "2F80A", "150E7" },
+ { "2F80B", "150CF" },
+ { "2F80C", "1349E" },
+ { "2F80D", "12063A" },
+ { "2F80E", "1514D" },
+ { "2F80F", "15154" },
+ { "2F810", "15164" },
+ { "2F811", "15177" },
+ { "2F812", "12051C" },
+ { "2F813", "134B9" },
+ { "2F814", "15167" },
+ { "2F815", "1518D" },
+ { "2F816", "12054B" },
+ { "2F817", "15197" },
+ { "2F818", "151A4" },
+ { "2F819", "14ECC" },
+ { "2F81A", "151AC" },
+ { "2F81B", "151B5" },
+ { "2F81C", "1291DF" },
+ { "2F81D", "151F5" },
+ { "2F81E", "15203" },
+ { "2F81F", "134DF" },
+ { "2F820", "1523B" },
+ { "2F821", "15246" },
+ { "2F822", "15272" },
+ { "2F823", "15277" },
+ { "2F824", "13515" },
+ { "2F825", "152C7" },
+ { "2F826", "152C9" },
+ { "2F827", "152E4" },
+ { "2F828", "152FA" },
+ { "2F829", "15305" },
+ { "2F82A", "15306" },
+ { "2F82B", "15317" },
+ { "2F82C", "15349" },
+ { "2F82D", "15351" },
+ { "2F82E", "1535A" },
+ { "2F82F", "15373" },
+ { "2F830", "1537D" },
+ { "2F831", "1537F" },
+ { "2F832", "1537F" },
+ { "2F833", "1537F" },
+ { "2F834", "120A2C" },
+ { "2F835", "17070" },
+ { "2F836", "153CA" },
+ { "2F837", "153DF" },
+ { "2F838", "120B63" },
+ { "2F839", "153EB" },
+ { "2F83A", "153F1" },
+ { "2F83B", "15406" },
+ { "2F83C", "1549E" },
+ { "2F83D", "15438" },
+ { "2F83E", "15448" },
+ { "2F83F", "15468" },
+ { "2F840", "154A2" },
+ { "2F841", "154F6" },
+ { "2F842", "15510" },
+ { "2F843", "15553" },
+ { "2F844", "15563" },
+ { "2F845", "15584" },
+ { "2F846", "15584" },
+ { "2F847", "15599" },
+ { "2F848", "155AB" },
+ { "2F849", "155B3" },
+ { "2F84A", "155C2" },
+ { "2F84B", "15716" },
+ { "2F84C", "15606" },
+ { "2F84D", "15717" },
+ { "2F84E", "15651" },
+ { "2F84F", "15674" },
+ { "2F850", "15207" },
+ { "2F851", "158EE" },
+ { "2F852", "157CE" },
+ { "2F853", "157F4" },
+ { "2F854", "1580D" },
+ { "2F855", "1578B" },
+ { "2F856", "15832" },
+ { "2F857", "15831" },
+ { "2F858", "158AC" },
+ { "2F859", "1214E4" },
+ { "2F85A", "158F2" },
+ { "2F85B", "158F7" },
+ { "2F85C", "15906" },
+ { "2F85D", "1591A" },
+ { "2F85E", "15922" },
+ { "2F85F", "15962" },
+ { "2F860", "1216A8" },
+ { "2F861", "1216EA" },
+ { "2F862", "159EC" },
+ { "2F863", "15A1B" },
+ { "2F864", "15A27" },
+ { "2F865", "159D8" },
+ { "2F866", "15A66" },
+ { "2F867", "136EE" },
+ { "2F868", "136FC" },
+ { "2F869", "15B08" },
+ { "2F86A", "15B3E" },
+ { "2F86B", "15B3E" },
+ { "2F86C", "1219C8" },
+ { "2F86D", "15BC3" },
+ { "2F86E", "15BD8" },
+ { "2F86F", "15BE7" },
+ { "2F870", "15BF3" },
+ { "2F871", "121B18" },
+ { "2F872", "15BFF" },
+ { "2F873", "15C06" },
+ { "2F874", "15F53" },
+ { "2F875", "15C22" },
+ { "2F876", "13781" },
+ { "2F877", "15C60" },
+ { "2F878", "15C6E" },
+ { "2F879", "15CC0" },
+ { "2F87A", "15C8D" },
+ { "2F87B", "121DE4" },
+ { "2F87C", "15D43" },
+ { "2F87D", "121DE6" },
+ { "2F87E", "15D6E" },
+ { "2F87F", "15D6B" },
+ { "2F880", "15D7C" },
+ { "2F881", "15DE1" },
+ { "2F882", "15DE2" },
+ { "2F883", "1382F" },
+ { "2F884", "15DFD" },
+ { "2F885", "15E28" },
+ { "2F886", "15E3D" },
+ { "2F887", "15E69" },
+ { "2F888", "13862" },
+ { "2F889", "122183" },
+ { "2F88A", "1387C" },
+ { "2F88B", "15EB0" },
+ { "2F88C", "15EB3" },
+ { "2F88D", "15EB6" },
+ { "2F88E", "15ECA" },
+ { "2F88F", "12A392" },
+ { "2F890", "15EFE" },
+ { "2F891", "122331" },
+ { "2F892", "122331" },
+ { "2F893", "18201" },
+ { "2F894", "15F22" },
+ { "2F895", "15F22" },
+ { "2F896", "138C7" },
+ { "2F897", "1232B8" },
+ { "2F898", "1261DA" },
+ { "2F899", "15F62" },
+ { "2F89A", "15F6B" },
+ { "2F89B", "138E3" },
+ { "2F89C", "15F9A" },
+ { "2F89D", "15FCD" },
+ { "2F89E", "15FD7" },
+ { "2F89F", "15FF9" },
+ { "2F8A0", "16081" },
+ { "2F8A1", "1393A" },
+ { "2F8A2", "1391C" },
+ { "2F8A3", "16094" },
+ { "2F8A4", "1226D4" },
+ { "2F8A5", "160C7" },
+ { "2F8A6", "16148" },
+ { "2F8A7", "1614C" },
+ { "2F8A8", "1614E" },
+ { "2F8A9", "1614C" },
+ { "2F8AA", "1617A" },
+ { "2F8AB", "1618E" },
+ { "2F8AC", "161B2" },
+ { "2F8AD", "161A4" },
+ { "2F8AE", "161AF" },
+ { "2F8AF", "161DE" },
+ { "2F8B0", "161F2" },
+ { "2F8B1", "161F6" },
+ { "2F8B2", "16210" },
+ { "2F8B3", "1621B" },
+ { "2F8B4", "1625D" },
+ { "2F8B5", "162B1" },
+ { "2F8B6", "162D4" },
+ { "2F8B7", "16350" },
+ { "2F8B8", "122B0C" },
+ { "2F8B9", "1633D" },
+ { "2F8BA", "162FC" },
+ { "2F8BB", "16368" },
+ { "2F8BC", "16383" },
+ { "2F8BD", "163E4" },
+ { "2F8BE", "122BF1" },
+ { "2F8BF", "16422" },
+ { "2F8C0", "163C5" },
+ { "2F8C1", "163A9" },
+ { "2F8C2", "13A2E" },
+ { "2F8C3", "16469" },
+ { "2F8C4", "1647E" },
+ { "2F8C5", "1649D" },
+ { "2F8C6", "16477" },
+ { "2F8C7", "13A6C" },
+ { "2F8C8", "1654F" },
+ { "2F8C9", "1656C" },
+ { "2F8CA", "12300A" },
+ { "2F8CB", "165E3" },
+ { "2F8CC", "166F8" },
+ { "2F8CD", "16649" },
+ { "2F8CE", "13B19" },
+ { "2F8CF", "16691" },
+ { "2F8D0", "13B08" },
+ { "2F8D1", "13AE4" },
+ { "2F8D2", "15192" },
+ { "2F8D3", "15195" },
+ { "2F8D4", "16700" },
+ { "2F8D5", "1669C" },
+ { "2F8D6", "180AD" },
+ { "2F8D7", "143D9" },
+ { "2F8D8", "16717" },
+ { "2F8D9", "1671B" },
+ { "2F8DA", "16721" },
+ { "2F8DB", "1675E" },
+ { "2F8DC", "16753" },
+ { "2F8DD", "1233C3" },
+ { "2F8DE", "13B49" },
+ { "2F8DF", "167FA" },
+ { "2F8E0", "16785" },
+ { "2F8E1", "16852" },
+ { "2F8E2", "16885" },
+ { "2F8E3", "12346D" },
+ { "2F8E4", "1688E" },
+ { "2F8E5", "1681F" },
+ { "2F8E6", "16914" },
+ { "2F8E7", "13B9D" },
+ { "2F8E8", "16942" },
+ { "2F8E9", "169A3" },
+ { "2F8EA", "169EA" },
+ { "2F8EB", "16AA8" },
+ { "2F8EC", "1236A3" },
+ { "2F8ED", "16ADB" },
+ { "2F8EE", "13C18" },
+ { "2F8EF", "16B21" },
+ { "2F8F0", "1238A7" },
+ { "2F8F1", "16B54" },
+ { "2F8F2", "13C4E" },
+ { "2F8F3", "16B72" },
+ { "2F8F4", "16B9F" },
+ { "2F8F5", "16BBA" },
+ { "2F8F6", "16BBB" },
+ { "2F8F7", "123A8D" },
+ { "2F8F8", "121D0B" },
+ { "2F8F9", "123AFA" },
+ { "2F8FA", "16C4E" },
+ { "2F8FB", "123CBC" },
+ { "2F8FC", "16CBF" },
+ { "2F8FD", "16CCD" },
+ { "2F8FE", "16C67" },
+ { "2F8FF", "16D16" },
+ { "2F900", "16D3E" },
+ { "2F901", "16D77" },
+ { "2F902", "16D41" },
+ { "2F903", "16D69" },
+ { "2F904", "16D78" },
+ { "2F905", "16D85" },
+ { "2F906", "123D1E" },
+ { "2F907", "16D34" },
+ { "2F908", "16E2F" },
+ { "2F909", "16E6E" },
+ { "2F90A", "13D33" },
+ { "2F90B", "16ECB" },
+ { "2F90C", "16EC7" },
+ { "2F90D", "123ED1" },
+ { "2F90E", "16DF9" },
+ { "2F90F", "16F6E" },
+ { "2F910", "123F5E" },
+ { "2F911", "123F8E" },
+ { "2F912", "16FC6" },
+ { "2F913", "17039" },
+ { "2F914", "1701E" },
+ { "2F915", "1701B" },
+ { "2F916", "13D96" },
+ { "2F917", "1704A" },
+ { "2F918", "1707D" },
+ { "2F919", "17077" },
+ { "2F91A", "170AD" },
+ { "2F91B", "120525" },
+ { "2F91C", "17145" },
+ { "2F91D", "124263" },
+ { "2F91E", "1719C" },
+ { "2F91F", "1243AB" },
+ { "2F920", "17228" },
+ { "2F921", "17235" },
+ { "2F922", "17250" },
+ { "2F923", "124608" },
+ { "2F924", "17280" },
+ { "2F925", "17295" },
+ { "2F926", "124735" },
+ { "2F927", "124814" },
+ { "2F928", "1737A" },
+ { "2F929", "1738B" },
+ { "2F92A", "13EAC" },
+ { "2F92B", "173A5" },
+ { "2F92C", "13EB8" },
+ { "2F92D", "13EB8" },
+ { "2F92E", "17447" },
+ { "2F92F", "1745C" },
+ { "2F930", "17471" },
+ { "2F931", "17485" },
+ { "2F932", "174CA" },
+ { "2F933", "13F1B" },
+ { "2F934", "17524" },
+ { "2F935", "124C36" },
+ { "2F936", "1753E" },
+ { "2F937", "124C92" },
+ { "2F938", "17570" },
+ { "2F939", "12219F" },
+ { "2F93A", "17610" },
+ { "2F93B", "124FA1" },
+ { "2F93C", "124FB8" },
+ { "2F93D", "125044" },
+ { "2F93E", "13FFC" },
+ { "2F93F", "14008" },
+ { "2F940", "176F4" },
+ { "2F941", "1250F3" },
+ { "2F942", "1250F2" },
+ { "2F943", "125119" },
+ { "2F944", "125133" },
+ { "2F945", "1771E" },
+ { "2F946", "1771F" },
+ { "2F947", "1771F" },
+ { "2F948", "1774A" },
+ { "2F949", "14039" },
+ { "2F94A", "1778B" },
+ { "2F94B", "14046" },
+ { "2F94C", "14096" },
+ { "2F94D", "12541D" },
+ { "2F94E", "1784E" },
+ { "2F94F", "1788C" },
+ { "2F950", "178CC" },
+ { "2F951", "140E3" },
+ { "2F952", "125626" },
+ { "2F953", "17956" },
+ { "2F954", "12569A" },
+ { "2F955", "1256C5" },
+ { "2F956", "1798F" },
+ { "2F957", "179EB" },
+ { "2F958", "1412F" },
+ { "2F959", "17A40" },
+ { "2F95A", "17A4A" },
+ { "2F95B", "17A4F" },
+ { "2F95C", "12597C" },
+ { "2F95D", "125AA7" },
+ { "2F95E", "125AA7" },
+ { "2F95F", "17AEE" },
+ { "2F960", "14202" },
+ { "2F961", "125BAB" },
+ { "2F962", "17BC6" },
+ { "2F963", "17BC9" },
+ { "2F964", "14227" },
+ { "2F965", "125C80" },
+ { "2F966", "17CD2" },
+ { "2F967", "142A0" },
+ { "2F968", "17CE8" },
+ { "2F969", "17CE3" },
+ { "2F96A", "17D00" },
+ { "2F96B", "125F86" },
+ { "2F96C", "17D63" },
+ { "2F96D", "14301" },
+ { "2F96E", "17DC7" },
+ { "2F96F", "17E02" },
+ { "2F970", "17E45" },
+ { "2F971", "14334" },
+ { "2F972", "126228" },
+ { "2F973", "126247" },
+ { "2F974", "14359" },
+ { "2F975", "1262D9" },
+ { "2F976", "17F7A" },
+ { "2F977", "12633E" },
+ { "2F978", "17F95" },
+ { "2F979", "17FFA" },
+ { "2F97A", "18005" },
+ { "2F97B", "1264DA" },
+ { "2F97C", "126523" },
+ { "2F97D", "18060" },
+ { "2F97E", "1265A8" },
+ { "2F97F", "18070" },
+ { "2F980", "12335F" },
+ { "2F981", "143D5" },
+ { "2F982", "180B2" },
+ { "2F983", "18103" },
+ { "2F984", "1440B" },
+ { "2F985", "1813E" },
+ { "2F986", "15AB5" },
+ { "2F987", "1267A7" },
+ { "2F988", "1267B5" },
+ { "2F989", "123393" },
+ { "2F98A", "12339C" },
+ { "2F98B", "18201" },
+ { "2F98C", "18204" },
+ { "2F98D", "18F9E" },
+ { "2F98E", "1446B" },
+ { "2F98F", "18291" },
+ { "2F990", "1828B" },
+ { "2F991", "1829D" },
+ { "2F992", "152B3" },
+ { "2F993", "182B1" },
+ { "2F994", "182B3" },
+ { "2F995", "182BD" },
+ { "2F996", "182E6" },
+ { "2F997", "126B3C" },
+ { "2F998", "182E5" },
+ { "2F999", "1831D" },
+ { "2F99A", "18363" },
+ { "2F99B", "183AD" },
+ { "2F99C", "18323" },
+ { "2F99D", "183BD" },
+ { "2F99E", "183E7" },
+ { "2F99F", "18457" },
+ { "2F9A0", "18353" },
+ { "2F9A1", "183CA" },
+ { "2F9A2", "183CC" },
+ { "2F9A3", "183DC" },
+ { "2F9A4", "126C36" },
+ { "2F9A5", "126D6B" },
+ { "2F9A6", "126CD5" },
+ { "2F9A7", "1452B" },
+ { "2F9A8", "184F1" },
+ { "2F9A9", "184F3" },
+ { "2F9AA", "18516" },
+ { "2F9AB", "1273CA" },
+ { "2F9AC", "18564" },
+ { "2F9AD", "126F2C" },
+ { "2F9AE", "1455D" },
+ { "2F9AF", "14561" },
+ { "2F9B0", "126FB1" },
+ { "2F9B1", "1270D2" },
+ { "2F9B2", "1456B" },
+ { "2F9B3", "18650" },
+ { "2F9B4", "1865C" },
+ { "2F9B5", "18667" },
+ { "2F9B6", "18669" },
+ { "2F9B7", "186A9" },
+ { "2F9B8", "18688" },
+ { "2F9B9", "1870E" },
+ { "2F9BA", "186E2" },
+ { "2F9BB", "18779" },
+ { "2F9BC", "18728" },
+ { "2F9BD", "1876B" },
+ { "2F9BE", "18786" },
+ { "2F9BF", "145D7" },
+ { "2F9C0", "187E1" },
+ { "2F9C1", "18801" },
+ { "2F9C2", "145F9" },
+ { "2F9C3", "18860" },
+ { "2F9C4", "18863" },
+ { "2F9C5", "127667" },
+ { "2F9C6", "188D7" },
+ { "2F9C7", "188DE" },
+ { "2F9C8", "14635" },
+ { "2F9C9", "188FA" },
+ { "2F9CA", "134BB" },
+ { "2F9CB", "1278AE" },
+ { "2F9CC", "127966" },
+ { "2F9CD", "146BE" },
+ { "2F9CE", "146C7" },
+ { "2F9CF", "18AA0" },
+ { "2F9D0", "18AED" },
+ { "2F9D1", "18B8A" },
+ { "2F9D2", "18C55" },
+ { "2F9D3", "127CA8" },
+ { "2F9D4", "18CAB" },
+ { "2F9D5", "18CC1" },
+ { "2F9D6", "18D1B" },
+ { "2F9D7", "18D77" },
+ { "2F9D8", "127F2F" },
+ { "2F9D9", "120804" },
+ { "2F9DA", "18DCB" },
+ { "2F9DB", "18DBC" },
+ { "2F9DC", "18DF0" },
+ { "2F9DD", "1208DE" },
+ { "2F9DE", "18ED4" },
+ { "2F9DF", "18F38" },
+ { "2F9E0", "1285D2" },
+ { "2F9E1", "1285ED" },
+ { "2F9E2", "19094" },
+ { "2F9E3", "190F1" },
+ { "2F9E4", "19111" },
+ { "2F9E5", "12872E" },
+ { "2F9E6", "1911B" },
+ { "2F9E7", "19238" },
+ { "2F9E8", "192D7" },
+ { "2F9E9", "192D8" },
+ { "2F9EA", "1927C" },
+ { "2F9EB", "193F9" },
+ { "2F9EC", "19415" },
+ { "2F9ED", "128BFA" },
+ { "2F9EE", "1958B" },
+ { "2F9EF", "14995" },
+ { "2F9F0", "195B7" },
+ { "2F9F1", "128D77" },
+ { "2F9F2", "149E6" },
+ { "2F9F3", "196C3" },
+ { "2F9F4", "15DB2" },
+ { "2F9F5", "19723" },
+ { "2F9F6", "129145" },
+ { "2F9F7", "12921A" },
+ { "2F9F8", "14A6E" },
+ { "2F9F9", "14A76" },
+ { "2F9FA", "197E0" },
+ { "2F9FB", "12940A" },
+ { "2F9FC", "14AB2" },
+ { "2F9FD", "129496" },
+ { "2F9FE", "1980B" },
+ { "2F9FF", "1980B" },
+ { "2FA00", "19829" },
+ { "2FA01", "1295B6" },
+ { "2FA02", "198E2" },
+ { "2FA03", "14B33" },
+ { "2FA04", "19929" },
+ { "2FA05", "199A7" },
+ { "2FA06", "199C2" },
+ { "2FA07", "199FE" },
+ { "2FA08", "14BCE" },
+ { "2FA09", "129B30" },
+ { "2FA0A", "19B12" },
+ { "2FA0B", "19C40" },
+ { "2FA0C", "19CFD" },
+ { "2FA0D", "14CCE" },
+ { "2FA0E", "14CED" },
+ { "2FA0F", "19D67" },
+ { "2FA10", "12A0CE" },
+ { "2FA11", "14CF8" },
+ { "2FA12", "12A105" },
+ { "2FA13", "12A20E" },
+ { "2FA14", "12A291" },
+ { "2FA15", "19EBB" },
+ { "2FA16", "14D56" },
+ { "2FA17", "19EF9" },
+ { "2FA18", "19EFE" },
+ { "2FA19", "19F05" },
+ { "2FA1A", "19F0F" },
+ { "2FA1B", "19F16" },
+ { "2FA1C", "19F3B" },
+ { "2FA1D", "12A600" },
+};
+
+// global constructor
+
+static struct unicode_decompose_init {
+ unicode_decompose_init();
+} _unicode_decompose_init;
+
+unicode_decompose_init::unicode_decompose_init()
+{
+ for (unsigned int i = 0;
+ i < sizeof(unicode_decompose_list)/sizeof(unicode_decompose_list[0]);
+ i++) {
+ unicode_decompose *dec = new unicode_decompose[1];
+ dec->value = (char *)unicode_decompose_list[i].value;
+ unicode_decompose_table.define(unicode_decompose_list[i].key, dec);
+ }
+}
+
+const char *decompose_unicode(const char *s)
+{
+ unicode_decompose *result = unicode_decompose_table.lookup(s);
+ return result ? result->value : 0;
+}
diff --git a/src/libs/libxutil/DviChar.c b/src/libs/libxutil/DviChar.c
new file mode 100644
index 0000000..fe086fc
--- /dev/null
+++ b/src/libs/libxutil/DviChar.c
@@ -0,0 +1,679 @@
+/* Copyright (C) 2014-2020 Free Software Foundation, Inc.
+
+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 of the License, 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.
+
+The GNU General Public License version 2 (GPL2) is available in the
+internet at <http://www.gnu.org/licenses/gpl-2.0.txt>. */
+
+/*
+ * DviChar.c
+ *
+ * Map DVI (ditroff output) character names to
+ * font indexes and back
+ */
+
+#include <config.h>
+
+#include <stdlib.h>
+#include <string.h>
+#include "DviChar.h"
+
+extern char *xmalloc(int);
+
+#define allocHash() ((DviCharNameHash *) xmalloc (sizeof (DviCharNameHash)))
+
+struct map_list {
+ struct map_list *next;
+ DviCharNameMap *map;
+};
+
+static struct map_list *world;
+
+static int standard_maps_loaded = 0;
+static void load_standard_maps (void);
+static int hash_name (const char *);
+static void dispose_hash(DviCharNameMap *);
+static void compute_hash(DviCharNameMap *);
+
+DviCharNameMap *
+DviFindMap (char *encoding)
+{
+ struct map_list *m;
+
+ if (!standard_maps_loaded)
+ load_standard_maps ();
+ for (m = world; m; m=m->next)
+ if (!strcmp (m->map->encoding, encoding))
+ return m->map;
+ return 0;
+}
+
+void
+DviRegisterMap (DviCharNameMap *map)
+{
+ struct map_list *m;
+
+ if (!standard_maps_loaded)
+ load_standard_maps ();
+ for (m = world; m; m = m->next)
+ if (!strcmp (m->map->encoding, map->encoding))
+ break;
+ if (!m) {
+ m = (struct map_list *) xmalloc (sizeof *m);
+ m->next = world;
+ world = m;
+ }
+ dispose_hash (map);
+ m->map = map;
+ compute_hash (map);
+}
+
+static void
+dispose_hash (DviCharNameMap *map)
+{
+ DviCharNameHash **buckets;
+ DviCharNameHash *h, *next;
+ int i;
+
+ buckets = map->buckets;
+ for (i = 0; i < DVI_HASH_SIZE; i++) {
+ for (h = buckets[i]; h; h=next) {
+ next = h->next;
+ free (h);
+ }
+ }
+}
+
+static int
+hash_name (const char *name)
+{
+ int i = 0;
+
+ while (*name)
+ i = (i << 1) ^ *name++;
+ if (i < 0)
+ i = -i;
+ return i;
+}
+
+static void
+compute_hash (DviCharNameMap *map)
+{
+ DviCharNameHash **buckets;
+ int c, s, i;
+ DviCharNameHash *h;
+
+ buckets = map->buckets;
+ for (i = 0; i < DVI_HASH_SIZE; i++)
+ buckets[i] = 0;
+ for (c = 0; c < DVI_MAP_SIZE; c++)
+ for (s = 0; s < DVI_MAX_SYNONYMS; s++) {
+ if (!map->dvi_names[c][s])
+ break;
+ i = hash_name (map->dvi_names[c][s]) % DVI_HASH_SIZE;
+ h = allocHash ();
+ h->next = buckets[i];
+ buckets[i] = h;
+ h->name = map->dvi_names[c][s];
+ h->position = c;
+ }
+
+}
+
+int
+DviCharIndex (DviCharNameMap *map, const char *name)
+{
+ int i;
+ DviCharNameHash *h;
+
+ i = hash_name (name) % DVI_HASH_SIZE;
+ for (h = map->buckets[i]; h; h=h->next)
+ if (!strcmp (h->name, name))
+ return h->position;
+ return -1;
+}
+
+static DviCharNameMap ISO8859_1_map = {
+ "iso8859-1",
+ 0,
+{
+{ 0, /* 0 */},
+{ 0, /* 1 */},
+{ 0, /* 2 */},
+{ 0, /* 3 */},
+{ 0, /* 4 */},
+{ 0, /* 5 */},
+{ 0, /* 6 */},
+{ 0, /* 7 */},
+{ 0, /* 8 */},
+{ 0, /* 9 */},
+{ 0, /* 10 */},
+{ 0, /* 11 */},
+{ 0, /* 12 */},
+{ 0, /* 13 */},
+{ 0, /* 14 */},
+{ 0, /* 15 */},
+{ 0, /* 16 */},
+{ 0, /* 17 */},
+{ 0, /* 18 */},
+{ 0, /* 19 */},
+{ 0, /* 20 */},
+{ 0, /* 21 */},
+{ 0, /* 22 */},
+{ 0, /* 23 */},
+{ 0, /* 24 */},
+{ 0, /* 25 */},
+{ 0, /* 26 */},
+{ 0, /* 27 */},
+{ 0, /* 28 */},
+{ 0, /* 29 */},
+{ 0, /* 30 */},
+{ 0, /* 31 */},
+{ 0, /* 32 */},
+{ "!", /* 33 */},
+{ "\"", "dq", /* 34 */},
+{ "#", "sh", /* 35 */},
+{ "$", "Do", /* 36 */},
+{ "%", /* 37 */},
+{ "&", /* 38 */},
+{ "'", "aq", "cq", "oq", /* 39 */},
+{ "(", /* 40 */},
+{ ")", /* 41 */},
+{ "*", /* 42 */},
+{ "+", /* 43 */},
+{ ",", /* 44 */},
+{ "\\-", /* 45 */},
+{ ".", /* 46 */},
+{ "/", "sl", /* 47 */},
+{ "0", /* 48 */},
+{ "1", /* 49 */},
+{ "2", /* 50 */},
+{ "3", /* 51 */},
+{ "4", /* 52 */},
+{ "5", /* 53 */},
+{ "6", /* 54 */},
+{ "7", /* 55 */},
+{ "8", /* 56 */},
+{ "9", /* 57 */},
+{ ":", /* 58 */},
+{ ";", /* 59 */},
+{ "<", /* 60 */},
+{ "=", /* 61 */},
+{ ">", /* 62 */},
+{ "?", /* 63 */},
+{ "@", "at", /* 64 */},
+{ "A", /* 65 */},
+{ "B", /* 66 */},
+{ "C", /* 67 */},
+{ "D", /* 68 */},
+{ "E", /* 69 */},
+{ "F", /* 70 */},
+{ "G", /* 71 */},
+{ "H", /* 72 */},
+{ "I", /* 73 */},
+{ "J", /* 74 */},
+{ "K", /* 75 */},
+{ "L", /* 76 */},
+{ "M", /* 77 */},
+{ "N", /* 78 */},
+{ "O", /* 79 */},
+{ "P", /* 80 */},
+{ "Q", /* 81 */},
+{ "R", /* 82 */},
+{ "S", /* 83 */},
+{ "T", /* 84 */},
+{ "U", /* 85 */},
+{ "V", /* 86 */},
+{ "W", /* 87 */},
+{ "X", /* 88 */},
+{ "Y", /* 89 */},
+{ "Z", /* 90 */},
+{ "[", "lB", /* 91 */},
+{ "\\", "rs", /* 92 */},
+{ "]", "rB", /* 93 */},
+{ "^", "a^", "ha", /* 94 */},
+{ "_", /* 95 */},
+{ "`", "ga", /* 96 */},
+{ "a", /* 97 */},
+{ "b", /* 98 */},
+{ "c", /* 99 */},
+{ "d", /* 100 */},
+{ "e", /* 101 */},
+{ "f", /* 102 */},
+{ "g", /* 103 */},
+{ "h", /* 104 */},
+{ "i", /* 105 */},
+{ "j", /* 106 */},
+{ "k", /* 107 */},
+{ "l", /* 108 */},
+{ "m", /* 109 */},
+{ "n", /* 110 */},
+{ "o", /* 111 */},
+{ "p", /* 112 */},
+{ "q", /* 113 */},
+{ "r", /* 114 */},
+{ "s", /* 115 */},
+{ "t", /* 116 */},
+{ "u", /* 117 */},
+{ "v", /* 118 */},
+{ "w", /* 119 */},
+{ "x", /* 120 */},
+{ "y", /* 121 */},
+{ "z", /* 122 */},
+{ "{", "lC", /* 123 */},
+{ "|", "ba", /* 124 */},
+{ "}", "rC", /* 125 */},
+{ "~", "a~", "ti", /* 126 */},
+{ 0, /* 127 */},
+{ 0, /* 128 */},
+{ 0, /* 129 */},
+{ 0, /* 130 */},
+{ 0, /* 131 */},
+{ 0, /* 132 */},
+{ 0, /* 133 */},
+{ 0, /* 134 */},
+{ 0, /* 135 */},
+{ 0, /* 136 */},
+{ 0, /* 137 */},
+{ 0, /* 138 */},
+{ 0, /* 139 */},
+{ 0, /* 140 */},
+{ 0, /* 141 */},
+{ 0, /* 142 */},
+{ 0, /* 143 */},
+{ 0, /* 144 */},
+{ 0, /* 145 */},
+{ 0, /* 146 */},
+{ 0, /* 147 */},
+{ 0, /* 148 */},
+{ 0, /* 149 */},
+{ 0, /* 150 */},
+{ 0, /* 151 */},
+{ 0, /* 152 */},
+{ 0, /* 153 */},
+{ 0, /* 154 */},
+{ 0, /* 155 */},
+{ 0, /* 156 */},
+{ 0, /* 157 */},
+{ 0, /* 158 */},
+{ 0, /* 159 */},
+{ 0, /* 160 */},
+{ "r!", /* 161 */},
+{ "ct", /* 162 */},
+{ "Po", /* 163 */},
+{ "Cs", /* 164 */},
+{ "Ye", /* 165 */},
+{ "bb", /* 166 */},
+{ "sc", /* 167 */},
+{ "ad", /* 168 */},
+{ "co", /* 169 */},
+{ "Of", /* 170 */},
+{ "Fo", /* 171 */},
+{ "tno", /* 172 */},
+{ "-", "hy", /* 173 */},
+{ "rg", /* 174 */},
+{ "a-", /* 175 */},
+{ "de", /* 176 */},
+{ "t+-", /* 177 */},
+{ "S2", /* 178 */},
+{ "S3", /* 179 */},
+{ "aa", /* 180 */},
+{ "mc", /* 181 */},
+{ "ps", /* 182 */},
+{ "pc", /* 183 */},
+{ "ac", /* 184 */},
+{ "S1", /* 185 */},
+{ "Om", /* 186 */},
+{ "Fc", /* 187 */},
+{ "14", /* 188 */},
+{ "12", /* 189 */},
+{ "34", /* 190 */},
+{ "r?", /* 191 */},
+{ "`A", /* 192 */},
+{ "'A", /* 193 */},
+{ "^A", /* 194 */},
+{ "~A", /* 195 */},
+{ ":A", /* 196 */},
+{ "oA", /* 197 */},
+{ "AE", /* 198 */},
+{ ",C", /* 199 */},
+{ "`E", /* 200 */},
+{ "'E", /* 201 */},
+{ "^E", /* 202 */},
+{ ":E", /* 203 */},
+{ "`I", /* 204 */},
+{ "'I", /* 205 */},
+{ "^I", /* 206 */},
+{ ":I", /* 207 */},
+{ "-D", /* 208 */},
+{ "~N", /* 209 */},
+{ "`O", /* 210 */},
+{ "'O", /* 211 */},
+{ "^O", /* 212 */},
+{ "~O", /* 213 */},
+{ ":O", /* 214 */},
+{ "tmu", /* 215 */},
+{ "/O", /* 216 */},
+{ "`U", /* 217 */},
+{ "'U", /* 218 */},
+{ "^U", /* 219 */},
+{ ":U", /* 220 */},
+{ "'Y", /* 221 */},
+{ "TP", /* 222 */},
+{ "ss", /* 223 */},
+{ "`a", /* 224 */},
+{ "'a", /* 225 */},
+{ "^a", /* 226 */},
+{ "~a", /* 227 */},
+{ ":a", /* 228 */},
+{ "oa", /* 229 */},
+{ "ae", /* 230 */},
+{ ",c", /* 231 */},
+{ "`e", /* 232 */},
+{ "'e", /* 233 */},
+{ "^e", /* 234 */},
+{ ":e", /* 235 */},
+{ "`i", /* 236 */},
+{ "'i", /* 237 */},
+{ "^i", /* 238 */},
+{ ":i", /* 239 */},
+{ "Sd", /* 240 */},
+{ "~n", /* 241 */},
+{ "`o", /* 242 */},
+{ "'o", /* 243 */},
+{ "^o", /* 244 */},
+{ "~o", /* 245 */},
+{ ":o", /* 246 */},
+{ "tdi", /* 247 */},
+{ "/o", /* 248 */},
+{ "`u", /* 249 */},
+{ "'u", /* 250 */},
+{ "^u", /* 251 */},
+{ ":u", /* 252 */},
+{ "'y", /* 253 */},
+{ "Tp", /* 254 */},
+{ ":y", /* 255 */},
+},
+ {0} /* buckets */};
+
+static DviCharNameMap Adobe_Symbol_map = {
+ "adobe-fontspecific",
+ 1,
+{
+{ 0, /* 0 */},
+{ 0, /* 1 */},
+{ 0, /* 2 */},
+{ 0, /* 3 */},
+{ 0, /* 4 */},
+{ 0, /* 5 */},
+{ 0, /* 6 */},
+{ 0, /* 7 */},
+{ 0, /* 8 */},
+{ 0, /* 9 */},
+{ 0, /* 10 */},
+{ 0, /* 11 */},
+{ 0, /* 12 */},
+{ 0, /* 13 */},
+{ 0, /* 14 */},
+{ 0, /* 15 */},
+{ 0, /* 16 */},
+{ 0, /* 17 */},
+{ 0, /* 18 */},
+{ 0, /* 19 */},
+{ 0, /* 20 */},
+{ 0, /* 21 */},
+{ 0, /* 22 */},
+{ 0, /* 23 */},
+{ 0, /* 24 */},
+{ 0, /* 25 */},
+{ 0, /* 26 */},
+{ 0, /* 27 */},
+{ 0, /* 28 */},
+{ 0, /* 29 */},
+{ 0, /* 30 */},
+{ 0, /* 31 */},
+{ 0, /* 32 */},
+{ "!", /* 33 */},
+{ "fa", /* 34 */},
+{ "#", "sh", /* 35 */},
+{ "te", /* 36 */},
+{ "%", /* 37 */},
+{ "&", /* 38 */},
+{ "st", /* 39 */},
+{ "(", /* 40 */},
+{ ")", /* 41 */},
+{ "**", /* 42 */},
+{ "+", "pl", /* 43 */},
+{ ",", /* 44 */},
+{ "\\-", "mi", /* 45 */},
+{ ".", /* 46 */},
+{ "/", "sl", /* 47 */},
+{ "0", /* 48 */},
+{ "1", /* 49 */},
+{ "2", /* 50 */},
+{ "3", /* 51 */},
+{ "4", /* 52 */},
+{ "5", /* 53 */},
+{ "6", /* 54 */},
+{ "7", /* 55 */},
+{ "8", /* 56 */},
+{ "9", /* 57 */},
+{ ":", /* 58 */},
+{ ";", /* 59 */},
+{ "<", /* 60 */},
+{ "=", "eq", /* 61 */},
+{ ">", /* 62 */},
+{ "?", /* 63 */},
+{ "=~", /* 64 */},
+{ "*A", /* 65 */},
+{ "*B", /* 66 */},
+{ "*X", /* 67 */},
+{ "*D", /* 68 */},
+{ "*E", /* 69 */},
+{ "*F", /* 70 */},
+{ "*G", /* 71 */},
+{ "*Y", /* 72 */},
+{ "*I", /* 73 */},
+{ "+h", /* 74 */},
+{ "*K", /* 75 */},
+{ "*L", /* 76 */},
+{ "*M", /* 77 */},
+{ "*N", /* 78 */},
+{ "*O", /* 79 */},
+{ "*P", /* 80 */},
+{ "*H", /* 81 */},
+{ "*R", /* 82 */},
+{ "*S", /* 83 */},
+{ "*T", /* 84 */},
+{ 0, /* 85 */},
+{ "ts", /* 86 */},
+{ "*W", /* 87 */},
+{ "*C", /* 88 */},
+{ "*Q", /* 89 */},
+{ "*Z", /* 90 */},
+{ "[", "lB", /* 91 */},
+{ "tf", "3d", /* 92 */},
+{ "]", "rB", /* 93 */},
+{ "pp", /* 94 */},
+{ "_", /* 95 */},
+{ "radicalex", /* 96 */},
+{ "*a", /* 97 */},
+{ "*b", /* 98 */},
+{ "*x", /* 99 */},
+{ "*d", /* 100 */},
+{ "*e", /* 101 */},
+{ "*f", /* 102 */},
+{ "*g", /* 103 */},
+{ "*y", /* 104 */},
+{ "*i", /* 105 */},
+{ "+f", /* 106 */},
+{ "*k", /* 107 */},
+{ "*l", /* 108 */},
+{ "*m", /* 109 */},
+{ "*n", /* 110 */},
+{ "*o", /* 111 */},
+{ "*p", /* 112 */},
+{ "*h", /* 113 */},
+{ "*r", /* 114 */},
+{ "*s", /* 115 */},
+{ "*t", /* 116 */},
+{ "*u", /* 117 */},
+{ "+p", /* 118 */},
+{ "*w", /* 119 */},
+{ "*c", /* 120 */},
+{ "*q", /* 121 */},
+{ "*z", /* 122 */},
+{ "lC", "{", /* 123 */},
+{ "ba", "|", /* 124 */},
+{ "rC", "}", /* 125 */},
+{ "ap", /* 126 */},
+{ 0, /* 127 */},
+{ 0, /* 128 */},
+{ 0, /* 129 */},
+{ 0, /* 130 */},
+{ 0, /* 131 */},
+{ 0, /* 132 */},
+{ 0, /* 133 */},
+{ 0, /* 134 */},
+{ 0, /* 135 */},
+{ 0, /* 136 */},
+{ 0, /* 137 */},
+{ 0, /* 138 */},
+{ 0, /* 139 */},
+{ 0, /* 140 */},
+{ 0, /* 141 */},
+{ 0, /* 142 */},
+{ 0, /* 143 */},
+{ 0, /* 144 */},
+{ 0, /* 145 */},
+{ 0, /* 146 */},
+{ 0, /* 147 */},
+{ 0, /* 148 */},
+{ 0, /* 149 */},
+{ 0, /* 150 */},
+{ 0, /* 151 */},
+{ 0, /* 152 */},
+{ 0, /* 153 */},
+{ 0, /* 154 */},
+{ 0, /* 155 */},
+{ 0, /* 156 */},
+{ 0, /* 157 */},
+{ 0, /* 158 */},
+{ 0, /* 159 */},
+{ 0, /* 160 */},
+{ "*U", /* 161 */},
+{ "fm", /* 162 */},
+{ "<=", /* 163 */},
+{ "f/", /* 164 */},
+{ "if", /* 165 */},
+{ "Fn", /* 166 */},
+{ "CL", /* 167 */},
+{ "DI", /* 168 */},
+{ "HE", /* 169 */},
+{ "SP", /* 170 */},
+{ "<>", /* 171 */},
+{ "<-", /* 172 */},
+{ "ua", "arrowverttp", /* 173 */},
+{ "->", /* 174 */},
+{ "da", "arrowvertbt", /* 175 */},
+{ "de", /* 176 */},
+{ "+-", /* 177 */},
+{ "sd", /* 178 */},
+{ ">=", /* 179 */},
+{ "mu", /* 180 */},
+{ "pt", /* 181 */},
+{ "pd", /* 182 */},
+{ "bu", /* 183 */},
+{ "di", /* 184 */},
+{ "!=", /* 185 */},
+{ "==", /* 186 */},
+{ "~=", "~~", /* 187 */},
+{ 0, /* 188 */},
+{ "arrowvertex", /* 189 */},
+{ "an", /* 190 */},
+{ "CR", /* 191 */},
+{ "Ah", /* 192 */},
+{ "Im", /* 193 */},
+{ "Re", /* 194 */},
+{ "wp", /* 195 */},
+{ "c*", /* 196 */},
+{ "c+", /* 197 */},
+{ "es", /* 198 */},
+{ "ca", /* 199 */},
+{ "cu", /* 200 */},
+{ "sp", /* 201 */},
+{ "ip", /* 202 */},
+{ "nb", /* 203 */},
+{ "sb", /* 204 */},
+{ "ib", /* 205 */},
+{ "mo", /* 206 */},
+{ "nm", /* 207 */},
+{ "/_", /* 208 */},
+{ "gr", /* 209 */},
+{ "rg", /* 210 */},
+{ "co", /* 211 */},
+{ "tm", /* 212 */},
+{ 0, /* 213 */},
+{ "sr", "sqrt", /* 214 */},
+{ "md", /* 215 */},
+{ "no", /* 216 */},
+{ "AN", /* 217 */},
+{ "OR", /* 218 */},
+{ "hA", /* 219 */},
+{ "lA", /* 220 */},
+{ "uA", /* 221 */},
+{ "rA", /* 222 */},
+{ "dA", /* 223 */},
+{ "lz", /* 224 */},
+{ "la", /* 225 */},
+{ 0, /* 226 */},
+{ 0, /* 227 */},
+{ 0, /* 228 */},
+{ 0, /* 229 */},
+{ "parenlefttp", /* 230 */},
+{ "parenleftex", /* 231 */},
+{ "parenleftbt", /* 232 */},
+{ "bracketlefttp", "lc", /* 233 */},
+{ "bracketleftex", /* 234 */},
+{ "bracketleftbt", "lf", /* 235 */},
+{ "bracelefttp", "lt", /* 236 */},
+{ "braceleftmid", "lk", /* 237 */},
+{ "braceleftbt", "lb", /* 238 */},
+{ "bracerightex", "braceleftex", "braceex", "bv", /* 239 */},
+{ 0, /* 240 */},
+{ "ra", /* 241 */},
+{ "is", "integral", /* 242 */},
+{ 0, /* 243 */},
+{ 0, /* 244 */},
+{ 0, /* 245 */},
+{ "parenrighttp", /* 246 */},
+{ "parenrightex", /* 247 */},
+{ "parenrightbt", /* 248 */},
+{ "bracketrighttp", "rc", /* 249 */},
+{ "bracketrightex", /* 250 */},
+{ "bracketrightbt", "rf", /* 251 */},
+{ "bracerighttp", "rt", /* 252 */},
+{ "bracerightmid", "rk", /* 253 */},
+{ "bracerightbt", "rb", /* 254 */},
+{ 0, /* 255 */},
+},
+ {0} /* buckets */};
+
+
+static void
+load_standard_maps (void)
+{
+ standard_maps_loaded = 1;
+ DviRegisterMap (&ISO8859_1_map);
+ DviRegisterMap (&Adobe_Symbol_map);
+}
diff --git a/src/libs/libxutil/XFontName.c b/src/libs/libxutil/XFontName.c
new file mode 100644
index 0000000..81ccdaa
--- /dev/null
+++ b/src/libs/libxutil/XFontName.c
@@ -0,0 +1,260 @@
+/* Copyright (C) 2014-2020 Free Software Foundation, Inc.
+
+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 of the License, 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.
+
+The GNU General Public License version 2 (GPL2) is available in the
+internet at <http://www.gnu.org/licenses/gpl-2.0.txt>. */
+
+/*
+ * XFontName.c
+ *
+ * build/parse X Font name strings
+ */
+
+#include <config.h>
+
+#include <X11/Xlib.h>
+#include <X11/Xos.h>
+#include "XFontName.h"
+#include <ctype.h>
+
+static char *
+extractStringField (char *name, char *buffer, int size,
+ unsigned int *attrp, unsigned int bit)
+{
+ char *buf = buffer;
+
+ if (!*name)
+ return 0;
+ while (*name && *name != '-' && size > 0) {
+ *buf++ = *name++;
+ --size;
+ }
+ if (size <= 0)
+ return 0;
+ *buf = '\0';
+ if (buffer[0] != '*' || buffer[1] != '\0')
+ *attrp |= bit;
+ if (*name == '-')
+ return name+1;
+ return name;
+}
+
+static char *
+extractUnsignedField (char *name, unsigned int *result,
+ unsigned int *attrp, unsigned int bit)
+{
+ char buf[256];
+ char *c;
+ unsigned int i;
+
+ name = extractStringField (name, buf, sizeof (buf), attrp, bit);
+ if (!name)
+ return 0;
+ if (!(*attrp & bit))
+ return name;
+ i = 0;
+ for (c = buf; *c; c++) {
+ if (!isdigit (*c))
+ return 0;
+ i = i * 10 + (*c - '0');
+ }
+ *result = i;
+ return name;
+}
+
+Bool
+XParseFontName (XFontNameString fontNameString, XFontName *fontName,
+ unsigned int *fontNameAttributes)
+{
+ char *name = fontNameString;
+ XFontName temp;
+ unsigned int attributes = 0;
+
+#define GetString(field,bit)\
+ if (!(name = extractStringField \
+ (name, temp.field, sizeof (temp.field),\
+ &attributes, bit))) \
+ return False;
+
+#define GetUnsigned(field,bit)\
+ if (!(name = extractUnsignedField \
+ (name, &temp.field, \
+ &attributes, bit))) \
+ return False;
+
+ GetString (Registry, FontNameRegistry)
+ GetString (Foundry, FontNameFoundry)
+ GetString (FamilyName, FontNameFamilyName)
+ GetString (WeightName, FontNameWeightName)
+ GetString (Slant, FontNameSlant)
+ GetString (SetwidthName, FontNameSetwidthName)
+ GetString (AddStyleName, FontNameAddStyleName)
+ GetUnsigned (PixelSize, FontNamePixelSize)
+ GetUnsigned (PointSize, FontNamePointSize)
+ GetUnsigned (ResolutionX, FontNameResolutionX)
+ GetUnsigned (ResolutionY, FontNameResolutionY)
+ GetString (Spacing, FontNameSpacing)
+ GetUnsigned (AverageWidth, FontNameAverageWidth)
+ GetString (CharSetRegistry, FontNameCharSetRegistry)
+ if (!*name) {
+ temp.CharSetEncoding[0] = '\0';
+ attributes |= FontNameCharSetEncoding;
+ } else {
+ GetString (CharSetEncoding, FontNameCharSetEncoding)
+ }
+ *fontName = temp;
+ *fontNameAttributes = attributes;
+ return True;
+}
+
+static char *
+utoa (unsigned int u, char *s, int size)
+{
+ char *t;
+
+ t = s + size;
+ *--t = '\0';
+ do
+ *--t = (u % 10) + '0';
+ while (u /= 10);
+ return t;
+}
+
+Bool
+XFormatFontName (XFontName *fontName, unsigned int fontNameAttributes,
+ XFontNameString fontNameString)
+{
+ char tmp[256];
+ char *name = tmp, *f;
+ int left = sizeof (tmp) - 1;
+ char number[32];
+
+#define PutString(field, bit)\
+ f = (fontNameAttributes & bit) ? \
+ fontName->field \
+ : (char *)"*"; \
+ if ((left -= strlen (f)) < 0) \
+ return False; \
+ while (*f) \
+ if ((*name++ = *f++) == '-') \
+ return False;
+#define PutHyphen()\
+ if (--left < 0) \
+ return False; \
+ *name++ = '-';
+
+#define PutUnsigned(field, bit) \
+ f = (fontNameAttributes & bit) ? \
+ utoa (fontName->field, number, sizeof (number)) \
+ : (char *)"*"; \
+ if ((left -= strlen (f)) < 0) \
+ return False; \
+ while (*f) \
+ *name++ = *f++;
+
+ PutString (Registry, FontNameRegistry)
+ PutHyphen ();
+ PutString (Foundry, FontNameFoundry)
+ PutHyphen ();
+ PutString (FamilyName, FontNameFamilyName)
+ PutHyphen ();
+ PutString (WeightName, FontNameWeightName)
+ PutHyphen ();
+ PutString (Slant, FontNameSlant)
+ PutHyphen ();
+ PutString (SetwidthName, FontNameSetwidthName)
+ PutHyphen ();
+ PutString (AddStyleName, FontNameAddStyleName)
+ PutHyphen ();
+ PutUnsigned (PixelSize, FontNamePixelSize)
+ PutHyphen ();
+ PutUnsigned (PointSize, FontNamePointSize)
+ PutHyphen ();
+ PutUnsigned (ResolutionX, FontNameResolutionX)
+ PutHyphen ();
+ PutUnsigned (ResolutionY, FontNameResolutionY)
+ PutHyphen ();
+ PutString (Spacing, FontNameSpacing)
+ PutHyphen ();
+ PutUnsigned (AverageWidth, FontNameAverageWidth)
+ PutHyphen ();
+ PutString (CharSetRegistry, FontNameCharSetRegistry)
+ PutHyphen ();
+ PutString (CharSetEncoding, FontNameCharSetEncoding)
+ *name = '\0';
+ strcpy (fontNameString, tmp);
+ return True;
+}
+
+Bool
+XCompareFontName (XFontName *name1, XFontName *name2,
+ unsigned int fontNameAttributes)
+{
+#define CompareString(field,bit) \
+ if (fontNameAttributes & bit) \
+ if (strcmp (name1->field, name2->field)) \
+ return False;
+
+#define CompareUnsigned(field,bit) \
+ if (fontNameAttributes & bit) \
+ if (name1->field != name2->field) \
+ return False;
+
+ CompareString (Registry, FontNameRegistry)
+ CompareString (Foundry, FontNameFoundry)
+ CompareString (FamilyName, FontNameFamilyName)
+ CompareString (WeightName, FontNameWeightName)
+ CompareString (Slant, FontNameSlant)
+ CompareString (SetwidthName, FontNameSetwidthName)
+ CompareString (AddStyleName, FontNameAddStyleName)
+ CompareUnsigned (PixelSize, FontNamePixelSize)
+ CompareUnsigned (PointSize, FontNamePointSize)
+ CompareUnsigned (ResolutionX, FontNameResolutionX)
+ CompareUnsigned (ResolutionY, FontNameResolutionY)
+ CompareString (Spacing, FontNameSpacing)
+ CompareUnsigned (AverageWidth, FontNameAverageWidth)
+ CompareString (CharSetRegistry, FontNameCharSetRegistry)
+ CompareString (CharSetEncoding, FontNameCharSetEncoding)
+ return True;
+}
+
+Bool
+XCopyFontName (XFontName *name1, XFontName *name2,
+ unsigned int fontNameAttributes)
+{
+#define CopyString(field,bit) \
+ if (fontNameAttributes & bit) \
+ strcpy (name2->field, name1->field);
+
+#define CopyUnsigned(field,bit) \
+ if (fontNameAttributes & bit) \
+ name2->field = name1->field;
+
+ CopyString (Registry, FontNameRegistry)
+ CopyString (Foundry, FontNameFoundry)
+ CopyString (FamilyName, FontNameFamilyName)
+ CopyString (WeightName, FontNameWeightName)
+ CopyString (Slant, FontNameSlant)
+ CopyString (SetwidthName, FontNameSetwidthName)
+ CopyString (AddStyleName, FontNameAddStyleName)
+ CopyUnsigned (PixelSize, FontNamePixelSize)
+ CopyUnsigned (PointSize, FontNamePointSize)
+ CopyUnsigned (ResolutionX, FontNameResolutionX)
+ CopyUnsigned (ResolutionY, FontNameResolutionY)
+ CopyString (Spacing, FontNameSpacing)
+ CopyUnsigned (AverageWidth, FontNameAverageWidth)
+ CopyString (CharSetRegistry, FontNameCharSetRegistry)
+ CopyString (CharSetEncoding, FontNameCharSetEncoding)
+ return True;
+}
diff --git a/src/libs/libxutil/libxutil.am b/src/libs/libxutil/libxutil.am
new file mode 100644
index 0000000..5963530
--- /dev/null
+++ b/src/libs/libxutil/libxutil.am
@@ -0,0 +1,35 @@
+# Automake rules for 'libxutil'
+#
+# Copyright (C) 2014-2020 Free Software Foundation, Inc.
+#
+# '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 of the License, 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 this program. If not, see
+# <http://www.gnu.org/licenses/gpl-2.0.html>.
+#
+########################################################################
+
+if !WITHOUT_X11
+noinst_LIBRARIES += libxutil.a
+libxutil_a_CPPFLAGS = $(AM_CPPFLAGS) $(X_CFLAGS)
+libxutil_a_SOURCES = \
+ src/libs/libxutil/DviChar.c \
+ src/libs/libxutil/XFontName.c \
+ src/libs/libxutil/xmalloc.c
+endif
+
+
+# Local Variables:
+# mode: makefile-automake
+# fill-column: 72
+# End:
+# vim: set autoindent filetype=automake textwidth=72:
diff --git a/src/libs/libxutil/xmalloc.c b/src/libs/libxutil/xmalloc.c
new file mode 100644
index 0000000..0852c62
--- /dev/null
+++ b/src/libs/libxutil/xmalloc.c
@@ -0,0 +1,28 @@
+/* Copyright (C) 2014-2020 Free Software Foundation, Inc.
+
+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 of the License, 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.
+
+The GNU General Public License version 2 (GPL2) is available in the
+internet at <http://www.gnu.org/licenses/gpl-2.0.txt>. */
+
+#include <config.h>
+
+#include <X11/Xlib.h>
+#include <X11/Intrinsic.h>
+
+char *xmalloc(int n);
+
+char *xmalloc(int n)
+{
+ return XtMalloc(n);
+}