libexplain
1.4.D001
|
00001 /* 00002 * libexplain - Explain errno values returned by libc functions 00003 * Copyright (C) 2008-2011, 2013 Peter Miller 00004 * Written by Peter Miller <pmiller@opensource.org.au> 00005 * 00006 * This program is free software; you can redistribute it and/or modify 00007 * it under the terms of the GNU Lesser General Public License as published by 00008 * the Free Software Foundation; either version 3 of the License, or (at 00009 * your option) any later version. 00010 * 00011 * This program is distributed in the hope that it will be useful, 00012 * but WITHOUT ANY WARRANTY; without even the implied warranty of 00013 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 00014 * Lesser General Public License for more details. 00015 * 00016 * You should have received a copy of the GNU Lesser General Public License 00017 * along with this program. If not, see <http://www.gnu.org/licenses/>. 00018 */ 00019 00020 #include <libexplain/ac/assert.h> 00021 #include <libexplain/ac/ctype.h> 00022 #include <libexplain/ac/limits.h> /* for PATH_MAX on Solaris */ 00023 #include <libexplain/ac/stdlib.h> 00024 #include <libexplain/ac/string.h> 00025 #include <libexplain/ac/sys/ioctl.h> 00026 #include <libexplain/ac/sys/param.h> /* for PATH_MAX except Solaris */ 00027 #include <libexplain/ac/termios.h> 00028 #include <libexplain/ac/unistd.h> 00029 #include <libexplain/ac/wchar.h> 00030 #include <libexplain/ac/wctype.h> 00031 00032 #ifdef HAVE_winsize_SYS_IOCTL_H 00033 #include <libexplain/ac/sys/ioctl.h> 00034 #endif 00035 00036 #include <libexplain/option.h> 00037 #include <libexplain/string_buffer.h> 00038 #include <libexplain/wrap_and_print.h> 00039 00040 #define DEFAULT_LINE_WIDTH 75 00041 00042 #define MAX_LINE_LENGTH (PATH_MAX + 10) 00043 00044 00045 #if defined(HAVE_MBRTOWC) && defined(HAVE_WCWIDTH) 00046 00047 void 00048 explain_wrap_and_print_width(FILE *fp, const char *text, int width) 00049 { 00050 const char *cp; 00051 const char *end; 00052 char line_string[MAX_LINE_LENGTH + 1]; 00053 explain_string_buffer_t line_buf; 00054 char word_string[MAX_LINE_LENGTH + 1]; 00055 explain_string_buffer_t word_buf; 00056 static mbstate_t mbz; 00057 mbstate_t state; 00058 int width_of_line; 00059 int width_of_word; 00060 int hanging_indent; 00061 int first_line; 00062 00063 assert(width > 0); 00064 if (width <= 0) 00065 width = DEFAULT_LINE_WIDTH ; 00066 if (width > MAX_LINE_LENGTH) 00067 width = MAX_LINE_LENGTH; 00068 hanging_indent = explain_option_hanging_indent(width); 00069 assert(sizeof(word_string) <= sizeof(line_string)); 00070 cp = text; 00071 end = text + strlen(text); 00072 explain_string_buffer_init(&line_buf, line_string, sizeof(line_string)); 00073 explain_string_buffer_init(&word_buf, word_string, sizeof(word_string)); 00074 state = mbz; 00075 width_of_line = 0; 00076 width_of_word = 0; 00077 first_line = 1; 00078 for (;;) 00079 { 00080 const char *starts_here; 00081 wchar_t wc; 00082 size_t n; 00083 00084 starts_here = cp; 00085 n = mbrtowc(&wc, cp, end - cp, &state); 00086 if ((ssize_t)n < 0) 00087 { 00088 wc = *cp; 00089 n = 1; 00090 } 00091 cp += n; 00092 if (n == 0 || wc == L'\0') 00093 { 00094 if (line_buf.position) 00095 { 00096 if (0 == fwrite(line_string, line_buf.position, 1, fp)) 00097 return; 00098 putc('\n', fp); 00099 } 00100 return; 00101 } 00102 00103 if (iswspace(wc)) 00104 continue; 00105 00106 /* 00107 * Grab the next word. 00108 * 00109 * The width_of_word records the number of character positions 00110 * consumed. This can be less than the number of bytes, when 00111 * multi-byte character sequences represent single displayed 00112 * characters. This can be more than the number of bytes, for 00113 * exmaple kanji, when a character is display 2 columns wide. 00114 */ 00115 word_buf.position = 0; 00116 width_of_word = 0; 00117 for (;;) 00118 { 00119 mbstate_t hold; 00120 00121 explain_string_buffer_write(&word_buf, starts_here, n); 00122 width_of_word += wcwidth(wc); 00123 if (explain_string_buffer_full(&word_buf)) 00124 break; 00125 00126 hold = state; 00127 starts_here = cp; 00128 n = mbrtowc(&wc, cp, end - cp, &state); 00129 if ((ssize_t)n < 0) 00130 { 00131 wc = *cp; 00132 n = 1; 00133 } 00134 00135 if (n == 0 || wc == '\0') 00136 { 00137 state = hold; 00138 break; 00139 } 00140 if (iswspace(wc)) 00141 { 00142 state = hold; 00143 break; 00144 } 00145 cp += n; 00146 } 00147 00148 if (line_buf.position == 0) 00149 { 00150 /* do nothing */ 00151 } 00152 else if 00153 ( 00154 width_of_line + 1 + width_of_word 00155 <= 00156 width - (first_line ? 0 : hanging_indent)) 00157 { 00158 explain_string_buffer_putc(&line_buf, ' '); 00159 ++width_of_line; 00160 } 00161 else 00162 { 00163 if (0 == fwrite(line_string, line_buf.position, 1, fp)) 00164 return; 00165 putc('\n', fp); 00166 line_buf.position = 0; 00167 width_of_line = 0; 00168 first_line = 0; 00169 } 00170 if (line_buf.position == 0 && !first_line && hanging_indent) 00171 { 00172 for (;;) 00173 { 00174 explain_string_buffer_putc(&line_buf, ' '); 00175 if (line_buf.position >= (size_t)hanging_indent) 00176 break; 00177 } 00178 } 00179 explain_string_buffer_puts(&line_buf, word_string); 00180 width_of_line += width_of_word; 00181 00182 /* 00183 * Note: it is possible for a line to be longer than (width) 00184 * when it contains a single word that is itself longer than 00185 * (width). We do this to avoid putting line breaks in the 00186 * middle of pathnames, provided the pathanme itself does not 00187 * contain white space. This is useful for copy-and-paste. 00188 */ 00189 } 00190 } 00191 00192 #else 00193 00194 void 00195 explain_wrap_and_print_width(FILE *fp, const char *text, int width) 00196 { 00197 const char *cp; 00198 char line_string[MAX_LINE_LENGTH + 1]; 00199 explain_string_buffer_t line_buf; 00200 char word_string[MAX_LINE_LENGTH + 1]; 00201 explain_string_buffer_t word_buf; 00202 unsigned hanging_indent; 00203 int first_line; 00204 00205 assert(width > 0); 00206 if (width <= 0) 00207 width = DEFAULT_LINE_WIDTH ; 00208 if (width > MAX_LINE_LENGTH) 00209 width = MAX_LINE_LENGTH; 00210 hanging_indent = explain_option_hanging_indent(width); 00211 assert(sizeof(word_string) <= sizeof(line_string)); 00212 cp = text; 00213 explain_string_buffer_init(&line_buf, line_string, sizeof(line_string)); 00214 explain_string_buffer_init(&word_buf, word_string, sizeof(word_string)); 00215 first_line = 1; 00216 for (;;) 00217 { 00218 unsigned char c = *cp++; 00219 if (c == '\0') 00220 { 00221 if (line_buf.position) 00222 { 00223 if (0 == fwrite(line_string, line_buf.position, 1, fp)) 00224 return; 00225 putc('\n', fp); 00226 } 00227 return; 00228 } 00229 00230 if (isspace(c)) 00231 continue; 00232 00233 /* 00234 * Grab the next word. 00235 */ 00236 word_buf.position = 0; 00237 for (;;) 00238 { 00239 explain_string_buffer_putc(&word_buf, c); 00240 if (explain_string_buffer_full(&word_buf)) 00241 break; 00242 c = *cp; 00243 if (c == '\0') 00244 break; 00245 if (isspace(c)) 00246 break; 00247 ++cp; 00248 } 00249 00250 if (line_buf.position == 0) 00251 { 00252 /* do nothing */ 00253 } 00254 else if 00255 ( 00256 line_buf.position + 1 + word_buf.position 00257 <= 00258 (size_t)(width - (first_line ? 0 : hanging_indent)) 00259 ) 00260 { 00261 explain_string_buffer_putc(&line_buf, ' '); 00262 } 00263 else 00264 { 00265 if (0 == fwrite(line_string, line_buf.position, 1, fp)) 00266 return; 00267 putc('\n', fp); 00268 line_buf.position = 0; 00269 first_line = 0; 00270 } 00271 if (line_buf.position == 0 && !first_line && hanging_indent) 00272 { 00273 for (;;) 00274 { 00275 explain_string_buffer_putc(&line_buf, ' '); 00276 if (line_buf.position >= hanging_indent) 00277 break; 00278 } 00279 } 00280 explain_string_buffer_puts(&line_buf, word_string); 00281 /* 00282 * Note: it is possible for a line to be longer than (width) 00283 * when it contains a single word that is itself longer than 00284 * (width). 00285 */ 00286 } 00287 } 00288 00289 #endif 00290 00291 00292 void 00293 explain_wrap_and_print(FILE *fp, const char *text) 00294 { 00295 int width; 00296 int fildes; 00297 00298 width = DEFAULT_LINE_WIDTH; 00299 00300 /* 00301 * If output is going to a terminal, use the terminal's with when 00302 * formatting error messages. 00303 */ 00304 fildes = fileno(fp); 00305 if (isatty(fildes)) 00306 { 00307 const char *cp; 00308 00309 cp = getenv("COLS"); 00310 if (cp && *cp) 00311 { 00312 char *ep; 00313 00314 width = strtol(cp, &ep, 0); 00315 if (ep == cp || *ep) 00316 width = 0; 00317 } 00318 #ifdef TIOCGWINSZ 00319 if (width <= 0 ) 00320 { 00321 struct winsize ws; 00322 00323 if (ioctl(fildes, TIOCGWINSZ, &ws) >= 0) 00324 width = ws.ws_col; 00325 } 00326 #endif 00327 if (width <= 0) 00328 width = DEFAULT_LINE_WIDTH; 00329 } 00330 00331 /* 00332 * Print the text using the window width. 00333 */ 00334 explain_wrap_and_print_width(fp, text, width); 00335 } 00336 00337 00338 /* vim: set ts=8 sw=4 et : */