2 |
* PUPA -- Preliminary Universal Programming Architecture for GRUB |
* PUPA -- Preliminary Universal Programming Architecture for GRUB |
3 |
* Copyright (C) 1999,2000,2001,2002 Free Software Foundation, Inc. |
* Copyright (C) 1999,2000,2001,2002 Free Software Foundation, Inc. |
4 |
* Copyright (C) 2003 Yoshinori K. Okuji <okuji@enbug.org> |
* Copyright (C) 2003 Yoshinori K. Okuji <okuji@enbug.org> |
5 |
|
* Copyright (C) 2003 Marco Gerards <metgerards@student.han.nl> |
6 |
* |
* |
7 |
* This program is free software; you can redistribute it and/or modify |
* This program is free software; you can redistribute it and/or modify |
8 |
* it under the terms of the GNU General Public License as published by |
* it under the terms of the GNU General Public License as published by |
25 |
#include <pupa/err.h> |
#include <pupa/err.h> |
26 |
#include <pupa/types.h> |
#include <pupa/types.h> |
27 |
#include <pupa/mm.h> |
#include <pupa/mm.h> |
28 |
|
#include <pupa/machine/partition.h> |
29 |
|
#include <pupa/disk.h> |
30 |
|
#include <pupa/file.h> |
31 |
|
|
32 |
static char *kill_buf; |
static char *kill_buf; |
33 |
|
|
34 |
|
static int hist_size; |
35 |
|
static char **hist_lines = 0; |
36 |
|
static int hist_pos = 0; |
37 |
|
static int hist_end = 0; |
38 |
|
static int hist_used = 0; |
39 |
|
|
40 |
|
pupa_err_t |
41 |
|
pupa_set_history (int newsize) |
42 |
|
{ |
43 |
|
char **old_hist_lines = hist_lines; |
44 |
|
hist_lines = pupa_malloc (sizeof (char *) * newsize); |
45 |
|
|
46 |
|
/* Copy the old lines into the new buffer. */ |
47 |
|
if (old_hist_lines) |
48 |
|
{ |
49 |
|
/* Remove the lines that don't fit in the new buffer. */ |
50 |
|
if (newsize < hist_used) |
51 |
|
{ |
52 |
|
int i; |
53 |
|
int delsize = hist_used - newsize; |
54 |
|
hist_used = newsize; |
55 |
|
|
56 |
|
for (i = 0; i < delsize; i++) |
57 |
|
{ |
58 |
|
int pos = hist_end - i; |
59 |
|
if (pos > hist_size) |
60 |
|
pos -= hist_size; |
61 |
|
pupa_free (old_hist_lines[pos]); |
62 |
|
} |
63 |
|
|
64 |
|
hist_end -= delsize; |
65 |
|
if (hist_end < 0) |
66 |
|
hist_end = hist_size - hist_end; |
67 |
|
} |
68 |
|
|
69 |
|
if (hist_pos < hist_end) |
70 |
|
pupa_memmove (hist_lines, old_hist_lines + hist_pos, |
71 |
|
(hist_end - hist_pos) * sizeof (char *)); |
72 |
|
else |
73 |
|
{ |
74 |
|
/* Copy the first part. */ |
75 |
|
pupa_memmove (hist_lines, old_hist_lines, |
76 |
|
hist_pos * sizeof (char *)); |
77 |
|
|
78 |
|
|
79 |
|
/* Copy the last part. */ |
80 |
|
pupa_memmove (hist_lines + hist_pos, old_hist_lines + hist_pos, |
81 |
|
(hist_size - hist_pos) * sizeof (char *)); |
82 |
|
|
83 |
|
} |
84 |
|
} |
85 |
|
|
86 |
|
pupa_free (old_hist_lines); |
87 |
|
|
88 |
|
hist_size = newsize; |
89 |
|
hist_pos = 0; |
90 |
|
hist_end = hist_used; |
91 |
|
return 0; |
92 |
|
} |
93 |
|
|
94 |
|
/* Get the entry POS from the history where `0' is the newest |
95 |
|
entry. */ |
96 |
|
static char * |
97 |
|
pupa_history_get (int pos) |
98 |
|
{ |
99 |
|
pos = (hist_pos + pos) % hist_size; |
100 |
|
return hist_lines[pos]; |
101 |
|
} |
102 |
|
|
103 |
|
|
104 |
|
/* Insert a new history line S on the top of the history. */ |
105 |
|
static void |
106 |
|
pupa_history_add (char *s) |
107 |
|
{ |
108 |
|
/* Remove the oldest entry in the history to make room for a new |
109 |
|
entry. */ |
110 |
|
if (hist_used + 1 > hist_size) |
111 |
|
{ |
112 |
|
hist_end--; |
113 |
|
if (hist_end < 0) |
114 |
|
hist_end = hist_size + hist_end; |
115 |
|
|
116 |
|
pupa_free (hist_lines[hist_end]); |
117 |
|
} |
118 |
|
else |
119 |
|
hist_used++; |
120 |
|
|
121 |
|
/* Move to the next position. */ |
122 |
|
hist_pos--; |
123 |
|
if (hist_pos < 0) |
124 |
|
hist_pos = hist_size + hist_pos; |
125 |
|
|
126 |
|
/* Insert into history. */ |
127 |
|
hist_lines[hist_pos] = pupa_strdup (s); |
128 |
|
} |
129 |
|
|
130 |
|
/* Replace the history entry on position POS with the string S. */ |
131 |
|
static void |
132 |
|
pupa_history_replace (int pos, char *s) |
133 |
|
{ |
134 |
|
pos = (hist_pos + pos) % hist_size; |
135 |
|
pupa_free (hist_lines[pos]); |
136 |
|
hist_lines[pos] = pupa_strdup (s); |
137 |
|
} |
138 |
|
|
139 |
|
/* Try to complete the string in BUF, return the characters that |
140 |
|
should be added to the string. This command outputs the possible |
141 |
|
completions, in that case set RESTORE to 1 so the caller can |
142 |
|
restore the prompt. */ |
143 |
|
static char * |
144 |
|
pupa_tab_complete (char *buf, int *restore) |
145 |
|
{ |
146 |
|
char *pos = buf; |
147 |
|
char *path; |
148 |
|
|
149 |
|
char *found = 0; |
150 |
|
int begin; |
151 |
|
int end; |
152 |
|
int len; |
153 |
|
int numfound = 0; |
154 |
|
|
155 |
|
/* The disk that is used for pupa_partition_iterate. */ |
156 |
|
pupa_device_t partdev; |
157 |
|
|
158 |
|
/* String that is added when matched. */ |
159 |
|
char *matchstr; |
160 |
|
|
161 |
|
void print_simple_completion (char *comp) |
162 |
|
{ |
163 |
|
pupa_printf (" %s", comp); |
164 |
|
} |
165 |
|
|
166 |
|
void print_partition_completion (char *comp) |
167 |
|
{ |
168 |
|
pupa_fs_t fs = 0; |
169 |
|
pupa_device_t part; |
170 |
|
char devname[20]; |
171 |
|
|
172 |
|
pupa_sprintf (devname, "%s,%s", partdev->disk->name, comp); |
173 |
|
part = pupa_device_open (devname); |
174 |
|
if (!part) |
175 |
|
pupa_printf ("\n\tPartition num:%s, Filesystem cannot be accessed", |
176 |
|
comp); |
177 |
|
else |
178 |
|
{ |
179 |
|
char *label; |
180 |
|
|
181 |
|
fs = pupa_fs_probe (part); |
182 |
|
/* Ignore all errors. */ |
183 |
|
pupa_errno = 0; |
184 |
|
|
185 |
|
pupa_printf ("\n\tPartition num:%s, Filesystem type %s", |
186 |
|
comp, fs ? fs->name : "Unknown"); |
187 |
|
|
188 |
|
if (fs) |
189 |
|
{ |
190 |
|
(fs->label) (part, &label); |
191 |
|
if (pupa_errno == PUPA_ERR_NONE) |
192 |
|
{ |
193 |
|
if (label && pupa_strlen (label)) |
194 |
|
pupa_printf (", Label: %s", label); |
195 |
|
pupa_free (label); |
196 |
|
} |
197 |
|
pupa_errno = PUPA_ERR_NONE; |
198 |
|
} |
199 |
|
pupa_device_close (part); |
200 |
|
} |
201 |
|
} |
202 |
|
|
203 |
|
/* Add a string to the list of possible completions. COMP is the |
204 |
|
string that should be added. If this string completely matches |
205 |
|
add the string MATCH to the input after adding COMP. The string |
206 |
|
WHAT contains a discription of the kind of data that is added. |
207 |
|
Use PRINT_COMPLETION to show the completions if there are |
208 |
|
multiple matches. XXX: Because of a bug in gcc it is required to |
209 |
|
use __regparm__ in some cases. */ |
210 |
|
|
211 |
|
int NESTED_FUNC_ATTR |
212 |
|
add_completion (const char *comp, const char *match, const char *what, |
213 |
|
void (*print_completion) (char *)) |
214 |
|
{ |
215 |
|
/* Bug in strncmp then len ==0. */ |
216 |
|
if (!len || pupa_strncmp (pos, comp, len) == 0) |
217 |
|
{ |
218 |
|
numfound++; |
219 |
|
|
220 |
|
if (numfound == 1) |
221 |
|
{ |
222 |
|
begin = len; |
223 |
|
found = pupa_strdup (comp); |
224 |
|
end = pupa_strlen (found); |
225 |
|
matchstr = (char *) match; |
226 |
|
} |
227 |
|
/* Multiple matches found, print the first instead of completing. */ |
228 |
|
else if (numfound == 2) |
229 |
|
{ |
230 |
|
pupa_printf ("\nPossible %s are: ", what); |
231 |
|
print_completion (found); |
232 |
|
} |
233 |
|
|
234 |
|
if (numfound > 1) |
235 |
|
{ |
236 |
|
char *s1 = found; |
237 |
|
const char *s2 = comp; |
238 |
|
int cnt = 0; |
239 |
|
|
240 |
|
print_completion ((char *) comp); |
241 |
|
|
242 |
|
/* Find out how many characters match. */ |
243 |
|
while ((cnt < end) && *s1 && *s2 && (*s1 == *s2)) |
244 |
|
{ |
245 |
|
s1++; |
246 |
|
s2++; |
247 |
|
cnt++; |
248 |
|
} |
249 |
|
end = cnt; |
250 |
|
} |
251 |
|
} |
252 |
|
|
253 |
|
return 0; |
254 |
|
} |
255 |
|
|
256 |
|
int iterate_part (const pupa_partition_t p) |
257 |
|
{ |
258 |
|
add_completion (pupa_partition_get_name (p), ")", "partitions", |
259 |
|
print_partition_completion); |
260 |
|
return 0; |
261 |
|
} |
262 |
|
|
263 |
|
int iterate_dir (const char *filename, int dir) |
264 |
|
{ |
265 |
|
if (!dir) |
266 |
|
add_completion (filename, " ", "files", print_simple_completion); |
267 |
|
else |
268 |
|
{ |
269 |
|
char fname[pupa_strlen (filename) + 2]; |
270 |
|
pupa_strcpy (fname, filename); |
271 |
|
pupa_sprintf (fname, "%s/", filename); |
272 |
|
add_completion (fname, "", "files", print_simple_completion); |
273 |
|
} |
274 |
|
return 0; |
275 |
|
} |
276 |
|
|
277 |
|
int iterate_dev (const char *devname) |
278 |
|
{ |
279 |
|
pupa_device_t dev; |
280 |
|
|
281 |
|
/* Complete the partition part. */ |
282 |
|
dev = pupa_device_open (devname); |
283 |
|
|
284 |
|
if (dev) |
285 |
|
{ |
286 |
|
if (dev->disk && dev->disk->has_partitions) |
287 |
|
add_completion (devname, ",", "disks", print_simple_completion); |
288 |
|
else |
289 |
|
add_completion (devname, ")", "disks", print_simple_completion); |
290 |
|
} |
291 |
|
|
292 |
|
return 0; |
293 |
|
} |
294 |
|
|
295 |
|
int iterate_commands (pupa_command_t cmd) |
296 |
|
{ |
297 |
|
if (cmd->flags & PUPA_COMMAND_FLAG_CMDLINE) |
298 |
|
add_completion (cmd->name, " ", "commands", print_simple_completion); |
299 |
|
return 0; |
300 |
|
} |
301 |
|
|
302 |
|
/* Remove blank space on the beginning of the line. */ |
303 |
|
while (*pos == ' ') |
304 |
|
pos++; |
305 |
|
|
306 |
|
/* Check if the string is a command or path. */ |
307 |
|
path = pupa_strchr (pos, ' '); |
308 |
|
|
309 |
|
if (!path) |
310 |
|
{ |
311 |
|
/* Tab complete a command. */ |
312 |
|
len = pupa_strlen (pos); |
313 |
|
|
314 |
|
pupa_iterate_commands (iterate_commands); |
315 |
|
} |
316 |
|
else |
317 |
|
{ |
318 |
|
pos = path; |
319 |
|
|
320 |
|
/* Remove blank space on the beginning of the line. */ |
321 |
|
while (*pos == ' ') |
322 |
|
pos++; |
323 |
|
|
324 |
|
/* Check if this is a completion for a device name. */ |
325 |
|
if (*pos == '(' && !pupa_strchr (pos, ')')) |
326 |
|
{ |
327 |
|
/* Check if this is a device or partition. */ |
328 |
|
char *partition = pupa_strchr (++pos, ','); |
329 |
|
|
330 |
|
if (!partition) |
331 |
|
{ |
332 |
|
/* Complete the disk part. */ |
333 |
|
len = pupa_strlen (pos); |
334 |
|
pupa_disk_dev_iterate (iterate_dev); |
335 |
|
if (pupa_errno) |
336 |
|
goto fail; |
337 |
|
} |
338 |
|
else |
339 |
|
{ |
340 |
|
*partition = '\0'; |
341 |
|
|
342 |
|
/* Complete the partition part. */ |
343 |
|
partdev = pupa_device_open (pos); |
344 |
|
*partition = ','; |
345 |
|
pupa_errno = PUPA_ERR_NONE; |
346 |
|
|
347 |
|
if (partdev) |
348 |
|
{ |
349 |
|
if (partdev->disk && partdev->disk->has_partitions) |
350 |
|
{ |
351 |
|
pos = partition + 1; |
352 |
|
len = pupa_strlen (pos); |
353 |
|
|
354 |
|
pupa_partition_iterate (partdev->disk, iterate_part); |
355 |
|
if (pupa_errno) |
356 |
|
pupa_errno = 0; |
357 |
|
} |
358 |
|
|
359 |
|
pupa_device_close (partdev); |
360 |
|
} |
361 |
|
else |
362 |
|
goto fail; |
363 |
|
} |
364 |
|
} |
365 |
|
else |
366 |
|
{ |
367 |
|
char *device = pupa_file_get_device_name (pos); |
368 |
|
pupa_device_t dev; |
369 |
|
pupa_fs_t fs; |
370 |
|
|
371 |
|
dev = pupa_device_open (device); |
372 |
|
if (!dev) |
373 |
|
goto fail; |
374 |
|
|
375 |
|
fs = pupa_fs_probe (dev); |
376 |
|
if (pupa_errno) |
377 |
|
goto fail; |
378 |
|
|
379 |
|
pos = pupa_strrchr (pos, '/'); |
380 |
|
if (pos) |
381 |
|
{ |
382 |
|
char *dir; |
383 |
|
char *dirfile; |
384 |
|
pos++; |
385 |
|
len = pupa_strlen (pos); |
386 |
|
|
387 |
|
dir = pupa_strchr (path, '/'); |
388 |
|
if (!dir) |
389 |
|
{ |
390 |
|
*restore = 0; |
391 |
|
return 0; |
392 |
|
} |
393 |
|
|
394 |
|
dir = pupa_strdup (dir); |
395 |
|
|
396 |
|
/* Cut away the filename part. */ |
397 |
|
dirfile = pupa_strrchr (dir, '/'); |
398 |
|
dirfile[1] = '\0'; |
399 |
|
|
400 |
|
/* Tab complete a file. */ |
401 |
|
(fs->dir) (dev, dir, iterate_dir); |
402 |
|
if (dev) |
403 |
|
pupa_device_close (dev); |
404 |
|
|
405 |
|
pupa_free (device); |
406 |
|
pupa_free (dir); |
407 |
|
|
408 |
|
if (pupa_errno) |
409 |
|
goto fail; |
410 |
|
} |
411 |
|
else |
412 |
|
{ |
413 |
|
found = pupa_strdup ("/"); |
414 |
|
matchstr = ""; |
415 |
|
numfound = 1; |
416 |
|
begin = 0; |
417 |
|
end = 1; |
418 |
|
} |
419 |
|
} |
420 |
|
|
421 |
|
} |
422 |
|
|
423 |
|
/* If more than one match is found those matches will be printed and |
424 |
|
the prompt should be restored. */ |
425 |
|
if (numfound > 1) |
426 |
|
*restore = 1; |
427 |
|
else |
428 |
|
*restore = 0; |
429 |
|
|
430 |
|
/* Return the part that matches. */ |
431 |
|
if (end && found) |
432 |
|
{ |
433 |
|
char *insert; |
434 |
|
insert = pupa_malloc (end - begin + 1 + sizeof (matchstr)); |
435 |
|
pupa_strncpy (insert, found + begin, end - begin); |
436 |
|
insert[end - begin] = '\0'; |
437 |
|
if (numfound == 1) |
438 |
|
pupa_strcat (insert, matchstr); |
439 |
|
pupa_free (found); |
440 |
|
|
441 |
|
return insert; |
442 |
|
} |
443 |
|
|
444 |
|
fail: |
445 |
|
pupa_free (found); |
446 |
|
pupa_errno = PUPA_ERR_NONE; |
447 |
|
|
448 |
|
return 0; |
449 |
|
} |
450 |
|
|
451 |
void |
void |
452 |
pupa_cmdline_run (int nested) |
pupa_cmdline_run (int nested) |
453 |
{ |
{ |
503 |
pupa_size_t plen; |
pupa_size_t plen; |
504 |
char buf[max_len]; |
char buf[max_len]; |
505 |
int key; |
int key; |
506 |
|
int histpos = 0; |
507 |
auto void cl_insert (const char *str); |
auto void cl_insert (const char *str); |
508 |
auto void cl_delete (unsigned len); |
auto void cl_delete (unsigned len); |
509 |
auto void cl_print (int pos, int c); |
auto void cl_print (int pos, int c); |
589 |
|
|
590 |
cl_insert (cmdline); |
cl_insert (cmdline); |
591 |
|
|
592 |
|
pupa_history_add (buf); |
593 |
|
|
594 |
while ((key = PUPA_TERM_ASCII_CHAR (pupa_getkey ())) != '\n' && key != '\r') |
while ((key = PUPA_TERM_ASCII_CHAR (pupa_getkey ())) != '\n' && key != '\r') |
595 |
{ |
{ |
596 |
if (readline) |
if (readline) |
624 |
break; |
break; |
625 |
|
|
626 |
case 9: /* Ctrl-i or TAB */ |
case 9: /* Ctrl-i or TAB */ |
627 |
/* FIXME */ |
{ |
628 |
|
char *insert; |
629 |
|
int restore; |
630 |
|
|
631 |
|
/* Backup the next character and make it 0 so it will |
632 |
|
be easy to use string functions. */ |
633 |
|
char backup = buf[lpos]; |
634 |
|
buf[lpos] = '\0'; |
635 |
|
|
636 |
|
|
637 |
|
insert = pupa_tab_complete (buf, &restore); |
638 |
|
/* Restore the original string. */ |
639 |
|
buf[lpos] = backup; |
640 |
|
|
641 |
|
if (restore) |
642 |
|
{ |
643 |
|
/* Restore the prompt. */ |
644 |
|
pupa_printf ("\n%s%s", prompt, buf); |
645 |
|
xpos = plen; |
646 |
|
ystart = ypos = (pupa_getxy () & 0xFF); |
647 |
|
} |
648 |
|
|
649 |
|
if (insert) |
650 |
|
{ |
651 |
|
cl_insert (insert); |
652 |
|
pupa_free (insert); |
653 |
|
} |
654 |
|
} |
655 |
break; |
break; |
656 |
|
|
657 |
case 11: /* Ctrl-k */ |
case 11: /* Ctrl-k */ |
668 |
break; |
break; |
669 |
|
|
670 |
case 14: /* Ctrl-n */ |
case 14: /* Ctrl-n */ |
671 |
/* FIXME */ |
{ |
672 |
break; |
char *hist; |
673 |
|
|
674 |
|
lpos = 0; |
675 |
|
|
676 |
|
if (histpos > 0) |
677 |
|
histpos--; |
678 |
|
|
679 |
|
cl_delete (llen); |
680 |
|
hist = pupa_history_get (histpos); |
681 |
|
cl_insert (hist); |
682 |
|
|
683 |
|
break; |
684 |
|
} |
685 |
case 16: /* Ctrl-p */ |
case 16: /* Ctrl-p */ |
686 |
/* FIXME */ |
{ |
687 |
|
char *hist; |
688 |
|
|
689 |
|
lpos = 0; |
690 |
|
|
691 |
|
if (histpos < hist_used - 1) |
692 |
|
histpos++; |
693 |
|
|
694 |
|
cl_delete (llen); |
695 |
|
hist = pupa_history_get (histpos); |
696 |
|
|
697 |
|
cl_insert (hist); |
698 |
|
} |
699 |
break; |
break; |
700 |
|
|
701 |
case 21: /* Ctrl-u */ |
case 21: /* Ctrl-u */ |
756 |
} |
} |
757 |
break; |
break; |
758 |
} |
} |
759 |
|
|
760 |
|
pupa_history_replace (histpos, buf); |
761 |
} |
} |
762 |
|
|
763 |
pupa_putchar ('\n'); |
pupa_putchar ('\n'); |