#include <errno.h>
#include <mach-o/nlist.h>
#include <mach/mach_error.h>
#include <mach/mach_init.h>
#include <mach/mach_traps.h>
#include <mach/vm_map.h>
#include <stdio.h>
#include <strings.h>

#ifndef KERNEL_FILE
#define KERNEL_FILE "/mach"
#endif

#ifndef KERNEL_FONT_SYMBOL
#define KERNEL_FONT_SYMBOL "_iso_font"
#endif

#ifndef KERNEL_MEMORY
#define KERNEL_MEMORY "/dev/kmem"
#endif

#ifndef KERNEL_FONT_SIZE
#define KERNEL_FONT_SIZE (256 * 16)
#endif

int main(int argc, char **argv, char **envp)
{
    task_t kernel_port = MACH_PORT_NULL;
    kern_return_t rv;
    int ret = 1;
    char *kernel_file = KERNEL_FILE;
    struct nlist nl[] = {
        { KERNEL_FONT_SYMBOL },
        { 0 }
    };
    int ninvalid = -1;
    unsigned char *iso_font_vm = NULL;
    unsigned char iso_font[KERNEL_FONT_SIZE];
    size_t nread = -1;
    FILE *kmem = NULL;
    FILE *outfile = stdout;
    const char *outfilename = "stdout";

    if ((argc < 1) || (argc > 2)
        ||
        ((argc == 2)
         &&
         (*(argv[1]) == '-')
         &&
         strcmp(argv[1], "--")
         &&
         strcmp(argv[1], "-")))
    {
        fprintf(stderr,
                "usage: %s [ OUTFILE ]\n"
                "writes kernel font to OUTFILE (default: stdout); exits with zero status if successful\n"
                "\n"
                "you may need to run this and reboot first:\n"
                "sudo nvram boot-args=\"kmem=1 `sudo nvram boot-args 2>/dev/null | cut -f 2- `\"\n"
                "", argv[0]);
        fflush(stderr);
        ret = 2;
        goto cleanup;
    }
    ninvalid = nlist(kernel_file, nl);
    if (ninvalid == -1)
    {
        perror("nlist"); fflush(stderr);
        goto cleanup;
    }
    if (ninvalid || ! nl[0].n_value)
    {
        fprintf(stderr, "%s: not found\n", nl[0].n_un.n_name); fflush(stderr);
        goto cleanup;
    }
    kmem = fopen(KERNEL_MEMORY, "rb");
    if (kmem != NULL)
    {
        if (fseek(kmem, nl[0].n_value, SEEK_SET))
        {
            perror("fseek"); fflush(stderr);
            goto cleanup;
        }
        nread = fread((void *) iso_font, 1, sizeof(iso_font), kmem);
    }
    if (nread != KERNEL_FONT_SIZE)
    {
        rv = task_for_pid(mach_task_self(), 0, &kernel_port);
        if (rv != KERN_SUCCESS)
        {
            kernel_port = MACH_PORT_NULL;
            mach_error("task_for_pid", rv); fflush(stderr);
            goto cleanup;
        }
        rv = vm_read(kernel_port, nl[0].n_value - (nl[0].n_value % vm_page_size), (KERNEL_FONT_SIZE + vm_page_size - 1) / vm_page_size * vm_page_size, &iso_font_vm, &nread);
        if (rv != KERN_SUCCESS)
        {
            iso_font_vm = NULL;
            nread = -1;
            mach_error("vm_read", rv); fflush(stderr);
            goto cleanup;
        }
        memcpy((void *) iso_font, (void *) (iso_font_vm + (nl[0].n_value % vm_page_size)), KERNEL_FONT_SIZE);
    }
    if (argc == 2)
    {
        if (strcmp(argv[1], "-") && strcmp(argv[1], "--"))
        {
            outfilename = argv[1];
            outfile = fopen(argv[1], "wb");
        }
    }
    if (isatty(fileno(outfile)))
    {
        fprintf(stderr, "%s: is a tty\n", outfilename); fflush(stderr);
        goto cleanup;
    }
    if (! outfile)
    {
        perror(outfilename); fflush(stderr);
        goto cleanup;
    }
    if (fwrite((void *) iso_font, 1, sizeof(iso_font), outfile) != sizeof(iso_font))
    {
        perror(outfilename); fflush(stderr);
        goto cleanup;
    }
    if (fflush(outfile))
    {
        perror(outfilename); fflush(stderr);
        goto cleanup;
    }
    ret = 0;
  cleanup:
    if (outfile) fclose(outfile);
    if (kmem) fclose(kmem);
    if (iso_font_vm) vm_deallocate(mach_task_self(), iso_font_vm, nread);
    return ret;
}
