2 * Copyright (c) 2000, 2001, 2002, 2003, 2004 by Martin C. Shepherd.
6 * Permission is hereby granted, free of charge, to any person obtaining a
7 * copy of this software and associated documentation files (the
8 * "Software"), to deal in the Software without restriction, including
9 * without limitation the rights to use, copy, modify, merge, publish,
10 * distribute, and/or sell copies of the Software, and to permit persons
11 * to whom the Software is furnished to do so, provided that the above
12 * copyright notice(s) and this permission notice appear in all copies of
13 * the Software and that both the above copyright notice(s) and this
14 * permission notice appear in supporting documentation.
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
17 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
19 * OF THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
20 * HOLDERS INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL
21 * INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING
22 * FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
23 * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
24 * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
26 * Except as contained in this notice, the name of a copyright holder
27 * shall not be used in advertising or otherwise to promote the sale, use
28 * or other dealings in this Software without prior written authorization
29 * of the copyright holder.
32 #pragma ident "%Z%%M% %I% %E% SMI"
42 static int _io_pad_line(GlWriteFn
*write_fn
, void *data
, int c
, int n
);
44 /*.......................................................................
45 * Display a left-justified string over multiple terminal lines,
46 * taking account of the specified width of the terminal. Optional
47 * indentation and an option prefix string can be specified to be
48 * displayed at the start of each new terminal line used, and if
49 * needed, a single paragraph can be broken across multiple calls.
50 * Note that literal newlines in the input string can be used to force
51 * a newline at any point, and that in order to allow individual
52 * paragraphs to be written using multiple calls to this function,
53 * unless an explicit newline character is specified at the end of the
54 * string, a newline will not be started at the end of the last word
55 * in the string. Note that when a new line is started between two
56 * words that are separated by spaces, those spaces are not output,
57 * whereas when a new line is started because a newline character was
58 * found in the string, only the spaces before the newline character
62 * write_fn GlWriteFn * The callback function to use to write the
64 * data void * A pointer to arbitrary data to be passed to
65 * write_fn() whenever it is called.
66 * fp FILE * The stdio stream to write to.
67 * indentation int The number of fill characters to use to
68 * indent the start of each new terminal line.
69 * prefix const char * An optional prefix string to write after the
70 * indentation margin at the start of each new
71 * terminal line. You can specify NULL if no
73 * suffix const char * An optional suffix string to draw at the end
74 * of the terminal line. The line will be padded
75 * where necessary to ensure that the suffix ends
76 * in the last column of the terminal line. If
77 * no suffix is desired, specify NULL.
78 * fill_char int The padding character to use when indenting
79 * and filling up to the suffix.
80 * term_width int The width of the terminal being written to.
81 * start int The number of characters already written to
82 * the start of the current terminal line. This
83 * is primarily used to allow individual
84 * paragraphs to be written over multiple calls
85 * to this function, but can also be used to
86 * allow you to start the first line of a
87 * paragraph with a different prefix or
88 * indentation than those specified above.
89 * string const char * The string to be written.
91 * return int On error -1 is returned. Otherwise the
92 * return value is the terminal column index at
93 * which the cursor was left after writing the
94 * final word in the string. Successful return
95 * values can thus be passed verbatim to the
96 * 'start' arguments of subsequent calls to
97 * _io_display_text() to allow the printing of a
98 * paragraph to be broken across multiple calls
99 * to _io_display_text().
101 int _io_display_text(GlWriteFn
*write_fn
, void *data
, int indentation
,
102 const char *prefix
, const char *suffix
, int fill_char
,
103 int term_width
, int start
, const char *string
)
105 int ndone
; /* The number of characters written from string[] */
106 int nnew
; /* The number of characters to be displayed next */
107 int was_space
; /* True if the previous character was a space or tab */
108 int last
= start
; /* The column number of the last character written */
109 int prefix_len
; /* The length of the optional line prefix string */
110 int suffix_len
; /* The length of the optional line prefix string */
111 int margin_width
; /* The total number of columns used by the indentation */
112 /* margin and the prefix string. */
115 * Check the arguments?
117 if(!string
|| !write_fn
) {
122 * Enforce sensible values on the arguments.
126 if(indentation
> term_width
)
127 indentation
= term_width
;
128 else if(indentation
< 0)
130 if(start
> term_width
)
135 * Get the length of the prefix string.
137 prefix_len
= prefix
? strlen(prefix
) : 0;
139 * Get the length of the suffix string.
141 suffix_len
= suffix
? strlen(suffix
) : 0;
143 * How many characters are devoted to indenting and prefixing each line?
145 margin_width
= indentation
+ prefix_len
;
147 * Write as many terminal lines as are needed to display the whole string.
149 for(ndone
=0; string
[ndone
]; start
=0) {
152 * Write spaces from the current position in the terminal line to the
153 * width of the requested indentation margin.
155 if(indentation
> 0 && last
< indentation
) {
156 if(_io_pad_line(write_fn
, data
, fill_char
, indentation
- last
))
161 * If a prefix string has been specified, display it unless we have
162 * passed where it should end in the terminal output line.
164 if(prefix_len
> 0 && last
< margin_width
) {
165 int pstart
= last
- indentation
;
166 int plen
= prefix_len
- pstart
;
167 if(write_fn(data
, prefix
+pstart
, plen
) != plen
)
172 * Locate the end of the last complete word in the string before
173 * (term_width - start) characters have been seen. To handle the case
174 * where a single word is wider than the available space after the
175 * indentation and prefix margins, always make sure that at least one
176 * word is printed after the margin, regardless of whether it won't
177 * fit on the line. The two exceptions to this rule are if an embedded
178 * newline is found in the string or the end of the string is reached
179 * before any word has been seen.
183 for(i
=ndone
; string
[i
] && (last
+i
-ndone
< term_width
- suffix_len
||
184 (nnew
==0 && last
==margin_width
)); i
++) {
185 if(string
[i
] == '\n') {
189 } else if(isspace((int) string
[i
])) {
199 * Does the end of the string delimit the last word that will fit on the
202 if(nnew
==0 && string
[i
] == '\0')
205 * Write the new line.
207 if(write_fn(data
, string
+ndone
, nnew
) != nnew
)
212 * Start a newline unless we have reached the end of the input string.
213 * In the latter case, in order to give the caller the chance to
214 * concatenate multiple calls to _io_display_text(), omit the newline,
215 * leaving it up to the caller to write this.
217 if(string
[ndone
] != '\0') {
219 * If a suffix has been provided, pad out the end of the line with spaces
220 * such that the suffix will end in the right-most terminal column.
223 int npad
= term_width
- suffix_len
- last
;
224 if(npad
> 0 && _io_pad_line(write_fn
, data
, fill_char
, npad
))
227 if(write_fn(data
, suffix
, suffix_len
) != suffix_len
)
234 if(write_fn(data
, "\n", 1) != 1)
237 * Skip any spaces and tabs that follow the last word that was written.
239 while(string
[ndone
] && isspace((int)string
[ndone
]) &&
240 string
[ndone
] != '\n')
243 * If the terminating character was a literal newline character,
244 * skip it in the input string, since we just wrote it.
246 if(string
[ndone
] == '\n')
252 * Return the column number of the last character printed.
257 /*.......................................................................
258 * Write a given number of spaces to the specified stdio output string.
261 * write_fn GlWriteFn * The callback function to use to write the
263 * data void * A pointer to arbitrary data to be passed to
264 * write_fn() whenever it is called.
265 * c int The padding character.
266 * n int The number of spaces to be written.
271 static int _io_pad_line(GlWriteFn
*write_fn
, void *data
, int c
, int n
)
274 char fill
[FILL_SIZE
+1];
276 * Fill the buffer with the specified padding character.
278 memset(fill
, c
, FILL_SIZE
);
279 fill
[FILL_SIZE
] = '\0';
281 * Write the spaces using the above literal string of spaces as
282 * many times as needed to output the requested number of spaces.
285 int nnew
= n
<= FILL_SIZE
? n
: FILL_SIZE
;
286 if(write_fn(data
, fill
, nnew
) != nnew
)
293 /*.......................................................................
294 * The following is an output callback function which uses fwrite()
295 * to write to the stdio stream specified via its callback data argument.
298 * data void * The stdio stream to write to, specified via a
299 * (FILE *) pointer cast to (void *).
300 * s const char * The string to be written.
301 * n int The length of the prefix of s[] to attempt to
304 * return int The number of characters written from s[]. This
305 * should normally be a number in the range 0 to n.
306 * To signal that an I/O error occurred, return -1.
308 GL_WRITE_FN(_io_write_stdio
)
310 int ndone
; /* The total number of characters written */
311 int nnew
; /* The number of characters written in the latest write */
313 * The callback data is the stdio stream to write to.
315 FILE *fp
= (FILE *) data
;
317 * Because of signals we may need to do more than one write to output
320 for(ndone
=0; ndone
<n
; ndone
+= nnew
) {
321 int nmore
= n
- ndone
;
322 nnew
= fwrite(s
, sizeof(char), nmore
, fp
);
327 return ferror(fp
) ? -1 : ndone
+ nnew
;