struct line
{
/* The line buffer. */
- char *buf;
+ grub_uint32_t *buf;
/* The length of the line. */
int len;
/* The maximum length of the line. */
int x;
/* The Y coordinate. */
int y;
+ int y_line_start;
/* Number of entries. */
int num_entries;
};
};
/* Used for storing completion items temporarily. */
-static struct line completion_buffer;
+static struct {
+ char *buf;
+ grub_size_t len;
+ grub_size_t max_len;
+} completion_buffer;
static int completion_type;
/* Initialize a line. */
init_line (struct line *linep)
{
linep->len = 0;
- linep->max_len = 80; /* XXX */
- linep->buf = grub_malloc (linep->max_len + 1);
+ linep->max_len = 80;
+ linep->buf = grub_malloc ((linep->max_len + 1) * sizeof (linep->buf[0]));
if (! linep->buf)
return 0;
{
if (linep->max_len < linep->len + extra)
{
- linep->max_len = linep->len + extra + 80; /* XXX */
- linep->buf = grub_realloc (linep->buf, linep->max_len + 1);
+ linep->max_len = 2 * (linep->len + extra);
+ linep->buf = grub_realloc (linep->buf, (linep->max_len + 1) * sizeof (linep->buf[0]));
if (! linep->buf)
return 0;
}
static int
get_logical_num_lines (struct line *linep, struct per_term_screen *term_screen)
{
- return (linep->len / grub_term_entry_width (term_screen->term)) + 1;
+ return (grub_getstringwidth (linep->buf, linep->buf + linep->len,
+ term_screen->term)
+ / grub_term_entry_width (term_screen->term)) + 1;
}
-/* Print a line. */
static void
-print_line (struct line *linep, int offset, int start, int y,
- struct per_term_screen *term_screen)
+advance (struct screen *screen)
{
- grub_term_gotoxy (term_screen->term,
- GRUB_TERM_LEFT_BORDER_X + GRUB_TERM_MARGIN + start + 1,
- y + GRUB_TERM_FIRST_ENTRY_Y);
+ unsigned i;
+ struct grub_unicode_glyph glyph;
- if (linep->len >= offset + grub_term_entry_width (term_screen->term))
- {
- char *p, c;
- p = linep->buf + offset + grub_term_entry_width (term_screen->term);
- c = *p;
- *p = 0;
- grub_puts_terminal (linep->buf + offset + start, term_screen->term);
- *p = c;
- grub_putcode ('\\', term_screen->term);
- }
- else
+ screen->column += grub_unicode_aglomerate_comb (screen->lines[screen->line].buf + screen->column,
+ screen->lines[screen->line].len - screen->column,
+ &glyph);
+
+ for (i = 0; i < screen->nterms; i++)
{
- int i;
- char *p, c;
-
- p = linep->buf + linep->len;
- c = *p;
- *p = 0;
- grub_puts_terminal (linep->buf + offset + start, term_screen->term);
- *p = c;
-
- for (i = 0;
- i <= grub_term_entry_width (term_screen->term) - linep->len + offset;
- i++)
- grub_putcode (' ', term_screen->term);
+ grub_ssize_t width;
+ width = grub_term_getcharwidth (screen->terms[i].term, &glyph);
+ screen->terms[i].x += width;
+ if (screen->terms[i].x
+ == grub_term_entry_width (screen->terms[i].term))
+ {
+ screen->terms[i].x = 0;
+ screen->terms[i].y++;
+ }
+ if (screen->terms[i].x
+ > grub_term_entry_width (screen->terms[i].term))
+ {
+ screen->terms[i].x = width;
+ screen->terms[i].y++;
+ }
}
+ grub_free (glyph.combining);
+}
+
+static void
+advance_to (struct screen *screen, int c)
+{
+ if (c > screen->lines[screen->line].len)
+ c = screen->lines[screen->line].len;
+
+ while (screen->column < c)
+ advance (screen);
+}
+
+/* Print a line. */
+static int
+print_line (struct line *linep, int offset, int y,
+ struct per_term_screen *term_screen, int dry_run)
+{
+ int x;
+ int i;
+
+ grub_term_gotoxy (term_screen->term,
+ GRUB_TERM_LEFT_BORDER_X + GRUB_TERM_MARGIN + 1,
+ y + GRUB_TERM_FIRST_ENTRY_Y);
+
+ x = 0;
+ for (i = 0; i + offset < (int) linep->len;)
+ {
+ grub_ssize_t width;
+ grub_size_t delta = 0;
+ struct grub_unicode_glyph glyph;
+
+ delta = grub_unicode_aglomerate_comb (linep->buf + offset + i,
+ linep->len - offset - i,
+ &glyph);
+ width = grub_term_getcharwidth (term_screen->term, &glyph);
+ grub_free (glyph.combining);
+
+ if (x + width > grub_term_entry_width (term_screen->term) && x != 0)
+ break;
+ x += width;
+ i += delta;
+ }
+
+ if (dry_run)
+ return i;
+
+ grub_print_ucs4 (linep->buf + offset,
+ linep->buf + offset + i, 0, 0, term_screen->term);
+
+ if (i + offset != linep->len)
+ grub_putcode ('\\', term_screen->term);
+ else
+ {
+ for (;
+ x < (int) grub_term_entry_width (term_screen->term);
+ x++)
+ grub_putcode (' ', term_screen->term);
+ }
+ return i;
}
/* Print an empty line. */
/* Check if scrolling is necessary. */
if (term_screen->y < 0 || term_screen->y >= term_screen->num_entries)
{
+ int delta;
if (term_screen->y < 0)
- term_screen->y = 0;
+ delta = -term_screen->y;
else
- term_screen->y = term_screen->num_entries - 1;
+ delta = term_screen->num_entries - 1 - term_screen->y;
+ term_screen->y += delta;
+ term_screen->y_line_start += delta;
region_start = 0;
region_column = 0;
{
/* Draw lines. This code is tricky, because this must calculate logical
positions. */
- y = term_screen->y - screen->column
- / grub_term_entry_width (term_screen->term);
+ y = term_screen->y_line_start;
i = screen->line;
linep = screen->lines + i;
while (y > 0)
do
{
int column;
+ int off = 0;
if (linep >= screen->lines + screen->num_lines)
break;
column += grub_term_entry_width (term_screen->term), y++)
{
if (y < 0)
- continue;
+ {
+ off += print_line (linep, off, y, term_screen, 1);
+ continue;
+ }
if (i == region_start)
{
&& region_column
< (column
+ grub_term_entry_width (term_screen->term)))
- print_line (linep, column, region_column - column, y,
- term_screen);
+ off += print_line (linep, off, y, term_screen, 0);
else if (region_column < column)
- print_line (linep, column, 0, y, term_screen);
+ off += print_line (linep, off, y, term_screen, 0);
+ else
+ off += print_line (linep, off, y, term_screen, 1);
}
else if (i > region_start && mode == ALL_LINES)
- print_line (linep, column, 0, y, term_screen);
+ off += print_line (linep, off, y, term_screen, 0);
}
if (y == term_screen->num_entries)
{
- if (column <= linep->len || i + 1 < screen->num_lines)
+ if (off <= linep->len || i + 1 < screen->num_lines)
down_flag = 1;
}
if (mode == ALL_LINES && i == screen->num_lines)
for (; y < term_screen->num_entries; y++)
print_empty_line (y, term_screen);
-
}
while (y < term_screen->num_entries);
grub_memmove (next_linep->buf,
current_linep->buf + screen->column,
- size);
+ size * sizeof (next_linep->buf[0]));
current_linep->len = screen->column;
next_linep->len = size;
{
screen->terms[i].x = 0;
screen->terms[i].y++;
+ screen->terms[i].y_line_start = screen->terms[i].y;
}
s++;
}
struct line *current_linep;
int size;
int orig_num[screen->nterms], new_num[screen->nterms];
+ grub_uint32_t *unicode_msg;
/* Find a string delimited by LF. */
p = grub_strchr (s, '\n');
/* Insert the string. */
current_linep = screen->lines + screen->line;
- size = p - s;
+ unicode_msg = grub_malloc ((p - s) * sizeof (grub_uint32_t));
+
+ if (!unicode_msg)
+ return 0;
+
+ size = grub_utf8_to_ucs4 (unicode_msg, (p - s),
+ (grub_uint8_t *) s, (p - s), 0);
+
if (! ensure_space (current_linep, size))
return 0;
grub_memmove (current_linep->buf + screen->column + size,
current_linep->buf + screen->column,
- current_linep->len - screen->column);
+ (current_linep->len - screen->column)
+ * sizeof (current_linep->buf[0]));
grub_memmove (current_linep->buf + screen->column,
- s,
- size);
+ unicode_msg,
+ size * sizeof (current_linep->buf[0]));
+ grub_free (unicode_msg);
+
for (i = 0; i < screen->nterms; i++)
orig_num[i] = get_logical_num_lines (current_linep,
&screen->terms[i]);
mode[i] = SINGLE_LINE;
/* Move the cursor. */
- screen->column += size;
+ advance_to (screen, screen->column + size);
+
screen->real_column = screen->column;
- for (i = 0; i < screen->nterms; i++)
- {
- screen->terms[i].x += size;
- screen->terms[i].y += screen->terms[i].x
- / grub_term_entry_width (screen->terms[i].term);
- screen->terms[i].x
- %= grub_term_entry_width (screen->terms[i].term);
- }
s = p;
}
}
{
screen->terms[i].x = 0;
screen->terms[i].y = 0;
+ screen->terms[i].y_line_start = screen->terms[i].y;
}
return screen;
linep = screen->lines + screen->line;
if (screen->column < linep->len)
- {
- screen->column++;
- for (i = 0; i < screen->nterms; i++)
- {
- screen->terms[i].x++;
- if (screen->terms[i].x
- == grub_term_entry_width (screen->terms[i].term))
- {
- screen->terms[i].x = 0;
- screen->terms[i].y++;
- }
- }
- }
+ advance (screen);
else if (screen->num_lines > screen->line + 1)
{
screen->column = 0;
{
screen->terms[i].x = 0;
screen->terms[i].y++;
+ screen->terms[i].y_line_start = screen->terms[i].y;
}
}
if (screen->column > 0)
{
+ struct grub_unicode_glyph glyph;
+ struct line *linep;
+
+ linep = screen->lines + screen->line;
+
screen->column--;
+ screen->column = grub_unicode_get_comb_start (linep->buf,
+ linep->buf + screen->column)
+ - linep->buf;
+
+ grub_unicode_aglomerate_comb (screen->lines[screen->line].buf + screen->column,
+ screen->lines[screen->line].len - screen->column,
+ &glyph);
+
for (i = 0; i < screen->nterms; i++)
{
- screen->terms[i].x--;
- if (screen->terms[i].x == -1)
+ grub_ssize_t width;
+ width = grub_term_getcharwidth (screen->terms[i].term, &glyph);
+ screen->terms[i].x -= width;
+ if (screen->terms[i].x < 0)
{
screen->terms[i].x
= grub_term_entry_width (screen->terms[i].term) - 1;
screen->terms[i].y--;
}
}
+ grub_free (glyph.combining);
}
else if (screen->line > 0)
{
struct line *linep;
- screen->line--;
linep = screen->lines + screen->line;
- screen->column = linep->len;
+ screen->column = 0;
+ screen->line--;
+
for (i = 0; i < screen->nterms; i++)
{
- screen->terms[i].x = screen->column
- % grub_term_entry_width (screen->terms[i].term);
- screen->terms[i].y--;
+ screen->terms[i].y_line_start -= get_logical_num_lines (linep, &screen->terms[i]);
+ screen->terms[i].y = screen->terms[i].y_line_start;
+ screen->terms[i].x = 0;
}
+ advance_to (screen, screen->lines[screen->line].len);
}
screen->real_column = screen->column;
struct line *linep;
int col;
- /* How many physical lines from the current position
- to the first physical line? */
- col = screen->column;
-
screen->line--;
linep = screen->lines + screen->line;
if (linep->len < screen->real_column)
- screen->column = linep->len;
+ col = linep->len;
else
- screen->column = screen->real_column;
+ col = screen->real_column;
+
+ screen->column = 0;
for (i = 0; i < screen->nterms; i++)
{
- int dy;
- dy = col / grub_term_entry_width (screen->terms[i].term);
-
- /* How many physical lines from the current position
- to the last physical line? */
- dy += (linep->len / grub_term_entry_width (screen->terms[i].term)
- - screen->column
- / grub_term_entry_width (screen->terms[i].term));
-
- screen->terms[i].y -= dy + 1;
- screen->terms[i].x
- = screen->column % grub_term_entry_width (screen->terms[i].term);
- }
+ screen->terms[i].y_line_start -= get_logical_num_lines (linep, &screen->terms[i]);
+ screen->terms[i].y = screen->terms[i].y_line_start;
+ screen->terms[i].x = 0;
+ }
+ advance_to (screen, col);
}
else
{
for (i = 0; i < screen->nterms; i++)
{
- screen->terms[i].y
- -= screen->column / grub_term_entry_width (screen->terms[i].term);
+ screen->terms[i].y = screen->terms[i].y_line_start;
screen->terms[i].x = 0;
}
screen->column = 0;
if (screen->line < screen->num_lines - 1)
{
struct line *linep;
- int l1, c1;
+ int c;
/* How many physical lines from the current position
to the last physical line? */
linep = screen->lines + screen->line;
- l1 = linep->len;
- c1 = screen->column;
screen->line++;
-
- linep++;
- if (linep->len < screen->real_column)
- screen->column = linep->len;
+ if ((linep + 1)->len < screen->real_column)
+ c = (linep + 1)->len;
else
- screen->column = screen->real_column;
+ c = screen->real_column;
+ screen->column = 0;
for (i = 0; i < screen->nterms; i++)
{
- int dy;
- dy = l1 / grub_term_entry_width (screen->terms[i].term)
- - c1 / grub_term_entry_width (screen->terms[i].term);
- /* How many physical lines from the current position
- to the first physical line? */
- dy += screen->column / grub_term_entry_width (screen->terms[i].term);
- screen->terms[i].y += dy + 1;
- screen->terms[i].x = screen->column
- % grub_term_entry_width (screen->terms[i].term);
+ screen->terms[i].y_line_start += get_logical_num_lines (linep, &screen->terms[i]);
+ screen->terms[i].x = 0;
+ screen->terms[i].y = screen->terms[i].y_line_start;
}
+ advance_to (screen, c);
}
else
- {
- struct line *linep;
- int l, s;
-
- linep = screen->lines + screen->line;
- l = linep->len;
- s = screen->column;
- screen->column = linep->len;
- for (i = 0; i < screen->nterms; i++)
- {
- screen->terms[i].y
- += (l / grub_term_entry_width (screen->terms[i].term)
- - s / grub_term_entry_width (screen->terms[i].term));
- screen->terms[i].x
- = screen->column % grub_term_entry_width (screen->terms[i].term);
- }
- }
+ advance_to (screen, screen->lines[screen->line].len);
if (update)
update_screen_all (screen, screen->num_lines, 0, 0, 0, NO_LINE);
beginning_of_line (struct screen *screen, int update)
{
unsigned i;
- int col;
-
- col = screen->column;
+
screen->column = screen->real_column = 0;
for (i = 0; i < screen->nterms; i++)
{
screen->terms[i].x = 0;
- screen->terms[i].y -= col / grub_term_entry_width (screen->terms[i].term);
+ screen->terms[i].y = screen->terms[i].y_line_start;
}
if (update)
static int
end_of_line (struct screen *screen, int update)
{
- struct line *linep;
- unsigned i;
- int col;
-
- linep = screen->lines + screen->line;
- col = screen->column;
- screen->column = screen->real_column = linep->len;
- for (i = 0; i < screen->nterms; i++)
- {
- screen->terms[i].y
- += (linep->len / grub_term_entry_width (screen->terms->term)
- - col / grub_term_entry_width (screen->terms->term));
- screen->terms[i].x
- = screen->column % grub_term_entry_width (screen->terms->term);
- }
+ advance_to (screen, screen->lines[screen->line].len);
if (update)
update_screen_all (screen, screen->num_lines, 0, 0, 0, NO_LINE);
grub_memmove (linep->buf + screen->column,
linep->buf + screen->column + 1,
- linep->len - screen->column - 1);
+ (linep->len - screen->column - 1)
+ * sizeof (linep->buf[0]));
linep->len--;
start = screen->line;
if (! ensure_space (linep, next_linep->len))
return 0;
- grub_memmove (linep->buf + linep->len, next_linep->buf, next_linep->len);
+ grub_memmove (linep->buf + linep->len, next_linep->buf,
+ next_linep->len * sizeof (linep->buf[0]));
linep->len += next_linep->len;
grub_free (next_linep->buf);
return 0;
for (i = 0; i < screen->nterms; i++)
- screen->terms[i].y = saved_y[i];
+ {
+ screen->terms[i].y = saved_y[i];
+ screen->terms[i].y_line_start = screen->terms[i].y;
+ }
if (update)
update_screen_all (screen, screen->line, screen->column, 0, 1, ALL_LINES);
static int
complete (struct screen *screen, int continuous, int update)
{
- char saved_char;
struct line *linep;
int restore;
char *insert;
grub_uint32_t *ucs4;
grub_size_t buflen;
grub_ssize_t ucs4len;
+ char *u8;
if (continuous)
count++;
completion_buffer.max_len = 0;
linep = screen->lines + screen->line;
- saved_char = linep->buf[screen->column];
- linep->buf[screen->column] = '\0';
-
- insert = grub_normal_do_completion (linep->buf, &restore, store_completion);
+ u8 = grub_ucs4_to_utf8_alloc (linep->buf, screen->column);
+ if (!u8)
+ return 1;
- linep->buf[screen->column] = saved_char;
+ insert = grub_normal_do_completion (u8, &restore, store_completion);
if (completion_buffer.buf)
{
screen->terms[i].term = term;
screen->terms[i].x = 0;
screen->terms[i].y = 0;
+ screen->terms[i].y_line_start = screen->terms[i].y;
i++;
}
/* Draw the screen. */
struct term_state *next;
const struct grub_unicode_glyph *backlog_glyphs;
const grub_uint32_t *backlog_ucs4;
+ int backlog_fixed_tab;
grub_size_t backlog_len;
void *free;
char *term_name;
};
+static int
+print_ucs4_real (const grub_uint32_t * str,
+ const grub_uint32_t * last_position,
+ int margin_left, int margin_right,
+ struct grub_term_output *term, int backlog,
+ int dry_run, int fixed_tab);
+
static struct term_state *term_states = NULL;
/* If the more pager is active. */
static int grub_more;
static void
-putcode_real (grub_uint32_t code, struct grub_term_output *term);
+putcode_real (grub_uint32_t code, struct grub_term_output *term, int fixed_tab);
void
grub_normal_reset_more (void)
return;
}
- grub_print_ucs4 (unicode_str, unicode_last_position, 0, 0, term);
+ print_ucs4_real (unicode_str, unicode_last_position, 0, 0, term, 0, 0, 0);
grub_free (unicode_str);
}
}
static void
-putglyph (const struct grub_unicode_glyph *c, struct grub_term_output *term)
+putglyph (const struct grub_unicode_glyph *c, struct grub_term_output *term,
+ int fixed_tab)
{
struct grub_unicode_glyph c2 =
{
.estimated_width = 1
};
+ if (c->base == '\t' && fixed_tab)
+ {
+ int n;
+
+ n = GRUB_TERM_TAB_WIDTH;
+ c2.base = ' ';
+ while (n--)
+ (term->putchar) (term, &c2);
+
+ return;
+ }
+
if (c->base == '\t' && term->getxy)
{
int n;
- n = 8 - ((term->getxy (term) >> 8) & 7);
+ n = GRUB_TERM_TAB_WIDTH - ((term->getxy (term) >> 8)
+ % GRUB_TERM_TAB_WIDTH);
c2.base = ' ';
while (n--)
(term->putchar) (term, &c2);
}
static void
-putcode_real (grub_uint32_t code, struct grub_term_output *term)
+putcode_real (grub_uint32_t code, struct grub_term_output *term, int fixed_tab)
{
struct grub_unicode_glyph c =
{
};
c.base = map_code (code, term);
- putglyph (&c, term);
+ putglyph (&c, term, fixed_tab);
}
/* Put a Unicode character. */
if (grub_unicode_get_comb_type (code) != GRUB_UNICODE_COMB_NONE)
return;
- putcode_real (code, term);
+ putcode_real (code, term, 0);
}
static grub_ssize_t
int margin_left, int margin_right,
struct grub_term_output *term,
struct term_state *state,
- int dry_run)
+ int dry_run, int fixed_tab)
{
const grub_uint32_t *ptr;
grub_ssize_t startwidth = dry_run ? 0 : get_startwidth (term, margin_left);
&& grub_unicode_get_comb_type (*ptr2)
!= GRUB_UNICODE_COMB_NONE)
continue;
- putcode_real (*ptr2, term);
+ putcode_real (*ptr2, term, fixed_tab);
}
grub_print_spaces (term, margin_right);
state->backlog_ucs4 = (ptr == last_space || *ptr == '\n')
? ptr + 1 : ptr;
state->backlog_len = last_position - state->backlog_ucs4;
+ state->backlog_fixed_tab = fixed_tab;
return 1;
}
}
&& grub_unicode_get_comb_type (*ptr2)
!= GRUB_UNICODE_COMB_NONE)
continue;
- putcode_real (*ptr2, term);
+ putcode_real (*ptr2, term, fixed_tab);
}
}
return dry_run ? lines : 0;
grub_ssize_t visual_len,
int margin_left, int margin_right,
struct grub_term_output *term,
- struct term_state *state)
+ struct term_state *state, int fixed_tab)
{
const struct grub_unicode_glyph *visual_ptr;
for (visual_ptr = visual; visual_ptr < visual + visual_len; visual_ptr++)
{
if (visual_ptr->base == '\n')
grub_print_spaces (term, margin_right);
- putglyph (visual_ptr, term);
+ putglyph (visual_ptr, term, fixed_tab);
if (visual_ptr->base == '\n')
{
if (state && ++state->num_lines
{
state->backlog_glyphs = visual_ptr + 1;
state->backlog_len = visual_len - (visual_ptr - visual) - 1;
+ state->backlog_fixed_tab = fixed_tab;
return 1;
}
int ret;
ret = print_ucs4_terminal (state->backlog_ucs4,
state->backlog_ucs4 + state->backlog_len,
- margin_left, margin_right, term, state, 0);
+ margin_left, margin_right, term, state, 0,
+ state->backlog_fixed_tab);
if (!ret)
{
grub_free (state->free);
int ret;
ret = put_glyphs_terminal (state->backlog_glyphs,
state->backlog_len,
- margin_left, margin_right, term, state);
+ margin_left, margin_right, term, state,
+ state->backlog_fixed_tab);
if (!ret)
{
grub_free (state->free);
const grub_uint32_t * last_position,
int margin_left, int margin_right,
struct grub_term_output *term, int backlog,
- int dry_run)
+ int dry_run, int fixed_tab)
{
struct term_state *state = NULL;
else
{
ret = put_glyphs_terminal (visual, visual_len, margin_left,
- margin_right, term, state);
+ margin_right, term, state, fixed_tab);
if (!ret)
grub_free (visual);
else
return ret;
}
return print_ucs4_terminal (str, last_position, margin_left, margin_right,
- term, state, dry_run);
+ term, state, dry_run, fixed_tab);
}
void
struct grub_term_output *term)
{
print_ucs4_real (str, last_position, margin_left, margin_right,
- term, 0, 0);
+ term, 0, 0, 1);
}
int
struct grub_term_output *term)
{
return print_ucs4_real (str, last_position, margin_left, margin_right,
- term, 0, 1);
+ term, 0, 1, 1);
}
void
{
int cur;
cur = print_ucs4_real (unicode_str, unicode_last_position, 0, 0,
- term, grub_more, 0);
+ term, grub_more, 0, 0);
if (cur)
backlog = 1;
}