text: #!/usr/bin/env node
  /*
   * genkeysymdef: X11 keysymdef.h to JavaScript converter
   * Copyright (C) 2018 The noVNC Authors
   * Licensed under MPL 2.0 (see LICENSE.txt)
   */
  
  "use strict";
  
  const fs = require('fs');
  
  let showHelp = process.argv.length === 2;
  let filename;
  
  for (let i = 2; i < process.argv.length; ++i) {
      switch (process.argv[i]) {
          case "--help":
          case "-h":
              showHelp = true;
              break;
          case "--file":
          case "-f":
          default:
              filename = process.argv[i];
      }
  }
  
  if (!filename) {
      showHelp = true;
      console.log("Error: No filename specified\n");
  }
  
  if (showHelp) {
      console.log("Parses a *nix keysymdef.h to generate Unicode code point mappings");
      console.log("Usage: node parse.js [options] filename:");
      console.log("  -h [ --help ]                 Produce this help message");
      console.log("  filename                      The keysymdef.h file to parse");
      process.exit(0);
  }
  
  const buf = fs.readFileSync(filename);
  const str = buf.toString('utf8');
  
  const re = /^#define XK_([a-zA-Z_0-9]+)\s+0x([0-9a-fA-F]+)\s*(\/\*\s*(.*)\s*\*\/)?\s*$/m;
  
  const arr = str.split('\n');
  
  const codepoints = {};
  
  for (let i = 0; i < arr.length; ++i) {
      const result = re.exec(arr[i]);
      if (result) {
          const keyname = result[1];
          const keysym = parseInt(result[2], 16);
          const remainder = result[3];
  
          const unicodeRes = /U\+([0-9a-fA-F]+)/.exec(remainder);
          if (unicodeRes) {
              const unicode = parseInt(unicodeRes[1], 16);
              // The first entry is the preferred one
              if (!codepoints[unicode]) {
                  codepoints[unicode] = { keysym: keysym, name: keyname };
              }
          }
      }
  }
  
  let out =
  "/*\n" +
  " * Mapping from Unicode codepoints to X11/RFB keysyms\n" +
  " *\n" +
  " * This file was automatically generated from keysymdef.h\n" +
  " * DO NOT EDIT!\n" +
  " */\n" +
  "\n" +
  "/* Functions at the bottom */\n" +
  "\n" +
  "const codepoints = {\n";
  
  function toHex(num) {
      let s = num.toString(16);
      if (s.length < 4) {
          s = ("0000" + s).slice(-4);
      }
      return "0x" + s;
  }
  
  for (let codepoint in codepoints) {
      codepoint = parseInt(codepoint);
  
      // Latin-1?
      if ((codepoint >= 0x20) && (codepoint <= 0xff)) {
          continue;
      }
  
      // Handled by the general Unicode mapping?
      if ((codepoint | 0x01000000) === codepoints[codepoint].keysym) {
          continue;
      }
  
      out += "    " + toHex(codepoint) + ": " +
             toHex(codepoints[codepoint].keysym) +
             ", // XK_" + codepoints[codepoint].name + "\n";
  }
  
  out +=
  "};\n" +
  "\n" +
  "export default {\n" +
  "    lookup(u) {\n" +
  "        // Latin-1 is one-to-one mapping\n" +
  "        if ((u >= 0x20) && (u <= 0xff)) {\n" +
  "            return u;\n" +
  "        }\n" +
  "\n" +
  "        // Lookup table (fairly random)\n" +
  "        const keysym = codepoints[u];\n" +
  "        if (keysym !== undefined) {\n" +
  "            return keysym;\n" +
  "        }\n" +
  "\n" +
  "        // General mapping as final fallback\n" +
  "        return 0x01000000 | u;\n" +
  "    },\n" +
  "};";
  
  console.log(out);
  