Merge branch 'lwip' into elflink
authorMatt Fleming <matt.fleming@intel.com>
Mon, 25 Feb 2013 15:25:16 +0000 (15:25 +0000)
committerMatt Fleming <matt.fleming@intel.com>
Tue, 26 Feb 2013 11:29:04 +0000 (11:29 +0000)
Welcome to Syslinux 5.10.

Conflicts:
NEWS
com32/lib/Makefile
com32/lib/sys/open.c
com32/lib/syslinux/ipappend.c
com32/modules/Makefile
com32/modules/prdhcp.c
core/Makefile
core/cmdline.inc
core/com32.inc
core/comboot.inc
core/configinit.inc
core/fs/chdir.c
core/fs/fs.c
core/fs/pxe/dnsresolv.c
core/fs/pxe/pxe.c
core/fs/pxe/pxe.h
core/idle.c
core/include/ctype.h
core/init.inc
core/mem/init.c
core/parseconfig.inc
core/runkernel.inc
core/syslinux.ld
core/ui.inc
doc/comboot.txt
version

52 files changed:
1  2 
NEWS
com32/chain/utility.c
com32/elflink/ldlinux/chainboot.c
com32/elflink/ldlinux/execute.c
com32/elflink/ldlinux/ldlinux.c
com32/elflink/ldlinux/readconfig.c
com32/gfxboot/gfxboot.c
com32/include/linux/list.h
com32/include/syslinux/config.h
com32/include/syslinux/pxe_api.h
com32/lib/Makefile
com32/lib/errno.c
com32/lib/sys/file.h
com32/lib/sys/open.c
com32/lib/syslinux/ipappend.c
com32/lib/syslinux/runimage.c
com32/menu/menumain.c
com32/menu/readconfig.c
com32/modules/Makefile
core/Makefile
core/callback.inc
core/diskfs.inc
core/errno.c
core/extern.inc
core/fs/chdir.c
core/fs/diskio.c
core/fs/fs.c
core/fs/lib/searchconfig.c
core/fs/pxe/dhcp_option.c
core/fs/pxe/dnsresolv.c
core/fs/pxe/http.c
core/fs/pxe/pxe.c
core/fs/pxe/pxe.h
core/fs/readdir.c
core/idle.c
core/include/core.h
core/include/fs.h
core/init.c
core/isolinux.asm
core/layout.inc
core/mem/free.c
core/mem/malloc.c
core/mem/malloc.h
core/pm.inc
core/pmapi.c
core/pxelinux.asm
core/sysappend.c
core/syslinux.ld
doc/pxelinux.txt
doc/syslinux.txt
mk/syslinux.mk
version

diff --cc NEWS
--- 1/NEWS
--- 2/NEWS
+++ b/NEWS
@@@ -2,70 -2,22 +2,84 @@@ Starting with 1.47, changes marked wit
  or EXTLINUX apply to that specific program only; other changes apply
  to all derivatives.
  
 -Changes in 4.10:
++Changes in 5.10:
+       * PXELINUX: An entirely new network implementation based on
+         the lwIP embedded TCP/IP stack.  As a result, plain PXELINUX
+         can now support HTTP and FTP without gPXE/iPXE.  ls/readdir
+         functionality is supported over HTTP with an indexing
+         webserver, or over FTP with most common FTP servers.
+       * Rename the "ipappend" option to "sysappend" ("ipappend" is
+         still accepted as an alias) and make it available for all
+         derivatives.  Add additional strings derived from the system
+         DMI/SMBIOS information if available.
+       * "sysappend" strings are also sent as http cookies, with the
+         prefix _Syslinux_ added, on all http transfers.  This can be
+         overridden with the SENDCOOKIES configuration file command.
 +Changes in 5.01:
 +      * txt/: A new AsciiDoc documentation set (work-in-progress)
 +        (Gene Cumm).
 +      * core: Fix a bug in the realloc() implementation that caused
 +        machines to appear to run out of free memory.
 +      * ldlinux: Fix multiple buffer overflows in cmdline parsing
 +        code that resulted in files failing to run and cmdlines
 +        being truncated.
 +      * core: Fix debug build by tagging __bad_SEG() with __export.
 +      * com32: Restrict library filenames to 8.3 format.
 +      * EXTLINUX: Fix installation and subdirectory patching.
 +      * ISOLINUX: Fix booting isohybrid images that are over 32K.
 +      * com32: Strip modules to reduce their size.
 +      * XFS: Implement directory block cache and fix
 +          shortform-directory lookup (Paulo Alcantara).
 +
 +Changes in 5.00:
 +      * com32: Switched from the COM32 object format to ELF as it is
 +        a much more powerful format that allows undefined symbols to
 +        be resolved at runtime and dynamic loading of module
 +        dependencies, which means modules now become shared object
 +        files instead of statically linked binaries - reducing both
 +        disk space and runtime memory consumption.
 +      * core: Split non-core functionality into ldlinux.c32, which
 +        is an ELF module loaded by the core that contains everything
 +        the core doesn't require to boot the system, e.g. config
 +        parser, command-line interface, etc.
 +      * Replaced __intcall() calls with direct function calls now
 +        that we can resolve undefined symbols at runtime, thanks to
 +        the ELF object support. Now that we no longer need to go
 +        through the 16-bit interrupt mechanism we can make full use
 +        of the 32-bit execution environment. This change required
 +        reimplementing lots of the 16-bit assembly code from core/
 +        in C.
 +      * com32: __com32.cs_bounce is gone now we always run in a
 +        32-bit environment once we execute ldlinux.c32.
 +      * ldlinux: A new "PATH" directive was added to the ldlinux.c32
 +        config parser that specifies a colon-separated list of
 +        directories to search when attempting to load modules.
 +      * ALL: Delete all references to/code for 16-bit COMBOOT files.
 +        COMBOOT files (.cbt and .com) are no longer supported under
 +        Syslinux.
 +
  Changes in 4.06:
        * Support for NTFS, by Paulo Alcantara.
 +      * EXTLINUX: more robust device detection, allow user to override.
 +      * kontron_wdt.c32: Add a new module to enable the hardware
 +        watchdog of some Kontron boards. It allows enabling the watchdog
 +        and then booting a given image.
 +      * HDT updated, and now can display images regarding some detection
 +        steps. Add postexec command to run a particular entry after
 +        HDT's execution, add silent option and various fixes.
 +      * ifcpu.c32: Detect hypervisor presence.
 +      * lua.c32: Add dhcp support and support for native Syslinux
 +        functions syslinux_config(), syslinux_ipappend_strings() and
 +        syslinux_reboot().
 +      * isohybrid: Workaround for various EFI systems.
 +      * pxechn.c32, a PXE NBP chainloader.  More versatile alternative
 +        to pxechain.com and resolves the PXELINUX -> WDS issue with
 +        Microsoft Windows Server 2008R2 (Gene Cumm).
 +      * btrfs: Fix booting off of a subvolume.
 +      * com32: Add device tree support.
 +      * SYSLINUX: Fix relative paths for VFAT. The CONFIG and APPEND
 +        directives now support entirely relative paths.
  
  Changes in 4.05:
        * HDT updated, and now supports uploading data to a TFTP
@@@ -1,34 -1,4 +1,35 @@@
 +/* ----------------------------------------------------------------------- *
 + *
 + *   Copyright 2003-2009 H. Peter Anvin - All Rights Reserved
 + *   Copyright 2009-2010 Intel Corporation; author: H. Peter Anvin
 + *   Copyright 2010 Shao Miller
 + *   Copyright 2010-2012 Michal Soltys
 + *
 + *   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.
 + *
 + * ----------------------------------------------------------------------- */
 +
  #include <com32.h>
++#include <fcntl.h>
  #include <stdint.h>
  #include <stdio.h>
  #include <errno.h>
