summaryrefslogtreecommitdiffstats
path: root/libtecla-1.6.3/ioutil.c
diff options
context:
space:
mode:
Diffstat (limited to 'libtecla-1.6.3/ioutil.c')
-rw-r--r--libtecla-1.6.3/ioutil.c330
1 files changed, 330 insertions, 0 deletions
diff --git a/libtecla-1.6.3/ioutil.c b/libtecla-1.6.3/ioutil.c
new file mode 100644
index 0000000..3d002c7
--- /dev/null
+++ b/libtecla-1.6.3/ioutil.c
@@ -0,0 +1,330 @@
+/*
+ * Copyright (c) 2000, 2001, 2002, 2003, 2004, 2012 by Martin C. Shepherd.
+ *
+ * All rights reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, and/or sell copies of the Software, and to permit persons
+ * to whom the Software is furnished to do so, provided that the above
+ * copyright notice(s) and this permission notice appear in all copies of
+ * the Software and that both the above copyright notice(s) and this
+ * permission notice appear in supporting documentation.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
+ * OF THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
+ * HOLDERS INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL
+ * INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING
+ * FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
+ * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
+ * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Except as contained in this notice, the name of a copyright holder
+ * shall not be used in advertising or otherwise to promote the sale, use
+ * or other dealings in this Software without prior written authorization
+ * of the copyright holder.
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <ctype.h>
+#include <errno.h>
+
+#include "ioutil.h"
+
+static int _io_pad_line(GlWriteFn *write_fn, void *data, int c, int n);
+
+/*.......................................................................
+ * Display a left-justified string over multiple terminal lines,
+ * taking account of the specified width of the terminal. Optional
+ * indentation and an option prefix string can be specified to be
+ * displayed at the start of each new terminal line used, and if
+ * needed, a single paragraph can be broken across multiple calls.
+ * Note that literal newlines in the input string can be used to force
+ * a newline at any point, and that in order to allow individual
+ * paragraphs to be written using multiple calls to this function,
+ * unless an explicit newline character is specified at the end of the
+ * string, a newline will not be started at the end of the last word
+ * in the string. Note that when a new line is started between two
+ * words that are separated by spaces, those spaces are not output,
+ * whereas when a new line is started because a newline character was
+ * found in the string, only the spaces before the newline character
+ * are discarded.
+ *
+ * Input:
+ * write_fn GlWriteFn * The callback function to use to write the
+ * output.
+ * data void * A pointer to arbitrary data to be passed to
+ * write_fn() whenever it is called.
+ * fp FILE * The stdio stream to write to.
+ * indentation int The number of fill characters to use to
+ * indent the start of each new terminal line.
+ * prefix const char * An optional prefix string to write after the
+ * indentation margin at the start of each new
+ * terminal line. You can specify NULL if no
+ * prefix is required.
+ * suffix const char * An optional suffix string to draw at the end
+ * of the terminal line. The line will be padded
+ * where necessary to ensure that the suffix ends
+ * in the last column of the terminal line. If
+ * no suffix is desired, specify NULL.
+ * fill_char int The padding character to use when indenting
+ * and filling up to the suffix.
+ * term_width int The width of the terminal being written to.
+ * start int The number of characters already written to
+ * the start of the current terminal line. This
+ * is primarily used to allow individual
+ * paragraphs to be written over multiple calls
+ * to this function, but can also be used to
+ * allow you to start the first line of a
+ * paragraph with a different prefix or
+ * indentation than those specified above.
+ * string const char * The string to be written.
+ * Output:
+ * return int On error -1 is returned. Otherwise the
+ * return value is the terminal column index at
+ * which the cursor was left after writing the
+ * final word in the string. Successful return
+ * values can thus be passed verbatim to the
+ * 'start' arguments of subsequent calls to
+ * _io_display_text() to allow the printing of a
+ * paragraph to be broken across multiple calls
+ * to _io_display_text().
+ */
+int _io_display_text(GlWriteFn *write_fn, void *data, int indentation,
+ const char *prefix, const char *suffix, int fill_char,
+ int term_width, int start, const char *string)
+{
+ int ndone; /* The number of characters written from string[] */
+ int nnew; /* The number of characters to be displayed next */
+ int was_space; /* True if the previous character was a space or tab */
+ int last = start; /* The column number of the last character written */
+ int prefix_len; /* The length of the optional line prefix string */
+ int suffix_len; /* The length of the optional line prefix string */
+ int margin_width; /* The total number of columns used by the indentation */
+ /* margin and the prefix string. */
+ int i;
+/*
+ * Check the arguments?
+ */
+ if(!string || !write_fn) {
+ errno = EINVAL;
+ return -1;
+ };
+/*
+ * Enforce sensible values on the arguments.
+ */
+ if(term_width < 0)
+ term_width = 0;
+ if(indentation > term_width)
+ indentation = term_width;
+ else if(indentation < 0)
+ indentation = 0;
+ if(start > term_width)
+ start = term_width;
+ else if(start < 0)
+ start = 0;
+/*
+ * Get the length of the prefix string.
+ */
+ prefix_len = prefix ? strlen(prefix) : 0;
+/*
+ * Get the length of the suffix string.
+ */
+ suffix_len = suffix ? strlen(suffix) : 0;
+/*
+ * How many characters are devoted to indenting and prefixing each line?
+ */
+ margin_width = indentation + prefix_len;
+/*
+ * Write as many terminal lines as are needed to display the whole string.
+ */
+ for(ndone=0; string[ndone]; start=0) {
+ last = start;
+/*
+ * Write spaces from the current position in the terminal line to the
+ * width of the requested indentation margin.
+ */
+ if(indentation > 0 && last < indentation) {
+ if(_io_pad_line(write_fn, data, fill_char, indentation - last))
+ return -1;
+ last = indentation;
+ };
+/*
+ * If a prefix string has been specified, display it unless we have
+ * passed where it should end in the terminal output line.
+ */
+ if(prefix_len > 0 && last < margin_width) {
+ int pstart = last - indentation;
+ int plen = prefix_len - pstart;
+ if(write_fn(data, prefix+pstart, plen) != plen)
+ return -1;
+ last = margin_width;
+ };
+/*
+ * Locate the end of the last complete word in the string before
+ * (term_width - start) characters have been seen. To handle the case
+ * where a single word is wider than the available space after the
+ * indentation and prefix margins, always make sure that at least one
+ * word is printed after the margin, regardless of whether it won't
+ * fit on the line. The two exceptions to this rule are if an embedded
+ * newline is found in the string or the end of the string is reached
+ * before any word has been seen.
+ */
+ nnew = 0;
+ was_space = 0;
+ for(i=ndone; string[i] && (last+i-ndone < term_width - suffix_len ||
+ (nnew==0 && last==margin_width)); i++) {
+ if(string[i] == '\n') {
+ if(!was_space)
+ nnew = i-ndone;
+ break;
+ } else if(isspace((int) string[i])) {
+ if(!was_space) {
+ nnew = i-ndone+1;
+ was_space = 1;
+ };
+ } else {
+ was_space = 0;
+ };
+ };
+/*
+ * Does the end of the string delimit the last word that will fit on the
+ * output line?
+ */
+ if(nnew==0 && string[i] == '\0')
+ nnew = i-ndone;
+/*
+ * Write the new line.
+ */
+ if(write_fn(data, string+ndone, nnew) != nnew)
+ return -1;
+ ndone += nnew;
+ last += nnew;
+/*
+ * Start a newline unless we have reached the end of the input string.
+ * In the latter case, in order to give the caller the chance to
+ * concatenate multiple calls to _io_display_text(), omit the newline,
+ * leaving it up to the caller to write this.
+ */
+ if(string[ndone] != '\0') {
+/*
+ * If a suffix has been provided, pad out the end of the line with spaces
+ * such that the suffix will end in the right-most terminal column.
+ */
+ if(suffix_len > 0) {
+ int npad = term_width - suffix_len - last;
+ if(npad > 0 && _io_pad_line(write_fn, data, fill_char, npad))
+ return -1;
+ last += npad;
+ if(write_fn(data, suffix, suffix_len) != suffix_len)
+ return -1;
+ last += suffix_len;
+ };
+/*
+ * Start a new line.
+ */
+ if(write_fn(data, "\n", 1) != 1)
+ return -1;
+/*
+ * Skip any spaces and tabs that follow the last word that was written.
+ */
+ while(string[ndone] && isspace((int)string[ndone]) &&
+ string[ndone] != '\n')
+ ndone++;
+/*
+ * If the terminating character was a literal newline character,
+ * skip it in the input string, since we just wrote it.
+ */
+ if(string[ndone] == '\n')
+ ndone++;
+ last = 0;
+ };
+ };
+/*
+ * Return the column number of the last character printed.
+ */
+ return last;
+}
+
+/*.......................................................................
+ * Write a given number of spaces to the specified stdio output string.
+ *
+ * Input:
+ * write_fn GlWriteFn * The callback function to use to write the
+ * output.
+ * data void * A pointer to arbitrary data to be passed to
+ * write_fn() whenever it is called.
+ * c int The padding character.
+ * n int The number of spaces to be written.
+ * Output:
+ * return int 0 - OK.
+ * 1 - Error.
+ */
+static int _io_pad_line(GlWriteFn *write_fn, void *data, int c, int n)
+{
+ enum {FILL_SIZE=20};
+ char fill[FILL_SIZE+1];
+/*
+ * Fill the buffer with the specified padding character.
+ */
+ memset(fill, c, FILL_SIZE);
+ fill[FILL_SIZE] = '\0';
+/*
+ * Write the spaces using the above literal string of spaces as
+ * many times as needed to output the requested number of spaces.
+ */
+ while(n > 0) {
+ int nnew = n <= FILL_SIZE ? n : FILL_SIZE;
+ if(write_fn(data, fill, nnew) != nnew)
+ return 1;
+ n -= nnew;
+ };
+ return 0;
+}
+
+/*.......................................................................
+ * The following is an output callback function which uses fwrite()
+ * to write to the stdio stream specified via its callback data argument.
+ *
+ * Input:
+ * data void * The stdio stream to write to, specified via a
+ * (FILE *) pointer cast to (void *).
+ * s const char * The string to be written.
+ * n int The length of the prefix of s[] to attempt to
+ * write.
+ * Output:
+ * return int The number of characters written from s[]. This
+ * should normally be a number in the range 0 to n.
+ * To signal that an I/O error occurred, return -1.
+ */
+GL_WRITE_FN(_io_write_stdio)
+{
+ int ndone; /* The total number of characters written */
+ int nnew; /* The number of characters written in the latest write */
+/*
+ * The callback data is the stdio stream to write to.
+ */
+ FILE *fp = (FILE *) data;
+/*
+ * Because of signals we may need to do more than one write to output
+ * the whole string.
+ */
+ for(ndone=0; ndone<n; ndone += nnew) {
+ int nmore = n - ndone;
+ nnew = fwrite(s, sizeof(char), nmore, fp);
+ if(nnew < nmore) {
+ if(errno == EINTR)
+ clearerr(fp);
+ else
+ return ferror(fp) ? -1 : ndone + nnew;
+ };
+ };
+ return ndone;
+}
+