diff -urN xenoloader-empty/Makefile xenoloader/Makefile --- xenoloader-empty/Makefile 1969-12-31 18:00:00.000000000 -0600 +++ xenoloader/Makefile 2005-02-03 14:47:08.000000000 -0600 @@ -0,0 +1,31 @@ +############################################################################### +## +## Makefile +## +## Copyright (C) International Business Machines Corp., 2005 +## Author(s): Anthony Liguori (aliguori@us.ibm.com) +## +## Xen Psuedo-Boot Loader +## +## This library is free software; you can redistribute it and/or modify +## it under the terms of the GNU Lesser General Public License as published +## by the Free Software Foundation; either version 2.1 of the License, or +## (at your option) any later version. +## +## This library is distributed in the hope that it will be useful, +## but WITHOUT ANY WARRANTY; without even the implied warranty of +## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See +## the GNU Lesser General Public License for more details. +## +## You should have received a copy of the GNU Lesser General Public License +## along with this library; if not, write to the Free Software +## Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +## +############################################################################### + +CFLAGS=-Wall -g + +all: main.o + $(CC) $(CFLAGS) -o xenoloader main.o -lcurses + +clean:; $(RM) main.o xenoloader diff -urN xenoloader-empty/main.c xenoloader/main.c --- xenoloader-empty/main.c 1969-12-31 18:00:00.000000000 -0600 +++ xenoloader/main.c 2005-02-03 14:56:00.000000000 -0600 @@ -0,0 +1,278 @@ +/*\ + * xenoloader/main.c + * + * Copyright (C) International Business Machines Corp., 2005 + * Author(s): Anthony Liguori (aliguori@us.ibm.com) + * + * Xen Psuedo-Boot Loader + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See + * the GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +\*/ + +#include +#include +#include +#include +#include +#include + +/* simple structure representing a single grub + command. argv[0] is the name of the command. + Use CMD_REST to concatinate multiple arguments + while preserving original spacing */ +struct grub_cmd { + int argc; + char **argv; + char *line; +}; + +/* the x,y offset of the first entry to select */ +#define X_OFFSET 3 +#define Y_OFFSET 6 + +/* gets the concatination of the rest of the line + starting at argv[i] */ +#define CMD_REST(cmd, i) ((cmd).line + ((cmd).argv[(i)] - (cmd).argv[0])) + +/* reads a grub configuration file and returns a null-terminated array of + grub commands */ +struct grub_cmd *read_grub_conf(const char *file) +{ + FILE *f = fopen(file, "r"); + char buffer[4096]; + struct grub_cmd *cmds = 0; + size_t capacity = 0; + size_t size = 0; + + if (!f) return NULL; + + while(fgets(buffer, sizeof(buffer), f)) { + char *ptr = buffer; + int words = 0; + int in_word = 0; + + /* skip spaces */ + while (isspace(*ptr)) ptr++; + + /* skip comments and blank lines */ + if (*ptr == '#' || !*ptr) continue; + + /* make sure we have enough room for this command and the null terminator */ + if ((size + 2) > capacity) { + capacity += 100; + cmds = realloc(cmds, capacity * sizeof(cmds[0])); + } + + cmds[size].line = strdup(ptr); + + /* count number of words */ + for (ptr = cmds[size].line; *ptr; ptr++) { + if (isspace(*ptr)) { + in_word = 0; + } else if (!in_word) { + words++; + in_word = 1; + } + if (*ptr == '\r' || *ptr == '\n') *ptr = 0; + } + + /* allocate argc/argv */ + cmds[size].argc = words; + cmds[size].argv = malloc(sizeof(char*) * words); + + /* duplicate a string to tokenize */ + cmds[size].argv[0] = strdup(cmds[size].line); + words = 1; + in_word = 1; + + /* tokenize */ + for (ptr = cmds[size].argv[0]; *ptr; ptr++) { + if (isspace(*ptr)) { + if (in_word) *ptr = 0; + in_word = 0; + } else if (!in_word) { + cmds[size].argv[words] = ptr; + words++; + in_word = 1; + } + } + + size++; + } + + fclose(f); + + /* NULL terminate */ + cmds[size].argc = 0; + cmds[size].argv = 0; + cmds[size].line = 0; + + return cmds; +} + +/* quick routine that draws a box using ASCII art. There's + probably a better way to do this with curses */ +static void make_box(int x, int y, int width, int height) +{ + int i; + + mvaddch(y, x, 0xda); + for (i = x + 1; i < x + width; i++) { + mvaddch(y, i, 0xc4); + } + mvaddch(y, x+width, 0xbf); + + for (i = y + 1; i < y + height; i++) { + mvaddch(i, x, 0xb3); + mvaddch(i, x+width, 0xb3); + } + + mvaddch(y+height, x, 0xc0); + for (i = x + 1; i < x + width; i++) { + mvaddch(y+height, i, 0xc4); + } + mvaddch(y+height, x+width, 0xd9); +} + +/* signal handler */ +static void finish(int sig) +{ + endwin(); + exit(0); +} + +int main(int argc, char **argv) +{ + int ch; + int pos = 0; + struct grub_cmd *cmds; + int selected = -1; + int i; + int n = 0; + + if (argc != 2) { + printf("Usage: %s CONFIG\n", argv[0]); + exit(1); + } + + /* Read the grub file */ + cmds = read_grub_conf(argv[1]); + if (!cmds) { + fprintf(stderr, "Error reading `%s': %m\n", argv[1]); + exit(1); + } + + /* Set up curses and signal handlers */ + signal(SIGINT, finish); + initscr(); + keypad(stdscr, TRUE); + intrflush(stdscr, FALSE); + nonl(); + cbreak(); + noecho(); + + /* Display banner */ + mvaddstr(2, 2, "XenoLoader v0.0.1"); + + /* Display box with choices */ + make_box(1, Y_OFFSET - 2, 74, 10); + for (i = 0; cmds[i].line; i++) { + if (!strcmp(cmds[i].argv[0], "title")) { + mvaddstr(Y_OFFSET + n, X_OFFSET + 2, CMD_REST(cmds[i], 1)); + n++; + } + } + + /* Display usage text */ + mvaddstr(18, 2, "Use the up and down keys to select which entry is highlighted."); + mvaddstr(19, 2, "Press enter to boot the selected OS."); + + /* Select the first choice */ + mvaddstr(Y_OFFSET + pos, X_OFFSET, " "); + + /* Input loop */ + while (selected == -1 && (ch = getch()) != EOF) { + int new_pos = pos; + switch (ch) { + case KEY_UP: + new_pos--; + break; + case KEY_DOWN: + new_pos++; + break; + case '\n': + case '\r': + selected = pos; + break; + } + + new_pos += n; + new_pos %= n; + + if (new_pos != pos) { + mvaddstr(Y_OFFSET + new_pos, X_OFFSET, " "); + pos = new_pos; + } + } + + /* Close the curses session */ + endwin(); + + /* This is all horribly inefficient but quick to write */ + + /* Find the index of the selected title */ + n = 0; + for (i = 0; cmds[i].line; i++) { + if (!strcmp(cmds[i].argv[0], "title")) { + if (n == selected) { + break; + } + n++; + } + } + + if (!cmds[i].line) { + printf("No kernel selected\n"); + } else { + const char *title = CMD_REST(cmds[i], 1); + const char *kernel = ""; + const char *cli = ""; + const char *root = ""; + + /* Read the options after the selected title until + the next title looking for the ones we care about + */ + for (i++; cmds[i].line; i++) { + if (!strcmp(cmds[i].argv[0], "title")) { + break; + } else if (!strcmp(cmds[i].argv[0], "kernel")) { + kernel = cmds[i].argv[1]; + if (cmds[i].argc > 2) { + cli = CMD_REST(cmds[i], 2); + } + } else if (!strcmp(cmds[i].argv[0], "root")) { + root = CMD_REST(cmds[i], 1); + } + } + + /* We've got our info.. now what do we do with it */ + printf("Selected kernel `%s'\n", title); + printf("Root is `%s'\n", root); + printf("Kernel image is `%s'\n", kernel); + printf("Kernel command line is `%s'\n", cli); + } + + return 0; +}