eeeee5580abea9798278ef85cf417366cf4f0e0a
[grub.git] / grub-core / normal / menu_entry.c
1 /*
2  *  GRUB  --  GRand Unified Bootloader
3  *  Copyright (C) 2005,2006,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
19 #include <grub/normal.h>
20 #include <grub/term.h>
21 #include <grub/misc.h>
22 #include <grub/mm.h>
23 #include <grub/loader.h>
24 #include <grub/command.h>
25 #include <grub/parser.h>
26 #include <grub/script_sh.h>
27 #include <grub/auth.h>
28 #include <grub/i18n.h>
29 #include <grub/charset.h>
30
31 enum update_mode
32   {
33     NO_LINE,
34     SINGLE_LINE,
35     ALL_LINES
36   };
37
38 struct line
39 {
40   /* The line buffer.  */
41   grub_uint32_t *buf;
42   /* The length of the line.  */
43   int len;
44   /* The maximum length of the line.  */
45   int max_len;
46   struct grub_term_pos **pos;
47 };
48
49 struct per_term_screen
50 {
51   struct grub_term_output *term;
52   int y_line_start;
53   struct grub_term_screen_geometry geo;
54   /* Scratch variables used when updating. Having them here avoids
55      loads of small mallocs.  */
56   int orig_num;
57   int down;
58   enum update_mode mode;
59 };
60
61 struct screen
62 {
63   /* The array of lines.  */
64   struct line *lines;
65   /* The number of lines.  */
66   int num_lines;
67   /* The current column.  */
68   int column;
69   /* The real column.  */
70   int real_column;
71   /* The current line.  */
72   int line;
73   /* The kill buffer.  */
74   char *killed_text;
75   /* The flag of a completion window.  */
76   int completion_shown;
77
78   int submenu;
79
80   struct per_term_screen *terms;
81   unsigned nterms;
82 };
83
84 /* Used for storing completion items temporarily.  */
85 static struct {
86   char *buf;
87   grub_size_t len;
88   grub_size_t max_len;
89 } completion_buffer;
90 static int completion_type;
91
92 /* Initialize a line.  */
93 static int
94 init_line (struct screen *screen, struct line *linep)
95 {
96   linep->len = 0;
97   linep->max_len = 80;
98   linep->buf = grub_malloc ((linep->max_len + 1) * sizeof (linep->buf[0]));
99   linep->pos = grub_zalloc (screen->nterms * sizeof (linep->pos[0]));
100   if (! linep->buf || !linep->pos)
101     {
102       grub_free (linep->buf);
103       grub_free (linep->pos);
104       return 0;
105     }
106
107   return 1;
108 }
109
110 /* Allocate extra space if necessary.  */
111 static int
112 ensure_space (struct line *linep, int extra)
113 {
114   if (linep->max_len < linep->len + extra)
115     {
116       linep->max_len = 2 * (linep->len + extra);
117       linep->buf = grub_realloc (linep->buf, (linep->max_len + 1) * sizeof (linep->buf[0]));
118       if (! linep->buf)
119         return 0;
120     }
121
122   return 1;
123 }
124
125 /* Return the number of lines occupied by this line on the screen.  */
126 static int
127 get_logical_num_lines (struct line *linep, struct per_term_screen *term_screen)
128 {
129   grub_size_t width = grub_getstringwidth (linep->buf, linep->buf + linep->len,
130                                            term_screen->term);
131
132   /* Empty line still consumes space on screen */
133   return width ? (width + (unsigned) term_screen->geo.entry_width - 1) /
134                  (unsigned) term_screen->geo.entry_width
135                : 1;
136 }
137
138 static void
139 advance_to (struct screen *screen, int c)
140 {
141   if (c > screen->lines[screen->line].len)
142     c = screen->lines[screen->line].len;
143
144   screen->column = grub_unicode_get_comb_end (screen->lines[screen->line].buf
145                                               + screen->lines[screen->line].len,
146                                               screen->lines[screen->line].buf
147                                               + c)
148     - screen->lines[screen->line].buf;
149 }
150
151 /* Print an empty line.  */
152 static void
153 print_empty_line (int y, struct per_term_screen *term_screen)
154 {
155   int i;
156
157   grub_term_gotoxy (term_screen->term,
158                     (struct grub_term_coordinate) { term_screen->geo.first_entry_x,
159                         y + term_screen->geo.first_entry_y });
160
161   for (i = 0; i < term_screen->geo.entry_width + 1; i++)
162     grub_putcode (' ', term_screen->term);
163 }
164
165 static void
166 print_updown (int upflag, int downflag, struct per_term_screen *term_screen)
167 {
168   grub_term_gotoxy (term_screen->term,
169                     (struct grub_term_coordinate) { term_screen->geo.first_entry_x
170                         + term_screen->geo.entry_width + 1
171                         + term_screen->geo.border,
172                         term_screen->geo.first_entry_y });
173
174   if (upflag && downflag)
175     grub_putcode (GRUB_UNICODE_UPDOWNARROW, term_screen->term);
176   else if (upflag)
177     grub_putcode (GRUB_UNICODE_UPARROW, term_screen->term);
178   else if (downflag)
179     grub_putcode (GRUB_UNICODE_DOWNARROW, term_screen->term);
180   else
181     grub_putcode (' ', term_screen->term);
182 }
183
184 /* Print an up arrow.  */
185 static void
186 print_up (int flag, struct per_term_screen *term_screen)
187 {
188   grub_term_gotoxy (term_screen->term,
189                     (struct grub_term_coordinate) { term_screen->geo.first_entry_x
190                         + term_screen->geo.entry_width + 1
191                         + term_screen->geo.border,
192                         term_screen->geo.first_entry_y });
193
194   if (flag)
195     grub_putcode (GRUB_UNICODE_UPARROW, term_screen->term);
196   else
197     grub_putcode (' ', term_screen->term);
198 }
199
200 /* Print a down arrow.  */
201 static void
202 print_down (int flag, struct per_term_screen *term_screen)
203 {
204   grub_term_gotoxy (term_screen->term,
205                     (struct grub_term_coordinate) { term_screen->geo.first_entry_x
206                         + term_screen->geo.entry_width + 1
207                         + term_screen->geo.border,
208                         term_screen->geo.first_entry_y
209                         + term_screen->geo.num_entries - 1 });
210
211   if (flag)
212     grub_putcode (GRUB_UNICODE_DOWNARROW, term_screen->term);
213   else
214     grub_putcode (' ', term_screen->term);
215 }
216
217 /* Draw the lines of the screen SCREEN.  */
218 static void
219 update_screen (struct screen *screen, struct per_term_screen *term_screen,
220                int region_start, int region_column __attribute__ ((unused)),
221                int up, int down, enum update_mode mode)
222 {
223   int up_flag = 0;
224   int down_flag = 0;
225   int y;
226   int i;
227   struct line *linep;
228
229   y = term_screen->y_line_start;
230   linep = screen->lines;
231
232   for (i = 0; i < screen->line; i++, linep++)
233     y += get_logical_num_lines (linep, term_screen);
234   linep = screen->lines + screen->line;
235   grub_size_t t = grub_getstringwidth (linep->buf, linep->buf + screen->column,
236                                        term_screen->term);
237   y += t / (unsigned) term_screen->geo.entry_width;
238   if (t % (unsigned) term_screen->geo.entry_width == 0
239       && t != 0 &&  screen->column == linep->len)
240     y--;
241   /* Check if scrolling is necessary.  */
242   if (y < 0 || y >= term_screen->geo.num_entries)
243     {
244       int delta;
245       if (y < 0)
246         delta = -y;
247       else
248         delta = term_screen->geo.num_entries - 1 - y;
249       term_screen->y_line_start += delta;
250
251       region_start = 0;
252       up = 1;
253       down = 1;
254       mode = ALL_LINES;
255     }
256
257   grub_term_setcursor (term_screen->term, 0);
258
259   if (mode != NO_LINE)
260     {
261       /* Draw lines. This code is tricky, because this must calculate logical
262          positions.  */
263       y = term_screen->y_line_start;
264       i = 0;
265       linep = screen->lines;
266       while (1)
267         {
268           int add;
269           add = get_logical_num_lines (linep, term_screen);
270           if (y + add > 0)
271             break;
272           i++;
273           linep++;
274           y += add;
275         }
276
277       if (y < 0 || i > 0)
278         up_flag = 1;
279
280       do
281         {
282           struct grub_term_pos **pos;
283
284           if (linep >= screen->lines + screen->num_lines)
285             break;
286
287           pos = linep->pos + (term_screen - screen->terms);
288
289           if (!*pos)
290             *pos = grub_zalloc ((linep->len + 1) * sizeof (**pos));
291
292           if (i == region_start || linep == screen->lines + screen->line
293               || (i > region_start && mode == ALL_LINES))
294             {
295               grub_term_gotoxy (term_screen->term,
296                                 (struct grub_term_coordinate) { term_screen->geo.first_entry_x,
297                                     (y < 0 ? 0 : y)
298                                     + term_screen->geo.first_entry_y });
299
300               grub_print_ucs4_menu (linep->buf,
301                                     linep->buf + linep->len,
302                                     term_screen->geo.first_entry_x,
303                                     term_screen->geo.right_margin,
304                                     term_screen->term,
305                                     (y < 0) ? -y : 0,
306                                     term_screen->geo.num_entries
307                                     - ((y > 0) ? y : 0), '\\',
308                                     *pos);
309             }
310           y += get_logical_num_lines (linep, term_screen);
311           if (y >= term_screen->geo.num_entries)
312             {
313               if (i + 1 < screen->num_lines)
314                 down_flag = 1;
315             }
316
317           linep++;
318           i++;
319
320           if (mode == ALL_LINES && i == screen->num_lines)
321             for (; y < term_screen->geo.num_entries; y++)
322               print_empty_line (y, term_screen);
323         }
324       while (y < term_screen->geo.num_entries);
325
326       /* Draw up and down arrows.  */
327       
328       if (term_screen->geo.num_entries == 1)
329         {
330           if (up || down)
331             print_updown (up_flag, down_flag, term_screen);
332         }
333       else
334         {
335           if (up)
336             print_up (up_flag, term_screen);
337           if (down)
338             print_down (down_flag, term_screen);
339         }
340     }
341
342   /* Place the cursor.  */
343   if (screen->lines[screen->line].pos[term_screen - screen->terms])
344     {
345       const struct grub_term_pos *cpos;
346       for (cpos = &(screen->lines[screen->line].pos[term_screen - screen->terms])[screen->column];
347            cpos >= &(screen->lines[screen->line].pos[term_screen - screen->terms])[0];
348            cpos--)
349         if (cpos->valid)
350           break;
351       y = term_screen->y_line_start;
352       for (i = 0; i < screen->line; i++)
353         y += get_logical_num_lines (screen->lines + i, term_screen);
354       if (cpos >= &(screen->lines[screen->line].pos[term_screen - screen->terms])[0])
355         grub_term_gotoxy (term_screen->term, 
356                           (struct grub_term_coordinate) { cpos->x + term_screen->geo.first_entry_x,
357                               cpos->y + y
358                               + term_screen->geo.first_entry_y });
359       else
360         grub_term_gotoxy (term_screen->term, 
361                           (struct grub_term_coordinate) { term_screen->geo.first_entry_x,
362                               y + term_screen->geo.first_entry_y });
363
364     }
365
366   grub_term_setcursor (term_screen->term, 1);
367
368   grub_term_refresh (term_screen->term);
369 }
370
371 static void
372 update_screen_all (struct screen *screen,
373                    int region_start, int region_column,
374                    int up, int down, enum update_mode mode)
375 {
376   unsigned i;
377   for (i = 0; i < screen->nterms; i++)
378     update_screen (screen, &screen->terms[i], region_start, region_column,
379                    up, down, mode);
380 }
381
382 static int
383 insert_string (struct screen *screen, const char *s, int update)
384 {
385   int region_start = screen->num_lines;
386   int region_column = 0;
387   unsigned i;
388
389   for (i = 0; i < screen->nterms; i++)
390     {
391       screen->terms[i].down = 0;
392       screen->terms[i].mode = NO_LINE;
393     }
394
395   while (*s)
396     {
397       if (*s == '\n')
398         {
399           /* LF is special because it creates a new line.  */
400           struct line *current_linep;
401           struct line *next_linep;
402           int size;
403
404           /* Make a new line.  */
405           screen->num_lines++;
406           screen->lines = grub_realloc (screen->lines,
407                                         screen->num_lines
408                                         * sizeof (screen->lines[0]));
409           if (! screen->lines)
410             return 0;
411
412           /* Shift down if not appending after the last line. */
413           if (screen->line < screen->num_lines - 2)
414             grub_memmove (screen->lines + screen->line + 2,
415                           screen->lines + screen->line + 1,
416                           ((screen->num_lines - screen->line - 2)
417                            * sizeof (struct line)));
418
419           if (! init_line (screen, screen->lines + screen->line + 1))
420             return 0;
421
422           /* Fold the line.  */
423           current_linep = screen->lines + screen->line;
424           next_linep = current_linep + 1;
425           size = current_linep->len - screen->column;
426
427           if (! ensure_space (next_linep, size))
428             return 0;
429
430           grub_memmove (next_linep->buf,
431                         current_linep->buf + screen->column,
432                         size * sizeof (next_linep->buf[0]));
433           current_linep->len = screen->column;
434           for (i = 0; i < screen->nterms; i++)
435             {
436               grub_free (current_linep->pos[i]);
437               current_linep->pos[i] = 0;
438             }
439           next_linep->len = size;
440
441           /* Update a dirty region.  */
442           if (region_start > screen->line)
443             {
444               region_start = screen->line;
445               region_column = screen->column;
446             }
447
448           for (i = 0; i < screen->nterms; i++)
449             {
450               screen->terms[i].mode = ALL_LINES;
451               screen->terms[i].down = 1; /* XXX not optimal.  */
452             }
453
454           /* Move the cursor.  */
455           screen->column = screen->real_column = 0;
456           screen->line++;
457           s++;
458         }
459       else
460         {
461           /* All but LF.  */
462           const char *p;
463           struct line *current_linep;
464           int size;
465           grub_uint32_t *unicode_msg;
466
467           /* Find a string delimited by LF.  */
468           p = grub_strchr (s, '\n');
469           if (! p)
470             p = s + grub_strlen (s);
471
472           /* Insert the string.  */
473           current_linep = screen->lines + screen->line;
474           unicode_msg = grub_malloc ((p - s) * sizeof (grub_uint32_t));
475
476           if (!unicode_msg)
477             return 0;
478
479           size = grub_utf8_to_ucs4 (unicode_msg, (p - s),
480                                     (grub_uint8_t *) s, (p - s), 0);
481
482           if (! ensure_space (current_linep, size))
483             {
484               grub_free (unicode_msg);
485               return 0;
486             }
487
488           grub_memmove (current_linep->buf + screen->column + size,
489                         current_linep->buf + screen->column,
490                         (current_linep->len - screen->column)
491                         * sizeof (current_linep->buf[0]));
492           grub_memmove (current_linep->buf + screen->column,
493                         unicode_msg,
494                         size * sizeof (current_linep->buf[0]));
495           grub_free (unicode_msg);
496
497           for (i = 0; i < screen->nterms; i++)
498             {
499               grub_free (current_linep->pos[i]);
500               current_linep->pos[i] = 0;
501             }
502
503           for (i = 0; i < screen->nterms; i++)
504             screen->terms[i].orig_num = get_logical_num_lines (current_linep,
505                                                  &screen->terms[i]);
506           current_linep->len += size;
507
508           /* Update the dirty region.  */
509           if (region_start > screen->line)
510             {
511               region_start = screen->line;
512               region_column = screen->column;
513             }
514
515           for (i = 0; i < screen->nterms; i++)
516             {
517               int new_num = get_logical_num_lines (current_linep,
518                                                    &screen->terms[i]);
519               if (screen->terms[i].orig_num != new_num)
520                 {
521                   screen->terms[i].mode = ALL_LINES;
522                   screen->terms[i].down = 1; /* XXX not optimal.  */
523                 }
524               else if (screen->terms[i].mode != ALL_LINES)
525                 screen->terms[i].mode = SINGLE_LINE;
526             }
527
528           /* Move the cursor.  */
529           advance_to (screen, screen->column + size);
530
531           screen->real_column = screen->column;
532           s = p;
533         }
534     }
535
536   if (update)
537     for (i = 0; i < screen->nterms; i++)
538       update_screen (screen, &screen->terms[i],
539                      region_start, region_column, 0, screen->terms[i].down,
540                      screen->terms[i].mode);
541
542   return 1;
543 }
544
545 /* Release the resource allocated for SCREEN.  */
546 static void
547 destroy_screen (struct screen *screen)
548 {
549   int i;
550
551   if (screen->lines)
552     for (i = 0; i < screen->num_lines; i++)
553       {
554         struct line *linep = screen->lines + i;
555
556         if (linep)
557           {
558             unsigned j;
559             if (linep->pos)
560               for (j = 0; j < screen->nterms; j++)
561                 grub_free (linep->pos[j]);
562
563             grub_free (linep->buf);
564             grub_free (linep->pos);
565           }
566       }
567
568   grub_free (screen->killed_text);
569   grub_free (screen->lines);
570   grub_free (screen->terms);
571   grub_free (screen);
572 }
573
574 /* Make a new screen.  */
575 static struct screen *
576 make_screen (grub_menu_entry_t entry)
577 {
578   struct screen *screen;
579   unsigned i;
580
581   /* Initialize the screen.  */
582   screen = grub_zalloc (sizeof (*screen));
583   if (! screen)
584     return 0;
585
586   screen->submenu = entry->submenu;
587
588   screen->num_lines = 1;
589   screen->lines = grub_malloc (sizeof (struct line));
590   if (! screen->lines)
591     goto fail;
592
593   /* Initialize the first line which must be always present.  */
594   if (! init_line (screen, screen->lines))
595     goto fail;
596
597   insert_string (screen, (char *) entry->sourcecode, 0);
598
599   /* Reset the cursor position.  */
600   screen->column = 0;
601   screen->real_column = 0;
602   screen->line = 0;
603   for (i = 0; i < screen->nterms; i++)
604     {
605       screen->terms[i].y_line_start = 0;
606     }
607
608   return screen;
609
610  fail:
611   destroy_screen (screen);
612   return 0;
613 }
614
615 static int
616 forward_char (struct screen *screen, int update)
617 {
618   struct line *linep;
619
620   linep = screen->lines + screen->line;
621   if (screen->column < linep->len)
622     {
623       screen->column = grub_unicode_get_comb_end (screen->lines[screen->line].buf
624                                                   + screen->lines[screen->line].len,
625                                                   screen->lines[screen->line].buf
626                                                   + screen->column + 1)
627         - screen->lines[screen->line].buf;
628     }
629   else if (screen->num_lines > screen->line + 1)
630     {
631       screen->column = 0;
632       screen->line++;
633     }
634
635   screen->real_column = screen->column;
636
637   if (update)
638     update_screen_all (screen, screen->num_lines, 0, 0, 0, NO_LINE);
639   return 1;
640 }
641
642 static int
643 backward_char (struct screen *screen, int update)
644 {
645   if (screen->column > 0)
646     {
647       struct grub_unicode_glyph glyph;
648       struct line *linep;
649
650       linep = screen->lines + screen->line;
651
652       screen->column--;
653       screen->column = grub_unicode_get_comb_start (linep->buf, 
654                                                     linep->buf + screen->column)
655         - linep->buf;
656
657       grub_unicode_aglomerate_comb (screen->lines[screen->line].buf + screen->column,
658                                     screen->lines[screen->line].len - screen->column,
659                                     &glyph);
660       screen->column = grub_unicode_get_comb_start (linep->buf, 
661                                                     linep->buf + screen->column)
662         - linep->buf;
663
664       grub_unicode_destroy_glyph (&glyph);
665     }
666   else if (screen->line > 0)
667     {
668       screen->line--;
669       screen->column = screen->lines[screen->line].len;
670     }
671
672   screen->real_column = screen->column;
673
674   if (update)
675     update_screen_all (screen, screen->num_lines, 0, 0, 0, NO_LINE);
676
677   return 1;
678 }
679
680 static int
681 previous_line (struct screen *screen, int update)
682 {
683   if (screen->line > 0)
684     {
685       struct line *linep;
686       int col;
687
688       screen->line--;
689
690       linep = screen->lines + screen->line;
691       if (linep->len < screen->real_column)
692         col = linep->len;
693       else
694         col = screen->real_column;
695
696       screen->column = 0;
697       advance_to (screen, col);
698     }
699   else
700     {
701       screen->column = 0;
702     }
703
704   if (update)
705     update_screen_all (screen, screen->num_lines, 0, 0, 0, NO_LINE);
706
707   return 1;
708 }
709
710 static int
711 next_line (struct screen *screen, int update)
712 {
713   if (screen->line < screen->num_lines - 1)
714     {
715       struct line *linep;
716       int c;
717
718       /* How many physical lines from the current position
719          to the last physical line?  */
720       linep = screen->lines + screen->line;
721
722       screen->line++;
723       if ((linep + 1)->len < screen->real_column)
724         c = (linep + 1)->len;
725       else
726         c = screen->real_column;
727       screen->column = 0;
728
729       advance_to (screen, c);
730     }
731   else
732     advance_to (screen, screen->lines[screen->line].len);
733
734   if (update)
735     update_screen_all (screen, screen->num_lines, 0, 0, 0, NO_LINE);
736
737   return 1;
738 }
739
740 static int
741 beginning_of_line (struct screen *screen, int update)
742 {
743   screen->column = screen->real_column = 0;
744
745   if (update)
746     update_screen_all (screen, screen->num_lines, 0, 0, 0, NO_LINE);
747
748   return 1;
749 }
750
751 static int
752 end_of_line (struct screen *screen, int update)
753 {
754   advance_to (screen, screen->lines[screen->line].len);
755
756   if (update)
757     update_screen_all (screen, screen->num_lines, 0, 0, 0, NO_LINE);
758
759   return 1;
760 }
761
762 static int
763 delete_char (struct screen *screen, int update)
764 {
765   struct line *linep;
766   int start = screen->num_lines;
767   int column = 0;
768
769   linep = screen->lines + screen->line;
770   if (linep->len > screen->column)
771     {
772       unsigned i;
773
774       for (i = 0; i < screen->nterms; i++)
775         screen->terms[i].orig_num = get_logical_num_lines (linep, &screen->terms[i]);
776
777       grub_memmove (linep->buf + screen->column,
778                     linep->buf + screen->column + 1,
779                     (linep->len - screen->column - 1)
780                     * sizeof (linep->buf[0]));
781       linep->len--;
782
783       for (i = 0; i < screen->nterms; i++)
784         {
785           grub_free (linep->pos[i]);
786           linep->pos[i] = 0;
787         }
788       start = screen->line;
789       column = screen->column;
790
791       screen->real_column = screen->column;
792
793       if (update)
794         {
795           for (i = 0; i < screen->nterms; i++)
796             {
797               int new_num;
798               new_num = get_logical_num_lines (linep, &screen->terms[i]);
799               if (screen->terms[i].orig_num != new_num)
800                 update_screen (screen, &screen->terms[i],
801                                start, column, 0, 0, ALL_LINES);
802               else
803                 update_screen (screen, &screen->terms[i],
804                                start, column, 0, 0, SINGLE_LINE);
805             }
806         }
807     }
808   else if (screen->num_lines > screen->line + 1)
809     {
810       struct line *next_linep;
811       unsigned i;
812
813       next_linep = linep + 1;
814       if (! ensure_space (linep, next_linep->len))
815         return 0;
816
817       grub_memmove (linep->buf + linep->len, next_linep->buf,
818                     next_linep->len * sizeof (linep->buf[0]));
819       linep->len += next_linep->len;
820       for (i = 0; i < screen->nterms; i++)
821         {
822           grub_free (linep->pos[i]);
823           linep->pos[i] = 0;
824         }
825
826       grub_free (next_linep->buf);
827       grub_memmove (next_linep,
828                     next_linep + 1,
829                     (screen->num_lines - screen->line - 2)
830                     * sizeof (struct line));
831       screen->num_lines--;
832
833       start = screen->line;
834       column = screen->column;
835
836       screen->real_column = screen->column;
837       if (update)
838         update_screen_all (screen, start, column, 0, 1, ALL_LINES);
839     }
840
841   return 1;
842 }
843
844 static int
845 backward_delete_char (struct screen *screen, int update)
846 {
847   int saved_column;
848   int saved_line;
849
850   saved_column = screen->column;
851   saved_line = screen->line;
852
853   if (! backward_char (screen, 0))
854     return 0;
855
856   if (saved_column != screen->column || saved_line != screen->line)
857     if (! delete_char (screen, update))
858       return 0;
859
860   return 1;
861 }
862
863 static int
864 kill_line (struct screen *screen, int continuous, int update)
865 {
866   struct line *linep;
867   char *p;
868   int size;
869   int offset;
870
871   p = screen->killed_text;
872   if (! continuous && p)
873     p[0] = '\0';
874
875   linep = screen->lines + screen->line;
876   size = linep->len - screen->column;
877
878   if (p)
879     offset = grub_strlen (p);
880   else
881     offset = 0;
882
883   if (size > 0)
884     {
885       unsigned i;
886
887       p = grub_realloc (p, offset + size + 1);
888       if (! p)
889         return 0;
890
891       grub_memmove (p + offset, linep->buf + screen->column, size);
892       p[offset + size] = '\0';
893
894       screen->killed_text = p;
895
896       for (i = 0; i < screen->nterms; i++)
897         screen->terms[i].orig_num = get_logical_num_lines (linep, &screen->terms[i]);
898       linep->len = screen->column;
899
900       if (update)
901         {
902           for (i = 0; i < screen->nterms; i++)
903             {
904               int new_num;
905               new_num = get_logical_num_lines (linep, &screen->terms[i]);
906               if (screen->terms[i].orig_num != new_num)
907                 update_screen (screen, &screen->terms[i],
908                                screen->line, screen->column, 0, 1, ALL_LINES);
909               else
910                 update_screen (screen, &screen->terms[i],
911                                screen->line, screen->column, 0, 0, SINGLE_LINE);
912             }
913         }
914     }
915   else if (screen->line + 1 < screen->num_lines)
916     {
917       p = grub_realloc (p, offset + 1 + 1);
918       if (! p)
919         return 0;
920
921       p[offset] = '\n';
922       p[offset + 1] = '\0';
923
924       screen->killed_text = p;
925
926       return delete_char (screen, update);
927     }
928
929   return 1;
930 }
931
932 static int
933 yank (struct screen *screen, int update)
934 {
935   if (screen->killed_text)
936     return insert_string (screen, screen->killed_text, update);
937
938   return 1;
939 }
940
941 static int
942 open_line (struct screen *screen, int update)
943 {
944   if (! insert_string (screen, "\n", 0))
945     return 0;
946
947   if (! backward_char (screen, 0))
948     return 0;
949
950   if (update)
951     update_screen_all (screen, screen->line, screen->column, 0, 1, ALL_LINES);
952
953   return 1;
954 }
955
956 /* A completion hook to print items.  */
957 static void
958 store_completion (const char *item, grub_completion_type_t type,
959                   int count __attribute__ ((unused)))
960 {
961   char *p;
962
963   completion_type = type;
964
965   /* Make sure that the completion buffer has enough room.  */
966   if (completion_buffer.max_len < (completion_buffer.len
967                                    + (int) grub_strlen (item) + 1 + 1))
968     {
969       grub_size_t new_len;
970
971       new_len = completion_buffer.len + grub_strlen (item) + 80;
972       p = grub_realloc (completion_buffer.buf, new_len);
973       if (! p)
974         {
975           /* Possibly not fatal.  */
976           grub_errno = GRUB_ERR_NONE;
977           return;
978         }
979       p[completion_buffer.len] = 0;
980       completion_buffer.buf = p;
981       completion_buffer.max_len = new_len;
982     }
983
984   p = completion_buffer.buf + completion_buffer.len;
985   if (completion_buffer.len != 0)
986     {
987       *p++ = ' ';
988       completion_buffer.len++;
989     }
990   grub_strcpy (p, item);
991   completion_buffer.len += grub_strlen (item);
992 }
993
994 static int
995 complete (struct screen *screen, int continuous, int update)
996 {
997   struct line *linep;
998   int restore;
999   char *insert;
1000   static int count = -1;
1001   unsigned i;
1002   grub_uint32_t *ucs4;
1003   grub_size_t buflen;
1004   grub_ssize_t ucs4len;
1005   char *u8;
1006
1007   if (continuous)
1008     count++;
1009   else
1010     count = 0;
1011
1012   completion_buffer.buf = 0;
1013   completion_buffer.len = 0;
1014   completion_buffer.max_len = 0;
1015
1016   linep = screen->lines + screen->line;
1017   u8 = grub_ucs4_to_utf8_alloc (linep->buf, screen->column);
1018   if (!u8)
1019     return 1;
1020
1021   insert = grub_normal_do_completion (u8, &restore, store_completion);
1022   
1023   if (completion_buffer.buf)
1024     {
1025       buflen = grub_strlen (completion_buffer.buf);
1026       ucs4 = grub_malloc (sizeof (grub_uint32_t) * (buflen + 1));
1027       
1028       if (!ucs4)
1029         {
1030           grub_print_error ();
1031           grub_errno = GRUB_ERR_NONE;
1032           return 1;
1033         }
1034
1035       ucs4len = grub_utf8_to_ucs4 (ucs4, buflen,
1036                                    (grub_uint8_t *) completion_buffer.buf,
1037                                    buflen, 0);
1038       ucs4[ucs4len] = 0;
1039
1040       if (restore)
1041         for (i = 0; i < screen->nterms; i++)
1042           {
1043             unsigned width = grub_term_width (screen->terms[i].term);
1044             if (width > 2)
1045               width -= 2;
1046             if (width > 15)
1047               width -= 6;
1048             unsigned num_sections = ((completion_buffer.len
1049                                       + width - 1)
1050                                      / width);
1051             grub_uint32_t *endp;
1052             struct grub_term_coordinate pos;
1053             grub_uint32_t *p = ucs4;
1054
1055             pos = grub_term_getxy (screen->terms[i].term);
1056
1057             screen->completion_shown = 1;
1058
1059             grub_term_gotoxy (screen->terms[i].term,
1060                               (struct grub_term_coordinate) { 0,
1061                                   screen->terms[i].geo.timeout_y });
1062             if (screen->terms[i].geo.timeout_lines >= 2)
1063               {
1064                 grub_puts_terminal ("   ", screen->terms[i].term);
1065                 switch (completion_type)
1066                   {
1067                   case GRUB_COMPLETION_TYPE_COMMAND:
1068                     grub_puts_terminal (_("Possible commands are:"),
1069                                         screen->terms[i].term);
1070                     break;
1071                   case GRUB_COMPLETION_TYPE_DEVICE:
1072                     grub_puts_terminal (_("Possible devices are:"),
1073                                         screen->terms[i].term);
1074                     break;
1075                   case GRUB_COMPLETION_TYPE_FILE:
1076                     grub_puts_terminal (_("Possible files are:"),
1077                                         screen->terms[i].term);
1078                     break;
1079                   case GRUB_COMPLETION_TYPE_PARTITION:
1080                     grub_puts_terminal (_("Possible partitions are:"),
1081                                         screen->terms[i].term);
1082                     break;
1083                   case GRUB_COMPLETION_TYPE_ARGUMENT:
1084                     grub_puts_terminal (_("Possible arguments are:"),
1085                                         screen->terms[i].term);
1086                     break;
1087                   default:
1088                     grub_puts_terminal (_("Possible things are:"),
1089                                         screen->terms[i].term);
1090                     break;
1091                   }
1092
1093                 grub_puts_terminal ("\n    ", screen->terms[i].term);
1094               }
1095
1096             p += ((unsigned) count % num_sections) * width;
1097             endp = p + width;
1098
1099             if (p != ucs4)
1100               grub_putcode (GRUB_UNICODE_LEFTARROW, screen->terms[i].term);
1101             else
1102               grub_putcode (' ', screen->terms[i].term);
1103
1104             grub_print_ucs4 (p, ucs4 + ucs4len < endp ? ucs4 + ucs4len : endp,
1105                              0, 0, screen->terms[i].term);
1106
1107             if (ucs4 + ucs4len > endp)
1108               grub_putcode (GRUB_UNICODE_RIGHTARROW, screen->terms[i].term);
1109             grub_term_gotoxy (screen->terms[i].term, pos);
1110           }
1111     }
1112
1113   if (insert)
1114     {
1115       insert_string (screen, insert, update);
1116       count = -1;
1117       grub_free (insert);
1118     }
1119   else if (update)
1120     grub_refresh ();
1121
1122   grub_free (completion_buffer.buf);
1123   return 1;
1124 }
1125
1126 /* Clear displayed completions.  */
1127 static void
1128 clear_completions (struct per_term_screen *term_screen)
1129 {
1130   struct grub_term_coordinate pos;
1131   unsigned j;
1132   int i;
1133
1134   pos = grub_term_getxy (term_screen->term);
1135   grub_term_gotoxy (term_screen->term,
1136                     (struct grub_term_coordinate) { 0,
1137                         term_screen->geo.timeout_y });
1138
1139   for (i = 0; i < term_screen->geo.timeout_lines; i++)
1140     {
1141       for (j = 0; j < grub_term_width (term_screen->term) - 1; j++)
1142         grub_putcode (' ', term_screen->term);
1143       if (i + 1 < term_screen->geo.timeout_lines)
1144         grub_putcode ('\n', term_screen->term);
1145     }
1146
1147   grub_term_gotoxy (term_screen->term, pos);
1148   grub_term_refresh (term_screen->term);
1149 }
1150
1151 static void
1152 clear_completions_all (struct screen *screen)
1153 {
1154   unsigned i;
1155
1156   for (i = 0; i < screen->nterms; i++)
1157     clear_completions (&screen->terms[i]);
1158 }
1159
1160 /* Execute the command list in the screen SCREEN.  */
1161 static int
1162 run (struct screen *screen)
1163 {
1164   char *script;
1165   int errs_before;
1166   grub_menu_t menu = NULL;
1167   char *dummy[1] = { NULL };
1168
1169   grub_cls ();
1170   grub_printf ("  ");
1171   grub_printf_ (N_("Booting a command list"));
1172   grub_printf ("\n\n");
1173
1174   errs_before = grub_err_printed_errors;
1175
1176   if (screen->submenu)
1177     {
1178       grub_env_context_open ();
1179       menu = grub_zalloc (sizeof (*menu));
1180       if (! menu)
1181         return 0;
1182       grub_env_set_menu (menu);
1183     }
1184
1185   /* Execute the script, line for line.  */
1186   {
1187     int i;
1188     grub_size_t size = 0, tot_size = 0;
1189
1190     for (i = 0; i < screen->num_lines; i++)
1191       tot_size += grub_get_num_of_utf8_bytes (screen->lines[i].buf,
1192                                               screen->lines[i].len) + 1;
1193
1194     script = grub_malloc (tot_size + 1);
1195     if (! script)
1196       return 0;
1197
1198     for (i = 0; i < screen->num_lines; i++)
1199       {
1200         size += grub_ucs4_to_utf8 (screen->lines[i].buf, screen->lines[i].len,
1201                                    (grub_uint8_t *) script + size,
1202                                    tot_size - size);
1203         script[size++] = '\n';
1204       }
1205     script[size] = '\0';
1206   }
1207   grub_script_execute_new_scope (script, 0, dummy);
1208   grub_free (script);
1209
1210   if (errs_before != grub_err_printed_errors)
1211     grub_wait_after_message ();
1212
1213   if (grub_errno == GRUB_ERR_NONE && grub_loader_is_loaded ())
1214     /* Implicit execution of boot, only if something is loaded.  */
1215     grub_command_execute ("boot", 0, 0);
1216
1217   if (screen->submenu)
1218     {
1219       if (menu && menu->size)
1220         {
1221           grub_show_menu (menu, 1, 0);
1222           grub_normal_free_menu (menu);
1223         }
1224       grub_env_context_close ();
1225     }
1226
1227   if (grub_errno != GRUB_ERR_NONE)
1228     {
1229       grub_print_error ();
1230       grub_errno = GRUB_ERR_NONE;
1231       grub_wait_after_message ();
1232     }
1233
1234   return 1;
1235 }
1236
1237 /* Edit a menu entry with an Emacs-like interface.  */
1238 void
1239 grub_menu_entry_run (grub_menu_entry_t entry)
1240 {
1241   struct screen *screen;
1242   int prev_c;
1243   grub_err_t err = GRUB_ERR_NONE;
1244   unsigned i;
1245   grub_term_output_t term;
1246
1247   err = grub_auth_check_authentication (NULL);
1248
1249   if (err)
1250     {
1251       grub_print_error ();
1252       grub_errno = GRUB_ERR_NONE;
1253       return;
1254     }
1255
1256   screen = make_screen (entry);
1257   if (! screen)
1258     return;
1259
1260   screen->terms = NULL;
1261
1262  refresh:
1263   grub_free (screen->terms);
1264   screen->nterms = 0;
1265   FOR_ACTIVE_TERM_OUTPUTS(term)
1266     screen->nterms++;
1267
1268   for (i = 0; i < (unsigned) screen->num_lines; i++)
1269     {
1270       grub_free (screen->lines[i].pos);
1271       screen->lines[i].pos = grub_zalloc (screen->nterms * sizeof (screen->lines[i].pos[0]));
1272       if (! screen->lines[i].pos)
1273         {
1274           grub_print_error ();
1275           destroy_screen (screen);
1276           grub_errno = GRUB_ERR_NONE;
1277           return;
1278         }
1279     }
1280
1281   screen->terms = grub_zalloc (screen->nterms * sizeof (screen->terms[0]));
1282   if (!screen->terms)
1283     {
1284       grub_print_error ();
1285       destroy_screen (screen);
1286       grub_errno = GRUB_ERR_NONE;
1287       return;
1288     }
1289   i = 0;
1290   FOR_ACTIVE_TERM_OUTPUTS(term)
1291   {
1292     screen->terms[i].term = term;
1293     screen->terms[i].y_line_start = 0;
1294     i++;
1295   }
1296   /* Draw the screen.  */
1297   for (i = 0; i < screen->nterms; i++)
1298     grub_menu_init_page (0, 1, &screen->terms[i].geo,
1299                          screen->terms[i].term);
1300   update_screen_all (screen, 0, 0, 1, 1, ALL_LINES);
1301   for (i = 0; i < screen->nterms; i++)
1302     grub_term_setcursor (screen->terms[i].term, 1);
1303   prev_c = '\0';
1304
1305   while (1)
1306     {
1307       int c = grub_getkey ();
1308
1309       if (screen->completion_shown)
1310         {
1311           clear_completions_all (screen);
1312           screen->completion_shown = 0;
1313         }
1314
1315       if (grub_normal_exit_level)
1316         {
1317           destroy_screen (screen);
1318           return;
1319         }
1320
1321       switch (c)
1322         {
1323         case GRUB_TERM_KEY_UP:
1324         case GRUB_TERM_CTRL | 'p':
1325           if (! previous_line (screen, 1))
1326             goto fail;
1327           break;
1328
1329         case GRUB_TERM_CTRL | 'n':
1330         case GRUB_TERM_KEY_DOWN:
1331           if (! next_line (screen, 1))
1332             goto fail;
1333           break;
1334
1335         case GRUB_TERM_CTRL | 'f':
1336         case GRUB_TERM_KEY_RIGHT:
1337           if (! forward_char (screen, 1))
1338             goto fail;
1339           break;
1340
1341         case GRUB_TERM_CTRL | 'b':
1342         case GRUB_TERM_KEY_LEFT:
1343           if (! backward_char (screen, 1))
1344             goto fail;
1345           break;
1346
1347         case GRUB_TERM_CTRL | 'a':
1348         case GRUB_TERM_KEY_HOME:
1349           if (! beginning_of_line (screen, 1))
1350             goto fail;
1351           break;
1352
1353         case GRUB_TERM_CTRL | 'e':
1354         case GRUB_TERM_KEY_END:
1355           if (! end_of_line (screen, 1))
1356             goto fail;
1357           break;
1358
1359         case GRUB_TERM_CTRL | 'i':
1360         case '\t':
1361           if (! complete (screen, prev_c == c, 1))
1362             goto fail;
1363           break;
1364
1365         case GRUB_TERM_CTRL | 'd':
1366         case GRUB_TERM_KEY_DC:
1367           if (! delete_char (screen, 1))
1368             goto fail;
1369           break;
1370
1371         case GRUB_TERM_CTRL | 'h':
1372         case '\b':
1373           if (! backward_delete_char (screen, 1))
1374             goto fail;
1375           break;
1376
1377         case GRUB_TERM_CTRL | 'k':
1378           if (! kill_line (screen, prev_c == c, 1))
1379             goto fail;
1380           break;
1381
1382         case GRUB_TERM_CTRL | 'u':
1383           /* FIXME: What behavior is good for this key?  */
1384           break;
1385
1386         case GRUB_TERM_CTRL | 'y':
1387           if (! yank (screen, 1))
1388             goto fail;
1389           break;
1390
1391         case GRUB_TERM_CTRL | 'l':
1392           /* FIXME: centering.  */
1393           goto refresh;
1394
1395         case GRUB_TERM_CTRL | 'o':
1396           if (! open_line (screen, 1))
1397             goto fail;
1398           break;
1399
1400         case '\n':
1401         case '\r':
1402           if (! insert_string (screen, "\n", 1))
1403             goto fail;
1404           break;
1405
1406         case '\e':
1407           destroy_screen (screen);
1408           return;
1409
1410         case GRUB_TERM_CTRL | 'c':
1411         case GRUB_TERM_KEY_F2:
1412           grub_cmdline_run (1, 0);
1413           goto refresh;
1414
1415         case GRUB_TERM_CTRL | 'x':
1416         case GRUB_TERM_KEY_F10:
1417           run (screen);
1418           goto refresh;
1419
1420         case GRUB_TERM_CTRL | 'r':
1421         case GRUB_TERM_CTRL | 's':
1422         case GRUB_TERM_CTRL | 't':
1423           /* FIXME */
1424           break;
1425
1426         default:
1427           if (grub_isprint (c))
1428             {
1429               char buf[2];
1430
1431               buf[0] = c;
1432               buf[1] = '\0';
1433               if (! insert_string (screen, buf, 1))
1434                 goto fail;
1435             }
1436           break;
1437         }
1438
1439       prev_c = c;
1440     }
1441
1442  fail:
1443   destroy_screen (screen);
1444
1445   grub_cls ();
1446   grub_print_error ();
1447   grub_errno = GRUB_ERR_NONE;
1448   grub_xputs ("\n");
1449   grub_printf_ (N_("Press any key to continue..."));
1450   (void) grub_getkey ();
1451 }