1 /* PSPP - a program for statistical analysis.
2 Copyright (C) 2011, 2012, 2020 Free Software Foundation, Inc.
4 This program is free software: you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation, either version 3 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program. If not, see <http://www.gnu.org/licenses/>. */
19 #include "libpspp/u8-line.h"
22 #include "libpspp/cast.h"
23 #include "libpspp/str.h"
25 /* Initializes LINE as an empty u8_line. */
27 u8_line_init (struct u8_line *line)
29 ds_init_empty (&line->s);
33 /* Frees data owned by LINE. */
35 u8_line_destroy (struct u8_line *line)
37 ds_destroy (&line->s);
40 /* Clears LINE to zero length. */
42 u8_line_clear (struct u8_line *line)
49 u8_mb_to_display (int *wp, const uint8_t *s, size_t n)
55 ofs = u8_mbtouc (&uc, s, n);
56 if (ofs < n && s[ofs] == '\b')
59 ofs += u8_mbtouc (&uc, s + ofs, n - ofs);
62 w = uc_width (uc, "UTF-8");
71 int mblen = u8_mbtouc (&uc, s + ofs, n - ofs);
72 if (uc_width (uc, "UTF-8") > 0)
81 /* Position of a character within a u8_line. */
84 /* 0-based display columns.
86 For a single-width character, x1 == x0 + 1.
87 For a double-width character, x1 == x0 + 2. */
93 For an ordinary ASCII character, ofs1 == ofs0 + 1.
94 For Unicode code point 0x80 or higher, 2 <= ofs1 - ofs0 <= 4. */
100 u8_line_find_pos (const struct u8_line *line, int target_x, struct u8_pos *c)
102 const uint8_t *s = CHAR_CAST (const uint8_t *, ds_cstr (&line->s));
103 size_t length = ds_length (&line->s);
111 for (ofs = 0; ofs <= length; ofs += mblen)
116 mblen = u8_mb_to_display (&w, s + ofs, length - ofs);
117 if (x + w > target_x)
120 c->ofs1 = ofs + mblen;
126 /* This can happen if there are non-printable characters
133 /* Prepares LINE to write N bytes of characters that comprise X1-X0 column
134 widths starting at 0-based column X0. Returns the first byte of the N for
135 the caller to fill in. */
137 u8_line_reserve (struct u8_line *line, int x0, int x1, int n)
140 if (x0 >= line->width)
142 /* The common case: adding new characters at the end of a line. */
143 ds_put_byte_multiple (&line->s, ' ', x0 - line->width);
145 return ds_put_uninit (&line->s, n);
151 /* An unusual case: overwriting characters in the middle of a line. We
152 don't keep any kind of mapping from bytes to display positions, so we
153 have to iterate over the whole line starting from the beginning. */
154 struct u8_pos p0, p1;
157 /* Find the positions of the first and last character. We must find both
158 characters' positions before changing the line, because that would
159 prevent finding the other character's position. */
160 u8_line_find_pos (line, x0, &p0);
161 if (x1 < line->width)
162 u8_line_find_pos (line, x1, &p1);
164 /* If a double-width character occupies both x0 - 1 and x0, then replace
165 its first character width by '?'. */
166 s = ds_data (&line->s);
173 if (x1 >= line->width)
175 ds_truncate (&line->s, p0.ofs0);
177 return ds_put_uninit (&line->s, n);
180 /* If a double-width character occupies both x1 - 1 and x1, then replace
181 its second character width by '?'. */
190 assert (p1.ofs1 >= p0.ofs0);
191 return ds_splice_uninit (&line->s, p0.ofs0, p1.ofs1 - p0.ofs0, n);
194 assert (p1.ofs0 >= p0.ofs0);
195 return ds_splice_uninit (&line->s, p0.ofs0, p1.ofs0 - p0.ofs0, n);
199 /* Writes the N bytes of characters that comprise X1-X0 column widths into LINE
200 starting at 0-based column X0. */
202 u8_line_put (struct u8_line *line, int x0, int x1, const char *s, int n)
204 memcpy (u8_line_reserve (line, x0, x1, n), s, n);
207 /* Changes the width of LINE to X column widths. If X is longer than LINE's
208 previous width, LINE is extended by appending spaces. If X is shorter than
209 LINE's previous width, LINE is shortened by removing trailing characters. */
211 u8_line_set_length (struct u8_line *line, int x)
215 ds_put_byte_multiple (&line->s, ' ', x - line->width);
218 else if (x < line->width)
222 u8_line_find_pos (line, x, &pos);
223 ds_truncate (&line->s, pos.ofs0);
224 line->width = pos.x0;
227 ds_put_byte_multiple (&line->s, '?', x - line->width);