17c478bd9Sstevel@tonic-gate /*
27c478bd9Sstevel@tonic-gate  * Copyright (c) 2000, 2001, 2002, 2003, 2004 by Martin C. Shepherd.
3*1da57d55SToomas Soome  *
47c478bd9Sstevel@tonic-gate  * All rights reserved.
5*1da57d55SToomas Soome  *
67c478bd9Sstevel@tonic-gate  * Permission is hereby granted, free of charge, to any person obtaining a
77c478bd9Sstevel@tonic-gate  * copy of this software and associated documentation files (the
87c478bd9Sstevel@tonic-gate  * "Software"), to deal in the Software without restriction, including
97c478bd9Sstevel@tonic-gate  * without limitation the rights to use, copy, modify, merge, publish,
107c478bd9Sstevel@tonic-gate  * distribute, and/or sell copies of the Software, and to permit persons
117c478bd9Sstevel@tonic-gate  * to whom the Software is furnished to do so, provided that the above
127c478bd9Sstevel@tonic-gate  * copyright notice(s) and this permission notice appear in all copies of
137c478bd9Sstevel@tonic-gate  * the Software and that both the above copyright notice(s) and this
147c478bd9Sstevel@tonic-gate  * permission notice appear in supporting documentation.
15*1da57d55SToomas Soome  *
167c478bd9Sstevel@tonic-gate  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
177c478bd9Sstevel@tonic-gate  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
187c478bd9Sstevel@tonic-gate  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
197c478bd9Sstevel@tonic-gate  * OF THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
207c478bd9Sstevel@tonic-gate  * HOLDERS INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL
217c478bd9Sstevel@tonic-gate  * INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING
227c478bd9Sstevel@tonic-gate  * FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
237c478bd9Sstevel@tonic-gate  * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
247c478bd9Sstevel@tonic-gate  * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
25*1da57d55SToomas Soome  *
267c478bd9Sstevel@tonic-gate  * Except as contained in this notice, the name of a copyright holder
277c478bd9Sstevel@tonic-gate  * shall not be used in advertising or otherwise to promote the sale, use
287c478bd9Sstevel@tonic-gate  * or other dealings in this Software without prior written authorization
297c478bd9Sstevel@tonic-gate  * of the copyright holder.
307c478bd9Sstevel@tonic-gate  */
317c478bd9Sstevel@tonic-gate 
327c478bd9Sstevel@tonic-gate #include <stdlib.h>
337c478bd9Sstevel@tonic-gate #include <stdio.h>
347c478bd9Sstevel@tonic-gate #include <string.h>
357c478bd9Sstevel@tonic-gate #include <ctype.h>
367c478bd9Sstevel@tonic-gate #include <errno.h>
377c478bd9Sstevel@tonic-gate 
387c478bd9Sstevel@tonic-gate #include "ioutil.h"
397c478bd9Sstevel@tonic-gate 
407c478bd9Sstevel@tonic-gate static int _io_pad_line(GlWriteFn *write_fn, void *data, int c, int n);
417c478bd9Sstevel@tonic-gate 
427c478bd9Sstevel@tonic-gate /*.......................................................................
437c478bd9Sstevel@tonic-gate  * Display a left-justified string over multiple terminal lines,
447c478bd9Sstevel@tonic-gate  * taking account of the specified width of the terminal. Optional
457c478bd9Sstevel@tonic-gate  * indentation and an option prefix string can be specified to be
467c478bd9Sstevel@tonic-gate  * displayed at the start of each new terminal line used, and if
477c478bd9Sstevel@tonic-gate  * needed, a single paragraph can be broken across multiple calls.
487c478bd9Sstevel@tonic-gate  * Note that literal newlines in the input string can be used to force
497c478bd9Sstevel@tonic-gate  * a newline at any point, and that in order to allow individual
507c478bd9Sstevel@tonic-gate  * paragraphs to be written using multiple calls to this function,
517c478bd9Sstevel@tonic-gate  * unless an explicit newline character is specified at the end of the
527c478bd9Sstevel@tonic-gate  * string, a newline will not be started at the end of the last word
537c478bd9Sstevel@tonic-gate  * in the string. Note that when a new line is started between two
547c478bd9Sstevel@tonic-gate  * words that are separated by spaces, those spaces are not output,
557c478bd9Sstevel@tonic-gate  * whereas when a new line is started because a newline character was
567c478bd9Sstevel@tonic-gate  * found in the string, only the spaces before the newline character
577c478bd9Sstevel@tonic-gate  * are discarded.
587c478bd9Sstevel@tonic-gate  *
597c478bd9Sstevel@tonic-gate  * Input:
607c478bd9Sstevel@tonic-gate  *  write_fn  GlWriteFn *  The callback function to use to write the
617c478bd9Sstevel@tonic-gate  *                         output.
627c478bd9Sstevel@tonic-gate  *  data           void *  A pointer to arbitrary data to be passed to
637c478bd9Sstevel@tonic-gate  *                         write_fn() whenever it is called.
647c478bd9Sstevel@tonic-gate  *  fp             FILE *  The stdio stream to write to.
657c478bd9Sstevel@tonic-gate  *  indentation     int    The number of fill characters to use to
667c478bd9Sstevel@tonic-gate  *                        indent the start of each new terminal line.
677c478bd9Sstevel@tonic-gate  *  prefix   const char *  An optional prefix string to write after the
687c478bd9Sstevel@tonic-gate  *                         indentation margin at the start of each new
697c478bd9Sstevel@tonic-gate  *                         terminal line. You can specify NULL if no
707c478bd9Sstevel@tonic-gate  *                         prefix is required.
717c478bd9Sstevel@tonic-gate  *  suffix   const char *  An optional suffix string to draw at the end
727c478bd9Sstevel@tonic-gate  *                         of the terminal line. The line will be padded
737c478bd9Sstevel@tonic-gate  *                         where necessary to ensure that the suffix ends
747c478bd9Sstevel@tonic-gate  *                         in the last column of the terminal line. If
757c478bd9Sstevel@tonic-gate  *                         no suffix is desired, specify NULL.
767c478bd9Sstevel@tonic-gate  *  fill_char       int    The padding character to use when indenting
777c478bd9Sstevel@tonic-gate  *                         and filling up to the suffix.
787c478bd9Sstevel@tonic-gate  *  term_width      int    The width of the terminal being written to.
797c478bd9Sstevel@tonic-gate  *  start           int    The number of characters already written to
807c478bd9Sstevel@tonic-gate  *                         the start of the current terminal line. This
817c478bd9Sstevel@tonic-gate  *                         is primarily used to allow individual
827c478bd9Sstevel@tonic-gate  *                         paragraphs to be written over multiple calls
837c478bd9Sstevel@tonic-gate  *                         to this function, but can also be used to
847c478bd9Sstevel@tonic-gate  *                         allow you to start the first line of a
857c478bd9Sstevel@tonic-gate  *                         paragraph with a different prefix or
867c478bd9Sstevel@tonic-gate  *                         indentation than those specified above.
877c478bd9Sstevel@tonic-gate  *  string   const char *  The string to be written.
887c478bd9Sstevel@tonic-gate  * Output:
897c478bd9Sstevel@tonic-gate  *  return          int    On error -1 is returned. Otherwise the
907c478bd9Sstevel@tonic-gate  *                         return value is the terminal column index at
917c478bd9Sstevel@tonic-gate  *                         which the cursor was left after writing the
927c478bd9Sstevel@tonic-gate  *                         final word in the string. Successful return
937c478bd9Sstevel@tonic-gate  *                         values can thus be passed verbatim to the
947c478bd9Sstevel@tonic-gate  *                         'start' arguments of subsequent calls to
957c478bd9Sstevel@tonic-gate  *                         _io_display_text() to allow the printing of a
967c478bd9Sstevel@tonic-gate  *                         paragraph to be broken across multiple calls
977c478bd9Sstevel@tonic-gate  *                         to _io_display_text().
987c478bd9Sstevel@tonic-gate  */
_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)997c478bd9Sstevel@tonic-gate int _io_display_text(GlWriteFn *write_fn, void *data, int indentation,
1007c478bd9Sstevel@tonic-gate 		     const char *prefix, const char *suffix, int fill_char,
1017c478bd9Sstevel@tonic-gate 		     int term_width, int start, const char *string)
1027c478bd9Sstevel@tonic-gate {
1037c478bd9Sstevel@tonic-gate   int ndone;        /* The number of characters written from string[] */
1047c478bd9Sstevel@tonic-gate   int nnew;         /* The number of characters to be displayed next */
1057c478bd9Sstevel@tonic-gate   int was_space;    /* True if the previous character was a space or tab */
1067c478bd9Sstevel@tonic-gate   int last = start; /* The column number of the last character written */
1077c478bd9Sstevel@tonic-gate   int prefix_len;   /* The length of the optional line prefix string */
1087c478bd9Sstevel@tonic-gate   int suffix_len;   /* The length of the optional line prefix string */
1097c478bd9Sstevel@tonic-gate   int margin_width; /* The total number of columns used by the indentation */
1107c478bd9Sstevel@tonic-gate                     /*  margin and the prefix string. */
1117c478bd9Sstevel@tonic-gate   int i;
1127c478bd9Sstevel@tonic-gate /*
1137c478bd9Sstevel@tonic-gate  * Check the arguments?
1147c478bd9Sstevel@tonic-gate  */
1157c478bd9Sstevel@tonic-gate   if(!string || !write_fn) {
1167c478bd9Sstevel@tonic-gate     errno = EINVAL;
1177c478bd9Sstevel@tonic-gate     return -1;
1187c478bd9Sstevel@tonic-gate   };
1197c478bd9Sstevel@tonic-gate /*
1207c478bd9Sstevel@tonic-gate  * Enforce sensible values on the arguments.
1217c478bd9Sstevel@tonic-gate  */
1227c478bd9Sstevel@tonic-gate   if(term_width < 0)
1237c478bd9Sstevel@tonic-gate     term_width = 0;
1247c478bd9Sstevel@tonic-gate   if(indentation > term_width)
1257c478bd9Sstevel@tonic-gate     indentation = term_width;
1267c478bd9Sstevel@tonic-gate   else if(indentation < 0)
1277c478bd9Sstevel@tonic-gate     indentation = 0;
1287c478bd9Sstevel@tonic-gate   if(start > term_width)
1297c478bd9Sstevel@tonic-gate     start = term_width;
1307c478bd9Sstevel@tonic-gate   else if(start < 0)
1317c478bd9Sstevel@tonic-gate     start = 0;
1327c478bd9Sstevel@tonic-gate /*
1337c478bd9Sstevel@tonic-gate  * Get the length of the prefix string.
1347c478bd9Sstevel@tonic-gate  */
1357c478bd9Sstevel@tonic-gate   prefix_len = prefix ? strlen(prefix) : 0;
1367c478bd9Sstevel@tonic-gate /*
1377c478bd9Sstevel@tonic-gate  * Get the length of the suffix string.
1387c478bd9Sstevel@tonic-gate  */
1397c478bd9Sstevel@tonic-gate   suffix_len = suffix ? strlen(suffix) : 0;
1407c478bd9Sstevel@tonic-gate /*
1417c478bd9Sstevel@tonic-gate  * How many characters are devoted to indenting and prefixing each line?
1427c478bd9Sstevel@tonic-gate  */
1437c478bd9Sstevel@tonic-gate   margin_width = indentation + prefix_len;
1447c478bd9Sstevel@tonic-gate /*
1457c478bd9Sstevel@tonic-gate  * Write as many terminal lines as are needed to display the whole string.
1467c478bd9Sstevel@tonic-gate  */
1477c478bd9Sstevel@tonic-gate   for(ndone=0; string[ndone]; start=0) {
1487c478bd9Sstevel@tonic-gate     last = start;
1497c478bd9Sstevel@tonic-gate /*
1507c478bd9Sstevel@tonic-gate  * Write spaces from the current position in the terminal line to the
1517c478bd9Sstevel@tonic-gate  * width of the requested indentation margin.
1527c478bd9Sstevel@tonic-gate  */
1537c478bd9Sstevel@tonic-gate     if(indentation > 0 && last < indentation) {
1547c478bd9Sstevel@tonic-gate       if(_io_pad_line(write_fn, data, fill_char, indentation - last))
1557c478bd9Sstevel@tonic-gate 	return -1;
1567c478bd9Sstevel@tonic-gate       last = indentation;
1577c478bd9Sstevel@tonic-gate     };
1587c478bd9Sstevel@tonic-gate /*
1597c478bd9Sstevel@tonic-gate  * If a prefix string has been specified, display it unless we have
1607c478bd9Sstevel@tonic-gate  * passed where it should end in the terminal output line.
1617c478bd9Sstevel@tonic-gate  */
1627c478bd9Sstevel@tonic-gate     if(prefix_len > 0 && last < margin_width) {
1637c478bd9Sstevel@tonic-gate       int pstart = last - indentation;
1647c478bd9Sstevel@tonic-gate       int plen = prefix_len - pstart;
1657c478bd9Sstevel@tonic-gate       if(write_fn(data, prefix+pstart, plen) != plen)
1667c478bd9Sstevel@tonic-gate 	return -1;
1677c478bd9Sstevel@tonic-gate       last = margin_width;
1687c478bd9Sstevel@tonic-gate     };
1697c478bd9Sstevel@tonic-gate /*
1707c478bd9Sstevel@tonic-gate  * Locate the end of the last complete word in the string before
1717c478bd9Sstevel@tonic-gate  * (term_width - start) characters have been seen. To handle the case
1727c478bd9Sstevel@tonic-gate  * where a single word is wider than the available space after the
1737c478bd9Sstevel@tonic-gate  * indentation and prefix margins, always make sure that at least one
1747c478bd9Sstevel@tonic-gate  * word is printed after the margin, regardless of whether it won't
1757c478bd9Sstevel@tonic-gate  * fit on the line. The two exceptions to this rule are if an embedded
1767c478bd9Sstevel@tonic-gate  * newline is found in the string or the end of the string is reached
1777c478bd9Sstevel@tonic-gate  * before any word has been seen.
1787c478bd9Sstevel@tonic-gate  */
1797c478bd9Sstevel@tonic-gate     nnew = 0;
1807c478bd9Sstevel@tonic-gate     was_space = 0;
1817c478bd9Sstevel@tonic-gate     for(i=ndone; string[i] && (last+i-ndone < term_width - suffix_len ||
1827c478bd9Sstevel@tonic-gate 			   (nnew==0 && last==margin_width)); i++) {
1837c478bd9Sstevel@tonic-gate       if(string[i] == '\n') {
1847c478bd9Sstevel@tonic-gate 	if(!was_space)
1857c478bd9Sstevel@tonic-gate 	  nnew = i-ndone;
1867c478bd9Sstevel@tonic-gate 	break;
1877c478bd9Sstevel@tonic-gate       } else if(isspace((int) string[i])) {
1887c478bd9Sstevel@tonic-gate 	if(!was_space) {
1897c478bd9Sstevel@tonic-gate 	  nnew = i-ndone+1;
1907c478bd9Sstevel@tonic-gate 	  was_space = 1;
1917c478bd9Sstevel@tonic-gate 	};
1927c478bd9Sstevel@tonic-gate       } else {
1937c478bd9Sstevel@tonic-gate 	was_space = 0;
1947c478bd9Sstevel@tonic-gate       };
1957c478bd9Sstevel@tonic-gate     };
1967c478bd9Sstevel@tonic-gate /*
1977c478bd9Sstevel@tonic-gate  * Does the end of the string delimit the last word that will fit on the
1987c478bd9Sstevel@tonic-gate  * output line?
1997c478bd9Sstevel@tonic-gate  */
2007c478bd9Sstevel@tonic-gate     if(nnew==0 && string[i] == '\0')
2017c478bd9Sstevel@tonic-gate       nnew = i-ndone;
2027c478bd9Sstevel@tonic-gate /*
2037c478bd9Sstevel@tonic-gate  * Write the new line.
2047c478bd9Sstevel@tonic-gate  */
2057c478bd9Sstevel@tonic-gate     if(write_fn(data, string+ndone, nnew) != nnew)
2067c478bd9Sstevel@tonic-gate       return -1;
2077c478bd9Sstevel@tonic-gate     ndone += nnew;
2087c478bd9Sstevel@tonic-gate     last += nnew;
2097c478bd9Sstevel@tonic-gate /*
2107c478bd9Sstevel@tonic-gate  * Start a newline unless we have reached the end of the input string.
2117c478bd9Sstevel@tonic-gate  * In the latter case, in order to give the caller the chance to
2127c478bd9Sstevel@tonic-gate  * concatenate multiple calls to _io_display_text(), omit the newline,
2137c478bd9Sstevel@tonic-gate  * leaving it up to the caller to write this.
2147c478bd9Sstevel@tonic-gate  */
2157c478bd9Sstevel@tonic-gate     if(string[ndone] != '\0') {
2167c478bd9Sstevel@tonic-gate /*
2177c478bd9Sstevel@tonic-gate  * If a suffix has been provided, pad out the end of the line with spaces
2187c478bd9Sstevel@tonic-gate  * such that the suffix will end in the right-most terminal column.
2197c478bd9Sstevel@tonic-gate  */
2207c478bd9Sstevel@tonic-gate       if(suffix_len > 0) {
2217c478bd9Sstevel@tonic-gate 	int npad = term_width - suffix_len - last;
2227c478bd9Sstevel@tonic-gate 	if(npad > 0 && _io_pad_line(write_fn, data, fill_char, npad))
2237c478bd9Sstevel@tonic-gate 	  return -1;
2247c478bd9Sstevel@tonic-gate 	last += npad;
2257c478bd9Sstevel@tonic-gate 	if(write_fn(data, suffix, suffix_len) != suffix_len)
2267c478bd9Sstevel@tonic-gate 	  return -1;
2277c478bd9Sstevel@tonic-gate 	last += suffix_len;
2287c478bd9Sstevel@tonic-gate       };
2297c478bd9Sstevel@tonic-gate /*
2307c478bd9Sstevel@tonic-gate  * Start a new line.
2317c478bd9Sstevel@tonic-gate  */
2327c478bd9Sstevel@tonic-gate       if(write_fn(data, "\n",  1) != 1)
2337c478bd9Sstevel@tonic-gate 	return -1;
2347c478bd9Sstevel@tonic-gate /*
2357c478bd9Sstevel@tonic-gate  * Skip any spaces and tabs that follow the last word that was written.
2367c478bd9Sstevel@tonic-gate  */
2377c478bd9Sstevel@tonic-gate       while(string[ndone] && isspace((int)string[ndone]) &&
2387c478bd9Sstevel@tonic-gate 	    string[ndone] != '\n')
2397c478bd9Sstevel@tonic-gate 	ndone++;
2407c478bd9Sstevel@tonic-gate /*
2417c478bd9Sstevel@tonic-gate  * If the terminating character was a literal newline character,
2427c478bd9Sstevel@tonic-gate  * skip it in the input string, since we just wrote it.
2437c478bd9Sstevel@tonic-gate  */
2447c478bd9Sstevel@tonic-gate       if(string[ndone] == '\n')
2457c478bd9Sstevel@tonic-gate 	ndone++;
2467c478bd9Sstevel@tonic-gate       last = 0;
2477c478bd9Sstevel@tonic-gate     };
2487c478bd9Sstevel@tonic-gate   };
2497c478bd9Sstevel@tonic-gate /*
2507c478bd9Sstevel@tonic-gate  * Return the column number of the last character printed.
2517c478bd9Sstevel@tonic-gate  */
2527c478bd9Sstevel@tonic-gate   return last;
2537c478bd9Sstevel@tonic-gate }
2547c478bd9Sstevel@tonic-gate 
2557c478bd9Sstevel@tonic-gate /*.......................................................................
2567c478bd9Sstevel@tonic-gate  * Write a given number of spaces to the specified stdio output string.
2577c478bd9Sstevel@tonic-gate  *
2587c478bd9Sstevel@tonic-gate  * Input:
2597c478bd9Sstevel@tonic-gate  *  write_fn  GlWriteFn *  The callback function to use to write the
2607c478bd9Sstevel@tonic-gate  *                         output.
2617c478bd9Sstevel@tonic-gate  *  data           void *  A pointer to arbitrary data to be passed to
2627c478bd9Sstevel@tonic-gate  *                         write_fn() whenever it is called.
2637c478bd9Sstevel@tonic-gate  *  c               int    The padding character.
2647c478bd9Sstevel@tonic-gate  *  n               int    The number of spaces to be written.
2657c478bd9Sstevel@tonic-gate  * Output:
2667c478bd9Sstevel@tonic-gate  *  return          int    0 - OK.
2677c478bd9Sstevel@tonic-gate  *                         1 - Error.
2687c478bd9Sstevel@tonic-gate  */
_io_pad_line(GlWriteFn * write_fn,void * data,int c,int n)2697c478bd9Sstevel@tonic-gate static int _io_pad_line(GlWriteFn *write_fn, void *data, int c, int n)
2707c478bd9Sstevel@tonic-gate {
2717c478bd9Sstevel@tonic-gate   enum {FILL_SIZE=20};
2727c478bd9Sstevel@tonic-gate   char fill[FILL_SIZE+1];
2737c478bd9Sstevel@tonic-gate /*
2747c478bd9Sstevel@tonic-gate  * Fill the buffer with the specified padding character.
2757c478bd9Sstevel@tonic-gate  */
2767c478bd9Sstevel@tonic-gate   memset(fill, c, FILL_SIZE);
2777c478bd9Sstevel@tonic-gate   fill[FILL_SIZE] = '\0';
2787c478bd9Sstevel@tonic-gate /*
2797c478bd9Sstevel@tonic-gate  * Write the spaces using the above literal string of spaces as
2807c478bd9Sstevel@tonic-gate  * many times as needed to output the requested number of spaces.
2817c478bd9Sstevel@tonic-gate  */
2827c478bd9Sstevel@tonic-gate   while(n > 0) {
2837c478bd9Sstevel@tonic-gate     int nnew = n <= FILL_SIZE ? n : FILL_SIZE;
2847c478bd9Sstevel@tonic-gate     if(write_fn(data, fill, nnew) != nnew)
2857c478bd9Sstevel@tonic-gate       return 1;
2867c478bd9Sstevel@tonic-gate     n -= nnew;
2877c478bd9Sstevel@tonic-gate   };
2887c478bd9Sstevel@tonic-gate   return 0;
2897c478bd9Sstevel@tonic-gate }
2907c478bd9Sstevel@tonic-gate 
2917c478bd9Sstevel@tonic-gate /*.......................................................................
2927c478bd9Sstevel@tonic-gate  * The following is an output callback function which uses fwrite()
2937c478bd9Sstevel@tonic-gate  * to write to the stdio stream specified via its callback data argument.
2947c478bd9Sstevel@tonic-gate  *
2957c478bd9Sstevel@tonic-gate  * Input:
2967c478bd9Sstevel@tonic-gate  *  data     void *  The stdio stream to write to, specified via a
2977c478bd9Sstevel@tonic-gate  *                   (FILE *) pointer cast to (void *).
2987c478bd9Sstevel@tonic-gate  *  s  const char *  The string to be written.
2997c478bd9Sstevel@tonic-gate  *  n         int    The length of the prefix of s[] to attempt to
3007c478bd9Sstevel@tonic-gate  *                   write.
3017c478bd9Sstevel@tonic-gate  * Output:
3027c478bd9Sstevel@tonic-gate  *  return    int    The number of characters written from s[]. This
3037c478bd9Sstevel@tonic-gate  *                   should normally be a number in the range 0 to n.
3047c478bd9Sstevel@tonic-gate  *                   To signal that an I/O error occurred, return -1.
3057c478bd9Sstevel@tonic-gate  */
GL_WRITE_FN(_io_write_stdio)3067c478bd9Sstevel@tonic-gate GL_WRITE_FN(_io_write_stdio)
3077c478bd9Sstevel@tonic-gate {
3087c478bd9Sstevel@tonic-gate   int ndone;   /* The total number of characters written */
3097c478bd9Sstevel@tonic-gate   int nnew;    /* The number of characters written in the latest write */
3107c478bd9Sstevel@tonic-gate /*
3117c478bd9Sstevel@tonic-gate  * The callback data is the stdio stream to write to.
3127c478bd9Sstevel@tonic-gate  */
3137c478bd9Sstevel@tonic-gate   FILE *fp = (FILE *) data;
3147c478bd9Sstevel@tonic-gate /*
3157c478bd9Sstevel@tonic-gate  * Because of signals we may need to do more than one write to output
3167c478bd9Sstevel@tonic-gate  * the whole string.
3177c478bd9Sstevel@tonic-gate  */
3187c478bd9Sstevel@tonic-gate   for(ndone=0; ndone<n; ndone += nnew) {
3197c478bd9Sstevel@tonic-gate     int nmore = n - ndone;
3207c478bd9Sstevel@tonic-gate     nnew = fwrite(s, sizeof(char), nmore, fp);
3217c478bd9Sstevel@tonic-gate     if(nnew < nmore) {
3227c478bd9Sstevel@tonic-gate       if(errno == EINTR)
3237c478bd9Sstevel@tonic-gate 	clearerr(fp);
3247c478bd9Sstevel@tonic-gate       else
3257c478bd9Sstevel@tonic-gate 	return ferror(fp) ? -1 : ndone + nnew;
3267c478bd9Sstevel@tonic-gate     };
3277c478bd9Sstevel@tonic-gate   };
3287c478bd9Sstevel@tonic-gate   return ndone;
3297c478bd9Sstevel@tonic-gate }
3307c478bd9Sstevel@tonic-gate 
331