aboutsummaryrefslogtreecommitdiffstats
path: root/com32/elflink/ldlinux
diff options
context:
space:
mode:
authorMatt Fleming <matt.fleming@linux.intel.com>2011-02-13 21:02:51 +0000
committerMatt Fleming <matt.fleming@linux.intel.com>2011-03-09 14:32:36 +0000
commitfd86cda5f1cb5bb1c33579105d0f2f515f50c3a4 (patch)
treed392729b82031d680df5d50f05d09dac4b213334 /com32/elflink/ldlinux
parent2adb4a29248b8def7a967802ec1595024e8b77d6 (diff)
downloadsyslinux-fd86cda5f1cb5bb1c33579105d0f2f515f50c3a4.tar.gz
syslinux-fd86cda5f1cb5bb1c33579105d0f2f515f50c3a4.tar.xz
syslinux-fd86cda5f1cb5bb1c33579105d0f2f515f50c3a4.zip
elflink: Move code out of core and into ldlinux.c32
Currently, there is a lot of code in core/elflink that is not strictly required for the core to function. There are benefits to keeping the core proper as small as possible, for example so that it can fit within a single 64K block on btrfs. So move the following pieces of code out of core/elflink and into a new ldlinux ELF module which will be automatically loaded by the core: - command line interface code - C configuration parser - refstr implementation - rest of the menu system Below are some measurements of the size of ldlinux.elf before and after this change. Before: text data bss dec hex filename 161328 10439 913468 1085235 108f33 core/ldlinux.elf After: text data bss dec hex filename 142423 10415 909500 1062338 1035c2 core/ldlinux.elf Signed-off-by: Matt Fleming <matt.fleming@linux.intel.com>
Diffstat (limited to 'com32/elflink/ldlinux')
-rw-r--r--com32/elflink/ldlinux/Makefile38
-rw-r--r--com32/elflink/ldlinux/adv.c49
-rw-r--r--com32/elflink/ldlinux/cli.c438
-rw-r--r--com32/elflink/ldlinux/colors.c184
-rw-r--r--com32/elflink/ldlinux/getadv.c68
-rw-r--r--com32/elflink/ldlinux/ipappend.c58
-rw-r--r--com32/elflink/ldlinux/ldlinux.c34
-rw-r--r--com32/elflink/ldlinux/readconfig.c1169
-rw-r--r--com32/elflink/ldlinux/refstr.c106
-rw-r--r--com32/elflink/ldlinux/refstr.h40
10 files changed, 2184 insertions, 0 deletions
diff --git a/com32/elflink/ldlinux/Makefile b/com32/elflink/ldlinux/Makefile
new file mode 100644
index 00000000..339e13ee
--- /dev/null
+++ b/com32/elflink/ldlinux/Makefile
@@ -0,0 +1,38 @@
+## -----------------------------------------------------------------------
+##
+## Copyright 2011 Intel Corporation - All Rights Reserved
+##
+## This program is free software; you can redistribute it and/or modify
+## it under the terms of the GNU General Public License as published by
+## the Free Software Foundation, Inc., 53 Temple Place Ste 330,
+## Boston MA 02111-1307, USA; either version 2 of the License, or
+## (at your option) any later version; incorporated herein by reference.
+##
+## -----------------------------------------------------------------------
+
+topdir = ../../..
+include ../modules/MCONFIG
+
+CFLAGS += -I../modules -I$(topdir)/core/elflink
+
+all: ldlinux.c32
+
+ldlinux.c32 : ldlinux.o cli.o readconfig.o refstr.o colors.o getadv.o \
+ adv.o ipappend.o
+ $(LD) $(LDFLAGS) -o $@ $^
+
+tidy dist:
+ rm -f *.o *.lo *.a *.lst .*.d
+
+clean: tidy
+ rm -f *.lss *.lnx *.com *.c32
+
+spotless: clean
+ rm -f *~ \#*
+
+install: all
+ mkdir -m 755 -p $(INSTALLROOT)$(AUXDIR)
+ install -m 644 ldlinux.c32 $(INSTALLROOT)$(AUXDIR)
+
+
+-include .*.d
diff --git a/com32/elflink/ldlinux/adv.c b/com32/elflink/ldlinux/adv.c
new file mode 100644
index 00000000..be38e89d
--- /dev/null
+++ b/com32/elflink/ldlinux/adv.c
@@ -0,0 +1,49 @@
+/* ----------------------------------------------------------------------- *
+ *
+ * Copyright 2007-2008 H. Peter Anvin - All Rights Reserved
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use,
+ * copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom
+ * the Software is furnished to do so, subject to the following
+ * conditions:
+ *
+ * The above copyright notice and this permission notice shall
+ * be included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * ----------------------------------------------------------------------- */
+
+/*
+ * syslinux/adv.c
+ *
+ * Access the syslinux auxilliary data vector
+ */
+
+#include <syslinux/adv.h>
+#include <klibc/compiler.h>
+#include <com32.h>
+
+void *__syslinux_adv_ptr;
+size_t __syslinux_adv_size;
+
+void __constructor __syslinux_get_adv(void)
+{
+ static com32sys_t reg;
+
+ reg.eax.w[0] = 0x001c;
+ __intcall(0x22, &reg, &reg);
+ __syslinux_adv_ptr = MK_PTR(reg.es, reg.ebx.w[0]);
+ __syslinux_adv_size = reg.ecx.w[0];
+}
diff --git a/com32/elflink/ldlinux/cli.c b/com32/elflink/ldlinux/cli.c
new file mode 100644
index 00000000..551113f8
--- /dev/null
+++ b/com32/elflink/ldlinux/cli.c
@@ -0,0 +1,438 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <console.h>
+#include <com32.h>
+#include <syslinux/adv.h>
+#include <syslinux/config.h>
+#include <setjmp.h>
+#include <netinet/in.h>
+#include <limits.h>
+#include <minmax.h>
+#include <linux/list.h>
+#include <sys/exec.h>
+#include <sys/module.h>
+#include <core-elf.h>
+
+#include "getkey.h"
+#include "menu.h"
+#include "cli.h"
+
+static jmp_buf timeout_jump;
+
+static struct list_head cli_history_head;
+
+void clear_screen(void)
+{
+ //mp("enter");
+ fputs("\033e\033%@\033)0\033(B\1#0\033[?25l\033[2J", stdout);
+}
+
+int mygetkey(clock_t timeout)
+{
+ clock_t t0, t;
+ clock_t tto, to;
+ int key;
+
+ //mp("enter");
+ if (!totaltimeout)
+ return get_key(stdin, timeout);
+
+ for (;;) {
+ tto = min(totaltimeout, INT_MAX);
+ to = timeout ? min(tto, timeout) : tto;
+
+ t0 = 0;
+ key = get_key(stdin, to);
+ t = 0 - t0;
+
+ if (totaltimeout <= t)
+ longjmp(timeout_jump, 1);
+
+ totaltimeout -= t;
+
+ if (key != KEY_NONE) {
+ //mp("get key 0x%x", key);
+ return key;
+ }
+
+ if (timeout) {
+ if (timeout <= t) {
+ //mp("timeout");
+ return KEY_NONE;
+ }
+
+ timeout -= t;
+ }
+ }
+}
+
+const char *edit_cmdline(const char *input, int top /*, int width */ ,
+ int (*pDraw_Menu) (int, int, int),
+ void (*show_fkey) (int))
+{
+ static char cmdline[MAX_CMDLINE_LEN];
+ char temp_cmdline[MAX_CMDLINE_LEN] = { };
+ int key, len, prev_len, cursor;
+ int redraw = 1; /* We enter with the menu already drawn */
+ int x, y;
+ bool done = false;
+ const char *ret;
+ int width = 0;
+ struct cli_command *comm_counter;
+ comm_counter =
+ list_entry(cli_history_head.next->prev, typeof(*comm_counter), list);
+
+ if (!width) {
+ int height;
+ if (getscreensize(1, &height, &width))
+ width = 80;
+ }
+
+ strncpy(cmdline, input, MAX_CMDLINE_LEN);
+ cmdline[MAX_CMDLINE_LEN - 1] = '\0';
+
+ len = cursor = strlen(cmdline);
+ prev_len = 0;
+ x = y = 0;
+
+ while (!done) {
+ if (redraw > 1 && pDraw_Menu != NULL) {
+ /* Clear and redraw whole screen */
+ /* Enable ASCII on G0 and DEC VT on G1; do it in this order
+ to avoid confusing the Linux console */
+ /* clear_screen();
+ draw_menu(-1, top, 1); */
+ clear_screen();
+ (*pDraw_Menu) (-1, top, 1);
+ prev_len = 0;
+ // printf("\033[0m\033[2J\033[H");
+ }
+
+ if (redraw > 0) {
+ int dy, at;
+
+ prev_len = max(len, prev_len);
+
+ /* Redraw the command line */
+ printf("\033[?7l\033[?25l");
+ if (y)
+ printf("\033[%dA", y);
+ printf("\033[1G\033[1;36m> \033[0m");
+
+ x = 2;
+ y = 0;
+ at = 0;
+ while (at < prev_len) {
+ putchar(at >= len ? ' ' : cmdline[at]);
+ at++;
+ x++;
+ if (x >= width) {
+ printf("\r\n");
+ x = 0;
+ y++;
+ }
+ }
+ printf("\033[K\r");
+
+ dy = y - (cursor + 2) / width;
+ x = (cursor + 2) % width;
+
+ if (dy) {
+ printf("\033[%dA", dy);
+ y -= dy;
+ }
+ if (x)
+ printf("\033[%dC", x);
+ printf("\033[?25h");
+ prev_len = len;
+ redraw = 0;
+ }
+
+ key = mygetkey(0);
+
+ switch (key) {
+ case KEY_CTRL('L'):
+ redraw = 2;
+ break;
+
+ case KEY_ENTER:
+ case KEY_CTRL('J'):
+ ret = cmdline;
+ done = true;
+ break;
+
+ case KEY_ESC:
+ case KEY_CTRL('C'):
+ ret = NULL;
+ done = true;
+ break;
+
+ case KEY_BACKSPACE:
+ case KEY_DEL:
+ if (cursor) {
+ memmove(cmdline + cursor - 1, cmdline + cursor,
+ len - cursor + 1);
+ len--;
+ cursor--;
+ redraw = 1;
+ }
+ break;
+
+ case KEY_CTRL('D'):
+ case KEY_DELETE:
+ if (cursor < len) {
+ memmove(cmdline + cursor, cmdline + cursor + 1, len - cursor);
+ len--;
+ redraw = 1;
+ }
+ break;
+
+ case KEY_CTRL('U'):
+ if (len) {
+ len = cursor = 0;
+ cmdline[len] = '\0';
+ redraw = 1;
+ }
+ break;
+
+ case KEY_CTRL('W'):
+ if (cursor) {
+ int prevcursor = cursor;
+
+ while (cursor && my_isspace(cmdline[cursor - 1]))
+ cursor--;
+
+ while (cursor && !my_isspace(cmdline[cursor - 1]))
+ cursor--;
+
+#if 0
+ memmove(cmdline + cursor, cmdline + prevcursor,
+ len - prevcursor + 1);
+#else
+ {
+ int i;
+ char *q = cmdline + cursor;
+ char *p = cmdline + prevcursor;
+ for (i = 0; i < len - prevcursor + 1; i++)
+ *q++ = *p++;
+ }
+#endif
+ len -= (prevcursor - cursor);
+ redraw = 1;
+ }
+ break;
+
+ case KEY_LEFT:
+ case KEY_CTRL('B'):
+ if (cursor) {
+ cursor--;
+ redraw = 1;
+ }
+ break;
+
+ case KEY_RIGHT:
+ case KEY_CTRL('F'):
+ if (cursor < len) {
+ putchar(cmdline[cursor]);
+ cursor++;
+ x++;
+ if (x >= width) {
+ printf("\r\n");
+ y++;
+ x = 0;
+ }
+ }
+ break;
+
+ case KEY_CTRL('K'):
+ if (cursor < len) {
+ cmdline[len = cursor] = '\0';
+ redraw = 1;
+ }
+ break;
+
+ case KEY_HOME:
+ case KEY_CTRL('A'):
+ if (cursor) {
+ cursor = 0;
+ redraw = 1;
+ }
+ break;
+
+ case KEY_END:
+ case KEY_CTRL('E'):
+ if (cursor != len) {
+ cursor = len;
+ redraw = 1;
+ }
+ break;
+
+ case KEY_F1:
+ case KEY_F2:
+ case KEY_F3:
+ case KEY_F4:
+ case KEY_F5:
+ case KEY_F6:
+ case KEY_F7:
+ case KEY_F8:
+ case KEY_F9:
+ case KEY_F10:
+ case KEY_F11:
+ case KEY_F12:
+ if (show_fkey != NULL) {
+ (*show_fkey) (key);
+ redraw = 1;
+ }
+ break;
+ case KEY_UP:
+ {
+ if (!list_empty(&cli_history_head)) {
+ comm_counter =
+ list_entry(comm_counter->list.next,
+ typeof(*comm_counter), list);
+ if (&comm_counter->list == &cli_history_head) {
+ strcpy(cmdline, temp_cmdline);
+ } else {
+ strcpy(cmdline, comm_counter->command);
+ }
+ cursor = len = strlen(cmdline);
+ redraw = 1;
+ }
+ }
+ break;
+ case KEY_DOWN:
+ {
+ if (!list_empty(&cli_history_head)) {
+ comm_counter =
+ list_entry(comm_counter->list.prev,
+ typeof(*comm_counter), list);
+ if (&comm_counter->list == &cli_history_head) {
+ strcpy(cmdline, temp_cmdline);
+ } else {
+ strcpy(cmdline, comm_counter->command);
+ }
+ cursor = len = strlen(cmdline);
+ redraw = 1;
+ }
+ }
+ break;
+ default:
+ if (key >= ' ' && key <= 0xFF && len < MAX_CMDLINE_LEN - 1) {
+ if (cursor == len) {
+ temp_cmdline[len] = key;
+ cmdline[len++] = key;
+ temp_cmdline[len] = cmdline[len] = '\0';
+ putchar(key);
+ cursor++;
+ x++;
+ if (x >= width) {
+ printf("\r\n\033[K");
+ y++;
+ x = 0;
+ }
+ prev_len++;
+ } else {
+ memmove(cmdline + cursor + 1, cmdline + cursor,
+ len - cursor + 1);
+ memmove(temp_cmdline + cursor + 1, temp_cmdline + cursor,
+ len - cursor + 1);
+ temp_cmdline[cursor] = key;
+ cmdline[cursor++] = key;
+ len++;
+ redraw = 1;
+ }
+ }
+ break;
+ }
+ }
+
+ printf("\033[?7h");
+ return ret;
+}
+
+void process_command(const char *cmd, bool history)
+{
+ char **argv = malloc((MAX_COMMAND_ARGS + 1) * sizeof(char *));
+ char *temp_cmd = (char *)malloc(sizeof(char) * (strlen(cmd) + 1));
+ int argc = 1, len_mn;
+ char *crt_arg, *module_name;
+
+ /* return if user only press enter */
+ if (cmd[0] == '\0') {
+ printf("\n");
+ return;
+ }
+ printf("\n");
+
+ if (history) {
+ struct cli_command *comm;
+
+ comm = (struct cli_command *)malloc(sizeof(struct cli_command *));
+ comm->command =
+ (char *)malloc(sizeof(char) * (strlen(cmd) + 1));
+ strcpy(comm->command, cmd);
+ list_add(&(comm->list), &cli_history_head);
+ }
+
+ // mp("raw cmd = %s", cmd);
+ strcpy(temp_cmd, cmd);
+ module_name = strtok(cmd, COMMAND_DELIM);
+ len_mn = strlen(module_name);
+
+ if (!strcmp(module_name + len_mn - 4, ".c32")) {
+ if (module_find(module_name) != NULL) {
+ /* make module re-enterable */
+ // mp("Module %s is already running");
+ }
+ do {
+ argv[0] = module_name;
+ crt_arg = strtok(NULL, COMMAND_DELIM);
+ if (crt_arg != NULL && strlen(crt_arg) > 0) {
+ argv[argc] = crt_arg;
+ argc++;
+ } else
+ break;
+ } while (argc < MAX_COMMAND_ARGS);
+ argv[argc] = NULL;
+ module_load_dependencies(module_name, MODULES_DEP);
+ spawn_load(module_name, argv);
+ } else if (!strcmp(module_name + len_mn - 2, ".0")) {
+ execute(cmd, KT_PXE);
+ } else if (!strcmp(module_name + len_mn - 3, ".bs")) {
+ } else if (!strcmp(module_name + len_mn - 4, ".img")) {
+ execute(cmd, KT_FDIMAGE);
+ } else if (!strcmp(module_name + len_mn - 4, ".bin")) {
+ } else if (!strcmp(module_name + len_mn - 4, ".bss")) {
+ execute(cmd, KT_BSS);
+ } else if (!strcmp(module_name + len_mn - 4, ".com")
+ || !strcmp(module_name + len_mn - 4, ".cbt")) {
+ execute(cmd, KT_COMBOOT);
+ } else if (!strcmp(module_name + len_mn - 4, ".cfg")
+ || !strcmp(module_name + len_mn - 5, ".conf")
+ || !strcmp(module_name + len_mn - 7, ".config")) {
+ execute(module_name, KT_CONFIG);
+ }
+ /* use KT_KERNEL as default */
+ else
+ execute(temp_cmd, KT_KERNEL);
+
+cleanup:
+ free(argv);
+ free(temp_cmd);
+}
+
+static int cli_init(void)
+{
+ INIT_LIST_HEAD(&cli_history_head);
+
+ return 0;
+}
+
+static void cli_exit(void)
+{
+ /* Nothing to do */
+}
+
+MODULE_INIT(cli_init);
+MODULE_EXIT(cli_exit);
diff --git a/com32/elflink/ldlinux/colors.c b/com32/elflink/ldlinux/colors.c
new file mode 100644
index 00000000..68732bdb
--- /dev/null
+++ b/com32/elflink/ldlinux/colors.c
@@ -0,0 +1,184 @@
+/* ----------------------------------------------------------------------- *
+ *
+ * Copyright 2004-2008 H. Peter Anvin - All Rights Reserved
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+ * Boston MA 02110-1301, USA; either version 2 of the License, or
+ * (at your option) any later version; incorporated herein by reference.
+ *
+ * ----------------------------------------------------------------------- */
+
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include <colortbl.h>
+#include "menu.h"
+
+/*
+ * The color/attribute indexes (\1#X, \2#XX, \3#XXX) are as follows
+ *
+ * 00 - screen Rest of the screen
+ * 01 - border Border area
+ * 02 - title Title bar
+ * 03 - unsel Unselected menu item
+ * 04 - hotkey Unselected hotkey
+ * 05 - sel Selection bar
+ * 06 - hotsel Selected hotkey
+ * 07 - scrollbar Scroll bar
+ * 08 - tabmsg Press [Tab] message
+ * 09 - cmdmark Command line marker
+ * 10 - cmdline Command line
+ * 11 - pwdborder Password box border
+ * 12 - pwdheader Password box header
+ * 13 - pwdentry Password box contents
+ * 14 - timeout_msg Timeout message
+ * 15 - timeout Timeout counter
+ * 16 - help Current entry help text
+ * 17 - disabled Disabled menu item
+ */
+
+static const struct color_table default_colors[] = {
+ {"screen", "37;40", 0x80ffffff, 0x00000000, SHADOW_NORMAL},
+ {"border", "30;44", 0x40000000, 0x00000000, SHADOW_NORMAL},
+ {"title", "1;36;44", 0xc00090f0, 0x00000000, SHADOW_NORMAL},
+ {"unsel", "37;44", 0x90ffffff, 0x00000000, SHADOW_NORMAL},
+ {"hotkey", "1;37;44", 0xffffffff, 0x00000000, SHADOW_NORMAL},
+ {"sel", "7;37;40", 0xe0000000, 0x20ff8000, SHADOW_ALL},
+ {"hotsel", "1;7;37;40", 0xe0400000, 0x20ff8000, SHADOW_ALL},
+ {"scrollbar", "30;44", 0x40000000, 0x00000000, SHADOW_NORMAL},
+ {"tabmsg", "31;40", 0x90ffff00, 0x00000000, SHADOW_NORMAL},
+ {"cmdmark", "1;36;40", 0xc000ffff, 0x00000000, SHADOW_NORMAL},
+ {"cmdline", "37;40", 0xc0ffffff, 0x00000000, SHADOW_NORMAL},
+ {"pwdborder", "30;47", 0x80ffffff, 0x20ffffff, SHADOW_NORMAL},
+ {"pwdheader", "31;47", 0x80ff8080, 0x20ffffff, SHADOW_NORMAL},
+ {"pwdentry", "30;47", 0x80ffffff, 0x20ffffff, SHADOW_NORMAL},
+ {"timeout_msg", "37;40", 0x80ffffff, 0x00000000, SHADOW_NORMAL},
+ {"timeout", "1;37;40", 0xc0ffffff, 0x00000000, SHADOW_NORMAL},
+ {"help", "37;40", 0xc0ffffff, 0x00000000, SHADOW_NORMAL},
+ {"disabled", "1;30;44", 0x60cccccc, 0x00000000, SHADOW_NORMAL},
+};
+
+#define NCOLORS (sizeof default_colors/sizeof default_colors[0])
+const int message_base_color = NCOLORS;
+const int menu_color_table_size = NCOLORS + 256;
+
+/* Algorithmically generate the msgXX colors */
+void set_msg_colors_global(struct color_table *tbl,
+ unsigned int fg, unsigned int bg,
+ enum color_table_shadow shadow)
+{
+ struct color_table *cp = tbl + message_base_color;
+ unsigned int i;
+ unsigned int fga, bga;
+ unsigned int fgh, bgh;
+ unsigned int fg_idx, bg_idx;
+ unsigned int fg_rgb, bg_rgb;
+
+ static const unsigned int pc2rgb[8] =
+ { 0x000000, 0x0000ff, 0x00ff00, 0x00ffff, 0xff0000, 0xff00ff, 0xffff00,
+ 0xffffff
+ };
+
+ /* Converting PC RGBI to sensible RGBA values is an "interesting"
+ proposition. This algorithm may need plenty of tweaking. */
+
+ fga = fg & 0xff000000;
+ fgh = ((fg >> 1) & 0xff000000) | 0x80000000;
+
+ bga = bg & 0xff000000;
+ bgh = ((bg >> 1) & 0xff000000) | 0x80000000;
+
+ for (i = 0; i < 256; i++) {
+ fg_idx = i & 15;
+ bg_idx = i >> 4;
+
+ fg_rgb = pc2rgb[fg_idx & 7] & fg;
+ bg_rgb = pc2rgb[bg_idx & 7] & bg;
+
+ if (fg_idx & 8) {
+ /* High intensity foreground */
+ fg_rgb |= fgh;
+ } else {
+ fg_rgb |= fga;
+ }
+
+ if (bg_idx == 0) {
+ /* Default black background, assume transparent */
+ bg_rgb = 0;
+ } else if (bg_idx & 8) {
+ bg_rgb |= bgh;
+ } else {
+ bg_rgb |= bga;
+ }
+
+ cp->argb_fg = fg_rgb;
+ cp->argb_bg = bg_rgb;
+ cp->shadow = shadow;
+ cp++;
+ }
+}
+
+struct color_table *default_color_table(void)
+{
+ unsigned int i;
+ const struct color_table *dp;
+ struct color_table *cp;
+ struct color_table *color_table;
+ static const int pc2ansi[8] = { 0, 4, 2, 6, 1, 5, 3, 7 };
+ static char msg_names[6 * 256];
+ char *mp;
+
+ color_table = calloc(NCOLORS + 256, sizeof(struct color_table));
+
+ dp = default_colors;
+ cp = color_table;
+
+ for (i = 0; i < NCOLORS; i++) {
+ *cp = *dp;
+ cp->ansi = refstrdup(dp->ansi);
+ cp++;
+ dp++;
+ }
+
+ mp = msg_names;
+ for (i = 0; i < 256; i++) {
+ cp->name = mp;
+ mp += sprintf(mp, "msg%02x", i) + 1;
+
+ rsprintf(&cp->ansi, "%s3%d;4%d", (i & 8) ? "1;" : "",
+ pc2ansi[i & 7], pc2ansi[(i >> 4) & 7]);
+ cp++;
+ }
+
+ /*** XXX: This needs to move to run_menu() ***/
+ console_color_table = color_table;
+ console_color_table_size = NCOLORS + 256;
+
+ set_msg_colors_global(color_table, MSG_COLORS_DEF_FG,
+ MSG_COLORS_DEF_BG, MSG_COLORS_DEF_SHADOW);
+
+ return color_table;
+}
+
+struct color_table *copy_color_table(const struct color_table *master)
+{
+ const struct color_table *dp;
+ struct color_table *color_table, *cp;
+ unsigned int i;
+
+ color_table = calloc(NCOLORS + 256, sizeof(struct color_table));
+
+ dp = master;
+ cp = color_table;
+
+ for (i = 0; i < NCOLORS + 256; i++) {
+ *cp = *dp;
+ cp->ansi = refstr_get(dp->ansi);
+ cp++;
+ dp++;
+ }
+
+ return color_table;
+}
diff --git a/com32/elflink/ldlinux/getadv.c b/com32/elflink/ldlinux/getadv.c
new file mode 100644
index 00000000..456084b0
--- /dev/null
+++ b/com32/elflink/ldlinux/getadv.c
@@ -0,0 +1,68 @@
+/* ----------------------------------------------------------------------- *
+ *
+ * Copyright 2007-2008 H. Peter Anvin - All Rights Reserved
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use,
+ * copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom
+ * the Software is furnished to do so, subject to the following
+ * conditions:
+ *
+ * The above copyright notice and this permission notice shall
+ * be included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * ----------------------------------------------------------------------- */
+
+/*
+ * syslinux/getadv.c
+ *
+ * Get a data item from the auxilliary data vector. Returns a pointer
+ * and sets *size on success; NULL on failure.
+ */
+
+#include <syslinux/adv.h>
+#include <klibc/compiler.h>
+#include <inttypes.h>
+
+const void *syslinux_getadv(int tag, size_t * size)
+{
+ const uint8_t *p;
+ size_t left, len;
+
+ p = syslinux_adv_ptr();
+ left = syslinux_adv_size();
+
+ while (left >= 2) {
+ uint8_t ptag = *p++;
+ size_t plen = *p++;
+ left -= 2;
+
+ if (ptag == ADV_END)
+ return NULL; /* Not found */
+
+ if (left < plen)
+ return NULL; /* Item overrun */
+
+ if (ptag == tag) {
+ *size = plen;
+ return p;
+ }
+
+ p += plen;
+ left -= plen;
+ }
+
+ return NULL;
+}
diff --git a/com32/elflink/ldlinux/ipappend.c b/com32/elflink/ldlinux/ipappend.c
new file mode 100644
index 00000000..bd000920
--- /dev/null
+++ b/com32/elflink/ldlinux/ipappend.c
@@ -0,0 +1,58 @@
+/* ----------------------------------------------------------------------- *
+ *
+ * Copyright 2008 H. Peter Anvin - All Rights Reserved
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use,
+ * copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom
+ * the Software is furnished to do so, subject to the following
+ * conditions:
+ *
+ * The above copyright notice and this permission notice shall
+ * be included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * ----------------------------------------------------------------------- */
+
+/*
+ * syslinux/ipappend.c
+ *
+ * Get ipappend strings
+ */
+
+#include <syslinux/config.h>
+#include <klibc/compiler.h>
+#include <com32.h>
+
+struct syslinux_ipappend_strings __syslinux_ipappend_strings;
+static const char *syslinux_ipappend_string_list[32];
+
+void __constructor __syslinux_get_ipappend_strings(void)
+{
+ static com32sys_t reg;
+ int i;
+
+ reg.eax.w[0] = 0x000f;
+ __intcall(0x22, &reg, &reg);
+
+ if (!(reg.eflags.l & EFLAGS_CF)) {
+ __syslinux_ipappend_strings.count = reg.ecx.w[0];
+ __syslinux_ipappend_strings.ptr = syslinux_ipappend_string_list;
+ for (i = 0; i < reg.ecx.w[0]; i++) {
+ syslinux_ipappend_string_list[i] =
+ MK_PTR(reg.es,
+ *(uint16_t *) MK_PTR(reg.es, reg.ebx.w[0] + i * 2));
+ }
+ }
+}
diff --git a/com32/elflink/ldlinux/ldlinux.c b/com32/elflink/ldlinux/ldlinux.c
new file mode 100644
index 00000000..967eebb9
--- /dev/null
+++ b/com32/elflink/ldlinux/ldlinux.c
@@ -0,0 +1,34 @@
+#include <linux/list.h>
+#include <sys/times.h>
+#include <stdbool.h>
+#include "cli.h"
+#include "console.h"
+
+#include <sys/module.h>
+
+static void enter_cmdline(void)
+{
+ struct cli_command *aux;
+ char *cmdline;
+
+ /* Enter endless command line prompt, should support "exit" */
+ while (1) {
+ cmdline = edit_cmdline("", 1, NULL, NULL);
+ /* feng: give up the aux check here */
+ //aux = list_entry(cli_history_head.next, typeof(*aux), list);
+ //if (strcmp(aux->command, cmdline)) {
+ process_command(cmdline, true);
+ //}
+ }
+}
+
+static int ldlinux_main(int argc, char **argv)
+{
+ openconsole(&dev_rawcon_r, &dev_ansiserial_w);
+
+ /* Should never return */
+ enter_cmdline();
+
+ return 0;
+}
+MODULE_MAIN(ldlinux_main);
diff --git a/com32/elflink/ldlinux/readconfig.c b/com32/elflink/ldlinux/readconfig.c
new file mode 100644
index 00000000..98572337
--- /dev/null
+++ b/com32/elflink/ldlinux/readconfig.c
@@ -0,0 +1,1169 @@
+/* ----------------------------------------------------------------------- *
+ *
+ * Copyright 2004-2009 H. Peter Anvin - All Rights Reserved
+ * Copyright 2009 Intel Corporation; author: H. Peter Anvin
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+ * Boston MA 02110-1301, USA; either version 2 of the License, or
+ * (at your option) any later version; incorporated herein by reference.
+ *
+ * ----------------------------------------------------------------------- */
+
+#include <stdio.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <string.h>
+#include <minmax.h>
+#include <alloca.h>
+#include <inttypes.h>
+#include <colortbl.h>
+#include <com32.h>
+#include <syslinux/adv.h>
+#include <syslinux/config.h>
+#include <core-elf.h>
+
+#include "menu.h"
+
+const struct menu_parameter mparm[NPARAMS] = {
+ [P_WIDTH] = {"width", 0},
+ [P_MARGIN] = {"margin", 10},
+ [P_PASSWD_MARGIN] = {"passwordmargin", 3},
+ [P_MENU_ROWS] = {"rows", 12},
+ [P_TABMSG_ROW] = {"tabmsgrow", 18},
+ [P_CMDLINE_ROW] = {"cmdlinerow", 18},
+ [P_END_ROW] = {"endrow", -1},
+ [P_PASSWD_ROW] = {"passwordrow", 11},
+ [P_TIMEOUT_ROW] = {"timeoutrow", 20},
+ [P_HELPMSG_ROW] = {"helpmsgrow", 22},
+ [P_HELPMSGEND_ROW] = {"helpmsgendrow", -1},
+ [P_HSHIFT] = {"hshift", 0},
+ [P_VSHIFT] = {"vshift", 0},
+ [P_HIDDEN_ROW] = {"hiddenrow", -2},
+};
+
+short uappendlen; //bytes in append= command
+short ontimeoutlen; //bytes in ontimeout command
+short onerrorlen; //bytes in onerror command
+short forceprompt; //force prompt
+short noescape; //no escape
+short nocomplete; //no label completion on TAB key
+short allowimplicit = 1; //allow implicit kernels
+short allowoptions = 1; //user-specified options allowed
+short includelevel = 1; //nesting level
+short defaultlevel; //the current level of default
+short vkernel; //have we seen any "label" statements?
+short displaycon = 1; //conio.inc
+short nohalt = 1; //idle.inc
+
+
+/* Empty refstring */
+const char *empty_string;
+
+/* Root menu, starting menu, hidden menu, and list of all menus */
+struct menu *root_menu, *start_menu, *hide_menu, *menu_list, *default_menu;
+
+/* These are global parameters regardless of which menu we're displaying */
+int shiftkey = 0; /* Only display menu if shift key pressed */
+int hiddenmenu = 0;
+long long totaltimeout = 0;
+
+/* Keep track of global default */
+static int has_ui = 0; /* DEFAULT only counts if UI is found */
+extern char *globaldefault;
+static bool menusave = false; /* True if there is any "menu save" */
+
+/* Linked list of all entires, hidden or not; used by unlabel() */
+static struct menu_entry *all_entries;
+static struct menu_entry **all_entries_end = &all_entries;
+
+static const struct messages messages[MSG_COUNT] = {
+ [MSG_AUTOBOOT] = {"autoboot", "Automatic boot in # second{,s}..."},
+ [MSG_TAB] = {"tabmsg", "Press [Tab] to edit options"},
+ [MSG_NOTAB] = {"notabmsg", ""},
+ [MSG_PASSPROMPT] = {"passprompt", "Password required"},
+};
+
+#define astrdup(x) ({ char *__x = (x); \
+ size_t __n = strlen(__x) + 1; \
+ char *__p = alloca(__n); \
+ if ( __p ) memcpy(__p, __x, __n); \
+ __p; })
+
+/*
+ * Search the list of all menus for a specific label
+ */
+static struct menu *find_menu(const char *label)
+{
+ struct menu *m;
+
+ for (m = menu_list; m; m = m->next) {
+ if (!strcmp(label, m->label))
+ return m;
+ }
+
+ return NULL;
+}
+
+#define MAX_LINE 4096
+
+static char *skipspace(char *p)
+{
+ while (*p && my_isspace(*p))
+ p++;
+
+ return p;
+}
+
+/* Strip ^ from a string, returning a new reference to the same refstring
+ if none present */
+static const char *strip_caret(const char *str)
+{
+ const char *p, *r;
+ char *q;
+ int carets = 0;
+
+ p = str;
+ for (;;) {
+ p = strchr(p, '^');
+ if (!p)
+ break;
+ carets++;
+ p++;
+ }
+
+ if (!carets)
+ return refstr_get(str);
+
+ r = q = refstr_alloc(strlen(str) - carets);
+ for (p = str; *p; p++)
+ if (*p != '^')
+ *q++ = *p;
+
+ *q = '\0'; /* refstr_alloc() already did this... */
+
+ return r;
+}
+
+/* Check to see if we are at a certain keyword (case insensitive) */
+/* Returns a pointer to the first character past the keyword */
+static char *looking_at(char *line, const char *kwd)
+{
+ char *p = line;
+ const char *q = kwd;
+
+ while (*p && *q && ((*p ^ *q) & ~0x20) == 0) {
+ p++;
+ q++;
+ }
+
+ if (*q)
+ return NULL; /* Didn't see the keyword */
+
+ return my_isspace(*p) ? p : NULL; /* Must be EOL or whitespace */
+}
+
+static struct menu *new_menu(struct menu *parent,
+ struct menu_entry *parent_entry, const char *label)
+{
+ struct menu *m = calloc(1, sizeof(struct menu));
+ int i;
+
+ //mp("enter: menu_label = %s", label);
+
+ m->label = label;
+ m->title = refstr_get(empty_string);
+
+ if (parent) {
+ /* Submenu */
+ m->parent = parent;
+ m->parent_entry = parent_entry;
+ parent_entry->action = MA_SUBMENU;
+ parent_entry->submenu = m;
+
+ for (i = 0; i < MSG_COUNT; i++)
+ m->messages[i] = refstr_get(parent->messages[i]);
+
+ memcpy(m->mparm, parent->mparm, sizeof m->mparm);
+
+ m->allowedit = parent->allowedit;
+ m->timeout = parent->timeout;
+ m->save = parent->save;
+
+ m->ontimeout = refstr_get(parent->ontimeout);
+ m->onerror = refstr_get(parent->onerror);
+ m->menu_master_passwd = refstr_get(parent->menu_master_passwd);
+ m->menu_background = refstr_get(parent->menu_background);
+
+ m->color_table = copy_color_table(parent->color_table);
+
+ for (i = 0; i < 12; i++) {
+ m->fkeyhelp[i].textname = refstr_get(parent->fkeyhelp[i].textname);
+ m->fkeyhelp[i].background =
+ refstr_get(parent->fkeyhelp[i].background);
+ }
+ } else {
+ /* Root menu */
+ for (i = 0; i < MSG_COUNT; i++)
+ m->messages[i] = refstrdup(messages[i].defmsg);
+ for (i = 0; i < NPARAMS; i++)
+ m->mparm[i] = mparm[i].value;
+
+ m->allowedit = true; /* Allow edits of the command line */
+ m->color_table = default_color_table();
+ }
+
+ m->next = menu_list;
+ menu_list = m;
+
+ return m;
+}
+
+struct labeldata {
+ const char *label;
+ const char *kernel;
+ enum kernel_type type;
+ const char *append;
+ const char *initrd;
+ const char *menulabel;
+ const char *passwd;
+ char *helptext;
+ unsigned int ipappend;
+ unsigned int menuhide;
+ unsigned int menudefault;
+ unsigned int menuseparator;
+ unsigned int menudisabled;
+ unsigned int menuindent;
+ enum menu_action action;
+ int save;
+ struct menu *submenu;
+};
+
+/* Menu currently being parsed */
+static struct menu *current_menu;
+
+static void clear_label_data(struct labeldata *ld)
+{
+ refstr_put(ld->label);
+ refstr_put(ld->kernel);
+ refstr_put(ld->append);
+ refstr_put(ld->initrd);
+ refstr_put(ld->menulabel);
+ refstr_put(ld->passwd);
+
+ memset(ld, 0, sizeof *ld);
+}
+
+static struct menu_entry *new_entry(struct menu *m)
+{
+ struct menu_entry *me;
+
+ //mp("enter, call from menu %s", m->label);
+
+ if (m->nentries >= m->nentries_space) {
+ if (!m->nentries_space)
+ m->nentries_space = 1;
+ else
+ m->nentries_space <<= 1;
+
+ m->menu_entries = realloc(m->menu_entries, m->nentries_space *
+ sizeof(struct menu_entry *));
+ }
+
+ me = calloc(1, sizeof(struct menu_entry));
+ me->menu = m;
+ me->entry = m->nentries;
+ m->menu_entries[m->nentries++] = me;
+ *all_entries_end = me;
+ all_entries_end = &me->next;
+
+ return me;
+}
+
+static void consider_for_hotkey(struct menu *m, struct menu_entry *me)
+{
+ const char *p = strchr(me->displayname, '^');
+
+ if (me->action != MA_DISABLED) {
+ if (p && p[1]) {
+ unsigned char hotkey = p[1] & ~0x20;
+ if (!m->menu_hotkeys[hotkey]) {
+ me->hotkey = hotkey;
+ m->menu_hotkeys[hotkey] = me;
+ }
+ }
+ }
+}
+
+static void record(struct menu *m, struct labeldata *ld, const char *append)
+{
+ int i;
+ struct menu_entry *me;
+ const struct syslinux_ipappend_strings *ipappend;
+
+ if (!ld->label)
+ return; /* Nothing defined */
+
+ /* Hidden entries are recorded on a special "hidden menu" */
+ if (ld->menuhide)
+ m = hide_menu;
+
+ char ipoptions[4096], *ipp;
+ const char *a;
+ char *s;
+
+ me = new_entry(m);
+
+ me->displayname = ld->menulabel
+ ? refstr_get(ld->menulabel) : refstr_get(ld->label);
+ me->label = refstr_get(ld->label);
+ me->passwd = refstr_get(ld->passwd);
+ me->helptext = ld->helptext;
+ me->hotkey = 0;
+ me->action = ld->action ? ld->action : MA_CMD;
+ me->save = ld->save ? (ld->save > 0) : m->save;
+
+ if (ld->menuindent) {
+ const char *dn;
+
+ rsprintf(&dn, "%*s%s", ld->menuindent, "", me->displayname);
+ refstr_put(me->displayname);
+ me->displayname = dn;
+ }
+
+ if (ld->menuseparator) {
+ refstr_put(me->displayname);
+ me->displayname = refstr_get(empty_string);
+ }
+
+ if (ld->menuseparator || ld->menudisabled) {
+ me->action = MA_DISABLED;
+ refstr_put(me->label);
+ me->label = NULL;
+ refstr_put(me->passwd);
+ me->passwd = NULL;
+ }
+
+ if (ld->menulabel)
+ consider_for_hotkey(m, me);
+
+ switch (me->action) {
+ case MA_CMD:
+ ipp = ipoptions;
+ *ipp = '\0';
+
+ if (ld->initrd)
+ ipp += sprintf(ipp, " initrd=%s", ld->initrd);
+
+ if (ld->ipappend) {
+ ipappend = syslinux_ipappend_strings();
+ for (i = 0; i < ipappend->count; i++) {
+ if ((ld->ipappend & (1U << i)) && ipappend->ptr[i])
+ ipp += sprintf(ipp, " %s", ipappend->ptr[i]);
+ }
+ }
+
+ a = ld->append;
+ if (!a)
+ a = append;
+ if (!a || (a[0] == '-' && !a[1]))
+ a = "";
+ s = a[0] ? " " : "";
+
+ if (ld->type == KT_KERNEL)
+ rsprintf(&me->cmdline, "%s%s%s%s", ld->kernel, s, a, ipoptions);
+ else
+ rsprintf(&me->cmdline, ".%s %s%s%s%s",
+ kernel_types[ld->type], ld->kernel, s, a, ipoptions);
+ mp("type = %s, cmd = %s", kernel_types[ld->type], me->cmdline);
+ break;
+
+ case MA_GOTO_UNRES:
+ case MA_EXIT_UNRES:
+ me->cmdline = refstr_get(ld->kernel);
+ break;
+
+ case MA_GOTO:
+ case MA_EXIT:
+ me->submenu = ld->submenu;
+ break;
+
+ default:
+ break;
+ }
+
+ if (ld->menudefault && me->action == MA_CMD)
+ m->defentry = m->nentries - 1;
+
+ clear_label_data(ld);
+}
+
+static struct menu *begin_submenu(const char *tag)
+{
+ struct menu_entry *me;
+
+ if (!tag[0])
+ tag = NULL;
+
+ me = new_entry(current_menu);
+ me->displayname = refstrdup(tag);
+ return new_menu(current_menu, me, refstr_get(me->displayname));
+}
+
+static struct menu *end_submenu(void)
+{
+ return current_menu->parent ? current_menu->parent : current_menu;
+}
+
+static struct menu_entry *find_label(const char *str)
+{
+ const char *p;
+ struct menu_entry *me;
+ int pos;
+
+ p = str;
+ while (*p && !my_isspace(*p))
+ p++;
+
+ /* p now points to the first byte beyond the kernel name */
+ pos = p - str;
+
+ for (me = all_entries; me; me = me->next) {
+ if (!strncmp(str, me->label, pos) && !me->label[pos])
+ return me;
+ }
+
+ return NULL;
+}
+
+static const char *unlabel(const char *str)
+{
+ /* Convert a CLI-style command line to an executable command line */
+ const char *p;
+ const char *q;
+ struct menu_entry *me;
+ int pos;
+
+ p = str;
+ while (*p && !my_isspace(*p))
+ p++;
+
+ /* p now points to the first byte beyond the kernel name */
+ pos = p - str;
+
+ for (me = all_entries; me; me = me->next) {
+ if (!strncmp(str, me->label, pos) && !me->label[pos]) {
+ /* Found matching label */
+ rsprintf(&q, "%s%s", me->cmdline, p);
+ refstr_put(str);
+ return q;
+ }
+ }
+
+ return str;
+}
+
+static const char *refdup_word(char **p)
+{
+ char *sp = *p;
+ char *ep = sp;
+
+ while (*ep && !my_isspace(*ep))
+ ep++;
+
+ *p = ep;
+ return refstrndup(sp, ep - sp);
+}
+
+int my_isxdigit(char c)
+{
+ unsigned int uc = c;
+
+ return (uc - '0') < 10 || ((uc | 0x20) - 'a') < 6;
+}
+
+unsigned int hexval(char c)
+{
+ unsigned char uc = c | 0x20;
+ unsigned int v;
+
+ v = uc - '0';
+ if (v < 10)
+ return v;
+
+ return uc - 'a' + 10;
+}
+
+unsigned int hexval2(const char *p)
+{
+ return (hexval(p[0]) << 4) + hexval(p[1]);
+}
+
+uint32_t parse_argb(char **p)
+{
+ char *sp = *p;
+ char *ep;
+ uint32_t argb;
+ size_t len, dl;
+
+ if (*sp == '#')
+ sp++;
+
+ ep = sp;
+
+ while (my_isxdigit(*ep))
+ ep++;
+
+ *p = ep;
+ len = ep - sp;
+
+ switch (len) {
+ case 3: /* #rgb */
+ argb =
+ 0xff000000 +
+ (hexval(sp[0]) * 0x11 << 16) +
+ (hexval(sp[1]) * 0x11 << 8) + (hexval(sp[2]) * 0x11);
+ break;
+ case 4: /* #argb */
+ argb =
+ (hexval(sp[0]) * 0x11 << 24) +
+ (hexval(sp[1]) * 0x11 << 16) +
+ (hexval(sp[2]) * 0x11 << 8) + (hexval(sp[3]) * 0x11);
+ break;
+ case 6: /* #rrggbb */
+ case 9: /* #rrrgggbbb */
+ case 12: /* #rrrrggggbbbb */
+ dl = len / 3;
+ argb =
+ 0xff000000 +
+ (hexval2(sp + 0) << 16) +
+ (hexval2(sp + dl) << 8) + hexval2(sp + dl * 2);
+ break;
+ case 8: /* #aarrggbb */
+ /* #aaarrrgggbbb is indistinguishable from #rrrrggggbbbb,
+ assume the latter is a more common format */
+ case 16: /* #aaaarrrrggggbbbb */
+ dl = len / 4;
+ argb =
+ (hexval2(sp + 0) << 24) +
+ (hexval2(sp + dl) << 16) +
+ (hexval2(sp + dl * 2) << 8) + hexval2(sp + dl * 3);
+ break;
+ default:
+ argb = 0xffff0000; /* Bright red (error indication) */
+ break;
+ }
+
+ return argb;
+}
+
+/*
+ * Parser state. This is global so that including multiple
+ * files work as expected, which is that everything works the
+ * same way as if the files had been concatenated together.
+ */
+//static const char *append = NULL;
+extern char *append;
+//static unsigned int ipappend = 0;
+unsigned int ipappend = 0;
+static struct labeldata ld;
+
+static int parse_one_config(const char *filename);
+
+static char *is_kernel_type(char *cmdstr, enum kernel_type *type)
+{
+ const char *const *p;
+ char *q;
+ enum kernel_type t = KT_NONE;
+
+ for (p = kernel_types; *p; p++, t++) {
+ if ((q = looking_at(cmdstr, *p))) {
+ *type = t;
+ return q;
+ }
+ }
+
+ return NULL;
+}
+
+static char *is_message_name(char *cmdstr, enum message_number *msgnr)
+{
+ char *q;
+ enum message_number i;
+
+ for (i = 0; i < MSG_COUNT; i++) {
+ if ((q = looking_at(cmdstr, messages[i].name))) {
+ *msgnr = i;
+ return q;
+ }
+ }
+
+ return NULL;
+}
+
+static char *is_fkey(char *cmdstr, int *fkeyno)
+{
+ char *q;
+ int no;
+
+ if ((cmdstr[0] | 0x20) != 'f')
+ return NULL;
+
+ no = strtoul(cmdstr + 1, &q, 10);
+ if (!my_isspace(*q))
+ return NULL;
+
+ if (no < 0 || no > 12)
+ return NULL;
+
+ *fkeyno = (no == 0) ? 10 : no - 1;
+ return q;
+}
+
+static void parse_config_file(FILE * f)
+{
+ char line[MAX_LINE], *p, *ep, ch;
+ enum kernel_type type;
+ enum message_number msgnr;
+ int fkeyno;
+ struct menu *m = current_menu;
+
+ while (fgets(line, sizeof line, f)) {
+ p = strchr(line, '\r');
+ if (p)
+ *p = '\0';
+ p = strchr(line, '\n');
+ if (p)
+ *p = '\0';
+
+ p = skipspace(line);
+
+ if (looking_at(p, "menu")) {
+
+ p = skipspace(p + 4);
+
+ if (looking_at(p, "label")) {
+ if (ld.label) {
+ refstr_put(ld.menulabel);
+ ld.menulabel = refstrdup(skipspace(p + 5));
+ } else if (m->parent_entry) {
+ refstr_put(m->parent_entry->displayname);
+ m->parent_entry->displayname = refstrdup(skipspace(p + 5));
+ consider_for_hotkey(m->parent, m->parent_entry);
+ if (!m->title[0]) {
+ /* MENU LABEL -> MENU TITLE on submenu */
+ refstr_put(m->title);
+ m->title = strip_caret(m->parent_entry->displayname);
+ }
+ }
+ } else if (looking_at(p, "title")) {
+ refstr_put(m->title);
+ m->title = refstrdup(skipspace(p + 5));
+ if (m->parent_entry) {
+ /* MENU TITLE -> MENU LABEL on submenu */
+ if (m->parent_entry->displayname == m->label) {
+ refstr_put(m->parent_entry->displayname);
+ m->parent_entry->displayname = refstr_get(m->title);
+ }
+ }
+ } else if (looking_at(p, "default")) {
+ if (ld.label) {
+ ld.menudefault = 1;
+ } else if (m->parent_entry) {
+ m->parent->defentry = m->parent_entry->entry;
+ }
+ } else if (looking_at(p, "hide")) {
+ ld.menuhide = 1;
+ } else if (looking_at(p, "passwd")) {
+ if (ld.label) {
+ refstr_put(ld.passwd);
+ ld.passwd = refstrdup(skipspace(p + 6));
+ } else if (m->parent_entry) {
+ refstr_put(m->parent_entry->passwd);
+ m->parent_entry->passwd = refstrdup(skipspace(p + 6));
+ }
+ } else if (looking_at(p, "shiftkey")) {
+ shiftkey = 1;
+ } else if (looking_at(p, "save")) {
+ menusave = true;
+ if (ld.label)
+ ld.save = 1;
+ else
+ m->save = true;
+ } else if (looking_at(p, "nosave")) {
+ if (ld.label)
+ ld.save = -1;
+ else
+ m->save = false;
+ } else if (looking_at(p, "onerror")) {
+ refstr_put(m->onerror);
+ m->onerror = refstrdup(skipspace(p + 7));
+ onerrorlen = strlen(m->onerror);
+ } else if (looking_at(p, "master")) {
+ p = skipspace(p + 6);
+ if (looking_at(p, "passwd")) {
+ refstr_put(m->menu_master_passwd);
+ m->menu_master_passwd = refstrdup(skipspace(p + 6));
+ }
+ } else if ((ep = looking_at(p, "include"))) {
+ goto do_include;
+ } else if ((ep = looking_at(p, "background"))) {
+ p = skipspace(ep);
+ refstr_put(m->menu_background);
+ m->menu_background = refdup_word(&p);
+ } else if ((ep = looking_at(p, "hidden"))) {
+ hiddenmenu = 1;
+ } else if ((ep = is_message_name(p, &msgnr))) {
+ refstr_put(m->messages[msgnr]);
+ m->messages[msgnr] = refstrdup(skipspace(ep));
+ } else if ((ep = looking_at(p, "color")) ||
+ (ep = looking_at(p, "colour"))) {
+ int i;
+ struct color_table *cptr;
+ p = skipspace(ep);
+ cptr = m->color_table;
+ for (i = 0; i < menu_color_table_size; i++) {
+ if ((ep = looking_at(p, cptr->name))) {
+ p = skipspace(ep);
+ if (*p) {
+ if (looking_at(p, "*")) {
+ p++;
+ } else {
+ refstr_put(cptr->ansi);
+ cptr->ansi = refdup_word(&p);
+ }
+
+ p = skipspace(p);
+ if (*p) {
+ if (looking_at(p, "*"))
+ p++;
+ else
+ cptr->argb_fg = parse_argb(&p);
+
+ p = skipspace(p);
+ if (*p) {
+ if (looking_at(p, "*"))
+ p++;
+ else
+ cptr->argb_bg = parse_argb(&p);
+
+ /* Parse a shadow mode */
+ p = skipspace(p);
+ ch = *p | 0x20;
+ if (ch == 'n') /* none */
+ cptr->shadow = SHADOW_NONE;
+ else if (ch == 's') /* std, standard */
+ cptr->shadow = SHADOW_NORMAL;
+ else if (ch == 'a') /* all */
+ cptr->shadow = SHADOW_ALL;
+ else if (ch == 'r') /* rev, reverse */
+ cptr->shadow = SHADOW_REVERSE;
+ }
+ }
+ }
+ break;
+ }
+ cptr++;
+ }
+ } else if ((ep = looking_at(p, "msgcolor")) ||
+ (ep = looking_at(p, "msgcolour"))) {
+ unsigned int fg_mask = MSG_COLORS_DEF_FG;
+ unsigned int bg_mask = MSG_COLORS_DEF_BG;
+ enum color_table_shadow shadow = MSG_COLORS_DEF_SHADOW;
+
+ p = skipspace(ep);
+ if (*p) {
+ if (!looking_at(p, "*"))
+ fg_mask = parse_argb(&p);
+
+ p = skipspace(p);
+ if (*p) {
+ if (!looking_at(p, "*"))
+ bg_mask = parse_argb(&p);
+
+ p = skipspace(p);
+ switch (*p | 0x20) {
+ case 'n':
+ shadow = SHADOW_NONE;
+ break;
+ case 's':
+ shadow = SHADOW_NORMAL;
+ break;
+ case 'a':
+ shadow = SHADOW_ALL;
+ break;
+ case 'r':
+ shadow = SHADOW_REVERSE;
+ break;
+ default:
+ /* go with default */
+ break;
+ }
+ }
+ }
+ set_msg_colors_global(m->color_table, fg_mask, bg_mask, shadow);
+ } else if (looking_at(p, "separator")) {
+ record(m, &ld, append);
+ ld.label = refstr_get(empty_string);
+ ld.menuseparator = 1;
+ record(m, &ld, append);
+ } else if (looking_at(p, "disable") || looking_at(p, "disabled")) {
+ ld.menudisabled = 1;
+ } else if (looking_at(p, "indent")) {
+ ld.menuindent = atoi(skipspace(p + 6));
+ } else if (looking_at(p, "begin")) {
+ record(m, &ld, append);
+ m = current_menu = begin_submenu(skipspace(p + 5));
+ } else if (looking_at(p, "end")) {
+ record(m, &ld, append);
+ m = current_menu = end_submenu();
+ } else if (looking_at(p, "quit")) {
+ if (ld.label)
+ ld.action = MA_QUIT;
+ } else if (looking_at(p, "goto")) {
+ if (ld.label) {
+ ld.action = MA_GOTO_UNRES;
+ refstr_put(ld.kernel);
+ ld.kernel = refstrdup(skipspace(p + 4));
+ }
+ } else if (looking_at(p, "exit")) {
+ p = skipspace(p + 4);
+ if (ld.label && m->parent) {
+ if (*p) {
+ /* This is really just a goto, except for the marker */
+ ld.action = MA_EXIT_UNRES;
+ refstr_put(ld.kernel);
+ ld.kernel = refstrdup(p);
+ } else {
+ ld.action = MA_EXIT;
+ ld.submenu = m->parent;
+ }
+ }
+ } else if (looking_at(p, "start")) {
+ start_menu = m;
+ } else {
+ /* Unknown, check for layout parameters */
+ enum parameter_number mp;
+ for (mp = 0; mp < NPARAMS; mp++) {
+ if ((ep = looking_at(p, mparm[mp].name))) {
+ m->mparm[mp] = atoi(skipspace(ep));
+ break;
+ }
+ }
+ }
+ }
+ /* feng: menu handling end */
+ else if (looking_at(p, "text")) {
+
+ /* loop till we fined the "endtext" */
+ enum text_cmd {
+ TEXT_UNKNOWN,
+ TEXT_HELP
+ } cmd = TEXT_UNKNOWN;
+ int len = ld.helptext ? strlen(ld.helptext) : 0;
+ int xlen;
+
+ p = skipspace(p + 4);
+
+ if (looking_at(p, "help"))
+ cmd = TEXT_HELP;
+
+ while (fgets(line, sizeof line, f)) {
+ p = skipspace(line);
+ if (looking_at(p, "endtext"))
+ break;
+
+ xlen = strlen(line);
+
+ switch (cmd) {
+ case TEXT_UNKNOWN:
+ break;
+ case TEXT_HELP:
+ ld.helptext = realloc(ld.helptext, len + xlen + 1);
+ memcpy(ld.helptext + len, line, xlen + 1);
+ len += xlen;
+ break;
+ }
+ }
+ } else if ((ep = is_fkey(p, &fkeyno))) {
+ p = skipspace(ep);
+ if (m->fkeyhelp[fkeyno].textname) {
+ refstr_put(m->fkeyhelp[fkeyno].textname);
+ m->fkeyhelp[fkeyno].textname = NULL;
+ }
+ if (m->fkeyhelp[fkeyno].background) {
+ refstr_put(m->fkeyhelp[fkeyno].background);
+ m->fkeyhelp[fkeyno].background = NULL;
+ }
+
+ refstr_put(m->fkeyhelp[fkeyno].textname);
+ m->fkeyhelp[fkeyno].textname = refdup_word(&p);
+ if (*p) {
+ p = skipspace(p);
+ m->fkeyhelp[fkeyno].background = refdup_word(&p);
+ }
+ } else if ((ep = looking_at(p, "include"))) {
+do_include:
+ {
+ const char *file;
+ p = skipspace(ep);
+ file = refdup_word(&p);
+ p = skipspace(p);
+ if (*p) {
+ record(m, &ld, append);
+ m = current_menu = begin_submenu(p);
+ parse_one_config(file);
+ record(m, &ld, append);
+ m = current_menu = end_submenu();
+ } else {
+ parse_one_config(file);
+ }
+ refstr_put(file);
+ }
+ } else if (looking_at(p, "append")) {
+ const char *a = refstrdup(skipspace(p + 6));
+ if (ld.label) {
+ refstr_put(ld.append);
+ ld.append = a;
+ } else {
+ refstr_put(append);
+ append = a;
+ }
+ //mp("we got a append: %s", a);
+ } else if (looking_at(p, "initrd")) {
+ const char *a = refstrdup(skipspace(p + 6));
+ if (ld.label) {
+ refstr_put(ld.initrd);
+ ld.initrd = a;
+ } else {
+ /* Ignore */
+ }
+ } else if (looking_at(p, "label")) {
+ p = skipspace(p + 5);
+ /* when first time see "label", it will not really record anything */
+ record(m, &ld, append);
+ ld.label = refstrdup(p);
+ ld.kernel = refstrdup(p);
+ /* feng: this is the default type for all */
+ ld.type = KT_KERNEL;
+ ld.passwd = NULL;
+ ld.append = NULL;
+ ld.initrd = NULL;
+ ld.menulabel = NULL;
+ ld.helptext = NULL;
+ ld.ipappend = ipappend;
+ ld.menudefault = ld.menuhide = ld.menuseparator =
+ ld.menudisabled = ld.menuindent = 0;
+ } else if ((ep = is_kernel_type(p, &type))) {
+ if (ld.label) {
+ refstr_put(ld.kernel);
+ ld.kernel = refstrdup(skipspace(ep));
+ ld.type = type;
+ //mp("got a kernel: %s, type = %d", ld.kernel, ld.type);
+ }
+ } else if (looking_at(p, "timeout")) {
+ m->timeout = (atoi(skipspace(p + 7)) * CLK_TCK + 9) / 10;
+ } else if (looking_at(p, "totaltimeout")) {
+ totaltimeout = (atoll(skipspace(p + 13)) * CLK_TCK + 9) / 10;
+ } else if (looking_at(p, "ontimeout")) {
+ m->ontimeout = refstrdup(skipspace(p + 9));
+ } else if (looking_at(p, "allowoptions")) {
+ m->allowedit = !!atoi(skipspace(p + 12));
+ } else if (looking_at(p, "ipappend")) {
+ if (ld.label)
+ ld.ipappend = atoi(skipspace(p + 8));
+ else
+ ipappend = atoi(skipspace(p + 8));
+ } else if (looking_at(p, "default")) {
+ /* default could be a kernel image or another label */
+ refstr_put(globaldefault);
+ globaldefault = refstrdup(skipspace(p + 7));
+ } else if (looking_at(p, "ui")) {
+ has_ui = 1;
+ }
+
+ /*
+ * subset 1: pc_opencmd
+ * display/font/kbdmap are rather similar, open a file then do sth
+ */
+ else if (looking_at(p, "display")) {
+
+ } else if (looking_at(p, "font")) {
+
+ } else if (looking_at(p, "kbdmap")) {
+
+ }
+ /*
+ * subset 2: pc_setint16
+ * set a global flag
+ */
+ else if (looking_at(p, "implicit")) {
+ allowimplicit = atoi(skipspace(p + 8));
+ } else if (looking_at(p, "prompt")) {
+ forceprompt = atoi(skipspace(p + 8));
+ } else if (looking_at(p, "console")) {
+ displaycon = atoi(skipspace(p + 7));
+ } else if (looking_at(p, "allowoptions")) {
+ allowoptions = atoi(skipspace(p + 12));
+ } else if (looking_at(p, "noescape")) {
+ noescape = atoi(skipspace(p + 8));
+ } else if (looking_at(p, "nocomplete")) {
+ nocomplete = atoi(skipspace(p + 10));
+ } else if (looking_at(p, "nohalt")) {
+ nohalt = atoi(skipspace(p + 8));
+ }
+
+ /* serial setting, bps, flow control */
+ else if (looking_at(p, "serial")) {
+ /* core/conio.inc
+ * should be able to find some code in com32
+ */
+
+ } else if (looking_at(p, "say")) {
+ printf("%s\n", p + 4);
+ }
+ }
+}
+
+static int parse_one_config(const char *filename)
+{
+ FILE *f;
+ int i;
+
+ /*
+ if (!strcmp(filename, "~"))
+ filename = syslinux_config_file();
+ */
+
+ f = fopen(filename, "r");
+ if (f)
+ goto config_found;
+
+ /* force to use hard coded config file name */
+ f = fopen("extlinux.conf", "r");
+ if (f)
+ goto config_found;
+
+ f = fopen("isolinux.cfg", "r");
+ if (f)
+ goto config_found;
+
+ return -1;
+config_found:
+ parse_config_file(f);
+ fclose(f);
+ return 0;
+}
+
+static void resolve_gotos(void)
+{
+ struct menu_entry *me;
+ struct menu *m;
+
+ for (me = all_entries; me; me = me->next) {
+ if (me->action == MA_GOTO_UNRES || me->action == MA_EXIT_UNRES) {
+ m = find_menu(me->cmdline);
+ refstr_put(me->cmdline);
+ me->cmdline = NULL;
+ if (m) {
+ me->submenu = m;
+ me->action--; /* Drop the _UNRES */
+ } else {
+ me->action = MA_DISABLED;
+ }
+ }
+ }
+}
+
+static void dump_menu(struct menu *menu)
+{
+ mp("will dump menu for %s:", menu->label);
+ printf("entries num: %d\n", menu->nentries);
+ printf("defentry: %d, nam = %s\n",
+ menu->defentry, menu->menu_entries[menu->defentry]->label);
+ printf("save: %d\n", menu->save);
+ //printf("", menu->);
+ //printf("", menu->);
+ //printf("", menu->);
+}
+
+void parse_configs(char **argv)
+{
+ const char *filename;
+ struct menu *m;
+ struct menu_entry *me;
+ char *cmdline;
+ mp("enter");
+
+ empty_string = refstrdup("");
+
+ /* feng: reset current menu_list and entry list */
+ menu_list = NULL;
+ all_entries = NULL;
+
+ /* Initialize defaults for the root and hidden menus */
+ hide_menu = new_menu(NULL, NULL, refstrdup(".hidden"));
+ root_menu = new_menu(NULL, NULL, refstrdup(".top"));
+ start_menu = root_menu;
+
+ /* Other initialization */
+ memset(&ld, 0, sizeof(struct labeldata));
+
+ /* Actually process the files */
+ current_menu = root_menu;
+
+ if (!argv || !*argv) {
+ parse_one_config("~");
+ } else {
+ while ((filename = *argv++)) {
+ mp("Parsing config: %s", filename);
+ parse_one_config(filename);
+ }
+ }
+
+ /* On final EOF process the last label statement */
+ record(current_menu, &ld, append);
+
+ /* Common postprocessing */
+ resolve_gotos();
+
+ /* Handle global default */
+ //if (has_ui && globaldefault) {
+ if (globaldefault) {
+ mp("gloabldefault = %s", globaldefault);
+ me = find_label(globaldefault);
+ if (me && me->menu != hide_menu) {
+ me->menu->defentry = me->entry;
+ start_menu = me->menu;
+ default_menu = me->menu;
+ }
+ }
+
+ /* If "menu save" is active, let the ADV override the global default */
+ if (menusave) {
+ size_t len;
+ const char *lbl = syslinux_getadv(ADV_MENUSAVE, &len);
+ char *lstr;
+ if (lbl && len) {
+ lstr = refstr_alloc(len);
+ memcpy(lstr, lbl, len); /* refstr_alloc() adds the final null */
+ me = find_label(lstr);
+ if (me && me->menu != hide_menu) {
+ me->menu->defentry = me->entry;
+ start_menu = me->menu;
+ }
+ refstr_put(lstr);
+ }
+ }
+
+ /* Final per-menu initialization, with all labels known */
+ for (m = menu_list; m; m = m->next) {
+ m->curentry = m->defentry; /* All menus start at their defaults */
+
+ if (m->ontimeout)
+ m->ontimeout = unlabel(m->ontimeout);
+ if (m->onerror)
+ m->onerror = unlabel(m->onerror);
+ }
+}
diff --git a/com32/elflink/ldlinux/refstr.c b/com32/elflink/ldlinux/refstr.c
new file mode 100644
index 00000000..f9d98e11
--- /dev/null
+++ b/com32/elflink/ldlinux/refstr.c
@@ -0,0 +1,106 @@
+/* ----------------------------------------------------------------------- *
+ *
+ * Copyright 2008 H. Peter Anvin - All Rights Reserved
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+ * Boston MA 02110-1301, USA; either version 2 of the License, or
+ * (at your option) any later version; incorporated herein by reference.
+ *
+ * ----------------------------------------------------------------------- */
+
+/*
+ * refstr.c
+ *
+ * Simple reference-counted strings
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include <sys/module.h>
+#include "refstr.h"
+
+/* Allocate space for a refstring of len bytes, plus final null */
+/* The final null is inserted in the string; the rest is uninitialized. */
+char *refstr_alloc(size_t len)
+{
+ char *r = malloc(sizeof(unsigned int) + len + 1);
+ if (!r)
+ return NULL;
+ *(unsigned int *)r = 1;
+ r += sizeof(unsigned int);
+ r[len] = '\0';
+ return r;
+}
+
+const char *refstrndup(const char *str, size_t len)
+{
+ char *r;
+
+ if (!str)
+ return NULL;
+
+ len = strnlen(str, len);
+ r = refstr_alloc(len);
+ if (r)
+ memcpy(r, str, len);
+ return r;
+}
+
+const char *refstrdup(const char *str)
+{
+ char *r;
+ size_t len;
+
+ if (!str)
+ return NULL;
+
+ len = strlen(str);
+ r = refstr_alloc(len);
+ if (r)
+ memcpy(r, str, len);
+ return r;
+}
+
+int vrsprintf(const char **bufp, const char *fmt, va_list ap)
+{
+ va_list ap1;
+ int len;
+ char *p;
+
+ va_copy(ap1, ap);
+ len = vsnprintf(NULL, 0, fmt, ap1);
+ va_end(ap1);
+
+ *bufp = p = refstr_alloc(len);
+ if (!p)
+ return -1;
+
+ return vsnprintf(p, len + 1, fmt, ap);
+}
+
+int rsprintf(const char **bufp, const char *fmt, ...)
+{
+ int rv;
+ va_list ap;
+
+ va_start(ap, fmt);
+ rv = vrsprintf(bufp, fmt, ap);
+ va_end(ap);
+
+ return rv;
+}
+
+void refstr_put(const char *r)
+{
+ unsigned int *ref;
+
+ if (r) {
+ ref = (unsigned int *)r - 1;
+
+ if (!--*ref)
+ free(ref);
+ }
+}
diff --git a/com32/elflink/ldlinux/refstr.h b/com32/elflink/ldlinux/refstr.h
new file mode 100644
index 00000000..7001d407
--- /dev/null
+++ b/com32/elflink/ldlinux/refstr.h
@@ -0,0 +1,40 @@
+/* ----------------------------------------------------------------------- *
+ *
+ * Copyright 2008 H. Peter Anvin - All Rights Reserved
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+ * Boston MA 02110-1301, USA; either version 2 of the License, or
+ * (at your option) any later version; incorporated herein by reference.
+ *
+ * ----------------------------------------------------------------------- */
+
+/*
+ * refstr.h
+ *
+ * Simple reference-counted strings
+ */
+
+#ifndef REFSTR_H
+#define REFSTR_H
+
+#include <stddef.h>
+#include <stdarg.h>
+
+static inline __attribute__ ((always_inline))
+const char *refstr_get(const char *r)
+{
+ if (r)
+ ((unsigned int *)r)[-1]++;
+ return r;
+}
+
+void refstr_put(const char *);
+char *refstr_alloc(size_t);
+const char *refstrdup(const char *);
+const char *refstrndup(const char *, size_t);
+int rsprintf(const char **, const char *, ...);
+int vrsprintf(const char **, const char *, va_list);
+
+#endif