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