]>
Commit | Line | Data |
---|---|---|
f4c623e1 VS |
1 | /* |
2 | * GRUB -- GRand Unified Bootloader | |
3 | * Copyright (C) 2002,2003,2005,2007,2008,2009 Free Software Foundation, Inc. | |
4 | * | |
5 | * GRUB is free software: you can redistribute it and/or modify | |
6 | * it under the terms of the GNU General Public License as published by | |
7 | * the Free Software Foundation, either version 3 of the License, or | |
8 | * (at your option) any later version. | |
9 | * | |
10 | * GRUB is distributed in the hope that it will be useful, | |
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
13 | * GNU General Public License for more details. | |
14 | * | |
15 | * You should have received a copy of the GNU General Public License | |
16 | * along with GRUB. If not, see <http://www.gnu.org/licenses/>. | |
17 | */ | |
18 | ||
2e713831 VS |
19 | #include <grub/term.h> |
20 | #include <grub/misc.h> | |
21 | #include <grub/mm.h> | |
33c1ed4c VS |
22 | #include <grub/file.h> |
23 | #include <grub/dl.h> | |
24 | #include <grub/env.h> | |
25 | #include <grub/normal.h> | |
dfed5c6b | 26 | #include <grub/charset.h> |
6e0632e2 | 27 | #include <grub/i18n.h> |
2e713831 | 28 | |
6fcebede VS |
29 | struct term_state |
30 | { | |
31 | struct term_state *next; | |
3c69fb81 VS |
32 | const struct grub_unicode_glyph *backlog_glyphs; |
33 | const grub_uint32_t *backlog_ucs4; | |
34 | grub_size_t backlog_len; | |
35 | ||
36 | void *free; | |
37 | int num_lines; | |
6fcebede VS |
38 | char *term_name; |
39 | }; | |
40 | ||
3c69fb81 VS |
41 | static struct term_state *term_states = NULL; |
42 | ||
f4c623e1 VS |
43 | /* If the more pager is active. */ |
44 | static int grub_more; | |
45 | ||
902f75f6 VS |
46 | static void |
47 | putcode_real (grub_uint32_t code, struct grub_term_output *term); | |
48 | ||
5c56cac9 VS |
49 | void |
50 | grub_normal_reset_more (void) | |
51 | { | |
52 | static struct term_state *state; | |
53 | for (state = term_states; state; state = state->next) | |
54 | state->num_lines = 0; | |
55 | } | |
56 | ||
f4c623e1 | 57 | static void |
3c69fb81 | 58 | print_more (void) |
f4c623e1 | 59 | { |
3c69fb81 VS |
60 | char key; |
61 | grub_uint16_t *pos; | |
62 | grub_term_output_t term; | |
63 | grub_uint32_t *unicode_str, *unicode_last_position; | |
2e713831 | 64 | |
3c69fb81 VS |
65 | pos = grub_term_save_pos (); |
66 | ||
6e0632e2 VS |
67 | /* TRANSLATORS: This has to fit on one line. It's ok to include few |
68 | words but don't write poems. */ | |
69 | grub_utf8_to_ucs4_alloc (_("--MORE--"), &unicode_str, | |
3c69fb81 | 70 | &unicode_last_position); |
2e713831 | 71 | |
3c69fb81 | 72 | if (!unicode_str) |
f4c623e1 | 73 | { |
3c69fb81 VS |
74 | grub_errno = GRUB_ERR_NONE; |
75 | return; | |
76 | } | |
2e713831 | 77 | |
3c69fb81 VS |
78 | grub_setcolorstate (GRUB_TERM_COLOR_HIGHLIGHT); |
79 | ||
80 | FOR_ACTIVE_TERM_OUTPUTS(term) | |
81 | { | |
82 | grub_print_ucs4 (unicode_str, unicode_last_position, 0, 0, term); | |
83 | } | |
9a9de209 | 84 | grub_setcolorstate (GRUB_TERM_COLOR_NORMAL); |
2e713831 | 85 | |
3c69fb81 VS |
86 | grub_free (unicode_str); |
87 | ||
88 | key = grub_getkey (); | |
2e713831 | 89 | |
3c69fb81 VS |
90 | /* Remove the message. */ |
91 | grub_term_restore_pos (pos); | |
92 | FOR_ACTIVE_TERM_OUTPUTS(term) | |
93 | grub_print_spaces (term, 8); | |
94 | grub_term_restore_pos (pos); | |
902f75f6 | 95 | grub_free (pos); |
2e713831 | 96 | |
32570200 | 97 | /* Scroll one line or an entire page, depending on the key. */ |
2e713831 | 98 | |
5c56cac9 | 99 | if (key == '\r' || key =='\n') |
5c56cac9 VS |
100 | { |
101 | static struct term_state *state; | |
102 | for (state = term_states; state; state = state->next) | |
32570200 | 103 | state->num_lines--; |
5c56cac9 | 104 | } |
32570200 CW |
105 | else |
106 | grub_normal_reset_more (); | |
f4c623e1 VS |
107 | } |
108 | ||
109 | void | |
110 | grub_set_more (int onoff) | |
111 | { | |
112 | if (onoff == 1) | |
113 | grub_more++; | |
114 | else | |
115 | grub_more--; | |
5c56cac9 | 116 | grub_normal_reset_more (); |
c6f2fe52 VS |
117 | } |
118 | ||
a68c4194 VS |
119 | enum |
120 | { | |
121 | GRUB_CP437_UPARROW = 0x18, | |
122 | GRUB_CP437_DOWNARROW = 0x19, | |
123 | GRUB_CP437_RIGHTARROW = 0x1a, | |
124 | GRUB_CP437_LEFTARROW = 0x1b, | |
125 | GRUB_CP437_VLINE = 0xb3, | |
126 | GRUB_CP437_CORNER_UR = 0xbf, | |
127 | GRUB_CP437_CORNER_LL = 0xc0, | |
128 | GRUB_CP437_HLINE = 0xc4, | |
129 | GRUB_CP437_CORNER_LR = 0xd9, | |
130 | GRUB_CP437_CORNER_UL = 0xda, | |
131 | }; | |
132 | ||
09f9aa3b VS |
133 | static grub_uint32_t |
134 | map_code (grub_uint32_t in, struct grub_term_output *term) | |
135 | { | |
136 | if (in <= 0x7f) | |
137 | return in; | |
138 | ||
139 | switch (term->flags & GRUB_TERM_CODE_TYPE_MASK) | |
140 | { | |
232dbbe5 | 141 | case GRUB_TERM_CODE_TYPE_CP437: |
09f9aa3b VS |
142 | switch (in) |
143 | { | |
b764bff2 | 144 | case GRUB_UNICODE_LEFTARROW: |
a68c4194 | 145 | return GRUB_CP437_LEFTARROW; |
b764bff2 | 146 | case GRUB_UNICODE_UPARROW: |
a68c4194 | 147 | return GRUB_CP437_UPARROW; |
b764bff2 | 148 | case GRUB_UNICODE_RIGHTARROW: |
a68c4194 | 149 | return GRUB_CP437_RIGHTARROW; |
b764bff2 | 150 | case GRUB_UNICODE_DOWNARROW: |
a68c4194 | 151 | return GRUB_CP437_DOWNARROW; |
b764bff2 | 152 | case GRUB_UNICODE_HLINE: |
a68c4194 | 153 | return GRUB_CP437_HLINE; |
b764bff2 | 154 | case GRUB_UNICODE_VLINE: |
a68c4194 | 155 | return GRUB_CP437_VLINE; |
b764bff2 | 156 | case GRUB_UNICODE_CORNER_UL: |
a68c4194 | 157 | return GRUB_CP437_CORNER_UL; |
b764bff2 | 158 | case GRUB_UNICODE_CORNER_UR: |
a68c4194 | 159 | return GRUB_CP437_CORNER_UR; |
b764bff2 | 160 | case GRUB_UNICODE_CORNER_LL: |
a68c4194 | 161 | return GRUB_CP437_CORNER_LL; |
b764bff2 | 162 | case GRUB_UNICODE_CORNER_LR: |
a68c4194 | 163 | return GRUB_CP437_CORNER_LR; |
09f9aa3b VS |
164 | } |
165 | return '?'; | |
166 | case GRUB_TERM_CODE_TYPE_ASCII: | |
167 | /* Better than nothing. */ | |
168 | switch (in) | |
169 | { | |
b764bff2 | 170 | case GRUB_UNICODE_LEFTARROW: |
09f9aa3b VS |
171 | return '<'; |
172 | ||
b764bff2 | 173 | case GRUB_UNICODE_UPARROW: |
09f9aa3b VS |
174 | return '^'; |
175 | ||
b764bff2 | 176 | case GRUB_UNICODE_RIGHTARROW: |
09f9aa3b VS |
177 | return '>'; |
178 | ||
b764bff2 | 179 | case GRUB_UNICODE_DOWNARROW: |
09f9aa3b VS |
180 | return 'v'; |
181 | ||
b764bff2 | 182 | case GRUB_UNICODE_HLINE: |
09f9aa3b VS |
183 | return '-'; |
184 | ||
b764bff2 | 185 | case GRUB_UNICODE_VLINE: |
09f9aa3b VS |
186 | return '|'; |
187 | ||
b764bff2 VS |
188 | case GRUB_UNICODE_CORNER_UL: |
189 | case GRUB_UNICODE_CORNER_UR: | |
190 | case GRUB_UNICODE_CORNER_LL: | |
191 | case GRUB_UNICODE_CORNER_LR: | |
09f9aa3b VS |
192 | return '+'; |
193 | ||
194 | } | |
195 | return '?'; | |
196 | } | |
197 | return in; | |
198 | } | |
199 | ||
f4c623e1 VS |
200 | void |
201 | grub_puts_terminal (const char *str, struct grub_term_output *term) | |
202 | { | |
81b0623a | 203 | grub_uint32_t *unicode_str, *unicode_last_position; |
5303b85d | 204 | grub_error_push (); |
81b0623a VS |
205 | grub_utf8_to_ucs4_alloc (str, &unicode_str, |
206 | &unicode_last_position); | |
5303b85d | 207 | grub_error_pop (); |
902f75f6 VS |
208 | if (!unicode_str) |
209 | { | |
5303b85d | 210 | for (; *str; str++) |
902f75f6 | 211 | { |
5303b85d VS |
212 | struct grub_unicode_glyph c = |
213 | { | |
214 | .variant = 0, | |
215 | .attributes = 0, | |
216 | .ncomb = 0, | |
217 | .combining = 0, | |
218 | .estimated_width = 1, | |
219 | .base = *str | |
220 | }; | |
902f75f6 | 221 | |
5303b85d VS |
222 | FOR_ACTIVE_TERM_OUTPUTS(term) |
223 | { | |
224 | (term->putchar) (term, &c); | |
225 | } | |
226 | if (*str == '\n') | |
227 | { | |
228 | c.base = '\r'; | |
229 | FOR_ACTIVE_TERM_OUTPUTS(term) | |
230 | { | |
231 | (term->putchar) (term, &c); | |
232 | } | |
233 | } | |
902f75f6 VS |
234 | } |
235 | return; | |
236 | } | |
f4c623e1 | 237 | |
81b0623a VS |
238 | grub_print_ucs4 (unicode_str, unicode_last_position, 0, 0, term); |
239 | grub_free (unicode_str); | |
f4c623e1 VS |
240 | } |
241 | ||
242 | grub_uint16_t * | |
243 | grub_term_save_pos (void) | |
244 | { | |
245 | struct grub_term_output *cur; | |
246 | unsigned cnt = 0; | |
247 | grub_uint16_t *ret, *ptr; | |
248 | ||
3be7f8de VS |
249 | FOR_ACTIVE_TERM_OUTPUTS(cur) |
250 | cnt++; | |
f4c623e1 VS |
251 | |
252 | ret = grub_malloc (cnt * sizeof (ret[0])); | |
253 | if (!ret) | |
254 | return NULL; | |
255 | ||
2e713831 | 256 | ptr = ret; |
3be7f8de VS |
257 | FOR_ACTIVE_TERM_OUTPUTS(cur) |
258 | *ptr++ = grub_term_getxy (cur); | |
f4c623e1 VS |
259 | |
260 | return ret; | |
261 | } | |
262 | ||
263 | void | |
264 | grub_term_restore_pos (grub_uint16_t *pos) | |
265 | { | |
266 | struct grub_term_output *cur; | |
2e713831 | 267 | grub_uint16_t *ptr = pos; |
f4c623e1 VS |
268 | |
269 | if (!pos) | |
270 | return; | |
271 | ||
3be7f8de VS |
272 | FOR_ACTIVE_TERM_OUTPUTS(cur) |
273 | { | |
274 | grub_term_gotoxy (cur, (*ptr & 0xff00) >> 8, *ptr & 0xff); | |
275 | ptr++; | |
276 | } | |
f4c623e1 | 277 | } |
33c1ed4c VS |
278 | |
279 | static void | |
280 | grub_terminal_autoload_free (void) | |
281 | { | |
282 | struct grub_term_autoload *cur, *next; | |
283 | unsigned i; | |
284 | for (i = 0; i < 2; i++) | |
285 | for (cur = i ? grub_term_input_autoload : grub_term_output_autoload; | |
286 | cur; cur = next) | |
287 | { | |
288 | next = cur->next; | |
289 | grub_free (cur->name); | |
290 | grub_free (cur->modname); | |
291 | grub_free (cur); | |
292 | } | |
293 | grub_term_input_autoload = NULL; | |
294 | grub_term_output_autoload = NULL; | |
295 | } | |
296 | ||
33c1ed4c VS |
297 | /* Read the file terminal.lst for auto-loading. */ |
298 | void | |
027de555 | 299 | read_terminal_list (const char *prefix) |
33c1ed4c | 300 | { |
33c1ed4c VS |
301 | char *filename; |
302 | grub_file_t file; | |
303 | char *buf = NULL; | |
304 | ||
33c1ed4c VS |
305 | if (!prefix) |
306 | { | |
307 | grub_errno = GRUB_ERR_NONE; | |
308 | return; | |
309 | } | |
310 | ||
92cd0f6e VS |
311 | filename = grub_xasprintf ("%s/" GRUB_TARGET_CPU "-" GRUB_PLATFORM |
312 | "/terminal.lst", prefix); | |
33c1ed4c VS |
313 | if (!filename) |
314 | { | |
315 | grub_errno = GRUB_ERR_NONE; | |
316 | return; | |
317 | } | |
318 | ||
33c1ed4c | 319 | file = grub_file_open (filename); |
4a8a763c | 320 | grub_free (filename); |
33c1ed4c VS |
321 | if (!file) |
322 | { | |
323 | grub_errno = GRUB_ERR_NONE; | |
324 | return; | |
325 | } | |
326 | ||
327 | /* Override previous terminal.lst. */ | |
328 | grub_terminal_autoload_free (); | |
329 | ||
330 | for (;; grub_free (buf)) | |
331 | { | |
332 | char *p, *name; | |
333 | struct grub_term_autoload *cur; | |
334 | struct grub_term_autoload **target = NULL; | |
335 | ||
336 | buf = grub_file_getline (file); | |
337 | ||
338 | if (! buf) | |
339 | break; | |
340 | ||
341 | switch (buf[0]) | |
342 | { | |
343 | case 'i': | |
344 | target = &grub_term_input_autoload; | |
345 | break; | |
346 | ||
347 | case 'o': | |
348 | target = &grub_term_output_autoload; | |
349 | break; | |
350 | } | |
351 | if (!target) | |
352 | continue; | |
353 | ||
354 | name = buf + 1; | |
355 | ||
356 | p = grub_strchr (name, ':'); | |
357 | if (! p) | |
358 | continue; | |
359 | ||
360 | *p = '\0'; | |
361 | while (*++p == ' ') | |
362 | ; | |
363 | ||
364 | cur = grub_malloc (sizeof (*cur)); | |
365 | if (!cur) | |
366 | { | |
367 | grub_errno = GRUB_ERR_NONE; | |
368 | continue; | |
369 | } | |
370 | ||
371 | cur->name = grub_strdup (name); | |
372 | if (! name) | |
373 | { | |
374 | grub_errno = GRUB_ERR_NONE; | |
375 | grub_free (cur); | |
376 | continue; | |
377 | } | |
378 | ||
379 | cur->modname = grub_strdup (p); | |
380 | if (! cur->modname) | |
381 | { | |
382 | grub_errno = GRUB_ERR_NONE; | |
33c1ed4c | 383 | grub_free (cur->name); |
e6d983ba | 384 | grub_free (cur); |
33c1ed4c VS |
385 | continue; |
386 | } | |
387 | cur->next = *target; | |
388 | *target = cur; | |
389 | } | |
390 | ||
391 | grub_file_close (file); | |
392 | ||
393 | grub_errno = GRUB_ERR_NONE; | |
394 | } | |
09f9aa3b VS |
395 | |
396 | static void | |
397 | putglyph (const struct grub_unicode_glyph *c, struct grub_term_output *term) | |
398 | { | |
399 | struct grub_unicode_glyph c2 = | |
400 | { | |
401 | .variant = 0, | |
402 | .attributes = 0, | |
403 | .ncomb = 0, | |
404 | .combining = 0, | |
405 | .estimated_width = 1 | |
406 | }; | |
407 | ||
408 | if (c->base == '\t' && term->getxy) | |
409 | { | |
410 | int n; | |
411 | ||
58664b94 | 412 | n = 8 - ((term->getxy (term) >> 8) & 7); |
09f9aa3b VS |
413 | c2.base = ' '; |
414 | while (n--) | |
58664b94 | 415 | (term->putchar) (term, &c2); |
09f9aa3b VS |
416 | |
417 | return; | |
418 | } | |
419 | ||
420 | if ((term->flags & GRUB_TERM_CODE_TYPE_MASK) | |
421 | == GRUB_TERM_CODE_TYPE_UTF8_LOGICAL | |
422 | || (term->flags & GRUB_TERM_CODE_TYPE_MASK) | |
423 | == GRUB_TERM_CODE_TYPE_UTF8_VISUAL) | |
424 | { | |
425 | int i; | |
426 | c2.estimated_width = grub_term_getcharwidth (term, c); | |
427 | for (i = -1; i < (int) c->ncomb; i++) | |
428 | { | |
429 | grub_uint8_t u8[20], *ptr; | |
430 | grub_uint32_t code; | |
431 | ||
432 | if (i == -1) | |
433 | { | |
434 | code = c->base; | |
435 | if ((term->flags & GRUB_TERM_CODE_TYPE_MASK) | |
a82890ff VS |
436 | == GRUB_TERM_CODE_TYPE_UTF8_VISUAL) |
437 | { | |
438 | if ((c->attributes & GRUB_UNICODE_GLYPH_ATTRIBUTE_MIRROR)) | |
439 | code = grub_unicode_mirror_code (code); | |
440 | code = grub_unicode_shape_code (code, c->attributes); | |
441 | } | |
09f9aa3b VS |
442 | } |
443 | else | |
444 | code = c->combining[i].code; | |
445 | ||
446 | grub_ucs4_to_utf8 (&code, 1, u8, sizeof (u8)); | |
447 | ||
448 | for (ptr = u8; *ptr; ptr++) | |
449 | { | |
450 | c2.base = *ptr; | |
58664b94 | 451 | (term->putchar) (term, &c2); |
09f9aa3b VS |
452 | c2.estimated_width = 0; |
453 | } | |
454 | } | |
455 | c2.estimated_width = 1; | |
456 | } | |
457 | else | |
58664b94 | 458 | (term->putchar) (term, c); |
09f9aa3b VS |
459 | |
460 | if (c->base == '\n') | |
461 | { | |
462 | c2.base = '\r'; | |
58664b94 | 463 | (term->putchar) (term, &c2); |
09f9aa3b VS |
464 | } |
465 | } | |
466 | ||
467 | static void | |
468 | putcode_real (grub_uint32_t code, struct grub_term_output *term) | |
469 | { | |
470 | struct grub_unicode_glyph c = | |
471 | { | |
472 | .variant = 0, | |
473 | .attributes = 0, | |
474 | .ncomb = 0, | |
475 | .combining = 0, | |
476 | .estimated_width = 1 | |
477 | }; | |
478 | ||
479 | c.base = map_code (code, term); | |
480 | putglyph (&c, term); | |
481 | } | |
482 | ||
483 | /* Put a Unicode character. */ | |
484 | void | |
485 | grub_putcode (grub_uint32_t code, struct grub_term_output *term) | |
486 | { | |
487 | /* Combining character by itself? */ | |
488 | if (grub_unicode_get_comb_type (code) != GRUB_UNICODE_COMB_NONE) | |
489 | return; | |
490 | ||
491 | putcode_real (code, term); | |
492 | } | |
493 | ||
3c69fb81 VS |
494 | static grub_ssize_t |
495 | get_maxwidth (struct grub_term_output *term, | |
496 | int margin_left, int margin_right) | |
497 | { | |
498 | struct grub_unicode_glyph space_glyph = { | |
499 | .base = ' ', | |
500 | .variant = 0, | |
501 | .attributes = 0, | |
502 | .ncomb = 0, | |
503 | .combining = 0 | |
504 | }; | |
505 | return (grub_term_width (term) | |
506 | - grub_term_getcharwidth (term, &space_glyph) | |
507 | * (margin_left + margin_right) - 1); | |
508 | } | |
509 | ||
510 | static grub_ssize_t | |
511 | get_startwidth (struct grub_term_output *term, | |
512 | int margin_left) | |
09f9aa3b | 513 | { |
58664b94 | 514 | return ((term->getxy (term) >> 8) & 0xff) - margin_left; |
3c69fb81 VS |
515 | } |
516 | ||
517 | static int | |
518 | print_ucs4_terminal (const grub_uint32_t * str, | |
519 | const grub_uint32_t * last_position, | |
520 | int margin_left, int margin_right, | |
521 | struct grub_term_output *term, | |
8b8a81fa VS |
522 | struct term_state *state, |
523 | int dry_run) | |
3c69fb81 VS |
524 | { |
525 | const grub_uint32_t *ptr; | |
8b8a81fa | 526 | grub_ssize_t startwidth = dry_run ? 0 : get_startwidth (term, margin_left); |
3c69fb81 VS |
527 | grub_ssize_t line_width = startwidth; |
528 | grub_ssize_t lastspacewidth = 0; | |
529 | grub_ssize_t max_width = get_maxwidth (term, margin_left, margin_right); | |
530 | const grub_uint32_t *line_start = str, *last_space = str - 1; | |
8b8a81fa | 531 | int lines = 0; |
3c69fb81 VS |
532 | |
533 | for (ptr = str; ptr < last_position; ptr++) | |
534 | { | |
535 | grub_ssize_t last_width = 0; | |
536 | if (grub_unicode_get_comb_type (*ptr) == GRUB_UNICODE_COMB_NONE) | |
537 | { | |
538 | struct grub_unicode_glyph c = { | |
539 | .variant = 0, | |
540 | .attributes = 0, | |
541 | .ncomb = 0, | |
542 | .combining = 0 | |
543 | }; | |
544 | c.base = *ptr; | |
545 | line_width += last_width = grub_term_getcharwidth (term, &c); | |
546 | } | |
547 | ||
548 | if (*ptr == ' ') | |
549 | { | |
550 | lastspacewidth = line_width; | |
551 | last_space = ptr; | |
552 | } | |
553 | ||
554 | if (line_width > max_width || *ptr == '\n') | |
555 | { | |
556 | const grub_uint32_t *ptr2; | |
557 | ||
558 | if (line_width > max_width && last_space > line_start) | |
559 | ptr = last_space; | |
560 | else if (line_width > max_width | |
6a6f8058 | 561 | && line_start == str && line_width - lastspacewidth < max_width - 5) |
3c69fb81 VS |
562 | { |
563 | ptr = str; | |
564 | lastspacewidth = startwidth; | |
565 | } | |
566 | else | |
567 | lastspacewidth = line_width - last_width; | |
568 | ||
8b8a81fa | 569 | lines++; |
3c69fb81 | 570 | |
8b8a81fa | 571 | if (!dry_run) |
3c69fb81 | 572 | { |
8b8a81fa VS |
573 | for (ptr2 = line_start; ptr2 < ptr; ptr2++) |
574 | { | |
575 | /* Skip combining characters on non-UTF8 terminals. */ | |
576 | if ((term->flags & GRUB_TERM_CODE_TYPE_MASK) | |
577 | != GRUB_TERM_CODE_TYPE_UTF8_LOGICAL | |
578 | && grub_unicode_get_comb_type (*ptr2) | |
579 | != GRUB_UNICODE_COMB_NONE) | |
580 | continue; | |
581 | putcode_real (*ptr2, term); | |
582 | } | |
583 | ||
584 | grub_print_spaces (term, margin_right); | |
585 | grub_putcode ('\n', term); | |
586 | if (state && ++state->num_lines | |
587 | >= (grub_ssize_t) grub_term_height (term) - 2) | |
588 | { | |
589 | state->backlog_ucs4 = (ptr == last_space || *ptr == '\n') | |
590 | ? ptr + 1 : ptr; | |
591 | state->backlog_len = last_position - state->backlog_ucs4; | |
592 | return 1; | |
593 | } | |
3c69fb81 VS |
594 | } |
595 | ||
596 | line_width -= lastspacewidth; | |
8b8a81fa VS |
597 | if (!dry_run) |
598 | grub_print_spaces (term, margin_left); | |
3c69fb81 VS |
599 | if (ptr == last_space || *ptr == '\n') |
600 | ptr++; | |
601 | line_start = ptr; | |
602 | } | |
603 | } | |
09f9aa3b | 604 | |
8b8a81fa VS |
605 | if (line_start < last_position) |
606 | lines++; | |
607 | if (!dry_run) | |
608 | { | |
609 | const grub_uint32_t *ptr2; | |
610 | for (ptr2 = line_start; ptr2 < last_position; ptr2++) | |
611 | { | |
612 | /* Skip combining characters on non-UTF8 terminals. */ | |
613 | if ((term->flags & GRUB_TERM_CODE_TYPE_MASK) | |
614 | != GRUB_TERM_CODE_TYPE_UTF8_LOGICAL | |
615 | && grub_unicode_get_comb_type (*ptr2) | |
616 | != GRUB_UNICODE_COMB_NONE) | |
617 | continue; | |
618 | putcode_real (*ptr2, term); | |
619 | } | |
620 | } | |
621 | return dry_run ? lines : 0; | |
3c69fb81 VS |
622 | } |
623 | ||
624 | static struct term_state * | |
625 | find_term_state (struct grub_term_output *term) | |
626 | { | |
627 | struct term_state *state; | |
628 | for (state = term_states; state; state = state->next) | |
629 | if (grub_strcmp (state->term_name, term->name) == 0) | |
630 | return state; | |
631 | ||
632 | state = grub_zalloc (sizeof (*state)); | |
633 | if (!state) | |
634 | { | |
635 | grub_errno = GRUB_ERR_NONE; | |
636 | return NULL; | |
637 | } | |
638 | ||
639 | state->term_name = grub_strdup (term->name); | |
640 | state->next = term_states; | |
641 | term_states = state; | |
642 | ||
643 | return state; | |
644 | } | |
645 | ||
646 | static int | |
647 | put_glyphs_terminal (const struct grub_unicode_glyph *visual, | |
648 | grub_ssize_t visual_len, | |
649 | int margin_left, int margin_right, | |
650 | struct grub_term_output *term, | |
651 | struct term_state *state) | |
652 | { | |
653 | const struct grub_unicode_glyph *visual_ptr; | |
654 | for (visual_ptr = visual; visual_ptr < visual + visual_len; visual_ptr++) | |
655 | { | |
656 | if (visual_ptr->base == '\n') | |
657 | grub_print_spaces (term, margin_right); | |
658 | putglyph (visual_ptr, term); | |
3c69fb81 | 659 | if (visual_ptr->base == '\n') |
249975ba VS |
660 | { |
661 | if (state && ++state->num_lines | |
662 | >= (grub_ssize_t) grub_term_height (term) - 2) | |
663 | { | |
664 | state->backlog_glyphs = visual_ptr + 1; | |
d547dc28 | 665 | state->backlog_len = visual_len - (visual_ptr - visual) - 1; |
249975ba VS |
666 | return 1; |
667 | } | |
668 | ||
669 | grub_print_spaces (term, margin_left); | |
670 | } | |
3c69fb81 VS |
671 | grub_free (visual_ptr->combining); |
672 | } | |
673 | return 0; | |
674 | } | |
675 | ||
676 | static int | |
677 | print_backlog (struct grub_term_output *term, | |
678 | int margin_left, int margin_right) | |
679 | { | |
680 | struct term_state *state = find_term_state (term); | |
681 | ||
682 | if (!state) | |
683 | return 0; | |
684 | ||
685 | if (state->backlog_ucs4) | |
686 | { | |
687 | int ret; | |
688 | ret = print_ucs4_terminal (state->backlog_ucs4, | |
689 | state->backlog_ucs4 + state->backlog_len, | |
8b8a81fa | 690 | margin_left, margin_right, term, state, 0); |
3c69fb81 VS |
691 | if (!ret) |
692 | { | |
693 | grub_free (state->free); | |
694 | state->free = NULL; | |
695 | state->backlog_len = 0; | |
d547dc28 | 696 | state->backlog_ucs4 = 0; |
3c69fb81 VS |
697 | } |
698 | return ret; | |
699 | } | |
700 | ||
701 | if (state->backlog_glyphs) | |
702 | { | |
703 | int ret; | |
704 | ret = put_glyphs_terminal (state->backlog_glyphs, | |
705 | state->backlog_len, | |
706 | margin_left, margin_right, term, state); | |
707 | if (!ret) | |
708 | { | |
709 | grub_free (state->free); | |
710 | state->free = NULL; | |
711 | state->backlog_len = 0; | |
d547dc28 | 712 | state->backlog_glyphs = 0; |
3c69fb81 VS |
713 | } |
714 | return ret; | |
715 | } | |
716 | ||
717 | return 0; | |
718 | } | |
719 | ||
720 | static int | |
721 | print_ucs4_real (const grub_uint32_t * str, | |
722 | const grub_uint32_t * last_position, | |
723 | int margin_left, int margin_right, | |
8b8a81fa VS |
724 | struct grub_term_output *term, int backlog, |
725 | int dry_run) | |
3c69fb81 VS |
726 | { |
727 | struct term_state *state = NULL; | |
728 | ||
8b8a81fa VS |
729 | if (!dry_run) |
730 | { | |
731 | if (backlog) | |
732 | state = find_term_state (term); | |
09f9aa3b | 733 | |
8b8a81fa VS |
734 | if (((term->getxy (term) >> 8) & 0xff) < margin_left) |
735 | grub_print_spaces (term, margin_left - ((term->getxy (term) >> 8) & 0xff)); | |
736 | } | |
09f9aa3b | 737 | |
09f9aa3b VS |
738 | if ((term->flags & GRUB_TERM_CODE_TYPE_MASK) |
739 | == GRUB_TERM_CODE_TYPE_VISUAL_GLYPHS | |
740 | || (term->flags & GRUB_TERM_CODE_TYPE_MASK) | |
741 | == GRUB_TERM_CODE_TYPE_UTF8_VISUAL) | |
742 | { | |
743 | grub_ssize_t visual_len; | |
744 | struct grub_unicode_glyph *visual; | |
3c69fb81 | 745 | int ret; |
09f9aa3b VS |
746 | |
747 | auto grub_ssize_t getcharwidth (const struct grub_unicode_glyph *c); | |
748 | grub_ssize_t getcharwidth (const struct grub_unicode_glyph *c) | |
749 | { | |
750 | return grub_term_getcharwidth (term, c); | |
751 | } | |
752 | ||
753 | visual_len = grub_bidi_logical_to_visual (str, last_position - str, | |
754 | &visual, getcharwidth, | |
3c69fb81 VS |
755 | get_maxwidth (term, |
756 | margin_left, | |
757 | margin_right), | |
758 | get_startwidth (term, | |
759 | margin_left)); | |
09f9aa3b VS |
760 | if (visual_len < 0) |
761 | { | |
762 | grub_print_error (); | |
3c69fb81 | 763 | return 0; |
09f9aa3b | 764 | } |
8b8a81fa VS |
765 | if (dry_run) |
766 | { | |
767 | struct grub_unicode_glyph *vptr; | |
768 | ret = 0; | |
769 | for (vptr = visual; vptr < visual + visual_len; vptr++) | |
770 | if (vptr->base == '\n') | |
771 | ret++; | |
772 | if (visual_len && visual[visual_len - 1].base != '\n') | |
773 | ret++; | |
774 | grub_free (visual); | |
775 | } | |
3c69fb81 | 776 | else |
8b8a81fa VS |
777 | { |
778 | ret = put_glyphs_terminal (visual, visual_len, margin_left, | |
779 | margin_right, term, state); | |
780 | if (!ret) | |
781 | grub_free (visual); | |
782 | else | |
783 | state->free = visual; | |
784 | } | |
3c69fb81 | 785 | return ret; |
09f9aa3b | 786 | } |
3c69fb81 | 787 | return print_ucs4_terminal (str, last_position, margin_left, margin_right, |
8b8a81fa | 788 | term, state, dry_run); |
3c69fb81 | 789 | } |
09f9aa3b | 790 | |
3c69fb81 VS |
791 | void |
792 | grub_print_ucs4 (const grub_uint32_t * str, | |
793 | const grub_uint32_t * last_position, | |
794 | int margin_left, int margin_right, | |
795 | struct grub_term_output *term) | |
796 | { | |
797 | print_ucs4_real (str, last_position, margin_left, margin_right, | |
8b8a81fa | 798 | term, 0, 0); |
09f9aa3b VS |
799 | } |
800 | ||
8b8a81fa VS |
801 | int |
802 | grub_ucs4_count_lines (const grub_uint32_t * str, | |
803 | const grub_uint32_t * last_position, | |
804 | int margin_left, int margin_right, | |
805 | struct grub_term_output *term) | |
806 | { | |
807 | return print_ucs4_real (str, last_position, margin_left, margin_right, | |
808 | term, 0, 1); | |
809 | } | |
3c69fb81 | 810 | |
09f9aa3b VS |
811 | void |
812 | grub_xputs_normal (const char *str) | |
813 | { | |
5303b85d | 814 | grub_uint32_t *unicode_str = NULL, *unicode_last_position; |
3c69fb81 | 815 | int backlog = 0; |
5303b85d VS |
816 | grub_term_output_t term; |
817 | ||
818 | grub_error_push (); | |
81b0623a | 819 | grub_utf8_to_ucs4_alloc (str, &unicode_str, |
5303b85d VS |
820 | &unicode_last_position); |
821 | grub_error_pop (); | |
09f9aa3b | 822 | |
3c69fb81 VS |
823 | if (!unicode_str) |
824 | { | |
902f75f6 VS |
825 | for (; *str; str++) |
826 | { | |
5303b85d VS |
827 | struct grub_unicode_glyph c = |
828 | { | |
829 | .variant = 0, | |
830 | .attributes = 0, | |
831 | .ncomb = 0, | |
832 | .combining = 0, | |
833 | .estimated_width = 1, | |
834 | .base = *str | |
835 | }; | |
902f75f6 VS |
836 | |
837 | FOR_ACTIVE_TERM_OUTPUTS(term) | |
838 | { | |
5303b85d | 839 | (term->putchar) (term, &c); |
902f75f6 | 840 | } |
5303b85d VS |
841 | if (*str == '\n') |
842 | { | |
843 | c.base = '\r'; | |
844 | FOR_ACTIVE_TERM_OUTPUTS(term) | |
845 | { | |
846 | (term->putchar) (term, &c); | |
847 | } | |
848 | } | |
902f75f6 VS |
849 | } |
850 | ||
3c69fb81 VS |
851 | return; |
852 | } | |
853 | ||
09f9aa3b VS |
854 | FOR_ACTIVE_TERM_OUTPUTS(term) |
855 | { | |
3c69fb81 VS |
856 | int cur; |
857 | cur = print_ucs4_real (unicode_str, unicode_last_position, 0, 0, | |
8b8a81fa | 858 | term, grub_more, 0); |
3c69fb81 VS |
859 | if (cur) |
860 | backlog = 1; | |
09f9aa3b | 861 | } |
3c69fb81 VS |
862 | while (backlog) |
863 | { | |
864 | print_more (); | |
865 | backlog = 0; | |
866 | FOR_ACTIVE_TERM_OUTPUTS(term) | |
867 | { | |
868 | int cur; | |
869 | cur = print_backlog (term, 0, 0); | |
870 | if (cur) | |
871 | backlog = 1; | |
872 | } | |
873 | } | |
81b0623a | 874 | grub_free (unicode_str); |
09f9aa3b | 875 | } |
919e37b0 VS |
876 | |
877 | void | |
878 | grub_cls (void) | |
879 | { | |
880 | struct grub_term_output *term; | |
881 | ||
882 | FOR_ACTIVE_TERM_OUTPUTS(term) | |
883 | { | |
884 | if ((term->flags & GRUB_TERM_DUMB) || (grub_env_get ("debug"))) | |
885 | { | |
886 | grub_putcode ('\n', term); | |
887 | grub_term_refresh (term); | |
888 | } | |
889 | else | |
890 | (term->cls) (term); | |
891 | } | |
892 | } |