@@@ -139,20 -93,25 +140,20 @@@ void lba2chs(disk_chs *dst, const struc
  
  uint32_t get_file_lba(const char *filename)
  {
 -    com32sys_t inregs;
 -    uint32_t lba;
 +    struct com32_filedata fd;
 +    uint32_t lba = 0;
 +    int size = 65536;
 +    char *buf;
  
 -    /* Start with clean registers */
 -    memset(&inregs, 0, sizeof(com32sys_t));
 +    buf = lmalloc(size);
 +    if (!buf)
 +      return 0;
  
      /* Put the filename in the bounce buffer */
 -    strlcpy(__com32.cs_bounce, filename, __com32.cs_bounce_size);
 -
 -    /* Call comapi_open() which returns a structure pointer in SI
 -     * to a structure whose first member happens to be the LBA.
 -     */
 -    inregs.eax.w[0] = 0x0006;
 -    inregs.esi.w[0] = OFFS(__com32.cs_bounce);
 -    inregs.es = SEG(__com32.cs_bounce);
 -    __com32.cs_intcall(0x22, &inregs, &inregs);
 +    strlcpy(buf, filename, size);
  
-     if (open_file(buf, &fd) <= 0) {
 -    if ((inregs.eflags.l & EFLAGS_CF) || inregs.esi.w[0] == 0) {
 -      return 0;               /* Filename not found */
++    if (open_file(buf, O_RDONLY, &fd) <= 0) {
 +      goto fail;              /* Filename not found */
      }
  
      /* Since the first member is the LBA, we simply cast */
index ff19c53,0000000..27d4618
mode 100644,000000..100644
--- /dev/null
@@@ -1,155 -1,0 +1,156 @@@
-     rv = open_file(file, &fd);
 +/* ----------------------------------------------------------------------- *
 + *   
 + *   Copyright 2012 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.
 + *
 + * ----------------------------------------------------------------------- */
 +
 +/*
 + * chainbooting - replace the current bootloader completely.  This
 + * is BIOS-specific.
 + */
 +
++#include <fcntl.h>
 +#include <stdlib.h>
 +#include <string.h>
 +#include <stdio.h>
 +#include <dprintf.h>
 +
 +#include <com32.h>
 +#include <sys/exec.h>
 +#include <sys/io.h>
 +#include "core.h"
 +#include "menu.h"
 +#include "fs.h"
 +#include "config.h"
 +#include "localboot.h"
 +#include "bios.h"
 +
 +#include <syslinux/boot.h>
 +#include <syslinux/bootrm.h>
 +#include <syslinux/movebits.h>
 +#include <syslinux/config.h>
 +
 +void chainboot_file(const char *file, uint32_t type)
 +{
 +    uint8_t keeppxe = 0;
 +    const union syslinux_derivative_info *sdi;
 +    struct syslinux_rm_regs regs;
 +    struct syslinux_movelist *fraglist = NULL;
 +    struct syslinux_memmap *mmap = NULL;
 +    struct com32_filedata fd;
 +    com32sys_t reg;
 +    char *stack;
 +    void *buf;
 +    int rv, max, size;
 +    
 +    max = 0xA0000;            /* Maximum load */
 +    buf = malloc(max);
 +    if (!buf)
 +      goto bail;
 +    
++    rv = open_file(file, O_RDONLY, &fd);
 +    if (rv == -1)
 +      goto bail;
 +    
 +    reg.eax.l = max;
 +    reg.ebx.l = 0;
 +    reg.edx.w[0] = 0;
 +    reg.edi.l = (uint32_t)buf;
 +    reg.ebp.l = -1;   /* XXX: limit? */
 +    reg.esi.w[0] = rv;
 +
 +    pm_load_high(&reg);
 +
 +    size = reg.edi.l - (unsigned long)buf;
 +    if (size > 0xA0000 - 0x7C00) {
 +      printf("Too large for a boostrap (need LINUX instead of KERNEL?)\n");
 +      goto bail;
 +    }
 +
 +    mmap = syslinux_memory_map();
 +    if (!mmap)
 +      goto bail;
 +
 +    sdi = syslinux_derivative_info();
 +
 +    memset(&regs, 0, sizeof(regs));
 +    regs.ip = 0x7c00;
 +
 +    if (sdi->c.filesystem == SYSLINUX_FS_SYSLINUX ||
 +      sdi->c.filesystem == SYSLINUX_FS_EXTLINUX) {
 +      if (syslinux_add_movelist(&fraglist, 0x800 - 18,
 +                                (addr_t)sdi->r.esbx, 16))
 +          goto bail;
 +
 +      /* DS:SI points to partition info */
 +      regs.esi.l = 0x800 - 18;
 +    }
 +
 +    /*
 +     * For a BSS boot sector we have to transfer the
 +     * superblock.
 +     */
 +    if (sdi->c.filesystem == SYSLINUX_FS_SYSLINUX &&
 +      type == IMAGE_TYPE_BSS && this_fs->fs_ops->copy_super(buf))
 +      goto bail;
 +
 +    if (sdi->c.filesystem == SYSLINUX_FS_PXELINUX) {
 +      keeppxe = 0x03;         /* Chainloading + keep PXE */
 +      stack = (char *)sdi->r.fssi;
 +
 +      /*
 +       * Set up the registers with their initial values
 +       */
 +
 +      regs.eax.l = *(uint32_t *)&stack[36];
 +      regs.ecx.l = *(uint32_t *)&stack[32];
 +      regs.edx.l = *(uint32_t *)&stack[28];
 +      regs.ebx.l = *(uint32_t *)&stack[24];
 +      regs.esp.l = sdi->rr.r.esi.w[0] + 44;
 +      regs.ebp.l = *(uint32_t *)&stack[16];
 +      regs.esi.l = *(uint32_t *)&stack[12];
 +      regs.edi.l = *(uint32_t *)&stack[8];
 +      regs.es = *(uint16_t *)&stack[4];
 +      regs.ss = sdi->rr.r.fs;
 +      regs.ds = *(uint16_t *)&stack[6];
 +      regs.fs = *(uint16_t *)&stack[2];
 +      regs.gs = *(uint16_t *)&stack[0];
 +    } else {
 +      const uint16_t *esdi = (const uint16_t *)sdi->disk.esdi_ptr;
 +
 +      regs.esp.l = (uint16_t)(unsigned long)StackBuf + 44;
 +
 +      /*
 +       * DON'T DO THIS FOR PXELINUX...
 +       * For PXE, ES:BX -> PXENV+, and this would
 +       * corrupt that use.
 +       *
 +       * Restore ES:DI -> $PnP (if we were ourselves
 +       * called that way...)
 +       */
 +      regs.edi.w[0] = esdi[0]; /* New DI */
 +      regs.es       = esdi[2]; /* New ES */
 +
 +      regs.edx.l    = sdi->rr.r.edx.b[0]; /* Drive number -> DL */
 +    }
 +
 +    if (syslinux_add_movelist(&fraglist, 0x7c00, (addr_t)buf, size))
 +      goto bail;
 +
 +    syslinux_shuffle_boot_rm(fraglist, mmap, keeppxe, &regs);
 +
 +bail:
 +    if (fraglist)
 +      syslinux_free_movelist(fraglist);
 +    if (mmap)
 +      syslinux_free_memmap(mmap);
 +    if (buf)
 +      free(buf);
 +    return;
 +}
index ffbcf74,0000000..49a0de5
mode 100644,000000..100644
--- /dev/null
@@@ -1,158 -1,0 +1,173 @@@
- __export void execute(const char *cmdline, uint32_t type)
 +/* ----------------------------------------------------------------------- *
 + *
 + *   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 <dprintf.h>
 +
 +#include <com32.h>
 +#include <sys/exec.h>
 +#include <sys/io.h>
 +#include <sys/module.h>
 +#include "core.h"
 +#include "menu.h"
 +#include "fs.h"
 +#include "config.h"
 +#include "localboot.h"
 +#include "bios.h"
 +
 +#include <syslinux/bootrm.h>
 +#include <syslinux/movebits.h>
 +#include <syslinux/config.h>
 +#include <syslinux/boot.h>
 +
 +const struct image_types image_boot_types[] = {
 +    { "localboot", IMAGE_TYPE_LOCALBOOT },
 +    { "kernel", IMAGE_TYPE_KERNEL },
 +    { "linux", IMAGE_TYPE_LINUX },
 +    { "boot", IMAGE_TYPE_BOOT },
 +    { "bss", IMAGE_TYPE_BSS },
 +    { "pxe", IMAGE_TYPE_PXE },
 +    { "fdimage", IMAGE_TYPE_FDIMAGE },
 +    { "com32", IMAGE_TYPE_COM32 },
 +    { "config", IMAGE_TYPE_CONFIG },
 +    { NULL, 0 },
 +};
 +
 +extern int create_args_and_load(char *);
 +
-       char *q;
++__export void execute(const char *cmdline, uint32_t type, bool sysappend)
 +{
 +      const char *kernel, *args;
 +      const char *p;
 +      com32sys_t ireg;
-       q = malloc(strlen(cmdline) + 2);
++      char *q, ch;
 +
 +      memset(&ireg, 0, sizeof ireg);
 +
-       strcpy(q, p);
++      if (strlen(cmdline) >= MAX_CMDLINE_LEN) {
++              printf("cmdline too long\n");
++              return;
++      }
++
++      q = malloc(MAX_CMDLINE_LEN);
 +      if (!q) {
 +              printf("%s(): Fail to malloc a buffer to exec %s\n",
 +                      __func__, cmdline);
 +              return;
 +      }
 +
 +      kernel = q;
 +      p = cmdline;
 +      while (*p && !my_isspace(*p))
 +              *q++ = *p++;
 +      *q++ = '\0';
 +
 +      args = q;
 +      while (*p && my_isspace(*p))
 +              p++;
 +
-                               execute(p, t->type);
++      do {
++              *q++ = ch = *p++;
++      } while (ch);
++
++      if (sysappend) {
++              /* If we've seen some args, insert a space */
++              if (--q != args)
++                      *q++ = ' ';
++
++              do_sysappend(q);
++      }
 +
 +      dprintf("kernel is %s, args = %s  type = %d \n", kernel, args, type);
 +
 +      if (kernel[0] == '.') {
 +              /* It might be a type specifier */
 +              const struct image_types *t;
 +              for (t = image_boot_types; t->name; t++) {
 +                      if (!strcmp(kernel + 1, t->name)) {
 +                              /*
 +                               * Strip the type specifier, apply the
 +                               * filename extension if COM32 and
 +                               * retry.
 +                               */
 +                              if (t->type == IMAGE_TYPE_COM32) {
 +                                      p = apply_extension(p, ".c32");
 +                                      if (!p)
 +                                              return;
 +                              }
 +
++                              execute(p, t->type, sysappend);
 +                              return;
 +                      }
 +              }
 +      }
 +
 +      if (type == IMAGE_TYPE_COM32) {
 +              /*
 +               * We may be called with the console in an unknown
 +               * state, so initialise it.
 +               */
 +              ldlinux_console_init();
 +
 +              /* new entry for elf format c32 */
 +              if (create_args_and_load((char *)cmdline))
 +                      printf("Failed to load COM32 file %s\n", kernel);
 +
 +              /*
 +               * The old COM32 module code would run the module then
 +               * drop the user back at the command prompt,
 +               * irrespective of how the COM32 module was loaded,
 +               * e.g. from vesamenu.c32.
 +               */
 +              unload_modules_since("ldlinux.c32");
 +
 +              /* Restore the console */
 +              ldlinux_console_init();
 +
 +              ldlinux_enter_command();
 +      } else if (type == IMAGE_TYPE_CONFIG) {
 +              char *argv[] = { "ldlinux.c32", NULL, NULL };
 +              char *config;
 +              int rv;
 +
 +              /* kernel contains the config file name */
 +              config = malloc(FILENAME_MAX);
 +              if (!config)
 +                      goto out;
 +
 +              realpath(config, kernel, FILENAME_MAX);
 +
 +              /* If we got anything on the command line, do a chdir */
 +              if (*args)
 +                      mangle_name(config_cwd, args);
 +
 +              argv[1] = config;
 +              rv = start_ldlinux(2, argv);
 +              printf("Failed to exec ldlinux.c32: %s\n", strerror(rv));
 +      } else if (type == IMAGE_TYPE_LOCALBOOT) {
 +              local_boot(strtoul(kernel, NULL, 0));
 +      } else if (type == IMAGE_TYPE_PXE || type == IMAGE_TYPE_BSS ||
 +                 type == IMAGE_TYPE_BOOT) {
 +              chainboot_file(kernel, type);
 +      } else {
 +              /* Need add one item for kernel load, as we don't use
 +              * the assembly runkernel.inc any more */
 +              new_linux_kernel((char *)kernel, (char *)args);
 +      }
 +
 +out:
 +      free((void *)kernel);
 +
 +      /* If this returns, something went bad; return to menu */
 +}
index c692d75,0000000..76d117c
mode 100644,000000..100644
--- /dev/null
@@@ -1,349 -1,0 +1,349 @@@
-               execute(cmd, type);
 +#include <linux/list.h>
 +#include <sys/times.h>
 +#include <fcntl.h>
 +#include <stdbool.h>
 +#include <string.h>
 +#include <core.h>
 +#include <fs.h>
 +#include "cli.h"
 +#include "console.h"
 +#include "com32.h"
 +#include "menu.h"
 +#include "config.h"
 +#include "syslinux/adv.h"
 +#include "syslinux/boot.h"
 +#include "syslinux/config.h"
 +
 +#include <sys/module.h>
 +
 +struct file_ext {
 +      const char *name;
 +      enum kernel_type type;
 +};
 +
 +static const struct file_ext file_extensions[] = {
 +      { ".c32", IMAGE_TYPE_COM32 },
 +      { ".img", IMAGE_TYPE_FDIMAGE },
 +      { ".bss", IMAGE_TYPE_BSS },
 +      { ".bin", IMAGE_TYPE_BOOT },
 +      { ".bs", IMAGE_TYPE_BOOT },
 +      { ".0", IMAGE_TYPE_PXE },
 +      { NULL, 0 },
 +};
 +
 +/*
 + * Return a pointer to one byte after the last character of the
 + * command.
 + */
 +static inline const char *find_command(const char *str)
 +{
 +      const char *p;
 +
 +      p = str;
 +      while (*p && !my_isspace(*p))
 +              p++;
 +      return p;
 +}
 +
 +__export uint32_t parse_image_type(const char *kernel)
 +{
 +      const struct file_ext *ext;
 +      const char *p;
 +      int len;
 +
 +      /* Find the end of the command */
 +      p = find_command(kernel);
 +      len = p - kernel;
 +
 +      for (ext = file_extensions; ext->name; ext++) {
 +              int elen = strlen(ext->name);
 +
 +              if (!strncmp(kernel + len - elen, ext->name, elen))
 +                      return ext->type;
 +      }
 +
 +      /* use IMAGE_TYPE_KERNEL as default */
 +      return IMAGE_TYPE_KERNEL;
 +}
 +
 +/*
 + * Returns the kernel name with file extension if one wasn't present.
 + */
 +static const char *get_extension(const char *kernel)
 +{
 +      const struct file_ext *ext;
 +      const char *p;
 +      int len;
 +
 +      /* Find the end of the command */
 +      p = find_command(kernel);
 +      len = p - kernel;
 +
 +      for (ext = file_extensions; ext->name; ext++) {
 +              char *str;
 +              int elen = strlen(ext->name);
 +              FILE *f;
 +
 +              str = malloc(len + elen + 1);
 +
 +              strncpy(str, kernel, len);
 +              strncpy(str + len, ext->name, elen);
 +              str[len + elen] = '\0';
 +              f = findpath(str);
 +              free(str);
 +
 +              if (f) {
 +                      fclose(f);
 +                      return ext->name;
 +              }
 +      }
 +
 +      return NULL;
 +}
 +
 +const char *apply_extension(const char *kernel, const char *ext)
 +{
 +      const char *p;
 +      char *k;
 +      int len = strlen(kernel);
 +      int elen = strlen(ext);
 +
 +      k = malloc(len + elen + 1);
 +      if (!k)
 +              return NULL;
 +
 +      p = find_command(kernel);
 +
 +      len = p - kernel;
 +
 +      /* Copy just the kernel name */
 +      memcpy(k, kernel, len);
 +
 +      /* Append the extension */
 +      if (strncmp(p - elen, ext, elen)) {
 +              memcpy(k + len, ext, elen);
 +              len += elen;
 +      }
 +
 +      /* Copy the rest of the command line */
 +      strcpy(k + len, p);
 +
 +      k[len + strlen(p)] = '\0';
 +
 +      return k;
 +}
 +
 +/*
 + * Attempt to load a kernel after deciding what type of image it is.
 + *
 + * We only return from this function if something went wrong loading
 + * the the kernel. If we return the caller should call enter_cmdline()
 + * so that the user can help us out.
 + */
 +__export void load_kernel(const char *command_line)
 +{
 +      struct menu_entry *me;
 +      const char *cmdline;
 +      const char *kernel;
 +      uint32_t type;
 +
 +      kernel = strdup(command_line);
 +      if (!kernel)
 +              goto bad_kernel;
 +
 +      /* Virtual kernel? */
 +      me = find_label(kernel);
 +      if (me) {
 +              const char *args;
 +              char *cmd;
 +              size_t len = strlen(me->cmdline) + 1;
 +
 +              /* Find the end of the command */
 +              args = find_command(kernel);
 +              while(*args && my_isspace(*args))
 +                      args++;
 +
 +              if (strlen(args))
 +                      len += strlen(args) + 1; /* +1 for space (' ') */
 +
 +              cmd = malloc(len);
 +              if (!cmd)
 +                      goto bad_kernel;
 +
 +              if (strlen(args))
 +                      snprintf(cmd, len, "%s %s", me->cmdline, args);
 +              else
 +                      strncpy(cmd, me->cmdline, len);
 +
 +              type = parse_image_type(cmd);
-       execute(kernel, type);
++              execute(cmd, type, false);
 +              /* We shouldn't return */
 +              goto bad_kernel;
 +      }
 +
 +      if (!allowimplicit)
 +              goto bad_implicit;
 +
 +      /* Insert a null character to ignore any user-specified options */
 +      if (!allowoptions) {
 +              char *p = (char *)find_command(kernel);
 +              *p = '\0';
 +      }
 +
 +      type = parse_image_type(kernel);
 +      if (type == IMAGE_TYPE_KERNEL) {
 +              const char *ext;
 +
 +              /*
 +               * Automatically lookup the extension if one wasn't
 +               * supplied by the user.
 +               */
 +              ext = get_extension(kernel);
 +              if (ext) {
 +                      const char *k;
 +
 +                      k = apply_extension(kernel, ext);
 +                      if (!k)
 +                              goto bad_kernel;
 +
 +                      free((void *)kernel);
 +                      kernel = k;
 +
 +                      type = parse_image_type(kernel);
 +              }
 +      }
 +
-               execute(cmdline, type);
++      execute(kernel, type, true);
 +      free((void *)kernel);
 +
 +bad_implicit:
 +bad_kernel:
 +      /*
 +       * If we fail to boot the kernel execute the "onerror" command
 +       * line.
 +       */
 +      if (onerrorlen) {
 +              me = find_label(onerror);
 +              if (me)
 +                      rsprintf(&cmdline, "%s %s", me->cmdline, default_cmd);
 +              else
 +                      rsprintf(&cmdline, "%s %s", onerror, default_cmd);
 +
 +              type = parse_image_type(cmdline);
++              execute(cmdline, type, true);
 +      }
 +}
 +
 +/*
 + * If this function returns you must call ldinux_enter_command() to
 + * preserve the 4.0x behaviour.
 + */
 +void ldlinux_auto_boot(void)
 +{
 +      if (!defaultlevel) {
 +              if (strlen(ConfigName))
 +                      printf("No DEFAULT or UI configuration directive found!\n");
 +              if (noescape)
 +                      kaboom();
 +      } else
 +              load_kernel(default_cmd);
 +}
 +
 +static void enter_cmdline(void)
 +{
 +      const char *cmdline;
 +
 +      /* Enter endless command line prompt, should support "exit" */
 +      while (1) {
 +              bool to = false;
 +
 +              if (noescape) {
 +                      ldlinux_auto_boot();
 +                      continue;
 +              }
 +
 +              cmdline = edit_cmdline("boot:", 1, NULL, cat_help_file, &to);
 +              printf("\n");
 +
 +              /* return if user only press enter or we timed out */
 +              if (!cmdline || cmdline[0] == '\0') {
 +                      if (to && ontimeoutlen)
 +                              load_kernel(ontimeout);
 +                      else
 +                              ldlinux_auto_boot();
 +              } else
 +                      load_kernel(cmdline);
 +      }
 +}
 +
 +void ldlinux_enter_command(void)
 +{
 +      enter_cmdline();
 +}
 +
 +/*
 + * Undo the work we did in openconsole().
 + */
 +static void __destructor close_console(void)
 +{
 +      int i;
 +
 +      for (i = 0; i <= 2; i++)
 +              close(i);
 +}
 +
 +void ldlinux_console_init(void)
 +{
 +      openconsole(&dev_stdcon_r, &dev_ansiserial_w);
 +}
 +
 +__export int main(int argc __unused, char **argv)
 +{
 +      const void *adv;
 +      const char *cmdline;
 +      size_t count = 0;
 +
 +      ldlinux_console_init();
 +
 +      parse_configs(&argv[1]);
 +
 +      __syslinux_set_serial_console_info();
 +
 +      adv = syslinux_getadv(ADV_BOOTONCE, &count);
 +      if (adv && count) {
 +              /*
 +               * We apparently have a boot-once set; clear it and
 +               * then execute the boot-once.
 +               */
 +              char *src, *dst;
 +              size_t i;
 +
 +              src = (char *)adv;
 +              cmdline = dst = malloc(count + 1);
 +              if (!dst) {
 +                      printf("Failed to allocate memory for ADV\n");
 +                      ldlinux_enter_command();
 +              }
 +
 +              for (i = 0; i < count; i++)
 +                      *dst++ = *src++;
 +              *dst = '\0';    /* Null-terminate */
 +
 +              /* Clear the boot-once data from the ADV */
 +              if (!syslinux_setadv(ADV_BOOTONCE, 0, NULL))
 +                      syslinux_adv_write();
 +
 +              load_kernel(cmdline); /* Shouldn't return */
 +              ldlinux_enter_command();
 +      }
 +
 +      /* TODO: Check KbdFlags? */
 +      if (!forceprompt)
 +              ldlinux_auto_boot();
 +
 +      if (defaultlevel > 1)
 +              ldlinux_auto_boot();
 +
 +      ldlinux_enter_command();
 +      return 0;
 +}
index a2421e9,0000000..0f11d15
mode 100644,000000..100644
--- /dev/null
@@@ -1,1466 -1,0 +1,1505 @@@
-                   if ((ld->ipappend & (1U << i)) && ipappend->ptr[i])
-                       ipp += sprintf(ipp, " %s", ipappend->ptr[i]);
 +/* ----------------------------------------------------------------------- *
 + *
 + *   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 <sys/io.h>
 +#include <fcntl.h>
 +#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 <dprintf.h>
 +#include <ctype.h>
 +#include <bios.h>
 +#include <core.h>
 +#include <fs.h>
++#include <syslinux/pxe_api.h>
 +
 +#include "menu.h"
 +#include "config.h"
 +#include "getkey.h"
 +#include "core.h"
 +#include "fs.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},
 +};
 +
 +/* Must match enum kernel_type */
 +static const char *const kernel_types[] = {
 +    "none",
 +    "localboot",
 +    "kernel",
 +    "linux",
 +    "boot",
 +    "bss",
 +    "pxe",
 +    "fdimage",
 +    "comboot",
 +    "com32",
 +    "config",
 +    NULL
 +};
 +
 +short uappendlen = 0;         //bytes in append= command
 +short ontimeoutlen = 0;               //bytes in ontimeout command
 +short onerrorlen = 0;         //bytes in onerror command
 +short forceprompt = 0;                //force prompt
 +short noescape = 0;           //no escape
 +short nocomplete = 0;         //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 = 0;               //the current level of default
 +short vkernel = 0;            //have we seen any "label" statements?
 +extern short NoHalt;          //idle.c
 +
 +const char *onerror = NULL;   //"onerror" command line
 +const char *ontimeout = NULL; //"ontimeout" command line
 +
 +__export const char *default_cmd = NULL;      //"default" command line
 +
 +/* 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;
 +unsigned int kbdtimeout = 0;
 +
 +/* Keep track of global default */
 +static int has_ui = 0;                /* DEFAULT only counts if UI is found */
 +extern const 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
 +
 +/* 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;
 +      
 +      //dprintf("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;
 +
 +    //dprintf("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;
 +          }
 +      }
 +    }
 +}
 +
++/*
++ * Copy a string, converting whitespace characters to underscores
++ * and compacting them.  Return a pointer to the final null.
++ */
++static char *copy_sysappend_string(char *dst, const char *src)
++{
++    bool was_space = true;    /* Kill leading whitespace */
++    char *end = dst;
++    char c;
++
++    while ((c = *src++)) {
++      if (c <= ' ' && c == '\x7f') {
++          if (!was_space)
++              *dst++ = '_';
++          was_space = true;
++      } else {
++          *dst++ = c;
++          end = dst;
++          was_space = false;
++      }
++    }
++    *end = '\0';
++    return end;
++}
++
 +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++) {
- //static unsigned int ipappend = 0;
- __export unsigned int ipappend = 0;
++                  if ((ld->ipappend & (1U << i)) &&
++                      ipappend->ptr[i] && ipappend->ptr[i][0]) {
++                      *ipp++ = ' ';
++                      ipp = copy_sysappend_string(ipp, 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);
 +              dprintf("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;
 +}
 +
 +void print_labels(const char *prefix, size_t len)
 +{
 +    struct menu_entry *me;
 +
 +    printf("\n");
 +    for (me = all_entries; me; me = me->next ) {
 +      if (!strncmp(prefix, me->label, len))
 +          printf(" %s", me->label);
 +    }
 +    printf("\n");
 +}
 +
 +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 **ref)
 +{
 +    char *sp = p;
 +    char *ep = sp;
 +
 +    while (*ep && !my_isspace(*ep))
 +      ep++;
 +
 +    if (ref)
 +      *ref = ep;
 +    return refstrndup(sp, ep - sp);
 +}
 +
 +static const char *refdup_word(char **p)
 +{
 +    return __refdup_word(*p, p);
 +}
 +
 +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 const char *append;
-           ld.ipappend = ipappend;
 +extern uint16_t PXERetry;
 +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;
 +}
 +
 +extern void get_msg_file(char *);
 +
 +void cat_help_file(int key)
 +{
 +      struct menu *cm = current_menu;
 +      int fkey;
 +
 +      switch (key) {
 +      case KEY_F1:
 +              fkey = 0;
 +              break;
 +      case KEY_F2:
 +              fkey = 1;
 +              break;
 +      case KEY_F3:
 +              fkey = 2;
 +              break;
 +      case KEY_F4:
 +              fkey = 3;
 +              break;
 +      case KEY_F5:
 +              fkey = 4;
 +              break;
 +      case KEY_F6:
 +              fkey = 5;
 +              break;
 +      case KEY_F7:
 +              fkey = 6;
 +              break;
 +      case KEY_F8:
 +              fkey = 7;
 +              break;
 +      case KEY_F9:
 +              fkey = 8;
 +              break;
 +      case KEY_F10:
 +              fkey = 9;
 +              break;
 +      case KEY_F11:
 +              fkey = 10;
 +              break;
 +      case KEY_F12:
 +              fkey = 11;
 +              break;
 +      default:
 +              fkey = -1;
 +              break;
 +      }
 +
 +      if (fkey == -1)
 +              return;
 +
 +      if (cm->fkeyhelp[fkey].textname) {
 +              printf("\n");
 +              get_msg_file((char *)cm->fkeyhelp[fkey].textname);
 +      }
 +}
 +
 +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;
 +}
 +
 +extern uint8_t FlowIgnore;
 +extern uint8_t FlowInput;
 +extern uint8_t FlowOutput;
 +extern uint16_t SerialPort;
 +extern uint16_t BaudDivisor;
 +static uint8_t SerialNotice = 1;
 +
 +#define DEFAULT_BAUD  9600
 +#define BAUD_DIVISOR  115200
 +#define serial_base   0x0400
 +
 +extern void sirq_cleanup_nowipe(void);
 +extern void sirq_install(void);
 +extern void write_serial_str(char *);
 +
 +extern void loadfont(char *);
 +extern void loadkeys(char *);
 +
 +extern char syslinux_banner[];
 +extern char copyright_str[];
 +
 +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);
 +              refstr_put(onerror);
 +              onerror = refstrdup(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;
 +          }
 +          //dprintf("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 = __refdup_word(p, NULL);
 +          ld.kernel = __refdup_word(p, NULL);
 +          /* 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;
-       } else if (looking_at(p, "ipappend")) {
++          ld.ipappend = SysAppends;
 +          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;
 +              //dprintf("got a kernel: %s, type = %d", ld.kernel, ld.type);
 +          }
 +      } else if (looking_at(p, "timeout")) {
 +          kbdtimeout = (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")) {
 +          ontimeout = refstrdup(skipspace(p + 9));
 +          ontimeoutlen = strlen(ontimeout);
 +      } else if (looking_at(p, "allowoptions")) {
 +          allowoptions = !!atoi(skipspace(p + 12));
-               ld.ipappend = atoi(skipspace(p + 8));
++      } else if ((ep = looking_at(p, "ipappend")) ||
++                 (ep = looking_at(p, "sysappend"))) {
++          uint32_t s = strtoul(skipspace(ep), NULL, 16);
 +          if (ld.label)
-               ipappend = atoi(skipspace(p + 8));
++              ld.ipappend = s;
 +          else
++              SysAppends = s;
 +      } else if (looking_at(p, "default")) {
 +          /* default could be a kernel image or another label */
 +          refstr_put(globaldefault);
 +          globaldefault = refstrdup(skipspace(p + 7));
 +
 +          /*
 +           * On the chance that "default" is actually a kernel image
 +           * and not a label, store a copy of it, but only if we
 +           * haven't seen a "ui" command. "ui" commands take
 +           * precendence over "default" commands.
 +           */
 +          if (defaultlevel < LEVEL_UI) {
 +              defaultlevel = LEVEL_DEFAULT;
 +              refstr_put(default_cmd);
 +              default_cmd = refstrdup(globaldefault);
 +          }
 +      } else if (looking_at(p, "ui")) {
 +          has_ui = 1;
 +          defaultlevel = LEVEL_UI;
 +          refstr_put(default_cmd);
 +          default_cmd = refstrdup(skipspace(p + 2));
 +      }
 +      
 +      /*
 +       * subset 1:  pc_opencmd 
 +       * display/font/kbdmap are rather similar, open a file then do sth
 +       */
 +      else if (looking_at(p, "display")) {
 +              const char *filename;
 +              char *dst = KernelName;
 +              size_t len = FILENAME_MAX - 1;
 +
 +              filename = refstrdup(skipspace(p + 7));
 +
 +              while (len-- && not_whitespace(*filename))
 +                      *dst++ = *filename++;
 +              *dst = '\0';
 +
 +              get_msg_file(KernelName);
 +              refstr_put(filename);
 +      } else if (looking_at(p, "font")) {
 +              const char *filename;
 +              char *dst = KernelName;
 +              size_t len = FILENAME_MAX - 1;
 +
 +              filename = refstrdup(skipspace(p + 4));
 +
 +              while (len-- && not_whitespace(*filename))
 +                      *dst++ = *filename++;
 +              *dst = '\0';
 +
 +              loadfont(KernelName);
 +              refstr_put(filename);
 +      } else if (looking_at(p, "kbdmap")) {
 +              const char *filename;
 +              char *dst = KernelName;
 +              size_t len = FILENAME_MAX - 1;
 +
 +              filename = refstrdup(skipspace(p + 4));
 +
 +              while (len-- && not_whitespace(*filename))
 +                      *dst++ = *filename++;
 +              *dst = '\0';
 +
 +              loadkeys(KernelName);
 +              refstr_put(filename);
 +      }
 +      /*
 +       * 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 + 6));
 +      } 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));
 +      } else if (looking_at(p, "onerror")) {
 +              refstr_put(m->onerror);
 +              m->onerror = refstrdup(skipspace(p + 7));
 +              onerrorlen = strlen(m->onerror);
 +              refstr_put(onerror);
 +              onerror = refstrdup(m->onerror);
 +      }
 +
 +      else if (looking_at(p, "pxeretry"))
 +              PXERetry = atoi(skipspace(p + 8));
 +
 +      /* serial setting, bps, flow control */
 +      else if (looking_at(p, "serial")) {
 +              uint16_t port, flow;
 +              uint32_t baud;
 +
 +              p = skipspace(p + 6);
 +              port = atoi(p);
 +
 +              while (isalnum(*p))
 +                      p++;
 +              p = skipspace(p);
 +
 +              /* Default to no flow control */
 +              FlowOutput = 0;
 +              FlowInput = 0;
 +
 +              baud = DEFAULT_BAUD;
 +              if (isalnum(*p)) {
 +                      uint8_t ignore;
 +
 +                      /* setup baud */
 +                      baud = atoi(p);
 +                      while (isalnum(*p))
 +                              p++;
 +                      p = skipspace(p);
 +
 +                      ignore = 0;
 +                      flow = 0;
 +                      if (isalnum(*p)) {
 +                              /* flow control */
 +                              flow = atoi(p);
 +                              ignore = ((flow & 0x0F00) >> 4);
 +                      }
 +
 +                      FlowIgnore = ignore;
 +                      flow = ((flow & 0xff) << 8) | (flow & 0xff);
 +                      flow &= 0xF00B;
 +                      FlowOutput = (flow & 0xff);
 +                      FlowInput = ((flow & 0xff00) >> 8);
 +              }
 +
 +              /*
 +               * Parse baud
 +               */
 +              if (baud < 75) {
 +                      /* < 75 baud == bogus */
 +                      SerialPort = 0;
 +                      continue;
 +              }
 +
 +              baud = BAUD_DIVISOR / baud;
 +              baud &= 0xffff;
 +              BaudDivisor = baud;
 +
 +              /*
 +               * If port > 3 then port is I/O addr
 +               */
 +              if (port <= 3) {
 +                      /* Get the I/O port from the BIOS */
 +                      port <<= 1;
 +                      port = *(volatile uint16_t *)serial_base;
 +              }
 +
 +              
 +              SerialPort = port;
 +
 +              /*
 +               * Begin code to actually set up the serial port
 +               */
 +              sirq_cleanup_nowipe();
 +
 +              outb(0x83, port + 3); /* Enable DLAB */
 +              io_delay();
 +
 +              outb((baud & 0xff), port); /* write divisor to LS */
 +              io_delay();
 +
 +              outb(((baud & 0xff00) >> 8), port + 1); /* write to MS */
 +              io_delay();
 +
 +              outb(0x03, port + 3); /* Disable DLAB */
 +              io_delay();
 +
 +              /*
 +               * Read back LCR (detect missing hw). If nothing here
 +               * we'll read 00 or FF.
 +               */
 +              if (inb(port + 3) != 0x03) {
 +                      /* Assume serial port busted */
 +                      SerialPort = 0;
 +                      continue;
 +              }
 +
 +              outb(0x01, port + 2); /* Enable FIFOs if present */
 +              io_delay();
 +
 +              /* Disable FIFO if unusable */
 +              if (inb(port + 2) < 0x0C0) {
 +                      outb(0, port + 2);
 +                      io_delay();
 +              }
 +
 +              /* Assert bits in MCR */
 +              outb(FlowOutput, port + 4);
 +              io_delay();
 +
 +              /* Enable interrupts if requested */
 +              if (FlowOutput & 0x8)
 +                      sirq_install();
 +
 +              /* Show some life */
 +              if (SerialNotice != 0) {
 +                      SerialNotice = 0;
 +
 +                      write_serial_str(syslinux_banner);
 +                      write_serial_str(copyright_str);
 +              }
 +
 +      } else if (looking_at(p, "say")) {
 +              printf("%s\n", p+4);
 +      } else if (looking_at(p, "path")) {
 +              /* PATH-based lookup */
 +              const char *new_path;
 +              char *_p;
 +              size_t len, new_len;
 +
 +              new_path = refstrdup(skipspace(p + 4));
 +              len = strlen(PATH);
 +              new_len = strlen(new_path);
 +              _p = malloc(len + new_len + 2);
 +              if (_p) {
 +                      strncpy(_p, PATH, len);
 +                      _p[len++] = ':';
 +                      strncpy(_p + len, new_path, new_len);
 +                      _p[len + new_len] = '\0';
 +                      free(PATH);
 +                      PATH = _p;
 +              } else
 +                      printf("Failed to realloc PATH\n");
++      } else if (looking_at(p, "sendcookies")) {
++              const union syslinux_derivative_info *sdi;
++
++              p += strlen("sendcookies");
++              sdi = syslinux_derivative_info();
++
++              if (sdi->c.filesystem == SYSLINUX_FS_PXELINUX) {
++                      SendCookies = strtoul(skipspace(p), NULL, 10);
++                      http_bake_cookies();
++              }
 +      }
 +    }
 +}
 +
 +static int parse_one_config(const char *filename)
 +{
 +      const char *mode = "r";
 +      FILE *f;
 +      int fd;
 +
 +      if (!filename)
 +              fd = open_config();
 +      else
 +              fd = open(filename, O_RDONLY);
 +
 +      if (fd < 0)
 +              return fd;
 +
 +      if (config_cwd[0]) {
 +              if (chdir(config_cwd) < 0)
 +                      printf("Failed to chdir to %s\n", config_cwd);
 +              config_cwd[0] = '\0';
 +      }
 +
 +      f = fdopen(fd, mode);
 +      parse_config_file(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;
 +          }
 +      }
 +    }
 +}
 +
 +void parse_configs(char **argv)
 +{
 +    const char *filename;
 +    struct menu *m;
 +    struct menu_entry *me;
 +    dprintf("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) {
 +      if (parse_one_config(NULL) < 0) {
 +          printf("WARNING: No configuration file found\n");
 +          return;
 +      }
 +    } else {
 +      while ((filename = *argv++)) {
 +              dprintf("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) {
 +      dprintf("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);
 +    }
 +}
Simple merge
index afe8980,0000000..157ded1
mode 100644,000000..100644
--- /dev/null
@@@ -1,476 -1,0 +1,464 @@@
- /**
-  * container_of - cast a member of a structure out to the containing structure
-  * @ptr:        the pointer to the member.
-  * @type:       the type of the container struct this is embedded in.
-  * @member:     the name of the member within the struct.
-  *
-  */
- #define container_of(ptr, type, member) ({                      \
-         const typeof( ((type *)0)->member ) *__mptr = (ptr);    \
-         (type *)( (char *)__mptr - offsetof(type,member) );})
 +// This list structure implementation is adapted from the list implementation
 +// on the Linux kernel.
 +
 +// Original source:
 +// http://git.kernel.org/?p=linux/kernel/git/stable/linux-2.6.25.y.git;a=blob_plain;f=include/linux/list.h;hb=HEAD
 +
 +#ifndef _LINUX_LIST_H
 +#define _LINUX_LIST_H
 +
 +/*
 + * Simple doubly linked list implementation.
 + *
 + * Some of the internal functions ("__xxx") are useful when
 + * manipulating whole lists rather than single entries, as
 + * sometimes we already know the next/prev entries and we can
 + * generate better code by using them directly rather than
 + * using the generic single-entry routines.
 + */
 +
 +#include <stdlib.h>
 +#include <stddef.h>
 +
 +struct list_head {
 +      struct list_head *next, *prev;
 +};
 +
 +#define LIST_HEAD_INIT(name) { &(name), &(name) }
 +
 +#define LIST_HEAD(name) \
 +      struct list_head name = LIST_HEAD_INIT(name)
 +
 +static inline void INIT_LIST_HEAD(struct list_head *list)
 +{
 +      list->next = list;
 +      list->prev = list;
 +}
 +
 +/*
 + * Insert a new entry between two known consecutive entries.
 + *
 + * This is only for internal list manipulation where we know
 + * the prev/next entries already!
 + */
 +static inline void __list_add(struct list_head *new,
 +                            struct list_head *prev,
 +                            struct list_head *next)
 +{
 +      next->prev = new;
 +      new->next = next;
 +      new->prev = prev;
 +      prev->next = new;
 +}
 +
 +/**
 + * list_add - add a new entry
 + * @new: new entry to be added
 + * @head: list head to add it after
 + *
 + * Insert a new entry after the specified head.
 + * This is good for implementing stacks.
 + */
 +static inline void list_add(struct list_head *new, struct list_head *head)
 +{
 +      __list_add(new, head, head->next);
 +}
 +
 +
 +
 +/**
 + * list_add_tail - add a new entry
 + * @new: new entry to be added
 + * @head: list head to add it before
 + *
 + * Insert a new entry before the specified head.
 + * This is useful for implementing queues.
 + */
 +static inline void list_add_tail(struct list_head *new, struct list_head *head)
 +{
 +      __list_add(new, head->prev, head);
 +}
 +
 +
 +/*
 + * Delete a list entry by making the prev/next entries
 + * point to each other.
 + *
 + * This is only for internal list manipulation where we know
 + * the prev/next entries already!
 + */
 +static inline void __list_del(struct list_head * prev, struct list_head * next)
 +{
 +      next->prev = prev;
 +      prev->next = next;
 +}
 +
 +/**
 + * list_del - deletes entry from list.
 + * @entry: the element to delete from the list.
 + * Note: list_empty() on entry does not return true after this, the entry is
 + * in an undefined state.
 + */
 +static inline void list_del(struct list_head *entry)
 +{
 +      __list_del(entry->prev, entry->next);
 +      entry->next = NULL;
 +      entry->prev = NULL;
 +}
 +
 +/**
 + * list_replace - replace old entry by new one
 + * @old : the element to be replaced
 + * @new : the new element to insert
 + *
 + * If @old was empty, it will be overwritten.
 + */
 +static inline void list_replace(struct list_head *old,
 +                              struct list_head *new)
 +{
 +      new->next = old->next;
 +      new->next->prev = new;
 +      new->prev = old->prev;
 +      new->prev->next = new;
 +}
 +
 +static inline void list_replace_init(struct list_head *old,
 +                                      struct list_head *new)
 +{
 +      list_replace(old, new);
 +      INIT_LIST_HEAD(old);
 +}
 +
 +/**
 + * list_del_init - deletes entry from list and reinitialize it.
 + * @entry: the element to delete from the list.
 + */
 +static inline void list_del_init(struct list_head *entry)
 +{
 +      __list_del(entry->prev, entry->next);
 +      INIT_LIST_HEAD(entry);
 +}
 +
 +/**
 + * list_move - delete from one list and add as another's head
 + * @list: the entry to move
 + * @head: the head that will precede our entry
 + */
 +static inline void list_move(struct list_head *list, struct list_head *head)
 +{
 +      __list_del(list->prev, list->next);
 +      list_add(list, head);
 +}
 +
 +/**
 + * list_move_tail - delete from one list and add as another's tail
 + * @list: the entry to move
 + * @head: the head that will follow our entry
 + */
 +static inline void list_move_tail(struct list_head *list,
 +                                struct list_head *head)
 +{
 +      __list_del(list->prev, list->next);
 +      list_add_tail(list, head);
 +}
 +
 +/**
 + * list_is_last - tests whether @list is the last entry in list @head
 + * @list: the entry to test
 + * @head: the head of the list
 + */
 +static inline int list_is_last(const struct list_head *list,
 +                              const struct list_head *head)
 +{
 +      return list->next == head;
 +}
 +
 +/**
 + * list_empty - tests whether a list is empty
 + * @head: the list to test.
 + */
 +static inline int list_empty(const struct list_head *head)
 +{
 +      return head->next == head;
 +}
 +
 +/**
 + * list_empty_careful - tests whether a list is empty and not being modified
 + * @head: the list to test
 + *
 + * Description:
 + * tests whether a list is empty _and_ checks that no other CPU might be
 + * in the process of modifying either member (next or prev)
 + *
 + * NOTE: using list_empty_careful() without synchronization
 + * can only be safe if the only activity that can happen
 + * to the list entry is list_del_init(). Eg. it cannot be used
 + * if another CPU could re-list_add() it.
 + */
 +static inline int list_empty_careful(const struct list_head *head)
 +{
 +      struct list_head *next = head->next;
 +      return (next == head) && (next == head->prev);
 +}
 +
 +static inline void __list_splice(struct list_head *list,
 +                               struct list_head *head)
 +{
 +      struct list_head *first = list->next;
 +      struct list_head *last = list->prev;
 +      struct list_head *at = head->next;
 +
 +      first->prev = head;
 +      head->next = first;
 +
 +      last->next = at;
 +      at->prev = last;
 +}
 +
 +/**
 + * list_splice - join two lists
 + * @list: the new list to add.
 + * @head: the place to add it in the first list.
 + */
 +static inline void list_splice(struct list_head *list, struct list_head *head)
 +{
 +      if (!list_empty(list))
 +              __list_splice(list, head);
 +}
 +
 +/**
 + * list_splice_init - join two lists and reinitialise the emptied list.
 + * @list: the new list to add.
 + * @head: the place to add it in the first list.
 + *
 + * The list at @list is reinitialised
 + */
 +static inline void list_splice_init(struct list_head *list,
 +                                  struct list_head *head)
 +{
 +      if (!list_empty(list)) {
 +              __list_splice(list, head);
 +              INIT_LIST_HEAD(list);
 +      }
 +}
 +
 +/**
 + * list_entry - get the struct for this entry
 + * @ptr:      the &struct list_head pointer.
 + * @type:     the type of the struct this is embedded in.
 + * @member:   the name of the list_struct within the struct.
 + */
 +#define list_entry(ptr, type, member) \
 +      container_of(ptr, type, member)
 +
 +/**
 + * list_first_entry - get the first element from a list
 + * @ptr:      the list head to take the element from.
 + * @type:     the type of the struct this is embedded in.
 + * @member:   the name of the list_struct within the struct.
 + *
 + * Note, that list is expected to be not empty.
 + */
 +#define list_first_entry(ptr, type, member) \
 +      list_entry((ptr)->next, type, member)
 +
 +/**
 + * list_for_each      -       iterate over a list
 + * @pos:      the &struct list_head to use as a loop cursor.
 + * @head:     the head for your list.
 + */
 +#define list_for_each(pos, head) \
 +      for (pos = (head)->next; pos != (head); \
 +              pos = pos->next)
 +
 +/**
 + * __list_for_each    -       iterate over a list
 + * @pos:      the &struct list_head to use as a loop cursor.
 + * @head:     the head for your list.
 + *
 + * This variant differs from list_for_each() in that it's the
 + * simplest possible list iteration code, no prefetching is done.
 + * Use this for code that knows the list to be very short (empty
 + * or 1 entry) most of the time.
 + */
 +#define __list_for_each(pos, head) \
 +      for (pos = (head)->next; pos != (head); pos = pos->next)
 +
 +/**
 + * list_for_each_prev -       iterate over a list backwards
 + * @pos:      the &struct list_head to use as a loop cursor.
 + * @head:     the head for your list.
 + */
 +#define list_for_each_prev(pos, head) \
 +      for (pos = (head)->prev; pos != (head); \
 +              pos = pos->prev)
 +
 +/**
 + * list_for_each_safe - iterate over a list safe against removal of list entry
 + * @pos:      the &struct list_head to use as a loop cursor.
 + * @n:                another &struct list_head to use as temporary storage
 + * @head:     the head for your list.
 + */
 +#define list_for_each_safe(pos, n, head) \
 +      for (pos = (head)->next, n = pos->next; pos != (head); \
 +              pos = n, n = pos->next)
 +
 +/**
 + * list_for_each_prev_safe - iterate over a list backwards safe against removal of list entry
 + * @pos:      the &struct list_head to use as a loop cursor.
 + * @n:                another &struct list_head to use as temporary storage
 + * @head:     the head for your list.
 + */
 +#define list_for_each_prev_safe(pos, n, head) \
 +      for (pos = (head)->prev, n = pos->prev; \
 +           pos != (head); \
 +           pos = n, n = pos->prev)
 +
 +/**
 + * list_for_each_entry        -       iterate over list of given type
 + * @pos:      the type * to use as a loop cursor.
 + * @head:     the head for your list.
 + * @member:   the name of the list_struct within the struct.
 + */
 +#define list_for_each_entry(pos, head, member)                                \
 +      for (pos = list_entry((head)->next, typeof(*pos), member);      \
 +           &pos->member != (head);    \
 +           pos = list_entry(pos->member.next, typeof(*pos), member))
 +
 +/**
 + * list_for_each_entry_safe - iterate over list of given type safe against removal of list entry
 +* @pos:        the type * to use as a loop cursor.
 +* @n:          another type * to use as temporary storage
 +* @head:       the head for your list.
 +* @member:     the name of the list_struct within the struct.
 +*/
 +#define list_for_each_entry_safe(pos, n, head, member)                  \
 +      for (pos = list_entry((head)->next, typeof(*pos), member),      \
 +              n = list_entry(pos->member.next, typeof(*pos), member); \
 +             &pos->member != (head);                                  \
 +             pos = n, n = list_entry(n->member.next, typeof(*n), member))
 +
 +/**
 + * list_for_each_entry_reverse - iterate backwards over list of given type.
 + * @pos:      the type * to use as a loop cursor.
 + * @head:     the head for your list.
 + * @member:   the name of the list_struct within the struct.
 + */
 +#define list_for_each_entry_reverse(pos, head, member)                        \
 +      for (pos = list_entry((head)->prev, typeof(*pos), member);      \
 +           &pos->member != (head);    \
 +           pos = list_entry(pos->member.prev, typeof(*pos), member))
 +
 +/**
 + * list_prepare_entry - prepare a pos entry for use in list_for_each_entry_continue()
 + * @pos:      the type * to use as a start point
 + * @head:     the head of the list
 + * @member:   the name of the list_struct within the struct.
 + *
 + * Prepares a pos entry for use as a start point in list_for_each_entry_continue().
 + */
 +#define list_prepare_entry(pos, head, member) \
 +      ((pos) ? : list_entry(head, typeof(*pos), member))
 +
 +/**
 + * list_for_each_entry_continue - continue iteration over list of given type
 + * @pos:      the type * to use as a loop cursor.
 + * @head:     the head for your list.
 + * @member:   the name of the list_struct within the struct.
 + *
 + * Continue to iterate over list of given type, continuing after
 + * the current position.
 + */
 +#define list_for_each_entry_continue(pos, head, member)               \
 +      for (pos = list_entry(pos->member.next, typeof(*pos), member);  \
 +           &pos->member != (head);    \
 +           pos = list_entry(pos->member.next, typeof(*pos), member))
 +
 +/**
 + * list_for_each_entry_continue_reverse - iterate backwards from the given point
 + * @pos:      the type * to use as a loop cursor.
 + * @head:     the head for your list.
 + * @member:   the name of the list_struct within the struct.
 + *
 + * Start to iterate over list of given type backwards, continuing after
 + * the current position.
 + */
 +#define list_for_each_entry_continue_reverse(pos, head, member)               \
 +      for (pos = list_entry(pos->member.prev, typeof(*pos), member);  \
 +           &pos->member != (head);    \
 +           pos = list_entry(pos->member.prev, typeof(*pos), member))
 +
 +/**
 + * list_for_each_entry_from - iterate over list of given type from the current point
 + * @pos:      the type * to use as a loop cursor.
 + * @head:     the head for your list.
 + * @member:   the name of the list_struct within the struct.
 + *
 + * Iterate over list of given type, continuing from current position.
 + */
 +#define list_for_each_entry_from(pos, head, member)                   \
 +      for (; &pos->member != (head);  \
 +           pos = list_entry(pos->member.next, typeof(*pos), member))
 +
 +/**
 + * list_for_each_entry_safe - iterate over list of given type safe against removal of list entry
 + * @pos:      the type * to use as a loop cursor.
 + * @n:                another type * to use as temporary storage
 + * @head:     the head for your list.
 + * @member:   the name of the list_struct within the struct.
 + */
 +#define list_for_each_entry_safe(pos, n, head, member)                        \
 +      for (pos = list_entry((head)->next, typeof(*pos), member),      \
 +              n = list_entry(pos->member.next, typeof(*pos), member); \
 +           &pos->member != (head);                                    \
 +           pos = n, n = list_entry(n->member.next, typeof(*n), member))
 +
 +/**
 + * list_for_each_entry_safe_continue
 + * @pos:      the type * to use as a loop cursor.
 + * @n:                another type * to use as temporary storage
 + * @head:     the head for your list.
 + * @member:   the name of the list_struct within the struct.
 + *
 + * Iterate over list of given type, continuing after current point,
 + * safe against removal of list entry.
 + */
 +#define list_for_each_entry_safe_continue(pos, n, head, member)               \
 +      for (pos = list_entry(pos->member.next, typeof(*pos), member),          \
 +              n = list_entry(pos->member.next, typeof(*pos), member);         \
 +           &pos->member != (head);                                            \
 +           pos = n, n = list_entry(n->member.next, typeof(*n), member))
 +
 +/**
 + * list_for_each_entry_safe_from
 + * @pos:      the type * to use as a loop cursor.
 + * @n:                another type * to use as temporary storage
 + * @head:     the head for your list.
 + * @member:   the name of the list_struct within the struct.
 + *
 + * Iterate over list of given type from current point, safe against
 + * removal of list entry.
 + */
 +#define list_for_each_entry_safe_from(pos, n, head, member)                   \
 +      for (n = list_entry(pos->member.next, typeof(*pos), member);            \
 +           &pos->member != (head);                                            \
 +           pos = n, n = list_entry(n->member.next, typeof(*n), member))
 +
 +/**
 + * list_for_each_entry_safe_reverse
 + * @pos:      the type * to use as a loop cursor.
 + * @n:                another type * to use as temporary storage
 + * @head:     the head for your list.
 + * @member:   the name of the list_struct within the struct.
 + *
 + * Iterate backwards over list of given type, safe against removal
 + * of list entry.
 + */
 +#define list_for_each_entry_safe_reverse(pos, n, head, member)                \
 +      for (pos = list_entry((head)->prev, typeof(*pos), member),      \
 +              n = list_entry(pos->member.prev, typeof(*pos), member); \
 +           &pos->member != (head);                                    \
 +           pos = n, n = list_entry(n->member.prev, typeof(*n), member))
 +
 +
 +#endif
Simple merge
@@@ -568,8 -585,4 +585,11 @@@ typedef struct s_PXENV_UNLOAD_STACK 
  #define PXENV_STATUS_LOADER_UNDI_START                         0xca
  #define PXENV_STATUS_LOADER_BC_START                   0xcb
  
 +int __weak pxe_call(int, void *);
 +void __weak unload_pxe(uint16_t flags);
 +uint32_t __weak dns_resolv(const char *);
 +
++uint32_t __weak SendCookies;
++void __weak http_bake_cookies(void);
++
  #endif /* _SYSLINUX_PXE_API_H */
@@@ -108,70 -67,85 +108,70 @@@ LIBCONSOLE_OBJS = 
        \
        sys/ansi.o                                                      \
        \
 -      sys/libansi.o                                                   \
 -      \
 -      sys/gpxe.o                                                      \
 -      \
 -      sys/ansicon_write.o sys/ansiserial_write.o                      \
 -      \
 -      sys/vesacon_write.o sys/vesaserial_write.o                      \
 -      sys/vesa/initvesa.o sys/vesa/drawtxt.o  sys/vesa/background.o   \
 -      sys/vesa/alphatbl.o sys/vesa/screencpy.o sys/vesa/fmtpixel.o    \
 -      sys/vesa/i915resolution.o                                       \
 -      \
 -      pci/cfgtype.o pci/scan.o pci/bios.o                             \
 -      pci/readb.o pci/readw.o pci/readl.o                             \
 -      pci/writeb.o pci/writew.o pci/writel.o                          \
 -      \
 -      zlib/adler32.o zlib/compress.o zlib/crc32.o                     \
 -      zlib/uncompr.o zlib/deflate.o zlib/trees.o zlib/zutil.o         \
 -      zlib/inflate.o zlib/infback.o zlib/inftrees.o zlib/inffast.o    \
 -      \
 -      libpng/png.o libpng/pngset.o libpng/pngget.o libpng/pngrutil.o  \
 -      libpng/pngtrans.o libpng/pngwutil.o libpng/pngread.o            \
 -      libpng/pngrio.o libpng/pngwio.o libpng/pngwrite.o               \
 -      libpng/pngrtran.o libpng/pngwtran.o libpng/pngmem.o             \
 -      libpng/pngerror.o libpng/pngpread.o                             \
 +      sys/ansicon_write.o sys/ansiserial_write.o      \
        \
 -      jpeg/tinyjpeg.o jpeg/jidctflt.o jpeg/decode1.o jpeg/decode3.o   \
 -      jpeg/grey.o jpeg/yuv420p.o                                      \
 -      jpeg/rgb24.o jpeg/bgr24.o                                       \
 -      jpeg/rgba32.o jpeg/bgra32.o                                     \
 -      \
 -      sys/x86_init_fpu.o math/pow.o math/strtod.o                     \
 +      syslinux/serial.o
 +
 +LIBOTHER_OBJS = \
 +      atoi.o atol.o atoll.o calloc.o creat.o          \
 +      fgets.o fprintf.o fputc.o       \
 +      putchar.o                               \
 +      getopt.o getopt_long.o                                          \
 +      lrand48.o stack.o memccpy.o memchr.o            \
 +      mempcpy.o memmem.o memmove.o memswap.o  \
 +      perror.o qsort.o seed48.o \
 +      srand48.o sscanf.o strcasecmp.o                                 \
 +      strerror.o errlist.o            \
 +      strnlen.o                                                       \
 +      strncat.o strndup.o             \
 +      stpncpy.o                                               \
-       strntoimax.o strntoumax.o strsep.o strspn.o strstr.o    \
-       strtoimax.o strtok.o strtol.o strtoll.o strtoul.o strtoull.o    \
++      strntoimax.o strsep.o strspn.o strstr.o                         \
++      strtoimax.o strtok.o strtol.o strtoll.o strtoull.o              \
 +      strtoumax.o vprintf.o vsprintf.o                \
 +      asprintf.o vasprintf.o                  \
 +      vsscanf.o                                                       \
 +      skipspace.o                                                     \
 +      chrreplace.o                                                    \
 +      bufprintf.o                                                     \
 +      inet.o dhcppack.o dhcpunpack.o                                  \
 +      strreplace.o                                                    \
 +      lstrdup.o                                               \
        \
 -      syslinux/idle.o syslinux/reboot.o                               \
 -      syslinux/features.o syslinux/config.o syslinux/serial.o         \
 -      syslinux/ipappend.o syslinux/dsinfo.o syslinux/version.o        \
 -      syslinux/keyboard.o                                             \
 +      suffix_number.o                                                 \
        \
 -      syslinux/memscan.o                                              \
 +      getcwd.o fdopendir.o    \
        \
 -      syslinux/addlist.o syslinux/freelist.o syslinux/memmap.o        \
 -      syslinux/movebits.o syslinux/shuffle.o syslinux/shuffle_pm.o    \
 -      syslinux/shuffle_rm.o syslinux/zonelist.o                       \
 -      syslinux/dump_mmap.o syslinux/dump_movelist.o                   \
 +      sys/line_input.o                                \
 +      sys/colortable.o sys/screensize.o                               \
        \
 -      syslinux/run_default.o syslinux/run_command.o                   \
 -      syslinux/cleanup.o syslinux/localboot.o syslinux/runimage.o     \
 +      sys/stdcon_read.o sys/stdcon_write.o sys/rawcon_read.o          \
 +      sys/rawcon_write.o              \
 +      sys/null_read.o sys/null_write.o sys/serial_write.o             \
        \
 -      syslinux/loadfile.o syslinux/floadfile.o syslinux/zloadfile.o   \
 +      sys/xserial_write.o                                             \
        \
 -      syslinux/load_linux.o syslinux/initramfs.o                      \
 -      syslinux/initramfs_file.o syslinux/initramfs_loadfile.o         \
 -      syslinux/initramfs_archive.o                                    \
 +      sys/ansi.o                                                      \
        \
 -      syslinux/pxe_get_cached.o syslinux/pxe_get_nic.o                \
 -      syslinux/pxe_dns.o                                              \
 +      sys/ansicon_write.o sys/ansiserial_write.o                      \
        \
 -      syslinux/adv.o syslinux/advwrite.o syslinux/getadv.o            \
 -      syslinux/setadv.o                                               \
 +      pci/cfgtype.o pci/scan.o pci/bios.o                                     \
 +      pci/readb.o pci/readw.o pci/readl.o                     \
 +      pci/writeb.o pci/writew.o pci/writel.o  \
        \
 -      syslinux/video/fontquery.o syslinux/video/forcetext.o           \
 -      syslinux/video/reportmode.o                                     \
 +      sys/x86_init_fpu.o math/pow.o math/strtod.o                     \
 +      syslinux/disk.o                                                 \
        \
 -      syslinux/disk.o
 +      syslinux/setup_data.o
  
 -# These are the objects which are also imported into the core
 -LIBCOREOBJS =         \
 -      memcpy.o mempcpy.o memset.o memcmp.o memmove.o                  \
 -      strlen.o stpcpy.o strcpy.o strcmp.o strlcpy.o strlcat.o         \
 -      strchr.o strncmp.o strncpy.o                                    \
 -      ctypes.o strtoul.o strntoumax.o                                 \
 -      \
 -      asprintf.o snprintf.o sprintf.o vsnprintf.o                     \
 -      \
 -      dprintf.o vdprintf.o                                            \
 -      \
 -      zalloc.o strdup.o                                               \
 -      \
 -      sys/intcall.o sys/farcall.o sys/cfarcall.o sys/zeroregs.o       \
 -      \
 +CORELIBOBJS = \
 +      memcpy.o memset.o memcmp.o printf.o strncmp.o vfprintf.o        \
 +      strlen.o vsnprintf.o snprintf.o stpcpy.o strcmp.o strdup.o      \
 +      strcpy.o strncpy.o setjmp.o fopen.o fread.o fread2.o puts.o     \
 +      sprintf.o strlcat.o strchr.o strlcpy.o strncasecmp.o ctypes.o   \
-       fputs.o fwrite2.o fwrite.o fgetc.o fclose.o errno.o lmalloc.o   \
-       sys/err_read.o sys/err_write.o sys/null_read.o                  \
++      fputs.o fwrite2.o fwrite.o fgetc.o fclose.o lmalloc.o strtoul.o \
++      sys/err_read.o sys/err_write.o sys/null_read.o strntoumax.o     \
 +      sys/stdcon_write.o                                              \
 +      syslinux/memscan.o strrchr.o strcat.o                           \
        libgcc/__ashldi3.o libgcc/__udivdi3.o                           \
        libgcc/__negdi2.o libgcc/__ashrdi3.o libgcc/__lshrdi3.o         \
        libgcc/__muldi3.o libgcc/__udivmoddi4.o libgcc/__umoddi3.o      \
diff --cc com32/lib/errno.c
index f280e30,f280e30..0000000
deleted file mode 100644,100644
+++ /dev/null
@@@ -1,7 -1,7 +1,0 @@@
--/*
-- * errno.c
-- *
-- */
--#include <errno.h>
--
--int errno;
Simple merge
@@@ -65,7 -61,7 +65,7 @@@ int open(const char *pathname, int flag
  
      fp = &__file_info[fd];
  
-     handle = open_file(pathname, &fp->i.fd);
 -    handle = __com32.cs_pm->open_file(pathname, flags, &fp->i.fd);
++    handle = open_file(pathname, flags, &fp->i.fd);
      if (handle < 0) {
        close(fd);
        errno = ENOENT;
   * Get ipappend strings
   */
  
+ #include <syslinux/sysappend.h>
  #include <syslinux/config.h>
+ #include <syslinux/pmapi.h>
  #include <klibc/compiler.h>
 -#include <com32.h>
 +#include <core.h>
  
  struct syslinux_ipappend_strings __syslinux_ipappend_strings;
- static const char *syslinux_ipappend_string_list[32];
  
  void __constructor __syslinux_get_ipappend_strings(void)
  {
-     unsigned int i;
-     __syslinux_ipappend_strings.count = (size_t)numIPAppends;
-     __syslinux_ipappend_strings.ptr = syslinux_ipappend_string_list;
-     for (i = 0; i < (size_t)numIPAppends; i++)
-       syslinux_ipappend_string_list[i] = (const char *)(size_t)IPAppends[i];
 -    __syslinux_ipappend_strings.count = __com32.cs_pm->sysappend_count;
 -    __syslinux_ipappend_strings.ptr   = __com32.cs_pm->sysappend_strings;
++    __syslinux_ipappend_strings.count = SYSAPPEND_MAX,
++    __syslinux_ipappend_strings.ptr   = sysappend_strings;
  }
  #include <stdlib.h>
  #include <string.h>
  #include <syslinux/boot.h>
 -#include <com32.h>
 +#include <syslinux/config.h>
 +#include <core.h>
  
- extern unsigned int ipappend;
  void syslinux_run_kernel_image(const char *filename, const char *cmdline,
                               uint32_t ipappend_flags, uint32_t type)
  {
 -    static com32sys_t ireg;
 -    char *bbfilename = NULL;
      char *bbcmdline  = NULL;
 +    size_t len;
 +    int rv;
  
 -    bbfilename = lstrdup(filename);
 -    if (!bbfilename)
 -      goto fail;
 -
 -    bbcmdline = lstrdup(cmdline);
 +    /* +2 for NULL and space */
 +    len = strlen(filename) + strlen(cmdline) + 2;
 +    bbcmdline = malloc(len);
      if (!bbcmdline)
 -      goto fail;
 -
 -
 -    ireg.eax.w[0] = 0x0016;
 -    ireg.ds = SEG(bbfilename);
 -    /* ireg.esi.w[0] = OFFS(bbfilename); */
 -    ireg.es = SEG(bbcmdline);
 -    /* ireg.ebx.w[0] = OFFS(bbcmdline); */
 -    ireg.ecx.l = ipappend_flags;
 -    ireg.edx.l = type;
 +      return;
  
 -    __intcall(0x22, &ireg, 0);
 +    rv = snprintf(bbcmdline, len, "%s %s", filename, cmdline);
 +    if (rv == -1 || (size_t)rv >= len)
 +      return;
  
-     if (syslinux_filesystem() == SYSLINUX_FS_PXELINUX)
-       ipappend = ipappend_flags;
-     execute(bbcmdline, type);
 -fail:
 -    if (bbcmdline)
 -      lfree(bbcmdline);
 -    if (bbfilename)
 -      lfree(bbfilename);
++    SysAppends = ipappend_flags;
++    execute(bbcmdline, type, true);
  }
@@@ -1159,13 -1157,9 +1159,13 @@@ int main(int argc, char *argv[]
        printf("\033[?25h\033[%d;1H\033[0m", END_ROW);
  
        if (cmdline) {
 -          execute(cmdline, KT_NONE);
 -          if (cm->onerror)
 -              execute(cm->onerror, KT_NONE);
 +          uint32_t type = parse_image_type(cmdline);
 +
-           execute(cmdline, type);
++          execute(cmdline, type, false);
 +          if (cm->onerror) {
 +              type = parse_image_type(cm->onerror);
-               execute(cm->onerror, type);
++              execute(cm->onerror, type, true);
 +          }
        } else {
            return 0;           /* Exit */
        }
Simple merge
@@@ -24,8 -24,7 +24,8 @@@ MODULES         = config.c32 ethersel.c32 dmi
            meminfo.c32 sdi.c32 sanboot.c32 ifcpu64.c32 vesainfo.c32 \
            kbdmap.c32 cmd.c32 vpdtest.c32 host.c32 ls.c32 gpxecmd.c32 \
            ifcpu.c32 cpuid.c32 cat.c32 pwd.c32 ifplop.c32 zzjson.c32 \
 -          whichsys.c32 cptime.c32 prdhcp.c32
 +          whichsys.c32 prdhcp.c32 pxechn.c32 kontron_wdt.c32 ifmemdsk.c32 \
-           hexdump.c32 poweroff.c32
++          hexdump.c32 poweroff.c32 cptime.c32
  
  TESTFILES =
  
diff --cc core/Makefile
@@@ -25,7 -25,8 +25,8 @@@ include $(MAKEDIR)/embedded.m
  -include $(topdir)/version.mk
  
  OPTFLAGS =
- INCLUDES = -I./include -I$(com32)/include -I$(com32)/lib
 -INCLUDES = -I./include -I$(com32)/include \
++INCLUDES = -I./include -I$(com32)/include -I$(com32)/lib \
+       -I./lwip/src/include -I./lwip/src/include/ipv4
  
  # This is very similar to cp437; technically it's for Norway and Denmark,
  # but it's unlikely the characters that are different will be used in
@@@ -49,13 -50,9 +50,13 @@@ ALLSRC    = $(NASMSRC) $(NASMHDR) $(CSR
  COBJ   := $(patsubst %.c,%.o,$(CSRC))
  SOBJ   := $(patsubst %.S,%.o,$(SSRC))
  
 -LIB    = libcore.a
 -LIBS   = $(LIB) $(com32)/lib/libcomcore.a $(LIBGCC)
 -LIBOBJS        = $(COBJ) $(SOBJ)
 +# Don't include console objects
- COBJS  = $(filter-out rawcon.o plaincon.o,$(COBJ))
++COBJS  = $(filter-out ./rawcon.o ./plaincon.o,$(COBJ))
 +
 +LIB    = libcom32.a
 +LIBS   = $(LIB) --whole-archive $(com32)/lib/libcom32core.a
 +LIBDEP   = $(filter-out -% %start%,$(LIBS))
 +LIBOBJS        = $(COBJS) $(SOBJ)
  
  NASMDEBUG = -g -F dwarf
  NASMOPT  += $(NASMDEBUG)
@@@ -142,9 -147,10 +147,10 @@@ core_syscall
  ; followed by the return CS:IP and the CS:IP of the target function.
  ; The value of IF is copied from the calling routine.
  ;
 -              global core_cfarcall
 +              global core_cfarcall:function hidden
  core_cfarcall:
                pushfd                          ; Save IF among other things...
+               inc dword [CallbackCtr]
                push ebx
                push ebp
                push esi
diff --cc core/diskfs.inc
@@@ -83,55 -65,32 +64,44 @@@ trackbuf   resb trackbufsize       ; Track buff
                stosw
  
  ;
 -; Now we're all set to start with our *real* business.        First load the
 -; configuration file (if any) and parse it.
 +; If we get to this point ldlinux.c32 failed to run. There's nothing
 +; left to do but inform that user that something went wrong.
  ;
 -; In previous versions I avoided using 32-bit registers because of a
 -; rumour some BIOSes clobbered the upper half of 32-bit registers at
 -; random.  I figure, though, that if there are any of those still left
 -; they probably won't be trying to install Linux on them...
 +enter_command:
 +auto_boot:
 +              jmp kaboom
 +
 +              section .bss16
 +              alignb 4
 +ThisKbdTo     resd 1                  ; Temporary holder for KbdTimeout
 +ThisTotalTo   resd 1                  ; Temporary holder for TotalTimeout
 +KernelExtPtr  resw 1                  ; During search, final null pointer
 +FuncFlag      resb 1                  ; Escape sequences received from keyboard
 +KernelType    resb 1                  ; Kernel type, from vkernel, if known
 +              global KernelName
 +KernelName    resb FILENAME_MAX       ; Mangled name for kernel
-               section .data16
-               global IPAppends, numIPAppends
- %if IS_PXELINUX
-               extern IPOption
-               alignz 2
- IPAppends     dw IPOption
- numIPAppends  equ ($-IPAppends)/2
- %else
- IPAppends     equ 0
- numIPAppends  equ 0
- %endif
 +
 +              section .text16
  ;
 -; The code is still ripe with 16-bitisms, though.  Not worth the hassle
 -; to take'm out.  In fact, we may want to put them back if we're going
 -; to boot ELKS at some point.
 +; COMBOOT-loading code
  ;
 +%include "comboot.inc"
 +%include "com32.inc"
  
  ;
 -; Load configuration file
 +; Boot sector loading code
  ;
 -              pm_call pm_load_config
 -              jz no_config_file
  
  ;
 -; Now we have the config file open.  Parse the config file and
 -; run the user interface.
 +; Abort loading code
  ;
 -%include "ui.inc"
  
  ;
 +; Hardware cleanup common code
 +;
 +
 +%include "localboot.inc"
 +
  ;
  ; kaboom2: once everything is loaded, replace the part of kaboom
  ;        starting with "kaboom.patch" with this part
diff --cc core/errno.c
index 0000000,d013499..adfd92f
mode 000000,100644..100644
--- /dev/null
@@@ -1,0 -1,3 +1,4 @@@
 -int errno;
++#include <klibc/compiler.h>
+ #include <errno.h>
++__export int errno;
diff --cc core/extern.inc
        ; newconfig.c
        extern pm_is_config_file
  
 -      ; mem/init.c
 -      extern MallocStart, mem_init
 -
 -      ; sysappend.c
 -      extern do_sysappend, print_sysappend
 -
 -      ; dmi.c
 -      extern dmi_init
 -
+ %ifdef DEBUG
+       ; debug.c
+       extern pm_debug_msg
+   %macro dprint 1+
+       push ax
+       call %%fwd
+       db %1
+       db 0
+ %%fwd:        pop ax
+       pm_call pm_debug_msg
+       pop ax
+   %endmacro
+ %else
+   %macro dprint 1+
+   %endmacro
+ %endif
  %if IS_PXELINUX
        ; pxe.c
-       extern unload_pxe, reset_pxe
+       extern unload_pxe, reset_pxe, http_bake_cookies
  %endif
  
 +      ; plaincon.c
 +      extern pm_writechr
 +
 +      ; cleanup.c
 +      extern cleanup_hardware
 +
 +      ; writestr.c
 +      extern pm_writestr, crlf
 +
 +      ; writehex.c
 +      extern pm_writehex2, pm_writehex4, pm_writehex8
 +
 +      ; graphics.c
 +      extern syslinux_force_text_mode, vgashowcursor, vgahidecursor, pm_using_vga
 +
 +      ; conio.c
 +      extern pm_pollchar, pm_write_serial, pm_serialcfg
 +
 +      ; font.c
 +      extern pm_getchar, pm_adjust_screen, pm_userfont
 +
 +      ; localboot.c
 +      extern pm_local_boot
 +
  %endif ; EXTERN_INC
diff --cc core/fs/chdir.c
@@@ -1,7 -1,7 +1,8 @@@
  #include <stdio.h>
  #include <stdbool.h>
  #include <string.h>
 +#include <dprintf.h>
+ #include <fcntl.h>
  #include "fs.h"
  #include "cache.h"
  
@@@ -17,73 -17,60 +18,73 @@@ void pm_realpath(com32sys_t *regs
      realpath(dst, src, FILENAME_MAX);
  }
  
 -#define EMIT(x)                               \
 -do {                                  \
 -    if (++n < bufsize)                        \
 -      *q++ = (x);                     \
 -} while (0)
 -
 -static size_t join_paths(char *dst, size_t bufsize,
 -                       const char *s1, const char *s2)
 +static size_t copy_string(char *buf, size_t ix, size_t bufsize, const char *src)
  {
 -    const char *list[2];
 -    int i;
      char c;
 -    const char *p;
 -    char *q  = dst;
 -    size_t n = 0;
 -    bool slash = false;
 -    
 -    list[0] = s1;
 -    list[1] = s2;
 -
 -    for (i = 0; i < 2; i++) {
 -      p = list[i];
 -
 -      while ((c = *p++)) {
 -          if (c == '/') {
 -              if (!slash)
 -                  EMIT(c);
 -              slash = true;
 -          } else {
 -              EMIT(c);
 -              slash = false;
 -          }
 -      }
 +
 +    while ((c = *src++)) {
 +      if (ix+1 < bufsize)
 +          buf[ix] = c;
 +      ix++;
      }
  
 -    if (bufsize)
 -      *q = '\0';
 +    if (ix < bufsize)
 +      buf[ix] = '\0';
  
 -    return n;
 +    return ix;
  }
  
 -size_t realpath(char *dst, const char *src, size_t bufsize)
 +static size_t generic_inode_to_path(struct inode *inode, char *dst, size_t bufsize)
  {
 +    size_t s = 0;
 +
 +    dprintf("inode %p name %s\n", inode, inode->name);
 +
 +    if (inode->parent) {
 +      if (!inode->name)       /* Only the root should have no name */
 +          return -1;
 +
 +      s = generic_inode_to_path(inode->parent, dst, bufsize);
 +      if (s == (size_t)-1)
 +          return s;           /* Error! */
 +
 +      s = copy_string(dst, s, bufsize, "/");
 +      s = copy_string(dst, s, bufsize, inode->name);
 +    }
 +
 +    return s;
 +}
 +
 +__export size_t realpath(char *dst, const char *src, size_t bufsize)
 +{
 +    int rv;
 +    struct file *file;
 +    size_t s;
 +
 +    dprintf("realpath: input: %s\n", src);
 +
      if (this_fs->fs_ops->realpath) {
 -      return this_fs->fs_ops->realpath(this_fs, dst, src, bufsize);
 +      s = this_fs->fs_ops->realpath(this_fs, dst, src, bufsize);
      } else {
-       rv = searchdir(src);
 -      /* Filesystems with "common" pathname resolution */
 -      return join_paths(dst, bufsize, 
 -                        src[0] == '/' ? "" : this_fs->cwd_name,
 -                        src);
++      rv = searchdir(src, O_RDONLY);
 +      if (rv < 0) {
 +          dprintf("realpath: searchpath failure\n");
 +          return -1;
 +      }
 +
 +      file = handle_to_file(rv);
 +      s = generic_inode_to_path(file->inode, dst, bufsize);
 +      if (s == 0)
 +          s = copy_string(dst, 0, bufsize, "/");
 +
 +      _close_file(file);
      }
 +
 +    dprintf("realpath: output: %s\n", dst);
 +    return s;
  }
  
 -int chdir(const char *src)
 +__export int chdir(const char *src)
  {
      int rv;
      struct file *file;
@@@ -407,13 -405,13 +405,12 @@@ struct device * device_init(uint8_t dev
                            uint32_t MaxTransfer)
  {
      static struct device dev;
-     static __hugebss char diskcache[128*1024];
 -    extern size_t HighMemSize, MallocStart;
  
      dev.disk = disk_init(devno, cdrom, part_start,
                         bsHeads, bsSecPerTrack, MaxTransfer);
  
-     dev.cache_data = diskcache;
-     dev.cache_size = sizeof diskcache;
 -    dev.cache_size = min(128*1024, (MallocStart-HighMemSize) >> 1);
++    dev.cache_size = 128*1024;
+     dev.cache_data = malloc(dev.cache_size);
  
      return &dev;
  }
diff --cc core/fs/fs.c
@@@ -2,10 -1,8 +2,11 @@@
  #include <stdio.h>
  #include <stdbool.h>
  #include <string.h>
 +#include <unistd.h>
+ #include <fcntl.h>
  #include <dprintf.h>
 +#include "core.h"
 +#include "dev.h"
  #include "fs.h"
  #include "cache.h"
  
@@@ -138,17 -182,32 +139,17 @@@ size_t pmapi_read_file(uint16_t *handle
      return bytes_read;
  }
  
- int searchdir(const char *name)
 -void pm_searchdir(com32sys_t *regs)
 -{
 -    char *name = MK_PTR(regs->ds, regs->edi.w[0]);
 -    int rv;
 -
 -    rv = searchdir(name, O_RDONLY);
 -    if (rv < 0) {
 -      regs->esi.w[0]  = 0;
 -      regs->eax.l     = 0;
 -      regs->eflags.l |= EFLAGS_ZF;
 -    } else {
 -      regs->esi.w[0]  = rv;
 -      regs->eax.l     = handle_to_file(rv)->inode->size;
 -      regs->eflags.l &= ~EFLAGS_ZF;
 -    }
 -}
 -
+ int searchdir(const char *name, int flags)
  {
 -    struct inode *inode = NULL;
 -    struct inode *parent = NULL;
 +    static char root_name[] = "/";
      struct file *file;
 -    char *pathbuf = NULL;
 -    char *part, *p, echar;
 +    char *path, *inode_name, *next_inode_name;
 +    struct inode *tmp, *inode = NULL;
      int symlink_count = MAX_SYMLINK_CNT;
  
 +    dprintf("searchdir: %s  root: %p  cwd: %p\n",
 +          name, this_fs->root, this_fs->cwd);
 +
      if (!(file = alloc_file()))
        goto err_no_close;
      file->fs = this_fs;
@@@ -336,16 -344,14 +337,16 @@@ err_no_close
      return -1;
  }
  
- __export int open_file(const char *name, struct com32_filedata *filedata)
 -int open_file(const char *name, int flags, struct com32_filedata *filedata)
++__export int open_file(const char *name, int flags, struct com32_filedata *filedata)
  {
      int rv;
      struct file *file;
      char mangled_name[FILENAME_MAX];
  
 +    dprintf("open_file %s\n", name);
 +
      mangle_name(mangled_name, name);
-     rv = searchdir(mangled_name);
+     rv = searchdir(mangled_name, flags);
  
      if (rv < 0)
        return rv;
@@@ -1,4 -1,4 +1,5 @@@
  #include <dprintf.h>
++#include <fcntl.h>
  #include <stdio.h>
  #include <string.h>
  #include <core.h>
@@@ -27,10 -25,11 +28,10 @@@ int search_dirs(struct com32_filedata *
                     "%s%s%s",
                     sd, (*sd && sd[strlen(sd)-1] == '/') ? "" : "/",
                     sf);
 -          realpath(ConfigName, confignamebuf, FILENAME_MAX);
 -          regs.edi.w[0] = OFFS_WRT(ConfigName, 0);
 -          dprintf("Config search: %s\n", ConfigName);
 -          call16(core_open, &regs, &regs);
 -          if (!(regs.eflags.l & EFLAGS_ZF)) {
 +          if (realpath(realname, namebuf, FILENAME_MAX) == (size_t)-1)
 +              continue;
 +          dprintf("Config search: %s\n", realname);
-           if (open_file(realname, filedata) >= 0) {
++          if (open_file(realname, O_RDONLY, filedata) >= 0) {
                chdir(sd);
                return 0;       /* Got it */
            }
Simple merge
@@@ -163,181 -82,51 +82,51 @@@ static bool parse_dotquad(const char *i
  }
  
  /*
-  * Actual resolver function
-  * Points to a null-terminated or :-terminated string in _name_
-  * and returns the ip addr in _ip_ if it exists and can be found.
-  * If _ip_ = 0 on exit, the lookup failed. _name_ will be updated
+  * Actual resolver function.
   *
-  * XXX: probably need some caching here.
+  * Points to a null-terminated in _name_ and returns the ip addr in
+  * _ip_ if it exists and can be found.  If _ip_ = 0 on exit, the
+  * lookup failed. _name_ will be updated
   */
 -uint32_t dns_resolv(const char *name)
 +__export uint32_t dns_resolv(const char *name)
  {
-     static char __lowmem DNSSendBuf[PKTBUF_SIZE];
-     static char __lowmem DNSRecvBuf[PKTBUF_SIZE];
-     char *p;
-     int err;
-     int dots;
-     int same;
-     int rd_len;
-     int ques, reps;    /* number of questions and replies */
-     uint8_t timeout;
-     const uint8_t *timeout_ptr = TimeoutTable;
-     uint32_t oldtime;
-     uint32_t srv;
-     uint32_t *srv_ptr;
-     struct dnshdr *hd1 = (struct dnshdr *)DNSSendBuf;
-     struct dnshdr *hd2 = (struct dnshdr *)DNSRecvBuf;
-     struct dnsquery *query;
-     struct dnsrr *rr;
-     static __lowmem struct s_PXENV_UDP_WRITE udp_write;
-     static __lowmem struct s_PXENV_UDP_READ  udp_read;
-     uint16_t local_port;
-     uint32_t result = 0;
-     /* Make sure we have at least one valid DNS server */
-     if (!dns_server[0])
+     err_t err;
+     struct ip_addr ip;
+     char fullname[512];
+     /*
+      * Return failure on an empty input... this can happen during
+      * some types of URL parsing, and this is the easiest place to
+      * check for it.
+      */
+     if (!name || !*name)
        return 0;
  
-     /* Get a local port number */
-     local_port = get_port();
-     /* First, fill the DNS header struct */
-     hd1->id++;                      /* New query ID */
-     hd1->flags   = htons(0x0100);   /* Recursion requested */
-     hd1->qdcount = htons(1);        /* One question */
-     hd1->ancount = 0;               /* No answers */
-     hd1->nscount = 0;               /* No NS */
-     hd1->arcount = 0;               /* No AR */
-     p = DNSSendBuf + sizeof(struct dnshdr);
-     dots = dns_mangle(&p, name);   /* store the CNAME */
-     if (!dots) {
-         p--; /* Remove final null */
-         /* Uncompressed DNS label set so it ends in null */
-         p = stpcpy(p, LocalDomain);
-     }
-     /* Fill the DNS query packet */
-     query = (struct dnsquery *)p;
-     query->qtype  = htons(TYPE_A);
-     query->qclass = htons(CLASS_IN);
-     p += sizeof(struct dnsquery);
-     /* Now send it to name server */
-     timeout_ptr = TimeoutTable;
-     timeout = *timeout_ptr++;
-     srv_ptr = dns_server;
-     while (timeout) {
-       srv = *srv_ptr++;
-       if (!srv) {
-           srv_ptr = dns_server;
-           srv = *srv_ptr++;
-       }
-         udp_write.status      = 0;
-         udp_write.ip          = srv;
-         udp_write.gw          = gateway(srv);
-         udp_write.src_port    = local_port;
-         udp_write.dst_port    = DNS_PORT;
-         udp_write.buffer_size = p - DNSSendBuf;
-         udp_write.buffer      = FAR_PTR(DNSSendBuf);
-         err = pxe_call(PXENV_UDP_WRITE, &udp_write);
-         if (err || udp_write.status)
-             continue;
-         oldtime = jiffies();
-       do {
-           if (jiffies() - oldtime >= timeout)
-               goto again;
-             udp_read.status      = 0;
-             udp_read.src_ip      = srv;
-             udp_read.dest_ip     = IPInfo.myip;
-             udp_read.s_port      = DNS_PORT;
-             udp_read.d_port      = local_port;
-             udp_read.buffer_size = PKTBUF_SIZE;
-             udp_read.buffer      = FAR_PTR(DNSRecvBuf);
-             err = pxe_call(PXENV_UDP_READ, &udp_read);
-       } while (err || udp_read.status || hd2->id != hd1->id);
+     /* If it is a valid dot quad, just return that value */
+     if (parse_dotquad(name, &ip.addr))
+       return ip.addr;
  
-         if ((hd2->flags ^ 0x80) & htons(0xf80f))
-             goto badness;
-         ques = htons(hd2->qdcount);   /* Questions */
-         reps = htons(hd2->ancount);   /* Replies   */
-         p = DNSRecvBuf + sizeof(struct dnshdr);
-         while (ques--) {
-             p = dns_skiplabel(p); /* Skip name */
-             p += 4;               /* Skip question trailer */
-         }
-         /* Parse the replies */
-         while (reps--) {
-             same = dns_compare(DNSSendBuf + sizeof(struct dnshdr),
-                              p, DNSRecvBuf);
-             p = dns_skiplabel(p);
-             rr = (struct dnsrr *)p;
-             rd_len = ntohs(rr->rdlength);
-             if (same && ntohs(rr->class) == CLASS_IN) {
-               switch (ntohs(rr->type)) {
-               case TYPE_A:
-                   if (rd_len == 4) {
-                       result = *(uint32_t *)rr->rdata;
-                       goto done;
-                   }
-                   break;
-               case TYPE_CNAME:
-                   dns_copylabel(DNSSendBuf + sizeof(struct dnshdr),
-                                 rr->rdata, DNSRecvBuf);
-                   /*
-                    * We should probably rescan the packet from the top
-                    * here, and technically we might have to send a whole
-                    * new request here...
-                    */
-                   break;
-               default:
-                   break;
-               }
-           }
-             /* not the one we want, try next */
-             p += sizeof(struct dnsrr) + rd_len;
-         }
-     badness:
-         /*
-          *
-          ; We got back no data from this server.
-          ; Unfortunately, for a recursive, non-authoritative
-          ; query there is no such thing as an NXDOMAIN reply,
-          ; which technically means we can't draw any
-          ; conclusions.  However, in practice that means the
-          ; domain doesn't exist.  If this turns out to be a
-          ; problem, we may want to add code to go through all
-          ; the servers before giving up.
-          ; If the DNS server wasn't capable of recursion, and
-          ; isn't capable of giving us an authoritative reply
-          ; (i.e. neither AA or RA set), then at least try a
-          ; different setver...
-         */
-         if (hd2->flags == htons(0x480))
-             continue;
-         break; /* failed */
+     /* Make sure we have at least one valid DNS server */
+     if (!dns_getserver(0).addr)
+       return 0;
  
-     again:
-       continue;
+     /* Is it a local (unqualified) domain name? */
+     if (!strchr(name, '.') && LocalDomain[0]) {
+       snprintf(fullname, sizeof fullname, "%s.%s", name, LocalDomain);
+       name = fullname;
      }
  
- done:
-     free_port(local_port);    /* Return port number to the free pool */
+     err = netconn_gethostbyname(name, &ip);
+     if (err)
+       return 0;
  
-     return result;
+     return ip.addr;
  }
  
 -void pxe_dns_resolv(com32sys_t *regs)
+ /*
+  * the one should be called from ASM file
+  */
 +void pm_pxe_dns_resolv(com32sys_t *regs)
  {
      const char *name = MK_PTR(regs->ds, regs->esi.w[0]);
  
index 0000000,1fd87aa..d5022eb
mode 000000,100644..100644
--- /dev/null
@@@ -1,0 -1,400 +1,400 @@@
 -extern uint32_t SendCookies;
+ #include <syslinux/sysappend.h>
+ #include <ctype.h>
+ #include <lwip/api.h>
+ #include "pxe.h"
+ #include "../../../version.h"
+ #include "url.h"
+ #define HTTP_PORT     80
+ static bool is_tspecial(int ch)
+ {
+     bool tspecial = false;
+     switch(ch) {
+     case '(':  case ')':  case '<':  case '>':  case '@':
+     case ',':  case ';':  case ':':  case '\\': case '"':
+     case '/':  case '[':  case ']':  case '?':  case '=':
+     case '{':  case '}':  case ' ':  case '\t':
+       tspecial = true;
+       break;
+     }
+     return tspecial;
+ }
+ static bool is_ctl(int ch)
+ {
+     return ch < 0x20;
+ }
+ static bool is_token(int ch)
+ {
+     /* Can by antying except a ctl character or a tspecial */
+     return !is_ctl(ch) && !is_tspecial(ch);
+ }
+ static bool append_ch(char *str, size_t size, size_t *pos, int ch)
+ {
+     bool success = true;
+     if ((*pos + 1) >= size) {
+       *pos = 0;
+       success = false;
+     } else {
+       str[*pos] = ch;
+       str[*pos + 1] = '\0';
+       *pos += 1;
+     }
+     return success;
+ }
+ static size_t cookie_len, header_len;
+ static char *cookie_buf, *header_buf;
++__export uint32_t SendCookies = -1UL; /* Send all cookies */
+ static size_t http_do_bake_cookies(char *q)
+ {
+     static const char uchexchar[16] = "0123456789ABCDEF";
+     int i;
+     size_t n = 0;
+     const char *p;
+     char c;
+     bool first = true;
+     uint32_t mask = SendCookies;
+     for (i = 0; i < SYSAPPEND_MAX; i++) {
+       if ((mask & 1) && (p = sysappend_strings[i])) {
+           if (first) {
+               if (q) {
+                   strcpy(q, "Cookie: ");
+                   q += 8;
+               }
+               n += 8;
+               first = false;
+           }
+           if (q) {
+               strcpy(q, "_Syslinux_");
+               q += 10;
+           }
+           n += 10;
+           /* Copy string up to and including '=' */
+           do {
+               c = *p++;
+               if (q)
+                   *q++ = c;
+               n++;
+           } while (c != '=');
+           while ((c = *p++)) {
+               if (c == ' ') {
+                   if (q)
+                       *q++ = '+';
+                   n++;
+               } else if (is_token(c)) {
+                   if (q)
+                       *q++ = c;
+                   n++;
+               } else {
+                   if (q) {
+                       *q++ = '%';
+                       *q++ = uchexchar[c >> 4];
+                       *q++ = uchexchar[c & 15];
+                   }
+                   n += 3;
+               }
+           }
+           if (q)
+               *q++ = ';';
+           n++;
+       }
+       mask >>= 1;
+     }
+     if (!first) {
+       if (q) {
+           *q++ = '\r';
+           *q++ = '\n';
+       }
+       n += 2;
+     }
+     if (q)
+       *q = '\0';
+     
+     return n;
+ }
+ void http_bake_cookies(void)
+ {
+     if (cookie_buf)
+       free(cookie_buf);
+     cookie_len = http_do_bake_cookies(NULL);
+     cookie_buf = malloc(cookie_len+1);
+     if (!cookie_buf) {
+       cookie_len = 0;
+       return;
+     }
+     if (header_buf)
+       free(header_buf);
+     header_len = cookie_len + 6*FILENAME_MAX + 256;
+     header_buf = malloc(header_len);
+     if (!header_buf) {
+       header_len = 0;
+       return;                 /* Uh-oh... */
+     }
+     http_do_bake_cookies(cookie_buf);
+ }
+ static const struct pxe_conn_ops http_conn_ops = {
+     .fill_buffer      = tcp_fill_buffer,
+     .close            = tcp_close_file,
+     .readdir          = http_readdir,
+ };
+ void http_open(struct url_info *url, int flags, struct inode *inode,
+              const char **redir)
+ {
+     struct pxe_pvt_inode *socket = PVT(inode);
+     int header_bytes;
+     const char *next;
+     char field_name[20];
+     char field_value[1024];
+     size_t field_name_len, field_value_len;
+     err_t err;
+     enum state {
+       st_httpver,
+       st_stcode,
+       st_skipline,
+       st_fieldfirst,
+       st_fieldname,
+       st_fieldvalue,
+       st_skip_fieldname,
+       st_skip_fieldvalue,
+       st_eoh,
+     } state;
+     struct ip_addr addr;
+     static char location[FILENAME_MAX];
+     uint32_t content_length; /* same as inode->size */
+     size_t response_size;
+     int status;
+     int pos;
+     (void)flags;
+     if (!header_buf)
+       return;                 /* http is broken... */
+     /* This is a straightforward TCP connection after headers */
+     socket->ops = &http_conn_ops;
+     /* Reset all of the variables */
+     inode->size = content_length = -1;
+     /* Start the http connection */
+     socket->conn = netconn_new(NETCONN_TCP);
+     if (!socket->conn) {
+       printf("netconn_new failed\n");
+         return;
+     }
+     addr.addr = url->ip;
+     if (!url->port)
+       url->port = HTTP_PORT;
+     err = netconn_connect(socket->conn, &addr, url->port);
+     if (err) {
+       printf("netconn_connect error %d\n", err);
+       goto fail;
+     }
+     strcpy(header_buf, "GET /");
+     header_bytes = 5;
+     header_bytes += url_escape_unsafe(header_buf+5, url->path,
+                                     header_len - 5);
+     if (header_bytes >= header_len)
+       goto fail;              /* Buffer overflow */
+     header_bytes += snprintf(header_buf + header_bytes,
+                            header_len - header_bytes,
+                            " HTTP/1.0\r\n"
+                            "Host: %s\r\n"
+                            "User-Agent: Syslinux/" VERSION_STR "\r\n"
+                            "Connection: close\r\n"
+                            "%s"
+                            "\r\n",
+                            url->host, cookie_buf ? cookie_buf : "");
+     if (header_bytes >= header_len)
+       goto fail;              /* Buffer overflow */
+     err = netconn_write(socket->conn, header_buf,
+                       header_bytes, NETCONN_NOCOPY);
+     if (err) {
+       printf("netconn_write error %d\n", err);
+       goto fail;
+     }
+     /* Parse the HTTP header */
+     state = st_httpver;
+     pos = 0;
+     status = 0;
+     response_size = 0;
+     field_value_len = 0;
+     field_name_len = 0;
+     while (state != st_eoh) {
+       int ch = pxe_getc(inode);
+       /* Eof before I finish paring the header */
+       if (ch == -1)
+           goto fail;
+ #if 0
+         printf("%c", ch);
+ #endif
+       response_size++;
+       if (ch == '\r' || ch == '\0')
+           continue;
+       switch (state) {
+       case st_httpver:
+           if (ch == ' ') {
+               state = st_stcode;
+               pos = 0;
+           }
+           break;
+       case st_stcode:
+           if (ch < '0' || ch > '9')
+              goto fail;
+           status = (status*10) + (ch - '0');
+           if (++pos == 3)
+               state = st_skipline;
+           break;
+       case st_skipline:
+           if (ch == '\n')
+               state = st_fieldfirst;
+           break;
+       case st_fieldfirst:
+           if (ch == '\n')
+               state = st_eoh;
+           else if (isspace(ch)) {
+               /* A continuation line */
+               state = st_fieldvalue;
+               goto fieldvalue;
+           }
+           else if (is_token(ch)) {
+               /* Process the previous field before starting on the next one */
+               if (strcasecmp(field_name, "Content-Length") == 0) {
+                   next = field_value;
+                   /* Skip leading whitespace */
+                   while (isspace(*next))
+                       next++;
+                   content_length = 0;
+                   for (;(*next >= '0' && *next <= '9'); next++) {
+                       if ((content_length * 10) < content_length)
+                           break;
+                       content_length = (content_length * 10) + (*next - '0');
+                   }
+                   /* In the case of overflow or other error ignore
+                    * Content-Length.
+                    */
+                   if (*next)
+                       content_length = -1;
+               }
+               else if (strcasecmp(field_name, "Location") == 0) {
+                   next = field_value;
+                   /* Skip leading whitespace */
+                   while (isspace(*next))
+                       next++;
+                   strlcpy(location, next, sizeof location);
+               }
+               /* Start the field name and field value afress */
+               field_name_len = 1;
+               field_name[0] = ch;
+               field_name[1] = '\0';
+               field_value_len = 0;
+               field_value[0] = '\0';
+               state = st_fieldname;
+           }
+           else /* Bogus try to recover */
+               state = st_skipline;
+           break;
+       case st_fieldname:
+           if (ch == ':' ) {
+               state = st_fieldvalue;
+           }
+           else if (is_token(ch)) {
+               if (!append_ch(field_name, sizeof field_name, &field_name_len, ch))
+                   state = st_skip_fieldname;
+           }
+           /* Bogus cases try to recover */
+           else if (ch == '\n')
+               state = st_fieldfirst;
+           else
+               state = st_skipline;
+           break;
+        case st_fieldvalue:
+           if (ch == '\n')
+               state = st_fieldfirst;
+           else {
+           fieldvalue:
+               if (!append_ch(field_value, sizeof field_value, &field_value_len, ch))
+                   state = st_skip_fieldvalue;
+           }
+           break;
+       /* For valid fields whose names are longer than I choose to support. */
+       case st_skip_fieldname:
+           if (ch == ':')
+               state = st_skip_fieldvalue;
+           else if (is_token(ch))
+               state = st_skip_fieldname;
+           /* Bogus cases try to recover */
+           else if (ch == '\n')
+               state = st_fieldfirst;
+           else
+               state = st_skipline;
+           break;
+       /* For valid fields whose bodies are longer than I choose to support. */
+       case st_skip_fieldvalue:
+           if (ch == '\n')
+               state = st_fieldfirst;
+           break;
+       case st_eoh:
+          break; /* Should never happen */
+       }
+     }
+     if (state != st_eoh)
+       status = 0;
+     switch (status) {
+     case 200:
+       /*
+        * All OK, need to mark header data consumed and set up a file
+        * structure...
+        */
+       /* Treat the remainder of the bytes as data */
+       socket->tftp_filepos -= response_size;
+       break;
+     case 301:
+     case 302:
+     case 303:
+     case 307:
+       /* A redirect */
+       if (!location[0])
+           goto fail;
+       *redir = location;
+       goto fail;
+     default:
+       goto fail;
+       break;
+     }
+     return;
+ fail:
+     inode->size = 0;
+     tcp_close_file(inode);
+     return;
+ }
@@@ -2,13 -2,21 +2,22 @@@
  #include <stdio.h>
  #include <string.h>
  #include <core.h>
 +#include <bios.h>
  #include <fs.h>
  #include <minmax.h>
+ #include <fcntl.h>
  #include <sys/cpu.h>
+ #include <lwip/api.h>
+ #include <lwip/dns.h>
+ #include <lwip/tcpip.h>
+ #include <lwip/opt.h>
  #include "pxe.h"
+ #include "thread.h"
+ #include "url.h"
+ #include "tftp.h"
  
- #define GPXE 1
+ __lowmem t_PXENV_UNDI_GET_INFORMATION pxe_undi_info;
+ __lowmem t_PXENV_UNDI_GET_IFACE_INFO  pxe_undi_iface;
  
  static uint16_t real_base_mem;           /* Amount of DOS memory after freeing */
  
@@@ -254,8 -119,9 +120,9 @@@ static int gendotquad(char *dst, uint32
   * the ASM pxenv function wrapper, return 1 if error, or 0
   *
   */
 -int pxe_call(int opcode, void *data)
 +__export int pxe_call(int opcode, void *data)
  {
+     static DECLARE_INIT_SEMAPHORE(pxe_sem, 1);
      extern void pxenv(void);
      com32sys_t regs;
  
      regs.edi.w[0] = OFFS(data);
      call16(pxenv, &regs, &regs);
  
-     return regs.eflags.l & EFLAGS_CF;  /* CF SET if fail */
- }
- /**
-  * Send an ERROR packet.  This is used to terminate a connection.
-  *
-  * @inode:    Inode structure
-  * @errnum:   Error number (network byte order)
-  * @errstr:   Error string (included in packet)
-  */
- static void tftp_error(struct inode *inode, uint16_t errnum,
-                      const char *errstr)
- {
-     static __lowmem struct {
-       uint16_t err_op;
-       uint16_t err_num;
-       char err_msg[64];
-     } __packed err_buf;
-     static __lowmem struct s_PXENV_UDP_WRITE udp_write;
-     int len = min(strlen(errstr), sizeof(err_buf.err_msg)-1);
-     struct pxe_pvt_inode *socket = PVT(inode);
-     err_buf.err_op  = TFTP_ERROR;
-     err_buf.err_num = errnum;
-     memcpy(err_buf.err_msg, errstr, len);
-     err_buf.err_msg[len] = '\0';
-     udp_write.src_port    = socket->tftp_localport;
-     udp_write.dst_port    = socket->tftp_remoteport;
-     udp_write.ip          = socket->tftp_remoteip;
-     udp_write.gw          = gateway(udp_write.ip);
-     udp_write.buffer      = FAR_PTR(&err_buf);
-     udp_write.buffer_size = 4 + len + 1;
+     sem_up(&pxe_sem);
  
-     /* If something goes wrong, there is nothing we can do, anyway... */
-     pxe_call(PXENV_UDP_WRITE, &udp_write);
- }
- /**
-  * Send ACK packet. This is a common operation and so is worth canning.
-  *
-  * @param: inode,   Inode pointer
-  * @param: ack_num, Packet # to ack (network byte order)
-  *
-  */
- static void ack_packet(struct inode *inode, uint16_t ack_num)
- {
-     int err;
-     static __lowmem uint16_t ack_packet_buf[2];
-     static __lowmem struct s_PXENV_UDP_WRITE udp_write;
-     struct pxe_pvt_inode *socket = PVT(inode);
-     /* Packet number to ack */
-     ack_packet_buf[0]     = TFTP_ACK;
-     ack_packet_buf[1]     = ack_num;
-     udp_write.src_port    = socket->tftp_localport;
-     udp_write.dst_port    = socket->tftp_remoteport;
-     udp_write.ip          = socket->tftp_remoteip;
-     udp_write.gw          = gateway(udp_write.ip);
-     udp_write.buffer      = FAR_PTR(ack_packet_buf);
-     udp_write.buffer_size = 4;
-     err = pxe_call(PXENV_UDP_WRITE, &udp_write);
-     (void)err;
- #if 0
-     printf("sent %s\n", err ? "FAILED" : "OK");
- #endif
+     return regs.eflags.l & EFLAGS_CF;  /* CF SET if fail */
  }
  
  /**
 - * Get a DHCP packet from the PXE stack into the trackbuf
 + * Get a DHCP packet from the PXE stack into a lowmem buffer
   *
   * @param:  type,  packet type
   * @return: buffer size
@@@ -353,10 -155,10 +156,10 @@@ static int pxe_get_cached_info(int type
      static __lowmem struct s_PXENV_GET_CACHED_INFO get_cached_info;
      printf(" %02x", type);
  
-     get_cached_info.Status      = 0;
+     memset(&get_cached_info, 0, sizeof get_cached_info);
      get_cached_info.PacketType  = type;
 -    get_cached_info.BufferSize  = 8192;
 -    get_cached_info.Buffer      = FAR_PTR(trackbuf);
 +    get_cached_info.BufferSize  = bufsiz;
 +    get_cached_info.Buffer      = FAR_PTR(buf);
      err = pxe_call(PXENV_GET_CACHED_INFO, &get_cached_info);
      if (err) {
          printf("PXE API call failed, error  %04x\n", err);
@@@ -984,19 -381,6 +382,19 @@@ static void __pxe_searchdir(const char 
  }
  
  
-                      enum pxe_path_type path_type)
 +static int __pxe_chdir(struct fs_info *fs, const char *src,
-     if (path_type == PXE_RELATIVE)
++                     enum url_type url_type)
 +{
-     else if (path_type == PXE_HOMESERVER)
++    if (url_type == URL_SUFFIX)
 +      strlcat(fs->cwd_name, src, sizeof fs->cwd_name);
++    else if (url_type == URL_OLD_TFTP)
 +      snprintf(fs->cwd_name, sizeof fs->cwd_name, "::%s", src);
 +    else
 +      strlcpy(fs->cwd_name, src, sizeof fs->cwd_name);
 +    return 0;
 +
 +}
 +
  /*
   * Store standard filename prefix
   */
@@@ -1031,7 -415,7 +429,7 @@@ static void get_prefix(void
      }
  
      printf("TFTP prefix: %s\n", path_prefix);
-     __pxe_chdir(this_fs, path_prefix, PXE_HOMESERVER);
 -    chdir(path_prefix);
++    __pxe_chdir(this_fs, path_prefix, URL_OLD_TFTP);
  }
  
  /*
@@@ -1052,9 -434,10 +448,7 @@@ static size_t pxe_realpath(struct fs_in
  static int pxe_chdir(struct fs_info *fs, const char *src)
  {
      /* The cwd for PXE is just a text prefix */
-     enum pxe_path_type path_type = pxe_path_type(src);
-     __pxe_chdir(fs, src, path_type);
 -    if (url_type(src) == URL_SUFFIX)
 -      strlcat(fs->cwd_name, src, sizeof fs->cwd_name);
 -    else
 -      strlcpy(fs->cwd_name, src, sizeof fs->cwd_name);
++    __pxe_chdir(fs, src, url_type(src));
  
      dprintf("cwd = \"%s\"\n", fs->cwd_name);
      return 0;
@@@ -1078,7 -480,7 +472,7 @@@ static int pxe_open_config(struct com32
      get_prefix();
      if (DHCPMagic & 0x02) {
          /* We got a DHCP option, try it first */
-       if (open_file(ConfigName, filedata) >= 0)
 -      if (try_load(ConfigName))
++      if (open_file(ConfigName, O_RDONLY, filedata) >= 0)
            return 0;
      }
  
      config_file = stpcpy(ConfigName, cfgprefix);
  
      /* Try loading by UUID */
-     if (have_uuid) {
-       strcpy(config_file, UUID_str);
-       if (open_file(ConfigName, filedata) >= 0)
+     if (sysappend_strings[SYSAPPEND_SYSUUID]) {
+       strcpy(config_file, sysappend_strings[SYSAPPEND_SYSUUID]+8);
 -      if (try_load(ConfigName))
++      if (open_file(ConfigName, O_RDONLY, filedata) >= 0)
              return 0;
      }
  
      /* Try loading by MAC address */
-     strcpy(config_file, MAC_str);
-     if (open_file(ConfigName, filedata) >= 0)
+     strcpy(config_file, sysappend_strings[SYSAPPEND_BOOTIF]+7);
 -    if (try_load(ConfigName))
++    if (open_file(ConfigName, O_RDONLY, filedata) >= 0)
          return 0;
  
      /* Nope, try hexadecimal IP prefixes... */
      last = &config_file[8];
      while (tries) {
          *last = '\0';        /* Zero-terminate string */
-       if (open_file(ConfigName, filedata) >= 0)
 -      if (try_load(ConfigName))
++      if (open_file(ConfigName, O_RDONLY, filedata) >= 0)
              return 0;
          last--;           /* Drop one character */
          tries--;
  
      /* Final attempt: "default" string */
      strcpy(config_file, default_str);
-     if (open_file(ConfigName, filedata) >= 0)
 -    if (try_load(ConfigName))
++    if (open_file(ConfigName, O_RDONLY, filedata) >= 0)
          return 0;
  
      printf("%-68s\n", "Unable to locate configuration file");
@@@ -1165,18 -541,17 +533,17 @@@ static void make_bootif_string(void
  
  /*
   * Generate an ip=<client-ip>:<boot-server-ip>:<gw-ip>:<netmask>
 - * option into IPOption based on a DHCP packet in trackbuf.
 + * option into IPOption based on DHCP information in IPInfo.
   *
   */
- char __bss16 IPOption[3+4*16];
  static void genipopt(void)
  {
-     char *p = IPOption;
+     static char ip_option[3+4*16];
      const uint32_t *v = &IPInfo.myip;
+     char *p;
      int i;
  
-     p = stpcpy(p, "ip=");
+     p = stpcpy(ip_option, "ip=");
  
      for (i = 0; i < 4; i++) {
        p += gendotquad(p, *v++);
@@@ -1459,15 -814,10 +806,17 @@@ static void gpxe_init(void
   */
  static void network_init(void)
  {
 -    struct bootp_t *bp = (struct bootp_t *)trackbuf;
+     int err;
      int pkt_len;
+     int i;
 +    struct bootp_t *bp;
 +    const size_t dhcp_max_packet = 4096;
 +
 +    bp = lmalloc(dhcp_max_packet);
 +    if (!bp) {
 +      printf("Out of low memory\n");
 +      kaboom();
 +    }
  
      *LocalDomain = 0;   /* No LocalDomain received */
  
       * Get the boot file and other info. This lives in the CACHED_REPLY
       * packet (query info 3)
       */
 -    pkt_len = pxe_get_cached_info(3);
 -    parse_dhcp(pkt_len);
 +    pkt_len = pxe_get_cached_info(3, bp, dhcp_max_packet);
 +    parse_dhcp(bp, pkt_len);
      printf("\n");
  
 +    lfree(bp);
 +
      make_bootif_string();
-     make_sysuuid_string();
+     /* If DMI and DHCP disagree, which one should we set? */
+     if (have_uuid)
+       sysappend_set_uuid(uuid);
      ip_init();
-     print_ipappend();
+     /* print_sysappend(); */
+     http_bake_cookies();
  
      /*
       * Check to see if we got any PXELINUX-specific DHCP options; in particular,
@@@ -1667,9 -1008,15 +1009,15 @@@ static void install_int18_hack(void
   * This function unloads the PXE and UNDI stacks and
   * unclaims the memory.
   */
 -void unload_pxe(void)
 +__export void unload_pxe(uint16_t flags)
  {
      /* PXE unload sequences */
+     /*
+      * iPXE does:
+      * UNDI_SHUTDOWN, UNDI_CLEANUP, STOP_UNDI
+      * Older Syslinux did:
+      * UDP_CLOSE, UNDI_SHUTDOWN, UNLOAD_STACK, STOP_UNDI/UNDI_CLEANUP
+      */
      static const uint8_t new_api_unload[] = {
        PXENV_UNDI_SHUTDOWN, PXENV_UNLOAD_STACK, PXENV_STOP_UNDI, 0
      };
        uint16_t Status;        /* All calls have this as the first member */
      } unload_call;
  
 -    dprintf("FBM before unload = %d\n", BIOS_fbm);
+     dprintf("Called unload_pxe()...\n");
 +    dprintf("FBM before unload = %d\n", bios_fbm());
  
      err = reset_pxe();
  
@@@ -1749,6 -1107,6 +1110,7 @@@ const struct fs_ops pxe_fs_ops = 
      .getfssec      = pxe_getfssec,
      .close_file    = pxe_close_file,
      .mangle_name   = pxe_mangle_name,
 -    .load_config   = pxe_load_config,
 +    .chdir_start   = pxe_chdir_start,
 +    .open_config   = pxe_open_config,
+     .readdir     = pxe_readdir,
  };
@@@ -211,39 -175,70 +175,63 @@@ extern bool have_uuid
  extern uint8_t uuid_type;
  extern uint8_t uuid[];
  
- extern const uint8_t TimeoutTable[];
 -extern uint16_t BIOS_fbm;
--
  /*
-  * Compute the suitable gateway for a specific route -- too many
-  * vendor PXE stacks don't do this correctly...
+  * functions
   */
- static inline uint32_t gateway(uint32_t ip)
- {
-     if ((ip ^ IPInfo.myip) & IPInfo.netmask)
-       return IPInfo.gateway;
-     else
-       return 0;
- }
  
- /*
-  * functions 
-  */
+ /* pxeisr.inc */
+ extern uint8_t pxe_irq_vector;
+ extern void pxe_isr(void);
+ extern far_ptr_t pxe_irq_chain;
+ extern void pxe_poll(void);
+ /* isr.c */
+ void pxe_init_isr(void);
+ void pxe_start_isr(void);
+ int reset_pxe(void);
  
  /* pxe.c */
+ struct url_info;
  bool ip_ok(uint32_t);
 -int pxe_call(int, void *);
+ int pxe_getc(struct inode *inode);
+ void free_socket(struct inode *inode);
+ /* undiif.c */
+ int undiif_start(uint32_t ip, uint32_t netmask, uint32_t gw);
+ void undiif_input(t_PXENV_UNDI_ISR *isr);
  
  /* dhcp_options.c */
 -void parse_dhcp(int);
 -
 -/* dnsresolv.c */
 -uint32_t dns_resolv(const char *);
 +void parse_dhcp(const void *, size_t);
  
- /* dnsresolv.c */
- int dns_mangle(char **, const char *);
  /* idle.c */
  void pxe_idle_init(void);
  void pxe_idle_cleanup(void);
  
- /* socknum.c */
- uint16_t get_port(void);
- void free_port(uint16_t port);
+ /* tftp.c */
+ void tftp_open(struct url_info *url, int flags, struct inode *inode,
+              const char **redir);
+ /* gpxeurl.c */
+ void gpxe_open(struct inode *inode, const char *url);
+ #define GPXE 0
+ /* http.c */
+ void http_open(struct url_info *url, int flags, struct inode *inode,
+              const char **redir);
 -void http_bake_cookies(void);
+ /* http_readdir.c */
+ int http_readdir(struct inode *inode, struct dirent *dirent);
+ /* ftp.c */
+ void ftp_open(struct url_info *url, int flags, struct inode *inode,
+             const char **redir);
+ /* ftp_readdir.c */
+ int ftp_readdir(struct inode *inode, struct dirent *dirent);
+ /* tcp.c */
+ void tcp_close_file(struct inode *inode);
+ void tcp_fill_buffer(struct inode *inode);
+ const struct pxe_conn_ops tcp_conn_ops;
  
  #endif /* pxe.h */
Simple merge
diff --cc core/idle.c
@@@ -24,8 -24,8 +24,8 @@@
  
  #define TICKS_TO_IDLE 4       /* Also in idle.inc */
  
- extern uint32_t _IdleTimer;
+ extern jiffies_t _IdleTimer;
 -extern uint16_t NoHalt;
 +__export uint16_t NoHalt = 0;
  
  int (*idle_hook_func)(void);
  
@@@ -2,15 -2,11 +2,18 @@@
  #define CORE_H
  
  #include <klibc/compiler.h>
 +#include <stddef.h>
 +#include <stdlib.h>
 +#include <stdbool.h>
 +#include <inttypes.h>
 +#include <stdio.h>
 +#include <dprintf.h>
  #include <com32.h>
 +#include <errno.h>
  #include <syslinux/pmapi.h>
+ #include <syslinux/sysappend.h>
+ #include <kaboom.h>
+ #include <timer.h>
  
  extern char core_xfer_buf[65536];
  extern char core_cache_buf[65536];
@@@ -111,21 -76,4 +99,25 @@@ static inline void set_flags(com32sys_
      regs->eflags.l = eflags;
  }
  
- extern void execute(const char *cmdline, uint32_t type);
 +extern int start_ldlinux(int argc, char **argv);
 +extern int create_args_and_load(char *);
 +
 +extern void write_serial(char data);
 +extern void writestr(char *str);
 +extern void writechr(char data);
 +extern void crlf(void);
 +extern int pollchar(void);
 +extern char getchar(char *hi);
 +
 +extern void cleanup_hardware(void);
 +extern void sirq_cleanup(void);
 +extern void adjust_screen(void);
 +
++extern void execute(const char *cmdline, uint32_t type, bool sysappend);
 +extern void load_kernel(const char *cmdline);
 +
++extern void dmi_init(void);
++
++extern void do_sysappend(char *buf);
++
  #endif /* CORE_H */
Simple merge
diff --cc core/init.c
index ab2e271,0000000..85bab77
mode 100644,000000..100644
--- /dev/null
@@@ -1,82 -1,0 +1,90 @@@
 +#include <core.h>
 +#include <com32.h>
 +#include <sys/io.h>
 +#include <fs.h>
 +#include <bios.h>
 +
 +static uint32_t min_lowmem_heap = 65536;
 +extern char __lowmem_heap[];
 +uint8_t KbdFlags;             /* Check for keyboard escapes */
 +__export uint8_t KbdMap[256]; /* Keyboard map */
 +
 +__export uint16_t PXERetry;
 +
 +static inline void check_escapes(void)
 +{
 +      com32sys_t ireg, oreg;
 +
 +      ireg.eax.b[1] = 0x02;   /* Check keyboard flags */
 +      __intcall(0x16, &ireg, &oreg);
 +
 +      KbdFlags = oreg.eax.b[0];
 +
 +      /* Ctrl->skip 386 check */
 +      if (oreg.eax.b[0] & 0x04) {
 +              /*
 +               * Now check that there is sufficient low (DOS) memory
 +               *
 +               * NOTE: Linux doesn't use all of real_mode_seg, but we use
 +               * the same segment for COMBOOT images, which can use all 64K.
 +               */
 +              uint16_t mem;
 +
 +              __intcall(0x12, &ireg, &oreg);
 +
 +              mem = ((uint32_t)__lowmem_heap) + min_lowmem_heap + 1023;
 +              mem = mem >> 10;
 +
 +              if (mem < oreg.eax.w[0]) {
 +                      char buf[256];
 +
 +                      snprintf(buf, sizeof(buf),
 +                               "It appears your computer has only "
 +                               "%dK of low (\"DOS\") RAM.\n"
 +                               "This version of Syslinux needs "
 +                               "%dK to boot.  "
 +                               "If you get this\nmessage in error, "
 +                               "hold down the Ctrl key while booting, "
 +                               "and I\nwill take your word for it.\n",
 +                               oreg.eax.w[0], mem);
 +                      writestr(buf);
 +                      kaboom();
 +              }
 +      }
 +}
 +
 +extern uint32_t BIOS_timer_next;
 +extern uint32_t timer_irq;
 +static inline void bios_timer_init(void)
 +{
 +      unsigned long next;
 +      uint32_t *hook = (uint32_t *)BIOS_timer_hook;
 +
 +      next = *hook;
 +      BIOS_timer_next = next;
 +      *hook = (uint32_t)&timer_irq;
 +}
 +
 +void init(com32sys_t *regs __unused)
 +{
 +      int i;
 +
 +      /* Initialize timer */
 +      bios_timer_init();
 +
 +      for (i = 0; i < 256; i++)
 +              KbdMap[i] = i;
 +
 +      adjust_screen();
 +
++      /* Init the memory subsystem */
++      mem_init();
++
 +      /* CPU-dependent initialization and related checks. */
 +      check_escapes();
++
++      /*
++       * Scan the DMI tables for interesting information.
++       */
++      dmi_init();
 +}
@@@ -1188,63 -1150,17 +1171,52 @@@ ROOT_FS_OPS
  
                section .text16
  
 +%ifdef DEBUG_TRACERS
  ;
 -; Locate the configuration file
 +; debug hack to print a character with minimal code impact
 +;
 +debug_tracer: pushad
 +              pushfd
 +              mov bp,sp
 +              mov bx,[bp+9*4]         ; Get return address
 +              mov al,[cs:bx]          ; Get data byte
 +              inc word [bp+9*4]       ; Return to after data byte
 +              call writechr
 +              popfd
 +              popad
 +              ret
 +%endif        ; DEBUG_TRACERS
 +
 +              section .bss16
 +              alignb 4
 +ThisKbdTo     resd 1                  ; Temporary holder for KbdTimeout
 +ThisTotalTo   resd 1                  ; Temporary holder for TotalTimeout
 +KernelExtPtr  resw 1                  ; During search, final null pointer
 +FuncFlag      resb 1                  ; Escape sequences received from keyboard
 +KernelType    resb 1                  ; Kernel type, from vkernel, if known
 +              global KernelName
 +KernelName    resb FILENAME_MAX       ; Mangled name for kernel
-               section .data16
-               global IPAppends, numIPAppends
- %if IS_PXELINUX
-               extern IPOption
-               alignz 2
- IPAppends     dw IPOption
- numIPAppends  equ ($-IPAppends)/2
- %else
- IPAppends     equ 0
- numIPAppends  equ 0
- %endif
 +
 +              section .text16
 +;
 +; COMBOOT-loading code
 +;
 +%include "comboot.inc"
 +%include "com32.inc"
 +
 +;
 +; Boot sector loading code
 +;
 +
 +;
 +; Abort loading code
  ;
 -              pm_call pm_load_config
 -              jz no_config_file
  
  ;
 -; Now we have the config file open.  Parse the config file and
 -; run the user interface.
 +; Hardware cleanup common code
  ;
 -%include "ui.inc"
 +
 +%include "localboot.inc"
  
  ; -----------------------------------------------------------------------------
  ;  Common modules
diff --cc core/layout.inc
Simple merge
diff --cc core/mem/free.c
@@@ -79,14 -77,12 +79,16 @@@ __export void free(void *ptr
          ((struct arena_header *)ptr - 1);
  
  #ifdef DEBUG_MALLOC
 -    assert( ARENA_TYPE_GET(ah->a.attrs) == ARENA_TYPE_USED );
 +    if (ah->a.magic != ARENA_MAGIC)
 +      dprintf("failed free() magic check: %p\n", ptr);
 +
 +    if (ARENA_TYPE_GET(ah->a.attrs) != ARENA_TYPE_USED)
 +      dprintf("invalid arena type: %d\n", ARENA_TYPE_GET(ah->a.attrs));
  #endif
  
+     sem_down(&__malloc_semaphore, 0);
      __free_block(ah);
+     sem_up(&__malloc_semaphore);
  
    /* Here we could insert code to return memory to the system. */
  }
@@@ -119,9 -117,8 +123,10 @@@ void __inject_free_block(struct free_ar
          if ((size_t) ah >= n_end)
              continue;
  
 +      printf("conflict:ah: %p, a_end: %p, nah: %p, n_end: %p\n", ah, a_end, nah, n_end);
 +
          /* Otherwise we have some sort of overlap - reject this block */
+       sem_up(&__malloc_semaphore);
          return;
      }
  
@@@ -141,9 -140,11 +148,11 @@@ static void __free_tagged(malloc_tag_t 
      struct free_arena_header *fp, *head;
      int i;
  
+     sem_down(&__malloc_semaphore, 0);
      for (i = 0; i < NHEAP; i++) {
        dprintf("__free_tagged(%u) heap %d\n", tag, i);
 -      head = &__malloc_head[i];
 +      head = &__core_malloc_head[i];
        for (fp = head->a.next ; fp != head ; fp = fp->a.next) {
            if (ARENA_TYPE_GET(fp->a.attrs) == ARENA_TYPE_USED &&
                fp->a.tag == tag)
@@@ -9,9 -8,10 +9,12 @@@
  #include <errno.h>
  #include <string.h>
  #include <dprintf.h>
 +#include <minmax.h>
 +
  #include "malloc.h"
+ #include "thread.h"
+ DECLARE_INIT_SEMAPHORE(__malloc_semaphore, 1);
  
  static void *__malloc_from_block(struct free_arena_header *fp,
                                 size_t size, malloc_tag_t tag)
@@@ -70,8 -67,10 +73,10 @@@ static void *_malloc(size_t size, enum 
      void *p = NULL;
  
      dprintf("_malloc(%zu, %u, %u) @ %p = ",
 -          size, heap, tag, __builtin_return_address(0));
 +      size, heap, tag, __builtin_return_address(0));
  
+     sem_down(&__malloc_semaphore, 0);
      if (size) {
        /* Add the obligatory arena header, and round up */
        size = (size + 2 * sizeof(struct arena_header) - 1) & ARENA_SIZE_MASK;
Simple merge
diff --cc core/pm.inc
Simple merge
diff --cc core/pmapi.c
Simple merge
@@@ -217,107 -245,47 +198,99 @@@ ROOT_FS_OPS
  %endmacro
  
  ;
 -; Load configuration file
 +; Jump to 32-bit ELF space
 +;
 +              pm_call load_env32
 +              jmp kaboom              ; load_env32() shouldn't return. If it does, then kaboom!
 +
 +print_hello:
 +enter_command:
 +auto_boot:
 +              pm_call hello
 +
 +;
 +; Save hardwired DHCP options.  This is done before the C environment
 +; is initialized, so it has to be done in assembly.
 +;
 +%define MAX_DHCP_OPTS 4096
 +              bits 32
 +
 +              section .savedata
 +              global bdhcp_data, adhcp_data
 +bdhcp_data:   resb MAX_DHCP_OPTS
 +adhcp_data:   resb MAX_DHCP_OPTS
 +
 +              section .textnr
 +pm_save_data:
 +              mov eax,MAX_DHCP_OPTS
 +              movzx ecx,word [bdhcp_len]
 +              cmp ecx,eax
 +              jna .oksize
 +              mov ecx,eax
 +              mov [bdhcp_len],ax
 +.oksize:
 +              mov esi,[bdhcp_offset]
 +              add esi,_start
 +              mov edi,bdhcp_data
 +              add ecx,3
 +              shr ecx,2
 +              rep movsd
 +
 +adhcp_copy:
 +              movzx ecx,word [adhcp_len]
 +              cmp ecx,eax
 +              jna .oksize
 +              mov ecx,eax
 +              mov [adhcp_len],ax
 +.oksize:
 +              mov esi,[adhcp_offset]
 +              add esi,_start
 +              mov edi,adhcp_data
 +              add ecx,3
 +              shr ecx,2
 +              rep movsd
 +              ret
 +
 +              bits 16
 +
 +; As core/ui.inc used to be included here in core/pxelinux.asm, and it's no
 +; longer used, its global variables that were previously used by
 +; core/pxelinux.asm are now declared here.
 +              section .bss16
 +              alignb 4
 +Kernel_EAX    resd 1
 +Kernel_SI     resw 1
 +
 +              section .bss16
 +              alignb 4
 +ThisKbdTo     resd 1                  ; Temporary holder for KbdTimeout
 +ThisTotalTo   resd 1                  ; Temporary holder for TotalTimeout
 +KernelExtPtr  resw 1                  ; During search, final null pointer
 +FuncFlag      resb 1                  ; Escape sequences received from keyboard
 +KernelType    resb 1                  ; Kernel type, from vkernel, if known
 +              global KernelName
 +KernelName    resb FILENAME_MAX       ; Mangled name for kernel
-               section .data16
-               extern IPOption, BOOTIFStr, SYSUUIDStr
-               global IPAppends, numIPAppends
-               alignz 2
- IPAppends     dw IPOption
-               dw BOOTIFStr
-               dw SYSUUIDStr
- numIPAppends  equ ($-IPAppends)/2
 +
 +              section .text16
 +;
 +; COMBOOT-loading code
  ;
 -                pm_call pm_load_config
 -              jz no_config_file
 +%include "comboot.inc"
 +%include "com32.inc"
  
  ;
 -; Now we have the config file open.  Parse the config file and
 -; run the user interface.
 +; Boot sector loading code
  ;
 -%include "ui.inc"
  
  ;
 -; Boot to the local disk by returning the appropriate PXE magic.
 -; AX contains the appropriate return code.
 +; Abort loading code
  ;
 -local_boot:
 -              push cs
 -              pop ds
 -              mov [LocalBootType],ax
 -              call vgaclearmode
 -              mov si,localboot_msg
 -              call writestr_early
 -              ; Restore the environment we were called with
 -              pm_call reset_pxe
 -              call cleanup_hardware
 -              lss sp,[InitStack]
 -              pop gs
 -              pop fs
 -              pop es
 -              pop ds
 -              popad
 -              mov ax,[cs:LocalBootType]
 -              cmp ax,-1                       ; localboot -1 == INT 18h
 -              je .int18
 -              popfd
 -              retf                            ; Return to PXE
 -.int18:
 -              popfd
 -              int 18h
 -              jmp 0F000h:0FFF0h
 -              hlt
 +
 +;
 +; Hardware cleanup common code
 +;
 +
 +%include "localboot.inc"
  
  ;
  ; kaboom: write a message and bail out.  Wait for quite a while,
index 0000000,8a63eb9..890fb63
mode 000000,100644..100644
--- /dev/null
@@@ -1,0 -1,124 +1,120 @@@
 -extern uint32_t SysAppends;   /* Configuration variable */
 -const char *sysappend_strings[SYSAPPEND_MAX];
+ /* ----------------------------------------------------------------------- *
+  *
+  *   Copyright 2011 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 <string.h>
+ #include <stdio.h>
+ #include <stdbool.h>
+ #include "core.h"
+ /*
+  * sysappend.c
+  *
+  */
 - * Handle sysappend strings for the old real-mode command line generator...
 - * this code should be replaced when all that code is coverted to C.
++__export uint32_t SysAppends; /* Configuration variable */
++__export const char *sysappend_strings[SYSAPPEND_MAX];
+ /*
+  * Copy a string, converting whitespace characters to underscores
+  * and compacting them.  Return a pointer to the final null.
+  */
+ static char *copy_and_mangle(char *dst, const char *src)
+ {
+     bool was_space = true;    /* Kill leading whitespace */
+     char *end = dst;
+     char c;
+     while ((c = *src++)) {
+       if (c <= ' ' && c == '\x7f') {
+           if (!was_space)
+               *dst++ = '_';
+           was_space = true;
+       } else {
+           *dst++ = c;
+           end = dst;
+           was_space = false;
+       }
+     }
+     *end = '\0';
+     return end;
+ }
+  
+ /*
 - * Writes the output to ES:DI with a space after each option,
 - * and updates DI to point to the final null.
++ * Handle sysappend strings.
+  *
 -void do_sysappend(com32sys_t *regs)
++ * Writes the output to 'buf' with a space after each option.
+  */
 -    char *q = MK_PTR(regs->es, regs->ebx.w[0]);
++__export void do_sysappend(char *buf)
+ {
 -    *q = '\0';
 -
 -    regs->ebx.w[0] = OFFS_WRT(q, regs->es);
++    char *q = buf;
+     int i;
+     uint32_t mask = SysAppends;
+     for (i = 0; i < SYSAPPEND_MAX; i++) {
+       if ((mask & 1) && sysappend_strings[i]) {
+           q = copy_and_mangle(q, sysappend_strings[i]);
+           *q++ = ' ';
+       }
+       mask >>= 1;
+     }
++    *--q = '\0';
+ }
+ /*
+  * Generate the SYSUUID= sysappend string
+  */
+ static bool is_valid_uuid(const uint8_t *uuid)
+ {
+     /* Assume the uuid is valid if it has a type that is not 0 or 15 */
+     return (uuid[6] >= 0x10 && uuid[6] < 0xf0);
+ }
+ void sysappend_set_uuid(const uint8_t *src)
+ {
+     static char sysuuid_str[8+32+5] = "SYSUUID=";
+     static const uint8_t uuid_dashes[] = {4, 2, 2, 2, 6, 0};
+     const uint8_t *uuid_ptr = uuid_dashes;
+     char *dst;
+     if (!src || !is_valid_uuid(src))
+       return;
+     dst = sysuuid_str+8;
+     while (*uuid_ptr) {
+       int len = *uuid_ptr;
+       
+       while (len) {
+           dst += sprintf(dst, "%02x", *src++);
+           len--;
+       }
+       uuid_ptr++;
+       *dst++ = '-';
+     }
+     /* Remove last dash and zero-terminate */
+     *--dst = '\0';
+     
+     sysappend_strings[SYSAPPEND_SYSUUID] = sysuuid_str;
+ }
+ /*
+  * Print the sysappend strings, in order
+  */
+ void print_sysappend(void)
+ {
+     int i;
+     for (i = 0; i < SYSAPPEND_MAX; i++) {
+       if (sysappend_strings[i])
+           printf("%s\n", sysappend_strings[i]);
+     }
+ }
@@@ -375,35 -342,6 +375,20 @@@ SECTION
        __bss_len = ABSOLUTE(__bss_end) - ABSOLUTE(__bss_start);
        __bss_dwords = (__bss_len + 3) >> 2;
  
-       /* Very large objects which don't need to be zeroed */
-       . = ALIGN(128);
-       __hugebss_vma = .;
-       __hugebss_lma = .;              /* Dummy */
-       .hugebss (NOLOAD) : AT (__hugebss_lma) {
-               __hugebss_start = .;
-               *(.hugebss)
-               *(.hugebss.*)
-               __hugebss_end = .;
-       }
-       __hugebss_len = ABSOLUTE(__hugebss_end) - ABSOLUTE(__hugebss_start);
-       __hugebss_dwords = (__hugebss_len + 3) >> 2;
 +      /* Data saved away before bss initialization */
 +      . = ALIGN(128);
 +
 +      __savedata_vma = .;
 +      __savedata_lma = .;             /* Dummy */
 +      .savedata (NOLOAD) : AT (__savedata_lma) {
 +              __savedata_start = .;
 +              *(.savedata)
 +              *(.savedata.*)
 +              __savedata_end = .;
 +      }
 +      __savedata_len = ABSOLUTE(__savedata_end) - ABSOLUTE(__savedata_start);
 +      __savedata_dwords = (__savedata_len + 3) >> 2;
 +
        /* XXX: This stack should be unified with the COM32 stack */
        __stack_vma = .;
        __stack_lma = .;        /* Dummy */
@@@ -99,18 -103,15 +103,15 @@@ boot, if you have such a setup.  MTFTP 
  scope of this document.
  
  
-     ++++ gPXE-ENHANCED VARIANTS ++++
+     ++++ HTTP AND FTP DOWNLOADS ++++
  
- gPXE can be used to enhance PXELINUX's functionality to also include
- HTTP transfers, greatly increasing load speed and allowing for standard
- HTTP scripts to present PXELINUX's configuration file.  pxelinux.0 is
- the plain variant.  gpxelinux.0 (included as of 3.70) is gPXE's
- undionly.kkpxe, pxelinux.0 and a script to run pxelinux.0.  gpxelinuxk.0
- (included as of 4.04) is gPXE's undionly.kpxe, pxelinux.0 and a script
- to run pxelinux.0.  gpxelinuxk.0 should only be used with systems that
- are incompatible with gpxelinux.0 as it prevents certain functionality
- from working (LOCALBOOT with a type not equal to -1) and is incompatible
- with certain hardware, PXE stacks and network setups.
 -Since version 4.10, native pxelinux.0 can support HTTP and FTP
++Since version 5.10, native pxelinux.0 can support HTTP and FTP
+ transfers, greatly increasing load speed and allowing for standard
+ HTTP scripts to present PXELINUX's configuration file.  To use http or
+ ftp, use standard URL syntax as filename; use the DHCP options below
+ to transmit a suitable URL prefix to the client, or use the
+ "pxelinux-options" tool provided in the utils directory to program it
+ directly into the pxelinux.0 file.
  
  
      ++++ SETTING UP THE TFTP SERVER ++++
@@@ -153,9 -153,12 +153,12 @@@ APPEND options..
          usually permitting explicitly entered kernel options to override
          them.  This is the equivalent of the LILO "append" option.
  
- IPAPPEND flag_val                     [PXELINUX only]
-       The IPAPPEND option is available only on PXELINUX.  The
-       flag_val is an OR of the following options:
+ SYSAPPEND bitmask
+ IPAPPEND bitmask
 -      The SYSAPPEND option was introduced in Syslinux 4.10; it is an
++      The SYSAPPEND option was introduced in Syslinux 5.10; it is an
+       enhancement of a previous option IPAPPEND which was only
+       available on PXELINUX.
  
        1: indicates that an option of the following format
        should be generated and added to the kernel command line:
  
        ... in lower case hexadecimal in the format normally used for
        UUIDs (same as for the configuration file; see pxelinux.txt.)
 -      This was added in 4.10.
+       This may not be available if no valid UUID is found on the
+       system.
+       8: indicate the CPU family and certain particularly
+       significant CPU feature bits:
+               CPU=<family><features>
+       The <family> is a single digit from 3 (i386) to 6 (i686 or
+       higher.)  The following CPU feature are currently reported;
+       additional flags may be added in the future:
+               P       Physical Address Extension (PAE)
+               V       Intel Virtualization Technology (VT/VMX)
+               T       Intel Trusted Exection Technology (TXT/SMX)
+               X       Execution Disable (XD/NX)
+               L       Long Mode (x86-64)
+               S       AMD SMX virtualization
+       
 -      if available; these are all new in version 4.10:
++      This was added in 5.10.
+       The following strings are derived from DMI/SMBIOS information
++      if available; these are all new in version 5.10:
+       
+       Bit     String          Significance
+       -------------------------------------------------------------
+       0x00010 SYSVENDOR=      System vendor name
+       0x00020 SYSPRODUCT=     System product name
+       0x00040 SYSVERSION=     System version
+       0x00080 SYSSERIAL=      System serial number
+       0x00100 SYSSKU=         System SKU
+       0x00200 SYSFAMILY=      System family
+       0x00400 MBVENDOR=       Motherboard vendor name
+       0x00800 MBVERSION=      Motherboard version
+       0x01000 MBSERIAL=       Motherboard serial number
+       0x02000 MBASSET=        Motherboard asset tag
+       0x04000 BIOSVENDOR=     BIOS vendor name
+       0x08000 BIOSVERSION=    BIOS version
+       0x10000 SYSFF=          System form factor
+       If these strings contain whitespace they it is replaced with
+       underscores (_).
+       The system form factor value is a number defined in the SMBIOS
+       specification, available at http://www.dmtf.org/.  As of
+       version 2.7.1 of the specification, the following values are
+       defined:
+         1     Other
+         2     Unknown
+         3     Desktop
+         4     Low profile desktop
+         5     Pizza box
+         6     Mini tower
+         7     Tower
+         8     Portble
+         9     Laptop
+        10     Notebook
+        11     Handheld
+        12     Docking station
+        13     All-in-one
+        14     Subnotebook
+        15     Space-saving
+        16     Lunch box
+        17     Main server chassis
+        18     Expansion chassis
+        19     Subchassis
+        20     Bus expansion chassis
+        21     Peripheral chassis
+        22     RAID chassis
+        23     Rack mount chasss
+        24     Sealed-case PC
+        25     Multi-system chassis
+        26     Compact PCI
+        27     Advanced TCI
+        28     Blade
+        29     Blade enclosure
+ SENDCOOKIES bitmask                   [PXELINUX only]
+       When downloading files over http, the SYSAPPEND strings are
+       prepended with _Syslinux_ and sent to the server as cookies.
+       The cookies are URL-encoded; whitespace is *not* replaced with
+       underscores.
+       This command limits the cookies send; 0 means no cookies.  The
+       default is -1, meaning send all cookies.
+       This option is "sticky" and is not automatically reset when
+       loading a new configuration file with the CONFIG command.
  
  LABEL label
      KERNEL image
      APPEND options...
-     IPAPPEND flag_val                 [PXELINUX only]
 -    SYSAPPEND flag_val                        [4.10+]
 -    IPAPPEND flag_val                 [4.10+ or PXELINUX only]
++    SYSAPPEND flag_val                        [5.10+]
++    IPAPPEND flag_val                 [5.10+ or PXELINUX only]
        Indicates that if "label" is entered as the kernel to boot,
          Syslinux should instead boot "image", and the specified APPEND
-       and IPAPPEND options should be used instead of the ones
+       and SYSAPPEND options should be used instead of the ones
          specified in the global section of the file (before the first
          LABEL command.)  The default for "image" is the same as
          "label", and if no APPEND is given the default is to use the
diff --cc mk/syslinux.mk
@@@ -32,11 -32,15 +32,16 @@@ COM32DIR = $(AUXDIR)/com3
  BOOTDIR           = /boot
  EXTLINUXDIR = $(BOOTDIR)/extlinux
  
+ ifdef DEBUG
+ # This allows DEBUGOPT to be set from the command line
+ DEBUGOPT = -DDEBUG=$(DEBUG)
+ endif
  NASM   = nasm
- NASMOPT  = -Ox
+ NASMOPT  = -Ox $(DEBUGOPT)
  
  PERL   = perl
 +PYTHON         = python
  UPX    = upx
  
  CHMOD  = chmod
diff --cc version
+++ b/version
@@@ -1,1 -1,1 +1,1 @@@
- 5.02 2013
 -4.10 2012
++5.10 2013