source: mainline/uspace/lib/fmtutil/fmtutil.c@ 1d2f85e

Last change on this file since 1d2f85e was 08e103d4, checked in by Jiří Zárevúcky <zarevucky.jiri@…>, 7 years ago

Use clearer naming for string length functions

This and the following commit change the names of functions, as well as
their documentation, to use unambiguous terms "bytes" and "code points"
instead of ambiguous terms "size", "length", and "characters".

  • Property mode set to 100644
File size: 6.2 KB
RevLine 
[22cf42d9]1/*
2 * Copyright (c) 2011 Martin Sucha
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 *
9 * - Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * - Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * - The name of the author may not be used to endorse or promote products
15 * derived from this software without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 */
28
29#include <io/console.h>
30#include <errno.h>
31#include <fmtutil.h>
[38d150e]32#include <stdlib.h>
[1d6dd2a]33#include <str.h>
[22cf42d9]34
35typedef struct {
36 align_mode_t alignment;
37 bool newline_always;
38 size_t width;
39} printmode_t;
40
[b7fd2a0]41errno_t print_wrapped_console(const char *str, align_mode_t alignment)
[22cf42d9]42{
43 console_ctrl_t *console = console_init(stdin, stdout);
44 if (console == NULL) {
45 printf("%s", str);
46 return EOK;
47 }
48 sysarg_t con_rows, con_cols, con_col, con_row;
[b7fd2a0]49 errno_t rc = console_get_size(console, &con_cols, &con_rows);
[22cf42d9]50 if (rc != EOK) {
51 return rc;
52 }
53 rc = console_get_pos(console, &con_col, &con_row);
54 if (rc != EOK) {
55 return rc;
56 }
57 if (con_col != 0) {
58 printf("\n");
59 }
60 return print_wrapped(str, con_cols, alignment);
61}
62
[7c3fb9b]63/** Line consumer that prints the lines aligned according to spec */
[b7fd2a0]64static errno_t print_line(wchar_t *wstr, size_t chars, bool last, void *data)
[22cf42d9]65{
66 printmode_t *pm = (printmode_t *) data;
[6045ecf]67 wchar_t old_char = wstr[chars];
[22cf42d9]68 wstr[chars] = 0;
[b7fd2a0]69 errno_t rc = print_aligned_w(wstr, pm->width, last, pm->alignment);
[6045ecf]70 wstr[chars] = old_char;
71 return rc;
[22cf42d9]72}
73
[b7fd2a0]74errno_t print_wrapped(const char *str, size_t width, align_mode_t mode)
[22cf42d9]75{
76 printmode_t pm;
77 pm.alignment = mode;
78 pm.newline_always = false;
79 pm.width = width;
80 wchar_t *wstr = str_to_awstr(str);
81 if (wstr == NULL) {
82 return ENOMEM;
83 }
[b7fd2a0]84 errno_t rc = wrap(wstr, width, print_line, &pm);
[22cf42d9]85 free(wstr);
86 return rc;
87}
88
[b7fd2a0]89errno_t print_aligned_w(const wchar_t *wstr, size_t width, bool last,
[6045ecf]90 align_mode_t mode)
[22cf42d9]91{
92 size_t i;
[08e103d4]93 size_t len = wstr_code_points(wstr);
[6045ecf]94 if (mode == ALIGN_LEFT || (mode == ALIGN_JUSTIFY && last)) {
[22cf42d9]95 for (i = 0; i < width; i++) {
96 if (i < len)
[ed88c8e]97 putwchar(wstr[i]);
[22cf42d9]98 else
[ed88c8e]99 putwchar(' ');
[22cf42d9]100 }
[1433ecda]101 } else if (mode == ALIGN_RIGHT) {
[22cf42d9]102 for (i = 0; i < width; i++) {
103 if (i < width - len)
[ed88c8e]104 putwchar(' ');
[22cf42d9]105 else
[ed88c8e]106 putwchar(wstr[i - (width - len)]);
[22cf42d9]107 }
[1433ecda]108 } else if (mode == ALIGN_CENTER) {
[22cf42d9]109 size_t padding = (width - len) / 2;
110 for (i = 0; i < width; i++) {
111 if ((i < padding) || ((i - padding) >= len))
[ed88c8e]112 putwchar(' ');
[22cf42d9]113 else
[ed88c8e]114 putwchar(wstr[i - padding]);
[22cf42d9]115 }
[1433ecda]116 } else if (mode == ALIGN_JUSTIFY) {
[22cf42d9]117 size_t words = 0;
118 size_t word_chars = 0;
119 bool space = true;
120 for (i = 0; i < len; i++) {
121 if (space && wstr[i] != ' ') {
122 words++;
123 }
124 space = wstr[i] == ' ';
[1433ecda]125 if (!space)
126 word_chars++;
[22cf42d9]127 }
[19d007e]128 size_t done_words = 0;
129 size_t done_chars = 0;
130 if (words == 0)
131 goto skip_words;
[1433ecda]132 size_t excess_spaces = width - word_chars - (words - 1);
[22cf42d9]133 space = true;
134 i = 0;
135 size_t j;
136 while (i < len) {
137 /* Find a word */
[1433ecda]138 while (i < len && wstr[i] == ' ')
139 i++;
140 if (i == len)
141 break;
[22cf42d9]142 if (done_words) {
[fcfac250]143 size_t spaces = 1 + (((done_words *
144 excess_spaces) / (words - 1)) -
145 (((done_words - 1) * excess_spaces) /
146 (words - 1)));
[22cf42d9]147 for (j = 0; j < spaces; j++) {
[ed88c8e]148 putwchar(' ');
[22cf42d9]149 }
150 done_chars += spaces;
151 }
152 while (i < len && wstr[i] != ' ') {
[ed88c8e]153 putwchar(wstr[i++]);
[22cf42d9]154 done_chars++;
155 }
156 done_words++;
157 }
[1433ecda]158 skip_words:
[22cf42d9]159 while (done_chars < width) {
[ed88c8e]160 putwchar(' ');
[22cf42d9]161 done_chars++;
162 }
[1433ecda]163 } else {
[e406736]164 return EINVAL;
165 }
[a35b458]166
[22cf42d9]167 return EOK;
168}
[b7fd2a0]169errno_t print_aligned(const char *str, size_t width, bool last, align_mode_t mode)
[22cf42d9]170{
171 wchar_t *wstr = str_to_awstr(str);
172 if (wstr == NULL) {
173 return ENOMEM;
174 }
[b7fd2a0]175 errno_t rc = print_aligned_w(wstr, width, last, mode);
[22cf42d9]176 free(wstr);
177 return rc;
178}
179
[b7fd2a0]180errno_t wrap(wchar_t *wstr, size_t width, line_consumer_fn consumer, void *data)
[22cf42d9]181{
182 size_t word_start = 0;
183 size_t last_word_end = 0;
184 size_t line_start = 0;
185 size_t line_len = 0;
186 size_t pos = 0;
[a35b458]187
[22cf42d9]188 /*
189 * Invariants:
190 * * line_len = last_word_end - line_start
191 * * line_start <= last_word_end <= word_start <= pos
192 */
[a35b458]193
[22cf42d9]194 while (wstr[pos] != 0) {
195 /* Skip spaces and process newlines */
196 while (wstr[pos] == ' ' || wstr[pos] == '\n') {
197 if (wstr[pos] == '\n') {
[6045ecf]198 consumer(wstr + line_start, line_len, true,
199 data);
[22cf42d9]200 last_word_end = line_start = pos + 1;
201 line_len = 0;
202 }
203 pos++;
204 }
205 word_start = pos;
206 /* Find end of word */
207 while (wstr[pos] != 0 && wstr[pos] != ' ' &&
208 wstr[pos] != '\n')
209 pos++;
[6045ecf]210 bool last = wstr[pos] == 0;
[22cf42d9]211 /* Check if the line still fits width */
212 if (pos - line_start > width) {
213 if (line_len > 0)
[6045ecf]214 consumer(wstr + line_start, line_len, last,
215 data);
[22cf42d9]216 line_start = last_word_end = word_start;
217 line_len = 0;
218 }
219 /* Check if we need to force wrap of long word*/
220 if (pos - word_start > width) {
[6045ecf]221 consumer(wstr + word_start, width, last, data);
[22cf42d9]222 pos = line_start = last_word_end = word_start + width;
223 line_len = 0;
224 }
225 last_word_end = pos;
226 line_len = last_word_end - line_start;
227 }
[7c3fb9b]228 /*
229 * Here we have less than width chars starting from line_start.
[1b20da0]230 * Moreover, the last portion does not contain spaces or newlines
[22cf42d9]231 */
232 if (pos - line_start > 0)
[6045ecf]233 consumer(wstr + line_start, pos - line_start, true, data);
[22cf42d9]234
235 return EOK;
236}
Note: See TracBrowser for help on using the repository browser.