[
  {
    "path": ".gitattributes",
    "content": "*.teliva linguist-language=Lua\n*.tlv linguist-language=Lua\n"
  },
  {
    "path": ".gitignore",
    "content": "*.o\n*.a\n"
  },
  {
    "path": "COPYRIGHT.md",
    "content": "Lua License\n-----------\n\nLua is licensed under the terms of the MIT license reproduced below.\nThis means that Lua is free software and can be used for both academic\nand commercial purposes at absolutely no cost.\n\nFor details and rationale, see http://www.lua.org/license.html .\n\n===============================================================================\n\nCopyright (C) 1994-2012 Lua.org, PUC-Rio.\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in\nall copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\nTHE SOFTWARE.\n\n===============================================================================\n"
  },
  {
    "path": "Makefile",
    "content": "# makefile for installing Lua\n# see INSTALL for installation instructions\n# see src/Makefile and src/luaconf.h for further customization\n\n# == CHANGE THE SETTINGS BELOW TO SUIT YOUR ENVIRONMENT =======================\n\n# Your platform. See PLATS for possible values.\nPLAT= none\n\n# Where to install. The installation starts in the src and doc directories,\n# so take care if INSTALL_TOP is not an absolute path.\nINSTALL_TOP= /usr/local\nINSTALL_BIN= $(INSTALL_TOP)/bin\nINSTALL_INC= $(INSTALL_TOP)/include\nINSTALL_LIB= $(INSTALL_TOP)/lib\nINSTALL_MAN= $(INSTALL_TOP)/man/man1\n#\n# You probably want to make INSTALL_LMOD and INSTALL_CMOD consistent with\n# LUA_ROOT, LUA_LDIR, and LUA_CDIR in luaconf.h (and also with etc/lua.pc).\nINSTALL_LMOD= $(INSTALL_TOP)/share/lua/$V\nINSTALL_CMOD= $(INSTALL_TOP)/lib/lua/$V\n\n# How to install. If your install program does not support \"-p\", then you\n# may have to run ranlib on the installed liblua.a (do \"make ranlib\").\nINSTALL= install -p\nINSTALL_EXEC= $(INSTALL) -m 0755\nINSTALL_DATA= $(INSTALL) -m 0644\n#\n# If you don't have install you can use cp instead.\n# INSTALL= cp -p\n# INSTALL_EXEC= $(INSTALL)\n# INSTALL_DATA= $(INSTALL)\n\n# Utilities.\nMKDIR= mkdir -p\nRANLIB= ranlib\n\n# == END OF USER SETTINGS. NO NEED TO CHANGE ANYTHING BELOW THIS LINE =========\n\n# Keep this sync'd with src/Makefile\nPLATS= freebsd linux macosx netbsd openbsd\n\n# What to install.\nTO_BIN= lua luac\nTO_INC= lua.h luaconf.h lualib.h lauxlib.h ../etc/lua.hpp\nTO_LIB= liblua.a\nTO_MAN= lua.1 luac.1\n\n# Lua version and release.\nV= 5.1\nR= 5.1.5\n\nall:\t$(PLAT)\n\n$(PLATS) clean:\n\tcd src && $(MAKE) $@\n\ninstall: dummy\n\tcd src && $(MKDIR) $(INSTALL_BIN) $(INSTALL_INC) $(INSTALL_LIB) $(INSTALL_MAN) $(INSTALL_LMOD) $(INSTALL_CMOD)\n\tcd src && $(INSTALL_EXEC) $(TO_BIN) $(INSTALL_BIN)\n\tcd src && $(INSTALL_DATA) $(TO_INC) $(INSTALL_INC)\n\tcd src && $(INSTALL_DATA) $(TO_LIB) $(INSTALL_LIB)\n\tcd doc && $(INSTALL_DATA) $(TO_MAN) $(INSTALL_MAN)\n\nranlib:\n\tcd src && cd $(INSTALL_LIB) && $(RANLIB) $(TO_LIB)\n\nlocal:\n\t$(MAKE) install INSTALL_TOP=..\n\nnone:\n\t@echo \"Please do\"\n\t@echo \"   make PLATFORM\"\n\t@echo \"where PLATFORM is one of these:\"\n\t@echo \"   $(PLATS)\"\n\t@echo \"See INSTALL for complete instructions.\"\n\n# make may get confused with test/ and INSTALL in a case-insensitive OS\ndummy:\n\n# echo config parameters\necho:\n\t@echo \"\"\n\t@echo \"These are the parameters currently set in src/Makefile to build Lua $R:\"\n\t@echo \"\"\n\t@cd src && $(MAKE) -s echo\n\t@echo \"\"\n\t@echo \"These are the parameters currently set in Makefile to install Lua $R:\"\n\t@echo \"\"\n\t@echo \"PLAT = $(PLAT)\"\n\t@echo \"INSTALL_TOP = $(INSTALL_TOP)\"\n\t@echo \"INSTALL_BIN = $(INSTALL_BIN)\"\n\t@echo \"INSTALL_INC = $(INSTALL_INC)\"\n\t@echo \"INSTALL_LIB = $(INSTALL_LIB)\"\n\t@echo \"INSTALL_MAN = $(INSTALL_MAN)\"\n\t@echo \"INSTALL_LMOD = $(INSTALL_LMOD)\"\n\t@echo \"INSTALL_CMOD = $(INSTALL_CMOD)\"\n\t@echo \"INSTALL_EXEC = $(INSTALL_EXEC)\"\n\t@echo \"INSTALL_DATA = $(INSTALL_DATA)\"\n\t@echo \"\"\n\t@echo \"See also src/luaconf.h .\"\n\t@echo \"\"\n\n# echo private config parameters\npecho:\n\t@echo \"V = $(V)\"\n\t@echo \"R = $(R)\"\n\t@echo \"TO_BIN = $(TO_BIN)\"\n\t@echo \"TO_INC = $(TO_INC)\"\n\t@echo \"TO_LIB = $(TO_LIB)\"\n\t@echo \"TO_MAN = $(TO_MAN)\"\n\n# echo config parameters as Lua code\n# uncomment the last sed expression if you want nil instead of empty strings\nlecho:\n\t@echo \"-- installation parameters for Lua $R\"\n\t@echo \"VERSION = '$V'\"\n\t@echo \"RELEASE = '$R'\"\n\t@$(MAKE) echo | grep = | sed -e 's/= /= \"/' -e 's/$$/\"/' #-e 's/\"\"/nil/'\n\t@echo \"-- EOF\"\n\n# list targets that do not create files (but not all makes understand .PHONY)\n.PHONY: all $(PLATS) clean test install local none dummy echo pecho lecho\n\n# (end of Makefile)\n"
  },
  {
    "path": "README.md",
    "content": "# Teliva - an environment for end-user programming\n\n> &ldquo;Enable all people to modify the software they use in the course of using it.&rdquo;\n> &mdash; https://futureofcoding.org/episodes/033.html\n\n> &ldquo;What if we, and all computer users, could reach in and modify our favorite apps?&rdquo;\n> &mdash; https://www.inkandswitch.com/end-user-programming\n\n> &ldquo;Software must be as easy to change as it is to use.&rdquo;\n> &mdash; https://malleable.systems\n\n## What's this, then?\n\nTeliva is the most naive way to make software as easy to change as it is to\nuse: an interpreted environment for running shareable little text-mode apps.\nIts language of choice is Lua, excluding all features that can conceivably\nintroduce moving parts in running a Lua program.\n\nHere's how you run one of the example apps (the [Tower of Hanoi](https://en.wikipedia.org/wiki/Tower_of_Hanoi)):\n\n```sh\ngit clone https://github.com/akkartik/teliva\ncd teliva\nmake linux  # replace with 'macosx', etc. depending on your OS\nsrc/teliva hanoi.tlv\n```\n\n![screenshot of Teliva running the Towers of Hanoi](doc/hanoi.png)\n\nNo matter what app you run, you are always guaranteed access to a single\nobvious, consistent way (currently the hotkey `ctrl-u`) to inspect its\nsources.\n\nWhen you look under the hood of an app, the first thing you see is a\n_big-picture view_ which shows the app's major data structures and a top-down\nview of the app's code.\n\n![screenshot of big-picture view for the Towers of Hanoi](doc/hanoi-big-picture.png)\n\nSelect a definition, make a change, hit `ctrl-x`, and the app will run with\nyour updates. ([video](https://archive.org/details/akkartik-2021-11-14))\n\nYou will need some Unix-like platform with a C compiler and the ncurses and\nopenssl libraries. Some possible commands to install them, depending on your\nOS and package manager of choice:\n\n* `guix shell -D lua openssl -- make linux`\n* `nix-shell --pure` (from a directory containing shell.nix in this repo)\n* `sudo apt install libncursesw6-dev openssl`\n* `brew install ncurses openssl`\n\nSo far I've tested Teliva on Linux, Mac OS X, OpenBSD, NetBSD and FreeBSD; it\nshould work on other flavors of BSD, WSL on Windows, etc. with only minor\nmodifications.\n\n## What else can it do?\n\nAnything! Some more sample apps to try out:\n\n* Conway's Game of Life, as an example of an animated local app.\n  ```\n  src/teliva life.tlv\n  ```\n\n  [video](https://merveilles.town/@akkartik/107277755421024772)\n\n* A viewer for [LiChess TV](https://lichess.org/tv), as an example of\n  animation and accessing a remote API over a network.\n  ```\n  src/teliva chesstv.tlv\n  ```\n\n  [video](https://merveilles.town/@akkartik/107319684018301051)\n\n* A browser for the [Gemini protocol](https://gemini.circumlunar.space).\n  ```\n  src/teliva gemini.tlv\n  ```\n\n  [video](https://merveilles.town/@akkartik/107489728557201145)\n\nThese are just a start. The sky is the limit.\n\n## So, just a programming language, then?\n\nThere's one big difference with other programming languages: Teliva apps are\nsandboxed like a browser. Most languages assume the fiction that the person\nrunning a program trusts all the code in the program. This assumption hasn't\nbeen valid since a decade after the Unix big bang, if then. Teliva takes on\nthe reality that apps can get complex and use code written by strangers. In\nresponse, Teliva tries to always summarize for you what the program you're\nrunning is trying to do, and to ask you before a random app tries to do\nsomething sensitive. Permissions you grant a Teliva app will be _flexible_ and\n_easy to understand_. Browsers and mobile apps today tend to make you choose\nbetween those two properties.\n\nThe sandboxing experience is still under construction. See [this talk](https://archive.org/details/akkartik-2022-01-16-fosdem)\n(15 minutes) for a rundown of current thinking. It isn't yet safe to run\nuntrusted Teliva apps you download from the internet. (Fortunately you're\nunlikely to run into any such apps.)\n\n## Isn't this just an IDE?\n\nThere's one big difference: these apps are not intended to be runnable outside\nof the Teliva environment. Editing the sources and visualizing permissions\ngranted will always be core features that are front and center in the UI.\n\nA second, more subtle difference: it's primarily an environment for _running_\napps, and only secondarily for editing them. Starting up the environment puts\nyou in a running app by default. Creating an app from a clean slate is a\nlow-priority use case, as is lots of specialized support for developing\ncomplex apps. The sweet spot for Teliva is simple apps that people will want\nto edit after using for a while.\n\n## Who are we trusting by trusting you?\n\nTeliva is designed to have a shallow, manageable software supply chain. I\nrely on packages distributed by the following reputable brands:\n\n* A well-known Posix OS, either Linux or BSD.\n* A standard C library, usually GNU libc.\n* The [ncurses](https://tldp.org/HOWTO/NCURSES-Programming-HOWTO) library for\n  building text-mode user interfaces. ([Alternative documentation](https://tldp.org/LDP/lpg-0.4.pdf))\n\nTeliva's codebase also includes forks of the following reputable brands:\n\n* [Lua 5.1](https://www.lua.org/manual/5.1)\n* The [Kilo](https://github.com/antirez/kilo) text editor, modified to use\n  ncurses. (Read more about it in this [fantastic walk-through](https://viewsourcecode.org/snaptoken/kilo).)\n* The [lcurses](https://github.com/lcurses/lcurses) binding for ncurses (as\n  module `curses`).\n* The [luasocket](https://w3.impa.br/~diego/software/luasocket) library of\n  networking primitives (modules `socket`, `http`, `url`, `headers`, `mime`,\n  `ltn12`).\n* The [luasec](https://github.com/brunoos/luasec) library for HTTPS support\n  (modules `https` and `ssl`).\n* The [json.lua](https://github.com/rxi/json.lua) library for\n  serializing/deserializing to JSON (modules `json` and `jsonf`).\n\nI only add to this list with great deliberation. Since it includes indirect\nsuppliers (suppliers of suppliers), I have an incentive to only include\nsuppliers who also have shallow supply chains. Minimizing the size of the\nsupply chain should result in more reliable software that requires less\nfrequent upgrades.\n\n(Look in [the manual](https://akkartik.github.io/teliva/doc/manual.html) for\nmore details of what's available.)\n\n## Why Lua?\n\nIt's reputedly the fastest interpreted language per line of implementation\ncode.\n\n## Will it run any Lua program?\n\nNot quite. My priority is providing a good experience for newcomers to\ncomprehend and modify the programs they use. If it's not clear how to provide\nthat experience for some kinds of Lua programs, I'd rather disable support for\nthem in Teliva and let people use regular Lua. Or other platforms!\n\n- This approach doesn't make sense for batch programs, I think. I also don't\n  yet have a good story for building server programs in this way.\n\n- I don't know how to obtain a simple, shallow graphics stack, so there's no\n  support for graphics at the moment.\n\n- Teliva initializes the ncurses library by default, so apps should assume\n  they have access to a (color, UTF-8) text-mode window for printing text to,\n  and a keyboard for reading keystrokes from.\n\n- Teliva doesn't use files for source code, so the `require` keyword no longer\n  makes sense. You get some libraries preloaded (see below). Beyond those,\n  apps should include all Lua code they want to use.\n\n- To create a well-behaved sandbox, Teliva doesn't support adding libraries\n  with C bindings beyond the few it starts up with.\n\n- Functions that start with `test_` are special. They're considered automated\n  tests and called without arguments every time an app starts up.\n\n- The function `main` is special. It runs every time an app starts up, if all\n  its automated tests pass.\n\n- Some functions are disabled because I don't know how to sandbox them\n  effectively:\n  - `os.execute`, `os.getenv`, `io.popen`\n  - `io.lines` (not a security issue; just difficult to distinguish missing\n    files from sandboxing issues)\n\n- Some functions are disabled because they don't seem to make sense in an\n  ncurses environment. This includes the Lua notions of default files, which\n  start out as stdin/stdout.\n  - `io.input`, `io.read`\n  - `io.output`, `io.write`, `io.flush`\n  - `curses.getstr()`, `curses.mvgetstr()` (When using these it's easy for the\n    screen to get confusing.)\n\n- Some functions in lcurses have [additional smarts](https://github.com/lcurses/lcurses/blob/master/lib/curses.lua).\n  Teliva is [consistent with the underlying ncurses](https://github.com/akkartik/teliva/blob/main/src/lcurses/curses.lua).\n\nLook in [the manual](https://akkartik.github.io/teliva/doc/manual.html) for\nmore details of what's available.\n\nI may delete more capabilities throughout this stack as I discover features\nthat don't fit well with the Teliva experience. If you find Teliva of use,\nplease [introduce yourself](http://akkartik.name/contact) to me so that I am\naware of your use cases. Anybody who is interested can gain a say in its\nfuture direction.\n\n## What's with the name?\n\nTeliva is the Tamil root for &lsquo;clear&rsquo;. Very much aspirational.\n\n## Known issues\n\n* Colors are currently hardcoded. You get a light background even if your\n  terminal started out dark. To tweak colors, look for calls to\n  `assume_default_colors()` and `init_pair()`, either in .tlv files for a\n  specific app, or in the C sources for the standard code browser/editor.\n\n* Backspace is known to not work in some configurations. As a workaround,\n  typing `ctrl-h` tends to work in those situations.\n\n* Keys outside the main keyboard area are mostly not supported. This includes\n  the delete key when it's set away from the main keyboard area. (Macs call\n  the backspace key &ldquo;delete&rdquo;; it should behave like backspace. As\n  a consequence the menu sometimes mentions keys that don't work, just to\n  encourage people to try options.)\n\n* chesstv.tlv silently fails on OpenBSD and NetBSD for some reason I haven't\n  investigated yet. Interestingly, I'm running Teliva with curses on NetBSD\n  but ncurses on OpenBSD, so that is probably not an issue.\n\n* life.tlv looks terrible on OpenBSD and NetBSD, with lines wrapping halfway.\n\n## Mirrors and Forks\n\nUpdates to Teliva can be downloaded from the following mirrors:\n* https://github.com/akkartik/teliva\n* https://repo.or.cz/teliva.git\n* https://codeberg.org/akkartik/teliva\n* https://tildegit.org/akkartik/teliva\n* https://git.tilde.institute/akkartik/teliva\n* https://git.sr.ht/~akkartik/teliva\n* https://pagure.io/teliva\n\nForks of Teliva are encouraged. If you show me your fork, I'll link to it\nhere.\n\n## Feedback\n\n[Most appreciated.](http://akkartik.name/contact)\n"
  },
  {
    "path": "anagrams.tlv",
    "content": "# .tlv file generated by https://github.com/akkartik/teliva\n# You may edit it if you are careful; however, you may see cryptic errors if you\n# violate Teliva's assumptions.\n#\n# .tlv files are representations of Teliva programs. Teliva programs consist of\n# sequences of definitions. Each definition is a table of key/value pairs. Keys\n# and values are both strings.\n#\n# Lines in .tlv files always follow exactly one of the following forms:\n# - comment lines at the top of the file starting with '#' at column 0\n# - beginnings of definitions starting with '- ' at column 0, followed by a\n#   key/value pair\n# - key/value pairs consisting of '  ' at column 0, containing either a\n#   spaceless value on the same line, or a multi-line value\n# - multiline values indented by more than 2 spaces, starting with a '>'\n#\n# If these constraints are violated, Teliva may unceremoniously crash. Please\n# report bugs at http://akkartik.name/contact\n- __teliva_timestamp: original\n  str_helpers:\n    >-- some string helpers from http://lua-users.org/wiki/StringIndexing\n    >\n    >-- index characters using []\n    >getmetatable('').__index = function(str,i)\n    >  if type(i) == 'number' then\n    >    return str:sub(i,i)\n    >  else\n    >    return string[i]\n    >  end\n    >end\n    >\n    >-- ranges using (), selected bytes using {}\n    >getmetatable('').__call = function(str,i,j)\n    >  if type(i)~='table' then\n    >    return str:sub(i,j)\n    >  else\n    >    local t={}\n    >    for k,v in ipairs(i) do\n    >      t[k]=str:sub(v,v)\n    >    end\n    >    return table.concat(t)\n    >  end\n    >end\n    >\n    >-- iterate over an ordered sequence\n    >function q(x)\n    >  if type(x) == 'string' then\n    >    return x:gmatch('.')\n    >  else\n    >    return ipairs(x)\n    >  end\n    >end\n    >\n    >-- insert within string\n    >function string.insert(str1, str2, pos)\n    >  return str1:sub(1,pos)..str2..str1:sub(pos+1)\n    >end\n    >\n    >function string.remove(s, pos)\n    >  return s:sub(1,pos-1)..s:sub(pos+1)\n    >end\n    >\n    >function string.pos(s, sub)\n    >  return string.find(s, sub, 1, true)  -- plain=true to disable regular expressions\n    >end\n    >\n    >-- TODO: backport utf-8 support from Lua 5.3\n- __teliva_timestamp: original\n  debugy:\n    >debugy = 5\n- __teliva_timestamp: original\n  dbg:\n    >-- helper for debug by print; overlay debug information towards the right\n    >-- reset debugy every time you refresh screen\n    >function dbg(window, s)\n    >  local oldy = 0\n    >  local oldx = 0\n    >  oldy, oldx = window:getyx()\n    >  window:mvaddstr(debugy, 60, s)\n    >  debugy = debugy+1\n    >  window:mvaddstr(oldy, oldx, '')\n    >end\n- __teliva_timestamp: original\n  check:\n    >function check(x, msg)\n    >  if x then\n    >    Window:addch('.')\n    >  else\n    >    print('F - '..msg)\n    >    print('  '..str(x)..' is false/nil')\n    >    teliva_num_test_failures = teliva_num_test_failures + 1\n    >    -- overlay first test failure on editors\n    >    if teliva_first_failure == nil then\n    >      teliva_first_failure = msg\n    >    end\n    >  end\n    >end\n- __teliva_timestamp: original\n  check_eq:\n    >function check_eq(x, expected, msg)\n    >  if eq(x, expected) then\n    >    Window:addch('.')\n    >  else\n    >    print('F - '..msg)\n    >    print('  expected '..str(expected)..' but got '..str(x))\n    >    teliva_num_test_failures = teliva_num_test_failures + 1\n    >    -- overlay first test failure on editors\n    >    if teliva_first_failure == nil then\n    >      teliva_first_failure = msg\n    >    end\n    >  end\n    >end\n- __teliva_timestamp: original\n  eq:\n    >function eq(a, b)\n    >  if type(a) ~= type(b) then return false end\n    >  if type(a) == 'table' then\n    >    if #a ~= #b then return false end\n    >    for k, v in pairs(a) do\n    >      if b[k] ~= v then\n    >        return false\n    >      end\n    >    end\n    >    for k, v in pairs(b) do\n    >      if a[k] ~= v then\n    >        return false\n    >      end\n    >    end\n    >    return true\n    >  end\n    >  return a == b\n    >end\n- __teliva_timestamp: original\n  str:\n    >-- smarter tostring\n    >-- slow; used only for debugging\n    >function str(x)\n    >  if type(x) == 'table' then\n    >    local result = ''\n    >    result = result..#x..'{'\n    >    for k, v in pairs(x) do\n    >      result = result..str(k)..'='..str(v)..', '\n    >    end\n    >    result = result..'}'\n    >    return result\n    >  elseif type(x) == 'string' then\n    >    return '\"'..x..'\"'\n    >  end\n    >  return tostring(x)\n    >end\n- __teliva_timestamp: original\n  find_index:\n    >function find_index(arr, x)\n    >  for n, y in ipairs(arr) do\n    >    if x == y then\n    >      return n\n    >    end\n    >  end\n    >end\n- __teliva_timestamp: original\n  trim:\n    >function trim(s)\n    >  return s:gsub('^%s*', ''):gsub('%s*$', '')\n    >end\n- __teliva_timestamp: original\n  split:\n    >function split(s, d)\n    >  result = {}\n    >  for match in (s..d):gmatch(\"(.-)\"..d) do\n    >    table.insert(result, match);\n    >  end\n    >  return result\n    >end\n- __teliva_timestamp: original\n  map:\n    >-- only for arrays\n    >function map(l, f)\n    >  result = {}\n    >  for _, x in ipairs(l) do\n    >    table.insert(result, f(x))\n    >  end\n    >  return result\n    >end\n- __teliva_timestamp: original\n  reduce:\n    >-- only for arrays\n    >function reduce(l, f, init)\n    >  result = init\n    >  for _, x in ipairs(l) do\n    >    result = f(result, x)\n    >  end\n    >  return result\n    >end\n- __teliva_timestamp: original\n  filter:\n    >function filter(h, f)\n    >  result = {}\n    >  for k, v in pairs(h) do\n    >    if f(k, v) then\n    >      result[k] = v\n    >    end\n    >  end\n    >  return result\n    >end\n- __teliva_timestamp: original\n  ifilter:\n    >-- only for arrays\n    >function ifilter(l, f)\n    >  result = {}\n    >  for _, x in ipairs(l) do\n    >    if f(x) then\n    >      table.insert(result, x)\n    >    end\n    >  end\n    >  return result\n    >end\n- __teliva_timestamp: original\n  sort_letters:\n    >function sort_letters(s)\n    >  tmp = {}\n    >  for i=1,#s do\n    >    table.insert(tmp, s[i])\n    >  end\n    >  table.sort(tmp)\n    >  local result = ''\n    >  for _, c in pairs(tmp) do\n    >    result = result..c\n    >  end\n    >  return result\n    >end\n    >\n    >function test_sort_letters(s)\n    >  check_eq(sort_letters(''), '', 'test_sort_letters: empty')\n    >  check_eq(sort_letters('ba'), 'ab', 'test_sort_letters: non-empty')\n    >  check_eq(sort_letters('abba'), 'aabb', 'test_sort_letters: duplicates')\n    >end\n- __teliva_timestamp: original\n  count_letters:\n    >-- TODO: handle unicode\n    >function count_letters(s)\n    >  local result = {}\n    >  for i=1,s:len() do\n    >    local c = s[i]\n    >    if result[c] == nil then\n    >      result[c] = 1\n    >    else\n    >      result[c] = result[c] + 1\n    >    end\n    >  end\n    >  return result\n    >end\n- __teliva_timestamp: original\n  count:\n    >-- turn an array of elements into a map from elements to their frequency\n    >-- analogous to count_letters for non-strings\n    >function count(a)\n    >  local result = {}\n    >  for i, v in ipairs(a) do\n    >    if result[v] == nil then\n    >      result[v] = 1\n    >    else\n    >      result[v] = result[v] + 1\n    >    end\n    >  end\n    >  return result\n    >end\n- __teliva_timestamp: original\n  union:\n    >function union(a, b)\n    >  for k, v in pairs(b) do\n    >    a[k] = v\n    >  end\n    >  return a\n    >end\n- __teliva_timestamp: original\n  subtract:\n    >-- set subtraction\n    >function subtract(a, b)\n    >  for k, v in pairs(b) do\n    >    a[k] = nil\n    >  end\n    >  return a\n    >end\n- __teliva_timestamp: original\n  all:\n    >-- universal quantifier on sets\n    >function all(s, f)\n    >  for k, v in pairs(s) do\n    >    if not f(k, v) then\n    >      return false\n    >    end\n    >  end\n    >  return true\n    >end\n- __teliva_timestamp: original\n  to_array:\n    >-- turn a set into an array\n    >-- drops values\n    >function to_array(h)\n    >  local result = {}\n    >  for k, _ in pairs(h) do\n    >    table.insert(result, k)\n    >  end\n    >  return result\n    >end\n- __teliva_timestamp: original\n  append:\n    >-- concatenate list 'elems' into 'l', modifying 'l' in the process\n    >function append(l, elems)\n    >  for i=1,#elems do\n    >    table.insert(l, elems[i])\n    >  end\n    >end\n- __teliva_timestamp: original\n  prepend:\n    >-- concatenate list 'elems' into the start of 'l', modifying 'l' in the process\n    >function prepend(l, elems)\n    >  for i=1,#elems do\n    >    table.insert(l, i, elems[i])\n    >  end\n    >end\n- __teliva_timestamp: original\n  all_but:\n    >function all_but(x, idx)\n    >  if type(x) == 'table' then\n    >    local result = {}\n    >    for i, elem in ipairs(x) do\n    >      if i ~= idx then\n    >        table.insert(result,elem)\n    >      end\n    >    end\n    >    return result\n    >  elseif type(x) == 'string' then\n    >    if idx < 1 then return x:sub(1) end\n    >    return x:sub(1, idx-1) .. x:sub(idx+1)\n    >  else\n    >    error('all_but: unsupported type '..type(x))\n    >  end\n    >end\n    >\n    >function test_all_but()\n    >  check_eq(all_but('', 0), '', 'all_but: empty')\n    >  check_eq(all_but('abc', 0), 'abc', 'all_but: invalid low index')\n    >  check_eq(all_but('abc', 4), 'abc', 'all_but: invalid high index')\n    >  check_eq(all_but('abc', 1), 'bc', 'all_but: first index')\n    >  check_eq(all_but('abc', 3), 'ab', 'all_but: final index')\n    >  check_eq(all_but('abc', 2), 'ac', 'all_but: middle index')\n    >end\n- __teliva_timestamp: original\n  set:\n    >function set(l)\n    >  local result = {}\n    >  for i, elem in ipairs(l) do\n    >    result[elem] = true\n    >  end\n    >  return result\n    >end\n- __teliva_timestamp: original\n  set_eq:\n    >function set_eq(l1, l2)\n    >  return eq(set(l1), set(l2))\n    >end\n    >\n    >function test_set_eq()\n    >  check(set_eq({1}, {1}), 'set_eq: identical')\n    >  check(not set_eq({1, 2}, {1, 3}), 'set_eq: different')\n    >  check(set_eq({1, 2}, {2, 1}), 'set_eq: order')\n    >  check(set_eq({1, 2, 2}, {2, 1}), 'set_eq: duplicates')\n    >end\n- __teliva_timestamp: original\n  clear:\n    >function clear(lines)\n    >  while #lines > 0 do\n    >    table.remove(lines)\n    >  end\n    >end\n- __teliva_timestamp: original\n  zap:\n    >function zap(target, src)\n    >  clear(target)\n    >  append(target, src)\n    >end\n- __teliva_timestamp: original\n  mfactorial:\n    >-- memoized version of factorial\n    >-- doesn't memoize recursive calls, but may be good enough\n    >mfactorial = memo1(factorial)\n- __teliva_timestamp: original\n  factorial:\n    >function factorial(n)\n    >  local result = 1\n    >  for i=1,n do\n    >    result = result*i\n    >  end\n    >  return result\n    >end\n- __teliva_timestamp: original\n  memo1:\n    >-- a higher-order function that takes a function of a single arg\n    >-- (that never returns nil)\n    >-- and returns a memoized version of it\n    >function memo1(f)\n    >  local memo = {}\n    >  return function(x)\n    >    if memo[x] == nil then\n    >      memo[x] = f(x)\n    >    end\n    >    return memo[x]\n    >  end\n    >end\n    >\n    >-- mfactorial doesn't seem noticeably faster\n    >function test_memo1()\n    >  for i=0,30 do\n    >    check_eq(mfactorial(i), factorial(i), 'memo1 over factorial: '..str(i))\n    >  end\n    >end\n- __teliva_timestamp: original\n  num_permutations:\n    >-- number of permutations of n distinct objects, taken r at a time\n    >function num_permutations(n, r)\n    >  return factorial(n)/factorial(n-r)\n    >end\n    >\n    >-- mfactorial doesn't seem noticeably faster\n    >function test_memo1()\n    >  for i=0,30 do\n    >    for j=0,i do\n    >      check_eq(num_permutations(i, j), mfactorial(i)/mfactorial(i-j), 'num_permutations memoizes: '..str(i)..'P'..str(j))\n    >    end\n    >  end\n    >end\n- __teliva_timestamp: original\n  menu:\n    >-- To show app-specific hotkeys in the menu bar, add hotkey/command\n    >-- arrays of strings to the menu array.\n    >menu = {\n    >  {'^h', 'backspace'},\n    >}\n- __teliva_timestamp: original\n  Window:\n    >Window = curses.stdscr()\n- __teliva_timestamp: original\n  doc:blurb:\n    >Show all anagrams of a given word\n- __teliva_timestamp: original\n  word:\n    >word = ''\n- __teliva_timestamp: original\n  cursor:\n    >cursor = 1\n- __teliva_timestamp: original\n  main:\n    >function main()\n    >  Window:nodelay(true)\n    >  while true do\n    >    render(Window)\n    >    update(Window)\n    >  end\n    >end\n- __teliva_timestamp: original\n  update:\n    >function update(window)\n    >  local key\n    >  while true do\n    >    key = window:getch()\n    >    if key then break end\n    >  end\n    >  if key == curses.KEY_LEFT then\n    >    if cursor > 1 then\n    >      cursor = cursor-1\n    >    end\n    >  elseif key == curses.KEY_RIGHT then\n    >    if cursor <= #word then\n    >      cursor = cursor+1\n    >    end\n    >  elseif key == curses.KEY_BACKSPACE or key == 8 or key == 127 then  -- ctrl-h, ctrl-?, delete\n    >    if cursor > 1 then\n    >      cursor = cursor-1\n    >      word = word:remove(cursor)\n    >    end\n    >  elseif key >= 32 and key < 127 then\n    >    word = word:insert(string.char(key), cursor-1)\n    >    cursor = cursor+1\n    >  end\n    >end\n- __teliva_timestamp: original\n  render:\n    >function render(window)\n    >  window:clear()\n    >\n    >  local prompt_str = ' what is your name? '\n    >  window:attron(curses.A_REVERSE)\n    >  window:mvaddstr(0, 0, prompt_str)\n    >  window:attroff(curses.A_REVERSE)\n    >  window:addstr(' ')\n    >  window:attron(curses.A_BOLD)\n    >  window:addstr(word)\n    >  window:attroff(curses.A_BOLD)\n    >  window:mvaddstr(2, 0, '')\n    >  local results = anagrams(word)\n    >  if #results > 0 then\n    >    window:attron(curses.A_REVERSE)\n    >    print(#results..' anagrams')\n    >    window:attroff(curses.A_REVERSE)\n    >    for i, w in ipairs(results) do\n    >      window:addstr(w)\n    >      window:addstr(' ')\n    >    end\n    >  end\n    >\n    >  window:mvaddstr(0, string.len(prompt_str)+cursor, '')\n    >  window:refresh()\n    >end\n- __teliva_timestamp:\n    >Mon Feb 21 17:42:28 2022\n  anagrams:\n    >function anagrams(word)\n    >  return gather(sort_letters(word))\n    >end\n- __teliva_timestamp:\n    >Mon Feb 21 18:18:07 2022\n  gather:\n    >function gather(s)\n    >  if s == '' then return {} end\n    >  local result = {}\n    >  for i=1, #s do\n    >    if i == 1 or s[i] ~= s[i-1] then\n    >      append(result, combine(s[i], gather(all_but(s, i))))\n    >    end\n    >  end\n    >  return result\n    >end\n- __teliva_timestamp: original\n  __teliva_note:\n    >basic version\n  combine:\n    >-- return 'l' with each element prefixed with 'prefix'\n    >function combine(prefix, l)\n    >  if #l == 0 then return {prefix} end\n    >  local result = {}\n    >  for _, elem in ipairs(l) do\n    >    table.insert(result, prefix..elem)\n    >  end\n    >  return result\n    >end\n    >\n    >function test_combine()\n    >  check_eq(combine('abc', {}), {'abc'}, 'test_combine: empty list')\n    >end\n- __teliva_timestamp:\n    >Sat Mar  5 15:24:00 2022\n  count_anagrams:\n    >function count_anagrams(s)\n    >  local result = factorial(s:len())\n    >  local letter_counts = count_letters(s)\n    >  for l, cnt in pairs(letter_counts) do\n    >    result = result / factorial(cnt)\n    >  end\n    >  return result\n    >end\n- __teliva_timestamp:\n    >Sat Mar  5 15:53:23 2022\n  key_pressed:\n    >-- only works when nodelay (non-blocking keyboard)\n    >function key_pressed(window)\n    >  local c = window:getch()\n    >  if c == nil then return false end\n    >  window:ungetch(c)\n    >  return true\n    >end\n- __teliva_timestamp:\n    >Sat Mar  5 15:55:34 2022\n  render:\n    >function render(window)\n    >  window:clear()\n    >\n    >  local prompt_str = ' what is your name? '\n    >  window:attron(curses.A_REVERSE)\n    >  window:mvaddstr(0, 0, prompt_str)\n    >  window:attroff(curses.A_REVERSE)\n    >  window:addstr(' ')\n    >  window:attron(curses.A_BOLD)\n    >  window:addstr(word)\n    >  window:attroff(curses.A_BOLD)\n    >  window:mvaddstr(2, 0, '')\n    >  if #word > 0 then\n    >    local num_anagrams = count_anagrams(word)\n    >    window:attron(curses.A_REVERSE)\n    >    print(num_anagrams..' anagrams')\n    >    window:attroff(curses.A_REVERSE)\n    >    local results = anagrams(word)\n    >    if results == nil then  -- interrupted\n    >      window:addstr('...')\n    >    else\n    >      assert(#results == num_anagrams, \"something's wrong; the count is unexpected\")\n    >      for i, w in ipairs(results) do\n    >        window:addstr(w)\n    >        window:addstr(' ')\n    >        if key_pressed(window) then\n    >          break\n    >        end\n    >      end\n    >    end\n    >  end\n    >\n    >  window:mvaddstr(0, string.len(prompt_str)+cursor, '')\n    >  window:refresh()\n    >end\n- __teliva_timestamp:\n    >Sat Mar  5 15:56:35 2022\n  __teliva_note:\n    >restart computation when a key is pressed\n  gather:\n    >-- return a list of unique permutations of a sorted string 's'\n    >-- the letters in 's' must be in alphabetical order, so that duplicates are adjacent\n    >-- this function can take a long time for long strings, so we make it interruptible\n    >-- if a key is pressed, it returns nil\n    >-- since it's recursive, we also need to handle recursive calls returning nil\n    >function gather(s)\n    >  if s == '' then return {} end\n    >  local result = {}\n    >  for i=1, #s do\n    >    if i == 1 or s[i] ~= s[i-1] then\n    >      local subresult = gather(all_but(s, i))\n    >      if subresult == nil then return nil end  -- interrupted\n    >      append(result, combine(s[i], subresult))\n    >    end\n    >    if key_pressed(Window) then return nil end  -- interrupted\n    >  end\n    >  return result\n    >end\n"
  },
  {
    "path": "break.tlv",
    "content": "# .tlv file generated by https://github.com/akkartik/teliva\n# You may edit it if you are careful; however, you may see cryptic errors if you\n# violate Teliva's assumptions.\n#\n# .tlv files are representations of Teliva programs. Teliva programs consist of\n# sequences of definitions. Each definition is a table of key/value pairs. Keys\n# and values are both strings.\n#\n# Lines in .tlv files always follow exactly one of the following forms:\n# - comment lines at the top of the file starting with '#' at column 0\n# - beginnings of definitions starting with '- ' at column 0, followed by a\n#   key/value pair\n# - key/value pairs consisting of '  ' at column 0, containing either a\n#   spaceless value on the same line, or a multi-line value\n# - multiline values indented by more than 2 spaces, starting with a '>'\n#\n# If these constraints are violated, Teliva may unceremoniously crash. Please\n# report bugs at http://akkartik.name/contact\n- __teliva_timestamp: original\n  str_helpers:\n    >-- some string helpers from http://lua-users.org/wiki/StringIndexing\n    >\n    >-- index characters using []\n    >getmetatable('').__index = function(str,i)\n    >  if type(i) == 'number' then\n    >    return str:sub(i,i)\n    >  else\n    >    return string[i]\n    >  end\n    >end\n    >\n    >-- ranges using (), selected bytes using {}\n    >getmetatable('').__call = function(str,i,j)\n    >  if type(i)~='table' then\n    >    return str:sub(i,j)\n    >  else\n    >    local t={}\n    >    for k,v in ipairs(i) do\n    >      t[k]=str:sub(v,v)\n    >    end\n    >    return table.concat(t)\n    >  end\n    >end\n    >\n    >-- iterate over an ordered sequence\n    >function q(x)\n    >  if type(x) == 'string' then\n    >    return x:gmatch('.')\n    >  else\n    >    return ipairs(x)\n    >  end\n    >end\n    >\n    >-- insert within string\n    >function string.insert(str1, str2, pos)\n    >  return str1:sub(1,pos)..str2..str1:sub(pos+1)\n    >end\n    >\n    >function string.remove(s, pos)\n    >  return s:sub(1,pos-1)..s:sub(pos+1)\n    >end\n    >\n    >function string.pos(s, sub)\n    >  return string.find(s, sub, 1, true)  -- plain=true to disable regular expressions\n    >end\n    >\n    >-- TODO: backport utf-8 support from Lua 5.3\n- __teliva_timestamp: original\n  debugy:\n    >debugy = 5\n- __teliva_timestamp: original\n  dbg:\n    >-- helper for debug by print; overlay debug information towards the right\n    >-- reset debugy every time you refresh screen\n    >function dbg(window, s)\n    >  local oldy = 0\n    >  local oldx = 0\n    >  oldy, oldx = window:getyx()\n    >  window:mvaddstr(debugy, 60, s)\n    >  debugy = debugy+1\n    >  window:mvaddstr(oldy, oldx, '')\n    >end\n- __teliva_timestamp: original\n  check:\n    >function check(x, msg)\n    >  if x then\n    >    Window:addch('.')\n    >  else\n    >    print('F - '..msg)\n    >    print('  '..str(x)..' is false/nil')\n    >    teliva_num_test_failures = teliva_num_test_failures + 1\n    >    -- overlay first test failure on editors\n    >    if teliva_first_failure == nil then\n    >      teliva_first_failure = msg\n    >    end\n    >  end\n    >end\n- __teliva_timestamp: original\n  check_eq:\n    >function check_eq(x, expected, msg)\n    >  if eq(x, expected) then\n    >    Window:addch('.')\n    >  else\n    >    print('F - '..msg)\n    >    print('  expected '..str(expected)..' but got '..str(x))\n    >    teliva_num_test_failures = teliva_num_test_failures + 1\n    >    -- overlay first test failure on editors\n    >    if teliva_first_failure == nil then\n    >      teliva_first_failure = msg\n    >    end\n    >  end\n    >end\n- __teliva_timestamp: original\n  eq:\n    >function eq(a, b)\n    >  if type(a) ~= type(b) then return false end\n    >  if type(a) == 'table' then\n    >    if #a ~= #b then return false end\n    >    for k, v in pairs(a) do\n    >      if b[k] ~= v then\n    >        return false\n    >      end\n    >    end\n    >    for k, v in pairs(b) do\n    >      if a[k] ~= v then\n    >        return false\n    >      end\n    >    end\n    >    return true\n    >  end\n    >  return a == b\n    >end\n- __teliva_timestamp: original\n  str:\n    >-- smarter tostring\n    >-- slow; used only for debugging\n    >function str(x)\n    >  if type(x) == 'table' then\n    >    local result = ''\n    >    result = result..#x..'{'\n    >    for k, v in pairs(x) do\n    >      result = result..str(k)..'='..str(v)..', '\n    >    end\n    >    result = result..'}'\n    >    return result\n    >  elseif type(x) == 'string' then\n    >    return '\"'..x..'\"'\n    >  end\n    >  return tostring(x)\n    >end\n- __teliva_timestamp: original\n  find_index:\n    >function find_index(arr, x)\n    >  for n, y in ipairs(arr) do\n    >    if x == y then\n    >      return n\n    >    end\n    >  end\n    >end\n- __teliva_timestamp: original\n  trim:\n    >function trim(s)\n    >  return s:gsub('^%s*', ''):gsub('%s*$', '')\n    >end\n- __teliva_timestamp: original\n  split:\n    >function split(s, d)\n    >  result = {}\n    >  for match in (s..d):gmatch(\"(.-)\"..d) do\n    >    table.insert(result, match);\n    >  end\n    >  return result\n    >end\n- __teliva_timestamp: original\n  map:\n    >-- only for arrays\n    >function map(l, f)\n    >  result = {}\n    >  for _, x in ipairs(l) do\n    >    table.insert(result, f(x))\n    >  end\n    >  return result\n    >end\n- __teliva_timestamp: original\n  reduce:\n    >-- only for arrays\n    >function reduce(l, f, init)\n    >  result = init\n    >  for _, x in ipairs(l) do\n    >    result = f(result, x)\n    >  end\n    >  return result\n    >end\n- __teliva_timestamp: original\n  filter:\n    >function filter(h, f)\n    >  result = {}\n    >  for k, v in pairs(h) do\n    >    if f(k, v) then\n    >      result[k] = v\n    >    end\n    >  end\n    >  return result\n    >end\n- __teliva_timestamp: original\n  ifilter:\n    >-- only for arrays\n    >function ifilter(l, f)\n    >  result = {}\n    >  for _, x in ipairs(l) do\n    >    if f(x) then\n    >      table.insert(result, x)\n    >    end\n    >  end\n    >  return result\n    >end\n- __teliva_timestamp: original\n  sort_letters:\n    >function sort_letters(s)\n    >  tmp = {}\n    >  for i=1,#s do\n    >    table.insert(tmp, s[i])\n    >  end\n    >  table.sort(tmp)\n    >  local result = ''\n    >  for _, c in pairs(tmp) do\n    >    result = result..c\n    >  end\n    >  return result\n    >end\n    >\n    >function test_sort_letters(s)\n    >  check_eq(sort_letters(''), '', 'test_sort_letters: empty')\n    >  check_eq(sort_letters('ba'), 'ab', 'test_sort_letters: non-empty')\n    >  check_eq(sort_letters('abba'), 'aabb', 'test_sort_letters: duplicates')\n    >end\n- __teliva_timestamp: original\n  count_letters:\n    >-- TODO: handle unicode\n    >function count_letters(s)\n    >  local result = {}\n    >  for i=1,s:len() do\n    >    local c = s[i]\n    >    if result[c] == nil then\n    >      result[c] = 1\n    >    else\n    >      result[c] = result[c] + 1\n    >    end\n    >  end\n    >  return result\n    >end\n- __teliva_timestamp: original\n  count:\n    >-- turn an array of elements into a map from elements to their frequency\n    >-- analogous to count_letters for non-strings\n    >function count(a)\n    >  local result = {}\n    >  for i, v in ipairs(a) do\n    >    if result[v] == nil then\n    >      result[v] = 1\n    >    else\n    >      result[v] = result[v] + 1\n    >    end\n    >  end\n    >  return result\n    >end\n- __teliva_timestamp: original\n  union:\n    >function union(a, b)\n    >  for k, v in pairs(b) do\n    >    a[k] = v\n    >  end\n    >  return a\n    >end\n- __teliva_timestamp: original\n  subtract:\n    >-- set subtraction\n    >function subtract(a, b)\n    >  for k, v in pairs(b) do\n    >    a[k] = nil\n    >  end\n    >  return a\n    >end\n- __teliva_timestamp: original\n  all:\n    >-- universal quantifier on sets\n    >function all(s, f)\n    >  for k, v in pairs(s) do\n    >    if not f(k, v) then\n    >      return false\n    >    end\n    >  end\n    >  return true\n    >end\n- __teliva_timestamp: original\n  to_array:\n    >-- turn a set into an array\n    >-- drops values\n    >function to_array(h)\n    >  local result = {}\n    >  for k, _ in pairs(h) do\n    >    table.insert(result, k)\n    >  end\n    >  return result\n    >end\n- __teliva_timestamp: original\n  append:\n    >-- concatenate list 'elems' into 'l', modifying 'l' in the process\n    >function append(l, elems)\n    >  for i=1,#elems do\n    >    table.insert(l, elems[i])\n    >  end\n    >end\n- __teliva_timestamp: original\n  prepend:\n    >-- concatenate list 'elems' into the start of 'l', modifying 'l' in the process\n    >function prepend(l, elems)\n    >  for i=1,#elems do\n    >    table.insert(l, i, elems[i])\n    >  end\n    >end\n- __teliva_timestamp: original\n  all_but:\n    >function all_but(x, idx)\n    >  if type(x) == 'table' then\n    >    local result = {}\n    >    for i, elem in ipairs(x) do\n    >      if i ~= idx then\n    >        table.insert(result,elem)\n    >      end\n    >    end\n    >    return result\n    >  elseif type(x) == 'string' then\n    >    if idx < 1 then return x:sub(1) end\n    >    return x:sub(1, idx-1) .. x:sub(idx+1)\n    >  else\n    >    error('all_but: unsupported type '..type(x))\n    >  end\n    >end\n    >\n    >function test_all_but()\n    >  check_eq(all_but('', 0), '', 'all_but: empty')\n    >  check_eq(all_but('abc', 0), 'abc', 'all_but: invalid low index')\n    >  check_eq(all_but('abc', 4), 'abc', 'all_but: invalid high index')\n    >  check_eq(all_but('abc', 1), 'bc', 'all_but: first index')\n    >  check_eq(all_but('abc', 3), 'ab', 'all_but: final index')\n    >  check_eq(all_but('abc', 2), 'ac', 'all_but: middle index')\n    >end\n- __teliva_timestamp: original\n  set:\n    >function set(l)\n    >  local result = {}\n    >  for i, elem in ipairs(l) do\n    >    result[elem] = true\n    >  end\n    >  return result\n    >end\n- __teliva_timestamp: original\n  set_eq:\n    >function set_eq(l1, l2)\n    >  return eq(set(l1), set(l2))\n    >end\n    >\n    >function test_set_eq()\n    >  check(set_eq({1}, {1}), 'set_eq: identical')\n    >  check(not set_eq({1, 2}, {1, 3}), 'set_eq: different')\n    >  check(set_eq({1, 2}, {2, 1}), 'set_eq: order')\n    >  check(set_eq({1, 2, 2}, {2, 1}), 'set_eq: duplicates')\n    >end\n- __teliva_timestamp: original\n  clear:\n    >function clear(lines)\n    >  while #lines > 0 do\n    >    table.remove(lines)\n    >  end\n    >end\n- __teliva_timestamp: original\n  zap:\n    >function zap(target, src)\n    >  clear(target)\n    >  append(target, src)\n    >end\n- __teliva_timestamp: original\n  mfactorial:\n    >-- memoized version of factorial\n    >-- doesn't memoize recursive calls, but may be good enough\n    >mfactorial = memo1(factorial)\n- __teliva_timestamp: original\n  factorial:\n    >function factorial(n)\n    >  local result = 1\n    >  for i=1,n do\n    >    result = result*i\n    >  end\n    >  return result\n    >end\n- __teliva_timestamp: original\n  memo1:\n    >-- a higher-order function that takes a function of a single arg\n    >-- (that never returns nil)\n    >-- and returns a memoized version of it\n    >function memo1(f)\n    >  local memo = {}\n    >  return function(x)\n    >    if memo[x] == nil then\n    >      memo[x] = f(x)\n    >    end\n    >    return memo[x]\n    >  end\n    >end\n    >\n    >-- mfactorial doesn't seem noticeably faster\n    >function test_memo1()\n    >  for i=0,30 do\n    >    check_eq(mfactorial(i), factorial(i), 'memo1 over factorial: '..str(i))\n    >  end\n    >end\n- __teliva_timestamp: original\n  num_permutations:\n    >-- number of permutations of n distinct objects, taken r at a time\n    >function num_permutations(n, r)\n    >  return factorial(n)/factorial(n-r)\n    >end\n    >\n    >-- mfactorial doesn't seem noticeably faster\n    >function test_memo1()\n    >  for i=0,30 do\n    >    for j=0,i do\n    >      check_eq(num_permutations(i, j), mfactorial(i)/mfactorial(i-j), 'num_permutations memoizes: '..str(i)..'P'..str(j))\n    >    end\n    >  end\n    >end\n- __teliva_timestamp: original\n  menu:\n    >-- To show app-specific hotkeys in the menu bar, add hotkey/command\n    >-- arrays of strings to the menu array.\n    >menu = {}\n- __teliva_timestamp: original\n  Window:\n    >Window = curses.stdscr()\n- __teliva_timestamp: original\n  window:\n    >-- constructor for fake screen and window\n    >-- call it like this:\n    >--   local w = window{\n    >--     kbd=kbd('abc'),\n    >--     scr=scr{h=5, w=4},\n    >--   }\n    >-- eventually it'll do everything a real ncurses window can\n    >function window(h)\n    >  h.__index = h\n    >  setmetatable(h, h)\n    >  h.__index = function(table, key)\n    >    return rawget(h, key)\n    >  end\n    >  h.attrset = function(self, x)\n    >    self.scr.attrs = x\n    >  end\n    >  h.attron = function(self, x)\n    >    -- currently same as attrset since Lua 5.1 doesn't have bitwise operators\n    >    -- doesn't support multiple attrs at once\n    >--    local old = self.scr.attrs\n    >--    self.scr.attrs = old|x\n    >    self.scr.attrs = x\n    >  end\n    >  h.attroff = function(self, x)\n    >    -- currently borked since Lua 5.1 doesn't have bitwise operators\n    >    -- doesn't support multiple attrs at once\n    >--    local old = self.scr.attrs\n    >--    self.scr.attrs = old & (~x)\n    >    self.scr.attrs = curses.A_NORMAL\n    >  end\n    >  h.getch = function(self)\n    >    local c = table.remove(h.kbd, 1)\n    >    if c == nil then return c end\n    >    return string.byte(c)  -- for verisimilitude with ncurses\n    >  end\n    >  h.addch = function(self, c)\n    >    local scr = self.scr\n    >    if c == '\\n' then\n    >      scr.cursy = scr.cursy+1\n    >      scr.cursx = 0\n    >      return\n    >    end\n    >    if scr.cursy <= scr.h then\n    >      scr[scr.cursy][scr.cursx] = {data=c, attrs=scr.attrs}\n    >      scr.cursx = scr.cursx+1\n    >      if scr.cursx > scr.w then\n    >        scr.cursy = scr.cursy+1\n    >        scr.cursx = 1\n    >      end\n    >    end\n    >  end\n    >  h.addstr = function(self, s)\n    >    for i=1,s:len() do\n    >      self:addch(s[i])\n    >    end\n    >  end\n    >  h.mvaddch = function(self, y, x, c)\n    >    self.scr.cursy = y\n    >    self.scr.cursx = x\n    >    self:addch(c)\n    >  end\n    >  h.mvaddstr = function(self, y, x, s)\n    >    self.scr.cursy = y\n    >    self.scr.cursx = x\n    >    self:addstr(s)\n    >  end\n    >  h.clear = function(self)\n    >    clear_scr(self.scr)\n    >  end\n    >  h.refresh = function(self)\n    >    -- nothing\n    >  end\n    >  return h\n    >end\n- __teliva_timestamp: original\n  kbd:\n    >function kbd(keys)\n    >  local result = {}\n    >  for i=1,keys:len() do\n    >    table.insert(result, keys[i])\n    >  end\n    >  return result\n    >end\n- __teliva_timestamp: original\n  scr:\n    >function scr(props)\n    >  props.cursx = 1\n    >  props.cursy = 1\n    >  clear_scr(props)\n    >  return props\n    >end\n- __teliva_timestamp: original\n  clear_scr:\n    >function clear_scr(props)\n    >  props.cursy = 1\n    >  props.cursx = 1\n    >  for y=1,props.h do\n    >    props[y] = {}\n    >    for x=1,props.w do\n    >      props[y][x] = {data=' ', attrs=curses.A_NORMAL}\n    >    end\n    >  end\n    >  return props\n    >end\n- __teliva_timestamp: original\n  check_screen:\n    >function check_screen(window, contents, message)\n    >  local x, y = 1, 1\n    >  for i=1,contents:len() do\n    >    check_eq(window.scr[y][x].data, contents[i], message..'/'..y..','..x)\n    >    x = x+1\n    >    if x > window.scr.w then\n    >      y = y+1\n    >      x = 1\n    >    end\n    >  end\n    >end\n    >\n    >-- putting it all together, an example test of both keyboard and screen\n    >function test_check_screen()\n    >  local lines = {\n    >    c='123',\n    >    d='234',\n    >    a='345',\n    >    b='456',\n    >  }\n    >  local w = window{\n    >    kbd=kbd('abc'),\n    >    scr=scr{h=3, w=5},\n    >  }\n    >  local y = 1\n    >  while true do\n    >    local b = w:getch()\n    >    if b == nil then break end\n    >    w:mvaddstr(y, 1, lines[string.char(b)])\n    >    y = y+1\n    >  end\n    >  check_screen(w, '345  '..\n    >                  '456  '..\n    >                  '123  ',\n    >              'test_check_screen')\n    >end\n- __teliva_timestamp: original\n  check_reverse:\n    >function check_reverse(window, contents, message)\n    >  local x, y = 1, 1\n    >  for i=1,contents:len() do\n    >    if contents[i] ~= ' ' then\n    >      -- hacky version while we're without bitwise operators on Lua 5.1\n    >--      check(window.scr[y][x].attrs & curses.A_REVERSE, message..'/'..y..','..x)\n    >      check_eq(window.scr[y][x].attrs, curses.A_REVERSE, message..'/'..y..','..x)\n    >    else\n    >      -- hacky version while we're without bitwise operators on Lua 5.1\n    >--      check(window.scr[y][x].attrs & (~curses.A_REVERSE), message..'/'..y..','..x)\n    >      check(window.scr[y][x].attrs ~= curses.A_REVERSE, message..'/'..y..','..x)\n    >    end\n    >    x = x+1\n    >    if x > window.scr.w then\n    >      y = y+1\n    >      x = 1\n    >    end\n    >  end\n    >end\n- __teliva_timestamp: original\n  check_bold:\n    >function check_bold(window, contents, message)\n    >  local x, y = 1, 1\n    >  for i=1,contents:len() do\n    >    if contents[i] ~= ' ' then\n    >      -- hacky version while we're without bitwise operators on Lua 5.1\n    >--      check(window.scr[y][x].attrs & curses.A_BOLD, message..'/'..y..','..x)\n    >      check_eq(window.scr[y][x].attrs, curses.A_BOLD, message..'/'..y..','..x)\n    >    else\n    >      -- hacky version while we're without bitwise operators on Lua 5.1\n    >--      check(window.scr[y][x].attrs & (~curses.A_BOLD), message..'/'..y..','..x)\n    >      check(window.scr[y][x].attrs ~= curses.A_BOLD, message..'/'..y..','..x)\n    >    end\n    >    x = x+1\n    >    if x > window.scr.w then\n    >      y = y+1\n    >      x = 1\n    >    end\n    >  end\n    >end\n- __teliva_timestamp: original\n  check_color:\n    >-- check which parts of a screen have the given color_pair\n    >function check_color(window, cp, contents, message)\n    >  local x, y = 1, 1\n    >  for i=1,contents:len() do\n    >    if contents[i] ~= ' ' then\n    >      -- hacky version while we're without bitwise operators on Lua 5.1\n    >--      check(window.scr[y][x].attrs & curses.color_pair(cp), message..'/'..y..','..x)\n    >      check_eq(window.scr[y][x].attrs, curses.color_pair(cp), message..'/'..y..','..x)\n    >    else\n    >      -- hacky version while we're without bitwise operators on Lua 5.1\n    >--      check(window.scr[y][x].attrs & (~curses.A_BOLD), message..'/'..y..','..x)\n    >      check(window.scr[y][x].attrs ~= curses.color_pair(cp), message..'/'..y..','..x)\n    >    end\n    >    x = x+1\n    >    if x > window.scr.w then\n    >      y = y+1\n    >      x = 1\n    >    end\n    >  end\n    >end\n- __teliva_timestamp: original\n  sep:\n    >-- horizontal separator\n    >function sep(window)\n    >  local y, _ = window:getyx()\n    >  window:mvaddstr(y+1, 0, '')\n    >  local _, cols = window:getmaxyx()\n    >  for col=1,cols do\n    >    window:addstr('_')\n    >  end\n    >end\n- __teliva_timestamp: original\n  render:\n    >function render(window)\n    >  window:clear()\n    >  -- draw stuff to screen here\n    >  window:attron(curses.A_BOLD)\n    >  window:mvaddstr(1, 5, \"example app\")\n    >  window:attrset(curses.A_NORMAL)\n    >  for i=0,15 do\n    >    window:attrset(curses.color_pair(i))\n    >    window:mvaddstr(3+i, 5, \"========================\")\n    >  end\n    >  window:refresh()\n    >end\n- __teliva_timestamp: original\n  update:\n    >function update(window)\n    >  local key = window:getch()\n    >  -- process key here\n    >end\n- __teliva_timestamp: original\n  init_colors:\n    >function init_colors()\n    >  for i=0,7 do\n    >    curses.init_pair(i, i, -1)\n    >  end\n    >  curses.init_pair(8, 7, 0)\n    >  curses.init_pair(9, 7, 1)\n    >  curses.init_pair(10, 7, 2)\n    >  curses.init_pair(11, 7, 3)\n    >  curses.init_pair(12, 7, 4)\n    >  curses.init_pair(13, 7, 5)\n    >  curses.init_pair(14, 7, 6)\n    >  curses.init_pair(15, -1, 15)\n    >end\n- __teliva_timestamp: original\n  main:\n    >function main()\n    >  init_colors()\n    >\n    >  while true do\n    >    render(Window)\n    >    update(Window)\n    >  end\n    >end\n- __teliva_timestamp:\n    >Thu Mar 17 21:21:29 2022\n  program:\n    >program = {lines = nil, cursor = nil}\n    >-- lines: [line]\n    >-- cursor: int, index into lines\n    >-- line: {[word], cursor}\n    >-- word: {string, cursor}\n- __teliva_timestamp:\n    >Thu Mar 17 21:27:38 2022\n  render:\n    >function render(window)\n    >  local key = window:getch()\n    >  for row, line in ipair(program.lines) do\n    >    for col, word in ipair(line.words) do\n    >      window:addstr(word)\n    >      window:addstr(' ')\n    >    end\n    >    window:addstr('\\n')\n    >  end\n    >end\n- __teliva_timestamp:\n    >Thu Mar 17 21:28:39 2022\n  render:\n    >function render(window)\n    >  local key = window:getch()\n    >  for row, line in ipairs(program.lines) do\n    >    for col, word in pairs(line.words) do\n    >      window:addstr(word)\n    >      window:addstr(' ')\n    >    end\n    >    window:addstr('\\n')\n    >  end\n    >end\n- __teliva_timestamp:\n    >Thu Mar 17 21:31:33 2022\n  Program:\n    >Program = {\n    >  cursor_y = 1,\n    >  lines = {\n    >    {\n    >      cursor_x = 1,\n    >      words = {\n    >        {\n    >          cursor = 0,\n    >          data = '',\n    >        },\n    >      },\n    >    },\n    >  },\n    >}\n    >\n    >\n    >\n    >\n    >\n- __teliva_timestamp:\n    >Thu Mar 17 21:32:05 2022\n  render:\n    >function render(window)\n    >  window:clear()\n    >  for row, line in ipairs(program.lines) do\n    >    for col, word in pairs(line.words) do\n    >      window:addstr(word)\n    >      window:addstr(' ')\n    >    end\n    >    window:addstr('\\n')\n    >  end\n    >  window:refresh()\n    >end\n- __teliva_timestamp:\n    >Thu Mar 17 21:32:22 2022\n  render:\n    >function render(window)\n    >  window:clear()\n    >  for row, line in ipairs(program.lines) do\n    >    for col, word in pairs(line.words) do\n    >      window:addstr(word.data)\n    >      window:addstr(' ')\n    >    end\n    >    window:addstr('\\n')\n    >  end\n    >  window:refresh()\n    >end\n- __teliva_timestamp:\n    >Thu Mar 17 21:33:00 2022\n  update:\n    >function update(window)\n    >  local key = window:getch()\n    >  if key == ' ' then\n    >    print('space')\n    >    window:getch()\n    >  end\n    >end\n- __teliva_timestamp:\n    >Thu Mar 17 21:33:17 2022\n  update:\n    >function update(window)\n    >  local key = window:getch()\n    >  if key == string.byte(' ') then\n    >    print('space')\n    >    window:getch()\n    >  end\n    >end\n- __teliva_timestamp:\n    >Thu Mar 17 21:33:31 2022\n  update:\n    >function update(window)\n    >  local key = string.char(window:getch())\n    >  if key == ' ' then\n    >    print('space')\n    >    window:getch()\n    >  end\n    >end\n- __teliva_timestamp:\n    >Thu Mar 17 21:36:14 2022\n  __teliva_note:\n    >very initial skeleton\n    >- just render, no eval\n    >- no stack rendering\n    >- no cursor (just appends)\n  update:\n    >function update(window)\n    >  local key = string.char(window:getch())\n    >  if key == ' ' then\n    >    table.insert(program.lines[1].words, {data='', cursor=0})\n    >  else\n    >    local words = program.lines[1].words\n    >    words[#words].data = words[#words].data .. key\n    >  end\n    >end\n- __teliva_timestamp:\n    >Thu Mar 17 21:39:24 2022\n  main:\n    >function main()\n    >  init_colors()\n    >\n    >  while true do\n    >    render(Window)\n    >    update(Window)\n    >  end\n    >end\n    >\n    >function test_basic_render()\n    >  \n- __teliva_timestamp:\n    >Thu Mar 17 21:43:05 2022\n  main:\n    >function main()\n    >  init_colors()\n    >\n    >  while true do\n    >    render(Window, Program)\n    >    update(Window, Program)\n    >  end\n    >end\n- __teliva_timestamp:\n    >Thu Mar 17 21:43:11 2022\n  render:\n    >function render(window, program)\n    >  window:clear()\n    >  for row, line in ipairs(program.lines) do\n    >    for col, word in pairs(line.words) do\n    >      window:addstr(word.data)\n    >      window:addstr(' ')\n    >    end\n    >    window:addstr('\\n')\n    >  end\n    >  window:refresh()\n    >end\n    >\n    >function test_render()\n    >  local w = window{\n    >    kbd=kbd(''),\n    >    scr=scr{h=3, w=5},\n    >  }\n    >end\n- __teliva_timestamp:\n    >Thu Mar 17 21:43:20 2022\n  update:\n    >function update(window, program, key)\n    >  if key == ' ' then\n    >    table.insert(program.lines[1].words, {data='', cursor=0})\n    >  else\n    >    local words = program.lines[1].words\n    >    words[#words].data = words[#words].data .. key\n    >  end\n    >end\n- __teliva_timestamp:\n    >Thu Mar 17 21:58:10 2022\n  Program:\n    >Program = empty_program()\n- __teliva_timestamp:\n    >Thu Mar 17 21:58:10 2022\n  empty_program:\n    >function empty_program()\n    >  return {\n    >    cursor_y = 1,\n    >    lines = {\n    >      {\n    >        cursor_x = 1,\n    >        words = {\n    >          {\n    >            cursor = 0,\n    >            data = '',\n    >          },\n    >        },\n    >      },\n    >    },\n    >  }\n    >end\n- __teliva_timestamp:\n    >Thu Mar 17 22:36:24 2022\n  render:\n    >function render(window, program)\n    >  window:clear()\n    >  for row, line in ipairs(program.lines) do\n    >    for col, word in pairs(line.words) do\n    >      window:addstr(word.data)\n    >      window:addstr(' ')\n    >    end\n    >    window:addstr('\\n')\n    >  end\n    >  window:refresh()\n    >end\n- __teliva_timestamp:\n    >Thu Mar 17 22:41:01 2022\n  __teliva_note:\n    >a passing test\n  main:\n    >function main()\n    >  init_colors()\n    >\n    >  while true do\n    >    render(Window, Program)\n    >    local key = string.char(Window:getch())  -- nodelay can't be set\n    >    update(Window, Program, key)\n    >  end\n    >end\n    >\n    >function test_incomplete()\n    >  local w = window{\n    >    kbd=kbd('ab c'),\n    >    scr=scr{h=3, w=5},\n    >  }\n    >  local prog = empty_program()\n    >  while true do\n    >    render(w, prog)\n    >    local key = w:getch()\n    >    if key == nil then break end\n    >    update(w, prog, string.char(key))\n    >  end\n    >  check_screen(w, 'ab c '..\n    >                  '     '..\n    >                  '     ',\n    >               'test_main_incomplete')\n    >end\n- __teliva_timestamp: original\n  doc:blurb:\n    >A concatenative programming language with a live-updating debug UI.\n    >by Kartik Agaram and Sumeet Agarwal\n    >\n    >Highly incomplete.\n    >\n    >Previous prototypes:\n    >  - https://archive.org/details/akkartik-2min-2020-12-06\n    >  - https://merveilles.town/@akkartik/105759816342173743\n"
  },
  {
    "path": "chesstv.tlv",
    "content": "# .tlv file generated by https://github.com/akkartik/teliva\n# You may edit it if you are careful; however, you may see cryptic errors if you\n# violate Teliva's assumptions.\n#\n# .tlv files are representations of Teliva programs. Teliva programs consist of\n# sequences of definitions. Each definition is a table of key/value pairs. Keys\n# and values are both strings.\n#\n# Lines in .tlv files always follow exactly one of the following forms:\n# - comment lines at the top of the file starting with '#' at column 0\n# - beginnings of definitions starting with '- ' at column 0, followed by a\n#   key/value pair\n# - key/value pairs consisting of '  ' at column 0, containing either a\n#   spaceless value on the same line, or a multi-line value\n# - multiline values indented by more than 2 spaces, starting with a '>'\n#\n# If these constraints are violated, Teliva may unceremoniously crash. Please\n# report bugs at http://akkartik.name/contact\n- __teliva_timestamp: original\n  check:\n    >function check(x, msg)\n    >  if x then\n    >    Window:addch('.')\n    >  else\n    >    print('F - '..msg)\n    >    print('  '..str(x)..' is false/nil')\n    >    teliva_num_test_failures = teliva_num_test_failures + 1\n    >    -- overlay first test failure on editors\n    >    if teliva_first_failure == nil then\n    >      teliva_first_failure = msg\n    >    end\n    >  end\n    >end\n- __teliva_timestamp: original\n  check_eq:\n    >function check_eq(x, expected, msg)\n    >  if eq(x, expected) then\n    >    Window:addch('.')\n    >  else\n    >    print('F - '..msg)\n    >    print('  expected '..str(expected)..' but got '..str(x))\n    >    teliva_num_test_failures = teliva_num_test_failures + 1\n    >    -- overlay first test failure on editors\n    >    if teliva_first_failure == nil then\n    >      teliva_first_failure = msg\n    >    end\n    >  end\n    >end\n- __teliva_timestamp: original\n  eq:\n    >function eq(a, b)\n    >  if type(a) ~= type(b) then return false end\n    >  if type(a) == 'table' then\n    >    if #a ~= #b then return false end\n    >    for k, v in pairs(a) do\n    >      if b[k] ~= v then\n    >        return false\n    >      end\n    >    end\n    >    for k, v in pairs(b) do\n    >      if a[k] ~= v then\n    >        return false\n    >      end\n    >    end\n    >    return true\n    >  end\n    >  return a == b\n    >end\n- __teliva_timestamp: original\n  str:\n    >-- smarter tostring\n    >-- slow; used only for debugging\n    >function str(x)\n    >  if type(x) == 'table' then\n    >    local result = ''\n    >    result = result..#x..'{'\n    >    for k, v in pairs(x) do\n    >      result = result..str(k)..'='..str(v)..', '\n    >    end\n    >    result = result..'}'\n    >    return result\n    >  elseif type(x) == 'string' then\n    >    return '\"'..x..'\"'\n    >  end\n    >  return tostring(x)\n    >end\n- __teliva_timestamp: original\n  Window:\n    >Window = curses.stdscr()\n    >-- animation-based app\n    >Window:nodelay(true)\n    >lines, cols = Window:getmaxyx()\n- __teliva_timestamp: original\n  current_game:\n    >current_game = {}\n- __teliva_timestamp: original\n  piece_glyph:\n    >piece_glyph = {\n    >  -- for legibility, white pieces also use unicode glyphs for black pieces\n    >  -- we rely on colors to distinguish them\n    >  K = 0x265a,\n    >  Q = 0x265b,\n    >  R = 0x265c,\n    >  B = 0x265d,\n    >  N = 0x265e,\n    >  P = 0x265f,\n    >  k = 0x265a,\n    >  q = 0x265b,\n    >  r = 0x265c,\n    >  b = 0x265d,\n    >  n = 0x265e,\n    >  p = 0x265f,\n    >}\n- __teliva_timestamp: original\n  top_player:\n    >function top_player(current_game)\n    >  if current_game.players[1].color == \"black\" then\n    >    return current_game.players[1]\n    >  end\n    >  return current_game.players[2]\n    >end\n- __teliva_timestamp: original\n  bottom_player:\n    >function bottom_player(current_game)\n    >  if current_game.players[1].color == \"white\" then\n    >    return current_game.players[1]\n    >  end\n    >  return current_game.players[2]\n    >end\n- __teliva_timestamp: original\n  render_player:\n    >function render_player(y, x, player)\n    >  Window:mvaddstr(y, x, player.user.name)\n    >  Window:addstr(\" · \")\n    >  Window:addstr(tostring(player.rating))\n    >end\n- __teliva_timestamp: original\n  render_square:\n    >function render_square(current_game, rank, file, highlighted_squares)\n    >  -- decide whether to highlight\n    >  local hl = 0\n    >  if (rank == highlighted_squares.from.rank and file == highlighted_squares.from.file)\n    >      or (rank == highlighted_squares.to.rank and file == highlighted_squares.to.file) then\n    >    hl = 4\n    >  end\n    >  if (rank+file)%2 == 1 then\n    >    -- light square\n    >    Window:attrset(curses.color_pair(1+hl))\n    >  else\n    >    -- dark square\n    >    Window:attrset(curses.color_pair(3+hl))\n    >  end\n    >  Window:mvaddstr((8 - rank + 1)*3,   file*5, \"     \")\n    >  Window:mvaddstr((8 - rank + 1)*3+1, file*5, \"     \")\n    >  Window:mvaddstr((8 - rank + 1)*3+2, file*5, \"     \")\n    >  Window:attrset(curses.A_NORMAL)\n    >end\n- __teliva_timestamp: original\n  render_fen_rank:\n    >function render_fen_rank(rank, fen_rank, highlighted_squares)\n    >  local file = 1\n    >  for x in fen_rank:gmatch(\".\") do\n    >    if x:match(\"%d\") then\n    >      file = file + tonumber(x)\n    >    else\n    >      -- decide whether to highlight\n    >      local hl = 0\n    >      if (rank == highlighted_squares.from.rank and file == highlighted_squares.from.file)\n    >          or (rank == highlighted_squares.to.rank and file == highlighted_squares.to.file) then\n    >        hl = 4\n    >      end\n    >      if (rank+file)%2 == 1 then\n    >        if x < 'Z' then\n    >          -- white piece on light square\n    >          Window:attrset(curses.color_pair(1+hl))\n    >        else\n    >          -- black piece on light square\n    >          Window:attrset(curses.color_pair(2+hl))\n    >        end\n    >      else\n    >        if x < 'Z' then\n    >          -- white piece on dark square\n    >          Window:attrset(curses.color_pair(3+hl))\n    >        else\n    >          -- black piece on dark square\n    >          Window:attrset(curses.color_pair(4+hl))\n    >        end\n    >      end\n    >      Window:mvaddstr((8 - rank + 1)*3+1, file*5+2, utf8(piece_glyph[x]))\n    >      Window:attrset(curses.A_NORMAL)\n    >      file = file + 1\n    >    end\n    >  end\n    >end\n- __teliva_timestamp: original\n  render_time:\n    >function render_time(y, x, seconds)\n    >  if seconds == nil then return end\n    >  Window:mvaddstr(y, x, tostring(math.floor(seconds/60)))\n    >  Window:addstr(string.format(\":%02d\", seconds%60))\n    >end\n- __teliva_timestamp: original\n  render_board:\n    >function render_board(current_game)\n    >--?   Window:mvaddstr(1, 50, dump(current_game.fen))\n    >--?   Window:mvaddstr(6, 50, dump(current_game.previously_moved_squares))\n    >  render_player(2, 5, top_player(current_game))\n    >  render_time(2, 35, current_game.bc)\n    >  for rank=8,1,-1 do\n    >    for file=1,8 do\n    >      render_square(current_game, rank, file, current_game.previously_moved_squares)\n    >    end\n    >    render_fen_rank(rank, current_game.fen_rank[8-rank+1], current_game.previously_moved_squares)\n    >  end\n    >  render_player(27, 5, bottom_player(current_game))\n    >  render_time(27, 35, current_game.wc)\n    >end\n- __teliva_timestamp: original\n  parse_lm:\n    >function parse_lm(move)\n    >--?   Window:mvaddstr(4, 50, move)\n    >  local file1 = string.byte(move:sub(1, 1)) - 96  -- 'a'-1\n    >  local rank1 = string.byte(move:sub(2, 2)) - 48  -- '0'\n    >  local file2 = string.byte(move:sub(3, 3)) - 96  -- 'a'-1\n    >  local rank2 = string.byte(move:sub(4, 4)) - 48  -- '0'\n    >--?   Window:mvaddstr(5, 50, dump({{rank1, file1}, {rank2, file2}}))\n    >  return {from={rank=rank1, file=file1}, to={rank=rank2, file=file2}}\n    >end\n- __teliva_timestamp: original\n  render:\n    >function render(chunk)\n    >  local o = json.decode(chunk)\n    >  if o.t == \"featured\" then\n    >    current_game = o.d\n    >--?     current_game.lm = \"__\"\n    >    current_game.previously_moved_squares = {from={rank=0, file=0}, to={rank=0, file=0}}  -- no highlight\n    >  else\n    >    current_game.fen = o.d.fen\n    >    current_game.wc = o.d.wc\n    >    current_game.bc = o.d.bc\n    >--?     current_game.lm = o.d.lm\n    >    current_game.previously_moved_squares = parse_lm(o.d.lm)\n    >--?     Window:nodelay(false)\n    >--?     Window:mvaddstr(3, 50, \"paused\")\n    >  end\n    >  current_game.fen_rank = split(current_game.fen, \"%w+\")\n    >  render_board(current_game)\n    >  Window:refresh()\n    >end\n- __teliva_timestamp: original\n  init_colors:\n    >function init_colors()\n    >  -- colors\n    >  local light_piece = 1\n    >  local dark_piece = 0\n    >  local light_square = 252\n    >  local dark_square = 242\n    >  local light_last_moved_square = 229\n    >  local dark_last_moved_square = 226\n    >  -- initialize colors\n    >  curses.init_pair(1, light_piece, light_square)\n    >  curses.init_pair(2, dark_piece, light_square)\n    >  curses.init_pair(3, light_piece, dark_square)\n    >  curses.init_pair(4, dark_piece, dark_square)\n    >  curses.init_pair(5, light_piece, light_last_moved_square)\n    >  curses.init_pair(6, dark_piece, light_last_moved_square)\n    >  curses.init_pair(7, light_piece, dark_last_moved_square)\n    >  curses.init_pair(8, dark_piece, dark_last_moved_square)\n    >end\n- __teliva_timestamp: original\n  main:\n    >function main()\n    >  init_colors()\n    >  local request = {\n    >    url = \"https://lichess.org/api/tv/feed\",\n    >    sink = function(chunk, err)\n    >             if chunk then\n    >               Window:clear()\n    >               render(chunk)\n    >               Window:getch()\n    >             end\n    >             return 1\n    >           end,\n    >  }\n    >  http.request(request)\n    >end\n- __teliva_timestamp: original\n  utf8:\n    >-- https://stackoverflow.com/questions/7983574/how-to-write-a-unicode-symbol-in-lua\n    >function utf8(decimal)\n    >  local bytemarkers = { {0x7FF,192}, {0xFFFF,224}, {0x1FFFFF,240} }\n    >  if decimal<128 then return string.char(decimal) end\n    >  local charbytes = {}\n    >  for bytes,vals in ipairs(bytemarkers) do\n    >    if decimal<=vals[1] then\n    >      for b=bytes+1,2,-1 do\n    >        local mod = decimal%64\n    >        decimal = (decimal-mod)/64\n    >        charbytes[b] = string.char(128+mod)\n    >      end\n    >      charbytes[1] = string.char(vals[2]+decimal)\n    >      break\n    >    end\n    >  end\n    >  return table.concat(charbytes)\n    >end\n- __teliva_timestamp: original\n  split:\n    >function split(s, pat)\n    >  result = {}\n    >  for x in s:gmatch(pat) do\n    >    table.insert(result, x)\n    >  end\n    >  return result\n    >end\n- __teliva_timestamp: original\n  dump:\n    >-- https://stackoverflow.com/questions/9168058/how-to-dump-a-table-to-console\n    >function dump(o)\n    >  if type(o) == 'table' then\n    >    local s = '{ '\n    >    for k,v in pairs(o) do\n    >      if type(k) ~= 'number' then k = '\"'..k..'\"' end\n    >      s = s .. '['..k..'] = ' .. dump(v) .. ','\n    >    end\n    >    return s .. '} '\n    >  else\n    >    return tostring(o)\n    >  end\n    >end\n- __teliva_timestamp:\n    >Sat Feb 12 18:57:02 2022\n  __teliva_note:\n    >better UX when app isn't permitted to access the network\n  main:\n    >function main()\n    >  init_colors()\n    >  local request = {\n    >    url = \"https://lichess.org/api/tv/feed\",\n    >    sink = function(chunk, err)\n    >             if chunk then\n    >               Window:clear()\n    >               -- main event loop is here\n    >               render(chunk)\n    >               Window:getch()\n    >             end\n    >             return 1\n    >           end,\n    >  }\n    >  http.request(request)\n    >  -- degenerate event loop just to show errors in sandboxing, etc.\n    >  while true do Window:getch(); end\n    >end\n- __teliva_timestamp:\n    >Thu Feb 17 20:00:23 2022\n  doc:blurb:\n    >A demo of Teliva's support for networking: watch the current live chess game from Lichess TV.\n    >\n    >Compare with https://lichess.org/tv on a browser.\n"
  },
  {
    "path": "counter.tlv",
    "content": "# .tlv file generated by https://github.com/akkartik/teliva\n# You may edit it if you are careful; however, you may see cryptic errors if you\n# violate Teliva's assumptions.\n#\n# .tlv files are representations of Teliva programs. Teliva programs consist of\n# sequences of definitions. Each definition is a table of key/value pairs. Keys\n# and values are both strings.\n#\n# Lines in .tlv files always follow exactly one of the following forms:\n# - comment lines at the top of the file starting with '#' at column 0\n# - beginnings of definitions starting with '- ' at column 0, followed by a\n#   key/value pair\n# - key/value pairs consisting of '  ' at column 0, containing either a\n#   spaceless value on the same line, or a multi-line value\n# - multiline values indented by more than 2 spaces, starting with a '>'\n#\n# If these constraints are violated, Teliva may unceremoniously crash. Please\n# report bugs at http://akkartik.name/contact\n- __teliva_timestamp: original\n  check:\n    >function check(x, msg)\n    >  if x then\n    >    Window:addch('.')\n    >  else\n    >    print('F - '..msg)\n    >    print('  '..str(x)..' is false/nil')\n    >    teliva_num_test_failures = teliva_num_test_failures + 1\n    >    -- overlay first test failure on editors\n    >    if teliva_first_failure == nil then\n    >      teliva_first_failure = msg\n    >    end\n    >  end\n    >end\n- __teliva_timestamp: original\n  check_eq:\n    >function check_eq(x, expected, msg)\n    >  if eq(x, expected) then\n    >    Window:addch('.')\n    >  else\n    >    print('F - '..msg)\n    >    print('  expected '..str(expected)..' but got '..str(x))\n    >    teliva_num_test_failures = teliva_num_test_failures + 1\n    >    -- overlay first test failure on editors\n    >    if teliva_first_failure == nil then\n    >      teliva_first_failure = msg\n    >    end\n    >  end\n    >end\n- __teliva_timestamp: original\n  eq:\n    >function eq(a, b)\n    >  if type(a) ~= type(b) then return false end\n    >  if type(a) == 'table' then\n    >    if #a ~= #b then return false end\n    >    for k, v in pairs(a) do\n    >      if b[k] ~= v then\n    >        return false\n    >      end\n    >    end\n    >    for k, v in pairs(b) do\n    >      if a[k] ~= v then\n    >        return false\n    >      end\n    >    end\n    >    return true\n    >  end\n    >  return a == b\n    >end\n- __teliva_timestamp: original\n  str:\n    >-- smarter tostring\n    >-- slow; used only for debugging\n    >function str(x)\n    >  if type(x) == 'table' then\n    >    local result = ''\n    >    result = result..#x..'{'\n    >    for k, v in pairs(x) do\n    >      result = result..str(k)..'='..str(v)..', '\n    >    end\n    >    result = result..'}'\n    >    return result\n    >  elseif type(x) == 'string' then\n    >    return '\"'..x..'\"'\n    >  end\n    >  return tostring(x)\n    >end\n- __teliva_timestamp: original\n  Window:\n    >Window = curses.stdscr()\n- __teliva_timestamp: original\n  n:\n    >n = 0\n- __teliva_timestamp: original\n  render:\n    >function render(window)\n    >  window:clear()\n    >  window:attron(curses.A_BOLD)\n    >  window:attron(curses.color_pair(6))\n    >  window:mvaddstr(10, 10, \"     \")\n    >  window:mvaddstr(10, 11, n)\n    >  window:attroff(curses.color_pair(6))\n    >  window:attroff(curses.A_BOLD)\n    >  window:refresh()\n    >end\n- __teliva_timestamp: original\n  menu:\n    >menu = {\n    >  {\"Enter\", \"increment\"}\n    >}\n- __teliva_timestamp: original\n  update:\n    >function update(window)\n    >  local key = window:getch()\n    >  if key == 10 then\n    >    n = n+1\n    >  end\n    >end\n- __teliva_timestamp: original\n  main:\n    >function main()\n    >  for i=1,7 do\n    >    curses.init_pair(i, 0, i)\n    >  end\n    >\n    >  while true do\n    >    render(Window)\n    >    update(Window)\n    >  end\n    >end\n- __teliva_timestamp:\n    >Thu Feb 17 20:01:19 2022\n  doc:blurb:\n    >Example app: counter widget\n    >\n    >Look at the menu below to see what keyboard shortcuts are available.\n"
  },
  {
    "path": "doc/contents.html",
    "content": "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\">\n<HTML>\n<HEAD>\n<TITLE>Teliva Reference Manual - contents</TITLE>\n<LINK REL=\"stylesheet\" TYPE=\"text/css\" HREF=\"lua.css\">\n<META HTTP-EQUIV=\"content-type\" CONTENT=\"text/html; charset=utf-8\">\n<STYLE TYPE=\"text/css\">\nul {\n\tlist-style-type: none ;\n}\n</STYLE>\n</HEAD>\n\n<BODY>\n\n<HR>\n<H1>\nTeliva Reference Manual\n</H1>\n\n<P>\nThis reference manual for Teliva is based on the official definition of the\nLua language.\n\n<P>\nPlease support Lua by <A HREF=\"http://www.lua.org/donations.html\">donating</A> or purchasing:\n<BLOCKQUOTE>\n<A HREF=\"http://www.amazon.com/exec/obidos/ASIN/8590379833/lua-indexmanual-20\">\n<IMG SRC=\"cover.png\" ALT=\"\" TITLE=\"buy from Amazon\" BORDER=1 ALIGN=\"left\" HSPACE=12>\n</A>\n<B>Lua 5.1 Reference Manual</B>\n<BR>by R. Ierusalimschy, L. H. de Figueiredo, W. Celes\n<BR>Lua.org, August 2006\n<BR>ISBN 85-903798-3-3\n<BR CLEAR=\"all\">\n</BLOCKQUOTE>\n\n<P>\n<A HREF=\"manual.html\">start</A>\n&middot;\n<A HREF=\"#contents\">contents</A>\n&middot;\n<A HREF=\"#index\">index</A>\n&middot;\n<A HREF=\"http://www.lua.org/manual/\">other versions</A>\n<HR>\n<SMALL>\nCopyright &copy; 2006&ndash;2012 Lua.org, PUC-Rio.\nFreely available under the terms of the\n<A HREF=\"http://www.lua.org/license.html\">Lua license</A>.\n</SMALL>\n\n<H2><A NAME=\"contents\">Contents</A></H2>\n<UL style=\"padding: 0\">\n<LI><A HREF=\"manual.html\">1 &ndash; Introduction</A>\n<P>\n<LI><A HREF=\"manual.html#2\">2 &ndash; The Language</A>\n<UL>\n<LI><A HREF=\"manual.html#2.1\">2.1 &ndash; Lexical Conventions</A>\n<LI><A HREF=\"manual.html#2.2\">2.2 &ndash; Values and Types</A>\n<UL>\n<LI><A HREF=\"manual.html#2.2.1\">2.2.1 &ndash; Coercion</A>\n</UL>\n<LI><A HREF=\"manual.html#2.3\">2.3 &ndash; Variables</A>\n<LI><A HREF=\"manual.html#2.4\">2.4 &ndash; Statements</A>\n<UL>\n<LI><A HREF=\"manual.html#2.4.1\">2.4.1 &ndash; Chunks</A>\n<LI><A HREF=\"manual.html#2.4.2\">2.4.2 &ndash; Blocks</A>\n<LI><A HREF=\"manual.html#2.4.3\">2.4.3 &ndash; Assignment</A>\n<LI><A HREF=\"manual.html#2.4.4\">2.4.4 &ndash; Control Structures</A>\n<LI><A HREF=\"manual.html#2.4.5\">2.4.5 &ndash; For Statement</A>\n<LI><A HREF=\"manual.html#2.4.6\">2.4.6 &ndash; Function Calls as Statements</A>\n<LI><A HREF=\"manual.html#2.4.7\">2.4.7 &ndash; Local Declarations</A>\n</UL>\n<LI><A HREF=\"manual.html#2.5\">2.5 &ndash; Expressions</A>\n<UL>\n<LI><A HREF=\"manual.html#2.5.1\">2.5.1 &ndash; Arithmetic Operators</A>\n<LI><A HREF=\"manual.html#2.5.2\">2.5.2 &ndash; Relational Operators</A>\n<LI><A HREF=\"manual.html#2.5.3\">2.5.3 &ndash; Logical Operators</A>\n<LI><A HREF=\"manual.html#2.5.4\">2.5.4 &ndash; Concatenation</A>\n<LI><A HREF=\"manual.html#2.5.5\">2.5.5 &ndash; The Length Operator</A>\n<LI><A HREF=\"manual.html#2.5.6\">2.5.6 &ndash; Precedence</A>\n<LI><A HREF=\"manual.html#2.5.7\">2.5.7 &ndash; Table Constructors</A>\n<LI><A HREF=\"manual.html#2.5.8\">2.5.8 &ndash; Function Calls</A>\n<LI><A HREF=\"manual.html#2.5.9\">2.5.9 &ndash; Function Definitions</A>\n</UL>\n<LI><A HREF=\"manual.html#2.6\">2.6 &ndash; Visibility Rules</A>\n<LI><A HREF=\"manual.html#2.7\">2.7 &ndash; Error Handling</A>\n<LI><A HREF=\"manual.html#2.8\">2.8 &ndash; Metatables</A>\n<LI><A HREF=\"manual.html#2.9\">2.9 &ndash; Environments</A>\n<LI><A HREF=\"manual.html#2.10\">2.10 &ndash; Garbage Collection</A>\n<UL>\n<LI><A HREF=\"manual.html#2.10.1\">2.10.1 &ndash; Garbage-Collection Metamethods</A>\n<LI><A HREF=\"manual.html#2.10.2\">2.10.2 &ndash; Weak Tables</A>\n</UL>\n<LI><A HREF=\"manual.html#2.11\">2.11 &ndash; Coroutines</A>\n</UL>\n<P>\n<LI><A HREF=\"manual.html#5\">5 &ndash; Standard Libraries</A>\n<UL>\n<LI><A HREF=\"manual.html#5.1\">5.1 &ndash; Basic Functions</A>\n<LI><A HREF=\"manual.html#5.2\">5.2 &ndash; Coroutine Manipulation</A>\n<LI><A HREF=\"manual.html#5.3\">5.3 &ndash; Modules</A>\n<LI><A HREF=\"manual.html#5.4\">5.4 &ndash; String Manipulation</A>\n<UL>\n<LI><A HREF=\"manual.html#5.4.1\">5.4.1 &ndash; Patterns</A>\n</UL>\n<LI><A HREF=\"manual.html#5.5\">5.5 &ndash; Table Manipulation</A>\n<LI><A HREF=\"manual.html#5.6\">5.6 &ndash; Mathematical Functions</A>\n<LI><span class='teliva'><A HREF=\"manual.html#5.7\">5.7 &ndash; File Input and Output Facilities</A></span>\n<LI><A HREF=\"manual.html#5.8\">5.8 &ndash; Operating System Facilities</A>\n<LI><span class='teliva'><A HREF=\"manual.html#5.10\">5.10 &ndash; Curses Window Facilities</A></span>\n<LI><span class='teliva'><A HREF=\"manual.html#5.11\">5.11 &ndash; Networking Facilities</A></span>\n<LI><span class='teliva'><A HREF=\"manual.html#5.12\">5.12 &ndash; JSON Facilities</A></span>\n<LI><span class='teliva'><A HREF=\"manual.html#5.13\">5.13 &ndash; Tasks and Channels</A></span>\n</UL>\n<P>\n<LI><A HREF=\"manual.html#8\">8 &ndash; The Complete Syntax of Lua</A>\n</UL>\n\n<H2><A NAME=\"index\">Index</A></H2>\n<TABLE WIDTH=\"100%\">\n<TR VALIGN=\"top\">\n<TD>\n<H3><A NAME=\"functions\">Lua functions</A></H3>\n<A HREF=\"manual.html#pdf-_G\">_G</A><BR>\n<A HREF=\"manual.html#pdf-_VERSION\">_VERSION</A><BR>\n<P>\n\n<A HREF=\"manual.html#pdf-assert\">assert</A><BR>\n<A HREF=\"manual.html#pdf-collectgarbage\">collectgarbage</A><BR>\n<A HREF=\"manual.html#pdf-error\">error</A><BR>\n<A HREF=\"manual.html#pdf-getfenv\">getfenv</A><BR>\n<A HREF=\"manual.html#pdf-getmetatable\">getmetatable</A><BR>\n<A HREF=\"manual.html#pdf-ipairs\">ipairs</A><BR>\n<A HREF=\"manual.html#pdf-next\">next</A><BR>\n<A HREF=\"manual.html#pdf-pairs\">pairs</A><BR>\n<A HREF=\"manual.html#pdf-print\">print</A><BR>\n<A HREF=\"manual.html#pdf-rawequal\">rawequal</A><BR>\n<A HREF=\"manual.html#pdf-rawget\">rawget</A><BR>\n<A HREF=\"manual.html#pdf-rawset\">rawset</A><BR>\n<A HREF=\"manual.html#pdf-select\">select</A><BR>\n<A HREF=\"manual.html#pdf-setfenv\">setfenv</A><BR>\n<A HREF=\"manual.html#pdf-setmetatable\">setmetatable</A><BR>\n<A HREF=\"manual.html#pdf-tonumber\">tonumber</A><BR>\n<A HREF=\"manual.html#pdf-tostring\">tostring</A><BR>\n<A HREF=\"manual.html#pdf-type\">type</A><BR>\n<A HREF=\"manual.html#pdf-unpack\">unpack</A><BR>\n<P>\n\n<A HREF=\"manual.html#pdf-coroutine.create\">coroutine.create</A><BR>\n<A HREF=\"manual.html#pdf-coroutine.resume\">coroutine.resume</A><BR>\n<A HREF=\"manual.html#pdf-coroutine.running\">coroutine.running</A><BR>\n<A HREF=\"manual.html#pdf-coroutine.status\">coroutine.status</A><BR>\n<A HREF=\"manual.html#pdf-coroutine.wrap\">coroutine.wrap</A><BR>\n<A HREF=\"manual.html#pdf-coroutine.yield\">coroutine.yield</A><BR>\n<P>\n\n<A HREF=\"manual.html#pdf-debug.debug\">debug.debug</A><BR>\n<A HREF=\"manual.html#pdf-debug.getfenv\">debug.getfenv</A><BR>\n<A HREF=\"manual.html#pdf-debug.gethook\">debug.gethook</A><BR>\n<A HREF=\"manual.html#pdf-debug.getinfo\">debug.getinfo</A><BR>\n<A HREF=\"manual.html#pdf-debug.getlocal\">debug.getlocal</A><BR>\n<A HREF=\"manual.html#pdf-debug.getmetatable\">debug.getmetatable</A><BR>\n<A HREF=\"manual.html#pdf-debug.getregistry\">debug.getregistry</A><BR>\n<A HREF=\"manual.html#pdf-debug.getupvalue\">debug.getupvalue</A><BR>\n<A HREF=\"manual.html#pdf-debug.setfenv\">debug.setfenv</A><BR>\n<A HREF=\"manual.html#pdf-debug.sethook\">debug.sethook</A><BR>\n<A HREF=\"manual.html#pdf-debug.setlocal\">debug.setlocal</A><BR>\n<A HREF=\"manual.html#pdf-debug.setmetatable\">debug.setmetatable</A><BR>\n<A HREF=\"manual.html#pdf-debug.setupvalue\">debug.setupvalue</A><BR>\n<A HREF=\"manual.html#pdf-debug.traceback\">debug.traceback</A><BR>\n\n</TD>\n<TD>\n<H3>&nbsp;</H3>\n<A HREF=\"manual.html#pdf-file:close\">file:close</A><BR>\n<A HREF=\"manual.html#pdf-file:flush\">file:flush</A><BR>\n<A HREF=\"manual.html#pdf-file:lines\">file:lines</A><BR>\n<A HREF=\"manual.html#pdf-file:read\">file:read</A><BR>\n<A HREF=\"manual.html#pdf-file:seek\">file:seek</A><BR>\n<A HREF=\"manual.html#pdf-file:setvbuf\">file:setvbuf</A><BR>\n<A HREF=\"manual.html#pdf-file:write\">file:write</A><BR>\n<P>\n\n<A HREF=\"manual.html#pdf-io.close\">io.close</A><BR>\n<A HREF=\"manual.html#pdf-io.open\">io.open</A><BR>\n<A HREF=\"manual.html#pdf-io.tmpfile\">io.tmpfile</A><BR>\n<A HREF=\"manual.html#pdf-io.type\">io.type</A><BR>\n<P>\n\n<A HREF=\"manual.html#pdf-math.abs\">math.abs</A><BR>\n<A HREF=\"manual.html#pdf-math.acos\">math.acos</A><BR>\n<A HREF=\"manual.html#pdf-math.asin\">math.asin</A><BR>\n<A HREF=\"manual.html#pdf-math.atan\">math.atan</A><BR>\n<A HREF=\"manual.html#pdf-math.atan2\">math.atan2</A><BR>\n<A HREF=\"manual.html#pdf-math.ceil\">math.ceil</A><BR>\n<A HREF=\"manual.html#pdf-math.cos\">math.cos</A><BR>\n<A HREF=\"manual.html#pdf-math.cosh\">math.cosh</A><BR>\n<A HREF=\"manual.html#pdf-math.deg\">math.deg</A><BR>\n<A HREF=\"manual.html#pdf-math.exp\">math.exp</A><BR>\n<A HREF=\"manual.html#pdf-math.floor\">math.floor</A><BR>\n<A HREF=\"manual.html#pdf-math.fmod\">math.fmod</A><BR>\n<A HREF=\"manual.html#pdf-math.frexp\">math.frexp</A><BR>\n<A HREF=\"manual.html#pdf-math.huge\">math.huge</A><BR>\n<A HREF=\"manual.html#pdf-math.ldexp\">math.ldexp</A><BR>\n<A HREF=\"manual.html#pdf-math.log\">math.log</A><BR>\n<A HREF=\"manual.html#pdf-math.log10\">math.log10</A><BR>\n<A HREF=\"manual.html#pdf-math.max\">math.max</A><BR>\n<A HREF=\"manual.html#pdf-math.min\">math.min</A><BR>\n<A HREF=\"manual.html#pdf-math.modf\">math.modf</A><BR>\n<A HREF=\"manual.html#pdf-math.pi\">math.pi</A><BR>\n<A HREF=\"manual.html#pdf-math.pow\">math.pow</A><BR>\n<A HREF=\"manual.html#pdf-math.rad\">math.rad</A><BR>\n<A HREF=\"manual.html#pdf-math.random\">math.random</A><BR>\n<A HREF=\"manual.html#pdf-math.randomseed\">math.randomseed</A><BR>\n<A HREF=\"manual.html#pdf-math.sin\">math.sin</A><BR>\n<A HREF=\"manual.html#pdf-math.sinh\">math.sinh</A><BR>\n<A HREF=\"manual.html#pdf-math.sqrt\">math.sqrt</A><BR>\n<A HREF=\"manual.html#pdf-math.tan\">math.tan</A><BR>\n<A HREF=\"manual.html#pdf-math.tanh\">math.tanh</A><BR>\n<P>\n\n<A HREF=\"manual.html#pdf-os.clock\">os.clock</A><BR>\n<A HREF=\"manual.html#pdf-os.date\">os.date</A><BR>\n<A HREF=\"manual.html#pdf-os.difftime\">os.difftime</A><BR>\n<A HREF=\"manual.html#pdf-os.exit\">os.exit</A><BR>\n<A HREF=\"manual.html#pdf-os.remove\">os.remove</A><BR>\n<A HREF=\"manual.html#pdf-os.rename\">os.rename</A><BR>\n<A HREF=\"manual.html#pdf-os.setlocale\">os.setlocale</A><BR>\n<A HREF=\"manual.html#pdf-os.time\">os.time</A><BR>\n<A HREF=\"manual.html#pdf-os.tmpname\">os.tmpname</A><BR>\n<P>\n\n<A HREF=\"manual.html#pdf-string.byte\">string.byte</A><BR>\n<A HREF=\"manual.html#pdf-string.char\">string.char</A><BR>\n<A HREF=\"manual.html#pdf-string.dump\">string.dump</A><BR>\n<A HREF=\"manual.html#pdf-string.find\">string.find</A><BR>\n<A HREF=\"manual.html#pdf-string.format\">string.format</A><BR>\n<A HREF=\"manual.html#pdf-string.gmatch\">string.gmatch</A><BR>\n<A HREF=\"manual.html#pdf-string.gsub\">string.gsub</A><BR>\n<A HREF=\"manual.html#pdf-string.len\">string.len</A><BR>\n<A HREF=\"manual.html#pdf-string.lower\">string.lower</A><BR>\n<A HREF=\"manual.html#pdf-string.match\">string.match</A><BR>\n<A HREF=\"manual.html#pdf-string.rep\">string.rep</A><BR>\n<A HREF=\"manual.html#pdf-string.reverse\">string.reverse</A><BR>\n<A HREF=\"manual.html#pdf-string.sub\">string.sub</A><BR>\n<A HREF=\"manual.html#pdf-string.upper\">string.upper</A><BR>\n<P>\n\n<A HREF=\"manual.html#pdf-table.concat\">table.concat</A><BR>\n<A HREF=\"manual.html#pdf-table.insert\">table.insert</A><BR>\n<A HREF=\"manual.html#pdf-table.maxn\">table.maxn</A><BR>\n<A HREF=\"manual.html#pdf-table.remove\">table.remove</A><BR>\n<A HREF=\"manual.html#pdf-table.sort\">table.sort</A><BR>\n\n</TD>\n</TR>\n</TABLE>\n<P>\n\n</BODY>\n</HTML>\n"
  },
  {
    "path": "doc/lua.css",
    "content": "body {\n\tcolor: #000000 ;\n\tbackground-color: #FFFFFF ;\n\tfont-family: Helvetica, Arial, sans-serif ;\n\ttext-align: justify ;\n\tmargin: auto ;\n\twidth: 50em ;\n}\n\nh1, h2, h3, h4 {\n\tfont-family: Verdana, Geneva, sans-serif ;\n\tfont-weight: normal ;\n\tfont-style: italic ;\n}\n\nh2 {\n\tpadding-top: 0.4em ;\n\tpadding-bottom: 0.4em ;\n\tpadding-left: 30px ;\n\tpadding-right: 30px ;\n\tmargin-left: -30px ;\n\tbackground-color: #E0E0FF ;\n}\n\nh3 {\n\tpadding-left: 0.5em ;\n\tborder-left: solid #E0E0FF 1em ;\n}\n\ntable h3 {\n\tpadding-left: 0px ;\n\tborder-left: none ;\n}\n\na:link {\n\tcolor: #000080 ;\n\tbackground-color: inherit ;\n\ttext-decoration: none ;\n}\n\na:visited {\n\tbackground-color: inherit ;\n\ttext-decoration: none ;\n}\n\na:link:hover, a:visited:hover {\n\tcolor: #000080 ;\n\tbackground-color: #E0E0FF ;\n}\n\na:link:active, a:visited:active {\n\tcolor: #FF0000 ;\n}\n\nhr {\n\tborder: 0 ;\n\theight: 1px ;\n\tcolor: #a0a0a0 ;\n\tbackground-color: #a0a0a0 ;\n}\n\n:target {\n\tbackground-color: #F8F8F8 ;\n\tpadding: 8px ;\n\tborder: solid #a0a0a0 2px ;\n}\n\n.footer {\n\tcolor: gray ;\n\tfont-size: small ;\n}\n\ninput[type=text] {\n\tborder: solid #a0a0a0 2px ;\n\tborder-radius: 2em ;\n\t-moz-border-radius: 2em ;\n\tbackground-image: url('images/search.png') ;\n\tbackground-repeat: no-repeat;\n\tbackground-position: 4px center ;\n\tpadding-left: 20px ;\n\theight: 2em ;\n}\n\n.teliva {\n\tbackground-color: #f1a7fe;\n\tpadding-left: 2px;\n\tpadding-right: 2px;\n\tpadding-top: 5px;\n\tpadding-bottom: 2px;\n}\n"
  },
  {
    "path": "doc/manual.css",
    "content": "h3 code {\n\tfont-family: inherit ;\n\tfont-size: inherit ;\n}\n\npre, code {\n\tfont-size: 12pt ;\n}\n\nspan.apii {\n\tfloat: right ;\n\tfont-family: inherit ;\n\tfont-style: normal ;\n\tfont-size: small ;\n\tcolor: gray ;\n}\n\np+h1, ul+h1 {\n\tpadding-top: 0.4em ;\n\tpadding-bottom: 0.4em ;\n\tpadding-left: 30px ;\n\tmargin-left: -30px ;\n\tbackground-color: #E0E0FF ;\n}\n\n.teliva {\n\tbackground-color: #f1a7fe;\n\tpadding-left: 2px;\n\tpadding-right: 2px;\n\tpadding-top: 5px;\n\tpadding-bottom: 2px;\n}\n"
  },
  {
    "path": "doc/manual.html",
    "content": "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\">\n<html>\n\n<head>\n<title>Teliva Reference Manual</title>\n<link rel=\"stylesheet\" type=\"text/css\" href=\"lua.css\">\n<link rel=\"stylesheet\" type=\"text/css\" href=\"manual.css\">\n<META HTTP-EQUIV=\"content-type\" CONTENT=\"text/html; charset=iso-8859-1\">\n</head>\n\n<body>\n\n<hr>\n<h1>\nTeliva Reference Manual\n</h1>\n\n<small>\nby Kartik Agaram\n</small>\n\n<p>\nBased on <a href=\"http://www.lua.org/\">Lua 5.1</a> by Roberto Ierusalimschy,\nLuiz Henrique de Figueiredo, Waldemar Celes\n\n<p>\n<small>\nCopyright &copy; 2006&ndash;2012 Lua.org, PUC-Rio.\nFreely available under the terms of the\n<a href=\"http://www.lua.org/license.html\">Lua license</a>.\n</small>\n\n<hr>\n<p>\n\n<a href=\"contents.html#contents\">contents</A>\n&middot;\n<a href=\"contents.html#index\">index</A>\n&middot;\n<A HREF=\"http://www.lua.org/manual/\">other versions</A>\n\n<!-- ====================================================================== -->\n<p>\n\n<!-- $Id: manual.of,v 1.49.1.2 2012/01/13 20:23:26 roberto Exp $ -->\n\n\n\n\n<h1>1 - <a name=\"1\">Introduction</a></h1>\n\nTeliva is a platform based on Lua for sandboxed software packaged with an\nenvironment for making changes to it. For a more detailed introduction, see\n<a href='../README.md'>the Readme</a>.\n\n<p>\nTeliva is free software, and is provided as usual with no guarantees, as\nstated in its license.\n\n<p>\nThis manual is based on the Lua manual. Lua features absent in Teliva should\nbe absent in this manual as well. <span class='teliva'>Features added to\nTeliva above and beyond Lua will be described in text like this.</span>\n\n<p>\nFor a discussion of the decisions behind the design of Lua,\nsee the technical papers available at Lua's web site.\nFor a detailed introduction to programming in Lua,\nsee Roberto's book, <em>Programming in Lua (Second Edition)</em>.\n\n\n\n<h1>2 - <a name=\"2\">The Language</a></h1>\n\n<p>\nThis section describes the lexis, the syntax, and the semantics of Lua.\nIn other words,\nthis section describes\nwhich tokens are valid,\nhow they can be combined,\nand what their combinations mean.\n\n\n<p>\nThe language constructs will be explained using the usual extended BNF notation,\nin which\n{<em>a</em>}&nbsp;means&nbsp;0 or more <em>a</em>'s, and\n[<em>a</em>]&nbsp;means an optional <em>a</em>.\nNon-terminals are shown like non-terminal,\nkeywords are shown like <b>kword</b>,\nand other terminal symbols are shown like `<b>=</b>&acute;.\nThe complete syntax of Lua can be found in <a href=\"#8\">&sect;8</a>\nat the end of this manual.\n\n\n\n<h2>2.1 - <a name=\"2.1\">Lexical Conventions</a></h2>\n\n<p>\n<em>Names</em>\n(also called <em>identifiers</em>)\nin Lua can be any string of letters,\ndigits, and underscores,\nnot beginning with a digit.\nThis coincides with the definition of names in most languages.\n(The definition of letter depends on the current locale:\nany character considered alphabetic by the current locale\ncan be used in an identifier.)\nIdentifiers are used to name variables and table fields.\n\n\n<p>\nThe following <em>keywords</em> are reserved\nand cannot be used as names:\n\n\n<pre>\n     and       break     do        else      elseif\n     end       false     for       function  if\n     in        local     nil       not       or\n     repeat    return    then      true      until     while\n</pre>\n\n<p>\nLua is a case-sensitive language:\n<code>and</code> is a reserved word, but <code>And</code> and <code>AND</code>\nare two different, valid names.\nAs a convention, names starting with an underscore followed by\nuppercase letters (such as <a href=\"#pdf-_VERSION\"><code>_VERSION</code></a>)\nare reserved for internal global variables used by Lua.\n\n\n<p>\nThe following strings denote other tokens:\n\n<pre>\n     +     -     *     /     %     ^     #\n     ==    ~=    &lt;=    &gt;=    &lt;     &gt;     =\n     (     )     {     }     [     ]\n     ;     :     ,     .     ..    ...\n</pre>\n\n<p>\n<em>Literal strings</em>\ncan be delimited by matching single or double quotes,\nand can contain the following C-like escape sequences:\n'<code>\\a</code>' (bell),\n'<code>\\b</code>' (backspace),\n'<code>\\f</code>' (form feed),\n'<code>\\n</code>' (newline),\n'<code>\\r</code>' (carriage return),\n'<code>\\t</code>' (horizontal tab),\n'<code>\\v</code>' (vertical tab),\n'<code>\\\\</code>' (backslash),\n'<code>\\\"</code>' (quotation mark [double quote]),\nand '<code>\\'</code>' (apostrophe [single quote]).\nMoreover, a backslash followed by a real newline\nresults in a newline in the string.\nA character in a string can also be specified by its numerical value\nusing the escape sequence <code>\\<em>ddd</em></code>,\nwhere <em>ddd</em> is a sequence of up to three decimal digits.\n(Note that if a numerical escape is to be followed by a digit,\nit must be expressed using exactly three digits.)\nStrings in Lua can contain any 8-bit value, including embedded zeros,\nwhich can be specified as '<code>\\0</code>'.\n\n\n<p>\nLiteral strings can also be defined using a long format\nenclosed by <em>long brackets</em>.\nWe define an <em>opening long bracket of level <em>n</em></em> as an opening\nsquare bracket followed by <em>n</em> equal signs followed by another\nopening square bracket.\nSo, an opening long bracket of level&nbsp;0 is written as <code>[[</code>,\nan opening long bracket of level&nbsp;1 is written as <code>[=[</code>,\nand so on.\nA <em>closing long bracket</em> is defined similarly;\nfor instance, a closing long bracket of level&nbsp;4 is written as <code>]====]</code>.\nA long string starts with an opening long bracket of any level and\nends at the first closing long bracket of the same level.\nLiterals in this bracketed form can run for several lines,\ndo not interpret any escape sequences,\nand ignore long brackets of any other level.\nThey can contain anything except a closing bracket of the proper level.\n\n\n<p>\nFor convenience,\nwhen the opening long bracket is immediately followed by a newline,\nthe newline is not included in the string.\nAs an example, in a system using ASCII\n(in which '<code>a</code>' is coded as&nbsp;97,\nnewline is coded as&nbsp;10, and '<code>1</code>' is coded as&nbsp;49),\nthe five literal strings below denote the same string:\n\n<pre>\n     a = 'alo\\n123\"'\n     a = \"alo\\n123\\\"\"\n     a = '\\97lo\\10\\04923\"'\n     a = [[alo\n     123\"]]\n     a = [==[\n     alo\n     123\"]==]\n</pre>\n\n<p>\nA <em>numerical constant</em> can be written with an optional decimal part\nand an optional decimal exponent.\nLua also accepts integer hexadecimal constants,\nby prefixing them with <code>0x</code>.\nExamples of valid numerical constants are\n\n<pre>\n     3   3.0   3.1416   314.16e-2   0.31416E1   0xff   0x56\n</pre>\n\n<p>\nA <em>comment</em> starts with a double hyphen (<code>--</code>)\nanywhere outside a string.\nIf the text immediately after <code>--</code> is not an opening long bracket,\nthe comment is a <em>short comment</em>,\nwhich runs until the end of the line.\nOtherwise, it is a <em>long comment</em>,\nwhich runs until the corresponding closing long bracket.\nLong comments are frequently used to disable code temporarily.\n\n\n\n\n\n<h2>2.2 - <a name=\"2.2\">Values and Types</a></h2>\n\n<p>\nLua is a <em>dynamically typed language</em>.\nThis means that\nvariables do not have types; only values do.\nThere are no type definitions in the language.\nAll values carry their own type.\n\n\n<p>\nAll values in Lua are <em>first-class values</em>.\nThis means that all values can be stored in variables,\npassed as arguments to other functions, and returned as results.\n\n\n<p>\nThere are eight basic types in Lua:\n<em>nil</em>, <em>boolean</em>, <em>number</em>,\n<em>string</em>, <em>function</em>, <em>userdata</em>,\n<em>thread</em>, and <em>table</em>.\n<em>Nil</em> is the type of the value <b>nil</b>,\nwhose main property is to be different from any other value;\nit usually represents the absence of a useful value.\n<em>Boolean</em> is the type of the values <b>false</b> and <b>true</b>.\nBoth <b>nil</b> and <b>false</b> make a condition false;\nany other value makes it true.\n<em>Number</em> represents real (double-precision floating-point) numbers.\n(It is easy to build Lua interpreters that use other\ninternal representations for numbers,\nsuch as single-precision float or long integers;\nsee file <code>luaconf.h</code>.)\n<em>String</em> represents arrays of characters.\n\nLua is 8-bit clean:\nstrings can contain any 8-bit character,\nincluding embedded zeros ('<code>\\0</code>') (see <a href=\"#2.1\">&sect;2.1</a>).\n\n\n<p>\n<div class='teliva'>The type <em>userdata</em> is opaque to Teliva, and\nused by low-level code to manage details of C&nbsp;data stored in Lua\nvariables. Unlike Lua, Teliva doesn't support creating or managing userdata\nsince it doesn't permit extension by C libraries. This manual doesn't allude\nto userdata further.</div>\n\n<p>\nThe type <em>thread</em> represents independent threads of execution\nand it is used to implement coroutines (see <a href=\"#2.11\">&sect;2.11</a>).\nDo not confuse Lua threads with operating-system threads.\nLua supports coroutines on all systems,\neven those that do not support threads.\n\n\n<p>\nThe type <em>table</em> implements associative arrays,\nthat is, arrays that can be indexed not only with numbers,\nbut with any value (except <b>nil</b>).\nTables can be <em>heterogeneous</em>;\nthat is, they can contain values of all types (except <b>nil</b>).\nTables are the sole data structuring mechanism in Lua;\nthey can be used to represent ordinary arrays,\nsymbol tables, sets, records, graphs, trees, etc.\nTo represent records, Lua uses the field name as an index.\nThe language supports this representation by\nproviding <code>a.name</code> as syntactic sugar for <code>a[\"name\"]</code>.\nThere are several convenient ways to create tables in Lua\n(see <a href=\"#2.5.7\">&sect;2.5.7</a>).\n\n\n<p>\nLike indices,\nthe value of a table field can be of any type (except <b>nil</b>).\nIn particular,\nbecause functions are first-class values,\ntable fields can contain functions.\nThus tables can also carry <em>methods</em> (see <a href=\"#2.5.9\">&sect;2.5.9</a>).\n\n\n<p>\nTables, functions and threads are <em>objects</em>:\nvariables do not actually <em>contain</em> these values,\nonly <em>references</em> to them.\nAssignment, parameter passing, and function returns\nalways manipulate references to such values;\nthese operations do not imply any kind of copy.\n\n\n<p>\nThe library function <a href=\"#pdf-type\"><code>type</code></a> returns a string describing the type\nof a given value.\n\n\n\n<h3>2.2.1 - <a name=\"2.2.1\">Coercion</a></h3>\n\n<p>\nLua provides automatic conversion between\nstring and number values at run time.\nAny arithmetic operation applied to a string tries to convert\nthis string to a number, following the usual conversion rules.\nConversely, whenever a number is used where a string is expected,\nthe number is converted to a string, in a reasonable format.\nFor complete control over how numbers are converted to strings,\nuse the <code>format</code> function from the string library\n(see <a href=\"#pdf-string.format\"><code>string.format</code></a>).\n\n\n\n\n\n\n\n<h2>2.3 - <a name=\"2.3\">Variables</a></h2>\n\n<p>\nVariables are places that store values.\n\nThere are three kinds of variables in Lua:\nglobal variables, local variables, and table fields.\n\n\n<p>\nA single name can denote a global variable or a local variable\n(or a function's formal parameter,\nwhich is a particular kind of local variable):\n\n<pre>\n\tvar ::= Name\n</pre><p>\nName denotes identifiers, as defined in <a href=\"#2.1\">&sect;2.1</a>.\n\n\n<p>\nAny variable is assumed to be global unless explicitly declared\nas a local (see <a href=\"#2.4.7\">&sect;2.4.7</a>).\nLocal variables are <em>lexically scoped</em>:\nlocal variables can be freely accessed by functions\ndefined inside their scope (see <a href=\"#2.6\">&sect;2.6</a>).\n\n\n<p>\nBefore the first assignment to a variable, its value is <b>nil</b>.\n\n\n<p>\nSquare brackets are used to index a table:\n\n<pre>\n\tvar ::= prefixexp `<b>[</b>&acute; exp `<b>]</b>&acute;\n</pre><p>\nThe meaning of accesses to global variables \nand table fields can be changed via metatables.\nAn access to an indexed variable <code>t[i]</code> is equivalent to\na call <code>gettable_event(t,i)</code>.\n(See <a href=\"#2.8\">&sect;2.8</a> for a complete description of the\n<code>gettable_event</code> function.\nThis function is not defined or callable in Lua.\nWe use it here only for explanatory purposes.)\n\n\n<p>\nThe syntax <code>var.Name</code> is just syntactic sugar for\n<code>var[\"Name\"]</code>:\n\n<pre>\n\tvar ::= prefixexp `<b>.</b>&acute; Name\n</pre>\n\n<p>\nAll global variables live as fields in ordinary Lua tables,\ncalled <em>environment tables</em> or simply\n<em>environments</em> (see <a href=\"#2.9\">&sect;2.9</a>).\nEach function has its own reference to an environment,\nso that all global variables in this function\nwill refer to this environment table.\nWhen a function is created,\nit inherits the environment from the function that created it.\nTo get the environment table of a Lua function,\nyou call <a href=\"#pdf-getfenv\"><code>getfenv</code></a>.\nTo replace it,\nyou call <a href=\"#pdf-setfenv\"><code>setfenv</code></a>.\n\n\n<p>\nAn access to a global variable <code>x</code>\nis equivalent to <code>_env.x</code>,\nwhich in turn is equivalent to\n\n<pre>\n     gettable_event(_env, \"x\")\n</pre><p>\nwhere <code>_env</code> is the environment of the running function.\n(See <a href=\"#2.8\">&sect;2.8</a> for a complete description of the\n<code>gettable_event</code> function.\nThis function is not defined or callable in Lua.\nSimilarly, the <code>_env</code> variable is not defined in Lua.\nWe use them here only for explanatory purposes.)\n\n\n\n\n\n<h2>2.4 - <a name=\"2.4\">Statements</a></h2>\n\n<p>\nLua supports an almost conventional set of statements,\nsimilar to those in Pascal or C.\nThis set includes\nassignments, control structures, function calls,\nand variable declarations.\n\n\n\n<h3>2.4.1 - <a name=\"2.4.1\">Chunks</a></h3>\n\n<p>\nThe unit of execution of Lua is called a <em>chunk</em>.\nA chunk is simply a sequence of statements,\nwhich are executed sequentially.\nEach statement can be optionally followed by a semicolon:\n\n<pre>\n\tchunk ::= {stat [`<b>;</b>&acute;]}\n</pre><p>\nThere are no empty statements and thus '<code>;;</code>' is not legal.\n\n\n<p>\nLua handles a chunk as the body of an anonymous function \nwith a variable number of arguments\n(see <a href=\"#2.5.9\">&sect;2.5.9</a>).\nAs such, chunks can define local variables,\nreceive arguments, and return values.\n\n\n<p>\nA chunk can be stored in a file or in a string inside the host program.\nTo execute a chunk,\nLua first pre-compiles the chunk into instructions for a virtual machine,\nand then it executes the compiled code\nwith an interpreter for the virtual machine.\n\n\n<p>\nChunks can also be pre-compiled into binary form;\nsee program <code>luac</code> for details.\nPrograms in source and compiled forms are interchangeable;\nLua automatically detects the file type and acts accordingly.\n\n\n\n\n\n\n<h3>2.4.2 - <a name=\"2.4.2\">Blocks</a></h3><p>\nA block is a list of statements;\nsyntactically, a block is the same as a chunk:\n\n<pre>\n\tblock ::= chunk\n</pre>\n\n<p>\nA block can be explicitly delimited to produce a single statement:\n\n<pre>\n\tstat ::= <b>do</b> block <b>end</b>\n</pre><p>\nExplicit blocks are useful\nto control the scope of variable declarations.\nExplicit blocks are also sometimes used to\nadd a <b>return</b> or <b>break</b> statement in the middle\nof another block (see <a href=\"#2.4.4\">&sect;2.4.4</a>).\n\n\n\n\n\n<h3>2.4.3 - <a name=\"2.4.3\">Assignment</a></h3>\n\n<p>\nLua allows multiple assignments.\nTherefore, the syntax for assignment\ndefines a list of variables on the left side\nand a list of expressions on the right side.\nThe elements in both lists are separated by commas:\n\n<pre>\n\tstat ::= varlist `<b>=</b>&acute; explist\n\tvarlist ::= var {`<b>,</b>&acute; var}\n\texplist ::= exp {`<b>,</b>&acute; exp}\n</pre><p>\nExpressions are discussed in <a href=\"#2.5\">&sect;2.5</a>.\n\n\n<p>\nBefore the assignment,\nthe list of values is <em>adjusted</em> to the length of\nthe list of variables.\nIf there are more values than needed,\nthe excess values are thrown away.\nIf there are fewer values than needed,\nthe list is extended with as many  <b>nil</b>'s as needed.\nIf the list of expressions ends with a function call,\nthen all values returned by that call enter the list of values,\nbefore the adjustment\n(except when the call is enclosed in parentheses; see <a href=\"#2.5\">&sect;2.5</a>).\n\n\n<p>\nThe assignment statement first evaluates all its expressions\nand only then are the assignments performed.\nThus the code\n\n<pre>\n     i = 3\n     i, a[i] = i+1, 20\n</pre><p>\nsets <code>a[3]</code> to 20, without affecting <code>a[4]</code>\nbecause the <code>i</code> in <code>a[i]</code> is evaluated (to 3)\nbefore it is assigned&nbsp;4.\nSimilarly, the line\n\n<pre>\n     x, y = y, x\n</pre><p>\nexchanges the values of <code>x</code> and <code>y</code>,\nand\n\n<pre>\n     x, y, z = y, z, x\n</pre><p>\ncyclically permutes the values of <code>x</code>, <code>y</code>, and <code>z</code>.\n\n\n<p>\nThe meaning of assignments to global variables\nand table fields can be changed via metatables.\nAn assignment to an indexed variable <code>t[i] = val</code> is equivalent to\n<code>settable_event(t,i,val)</code>.\n(See <a href=\"#2.8\">&sect;2.8</a> for a complete description of the\n<code>settable_event</code> function.\nThis function is not defined or callable in Lua.\nWe use it here only for explanatory purposes.)\n\n\n<p>\nAn assignment to a global variable <code>x = val</code>\nis equivalent to the assignment\n<code>_env.x = val</code>,\nwhich in turn is equivalent to\n\n<pre>\n     settable_event(_env, \"x\", val)\n</pre><p>\nwhere <code>_env</code> is the environment of the running function.\n(The <code>_env</code> variable is not defined in Lua.\nWe use it here only for explanatory purposes.)\n\n\n\n\n\n<h3>2.4.4 - <a name=\"2.4.4\">Control Structures</a></h3><p>\nThe control structures\n<b>if</b>, <b>while</b>, and <b>repeat</b> have the usual meaning and\nfamiliar syntax:\n\n\n\n\n<pre>\n\tstat ::= <b>while</b> exp <b>do</b> block <b>end</b>\n\tstat ::= <b>repeat</b> block <b>until</b> exp\n\tstat ::= <b>if</b> exp <b>then</b> block {<b>elseif</b> exp <b>then</b> block} [<b>else</b> block] <b>end</b>\n</pre><p>\nLua also has a <b>for</b> statement, in two flavors (see <a href=\"#2.4.5\">&sect;2.4.5</a>).\n\n\n<p>\nThe condition expression of a\ncontrol structure can return any value.\nBoth <b>false</b> and <b>nil</b> are considered false.\nAll values different from <b>nil</b> and <b>false</b> are considered true\n(in particular, the number 0 and the empty string are also true).\n\n\n<p>\nIn the <b>repeat</b>&ndash;<b>until</b> loop,\nthe inner block does not end at the <b>until</b> keyword,\nbut only after the condition.\nSo, the condition can refer to local variables\ndeclared inside the loop block.\n\n\n<p>\nThe <b>return</b> statement is used to return values\nfrom a function or a chunk (which is just a function).\n\nFunctions and chunks can return more than one value,\nand so the syntax for the <b>return</b> statement is\n\n<pre>\n\tstat ::= <b>return</b> [explist]\n</pre>\n\n<p>\nThe <b>break</b> statement is used to terminate the execution of a\n<b>while</b>, <b>repeat</b>, or <b>for</b> loop,\nskipping to the next statement after the loop:\n\n\n<pre>\n\tstat ::= <b>break</b>\n</pre><p>\nA <b>break</b> ends the innermost enclosing loop.\n\n\n<p>\nThe <b>return</b> and <b>break</b>\nstatements can only be written as the <em>last</em> statement of a block.\nIf it is really necessary to <b>return</b> or <b>break</b> in the\nmiddle of a block,\nthen an explicit inner block can be used,\nas in the idioms\n<code>do return end</code> and <code>do break end</code>,\nbecause now <b>return</b> and <b>break</b> are the last statements in\ntheir (inner) blocks.\n\n\n\n\n\n<h3>2.4.5 - <a name=\"2.4.5\">For Statement</a></h3>\n\n<p>\n\nThe <b>for</b> statement has two forms:\none numeric and one generic.\n\n\n<p>\nThe numeric <b>for</b> loop repeats a block of code while a\ncontrol variable runs through an arithmetic progression.\nIt has the following syntax:\n\n<pre>\n\tstat ::= <b>for</b> Name `<b>=</b>&acute; exp `<b>,</b>&acute; exp [`<b>,</b>&acute; exp] <b>do</b> block <b>end</b>\n</pre><p>\nThe <em>block</em> is repeated for <em>name</em> starting at the value of\nthe first <em>exp</em>, until it passes the second <em>exp</em> by steps of the\nthird <em>exp</em>.\nMore precisely, a <b>for</b> statement like\n\n<pre>\n     for v = <em>e1</em>, <em>e2</em>, <em>e3</em> do <em>block</em> end\n</pre><p>\nis equivalent to the code:\n\n<pre>\n     do\n       local <em>var</em>, <em>limit</em>, <em>step</em> = tonumber(<em>e1</em>), tonumber(<em>e2</em>), tonumber(<em>e3</em>)\n       if not (<em>var</em> and <em>limit</em> and <em>step</em>) then error() end\n       while (<em>step</em> &gt; 0 and <em>var</em> &lt;= <em>limit</em>) or (<em>step</em> &lt;= 0 and <em>var</em> &gt;= <em>limit</em>) do\n         local v = <em>var</em>\n         <em>block</em>\n         <em>var</em> = <em>var</em> + <em>step</em>\n       end\n     end\n</pre><p>\nNote the following:\n\n<ul>\n\n<li>\nAll three control expressions are evaluated only once,\nbefore the loop starts.\nThey must all result in numbers.\n</li>\n\n<li>\n<code><em>var</em></code>, <code><em>limit</em></code>, and <code><em>step</em></code> are invisible variables.\nThe names shown here are for explanatory purposes only.\n</li>\n\n<li>\nIf the third expression (the step) is absent,\nthen a step of&nbsp;1 is used.\n</li>\n\n<li>\nYou can use <b>break</b> to exit a <b>for</b> loop.\n</li>\n\n<li>\nThe loop variable <code>v</code> is local to the loop;\nyou cannot use its value after the <b>for</b> ends or is broken.\nIf you need this value,\nassign it to another variable before breaking or exiting the loop.\n</li>\n\n</ul>\n\n<p>\nThe generic <b>for</b> statement works over functions,\ncalled <em>iterators</em>.\nOn each iteration, the iterator function is called to produce a new value,\nstopping when this new value is <b>nil</b>.\nThe generic <b>for</b> loop has the following syntax:\n\n<pre>\n\tstat ::= <b>for</b> namelist <b>in</b> explist <b>do</b> block <b>end</b>\n\tnamelist ::= Name {`<b>,</b>&acute; Name}\n</pre><p>\nA <b>for</b> statement like\n\n<pre>\n     for <em>var_1</em>, &middot;&middot;&middot;, <em>var_n</em> in <em>explist</em> do <em>block</em> end\n</pre><p>\nis equivalent to the code:\n\n<pre>\n     do\n       local <em>f</em>, <em>s</em>, <em>var</em> = <em>explist</em>\n       while true do\n         local <em>var_1</em>, &middot;&middot;&middot;, <em>var_n</em> = <em>f</em>(<em>s</em>, <em>var</em>)\n         <em>var</em> = <em>var_1</em>\n         if <em>var</em> == nil then break end\n         <em>block</em>\n       end\n     end\n</pre><p>\nNote the following:\n\n<ul>\n\n<li>\n<code><em>explist</em></code> is evaluated only once.\nIts results are an <em>iterator</em> function,\na <em>state</em>,\nand an initial value for the first <em>iterator variable</em>.\n</li>\n\n<li>\n<code><em>f</em></code>, <code><em>s</em></code>, and <code><em>var</em></code> are invisible variables.\nThe names are here for explanatory purposes only.\n</li>\n\n<li>\nYou can use <b>break</b> to exit a <b>for</b> loop.\n</li>\n\n<li>\nThe loop variables <code><em>var_i</em></code> are local to the loop;\nyou cannot use their values after the <b>for</b> ends.\nIf you need these values,\nthen assign them to other variables before breaking or exiting the loop.\n</li>\n\n</ul>\n\n\n\n\n<h3>2.4.6 - <a name=\"2.4.6\">Function Calls as Statements</a></h3><p>\nTo allow possible side-effects,\nfunction calls can be executed as statements:\n\n<pre>\n\tstat ::= functioncall\n</pre><p>\nIn this case, all returned values are thrown away.\nFunction calls are explained in <a href=\"#2.5.8\">&sect;2.5.8</a>.\n\n\n\n\n\n<h3>2.4.7 - <a name=\"2.4.7\">Local Declarations</a></h3><p>\nLocal variables can be declared anywhere inside a block.\nThe declaration can include an initial assignment:\n\n<pre>\n\tstat ::= <b>local</b> namelist [`<b>=</b>&acute; explist]\n</pre><p>\nIf present, an initial assignment has the same semantics\nof a multiple assignment (see <a href=\"#2.4.3\">&sect;2.4.3</a>).\nOtherwise, all variables are initialized with <b>nil</b>.\n\n\n<p>\nA chunk is also a block (see <a href=\"#2.4.1\">&sect;2.4.1</a>),\nand so local variables can be declared in a chunk outside any explicit block.\nThe scope of such local variables extends until the end of the chunk.\n\n\n<p>\nThe visibility rules for local variables are explained in <a href=\"#2.6\">&sect;2.6</a>.\n\n\n\n\n\n\n\n<h2>2.5 - <a name=\"2.5\">Expressions</a></h2>\n\n<p>\nThe basic expressions in Lua are the following:\n\n<pre>\n\texp ::= prefixexp\n\texp ::= <b>nil</b> | <b>false</b> | <b>true</b>\n\texp ::= Number\n\texp ::= String\n\texp ::= function\n\texp ::= tableconstructor\n\texp ::= `<b>...</b>&acute;\n\texp ::= exp binop exp\n\texp ::= unop exp\n\tprefixexp ::= var | functioncall | `<b>(</b>&acute; exp `<b>)</b>&acute;\n</pre>\n\n<p>\nNumbers and literal strings are explained in <a href=\"#2.1\">&sect;2.1</a>;\nvariables are explained in <a href=\"#2.3\">&sect;2.3</a>;\nfunction definitions are explained in <a href=\"#2.5.9\">&sect;2.5.9</a>;\nfunction calls are explained in <a href=\"#2.5.8\">&sect;2.5.8</a>;\ntable constructors are explained in <a href=\"#2.5.7\">&sect;2.5.7</a>.\nVararg expressions,\ndenoted by three dots ('<code>...</code>'), can only be used when\ndirectly inside a vararg function;\nthey are explained in <a href=\"#2.5.9\">&sect;2.5.9</a>.\n\n\n<p>\nBinary operators comprise arithmetic operators (see <a href=\"#2.5.1\">&sect;2.5.1</a>),\nrelational operators (see <a href=\"#2.5.2\">&sect;2.5.2</a>), logical operators (see <a href=\"#2.5.3\">&sect;2.5.3</a>),\nand the concatenation operator (see <a href=\"#2.5.4\">&sect;2.5.4</a>).\nUnary operators comprise the unary minus (see <a href=\"#2.5.1\">&sect;2.5.1</a>),\nthe unary <b>not</b> (see <a href=\"#2.5.3\">&sect;2.5.3</a>),\nand the unary <em>length operator</em> (see <a href=\"#2.5.5\">&sect;2.5.5</a>).\n\n\n<p>\nBoth function calls and vararg expressions can result in multiple values.\nIf an expression is used as a statement\n(only possible for function calls (see <a href=\"#2.4.6\">&sect;2.4.6</a>)),\nthen its return list is adjusted to zero elements,\nthus discarding all returned values.\nIf an expression is used as the last (or the only) element\nof a list of expressions,\nthen no adjustment is made\n(unless the call is enclosed in parentheses).\nIn all other contexts,\nLua adjusts the result list to one element,\ndiscarding all values except the first one.\n\n\n<p>\nHere are some examples:\n\n<pre>\n     f()                -- adjusted to 0 results\n     g(f(), x)          -- f() is adjusted to 1 result\n     g(x, f())          -- g gets x plus all results from f()\n     a,b,c = f(), x     -- f() is adjusted to 1 result (c gets nil)\n     a,b = ...          -- a gets the first vararg parameter, b gets\n                        -- the second (both a and b can get nil if there\n                        -- is no corresponding vararg parameter)\n     \n     a,b,c = x, f()     -- f() is adjusted to 2 results\n     a,b,c = f()        -- f() is adjusted to 3 results\n     return f()         -- returns all results from f()\n     return ...         -- returns all received vararg parameters\n     return x,y,f()     -- returns x, y, and all results from f()\n     {f()}              -- creates a list with all results from f()\n     {...}              -- creates a list with all vararg parameters\n     {f(), nil}         -- f() is adjusted to 1 result\n</pre>\n\n<p>\nAny expression enclosed in parentheses always results in only one value.\nThus,\n<code>(f(x,y,z))</code> is always a single value,\neven if <code>f</code> returns several values.\n(The value of <code>(f(x,y,z))</code> is the first value returned by <code>f</code>\nor <b>nil</b> if <code>f</code> does not return any values.)\n\n\n\n<h3>2.5.1 - <a name=\"2.5.1\">Arithmetic Operators</a></h3><p>\nLua supports the usual arithmetic operators:\nthe binary <code>+</code> (addition),\n<code>-</code> (subtraction), <code>*</code> (multiplication),\n<code>/</code> (division), <code>%</code> (modulo), and <code>^</code> (exponentiation);\nand unary <code>-</code> (negation).\nIf the operands are numbers, or strings that can be converted to\nnumbers (see <a href=\"#2.2.1\">&sect;2.2.1</a>),\nthen all operations have the usual meaning.\nExponentiation works for any exponent.\nFor instance, <code>x^(-0.5)</code> computes the inverse of the square root of <code>x</code>.\nModulo is defined as\n\n<pre>\n     a % b == a - math.floor(a/b)*b\n</pre><p>\nThat is, it is the remainder of a division that rounds\nthe quotient towards minus infinity.\n\n\n\n\n\n<h3>2.5.2 - <a name=\"2.5.2\">Relational Operators</a></h3><p>\nThe relational operators in Lua are\n\n<pre>\n     ==    ~=    &lt;     &gt;     &lt;=    &gt;=\n</pre><p>\nThese operators always result in <b>false</b> or <b>true</b>.\n\n\n<p>\nEquality (<code>==</code>) first compares the type of its operands.\nIf the types are different, then the result is <b>false</b>.\nOtherwise, the values of the operands are compared.\nNumbers and strings are compared in the usual way.\nObjects (tables, threads, and functions)\nare compared by <em>reference</em>:\ntwo objects are considered equal only if they are the <em>same</em> object.\nEvery time you create a new object\n(a table, thread, or function),\nthis new object is different from any previously existing object.\n\n\n<p>\nYou can change the way that Lua compares tables by using the \"eq\" metamethod\n(see <a href=\"#2.8\">&sect;2.8</a>).\n\n\n<p>\nThe conversion rules of <a href=\"#2.2.1\">&sect;2.2.1</a>\n<em>do not</em> apply to equality comparisons.\nThus, <code>\"0\"==0</code> evaluates to <b>false</b>,\nand <code>t[0]</code> and <code>t[\"0\"]</code> denote different\nentries in a table.\n\n\n<p>\nThe operator <code>~=</code> is exactly the negation of equality (<code>==</code>).\n\n\n<p>\nThe order operators work as follows.\nIf both arguments are numbers, then they are compared as such.\nOtherwise, if both arguments are strings,\nthen their values are compared according to the current locale.\nOtherwise, Lua tries to call the \"lt\" or the \"le\"\nmetamethod (see <a href=\"#2.8\">&sect;2.8</a>).\nA comparison <code>a &gt; b</code> is translated to <code>b &lt; a</code>\nand <code>a &gt;= b</code> is translated to <code>b &lt;= a</code>.\n\n\n\n\n\n<h3>2.5.3 - <a name=\"2.5.3\">Logical Operators</a></h3><p>\nThe logical operators in Lua are\n<b>and</b>, <b>or</b>, and <b>not</b>.\nLike the control structures (see <a href=\"#2.4.4\">&sect;2.4.4</a>),\nall logical operators consider both <b>false</b> and <b>nil</b> as false\nand anything else as true.\n\n\n<p>\nThe negation operator <b>not</b> always returns <b>false</b> or <b>true</b>.\nThe conjunction operator <b>and</b> returns its first argument\nif this value is <b>false</b> or <b>nil</b>;\notherwise, <b>and</b> returns its second argument.\nThe disjunction operator <b>or</b> returns its first argument\nif this value is different from <b>nil</b> and <b>false</b>;\notherwise, <b>or</b> returns its second argument.\nBoth <b>and</b> and <b>or</b> use short-cut evaluation;\nthat is,\nthe second operand is evaluated only if necessary.\nHere are some examples:\n\n<pre>\n     10 or 20            --&gt; 10\n     10 or error()       --&gt; 10\n     nil or \"a\"          --&gt; \"a\"\n     nil and 10          --&gt; nil\n     false and error()   --&gt; false\n     false and nil       --&gt; false\n     false or nil        --&gt; nil\n     10 and 20           --&gt; 20\n</pre><p>\n(In this manual,\n<code>--&gt;</code> indicates the result of the preceding expression.)\n\n\n\n\n\n<h3>2.5.4 - <a name=\"2.5.4\">Concatenation</a></h3><p>\nThe string concatenation operator in Lua is\ndenoted by two dots ('<code>..</code>').\nIf both operands are strings or numbers, then they are converted to\nstrings according to the rules mentioned in <a href=\"#2.2.1\">&sect;2.2.1</a>.\nOtherwise, the \"concat\" metamethod is called (see <a href=\"#2.8\">&sect;2.8</a>).\n\n\n\n\n\n<h3>2.5.5 - <a name=\"2.5.5\">The Length Operator</a></h3>\n\n<p>\nThe length operator is denoted by the unary operator <code>#</code>.\nThe length of a string is its number of bytes\n(that is, the usual meaning of string length when each\ncharacter is one byte).\n\n\n<p>\nThe length of a table <code>t</code> is defined to be any\ninteger index <code>n</code>\nsuch that <code>t[n]</code> is not <b>nil</b> and <code>t[n+1]</code> is <b>nil</b>;\nmoreover, if <code>t[1]</code> is <b>nil</b>, <code>n</code> can be zero.\nFor a regular array, with non-nil values from 1 to a given <code>n</code>,\nits length is exactly that <code>n</code>,\nthe index of its last value.\nIf the array has \"holes\"\n(that is, <b>nil</b> values between other non-nil values),\nthen <code>#t</code> can be any of the indices that\ndirectly precedes a <b>nil</b> value\n(that is, it may consider any such <b>nil</b> value as the end of\nthe array). \n\n\n\n\n\n<h3>2.5.6 - <a name=\"2.5.6\">Precedence</a></h3><p>\nOperator precedence in Lua follows the table below,\nfrom lower to higher priority:\n\n<pre>\n     or\n     and\n     &lt;     &gt;     &lt;=    &gt;=    ~=    ==\n     ..\n     +     -\n     *     /     %\n     not   #     - (unary)\n     ^\n</pre><p>\nAs usual,\nyou can use parentheses to change the precedences of an expression.\nThe concatenation ('<code>..</code>') and exponentiation ('<code>^</code>')\noperators are right associative.\nAll other binary operators are left associative.\n\n\n\n\n\n<h3>2.5.7 - <a name=\"2.5.7\">Table Constructors</a></h3><p>\nTable constructors are expressions that create tables.\nEvery time a constructor is evaluated, a new table is created.\nA constructor can be used to create an empty table\nor to create a table and initialize some of its fields.\nThe general syntax for constructors is\n\n<pre>\n\ttableconstructor ::= `<b>{</b>&acute; [fieldlist] `<b>}</b>&acute;\n\tfieldlist ::= field {fieldsep field} [fieldsep]\n\tfield ::= `<b>[</b>&acute; exp `<b>]</b>&acute; `<b>=</b>&acute; exp | Name `<b>=</b>&acute; exp | exp\n\tfieldsep ::= `<b>,</b>&acute; | `<b>;</b>&acute;\n</pre>\n\n<p>\nEach field of the form <code>[exp1] = exp2</code> adds to the new table an entry\nwith key <code>exp1</code> and value <code>exp2</code>.\nA field of the form <code>name = exp</code> is equivalent to\n<code>[\"name\"] = exp</code>.\nFinally, fields of the form <code>exp</code> are equivalent to\n<code>[i] = exp</code>, where <code>i</code> are consecutive numerical integers,\nstarting with 1.\nFields in the other formats do not affect this counting.\nFor example,\n\n<pre>\n     a = { [f(1)] = g; \"x\", \"y\"; x = 1, f(x), [30] = 23; 45 }\n</pre><p>\nis equivalent to\n\n<pre>\n     do\n       local t = {}\n       t[f(1)] = g\n       t[1] = \"x\"         -- 1st exp\n       t[2] = \"y\"         -- 2nd exp\n       t.x = 1            -- t[\"x\"] = 1\n       t[3] = f(x)        -- 3rd exp\n       t[30] = 23\n       t[4] = 45          -- 4th exp\n       a = t\n     end\n</pre>\n\n<p>\nIf the last field in the list has the form <code>exp</code>\nand the expression is a function call or a vararg expression,\nthen all values returned by this expression enter the list consecutively\n(see <a href=\"#2.5.8\">&sect;2.5.8</a>).\nTo avoid this,\nenclose the function call or the vararg expression\nin parentheses (see <a href=\"#2.5\">&sect;2.5</a>).\n\n\n<p>\nThe field list can have an optional trailing separator,\nas a convenience for machine-generated code.\n\n\n\n\n\n<h3>2.5.8 - <a name=\"2.5.8\">Function Calls</a></h3><p>\nA function call in Lua has the following syntax:\n\n<pre>\n\tfunctioncall ::= prefixexp args\n</pre><p>\nIn a function call,\nfirst prefixexp and args are evaluated.\nIf the value of prefixexp has type <em>function</em>,\nthen this function is called\nwith the given arguments.\nOtherwise, the prefixexp \"call\" metamethod is called,\nhaving as first parameter the value of prefixexp,\nfollowed by the original call arguments\n(see <a href=\"#2.8\">&sect;2.8</a>).\n\n\n<p>\nThe form\n\n<pre>\n\tfunctioncall ::= prefixexp `<b>:</b>&acute; Name args\n</pre><p>\ncan be used to call \"methods\".\nA call <code>v:name(<em>args</em>)</code>\nis syntactic sugar for <code>v.name(v,<em>args</em>)</code>,\nexcept that <code>v</code> is evaluated only once.\n\n\n<p>\nArguments have the following syntax:\n\n<pre>\n\targs ::= `<b>(</b>&acute; [explist] `<b>)</b>&acute;\n\targs ::= tableconstructor\n\targs ::= String\n</pre><p>\nAll argument expressions are evaluated before the call.\nA call of the form <code>f{<em>fields</em>}</code> is\nsyntactic sugar for <code>f({<em>fields</em>})</code>;\nthat is, the argument list is a single new table.\nA call of the form <code>f'<em>string</em>'</code>\n(or <code>f\"<em>string</em>\"</code> or <code>f[[<em>string</em>]]</code>)\nis syntactic sugar for <code>f('<em>string</em>')</code>;\nthat is, the argument list is a single literal string.\n\n\n<p>\nAs an exception to the free-format syntax of Lua,\nyou cannot put a line break before the '<code>(</code>' in a function call.\nThis restriction avoids some ambiguities in the language.\nIf you write\n\n<pre>\n     a = f\n     (g).x(a)\n</pre><p>\nLua would see that as a single statement, <code>a = f(g).x(a)</code>.\nSo, if you want two statements, you must add a semi-colon between them.\nIf you actually want to call <code>f</code>,\nyou must remove the line break before <code>(g)</code>.\n\n\n<p>\nA call of the form <code>return</code> <em>functioncall</em> is called\na <em>tail call</em>.\nLua implements <em>proper tail calls</em>\n(or <em>proper tail recursion</em>):\nin a tail call,\nthe called function reuses the stack entry of the calling function.\nTherefore, there is no limit on the number of nested tail calls that\na program can execute.\nHowever, a tail call erases any debug information about the\ncalling function.\nNote that a tail call only happens with a particular syntax,\nwhere the <b>return</b> has one single function call as argument;\nthis syntax makes the calling function return exactly\nthe returns of the called function.\nSo, none of the following examples are tail calls:\n\n<pre>\n     return (f(x))        -- results adjusted to 1\n     return 2 * f(x)\n     return x, f(x)       -- additional results\n     f(x); return         -- results discarded\n     return x or f(x)     -- results adjusted to 1\n</pre>\n\n\n\n\n<h3>2.5.9 - <a name=\"2.5.9\">Function Definitions</a></h3>\n\n<p>\nThe syntax for function definition is\n\n<pre>\n\tfunction ::= <b>function</b> funcbody\n\tfuncbody ::= `<b>(</b>&acute; [parlist] `<b>)</b>&acute; block <b>end</b>\n</pre>\n\n<p>\nThe following syntactic sugar simplifies function definitions:\n\n<pre>\n\tstat ::= <b>function</b> funcname funcbody\n\tstat ::= <b>local</b> <b>function</b> Name funcbody\n\tfuncname ::= Name {`<b>.</b>&acute; Name} [`<b>:</b>&acute; Name]\n</pre><p>\nThe statement\n\n<pre>\n     function f () <em>body</em> end\n</pre><p>\ntranslates to\n\n<pre>\n     f = function () <em>body</em> end\n</pre><p>\nThe statement\n\n<pre>\n     function t.a.b.c.f () <em>body</em> end\n</pre><p>\ntranslates to\n\n<pre>\n     t.a.b.c.f = function () <em>body</em> end\n</pre><p>\nThe statement\n\n<pre>\n     local function f () <em>body</em> end\n</pre><p>\ntranslates to\n\n<pre>\n     local f; f = function () <em>body</em> end\n</pre><p>\n<em>not</em> to\n\n<pre>\n     local f = function () <em>body</em> end\n</pre><p>\n(This only makes a difference when the body of the function\ncontains references to <code>f</code>.)\n\n\n<p>\nA function definition is an executable expression,\nwhose value has type <em>function</em>.\nWhen Lua pre-compiles a chunk,\nall its function bodies are pre-compiled too.\nThen, whenever Lua executes the function definition,\nthe function is <em>instantiated</em> (or <em>closed</em>).\nThis function instance (or <em>closure</em>)\nis the final value of the expression.\nDifferent instances of the same function\ncan refer to different  external local variables\nand can have different environment tables.\n\n\n<p>\nParameters act as local variables that are\ninitialized with the argument values:\n\n<pre>\n\tparlist ::= namelist [`<b>,</b>&acute; `<b>...</b>&acute;] | `<b>...</b>&acute;\n</pre><p>\nWhen a function is called,\nthe list of arguments is adjusted to\nthe length of the list of parameters,\nunless the function is a variadic or <em>vararg function</em>,\nwhich is\nindicated by three dots ('<code>...</code>') at the end of its parameter list.\nA vararg function does not adjust its argument list;\ninstead, it collects all extra arguments and supplies them\nto the function through a <em>vararg expression</em>,\nwhich is also written as three dots.\nThe value of this expression is a list of all actual extra arguments,\nsimilar to a function with multiple results.\nIf a vararg expression is used inside another expression\nor in the middle of a list of expressions,\nthen its return list is adjusted to one element.\nIf the expression is used as the last element of a list of expressions,\nthen no adjustment is made\n(unless that last expression is enclosed in parentheses).\n\n\n<p>\nAs an example, consider the following definitions:\n\n<pre>\n     function f(a, b) end\n     function g(a, b, ...) end\n     function r() return 1,2,3 end\n</pre><p>\nThen, we have the following mapping from arguments to parameters and\nto the vararg expression:\n\n<pre>\n     CALL            PARAMETERS\n     \n     f(3)             a=3, b=nil\n     f(3, 4)          a=3, b=4\n     f(3, 4, 5)       a=3, b=4\n     f(r(), 10)       a=1, b=10\n     f(r())           a=1, b=2\n     \n     g(3)             a=3, b=nil, ... --&gt;  (nothing)\n     g(3, 4)          a=3, b=4,   ... --&gt;  (nothing)\n     g(3, 4, 5, 8)    a=3, b=4,   ... --&gt;  5  8\n     g(5, r())        a=5, b=1,   ... --&gt;  2  3\n</pre>\n\n<p>\nResults are returned using the <b>return</b> statement (see <a href=\"#2.4.4\">&sect;2.4.4</a>).\nIf control reaches the end of a function\nwithout encountering a <b>return</b> statement,\nthen the function returns with no results.\n\n\n<p>\nThe <em>colon</em> syntax\nis used for defining <em>methods</em>,\nthat is, functions that have an implicit extra parameter <code>self</code>.\nThus, the statement\n\n<pre>\n     function t.a.b.c:f (<em>params</em>) <em>body</em> end\n</pre><p>\nis syntactic sugar for\n\n<pre>\n     t.a.b.c.f = function (self, <em>params</em>) <em>body</em> end\n</pre>\n\n\n\n\n\n\n<h2>2.6 - <a name=\"2.6\">Visibility Rules</a></h2>\n\n<p>\n\nLua is a lexically scoped language.\nThe scope of variables begins at the first statement <em>after</em>\ntheir declaration and lasts until the end of the innermost block that\nincludes the declaration.\nConsider the following example:\n\n<pre>\n     x = 10                -- global variable\n     do                    -- new block\n       local x = x         -- new 'x', with value 10\n       print(x)            --&gt; 10\n       x = x+1\n       do                  -- another block\n         local x = x+1     -- another 'x'\n         print(x)          --&gt; 12\n       end\n       print(x)            --&gt; 11\n     end\n     print(x)              --&gt; 10  (the global one)\n</pre>\n\n<p>\nNotice that, in a declaration like <code>local x = x</code>,\nthe new <code>x</code> being declared is not in scope yet,\nand so the second <code>x</code> refers to the outside variable.\n\n\n<p>\nBecause of the lexical scoping rules,\nlocal variables can be freely accessed by functions\ndefined inside their scope.\nA local variable used by an inner function is called\nan <em>upvalue</em>, or <em>external local variable</em>,\ninside the inner function.\n\n\n<p>\nNotice that each execution of a <b>local</b> statement\ndefines new local variables.\nConsider the following example:\n\n<pre>\n     a = {}\n     local x = 20\n     for i=1,10 do\n       local y = 0\n       a[i] = function () y=y+1; return x+y end\n     end\n</pre><p>\nThe loop creates ten closures\n(that is, ten instances of the anonymous function).\nEach of these closures uses a different <code>y</code> variable,\nwhile all of them share the same <code>x</code>.\n\n\n\n\n\n<h2>2.7 - <a name=\"2.7\">Error Handling</a></h2>\n\n<p>\n<div class='teliva'>\nErrors returned by Teliva code should display on screen in red. Please report\ncrashes or other behavior.\n</div>\n\n\n<p>\nLua code can explicitly generate an error by calling the\n<a href=\"#pdf-error\"><code>error</code></a> function.\nIf you need to catch errors in Lua,\nyou can use the <a href=\"#pdf-pcall\"><code>pcall</code></a> function.\n\n\n\n\n\n<h2>2.8 - <a name=\"2.8\">Metatables</a></h2>\n\n<p>\nEvery value in Lua can have a <em>metatable</em>.\nThis <em>metatable</em> is an ordinary Lua table\nthat defines the behavior of the original value\nunder certain special operations.\nYou can change several aspects of the behavior\nof operations over a value by setting specific fields in its metatable.\nFor instance, when a non-numeric value is the operand of an addition,\nLua checks for a function in the field <code>\"__add\"</code> in its metatable.\nIf it finds one,\nLua calls this function to perform the addition.\n\n\n<p>\nWe call the keys in a metatable <em>events</em>\nand the values <em>metamethods</em>.\nIn the previous example, the event is <code>\"add\"</code> \nand the metamethod is the function that performs the addition.\n\n\n<p>\nYou can query the metatable of any value\nthrough the <a href=\"#pdf-getmetatable\"><code>getmetatable</code></a> function.\n\n\n<p>\nYou can replace the metatable of tables\nthrough the <a href=\"#pdf-setmetatable\"><code>setmetatable</code></a>\nfunction.\n\n\n<p>\nTables have individual metatables\n(although multiple tables can share their metatables).\nValues of all other types share one single metatable per type;\nthat is, there is one single metatable for all numbers,\none for all strings, etc.\n\n\n<p>\nA metatable controls how an object behaves in arithmetic operations,\norder comparisons, concatenation, length operation, and indexing.\nFor each of these operations Lua associates a specific key\ncalled an <em>event</em>.\nWhen Lua performs one of these operations over a value,\nit checks whether this value has a metatable with the corresponding event.\nIf so, the value associated with that key (the metamethod)\ncontrols how Lua will perform the operation.\n\n\n<p>\nMetatables control the operations listed next.\nEach operation is identified by its corresponding name.\nThe key for each operation is a string with its name prefixed by\ntwo underscores, '<code>__</code>';\nfor instance, the key for operation \"add\" is the\nstring <code>\"__add\"</code>.\nThe semantics of these operations is better explained by a Lua function\ndescribing how the interpreter executes the operation.\n\n\n<p>\nThe code shown here in Lua is only illustrative;\nthe real behavior is hard coded in the interpreter\nand it is much more efficient than this simulation.\nAll functions used in these descriptions\n(<a href=\"#pdf-rawget\"><code>rawget</code></a>, <a href=\"#pdf-tonumber\"><code>tonumber</code></a>, etc.)\nare described in <a href=\"#5.1\">&sect;5.1</a>.\nIn particular, to retrieve the metamethod of a given object,\nwe use the expression\n\n<pre>\n     metatable(obj)[event]\n</pre><p>\nThis should be read as\n\n<pre>\n     rawget(getmetatable(obj) or {}, event)\n</pre><p>\n\nThat is, the access to a metamethod does not invoke other metamethods,\nand the access to objects with no metatables does not fail\n(it simply results in <b>nil</b>).\n\n\n\n<ul>\n\n<li><b>\"add\":</b>\nthe <code>+</code> operation.\n\n\n\n<p>\nThe function <code>getbinhandler</code> below defines how Lua chooses a handler\nfor a binary operation.\nFirst, Lua tries the first operand.\nIf its type does not define a handler for the operation,\nthen Lua tries the second operand.\n\n<pre>\n     function getbinhandler (op1, op2, event)\n       return metatable(op1)[event] or metatable(op2)[event]\n     end\n</pre><p>\nBy using this function,\nthe behavior of the <code>op1 + op2</code> is\n\n<pre>\n     function add_event (op1, op2)\n       local o1, o2 = tonumber(op1), tonumber(op2)\n       if o1 and o2 then  -- both operands are numeric?\n         return o1 + o2   -- '+' here is the primitive 'add'\n       else  -- at least one of the operands is not numeric\n         local h = getbinhandler(op1, op2, \"__add\")\n         if h then\n           -- call the handler with both operands\n           return (h(op1, op2))\n         else  -- no handler available: default behavior\n           error(&middot;&middot;&middot;)\n         end\n       end\n     end\n</pre><p>\n</li>\n\n<li><b>\"sub\":</b>\nthe <code>-</code> operation.\n\nBehavior similar to the \"add\" operation.\n</li>\n\n<li><b>\"mul\":</b>\nthe <code>*</code> operation.\n\nBehavior similar to the \"add\" operation.\n</li>\n\n<li><b>\"div\":</b>\nthe <code>/</code> operation.\n\nBehavior similar to the \"add\" operation.\n</li>\n\n<li><b>\"mod\":</b>\nthe <code>%</code> operation.\n\nBehavior similar to the \"add\" operation,\nwith the operation\n<code>o1 - floor(o1/o2)*o2</code> as the primitive operation.\n</li>\n\n<li><b>\"pow\":</b>\nthe <code>^</code> (exponentiation) operation.\n\nBehavior similar to the \"add\" operation,\nwith the function <code>pow</code> (from the C&nbsp;math library)\nas the primitive operation.\n</li>\n\n<li><b>\"unm\":</b>\nthe unary <code>-</code> operation.\n\n\n<pre>\n     function unm_event (op)\n       local o = tonumber(op)\n       if o then  -- operand is numeric?\n         return -o  -- '-' here is the primitive 'unm'\n       else  -- the operand is not numeric.\n         -- Try to get a handler from the operand\n         local h = metatable(op).__unm\n         if h then\n           -- call the handler with the operand\n           return (h(op))\n         else  -- no handler available: default behavior\n           error(&middot;&middot;&middot;)\n         end\n       end\n     end\n</pre><p>\n</li>\n\n<li><b>\"concat\":</b>\nthe <code>..</code> (concatenation) operation.\n\n\n<pre>\n     function concat_event (op1, op2)\n       if (type(op1) == \"string\" or type(op1) == \"number\") and\n          (type(op2) == \"string\" or type(op2) == \"number\") then\n         return op1 .. op2  -- primitive string concatenation\n       else\n         local h = getbinhandler(op1, op2, \"__concat\")\n         if h then\n           return (h(op1, op2))\n         else\n           error(&middot;&middot;&middot;)\n         end\n       end\n     end\n</pre><p>\n</li>\n\n<li><b>\"len\":</b>\nthe <code>#</code> operation.\n\n\n<pre>\n     function len_event (op)\n       if type(op) == \"string\" then\n         return strlen(op)         -- primitive string length\n       elseif type(op) == \"table\" then\n         return #op                -- primitive table length\n       else\n         local h = metatable(op).__len\n         if h then\n           -- call the handler with the operand\n           return (h(op))\n         else  -- no handler available: default behavior\n           error(&middot;&middot;&middot;)\n         end\n       end\n     end\n</pre><p>\nSee <a href=\"#2.5.5\">&sect;2.5.5</a> for a description of the length of a table.\n</li>\n\n<li><b>\"eq\":</b>\nthe <code>==</code> operation.\n\nThe function <code>getcomphandler</code> defines how Lua chooses a metamethod\nfor comparison operators.\nA metamethod only is selected when both objects\nbeing compared have the same type\nand the same metamethod for the selected operation.\n\n<pre>\n     function getcomphandler (op1, op2, event)\n       if type(op1) ~= type(op2) then return nil end\n       local mm1 = metatable(op1)[event]\n       local mm2 = metatable(op2)[event]\n       if mm1 == mm2 then return mm1 else return nil end\n     end\n</pre><p>\nThe \"eq\" event is defined as follows:\n\n<pre>\n     function eq_event (op1, op2)\n       if type(op1) ~= type(op2) then  -- different types?\n         return false   -- different objects\n       end\n       if op1 == op2 then   -- primitive equal?\n         return true   -- objects are equal\n       end\n       -- try metamethod\n       local h = getcomphandler(op1, op2, \"__eq\")\n       if h then\n         return (h(op1, op2))\n       else\n         return false\n       end\n     end\n</pre><p>\n<code>a ~= b</code> is equivalent to <code>not (a == b)</code>.\n</li>\n\n<li><b>\"lt\":</b>\nthe <code>&lt;</code> operation.\n\n\n<pre>\n     function lt_event (op1, op2)\n       if type(op1) == \"number\" and type(op2) == \"number\" then\n         return op1 &lt; op2   -- numeric comparison\n       elseif type(op1) == \"string\" and type(op2) == \"string\" then\n         return op1 &lt; op2   -- lexicographic comparison\n       else\n         local h = getcomphandler(op1, op2, \"__lt\")\n         if h then\n           return (h(op1, op2))\n         else\n           error(&middot;&middot;&middot;)\n         end\n       end\n     end\n</pre><p>\n<code>a &gt; b</code> is equivalent to <code>b &lt; a</code>.\n</li>\n\n<li><b>\"le\":</b>\nthe <code>&lt;=</code> operation.\n\n\n<pre>\n     function le_event (op1, op2)\n       if type(op1) == \"number\" and type(op2) == \"number\" then\n         return op1 &lt;= op2   -- numeric comparison\n       elseif type(op1) == \"string\" and type(op2) == \"string\" then\n         return op1 &lt;= op2   -- lexicographic comparison\n       else\n         local h = getcomphandler(op1, op2, \"__le\")\n         if h then\n           return (h(op1, op2))\n         else\n           h = getcomphandler(op1, op2, \"__lt\")\n           if h then\n             return not h(op2, op1)\n           else\n             error(&middot;&middot;&middot;)\n           end\n         end\n       end\n     end\n</pre><p>\n<code>a &gt;= b</code> is equivalent to <code>b &lt;= a</code>.\nNote that, in the absence of a \"le\" metamethod,\nLua tries the \"lt\", assuming that <code>a &lt;= b</code> is\nequivalent to <code>not (b &lt; a)</code>.\n</li>\n\n<li><b>\"index\":</b>\nThe indexing access <code>table[key]</code>.\n\n\n<pre>\n     function gettable_event (table, key)\n       local h\n       if type(table) == \"table\" then\n         local v = rawget(table, key)\n         if v ~= nil then return v end\n         h = metatable(table).__index\n         if h == nil then return nil end\n       else\n         h = metatable(table).__index\n         if h == nil then\n           error(&middot;&middot;&middot;)\n         end\n       end\n       if type(h) == \"function\" then\n         return (h(table, key))     -- call the handler\n       else return h[key]           -- or repeat operation on it\n       end\n     end\n</pre><p>\n</li>\n\n<li><b>\"newindex\":</b>\nThe indexing assignment <code>table[key] = value</code>.\n\n\n<pre>\n     function settable_event (table, key, value)\n       local h\n       if type(table) == \"table\" then\n         local v = rawget(table, key)\n         if v ~= nil then rawset(table, key, value); return end\n         h = metatable(table).__newindex\n         if h == nil then rawset(table, key, value); return end\n       else\n         h = metatable(table).__newindex\n         if h == nil then\n           error(&middot;&middot;&middot;)\n         end\n       end\n       if type(h) == \"function\" then\n         h(table, key,value)           -- call the handler\n       else h[key] = value             -- or repeat operation on it\n       end\n     end\n</pre><p>\n</li>\n\n<li><b>\"call\":</b>\ncalled when Lua calls a value.\n\n\n<pre>\n     function function_event (func, ...)\n       if type(func) == \"function\" then\n         return func(...)   -- primitive call\n       else\n         local h = metatable(func).__call\n         if h then\n           return h(func, ...)\n         else\n           error(&middot;&middot;&middot;)\n         end\n       end\n     end\n</pre><p>\n</li>\n\n</ul>\n\n\n\n\n<h2>2.9 - <a name=\"2.9\">Environments</a></h2>\n\n<p>\nBesides metatables, objects of types thread and function\nhave another table associated with them, called their\n<em>environment</em>.\nLike metatables, environments are regular tables and\nmultiple objects can share the same environment.\n\n\n<p>\nThreads are created sharing the environment of the creating thread.\nNested Lua functions are created sharing the environment of\nthe creating Lua function.\n\n\n<p>\nEnvironments associated with threads are called\n<em>global environments</em>.\nThey are used as the default environment for threads.\n\n\n<p>\nEnvironments associated with Lua functions are used to resolve\nall accesses to global variables within the function (see <a href=\"#2.3\">&sect;2.3</a>).\nThey are used as the default environment for nested Lua functions\ncreated by the function.\n\n\n<p>\nYou can change the environment of a Lua function or the\nrunning thread by calling <a href=\"#pdf-setfenv\"><code>setfenv</code></a>.\nYou can get the environment of a Lua function or the running thread\nby calling <a href=\"#pdf-getfenv\"><code>getfenv</code></a>.\n\n\n\n\n\n<h2>2.10 - <a name=\"2.10\">Garbage Collection</a></h2>\n\n<p>\nLua performs automatic memory management.\nThis means that\nyou have to worry neither about allocating memory for new objects\nnor about freeing it when the objects are no longer needed.\nLua manages memory automatically by running\na <em>garbage collector</em> from time to time\nto collect all <em>dead objects</em>\n(that is, objects that are no longer accessible from Lua).\nAll memory used by Lua is subject to automatic management:\ntables, functions, threads, strings, etc.\n\n\n<p>\nLua implements an incremental mark-and-sweep collector.\nIt uses two numbers to control its garbage-collection cycles:\nthe <em>garbage-collector pause</em> and\nthe <em>garbage-collector step multiplier</em>.\nBoth use percentage points as units\n(so that a value of 100 means an internal value of 1).\n\n\n<p>\nThe garbage-collector pause\ncontrols how long the collector waits before starting a new cycle.\nLarger values make the collector less aggressive.\nValues smaller than 100 mean the collector will not wait to\nstart a new cycle.\nA value of 200 means that the collector waits for the total memory in use\nto double before starting a new cycle.\n\n\n<p>\nThe step multiplier\ncontrols the relative speed of the collector relative to\nmemory allocation.\nLarger values make the collector more aggressive but also increase\nthe size of each incremental step.\nValues smaller than 100 make the collector too slow and\ncan result in the collector never finishing a cycle.\nThe default, 200, means that the collector runs at \"twice\"\nthe speed of memory allocation.\n\n\n<p>\n<div class='teliva'>Modifying these parameters requires changes to Teliva's C\nsources.</div>\n\n\n\n<h3>2.10.2 - <a name=\"2.10.2\">Weak Tables</a></h3>\n\n<p>\nA <em>weak table</em> is a table whose elements are\n<em>weak references</em>.\nA weak reference is ignored by the garbage collector.\nIn other words,\nif the only references to an object are weak references,\nthen the garbage collector will collect this object.\n\n\n<p>\nA weak table can have weak keys, weak values, or both.\nA table with weak keys allows the collection of its keys,\nbut prevents the collection of its values.\nA table with both weak keys and weak values allows the collection of\nboth keys and values.\nIn any case, if either the key or the value is collected,\nthe whole pair is removed from the table.\nThe weakness of a table is controlled by the\n<code>__mode</code> field of its metatable.\nIf the <code>__mode</code> field is a string containing the character&nbsp;'<code>k</code>',\nthe keys in the table are weak.\nIf <code>__mode</code> contains '<code>v</code>',\nthe values in the table are weak.\n\n\n<p>\nAfter you use a table as a metatable,\nyou should not change the value of its <code>__mode</code> field.\nOtherwise, the weak behavior of the tables controlled by this\nmetatable is undefined.\n\n\n\n\n\n\n\n<h2>2.11 - <a name=\"2.11\">Coroutines</a></h2>\n\n<p>\nLua supports coroutines,\nalso called <em>collaborative multithreading</em>.\nA coroutine in Lua represents an independent thread of execution.\nUnlike threads in multithread systems, however,\na coroutine only suspends its execution by explicitly calling\na yield function.\n\n\n<p>\nYou create a coroutine with a call to <a href=\"#pdf-coroutine.create\"><code>coroutine.create</code></a>.\nIts sole argument is a function\nthat is the main function of the coroutine.\nThe <code>create</code> function only creates a new coroutine and\nreturns a handle to it (an object of type <em>thread</em>);\nit does not start the coroutine execution.\n\n\n<p>\nWhen you first call <a href=\"#pdf-coroutine.resume\"><code>coroutine.resume</code></a>,\npassing as its first argument\na thread returned by <a href=\"#pdf-coroutine.create\"><code>coroutine.create</code></a>,\nthe coroutine starts its execution,\nat the first line of its main function.\nExtra arguments passed to <a href=\"#pdf-coroutine.resume\"><code>coroutine.resume</code></a> are passed on\nto the coroutine main function.\nAfter the coroutine starts running,\nit runs until it terminates or <em>yields</em>.\n\n\n<p>\nA coroutine can terminate its execution in two ways:\nnormally, when its main function returns\n(explicitly or implicitly, after the last instruction);\nand abnormally, if there is an unprotected error.\nIn the first case, <a href=\"#pdf-coroutine.resume\"><code>coroutine.resume</code></a> returns <b>true</b>,\nplus any values returned by the coroutine main function.\nIn case of errors, <a href=\"#pdf-coroutine.resume\"><code>coroutine.resume</code></a> returns <b>false</b>\nplus an error message.\n\n\n<p>\nA coroutine yields by calling <a href=\"#pdf-coroutine.yield\"><code>coroutine.yield</code></a>.\nWhen a coroutine yields,\nthe corresponding <a href=\"#pdf-coroutine.resume\"><code>coroutine.resume</code></a> returns immediately,\neven if the yield happens inside nested function calls\n(that is, not in the main function,\nbut in a function directly or indirectly called by the main function).\nIn the case of a yield, <a href=\"#pdf-coroutine.resume\"><code>coroutine.resume</code></a> also returns <b>true</b>,\nplus any values passed to <a href=\"#pdf-coroutine.yield\"><code>coroutine.yield</code></a>.\nThe next time you resume the same coroutine,\nit continues its execution from the point where it yielded,\nwith the call to <a href=\"#pdf-coroutine.yield\"><code>coroutine.yield</code></a> returning any extra\narguments passed to <a href=\"#pdf-coroutine.resume\"><code>coroutine.resume</code></a>.\n\n\n<p>\nLike <a href=\"#pdf-coroutine.create\"><code>coroutine.create</code></a>,\nthe <a href=\"#pdf-coroutine.wrap\"><code>coroutine.wrap</code></a> function also creates a coroutine,\nbut instead of returning the coroutine itself,\nit returns a function that, when called, resumes the coroutine.\nAny arguments passed to this function\ngo as extra arguments to <a href=\"#pdf-coroutine.resume\"><code>coroutine.resume</code></a>.\n<a href=\"#pdf-coroutine.wrap\"><code>coroutine.wrap</code></a> returns all the values returned by <a href=\"#pdf-coroutine.resume\"><code>coroutine.resume</code></a>,\nexcept the first one (the boolean error code).\nUnlike <a href=\"#pdf-coroutine.resume\"><code>coroutine.resume</code></a>,\n<a href=\"#pdf-coroutine.wrap\"><code>coroutine.wrap</code></a> does not catch errors;\nany error is propagated to the caller.\n\n\n<p>\nAs an example,\nconsider the following code:\n\n<pre>\n     function foo (a)\n       print(\"foo\", a)\n       return coroutine.yield(2*a)\n     end\n     \n     co = coroutine.create(function (a,b)\n           print(\"co-body\", a, b)\n           local r = foo(a+1)\n           print(\"co-body\", r)\n           local r, s = coroutine.yield(a+b, a-b)\n           print(\"co-body\", r, s)\n           return b, \"end\"\n     end)\n            \n     print(\"main\", coroutine.resume(co, 1, 10))\n     print(\"main\", coroutine.resume(co, \"r\"))\n     print(\"main\", coroutine.resume(co, \"x\", \"y\"))\n     print(\"main\", coroutine.resume(co, \"x\", \"y\"))\n</pre><p>\nWhen you run it, it produces the following output:\n\n<pre>\n     co-body 1       10\n     foo     2\n     \n     main    true    4\n     co-body r\n     main    true    11      -9\n     co-body x       y\n     main    true    10      end\n     main    false   cannot resume dead coroutine\n</pre>\n\n<div class='teliva'>\nCoroutines are powerful, but require some experience to use tastefully. Try to\nuse them judiciously. If you find yourself juggling lots of coroutines and\ntrying to decide which one to try to resume, you should probably be using\n<a href='#5.13'>tasks and channels</a> instead. On the other hand, tasks have\nsome cognitive overheads. You have to explicitly manage them with\n<a href='#pdf-task.scheduler'><code>task.scheduler</code></a>, and errors can\nsometimes be hard to track down. For simple generators that emit elements\non demand, a coroutine is likely the best solution.\n</div>\n\n\n<h1>3 - <a name=\"3\">C API</a></h1>\n\n<div class='teliva'>Unlike Lua, Teliva isn't intended to be modified at the C\nlevel. However, forks of Teliva are encouraged.</div>\n\n\n<h1>5 - <a name=\"5\">Standard Libraries</a></h1>\n\n<p>\nThe standard Lua libraries provide useful functions\nthat are implemented directly in C.\nSome of these functions provide essential services to the language\n(e.g., <a href=\"#pdf-type\"><code>type</code></a> and <a href=\"#pdf-getmetatable\"><code>getmetatable</code></a>);\nothers provide access to \"outside\" services (e.g., I/O);\nand others could be implemented in Lua itself,\nbut are quite useful or have critical performance requirements that\ndeserve an implementation in C (e.g., <a href=\"#pdf-table.sort\"><code>table.sort</code></a>).\n\n\n<p>\nCurrently, Lua has the following standard libraries:\n\n<ul>\n\n<li>basic library, which includes the coroutine sub-library;</li>\n\n<li>package library;</li>\n\n<li>string manipulation;</li>\n\n<li>table manipulation;</li>\n\n<li>mathematical functions (sin, log, etc.);</li>\n\n<li>input and output;</li>\n\n<li>operating system facilities;</li>\n\n<li>debug facilities.</li>\n\n</ul><p>\nExcept for the basic and package libraries,\neach library provides all its functions as fields of a global table\nor as methods of its objects.\n\n\n<h2>5.1 - <a name=\"5.1\">Basic Primitives</a></h2>\n\n<p>\nThe basic library provides some core functions to Lua.\nIf you do not include this library in your application,\nyou should check carefully whether you need to provide \nimplementations for some of its facilities.\n\n\n<p>\n<hr><h3><a name=\"pdf-arg\"><code>arg</code></a></h3>\nA global variable containing any commandline arguments.\n\n\n<p>\n<hr><h3><a name=\"pdf-assert\"><code>assert (v [, message])</code></a></h3>\nIssues an  error when\nthe value of its argument <code>v</code> is false (i.e., <b>nil</b> or <b>false</b>);\notherwise, returns all its arguments.\n<code>message</code> is an error message;\nwhen absent, it defaults to \"assertion failed!\"\n\n\n\n\n<p>\n<hr><h3><a name=\"pdf-collectgarbage\"><code>collectgarbage ([opt [, arg]])</code></a></h3>\n\n\n<p>\nThis function is a generic interface to the garbage collector.\nIt performs different functions according to its first argument, <code>opt</code>:\n\n<ul>\n\n<li><b>\"collect\":</b>\nperforms a full garbage-collection cycle.\nThis is the default option.\n</li>\n\n<li><b>\"stop\":</b>\nstops the garbage collector.\n</li>\n\n<li><b>\"restart\":</b>\nrestarts the garbage collector.\n</li>\n\n<li><b>\"count\":</b>\nreturns the total memory in use by Lua (in Kbytes).\n</li>\n\n<li><b>\"step\":</b>\nperforms a garbage-collection step.\nThe step \"size\" is controlled by <code>arg</code>\n(larger values mean more steps) in a non-specified way.\nIf you want to control the step size\nyou must experimentally tune the value of <code>arg</code>.\nReturns <b>true</b> if the step finished a collection cycle.\n</li>\n\n<li><b>\"setpause\":</b>\nsets <code>arg</code> as the new value for the <em>pause</em> of\nthe collector (see <a href=\"#2.10\">&sect;2.10</a>).\nReturns the previous value for <em>pause</em>.\n</li>\n\n<li><b>\"setstepmul\":</b>\nsets <code>arg</code> as the new value for the <em>step multiplier</em> of\nthe collector (see <a href=\"#2.10\">&sect;2.10</a>).\nReturns the previous value for <em>step</em>.\n</li>\n\n</ul>\n\n\n\n\n<p>\n<hr><h3><a name=\"pdf-error\"><code>error (message [, level])</code></a></h3>\nTerminates the last protected function called\nand returns <code>message</code> as the error message.\nFunction <code>error</code> never returns.\n\n\n<p>\nUsually, <code>error</code> adds some information about the error position\nat the beginning of the message.\nThe <code>level</code> argument specifies how to get the error position.\nWith level&nbsp;1 (the default), the error position is where the\n<code>error</code> function was called.\nLevel&nbsp;2 points the error to where the function\nthat called <code>error</code> was called; and so on.\nPassing a level&nbsp;0 avoids the addition of error position information\nto the message.\n\n\n\n\n<p>\n<hr><h3><a name=\"pdf-_G\"><code>_G</code></a></h3>\nA global variable that\nholds the global environment (that is, <code>_G._G = _G</code>).\nLua itself does not use this variable;\nchanging its value does not affect any environment,\nnor vice-versa.\n(Use <a href=\"#pdf-setfenv\"><code>setfenv</code></a> to change environments.)\n\n\n\n\n<p>\n<hr><h3><a name=\"pdf-getfenv\"><code>getfenv ([f])</code></a></h3>\nReturns the current environment in use by the function.\n<code>f</code> can be a Lua function or a number\nthat specifies the function at that stack level:\nLevel&nbsp;1 is the function calling <code>getfenv</code>.\nIf the given function is not a Lua function,\nor if <code>f</code> is 0,\n<code>getfenv</code> returns the global environment.\nThe default for <code>f</code> is 1.\n\n\n\n\n<p>\n<hr><h3><a name=\"pdf-getmetatable\"><code>getmetatable (object)</code></a></h3>\n\n\n<p>\nIf <code>object</code> does not have a metatable, returns <b>nil</b>.\nOtherwise,\nif the object's metatable has a <code>\"__metatable\"</code> field,\nreturns the associated value.\nOtherwise, returns the metatable of the given object.\n\n\n\n\n<p>\n<hr><h3><a name=\"pdf-ipairs\"><code>ipairs (t)</code></a></h3>\n\n\n<p>\nReturns three values: an iterator function, the table <code>t</code>, and 0,\nso that the construction\n\n<pre>\n     for i,v in ipairs(t) do <em>body</em> end\n</pre><p>\nwill iterate over the pairs (<code>1,t[1]</code>), (<code>2,t[2]</code>), &middot;&middot;&middot;,\nup to the first integer key absent from the table.\n\n\n<p>\n<hr><h3><a name=\"pdf-next\"><code>next (table [, index])</code></a></h3>\n\n\n<p>\nAllows a program to traverse all fields of a table.\nIts first argument is a table and its second argument\nis an index in this table.\n<code>next</code> returns the next index of the table\nand its associated value.\nWhen called with <b>nil</b> as its second argument,\n<code>next</code> returns an initial index\nand its associated value.\nWhen called with the last index,\nor with <b>nil</b> in an empty table,\n<code>next</code> returns <b>nil</b>.\nIf the second argument is absent, then it is interpreted as <b>nil</b>.\nIn particular,\nyou can use <code>next(t)</code> to check whether a table is empty.\n\n\n<p>\nThe order in which the indices are enumerated is not specified,\n<em>even for numeric indices</em>.\n(To traverse a table in numeric order,\nuse a numerical <b>for</b> or the <a href=\"#pdf-ipairs\"><code>ipairs</code></a> function.)\n\n\n<p>\nThe behavior of <code>next</code> is <em>undefined</em> if,\nduring the traversal,\nyou assign any value to a non-existent field in the table.\nYou may however modify existing fields.\nIn particular, you may clear existing fields.\n\n\n\n\n<p>\n<hr><h3><a name=\"pdf-pairs\"><code>pairs (t)</code></a></h3>\n\n\n<p>\nReturns three values: the <a href=\"#pdf-next\"><code>next</code></a> function, the table <code>t</code>, and <b>nil</b>,\nso that the construction\n\n<pre>\n     for k,v in pairs(t) do <em>body</em> end\n</pre><p>\nwill iterate over all key&ndash;value pairs of table <code>t</code>.\n\n\n<p>\nSee function <a href=\"#pdf-next\"><code>next</code></a> for the caveats of modifying\nthe table during its traversal.\n\n\n\n\n<p>\n<hr><h3><a name=\"pdf-print\"><code>print (&middot;&middot;&middot;)</code></a></h3>\nReceives any number of arguments,\nand prints their values to <code>stdout</code>,\nusing the <a href=\"#pdf-tostring\"><code>tostring</code></a> function to convert them to strings.\n<code>print</code> is not intended for formatted output,\nbut only as a quick way to show a value,\ntypically for debugging.\nFor formatted output, use <a href=\"#pdf-string.format\"><code>string.format</code></a>.\n\n\n\n\n<p>\n<hr><h3><a name=\"pdf-rawequal\"><code>rawequal (v1, v2)</code></a></h3>\nChecks whether <code>v1</code> is equal to <code>v2</code>,\nwithout invoking any metamethod.\nReturns a boolean.\n\n\n\n\n<p>\n<hr><h3><a name=\"pdf-rawget\"><code>rawget (table, index)</code></a></h3>\nGets the real value of <code>table[index]</code>,\nwithout invoking any metamethod.\n<code>table</code> must be a table;\n<code>index</code> may be any value.\n\n\n\n\n<p>\n<hr><h3><a name=\"pdf-rawset\"><code>rawset (table, index, value)</code></a></h3>\nSets the real value of <code>table[index]</code> to <code>value</code>,\nwithout invoking any metamethod.\n<code>table</code> must be a table,\n<code>index</code> any value different from <b>nil</b>,\nand <code>value</code> any Lua value.\n\n\n<p>\nThis function returns <code>table</code>.\n\n\n\n\n<p>\n<hr><h3><a name=\"pdf-select\"><code>select (index, &middot;&middot;&middot;)</code></a></h3>\n\n\n<p>\nIf <code>index</code> is a number,\nreturns all arguments after argument number <code>index</code>.\nOtherwise, <code>index</code> must be the string <code>\"#\"</code>,\nand <code>select</code> returns the total number of extra arguments it received.\n\n\n\n\n<p>\n<hr><h3><a name=\"pdf-setfenv\"><code>setfenv (f, table)</code></a></h3>\n\n\n<p>\nSets the environment to be used by the given function.\n<code>f</code> can be a Lua function or a number\nthat specifies the function at that stack level:\nLevel&nbsp;1 is the function calling <code>setfenv</code>.\n<code>setfenv</code> returns the given function.\n\n\n<p>\nAs a special case, when <code>f</code> is 0 <code>setfenv</code> changes\nthe environment of the running thread.\nIn this case, <code>setfenv</code> returns no values.\n\n\n\n\n<p>\n<hr><h3><a name=\"pdf-setmetatable\"><code>setmetatable (table, metatable)</code></a></h3>\n\n\n<p>\nSets the metatable for the given table.\n(You cannot change the metatable of other types.)\nIf <code>metatable</code> is <b>nil</b>,\nremoves the metatable of the given table.\nIf the original metatable has a <code>\"__metatable\"</code> field,\nraises an error.\n\n\n<p>\nThis function returns <code>table</code>.\n\n\n\n\n<p>\n<hr><h3><a name=\"pdf-tonumber\"><code>tonumber (e [, base])</code></a></h3>\nTries to convert its argument to a number.\nIf the argument is already a number or a string convertible\nto a number, then <code>tonumber</code> returns this number;\notherwise, it returns <b>nil</b>.\n\n\n<p>\nAn optional argument specifies the base to interpret the numeral.\nThe base may be any integer between 2 and 36, inclusive.\nIn bases above&nbsp;10, the letter '<code>A</code>' (in either upper or lower case)\nrepresents&nbsp;10, '<code>B</code>' represents&nbsp;11, and so forth,\nwith '<code>Z</code>' representing 35.\nIn base 10 (the default), the number can have a decimal part,\nas well as an optional exponent part (see <a href=\"#2.1\">&sect;2.1</a>).\nIn other bases, only unsigned integers are accepted.\n\n\n\n\n<p>\n<hr><h3><a name=\"pdf-tostring\"><code>tostring (e)</code></a></h3>\nReceives an argument of any type and\nconverts it to a string in a reasonable format.\nFor complete control of how numbers are converted,\nuse <a href=\"#pdf-string.format\"><code>string.format</code></a>.\n\n\n<p>\nIf the metatable of <code>e</code> has a <code>\"__tostring\"</code> field,\nthen <code>tostring</code> calls the corresponding value\nwith <code>e</code> as argument,\nand uses the result of the call as its result.\n\n\n\n\n<p>\n<hr><h3><a name=\"pdf-type\"><code>type (v)</code></a></h3>\nReturns the type of its only argument, coded as a string.\nThe possible results of this function are\n\"<code>nil</code>\" (a string, not the value <b>nil</b>),\n\"<code>number</code>\",\n\"<code>string</code>\",\n\"<code>boolean</code>\",\n\"<code>table</code>\",\n\"<code>function</code>\",\n\"<code>thread</code>\",\nand \"<code>userdata</code>\".\n\n\n\n\n<p>\n<hr><h3><a name=\"pdf-unpack\"><code>unpack (list [, i [, j]])</code></a></h3>\nReturns the elements from the given table.\nThis function is equivalent to\n\n<pre>\n     return list[i], list[i+1], &middot;&middot;&middot;, list[j]\n</pre><p>\nexcept that the above code can be written only for a fixed number\nof elements.\nBy default, <code>i</code> is&nbsp;1 and <code>j</code> is the length of the list,\nas defined by the length operator (see <a href=\"#2.5.5\">&sect;2.5.5</a>).\n\n\n\n\n<p>\n<hr><h3><a name=\"pdf-_VERSION\"><code>_VERSION</code></a></h3>\nA global variable (not a function) that\nholds a string containing the current interpreter version.\nThe current contents of this variable is \"<code>Lua 5.1</code>\".\n\n\n\n\n<h2>5.2 - <a name=\"5.2\">Coroutine Manipulation</a></h2>\n\n<p>\nThe operations related to coroutines comprise a sub-library of\nthe basic library and come inside the table <a name=\"pdf-coroutine\"><code>coroutine</code></a>.\nSee <a href=\"#2.11\">&sect;2.11</a> for a general description of coroutines.\n\n\n<p>\n<hr><h3><a name=\"pdf-coroutine.create\"><code>coroutine.create (f)</code></a></h3>\n\n\n<p>\nCreates a new coroutine, with body <code>f</code>.\n<code>f</code> must be a Lua function.\nReturns this new coroutine,\nan object with type <code>\"thread\"</code>.\n\n\n\n\n<p>\n<hr><h3><a name=\"pdf-coroutine.resume\"><code>coroutine.resume (co [, val1, &middot;&middot;&middot;])</code></a></h3>\n\n\n<p>\nStarts or continues the execution of coroutine <code>co</code>.\nThe first time you resume a coroutine,\nit starts running its body.\nThe values <code>val1</code>, &middot;&middot;&middot; are passed\nas the arguments to the body function.\nIf the coroutine has yielded,\n<code>resume</code> restarts it;\nthe values <code>val1</code>, &middot;&middot;&middot; are passed\nas the results from the yield.\n\n\n<p>\nIf the coroutine runs without any errors,\n<code>resume</code> returns <b>true</b> plus any values passed to <code>yield</code>\n(if the coroutine yields) or any values returned by the body function\n(if the coroutine terminates).\nIf there is any error,\n<code>resume</code> returns <b>false</b> plus the error message.\n\n\n\n\n<p>\n<hr><h3><a name=\"pdf-coroutine.running\"><code>coroutine.running ()</code></a></h3>\n\n\n<p>\nReturns the running coroutine,\nor <b>nil</b> when called by the main thread.\n\n\n\n\n<p>\n<hr><h3><a name=\"pdf-coroutine.status\"><code>coroutine.status (co)</code></a></h3>\n\n\n<p>\nReturns the status of coroutine <code>co</code>, as a string:\n<code>\"running\"</code>,\nif the coroutine is running (that is, it called <code>status</code>);\n<code>\"suspended\"</code>, if the coroutine is suspended in a call to <code>yield</code>,\nor if it has not started running yet;\n<code>\"normal\"</code> if the coroutine is active but not running\n(that is, it has resumed another coroutine);\nand <code>\"dead\"</code> if the coroutine has finished its body function,\nor if it has stopped with an error.\n\n\n\n\n<p>\n<hr><h3><a name=\"pdf-coroutine.wrap\"><code>coroutine.wrap (f)</code></a></h3>\n\n\n<p>\nCreates a new coroutine, with body <code>f</code>.\n<code>f</code> must be a Lua function.\nReturns a function that resumes the coroutine each time it is called.\nAny arguments passed to the function behave as the\nextra arguments to <code>resume</code>.\nReturns the same values returned by <code>resume</code>,\nexcept the first boolean.\nIn case of error, propagates the error.\n\n\n\n\n<p>\n<hr><h3><a name=\"pdf-coroutine.yield\"><code>coroutine.yield (&middot;&middot;&middot;)</code></a></h3>\n\n\n<p>\nSuspends the execution of the calling coroutine.\nThe coroutine cannot be running a primitive implemented in\nC, a metamethod, or an iterator.\nAny arguments to <code>yield</code> are passed as extra results to <code>resume</code>.\n\n\n\n\n\n\n\n<h2>5.4 - <a name=\"5.4\">String Manipulation</a></h2>\n\n<p>\nThis library provides generic functions for string manipulation,\nsuch as finding and extracting substrings, and pattern matching.\nWhen indexing a string in Lua, the first character is at position&nbsp;1\n(not at&nbsp;0, as in C).\nIndices are allowed to be negative and are interpreted as indexing backwards,\nfrom the end of the string.\nThus, the last character is at position -1, and so on.\n\n\n<p>\nThe string library provides all its functions inside the table\n<a name=\"pdf-string\"><code>string</code></a>.\nIt also sets a metatable for strings\nwhere the <code>__index</code> field points to the <code>string</code> table.\nTherefore, you can use the string functions in object-oriented style.\nFor instance, <code>string.byte(s, i)</code>\ncan be written as <code>s:byte(i)</code>.\n\n\n<p>\nThe string library assumes one-byte character encodings.\n\n\n<p>\n<hr><h3><a name=\"pdf-string.byte\"><code>string.byte (s [, i [, j]])</code></a></h3>\nReturns the internal numerical codes of the characters <code>s[i]</code>,\n<code>s[i+1]</code>, &middot;&middot;&middot;, <code>s[j]</code>.\nThe default value for <code>i</code> is&nbsp;1;\nthe default value for <code>j</code> is&nbsp;<code>i</code>.\n\n\n<p>\nNote that numerical codes are not necessarily portable across platforms.\n\n\n\n\n<p>\n<hr><h3><a name=\"pdf-string.char\"><code>string.char (&middot;&middot;&middot;)</code></a></h3>\nReceives zero or more integers.\nReturns a string with length equal to the number of arguments,\nin which each character has the internal numerical code equal\nto its corresponding argument.\n\n\n<p>\nNote that numerical codes are not necessarily portable across platforms.\n\n\n\n<p>\n<hr><h3><a name=\"pdf-string.find\"><code>string.find (s, pattern [, init [, plain]])</code></a></h3>\nLooks for the first match of\n<code>pattern</code> in the string <code>s</code>.\nIf it finds a match, then <code>find</code> returns the indices of&nbsp;<code>s</code>\nwhere this occurrence starts and ends;\notherwise, it returns <b>nil</b>.\nA third, optional numerical argument <code>init</code> specifies\nwhere to start the search;\nits default value is&nbsp;1 and can be negative.\nA value of <b>true</b> as a fourth, optional argument <code>plain</code>\nturns off the pattern matching facilities,\nso the function does a plain \"find substring\" operation,\nwith no characters in <code>pattern</code> being considered \"magic\".\nNote that if <code>plain</code> is given, then <code>init</code> must be given as well.\n\n\n<p>\nIf the pattern has captures,\nthen in a successful match\nthe captured values are also returned,\nafter the two indices.\n\n\n\n\n<p>\n<hr><h3><a name=\"pdf-string.format\"><code>string.format (formatstring, &middot;&middot;&middot;)</code></a></h3>\nReturns a formatted version of its variable number of arguments\nfollowing the description given in its first argument (which must be a string).\nThe format string follows the same rules as the <code>printf</code> family of\nstandard C&nbsp;functions.\nThe only differences are that the options/modifiers\n<code>*</code>, <code>l</code>, <code>L</code>, <code>n</code>, <code>p</code>,\nand <code>h</code> are not supported\nand that there is an extra option, <code>q</code>.\nThe <code>q</code> option formats a string in a form suitable to be safely read\nback by the Lua interpreter:\nthe string is written between double quotes,\nand all double quotes, newlines, embedded zeros,\nand backslashes in the string\nare correctly escaped when written.\nFor instance, the call\n\n<pre>\n     string.format('%q', 'a string with \"quotes\" and \\n new line')\n</pre><p>\nwill produce the string:\n\n<pre>\n     \"a string with \\\"quotes\\\" and \\\n      new line\"\n</pre>\n\n<p>\nThe options <code>c</code>, <code>d</code>, <code>E</code>, <code>e</code>, <code>f</code>,\n<code>g</code>, <code>G</code>, <code>i</code>, <code>o</code>, <code>u</code>, <code>X</code>, and <code>x</code> all\nexpect a number as argument,\nwhereas <code>q</code> and <code>s</code> expect a string.\n\n\n<p>\nThis function does not accept string values\ncontaining embedded zeros,\nexcept as arguments to the <code>q</code> option.\n\n\n\n\n<p>\n<hr><h3><a name=\"pdf-string.gmatch\"><code>string.gmatch (s, pattern)</code></a></h3>\nReturns an iterator function that,\neach time it is called,\nreturns the next captures from <code>pattern</code> over string <code>s</code>.\nIf <code>pattern</code> specifies no captures,\nthen the whole match is produced in each call.\n\n\n<p>\nAs an example, the following loop\n\n<pre>\n     s = \"hello world from Lua\"\n     for w in string.gmatch(s, \"%a+\") do\n       print(w)\n     end\n</pre><p>\nwill iterate over all the words from string <code>s</code>,\nprinting one per line.\nThe next example collects all pairs <code>key=value</code> from the\ngiven string into a table:\n\n<pre>\n     t = {}\n     s = \"from=world, to=Lua\"\n     for k, v in string.gmatch(s, \"(%w+)=(%w+)\") do\n       t[k] = v\n     end\n</pre>\n\n<p>\nFor this function, a '<code>^</code>' at the start of a pattern does not\nwork as an anchor, as this would prevent the iteration.\n\n\n\n\n<p>\n<hr><h3><a name=\"pdf-string.gsub\"><code>string.gsub (s, pattern, repl [, n])</code></a></h3>\nReturns a copy of <code>s</code>\nin which all (or the first <code>n</code>, if given)\noccurrences of the <code>pattern</code> have been\nreplaced by a replacement string specified by <code>repl</code>,\nwhich can be a string, a table, or a function.\n<code>gsub</code> also returns, as its second value,\nthe total number of matches that occurred.\n\n\n<p>\nIf <code>repl</code> is a string, then its value is used for replacement.\nThe character&nbsp;<code>%</code> works as an escape character:\nany sequence in <code>repl</code> of the form <code>%<em>n</em></code>,\nwith <em>n</em> between 1 and 9,\nstands for the value of the <em>n</em>-th captured substring (see below).\nThe sequence <code>%0</code> stands for the whole match.\nThe sequence <code>%%</code> stands for a single&nbsp;<code>%</code>.\n\n\n<p>\nIf <code>repl</code> is a table, then the table is queried for every match,\nusing the first capture as the key;\nif the pattern specifies no captures,\nthen the whole match is used as the key.\n\n\n<p>\nIf <code>repl</code> is a function, then this function is called every time a\nmatch occurs, with all captured substrings passed as arguments,\nin order;\nif the pattern specifies no captures,\nthen the whole match is passed as a sole argument.\n\n\n<p>\nIf the value returned by the table query or by the function call\nis a string or a number,\nthen it is used as the replacement string;\notherwise, if it is <b>false</b> or <b>nil</b>,\nthen there is no replacement\n(that is, the original match is kept in the string).\n\n\n<p>\nHere are some examples:\n\n<pre>\n     x = string.gsub(\"hello world\", \"(%w+)\", \"%1 %1\")\n     --&gt; x=\"hello hello world world\"\n     \n     x = string.gsub(\"hello world\", \"%w+\", \"%0 %0\", 1)\n     --&gt; x=\"hello hello world\"\n     \n     x = string.gsub(\"hello world from Lua\", \"(%w+)%s*(%w+)\", \"%2 %1\")\n     --&gt; x=\"world hello Lua from\"\n     \n     x = string.gsub(\"home = $HOME, user = $USER\", \"%$(%w+)\", os.getenv)\n     --&gt; x=\"home = /home/roberto, user = roberto\"\n     \n     local t = {name=\"lua\", version=\"5.1\"}\n     x = string.gsub(\"$name-$version.tar.gz\", \"%$(%w+)\", t)\n     --&gt; x=\"lua-5.1.tar.gz\"\n</pre>\n\n\n\n<p>\n<hr><h3><a name=\"pdf-string.len\"><code>string.len (s)</code></a></h3>\nReceives a string and returns its length.\nThe empty string <code>\"\"</code> has length 0.\nEmbedded zeros are counted,\nso <code>\"a\\000bc\\000\"</code> has length 5.\n\n\n\n\n<p>\n<hr><h3><a name=\"pdf-string.lower\"><code>string.lower (s)</code></a></h3>\nReceives a string and returns a copy of this string with all\nuppercase letters changed to lowercase.\nAll other characters are left unchanged.\nThe definition of what an uppercase letter is depends on the current locale.\n\n\n\n\n<p>\n<hr><h3><a name=\"pdf-string.match\"><code>string.match (s, pattern [, init])</code></a></h3>\nLooks for the first <em>match</em> of\n<code>pattern</code> in the string <code>s</code>.\nIf it finds one, then <code>match</code> returns\nthe captures from the pattern;\notherwise it returns <b>nil</b>.\nIf <code>pattern</code> specifies no captures,\nthen the whole match is returned.\nA third, optional numerical argument <code>init</code> specifies\nwhere to start the search;\nits default value is&nbsp;1 and can be negative.\n\n\n\n\n<p>\n<hr><h3><a name=\"pdf-string.rep\"><code>string.rep (s, n)</code></a></h3>\nReturns a string that is the concatenation of <code>n</code> copies of\nthe string <code>s</code>.\n\n\n\n\n<p>\n<hr><h3><a name=\"pdf-string.reverse\"><code>string.reverse (s)</code></a></h3>\nReturns a string that is the string <code>s</code> reversed.\n\n\n\n\n<p>\n<hr><h3><a name=\"pdf-string.sub\"><code>string.sub (s, i [, j])</code></a></h3>\nReturns the substring of <code>s</code> that\nstarts at <code>i</code>  and continues until <code>j</code>;\n<code>i</code> and <code>j</code> can be negative.\nIf <code>j</code> is absent, then it is assumed to be equal to -1\n(which is the same as the string length).\nIn particular,\nthe call <code>string.sub(s,1,j)</code> returns a prefix of <code>s</code>\nwith length <code>j</code>,\nand <code>string.sub(s, -i)</code> returns a suffix of <code>s</code>\nwith length <code>i</code>.\n\n\n\n\n<p>\n<hr><h3><a name=\"pdf-string.upper\"><code>string.upper (s)</code></a></h3>\nReceives a string and returns a copy of this string with all\nlowercase letters changed to uppercase.\nAll other characters are left unchanged.\nThe definition of what a lowercase letter is depends on the current locale.\n\n\n\n<h3>5.4.1 - <a name=\"5.4.1\">Patterns</a></h3>\n\n\n<h4>Character Class:</h4><p>\nA <em>character class</em> is used to represent a set of characters.\nThe following combinations are allowed in describing a character class:\n\n<ul>\n\n<li><b><em>x</em>:</b>\n(where <em>x</em> is not one of the <em>magic characters</em>\n<code>^$()%.[]*+-?</code>)\nrepresents the character <em>x</em> itself.\n</li>\n\n<li><b><code>.</code>:</b> (a dot) represents all characters.</li>\n\n<li><b><code>%a</code>:</b> represents all letters.</li>\n\n<li><b><code>%c</code>:</b> represents all control characters.</li>\n\n<li><b><code>%d</code>:</b> represents all digits.</li>\n\n<li><b><code>%l</code>:</b> represents all lowercase letters.</li>\n\n<li><b><code>%p</code>:</b> represents all punctuation characters.</li>\n\n<li><b><code>%s</code>:</b> represents all space characters.</li>\n\n<li><b><code>%u</code>:</b> represents all uppercase letters.</li>\n\n<li><b><code>%w</code>:</b> represents all alphanumeric characters.</li>\n\n<li><b><code>%x</code>:</b> represents all hexadecimal digits.</li>\n\n<li><b><code>%z</code>:</b> represents the character with representation 0.</li>\n\n<li><b><code>%<em>x</em></code>:</b> (where <em>x</em> is any non-alphanumeric character)\nrepresents the character <em>x</em>.\nThis is the standard way to escape the magic characters.\nAny punctuation character (even the non magic)\ncan be preceded by a '<code>%</code>'\nwhen used to represent itself in a pattern.\n</li>\n\n<li><b><code>[<em>set</em>]</code>:</b>\nrepresents the class which is the union of all\ncharacters in <em>set</em>.\nA range of characters can be specified by\nseparating the end characters of the range with a '<code>-</code>'.\nAll classes <code>%</code><em>x</em> described above can also be used as\ncomponents in <em>set</em>.\nAll other characters in <em>set</em> represent themselves.\nFor example, <code>[%w_]</code> (or <code>[_%w]</code>)\nrepresents all alphanumeric characters plus the underscore,\n<code>[0-7]</code> represents the octal digits,\nand <code>[0-7%l%-]</code> represents the octal digits plus\nthe lowercase letters plus the '<code>-</code>' character.\n\n\n<p>\nThe interaction between ranges and classes is not defined.\nTherefore, patterns like <code>[%a-z]</code> or <code>[a-%%]</code>\nhave no meaning.\n</li>\n\n<li><b><code>[^<em>set</em>]</code>:</b>\nrepresents the complement of <em>set</em>,\nwhere <em>set</em> is interpreted as above.\n</li>\n\n</ul><p>\nFor all classes represented by single letters (<code>%a</code>, <code>%c</code>, etc.),\nthe corresponding uppercase letter represents the complement of the class.\nFor instance, <code>%S</code> represents all non-space characters.\n\n\n<p>\nThe definitions of letter, space, and other character groups\ndepend on the current locale.\nIn particular, the class <code>[a-z]</code> may not be equivalent to <code>%l</code>.\n\n\n\n\n\n<h4>Pattern Item:</h4><p>\nA <em>pattern item</em> can be\n\n<ul>\n\n<li>\na single character class,\nwhich matches any single character in the class;\n</li>\n\n<li>\na single character class followed by '<code>*</code>',\nwhich matches 0 or more repetitions of characters in the class.\nThese repetition items will always match the longest possible sequence;\n</li>\n\n<li>\na single character class followed by '<code>+</code>',\nwhich matches 1 or more repetitions of characters in the class.\nThese repetition items will always match the longest possible sequence;\n</li>\n\n<li>\na single character class followed by '<code>-</code>',\nwhich also matches 0 or more repetitions of characters in the class.\nUnlike '<code>*</code>',\nthese repetition items will always match the <em>shortest</em> possible sequence;\n</li>\n\n<li>\na single character class followed by '<code>?</code>',\nwhich matches 0 or 1 occurrence of a character in the class;\n</li>\n\n<li>\n<code>%<em>n</em></code>, for <em>n</em> between 1 and 9;\nsuch item matches a substring equal to the <em>n</em>-th captured string\n(see below);\n</li>\n\n<li>\n<code>%b<em>xy</em></code>, where <em>x</em> and <em>y</em> are two distinct characters;\nsuch item matches strings that start with&nbsp;<em>x</em>, end with&nbsp;<em>y</em>,\nand where the <em>x</em> and <em>y</em> are <em>balanced</em>.\nThis means that, if one reads the string from left to right,\ncounting <em>+1</em> for an <em>x</em> and <em>-1</em> for a <em>y</em>,\nthe ending <em>y</em> is the first <em>y</em> where the count reaches 0.\nFor instance, the item <code>%b()</code> matches expressions with\nbalanced parentheses.\n</li>\n\n</ul>\n\n\n\n\n<h4>Pattern:</h4><p>\nA <em>pattern</em> is a sequence of pattern items.\nA '<code>^</code>' at the beginning of a pattern anchors the match at the\nbeginning of the subject string.\nA '<code>$</code>' at the end of a pattern anchors the match at the\nend of the subject string.\nAt other positions,\n'<code>^</code>' and '<code>$</code>' have no special meaning and represent themselves.\n\n\n\n\n\n<h4>Captures:</h4><p>\nA pattern can contain sub-patterns enclosed in parentheses;\nthey describe <em>captures</em>.\nWhen a match succeeds, the substrings of the subject string\nthat match captures are stored (<em>captured</em>) for future use.\nCaptures are numbered according to their left parentheses.\nFor instance, in the pattern <code>\"(a*(.)%w(%s*))\"</code>,\nthe part of the string matching <code>\"a*(.)%w(%s*)\"</code> is\nstored as the first capture (and therefore has number&nbsp;1);\nthe character matching \"<code>.</code>\" is captured with number&nbsp;2,\nand the part matching \"<code>%s*</code>\" has number&nbsp;3.\n\n\n<p>\nAs a special case, the empty capture <code>()</code> captures\nthe current string position (a number).\nFor instance, if we apply the pattern <code>\"()aa()\"</code> on the\nstring <code>\"flaaap\"</code>, there will be two captures: 3&nbsp;and&nbsp;5.\n\n\n<p>\nA pattern cannot contain embedded zeros.  Use <code>%z</code> instead.\n\n\n\n\n\n\n\n\n\n\n\n<h2>5.5 - <a name=\"5.5\">Table Manipulation</a></h2><p>\nThis library provides generic functions for table manipulation.\nIt provides all its functions inside the table <a name=\"pdf-table\"><code>table</code></a>.\n\n\n<p>\nMost functions in the table library assume that the table\nrepresents an array or a list.\nFor these functions, when we talk about the \"length\" of a table\nwe mean the result of the length operator.\n\n\n<p>\n<hr><h3><a name=\"pdf-table.concat\"><code>table.concat (table [, sep [, i [, j]]])</code></a></h3>\nGiven an array where all elements are strings or numbers,\nreturns <code>table[i]..sep..table[i+1] &middot;&middot;&middot; sep..table[j]</code>.\nThe default value for <code>sep</code> is the empty string,\nthe default for <code>i</code> is 1,\nand the default for <code>j</code> is the length of the table.\nIf <code>i</code> is greater than <code>j</code>, returns the empty string.\n\n\n\n\n<p>\n<hr><h3><a name=\"pdf-table.insert\"><code>table.insert (table, [pos,] value)</code></a></h3>\n\n\n<p>\nInserts element <code>value</code> at position <code>pos</code> in <code>table</code>,\nshifting up other elements to open space, if necessary.\nThe default value for <code>pos</code> is <code>n+1</code>,\nwhere <code>n</code> is the length of the table (see <a href=\"#2.5.5\">&sect;2.5.5</a>),\nso that a call <code>table.insert(t,x)</code> inserts <code>x</code> at the end\nof table <code>t</code>.\n\n\n\n\n<p>\n<hr><h3><a name=\"pdf-table.maxn\"><code>table.maxn (table)</code></a></h3>\n\n\n<p>\nReturns the largest positive numerical index of the given table,\nor zero if the table has no positive numerical indices.\n(To do its job this function does a linear traversal of\nthe whole table.) \n\n\n\n\n<p>\n<hr><h3><a name=\"pdf-table.remove\"><code>table.remove (table [, pos])</code></a></h3>\n\n\n<p>\nRemoves from <code>table</code> the element at position <code>pos</code>,\nshifting down other elements to close the space, if necessary.\nReturns the value of the removed element.\nThe default value for <code>pos</code> is <code>n</code>,\nwhere <code>n</code> is the length of the table,\nso that a call <code>table.remove(t)</code> removes the last element\nof table <code>t</code>.\n\n\n\n\n<p>\n<hr><h3><a name=\"pdf-table.sort\"><code>table.sort (table [, comp])</code></a></h3>\nSorts table elements in a given order, <em>in-place</em>,\nfrom <code>table[1]</code> to <code>table[n]</code>,\nwhere <code>n</code> is the length of the table.\nIf <code>comp</code> is given,\nthen it must be a function that receives two table elements,\nand returns true\nwhen the first is less than the second\n(so that <code>not comp(a[i+1],a[i])</code> will be true after the sort).\nIf <code>comp</code> is not given,\nthen the standard Lua operator <code>&lt;</code> is used instead.\n\n\n<p>\nThe sort algorithm is not stable;\nthat is, elements considered equal by the given order\nmay have their relative positions changed by the sort.\n\n\n\n\n\n\n\n<h2>5.6 - <a name=\"5.6\">Mathematical Functions</a></h2>\n\n<p>\nThis library is an interface to the standard C&nbsp;math library.\nIt provides all its functions inside the table <a name=\"pdf-math\"><code>math</code></a>.\n\n\n<p>\n<hr><h3><a name=\"pdf-math.abs\"><code>math.abs (x)</code></a></h3>\n\n\n<p>\nReturns the absolute value of <code>x</code>.\n\n\n\n\n<p>\n<hr><h3><a name=\"pdf-math.acos\"><code>math.acos (x)</code></a></h3>\n\n\n<p>\nReturns the arc cosine of <code>x</code> (in radians).\n\n\n\n\n<p>\n<hr><h3><a name=\"pdf-math.asin\"><code>math.asin (x)</code></a></h3>\n\n\n<p>\nReturns the arc sine of <code>x</code> (in radians).\n\n\n\n\n<p>\n<hr><h3><a name=\"pdf-math.atan\"><code>math.atan (x)</code></a></h3>\n\n\n<p>\nReturns the arc tangent of <code>x</code> (in radians).\n\n\n\n\n<p>\n<hr><h3><a name=\"pdf-math.atan2\"><code>math.atan2 (y, x)</code></a></h3>\n\n\n<p>\nReturns the arc tangent of <code>y/x</code> (in radians),\nbut uses the signs of both parameters to find the\nquadrant of the result.\n(It also handles correctly the case of <code>x</code> being zero.)\n\n\n\n\n<p>\n<hr><h3><a name=\"pdf-math.ceil\"><code>math.ceil (x)</code></a></h3>\n\n\n<p>\nReturns the smallest integer larger than or equal to <code>x</code>.\n\n\n\n\n<p>\n<hr><h3><a name=\"pdf-math.cos\"><code>math.cos (x)</code></a></h3>\n\n\n<p>\nReturns the cosine of <code>x</code> (assumed to be in radians).\n\n\n\n\n<p>\n<hr><h3><a name=\"pdf-math.cosh\"><code>math.cosh (x)</code></a></h3>\n\n\n<p>\nReturns the hyperbolic cosine of <code>x</code>.\n\n\n\n\n<p>\n<hr><h3><a name=\"pdf-math.deg\"><code>math.deg (x)</code></a></h3>\n\n\n<p>\nReturns the angle <code>x</code> (given in radians) in degrees.\n\n\n\n\n<p>\n<hr><h3><a name=\"pdf-math.exp\"><code>math.exp (x)</code></a></h3>\n\n\n<p>\nReturns the value <em>e<sup>x</sup></em>.\n\n\n\n\n<p>\n<hr><h3><a name=\"pdf-math.floor\"><code>math.floor (x)</code></a></h3>\n\n\n<p>\nReturns the largest integer smaller than or equal to <code>x</code>.\n\n\n\n\n<p>\n<hr><h3><a name=\"pdf-math.fmod\"><code>math.fmod (x, y)</code></a></h3>\n\n\n<p>\nReturns the remainder of the division of <code>x</code> by <code>y</code>\nthat rounds the quotient towards zero.\n\n\n\n\n<p>\n<hr><h3><a name=\"pdf-math.frexp\"><code>math.frexp (x)</code></a></h3>\n\n\n<p>\nReturns <code>m</code> and <code>e</code> such that <em>x = m2<sup>e</sup></em>,\n<code>e</code> is an integer and the absolute value of <code>m</code> is\nin the range <em>[0.5, 1)</em>\n(or zero when <code>x</code> is zero).\n\n\n\n\n<p>\n<hr><h3><a name=\"pdf-math.huge\"><code>math.huge</code></a></h3>\n\n\n<p>\nThe value <code>HUGE_VAL</code>,\na value larger than or equal to any other numerical value.\n\n\n\n\n<p>\n<hr><h3><a name=\"pdf-math.ldexp\"><code>math.ldexp (m, e)</code></a></h3>\n\n\n<p>\nReturns <em>m2<sup>e</sup></em> (<code>e</code> should be an integer).\n\n\n\n\n<p>\n<hr><h3><a name=\"pdf-math.log\"><code>math.log (x)</code></a></h3>\n\n\n<p>\nReturns the natural logarithm of <code>x</code>.\n\n\n\n\n<p>\n<hr><h3><a name=\"pdf-math.log10\"><code>math.log10 (x)</code></a></h3>\n\n\n<p>\nReturns the base-10 logarithm of <code>x</code>.\n\n\n\n\n<p>\n<hr><h3><a name=\"pdf-math.max\"><code>math.max (x, &middot;&middot;&middot;)</code></a></h3>\n\n\n<p>\nReturns the maximum value among its arguments.\n\n\n\n\n<p>\n<hr><h3><a name=\"pdf-math.min\"><code>math.min (x, &middot;&middot;&middot;)</code></a></h3>\n\n\n<p>\nReturns the minimum value among its arguments.\n\n\n\n\n<p>\n<hr><h3><a name=\"pdf-math.modf\"><code>math.modf (x)</code></a></h3>\n\n\n<p>\nReturns two numbers,\nthe integral part of <code>x</code> and the fractional part of <code>x</code>.\n\n\n\n\n<p>\n<hr><h3><a name=\"pdf-math.pi\"><code>math.pi</code></a></h3>\n\n\n<p>\nThe value of <em>pi</em>.\n\n\n\n\n<p>\n<hr><h3><a name=\"pdf-math.pow\"><code>math.pow (x, y)</code></a></h3>\n\n\n<p>\nReturns <em>x<sup>y</sup></em>.\n(You can also use the expression <code>x^y</code> to compute this value.)\n\n\n\n\n<p>\n<hr><h3><a name=\"pdf-math.rad\"><code>math.rad (x)</code></a></h3>\n\n\n<p>\nReturns the angle <code>x</code> (given in degrees) in radians.\n\n\n\n\n<p>\n<hr><h3><a name=\"pdf-math.random\"><code>math.random ([m [, n]])</code></a></h3>\n\n\n<p>\nThis function is an interface to the simple\npseudo-random generator function <code>rand</code> provided by ANSI&nbsp;C.\n(No guarantees can be given for its statistical properties.)\n\n\n<p>\nWhen called without arguments,\nreturns a uniform pseudo-random real number\nin the range <em>[0,1)</em>.  \nWhen called with an integer number <code>m</code>,\n<code>math.random</code> returns\na uniform pseudo-random integer in the range <em>[1, m]</em>.\nWhen called with two integer numbers <code>m</code> and <code>n</code>,\n<code>math.random</code> returns a uniform pseudo-random\ninteger in the range <em>[m, n]</em>.\n\n\n\n\n<p>\n<hr><h3><a name=\"pdf-math.randomseed\"><code>math.randomseed (x)</code></a></h3>\n\n\n<p>\nSets <code>x</code> as the \"seed\"\nfor the pseudo-random generator:\nequal seeds produce equal sequences of numbers.\n\n\n\n\n<p>\n<hr><h3><a name=\"pdf-math.sin\"><code>math.sin (x)</code></a></h3>\n\n\n<p>\nReturns the sine of <code>x</code> (assumed to be in radians).\n\n\n\n\n<p>\n<hr><h3><a name=\"pdf-math.sinh\"><code>math.sinh (x)</code></a></h3>\n\n\n<p>\nReturns the hyperbolic sine of <code>x</code>.\n\n\n\n\n<p>\n<hr><h3><a name=\"pdf-math.sqrt\"><code>math.sqrt (x)</code></a></h3>\n\n\n<p>\nReturns the square root of <code>x</code>.\n(You can also use the expression <code>x^0.5</code> to compute this value.)\n\n\n\n\n<p>\n<hr><h3><a name=\"pdf-math.tan\"><code>math.tan (x)</code></a></h3>\n\n\n<p>\nReturns the tangent of <code>x</code> (assumed to be in radians).\n\n\n\n\n<p>\n<hr><h3><a name=\"pdf-math.tanh\"><code>math.tanh (x)</code></a></h3>\n\n\n<p>\nReturns the hyperbolic tangent of <code>x</code>.\n\n\n\n\n\n\n\n<h2>5.7 - <a name=\"5.7\">File Input and Output Facilities</a></h2>\n\n<div class='teliva'>\nWhile Teliva supports the standard Lua primitives for managing files via\nhandles, idiomatic Teliva uses some slightly different primitives that use a\nsubtly different file object (name tbd).\n\n<p>\n<hr><h3><a name=\"pdf-start_reading\"><code>start_reading (fs, filename)</code></a></h3>\n\n<p>\nThis function opens a file exclusively for reading, and returns an object (NOT\na file handle as in Lua) or <b>nil</b> on error. If the returned object is\nstored in variable <code>f</code>, <code>f.read(format)</code> will read from\nthe file. Legal values for <code>format</code> are identical to\n<a href='#pdf-file:read'><code>file:read(format)</code></a>.\n\n<p>\n(The <code>fs</code> parameter is currently unused. It will be used to pass in\nfake file systems for tests.)\n\n\n<p>\n<hr><h3><a name=\"pdf-start_writing\"><code>start_writing(fs, filename)</code></a></h3>\n\n<p>\nThis function opens a file exclusively for writing, and returns an object (NOT\na file handle as in Lua) or <b>nil</b> on error. If the result is stored in\nvariable <code>f</code>, <code>f.write(x)</code> will write <code>x</code> to\nthe file. <code>f.close()</code> will persist the changes and make them\nexternally visible. All changes will be hidden until <code>f.close()</code>.\n\n<p>\n(The <code>fs</code> parameter is currently unused. It will be used to pass in\nfake file systems for tests.)\n\n\n<hr>\nThe rest of this section describes Lua's standard primitives for File I/O.\n</div>\n\n\n<p>\nUnless otherwise stated,\nall File I/O functions return <b>nil</b> on failure\n(plus an error message as a second result and\na system-dependent error code as a third result)\nand some value different from <b>nil</b> on success.\n\n\n<p>\n<hr><h3><a name=\"pdf-io.close\"><code>io.close (file)</code></a></h3>\n\n\n<p>\nEquivalent to <code>file:close()</code>.\n\n\n\n<p>\n<hr><h3><a name=\"pdf-io.open\"><code>io.open (filename [, mode])</code></a></h3>\n\n\n<p>\nThis function opens a file,\nin the mode specified in the string <code>mode</code>.\nIt returns a new file handle,\nor, in case of errors, <b>nil</b> plus an error message.\n\n\n<p>\nThe <code>mode</code> string can be any of the following:\n\n<ul>\n<li><b>\"r\":</b> read mode (the default);</li>\n<li><b>\"w\":</b> write mode;</li>\n<li><b>\"a\":</b> append mode;</li>\n<li><b>\"r+\":</b> update mode, all previous data is preserved;</li>\n<li><b>\"w+\":</b> update mode, all previous data is erased;</li>\n<li><b>\"a+\":</b> append update mode, previous data is preserved,\n  writing is only allowed at the end of file.</li>\n</ul><p>\nThe <code>mode</code> string can also have a '<code>b</code>' at the end,\nwhich is needed in some systems to open the file in binary mode.\nThis string is exactly what is used in the\nstandard&nbsp;C function <code>fopen</code>.\n\n<div class='teliva'>\n<em>This function is sandboxed. It may fail if the computer owner's\npermissions disallow it.</em>\n</div>\n\n\n\n<p>\n<hr><h3><a name=\"pdf-io.tmpfile\"><code>io.tmpfile ()</code></a></h3>\n\n\n<p>\nReturns a handle for a temporary file.\nThis file is opened in update mode\nand it is automatically removed when the program ends.\n\n\n\n\n<p>\n<hr><h3><a name=\"pdf-io.type\"><code>io.type (obj)</code></a></h3>\n\n\n<p>\nChecks whether <code>obj</code> is a valid file handle.\nReturns the string <code>\"file\"</code> if <code>obj</code> is an open file handle,\n<code>\"closed file\"</code> if <code>obj</code> is a closed file handle,\nor <b>nil</b> if <code>obj</code> is not a file handle.\n\n\n\n\n<p>\n<hr><h3><a name=\"pdf-file:close\"><code>file:close ()</code></a></h3>\n\n\n<p>\nCloses <code>file</code>.\nNote that files are automatically closed when\ntheir handles are garbage collected,\nbut that takes an unpredictable amount of time to happen.\n\n\n\n\n<p>\n<hr><h3><a name=\"pdf-file:flush\"><code>file:flush ()</code></a></h3>\n\n\n<p>\nSaves any written data to <code>file</code>.\n\n\n\n\n<p>\n<hr><h3><a name=\"pdf-file:lines\"><code>file:lines ()</code></a></h3>\n\n\n<p>\nReturns an iterator function that,\neach time it is called,\nreturns a new line from the file.\nTherefore, the construction\n\n<pre>\n     for line in file:lines() do <em>body</em> end\n</pre><p>\nwill iterate over all lines of the file.\n(Unlike <a href=\"#pdf-io.lines\"><code>io.lines</code></a>, this function does not close the file\nwhen the loop ends.)\n\n\n\n\n<p>\n<hr><h3><a name=\"pdf-file:read\"><code>file:read (&middot;&middot;&middot;)</code></a></h3>\n\n\n<p>\nReads the file <code>file</code>,\naccording to the given formats, which specify what to read.\nFor each format,\nthe function returns a string (or a number) with the characters read,\nor <b>nil</b> if it cannot read data with the specified format.\nWhen called without formats,\nit uses a default format that reads the entire next line\n(see below).\n\n\n<p>\nThe available formats are\n\n<ul>\n\n<li><b>\"*n\":</b>\nreads a number;\nthis is the only format that returns a number instead of a string.\n</li>\n\n<li><b>\"*a\":</b>\nreads the whole file, starting at the current position.\nOn end of file, it returns the empty string.\n</li>\n\n<li><b>\"*l\":</b>\nreads the next line (skipping the end of line),\nreturning <b>nil</b> on end of file.\nThis is the default format.\n</li>\n\n<li><b><em>number</em>:</b>\nreads a string with up to this number of characters,\nreturning <b>nil</b> on end of file.\nIf number is zero,\nit reads nothing and returns an empty string,\nor <b>nil</b> on end of file.\n</li>\n\n</ul>\n\n\n\n<p>\n<hr><h3><a name=\"pdf-file:seek\"><code>file:seek ([whence] [, offset])</code></a></h3>\n\n\n<p>\nSets and gets the file position,\nmeasured from the beginning of the file,\nto the position given by <code>offset</code> plus a base\nspecified by the string <code>whence</code>, as follows:\n\n<ul>\n<li><b>\"set\":</b> base is position 0 (beginning of the file);</li>\n<li><b>\"cur\":</b> base is current position;</li>\n<li><b>\"end\":</b> base is end of file;</li>\n</ul><p>\nIn case of success, function <code>seek</code> returns the final file position,\nmeasured in bytes from the beginning of the file.\nIf this function fails, it returns <b>nil</b>,\nplus a string describing the error.\n\n\n<p>\nThe default value for <code>whence</code> is <code>\"cur\"</code>,\nand for <code>offset</code> is 0.\nTherefore, the call <code>file:seek()</code> returns the current\nfile position, without changing it;\nthe call <code>file:seek(\"set\")</code> sets the position to the\nbeginning of the file (and returns 0);\nand the call <code>file:seek(\"end\")</code> sets the position to the\nend of the file, and returns its size.\n\n\n\n\n<p>\n<hr><h3><a name=\"pdf-file:setvbuf\"><code>file:setvbuf (mode [, size])</code></a></h3>\n\n\n<p>\nSets the buffering mode for an output file.\nThere are three available modes:\n\n<ul>\n\n<li><b>\"no\":</b>\nno buffering; the result of any output operation appears immediately.\n</li>\n\n<li><b>\"full\":</b>\nfull buffering; output operation is performed only\nwhen the buffer is full (or when you explicitly <code>flush</code> the file\n(see <a href=\"#pdf-io.flush\"><code>io.flush</code></a>)).\n</li>\n\n<li><b>\"line\":</b>\nline buffering; output is buffered until a newline is output\nor there is any input from some special files\n(such as a terminal device).\n</li>\n\n</ul><p>\nFor the last two cases, <code>size</code>\nspecifies the size of the buffer, in bytes.\nThe default is an appropriate size.\n\n\n\n\n<p>\n<hr><h3><a name=\"pdf-file:write\"><code>file:write (&middot;&middot;&middot;)</code></a></h3>\n\n\n<p>\nWrites the value of each of its arguments to\nthe <code>file</code>.\nThe arguments must be strings or numbers.\nTo write other values,\nuse <a href=\"#pdf-tostring\"><code>tostring</code></a> or <a href=\"#pdf-string.format\"><code>string.format</code></a> before <code>write</code>.\n\n\n\n\n\n\n\n<h2>5.8 - <a name=\"5.8\">Operating System Facilities</a></h2>\n\n<p>\nThis library is implemented through table <a name=\"pdf-os\"><code>os</code></a>.\n\n\n<p>\n<hr><h3><a name=\"pdf-os.clock\"><code>os.clock ()</code></a></h3>\n\n\n<p>\nReturns an approximation of the amount in seconds of CPU time\nused by the program.\n\n\n\n\n<p>\n<hr><h3><a name=\"pdf-os.date\"><code>os.date ([format [, time]])</code></a></h3>\n\n\n<p>\nReturns a string or a table containing date and time,\nformatted according to the given string <code>format</code>.\n\n\n<p>\nIf the <code>time</code> argument is present,\nthis is the time to be formatted\n(see the <a href=\"#pdf-os.time\"><code>os.time</code></a> function for a description of this value).\nOtherwise, <code>date</code> formats the current time.\n\n\n<p>\nIf <code>format</code> starts with '<code>!</code>',\nthen the date is formatted in Coordinated Universal Time.\nAfter this optional character,\nif <code>format</code> is the string \"<code>*t</code>\",\nthen <code>date</code> returns a table with the following fields:\n<code>year</code> (four digits), <code>month</code> (1--12), <code>day</code> (1--31),\n<code>hour</code> (0--23), <code>min</code> (0--59), <code>sec</code> (0--61),\n<code>wday</code> (weekday, Sunday is&nbsp;1),\n<code>yday</code> (day of the year),\nand <code>isdst</code> (daylight saving flag, a boolean).\n\n\n<p>\nIf <code>format</code> is not \"<code>*t</code>\",\nthen <code>date</code> returns the date as a string,\nformatted according to the same rules as the C&nbsp;function <code>strftime</code>.\n\n\n<p>\nWhen called without arguments,\n<code>date</code> returns a reasonable date and time representation that depends on\nthe host system and on the current locale\n(that is, <code>os.date()</code> is equivalent to <code>os.date(\"%c\")</code>).\n\n\n\n\n<p>\n<hr><h3><a name=\"pdf-os.difftime\"><code>os.difftime (t2, t1)</code></a></h3>\n\n\n<p>\nReturns the number of seconds from time <code>t1</code> to time <code>t2</code>.\nIn POSIX, Windows, and some other systems,\nthis value is exactly <code>t2</code><em>-</em><code>t1</code>.\n\n\n\n\n<p>\n<hr><h3><a name=\"pdf-os.exit\"><code>os.exit ([code])</code></a></h3>\n\n\n<p>\nCalls the C&nbsp;function <code>exit</code>,\nwith an optional <code>code</code>,\nto terminate the host program.\nThe default value for <code>code</code> is the success code.\n\n\n\n\n<p>\n<hr><h3><a name=\"pdf-os.remove\"><code>os.remove (filename)</code></a></h3>\n\n\n<p>\nDeletes the file or directory with the given name.\nDirectories must be empty to be removed.\nIf this function fails, it returns <b>nil</b>,\nplus a string describing the error.\n\n\n\n\n<p>\n<hr><h3><a name=\"pdf-os.rename\"><code>os.rename (oldname, newname)</code></a></h3>\n\n\n<p>\nRenames file or directory named <code>oldname</code> to <code>newname</code>.\nIf this function fails, it returns <b>nil</b>,\nplus a string describing the error.\n\n<div class='teliva'>\n<em>This function is sandboxed. It may fail if the computer owner's\npermissions disallow it.</em>\n</div>\n\n\n\n<p>\n<hr><h3><a name=\"pdf-os.setlocale\"><code>os.setlocale (locale [, category])</code></a></h3>\n\n\n<p>\nSets the current locale of the program.\n<code>locale</code> is a string specifying a locale;\n<code>category</code> is an optional string describing which category to change:\n<code>\"all\"</code>, <code>\"collate\"</code>, <code>\"ctype\"</code>,\n<code>\"monetary\"</code>, <code>\"numeric\"</code>, or <code>\"time\"</code>;\nthe default category is <code>\"all\"</code>.\nThe function returns the name of the new locale,\nor <b>nil</b> if the request cannot be honored.\n\n\n<p>\nIf <code>locale</code> is the empty string,\nthe current locale is set to an implementation-defined native locale.\nIf <code>locale</code> is the string \"<code>C</code>\",\nthe current locale is set to the standard C locale.\n\n\n<p>\nWhen called with <b>nil</b> as the first argument,\nthis function only returns the name of the current locale\nfor the given category.\n\n\n\n\n<p>\n<hr><h3><a name=\"pdf-os.time\"><code>os.time ([table])</code></a></h3>\n\n\n<p>\nReturns the current time when called without arguments,\nor a time representing the date and time specified by the given table.\nThis table must have fields <code>year</code>, <code>month</code>, and <code>day</code>,\nand may have fields <code>hour</code>, <code>min</code>, <code>sec</code>, and <code>isdst</code>\n(for a description of these fields, see the <a href=\"#pdf-os.date\"><code>os.date</code></a> function).\n\n\n<p>\nThe returned value is a number, whose meaning depends on your system.\nIn POSIX, Windows, and some other systems, this number counts the number\nof seconds since some given start time (the \"epoch\").\nIn other systems, the meaning is not specified,\nand the number returned by <code>time</code> can be used only as an argument to\n<code>date</code> and <code>difftime</code>.\n\n\n\n\n<p>\n<hr><h3><a name=\"pdf-os.tmpname\"><code>os.tmpname ()</code></a></h3>\n\n\n<p>\nReturns a string with a file name that can\nbe used for a temporary file.\nThe file must be explicitly opened before its use\nand explicitly removed when no longer needed.\n\n\n<p>\nOn some systems (POSIX),\nthis function also creates a file with that name,\nto avoid security risks.\n(Someone else might create the file with wrong permissions\nin the time between getting the name and creating the file.)\nYou still have to open the file to use it\nand to remove it (even if you do not use it).\n\n\n<p>\nWhen possible,\nyou may prefer to use <a href=\"#pdf-io.tmpfile\"><code>io.tmpfile</code></a>,\nwhich automatically removes the file when the program ends.\n\n\n\n\n\n\n\n<div class='teliva'>\n<h2>5.10 - <a name=\"5.10\">Curses Window Facilities</a></h2>\n\nTeliva includes curses facilities identical to Lua's <a href='http://lcurses.github.io/lcurses/'>lcurses</a>\nlibrary. As there, the top-level module is called <code>curses</code>. All apps\nstart with the terminal window initialized using\n<a href=\"#pdf-curses.initscr\"><code>curses.initscr</code></a>. Look at the\nsample apps for example usage.\n\n\n<p>\n<hr><h3><a name=\"pdf-curses.initscr\"><code>curses.initscr ()</code></a></h3>\n\n\n<p>\nInitializes the current terminal to stop scrolling and enable moving the\ncursor. You shouldn't need to ever call this from Teliva; it's always called\nfor you before an app is loaded.\n\n\n<p>\n<hr><h3><a name=\"pdf-curses.stdscr\"><code>curses.stdscr ()</code></a></h3>\n\n\n<p>\nReturns a <em>window</em> object for the current terminal. Most curses\noperations require a window. Windows are an app's gateway to both print to\nscreen and read keys from keyboard. Teliva's template.tlv for new applications\nsaves the result in a global called <code>Window</code>, so you should be able\nto avoid calling <code>stdscr</code> directly most of the time.\n\n<p>\nCurses supports multiple and nested windows. They haven't been tried\nyet in the context of Teliva, but they're expected to work. Please report your\nexperience if you try them out.\n\n\n<p>\n<hr><h3><a name=\"pdf-window\"><code>window {}</code></a></h3>\n\n\n<p>\nCreates a fake window suitable for passing around in tests. The table passed\nin should have two keys: a <a href='#pdf-kbd'><code>kbd</code></a>\ncontaining a keyboard, and a <a href='#pdf-scr'><code>scr</code></a>\ncontaining a screen.\n\n<p>\nThis helper is implemented in template.tlv, so new apps should pick it up from\nthere.\n\n\n<p>\n<hr><h3><a name=\"pdf-kbd\"><code>kbd (str)</code></a></h3>\n\n\n<p>\nCreates a fake keyboard suitable for passing into\n<a href='#pdf-window'><code>window</code></a> with the characters in\n<code>str</code> already &ldquo;typed in&rdquo;.\n\n\n\n<p>\nThis helper is implemented in template.tlv, so new apps should pick it up from\nthere.\n\n\n<p>\n<hr><h3><a name=\"pdf-scr\"><code>scr {}</code></a></h3>\n\n\n<p>\nCreates a fake screen suitable for passing into\n<a href='#pdf-window'><code>window</code></a>. The table passed in should\ncontain two keys: a height <code>h</code> and a width <code>w</code>.\n\n\n<p>\nThis helper is implemented in template.tlv, so new apps should pick it up from\nthere.\n\n\n<p>\n<hr><h3><a name=\"pdf-window:clear\"><code>window:clear ()</code></a></h3>\n\n\n<p>\nClears all prints in <code>window</code>.\n\n\n<p>\n<hr><h3><a name=\"pdf-window:refresh\"><code>window:refresh ()</code></a></h3>\n\n\n<p>\nFlushes all prints to <code>window</code>. Also redraws the Teliva menu.\n\n\n<p>\n<hr><h3><a name=\"pdf-window:addch\"><code>window:addch (c)</code></a></h3>\n\n\n<p>\nPrints character <code>c</code> with\n<a href='https://tldp.org/HOWTO/NCURSES-Programming-HOWTO/attrib.html'>the current attributes</a>\nat the cursor in <code>window</code>. May not be visible until\n<a href=\"#pdf-window:refresh\"><code>window:refresh</code></a> is called.\n\n\n<p>\n<hr><h3><a name=\"pdf-window:mvaddch\"><code>window:mvaddch (y, x, c)</code></a></h3>\n\n\n<p>\nMoves <code>window</code>'s cursor to (<code>x</code>, <code>y</code>) before\nprinting character <code>c</code> to it with\n<a href='https://tldp.org/HOWTO/NCURSES-Programming-HOWTO/attrib.html'>the current attributes</a>.\nMay not be visible until <a href=\"#pdf-window:refresh\"><code>window:refresh</code></a>\nis called.\n\n\n<p>\n<hr><h3><a name=\"pdf-window:addstr\"><code>window:addstr (str)</code></a></h3>\n\n\n<p>\nPrints string <code>str</code> with\n<a href='https://tldp.org/HOWTO/NCURSES-Programming-HOWTO/attrib.html'>the current attributes</a>\nat the cursor in <code>window</code>. May not be visible until\n<a href=\"#pdf-window:refresh\"><code>window:refresh</code></a> is called.\n\n\n<p>\n<hr><h3><a name=\"pdf-window:mvaddstr\"><code>window:mvaddstr (y, x, str)</code></a></h3>\n\n\n<p>\nMoves <code>window</code>'s cursor to (<code>x</code>, <code>y</code>) before\nprinting string <code>str</code> to it with\n<a href='https://tldp.org/HOWTO/NCURSES-Programming-HOWTO/attrib.html'>the current attributes</a>.\nMay not be visible until <a href=\"#pdf-window:refresh\"><code>window:refresh</code></a> is called.\n\n\n<p>\n<hr><h3><a name=\"pdf-window:getmaxyx\"><code>window:getmaxyx ()</code></a></h3>\n\n\n<p>\nReturns <code>window</code>'s <code>height</code> and <code>width</code>.\n\n\n<p>\n<hr><h3><a name=\"pdf-window:getyx\"><code>window:getyx ()</code></a></h3>\n\n\n<p>\nReturns <code>window</code>'s cursor coordinates <code>y</code> and\n<code>x</code>.\n\n\n<p>\n<hr><h3><a name=\"pdf-window:attrset\"><code>window:attrset (attr)</code></a></h3>\n\n\n<p>\nSets <a href='https://tldp.org/HOWTO/NCURSES-Programming-HOWTO/attrib.html'>the current attributes</a>\nfor future prints to <code>window</code>. Attributes can be one of:\n\n<ul>\n<li><code>curses.A_NORMAL</code>: disables all other attributes.\n<li><code>curses.A_BOLD</code>\n<li><code>curses.A_REVERSE</code>: swaps foreground and background colors.\n<li><code>curses.color_pair(n)</code> for some integer <code>n</code>: color\npair <code>n</code> which must have been initialized using\n<a href=\"#pdf-curses.init_pair\"><code>curses.init_pair</code></a>.\n</ul>\n\n<p>\nSince Lua 5.1 has no bitwise operations, this function currently only supports\nsetting a single attribute.\n\n\n<p>\n<hr><h3><a name=\"pdf-window:attron\"><code>window:attron (attr)</code></a></h3>\n\n\n<p>\nAdds the given <a href='https://tldp.org/HOWTO/NCURSES-Programming-HOWTO/attrib.html'>attribute</a>\nto the set of current attributes for future prints to <code>window</code>. For\nthe list of available attributes see <a href=\"#pdf-window:attrset\"><code>window:attrset</code></a>.\n\n<p>\nSince Lua 5.1 has no bitwise operations, this function currently only supports\nadding a single attribute at a time.\n\n\n<p>\n<hr><h3><a name=\"pdf-window:attroff\"><code>window:attroff (attr)</code></a></h3>\n\n\n<p>\nRemoves the given <a href='https://tldp.org/HOWTO/NCURSES-Programming-HOWTO/attrib.html'>attribute</a>\nfrom the set of current attributes for future prints to <code>window</code>.\nthe list of available attributes see <a href=\"#pdf-window:attrset\"><code>window:attrset</code></a>.\n\n<p>\nSince Lua 5.1 has no bitwise operations, this function currently only supports\nremoving a single attribute at a time.\n\n\n<p>\n<hr><h3><a name=\"pdf-curses.init_pair\"><code>curses.init_pair (i, fg, bg)</code></a></h3>\n\n\n<p>\nInitializes color pair <code>i</code> to (<code>foreground</code>, <code>background</code>).\nNow calls to <a href='#pdf-curses.color_pair'>curses.color_pair(i)</code></a> will\nyield the attributes for that color pair.\n\n\n<p>\n<hr><h3><a name=\"pdf-curses.color_pair\"><code>curses.color_pair (i)</code></a></h3>\n\n\n<p>\nReturns attributes for a (<code>foreground</code>, <code>background</code>)\npair of colors suitable to pass into\n<a href=\"#pdf-window:attrset\"><code>window:attrset</code></a>,\n<a href=\"#pdf-window:attron\"><code>window:attron</code></a> and\n<a href=\"#pdf-window:attroff\"><code>window:attroff</code></a>.\n\n\n<p>\n<hr><h3><a name=\"pdf-window:getch\"><code>window:getch ()</code></a></h3>\n\n\n<p>\nReturns a character from the keyboard. Waits for a key to be pressed by\ndefault, but this behavior can be changed by calling <a href=\"#pdf-window:nodelay\"><code>window:nodelay(true)</code></a>.\n\n<p>\n<code>window:getch</code> is the only supported way to get input from\nkeyboard in Teliva, handling Teliva's menu and so on.\n\n\n<p>\n<hr><h3><a name=\"pdf-window:nodelay\"><code>window:nodelay (on)</code></a></h3>\n\n\n<p>\nForces <a href=\"#pdf-window:getch\"><code>window:getch()</code></a> to be non-blocking.\n\n\n<hr>\nBesides these, there are other primitives that have never been used in Teliva\napps, but should still work. Please report if you try them out.\n\n</div>\n\n<div class='teliva'>\n<h2>5.11 - <a name=\"5.11\">Networking Facilities</a></h2>\n\nTeliva includes the following well-known Lua libraries for networking:\n\n<li><a href='https://w3.impa.br/~diego/software/luasocket/reference.html'>LuaSocket</a>,\nconsisting of modules <code>socket</code>, <code>http</code>, <code>url</code>,\n<code>headers</code>, <code>mime</code> and <code>ltn12</code>.\n\n<li><a href='https://github.com/brunoos/luasec/wiki'>LuaSec</a>,\nconsisting of modules <code>https</code> and <code>ssl</code>.\n\n<div class='teliva'>\n<em>Networking primitives are sandboxed, and may fail if the computer owner's\npermissions disallow it.</em>\n</div>\n\n</div>\n\n<div class='teliva'>\n<h2>5.12 - <a name=\"5.12\">JSON Facilities</a></h2>\n\nTeliva includes the well-known\n<a href='https://github.com/rxi/json.lua'>json.lua</a> library (module\n<code>json</code>). It also includes a variant in module <code>jsonf</code> that can\nread JSON from channels opened by\n<a href='#pdf-start_reading'><code>start_reading</code></a>.\n\n<p>\n<hr><h3><a name=\"pdf-json.encode\"><code>json.encode (value)</code></a></h3>\n\n\n<p>\nReturns a string representing <code>value</code> encoded in JSON.\n\n<pre>\n     json.encode({ 1, 2, 3, { x = 10 } }) -- Returns '[1,2,3,{\"x\":10}]'\n</pre><p>\n\n\n\n<p>\n<hr><h3><a name=\"pdf-json.decode\"><code>json.decode (str)</code></a></h3>\n\n\n<p>\nReturns a value representing the JSON string <code>str</code>.\n\n<pre>\n     json.decode('[1,2,3,{\"x\":10}]')      -- Returns { 1, 2, 3, { x = 10 } }\n</pre><p>\n\n\n\n<p>\n<hr><h3><a name=\"pdf-jsonf.decode\"><code>jsonf.decode (f)</code></a></h3>\n\n\n<p>\nReads a value encoded in JSON from a file and returns it.\n<code>f</code> is the type of file object returned by\n<a href='#pdf-start_reading'><code>start_reading</code></a> (i.e. supporting\n<code>f.read(format)</code>).\n\nFor example, suppose file <code>foo</code> contains '[1,2,3,{\"x\":10}]'. Then:\n<pre>\n     local infile = start_reading(nil, 'foo')\n     jsonf.decode(infile)                 -- Returns { 1, 2, 3, { x = 10 } }\n</pre><p>\n\n\n\n</div>\n\n<div class='teliva'>\n<h2>5.13 - <a name=\"5.13\">Tasks and Channels</a></h2>\n\nTeliva includes the well-known\n<a href='https://github.com/majek/lua-channels#readme'>lua-channels</a>\nlibrary in module <code>task</code>. See sieve.tlv for a basic example.\n\n<p>\n<hr><h3><a name=\"pdf-task.spawn\"><code>task.spawn (fun, [...])</code></a></h3>\n\n\n<p>\nRun <code>fun</code> as a coroutine with given parameters. Spawn tasks instead of\njust calling <a href=\"#pdf-coroutine.create\"><code>coroutine.create</code></a>\nwhen you can't statically predict how your coroutines will transfer control to\neach other.\n\n\n<p>\n<hr><h3><a name=\"pdf-task.scheduler\"><code>task.scheduler ()</code></a></h3>\n\n\n<p>\nStarts running any spawned tasks. Execution transfers to spawned tasks;\nthis function only returns when there are no tasks left to run or when all\ntasks are blocked (deadlock).\n\n\n<p>\n<hr><h3><a name=\"pdf-Channel:new\"><code>task.Channel:new ([size])</code></a></h3>\n\n\n<p>\nCreate a new channel with given size (which defaults to 0).\n\n\n<p>\n<hr><h3><a name=\"pdf-channel:send\"><code>channel:send (value)</code></a></h3>\n\n\n<p>\nWrite <code>value</code> to a channel. Blocks the current coroutine if the\nchannel is already full. (Channels with size 0 always block if there isn't\nalready a coroutine trying to <a href=\"#pdf-channel:recv\"><code>recv</code></a>\nfrom them.)\n\n\n<p>\n<hr><h3><a name=\"pdf-channel:recv\"><code>channel:recv ()</code></a></h3>\n\n\n<p>\nRead a value from a channel. Blocks the current coroutine if the\nchannel is empty and there isn't already a coroutine trying to\n<a href=\"#pdf-channel:send\"><code>send</code></a> to them.\n\n\n<hr>\nBesides these, there are other primitives that have never been used in Teliva\napps, but should still work. Please report if you try them out.\n\n</div>\n\n\n\n<h1>8 - <a name=\"8\">The Complete Syntax of Lua</a></h1>\n\n<p>\nHere is the complete syntax of Lua in extended BNF.\n(It does not describe operator precedences.)\n\n\n\n\n<pre>\n\n\tchunk ::= {stat [`<b>;</b>&acute;]} [laststat [`<b>;</b>&acute;]]\n\n\tblock ::= chunk\n\n\tstat ::=  varlist `<b>=</b>&acute; explist | \n\t\t functioncall | \n\t\t <b>do</b> block <b>end</b> | \n\t\t <b>while</b> exp <b>do</b> block <b>end</b> | \n\t\t <b>repeat</b> block <b>until</b> exp | \n\t\t <b>if</b> exp <b>then</b> block {<b>elseif</b> exp <b>then</b> block} [<b>else</b> block] <b>end</b> | \n\t\t <b>for</b> Name `<b>=</b>&acute; exp `<b>,</b>&acute; exp [`<b>,</b>&acute; exp] <b>do</b> block <b>end</b> | \n\t\t <b>for</b> namelist <b>in</b> explist <b>do</b> block <b>end</b> | \n\t\t <b>function</b> funcname funcbody | \n\t\t <b>local</b> <b>function</b> Name funcbody | \n\t\t <b>local</b> namelist [`<b>=</b>&acute; explist] \n\n\tlaststat ::= <b>return</b> [explist] | <b>break</b>\n\n\tfuncname ::= Name {`<b>.</b>&acute; Name} [`<b>:</b>&acute; Name]\n\n\tvarlist ::= var {`<b>,</b>&acute; var}\n\n\tvar ::=  Name | prefixexp `<b>[</b>&acute; exp `<b>]</b>&acute; | prefixexp `<b>.</b>&acute; Name \n\n\tnamelist ::= Name {`<b>,</b>&acute; Name}\n\n\texplist ::= {exp `<b>,</b>&acute;} exp\n\n\texp ::=  <b>nil</b> | <b>false</b> | <b>true</b> | Number | String | `<b>...</b>&acute; | function | \n\t\t prefixexp | tableconstructor | exp binop exp | unop exp \n\n\tprefixexp ::= var | functioncall | `<b>(</b>&acute; exp `<b>)</b>&acute;\n\n\tfunctioncall ::=  prefixexp args | prefixexp `<b>:</b>&acute; Name args \n\n\targs ::=  `<b>(</b>&acute; [explist] `<b>)</b>&acute; | tableconstructor | String \n\n\tfunction ::= <b>function</b> funcbody\n\n\tfuncbody ::= `<b>(</b>&acute; [parlist] `<b>)</b>&acute; block <b>end</b>\n\n\tparlist ::= namelist [`<b>,</b>&acute; `<b>...</b>&acute;] | `<b>...</b>&acute;\n\n\ttableconstructor ::= `<b>{</b>&acute; [fieldlist] `<b>}</b>&acute;\n\n\tfieldlist ::= field {fieldsep field} [fieldsep]\n\n\tfield ::= `<b>[</b>&acute; exp `<b>]</b>&acute; `<b>=</b>&acute; exp | Name `<b>=</b>&acute; exp | exp\n\n\tfieldsep ::= `<b>,</b>&acute; | `<b>;</b>&acute;\n\n\tbinop ::= `<b>+</b>&acute; | `<b>-</b>&acute; | `<b>*</b>&acute; | `<b>/</b>&acute; | `<b>^</b>&acute; | `<b>%</b>&acute; | `<b>..</b>&acute; | \n\t\t `<b>&lt;</b>&acute; | `<b>&lt;=</b>&acute; | `<b>&gt;</b>&acute; | `<b>&gt;=</b>&acute; | `<b>==</b>&acute; | `<b>~=</b>&acute; | \n\t\t <b>and</b> | <b>or</b>\n\n\tunop ::= `<b>-</b>&acute; | <b>not</b> | `<b>#</b>&acute;\n\n</pre>\n\n<p>\n\n\n\n\n\n\n\n<HR>\n<SMALL CLASS=\"footer\">\nLast update:\nMon Feb 13 18:54:19 BRST 2012\n</SMALL>\n\n</body></html>\n\n"
  },
  {
    "path": "gemini.tlv",
    "content": "# .tlv file generated by https://github.com/akkartik/teliva\n# You may edit it if you are careful; however, you may see cryptic errors if you\n# violate Teliva's assumptions.\n#\n# .tlv files are representations of Teliva programs. Teliva programs consist of\n# sequences of definitions. Each definition is a table of key/value pairs. Keys\n# and values are both strings.\n#\n# Lines in .tlv files always follow exactly one of the following forms:\n# - comment lines at the top of the file starting with '#' at column 0\n# - beginnings of definitions starting with '- ' at column 0, followed by a\n#   key/value pair\n# - key/value pairs consisting of '  ' at column 0, containing either a\n#   spaceless value on the same line, or a multi-line value\n# - multiline values indented by more than 2 spaces, starting with a '>'\n#\n# If these constraints are violated, Teliva may unceremoniously crash. Please\n# report bugs at http://akkartik.name/contact\n- __teliva_timestamp: original\n  str_helpers:\n    >-- some string helpers from http://lua-users.org/wiki/StringIndexing\n    >\n    >-- index characters using []\n    >getmetatable('').__index = function(str,i)\n    >  if type(i) == 'number' then\n    >    return str:sub(i,i)\n    >  else\n    >    return string[i]\n    >  end\n    >end\n    >\n    >-- ranges using (), selected bytes using {}\n    >getmetatable('').__call = function(str,i,j)\n    >  if type(i)~='table' then\n    >    return str:sub(i,j)\n    >  else\n    >    local t={}\n    >    for k,v in ipairs(i) do\n    >      t[k]=str:sub(v,v)\n    >    end\n    >    return table.concat(t)\n    >  end\n    >end\n    >\n    >-- iterate over an ordered sequence\n    >function q(x)\n    >  if type(x) == 'string' then\n    >    return x:gmatch('.')\n    >  else\n    >    return ipairs(x)\n    >  end\n    >end\n    >\n    >-- insert within string\n    >function string.insert(str1, str2, pos)\n    >  return str1:sub(1,pos)..str2..str1:sub(pos+1)\n    >end\n    >\n    >function string.remove(s, pos)\n    >  return s:sub(1,pos-1)..s:sub(pos+1)\n    >end\n    >\n    >function string.pos(s, sub)\n    >  return string.find(s, sub, 1, true)  -- plain=true to disable regular expressions\n    >end\n    >\n    >-- TODO: backport utf-8 support from Lua 5.3\n- __teliva_timestamp: original\n  debugy:\n    >debugy = 5\n- __teliva_timestamp: original\n  dbg:\n    >-- helper for debug by print; overlay debug information towards the right\n    >-- reset debugy every time you refresh screen\n    >function dbg(window, s)\n    >  local oldy = 0\n    >  local oldx = 0\n    >  oldy, oldx = window:getyx()\n    >  window:mvaddstr(debugy, 60, s)\n    >  debugy = debugy+1\n    >  window:mvaddstr(oldy, oldx, '')\n    >end\n- __teliva_timestamp: original\n  check:\n    >function check(x, msg)\n    >  if x then\n    >    Window:addch('.')\n    >  else\n    >    print('F - '..msg)\n    >    print('  '..str(x)..' is false/nil')\n    >    teliva_num_test_failures = teliva_num_test_failures + 1\n    >    -- overlay first test failure on editors\n    >    if teliva_first_failure == nil then\n    >      teliva_first_failure = msg\n    >    end\n    >  end\n    >end\n- __teliva_timestamp: original\n  check_eq:\n    >function check_eq(x, expected, msg)\n    >  if eq(x, expected) then\n    >    Window:addch('.')\n    >  else\n    >    print('F - '..msg)\n    >    print('  expected '..str(expected)..' but got '..str(x))\n    >    teliva_num_test_failures = teliva_num_test_failures + 1\n    >    -- overlay first test failure on editors\n    >    if teliva_first_failure == nil then\n    >      teliva_first_failure = msg\n    >    end\n    >  end\n    >end\n- __teliva_timestamp: original\n  eq:\n    >function eq(a, b)\n    >  if type(a) ~= type(b) then return false end\n    >  if type(a) == 'table' then\n    >    if #a ~= #b then return false end\n    >    for k, v in pairs(a) do\n    >      if b[k] ~= v then\n    >        return false\n    >      end\n    >    end\n    >    for k, v in pairs(b) do\n    >      if a[k] ~= v then\n    >        return false\n    >      end\n    >    end\n    >    return true\n    >  end\n    >  return a == b\n    >end\n- __teliva_timestamp: original\n  str:\n    >-- smarter tostring\n    >-- slow; used only for debugging\n    >function str(x)\n    >  if type(x) == 'table' then\n    >    local result = ''\n    >    result = result..#x..'{'\n    >    for k, v in pairs(x) do\n    >      result = result..str(k)..'='..str(v)..', '\n    >    end\n    >    result = result..'}'\n    >    return result\n    >  elseif type(x) == 'string' then\n    >    return '\"'..x..'\"'\n    >  end\n    >  return tostring(x)\n    >end\n- __teliva_timestamp: original\n  find_index:\n    >function find_index(arr, x)\n    >  for n, y in ipairs(arr) do\n    >    if x == y then\n    >      return n\n    >    end\n    >  end\n    >end\n- __teliva_timestamp: original\n  trim:\n    >function trim(s)\n    >  return s:gsub('^%s*', ''):gsub('%s*$', '')\n    >end\n- __teliva_timestamp: original\n  split:\n    >function split(s, d)\n    >  result = {}\n    >  for match in (s..d):gmatch(\"(.-)\"..d) do\n    >    table.insert(result, match);\n    >  end\n    >  return result\n    >end\n- __teliva_timestamp: original\n  clear:\n    >function clear(lines)\n    >  while #lines > 0 do\n    >    table.remove(lines)\n    >  end\n    >end\n- __teliva_timestamp: original\n  Window:\n    >Window = curses.stdscr()\n- __teliva_timestamp: original\n  render_line:\n    >function render_line(window, y, line)\n    >  window:mvaddstr(y, 0, '')\n    >  for i=1,line:len() do\n    >    window:addstr(line[i])\n    >  end\n    >end\n- __teliva_timestamp: original\n  render_link:\n    >function render_link(window, y, line)\n    >  local rendered_line = line:gsub('=>%s*%S*%s*', '')\n    >  if trim(rendered_line) == '' then\n    >    rendered_line = line\n    >  end\n    >  render_line(window, y, rendered_line)\n    >end\n- __teliva_timestamp: original\n  state:\n    >state = {\n    >  lines={},\n    >  history={},\n    >  highlight_index=0,\n    >  source=false,  -- show source (link urls, etc.)\n    >}\n- __teliva_timestamp: original\n  render_page:\n    >function render_page(window)\n    >  local y = 0\n    >  window:attron(curses.color_pair(6))\n    >  print(state.url)\n    >  window:attroff(curses.color_pair(6))\n    >  y = y+2\n    >--?   dbg(window, state.highlight_index)\n    >  for i, line in pairs(state.lines) do\n    >    if not state.source and line:find('=> ') == 1 then\n    >      if state.highlight_index == 0 or i == state.highlight_index then\n    >        -- highlighted link\n    >        state.highlight_index = i  -- TODO: ugly state update while rendering, just for first render after gemini_get\n    >        window:attron(curses.A_REVERSE)\n    >        render_link(window, y, line)\n    >        window:attroff(curses.A_REVERSE)\n    >      else\n    >        -- link\n    >        window:attron(curses.A_BOLD)\n    >        render_link(window, y, line)\n    >        window:attroff(curses.A_BOLD)\n    >      end\n    >    else\n    >      -- non-link\n    >      render_line(window, y, line)\n    >    end\n    >    y = y+1\n    >  end\n    >end\n- __teliva_timestamp: original\n  render:\n    >function render(window, lines)\n    >  window:clear()\n    >  render_page(window, lines)\n    >  curses.curs_set(0)\n    >  window:refresh()\n    >end\n- __teliva_timestamp: original\n  menu:\n    >menu = {\n    >  {'Enter', 'go to highlight'},\n    >  {'<-', 'back'},\n    >  {'^g', 'enter url'},\n    >  {'^u', 'view source'},\n    >}\n- __teliva_timestamp: original\n  edit_line:\n    >function edit_line(window)\n    >  local result = ''\n    >  local cursor = 1\n    >  local screen_rows, screen_cols = window:getmaxyx()\n    >  menu = {\n    >    {'enter', 'submit'},\n    >    {'^g', 'cancel'},\n    >    {'^u', 'clear'},\n    >  }\n    >  while true do\n    >    window:mvaddstr(screen_rows-1, 9, '')\n    >    window:clrtoeol()\n    >    window:mvaddstr(screen_rows-1, 9, result)\n    >    window:attron(curses.A_REVERSE)\n    >    -- window:refresh()\n    >    local key = window:getch()\n    >    window:attrset(curses.A_NORMAL)\n    >    if key >= 32 and key < 127 then\n    >      local screen_rows, screen_cols = window:getmaxyx()\n    >      if #result < screen_cols then\n    >        result = result:insert(string.char(key), cursor-1)\n    >        cursor = cursor+1\n    >      end\n    >    elseif key == curses.KEY_LEFT then\n    >      if cursor > 1 then\n    >        cursor = cursor-1\n    >      end\n    >    elseif key == curses.KEY_RIGHT then\n    >      if cursor <= #result then\n    >        cursor = cursor+1\n    >      end\n    >    elseif key == curses.KEY_BACKSPACE then\n    >      if cursor > 1 then\n    >        cursor = cursor-1\n    >        result = result:remove(cursor)\n    >      end\n    >    elseif key == 21 then  -- ctrl-u\n    >      result = ''\n    >      cursor = 1\n    >    elseif key == 10 then  -- enter\n    >      return result\n    >    elseif key == 7 then  -- ctrl-g\n    >      return nil\n    >    end\n    >  end\n    >end\n- __teliva_timestamp: original\n  is_link:\n    >function is_link(line)\n    >  return line:find('=>%s*%S*%s*') == 1\n    >end\n- __teliva_timestamp: original\n  next_link:\n    >function next_link()\n    >  local new_index = state.highlight_index\n    >  while true do\n    >    new_index = new_index+1\n    >    if new_index > #state.lines then return end\n    >    if is_link(state.lines[new_index]) then break end\n    >  end\n    >  state.highlight_index = new_index\n    >end\n- __teliva_timestamp: original\n  previous_link:\n    >function previous_link()\n    >  local new_index = state.highlight_index\n    >  while true do\n    >    new_index = new_index - 1\n    >    if new_index < 1 then return end\n    >    if is_link(state.lines[new_index]) then break end\n    >  end\n    >  state.highlight_index = new_index\n    >end\n- __teliva_timestamp: original\n  update:\n    >function update(window)\n    >  local key = window:getch()\n    >  local screen_rows, screen_cols = window:getmaxyx()\n    >  if key == curses.KEY_DOWN then\n    >    next_link()\n    >  elseif key == curses.KEY_UP then\n    >    previous_link()\n    >  elseif key == curses.KEY_LEFT then\n    >    if #state.history > 1 then\n    >      table.remove(state.history)\n    >      gemini_get(table.remove(state.history))\n    >    end\n    >  elseif key == 21 then  -- ctrl-u\n    >    state.source = not state.source\n    >  elseif key == 10 then  -- enter\n    >    local s, e, new_url = string.find(state.lines[state.highlight_index], '=>%s*(%S*)')\n    >    gemini_get(url.absolute(state.url, new_url))\n    >  elseif key == 7 then  -- ctrl-g\n    >    window:mvaddstr(screen_rows-2, 0, '')\n    >    window:clrtoeol()\n    >    window:mvaddstr(screen_rows-1, 0, '')\n    >    window:clrtoeol()\n    >    window:mvaddstr(screen_rows-1, 5, 'go: ')\n    >    curses.curs_set(2)\n    >    local old_menu = menu\n    >    local new_url = edit_line(window)\n    >    menu = old_menu\n    >    if new_url then\n    >      state.url = new_url\n    >      gemini_get(new_url)\n    >    end\n    >    curses.curs_set(0)\n    >  end\n    >end\n- __teliva_timestamp: original\n  init_colors:\n    >function init_colors()\n    >  for i=0,7 do\n    >    curses.init_pair(i, i, -1)\n    >  end\n    >  curses.init_pair(8, 7, 0)\n    >  curses.init_pair(9, 7, 1)\n    >  curses.init_pair(10, 7, 2)\n    >  curses.init_pair(11, 7, 3)\n    >  curses.init_pair(12, 7, 4)\n    >  curses.init_pair(13, 7, 5)\n    >  curses.init_pair(14, 7, 6)\n    >  curses.init_pair(15, -1, 15)\n    >end\n- __teliva_timestamp: original\n  main:\n    >function main()\n    >  Window:clear()\n    >  Window:refresh()\n    >  init_colors()\n    >  local lines = {}\n    >  local url = ''\n    >  if #arg > 0 then\n    >    state.url = arg[1]\n    >    lines = gemini_get(state.url)\n    >  end\n    >  while true do\n    >    render(Window, lines)\n    >    update(Window, lines)\n    >  end\n    >end\n- __teliva_timestamp: original\n  http_get:\n    >function http_get(url)\n    >  -- https://stackoverflow.com/questions/42445423/luasocket-serveraccept-timeout-tcp\n    >  local parsed_url = socket.url.parse(url)\n    >  local tcp = socket.tcp()\n    >  tcp:connect(parsed_url.host, 80)\n    >  tcp:send('GET / HTTP/1.1\\r\\n')\n    >  -- http requires the Host header\n    >  tcp:send(string.format('Host: %s\\r\\n', parsed_url.host))\n    >  tcp:send('\\r\\n')\n    >  -- tcp:receive('*a') doesn't seem to detect when a request is done\n    >  -- so we have to manage the size of the expected response\n    >  headers = {}\n    >  while true do\n    >    local s, status = tcp:receive()\n    >    if s == nil then break end\n    >    if s == '' then break end\n    >    local header, value = s:match('(.-): (.*)')\n    >    if header == nil then\n    >      print(s)\n    >    else\n    >      headers[header:lower()] = value\n    >      print(header, value)\n    >    end\n    >  end\n    >  local bytes_remaining = tonumber(headers['content-length'])\n    >  body = ''\n    >  while true do\n    >    local s, status = tcp:receive(bytes_remaining)\n    >    if s == nil then break end\n    >    body = body .. s\n    >    bytes_remaining = bytes_remaining - s:len()\n    >    if bytes_remaining <= 0 then break end\n    >  end\n    >  return body\n    >end\n- __teliva_timestamp: original\n  https_get:\n    >-- http://notebook.kulchenko.com/programming/https-ssl-calls-with-lua-and-luasec\n    >function https_get(url)\n    >  local parsed_url = socket.url.parse(url)\n    >  local params = {\n    >    mode = 'client',\n    >    protocol = 'any',\n    >    verify = 'none',  -- I don't know what I'm doing\n    >    options = 'all',\n    >  }\n    >  local conn = socket.tcp()\n    >  conn:connect(parsed_url.host, parsed_url.port or 443)\n    >  conn, err = ssl.wrap(conn, params)\n    >  if conn == nil then\n    >      io.write(err)\n    >      os.exit(1)\n    >  end\n    >  conn:dohandshake()\n    >\n    >  conn:send(url .. \"\\r\\n\")\n    >  local line, err = conn:receive()\n    >  return line or err\n    >end\n- __teliva_timestamp: original\n  parse_gemini_body:\n    >function parse_gemini_body(conn, type)\n    >  if type == 'text/gemini' then\n    >    while true do\n    >      local line, err = conn:receive()\n    >      if line == nil then break end\n    >      table.insert(state.lines, line)\n    >    end\n    >  elseif type:sub(1, 5) == 'text/' then\n    >    while true do\n    >      local line, err = conn:receive()\n    >      if line == nil then break end\n    >      table.insert(state.lines, line)\n    >    end\n    >  end\n    >end\n- __teliva_timestamp: original\n  gemini_get:\n    >-- http://notebook.kulchenko.com/programming/https-ssl-calls-with-lua-and-luasec\n    >-- https://tildegit.org/solderpunk/gemini-demo-2\n    >-- returns an array of lines, containing either the body or just an error\n    >function gemini_get(url)\n    >  if url:find(\"://\") == nil then\n    >    url = \"gemini://\" .. url\n    >  end\n    >  local parsed_url = socket.url.parse(url)\n    >  local params = {\n    >    mode = 'client',\n    >    protocol = 'any',\n    >    verify = 'none',  -- I don't know what I'm doing\n    >    options = 'all',\n    >  }\n    >  local conn = socket.tcp()\n    >  local conn2, err = conn:connect(parsed_url.host, parsed_url.port or 1965)\n    >  clear(state.lines)\n    >  state.highlight_index = 0  -- highlighted link not computed yet\n    >  if conn2 == nil then\n    >    table.insert(state.lines, err)\n    >    return\n    >  end\n    >  conn, err = ssl.wrap(conn, params)\n    >  if conn == nil then\n    >    table.insert(state.lines, err)\n    >    return\n    >  end\n    >  conn:dohandshake()\n    >  conn:send(url .. \"\\r\\n\")\n    >  local line, err = conn:receive()\n    >  if line == nil then\n    >    table.insert(state.lines, err)\n    >    return\n    >  end\n    >  local status, meta = line:match(\"(%S+) (%S+)\")\n    >  if status[1] == '2' then\n    >    parse_gemini_body(conn, meta)\n    >    state.url = url\n    >    table.insert(state.history, url)\n    >  elseif status[1] == '3' then\n    >    gemini_get(socket.url.absolute(url, meta))\n    >  elseif status[1] == '4' or line[1] == '5' then\n    >    table.insert(state.lines, 'Error: '..meta)\n    >  else\n    >    table.insert(state.lines, 'invalid response from server: '..line)\n    >  end\n    >end\n- __teliva_timestamp:\n    >Thu Feb 17 20:04:42 2022\n  doc:blurb:\n    >A bare-bones browser for the Gemini protocol\n    >\n    >https://gemini.circumlunar.space\n    >\n    >A couple of good pages to try it out with:\n    >  $ src/teliva gemini.tlv gemini.circumlunar.space\n    >  $ src/teliva gemini.tlv gemini.conman.org\n    >  $ src/teliva gemini.tlv gemini.susa.net/cgi-bin/links.lua  # shows 20 random links from Gemini space\n"
  },
  {
    "path": "graphviz.tlv",
    "content": "# .tlv file generated by https://github.com/akkartik/teliva\n# You may edit it if you are careful; however, you may see cryptic errors if you\n# violate Teliva's assumptions.\n#\n# .tlv files are representations of Teliva programs. Teliva programs consist of\n# sequences of definitions. Each definition is a table of key/value pairs. Keys\n# and values are both strings.\n#\n# Lines in .tlv files always follow exactly one of the following forms:\n# - comment lines at the top of the file starting with '#' at column 0\n# - beginnings of definitions starting with '- ' at column 0, followed by a\n#   key/value pair\n# - key/value pairs consisting of '  ' at column 0, containing either a\n#   spaceless value on the same line, or a multi-line value\n# - multiline values indented by more than 2 spaces, starting with a '>'\n#\n# If these constraints are violated, Teliva may unceremoniously crash. Please\n# report bugs at http://akkartik.name/contact\n- __teliva_timestamp: original\n  str_helpers:\n    >-- some string helpers from http://lua-users.org/wiki/StringIndexing\n    >\n    >-- index characters using []\n    >getmetatable('').__index = function(str,i)\n    >  if type(i) == 'number' then\n    >    return str:sub(i,i)\n    >  else\n    >    return string[i]\n    >  end\n    >end\n    >\n    >-- ranges using (), selected bytes using {}\n    >getmetatable('').__call = function(str,i,j)\n    >  if type(i)~='table' then\n    >    return str:sub(i,j)\n    >  else\n    >    local t={}\n    >    for k,v in ipairs(i) do\n    >      t[k]=str:sub(v,v)\n    >    end\n    >    return table.concat(t)\n    >  end\n    >end\n    >\n    >-- iterate over an ordered sequence\n    >function q(x)\n    >  if type(x) == 'string' then\n    >    return x:gmatch('.')\n    >  else\n    >    return ipairs(x)\n    >  end\n    >end\n    >\n    >-- insert within string\n    >function string.insert(str1, str2, pos)\n    >  return str1:sub(1,pos)..str2..str1:sub(pos+1)\n    >end\n    >\n    >function string.remove(s, pos)\n    >  return s:sub(1,pos-1)..s:sub(pos+1)\n    >end\n    >\n    >function string.pos(s, sub)\n    >  return string.find(s, sub, 1, true)  -- plain=true to disable regular expressions\n    >end\n    >\n    >-- TODO: backport utf-8 support from Lua 5.3\n- __teliva_timestamp: original\n  debugy:\n    >debugy = 5\n- __teliva_timestamp: original\n  dbg:\n    >-- helper for debug by print; overlay debug information towards the right\n    >-- reset debugy every time you refresh screen\n    >function dbg(window, s)\n    >  local oldy = 0\n    >  local oldx = 0\n    >  oldy, oldx = window:getyx()\n    >  window:mvaddstr(debugy, 60, s)\n    >  debugy = debugy+1\n    >  window:mvaddstr(oldy, oldx, '')\n    >end\n- __teliva_timestamp: original\n  check:\n    >function check(x, msg)\n    >  if x then\n    >    Window:addch('.')\n    >  else\n    >    print('F - '..msg)\n    >    print('  '..str(x)..' is false/nil')\n    >    teliva_num_test_failures = teliva_num_test_failures + 1\n    >    -- overlay first test failure on editors\n    >    if teliva_first_failure == nil then\n    >      teliva_first_failure = msg\n    >    end\n    >  end\n    >end\n- __teliva_timestamp: original\n  check_eq:\n    >function check_eq(x, expected, msg)\n    >  if eq(x, expected) then\n    >    Window:addch('.')\n    >  else\n    >    print('F - '..msg)\n    >    print('  expected '..str(expected)..' but got '..str(x))\n    >    teliva_num_test_failures = teliva_num_test_failures + 1\n    >    -- overlay first test failure on editors\n    >    if teliva_first_failure == nil then\n    >      teliva_first_failure = msg\n    >    end\n    >  end\n    >end\n- __teliva_timestamp: original\n  eq:\n    >function eq(a, b)\n    >  if type(a) ~= type(b) then return false end\n    >  if type(a) == 'table' then\n    >    if #a ~= #b then return false end\n    >    for k, v in pairs(a) do\n    >      if b[k] ~= v then\n    >        return false\n    >      end\n    >    end\n    >    for k, v in pairs(b) do\n    >      if a[k] ~= v then\n    >        return false\n    >      end\n    >    end\n    >    return true\n    >  end\n    >  return a == b\n    >end\n- __teliva_timestamp: original\n  str:\n    >-- smarter tostring\n    >-- slow; used only for debugging\n    >function str(x)\n    >  if type(x) == 'table' then\n    >    local result = ''\n    >    result = result..#x..'{'\n    >    for k, v in pairs(x) do\n    >      result = result..str(k)..'='..str(v)..', '\n    >    end\n    >    result = result..'}'\n    >    return result\n    >  elseif type(x) == 'string' then\n    >    return '\"'..x..'\"'\n    >  end\n    >  return tostring(x)\n    >end\n- __teliva_timestamp: original\n  find_index:\n    >function find_index(arr, x)\n    >  for n, y in ipairs(arr) do\n    >    if x == y then\n    >      return n\n    >    end\n    >  end\n    >end\n- __teliva_timestamp: original\n  trim:\n    >function trim(s)\n    >  return s:gsub('^%s*', ''):gsub('%s*$', '')\n    >end\n- __teliva_timestamp: original\n  split:\n    >function split(s, d)\n    >  result = {}\n    >  for match in (s..d):gmatch(\"(.-)\"..d) do\n    >    table.insert(result, match);\n    >  end\n    >  return result\n    >end\n- __teliva_timestamp: original\n  map:\n    >-- only for arrays\n    >function map(l, f)\n    >  result = {}\n    >  for _, x in ipairs(l) do\n    >    table.insert(result, f(x))\n    >  end\n    >  return result\n    >end\n- __teliva_timestamp: original\n  reduce:\n    >-- only for arrays\n    >function reduce(l, f, init)\n    >  result = init\n    >  for _, x in ipairs(l) do\n    >    result = f(result, x)\n    >  end\n    >  return result\n    >end\n- __teliva_timestamp: original\n  filter:\n    >function filter(h, f)\n    >  result = {}\n    >  for k, v in pairs(h) do\n    >    if f(k, v) then\n    >      result[k] = v\n    >    end\n    >  end\n    >  return result\n    >end\n- __teliva_timestamp: original\n  ifilter:\n    >-- only for arrays\n    >function ifilter(l, f)\n    >  result = {}\n    >  for _, x in ipairs(l) do\n    >    if f(x) then\n    >      table.insert(result, x)\n    >    end\n    >  end\n    >  return result\n    >end\n- __teliva_timestamp: original\n  sort_letters:\n    >function sort_letters(s)\n    >  tmp = {}\n    >  for i=1,#s do\n    >    table.insert(tmp, s[i])\n    >  end\n    >  table.sort(tmp)\n    >  local result = ''\n    >  for _, c in pairs(tmp) do\n    >    result = result..c\n    >  end\n    >  return result\n    >end\n    >\n    >function test_sort_letters(s)\n    >  check_eq(sort_letters(''), '', 'test_sort_letters: empty')\n    >  check_eq(sort_letters('ba'), 'ab', 'test_sort_letters: non-empty')\n    >  check_eq(sort_letters('abba'), 'aabb', 'test_sort_letters: duplicates')\n    >end\n- __teliva_timestamp: original\n  count_letters:\n    >-- TODO: handle unicode\n    >function count_letters(s)\n    >  local result = {}\n    >  for i=1,s:len() do\n    >    local c = s[i]\n    >    if result[c] == nil then\n    >      result[c] = 1\n    >    else\n    >      result[c] = result[c] + 1\n    >    end\n    >  end\n    >  return result\n    >end\n- __teliva_timestamp: original\n  count:\n    >-- turn an array of elements into a map from elements to their frequency\n    >-- analogous to count_letters for non-strings\n    >function count(a)\n    >  local result = {}\n    >  for i, v in ipairs(a) do\n    >    if result[v] == nil then\n    >      result[v] = 1\n    >    else\n    >      result[v] = result[v] + 1\n    >    end\n    >  end\n    >  return result\n    >end\n- __teliva_timestamp: original\n  union:\n    >function union(a, b)\n    >  for k, v in pairs(b) do\n    >    a[k] = v\n    >  end\n    >  return a\n    >end\n- __teliva_timestamp: original\n  subtract:\n    >-- set subtraction\n    >function subtract(a, b)\n    >  for k, v in pairs(b) do\n    >    a[k] = nil\n    >  end\n    >  return a\n    >end\n- __teliva_timestamp: original\n  all:\n    >-- universal quantifier on sets\n    >function all(s, f)\n    >  for k, v in pairs(s) do\n    >    if not f(k, v) then\n    >      return false\n    >    end\n    >  end\n    >  return true\n    >end\n- __teliva_timestamp: original\n  to_array:\n    >-- turn a set into an array\n    >-- drops values\n    >function to_array(h)\n    >  local result = {}\n    >  for k, _ in pairs(h) do\n    >    table.insert(result, k)\n    >  end\n    >  return result\n    >end\n- __teliva_timestamp: original\n  append:\n    >-- concatenate list 'elems' into 'l', modifying 'l' in the process\n    >function append(l, elems)\n    >  for i=1,#elems do\n    >    table.insert(l, elems[i])\n    >  end\n    >end\n- __teliva_timestamp: original\n  prepend:\n    >-- concatenate list 'elems' into the start of 'l', modifying 'l' in the process\n    >function prepend(l, elems)\n    >  for i=1,#elems do\n    >    table.insert(l, i, elems[i])\n    >  end\n    >end\n- __teliva_timestamp: original\n  all_but:\n    >function all_but(x, idx)\n    >  if type(x) == 'table' then\n    >    local result = {}\n    >    for i, elem in ipairs(x) do\n    >      if i ~= idx then\n    >        table.insert(result,elem)\n    >      end\n    >    end\n    >    return result\n    >  elseif type(x) == 'string' then\n    >    if idx < 1 then return x:sub(1) end\n    >    return x:sub(1, idx-1) .. x:sub(idx+1)\n    >  else\n    >    error('all_but: unsupported type '..type(x))\n    >  end\n    >end\n    >\n    >function test_all_but()\n    >  check_eq(all_but('', 0), '', 'all_but: empty')\n    >  check_eq(all_but('abc', 0), 'abc', 'all_but: invalid low index')\n    >  check_eq(all_but('abc', 4), 'abc', 'all_but: invalid high index')\n    >  check_eq(all_but('abc', 1), 'bc', 'all_but: first index')\n    >  check_eq(all_but('abc', 3), 'ab', 'all_but: final index')\n    >  check_eq(all_but('abc', 2), 'ac', 'all_but: middle index')\n    >end\n- __teliva_timestamp: original\n  set:\n    >function set(l)\n    >  local result = {}\n    >  for i, elem in ipairs(l) do\n    >    result[elem] = true\n    >  end\n    >  return result\n    >end\n- __teliva_timestamp: original\n  set_eq:\n    >function set_eq(l1, l2)\n    >  return eq(set(l1), set(l2))\n    >end\n    >\n    >function test_set_eq()\n    >  check(set_eq({1}, {1}), 'set_eq: identical')\n    >  check(not set_eq({1, 2}, {1, 3}), 'set_eq: different')\n    >  check(set_eq({1, 2}, {2, 1}), 'set_eq: order')\n    >  check(set_eq({1, 2, 2}, {2, 1}), 'set_eq: duplicates')\n    >end\n- __teliva_timestamp: original\n  clear:\n    >function clear(lines)\n    >  while #lines > 0 do\n    >    table.remove(lines)\n    >  end\n    >end\n- __teliva_timestamp: original\n  zap:\n    >function zap(target, src)\n    >  clear(target)\n    >  append(target, src)\n    >end\n- __teliva_timestamp: original\n  mfactorial:\n    >-- memoized version of factorial\n    >-- doesn't memoize recursive calls, but may be good enough\n    >mfactorial = memo1(factorial)\n- __teliva_timestamp: original\n  factorial:\n    >function factorial(n)\n    >  local result = 1\n    >  for i=1,n do\n    >    result = result*i\n    >  end\n    >  return result\n    >end\n- __teliva_timestamp: original\n  memo1:\n    >-- a higher-order function that takes a function of a single arg\n    >-- (that never returns nil)\n    >-- and returns a memoized version of it\n    >function memo1(f)\n    >  local memo = {}\n    >  return function(x)\n    >    if memo[x] == nil then\n    >      memo[x] = f(x)\n    >    end\n    >    return memo[x]\n    >  end\n    >end\n    >\n    >-- mfactorial doesn't seem noticeably faster\n    >function test_memo1()\n    >  for i=0,30 do\n    >    check_eq(mfactorial(i), factorial(i), 'memo1 over factorial: '..str(i))\n    >  end\n    >end\n- __teliva_timestamp: original\n  num_permutations:\n    >-- number of permutations of n distinct objects, taken r at a time\n    >function num_permutations(n, r)\n    >  return factorial(n)/factorial(n-r)\n    >end\n    >\n    >-- mfactorial doesn't seem noticeably faster\n    >function test_memo1()\n    >  for i=0,30 do\n    >    for j=0,i do\n    >      check_eq(num_permutations(i, j), mfactorial(i)/mfactorial(i-j), 'num_permutations memoizes: '..str(i)..'P'..str(j))\n    >    end\n    >  end\n    >end\n- __teliva_timestamp: original\n  menu:\n    >-- To show app-specific hotkeys in the menu bar, add hotkey/command\n    >-- arrays of strings to the menu array.\n    >menu = {}\n- __teliva_timestamp: original\n  Window:\n    >Window = curses.stdscr()\n- __teliva_timestamp: original\n  window:\n    >-- constructor for fake screen and window\n    >-- call it like this:\n    >--   local w = window{\n    >--     kbd=kbd('abc'),\n    >--     scr=scr{h=5, w=4},\n    >--   }\n    >-- eventually it'll do everything a real ncurses window can\n    >function window(h)\n    >  h.__index = h\n    >  setmetatable(h, h)\n    >  h.__index = function(table, key)\n    >    return rawget(h, key)\n    >  end\n    >  h.attrset = function(self, x)\n    >    self.scr.attrs = x\n    >  end\n    >  h.attron = function(self, x)\n    >    -- currently same as attrset since Lua 5.1 doesn't have bitwise operators\n    >    -- doesn't support multiple attrs at once\n    >--    local old = self.scr.attrs\n    >--    self.scr.attrs = old|x\n    >    self.scr.attrs = x\n    >  end\n    >  h.attroff = function(self, x)\n    >    -- currently borked since Lua 5.1 doesn't have bitwise operators\n    >    -- doesn't support multiple attrs at once\n    >--    local old = self.scr.attrs\n    >--    self.scr.attrs = old & (~x)\n    >    self.scr.attrs = curses.A_NORMAL\n    >  end\n    >  h.getch = function(self)\n    >    local c = table.remove(h.kbd, 1)\n    >    if c == nil then return c end\n    >    return string.byte(c)  -- for verisimilitude with ncurses\n    >  end\n    >  h.addch = function(self, c)\n    >    local scr = self.scr\n    >    if c == '\\n' then\n    >      scr.cursy = scr.cursy+1\n    >      scr.cursx = 0\n    >      return\n    >    end\n    >    if scr.cursy <= scr.h then\n    >      scr[scr.cursy][scr.cursx] = {data=c, attrs=scr.attrs}\n    >      scr.cursx = scr.cursx+1\n    >      if scr.cursx > scr.w then\n    >        scr.cursy = scr.cursy+1\n    >        scr.cursx = 1\n    >      end\n    >    end\n    >  end\n    >  h.addstr = function(self, s)\n    >    for i=1,s:len() do\n    >      self:addch(s[i])\n    >    end\n    >  end\n    >  h.mvaddch = function(self, y, x, c)\n    >    self.scr.cursy = y\n    >    self.scr.cursx = x\n    >    self:addch(c)\n    >  end\n    >  h.mvaddstr = function(self, y, x, s)\n    >    self.scr.cursy = y\n    >    self.scr.cursx = x\n    >    self:addstr(s)\n    >  end\n    >  h.clear = function(self)\n    >    clear_scr(self.scr)\n    >  end\n    >  h.refresh = function(self)\n    >    -- nothing\n    >  end\n    >  return h\n    >end\n- __teliva_timestamp: original\n  kbd:\n    >function kbd(keys)\n    >  local result = {}\n    >  for i=1,keys:len() do\n    >    table.insert(result, keys[i])\n    >  end\n    >  return result\n    >end\n- __teliva_timestamp: original\n  scr:\n    >function scr(props)\n    >  props.cursx = 1\n    >  props.cursy = 1\n    >  clear_scr(props)\n    >  return props\n    >end\n- __teliva_timestamp: original\n  clear_scr:\n    >function clear_scr(props)\n    >  props.cursy = 1\n    >  props.cursx = 1\n    >  for y=1,props.h do\n    >    props[y] = {}\n    >    for x=1,props.w do\n    >      props[y][x] = {data=' ', attrs=curses.A_NORMAL}\n    >    end\n    >  end\n    >  return props\n    >end\n- __teliva_timestamp: original\n  check_screen:\n    >function check_screen(window, contents, message)\n    >  local x, y = 1, 1\n    >  for i=1,contents:len() do\n    >    check_eq(window.scr[y][x].data, contents[i], message..'/'..y..','..x)\n    >    x = x+1\n    >    if x > window.scr.w then\n    >      y = y+1\n    >      x = 1\n    >    end\n    >  end\n    >end\n    >\n    >-- putting it all together, an example test of both keyboard and screen\n    >function test_check_screen()\n    >  local lines = {\n    >    c='123',\n    >    d='234',\n    >    a='345',\n    >    b='456',\n    >  }\n    >  local w = window{\n    >    kbd=kbd('abc'),\n    >    scr=scr{h=3, w=5},\n    >  }\n    >  local y = 1\n    >  while true do\n    >    local b = w:getch()\n    >    if b == nil then break end\n    >    w:mvaddstr(y, 1, lines[string.char(b)])\n    >    y = y+1\n    >  end\n    >  check_screen(w, '345  '..\n    >                  '456  '..\n    >                  '123  ',\n    >              'test_check_screen')\n    >end\n- __teliva_timestamp: original\n  check_reverse:\n    >function check_reverse(window, contents, message)\n    >  local x, y = 1, 1\n    >  for i=1,contents:len() do\n    >    if contents[i] ~= ' ' then\n    >      -- hacky version while we're without bitwise operators on Lua 5.1\n    >--      check(window.scr[y][x].attrs & curses.A_REVERSE, message..'/'..y..','..x)\n    >      check_eq(window.scr[y][x].attrs, curses.A_REVERSE, message..'/'..y..','..x)\n    >    else\n    >      -- hacky version while we're without bitwise operators on Lua 5.1\n    >--      check(window.scr[y][x].attrs & (~curses.A_REVERSE), message..'/'..y..','..x)\n    >      check(window.scr[y][x].attrs ~= curses.A_REVERSE, message..'/'..y..','..x)\n    >    end\n    >    x = x+1\n    >    if x > window.scr.w then\n    >      y = y+1\n    >      x = 1\n    >    end\n    >  end\n    >end\n- __teliva_timestamp: original\n  check_bold:\n    >function check_bold(window, contents, message)\n    >  local x, y = 1, 1\n    >  for i=1,contents:len() do\n    >    if contents[i] ~= ' ' then\n    >      -- hacky version while we're without bitwise operators on Lua 5.1\n    >--      check(window.scr[y][x].attrs & curses.A_BOLD, message..'/'..y..','..x)\n    >      check_eq(window.scr[y][x].attrs, curses.A_BOLD, message..'/'..y..','..x)\n    >    else\n    >      -- hacky version while we're without bitwise operators on Lua 5.1\n    >--      check(window.scr[y][x].attrs & (~curses.A_BOLD), message..'/'..y..','..x)\n    >      check(window.scr[y][x].attrs ~= curses.A_BOLD, message..'/'..y..','..x)\n    >    end\n    >    x = x+1\n    >    if x > window.scr.w then\n    >      y = y+1\n    >      x = 1\n    >    end\n    >  end\n    >end\n- __teliva_timestamp: original\n  check_color:\n    >-- check which parts of a screen have the given color_pair\n    >function check_color(window, cp, contents, message)\n    >  local x, y = 1, 1\n    >  for i=1,contents:len() do\n    >    if contents[i] ~= ' ' then\n    >      -- hacky version while we're without bitwise operators on Lua 5.1\n    >--      check(window.scr[y][x].attrs & curses.color_pair(cp), message..'/'..y..','..x)\n    >      check_eq(window.scr[y][x].attrs, curses.color_pair(cp), message..'/'..y..','..x)\n    >    else\n    >      -- hacky version while we're without bitwise operators on Lua 5.1\n    >--      check(window.scr[y][x].attrs & (~curses.A_BOLD), message..'/'..y..','..x)\n    >      check(window.scr[y][x].attrs ~= curses.color_pair(cp), message..'/'..y..','..x)\n    >    end\n    >    x = x+1\n    >    if x > window.scr.w then\n    >      y = y+1\n    >      x = 1\n    >    end\n    >  end\n    >end\n- __teliva_timestamp: original\n  sep:\n    >-- horizontal separator\n    >function sep(window)\n    >  local y, _ = window:getyx()\n    >  window:mvaddstr(y+1, 0, '')\n    >  local _, cols = window:getmaxyx()\n    >  for col=1,cols do\n    >    window:addstr('_')\n    >  end\n    >end\n- __teliva_timestamp: original\n  render:\n    >function render(window)\n    >  window:clear()\n    >  -- draw stuff to screen here\n    >  window:attron(curses.A_BOLD)\n    >  window:mvaddstr(1, 5, \"example app\")\n    >  window:attrset(curses.A_NORMAL)\n    >  for i=0,15 do\n    >    window:attrset(curses.color_pair(i))\n    >    window:mvaddstr(3+i, 5, \"========================\")\n    >  end\n    >  window:refresh()\n    >end\n- __teliva_timestamp: original\n  update:\n    >function update(window)\n    >  local key = window:getch()\n    >  -- process key here\n    >end\n- __teliva_timestamp: original\n  doc:blurb:\n    >A REPL for queries about a graph in a .dot file.\n- __teliva_timestamp: original\n  main:\n    >function main()\n    >  if #arg == 0 then\n    >    Window:clear()\n    >    print('restart this app with the name of a .dot file')\n    >    Window:refresh()\n    >    while true do Window:getch(); end\n    >  end\n    >  Graph = read_dot_file(arg[1])\n    >\n    >  while true do\n    >    render(Window)\n    >    update(Window)\n    >  end\n    >end\n- __teliva_timestamp: original\n  Graph:\n    >Graph = {}\n- __teliva_timestamp: original\n  read_dot_file:\n    >function read_dot_file(filename)\n    >  local graph = {}\n    >  local infile = start_reading(nil, filename)\n    >  if infile then\n    >    local chars = graphviz_buffered_reader(infile)\n    >    local tokens = graphviz_tokenizer(chars)\n    >    parse_graph(tokens, graph)\n    >  end\n    >  return graph\n    >end\n- __teliva_timestamp: original\n  graphviz_buffered_reader:\n    >-- a stream of characters that can peek up to two characters ahead at a time\n    >-- returns nil when there's nothing left to read\n    >function graphviz_buffered_reader(infile)\n    >  return {\n    >    infile = infile,\n    >    peek = infile.read(1),\n    >    peek2 = infile.read(1),\n    >    read = function(self)\n    >      local result = self.peek\n    >      self.peek = self.peek2\n    >      self.peek2 = self.infile.read(1)\n    >      return result\n    >    end,\n    >  }\n    >end\n- __teliva_timestamp: original\n  graphviz_tokenizer:\n    >-- a stream of tokens that can peek up to one token at a time\n    >-- returns nil when there's nothing left to read\n    >function graphviz_tokenizer(chars)\n    >  return {\n    >    chars = chars,\n    >    peek = function(self)\n    >      if not self.buffer then\n    >        self.buffer = self:next_token()\n    >      end\n    >      return self.buffer\n    >    end,\n    >    read = function(self)\n    >      local result\n    >      if self.buffer then\n    >        result = self.buffer\n    >        self.buffer = nil\n    >      else\n    >        result = self:next_token()\n    >      end\n    >      return result\n    >    end,\n    >    next_token = function(self)\n    >      self:skip_whitespace_and_comments()\n    >      local c = self.chars.peek\n    >      if c == nil then return nil end\n    >      if string.pos('/*;,', c) then\n    >        -- should be skipped as comments\n    >        error('unexpected character '..c)\n    >      elseif string.pos('[]{}():=', c) then\n    >        -- single-char tokens\n    >        return self.chars:read()\n    >      elseif c == '\"' then\n    >        return self:string()\n    >      elseif c == '<' then\n    >        error('html strings are not implemented yet')\n    >      elseif c == '-' then\n    >        if self.chars.peek2 == '-' or self.chars.peek2 == '>' then\n    >          return self:edgeop()\n    >        else\n    >          return self:numeral()\n    >        end\n    >      elseif string.pos('.0123456789', c) then\n    >        return self:numeral()\n    >      elseif string.match(c, '[%a_]') then\n    >        return self:identifier()\n    >      else\n    >        error('unexpected character '..str(c))\n    >      end\n    >    end,\n    >    skip_whitespace_and_comments = function(self)\n    >      while true do\n    >        local c = self.chars.peek\n    >        if c == nil then\n    >          break  -- end of chars\n    >        elseif string.match(c, '%s') then\n    >          self.chars:read()\n    >        elseif string.pos(',;', c) then\n    >          self.chars:read()\n    >        elseif c == '#' then\n    >          self.chars:read()  -- skip\n    >          while self.chars:read() ~= '\\n' do end\n    >        elseif c == '/' then\n    >          local c2 = self.chars.peek2\n    >          if c2 == '*' then\n    >            self.chars:read()  -- skip '/'\n    >            self.chars:read()  -- skip '*'\n    >            while true do\n    >              if self.chars.peek == '*' and self.chars.peek2 == '/' then\n    >                self.chars:read()  -- skip '*'\n    >                self.chars:read()  -- skip '/'\n    >                break\n    >              end\n    >            end\n    >          elseif c2 == '/' then\n    >            self.chars:read()  -- skip '/'\n    >            self.chars:read()  -- skip '/'\n    >            while self.chars:read() ~= '\\n' do end\n    >          else\n    >            error('unexpected character after \"/\": '..c)\n    >          end\n    >        else\n    >          break\n    >        end\n    >      end\n    >    end,\n    >    string = function(self)\n    >      assert(self.chars.peek == '\"')\n    >      local result = self.chars:read()\n    >      while true do\n    >        local c = self.chars.peek\n    >        if c == nil then\n    >          error('unterminated string literal')\n    >        end\n    >        result = result..self.chars:read()\n    >        if c == '\\\\' then\n    >          result = result..self.chars:read()  -- unconditionally read next char\n    >        elseif c == '\"' then\n    >          break\n    >        end\n    >      end\n    >      return result\n    >    end,\n    >    numeral = function(self)\n    >      local result = ''\n    >      while true do\n    >        local c = self.chars.peek\n    >        if c == nil then\n    >          return result\n    >        elseif string.pos('-.0123456789', c) then\n    >          result = result..self.chars:read()\n    >        else\n    >          break\n    >        end\n    >      end\n    >      if string.match(self.chars.peek, '%w') then\n    >        error('invalid character after numeral '..result)\n    >      end\n    >      return result\n    >    end,\n    >    identifier = function(self)\n    >      local result = ''\n    >      while true do\n    >        local c = self.chars.peek\n    >        if c == nil then\n    >          error('unterminated string literal')\n    >        elseif string.match(c, '[%w_]') then\n    >          result = result..self.chars:read()\n    >        else\n    >          break\n    >        end\n    >      end\n    >      return result\n    >    end,\n    >    edgeop = function(self)\n    >      return self.chars:read()..self.chars:read()\n    >    end,\n    >  }\n    >end\n    >\n    >function check_tokenizer(stream, expected, msg)\n    >  local infile = fake_file_stream(stream)\n    >  local chars = graphviz_buffered_reader(infile)\n    >  local tokens = graphviz_tokenizer(chars)\n    >  check_eq(tokens:read(), expected, msg)\n    >end\n    >\n    >function test_graphviz_tokenizer()\n    >  check_tokenizer('123',           '123',       'tokenizer: numeral')\n    >  check_tokenizer('  123',         '123',       'tokenizer: skips whitespace')\n    >  check_tokenizer('123 124',       '123',       'tokenizer: numeral')\n    >  check_tokenizer('-12.3 124',     '-12.3',     'tokenizer: numeral')\n    >  check_tokenizer('a123 124',      'a123',      'tokenizer: identifier')\n    >  check_tokenizer('\"abc def\" 124', '\"abc def\"', 'tokenizer: string')\n    >  check_tokenizer('',              nil,         'tokenizer: eof')\n    >end\n- __teliva_timestamp: original\n  parse_graph:\n    >-- https://graphviz.org/doc/info/lang.html\n    >function parse_graph(tokens, graph)\n    >  local t = tokens:read()\n    >  if t == 'strict' then\n    >    t = tokens:read()\n    >  end\n    >  if t == 'graph' then\n    >    error('undirected graphs not supported just yet')\n    >  elseif t == 'digraph' then\n    >    return parse_directed_graph(tokens, graph)\n    >  else\n    >    error('parse_graph: unexpected token '..t)\n    >  end\n    >end\n- __teliva_timestamp:\n    >Fri Mar 18 16:52:39 2022\n  skip_attr_list:\n    >function skip_attr_list(tokens)\n    >  while true do\n    >    local tok = tokens:read()\n    >    if tok == nil then\n    >      error('unterminated attr list; looked for \"]\" in vain')\n    >    end\n    >    if tok == ']' then break end\n    >  end\n    >end\n- __teliva_timestamp:\n    >Fri Mar 18 17:01:49 2022\n  parse_directed_graph:\n    >function parse_directed_graph(tokens, graph)\n    >  tokens:read()  -- skip name\n    >  assert(tokens:read() == '{')\n    >  while true do\n    >    if tokens:peek() == nil then\n    >      error('file not terminated with \"}\"')\n    >    end\n    >    if tokens:peek() == '}' then break end\n    >    local tok1 = tokens:read()\n    >    if tok1 == '[' then\n    >      skip_attr_list(tokens)\n    >    else\n    >      local tok2 = tokens:read()\n    >      if tok2 == '[' then\n    >        -- node_stmt\n    >        skip_attr_list(tokens)\n    >        -- otherwise ignore node declarations;\n    >        -- we'll assume the graph is fully connected\n    >        -- and we can focus on just edges\n    >      elseif tok2 == '->' then\n    >        -- edge_stmt\n    >        local tok3 = tokens:read()\n    >        if graph[tok1] == nil then\n    >          graph[tok1] = {}\n    >        end\n    >        graph[tok1][tok3] = true\n    >      elseif tok2 == '--' then\n    >        error('unexpected token \"--\" in digraph; edges should be directed using \"->\"')\n    >      elseif tok2 == '=' then\n    >        -- id '=' id\n    >        -- skip\n    >        tokens:read()\n    >      else\n    >        error('unexpected token '..tok2)\n    >      end\n    >    end\n    >  end\n    >  assert(tokens:read() == '}')\n    >end\n- __teliva_timestamp:\n    >Fri Mar 18 17:21:16 2022\n  Focus:\n    >-- The focus is a set of nodes we're constantly running\n    >-- certain queries against.\n    >Focus = {}\n- __teliva_timestamp:\n    >Fri Mar 18 17:21:40 2022\n  Graph:\n    >-- The set of edges parsed from the given .dot file.\n    >Graph = {}\n- __teliva_timestamp:\n    >Fri Mar 18 17:29:25 2022\n  render_focus:\n    >function render_focus(window)\n    >  window:attrset(curses.A_BOLD)\n    >  window:mvaddstr(5, 1, 'focus: ')\n    >  window:attrset(curses.A_NORMAL)\n    >  for _, node in ipairs(Focus) do\n    >    window:addstr(node)\n    >    window:addstr(' ')\n    >  end\n    >\n    >  window:mvaddstr(8, 0, '')\n    >  local lines, cols = window:getmaxyx()\n    >  for col=1,cols do\n    >    window:addstr('_')\n    >  end\n    >end\n- __teliva_timestamp:\n    >Fri Mar 18 17:30:11 2022\n  render_queries_on_focus:\n    >function render_queries_on_focus(window)\n    >  window:attrset(curses.A_BOLD)\n    >  window:mvaddstr(10, 1, '')\n    >  -- TODO\n    >end\n- __teliva_timestamp:\n    >Fri Mar 18 17:30:39 2022\n  render:\n    >function render(window)\n    >  window:clear()\n    >  render_basic_stats(window)\n    >  render_focus(window)\n    >  render_queries_on_focus(window)\n    >  window:refresh()\n    >end\n- __teliva_timestamp:\n    >Fri Mar 18 17:35:20 2022\n  sources:\n    >function sources(Graph)\n    >  local is_target = {}\n    >  for source, targets in pairs(Graph) do\n    >    for target, _ in pairs(targets) do\n    >      is_target[target] = true\n    >    end\n    >  end\n    >  local result = {}\n    >  for source, _ in pairs(Graph) do\n    >    if not is_target[source] then\n    >      table.insert(result, source)\n    >    end\n    >  end\n    >  return result\n    >end\n- __teliva_timestamp:\n    >Fri Mar 18 17:35:57 2022\n  render_basic_stats:\n    >function render_basic_stats(window)\n    >  window:attrset(curses.A_BOLD)\n    >  window:mvaddstr(1, 1, 'sources: ')\n    >  window:attrset(curses.A_NORMAL)\n    >  local sources = sources(Graph)\n    >  for _, node in ipairs(sources) do\n    >    window:addstr(node)\n    >    window:addstr(' ')\n    >  end\n    >  window:mvaddstr(3, 0, '')\n    >  local lines, cols = window:getmaxyx()\n    >  for col=1,cols do\n    >    window:addstr('_')\n    >  end\n    >end\n- __teliva_timestamp:\n    >Fri Mar 18 17:51:18 2022\n  main:\n    >function main()\n    >  if #arg == 0 then\n    >    Window:clear()\n    >    print('restart this app with the name of a .dot file')\n    >    Window:refresh()\n    >    while true do Window:getch(); end\n    >  end\n    >  for _, filename in ipairs(arg) do\n    >    read_dot_file(filename, Graph)\n    >  end\n    >\n    >  while true do\n    >    render(Window)\n    >    update(Window)\n    >  end\n    >end\n- __teliva_timestamp:\n    >Fri Mar 18 17:51:32 2022\n  read_dot_file:\n    >function read_dot_file(filename, graph)\n    >  local infile = start_reading(nil, filename)\n    >  if infile then\n    >    local chars = graphviz_buffered_reader(infile)\n    >    local tokens = graphviz_tokenizer(chars)\n    >    parse_graph(tokens, graph)\n    >  end\n    >end\n- __teliva_timestamp:\n    >Fri Mar 18 18:59:24 2022\n  count:\n    >function count(h)\n    >  local result = 0\n    >  for k, v in pairs(h) do\n    >    result = result+1\n    >  end\n    >  return result\n    >end\n- __teliva_timestamp:\n    >Fri Mar 18 18:59:24 2022\n  num_nodes:\n    >function num_nodes(Graph)\n    >  local nodes = {}\n    >  for k, v in pairs(Graph) do\n    >    nodes[k] = true\n    >    for k, v in pairs(v) do\n    >      nodes[k] = true\n    >    end\n    >  end\n    >  local result = 0\n    >  for k, v in pairs(nodes) do\n    >    result = result+1\n    >  end\n    >  return result\n    >end\n- __teliva_timestamp:\n    >Fri Mar 18 19:00:19 2022\n  render_basic_stats:\n    >function render_basic_stats(window)\n    >  window:attrset(curses.A_BOLD)\n    >  window:mvaddstr(1, 1, 'sources: ')\n    >  window:attrset(curses.A_NORMAL)\n    >  local sources = sources(Graph)\n    >  for _, node in ipairs(sources) do\n    >    window:addstr(node)\n    >    window:addstr(' ')\n    >  end\n    >  window:attrset(curses.A_BOLD)\n    >  window:addstr('size: ')\n    >  window:attrset(curses.A_NORMAL)\n    >  window:addstr(tostring(num_nodes(Graph)))\n    >  window:addstr(' nodes')\n    >  window:mvaddstr(3, 0, '')\n    >  local lines, cols = window:getmaxyx()\n    >  for col=1,cols do\n    >    window:addstr('_')\n    >  end\n    >end\n- __teliva_timestamp:\n    >Fri Mar 18 19:01:49 2022\n  main:\n    >function main()\n    >  if #arg == 0 then\n    >    Window:clear()\n    >    print('restart this app with the name of a .dot file')\n    >    Window:refresh()\n    >    while true do Window:getch(); end\n    >  end\n    >  for _, filename in ipairs(arg) do\n    >    read_dot_file(filename, Graph)\n    >  end\n    >  Focus = sources(Graph)\n    >\n    >  while true do\n    >    render(Window)\n    >    update(Window)\n    >  end\n    >end\n- __teliva_timestamp:\n    >Fri Mar 18 19:09:56 2022\n  reachable:\n    >function reachable(graph, node)\n    >  local reached = {}\n    >  local todo = {node}\n    >  while #todo > 0 do\n    >    local curr = table.remove(todo)\n    >    if reached[curr] == nil then\n    >      reached[curr] = true\n    >      local targets = graph[curr]\n    >      if targets then\n    >        for target, _ in pairs(graph[curr]) do\n    >          table.insert(todo, target)\n    >        end\n    >      end\n    >    end\n    >  end\n    >  return reached\n    >end\n- __teliva_timestamp:\n    >Fri Mar 18 20:27:16 2022\n  bold:\n    >function bold(window, text)\n    >  window:attrset(curses.A_BOLD)\n    >  window:addstr(text)\n    >  window:attrset(curses.A_NORMAL)\n    >end\n- __teliva_timestamp:\n    >Fri Mar 18 20:30:39 2022\n  render_queries_on_focus:\n    >function render_queries_on_focus(window)\n    >  render_reachable_sets(window)\n    >end\n- __teliva_timestamp:\n    >Fri Mar 18 20:30:39 2022\n  render_reachable_sets:\n    >function render_reachable_sets(window)\n    >  local deps = {}\n    >  local needed_by = {}\n    >  for _, node in ipairs(Focus) do\n    >    deps[node] = reachable(Graph, node)\n    >    for dep, _ in pairs(deps[node]) do\n    >      if needed_by[dep] == nil then\n    >        needed_by[dep] = {}\n    >      end\n    >      append(needed_by[dep], {node})\n    >    end\n    >  end\n    >  for k, v in ipairs(needed_by) do\n    >    table.sort(v)\n    >  end\n    >  window:mvaddstr(10, 0, '')\n    >  local sets = {Focus}  -- queue\n    >  local done = {}\n    >  while #sets > 0 do\n    >    local from_nodes = table.remove(sets, 1)\n    >    if #from_nodes == 0 then break end\n    >    table.sort(from_nodes)\n    >    local key = table.concat(from_nodes)\n    >    if done[key] == nil then\n    >      done[key] = true\n    >      local y, x = window:getyx()\n    >      window:mvaddstr(y+2, 0, '')\n    >      window:attrset(curses.A_BOLD)\n    >      render_list(window, from_nodes)\n    >      window:attrset(curses.A_NORMAL)\n    >      window:addstr(' -> ')\n    >      render_set(window, filter(needed_by, function(node, users) return set_eq(users, from_nodes) end))\n    >      for i, elem in ipairs(from_nodes) do\n    >        table.insert(sets, all_but(from_nodes, i))\n    >      end\n    >    end\n    >  end\n    >end\n- __teliva_timestamp:\n    >Fri Mar 18 20:32:18 2022\n  render_list:\n    >function render_list(window, l)\n    >  window:addstr('{')\n    >  for i, node in ipairs(l) do\n    >    if i > 1 then window:addstr(' ') end\n    >    window:addstr(node)\n    >  end\n    >  window:addstr('}')\n    >end\n- __teliva_timestamp:\n    >Fri Mar 18 20:32:18 2022\n  render_set:\n    >function render_set(window, h)\n    >  window:addstr('(')\n    >  window:addstr(count(h))\n    >  window:addstr(') ')\n    >  for node, _ in pairs(h) do\n    >    window:addstr(node)\n    >    window:addstr(' ')\n    >  end\n    >end\n- __teliva_timestamp:\n    >Sat Mar 19 09:19:10 2022\n  main:\n    >function main()\n    >  if #arg == 0 then\n    >    Window:clear()\n    >    print('restart this app with the name of a .dot file')\n    >    Window:refresh()\n    >    while true do Window:getch(); end\n    >  end\n    >  for _, filename in ipairs(arg) do\n    >    read_dot_file(filename, Graph)\n    >  end\n    >  Focus = sources(Graph)\n    >  Nodes = toposort(Graph)\n    >\n    >  while true do\n    >    render(Window)\n    >    update(Window)\n    >  end\n    >end\n- __teliva_timestamp:\n    >Sat Mar 19 09:32:33 2022\n  nodes:\n    >function nodes(graph)\n    >  local result = {}\n    >  for n, deps in pairs(graph) do\n    >    result[n] = true\n    >    for n, _ in pairs(deps) do\n    >      result[n] = true\n    >    end\n    >  end\n    >  return result\n    >end\n- __teliva_timestamp:\n    >Sat Mar 19 16:27:32 2022\n  toposort:\n    >-- stable sort of nodes in a graph\n    >-- nodes always occur before all their dependencies\n    >-- disconnected nodes are in alphabetical order\n    >function toposort(graph)\n    >  -- non-map variables are arrays\n    >  -- result = leaves in graph\n    >  -- candidates = non-leaves\n    >  local result = {}\n    >  local resultMap = {}\n    >  local candidatesMap = nodes(graph)\n    >  local leavesMap = filter(candidatesMap, function(k, v) return graph[k] == nil end)\n    >  local leaves = to_array(leavesMap)\n    >  table.sort(leaves)\n    >  union(resultMap, leavesMap)\n    >  prepend(result, leaves)\n    >  subtract(candidatesMap, leavesMap)\n    >\n    >  function in_result(x, _) return resultMap[x] end\n    >  function all_deps_in_result(k, _) return all(graph[k], in_result) end\n    >  while true do\n    >    local oldcount = count(candidatesMap)\n    >    if oldcount == 0 then break end\n    >    local inducteesMap = filter(candidatesMap, all_deps_in_result)\n    >    local inductees = to_array(inducteesMap)\n    >    table.sort(inductees)\n    >    union(resultMap, inducteesMap)\n    >    prepend(result, inductees)\n    >    subtract(candidatesMap, inducteesMap)\n    >    if oldcount == count(candidatesMap) then\n    >      error('toposort: graph is not connected')\n    >    end\n    >  end\n    >  return result\n    >end\n- __teliva_timestamp:\n    >Sat Mar 19 16:32:24 2022\n  render_focus:\n    >function render_focus(window)\n    >  local y, _ = window:getyx()\n    >  window:mvaddstr(y+1, 0, '')\n    >  bold(window, 'focus: ')\n    >  for _, node in ipairs(Focus) do\n    >    window:addstr(node)\n    >    window:addstr(' ')\n    >  end\n    >  sep(window)\n    >end\n- __teliva_timestamp:\n    >Sat Mar 19 16:33:19 2022\n  render_basic_stats:\n    >function render_basic_stats(window)\n    >  bold(window, tostring(#Nodes)..' nodes: ')\n    >  for i, node in ipairs(Nodes) do\n    >    window:attrset(curses.A_REVERSE)\n    >    window:addstr(i)\n    >    window:attrset(curses.A_NORMAL)\n    >    window:addstr(' ')\n    >    window:addstr(node)\n    >    window:addstr(' ')\n    >  end\n    >  sep(window)\n    >end\n- __teliva_timestamp:\n    >Sat Mar 19 16:35:34 2022\n  render_reachable_sets:\n    >function render_reachable_sets(window)\n    >  local deps = {}\n    >  local needed_by = {}\n    >  for _, node in ipairs(Focus) do\n    >    deps[node] = reachable(Graph, node)\n    >    for dep, _ in pairs(deps[node]) do\n    >      if needed_by[dep] == nil then\n    >        needed_by[dep] = {}\n    >      end\n    >      append(needed_by[dep], {node})\n    >    end\n    >  end\n    >  for k, v in ipairs(needed_by) do\n    >    table.sort(v)\n    >  end\n    >  local sets = {Focus}  -- queue\n    >  local done = {}\n    >  while #sets > 0 do\n    >    local from_nodes = table.remove(sets, 1)\n    >    if #from_nodes == 0 then break end\n    >    table.sort(from_nodes)\n    >    local key = table.concat(from_nodes)\n    >    if done[key] == nil then\n    >      done[key] = true\n    >      local y, x = window:getyx()\n    >      window:mvaddstr(y+2, 0, '')\n    >      window:attrset(curses.A_BOLD)\n    >      render_list(window, from_nodes)\n    >      window:attrset(curses.A_NORMAL)\n    >      window:addstr(' -> ')\n    >      render_set(window, filter(needed_by, function(node, users) return set_eq(users, from_nodes) end))\n    >      for i, elem in ipairs(from_nodes) do\n    >        table.insert(sets, all_but(from_nodes, i))\n    >      end\n    >    end\n    >  end\n    >end\n- __teliva_timestamp:\n    >Sat Mar 19 21:05:05 2022\n  toposort:\n    >-- stable sort of nodes in a graph\n    >-- nodes always occur before all their dependencies\n    >-- disconnected nodes are in alphabetical order\n    >function toposort(graph)\n    >  -- non-map variables are arrays\n    >  -- result = leaves in graph\n    >  -- candidates = non-leaves\n    >  local inResultMap = {}\n    >  local candidatesMap = nodes(graph)\n    >  local leavesMap = filter(candidatesMap, function(k, v) return graph[k] == nil end)\n    >  local leaves = to_array(leavesMap)\n    >  table.sort(leaves)\n    >  union(inResultMap, leavesMap)\n    >  local result = {leaves}\n    >  subtract(candidatesMap, leavesMap)\n    >\n    >  function in_result(x, _) return inResultMap[x] end\n    >  function all_deps_in_result(k, _) return all(graph[k], in_result) end\n    >  while true do\n    >    local oldcount = count(candidatesMap)\n    >    if oldcount == 0 then break end\n    >    local inducteesMap = filter(candidatesMap, all_deps_in_result)\n    >    local inductees = to_array(inducteesMap)\n    >    table.sort(inductees)\n    >    union(inResultMap, inducteesMap)\n    >    table.insert(result, 1, inductees)\n    >    subtract(candidatesMap, inducteesMap)\n    >    if oldcount == count(candidatesMap) then\n    >      error('toposort: graph is not connected')\n    >    end\n    >  end\n    >  return result\n    >end\n- __teliva_timestamp:\n    >Sat Mar 19 21:05:57 2022\n  render_basic_stats:\n    >function render_basic_stats(window)\n    >  bold(window, tostring(#Nodes)..' nodes:')\n    >  local i = 1\n    >  for _, stratum in ipairs(Nodes) do\n    >    window:addstr('\\n  ')\n    >    for _, node in ipairs(stratum) do\n    >      window:attrset(curses.A_REVERSE)\n    >      window:addstr(i)\n    >      window:attrset(curses.A_NORMAL)\n    >      window:addstr(' ')\n    >      window:addstr(node)\n    >      window:addstr(' ')\n    >      i = i+1\n    >    end\n    >  end\n    >  sep(window)\n    >end\n"
  },
  {
    "path": "hanoi.tlv",
    "content": "# .tlv file generated by https://github.com/akkartik/teliva\n# You may edit it if you are careful; however, you may see cryptic errors if you\n# violate Teliva's assumptions.\n#\n# .tlv files are representations of Teliva programs. Teliva programs consist of\n# sequences of definitions. Each definition is a table of key/value pairs. Keys\n# and values are both strings.\n#\n# Lines in .tlv files always follow exactly one of the following forms:\n# - comment lines at the top of the file starting with '#' at column 0\n# - beginnings of definitions starting with '- ' at column 0, followed by a\n#   key/value pair\n# - key/value pairs consisting of '  ' at column 0, containing either a\n#   spaceless value on the same line, or a multi-line value\n# - multiline values indented by more than 2 spaces, starting with a '>'\n#\n# If these constraints are violated, Teliva may unceremoniously crash. Please\n# report bugs at http://akkartik.name/contact\n- __teliva_timestamp: original\n  check:\n    >function check(x, msg)\n    >  if x then\n    >    Window:addch('.')\n    >  else\n    >    print('F - '..msg)\n    >    print('  '..str(x)..' is false/nil')\n    >    teliva_num_test_failures = teliva_num_test_failures + 1\n    >    -- overlay first test failure on editors\n    >    if teliva_first_failure == nil then\n    >      teliva_first_failure = msg\n    >    end\n    >  end\n    >end\n- __teliva_timestamp: original\n  check_eq:\n    >function check_eq(x, expected, msg)\n    >  if eq(x, expected) then\n    >    Window:addch('.')\n    >  else\n    >    print('F - '..msg)\n    >    print('  expected '..str(expected)..' but got '..str(x))\n    >    teliva_num_test_failures = teliva_num_test_failures + 1\n    >    -- overlay first test failure on editors\n    >    if teliva_first_failure == nil then\n    >      teliva_first_failure = msg\n    >    end\n    >  end\n    >end\n- __teliva_timestamp: original\n  eq:\n    >function eq(a, b)\n    >  if type(a) ~= type(b) then return false end\n    >  if type(a) == 'table' then\n    >    if #a ~= #b then return false end\n    >    for k, v in pairs(a) do\n    >      if b[k] ~= v then\n    >        return false\n    >      end\n    >    end\n    >    for k, v in pairs(b) do\n    >      if a[k] ~= v then\n    >        return false\n    >      end\n    >    end\n    >    return true\n    >  end\n    >  return a == b\n    >end\n- __teliva_timestamp: original\n  str:\n    >-- smarter tostring\n    >-- slow; used only for debugging\n    >function str(x)\n    >  if type(x) == 'table' then\n    >    local result = ''\n    >    result = result..#x..'{'\n    >    for k, v in pairs(x) do\n    >      result = result..str(k)..'='..str(v)..', '\n    >    end\n    >    result = result..'}'\n    >    return result\n    >  elseif type(x) == 'string' then\n    >    return '\"'..x..'\"'\n    >  end\n    >  return tostring(x)\n    >end\n- __teliva_timestamp: original\n  render:\n    >function render(window)\n    >  window:clear()\n    >  local lines, cols = window:getmaxyx()\n    >  local line = math.floor(lines/2)\n    >  local col = math.floor(cols/4)\n    >  for i,t in ipairs(tower) do\n    >    render_tower(window, line, i*col, i, t)\n    >  end\n    >  window:refresh()\n    >end\n- __teliva_timestamp: original\n  lines:\n    >function lines(window)\n    >  local lines, cols = window:getmaxyx()\n    >  return lines\n    >end\n- __teliva_timestamp: original\n  pop:\n    >function pop(array)\n    >  return table.remove(array)\n    >end\n- __teliva_timestamp: original\n  Window:\n    >Window = curses.stdscr()\n- __teliva_timestamp: original\n  render_tower:\n    >function render_tower(window, line, col, tower_index, tower)\n    >  window:attron(curses.A_BOLD)\n    >  window:mvaddch(line+2, col, string.char(96+tower_index))\n    >  window:attroff(curses.A_BOLD)\n    >  window:attron(curses.color_pair(15))\n    >  window:mvaddstr(line+1, col-6, \"              \")\n    >  window:attroff(curses.color_pair(15))\n    >  for i, n in ipairs(tower) do\n    >    render_disk(window, line, col, n)\n    >    line = line - 1\n    >  end\n    >  for i=1,5-len(tower)+1 do\n    >    window:attron(curses.color_pair(15))\n    >    window:mvaddstr(line, col, \"  \")\n    >    window:attroff(curses.color_pair(15))\n    >    line = line - 1\n    >  end\n    >end\n- __teliva_timestamp: original\n  tower:\n    >tower = {{6, 5, 4, 3, 2}, {}, {}}\n- __teliva_timestamp: original\n  render_disk:\n    >function render_disk(window, line, col, size)\n    >  col = col-size+1\n    >  for i=1,size do\n    >    window:attron(curses.color_pair(size))\n    >    window:mvaddstr(line, col, \"  \")\n    >    window:attroff(curses.color_pair(size))\n    >    col = col+2\n    >  end\n    >end\n- __teliva_timestamp: original\n  main:\n    >function main()\n    >  curses.assume_default_colors(238, 139)\n    >  for i=1,7 do\n    >    curses.init_pair(i, 0, i)\n    >  end\n    >  curses.init_pair(15, 0, 250)  -- tower frames\n    >\n    >  while true do\n    >    render(Window)\n    >    update(Window)\n    >  end\n    >end\n- __teliva_timestamp: original\n  len:\n    >function len(array)\n    >  local result = 0\n    >  for k in pairs(array) do\n    >    result = result+1\n    >  end\n    >  return result\n    >end\n- __teliva_timestamp: original\n  update:\n    >function update(window)\n    >  window:mvaddstr(lines(window)-2, 5, \"tower to remove top disk from? \")\n    >  local from = window:getch() - 96\n    >  window:mvaddstr(lines(window)-1, 5, \"tower to stack it on? \")\n    >  local to = window:getch() - 96\n    >  make_move(from, to)\n    >end\n- __teliva_timestamp: original\n  make_move:\n    >function make_move(from, to)\n    >  local disk = pop(tower[from])\n    >  table.insert(tower[to], disk)\n    >end\n- __teliva_timestamp: original\n  cols:\n    >function cols(window)\n    >  local lines, cols = window:getmaxyx()\n    >  return cols\n    >end\n- __teliva_timestamp:\n    >Thu Feb 17 20:07:06 2022\n  doc:blurb:\n    >Single-player game: the Towers of Hanoi\n    >\n    >Move disks around from one tower A to tower B, under one constraint: a disk can never lie above a smaller disk.\n    >\n    >https://en.wikipedia.org/wiki/Tower_of_Hanoi\n"
  },
  {
    "path": "life.tlv",
    "content": "# .tlv file generated by https://github.com/akkartik/teliva\n# You may edit it if you are careful; however, you may see cryptic errors if you\n# violate Teliva's assumptions.\n#\n# .tlv files are representations of Teliva programs. Teliva programs consist of\n# sequences of definitions. Each definition is a table of key/value pairs. Keys\n# and values are both strings.\n#\n# Lines in .tlv files always follow exactly one of the following forms:\n# - comment lines at the top of the file starting with '#' at column 0\n# - beginnings of definitions starting with '- ' at column 0, followed by a\n#   key/value pair\n# - key/value pairs consisting of '  ' at column 0, containing either a\n#   spaceless value on the same line, or a multi-line value\n# - multiline values indented by more than 2 spaces, starting with a '>'\n#\n# If these constraints are violated, Teliva may unceremoniously crash. Please\n# report bugs at http://akkartik.name/contact\n- __teliva_timestamp: original\n  str_helpers:\n    >-- some string helpers from http://lua-users.org/wiki/StringIndexing\n    >\n    >-- index characters using []\n    >getmetatable('').__index = function(str,i)\n    >  if type(i) == 'number' then\n    >    return str:sub(i,i)\n    >  else\n    >    return string[i]\n    >  end\n    >end\n    >\n    >-- ranges using (), selected bytes using {}\n    >getmetatable('').__call = function(str,i,j)\n    >  if type(i)~='table' then\n    >    return str:sub(i,j)\n    >  else\n    >    local t={}\n    >    for k,v in ipairs(i) do\n    >      t[k]=str:sub(v,v)\n    >    end\n    >    return table.concat(t)\n    >  end\n    >end\n    >\n    >-- iterate over an ordered sequence\n    >function q(x)\n    >  if type(x) == 'string' then\n    >    return x:gmatch('.')\n    >  else\n    >    return ipairs(x)\n    >  end\n    >end\n    >\n    >-- insert within string\n    >function string.insert(str1, str2, pos)\n    >  return str1:sub(1,pos)..str2..str1:sub(pos+1)\n    >end\n    >\n    >function string.remove(s, pos)\n    >  return s:sub(1,pos-1)..s:sub(pos+1)\n    >end\n    >\n    >function string.pos(s, sub)\n    >  return string.find(s, sub, 1, true)  -- plain=true to disable regular expressions\n    >end\n    >\n    >-- TODO: backport utf-8 support from Lua 5.3\n- __teliva_timestamp: original\n  debugy:\n    >debugy = 5\n- __teliva_timestamp: original\n  dbg:\n    >-- helper for debug by print; overlay debug information towards the right\n    >-- reset debugy every time you refresh screen\n    >function dbg(window, s)\n    >  local oldy = 0\n    >  local oldx = 0\n    >  oldy, oldx = window:getyx()\n    >  window:mvaddstr(debugy, 60, s)\n    >  debugy = debugy+1\n    >  window:mvaddstr(oldy, oldx, '')\n    >end\n- __teliva_timestamp: original\n  check:\n    >function check(x, msg)\n    >  if x then\n    >    Window:addch('.')\n    >  else\n    >    print('F - '..msg)\n    >    print('  '..str(x)..' is false/nil')\n    >    teliva_num_test_failures = teliva_num_test_failures + 1\n    >    -- overlay first test failure on editors\n    >    if teliva_first_failure == nil then\n    >      teliva_first_failure = msg\n    >    end\n    >  end\n    >end\n- __teliva_timestamp: original\n  check_eq:\n    >function check_eq(x, expected, msg)\n    >  if eq(x, expected) then\n    >    Window:addch('.')\n    >  else\n    >    print('F - '..msg)\n    >    print('  expected '..str(expected)..' but got '..str(x))\n    >    teliva_num_test_failures = teliva_num_test_failures + 1\n    >    -- overlay first test failure on editors\n    >    if teliva_first_failure == nil then\n    >      teliva_first_failure = msg\n    >    end\n    >  end\n    >end\n- __teliva_timestamp: original\n  eq:\n    >function eq(a, b)\n    >  if type(a) ~= type(b) then return false end\n    >  if type(a) == 'table' then\n    >    if #a ~= #b then return false end\n    >    for k, v in pairs(a) do\n    >      if b[k] ~= v then\n    >        return false\n    >      end\n    >    end\n    >    for k, v in pairs(b) do\n    >      if a[k] ~= v then\n    >        return false\n    >      end\n    >    end\n    >    return true\n    >  end\n    >  return a == b\n    >end\n- __teliva_timestamp: original\n  str:\n    >-- smarter tostring\n    >-- slow; used only for debugging\n    >function str(x)\n    >  if type(x) == 'table' then\n    >    local result = ''\n    >    result = result..#x..'{'\n    >    for k, v in pairs(x) do\n    >      result = result..str(k)..'='..str(v)..', '\n    >    end\n    >    result = result..'}'\n    >    return result\n    >  elseif type(x) == 'string' then\n    >    return '\"'..x..'\"'\n    >  end\n    >  return tostring(x)\n    >end\n- __teliva_timestamp: original\n  menu:\n    >-- To show app-specific hotkeys in the menu bar, add hotkey/command\n    >-- arrays of strings to the menu array.\n    >menu = {}\n- __teliva_timestamp: original\n  window:\n    >-- constructor for fake screen and window\n    >-- call it like this:\n    >--   local w = window{\n    >--     kbd=kbd('abc'),\n    >--     scr=scr{h=5, w=4},\n    >--   }\n    >-- eventually it'll do everything a real ncurses window can\n    >function window(h)\n    >  h.__index = h\n    >  setmetatable(h, h)\n    >  h.__index = function(table, key)\n    >    return rawget(h, key)\n    >  end\n    >  h.attrset = function(self, x)\n    >    self.scr.attrs = x\n    >  end\n    >  h.attron = function(self, x)\n    >    -- currently same as attrset since Lua 5.1 doesn't have bitwise operators\n    >    -- doesn't support multiple attrs at once\n    >--    local old = self.scr.attrs\n    >--    self.scr.attrs = old|x\n    >    self.scr.attrs = x\n    >  end\n    >  h.attroff = function(self, x)\n    >    -- currently borked since Lua 5.1 doesn't have bitwise operators\n    >    -- doesn't support multiple attrs at once\n    >--    local old = self.scr.attrs\n    >--    self.scr.attrs = old & (~x)\n    >    self.scr.attrs = curses.A_NORMAL\n    >  end\n    >  h.getch = function(self)\n    >    local c = table.remove(h.kbd, 1)\n    >    if c == nil then return c end\n    >    return string.byte(c)  -- for verisimilitude with ncurses\n    >  end\n    >  h.addch = function(self, c)\n    >    local scr = self.scr\n    >    if c == '\\n' then\n    >      scr.cursy = scr.cursy+1\n    >      scr.cursx = 0\n    >      return\n    >    end\n    >    if scr.cursy <= scr.h then\n    >      scr[scr.cursy][scr.cursx] = {data=c, attrs=scr.attrs}\n    >      scr.cursx = scr.cursx+1\n    >      if scr.cursx > scr.w then\n    >        scr.cursy = scr.cursy+1\n    >        scr.cursx = 1\n    >      end\n    >    end\n    >  end\n    >  h.addstr = function(self, s)\n    >    for i=1,s:len() do\n    >      self:addch(s[i])\n    >    end\n    >  end\n    >  h.mvaddch = function(self, y, x, c)\n    >    self.scr.cursy = y\n    >    self.scr.cursx = x\n    >    self:addch(c)\n    >  end\n    >  h.mvaddstr = function(self, y, x, s)\n    >    self.scr.cursy = y\n    >    self.scr.cursx = x\n    >    self:addstr(s)\n    >  end\n    >  h.clear = function(self)\n    >    clear_scr(self.scr)\n    >  end\n    >  h.refresh = function(self)\n    >    -- nothing\n    >  end\n    >  return h\n    >end\n- __teliva_timestamp: original\n  kbd:\n    >function kbd(keys)\n    >  local result = {}\n    >  for i=1,keys:len() do\n    >    table.insert(result, keys[i])\n    >  end\n    >  return result\n    >end\n- __teliva_timestamp: original\n  scr:\n    >function scr(props)\n    >  props.cursx = 1\n    >  props.cursy = 1\n    >  clear_scr(props)\n    >  return props\n    >end\n- __teliva_timestamp: original\n  clear_scr:\n    >function clear_scr(props)\n    >  props.cursy = 1\n    >  props.cursx = 1\n    >  for y=1,props.h do\n    >    props[y] = {}\n    >    for x=1,props.w do\n    >      props[y][x] = {data=' ', attrs=curses.A_NORMAL}\n    >    end\n    >  end\n    >  return props\n    >end\n- __teliva_timestamp: original\n  check_screen:\n    >function check_screen(window, contents, message)\n    >  local x, y = 1, 1\n    >  for i=1,contents:len() do\n    >    check_eq(window.scr[y][x].data, contents[i], message..'/'..y..','..x)\n    >    x = x+1\n    >    if x > window.scr.w then\n    >      y = y+1\n    >      x = 1\n    >    end\n    >  end\n    >end\n    >\n    >-- putting it all together, an example test of both keyboard and screen\n    >function test_check_screen()\n    >  local lines = {\n    >    c='123',\n    >    d='234',\n    >    a='345',\n    >    b='456',\n    >  }\n    >  local w = window{\n    >    kbd=kbd('abc'),\n    >    scr=scr{h=3, w=5},\n    >  }\n    >  local y = 1\n    >  while true do\n    >    local b = w:getch()\n    >    if b == nil then break end\n    >    w:mvaddstr(y, 1, lines[string.char(b)])\n    >    y = y+1\n    >  end\n    >  check_screen(w, '345  '..\n    >                  '456  '..\n    >                  '123  ',\n    >              'test_check_screen')\n    >end\n- __teliva_timestamp: original\n  grid:\n    >-- main data structure\n    >grid = {}\n    >for i=1,lines*4 do\n    >  grid[i] = {}\n    >  for j=1,cols*2 do\n    >    grid[i][j] = 0\n    >  end\n    >end\n- __teliva_timestamp: original\n  Window:\n    >Window = curses.stdscr()\n    >-- animation-based app\n    >Window:nodelay(true)\n    >curses.curs_set(0)\n    >lines, cols = Window:getmaxyx()\n- __teliva_timestamp: original\n  grid_char:\n    >-- grab a 4x2 chunk of grid\n    >function grid_char(line, col)\n    >  result = {}\n    >  for l, row in ipairs({unpack(grid, (line-1)*4+1, line*4)}) do\n    >    result[l] = {unpack(row, (col-1)*2+1, col*2)}\n    >  end\n    >  return result\n    >end\n- __teliva_timestamp: original\n  print_grid_char:\n    >function print_grid_char(window, x)\n    >  result = {}\n    >  for l, row in ipairs(x) do\n    >    for c, val in ipairs(row) do\n    >      window:mvaddstr(l, c, val)\n    >    end\n    >  end\n    >  return result\n    >end\n- __teliva_timestamp: original\n  glyph:\n    >-- look up the braille pattern corresponding to a 4x2 chunk of grid\n    >-- https://en.wikipedia.org/wiki/Braille_Patterns\n    >-- not obviously programmatic because Unicode added 4x2 after 3x2\n    >glyph = {\n    >  0x2800, 0x2801, 0x2802, 0x2803, 0x2804, 0x2805, 0x2806, 0x2807,   0x2840, 0x2841, 0x2842, 0x2843, 0x2844, 0x2845, 0x2846, 0x2847,\n    >  0x2808, 0x2809, 0x280a, 0x280b, 0x280c, 0x280d, 0x280e, 0x280f,   0x2848, 0x2849, 0x284a, 0x284b, 0x284c, 0x284d, 0x284e, 0x284f,\n    >  0x2810, 0x2811, 0x2812, 0x2813, 0x2814, 0x2815, 0x2816, 0x2817,   0x2850, 0x2851, 0x2852, 0x2853, 0x2854, 0x2855, 0x2856, 0x2857,\n    >  0x2818, 0x2819, 0x281a, 0x281b, 0x281c, 0x281d, 0x281e, 0x281f,   0x2858, 0x2859, 0x285a, 0x285b, 0x285c, 0x285d, 0x285e, 0x285f,\n    >  0x2820, 0x2821, 0x2822, 0x2823, 0x2824, 0x2825, 0x2826, 0x2827,   0x2860, 0x2861, 0x2862, 0x2863, 0x2864, 0x2865, 0x2866, 0x2867,\n    >  0x2828, 0x2829, 0x282a, 0x282b, 0x282c, 0x282d, 0x282e, 0x282f,   0x2868, 0x2869, 0x286a, 0x286b, 0x286c, 0x286d, 0x286e, 0x286f,\n    >  0x2830, 0x2831, 0x2832, 0x2833, 0x2834, 0x2835, 0x2836, 0x2837,   0x2870, 0x2871, 0x2872, 0x2873, 0x2874, 0x2875, 0x2876, 0x2877,\n    >  0x2838, 0x2839, 0x283a, 0x283b, 0x283c, 0x283d, 0x283e, 0x283f,   0x2878, 0x2879, 0x287a, 0x287b, 0x287c, 0x287d, 0x287e, 0x287f,\n    >\n    >  0x2880, 0x2881, 0x2882, 0x2883, 0x2884, 0x2885, 0x2886, 0x2887,   0x28c0, 0x28c1, 0x28c2, 0x28c3, 0x28c4, 0x28c5, 0x28c6, 0x28c7,\n    >  0x2888, 0x2889, 0x288a, 0x288b, 0x288c, 0x288d, 0x288e, 0x288f,   0x28c8, 0x28c9, 0x28ca, 0x28cb, 0x28cc, 0x28cd, 0x28ce, 0x28cf,\n    >  0x2890, 0x2891, 0x2892, 0x2893, 0x2894, 0x2895, 0x2896, 0x2897,   0x28d0, 0x28d1, 0x28d2, 0x28d3, 0x28d4, 0x28d5, 0x28d6, 0x28d7,\n    >  0x2898, 0x2899, 0x289a, 0x289b, 0x289c, 0x289d, 0x289e, 0x289f,   0x28d8, 0x28d9, 0x28da, 0x28db, 0x28dc, 0x28dd, 0x28de, 0x28df,\n    >  0x28a0, 0x28a1, 0x28a2, 0x28a3, 0x28a4, 0x28a5, 0x28a6, 0x28a7,   0x28e0, 0x28e1, 0x28e2, 0x28e3, 0x28e4, 0x28e5, 0x28e6, 0x28e7,\n    >  0x28a8, 0x28a9, 0x28aa, 0x28ab, 0x28ac, 0x28ad, 0x28ae, 0x28af,   0x28e8, 0x28e9, 0x28ea, 0x28eb, 0x28ec, 0x28ed, 0x28ee, 0x28ef,\n    >  0x28b0, 0x28b1, 0x28b2, 0x28b3, 0x28b4, 0x28b5, 0x28b6, 0x28b7,   0x28f0, 0x28f1, 0x28f2, 0x28f3, 0x28f4, 0x28f5, 0x28f6, 0x28f7,\n    >  0x28b8, 0x28b9, 0x28ba, 0x28bb, 0x28bc, 0x28bd, 0x28be, 0x28bf,   0x28f8, 0x28f9, 0x28fa, 0x28fb, 0x28fc, 0x28fd, 0x28fe, 0x28ff,\n    >}\n- __teliva_timestamp: original\n  utf8:\n    >-- https://stackoverflow.com/questions/7983574/how-to-write-a-unicode-symbol-in-lua\n    >function utf8(decimal)\n    >  local bytemarkers = { {0x7FF,192}, {0xFFFF,224}, {0x1FFFFF,240} }\n    >  if decimal<128 then return string.char(decimal) end\n    >  local charbytes = {}\n    >  for bytes,vals in ipairs(bytemarkers) do\n    >    if decimal<=vals[1] then\n    >      for b=bytes+1,2,-1 do\n    >        local mod = decimal%64\n    >        decimal = (decimal-mod)/64\n    >        charbytes[b] = string.char(128+mod)\n    >      end\n    >      charbytes[1] = string.char(vals[2]+decimal)\n    >      break\n    >    end\n    >  end\n    >  return table.concat(charbytes)\n    >end\n- __teliva_timestamp: original\n  grid_char_to_glyph_index:\n    >-- convert a chunk of grid into a number\n    >function grid_char_to_glyph_index(g)\n    >  return g[1][1]    + g[2][1]*2  + g[3][1]*4  + g[4][1]*8 +\n    >         g[1][2]*16 + g[2][2]*32 + g[3][2]*64 + g[4][2]*128 +\n    >         1  -- 1-indexing\n    >end\n- __teliva_timestamp: original\n  render:\n    >function render(window)\n    >  window:clear()\n    >  window:attron(curses.color_pair(1))\n    >  for line=1,lines do\n    >    for col=1,cols do\n    >      window:addstr(utf8(glyph[grid_char_to_glyph_index(grid_char(line, col))]))\n    >    end\n    >  end\n    >  window:attroff(curses.color_pair(1))\n    >  window:refresh()\n    >end\n- __teliva_timestamp: original\n  state:\n    >function state(line, col)\n    >  if line < 1 or line > #grid or col < 1 or col > #grid[1] then\n    >    return 0\n    >  end\n    >  return grid[line][col]\n    >end\n- __teliva_timestamp: original\n  num_live_neighbors:\n    >function num_live_neighbors(line, col)\n    >  return state(line-1, col-1) + state(line-1, col) + state(line-1, col+1) +\n    >         state(line,   col-1) +                      state(line,   col+1) +\n    >         state(line+1, col-1) + state(line+1, col) + state(line+1, col+1)\n    >end\n- __teliva_timestamp: original\n  step:\n    >function step()\n    >  local new_grid = {}\n    >  for line=1,#grid do\n    >    new_grid[line] = {}\n    >    for col=1,#grid[1] do\n    >      local n = num_live_neighbors(line, col)\n    >      if n == 3 then\n    >        new_grid[line][col] = 1\n    >      elseif n == 2 then\n    >        new_grid[line][col] = grid[line][col]\n    >      else\n    >        new_grid[line][col] = 0\n    >      end\n    >    end\n    >  end\n    >  grid = new_grid\n    >end\n- __teliva_timestamp: original\n  sleep:\n    >function sleep(a)\n    >    local sec = tonumber(os.clock() + a);\n    >    while (os.clock() < sec) do\n    >    end\n    >end\n- __teliva_timestamp: original\n  load_file:\n    >function load_file(window, fs, filename)\n    >  local infile = start_reading(fs, filename)\n    >  if infile == nil then return end\n    >  local line_index = lines  -- quarter of the way down in pixels\n    >  while true do\n    >    local line = infile.read()\n    >    if line == nil then break end\n    >    if line:sub(1,1) ~= '!' then  -- comment; plaintext files can't have whitespace before comments\n    >      local col_index = cols\n    >      for c in line:gmatch(\".\") do\n    >        if c == '\\r' then break end  -- DOS line ending\n    >        if c == '.' then\n    >          grid[line_index][col_index] = 0\n    >        else\n    >          grid[line_index][col_index] = 1\n    >        end\n    >        col_index = col_index+1\n    >      end\n    >      line_index = line_index+1\n    >    end\n    >  end\n    >end\n- __teliva_timestamp: original\n  update:\n    >menu = {{\"arrow\", \"pan\"}}\n    >\n    >function update(window, c)\n    >  if c == curses.KEY_LEFT then\n    >    for i=1,lines*4 do\n    >      for j=2,cols*2 do\n    >        grid[i][j-1] = grid[i][j]\n    >      end\n    >      grid[i][cols*2] = 0\n    >    end\n    >  elseif c == curses.KEY_DOWN then\n    >    for i=lines*4-1,1,-1 do\n    >      for j=1,cols*2 do\n    >        grid[i+1][j] = grid[i][j]\n    >      end\n    >    end\n    >    for j=1,cols*2 do\n    >      grid[1][j] = 0\n    >    end\n    >  elseif c == curses.KEY_UP then\n    >    for i=2,lines*4 do\n    >      for j=1,cols*2 do\n    >        grid[i-1][j] = grid[i][j]\n    >      end\n    >    end\n    >    for j=1,cols*2 do\n    >      grid[lines*4][j] = 0\n    >    end\n    >  elseif c == curses.KEY_RIGHT then\n    >    for i=1,lines*4 do\n    >      for j=cols*2-1,1,-1 do\n    >        grid[i][j+1] = grid[i][j]\n    >      end\n    >      grid[i][1] = 0\n    >    end\n    >  end\n    >end\n- __teliva_timestamp: original\n  main:\n    >function main()\n    >  curses.init_pair(1, 22, 189)\n    >\n    >  -- initialize grid based on commandline args\n    >  if (#arg == 0) then\n    >    -- by default, start from a deterministically random state\n    >    for i=1,lines*4 do\n    >      for j=1,cols*2 do\n    >        grid[i][j] = math.random(0, 1)\n    >      end\n    >    end\n    >  elseif arg[1] == \"random\" then\n    >    -- start from a non-deterministically random start state\n    >    math.randomseed(os.time())\n    >    for i=1,lines*4 do\n    >      for j=1,cols*2 do\n    >        grid[i][j] = math.random(0, 1)\n    >      end\n    >    end\n    >  -- shortcuts for some common patterns\n    >  elseif arg[1] == \"pentomino\" then\n    >    -- https://www.conwaylife.com/wiki/Pentomino\n    >    grid[83][172] = 1\n    >    grid[83][173] = 1\n    >    grid[84][173] = 1\n    >    grid[84][174] = 1\n    >    grid[85][173] = 1\n    >  elseif arg[1] == \"glider\" then\n    >    -- https://www.conwaylife.com/wiki/Glider\n    >    grid[5][4] = 1\n    >    grid[6][5] = 1\n    >    grid[7][3] = 1\n    >    grid[7][4] = 1\n    >    grid[7][5] = 1\n    >  elseif arg[1] == \"blinker\" then\n    >    -- https://www.conwaylife.com/wiki/Blinker\n    >    grid[7][3] = 1\n    >    grid[7][4] = 1\n    >    grid[7][5] = 1\n    >  elseif arg[1] == \"block\" then\n    >    -- https://www.conwaylife.com/wiki/Block\n    >    grid[5][4] = 1\n    >    grid[5][5] = 1\n    >    grid[6][4] = 1\n    >    grid[6][5] = 1\n    >  elseif arg[1] == \"loaf\" then\n    >    -- https://www.conwaylife.com/wiki/Loaf\n    >    grid[5][4] = 1\n    >    grid[5][5] = 1\n    >    grid[6][6] = 1\n    >    grid[7][6] = 1\n    >    grid[8][5] = 1\n    >    grid[7][4] = 1\n    >    grid[6][3] = 1\n    >  else\n    >    -- Load a file in the standard \"plaintext\" format: https://www.conwaylife.com/wiki/Plaintext\n    >    --\n    >    -- Each pattern page at https://www.conwaylife.com/wiki provides its\n    >    -- plaintext representation in a block called \"Pattern Files\" on the right.\n    >    --\n    >    -- For example, check out the list of Important Patterns at\n    >    -- https://www.conwaylife.com/wiki/Category:Patterns_with_Catagolue_frequency_class_0\n    >    load_file(Window, nil, arg[1])\n    >  end\n    >\n    >  -- main loop\n    >  while true do\n    >    render(Window)\n    >    c = Window:getch()\n    >    update(Window, c)\n    >    step()\n    >  end\n    >end\n- __teliva_timestamp:\n    >Thu Feb 17 19:58:19 2022\n  doc:blurb:\n    >Conway's Game of Life\n    >\n    >To get around limitations of text mode we use the braille character set to render 8 cells per character.\n    >\n    >By default it initializes the space with a random state of cells. You can also start it up with .cells files from https://conwaylife.com/wiki, or with a few special names:\n    >  $ src/teliva life.tlv block\n    >  $ src/teliva life.tlv loaf\n    >  $ src/teliva life.tlv blinker\n    >  $ src/teliva life.tlv glider\n    >  $ src/teliva life.tlv pentomino\n"
  },
  {
    "path": "lisp.lua",
    "content": "-- atom types:\n--   nil\n--   true\n--   {num=3.4}\n--   {char='a'}\n--   {str='bc'}\n--   {sym='foo'}\n-- non-atom type:\n--   {car={num=3.4}, cdr=nil}\n--\n-- should {} mean anything special? currently just '(nil)\nfunction atom(x)\n  return x == nil or x.num or x.char or x.str or x.sym\nend\n\nfunction car(x) return x.car end\nfunction cdr(x) return x.cdr end\nfunction cons(x, y) return {car=x, cdr=y} end\n\nfunction iso(x, y)\n  if x == nil then return y == nil end\n  local done={}\n  if done[x] then return done[x] == y end\n  done[x] = y\n  if atom(x) then\n    if not atom(y) then return nil end\n    for k, v in pairs(x) do\n      if y[k] ~= v then return nil end\n    end\n    return true\n  end\n  for k, v in pairs(x) do\n    if not iso(y[k], v) then return nil end\n  end\n  for k, v in pairs(y) do\n    if not iso(x[k], v) then return nil end\n  end\n  return true\nend\n\n-- primitives; feel free to add more\n-- format: lisp name = lua function that implements it\nunary_functions = {\n  atom=atom,\n  car=car,\n  cdr=cdr,\n}\n\nbinary_functions = {\n  cons=cons,\n  iso=iso,\n}\n\nfunction lookup(env, s)\n  if env[s] then return env[s] end\n  if env.next then return lookup(env.next, s) end\nend\n\nfunction eval(x, env)\n  function symeq(x, s)\n    return x and x.sym == s\n  end\n  if x.sym then\n    return lookup(env, x.sym)\n  elseif atom(x) then\n    return x\n  -- otherwise x is a pair\n  elseif symeq(x.car, 'quote') then\n    return x.cdr\n  elseif unary_functions[x.car.sym] then\n    return eval_unary(x, env)\n  elseif binary_functions[x.car.sym] then\n    return eval_binary(x, env)\n  -- special forms that don't always eval all their args\n  elseif symeq(x.car, 'if') then\n    return eval_if(x, env)\n  elseif symeq(x.car.car, 'fn') then\n    return eval_fn(x, env)\n  elseif symeq(x.car.car, 'label') then\n    return eval_label(x, env)\n  end\nend\n\nfunction eval_unary(x, env)\n  return unary_functions[x.car.sym](eval(x.cdr.car, env))\nend\n\nfunction eval_binary(x, env)\n  return binary_functions[x.car.sym](eval(x.cdr.car, env),\n                                     eval(x.cdr.cdr.car, env))\nend\n\nfunction eval_if(x, env)\n  -- syntax: (if check b1 b2)\n  local check = x.cdr.car\n  local b1    = x.cdr.cdr.car\n  local b2    = x.cdr.cdr.cdr.car\n  if eval(check, env) then\n    return eval(b1, env)\n  else\n    return eval(b2, env)\n  end\nend\n\nfunction eval_fn(x, env)\n  -- syntax: ((fn params body*) args*)\n  local callee = x.car\n  local args = x.cdr\n  local params = callee.cdr.car\n  local body = callee.cdr.cdr\n  return eval_exprs(body,\n                    bind_env(params, args, env))\nend\n\nfunction bind_env(params, args, env)\n  if params == nil then return env end\n  local result = {next=env}\n  while true do\n    result[params.car.sym] = eval(args.car, env)\n    params = params.cdr\n    args = args.cdr\n    if params == nil then break end\n  end\n  return result\nend\n\nfunction eval_exprs(xs, env)\n  local result = nil\n  while xs do\n    result = eval(xs.car, env)\n    xs = xs.cdr\n  end\n  return result\nend\n\nfunction eval_label(x, env)\n  -- syntax: ((label f (fn params body*)) args*)\n  local callee = x.car\n  local args = x.cdr\n  local f = callee.cdr.car\n  local fn = callee.cdr.cdr.car\n  return eval({car=fn, cdr=args},\n              bind_env({f}, {callee}, env))\nend\n\n-- testing\nfunction num(n) return {num=n} end\nfunction char(c) return {char=c} end\nfunction str(s) return {str=s} end\nfunction sym(s) return {sym=s} end\nfunction list(...)\n  -- gotcha: no element in arg can be nil; that short-circuits the ipairs below\n  local result = nil\n  local curr = nil\n  for _, x in ipairs({...}) do\n    if curr == nil then\n      result = {car=x}\n      curr = result\n    else\n      curr.cdr = {car=x}\n      curr = curr.cdr\n    end\n  end\n  return result\nend\n\nfunction p(x)\n  p2(x)\n  print()\nend\n\nfunction p2(x)\n  if x == nil then\n    io.write('nil')\n  elseif x == true then\n    io.write('true')\n  elseif x.num then\n    io.write(x.num)\n  elseif x.char then\n    io.write(\"\\\\\"..x.char)\n  elseif x.str then\n    io.write('\"'..x.str..'\"')\n  elseif x.sym then\n    io.write(x.sym)\n  elseif x.cdr == nil then\n    io.write('(')\n    p2(x.car)\n    io.write(')')\n  elseif atom(x.cdr) then\n    io.write('(')\n    p2(x.car)\n    io.write(' . ')\n    p2(x.cdr)\n    io.write(')')\n  else\n    io.write('(')\n    while true do\n      p2(x.car)\n      x = x.cdr\n      if x == nil then break end\n      if atom(x) then\n        io.write(' . ')\n        p2(x)\n        break\n      end\n      io.write(' ')\n    end\n    io.write(')')\n  end\nend\n\nx = {num=3.4}\np(x)\n\np(cons(x, nil))\np(list(x))\n\np(iso(cons(x, nil), cons(x, nil)))\np(iso(list(x), list(x)))\np(iso(list(x, x), list(x)))\np(iso(list(x, x), list(x, x)))\np(iso(x, cons(x, nil)))\n\np     (list(sym(\"cons\"), num(42), num(1)))\np(eval(list(sym(\"cons\"), num(42), num(1)), {}))\n\n-- ((fn () 42)) => 42\n-- can't use list here because of the gotcha above\nassert(iso(eval(cons(cons(sym('fn'), cons(nil, cons(num(42))))), {}), num(42)))\n-- ((fn (a) (cons a 1)) 42) => '(42 . 1)\nassert(iso(eval(cons(cons(sym('fn'), cons(cons(sym('a')), cons(cons(sym('cons'), cons(sym('a'), cons(num(1))))))), cons(num(42)))), cons(num(42), num(1))))\n"
  },
  {
    "path": "lisp.tlv",
    "content": "# .tlv file generated by https://github.com/akkartik/teliva\n# You may edit it if you are careful; however, you may see cryptic errors if you\n# violate Teliva's assumptions.\n#\n# .tlv files are representations of Teliva programs. Teliva programs consist of\n# sequences of definitions. Each definition is a table of key/value pairs. Keys\n# and values are both strings.\n#\n# Lines in .tlv files always follow exactly one of the following forms:\n# - comment lines at the top of the file starting with '#' at column 0\n# - beginnings of definitions starting with '- ' at column 0, followed by a\n#   key/value pair\n# - key/value pairs consisting of '  ' at column 0, containing either a\n#   spaceless value on the same line, or a multi-line value\n# - multiline values indented by more than 2 spaces, starting with a '>'\n#\n# If these constraints are violated, Teliva may unceremoniously crash. Please\n# report bugs at http://akkartik.name/contact\n- __teliva_timestamp: original\n  str_helpers:\n    >-- some string helpers from http://lua-users.org/wiki/StringIndexing\n    >\n    >-- index characters using []\n    >getmetatable('').__index = function(str,i)\n    >  if type(i) == 'number' then\n    >    return str:sub(i,i)\n    >  else\n    >    return string[i]\n    >  end\n    >end\n    >\n    >-- ranges using (), selected bytes using {}\n    >getmetatable('').__call = function(str,i,j)\n    >  if type(i)~='table' then\n    >    return str:sub(i,j)\n    >  else\n    >    local t={}\n    >    for k,v in ipairs(i) do\n    >      t[k]=str:sub(v,v)\n    >    end\n    >    return table.concat(t)\n    >  end\n    >end\n    >\n    >-- iterate over an ordered sequence\n    >function q(x)\n    >  if type(x) == 'string' then\n    >    return x:gmatch('.')\n    >  else\n    >    return ipairs(x)\n    >  end\n    >end\n    >\n    >-- insert within string\n    >function string.insert(str1, str2, pos)\n    >  return str1:sub(1,pos)..str2..str1:sub(pos+1)\n    >end\n    >\n    >function string.remove(s, pos)\n    >  return s:sub(1,pos-1)..s:sub(pos+1)\n    >end\n    >\n    >function string.pos(s, sub)\n    >  return string.find(s, sub, 1, true)  -- plain=true to disable regular expressions\n    >end\n    >\n    >-- TODO: backport utf-8 support from Lua 5.3\n- __teliva_timestamp: original\n  check:\n    >function check(x, msg)\n    >  if x then\n    >    Window:addch('.')\n    >  else\n    >    print('F - '..msg)\n    >    print('  '..str(x)..' is false/nil')\n    >    teliva_num_test_failures = teliva_num_test_failures + 1\n    >    -- overlay first test failure on editors\n    >    if teliva_first_failure == nil then\n    >      teliva_first_failure = msg\n    >    end\n    >  end\n    >end\n- __teliva_timestamp: original\n  check_eq:\n    >function check_eq(x, expected, msg)\n    >  if eq(x, expected) then\n    >    Window:addch('.')\n    >  else\n    >    print('F - '..msg)\n    >    print('  expected '..str(expected)..' but got '..str(x))\n    >    teliva_num_test_failures = teliva_num_test_failures + 1\n    >    -- overlay first test failure on editors\n    >    if teliva_first_failure == nil then\n    >      teliva_first_failure = msg\n    >    end\n    >  end\n    >end\n- __teliva_timestamp: original\n  eq:\n    >function eq(a, b)\n    >  if type(a) ~= type(b) then return false end\n    >  if type(a) == 'table' then\n    >    if #a ~= #b then return false end\n    >    for k, v in pairs(a) do\n    >      if b[k] ~= v then\n    >        return false\n    >      end\n    >    end\n    >    for k, v in pairs(b) do\n    >      if a[k] ~= v then\n    >        return false\n    >      end\n    >    end\n    >    return true\n    >  end\n    >  return a == b\n    >end\n- __teliva_timestamp: original\n  str:\n    >-- smarter tostring\n    >-- slow; used only for debugging\n    >function str(x)\n    >  if type(x) == 'table' then\n    >    local result = ''\n    >    result = result..#x..'{'\n    >    for k, v in pairs(x) do\n    >      result = result..str(k)..'='..str(v)..', '\n    >    end\n    >    result = result..'}'\n    >    return result\n    >  elseif type(x) == 'string' then\n    >    return '\"'..x..'\"'\n    >  end\n    >  return tostring(x)\n    >end\n- __teliva_timestamp: original\n  menu:\n    >-- To show app-specific hotkeys in the menu bar, add hotkey/command\n    >-- arrays of strings to the menu array.\n    >menu = {}\n- __teliva_timestamp: original\n  Window:\n    >Window = curses.stdscr()\n- __teliva_timestamp: original\n  render:\n    >function render(window)\n    >  window:clear()\n    >  -- draw stuff to screen here\n    >  window:attron(curses.A_BOLD)\n    >  window:mvaddstr(1, 5, \"example app\")\n    >  window:attrset(curses.A_NORMAL)\n    >  for i=0,15 do\n    >    window:attrset(curses.color_pair(i))\n    >    window:mvaddstr(3+i, 5, \"========================\")\n    >  end\n    >  window:refresh()\n    >end\n- __teliva_timestamp: original\n  menu:\n    >-- To show app-specific hotkeys in the menu bar, add hotkey/command\n    >-- arrays of strings to the menu array.\n    >menu = {}\n- __teliva_timestamp: original\n  update:\n    >function update(window)\n    >  local key = window:getch()\n    >  -- process key here\n    >end\n- __teliva_timestamp: original\n  init_colors:\n    >function init_colors()\n    >  for i=0,7 do\n    >    curses.init_pair(i, i, -1)\n    >  end\n    >  curses.init_pair(8, 7, 0)\n    >  curses.init_pair(9, 7, 1)\n    >  curses.init_pair(10, 7, 2)\n    >  curses.init_pair(11, 7, 3)\n    >  curses.init_pair(12, 7, 4)\n    >  curses.init_pair(13, 7, 5)\n    >  curses.init_pair(14, 7, 6)\n    >  curses.init_pair(15, -1, 15)\n    >end\n- __teliva_timestamp: original\n  main:\n    >function main()\n    >  init_colors()\n    >\n    >  while true do\n    >    render(Window)\n    >    update(Window)\n    >  end\n    >end\n- __teliva_timestamp: original\n  eval:\n    >function eval(x, env)\n    >  function symeq(x, s)\n    >    return x and x.sym == s\n    >  end\n    >  if x.sym then\n    >    return lookup(env, x.sym)\n    >  elseif atom(x) then\n    >    return x\n    >  -- otherwise x is a pair\n    >  elseif symeq(x.car, 'quote') then\n    >    return x.cdr\n    >  elseif unary_functions[x.car.sym] then\n    >    return eval_unary(x, env)\n    >  elseif binary_functions[x.car.sym] then\n    >    return eval_binary(x, env)\n    >  -- special forms that don't always eval all their args\n    >  elseif symeq(x.car, 'if') then\n    >    return eval_if(x, env)\n    >  elseif symeq(x.car.car, 'fn') then\n    >    return eval_fn(x, env)\n    >  elseif symeq(x.car.car, 'label') then\n    >    return eval_label(x, env)\n    >  end\n    >end\n- __teliva_timestamp: original\n  eval_unary:\n    >function eval_unary(x, env)\n    >  return unary_functions[x.car.sym](eval(x.cdr.car, env))\n    >end\n- __teliva_timestamp: original\n  eval_binary:\n    >function eval_binary(x, env)\n    >  return binary_functions[x.car.sym](eval(x.cdr.car, env))\n    >end\n- __teliva_timestamp: original\n  unary_functions:\n    >-- format: lisp name = lua function that implements it\n    >unary_functions = {\n    >  atom=atom,\n    >  car=car,\n    >  cdr=cdr,\n    >}\n- __teliva_timestamp: original\n  binary_functions:\n    >-- format: lisp name = lua function that implements it\n    >binary_functions = {\n    >  cons=cons,\n    >  iso=iso,\n    >}\n- __teliva_timestamp: original\n  lookup:\n    >function lookup(env, s)\n    >  if env[s] then return env[s] end\n    >  if env.next then return lookup(env.next, s) end\n    >end\n- __teliva_timestamp: original\n  eval_if:\n    >function eval_if(x, env)\n    >  -- syntax: (if check b1 b2)\n    >  local check = x.cdr.car\n    >  local b1    = x.cdr.cdr.car\n    >  local b2    = x.cdr.cdr.cdr.car\n    >  if eval(check, env) then\n    >    return eval(b1, env)\n    >  else\n    >    return eval(b2, env)\n    >  end\n    >end\n- __teliva_timestamp: original\n  eval_fn:\n    >function eval_fn(x, env)\n    >  -- syntax: ((fn params body*) args*)\n    >  local callee = x.car\n    >  local args = x.cdr\n    >  local params = callee.cdr.car\n    >  local body = callee.cdr.cdr\n    >  return eval_exprs(body,\n    >                    bind_env(params, args, env))\n    >end\n- __teliva_timestamp: original\n  bind_env:\n    >function bind_env(params, args, env)\n    >  if params == nil then return env end\n    >  local result = {next=env}\n    >  while true do\n    >    result[params.car.sym] = eval(args.car, env)\n    >    params = params.cdr\n    >    args = args.cdr\n    >    if params == nil then break end\n    >  end\n    >  return result\n    >end\n- __teliva_timestamp: original\n  eval_exprs:\n    >function eval_exprs(xs, env)\n    >  local result = nil\n    >  while xs do\n    >    result = eval(xs.car, env)\n    >    xs = xs.cdr\n    >  end\n    >  return result\n    >end\n- __teliva_timestamp: original\n  eval_label:\n    >function eval_label(x, env)\n    >  -- syntax: ((label f (fn params body*)) args*)\n    >  local callee = x.car\n    >  local args = x.cdr\n    >  local f = callee.cdr.car\n    >  local fn = callee.cdr.cdr.car\n    >  return eval({car=fn, cdr=args},\n    >              bind_env({f}, {callee}, env))\n    >end\n- __teliva_timestamp: original\n  atom:\n    >function atom(x)\n    >  return x == nil or x.num or x.char or x.str or x.sym\n    >end\n- __teliva_timestamp: original\n  car:\n    >function car(x) return x.car end\n- __teliva_timestamp: original\n  cdr:\n    >function cdr(x) return x.cdr end\n- __teliva_timestamp: original\n  cons:\n    >function cons(x, y) return {car=x, cdr=y} end\n- __teliva_timestamp: original\n  doc:main:\n    >John McCarthy's Lisp -- without the metacircularity\n    >If you know Lua, this version might be easier to understand.\n    >\n    >Words highlighted like [[this]] are suggestions for places to jump to using ctrl-g (see the menu below).\n    >You can always jump back here using ctrl-b (for 'big picture').\n    >\n    >Lisp is a programming language that manipulates objects of a few different types.\n    >There are a few _atomic_ types, and one type that can combine them.\n    >The atomic types are what you would expect: numbers, characters, strings, symbols (variables). You can add others.\n    >\n    >The way to combine them is the [[cons]] table which has just two keys: a [[car]] and a [[cdr]]. Both can hold objects, either atoms or other cons tables.\n    >\n    >We'll now build an interpreter that can run programs constructed out of cons tables.\n    >\n    >One thing we'll need for an interpreter is a symbol table (env) that maps symbols to values (objects).\n    >We'll just use a Lua table for this purpose, but with one tweak: a _next_ pointer that allows us to combine tables together.\n    >See [[lookup]] now to get a sense for how we'll use envs.\n    >\n    >Lisp programs are just cons tables and atoms nested to arbitrary depths, constructing trees. A Lisp interpreter walks the tree of code,\n    >performing computations. Since cons tables can point to other cons tables, the tree-walker interpreter [[eval]] is recursive.\n    >As the interpreter gets complex, we'll extract parts of it into their own helper functions: [[eval_unary]], [[eval_binary]], [[eval_if]], and so on.\n    >The helper functions contain recursive calls to [[eval]], so that [[eval]] becomes indirectly recursive, and [[eval]] together with its helpers\n    >is mutually recursive. I sometimes find it helpful to think of them all as just one big function.\n    >\n    >All these mutually recursive functions take the same arguments: a current expression 'x' and the symbol table 'env'.\n    >But really, most of the interpreter is just walking the tree of expressions. Only two functions care about the internals of 'env':\n    >  - [[lookup]] which reads within env as we saw before\n    >  - [[bind_env]] which creates a new _scope_ of symbols for each new function call.\n    >More complex Lisps add even more arguments to every. single. helper. Each arg will still only really matter to a couple of functions.\n    >But we still have to pass them around all over the place.\n    >\n    >Hopefully this quick overview will help you get a sense for this codebase.\n    >\n    >Here's a reference list of eval helpers: [[eval_unary]], [[eval_binary]], [[eval_if]], [[eval_fn]], [[eval_exprs]], [[eval_label]]\n    >More complex Lisps with more features will likely add helpers for lumpy bits of the language.\n    >Here's a list of primitives implemented in Lua: [[atom]], [[car]], [[cdr]], [[cons]], [[iso]] (for 'isomorphic'; comparing trees all the way down to the leaves)\n    >Here's a list of _constructors_ for creating objects of different types: [[num]], [[char]], [[str]], [[sym]] (and of course [[cons]])\n    >I should probably add more primitives for operating on numbers, characters and strings..\n- __teliva_timestamp: original\n  iso:\n    >function iso(x, y)\n    >  if x == nil then return y == nil end\n    >  local done={}\n    >  -- watch out for the rare cyclical expression\n    >  if done[x] then return done[x] == y end\n    >  done[x] = y\n    >  if atom(x) then\n    >    if not atom(y) then return nil end\n    >    for k, v in pairs(x) do\n    >      if y[k] ~= v then return nil end\n    >    end\n    >    return true\n    >  end\n    >  for k, v in pairs(x) do\n    >    if not iso(y[k], v) then return nil end\n    >  end\n    >  for k, v in pairs(y) do\n    >    if not iso(x[k], v) then return nil end\n    >  end\n    >  return true\n    >end\n"
  },
  {
    "path": "manual_tests",
    "content": "run a program\nrun a program, edit\nrun a program, edit, make an edit, run | edit takes effect\nrun a program with error | big picture\nrun a program, edit, make an error, run\nrun a program, edit, ^g to a different definition, make an edit, ^e to run again\nrun a program, edit, ^g to a non-existent definition\nrun a program, edit, ^g to a different definition, ^g to a different definition, ^e to run again\nstart -> big picture -> edit -> move cursor -> run -> edit | cursor preserved\nstart -> big picture -> edit A -> move cursor -> big picture -> edit B | cursor initialized\nstart -> big picture -> edit -> move cursor -> run -> exit -> start | big picture (optional)\nstart -> big picture -> edit A -> move cursor -> run -> exit -> start -> ... -> edit B | cursor initialized\nstart -> big picture -> edit A -> move cursor -> run -> exit -> start -> ... -> edit B | big picture\nsyntax highlighting for line comments\nsyntax highlighting for multiline comments\nstart -> big picture -> recent changes -> add note -> save | note visible\nstart -> big picture -> arrow keys* | always exactly one definition highlighted\n\nIt's very important not to leak space on the Lua stack, particularly\nproportionate to keypresses. That's a recipe for segfaults.\n\nThis implies that bouncing around between big picture, editor, recent changes,\nrunning app.. shouldn't grow the call stack either.\n\n== security/privacy\nprogram draws over menu -> getch -> Teliva menu is still visible\n\napp tries to read/write sensitive teliva files (teliva_edit_buffer, etc.) -> never allowed\n  TODO should we protect .c sources?\n\nTODO protect against DoS attack filling up disk\n\nassumptions:\n  listing files in a directory is not worth sandboxing\n    since reading their contents is sandboxed\n    and since UNIX permissions protect system directories\n\n  rmdir() is not worth sandboxing, since it only succeeds on empty directories\n  no need to sandbox unlink() since it's not exposed\n"
  },
  {
    "path": "sandboxing/README.md",
    "content": "This directory includes some working notes to audit the entire Teliva codebase\nfor side-effects that should be gated/sandboxed.\n\nFounding principle for this approach: Side-effects come from the OS. There can\nbe no effects visible outside a Unix process (regardless of language) if it\ndoesn't invoke any OS syscalls.\n\n## Top down\n\nThings to secure:\n* screen? Keep apps from drawing over standard Teliva UI elements.\n  * Teliva currently doesn't stop apps from overwriting the menu, if they're\n    clever. However, it always redraws its UI elements before accepting any\n    input from the keyboard.\n\n* code? There are currently no protections against .tlv files clobbering\n  existing definitions. I'm hoping that disallowing native code keeps this\n  safe. Apps can only affect themselves.\n\n* files opened (for read/write) on file system\n  * `io_open`\n  * `io_lines`\n\n* destinations opened (for read/write) on network\n  * `inet_tryconnect` // `socket_connect`\n  * `inet_tryaccept` // `socket_accept`\n\nIt seems more difficult to control what is written to a file or socket once\nit's opened. For starters let's just focus on the interfaces that convert a\nstring path or url to a file descriptor.\n\nScenarios:\n  * (1) app reads system files\n  * (1) app sends data to a remote server\n  * (1) app should _never_ be allowed to open Teliva's system files:\n      - `teliva_editor_state`\n      - app-specific sandboxing policies\n  * (2) app can read from a remote server but not write (POST)\n  * (1) app permissions are saved across restart\n  * (1) permissions the owner grants to one app are not automatically granted\n    to another\n  * (2) downloading a second app with identical name doesn't receive its\n    predecessors permissions\n  * app gains access to a remote server for a legitimate purpose, reads\n    sensitive data from the local system file for legitimate purpose. Now\n    there's nothing preventing it from exfiltrating the sensitive data to the\n    remote server.\n    - (2) solution: make it obvious in the UI that granting both permissions\n      allows an app to do anything. Educate people to separate apps that read\n      sensitive data from apps that access remote servers.\n    - (2) solution: map phases within an app to distinct permission sets\n  * app A legitimately needs to read sensitive data. It saves a copy to file\n    X. app B seems to legitimately needs to access the network, but also\n    asks to read file X. If the owner forgets who wrote file X and what it\n    contains, sensitive data could be exfiltrated.\n  * (3) app wants access to system() or exec() or popen()\n\nDifficulty levels\n  1. I have some sense of how to enforce this.\n  2. Seems vaguely doable.\n  3. Seems unlikely to be doable.\n\nUX:\n  * distinguish what Teliva can do, what the app can do, and Teliva's ability\n    to police the app.\n  * easily visualize Teliva's ability to police an app.\n    - maybe show a lock in halves; left half = file system, right half =\n      network. One half unlocked = orange. Both unlocked = red.\n\n## Bottom up\n\n* `includes`: all `#include`s throughout the codebase. I assume that C the\n  language itself can't invoke any syscalls without at least triggering\n  warnings from the compiler.\n  ```\n  cd src\n  grep '#include' * */* > ../sandboxing/includes\n  ```\n* `system_includes`: all `#include <...>`s throughout the codebase. I assume\n  side-effects require going outside the codebase. `#include`s could smuggle\n  out of the codebase using relative paths (`../`) but I assume it's easy to\n  protect against this using code review.\n  ```\n  grep '<' sandboxing/includes > sandboxing/system_includes\n  ```\n* `unique_system_includes`: deduped\n  ```\n  sed 's/.*<\\|>.*//g' sandboxing/system_includes |sort |uniq > sandboxing/unique_system_includes\n  ```\n"
  },
  {
    "path": "sandboxing/includes",
    "content": "kilo.c:#include <assert.h>\nkilo.c:#include <ncurses.h>\nkilo.c:#include <stdlib.h>\nkilo.c:#include <stdio.h>\nkilo.c:#include <stdint.h>\nkilo.c:#include <errno.h>\nkilo.c:#include <string.h>\nkilo.c:#include <ctype.h>\nkilo.c:#include <time.h>\nkilo.c:#include <sys/types.h>\nkilo.c:#include <sys/time.h>\nkilo.c:#include <unistd.h>\nkilo.c:#include <fcntl.h>\nkilo.c:#include \"lua.h\"\nkilo.c:#include \"teliva.h\"\nlapi.c:#include <assert.h>\nlapi.c:#include <math.h>\nlapi.c:#include <stdarg.h>\nlapi.c:#include <string.h>\nlapi.c:#include \"lua.h\"\nlapi.c:#include \"lapi.h\"\nlapi.c:#include \"ldebug.h\"\nlapi.c:#include \"ldo.h\"\nlapi.c:#include \"lfunc.h\"\nlapi.c:#include \"lgc.h\"\nlapi.c:#include \"lmem.h\"\nlapi.c:#include \"lobject.h\"\nlapi.c:#include \"lstate.h\"\nlapi.c:#include \"lstring.h\"\nlapi.c:#include \"ltable.h\"\nlapi.c:#include \"ltm.h\"\nlapi.c:#include \"lundump.h\"\nlapi.c:#include \"lvm.h\"\nlapi.h:#include \"lobject.h\"\nlauxlib.c:#include <ctype.h>\nlauxlib.c:#include <errno.h>\nlauxlib.c:#include <stdarg.h>\nlauxlib.c:#include <stdio.h>\nlauxlib.c:#include <stdlib.h>\nlauxlib.c:#include <string.h>\nlauxlib.c:#include \"lua.h\"\nlauxlib.c:#include \"lauxlib.h\"\nlauxlib.h:#include <stddef.h>\nlauxlib.h:#include <stdio.h>\nlauxlib.h:#include \"lua.h\"\nlbaselib.c:#include <ctype.h>\nlbaselib.c:#include <ncurses.h>\nlbaselib.c:#include <stdio.h>\nlbaselib.c:#include <stdlib.h>\nlbaselib.c:#include <string.h>\nlbaselib.c:#include \"lua.h\"\nlbaselib.c:#include \"lauxlib.h\"\nlbaselib.c:#include \"lualib.h\"\nlcode.c:#include <stdlib.h>\nlcode.c:#include \"lua.h\"\nlcode.c:#include \"lcode.h\"\nlcode.c:#include \"ldebug.h\"\nlcode.c:#include \"ldo.h\"\nlcode.c:#include \"lgc.h\"\nlcode.c:#include \"llex.h\"\nlcode.c:#include \"lmem.h\"\nlcode.c:#include \"lobject.h\"\nlcode.c:#include \"lopcodes.h\"\nlcode.c:#include \"lparser.h\"\nlcode.c:#include \"ltable.h\"\nlcode.h:#include \"llex.h\"\nlcode.h:#include \"lobject.h\"\nlcode.h:#include \"lopcodes.h\"\nlcode.h:#include \"lparser.h\"\nldblib.c:#include <stdio.h>\nldblib.c:#include <stdlib.h>\nldblib.c:#include <string.h>\nldblib.c:#include \"lua.h\"\nldblib.c:#include \"lauxlib.h\"\nldblib.c:#include \"lualib.h\"\nldebug.c:#include <stdarg.h>\nldebug.c:#include <stddef.h>\nldebug.c:#include <string.h>\nldebug.c:#include \"lua.h\"\nldebug.c:#include \"lapi.h\"\nldebug.c:#include \"lcode.h\"\nldebug.c:#include \"ldebug.h\"\nldebug.c:#include \"ldo.h\"\nldebug.c:#include \"lfunc.h\"\nldebug.c:#include \"lobject.h\"\nldebug.c:#include \"lopcodes.h\"\nldebug.c:#include \"lstate.h\"\nldebug.c:#include \"lstring.h\"\nldebug.c:#include \"ltable.h\"\nldebug.c:#include \"ltm.h\"\nldebug.c:#include \"lvm.h\"\nldebug.h:#include \"lstate.h\"\nldo.c:#include <setjmp.h>\nldo.c:#include <stdio.h>\nldo.c:#include <stdlib.h>\nldo.c:#include <string.h>\nldo.c:#include \"lua.h\"\nldo.c:#include \"ldebug.h\"\nldo.c:#include \"ldo.h\"\nldo.c:#include \"lfunc.h\"\nldo.c:#include \"lgc.h\"\nldo.c:#include \"lmem.h\"\nldo.c:#include \"lobject.h\"\nldo.c:#include \"lopcodes.h\"\nldo.c:#include \"lparser.h\"\nldo.c:#include \"lstate.h\"\nldo.c:#include \"lstring.h\"\nldo.c:#include \"ltable.h\"\nldo.c:#include \"ltm.h\"\nldo.c:#include \"lundump.h\"\nldo.c:#include \"lvm.h\"\nldo.c:#include \"lzio.h\"\nldo.h:#include \"lobject.h\"\nldo.h:#include \"lstate.h\"\nldo.h:#include \"lzio.h\"\nldump.c:#include <stddef.h>\nldump.c:#include \"lua.h\"\nldump.c:#include \"lobject.h\"\nldump.c:#include \"lstate.h\"\nldump.c:#include \"lundump.h\"\nlfunc.c:#include <stddef.h>\nlfunc.c:#include \"lua.h\"\nlfunc.c:#include \"lfunc.h\"\nlfunc.c:#include \"lgc.h\"\nlfunc.c:#include \"lmem.h\"\nlfunc.c:#include \"lobject.h\"\nlfunc.c:#include \"lstate.h\"\nlfunc.h:#include \"lobject.h\"\nlgc.c:#include <string.h>\nlgc.c:#include \"lua.h\"\nlgc.c:#include \"ldebug.h\"\nlgc.c:#include \"ldo.h\"\nlgc.c:#include \"lfunc.h\"\nlgc.c:#include \"lgc.h\"\nlgc.c:#include \"lmem.h\"\nlgc.c:#include \"lobject.h\"\nlgc.c:#include \"lstate.h\"\nlgc.c:#include \"lstring.h\"\nlgc.c:#include \"ltable.h\"\nlgc.c:#include \"ltm.h\"\nlgc.h:#include \"lobject.h\"\nlinit.c:#include \"lua.h\"\nlinit.c:#include \"lualib.h\"\nlinit.c:#include \"lauxlib.h\"\nliolib.c:#include <errno.h>\nliolib.c:#include <stdio.h>\nliolib.c:#include <stdlib.h>\nliolib.c:#include <string.h>\nliolib.c:#include \"lua.h\"\nliolib.c:#include \"lauxlib.h\"\nliolib.c:#include \"lualib.h\"\nllex.c:#include <ctype.h>\nllex.c:#include <locale.h>\nllex.c:#include <string.h>\nllex.c:#include \"lua.h\"\nllex.c:#include \"ldo.h\"\nllex.c:#include \"llex.h\"\nllex.c:#include \"lobject.h\"\nllex.c:#include \"lparser.h\"\nllex.c:#include \"lstate.h\"\nllex.c:#include \"lstring.h\"\nllex.c:#include \"ltable.h\"\nllex.c:#include \"lzio.h\"\nllex.h:#include \"lobject.h\"\nllex.h:#include \"lzio.h\"\nllimits.h:#include <limits.h>\nllimits.h:#include <stddef.h>\nllimits.h:#include \"lua.h\"\nlmathlib.c:#include <stdlib.h>\nlmathlib.c:#include <math.h>\nlmathlib.c:#include \"lua.h\"\nlmathlib.c:#include \"lauxlib.h\"\nlmathlib.c:#include \"lualib.h\"\nlmem.c:#include <stddef.h>\nlmem.c:#include \"lua.h\"\nlmem.c:#include \"ldebug.h\"\nlmem.c:#include \"ldo.h\"\nlmem.c:#include \"lmem.h\"\nlmem.c:#include \"lobject.h\"\nlmem.c:#include \"lstate.h\"\nlmem.h:#include <stddef.h>\nlmem.h:#include \"llimits.h\"\nlmem.h:#include \"lua.h\"\nloadlib.c:#include <stdlib.h>\nloadlib.c:#include <string.h>\nloadlib.c:#include \"lua.h\"\nloadlib.c:#include \"lauxlib.h\"\nloadlib.c:#include \"lualib.h\"\nloadlib.c:#include <dlfcn.h>\nloadlib.c:#include <windows.h>\nloadlib.c:#include <mach-o/dyld.h>\nlobject.c:#include <ctype.h>\nlobject.c:#include <stdarg.h>\nlobject.c:#include <stdio.h>\nlobject.c:#include <stdlib.h>\nlobject.c:#include <string.h>\nlobject.c:#include \"lua.h\"\nlobject.c:#include \"ldo.h\"\nlobject.c:#include \"lmem.h\"\nlobject.c:#include \"lobject.h\"\nlobject.c:#include \"lstate.h\"\nlobject.c:#include \"lstring.h\"\nlobject.c:#include \"lvm.h\"\nlobject.h:#include <stdarg.h>\nlobject.h:#include \"llimits.h\"\nlobject.h:#include \"lua.h\"\nlopcodes.c:#include \"lopcodes.h\"\nlopcodes.h:#include \"llimits.h\"\nloslib.c:#include <errno.h>\nloslib.c:#include <locale.h>\nloslib.c:#include <stdlib.h>\nloslib.c:#include <string.h>\nloslib.c:#include <time.h>\nloslib.c:#include \"lua.h\"\nloslib.c:#include \"lauxlib.h\"\nloslib.c:#include \"lualib.h\"\nlparser.c:#include <string.h>\nlparser.c:#include \"lua.h\"\nlparser.c:#include \"lcode.h\"\nlparser.c:#include \"ldebug.h\"\nlparser.c:#include \"ldo.h\"\nlparser.c:#include \"lfunc.h\"\nlparser.c:#include \"llex.h\"\nlparser.c:#include \"lmem.h\"\nlparser.c:#include \"lobject.h\"\nlparser.c:#include \"lopcodes.h\"\nlparser.c:#include \"lparser.h\"\nlparser.c:#include \"lstate.h\"\nlparser.c:#include \"lstring.h\"\nlparser.c:#include \"ltable.h\"\nlparser.h:#include \"llimits.h\"\nlparser.h:#include \"lobject.h\"\nlparser.h:#include \"lzio.h\"\nlstate.c:#include <stddef.h>\nlstate.c:#include \"lua.h\"\nlstate.c:#include \"ldebug.h\"\nlstate.c:#include \"ldo.h\"\nlstate.c:#include \"lfunc.h\"\nlstate.c:#include \"lgc.h\"\nlstate.c:#include \"llex.h\"\nlstate.c:#include \"lmem.h\"\nlstate.c:#include \"lstate.h\"\nlstate.c:#include \"lstring.h\"\nlstate.c:#include \"ltable.h\"\nlstate.c:#include \"ltm.h\"\nlstate.h:#include \"lua.h\"\nlstate.h:#include \"lobject.h\"\nlstate.h:#include \"ltm.h\"\nlstate.h:#include \"lzio.h\"\nlstring.c:#include <string.h>\nlstring.c:#include \"lua.h\"\nlstring.c:#include \"lmem.h\"\nlstring.c:#include \"lobject.h\"\nlstring.c:#include \"lstate.h\"\nlstring.c:#include \"lstring.h\"\nlstring.h:#include \"lgc.h\"\nlstring.h:#include \"lobject.h\"\nlstring.h:#include \"lstate.h\"\nlstrlib.c:#include <ctype.h>\nlstrlib.c:#include <stddef.h>\nlstrlib.c:#include <stdio.h>\nlstrlib.c:#include <stdlib.h>\nlstrlib.c:#include <string.h>\nlstrlib.c:#include \"lua.h\"\nlstrlib.c:#include \"lauxlib.h\"\nlstrlib.c:#include \"lualib.h\"\nltable.c:#include <math.h>\nltable.c:#include <string.h>\nltable.c:#include \"lua.h\"\nltable.c:#include \"ldebug.h\"\nltable.c:#include \"ldo.h\"\nltable.c:#include \"lgc.h\"\nltable.c:#include \"lmem.h\"\nltable.c:#include \"lobject.h\"\nltable.c:#include \"lstate.h\"\nltable.c:#include \"ltable.h\"\nltable.h:#include \"lobject.h\"\nltablib.c:#include <stddef.h>\nltablib.c:#include \"lua.h\"\nltablib.c:#include \"lauxlib.h\"\nltablib.c:#include \"lualib.h\"\nltm.c:#include <string.h>\nltm.c:#include \"lua.h\"\nltm.c:#include \"lobject.h\"\nltm.c:#include \"lstate.h\"\nltm.c:#include \"lstring.h\"\nltm.c:#include \"ltable.h\"\nltm.c:#include \"ltm.h\"\nltm.h:#include \"lobject.h\"\nlua.c:#include <assert.h>\nlua.c:#include <ctype.h>\nlua.c:#include <fcntl.h>\nlua.c:#include <locale.h>\nlua.c:#include <ncurses.h>\nlua.c:#include <signal.h>\nlua.c:#include <stdio.h>\nlua.c:#include <stdlib.h>\nlua.c:#include <string.h>\nlua.c:#include <time.h>\nlua.c:#include <unistd.h>\nlua.c:#include \"lua.h\"\nlua.c:#include \"teliva.h\"\nlua.c:#include \"lauxlib.h\"\nlua.c:#include \"lualib.h\"\nlua.h:#include <stdarg.h>\nlua.h:#include <stddef.h>\nlua.h:#include \"luaconf.h\"\nlua.h:#include LUA_USER_H\nluaconf.h:#include <limits.h>\nluaconf.h:#include <stddef.h>\nluaconf.h:#include <assert.h>\nluaconf.h:#include <math.h>\nluaconf.h:#include <unistd.h>\nluaconf.h:#include <stdio.h>\nlualib.h:#include \"lua.h\"\nlundump.c:#include <string.h>\nlundump.c:#include \"lua.h\"\nlundump.c:#include \"ldebug.h\"\nlundump.c:#include \"ldo.h\"\nlundump.c:#include \"lfunc.h\"\nlundump.c:#include \"lmem.h\"\nlundump.c:#include \"lobject.h\"\nlundump.c:#include \"lstring.h\"\nlundump.c:#include \"lundump.h\"\nlundump.c:#include \"lzio.h\"\nlundump.h:#include \"lobject.h\"\nlundump.h:#include \"lzio.h\"\nlvm.c:#include <stdio.h>\nlvm.c:#include <stdlib.h>\nlvm.c:#include <string.h>\nlvm.c:#include \"lua.h\"\nlvm.c:#include \"ldebug.h\"\nlvm.c:#include \"ldo.h\"\nlvm.c:#include \"lfunc.h\"\nlvm.c:#include \"lgc.h\"\nlvm.c:#include \"lobject.h\"\nlvm.c:#include \"lopcodes.h\"\nlvm.c:#include \"lstate.h\"\nlvm.c:#include \"lstring.h\"\nlvm.c:#include \"ltable.h\"\nlvm.c:#include \"ltm.h\"\nlvm.c:#include \"lvm.h\"\nlvm.h:#include \"ldo.h\"\nlvm.h:#include \"lobject.h\"\nlvm.h:#include \"ltm.h\"\nlzio.c:#include <string.h>\nlzio.c:#include \"lua.h\"\nlzio.c:#include \"llimits.h\"\nlzio.c:#include \"lmem.h\"\nlzio.c:#include \"lstate.h\"\nlzio.c:#include \"lzio.h\"\nlzio.h:#include \"lua.h\"\nlzio.h:#include \"lmem.h\"\nmenu.c:#include <ncurses.h>\nmenu.c:#include <string.h>\nmenu.c:#include \"lua.h\"\nmenu.c:#include \"lauxlib.h\"\nmenu.c:#include \"teliva.h\"\ntlv.c:#include <assert.h>\ntlv.c:#include <ncurses.h>\ntlv.c:#include <stdio.h>\ntlv.c:#include <stdlib.h>\ntlv.c:#include <string.h>\ntlv.c:#include <strings.h>\ntlv.c:#include \"lua.h\"\ntlv.c:#include \"lauxlib.h\"\nlcurses/_helpers.c:#include <errno.h>\nlcurses/_helpers.c:#include <grp.h>\nlcurses/_helpers.c:#include <pwd.h>\nlcurses/_helpers.c:#include <stdlib.h>\nlcurses/_helpers.c:#include <string.h>\nlcurses/_helpers.c:#include <sys/stat.h>\nlcurses/_helpers.c:#include <unistd.h>\nlcurses/_helpers.c:#include <ncurses.h>\nlcurses/_helpers.c:#include <term.h>\nlcurses/_helpers.c:#include \"../lua.h\"\nlcurses/_helpers.c:#include \"../lualib.h\"\nlcurses/_helpers.c:#include \"../lauxlib.h\"\nlcurses/chstr.c:#include \"_helpers.c\"\nlcurses/compat-5.2.c:#include <errno.h>\nlcurses/compat-5.2.c:#include <string.h>\nlcurses/compat-5.2.c:#include \"../lua.h\"\nlcurses/compat-5.2.c:#include \"../lauxlib.h\"\nlcurses/compat-5.2.c:#include \"compat-5.2.h\"\nlcurses/compat-5.2.c:#include <limits.h>\nlcurses/compat-5.2.c:#include <math.h>\nlcurses/compat-5.2.h:#include <stddef.h>\nlcurses/compat-5.2.h:#include <string.h>\nlcurses/compat-5.2.h:#include <stdio.h>\nlcurses/compat-5.2.h:#include \"../lua.h\"\nlcurses/compat-5.2.h:#include \"../lauxlib.h\"\nlcurses/compat-5.2.h:#include \"../lualib.h\"\nlcurses/compat-5.2.h:#include <limits.h>\nlcurses/curses.c:#include \"_helpers.c\"\nlcurses/curses.c:#include \"strlcpy.c\"\nlcurses/curses.c:#include \"chstr.c\"\nlcurses/curses.c:#include \"window.c\"\nlcurses/strlcpy.c:#include <sys/types.h>\nlcurses/strlcpy.c:#include <string.h>\nlcurses/window.c:#include \"../teliva.h\"\nlcurses/window.c:#include \"_helpers.c\"\nlcurses/window.c:#include \"chstr.c\"\nluasec/compat.h:#include <openssl/ssl.h>\nluasec/config.c:#include \"compat.h\"\nluasec/config.c:#include \"options.h\"\nluasec/config.c:#include \"ec.h\"\nluasec/context.c:#include <string.h>\nluasec/context.c:#include <windows.h>\nluasec/context.c:#include <openssl/ssl.h>\nluasec/context.c:#include <openssl/err.h>\nluasec/context.c:#include <openssl/x509.h>\nluasec/context.c:#include <openssl/x509v3.h>\nluasec/context.c:#include <openssl/dh.h>\nluasec/context.c:#include \"../lua.h\"\nluasec/context.c:#include \"../lauxlib.h\"\nluasec/context.c:#include \"compat.h\"\nluasec/context.c:#include \"context.h\"\nluasec/context.c:#include \"options.h\"\nluasec/context.c:#include <openssl/ec.h>\nluasec/context.c:#include \"ec.h\"\nluasec/context.h:#include \"../lua.h\"\nluasec/context.h:#include <openssl/ssl.h>\nluasec/context.h:#include \"compat.h\"\nluasec/ec.c:#include <openssl/objects.h>\nluasec/ec.c:#include \"ec.h\"\nluasec/ec.h:#include \"../lua.h\"\nluasec/ec.h:#include <openssl/ec.h>\nluasec/options.c:#include <openssl/ssl.h>\nluasec/options.c:#include \"options.h\"\nluasec/options.h:#include \"compat.h\"\nluasec/options.lua:#include <openssl/ssl.h>\nluasec/options.lua:#include \"options.h\"\nluasec/ssl.c:#include <errno.h>\nluasec/ssl.c:#include <string.h>\nluasec/ssl.c:#include <winsock2.h>\nluasec/ssl.c:#include <openssl/ssl.h>\nluasec/ssl.c:#include <openssl/x509v3.h>\nluasec/ssl.c:#include <openssl/x509_vfy.h>\nluasec/ssl.c:#include <openssl/err.h>\nluasec/ssl.c:#include <openssl/dh.h>\nluasec/ssl.c:#include \"../lua.h\"\nluasec/ssl.c:#include \"../lauxlib.h\"\nluasec/ssl.c:#include \"../luasocket/io.h\"\nluasec/ssl.c:#include \"../luasocket/buffer.h\"\nluasec/ssl.c:#include \"../luasocket/timeout.h\"\nluasec/ssl.c:#include \"../luasocket/socket.h\"\nluasec/ssl.c:#include \"x509.h\"\nluasec/ssl.c:#include \"context.h\"\nluasec/ssl.c:#include \"ssl.h\"\nluasec/ssl.h:#include <openssl/ssl.h>\nluasec/ssl.h:#include \"../lua.h\"\nluasec/ssl.h:#include \"../luasocket/io.h\"\nluasec/ssl.h:#include \"../luasocket/buffer.h\"\nluasec/ssl.h:#include \"../luasocket/timeout.h\"\nluasec/ssl.h:#include \"../luasocket/socket.h\"\nluasec/ssl.h:#include \"compat.h\"\nluasec/ssl.h:#include \"context.h\"\nluasec/x509.c:#include <stdio.h>\nluasec/x509.c:#include <string.h>\nluasec/x509.c:#include <ws2tcpip.h>\nluasec/x509.c:#include <windows.h>\nluasec/x509.c:#include <sys/types.h>\nluasec/x509.c:#include <sys/socket.h>\nluasec/x509.c:#include <netinet/in.h>\nluasec/x509.c:#include <arpa/inet.h>\nluasec/x509.c:#include <openssl/ssl.h>\nluasec/x509.c:#include <openssl/x509v3.h>\nluasec/x509.c:#include <openssl/evp.h>\nluasec/x509.c:#include <openssl/err.h>\nluasec/x509.c:#include <openssl/asn1.h>\nluasec/x509.c:#include <openssl/bio.h>\nluasec/x509.c:#include <openssl/bn.h>\nluasec/x509.c:#include \"../lua.h\"\nluasec/x509.c:#include \"../lauxlib.h\"\nluasec/x509.c:#include \"x509.h\"\nluasec/x509.h:#include <openssl/x509v3.h>\nluasec/x509.h:#include \"../lua.h\"\nluasec/x509.h:#include \"compat.h\"\nluasocket/auxiliar.c:#include \"luasocket.h\"\nluasocket/auxiliar.c:#include \"auxiliar.h\"\nluasocket/auxiliar.c:#include <string.h>\nluasocket/auxiliar.c:#include <stdio.h>\nluasocket/auxiliar.h:#include \"luasocket.h\"\nluasocket/buffer.c:#include \"luasocket.h\"\nluasocket/buffer.c:#include \"buffer.h\"\nluasocket/buffer.h:#include \"luasocket.h\"\nluasocket/buffer.h:#include \"io.h\"\nluasocket/buffer.h:#include \"timeout.h\"\nluasocket/compat.c:#include \"luasocket.h\"\nluasocket/compat.c:#include \"compat.h\"\nluasocket/except.c:#include \"luasocket.h\"\nluasocket/except.c:#include \"except.h\"\nluasocket/except.c:#include <stdio.h>\nluasocket/except.h:#include \"luasocket.h\"\nluasocket/inet.c:#include \"luasocket.h\"\nluasocket/inet.c:#include \"inet.h\"\nluasocket/inet.c:#include <stdio.h>\nluasocket/inet.c:#include <stdlib.h>\nluasocket/inet.c:#include <string.h>\nluasocket/inet.h:#include \"luasocket.h\"\nluasocket/inet.h:#include \"socket.h\"\nluasocket/inet.h:#include \"timeout.h\"\nluasocket/io.c:#include \"luasocket.h\"\nluasocket/io.c:#include \"io.h\"\nluasocket/io.h:#include \"luasocket.h\"\nluasocket/io.h:#include \"timeout.h\"\nluasocket/luasocket.c:#include \"luasocket.h\"\nluasocket/luasocket.c:#include \"auxiliar.h\"\nluasocket/luasocket.c:#include \"except.h\"\nluasocket/luasocket.c:#include \"timeout.h\"\nluasocket/luasocket.c:#include \"buffer.h\"\nluasocket/luasocket.c:#include \"inet.h\"\nluasocket/luasocket.c:#include \"tcp.h\"\nluasocket/luasocket.c:#include \"udp.h\"\nluasocket/luasocket.c:#include \"select.h\"\nluasocket/luasocket.h:#include \"../lua.h\"\nluasocket/luasocket.h:#include \"../lauxlib.h\"\nluasocket/luasocket.h:#include \"compat.h\"\nluasocket/mime.c:#include \"luasocket.h\"\nluasocket/mime.c:#include \"mime.h\"\nluasocket/mime.c:#include <string.h>\nluasocket/mime.c:#include <ctype.h>\nluasocket/mime.h:#include \"luasocket.h\"\nluasocket/options.c:#include \"luasocket.h\"\nluasocket/options.c:#include \"auxiliar.h\"\nluasocket/options.c:#include \"options.h\"\nluasocket/options.c:#include \"inet.h\"\nluasocket/options.c:#include <string.h>\nluasocket/options.h:#include \"luasocket.h\"\nluasocket/options.h:#include \"socket.h\"\nluasocket/select.c:#include \"luasocket.h\"\nluasocket/select.c:#include \"socket.h\"\nluasocket/select.c:#include \"timeout.h\"\nluasocket/select.c:#include \"select.h\"\nluasocket/select.c:#include <string.h>\nluasocket/serial.c:#include \"luasocket.h\"\nluasocket/serial.c:#include \"auxiliar.h\"\nluasocket/serial.c:#include \"socket.h\"\nluasocket/serial.c:#include \"options.h\"\nluasocket/serial.c:#include \"unix.h\"\nluasocket/serial.c:#include <string.h>\nluasocket/serial.c:#include <sys/un.h>\nluasocket/socket.h:#include \"io.h\"\nluasocket/socket.h:#include \"wsocket.h\"\nluasocket/socket.h:#include \"usocket.h\"\nluasocket/socket.h:#include \"timeout.h\"\nluasocket/tcp.c:#include \"luasocket.h\"\nluasocket/tcp.c:#include \"auxiliar.h\"\nluasocket/tcp.c:#include \"socket.h\"\nluasocket/tcp.c:#include \"inet.h\"\nluasocket/tcp.c:#include \"options.h\"\nluasocket/tcp.c:#include \"tcp.h\"\nluasocket/tcp.c:#include <string.h>\nluasocket/tcp.h:#include \"luasocket.h\"\nluasocket/tcp.h:#include \"buffer.h\"\nluasocket/tcp.h:#include \"timeout.h\"\nluasocket/tcp.h:#include \"socket.h\"\nluasocket/timeout.c:#include \"luasocket.h\"\nluasocket/timeout.c:#include \"auxiliar.h\"\nluasocket/timeout.c:#include \"timeout.h\"\nluasocket/timeout.c:#include <stdio.h>\nluasocket/timeout.c:#include <limits.h>\nluasocket/timeout.c:#include <float.h>\nluasocket/timeout.c:#include <windows.h>\nluasocket/timeout.c:#include <time.h>\nluasocket/timeout.c:#include <sys/time.h>\nluasocket/timeout.h:#include \"luasocket.h\"\nluasocket/udp.c:#include \"luasocket.h\"\nluasocket/udp.c:#include \"auxiliar.h\"\nluasocket/udp.c:#include \"socket.h\"\nluasocket/udp.c:#include \"inet.h\"\nluasocket/udp.c:#include \"options.h\"\nluasocket/udp.c:#include \"udp.h\"\nluasocket/udp.c:#include <string.h>\nluasocket/udp.c:#include <stdlib.h>\nluasocket/udp.h:#include \"luasocket.h\"\nluasocket/udp.h:#include \"timeout.h\"\nluasocket/udp.h:#include \"socket.h\"\nluasocket/unix.c:#include \"luasocket.h\"\nluasocket/unix.c:#include \"unixstream.h\"\nluasocket/unix.c:#include \"unixdgram.h\"\nluasocket/unix.h:#include \"luasocket.h\"\nluasocket/unix.h:#include \"buffer.h\"\nluasocket/unix.h:#include \"timeout.h\"\nluasocket/unix.h:#include \"socket.h\"\nluasocket/unixdgram.c:#include \"luasocket.h\"\nluasocket/unixdgram.c:#include \"auxiliar.h\"\nluasocket/unixdgram.c:#include \"socket.h\"\nluasocket/unixdgram.c:#include \"options.h\"\nluasocket/unixdgram.c:#include \"unix.h\"\nluasocket/unixdgram.c:#include <string.h>\nluasocket/unixdgram.c:#include <stdlib.h>\nluasocket/unixdgram.c:#include <sys/un.h>\nluasocket/unixdgram.h:#include \"unix.h\"\nluasocket/unixstream.c:#include \"luasocket.h\"\nluasocket/unixstream.c:#include \"auxiliar.h\"\nluasocket/unixstream.c:#include \"socket.h\"\nluasocket/unixstream.c:#include \"options.h\"\nluasocket/unixstream.c:#include \"unixstream.h\"\nluasocket/unixstream.c:#include <string.h>\nluasocket/unixstream.c:#include <sys/un.h>\nluasocket/unixstream.h:#include \"unix.h\"\nluasocket/usocket.c:#include \"luasocket.h\"\nluasocket/usocket.c:#include \"socket.h\"\nluasocket/usocket.c:#include \"pierror.h\"\nluasocket/usocket.c:#include <string.h>\nluasocket/usocket.c:#include <signal.h>\nluasocket/usocket.c:#include <sys/poll.h>\nluasocket/usocket.h:#include <errno.h>\nluasocket/usocket.h:#include <unistd.h>\nluasocket/usocket.h:#include <fcntl.h>\nluasocket/usocket.h:#include <sys/types.h>\nluasocket/usocket.h:#include <sys/socket.h>\nluasocket/usocket.h:#include <sys/time.h>\nluasocket/usocket.h:#include <netdb.h>\nluasocket/usocket.h:#include <signal.h>\nluasocket/usocket.h:#include <netinet/in.h>\nluasocket/usocket.h:#include <arpa/inet.h>\nluasocket/usocket.h:#include <netinet/tcp.h>\nluasocket/usocket.h:#include <net/if.h>\nluasocket/usocket.h:#include <sys/poll.h>\nluasocket/wsocket.c:#include \"luasocket.h\"\nluasocket/wsocket.c:#include <string.h>\nluasocket/wsocket.c:#include \"socket.h\"\nluasocket/wsocket.c:#include \"pierror.h\"\nluasocket/wsocket.h:#include <winsock2.h>\nluasocket/wsocket.h:#include <ws2tcpip.h>\n"
  },
  {
    "path": "sandboxing/system_includes",
    "content": "kilo.c:#include <assert.h>\nkilo.c:#include <ncurses.h>\nkilo.c:#include <stdlib.h>\nkilo.c:#include <stdio.h>\nkilo.c:#include <stdint.h>\nkilo.c:#include <errno.h>\nkilo.c:#include <string.h>\nkilo.c:#include <ctype.h>\nkilo.c:#include <time.h>\nkilo.c:#include <sys/types.h>\nkilo.c:#include <sys/time.h>\nkilo.c:#include <unistd.h>\nkilo.c:#include <fcntl.h>\nlapi.c:#include <assert.h>\nlapi.c:#include <math.h>\nlapi.c:#include <stdarg.h>\nlapi.c:#include <string.h>\nlauxlib.c:#include <ctype.h>\nlauxlib.c:#include <errno.h>\nlauxlib.c:#include <stdarg.h>\nlauxlib.c:#include <stdio.h>\nlauxlib.c:#include <stdlib.h>\nlauxlib.c:#include <string.h>\nlauxlib.h:#include <stddef.h>\nlauxlib.h:#include <stdio.h>\nlbaselib.c:#include <ctype.h>\nlbaselib.c:#include <ncurses.h>\nlbaselib.c:#include <stdio.h>\nlbaselib.c:#include <stdlib.h>\nlbaselib.c:#include <string.h>\nlcode.c:#include <stdlib.h>\nldblib.c:#include <stdio.h>\nldblib.c:#include <stdlib.h>\nldblib.c:#include <string.h>\nldebug.c:#include <stdarg.h>\nldebug.c:#include <stddef.h>\nldebug.c:#include <string.h>\nldo.c:#include <setjmp.h>\nldo.c:#include <stdio.h>\nldo.c:#include <stdlib.h>\nldo.c:#include <string.h>\nldump.c:#include <stddef.h>\nlfunc.c:#include <stddef.h>\nlgc.c:#include <string.h>\nliolib.c:#include <errno.h>\nliolib.c:#include <stdio.h>\nliolib.c:#include <stdlib.h>\nliolib.c:#include <string.h>\nllex.c:#include <ctype.h>\nllex.c:#include <locale.h>\nllex.c:#include <string.h>\nllimits.h:#include <limits.h>\nllimits.h:#include <stddef.h>\nlmathlib.c:#include <stdlib.h>\nlmathlib.c:#include <math.h>\nlmem.c:#include <stddef.h>\nlmem.h:#include <stddef.h>\nloadlib.c:#include <stdlib.h>\nloadlib.c:#include <string.h>\nloadlib.c:#include <dlfcn.h>\nloadlib.c:#include <windows.h>\nloadlib.c:#include <mach-o/dyld.h>\nlobject.c:#include <ctype.h>\nlobject.c:#include <stdarg.h>\nlobject.c:#include <stdio.h>\nlobject.c:#include <stdlib.h>\nlobject.c:#include <string.h>\nlobject.h:#include <stdarg.h>\nloslib.c:#include <errno.h>\nloslib.c:#include <locale.h>\nloslib.c:#include <stdlib.h>\nloslib.c:#include <string.h>\nloslib.c:#include <time.h>\nlparser.c:#include <string.h>\nlstate.c:#include <stddef.h>\nlstring.c:#include <string.h>\nlstrlib.c:#include <ctype.h>\nlstrlib.c:#include <stddef.h>\nlstrlib.c:#include <stdio.h>\nlstrlib.c:#include <stdlib.h>\nlstrlib.c:#include <string.h>\nltable.c:#include <math.h>\nltable.c:#include <string.h>\nltablib.c:#include <stddef.h>\nltm.c:#include <string.h>\nlua.c:#include <assert.h>\nlua.c:#include <ctype.h>\nlua.c:#include <fcntl.h>\nlua.c:#include <locale.h>\nlua.c:#include <ncurses.h>\nlua.c:#include <signal.h>\nlua.c:#include <stdio.h>\nlua.c:#include <stdlib.h>\nlua.c:#include <string.h>\nlua.c:#include <time.h>\nlua.c:#include <unistd.h>\nlua.h:#include <stdarg.h>\nlua.h:#include <stddef.h>\nluaconf.h:#include <limits.h>\nluaconf.h:#include <stddef.h>\nluaconf.h:#include <assert.h>\nluaconf.h:#include <math.h>\nluaconf.h:#include <unistd.h>\nluaconf.h:#include <stdio.h>\nlundump.c:#include <string.h>\nlvm.c:#include <stdio.h>\nlvm.c:#include <stdlib.h>\nlvm.c:#include <string.h>\nlzio.c:#include <string.h>\nmenu.c:#include <ncurses.h>\nmenu.c:#include <string.h>\ntlv.c:#include <assert.h>\ntlv.c:#include <ncurses.h>\ntlv.c:#include <stdio.h>\ntlv.c:#include <stdlib.h>\ntlv.c:#include <string.h>\ntlv.c:#include <strings.h>\nlcurses/_helpers.c:#include <errno.h>\nlcurses/_helpers.c:#include <grp.h>\nlcurses/_helpers.c:#include <pwd.h>\nlcurses/_helpers.c:#include <stdlib.h>\nlcurses/_helpers.c:#include <string.h>\nlcurses/_helpers.c:#include <sys/stat.h>\nlcurses/_helpers.c:#include <unistd.h>\nlcurses/_helpers.c:#include <ncurses.h>\nlcurses/_helpers.c:#include <term.h>\nlcurses/compat-5.2.c:#include <errno.h>\nlcurses/compat-5.2.c:#include <string.h>\nlcurses/compat-5.2.c:#include <limits.h>\nlcurses/compat-5.2.c:#include <math.h>\nlcurses/compat-5.2.h:#include <stddef.h>\nlcurses/compat-5.2.h:#include <string.h>\nlcurses/compat-5.2.h:#include <stdio.h>\nlcurses/compat-5.2.h:#include <limits.h>\nlcurses/strlcpy.c:#include <sys/types.h>\nlcurses/strlcpy.c:#include <string.h>\nluasec/compat.h:#include <openssl/ssl.h>\nluasec/context.c:#include <string.h>\nluasec/context.c:#include <windows.h>\nluasec/context.c:#include <openssl/ssl.h>\nluasec/context.c:#include <openssl/err.h>\nluasec/context.c:#include <openssl/x509.h>\nluasec/context.c:#include <openssl/x509v3.h>\nluasec/context.c:#include <openssl/dh.h>\nluasec/context.c:#include <openssl/ec.h>\nluasec/context.h:#include <openssl/ssl.h>\nluasec/ec.c:#include <openssl/objects.h>\nluasec/ec.h:#include <openssl/ec.h>\nluasec/options.c:#include <openssl/ssl.h>\nluasec/options.lua:#include <openssl/ssl.h>\nluasec/ssl.c:#include <errno.h>\nluasec/ssl.c:#include <string.h>\nluasec/ssl.c:#include <winsock2.h>\nluasec/ssl.c:#include <openssl/ssl.h>\nluasec/ssl.c:#include <openssl/x509v3.h>\nluasec/ssl.c:#include <openssl/x509_vfy.h>\nluasec/ssl.c:#include <openssl/err.h>\nluasec/ssl.c:#include <openssl/dh.h>\nluasec/ssl.h:#include <openssl/ssl.h>\nluasec/x509.c:#include <stdio.h>\nluasec/x509.c:#include <string.h>\nluasec/x509.c:#include <ws2tcpip.h>\nluasec/x509.c:#include <windows.h>\nluasec/x509.c:#include <sys/types.h>\nluasec/x509.c:#include <sys/socket.h>\nluasec/x509.c:#include <netinet/in.h>\nluasec/x509.c:#include <arpa/inet.h>\nluasec/x509.c:#include <openssl/ssl.h>\nluasec/x509.c:#include <openssl/x509v3.h>\nluasec/x509.c:#include <openssl/evp.h>\nluasec/x509.c:#include <openssl/err.h>\nluasec/x509.c:#include <openssl/asn1.h>\nluasec/x509.c:#include <openssl/bio.h>\nluasec/x509.c:#include <openssl/bn.h>\nluasec/x509.h:#include <openssl/x509v3.h>\nluasocket/auxiliar.c:#include <string.h>\nluasocket/auxiliar.c:#include <stdio.h>\nluasocket/except.c:#include <stdio.h>\nluasocket/inet.c:#include <stdio.h>\nluasocket/inet.c:#include <stdlib.h>\nluasocket/inet.c:#include <string.h>\nluasocket/mime.c:#include <string.h>\nluasocket/mime.c:#include <ctype.h>\nluasocket/options.c:#include <string.h>\nluasocket/select.c:#include <string.h>\nluasocket/serial.c:#include <string.h>\nluasocket/serial.c:#include <sys/un.h>\nluasocket/tcp.c:#include <string.h>\nluasocket/timeout.c:#include <stdio.h>\nluasocket/timeout.c:#include <limits.h>\nluasocket/timeout.c:#include <float.h>\nluasocket/timeout.c:#include <windows.h>\nluasocket/timeout.c:#include <time.h>\nluasocket/timeout.c:#include <sys/time.h>\nluasocket/udp.c:#include <string.h>\nluasocket/udp.c:#include <stdlib.h>\nluasocket/unixdgram.c:#include <string.h>\nluasocket/unixdgram.c:#include <stdlib.h>\nluasocket/unixdgram.c:#include <sys/un.h>\nluasocket/unixstream.c:#include <string.h>\nluasocket/unixstream.c:#include <sys/un.h>\nluasocket/usocket.c:#include <string.h>\nluasocket/usocket.c:#include <signal.h>\nluasocket/usocket.c:#include <sys/poll.h>\nluasocket/usocket.h:#include <errno.h>\nluasocket/usocket.h:#include <unistd.h>\nluasocket/usocket.h:#include <fcntl.h>\nluasocket/usocket.h:#include <sys/types.h>\nluasocket/usocket.h:#include <sys/socket.h>\nluasocket/usocket.h:#include <sys/time.h>\nluasocket/usocket.h:#include <netdb.h>\nluasocket/usocket.h:#include <signal.h>\nluasocket/usocket.h:#include <netinet/in.h>\nluasocket/usocket.h:#include <arpa/inet.h>\nluasocket/usocket.h:#include <netinet/tcp.h>\nluasocket/usocket.h:#include <net/if.h>\nluasocket/usocket.h:#include <sys/poll.h>\nluasocket/wsocket.c:#include <string.h>\nluasocket/wsocket.h:#include <winsock2.h>\nluasocket/wsocket.h:#include <ws2tcpip.h>\n"
  },
  {
    "path": "sandboxing/unique_system_includes",
    "content": "arpa/inet.h\nassert.h\nctype.h\ndlfcn.h\nerrno.h\nfcntl.h\nfloat.h\ngrp.h\nlimits.h\nlocale.h\nmach-o/dyld.h\nmath.h\nncurses.h\nnet/if.h\nnetdb.h\nnetinet/in.h\nnetinet/tcp.h\nopenssl/asn1.h\nopenssl/bio.h\nopenssl/bn.h\nopenssl/dh.h\nopenssl/ec.h\nopenssl/err.h\nopenssl/evp.h\nopenssl/objects.h\nopenssl/ssl.h\nopenssl/x509.h\nopenssl/x509_vfy.h\nopenssl/x509v3.h\npwd.h\nsetjmp.h\nsignal.h\nstdarg.h\nstddef.h\nstdint.h\nstdio.h\nstdlib.h\nstring.h\nstrings.h\nsys/poll.h\nsys/socket.h\nsys/stat.h\nsys/time.h\nsys/types.h\nsys/un.h\nterm.h\ntime.h\nunistd.h\nwindows.h\nwinsock2.h\nws2tcpip.h\n"
  },
  {
    "path": "shell.nix",
    "content": "{ pkgs ? import (fetchTarball \"https://github.com/NixOS/nixpkgs/archive/nixos-21.11.tar.gz\") {} }:\n\npkgs.mkShell {\n\tLOCALE_ARCHIVE_2_27 = if (pkgs.glibcLocales != null) then \"${pkgs.glibcLocales}/lib/locale/locale-archive\" else \"\";\n\n\tbuildInputs = [\n\t\tpkgs.glibcLocales\n\t\tpkgs.git\n\t\tpkgs.gnumake\n\t\tpkgs.ncurses\n\t\tpkgs.gcc\n\t\tpkgs.openssl\n\t];\n\tshellHook = ''\n\t\texport LC_ALL=en_US.UTF-8\n\t\texport GIT_SSL_CAINFO=/etc/ssl/certs/ca-certificates.crt\n\t\texport SSL_CERT_FILE=/etc/ssl/certs/ca-certificates.crt\n\t'';\n}\n"
  },
  {
    "path": "sieve.tlv",
    "content": "# .tlv file generated by https://github.com/akkartik/teliva\n# You may edit it if you are careful; however, you may see cryptic errors if you\n# violate Teliva's assumptions.\n#\n# .tlv files are representations of Teliva programs. Teliva programs consist of\n# sequences of definitions. Each definition is a table of key/value pairs. Keys\n# and values are both strings.\n#\n# Lines in .tlv files always follow exactly one of the following forms:\n# - comment lines at the top of the file starting with '#' at column 0\n# - beginnings of definitions starting with '- ' at column 0, followed by a\n#   key/value pair\n# - key/value pairs consisting of '  ' at column 0, containing either a\n#   spaceless value on the same line, or a multi-line value\n# - multiline values indented by more than 2 spaces, starting with a '>'\n#\n# If these constraints are violated, Teliva may unceremoniously crash. Please\n# report bugs at http://akkartik.name/contact\n- __teliva_timestamp: original\n  check:\n    >function check(x, msg)\n    >  if x then\n    >    Window:addch('.')\n    >  else\n    >    print('F - '..msg)\n    >    print('  '..str(x)..' is false/nil')\n    >    teliva_num_test_failures = teliva_num_test_failures + 1\n    >    -- overlay first test failure on editors\n    >    if teliva_first_failure == nil then\n    >      teliva_first_failure = msg\n    >    end\n    >  end\n    >end\n- __teliva_timestamp: original\n  check_eq:\n    >function check_eq(x, expected, msg)\n    >  if eq(x, expected) then\n    >    Window:addch('.')\n    >  else\n    >    print('F - '..msg)\n    >    print('  expected '..str(expected)..' but got '..str(x))\n    >    teliva_num_test_failures = teliva_num_test_failures + 1\n    >    -- overlay first test failure on editors\n    >    if teliva_first_failure == nil then\n    >      teliva_first_failure = msg\n    >    end\n    >  end\n    >end\n- __teliva_timestamp: original\n  eq:\n    >function eq(a, b)\n    >  if type(a) ~= type(b) then return false end\n    >  if type(a) == 'table' then\n    >    if #a ~= #b then return false end\n    >    for k, v in pairs(a) do\n    >      if b[k] ~= v then\n    >        return false\n    >      end\n    >    end\n    >    for k, v in pairs(b) do\n    >      if a[k] ~= v then\n    >        return false\n    >      end\n    >    end\n    >    return true\n    >  end\n    >  return a == b\n    >end\n- __teliva_timestamp: original\n  str:\n    >-- smarter tostring\n    >-- slow; used only for debugging\n    >function str(x)\n    >  if type(x) == 'table' then\n    >    local result = ''\n    >    result = result..#x..'{'\n    >    for k, v in pairs(x) do\n    >      result = result..str(k)..'='..str(v)..', '\n    >    end\n    >    result = result..'}'\n    >    return result\n    >  elseif type(x) == 'string' then\n    >    return '\"'..x..'\"'\n    >  end\n    >  return tostring(x)\n    >end\n- __teliva_timestamp: original\n  menu:\n    >-- To show app-specific hotkeys in the menu bar, add hotkey/command\n    >-- arrays of strings to the menu array.\n    >menu = {}\n- __teliva_timestamp: original\n  Window:\n    >Window = curses.stdscr()\n- __teliva_timestamp: original\n  main:\n    >function main()\n    >  task.spawn(main_task)\n    >  task.scheduler()\n    >  Window:refresh()\n    >  Window:getch()\n    >end\n- __teliva_timestamp: original\n  main_task:\n    >function main_task()\n    >  Window:clear()\n    >  local c = task.Channel:new()\n    >  task.spawn(counter, c)\n    >  for i=1,10 do\n    >    print(c:recv())\n    >  end\n    >end\n- __teliva_timestamp:\n    >Sat Feb 26 21:50:11 2022\n  __teliva_note:\n    >a simple counter\n  counter:\n    >function counter(c)\n    >  local i = 2\n    >  while true do\n    >    c:send(i)\n    >    i = i+1\n    >  end\n    >end\n- __teliva_timestamp:\n    >Sat Feb 26 21:54:53 2022\n  filter_task:\n    >function filter_task(p, cin, cout)\n    >  while true do\n    >    local i = cin:recv()\n    >    if i%p ~= 0 then\n    >      cout:send(i)\n    >    end\n    >  end\n    >end\n- __teliva_timestamp:\n    >Sat Feb 26 21:55:46 2022\n  main_task:\n    >function main_task()\n    >  local primes = task.Channel:new()\n    >  task.spawn(sieve, primes)\n    >  for i=1,10 do\n    >    print(primes:recv())\n    >  end\n    >end\n- __teliva_timestamp:\n    >Sat Feb 26 21:59:37 2022\n  __teliva_note:\n    >filter out multiples of a single number\n  sieve:\n    >function sieve(ch)\n    >  local iota = task.Channel:new()\n    >  task.spawn(counter, iota)\n    >  task.spawn(filter_task, 2, iota, ch)\n    >end\n- __teliva_timestamp:\n    >Sat Feb 26 22:08:07 2022\n  __teliva_note:\n    >implement the complete sieve algorithm\n  sieve:\n    >-- Set up a Sieve of Eratosthenes (https://en.wikipedia.org/wiki/Sieve_of_Eratosthenes)\n    >-- for computing prime numbers by chaining tasks, one per prime.\n    >-- Each task is responsible for filtering out all multiples of its prime.\n    >function sieve(primes_ch)\n    >  local c = task.Channel:new()\n    >  task.spawn(counter, c)\n    >  while true do\n    >    local p, newc = c:recv(), task.Channel:new()\n    >    primes_ch:send(p)\n    >    task.spawn(filter_task, p, c, newc)\n    >    c = newc\n    >  end\n    >end\n- __teliva_timestamp:\n    >Sat Feb 26 22:09:47 2022\n  __teliva_note:\n    >infinite primes\n  main_task:\n    >function main_task()\n    >  local primes = task.Channel:new()\n    >  task.spawn(sieve, primes)\n    >  while true do\n    >    Window:addstr(primes:recv())\n    >    Window:addstr(' ')\n    >    Window:refresh()\n    >  end\n    >end\n- __teliva_timestamp:\n    >Sat Feb 26 22:09:47 2022\n  __teliva_note:\n    >clear screen when it fills up; pause on keypress\n    >\n    >In Teliva getch() implicitly refreshes the screen.\n  main_task:\n    >function main_task()\n    >  Window:nodelay(true)\n    >  Window:clear()\n    >  local primes = task.Channel:new()\n    >  task.spawn(sieve, primes)\n    >  local h, w = Window:getmaxyx()\n    >  while true do\n    >    Window:addstr(primes:recv())\n    >    Window:addstr(' ')\n    >    local c = Window:getch()\n    >    if c then break end  -- key pressed\n    >    local y, x = Window:getyx()\n    >    if y > h-1 then\n    >      Window:clear()\n    >    end\n    >  end\n    >  print('key pressed; done')\n    >  Window:nodelay(false)\n    >end\n- __teliva_timestamp:\n    >Sat Feb 26 22:27:25 2022\n  doc:blurb:\n    >Sieve of Eratosthenes\n    >https://en.wikipedia.org/wiki/Sieve_of_Eratosthenes\n    >\n    >A demonstration of tasks and channels, the primitives for (cooperative) concurrency in Teliva.\n    >\n    >We string together a cascade of tasks connected by channels. Every prime number gets a new task that prints the first incoming number, and then filters out multiples of it from the incoming channel.\n    >\n    >This approach has the advantage that we don't need to create an array of n numbers to compute primes less than n.\n    >\n    >However, we still need to create p tasks and p channels if there are p primes less than n. Probably not worth it, given tasks and channels are much larger than numbers. This is just a demo.\n    >\n    >The noticeable periodic pauses are perhaps due to garbage collection.\n"
  },
  {
    "path": "smol.tlv",
    "content": "# .tlv file generated by https://github.com/akkartik/teliva\n# You may edit it if you are careful; however, you may see cryptic errors if you\n# violate Teliva's assumptions.\n#\n# .tlv files are representations of Teliva programs. Teliva programs consist of\n# sequences of definitions. Each definition is a table of key/value pairs. Keys\n# and values are both strings.\n#\n# Lines in .tlv files always follow exactly one of the following forms:\n# - comment lines at the top of the file starting with '#' at column 0\n# - beginnings of definitions starting with '- ' at column 0, followed by a\n#   key/value pair\n# - key/value pairs consisting of '  ' at column 0, containing either a\n#   spaceless value on the same line, or a multi-line value\n# - multiline values indented by more than 2 spaces, starting with a '>'\n#\n# If these constraints are violated, Teliva may unceremoniously crash. Please\n# report bugs at http://akkartik.name/contact\n- __teliva_timestamp: original\n  str_helpers:\n    >-- some string helpers from http://lua-users.org/wiki/StringIndexing\n    >\n    >-- index characters using []\n    >getmetatable('').__index = function(str,i)\n    >  if type(i) == 'number' then\n    >    return str:sub(i,i)\n    >  else\n    >    return string[i]\n    >  end\n    >end\n    >\n    >-- ranges using (), selected bytes using {}\n    >getmetatable('').__call = function(str,i,j)\n    >  if type(i)~='table' then\n    >    return str:sub(i,j)\n    >  else\n    >    local t={}\n    >    for k,v in ipairs(i) do\n    >      t[k]=str:sub(v,v)\n    >    end\n    >    return table.concat(t)\n    >  end\n    >end\n    >\n    >-- iterate over an ordered sequence\n    >function q(x)\n    >  if type(x) == 'string' then\n    >    return x:gmatch('.')\n    >  else\n    >    return ipairs(x)\n    >  end\n    >end\n    >\n    >-- insert within string\n    >function string.insert(str1, str2, pos)\n    >  return str1:sub(1,pos)..str2..str1:sub(pos+1)\n    >end\n    >\n    >function string.remove(s, pos)\n    >  return s:sub(1,pos-1)..s:sub(pos+1)\n    >end\n    >\n    >function string.pos(s, sub)\n    >  return string.find(s, sub, 1, true)  -- plain=true to disable regular expressions\n    >end\n    >\n    >-- TODO: backport utf-8 support from Lua 5.3\n- __teliva_timestamp: original\n  debugy:\n    >debugy = 5\n- __teliva_timestamp: original\n  dbg:\n    >-- helper for debug by print; overlay debug information towards the right\n    >-- reset debugy every time you refresh screen\n    >function dbg(window, s)\n    >  local oldy = 0\n    >  local oldx = 0\n    >  oldy, oldx = window:getyx()\n    >  window:mvaddstr(debugy, 60, s)\n    >  debugy = debugy+1\n    >  window:mvaddstr(oldy, oldx, '')\n    >end\n- __teliva_timestamp: original\n  check:\n    >function check(x, msg)\n    >  if x then\n    >    Window:addch('.')\n    >  else\n    >    print('F - '..msg)\n    >    print('  '..str(x)..' is false/nil')\n    >    teliva_num_test_failures = teliva_num_test_failures + 1\n    >    -- overlay first test failure on editors\n    >    if teliva_first_failure == nil then\n    >      teliva_first_failure = msg\n    >    end\n    >  end\n    >end\n- __teliva_timestamp: original\n  check_eq:\n    >function check_eq(x, expected, msg)\n    >  if eq(x, expected) then\n    >    Window:addch('.')\n    >  else\n    >    print('F - '..msg)\n    >    print('  expected '..str(expected)..' but got '..str(x))\n    >    teliva_num_test_failures = teliva_num_test_failures + 1\n    >    -- overlay first test failure on editors\n    >    if teliva_first_failure == nil then\n    >      teliva_first_failure = msg\n    >    end\n    >  end\n    >end\n- __teliva_timestamp: original\n  eq:\n    >function eq(a, b)\n    >  if type(a) ~= type(b) then return false end\n    >  if type(a) == 'table' then\n    >    if #a ~= #b then return false end\n    >    for k, v in pairs(a) do\n    >      if b[k] ~= v then\n    >        return false\n    >      end\n    >    end\n    >    for k, v in pairs(b) do\n    >      if a[k] ~= v then\n    >        return false\n    >      end\n    >    end\n    >    return true\n    >  end\n    >  return a == b\n    >end\n- __teliva_timestamp: original\n  str:\n    >-- smarter tostring\n    >-- slow; used only for debugging\n    >function str(x)\n    >  if type(x) == 'table' then\n    >    local result = ''\n    >    result = result..#x..'{'\n    >    for k, v in pairs(x) do\n    >      result = result..str(k)..'='..str(v)..', '\n    >    end\n    >    result = result..'}'\n    >    return result\n    >  elseif type(x) == 'string' then\n    >    return '\"'..x..'\"'\n    >  end\n    >  return tostring(x)\n    >end\n- __teliva_timestamp: original\n  find_index:\n    >function find_index(arr, x)\n    >  for n, y in ipairs(arr) do\n    >    if x == y then\n    >      return n\n    >    end\n    >  end\n    >end\n- __teliva_timestamp: original\n  trim:\n    >function trim(s)\n    >  return s:gsub('^%s*', ''):gsub('%s*$', '')\n    >end\n- __teliva_timestamp: original\n  split:\n    >function split(s, d)\n    >  result = {}\n    >  for match in (s..d):gmatch(\"(.-)\"..d) do\n    >    table.insert(result, match);\n    >  end\n    >  return result\n    >end\n- __teliva_timestamp: original\n  map:\n    >-- only for arrays\n    >function map(l, f)\n    >  result = {}\n    >  for _, x in ipairs(l) do\n    >    table.insert(result, f(x))\n    >  end\n    >  return result\n    >end\n- __teliva_timestamp: original\n  reduce:\n    >-- only for arrays\n    >function reduce(l, f, init)\n    >  result = init\n    >  for _, x in ipairs(l) do\n    >    result = f(result, x)\n    >  end\n    >  return result\n    >end\n- __teliva_timestamp: original\n  filter:\n    >function filter(h, f)\n    >  result = {}\n    >  for k, v in pairs(h) do\n    >    if f(k, v) then\n    >      result[k] = v\n    >    end\n    >  end\n    >  return result\n    >end\n- __teliva_timestamp: original\n  ifilter:\n    >-- only for arrays\n    >function ifilter(l, f)\n    >  result = {}\n    >  for _, x in ipairs(l) do\n    >    if f(x) then\n    >      table.insert(result, x)\n    >    end\n    >  end\n    >  return result\n    >end\n- __teliva_timestamp: original\n  sort_letters:\n    >function sort_letters(s)\n    >  tmp = {}\n    >  for i=1,#s do\n    >    table.insert(tmp, s[i])\n    >  end\n    >  table.sort(tmp)\n    >  local result = ''\n    >  for _, c in pairs(tmp) do\n    >    result = result..c\n    >  end\n    >  return result\n    >end\n    >\n    >function test_sort_letters(s)\n    >  check_eq(sort_letters(''), '', 'test_sort_letters: empty')\n    >  check_eq(sort_letters('ba'), 'ab', 'test_sort_letters: non-empty')\n    >  check_eq(sort_letters('abba'), 'aabb', 'test_sort_letters: duplicates')\n    >end\n- __teliva_timestamp: original\n  count_letters:\n    >-- TODO: handle unicode\n    >function count_letters(s)\n    >  local result = {}\n    >  for i=1,s:len() do\n    >    local c = s[i]\n    >    if result[c] == nil then\n    >      result[c] = 1\n    >    else\n    >      result[c] = result[c] + 1\n    >    end\n    >  end\n    >  return result\n    >end\n- __teliva_timestamp: original\n  count:\n    >-- turn an array of elements into a map from elements to their frequency\n    >-- analogous to count_letters for non-strings\n    >function count(a)\n    >  local result = {}\n    >  for i, v in ipairs(a) do\n    >    if result[v] == nil then\n    >      result[v] = 1\n    >    else\n    >      result[v] = result[v] + 1\n    >    end\n    >  end\n    >  return result\n    >end\n- __teliva_timestamp: original\n  union:\n    >function union(a, b)\n    >  for k, v in pairs(b) do\n    >    a[k] = v\n    >  end\n    >  return a\n    >end\n- __teliva_timestamp: original\n  subtract:\n    >-- set subtraction\n    >function subtract(a, b)\n    >  for k, v in pairs(b) do\n    >    a[k] = nil\n    >  end\n    >  return a\n    >end\n- __teliva_timestamp: original\n  all:\n    >-- universal quantifier on sets\n    >function all(s, f)\n    >  for k, v in pairs(s) do\n    >    if not f(k, v) then\n    >      return false\n    >    end\n    >  end\n    >  return true\n    >end\n- __teliva_timestamp: original\n  to_array:\n    >-- turn a set into an array\n    >-- drops values\n    >function to_array(h)\n    >  local result = {}\n    >  for k, _ in pairs(h) do\n    >    table.insert(result, k)\n    >  end\n    >  return result\n    >end\n- __teliva_timestamp: original\n  append:\n    >-- concatenate list 'elems' into 'l', modifying 'l' in the process\n    >function append(l, elems)\n    >  for i=1,#elems do\n    >    table.insert(l, elems[i])\n    >  end\n    >end\n- __teliva_timestamp: original\n  prepend:\n    >-- concatenate list 'elems' into the start of 'l', modifying 'l' in the process\n    >function prepend(l, elems)\n    >  for i=1,#elems do\n    >    table.insert(l, i, elems[i])\n    >  end\n    >end\n- __teliva_timestamp: original\n  all_but:\n    >function all_but(x, idx)\n    >  if type(x) == 'table' then\n    >    local result = {}\n    >    for i, elem in ipairs(x) do\n    >      if i ~= idx then\n    >        table.insert(result,elem)\n    >      end\n    >    end\n    >    return result\n    >  elseif type(x) == 'string' then\n    >    if idx < 1 then return x:sub(1) end\n    >    return x:sub(1, idx-1) .. x:sub(idx+1)\n    >  else\n    >    error('all_but: unsupported type '..type(x))\n    >  end\n    >end\n    >\n    >function test_all_but()\n    >  check_eq(all_but('', 0), '', 'all_but: empty')\n    >  check_eq(all_but('abc', 0), 'abc', 'all_but: invalid low index')\n    >  check_eq(all_but('abc', 4), 'abc', 'all_but: invalid high index')\n    >  check_eq(all_but('abc', 1), 'bc', 'all_but: first index')\n    >  check_eq(all_but('abc', 3), 'ab', 'all_but: final index')\n    >  check_eq(all_but('abc', 2), 'ac', 'all_but: middle index')\n    >end\n- __teliva_timestamp: original\n  set:\n    >function set(l)\n    >  local result = {}\n    >  for i, elem in ipairs(l) do\n    >    result[elem] = true\n    >  end\n    >  return result\n    >end\n- __teliva_timestamp: original\n  set_eq:\n    >function set_eq(l1, l2)\n    >  return eq(set(l1), set(l2))\n    >end\n    >\n    >function test_set_eq()\n    >  check(set_eq({1}, {1}), 'set_eq: identical')\n    >  check(not set_eq({1, 2}, {1, 3}), 'set_eq: different')\n    >  check(set_eq({1, 2}, {2, 1}), 'set_eq: order')\n    >  check(set_eq({1, 2, 2}, {2, 1}), 'set_eq: duplicates')\n    >end\n- __teliva_timestamp: original\n  clear:\n    >function clear(lines)\n    >  while #lines > 0 do\n    >    table.remove(lines)\n    >  end\n    >end\n- __teliva_timestamp: original\n  zap:\n    >function zap(target, src)\n    >  clear(target)\n    >  append(target, src)\n    >end\n- __teliva_timestamp: original\n  mfactorial:\n    >-- memoized version of factorial\n    >-- doesn't memoize recursive calls, but may be good enough\n    >mfactorial = memo1(factorial)\n- __teliva_timestamp: original\n  factorial:\n    >function factorial(n)\n    >  local result = 1\n    >  for i=1,n do\n    >    result = result*i\n    >  end\n    >  return result\n    >end\n- __teliva_timestamp: original\n  memo1:\n    >-- a higher-order function that takes a function of a single arg\n    >-- (that never returns nil)\n    >-- and returns a memoized version of it\n    >function memo1(f)\n    >  local memo = {}\n    >  return function(x)\n    >    if memo[x] == nil then\n    >      memo[x] = f(x)\n    >    end\n    >    return memo[x]\n    >  end\n    >end\n    >\n    >-- mfactorial doesn't seem noticeably faster\n    >function test_memo1()\n    >  for i=0,30 do\n    >    check_eq(mfactorial(i), factorial(i), 'memo1 over factorial: '..str(i))\n    >  end\n    >end\n- __teliva_timestamp: original\n  num_permutations:\n    >-- number of permutations of n distinct objects, taken r at a time\n    >function num_permutations(n, r)\n    >  return factorial(n)/factorial(n-r)\n    >end\n    >\n    >-- mfactorial doesn't seem noticeably faster\n    >function test_memo1()\n    >  for i=0,30 do\n    >    for j=0,i do\n    >      check_eq(num_permutations(i, j), mfactorial(i)/mfactorial(i-j), 'num_permutations memoizes: '..str(i)..'P'..str(j))\n    >    end\n    >  end\n    >end\n- __teliva_timestamp: original\n  menu:\n    >-- To show app-specific hotkeys in the menu bar, add hotkey/command\n    >-- arrays of strings to the menu array.\n    >menu = {}\n- __teliva_timestamp: original\n  Window:\n    >Window = curses.stdscr()\n- __teliva_timestamp: original\n  window:\n    >-- constructor for fake screen and window\n    >-- call it like this:\n    >--   local w = window{\n    >--     kbd=kbd('abc'),\n    >--     scr=scr{h=5, w=4},\n    >--   }\n    >-- eventually it'll do everything a real ncurses window can\n    >function window(h)\n    >  h.__index = h\n    >  setmetatable(h, h)\n    >  h.__index = function(table, key)\n    >    return rawget(h, key)\n    >  end\n    >  h.attrset = function(self, x)\n    >    self.scr.attrs = x\n    >  end\n    >  h.attron = function(self, x)\n    >    -- currently same as attrset since Lua 5.1 doesn't have bitwise operators\n    >    -- doesn't support multiple attrs at once\n    >--    local old = self.scr.attrs\n    >--    self.scr.attrs = old|x\n    >    self.scr.attrs = x\n    >  end\n    >  h.attroff = function(self, x)\n    >    -- currently borked since Lua 5.1 doesn't have bitwise operators\n    >    -- doesn't support multiple attrs at once\n    >--    local old = self.scr.attrs\n    >--    self.scr.attrs = old & (~x)\n    >    self.scr.attrs = curses.A_NORMAL\n    >  end\n    >  h.getch = function(self)\n    >    local c = table.remove(h.kbd, 1)\n    >    if c == nil then return c end\n    >    return string.byte(c)  -- for verisimilitude with ncurses\n    >  end\n    >  h.addch = function(self, c)\n    >    local scr = self.scr\n    >    if c == '\\n' then\n    >      scr.cursy = scr.cursy+1\n    >      scr.cursx = 0\n    >      return\n    >    end\n    >    if scr.cursy <= scr.h then\n    >      scr[scr.cursy][scr.cursx] = {data=c, attrs=scr.attrs}\n    >      scr.cursx = scr.cursx+1\n    >      if scr.cursx > scr.w then\n    >        scr.cursy = scr.cursy+1\n    >        scr.cursx = 1\n    >      end\n    >    end\n    >  end\n    >  h.addstr = function(self, s)\n    >    for i=1,s:len() do\n    >      self:addch(s[i])\n    >    end\n    >  end\n    >  h.mvaddch = function(self, y, x, c)\n    >    self.scr.cursy = y\n    >    self.scr.cursx = x\n    >    self:addch(c)\n    >  end\n    >  h.mvaddstr = function(self, y, x, s)\n    >    self.scr.cursy = y\n    >    self.scr.cursx = x\n    >    self:addstr(s)\n    >  end\n    >  h.clear = function(self)\n    >    clear_scr(self.scr)\n    >  end\n    >  h.refresh = function(self)\n    >    -- nothing\n    >  end\n    >  return h\n    >end\n- __teliva_timestamp: original\n  kbd:\n    >function kbd(keys)\n    >  local result = {}\n    >  for i=1,keys:len() do\n    >    table.insert(result, keys[i])\n    >  end\n    >  return result\n    >end\n- __teliva_timestamp: original\n  scr:\n    >function scr(props)\n    >  props.cursx = 1\n    >  props.cursy = 1\n    >  clear_scr(props)\n    >  return props\n    >end\n- __teliva_timestamp: original\n  clear_scr:\n    >function clear_scr(props)\n    >  props.cursy = 1\n    >  props.cursx = 1\n    >  for y=1,props.h do\n    >    props[y] = {}\n    >    for x=1,props.w do\n    >      props[y][x] = {data=' ', attrs=curses.A_NORMAL}\n    >    end\n    >  end\n    >  return props\n    >end\n- __teliva_timestamp: original\n  check_screen:\n    >function check_screen(window, contents, message)\n    >  local x, y = 1, 1\n    >  for i=1,contents:len() do\n    >    check_eq(window.scr[y][x].data, contents[i], message..'/'..y..','..x)\n    >    x = x+1\n    >    if x > window.scr.w then\n    >      y = y+1\n    >      x = 1\n    >    end\n    >  end\n    >end\n    >\n    >-- putting it all together, an example test of both keyboard and screen\n    >function test_check_screen()\n    >  local lines = {\n    >    c='123',\n    >    d='234',\n    >    a='345',\n    >    b='456',\n    >  }\n    >  local w = window{\n    >    kbd=kbd('abc'),\n    >    scr=scr{h=3, w=5},\n    >  }\n    >  local y = 1\n    >  while true do\n    >    local b = w:getch()\n    >    if b == nil then break end\n    >    w:mvaddstr(y, 1, lines[string.char(b)])\n    >    y = y+1\n    >  end\n    >  check_screen(w, '345  '..\n    >                  '456  '..\n    >                  '123  ',\n    >              'test_check_screen')\n    >end\n- __teliva_timestamp: original\n  check_reverse:\n    >function check_reverse(window, contents, message)\n    >  local x, y = 1, 1\n    >  for i=1,contents:len() do\n    >    if contents[i] ~= ' ' then\n    >      -- hacky version while we're without bitwise operators on Lua 5.1\n    >--      check(window.scr[y][x].attrs & curses.A_REVERSE, message..'/'..y..','..x)\n    >      check_eq(window.scr[y][x].attrs, curses.A_REVERSE, message..'/'..y..','..x)\n    >    else\n    >      -- hacky version while we're without bitwise operators on Lua 5.1\n    >--      check(window.scr[y][x].attrs & (~curses.A_REVERSE), message..'/'..y..','..x)\n    >      check(window.scr[y][x].attrs ~= curses.A_REVERSE, message..'/'..y..','..x)\n    >    end\n    >    x = x+1\n    >    if x > window.scr.w then\n    >      y = y+1\n    >      x = 1\n    >    end\n    >  end\n    >end\n- __teliva_timestamp: original\n  check_bold:\n    >function check_bold(window, contents, message)\n    >  local x, y = 1, 1\n    >  for i=1,contents:len() do\n    >    if contents[i] ~= ' ' then\n    >      -- hacky version while we're without bitwise operators on Lua 5.1\n    >--      check(window.scr[y][x].attrs & curses.A_BOLD, message..'/'..y..','..x)\n    >      check_eq(window.scr[y][x].attrs, curses.A_BOLD, message..'/'..y..','..x)\n    >    else\n    >      -- hacky version while we're without bitwise operators on Lua 5.1\n    >--      check(window.scr[y][x].attrs & (~curses.A_BOLD), message..'/'..y..','..x)\n    >      check(window.scr[y][x].attrs ~= curses.A_BOLD, message..'/'..y..','..x)\n    >    end\n    >    x = x+1\n    >    if x > window.scr.w then\n    >      y = y+1\n    >      x = 1\n    >    end\n    >  end\n    >end\n- __teliva_timestamp: original\n  check_color:\n    >-- check which parts of a screen have the given color_pair\n    >function check_color(window, cp, contents, message)\n    >  local x, y = 1, 1\n    >  for i=1,contents:len() do\n    >    if contents[i] ~= ' ' then\n    >      -- hacky version while we're without bitwise operators on Lua 5.1\n    >--      check(window.scr[y][x].attrs & curses.color_pair(cp), message..'/'..y..','..x)\n    >      check_eq(window.scr[y][x].attrs, curses.color_pair(cp), message..'/'..y..','..x)\n    >    else\n    >      -- hacky version while we're without bitwise operators on Lua 5.1\n    >--      check(window.scr[y][x].attrs & (~curses.A_BOLD), message..'/'..y..','..x)\n    >      check(window.scr[y][x].attrs ~= curses.color_pair(cp), message..'/'..y..','..x)\n    >    end\n    >    x = x+1\n    >    if x > window.scr.w then\n    >      y = y+1\n    >      x = 1\n    >    end\n    >  end\n    >end\n- __teliva_timestamp: original\n  sep:\n    >-- horizontal separator\n    >function sep(window)\n    >  local y, _ = window:getyx()\n    >  window:mvaddstr(y+1, 0, '')\n    >  local _, cols = window:getmaxyx()\n    >  for col=1,cols do\n    >    window:addstr('_')\n    >  end\n    >end\n- __teliva_timestamp: original\n  render:\n    >function render(window)\n    >  window:clear()\n    >  -- draw stuff to screen here\n    >  window:attron(curses.A_BOLD)\n    >  window:mvaddstr(1, 5, \"example app\")\n    >  window:attrset(curses.A_NORMAL)\n    >  for i=0,15 do\n    >    window:attrset(curses.color_pair(i))\n    >    window:mvaddstr(3+i, 5, \"========================\")\n    >  end\n    >  window:refresh()\n    >end\n- __teliva_timestamp: original\n  update:\n    >function update(window)\n    >  local key = window:getch()\n    >  -- process key here\n    >end\n- __teliva_timestamp: original\n  init_colors:\n    >function init_colors()\n    >  for i=0,7 do\n    >    curses.init_pair(i, i, -1)\n    >  end\n    >  curses.init_pair(8, 7, 0)\n    >  curses.init_pair(9, 7, 1)\n    >  curses.init_pair(10, 7, 2)\n    >  curses.init_pair(11, 7, 3)\n    >  curses.init_pair(12, 7, 4)\n    >  curses.init_pair(13, 7, 5)\n    >  curses.init_pair(14, 7, 6)\n    >  curses.init_pair(15, -1, 15)\n    >end\n- __teliva_timestamp: original\n  main:\n    >function main()\n    >  init_colors()\n    >\n    >  while true do\n    >    render(Window)\n    >    update(Window)\n    >  end\n    >end\n- __teliva_timestamp: original\n  doc:blurb:\n    >To show a brief description of the app on the 'big picture' screen, put the text in a special buffer called 'doc:blurb'.\n    >\n    >You can also override the default big picture screen entirely by creating a buffer called 'doc:main'.\n- __teliva_timestamp:\n    >Mon Apr 11 21:47:50 2022\n  main:\n    >function main()\n    >  init_colors()\n    >  read_feeds()\n    >\n    >  while true do\n    >    render(Window)\n    >    update(Window)\n    >  end\n    >end\n- __teliva_timestamp:\n    >Mon Apr 11 21:49:18 2022\n  read_feeds:\n    >function read_feeds()\n    >  local f = start_reading('feeds')\n    >  while true\n    >    local line = f.read()\n    >    if line == nil then break end\n    >  end\n    >end\n- __teliva_timestamp:\n    >Mon Apr 11 21:52:26 2022\n  read_feeds:\n    >function read_feeds()\n    >  local f = start_reading('feeds')\n    >  Feeds = {}\n    >  while true\n    >    local feed = f.read()\n    >    if feed == nil then break end\n    >    table.insert(Feeds, feed)\n    >  end\n    >end\n- __teliva_timestamp:\n    >Mon Apr 11 21:52:33 2022\n  Feeds:\n    >Feeds = {}\n- __teliva_timestamp:\n    >Mon Apr 11 21:52:59 2022\n  render:\n    >function render(window)\n    >  window:clear()\n    >  -- draw stuff to screen here\n    >  for i, feed in ipairs(Feeds) do\n    >    print(feed)\n    >  end\n    >  window:refresh()\n    >end\n- __teliva_timestamp:\n    >Mon Apr 11 21:53:11 2022\n  read_feeds:\n    >function read_feeds()\n    >  local f = start_reading('feeds')\n    >  Feeds = {}\n    >  while true do\n    >    local feed = f.read()\n    >    if feed == nil then break end\n    >    table.insert(Feeds, feed)\n    >  end\n    >end\n- __teliva_timestamp:\n    >Mon Apr 11 21:53:22 2022\n  read_feeds:\n    >function read_feeds()\n    >  local f = start_reading(nil, 'feeds')\n    >  Feeds = {}\n    >  while true do\n    >    local feed = f.read()\n    >    if feed == nil then break end\n    >    table.insert(Feeds, feed)\n    >  end\n    >end\n- __teliva_timestamp:\n    >Mon Apr 11 21:53:39 2022\n  read_feeds:\n    >function read_feeds()\n    >  local f = start_reading(nil, 'feeds')\n    >  if f == nil then return end\n    >  Feeds = {}\n    >  while true do\n    >    local feed = f.read()\n    >    if feed == nil then break end\n    >    table.insert(Feeds, feed)\n    >  end\n    >end\n- __teliva_timestamp:\n    >Fri Apr 22 22:39:15 2022\n  menu:\n    >-- To show app-specific hotkeys in the menu bar, add hotkey/command\n    >-- arrays of strings to the menu array.\n    >menu = {\n    >  {'^r', 'refresh all feeds'},\n    >}\n- __teliva_timestamp:\n    >Fri Apr 22 22:39:45 2022\n  update:\n    >function update(window)\n    >  local key = window:getch()\n    >  if key == 18 then  -- ctrl-r\n    >    print('aaa')\n    >    window:getch()\n    >  end\n    >end\n- __teliva_timestamp:\n    >Fri Apr 22 22:40:02 2022\n  update:\n    >function update(window)\n    >  local key = window:getch()\n    >  if key == 18 then  -- ctrl-r\n    >    download_feeds()\n    >  end\n    >end\n- __teliva_timestamp:\n    >Fri Apr 22 22:40:27 2022\n  update:\n    >function update(window)\n    >  local key = window:getch()\n    >  if key == 18 then  -- ctrl-r\n    >    reload_feeds()\n    >  end\n    >end\n- __teliva_timestamp:\n    >Fri Apr 22 22:40:37 2022\n  menu:\n    >-- To show app-specific hotkeys in the menu bar, add hotkey/command\n    >-- arrays of strings to the menu array.\n    >menu = {\n    >  {'^r', 'reload feeds'},\n    >}\n- __teliva_timestamp:\n    >Fri Apr 22 22:41:00 2022\n  reload_feeds:\n    >function reload_feeds()\n    >  print('aaa')\n    >  Window:getch()\n    >end\n- __teliva_timestamp:\n    >Fri Apr 22 22:41:37 2022\n  reload_feeds:\n    >function reload_feeds()\n    >  for _, feed in ipairs(Feeds) do\n    >    print(feed)\n    >  end\n    >  Window:getch()\n    >end\n- __teliva_timestamp:\n    >Fri Apr 22 22:43:36 2022\n  reload_feeds:\n    >function reload_feeds()\n    >  for _, feed in ipairs(Feeds) do\n    >    local response, status, headers = http.request(feed)\n    >    Window:clear()\n    >    print(response)\n    >    Window:getch()\n    >  end\n    >end\n- __teliva_timestamp:\n    >Fri Apr 22 22:55:25 2022\n  Items:\n    >Items = {}\n- __teliva_timestamp:\n    >Fri Apr 22 22:57:54 2022\n  reload_feeds:\n    >function reload_feeds()\n    >  for _, feed in ipairs(Feeds) do\n    >    local response, status, headers = http.request(feed)\n    >    print(type(status))\n    >--?     parse_items(response)\n    >    Window:clear()\n    >    print(response)\n    >--?     print(feed, #Items)\n    >    Window:getch()\n    >  end\n    >end\n- __teliva_timestamp:\n    >Fri Apr 22 22:58:15 2022\n  reload_feeds:\n    >function reload_feeds()\n    >  for _, feed in ipairs(Feeds) do\n    >    local response, status, headers = http.request(feed)\n    >    Window:clear()\n    >    print(type(status))\n    >--?     parse_items(response)\n    >    print(response)\n    >--?     print(feed, #Items)\n    >    Window:getch()\n    >  end\n    >end\n- __teliva_timestamp:\n    >Fri Apr 22 22:59:10 2022\n  reload_feeds:\n    >function reload_feeds()\n    >  for _, feed in ipairs(Feeds) do\n    >    local response, status, headers = http.request(feed)\n    >    if response == 200 then\n    >      parse_items(response)\n    >      Window:clear()\n    >      print(response)\n    >--?       print(feed, #Items)\n    >      Window:getch()\n    >    end\n    >  end\n    >end\n- __teliva_timestamp:\n    >Fri Apr 22 22:59:38 2022\n  reload_feeds:\n    >function reload_feeds()\n    >  for _, feed in ipairs(Feeds) do\n    >    local response, status, headers = http.request(feed)\n    >    print(status)\n    >    if status == 200 then\n    >      parse_items(response)\n    >      Window:clear()\n    >      print(response)\n    >--?       print(feed, #Items)\n    >      Window:getch()\n    >    end\n    >  end\n    >end\n- __teliva_timestamp:\n    >Fri Apr 22 23:01:46 2022\n  parse_items:\n    >function parse_items(lines)\n    >  for line in lines:gmatch('[^\\n]*') do\n    >    print('^', line, '$')\n    >  end\n    >end\n- __teliva_timestamp:\n    >Fri Apr 22 23:02:26 2022\n  reload_feeds:\n    >function reload_feeds()\n    >  for _, feed in ipairs(Feeds) do\n    >    local response, status, headers = http.request(feed)\n    >    print(status)\n    >    if status == 200 then\n    >      Window:clear()\n    >      parse_items(response)\n    >      print(response)\n    >--?       print(feed, #Items)\n    >      Window:getch()\n    >    end\n    >  end\n    >end\n- __teliva_timestamp:\n    >Fri Apr 22 23:02:53 2022\n  parse_items:\n    >function parse_items(lines)\n    >  for line in lines:gmatch('[^\\n]+') do\n    >    print('^', line, '$')\n    >  end\n    >end\n- __teliva_timestamp:\n    >Fri Apr 22 23:06:14 2022\n  parse_items:\n    >function parse_items(lines)\n    >  for line in lines:gmatch('[^\\n]+') do\n    >    local t, text = line:gmatch('([^%s]+)\\t(.*)')\n    >    print('^', t, '--', text, '$')\n    >  end\n    >end\n- __teliva_timestamp:\n    >Fri Apr 22 23:06:29 2022\n  parse_items:\n    >function parse_items(lines)\n    >  for line in lines:gmatch('[^\\n]+') do\n    >    local t, text = line:match('([^%s]+)\\t(.*)')\n    >    print('^', t, '--', text, '$')\n    >  end\n    >end\n- __teliva_timestamp:\n    >Fri Apr 22 23:07:06 2022\n  reload_feeds:\n    >function reload_feeds()\n    >  for _, feed in ipairs(Feeds) do\n    >    local response, status, headers = http.request(feed)\n    >    print(status)\n    >    if status == 200 then\n    >      Window:clear()\n    >      parse_items(feed, response)\n    >      print(response)\n    >--?       print(feed, #Items)\n    >      Window:getch()\n    >    end\n    >  end\n    >end\n- __teliva_timestamp:\n    >Fri Apr 22 23:10:44 2022\n  parse_items:\n    >function parse_items(feed, lines)\n    >  if Items[feed] == nil then Items[feed] = {} end\n    >  for line in lines:gmatch('[^\\n]+') do\n    >    local t, text = line:match('([^%s]+)\\t(.*)')\n    >    table.insert(Items[feed], {time=parse_time(t), text=text})\n    >  end\n    >  table.sort(Items[feed], function(a, b) return a.time < b.time end)\n    >end\n- __teliva_timestamp:\n    >Fri Apr 22 23:11:24 2022\n  reload_feeds:\n    >function reload_feeds()\n    >  for _, feed in ipairs(Feeds) do\n    >    local response, status, headers = http.request(feed)\n    >    print(status)\n    >    if status == 200 then\n    >      Window:clear()\n    >      parse_items(feed, response)\n    >      for _, x in ipairs(Items) do\n    >        print(x.time, x.text)\n    >--?       print(feed, #Items)\n    >      Window:getch()\n    >    end\n    >  end\n    >end\n- __teliva_timestamp:\n    >Fri Apr 22 23:11:36 2022\n  reload_feeds:\n    >function reload_feeds()\n    >  for _, feed in ipairs(Feeds) do\n    >    local response, status, headers = http.request(feed)\n    >    print(status)\n    >    if status == 200 then\n    >      Window:clear()\n    >      parse_items(feed, response)\n    >      for _, x in ipairs(Items) do\n    >        print(x.time, x.text)\n    >      end\n    >--?       print(feed, #Items)\n    >      Window:getch()\n    >    end\n    >  end\n    >end\n- __teliva_timestamp:\n    >Fri Apr 22 23:12:05 2022\n  parse_time:\n    >function parse_time(t)\n    >  return t\n    >end\n- __teliva_timestamp:\n    >Fri Apr 22 23:13:02 2022\n  reload_feeds:\n    >function reload_feeds()\n    >  for _, feed in ipairs(Feeds) do\n    >    local response, status, headers = http.request(feed)\n    >    print(status)\n    >    if status == 200 then\n    >      Window:clear()\n    >      parse_items(feed, response)\n    >      for _, x in ipairs(Items[feed]) do\n    >        print(x.time, x.text)\n    >      end\n    >--?       print(feed, #Items)\n    >      Window:getch()\n    >    end\n    >  end\n    >end\n- __teliva_timestamp:\n    >Fri Apr 22 23:13:50 2022\n  parse_items:\n    >function parse_items(feed, lines)\n    >  if Items[feed] == nil then Items[feed] = {} end\n    >  for line in lines:gmatch('[^\\n]+') do\n    >    local t, text = line:match('([^%s]+)\\t(.*)')\n    >    print(t, text)\n    >    table.insert(Items[feed], {time=parse_time(t), text=text})\n    >  end\n    >  table.sort(Items[feed], function(a, b) return a.time < b.time end)\n    >end\n- __teliva_timestamp:\n    >Fri Apr 22 23:14:36 2022\n  parse_items:\n    >function parse_items(feed, lines)\n    >  if Items[feed] == nil then Items[feed] = {} end\n    >  for line in lines:gmatch('[^\\n]+') do\n    >    print(line)\n    >    local t, text = line:match('([^%s]+)\\t(.*)')\n    >    print(t, text)\n    >    table.insert(Items[feed], {time=parse_time(t), text=text})\n    >  end\n    >  table.sort(Items[feed], function(a, b) return a.time < b.time end)\n    >end\n- __teliva_timestamp:\n    >Fri Apr 22 23:19:20 2022\n  parse_items:\n    >function parse_items(feed, lines)\n    >  if Items[feed] == nil then Items[feed] = {} end\n    >  for line in lines:gmatch('[^\\n]+') do\n    >    print(line)\n    >    local t, status = line:match('([^%s]+)\\t(.*)')\n    >    if t and status then\n    >      print(t, text)\n    >      table.insert(Items[feed], {time=parse_time(t), text=text})\n    >    end\n    >  end\n    >  table.sort(Items[feed], function(a, b) return a.time < b.time end)\n    >end\n- __teliva_timestamp:\n    >Fri Apr 22 23:19:44 2022\n  parse_items:\n    >function parse_items(feed, lines)\n    >  if Items[feed] == nil then Items[feed] = {} end\n    >  for line in lines:gmatch('[^\\n]+') do\n    >    print(line)\n    >    local t, status = line:match('([^%s]+)\\t(.*)')\n    >    if t and status then\n    >      print(t, status)\n    >      table.insert(Items[feed], {time=parse_time(t), status=status})\n    >    end\n    >  end\n    >  table.sort(Items[feed], function(a, b) return a.time < b.time end)\n    >end\n- __teliva_timestamp:\n    >Fri Apr 22 23:19:56 2022\n  reload_feeds:\n    >function reload_feeds()\n    >  for _, feed in ipairs(Feeds) do\n    >    local response, status, headers = http.request(feed)\n    >    print(status)\n    >    if status == 200 then\n    >      Window:clear()\n    >      parse_items(feed, response)\n    >      for _, x in ipairs(Items[feed]) do\n    >        print(x.time, x.status)\n    >      end\n    >--?       print(feed, #Items)\n    >      Window:getch()\n    >    end\n    >  end\n    >end\n- __teliva_timestamp:\n    >Fri Apr 22 23:21:40 2022\n  parse_items:\n    >function parse_items(feed, lines)\n    >  if Items[feed] == nil then Items[feed] = {} end\n    >  for line in lines:gmatch('[^\\n]+') do\n    >    local t, status = line:match('([^%s]+)\\t(.*)')\n    >    if t and status then\n    >      table.insert(Items[feed], {time=parse_time(t), status=status})\n    >    end\n    >  end\n    >  table.sort(Items[feed], function(a, b) return a.time < b.time end)\n    >end\n- __teliva_timestamp:\n    >Fri Apr 22 23:23:29 2022\n  reload_feeds:\n    >function reload_feeds()\n    >  for _, feed in ipairs(Feeds) do\n    >    local response, status, headers = http.request(feed)\n    >    print(status)\n    >    if status == 200 then\n    >      Window:clear()\n    >      parse_items(feed, response)\n    >      local all_items = {}\n    >      for feed, items in pairs(Items) do\n    >        for _, item in ipairs(items) do\n    >          table.insert(all_items, item)\n    >        end\n    >      end\n    >      table.sort(all_items, function(a, b) return a.time < b.time end)\n    >      for _, item in ipairs(all_items) do\n    >        print(x.time, x.status)\n    >      end\n    >--?       print(feed, #Items)\n    >      Window:getch()\n    >    end\n    >  end\n    >end\n- __teliva_timestamp:\n    >Fri Apr 22 23:23:48 2022\n  reload_feeds:\n    >function reload_feeds()\n    >  for _, feed in ipairs(Feeds) do\n    >    local response, status, headers = http.request(feed)\n    >    print(status)\n    >    if status == 200 then\n    >      Window:clear()\n    >      parse_items(feed, response)\n    >      local all_items = {}\n    >      for feed, items in pairs(Items) do\n    >        for _, item in ipairs(items) do\n    >          table.insert(all_items, item)\n    >        end\n    >      end\n    >      table.sort(all_items, function(a, b) return a.time < b.time end)\n    >      for _, x in ipairs(all_items) do\n    >        print(x.time, x.status)\n    >      end\n    >--?       print(feed, #Items)\n    >      Window:getch()\n    >    end\n    >  end\n    >end\n- __teliva_timestamp:\n    >Fri Apr 22 23:24:28 2022\n  parse_items:\n    >function parse_items(feed, lines)\n    >  if Items[feed] == nil then Items[feed] = {} end\n    >  for line in lines:gmatch('[^\\n]+') do\n    >    local t, status = line:match('([^%s]+)\\t(.*)')\n    >    if t and status then\n    >      table.insert(Items[feed], {feed=feed, time=parse_time(t), status=status})\n    >    end\n    >  end\n    >  table.sort(Items[feed], function(a, b) return a.time < b.time end)\n    >end\n- __teliva_timestamp:\n    >Fri Apr 22 23:25:34 2022\n  reload_feeds:\n    >function reload_feeds()\n    >  for _, feed in ipairs(Feeds) do\n    >    local response, status, headers = http.request(feed)\n    >    print(status)\n    >    if status == 200 then\n    >      Window:clear()\n    >      parse_items(feed, response)\n    >      local all_items = {}\n    >      for feed, items in pairs(Items) do\n    >        for _, item in ipairs(items) do\n    >          table.insert(all_items, item)\n    >        end\n    >      end\n    >      table.sort(all_items, function(a, b) return a.time < b.time end)\n    >      for _, x in ipairs(all_items) do\n    >        print(x.feed, x.time, x.status)\n    >      end\n    >--?       print(feed, #Items)\n    >      Window:getch()\n    >    end\n    >  end\n    >end\n- __teliva_timestamp:\n    >Fri Apr 22 23:27:08 2022\n  reload_feeds:\n    >function reload_feeds()\n    >  for _, feed in ipairs(Feeds) do\n    >    local response, status, headers = http.request(feed)\n    >    print(status)\n    >    if status == 200 then\n    >      Window:clear()\n    >      parse_items(feed, response)\n    >      local all_items = {}\n    >      for feed, items in pairs(Items) do\n    >        for _, item in ipairs(items) do\n    >          table.insert(all_items, item)\n    >        end\n    >      end\n    >      table.sort(all_items, function(a, b) return a.time < b.time end)\n    >      for _, x in ipairs(all_items) do\n    >        print(x.feed, x.time, x.status)\n    >      end\n    >      Window:getch()\n    >    end\n    >  end\n    >end\n- __teliva_timestamp:\n    >Fri Apr 22 23:29:49 2022\n  parse_items:\n    >function parse_items(feed, lines)\n    >  if Items[feed] == nil then Items[feed] = {} end\n    >  for line in lines:gmatch('[^\\n]+') do\n    >    local t, status = line:match('([^%s]+)\\t(.*)')\n    >    if t and status then\n    >      table.insert(Items[feed], {feed=feed, time=t, parsed_time=parse_time(t), status=status})\n    >    end\n    >  end\n    >  table.sort(Items[feed], compare_parsed_time)\n    >end\n- __teliva_timestamp:\n    >Fri Apr 22 23:31:16 2022\n  compare_parsed_time:\n    >function compare_parsed_time(a, b)\n    >  local a, b = a.parsed_time, b.parsed_time\n    >  if a.\n- __teliva_timestamp:\n    >Fri Apr 22 23:34:46 2022\n  parse_time:\n    >function parse_time(t)\n    >  local year, month, day, hour, min, sec, patt_end = str:match(\"^(%d%d%d%d)%-(%d%d)%-(%d%d)[Tt](%d%d%.?%d*):(%d%d):(%d%d)()\")\n    >  return { year=year, month=month, day=day, hour=hour, min=min, sec=sec }\n    >end\n- __teliva_timestamp:\n    >Fri Apr 22 23:36:13 2022\n  compare_parsed_time:\n    >function compare_parsed_time(a, b)\n    >  local a, b = a.parsed_time, b.parsed_time\n    >  if a.year ~= b.year then return a.year < b.year end\n    >  if a.month ~= b.month then return a.month < b.month end\n    >  if a.day ~= b.day then return a.day < b.day end\n    >  if a.hour ~= b.hour then return a.hour < b.hour end\n    >  if a.min ~= b.min then return a.min < b.min end\n    >  return a.sec < b.sec\n    >end\n- __teliva_timestamp:\n    >Fri Apr 22 23:36:35 2022\n  parse_time:\n    >function parse_time(t)\n    >  local year, month, day, hour, min, sec, patt_end = t:match(\"^(%d%d%d%d)%-(%d%d)%-(%d%d)[Tt](%d%d%.?%d*):(%d%d):(%d%d)()\")\n    >  return { year=year, month=month, day=day, hour=hour, min=min, sec=sec }\n    >end\n- __teliva_timestamp:\n    >Fri Apr 22 23:39:20 2022\n  parse_time:\n    >function parse_time(t)\n    >  local year, month, day, hour, min, sec, tz_hour, tz_min = t:match(\"^(%d%d%d%d)%-(%d%d)%-(%d%d)[Tt](%d%d%.?%d*):(%d%d):(%d%d)[+-](%d%d):(%d%d)\")\n    >  return { year=year, month=month, day=day, hour=hour, min=min, sec=sec, tz_hour=tz_hour, tz_min=tz_min }\n    >end\n- __teliva_timestamp:\n    >Fri Apr 22 23:47:55 2022\n  parse_time:\n    >function parse_time(t)\n    >  local year, month, day, hour, min, sec, tz_op, tz_hour, tz_min = t:match(\"^(%d%d%d%d)%-(%d%d)%-(%d%d)[Tt](%d%d%.?%d*):(%d%d):(%d%d)([+-])(%d%d):(%d%d)\")\n    >  if tz_op == '+' then\n    >    hour = int(hour) + int(tz_hour)\n    >    min = int(min) + int(tz_min)\n    >  else\n    >    hour = int(hour) - int(tz_hour)\n    >    min = int(min) - int(tz_min)\n    >  end\n    >  return os.time{ year=int(year), month=int(month), day=int(day), hour=int(hour), min=int(min), sec=int(sec) }\n    >end\n- __teliva_timestamp:\n    >Fri Apr 22 23:48:46 2022\n  compare_parsed_time:\n    >function compare_parsed_time(a, b)\n    >  return a.parsed_time < b.parsed_time\n    >end\n    >\n    >-- unused\n    >function compare_parsed_time2(a, b)\n    >  local a, b = a.parsed_time, b.parsed_time\n    >  if a.year ~= b.year then return a.year < b.year end\n    >  if a.month ~= b.month then return a.month < b.month end\n    >  if a.day ~= b.day then return a.day < b.day end\n    >  if a.hour ~= b.hour then return a.hour < b.hour end\n    >  if a.min ~= b.min then return a.min < b.min end\n    >  return a.sec < b.sec\n    >end\n- __teliva_timestamp:\n    >Fri Apr 22 23:49:13 2022\n  parse_time:\n    >function parse_time(t)\n    >  local year, month, day, hour, min, sec, tz_op, tz_hour, tz_min = t:match(\"^(%d%d%d%d)%-(%d%d)%-(%d%d)[Tt](%d%d%.?%d*):(%d%d):(%d%d)([+-])(%d%d):(%d%d)\")\n    >  local int = tonumber\n    >  if tz_op == '+' then\n    >    hour = int(hour) + int(tz_hour)\n    >    min = int(min) + int(tz_min)\n    >  else\n    >    hour = int(hour) - int(tz_hour)\n    >    min = int(min) - int(tz_min)\n    >  end\n    >  return os.time{ year=int(year), month=int(month), day=int(day), hour=int(hour), min=int(min), sec=int(sec) }\n    >end\n- __teliva_timestamp:\n    >Fri Apr 22 23:51:08 2022\n  parse_time:\n    >function parse_time(t)\n    >  local year, month, day, hour, min, sec, tz_op, tz_hour, tz_min = t:match(\"^(%d%d%d%d)%-(%d%d)%-(%d%d)[Tt](%d%d%.?%d*):(%d%d):(%d%d)([+-])(%d%d):(%d%d)\")\n    >  local int = tonumber\n    >  -- convert to UTC\n    >  if tz_op == '+' then\n    >    hour = int(hour) - int(tz_hour)\n    >    min = int(min) - int(tz_min)\n    >  else\n    >    hour = int(hour) + int(tz_hour)\n    >    min = int(min) + int(tz_min)\n    >  end\n    >  return os.time{ year=int(year), month=int(month), day=int(day), hour=hour, min=min, sec=int(sec) }\n    >end\n- __teliva_timestamp:\n    >Fri Apr 22 23:52:27 2022\n  __teliva_note:\n    >parsing twtxt feeds and sorting items after accounting for timezones\n  reload_feeds:\n    >function reload_feeds()\n    >  for _, feed in ipairs(Feeds) do\n    >    local response, status, headers = http.request(feed)\n    >    print(status)\n    >    if status == 200 then\n    >      Window:clear()\n    >      parse_items(feed, response)\n    >      local all_items = {}\n    >      for feed, items in pairs(Items) do\n    >        for _, item in ipairs(items) do\n    >          table.insert(all_items, item)\n    >        end\n    >      end\n    >      table.sort(all_items, function(a, b) return a.parsed_time < b.parsed_time end)\n    >      for _, x in ipairs(all_items) do\n    >        print(x.feed, x.time, x.status)\n    >      end\n    >      Window:getch()\n    >    end\n    >  end\n    >end\n- __teliva_timestamp:\n    >Fri Apr 22 23:57:10 2022\n  update:\n    >function update(window)\n    >  local key = window:getch()\n    >  if key == 18 then  -- ctrl-r\n    >    reload_feeds(window)\n    >  end\n    >end\n- __teliva_timestamp:\n    >Sat Apr 23 00:13:26 2022\n  render_feed_status:\n    >function render_feed_status(window, fs)\n    >  window:clear()\n    >  for _, fs in ipairs(feed_status) do\n    >    if fs.status then\n    >      if fs.status/100 == 2 then\n    >        window:attrset(curses.color_pair(10))\n    >        window:addstr(fs.feed..'\\n')\n    >        window:attrset(curses.A_NORMAL)\n    >      else\n    >        window:attrset(curses.color_pair(9))\n    >        window:addstr(fs.feed..'\\n')\n    >        window:attrset(curses.A_NORMAL)\n    >      end\n    >    else\n    >      window:addstr(fs.feed..'\\n')\n    >    end\n    >  end\n    >  window:refresh()\n    >end\n- __teliva_timestamp:\n    >Sat Apr 23 00:18:59 2022\n  render:\n    >function render(window)\n    >  window:clear()\n    >  -- draw feeds\n    >  for i, feed in ipairs(Feeds) do\n    >    print(feed)\n    >  end\n    >  -- draw items\n    >  local all_items = {}\n    >  local n = 0\n    >  for feed, items in pairs(Items) do\n    >    n = n+1\n    >    for _, item in ipairs(items) do\n    >      table.insert(all_items, item)\n    >    end\n    >  end\n    >  table.sort(all_items, function(a, b) return a.parsed_time < b.parsed_time end)\n    >  for _, x in ipairs(all_items) do\n    >    print(x.feed, x.time, x.status)\n    >  end\n    >  window:refresh()\n    >end\n- __teliva_timestamp:\n    >Sat Apr 23 00:20:31 2022\n  __teliva_note:\n    >show progress when downloading feeds\n  reload_feeds:\n    >function reload_feeds(window)\n    >  window:nodelay(true)\n    >  feed_status = {}\n    >  for _, feed in ipairs(Feeds) do\n    >    table.insert(feed_status, {feed=feed})\n    >  end\n    >  for _, fs in ipairs(feed_status) do\n    >    local response, status, headers = http.request(fs.feed)\n    >    fs.status = status\n    >    parse_items(fs.feed, response)\n    >    render_feed_status(window, fs)\n    >    if window:getch() then break end\n    >  end\n    >  window:nodelay(false)\n    >end\n- __teliva_timestamp:\n    >Sat Apr 23 00:28:09 2022\n  render:\n    >function render(window)\n    >  window:clear()\n    >  -- draw feeds\n    >  print(str(#Feeds)..' feeds')\n    >  -- draw items\n    >  local all_items = {}\n    >  local n = 0\n    >  for feed, items in pairs(Items) do\n    >    n = n+1\n    >    for _, item in ipairs(items) do\n    >      table.insert(all_items, item)\n    >    end\n    >  end\n    >  table.sort(all_items, function(a, b) return a.parsed_time < b.parsed_time end)\n    >  for _, x in ipairs(all_items) do\n    >    print(x.feed, x.time, x.status)\n    >  end\n    >  window:refresh()\n    >end\n- __teliva_timestamp:\n    >Sat Apr 23 00:29:10 2022\n  reload_feeds:\n    >function reload_feeds(window)\n    >  window:nodelay(true)\n    >  feed_status = {}\n    >  for _, feed in ipairs(Feeds) do\n    >    table.insert(feed_status, {feed=feed})\n    >  end\n    >  render_feed_status(\n    >  for _, fs in ipairs(feed_status) do\n    >    local response, status, headers = http.request(fs.feed)\n    >    fs.status = status\n    >    parse_items(fs.feed, response)\n    >    render_feed_status(window, fs)\n    >    if window:getch() then break end\n    >  end\n    >  window:nodelay(false)\n    >end\n- __teliva_timestamp:\n    >Sat Apr 23 00:29:23 2022\n  render_feed_status:\n    >function render_feed_status(window, feed_status)\n    >  window:clear()\n    >  for _, fs in ipairs(feed_status) do\n    >    if fs.status then\n    >      if fs.status/100 == 2 then\n    >        window:attrset(curses.color_pair(10))\n    >        window:addstr(fs.feed..'\\n')\n    >        window:attrset(curses.A_NORMAL)\n    >      else\n    >        window:attrset(curses.color_pair(9))\n    >        window:addstr(fs.feed..'\\n')\n    >        window:attrset(curses.A_NORMAL)\n    >      end\n    >    else\n    >      window:addstr(fs.feed..'\\n')\n    >    end\n    >  end\n    >  window:refresh()\n    >end\n- __teliva_timestamp:\n    >Sat Apr 23 00:29:48 2022\n  reload_feeds:\n    >function reload_feeds(window)\n    >  window:nodelay(true)\n    >  local feed_status = {}\n    >  for _, feed in ipairs(Feeds) do\n    >    table.insert(feed_status, {feed=feed})\n    >  end\n    >  render_feed_status(feed_status)\n    >  for _, fs in ipairs(feed_status) do\n    >    local response, status, headers = http.request(fs.feed)\n    >    fs.status = status\n    >    parse_items(fs.feed, response)\n    >    render_feed_status(window, feed_status)\n    >    if window:getch() then break end\n    >  end\n    >  window:nodelay(false)\n    >end\n- __teliva_timestamp:\n    >Sat Apr 23 00:30:18 2022\n  __teliva_note:\n    >fix an accidental global variable\n  reload_feeds:\n    >function reload_feeds(window)\n    >  window:nodelay(true)\n    >  local feed_status = {}\n    >  for _, feed in ipairs(Feeds) do\n    >    table.insert(feed_status, {feed=feed})\n    >  end\n    >  render_feed_status(window, feed_status)\n    >  for _, fs in ipairs(feed_status) do\n    >    local response, status, headers = http.request(fs.feed)\n    >    fs.status = status\n    >    parse_items(fs.feed, response)\n    >    render_feed_status(window, feed_status)\n    >    if window:getch() then break end\n    >  end\n    >  window:nodelay(false)\n    >end\n- __teliva_timestamp:\n    >Sat Apr 23 00:45:54 2022\n  __teliva_note:\n    >ignore non-compliant \"comments\" containing tabs\n    >https://merveilles.town/@akkartik/108179923032818671\n  parse_items:\n    >function parse_items(feed, lines)\n    >  if Items[feed] == nil then Items[feed] = {} end\n    >  for line in lines:gmatch('[^\\n]+') do\n    >    local t, status = line:match('([0-9][^%s]+)\\t(.*)')\n    >    if t and status then\n    >      table.insert(Items[feed], {feed=feed, time=t, parsed_time=parse_time(t), status=status})\n    >    end\n    >  end\n    >  table.sort(Items[feed], compare_parsed_time)\n    >end\n- __teliva_timestamp:\n    >Sat Apr 23 00:52:39 2022\n  render_feed_status:\n    >function render_feed_status(window, feed_status)\n    >  window:clear()\n    >  for _, fs in ipairs(feed_status) do\n    >    if fs.status then\n    >      if fs.status == 2 then\n    >        window:attrset(curses.color_pair(10))\n    >        window:addstr(fs.feed..'\\n')\n    >        window:attrset(curses.A_NORMAL)\n    >      else\n    >        window:attrset(curses.color_pair(9))\n    >        window:addstr(fs.feed..'\\n')\n    >        window:attrset(curses.A_NORMAL)\n    >      end\n    >    else\n    >      window:addstr(fs.feed..'\\n')\n    >    end\n    >  end\n    >  window:refresh()\n    >end\n- __teliva_timestamp:\n    >Sat Apr 23 00:56:34 2022\n  __teliva_note:\n    >handle errors in requests\n  reload_feeds:\n    >function reload_feeds(window)\n    >  window:nodelay(true)\n    >  local feed_status = {}\n    >  for _, feed in ipairs(Feeds) do\n    >    table.insert(feed_status, {feed=feed})\n    >  end\n    >  render_feed_status(window, feed_status)\n    >  for _, fs in ipairs(feed_status) do\n    >    local response, status, headers = http.request(fs.feed)\n    >    if response == nil then\n    >      fs.status = 4  -- treat any network issue like a 404\n    >    else\n    >      fs.status = status/100\n    >      if fs.status == 2 then\n    >        parse_items(fs.feed, response)\n    >      end\n    >      render_feed_status(window, feed_status)\n    >    end\n    >    if window:getch() then break end\n    >  end\n    >  window:nodelay(false)\n    >end\n- __teliva_timestamp:\n    >Sat Apr 23 01:06:55 2022\n  __teliva_note:\n    >handle more time formats\n  parse_time:\n    >function parse_time(t)\n    >  local int = tonumber\n    >  -- hackily strip out fractions of seconds\n    >  local year, month, day, hour, min, sec, tz_op, tz_hour, tz_min = t:match(\"^(%d%d%d%d)%-(%d%d)%-(%d%d)[Tt](%d%d%.?%d*):(%d%d):(%d%d)%.?[0-9]*([+-])(%d%d):(%d%d)\")\n    >  if year == nil then\n    >    year, month, day, hour, min, sec = t:match(\"^(%d%d%d%d)%-(%d%d)%-(%d%d)[Tt](%d%d%.?%d*):(%d%d):(%d%d)%.?[0-9]*[Zz]\")\n    >  end\n    >  if year == nil then\n    >    return 0\n    >  end\n    >  -- convert to UTC\n    >  if tz_op == '+' then\n    >    hour = int(hour) - int(tz_hour)\n    >    min = int(min) - int(tz_min)\n    >  elseif tz_op == '-' then\n    >    hour = int(hour) + int(tz_hour)\n    >    min = int(min) + int(tz_min)\n    >  end\n    >  return os.time{ year=int(year), month=int(month), day=int(day), hour=hour, min=min, sec=int(sec) }\n    >end\n    >\n    >function test_parse_time()\n    >  parse_time('2019-07-31T14:19+01:00')  -- invalid per https://www.ietf.org/rfc/rfc3339.txt, but seen in the while; just ignore\n    >  parse_time('2019-09-16T01:13:07-07:00')\n    >  parse_time('2019-09-16T01:13:07Z')\n    >end\n"
  },
  {
    "path": "src/Makefile",
    "content": "# makefile for building Lua\n# see ../INSTALL for installation instructions\n# see ../Makefile and luaconf.h for further customization\n\n# == CHANGE THE SETTINGS BELOW TO SUIT YOUR ENVIRONMENT =======================\n\n# Your platform. See PLATS for possible values.\nPLAT= none\n\nCC= gcc\nCFLAGS= -g -O2 -std=c99 -Wall $(MYCFLAGS) -D_DEFAULT_SOURCE\nAR= ar rc\nRANLIB= ranlib\nRM= rm -f\nLIBS= -lm $(MYLIBS) -lssl -lcrypto\n\nMYCFLAGS=\nMYLDFLAGS=\nMYLIBS=\n\n# == END OF USER SETTINGS. NO NEED TO CHANGE ANYTHING BELOW THIS LINE =========\n\n# Keep this sync'd with src/Makefile\nPLATS= freebsd linux macosx netbsd openbsd\n\nLUA_A=\tliblua.a\nCORE_O=\tlapi.o lcode.o ldebug.o ldo.o ldump.o lfunc.o lgc.o llex.o lmem.o \\\n\tlobject.o lopcodes.o lparser.o lstate.o lstring.o ltable.o ltm.o  \\\n\tlundump.o lvm.o lzio.o \\\n\trealpath.o kilo.o tlv.o teliva.o\nLIB_O=\tlauxlib.o lbaselib.o liolib.o lmathlib.o loslib.o \\\n\tltablib.o lstrlib.o linit.o\n\nLUA_T=\tteliva\nLUA_O=\tlua.o\n\nALL_O= $(CORE_O) $(LIB_O) $(LUA_O)\nALL_T= $(LUA_A) $(LUA_T)\nALL_A= $(LUA_A)\n\ndefault: $(PLAT)\n\nall:\t$(ALL_T)\n\no:\t$(ALL_O)\n\na:\t$(ALL_A)\n\n$(LUA_A): $(CORE_O) $(LIB_O)\n\t$(AR) $@ $(CORE_O) $(LIB_O)\n\t$(RANLIB) $@\n\n$(LUA_T): $(LUA_O) $(LUA_A) lcurses/curses.o luasocket/socket.a luasocket/mime.a luasec/ssl.a\n\t$(CC) -o $@ $(MYLDFLAGS) $(LUA_O) $(LUA_A) lcurses/curses.o luasocket/socket.a luasocket/mime.a luasec/ssl.a $(LIBS)\n\nclean:\n\t$(RM) lcurses/curses.o\n\t$(MAKE) -C luasocket clean\n\t$(MAKE) -C luasec clean\n\t$(RM) $(ALL_T) $(ALL_O)\n\ndepend:\n\t@$(CC) $(CFLAGS) -MM l*.c print.c\n\necho:\n\t@echo \"PLAT = $(PLAT)\"\n\t@echo \"CC = $(CC)\"\n\t@echo \"CFLAGS = $(CFLAGS)\"\n\t@echo \"AR = $(AR)\"\n\t@echo \"RANLIB = $(RANLIB)\"\n\t@echo \"RM = $(RM)\"\n\t@echo \"MYCFLAGS = $(MYCFLAGS)\"\n\t@echo \"MYLDFLAGS = $(MYLDFLAGS)\"\n\t@echo \"MYLIBS = $(MYLIBS)\"\n\n# convenience targets for popular platforms\n\nnone:\n\t@echo \"Please choose a platform:\"\n\t@echo \"   $(PLATS)\"\n\nfreebsd:\n\t$(MAKE) -C luasocket bsd\n\t$(MAKE) -C luasec bsd\n\t$(MAKE) all MYLIBS=\"-Wl,-E -lncursesw\"\n\nlinux:\n\t$(MAKE) -C lcurses CC=\"$(CC)\" CFLAGS=\"$(CFLAGS)\"\n\t$(MAKE) -C luasocket linux\n\t$(MAKE) -C luasec linux\n\t$(MAKE) all MYCFLAGS=-Wpedantic MYLIBS=\"-Wl,-E -lncursesw\"\n\nmacosx:\n\t$(MAKE) -C lcurses CC=\"$(CC)\" CFLAGS=\"$(CFLAGS)\"\n\t$(MAKE) -C luasocket macosx\n\t$(MAKE) -C luasec macosx\n\t$(MAKE) all MYCFLAGS=-Wpedantic MYLIBS=\"-L/usr/local/opt/openssl@3/lib -lncurses\"\n\nopenbsd:\n\t$(MAKE) -C lcurses CC=\"$(CC)\" CFLAGS=\"$(CFLAGS)\"\n\t$(MAKE) -C luasocket bsd\n\t$(MAKE) -C luasec bsd\n\t$(MAKE) all MYLIBS=\"-Wl,-E -lncurses\"\n\nnetbsd:\n\t$(MAKE) -C lcurses CC=\"$(CC)\" CFLAGS=\"$(CFLAGS)\"\n\t$(MAKE) -C luasocket bsd\n\t$(MAKE) -C luasec bsd\n\t$(MAKE) all MYLIBS=\"-Wl,-E -lcurses\"\n\n# list targets that do not create files (but not all makes understand .PHONY)\n.PHONY: all $(PLATS) default o a clean depend echo none\n\n# DO NOT DELETE\n\nlapi.o: lapi.c lua.h luaconf.h lapi.h lobject.h llimits.h ldebug.h \\\n  lstate.h ltm.h lzio.h lmem.h ldo.h lfunc.h lgc.h lstring.h ltable.h \\\n  lundump.h lvm.h\nlauxlib.o: lauxlib.c lua.h luaconf.h lauxlib.h\nlbaselib.o: lbaselib.c lua.h luaconf.h lauxlib.h lualib.h\nlcode.o: lcode.c lua.h luaconf.h lcode.h llex.h lobject.h llimits.h \\\n  lzio.h lmem.h lopcodes.h lparser.h ldebug.h lstate.h ltm.h ldo.h lgc.h \\\n  ltable.h\nldebug.o: ldebug.c lua.h luaconf.h lapi.h lobject.h llimits.h lcode.h \\\n  llex.h lzio.h lmem.h lopcodes.h lparser.h ldebug.h lstate.h ltm.h ldo.h \\\n  lfunc.h lstring.h lgc.h ltable.h lvm.h\nldo.o: ldo.c lua.h luaconf.h ldebug.h lstate.h lobject.h llimits.h ltm.h \\\n  lzio.h lmem.h ldo.h lfunc.h lgc.h lopcodes.h lparser.h lstring.h \\\n  ltable.h lundump.h lvm.h\nldump.o: ldump.c lua.h luaconf.h lobject.h llimits.h lstate.h ltm.h \\\n  lzio.h lmem.h lundump.h\nlfunc.o: lfunc.c lua.h luaconf.h lfunc.h lobject.h llimits.h lgc.h lmem.h \\\n  lstate.h ltm.h lzio.h\nlgc.o: lgc.c lua.h luaconf.h ldebug.h lstate.h lobject.h llimits.h ltm.h \\\n  lzio.h lmem.h ldo.h lfunc.h lgc.h lstring.h ltable.h\nlinit.o: linit.c lua.h luaconf.h lualib.h lauxlib.h\nliolib.o: liolib.c lua.h luaconf.h lauxlib.h lualib.h\nllex.o: llex.c lua.h luaconf.h ldo.h lobject.h llimits.h lstate.h ltm.h \\\n  lzio.h lmem.h llex.h lparser.h lstring.h lgc.h ltable.h\nlmathlib.o: lmathlib.c lua.h luaconf.h lauxlib.h lualib.h\nlmem.o: lmem.c lua.h luaconf.h ldebug.h lstate.h lobject.h llimits.h \\\n  ltm.h lzio.h lmem.h ldo.h\nlobject.o: lobject.c lua.h luaconf.h ldo.h lobject.h llimits.h lstate.h \\\n  ltm.h lzio.h lmem.h lstring.h lgc.h lvm.h\nlopcodes.o: lopcodes.c lopcodes.h llimits.h lua.h luaconf.h\nloslib.o: loslib.c lua.h luaconf.h lauxlib.h lualib.h\nlparser.o: lparser.c lua.h luaconf.h lcode.h llex.h lobject.h llimits.h \\\n  lzio.h lmem.h lopcodes.h lparser.h ldebug.h lstate.h ltm.h ldo.h \\\n  lfunc.h lstring.h lgc.h ltable.h\nlstate.o: lstate.c lua.h luaconf.h ldebug.h lstate.h lobject.h llimits.h \\\n  ltm.h lzio.h lmem.h ldo.h lfunc.h lgc.h llex.h lstring.h ltable.h\nlstring.o: lstring.c lua.h luaconf.h lmem.h llimits.h lobject.h lstate.h \\\n  ltm.h lzio.h lstring.h lgc.h\nlstrlib.o: lstrlib.c lua.h luaconf.h lauxlib.h lualib.h\nltable.o: ltable.c lua.h luaconf.h ldebug.h lstate.h lobject.h llimits.h \\\n  ltm.h lzio.h lmem.h ldo.h lgc.h ltable.h\nltablib.o: ltablib.c lua.h luaconf.h lauxlib.h lualib.h\nltm.o: ltm.c lua.h luaconf.h lobject.h llimits.h lstate.h ltm.h lzio.h \\\n  lmem.h lstring.h lgc.h ltable.h\nlua.o: lua.c lua.h luaconf.h lauxlib.h lualib.h teliva.h\nlundump.o: lundump.c lua.h luaconf.h ldebug.h lstate.h lobject.h \\\n  llimits.h ltm.h lzio.h lmem.h ldo.h lfunc.h lstring.h lgc.h lundump.h\nlvm.o: lvm.c lua.h luaconf.h ldebug.h lstate.h lobject.h llimits.h ltm.h \\\n  lzio.h lmem.h ldo.h lfunc.h lgc.h lopcodes.h lstring.h ltable.h lvm.h\nlzio.o: lzio.c lua.h luaconf.h llimits.h lmem.h lstate.h lobject.h ltm.h \\\n  lzio.h\nprint.o: print.c ldebug.h lstate.h lua.h luaconf.h lobject.h llimits.h \\\n  ltm.h lzio.h lmem.h lopcodes.h lundump.h\nrealpath.o: realpath.c\nkilo.o: kilo.c lua.h teliva.h\nteliva.o: teliva.c lua.h lauxlib.h lualib.h teliva.h tlv.h\n\n# (end of Makefile)\n"
  },
  {
    "path": "src/file.lua",
    "content": "-- primitive for reading files from a file system (or, later, network)\n-- returns an object or nil on error\n-- read lines from the object using .read() with args similar to file:read()\n-- read() will indicate end of file by returning nil.\nfunction start_reading(fs, filename)\n  local infile = io.open(filename)\n  if infile == nil then return nil end\n  return {\n    read = coroutine.wrap(function(format)\n      while true do\n        if format == nil then format = '*l' end\n        format = coroutine.yield(infile:read(format))\n      end\n    end),\n  }\nend\n\n-- fake file object with the same 'shape' as that returned by start_reading\nfunction fake_file_stream(s)\n  local i = 1\n  local max = string.len(s)\n  return {\n    read = function(format)\n      if i > max then\n        return nil\n      end\n      if type(format) == 'number' then\n        local result = s:sub(i, i+format-1)\n        i = i+format\n        return result\n      elseif format == '*a' then\n        local result = s:sub(i)\n        i = max+1\n        return result\n      elseif format == '*l' then\n        local start = i\n        while i <= max do\n          if s:sub(i, i) == '\\n' then\n            break\n          end\n          i = i+1\n        end\n        local result = s:sub(start, i)\n        i = i+1\n        return result\n      elseif format == '*n' then\n        error('fake file streams: *n not yet supported')\n      end\n    end,\n  }\nend\n\nfunction test_fake_file_system()\n  local s = fake_file_stream('abcdefgh\\nijk\\nlmn')\n  check_eq(s.read(1), 'a', 'fake_file_system: 1 char')\n  check_eq(s.read(1), 'b', 'fake_file_system: 1 more char')\n  check_eq(s.read(3), 'cde', 'fake_file_system: multiple chars')\n  check_eq(s.read('*l'), 'fgh\\n', 'fake_file_system: line')\n  check_eq(s.read('*a'), 'ijk\\nlmn', 'fake_file_system: all')\nend\n\n-- primitive for writing files to a file system (or, later, network)\n-- returns an object or nil on error\n-- write to the object using .write()\n-- indicate you're done writing by calling .close()\n-- file will not be externally visible until .close()\nfunction start_writing(fs, filename)\n  if filename == nil then\n    error('start_writing requires two arguments: a file-system (nil for real disk) and a filename')\n  end\n  local initial_filename = temporary_filename_in_same_volume(filename)\n  local outfile = io.open(initial_filename, 'w')\n  if outfile == nil then return nil end\n  return {\n    write = coroutine.wrap(function(x)\n      while true do\n        x = coroutine.yield(outfile:write(x))\n      end\n    end),\n    close = function()\n      outfile:close()\n      os.rename(initial_filename, filename)\n    end,\n  }\nend\n\nfunction temporary_filename_in_same_volume(filename)\n  -- opening in same directory will hopefully keep it on the same volume,\n  -- so that a future rename works\n  local i = 1\n  while true do\n    temporary_filename = 'teliva_tmp_'..filename..'_'..i\n    if io.open(temporary_filename) == nil then\n      -- file doesn't exist yet; create a placeholder and return it\n      local handle = io.open(temporary_filename, 'w')\n      if handle == nil then\n        -- HERE: if an app doesn't have permissions, attempts to write to disk eventually get here\n        error(\"this is unexpected; I can't create temporary files..\")\n      end\n      handle:close()\n      return temporary_filename\n    end\n    i = i+1\n  end\nend\n"
  },
  {
    "path": "src/json.lua",
    "content": "--\n-- https://github.com/rxi/json.lua\n--\n-- Copyright (c) 2020 rxi\n--\n-- Permission is hereby granted, free of charge, to any person obtaining a copy of\n-- this software and associated documentation files (the \"Software\"), to deal in\n-- the Software without restriction, including without limitation the rights to\n-- use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies\n-- of the Software, and to permit persons to whom the Software is furnished to do\n-- so, subject to the following conditions:\n--\n-- The above copyright notice and this permission notice shall be included in all\n-- copies or substantial portions of the Software.\n--\n-- THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n-- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n-- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n-- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n-- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n-- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n-- SOFTWARE.\n--\n\nlocal json = { _version = \"0.1.2\" }\n\n-------------------------------------------------------------------------------\n-- Encode\n-------------------------------------------------------------------------------\n\nlocal encode\n\nlocal escape_char_map = {\n  [ \"\\\\\" ] = \"\\\\\",\n  [ \"\\\"\" ] = \"\\\"\",\n  [ \"\\b\" ] = \"b\",\n  [ \"\\f\" ] = \"f\",\n  [ \"\\n\" ] = \"n\",\n  [ \"\\r\" ] = \"r\",\n  [ \"\\t\" ] = \"t\",\n}\n\nlocal escape_char_map_inv = { [ \"/\" ] = \"/\" }\nfor k, v in pairs(escape_char_map) do\n  escape_char_map_inv[v] = k\nend\n\n\nlocal function escape_char(c)\n  return \"\\\\\" .. (escape_char_map[c] or string.format(\"u%04x\", c:byte()))\nend\n\n\nlocal function encode_nil(val)\n  return \"null\"\nend\n\n\nlocal function encode_table(val, stack)\n  local res = {}\n  stack = stack or {}\n\n  -- Circular reference?\n  if stack[val] then error(\"circular reference\") end\n\n  stack[val] = true\n\n  if rawget(val, 1) ~= nil or next(val) == nil then\n    -- Treat as array -- check keys are valid and it is not sparse\n    local n = 0\n    for k in pairs(val) do\n      if type(k) ~= \"number\" then\n        error(\"invalid table: mixed or invalid key types\")\n      end\n      n = n + 1\n    end\n    if n ~= #val then\n      error(\"invalid table: sparse array\")\n    end\n    -- Encode\n    for i, v in ipairs(val) do\n      table.insert(res, encode(v, stack))\n    end\n    stack[val] = nil\n    return \"[\" .. table.concat(res, \",\") .. \"]\"\n\n  else\n    -- Treat as an object\n    for k, v in pairs(val) do\n      if type(k) ~= \"string\" then\n        error(\"invalid table: mixed or invalid key types\")\n      end\n      table.insert(res, encode(k, stack) .. \":\" .. encode(v, stack))\n    end\n    stack[val] = nil\n    return \"{\" .. table.concat(res, \",\") .. \"}\"\n  end\nend\n\n\nlocal function encode_string(val)\n  return '\"' .. val:gsub('[%z\\1-\\31\\\\\"]', escape_char) .. '\"'\nend\n\n\nlocal function encode_number(val)\n  -- Check for NaN, -inf and inf\n  if val ~= val or val <= -math.huge or val >= math.huge then\n    error(\"unexpected number value '\" .. tostring(val) .. \"'\")\n  end\n  return string.format(\"%.14g\", val)\nend\n\n\nlocal type_func_map = {\n  [ \"nil\"     ] = encode_nil,\n  [ \"table\"   ] = encode_table,\n  [ \"string\"  ] = encode_string,\n  [ \"number\"  ] = encode_number,\n  [ \"boolean\" ] = tostring,\n}\n\n\nencode = function(val, stack)\n  local t = type(val)\n  local f = type_func_map[t]\n  if f then\n    return f(val, stack)\n  end\n  error(\"unexpected type '\" .. t .. \"'\")\nend\n\n\nfunction json.encode(val)\n  return ( encode(val) )\nend\n\n\n-------------------------------------------------------------------------------\n-- Decode\n-------------------------------------------------------------------------------\n\nlocal parse\n\nlocal function create_set(...)\n  local res = {}\n  for i = 1, select(\"#\", ...) do\n    res[ select(i, ...) ] = true\n  end\n  return res\nend\n\nlocal space_chars   = create_set(\" \", \"\\t\", \"\\r\", \"\\n\")\nlocal delim_chars   = create_set(\" \", \"\\t\", \"\\r\", \"\\n\", \"]\", \"}\", \",\")\nlocal escape_chars  = create_set(\"\\\\\", \"/\", '\"', \"b\", \"f\", \"n\", \"r\", \"t\", \"u\")\nlocal literals      = create_set(\"true\", \"false\", \"null\")\n\nlocal literal_map = {\n  [ \"true\"  ] = true,\n  [ \"false\" ] = false,\n  [ \"null\"  ] = nil,\n}\n\n\nlocal function next_char(str, idx, set, negate)\n  for i = idx, #str do\n    if set[str:sub(i, i)] ~= negate then\n      return i\n    end\n  end\n  return #str + 1\nend\n\n\nlocal function decode_error(str, idx, msg)\n  local line_count = 1\n  local col_count = 1\n  for i = 1, idx - 1 do\n    col_count = col_count + 1\n    if str:sub(i, i) == \"\\n\" then\n      line_count = line_count + 1\n      col_count = 1\n    end\n  end\n  error( string.format(\"%s at line %d col %d\", msg, line_count, col_count) )\nend\n\n\nlocal function codepoint_to_utf8(n)\n  -- http://scripts.sil.org/cms/scripts/page.php?site_id=nrsi&id=iws-appendixa\n  local f = math.floor\n  if n <= 0x7f then\n    return string.char(n)\n  elseif n <= 0x7ff then\n    return string.char(f(n / 64) + 192, n % 64 + 128)\n  elseif n <= 0xffff then\n    return string.char(f(n / 4096) + 224, f(n % 4096 / 64) + 128, n % 64 + 128)\n  elseif n <= 0x10ffff then\n    return string.char(f(n / 262144) + 240, f(n % 262144 / 4096) + 128,\n                       f(n % 4096 / 64) + 128, n % 64 + 128)\n  end\n  error( string.format(\"invalid unicode codepoint '%x'\", n) )\nend\n\n\nlocal function parse_unicode_escape(s)\n  local n1 = tonumber( s:sub(1, 4),  16 )\n  local n2 = tonumber( s:sub(7, 10), 16 )\n   -- Surrogate pair?\n  if n2 then\n    return codepoint_to_utf8((n1 - 0xd800) * 0x400 + (n2 - 0xdc00) + 0x10000)\n  else\n    return codepoint_to_utf8(n1)\n  end\nend\n\n\nlocal function parse_string(str, i)\n  local res = \"\"\n  local j = i + 1\n  local k = j\n\n  while j <= #str do\n    local x = str:byte(j)\n\n    if x < 32 then\n      decode_error(str, j, \"control character in string\")\n\n    elseif x == 92 then -- `\\`: Escape\n      res = res .. str:sub(k, j - 1)\n      j = j + 1\n      local c = str:sub(j, j)\n      if c == \"u\" then\n        local hex = str:match(\"^[dD][89aAbB]%x%x\\\\u%x%x%x%x\", j + 1)\n                 or str:match(\"^%x%x%x%x\", j + 1)\n                 or decode_error(str, j - 1, \"invalid unicode escape in string\")\n        res = res .. parse_unicode_escape(hex)\n        j = j + #hex\n      else\n        if not escape_chars[c] then\n          decode_error(str, j - 1, \"invalid escape char '\" .. c .. \"' in string\")\n        end\n        res = res .. escape_char_map_inv[c]\n      end\n      k = j + 1\n\n    elseif x == 34 then -- `\"`: End of string\n      res = res .. str:sub(k, j - 1)\n      return res, j + 1\n    end\n\n    j = j + 1\n  end\n\n  decode_error(str, i, \"expected closing quote for string\")\nend\n\n\nlocal function parse_number(str, i)\n  local x = next_char(str, i, delim_chars)\n  local s = str:sub(i, x - 1)\n  local n = tonumber(s)\n  if not n then\n    decode_error(str, i, \"invalid number '\" .. s .. \"'\")\n  end\n  return n, x\nend\n\n\nlocal function parse_literal(str, i)\n  local x = next_char(str, i, delim_chars)\n  local word = str:sub(i, x - 1)\n  if not literals[word] then\n    decode_error(str, i, \"invalid literal '\" .. word .. \"'\")\n  end\n  return literal_map[word], x\nend\n\n\nlocal function parse_array(str, i)\n  local res = {}\n  local n = 1\n  i = i + 1\n  while 1 do\n    local x\n    i = next_char(str, i, space_chars, true)\n    -- Empty / end of array?\n    if str:sub(i, i) == \"]\" then\n      i = i + 1\n      break\n    end\n    -- Read token\n    x, i = parse(str, i)\n    res[n] = x\n    n = n + 1\n    -- Next token\n    i = next_char(str, i, space_chars, true)\n    local chr = str:sub(i, i)\n    i = i + 1\n    if chr == \"]\" then break end\n    if chr ~= \",\" then decode_error(str, i, \"expected ']' or ','\") end\n  end\n  return res, i\nend\n\n\nlocal function parse_object(str, i)\n  local res = {}\n  i = i + 1\n  while 1 do\n    local key, val\n    i = next_char(str, i, space_chars, true)\n    -- Empty / end of object?\n    if str:sub(i, i) == \"}\" then\n      i = i + 1\n      break\n    end\n    -- Read key\n    if str:sub(i, i) ~= '\"' then\n      decode_error(str, i, \"expected string for key\")\n    end\n    key, i = parse(str, i)\n    -- Read ':' delimiter\n    i = next_char(str, i, space_chars, true)\n    if str:sub(i, i) ~= \":\" then\n      decode_error(str, i, \"expected ':' after key\")\n    end\n    i = next_char(str, i + 1, space_chars, true)\n    -- Read value\n    val, i = parse(str, i)\n    -- Set\n    res[key] = val\n    -- Next token\n    i = next_char(str, i, space_chars, true)\n    local chr = str:sub(i, i)\n    i = i + 1\n    if chr == \"}\" then break end\n    if chr ~= \",\" then decode_error(str, i, \"expected '}' or ','\") end\n  end\n  return res, i\nend\n\n\nlocal char_func_map = {\n  [ '\"' ] = parse_string,\n  [ \"0\" ] = parse_number,\n  [ \"1\" ] = parse_number,\n  [ \"2\" ] = parse_number,\n  [ \"3\" ] = parse_number,\n  [ \"4\" ] = parse_number,\n  [ \"5\" ] = parse_number,\n  [ \"6\" ] = parse_number,\n  [ \"7\" ] = parse_number,\n  [ \"8\" ] = parse_number,\n  [ \"9\" ] = parse_number,\n  [ \"-\" ] = parse_number,\n  [ \"t\" ] = parse_literal,\n  [ \"f\" ] = parse_literal,\n  [ \"n\" ] = parse_literal,\n  [ \"[\" ] = parse_array,\n  [ \"{\" ] = parse_object,\n}\n\n\nparse = function(str, idx)\n  local chr = str:sub(idx, idx)\n  local f = char_func_map[chr]\n  if f then\n    return f(str, idx)\n  end\n  decode_error(str, idx, \"unexpected character '\" .. chr .. \"'\")\nend\n\n\nfunction json.decode(str)\n  if type(str) ~= \"string\" then\n    error(\"expected argument of type string, got \" .. type(str))\n  end\n  local res, idx = parse(str, next_char(str, 1, space_chars, true))\n  idx = next_char(str, idx, space_chars, true)\n  if idx <= #str then\n    decode_error(str, idx, \"trailing garbage\")\n  end\n  return res\nend\n\n\nreturn json\n"
  },
  {
    "path": "src/jsonf.lua",
    "content": "--\n-- variant of https://github.com/rxi/json.lua decoding from channels of\n-- characters rather than strings\n--\n-- Copyright (c) 2020 rxi\n--\n-- Permission is hereby granted, free of charge, to any person obtaining a copy of\n-- this software and associated documentation files (the \"Software\"), to deal in\n-- the Software without restriction, including without limitation the rights to\n-- use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies\n-- of the Software, and to permit persons to whom the Software is furnished to do\n-- so, subject to the following conditions:\n--\n-- The above copyright notice and this permission notice shall be included in all\n-- copies or substantial portions of the Software.\n--\n-- THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n-- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n-- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n-- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n-- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n-- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n-- SOFTWARE.\n--\n\nlocal jsonf = { _version = \"0.1.2\" }\n\nlocal escape_char_map = {\n  [ \"\\\\\" ] = \"\\\\\",\n  [ \"\\\"\" ] = \"\\\"\",\n  [ \"\\b\" ] = \"b\",\n  [ \"\\f\" ] = \"f\",\n  [ \"\\n\" ] = \"n\",\n  [ \"\\r\" ] = \"r\",\n  [ \"\\t\" ] = \"t\",\n}\n\nlocal escape_char_map_inv = { [ \"/\" ] = \"/\" }\nfor k, v in pairs(escape_char_map) do\n  escape_char_map_inv[v] = k\nend\n\n\n-------------------------------------------------------------------------------\n-- Decode\n-------------------------------------------------------------------------------\n\nlocal function create_set(...)\n  local res = {}\n  for i = 1, select(\"#\", ...) do\n    res[ select(i, ...) ] = true\n  end\n  return res\nend\n\nlocal space_chars   = create_set(\" \", \"\\t\", \"\\r\", \"\\n\")\nlocal delim_chars   = create_set(\" \", \"\\t\", \"\\r\", \"\\n\", \"]\", \"}\", \",\")\nlocal escape_chars  = create_set(\"\\\\\", \"/\", '\"', \"b\", \"f\", \"n\", \"r\", \"t\", \"u\")\nlocal literals      = create_set(\"true\", \"false\", \"null\")\n\nlocal literal_map = {\n  [ \"true\"  ] = true,\n  [ \"false\" ] = false,\n  [ \"null\"  ] = nil,\n}\n\n\nlocal function skip_spaces(infile)\n  while true do\n    local c = infile.read(1)\n    if c == nil then break end\n    if space_chars[c] == nil then return c end\n  end\n  return nil\nend\n\n\nlocal function next_chars(infile, set, firstc)\n  local res = {firstc}\n  local nextc\n  while true do\n    nextc = infile.read(1)\n    if nextc == nil then break end\n    if set[nextc] then break end\n    table.insert(res, nextc)\n  end\n  return table.concat(res), nextc\nend\n\n\nlocal function codepoint_to_utf8(n)\n  -- http://scripts.sil.org/cms/scripts/page.php?site_id=nrsi&id=iws-appendixa\n  local f = math.floor\n  if n <= 0x7f then\n    return string.char(n)\n  elseif n <= 0x7ff then\n    return string.char(f(n / 64) + 192, n % 64 + 128)\n  elseif n <= 0xffff then\n    return string.char(f(n / 4096) + 224, f(n % 4096 / 64) + 128, n % 64 + 128)\n  elseif n <= 0x10ffff then\n    return string.char(f(n / 262144) + 240, f(n % 262144 / 4096) + 128,\n                       f(n % 4096 / 64) + 128, n % 64 + 128)\n  end\n  error( string.format(\"invalid unicode codepoint '%x'\", n) )\nend\n\n\nlocal function parse_unicode_escape(s)\n  local n1 = tonumber( s:sub(1, 4),  16 )\n  local n2 = tonumber( s:sub(7, 10), 16 )\n   -- Surrogate pair?\n  if n2 then\n    return codepoint_to_utf8((n1 - 0xd800) * 0x400 + (n2 - 0xdc00) + 0x10000)\n  else\n    return codepoint_to_utf8(n1)\n  end\nend\n\n\nlocal function parse_string(infile, firstc)\n  local res = {}\n\n  while true do\n    local chr = infile.read(1)\n    if chr == nil then break end\n    local x = chr:byte()\n\n    if x < 32 then\n      error(\"control character in string\")\n    elseif chr == '\\\\' then\n      local c = infile.read(1)\n      if c == nil then break end\n      if c == \"u\" then\n        local hex = ''\n        c = infile.read(1)\n        if c == nil then break end\n        hex = hex..c\n        c = infile.read(1)\n        if c == nil then break end\n        hex = hex..c\n        c = infile.read(1)\n        if c == nil then break end\n        hex = hex..c\n        c = infile.read(1)\n        if c == nil then break end\n        hex = hex..c\n        if not hex:match('^%x%x%x%x') then\n          error('invalid unicode escape in string')\n        end\n        table.insert(res, parse_unicode_escape(hex))\n      else\n        if not escape_chars[c] then\n          error(\"invalid escape char '\" .. c .. \"' in string\")\n        end\n        table.insert(res, escape_char_map_inv[c])\n      end\n    elseif chr == '\"' then\n      return table.concat(res), infile.read(1)\n    else\n      table.insert(res, chr)\n    end\n  end\n\n  error(\"expected closing quote for string\")\nend\n\n\nlocal function parse_number(infile, firstc)\n--?   print('parse_number')\n  local res = {firstc}\n  local nextc\n  while true do\n    nextc = infile.read(1)\n    if nextc == nil then break end\n    if delim_chars[nextc] then break end\n    table.insert(res, nextc)\n  end\n  local s = table.concat(res)\n--?   print('parse_number: '..s)\n  local n = tonumber(s)\n  if not n then\n    error(\"invalid number '\" .. s .. \"'\")\n  end\n  return n, nextc\nend\n\n\nlocal function parse_literal(infile, firstc)\n--?   print('parse_literal')\n  local word, nextc = next_chars(infile, delim_chars, firstc)\n  if not literals[word] then\n    error(\"invalid literal '\" .. word .. \"'\")\n  end\n--?   print('parse_literal: '..word)\n  return literal_map[word], nextc\nend\n\n\nlocal function parse_array(infile, firstc)\n  local res = {}\n  local x, nextc\n  while true do\n    nextc = skip_spaces(infile)\n    if nextc == nil then\n      error(\"expected ']' or ','\")\n    end\n    if nextc == ']' then break end  -- empty array\n    -- Read token\n    x, nextc = parse(infile, nextc)\n--?     print('array elem: '..str(x))\n    table.insert(res, x)\n    -- Next token\n    if space_chars[nextc] then\n      nextc = skip_spaces(infile)\n    end\n    if nextc == ']' then break end\n    if nextc ~= ',' then\n      error(\"expected ']' or ','\")\n    end\n  end\n  return res, skip_spaces(infile)\nend\n\n\nlocal function parse_object(infile, firstc)\n  local res = {}\n  local nextc\n  while true do\n    local key, val\n    nextc = skip_spaces(infile)\n    if nextc == nil then\n      error(\"expected '}' or ','\")\n    end\n    if nextc == '}' then break end  -- empty object\n    -- Read key\n    if nextc ~= '\"' then\n      error(\"expected string for key\")\n    end\n    key, nextc = parse(infile, nextc)\n--?     print('object key: '..key)\n    -- Read ':' delimiter\n    if space_chars[nextc] then\n      nextc = skip_spaces(infile)\n    end\n    if nextc ~= ':' then\n      error(\"expected ':' after key\")\n    end\n    -- Read value\n    nextc = skip_spaces(infile)\n    val, nextc = parse(infile, nextc)\n--?     print('object val: '..str(val))\n    -- Set\n    res[key] = val\n    -- Next token\n    if space_chars[nextc] then\n      nextc = skip_spaces(infile)\n    end\n    if nextc == '}' then break end\n    if nextc ~= ',' then\n      error(\"expected '}' or ','\")\n    end\n  end\n  return res, skip_spaces(infile)\nend\n\n\nlocal char_func_map = {\n  [ '\"' ] = parse_string,\n  [ \"0\" ] = parse_number,\n  [ \"1\" ] = parse_number,\n  [ \"2\" ] = parse_number,\n  [ \"3\" ] = parse_number,\n  [ \"4\" ] = parse_number,\n  [ \"5\" ] = parse_number,\n  [ \"6\" ] = parse_number,\n  [ \"7\" ] = parse_number,\n  [ \"8\" ] = parse_number,\n  [ \"9\" ] = parse_number,\n  [ \"-\" ] = parse_number,\n  [ \"t\" ] = parse_literal,\n  [ \"f\" ] = parse_literal,\n  [ \"n\" ] = parse_literal,\n  [ \"[\" ] = parse_array,\n  [ \"{\" ] = parse_object,\n}\n\n\nparse = function(infile, chr)\n  local f = char_func_map[chr]\n  if f then\n    return f(infile, chr)\n  end\n  error(\"unexpected character '\" .. chr .. \"'\")\nend\n\n\nfunction jsonf.decode(infile)\n  local firstc = skip_spaces(infile)\n  local res, nextc = parse(infile, firstc)\n  if nextc then\n    error(\"trailing garbage\")\n  end\n  return res\nend\n\n\n-- test cases:\n--   \"abc\"\n--   234\n--   true\n--   false\n--   nil\n--   [\"abc\", 234, true, false, nil]\n--   [\"abc\", 234, true, false, nil\n--   [\"abc\",\n--   {\"abc\": 234, \"def\": true}\n\nreturn jsonf\n"
  },
  {
    "path": "src/kilo.c",
    "content": "/* Based on https://github.com/antirez/kilo\n *\n * -----------------------------------------------------------------------\n *\n * Copyright (C) 2016 Salvatore Sanfilippo <antirez at gmail dot com>\n *\n * All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions are\n * met:\n *\n *  *  Redistributions of source code must retain the above copyright\n *     notice, this list of conditions and the following disclaimer.\n *\n *  *  Redistributions in binary form must reproduce the above copyright\n *     notice, this list of conditions and the following disclaimer in the\n *     documentation and/or other materials provided with the distribution.\n *\n * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\n * \"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\n * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR\n * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT\n * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\n * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT\n * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,\n * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY\n * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n#define KILO_VERSION \"0.0.1\"\n\n#include <assert.h>\n#ifdef __NetBSD__\n#include <curses.h>\n#else\n#include <ncurses.h>\n#endif\n#include <stdlib.h>\n#include <stdio.h>\n#include <stdint.h>\n#include <errno.h>\n#include <string.h>\n#include <ctype.h>\n#include <time.h>\n#include <sys/types.h>\n#include <sys/time.h>\n#include <unistd.h>\n#include <fcntl.h>\n\n#include \"lua.h\"\n#include \"teliva.h\"\n\n/* Syntax highlight types */\n#define HL_NORMAL 0\n#define HL_NONPRINT 1\n#define HL_COMMENT 2   /* Single line comment. */\n#define HL_MLCOMMENT 3 /* Multi-line comment. */\n#define HL_KEYWORD1 4\n#define HL_KEYWORD2 5\n#define HL_STRING 6\n#define HL_NUMBER 7\n#define HL_MATCH 8      /* Search match. */\n#define HL_SELECTABLE 9\n#define HL_SELECTABLE_BORDER 10\n\nstruct editorSyntax {\n    char **keywords;\n    char *singleline_comment_start;\n    char *multiline_comment_start;\n    char *multiline_comment_end;\n    char *selectable_start;\n    char *selectable_end;\n};\n\n/* This structure represents a single line of the file we are editing. */\ntypedef struct erow {\n    int idx;            /* Row index in the file, zero-based. */\n    int size;           /* Size of the row, excluding the null term. */\n    int rsize;          /* Size of the rendered row. */\n    char *chars;        /* Row content. */\n    char *render;       /* Row content \"rendered\" for screen (for TABs). */\n    unsigned char *hl;  /* Syntax highlight type for each character in render.*/\n    int hl_oc;          /* Row had open comment at end in last syntax highlight\n                           check. */\n} erow;\n\ntypedef struct hlcolor {\n    int r,g,b;\n} hlcolor;\n\n#define LINE_NUMBER_SPACE 4\n#define MENU_SPACE 1\n#define CALLERS_SPACE 2\n\nstruct editorConfig {\n    int cx,cy;  /* Cursor x and y position in characters relative to top-left of viewport */\n    int startcol, cols;  /* viewport column bounds */\n    int startrow, endrow;  /* viewport row bounds */\n    int rowoff;     /* Offset of row displayed. */\n    int coloff;     /* Offset of column displayed. */\n    int numrows;    /* Number of rows */\n    erow *row;      /* Rows */\n    int dirty;      /* File modified but not saved. */\n    char *filename; /* Currently open filename */\n    struct editorSyntax *syntax;    /* Current syntax highlight, or NULL. */\n};\n\nstatic struct editorConfig E;\n\n/* =========================== Syntax highlights DB =========================\n *\n * In order to add a new syntax, define two arrays with a list of file name\n * matches and keywords. The file name matches are used in order to match\n * a given syntax with a given file name: if a match pattern starts with a\n * dot, it is matched as the last past of the filename, for example \".c\".\n * Otherwise the pattern is just searched inside the filenme, like \"Makefile\").\n *\n * The list of keywords to highlight is just a list of words, however if they\n * a trailing '|' character is added at the end, they are highlighted in\n * a different color, so that you can have two different sets of keywords.\n *\n * Finally add a stanza in the HLDB global variable with two two arrays\n * of strings.\n *\n * The characters for single and multi line comments must be exactly two\n * and must be provided as well (see the C language example).\n *\n * There is no support to highlight patterns currently. */\n\n/* Lua */\nchar *Lua_HL_keywords[] = {\n    /* keywords */\n    \"do\", \"end\", \"function\", \"return\", \"require\", \"local\",\n    \"if\", \"then\", \"else\", \"elseif\",\n    \"while\", \"for\", \"repeat\", \"until\", \"break\",\n    \"and\", \"or\", \"not\", \"in\",\n\n    /* types */\n    \"nil|\", \"false|\", \"true|\",\n\n    NULL\n};\n\nstruct editorSyntax LuaSyntax = {\n    Lua_HL_keywords,\n    \"--\",  /* line comment */\n    \"--[[\", \"--]]\",  /* multiline comment */\n    NULL, NULL  /* no selectables */\n};\n\n/* Prose */\nstruct editorSyntax ProseSyntax = {\n    NULL,  /* no keywords */\n    NULL,  /* no line comment */\n    NULL, NULL,  /* no multiline comment */\n    \"[[\", \"]]\"  /* \"selectables\" */\n};\n\n#define HLDB_ENTRIES (sizeof(HLDB)/sizeof(HLDB[0]))\n\n/* ====================== Syntax highlight color scheme  ==================== */\n\nstatic int is_separator(int c) {\n    return c == '\\0' || isspace(c) || strchr(\",.()+-/*=~%[];\",c) != NULL;\n}\n\n/* Return true if the specified row last char is part of a multi line comment\n * that starts at this row or at one before, and does not end at the end\n * of the row but spawns to the next row. */\nstatic int editorRowHasOpenComment(erow *row, char *mce) {\n    if (row->hl && row->rsize && row->hl[row->rsize-1] == HL_MLCOMMENT &&\n        (row->rsize < strlen(mce) || strcmp(&row->render[row->rsize-strlen(mce)], mce) != 0)) return 1;\n    return 0;\n}\n\n/* Set every byte of row->hl (that corresponds to every character in the line)\n * to the right syntax highlight type (HL_* defines). */\nstatic void editorUpdateSyntax(erow *row) {\n    row->hl = realloc(row->hl,row->rsize);\n    memset(row->hl,HL_NORMAL,row->rsize);\n\n    if (E.syntax == NULL) return; /* No syntax, everything is HL_NORMAL. */\n\n    int i, prev_sep, in_string, in_comment, in_selectable;\n    char *p;\n    char **keywords = E.syntax->keywords;\n    char *scs = E.syntax->singleline_comment_start;\n    char *mcs = E.syntax->multiline_comment_start;\n    char *mce = E.syntax->multiline_comment_end;\n    char *ss = E.syntax->selectable_start;\n    char *se = E.syntax->selectable_end;\n\n    /* Point to the first non-space char. */\n    p = row->render;\n    i = 0; /* Current char offset */\n    while(*p && isspace(*p)) {\n        p++;\n        i++;\n    }\n    prev_sep = 1; /* Tell the parser if 'i' points to start of word. */\n    in_string = 0; /* Are we inside \"\" or '' ? */\n    in_comment = 0; /* Are we inside multi-line comment? */\n    in_selectable = 0;  /* Are we inside [[]] in prose? */\n\n    /* If the previous line has an open comment, this line starts\n     * with an open comment state. */\n    if (row->idx > 0 && editorRowHasOpenComment(&E.row[row->idx-1], mce))\n        in_comment = 1;\n\n    while(*p) {\n        /* Handle multi line comments. */\n        if (mcs && mce) {\n            if (in_comment) {\n                row->hl[i] = HL_MLCOMMENT;\n                if (starts_with(p, mce)) {\n                    memset(&row->hl[i],HL_MLCOMMENT, strlen(mce));\n                    p += strlen(mce); i += strlen(mce);\n                    in_comment = 0;\n                    prev_sep = 1;\n                    continue;\n                } else {\n                    prev_sep = 0;\n                    p++; i++;\n                    continue;\n                }\n            } else if (starts_with(p, mcs)) {\n                memset(&row->hl[i],HL_MLCOMMENT, strlen(mcs));\n                p += strlen(mcs); i += strlen(mcs);\n                in_comment = 1;\n                prev_sep = 0;\n                continue;\n            }\n        }\n\n        /* Handle single-line comments. */\n        if (scs && prev_sep && starts_with(p, scs)) {\n            /* From here to end is a comment */\n            memset(row->hl+i,HL_COMMENT,row->rsize-i);\n            return;\n        }\n\n        /* Handle selectable widgets */\n        if (ss && se) {\n            if (in_selectable) {\n                if (starts_with(p, se)) {\n                    memset(&row->hl[i],HL_SELECTABLE_BORDER, strlen(se));\n                    p += strlen(se); i += strlen(se);\n                    in_selectable = 0;\n                    prev_sep = 1;\n                    continue;\n                } else {\n                    row->hl[i] = HL_SELECTABLE;\n                    prev_sep = 0;\n                    p++; i++;\n                    continue;\n                }\n            } else if (starts_with(p, ss)) {\n                memset(&row->hl[i],HL_SELECTABLE_BORDER, strlen(ss));\n                p += strlen(ss); i += strlen(ss);\n                in_selectable = 1;\n                prev_sep = 0;\n                continue;\n            }\n        }\n\n        /* Handle \"\" and '' */\n        if (E.syntax == &LuaSyntax) {  // obscene hack\n            if (in_string) {\n                row->hl[i] = HL_STRING;\n                if (*p == '\\\\' && *(p+1)) {\n                    row->hl[i+1] = HL_STRING;\n                    p += 2; i += 2;\n                    prev_sep = 0;\n                    continue;\n                }\n                if (*p == in_string) in_string = 0;\n                p++; i++;\n                continue;\n            } else {\n                if (*p == '\"' || *p == '\\'') {\n                    in_string = *p;\n                    row->hl[i] = HL_STRING;\n                    p++; i++;\n                    prev_sep = 0;\n                    continue;\n                }\n            }\n        }\n\n        /* Handle non printable chars. */\n        if (!isprint(*p)) {\n            row->hl[i] = HL_NONPRINT;\n            p++; i++;\n            prev_sep = 0;\n            continue;\n        }\n\n        /* Handle numbers */\n        if ((isdigit(*p) && (prev_sep || row->hl[i-1] == HL_NUMBER)) ||\n            (*p == '.' && i > 0 && row->hl[i-1] == HL_NUMBER)) {\n            row->hl[i] = HL_NUMBER;\n            p++; i++;\n            prev_sep = 0;\n            continue;\n        }\n\n        /* Handle keywords and lib calls */\n        if (keywords && prev_sep) {\n            int j;\n            int ileft = row->rsize-i;\n            for (j = 0; keywords[j]; j++) {\n                int klen = strlen(keywords[j]);\n                int kw2 = keywords[j][klen-1] == '|';\n                if (kw2) klen--;\n\n                if (klen <= ileft && !memcmp(p,keywords[j],klen) &&\n                    is_separator(*(p+klen)))\n                {\n                    /* Keyword */\n                    memset(row->hl+i,kw2 ? HL_KEYWORD2 : HL_KEYWORD1,klen);\n                    p += klen;\n                    i += klen;\n                    break;\n                }\n            }\n            if (keywords[j] != NULL) {\n                prev_sep = 0;\n                continue; /* We had a keyword match */\n            }\n        }\n\n        /* Not special chars */\n        prev_sep = is_separator(*p);\n        p++; i++;\n    }\n\n    /* Propagate syntax change to the next row if the open comment\n     * state changed. This may recursively affect all the following rows\n     * in the file. */\n    int oc = editorRowHasOpenComment(row, mce);\n    if (row->hl_oc != oc && row->idx+1 < E.numrows)\n        editorUpdateSyntax(&E.row[row->idx+1]);\n    row->hl_oc = oc;\n}\n\n/* Maps syntax highlight token types to terminal colors. */\nstatic int editorSyntaxToColorPair(int hl) {\n    switch(hl) {\n    case HL_COMMENT:\n    case HL_MLCOMMENT: return COLOR_PAIR_LUA_COMMENT;\n    case HL_KEYWORD1: return COLOR_PAIR_LUA_KEYWORD;\n    case HL_KEYWORD2: return COLOR_PAIR_LUA_CONSTANT;\n    case HL_STRING: return COLOR_PAIR_LUA_CONSTANT;\n    case HL_NUMBER: return COLOR_PAIR_LUA_CONSTANT;\n    case HL_MATCH: return COLOR_PAIR_MATCH;\n    case HL_SELECTABLE: return COLOR_PAIR_SELECTABLE;\n    case HL_SELECTABLE_BORDER: return COLOR_PAIR_FADE;\n    default: return COLOR_PAIR_NORMAL;\n    }\n}\n\n/* ======================= Editor rows implementation ======================= */\n\n/* Update the rendered version and the syntax highlight of a row. */\nstatic void editorUpdateRow(erow *row) {\n    unsigned int tabs = 0, nonprint = 0;\n    int j, idx;\n\n   /* Create a version of the row we can directly print on the screen,\n     * respecting tabs, substituting non printable characters with '?'. */\n    free(row->render);\n    for (j = 0; j < row->size; j++)\n        if (row->chars[j] == TAB) tabs++;\n\n    unsigned long long allocsize =\n        (unsigned long long) row->size + tabs*8 + nonprint*9 + 1;\n    if (allocsize > UINT32_MAX) {\n        printf(\"Some line of the edited file is too long for kilo\\n\");\n        exit(1);\n    }\n\n    row->render = malloc(row->size + tabs*8 + nonprint*9 + 1);\n    idx = 0;\n    for (j = 0; j < row->size; j++) {\n        if (row->chars[j] == TAB) {\n            row->render[idx++] = ' ';\n            while((idx+1) % 8 != 0) row->render[idx++] = ' ';\n        } else {\n            row->render[idx++] = row->chars[j];\n        }\n    }\n    row->rsize = idx;\n    row->render[idx] = '\\0';\n\n    /* Update the syntax highlighting attributes of the row. */\n    editorUpdateSyntax(row);\n}\n\n/* Insert a row at the specified position, shifting the other rows on the bottom\n * if required. */\nstatic void editorInsertRow(int at, char *s, size_t len) {\n    if (at > E.numrows) return;\n    E.row = realloc(E.row,sizeof(erow)*(E.numrows+1));\n    if (at != E.numrows) {\n        memmove(E.row+at+1,E.row+at,sizeof(E.row[0])*(E.numrows-at));\n        for (int j = at+1; j <= E.numrows; j++) E.row[j].idx++;\n    }\n    E.row[at].size = len;\n    E.row[at].chars = malloc(len+1);\n    memcpy(E.row[at].chars,s,len+1);\n    E.row[at].hl = NULL;\n    E.row[at].hl_oc = 0;\n    E.row[at].render = NULL;\n    E.row[at].rsize = 0;\n    E.row[at].idx = at;\n    editorUpdateRow(E.row+at);\n    E.numrows++;\n    E.dirty++;\n}\n\n/* Free row's heap allocated stuff. */\nstatic void editorFreeRow(erow *row) {\n    free(row->render);\n    free(row->chars);\n    free(row->hl);\n}\n\n/* Remove the row at the specified position, shifting the remaining up. */\nstatic void editorDelRow(int at) {\n    erow *row;\n\n    if (at >= E.numrows) return;\n    row = E.row+at;\n    editorFreeRow(row);\n    memmove(E.row+at,E.row+at+1,sizeof(E.row[0])*(E.numrows-at-1));\n    for (int j = at; j < E.numrows-1; j++) E.row[j].idx--;\n    E.numrows--;\n    E.dirty++;\n}\n\nvoid clearEditor(void) {\n    E.cx = E.cy = 0;\n    E.rowoff = E.coloff = 0;\n    for (int j = E.numrows-1; j >= 0; j--)\n      editorDelRow(j);\n}\n\n/* Turn the editor rows into a single heap-allocated string.\n * Returns the pointer to the heap-allocated string and populate the\n * integer pointed by 'buflen' with the size of the string, excluding\n * the final null term. */\nstatic char *editorRowsToString(int *buflen) {\n    char *buf = NULL, *p;\n    int totlen = 0;\n    int j;\n\n    /* Compute count of bytes */\n    for (j = 0; j < E.numrows; j++)\n        totlen += E.row[j].size+1; /* +1 is for \"\\n\" at end of every row */\n    *buflen = totlen;\n    totlen++; /* Also make space for null term */\n\n    p = buf = malloc(totlen);\n    for (j = 0; j < E.numrows; j++) {\n        memcpy(p,E.row[j].chars,E.row[j].size);\n        p += E.row[j].size;\n        *p = '\\n';\n        p++;\n    }\n    *p = '\\0';\n    return buf;\n}\n\n/* Insert a character at the specified position in a row, moving the remaining\n * chars on the right if needed. */\nstatic void editorRowInsertChar(erow *row, int at, int c) {\n    if (at > row->size) {\n        /* Pad the string with spaces if the insert location is outside the\n         * current length by more than a single character. */\n        int padlen = at-row->size;\n        /* In the next line +2 means: new char and null term. */\n        row->chars = realloc(row->chars,row->size+padlen+2);\n        memset(row->chars+row->size,' ',padlen);\n        row->chars[row->size+padlen+1] = '\\0';\n        row->size += padlen+1;\n    } else {\n        /* If we are in the middle of the string just make space for 1 new\n         * char plus the (already existing) null term. */\n        row->chars = realloc(row->chars,row->size+2);\n        memmove(row->chars+at+1,row->chars+at,row->size-at+1);\n        row->size++;\n    }\n    row->chars[at] = c;\n    editorUpdateRow(row);\n    E.dirty++;\n}\n\n/* Append the string 's' at the end of a row */\nstatic void editorRowAppendString(erow *row, char *s, size_t len) {\n    row->chars = realloc(row->chars,row->size+len+1);\n    memcpy(row->chars+row->size,s,len);\n    row->size += len;\n    row->chars[row->size] = '\\0';\n    editorUpdateRow(row);\n    E.dirty++;\n}\n\n/* Delete the character at offset 'at' from the specified row. */\nstatic void editorRowDelChar(erow *row, int at) {\n    if (row->size <= at) return;\n    memmove(row->chars+at,row->chars+at+1,row->size-at);\n    editorUpdateRow(row);\n    row->size--;\n    E.dirty++;\n}\n\n/* Insert the specified char at the current prompt position. */\nstatic void editorInsertChar(int c) {\n    int filerow = E.rowoff+E.cy;\n    int filecol = E.coloff+E.cx;\n    erow *row = (filerow >= E.numrows) ? NULL : &E.row[filerow];\n\n    /* If the row where the cursor is currently located does not exist in our\n     * logical representaion of the file, add enough empty rows as needed. */\n    if (!row) {\n        while(E.numrows <= filerow)\n            editorInsertRow(E.numrows,\"\",0);\n    }\n    row = &E.row[filerow];\n    editorRowInsertChar(row,filecol,c);\n    if (E.cx == E.cols-1)\n        E.coloff++;\n    else\n        E.cx++;\n    E.dirty++;\n}\n\n/* Inserting a newline is slightly complex as we have to handle inserting a\n * newline in the middle of a line, splitting the line as needed. */\nstatic void editorInsertNewline(void) {\n    int filerow = E.rowoff+E.cy;\n    int filecol = E.coloff+E.cx;\n    erow *row = (filerow >= E.numrows) ? NULL : &E.row[filerow];\n\n    if (!row) {\n        if (filerow == E.numrows) {\n            editorInsertRow(filerow,\"\",0);\n            goto fixcursor;\n        }\n        return;\n    }\n    /* If the cursor is over the current line size, we want to conceptually\n     * think it's just over the last character. */\n    if (filecol >= row->size) filecol = row->size;\n    if (filecol == 0) {\n        editorInsertRow(filerow,\"\",0);\n    } else {\n        /* We are in the middle of a line. Split it between two rows. */\n        editorInsertRow(filerow+1,row->chars+filecol,row->size-filecol);\n        row = &E.row[filerow];\n        row->chars[filecol] = '\\0';\n        row->size = filecol;\n        editorUpdateRow(row);\n    }\nfixcursor:\n    if (E.startrow + E.cy == E.endrow-1) {\n        E.rowoff++;\n    } else {\n        E.cy++;\n    }\n    E.cx = 0;\n    E.coloff = 0;\n}\n\n/* Delete the char at the current prompt position. */\nstatic void editorDelChar() {\n    int filerow = E.rowoff+E.cy;\n    int filecol = E.coloff+E.cx;\n    erow *row = (filerow >= E.numrows) ? NULL : &E.row[filerow];\n\n    if (!row || (filecol == 0 && filerow == 0)) return;\n    if (filecol == 0) {\n        /* Handle the case of column 0, we need to move the current line\n         * on the right of the previous one. */\n        filecol = E.row[filerow-1].size;\n        editorRowAppendString(&E.row[filerow-1],row->chars,row->size);\n        editorDelRow(filerow);\n        row = NULL;\n        if (E.cy == 0)\n            E.rowoff--;\n        else\n            E.cy--;\n        E.cx = filecol;\n        if (E.cx >= E.cols) {\n            int shift = (E.cols-E.cx)+1;\n            E.cx -= shift;\n            E.coloff += shift;\n        }\n    } else {\n        editorRowDelChar(row,filecol-1);\n        if (E.cx == 0 && E.coloff)\n            E.coloff--;\n        else\n            E.cx--;\n    }\n    if (row) editorUpdateRow(row);\n    E.dirty++;\n}\n\nstatic void editorUncommentCursorRow() {\n    erow *row = &E.row[E.rowoff+E.cy];\n    E.coloff = 0;\n    E.cx = 4;\n    editorUpdateRow(row);\n    editorDelChar();\n    editorDelChar();\n    editorDelChar();\n    editorDelChar();\n}\n\nstatic void editorCommentCursorRow() {\n    erow *row = &E.row[E.rowoff+E.cy];\n    editorRowInsertChar(row, 0, ' ');\n    editorRowInsertChar(row, 0, '?');\n    editorRowInsertChar(row, 0, '-');\n    editorRowInsertChar(row, 0, '-');\n    E.coloff = 0;\n    E.cx = 0;\n}\n\n/* Load the specified program in the editor memory and returns 0 on success\n * or 1 on error. */\nint editorOpen(char *filename) {\n    FILE *fp;\n\n    E.dirty = 0;\n    free(E.filename);\n    size_t fnlen = strlen(filename)+1;\n    E.filename = malloc(fnlen);\n    memcpy(E.filename,filename,fnlen);\n\n    fp = fopen(filename,\"r\");\n    if (!fp) {\n        if (errno != ENOENT) {\n            perror(\"Opening file\");\n            exit(1);\n        }\n        return 1;\n    }\n\n    char *line = NULL;\n    size_t linecap = 0;\n    ssize_t linelen;\n    while((linelen = getline(&line,&linecap,fp)) != -1) {\n        if (linelen && (line[linelen-1] == '\\n' || line[linelen-1] == '\\r'))\n            line[--linelen] = '\\0';\n        editorInsertRow(E.numrows,line,linelen);\n    }\n    free(line);\n    fclose(fp);\n    E.dirty = 0;\n    return 0;\n}\n\n/* Save the current file on disk. Return 0 on success, 1 on error. */\nstatic int editorSaveToDisk(void) {\n    int len;\n    char *buf = editorRowsToString(&len);\n    int fd = open(E.filename,O_RDWR|O_CREAT,0644);\n    if (fd == -1) goto writeerr;\n\n    /* Use truncate + a single write(2) call in order to make saving\n     * a bit safer, under the limits of what we can do in a small editor. */\n    if (ftruncate(fd,len) == -1) goto writeerr;\n    if (write(fd,buf,len) != len) goto writeerr;\n\n    close(fd);\n    free(buf);\n    E.dirty = 0;\n    return 0;\n\nwriteerr:\n    free(buf);\n    if (fd != -1) close(fd);\n    /* TODO: better error handling. */\n    /* I haven't gotten to this yet since we have version control. */\n    endwin();\n    printf(\"Can't save! I/O error: %s\",strerror(errno));\n    exit(1);\n    return 1;\n}\n\n/* ============================= Terminal update ============================ */\n\nstatic void editorMenu(void) {\n    attrset(A_REVERSE);\n    for (int x = 0; x < COLS; ++x)\n      mvaddch(LINES-1, x, ' ');\n    attrset(A_NORMAL);\n    menu_column = 2;\n    draw_menu_item(\"^x\", \"run\");\n    if (Previous_error != NULL) {\n      attron(A_BOLD);\n      draw_menu_item(\"^c\", \"abort\");\n      attroff(A_BOLD);\n    }\n    draw_menu_item(\"^g\", \"go\");\n    draw_menu_item(\"^b\", \"big picture\");\n    draw_menu_item(\"^f\", \"find\");\n    draw_menu_item(\"^h\", \"backspace\");\n    draw_menu_item(\"^l\", \"end of line\");\n    /* draw_menu_item(\"^/|^-|^_\", \"(un)comment line\"); */\n    attroff(A_REVERSE);\n    mvaddstr(LINES-1, menu_column, \" ^/\");\n    attron(COLOR_PAIR(COLOR_PAIR_MENU_ALTERNATE));\n    addstr(\"|\");\n    attroff(COLOR_PAIR(COLOR_PAIR_MENU_ALTERNATE));\n    addstr(\"^-\");\n    attron(COLOR_PAIR(COLOR_PAIR_MENU_ALTERNATE));\n    addstr(\"|\");\n    attroff(COLOR_PAIR(COLOR_PAIR_MENU_ALTERNATE));\n    addstr(\"^_ \");\n    menu_column += 10;\n    attron(A_REVERSE);\n    mvaddstr(LINES-1, menu_column, \" (un)comment line \");\n    menu_column += 18;\n    attrset(A_NORMAL);\n}\n\nstatic void editorFindMenu(void) {\n    attrset(A_REVERSE);\n    for (int x = 0; x < COLS; ++x)\n      mvaddch(LINES-1, x, ' ');\n    attrset(A_NORMAL);\n    menu_column = 2;\n    draw_menu_item(\"^x\", \"cancel\");\n    draw_menu_item(\"Enter\", \"submit\");\n    draw_menu_item(\"^h\", \"backspace\");\n    draw_menu_item(\"^u\", \"clear\");\n    /* draw_menu_item(\"←|↑\", \"previous\"); */\n    attroff(A_REVERSE);\n    mvaddstr(LINES-1, menu_column, \" ←|↑ \");\n    menu_column += 5;  /* strlen isn't sufficient */\n    attron(A_REVERSE);\n    draw_string_on_menu(\"previous\");\n    /* draw_menu_item(\"↓|→\", \"next\"); */\n    attroff(A_REVERSE);\n    mvaddstr(LINES-1, menu_column, \" ↓|→ \");\n    menu_column += 5;  /* strlen isn't sufficient */\n    attron(A_REVERSE);\n    draw_string_on_menu(\"next\");\n    attrset(A_NORMAL);\n}\n\nstatic void editorGoMenu(void) {\n    attrset(A_REVERSE);\n    for (int x = 0; x < COLS; ++x)\n      mvaddch(LINES-1, x, ' ');\n    attrset(A_NORMAL);\n    menu_column = 2;\n    draw_menu_item(\"^x\", \"cancel\");\n    draw_menu_item(\"Enter\", \"submit\");\n    draw_menu_item(\"^h\", \"backspace\");\n    draw_menu_item(\"^u\", \"clear\");\n    attrset(A_NORMAL);\n}\n\nstatic void editorRefreshScreen(void (*menu_func)(void)) {\n    int y;\n    erow *r;\n    int current_color = -1;\n    curs_set(0);\n    mvaddstr(E.startrow, 0, \"\");\n    clrtobot();\n    attrset(A_NORMAL);\n    /* Draw all line numbers first so they don't mess up curses state later\n     * when rendering lines. */\n    for (y = E.startrow; y < E.endrow; y++) {\n        int filerow = E.rowoff+y-E.startrow;\n\n        if (filerow >= E.numrows) {\n            continue;\n        }\n        mvaddstr(y, 0, \"\");\n        attron(COLOR_PAIR(COLOR_PAIR_FADE));\n        printw(\"%3d \", filerow+1);  // %3d = LINE_NUMBER_SPACE-1\n        attroff(COLOR_PAIR(COLOR_PAIR_FADE));\n    }\n    for (y = E.startrow; y < E.endrow; y++) {\n        int filerow = E.rowoff+y-E.startrow;\n\n        if (filerow >= E.numrows) {\n            continue;\n        }\n\n        r = &E.row[filerow];\n\n        int len = r->rsize - E.coloff;\n        mvaddstr(y, E.startcol, \"\");\n        if (len > 0) {\n            if (len > E.cols) len = E.cols;\n            char *c = r->render+E.coloff;\n            unsigned char *hl = r->hl+E.coloff;\n            int j;\n            for (j = 0; j < len; j++) {\n                if (hl[j] == HL_NONPRINT) {\n                    char sym;\n                    attron(A_REVERSE);\n                    if (c[j] <= 26)\n                        sym = '@'+c[j];\n                    else\n                        sym = '?';\n                    addch(sym);\n                    attroff(A_REVERSE);\n                } else if (hl[j] == HL_NORMAL) {\n                    if (current_color != -1) {\n                        attrset(A_NORMAL);\n                        current_color = -1;\n                    }\n                    addch(c[j]);\n                } else {\n                    int color = editorSyntaxToColorPair(hl[j]);\n                    if (color != current_color) {\n                        attrset(COLOR_PAIR(color));\n                        current_color = color;\n                    }\n                    addch(c[j]);\n                }\n            }\n        }\n    }\n\n    render_previous_error();\n\n    (*menu_func)();\n\n    /* Put cursor at its current position. Note that the horizontal position\n     * at which the cursor is displayed may be different compared to 'E.cx'\n     * because of TABs. */\n    int j;\n    int cx = 0;\n    int filerow = E.rowoff+E.cy;\n    erow *row = (filerow >= E.numrows) ? NULL : &E.row[filerow];\n    if (row) {\n        for (j = E.coloff; j < (E.cx+E.coloff); j++) {\n            if (j < row->size && row->chars[j] == TAB) cx += 7-((cx)%8);\n            cx++;\n        }\n    }\n    mvaddstr(E.cy+E.startrow, cx+LINE_NUMBER_SPACE, \"\");\n    curs_set(1);\n}\n\n/* =============================== Find mode ================================ */\n\n#define KILO_QUERY_LEN 256\n\nstatic void editorFind() {\n    char query[KILO_QUERY_LEN+1] = {0};\n    int qlen = 0;\n    int last_match = -1; /* Last line where a match was found. -1 for none. */\n    int find_next = 0; /* if 1 search next, if -1 search prev. */\n    int saved_hl_line = -1;  /* No saved HL */\n    char *saved_hl = NULL;\n\n#define FIND_RESTORE_HL do { \\\n    if (saved_hl) { \\\n        memcpy(E.row[saved_hl_line].hl,saved_hl, E.row[saved_hl_line].rsize); \\\n        free(saved_hl); \\\n        saved_hl = NULL; \\\n    } \\\n} while (0)\n\n    /* Save the cursor position in order to restore it later. */\n    int saved_cx = E.cx, saved_cy = E.cy;\n    int saved_coloff = E.coloff, saved_rowoff = E.rowoff;\n\n    while(1) {\n        editorRefreshScreen(editorFindMenu);\n        mvprintw(LINES-2, 0, \"Find: %s\", query);\n\n        int c = getch();\n        if (c == KEY_BACKSPACE || c == DELETE || c == CTRL_H) {\n            if (qlen != 0) query[--qlen] = '\\0';\n            last_match = -1;\n        } else if (c == CTRL_X || c == ENTER) {\n            if (c == CTRL_X) {\n                E.cx = saved_cx; E.cy = saved_cy;\n                E.coloff = saved_coloff; E.rowoff = saved_rowoff;\n            }\n            FIND_RESTORE_HL;\n            return;\n        } else if (c == CTRL_U) {\n            qlen = 0;\n            query[qlen] = '\\0';\n        } else if (c == KEY_RIGHT || c == KEY_DOWN) {\n            find_next = 1;\n        } else if (c == KEY_LEFT || c == KEY_UP) {\n            find_next = -1;\n        } else if (isprint(c)) {\n            if (qlen < KILO_QUERY_LEN) {\n                query[qlen++] = c;\n                query[qlen] = '\\0';\n                last_match = -1;\n            }\n        }\n\n        /* Search occurrence. */\n        if (last_match == -1) find_next = 1;\n        if (find_next) {\n            char *match = NULL;\n            int match_offset = 0;\n            int i, current = last_match;\n\n            for (i = 0; i < E.numrows; i++) {\n                current += find_next;\n                if (current == -1) current = E.numrows-1;\n                else if (current == E.numrows) current = 0;\n                match = strstr(E.row[current].render,query);\n                if (match) {\n                    match_offset = match-E.row[current].render;\n                    break;\n                }\n            }\n            find_next = 0;\n\n            /* Highlight */\n            FIND_RESTORE_HL;\n\n            if (match) {\n                erow *row = &E.row[current];\n                last_match = current;\n                if (row->hl) {\n                    saved_hl_line = current;\n                    saved_hl = malloc(row->rsize);\n                    memcpy(saved_hl,row->hl,row->rsize);\n                    memset(row->hl+match_offset,HL_MATCH,qlen);\n                }\n                E.cy = 0;\n                E.cx = match_offset;\n                E.rowoff = current;\n                E.coloff = 0;\n                /* Scroll horizontally as needed. */\n                if (E.cx > E.cols) {\n                    int diff = E.cx - E.cols;\n                    E.cx -= diff;\n                    E.coloff += diff;\n                }\n            }\n        }\n    }\n}\n\n/* ========================= Editor events handling  ======================== */\n\nstatic int editorAtStartOfLine() {\n    return E.coloff == 0 && E.cx == 0;\n}\n\n/* Handle cursor position change because arrow keys were pressed. */\nstatic void editorMoveCursor(int key) {\n    int filerow = E.rowoff+E.cy;\n    int filecol = E.coloff+E.cx;\n    int rowlen;\n    erow *row = (filerow >= E.numrows) ? NULL : &E.row[filerow];\n\n    switch(key) {\n    case KEY_LEFT:\n        if (E.cx == 0) {\n            if (E.coloff) {\n                E.coloff--;\n            } else {\n                if (filerow > 0) {\n                    E.cy--;\n                    E.cx = E.row[filerow-1].size;\n                    if (E.cx > E.cols-1) {\n                        E.coloff = E.cx-E.cols+1;\n                        E.cx = E.cols-1;\n                    }\n                }\n            }\n        } else {\n            E.cx -= 1;\n        }\n        break;\n    case KEY_RIGHT:\n        if (row && filecol < row->size) {\n            if (E.cx == E.cols-1) {\n                E.coloff++;\n            } else {\n                E.cx += 1;\n            }\n        } else if (row && filecol == row->size) {\n            E.cx = 0;\n            E.coloff = 0;\n            if (E.startrow + E.cy == E.endrow-1) {\n                E.rowoff++;\n            } else {\n                E.cy += 1;\n            }\n        }\n        break;\n    case KEY_UP:\n        if (E.cy == 0) {\n            if (E.rowoff) E.rowoff--;\n        } else {\n            E.cy -= 1;\n        }\n        break;\n    case KEY_DOWN:\n        if (filerow < E.numrows-1) {\n            if (E.startrow + E.cy == E.endrow-1) {\n                E.rowoff++;\n            } else {\n                E.cy += 1;\n            }\n        }\n        break;\n    }\n    /* Fix cx if the current line has not enough chars. */\n    filerow = E.rowoff+E.cy;\n    filecol = E.coloff+E.cx;\n    row = (filerow >= E.numrows) ? NULL : &E.row[filerow];\n    rowlen = row ? row->size : 0;\n    if (filecol > rowlen) {\n        E.cx -= filecol-rowlen;\n        if (E.cx < 0) {\n            E.coloff += E.cx;\n            E.cx = 0;\n        }\n    }\n}\n\nstatic int identifier_char(char c) {\n    /* keep sync'd with llex, with one exception for prose */\n    return isalnum(c) || c == '_' || c == ':';\n}\n\nstatic void word_at_cursor(char* out, int capacity) {\n    erow* row = &E.row[E.rowoff + E.cy];\n    int cidx = E.coloff + E.cx;\n    int len = 0;\n    memset(out, 0, capacity);\n    if (row == NULL) return;\n    /* scan back to first identifier char */\n    while (cidx > 0) {\n        --cidx;\n        if (!identifier_char(row->chars[cidx])) {\n            ++cidx;\n            break;\n        }\n    }\n    /* now scan forward */\n    for (len = 0; cidx+len < row->size; ++len) {\n        if (!identifier_char(row->chars[cidx+len]))\n            break;\n    }\n    if (len < capacity)\n        strncpy(out, &row->chars[cidx], len);\n}\n\n/* Jump to some definition. */\nextern char Current_definition[];\n#define CURRENT_DEFINITION_LEN 256\nstatic void editorGo(lua_State* L) {\n    char query[CURRENT_DEFINITION_LEN+1] = {0};\n    int qlen = 0;\n\n    if (strlen(Current_definition) > 0) {\n      /* We're currently editing a definition. Save it. */\n      editorSaveToDisk();\n      load_editor_buffer_to_current_definition_in_image_and_reload(L);\n    }\n\n    word_at_cursor(query, CURRENT_DEFINITION_LEN);\n    qlen = strlen(query);\n\n    while(1) {\n        editorRefreshScreen(editorGoMenu);\n        mvaddstr(E.endrow-1, 0, \"\");\n        clrtoeol();\n        mvprintw(E.endrow-1, 0, \"Go to: %s\", query);\n\n        int c = getch();\n        if (c == KEY_BACKSPACE || c == DELETE || c == CTRL_H) {\n            if (qlen != 0) query[--qlen] = '\\0';\n        } else if (c == CTRL_X || c == ENTER) {\n            if (c == ENTER) {\n                save_to_current_definition_and_editor_buffer(L, query);\n                clearEditor();\n                if (starts_with(query, \"doc:\"))\n                    E.syntax = &ProseSyntax;\n                else\n                    E.syntax = &LuaSyntax;\n                editorOpen(\"teliva_editor_buffer\");\n                attrset(A_NORMAL);\n                clear();\n                draw_current_definition_name_and_callers(L);\n            }\n            return;\n        } else if (c == CTRL_U) {\n            qlen = 0;\n            query[qlen] = '\\0';\n        } else if (isprint(c)) {\n            if (qlen < CURRENT_DEFINITION_LEN) {\n                query[qlen++] = c;\n                query[qlen] = '\\0';\n            }\n        }\n    }\n}\n\n/* Process events arriving from the standard input, which is, the user\n * is typing stuff on the terminal. */\nstatic int Quit = 0;\nstatic int Back_to_big_picture = 0;\nstatic void editorProcessKeypress2(int c) {\n    switch(c) {\n    case ENTER:\n        {\n            editorInsertNewline();\n            /* auto-indent */\n            erow* prevrow = &E.row[E.rowoff + E.cy - 1];\n            for (int x = 0; x < prevrow->size && prevrow->chars[x] == ' '; ++x)\n                editorInsertChar(' ');\n        }\n        break;\n    case CTRL_C:\n        if (Previous_error != NULL)\n            exit(1);\n        break;\n    case CTRL_X:\n        /* Save and quit. */\n        editorSaveToDisk();\n        save_editor_state(E.rowoff, E.coloff, E.cy, E.cx);\n        Quit = 1;\n        break;\n    case CTRL_F:\n        editorFind();\n        break;\n    case KEY_BACKSPACE:\n    case DELETE:\n    case CTRL_H:\n        editorDelChar();\n        break;\n    case KEY_NPAGE:\n    case KEY_PPAGE:\n        if (c == KEY_PPAGE && E.cy != 0)\n            E.cy = 0;\n        else if (c == KEY_NPAGE && E.startrow + E.cy != E.endrow-1)\n            E.cy = E.endrow-1-E.startrow;\n        {\n            int times = E.endrow-E.startrow;\n            while(times--)\n                editorMoveCursor(c == KEY_PPAGE ? KEY_UP : KEY_DOWN);\n        }\n        break;\n    case CTRL_A:\n        while (!editorAtStartOfLine())\n            editorMoveCursor(KEY_LEFT);\n        break;\n    case CTRL_L:\n        while (1) {\n            editorMoveCursor(KEY_RIGHT);\n            if (editorAtStartOfLine()) {\n                editorMoveCursor(KEY_LEFT);\n                break;\n            }\n        }\n        break;\n    case CTRL_U:\n        while (!editorAtStartOfLine())\n            editorDelChar();\n        break;\n    case CTRL_K:\n        while (1) {\n            editorMoveCursor(KEY_RIGHT);\n            if (editorAtStartOfLine()) {\n                editorMoveCursor(KEY_LEFT);\n                break;\n            }\n            editorDelChar();\n        }\n        break;\n    case CTRL_SLASH:  /* same as CTRL_UNDERSCORE */\n        if (starts_with(E.row[E.rowoff+E.cy].chars, \"--? \"))\n            editorUncommentCursorRow();\n        else\n            editorCommentCursorRow();\n        break;\n    case KEY_UP:\n    case KEY_DOWN:\n    case KEY_LEFT:\n    case KEY_RIGHT:\n        editorMoveCursor(c);\n        break;\n    case TAB:\n        /* insert 2 spaces */\n        editorInsertChar(' ');\n        editorInsertChar(' ');\n        break;\n    default:\n        if (c >= ' ')\n            editorInsertChar(c);\n        break;\n    }\n}\n\nstatic void editorProcessKeypress(lua_State* L) {\n    int c = getch();\n//?     mvprintw(LINES-3, 60, \"key: %d\\n\", c);\n//?     getch();\n    switch(c) {\n    case CTRL_G:\n        /* Go to a different definition. */\n        editorGo(L);\n        break;\n    case CTRL_B:\n        /* Go to big-picture view. */\n        editorSaveToDisk();\n        Quit = 1;\n        Back_to_big_picture = 1;\n        break;\n    default:\n        editorProcessKeypress2(c);\n    }\n}\n\nstatic void initEditor(void) {\n    E.cx = 0;\n    E.cy = 0;\n    E.rowoff = 0;\n    E.coloff = 0;\n    E.numrows = 0;\n    E.row = NULL;\n    E.dirty = 0;\n    E.filename = NULL;\n}\n\n/* return true if user chose to back into the big picture view */\nint edit(lua_State* L, char* filename, char* definition_name) {\n    Quit = 0;\n    Back_to_big_picture = 0;\n    initEditor();\n    if (starts_with(definition_name, \"doc:\"))\n        E.syntax = &ProseSyntax;\n    else\n        E.syntax = &LuaSyntax;\n    editorOpen(filename);\n    attrset(A_NORMAL);\n    clear();\n    draw_current_definition_name_and_callers(L);\n    while(!Quit) {\n        /* update on resize */\n        E.startcol = LINE_NUMBER_SPACE;\n        E.cols = COLS-LINE_NUMBER_SPACE;\n        E.startrow = CALLERS_SPACE;\n        E.endrow = LINES-MENU_SPACE;\n        editorRefreshScreen(editorMenu);\n        editorProcessKeypress(L);\n    }\n    return Back_to_big_picture;\n}\n\n/* Like editFrom(), but no highlighting, no callers. */\nint editProse(lua_State* L, char* filename) {\n    Quit = 0;\n    Back_to_big_picture = 0;\n    initEditor();\n    E.syntax = &ProseSyntax;\n    editorOpen(filename);\n    attrset(A_NORMAL);\n    clear();\n    while(!Quit) {\n        /* update on resize */\n        E.startcol = LINE_NUMBER_SPACE;\n        E.cols = COLS-LINE_NUMBER_SPACE;\n        E.startrow = CALLERS_SPACE;\n        E.endrow = LINES-MENU_SPACE;\n        editorRefreshScreen(editorMenu);\n        editorProcessKeypress(L);\n    }\n    return Back_to_big_picture;\n}\n\nstatic void editorNonCodeMenu(void) {\n    attrset(A_REVERSE);\n    for (int x = 0; x < COLS; ++x)\n      mvaddch(LINES-1, x, ' ');\n    attrset(A_NORMAL);\n    menu_column = 2;\n    draw_menu_item(\"^x\", \"back\");\n    if (Previous_error != NULL) {\n      attron(A_BOLD);\n      draw_menu_item(\"^c\", \"abort\");\n      attroff(A_BOLD);\n    }\n    draw_menu_item(\"^f\", \"find\");\n    draw_menu_item(\"^h\", \"backspace\");\n    draw_menu_item(\"^l\", \"end of line\");\n    /* draw_menu_item(\"^/|^-|^_\", \"(un)comment line\"); */\n    attroff(A_REVERSE);\n    mvaddstr(LINES-1, menu_column, \" ^/\");\n    attron(COLOR_PAIR(COLOR_PAIR_MENU_ALTERNATE));\n    addstr(\"|\");\n    attroff(COLOR_PAIR(COLOR_PAIR_MENU_ALTERNATE));\n    addstr(\"^-\");\n    attron(COLOR_PAIR(COLOR_PAIR_MENU_ALTERNATE));\n    addstr(\"|\");\n    attroff(COLOR_PAIR(COLOR_PAIR_MENU_ALTERNATE));\n    addstr(\"^_ \");\n    menu_column += 10;\n    attron(A_REVERSE);\n    mvaddstr(LINES-1, menu_column, \" (un)comment line \");\n    menu_column += 18;\n    attrset(A_NORMAL);\n}\n\nvoid editNonCode(char* filename) {\n    Quit = 0;\n    Back_to_big_picture = 0;\n    initEditor();\n    editorOpen(filename);\n    while(!Quit) {\n        /* update on resize */\n        E.startcol = LINE_NUMBER_SPACE;\n        E.cols = COLS-LINE_NUMBER_SPACE;\n        E.startrow = CALLERS_SPACE;\n        E.endrow = LINES-MENU_SPACE;\n        editorRefreshScreen(editorNonCodeMenu);\n        int c = getch();\n        editorProcessKeypress2(c);\n    }\n}\n\n#define MIN(x, y) ((x) < (y) ? (x) : (y))\n\nvoid print_file_permission_suggestions(int row);\nvoid editFilePermissions(char* filename) {\n    Quit = 0;\n    Back_to_big_picture = 0;\n    initEditor();\n    E.syntax = &LuaSyntax;\n    E.startcol = LINE_NUMBER_SPACE;\n    E.startrow = 1;  /* space for function header */\n    E.endrow = 10;  /* nudge people to keep function short */\n    editorOpen(filename);\n    while(!Quit) {\n        /* update on resize */\n        E.cols = COLS-LINE_NUMBER_SPACE;\n        editorRefreshScreen(editorNonCodeMenu);\n        int y, x;\n        getyx(stdscr, y, x);\n        mvaddstr(0, 0, \"function file_operation_permitted(filename, is_write)\");\n        int past_end_row = MIN(E.startrow + E.numrows, E.endrow);\n        mvaddstr(past_end_row, 0, \"end\");\n        attrset(COLOR_PAIR(COLOR_PAIR_LUA_COMMENT));\n        print_file_permission_suggestions(past_end_row+2);\n        attrset(A_NORMAL);\n        mvaddstr(y, x, \"\");\n        int c = getch();\n        editorProcessKeypress2(c);\n    }\n}\n\n/* return true if user chose to back into the big picture view */\nint editFrom(lua_State* L, char* filename, char* definition_name, int rowoff, int coloff, int cy, int cx) {\n    Quit = 0;\n    Back_to_big_picture = 0;\n    initEditor();\n    if (starts_with(definition_name, \"doc:\"))\n        E.syntax = &ProseSyntax;\n    else\n        E.syntax = &LuaSyntax;\n    E.rowoff = rowoff;\n    E.coloff = coloff;\n    E.cy = cy;\n    E.cx = cx;\n    editorOpen(filename);\n    attrset(A_NORMAL);\n    clear();\n    draw_current_definition_name_and_callers(L);\n    while(!Quit) {\n        /* update on resize */\n        E.startcol = LINE_NUMBER_SPACE;\n        E.cols = COLS-LINE_NUMBER_SPACE;\n        E.startrow = CALLERS_SPACE;\n        E.endrow = LINES-MENU_SPACE;\n        editorRefreshScreen(editorMenu);\n        editorProcessKeypress(L);\n    }\n    return Back_to_big_picture;\n}\n\n/* Like editFrom(), but no highlighting, no callers. */\nint editProseFrom(lua_State* L, char* filename, int rowoff, int coloff, int cy, int cx) {\n    Quit = 0;\n    Back_to_big_picture = 0;\n    initEditor();\n    E.syntax = &ProseSyntax;\n    E.rowoff = rowoff;\n    E.coloff = coloff;\n    E.cy = cy;\n    E.cx = cx;\n    editorOpen(filename);\n    attrset(A_NORMAL);\n    clear();\n    while(!Quit) {\n        /* update on resize */\n        E.startcol = LINE_NUMBER_SPACE;\n        E.cols = COLS-LINE_NUMBER_SPACE;\n        E.startrow = CALLERS_SPACE;\n        E.endrow = LINES-MENU_SPACE;\n        editorRefreshScreen(editorMenu);\n        editorProcessKeypress(L);\n    }\n    return Back_to_big_picture;\n}\n\nint resumeEdit(lua_State* L) {\n    Quit = 0;\n    Back_to_big_picture = 0;\n    attrset(A_NORMAL);\n    clear();\n    draw_current_definition_name_and_callers(L);\n    while(!Quit) {\n        /* update on resize */\n        E.startcol = LINE_NUMBER_SPACE;\n        E.cols = COLS-LINE_NUMBER_SPACE;\n        E.startrow = CALLERS_SPACE;\n        E.endrow = LINES-MENU_SPACE;\n        editorRefreshScreen(editorMenu);\n        editorProcessKeypress(L);\n    }\n    return Back_to_big_picture;\n}\n\nvoid resumeNonCodeEdit() {\n    Quit = 0;\n    Back_to_big_picture = 0;\n    while(!Quit) {\n        /* update on resize */\n        E.startcol = LINE_NUMBER_SPACE;\n        E.cols = COLS-LINE_NUMBER_SPACE;\n        E.startrow = CALLERS_SPACE;\n        E.endrow = LINES-MENU_SPACE;\n        editorRefreshScreen(editorNonCodeMenu);\n        int c = getch();\n        editorProcessKeypress2(c);\n    }\n}\n\nvoid resumeFilePermissionsEdit() {\n    Quit = 0;\n    Back_to_big_picture = 0;\n    E.startcol = LINE_NUMBER_SPACE;\n    E.startrow = 1;  /* space for function header */\n    E.endrow = 10;  /* nudge people to keep function short */\n    while(!Quit) {\n        /* update on resize */\n        E.cols = COLS-LINE_NUMBER_SPACE;\n        editorRefreshScreen(editorNonCodeMenu);\n        int y, x;\n        getyx(stdscr, y, x);\n        mvaddstr(0, 0, \"function file_operation_permitted(filename, is_write)\");\n        mvaddstr(MIN(E.startrow + E.numrows, E.endrow), 0, \"end\");\n        mvaddstr(y, x, \"\");\n        int c = getch();\n        editorProcessKeypress2(c);\n    }\n}\n\n/* vim:tabstop=4:shiftwidth=0:expandtab:softtabstop=-1\n */\n"
  },
  {
    "path": "src/lapi.c",
    "content": "/*\n** $Id: lapi.c,v 2.55.1.5 2008/07/04 18:41:18 roberto Exp $\n** Lua API\n** See Copyright Notice in lua.h\n*/\n\n\n#include <assert.h>\n#include <math.h>\n#include <stdarg.h>\n#include <string.h>\n\n#define lapi_c\n#define LUA_CORE\n\n#include \"lua.h\"\n\n#include \"lapi.h\"\n#include \"ldebug.h\"\n#include \"ldo.h\"\n#include \"lfunc.h\"\n#include \"lgc.h\"\n#include \"lmem.h\"\n#include \"lobject.h\"\n#include \"lstate.h\"\n#include \"lstring.h\"\n#include \"ltable.h\"\n#include \"ltm.h\"\n#include \"lundump.h\"\n#include \"lvm.h\"\n\n\n\nconst char lua_ident[] =\n  \"$Lua: \" LUA_RELEASE \" \" LUA_COPYRIGHT \" $\\n\"\n  \"$Authors: \" LUA_AUTHORS \" $\\n\"\n  \"$URL: www.lua.org $\\n\";\n\n\n\n#define api_checknelems(L, n)\tapi_check(L, (n) <= (L->top - L->base))\n\n#define api_checkvalidindex(L, i)\tapi_check(L, (i) != luaO_nilobject)\n\n#define api_incr_top(L)   {api_check(L, L->top < L->ci->top); L->top++;}\n\n\n\nstatic TValue *index2adr (lua_State *L, int idx) {\n  if (idx > 0) {\n    TValue *o = L->base + (idx - 1);\n    api_check(L, idx <= L->ci->top - L->base);\n    if (o >= L->top) return cast(TValue *, luaO_nilobject);\n    else return o;\n  }\n  else if (idx > LUA_REGISTRYINDEX) {\n    api_check(L, idx != 0 && -idx <= L->top - L->base);\n    return L->top + idx;\n  }\n  else switch (idx) {  /* pseudo-indices */\n    case LUA_REGISTRYINDEX: return registry(L);\n    case LUA_ENVIRONINDEX: {\n      Closure *func = curr_func(L);\n      sethvalue(L, &L->env, func->c.env);\n      return &L->env;\n    }\n    case LUA_GLOBALSINDEX: return gt(L);\n    default: {\n      Closure *func = curr_func(L);\n      idx = LUA_GLOBALSINDEX - idx;\n      return (idx <= func->c.nupvalues)\n                ? &func->c.upvalue[idx-1]\n                : cast(TValue *, luaO_nilobject);\n    }\n  }\n}\n\n\nstatic Table *getcurrenv (lua_State *L) {\n  if (L->ci == L->base_ci)  /* no enclosing function? */\n    return hvalue(gt(L));  /* use global table as environment */\n  else {\n    Closure *func = curr_func(L);\n    return func->c.env;\n  }\n}\n\n\nvoid luaA_pushobject (lua_State *L, const TValue *o) {\n  setobj2s(L, L->top, o);\n  api_incr_top(L);\n}\n\n\nLUA_API int lua_checkstack (lua_State *L, int size) {\n  int res = 1;\n  lua_lock(L);\n  if (size > LUAI_MAXCSTACK || (L->top - L->base + size) > LUAI_MAXCSTACK)\n    res = 0;  /* stack overflow */\n  else if (size > 0) {\n    luaD_checkstack(L, size);\n    if (L->ci->top < L->top + size)\n      L->ci->top = L->top + size;\n  }\n  lua_unlock(L);\n  return res;\n}\n\n\nLUA_API void lua_xmove (lua_State *from, lua_State *to, int n) {\n  int i;\n  if (from == to) return;\n  lua_lock(to);\n  api_checknelems(from, n);\n  api_check(from, G(from) == G(to));\n  api_check(from, to->ci->top - to->top >= n);\n  from->top -= n;\n  for (i = 0; i < n; i++) {\n    setobj2s(to, to->top++, from->top + i);\n  }\n  lua_unlock(to);\n}\n\n\nLUA_API void lua_setlevel (lua_State *from, lua_State *to) {\n  to->nCcalls = from->nCcalls;\n}\n\n\nLUA_API lua_CFunction lua_atpanic (lua_State *L, lua_CFunction panicf) {\n  lua_CFunction old;\n  lua_lock(L);\n  old = G(L)->panic;\n  G(L)->panic = panicf;\n  lua_unlock(L);\n  return old;\n}\n\n\nLUA_API lua_State *lua_newthread (lua_State *L) {\n  lua_State *L1;\n  lua_lock(L);\n  luaC_checkGC(L);\n  L1 = luaE_newthread(L);\n  setthvalue(L, L->top, L1);\n  api_incr_top(L);\n  lua_unlock(L);\n  return L1;\n}\n\n\n\n/*\n** basic stack manipulation\n*/\n\n\nLUA_API int lua_gettop (lua_State *L) {\n  return cast_int(L->top - L->base);\n}\n\n\nLUA_API void lua_settop (lua_State *L, int idx) {\n  lua_lock(L);\n  if (idx >= 0) {\n    api_check(L, idx <= L->stack_last - L->base);\n    while (L->top < L->base + idx)\n      setnilvalue(L->top++);\n    L->top = L->base + idx;\n  }\n  else {\n    api_check(L, -(idx+1) <= (L->top - L->base));\n    L->top += idx+1;  /* `subtract' index (index is negative) */\n  }\n  lua_unlock(L);\n}\n\n\nLUA_API void lua_remove (lua_State *L, int idx) {\n  StkId p;\n  lua_lock(L);\n  p = index2adr(L, idx);\n  api_checkvalidindex(L, p);\n  while (++p < L->top) setobjs2s(L, p-1, p);\n  L->top--;\n  lua_unlock(L);\n}\n\n\nLUA_API void lua_insert (lua_State *L, int idx) {\n  StkId p;\n  StkId q;\n  lua_lock(L);\n  p = index2adr(L, idx);\n  api_checkvalidindex(L, p);\n  for (q = L->top; q>p; q--) setobjs2s(L, q, q-1);\n  setobjs2s(L, p, L->top);\n  lua_unlock(L);\n}\n\n\nLUA_API void lua_replace (lua_State *L, int idx) {\n  StkId o;\n  lua_lock(L);\n  /* explicit test for incompatible code */\n  if (idx == LUA_ENVIRONINDEX && L->ci == L->base_ci)\n    luaG_runerror(L, \"no calling environment\");\n  api_checknelems(L, 1);\n  o = index2adr(L, idx);\n  api_checkvalidindex(L, o);\n  if (idx == LUA_ENVIRONINDEX) {\n    Closure *func = curr_func(L);\n    api_check(L, ttistable(L->top - 1)); \n    func->c.env = hvalue(L->top - 1);\n    luaC_barrier(L, func, L->top - 1);\n  }\n  else {\n    setobj(L, o, L->top - 1);\n    if (idx < LUA_GLOBALSINDEX)  /* function upvalue? */\n      luaC_barrier(L, curr_func(L), L->top - 1);\n  }\n  L->top--;\n  lua_unlock(L);\n}\n\n\nLUA_API void lua_pushvalue (lua_State *L, int idx) {\n  lua_lock(L);\n  setobj2s(L, L->top, index2adr(L, idx));\n  api_incr_top(L);\n  lua_unlock(L);\n}\n\n\n\n/*\n** access functions (stack -> C)\n*/\n\n\nLUA_API int lua_type (lua_State *L, int idx) {\n  StkId o = index2adr(L, idx);\n  return (o == luaO_nilobject) ? LUA_TNONE : ttype(o);\n}\n\n\nLUA_API const char *lua_typename (lua_State *L, int t) {\n  UNUSED(L);\n  return (t == LUA_TNONE) ? \"no value\" : luaT_typenames[t];\n}\n\n\nLUA_API int lua_iscfunction (lua_State *L, int idx) {\n  StkId o = index2adr(L, idx);\n  return iscfunction(o);\n}\n\n\nLUA_API int lua_isnumber (lua_State *L, int idx) {\n  TValue n;\n  const TValue *o = index2adr(L, idx);\n  return tonumber(o, &n);\n}\n\n\nLUA_API int lua_isstring (lua_State *L, int idx) {\n  int t = lua_type(L, idx);\n  return (t == LUA_TSTRING || t == LUA_TNUMBER);\n}\n\n\nLUA_API int lua_isuserdata (lua_State *L, int idx) {\n  const TValue *o = index2adr(L, idx);\n  return (ttisuserdata(o) || ttislightuserdata(o));\n}\n\n\nLUA_API int lua_rawequal (lua_State *L, int index1, int index2) {\n  StkId o1 = index2adr(L, index1);\n  StkId o2 = index2adr(L, index2);\n  return (o1 == luaO_nilobject || o2 == luaO_nilobject) ? 0\n         : luaO_rawequalObj(o1, o2);\n}\n\n\nLUA_API int lua_equal (lua_State *L, int index1, int index2) {\n  StkId o1, o2;\n  int i;\n  lua_lock(L);  /* may call tag method */\n  o1 = index2adr(L, index1);\n  o2 = index2adr(L, index2);\n  i = (o1 == luaO_nilobject || o2 == luaO_nilobject) ? 0 : equalobj(L, o1, o2);\n  lua_unlock(L);\n  return i;\n}\n\n\nLUA_API int lua_lessthan (lua_State *L, int index1, int index2) {\n  StkId o1, o2;\n  int i;\n  lua_lock(L);  /* may call tag method */\n  o1 = index2adr(L, index1);\n  o2 = index2adr(L, index2);\n  i = (o1 == luaO_nilobject || o2 == luaO_nilobject) ? 0\n       : luaV_lessthan(L, o1, o2);\n  lua_unlock(L);\n  return i;\n}\n\n\n\nLUA_API lua_Number lua_tonumber (lua_State *L, int idx) {\n  TValue n;\n  const TValue *o = index2adr(L, idx);\n  if (tonumber(o, &n))\n    return nvalue(o);\n  else\n    return 0;\n}\n\n\nLUA_API lua_Integer lua_tointeger (lua_State *L, int idx) {\n  TValue n;\n  const TValue *o = index2adr(L, idx);\n  if (tonumber(o, &n)) {\n    lua_Integer res;\n    lua_Number num = nvalue(o);\n    lua_number2integer(res, num);\n    return res;\n  }\n  else\n    return 0;\n}\n\n\nLUA_API int lua_toboolean (lua_State *L, int idx) {\n  const TValue *o = index2adr(L, idx);\n  return !l_isfalse(o);\n}\n\n\nLUA_API const char *lua_tolstring (lua_State *L, int idx, size_t *len) {\n  StkId o = index2adr(L, idx);\n  if (!ttisstring(o)) {\n    lua_lock(L);  /* `luaV_tostring' may create a new string */\n    if (!luaV_tostring(L, o)) {  /* conversion failed? */\n      if (len != NULL) *len = 0;\n      lua_unlock(L);\n      return NULL;\n    }\n    luaC_checkGC(L);\n    o = index2adr(L, idx);  /* previous call may reallocate the stack */\n    lua_unlock(L);\n  }\n  if (len != NULL) *len = tsvalue(o)->len;\n  return svalue(o);\n}\n\n\nLUA_API size_t lua_objlen (lua_State *L, int idx) {\n  StkId o = index2adr(L, idx);\n  switch (ttype(o)) {\n    case LUA_TSTRING: return tsvalue(o)->len;\n    case LUA_TUSERDATA: return uvalue(o)->len;\n    case LUA_TTABLE: return luaH_getn(hvalue(o));\n    case LUA_TNUMBER: {\n      size_t l;\n      lua_lock(L);  /* `luaV_tostring' may create a new string */\n      l = (luaV_tostring(L, o) ? tsvalue(o)->len : 0);\n      lua_unlock(L);\n      return l;\n    }\n    default: return 0;\n  }\n}\n\n\nLUA_API lua_CFunction lua_tocfunction (lua_State *L, int idx) {\n  StkId o = index2adr(L, idx);\n  return (!iscfunction(o)) ? NULL : clvalue(o)->c.f;\n}\n\n\nLUA_API void *lua_touserdata (lua_State *L, int idx) {\n  StkId o = index2adr(L, idx);\n  switch (ttype(o)) {\n    case LUA_TUSERDATA: return (rawuvalue(o) + 1);\n    case LUA_TLIGHTUSERDATA: return pvalue(o);\n    default: return NULL;\n  }\n}\n\n\nLUA_API lua_State *lua_tothread (lua_State *L, int idx) {\n  StkId o = index2adr(L, idx);\n  return (!ttisthread(o)) ? NULL : thvalue(o);\n}\n\n\nLUA_API const void *lua_topointer (lua_State *L, int idx) {\n  StkId o = index2adr(L, idx);\n  switch (ttype(o)) {\n    case LUA_TTABLE: return hvalue(o);\n    case LUA_TFUNCTION: return clvalue(o);\n    case LUA_TTHREAD: return thvalue(o);\n    case LUA_TUSERDATA:\n    case LUA_TLIGHTUSERDATA:\n      return lua_touserdata(L, idx);\n    default: return NULL;\n  }\n}\n\n\n\n/*\n** push functions (C -> stack)\n*/\n\n\nLUA_API void lua_pushnil (lua_State *L) {\n  lua_lock(L);\n  setnilvalue(L->top);\n  api_incr_top(L);\n  lua_unlock(L);\n}\n\n\nLUA_API void lua_pushnumber (lua_State *L, lua_Number n) {\n  lua_lock(L);\n  setnvalue(L->top, n);\n  api_incr_top(L);\n  lua_unlock(L);\n}\n\n\nLUA_API void lua_pushinteger (lua_State *L, lua_Integer n) {\n  lua_lock(L);\n  setnvalue(L->top, cast_num(n));\n  api_incr_top(L);\n  lua_unlock(L);\n}\n\n\nLUA_API void lua_pushlstring (lua_State *L, const char *s, size_t len) {\n  lua_lock(L);\n  luaC_checkGC(L);\n  setsvalue2s(L, L->top, luaS_newlstr(L, s, len));\n  api_incr_top(L);\n  lua_unlock(L);\n}\n\n\nLUA_API void lua_pushstring (lua_State *L, const char *s) {\n  if (s == NULL)\n    lua_pushnil(L);\n  else\n    lua_pushlstring(L, s, strlen(s));\n}\n\n\nLUA_API const char *lua_pushvfstring (lua_State *L, const char *fmt,\n                                      va_list argp) {\n  const char *ret;\n  lua_lock(L);\n  luaC_checkGC(L);\n  ret = luaO_pushvfstring(L, fmt, argp);\n  lua_unlock(L);\n  return ret;\n}\n\n\nLUA_API const char *lua_pushfstring (lua_State *L, const char *fmt, ...) {\n  const char *ret;\n  va_list argp;\n  lua_lock(L);\n  luaC_checkGC(L);\n  va_start(argp, fmt);\n  ret = luaO_pushvfstring(L, fmt, argp);\n  va_end(argp);\n  lua_unlock(L);\n  return ret;\n}\n\n\nLUA_API void lua_pushcclosure (lua_State *L, lua_CFunction fn, int n) {\n  Closure *cl;\n  lua_lock(L);\n  luaC_checkGC(L);\n  api_checknelems(L, n);\n  cl = luaF_newCclosure(L, n, getcurrenv(L));\n  cl->c.f = fn;\n  L->top -= n;\n  while (n--)\n    setobj2n(L, &cl->c.upvalue[n], L->top+n);\n  setclvalue(L, L->top, cl);\n  lua_assert(iswhite(obj2gco(cl)));\n  api_incr_top(L);\n  lua_unlock(L);\n}\n\n\nLUA_API void lua_pushboolean (lua_State *L, int b) {\n  lua_lock(L);\n  setbvalue(L->top, (b != 0));  /* ensure that true is 1 */\n  api_incr_top(L);\n  lua_unlock(L);\n}\n\n\nLUA_API void lua_pushlightuserdata (lua_State *L, void *p) {\n  lua_lock(L);\n  setpvalue(L->top, p);\n  api_incr_top(L);\n  lua_unlock(L);\n}\n\n\nLUA_API int lua_pushthread (lua_State *L) {\n  lua_lock(L);\n  setthvalue(L, L->top, L);\n  api_incr_top(L);\n  lua_unlock(L);\n  return (G(L)->mainthread == L);\n}\n\n\n\n/*\n** get functions (Lua -> stack)\n*/\n\n\nLUA_API void lua_gettable (lua_State *L, int idx) {\n  StkId t;\n  lua_lock(L);\n  t = index2adr(L, idx);\n  api_checkvalidindex(L, t);\n  luaV_gettable(L, t, L->top - 1, L->top - 1);\n  lua_unlock(L);\n}\n\n\nLUA_API void lua_getfield (lua_State *L, int idx, const char *k) {\n  StkId t;\n  TValue key;\n  lua_lock(L);\n  t = index2adr(L, idx);\n  api_checkvalidindex(L, t);\n  setsvalue(L, &key, luaS_new(L, k));\n  luaV_gettable(L, t, &key, L->top);\n  api_incr_top(L);\n  lua_unlock(L);\n}\n\n\nLUA_API void lua_rawget (lua_State *L, int idx) {\n  StkId t;\n  lua_lock(L);\n  t = index2adr(L, idx);\n  api_check(L, ttistable(t));\n  setobj2s(L, L->top - 1, luaH_get(hvalue(t), L->top - 1));\n  lua_unlock(L);\n}\n\n\nLUA_API void lua_rawgeti (lua_State *L, int idx, int n) {\n  StkId o;\n  lua_lock(L);\n  o = index2adr(L, idx);\n  api_check(L, ttistable(o));\n  setobj2s(L, L->top, luaH_getnum(hvalue(o), n));\n  api_incr_top(L);\n  lua_unlock(L);\n}\n\n\nLUA_API void lua_createtable (lua_State *L, int narray, int nrec) {\n  lua_lock(L);\n  luaC_checkGC(L);\n  sethvalue(L, L->top, luaH_new(L, narray, nrec));\n  api_incr_top(L);\n  lua_unlock(L);\n}\n\n\nLUA_API int lua_getmetatable (lua_State *L, int objindex) {\n  const TValue *obj;\n  Table *mt = NULL;\n  int res;\n  lua_lock(L);\n  obj = index2adr(L, objindex);\n  switch (ttype(obj)) {\n    case LUA_TTABLE:\n      mt = hvalue(obj)->metatable;\n      break;\n    case LUA_TUSERDATA:\n      mt = uvalue(obj)->metatable;\n      break;\n    default:\n      mt = G(L)->mt[ttype(obj)];\n      break;\n  }\n  if (mt == NULL)\n    res = 0;\n  else {\n    sethvalue(L, L->top, mt);\n    api_incr_top(L);\n    res = 1;\n  }\n  lua_unlock(L);\n  return res;\n}\n\n\nLUA_API void lua_getfenv (lua_State *L, int idx) {\n  StkId o;\n  lua_lock(L);\n  o = index2adr(L, idx);\n  api_checkvalidindex(L, o);\n  switch (ttype(o)) {\n    case LUA_TFUNCTION:\n      sethvalue(L, L->top, clvalue(o)->c.env);\n      break;\n    case LUA_TUSERDATA:\n      sethvalue(L, L->top, uvalue(o)->env);\n      break;\n    case LUA_TTHREAD:\n      setobj2s(L, L->top,  gt(thvalue(o)));\n      break;\n    default:\n      setnilvalue(L->top);\n      break;\n  }\n  api_incr_top(L);\n  lua_unlock(L);\n}\n\n\n/*\n** set functions (stack -> Lua)\n*/\n\n\nLUA_API void lua_settable (lua_State *L, int idx) {\n  StkId t;\n  lua_lock(L);\n  api_checknelems(L, 2);\n  t = index2adr(L, idx);\n  api_checkvalidindex(L, t);\n  luaV_settable(L, t, L->top - 2, L->top - 1);\n  L->top -= 2;  /* pop index and value */\n  lua_unlock(L);\n}\n\n\nLUA_API void lua_setfield (lua_State *L, int idx, const char *k) {\n  StkId t;\n  TValue key;\n  lua_lock(L);\n  api_checknelems(L, 1);\n  t = index2adr(L, idx);\n  api_checkvalidindex(L, t);\n  setsvalue(L, &key, luaS_new(L, k));\n  luaV_settable(L, t, &key, L->top - 1);\n  L->top--;  /* pop value */\n  lua_unlock(L);\n}\n\n\nLUA_API void lua_rawset (lua_State *L, int idx) {\n  StkId t;\n  lua_lock(L);\n  api_checknelems(L, 2);\n  t = index2adr(L, idx);\n  api_check(L, ttistable(t));\n  setobj2t(L, luaH_set(L, hvalue(t), L->top-2), L->top-1);\n  luaC_barriert(L, hvalue(t), L->top-1);\n  L->top -= 2;\n  lua_unlock(L);\n}\n\n\nLUA_API void lua_rawseti (lua_State *L, int idx, int n) {\n  StkId o;\n  lua_lock(L);\n  api_checknelems(L, 1);\n  o = index2adr(L, idx);\n  api_check(L, ttistable(o));\n  setobj2t(L, luaH_setnum(L, hvalue(o), n), L->top-1);\n  luaC_barriert(L, hvalue(o), L->top-1);\n  L->top--;\n  lua_unlock(L);\n}\n\n\nLUA_API int lua_setmetatable (lua_State *L, int objindex) {\n  TValue *obj;\n  Table *mt;\n  lua_lock(L);\n  api_checknelems(L, 1);\n  obj = index2adr(L, objindex);\n  api_checkvalidindex(L, obj);\n  if (ttisnil(L->top - 1))\n    mt = NULL;\n  else {\n    api_check(L, ttistable(L->top - 1));\n    mt = hvalue(L->top - 1);\n  }\n  switch (ttype(obj)) {\n    case LUA_TTABLE: {\n      hvalue(obj)->metatable = mt;\n      if (mt)\n        luaC_objbarriert(L, hvalue(obj), mt);\n      break;\n    }\n    case LUA_TUSERDATA: {\n      uvalue(obj)->metatable = mt;\n      if (mt)\n        luaC_objbarrier(L, rawuvalue(obj), mt);\n      break;\n    }\n    default: {\n      G(L)->mt[ttype(obj)] = mt;\n      break;\n    }\n  }\n  L->top--;\n  lua_unlock(L);\n  return 1;\n}\n\n\nLUA_API int lua_setfenv (lua_State *L, int idx) {\n  StkId o;\n  int res = 1;\n  lua_lock(L);\n  api_checknelems(L, 1);\n  o = index2adr(L, idx);\n  api_checkvalidindex(L, o);\n  api_check(L, ttistable(L->top - 1));\n  switch (ttype(o)) {\n    case LUA_TFUNCTION:\n      clvalue(o)->c.env = hvalue(L->top - 1);\n      break;\n    case LUA_TUSERDATA:\n      uvalue(o)->env = hvalue(L->top - 1);\n      break;\n    case LUA_TTHREAD:\n      sethvalue(L, gt(thvalue(o)), hvalue(L->top - 1));\n      break;\n    default:\n      res = 0;\n      break;\n  }\n  if (res) luaC_objbarrier(L, gcvalue(o), hvalue(L->top - 1));\n  L->top--;\n  lua_unlock(L);\n  return res;\n}\n\n\n/*\n** `load' and `call' functions (run Lua code)\n*/\n\n\n#define adjustresults(L,nres) \\\n    { if (nres == LUA_MULTRET && L->top >= L->ci->top) L->ci->top = L->top; }\n\n\n#define checkresults(L,na,nr) \\\n     api_check(L, (nr) == LUA_MULTRET || (L->ci->top - L->top >= (nr) - (na)))\n\t\n\nLUA_API void lua_call (lua_State *L, int nargs, int nresults) {\n  StkId func;\n  lua_lock(L);\n  api_checknelems(L, nargs+1);\n  checkresults(L, nargs, nresults);\n  func = L->top - (nargs+1);\n  luaD_call(L, func, nresults);\n  adjustresults(L, nresults);\n  lua_unlock(L);\n}\n\n\n\n/*\n** Execute a protected call.\n*/\nstruct CallS {  /* data to `f_call' */\n  StkId func;\n  int nresults;\n};\n\n\nstatic void f_call (lua_State *L, void *ud) {\n  struct CallS *c = cast(struct CallS *, ud);\n  luaD_call(L, c->func, c->nresults);\n}\n\n\n\nLUA_API int lua_pcall (lua_State *L, int nargs, int nresults, int errfunc) {\n  struct CallS c;\n  int status;\n  ptrdiff_t func;\n  lua_lock(L);\n  api_checknelems(L, nargs+1);\n  checkresults(L, nargs, nresults);\n  if (errfunc == 0)\n    func = 0;\n  else {\n    StkId o = index2adr(L, errfunc);\n    api_checkvalidindex(L, o);\n    func = savestack(L, o);\n  }\n  c.func = L->top - (nargs+1);  /* function to be called */\n  c.nresults = nresults;\n  status = luaD_pcall(L, f_call, &c, savestack(L, c.func), func);\n  adjustresults(L, nresults);\n  lua_unlock(L);\n  return status;\n}\n\n\n/*\n** Execute a protected C call.\n*/\nstruct CCallS {  /* data to `f_Ccall' */\n  lua_CFunction func;\n  void *ud;\n};\n\n\nstatic void f_Ccall (lua_State *L, void *ud) {\n  struct CCallS *c = cast(struct CCallS *, ud);\n  Closure *cl;\n  cl = luaF_newCclosure(L, 0, getcurrenv(L));\n  cl->c.f = c->func;\n  setclvalue(L, L->top, cl);  /* push function */\n  api_incr_top(L);\n  setpvalue(L->top, c->ud);  /* push only argument */\n  api_incr_top(L);\n  luaD_call(L, L->top - 2, 0);\n}\n\n\nLUA_API int lua_cpcall (lua_State *L, lua_CFunction func, void *ud) {\n  struct CCallS c;\n  int status;\n  lua_lock(L);\n  c.func = func;\n  c.ud = ud;\n  status = luaD_pcall(L, f_Ccall, &c, savestack(L, L->top), 0);\n  lua_unlock(L);\n  return status;\n}\n\n\nLUA_API int lua_load (lua_State *L, lua_Reader reader, void *data,\n                      const char *chunkname) {\n  ZIO z;\n  int status;\n  lua_lock(L);\n  if (!chunkname) chunkname = \"?\";\n  luaZ_init(L, &z, reader, data);\n  status = luaD_protectedparser(L, &z, chunkname);\n  lua_unlock(L);\n  return status;\n}\n\n\nLUA_API int lua_dump (lua_State *L, lua_Writer writer, void *data) {\n  int status;\n  TValue *o;\n  lua_lock(L);\n  api_checknelems(L, 1);\n  o = L->top - 1;\n  if (isLfunction(o))\n    status = luaU_dump(L, clvalue(o)->l.p, writer, data, 0);\n  else\n    status = 1;\n  lua_unlock(L);\n  return status;\n}\n\n\nLUA_API int  lua_status (lua_State *L) {\n  return L->status;\n}\n\n\n/*\n** Garbage-collection function\n*/\n\nLUA_API int lua_gc (lua_State *L, int what, int data) {\n  int res = 0;\n  global_State *g;\n  lua_lock(L);\n  g = G(L);\n  switch (what) {\n    case LUA_GCSTOP: {\n      g->GCthreshold = MAX_LUMEM;\n      break;\n    }\n    case LUA_GCRESTART: {\n      g->GCthreshold = g->totalbytes;\n      break;\n    }\n    case LUA_GCCOLLECT: {\n      luaC_fullgc(L);\n      break;\n    }\n    case LUA_GCCOUNT: {\n      /* GC values are expressed in Kbytes: #bytes/2^10 */\n      res = cast_int(g->totalbytes >> 10);\n      break;\n    }\n    case LUA_GCCOUNTB: {\n      res = cast_int(g->totalbytes & 0x3ff);\n      break;\n    }\n    case LUA_GCSTEP: {\n      lu_mem a = (cast(lu_mem, data) << 10);\n      if (a <= g->totalbytes)\n        g->GCthreshold = g->totalbytes - a;\n      else\n        g->GCthreshold = 0;\n      while (g->GCthreshold <= g->totalbytes) {\n        luaC_step(L);\n        if (g->gcstate == GCSpause) {  /* end of cycle? */\n          res = 1;  /* signal it */\n          break;\n        }\n      }\n      break;\n    }\n    case LUA_GCSETPAUSE: {\n      res = g->gcpause;\n      g->gcpause = data;\n      break;\n    }\n    case LUA_GCSETSTEPMUL: {\n      res = g->gcstepmul;\n      g->gcstepmul = data;\n      break;\n    }\n    default: res = -1;  /* invalid option */\n  }\n  lua_unlock(L);\n  return res;\n}\n\n\n\n/*\n** miscellaneous functions\n*/\n\n\nLUA_API int lua_error (lua_State *L) {\n  lua_lock(L);\n  api_checknelems(L, 1);\n  luaG_errormsg(L);\n  lua_unlock(L);\n  return 0;  /* to avoid warnings */\n}\n\n\nLUA_API int lua_next (lua_State *L, int idx) {\n  StkId t;\n  int more;\n  lua_lock(L);\n  t = index2adr(L, idx);\n  api_check(L, ttistable(t));\n  more = luaH_next(L, hvalue(t), L->top - 1);\n  if (more) {\n    api_incr_top(L);\n  }\n  else  /* no more elements */\n    L->top -= 1;  /* remove key */\n  lua_unlock(L);\n  return more;\n}\n\n\nLUA_API void lua_concat (lua_State *L, int n) {\n  lua_lock(L);\n  api_checknelems(L, n);\n  if (n >= 2) {\n    luaC_checkGC(L);\n    luaV_concat(L, n, cast_int(L->top - L->base) - 1);\n    L->top -= (n-1);\n  }\n  else if (n == 0) {  /* push empty string */\n    setsvalue2s(L, L->top, luaS_newlstr(L, \"\", 0));\n    api_incr_top(L);\n  }\n  /* else n == 1; nothing to do */\n  lua_unlock(L);\n}\n\n\nLUA_API lua_Alloc lua_getallocf (lua_State *L, void **ud) {\n  lua_Alloc f;\n  lua_lock(L);\n  if (ud) *ud = G(L)->ud;\n  f = G(L)->frealloc;\n  lua_unlock(L);\n  return f;\n}\n\n\nLUA_API void lua_setallocf (lua_State *L, lua_Alloc f, void *ud) {\n  lua_lock(L);\n  G(L)->ud = ud;\n  G(L)->frealloc = f;\n  lua_unlock(L);\n}\n\n\nLUA_API void *lua_newuserdata (lua_State *L, size_t size) {\n  Udata *u;\n  lua_lock(L);\n  luaC_checkGC(L);\n  u = luaS_newudata(L, size, getcurrenv(L));\n  setuvalue(L, L->top, u);\n  api_incr_top(L);\n  lua_unlock(L);\n  return u + 1;\n}\n\n\n\n\nstatic const char *aux_upvalue (StkId fi, int n, TValue **val) {\n  Closure *f;\n  if (!ttisfunction(fi)) return NULL;\n  f = clvalue(fi);\n  if (f->c.isC) {\n    if (!(1 <= n && n <= f->c.nupvalues)) return NULL;\n    *val = &f->c.upvalue[n-1];\n    return \"\";\n  }\n  else {\n    Proto *p = f->l.p;\n    if (!(1 <= n && n <= p->sizeupvalues)) return NULL;\n    *val = f->l.upvals[n-1]->v;\n    return getstr(p->upvalues[n-1]);\n  }\n}\n\n\nLUA_API const char *lua_getupvalue (lua_State *L, int funcindex, int n) {\n  const char *name;\n  TValue *val;\n  lua_lock(L);\n  name = aux_upvalue(index2adr(L, funcindex), n, &val);\n  if (name) {\n    setobj2s(L, L->top, val);\n    api_incr_top(L);\n  }\n  lua_unlock(L);\n  return name;\n}\n\n\nLUA_API const char *lua_setupvalue (lua_State *L, int funcindex, int n) {\n  const char *name;\n  TValue *val;\n  StkId fi;\n  lua_lock(L);\n  fi = index2adr(L, funcindex);\n  api_checknelems(L, 1);\n  name = aux_upvalue(fi, n, &val);\n  if (name) {\n    L->top--;\n    setobj(L, val, L->top);\n    luaC_barrier(L, clvalue(fi), L->top);\n  }\n  lua_unlock(L);\n  return name;\n}\n\n"
  },
  {
    "path": "src/lapi.h",
    "content": "/*\n** $Id: lapi.h,v 2.2.1.1 2007/12/27 13:02:25 roberto Exp $\n** Auxiliary functions from Lua API\n** See Copyright Notice in lua.h\n*/\n\n#ifndef lapi_h\n#define lapi_h\n\n\n#include \"lobject.h\"\n\n\nLUAI_FUNC void luaA_pushobject (lua_State *L, const TValue *o);\n\n#endif\n"
  },
  {
    "path": "src/lauxlib.c",
    "content": "/*\n** $Id: lauxlib.c,v 1.159.1.3 2008/01/21 13:20:51 roberto Exp $\n** Auxiliary functions for building Lua libraries\n** See Copyright Notice in lua.h\n*/\n\n\n#include <ctype.h>\n#include <errno.h>\n#include <stdarg.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n\n\n/* This file uses only the official API of Lua.\n** Any function declared here could be written as an application function.\n*/\n\n#define lauxlib_c\n#define LUA_LIB\n\n#include \"lua.h\"\n\n#include \"lauxlib.h\"\n\n\n#define FREELIST_REF\t0\t/* free list of references */\n\n\n/* convert a stack index to positive */\n#define abs_index(L, i)\t\t((i) > 0 || (i) <= LUA_REGISTRYINDEX ? (i) : \\\n\t\t\t\t\tlua_gettop(L) + (i) + 1)\n\n\n/*\n** {======================================================\n** Error-report functions\n** =======================================================\n*/\n\n\nLUALIB_API int luaL_argerror (lua_State *L, int narg, const char *extramsg) {\n  lua_Debug ar;\n  if (!lua_getstack(L, 0, &ar))  /* no stack frame? */\n    return luaL_error(L, \"bad argument #%d (%s)\", narg, extramsg);\n  lua_getinfo(L, \"n\", &ar);\n  if (strcmp(ar.namewhat, \"method\") == 0) {\n    narg--;  /* do not count `self' */\n    if (narg == 0)  /* error is in the self argument itself? */\n      return luaL_error(L, \"calling \" LUA_QS \" on bad self (%s)\",\n                           ar.name, extramsg);\n  }\n  if (ar.name == NULL)\n    ar.name = \"?\";\n  return luaL_error(L, \"bad argument #%d to \" LUA_QS \" (%s)\",\n                        narg, ar.name, extramsg);\n}\n\n\nLUALIB_API int luaL_typerror (lua_State *L, int narg, const char *tname) {\n  const char *msg = lua_pushfstring(L, \"%s expected, got %s\",\n                                    tname, luaL_typename(L, narg));\n  return luaL_argerror(L, narg, msg);\n}\n\n\nstatic void tag_error (lua_State *L, int narg, int tag) {\n  luaL_typerror(L, narg, lua_typename(L, tag));\n}\n\n\nLUALIB_API void luaL_where (lua_State *L, int level) {\n  lua_Debug ar;\n  if (lua_getstack(L, level, &ar)) {  /* check function at level */\n    lua_getinfo(L, \"Sl\", &ar);  /* get info about it */\n    if (ar.currentline > 0) {  /* is there info? */\n      lua_pushfstring(L, \"%s:%d: \", ar.short_src, ar.currentline);\n      return;\n    }\n  }\n  lua_pushliteral(L, \"\");  /* else, no information available... */\n}\n\n\nLUALIB_API int luaL_error (lua_State *L, const char *fmt, ...) {\n  va_list argp;\n  va_start(argp, fmt);\n  luaL_where(L, 1);\n  lua_pushvfstring(L, fmt, argp);\n  va_end(argp);\n  lua_concat(L, 2);\n  return lua_error(L);\n}\n\n/* }====================================================== */\n\n\nLUALIB_API int luaL_checkoption (lua_State *L, int narg, const char *def,\n                                 const char *const lst[]) {\n  const char *name = (def) ? luaL_optstring(L, narg, def) :\n                             luaL_checkstring(L, narg);\n  int i;\n  for (i=0; lst[i]; i++)\n    if (strcmp(lst[i], name) == 0)\n      return i;\n  return luaL_argerror(L, narg,\n                       lua_pushfstring(L, \"invalid option \" LUA_QS, name));\n}\n\n\nLUALIB_API int luaL_newmetatable (lua_State *L, const char *tname) {\n  lua_getfield(L, LUA_REGISTRYINDEX, tname);  /* get registry.name */\n  if (!lua_isnil(L, -1))  /* name already in use? */\n    return 0;  /* leave previous value on top, but return 0 */\n  lua_pop(L, 1);\n  lua_newtable(L);  /* create metatable */\n  lua_pushvalue(L, -1);\n  lua_setfield(L, LUA_REGISTRYINDEX, tname);  /* registry.name = metatable */\n  return 1;\n}\n\n\nLUALIB_API void *luaL_checkudata (lua_State *L, int ud, const char *tname) {\n  void *p = lua_touserdata(L, ud);\n  if (p != NULL) {  /* value is a userdata? */\n    if (lua_getmetatable(L, ud)) {  /* does it have a metatable? */\n      lua_getfield(L, LUA_REGISTRYINDEX, tname);  /* get correct metatable */\n      if (lua_rawequal(L, -1, -2)) {  /* does it have the correct mt? */\n        lua_pop(L, 2);  /* remove both metatables */\n        return p;\n      }\n    }\n  }\n  luaL_typerror(L, ud, tname);  /* else error */\n  return NULL;  /* to avoid warnings */\n}\n\n\nLUALIB_API void luaL_checkstack (lua_State *L, int space, const char *mes) {\n  if (!lua_checkstack(L, space))\n    luaL_error(L, \"stack overflow (%s)\", mes);\n}\n\n\nLUALIB_API void luaL_checktype (lua_State *L, int narg, int t) {\n  if (lua_type(L, narg) != t)\n    tag_error(L, narg, t);\n}\n\n\nLUALIB_API void luaL_checkany (lua_State *L, int narg) {\n  if (lua_type(L, narg) == LUA_TNONE)\n    luaL_argerror(L, narg, \"value expected\");\n}\n\n\nLUALIB_API const char *luaL_checklstring (lua_State *L, int narg, size_t *len) {\n  const char *s = lua_tolstring(L, narg, len);\n  if (!s) tag_error(L, narg, LUA_TSTRING);\n  return s;\n}\n\n\nLUALIB_API const char *luaL_optlstring (lua_State *L, int narg,\n                                        const char *def, size_t *len) {\n  if (lua_isnoneornil(L, narg)) {\n    if (len)\n      *len = (def ? strlen(def) : 0);\n    return def;\n  }\n  else return luaL_checklstring(L, narg, len);\n}\n\n\nLUALIB_API lua_Number luaL_checknumber (lua_State *L, int narg) {\n  lua_Number d = lua_tonumber(L, narg);\n  if (d == 0 && !lua_isnumber(L, narg))  /* avoid extra test when d is not 0 */\n    tag_error(L, narg, LUA_TNUMBER);\n  return d;\n}\n\n\nLUALIB_API lua_Number luaL_optnumber (lua_State *L, int narg, lua_Number def) {\n  return luaL_opt(L, luaL_checknumber, narg, def);\n}\n\n\nLUALIB_API lua_Integer luaL_checkinteger (lua_State *L, int narg) {\n  lua_Integer d = lua_tointeger(L, narg);\n  if (d == 0 && !lua_isnumber(L, narg))  /* avoid extra test when d is not 0 */\n    tag_error(L, narg, LUA_TNUMBER);\n  return d;\n}\n\n\nLUALIB_API lua_Integer luaL_optinteger (lua_State *L, int narg,\n                                                      lua_Integer def) {\n  return luaL_opt(L, luaL_checkinteger, narg, def);\n}\n\n\nLUALIB_API int luaL_getmetafield (lua_State *L, int obj, const char *event) {\n  if (!lua_getmetatable(L, obj))  /* no metatable? */\n    return 0;\n  lua_pushstring(L, event);\n  lua_rawget(L, -2);\n  if (lua_isnil(L, -1)) {\n    lua_pop(L, 2);  /* remove metatable and metafield */\n    return 0;\n  }\n  else {\n    lua_remove(L, -2);  /* remove only metatable */\n    return 1;\n  }\n}\n\n\nLUALIB_API int luaL_callmeta (lua_State *L, int obj, const char *event) {\n  obj = abs_index(L, obj);\n  if (!luaL_getmetafield(L, obj, event))  /* no metafield? */\n    return 0;\n  lua_pushvalue(L, obj);\n  lua_call(L, 1, 1);\n  return 1;\n}\n\n\nLUALIB_API void (luaL_register) (lua_State *L, const char *libname,\n                                const luaL_Reg *l) {\n  luaI_openlib(L, libname, l, 0);\n}\n\n\nstatic int libsize (const luaL_Reg *l) {\n  int size = 0;\n  for (; l->name; l++) size++;\n  return size;\n}\n\n\nLUALIB_API void luaI_openlib (lua_State *L, const char *libname,\n                              const luaL_Reg *l, int nup) {\n  if (libname) {\n    int size = libsize(l);\n    /* check whether lib already exists */\n    luaL_findtable(L, LUA_REGISTRYINDEX, \"_LOADED\", 1);\n    lua_getfield(L, -1, libname);  /* get _LOADED[libname] */\n    if (!lua_istable(L, -1)) {  /* not found? */\n      lua_pop(L, 1);  /* remove previous result */\n      /* try global variable (and create one if it does not exist) */\n      if (luaL_findtable(L, LUA_GLOBALSINDEX, libname, size) != NULL)\n        luaL_error(L, \"name conflict for module \" LUA_QS, libname);\n      lua_pushvalue(L, -1);\n      lua_setfield(L, -3, libname);  /* _LOADED[libname] = new table */\n    }\n    lua_remove(L, -2);  /* remove _LOADED table */\n    lua_insert(L, -(nup+1));  /* move library table to below upvalues */\n  }\n  for (; l->name; l++) {\n    int i;\n    for (i=0; i<nup; i++)  /* copy upvalues to the top */\n      lua_pushvalue(L, -nup);\n    lua_pushcclosure(L, l->func, nup);\n    lua_setfield(L, -(nup+2), l->name);\n  }\n  lua_pop(L, nup);  /* remove upvalues */\n}\n\n\n\n/*\n** {======================================================\n** getn-setn: size for arrays\n** =======================================================\n*/\n\n#if defined(LUA_COMPAT_GETN)\n\nstatic int checkint (lua_State *L, int topop) {\n  int n = (lua_type(L, -1) == LUA_TNUMBER) ? lua_tointeger(L, -1) : -1;\n  lua_pop(L, topop);\n  return n;\n}\n\n\nstatic void getsizes (lua_State *L) {\n  lua_getfield(L, LUA_REGISTRYINDEX, \"LUA_SIZES\");\n  if (lua_isnil(L, -1)) {  /* no `size' table? */\n    lua_pop(L, 1);  /* remove nil */\n    lua_newtable(L);  /* create it */\n    lua_pushvalue(L, -1);  /* `size' will be its own metatable */\n    lua_setmetatable(L, -2);\n    lua_pushliteral(L, \"kv\");\n    lua_setfield(L, -2, \"__mode\");  /* metatable(N).__mode = \"kv\" */\n    lua_pushvalue(L, -1);\n    lua_setfield(L, LUA_REGISTRYINDEX, \"LUA_SIZES\");  /* store in register */\n  }\n}\n\n\nLUALIB_API void luaL_setn (lua_State *L, int t, int n) {\n  t = abs_index(L, t);\n  lua_pushliteral(L, \"n\");\n  lua_rawget(L, t);\n  if (checkint(L, 1) >= 0) {  /* is there a numeric field `n'? */\n    lua_pushliteral(L, \"n\");  /* use it */\n    lua_pushinteger(L, n);\n    lua_rawset(L, t);\n  }\n  else {  /* use `sizes' */\n    getsizes(L);\n    lua_pushvalue(L, t);\n    lua_pushinteger(L, n);\n    lua_rawset(L, -3);  /* sizes[t] = n */\n    lua_pop(L, 1);  /* remove `sizes' */\n  }\n}\n\n\nLUALIB_API int luaL_getn (lua_State *L, int t) {\n  int n;\n  t = abs_index(L, t);\n  lua_pushliteral(L, \"n\");  /* try t.n */\n  lua_rawget(L, t);\n  if ((n = checkint(L, 1)) >= 0) return n;\n  getsizes(L);  /* else try sizes[t] */\n  lua_pushvalue(L, t);\n  lua_rawget(L, -2);\n  if ((n = checkint(L, 2)) >= 0) return n;\n  return (int)lua_objlen(L, t);\n}\n\n#endif\n\n/* }====================================================== */\n\n\n\nLUALIB_API const char *luaL_gsub (lua_State *L, const char *s, const char *p,\n                                                               const char *r) {\n  const char *wild;\n  size_t l = strlen(p);\n  luaL_Buffer b;\n  luaL_buffinit(L, &b);\n  while ((wild = strstr(s, p)) != NULL) {\n    luaL_addlstring(&b, s, wild - s);  /* push prefix */\n    luaL_addstring(&b, r);  /* push replacement in place of pattern */\n    s = wild + l;  /* continue after `p' */\n  }\n  luaL_addstring(&b, s);  /* push last suffix */\n  luaL_pushresult(&b);\n  return lua_tostring(L, -1);\n}\n\n\nLUALIB_API const char *luaL_findtable (lua_State *L, int idx,\n                                       const char *fname, int szhint) {\n  const char *e;\n  lua_pushvalue(L, idx);\n  do {\n    e = strchr(fname, '.');\n    if (e == NULL) e = fname + strlen(fname);\n    lua_pushlstring(L, fname, e - fname);\n    lua_rawget(L, -2);\n    if (lua_isnil(L, -1)) {  /* no such field? */\n      lua_pop(L, 1);  /* remove this nil */\n      lua_createtable(L, 0, (*e == '.' ? 1 : szhint)); /* new table for field */\n      lua_pushlstring(L, fname, e - fname);\n      lua_pushvalue(L, -2);\n      lua_settable(L, -4);  /* set new table into field */\n    }\n    else if (!lua_istable(L, -1)) {  /* field has a non-table value? */\n      lua_pop(L, 2);  /* remove table and value */\n      return fname;  /* return problematic part of the name */\n    }\n    lua_remove(L, -2);  /* remove previous table */\n    fname = e + 1;\n  } while (*e == '.');\n  return NULL;\n}\n\n\n\n/*\n** {======================================================\n** Generic Buffer manipulation\n** =======================================================\n*/\n\n\n#define bufflen(B)\t((B)->p - (B)->buffer)\n#define bufffree(B)\t((size_t)(LUAL_BUFFERSIZE - bufflen(B)))\n\n#define LIMIT\t(LUA_MINSTACK/2)\n\n\nstatic int emptybuffer (luaL_Buffer *B) {\n  size_t l = bufflen(B);\n  if (l == 0) return 0;  /* put nothing on stack */\n  else {\n    lua_pushlstring(B->L, B->buffer, l);\n    B->p = B->buffer;\n    B->lvl++;\n    return 1;\n  }\n}\n\n\nstatic void adjuststack (luaL_Buffer *B) {\n  if (B->lvl > 1) {\n    lua_State *L = B->L;\n    int toget = 1;  /* number of levels to concat */\n    size_t toplen = lua_strlen(L, -1);\n    do {\n      size_t l = lua_strlen(L, -(toget+1));\n      if (B->lvl - toget + 1 >= LIMIT || toplen > l) {\n        toplen += l;\n        toget++;\n      }\n      else break;\n    } while (toget < B->lvl);\n    lua_concat(L, toget);\n    B->lvl = B->lvl - toget + 1;\n  }\n}\n\n\nLUALIB_API char *luaL_prepbuffer (luaL_Buffer *B) {\n  if (emptybuffer(B))\n    adjuststack(B);\n  return B->buffer;\n}\n\n\nLUALIB_API void luaL_addlstring (luaL_Buffer *B, const char *s, size_t l) {\n  while (l--)\n    luaL_addchar(B, *s++);\n}\n\n\nLUALIB_API void luaL_addstring (luaL_Buffer *B, const char *s) {\n  luaL_addlstring(B, s, strlen(s));\n}\n\n\nLUALIB_API void luaL_pushresult (luaL_Buffer *B) {\n  emptybuffer(B);\n  lua_concat(B->L, B->lvl);\n  B->lvl = 1;\n}\n\n\nLUALIB_API void luaL_addvalue (luaL_Buffer *B) {\n  lua_State *L = B->L;\n  size_t vl;\n  const char *s = lua_tolstring(L, -1, &vl);\n  if (vl <= bufffree(B)) {  /* fit into buffer? */\n    memcpy(B->p, s, vl);  /* put it there */\n    B->p += vl;\n    lua_pop(L, 1);  /* remove from stack */\n  }\n  else {\n    if (emptybuffer(B))\n      lua_insert(L, -2);  /* put buffer before new value */\n    B->lvl++;  /* add new value into B stack */\n    adjuststack(B);\n  }\n}\n\n\nLUALIB_API void luaL_buffinit (lua_State *L, luaL_Buffer *B) {\n  B->L = L;\n  B->p = B->buffer;\n  B->lvl = 0;\n}\n\n/* }====================================================== */\n\n\nLUALIB_API int luaL_ref (lua_State *L, int t) {\n  int ref;\n  t = abs_index(L, t);\n  if (lua_isnil(L, -1)) {\n    lua_pop(L, 1);  /* remove from stack */\n    return LUA_REFNIL;  /* `nil' has a unique fixed reference */\n  }\n  lua_rawgeti(L, t, FREELIST_REF);  /* get first free element */\n  ref = (int)lua_tointeger(L, -1);  /* ref = t[FREELIST_REF] */\n  lua_pop(L, 1);  /* remove it from stack */\n  if (ref != 0) {  /* any free element? */\n    lua_rawgeti(L, t, ref);  /* remove it from list */\n    lua_rawseti(L, t, FREELIST_REF);  /* (t[FREELIST_REF] = t[ref]) */\n  }\n  else {  /* no free elements */\n    ref = (int)lua_objlen(L, t);\n    ref++;  /* create new reference */\n  }\n  lua_rawseti(L, t, ref);\n  return ref;\n}\n\n\nLUALIB_API void luaL_unref (lua_State *L, int t, int ref) {\n  if (ref >= 0) {\n    t = abs_index(L, t);\n    lua_rawgeti(L, t, FREELIST_REF);\n    lua_rawseti(L, t, ref);  /* t[ref] = t[FREELIST_REF] */\n    lua_pushinteger(L, ref);\n    lua_rawseti(L, t, FREELIST_REF);  /* t[FREELIST_REF] = ref */\n  }\n}\n\n\n\n/*\n** {======================================================\n** Load functions\n** =======================================================\n*/\n\ntypedef struct LoadF {\n  int extraline;\n  FILE *f;\n  char buff[LUAL_BUFFERSIZE];\n} LoadF;\n\n\nstatic const char *getF (lua_State *L, void *ud, size_t *size) {\n  LoadF *lf = (LoadF *)ud;\n  (void)L;\n  if (lf->extraline) {\n    lf->extraline = 0;\n    *size = 1;\n    return \"\\n\";\n  }\n  if (feof(lf->f)) return NULL;\n  *size = fread(lf->buff, 1, sizeof(lf->buff), lf->f);\n  return (*size > 0) ? lf->buff : NULL;\n}\n\n\nstatic int errfile (lua_State *L, const char *what, int fnameindex) {\n  const char *serr = strerror(errno);\n  const char *filename = lua_tostring(L, fnameindex) + 1;\n  lua_pushfstring(L, \"cannot %s %s: %s\", what, filename, serr);\n  lua_remove(L, fnameindex);\n  return LUA_ERRFILE;\n}\n\n\nLUALIB_API int luaL_loadfile (lua_State *L, const char *filename) {\n  LoadF lf;\n  int status, readstatus;\n  int c;\n  int fnameindex = lua_gettop(L) + 1;  /* index of filename on the stack */\n  lf.extraline = 0;\n  if (filename == NULL) {\n    lua_pushliteral(L, \"=stdin\");\n    lf.f = stdin;\n  }\n  else {\n    lua_pushfstring(L, \"@%s\", filename);\n    lf.f = fopen(filename, \"r\");\n    if (lf.f == NULL) return errfile(L, \"open\", fnameindex);\n  }\n  c = getc(lf.f);\n  if (c == '#') {  /* Unix exec. file? */\n    lf.extraline = 1;\n    while ((c = getc(lf.f)) != EOF && c != '\\n') ;  /* skip first line */\n    if (c == '\\n') c = getc(lf.f);\n  }\n  if (c == LUA_SIGNATURE[0] && filename) {  /* binary file? */\n    lf.f = freopen(filename, \"rb\", lf.f);  /* reopen in binary mode */\n    if (lf.f == NULL) return errfile(L, \"reopen\", fnameindex);\n    /* skip eventual `#!...' */\n    while ((c = getc(lf.f)) != EOF && c != LUA_SIGNATURE[0]) ;\n    lf.extraline = 0;\n  }\n  ungetc(c, lf.f);\n  status = lua_load(L, getF, &lf, lua_tostring(L, -1));\n  readstatus = ferror(lf.f);\n  if (filename) fclose(lf.f);  /* close file (even in case of errors) */\n  if (readstatus) {\n    lua_settop(L, fnameindex);  /* ignore results from `lua_load' */\n    return errfile(L, \"read\", fnameindex);\n  }\n  lua_remove(L, fnameindex);\n  return status;\n}\n\n\ntypedef struct LoadS {\n  const char *s;\n  size_t size;\n} LoadS;\n\n\nstatic const char *getS (lua_State *L, void *ud, size_t *size) {\n  LoadS *ls = (LoadS *)ud;\n  (void)L;\n  if (ls->size == 0) return NULL;\n  *size = ls->size;\n  ls->size = 0;\n  return ls->s;\n}\n\n\nLUALIB_API int luaL_loadbuffer (lua_State *L, const char *buff, size_t size,\n                                const char *name) {\n  LoadS ls;\n  ls.s = buff;\n  ls.size = size;\n  return lua_load(L, getS, &ls, name);\n}\n\n\nLUALIB_API int (luaL_loadstring) (lua_State *L, const char *s) {\n  return luaL_loadbuffer(L, s, strlen(s), s);\n}\n\n\n\n/* }====================================================== */\n\n\nstatic void *l_alloc (void *ud, void *ptr, size_t osize, size_t nsize) {\n  (void)ud;\n  (void)osize;\n  if (nsize == 0) {\n    free(ptr);\n    return NULL;\n  }\n  else\n    return realloc(ptr, nsize);\n}\n\n\nstatic int panic (lua_State *L) {\n  (void)L;  /* to avoid warnings */\n  fprintf(stderr, \"PANIC: unprotected error in call to Lua API (%s)\\n\",\n                   lua_tostring(L, -1));\n  return 0;\n}\n\n\nLUALIB_API lua_State *luaL_newstate (void) {\n  lua_State *L = lua_newstate(l_alloc, NULL);\n  if (L) lua_atpanic(L, &panic);\n  return L;\n}\n\n"
  },
  {
    "path": "src/lauxlib.h",
    "content": "/*\n** $Id: lauxlib.h,v 1.88.1.1 2007/12/27 13:02:25 roberto Exp $\n** Auxiliary functions for building Lua libraries\n** See Copyright Notice in lua.h\n*/\n\n\n#ifndef lauxlib_h\n#define lauxlib_h\n\n\n#include <stddef.h>\n#include <stdio.h>\n\n#include \"lua.h\"\n\n\n#if defined(LUA_COMPAT_GETN)\nLUALIB_API int (luaL_getn) (lua_State *L, int t);\nLUALIB_API void (luaL_setn) (lua_State *L, int t, int n);\n#else\n#define luaL_getn(L,i)          ((int)lua_objlen(L, i))\n#define luaL_setn(L,i,j)        ((void)0)  /* no op! */\n#endif\n\n#if defined(LUA_COMPAT_OPENLIB)\n#define luaI_openlib\tluaL_openlib\n#endif\n\n\n/* extra error code for `luaL_load' */\n#define LUA_ERRFILE     (LUA_ERRERR+1)\n\n\ntypedef struct luaL_Reg {\n  const char *name;\n  lua_CFunction func;\n} luaL_Reg;\n\n\n\nLUALIB_API void (luaI_openlib) (lua_State *L, const char *libname,\n                                const luaL_Reg *l, int nup);\nLUALIB_API void (luaL_register) (lua_State *L, const char *libname,\n                                const luaL_Reg *l);\nLUALIB_API int (luaL_getmetafield) (lua_State *L, int obj, const char *e);\nLUALIB_API int (luaL_callmeta) (lua_State *L, int obj, const char *e);\nLUALIB_API int (luaL_typerror) (lua_State *L, int narg, const char *tname);\nLUALIB_API int (luaL_argerror) (lua_State *L, int numarg, const char *extramsg);\nLUALIB_API const char *(luaL_checklstring) (lua_State *L, int numArg,\n                                                          size_t *l);\nLUALIB_API const char *(luaL_optlstring) (lua_State *L, int numArg,\n                                          const char *def, size_t *l);\nLUALIB_API lua_Number (luaL_checknumber) (lua_State *L, int numArg);\nLUALIB_API lua_Number (luaL_optnumber) (lua_State *L, int nArg, lua_Number def);\n\nLUALIB_API lua_Integer (luaL_checkinteger) (lua_State *L, int numArg);\nLUALIB_API lua_Integer (luaL_optinteger) (lua_State *L, int nArg,\n                                          lua_Integer def);\n\nLUALIB_API void (luaL_checkstack) (lua_State *L, int sz, const char *msg);\nLUALIB_API void (luaL_checktype) (lua_State *L, int narg, int t);\nLUALIB_API void (luaL_checkany) (lua_State *L, int narg);\n\nLUALIB_API int   (luaL_newmetatable) (lua_State *L, const char *tname);\nLUALIB_API void *(luaL_checkudata) (lua_State *L, int ud, const char *tname);\n\nLUALIB_API void (luaL_where) (lua_State *L, int lvl);\nLUALIB_API int (luaL_error) (lua_State *L, const char *fmt, ...);\n\nLUALIB_API int (luaL_checkoption) (lua_State *L, int narg, const char *def,\n                                   const char *const lst[]);\n\nLUALIB_API int (luaL_ref) (lua_State *L, int t);\nLUALIB_API void (luaL_unref) (lua_State *L, int t, int ref);\n\nLUALIB_API int (luaL_loadfile) (lua_State *L, const char *filename);\nLUALIB_API int (luaL_loadbuffer) (lua_State *L, const char *buff, size_t sz,\n                                  const char *name);\nLUALIB_API int (luaL_loadstring) (lua_State *L, const char *s);\n\nLUALIB_API lua_State *(luaL_newstate) (void);\n\n\nLUALIB_API const char *(luaL_gsub) (lua_State *L, const char *s, const char *p,\n                                                  const char *r);\n\nLUALIB_API const char *(luaL_findtable) (lua_State *L, int idx,\n                                         const char *fname, int szhint);\n\n\n\n\n/*\n** ===============================================================\n** some useful macros\n** ===============================================================\n*/\n\n#define luaL_argcheck(L, cond,numarg,extramsg)\t\\\n\t\t((void)((cond) || luaL_argerror(L, (numarg), (extramsg))))\n#define luaL_checkstring(L,n)\t(luaL_checklstring(L, (n), NULL))\n#define luaL_optstring(L,n,d)\t(luaL_optlstring(L, (n), (d), NULL))\n#define luaL_checkint(L,n)\t((int)luaL_checkinteger(L, (n)))\n#define luaL_optint(L,n,d)\t((int)luaL_optinteger(L, (n), (d)))\n#define luaL_checklong(L,n)\t((long)luaL_checkinteger(L, (n)))\n#define luaL_optlong(L,n,d)\t((long)luaL_optinteger(L, (n), (d)))\n\n#define luaL_typename(L,i)\tlua_typename(L, lua_type(L,(i)))\n\n#define luaL_dofile(L, fn) \\\n\t(luaL_loadfile(L, fn) || lua_pcall(L, 0, LUA_MULTRET, 0))\n\n#define luaL_dostring(L, s) \\\n\t(luaL_loadstring(L, s) || lua_pcall(L, 0, LUA_MULTRET, 0))\n\n#define luaL_getmetatable(L,n)\t(lua_getfield(L, LUA_REGISTRYINDEX, (n)))\n\n#define luaL_opt(L,f,n,d)\t(lua_isnoneornil(L,(n)) ? (d) : f(L,(n)))\n\n/*\n** {======================================================\n** Generic Buffer manipulation\n** =======================================================\n*/\n\n\n\ntypedef struct luaL_Buffer {\n  char *p;\t\t\t/* current position in buffer */\n  int lvl;  /* number of strings in the stack (level) */\n  lua_State *L;\n  char buffer[LUAL_BUFFERSIZE];\n} luaL_Buffer;\n\n#define luaL_addchar(B,c) \\\n  ((void)((B)->p < ((B)->buffer+LUAL_BUFFERSIZE) || luaL_prepbuffer(B)), \\\n   (*(B)->p++ = (char)(c)))\n\n/* compatibility only */\n#define luaL_putchar(B,c)\tluaL_addchar(B,c)\n\n#define luaL_addsize(B,n)\t((B)->p += (n))\n\nLUALIB_API void (luaL_buffinit) (lua_State *L, luaL_Buffer *B);\nLUALIB_API char *(luaL_prepbuffer) (luaL_Buffer *B);\nLUALIB_API void (luaL_addlstring) (luaL_Buffer *B, const char *s, size_t l);\nLUALIB_API void (luaL_addstring) (luaL_Buffer *B, const char *s);\nLUALIB_API void (luaL_addvalue) (luaL_Buffer *B);\nLUALIB_API void (luaL_pushresult) (luaL_Buffer *B);\n\n\n/* }====================================================== */\n\n\n/* compatibility with ref system */\n\n/* pre-defined references */\n#define LUA_NOREF       (-2)\n#define LUA_REFNIL      (-1)\n\n#define lua_ref(L,lock) ((lock) ? luaL_ref(L, LUA_REGISTRYINDEX) : \\\n      (lua_pushstring(L, \"unlocked references are obsolete\"), lua_error(L), 0))\n\n#define lua_unref(L,ref)        luaL_unref(L, LUA_REGISTRYINDEX, (ref))\n\n#define lua_getref(L,ref)       lua_rawgeti(L, LUA_REGISTRYINDEX, (ref))\n\n\n#define luaL_reg\tluaL_Reg\n\n#endif\n\n\n"
  },
  {
    "path": "src/lbaselib.c",
    "content": "/*\n** $Id: lbaselib.c,v 1.191.1.6 2008/02/14 16:46:22 roberto Exp $\n** Basic library\n** See Copyright Notice in lua.h\n*/\n\n\n\n#include <ctype.h>\n#ifdef __NetBSD__\n#include <curses.h>\n#else\n#include <ncurses.h>\n#endif\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n\n#define lbaselib_c\n#define LUA_LIB\n\n#include \"lua.h\"\n\n#include \"lauxlib.h\"\n#include \"lualib.h\"\n\n\n\n\nstatic int luaB_print (lua_State *L) {\n  int n = lua_gettop(L);  /* number of arguments */\n  int i, y, x;\n  lua_getglobal(L, \"tostring\");\n  for (i=1; i<=n; i++) {\n    const char *s;\n    lua_pushvalue(L, -1);  /* function to be called */\n    lua_pushvalue(L, i);   /* value to print */\n    lua_call(L, 1, 1);\n    s = lua_tostring(L, -1);  /* get result */\n    if (s == NULL)\n      return luaL_error(L, LUA_QL(\"tostring\") \" must return a string to \"\n                           LUA_QL(\"print\"));\n    if (i>1) addstr(\"\\t\");\n    addstr(s);\n    lua_pop(L, 1);  /* pop result */\n  }\n  getyx(stdscr, y, x);\n  mvprintw(y+1, x=0, \"\");\n  refresh();\n  return 0;\n}\n\n\nstatic int luaB_tonumber (lua_State *L) {\n  int base = luaL_optint(L, 2, 10);\n  if (base == 10) {  /* standard conversion */\n    luaL_checkany(L, 1);\n    if (lua_isnumber(L, 1)) {\n      lua_pushnumber(L, lua_tonumber(L, 1));\n      return 1;\n    }\n  }\n  else {\n    const char *s1 = luaL_checkstring(L, 1);\n    char *s2;\n    unsigned long n;\n    luaL_argcheck(L, 2 <= base && base <= 36, 2, \"base out of range\");\n    n = strtoul(s1, &s2, base);\n    if (s1 != s2) {  /* at least one valid digit? */\n      while (isspace((unsigned char)(*s2))) s2++;  /* skip trailing spaces */\n      if (*s2 == '\\0') {  /* no invalid trailing characters? */\n        lua_pushnumber(L, (lua_Number)n);\n        return 1;\n      }\n    }\n  }\n  lua_pushnil(L);  /* else not a number */\n  return 1;\n}\n\n\nstatic int luaB_error (lua_State *L) {\n  int level = luaL_optint(L, 2, 1);\n  lua_settop(L, 1);\n  if (lua_isstring(L, 1) && level > 0) {  /* add extra information? */\n    luaL_where(L, level);\n    lua_pushvalue(L, 1);\n    lua_concat(L, 2);\n  }\n  return lua_error(L);\n}\n\n\nstatic int luaB_getmetatable (lua_State *L) {\n  luaL_checkany(L, 1);\n  if (!lua_getmetatable(L, 1)) {\n    lua_pushnil(L);\n    return 1;  /* no metatable */\n  }\n  luaL_getmetafield(L, 1, \"__metatable\");\n  return 1;  /* returns either __metatable field (if present) or metatable */\n}\n\n\nstatic int luaB_setmetatable (lua_State *L) {\n  int t = lua_type(L, 2);\n  luaL_checktype(L, 1, LUA_TTABLE);\n  luaL_argcheck(L, t == LUA_TNIL || t == LUA_TTABLE, 2,\n                    \"nil or table expected\");\n  if (luaL_getmetafield(L, 1, \"__metatable\"))\n    luaL_error(L, \"cannot change a protected metatable\");\n  lua_settop(L, 2);\n  lua_setmetatable(L, 1);\n  return 1;\n}\n\n\nstatic void getfunc (lua_State *L, int opt) {\n  if (lua_isfunction(L, 1)) lua_pushvalue(L, 1);\n  else {\n    lua_Debug ar;\n    int level = opt ? luaL_optint(L, 1, 1) : luaL_checkint(L, 1);\n    luaL_argcheck(L, level >= 0, 1, \"level must be non-negative\");\n    if (lua_getstack(L, level, &ar) == 0)\n      luaL_argerror(L, 1, \"invalid level\");\n    lua_getinfo(L, \"f\", &ar);\n    if (lua_isnil(L, -1))\n      luaL_error(L, \"no function environment for tail call at level %d\",\n                    level);\n  }\n}\n\n\nstatic int luaB_getfenv (lua_State *L) {\n  getfunc(L, 1);\n  if (lua_iscfunction(L, -1))  /* is a C function? */\n    lua_pushvalue(L, LUA_GLOBALSINDEX);  /* return the thread's global env. */\n  else\n    lua_getfenv(L, -1);\n  return 1;\n}\n\n\nstatic int luaB_setfenv (lua_State *L) {\n  luaL_checktype(L, 2, LUA_TTABLE);\n  getfunc(L, 0);\n  lua_pushvalue(L, 2);\n  if (lua_isnumber(L, 1) && lua_tonumber(L, 1) == 0) {\n    /* change environment of current thread */\n    lua_pushthread(L);\n    lua_insert(L, -2);\n    lua_setfenv(L, -2);\n    return 0;\n  }\n  else if (lua_iscfunction(L, -2) || lua_setfenv(L, -2) == 0)\n    luaL_error(L,\n          LUA_QL(\"setfenv\") \" cannot change environment of given object\");\n  return 1;\n}\n\n\nstatic int luaB_rawequal (lua_State *L) {\n  luaL_checkany(L, 1);\n  luaL_checkany(L, 2);\n  lua_pushboolean(L, lua_rawequal(L, 1, 2));\n  return 1;\n}\n\n\nstatic int luaB_rawget (lua_State *L) {\n  luaL_checktype(L, 1, LUA_TTABLE);\n  luaL_checkany(L, 2);\n  lua_settop(L, 2);\n  lua_rawget(L, 1);\n  return 1;\n}\n\nstatic int luaB_rawset (lua_State *L) {\n  luaL_checktype(L, 1, LUA_TTABLE);\n  luaL_checkany(L, 2);\n  luaL_checkany(L, 3);\n  lua_settop(L, 3);\n  lua_rawset(L, 1);\n  return 1;\n}\n\n\nstatic int luaB_gcinfo (lua_State *L) {\n  lua_pushinteger(L, lua_getgccount(L));\n  return 1;\n}\n\n\nstatic int luaB_collectgarbage (lua_State *L) {\n  static const char *const opts[] = {\"stop\", \"restart\", \"collect\",\n    \"count\", \"step\", \"setpause\", \"setstepmul\", NULL};\n  static const int optsnum[] = {LUA_GCSTOP, LUA_GCRESTART, LUA_GCCOLLECT,\n    LUA_GCCOUNT, LUA_GCSTEP, LUA_GCSETPAUSE, LUA_GCSETSTEPMUL};\n  int o = luaL_checkoption(L, 1, \"collect\", opts);\n  int ex = luaL_optint(L, 2, 0);\n  int res = lua_gc(L, optsnum[o], ex);\n  switch (optsnum[o]) {\n    case LUA_GCCOUNT: {\n      int b = lua_gc(L, LUA_GCCOUNTB, 0);\n      lua_pushnumber(L, res + ((lua_Number)b/1024));\n      return 1;\n    }\n    case LUA_GCSTEP: {\n      lua_pushboolean(L, res);\n      return 1;\n    }\n    default: {\n      lua_pushnumber(L, res);\n      return 1;\n    }\n  }\n}\n\n\nstatic int luaB_type (lua_State *L) {\n  luaL_checkany(L, 1);\n  lua_pushstring(L, luaL_typename(L, 1));\n  return 1;\n}\n\n\nstatic int luaB_next (lua_State *L) {\n  luaL_checktype(L, 1, LUA_TTABLE);\n  lua_settop(L, 2);  /* create a 2nd argument if there isn't one */\n  if (lua_next(L, 1))\n    return 2;\n  else {\n    lua_pushnil(L);\n    return 1;\n  }\n}\n\n\nstatic int luaB_pairs (lua_State *L) {\n  luaL_checktype(L, 1, LUA_TTABLE);\n  lua_pushvalue(L, lua_upvalueindex(1));  /* return generator, */\n  lua_pushvalue(L, 1);  /* state, */\n  lua_pushnil(L);  /* and initial value */\n  return 3;\n}\n\n\nstatic int ipairsaux (lua_State *L) {\n  int i = luaL_checkint(L, 2);\n  luaL_checktype(L, 1, LUA_TTABLE);\n  i++;  /* next value */\n  lua_pushinteger(L, i);\n  lua_rawgeti(L, 1, i);\n  return (lua_isnil(L, -1)) ? 0 : 2;\n}\n\n\nstatic int luaB_ipairs (lua_State *L) {\n  luaL_checktype(L, 1, LUA_TTABLE);\n  lua_pushvalue(L, lua_upvalueindex(1));  /* return generator, */\n  lua_pushvalue(L, 1);  /* state, */\n  lua_pushinteger(L, 0);  /* and initial value */\n  return 3;\n}\n\n\nstatic int luaB_assert (lua_State *L) {\n  luaL_checkany(L, 1);\n  if (!lua_toboolean(L, 1))\n    return luaL_error(L, \"%s\", luaL_optstring(L, 2, \"assertion failed!\"));\n  return lua_gettop(L);\n}\n\n\nstatic int luaB_unpack (lua_State *L) {\n  int i, e, n;\n  luaL_checktype(L, 1, LUA_TTABLE);\n  i = luaL_optint(L, 2, 1);\n  e = luaL_opt(L, luaL_checkint, 3, luaL_getn(L, 1));\n  if (i > e) return 0;  /* empty range */\n  n = e - i + 1;  /* number of elements */\n  if (n <= 0 || !lua_checkstack(L, n))  /* n <= 0 means arith. overflow */\n    return luaL_error(L, \"too many results to unpack\");\n  lua_rawgeti(L, 1, i);  /* push arg[i] (avoiding overflow problems) */\n  while (i++ < e)  /* push arg[i + 1...e] */\n    lua_rawgeti(L, 1, i);\n  return n;\n}\n\n\nstatic int luaB_select (lua_State *L) {\n  int n = lua_gettop(L);\n  if (lua_type(L, 1) == LUA_TSTRING && *lua_tostring(L, 1) == '#') {\n    lua_pushinteger(L, n-1);\n    return 1;\n  }\n  else {\n    int i = luaL_checkint(L, 1);\n    if (i < 0) i = n + i;\n    else if (i > n) i = n;\n    luaL_argcheck(L, 1 <= i, 1, \"index out of range\");\n    return n - i;\n  }\n}\n\n\nstatic int luaB_pcall (lua_State *L) {\n  int status;\n  luaL_checkany(L, 1);\n  status = lua_pcall(L, lua_gettop(L) - 1, LUA_MULTRET, 0);\n  lua_pushboolean(L, (status == 0));\n  lua_insert(L, 1);\n  return lua_gettop(L);  /* return status + all results */\n}\n\n\nstatic int luaB_xpcall (lua_State *L) {\n  int status;\n  luaL_checkany(L, 2);\n  lua_settop(L, 2);\n  lua_insert(L, 1);  /* put error function under function to be called */\n  status = lua_pcall(L, 0, LUA_MULTRET, 1);\n  lua_pushboolean(L, (status == 0));\n  lua_replace(L, 1);\n  return lua_gettop(L);  /* return status + all results */\n}\n\n\nstatic int luaB_tostring (lua_State *L) {\n  luaL_checkany(L, 1);\n  if (luaL_callmeta(L, 1, \"__tostring\"))  /* is there a metafield? */\n    return 1;  /* use its value */\n  switch (lua_type(L, 1)) {\n    case LUA_TNUMBER:\n      lua_pushstring(L, lua_tostring(L, 1));\n      break;\n    case LUA_TSTRING:\n      lua_pushvalue(L, 1);\n      break;\n    case LUA_TBOOLEAN:\n      lua_pushstring(L, (lua_toboolean(L, 1) ? \"true\" : \"false\"));\n      break;\n    case LUA_TNIL:\n      lua_pushliteral(L, \"nil\");\n      break;\n    default:\n      lua_pushfstring(L, \"%s: %p\", luaL_typename(L, 1), lua_topointer(L, 1));\n      break;\n  }\n  return 1;\n}\n\n\nstatic int luaB_newproxy (lua_State *L) {\n  lua_settop(L, 1);\n  lua_newuserdata(L, 0);  /* create proxy */\n  if (lua_toboolean(L, 1) == 0)\n    return 1;  /* no metatable */\n  else if (lua_isboolean(L, 1)) {\n    lua_newtable(L);  /* create a new metatable `m' ... */\n    lua_pushvalue(L, -1);  /* ... and mark `m' as a valid metatable */\n    lua_pushboolean(L, 1);\n    lua_rawset(L, lua_upvalueindex(1));  /* weaktable[m] = true */\n  }\n  else {\n    int validproxy = 0;  /* to check if weaktable[metatable(u)] == true */\n    if (lua_getmetatable(L, 1)) {\n      lua_rawget(L, lua_upvalueindex(1));\n      validproxy = lua_toboolean(L, -1);\n      lua_pop(L, 1);  /* remove value */\n    }\n    luaL_argcheck(L, validproxy, 1, \"boolean or proxy expected\");\n    lua_getmetatable(L, 1);  /* metatable is valid; get it */\n  }\n  lua_setmetatable(L, 2);\n  return 1;\n}\n\n\nstatic const luaL_Reg base_funcs[] = {\n  {\"assert\", luaB_assert},\n  {\"collectgarbage\", luaB_collectgarbage},\n  /* no 'dofile' without sandboxing it */\n  {\"error\", luaB_error},\n  {\"gcinfo\", luaB_gcinfo},\n  {\"getfenv\", luaB_getfenv},\n  {\"getmetatable\", luaB_getmetatable},\n  /* no 'loadfile' without sandboxing it */\n  /* no 'load' without sandboxing it */\n  /* no 'loadstring' without sandboxing it */\n  {\"next\", luaB_next},\n  {\"pcall\", luaB_pcall},\n  {\"print\", luaB_print},\n  {\"rawequal\", luaB_rawequal},\n  {\"rawget\", luaB_rawget},\n  {\"rawset\", luaB_rawset},\n  {\"select\", luaB_select},\n  {\"setfenv\", luaB_setfenv},\n  {\"setmetatable\", luaB_setmetatable},\n  {\"tonumber\", luaB_tonumber},\n  {\"tostring\", luaB_tostring},\n  {\"type\", luaB_type},\n  {\"unpack\", luaB_unpack},\n  {\"xpcall\", luaB_xpcall},\n  {NULL, NULL}\n};\n\n\n/*\n** {======================================================\n** Coroutine library\n** =======================================================\n*/\n\n#define CO_RUN\t0\t/* running */\n#define CO_SUS\t1\t/* suspended */\n#define CO_NOR\t2\t/* 'normal' (it resumed another coroutine) */\n#define CO_DEAD\t3\n\nstatic const char *const statnames[] =\n    {\"running\", \"suspended\", \"normal\", \"dead\"};\n\nstatic int costatus (lua_State *L, lua_State *co) {\n  if (L == co) return CO_RUN;\n  switch (lua_status(co)) {\n    case LUA_YIELD:\n      return CO_SUS;\n    case 0: {\n      lua_Debug ar;\n      if (lua_getstack(co, 0, &ar) > 0)  /* does it have frames? */\n        return CO_NOR;  /* it is running */\n      else if (lua_gettop(co) == 0)\n          return CO_DEAD;\n      else\n        return CO_SUS;  /* initial state */\n    }\n    default:  /* some error occured */\n      return CO_DEAD;\n  }\n}\n\n\nstatic int luaB_costatus (lua_State *L) {\n  lua_State *co = lua_tothread(L, 1);\n  luaL_argcheck(L, co, 1, \"coroutine expected\");\n  lua_pushstring(L, statnames[costatus(L, co)]);\n  return 1;\n}\n\n\nstatic int auxresume (lua_State *L, lua_State *co, int narg) {\n  int status = costatus(L, co);\n  if (!lua_checkstack(co, narg))\n    luaL_error(L, \"too many arguments to resume\");\n  if (status != CO_SUS) {\n    lua_pushfstring(L, \"cannot resume %s coroutine\", statnames[status]);\n    return -1;  /* error flag */\n  }\n  lua_xmove(L, co, narg);\n  lua_setlevel(L, co);\n  status = lua_resume(co, narg);\n  if (status == 0 || status == LUA_YIELD) {\n    int nres = lua_gettop(co);\n    if (!lua_checkstack(L, nres + 1))\n      luaL_error(L, \"too many results to resume\");\n    lua_xmove(co, L, nres);  /* move yielded values */\n    return nres;\n  }\n  else {\n    lua_xmove(co, L, 1);  /* move error message */\n    return -1;  /* error flag */\n  }\n}\n\n\nstatic int luaB_coresume (lua_State *L) {\n  lua_State *co = lua_tothread(L, 1);\n  int r;\n  luaL_argcheck(L, co, 1, \"coroutine expected\");\n  r = auxresume(L, co, lua_gettop(L) - 1);\n  if (r < 0) {\n    lua_pushboolean(L, 0);\n    lua_insert(L, -2);\n    return 2;  /* return false + error message */\n  }\n  else {\n    lua_pushboolean(L, 1);\n    lua_insert(L, -(r + 1));\n    return r + 1;  /* return true + `resume' returns */\n  }\n}\n\n\nstatic int luaB_auxwrap (lua_State *L) {\n  lua_State *co = lua_tothread(L, lua_upvalueindex(1));\n  int r = auxresume(L, co, lua_gettop(L));\n  if (r < 0) {\n    if (lua_isstring(L, -1)) {  /* error object is a string? */\n      luaL_where(L, 1);  /* add extra info */\n      lua_insert(L, -2);\n      lua_concat(L, 2);\n    }\n    lua_error(L);  /* propagate error */\n  }\n  return r;\n}\n\n\nstatic int luaB_cocreate (lua_State *L) {\n  lua_State *NL = lua_newthread(L);\n  luaL_argcheck(L, lua_isfunction(L, 1) && !lua_iscfunction(L, 1), 1,\n    \"Lua function expected\");\n  lua_pushvalue(L, 1);  /* move function to top */\n  lua_xmove(L, NL, 1);  /* move function from L to NL */\n  return 1;\n}\n\n\nstatic int luaB_cowrap (lua_State *L) {\n  luaB_cocreate(L);\n  lua_pushcclosure(L, luaB_auxwrap, 1);\n  return 1;\n}\n\n\nstatic int luaB_yield (lua_State *L) {\n  return lua_yield(L, lua_gettop(L));\n}\n\n\nstatic int luaB_corunning (lua_State *L) {\n  if (lua_pushthread(L))\n    lua_pushnil(L);  /* main thread is not a coroutine */\n  return 1;\n}\n\n\nstatic const luaL_Reg co_funcs[] = {\n  {\"create\", luaB_cocreate},\n  {\"resume\", luaB_coresume},\n  {\"running\", luaB_corunning},\n  {\"status\", luaB_costatus},\n  {\"wrap\", luaB_cowrap},\n  {\"yield\", luaB_yield},\n  {NULL, NULL}\n};\n\n/* }====================================================== */\n\n\nstatic void auxopen (lua_State *L, const char *name,\n                     lua_CFunction f, lua_CFunction u) {\n  lua_pushcfunction(L, u);\n  lua_pushcclosure(L, f, 1);\n  lua_setfield(L, -2, name);\n}\n\n\nstatic void base_open (lua_State *L) {\n  /* set global _G */\n  lua_pushvalue(L, LUA_GLOBALSINDEX);\n  lua_setglobal(L, \"_G\");\n  /* open lib into global table */\n  luaL_register(L, \"_G\", base_funcs);\n  lua_pushliteral(L, LUA_VERSION);\n  lua_setglobal(L, \"_VERSION\");  /* set global _VERSION */\n  /* `ipairs' and `pairs' need auxiliary functions as upvalues */\n  auxopen(L, \"ipairs\", luaB_ipairs, ipairsaux);\n  auxopen(L, \"pairs\", luaB_pairs, luaB_next);\n  /* `newproxy' needs a weaktable as upvalue */\n  lua_createtable(L, 0, 1);  /* new table `w' */\n  lua_pushvalue(L, -1);  /* `w' will be its own metatable */\n  lua_setmetatable(L, -2);\n  lua_pushliteral(L, \"kv\");\n  lua_setfield(L, -2, \"__mode\");  /* metatable(w).__mode = \"kv\" */\n  lua_pushcclosure(L, luaB_newproxy, 1);\n  lua_setglobal(L, \"newproxy\");  /* set global `newproxy' */\n}\n\n\nLUALIB_API int luaopen_base (lua_State *L) {\n  base_open(L);\n  luaL_register(L, LUA_COLIBNAME, co_funcs);\n  return 2;\n}\n\n"
  },
  {
    "path": "src/lcode.c",
    "content": "/*\n** $Id: lcode.c,v 2.25.1.5 2011/01/31 14:53:16 roberto Exp $\n** Code generator for Lua\n** See Copyright Notice in lua.h\n*/\n\n\n#include <stdlib.h>\n\n#define lcode_c\n#define LUA_CORE\n\n#include \"lua.h\"\n\n#include \"lcode.h\"\n#include \"ldebug.h\"\n#include \"ldo.h\"\n#include \"lgc.h\"\n#include \"llex.h\"\n#include \"lmem.h\"\n#include \"lobject.h\"\n#include \"lopcodes.h\"\n#include \"lparser.h\"\n#include \"ltable.h\"\n\n\n#define hasjumps(e)\t((e)->t != (e)->f)\n\n\nstatic int isnumeral(expdesc *e) {\n  return (e->k == VKNUM && e->t == NO_JUMP && e->f == NO_JUMP);\n}\n\n\nvoid luaK_nil (FuncState *fs, int from, int n) {\n  Instruction *previous;\n  if (fs->pc > fs->lasttarget) {  /* no jumps to current position? */\n    if (fs->pc == 0) {  /* function start? */\n      if (from >= fs->nactvar)\n        return;  /* positions are already clean */\n    }\n    else {\n      previous = &fs->f->code[fs->pc-1];\n      if (GET_OPCODE(*previous) == OP_LOADNIL) {\n        int pfrom = GETARG_A(*previous);\n        int pto = GETARG_B(*previous);\n        if (pfrom <= from && from <= pto+1) {  /* can connect both? */\n          if (from+n-1 > pto)\n            SETARG_B(*previous, from+n-1);\n          return;\n        }\n      }\n    }\n  }\n  luaK_codeABC(fs, OP_LOADNIL, from, from+n-1, 0);  /* else no optimization */\n}\n\n\nint luaK_jump (FuncState *fs) {\n  int jpc = fs->jpc;  /* save list of jumps to here */\n  int j;\n  fs->jpc = NO_JUMP;\n  j = luaK_codeAsBx(fs, OP_JMP, 0, NO_JUMP);\n  luaK_concat(fs, &j, jpc);  /* keep them on hold */\n  return j;\n}\n\n\nvoid luaK_ret (FuncState *fs, int first, int nret) {\n  luaK_codeABC(fs, OP_RETURN, first, nret+1, 0);\n}\n\n\nstatic int condjump (FuncState *fs, OpCode op, int A, int B, int C) {\n  luaK_codeABC(fs, op, A, B, C);\n  return luaK_jump(fs);\n}\n\n\nstatic void fixjump (FuncState *fs, int pc, int dest) {\n  Instruction *jmp = &fs->f->code[pc];\n  int offset = dest-(pc+1);\n  lua_assert(dest != NO_JUMP);\n  if (abs(offset) > MAXARG_sBx)\n    luaX_syntaxerror(fs->ls, \"control structure too long\");\n  SETARG_sBx(*jmp, offset);\n}\n\n\n/*\n** returns current `pc' and marks it as a jump target (to avoid wrong\n** optimizations with consecutive instructions not in the same basic block).\n*/\nint luaK_getlabel (FuncState *fs) {\n  fs->lasttarget = fs->pc;\n  return fs->pc;\n}\n\n\nstatic int getjump (FuncState *fs, int pc) {\n  int offset = GETARG_sBx(fs->f->code[pc]);\n  if (offset == NO_JUMP)  /* point to itself represents end of list */\n    return NO_JUMP;  /* end of list */\n  else\n    return (pc+1)+offset;  /* turn offset into absolute position */\n}\n\n\nstatic Instruction *getjumpcontrol (FuncState *fs, int pc) {\n  Instruction *pi = &fs->f->code[pc];\n  if (pc >= 1 && testTMode(GET_OPCODE(*(pi-1))))\n    return pi-1;\n  else\n    return pi;\n}\n\n\n/*\n** check whether list has any jump that do not produce a value\n** (or produce an inverted value)\n*/\nstatic int need_value (FuncState *fs, int list) {\n  for (; list != NO_JUMP; list = getjump(fs, list)) {\n    Instruction i = *getjumpcontrol(fs, list);\n    if (GET_OPCODE(i) != OP_TESTSET) return 1;\n  }\n  return 0;  /* not found */\n}\n\n\nstatic int patchtestreg (FuncState *fs, int node, int reg) {\n  Instruction *i = getjumpcontrol(fs, node);\n  if (GET_OPCODE(*i) != OP_TESTSET)\n    return 0;  /* cannot patch other instructions */\n  if (reg != NO_REG && reg != GETARG_B(*i))\n    SETARG_A(*i, reg);\n  else  /* no register to put value or register already has the value */\n    *i = CREATE_ABC(OP_TEST, GETARG_B(*i), 0, GETARG_C(*i));\n\n  return 1;\n}\n\n\nstatic void removevalues (FuncState *fs, int list) {\n  for (; list != NO_JUMP; list = getjump(fs, list))\n      patchtestreg(fs, list, NO_REG);\n}\n\n\nstatic void patchlistaux (FuncState *fs, int list, int vtarget, int reg,\n                          int dtarget) {\n  while (list != NO_JUMP) {\n    int next = getjump(fs, list);\n    if (patchtestreg(fs, list, reg))\n      fixjump(fs, list, vtarget);\n    else\n      fixjump(fs, list, dtarget);  /* jump to default target */\n    list = next;\n  }\n}\n\n\nstatic void dischargejpc (FuncState *fs) {\n  patchlistaux(fs, fs->jpc, fs->pc, NO_REG, fs->pc);\n  fs->jpc = NO_JUMP;\n}\n\n\nvoid luaK_patchlist (FuncState *fs, int list, int target) {\n  if (target == fs->pc)\n    luaK_patchtohere(fs, list);\n  else {\n    lua_assert(target < fs->pc);\n    patchlistaux(fs, list, target, NO_REG, target);\n  }\n}\n\n\nvoid luaK_patchtohere (FuncState *fs, int list) {\n  luaK_getlabel(fs);\n  luaK_concat(fs, &fs->jpc, list);\n}\n\n\nvoid luaK_concat (FuncState *fs, int *l1, int l2) {\n  if (l2 == NO_JUMP) return;\n  else if (*l1 == NO_JUMP)\n    *l1 = l2;\n  else {\n    int list = *l1;\n    int next;\n    while ((next = getjump(fs, list)) != NO_JUMP)  /* find last element */\n      list = next;\n    fixjump(fs, list, l2);\n  }\n}\n\n\nvoid luaK_checkstack (FuncState *fs, int n) {\n  int newstack = fs->freereg + n;\n  if (newstack > fs->f->maxstacksize) {\n    if (newstack >= MAXSTACK)\n      luaX_syntaxerror(fs->ls, \"function or expression too complex\");\n    fs->f->maxstacksize = cast_byte(newstack);\n  }\n}\n\n\nvoid luaK_reserveregs (FuncState *fs, int n) {\n  luaK_checkstack(fs, n);\n  fs->freereg += n;\n}\n\n\nstatic void freereg (FuncState *fs, int reg) {\n  if (!ISK(reg) && reg >= fs->nactvar) {\n    fs->freereg--;\n    lua_assert(reg == fs->freereg);\n  }\n}\n\n\nstatic void freeexp (FuncState *fs, expdesc *e) {\n  if (e->k == VNONRELOC)\n    freereg(fs, e->u.s.info);\n}\n\n\nstatic int addk (FuncState *fs, TValue *k, TValue *v) {\n  lua_State *L = fs->L;\n  TValue *idx = luaH_set(L, fs->h, k);\n  Proto *f = fs->f;\n  int oldsize = f->sizek;\n  if (ttisnumber(idx)) {\n    lua_assert(luaO_rawequalObj(&fs->f->k[cast_int(nvalue(idx))], v));\n    return cast_int(nvalue(idx));\n  }\n  else {  /* constant not found; create a new entry */\n    setnvalue(idx, cast_num(fs->nk));\n    luaM_growvector(L, f->k, fs->nk, f->sizek, TValue,\n                    MAXARG_Bx, \"constant table overflow\");\n    while (oldsize < f->sizek) setnilvalue(&f->k[oldsize++]);\n    setobj(L, &f->k[fs->nk], v);\n    luaC_barrier(L, f, v);\n    return fs->nk++;\n  }\n}\n\n\nint luaK_stringK (FuncState *fs, TString *s) {\n  TValue o;\n  setsvalue(fs->L, &o, s);\n  return addk(fs, &o, &o);\n}\n\n\nint luaK_numberK (FuncState *fs, lua_Number r) {\n  TValue o;\n  setnvalue(&o, r);\n  return addk(fs, &o, &o);\n}\n\n\nstatic int boolK (FuncState *fs, int b) {\n  TValue o;\n  setbvalue(&o, b);\n  return addk(fs, &o, &o);\n}\n\n\nstatic int nilK (FuncState *fs) {\n  TValue k, v;\n  setnilvalue(&v);\n  /* cannot use nil as key; instead use table itself to represent nil */\n  sethvalue(fs->L, &k, fs->h);\n  return addk(fs, &k, &v);\n}\n\n\nvoid luaK_setreturns (FuncState *fs, expdesc *e, int nresults) {\n  if (e->k == VCALL) {  /* expression is an open function call? */\n    SETARG_C(getcode(fs, e), nresults+1);\n  }\n  else if (e->k == VVARARG) {\n    SETARG_B(getcode(fs, e), nresults+1);\n    SETARG_A(getcode(fs, e), fs->freereg);\n    luaK_reserveregs(fs, 1);\n  }\n}\n\n\nvoid luaK_setoneret (FuncState *fs, expdesc *e) {\n  if (e->k == VCALL) {  /* expression is an open function call? */\n    e->k = VNONRELOC;\n    e->u.s.info = GETARG_A(getcode(fs, e));\n  }\n  else if (e->k == VVARARG) {\n    SETARG_B(getcode(fs, e), 2);\n    e->k = VRELOCABLE;  /* can relocate its simple result */\n  }\n}\n\n\nvoid luaK_dischargevars (FuncState *fs, expdesc *e) {\n  switch (e->k) {\n    case VLOCAL: {\n      e->k = VNONRELOC;\n      break;\n    }\n    case VUPVAL: {\n      e->u.s.info = luaK_codeABC(fs, OP_GETUPVAL, 0, e->u.s.info, 0);\n      e->k = VRELOCABLE;\n      break;\n    }\n    case VGLOBAL: {\n      e->u.s.info = luaK_codeABx(fs, OP_GETGLOBAL, 0, e->u.s.info);\n      e->k = VRELOCABLE;\n      break;\n    }\n    case VINDEXED: {\n      freereg(fs, e->u.s.aux);\n      freereg(fs, e->u.s.info);\n      e->u.s.info = luaK_codeABC(fs, OP_GETTABLE, 0, e->u.s.info, e->u.s.aux);\n      e->k = VRELOCABLE;\n      break;\n    }\n    case VVARARG:\n    case VCALL: {\n      luaK_setoneret(fs, e);\n      break;\n    }\n    default: break;  /* there is one value available (somewhere) */\n  }\n}\n\n\nstatic int code_label (FuncState *fs, int A, int b, int jump) {\n  luaK_getlabel(fs);  /* those instructions may be jump targets */\n  return luaK_codeABC(fs, OP_LOADBOOL, A, b, jump);\n}\n\n\nstatic void discharge2reg (FuncState *fs, expdesc *e, int reg) {\n  luaK_dischargevars(fs, e);\n  switch (e->k) {\n    case VNIL: {\n      luaK_nil(fs, reg, 1);\n      break;\n    }\n    case VFALSE:  case VTRUE: {\n      luaK_codeABC(fs, OP_LOADBOOL, reg, e->k == VTRUE, 0);\n      break;\n    }\n    case VK: {\n      luaK_codeABx(fs, OP_LOADK, reg, e->u.s.info);\n      break;\n    }\n    case VKNUM: {\n      luaK_codeABx(fs, OP_LOADK, reg, luaK_numberK(fs, e->u.nval));\n      break;\n    }\n    case VRELOCABLE: {\n      Instruction *pc = &getcode(fs, e);\n      SETARG_A(*pc, reg);\n      break;\n    }\n    case VNONRELOC: {\n      if (reg != e->u.s.info)\n        luaK_codeABC(fs, OP_MOVE, reg, e->u.s.info, 0);\n      break;\n    }\n    default: {\n      lua_assert(e->k == VVOID || e->k == VJMP);\n      return;  /* nothing to do... */\n    }\n  }\n  e->u.s.info = reg;\n  e->k = VNONRELOC;\n}\n\n\nstatic void discharge2anyreg (FuncState *fs, expdesc *e) {\n  if (e->k != VNONRELOC) {\n    luaK_reserveregs(fs, 1);\n    discharge2reg(fs, e, fs->freereg-1);\n  }\n}\n\n\nstatic void exp2reg (FuncState *fs, expdesc *e, int reg) {\n  discharge2reg(fs, e, reg);\n  if (e->k == VJMP)\n    luaK_concat(fs, &e->t, e->u.s.info);  /* put this jump in `t' list */\n  if (hasjumps(e)) {\n    int final;  /* position after whole expression */\n    int p_f = NO_JUMP;  /* position of an eventual LOAD false */\n    int p_t = NO_JUMP;  /* position of an eventual LOAD true */\n    if (need_value(fs, e->t) || need_value(fs, e->f)) {\n      int fj = (e->k == VJMP) ? NO_JUMP : luaK_jump(fs);\n      p_f = code_label(fs, reg, 0, 1);\n      p_t = code_label(fs, reg, 1, 0);\n      luaK_patchtohere(fs, fj);\n    }\n    final = luaK_getlabel(fs);\n    patchlistaux(fs, e->f, final, reg, p_f);\n    patchlistaux(fs, e->t, final, reg, p_t);\n  }\n  e->f = e->t = NO_JUMP;\n  e->u.s.info = reg;\n  e->k = VNONRELOC;\n}\n\n\nvoid luaK_exp2nextreg (FuncState *fs, expdesc *e) {\n  luaK_dischargevars(fs, e);\n  freeexp(fs, e);\n  luaK_reserveregs(fs, 1);\n  exp2reg(fs, e, fs->freereg - 1);\n}\n\n\nint luaK_exp2anyreg (FuncState *fs, expdesc *e) {\n  luaK_dischargevars(fs, e);\n  if (e->k == VNONRELOC) {\n    if (!hasjumps(e)) return e->u.s.info;  /* exp is already in a register */\n    if (e->u.s.info >= fs->nactvar) {  /* reg. is not a local? */\n      exp2reg(fs, e, e->u.s.info);  /* put value on it */\n      return e->u.s.info;\n    }\n  }\n  luaK_exp2nextreg(fs, e);  /* default */\n  return e->u.s.info;\n}\n\n\nvoid luaK_exp2val (FuncState *fs, expdesc *e) {\n  if (hasjumps(e))\n    luaK_exp2anyreg(fs, e);\n  else\n    luaK_dischargevars(fs, e);\n}\n\n\nint luaK_exp2RK (FuncState *fs, expdesc *e) {\n  luaK_exp2val(fs, e);\n  switch (e->k) {\n    case VKNUM:\n    case VTRUE:\n    case VFALSE:\n    case VNIL: {\n      if (fs->nk <= MAXINDEXRK) {  /* constant fit in RK operand? */\n        e->u.s.info = (e->k == VNIL)  ? nilK(fs) :\n                      (e->k == VKNUM) ? luaK_numberK(fs, e->u.nval) :\n                                        boolK(fs, (e->k == VTRUE));\n        e->k = VK;\n        return RKASK(e->u.s.info);\n      }\n      else break;\n    }\n    case VK: {\n      if (e->u.s.info <= MAXINDEXRK)  /* constant fit in argC? */\n        return RKASK(e->u.s.info);\n      else break;\n    }\n    default: break;\n  }\n  /* not a constant in the right range: put it in a register */\n  return luaK_exp2anyreg(fs, e);\n}\n\n\nvoid luaK_storevar (FuncState *fs, expdesc *var, expdesc *ex) {\n  switch (var->k) {\n    case VLOCAL: {\n      freeexp(fs, ex);\n      exp2reg(fs, ex, var->u.s.info);\n      return;\n    }\n    case VUPVAL: {\n      int e = luaK_exp2anyreg(fs, ex);\n      luaK_codeABC(fs, OP_SETUPVAL, e, var->u.s.info, 0);\n      break;\n    }\n    case VGLOBAL: {\n      int e = luaK_exp2anyreg(fs, ex);\n      luaK_codeABx(fs, OP_SETGLOBAL, e, var->u.s.info);\n      break;\n    }\n    case VINDEXED: {\n      int e = luaK_exp2RK(fs, ex);\n      luaK_codeABC(fs, OP_SETTABLE, var->u.s.info, var->u.s.aux, e);\n      break;\n    }\n    default: {\n      lua_assert(0);  /* invalid var kind to store */\n      break;\n    }\n  }\n  freeexp(fs, ex);\n}\n\n\nvoid luaK_self (FuncState *fs, expdesc *e, expdesc *key) {\n  int func;\n  luaK_exp2anyreg(fs, e);\n  freeexp(fs, e);\n  func = fs->freereg;\n  luaK_reserveregs(fs, 2);\n  luaK_codeABC(fs, OP_SELF, func, e->u.s.info, luaK_exp2RK(fs, key));\n  freeexp(fs, key);\n  e->u.s.info = func;\n  e->k = VNONRELOC;\n}\n\n\nstatic void invertjump (FuncState *fs, expdesc *e) {\n  Instruction *pc = getjumpcontrol(fs, e->u.s.info);\n  lua_assert(testTMode(GET_OPCODE(*pc)) && GET_OPCODE(*pc) != OP_TESTSET &&\n                                           GET_OPCODE(*pc) != OP_TEST);\n  SETARG_A(*pc, !(GETARG_A(*pc)));\n}\n\n\nstatic int jumponcond (FuncState *fs, expdesc *e, int cond) {\n  if (e->k == VRELOCABLE) {\n    Instruction ie = getcode(fs, e);\n    if (GET_OPCODE(ie) == OP_NOT) {\n      fs->pc--;  /* remove previous OP_NOT */\n      return condjump(fs, OP_TEST, GETARG_B(ie), 0, !cond);\n    }\n    /* else go through */\n  }\n  discharge2anyreg(fs, e);\n  freeexp(fs, e);\n  return condjump(fs, OP_TESTSET, NO_REG, e->u.s.info, cond);\n}\n\n\nvoid luaK_goiftrue (FuncState *fs, expdesc *e) {\n  int pc;  /* pc of last jump */\n  luaK_dischargevars(fs, e);\n  switch (e->k) {\n    case VK: case VKNUM: case VTRUE: {\n      pc = NO_JUMP;  /* always true; do nothing */\n      break;\n    }\n    case VJMP: {\n      invertjump(fs, e);\n      pc = e->u.s.info;\n      break;\n    }\n    default: {\n      pc = jumponcond(fs, e, 0);\n      break;\n    }\n  }\n  luaK_concat(fs, &e->f, pc);  /* insert last jump in `f' list */\n  luaK_patchtohere(fs, e->t);\n  e->t = NO_JUMP;\n}\n\n\nstatic void luaK_goiffalse (FuncState *fs, expdesc *e) {\n  int pc;  /* pc of last jump */\n  luaK_dischargevars(fs, e);\n  switch (e->k) {\n    case VNIL: case VFALSE: {\n      pc = NO_JUMP;  /* always false; do nothing */\n      break;\n    }\n    case VJMP: {\n      pc = e->u.s.info;\n      break;\n    }\n    default: {\n      pc = jumponcond(fs, e, 1);\n      break;\n    }\n  }\n  luaK_concat(fs, &e->t, pc);  /* insert last jump in `t' list */\n  luaK_patchtohere(fs, e->f);\n  e->f = NO_JUMP;\n}\n\n\nstatic void codenot (FuncState *fs, expdesc *e) {\n  luaK_dischargevars(fs, e);\n  switch (e->k) {\n    case VNIL: case VFALSE: {\n      e->k = VTRUE;\n      break;\n    }\n    case VK: case VKNUM: case VTRUE: {\n      e->k = VFALSE;\n      break;\n    }\n    case VJMP: {\n      invertjump(fs, e);\n      break;\n    }\n    case VRELOCABLE:\n    case VNONRELOC: {\n      discharge2anyreg(fs, e);\n      freeexp(fs, e);\n      e->u.s.info = luaK_codeABC(fs, OP_NOT, 0, e->u.s.info, 0);\n      e->k = VRELOCABLE;\n      break;\n    }\n    default: {\n      lua_assert(0);  /* cannot happen */\n      break;\n    }\n  }\n  /* interchange true and false lists */\n  { int temp = e->f; e->f = e->t; e->t = temp; }\n  removevalues(fs, e->f);\n  removevalues(fs, e->t);\n}\n\n\nvoid luaK_indexed (FuncState *fs, expdesc *t, expdesc *k) {\n  t->u.s.aux = luaK_exp2RK(fs, k);\n  t->k = VINDEXED;\n}\n\n\nstatic int constfolding (OpCode op, expdesc *e1, expdesc *e2) {\n  lua_Number v1, v2, r;\n  if (!isnumeral(e1) || !isnumeral(e2)) return 0;\n  v1 = e1->u.nval;\n  v2 = e2->u.nval;\n  switch (op) {\n    case OP_ADD: r = luai_numadd(v1, v2); break;\n    case OP_SUB: r = luai_numsub(v1, v2); break;\n    case OP_MUL: r = luai_nummul(v1, v2); break;\n    case OP_DIV:\n      if (v2 == 0) return 0;  /* do not attempt to divide by 0 */\n      r = luai_numdiv(v1, v2); break;\n    case OP_MOD:\n      if (v2 == 0) return 0;  /* do not attempt to divide by 0 */\n      r = luai_nummod(v1, v2); break;\n    case OP_POW: r = luai_numpow(v1, v2); break;\n    case OP_UNM: r = luai_numunm(v1); break;\n    case OP_LEN: return 0;  /* no constant folding for 'len' */\n    default: lua_assert(0); r = 0; break;\n  }\n  if (luai_numisnan(r)) return 0;  /* do not attempt to produce NaN */\n  e1->u.nval = r;\n  return 1;\n}\n\n\nstatic void codearith (FuncState *fs, OpCode op, expdesc *e1, expdesc *e2) {\n  if (constfolding(op, e1, e2))\n    return;\n  else {\n    int o2 = (op != OP_UNM && op != OP_LEN) ? luaK_exp2RK(fs, e2) : 0;\n    int o1 = luaK_exp2RK(fs, e1);\n    if (o1 > o2) {\n      freeexp(fs, e1);\n      freeexp(fs, e2);\n    }\n    else {\n      freeexp(fs, e2);\n      freeexp(fs, e1);\n    }\n    e1->u.s.info = luaK_codeABC(fs, op, 0, o1, o2);\n    e1->k = VRELOCABLE;\n  }\n}\n\n\nstatic void codecomp (FuncState *fs, OpCode op, int cond, expdesc *e1,\n                                                          expdesc *e2) {\n  int o1 = luaK_exp2RK(fs, e1);\n  int o2 = luaK_exp2RK(fs, e2);\n  freeexp(fs, e2);\n  freeexp(fs, e1);\n  if (cond == 0 && op != OP_EQ) {\n    int temp;  /* exchange args to replace by `<' or `<=' */\n    temp = o1; o1 = o2; o2 = temp;  /* o1 <==> o2 */\n    cond = 1;\n  }\n  e1->u.s.info = condjump(fs, op, cond, o1, o2);\n  e1->k = VJMP;\n}\n\n\nvoid luaK_prefix (FuncState *fs, UnOpr op, expdesc *e) {\n  expdesc e2;\n  e2.t = e2.f = NO_JUMP; e2.k = VKNUM; e2.u.nval = 0;\n  switch (op) {\n    case OPR_MINUS: {\n      if (!isnumeral(e))\n        luaK_exp2anyreg(fs, e);  /* cannot operate on non-numeric constants */\n      codearith(fs, OP_UNM, e, &e2);\n      break;\n    }\n    case OPR_NOT: codenot(fs, e); break;\n    case OPR_LEN: {\n      luaK_exp2anyreg(fs, e);  /* cannot operate on constants */\n      codearith(fs, OP_LEN, e, &e2);\n      break;\n    }\n    default: lua_assert(0);\n  }\n}\n\n\nvoid luaK_infix (FuncState *fs, BinOpr op, expdesc *v) {\n  switch (op) {\n    case OPR_AND: {\n      luaK_goiftrue(fs, v);\n      break;\n    }\n    case OPR_OR: {\n      luaK_goiffalse(fs, v);\n      break;\n    }\n    case OPR_CONCAT: {\n      luaK_exp2nextreg(fs, v);  /* operand must be on the `stack' */\n      break;\n    }\n    case OPR_ADD: case OPR_SUB: case OPR_MUL: case OPR_DIV:\n    case OPR_MOD: case OPR_POW: {\n      if (!isnumeral(v)) luaK_exp2RK(fs, v);\n      break;\n    }\n    default: {\n      luaK_exp2RK(fs, v);\n      break;\n    }\n  }\n}\n\n\nvoid luaK_posfix (FuncState *fs, BinOpr op, expdesc *e1, expdesc *e2) {\n  switch (op) {\n    case OPR_AND: {\n      lua_assert(e1->t == NO_JUMP);  /* list must be closed */\n      luaK_dischargevars(fs, e2);\n      luaK_concat(fs, &e2->f, e1->f);\n      *e1 = *e2;\n      break;\n    }\n    case OPR_OR: {\n      lua_assert(e1->f == NO_JUMP);  /* list must be closed */\n      luaK_dischargevars(fs, e2);\n      luaK_concat(fs, &e2->t, e1->t);\n      *e1 = *e2;\n      break;\n    }\n    case OPR_CONCAT: {\n      luaK_exp2val(fs, e2);\n      if (e2->k == VRELOCABLE && GET_OPCODE(getcode(fs, e2)) == OP_CONCAT) {\n        lua_assert(e1->u.s.info == GETARG_B(getcode(fs, e2))-1);\n        freeexp(fs, e1);\n        SETARG_B(getcode(fs, e2), e1->u.s.info);\n        e1->k = VRELOCABLE; e1->u.s.info = e2->u.s.info;\n      }\n      else {\n        luaK_exp2nextreg(fs, e2);  /* operand must be on the 'stack' */\n        codearith(fs, OP_CONCAT, e1, e2);\n      }\n      break;\n    }\n    case OPR_ADD: codearith(fs, OP_ADD, e1, e2); break;\n    case OPR_SUB: codearith(fs, OP_SUB, e1, e2); break;\n    case OPR_MUL: codearith(fs, OP_MUL, e1, e2); break;\n    case OPR_DIV: codearith(fs, OP_DIV, e1, e2); break;\n    case OPR_MOD: codearith(fs, OP_MOD, e1, e2); break;\n    case OPR_POW: codearith(fs, OP_POW, e1, e2); break;\n    case OPR_EQ: codecomp(fs, OP_EQ, 1, e1, e2); break;\n    case OPR_NE: codecomp(fs, OP_EQ, 0, e1, e2); break;\n    case OPR_LT: codecomp(fs, OP_LT, 1, e1, e2); break;\n    case OPR_LE: codecomp(fs, OP_LE, 1, e1, e2); break;\n    case OPR_GT: codecomp(fs, OP_LT, 0, e1, e2); break;\n    case OPR_GE: codecomp(fs, OP_LE, 0, e1, e2); break;\n    default: lua_assert(0);\n  }\n}\n\n\nvoid luaK_fixline (FuncState *fs, int line) {\n  fs->f->lineinfo[fs->pc - 1] = line;\n}\n\n\nstatic int luaK_code (FuncState *fs, Instruction i, int line) {\n  Proto *f = fs->f;\n  dischargejpc(fs);  /* `pc' will change */\n  /* put new instruction in code array */\n  luaM_growvector(fs->L, f->code, fs->pc, f->sizecode, Instruction,\n                  MAX_INT, \"code size overflow\");\n  f->code[fs->pc] = i;\n  /* save corresponding line information */\n  luaM_growvector(fs->L, f->lineinfo, fs->pc, f->sizelineinfo, int,\n                  MAX_INT, \"code size overflow\");\n  f->lineinfo[fs->pc] = line;\n  return fs->pc++;\n}\n\n\nint luaK_codeABC (FuncState *fs, OpCode o, int a, int b, int c) {\n  lua_assert(getOpMode(o) == iABC);\n  lua_assert(getBMode(o) != OpArgN || b == 0);\n  lua_assert(getCMode(o) != OpArgN || c == 0);\n  return luaK_code(fs, CREATE_ABC(o, a, b, c), fs->ls->lastline);\n}\n\n\nint luaK_codeABx (FuncState *fs, OpCode o, int a, unsigned int bc) {\n  lua_assert(getOpMode(o) == iABx || getOpMode(o) == iAsBx);\n  lua_assert(getCMode(o) == OpArgN);\n  return luaK_code(fs, CREATE_ABx(o, a, bc), fs->ls->lastline);\n}\n\n\nvoid luaK_setlist (FuncState *fs, int base, int nelems, int tostore) {\n  int c =  (nelems - 1)/LFIELDS_PER_FLUSH + 1;\n  int b = (tostore == LUA_MULTRET) ? 0 : tostore;\n  lua_assert(tostore != 0);\n  if (c <= MAXARG_C)\n    luaK_codeABC(fs, OP_SETLIST, base, b, c);\n  else {\n    luaK_codeABC(fs, OP_SETLIST, base, b, 0);\n    luaK_code(fs, cast(Instruction, c), fs->ls->lastline);\n  }\n  fs->freereg = base + 1;  /* free registers with list values */\n}\n\n"
  },
  {
    "path": "src/lcode.h",
    "content": "/*\n** $Id: lcode.h,v 1.48.1.1 2007/12/27 13:02:25 roberto Exp $\n** Code generator for Lua\n** See Copyright Notice in lua.h\n*/\n\n#ifndef lcode_h\n#define lcode_h\n\n#include \"llex.h\"\n#include \"lobject.h\"\n#include \"lopcodes.h\"\n#include \"lparser.h\"\n\n\n/*\n** Marks the end of a patch list. It is an invalid value both as an absolute\n** address, and as a list link (would link an element to itself).\n*/\n#define NO_JUMP (-1)\n\n\n/*\n** grep \"ORDER OPR\" if you change these enums\n*/\ntypedef enum BinOpr {\n  OPR_ADD, OPR_SUB, OPR_MUL, OPR_DIV, OPR_MOD, OPR_POW,\n  OPR_CONCAT,\n  OPR_NE, OPR_EQ,\n  OPR_LT, OPR_LE, OPR_GT, OPR_GE,\n  OPR_AND, OPR_OR,\n  OPR_NOBINOPR\n} BinOpr;\n\n\ntypedef enum UnOpr { OPR_MINUS, OPR_NOT, OPR_LEN, OPR_NOUNOPR } UnOpr;\n\n\n#define getcode(fs,e)\t((fs)->f->code[(e)->u.s.info])\n\n#define luaK_codeAsBx(fs,o,A,sBx)\tluaK_codeABx(fs,o,A,(sBx)+MAXARG_sBx)\n\n#define luaK_setmultret(fs,e)\tluaK_setreturns(fs, e, LUA_MULTRET)\n\nLUAI_FUNC int luaK_codeABx (FuncState *fs, OpCode o, int A, unsigned int Bx);\nLUAI_FUNC int luaK_codeABC (FuncState *fs, OpCode o, int A, int B, int C);\nLUAI_FUNC void luaK_fixline (FuncState *fs, int line);\nLUAI_FUNC void luaK_nil (FuncState *fs, int from, int n);\nLUAI_FUNC void luaK_reserveregs (FuncState *fs, int n);\nLUAI_FUNC void luaK_checkstack (FuncState *fs, int n);\nLUAI_FUNC int luaK_stringK (FuncState *fs, TString *s);\nLUAI_FUNC int luaK_numberK (FuncState *fs, lua_Number r);\nLUAI_FUNC void luaK_dischargevars (FuncState *fs, expdesc *e);\nLUAI_FUNC int luaK_exp2anyreg (FuncState *fs, expdesc *e);\nLUAI_FUNC void luaK_exp2nextreg (FuncState *fs, expdesc *e);\nLUAI_FUNC void luaK_exp2val (FuncState *fs, expdesc *e);\nLUAI_FUNC int luaK_exp2RK (FuncState *fs, expdesc *e);\nLUAI_FUNC void luaK_self (FuncState *fs, expdesc *e, expdesc *key);\nLUAI_FUNC void luaK_indexed (FuncState *fs, expdesc *t, expdesc *k);\nLUAI_FUNC void luaK_goiftrue (FuncState *fs, expdesc *e);\nLUAI_FUNC void luaK_storevar (FuncState *fs, expdesc *var, expdesc *e);\nLUAI_FUNC void luaK_setreturns (FuncState *fs, expdesc *e, int nresults);\nLUAI_FUNC void luaK_setoneret (FuncState *fs, expdesc *e);\nLUAI_FUNC int luaK_jump (FuncState *fs);\nLUAI_FUNC void luaK_ret (FuncState *fs, int first, int nret);\nLUAI_FUNC void luaK_patchlist (FuncState *fs, int list, int target);\nLUAI_FUNC void luaK_patchtohere (FuncState *fs, int list);\nLUAI_FUNC void luaK_concat (FuncState *fs, int *l1, int l2);\nLUAI_FUNC int luaK_getlabel (FuncState *fs);\nLUAI_FUNC void luaK_prefix (FuncState *fs, UnOpr op, expdesc *v);\nLUAI_FUNC void luaK_infix (FuncState *fs, BinOpr op, expdesc *v);\nLUAI_FUNC void luaK_posfix (FuncState *fs, BinOpr op, expdesc *v1, expdesc *v2);\nLUAI_FUNC void luaK_setlist (FuncState *fs, int base, int nelems, int tostore);\n\n\n#endif\n"
  },
  {
    "path": "src/lcurses/Makefile",
    "content": "curses.o: curses.c window.c chstr.c _helpers.c compat-5.2.c compat-5.2.h strlcpy.c\n"
  },
  {
    "path": "src/lcurses/_helpers.c",
    "content": "/*\n * POSIX library for Lua 5.1, 5.2 & 5.3.\n * (c) Gary V. Vaughan <gary@vaughan.pe>, 2013-2017\n * (c) Reuben Thomas <rrt@sc3d.org> 2010-2013\n * (c) Natanael Copa <natanael.copa@gmail.com> 2008-2010\n * Clean up and bug fixes by Leo Razoumov <slonik.az@gmail.com> 2006-10-11\n * Luiz Henrique de Figueiredo <lhf@tecgraf.puc-rio.br> 07 Apr 2006 23:17:49\n * Based on original by Claudio Terra for Lua 3.x.\n * With contributions by Roberto Ierusalimschy.\n * With documentation from Steve Donovan 2012\n */\n\n#ifndef LCURSES__HELPERS_C\n#define LCURSES__HELPERS_C 1\n\n#include <errno.h>\n#include <grp.h>\n#include <pwd.h>\n#include <stdlib.h>\n#include <string.h>\n#include <sys/stat.h>\n#include <unistd.h>\t\t/* for _POSIX_VERSION */\n\n#ifdef __NetBSD__\n#include <curses.h>\n#else\n#include <ncurses.h>\n#endif\n#include <term.h>\n\n#include \"../lua.h\"\n#include \"../lualib.h\"\n#include \"../lauxlib.h\"\n\n#if LUA_VERSION_NUM < 503\n#  define lua_isinteger lua_isnumber\n#  if LUA_VERSION_NUM == 501\n#    include \"compat-5.2.c\"\n#  endif\n#endif\n\n#if LUA_VERSION_NUM == 502 || LUA_VERSION_NUM == 503\n#  define lua_objlen lua_rawlen\n#  define lua_strlen lua_rawlen\n#  define luaL_openlib(L,n,l,nup) luaL_setfuncs((L),(l),(nup))\n#  define luaL_register(L,n,l) (luaL_newlib(L,l))\n#endif\n\n#ifndef STREQ\n#  define STREQ(a, b)     (strcmp (a, b) == 0)\n#endif\n\n/* Mark unused parameters required only to match a function type\n   specification. */\n#ifdef __GNUC__\n#  define LCURSES_UNUSED(x) UNUSED_ ## x __attribute__((__unused__))\n#else\n#  define LCURSES_UNUSED(x) UNUSED_ ## x\n#endif\n\n/* LCURSES_STMT_BEG/END are used to create macros that expand to a\n   single compound statement in a portable way. */\n#if defined __GNUC__ && !defined __STRICT_ANSI__ && !defined __cplusplus\n#  define LCURSES_STMT_BEG\t(void)(\n#  define LCURSES_STMT_END\t)\n#else\n#  if (defined sun || defined __sun__)\n#    define LCURSES_STMT_BEG\tif (1)\n#    define LCURSES_STMT_END\telse (void)0\n#  else\n#    define LCURSES_STMT_BEG\tdo\n#    define LCURSES_STMT_END\twhile (0)\n#  endif\n#endif\n\n\n/* The extra indirection to these macros is required so that if the\n   arguments are themselves macros, they will get expanded too.  */\n#define LCURSES__SPLICE(_s, _t)\t_s##_t\n#define LCURSES_SPLICE(_s, _t)\tLCURSES__SPLICE(_s, _t)\n\n#define LCURSES__STR(_s)\t#_s\n#define LCURSES_STR(_s)\t\tLCURSES__STR(_s)\n\n/* The +1 is to step over the leading '_' that is required to prevent\n   premature expansion of MENTRY arguments if we didn't add it.  */\n#define LCURSES__STR_1(_s)\t(#_s + 1)\n#define LCURSES_STR_1(_s)\tLCURSES__STR_1(_s)\n\n#define LCURSES_CONST(_f)\tLCURSES_STMT_BEG {\t\t\t\\\n\t\t\t\t\tlua_pushinteger(L, _f);\t\t\\\n\t\t\t\t\tlua_setfield(L, -2, #_f);\t\\\n\t\t\t\t} LCURSES_STMT_END\n\n#define LCURSES_FUNC(_s)\t{LCURSES_STR_1(_s), (_s)}\n\n#define pushokresult(b)\tpushboolresult((int) (b) == OK)\n\n#ifndef errno\nextern int errno;\n#endif\n\n\n/* ========================= *\n * Bad argument diagnostics. *\n * ========================= */\n\n\nstatic int\nargtypeerror(lua_State *L, int narg, const char *expected)\n{\n\tconst char *got = luaL_typename(L, narg);\n\treturn luaL_argerror(L, narg,\n\t\tlua_pushfstring(L, \"%s expected, got %s\", expected, got));\n}\n\nstatic lua_Integer\ncheckinteger(lua_State *L, int narg, const char *expected)\n{\n\tlua_Integer d = lua_tointeger(L, narg);\n\tif (d == 0 && !lua_isinteger(L, narg))\n\t\targtypeerror(L, narg, expected);\n\treturn d;\n}\n\nstatic int\ncheckint(lua_State *L, int narg)\n{\n\treturn (int)checkinteger(L, narg, \"int\");\n}\n\n\nstatic chtype\ncheckch(lua_State *L, int narg)\n{\n\tif (lua_isnumber(L, narg))\n\t\treturn (chtype)checkint(L, narg);\n\tif (lua_isstring(L, narg))\n\t\treturn *lua_tostring(L, narg);\n\n\treturn argtypeerror(L, narg, \"int or char\");\n}\n\n\nstatic chtype\noptch(lua_State *L, int narg, chtype def)\n{\n\tif (lua_isnoneornil(L, narg))\n\t\treturn def;\n\tif (lua_isnumber(L, narg) || lua_isstring(L, narg))\n\t\treturn checkch(L, narg);\n\treturn argtypeerror(L, narg, \"int or char or nil\");\n}\n\n\nstatic int\noptint(lua_State *L, int narg, lua_Integer def)\n{\n\tif (lua_isnoneornil(L, narg))\n\t\treturn (int) def;\n\treturn (int)checkinteger(L, narg, \"int or nil\");\n}\n\n#define pushboolresult(b)\t(lua_pushboolean(L, (b)), 1)\n\n#define pushintresult(n)\t(lua_pushinteger(L, (n)), 1)\n\n#define pushstringresult(s)\t(lua_pushstring(L, (s)), 1)\n\n\n\n/* ================== *\n * Utility functions. *\n * ================== */\n\n#define pushintegerfield(k,v) LCURSES_STMT_BEG {\t\t\t\\\n\tlua_pushinteger(L, (lua_Integer) v); lua_setfield(L, -2, k);\t\\\n} LCURSES_STMT_END\n\n#define pushnumberfield(k,v) LCURSES_STMT_BEG {\t\t\t\t\\\n\tlua_pushnumber(L, (lua_Number) v); lua_setfield(L, -2, k);\t\\\n} LCURSES_STMT_END\n\n#define pushstringfield(k,v) LCURSES_STMT_BEG {\t\t\t\t\\\n\tif (v) {\t\t\t\t\t\t\t\\\n\t\tlua_pushstring(L, (const char *) v);\t\t\t\\\n\t\tlua_setfield(L, -2, k);\t\t\t\t\t\\\n\t}\t\t\t\t\t\t\t\t\\\n} LCURSES_STMT_END\n\n#define pushliteralfield(k,v) LCURSES_STMT_BEG {\t\t\t\\\n\tif (v) {\t\t\t\t\t\t\t\\\n\t\tlua_pushliteral(L, v);\t\t\t\t\t\\\n\t\tlua_setfield(L, -2, k);\t\t\t\t\t\\\n\t}\t\t\t\t\t\t\t\t\\\n} LCURSES_STMT_END\n\n#define settypemetatable(t) LCURSES_STMT_BEG {\t\t\t\t\\\n\tif (luaL_newmetatable(L, t) == 1)\t\t\t\t\\\n\t\tpushliteralfield(\"_type\", t);\t\t\t\t\\\n\tlua_setmetatable(L, -2);\t\t\t\t\t\\\n} LCURSES_STMT_END\n\n#define setintegerfield(_p, _n) pushintegerfield(LCURSES_STR(_n), _p->_n)\n#define setnumberfield(_p, _n) pushnumberfield(LCURSES_STR(_n), _p->_n)\n#define setstringfield(_p, _n) pushstringfield(LCURSES_STR(_n), _p->_n)\n\n#endif /*LCURSES__HELPERS_C*/\n"
  },
  {
    "path": "src/lcurses/chstr.c",
    "content": "/*\n * Curses binding for Lua 5.1, 5.2 & 5.3.\n *\n * (c) Gary V. Vaughan <gary@vaughan.pe> 2013-2017\n * (c) Reuben Thomas <rrt@sc3d.org> 2009-2012\n * (c) Tiago Dionizio <tiago.dionizio AT gmail.com> 2004-2007\n *\n * Permission is hereby granted, free of charge, to any person obtaining\n * a copy of this software and associated documentation files (the\n * \"Software\"), to deal in the Software without restriction, including\n * without limitation the rights to use, copy, modify, merge, publish,\n * distribute, sublicense, and/or sell copies of the Software, and to\n * permit persons to whom the Software is furnished to do so, subject to\n * the following conditions:\n *\n * The above copyright notice and this permission notice shall be\n * included in all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.\n * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\n * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,\n * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE\n * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n */\n/***\nCurses attributed string buffers.\n\nAn array of characters, plus associated curses attributes and\ncolors at each position.\n\nAlthough marginally useful alone, the constants used to set colors\nand attributes in `chstr` buffers are not defined until **after**\n`curses.initscr ()` has been called.\n\n@classmod curses.chstr\n*/\n\n#ifndef LCURSES_CHSTR_C\n#define LCURSES_CHSTR_C 1\n\n#include \"_helpers.c\"\n\n\nstatic const char *CHSTRMETA = \"curses:chstr\";\n\n\ntypedef struct\n{\n\tunsigned int len;\n\tchtype str[1];\n} chstr;\n#define CHSTR_SIZE(len) (sizeof(chstr) + len * sizeof(chtype))\n\n\n/* create new chstr object and leave it in the lua stack */\nstatic chstr *\nchstr_new(lua_State *L, int len)\n{\n\tchstr *cs;\n\n\tif (len < 1)\n\t\treturn luaL_error(L, \"invalid chstr length\"), NULL;\n\n\tcs = lua_newuserdata(L, CHSTR_SIZE(len));\n\tluaL_getmetatable(L, CHSTRMETA);\n\tlua_setmetatable(L, -2);\n\tcs->len = len;\n\treturn cs;\n}\n\n\n/* get chstr from lua (convert if needed) */\nstatic chstr *\ncheckchstr(lua_State *L, int narg)\n{\n\tchstr *cs = (chstr*)luaL_checkudata(L, narg, CHSTRMETA);\n\tif (cs)\n\t\treturn cs;\n\n\tluaL_argerror(L, narg, \"bad curses chstr\");\n\n\t/*NOTREACHED*/\n\treturn NULL;\n}\n\n\n/***\nChange the contents of the chstr.\n@function set_str\n@int o offset to start of change\n@string s characters to insert into *cs* at *o*\n@int[opt=A_NORMAL] attr attributes for changed elements\n@int[opt=1] rep repeat count\n@usage\n  cs = curses.chstr (10)\n  cs:set_str(0, \"0123456789\", curses.A_BOLD)\n*/\nstatic int\nCset_str(lua_State *L)\n{\n\tchstr *cs = checkchstr(L, 1);\n\tint offset = checkint(L, 2);\n\tconst char *str = luaL_checkstring(L, 3);\n\tint len = lua_strlen(L, 3);\n\tint attr = optint(L, 4, A_NORMAL);\n\tint rep = optint(L, 5, 1);\n\tint i;\n\n\tif (offset < 0)\n\t\treturn 0;\n\n\twhile (rep-- > 0 && offset <= (int)cs->len)\n\t{\n\t\tif (offset + len - 1 > (int)cs->len)\n\t\t\tlen = cs->len - offset + 1;\n\n\t\tfor (i = 0; i < len; ++i)\n\t\t\tcs->str[offset + i] = str[i] | attr;\n\t\toffset += len;\n\t}\n\n\treturn 0;\n}\n\n\n/***\nSet a character in the buffer.\n*ch* can be a one-character string, or an integer from `string.byte`\n@function set_ch\n@int o offset to start of change\n@param int|string ch character to insert\n@int[opt=A_NORMAL] attr attributes for changed elements\n@int[opt=1] rep repeat count\n@usage\n  -- Write a bold 'A' followed by normal 'a' chars to a new buffer\n  size = 10\n  cs = curses.chstr (size)\n  cs:set_ch(0, 'A', curses.A_BOLD)\n  cs:set_ch(1, 'a', curses.A_NORMAL, size - 1)\n*/\nstatic int\nCset_ch(lua_State *L)\n{\n\tchstr* cs = checkchstr(L, 1);\n\tint offset = checkint(L, 2);\n\tchtype ch = checkch(L, 3);\n\tint attr = optint(L, 4, A_NORMAL);\n\tint rep = optint(L, 5, 1);\n\n\twhile (rep-- > 0)\n\t{\n\t\tif (offset < 0 || offset >= (int) cs->len)\n\t\t\treturn 0;\n\n\t\tcs->str[offset] = ch | attr;\n\n\t\t++offset;\n\t}\n\treturn 0;\n}\n\n\n/***\nGet information from the chstr.\n@function get\n@int o offset from start of *cs*\n@treturn int character at offset *o* in *cs*\n@treturn int bitwise-OR of attributes at offset *o* in *cs*\n@treturn int colorpair at offset *o* in *cs*\n@usage\n  cs = curses.chstr (10)\n  cs:set_ch(0, 'A', curses.A_BOLD, 10)\n  --> 65 2097152 0\n  print (cs:get (9))\n*/\nstatic int\nCget(lua_State *L)\n{\n\tchstr* cs = checkchstr(L, 1);\n\tint offset = checkint(L, 2);\n\tchtype ch;\n\n\tif (offset < 0 || offset >= (int) cs->len)\n\t\treturn 0;\n\n\tch = cs->str[offset];\n\n\tlua_pushinteger(L, ch & A_CHARTEXT);\n\tlua_pushinteger(L, ch & A_ATTRIBUTES);\n\tlua_pushinteger(L, ch & A_COLOR);\n\treturn 3;\n}\n\n\n/***\nRetrieve chstr length.\n@function len\n@tparam chstr cs buffer to act on\n@treturn int length of *cs*\n@usage\n  cs = curses.chstr (123)\n  --> 123\n  print (cs:len ())\n*/\nstatic int\nClen(lua_State *L)\n{\n\tchstr *cs = checkchstr(L, 1);\n\treturn pushintresult(cs->len);\n}\n\n\n/***\nDuplicate chstr.\n@function dup\n@treturn chstr duplicate of *cs*\n@usage\n  dup = cs:dup ()\n*/\nstatic int\nCdup(lua_State *L)\n{\n\tchstr *cs = checkchstr(L, 1);\n\tchstr *ncs = chstr_new(L, cs->len);\n\n\tmemcpy(ncs->str, cs->str, CHSTR_SIZE(cs->len));\n\treturn 1;\n}\n\n\n/***\nInitialise a new chstr.\n@function __call\n@int len buffer length\n@treturn chstr a new chstr filled with spaces\n@usage\n  cs = curses.chstr (10)\n*/\nstatic int\nC__call(lua_State *L)\n{\n\tint len = checkint(L, 2);\n\tchstr* ncs = chstr_new(L, len);\n\tmemset(ncs->str, ' ', len * sizeof(chtype));\n\treturn 1;\n}\n\n\nstatic const luaL_Reg curses_chstr_fns[] =\n{\n\tLCURSES_FUNC( Clen\t\t),\n\tLCURSES_FUNC( Cset_ch\t\t),\n\tLCURSES_FUNC( Cset_str\t\t),\n\tLCURSES_FUNC( Cget\t\t),\n\tLCURSES_FUNC( Cdup\t\t),\n\t{ NULL, NULL }\n};\n\n\n\nLUALIB_API int\nluaopen_curses_chstr(lua_State *L)\n{\n\tint t, mt;\n\n\tluaL_register(L, \"curses.chstr\", curses_chstr_fns);\n\tt = lua_gettop(L);\n\n\tlua_createtable(L, 0, 1);\t\t/* u = {} */\n\tlua_pushcfunction(L, C__call);\n\tlua_setfield(L, -2, \"__call\");\t\t/* u.__call = C__call */\n\tlua_setmetatable(L, -2);\t\t/* setmetatable (t, u) */\n\n\tluaL_newmetatable(L, CHSTRMETA);\n\tmt = lua_gettop(L);\n\n\tlua_pushvalue(L, mt);\n\tlua_setfield(L, -2, \"__index\");\t\t/* mt.__index = mt */\n\tlua_pushliteral(L, \"CursesChstr\");\n\tlua_setfield(L, -2, \"_type\");\t\t/* mt._type = \"CursesChstr\" */\n\n\t/* for k,v in pairs(t) do mt[k]=v end */\n\tfor (lua_pushnil(L); lua_next(L, t) != 0;)\n\t\tlua_setfield(L, mt, lua_tostring(L, -2));\n\n\tlua_pop(L, 1);\t\t\t\t/* pop mt */\n\n\treturn 1;\n}\n\n#endif /*!LCURSES_CHSTR_C*/\n"
  },
  {
    "path": "src/lcurses/compat-5.2.c",
    "content": "#include <errno.h>\n#include <string.h>\n\n#include \"../lua.h\"\n#include \"../lauxlib.h\"\n#include \"compat-5.2.h\"\n\n#if !defined(LUA_VERSION_NUM) || LUA_VERSION_NUM == 501\n\nint lua_absindex (lua_State *L, int i) {\n  if (i < 0 && i > LUA_REGISTRYINDEX)\n    i += lua_gettop(L) + 1;\n  return i;\n}\n\n\nvoid lua_copy (lua_State *L, int from, int to) {\n  int abs_to = lua_absindex(L, to);\n  luaL_checkstack(L, 1, \"not enough stack slots\");\n  lua_pushvalue(L, from);\n  lua_replace(L, abs_to);\n}\n\n\nvoid lua_rawgetp (lua_State *L, int i, const void *p) {\n  int abs_i = lua_absindex(L, i);\n  lua_pushlightuserdata(L, (void*)p);\n  lua_rawget(L, abs_i);\n}\n\nvoid lua_rawsetp (lua_State *L, int i, const void *p) {\n  int abs_i = lua_absindex(L, i);\n  luaL_checkstack(L, 1, \"not enough stack slots\");\n  lua_pushlightuserdata(L, (void*)p);\n  lua_insert(L, -2);\n  lua_rawset(L, abs_i);\n}\n\n\nvoid *luaL_testudata (lua_State *L, int i, const char *tname) {\n  void *p = lua_touserdata(L, i);\n  luaL_checkstack(L, 2, \"not enough stack slots\");\n  if (p == NULL || !lua_getmetatable(L, i))\n    return NULL;\n  else {\n    int res = 0;\n    luaL_getmetatable(L, tname);\n    res = lua_rawequal(L, -1, -2);\n    lua_pop(L, 2);\n    if (!res)\n      p = NULL;\n  }\n  return p;\n}\n\n\nlua_Number lua_tonumberx (lua_State *L, int i, int *isnum) {\n  lua_Number n = lua_tonumber(L, i);\n  if (isnum != NULL) {\n    *isnum = (n != 0 || lua_isnumber(L, i));\n  }\n  return n;\n}\n\n\n#define PACKAGE_KEY \"_COMPAT52_PACKAGE\"\n\nstatic void push_package_table (lua_State *L) {\n  lua_pushliteral(L, PACKAGE_KEY);\n  lua_rawget(L, LUA_REGISTRYINDEX);\n  if (!lua_istable(L, -1)) {\n    lua_pop(L, 1);\n    /* try to get package table from globals */\n    lua_pushliteral(L, \"package\");\n    lua_rawget(L, LUA_GLOBALSINDEX);\n    if (lua_istable(L, -1)) {\n      lua_pushliteral(L, PACKAGE_KEY);\n      lua_pushvalue(L, -2);\n      lua_rawset(L, LUA_REGISTRYINDEX);\n    }\n  }\n}\n\nvoid lua_getuservalue (lua_State *L, int i) {\n  luaL_checktype(L, i, LUA_TUSERDATA);\n  luaL_checkstack(L, 2, \"not enough stack slots\");\n  lua_getfenv(L, i);\n  lua_pushvalue(L, LUA_GLOBALSINDEX);\n  if (lua_rawequal(L, -1, -2)) {\n    lua_pop(L, 1);\n    lua_pushnil(L);\n    lua_replace(L, -2);\n  } else {\n    lua_pop(L, 1);\n    push_package_table(L);\n    if (lua_rawequal(L, -1, -2)) {\n      lua_pop(L, 1);\n      lua_pushnil(L);\n      lua_replace(L, -2);\n    } else\n      lua_pop(L, 1);\n  }\n}\n\nvoid lua_setuservalue (lua_State *L, int i) {\n  luaL_checktype(L, i, LUA_TUSERDATA);\n  if (lua_isnil(L, -1)) {\n    luaL_checkstack(L, 1, \"not enough stack slots\");\n    lua_pushvalue(L, LUA_GLOBALSINDEX);\n    lua_replace(L, -2);\n  }\n  lua_setfenv(L, i);\n}\n\n\n/*\n** Adapted from Lua 5.2.0\n*/\nvoid luaL_setfuncs (lua_State *L, const luaL_Reg *l, int nup) {\n  luaL_checkstack(L, nup+1, \"too many upvalues\");\n  for (; l->name != NULL; l++) {  /* fill the table with given functions */\n    int i;\n    lua_pushstring(L, l->name);\n    for (i = 0; i < nup; i++)  /* copy upvalues to the top */\n      lua_pushvalue(L, -(nup + 1));\n    lua_pushcclosure(L, l->func, nup);  /* closure with those upvalues */\n    lua_settable(L, -(nup + 3)); /* table must be below the upvalues, the name and the closure */\n  }\n  lua_pop(L, nup);  /* remove upvalues */\n}\n\n\nvoid luaL_setmetatable (lua_State *L, const char *tname) {\n  luaL_checkstack(L, 1, \"not enough stack slots\");\n  luaL_getmetatable(L, tname);\n  lua_setmetatable(L, -2);\n}\n\n\nint luaL_getsubtable (lua_State *L, int i, const char *name) {\n  int abs_i = lua_absindex(L, i);\n  luaL_checkstack(L, 3, \"not enough stack slots\");\n  lua_pushstring(L, name);\n  lua_gettable(L, abs_i);\n  if (lua_istable(L, -1))\n    return 1;\n  lua_pop(L, 1);\n  lua_newtable(L);\n  lua_pushstring(L, name);\n  lua_pushvalue(L, -2);\n  lua_settable(L, abs_i);\n  return 0;\n}\n\n\n#if !defined(COMPAT52_IS_LUAJIT)\nstatic int countlevels (lua_State *L) {\n  lua_Debug ar;\n  int li = 1, le = 1;\n  /* find an upper bound */\n  while (lua_getstack(L, le, &ar)) { li = le; le *= 2; }\n  /* do a binary search */\n  while (li < le) {\n    int m = (li + le)/2;\n    if (lua_getstack(L, m, &ar)) li = m + 1;\n    else le = m;\n  }\n  return le - 1;\n}\n\nstatic int findfield (lua_State *L, int objidx, int level) {\n  if (level == 0 || !lua_istable(L, -1))\n    return 0;  /* not found */\n  lua_pushnil(L);  /* start 'next' loop */\n  while (lua_next(L, -2)) {  /* for each pair in table */\n    if (lua_type(L, -2) == LUA_TSTRING) {  /* ignore non-string keys */\n      if (lua_rawequal(L, objidx, -1)) {  /* found object? */\n        lua_pop(L, 1);  /* remove value (but keep name) */\n        return 1;\n      }\n      else if (findfield(L, objidx, level - 1)) {  /* try recursively */\n        lua_remove(L, -2);  /* remove table (but keep name) */\n        lua_pushliteral(L, \".\");\n        lua_insert(L, -2);  /* place '.' between the two names */\n        lua_concat(L, 3);\n        return 1;\n      }\n    }\n    lua_pop(L, 1);  /* remove value */\n  }\n  return 0;  /* not found */\n}\n\nstatic int pushglobalfuncname (lua_State *L, lua_Debug *ar) {\n  int top = lua_gettop(L);\n  lua_getinfo(L, \"f\", ar);  /* push function */\n  lua_pushvalue(L, LUA_GLOBALSINDEX);\n  if (findfield(L, top + 1, 2)) {\n    lua_copy(L, -1, top + 1);  /* move name to proper place */\n    lua_pop(L, 2);  /* remove pushed values */\n    return 1;\n  }\n  else {\n    lua_settop(L, top);  /* remove function and global table */\n    return 0;\n  }\n}\n\nstatic void pushfuncname (lua_State *L, lua_Debug *ar) {\n  if (*ar->namewhat != '\\0')  /* is there a name? */\n    lua_pushfstring(L, \"function \" LUA_QS, ar->name);\n  else if (*ar->what == 'm')  /* main? */\n      lua_pushliteral(L, \"main chunk\");\n  else if (*ar->what == 'C') {\n    if (pushglobalfuncname(L, ar)) {\n      lua_pushfstring(L, \"function \" LUA_QS, lua_tostring(L, -1));\n      lua_remove(L, -2);  /* remove name */\n    }\n    else\n      lua_pushliteral(L, \"?\");\n  }\n  else\n    lua_pushfstring(L, \"function <%s:%d>\", ar->short_src, ar->linedefined);\n}\n\n#define LEVELS1 12  /* size of the first part of the stack */\n#define LEVELS2 10  /* size of the second part of the stack */\n\nvoid luaL_traceback (lua_State *L, lua_State *L1,\n                                const char *msg, int level) {\n  lua_Debug ar;\n  int top = lua_gettop(L);\n  int numlevels = countlevels(L1);\n  int mark = (numlevels > LEVELS1 + LEVELS2) ? LEVELS1 : 0;\n  if (msg) lua_pushfstring(L, \"%s\\n\", msg);\n  lua_pushliteral(L, \"stack traceback:\");\n  while (lua_getstack(L1, level++, &ar)) {\n    if (level == mark) {  /* too many levels? */\n      lua_pushliteral(L, \"\\n\\t...\");  /* add a '...' */\n      level = numlevels - LEVELS2;  /* and skip to last ones */\n    }\n    else {\n      lua_getinfo(L1, \"Slnt\", &ar);\n      lua_pushfstring(L, \"\\n\\t%s:\", ar.short_src);\n      if (ar.currentline > 0)\n        lua_pushfstring(L, \"%d:\", ar.currentline);\n      lua_pushliteral(L, \" in \");\n      pushfuncname(L, &ar);\n      lua_concat(L, lua_gettop(L) - top);\n    }\n  }\n  lua_concat(L, lua_gettop(L) - top);\n}\n#endif\n\n\nvoid luaL_checkversion (lua_State *L) {\n  (void)L;\n}\n\n\n#if !defined(COMPAT52_IS_LUAJIT)\nint luaL_fileresult (lua_State *L, int stat, const char *fname) {\n  int en = errno;  /* calls to Lua API may change this value */\n  if (stat) {\n    lua_pushboolean(L, 1);\n    return 1;\n  }\n  else {\n    lua_pushnil(L);\n    if (fname)\n      lua_pushfstring(L, \"%s: %s\", fname, strerror(en));\n    else\n      lua_pushstring(L, strerror(en));\n    lua_pushnumber(L, (lua_Number)en);\n    return 3;\n  }\n}\n#endif\n\n\n#endif /* Lua 5.0 or Lua 5.1 */\n\n\n#if defined(LUA_VERSION_NUM) && LUA_VERSION_NUM == 501\n#include <limits.h>\n\ntypedef LUAI_INT32 LUA_INT32;\n\n/********************************************************************/\n/*                    extract of 5.2's luaconf.h                    */\n/*  detects proper defines for faster unsigned<->number conversion  */\n/*           see copyright notice at the end of this file           */\n/********************************************************************/\n\n#if !defined(LUA_ANSI) && defined(_WIN32) && !defined(_WIN32_WCE)\n#define LUA_WIN\t\t/* enable goodies for regular Windows platforms */\n#endif\n\n\n#if defined(LUA_NUMBER_DOUBLE) && !defined(LUA_ANSI)\t/* { */\n\n/* Microsoft compiler on a Pentium (32 bit) ? */\n#if defined(LUA_WIN) && defined(_MSC_VER) && defined(_M_IX86)\t/* { */\n\n#define LUA_MSASMTRICK\n#define LUA_IEEEENDIAN\t\t0\n#define LUA_NANTRICK\n\n/* pentium 32 bits? */\n#elif defined(__i386__) || defined(__i386) || defined(__X86__) /* }{ */\n\n#define LUA_IEEE754TRICK\n#define LUA_IEEELL\n#define LUA_IEEEENDIAN\t\t0\n#define LUA_NANTRICK\n\n/* pentium 64 bits? */\n#elif defined(__x86_64)\t\t\t\t\t\t/* }{ */\n\n#define LUA_IEEE754TRICK\n#define LUA_IEEEENDIAN\t\t0\n\n#elif defined(__POWERPC__) || defined(__ppc__)\t\t\t/* }{ */\n\n#define LUA_IEEE754TRICK\n#define LUA_IEEEENDIAN\t\t1\n\n#else\t\t\t\t\t\t\t\t/* }{ */\n\n/* assume IEEE754 and a 32-bit integer type */\n#define LUA_IEEE754TRICK\n\n#endif\t\t\t\t\t\t\t\t/* } */\n\n#endif\t\t\t\t\t\t\t/* } */\n\n\n/********************************************************************/\n/*                    extract of 5.2's llimits.h                    */\n/*       gives us lua_number2unsigned and lua_unsigned2number       */\n/*           see copyright notice at the end of this file           */\n/********************************************************************/\n\n#if defined(MS_ASMTRICK) || defined(LUA_MSASMTRICK)\t/* { */\n/* trick with Microsoft assembler for X86 */\n\n#define lua_number2unsigned(i,n)  \\\n  {__int64 l; __asm {__asm fld n   __asm fistp l} i = (unsigned int)l;}\n\n\n#elif defined(LUA_IEEE754TRICK)\t\t/* }{ */\n/* the next trick should work on any machine using IEEE754 with\n   a 32-bit int type */\n\nunion compat52_luai_Cast { double l_d; LUA_INT32 l_p[2]; };\n\n#if !defined(LUA_IEEEENDIAN)\t/* { */\n#define LUAI_EXTRAIEEE\t\\\n  static const union compat52_luai_Cast ieeeendian = {-(33.0 + 6755399441055744.0)};\n#define LUA_IEEEENDIANLOC\t(ieeeendian.l_p[1] == 33)\n#else\n#define LUA_IEEEENDIANLOC\tLUA_IEEEENDIAN\n#define LUAI_EXTRAIEEE\t\t/* empty */\n#endif\t\t\t\t/* } */\n\n#define lua_number2int32(i,n,t) \\\n  { LUAI_EXTRAIEEE \\\n    volatile union compat52_luai_Cast u; u.l_d = (n) + 6755399441055744.0; \\\n    (i) = (t)u.l_p[LUA_IEEEENDIANLOC]; }\n\n#define lua_number2unsigned(i,n)\tlua_number2int32(i, n, lua_Unsigned)\n\n#endif\t\t\t\t/* } */\n\n\n/* the following definitions always work, but may be slow */\n\n#if !defined(lua_number2unsigned)\t/* { */\n/* the following definition assures proper modulo behavior */\n#if defined(LUA_NUMBER_DOUBLE) || defined(LUA_NUMBER_FLOAT)\n#include <math.h>\n#define SUPUNSIGNED\t((lua_Number)(~(lua_Unsigned)0) + 1)\n#define lua_number2unsigned(i,n)  \\\n\t((i)=(lua_Unsigned)((n) - floor((n)/SUPUNSIGNED)*SUPUNSIGNED))\n#else\n#define lua_number2unsigned(i,n)\t((i)=(lua_Unsigned)(n))\n#endif\n#endif\t\t\t\t/* } */\n\n\n#if !defined(lua_unsigned2number)\n/* on several machines, coercion from unsigned to double is slow,\n   so it may be worth to avoid */\n#define lua_unsigned2number(u)  \\\n    (((u) <= (lua_Unsigned)INT_MAX) ? (lua_Number)(int)(u) : (lua_Number)(u))\n#endif\n\n/********************************************************************/\n\n\nstatic void compat52_call_lua (lua_State *L, char const code[], size_t len,\n                               int nargs, int nret) {\n  lua_rawgetp(L, LUA_REGISTRYINDEX, (void*)code);\n  if (lua_type(L, -1) != LUA_TFUNCTION) {\n    lua_pop(L, 1);\n    if (luaL_loadbuffer(L, code, len, \"=none\"))\n      lua_error(L);\n    lua_pushvalue(L, -1);\n    lua_rawsetp(L, LUA_REGISTRYINDEX, (void*)code);\n  }\n  lua_insert(L, -nargs-1);\n  lua_call(L, nargs, nret);\n}\n\nstatic const char compat52_arith_code[] = {\n  'l', 'o', 'c', 'a', 'l', ' ', 'o', 'p', ',', 'a', ',', 'b',\n  '=', '.', '.', '.', '\\n',\n  'i', 'f', ' ', 'o', 'p', '=', '=', '0', ' ',\n  't', 'h', 'e', 'n', '\\n',\n  'r', 'e', 't', 'u', 'r', 'n', ' ', 'a', '+', 'b', '\\n',\n  'e', 'l', 's', 'e', 'i', 'f', ' ', 'o', 'p', '=', '=', '1', ' ',\n  't', 'h', 'e', 'n', '\\n',\n  'r', 'e', 't', 'u', 'r', 'n', ' ', 'a', '-', 'b', '\\n',\n  'e', 'l', 's', 'e', 'i', 'f', ' ', 'o', 'p', '=', '=', '2', ' ',\n  't', 'h', 'e', 'n', '\\n',\n  'r', 'e', 't', 'u', 'r', 'n', ' ', 'a', '*', 'b', '\\n',\n  'e', 'l', 's', 'e', 'i', 'f', ' ', 'o', 'p', '=', '=', '3', ' ',\n  't', 'h', 'e', 'n', '\\n',\n  'r', 'e', 't', 'u', 'r', 'n', ' ', 'a', '/', 'b', '\\n',\n  'e', 'l', 's', 'e', 'i', 'f', ' ', 'o', 'p', '=', '=', '4', ' ',\n  't', 'h', 'e', 'n', '\\n',\n  'r', 'e', 't', 'u', 'r', 'n', ' ', 'a', '%', 'b', '\\n',\n  'e', 'l', 's', 'e', 'i', 'f', ' ', 'o', 'p', '=', '=', '5', ' ',\n  't', 'h', 'e', 'n', '\\n',\n  'r', 'e', 't', 'u', 'r', 'n', ' ', 'a', '^', 'b', '\\n',\n  'e', 'l', 's', 'e', 'i', 'f', ' ', 'o', 'p', '=', '=', '6', ' ',\n  't', 'h', 'e', 'n', '\\n',\n  'r', 'e', 't', 'u', 'r', 'n', ' ', '-', 'a', '\\n',\n  'e', 'n', 'd', '\\n', '\\0'\n};\n\nvoid lua_arith (lua_State *L, int op) {\n  if (op < LUA_OPADD && op > LUA_OPUNM)\n    luaL_error(L, \"invalid 'op' argument for lua_arith\");\n  luaL_checkstack(L, 5, \"not enough stack slots\");\n  if (op == LUA_OPUNM)\n    lua_pushvalue(L, -1);\n  lua_pushnumber(L, op);\n  lua_insert(L, -3);\n  compat52_call_lua(L, compat52_arith_code,\n                    sizeof(compat52_arith_code)-1, 3, 1);\n}\n\n\nstatic const char compat52_compare_code[] = {\n  'l', 'o', 'c', 'a', 'l', ' ', 'a', ',', 'b', '=', '.', '.', '.', '\\n',\n  'r', 'e', 't', 'u', 'r', 'n', ' ', 'a', '<', '=', 'b', '\\n', '\\0'\n};\n\nint lua_compare (lua_State *L, int idx1, int idx2, int op) {\n  int result = 0;\n  switch (op) {\n    case LUA_OPEQ:\n      return lua_equal(L, idx1, idx2);\n    case LUA_OPLT:\n      return lua_lessthan(L, idx1, idx2);\n    case LUA_OPLE:\n      luaL_checkstack(L, 5, \"not enough stack slots\");\n      idx1 = lua_absindex(L, idx1);\n      idx2 = lua_absindex(L, idx2);\n      lua_pushvalue(L, idx1);\n      lua_pushvalue(L, idx2);\n      compat52_call_lua(L, (void*)compat52_compare_code,\n                        sizeof(compat52_compare_code)-1, 2, 1);\n      result = lua_toboolean(L, -1);\n      lua_pop(L, 1);\n      return result;\n    default:\n      luaL_error(L, \"invalid 'op' argument for lua_compare\");\n  }\n  return 0;\n}\n\n\nvoid lua_pushunsigned (lua_State *L, lua_Unsigned n) {\n  lua_pushnumber(L, lua_unsigned2number(n));\n}\n\n\nlua_Unsigned luaL_checkunsigned (lua_State *L, int i) {\n  lua_Unsigned result;\n  lua_Number n = lua_tonumber(L, i);\n  if (n == 0 && !lua_isnumber(L, i))\n    luaL_checktype(L, i, LUA_TNUMBER);\n  lua_number2unsigned(result, n);\n  return result;\n}\n\n\nlua_Unsigned lua_tounsignedx (lua_State *L, int i, int *isnum) {\n  lua_Unsigned result;\n  lua_Number n = lua_tonumberx(L, i, isnum);\n  lua_number2unsigned(result, n);\n  return result;\n}\n\n\nlua_Unsigned luaL_optunsigned (lua_State *L, int i, lua_Unsigned def) {\n  return luaL_opt(L, luaL_checkunsigned, i, def);\n}\n\n\nlua_Integer lua_tointegerx (lua_State *L, int i, int *isnum) {\n  lua_Integer n = lua_tointeger(L, i);\n  if (isnum != NULL) {\n    *isnum = (n != 0 || lua_isnumber(L, i));\n  }\n  return n;\n}\n\n\nvoid lua_len (lua_State *L, int i) {\n  switch (lua_type(L, i)) {\n    case LUA_TSTRING: /* fall through */\n    case LUA_TTABLE:\n      if (!luaL_callmeta(L, i, \"__len\"))\n        lua_pushnumber(L, (int)lua_objlen(L, i));\n      break;\n    case LUA_TUSERDATA:\n      if (luaL_callmeta(L, i, \"__len\"))\n        break;\n    /* maybe fall through */\n    default:\n      luaL_error(L, \"attempt to get length of a %s value\",\n                 lua_typename(L, lua_type(L, i)));\n  }\n}\n\n\nint luaL_len (lua_State *L, int i) {\n  int res = 0, isnum = 0;\n  luaL_checkstack(L, 1, \"not enough stack slots\");\n  lua_len(L, i);\n  res = (int)lua_tointegerx(L, -1, &isnum);\n  lua_pop(L, 1);\n  if (!isnum)\n    luaL_error(L, \"object length is not a number\");\n  return res;\n}\n\n\nconst char *luaL_tolstring (lua_State *L, int idx, size_t *len) {\n  if (!luaL_callmeta(L, idx, \"__tostring\")) {\n    int t = lua_type(L, idx);\n    switch (t) {\n      case LUA_TNIL:\n        lua_pushliteral(L, \"nil\");\n        break;\n      case LUA_TSTRING:\n      case LUA_TNUMBER:\n        lua_pushvalue(L, idx);\n        break;\n      case LUA_TBOOLEAN:\n        if (lua_toboolean(L, idx))\n          lua_pushliteral(L, \"true\");\n        else\n          lua_pushliteral(L, \"false\");\n        break;\n      default:\n        lua_pushfstring(L, \"%s: %p\", lua_typename(L, t),\n                                     lua_topointer(L, idx));\n        break;\n    }\n  }\n  return lua_tolstring(L, -1, len);\n}\n\n\nvoid luaL_requiref (lua_State *L, char const* modname,\n                    lua_CFunction openf, int glb) {\n  luaL_checkstack(L, 3, \"not enough stack slots\");\n  lua_pushcfunction(L, openf);\n  lua_pushstring(L, modname);\n  lua_call(L, 1, 1);\n  lua_getglobal(L, \"package\");\n  lua_getfield(L, -1, \"loaded\");\n  lua_replace(L, -2);\n  lua_pushvalue(L, -2);\n  lua_setfield(L, -2, modname);\n  lua_pop(L, 1);\n  if (glb) {\n    lua_pushvalue(L, -1);\n    lua_setglobal(L, modname);\n  }\n}\n\n\nvoid luaL_buffinit (lua_State *L, luaL_Buffer_52 *B) {\n  /* make it crash if used via pointer to a 5.1-style luaL_Buffer */\n  B->b.p = NULL;\n  B->b.L = NULL;\n  B->b.lvl = 0;\n  /* reuse the buffer from the 5.1-style luaL_Buffer though! */\n  B->ptr = B->b.buffer;\n  B->capacity = LUAL_BUFFERSIZE;\n  B->nelems = 0;\n  B->L2 = L;\n}\n\n\nchar *luaL_prepbuffsize (luaL_Buffer_52 *B, size_t s) {\n  if (B->capacity - B->nelems < s) { /* needs to grow */\n    char* newptr = NULL;\n    size_t newcap = B->capacity * 2;\n    if (newcap - B->nelems < s)\n      newcap = B->nelems + s;\n    if (newcap < B->capacity) /* overflow */\n      luaL_error(B->L2, \"buffer too large\");\n    newptr = lua_newuserdata(B->L2, newcap);\n    memcpy(newptr, B->ptr, B->nelems);\n    if (B->ptr != B->b.buffer)\n      lua_replace(B->L2, -2); /* remove old buffer */\n    B->ptr = newptr;\n    B->capacity = newcap;\n  }\n  return B->ptr+B->nelems;\n}\n\n\nvoid luaL_addlstring (luaL_Buffer_52 *B, const char *s, size_t l) {\n  memcpy(luaL_prepbuffsize(B, l), s, l);\n  luaL_addsize(B, l);\n}\n\n\nvoid luaL_addvalue (luaL_Buffer_52 *B) {\n  size_t len = 0;\n  const char *s = lua_tolstring(B->L2, -1, &len);\n  if (!s)\n    luaL_error(B->L2, \"cannot convert value to string\");\n  if (B->ptr != B->b.buffer)\n    lua_insert(B->L2, -2); /* userdata buffer must be at stack top */\n  luaL_addlstring(B, s, len);\n  lua_remove(B->L2, B->ptr != B->b.buffer ? -2 : -1);\n}\n\n\nvoid luaL_pushresult (luaL_Buffer_52 *B) {\n  lua_pushlstring(B->L2, B->ptr, B->nelems);\n  if (B->ptr != B->b.buffer)\n    lua_replace(B->L2, -2); /* remove userdata buffer */\n}\n\n\n#endif /* LUA_VERSION_NUM == 501 */\n\n\n/*********************************************************************\n* This file contains parts of Lua 5.2's source code:\n*\n* Copyright (C) 1994-2013 Lua.org, PUC-Rio.\n*\n* Permission is hereby granted, free of charge, to any person obtaining\n* a copy of this software and associated documentation files (the\n* \"Software\"), to deal in the Software without restriction, including\n* without limitation the rights to use, copy, modify, merge, publish,\n* distribute, sublicense, and/or sell copies of the Software, and to\n* permit persons to whom the Software is furnished to do so, subject to\n* the following conditions:\n*\n* The above copyright notice and this permission notice shall be\n* included in all copies or substantial portions of the Software.\n*\n* THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.\n* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\n* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,\n* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE\n* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n*********************************************************************/\n"
  },
  {
    "path": "src/lcurses/compat-5.2.h",
    "content": "#include <stddef.h>\n#include <string.h>\n#include <stdio.h>\n#include \"../lua.h\"\n#include \"../lauxlib.h\"\n#include \"../lualib.h\"\n\n#if !defined(LUA_VERSION_NUM)\n/* Lua 5.0 */\n\n#define LUA_QL(x) \"'\" x \"'\"\n#define LUA_QS LUA_QL(\"%s\")\n\n#define luaL_Reg luaL_reg\n\n#define luaL_opt(L, f, n, d) \\\n  (lua_isnoneornil(L, n) ? (d) : f(L, n))\n\n#define luaL_addchar(B,c) \\\n  ((void)((B)->p < ((B)->buffer+LUAL_BUFFERSIZE) || luaL_prepbuffer(B)), \\\n   (*(B)->p++ = (char)(c)))\n\n#endif /* Lua 5.0 */\n\n\n#if defined(LUA_VERSION_NUM) && LUA_VERSION_NUM == 501\n/* Lua 5.1 */\n\n/* PUC-Rio Lua uses lconfig_h as include guard for luaconf.h,\n * LuaJIT uses luaconf_h. If you use PUC-Rio's include files\n * but LuaJIT's library, you will need to define the macro\n * COMPAT52_IS_LUAJIT yourself! */\n#if !defined(COMPAT52_IS_LUAJIT) && defined(luaconf_h)\n#define COMPAT52_IS_LUAJIT\n#endif\n\n/* LuaJIT doesn't define these unofficial macros ... */\n#if !defined(LUAI_INT32)\n#include <limits.h>\n#if INT_MAX-20 < 32760\n#define LUAI_INT32  long\n#define LUAI_UINT32 unsigned long\n#elif INT_MAX > 2147483640L\n#define LUAI_INT32  int\n#define LUAI_UINT32 unsigned int\n#else\n#error \"could not detect suitable lua_Unsigned datatype\"\n#endif\n#endif\n\n#define LUA_OPADD 0\n#define LUA_OPSUB 1\n#define LUA_OPMUL 2\n#define LUA_OPDIV 3\n#define LUA_OPMOD 4\n#define LUA_OPPOW 5\n#define LUA_OPUNM 6\n#define LUA_OPEQ 0\n#define LUA_OPLT 1\n#define LUA_OPLE 2\n\ntypedef LUAI_UINT32 lua_Unsigned;\n\ntypedef struct luaL_Buffer_52 {\n  luaL_Buffer b; /* make incorrect code crash! */\n  char *ptr;\n  size_t nelems;\n  size_t capacity;\n  lua_State *L2;\n} luaL_Buffer_52;\n#define luaL_Buffer luaL_Buffer_52\n\ntypedef struct luaL_Stream {\n  FILE *f;\n  /* The following field is for LuaJIT which adds a uint32_t field\n   * to file handles. */\n  lua_Unsigned type;\n  lua_CFunction closef;\n} luaL_Stream;\n\n\n#define lua_tounsigned(L, i) lua_tounsignedx(L, i, NULL)\n\n#define lua_rawlen(L, i) lua_objlen(L, i)\n\nvoid lua_arith (lua_State *L, int op);\nint lua_compare (lua_State *L, int idx1, int idx2, int op);\nvoid lua_pushunsigned (lua_State *L, lua_Unsigned n);\nlua_Unsigned luaL_checkunsigned (lua_State *L, int i);\nlua_Unsigned lua_tounsignedx (lua_State *L, int i, int *isnum);\nlua_Unsigned luaL_optunsigned (lua_State *L, int i, lua_Unsigned def);\nlua_Integer lua_tointegerx (lua_State *L, int i, int *isnum);\nvoid lua_len (lua_State *L, int i);\nint luaL_len (lua_State *L, int i);\nconst char *luaL_tolstring (lua_State *L, int idx, size_t *len);\nvoid luaL_requiref (lua_State *L, char const* modname, lua_CFunction openf, int glb);\n\n#define luaL_buffinit luaL_buffinit_52\nvoid luaL_buffinit (lua_State *L, luaL_Buffer_52 *B);\n\n#define luaL_prepbuffsize luaL_prepbuffsize_52\nchar *luaL_prepbuffsize (luaL_Buffer_52 *B, size_t s);\n\n#define luaL_addlstring luaL_addlstring_52\nvoid luaL_addlstring (luaL_Buffer_52 *B, const char *s, size_t l);\n\n#define luaL_addvalue luaL_addvalue_52\nvoid luaL_addvalue (luaL_Buffer_52 *B);\n\n#define luaL_pushresult luaL_pushresult_52\nvoid luaL_pushresult (luaL_Buffer_52 *B);\n\n#undef luaL_buffinitsize\n#define luaL_buffinitsize(L, B, s) \\\n  (luaL_buffinit(L, B), luaL_prepbuffsize(B, s))\n\n#undef luaL_prepbuffer\n#define luaL_prepbuffer(B) \\\n  luaL_prepbuffsize(B, LUAL_BUFFERSIZE)\n\n#undef luaL_addchar\n#define luaL_addchar(B, c) \\\n  ((void)((B)->nelems < (B)->capacity || luaL_prepbuffsize(B, 1)), \\\n   ((B)->ptr[(B)->nelems++] = (c)))\n\n#undef luaL_addsize\n#define luaL_addsize(B, s) \\\n  ((B)->nelems += (s))\n\n#undef luaL_addstring\n#define luaL_addstring(B, s) \\\n  luaL_addlstring(B, s, strlen(s))\n\n#undef luaL_pushresultsize\n#define luaL_pushresultsize(B, s) \\\n  (luaL_addsize(B, s), luaL_pushresult(B))\n\n#endif /* Lua 5.1 */\n\n\n#if !defined(LUA_VERSION_NUM) || LUA_VERSION_NUM == 501\n/* Lua 5.0 *or* 5.1 */\n\n#define LUA_OK 0\n\n#define lua_pushglobaltable(L) \\\n  lua_pushvalue(L, LUA_GLOBALSINDEX)\n\n#define luaL_newlib(L, l) \\\n  (lua_newtable((L)),luaL_setfuncs((L), (l), 0))\n\nvoid luaL_checkversion (lua_State *L);\n\n#endif /* Lua 5.0 *or* 5.1 */\n\nint lua_absindex (lua_State *L, int i);\nvoid lua_copy (lua_State *L, int from, int to);\nvoid lua_rawgetp (lua_State *L, int i, const void *p);\nvoid lua_rawsetp (lua_State *L, int i, const void *p);\nvoid *luaL_testudata (lua_State *L, int i, const char *tname);\nlua_Number lua_tonumberx (lua_State *L, int i, int *isnum);\nvoid lua_getuservalue (lua_State *L, int i);\nvoid lua_setuservalue (lua_State *L, int i);\nvoid luaL_setfuncs (lua_State *L, const luaL_Reg *l, int nup);\nvoid luaL_setmetatable (lua_State *L, const char *tname);\nint luaL_getsubtable (lua_State *L, int i, const char *name);\nvoid luaL_traceback (lua_State *L, lua_State *L1, const char *msg, int level);\nint luaL_fileresult (lua_State *L, int stat, const char *fname);\n"
  },
  {
    "path": "src/lcurses/curses.c",
    "content": "/*\n * Curses binding for Lua 5.1, 5.2 & 5.3.\n *\n * (c) Gary V. Vaughan <gary@vaughan.pe> 2013-2017\n * (c) Reuben Thomas <rrt@sc3d.org> 2009-2012\n * (c) Tiago Dionizio <tiago.dionizio AT gmail.com> 2004-2007\n *\n * Permission is hereby granted, free of charge, to any person obtaining\n * a copy of this software and associated documentation files (the\n * \"Software\"), to deal in the Software without restriction, including\n * without limitation the rights to use, copy, modify, merge, publish,\n * distribute, sublicense, and/or sell copies of the Software, and to\n * permit persons to whom the Software is furnished to do so, subject to\n * the following conditions:\n *\n * The above copyright notice and this permission notice shall be\n * included in all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.\n * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\n * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,\n * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE\n * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n */\n/***\n Full-screen Text Terminal Manipulation.\n\n Some of the C functions beginning with \"no\" do not exist in Lua. You should\n use `curses.nl(false)` and `curses.nl(true)` instead of `nonl()` and `nl()`,\n and likewise `curses.echo(false)` and `curses.echo(true)` instead of\n `noecho()` and `echo()` .\n\n In this Lua module the `stdscr:getch()` function always returns an integer.\n In C, a single character is an integer, but in Lua (and Perl) a single\n character is a short string. The Perl Curses function `getch()` returns a\n char if it was a char, and a number if it was a constant; to get this\n behaviour in Lua you have to convert explicitly, e.g.:\n\n     if c < 256 then c = string.char(c) end\n\n Some Lua functions take a different set of parameters than their C\n counterparts; for example, you should use `y, x = stdscr.getyx()` instead of\n `getstr(str)` and `getyx(y, x)`, and likewise for `getbegyx` and `getmaxyx`\n and `getparyx` and `pair_content`. The Perl Curses module now uses the\n C-compatible parameters, so be aware of this difference when translating code\n from Perl into Lua, as well as from C into Lua.\n\n Many curses functions have variants starting with the prefixes `w-`, `mv-`,\n and/or `wmv-`. These variants differ only in the explicit addition of a\n window, or by the addition of two coordinates that are used to move the\n cursor first. For example, in C `addch()` has three other variants:\n `waddch()`, `mvaddch()` and `mvwaddch()`.  The Lua equivalents,\n respectively being `stdscr:addch()`, `somewindow:addch()`,\n `stdscr:mvaddch()` and `somewindow:mvaddch()`, with the window argument\n passed implicitly with Lua's `:` syntax sugar.\n\n@module curses\n*/\n\n\n#include \"_helpers.c\"\n\n#ifdef __linux__\n#include \"strlcpy.c\"\n#endif\n\n#include \"chstr.c\"\n#include \"window.c\"\n\nstatic const char *STDSCR_REGISTRY\t= \"curses:stdscr\";\nstatic const char *RIPOFF_TABLE\t\t= \"curses:ripoffline\";\n\n\n/***\nCreate a new line drawing buffer instance.\n@function new_chstr\n@int len number of element to allocate\n@treturn chstr a new char buffer object\n@see curses.chstr\n*/\nstatic int\nPnew_chstr(lua_State *L)\n{\n\tint len = checkint(L, 1);\n\tchstr* ncs = chstr_new(L, len);  /* defined in curses/chstr.c */\n\tmemset(ncs->str, ' ', len*sizeof(chtype));\n\treturn 1;\n}\n\n\n\n#define CCR(n, v)\t\t\t\t\\\n\tlua_pushstring(L, n);\t\t\t\\\n\tlua_pushinteger(L, v);\t\t\t\\\n\tlua_settable(L, -3);\n\n#define CC(s)\t   CCR(#s, s)\n#define CF(i)\t   CCR(LCURSES_STR(LCURSES_SPLICE(KEY_F, i)), KEY_F(i))\n\n/*\n** some of these values are not constant so need to register\n** them directly instead of using a table\n*/\n\nstatic void\nregister_curses_constants(lua_State *L)\n{\n\t/* colors */\n\tCC(COLOR_BLACK);\tCC(COLOR_RED);\t\tCC(COLOR_GREEN);\n\tCC(COLOR_YELLOW);\tCC(COLOR_BLUE);\t\tCC(COLOR_MAGENTA);\n\tCC(COLOR_CYAN);\t\tCC(COLOR_WHITE);\n\n\t/* alternate character set */\n\tCC(ACS_BLOCK);\t\tCC(ACS_BOARD);\n\n\tCC(ACS_BTEE);\t\tCC(ACS_TTEE);\n\tCC(ACS_LTEE);\t\tCC(ACS_RTEE);\n\tCC(ACS_LLCORNER);\tCC(ACS_LRCORNER);\n\tCC(ACS_URCORNER);\tCC(ACS_ULCORNER);\n\n\tCC(ACS_LARROW);\t\tCC(ACS_RARROW);\n\tCC(ACS_UARROW);\t\tCC(ACS_DARROW);\n\n\tCC(ACS_HLINE);\t\tCC(ACS_VLINE);\n\n\tCC(ACS_BULLET);\t\tCC(ACS_CKBOARD);\tCC(ACS_LANTERN);\n\tCC(ACS_DEGREE);\t\tCC(ACS_DIAMOND);\n\n\tCC(ACS_PLMINUS);\tCC(ACS_PLUS);\n\tCC(ACS_S1);\t\tCC(ACS_S9);\n\n\t/* attributes */\n\tCC(A_NORMAL);\t\tCC(A_STANDOUT);\t\tCC(A_UNDERLINE);\n\tCC(A_REVERSE);\t\tCC(A_BLINK);\t\tCC(A_DIM);\n\tCC(A_BOLD);\t\tCC(A_PROTECT);\t\tCC(A_INVIS);\n\tCC(A_ALTCHARSET);\tCC(A_CHARTEXT);\n\tCC(A_ATTRIBUTES);\n#ifdef A_COLOR\n\tCC(A_COLOR);\n#endif\n\n\t/* key functions */\n\tCC(KEY_BREAK);\t\tCC(KEY_DOWN);\t\tCC(KEY_UP);\n\tCC(KEY_LEFT);\t\tCC(KEY_RIGHT);\t\tCC(KEY_HOME);\n\tCC(KEY_BACKSPACE);\n\n\tCC(KEY_DL);\t\tCC(KEY_IL);\t\tCC(KEY_DC);\n\tCC(KEY_IC);\t\tCC(KEY_EIC);\t\tCC(KEY_CLEAR);\n\tCC(KEY_EOS);\t\tCC(KEY_EOL);\t\tCC(KEY_SF);\n\tCC(KEY_SR);\t\tCC(KEY_NPAGE);\t\tCC(KEY_PPAGE);\n\tCC(KEY_STAB);\t\tCC(KEY_CTAB);\t\tCC(KEY_CATAB);\n\tCC(KEY_ENTER);\t\tCC(KEY_SRESET);\t\tCC(KEY_RESET);\n\tCC(KEY_PRINT);\t\tCC(KEY_LL);\t\tCC(KEY_A1);\n\tCC(KEY_A3);\t\tCC(KEY_B2);\t\tCC(KEY_C1);\n\tCC(KEY_C3);\t\tCC(KEY_BTAB);\t\tCC(KEY_BEG);\n\tCC(KEY_CANCEL);\t\tCC(KEY_CLOSE);\t\tCC(KEY_COMMAND);\n\tCC(KEY_COPY);\t\tCC(KEY_CREATE);\t\tCC(KEY_END);\n\tCC(KEY_EXIT);\t\tCC(KEY_FIND);\t\tCC(KEY_HELP);\n\tCC(KEY_MARK);\t\tCC(KEY_MESSAGE); /* ncurses extension: CC(KEY_MOUSE); */\n\tCC(KEY_MOVE);\t\tCC(KEY_NEXT);\t\tCC(KEY_OPEN);\n\tCC(KEY_OPTIONS);\tCC(KEY_PREVIOUS);\tCC(KEY_REDO);\n\tCC(KEY_REFERENCE);\tCC(KEY_REFRESH);\tCC(KEY_REPLACE);\n\tCC(KEY_RESIZE);\t\tCC(KEY_RESTART);\tCC(KEY_RESUME);\n\tCC(KEY_SAVE);\t\tCC(KEY_SBEG);\t\tCC(KEY_SCANCEL);\n\tCC(KEY_SCOMMAND);\tCC(KEY_SCOPY);\t\tCC(KEY_SCREATE);\n\tCC(KEY_SDC);\t\tCC(KEY_SDL);\t\tCC(KEY_SELECT);\n\tCC(KEY_SEND);\t\tCC(KEY_SEOL);\t\tCC(KEY_SEXIT);\n\tCC(KEY_SFIND);\t\tCC(KEY_SHELP);\t\tCC(KEY_SHOME);\n\tCC(KEY_SIC);\t\tCC(KEY_SLEFT);\t\tCC(KEY_SMESSAGE);\n\tCC(KEY_SMOVE);\t\tCC(KEY_SNEXT);\t\tCC(KEY_SOPTIONS);\n\tCC(KEY_SPREVIOUS);\tCC(KEY_SPRINT);\t\tCC(KEY_SREDO);\n\tCC(KEY_SREPLACE);\tCC(KEY_SRIGHT);\t\tCC(KEY_SRSUME);\n\tCC(KEY_SSAVE);\t\tCC(KEY_SSUSPEND);\tCC(KEY_SUNDO);\n\tCC(KEY_SUSPEND);\tCC(KEY_UNDO);\n\n\t/* KEY_Fx  0 <= x <= 63 */\n\tCC(KEY_F0);\n\tCF(1);  CF(2);  CF(3);  CF(4);  CF(5);  CF(6);  CF(7);  CF(8);\n\tCF(9);  CF(10); CF(11); CF(12); CF(13); CF(14); CF(15); CF(16);\n\tCF(17); CF(18); CF(19); CF(20); CF(21); CF(22); CF(23); CF(24);\n\tCF(25); CF(26); CF(27); CF(28); CF(29); CF(30); CF(31); CF(32);\n\tCF(33); CF(34); CF(35); CF(36); CF(37); CF(38); CF(39); CF(40);\n\tCF(41); CF(42); CF(43); CF(44); CF(45); CF(46); CF(47); CF(48);\n\tCF(49); CF(50); CF(51); CF(52); CF(53); CF(54); CF(55); CF(56);\n\tCF(57); CF(58); CF(59); CF(60); CF(61); CF(62); CF(63);\n}\n\n/*\n** make sure screen is restored (and cleared) at exit\n** (for the situations where program is aborted without a\n** proper cleanup)\n*/\n\nvoid\ncleanup_curses(void)\n{\n\tif (!isendwin())\n\t{\n\t\twclear(stdscr);\n\t\twrefresh(stdscr);\n\t\tendwin();\n\t}\n}\n\n\nextern void stack_dump(lua_State *L);\nstatic int\ninit_stdscr(lua_State *L)\n{\n\t/* return stdscr - main window */\n\tlc_newwin(L, stdscr);\n\n\t/* save main window on registry */\n\tlua_pushstring(L, STDSCR_REGISTRY);\n\tlua_pushvalue(L, -2);\n\tlua_rawset(L, LUA_REGISTRYINDEX);\n\n\t/* setup curses constants - curses.xxx numbers */\n\tlua_pushvalue(L, -2);\n\tregister_curses_constants(L);\n\n\t/* install cleanup handler to help in debugging and screen trashing */\n\tatexit(cleanup_curses);\n\n\treturn 1;\n}\n\n\n/***\nClean up terminal prior to exiting or escaping curses.\n@function endwin\n@treturn bool `true`, if successful\n@see endwin(3x)\n*/\nstatic int\nPendwin(lua_State *L)\n{\n\treturn pushokresult(endwin());\n}\n\n\n/***\nHas @{endwin} been called more recently than @{curses.window:refresh}?\n@function isendwin\n@treturn bool whether @{endwin} has been called\n@see isendwin(3x)\n*/\nstatic int\nPisendwin(lua_State *L)\n{\n\treturn pushboolresult(isendwin());\n}\n\n\n/***\nRetern the main screen window.\n@function stdscr\n@treturn window main screen\n@see initscr\n@see stdscr(3x)\n*/\nstatic int\nPstdscr(lua_State *L)\n{\n\tlua_pushstring(L, STDSCR_REGISTRY);\n\tlua_rawget(L, LUA_REGISTRYINDEX);\n\treturn 1;\n}\n\n\n/***\nNumber of columns in the main screen window.\n@function cols\n@treturn int number of columns in the main screen\n@see lines\n@see stdscr\n@see COLS(3x)\n*/\nstatic int\nPcols(lua_State *L)\n{\n\treturn pushintresult(COLS);\n}\n\n\n/***\nNumber of lines in the main screen window.\n@function lines\n@treturn int number of lines in the main screen\n@see cols\n@see stdscr\n@see LINES(3x)\n*/\nstatic int\nPlines(lua_State *L)\n{\n\treturn pushintresult(LINES);\n}\n\n\n/***\nInitialise color output facility.\n@function start_color\n@treturn bool `true`, if successful\n@see can_change_color(3x)\n@see has_colors\n*/\nstatic int\nPstart_color(lua_State *L)\n{\n\treturn pushokresult(start_color());\n}\n\n\n/***\nDoes the terminal have color capability?\n@function has_colors\n@treturn bool `true`, if the terminal supports colors\n@see can_change_color(3x)\n@see start_color\n@usage\nif curses.has_colors () then\n  curses.start_color ()\nend\n*/\nstatic int\nPhas_colors(lua_State *L)\n{\n\treturn pushboolresult(has_colors());\n}\n\n\n/***\nReserve `-1` to represent terminal default colors.\n@function use_default_colors\n@treturn bool `true`, if successful\n@see use_default_colors(3x)\n@fixme ncurses only?\n*/\nstatic int\nPuse_default_colors(lua_State *L)\n{\n\treturn pushokresult(use_default_colors());\n}\n\n\n/***\nSet -1 foreground and background colors.\n@function use_default_colors\n@treturn bool `true`, if successful\n@see use_default_colors(3x)\n@fixme ncurses only?\n*/\nstatic int\nPassume_default_colors(lua_State *L)\n{\n\tint fg = checkint(L, 1);\n\tint bg = checkint(L, 2);\n\treturn pushokresult(assume_default_colors(fg, bg));\n}\n\n\n/***\nAssociate a color pair id with a specific foreground and background color.\n@function init_pair\n@int pair color pair id to act on\n@int f foreground color to assign\n@int b background color to assign\n@treturn bool `true`, if successful\n@see init_pair(3x)\n@see pair_content\n*/\nstatic int\nPinit_pair(lua_State *L)\n{\n\tshort pair = checkint(L, 1);\n\tshort f = checkint(L, 2);\n\tshort b = checkint(L, 3);\n\treturn pushokresult(init_pair(pair, f, b));\n}\n\n\n/***\nReturn the foreground and background colors associated with a color pair id.\n@function pair_content\n@int pair color pair id to act on\n@treturn int foreground color of *pair*\n@treturn int background color of *pair*\n@see can_change_color(3x)\n@see init_pair\n*/\nstatic int\nPpair_content(lua_State *L)\n{\n\tshort pair = checkint(L, 1);\n\tshort f;\n\tshort b;\n\tint ret = pair_content(pair, &f, &b);\n\n\tif (ret == ERR)\n\t\treturn 0;\n\n\tlua_pushinteger(L, f);\n\tlua_pushinteger(L, b);\n\treturn 2;\n}\n\n\n/***\nHow many colors are available for this terminal?\n@function colors\n@treturn int total number of available colors\n@see can_change_color(3x)\n@see color_pairs\n*/\nstatic int\nPcolors(lua_State *L)\n{\n\treturn pushintresult(COLORS);\n}\n\n\n/***\nHow may distinct color pairs are supported by this terminal?\n@function color_pairs\n@treturn int total number of available color pairs\n@see can_change_color(3x)\n@see colors\n*/\nstatic int\nPcolor_pairs(lua_State *L)\n{\n\treturn pushintresult(COLOR_PAIRS);\n}\n\n\n/***\nReturn the attributes for the given color pair id.\n@function color_pair\n@int pair color pair id to act on\n@treturn int attributes for color pair *pair*\n@see can_change_color(3x)\n*/\nstatic int\nPcolor_pair(lua_State *L)\n{\n\tint n = checkint(L, 1);\n\treturn pushintresult(COLOR_PAIR(n));\n}\n\n\n/***\nFetch the output speed of the terminal.\n@function baudrate\n@treturn int output speed of the terminal in bits-per-second\n@see baudrate(3x)\n*/\nstatic int\nPbaudrate(lua_State *L)\n{\n\treturn pushintresult(baudrate());\n}\n\n\n/***\nFetch the terminal's current erase character.\n@function erasechar\n@treturn int current erase character\n@see erasechar(3x)\n*/\nstatic int\nPerasechar(lua_State *L)\n{\n\treturn pushintresult(erasechar());\n}\n\n\n/***\nFetch the character insert and delete capability of the terminal.\n@function has_ic\n@treturn bool `true`, if the terminal has insert and delete character\n  operations\n@see has_ic(3x)\n*/\nstatic int\nPhas_ic(lua_State *L)\n{\n\treturn pushboolresult(has_ic());\n}\n\n\n/***\nFetch the line insert and delete capability of the terminal.\n@function has_il\n@treturn bool `true`, if the terminal has insert and delete line operations\n@see has_il(3x)\n*/\nstatic int\nPhas_il(lua_State *L)\n{\n\treturn pushboolresult(has_il());\n}\n\n\n/***\nFetch the terminal's current kill character.\n@function killchar\n@treturn int current line kill character\n@see killchar(3x)\n*/\nstatic int\nPkillchar(lua_State *L)\n{\n\treturn pushintresult(killchar());\n}\n\n\n/***\nBitwise OR of all (or selected) video attributes supported by the terminal.\n@function termattrs\n@int[opt] a terminal attribute bits\n@treturn[1] bool `true`, if the terminal supports attribute *a*\n@treturn[2] int bitarray of supported terminal attributes\n@see termattrs(3x)\n*/\nstatic int\nPtermattrs(lua_State *L)\n{\n\tif (lua_gettop(L) > 0)\n\t{\n\t\tint a = checkint(L, 1);\n\t\treturn pushboolresult(termattrs() & a);\n\t}\n\treturn pushintresult(termattrs());\n}\n\n\n/***\nFetch the name of the terminal.\n@function termname\n@treturn string terminal name\n@see termname(3x)\n*/\nstatic int\nPtermname(lua_State *L)\n{\n\treturn pushstringresult(termname());\n}\n\n\n/***\nFetch the verbose name of the terminal.\n@function longname\n@treturn string verbose description of the current terminal\n@see longname(3x)\n*/\nstatic int\nPlongname(lua_State *L)\n{\n\treturn pushstringresult(longname());\n}\n\n\n/* there is no easy way to implement this... */\n\nstatic lua_State *rip_L = NULL;\n\nstatic int\nripoffline_cb(WINDOW* w, int cols)\n{\n\tstatic int line = 0;\n\tint top = lua_gettop(rip_L);\n\n\t/* better be safe */\n\tif (!lua_checkstack(rip_L, 5))\n\t\treturn 0;\n\n\t/* get the table from the registry */\n\tlua_pushstring(rip_L, RIPOFF_TABLE);\n\tlua_gettable(rip_L, LUA_REGISTRYINDEX);\n\n\t/* get user callback function */\n\tif (lua_isnil(rip_L, -1)) {\n\t\tlua_pop(rip_L, 1);\n\t\treturn 0;\n\t}\n\n\tlua_rawgeti(rip_L, -1, ++line);\t/* function to be called */\n\tlc_newwin(rip_L, w);\t\t/* create window object */\n\tlua_pushinteger(rip_L, cols);   /* push number of columns */\n\n\tlua_pcall(rip_L, 2,  0, 0);\t/* call the lua function */\n\n\tlua_settop(rip_L, top);\n\treturn 1;\n}\n\n\n/***\nReduce the available size of the main screen.\n@function ripoffline\n@bool top_line\n@func callback\n@treturn bool `true`, if successful\n@see ripoffline(3x)\n*/\nstatic int\nPripoffline(lua_State *L)\n{\n\tstatic int rip = 0;\n\tint top_line = lua_toboolean(L, 1);\n\n\tif (!lua_isfunction(L, 2))\n\t{\n\t\tlua_pushliteral(L, \"invalid callback passed as second parameter\");\n\t\tlua_error(L);\n\t}\n\n\t/* need to save the lua state somewhere... */\n\trip_L = L;\n\n\t/* get the table where we are going to save the callbacks */\n\tlua_pushstring(L, RIPOFF_TABLE);\n\tlua_gettable(L, LUA_REGISTRYINDEX);\n\n\tif (lua_isnil(L, -1))\n\t{\n\t\tlua_pop(L, 1);\n\t\tlua_newtable(L);\n\n\t\tlua_pushstring(L, RIPOFF_TABLE);\n\t\tlua_pushvalue(L, -2);\n\t\tlua_settable(L, LUA_REGISTRYINDEX);\n\t}\n\n\t/* save function callback in registry table */\n\tlua_pushvalue(L, 2);\n\tlua_rawseti(L, -2, ++rip);\n\n\t/* and tell curses we are going to take the line */\n\treturn pushokresult(ripoffline(top_line ? 1 : -1, ripoffline_cb));\n}\n\n\n/***\nChange the visibility of the cursor.\n@function curs_set\n@int vis one of `0` (invisible), `1` (visible) or `2` (very visible)\n@treturn[1] int previous cursor state\n@return[2] nil if *vis* is not supported\n@see curs_set(3x)\n*/\nstatic int\nPcurs_set(lua_State *L)\n{\n\tint vis = checkint(L, 1);\n\tint state = curs_set(vis);\n\tif (state == ERR)\n\t\treturn 0;\n\n\treturn pushintresult(state);\n}\n\n\n/***\nSleep for a few milliseconds.\n@function napms\n@int ms time to wait in milliseconds\n@treturn bool `true`, if successful\n@see napms(3x)\n@see delay_output\n*/\nstatic int\nPnapms(lua_State *L)\n{\n\tint ms = checkint(L, 1);\n\treturn pushokresult(napms(ms));\n}\n\n\n/***\nChange the terminal size.\n@function resizeterm\n@int nlines number of lines\n@int ncols number of columns\n@treturn bool `true`, if successful\n@raise unimplemented\n@fixme ncurses only?\n*/\nstatic int\nPresizeterm(lua_State *L)\n{\n\tint nlines  = checkint(L, 1);\n\tint ncols   = checkint(L, 2);\n\treturn pushokresult(resizeterm (nlines, ncols));\n}\n\n\n/***\nSend the terminal audible bell.\n@function beep\n@treturn bool `true`, if successful\n@see beep(3x)\n@see flash\n*/\nstatic int\nPbeep(lua_State *L)\n{\n\treturn pushokresult(beep());\n}\n\n\n/***\nSend the terminal visible bell.\n@function flash\n@treturn bool `true`, if successful\n@see flash(3x)\n@see beep\n*/\nstatic int\nPflash(lua_State *L)\n{\n\treturn pushokresult(flash());\n}\n\n\n/***\nCreate a new window.\n@function newwin\n@int nlines number of window lines\n@int ncols number of window columns\n@int begin_y top line of window\n@int begin_x leftmost column of window\n@treturn window a new window object\n@see newwin(3x)\n@see curses.window\n*/\nstatic int\nPnewwin(lua_State *L)\n{\n\tint nlines  = checkint(L, 1);\n\tint ncols   = checkint(L, 2);\n\tint begin_y = checkint(L, 3);\n\tint begin_x = checkint(L, 4);\n\n\tlc_newwin(L, newwin(nlines, ncols, begin_y, begin_x));\n\treturn 1;\n}\n\n\n/***\nRefresh the visible terminal screen.\n@function doupdate\n@treturn bool `true`, if successful\n@see doupdate(3x)\n@see curses.window:refresh\n*/\nstatic int\nPdoupdate(lua_State *L)\n{\n\treturn pushokresult(doupdate());\n}\n\n\n/***\nInitialise the soft label keys area.\nThis must be called before @{initscr}.\n@function slk_init\n@int fmt\n@treturn bool `true`, if successful\n@see slk_init(3x)\n*/\nstatic int\nPslk_init(lua_State *L)\n{\n\tint fmt = checkint(L, 1);\n\treturn pushokresult(slk_init(fmt));\n}\n\n\n/***\nSet the label for a soft label key.\n@function slk_set\n@int labnum\n@string label\n@int fmt\n@treturn bool `true`, if successful\n@see slk_set(3x)\n*/\nstatic int\nPslk_set(lua_State *L)\n{\n\tint labnum = checkint(L, 1);\n\tconst char* label = luaL_checkstring(L, 2);\n\tint fmt = checkint(L, 3);\n\treturn pushokresult(slk_set(labnum, label, fmt));\n}\n\n\n/***\nRefresh the soft label key area.\n@function slk_refresh\n@treturn bool `true`, if successful\n@see slk_refresh(3x)\n@see curses.window:refresh\n*/\nstatic int\nPslk_refresh(lua_State *L)\n{\n\treturn pushokresult(slk_refresh());\n}\n\n\n/***\nCopy the soft label key area backing screen to the virtual screen.\n@function slk_noutrefresh\n@treturn bool `true`, if successful\n@see slk_noutrefresh(3x)\n@see curses.window:refresh\n*/\nstatic int\nPslk_noutrefresh(lua_State *L)\n{\n\treturn pushokresult(slk_noutrefresh());\n}\n\n\n/***\nFetch the label for a soft label key.\n@function slk_label\n@int labnum\n@treturn string current label for *labnum*\n@see slk_label(3x)\n*/\nstatic int\nPslk_label(lua_State *L)\n{\n\tint labnum = checkint(L, 1);\n\treturn pushstringresult(slk_label(labnum));\n}\n\n\n/***\nClears the soft labels from the screen.\n@function slk_clear\n@treturn bool `true`, if successful\n@see slk_clear(3x)\n@see slk_restore\n*/\nstatic int\nPslk_clear(lua_State *L)\n{\n\treturn pushokresult(slk_clear());\n}\n\n\n/***\nRestores the soft labels to the screen.\n@function slk_restore\n@treturn bool `true`, if successful\n@see slk_restore(3x)\n@see slk_clear\n*/\nstatic int\nPslk_restore(lua_State *L)\n{\n\treturn pushokresult(slk_restore());\n}\n\n\n/***\nMark the soft label key area for refresh.\n@function slk_touch\n@treturn bool `true`, if successful\n@see slk_touch(3x)\n*/\nstatic int\nPslk_touch(lua_State *L)\n{\n\treturn pushokresult(slk_touch());\n}\n\n\n/***\nEnable an attribute for soft labels.\n@function slk_attron\n@int attrs\n@treturn bool `true`, if successful\n@see slk_attron(3x)\n*/\nstatic int\nPslk_attron(lua_State *L)\n{\n\tchtype attrs = checkch(L, 1);\n\treturn pushokresult(slk_attron(attrs));\n}\n\n\n/***\nDisable an attribute for soft labels.\n@function slk_attroff\n@int attrs\n@treturn bool `true`, if successful\n@see slk_attroff(3x)\n*/\nstatic int\nPslk_attroff(lua_State *L)\n{\n\tchtype attrs = checkch(L, 1);\n\treturn pushokresult(slk_attroff(attrs));\n}\n\n\n/***\nSet the attributes for soft labels.\n@function slk_attrset\n@int attrs\n@treturn bool `true`, if successful\n@see slk_attrset(3x)\n*/\nstatic int\nPslk_attrset(lua_State *L)\n{\n\tchtype attrs = checkch(L, 1);\n\treturn pushokresult(slk_attrset(attrs));\n}\n\n\n/***\nPut the terminal into cbreak mode.\n@function cbreak\n@bool[opt] on\n@treturn bool `true`, if successful\n@see cbreak(3x)\n@see nocbreak(3x)\n*/\nstatic int\nPcbreak(lua_State *L)\n{\n\tif (lua_isnoneornil(L, 1) || lua_toboolean(L, 1))\n\t\treturn pushokresult(cbreak());\n\treturn pushokresult(nocbreak());\n}\n\n\n/***\nWhether characters are echoed to the terminal as they are typed.\n@function echo\n@bool[opt] on\n@treturn bool `true`, if successful\n@see echo(3x)\n@see noecho(3x)\n*/\nstatic int\nPecho(lua_State *L)\n{\n\tif (lua_isnoneornil(L, 1) || lua_toboolean(L, 1))\n\t\treturn pushokresult(echo());\n\treturn pushokresult(noecho());\n}\n\n\n/***\nPut the terminal into raw mode.\n@function raw\n@bool[opt] on\n@treturn bool `true`, if successful\n@see noraw(3x)\n@see raw(3x)\n*/\nstatic int\nPraw(lua_State *L)\n{\n\tif (lua_isnoneornil(L, 1) || lua_toboolean(L, 1))\n\t\treturn pushokresult(raw());\n\treturn pushokresult(noraw());\n}\n\n\n/***\nPut the terminal into halfdelay mode.\n@function halfdelay\n@int tenths delay in tenths of a second\n@treturn bool `true`, if successful\n@see halfdelay(3x)\n*/\nstatic int\nPhalfdelay(lua_State *L)\n{\n\tint tenths = checkint(L, 1);\n\treturn pushokresult(halfdelay(tenths));\n}\n\n\n/***\nWhether to translate a return key to newline on input.\n@function nl\n@bool[opt] on\n@treturn bool `true`, if successful\n@see nl(3x)\n@see nonl(3x)\n*/\nstatic int\nPnl(lua_State *L)\n{\n\tif (lua_isnoneornil(L, 1) || lua_toboolean(L, 1))\n\t\treturn pushokresult(nl());\n\treturn pushokresult(nonl());\n}\n\n\n/***\nReturn a printable representation of a character, ignoring attributes.\n@function unctrl\n@int c character to act on\n@treturn string printable representation of *c*\n@see unctrl(3x)\n@see keyname\n*/\nstatic int\nPunctrl(lua_State *L)\n{\n\tchtype c = checkch(L, 1);\n\treturn pushstringresult(unctrl(c));\n}\n\n\n/***\nReturn a printable representation of a key.\n@function keyname\n@int c a key\n@treturn string key name of *c*\n@see keyname(3x)\n@see unctrl\n*/\nstatic int\nPkeyname(lua_State *L)\n{\n\tint c = checkint(L, 1);\n\treturn pushstringresult(keyname(c));\n}\n\n\n/***\nInsert padding characters to force a short delay.\n@function delay_output\n@int ms delay time in milliseconds\n@treturn bool `true`, if successful\n@see napms\n@fixme ncurses only?\n*/\nstatic int\nPdelay_output(lua_State *L)\n{\n\tint ms = checkint(L, 1);\n\treturn pushokresult(delay_output(ms));\n}\n\n\n/***\nThrow away any typeahead in the keyboard input buffer.\n@function flushinp\n@treturn bool `true`, if successful\n@see flushinp(3x)\n@see ungetch\n*/\nstatic int\nPflushinp(lua_State *L)\n{\n\treturn pushboolresult(flushinp());\n}\n\n\n/***\nReturn a character to the keyboard input buffer.\n@function ungetch\n@int c\n@treturn bool `true`, if successful\n@see ungetch(3x)\n@see flushinp\n*/\nstatic int\nPungetch(lua_State *L)\n{\n\tint c = checkint(L, 1);\n\treturn pushokresult(ungetch(c));\n}\n\n\n/***\nCreate a new pad.\n@function newpad\n@int nlines\n@int ncols\n@treturn bool `true`, if successful\n@see newpad(3x)\n*/\nstatic int\nPnewpad(lua_State *L)\n{\n\tint nlines = checkint(L, 1);\n\tint ncols = checkint(L, 2);\n\tlc_newwin(L, newpad(nlines, ncols));\n\treturn 1;\n}\n\n\nstatic char ti_capname[32];\n\n/***\nFetch terminfo boolean capability.\n@function tigetflag\n@string capname\n@treturn bool content of terminal boolean capability\n@see tigetflag(3x)\n@see terminfo(5)\n*/\nstatic int\nPtigetflag (lua_State *L)\n{\n\tint r;\n\n\tstrlcpy (ti_capname, luaL_checkstring (L, 1), sizeof (ti_capname));\n\tr = tigetflag (ti_capname);\n\tif (-1 == r)\n\t\treturn luaL_error (L, \"`%s' is not a boolean capability\", ti_capname);\n\treturn pushboolresult (r);\n}\n\n\n/***\nFetch terminfo numeric capability.\n@function tigetnum\n@string capname\n@treturn int content of terminal numeric capability\n@see tigetnum(3x)\n@see terminfo(5)\n*/\nstatic int\nPtigetnum (lua_State *L)\n{\n\tint res;\n\n\tstrlcpy (ti_capname, luaL_checkstring (L, 1), sizeof (ti_capname));\n\tres = tigetnum (ti_capname);\n\tif (-2 == res)\n\t\treturn luaL_error (L, \"`%s' is not a numeric capability\", ti_capname);\n\telse if (-1 == res)\n\t\tlua_pushnil (L);\n\telse\n\t\tlua_pushinteger(L, res);\n\treturn 1;\n}\n\n\n/***\nFetch terminfo string capability.\n@function tigetstr\n@string capname\n@treturn string content of terminal string capability\n@see tigetstr(3x)\n@see terminfo(5)\n*/\nstatic int\nPtigetstr (lua_State *L)\n{\n\tconst char *res;\n\n\tstrlcpy (ti_capname, luaL_checkstring (L, 1), sizeof (ti_capname));\n\tres = tigetstr (ti_capname);\n\tif ((char *) -1 == res)\n\t\treturn luaL_error (L, \"`%s' is not a string capability\", ti_capname);\n\telse if (NULL == res)\n\t\tlua_pushnil (L);\n\telse\n\t\tlua_pushstring(L, res);\n\treturn 1;\n}\n\n\nstatic const luaL_Reg curseslib[] =\n{\n\tLCURSES_FUNC( Pbaudrate\t\t),\n\tLCURSES_FUNC( Pbeep\t\t),\n\tLCURSES_FUNC( Pcbreak\t\t),\n\tLCURSES_FUNC( Pcolor_pair\t),\n\tLCURSES_FUNC( Pcolor_pairs\t),\n\tLCURSES_FUNC( Pcolors\t\t),\n\tLCURSES_FUNC( Pcols\t\t),\n\tLCURSES_FUNC( Pcurs_set\t\t),\n\tLCURSES_FUNC( Pdelay_output\t),\n\tLCURSES_FUNC( Pdoupdate\t\t),\n\tLCURSES_FUNC( Pecho\t\t),\n\tLCURSES_FUNC( Pendwin\t\t),\n\tLCURSES_FUNC( Perasechar\t),\n\tLCURSES_FUNC( Pflash\t\t),\n\tLCURSES_FUNC( Pflushinp\t\t),\n\tLCURSES_FUNC( Phalfdelay\t),\n\tLCURSES_FUNC( Phas_colors\t),\n\tLCURSES_FUNC( Phas_ic\t\t),\n\tLCURSES_FUNC( Phas_il\t\t),\n\tLCURSES_FUNC( Pinit_pair\t),\n\tLCURSES_FUNC( Pisendwin\t\t),\n\tLCURSES_FUNC( Pkeyname\t\t),\n\tLCURSES_FUNC( Pkillchar\t\t),\n\tLCURSES_FUNC( Plines\t\t),\n\tLCURSES_FUNC( Plongname\t\t),\n\tLCURSES_FUNC( Pnapms\t\t),\n\tLCURSES_FUNC( Pnew_chstr\t),\n\tLCURSES_FUNC( Pnewpad\t\t),\n\tLCURSES_FUNC( Pnewwin\t\t),\n\tLCURSES_FUNC( Pnl\t\t),\n\tLCURSES_FUNC( Ppair_content\t),\n\tLCURSES_FUNC( Praw\t\t),\n\tLCURSES_FUNC( Presizeterm\t),\n\tLCURSES_FUNC( Pripoffline\t),\n\tLCURSES_FUNC( Pslk_attroff\t),\n\tLCURSES_FUNC( Pslk_attron\t),\n\tLCURSES_FUNC( Pslk_attrset\t),\n\tLCURSES_FUNC( Pslk_clear\t),\n\tLCURSES_FUNC( Pslk_init\t\t),\n\tLCURSES_FUNC( Pslk_label\t),\n\tLCURSES_FUNC( Pslk_noutrefresh\t),\n\tLCURSES_FUNC( Pslk_refresh\t),\n\tLCURSES_FUNC( Pslk_restore\t),\n\tLCURSES_FUNC( Pslk_set\t\t),\n\tLCURSES_FUNC( Pslk_touch\t),\n\tLCURSES_FUNC( Pstart_color\t),\n\tLCURSES_FUNC( Pstdscr\t\t),\n\tLCURSES_FUNC( Ptermattrs\t),\n\tLCURSES_FUNC( Ptermname\t\t),\n\tLCURSES_FUNC( Ptigetflag\t),\n\tLCURSES_FUNC( Ptigetnum\t\t),\n\tLCURSES_FUNC( Ptigetstr\t\t),\n\tLCURSES_FUNC( Punctrl\t\t),\n\tLCURSES_FUNC( Pungetch\t\t),\n\tLCURSES_FUNC( Puse_default_colors),\n\tLCURSES_FUNC( Passume_default_colors),\n\t{NULL, NULL}\n};\n\n/***\nConstants.\n@section constants\n*/\n\n/***\nCurses constants.\nAny constants not available in the underlying system will be `nil` valued,\nsee @{curses.lua}. Many of the `KEY_` constants cannot be generated by\nmodern keyboards and are mostly for historical compatibility with ancient\nterminal hardware keyboards.\n\nNote that almost all of these constants remain undefined (`nil`) until\nafter @{curses.initscr} has returned successfully.\n@table curses\n@int ACS_BLOCK alternate character set solid block\n@int ACS_BOARD alternate character set board of squares\n@int ACS_BTEE alternate character set bottom-tee\n@int ACS_BULLET alternate character set bullet\n@int ACS_CKBOARD alternate character set stipple\n@int ACS_DARROW alternate character set down arrow\n@int ACS_DEGREE alternate character set degrees mark\n@int ACS_DIAMOND alternate character set diamond\n@int ACS_HLINE alternate character set horizontal line\n@int ACS_LANTERN alternate character set lantern\n@int ACS_LARROW alternate character set left arrow\n@int ACS_LLCORNER alternate character set lower left corner\n@int ACS_LRCORNER alternate character set lower right corner\n@int ACS_LTEE alternate character set left tee\n@int ACS_PLMINUS alternate character set plus/minus\n@int ACS_PLUS alternate character set plus\n@int ACS_RARROW alternate character set right arrow\n@int ACS_RTEE alternate character set right tee\n@int ACS_S1 alternate character set scan-line 1\n@int ACS_S9 alternate character set scan-line 9\n@int ACS_TTEE alternate character set top tee\n@int ACS_UARROW alternate character set up arrow\n@int ACS_ULCORNER alternate character set upper left corner\n@int ACS_URCORNER alternate character set upper right corner\n@int ACS_VLINE alternate character set vertical line\n@int A_ALTCHARSET alternatate character set attribute\n@int A_ATTRIBUTES attributed character attributes bitmask\n@int A_BLINK blinking attribute\n@int A_BOLD bold attribute\n@int A_CHARTEXT attributed character text bitmask\n@int A_COLOR attributed character color-pair bitmask\n@int A_DIM half-bright attribute\n@int A_INVIS invisible attribute\n@int A_NORMAL normal attribute (all attributes off)\n@int A_PROTECT protected attribute\n@int A_REVERSE reverse video attribute\n@int A_STANDOUT standout attribute\n@int A_UNDERLINE underline attribute\n@int COLOR_BLACK black color attribute\n@int COLOR_BLUE blue color attribute\n@int COLOR_CYAN cyan color attribute\n@int COLOR_GREEN green color attribute\n@int COLOR_MAGENTA magenta color attribute\n@int COLOR_RED red color attribute\n@int COLOR_WHITE white color attribute\n@int COLOR_YELLOW yellow color attribute\n@int KEY_A1 upper-left of keypad key\n@int KEY_A3 upper-right of keypad key\n@int KEY_B2 center of keypad key\n@int KEY_BACKSPACE backspace key\n@int KEY_BEG beginning key\n@int KEY_BREAK break key\n@int KEY_BTAB backtab key\n@int KEY_C1 bottom-left of keypad key\n@int KEY_C3 bottom-right of keypad key\n@int KEY_CANCEL cancel key\n@int KEY_CATAB clear all tabs key\n@int KEY_CLEAR clear screen key\n@int KEY_CLOSE close key\n@int KEY_COMMAND command key\n@int KEY_COPY copy key\n@int KEY_CREATE create key\n@int KEY_CTAB clear tab key\n@int KEY_DC delete character key\n@int KEY_DL delete line key\n@int KEY_DOWN down arrow key\n@int KEY_EIC exit insert char mode key\n@int KEY_END end key\n@int KEY_ENTER enter key\n@int KEY_EOL clear to end of line key\n@int KEY_EOS clear to end of screen key\n@int KEY_EXIT exit key\n@int KEY_F0 f0 key\n@int KEY_F1 f1 key\n@int KEY_F2 f2 key\n@int KEY_F3 f3 key\n@int KEY_F4 f4 key\n@int KEY_F5 f5 key\n@int KEY_F6 f6 key\n@int KEY_F7 f7 key\n@int KEY_F8 f8 key\n@int KEY_F9 f9 key\n@int KEY_F10 f10 key\n@int KEY_F11 f11 key\n@int KEY_F12 f12 key\n@int KEY_F13 f13 key\n@int KEY_F14 f14 key\n@int KEY_F15 f15 key\n@int KEY_F16 f16 key\n@int KEY_F17 f17 key\n@int KEY_F18 f18 key\n@int KEY_F19 f19 key\n@int KEY_F20 f20 key\n@int KEY_F21 f21 key\n@int KEY_F22 f22 key\n@int KEY_F23 f23 key\n@int KEY_F24 f24 key\n@int KEY_F25 f25 key\n@int KEY_F26 f26 key\n@int KEY_F27 f27 key\n@int KEY_F28 f28 key\n@int KEY_F29 f29 key\n@int KEY_F30 f30 key\n@int KEY_F31 f31 key\n@int KEY_F32 f32 key\n@int KEY_F33 f33 key\n@int KEY_F34 f34 key\n@int KEY_F35 f35 key\n@int KEY_F36 f36 key\n@int KEY_F37 f37 key\n@int KEY_F38 f38 key\n@int KEY_F39 f39 key\n@int KEY_F40 f40 key\n@int KEY_F41 f41 key\n@int KEY_F42 f42 key\n@int KEY_F43 f43 key\n@int KEY_F44 f44 key\n@int KEY_F45 f45 key\n@int KEY_F46 f46 key\n@int KEY_F47 f47 key\n@int KEY_F48 f48 key\n@int KEY_F49 f49 key\n@int KEY_F50 f50 key\n@int KEY_F51 f51 key\n@int KEY_F52 f52 key\n@int KEY_F53 f53 key\n@int KEY_F54 f54 key\n@int KEY_F55 f55 key\n@int KEY_F56 f56 key\n@int KEY_F57 f57 key\n@int KEY_F58 f58 key\n@int KEY_F59 f59 key\n@int KEY_F60 f60 key\n@int KEY_F61 f61 key\n@int KEY_F62 f62 key\n@int KEY_F63 f63 key\n@int KEY_FIND find key\n@int KEY_HELP help key\n@int KEY_HOME home key\n@int KEY_IC enter insert char mode key\n@int KEY_IL insert line key\n@int KEY_LEFT cursor left key\n@int KEY_LL home down or bottom key\n@int KEY_MARK mark key\n@int KEY_MESSAGE message key\n@int KEY_MOUSE mouse event available virtual key\n@int KEY_MOVE move key\n@int KEY_NEXT next object key\n@int KEY_NPAGE next page key\n@int KEY_OPEN open key\n@int KEY_OPTIONS options key\n@int KEY_PPAGE previous page key\n@int KEY_PREVIOUS prewious object key\n@int KEY_PRINT print key\n@int KEY_REDO redo key\n@int KEY_REFERENCE reference key\n@int KEY_REFRESH refresh key\n@int KEY_REPLACE replace key\n@int KEY_RESET hard reset key\n@int KEY_RESIZE resize event virtual key\n@int KEY_RESTART restart key\n@int KEY_RESUME resume key\n@int KEY_RIGHT cursor right key\n@int KEY_SAVE save key\n@int KEY_SBEG shift beginning key\n@int KEY_SCANCEL shift cancel key\n@int KEY_SCOMMAND shift command key\n@int KEY_SCOPY shift copy key\n@int KEY_SCREATE shift create key\n@int KEY_SDC shift delete character key\n@int KEY_SDL shift delete line key\n@int KEY_SELECT select key\n@int KEY_SEND send key\n@int KEY_SEOL shift clear to end of line key\n@int KEY_SEXIT shift exit key\n@int KEY_SF scroll one line forward key\n@int KEY_SFIND shift find key\n@int KEY_SHELP shift help key\n@int KEY_SHOME shift home key\n@int KEY_SIC shift enter insert mode key\n@int KEY_SLEFT shift cursor left key\n@int KEY_SMESSAGE shift message key\n@int KEY_SMOVE shift move key\n@int KEY_SNEXT shift next object key\n@int KEY_SOPTIONS shift options key\n@int KEY_SPREVIOUS shift previous object key\n@int KEY_SPRINT shift print key\n@int KEY_SR scroll one line backward key\n@int KEY_SREDO shift redo key\n@int KEY_SREPLACE shift replace key\n@int KEY_SRESET soft reset key\n@int KEY_SRIGHT shift cursor right key\n@int KEY_SRSUME shift resume key\n@int KEY_SSAVE shift save key\n@int KEY_SSUSPEND shift suspend key\n@int KEY_STAB shift tab key\n@int KEY_SUNDO shift undo key\n@int KEY_SUSPEND suspend key\n@int KEY_UNDO undo key\n@int KEY_UP cursor up key\n@usage\n  -- Print curses constants supported on this host.\n  for name, value in pairs (require \"curses\") do\n    if type (value) == \"number\" then\n      print (name, value)\n     end\n  end\n*/\n\nLUALIB_API int\nluaopen_curses(lua_State *L)\n{\n\tluaopen_curses_window(L);\n\tluaopen_curses_chstr(L);\n\n\tluaL_register(L, \"curses\", curseslib);\n\n\tinit_stdscr(L);\n\n\treturn 1;\n}\n"
  },
  {
    "path": "src/lcurses/curses.lua",
    "content": "--- Lua bindings for curses\nlocal M = curses\n\nfunction M.addch (c) return curses.stdscr():addch(c) end\nfunction M.mvaddch (y, x, c) return curses.stdscr():mvaddch(y, x, c) end\nfunction M.addstr (s) return curses.stdscr():addstr(s) end\nfunction M.mvaddstr (y, x, s) return curses.stdscr():mvaddstr(y, x, s) end\n\nfunction M.attron (a) return curses.stdscr():attron(a) end\nfunction M.attroff (a) return curses.stdscr():attroff(a) end\nfunction M.attrset (a) return curses.stdscr():attrset(a) end\n\nfunction M.clear ()    return curses.stdscr():clear() end\nfunction M.clrtobot () return curses.stdscr():clrtobot() end\nfunction M.clrtoeol () return curses.stdscr():clrtoeol() end\n\nfunction M.getch () return curses.stdscr():getch() end\nfunction M.mvgetch (y, x) return curses.stdscr():getch(y, x) end\nfunction M.getstr (s) return curses.stdscr():getstr(s) end\nfunction M.mvgetstr (y, x, s) return curses.stdscr():mvgetstr(y, x, s) end\n\nfunction M.getyx ()    return curses.stdscr():getyx() end\nfunction M.getmaxyx () return curses.stdscr():getmaxyx() end\nfunction M.keypad (b)  return curses.stdscr():keypad(b) end\nfunction M.move (y,x)  return curses.stdscr():move(y,x) end\nfunction M.refresh ()  return curses.stdscr():refresh() end\nfunction M.timeout (t) return curses.stdscr():timeout(t) end\n\nreturn M\n"
  },
  {
    "path": "src/lcurses/strlcpy.c",
    "content": "/*\t$OpenBSD: strlcpy.c,v 1.4 1999/05/01 18:56:41 millert Exp $\t*/\n\n/*\n * Copyright (c) 1998 Todd C. Miller <Todd.Miller@courtesan.com>\n * All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n * 1. Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n * 2. Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n * 3. The name of the author may not be used to endorse or promote products\n *    derived from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,\n * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY\n * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL\n * THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,\n * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,\n * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;\n * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,\n * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR\n * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF\n * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n#ifndef LCURSES_STRLCPY_C\n#define LCURSES_STRLCPY_C 1\n\n#ifndef HAVE_STRLCPY\n\n#if defined LIBC_SCCS && ! defined lint\nstatic char *rcsid = \"$OpenBSD: strlcpy.c,v 1.4 1999/05/01 18:56:41 millert Exp $\";\n#endif /* LIBC_SCCS and not lint */\n\n#include <sys/types.h>\n#include <string.h>\n\nstatic size_t strlcpy(char *dst, const char *src, size_t siz);\n\n/*\n * Copy src to string dst of size siz.  At most siz-1 characters\n * will be copied.  Always NUL terminates (unless siz == 0).\n * Returns strlen(src); if retval >= siz, truncation occurred.\n */\nstatic size_t strlcpy(char *dst, const char *src, size_t siz)\n{\n        register char *d = dst;\n        register const char *s = src;\n        register size_t n = siz;\n\n        /* Copy as many bytes as will fit */\n        if (n != 0 && --n != 0) {\n                do {\n                        if ((*d++ = *s++) == 0)\n                                break;\n                } while (--n != 0);\n        }\n\n        /* Not enough room in dst, add NUL and traverse rest of src */\n        if (n == 0) {\n                if (siz != 0)\n                        *d = '\\0';\t\t/* NUL-terminate dst */\n                while (*s++)\n                        ;\n        }\n\n        return(s - src - 1);\t/* count does not include NUL */\n}\n\n#endif /* !HAVE_STRLCPY */\n\n#endif /* !LCURSES_STRLCPY_C */\n"
  },
  {
    "path": "src/lcurses/window.c",
    "content": "/*\n * Curses binding for Lua 5.1, 5.2 & 5.3.\n *\n * (c) Gary V. Vaughan <gary@vaughan.pe> 2013-2017\n * (c) Reuben Thomas <rrt@sc3d.org> 2009-2012\n * (c) Tiago Dionizio <tiago.dionizio AT gmail.com> 2004-2007\n *\n * Permission is hereby granted, free of charge, to any person obtaining\n * a copy of this software and associated documentation files (the\n * \"Software\"), to deal in the Software without restriction, including\n * without limitation the rights to use, copy, modify, merge, publish,\n * distribute, sublicense, and/or sell copies of the Software, and to\n * permit persons to whom the Software is furnished to do so, subject to\n * the following conditions:\n *\n * The above copyright notice and this permission notice shall be\n * included in all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.\n * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\n * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,\n * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE\n * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n */\n/***\n Curses Window objects.\n\n The methods documented on this page are available on any Curses Window\n object, such as created by:\n\n     stdscr = curses.initscr ()\n     window = curses.newwin (25, 80, 0, 0)\n\n@classmod curses.window\n*/\n\n#include <ctype.h>\n\n#include \"../teliva.h\"\n#include \"_helpers.c\"\n#include \"chstr.c\"\n\n\nstatic const char *WINDOWMETA = \"curses:window\";\n\nstatic void\nlc_newwin(lua_State *L, WINDOW *nw)\n{\n\tif (nw)\n\t{\n\t\tWINDOW **w = lua_newuserdata(L, sizeof(WINDOW*));\n\t\tluaL_getmetatable(L, WINDOWMETA);\n\t\tlua_setmetatable(L, -2);\n\t\t*w = nw;\n\t}\n\telse\n\t{\n\t\tlua_pushliteral(L, \"failed to create window\");\n\t\tlua_error(L);\n\t}\n}\n\n\nstatic WINDOW **\nlc_getwin(lua_State *L, int offset)\n{\n\tWINDOW **w = (WINDOW**)luaL_checkudata(L, offset, WINDOWMETA);\n\tif (w == NULL)\n\t\tluaL_argerror(L, offset, \"bad curses window\");\n\treturn w;\n}\n\n\nstatic WINDOW *\ncheckwin(lua_State *L, int offset)\n{\n\tWINDOW **w = lc_getwin(L, offset);\n\tif (*w == NULL)\n\t\tluaL_argerror(L, offset, \"attempt to use closed curses window\");\n\treturn *w;\n}\n\n\n/***\nUnique string representation of a @{curses.window}.\n@function __tostring\n@treturn string unique string representation of the window object.\n*/\nstatic int\nW__tostring(lua_State *L)\n{\n\tWINDOW **w = lc_getwin(L, 1);\n\tchar buff[34];\n\tif (*w == NULL)\n\t\tstrcpy(buff, \"closed\");\n\telse\n\t\tsprintf(buff, \"%p\", lua_touserdata(L, 1));\n\tlua_pushfstring(L, \"curses window (%s)\", buff);\n\treturn 1;\n}\n\n\n/***\nFree all the resources associated with a window.\n@function close\n@see delwin(3x)\n*/\nstatic int\nWclose(lua_State *L)\n{\n\tWINDOW **w = lc_getwin(L, 1);\n\tif (*w != NULL && *w != stdscr)\n\t{\n\t\tdelwin(*w);\n\t\t*w = NULL;\n\t}\n\treturn 0;\n}\n\n\n/***\nMove the position of a window.\n@function move_window\n@int y offset frow top of screen\n@int x offset from left of screen\n@treturn bool `true`, if successful\n@see mvwin(3x)\n*/\nstatic int\nWmove_window(lua_State *L)\n{\n\tWINDOW *w = checkwin(L, 1);\n\tint y = checkint(L, 2);\n\tint x = checkint(L, 3);\n\treturn pushokresult(mvwin(w, y, x));\n}\n\n\n/***\nCreate a new subwindow.\n@function sub\n@treturn window a new absolutely positioned subwindow\n@int nlines number of window lines\n@int ncols number of window columns\n@int begin_y top line of window\n@int begin_x leftmost column of window\n@see subwin(3x)\n@see derive\n*/\nstatic int\nWsub(lua_State *L)\n{\n\tWINDOW *orig = checkwin(L, 1);\n\tint nlines  = checkint(L, 2);\n\tint ncols   = checkint(L, 3);\n\tint begin_y = checkint(L, 4);\n\tint begin_x = checkint(L, 5);\n\n\tlc_newwin(L, subwin(orig, nlines, ncols, begin_y, begin_x));\n\treturn 1;\n}\n\n\n/***\nCreate a new derived window.\n@function derive\n@int nlines number of window lines\n@int ncols number of window columns\n@int begin_y top line of window\n@int begin_x leftmost column of window\n@treturn window a new relatively positioned subwindow\n@see derwin(3x)\n@see sub\n*/\nstatic int\nWderive(lua_State *L)\n{\n\tWINDOW *orig = checkwin(L, 1);\n\tint nlines  = checkint(L, 2);\n\tint ncols   = checkint(L, 3);\n\tint begin_y = checkint(L, 4);\n\tint begin_x = checkint(L, 5);\n\n\tlc_newwin(L, derwin(orig, nlines, ncols, begin_y, begin_x));\n\treturn 1;\n}\n\n\n/***\nMove the position of a derived window.\n@function move_derived\n@int par_y lines from top of parent window\n@int par_x columns from left of parent window\n@treturn bool `true`, if successful\n@see mvderwin(3x)\n*/\nstatic int\nWmove_derived(lua_State *L)\n{\n\tWINDOW *w = checkwin(L, 1);\n\tint par_y = checkint(L, 2);\n\tint par_x = checkint(L, 3);\n\treturn pushokresult(mvderwin(w, par_y, par_x));\n}\n\n\n/***\nChange the size of a window.\n@function resize\n@int height new number of lines\n@int width new number of columns\n@treturn bool `true`, if successful\n@fixme ncurses only?\n*/\nstatic int\nWresize(lua_State *L)\n{\n\tWINDOW *w = checkwin(L, 1);\n\tint height = checkint(L, 2);\n\tint width = checkint(L, 3);\n\n\tint c = wresize(w, height, width);\n\tif (c == ERR) return 0;\n\n\treturn pushokresult(true);\n}\n\n\n/***\nMake a duplicate of a window.\n@function clone\n@treturn window a new duplicate of this window\n@see dupwin(3x)\n*/\nstatic int\nWclone(lua_State *L)\n{\n\tWINDOW *w = checkwin(L, 1);\n\tlc_newwin(L, dupwin(w));\n\treturn 1;\n}\n\n\n/***\nMark ancestors of a window for refresh.\n@function syncup\n@see wsyncup(3x)\n*/\nstatic int\nWsyncup(lua_State *L)\n{\n\twsyncup(checkwin(L, 1));\n\treturn 0;\n}\n\n\n/***\nAutomatically mark ancestors of a changed window for refresh.\n@function syncok\n@bool bf\n@treturn bool `true`, if successful\n@fixme ncurses only\n*/\nstatic int\nWsyncok(lua_State *L)\n{\n\tWINDOW *w = checkwin(L, 1);\n\tint bf = lua_toboolean(L, 2);\n\treturn pushokresult(syncok(w, bf));\n}\n\n\n/***\nMark cursor position of ancestors of a window for refresh.\n@function cursyncup\n@see wcursyncup(3x)\n*/\nstatic int\nWcursyncup(lua_State *L)\n{\n\twcursyncup(checkwin(L, 1));\n\treturn 0;\n}\n\n\n/***\nMark child windows for refresh.\n@function syncdown\n@see syncdown(3x)\n@see refresh\n*/\nstatic int\nWsyncdown(lua_State *L)\n{\n\twsyncdown(checkwin(L, 1));\n\treturn 0;\n}\n\n\n/***\nRefresh the window terminal display from the virtual screen.\n@function refresh\n@treturn bool `true`, if successful\n@see wrefresh(3x)\n@see curses.doupdate\n@see noutrefresh\n*/\nstatic int\nWrefresh(lua_State *L)\n{\n\tint result = wrefresh(checkwin(L, 1));\n\trender_trusted_teliva_data(L);\n\treturn pushokresult(result);\n}\n\n\n/***\nCopy the window backing screen to the virtual screen.\n@function noutrefresh\n@treturn bool `true`, if successful\n@see wnoutrefresh(3x)\n@see curses.doupdate\n@see refresh\n*/\nstatic int\nWnoutrefresh(lua_State *L)\n{\n\treturn pushokresult(wnoutrefresh(checkwin(L, 1)));\n}\n\n\n/***\nMark a window as having corrupted display that needs fully redrawing.\n@function redrawwin\n@treturn bool `true`, if successful\n@see redrawwin(3x)\n@see redrawln\n*/\nstatic int\nWredrawwin(lua_State *L)\n{\n\treturn pushokresult(redrawwin(checkwin(L, 1)));\n}\n\n\n/***\nMark a range of lines in a window as corrupted and in need of redrawing.\n@function redrawln\n@int beg_line\n@int num_lines\n@treturn bool `true`, if successful\n@see wredrawln(3x)\n*/\nstatic int\nWredrawln(lua_State *L)\n{\n\tWINDOW *w = checkwin(L, 1);\n\tint beg_line = checkint(L, 2);\n\tint num_lines = checkint(L, 3);\n\treturn pushokresult(wredrawln(w, beg_line, num_lines));\n}\n\n\n/***\nChange the cursor position.\n@function move\n@int y\n@int x\n@treturn bool `true`, if successful\n@see wmove(3x)\n*/\nstatic int\nWmove(lua_State *L)\n{\n\tWINDOW *w = checkwin(L, 1);\n\tint y = checkint(L, 2);\n\tint x = checkint(L, 3);\n\treturn pushokresult(wmove(w, y, x));\n}\n\n\n/***\nScroll the window up *n* lines.\n@function scrl\n@int n\n@treturn bool `true`, if successful\n@see wscrl(3x)\n*/\nstatic int\nWscrl(lua_State *L)\n{\n\tWINDOW *w = checkwin(L, 1);\n\tint n = checkint(L, 2);\n\treturn pushokresult(wscrl(w, n));\n}\n\n\n/***\nSet the changed state of a window since the last refresh.\n@function touch\n@param[opt] changed\n@treturn bool `true`, if successful\n@see touchwin(3x)\n*/\nstatic int\nWtouch(lua_State *L)\n{\n\tWINDOW *w = checkwin(L, 1);\n\tint changed;\n\tif (lua_isnoneornil(L, 2))\n\t\tchanged = TRUE;\n\telse\n\t\tchanged = lua_toboolean(L, 2);\n\n\tif (changed)\n\t\treturn pushokresult(touchwin(w));\n\treturn pushokresult(untouchwin(w));\n}\n\n\n/***\nMark a range of lines as changed since the last refresh.\n@function touchline\n@int y\n@int n\n@param[opt] changed\n@treturn bool `true`, if successful\n@see wtouchln(3x)\n*/\nstatic int\nWtouchline(lua_State *L)\n{\n\tWINDOW *w = checkwin(L, 1);\n\tint y = checkint(L, 2);\n\tint n = checkint(L, 3);\n\tint changed;\n\tif (lua_isnoneornil(L, 4))\n\t\tchanged = TRUE;\n\telse\n\t\tchanged = lua_toboolean(L, 4);\n\treturn pushokresult(wtouchln(w, y, n, changed));\n}\n\n\n/***\nHas a particular window line changed since the last refresh?\n@function is_linetouched\n@int line\n@treturn bool `true`, if successful\n@see is_linetouched(3x)\n*/\nstatic int\nWis_linetouched(lua_State *L)\n{\n\tWINDOW *w = checkwin(L, 1);\n\tint line = checkint(L, 2);\n\treturn pushboolresult(is_linetouched(w, line));\n}\n\n\n/***\nHas a window changed since the last refresh?\n@function is_wintouched\n@treturn bool `true`, if successful\n@see is_wintouched(3x)\n*/\nstatic int\nWis_wintouched(lua_State *L)\n{\n\tWINDOW *w = checkwin(L, 1);\n\treturn pushboolresult(is_wintouched(w));\n}\n\n\n/***\nFetch the cursor position.\n@function getyx\n@treturn int y coordinate of top line\n@treturn int x coordinate of left column\n@see getyx(3x)\n*/\nstatic int\nWgetyx(lua_State *L)\n{\n\tWINDOW *w = checkwin(L, 1);\n\tint y, x;\n\tgetyx(w, y, x);\n\tlua_pushinteger(L, y);\n\tlua_pushinteger(L, x);\n\treturn 2;\n}\n\n\n/***\nFetch the parent-relative coordinates of a subwindow.\n@function getparyx\n@treturn int y coordinate of top line relative to parent window\n@treturn int x coordinate of left column relative to parent window\n@see getparyx(3x)\n*/\nstatic int\nWgetparyx(lua_State *L)\n{\n\tWINDOW *w = checkwin(L, 1);\n\tint y, x;\n\tgetparyx(w, y, x);\n\tlua_pushinteger(L, y);\n\tlua_pushinteger(L, x);\n\treturn 2;\n}\n\n\n/***\nFetch the absolute top-left coordinates of a window.\n@function getbegyx\n@treturn int y coordinate of top line\n@treturn int x coordinate of left column\n@see getbegyx(3x)\n*/\nstatic int\nWgetbegyx(lua_State *L)\n{\n\tWINDOW *w = checkwin(L, 1);\n\tint y, x;\n\tgetbegyx(w, y, x);\n\tlua_pushinteger(L, y);\n\tlua_pushinteger(L, x);\n\treturn 2;\n}\n\n\n/***\nFetch the absolute bottom-right coordinates of a window.\n@function getmaxyx\n@treturn int y coordinate of bottom line\n@treturn int x coordinate of right column\n@see getmaxyx(3x)\n*/\nstatic int\nWgetmaxyx(lua_State *L)\n{\n\tWINDOW *w = checkwin(L, 1);\n\tint y, x;\n\tgetmaxyx(w, y, x);\n\t--y;  // set aside space for the menu bar\n\tlua_pushinteger(L, y);\n\tlua_pushinteger(L, x);\n\treturn 2;\n}\n\n\n/***\nDraw a border around a window.\n@function border\n@int[opt] ls\n@int[opt] rs\n@int[opt] ts\n@int[opt] bs\n@int[opt] tl\n@int[opt] tr\n@int[opt] bl\n@int[opt] br\n@treturn bool `true`, if successful\n@see wborder(3x)\n@usage\n  win:border (curses.ACS_VLINE, curses.ACS_VLINE,\n              curses.ACS_HLINE, curses.ACS_HLINE,\n\t      curses.ACS_ULCORNER, curses.ACS_URCORNER,\n\t      curses.ACS_LLCORNER, curses.ACS_LRCORNER)\n*/\nstatic int\nWborder(lua_State *L)\n{\n\tWINDOW *w = checkwin(L, 1);\n\tchtype ls = optch(L, 2, 0);\n\tchtype rs = optch(L, 3, 0);\n\tchtype ts = optch(L, 4, 0);\n\tchtype bs = optch(L, 5, 0);\n\tchtype tl = optch(L, 6, 0);\n\tchtype tr = optch(L, 7, 0);\n\tchtype bl = optch(L, 8, 0);\n\tchtype br = optch(L, 9, 0);\n\n\treturn pushokresult(wborder(w, ls, rs, ts, bs, tl, tr, bl, br));\n}\n\n\n/***\nDraw a box around a window.\n@function box\n@int verch\n@int horch\n@treturn bool `true`, if successful\n@see box(3x)\n@see border\n@usage\n  win:box (curses.ACS_VLINE, curses.ACS_HLINE)\n*/\nstatic int\nWbox(lua_State *L)\n{\n\tWINDOW *w = checkwin(L, 1);\n\tchtype verch = checkch(L, 2);\n\tchtype horch = checkch(L, 3);\n\n\treturn pushokresult(box(w, verch, horch));\n}\n\n\n/***\nDraw a row of characters from the current cursor position.\n@function hline\n@int ch\n@int n\n@treturn bool `true`, if successful\n@see whline(3x)\n@see mvhline\n@see vline\n@usage\n  _, width = win:getmaxyx ()\n  win:hline (curses.ACS_HLINE, width - curs_x)\n*/\nstatic int\nWhline(lua_State *L)\n{\n\tWINDOW *w = checkwin(L, 1);\n\tchtype ch = checkch(L, 2);\n\tint n = checkint(L, 3);\n\n\treturn pushokresult(whline(w, ch, n));\n}\n\n\n/***\nDraw a column of characters from the current cursor position.\n@function vline\n@int ch\n@int n\n@treturn bool `true`, if successful\n@see wvline(3x)\n@see hline\n@see mvvline\n*/\nstatic int\nWvline(lua_State *L)\n{\n\tWINDOW *w = checkwin(L, 1);\n\tchtype ch = checkch(L, 2);\n\tint n = checkint(L, 3);\n\n\treturn pushokresult(wvline(w, ch, n));\n}\n\n\n/***\nMove the cursor, then draw a row of characters from the new cursor position.\n@function mvhline\n@int y\n@int x\n@int ch\n@int n\n@treturn bool `true`, if successful\n@see mvwhline(3x)\n*/\nstatic int\nWmvhline(lua_State *L)\n{\n\tWINDOW *w = checkwin(L, 1);\n\tint y = checkint(L, 2);\n\tint x = checkint(L, 3);\n\tchtype ch = checkch(L, 4);\n\tint n = checkint(L, 5);\n\n\treturn pushokresult(mvwhline(w, y, x, ch, n));\n}\n\n\n/***\nMove the cursor, then draw a column of characters from the new cursor position.\n@function mvvline\n@int y\n@int x\n@int ch\n@int n\n@treturn bool `true`, if successful\n@see mvwvline(3x)\n*/\nstatic int\nWmvvline(lua_State *L)\n{\n\tWINDOW *w = checkwin(L, 1);\n\tint y = checkint(L, 2);\n\tint x = checkint(L, 3);\n\tchtype ch = checkch(L, 4);\n\tint n = checkint(L, 5);\n\n\treturn pushokresult(mvwvline(w, y, x, ch, n));\n}\n\n\n/***\nWrite blanks to every character position in the window.\n@function erase\n@treturn bool `true`, if successful\n@see werase(3x)\n*/\nstatic int\nWerase(lua_State *L)\n{\n\treturn pushokresult(werase(checkwin(L, 1)));\n}\n\n\n/***\nCall @{erase} and then @{clearok}.\n@function clear\n@treturn bool `true`, if successful\n@see wclear(3x)\n*/\nstatic int\nWclear(lua_State *L)\n{\n\treturn pushokresult(wclear(checkwin(L, 1)));\n}\n\n\n/***\nWrite blanks to every character position after the cursor.\n@function clrtobot\n@treturn bool `true`, if successful\n@see wclrtobot(3x)\n*/\nstatic int\nWclrtobot(lua_State *L)\n{\n\treturn pushokresult(wclrtobot(checkwin(L, 1)));\n}\n\n\n/***\nWrite blanks from the cursor to the end of the current line.\n@function clrtoeol\n@treturn bool `true`, if successful\n@see wclrtoeol(3x)\n*/\nstatic int\nWclrtoeol(lua_State *L)\n{\n\treturn pushokresult(wclrtoeol(checkwin(L, 1)));\n}\n\n\n/***\nAdvance the cursor after writing a character at the old position.\n@function addch\n@int ch\n@treturn bool `true`, if successful\n@see waddch(3x)\n*/\nstatic int\nWaddch(lua_State *L)\n{\n\tWINDOW *w = checkwin(L, 1);\n\tchtype ch = checkch(L, 2);\n\treturn pushokresult(waddch(w, ch));\n}\n\n\n/***\nCall @{move}, then @{addch}.\n@function mvaddch\n@int y\n@int x\n@int ch\n@treturn bool `true`, if successful\n@see mvwaddch(3x)\n*/\nstatic int\nWmvaddch(lua_State *L)\n{\n\tWINDOW *w = checkwin(L, 1);\n\tint y = checkint(L, 2);\n\tint x = checkint(L, 3);\n\tchtype ch = checkch(L, 4);\n\treturn pushokresult(mvwaddch(w, y, x, ch));\n}\n\n\n/***\nCall @{addch} then @{refresh}.\n@function echoch\n@int ch\n@treturn bool `true`, if successful\n@see wechochar(3x)\n*/\nstatic int\nWechoch(lua_State *L)\n{\n\tWINDOW *w = checkwin(L, 1);\n\tchtype ch = checkch(L, 2);\n\treturn pushokresult(wechochar(w, ch));\n}\n\n\n/***\nCopy a @{curses.chstr} starting at the current cursor position.\n@function addchstr\n@int chstr cs\n@int[opt] n\n@treturn bool `true`, if successful\n@see waddchnstr(3x)\n*/\nstatic int\nWaddchstr(lua_State *L)\n{\n\tWINDOW *w = checkwin(L, 1);\n\tint n = optint(L, 3, -1);\n\tchstr *cs = checkchstr(L, 2);\n\n\tif (n < 0 || n > (int) cs->len)\n\t\tn = cs->len;\n\n\treturn pushokresult(waddchnstr(w, cs->str, n));\n}\n\n\n/***\nCall @{move} then @{addchstr}.\n@function mvaddchstr\n@int y\n@int x\n@int[opt] n\n@treturn bool `true`, if successful\n@see mvwaddchnstr(3x)\n*/\nstatic int\nWmvaddchstr(lua_State *L)\n{\n\tWINDOW *w = checkwin(L, 1);\n\tint y = checkint(L, 2);\n\tint x = checkint(L, 3);\n\tint n = optint(L, 5, -1);\n\tchstr *cs = checkchstr(L, 4);\n\n\tif (n < 0 || n > (int) cs->len)\n\t\tn = cs->len;\n\n\treturn pushokresult(mvwaddchnstr(w, y, x, cs->str, n));\n}\n\n\n/***\nCopy a Lua string starting at the current cursor position.\n@function addstr\n@string str\n@int[opt] n\n@treturn bool `true`, if successful\n@see waddnstr(3x)\n*/\nstatic int\nWaddstr(lua_State *L)\n{\n\tWINDOW *w = checkwin(L, 1);\n\tconst char *str = luaL_checkstring(L, 2);\n\tint n = optint(L, 3, -1);\n\treturn pushokresult(waddnstr(w, str, n));\n}\n\n\n/***\nCall @{move} then @{addstr}.\n@function mvaddstr\n@int y\n@int x\n@string str\n@int[opt] n\n@treturn bool `true`, if successful\n@see mvwaddnstr(3x)\n*/\nstatic int\nWmvaddstr(lua_State *L)\n{\n\tWINDOW *w = checkwin(L, 1);\n\tint y = checkint(L, 2);\n\tint x = checkint(L, 3);\n\tconst char *str = luaL_checkstring(L, 4);\n\tint n = optint(L, 5, -1);\n\treturn pushokresult(mvwaddnstr(w, y, x, str, n));\n}\n\n\n/***\nSet the background attributes for subsequently written characters.\n@function wbkgdset\n@int ch\n@see wbkgdset(3x)\n*/\nstatic int\nWwbkgdset(lua_State *L)\n{\n\tWINDOW *w = checkwin(L, 1);\n\tchtype ch = checkch(L, 2);\n\twbkgdset(w, ch);\n\treturn 0;\n}\n\n\n/***\nCall @{wbkgdset} and then set the background of every position accordingly.\n@function wbkgd\n@int ch\n@treturn bool `true`, if successful\n@see wbkgd(3x)\n*/\nstatic int\nWwbkgd(lua_State *L)\n{\n\tWINDOW *w = checkwin(L, 1);\n\tchtype ch = checkch(L, 2);\n\treturn pushokresult(wbkgd(w, ch));\n}\n\n\n/***\nFetch the current background attribute for the window.\n@function getbkgd\n@treturn int current window background attribute\n@see getbkgd(3x)\n@see wbkgd\n*/\nstatic int\nWgetbkgd(lua_State *L)\n{\n\treturn pushintresult(getbkgd(checkwin(L, 1)));\n}\n\n\n/***\nSet the flush on interrupt behaviour for the window.\n@function intrflush\n@bool bf\n@treturn bool `true`, if successful\n@see intrflush(3x)\n*/\nstatic int\nWintrflush(lua_State *L)\n{\n\tWINDOW *w = checkwin(L, 1);\n\tint bf = lua_toboolean(L, 2);\n\treturn pushokresult(intrflush(w, bf));\n}\n\n\n/***\nSet the single value keypad keys behaviour for the window.\n@function keypad\n@bool[opt] on\n@treturn bool `true`, if successful\n@see keypad(3x)\n*/\nstatic int\nWkeypad(lua_State *L)\n{\n\tWINDOW *w = checkwin(L, 1);\n\tint bf = lua_isnoneornil(L, 2) ? 1 : lua_toboolean(L, 2);\n\treturn pushokresult(keypad(w, bf));\n}\n\n\n/***\nForce 8-bit (or 7-bit) input characters for the window.\n@function meta\n@bool on `true` to force 8-bit input characters\n@treturn bool `true`, if successful\n@see meta(3x)\n*/\nstatic int\nWmeta(lua_State *L)\n{\n\tWINDOW *w = checkwin(L, 1);\n\tint bf = lua_toboolean(L, 2);\n\treturn pushokresult(meta(w, bf));\n}\n\n\n/***\nForce @{getch} to be non-blocking.\n@function nodelay\n@bool on\n@treturn bool `true`, if successful\n@see nodelay(3x)\n*/\nstatic int\nWnodelay(lua_State *L)\n{\n\tWINDOW *w = checkwin(L, 1);\n\tint bf = lua_toboolean(L, 2);\n\treturn pushokresult(nodelay(w, bf));\n}\n\n\n/***\nFor differentiating user input from terminal control sequences.\n@function timeout\n@int delay milliseconds, `0` for blocking, `-1` for non-blocking\n@see wtimeout(3x)\n*/\nstatic int\nWtimeout(lua_State *L)\n{\n\tWINDOW *w = checkwin(L, 1);\n\tint delay = checkint(L, 2);\n\twtimeout(w, delay);\n\treturn 0;\n}\n\n\n/***\nReturn input immediately from this window.\n@function notimeout\n@bool bf\n@treturn bool `true`, if successful\n@fixme ncurses only?\n*/\nstatic int\nWnotimeout(lua_State *L)\n{\n\tWINDOW *w = checkwin(L, 1);\n\tint bf = lua_toboolean(L, 2);\n\treturn pushokresult(notimeout(w, bf));\n}\n\n\n/***\nThe next call to @{refresh} will clear and completely redraw the window.\n@function clearok\n@bool bf\n@treturn bool `true`, if successful\n@see clearok(3x)\n*/\nstatic int\nWclearok(lua_State *L)\n{\n\tWINDOW *w = checkwin(L, 1);\n\tint bf = lua_toboolean(L, 2);\n\treturn pushokresult(clearok(w, bf));\n}\n\n\n/***\nUse hardware character insert and delete on supporting terminals.\n@function idcok\n@bool bf\n@treturn bool `true`, if successful\n@see idcok(3x)\n*/\nstatic int\nWidcok(lua_State *L)\n{\n\tWINDOW *w = checkwin(L, 1);\n\tint bf = lua_toboolean(L, 2);\n\tidcok(w, bf);\n\treturn 0;\n}\n\n\n/***\nUse hardware line insert and delete on supporting terminals.\n@function idlok\n@bool bf\n@treturn bool `true`, if successful\n@see idlok(3x)\n*/\nstatic int\nWidlok(lua_State *L)\n{\n\tWINDOW *w = checkwin(L, 1);\n\tint bf = lua_toboolean(L, 2);\n\treturn pushokresult(idlok(w, bf));\n}\n\n\n/***\nNo need to force synchronisation of hardware cursor.\n@function leaveok\n@bool bf\n@treturn bool `true`, if successful\n@see leaveok(3x)\n*/\nstatic int\nWleaveok(lua_State *L)\n{\n\tWINDOW *w = checkwin(L, 1);\n\tint bf = lua_toboolean(L, 2);\n\treturn pushokresult(leaveok(w, bf));\n}\n\n\n/***\nScroll up one line when the cursor writes to the last screen position.\n@function scrollok\n@bool bf\n@treturn bool `true`, if successful\n@see scrollok(3x)\n*/\nstatic int\nWscrollok(lua_State *L)\n{\n\tWINDOW *w = checkwin(L, 1);\n\tint bf = lua_toboolean(L, 2);\n\treturn pushokresult(scrollok(w, bf));\n}\n\n\n/***\nAutomatically call @{refresh} whenever the window content is changed.\n@function immedok\n@bool bf\n@treturn bool `true`, if successful\n@see immedok(3x)\n*/\nstatic int\nWimmedok(lua_State *L)\n{\n\tWINDOW *w = checkwin(L, 1);\n\tint bf = lua_toboolean(L, 2);\n\timmedok(w, bf);\n\treturn 0;\n}\n\n\n/***\nSet a software scrolling region for the window.\n@function wsetscrreg\n@int top top line of the scrolling region (line 0 is the first line)\n@int bot bottom line of the scrolling region\n@treturn bool `true`, if successful\n@see wsetscrreg(3x)\n*/\nstatic int\nWwsetscrreg(lua_State *L)\n{\n\tWINDOW *w = checkwin(L, 1);\n\tint top = checkint(L, 2);\n\tint bot = checkint(L, 3);\n\treturn pushokresult(wsetscrreg(w, top, bot));\n}\n\n\n/***\nOverlay this window on top of another non-destructively.\n@function overlay\n@tparam window dst destination window\n@treturn bool `true`, if successful\n@see overlay(3x)\n@see overwrite\n*/\nstatic int\nWoverlay(lua_State *L)\n{\n\tWINDOW *srcwin = checkwin(L, 1);\n\tWINDOW *dstwin = checkwin(L, 2);\n\treturn pushokresult(overlay(srcwin, dstwin));\n}\n\n\n/***\nDestructively overwrite another window with this one.\n@function overwrite\n@tparam window dst destination window\n@treturn bool `true`, if successful\n@see overwrite(3x)\n*/\nstatic int\nWoverwrite(lua_State *L)\n{\n\tWINDOW *srcwin = checkwin(L, 1);\n\tWINDOW *dstwin = checkwin(L, 2);\n\treturn pushokresult(overwrite(srcwin, dstwin));\n}\n\n\n/***\nOverlay a rectangle of this window over another.\n@function copywin\n@tparam window dst destination window\n@int st top row from this window\n@int sl left column from this window\n@int dt top row of rectangle\n@int dl left column of rectangle\n@int db bottom row of rectangle\n@int dr right column of rectangle\n@bool overlay if `true`, copy destructively like @{overlay}\n@treturn bool `true`, if successful\n@see copywin(3x)\n*/\nstatic int\nWcopywin(lua_State *L)\n{\n\tWINDOW *srcwin = checkwin(L, 1);\n\tWINDOW *dstwin = checkwin(L, 2);\n\tint sminrow = checkint(L, 3);\n\tint smincol = checkint(L, 4);\n\tint dminrow = checkint(L, 5);\n\tint dmincol = checkint(L, 6);\n\tint dmaxrow = checkint(L, 7);\n\tint dmaxcol = checkint(L, 8);\n\tint woverlay = lua_toboolean(L, 9);\n\treturn pushokresult(copywin(srcwin, dstwin, sminrow,\n\t\tsmincol, dminrow, dmincol, dmaxrow, dmaxcol, woverlay));\n}\n\n\n/***\nDelete the character under the cursor.\n@function delch\n@treturn bool `true`, if successful\n@see wdelch(3x)\n*/\nstatic int\nWdelch(lua_State *L)\n{\n\treturn pushokresult(wdelch(checkwin(L, 1)));\n}\n\n\n/***\nCall @{move} then @{delch}.\n@function mvdelch\n@int y\n@int x\n@treturn bool `true`, if successful\n@see mvwdelch(3x)\n*/\nstatic int\nWmvdelch(lua_State *L)\n{\n\tWINDOW *w = checkwin(L, 1);\n\tint y = checkint(L, 2);\n\tint x = checkint(L, 3);\n\treturn pushokresult(mvwdelch(w, y, x));\n}\n\n\n/***\nMove the lines below the cursor up, to delete the current line.\n@function deleteln\n@treturn bool `true`, if successful\n@see wdeleteln(3x)\n*/\nstatic int\nWdeleteln(lua_State *L)\n{\n\treturn pushokresult(wdeleteln(checkwin(L, 1)));\n}\n\n\n/***\nMove the current line and those below down one line, leaving a new blank line.\n@function insertln\n@treturn bool `true`, if successful\n@see wdeleteln(3x)\n*/\nstatic int\nWinsertln(lua_State *L)\n{\n\treturn pushokresult(winsertln(checkwin(L, 1)));\n}\n\n\n/***\nCall @{deleteln} *n* times.\n@function winsdelln\n@int n\n@treturn bool `true`, if successful\n@see winsdelln(3x)\n*/\nstatic int\nWwinsdelln(lua_State *L)\n{\n\tWINDOW *w = checkwin(L, 1);\n\tint n = checkint(L, 2);\n\treturn pushokresult(winsdelln(w, n));\n}\n\n/***\nRead a character from the window input.\n@function getch\n@treturn int character read from input buffer\n@see wgetch(3x)\n@see curses.cbreak\n@see curses.echo\n@see keypad\n*/\nstatic int\nWgetch(lua_State *L)\n{\n\tWINDOW *w = checkwin(L, 1);\n\tint y, x;\n\tgetyx(w, y, x);\n\trender_trusted_teliva_data(L);  /* Apps can draw what they want on screen,\n\t                                 * but Teliva's UI is always visible when\n\t                                 * asking the user to make a decision. */\n\tif (x > COLS-2) x = COLS-2; if (y > LINES-1) y = LINES-1; /* http://gnats.netbsd.org/56664 */\n\tmvaddstr(y, x, \"\");\n\tint c = wgetch(w);\n\n\t/* audit log */\n\tstatic char buffer[1024] = {0};\n\tmemset(buffer, '\\0', 1024);\n\tif (isspace(c))\n\t\tsnprintf(buffer, 1020, \"getch() => %s\", character_name(c));\n\telse\n\t\tsnprintf(buffer, 1020, \"getch() => %c\", c);\n\tappend_to_audit_log(L, buffer);\n\n\tif (c == ERR)\n\t\treturn 0;\n\n\t/* standard menu hotkeys */\n\tif (c == CTRL_U && editor_view_in_progress(L))\n\t\tdeveloper_mode(L);\n\telse if (c == CTRL_X || c == CTRL_U || c == CTRL_P) {\n\t\t/* always confirm; we're going to throw away data past this point */\n\n\t\t/* draw a special menu just for this situation */\n\t\tattron(A_BOLD|A_REVERSE);\n\t\tcolor_set(COLOR_PAIR_MENU, NULL);\n\t\tfor (int x = 0; x < COLS; ++x)\n\t\t\tmvaddch(LINES-1, x, ' ');\n\t\tmenu_column = 2;\n\t\tif (c == CTRL_X)\n\t\t\tdraw_menu_item(\"^x\", \"exit\");\n\t\telse if (c == CTRL_U)\n\t\t\tdraw_menu_item(\"^u\", \"edit app code\");\n\t\telse if (c == CTRL_P)\n\t\t\tdraw_menu_item(\"^p\", \"modify app permissions\");\n\t\tdraw_menu_item(\"anything else\", \"cancel\");\n\t\tcolor_set(COLOR_PAIR_ERROR, NULL);\n\t\tmvaddstr(LINES-1, menu_column+1, \" Please confirm. App will restart, losing unsaved data. \");\n\t\tcolor_set(COLOR_PAIR_NORMAL, NULL);\n\t\tattroff(A_BOLD|A_REVERSE);\n\n\t\tint secondc;\n\t\tdo  /* just in case getch is currently non-blocking (nodelay) */\n\t\t\tsecondc = wgetch(w);\n\t\twhile(secondc == ERR);\n\t\tif (c != secondc)\n\t\t\treturn pushintresult(0);\n\n\t\tif (c == CTRL_X) {\n\t\t\tunlink(\"teliva_editor_state\");\n\t\t\texit(0);\n\t\t}\n\t\tif (c == CTRL_U)\n\t\t\tdeveloper_mode(L);\n\t\tif (c == CTRL_P)\n\t\t\tpermissions_mode(L);\n\t}\n\n\treturn pushintresult(c);\n}\n\n\n/***\nPut back a character obtained from @{getch}\n@function ungetch\n@int ch\n@treturn OK or ERR\n@see mvwgetch(3x)\n@see getch\n*/\nstatic int\nWungetch(lua_State *L)\n{\n\tint ch = checkint(L, 2);\n\tint result = ungetch(ch);\n\tif (result == ERR)\n\t\treturn 0;\n\treturn pushintresult(result);\n}\n\n\n/***\nCall @{move} then @{getch}\n@function mvgetch\n@int y\n@int x\n@treturn int character read from input buffer\n@see mvwgetch(3x)\n@see getch\n*/\nstatic int\nWmvgetch(lua_State *L)\n{\n\tWINDOW *w = checkwin(L, 1);\n\tint y = checkint(L, 2);\n\tint x = checkint(L, 3);\n\tint c;\n\n\tif (wmove(w, y, x) == ERR)\n\t\treturn 0;\n\n\tc = wgetch(w);\n\n\tif (c == ERR)\n\t\treturn 0;\n\n\treturn pushintresult(c);\n}\n\n\n/***\nFetch the attributed character at the current cursor position.\n@function winch\n@treturn int attributed character read from input buffer\n@see winch(3x)\n*/\nstatic int\nWwinch(lua_State *L)\n{\n\tWINDOW *w = checkwin(L, 1);\n\treturn pushintresult(winch(w));\n}\n\n\n/***\nCall @{move} then @{winch}\n@function mvwinch\n@int y\n@int x\n@treturn int attributed character read from input buffer\n@see mvwinch(3x)\n*/\nstatic int\nWmvwinch(lua_State *L)\n{\n\tWINDOW *w = checkwin(L, 1);\n\tint y = checkint(L, 2);\n\tint x = checkint(L, 3);\n\treturn pushintresult(mvwinch(w, y, x));\n}\n\n\n/***\nFetch attributed characters from cursor position up to rightmost window position.\n@function winchnstr\n@int n\n@treturn curses.chstr characters from cursor to end of line\n@see winchnstr(3x)\n@see winnstr\n*/\nstatic int\nWwinchnstr(lua_State *L)\n{\n\tWINDOW *w = checkwin(L, 1);\n\tint n = checkint(L, 2);\n\tchstr *cs = chstr_new(L, n);\n\n\tif (winchnstr(w, cs->str, n) == ERR)\n\t\treturn 0;\n\n\treturn 1;\n}\n\n\n/***\nCall @{move} then @{winchnstr}.\n@function mvwinchnstr\n@int y\n@int x\n@int n\n@treturn curses.chstr characters from cursor to end of line\n@see mvwinchnstr(3x)\n*/\nstatic int\nWmvwinchnstr(lua_State *L)\n{\n\tWINDOW *w = checkwin(L, 1);\n\tint y = checkint(L, 2);\n\tint x = checkint(L, 3);\n\tint n = checkint(L, 4);\n\tchstr *cs = chstr_new(L, n);\n\n\tif (mvwinchnstr(w, y, x, cs->str, n) == ERR)\n\t\treturn 0;\n\n\treturn 1;\n}\n\n\n/***\nFetch a string from cursor position up to rightmost window position.\n@function winnstr\n@int n\n@treturn string string read from input buffer\n@see winnstr(3x)\n@see winchnstr\n*/\nstatic int\nWwinnstr(lua_State *L)\n{\n\tWINDOW *w = checkwin(L, 1);\n\tint n = checkint(L, 2);\n\tchar buf[LUAL_BUFFERSIZE];\n\tchar *ptr = buf;\n\n\tif (abs(n) >= LUAL_BUFFERSIZE)\n\t    ptr = lua_newuserdata(L, (size_t) abs(n) + 1);\n\n\tn = winnstr(w, ptr, n);\n\tif (n == ERR)\n\t    return 0;\n\n\tlua_pushlstring(L, ptr, n);\n\treturn 1;\n}\n\n\n/***\nCall @{move} then @{winnstr}.\n@function mvwinnstr\n@int y\n@int x\n@int n\n@treturn string string read from input buffer\n@see mvwinnstr(3x)\n*/\nstatic int\nWmvwinnstr(lua_State *L)\n{\n\tWINDOW *w = checkwin(L, 1);\n\tint y = checkint(L, 2);\n\tint x = checkint(L, 3);\n\tint n = checkint(L, 4);\n\tchar buf[LUAL_BUFFERSIZE];\n\tchar *ptr = buf;\n\n\tif (abs(n) >= LUAL_BUFFERSIZE)\n\t    ptr = lua_newuserdata(L, (size_t) abs(n) + 1);\n\n\tn = mvwinnstr(w, y, x, ptr, n);\n\tif (n == ERR)\n\t    return 0;\n\n\tlua_pushlstring(L, ptr, n);\n\treturn 1;\n}\n\n\n/***\nInsert an attributed character before the current cursor position.\n@function winsch\n@int ch\n@treturn bool `true`, if successful\n@see winsch(3x)\n*/\nstatic int\nWwinsch(lua_State *L)\n{\n\tWINDOW *w = checkwin(L, 1);\n\tchtype ch = checkch(L, 2);\n\treturn pushokresult(winsch(w, ch));\n}\n\n\n/***\nCall @{move} then @{winsch}.\n@function mvwinsch\n@int y\n@int x\n@int ch\n@treturn bool `true`, if successful\n@see mvwinsch(3x)\n*/\nstatic int\nWmvwinsch(lua_State *L)\n{\n\tWINDOW *w = checkwin(L, 1);\n\tint y = checkint(L, 2);\n\tint x = checkint(L, 3);\n\tchtype ch = checkch(L, 4);\n\treturn pushokresult(mvwinsch(w, y, x, ch));\n}\n\n\n/***\nInsert a string of characters before the current cursor position.\n@function winsstr\n@string str\n@treturn bool `true`, if successful\n@see winsstr(3x)\n*/\nstatic int\nWwinsstr(lua_State *L)\n{\n\tWINDOW *w = checkwin(L, 1);\n\tconst char *str = luaL_checkstring(L, 2);\n\treturn pushokresult(winsnstr(w, str, lua_strlen(L, 2)));\n}\n\n\n/***\nCall @{move} then @{winsstr}.\n@function mvwinsstr\n@int y\n@int x\n@string str\n@treturn bool `true`, if successful\n@see mvwinsstr(3x)\n*/\nstatic int\nWmvwinsstr(lua_State *L)\n{\n\tWINDOW *w = checkwin(L, 1);\n\tint y = checkint(L, 2);\n\tint x = checkint(L, 3);\n\tconst char *str = luaL_checkstring(L, 4);\n\treturn pushokresult(mvwinsnstr(w, y, x, str, lua_strlen(L, 2)));\n}\n\n\n/***\nLike @{winsstr}, but no more than *n* characters.\n@function winsnstr\n@string str\n@int n\n@treturn bool `true`, if successful\n@see winsnstr(3x)\n*/\nstatic int\nWwinsnstr(lua_State *L)\n{\n\tWINDOW *w = checkwin(L, 1);\n\tconst char *str = luaL_checkstring(L, 2);\n\tint n = checkint(L, 3);\n\treturn pushokresult(winsnstr(w, str, n));\n}\n\n\n/***\nCall @{move} then @{winsnstr}.\n@function mvwinsnstr\n@int y\n@int x\n@string str\n@int n\n@treturn bool `true`, if successful\n@see mvwinsnstr(3x)\n*/\nstatic int\nWmvwinsnstr(lua_State *L)\n{\n\tWINDOW *w = checkwin(L, 1);\n\tint y = checkint(L, 2);\n\tint x = checkint(L, 3);\n\tconst char *str = luaL_checkstring(L, 4);\n\tint n = checkint(L, 5);\n\treturn pushokresult(mvwinsnstr(w, y, x, str, n));\n}\n\n\n/***\nReturn a new subpad window object.\n@function subpad\n@int nlines\n@int ncols\n@int begin_y\n@int begin_x\n@treturn window a new subpad window object\n@see subpad(3x)\n*/\nstatic int\nWsubpad(lua_State *L)\n{\n\tWINDOW *orig = checkwin(L, 1);\n\tint nlines  = checkint(L, 2);\n\tint ncols   = checkint(L, 3);\n\tint begin_y = checkint(L, 4);\n\tint begin_x = checkint(L, 5);\n\n\tlc_newwin(L, subpad(orig, nlines, ncols, begin_y, begin_x));\n\treturn 1;\n}\n\n\n/***\nEquivalent to @{refresh} for use with pad windows.\n@function prefresh\n@int st top row from this pad window\n@int sl left column from this pad window\n@int dt top row of rectangle\n@int dl left column of rectangle\n@int db bottom row of rectangle\n@int dr right column of rectangle\n@treturn bool `true`, if successful\n@see prefresh(3x)\n*/\nstatic int\nWprefresh(lua_State *L)\n{\n\tWINDOW *p = checkwin(L, 1);\n\tint pminrow = checkint(L, 2);\n\tint pmincol = checkint(L, 3);\n\tint sminrow = checkint(L, 4);\n\tint smincol = checkint(L, 5);\n\tint smaxrow = checkint(L, 6);\n\tint smaxcol = checkint(L, 7);\n\n\treturn pushokresult(prefresh(p, pminrow, pmincol,\n\t\tsminrow, smincol, smaxrow, smaxcol));\n}\n\n\n/***\nEquivalent to @{noutrefresh} for use with pad windows.\n@function pnoutrefresh\n@int st top row from this pad window\n@int sl left column from this pad window\n@int dt top row of rectangle\n@int dl left column of rectangle\n@int db bottom row of rectangle\n@int dr right column of rectangle\n@treturn bool `true`, if successful\n@see pnoutrefresh(3x)\n*/\nstatic int\nWpnoutrefresh(lua_State *L)\n{\n\tWINDOW *p = checkwin(L, 1);\n\tint pminrow = checkint(L, 2);\n\tint pmincol = checkint(L, 3);\n\tint sminrow = checkint(L, 4);\n\tint smincol = checkint(L, 5);\n\tint smaxrow = checkint(L, 6);\n\tint smaxcol = checkint(L, 7);\n\n\treturn pushokresult(pnoutrefresh(p, pminrow, pmincol,\n\t\tsminrow, smincol, smaxrow, smaxcol));\n}\n\n\n/***\nAn efficient equivalent to @{addch} followed by @{refresh}.\n@function pechochar\n@int ch\n@treturn bool `true`, if successful\n@see pechochar(3x)\n*/\nstatic int\nWpechochar(lua_State *L)\n{\n\tWINDOW *p = checkwin(L, 1);\n\tchtype ch = checkch(L, 2);\n\treturn pushokresult(pechochar(p, ch));\n}\n\n\n/***\nTurn off the given attributes for subsequent writes to the window.\n@function attroff\n@int attrs\n@treturn bool `true`, if successful\n@see wattroff(3x)\n@see standend\n*/\nstatic int\nWattroff(lua_State *L)\n{\n\tWINDOW *w = checkwin(L, 1);\n\tint attrs = checkint(L, 2);\n\treturn pushokresult(wattroff(w, attrs));\n}\n\n\n/***\nTurn on the given attributes for subsequent writes to the window.\n@function attron\n@int attrs\n@treturn bool `true`, if successful\n@see wattron(3x)\n*/\nstatic int\nWattron(lua_State *L)\n{\n\tWINDOW *w = checkwin(L, 1);\n\tint attrs = checkint(L, 2);\n\treturn pushokresult(wattron(w, attrs));\n}\n\n\n/***\nSet the given attributes for subsequent writes to the window.\n@function attrset\n@int attrs\n@treturn bool `true`, if successful\n@see wattrset(3x)\n*/\nstatic int\nWattrset(lua_State *L)\n{\n\tWINDOW *w = checkwin(L, 1);\n\tint attrs = checkint(L, 2);\n\treturn pushokresult(wattrset(w, attrs));\n}\n\n\n/***\nTurn off all attributes for subsequent writes to the window.\n@function standend\n@treturn bool `true`, if successful\n@see wstandend(3x)\n*/\nstatic int\nWstandend(lua_State *L)\n{\n\treturn pushokresult(wstandend(checkwin(L, 1)));\n}\n\n\n/***\nSet `A_STANDOUT` for subsequent writes to the window.\n@function standout\n@treturn bool `true`, if successful\n@see wstandout(3x)\n*/\nstatic int\nWstandout(lua_State *L)\n{\n\treturn pushokresult(wstandout(checkwin(L, 1)));\n}\n\n\nstatic const luaL_Reg curses_window_fns[] =\n{\n\tLCURSES_FUNC( W__tostring\t),\n\tLCURSES_FUNC( Waddch\t\t),\n\tLCURSES_FUNC( Waddchstr\t\t),\n\tLCURSES_FUNC( Waddstr\t\t),\n\tLCURSES_FUNC( Wattroff\t\t),\n\tLCURSES_FUNC( Wattron\t\t),\n\tLCURSES_FUNC( Wattrset\t\t),\n\tLCURSES_FUNC( Wborder\t\t),\n\tLCURSES_FUNC( Wbox\t\t),\n\tLCURSES_FUNC( Wclear\t\t),\n\tLCURSES_FUNC( Wclearok\t\t),\n\tLCURSES_FUNC( Wclone\t\t),\n\tLCURSES_FUNC( Wclose\t\t),\n\tLCURSES_FUNC( Wclrtobot\t\t),\n\tLCURSES_FUNC( Wclrtoeol\t\t),\n\tLCURSES_FUNC( Wcopywin\t\t),\n\tLCURSES_FUNC( Wcursyncup\t),\n\tLCURSES_FUNC( Wdelch\t\t),\n\tLCURSES_FUNC( Wdeleteln\t\t),\n\tLCURSES_FUNC( Wderive\t\t),\n\tLCURSES_FUNC( Wechoch\t\t),\n\tLCURSES_FUNC( Werase\t\t),\n\tLCURSES_FUNC( Wgetbegyx\t\t),\n\tLCURSES_FUNC( Wgetbkgd\t\t),\n\tLCURSES_FUNC( Wgetch\t\t),\n\tLCURSES_FUNC( Wgetmaxyx\t\t),\n\tLCURSES_FUNC( Wgetparyx\t\t),\n\t/* no 'getstr' because there's no way to hook standard Teliva hotkeys into it */\n\tLCURSES_FUNC( Wgetyx\t\t),\n\tLCURSES_FUNC( Whline\t\t),\n\tLCURSES_FUNC( Widcok\t\t),\n\tLCURSES_FUNC( Widlok\t\t),\n\tLCURSES_FUNC( Wimmedok\t\t),\n\tLCURSES_FUNC( Winsertln\t\t),\n\tLCURSES_FUNC( Wintrflush\t),\n\tLCURSES_FUNC( Wis_linetouched\t),\n\tLCURSES_FUNC( Wis_wintouched\t),\n\tLCURSES_FUNC( Wkeypad\t\t),\n\tLCURSES_FUNC( Wleaveok\t\t),\n\tLCURSES_FUNC( Wmeta\t\t),\n\tLCURSES_FUNC( Wmove\t\t),\n\tLCURSES_FUNC( Wmove_derived\t),\n\tLCURSES_FUNC( Wmove_window\t),\n\tLCURSES_FUNC( Wmvaddch\t\t),\n\tLCURSES_FUNC( Wmvaddchstr\t),\n\tLCURSES_FUNC( Wmvaddstr\t\t),\n\tLCURSES_FUNC( Wmvdelch\t\t),\n\tLCURSES_FUNC( Wmvgetch\t\t),\n\t/* no 'mvgetstr' because there's no way to hook standard Teliva hotkeys into it */\n\tLCURSES_FUNC( Wmvhline\t\t),\n\tLCURSES_FUNC( Wmvvline\t\t),\n\tLCURSES_FUNC( Wmvwinch\t\t),\n\tLCURSES_FUNC( Wmvwinchnstr\t),\n\tLCURSES_FUNC( Wmvwinnstr\t),\n\tLCURSES_FUNC( Wmvwinsch\t\t),\n\tLCURSES_FUNC( Wmvwinsnstr\t),\n\tLCURSES_FUNC( Wmvwinsstr\t),\n\tLCURSES_FUNC( Wnodelay\t\t),\n\tLCURSES_FUNC( Wnotimeout\t),\n\tLCURSES_FUNC( Wnoutrefresh\t),\n\tLCURSES_FUNC( Woverlay\t\t),\n\tLCURSES_FUNC( Woverwrite\t),\n\tLCURSES_FUNC( Wpechochar\t),\n\tLCURSES_FUNC( Wpnoutrefresh\t),\n\tLCURSES_FUNC( Wprefresh\t\t),\n\tLCURSES_FUNC( Wredrawln\t\t),\n\tLCURSES_FUNC( Wredrawwin\t),\n\tLCURSES_FUNC( Wrefresh\t\t),\n\tLCURSES_FUNC( Wresize\t\t),\n\tLCURSES_FUNC( Wscrl\t\t),\n\tLCURSES_FUNC( Wscrollok\t\t),\n\tLCURSES_FUNC( Wstandend\t\t),\n\tLCURSES_FUNC( Wstandout\t\t),\n\tLCURSES_FUNC( Wsub\t\t),\n\tLCURSES_FUNC( Wsubpad\t\t),\n\tLCURSES_FUNC( Wsyncdown\t\t),\n\tLCURSES_FUNC( Wsyncok\t\t),\n\tLCURSES_FUNC( Wsyncup\t\t),\n\tLCURSES_FUNC( Wtimeout\t\t),\n\tLCURSES_FUNC( Wtouch\t\t),\n\tLCURSES_FUNC( Wtouchline\t),\n\tLCURSES_FUNC( Wungetch\t\t),\n\tLCURSES_FUNC( Wvline\t\t),\n\tLCURSES_FUNC( Wwbkgd\t\t),\n\tLCURSES_FUNC( Wwbkgdset\t\t),\n\tLCURSES_FUNC( Wwinch\t\t),\n\tLCURSES_FUNC( Wwinchnstr\t),\n\tLCURSES_FUNC( Wwinnstr\t\t),\n\tLCURSES_FUNC( Wwinsch\t\t),\n\tLCURSES_FUNC( Wwinsdelln\t),\n\tLCURSES_FUNC( Wwinsnstr\t\t),\n\tLCURSES_FUNC( Wwinsstr\t\t),\n\tLCURSES_FUNC( Wwsetscrreg\t),\n\t{\"__gc\",     Wclose\t\t}, /* rough safety net */\n\t{NULL, NULL}\n};\n\n\nLUALIB_API int\nluaopen_curses_window(lua_State *L)\n{\n\tint t, mt;\n\n\tluaL_register(L, \"curses.window\", curses_window_fns);\n\tt = lua_gettop(L);\n\n\tluaL_newmetatable(L, WINDOWMETA);\n\tmt = lua_gettop(L);\n\n\tlua_pushvalue(L, mt);\n\tlua_setfield(L, mt, \"__index\");\t\t/* mt.__index = mt */\n\tlua_pushliteral(L, \"CursesWindow\");\n\tlua_setfield(L, mt, \"_type\");\t\t/* mt._type = \"Curses Window\" */\n\n\t/* for k,v in pairs(t) do mt[k]=v end */\n\tfor (lua_pushnil(L); lua_next(L, t) != 0;)\n\t\tlua_setfield(L, mt, lua_tostring(L, -2));\n\n\tlua_pop(L, 1);\t\t\t\t/* pop mt */\n\n\treturn 1;\n}\n"
  },
  {
    "path": "src/ldebug.c",
    "content": "/*\n** $Id: ldebug.c,v 2.29.1.6 2008/05/08 16:56:26 roberto Exp $\n** Debug Interface\n** See Copyright Notice in lua.h\n*/\n\n\n#include <stdarg.h>\n#include <stddef.h>\n#include <string.h>\n\n\n#define ldebug_c\n#define LUA_CORE\n\n#include \"lua.h\"\n\n#include \"lapi.h\"\n#include \"lcode.h\"\n#include \"ldebug.h\"\n#include \"ldo.h\"\n#include \"lfunc.h\"\n#include \"lobject.h\"\n#include \"lopcodes.h\"\n#include \"lstate.h\"\n#include \"lstring.h\"\n#include \"ltable.h\"\n#include \"ltm.h\"\n#include \"lvm.h\"\n\n\n\nstatic const char *getfuncname (lua_State *L, CallInfo *ci, const char **name);\n\n\nstatic int currentpc (lua_State *L, CallInfo *ci) {\n  if (!isLua(ci)) return -1;  /* function is not a Lua function? */\n  if (ci == L->ci)\n    ci->savedpc = L->savedpc;\n  return pcRel(ci->savedpc, ci_func(ci)->l.p);\n}\n\n\nstatic int currentline (lua_State *L, CallInfo *ci) {\n  int pc = currentpc(L, ci);\n  if (pc < 0)\n    return -1;  /* only active lua functions have current-line information */\n  else\n    return getline(ci_func(ci)->l.p, pc);\n}\n\n\n/*\n** this function can be called asynchronous (e.g. during a signal)\n*/\nLUA_API int lua_sethook (lua_State *L, lua_Hook func, int mask, int count) {\n  if (func == NULL || mask == 0) {  /* turn off hooks? */\n    mask = 0;\n    func = NULL;\n  }\n  L->hook = func;\n  L->basehookcount = count;\n  resethookcount(L);\n  L->hookmask = cast_byte(mask);\n  return 1;\n}\n\n\nLUA_API lua_Hook lua_gethook (lua_State *L) {\n  return L->hook;\n}\n\n\nLUA_API int lua_gethookmask (lua_State *L) {\n  return L->hookmask;\n}\n\n\nLUA_API int lua_gethookcount (lua_State *L) {\n  return L->basehookcount;\n}\n\n\nLUA_API int lua_getstack (lua_State *L, int level, lua_Debug *ar) {\n  int status;\n  CallInfo *ci;\n  lua_lock(L);\n  for (ci = L->ci; level > 0 && ci > L->base_ci; ci--) {\n    level--;\n    if (f_isLua(ci))  /* Lua function? */\n      level -= ci->tailcalls;  /* skip lost tail calls */\n  }\n  if (level == 0 && ci > L->base_ci) {  /* level found? */\n    status = 1;\n    ar->i_ci = cast_int(ci - L->base_ci);\n  }\n  else if (level < 0) {  /* level is of a lost tail call? */\n    status = 1;\n    ar->i_ci = 0;\n  }\n  else status = 0;  /* no such level */\n  lua_unlock(L);\n  return status;\n}\n\n\nstatic Proto *getluaproto (CallInfo *ci) {\n  return (isLua(ci) ? ci_func(ci)->l.p : NULL);\n}\n\n\nstatic const char *findlocal (lua_State *L, CallInfo *ci, int n) {\n  const char *name;\n  Proto *fp = getluaproto(ci);\n  if (fp && (name = luaF_getlocalname(fp, n, currentpc(L, ci))) != NULL)\n    return name;  /* is a local variable in a Lua function */\n  else {\n    StkId limit = (ci == L->ci) ? L->top : (ci+1)->func;\n    if (limit - ci->base >= n && n > 0)  /* is 'n' inside 'ci' stack? */\n      return \"(*temporary)\";\n    else\n      return NULL;\n  }\n}\n\n\nLUA_API const char *lua_getlocal (lua_State *L, const lua_Debug *ar, int n) {\n  CallInfo *ci = L->base_ci + ar->i_ci;\n  const char *name = findlocal(L, ci, n);\n  lua_lock(L);\n  if (name)\n      luaA_pushobject(L, ci->base + (n - 1));\n  lua_unlock(L);\n  return name;\n}\n\n\nLUA_API const char *lua_setlocal (lua_State *L, const lua_Debug *ar, int n) {\n  CallInfo *ci = L->base_ci + ar->i_ci;\n  const char *name = findlocal(L, ci, n);\n  lua_lock(L);\n  if (name)\n      setobjs2s(L, ci->base + (n - 1), L->top - 1);\n  L->top--;  /* pop value */\n  lua_unlock(L);\n  return name;\n}\n\n\nstatic void funcinfo (lua_Debug *ar, Closure *cl) {\n  if (cl->c.isC) {\n    ar->source = \"=[C]\";\n    ar->linedefined = -1;\n    ar->lastlinedefined = -1;\n    ar->what = \"C\";\n  }\n  else {\n    ar->source = getstr(cl->l.p->source);\n    ar->linedefined = cl->l.p->linedefined;\n    ar->lastlinedefined = cl->l.p->lastlinedefined;\n    ar->what = (ar->linedefined == 0) ? \"main\" : \"Lua\";\n  }\n  luaO_chunkid(ar->short_src, ar->source, LUA_IDSIZE);\n}\n\n\nstatic void info_tailcall (lua_Debug *ar) {\n  ar->name = ar->namewhat = \"\";\n  ar->what = \"tail\";\n  ar->lastlinedefined = ar->linedefined = ar->currentline = -1;\n  ar->source = \"=(tail call)\";\n  luaO_chunkid(ar->short_src, ar->source, LUA_IDSIZE);\n  ar->nups = 0;\n}\n\n\nstatic void collectvalidlines (lua_State *L, Closure *f) {\n  if (f == NULL || f->c.isC) {\n    setnilvalue(L->top);\n  }\n  else {\n    Table *t = luaH_new(L, 0, 0);\n    int *lineinfo = f->l.p->lineinfo;\n    int i;\n    for (i=0; i<f->l.p->sizelineinfo; i++)\n      setbvalue(luaH_setnum(L, t, lineinfo[i]), 1);\n    sethvalue(L, L->top, t); \n  }\n  incr_top(L);\n}\n\n\nstatic int auxgetinfo (lua_State *L, const char *what, lua_Debug *ar,\n                    Closure *f, CallInfo *ci) {\n  int status = 1;\n  if (f == NULL) {\n    info_tailcall(ar);\n    return status;\n  }\n  for (; *what; what++) {\n    switch (*what) {\n      case 'S': {\n        funcinfo(ar, f);\n        break;\n      }\n      case 'l': {\n        ar->currentline = (ci) ? currentline(L, ci) : -1;\n        break;\n      }\n      case 'u': {\n        ar->nups = f->c.nupvalues;\n        break;\n      }\n      case 'n': {\n        ar->namewhat = (ci) ? getfuncname(L, ci, &ar->name) : NULL;\n        if (ar->namewhat == NULL) {\n          ar->namewhat = \"\";  /* not found */\n          ar->name = NULL;\n        }\n        break;\n      }\n      case 'L':\n      case 'f':  /* handled by lua_getinfo */\n        break;\n      default: status = 0;  /* invalid option */\n    }\n  }\n  return status;\n}\n\n\nLUA_API int lua_getinfo (lua_State *L, const char *what, lua_Debug *ar) {\n  int status;\n  Closure *f = NULL;\n  CallInfo *ci = NULL;\n  lua_lock(L);\n  if (*what == '>') {\n    StkId func = L->top - 1;\n    luai_apicheck(L, ttisfunction(func));\n    what++;  /* skip the '>' */\n    f = clvalue(func);\n    L->top--;  /* pop function */\n  }\n  else if (ar->i_ci != 0) {  /* no tail call? */\n    ci = L->base_ci + ar->i_ci;\n    lua_assert(ttisfunction(ci->func));\n    f = clvalue(ci->func);\n  }\n  status = auxgetinfo(L, what, ar, f, ci);\n  if (strchr(what, 'f')) {\n    if (f == NULL) setnilvalue(L->top);\n    else setclvalue(L, L->top, f);\n    incr_top(L);\n  }\n  if (strchr(what, 'L'))\n    collectvalidlines(L, f);\n  lua_unlock(L);\n  return status;\n}\n\n\n/*\n** {======================================================\n** Symbolic Execution and code checker\n** =======================================================\n*/\n\n#define check(x)\t\tif (!(x)) return 0;\n\n#define checkjump(pt,pc)\tcheck(0 <= pc && pc < pt->sizecode)\n\n#define checkreg(pt,reg)\tcheck((reg) < (pt)->maxstacksize)\n\n\n\nstatic int precheck (const Proto *pt) {\n  check(pt->maxstacksize <= MAXSTACK);\n  check(pt->numparams+(pt->is_vararg & VARARG_HASARG) <= pt->maxstacksize);\n  check(!(pt->is_vararg & VARARG_NEEDSARG) ||\n              (pt->is_vararg & VARARG_HASARG));\n  check(pt->sizeupvalues <= pt->nups);\n  check(pt->sizelineinfo == pt->sizecode || pt->sizelineinfo == 0);\n  check(pt->sizecode > 0 && GET_OPCODE(pt->code[pt->sizecode-1]) == OP_RETURN);\n  return 1;\n}\n\n\n#define checkopenop(pt,pc)\tluaG_checkopenop((pt)->code[(pc)+1])\n\nint luaG_checkopenop (Instruction i) {\n  switch (GET_OPCODE(i)) {\n    case OP_CALL:\n    case OP_TAILCALL:\n    case OP_RETURN:\n    case OP_SETLIST: {\n      check(GETARG_B(i) == 0);\n      return 1;\n    }\n    default: return 0;  /* invalid instruction after an open call */\n  }\n}\n\n\nstatic int checkArgMode (const Proto *pt, int r, enum OpArgMask mode) {\n  switch (mode) {\n    case OpArgN: check(r == 0); break;\n    case OpArgU: break;\n    case OpArgR: checkreg(pt, r); break;\n    case OpArgK:\n      check(ISK(r) ? INDEXK(r) < pt->sizek : r < pt->maxstacksize);\n      break;\n  }\n  return 1;\n}\n\n\nInstruction symbexec (const Proto *pt, int lastpc, int reg) {\n  int pc;\n  int last;  /* stores position of last instruction that changed `reg' */\n  last = pt->sizecode-1;  /* points to final return (a `neutral' instruction) */\n  check(precheck(pt));\n  for (pc = 0; pc < lastpc; pc++) {\n    Instruction i = pt->code[pc];\n    OpCode op = GET_OPCODE(i);\n    int a = GETARG_A(i);\n    int b = 0;\n    int c = 0;\n    check(op < NUM_OPCODES);\n    checkreg(pt, a);\n    switch (getOpMode(op)) {\n      case iABC: {\n        b = GETARG_B(i);\n        c = GETARG_C(i);\n        check(checkArgMode(pt, b, getBMode(op)));\n        check(checkArgMode(pt, c, getCMode(op)));\n        break;\n      }\n      case iABx: {\n        b = GETARG_Bx(i);\n        if (getBMode(op) == OpArgK) check(b < pt->sizek);\n        break;\n      }\n      case iAsBx: {\n        b = GETARG_sBx(i);\n        if (getBMode(op) == OpArgR) {\n          int dest = pc+1+b;\n          check(0 <= dest && dest < pt->sizecode);\n          if (dest > 0) {\n            int j;\n            /* check that it does not jump to a setlist count; this\n               is tricky, because the count from a previous setlist may\n               have the same value of an invalid setlist; so, we must\n               go all the way back to the first of them (if any) */\n            for (j = 0; j < dest; j++) {\n              Instruction d = pt->code[dest-1-j];\n              if (!(GET_OPCODE(d) == OP_SETLIST && GETARG_C(d) == 0)) break;\n            }\n            /* if 'j' is even, previous value is not a setlist (even if\n               it looks like one) */\n            check((j&1) == 0);\n          }\n        }\n        break;\n      }\n    }\n    if (testAMode(op)) {\n      if (a == reg) last = pc;  /* change register `a' */\n    }\n    if (testTMode(op)) {\n      check(pc+2 < pt->sizecode);  /* check skip */\n      check(GET_OPCODE(pt->code[pc+1]) == OP_JMP);\n    }\n    switch (op) {\n      case OP_LOADBOOL: {\n        if (c == 1) {  /* does it jump? */\n          check(pc+2 < pt->sizecode);  /* check its jump */\n          check(GET_OPCODE(pt->code[pc+1]) != OP_SETLIST ||\n                GETARG_C(pt->code[pc+1]) != 0);\n        }\n        break;\n      }\n      case OP_LOADNIL: {\n        if (a <= reg && reg <= b)\n          last = pc;  /* set registers from `a' to `b' */\n        break;\n      }\n      case OP_GETUPVAL:\n      case OP_SETUPVAL: {\n        check(b < pt->nups);\n        break;\n      }\n      case OP_GETGLOBAL:\n      case OP_SETGLOBAL: {\n        check(ttisstring(&pt->k[b]));\n        break;\n      }\n      case OP_SELF: {\n        checkreg(pt, a+1);\n        if (reg == a+1) last = pc;\n        break;\n      }\n      case OP_CONCAT: {\n        check(b < c);  /* at least two operands */\n        break;\n      }\n      case OP_TFORLOOP: {\n        check(c >= 1);  /* at least one result (control variable) */\n        checkreg(pt, a+2+c);  /* space for results */\n        if (reg >= a+2) last = pc;  /* affect all regs above its base */\n        break;\n      }\n      case OP_FORLOOP:\n      case OP_FORPREP:\n        checkreg(pt, a+3);\n        /* fall through */\n      case OP_JMP: {\n        int dest = pc+1+b;\n        /* not full check and jump is forward and do not skip `lastpc'? */\n        if (reg != NO_REG && pc < dest && dest <= lastpc)\n          pc += b;  /* do the jump */\n        break;\n      }\n      case OP_CALL:\n      case OP_TAILCALL: {\n        if (b != 0) {\n          checkreg(pt, a+b-1);\n        }\n        c--;  /* c = num. returns */\n        if (c == LUA_MULTRET) {\n          check(checkopenop(pt, pc));\n        }\n        else if (c != 0)\n          checkreg(pt, a+c-1);\n        if (reg >= a) last = pc;  /* affect all registers above base */\n        break;\n      }\n      case OP_RETURN: {\n        b--;  /* b = num. returns */\n        if (b > 0) checkreg(pt, a+b-1);\n        break;\n      }\n      case OP_SETLIST: {\n        if (b > 0) checkreg(pt, a + b);\n        if (c == 0) {\n          pc++;\n          check(pc < pt->sizecode - 1);\n        }\n        break;\n      }\n      case OP_CLOSURE: {\n        int nup, j;\n        check(b < pt->sizep);\n        nup = pt->p[b]->nups;\n        check(pc + nup < pt->sizecode);\n        for (j = 1; j <= nup; j++) {\n          OpCode op1 = GET_OPCODE(pt->code[pc + j]);\n          check(op1 == OP_GETUPVAL || op1 == OP_MOVE);\n        }\n        if (reg != NO_REG)  /* tracing? */\n          pc += nup;  /* do not 'execute' these pseudo-instructions */\n        break;\n      }\n      case OP_VARARG: {\n        check((pt->is_vararg & VARARG_ISVARARG) &&\n             !(pt->is_vararg & VARARG_NEEDSARG));\n        b--;\n        if (b == LUA_MULTRET) check(checkopenop(pt, pc));\n        checkreg(pt, a+b-1);\n        break;\n      }\n      default: break;\n    }\n  }\n  return pt->code[last];\n}\n\n#undef check\n#undef checkjump\n#undef checkreg\n\n/* }====================================================== */\n\n\nint luaG_checkcode (const Proto *pt) {\n  return (symbexec(pt, pt->sizecode, NO_REG) != 0);\n}\n\n\nstatic const char *kname (Proto *p, int c) {\n  if (ISK(c) && ttisstring(&p->k[INDEXK(c)]))\n    return svalue(&p->k[INDEXK(c)]);\n  else\n    return \"?\";\n}\n\n\nstatic const char *getobjname (lua_State *L, CallInfo *ci, int stackpos,\n                               const char **name) {\n  if (isLua(ci)) {  /* a Lua function? */\n    Proto *p = ci_func(ci)->l.p;\n    int pc = currentpc(L, ci);\n    Instruction i;\n    *name = luaF_getlocalname(p, stackpos+1, pc);\n    if (*name)  /* is a local? */\n      return \"local\";\n    i = symbexec(p, pc, stackpos);  /* try symbolic execution */\n    lua_assert(pc != -1);\n    switch (GET_OPCODE(i)) {\n      case OP_GETGLOBAL: {\n        int g = GETARG_Bx(i);  /* global index */\n        lua_assert(ttisstring(&p->k[g]));\n        *name = svalue(&p->k[g]);\n        return \"global\";\n      }\n      case OP_MOVE: {\n        int a = GETARG_A(i);\n        int b = GETARG_B(i);  /* move from `b' to `a' */\n        if (b < a)\n          return getobjname(L, ci, b, name);  /* get name for `b' */\n        break;\n      }\n      case OP_GETTABLE: {\n        int k = GETARG_C(i);  /* key index */\n        *name = kname(p, k);\n        return \"field\";\n      }\n      case OP_GETUPVAL: {\n        int u = GETARG_B(i);  /* upvalue index */\n        *name = p->upvalues ? getstr(p->upvalues[u]) : \"?\";\n        return \"upvalue\";\n      }\n      case OP_SELF: {\n        int k = GETARG_C(i);  /* key index */\n        *name = kname(p, k);\n        return \"method\";\n      }\n      default: break;\n    }\n  }\n  return NULL;  /* no useful name found */\n}\n\n\nstatic const char *getfuncname (lua_State *L, CallInfo *ci, const char **name) {\n  Instruction i;\n  if ((isLua(ci) && ci->tailcalls > 0) || !isLua(ci - 1))\n    return NULL;  /* calling function is not Lua (or is unknown) */\n  ci--;  /* calling function */\n  i = ci_func(ci)->l.p->code[currentpc(L, ci)];\n  if (GET_OPCODE(i) == OP_CALL || GET_OPCODE(i) == OP_TAILCALL ||\n      GET_OPCODE(i) == OP_TFORLOOP)\n    return getobjname(L, ci, GETARG_A(i), name);\n  else\n    return NULL;  /* no useful name can be found */\n}\n\n\n/* only ANSI way to check whether a pointer points to an array */\nstatic int isinstack (CallInfo *ci, const TValue *o) {\n  StkId p;\n  for (p = ci->base; p < ci->top; p++)\n    if (o == p) return 1;\n  return 0;\n}\n\n\nvoid luaG_typeerror (lua_State *L, const TValue *o, const char *op) {\n  const char *name = NULL;\n  const char *t = luaT_typenames[ttype(o)];\n  const char *kind = (isinstack(L->ci, o)) ?\n                         getobjname(L, L->ci, cast_int(o - L->base), &name) :\n                         NULL;\n  if (kind)\n    luaG_runerror(L, \"attempt to %s %s \" LUA_QS \" (a %s value)\",\n                op, kind, name, t);\n  else\n    luaG_runerror(L, \"attempt to %s a %s value\", op, t);\n}\n\n\nvoid luaG_concaterror (lua_State *L, StkId p1, StkId p2) {\n  if (ttisstring(p1) || ttisnumber(p1)) p1 = p2;\n  lua_assert(!ttisstring(p1) && !ttisnumber(p1));\n  luaG_typeerror(L, p1, \"concatenate\");\n}\n\n\nvoid luaG_aritherror (lua_State *L, const TValue *p1, const TValue *p2) {\n  TValue temp;\n  if (luaV_tonumber(p1, &temp) == NULL)\n    p2 = p1;  /* first operand is wrong */\n  luaG_typeerror(L, p2, \"perform arithmetic on\");\n}\n\n\nint luaG_ordererror (lua_State *L, const TValue *p1, const TValue *p2) {\n  const char *t1 = luaT_typenames[ttype(p1)];\n  const char *t2 = luaT_typenames[ttype(p2)];\n  if (t1[2] == t2[2])\n    luaG_runerror(L, \"attempt to compare two %s values\", t1);\n  else\n    luaG_runerror(L, \"attempt to compare %s with %s\", t1, t2);\n  return 0;\n}\n\n\nstatic void addinfo (lua_State *L, const char *msg) {\n  CallInfo *ci = L->ci;\n  if (isLua(ci)) {  /* is Lua code? */\n    char buff[LUA_IDSIZE];  /* add file:line information */\n    int line = currentline(L, ci);\n    luaO_chunkid(buff, getstr(getluaproto(ci)->source), LUA_IDSIZE);\n    luaO_pushfstring(L, \"%s:%d: %s\", buff, line, msg);\n  }\n}\n\n\nvoid luaG_errormsg (lua_State *L) {\n  if (L->errfunc != 0) {  /* is there an error handling function? */\n    StkId errfunc = restorestack(L, L->errfunc);\n    if (!ttisfunction(errfunc)) luaD_throw(L, LUA_ERRERR);\n    setobjs2s(L, L->top, L->top - 1);  /* move argument */\n    setobjs2s(L, L->top - 1, errfunc);  /* push function */\n    incr_top(L);\n    luaD_call(L, L->top - 2, 1);  /* call it */\n  }\n  luaD_throw(L, LUA_ERRRUN);\n}\n\n\nvoid luaG_runerror (lua_State *L, const char *fmt, ...) {\n  va_list argp;\n  va_start(argp, fmt);\n  addinfo(L, luaO_pushvfstring(L, fmt, argp));\n  va_end(argp);\n  luaG_errormsg(L);\n}\n\n"
  },
  {
    "path": "src/ldebug.h",
    "content": "/*\n** $Id: ldebug.h,v 2.3.1.1 2007/12/27 13:02:25 roberto Exp $\n** Auxiliary functions from Debug Interface module\n** See Copyright Notice in lua.h\n*/\n\n#ifndef ldebug_h\n#define ldebug_h\n\n\n#include \"lstate.h\"\n\n\n#define pcRel(pc, p)\t(cast(int, (pc) - (p)->code) - 1)\n\n#define getline(f,pc)\t(((f)->lineinfo) ? (f)->lineinfo[pc] : 0)\n\n#define resethookcount(L)\t(L->hookcount = L->basehookcount)\n\n\nLUAI_FUNC void luaG_typeerror (lua_State *L, const TValue *o,\n                                             const char *opname);\nLUAI_FUNC void luaG_concaterror (lua_State *L, StkId p1, StkId p2);\nLUAI_FUNC void luaG_aritherror (lua_State *L, const TValue *p1,\n                                              const TValue *p2);\nLUAI_FUNC int luaG_ordererror (lua_State *L, const TValue *p1,\n                                             const TValue *p2);\nLUAI_FUNC void luaG_runerror (lua_State *L, const char *fmt, ...);\nLUAI_FUNC void luaG_errormsg (lua_State *L);\nLUAI_FUNC int luaG_checkcode (const Proto *pt);\nLUAI_FUNC int luaG_checkopenop (Instruction i);\n\n#endif\n"
  },
  {
    "path": "src/ldo.c",
    "content": "/*\n** $Id: ldo.c,v 2.38.1.4 2012/01/18 02:27:10 roberto Exp $\n** Stack and Call structure of Lua\n** See Copyright Notice in lua.h\n*/\n\n\n#include <setjmp.h>\n#include <stdlib.h>\n#include <string.h>\n\n#define ldo_c\n#define LUA_CORE\n\n#include \"lua.h\"\n\n#include \"ldebug.h\"\n#include \"ldo.h\"\n#include \"lfunc.h\"\n#include \"lgc.h\"\n#include \"lmem.h\"\n#include \"lobject.h\"\n#include \"lopcodes.h\"\n#include \"lparser.h\"\n#include \"lstate.h\"\n#include \"lstring.h\"\n#include \"ltable.h\"\n#include \"ltm.h\"\n#include \"lundump.h\"\n#include \"lvm.h\"\n#include \"lzio.h\"\n\n\n\n/*\n** {======================================================\n** Error-recovery functions\n** =======================================================\n*/\n\n\n/* chain list of long jump buffers */\nstruct lua_longjmp {\n  struct lua_longjmp *previous;\n  luai_jmpbuf b;\n  volatile int status;  /* error code */\n};\n\n\nvoid luaD_seterrorobj (lua_State *L, int errcode, StkId oldtop) {\n  switch (errcode) {\n    case LUA_ERRMEM: {\n      setsvalue2s(L, oldtop, luaS_newliteral(L, MEMERRMSG));\n      break;\n    }\n    case LUA_ERRERR: {\n      setsvalue2s(L, oldtop, luaS_newliteral(L, \"error in error handling\"));\n      break;\n    }\n    case LUA_ERRSYNTAX:\n    case LUA_ERRRUN: {\n      setobjs2s(L, oldtop, L->top - 1);  /* error message on current top */\n      break;\n    }\n  }\n  L->top = oldtop + 1;\n}\n\n\nstatic void restore_stack_limit (lua_State *L) {\n  lua_assert(L->stack_last - L->stack == L->stacksize - EXTRA_STACK - 1);\n  if (L->size_ci > LUAI_MAXCALLS) {  /* there was an overflow? */\n    int inuse = cast_int(L->ci - L->base_ci);\n    if (inuse + 1 < LUAI_MAXCALLS)  /* can `undo' overflow? */\n      luaD_reallocCI(L, LUAI_MAXCALLS);\n  }\n}\n\n\nstatic void resetstack (lua_State *L, int status) {\n  L->ci = L->base_ci;\n  L->base = L->ci->base;\n  luaF_close(L, L->base);  /* close eventual pending closures */\n  luaD_seterrorobj(L, status, L->base);\n  L->nCcalls = L->baseCcalls;\n  L->allowhook = 1;\n  restore_stack_limit(L);\n  L->errfunc = 0;\n  L->errorJmp = NULL;\n}\n\n\nvoid luaD_throw (lua_State *L, int errcode) {\n  if (L->errorJmp) {\n    L->errorJmp->status = errcode;\n    LUAI_THROW(L, L->errorJmp);\n  }\n  else {\n    L->status = cast_byte(errcode);\n    if (G(L)->panic) {\n      resetstack(L, errcode);\n      lua_unlock(L);\n      G(L)->panic(L);\n    }\n    exit(EXIT_FAILURE);\n  }\n}\n\n\nint luaD_rawrunprotected (lua_State *L, Pfunc f, void *ud) {\n  struct lua_longjmp lj;\n  lj.status = 0;\n  lj.previous = L->errorJmp;  /* chain new error handler */\n  L->errorJmp = &lj;\n  LUAI_TRY(L, &lj,\n    (*f)(L, ud);\n  );\n  L->errorJmp = lj.previous;  /* restore old error handler */\n  return lj.status;\n}\n\n/* }====================================================== */\n\n\nstatic void correctstack (lua_State *L, TValue *oldstack) {\n  CallInfo *ci;\n  GCObject *up;\n  L->top = (L->top - oldstack) + L->stack;\n  for (up = L->openupval; up != NULL; up = up->gch.next)\n    gco2uv(up)->v = (gco2uv(up)->v - oldstack) + L->stack;\n  for (ci = L->base_ci; ci <= L->ci; ci++) {\n    ci->top = (ci->top - oldstack) + L->stack;\n    ci->base = (ci->base - oldstack) + L->stack;\n    ci->func = (ci->func - oldstack) + L->stack;\n  }\n  L->base = (L->base - oldstack) + L->stack;\n}\n\n\nvoid luaD_reallocstack (lua_State *L, int newsize) {\n  TValue *oldstack = L->stack;\n  int realsize = newsize + 1 + EXTRA_STACK;\n  lua_assert(L->stack_last - L->stack == L->stacksize - EXTRA_STACK - 1);\n  luaM_reallocvector(L, L->stack, L->stacksize, realsize, TValue);\n  L->stacksize = realsize;\n  L->stack_last = L->stack+newsize;\n  correctstack(L, oldstack);\n}\n\n\nvoid luaD_reallocCI (lua_State *L, int newsize) {\n  CallInfo *oldci = L->base_ci;\n  luaM_reallocvector(L, L->base_ci, L->size_ci, newsize, CallInfo);\n  L->size_ci = newsize;\n  L->ci = (L->ci - oldci) + L->base_ci;\n  L->end_ci = L->base_ci + L->size_ci - 1;\n}\n\n\nvoid luaD_growstack (lua_State *L, int n) {\n  if (n <= L->stacksize)  /* double size is enough? */\n    luaD_reallocstack(L, 2*L->stacksize);\n  else\n    luaD_reallocstack(L, L->stacksize + n);\n}\n\n\nstatic CallInfo *growCI (lua_State *L) {\n  if (L->size_ci > LUAI_MAXCALLS)  /* overflow while handling overflow? */\n    luaD_throw(L, LUA_ERRERR);\n  else {\n    luaD_reallocCI(L, 2*L->size_ci);\n    if (L->size_ci > LUAI_MAXCALLS)\n      luaG_runerror(L, \"stack overflow\");\n  }\n  return ++L->ci;\n}\n\n\nvoid luaD_callhook (lua_State *L, int event, int line) {\n  lua_Hook hook = L->hook;\n  if (hook && L->allowhook) {\n    ptrdiff_t top = savestack(L, L->top);\n    ptrdiff_t ci_top = savestack(L, L->ci->top);\n    lua_Debug ar;\n    ar.event = event;\n    ar.currentline = line;\n    if (event == LUA_HOOKTAILRET)\n      ar.i_ci = 0;  /* tail call; no debug information about it */\n    else\n      ar.i_ci = cast_int(L->ci - L->base_ci);\n    luaD_checkstack(L, LUA_MINSTACK);  /* ensure minimum stack size */\n    L->ci->top = L->top + LUA_MINSTACK;\n    lua_assert(L->ci->top <= L->stack_last);\n    L->allowhook = 0;  /* cannot call hooks inside a hook */\n    lua_unlock(L);\n    (*hook)(L, &ar);\n    lua_lock(L);\n    lua_assert(!L->allowhook);\n    L->allowhook = 1;\n    L->ci->top = restorestack(L, ci_top);\n    L->top = restorestack(L, top);\n  }\n}\n\n\nstatic StkId adjust_varargs (lua_State *L, Proto *p, int actual) {\n  int i;\n  int nfixargs = p->numparams;\n  Table *htab = NULL;\n  StkId base, fixed;\n  for (; actual < nfixargs; ++actual)\n    setnilvalue(L->top++);\n#if defined(LUA_COMPAT_VARARG)\n  if (p->is_vararg & VARARG_NEEDSARG) { /* compat. with old-style vararg? */\n    int nvar = actual - nfixargs;  /* number of extra arguments */\n    lua_assert(p->is_vararg & VARARG_HASARG);\n    luaC_checkGC(L);\n    luaD_checkstack(L, p->maxstacksize);\n    htab = luaH_new(L, nvar, 1);  /* create `arg' table */\n    for (i=0; i<nvar; i++)  /* put extra arguments into `arg' table */\n      setobj2n(L, luaH_setnum(L, htab, i+1), L->top - nvar + i);\n    /* store counter in field `n' */\n    setnvalue(luaH_setstr(L, htab, luaS_newliteral(L, \"n\")), cast_num(nvar));\n  }\n#endif\n  /* move fixed parameters to final position */\n  fixed = L->top - actual;  /* first fixed argument */\n  base = L->top;  /* final position of first argument */\n  for (i=0; i<nfixargs; i++) {\n    setobjs2s(L, L->top++, fixed+i);\n    setnilvalue(fixed+i);\n  }\n  /* add `arg' parameter */\n  if (htab) {\n    sethvalue(L, L->top++, htab);\n    lua_assert(iswhite(obj2gco(htab)));\n  }\n  return base;\n}\n\n\nstatic StkId tryfuncTM (lua_State *L, StkId func) {\n  const TValue *tm = luaT_gettmbyobj(L, func, TM_CALL);\n  StkId p;\n  ptrdiff_t funcr = savestack(L, func);\n  if (!ttisfunction(tm))\n    luaG_typeerror(L, func, \"call\");\n  /* Open a hole inside the stack at `func' */\n  for (p = L->top; p > func; p--) setobjs2s(L, p, p-1);\n  incr_top(L);\n  func = restorestack(L, funcr);  /* previous call may change stack */\n  setobj2s(L, func, tm);  /* tag method is the new function to be called */\n  return func;\n}\n\n\nextern void record_metadata_about_function_call (lua_State *L, CallInfo *ci);\n\n#define inc_ci(L) \\\n  ((L->ci == L->end_ci) ? growCI(L) : \\\n   (condhardstacktests(luaD_reallocCI(L, L->size_ci)), ++L->ci))\n\n\nint luaD_precall (lua_State *L, StkId func, int nresults) {\n  LClosure *cl;\n  ptrdiff_t funcr;\n  if (!ttisfunction(func)) /* `func' is not a function? */\n    func = tryfuncTM(L, func);  /* check the `function' tag method */\n  funcr = savestack(L, func);\n  cl = &clvalue(func)->l;\n  L->ci->savedpc = L->savedpc;\n  if (!cl->isC) {  /* Lua function? prepare its call */\n    CallInfo *ci;\n    StkId st, base;\n    Proto *p = cl->p;\n    luaD_checkstack(L, p->maxstacksize);\n    func = restorestack(L, funcr);\n    if (!p->is_vararg) {  /* no varargs? */\n      base = func + 1;\n      if (L->top > base + p->numparams)\n        L->top = base + p->numparams;\n    }\n    else {  /* vararg function */\n      int nargs = cast_int(L->top - func) - 1;\n      base = adjust_varargs(L, p, nargs);\n      func = restorestack(L, funcr);  /* previous call may change the stack */\n    }\n    ci = inc_ci(L);  /* now `enter' new function */\n    ci->func = func;\n    L->base = ci->base = base;\n    ci->top = L->base + p->maxstacksize;\n    lua_assert(ci->top <= L->stack_last);\n    L->savedpc = p->code;  /* starting point */\n    ci->tailcalls = 0;\n    ci->nresults = nresults;\n    for (st = L->top; st < ci->top; st++)\n      setnilvalue(st);\n    L->top = ci->top;\n    record_metadata_about_function_call(L, ci);\n    if (L->hookmask & LUA_MASKCALL) {\n      L->savedpc++;  /* hooks assume 'pc' is already incremented */\n      luaD_callhook(L, LUA_HOOKCALL, -1);\n      L->savedpc--;  /* correct 'pc' */\n    }\n    return PCRLUA;\n  }\n  else {  /* if is a C function, call it */\n    CallInfo *ci;\n    int n;\n    luaD_checkstack(L, LUA_MINSTACK);  /* ensure minimum stack size */\n    ci = inc_ci(L);  /* now `enter' new function */\n    ci->func = restorestack(L, funcr);\n    L->base = ci->base = ci->func + 1;\n    ci->top = L->top + LUA_MINSTACK;\n    lua_assert(ci->top <= L->stack_last);\n    ci->nresults = nresults;\n    if (L->hookmask & LUA_MASKCALL)\n      luaD_callhook(L, LUA_HOOKCALL, -1);\n    lua_unlock(L);\n    n = (*curr_func(L)->c.f)(L);  /* do the actual call */\n    lua_lock(L);\n    if (n < 0)  /* yielding? */\n      return PCRYIELD;\n    else {\n      luaD_poscall(L, L->top - n);\n      return PCRC;\n    }\n  }\n}\n\n\nstatic StkId callrethooks (lua_State *L, StkId firstResult) {\n  ptrdiff_t fr = savestack(L, firstResult);  /* next call may change stack */\n  luaD_callhook(L, LUA_HOOKRET, -1);\n  if (f_isLua(L->ci)) {  /* Lua function? */\n    while ((L->hookmask & LUA_MASKRET) && L->ci->tailcalls--) /* tail calls */\n      luaD_callhook(L, LUA_HOOKTAILRET, -1);\n  }\n  return restorestack(L, fr);\n}\n\n\nint luaD_poscall (lua_State *L, StkId firstResult) {\n  StkId res;\n  int wanted, i;\n  CallInfo *ci;\n  if (L->hookmask & LUA_MASKRET)\n    firstResult = callrethooks(L, firstResult);\n  ci = L->ci--;\n  res = ci->func;  /* res == final position of 1st result */\n  wanted = ci->nresults;\n  L->base = (ci - 1)->base;  /* restore base */\n  L->savedpc = (ci - 1)->savedpc;  /* restore savedpc */\n  /* move results to correct place */\n  for (i = wanted; i != 0 && firstResult < L->top; i--)\n    setobjs2s(L, res++, firstResult++);\n  while (i-- > 0)\n    setnilvalue(res++);\n  L->top = res;\n  return (wanted - LUA_MULTRET);  /* 0 iff wanted == LUA_MULTRET */\n}\n\n\n/*\n** Call a function (C or Lua). The function to be called is at *func.\n** The arguments are on the stack, right after the function.\n** When returns, all the results are on the stack, starting at the original\n** function position.\n*/ \nvoid luaD_call (lua_State *L, StkId func, int nResults) {\n  if (++L->nCcalls >= LUAI_MAXCCALLS) {\n    if (L->nCcalls == LUAI_MAXCCALLS)\n      luaG_runerror(L, \"C stack overflow\");\n    else if (L->nCcalls >= (LUAI_MAXCCALLS + (LUAI_MAXCCALLS>>3)))\n      luaD_throw(L, LUA_ERRERR);  /* error while handing stack error */\n  }\n  if (luaD_precall(L, func, nResults) == PCRLUA)  /* is a Lua function? */\n    luaV_execute(L, 1);  /* call it */\n  L->nCcalls--;\n  luaC_checkGC(L);\n}\n\n\nstatic void resume (lua_State *L, void *ud) {\n  StkId firstArg = cast(StkId, ud);\n  CallInfo *ci = L->ci;\n  if (L->status == 0) {  /* start coroutine? */\n    lua_assert(ci == L->base_ci && firstArg > L->base);\n    if (luaD_precall(L, firstArg - 1, LUA_MULTRET) != PCRLUA)\n      return;\n  }\n  else {  /* resuming from previous yield */\n    lua_assert(L->status == LUA_YIELD);\n    L->status = 0;\n    if (!f_isLua(ci)) {  /* `common' yield? */\n      /* finish interrupted execution of `OP_CALL' */\n      lua_assert(GET_OPCODE(*((ci-1)->savedpc - 1)) == OP_CALL ||\n                 GET_OPCODE(*((ci-1)->savedpc - 1)) == OP_TAILCALL);\n      if (luaD_poscall(L, firstArg))  /* complete it... */\n        L->top = L->ci->top;  /* and correct top if not multiple results */\n    }\n    else  /* yielded inside a hook: just continue its execution */\n      L->base = L->ci->base;\n  }\n  luaV_execute(L, cast_int(L->ci - L->base_ci));\n}\n\n\nstatic int resume_error (lua_State *L, const char *msg) {\n  L->top = L->ci->base;\n  setsvalue2s(L, L->top, luaS_new(L, msg));\n  incr_top(L);\n  lua_unlock(L);\n  return LUA_ERRRUN;\n}\n\n\nLUA_API int lua_resume (lua_State *L, int nargs) {\n  int status;\n  lua_lock(L);\n  if (L->status != LUA_YIELD && (L->status != 0 || L->ci != L->base_ci))\n      return resume_error(L, \"cannot resume non-suspended coroutine\");\n  if (L->nCcalls >= LUAI_MAXCCALLS)\n    return resume_error(L, \"C stack overflow\");\n  lua_assert(L->errfunc == 0);\n  L->baseCcalls = ++L->nCcalls;\n  status = luaD_rawrunprotected(L, resume, L->top - nargs);\n  if (status != 0) {  /* error? */\n    L->status = cast_byte(status);  /* mark thread as `dead' */\n    luaD_seterrorobj(L, status, L->top);\n    L->ci->top = L->top;\n  }\n  else {\n    lua_assert(L->nCcalls == L->baseCcalls);\n    status = L->status;\n  }\n  --L->nCcalls;\n  lua_unlock(L);\n  return status;\n}\n\n\nLUA_API int lua_yield (lua_State *L, int nresults) {\n  lua_lock(L);\n  if (L->nCcalls > L->baseCcalls)\n    luaG_runerror(L, \"attempt to yield across metamethod/C-call boundary\");\n  L->base = L->top - nresults;  /* protect stack slots below */\n  L->status = LUA_YIELD;\n  lua_unlock(L);\n  return -1;\n}\n\n\nint luaD_pcall (lua_State *L, Pfunc func, void *u,\n                ptrdiff_t old_top, ptrdiff_t ef) {\n  int status;\n  unsigned short oldnCcalls = L->nCcalls;\n  ptrdiff_t old_ci = saveci(L, L->ci);\n  lu_byte old_allowhooks = L->allowhook;\n  ptrdiff_t old_errfunc = L->errfunc;\n  L->errfunc = ef;\n  status = luaD_rawrunprotected(L, func, u);\n  if (status != 0) {  /* an error occurred? */\n    StkId oldtop = restorestack(L, old_top);\n    luaF_close(L, oldtop);  /* close eventual pending closures */\n    luaD_seterrorobj(L, status, oldtop);\n    L->nCcalls = oldnCcalls;\n    L->ci = restoreci(L, old_ci);\n    L->base = L->ci->base;\n    L->savedpc = L->ci->savedpc;\n    L->allowhook = old_allowhooks;\n    restore_stack_limit(L);\n  }\n  L->errfunc = old_errfunc;\n  return status;\n}\n\n\n\n/*\n** Execute a protected parser.\n*/\nstruct SParser {  /* data to `f_parser' */\n  ZIO *z;\n  Mbuffer buff;  /* buffer to be used by the scanner */\n  const char *name;\n};\n\nstatic void f_parser (lua_State *L, void *ud) {\n  int i;\n  Proto *tf;\n  Closure *cl;\n  struct SParser *p = cast(struct SParser *, ud);\n  int c = luaZ_lookahead(p->z);\n  luaC_checkGC(L);\n  tf = ((c == LUA_SIGNATURE[0]) ? luaU_undump : luaY_parser)(L, p->z,\n                                                             &p->buff, p->name);\n  cl = luaF_newLclosure(L, tf->nups, hvalue(gt(L)));\n  cl->l.p = tf;\n  for (i = 0; i < tf->nups; i++)  /* initialize eventual upvalues */\n    cl->l.upvals[i] = luaF_newupval(L);\n  setclvalue(L, L->top, cl);\n  incr_top(L);\n}\n\n\nint luaD_protectedparser (lua_State *L, ZIO *z, const char *name) {\n  struct SParser p;\n  int status;\n  p.z = z; p.name = name;\n  luaZ_initbuffer(L, &p.buff);\n  status = luaD_pcall(L, f_parser, &p, savestack(L, L->top), L->errfunc);\n  luaZ_freebuffer(L, &p.buff);\n  return status;\n}\n\n\n"
  },
  {
    "path": "src/ldo.h",
    "content": "/*\n** $Id: ldo.h,v 2.7.1.1 2007/12/27 13:02:25 roberto Exp $\n** Stack and Call structure of Lua\n** See Copyright Notice in lua.h\n*/\n\n#ifndef ldo_h\n#define ldo_h\n\n\n#include \"lobject.h\"\n#include \"lstate.h\"\n#include \"lzio.h\"\n\n\n#define luaD_checkstack(L,n)\t\\\n  if ((char *)L->stack_last - (char *)L->top <= (n)*(int)sizeof(TValue)) \\\n    luaD_growstack(L, n); \\\n  else condhardstacktests(luaD_reallocstack(L, L->stacksize - EXTRA_STACK - 1));\n\n\n#define incr_top(L) {luaD_checkstack(L,1); L->top++;}\n\n#define savestack(L,p)\t\t((char *)(p) - (char *)L->stack)\n#define restorestack(L,n)\t((TValue *)((char *)L->stack + (n)))\n\n#define saveci(L,p)\t\t((char *)(p) - (char *)L->base_ci)\n#define restoreci(L,n)\t\t((CallInfo *)((char *)L->base_ci + (n)))\n\n\n/* results from luaD_precall */\n#define PCRLUA\t\t0\t/* initiated a call to a Lua function */\n#define PCRC\t\t1\t/* did a call to a C function */\n#define PCRYIELD\t2\t/* C funtion yielded */\n\n\n/* type of protected functions, to be ran by `runprotected' */\ntypedef void (*Pfunc) (lua_State *L, void *ud);\n\nLUAI_FUNC int luaD_protectedparser (lua_State *L, ZIO *z, const char *name);\nLUAI_FUNC void luaD_callhook (lua_State *L, int event, int line);\nLUAI_FUNC int luaD_precall (lua_State *L, StkId func, int nresults);\nLUAI_FUNC void luaD_call (lua_State *L, StkId func, int nResults);\nLUAI_FUNC int luaD_pcall (lua_State *L, Pfunc func, void *u,\n                                        ptrdiff_t oldtop, ptrdiff_t ef);\nLUAI_FUNC int luaD_poscall (lua_State *L, StkId firstResult);\nLUAI_FUNC void luaD_reallocCI (lua_State *L, int newsize);\nLUAI_FUNC void luaD_reallocstack (lua_State *L, int newsize);\nLUAI_FUNC void luaD_growstack (lua_State *L, int n);\n\nLUAI_FUNC void luaD_throw (lua_State *L, int errcode);\nLUAI_FUNC int luaD_rawrunprotected (lua_State *L, Pfunc f, void *ud);\n\nLUAI_FUNC void luaD_seterrorobj (lua_State *L, int errcode, StkId oldtop);\n\n#endif\n\n"
  },
  {
    "path": "src/ldump.c",
    "content": "/*\n** $Id: ldump.c,v 2.8.1.1 2007/12/27 13:02:25 roberto Exp $\n** save precompiled Lua chunks\n** See Copyright Notice in lua.h\n*/\n\n#include <stddef.h>\n\n#define ldump_c\n#define LUA_CORE\n\n#include \"lua.h\"\n\n#include \"lobject.h\"\n#include \"lstate.h\"\n#include \"lundump.h\"\n\ntypedef struct {\n lua_State* L;\n lua_Writer writer;\n void* data;\n int strip;\n int status;\n} DumpState;\n\n#define DumpMem(b,n,size,D)\tDumpBlock(b,(n)*(size),D)\n#define DumpVar(x,D)\t \tDumpMem(&x,1,sizeof(x),D)\n\nstatic void DumpBlock(const void* b, size_t size, DumpState* D)\n{\n if (D->status==0)\n {\n  lua_unlock(D->L);\n  D->status=(*D->writer)(D->L,b,size,D->data);\n  lua_lock(D->L);\n }\n}\n\nstatic void DumpChar(int y, DumpState* D)\n{\n char x=(char)y;\n DumpVar(x,D);\n}\n\nstatic void DumpInt(int x, DumpState* D)\n{\n DumpVar(x,D);\n}\n\nstatic void DumpNumber(lua_Number x, DumpState* D)\n{\n DumpVar(x,D);\n}\n\nstatic void DumpVector(const void* b, int n, size_t size, DumpState* D)\n{\n DumpInt(n,D);\n DumpMem(b,n,size,D);\n}\n\nstatic void DumpString(const TString* s, DumpState* D)\n{\n if (s==NULL || getstr(s)==NULL)\n {\n  size_t size=0;\n  DumpVar(size,D);\n }\n else\n {\n  size_t size=s->tsv.len+1;\t\t/* include trailing '\\0' */\n  DumpVar(size,D);\n  DumpBlock(getstr(s),size,D);\n }\n}\n\n#define DumpCode(f,D)\t DumpVector(f->code,f->sizecode,sizeof(Instruction),D)\n\nstatic void DumpFunction(const Proto* f, const TString* p, DumpState* D);\n\nstatic void DumpConstants(const Proto* f, DumpState* D)\n{\n int i,n=f->sizek;\n DumpInt(n,D);\n for (i=0; i<n; i++)\n {\n  const TValue* o=&f->k[i];\n  DumpChar(ttype(o),D);\n  switch (ttype(o))\n  {\n   case LUA_TNIL:\n\tbreak;\n   case LUA_TBOOLEAN:\n\tDumpChar(bvalue(o),D);\n\tbreak;\n   case LUA_TNUMBER:\n\tDumpNumber(nvalue(o),D);\n\tbreak;\n   case LUA_TSTRING:\n\tDumpString(rawtsvalue(o),D);\n\tbreak;\n   default:\n\tlua_assert(0);\t\t\t/* cannot happen */\n\tbreak;\n  }\n }\n n=f->sizep;\n DumpInt(n,D);\n for (i=0; i<n; i++) DumpFunction(f->p[i],f->source,D);\n}\n\nstatic void DumpDebug(const Proto* f, DumpState* D)\n{\n int i,n;\n n= (D->strip) ? 0 : f->sizelineinfo;\n DumpVector(f->lineinfo,n,sizeof(int),D);\n n= (D->strip) ? 0 : f->sizelocvars;\n DumpInt(n,D);\n for (i=0; i<n; i++)\n {\n  DumpString(f->locvars[i].varname,D);\n  DumpInt(f->locvars[i].startpc,D);\n  DumpInt(f->locvars[i].endpc,D);\n }\n n= (D->strip) ? 0 : f->sizeupvalues;\n DumpInt(n,D);\n for (i=0; i<n; i++) DumpString(f->upvalues[i],D);\n}\n\nstatic void DumpFunction(const Proto* f, const TString* p, DumpState* D)\n{\n DumpString((f->source==p || D->strip) ? NULL : f->source,D);\n DumpInt(f->linedefined,D);\n DumpInt(f->lastlinedefined,D);\n DumpChar(f->nups,D);\n DumpChar(f->numparams,D);\n DumpChar(f->is_vararg,D);\n DumpChar(f->maxstacksize,D);\n DumpCode(f,D);\n DumpConstants(f,D);\n DumpDebug(f,D);\n}\n\nstatic void DumpHeader(DumpState* D)\n{\n char h[LUAC_HEADERSIZE];\n luaU_header(h);\n DumpBlock(h,LUAC_HEADERSIZE,D);\n}\n\n/*\n** dump Lua function as precompiled chunk\n*/\nint luaU_dump (lua_State* L, const Proto* f, lua_Writer w, void* data, int strip)\n{\n DumpState D;\n D.L=L;\n D.writer=w;\n D.data=data;\n D.strip=strip;\n D.status=0;\n DumpHeader(&D);\n DumpFunction(f,NULL,&D);\n return D.status;\n}\n"
  },
  {
    "path": "src/lfunc.c",
    "content": "/*\n** $Id: lfunc.c,v 2.12.1.2 2007/12/28 14:58:43 roberto Exp $\n** Auxiliary functions to manipulate prototypes and closures\n** See Copyright Notice in lua.h\n*/\n\n\n#include <stddef.h>\n\n#define lfunc_c\n#define LUA_CORE\n\n#include \"lua.h\"\n\n#include \"lfunc.h\"\n#include \"lgc.h\"\n#include \"lmem.h\"\n#include \"lobject.h\"\n#include \"lstate.h\"\n\n\n\nClosure *luaF_newCclosure (lua_State *L, int nelems, Table *e) {\n  Closure *c = cast(Closure *, luaM_malloc(L, sizeCclosure(nelems)));\n  luaC_link(L, obj2gco(c), LUA_TFUNCTION);\n  c->c.isC = 1;\n  c->c.env = e;\n  c->c.nupvalues = cast_byte(nelems);\n  return c;\n}\n\n\nClosure *luaF_newLclosure (lua_State *L, int nelems, Table *e) {\n  Closure *c = cast(Closure *, luaM_malloc(L, sizeLclosure(nelems)));\n  luaC_link(L, obj2gco(c), LUA_TFUNCTION);\n  c->l.isC = 0;\n  c->l.env = e;\n  c->l.nupvalues = cast_byte(nelems);\n  while (nelems--) c->l.upvals[nelems] = NULL;\n  return c;\n}\n\n\nUpVal *luaF_newupval (lua_State *L) {\n  UpVal *uv = luaM_new(L, UpVal);\n  luaC_link(L, obj2gco(uv), LUA_TUPVAL);\n  uv->v = &uv->u.value;\n  setnilvalue(uv->v);\n  return uv;\n}\n\n\nUpVal *luaF_findupval (lua_State *L, StkId level) {\n  global_State *g = G(L);\n  GCObject **pp = &L->openupval;\n  UpVal *p;\n  UpVal *uv;\n  while (*pp != NULL && (p = ngcotouv(*pp))->v >= level) {\n    lua_assert(p->v != &p->u.value);\n    if (p->v == level) {  /* found a corresponding upvalue? */\n      if (isdead(g, obj2gco(p)))  /* is it dead? */\n        changewhite(obj2gco(p));  /* ressurect it */\n      return p;\n    }\n    pp = &p->next;\n  }\n  uv = luaM_new(L, UpVal);  /* not found: create a new one */\n  uv->tt = LUA_TUPVAL;\n  uv->marked = luaC_white(g);\n  uv->v = level;  /* current value lives in the stack */\n  uv->next = *pp;  /* chain it in the proper position */\n  *pp = obj2gco(uv);\n  uv->u.l.prev = &g->uvhead;  /* double link it in `uvhead' list */\n  uv->u.l.next = g->uvhead.u.l.next;\n  uv->u.l.next->u.l.prev = uv;\n  g->uvhead.u.l.next = uv;\n  lua_assert(uv->u.l.next->u.l.prev == uv && uv->u.l.prev->u.l.next == uv);\n  return uv;\n}\n\n\nstatic void unlinkupval (UpVal *uv) {\n  lua_assert(uv->u.l.next->u.l.prev == uv && uv->u.l.prev->u.l.next == uv);\n  uv->u.l.next->u.l.prev = uv->u.l.prev;  /* remove from `uvhead' list */\n  uv->u.l.prev->u.l.next = uv->u.l.next;\n}\n\n\nvoid luaF_freeupval (lua_State *L, UpVal *uv) {\n  if (uv->v != &uv->u.value)  /* is it open? */\n    unlinkupval(uv);  /* remove from open list */\n  luaM_free(L, uv);  /* free upvalue */\n}\n\n\nvoid luaF_close (lua_State *L, StkId level) {\n  UpVal *uv;\n  global_State *g = G(L);\n  while (L->openupval != NULL && (uv = ngcotouv(L->openupval))->v >= level) {\n    GCObject *o = obj2gco(uv);\n    lua_assert(!isblack(o) && uv->v != &uv->u.value);\n    L->openupval = uv->next;  /* remove from `open' list */\n    if (isdead(g, o))\n      luaF_freeupval(L, uv);  /* free upvalue */\n    else {\n      unlinkupval(uv);\n      setobj(L, &uv->u.value, uv->v);\n      uv->v = &uv->u.value;  /* now current value lives here */\n      luaC_linkupval(L, uv);  /* link upvalue into `gcroot' list */\n    }\n  }\n}\n\n\nProto *luaF_newproto (lua_State *L) {\n  Proto *f = luaM_new(L, Proto);\n  luaC_link(L, obj2gco(f), LUA_TPROTO);\n  f->k = NULL;\n  f->sizek = 0;\n  f->p = NULL;\n  f->sizep = 0;\n  f->code = NULL;\n  f->sizecode = 0;\n  f->sizelineinfo = 0;\n  f->sizeupvalues = 0;\n  f->nups = 0;\n  f->upvalues = NULL;\n  f->numparams = 0;\n  f->is_vararg = 0;\n  f->maxstacksize = 0;\n  f->lineinfo = NULL;\n  f->sizelocvars = 0;\n  f->locvars = NULL;\n  f->linedefined = 0;\n  f->lastlinedefined = 0;\n  f->source = NULL;\n  return f;\n}\n\n\nvoid luaF_freeproto (lua_State *L, Proto *f) {\n  luaM_freearray(L, f->code, f->sizecode, Instruction);\n  luaM_freearray(L, f->p, f->sizep, Proto *);\n  luaM_freearray(L, f->k, f->sizek, TValue);\n  luaM_freearray(L, f->lineinfo, f->sizelineinfo, int);\n  luaM_freearray(L, f->locvars, f->sizelocvars, struct LocVar);\n  luaM_freearray(L, f->upvalues, f->sizeupvalues, TString *);\n  luaM_free(L, f);\n}\n\n\nvoid luaF_freeclosure (lua_State *L, Closure *c) {\n  int size = (c->c.isC) ? sizeCclosure(c->c.nupvalues) :\n                          sizeLclosure(c->l.nupvalues);\n  luaM_freemem(L, c, size);\n}\n\n\n/*\n** Look for n-th local variable at line `line' in function `func'.\n** Returns NULL if not found.\n*/\nconst char *luaF_getlocalname (const Proto *f, int local_number, int pc) {\n  int i;\n  for (i = 0; i<f->sizelocvars && f->locvars[i].startpc <= pc; i++) {\n    if (pc < f->locvars[i].endpc) {  /* is variable active? */\n      local_number--;\n      if (local_number == 0)\n        return getstr(f->locvars[i].varname);\n    }\n  }\n  return NULL;  /* not found */\n}\n\n"
  },
  {
    "path": "src/lfunc.h",
    "content": "/*\n** $Id: lfunc.h,v 2.4.1.1 2007/12/27 13:02:25 roberto Exp $\n** Auxiliary functions to manipulate prototypes and closures\n** See Copyright Notice in lua.h\n*/\n\n#ifndef lfunc_h\n#define lfunc_h\n\n\n#include \"lobject.h\"\n\n\n#define sizeCclosure(n)\t(cast(int, sizeof(CClosure)) + \\\n                         cast(int, sizeof(TValue)*((n)-1)))\n\n#define sizeLclosure(n)\t(cast(int, sizeof(LClosure)) + \\\n                         cast(int, sizeof(TValue *)*((n)-1)))\n\n\nLUAI_FUNC Proto *luaF_newproto (lua_State *L);\nLUAI_FUNC Closure *luaF_newCclosure (lua_State *L, int nelems, Table *e);\nLUAI_FUNC Closure *luaF_newLclosure (lua_State *L, int nelems, Table *e);\nLUAI_FUNC UpVal *luaF_newupval (lua_State *L);\nLUAI_FUNC UpVal *luaF_findupval (lua_State *L, StkId level);\nLUAI_FUNC void luaF_close (lua_State *L, StkId level);\nLUAI_FUNC void luaF_freeproto (lua_State *L, Proto *f);\nLUAI_FUNC void luaF_freeclosure (lua_State *L, Closure *c);\nLUAI_FUNC void luaF_freeupval (lua_State *L, UpVal *uv);\nLUAI_FUNC const char *luaF_getlocalname (const Proto *func, int local_number,\n                                         int pc);\n\n\n#endif\n"
  },
  {
    "path": "src/lgc.c",
    "content": "/*\n** $Id: lgc.c,v 2.38.1.2 2011/03/18 18:05:38 roberto Exp $\n** Garbage Collector\n** See Copyright Notice in lua.h\n*/\n\n#include <string.h>\n\n#define lgc_c\n#define LUA_CORE\n\n#include \"lua.h\"\n\n#include \"ldebug.h\"\n#include \"ldo.h\"\n#include \"lfunc.h\"\n#include \"lgc.h\"\n#include \"lmem.h\"\n#include \"lobject.h\"\n#include \"lstate.h\"\n#include \"lstring.h\"\n#include \"ltable.h\"\n#include \"ltm.h\"\n\n\n#define GCSTEPSIZE\t1024u\n#define GCSWEEPMAX\t40\n#define GCSWEEPCOST\t10\n#define GCFINALIZECOST\t100\n\n\n#define maskmarks\tcast_byte(~(bitmask(BLACKBIT)|WHITEBITS))\n\n#define makewhite(g,x)\t\\\n   ((x)->gch.marked = cast_byte(((x)->gch.marked & maskmarks) | luaC_white(g)))\n\n#define white2gray(x)\treset2bits((x)->gch.marked, WHITE0BIT, WHITE1BIT)\n#define black2gray(x)\tresetbit((x)->gch.marked, BLACKBIT)\n\n#define stringmark(s)\treset2bits((s)->tsv.marked, WHITE0BIT, WHITE1BIT)\n\n\n#define isfinalized(u)\t\ttestbit((u)->marked, FINALIZEDBIT)\n#define markfinalized(u)\tl_setbit((u)->marked, FINALIZEDBIT)\n\n\n#define KEYWEAK         bitmask(KEYWEAKBIT)\n#define VALUEWEAK       bitmask(VALUEWEAKBIT)\n\n\n\n#define markvalue(g,o) { checkconsistency(o); \\\n  if (iscollectable(o) && iswhite(gcvalue(o))) reallymarkobject(g,gcvalue(o)); }\n\n#define markobject(g,t) { if (iswhite(obj2gco(t))) \\\n\t\treallymarkobject(g, obj2gco(t)); }\n\n\n#define setthreshold(g)  (g->GCthreshold = (g->estimate/100) * g->gcpause)\n\n\nstatic void removeentry (Node *n) {\n  lua_assert(ttisnil(gval(n)));\n  if (iscollectable(gkey(n)))\n    setttype(gkey(n), LUA_TDEADKEY);  /* dead key; remove it */\n}\n\n\nstatic void reallymarkobject (global_State *g, GCObject *o) {\n  lua_assert(iswhite(o) && !isdead(g, o));\n  white2gray(o);\n  switch (o->gch.tt) {\n    case LUA_TSTRING: {\n      return;\n    }\n    case LUA_TUSERDATA: {\n      Table *mt = gco2u(o)->metatable;\n      gray2black(o);  /* udata are never gray */\n      if (mt) markobject(g, mt);\n      markobject(g, gco2u(o)->env);\n      return;\n    }\n    case LUA_TUPVAL: {\n      UpVal *uv = gco2uv(o);\n      markvalue(g, uv->v);\n      if (uv->v == &uv->u.value)  /* closed? */\n        gray2black(o);  /* open upvalues are never black */\n      return;\n    }\n    case LUA_TFUNCTION: {\n      gco2cl(o)->c.gclist = g->gray;\n      g->gray = o;\n      break;\n    }\n    case LUA_TTABLE: {\n      gco2h(o)->gclist = g->gray;\n      g->gray = o;\n      break;\n    }\n    case LUA_TTHREAD: {\n      gco2th(o)->gclist = g->gray;\n      g->gray = o;\n      break;\n    }\n    case LUA_TPROTO: {\n      gco2p(o)->gclist = g->gray;\n      g->gray = o;\n      break;\n    }\n    default: lua_assert(0);\n  }\n}\n\n\nstatic void marktmu (global_State *g) {\n  GCObject *u = g->tmudata;\n  if (u) {\n    do {\n      u = u->gch.next;\n      makewhite(g, u);  /* may be marked, if left from previous GC */\n      reallymarkobject(g, u);\n    } while (u != g->tmudata);\n  }\n}\n\n\n/* move `dead' udata that need finalization to list `tmudata' */\nsize_t luaC_separateudata (lua_State *L, int all) {\n  global_State *g = G(L);\n  size_t deadmem = 0;\n  GCObject **p = &g->mainthread->next;\n  GCObject *curr;\n  while ((curr = *p) != NULL) {\n    if (!(iswhite(curr) || all) || isfinalized(gco2u(curr)))\n      p = &curr->gch.next;  /* don't bother with them */\n    else if (fasttm(L, gco2u(curr)->metatable, TM_GC) == NULL) {\n      markfinalized(gco2u(curr));  /* don't need finalization */\n      p = &curr->gch.next;\n    }\n    else {  /* must call its gc method */\n      deadmem += sizeudata(gco2u(curr));\n      markfinalized(gco2u(curr));\n      *p = curr->gch.next;\n      /* link `curr' at the end of `tmudata' list */\n      if (g->tmudata == NULL)  /* list is empty? */\n        g->tmudata = curr->gch.next = curr;  /* creates a circular list */\n      else {\n        curr->gch.next = g->tmudata->gch.next;\n        g->tmudata->gch.next = curr;\n        g->tmudata = curr;\n      }\n    }\n  }\n  return deadmem;\n}\n\n\nstatic int traversetable (global_State *g, Table *h) {\n  int i;\n  int weakkey = 0;\n  int weakvalue = 0;\n  const TValue *mode;\n  if (h->metatable)\n    markobject(g, h->metatable);\n  mode = gfasttm(g, h->metatable, TM_MODE);\n  if (mode && ttisstring(mode)) {  /* is there a weak mode? */\n    weakkey = (strchr(svalue(mode), 'k') != NULL);\n    weakvalue = (strchr(svalue(mode), 'v') != NULL);\n    if (weakkey || weakvalue) {  /* is really weak? */\n      h->marked &= ~(KEYWEAK | VALUEWEAK);  /* clear bits */\n      h->marked |= cast_byte((weakkey << KEYWEAKBIT) |\n                             (weakvalue << VALUEWEAKBIT));\n      h->gclist = g->weak;  /* must be cleared after GC, ... */\n      g->weak = obj2gco(h);  /* ... so put in the appropriate list */\n    }\n  }\n  if (weakkey && weakvalue) return 1;\n  if (!weakvalue) {\n    i = h->sizearray;\n    while (i--)\n      markvalue(g, &h->array[i]);\n  }\n  i = sizenode(h);\n  while (i--) {\n    Node *n = gnode(h, i);\n    lua_assert(ttype(gkey(n)) != LUA_TDEADKEY || ttisnil(gval(n)));\n    if (ttisnil(gval(n)))\n      removeentry(n);  /* remove empty entries */\n    else {\n      lua_assert(!ttisnil(gkey(n)));\n      if (!weakkey) markvalue(g, gkey(n));\n      if (!weakvalue) markvalue(g, gval(n));\n    }\n  }\n  return weakkey || weakvalue;\n}\n\n\n/*\n** All marks are conditional because a GC may happen while the\n** prototype is still being created\n*/\nstatic void traverseproto (global_State *g, Proto *f) {\n  int i;\n  if (f->source) stringmark(f->source);\n  for (i=0; i<f->sizek; i++)  /* mark literals */\n    markvalue(g, &f->k[i]);\n  for (i=0; i<f->sizeupvalues; i++) {  /* mark upvalue names */\n    if (f->upvalues[i])\n      stringmark(f->upvalues[i]);\n  }\n  for (i=0; i<f->sizep; i++) {  /* mark nested protos */\n    if (f->p[i])\n      markobject(g, f->p[i]);\n  }\n  for (i=0; i<f->sizelocvars; i++) {  /* mark local-variable names */\n    if (f->locvars[i].varname)\n      stringmark(f->locvars[i].varname);\n  }\n}\n\n\n\nstatic void traverseclosure (global_State *g, Closure *cl) {\n  markobject(g, cl->c.env);\n  if (cl->c.isC) {\n    int i;\n    for (i=0; i<cl->c.nupvalues; i++)  /* mark its upvalues */\n      markvalue(g, &cl->c.upvalue[i]);\n  }\n  else {\n    int i;\n    lua_assert(cl->l.nupvalues == cl->l.p->nups);\n    markobject(g, cl->l.p);\n    for (i=0; i<cl->l.nupvalues; i++)  /* mark its upvalues */\n      markobject(g, cl->l.upvals[i]);\n  }\n}\n\n\nstatic void checkstacksizes (lua_State *L, StkId max) {\n  int ci_used = cast_int(L->ci - L->base_ci);  /* number of `ci' in use */\n  int s_used = cast_int(max - L->stack);  /* part of stack in use */\n  if (L->size_ci > LUAI_MAXCALLS)  /* handling overflow? */\n    return;  /* do not touch the stacks */\n  if (4*ci_used < L->size_ci && 2*BASIC_CI_SIZE < L->size_ci)\n    luaD_reallocCI(L, L->size_ci/2);  /* still big enough... */\n  condhardstacktests(luaD_reallocCI(L, ci_used + 1));\n  if (4*s_used < L->stacksize &&\n      2*(BASIC_STACK_SIZE+EXTRA_STACK) < L->stacksize)\n    luaD_reallocstack(L, L->stacksize/2);  /* still big enough... */\n  condhardstacktests(luaD_reallocstack(L, s_used));\n}\n\n\nstatic void traversestack (global_State *g, lua_State *l) {\n  StkId o, lim;\n  CallInfo *ci;\n  markvalue(g, gt(l));\n  lim = l->top;\n  for (ci = l->base_ci; ci <= l->ci; ci++) {\n    lua_assert(ci->top <= l->stack_last);\n    if (lim < ci->top) lim = ci->top;\n  }\n  for (o = l->stack; o < l->top; o++)\n    markvalue(g, o);\n  for (; o <= lim; o++)\n    setnilvalue(o);\n  checkstacksizes(l, lim);\n}\n\n\n/*\n** traverse one gray object, turning it to black.\n** Returns `quantity' traversed.\n*/\nstatic l_mem propagatemark (global_State *g) {\n  GCObject *o = g->gray;\n  lua_assert(isgray(o));\n  gray2black(o);\n  switch (o->gch.tt) {\n    case LUA_TTABLE: {\n      Table *h = gco2h(o);\n      g->gray = h->gclist;\n      if (traversetable(g, h))  /* table is weak? */\n        black2gray(o);  /* keep it gray */\n      return sizeof(Table) + sizeof(TValue) * h->sizearray +\n                             sizeof(Node) * sizenode(h);\n    }\n    case LUA_TFUNCTION: {\n      Closure *cl = gco2cl(o);\n      g->gray = cl->c.gclist;\n      traverseclosure(g, cl);\n      return (cl->c.isC) ? sizeCclosure(cl->c.nupvalues) :\n                           sizeLclosure(cl->l.nupvalues);\n    }\n    case LUA_TTHREAD: {\n      lua_State *th = gco2th(o);\n      g->gray = th->gclist;\n      th->gclist = g->grayagain;\n      g->grayagain = o;\n      black2gray(o);\n      traversestack(g, th);\n      return sizeof(lua_State) + sizeof(TValue) * th->stacksize +\n                                 sizeof(CallInfo) * th->size_ci;\n    }\n    case LUA_TPROTO: {\n      Proto *p = gco2p(o);\n      g->gray = p->gclist;\n      traverseproto(g, p);\n      return sizeof(Proto) + sizeof(Instruction) * p->sizecode +\n                             sizeof(Proto *) * p->sizep +\n                             sizeof(TValue) * p->sizek + \n                             sizeof(int) * p->sizelineinfo +\n                             sizeof(LocVar) * p->sizelocvars +\n                             sizeof(TString *) * p->sizeupvalues;\n    }\n    default: lua_assert(0); return 0;\n  }\n}\n\n\nstatic size_t propagateall (global_State *g) {\n  size_t m = 0;\n  while (g->gray) m += propagatemark(g);\n  return m;\n}\n\n\n/*\n** The next function tells whether a key or value can be cleared from\n** a weak table. Non-collectable objects are never removed from weak\n** tables. Strings behave as `values', so are never removed too. for\n** other objects: if really collected, cannot keep them; for userdata\n** being finalized, keep them in keys, but not in values\n*/\nstatic int iscleared (const TValue *o, int iskey) {\n  if (!iscollectable(o)) return 0;\n  if (ttisstring(o)) {\n    stringmark(rawtsvalue(o));  /* strings are `values', so are never weak */\n    return 0;\n  }\n  return iswhite(gcvalue(o)) ||\n    (ttisuserdata(o) && (!iskey && isfinalized(uvalue(o))));\n}\n\n\n/*\n** clear collected entries from weaktables\n*/\nstatic void cleartable (GCObject *l) {\n  while (l) {\n    Table *h = gco2h(l);\n    int i = h->sizearray;\n    lua_assert(testbit(h->marked, VALUEWEAKBIT) ||\n               testbit(h->marked, KEYWEAKBIT));\n    if (testbit(h->marked, VALUEWEAKBIT)) {\n      while (i--) {\n        TValue *o = &h->array[i];\n        if (iscleared(o, 0))  /* value was collected? */\n          setnilvalue(o);  /* remove value */\n      }\n    }\n    i = sizenode(h);\n    while (i--) {\n      Node *n = gnode(h, i);\n      if (!ttisnil(gval(n)) &&  /* non-empty entry? */\n          (iscleared(key2tval(n), 1) || iscleared(gval(n), 0))) {\n        setnilvalue(gval(n));  /* remove value ... */\n        removeentry(n);  /* remove entry from table */\n      }\n    }\n    l = h->gclist;\n  }\n}\n\n\nstatic void freeobj (lua_State *L, GCObject *o) {\n  switch (o->gch.tt) {\n    case LUA_TPROTO: luaF_freeproto(L, gco2p(o)); break;\n    case LUA_TFUNCTION: luaF_freeclosure(L, gco2cl(o)); break;\n    case LUA_TUPVAL: luaF_freeupval(L, gco2uv(o)); break;\n    case LUA_TTABLE: luaH_free(L, gco2h(o)); break;\n    case LUA_TTHREAD: {\n      lua_assert(gco2th(o) != L && gco2th(o) != G(L)->mainthread);\n      luaE_freethread(L, gco2th(o));\n      break;\n    }\n    case LUA_TSTRING: {\n      G(L)->strt.nuse--;\n      luaM_freemem(L, o, sizestring(gco2ts(o)));\n      break;\n    }\n    case LUA_TUSERDATA: {\n      luaM_freemem(L, o, sizeudata(gco2u(o)));\n      break;\n    }\n    default: lua_assert(0);\n  }\n}\n\n\n\n#define sweepwholelist(L,p)\tsweeplist(L,p,MAX_LUMEM)\n\n\nstatic GCObject **sweeplist (lua_State *L, GCObject **p, lu_mem count) {\n  GCObject *curr;\n  global_State *g = G(L);\n  int deadmask = otherwhite(g);\n  while ((curr = *p) != NULL && count-- > 0) {\n    if (curr->gch.tt == LUA_TTHREAD)  /* sweep open upvalues of each thread */\n      sweepwholelist(L, &gco2th(curr)->openupval);\n    if ((curr->gch.marked ^ WHITEBITS) & deadmask) {  /* not dead? */\n      lua_assert(!isdead(g, curr) || testbit(curr->gch.marked, FIXEDBIT));\n      makewhite(g, curr);  /* make it white (for next cycle) */\n      p = &curr->gch.next;\n    }\n    else {  /* must erase `curr' */\n      lua_assert(isdead(g, curr) || deadmask == bitmask(SFIXEDBIT));\n      *p = curr->gch.next;\n      if (curr == g->rootgc)  /* is the first element of the list? */\n        g->rootgc = curr->gch.next;  /* adjust first */\n      freeobj(L, curr);\n    }\n  }\n  return p;\n}\n\n\nstatic void checkSizes (lua_State *L) {\n  global_State *g = G(L);\n  /* check size of string hash */\n  if (g->strt.nuse < cast(lu_int32, g->strt.size/4) &&\n      g->strt.size > MINSTRTABSIZE*2)\n    luaS_resize(L, g->strt.size/2);  /* table is too big */\n  /* check size of buffer */\n  if (luaZ_sizebuffer(&g->buff) > LUA_MINBUFFER*2) {  /* buffer too big? */\n    size_t newsize = luaZ_sizebuffer(&g->buff) / 2;\n    luaZ_resizebuffer(L, &g->buff, newsize);\n  }\n}\n\n\nstatic void GCTM (lua_State *L) {\n  global_State *g = G(L);\n  GCObject *o = g->tmudata->gch.next;  /* get first element */\n  Udata *udata = rawgco2u(o);\n  const TValue *tm;\n  /* remove udata from `tmudata' */\n  if (o == g->tmudata)  /* last element? */\n    g->tmudata = NULL;\n  else\n    g->tmudata->gch.next = udata->uv.next;\n  udata->uv.next = g->mainthread->next;  /* return it to `root' list */\n  g->mainthread->next = o;\n  makewhite(g, o);\n  tm = fasttm(L, udata->uv.metatable, TM_GC);\n  if (tm != NULL) {\n    lu_byte oldah = L->allowhook;\n    lu_mem oldt = g->GCthreshold;\n    L->allowhook = 0;  /* stop debug hooks during GC tag method */\n    g->GCthreshold = 2*g->totalbytes;  /* avoid GC steps */\n    setobj2s(L, L->top, tm);\n    setuvalue(L, L->top+1, udata);\n    L->top += 2;\n    luaD_call(L, L->top - 2, 0);\n    L->allowhook = oldah;  /* restore hooks */\n    g->GCthreshold = oldt;  /* restore threshold */\n  }\n}\n\n\n/*\n** Call all GC tag methods\n*/\nvoid luaC_callGCTM (lua_State *L) {\n  while (G(L)->tmudata)\n    GCTM(L);\n}\n\n\nvoid luaC_freeall (lua_State *L) {\n  global_State *g = G(L);\n  int i;\n  g->currentwhite = WHITEBITS | bitmask(SFIXEDBIT);  /* mask to collect all elements */\n  sweepwholelist(L, &g->rootgc);\n  for (i = 0; i < g->strt.size; i++)  /* free all string lists */\n    sweepwholelist(L, &g->strt.hash[i]);\n}\n\n\nstatic void markmt (global_State *g) {\n  int i;\n  for (i=0; i<NUM_TAGS; i++)\n    if (g->mt[i]) markobject(g, g->mt[i]);\n}\n\n\n/* mark root set */\nstatic void markroot (lua_State *L) {\n  global_State *g = G(L);\n  g->gray = NULL;\n  g->grayagain = NULL;\n  g->weak = NULL;\n  markobject(g, g->mainthread);\n  /* make global table be traversed before main stack */\n  markvalue(g, gt(g->mainthread));\n  markvalue(g, registry(L));\n  markmt(g);\n  g->gcstate = GCSpropagate;\n}\n\n\nstatic void remarkupvals (global_State *g) {\n  UpVal *uv;\n  for (uv = g->uvhead.u.l.next; uv != &g->uvhead; uv = uv->u.l.next) {\n    lua_assert(uv->u.l.next->u.l.prev == uv && uv->u.l.prev->u.l.next == uv);\n    if (isgray(obj2gco(uv)))\n      markvalue(g, uv->v);\n  }\n}\n\n\nstatic void atomic (lua_State *L) {\n  global_State *g = G(L);\n  size_t udsize;  /* total size of userdata to be finalized */\n  /* remark occasional upvalues of (maybe) dead threads */\n  remarkupvals(g);\n  /* traverse objects cautch by write barrier and by 'remarkupvals' */\n  propagateall(g);\n  /* remark weak tables */\n  g->gray = g->weak;\n  g->weak = NULL;\n  lua_assert(!iswhite(obj2gco(g->mainthread)));\n  markobject(g, L);  /* mark running thread */\n  markmt(g);  /* mark basic metatables (again) */\n  propagateall(g);\n  /* remark gray again */\n  g->gray = g->grayagain;\n  g->grayagain = NULL;\n  propagateall(g);\n  udsize = luaC_separateudata(L, 0);  /* separate userdata to be finalized */\n  marktmu(g);  /* mark `preserved' userdata */\n  udsize += propagateall(g);  /* remark, to propagate `preserveness' */\n  cleartable(g->weak);  /* remove collected objects from weak tables */\n  /* flip current white */\n  g->currentwhite = cast_byte(otherwhite(g));\n  g->sweepstrgc = 0;\n  g->sweepgc = &g->rootgc;\n  g->gcstate = GCSsweepstring;\n  g->estimate = g->totalbytes - udsize;  /* first estimate */\n}\n\n\nstatic l_mem singlestep (lua_State *L) {\n  global_State *g = G(L);\n  /*lua_checkmemory(L);*/\n  switch (g->gcstate) {\n    case GCSpause: {\n      markroot(L);  /* start a new collection */\n      return 0;\n    }\n    case GCSpropagate: {\n      if (g->gray)\n        return propagatemark(g);\n      else {  /* no more `gray' objects */\n        atomic(L);  /* finish mark phase */\n        return 0;\n      }\n    }\n    case GCSsweepstring: {\n      lu_mem old = g->totalbytes;\n      sweepwholelist(L, &g->strt.hash[g->sweepstrgc++]);\n      if (g->sweepstrgc >= g->strt.size)  /* nothing more to sweep? */\n        g->gcstate = GCSsweep;  /* end sweep-string phase */\n      lua_assert(old >= g->totalbytes);\n      g->estimate -= old - g->totalbytes;\n      return GCSWEEPCOST;\n    }\n    case GCSsweep: {\n      lu_mem old = g->totalbytes;\n      g->sweepgc = sweeplist(L, g->sweepgc, GCSWEEPMAX);\n      if (*g->sweepgc == NULL) {  /* nothing more to sweep? */\n        checkSizes(L);\n        g->gcstate = GCSfinalize;  /* end sweep phase */\n      }\n      lua_assert(old >= g->totalbytes);\n      g->estimate -= old - g->totalbytes;\n      return GCSWEEPMAX*GCSWEEPCOST;\n    }\n    case GCSfinalize: {\n      if (g->tmudata) {\n        GCTM(L);\n        if (g->estimate > GCFINALIZECOST)\n          g->estimate -= GCFINALIZECOST;\n        return GCFINALIZECOST;\n      }\n      else {\n        g->gcstate = GCSpause;  /* end collection */\n        g->gcdept = 0;\n        return 0;\n      }\n    }\n    default: lua_assert(0); return 0;\n  }\n}\n\n\nvoid luaC_step (lua_State *L) {\n  global_State *g = G(L);\n  l_mem lim = (GCSTEPSIZE/100) * g->gcstepmul;\n  if (lim == 0)\n    lim = (MAX_LUMEM-1)/2;  /* no limit */\n  g->gcdept += g->totalbytes - g->GCthreshold;\n  do {\n    lim -= singlestep(L);\n    if (g->gcstate == GCSpause)\n      break;\n  } while (lim > 0);\n  if (g->gcstate != GCSpause) {\n    if (g->gcdept < GCSTEPSIZE)\n      g->GCthreshold = g->totalbytes + GCSTEPSIZE;  /* - lim/g->gcstepmul;*/\n    else {\n      g->gcdept -= GCSTEPSIZE;\n      g->GCthreshold = g->totalbytes;\n    }\n  }\n  else {\n    setthreshold(g);\n  }\n}\n\n\nvoid luaC_fullgc (lua_State *L) {\n  global_State *g = G(L);\n  if (g->gcstate <= GCSpropagate) {\n    /* reset sweep marks to sweep all elements (returning them to white) */\n    g->sweepstrgc = 0;\n    g->sweepgc = &g->rootgc;\n    /* reset other collector lists */\n    g->gray = NULL;\n    g->grayagain = NULL;\n    g->weak = NULL;\n    g->gcstate = GCSsweepstring;\n  }\n  lua_assert(g->gcstate != GCSpause && g->gcstate != GCSpropagate);\n  /* finish any pending sweep phase */\n  while (g->gcstate != GCSfinalize) {\n    lua_assert(g->gcstate == GCSsweepstring || g->gcstate == GCSsweep);\n    singlestep(L);\n  }\n  markroot(L);\n  while (g->gcstate != GCSpause) {\n    singlestep(L);\n  }\n  setthreshold(g);\n}\n\n\nvoid luaC_barrierf (lua_State *L, GCObject *o, GCObject *v) {\n  global_State *g = G(L);\n  lua_assert(isblack(o) && iswhite(v) && !isdead(g, v) && !isdead(g, o));\n  lua_assert(g->gcstate != GCSfinalize && g->gcstate != GCSpause);\n  lua_assert(ttype(&o->gch) != LUA_TTABLE);\n  /* must keep invariant? */\n  if (g->gcstate == GCSpropagate)\n    reallymarkobject(g, v);  /* restore invariant */\n  else  /* don't mind */\n    makewhite(g, o);  /* mark as white just to avoid other barriers */\n}\n\n\nvoid luaC_barrierback (lua_State *L, Table *t) {\n  global_State *g = G(L);\n  GCObject *o = obj2gco(t);\n  lua_assert(isblack(o) && !isdead(g, o));\n  lua_assert(g->gcstate != GCSfinalize && g->gcstate != GCSpause);\n  black2gray(o);  /* make table gray (again) */\n  t->gclist = g->grayagain;\n  g->grayagain = o;\n}\n\n\nvoid luaC_link (lua_State *L, GCObject *o, lu_byte tt) {\n  global_State *g = G(L);\n  o->gch.next = g->rootgc;\n  g->rootgc = o;\n  o->gch.marked = luaC_white(g);\n  o->gch.tt = tt;\n}\n\n\nvoid luaC_linkupval (lua_State *L, UpVal *uv) {\n  global_State *g = G(L);\n  GCObject *o = obj2gco(uv);\n  o->gch.next = g->rootgc;  /* link upvalue into `rootgc' list */\n  g->rootgc = o;\n  if (isgray(o)) { \n    if (g->gcstate == GCSpropagate) {\n      gray2black(o);  /* closed upvalues need barrier */\n      luaC_barrier(L, uv, uv->v);\n    }\n    else {  /* sweep phase: sweep it (turning it into white) */\n      makewhite(g, o);\n      lua_assert(g->gcstate != GCSfinalize && g->gcstate != GCSpause);\n    }\n  }\n}\n\n"
  },
  {
    "path": "src/lgc.h",
    "content": "/*\n** $Id: lgc.h,v 2.15.1.1 2007/12/27 13:02:25 roberto Exp $\n** Garbage Collector\n** See Copyright Notice in lua.h\n*/\n\n#ifndef lgc_h\n#define lgc_h\n\n\n#include \"lobject.h\"\n\n\n/*\n** Possible states of the Garbage Collector\n*/\n#define GCSpause\t0\n#define GCSpropagate\t1\n#define GCSsweepstring\t2\n#define GCSsweep\t3\n#define GCSfinalize\t4\n\n\n/*\n** some userful bit tricks\n*/\n#define resetbits(x,m)\t((x) &= cast(lu_byte, ~(m)))\n#define setbits(x,m)\t((x) |= (m))\n#define testbits(x,m)\t((x) & (m))\n#define bitmask(b)\t(1<<(b))\n#define bit2mask(b1,b2)\t(bitmask(b1) | bitmask(b2))\n#define l_setbit(x,b)\tsetbits(x, bitmask(b))\n#define resetbit(x,b)\tresetbits(x, bitmask(b))\n#define testbit(x,b)\ttestbits(x, bitmask(b))\n#define set2bits(x,b1,b2)\tsetbits(x, (bit2mask(b1, b2)))\n#define reset2bits(x,b1,b2)\tresetbits(x, (bit2mask(b1, b2)))\n#define test2bits(x,b1,b2)\ttestbits(x, (bit2mask(b1, b2)))\n\n\n\n/*\n** Layout for bit use in `marked' field:\n** bit 0 - object is white (type 0)\n** bit 1 - object is white (type 1)\n** bit 2 - object is black\n** bit 3 - for userdata: has been finalized\n** bit 3 - for tables: has weak keys\n** bit 4 - for tables: has weak values\n** bit 5 - object is fixed (should not be collected)\n** bit 6 - object is \"super\" fixed (only the main thread)\n*/\n\n\n#define WHITE0BIT\t0\n#define WHITE1BIT\t1\n#define BLACKBIT\t2\n#define FINALIZEDBIT\t3\n#define KEYWEAKBIT\t3\n#define VALUEWEAKBIT\t4\n#define FIXEDBIT\t5\n#define SFIXEDBIT\t6\n#define WHITEBITS\tbit2mask(WHITE0BIT, WHITE1BIT)\n\n\n#define iswhite(x)      test2bits((x)->gch.marked, WHITE0BIT, WHITE1BIT)\n#define isblack(x)      testbit((x)->gch.marked, BLACKBIT)\n#define isgray(x)\t(!isblack(x) && !iswhite(x))\n\n#define otherwhite(g)\t(g->currentwhite ^ WHITEBITS)\n#define isdead(g,v)\t((v)->gch.marked & otherwhite(g) & WHITEBITS)\n\n#define changewhite(x)\t((x)->gch.marked ^= WHITEBITS)\n#define gray2black(x)\tl_setbit((x)->gch.marked, BLACKBIT)\n\n#define valiswhite(x)\t(iscollectable(x) && iswhite(gcvalue(x)))\n\n#define luaC_white(g)\tcast(lu_byte, (g)->currentwhite & WHITEBITS)\n\n\n#define luaC_checkGC(L) { \\\n  condhardstacktests(luaD_reallocstack(L, L->stacksize - EXTRA_STACK - 1)); \\\n  if (G(L)->totalbytes >= G(L)->GCthreshold) \\\n\tluaC_step(L); }\n\n\n#define luaC_barrier(L,p,v) { if (valiswhite(v) && isblack(obj2gco(p)))  \\\n\tluaC_barrierf(L,obj2gco(p),gcvalue(v)); }\n\n#define luaC_barriert(L,t,v) { if (valiswhite(v) && isblack(obj2gco(t)))  \\\n\tluaC_barrierback(L,t); }\n\n#define luaC_objbarrier(L,p,o)  \\\n\t{ if (iswhite(obj2gco(o)) && isblack(obj2gco(p))) \\\n\t\tluaC_barrierf(L,obj2gco(p),obj2gco(o)); }\n\n#define luaC_objbarriert(L,t,o)  \\\n   { if (iswhite(obj2gco(o)) && isblack(obj2gco(t))) luaC_barrierback(L,t); }\n\nLUAI_FUNC size_t luaC_separateudata (lua_State *L, int all);\nLUAI_FUNC void luaC_callGCTM (lua_State *L);\nLUAI_FUNC void luaC_freeall (lua_State *L);\nLUAI_FUNC void luaC_step (lua_State *L);\nLUAI_FUNC void luaC_fullgc (lua_State *L);\nLUAI_FUNC void luaC_link (lua_State *L, GCObject *o, lu_byte tt);\nLUAI_FUNC void luaC_linkupval (lua_State *L, UpVal *uv);\nLUAI_FUNC void luaC_barrierf (lua_State *L, GCObject *o, GCObject *v);\nLUAI_FUNC void luaC_barrierback (lua_State *L, Table *t);\n\n\n#endif\n"
  },
  {
    "path": "src/linit.c",
    "content": "/*\n** $Id: linit.c,v 1.14.1.1 2007/12/27 13:02:25 roberto Exp $\n** Initialization of libraries for lua.c\n** See Copyright Notice in lua.h\n*/\n\n\n#define linit_c\n#define LUA_LIB\n\n#include \"lua.h\"\n\n#include \"lualib.h\"\n#include \"lauxlib.h\"\n\n\nstatic const luaL_Reg lualibs[] = {\n  {\"\", luaopen_base},\n  {LUA_TABLIBNAME, luaopen_table},\n  {LUA_IOLIBNAME, luaopen_io},\n  {LUA_OSLIBNAME, luaopen_os},\n  {LUA_STRLIBNAME, luaopen_string},\n  {LUA_MATHLIBNAME, luaopen_math},\n  {LUA_CURSESLIBNAME, luaopen_curses},\n  {LUA_SOCKETCORELIBNAME, luaopen_socket_core},\n  {LUA_MIMECORELIBNAME, luaopen_mime_core},\n  {LUA_SSLLIBNAME, luaopen_ssl_core},\n  {LUA_SSLCONTEXTLIBNAME, luaopen_ssl_context},\n  {LUA_SSLX509LIBNAME, luaopen_ssl_x509},\n  {LUA_SSLCONFIGLIBNAME, luaopen_ssl_config},\n  {NULL, NULL}\n};\n\n\nLUALIB_API void luaL_openlibs (lua_State *L) {\n  const luaL_Reg *lib = lualibs;\n  for (; lib->func; lib++) {\n    lua_pushcfunction(L, lib->func);\n    lua_pushstring(L, lib->name);\n    lua_call(L, 1, 0);\n  }\n}\n\n"
  },
  {
    "path": "src/liolib.c",
    "content": "/*\n** $Id: liolib.c,v 2.73.1.4 2010/05/14 15:33:51 roberto Exp $\n** Standard I/O (and system) library\n** See Copyright Notice in lua.h\n*/\n\n\n#include <errno.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <ncurses.h>\n\n#define liolib_c\n#define LUA_LIB\n\n#include \"lua.h\"\n#include \"lauxlib.h\"\n#include \"lualib.h\"\n#include \"teliva.h\"\n\n\n\nstatic int pushresult (lua_State *L, int i, const char *filename) {\n  int en = errno;  /* calls to Lua API may change this value */\n  if (i) {\n    lua_pushboolean(L, 1);\n    return 1;\n  }\n  else {\n    lua_pushnil(L);\n    if (filename)\n      lua_pushfstring(L, \"%s: %s\", filename, strerror(en));\n    else\n      lua_pushfstring(L, \"%s\", strerror(en));\n    lua_pushinteger(L, en);\n    return 3;\n  }\n}\n\n\n#define tofilep(L)\t((FILE **)luaL_checkudata(L, 1, LUA_FILEHANDLE))\n\n\nstatic int io_type (lua_State *L) {\n  void *ud;\n  luaL_checkany(L, 1);\n  ud = lua_touserdata(L, 1);\n  lua_getfield(L, LUA_REGISTRYINDEX, LUA_FILEHANDLE);\n  if (ud == NULL || !lua_getmetatable(L, 1) || !lua_rawequal(L, -2, -1))\n    lua_pushnil(L);  /* not a file */\n  else if (*((FILE **)ud) == NULL)\n    lua_pushliteral(L, \"closed file\");\n  else\n    lua_pushliteral(L, \"file\");\n  return 1;\n}\n\n\nstatic FILE *tofile (lua_State *L) {\n  FILE **f = tofilep(L);\n  if (*f == NULL)\n    luaL_error(L, \"attempt to use a closed file\");\n  return *f;\n}\n\n\n\n/*\n** When creating file handles, always creates a `closed' file handle\n** before opening the actual file; so, if there is a memory error, the\n** file is not left opened.\n*/\nstatic FILE **newfile (lua_State *L) {\n  FILE **pf = (FILE **)lua_newuserdata(L, sizeof(FILE *));\n  *pf = NULL;  /* file handle is currently `closed' */\n  luaL_getmetatable(L, LUA_FILEHANDLE);\n  lua_setmetatable(L, -2);\n  return pf;\n}\n\n\n/*\n** function to close regular files\n*/\nstatic int io_fclose (lua_State *L) {\n  FILE **p = tofilep(L);\n  int ok = (fclose(*p) == 0);\n  *p = NULL;\n  return pushresult(L, ok, NULL);\n}\n\n\nstatic int aux_close (lua_State *L) {\n  lua_getfenv(L, 1);\n  lua_getfield(L, -1, \"__close\");\n  return (lua_tocfunction(L, -1))(L);\n}\n\n\nstatic int io_close (lua_State *L) {\n  tofile(L);  /* make sure argument is a file */\n  return aux_close(L);\n}\n\n\nstatic int io_gc (lua_State *L) {\n  FILE *f = *tofilep(L);\n  /* ignore closed files */\n  if (f != NULL)\n    aux_close(L);\n  return 0;\n}\n\n\nstatic int io_tostring (lua_State *L) {\n  FILE *f = *tofilep(L);\n  if (f == NULL)\n    lua_pushliteral(L, \"file (closed)\");\n  else\n    lua_pushfstring(L, \"file (%p)\", f);\n  return 1;\n}\n\n\nstatic char iolib_errbuf[1024] = {0};\nstatic int io_open (lua_State *L) {\n  const char *filename = luaL_checkstring(L, 1);\n  const char *mode = luaL_optstring(L, 2, \"r\");\n  static char buffer[1024] = {0};\n  memset(buffer, '\\0', 1024);\n  snprintf(buffer, 1020, \"io.open(\\\"%s\\\", \\\"%s\\\")\", filename, mode);\n  append_to_audit_log(L, buffer);\n  FILE **pf = newfile(L);\n  /* filenames starting with teliva_tmp_ are always ok */\n  if (starts_with(filename, \"teliva_tmp_\")) {\n    *pf = fopen(filename, mode);\n  }\n  /* other filenames starting with teliva_ are never ok (reserved for the\n   * framework, should not be accessed by apps directly */\n  else if (starts_with(filename, \"teliva_\")) {\n    snprintf(iolib_errbuf, 1024, \"app tried to open file '%s'; relative paths are never allowed\", filename);\n    Previous_message = iolib_errbuf;\n  }\n  else if (contains(filename, \"./\")) {\n    snprintf(iolib_errbuf, 1024, \"app tried to open file '%s'; relative paths are never allowed\", filename);\n    Previous_message = iolib_errbuf;\n  }\n  else if (file_operation_permitted(filename, mode)) {\n    *pf = fopen(filename, mode);\n  }\n  else {\n    snprintf(iolib_errbuf, 1024, \"app tried to open file '%s'; adjust its permissions (ctrl-p) if that is expected\", filename);\n    Previous_message = iolib_errbuf;\n  }\n  return (*pf == NULL) ? pushresult(L, 0, filename) : 1;\n}\n\n\nstatic int io_tmpfile (lua_State *L) {\n  FILE **pf = newfile(L);\n  *pf = tmpfile();\n  return (*pf == NULL) ? pushresult(L, 0, NULL) : 1;\n}\n\n\nstatic int io_readline (lua_State *L);\n\n\nstatic void aux_lines (lua_State *L, int idx, int toclose) {\n  lua_pushvalue(L, idx);\n  lua_pushboolean(L, toclose);  /* close/not close file when finished */\n  lua_pushcclosure(L, io_readline, 2);\n}\n\n\nstatic int f_lines (lua_State *L) {\n  tofile(L);  /* check that it's a valid file handle */\n  aux_lines(L, 1, 0);\n  return 1;\n}\n\n\n/*\n** {======================================================\n** READ\n** =======================================================\n*/\n\n\nstatic int read_number (lua_State *L, FILE *f) {\n  lua_Number d;\n  if (fscanf(f, LUA_NUMBER_SCAN, &d) == 1) {\n    lua_pushnumber(L, d);\n    return 1;\n  }\n  else {\n    lua_pushnil(L);  /* \"result\" to be removed */\n    return 0;  /* read fails */\n  }\n}\n\n\nstatic int test_eof (lua_State *L, FILE *f) {\n  int c = getc(f);\n  ungetc(c, f);\n  lua_pushlstring(L, NULL, 0);\n  return (c != EOF);\n}\n\n\nstatic int read_line (lua_State *L, FILE *f) {\n  luaL_Buffer b;\n  luaL_buffinit(L, &b);\n  for (;;) {\n    size_t l;\n    char *p = luaL_prepbuffer(&b);\n    if (fgets(p, LUAL_BUFFERSIZE, f) == NULL) {  /* eof? */\n      luaL_pushresult(&b);  /* close buffer */\n      return (lua_objlen(L, -1) > 0);  /* check whether read something */\n    }\n    l = strlen(p);\n    if (l == 0 || p[l-1] != '\\n')\n      luaL_addsize(&b, l);\n    else {\n      luaL_addsize(&b, l - 1);  /* do not include `eol' */\n      luaL_pushresult(&b);  /* close buffer */\n      return 1;  /* read at least an `eol' */\n    }\n  }\n}\n\n\nstatic int read_chars (lua_State *L, FILE *f, size_t n) {\n  size_t rlen;  /* how much to read */\n  size_t nr;  /* number of chars actually read */\n  luaL_Buffer b;\n  luaL_buffinit(L, &b);\n  rlen = LUAL_BUFFERSIZE;  /* try to read that much each time */\n  do {\n    char *p = luaL_prepbuffer(&b);\n    if (rlen > n) rlen = n;  /* cannot read more than asked */\n    nr = fread(p, sizeof(char), rlen, f);\n    luaL_addsize(&b, nr);\n    n -= nr;  /* still have to read `n' chars */\n  } while (n > 0 && nr == rlen);  /* until end of count or eof */\n  luaL_pushresult(&b);  /* close buffer */\n  return (n == 0 || lua_objlen(L, -1) > 0);\n}\n\n\nstatic int g_read (lua_State *L, FILE *f, int first) {\n  int nargs = lua_gettop(L) - 1;\n  int success;\n  int n;\n  clearerr(f);\n  if (nargs == 0) {  /* no arguments? */\n    success = read_line(L, f);\n    n = first+1;  /* to return 1 result */\n  }\n  else {  /* ensure stack space for all results and for auxlib's buffer */\n    luaL_checkstack(L, nargs+LUA_MINSTACK, \"too many arguments\");\n    success = 1;\n    for (n = first; nargs-- && success; n++) {\n      if (lua_type(L, n) == LUA_TNUMBER) {\n        size_t l = (size_t)lua_tointeger(L, n);\n        success = (l == 0) ? test_eof(L, f) : read_chars(L, f, l);\n      }\n      else {\n        const char *p = lua_tostring(L, n);\n        luaL_argcheck(L, p && p[0] == '*', n, \"invalid option\");\n        switch (p[1]) {\n          case 'n':  /* number */\n            success = read_number(L, f);\n            break;\n          case 'l':  /* line */\n            success = read_line(L, f);\n            break;\n          case 'a':  /* file */\n            read_chars(L, f, ~((size_t)0));  /* read MAX_SIZE_T chars */\n            success = 1; /* always success */\n            break;\n          default:\n            return luaL_argerror(L, n, \"invalid format\");\n        }\n      }\n    }\n  }\n  if (ferror(f))\n    return pushresult(L, 0, NULL);\n  if (!success) {\n    lua_pop(L, 1);  /* remove last result */\n    lua_pushnil(L);  /* push nil instead */\n  }\n  return n - first;\n}\n\n\nstatic int f_read (lua_State *L) {\n  return g_read(L, tofile(L), 2);\n}\n\n\nstatic int io_readline (lua_State *L) {\n  FILE *f = *(FILE **)lua_touserdata(L, lua_upvalueindex(1));\n  int sucess;\n  if (f == NULL)  /* file is already closed? */\n    luaL_error(L, \"file is already closed\");\n  sucess = read_line(L, f);\n  if (ferror(f))\n    return luaL_error(L, \"%s\", strerror(errno));\n  if (sucess) return 1;\n  else {  /* EOF */\n    if (lua_toboolean(L, lua_upvalueindex(2))) {  /* generator created file? */\n      lua_settop(L, 0);\n      lua_pushvalue(L, lua_upvalueindex(1));\n      aux_close(L);  /* close it */\n    }\n    return 0;\n  }\n}\n\n/* }====================================================== */\n\n\nstatic int g_write (lua_State *L, FILE *f, int arg) {\n  int nargs = lua_gettop(L) - 1;\n  int status = 1;\n  for (; nargs--; arg++) {\n    if (lua_type(L, arg) == LUA_TNUMBER) {\n      /* optimization: could be done exactly as for strings */\n      status = status &&\n          fprintf(f, LUA_NUMBER_FMT, lua_tonumber(L, arg)) > 0;\n    }\n    else {\n      size_t l;\n      const char *s = luaL_checklstring(L, arg, &l);\n      status = status && (fwrite(s, sizeof(char), l, f) == l);\n    }\n  }\n  return pushresult(L, status, NULL);\n}\n\n\nstatic int f_write (lua_State *L) {\n  return g_write(L, tofile(L), 2);\n}\n\n\nstatic int f_seek (lua_State *L) {\n  static const int mode[] = {SEEK_SET, SEEK_CUR, SEEK_END};\n  static const char *const modenames[] = {\"set\", \"cur\", \"end\", NULL};\n  FILE *f = tofile(L);\n  int op = luaL_checkoption(L, 2, \"cur\", modenames);\n  long offset = luaL_optlong(L, 3, 0);\n  op = fseek(f, offset, mode[op]);\n  if (op)\n    return pushresult(L, 0, NULL);  /* error */\n  else {\n    lua_pushinteger(L, ftell(f));\n    return 1;\n  }\n}\n\n\nstatic int f_setvbuf (lua_State *L) {\n  static const int mode[] = {_IONBF, _IOFBF, _IOLBF};\n  static const char *const modenames[] = {\"no\", \"full\", \"line\", NULL};\n  FILE *f = tofile(L);\n  int op = luaL_checkoption(L, 2, NULL, modenames);\n  lua_Integer sz = luaL_optinteger(L, 3, LUAL_BUFFERSIZE);\n  int res = setvbuf(f, NULL, mode[op], sz);\n  return pushresult(L, res == 0, NULL);\n}\n\n\nstatic int f_flush (lua_State *L) {\n  return pushresult(L, fflush(tofile(L)) == 0, NULL);\n}\n\n\nstatic const luaL_Reg iolib[] = {\n  {\"close\", io_close},\n  /* no 'flush' since Teliva is ncurses-based */\n  /* no 'input' since Teliva is ncurses-based */\n  /* no 'io.lines'; it can confusingly fail without showing sandboxing errors */\n  {\"open\", io_open},\n  /* no 'output' since Teliva is ncurses-based */\n  /* no 'popen' without sandboxing it */\n  /* no 'read' since Teliva is ncurses-based */\n  {\"tmpfile\", io_tmpfile},\n  {\"type\", io_type},\n  /* no 'write' since Teliva is ncurses-based */\n  {NULL, NULL}\n};\n\n\nstatic const luaL_Reg flib[] = {\n  {\"close\", io_close},\n  {\"flush\", f_flush},\n  {\"lines\", f_lines},\n  {\"read\", f_read},\n  {\"seek\", f_seek},\n  {\"setvbuf\", f_setvbuf},\n  {\"write\", f_write},\n  {\"__gc\", io_gc},\n  {\"__tostring\", io_tostring},\n  {NULL, NULL}\n};\n\n\nstatic void createmeta (lua_State *L) {\n  luaL_newmetatable(L, LUA_FILEHANDLE);  /* create metatable for file handles */\n  lua_pushvalue(L, -1);  /* push metatable */\n  lua_setfield(L, -2, \"__index\");  /* metatable.__index = metatable */\n  luaL_register(L, NULL, flib);  /* file methods */\n}\n\n\nstatic void newfenv (lua_State *L, lua_CFunction cls) {\n  lua_createtable(L, 0, 1);\n  lua_pushcfunction(L, cls);\n  lua_setfield(L, -2, \"__close\");\n}\n\n\nLUALIB_API int luaopen_io (lua_State *L) {\n  createmeta(L);\n  newfenv(L, io_fclose);\n  lua_replace(L, LUA_ENVIRONINDEX);\n  /* open library */\n  luaL_register(L, LUA_IOLIBNAME, iolib);\n  return 1;\n}\n\n"
  },
  {
    "path": "src/llex.c",
    "content": "/*\n** $Id: llex.c,v 2.20.1.2 2009/11/23 14:58:22 roberto Exp $\n** Lexical Analyzer\n** See Copyright Notice in lua.h\n*/\n\n\n#include <ctype.h>\n#include <locale.h>\n#include <string.h>\n\n#define llex_c\n#define LUA_CORE\n\n#include \"lua.h\"\n\n#include \"ldo.h\"\n#include \"llex.h\"\n#include \"lobject.h\"\n#include \"lparser.h\"\n#include \"lstate.h\"\n#include \"lstring.h\"\n#include \"ltable.h\"\n#include \"lzio.h\"\n\n\n\n#define next(ls) (ls->current = zgetc(ls->z))\n\n\n\n\n#define currIsNewline(ls)\t(ls->current == '\\n' || ls->current == '\\r')\n\n\n/* ORDER RESERVED */\nconst char *const luaX_tokens [] = {\n    \"and\", \"break\", \"do\", \"else\", \"elseif\",\n    \"end\", \"false\", \"for\", \"function\", \"if\",\n    \"in\", \"local\", \"nil\", \"not\", \"or\", \"repeat\",\n    \"return\", \"then\", \"true\", \"until\", \"while\",\n    \"..\", \"...\", \"==\", \">=\", \"<=\", \"~=\",\n    \"<number>\", \"<name>\", \"<string>\", \"<eof>\",\n    NULL\n};\n\n\n#define save_and_next(ls) (save(ls, ls->current), next(ls))\n\n\nstatic void save (LexState *ls, int c) {\n  Mbuffer *b = ls->buff;\n  if (b->n + 1 > b->buffsize) {\n    size_t newsize;\n    if (b->buffsize >= MAX_SIZET/2)\n      luaX_lexerror(ls, \"lexical element too long\", 0);\n    newsize = b->buffsize * 2;\n    luaZ_resizebuffer(ls->L, b, newsize);\n  }\n  b->buffer[b->n++] = cast(char, c);\n}\n\n\nvoid luaX_init (lua_State *L) {\n  int i;\n  for (i=0; i<NUM_RESERVED; i++) {\n    TString *ts = luaS_new(L, luaX_tokens[i]);\n    luaS_fix(ts);  /* reserved words are never collected */\n    lua_assert(strlen(luaX_tokens[i])+1 <= TOKEN_LEN);\n    ts->tsv.reserved = cast_byte(i+1);  /* reserved word */\n  }\n}\n\n\n#define MAXSRC          80\n\n\nconst char *luaX_token2str (LexState *ls, int token) {\n  if (token < FIRST_RESERVED) {\n    lua_assert(token == cast(unsigned char, token));\n    return (iscntrl(token)) ? luaO_pushfstring(ls->L, \"char(%d)\", token) :\n                              luaO_pushfstring(ls->L, \"%c\", token);\n  }\n  else\n    return luaX_tokens[token-FIRST_RESERVED];\n}\n\n\nstatic const char *txtToken (LexState *ls, int token) {\n  switch (token) {\n    case TK_NAME:\n    case TK_STRING:\n    case TK_NUMBER:\n      save(ls, '\\0');\n      return luaZ_buffer(ls->buff);\n    default:\n      return luaX_token2str(ls, token);\n  }\n}\n\n\nvoid luaX_lexerror (LexState *ls, const char *msg, int token) {\n  char buff[MAXSRC];\n  luaO_chunkid(buff, getstr(ls->source), MAXSRC);\n  msg = luaO_pushfstring(ls->L, \"%s:%d: %s\", buff, ls->linenumber, msg);\n  if (token)\n    luaO_pushfstring(ls->L, \"%s near \" LUA_QS, msg, txtToken(ls, token));\n  luaD_throw(ls->L, LUA_ERRSYNTAX);\n}\n\n\nvoid luaX_syntaxerror (LexState *ls, const char *msg) {\n  luaX_lexerror(ls, msg, ls->t.token);\n}\n\n\nTString *luaX_newstring (LexState *ls, const char *str, size_t l) {\n  lua_State *L = ls->L;\n  TString *ts = luaS_newlstr(L, str, l);\n  TValue *o = luaH_setstr(L, ls->fs->h, ts);  /* entry for `str' */\n  if (ttisnil(o)) {\n    setbvalue(o, 1);  /* make sure `str' will not be collected */\n    luaC_checkGC(L);\n  }\n  return ts;\n}\n\n\nstatic void inclinenumber (LexState *ls) {\n  int old = ls->current;\n  lua_assert(currIsNewline(ls));\n  next(ls);  /* skip `\\n' or `\\r' */\n  if (currIsNewline(ls) && ls->current != old)\n    next(ls);  /* skip `\\n\\r' or `\\r\\n' */\n  if (++ls->linenumber >= MAX_INT)\n    luaX_syntaxerror(ls, \"chunk has too many lines\");\n}\n\n\nvoid luaX_setinput (lua_State *L, LexState *ls, ZIO *z, TString *source) {\n  ls->decpoint = '.';\n  ls->L = L;\n  ls->lookahead.token = TK_EOS;  /* no look-ahead token */\n  ls->z = z;\n  ls->fs = NULL;\n  ls->linenumber = 1;\n  ls->lastline = 1;\n  ls->source = source;\n  luaZ_resizebuffer(ls->L, ls->buff, LUA_MINBUFFER);  /* initialize buffer */\n  next(ls);  /* read first char */\n}\n\n\n\n/*\n** =======================================================\n** LEXICAL ANALYZER\n** =======================================================\n*/\n\n\n\nstatic int check_next (LexState *ls, const char *set) {\n  if (!strchr(set, ls->current))\n    return 0;\n  save_and_next(ls);\n  return 1;\n}\n\n\nstatic void buffreplace (LexState *ls, char from, char to) {\n  size_t n = luaZ_bufflen(ls->buff);\n  char *p = luaZ_buffer(ls->buff);\n  while (n--)\n    if (p[n] == from) p[n] = to;\n}\n\n\nstatic void trydecpoint (LexState *ls, SemInfo *seminfo) {\n  /* format error: try to update decimal point separator */\n  struct lconv *cv = localeconv();\n  char old = ls->decpoint;\n  ls->decpoint = (cv ? cv->decimal_point[0] : '.');\n  buffreplace(ls, old, ls->decpoint);  /* try updated decimal separator */\n  if (!luaO_str2d(luaZ_buffer(ls->buff), &seminfo->r)) {\n    /* format error with correct decimal point: no more options */\n    buffreplace(ls, ls->decpoint, '.');  /* undo change (for error message) */\n    luaX_lexerror(ls, \"malformed number\", TK_NUMBER);\n  }\n}\n\n\n/* LUA_NUMBER */\nstatic void read_numeral (LexState *ls, SemInfo *seminfo) {\n  lua_assert(isdigit(ls->current));\n  do {\n    save_and_next(ls);\n  } while (isdigit(ls->current) || ls->current == '.');\n  if (check_next(ls, \"Ee\"))  /* `E'? */\n    check_next(ls, \"+-\");  /* optional exponent sign */\n  while (isalnum(ls->current) || ls->current == '_')\n    save_and_next(ls);\n  save(ls, '\\0');\n  buffreplace(ls, '.', ls->decpoint);  /* follow locale for decimal point */\n  if (!luaO_str2d(luaZ_buffer(ls->buff), &seminfo->r))  /* format error? */\n    trydecpoint(ls, seminfo); /* try to update decimal point separator */\n}\n\n\nstatic int skip_sep (LexState *ls) {\n  int count = 0;\n  int s = ls->current;\n  lua_assert(s == '[' || s == ']');\n  save_and_next(ls);\n  while (ls->current == '=') {\n    save_and_next(ls);\n    count++;\n  }\n  return (ls->current == s) ? count : (-count) - 1;\n}\n\n\nstatic void read_long_string (LexState *ls, SemInfo *seminfo, int sep) {\n  int cont = 0;\n  (void)(cont);  /* avoid warnings when `cont' is not used */\n  save_and_next(ls);  /* skip 2nd `[' */\n  if (currIsNewline(ls))  /* string starts with a newline? */\n    inclinenumber(ls);  /* skip it */\n  for (;;) {\n    switch (ls->current) {\n      case EOZ:\n        luaX_lexerror(ls, (seminfo) ? \"unfinished long string\" :\n                                   \"unfinished long comment\", TK_EOS);\n        break;  /* to avoid warnings */\n#if defined(LUA_COMPAT_LSTR)\n      case '[': {\n        if (skip_sep(ls) == sep) {\n          save_and_next(ls);  /* skip 2nd `[' */\n          cont++;\n#if LUA_COMPAT_LSTR == 1\n          if (sep == 0)\n            luaX_lexerror(ls, \"nesting of [[...]] is deprecated\", '[');\n#endif\n        }\n        break;\n      }\n#endif\n      case ']': {\n        if (skip_sep(ls) == sep) {\n          save_and_next(ls);  /* skip 2nd `]' */\n#if defined(LUA_COMPAT_LSTR) && LUA_COMPAT_LSTR == 2\n          cont--;\n          if (sep == 0 && cont >= 0) break;\n#endif\n          goto endloop;\n        }\n        break;\n      }\n      case '\\n':\n      case '\\r': {\n        save(ls, '\\n');\n        inclinenumber(ls);\n        if (!seminfo) luaZ_resetbuffer(ls->buff);  /* avoid wasting space */\n        break;\n      }\n      default: {\n        if (seminfo) save_and_next(ls);\n        else next(ls);\n      }\n    }\n  } endloop:\n  if (seminfo)\n    seminfo->ts = luaX_newstring(ls, luaZ_buffer(ls->buff) + (2 + sep),\n                                     luaZ_bufflen(ls->buff) - 2*(2 + sep));\n}\n\n\nstatic void read_string (LexState *ls, int del, SemInfo *seminfo) {\n  save_and_next(ls);\n  while (ls->current != del) {\n    switch (ls->current) {\n      case EOZ:\n        luaX_lexerror(ls, \"unfinished string\", TK_EOS);\n        continue;  /* to avoid warnings */\n      case '\\n':\n      case '\\r':\n        luaX_lexerror(ls, \"unfinished string\", TK_STRING);\n        continue;  /* to avoid warnings */\n      case '\\\\': {\n        int c;\n        next(ls);  /* do not save the `\\' */\n        switch (ls->current) {\n          case 'a': c = '\\a'; break;\n          case 'b': c = '\\b'; break;\n          case 'f': c = '\\f'; break;\n          case 'n': c = '\\n'; break;\n          case 'r': c = '\\r'; break;\n          case 't': c = '\\t'; break;\n          case 'v': c = '\\v'; break;\n          case '\\n':  /* go through */\n          case '\\r': save(ls, '\\n'); inclinenumber(ls); continue;\n          case EOZ: continue;  /* will raise an error next loop */\n          default: {\n            if (!isdigit(ls->current))\n              save_and_next(ls);  /* handles \\\\, \\\", \\', and \\? */\n            else {  /* \\xxx */\n              int i = 0;\n              c = 0;\n              do {\n                c = 10*c + (ls->current-'0');\n                next(ls);\n              } while (++i<3 && isdigit(ls->current));\n              if (c > UCHAR_MAX)\n                luaX_lexerror(ls, \"escape sequence too large\", TK_STRING);\n              save(ls, c);\n            }\n            continue;\n          }\n        }\n        save(ls, c);\n        next(ls);\n        continue;\n      }\n      default:\n        save_and_next(ls);\n    }\n  }\n  save_and_next(ls);  /* skip delimiter */\n  seminfo->ts = luaX_newstring(ls, luaZ_buffer(ls->buff) + 1,\n                                   luaZ_bufflen(ls->buff) - 2);\n}\n\n\nstatic int llex (LexState *ls, SemInfo *seminfo) {\n  luaZ_resetbuffer(ls->buff);\n  for (;;) {\n    switch (ls->current) {\n      case '\\n':\n      case '\\r': {\n        inclinenumber(ls);\n        continue;\n      }\n      case '-': {\n        next(ls);\n        if (ls->current != '-') return '-';\n        /* else is a comment */\n        next(ls);\n        if (ls->current == '[') {\n          int sep = skip_sep(ls);\n          luaZ_resetbuffer(ls->buff);  /* `skip_sep' may dirty the buffer */\n          if (sep >= 0) {\n            read_long_string(ls, NULL, sep);  /* long comment */\n            luaZ_resetbuffer(ls->buff);\n            continue;\n          }\n        }\n        /* else short comment */\n        while (!currIsNewline(ls) && ls->current != EOZ)\n          next(ls);\n        continue;\n      }\n      case '[': {\n        int sep = skip_sep(ls);\n        if (sep >= 0) {\n          read_long_string(ls, seminfo, sep);\n          return TK_STRING;\n        }\n        else if (sep == -1) return '[';\n        else luaX_lexerror(ls, \"invalid long string delimiter\", TK_STRING);\n      }\n      case '=': {\n        next(ls);\n        if (ls->current != '=') return '=';\n        else { next(ls); return TK_EQ; }\n      }\n      case '<': {\n        next(ls);\n        if (ls->current != '=') return '<';\n        else { next(ls); return TK_LE; }\n      }\n      case '>': {\n        next(ls);\n        if (ls->current != '=') return '>';\n        else { next(ls); return TK_GE; }\n      }\n      case '~': {\n        next(ls);\n        if (ls->current != '=') return '~';\n        else { next(ls); return TK_NE; }\n      }\n      case '\"':\n      case '\\'': {\n        read_string(ls, ls->current, seminfo);\n        return TK_STRING;\n      }\n      case '.': {\n        save_and_next(ls);\n        if (check_next(ls, \".\")) {\n          if (check_next(ls, \".\"))\n            return TK_DOTS;   /* ... */\n          else return TK_CONCAT;   /* .. */\n        }\n        else if (!isdigit(ls->current)) return '.';\n        else {\n          read_numeral(ls, seminfo);\n          return TK_NUMBER;\n        }\n      }\n      case EOZ: {\n        return TK_EOS;\n      }\n      default: {\n        if (isspace(ls->current)) {\n          lua_assert(!currIsNewline(ls));\n          next(ls);\n          continue;\n        }\n        else if (isdigit(ls->current)) {\n          read_numeral(ls, seminfo);\n          return TK_NUMBER;\n        }\n        else if (isalpha(ls->current) || ls->current == '_') {\n          /* identifier or reserved word */\n          TString *ts;\n          do {\n            save_and_next(ls);\n          } while (isalnum(ls->current) || ls->current == '_');\n          ts = luaX_newstring(ls, luaZ_buffer(ls->buff),\n                                  luaZ_bufflen(ls->buff));\n          if (ts->tsv.reserved > 0)  /* reserved word? */\n            return ts->tsv.reserved - 1 + FIRST_RESERVED;\n          else {\n            seminfo->ts = ts;\n            return TK_NAME;\n          }\n        }\n        else {\n          int c = ls->current;\n          next(ls);\n          return c;  /* single-char tokens (+ - / ...) */\n        }\n      }\n    }\n  }\n}\n\n\nvoid luaX_next (LexState *ls) {\n  ls->lastline = ls->linenumber;\n  if (ls->lookahead.token != TK_EOS) {  /* is there a look-ahead token? */\n    ls->t = ls->lookahead;  /* use this one */\n    ls->lookahead.token = TK_EOS;  /* and discharge it */\n  }\n  else\n    ls->t.token = llex(ls, &ls->t.seminfo);  /* read next token */\n}\n\n\nvoid luaX_lookahead (LexState *ls) {\n  lua_assert(ls->lookahead.token == TK_EOS);\n  ls->lookahead.token = llex(ls, &ls->lookahead.seminfo);\n}\n\n"
  },
  {
    "path": "src/llex.h",
    "content": "/*\n** $Id: llex.h,v 1.58.1.1 2007/12/27 13:02:25 roberto Exp $\n** Lexical Analyzer\n** See Copyright Notice in lua.h\n*/\n\n#ifndef llex_h\n#define llex_h\n\n#include \"lobject.h\"\n#include \"lzio.h\"\n\n\n#define FIRST_RESERVED\t257\n\n/* maximum length of a reserved word */\n#define TOKEN_LEN\t(sizeof(\"function\")/sizeof(char))\n\n\n/*\n* WARNING: if you change the order of this enumeration,\n* grep \"ORDER RESERVED\"\n*/\nenum RESERVED {\n  /* terminal symbols denoted by reserved words */\n  TK_AND = FIRST_RESERVED, TK_BREAK,\n  TK_DO, TK_ELSE, TK_ELSEIF, TK_END, TK_FALSE, TK_FOR, TK_FUNCTION,\n  TK_IF, TK_IN, TK_LOCAL, TK_NIL, TK_NOT, TK_OR, TK_REPEAT,\n  TK_RETURN, TK_THEN, TK_TRUE, TK_UNTIL, TK_WHILE,\n  /* other terminal symbols */\n  TK_CONCAT, TK_DOTS, TK_EQ, TK_GE, TK_LE, TK_NE, TK_NUMBER,\n  TK_NAME, TK_STRING, TK_EOS\n};\n\n/* number of reserved words */\n#define NUM_RESERVED\t(cast(int, TK_WHILE-FIRST_RESERVED+1))\n\n\n/* array with token `names' */\nLUAI_DATA const char *const luaX_tokens [];\n\n\ntypedef union {\n  lua_Number r;\n  TString *ts;\n} SemInfo;  /* semantics information */\n\n\ntypedef struct Token {\n  int token;\n  SemInfo seminfo;\n} Token;\n\n\ntypedef struct LexState {\n  int current;  /* current character (charint) */\n  int linenumber;  /* input line counter */\n  int lastline;  /* line of last token `consumed' */\n  Token t;  /* current token */\n  Token lookahead;  /* look ahead token */\n  struct FuncState *fs;  /* `FuncState' is private to the parser */\n  struct lua_State *L;\n  ZIO *z;  /* input stream */\n  Mbuffer *buff;  /* buffer for tokens */\n  TString *source;  /* current source name */\n  char decpoint;  /* locale decimal point */\n} LexState;\n\n\nLUAI_FUNC void luaX_init (lua_State *L);\nLUAI_FUNC void luaX_setinput (lua_State *L, LexState *ls, ZIO *z,\n                              TString *source);\nLUAI_FUNC TString *luaX_newstring (LexState *ls, const char *str, size_t l);\nLUAI_FUNC void luaX_next (LexState *ls);\nLUAI_FUNC void luaX_lookahead (LexState *ls);\nLUAI_FUNC void luaX_lexerror (LexState *ls, const char *msg, int token);\nLUAI_FUNC void luaX_syntaxerror (LexState *ls, const char *s);\nLUAI_FUNC const char *luaX_token2str (LexState *ls, int token);\n\n\n#endif\n"
  },
  {
    "path": "src/llimits.h",
    "content": "/*\n** $Id: llimits.h,v 1.69.1.1 2007/12/27 13:02:25 roberto Exp $\n** Limits, basic types, and some other `installation-dependent' definitions\n** See Copyright Notice in lua.h\n*/\n\n#ifndef llimits_h\n#define llimits_h\n\n\n#include <limits.h>\n#include <stddef.h>\n\n\n#include \"lua.h\"\n\n\ntypedef LUAI_UINT32 lu_int32;\n\ntypedef LUAI_UMEM lu_mem;\n\ntypedef LUAI_MEM l_mem;\n\n\n\n/* chars used as small naturals (so that `char' is reserved for characters) */\ntypedef unsigned char lu_byte;\n\n\n#define MAX_SIZET\t((size_t)(~(size_t)0)-2)\n\n#define MAX_LUMEM\t((lu_mem)(~(lu_mem)0)-2)\n\n\n#define MAX_INT (INT_MAX-2)  /* maximum value of an int (-2 for safety) */\n\n/*\n** conversion of pointer to integer\n** this is for hashing only; there is no problem if the integer\n** cannot hold the whole pointer value\n*/\n#define IntPoint(p)  ((unsigned int)(lu_mem)(p))\n\n\n\n/* type to ensure maximum alignment */\ntypedef LUAI_USER_ALIGNMENT_T L_Umaxalign;\n\n\n/* result of a `usual argument conversion' over lua_Number */\ntypedef LUAI_UACNUMBER l_uacNumber;\n\n\n/* internal assertions for in-house debugging */\n#ifdef lua_assert\n\n#define check_exp(c,e)\t\t(lua_assert(c), (e))\n#define api_check(l,e)\t\tlua_assert(e)\n\n#else\n\n#define lua_assert(c)\t\t((void)0)\n#define check_exp(c,e)\t\t(e)\n#define api_check\t\tluai_apicheck\n\n#endif\n\n\n#ifndef UNUSED\n#define UNUSED(x)\t((void)(x))\t/* to avoid warnings */\n#endif\n\n\n#ifndef cast\n#define cast(t, exp)\t((t)(exp))\n#endif\n\n#define cast_byte(i)\tcast(lu_byte, (i))\n#define cast_num(i)\tcast(lua_Number, (i))\n#define cast_int(i)\tcast(int, (i))\n\n\n\n/*\n** type for virtual-machine instructions\n** must be an unsigned with (at least) 4 bytes (see details in lopcodes.h)\n*/\ntypedef lu_int32 Instruction;\n\n\n\n/* maximum stack for a Lua function */\n#define MAXSTACK\t250\n\n\n\n/* minimum size for the string table (must be power of 2) */\n#ifndef MINSTRTABSIZE\n#define MINSTRTABSIZE\t32\n#endif\n\n\n/* minimum size for string buffer */\n#ifndef LUA_MINBUFFER\n#define LUA_MINBUFFER\t32\n#endif\n\n\n#ifndef lua_lock\n#define lua_lock(L)     ((void) 0) \n#define lua_unlock(L)   ((void) 0)\n#endif\n\n#ifndef luai_threadyield\n#define luai_threadyield(L)     {lua_unlock(L); lua_lock(L);}\n#endif\n\n\n/*\n** macro to control inclusion of some hard tests on stack reallocation\n*/ \n#ifndef HARDSTACKTESTS\n#define condhardstacktests(x)\t((void)0)\n#else\n#define condhardstacktests(x)\tx\n#endif\n\n#endif\n"
  },
  {
    "path": "src/lmathlib.c",
    "content": "/*\n** $Id: lmathlib.c,v 1.67.1.1 2007/12/27 13:02:25 roberto Exp $\n** Standard mathematical library\n** See Copyright Notice in lua.h\n*/\n\n\n#include <stdlib.h>\n#include <math.h>\n\n#define lmathlib_c\n#define LUA_LIB\n\n#include \"lua.h\"\n\n#include \"lauxlib.h\"\n#include \"lualib.h\"\n\n\n#undef PI\n#define PI (3.14159265358979323846)\n#define RADIANS_PER_DEGREE (PI/180.0)\n\n\n\nstatic int math_abs (lua_State *L) {\n  lua_pushnumber(L, fabs(luaL_checknumber(L, 1)));\n  return 1;\n}\n\nstatic int math_sin (lua_State *L) {\n  lua_pushnumber(L, sin(luaL_checknumber(L, 1)));\n  return 1;\n}\n\nstatic int math_sinh (lua_State *L) {\n  lua_pushnumber(L, sinh(luaL_checknumber(L, 1)));\n  return 1;\n}\n\nstatic int math_cos (lua_State *L) {\n  lua_pushnumber(L, cos(luaL_checknumber(L, 1)));\n  return 1;\n}\n\nstatic int math_cosh (lua_State *L) {\n  lua_pushnumber(L, cosh(luaL_checknumber(L, 1)));\n  return 1;\n}\n\nstatic int math_tan (lua_State *L) {\n  lua_pushnumber(L, tan(luaL_checknumber(L, 1)));\n  return 1;\n}\n\nstatic int math_tanh (lua_State *L) {\n  lua_pushnumber(L, tanh(luaL_checknumber(L, 1)));\n  return 1;\n}\n\nstatic int math_asin (lua_State *L) {\n  lua_pushnumber(L, asin(luaL_checknumber(L, 1)));\n  return 1;\n}\n\nstatic int math_acos (lua_State *L) {\n  lua_pushnumber(L, acos(luaL_checknumber(L, 1)));\n  return 1;\n}\n\nstatic int math_atan (lua_State *L) {\n  lua_pushnumber(L, atan(luaL_checknumber(L, 1)));\n  return 1;\n}\n\nstatic int math_atan2 (lua_State *L) {\n  lua_pushnumber(L, atan2(luaL_checknumber(L, 1), luaL_checknumber(L, 2)));\n  return 1;\n}\n\nstatic int math_ceil (lua_State *L) {\n  lua_pushnumber(L, ceil(luaL_checknumber(L, 1)));\n  return 1;\n}\n\nstatic int math_floor (lua_State *L) {\n  lua_pushnumber(L, floor(luaL_checknumber(L, 1)));\n  return 1;\n}\n\nstatic int math_fmod (lua_State *L) {\n  lua_pushnumber(L, fmod(luaL_checknumber(L, 1), luaL_checknumber(L, 2)));\n  return 1;\n}\n\nstatic int math_modf (lua_State *L) {\n  double ip;\n  double fp = modf(luaL_checknumber(L, 1), &ip);\n  lua_pushnumber(L, ip);\n  lua_pushnumber(L, fp);\n  return 2;\n}\n\nstatic int math_sqrt (lua_State *L) {\n  lua_pushnumber(L, sqrt(luaL_checknumber(L, 1)));\n  return 1;\n}\n\nstatic int math_pow (lua_State *L) {\n  lua_pushnumber(L, pow(luaL_checknumber(L, 1), luaL_checknumber(L, 2)));\n  return 1;\n}\n\nstatic int math_log (lua_State *L) {\n  lua_pushnumber(L, log(luaL_checknumber(L, 1)));\n  return 1;\n}\n\nstatic int math_log10 (lua_State *L) {\n  lua_pushnumber(L, log10(luaL_checknumber(L, 1)));\n  return 1;\n}\n\nstatic int math_exp (lua_State *L) {\n  lua_pushnumber(L, exp(luaL_checknumber(L, 1)));\n  return 1;\n}\n\nstatic int math_deg (lua_State *L) {\n  lua_pushnumber(L, luaL_checknumber(L, 1)/RADIANS_PER_DEGREE);\n  return 1;\n}\n\nstatic int math_rad (lua_State *L) {\n  lua_pushnumber(L, luaL_checknumber(L, 1)*RADIANS_PER_DEGREE);\n  return 1;\n}\n\nstatic int math_frexp (lua_State *L) {\n  int e;\n  lua_pushnumber(L, frexp(luaL_checknumber(L, 1), &e));\n  lua_pushinteger(L, e);\n  return 2;\n}\n\nstatic int math_ldexp (lua_State *L) {\n  lua_pushnumber(L, ldexp(luaL_checknumber(L, 1), luaL_checkint(L, 2)));\n  return 1;\n}\n\n\n\nstatic int math_min (lua_State *L) {\n  int n = lua_gettop(L);  /* number of arguments */\n  lua_Number dmin = luaL_checknumber(L, 1);\n  int i;\n  for (i=2; i<=n; i++) {\n    lua_Number d = luaL_checknumber(L, i);\n    if (d < dmin)\n      dmin = d;\n  }\n  lua_pushnumber(L, dmin);\n  return 1;\n}\n\n\nstatic int math_max (lua_State *L) {\n  int n = lua_gettop(L);  /* number of arguments */\n  lua_Number dmax = luaL_checknumber(L, 1);\n  int i;\n  for (i=2; i<=n; i++) {\n    lua_Number d = luaL_checknumber(L, i);\n    if (d > dmax)\n      dmax = d;\n  }\n  lua_pushnumber(L, dmax);\n  return 1;\n}\n\n\nstatic int math_random (lua_State *L) {\n  /* the `%' avoids the (rare) case of r==1, and is needed also because on\n     some systems (SunOS!) `rand()' may return a value larger than RAND_MAX */\n  lua_Number r = (lua_Number)(rand()%RAND_MAX) / (lua_Number)RAND_MAX;\n  switch (lua_gettop(L)) {  /* check number of arguments */\n    case 0: {  /* no arguments */\n      lua_pushnumber(L, r);  /* Number between 0 and 1 */\n      break;\n    }\n    case 1: {  /* only upper limit */\n      int u = luaL_checkint(L, 1);\n      luaL_argcheck(L, 1<=u, 1, \"interval is empty\");\n      lua_pushnumber(L, floor(r*u)+1);  /* int between 1 and `u' */\n      break;\n    }\n    case 2: {  /* lower and upper limits */\n      int l = luaL_checkint(L, 1);\n      int u = luaL_checkint(L, 2);\n      luaL_argcheck(L, l<=u, 2, \"interval is empty\");\n      lua_pushnumber(L, floor(r*(u-l+1))+l);  /* int between `l' and `u' */\n      break;\n    }\n    default: return luaL_error(L, \"wrong number of arguments\");\n  }\n  return 1;\n}\n\n\nstatic int math_randomseed (lua_State *L) {\n  srand(luaL_checkint(L, 1));\n  return 0;\n}\n\n\nstatic const luaL_Reg mathlib[] = {\n  {\"abs\",   math_abs},\n  {\"acos\",  math_acos},\n  {\"asin\",  math_asin},\n  {\"atan2\", math_atan2},\n  {\"atan\",  math_atan},\n  {\"ceil\",  math_ceil},\n  {\"cosh\",   math_cosh},\n  {\"cos\",   math_cos},\n  {\"deg\",   math_deg},\n  {\"exp\",   math_exp},\n  {\"floor\", math_floor},\n  {\"fmod\",   math_fmod},\n  {\"frexp\", math_frexp},\n  {\"ldexp\", math_ldexp},\n  {\"log10\", math_log10},\n  {\"log\",   math_log},\n  {\"max\",   math_max},\n  {\"min\",   math_min},\n  {\"modf\",   math_modf},\n  {\"pow\",   math_pow},\n  {\"rad\",   math_rad},\n  {\"random\",     math_random},\n  {\"randomseed\", math_randomseed},\n  {\"sinh\",   math_sinh},\n  {\"sin\",   math_sin},\n  {\"sqrt\",  math_sqrt},\n  {\"tanh\",   math_tanh},\n  {\"tan\",   math_tan},\n  {NULL, NULL}\n};\n\n\n/*\n** Open math library\n*/\nLUALIB_API int luaopen_math (lua_State *L) {\n  luaL_register(L, LUA_MATHLIBNAME, mathlib);\n  lua_pushnumber(L, PI);\n  lua_setfield(L, -2, \"pi\");\n  lua_pushnumber(L, HUGE_VAL);\n  lua_setfield(L, -2, \"huge\");\n#if defined(LUA_COMPAT_MOD)\n  lua_getfield(L, -1, \"fmod\");\n  lua_setfield(L, -2, \"mod\");\n#endif\n  return 1;\n}\n\n"
  },
  {
    "path": "src/lmem.c",
    "content": "/*\n** $Id: lmem.c,v 1.70.1.1 2007/12/27 13:02:25 roberto Exp $\n** Interface to Memory Manager\n** See Copyright Notice in lua.h\n*/\n\n\n#include <stddef.h>\n\n#define lmem_c\n#define LUA_CORE\n\n#include \"lua.h\"\n\n#include \"ldebug.h\"\n#include \"ldo.h\"\n#include \"lmem.h\"\n#include \"lobject.h\"\n#include \"lstate.h\"\n\n\n\n/*\n** About the realloc function:\n** void * frealloc (void *ud, void *ptr, size_t osize, size_t nsize);\n** (`osize' is the old size, `nsize' is the new size)\n**\n** Lua ensures that (ptr == NULL) iff (osize == 0).\n**\n** * frealloc(ud, NULL, 0, x) creates a new block of size `x'\n**\n** * frealloc(ud, p, x, 0) frees the block `p'\n** (in this specific case, frealloc must return NULL).\n** particularly, frealloc(ud, NULL, 0, 0) does nothing\n** (which is equivalent to free(NULL) in ANSI C)\n**\n** frealloc returns NULL if it cannot create or reallocate the area\n** (any reallocation to an equal or smaller size cannot fail!)\n*/\n\n\n\n#define MINSIZEARRAY\t4\n\n\nvoid *luaM_growaux_ (lua_State *L, void *block, int *size, size_t size_elems,\n                     int limit, const char *errormsg) {\n  void *newblock;\n  int newsize;\n  if (*size >= limit/2) {  /* cannot double it? */\n    if (*size >= limit)  /* cannot grow even a little? */\n      luaG_runerror(L, errormsg);\n    newsize = limit;  /* still have at least one free place */\n  }\n  else {\n    newsize = (*size)*2;\n    if (newsize < MINSIZEARRAY)\n      newsize = MINSIZEARRAY;  /* minimum size */\n  }\n  newblock = luaM_reallocv(L, block, *size, newsize, size_elems);\n  *size = newsize;  /* update only when everything else is OK */\n  return newblock;\n}\n\n\nvoid *luaM_toobig (lua_State *L) {\n  luaG_runerror(L, \"memory allocation error: block too big\");\n  return NULL;  /* to avoid warnings */\n}\n\n\n\n/*\n** generic allocation routine.\n*/\nvoid *luaM_realloc_ (lua_State *L, void *block, size_t osize, size_t nsize) {\n  global_State *g = G(L);\n  lua_assert((osize == 0) == (block == NULL));\n  block = (*g->frealloc)(g->ud, block, osize, nsize);\n  if (block == NULL && nsize > 0)\n    luaD_throw(L, LUA_ERRMEM);\n  lua_assert((nsize == 0) == (block == NULL));\n  g->totalbytes = (g->totalbytes - osize) + nsize;\n  return block;\n}\n\n"
  },
  {
    "path": "src/lmem.h",
    "content": "/*\n** $Id: lmem.h,v 1.31.1.1 2007/12/27 13:02:25 roberto Exp $\n** Interface to Memory Manager\n** See Copyright Notice in lua.h\n*/\n\n#ifndef lmem_h\n#define lmem_h\n\n\n#include <stddef.h>\n\n#include \"llimits.h\"\n#include \"lua.h\"\n\n#define MEMERRMSG\t\"not enough memory\"\n\n\n#define luaM_reallocv(L,b,on,n,e) \\\n\t((cast(size_t, (n)+1) <= MAX_SIZET/(e)) ?  /* +1 to avoid warnings */ \\\n\t\tluaM_realloc_(L, (b), (on)*(e), (n)*(e)) : \\\n\t\tluaM_toobig(L))\n\n#define luaM_freemem(L, b, s)\tluaM_realloc_(L, (b), (s), 0)\n#define luaM_free(L, b)\t\tluaM_realloc_(L, (b), sizeof(*(b)), 0)\n#define luaM_freearray(L, b, n, t)   luaM_reallocv(L, (b), n, 0, sizeof(t))\n\n#define luaM_malloc(L,t)\tluaM_realloc_(L, NULL, 0, (t))\n#define luaM_new(L,t)\t\tcast(t *, luaM_malloc(L, sizeof(t)))\n#define luaM_newvector(L,n,t) \\\n\t\tcast(t *, luaM_reallocv(L, NULL, 0, n, sizeof(t)))\n\n#define luaM_growvector(L,v,nelems,size,t,limit,e) \\\n          if ((nelems)+1 > (size)) \\\n            ((v)=cast(t *, luaM_growaux_(L,v,&(size),sizeof(t),limit,e)))\n\n#define luaM_reallocvector(L, v,oldn,n,t) \\\n   ((v)=cast(t *, luaM_reallocv(L, v, oldn, n, sizeof(t))))\n\n\nLUAI_FUNC void *luaM_realloc_ (lua_State *L, void *block, size_t oldsize,\n                                                          size_t size);\nLUAI_FUNC void *luaM_toobig (lua_State *L);\nLUAI_FUNC void *luaM_growaux_ (lua_State *L, void *block, int *size,\n                               size_t size_elem, int limit,\n                               const char *errormsg);\n\n#endif\n\n"
  },
  {
    "path": "src/lobject.c",
    "content": "/*\n** $Id: lobject.c,v 2.22.1.1 2007/12/27 13:02:25 roberto Exp $\n** Some generic functions over Lua objects\n** See Copyright Notice in lua.h\n*/\n\n#include <ctype.h>\n#include <stdarg.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n\n#define lobject_c\n#define LUA_CORE\n\n#include \"lua.h\"\n\n#include \"ldo.h\"\n#include \"lmem.h\"\n#include \"lobject.h\"\n#include \"lstate.h\"\n#include \"lstring.h\"\n#include \"lvm.h\"\n\n\n\nconst TValue luaO_nilobject_ = {{NULL}, LUA_TNIL};\n\n\n/*\n** converts an integer to a \"floating point byte\", represented as\n** (eeeeexxx), where the real value is (1xxx) * 2^(eeeee - 1) if\n** eeeee != 0 and (xxx) otherwise.\n*/\nint luaO_int2fb (unsigned int x) {\n  int e = 0;  /* expoent */\n  while (x >= 16) {\n    x = (x+1) >> 1;\n    e++;\n  }\n  if (x < 8) return x;\n  else return ((e+1) << 3) | (cast_int(x) - 8);\n}\n\n\n/* converts back */\nint luaO_fb2int (int x) {\n  int e = (x >> 3) & 31;\n  if (e == 0) return x;\n  else return ((x & 7)+8) << (e - 1);\n}\n\n\nint luaO_log2 (unsigned int x) {\n  static const lu_byte log_2[256] = {\n    0,1,2,2,3,3,3,3,4,4,4,4,4,4,4,4,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,\n    6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,\n    7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,\n    7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,\n    8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,\n    8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,\n    8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,\n    8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8\n  };\n  int l = -1;\n  while (x >= 256) { l += 8; x >>= 8; }\n  return l + log_2[x];\n\n}\n\n\nint luaO_rawequalObj (const TValue *t1, const TValue *t2) {\n  if (ttype(t1) != ttype(t2)) return 0;\n  else switch (ttype(t1)) {\n    case LUA_TNIL:\n      return 1;\n    case LUA_TNUMBER:\n      return luai_numeq(nvalue(t1), nvalue(t2));\n    case LUA_TBOOLEAN:\n      return bvalue(t1) == bvalue(t2);  /* boolean true must be 1 !! */\n    case LUA_TLIGHTUSERDATA:\n      return pvalue(t1) == pvalue(t2);\n    default:\n      lua_assert(iscollectable(t1));\n      return gcvalue(t1) == gcvalue(t2);\n  }\n}\n\n\nint luaO_str2d (const char *s, lua_Number *result) {\n  char *endptr;\n  *result = lua_str2number(s, &endptr);\n  if (endptr == s) return 0;  /* conversion failed */\n  if (*endptr == 'x' || *endptr == 'X')  /* maybe an hexadecimal constant? */\n    *result = cast_num(strtoul(s, &endptr, 16));\n  if (*endptr == '\\0') return 1;  /* most common case */\n  while (isspace(cast(unsigned char, *endptr))) endptr++;\n  if (*endptr != '\\0') return 0;  /* invalid trailing characters? */\n  return 1;\n}\n\n\n\nstatic void pushstr (lua_State *L, const char *str) {\n  setsvalue2s(L, L->top, luaS_new(L, str));\n  incr_top(L);\n}\n\n\n/* this function handles only `%d', `%c', %f, %p, and `%s' formats */\nconst char *luaO_pushvfstring (lua_State *L, const char *fmt, va_list argp) {\n  int n = 1;\n  pushstr(L, \"\");\n  for (;;) {\n    const char *e = strchr(fmt, '%');\n    if (e == NULL) break;\n    setsvalue2s(L, L->top, luaS_newlstr(L, fmt, e-fmt));\n    incr_top(L);\n    switch (*(e+1)) {\n      case 's': {\n        const char *s = va_arg(argp, char *);\n        if (s == NULL) s = \"(null)\";\n        pushstr(L, s);\n        break;\n      }\n      case 'c': {\n        char buff[2];\n        buff[0] = cast(char, va_arg(argp, int));\n        buff[1] = '\\0';\n        pushstr(L, buff);\n        break;\n      }\n      case 'd': {\n        setnvalue(L->top, cast_num(va_arg(argp, int)));\n        incr_top(L);\n        break;\n      }\n      case 'f': {\n        setnvalue(L->top, cast_num(va_arg(argp, l_uacNumber)));\n        incr_top(L);\n        break;\n      }\n      case 'p': {\n        char buff[4*sizeof(void *) + 8]; /* should be enough space for a `%p' */\n        sprintf(buff, \"%p\", va_arg(argp, void *));\n        pushstr(L, buff);\n        break;\n      }\n      case '%': {\n        pushstr(L, \"%\");\n        break;\n      }\n      default: {\n        char buff[3];\n        buff[0] = '%';\n        buff[1] = *(e+1);\n        buff[2] = '\\0';\n        pushstr(L, buff);\n        break;\n      }\n    }\n    n += 2;\n    fmt = e+2;\n  }\n  pushstr(L, fmt);\n  luaV_concat(L, n+1, cast_int(L->top - L->base) - 1);\n  L->top -= n;\n  return svalue(L->top - 1);\n}\n\n\nconst char *luaO_pushfstring (lua_State *L, const char *fmt, ...) {\n  const char *msg;\n  va_list argp;\n  va_start(argp, fmt);\n  msg = luaO_pushvfstring(L, fmt, argp);\n  va_end(argp);\n  return msg;\n}\n\n\nvoid luaO_chunkid (char *out, const char *source, size_t bufflen) {\n  if (*source == '=') {\n    strncpy(out, source+1, bufflen);  /* remove first char */\n    out[bufflen-1] = '\\0';  /* ensures null termination */\n  }\n  else {  /* out = \"source\", or \"...source\" */\n    if (*source == '@') {\n      size_t l;\n      source++;  /* skip the `@' */\n      bufflen -= sizeof(\" '...' \");\n      l = strlen(source);\n      strcpy(out, \"\");\n      if (l > bufflen) {\n        source += (l-bufflen);  /* get last part of file name */\n        strcat(out, \"...\");\n      }\n      strcat(out, source);\n    }\n    else {  /* out = [string \"string\"] */\n      size_t len = strcspn(source, \"\\n\\r\");  /* stop at first newline */\n      bufflen -= sizeof(\" [string \\\"...\\\"] \");\n      if (len > bufflen) len = bufflen;\n      strcpy(out, \"[string \\\"\");\n      if (source[len] != '\\0') {  /* must truncate? */\n        strncat(out, source, len);\n        strcat(out, \"...\");\n      }\n      else\n        strcat(out, source);\n      strcat(out, \"\\\"]\");\n    }\n  }\n}\n"
  },
  {
    "path": "src/lobject.h",
    "content": "/*\n** $Id: lobject.h,v 2.20.1.2 2008/08/06 13:29:48 roberto Exp $\n** Type definitions for Lua objects\n** See Copyright Notice in lua.h\n*/\n\n\n#ifndef lobject_h\n#define lobject_h\n\n\n#include <stdarg.h>\n\n\n#include \"llimits.h\"\n#include \"lua.h\"\n\n\n/* tags for values visible from Lua */\n#define LAST_TAG\tLUA_TTHREAD\n\n#define NUM_TAGS\t(LAST_TAG+1)\n\n\n/*\n** Extra tags for non-values\n*/\n#define LUA_TPROTO\t(LAST_TAG+1)\n#define LUA_TUPVAL\t(LAST_TAG+2)\n#define LUA_TDEADKEY\t(LAST_TAG+3)\n\n\n/*\n** Union of all collectable objects\n*/\ntypedef union GCObject GCObject;\n\n\n/*\n** Common Header for all collectable objects (in macro form, to be\n** included in other objects)\n*/\n#define CommonHeader\tGCObject *next; lu_byte tt; lu_byte marked\n\n\n/*\n** Common header in struct form\n*/\ntypedef struct GCheader {\n  CommonHeader;\n} GCheader;\n\n\n\n\n/*\n** Union of all Lua values\n*/\ntypedef union {\n  GCObject *gc;\n  void *p;\n  lua_Number n;\n  int b;\n} Value;\n\n\n/*\n** Tagged Values\n*/\n\n#define TValuefields\tValue value; int tt\n\ntypedef struct lua_TValue {\n  TValuefields;\n} TValue;\n\n\n/* Macros to test type */\n#define ttisnil(o)\t(ttype(o) == LUA_TNIL)\n#define ttisnumber(o)\t(ttype(o) == LUA_TNUMBER)\n#define ttisstring(o)\t(ttype(o) == LUA_TSTRING)\n#define ttistable(o)\t(ttype(o) == LUA_TTABLE)\n#define ttisfunction(o)\t(ttype(o) == LUA_TFUNCTION)\n#define ttisboolean(o)\t(ttype(o) == LUA_TBOOLEAN)\n#define ttisuserdata(o)\t(ttype(o) == LUA_TUSERDATA)\n#define ttisthread(o)\t(ttype(o) == LUA_TTHREAD)\n#define ttislightuserdata(o)\t(ttype(o) == LUA_TLIGHTUSERDATA)\n\n/* Macros to access values */\n#define ttype(o)\t((o)->tt)\n#define gcvalue(o)\tcheck_exp(iscollectable(o), (o)->value.gc)\n#define pvalue(o)\tcheck_exp(ttislightuserdata(o), (o)->value.p)\n#define nvalue(o)\tcheck_exp(ttisnumber(o), (o)->value.n)\n#define rawtsvalue(o)\tcheck_exp(ttisstring(o), &(o)->value.gc->ts)\n#define tsvalue(o)\t(&rawtsvalue(o)->tsv)\n#define rawuvalue(o)\tcheck_exp(ttisuserdata(o), &(o)->value.gc->u)\n#define uvalue(o)\t(&rawuvalue(o)->uv)\n#define clvalue(o)\tcheck_exp(ttisfunction(o), &(o)->value.gc->cl)\n#define hvalue(o)\tcheck_exp(ttistable(o), &(o)->value.gc->h)\n#define bvalue(o)\tcheck_exp(ttisboolean(o), (o)->value.b)\n#define thvalue(o)\tcheck_exp(ttisthread(o), &(o)->value.gc->th)\n\n#define l_isfalse(o)\t(ttisnil(o) || (ttisboolean(o) && bvalue(o) == 0))\n\n/*\n** for internal debug only\n*/\n#define checkconsistency(obj) \\\n  lua_assert(!iscollectable(obj) || (ttype(obj) == (obj)->value.gc->gch.tt))\n\n#define checkliveness(g,obj) \\\n  lua_assert(!iscollectable(obj) || \\\n  ((ttype(obj) == (obj)->value.gc->gch.tt) && !isdead(g, (obj)->value.gc)))\n\n\n/* Macros to set values */\n#define setnilvalue(obj) ((obj)->tt=LUA_TNIL)\n\n#define setnvalue(obj,x) \\\n  { TValue *i_o=(obj); i_o->value.n=(x); i_o->tt=LUA_TNUMBER; }\n\n#define setpvalue(obj,x) \\\n  { TValue *i_o=(obj); i_o->value.p=(x); i_o->tt=LUA_TLIGHTUSERDATA; }\n\n#define setbvalue(obj,x) \\\n  { TValue *i_o=(obj); i_o->value.b=(x); i_o->tt=LUA_TBOOLEAN; }\n\n#define setsvalue(L,obj,x) \\\n  { TValue *i_o=(obj); \\\n    i_o->value.gc=cast(GCObject *, (x)); i_o->tt=LUA_TSTRING; \\\n    checkliveness(G(L),i_o); }\n\n#define setuvalue(L,obj,x) \\\n  { TValue *i_o=(obj); \\\n    i_o->value.gc=cast(GCObject *, (x)); i_o->tt=LUA_TUSERDATA; \\\n    checkliveness(G(L),i_o); }\n\n#define setthvalue(L,obj,x) \\\n  { TValue *i_o=(obj); \\\n    i_o->value.gc=cast(GCObject *, (x)); i_o->tt=LUA_TTHREAD; \\\n    checkliveness(G(L),i_o); }\n\n#define setclvalue(L,obj,x) \\\n  { TValue *i_o=(obj); \\\n    i_o->value.gc=cast(GCObject *, (x)); i_o->tt=LUA_TFUNCTION; \\\n    checkliveness(G(L),i_o); }\n\n#define sethvalue(L,obj,x) \\\n  { TValue *i_o=(obj); \\\n    i_o->value.gc=cast(GCObject *, (x)); i_o->tt=LUA_TTABLE; \\\n    checkliveness(G(L),i_o); }\n\n#define setptvalue(L,obj,x) \\\n  { TValue *i_o=(obj); \\\n    i_o->value.gc=cast(GCObject *, (x)); i_o->tt=LUA_TPROTO; \\\n    checkliveness(G(L),i_o); }\n\n\n\n\n#define setobj(L,obj1,obj2) \\\n  { const TValue *o2=(obj2); TValue *o1=(obj1); \\\n    o1->value = o2->value; o1->tt=o2->tt; \\\n    checkliveness(G(L),o1); }\n\n\n/*\n** different types of sets, according to destination\n*/\n\n/* from stack to (same) stack */\n#define setobjs2s\tsetobj\n/* to stack (not from same stack) */\n#define setobj2s\tsetobj\n#define setsvalue2s\tsetsvalue\n#define sethvalue2s\tsethvalue\n#define setptvalue2s\tsetptvalue\n/* from table to same table */\n#define setobjt2t\tsetobj\n/* to table */\n#define setobj2t\tsetobj\n/* to new object */\n#define setobj2n\tsetobj\n#define setsvalue2n\tsetsvalue\n\n#define setttype(obj, tt) (ttype(obj) = (tt))\n\n\n#define iscollectable(o)\t(ttype(o) >= LUA_TSTRING)\n\n\n\ntypedef TValue *StkId;  /* index to stack elements */\n\n\n/*\n** String headers for string table\n*/\ntypedef union TString {\n  L_Umaxalign dummy;  /* ensures maximum alignment for strings */\n  struct {\n    CommonHeader;\n    lu_byte reserved;\n    unsigned int hash;\n    size_t len;\n  } tsv;\n} TString;\n\n\n#define getstr(ts)\tcast(const char *, (ts) + 1)\n#define svalue(o)       getstr(rawtsvalue(o))\n\n\n\ntypedef union Udata {\n  L_Umaxalign dummy;  /* ensures maximum alignment for `local' udata */\n  struct {\n    CommonHeader;\n    struct Table *metatable;\n    struct Table *env;\n    size_t len;\n  } uv;\n} Udata;\n\n\n\n\n/*\n** Function Prototypes\n*/\ntypedef struct Proto {\n  CommonHeader;\n  TValue *k;  /* constants used by the function */\n  Instruction *code;\n  struct Proto **p;  /* functions defined inside the function */\n  int *lineinfo;  /* map from opcodes to source lines */\n  struct LocVar *locvars;  /* information about local variables */\n  TString **upvalues;  /* upvalue names */\n  TString  *source;\n  int sizeupvalues;\n  int sizek;  /* size of `k' */\n  int sizecode;\n  int sizelineinfo;\n  int sizep;  /* size of `p' */\n  int sizelocvars;\n  int linedefined;\n  int lastlinedefined;\n  GCObject *gclist;\n  lu_byte nups;  /* number of upvalues */\n  lu_byte numparams;\n  lu_byte is_vararg;\n  lu_byte maxstacksize;\n} Proto;\n\n\n/* masks for new-style vararg */\n#define VARARG_HASARG\t\t1\n#define VARARG_ISVARARG\t\t2\n#define VARARG_NEEDSARG\t\t4\n\n\ntypedef struct LocVar {\n  TString *varname;\n  int startpc;  /* first point where variable is active */\n  int endpc;    /* first point where variable is dead */\n} LocVar;\n\n\n\n/*\n** Upvalues\n*/\n\ntypedef struct UpVal {\n  CommonHeader;\n  TValue *v;  /* points to stack or to its own value */\n  union {\n    TValue value;  /* the value (when closed) */\n    struct {  /* double linked list (when open) */\n      struct UpVal *prev;\n      struct UpVal *next;\n    } l;\n  } u;\n} UpVal;\n\n\n/*\n** Closures\n*/\n\n#define ClosureHeader \\\n\tCommonHeader; lu_byte isC; lu_byte nupvalues; GCObject *gclist; \\\n\tstruct Table *env\n\ntypedef struct CClosure {\n  ClosureHeader;\n  lua_CFunction f;\n  TValue upvalue[1];\n} CClosure;\n\n\ntypedef struct LClosure {\n  ClosureHeader;\n  struct Proto *p;\n  UpVal *upvals[1];\n} LClosure;\n\n\ntypedef union Closure {\n  CClosure c;\n  LClosure l;\n} Closure;\n\n\n#define iscfunction(o)\t(ttype(o) == LUA_TFUNCTION && clvalue(o)->c.isC)\n#define isLfunction(o)\t(ttype(o) == LUA_TFUNCTION && !clvalue(o)->c.isC)\n\n\n/*\n** Tables\n*/\n\ntypedef union TKey {\n  struct {\n    TValuefields;\n    struct Node *next;  /* for chaining */\n  } nk;\n  TValue tvk;\n} TKey;\n\n\ntypedef struct Node {\n  TValue i_val;\n  TKey i_key;\n} Node;\n\n\ntypedef struct Table {\n  CommonHeader;\n  lu_byte flags;  /* 1<<p means tagmethod(p) is not present */ \n  lu_byte lsizenode;  /* log2 of size of `node' array */\n  struct Table *metatable;\n  TValue *array;  /* array part */\n  Node *node;\n  Node *lastfree;  /* any free position is before this position */\n  GCObject *gclist;\n  int sizearray;  /* size of `array' array */\n} Table;\n\n\n\n/*\n** `module' operation for hashing (size is always a power of 2)\n*/\n#define lmod(s,size) \\\n\t(check_exp((size&(size-1))==0, (cast(int, (s) & ((size)-1)))))\n\n\n#define twoto(x)\t(1<<(x))\n#define sizenode(t)\t(twoto((t)->lsizenode))\n\n\n#define luaO_nilobject\t\t(&luaO_nilobject_)\n\nLUAI_DATA const TValue luaO_nilobject_;\n\n#define ceillog2(x)\t(luaO_log2((x)-1) + 1)\n\nLUAI_FUNC int luaO_log2 (unsigned int x);\nLUAI_FUNC int luaO_int2fb (unsigned int x);\nLUAI_FUNC int luaO_fb2int (int x);\nLUAI_FUNC int luaO_rawequalObj (const TValue *t1, const TValue *t2);\nLUAI_FUNC int luaO_str2d (const char *s, lua_Number *result);\nLUAI_FUNC const char *luaO_pushvfstring (lua_State *L, const char *fmt,\n                                                       va_list argp);\nLUAI_FUNC const char *luaO_pushfstring (lua_State *L, const char *fmt, ...);\nLUAI_FUNC void luaO_chunkid (char *out, const char *source, size_t len);\n\n\n#endif\n\n"
  },
  {
    "path": "src/lopcodes.c",
    "content": "/*\n** $Id: lopcodes.c,v 1.37.1.1 2007/12/27 13:02:25 roberto Exp $\n** See Copyright Notice in lua.h\n*/\n\n\n#define lopcodes_c\n#define LUA_CORE\n\n\n#include \"lopcodes.h\"\n\n\n/* ORDER OP */\n\nconst char *const luaP_opnames[NUM_OPCODES+1] = {\n  \"MOVE\",\n  \"LOADK\",\n  \"LOADBOOL\",\n  \"LOADNIL\",\n  \"GETUPVAL\",\n  \"GETGLOBAL\",\n  \"GETTABLE\",\n  \"SETGLOBAL\",\n  \"SETUPVAL\",\n  \"SETTABLE\",\n  \"NEWTABLE\",\n  \"SELF\",\n  \"ADD\",\n  \"SUB\",\n  \"MUL\",\n  \"DIV\",\n  \"MOD\",\n  \"POW\",\n  \"UNM\",\n  \"NOT\",\n  \"LEN\",\n  \"CONCAT\",\n  \"JMP\",\n  \"EQ\",\n  \"LT\",\n  \"LE\",\n  \"TEST\",\n  \"TESTSET\",\n  \"CALL\",\n  \"TAILCALL\",\n  \"RETURN\",\n  \"FORLOOP\",\n  \"FORPREP\",\n  \"TFORLOOP\",\n  \"SETLIST\",\n  \"CLOSE\",\n  \"CLOSURE\",\n  \"VARARG\",\n  NULL\n};\n\n\n#define opmode(t,a,b,c,m) (((t)<<7) | ((a)<<6) | ((b)<<4) | ((c)<<2) | (m))\n\nconst lu_byte luaP_opmodes[NUM_OPCODES] = {\n/*       T  A    B       C     mode\t\t   opcode\t*/\n  opmode(0, 1, OpArgR, OpArgN, iABC) \t\t/* OP_MOVE */\n ,opmode(0, 1, OpArgK, OpArgN, iABx)\t\t/* OP_LOADK */\n ,opmode(0, 1, OpArgU, OpArgU, iABC)\t\t/* OP_LOADBOOL */\n ,opmode(0, 1, OpArgR, OpArgN, iABC)\t\t/* OP_LOADNIL */\n ,opmode(0, 1, OpArgU, OpArgN, iABC)\t\t/* OP_GETUPVAL */\n ,opmode(0, 1, OpArgK, OpArgN, iABx)\t\t/* OP_GETGLOBAL */\n ,opmode(0, 1, OpArgR, OpArgK, iABC)\t\t/* OP_GETTABLE */\n ,opmode(0, 0, OpArgK, OpArgN, iABx)\t\t/* OP_SETGLOBAL */\n ,opmode(0, 0, OpArgU, OpArgN, iABC)\t\t/* OP_SETUPVAL */\n ,opmode(0, 0, OpArgK, OpArgK, iABC)\t\t/* OP_SETTABLE */\n ,opmode(0, 1, OpArgU, OpArgU, iABC)\t\t/* OP_NEWTABLE */\n ,opmode(0, 1, OpArgR, OpArgK, iABC)\t\t/* OP_SELF */\n ,opmode(0, 1, OpArgK, OpArgK, iABC)\t\t/* OP_ADD */\n ,opmode(0, 1, OpArgK, OpArgK, iABC)\t\t/* OP_SUB */\n ,opmode(0, 1, OpArgK, OpArgK, iABC)\t\t/* OP_MUL */\n ,opmode(0, 1, OpArgK, OpArgK, iABC)\t\t/* OP_DIV */\n ,opmode(0, 1, OpArgK, OpArgK, iABC)\t\t/* OP_MOD */\n ,opmode(0, 1, OpArgK, OpArgK, iABC)\t\t/* OP_POW */\n ,opmode(0, 1, OpArgR, OpArgN, iABC)\t\t/* OP_UNM */\n ,opmode(0, 1, OpArgR, OpArgN, iABC)\t\t/* OP_NOT */\n ,opmode(0, 1, OpArgR, OpArgN, iABC)\t\t/* OP_LEN */\n ,opmode(0, 1, OpArgR, OpArgR, iABC)\t\t/* OP_CONCAT */\n ,opmode(0, 0, OpArgR, OpArgN, iAsBx)\t\t/* OP_JMP */\n ,opmode(1, 0, OpArgK, OpArgK, iABC)\t\t/* OP_EQ */\n ,opmode(1, 0, OpArgK, OpArgK, iABC)\t\t/* OP_LT */\n ,opmode(1, 0, OpArgK, OpArgK, iABC)\t\t/* OP_LE */\n ,opmode(1, 1, OpArgR, OpArgU, iABC)\t\t/* OP_TEST */\n ,opmode(1, 1, OpArgR, OpArgU, iABC)\t\t/* OP_TESTSET */\n ,opmode(0, 1, OpArgU, OpArgU, iABC)\t\t/* OP_CALL */\n ,opmode(0, 1, OpArgU, OpArgU, iABC)\t\t/* OP_TAILCALL */\n ,opmode(0, 0, OpArgU, OpArgN, iABC)\t\t/* OP_RETURN */\n ,opmode(0, 1, OpArgR, OpArgN, iAsBx)\t\t/* OP_FORLOOP */\n ,opmode(0, 1, OpArgR, OpArgN, iAsBx)\t\t/* OP_FORPREP */\n ,opmode(1, 0, OpArgN, OpArgU, iABC)\t\t/* OP_TFORLOOP */\n ,opmode(0, 0, OpArgU, OpArgU, iABC)\t\t/* OP_SETLIST */\n ,opmode(0, 0, OpArgN, OpArgN, iABC)\t\t/* OP_CLOSE */\n ,opmode(0, 1, OpArgU, OpArgN, iABx)\t\t/* OP_CLOSURE */\n ,opmode(0, 1, OpArgU, OpArgN, iABC)\t\t/* OP_VARARG */\n};\n\n"
  },
  {
    "path": "src/lopcodes.h",
    "content": "/*\n** $Id: lopcodes.h,v 1.125.1.1 2007/12/27 13:02:25 roberto Exp $\n** Opcodes for Lua virtual machine\n** See Copyright Notice in lua.h\n*/\n\n#ifndef lopcodes_h\n#define lopcodes_h\n\n#include \"llimits.h\"\n\n\n/*===========================================================================\n  We assume that instructions are unsigned numbers.\n  All instructions have an opcode in the first 6 bits.\n  Instructions can have the following fields:\n\t`A' : 8 bits\n\t`B' : 9 bits\n\t`C' : 9 bits\n\t`Bx' : 18 bits (`B' and `C' together)\n\t`sBx' : signed Bx\n\n  A signed argument is represented in excess K; that is, the number\n  value is the unsigned value minus K. K is exactly the maximum value\n  for that argument (so that -max is represented by 0, and +max is\n  represented by 2*max), which is half the maximum for the corresponding\n  unsigned argument.\n===========================================================================*/\n\n\nenum OpMode {iABC, iABx, iAsBx};  /* basic instruction format */\n\n\n/*\n** size and position of opcode arguments.\n*/\n#define SIZE_C\t\t9\n#define SIZE_B\t\t9\n#define SIZE_Bx\t\t(SIZE_C + SIZE_B)\n#define SIZE_A\t\t8\n\n#define SIZE_OP\t\t6\n\n#define POS_OP\t\t0\n#define POS_A\t\t(POS_OP + SIZE_OP)\n#define POS_C\t\t(POS_A + SIZE_A)\n#define POS_B\t\t(POS_C + SIZE_C)\n#define POS_Bx\t\tPOS_C\n\n\n/*\n** limits for opcode arguments.\n** we use (signed) int to manipulate most arguments,\n** so they must fit in LUAI_BITSINT-1 bits (-1 for sign)\n*/\n#if SIZE_Bx < LUAI_BITSINT-1\n#define MAXARG_Bx        ((1<<SIZE_Bx)-1)\n#define MAXARG_sBx        (MAXARG_Bx>>1)         /* `sBx' is signed */\n#else\n#define MAXARG_Bx        MAX_INT\n#define MAXARG_sBx        MAX_INT\n#endif\n\n\n#define MAXARG_A        ((1<<SIZE_A)-1)\n#define MAXARG_B        ((1<<SIZE_B)-1)\n#define MAXARG_C        ((1<<SIZE_C)-1)\n\n\n/* creates a mask with `n' 1 bits at position `p' */\n#define MASK1(n,p)\t((~((~(Instruction)0)<<n))<<p)\n\n/* creates a mask with `n' 0 bits at position `p' */\n#define MASK0(n,p)\t(~MASK1(n,p))\n\n/*\n** the following macros help to manipulate instructions\n*/\n\n#define GET_OPCODE(i)\t(cast(OpCode, ((i)>>POS_OP) & MASK1(SIZE_OP,0)))\n#define SET_OPCODE(i,o)\t((i) = (((i)&MASK0(SIZE_OP,POS_OP)) | \\\n\t\t((cast(Instruction, o)<<POS_OP)&MASK1(SIZE_OP,POS_OP))))\n\n#define GETARG_A(i)\t(cast(int, ((i)>>POS_A) & MASK1(SIZE_A,0)))\n#define SETARG_A(i,u)\t((i) = (((i)&MASK0(SIZE_A,POS_A)) | \\\n\t\t((cast(Instruction, u)<<POS_A)&MASK1(SIZE_A,POS_A))))\n\n#define GETARG_B(i)\t(cast(int, ((i)>>POS_B) & MASK1(SIZE_B,0)))\n#define SETARG_B(i,b)\t((i) = (((i)&MASK0(SIZE_B,POS_B)) | \\\n\t\t((cast(Instruction, b)<<POS_B)&MASK1(SIZE_B,POS_B))))\n\n#define GETARG_C(i)\t(cast(int, ((i)>>POS_C) & MASK1(SIZE_C,0)))\n#define SETARG_C(i,b)\t((i) = (((i)&MASK0(SIZE_C,POS_C)) | \\\n\t\t((cast(Instruction, b)<<POS_C)&MASK1(SIZE_C,POS_C))))\n\n#define GETARG_Bx(i)\t(cast(int, ((i)>>POS_Bx) & MASK1(SIZE_Bx,0)))\n#define SETARG_Bx(i,b)\t((i) = (((i)&MASK0(SIZE_Bx,POS_Bx)) | \\\n\t\t((cast(Instruction, b)<<POS_Bx)&MASK1(SIZE_Bx,POS_Bx))))\n\n#define GETARG_sBx(i)\t(GETARG_Bx(i)-MAXARG_sBx)\n#define SETARG_sBx(i,b)\tSETARG_Bx((i),cast(unsigned int, (b)+MAXARG_sBx))\n\n\n#define CREATE_ABC(o,a,b,c)\t((cast(Instruction, o)<<POS_OP) \\\n\t\t\t| (cast(Instruction, a)<<POS_A) \\\n\t\t\t| (cast(Instruction, b)<<POS_B) \\\n\t\t\t| (cast(Instruction, c)<<POS_C))\n\n#define CREATE_ABx(o,a,bc)\t((cast(Instruction, o)<<POS_OP) \\\n\t\t\t| (cast(Instruction, a)<<POS_A) \\\n\t\t\t| (cast(Instruction, bc)<<POS_Bx))\n\n\n/*\n** Macros to operate RK indices\n*/\n\n/* this bit 1 means constant (0 means register) */\n#define BITRK\t\t(1 << (SIZE_B - 1))\n\n/* test whether value is a constant */\n#define ISK(x)\t\t((x) & BITRK)\n\n/* gets the index of the constant */\n#define INDEXK(r)\t((int)(r) & ~BITRK)\n\n#define MAXINDEXRK\t(BITRK - 1)\n\n/* code a constant index as a RK value */\n#define RKASK(x)\t((x) | BITRK)\n\n\n/*\n** invalid register that fits in 8 bits\n*/\n#define NO_REG\t\tMAXARG_A\n\n\n/*\n** R(x) - register\n** Kst(x) - constant (in constant table)\n** RK(x) == if ISK(x) then Kst(INDEXK(x)) else R(x)\n*/\n\n\n/*\n** grep \"ORDER OP\" if you change these enums\n*/\n\ntypedef enum {\n/*----------------------------------------------------------------------\nname\t\targs\tdescription\n------------------------------------------------------------------------*/\nOP_MOVE,/*\tA B\tR(A) := R(B)\t\t\t\t\t*/\nOP_LOADK,/*\tA Bx\tR(A) := Kst(Bx)\t\t\t\t\t*/\nOP_LOADBOOL,/*\tA B C\tR(A) := (Bool)B; if (C) pc++\t\t\t*/\nOP_LOADNIL,/*\tA B\tR(A) := ... := R(B) := nil\t\t\t*/\nOP_GETUPVAL,/*\tA B\tR(A) := UpValue[B]\t\t\t\t*/\n\nOP_GETGLOBAL,/*\tA Bx\tR(A) := Gbl[Kst(Bx)]\t\t\t\t*/\nOP_GETTABLE,/*\tA B C\tR(A) := R(B)[RK(C)]\t\t\t\t*/\n\nOP_SETGLOBAL,/*\tA Bx\tGbl[Kst(Bx)] := R(A)\t\t\t\t*/\nOP_SETUPVAL,/*\tA B\tUpValue[B] := R(A)\t\t\t\t*/\nOP_SETTABLE,/*\tA B C\tR(A)[RK(B)] := RK(C)\t\t\t\t*/\n\nOP_NEWTABLE,/*\tA B C\tR(A) := {} (size = B,C)\t\t\t\t*/\n\nOP_SELF,/*\tA B C\tR(A+1) := R(B); R(A) := R(B)[RK(C)]\t\t*/\n\nOP_ADD,/*\tA B C\tR(A) := RK(B) + RK(C)\t\t\t\t*/\nOP_SUB,/*\tA B C\tR(A) := RK(B) - RK(C)\t\t\t\t*/\nOP_MUL,/*\tA B C\tR(A) := RK(B) * RK(C)\t\t\t\t*/\nOP_DIV,/*\tA B C\tR(A) := RK(B) / RK(C)\t\t\t\t*/\nOP_MOD,/*\tA B C\tR(A) := RK(B) % RK(C)\t\t\t\t*/\nOP_POW,/*\tA B C\tR(A) := RK(B) ^ RK(C)\t\t\t\t*/\nOP_UNM,/*\tA B\tR(A) := -R(B)\t\t\t\t\t*/\nOP_NOT,/*\tA B\tR(A) := not R(B)\t\t\t\t*/\nOP_LEN,/*\tA B\tR(A) := length of R(B)\t\t\t\t*/\n\nOP_CONCAT,/*\tA B C\tR(A) := R(B).. ... ..R(C)\t\t\t*/\n\nOP_JMP,/*\tsBx\tpc+=sBx\t\t\t\t\t*/\n\nOP_EQ,/*\tA B C\tif ((RK(B) == RK(C)) ~= A) then pc++\t\t*/\nOP_LT,/*\tA B C\tif ((RK(B) <  RK(C)) ~= A) then pc++  \t\t*/\nOP_LE,/*\tA B C\tif ((RK(B) <= RK(C)) ~= A) then pc++  \t\t*/\n\nOP_TEST,/*\tA C\tif not (R(A) <=> C) then pc++\t\t\t*/ \nOP_TESTSET,/*\tA B C\tif (R(B) <=> C) then R(A) := R(B) else pc++\t*/ \n\nOP_CALL,/*\tA B C\tR(A), ... ,R(A+C-2) := R(A)(R(A+1), ... ,R(A+B-1)) */\nOP_TAILCALL,/*\tA B C\treturn R(A)(R(A+1), ... ,R(A+B-1))\t\t*/\nOP_RETURN,/*\tA B\treturn R(A), ... ,R(A+B-2)\t(see note)\t*/\n\nOP_FORLOOP,/*\tA sBx\tR(A)+=R(A+2);\n\t\t\tif R(A) <?= R(A+1) then { pc+=sBx; R(A+3)=R(A) }*/\nOP_FORPREP,/*\tA sBx\tR(A)-=R(A+2); pc+=sBx\t\t\t\t*/\n\nOP_TFORLOOP,/*\tA C\tR(A+3), ... ,R(A+2+C) := R(A)(R(A+1), R(A+2)); \n                        if R(A+3) ~= nil then R(A+2)=R(A+3) else pc++\t*/ \nOP_SETLIST,/*\tA B C\tR(A)[(C-1)*FPF+i] := R(A+i), 1 <= i <= B\t*/\n\nOP_CLOSE,/*\tA \tclose all variables in the stack up to (>=) R(A)*/\nOP_CLOSURE,/*\tA Bx\tR(A) := closure(KPROTO[Bx], R(A), ... ,R(A+n))\t*/\n\nOP_VARARG/*\tA B\tR(A), R(A+1), ..., R(A+B-1) = vararg\t\t*/\n} OpCode;\n\n\n#define NUM_OPCODES\t(cast(int, OP_VARARG) + 1)\n\n\n\n/*===========================================================================\n  Notes:\n  (*) In OP_CALL, if (B == 0) then B = top. C is the number of returns - 1,\n      and can be 0: OP_CALL then sets `top' to last_result+1, so\n      next open instruction (OP_CALL, OP_RETURN, OP_SETLIST) may use `top'.\n\n  (*) In OP_VARARG, if (B == 0) then use actual number of varargs and\n      set top (like in OP_CALL with C == 0).\n\n  (*) In OP_RETURN, if (B == 0) then return up to `top'\n\n  (*) In OP_SETLIST, if (B == 0) then B = `top';\n      if (C == 0) then next `instruction' is real C\n\n  (*) For comparisons, A specifies what condition the test should accept\n      (true or false).\n\n  (*) All `skips' (pc++) assume that next instruction is a jump\n===========================================================================*/\n\n\n/*\n** masks for instruction properties. The format is:\n** bits 0-1: op mode\n** bits 2-3: C arg mode\n** bits 4-5: B arg mode\n** bit 6: instruction set register A\n** bit 7: operator is a test\n*/  \n\nenum OpArgMask {\n  OpArgN,  /* argument is not used */\n  OpArgU,  /* argument is used */\n  OpArgR,  /* argument is a register or a jump offset */\n  OpArgK   /* argument is a constant or register/constant */\n};\n\nLUAI_DATA const lu_byte luaP_opmodes[NUM_OPCODES];\n\n#define getOpMode(m)\t(cast(enum OpMode, luaP_opmodes[m] & 3))\n#define getBMode(m)\t(cast(enum OpArgMask, (luaP_opmodes[m] >> 4) & 3))\n#define getCMode(m)\t(cast(enum OpArgMask, (luaP_opmodes[m] >> 2) & 3))\n#define testAMode(m)\t(luaP_opmodes[m] & (1 << 6))\n#define testTMode(m)\t(luaP_opmodes[m] & (1 << 7))\n\n\nLUAI_DATA const char *const luaP_opnames[NUM_OPCODES+1];  /* opcode names */\n\n\n/* number of list items to accumulate before a SETLIST instruction */\n#define LFIELDS_PER_FLUSH\t50\n\n\n#endif\n"
  },
  {
    "path": "src/loslib.c",
    "content": "/*\n** $Id: loslib.c,v 1.19.1.3 2008/01/18 16:38:18 roberto Exp $\n** Standard Operating System library\n** See Copyright Notice in lua.h\n*/\n\n\n#include <errno.h>\n#include <locale.h>\n#include <stdlib.h>\n#include <string.h>\n#include <time.h>\n\n#define loslib_c\n#define LUA_LIB\n\n#include \"lua.h\"\n\n#include \"lauxlib.h\"\n#include \"lualib.h\"\n#include \"teliva.h\"\n\n\nstatic int os_pushresult (lua_State *L, int i, const char *filename) {\n  int en = errno;  /* calls to Lua API may change this value */\n  if (i) {\n    lua_pushboolean(L, 1);\n    return 1;\n  }\n  else {\n    lua_pushnil(L);\n    lua_pushfstring(L, \"%s: %s\", filename, strerror(en));\n    lua_pushinteger(L, en);\n    return 3;\n  }\n}\n\n\nstatic char oslib_errbuf[1024] = {0};\nstatic int os_remove (lua_State *L) {\n  const char *filename = luaL_checkstring(L, 1);\n  if (starts_with(filename, \"teliva_tmp_\")) {\n    /* continue */\n  }\n  else if (starts_with(filename, \"teliva_\")) {\n    snprintf(oslib_errbuf, 1024, \"app tried to remove file '%s'; that's never allowed for filenames starting with 'teliva_'\", filename);\n    Previous_message = oslib_errbuf;\n    return os_pushresult(L, 0, filename);\n  }\n  else if (contains(filename, \"./\")) {\n    snprintf(oslib_errbuf, 1024, \"app tried to remove file '%s'; relative paths are never allowed\", filename);\n    Previous_message = oslib_errbuf;\n    return os_pushresult(L, 0, filename);\n  }\n  else if (!file_operation_permitted(filename, \"w\")) {\n    snprintf(oslib_errbuf, 1024, \"app tried to remove file '%s'; give it write permissions (ctrl-p) if that is expected\", filename);\n    Previous_message = oslib_errbuf;\n    return os_pushresult(L, 0, filename);\n  }\n  return os_pushresult(L, remove(filename) == 0, filename);\n}\n\n\nstatic int os_rename (lua_State *L) {\n  const char *fromname = luaL_checkstring(L, 1);\n  const char *toname = luaL_checkstring(L, 2);\n  /* Sandboxing {\n   * A rename is like reading from one file and writing to another file. */\n  if (starts_with(fromname, \"teliva_tmp_\")) {\n    /* continue */\n  }\n  else if (starts_with(fromname, \"teliva_\")) {\n    snprintf(oslib_errbuf, 1024, \"app tried to rename file '%s'; that's never allowed for filenames starting with 'teliva_'\", fromname);\n    Previous_message = oslib_errbuf;\n    return os_pushresult(L, 0, fromname);\n  }\n  else if (contains(fromname, \"./\")) {\n    snprintf(oslib_errbuf, 1024, \"app tried to rename file '%s'; relative paths are never allowed\", fromname);\n    Previous_message = oslib_errbuf;\n    return os_pushresult(L, 0, fromname);\n  }\n  else if (!file_operation_permitted(fromname, \"r\")) {\n    snprintf(oslib_errbuf, 1024, \"app tried to rename file '%s'; give it read permissions (ctrl-p) if that is expected\", fromname);\n    Previous_message = oslib_errbuf;\n    return os_pushresult(L, 0, fromname);\n  }\n  if (starts_with(toname, \"teliva_tmp_\")) {\n    /* continue */\n  }\n  else if (starts_with(toname, \"teliva_\")) {\n    snprintf(oslib_errbuf, 1024, \"app tried to rename to file '%s'; that's never allowed for filenames starting with 'teliva_'\", toname);\n    Previous_message = oslib_errbuf;\n    return os_pushresult(L, 0, toname);\n  }\n  else if (contains(fromname, \"./\")) {\n    snprintf(oslib_errbuf, 1024, \"app tried to rename to file '%s'; relative paths are never allowed\", toname);\n    Previous_message = oslib_errbuf;\n    return os_pushresult(L, 0, toname);\n  }\n  else if (!file_operation_permitted(toname, \"w\")) {\n    snprintf(oslib_errbuf, 1024, \"app tried to rename to file '%s'; give it write permissions (ctrl-p) if that is expected\", toname);\n    Previous_message = oslib_errbuf;\n    return os_pushresult(L, 0, toname);\n  }\n  /* } */\n  return os_pushresult(L, rename(fromname, toname) == 0, fromname);\n}\n\n\nstatic int os_tmpname (lua_State *L) {\n  char buff[LUA_TMPNAMBUFSIZE];\n  int err;\n  lua_tmpnam(buff, err);\n  if (err)\n    return luaL_error(L, \"unable to generate a unique filename\");\n  lua_pushstring(L, buff);\n  return 1;\n}\n\n\nstatic int os_clock (lua_State *L) {\n  lua_pushnumber(L, ((lua_Number)clock())/(lua_Number)CLOCKS_PER_SEC);\n  return 1;\n}\n\n\n/*\n** {======================================================\n** Time/Date operations\n** { year=%Y, month=%m, day=%d, hour=%H, min=%M, sec=%S,\n**   wday=%w+1, yday=%j, isdst=? }\n** =======================================================\n*/\n\nstatic void setfield (lua_State *L, const char *key, int value) {\n  lua_pushinteger(L, value);\n  lua_setfield(L, -2, key);\n}\n\nstatic void setboolfield (lua_State *L, const char *key, int value) {\n  if (value < 0)  /* undefined? */\n    return;  /* does not set field */\n  lua_pushboolean(L, value);\n  lua_setfield(L, -2, key);\n}\n\nstatic int getboolfield (lua_State *L, const char *key) {\n  int res;\n  lua_getfield(L, -1, key);\n  res = lua_isnil(L, -1) ? -1 : lua_toboolean(L, -1);\n  lua_pop(L, 1);\n  return res;\n}\n\n\nstatic int getfield (lua_State *L, const char *key, int d) {\n  int res;\n  lua_getfield(L, -1, key);\n  if (lua_isnumber(L, -1))\n    res = (int)lua_tointeger(L, -1);\n  else {\n    if (d < 0)\n      return luaL_error(L, \"field \" LUA_QS \" missing in date table\", key);\n    res = d;\n  }\n  lua_pop(L, 1);\n  return res;\n}\n\n\nstatic int os_date (lua_State *L) {\n  const char *s = luaL_optstring(L, 1, \"%c\");\n  time_t t = luaL_opt(L, (time_t)luaL_checknumber, 2, time(NULL));\n  struct tm *stm;\n  if (*s == '!') {  /* UTC? */\n    stm = gmtime(&t);\n    s++;  /* skip `!' */\n  }\n  else\n    stm = localtime(&t);\n  if (stm == NULL)  /* invalid date? */\n    lua_pushnil(L);\n  else if (strcmp(s, \"*t\") == 0) {\n    lua_createtable(L, 0, 9);  /* 9 = number of fields */\n    setfield(L, \"sec\", stm->tm_sec);\n    setfield(L, \"min\", stm->tm_min);\n    setfield(L, \"hour\", stm->tm_hour);\n    setfield(L, \"day\", stm->tm_mday);\n    setfield(L, \"month\", stm->tm_mon+1);\n    setfield(L, \"year\", stm->tm_year+1900);\n    setfield(L, \"wday\", stm->tm_wday+1);\n    setfield(L, \"yday\", stm->tm_yday+1);\n    setboolfield(L, \"isdst\", stm->tm_isdst);\n  }\n  else {\n    char cc[3];\n    luaL_Buffer b;\n    cc[0] = '%'; cc[2] = '\\0';\n    luaL_buffinit(L, &b);\n    for (; *s; s++) {\n      if (*s != '%' || *(s + 1) == '\\0')  /* no conversion specifier? */\n        luaL_addchar(&b, *s);\n      else {\n        size_t reslen;\n        char buff[200];  /* should be big enough for any conversion result */\n        cc[1] = *(++s);\n        reslen = strftime(buff, sizeof(buff), cc, stm);\n        luaL_addlstring(&b, buff, reslen);\n      }\n    }\n    luaL_pushresult(&b);\n  }\n  return 1;\n}\n\n\nstatic int os_time (lua_State *L) {\n  time_t t;\n  if (lua_isnoneornil(L, 1))  /* called without args? */\n    t = time(NULL);  /* get current time */\n  else {\n    struct tm ts;\n    luaL_checktype(L, 1, LUA_TTABLE);\n    lua_settop(L, 1);  /* make sure table is at the top */\n    ts.tm_sec = getfield(L, \"sec\", 0);\n    ts.tm_min = getfield(L, \"min\", 0);\n    ts.tm_hour = getfield(L, \"hour\", 12);\n    ts.tm_mday = getfield(L, \"day\", -1);\n    ts.tm_mon = getfield(L, \"month\", -1) - 1;\n    ts.tm_year = getfield(L, \"year\", -1) - 1900;\n    ts.tm_isdst = getboolfield(L, \"isdst\");\n    t = mktime(&ts);\n  }\n  if (t == (time_t)(-1))\n    lua_pushnil(L);\n  else\n    lua_pushnumber(L, (lua_Number)t);\n  return 1;\n}\n\n\nstatic int os_difftime (lua_State *L) {\n  lua_pushnumber(L, difftime((time_t)(luaL_checknumber(L, 1)),\n                             (time_t)(luaL_optnumber(L, 2, 0))));\n  return 1;\n}\n\n/* }====================================================== */\n\n\nstatic int os_setlocale (lua_State *L) {\n  static const int cat[] = {LC_ALL, LC_COLLATE, LC_CTYPE, LC_MONETARY,\n                      LC_NUMERIC, LC_TIME};\n  static const char *const catnames[] = {\"all\", \"collate\", \"ctype\", \"monetary\",\n     \"numeric\", \"time\", NULL};\n  const char *l = luaL_optstring(L, 1, NULL);\n  int op = luaL_checkoption(L, 2, \"all\", catnames);\n  lua_pushstring(L, setlocale(cat[op], l));\n  return 1;\n}\n\n\nstatic int os_exit (lua_State *L) {\n  exit(luaL_optint(L, 1, EXIT_SUCCESS));\n}\n\nstatic const luaL_Reg syslib[] = {\n  {\"clock\",     os_clock},\n  {\"date\",      os_date},\n  {\"difftime\",  os_difftime},\n  /* no execute without sandboxing it */\n  {\"exit\",      os_exit},\n  /* no getenv without sandboxing it */\n  {\"remove\",    os_remove},\n  {\"rename\",    os_rename},\n  {\"setlocale\", os_setlocale},\n  {\"time\",      os_time},\n  {\"tmpname\",   os_tmpname},\n  {NULL, NULL}\n};\n\n/* }====================================================== */\n\n\n\nLUALIB_API int luaopen_os (lua_State *L) {\n  luaL_register(L, LUA_OSLIBNAME, syslib);\n  return 1;\n}\n\n"
  },
  {
    "path": "src/lparser.c",
    "content": "/*\n** $Id: lparser.c,v 2.42.1.4 2011/10/21 19:31:42 roberto Exp $\n** Lua Parser\n** See Copyright Notice in lua.h\n*/\n\n\n#include <string.h>\n\n#define lparser_c\n#define LUA_CORE\n\n#include \"lua.h\"\n\n#include \"lcode.h\"\n#include \"ldebug.h\"\n#include \"ldo.h\"\n#include \"lfunc.h\"\n#include \"llex.h\"\n#include \"lmem.h\"\n#include \"lobject.h\"\n#include \"lopcodes.h\"\n#include \"lparser.h\"\n#include \"lstate.h\"\n#include \"lstring.h\"\n#include \"ltable.h\"\n\n\n\n#define hasmultret(k)\t\t((k) == VCALL || (k) == VVARARG)\n\n#define getlocvar(fs, i)\t((fs)->f->locvars[(fs)->actvar[i]])\n\n#define luaY_checklimit(fs,v,l,m)\tif ((v)>(l)) errorlimit(fs,l,m)\n\n\n/*\n** nodes for block list (list of active blocks)\n*/\ntypedef struct BlockCnt {\n  struct BlockCnt *previous;  /* chain */\n  int breaklist;  /* list of jumps out of this loop */\n  lu_byte nactvar;  /* # active locals outside the breakable structure */\n  lu_byte upval;  /* true if some variable in the block is an upvalue */\n  lu_byte isbreakable;  /* true if `block' is a loop */\n} BlockCnt;\n\n\n\n/*\n** prototypes for recursive non-terminal functions\n*/\nstatic void chunk (LexState *ls);\nstatic void expr (LexState *ls, expdesc *v);\n\n\nstatic void anchor_token (LexState *ls) {\n  if (ls->t.token == TK_NAME || ls->t.token == TK_STRING) {\n    TString *ts = ls->t.seminfo.ts;\n    luaX_newstring(ls, getstr(ts), ts->tsv.len);\n  }\n}\n\n\nstatic void error_expected (LexState *ls, int token) {\n  luaX_syntaxerror(ls,\n      luaO_pushfstring(ls->L, LUA_QS \" expected\", luaX_token2str(ls, token)));\n}\n\n\nstatic void errorlimit (FuncState *fs, int limit, const char *what) {\n  const char *msg = (fs->f->linedefined == 0) ?\n    luaO_pushfstring(fs->L, \"main function has more than %d %s\", limit, what) :\n    luaO_pushfstring(fs->L, \"function at line %d has more than %d %s\",\n                            fs->f->linedefined, limit, what);\n  luaX_lexerror(fs->ls, msg, 0);\n}\n\n\nstatic int testnext (LexState *ls, int c) {\n  if (ls->t.token == c) {\n    luaX_next(ls);\n    return 1;\n  }\n  else return 0;\n}\n\n\nstatic void check (LexState *ls, int c) {\n  if (ls->t.token != c)\n    error_expected(ls, c);\n}\n\nstatic void checknext (LexState *ls, int c) {\n  check(ls, c);\n  luaX_next(ls);\n}\n\n\n#define check_condition(ls,c,msg)\t{ if (!(c)) luaX_syntaxerror(ls, msg); }\n\n\n\nstatic void check_match (LexState *ls, int what, int who, int where) {\n  if (!testnext(ls, what)) {\n    if (where == ls->linenumber)\n      error_expected(ls, what);\n    else {\n      luaX_syntaxerror(ls, luaO_pushfstring(ls->L,\n             LUA_QS \" expected (to close \" LUA_QS \" at line %d)\",\n              luaX_token2str(ls, what), luaX_token2str(ls, who), where));\n    }\n  }\n}\n\n\nstatic TString *str_checkname (LexState *ls) {\n  TString *ts;\n  check(ls, TK_NAME);\n  ts = ls->t.seminfo.ts;\n  luaX_next(ls);\n  return ts;\n}\n\n\nstatic void init_exp (expdesc *e, expkind k, int i) {\n  e->f = e->t = NO_JUMP;\n  e->k = k;\n  e->u.s.info = i;\n}\n\n\nstatic void codestring (LexState *ls, expdesc *e, TString *s) {\n  init_exp(e, VK, luaK_stringK(ls->fs, s));\n}\n\n\nstatic void checkname(LexState *ls, expdesc *e) {\n  codestring(ls, e, str_checkname(ls));\n}\n\n\nstatic int registerlocalvar (LexState *ls, TString *varname) {\n  FuncState *fs = ls->fs;\n  Proto *f = fs->f;\n  int oldsize = f->sizelocvars;\n  luaM_growvector(ls->L, f->locvars, fs->nlocvars, f->sizelocvars,\n                  LocVar, SHRT_MAX, \"too many local variables\");\n  while (oldsize < f->sizelocvars) f->locvars[oldsize++].varname = NULL;\n  f->locvars[fs->nlocvars].varname = varname;\n  luaC_objbarrier(ls->L, f, varname);\n  return fs->nlocvars++;\n}\n\n\n#define new_localvarliteral(ls,v,n) \\\n  new_localvar(ls, luaX_newstring(ls, \"\" v, (sizeof(v)/sizeof(char))-1), n)\n\n\nstatic void new_localvar (LexState *ls, TString *name, int n) {\n  FuncState *fs = ls->fs;\n  luaY_checklimit(fs, fs->nactvar+n+1, LUAI_MAXVARS, \"local variables\");\n  fs->actvar[fs->nactvar+n] = cast(unsigned short, registerlocalvar(ls, name));\n}\n\n\nstatic void adjustlocalvars (LexState *ls, int nvars) {\n  FuncState *fs = ls->fs;\n  fs->nactvar = cast_byte(fs->nactvar + nvars);\n  for (; nvars; nvars--) {\n    getlocvar(fs, fs->nactvar - nvars).startpc = fs->pc;\n  }\n}\n\n\nstatic void removevars (LexState *ls, int tolevel) {\n  FuncState *fs = ls->fs;\n  while (fs->nactvar > tolevel)\n    getlocvar(fs, --fs->nactvar).endpc = fs->pc;\n}\n\n\nstatic int indexupvalue (FuncState *fs, TString *name, expdesc *v) {\n  int i;\n  Proto *f = fs->f;\n  int oldsize = f->sizeupvalues;\n  for (i=0; i<f->nups; i++) {\n    if (fs->upvalues[i].k == v->k && fs->upvalues[i].info == v->u.s.info) {\n      lua_assert(f->upvalues[i] == name);\n      return i;\n    }\n  }\n  /* new one */\n  luaY_checklimit(fs, f->nups + 1, LUAI_MAXUPVALUES, \"upvalues\");\n  luaM_growvector(fs->L, f->upvalues, f->nups, f->sizeupvalues,\n                  TString *, MAX_INT, \"\");\n  while (oldsize < f->sizeupvalues) f->upvalues[oldsize++] = NULL;\n  f->upvalues[f->nups] = name;\n  luaC_objbarrier(fs->L, f, name);\n  lua_assert(v->k == VLOCAL || v->k == VUPVAL);\n  fs->upvalues[f->nups].k = cast_byte(v->k);\n  fs->upvalues[f->nups].info = cast_byte(v->u.s.info);\n  return f->nups++;\n}\n\n\nstatic int searchvar (FuncState *fs, TString *n) {\n  int i;\n  for (i=fs->nactvar-1; i >= 0; i--) {\n    if (n == getlocvar(fs, i).varname)\n      return i;\n  }\n  return -1;  /* not found */\n}\n\n\nstatic void markupval (FuncState *fs, int level) {\n  BlockCnt *bl = fs->bl;\n  while (bl && bl->nactvar > level) bl = bl->previous;\n  if (bl) bl->upval = 1;\n}\n\n\nstatic int singlevaraux (FuncState *fs, TString *n, expdesc *var, int base) {\n  if (fs == NULL) {  /* no more levels? */\n    init_exp(var, VGLOBAL, NO_REG);  /* default is global variable */\n    return VGLOBAL;\n  }\n  else {\n    int v = searchvar(fs, n);  /* look up at current level */\n    if (v >= 0) {\n      init_exp(var, VLOCAL, v);\n      if (!base)\n        markupval(fs, v);  /* local will be used as an upval */\n      return VLOCAL;\n    }\n    else {  /* not found at current level; try upper one */\n      if (singlevaraux(fs->prev, n, var, 0) == VGLOBAL)\n        return VGLOBAL;\n      var->u.s.info = indexupvalue(fs, n, var);  /* else was LOCAL or UPVAL */\n      var->k = VUPVAL;  /* upvalue in this level */\n      return VUPVAL;\n    }\n  }\n}\n\n\nstatic void singlevar (LexState *ls, expdesc *var) {\n  TString *varname = str_checkname(ls);\n  FuncState *fs = ls->fs;\n  if (singlevaraux(fs, varname, var, 1) == VGLOBAL)\n    var->u.s.info = luaK_stringK(fs, varname);  /* info points to global name */\n}\n\n\nstatic void adjust_assign (LexState *ls, int nvars, int nexps, expdesc *e) {\n  FuncState *fs = ls->fs;\n  int extra = nvars - nexps;\n  if (hasmultret(e->k)) {\n    extra++;  /* includes call itself */\n    if (extra < 0) extra = 0;\n    luaK_setreturns(fs, e, extra);  /* last exp. provides the difference */\n    if (extra > 1) luaK_reserveregs(fs, extra-1);\n  }\n  else {\n    if (e->k != VVOID) luaK_exp2nextreg(fs, e);  /* close last expression */\n    if (extra > 0) {\n      int reg = fs->freereg;\n      luaK_reserveregs(fs, extra);\n      luaK_nil(fs, reg, extra);\n    }\n  }\n}\n\n\nstatic void enterlevel (LexState *ls) {\n  if (++ls->L->nCcalls > LUAI_MAXCCALLS)\n\tluaX_lexerror(ls, \"chunk has too many syntax levels\", 0);\n}\n\n\n#define leavelevel(ls)\t((ls)->L->nCcalls--)\n\n\nstatic void enterblock (FuncState *fs, BlockCnt *bl, lu_byte isbreakable) {\n  bl->breaklist = NO_JUMP;\n  bl->isbreakable = isbreakable;\n  bl->nactvar = fs->nactvar;\n  bl->upval = 0;\n  bl->previous = fs->bl;\n  fs->bl = bl;\n  lua_assert(fs->freereg == fs->nactvar);\n}\n\n\nstatic void leaveblock (FuncState *fs) {\n  BlockCnt *bl = fs->bl;\n  fs->bl = bl->previous;\n  removevars(fs->ls, bl->nactvar);\n  if (bl->upval)\n    luaK_codeABC(fs, OP_CLOSE, bl->nactvar, 0, 0);\n  /* a block either controls scope or breaks (never both) */\n  lua_assert(!bl->isbreakable || !bl->upval);\n  lua_assert(bl->nactvar == fs->nactvar);\n  fs->freereg = fs->nactvar;  /* free registers */\n  luaK_patchtohere(fs, bl->breaklist);\n}\n\n\nstatic void pushclosure (LexState *ls, FuncState *func, expdesc *v) {\n  FuncState *fs = ls->fs;\n  Proto *f = fs->f;\n  int oldsize = f->sizep;\n  int i;\n  luaM_growvector(ls->L, f->p, fs->np, f->sizep, Proto *,\n                  MAXARG_Bx, \"constant table overflow\");\n  while (oldsize < f->sizep) f->p[oldsize++] = NULL;\n  f->p[fs->np++] = func->f;\n  luaC_objbarrier(ls->L, f, func->f);\n  init_exp(v, VRELOCABLE, luaK_codeABx(fs, OP_CLOSURE, 0, fs->np-1));\n  for (i=0; i<func->f->nups; i++) {\n    OpCode o = (func->upvalues[i].k == VLOCAL) ? OP_MOVE : OP_GETUPVAL;\n    luaK_codeABC(fs, o, 0, func->upvalues[i].info, 0);\n  }\n}\n\n\nstatic void open_func (LexState *ls, FuncState *fs) {\n  lua_State *L = ls->L;\n  Proto *f = luaF_newproto(L);\n  fs->f = f;\n  fs->prev = ls->fs;  /* linked list of funcstates */\n  fs->ls = ls;\n  fs->L = L;\n  ls->fs = fs;\n  fs->pc = 0;\n  fs->lasttarget = -1;\n  fs->jpc = NO_JUMP;\n  fs->freereg = 0;\n  fs->nk = 0;\n  fs->np = 0;\n  fs->nlocvars = 0;\n  fs->nactvar = 0;\n  fs->bl = NULL;\n  f->source = ls->source;\n  f->maxstacksize = 2;  /* registers 0/1 are always valid */\n  fs->h = luaH_new(L, 0, 0);\n  /* anchor table of constants and prototype (to avoid being collected) */\n  sethvalue2s(L, L->top, fs->h);\n  incr_top(L);\n  setptvalue2s(L, L->top, f);\n  incr_top(L);\n}\n\n\nstatic void close_func (LexState *ls) {\n  lua_State *L = ls->L;\n  FuncState *fs = ls->fs;\n  Proto *f = fs->f;\n  removevars(ls, 0);\n  luaK_ret(fs, 0, 0);  /* final return */\n  luaM_reallocvector(L, f->code, f->sizecode, fs->pc, Instruction);\n  f->sizecode = fs->pc;\n  luaM_reallocvector(L, f->lineinfo, f->sizelineinfo, fs->pc, int);\n  f->sizelineinfo = fs->pc;\n  luaM_reallocvector(L, f->k, f->sizek, fs->nk, TValue);\n  f->sizek = fs->nk;\n  luaM_reallocvector(L, f->p, f->sizep, fs->np, Proto *);\n  f->sizep = fs->np;\n  luaM_reallocvector(L, f->locvars, f->sizelocvars, fs->nlocvars, LocVar);\n  f->sizelocvars = fs->nlocvars;\n  luaM_reallocvector(L, f->upvalues, f->sizeupvalues, f->nups, TString *);\n  f->sizeupvalues = f->nups;\n  lua_assert(luaG_checkcode(f));\n  lua_assert(fs->bl == NULL);\n  ls->fs = fs->prev;\n  /* last token read was anchored in defunct function; must reanchor it */\n  if (fs) anchor_token(ls);\n  L->top -= 2;  /* remove table and prototype from the stack */\n}\n\n\nProto *luaY_parser (lua_State *L, ZIO *z, Mbuffer *buff, const char *name) {\n  struct LexState lexstate;\n  struct FuncState funcstate;\n  lexstate.buff = buff;\n  luaX_setinput(L, &lexstate, z, luaS_new(L, name));\n  open_func(&lexstate, &funcstate);\n  funcstate.f->is_vararg = VARARG_ISVARARG;  /* main func. is always vararg */\n  luaX_next(&lexstate);  /* read first token */\n  chunk(&lexstate);\n  check(&lexstate, TK_EOS);\n  close_func(&lexstate);\n  lua_assert(funcstate.prev == NULL);\n  lua_assert(funcstate.f->nups == 0);\n  lua_assert(lexstate.fs == NULL);\n  return funcstate.f;\n}\n\n\n\n/*============================================================*/\n/* GRAMMAR RULES */\n/*============================================================*/\n\n\nstatic void field (LexState *ls, expdesc *v) {\n  /* field -> ['.' | ':'] NAME */\n  FuncState *fs = ls->fs;\n  expdesc key;\n  luaK_exp2anyreg(fs, v);\n  luaX_next(ls);  /* skip the dot or colon */\n  checkname(ls, &key);\n  luaK_indexed(fs, v, &key);\n}\n\n\nstatic void yindex (LexState *ls, expdesc *v) {\n  /* index -> '[' expr ']' */\n  luaX_next(ls);  /* skip the '[' */\n  expr(ls, v);\n  luaK_exp2val(ls->fs, v);\n  checknext(ls, ']');\n}\n\n\n/*\n** {======================================================================\n** Rules for Constructors\n** =======================================================================\n*/\n\n\nstruct ConsControl {\n  expdesc v;  /* last list item read */\n  expdesc *t;  /* table descriptor */\n  int nh;  /* total number of `record' elements */\n  int na;  /* total number of array elements */\n  int tostore;  /* number of array elements pending to be stored */\n};\n\n\nstatic void recfield (LexState *ls, struct ConsControl *cc) {\n  /* recfield -> (NAME | `['exp1`]') = exp1 */\n  FuncState *fs = ls->fs;\n  int reg = ls->fs->freereg;\n  expdesc key, val;\n  int rkkey;\n  if (ls->t.token == TK_NAME) {\n    luaY_checklimit(fs, cc->nh, MAX_INT, \"items in a constructor\");\n    checkname(ls, &key);\n  }\n  else  /* ls->t.token == '[' */\n    yindex(ls, &key);\n  cc->nh++;\n  checknext(ls, '=');\n  rkkey = luaK_exp2RK(fs, &key);\n  expr(ls, &val);\n  luaK_codeABC(fs, OP_SETTABLE, cc->t->u.s.info, rkkey, luaK_exp2RK(fs, &val));\n  fs->freereg = reg;  /* free registers */\n}\n\n\nstatic void closelistfield (FuncState *fs, struct ConsControl *cc) {\n  if (cc->v.k == VVOID) return;  /* there is no list item */\n  luaK_exp2nextreg(fs, &cc->v);\n  cc->v.k = VVOID;\n  if (cc->tostore == LFIELDS_PER_FLUSH) {\n    luaK_setlist(fs, cc->t->u.s.info, cc->na, cc->tostore);  /* flush */\n    cc->tostore = 0;  /* no more items pending */\n  }\n}\n\n\nstatic void lastlistfield (FuncState *fs, struct ConsControl *cc) {\n  if (cc->tostore == 0) return;\n  if (hasmultret(cc->v.k)) {\n    luaK_setmultret(fs, &cc->v);\n    luaK_setlist(fs, cc->t->u.s.info, cc->na, LUA_MULTRET);\n    cc->na--;  /* do not count last expression (unknown number of elements) */\n  }\n  else {\n    if (cc->v.k != VVOID)\n      luaK_exp2nextreg(fs, &cc->v);\n    luaK_setlist(fs, cc->t->u.s.info, cc->na, cc->tostore);\n  }\n}\n\n\nstatic void listfield (LexState *ls, struct ConsControl *cc) {\n  expr(ls, &cc->v);\n  luaY_checklimit(ls->fs, cc->na, MAX_INT, \"items in a constructor\");\n  cc->na++;\n  cc->tostore++;\n}\n\n\nstatic void constructor (LexState *ls, expdesc *t) {\n  /* constructor -> ?? */\n  FuncState *fs = ls->fs;\n  int line = ls->linenumber;\n  int pc = luaK_codeABC(fs, OP_NEWTABLE, 0, 0, 0);\n  struct ConsControl cc;\n  cc.na = cc.nh = cc.tostore = 0;\n  cc.t = t;\n  init_exp(t, VRELOCABLE, pc);\n  init_exp(&cc.v, VVOID, 0);  /* no value (yet) */\n  luaK_exp2nextreg(ls->fs, t);  /* fix it at stack top (for gc) */\n  checknext(ls, '{');\n  do {\n    lua_assert(cc.v.k == VVOID || cc.tostore > 0);\n    if (ls->t.token == '}') break;\n    closelistfield(fs, &cc);\n    switch(ls->t.token) {\n      case TK_NAME: {  /* may be listfields or recfields */\n        luaX_lookahead(ls);\n        if (ls->lookahead.token != '=')  /* expression? */\n          listfield(ls, &cc);\n        else\n          recfield(ls, &cc);\n        break;\n      }\n      case '[': {  /* constructor_item -> recfield */\n        recfield(ls, &cc);\n        break;\n      }\n      default: {  /* constructor_part -> listfield */\n        listfield(ls, &cc);\n        break;\n      }\n    }\n  } while (testnext(ls, ',') || testnext(ls, ';'));\n  check_match(ls, '}', '{', line);\n  lastlistfield(fs, &cc);\n  SETARG_B(fs->f->code[pc], luaO_int2fb(cc.na)); /* set initial array size */\n  SETARG_C(fs->f->code[pc], luaO_int2fb(cc.nh));  /* set initial table size */\n}\n\n/* }====================================================================== */\n\n\n\nstatic void parlist (LexState *ls) {\n  /* parlist -> [ param { `,' param } ] */\n  FuncState *fs = ls->fs;\n  Proto *f = fs->f;\n  int nparams = 0;\n  f->is_vararg = 0;\n  if (ls->t.token != ')') {  /* is `parlist' not empty? */\n    do {\n      switch (ls->t.token) {\n        case TK_NAME: {  /* param -> NAME */\n          new_localvar(ls, str_checkname(ls), nparams++);\n          break;\n        }\n        case TK_DOTS: {  /* param -> `...' */\n          luaX_next(ls);\n#if defined(LUA_COMPAT_VARARG)\n          /* use `arg' as default name */\n          new_localvarliteral(ls, \"arg\", nparams++);\n          f->is_vararg = VARARG_HASARG | VARARG_NEEDSARG;\n#endif\n          f->is_vararg |= VARARG_ISVARARG;\n          break;\n        }\n        default: luaX_syntaxerror(ls, \"<name> or \" LUA_QL(\"...\") \" expected\");\n      }\n    } while (!f->is_vararg && testnext(ls, ','));\n  }\n  adjustlocalvars(ls, nparams);\n  f->numparams = cast_byte(fs->nactvar - (f->is_vararg & VARARG_HASARG));\n  luaK_reserveregs(fs, fs->nactvar);  /* reserve register for parameters */\n}\n\n\nstatic void body (LexState *ls, expdesc *e, int needself, int line) {\n  /* body ->  `(' parlist `)' chunk END */\n  FuncState new_fs;\n  open_func(ls, &new_fs);\n  new_fs.f->linedefined = line;\n  checknext(ls, '(');\n  if (needself) {\n    new_localvarliteral(ls, \"self\", 0);\n    adjustlocalvars(ls, 1);\n  }\n  parlist(ls);\n  checknext(ls, ')');\n  chunk(ls);\n  new_fs.f->lastlinedefined = ls->linenumber;\n  check_match(ls, TK_END, TK_FUNCTION, line);\n  close_func(ls);\n  pushclosure(ls, &new_fs, e);\n}\n\n\nstatic int explist1 (LexState *ls, expdesc *v) {\n  /* explist1 -> expr { `,' expr } */\n  int n = 1;  /* at least one expression */\n  expr(ls, v);\n  while (testnext(ls, ',')) {\n    luaK_exp2nextreg(ls->fs, v);\n    expr(ls, v);\n    n++;\n  }\n  return n;\n}\n\n\nstatic void funcargs (LexState *ls, expdesc *f) {\n  FuncState *fs = ls->fs;\n  expdesc args;\n  int base, nparams;\n  int line = ls->linenumber;\n  switch (ls->t.token) {\n    case '(': {  /* funcargs -> `(' [ explist1 ] `)' */\n      if (line != ls->lastline)\n        luaX_syntaxerror(ls,\"ambiguous syntax (function call x new statement)\");\n      luaX_next(ls);\n      if (ls->t.token == ')')  /* arg list is empty? */\n        args.k = VVOID;\n      else {\n        explist1(ls, &args);\n        luaK_setmultret(fs, &args);\n      }\n      check_match(ls, ')', '(', line);\n      break;\n    }\n    case '{': {  /* funcargs -> constructor */\n      constructor(ls, &args);\n      break;\n    }\n    case TK_STRING: {  /* funcargs -> STRING */\n      codestring(ls, &args, ls->t.seminfo.ts);\n      luaX_next(ls);  /* must use `seminfo' before `next' */\n      break;\n    }\n    default: {\n      luaX_syntaxerror(ls, \"function arguments expected\");\n      return;\n    }\n  }\n  lua_assert(f->k == VNONRELOC);\n  base = f->u.s.info;  /* base register for call */\n  if (hasmultret(args.k))\n    nparams = LUA_MULTRET;  /* open call */\n  else {\n    if (args.k != VVOID)\n      luaK_exp2nextreg(fs, &args);  /* close last argument */\n    nparams = fs->freereg - (base+1);\n  }\n  init_exp(f, VCALL, luaK_codeABC(fs, OP_CALL, base, nparams+1, 2));\n  luaK_fixline(fs, line);\n  fs->freereg = base+1;  /* call remove function and arguments and leaves\n                            (unless changed) one result */\n}\n\n\n\n\n/*\n** {======================================================================\n** Expression parsing\n** =======================================================================\n*/\n\n\nstatic void prefixexp (LexState *ls, expdesc *v) {\n  /* prefixexp -> NAME | '(' expr ')' */\n  switch (ls->t.token) {\n    case '(': {\n      int line = ls->linenumber;\n      luaX_next(ls);\n      expr(ls, v);\n      check_match(ls, ')', '(', line);\n      luaK_dischargevars(ls->fs, v);\n      return;\n    }\n    case TK_NAME: {\n      singlevar(ls, v);\n      return;\n    }\n    default: {\n      luaX_syntaxerror(ls, \"unexpected symbol\");\n      return;\n    }\n  }\n}\n\n\nstatic void primaryexp (LexState *ls, expdesc *v) {\n  /* primaryexp ->\n        prefixexp { `.' NAME | `[' exp `]' | `:' NAME funcargs | funcargs } */\n  FuncState *fs = ls->fs;\n  prefixexp(ls, v);\n  for (;;) {\n    switch (ls->t.token) {\n      case '.': {  /* field */\n        field(ls, v);\n        break;\n      }\n      case '[': {  /* `[' exp1 `]' */\n        expdesc key;\n        luaK_exp2anyreg(fs, v);\n        yindex(ls, &key);\n        luaK_indexed(fs, v, &key);\n        break;\n      }\n      case ':': {  /* `:' NAME funcargs */\n        expdesc key;\n        luaX_next(ls);\n        checkname(ls, &key);\n        luaK_self(fs, v, &key);\n        funcargs(ls, v);\n        break;\n      }\n      case '(': case TK_STRING: case '{': {  /* funcargs */\n        luaK_exp2nextreg(fs, v);\n        funcargs(ls, v);\n        break;\n      }\n      default: return;\n    }\n  }\n}\n\n\nstatic void simpleexp (LexState *ls, expdesc *v) {\n  /* simpleexp -> NUMBER | STRING | NIL | true | false | ... |\n                  constructor | FUNCTION body | primaryexp */\n  switch (ls->t.token) {\n    case TK_NUMBER: {\n      init_exp(v, VKNUM, 0);\n      v->u.nval = ls->t.seminfo.r;\n      break;\n    }\n    case TK_STRING: {\n      codestring(ls, v, ls->t.seminfo.ts);\n      break;\n    }\n    case TK_NIL: {\n      init_exp(v, VNIL, 0);\n      break;\n    }\n    case TK_TRUE: {\n      init_exp(v, VTRUE, 0);\n      break;\n    }\n    case TK_FALSE: {\n      init_exp(v, VFALSE, 0);\n      break;\n    }\n    case TK_DOTS: {  /* vararg */\n      FuncState *fs = ls->fs;\n      check_condition(ls, fs->f->is_vararg,\n                      \"cannot use \" LUA_QL(\"...\") \" outside a vararg function\");\n      fs->f->is_vararg &= ~VARARG_NEEDSARG;  /* don't need 'arg' */\n      init_exp(v, VVARARG, luaK_codeABC(fs, OP_VARARG, 0, 1, 0));\n      break;\n    }\n    case '{': {  /* constructor */\n      constructor(ls, v);\n      return;\n    }\n    case TK_FUNCTION: {\n      luaX_next(ls);\n      body(ls, v, 0, ls->linenumber);\n      return;\n    }\n    default: {\n      primaryexp(ls, v);\n      return;\n    }\n  }\n  luaX_next(ls);\n}\n\n\nstatic UnOpr getunopr (int op) {\n  switch (op) {\n    case TK_NOT: return OPR_NOT;\n    case '-': return OPR_MINUS;\n    case '#': return OPR_LEN;\n    default: return OPR_NOUNOPR;\n  }\n}\n\n\nstatic BinOpr getbinopr (int op) {\n  switch (op) {\n    case '+': return OPR_ADD;\n    case '-': return OPR_SUB;\n    case '*': return OPR_MUL;\n    case '/': return OPR_DIV;\n    case '%': return OPR_MOD;\n    case '^': return OPR_POW;\n    case TK_CONCAT: return OPR_CONCAT;\n    case TK_NE: return OPR_NE;\n    case TK_EQ: return OPR_EQ;\n    case '<': return OPR_LT;\n    case TK_LE: return OPR_LE;\n    case '>': return OPR_GT;\n    case TK_GE: return OPR_GE;\n    case TK_AND: return OPR_AND;\n    case TK_OR: return OPR_OR;\n    default: return OPR_NOBINOPR;\n  }\n}\n\n\nstatic const struct {\n  lu_byte left;  /* left priority for each binary operator */\n  lu_byte right; /* right priority */\n} priority[] = {  /* ORDER OPR */\n   {6, 6}, {6, 6}, {7, 7}, {7, 7}, {7, 7},  /* `+' `-' `/' `%' */\n   {10, 9}, {5, 4},                 /* power and concat (right associative) */\n   {3, 3}, {3, 3},                  /* equality and inequality */\n   {3, 3}, {3, 3}, {3, 3}, {3, 3},  /* order */\n   {2, 2}, {1, 1}                   /* logical (and/or) */\n};\n\n#define UNARY_PRIORITY\t8  /* priority for unary operators */\n\n\n/*\n** subexpr -> (simpleexp | unop subexpr) { binop subexpr }\n** where `binop' is any binary operator with a priority higher than `limit'\n*/\nstatic BinOpr subexpr (LexState *ls, expdesc *v, unsigned int limit) {\n  BinOpr op;\n  UnOpr uop;\n  enterlevel(ls);\n  uop = getunopr(ls->t.token);\n  if (uop != OPR_NOUNOPR) {\n    luaX_next(ls);\n    subexpr(ls, v, UNARY_PRIORITY);\n    luaK_prefix(ls->fs, uop, v);\n  }\n  else simpleexp(ls, v);\n  /* expand while operators have priorities higher than `limit' */\n  op = getbinopr(ls->t.token);\n  while (op != OPR_NOBINOPR && priority[op].left > limit) {\n    expdesc v2;\n    BinOpr nextop;\n    luaX_next(ls);\n    luaK_infix(ls->fs, op, v);\n    /* read sub-expression with higher priority */\n    nextop = subexpr(ls, &v2, priority[op].right);\n    luaK_posfix(ls->fs, op, v, &v2);\n    op = nextop;\n  }\n  leavelevel(ls);\n  return op;  /* return first untreated operator */\n}\n\n\nstatic void expr (LexState *ls, expdesc *v) {\n  subexpr(ls, v, 0);\n}\n\n/* }==================================================================== */\n\n\n\n/*\n** {======================================================================\n** Rules for Statements\n** =======================================================================\n*/\n\n\nstatic int block_follow (int token) {\n  switch (token) {\n    case TK_ELSE: case TK_ELSEIF: case TK_END:\n    case TK_UNTIL: case TK_EOS:\n      return 1;\n    default: return 0;\n  }\n}\n\n\nstatic void block (LexState *ls) {\n  /* block -> chunk */\n  FuncState *fs = ls->fs;\n  BlockCnt bl;\n  enterblock(fs, &bl, 0);\n  chunk(ls);\n  lua_assert(bl.breaklist == NO_JUMP);\n  leaveblock(fs);\n}\n\n\n/*\n** structure to chain all variables in the left-hand side of an\n** assignment\n*/\nstruct LHS_assign {\n  struct LHS_assign *prev;\n  expdesc v;  /* variable (global, local, upvalue, or indexed) */\n};\n\n\n/*\n** check whether, in an assignment to a local variable, the local variable\n** is needed in a previous assignment (to a table). If so, save original\n** local value in a safe place and use this safe copy in the previous\n** assignment.\n*/\nstatic void check_conflict (LexState *ls, struct LHS_assign *lh, expdesc *v) {\n  FuncState *fs = ls->fs;\n  int extra = fs->freereg;  /* eventual position to save local variable */\n  int conflict = 0;\n  for (; lh; lh = lh->prev) {\n    if (lh->v.k == VINDEXED) {\n      if (lh->v.u.s.info == v->u.s.info) {  /* conflict? */\n        conflict = 1;\n        lh->v.u.s.info = extra;  /* previous assignment will use safe copy */\n      }\n      if (lh->v.u.s.aux == v->u.s.info) {  /* conflict? */\n        conflict = 1;\n        lh->v.u.s.aux = extra;  /* previous assignment will use safe copy */\n      }\n    }\n  }\n  if (conflict) {\n    luaK_codeABC(fs, OP_MOVE, fs->freereg, v->u.s.info, 0);  /* make copy */\n    luaK_reserveregs(fs, 1);\n  }\n}\n\n\nstatic void assignment (LexState *ls, struct LHS_assign *lh, int nvars) {\n  expdesc e;\n  check_condition(ls, VLOCAL <= lh->v.k && lh->v.k <= VINDEXED,\n                      \"syntax error\");\n  if (testnext(ls, ',')) {  /* assignment -> `,' primaryexp assignment */\n    struct LHS_assign nv;\n    nv.prev = lh;\n    primaryexp(ls, &nv.v);\n    if (nv.v.k == VLOCAL)\n      check_conflict(ls, lh, &nv.v);\n    luaY_checklimit(ls->fs, nvars, LUAI_MAXCCALLS - ls->L->nCcalls,\n                    \"variables in assignment\");\n    assignment(ls, &nv, nvars+1);\n  }\n  else {  /* assignment -> `=' explist1 */\n    int nexps;\n    checknext(ls, '=');\n    nexps = explist1(ls, &e);\n    if (nexps != nvars) {\n      adjust_assign(ls, nvars, nexps, &e);\n      if (nexps > nvars)\n        ls->fs->freereg -= nexps - nvars;  /* remove extra values */\n    }\n    else {\n      luaK_setoneret(ls->fs, &e);  /* close last expression */\n      luaK_storevar(ls->fs, &lh->v, &e);\n      return;  /* avoid default */\n    }\n  }\n  init_exp(&e, VNONRELOC, ls->fs->freereg-1);  /* default assignment */\n  luaK_storevar(ls->fs, &lh->v, &e);\n}\n\n\nstatic int cond (LexState *ls) {\n  /* cond -> exp */\n  expdesc v;\n  expr(ls, &v);  /* read condition */\n  if (v.k == VNIL) v.k = VFALSE;  /* `falses' are all equal here */\n  luaK_goiftrue(ls->fs, &v);\n  return v.f;\n}\n\n\nstatic void breakstat (LexState *ls) {\n  FuncState *fs = ls->fs;\n  BlockCnt *bl = fs->bl;\n  int upval = 0;\n  while (bl && !bl->isbreakable) {\n    upval |= bl->upval;\n    bl = bl->previous;\n  }\n  if (!bl)\n    luaX_syntaxerror(ls, \"no loop to break\");\n  if (upval)\n    luaK_codeABC(fs, OP_CLOSE, bl->nactvar, 0, 0);\n  luaK_concat(fs, &bl->breaklist, luaK_jump(fs));\n}\n\n\nstatic void whilestat (LexState *ls, int line) {\n  /* whilestat -> WHILE cond DO block END */\n  FuncState *fs = ls->fs;\n  int whileinit;\n  int condexit;\n  BlockCnt bl;\n  luaX_next(ls);  /* skip WHILE */\n  whileinit = luaK_getlabel(fs);\n  condexit = cond(ls);\n  enterblock(fs, &bl, 1);\n  checknext(ls, TK_DO);\n  block(ls);\n  luaK_patchlist(fs, luaK_jump(fs), whileinit);\n  check_match(ls, TK_END, TK_WHILE, line);\n  leaveblock(fs);\n  luaK_patchtohere(fs, condexit);  /* false conditions finish the loop */\n}\n\n\nstatic void repeatstat (LexState *ls, int line) {\n  /* repeatstat -> REPEAT block UNTIL cond */\n  int condexit;\n  FuncState *fs = ls->fs;\n  int repeat_init = luaK_getlabel(fs);\n  BlockCnt bl1, bl2;\n  enterblock(fs, &bl1, 1);  /* loop block */\n  enterblock(fs, &bl2, 0);  /* scope block */\n  luaX_next(ls);  /* skip REPEAT */\n  chunk(ls);\n  check_match(ls, TK_UNTIL, TK_REPEAT, line);\n  condexit = cond(ls);  /* read condition (inside scope block) */\n  if (!bl2.upval) {  /* no upvalues? */\n    leaveblock(fs);  /* finish scope */\n    luaK_patchlist(ls->fs, condexit, repeat_init);  /* close the loop */\n  }\n  else {  /* complete semantics when there are upvalues */\n    breakstat(ls);  /* if condition then break */\n    luaK_patchtohere(ls->fs, condexit);  /* else... */\n    leaveblock(fs);  /* finish scope... */\n    luaK_patchlist(ls->fs, luaK_jump(fs), repeat_init);  /* and repeat */\n  }\n  leaveblock(fs);  /* finish loop */\n}\n\n\nstatic int exp1 (LexState *ls) {\n  expdesc e;\n  int k;\n  expr(ls, &e);\n  k = e.k;\n  luaK_exp2nextreg(ls->fs, &e);\n  return k;\n}\n\n\nstatic void forbody (LexState *ls, int base, int line, int nvars, int isnum) {\n  /* forbody -> DO block */\n  BlockCnt bl;\n  FuncState *fs = ls->fs;\n  int prep, endfor;\n  adjustlocalvars(ls, 3);  /* control variables */\n  checknext(ls, TK_DO);\n  prep = isnum ? luaK_codeAsBx(fs, OP_FORPREP, base, NO_JUMP) : luaK_jump(fs);\n  enterblock(fs, &bl, 0);  /* scope for declared variables */\n  adjustlocalvars(ls, nvars);\n  luaK_reserveregs(fs, nvars);\n  block(ls);\n  leaveblock(fs);  /* end of scope for declared variables */\n  luaK_patchtohere(fs, prep);\n  endfor = (isnum) ? luaK_codeAsBx(fs, OP_FORLOOP, base, NO_JUMP) :\n                     luaK_codeABC(fs, OP_TFORLOOP, base, 0, nvars);\n  luaK_fixline(fs, line);  /* pretend that `OP_FOR' starts the loop */\n  luaK_patchlist(fs, (isnum ? endfor : luaK_jump(fs)), prep + 1);\n}\n\n\nstatic void fornum (LexState *ls, TString *varname, int line) {\n  /* fornum -> NAME = exp1,exp1[,exp1] forbody */\n  FuncState *fs = ls->fs;\n  int base = fs->freereg;\n  new_localvarliteral(ls, \"(for index)\", 0);\n  new_localvarliteral(ls, \"(for limit)\", 1);\n  new_localvarliteral(ls, \"(for step)\", 2);\n  new_localvar(ls, varname, 3);\n  checknext(ls, '=');\n  exp1(ls);  /* initial value */\n  checknext(ls, ',');\n  exp1(ls);  /* limit */\n  if (testnext(ls, ','))\n    exp1(ls);  /* optional step */\n  else {  /* default step = 1 */\n    luaK_codeABx(fs, OP_LOADK, fs->freereg, luaK_numberK(fs, 1));\n    luaK_reserveregs(fs, 1);\n  }\n  forbody(ls, base, line, 1, 1);\n}\n\n\nstatic void forlist (LexState *ls, TString *indexname) {\n  /* forlist -> NAME {,NAME} IN explist1 forbody */\n  FuncState *fs = ls->fs;\n  expdesc e;\n  int nvars = 0;\n  int line;\n  int base = fs->freereg;\n  /* create control variables */\n  new_localvarliteral(ls, \"(for generator)\", nvars++);\n  new_localvarliteral(ls, \"(for state)\", nvars++);\n  new_localvarliteral(ls, \"(for control)\", nvars++);\n  /* create declared variables */\n  new_localvar(ls, indexname, nvars++);\n  while (testnext(ls, ','))\n    new_localvar(ls, str_checkname(ls), nvars++);\n  checknext(ls, TK_IN);\n  line = ls->linenumber;\n  adjust_assign(ls, 3, explist1(ls, &e), &e);\n  luaK_checkstack(fs, 3);  /* extra space to call generator */\n  forbody(ls, base, line, nvars - 3, 0);\n}\n\n\nstatic void forstat (LexState *ls, int line) {\n  /* forstat -> FOR (fornum | forlist) END */\n  FuncState *fs = ls->fs;\n  TString *varname;\n  BlockCnt bl;\n  enterblock(fs, &bl, 1);  /* scope for loop and control variables */\n  luaX_next(ls);  /* skip `for' */\n  varname = str_checkname(ls);  /* first variable name */\n  switch (ls->t.token) {\n    case '=': fornum(ls, varname, line); break;\n    case ',': case TK_IN: forlist(ls, varname); break;\n    default: luaX_syntaxerror(ls, LUA_QL(\"=\") \" or \" LUA_QL(\"in\") \" expected\");\n  }\n  check_match(ls, TK_END, TK_FOR, line);\n  leaveblock(fs);  /* loop scope (`break' jumps to this point) */\n}\n\n\nstatic int test_then_block (LexState *ls) {\n  /* test_then_block -> [IF | ELSEIF] cond THEN block */\n  int condexit;\n  luaX_next(ls);  /* skip IF or ELSEIF */\n  condexit = cond(ls);\n  checknext(ls, TK_THEN);\n  block(ls);  /* `then' part */\n  return condexit;\n}\n\n\nstatic void ifstat (LexState *ls, int line) {\n  /* ifstat -> IF cond THEN block {ELSEIF cond THEN block} [ELSE block] END */\n  FuncState *fs = ls->fs;\n  int flist;\n  int escapelist = NO_JUMP;\n  flist = test_then_block(ls);  /* IF cond THEN block */\n  while (ls->t.token == TK_ELSEIF) {\n    luaK_concat(fs, &escapelist, luaK_jump(fs));\n    luaK_patchtohere(fs, flist);\n    flist = test_then_block(ls);  /* ELSEIF cond THEN block */\n  }\n  if (ls->t.token == TK_ELSE) {\n    luaK_concat(fs, &escapelist, luaK_jump(fs));\n    luaK_patchtohere(fs, flist);\n    luaX_next(ls);  /* skip ELSE (after patch, for correct line info) */\n    block(ls);  /* `else' part */\n  }\n  else\n    luaK_concat(fs, &escapelist, flist);\n  luaK_patchtohere(fs, escapelist);\n  check_match(ls, TK_END, TK_IF, line);\n}\n\n\nstatic void localfunc (LexState *ls) {\n  expdesc v, b;\n  FuncState *fs = ls->fs;\n  new_localvar(ls, str_checkname(ls), 0);\n  init_exp(&v, VLOCAL, fs->freereg);\n  luaK_reserveregs(fs, 1);\n  adjustlocalvars(ls, 1);\n  body(ls, &b, 0, ls->linenumber);\n  luaK_storevar(fs, &v, &b);\n  /* debug information will only see the variable after this point! */\n  getlocvar(fs, fs->nactvar - 1).startpc = fs->pc;\n}\n\n\nstatic void localstat (LexState *ls) {\n  /* stat -> LOCAL NAME {`,' NAME} [`=' explist1] */\n  int nvars = 0;\n  int nexps;\n  expdesc e;\n  do {\n    new_localvar(ls, str_checkname(ls), nvars++);\n  } while (testnext(ls, ','));\n  if (testnext(ls, '='))\n    nexps = explist1(ls, &e);\n  else {\n    e.k = VVOID;\n    nexps = 0;\n  }\n  adjust_assign(ls, nvars, nexps, &e);\n  adjustlocalvars(ls, nvars);\n}\n\n\nstatic int funcname (LexState *ls, expdesc *v) {\n  /* funcname -> NAME {field} [`:' NAME] */\n  int needself = 0;\n  singlevar(ls, v);\n  while (ls->t.token == '.')\n    field(ls, v);\n  if (ls->t.token == ':') {\n    needself = 1;\n    field(ls, v);\n  }\n  return needself;\n}\n\n\nstatic void funcstat (LexState *ls, int line) {\n  /* funcstat -> FUNCTION funcname body */\n  int needself;\n  expdesc v, b;\n  luaX_next(ls);  /* skip FUNCTION */\n  needself = funcname(ls, &v);\n  body(ls, &b, needself, line);\n  luaK_storevar(ls->fs, &v, &b);\n  luaK_fixline(ls->fs, line);  /* definition `happens' in the first line */\n}\n\n\nstatic void exprstat (LexState *ls) {\n  /* stat -> func | assignment */\n  FuncState *fs = ls->fs;\n  struct LHS_assign v;\n  primaryexp(ls, &v.v);\n  if (v.v.k == VCALL)  /* stat -> func */\n    SETARG_C(getcode(fs, &v.v), 1);  /* call statement uses no results */\n  else {  /* stat -> assignment */\n    v.prev = NULL;\n    assignment(ls, &v, 1);\n  }\n}\n\n\nstatic void retstat (LexState *ls) {\n  /* stat -> RETURN explist */\n  FuncState *fs = ls->fs;\n  expdesc e;\n  int first, nret;  /* registers with returned values */\n  luaX_next(ls);  /* skip RETURN */\n  if (block_follow(ls->t.token) || ls->t.token == ';')\n    first = nret = 0;  /* return no values */\n  else {\n    nret = explist1(ls, &e);  /* optional return values */\n    if (hasmultret(e.k)) {\n      luaK_setmultret(fs, &e);\n      if (e.k == VCALL && nret == 1) {  /* tail call? */\n        SET_OPCODE(getcode(fs,&e), OP_TAILCALL);\n        lua_assert(GETARG_A(getcode(fs,&e)) == fs->nactvar);\n      }\n      first = fs->nactvar;\n      nret = LUA_MULTRET;  /* return all values */\n    }\n    else {\n      if (nret == 1)  /* only one single value? */\n        first = luaK_exp2anyreg(fs, &e);\n      else {\n        luaK_exp2nextreg(fs, &e);  /* values must go to the `stack' */\n        first = fs->nactvar;  /* return all `active' values */\n        lua_assert(nret == fs->freereg - first);\n      }\n    }\n  }\n  luaK_ret(fs, first, nret);\n}\n\n\nstatic int statement (LexState *ls) {\n  int line = ls->linenumber;  /* may be needed for error messages */\n  switch (ls->t.token) {\n    case TK_IF: {  /* stat -> ifstat */\n      ifstat(ls, line);\n      return 0;\n    }\n    case TK_WHILE: {  /* stat -> whilestat */\n      whilestat(ls, line);\n      return 0;\n    }\n    case TK_DO: {  /* stat -> DO block END */\n      luaX_next(ls);  /* skip DO */\n      block(ls);\n      check_match(ls, TK_END, TK_DO, line);\n      return 0;\n    }\n    case TK_FOR: {  /* stat -> forstat */\n      forstat(ls, line);\n      return 0;\n    }\n    case TK_REPEAT: {  /* stat -> repeatstat */\n      repeatstat(ls, line);\n      return 0;\n    }\n    case TK_FUNCTION: {\n      funcstat(ls, line);  /* stat -> funcstat */\n      return 0;\n    }\n    case TK_LOCAL: {  /* stat -> localstat */\n      luaX_next(ls);  /* skip LOCAL */\n      if (testnext(ls, TK_FUNCTION))  /* local function? */\n        localfunc(ls);\n      else\n        localstat(ls);\n      return 0;\n    }\n    case TK_RETURN: {  /* stat -> retstat */\n      retstat(ls);\n      return 1;  /* must be last statement */\n    }\n    case TK_BREAK: {  /* stat -> breakstat */\n      luaX_next(ls);  /* skip BREAK */\n      breakstat(ls);\n      return 1;  /* must be last statement */\n    }\n    default: {\n      exprstat(ls);\n      return 0;  /* to avoid warnings */\n    }\n  }\n}\n\n\nstatic void chunk (LexState *ls) {\n  /* chunk -> { stat [`;'] } */\n  int islast = 0;\n  enterlevel(ls);\n  while (!islast && !block_follow(ls->t.token)) {\n    islast = statement(ls);\n    testnext(ls, ';');\n    lua_assert(ls->fs->f->maxstacksize >= ls->fs->freereg &&\n               ls->fs->freereg >= ls->fs->nactvar);\n    ls->fs->freereg = ls->fs->nactvar;  /* free registers */\n  }\n  leavelevel(ls);\n}\n\n/* }====================================================================== */\n"
  },
  {
    "path": "src/lparser.h",
    "content": "/*\n** $Id: lparser.h,v 1.57.1.1 2007/12/27 13:02:25 roberto Exp $\n** Lua Parser\n** See Copyright Notice in lua.h\n*/\n\n#ifndef lparser_h\n#define lparser_h\n\n#include \"llimits.h\"\n#include \"lobject.h\"\n#include \"lzio.h\"\n\n\n/*\n** Expression descriptor\n*/\n\ntypedef enum {\n  VVOID,\t/* no value */\n  VNIL,\n  VTRUE,\n  VFALSE,\n  VK,\t\t/* info = index of constant in `k' */\n  VKNUM,\t/* nval = numerical value */\n  VLOCAL,\t/* info = local register */\n  VUPVAL,       /* info = index of upvalue in `upvalues' */\n  VGLOBAL,\t/* info = index of table; aux = index of global name in `k' */\n  VINDEXED,\t/* info = table register; aux = index register (or `k') */\n  VJMP,\t\t/* info = instruction pc */\n  VRELOCABLE,\t/* info = instruction pc */\n  VNONRELOC,\t/* info = result register */\n  VCALL,\t/* info = instruction pc */\n  VVARARG\t/* info = instruction pc */\n} expkind;\n\ntypedef struct expdesc {\n  expkind k;\n  union {\n    struct { int info, aux; } s;\n    lua_Number nval;\n  } u;\n  int t;  /* patch list of `exit when true' */\n  int f;  /* patch list of `exit when false' */\n} expdesc;\n\n\ntypedef struct upvaldesc {\n  lu_byte k;\n  lu_byte info;\n} upvaldesc;\n\n\nstruct BlockCnt;  /* defined in lparser.c */\n\n\n/* state needed to generate code for a given function */\ntypedef struct FuncState {\n  Proto *f;  /* current function header */\n  Table *h;  /* table to find (and reuse) elements in `k' */\n  struct FuncState *prev;  /* enclosing function */\n  struct LexState *ls;  /* lexical state */\n  struct lua_State *L;  /* copy of the Lua state */\n  struct BlockCnt *bl;  /* chain of current blocks */\n  int pc;  /* next position to code (equivalent to `ncode') */\n  int lasttarget;   /* `pc' of last `jump target' */\n  int jpc;  /* list of pending jumps to `pc' */\n  int freereg;  /* first free register */\n  int nk;  /* number of elements in `k' */\n  int np;  /* number of elements in `p' */\n  short nlocvars;  /* number of elements in `locvars' */\n  lu_byte nactvar;  /* number of active local variables */\n  upvaldesc upvalues[LUAI_MAXUPVALUES];  /* upvalues */\n  unsigned short actvar[LUAI_MAXVARS];  /* declared-variable stack */\n} FuncState;\n\n\nLUAI_FUNC Proto *luaY_parser (lua_State *L, ZIO *z, Mbuffer *buff,\n                                            const char *name);\n\n\n#endif\n"
  },
  {
    "path": "src/lstate.c",
    "content": "/*\n** $Id: lstate.c,v 2.36.1.2 2008/01/03 15:20:39 roberto Exp $\n** Global State\n** See Copyright Notice in lua.h\n*/\n\n\n#include <stddef.h>\n\n#define lstate_c\n#define LUA_CORE\n\n#include \"lua.h\"\n\n#include \"ldebug.h\"\n#include \"ldo.h\"\n#include \"lfunc.h\"\n#include \"lgc.h\"\n#include \"llex.h\"\n#include \"lmem.h\"\n#include \"lstate.h\"\n#include \"lstring.h\"\n#include \"ltable.h\"\n#include \"ltm.h\"\n\n\n/*\n** Main thread combines a thread state and the global state\n*/\ntypedef struct LG {\n  lua_State l;\n  global_State g;\n} LG;\n  \n\n\nstatic void stack_init (lua_State *L1, lua_State *L) {\n  /* initialize CallInfo array */\n  L1->base_ci = luaM_newvector(L, BASIC_CI_SIZE, CallInfo);\n  L1->ci = L1->base_ci;\n  L1->size_ci = BASIC_CI_SIZE;\n  L1->end_ci = L1->base_ci + L1->size_ci - 1;\n  /* initialize stack array */\n  L1->stack = luaM_newvector(L, BASIC_STACK_SIZE + EXTRA_STACK, TValue);\n  L1->stacksize = BASIC_STACK_SIZE + EXTRA_STACK;\n  L1->top = L1->stack;\n  L1->stack_last = L1->stack+(L1->stacksize - EXTRA_STACK)-1;\n  /* initialize first ci */\n  L1->ci->func = L1->top;\n  setnilvalue(L1->top++);  /* `function' entry for this `ci' */\n  L1->base = L1->ci->base = L1->top;\n  L1->ci->top = L1->top + LUA_MINSTACK;\n}\n\n\nstatic void freestack (lua_State *L, lua_State *L1) {\n  luaM_freearray(L, L1->base_ci, L1->size_ci, CallInfo);\n  luaM_freearray(L, L1->stack, L1->stacksize, TValue);\n}\n\n\n/*\n** open parts that may cause memory-allocation errors\n*/\nstatic void f_luaopen (lua_State *L, void *ud) {\n  global_State *g = G(L);\n  UNUSED(ud);\n  stack_init(L, L);  /* init stack */\n  sethvalue(L, gt(L), luaH_new(L, 0, 2));  /* table of globals */\n  sethvalue(L, registry(L), luaH_new(L, 0, 2));  /* registry */\n  luaS_resize(L, MINSTRTABSIZE);  /* initial size of string table */\n  luaT_init(L);\n  luaX_init(L);\n  luaS_fix(luaS_newliteral(L, MEMERRMSG));\n  g->GCthreshold = 4*g->totalbytes;\n}\n\n\nstatic void preinit_state (lua_State *L, global_State *g) {\n  G(L) = g;\n  L->stack = NULL;\n  L->stacksize = 0;\n  L->errorJmp = NULL;\n  L->hook = NULL;\n  L->hookmask = 0;\n  L->basehookcount = 0;\n  L->allowhook = 1;\n  resethookcount(L);\n  L->openupval = NULL;\n  L->size_ci = 0;\n  L->nCcalls = L->baseCcalls = 0;\n  L->status = 0;\n  L->base_ci = L->ci = NULL;\n  L->savedpc = NULL;\n  L->errfunc = 0;\n  setnilvalue(gt(L));\n}\n\n\nstatic void close_state (lua_State *L) {\n  global_State *g = G(L);\n  luaF_close(L, L->stack);  /* close all upvalues for this thread */\n  luaC_freeall(L);  /* collect all objects */\n  lua_assert(g->rootgc == obj2gco(L));\n  lua_assert(g->strt.nuse == 0);\n  luaM_freearray(L, G(L)->strt.hash, G(L)->strt.size, TString *);\n  luaZ_freebuffer(L, &g->buff);\n  freestack(L, L);\n  lua_assert(g->totalbytes == sizeof(LG));\n  (*g->frealloc)(g->ud, cast(lu_byte*, L), sizeof(LG), 0);\n}\n\n\nlua_State *luaE_newthread (lua_State *L) {\n  lua_State *L1 = cast(lua_State*, luaM_malloc(L, sizeof(lua_State)));\n  luaC_link(L, obj2gco(L1), LUA_TTHREAD);\n  preinit_state(L1, G(L));\n  stack_init(L1, L);  /* init stack */\n  setobj2n(L, gt(L1), gt(L));  /* share table of globals */\n  L1->hookmask = L->hookmask;\n  L1->basehookcount = L->basehookcount;\n  L1->hook = L->hook;\n  resethookcount(L1);\n  lua_assert(iswhite(obj2gco(L1)));\n  return L1;\n}\n\n\nvoid luaE_freethread (lua_State *L, lua_State *L1) {\n  luaF_close(L1, L1->stack);  /* close all upvalues for this thread */\n  lua_assert(L1->openupval == NULL);\n  freestack(L, L1);\n  luaM_freemem(L, cast(lu_byte*, L1), sizeof(lua_State));\n}\n\n\nLUA_API lua_State *lua_newstate (lua_Alloc f, void *ud) {\n  int i;\n  lua_State *L;\n  global_State *g;\n  void *l = (*f)(ud, NULL, 0, sizeof(LG));\n  if (l == NULL) return NULL;\n  L = cast(lua_State*, l);\n  g = &cast(LG *, l)->g;\n  L->next = NULL;\n  L->tt = LUA_TTHREAD;\n  g->currentwhite = bit2mask(WHITE0BIT, FIXEDBIT);\n  L->marked = luaC_white(g);\n  set2bits(L->marked, FIXEDBIT, SFIXEDBIT);\n  preinit_state(L, g);\n  g->frealloc = f;\n  g->ud = ud;\n  g->mainthread = L;\n  g->uvhead.u.l.prev = &g->uvhead;\n  g->uvhead.u.l.next = &g->uvhead;\n  g->GCthreshold = 0;  /* mark it as unfinished state */\n  g->strt.size = 0;\n  g->strt.nuse = 0;\n  g->strt.hash = NULL;\n  setnilvalue(registry(L));\n  luaZ_initbuffer(L, &g->buff);\n  g->panic = NULL;\n  g->gcstate = GCSpause;\n  g->rootgc = obj2gco(L);\n  g->sweepstrgc = 0;\n  g->sweepgc = &g->rootgc;\n  g->gray = NULL;\n  g->grayagain = NULL;\n  g->weak = NULL;\n  g->tmudata = NULL;\n  g->totalbytes = sizeof(LG);\n  g->gcpause = LUAI_GCPAUSE;\n  g->gcstepmul = LUAI_GCMUL;\n  g->gcdept = 0;\n  for (i=0; i<NUM_TAGS; i++) g->mt[i] = NULL;\n  if (luaD_rawrunprotected(L, f_luaopen, NULL) != 0) {\n    /* memory allocation error: free partial state */\n    close_state(L);\n    L = NULL;\n  }\n  return L;\n}\n\n\nstatic void callallgcTM (lua_State *L, void *ud) {\n  UNUSED(ud);\n  luaC_callGCTM(L);  /* call GC metamethods for all udata */\n}\n\n\nLUA_API void lua_close (lua_State *L) {\n  L = G(L)->mainthread;  /* only the main thread can be closed */\n  lua_lock(L);\n  luaF_close(L, L->stack);  /* close all upvalues for this thread */\n  luaC_separateudata(L, 1);  /* separate udata that have GC metamethods */\n  L->errfunc = 0;  /* no error function during GC metamethods */\n  do {  /* repeat until no more errors */\n    L->ci = L->base_ci;\n    L->base = L->top = L->ci->base;\n    L->nCcalls = L->baseCcalls = 0;\n  } while (luaD_rawrunprotected(L, callallgcTM, NULL) != 0);\n  lua_assert(G(L)->tmudata == NULL);\n  close_state(L);\n}\n\n"
  },
  {
    "path": "src/lstate.h",
    "content": "/*\n** $Id: lstate.h,v 2.24.1.2 2008/01/03 15:20:39 roberto Exp $\n** Global State\n** See Copyright Notice in lua.h\n*/\n\n#ifndef lstate_h\n#define lstate_h\n\n#include \"lua.h\"\n\n#include \"lobject.h\"\n#include \"ltm.h\"\n#include \"lzio.h\"\n\n\n\nstruct lua_longjmp;  /* defined in ldo.c */\n\n\n/* table of globals */\n#define gt(L)\t(&L->l_gt)\n\n/* registry */\n#define registry(L)\t(&G(L)->l_registry)\n\n\n/* extra stack space to handle TM calls and some other extras */\n#define EXTRA_STACK   5\n\n\n#define BASIC_CI_SIZE           8\n\n#define BASIC_STACK_SIZE        (2*LUA_MINSTACK)\n\n\n\ntypedef struct stringtable {\n  GCObject **hash;\n  lu_int32 nuse;  /* number of elements */\n  int size;\n} stringtable;\n\n\n/*\n** informations about a call\n*/\ntypedef struct CallInfo {\n  StkId base;  /* base for this function */\n  StkId func;  /* function index in the stack */\n  StkId\ttop;  /* top for this function */\n  const Instruction *savedpc;\n  int nresults;  /* expected number of results from this function */\n  int tailcalls;  /* number of tail calls lost under this entry */\n} CallInfo;\n\n\n\n#define curr_func(L)\t(clvalue(L->ci->func))\n#define ci_func(ci)\t(clvalue((ci)->func))\n#define f_isLua(ci)\t(!ci_func(ci)->c.isC)\n#define isLua(ci)\t(ttisfunction((ci)->func) && f_isLua(ci))\n\n\n/*\n** `global state', shared by all threads of this state\n*/\ntypedef struct global_State {\n  stringtable strt;  /* hash table for strings */\n  lua_Alloc frealloc;  /* function to reallocate memory */\n  void *ud;         /* auxiliary data to `frealloc' */\n  lu_byte currentwhite;\n  lu_byte gcstate;  /* state of garbage collector */\n  int sweepstrgc;  /* position of sweep in `strt' */\n  GCObject *rootgc;  /* list of all collectable objects */\n  GCObject **sweepgc;  /* position of sweep in `rootgc' */\n  GCObject *gray;  /* list of gray objects */\n  GCObject *grayagain;  /* list of objects to be traversed atomically */\n  GCObject *weak;  /* list of weak tables (to be cleared) */\n  GCObject *tmudata;  /* last element of list of userdata to be GC */\n  Mbuffer buff;  /* temporary buffer for string concatentation */\n  lu_mem GCthreshold;\n  lu_mem totalbytes;  /* number of bytes currently allocated */\n  lu_mem estimate;  /* an estimate of number of bytes actually in use */\n  lu_mem gcdept;  /* how much GC is `behind schedule' */\n  int gcpause;  /* size of pause between successive GCs */\n  int gcstepmul;  /* GC `granularity' */\n  lua_CFunction panic;  /* to be called in unprotected errors */\n  TValue l_registry;\n  struct lua_State *mainthread;\n  UpVal uvhead;  /* head of double-linked list of all open upvalues */\n  struct Table *mt[NUM_TAGS];  /* metatables for basic types */\n  TString *tmname[TM_N];  /* array with tag-method names */\n} global_State;\n\n\n/*\n** `per thread' state\n*/\nstruct lua_State {\n  CommonHeader;\n  lu_byte status;\n  StkId top;  /* first free slot in the stack */\n  StkId base;  /* base of current function */\n  global_State *l_G;\n  CallInfo *ci;  /* call info for current function */\n  const Instruction *savedpc;  /* `savedpc' of current function */\n  StkId stack_last;  /* last free slot in the stack */\n  StkId stack;  /* stack base */\n  CallInfo *end_ci;  /* points after end of ci array*/\n  CallInfo *base_ci;  /* array of CallInfo's */\n  int stacksize;\n  int size_ci;  /* size of array `base_ci' */\n  unsigned short nCcalls;  /* number of nested C calls */\n  unsigned short baseCcalls;  /* nested C calls when resuming coroutine */\n  lu_byte hookmask;\n  lu_byte allowhook;\n  int basehookcount;\n  int hookcount;\n  lua_Hook hook;\n  TValue l_gt;  /* table of globals */\n  TValue env;  /* temporary place for environments */\n  GCObject *openupval;  /* list of open upvalues in this stack */\n  GCObject *gclist;\n  struct lua_longjmp *errorJmp;  /* current error recover point */\n  ptrdiff_t errfunc;  /* current error handling function (stack index) */\n};\n\n\n#define G(L)\t(L->l_G)\n\n\n/*\n** Union of all collectable objects\n*/\nunion GCObject {\n  GCheader gch;\n  union TString ts;\n  union Udata u;\n  union Closure cl;\n  struct Table h;\n  struct Proto p;\n  struct UpVal uv;\n  struct lua_State th;  /* thread */\n};\n\n\n/* macros to convert a GCObject into a specific value */\n#define rawgco2ts(o)\tcheck_exp((o)->gch.tt == LUA_TSTRING, &((o)->ts))\n#define gco2ts(o)\t(&rawgco2ts(o)->tsv)\n#define rawgco2u(o)\tcheck_exp((o)->gch.tt == LUA_TUSERDATA, &((o)->u))\n#define gco2u(o)\t(&rawgco2u(o)->uv)\n#define gco2cl(o)\tcheck_exp((o)->gch.tt == LUA_TFUNCTION, &((o)->cl))\n#define gco2h(o)\tcheck_exp((o)->gch.tt == LUA_TTABLE, &((o)->h))\n#define gco2p(o)\tcheck_exp((o)->gch.tt == LUA_TPROTO, &((o)->p))\n#define gco2uv(o)\tcheck_exp((o)->gch.tt == LUA_TUPVAL, &((o)->uv))\n#define ngcotouv(o) \\\n\tcheck_exp((o) == NULL || (o)->gch.tt == LUA_TUPVAL, &((o)->uv))\n#define gco2th(o)\tcheck_exp((o)->gch.tt == LUA_TTHREAD, &((o)->th))\n\n/* macro to convert any Lua object into a GCObject */\n#define obj2gco(v)\t(cast(GCObject *, (v)))\n\n\nLUAI_FUNC lua_State *luaE_newthread (lua_State *L);\nLUAI_FUNC void luaE_freethread (lua_State *L, lua_State *L1);\n\n#endif\n\n"
  },
  {
    "path": "src/lstring.c",
    "content": "/*\n** $Id: lstring.c,v 2.8.1.1 2007/12/27 13:02:25 roberto Exp $\n** String table (keeps all strings handled by Lua)\n** See Copyright Notice in lua.h\n*/\n\n\n#include <string.h>\n\n#define lstring_c\n#define LUA_CORE\n\n#include \"lua.h\"\n\n#include \"lmem.h\"\n#include \"lobject.h\"\n#include \"lstate.h\"\n#include \"lstring.h\"\n\n\n\nvoid luaS_resize (lua_State *L, int newsize) {\n  GCObject **newhash;\n  stringtable *tb;\n  int i;\n  if (G(L)->gcstate == GCSsweepstring)\n    return;  /* cannot resize during GC traverse */\n  newhash = luaM_newvector(L, newsize, GCObject *);\n  tb = &G(L)->strt;\n  for (i=0; i<newsize; i++) newhash[i] = NULL;\n  /* rehash */\n  for (i=0; i<tb->size; i++) {\n    GCObject *p = tb->hash[i];\n    while (p) {  /* for each node in the list */\n      GCObject *next = p->gch.next;  /* save next */\n      unsigned int h = gco2ts(p)->hash;\n      int h1 = lmod(h, newsize);  /* new position */\n      lua_assert(cast_int(h%newsize) == lmod(h, newsize));\n      p->gch.next = newhash[h1];  /* chain it */\n      newhash[h1] = p;\n      p = next;\n    }\n  }\n  luaM_freearray(L, tb->hash, tb->size, TString *);\n  tb->size = newsize;\n  tb->hash = newhash;\n}\n\n\nstatic TString *newlstr (lua_State *L, const char *str, size_t l,\n                                       unsigned int h) {\n  TString *ts;\n  stringtable *tb;\n  if (l+1 > (MAX_SIZET - sizeof(TString))/sizeof(char))\n    luaM_toobig(L);\n  ts = cast(TString *, luaM_malloc(L, (l+1)*sizeof(char)+sizeof(TString)));\n  ts->tsv.len = l;\n  ts->tsv.hash = h;\n  ts->tsv.marked = luaC_white(G(L));\n  ts->tsv.tt = LUA_TSTRING;\n  ts->tsv.reserved = 0;\n  memcpy(ts+1, str, l*sizeof(char));\n  ((char *)(ts+1))[l] = '\\0';  /* ending 0 */\n  tb = &G(L)->strt;\n  h = lmod(h, tb->size);\n  ts->tsv.next = tb->hash[h];  /* chain new entry */\n  tb->hash[h] = obj2gco(ts);\n  tb->nuse++;\n  if (tb->nuse > cast(lu_int32, tb->size) && tb->size <= MAX_INT/2)\n    luaS_resize(L, tb->size*2);  /* too crowded */\n  return ts;\n}\n\n\nTString *luaS_newlstr (lua_State *L, const char *str, size_t l) {\n  GCObject *o;\n  unsigned int h = cast(unsigned int, l);  /* seed */\n  size_t step = (l>>5)+1;  /* if string is too long, don't hash all its chars */\n  size_t l1;\n  for (l1=l; l1>=step; l1-=step)  /* compute hash */\n    h = h ^ ((h<<5)+(h>>2)+cast(unsigned char, str[l1-1]));\n  for (o = G(L)->strt.hash[lmod(h, G(L)->strt.size)];\n       o != NULL;\n       o = o->gch.next) {\n    TString *ts = rawgco2ts(o);\n    if (ts->tsv.len == l && (memcmp(str, getstr(ts), l) == 0)) {\n      /* string may be dead */\n      if (isdead(G(L), o)) changewhite(o);\n      return ts;\n    }\n  }\n  return newlstr(L, str, l, h);  /* not found */\n}\n\n\nUdata *luaS_newudata (lua_State *L, size_t s, Table *e) {\n  Udata *u;\n  if (s > MAX_SIZET - sizeof(Udata))\n    luaM_toobig(L);\n  u = cast(Udata *, luaM_malloc(L, s + sizeof(Udata)));\n  u->uv.marked = luaC_white(G(L));  /* is not finalized */\n  u->uv.tt = LUA_TUSERDATA;\n  u->uv.len = s;\n  u->uv.metatable = NULL;\n  u->uv.env = e;\n  /* chain it on udata list (after main thread) */\n  u->uv.next = G(L)->mainthread->next;\n  G(L)->mainthread->next = obj2gco(u);\n  return u;\n}\n\n"
  },
  {
    "path": "src/lstring.h",
    "content": "/*\n** $Id: lstring.h,v 1.43.1.1 2007/12/27 13:02:25 roberto Exp $\n** String table (keep all strings handled by Lua)\n** See Copyright Notice in lua.h\n*/\n\n#ifndef lstring_h\n#define lstring_h\n\n\n#include \"lgc.h\"\n#include \"lobject.h\"\n#include \"lstate.h\"\n\n\n#define sizestring(s)\t(sizeof(union TString)+((s)->len+1)*sizeof(char))\n\n#define sizeudata(u)\t(sizeof(union Udata)+(u)->len)\n\n#define luaS_new(L, s)\t(luaS_newlstr(L, s, strlen(s)))\n#define luaS_newliteral(L, s)\t(luaS_newlstr(L, \"\" s, \\\n                                 (sizeof(s)/sizeof(char))-1))\n\n#define luaS_fix(s)\tl_setbit((s)->tsv.marked, FIXEDBIT)\n\nLUAI_FUNC void luaS_resize (lua_State *L, int newsize);\nLUAI_FUNC Udata *luaS_newudata (lua_State *L, size_t s, Table *e);\nLUAI_FUNC TString *luaS_newlstr (lua_State *L, const char *str, size_t l);\n\n\n#endif\n"
  },
  {
    "path": "src/lstrlib.c",
    "content": "/*\n** $Id: lstrlib.c,v 1.132.1.5 2010/05/14 15:34:19 roberto Exp $\n** Standard library for string operations and pattern-matching\n** See Copyright Notice in lua.h\n*/\n\n\n#include <ctype.h>\n#include <stddef.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n\n#define lstrlib_c\n#define LUA_LIB\n\n#include \"lua.h\"\n\n#include \"lauxlib.h\"\n#include \"lualib.h\"\n\n\n/* macro to `unsign' a character */\n#define uchar(c)        ((unsigned char)(c))\n\n\n\nstatic int str_len (lua_State *L) {\n  size_t l;\n  luaL_checklstring(L, 1, &l);\n  lua_pushinteger(L, l);\n  return 1;\n}\n\n\nstatic ptrdiff_t posrelat (ptrdiff_t pos, size_t len) {\n  /* relative string position: negative means back from end */\n  if (pos < 0) pos += (ptrdiff_t)len + 1;\n  return (pos >= 0) ? pos : 0;\n}\n\n\nstatic int str_sub (lua_State *L) {\n  size_t l;\n  const char *s = luaL_checklstring(L, 1, &l);\n  ptrdiff_t start = posrelat(luaL_checkinteger(L, 2), l);\n  ptrdiff_t end = posrelat(luaL_optinteger(L, 3, -1), l);\n  if (start < 1) start = 1;\n  if (end > (ptrdiff_t)l) end = (ptrdiff_t)l;\n  if (start <= end)\n    lua_pushlstring(L, s+start-1, end-start+1);\n  else lua_pushliteral(L, \"\");\n  return 1;\n}\n\n\nstatic int str_reverse (lua_State *L) {\n  size_t l;\n  luaL_Buffer b;\n  const char *s = luaL_checklstring(L, 1, &l);\n  luaL_buffinit(L, &b);\n  while (l--) luaL_addchar(&b, s[l]);\n  luaL_pushresult(&b);\n  return 1;\n}\n\n\nstatic int str_lower (lua_State *L) {\n  size_t l;\n  size_t i;\n  luaL_Buffer b;\n  const char *s = luaL_checklstring(L, 1, &l);\n  luaL_buffinit(L, &b);\n  for (i=0; i<l; i++)\n    luaL_addchar(&b, tolower(uchar(s[i])));\n  luaL_pushresult(&b);\n  return 1;\n}\n\n\nstatic int str_upper (lua_State *L) {\n  size_t l;\n  size_t i;\n  luaL_Buffer b;\n  const char *s = luaL_checklstring(L, 1, &l);\n  luaL_buffinit(L, &b);\n  for (i=0; i<l; i++)\n    luaL_addchar(&b, toupper(uchar(s[i])));\n  luaL_pushresult(&b);\n  return 1;\n}\n\nstatic int str_rep (lua_State *L) {\n  size_t l;\n  luaL_Buffer b;\n  const char *s = luaL_checklstring(L, 1, &l);\n  int n = luaL_checkint(L, 2);\n  luaL_buffinit(L, &b);\n  while (n-- > 0)\n    luaL_addlstring(&b, s, l);\n  luaL_pushresult(&b);\n  return 1;\n}\n\n\nstatic int str_byte (lua_State *L) {\n  size_t l;\n  const char *s = luaL_checklstring(L, 1, &l);\n  ptrdiff_t posi = posrelat(luaL_optinteger(L, 2, 1), l);\n  ptrdiff_t pose = posrelat(luaL_optinteger(L, 3, posi), l);\n  int n, i;\n  if (posi <= 0) posi = 1;\n  if ((size_t)pose > l) pose = l;\n  if (posi > pose) return 0;  /* empty interval; return no values */\n  n = (int)(pose -  posi + 1);\n  if (posi + n <= pose)  /* overflow? */\n    luaL_error(L, \"string slice too long\");\n  luaL_checkstack(L, n, \"string slice too long\");\n  for (i=0; i<n; i++)\n    lua_pushinteger(L, uchar(s[posi+i-1]));\n  return n;\n}\n\n\nstatic int str_char (lua_State *L) {\n  int n = lua_gettop(L);  /* number of arguments */\n  int i;\n  luaL_Buffer b;\n  luaL_buffinit(L, &b);\n  for (i=1; i<=n; i++) {\n    int c = luaL_checkint(L, i);\n    luaL_argcheck(L, uchar(c) == c, i, \"invalid value\");\n    luaL_addchar(&b, uchar(c));\n  }\n  luaL_pushresult(&b);\n  return 1;\n}\n\n\n\n/*\n** {======================================================\n** PATTERN MATCHING\n** =======================================================\n*/\n\n\n#define CAP_UNFINISHED\t(-1)\n#define CAP_POSITION\t(-2)\n\ntypedef struct MatchState {\n  const char *src_init;  /* init of source string */\n  const char *src_end;  /* end (`\\0') of source string */\n  lua_State *L;\n  int level;  /* total number of captures (finished or unfinished) */\n  struct {\n    const char *init;\n    ptrdiff_t len;\n  } capture[LUA_MAXCAPTURES];\n} MatchState;\n\n\n#define L_ESC\t\t'%'\n#define SPECIALS\t\"^$*+?.([%-\"\n\n\nstatic int check_capture (MatchState *ms, int l) {\n  l -= '1';\n  if (l < 0 || l >= ms->level || ms->capture[l].len == CAP_UNFINISHED)\n    return luaL_error(ms->L, \"invalid capture index\");\n  return l;\n}\n\n\nstatic int capture_to_close (MatchState *ms) {\n  int level = ms->level;\n  for (level--; level>=0; level--)\n    if (ms->capture[level].len == CAP_UNFINISHED) return level;\n  return luaL_error(ms->L, \"invalid pattern capture\");\n}\n\n\nstatic const char *classend (MatchState *ms, const char *p) {\n  switch (*p++) {\n    case L_ESC: {\n      if (*p == '\\0')\n        luaL_error(ms->L, \"malformed pattern (ends with \" LUA_QL(\"%%\") \")\");\n      return p+1;\n    }\n    case '[': {\n      if (*p == '^') p++;\n      do {  /* look for a `]' */\n        if (*p == '\\0')\n          luaL_error(ms->L, \"malformed pattern (missing \" LUA_QL(\"]\") \")\");\n        if (*(p++) == L_ESC && *p != '\\0')\n          p++;  /* skip escapes (e.g. `%]') */\n      } while (*p != ']');\n      return p+1;\n    }\n    default: {\n      return p;\n    }\n  }\n}\n\n\nstatic int match_class (int c, int cl) {\n  int res;\n  switch (tolower(cl)) {\n    case 'a' : res = isalpha(c); break;\n    case 'c' : res = iscntrl(c); break;\n    case 'd' : res = isdigit(c); break;\n    case 'l' : res = islower(c); break;\n    case 'p' : res = ispunct(c); break;\n    case 's' : res = isspace(c); break;\n    case 'u' : res = isupper(c); break;\n    case 'w' : res = isalnum(c); break;\n    case 'x' : res = isxdigit(c); break;\n    case 'z' : res = (c == 0); break;\n    default: return (cl == c);\n  }\n  return (islower(cl) ? res : !res);\n}\n\n\nstatic int matchbracketclass (int c, const char *p, const char *ec) {\n  int sig = 1;\n  if (*(p+1) == '^') {\n    sig = 0;\n    p++;  /* skip the `^' */\n  }\n  while (++p < ec) {\n    if (*p == L_ESC) {\n      p++;\n      if (match_class(c, uchar(*p)))\n        return sig;\n    }\n    else if ((*(p+1) == '-') && (p+2 < ec)) {\n      p+=2;\n      if (uchar(*(p-2)) <= c && c <= uchar(*p))\n        return sig;\n    }\n    else if (uchar(*p) == c) return sig;\n  }\n  return !sig;\n}\n\n\nstatic int singlematch (int c, const char *p, const char *ep) {\n  switch (*p) {\n    case '.': return 1;  /* matches any char */\n    case L_ESC: return match_class(c, uchar(*(p+1)));\n    case '[': return matchbracketclass(c, p, ep-1);\n    default:  return (uchar(*p) == c);\n  }\n}\n\n\nstatic const char *match (MatchState *ms, const char *s, const char *p);\n\n\nstatic const char *matchbalance (MatchState *ms, const char *s,\n                                   const char *p) {\n  if (*p == 0 || *(p+1) == 0)\n    luaL_error(ms->L, \"unbalanced pattern\");\n  if (*s != *p) return NULL;\n  else {\n    int b = *p;\n    int e = *(p+1);\n    int cont = 1;\n    while (++s < ms->src_end) {\n      if (*s == e) {\n        if (--cont == 0) return s+1;\n      }\n      else if (*s == b) cont++;\n    }\n  }\n  return NULL;  /* string ends out of balance */\n}\n\n\nstatic const char *max_expand (MatchState *ms, const char *s,\n                                 const char *p, const char *ep) {\n  ptrdiff_t i = 0;  /* counts maximum expand for item */\n  while ((s+i)<ms->src_end && singlematch(uchar(*(s+i)), p, ep))\n    i++;\n  /* keeps trying to match with the maximum repetitions */\n  while (i>=0) {\n    const char *res = match(ms, (s+i), ep+1);\n    if (res) return res;\n    i--;  /* else didn't match; reduce 1 repetition to try again */\n  }\n  return NULL;\n}\n\n\nstatic const char *min_expand (MatchState *ms, const char *s,\n                                 const char *p, const char *ep) {\n  for (;;) {\n    const char *res = match(ms, s, ep+1);\n    if (res != NULL)\n      return res;\n    else if (s<ms->src_end && singlematch(uchar(*s), p, ep))\n      s++;  /* try with one more repetition */\n    else return NULL;\n  }\n}\n\n\nstatic const char *start_capture (MatchState *ms, const char *s,\n                                    const char *p, int what) {\n  const char *res;\n  int level = ms->level;\n  if (level >= LUA_MAXCAPTURES) luaL_error(ms->L, \"too many captures\");\n  ms->capture[level].init = s;\n  ms->capture[level].len = what;\n  ms->level = level+1;\n  if ((res=match(ms, s, p)) == NULL)  /* match failed? */\n    ms->level--;  /* undo capture */\n  return res;\n}\n\n\nstatic const char *end_capture (MatchState *ms, const char *s,\n                                  const char *p) {\n  int l = capture_to_close(ms);\n  const char *res;\n  ms->capture[l].len = s - ms->capture[l].init;  /* close capture */\n  if ((res = match(ms, s, p)) == NULL)  /* match failed? */\n    ms->capture[l].len = CAP_UNFINISHED;  /* undo capture */\n  return res;\n}\n\n\nstatic const char *match_capture (MatchState *ms, const char *s, int l) {\n  size_t len;\n  l = check_capture(ms, l);\n  len = ms->capture[l].len;\n  if ((size_t)(ms->src_end-s) >= len &&\n      memcmp(ms->capture[l].init, s, len) == 0)\n    return s+len;\n  else return NULL;\n}\n\n\nstatic const char *match (MatchState *ms, const char *s, const char *p) {\n  init: /* using goto's to optimize tail recursion */\n  switch (*p) {\n    case '(': {  /* start capture */\n      if (*(p+1) == ')')  /* position capture? */\n        return start_capture(ms, s, p+2, CAP_POSITION);\n      else\n        return start_capture(ms, s, p+1, CAP_UNFINISHED);\n    }\n    case ')': {  /* end capture */\n      return end_capture(ms, s, p+1);\n    }\n    case L_ESC: {\n      switch (*(p+1)) {\n        case 'b': {  /* balanced string? */\n          s = matchbalance(ms, s, p+2);\n          if (s == NULL) return NULL;\n          p+=4; goto init;  /* else return match(ms, s, p+4); */\n        }\n        case 'f': {  /* frontier? */\n          const char *ep; char previous;\n          p += 2;\n          if (*p != '[')\n            luaL_error(ms->L, \"missing \" LUA_QL(\"[\") \" after \"\n                               LUA_QL(\"%%f\") \" in pattern\");\n          ep = classend(ms, p);  /* points to what is next */\n          previous = (s == ms->src_init) ? '\\0' : *(s-1);\n          if (matchbracketclass(uchar(previous), p, ep-1) ||\n             !matchbracketclass(uchar(*s), p, ep-1)) return NULL;\n          p=ep; goto init;  /* else return match(ms, s, ep); */\n        }\n        default: {\n          if (isdigit(uchar(*(p+1)))) {  /* capture results (%0-%9)? */\n            s = match_capture(ms, s, uchar(*(p+1)));\n            if (s == NULL) return NULL;\n            p+=2; goto init;  /* else return match(ms, s, p+2) */\n          }\n          goto dflt;  /* case default */\n        }\n      }\n    }\n    case '\\0': {  /* end of pattern */\n      return s;  /* match succeeded */\n    }\n    case '$': {\n      if (*(p+1) == '\\0')  /* is the `$' the last char in pattern? */\n        return (s == ms->src_end) ? s : NULL;  /* check end of string */\n      else goto dflt;\n    }\n    default: dflt: {  /* it is a pattern item */\n      const char *ep = classend(ms, p);  /* points to what is next */\n      int m = s<ms->src_end && singlematch(uchar(*s), p, ep);\n      switch (*ep) {\n        case '?': {  /* optional */\n          const char *res;\n          if (m && ((res=match(ms, s+1, ep+1)) != NULL))\n            return res;\n          p=ep+1; goto init;  /* else return match(ms, s, ep+1); */\n        }\n        case '*': {  /* 0 or more repetitions */\n          return max_expand(ms, s, p, ep);\n        }\n        case '+': {  /* 1 or more repetitions */\n          return (m ? max_expand(ms, s+1, p, ep) : NULL);\n        }\n        case '-': {  /* 0 or more repetitions (minimum) */\n          return min_expand(ms, s, p, ep);\n        }\n        default: {\n          if (!m) return NULL;\n          s++; p=ep; goto init;  /* else return match(ms, s+1, ep); */\n        }\n      }\n    }\n  }\n}\n\n\n\nstatic const char *lmemfind (const char *s1, size_t l1,\n                               const char *s2, size_t l2) {\n  if (l2 == 0) return s1;  /* empty strings are everywhere */\n  else if (l2 > l1) return NULL;  /* avoids a negative `l1' */\n  else {\n    const char *init;  /* to search for a `*s2' inside `s1' */\n    l2--;  /* 1st char will be checked by `memchr' */\n    l1 = l1-l2;  /* `s2' cannot be found after that */\n    while (l1 > 0 && (init = (const char *)memchr(s1, *s2, l1)) != NULL) {\n      init++;   /* 1st char is already checked */\n      if (memcmp(init, s2+1, l2) == 0)\n        return init-1;\n      else {  /* correct `l1' and `s1' to try again */\n        l1 -= init-s1;\n        s1 = init;\n      }\n    }\n    return NULL;  /* not found */\n  }\n}\n\n\nstatic void push_onecapture (MatchState *ms, int i, const char *s,\n                                                    const char *e) {\n  if (i >= ms->level) {\n    if (i == 0)  /* ms->level == 0, too */\n      lua_pushlstring(ms->L, s, e - s);  /* add whole match */\n    else\n      luaL_error(ms->L, \"invalid capture index\");\n  }\n  else {\n    ptrdiff_t l = ms->capture[i].len;\n    if (l == CAP_UNFINISHED) luaL_error(ms->L, \"unfinished capture\");\n    if (l == CAP_POSITION)\n      lua_pushinteger(ms->L, ms->capture[i].init - ms->src_init + 1);\n    else\n      lua_pushlstring(ms->L, ms->capture[i].init, l);\n  }\n}\n\n\nstatic int push_captures (MatchState *ms, const char *s, const char *e) {\n  int i;\n  int nlevels = (ms->level == 0 && s) ? 1 : ms->level;\n  luaL_checkstack(ms->L, nlevels, \"too many captures\");\n  for (i = 0; i < nlevels; i++)\n    push_onecapture(ms, i, s, e);\n  return nlevels;  /* number of strings pushed */\n}\n\n\nstatic int str_find_aux (lua_State *L, int find) {\n  size_t l1, l2;\n  const char *s = luaL_checklstring(L, 1, &l1);\n  const char *p = luaL_checklstring(L, 2, &l2);\n  ptrdiff_t init = posrelat(luaL_optinteger(L, 3, 1), l1) - 1;\n  if (init < 0) init = 0;\n  else if ((size_t)(init) > l1) init = (ptrdiff_t)l1;\n  if (find && (lua_toboolean(L, 4) ||  /* explicit request? */\n      strpbrk(p, SPECIALS) == NULL)) {  /* or no special characters? */\n    /* do a plain search */\n    const char *s2 = lmemfind(s+init, l1-init, p, l2);\n    if (s2) {\n      lua_pushinteger(L, s2-s+1);\n      lua_pushinteger(L, s2-s+l2);\n      return 2;\n    }\n  }\n  else {\n    MatchState ms;\n    int anchor = (*p == '^') ? (p++, 1) : 0;\n    const char *s1=s+init;\n    ms.L = L;\n    ms.src_init = s;\n    ms.src_end = s+l1;\n    do {\n      const char *res;\n      ms.level = 0;\n      if ((res=match(&ms, s1, p)) != NULL) {\n        if (find) {\n          lua_pushinteger(L, s1-s+1);  /* start */\n          lua_pushinteger(L, res-s);   /* end */\n          return push_captures(&ms, NULL, 0) + 2;\n        }\n        else\n          return push_captures(&ms, s1, res);\n      }\n    } while (s1++ < ms.src_end && !anchor);\n  }\n  lua_pushnil(L);  /* not found */\n  return 1;\n}\n\n\nstatic int str_find (lua_State *L) {\n  return str_find_aux(L, 1);\n}\n\n\nstatic int str_match (lua_State *L) {\n  return str_find_aux(L, 0);\n}\n\n\nstatic int gmatch_aux (lua_State *L) {\n  MatchState ms;\n  size_t ls;\n  const char *s = lua_tolstring(L, lua_upvalueindex(1), &ls);\n  const char *p = lua_tostring(L, lua_upvalueindex(2));\n  const char *src;\n  ms.L = L;\n  ms.src_init = s;\n  ms.src_end = s+ls;\n  for (src = s + (size_t)lua_tointeger(L, lua_upvalueindex(3));\n       src <= ms.src_end;\n       src++) {\n    const char *e;\n    ms.level = 0;\n    if ((e = match(&ms, src, p)) != NULL) {\n      lua_Integer newstart = e-s;\n      if (e == src) newstart++;  /* empty match? go at least one position */\n      lua_pushinteger(L, newstart);\n      lua_replace(L, lua_upvalueindex(3));\n      return push_captures(&ms, src, e);\n    }\n  }\n  return 0;  /* not found */\n}\n\n\nstatic int gmatch (lua_State *L) {\n  luaL_checkstring(L, 1);\n  luaL_checkstring(L, 2);\n  lua_settop(L, 2);\n  lua_pushinteger(L, 0);\n  lua_pushcclosure(L, gmatch_aux, 3);\n  return 1;\n}\n\n\nstatic int gfind_nodef (lua_State *L) {\n  return luaL_error(L, LUA_QL(\"string.gfind\") \" was renamed to \"\n                       LUA_QL(\"string.gmatch\"));\n}\n\n\nstatic void add_s (MatchState *ms, luaL_Buffer *b, const char *s,\n                                                   const char *e) {\n  size_t l, i;\n  const char *news = lua_tolstring(ms->L, 3, &l);\n  for (i = 0; i < l; i++) {\n    if (news[i] != L_ESC)\n      luaL_addchar(b, news[i]);\n    else {\n      i++;  /* skip ESC */\n      if (!isdigit(uchar(news[i])))\n        luaL_addchar(b, news[i]);\n      else if (news[i] == '0')\n          luaL_addlstring(b, s, e - s);\n      else {\n        push_onecapture(ms, news[i] - '1', s, e);\n        luaL_addvalue(b);  /* add capture to accumulated result */\n      }\n    }\n  }\n}\n\n\nstatic void add_value (MatchState *ms, luaL_Buffer *b, const char *s,\n                                                       const char *e) {\n  lua_State *L = ms->L;\n  switch (lua_type(L, 3)) {\n    case LUA_TNUMBER:\n    case LUA_TSTRING: {\n      add_s(ms, b, s, e);\n      return;\n    }\n    case LUA_TFUNCTION: {\n      int n;\n      lua_pushvalue(L, 3);\n      n = push_captures(ms, s, e);\n      lua_call(L, n, 1);\n      break;\n    }\n    case LUA_TTABLE: {\n      push_onecapture(ms, 0, s, e);\n      lua_gettable(L, 3);\n      break;\n    }\n  }\n  if (!lua_toboolean(L, -1)) {  /* nil or false? */\n    lua_pop(L, 1);\n    lua_pushlstring(L, s, e - s);  /* keep original text */\n  }\n  else if (!lua_isstring(L, -1))\n    luaL_error(L, \"invalid replacement value (a %s)\", luaL_typename(L, -1)); \n  luaL_addvalue(b);  /* add result to accumulator */\n}\n\n\nstatic int str_gsub (lua_State *L) {\n  size_t srcl;\n  const char *src = luaL_checklstring(L, 1, &srcl);\n  const char *p = luaL_checkstring(L, 2);\n  int  tr = lua_type(L, 3);\n  int max_s = luaL_optint(L, 4, srcl+1);\n  int anchor = (*p == '^') ? (p++, 1) : 0;\n  int n = 0;\n  MatchState ms;\n  luaL_Buffer b;\n  luaL_argcheck(L, tr == LUA_TNUMBER || tr == LUA_TSTRING ||\n                   tr == LUA_TFUNCTION || tr == LUA_TTABLE, 3,\n                      \"string/function/table expected\");\n  luaL_buffinit(L, &b);\n  ms.L = L;\n  ms.src_init = src;\n  ms.src_end = src+srcl;\n  while (n < max_s) {\n    const char *e;\n    ms.level = 0;\n    e = match(&ms, src, p);\n    if (e) {\n      n++;\n      add_value(&ms, &b, src, e);\n    }\n    if (e && e>src) /* non empty match? */\n      src = e;  /* skip it */\n    else if (src < ms.src_end)\n      luaL_addchar(&b, *src++);\n    else break;\n    if (anchor) break;\n  }\n  luaL_addlstring(&b, src, ms.src_end-src);\n  luaL_pushresult(&b);\n  lua_pushinteger(L, n);  /* number of substitutions */\n  return 2;\n}\n\n/* }====================================================== */\n\n\n/* maximum size of each formatted item (> len(format('%99.99f', -1e308))) */\n#define MAX_ITEM\t512\n/* valid flags in a format specification */\n#define FLAGS\t\"-+ #0\"\n/*\n** maximum size of each format specification (such as '%-099.99d')\n** (+10 accounts for %99.99x plus margin of error)\n*/\n#define MAX_FORMAT\t(sizeof(FLAGS) + sizeof(LUA_INTFRMLEN) + 10)\n\n\nstatic void addquoted (lua_State *L, luaL_Buffer *b, int arg) {\n  size_t l;\n  const char *s = luaL_checklstring(L, arg, &l);\n  luaL_addchar(b, '\"');\n  while (l--) {\n    switch (*s) {\n      case '\"': case '\\\\': case '\\n': {\n        luaL_addchar(b, '\\\\');\n        luaL_addchar(b, *s);\n        break;\n      }\n      case '\\r': {\n        luaL_addlstring(b, \"\\\\r\", 2);\n        break;\n      }\n      case '\\0': {\n        luaL_addlstring(b, \"\\\\000\", 4);\n        break;\n      }\n      default: {\n        luaL_addchar(b, *s);\n        break;\n      }\n    }\n    s++;\n  }\n  luaL_addchar(b, '\"');\n}\n\nstatic const char *scanformat (lua_State *L, const char *strfrmt, char *form) {\n  const char *p = strfrmt;\n  while (*p != '\\0' && strchr(FLAGS, *p) != NULL) p++;  /* skip flags */\n  if ((size_t)(p - strfrmt) >= sizeof(FLAGS))\n    luaL_error(L, \"invalid format (repeated flags)\");\n  if (isdigit(uchar(*p))) p++;  /* skip width */\n  if (isdigit(uchar(*p))) p++;  /* (2 digits at most) */\n  if (*p == '.') {\n    p++;\n    if (isdigit(uchar(*p))) p++;  /* skip precision */\n    if (isdigit(uchar(*p))) p++;  /* (2 digits at most) */\n  }\n  if (isdigit(uchar(*p)))\n    luaL_error(L, \"invalid format (width or precision too long)\");\n  *(form++) = '%';\n  strncpy(form, strfrmt, p - strfrmt + 1);\n  form += p - strfrmt + 1;\n  *form = '\\0';\n  return p;\n}\n\n\nstatic void addintlen (char *form) {\n  size_t l = strlen(form);\n  char spec = form[l - 1];\n  strcpy(form + l - 1, LUA_INTFRMLEN);\n  form[l + sizeof(LUA_INTFRMLEN) - 2] = spec;\n  form[l + sizeof(LUA_INTFRMLEN) - 1] = '\\0';\n}\n\n\nstatic int str_format (lua_State *L) {\n  int top = lua_gettop(L);\n  int arg = 1;\n  size_t sfl;\n  const char *strfrmt = luaL_checklstring(L, arg, &sfl);\n  const char *strfrmt_end = strfrmt+sfl;\n  luaL_Buffer b;\n  luaL_buffinit(L, &b);\n  while (strfrmt < strfrmt_end) {\n    if (*strfrmt != L_ESC)\n      luaL_addchar(&b, *strfrmt++);\n    else if (*++strfrmt == L_ESC)\n      luaL_addchar(&b, *strfrmt++);  /* %% */\n    else { /* format item */\n      char form[MAX_FORMAT];  /* to store the format (`%...') */\n      char buff[MAX_ITEM];  /* to store the formatted item */\n      if (++arg > top)\n        luaL_argerror(L, arg, \"no value\");\n      strfrmt = scanformat(L, strfrmt, form);\n      switch (*strfrmt++) {\n        case 'c': {\n          sprintf(buff, form, (int)luaL_checknumber(L, arg));\n          break;\n        }\n        case 'd':  case 'i': {\n          addintlen(form);\n          sprintf(buff, form, (LUA_INTFRM_T)luaL_checknumber(L, arg));\n          break;\n        }\n        case 'o':  case 'u':  case 'x':  case 'X': {\n          addintlen(form);\n          sprintf(buff, form, (unsigned LUA_INTFRM_T)luaL_checknumber(L, arg));\n          break;\n        }\n        case 'e':  case 'E': case 'f':\n        case 'g': case 'G': {\n          sprintf(buff, form, (double)luaL_checknumber(L, arg));\n          break;\n        }\n        case 'q': {\n          addquoted(L, &b, arg);\n          continue;  /* skip the 'addsize' at the end */\n        }\n        case 's': {\n          size_t l;\n          const char *s = luaL_checklstring(L, arg, &l);\n          if (!strchr(form, '.') && l >= 100) {\n            /* no precision and string is too long to be formatted;\n               keep original string */\n            lua_pushvalue(L, arg);\n            luaL_addvalue(&b);\n            continue;  /* skip the `addsize' at the end */\n          }\n          else {\n            sprintf(buff, form, s);\n            break;\n          }\n        }\n        default: {  /* also treat cases `pnLlh' */\n          return luaL_error(L, \"invalid option \" LUA_QL(\"%%%c\") \" to \"\n                               LUA_QL(\"format\"), *(strfrmt - 1));\n        }\n      }\n      luaL_addlstring(&b, buff, strlen(buff));\n    }\n  }\n  luaL_pushresult(&b);\n  return 1;\n}\n\n\nstatic const luaL_Reg strlib[] = {\n  {\"byte\", str_byte},\n  {\"char\", str_char},\n  /* no 'dump' without sandboxing 'loadstring', etc. */\n  {\"find\", str_find},\n  {\"format\", str_format},\n  {\"gfind\", gfind_nodef},\n  {\"gmatch\", gmatch},\n  {\"gsub\", str_gsub},\n  {\"len\", str_len},\n  {\"lower\", str_lower},\n  {\"match\", str_match},\n  {\"rep\", str_rep},\n  {\"reverse\", str_reverse},\n  {\"sub\", str_sub},\n  {\"upper\", str_upper},\n  {NULL, NULL}\n};\n\n\nstatic void createmetatable (lua_State *L) {\n  lua_createtable(L, 0, 1);  /* create metatable for strings */\n  lua_pushliteral(L, \"\");  /* dummy string */\n  lua_pushvalue(L, -2);\n  lua_setmetatable(L, -2);  /* set string metatable */\n  lua_pop(L, 1);  /* pop dummy string */\n  lua_pushvalue(L, -2);  /* string library... */\n  lua_setfield(L, -2, \"__index\");  /* ...is the __index metamethod */\n  lua_pop(L, 1);  /* pop metatable */\n}\n\n\n/*\n** Open string library\n*/\nLUALIB_API int luaopen_string (lua_State *L) {\n  luaL_register(L, LUA_STRLIBNAME, strlib);\n#if defined(LUA_COMPAT_GFIND)\n  lua_getfield(L, -1, \"gmatch\");\n  lua_setfield(L, -2, \"gfind\");\n#endif\n  createmetatable(L);\n  return 1;\n}\n\n"
  },
  {
    "path": "src/ltable.c",
    "content": "/*\n** $Id: ltable.c,v 2.32.1.2 2007/12/28 15:32:23 roberto Exp $\n** Lua tables (hash)\n** See Copyright Notice in lua.h\n*/\n\n\n/*\n** Implementation of tables (aka arrays, objects, or hash tables).\n** Tables keep its elements in two parts: an array part and a hash part.\n** Non-negative integer keys are all candidates to be kept in the array\n** part. The actual size of the array is the largest `n' such that at\n** least half the slots between 0 and n are in use.\n** Hash uses a mix of chained scatter table with Brent's variation.\n** A main invariant of these tables is that, if an element is not\n** in its main position (i.e. the `original' position that its hash gives\n** to it), then the colliding element is in its own main position.\n** Hence even when the load factor reaches 100%, performance remains good.\n*/\n\n#include <math.h>\n#include <string.h>\n\n#define ltable_c\n#define LUA_CORE\n\n#include \"lua.h\"\n\n#include \"ldebug.h\"\n#include \"ldo.h\"\n#include \"lgc.h\"\n#include \"lmem.h\"\n#include \"lobject.h\"\n#include \"lstate.h\"\n#include \"ltable.h\"\n\n\n/*\n** max size of array part is 2^MAXBITS\n*/\n#if LUAI_BITSINT > 26\n#define MAXBITS\t\t26\n#else\n#define MAXBITS\t\t(LUAI_BITSINT-2)\n#endif\n\n#define MAXASIZE\t(1 << MAXBITS)\n\n\n#define hashpow2(t,n)      (gnode(t, lmod((n), sizenode(t))))\n  \n#define hashstr(t,str)  hashpow2(t, (str)->tsv.hash)\n#define hashboolean(t,p)        hashpow2(t, p)\n\n\n/*\n** for some types, it is better to avoid modulus by power of 2, as\n** they tend to have many 2 factors.\n*/\n#define hashmod(t,n)\t(gnode(t, ((n) % ((sizenode(t)-1)|1))))\n\n\n#define hashpointer(t,p)\thashmod(t, IntPoint(p))\n\n\n/*\n** number of ints inside a lua_Number\n*/\n#define numints\t\tcast_int(sizeof(lua_Number)/sizeof(int))\n\n\n\n#define dummynode\t\t(&dummynode_)\n\nstatic const Node dummynode_ = {\n  {{NULL}, LUA_TNIL},  /* value */\n  {{{NULL}, LUA_TNIL, NULL}}  /* key */\n};\n\n\n/*\n** hash for lua_Numbers\n*/\nstatic Node *hashnum (const Table *t, lua_Number n) {\n  unsigned int a[numints];\n  int i;\n  if (luai_numeq(n, 0))  /* avoid problems with -0 */\n    return gnode(t, 0);\n  memcpy(a, &n, sizeof(a));\n  for (i = 1; i < numints; i++) a[0] += a[i];\n  return hashmod(t, a[0]);\n}\n\n\n\n/*\n** returns the `main' position of an element in a table (that is, the index\n** of its hash value)\n*/\nstatic Node *mainposition (const Table *t, const TValue *key) {\n  switch (ttype(key)) {\n    case LUA_TNUMBER:\n      return hashnum(t, nvalue(key));\n    case LUA_TSTRING:\n      return hashstr(t, rawtsvalue(key));\n    case LUA_TBOOLEAN:\n      return hashboolean(t, bvalue(key));\n    case LUA_TLIGHTUSERDATA:\n      return hashpointer(t, pvalue(key));\n    default:\n      return hashpointer(t, gcvalue(key));\n  }\n}\n\n\n/*\n** returns the index for `key' if `key' is an appropriate key to live in\n** the array part of the table, -1 otherwise.\n*/\nstatic int arrayindex (const TValue *key) {\n  if (ttisnumber(key)) {\n    lua_Number n = nvalue(key);\n    int k;\n    lua_number2int(k, n);\n    if (luai_numeq(cast_num(k), n))\n      return k;\n  }\n  return -1;  /* `key' did not match some condition */\n}\n\n\n/*\n** returns the index of a `key' for table traversals. First goes all\n** elements in the array part, then elements in the hash part. The\n** beginning of a traversal is signalled by -1.\n*/\nstatic int findindex (lua_State *L, Table *t, StkId key) {\n  int i;\n  if (ttisnil(key)) return -1;  /* first iteration */\n  i = arrayindex(key);\n  if (0 < i && i <= t->sizearray)  /* is `key' inside array part? */\n    return i-1;  /* yes; that's the index (corrected to C) */\n  else {\n    Node *n = mainposition(t, key);\n    do {  /* check whether `key' is somewhere in the chain */\n      /* key may be dead already, but it is ok to use it in `next' */\n      if (luaO_rawequalObj(key2tval(n), key) ||\n            (ttype(gkey(n)) == LUA_TDEADKEY && iscollectable(key) &&\n             gcvalue(gkey(n)) == gcvalue(key))) {\n        i = cast_int(n - gnode(t, 0));  /* key index in hash table */\n        /* hash elements are numbered after array ones */\n        return i + t->sizearray;\n      }\n      else n = gnext(n);\n    } while (n);\n    luaG_runerror(L, \"invalid key to \" LUA_QL(\"next\"));  /* key not found */\n    return 0;  /* to avoid warnings */\n  }\n}\n\n\nint luaH_next (lua_State *L, Table *t, StkId key) {\n  int i = findindex(L, t, key);  /* find original element */\n  for (i++; i < t->sizearray; i++) {  /* try first array part */\n    if (!ttisnil(&t->array[i])) {  /* a non-nil value? */\n      setnvalue(key, cast_num(i+1));\n      setobj2s(L, key+1, &t->array[i]);\n      return 1;\n    }\n  }\n  for (i -= t->sizearray; i < sizenode(t); i++) {  /* then hash part */\n    if (!ttisnil(gval(gnode(t, i)))) {  /* a non-nil value? */\n      setobj2s(L, key, key2tval(gnode(t, i)));\n      setobj2s(L, key+1, gval(gnode(t, i)));\n      return 1;\n    }\n  }\n  return 0;  /* no more elements */\n}\n\n\n/*\n** {=============================================================\n** Rehash\n** ==============================================================\n*/\n\n\nstatic int computesizes (int nums[], int *narray) {\n  int i;\n  int twotoi;  /* 2^i */\n  int a = 0;  /* number of elements smaller than 2^i */\n  int na = 0;  /* number of elements to go to array part */\n  int n = 0;  /* optimal size for array part */\n  for (i = 0, twotoi = 1; twotoi/2 < *narray; i++, twotoi *= 2) {\n    if (nums[i] > 0) {\n      a += nums[i];\n      if (a > twotoi/2) {  /* more than half elements present? */\n        n = twotoi;  /* optimal size (till now) */\n        na = a;  /* all elements smaller than n will go to array part */\n      }\n    }\n    if (a == *narray) break;  /* all elements already counted */\n  }\n  *narray = n;\n  lua_assert(*narray/2 <= na && na <= *narray);\n  return na;\n}\n\n\nstatic int countint (const TValue *key, int *nums) {\n  int k = arrayindex(key);\n  if (0 < k && k <= MAXASIZE) {  /* is `key' an appropriate array index? */\n    nums[ceillog2(k)]++;  /* count as such */\n    return 1;\n  }\n  else\n    return 0;\n}\n\n\nstatic int numusearray (const Table *t, int *nums) {\n  int lg;\n  int ttlg;  /* 2^lg */\n  int ause = 0;  /* summation of `nums' */\n  int i = 1;  /* count to traverse all array keys */\n  for (lg=0, ttlg=1; lg<=MAXBITS; lg++, ttlg*=2) {  /* for each slice */\n    int lc = 0;  /* counter */\n    int lim = ttlg;\n    if (lim > t->sizearray) {\n      lim = t->sizearray;  /* adjust upper limit */\n      if (i > lim)\n        break;  /* no more elements to count */\n    }\n    /* count elements in range (2^(lg-1), 2^lg] */\n    for (; i <= lim; i++) {\n      if (!ttisnil(&t->array[i-1]))\n        lc++;\n    }\n    nums[lg] += lc;\n    ause += lc;\n  }\n  return ause;\n}\n\n\nstatic int numusehash (const Table *t, int *nums, int *pnasize) {\n  int totaluse = 0;  /* total number of elements */\n  int ause = 0;  /* summation of `nums' */\n  int i = sizenode(t);\n  while (i--) {\n    Node *n = &t->node[i];\n    if (!ttisnil(gval(n))) {\n      ause += countint(key2tval(n), nums);\n      totaluse++;\n    }\n  }\n  *pnasize += ause;\n  return totaluse;\n}\n\n\nstatic void setarrayvector (lua_State *L, Table *t, int size) {\n  int i;\n  luaM_reallocvector(L, t->array, t->sizearray, size, TValue);\n  for (i=t->sizearray; i<size; i++)\n     setnilvalue(&t->array[i]);\n  t->sizearray = size;\n}\n\n\nstatic void setnodevector (lua_State *L, Table *t, int size) {\n  int lsize;\n  if (size == 0) {  /* no elements to hash part? */\n    t->node = cast(Node *, dummynode);  /* use common `dummynode' */\n    lsize = 0;\n  }\n  else {\n    int i;\n    lsize = ceillog2(size);\n    if (lsize > MAXBITS)\n      luaG_runerror(L, \"table overflow\");\n    size = twoto(lsize);\n    t->node = luaM_newvector(L, size, Node);\n    for (i=0; i<size; i++) {\n      Node *n = gnode(t, i);\n      gnext(n) = NULL;\n      setnilvalue(gkey(n));\n      setnilvalue(gval(n));\n    }\n  }\n  t->lsizenode = cast_byte(lsize);\n  t->lastfree = gnode(t, size);  /* all positions are free */\n}\n\n\nstatic void resize (lua_State *L, Table *t, int nasize, int nhsize) {\n  int i;\n  int oldasize = t->sizearray;\n  int oldhsize = t->lsizenode;\n  Node *nold = t->node;  /* save old hash ... */\n  if (nasize > oldasize)  /* array part must grow? */\n    setarrayvector(L, t, nasize);\n  /* create new hash part with appropriate size */\n  setnodevector(L, t, nhsize);  \n  if (nasize < oldasize) {  /* array part must shrink? */\n    t->sizearray = nasize;\n    /* re-insert elements from vanishing slice */\n    for (i=nasize; i<oldasize; i++) {\n      if (!ttisnil(&t->array[i]))\n        setobjt2t(L, luaH_setnum(L, t, i+1), &t->array[i]);\n    }\n    /* shrink array */\n    luaM_reallocvector(L, t->array, oldasize, nasize, TValue);\n  }\n  /* re-insert elements from hash part */\n  for (i = twoto(oldhsize) - 1; i >= 0; i--) {\n    Node *old = nold+i;\n    if (!ttisnil(gval(old)))\n      setobjt2t(L, luaH_set(L, t, key2tval(old)), gval(old));\n  }\n  if (nold != dummynode)\n    luaM_freearray(L, nold, twoto(oldhsize), Node);  /* free old array */\n}\n\n\nvoid luaH_resizearray (lua_State *L, Table *t, int nasize) {\n  int nsize = (t->node == dummynode) ? 0 : sizenode(t);\n  resize(L, t, nasize, nsize);\n}\n\n\nstatic void rehash (lua_State *L, Table *t, const TValue *ek) {\n  int nasize, na;\n  int nums[MAXBITS+1];  /* nums[i] = number of keys between 2^(i-1) and 2^i */\n  int i;\n  int totaluse;\n  for (i=0; i<=MAXBITS; i++) nums[i] = 0;  /* reset counts */\n  nasize = numusearray(t, nums);  /* count keys in array part */\n  totaluse = nasize;  /* all those keys are integer keys */\n  totaluse += numusehash(t, nums, &nasize);  /* count keys in hash part */\n  /* count extra key */\n  nasize += countint(ek, nums);\n  totaluse++;\n  /* compute new size for array part */\n  na = computesizes(nums, &nasize);\n  /* resize the table to new computed sizes */\n  resize(L, t, nasize, totaluse - na);\n}\n\n\n\n/*\n** }=============================================================\n*/\n\n\nTable *luaH_new (lua_State *L, int narray, int nhash) {\n  Table *t = luaM_new(L, Table);\n  luaC_link(L, obj2gco(t), LUA_TTABLE);\n  t->metatable = NULL;\n  t->flags = cast_byte(~0);\n  /* temporary values (kept only if some malloc fails) */\n  t->array = NULL;\n  t->sizearray = 0;\n  t->lsizenode = 0;\n  t->node = cast(Node *, dummynode);\n  setarrayvector(L, t, narray);\n  setnodevector(L, t, nhash);\n  return t;\n}\n\n\nvoid luaH_free (lua_State *L, Table *t) {\n  if (t->node != dummynode)\n    luaM_freearray(L, t->node, sizenode(t), Node);\n  luaM_freearray(L, t->array, t->sizearray, TValue);\n  luaM_free(L, t);\n}\n\n\nstatic Node *getfreepos (Table *t) {\n  while (t->lastfree-- > t->node) {\n    if (ttisnil(gkey(t->lastfree)))\n      return t->lastfree;\n  }\n  return NULL;  /* could not find a free place */\n}\n\n\n\n/*\n** inserts a new key into a hash table; first, check whether key's main \n** position is free. If not, check whether colliding node is in its main \n** position or not: if it is not, move colliding node to an empty place and \n** put new key in its main position; otherwise (colliding node is in its main \n** position), new key goes to an empty position. \n*/\nstatic TValue *newkey (lua_State *L, Table *t, const TValue *key) {\n  Node *mp = mainposition(t, key);\n  if (!ttisnil(gval(mp)) || mp == dummynode) {\n    Node *othern;\n    Node *n = getfreepos(t);  /* get a free place */\n    if (n == NULL) {  /* cannot find a free place? */\n      rehash(L, t, key);  /* grow table */\n      return luaH_set(L, t, key);  /* re-insert key into grown table */\n    }\n    lua_assert(n != dummynode);\n    othern = mainposition(t, key2tval(mp));\n    if (othern != mp) {  /* is colliding node out of its main position? */\n      /* yes; move colliding node into free position */\n      while (gnext(othern) != mp) othern = gnext(othern);  /* find previous */\n      gnext(othern) = n;  /* redo the chain with `n' in place of `mp' */\n      *n = *mp;  /* copy colliding node into free pos. (mp->next also goes) */\n      gnext(mp) = NULL;  /* now `mp' is free */\n      setnilvalue(gval(mp));\n    }\n    else {  /* colliding node is in its own main position */\n      /* new node will go into free position */\n      gnext(n) = gnext(mp);  /* chain new position */\n      gnext(mp) = n;\n      mp = n;\n    }\n  }\n  gkey(mp)->value = key->value; gkey(mp)->tt = key->tt;\n  luaC_barriert(L, t, key);\n  lua_assert(ttisnil(gval(mp)));\n  return gval(mp);\n}\n\n\n/*\n** search function for integers\n*/\nconst TValue *luaH_getnum (Table *t, int key) {\n  /* (1 <= key && key <= t->sizearray) */\n  if (cast(unsigned int, key-1) < cast(unsigned int, t->sizearray))\n    return &t->array[key-1];\n  else {\n    lua_Number nk = cast_num(key);\n    Node *n = hashnum(t, nk);\n    do {  /* check whether `key' is somewhere in the chain */\n      if (ttisnumber(gkey(n)) && luai_numeq(nvalue(gkey(n)), nk))\n        return gval(n);  /* that's it */\n      else n = gnext(n);\n    } while (n);\n    return luaO_nilobject;\n  }\n}\n\n\n/*\n** search function for strings\n*/\nconst TValue *luaH_getstr (Table *t, TString *key) {\n  Node *n = hashstr(t, key);\n  do {  /* check whether `key' is somewhere in the chain */\n    if (ttisstring(gkey(n)) && rawtsvalue(gkey(n)) == key)\n      return gval(n);  /* that's it */\n    else n = gnext(n);\n  } while (n);\n  return luaO_nilobject;\n}\n\n\n/*\n** main search function\n*/\nconst TValue *luaH_get (Table *t, const TValue *key) {\n  switch (ttype(key)) {\n    case LUA_TNIL: return luaO_nilobject;\n    case LUA_TSTRING: return luaH_getstr(t, rawtsvalue(key));\n    case LUA_TNUMBER: {\n      int k;\n      lua_Number n = nvalue(key);\n      lua_number2int(k, n);\n      if (luai_numeq(cast_num(k), nvalue(key))) /* index is int? */\n        return luaH_getnum(t, k);  /* use specialized version */\n      /* else go through */\n    }\n    default: {\n      Node *n = mainposition(t, key);\n      do {  /* check whether `key' is somewhere in the chain */\n        if (luaO_rawequalObj(key2tval(n), key))\n          return gval(n);  /* that's it */\n        else n = gnext(n);\n      } while (n);\n      return luaO_nilobject;\n    }\n  }\n}\n\n\nTValue *luaH_set (lua_State *L, Table *t, const TValue *key) {\n  const TValue *p = luaH_get(t, key);\n  t->flags = 0;\n  if (p != luaO_nilobject)\n    return cast(TValue *, p);\n  else {\n    if (ttisnil(key)) luaG_runerror(L, \"table index is nil\");\n    else if (ttisnumber(key) && luai_numisnan(nvalue(key)))\n      luaG_runerror(L, \"table index is NaN\");\n    return newkey(L, t, key);\n  }\n}\n\n\nTValue *luaH_setnum (lua_State *L, Table *t, int key) {\n  const TValue *p = luaH_getnum(t, key);\n  if (p != luaO_nilobject)\n    return cast(TValue *, p);\n  else {\n    TValue k;\n    setnvalue(&k, cast_num(key));\n    return newkey(L, t, &k);\n  }\n}\n\n\nTValue *luaH_setstr (lua_State *L, Table *t, TString *key) {\n  const TValue *p = luaH_getstr(t, key);\n  if (p != luaO_nilobject)\n    return cast(TValue *, p);\n  else {\n    TValue k;\n    setsvalue(L, &k, key);\n    return newkey(L, t, &k);\n  }\n}\n\n\nstatic int unbound_search (Table *t, unsigned int j) {\n  unsigned int i = j;  /* i is zero or a present index */\n  j++;\n  /* find `i' and `j' such that i is present and j is not */\n  while (!ttisnil(luaH_getnum(t, j))) {\n    i = j;\n    j *= 2;\n    if (j > cast(unsigned int, MAX_INT)) {  /* overflow? */\n      /* table was built with bad purposes: resort to linear search */\n      i = 1;\n      while (!ttisnil(luaH_getnum(t, i))) i++;\n      return i - 1;\n    }\n  }\n  /* now do a binary search between them */\n  while (j - i > 1) {\n    unsigned int m = (i+j)/2;\n    if (ttisnil(luaH_getnum(t, m))) j = m;\n    else i = m;\n  }\n  return i;\n}\n\n\n/*\n** Try to find a boundary in table `t'. A `boundary' is an integer index\n** such that t[i] is non-nil and t[i+1] is nil (and 0 if t[1] is nil).\n*/\nint luaH_getn (Table *t) {\n  unsigned int j = t->sizearray;\n  if (j > 0 && ttisnil(&t->array[j - 1])) {\n    /* there is a boundary in the array part: (binary) search for it */\n    unsigned int i = 0;\n    while (j - i > 1) {\n      unsigned int m = (i+j)/2;\n      if (ttisnil(&t->array[m - 1])) j = m;\n      else i = m;\n    }\n    return i;\n  }\n  /* else must find a boundary in hash part */\n  else if (t->node == dummynode)  /* hash part is empty? */\n    return j;  /* that is easy... */\n  else return unbound_search(t, j);\n}\n\n\n\n#if defined(LUA_DEBUG)\n\nNode *luaH_mainposition (const Table *t, const TValue *key) {\n  return mainposition(t, key);\n}\n\nint luaH_isdummy (Node *n) { return n == dummynode; }\n\n#endif\n"
  },
  {
    "path": "src/ltable.h",
    "content": "/*\n** $Id: ltable.h,v 2.10.1.1 2007/12/27 13:02:25 roberto Exp $\n** Lua tables (hash)\n** See Copyright Notice in lua.h\n*/\n\n#ifndef ltable_h\n#define ltable_h\n\n#include \"lobject.h\"\n\n\n#define gnode(t,i)\t(&(t)->node[i])\n#define gkey(n)\t\t(&(n)->i_key.nk)\n#define gval(n)\t\t(&(n)->i_val)\n#define gnext(n)\t((n)->i_key.nk.next)\n\n#define key2tval(n)\t(&(n)->i_key.tvk)\n\n\nLUAI_FUNC const TValue *luaH_getnum (Table *t, int key);\nLUAI_FUNC TValue *luaH_setnum (lua_State *L, Table *t, int key);\nLUAI_FUNC const TValue *luaH_getstr (Table *t, TString *key);\nLUAI_FUNC TValue *luaH_setstr (lua_State *L, Table *t, TString *key);\nLUAI_FUNC const TValue *luaH_get (Table *t, const TValue *key);\nLUAI_FUNC TValue *luaH_set (lua_State *L, Table *t, const TValue *key);\nLUAI_FUNC Table *luaH_new (lua_State *L, int narray, int lnhash);\nLUAI_FUNC void luaH_resizearray (lua_State *L, Table *t, int nasize);\nLUAI_FUNC void luaH_free (lua_State *L, Table *t);\nLUAI_FUNC int luaH_next (lua_State *L, Table *t, StkId key);\nLUAI_FUNC int luaH_getn (Table *t);\n\n\n#if defined(LUA_DEBUG)\nLUAI_FUNC Node *luaH_mainposition (const Table *t, const TValue *key);\nLUAI_FUNC int luaH_isdummy (Node *n);\n#endif\n\n\n#endif\n"
  },
  {
    "path": "src/ltablib.c",
    "content": "/*\n** $Id: ltablib.c,v 1.38.1.3 2008/02/14 16:46:58 roberto Exp $\n** Library for Table Manipulation\n** See Copyright Notice in lua.h\n*/\n\n\n#include <stddef.h>\n\n#define ltablib_c\n#define LUA_LIB\n\n#include \"lua.h\"\n\n#include \"lauxlib.h\"\n#include \"lualib.h\"\n\n\n#define aux_getn(L,n)\t(luaL_checktype(L, n, LUA_TTABLE), luaL_getn(L, n))\n\n\nstatic int foreachi (lua_State *L) {\n  int i;\n  int n = aux_getn(L, 1);\n  luaL_checktype(L, 2, LUA_TFUNCTION);\n  for (i=1; i <= n; i++) {\n    lua_pushvalue(L, 2);  /* function */\n    lua_pushinteger(L, i);  /* 1st argument */\n    lua_rawgeti(L, 1, i);  /* 2nd argument */\n    lua_call(L, 2, 1);\n    if (!lua_isnil(L, -1))\n      return 1;\n    lua_pop(L, 1);  /* remove nil result */\n  }\n  return 0;\n}\n\n\nstatic int foreach (lua_State *L) {\n  luaL_checktype(L, 1, LUA_TTABLE);\n  luaL_checktype(L, 2, LUA_TFUNCTION);\n  lua_pushnil(L);  /* first key */\n  while (lua_next(L, 1)) {\n    lua_pushvalue(L, 2);  /* function */\n    lua_pushvalue(L, -3);  /* key */\n    lua_pushvalue(L, -3);  /* value */\n    lua_call(L, 2, 1);\n    if (!lua_isnil(L, -1))\n      return 1;\n    lua_pop(L, 2);  /* remove value and result */\n  }\n  return 0;\n}\n\n\nstatic int maxn (lua_State *L) {\n  lua_Number max = 0;\n  luaL_checktype(L, 1, LUA_TTABLE);\n  lua_pushnil(L);  /* first key */\n  while (lua_next(L, 1)) {\n    lua_pop(L, 1);  /* remove value */\n    if (lua_type(L, -1) == LUA_TNUMBER) {\n      lua_Number v = lua_tonumber(L, -1);\n      if (v > max) max = v;\n    }\n  }\n  lua_pushnumber(L, max);\n  return 1;\n}\n\n\nstatic int getn (lua_State *L) {\n  lua_pushinteger(L, aux_getn(L, 1));\n  return 1;\n}\n\n\nstatic int setn (lua_State *L) {\n  luaL_checktype(L, 1, LUA_TTABLE);\n#ifndef luaL_setn\n  luaL_setn(L, 1, luaL_checkint(L, 2));\n#else\n  luaL_error(L, LUA_QL(\"setn\") \" is obsolete\");\n#endif\n  lua_pushvalue(L, 1);\n  return 1;\n}\n\n\nstatic int tinsert (lua_State *L) {\n  int e = aux_getn(L, 1) + 1;  /* first empty element */\n  int pos;  /* where to insert new element */\n  switch (lua_gettop(L)) {\n    case 2: {  /* called with only 2 arguments */\n      pos = e;  /* insert new element at the end */\n      break;\n    }\n    case 3: {\n      int i;\n      pos = luaL_checkint(L, 2);  /* 2nd argument is the position */\n      if (pos > e) e = pos;  /* `grow' array if necessary */\n      for (i = e; i > pos; i--) {  /* move up elements */\n        lua_rawgeti(L, 1, i-1);\n        lua_rawseti(L, 1, i);  /* t[i] = t[i-1] */\n      }\n      break;\n    }\n    default: {\n      return luaL_error(L, \"wrong number of arguments to \" LUA_QL(\"insert\"));\n    }\n  }\n  luaL_setn(L, 1, e);  /* new size */\n  lua_rawseti(L, 1, pos);  /* t[pos] = v */\n  return 0;\n}\n\n\nstatic int tremove (lua_State *L) {\n  int e = aux_getn(L, 1);\n  int pos = luaL_optint(L, 2, e);\n  if (!(1 <= pos && pos <= e))  /* position is outside bounds? */\n   return 0;  /* nothing to remove */\n  luaL_setn(L, 1, e - 1);  /* t.n = n-1 */\n  lua_rawgeti(L, 1, pos);  /* result = t[pos] */\n  for ( ;pos<e; pos++) {\n    lua_rawgeti(L, 1, pos+1);\n    lua_rawseti(L, 1, pos);  /* t[pos] = t[pos+1] */\n  }\n  lua_pushnil(L);\n  lua_rawseti(L, 1, e);  /* t[e] = nil */\n  return 1;\n}\n\n\nstatic void addfield (lua_State *L, luaL_Buffer *b, int i) {\n  lua_rawgeti(L, 1, i);\n  if (!lua_isstring(L, -1))\n    luaL_error(L, \"invalid value (%s) at index %d in table for \"\n                  LUA_QL(\"concat\"), luaL_typename(L, -1), i);\n  luaL_addvalue(b);\n}\n\n\nstatic int tconcat (lua_State *L) {\n  luaL_Buffer b;\n  size_t lsep;\n  int i, last;\n  const char *sep = luaL_optlstring(L, 2, \"\", &lsep);\n  luaL_checktype(L, 1, LUA_TTABLE);\n  i = luaL_optint(L, 3, 1);\n  last = luaL_opt(L, luaL_checkint, 4, luaL_getn(L, 1));\n  luaL_buffinit(L, &b);\n  for (; i < last; i++) {\n    addfield(L, &b, i);\n    luaL_addlstring(&b, sep, lsep);\n  }\n  if (i == last)  /* add last value (if interval was not empty) */\n    addfield(L, &b, i);\n  luaL_pushresult(&b);\n  return 1;\n}\n\n\n\n/*\n** {======================================================\n** Quicksort\n** (based on `Algorithms in MODULA-3', Robert Sedgewick;\n**  Addison-Wesley, 1993.)\n*/\n\n\nstatic void set2 (lua_State *L, int i, int j) {\n  lua_rawseti(L, 1, i);\n  lua_rawseti(L, 1, j);\n}\n\nstatic int sort_comp (lua_State *L, int a, int b) {\n  if (!lua_isnil(L, 2)) {  /* function? */\n    int res;\n    lua_pushvalue(L, 2);\n    lua_pushvalue(L, a-1);  /* -1 to compensate function */\n    lua_pushvalue(L, b-2);  /* -2 to compensate function and `a' */\n    lua_call(L, 2, 1);\n    res = lua_toboolean(L, -1);\n    lua_pop(L, 1);\n    return res;\n  }\n  else  /* a < b? */\n    return lua_lessthan(L, a, b);\n}\n\nstatic void auxsort (lua_State *L, int l, int u) {\n  while (l < u) {  /* for tail recursion */\n    int i, j;\n    /* sort elements a[l], a[(l+u)/2] and a[u] */\n    lua_rawgeti(L, 1, l);\n    lua_rawgeti(L, 1, u);\n    if (sort_comp(L, -1, -2))  /* a[u] < a[l]? */\n      set2(L, l, u);  /* swap a[l] - a[u] */\n    else\n      lua_pop(L, 2);\n    if (u-l == 1) break;  /* only 2 elements */\n    i = (l+u)/2;\n    lua_rawgeti(L, 1, i);\n    lua_rawgeti(L, 1, l);\n    if (sort_comp(L, -2, -1))  /* a[i]<a[l]? */\n      set2(L, i, l);\n    else {\n      lua_pop(L, 1);  /* remove a[l] */\n      lua_rawgeti(L, 1, u);\n      if (sort_comp(L, -1, -2))  /* a[u]<a[i]? */\n        set2(L, i, u);\n      else\n        lua_pop(L, 2);\n    }\n    if (u-l == 2) break;  /* only 3 elements */\n    lua_rawgeti(L, 1, i);  /* Pivot */\n    lua_pushvalue(L, -1);\n    lua_rawgeti(L, 1, u-1);\n    set2(L, i, u-1);\n    /* a[l] <= P == a[u-1] <= a[u], only need to sort from l+1 to u-2 */\n    i = l; j = u-1;\n    for (;;) {  /* invariant: a[l..i] <= P <= a[j..u] */\n      /* repeat ++i until a[i] >= P */\n      while (lua_rawgeti(L, 1, ++i), sort_comp(L, -1, -2)) {\n        if (i>u) luaL_error(L, \"invalid order function for sorting\");\n        lua_pop(L, 1);  /* remove a[i] */\n      }\n      /* repeat --j until a[j] <= P */\n      while (lua_rawgeti(L, 1, --j), sort_comp(L, -3, -1)) {\n        if (j<l) luaL_error(L, \"invalid order function for sorting\");\n        lua_pop(L, 1);  /* remove a[j] */\n      }\n      if (j<i) {\n        lua_pop(L, 3);  /* pop pivot, a[i], a[j] */\n        break;\n      }\n      set2(L, i, j);\n    }\n    lua_rawgeti(L, 1, u-1);\n    lua_rawgeti(L, 1, i);\n    set2(L, u-1, i);  /* swap pivot (a[u-1]) with a[i] */\n    /* a[l..i-1] <= a[i] == P <= a[i+1..u] */\n    /* adjust so that smaller half is in [j..i] and larger one in [l..u] */\n    if (i-l < u-i) {\n      j=l; i=i-1; l=i+2;\n    }\n    else {\n      j=i+1; i=u; u=j-2;\n    }\n    auxsort(L, j, i);  /* call recursively the smaller one */\n  }  /* repeat the routine for the larger one */\n}\n\nstatic int sort (lua_State *L) {\n  int n = aux_getn(L, 1);\n  luaL_checkstack(L, 40, \"\");  /* assume array is smaller than 2^40 */\n  if (!lua_isnoneornil(L, 2))  /* is there a 2nd argument? */\n    luaL_checktype(L, 2, LUA_TFUNCTION);\n  lua_settop(L, 2);  /* make sure there is two arguments */\n  auxsort(L, 1, n);\n  return 0;\n}\n\n/* }====================================================== */\n\n\nstatic const luaL_Reg tab_funcs[] = {\n  {\"concat\", tconcat},\n  {\"foreach\", foreach},\n  {\"foreachi\", foreachi},\n  {\"getn\", getn},\n  {\"maxn\", maxn},\n  {\"insert\", tinsert},\n  {\"remove\", tremove},\n  {\"setn\", setn},\n  {\"sort\", sort},\n  {NULL, NULL}\n};\n\n\nLUALIB_API int luaopen_table (lua_State *L) {\n  luaL_register(L, LUA_TABLIBNAME, tab_funcs);\n  return 1;\n}\n\n"
  },
  {
    "path": "src/ltm.c",
    "content": "/*\n** $Id: ltm.c,v 2.8.1.1 2007/12/27 13:02:25 roberto Exp $\n** Tag methods\n** See Copyright Notice in lua.h\n*/\n\n\n#include <string.h>\n\n#define ltm_c\n#define LUA_CORE\n\n#include \"lua.h\"\n\n#include \"lobject.h\"\n#include \"lstate.h\"\n#include \"lstring.h\"\n#include \"ltable.h\"\n#include \"ltm.h\"\n\n\n\nconst char *const luaT_typenames[] = {\n  \"nil\", \"boolean\", \"userdata\", \"number\",\n  \"string\", \"table\", \"function\", \"userdata\", \"thread\",\n  \"proto\", \"upval\"\n};\n\n\nvoid luaT_init (lua_State *L) {\n  static const char *const luaT_eventname[] = {  /* ORDER TM */\n    \"__index\", \"__newindex\",\n    \"__gc\", \"__mode\", \"__eq\",\n    \"__add\", \"__sub\", \"__mul\", \"__div\", \"__mod\",\n    \"__pow\", \"__unm\", \"__len\", \"__lt\", \"__le\",\n    \"__concat\", \"__call\"\n  };\n  int i;\n  for (i=0; i<TM_N; i++) {\n    G(L)->tmname[i] = luaS_new(L, luaT_eventname[i]);\n    luaS_fix(G(L)->tmname[i]);  /* never collect these names */\n  }\n}\n\n\n/*\n** function to be used with macro \"fasttm\": optimized for absence of\n** tag methods\n*/\nconst TValue *luaT_gettm (Table *events, TMS event, TString *ename) {\n  const TValue *tm = luaH_getstr(events, ename);\n  lua_assert(event <= TM_EQ);\n  if (ttisnil(tm)) {  /* no tag method? */\n    events->flags |= cast_byte(1u<<event);  /* cache this fact */\n    return NULL;\n  }\n  else return tm;\n}\n\n\nconst TValue *luaT_gettmbyobj (lua_State *L, const TValue *o, TMS event) {\n  Table *mt;\n  switch (ttype(o)) {\n    case LUA_TTABLE:\n      mt = hvalue(o)->metatable;\n      break;\n    case LUA_TUSERDATA:\n      mt = uvalue(o)->metatable;\n      break;\n    default:\n      mt = G(L)->mt[ttype(o)];\n  }\n  return (mt ? luaH_getstr(mt, G(L)->tmname[event]) : luaO_nilobject);\n}\n\n"
  },
  {
    "path": "src/ltm.h",
    "content": "/*\n** $Id: ltm.h,v 2.6.1.1 2007/12/27 13:02:25 roberto Exp $\n** Tag methods\n** See Copyright Notice in lua.h\n*/\n\n#ifndef ltm_h\n#define ltm_h\n\n\n#include \"lobject.h\"\n\n\n/*\n* WARNING: if you change the order of this enumeration,\n* grep \"ORDER TM\"\n*/\ntypedef enum {\n  TM_INDEX,\n  TM_NEWINDEX,\n  TM_GC,\n  TM_MODE,\n  TM_EQ,  /* last tag method with `fast' access */\n  TM_ADD,\n  TM_SUB,\n  TM_MUL,\n  TM_DIV,\n  TM_MOD,\n  TM_POW,\n  TM_UNM,\n  TM_LEN,\n  TM_LT,\n  TM_LE,\n  TM_CONCAT,\n  TM_CALL,\n  TM_N\t\t/* number of elements in the enum */\n} TMS;\n\n\n\n#define gfasttm(g,et,e) ((et) == NULL ? NULL : \\\n  ((et)->flags & (1u<<(e))) ? NULL : luaT_gettm(et, e, (g)->tmname[e]))\n\n#define fasttm(l,et,e)\tgfasttm(G(l), et, e)\n\nLUAI_DATA const char *const luaT_typenames[];\n\n\nLUAI_FUNC const TValue *luaT_gettm (Table *events, TMS event, TString *ename);\nLUAI_FUNC const TValue *luaT_gettmbyobj (lua_State *L, const TValue *o,\n                                                       TMS event);\nLUAI_FUNC void luaT_init (lua_State *L);\n\n#endif\n"
  },
  {
    "path": "src/lua.c",
    "content": "/*\n** $Id: lua.c,v 1.160.1.2 2007/12/28 15:32:23 roberto Exp $\n** Lua stand-alone interpreter\n** See Copyright Notice in lua.h\n*/\n\n\n#include <locale.h>\n#ifdef __NetBSD__\n#include <curses.h>\n#else\n#include <ncurses.h>\n#endif\n#include <signal.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n\n#define lua_c\n\n#include \"lua.h\"\n#include \"teliva.h\"\n\n#include \"lauxlib.h\"\n#include \"lualib.h\"\n\n\nstatic lua_State *globalL = NULL;\n\nstatic const char *progname = LUA_PROGNAME;\n\n\n\nstatic void lstop (lua_State *L, lua_Debug *ar) {\n  (void)ar;  /* unused arg. */\n  lua_sethook(L, NULL, 0, 0);\n  luaL_error(L, \"interrupted!\");\n}\n\n\nstatic void laction (int i) {\n  signal(i, SIG_DFL); /* if another SIGINT happens before lstop,\n                              terminate process (default action) */\n  lua_sethook(globalL, lstop, LUA_MASKCALL | LUA_MASKRET | LUA_MASKCOUNT, 1);\n}\n\n\nstatic void print_usage (void) {\n  printf(\"usage: %s ___.tlv [args]\\n\", progname);\n}\n\n\nstatic void l_message (const char *pname, const char *msg) {\n  if (!stdscr || isendwin()) {\n    printf(\"%s: %s\\n\", pname, msg);\n    exit(1);\n  }\n  if (pname) mvprintw(LINES-2, 0, \"%s: \", pname);\n  printw(msg);\n  mvprintw(LINES-1, 0, \"sorry, you'll need to edit the image directly. press any key to exit.\");\n  refresh();\n  nodelay(stdscr, 0);  /* make getch() block */\n  getch();\n}\n\n\nstatic int report (lua_State *L, int status) {\n  if (status && !lua_isnil(L, -1)) {\n    const char *msg = lua_tostring(L, -1);\n    if (msg == NULL) msg = \"(error object is not a string)\";\n    l_message(progname, msg);\n    lua_pop(L, 1);\n  }\n  return status;\n}\n\n\nstatic int traceback (lua_State *L) {\n  if (!lua_isstring(L, 1))  /* 'message' not a string? */\n    return 1;  /* keep it intact */\n  lua_getfield(L, LUA_GLOBALSINDEX, \"debug\");\n  if (!lua_istable(L, -1)) {\n    lua_pop(L, 1);\n    return 1;\n  }\n  lua_getfield(L, -1, \"traceback\");\n  if (!lua_isfunction(L, -1)) {\n    lua_pop(L, 2);\n    return 1;\n  }\n  lua_pushvalue(L, 1);  /* pass error message */\n  lua_pushinteger(L, 2);  /* skip this function and traceback */\n  lua_call(L, 2, 1);  /* call debug.traceback */\n  return 1;\n}\n\n\nint docall (lua_State *L, int narg, int clear) {\n  int status;\n  int base = lua_gettop(L) - narg;  /* function index */\n  lua_pushcfunction(L, traceback);  /* push traceback function */\n  lua_insert(L, base);  /* put it under chunk and args */\n  signal(SIGINT, laction);\n  status = lua_pcall(L, narg, (clear ? 0 : LUA_MULTRET), base);\n  signal(SIGINT, SIG_DFL);\n  lua_remove(L, base);  /* remove traceback function */\n  /* force a complete garbage collection in case of errors */\n  if (status != 0) lua_gc(L, LUA_GCCOLLECT, 0);\n  return status;\n}\n\n\n/* initialize global binding \"args\" for commandline args */\nvoid set_args (lua_State *L, char **argv, int n) {\n  int narg;\n  int i;\n  int argc = 0;\n  while (argv[argc]) argc++;  /* count total number of arguments */\n  narg = argc - (n + 1);  /* number of arguments to the script */\n  luaL_checkstack(L, narg + 3, \"too many arguments to script\");\n  lua_newtable(L);\n  for (i=0; i < argc; i++) {\n    lua_pushstring(L, argv[i]);\n    lua_rawseti(L, -2, i - n);\n  }\n  lua_setglobal(L, \"arg\");\n}\n\n\nstatic int dofile (lua_State *L, const char *name) {\n  int status = luaL_loadfile(L, name) || docall(L, 0, 1);\n  return report_in_developer_mode(L, status);\n}\n\n\nint dostring (lua_State *L, const char *s, const char *name) {\n  int status = luaL_loadbuffer(L, s, strlen(s), name) || docall(L, 0, 1);\n  return report_in_developer_mode(L, status);\n}\n\n\nvoid stack_dump (lua_State *L) {\n  int i;\n  int top = lua_gettop(L);\n  int y = 1;\n  for (i = 1; i <= top; i++) {  /* repeat for each level */\n    int t = lua_type(L, i);\n    switch (t) {\n\n      case LUA_TSTRING:  /* strings */\n        mvprintw(y, 30, \"`%s'\", lua_tostring(L, i));\n        break;\n\n      case LUA_TBOOLEAN:  /* booleans */\n        mvprintw(y, 30, lua_toboolean(L, i) ? \"true\" : \"false\");\n        break;\n\n      case LUA_TNUMBER:  /* numbers */\n        mvprintw(y, 30, \"%g\", lua_tonumber(L, i));\n        break;\n\n      default:  /* other values */\n        mvprintw(y, 30, \"%s\", lua_typename(L, t));\n        break;\n\n    }\n    y++;\n    mvprintw(y, 30, \"  \");  /* put a separator */\n    y++;\n  }\n  mvprintw(y, 30, \"\\n\");  /* end the listing */\n  y++;\n}\n\n\nstatic int handle_luainit (lua_State *L) {\n  const char *init = getenv(LUA_INIT);\n  if (init == NULL) return 0;  /* status OK */\n  else if (init[0] == '@')\n    return dofile(L, init+1);\n  else\n    return dostring(L, init, \"=\" LUA_INIT);\n}\n\n\n/* roughly equivalent to:\n *  globalname = require(filename) */\nstatic int dorequire (lua_State *L, const char *filename, const char *globalname) {\n  int status = luaL_loadfile(L, filename) || docall(L, /*nargs*/0, /*don't clean up stack*/0);\n  if (status != 0) return report_in_developer_mode(L, status);\n  if (lua_isnil(L, -1)) {\n    endwin();\n    printf(\"%s didn't return a module\\n\", filename);\n    exit(1);\n  }\n  lua_setglobal(L, globalname);\n  return 0;\n}\n\n\nstruct Smain {\n  int argc;\n  char **argv;\n  int status;\n};\n\n\n/* does its own error handling, always returns 0 to prevent duplicate messages */\nstatic int pmain (lua_State *L) {\n  struct Smain *s = (struct Smain *)lua_touserdata(L, 1);\n  char **argv = s->argv;\n  int status;\n  globalL = L;\n  if (argv[0] && argv[0][0]) progname = argv[0];\n\n  lua_gc(L, LUA_GCSTOP, 0);  /* stop collector during initialization */\n  luaL_openlibs(L);\n  status = dorequire(L, \"src/lcurses/curses.lua\", \"curses\");\n  if (status != 0) return 0;\n  status = dorequire(L, \"src/luasocket/socket.lua\", \"socket\");\n  if (status != 0) return 0;\n  status = dorequire(L, \"src/luasocket/url.lua\", \"url\");\n  if (status != 0) return 0;\n  status = dorequire(L, \"src/luasocket/ltn12.lua\", \"ltn12\");\n  if (status != 0) return 0;\n  status = dorequire(L, \"src/luasocket/mime.lua\", \"mime\");\n  if (status != 0) return 0;\n  status = dorequire(L, \"src/luasocket/headers.lua\", \"headers\");\n  if (status != 0) return 0;\n  status = dorequire(L, \"src/luasocket/http.lua\", \"http\");\n  if (status != 0) return 0;\n  status = dorequire(L, \"src/luasec/ssl.lua\", \"ssl\");\n  if (status != 0) return 0;\n  status = dorequire(L, \"src/luasec/https.lua\", \"https\");\n  if (status != 0) return 0;\n  status = dorequire(L, \"src/json.lua\", \"json\");\n  if (status != 0) return 0;\n  status = dorequire(L, \"src/jsonf.lua\", \"jsonf\");\n  if (status != 0) return 0;\n  status = dorequire(L, \"src/task.lua\", \"task\");\n  if (status != 0) return 0;\n  status = dofile(L, \"src/file.lua\");\n  if (status != 0) return 0;\n  lua_gc(L, LUA_GCRESTART, 0);\n\n  s->status = handle_luainit(L);\n  if (s->status != 0) return 0;\n  s->status = load_image(L, argv, 1);\n  if (s->status != 0) return 0;\n\n  /* call main() */\n  save_call_graph_depth(L, /*depth*/2, \"main\");  /* manually seed debug info */\n  lua_getglobal(L, \"main\");\n  s->status = docall(L, 0, 1);\n  if (s->status != 0) return report_in_developer_mode(L, s->status);\n\n  return 0;\n}\n\n\nextern void cleanup_curses(void);\nint main (int argc, char **argv) {\n  int status;\n  struct Smain s;\n  lua_State *L = luaL_newstate();\n  if (L == NULL) {\n    l_message(argv[0], \"cannot create state: not enough memory\");\n    return EXIT_FAILURE;\n  }\n  if (argc == 1) {\n    print_usage();\n    exit(1);\n  }\n  setlocale(LC_ALL, \"\");\n  initscr();\n  keypad(stdscr, 1);\n  start_color();\n  assume_default_colors(COLOR_FOREGROUND, COLOR_BACKGROUND);\n  render_trusted_teliva_data(L);\n  echo();\n  s.argc = argc;\n  s.argv = argv;\n  Argv = argv;\n  status = lua_cpcall(L, &pmain, &s);\n  report(L, status);\n  lua_close(L);\n  cleanup_curses();\n  return (status || s.status) ? EXIT_FAILURE : EXIT_SUCCESS;\n}\n\n"
  },
  {
    "path": "src/lua.h",
    "content": "/*\n** $Id: lua.h,v 1.218.1.7 2012/01/13 20:36:20 roberto Exp $\n** Lua - An Extensible Extension Language\n** Lua.org, PUC-Rio, Brazil (http://www.lua.org)\n** See Copyright Notice at the end of this file\n*/\n\n\n#ifndef lua_h\n#define lua_h\n\n#include <stdarg.h>\n#include <stddef.h>\n\n\n#include \"luaconf.h\"\n\n\n#define LUA_VERSION\t\"Lua 5.1\"\n#define LUA_RELEASE\t\"Lua 5.1.5\"\n#define LUA_VERSION_NUM\t501\n#define LUA_COPYRIGHT\t\"Copyright (C) 1994-2012 Lua.org, PUC-Rio\"\n#define LUA_AUTHORS \t\"R. Ierusalimschy, L. H. de Figueiredo & W. Celes\"\n\n\nextern char *Image_name;\n\n\n/* mark for precompiled code (`<esc>Lua') */\n#define\tLUA_SIGNATURE\t\"\\033Lua\"\n\n/* option for multiple returns in `lua_pcall' and `lua_call' */\n#define LUA_MULTRET\t(-1)\n\n\n/*\n** pseudo-indices\n*/\n#define LUA_REGISTRYINDEX\t(-10000)\n#define LUA_ENVIRONINDEX\t(-10001)\n#define LUA_GLOBALSINDEX\t(-10002)\n#define lua_upvalueindex(i)\t(LUA_GLOBALSINDEX-(i))\n\n\n/* thread status; 0 is OK */\n#define LUA_YIELD\t1\n#define LUA_ERRRUN\t2\n#define LUA_ERRSYNTAX\t3\n#define LUA_ERRMEM\t4\n#define LUA_ERRERR\t5\n\n\ntypedef struct lua_State lua_State;\n\ntypedef int (*lua_CFunction) (lua_State *L);\n\n\n/*\n** functions that read/write blocks when loading/dumping Lua chunks\n*/\ntypedef const char * (*lua_Reader) (lua_State *L, void *ud, size_t *sz);\n\ntypedef int (*lua_Writer) (lua_State *L, const void* p, size_t sz, void* ud);\n\n\n/*\n** prototype for memory-allocation functions\n*/\ntypedef void * (*lua_Alloc) (void *ud, void *ptr, size_t osize, size_t nsize);\n\n\n/*\n** basic types\n*/\n#define LUA_TNONE\t\t(-1)\n\n#define LUA_TNIL\t\t0\n#define LUA_TBOOLEAN\t\t1\n#define LUA_TLIGHTUSERDATA\t2\n#define LUA_TNUMBER\t\t3\n#define LUA_TSTRING\t\t4\n#define LUA_TTABLE\t\t5\n#define LUA_TFUNCTION\t\t6\n#define LUA_TUSERDATA\t\t7\n#define LUA_TTHREAD\t\t8\n\n\n\n/* minimum Lua stack available to a C function */\n#define LUA_MINSTACK\t20\n\n\n/*\n** generic extra include file\n*/\n#if defined(LUA_USER_H)\n#include LUA_USER_H\n#endif\n\n\n/* type of numbers in Lua */\ntypedef LUA_NUMBER lua_Number;\n\n\n/* type for integer functions */\ntypedef LUA_INTEGER lua_Integer;\n\n\n\n/*\n** state manipulation\n*/\nLUA_API lua_State *(lua_newstate) (lua_Alloc f, void *ud);\nLUA_API void       (lua_close) (lua_State *L);\nLUA_API lua_State *(lua_newthread) (lua_State *L);\n\nLUA_API lua_CFunction (lua_atpanic) (lua_State *L, lua_CFunction panicf);\n\n\n/*\n** basic stack manipulation\n*/\nLUA_API int   (lua_gettop) (lua_State *L);\nLUA_API void  (lua_settop) (lua_State *L, int idx);\nLUA_API void  (lua_pushvalue) (lua_State *L, int idx);\nLUA_API void  (lua_remove) (lua_State *L, int idx);\nLUA_API void  (lua_insert) (lua_State *L, int idx);\nLUA_API void  (lua_replace) (lua_State *L, int idx);\nLUA_API int   (lua_checkstack) (lua_State *L, int sz);\n\nLUA_API void  (lua_xmove) (lua_State *from, lua_State *to, int n);\n\n\n/*\n** access functions (stack -> C)\n*/\n\nLUA_API int             (lua_isnumber) (lua_State *L, int idx);\nLUA_API int             (lua_isstring) (lua_State *L, int idx);\nLUA_API int             (lua_iscfunction) (lua_State *L, int idx);\nLUA_API int             (lua_isuserdata) (lua_State *L, int idx);\nLUA_API int             (lua_type) (lua_State *L, int idx);\nLUA_API const char     *(lua_typename) (lua_State *L, int tp);\n\nLUA_API int            (lua_equal) (lua_State *L, int idx1, int idx2);\nLUA_API int            (lua_rawequal) (lua_State *L, int idx1, int idx2);\nLUA_API int            (lua_lessthan) (lua_State *L, int idx1, int idx2);\n\nLUA_API lua_Number      (lua_tonumber) (lua_State *L, int idx);\nLUA_API lua_Integer     (lua_tointeger) (lua_State *L, int idx);\nLUA_API int             (lua_toboolean) (lua_State *L, int idx);\nLUA_API const char     *(lua_tolstring) (lua_State *L, int idx, size_t *len);\nLUA_API size_t          (lua_objlen) (lua_State *L, int idx);\nLUA_API lua_CFunction   (lua_tocfunction) (lua_State *L, int idx);\nLUA_API void\t       *(lua_touserdata) (lua_State *L, int idx);\nLUA_API lua_State      *(lua_tothread) (lua_State *L, int idx);\nLUA_API const void     *(lua_topointer) (lua_State *L, int idx);\n\n\n/*\n** push functions (C -> stack)\n*/\nLUA_API void  (lua_pushnil) (lua_State *L);\nLUA_API void  (lua_pushnumber) (lua_State *L, lua_Number n);\nLUA_API void  (lua_pushinteger) (lua_State *L, lua_Integer n);\nLUA_API void  (lua_pushlstring) (lua_State *L, const char *s, size_t l);\nLUA_API void  (lua_pushstring) (lua_State *L, const char *s);\nLUA_API const char *(lua_pushvfstring) (lua_State *L, const char *fmt,\n                                                      va_list argp);\nLUA_API const char *(lua_pushfstring) (lua_State *L, const char *fmt, ...);\nLUA_API void  (lua_pushcclosure) (lua_State *L, lua_CFunction fn, int n);\nLUA_API void  (lua_pushboolean) (lua_State *L, int b);\nLUA_API void  (lua_pushlightuserdata) (lua_State *L, void *p);\nLUA_API int   (lua_pushthread) (lua_State *L);\n\n\n/*\n** get functions (Lua -> stack)\n*/\nLUA_API void  (lua_gettable) (lua_State *L, int idx);\nLUA_API void  (lua_getfield) (lua_State *L, int idx, const char *k);\nLUA_API void  (lua_rawget) (lua_State *L, int idx);\nLUA_API void  (lua_rawgeti) (lua_State *L, int idx, int n);\nLUA_API void  (lua_createtable) (lua_State *L, int narr, int nrec);\nLUA_API void *(lua_newuserdata) (lua_State *L, size_t sz);\nLUA_API int   (lua_getmetatable) (lua_State *L, int objindex);\nLUA_API void  (lua_getfenv) (lua_State *L, int idx);\n\n\n/*\n** set functions (stack -> Lua)\n*/\nLUA_API void  (lua_settable) (lua_State *L, int idx);\nLUA_API void  (lua_setfield) (lua_State *L, int idx, const char *k);\nLUA_API void  (lua_rawset) (lua_State *L, int idx);\nLUA_API void  (lua_rawseti) (lua_State *L, int idx, int n);\nLUA_API int   (lua_setmetatable) (lua_State *L, int objindex);\nLUA_API int   (lua_setfenv) (lua_State *L, int idx);\n\n\n/*\n** `load' and `call' functions (load and run Lua code)\n*/\nLUA_API void  (lua_call) (lua_State *L, int nargs, int nresults);\nLUA_API int   (lua_pcall) (lua_State *L, int nargs, int nresults, int errfunc);\nLUA_API int   (lua_cpcall) (lua_State *L, lua_CFunction func, void *ud);\nLUA_API int   (lua_load) (lua_State *L, lua_Reader reader, void *dt,\n                                        const char *chunkname);\n\nLUA_API int (lua_dump) (lua_State *L, lua_Writer writer, void *data);\n\n\n/*\n** coroutine functions\n*/\nLUA_API int  (lua_yield) (lua_State *L, int nresults);\nLUA_API int  (lua_resume) (lua_State *L, int narg);\nLUA_API int  (lua_status) (lua_State *L);\n\n/*\n** garbage-collection function and options\n*/\n\n#define LUA_GCSTOP\t\t0\n#define LUA_GCRESTART\t\t1\n#define LUA_GCCOLLECT\t\t2\n#define LUA_GCCOUNT\t\t3\n#define LUA_GCCOUNTB\t\t4\n#define LUA_GCSTEP\t\t5\n#define LUA_GCSETPAUSE\t\t6\n#define LUA_GCSETSTEPMUL\t7\n\nLUA_API int (lua_gc) (lua_State *L, int what, int data);\n\n\n/*\n** miscellaneous functions\n*/\n\nLUA_API int   (lua_error) (lua_State *L);\n\nLUA_API int   (lua_next) (lua_State *L, int idx);\n\nLUA_API void  (lua_concat) (lua_State *L, int n);\n\nLUA_API lua_Alloc (lua_getallocf) (lua_State *L, void **ud);\nLUA_API void lua_setallocf (lua_State *L, lua_Alloc f, void *ud);\n\n\n\n/* \n** ===============================================================\n** some useful macros\n** ===============================================================\n*/\n\n#define lua_pop(L,n)\t\tlua_settop(L, -(n)-1)\n\n#define lua_newtable(L)\t\tlua_createtable(L, 0, 0)\n\n#define lua_register(L,n,f) (lua_pushcfunction(L, (f)), lua_setglobal(L, (n)))\n\n#define lua_pushcfunction(L,f)\tlua_pushcclosure(L, (f), 0)\n\n#define lua_strlen(L,i)\t\tlua_objlen(L, (i))\n\n#define lua_isfunction(L,n)\t(lua_type(L, (n)) == LUA_TFUNCTION)\n#define lua_istable(L,n)\t(lua_type(L, (n)) == LUA_TTABLE)\n#define lua_islightuserdata(L,n)\t(lua_type(L, (n)) == LUA_TLIGHTUSERDATA)\n#define lua_isnil(L,n)\t\t(lua_type(L, (n)) == LUA_TNIL)\n#define lua_isboolean(L,n)\t(lua_type(L, (n)) == LUA_TBOOLEAN)\n#define lua_isthread(L,n)\t(lua_type(L, (n)) == LUA_TTHREAD)\n#define lua_isnone(L,n)\t\t(lua_type(L, (n)) == LUA_TNONE)\n#define lua_isnoneornil(L, n)\t(lua_type(L, (n)) <= 0)\n\n#define lua_pushliteral(L, s)\t\\\n\tlua_pushlstring(L, \"\" s, (sizeof(s)/sizeof(char))-1)\n\n#define lua_setglobal(L,s)\tlua_setfield(L, LUA_GLOBALSINDEX, (s))\n#define lua_getglobal(L,s)\tlua_getfield(L, LUA_GLOBALSINDEX, (s))\n\n#define lua_tostring(L,i)\tlua_tolstring(L, (i), NULL)\n\n\n\n/*\n** compatibility macros and functions\n*/\n\n#define lua_getregistry(L)\tlua_pushvalue(L, LUA_REGISTRYINDEX)\n\n#define lua_getgccount(L)\tlua_gc(L, LUA_GCCOUNT, 0)\n\n#define lua_Chunkreader\t\tlua_Reader\n#define lua_Chunkwriter\t\tlua_Writer\n\n\n/* hack */\nLUA_API void lua_setlevel\t(lua_State *from, lua_State *to);\n\n\n/*\n** {======================================================================\n** Debug API\n** =======================================================================\n*/\n\n\n/*\n** Event codes\n*/\n#define LUA_HOOKCALL\t0\n#define LUA_HOOKRET\t1\n#define LUA_HOOKLINE\t2\n#define LUA_HOOKCOUNT\t3\n#define LUA_HOOKTAILRET 4\n\n\n/*\n** Event masks\n*/\n#define LUA_MASKCALL\t(1 << LUA_HOOKCALL)\n#define LUA_MASKRET\t(1 << LUA_HOOKRET)\n#define LUA_MASKLINE\t(1 << LUA_HOOKLINE)\n#define LUA_MASKCOUNT\t(1 << LUA_HOOKCOUNT)\n\ntypedef struct lua_Debug lua_Debug;  /* activation record */\n\n\n/* Functions to be called by the debuger in specific events */\ntypedef void (*lua_Hook) (lua_State *L, lua_Debug *ar);\n\n\nLUA_API int lua_getstack (lua_State *L, int level, lua_Debug *ar);\nLUA_API int lua_getinfo (lua_State *L, const char *what, lua_Debug *ar);\nLUA_API const char *lua_getlocal (lua_State *L, const lua_Debug *ar, int n);\nLUA_API const char *lua_setlocal (lua_State *L, const lua_Debug *ar, int n);\nLUA_API const char *lua_getupvalue (lua_State *L, int funcindex, int n);\nLUA_API const char *lua_setupvalue (lua_State *L, int funcindex, int n);\n\nLUA_API int lua_sethook (lua_State *L, lua_Hook func, int mask, int count);\nLUA_API lua_Hook lua_gethook (lua_State *L);\nLUA_API int lua_gethookmask (lua_State *L);\nLUA_API int lua_gethookcount (lua_State *L);\n\n\nstruct lua_Debug {\n  int event;\n  const char *name;\t/* (n) */\n  const char *namewhat;\t/* (n) `global', `local', `field', `method' */\n  const char *what;\t/* (S) `Lua', `C', `main', `tail' */\n  const char *source;\t/* (S) */\n  int currentline;\t/* (l) */\n  int nups;\t\t/* (u) number of upvalues */\n  int linedefined;\t/* (S) */\n  int lastlinedefined;\t/* (S) */\n  char short_src[LUA_IDSIZE]; /* (S) */\n  /* private part */\n  int i_ci;  /* active function */\n};\n\n/* }====================================================================== */\n\n\n/******************************************************************************\n* Copyright (C) 1994-2012 Lua.org, PUC-Rio.  All rights reserved.\n*\n* Permission is hereby granted, free of charge, to any person obtaining\n* a copy of this software and associated documentation files (the\n* \"Software\"), to deal in the Software without restriction, including\n* without limitation the rights to use, copy, modify, merge, publish,\n* distribute, sublicense, and/or sell copies of the Software, and to\n* permit persons to whom the Software is furnished to do so, subject to\n* the following conditions:\n*\n* The above copyright notice and this permission notice shall be\n* included in all copies or substantial portions of the Software.\n*\n* THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\n* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.\n* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY\n* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,\n* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE\n* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n******************************************************************************/\n\n\n#endif\n"
  },
  {
    "path": "src/luaconf.h",
    "content": "/*\n** $Id: luaconf.h,v 1.82.1.7 2008/02/11 16:25:08 roberto Exp $\n** Configuration file for Lua\n** See Copyright Notice in lua.h\n*/\n\n\n#ifndef lconfig_h\n#define lconfig_h\n\n#include <limits.h>\n#include <stddef.h>\n\n\n/*\n** ==================================================================\n** Search for \"@@\" to find all configurable definitions.\n** ===================================================================\n*/\n\n\n/*\n@@ LUA_ANSI controls the use of non-ansi features.\n** CHANGE it (define it) if you want Lua to avoid the use of any\n** non-ansi feature or library.\n*/\n#if defined(__STRICT_ANSI__)\n#define LUA_ANSI\n#endif\n\n\n#if !defined(LUA_ANSI) && defined(_WIN32)\n#define LUA_WIN\n#endif\n\n\n#define LUA_USE_MKSTEMP\n#define LUA_USE_ISATTY\n#define LUA_USE_POPEN\n#define LUA_USE_ULONGJMP\n\n\n/*\n@@ LUA_PATH and LUA_CPATH are the names of the environment variables that\n@* Lua check to set its paths.\n@@ LUA_INIT is the name of the environment variable that Lua\n@* checks for initialization code.\n** CHANGE them if you want different names.\n*/\n#define LUA_PATH        \"LUA_PATH\"\n#define LUA_CPATH       \"LUA_CPATH\"\n#define LUA_INIT\t\"LUA_INIT\"\n\n\n/*\n@@ LUA_PATH_DEFAULT is the default path that Lua uses to look for\n@* Lua libraries.\n@@ LUA_CPATH_DEFAULT is the default path that Lua uses to look for\n@* C libraries.\n** CHANGE them if your machine has a non-conventional directory\n** hierarchy or if you want to install your libraries in\n** non-conventional directories.\n*/\n#if defined(_WIN32)\n/*\n** In Windows, any exclamation mark ('!') in the path is replaced by the\n** path of the directory of the executable file of the current process.\n*/\n#define LUA_LDIR\t\"!\\\\lua\\\\\"\n#define LUA_CDIR\t\"!\\\\\"\n#define LUA_PATH_DEFAULT  \\\n\t\t\".\\\\?.lua;\"  LUA_LDIR\"?.lua;\"  LUA_LDIR\"?\\\\init.lua;\" \\\n\t\t             LUA_CDIR\"?.lua;\"  LUA_CDIR\"?\\\\init.lua\"\n#define LUA_CPATH_DEFAULT \\\n\t\".\\\\?.dll;\"  LUA_CDIR\"?.dll;\" LUA_CDIR\"loadall.dll\"\n\n#else\n#define LUA_ROOT\t\"/usr/local/\"\n#define LUA_LDIR\tLUA_ROOT \"share/lua/5.1/\"\n#define LUA_CDIR\tLUA_ROOT \"lib/lua/5.1/\"\n#define LUA_PATH_DEFAULT  \\\n\t\t\"./?.lua;\"  LUA_LDIR\"?.lua;\"  LUA_LDIR\"?/init.lua;\" \\\n\t\t            LUA_CDIR\"?.lua;\"  LUA_CDIR\"?/init.lua\"\n#define LUA_CPATH_DEFAULT \\\n\t\"./?.so;\"  LUA_CDIR\"?.so;\" LUA_CDIR\"loadall.so\"\n#endif\n\n\n/*\n@@ LUA_DIRSEP is the directory separator (for submodules).\n** CHANGE it if your machine does not use \"/\" as the directory separator\n** and is not Windows. (On Windows Lua automatically uses \"\\\".)\n*/\n#if defined(_WIN32)\n#define LUA_DIRSEP\t\"\\\\\"\n#else\n#define LUA_DIRSEP\t\"/\"\n#endif\n\n\n/*\n@@ LUA_PATHSEP is the character that separates templates in a path.\n@@ LUA_PATH_MARK is the string that marks the substitution points in a\n@* template.\n@@ LUA_EXECDIR in a Windows path is replaced by the executable's\n@* directory.\n@@ LUA_IGMARK is a mark to ignore all before it when bulding the\n@* luaopen_ function name.\n** CHANGE them if for some reason your system cannot use those\n** characters. (E.g., if one of those characters is a common character\n** in file/directory names.) Probably you do not need to change them.\n*/\n#define LUA_PATHSEP\t\";\"\n#define LUA_PATH_MARK\t\"?\"\n#define LUA_EXECDIR\t\"!\"\n#define LUA_IGMARK\t\"-\"\n\n\n/*\n@@ LUA_INTEGER is the integral type used by lua_pushinteger/lua_tointeger.\n** CHANGE that if ptrdiff_t is not adequate on your machine. (On most\n** machines, ptrdiff_t gives a good choice between int or long.)\n*/\n#define LUA_INTEGER\tptrdiff_t\n\n\n/*\n@@ LUA_API is a mark for all core API functions.\n@@ LUALIB_API is a mark for all standard library functions.\n** CHANGE them if you need to define those functions in some special way.\n** For instance, if you want to create one Windows DLL with the core and\n** the libraries, you may want to use the following definition (define\n** LUA_BUILD_AS_DLL to get it).\n*/\n#if defined(LUA_BUILD_AS_DLL)\n\n#if defined(LUA_CORE) || defined(LUA_LIB)\n#define LUA_API __declspec(dllexport)\n#else\n#define LUA_API __declspec(dllimport)\n#endif\n\n#else\n\n#define LUA_API\t\textern\n\n#endif\n\n/* more often than not the libs go together with the core */\n#define LUALIB_API\tLUA_API\n\n\n/*\n@@ LUAI_FUNC is a mark for all extern functions that are not to be\n@* exported to outside modules.\n@@ LUAI_DATA is a mark for all extern (const) variables that are not to\n@* be exported to outside modules.\n** CHANGE them if you need to mark them in some special way. Elf/gcc\n** (versions 3.2 and later) mark them as \"hidden\" to optimize access\n** when Lua is compiled as a shared library.\n*/\n#if defined(luaall_c)\n#define LUAI_FUNC\tstatic\n#define LUAI_DATA\t/* empty */\n\n#elif defined(__GNUC__) && ((__GNUC__*100 + __GNUC_MINOR__) >= 302) && \\\n      defined(__ELF__)\n#define LUAI_FUNC\t__attribute__((visibility(\"hidden\"))) extern\n#define LUAI_DATA\tLUAI_FUNC\n\n#else\n#define LUAI_FUNC\textern\n#define LUAI_DATA\textern\n#endif\n\n\n\n/*\n@@ LUA_QL describes how error messages quote program elements.\n** CHANGE it if you want a different appearance.\n*/\n#define LUA_QL(x)\t\"'\" x \"'\"\n#define LUA_QS\t\tLUA_QL(\"%s\")\n\n\n/*\n@@ LUA_IDSIZE gives the maximum size for the description of the source\n@* of a function in debug information.\n** CHANGE it if you want a different size.\n*/\n#define LUA_IDSIZE\t60\n\n\n/*\n** {==================================================================\n** Stand-alone configuration\n** ===================================================================\n*/\n\n#if defined(lua_c) || defined(luaall_c)\n\n\n/*\n@@ LUA_PROMPT is the default prompt used by stand-alone Lua.\n@@ LUA_PROMPT2 is the default continuation prompt used by stand-alone Lua.\n** CHANGE them if you want different prompts. (You can also change the\n** prompts dynamically, assigning to globals _PROMPT/_PROMPT2.)\n*/\n#define LUA_PROMPT\t\t\"> \"\n#define LUA_PROMPT2\t\t\">> \"\n\n\n/*\n@@ LUA_PROGNAME is the default name for the stand-alone Lua program.\n** CHANGE it if your stand-alone interpreter has a different name and\n** your system is not able to detect that name automatically.\n*/\n#define LUA_PROGNAME\t\t\"teliva\"\n\n\n/*\n@@ LUA_MAXINPUT is the maximum length for an input line in the\n@* stand-alone interpreter.\n** CHANGE it if you need longer lines.\n*/\n#define LUA_MAXINPUT\t512\n\n\n#endif\n\n/* }================================================================== */\n\n\n/*\n@@ LUAI_GCPAUSE defines the default pause between garbage-collector cycles\n@* as a percentage.\n** CHANGE it if you want the GC to run faster or slower (higher values\n** mean larger pauses which mean slower collection.) You can also change\n** this value dynamically.\n*/\n#define LUAI_GCPAUSE\t200  /* 200% (wait memory to double before next GC) */\n\n\n/*\n@@ LUAI_GCMUL defines the default speed of garbage collection relative to\n@* memory allocation as a percentage.\n** CHANGE it if you want to change the granularity of the garbage\n** collection. (Higher values mean coarser collections. 0 represents\n** infinity, where each step performs a full collection.) You can also\n** change this value dynamically.\n*/\n#define LUAI_GCMUL\t200 /* GC runs 'twice the speed' of memory allocation */\n\n\n\n/*\n@@ LUA_COMPAT_GETN controls compatibility with old getn behavior.\n** CHANGE it (define it) if you want exact compatibility with the\n** behavior of setn/getn in Lua 5.0.\n*/\n#undef LUA_COMPAT_GETN\n\n/*\n@@ LUA_COMPAT_VARARG controls compatibility with old vararg feature.\n** CHANGE it to undefined as soon as your programs use only '...' to\n** access vararg parameters (instead of the old 'arg' table).\n*/\n#define LUA_COMPAT_VARARG\n\n/*\n@@ LUA_COMPAT_MOD controls compatibility with old math.mod function.\n** CHANGE it to undefined as soon as your programs use 'math.fmod' or\n** the new '%' operator instead of 'math.mod'.\n*/\n#define LUA_COMPAT_MOD\n\n/*\n@@ LUA_COMPAT_LSTR controls compatibility with old long string nesting\n@* facility.\n** CHANGE it to 2 if you want the old behaviour, or undefine it to turn\n** off the advisory error when nesting [[...]].\n*/\n#define LUA_COMPAT_LSTR\t\t1\n\n/*\n@@ LUA_COMPAT_GFIND controls compatibility with old 'string.gfind' name.\n** CHANGE it to undefined as soon as you rename 'string.gfind' to\n** 'string.gmatch'.\n*/\n#define LUA_COMPAT_GFIND\n\n/*\n@@ LUA_COMPAT_OPENLIB controls compatibility with old 'luaL_openlib'\n@* behavior.\n** CHANGE it to undefined as soon as you replace to 'luaL_register'\n** your uses of 'luaL_openlib'\n*/\n#define LUA_COMPAT_OPENLIB\n\n\n\n/*\n@@ luai_apicheck is the assert macro used by the Lua-C API.\n** CHANGE luai_apicheck if you want Lua to perform some checks in the\n** parameters it gets from API calls. This may slow down the interpreter\n** a bit, but may be quite useful when debugging C code that interfaces\n** with Lua. A useful redefinition is to use assert.h.\n*/\n#if defined(LUA_USE_APICHECK)\n#include <assert.h>\n#define luai_apicheck(L,o)\t{ (void)L; assert(o); }\n#else\n#define luai_apicheck(L,o)\t{ (void)L; }\n#endif\n\n\n/*\n@@ LUAI_BITSINT defines the number of bits in an int.\n** CHANGE here if Lua cannot automatically detect the number of bits of\n** your machine. Probably you do not need to change this.\n*/\n/* avoid overflows in comparison */\n#if INT_MAX-20 < 32760\n#define LUAI_BITSINT\t16\n#elif INT_MAX > 2147483640L\n/* int has at least 32 bits */\n#define LUAI_BITSINT\t32\n#else\n#error \"you must define LUA_BITSINT with number of bits in an integer\"\n#endif\n\n\n/*\n@@ LUAI_UINT32 is an unsigned integer with at least 32 bits.\n@@ LUAI_INT32 is an signed integer with at least 32 bits.\n@@ LUAI_UMEM is an unsigned integer big enough to count the total\n@* memory used by Lua.\n@@ LUAI_MEM is a signed integer big enough to count the total memory\n@* used by Lua.\n** CHANGE here if for some weird reason the default definitions are not\n** good enough for your machine. (The definitions in the 'else'\n** part always works, but may waste space on machines with 64-bit\n** longs.) Probably you do not need to change this.\n*/\n#if LUAI_BITSINT >= 32\n#define LUAI_UINT32\tunsigned int\n#define LUAI_INT32\tint\n#define LUAI_MAXINT32\tINT_MAX\n#define LUAI_UMEM\tsize_t\n#define LUAI_MEM\tptrdiff_t\n#else\n/* 16-bit ints */\n#define LUAI_UINT32\tunsigned long\n#define LUAI_INT32\tlong\n#define LUAI_MAXINT32\tLONG_MAX\n#define LUAI_UMEM\tunsigned long\n#define LUAI_MEM\tlong\n#endif\n\n\n/*\n@@ LUAI_MAXCALLS limits the number of nested calls.\n** CHANGE it if you need really deep recursive calls. This limit is\n** arbitrary; its only purpose is to stop infinite recursion before\n** exhausting memory.\n*/\n#define LUAI_MAXCALLS\t20000\n\n\n/*\n@@ LUAI_MAXCSTACK limits the number of Lua stack slots that a C function\n@* can use.\n** CHANGE it if you need lots of (Lua) stack space for your C\n** functions. This limit is arbitrary; its only purpose is to stop C\n** functions to consume unlimited stack space. (must be smaller than\n** -LUA_REGISTRYINDEX)\n*/\n#define LUAI_MAXCSTACK\t8000\n\n\n\n/*\n** {==================================================================\n** CHANGE (to smaller values) the following definitions if your system\n** has a small C stack. (Or you may want to change them to larger\n** values if your system has a large C stack and these limits are\n** too rigid for you.) Some of these constants control the size of\n** stack-allocated arrays used by the compiler or the interpreter, while\n** others limit the maximum number of recursive calls that the compiler\n** or the interpreter can perform. Values too large may cause a C stack\n** overflow for some forms of deep constructs.\n** ===================================================================\n*/\n\n\n/*\n@@ LUAI_MAXCCALLS is the maximum depth for nested C calls (short) and\n@* syntactical nested non-terminals in a program.\n*/\n#define LUAI_MAXCCALLS\t\t200\n\n\n/*\n@@ LUAI_MAXVARS is the maximum number of local variables per function\n@* (must be smaller than 250).\n*/\n#define LUAI_MAXVARS\t\t200\n\n\n/*\n@@ LUAI_MAXUPVALUES is the maximum number of upvalues per function\n@* (must be smaller than 250).\n*/\n#define LUAI_MAXUPVALUES\t60\n\n\n/*\n@@ LUAL_BUFFERSIZE is the buffer size used by the lauxlib buffer system.\n*/\n#define LUAL_BUFFERSIZE\t\tBUFSIZ\n\n/* }================================================================== */\n\n\n\n\n/*\n** {==================================================================\n@@ LUA_NUMBER is the type of numbers in Lua.\n** CHANGE the following definitions only if you want to build Lua\n** with a number type different from double. You may also need to\n** change lua_number2int & lua_number2integer.\n** ===================================================================\n*/\n\n#define LUA_NUMBER_DOUBLE\n#define LUA_NUMBER\tdouble\n\n/*\n@@ LUAI_UACNUMBER is the result of an 'usual argument conversion'\n@* over a number.\n*/\n#define LUAI_UACNUMBER\tdouble\n\n\n/*\n@@ LUA_NUMBER_SCAN is the format for reading numbers.\n@@ LUA_NUMBER_FMT is the format for writing numbers.\n@@ lua_number2str converts a number to a string.\n@@ LUAI_MAXNUMBER2STR is maximum size of previous conversion.\n@@ lua_str2number converts a string to a number.\n*/\n#define LUA_NUMBER_SCAN\t\t\"%lf\"\n#define LUA_NUMBER_FMT\t\t\"%.14g\"\n#define lua_number2str(s,n)\tsprintf((s), LUA_NUMBER_FMT, (n))\n#define LUAI_MAXNUMBER2STR\t32 /* 16 digits, sign, point, and \\0 */\n#define lua_str2number(s,p)\tstrtod((s), (p))\n\n\n/*\n@@ The luai_num* macros define the primitive operations over numbers.\n*/\n#if defined(LUA_CORE)\n#include <math.h>\n#define luai_numadd(a,b)\t((a)+(b))\n#define luai_numsub(a,b)\t((a)-(b))\n#define luai_nummul(a,b)\t((a)*(b))\n#define luai_numdiv(a,b)\t((a)/(b))\n#define luai_nummod(a,b)\t((a) - floor((a)/(b))*(b))\n#define luai_numpow(a,b)\t(pow(a,b))\n#define luai_numunm(a)\t\t(-(a))\n#define luai_numeq(a,b)\t\t((a)==(b))\n#define luai_numlt(a,b)\t\t((a)<(b))\n#define luai_numle(a,b)\t\t((a)<=(b))\n#define luai_numisnan(a)\t(!luai_numeq((a), (a)))\n#endif\n\n\n/*\n@@ lua_number2int is a macro to convert lua_Number to int.\n@@ lua_number2integer is a macro to convert lua_Number to lua_Integer.\n** CHANGE them if you know a faster way to convert a lua_Number to\n** int (with any rounding method and without throwing errors) in your\n** system. In Pentium machines, a naive typecast from double to int\n** in C is extremely slow, so any alternative is worth trying.\n*/\n\n/* On a Pentium, resort to a trick */\n#if defined(LUA_NUMBER_DOUBLE) && !defined(LUA_ANSI) && !defined(__SSE2__) && \\\n    (defined(__i386) || defined (_M_IX86) || defined(__i386__))\n\n/* On a Microsoft compiler, use assembler */\n#if defined(_MSC_VER)\n\n#define lua_number2int(i,d)   __asm fld d   __asm fistp i\n#define lua_number2integer(i,n)\t\tlua_number2int(i, n)\n\n/* the next trick should work on any Pentium, but sometimes clashes\n   with a DirectX idiosyncrasy */\n#else\n\nunion luai_Cast { double l_d; long l_l; };\n#define lua_number2int(i,d) \\\n  { volatile union luai_Cast u; u.l_d = (d) + 6755399441055744.0; (i) = u.l_l; }\n#define lua_number2integer(i,n)\t\tlua_number2int(i, n)\n\n#endif\n\n\n/* this option always works, but may be slow */\n#else\n#define lua_number2int(i,d)\t((i)=(int)(d))\n#define lua_number2integer(i,d)\t((i)=(lua_Integer)(d))\n\n#endif\n\n/* }================================================================== */\n\n\n/*\n@@ LUAI_USER_ALIGNMENT_T is a type that requires maximum alignment.\n** CHANGE it if your system requires alignments larger than double. (For\n** instance, if your system supports long doubles and they must be\n** aligned in 16-byte boundaries, then you should add long double in the\n** union.) Probably you do not need to change this.\n*/\n#define LUAI_USER_ALIGNMENT_T\tunion { double u; void *s; long l; }\n\n\n/*\n@@ LUAI_THROW/LUAI_TRY define how Lua does exception handling.\n** CHANGE them if you prefer to use longjmp/setjmp even with C++\n** or if want/don't to use _longjmp/_setjmp instead of regular\n** longjmp/setjmp. By default, Lua handles errors with exceptions when\n** compiling as C++ code, with _longjmp/_setjmp when asked to use them,\n** and with longjmp/setjmp otherwise.\n*/\n#if defined(__cplusplus)\n/* C++ exceptions */\n#define LUAI_THROW(L,c)\tthrow(c)\n#define LUAI_TRY(L,c,a)\ttry { a } catch(...) \\\n\t{ if ((c)->status == 0) (c)->status = -1; }\n#define luai_jmpbuf\tint  /* dummy variable */\n\n#elif defined(LUA_USE_ULONGJMP)\n/* in Unix, try _longjmp/_setjmp (more efficient) */\n#define LUAI_THROW(L,c)\t_longjmp((c)->b, 1)\n#define LUAI_TRY(L,c,a)\tif (_setjmp((c)->b) == 0) { a }\n#define luai_jmpbuf\tjmp_buf\n\n#else\n/* default handling with long jumps */\n#define LUAI_THROW(L,c)\tlongjmp((c)->b, 1)\n#define LUAI_TRY(L,c,a)\tif (setjmp((c)->b) == 0) { a }\n#define luai_jmpbuf\tjmp_buf\n\n#endif\n\n\n/*\n@@ LUA_MAXCAPTURES is the maximum number of captures that a pattern\n@* can do during pattern-matching.\n** CHANGE it if you need more captures. This limit is arbitrary.\n*/\n#define LUA_MAXCAPTURES\t\t32\n\n\n/*\n@@ lua_tmpnam is the function that the OS library uses to create a\n@* temporary name.\n@@ LUA_TMPNAMBUFSIZE is the maximum size of a name created by lua_tmpnam.\n** CHANGE them if you have an alternative to tmpnam (which is considered\n** insecure) or if you want the original tmpnam anyway.  By default, Lua\n** uses tmpnam except when POSIX is available, where it uses mkstemp.\n*/\n#if defined(loslib_c) || defined(luaall_c)\n\n#if defined(LUA_USE_MKSTEMP)\n#include <unistd.h>\n/* we have newer libraries even though the dialect is C99 */\nextern int mkstemp(char *);\n#define LUA_TMPNAMBUFSIZE\t32\n#define lua_tmpnam(b,e)\t{ \\\n\tstrcpy(b, \"/tmp/lua_XXXXXX\"); \\\n\te = mkstemp(b); \\\n\tif (e != -1) close(e); \\\n\te = (e == -1); }\n\n#else\n#define LUA_TMPNAMBUFSIZE\tL_tmpnam\n#define lua_tmpnam(b,e)\t\t{ e = (tmpnam(b) == NULL); }\n#endif\n\n#endif\n\n\n/*\n@@ LUA_INTFRMLEN is the length modifier for integer conversions\n@* in 'string.format'.\n@@ LUA_INTFRM_T is the integer type correspoding to the previous length\n@* modifier.\n** CHANGE them if your system supports long long or does not support long.\n*/\n\n#if defined(LUA_USELONGLONG)\n\n#define LUA_INTFRMLEN\t\t\"ll\"\n#define LUA_INTFRM_T\t\tlong long\n\n#else\n\n#define LUA_INTFRMLEN\t\t\"l\"\n#define LUA_INTFRM_T\t\tlong\n\n#endif\n\n\n\n/* =================================================================== */\n\n/*\n** Local configuration. You can use this space to add your redefinitions\n** without modifying the main part of the file.\n*/\n\n\n\n#endif\n\n"
  },
  {
    "path": "src/lualib.h",
    "content": "/*\n** $Id: lualib.h,v 1.36.1.1 2007/12/27 13:02:25 roberto Exp $\n** Lua standard libraries\n** See Copyright Notice in lua.h\n*/\n\n\n#ifndef lualib_h\n#define lualib_h\n\n#include \"lua.h\"\n\n\n/* Key to file-handle type */\n#define LUA_FILEHANDLE\t\t\"FILE*\"\n\n\n#define LUA_COLIBNAME\t\"coroutine\"\nLUALIB_API int (luaopen_base) (lua_State *L);\n\n#define LUA_TABLIBNAME\t\"table\"\nLUALIB_API int (luaopen_table) (lua_State *L);\n\n#define LUA_IOLIBNAME\t\"io\"\nLUALIB_API int (luaopen_io) (lua_State *L);\n\n#define LUA_OSLIBNAME\t\"os\"\nLUALIB_API int (luaopen_os) (lua_State *L);\n\n#define LUA_STRLIBNAME\t\"string\"\nLUALIB_API int (luaopen_string) (lua_State *L);\n\n#define LUA_MATHLIBNAME\t\"math\"\nLUALIB_API int (luaopen_math) (lua_State *L);\n\n#define LUA_CURSESLIBNAME\t\"curses\"\nLUALIB_API int (luaopen_curses) (lua_State *L);\n\n#define LUA_SOCKETCORELIBNAME\t\"socket\"\nLUALIB_API int (luaopen_socket_core) (lua_State *L);\n\n#define LUA_MIMECORELIBNAME\t\"mime\"\nLUALIB_API int (luaopen_mime_core) (lua_State *L);\n\n#define LUA_SSLLIBNAME\t\"ssl\"\nLUALIB_API int (luaopen_ssl_core) (lua_State *L);\n\n#define LUA_SSLCONTEXTLIBNAME\t\"context\"\nLUALIB_API int (luaopen_ssl_context) (lua_State *L);\n\n#define LUA_SSLX509LIBNAME\t\"x509\"\nLUALIB_API int (luaopen_ssl_x509) (lua_State *L);\n\n#define LUA_SSLCONFIGLIBNAME\t\"config\"\nLUALIB_API int (luaopen_ssl_config) (lua_State *L);\n\n#define LUA_DBLIBNAME\t\"debug\"\nLUALIB_API int (luaopen_debug) (lua_State *L);\n\n\n/* open all previous libraries */\nLUALIB_API void (luaL_openlibs) (lua_State *L); \n\n\n\n#ifndef lua_assert\n#define lua_assert(x)\t((void)0)\n#endif\n\n\n#endif\n"
  },
  {
    "path": "src/luasec/Makefile",
    "content": "CMOD=ssl.a\nLMOD=ssl.lua\n\nOBJS= \\\n options.o \\\n x509.o    \\\n context.o \\\n ssl.o     \\\n config.o  \\\n ec.o\n\nWARN=-Wall\n\nBSD_CFLAGS=-O2 -fPIC $(WARN) $(DEFS)\nBSD_LDFLAGS=-O -fPIC -shared\n\nlinux_CFLAGS=-g -O2 -std=c99 $(WARN) -Wpedantic $(DEFS)\n\nmacosx_ENV=env MACOSX_DEPLOYMENT_TARGET='$(MACVER)'\nmacosx_CFLAGS=-g -O2 -std=c99 -fno-common $(WARN) -Wpedantic $(DEFS) -I/usr/local/opt/openssl@3/include\nmacosx_LDFLAGS=-bundle -undefined dynamic_lookup -L/usr/local/opt/openssl@3/lib\n\nINSTALL  = install\nCC       = gcc\nCCLD      ?= $(MYENV) $(CC)\nCFLAGS  = $(MYCFLAGS)\nLDFLAGS += $(MYLDFLAGS)\nAR= ar rc\nRANLIB= ranlib\n\n.PHONY: all clean install none linux bsd macosx\n\nall:\n\ninstall: $(CMOD) $(LMOD)\n\t$(INSTALL) -d $(DESTDIR)$(LUAPATH)/ssl $(DESTDIR)$(LUACPATH)\n\t$(INSTALL) $(CMOD) $(DESTDIR)$(LUACPATH)\n\t$(INSTALL) -m644 $(LMOD) $(DESTDIR)$(LUAPATH)\n\t$(INSTALL) -m644 https.lua $(DESTDIR)$(LUAPATH)/ssl\n\nlinux:\n\t@$(MAKE) $(CMOD) MYCFLAGS=\"$(linux_CFLAGS)\" MYLDFLAGS=\"$(linux_LDFLAGS)\" EXTRA=\"$(EXTRA)\"\n\nbsd:\n\t@$(MAKE) $(CMOD) MYCFLAGS=\"$(BSD_CFLAGS)\" MYLDFLAGS=\"$(BSD_LDFLAGS)\" EXTRA=\"$(EXTRA)\"\n\nmacosx:\n\t@$(MAKE) $(CMOD) MYCFLAGS=\"$(macosx_CFLAGS)\" MYLDFLAGS=\"$(macosx_LDFLAGS)\" MYENV=\"$(macosx_ENV)\" EXTRA=\"$(EXTRA)\"\n\n$(CMOD): $(OBJS)\n\t$(AR) $(CMOD) $(OBJS)\n\t$(RANLIB) $(CMOD)\n\nclean:\n\trm -f $(OBJS) $(CMOD)\n\noptions.o: options.h options.c\nec.o: ec.c ec.h\nx509.o: x509.c x509.h compat.h\ncontext.o: context.c context.h ec.h compat.h options.h\nssl.o: ssl.c ssl.h context.h x509.h compat.h\nconfig.o: config.c ec.h options.h compat.h\n"
  },
  {
    "path": "src/luasec/compat.h",
    "content": "/*--------------------------------------------------------------------------\n * LuaSec 1.0.2\n *\n * Copyright (C) 2006-2021 Bruno Silvestre\n *\n *--------------------------------------------------------------------------*/\n\n#ifndef LSEC_COMPAT_H\n#define LSEC_COMPAT_H\n\n#include <openssl/ssl.h>\n\n//------------------------------------------------------------------------------\n\n#if defined(_WIN32)\n#define LSEC_API __declspec(dllexport) \n#else\n#define LSEC_API extern\n#endif\n\n//------------------------------------------------------------------------------\n\n#define setfuncs(L, R)    luaL_register(L, NULL, R)\n#define lua_rawlen(L, i)  lua_objlen(L, i)\n\n#ifndef luaL_newlib\n#define luaL_newlib(L, R) do { lua_newtable(L); luaL_register(L, NULL, R); } while(0)\n#endif\n\n//------------------------------------------------------------------------------\n\n#if (!defined(LIBRESSL_VERSION_NUMBER) && (OPENSSL_VERSION_NUMBER >= 0x1010000fL))\n#define LSEC_ENABLE_DANE\n#endif\n\n//------------------------------------------------------------------------------\n\n#if !((defined(LIBRESSL_VERSION_NUMBER) && (LIBRESSL_VERSION_NUMBER < 0x2070000fL)) || (OPENSSL_VERSION_NUMBER < 0x1010000fL))\n#define LSEC_API_OPENSSL_1_1_0\n#endif\n\n//------------------------------------------------------------------------------\n\n#if !defined(LIBRESSL_VERSION_NUMBER) && ((OPENSSL_VERSION_NUMBER & 0xFFFFF000L) == 0x10101000L)\n#define LSEC_OPENSSL_1_1_1\n#endif\n\n//------------------------------------------------------------------------------\n\n#endif\n"
  },
  {
    "path": "src/luasec/config.c",
    "content": "/*--------------------------------------------------------------------------\n * LuaSec 1.0.2\n *\n * Copyright (C) 2006-2021 Bruno Silvestre.\n *\n *--------------------------------------------------------------------------*/\n\n#include \"compat.h\"\n#include \"options.h\"\n#include \"ec.h\"\n\n/**\n * Registre the module.\n */\nLSEC_API int luaopen_ssl_config(lua_State *L)\n{\n  lsec_ssl_option_t *opt;\n\n  lua_newtable(L);\n  lua_pushvalue(L, -1);\n  lua_setglobal(L, \"config\");\n\n  // Options\n  lua_pushstring(L, \"options\");\n  lua_newtable(L);\n  for (opt = lsec_get_ssl_options(); opt->name; opt++) {\n    lua_pushstring(L, opt->name);\n    lua_pushboolean(L, 1);\n    lua_rawset(L, -3);\n  }\n  lua_rawset(L, -3);\n\n  // Protocols\n  lua_pushstring(L, \"protocols\");\n  lua_newtable(L);\n\n  lua_pushstring(L, \"tlsv1\");\n  lua_pushboolean(L, 1);\n  lua_rawset(L, -3);\n  lua_pushstring(L, \"tlsv1_1\");\n  lua_pushboolean(L, 1);\n  lua_rawset(L, -3);\n  lua_pushstring(L, \"tlsv1_2\");\n  lua_pushboolean(L, 1);\n  lua_rawset(L, -3);\n#ifdef TLS1_3_VERSION\n  lua_pushstring(L, \"tlsv1_3\");\n  lua_pushboolean(L, 1);\n  lua_rawset(L, -3);\n#endif\n\n  lua_rawset(L, -3);\n\n  // Algorithms\n  lua_pushstring(L, \"algorithms\");\n  lua_newtable(L);\n\n#ifndef OPENSSL_NO_EC\n  lua_pushstring(L, \"ec\");\n  lua_pushboolean(L, 1);\n  lua_rawset(L, -3);\n#endif\n  lua_rawset(L, -3);\n \n  // Curves\n  lua_pushstring(L, \"curves\");\n  lsec_get_curves(L);\n  lua_rawset(L, -3);\n\n  // Capabilities\n  lua_pushstring(L, \"capabilities\");\n  lua_newtable(L);\n\n  // ALPN\n  lua_pushstring(L, \"alpn\");\n  lua_pushboolean(L, 1);\n  lua_rawset(L, -3);\n\n#ifdef LSEC_ENABLE_DANE\n  // DANE\n  lua_pushstring(L, \"dane\");\n  lua_pushboolean(L, 1);\n  lua_rawset(L, -3);\n#endif\n\n#ifndef OPENSSL_NO_EC\n  lua_pushstring(L, \"curves_list\");\n  lua_pushboolean(L, 1);\n  lua_rawset(L, -3);\n  lua_pushstring(L, \"ecdh_auto\");\n  lua_pushboolean(L, 1);\n  lua_rawset(L, -3);\n#endif\n  lua_rawset(L, -3);\n\n  return 1;\n}\n"
  },
  {
    "path": "src/luasec/context.c",
    "content": "/*--------------------------------------------------------------------------\n * LuaSec 1.0.2\n *\n * Copyright (C) 2014-2021 Kim Alvefur, Paul Aurich, Tobias Markmann,\n *                         Matthew Wild.\n * Copyright (C) 2006-2021 Bruno Silvestre.\n *\n *--------------------------------------------------------------------------*/\n\n#include <string.h>\n\n#if defined(WIN32)\n#include <windows.h>\n#endif\n\n#include <openssl/ssl.h>\n#include <openssl/err.h>\n#include <openssl/x509.h>\n#include <openssl/x509v3.h>\n#include <openssl/dh.h>\n\n#include \"../lua.h\"\n#include \"../lauxlib.h\"\n\n#include \"compat.h\"\n#include \"context.h\"\n#include \"options.h\"\n\n#ifndef OPENSSL_NO_EC\n#include <openssl/ec.h>\n#include \"ec.h\"\n#endif\n\n/*--------------------------- Auxiliary Functions ----------------------------*/\n\n/**\n * Return the context.\n */\nstatic p_context checkctx(lua_State *L, int idx)\n{\n  return (p_context)luaL_checkudata(L, idx, \"SSL:Context\");\n}\n\nstatic p_context testctx(lua_State *L, int idx)\n{\n  return (p_context)luasocket_testudata(L, idx, \"SSL:Context\");\n}\n\n/**\n * Prepare the SSL options flag.\n */\nstatic int set_option_flag(const char *opt, unsigned long *flag)\n{\n  lsec_ssl_option_t *p;\n  for (p = lsec_get_ssl_options(); p->name; p++) {\n    if (!strcmp(opt, p->name)) {\n      *flag |= p->code;\n      return 1;\n    }\n  }\n  return 0;\n}\n\n#ifndef LSEC_API_OPENSSL_1_1_0\n/**\n * Find the protocol.\n */\nstatic const SSL_METHOD* str2method(const char *method, int *vmin, int *vmax)\n{\n  (void)vmin;\n  (void)vmax;\n  if (!strcmp(method, \"any\"))     return SSLv23_method();\n  if (!strcmp(method, \"sslv23\"))  return SSLv23_method();  // deprecated\n  if (!strcmp(method, \"tlsv1\"))   return TLSv1_method();\n  if (!strcmp(method, \"tlsv1_1\")) return TLSv1_1_method();\n  if (!strcmp(method, \"tlsv1_2\")) return TLSv1_2_method();\n  return NULL;\n}\n\n#else\n\n/**\n * Find the protocol.\n */\nstatic const SSL_METHOD* str2method(const char *method, int *vmin, int *vmax)\n{\n  if (!strcmp(method, \"any\") || !strcmp(method, \"sslv23\")) { // 'sslv23' is deprecated\n    *vmin = 0;\n    *vmax = 0;\n    return TLS_method();\n  }\n  else if (!strcmp(method, \"tlsv1\")) {\n    *vmin = TLS1_VERSION;\n    *vmax = TLS1_VERSION;\n    return TLS_method();\n  }\n  else if (!strcmp(method, \"tlsv1_1\")) {\n    *vmin = TLS1_1_VERSION;\n    *vmax = TLS1_1_VERSION;\n    return TLS_method();\n  }\n  else if (!strcmp(method, \"tlsv1_2\")) {\n    *vmin = TLS1_2_VERSION;\n    *vmax = TLS1_2_VERSION;\n    return TLS_method();\n  }\n#if defined(TLS1_3_VERSION)\n  else if (!strcmp(method, \"tlsv1_3\")) {\n    *vmin = TLS1_3_VERSION;\n    *vmax = TLS1_3_VERSION;\n    return TLS_method();\n  }\n#endif\n  return NULL;\n}\n#endif\n\n/**\n * Prepare the SSL handshake verify flag.\n */\nstatic int set_verify_flag(const char *str, int *flag)\n{\n  if (!strcmp(str, \"none\")) {\n    *flag |= SSL_VERIFY_NONE;\n    return 1;\n  }\n  if (!strcmp(str, \"peer\")) {\n    *flag |= SSL_VERIFY_PEER;\n    return 1;\n  }\n  if (!strcmp(str, \"client_once\")) {\n    *flag |= SSL_VERIFY_CLIENT_ONCE;\n    return 1;\n  }\n  if (!strcmp(str, \"fail_if_no_peer_cert\")) {\n    *flag |= SSL_VERIFY_FAIL_IF_NO_PEER_CERT;\n    return 1;\n  }\n  return 0;\n}\n\n/**\n * Password callback for reading the private key.\n */\nstatic int passwd_cb(char *buf, int size, int flag, void *udata)\n{\n  lua_State *L = (lua_State*)udata;\n  switch (lua_type(L, 3)) {\n  case LUA_TFUNCTION:\n    lua_pushvalue(L, 3);\n    lua_call(L, 0, 1);\n    if (lua_type(L, -1) != LUA_TSTRING) {\n       lua_pop(L, 1);  /* Remove the result from the stack */\n       return 0;\n    }\n    /* fall through */\n  case LUA_TSTRING:\n    strncpy(buf, lua_tostring(L, -1), size);\n    lua_pop(L, 1);  /* Remove the result from the stack */\n    buf[size-1] = '\\0';\n    return (int)strlen(buf);\n  }\n  return 0;\n}\n\n/**\n * Add an error related to a depth certificate of the chain.\n */\nstatic void add_cert_error(lua_State *L, SSL *ssl, int err, int depth)\n{\n  luaL_getmetatable(L, \"SSL:Verify:Registry\");\n  lua_pushlightuserdata(L, (void*)ssl);\n  lua_gettable(L, -2);\n  if (lua_isnil(L, -1)) {\n    lua_pop(L, 1);\n    /* Create an error table for this connection */\n    lua_newtable(L);\n    lua_pushlightuserdata(L, (void*)ssl);\n    lua_pushvalue(L, -2);  /* keep the table on stack */\n    lua_settable(L, -4);\n  }\n  lua_rawgeti(L, -1, depth+1);\n  /* If the table doesn't exist, create it */\n  if (lua_isnil(L, -1)) {\n    lua_pop(L, 1);               /* remove 'nil' from stack */\n    lua_newtable(L);\n    lua_pushvalue(L, -1);        /* keep the table on stack */\n    lua_rawseti(L, -3, depth+1);\n  }\n  lua_pushstring(L, X509_verify_cert_error_string(err));\n  lua_rawseti(L, -2, lua_rawlen(L, -2) + 1);\n  /* Clear the stack */\n  lua_pop(L, 3);\n}\n\n/**\n * Call Lua user function to get the DH key.\n */\nstatic DH *dhparam_cb(SSL *ssl, int is_export, int keylength)\n{\n  BIO *bio;\n  lua_State *L;\n  SSL_CTX *ctx = SSL_get_SSL_CTX(ssl);\n  p_context pctx = (p_context)SSL_CTX_get_app_data(ctx);\n\n  L = pctx->L;\n\n  /* Get the callback */\n  luaL_getmetatable(L, \"SSL:DH:Registry\");\n  lua_pushlightuserdata(L, (void*)ctx);\n  lua_gettable(L, -2);\n\n  /* Invoke the callback */\n  lua_pushboolean(L, is_export);\n  lua_pushnumber(L, keylength);\n  lua_call(L, 2, 1);\n\n  /* Load parameters from returned value */\n  if (lua_type(L, -1) != LUA_TSTRING) {\n    lua_pop(L, 2);  /* Remove values from stack */\n    return NULL;\n  }\n\n  bio = BIO_new_mem_buf((void*)lua_tostring(L, -1), lua_rawlen(L, -1));\n  if (bio) {\n    pctx->dh_param = PEM_read_bio_DHparams(bio, NULL, NULL, NULL);\n    BIO_free(bio);\n  }\n\n  lua_pop(L, 2);    /* Remove values from stack */\n  return pctx->dh_param;\n}\n\n/**\n * Set the \"ignore purpose\" before to start verifing the certificate chain.\n */\nstatic int cert_verify_cb(X509_STORE_CTX *x509_ctx, void *ptr)\n{\n  int verify;\n  lua_State *L;\n  SSL_CTX *ctx = (SSL_CTX*)ptr;\n  p_context pctx = (p_context)SSL_CTX_get_app_data(ctx);\n\n  L = pctx->L;\n\n  /* Get verify flags */\n  luaL_getmetatable(L, \"SSL:Verify:Registry\");\n  lua_pushlightuserdata(L, (void*)ctx);\n  lua_gettable(L, -2);\n  verify = (int)lua_tonumber(L, -1);\n\n  lua_pop(L, 2); /* Remove values from stack */\n\n  if (verify & LSEC_VERIFY_IGNORE_PURPOSE) {\n    /* Set parameters to ignore the server purpose */\n    X509_VERIFY_PARAM *param = X509_STORE_CTX_get0_param(x509_ctx);\n    if (param) {\n      X509_VERIFY_PARAM_set_purpose(param, X509_PURPOSE_SSL_SERVER);\n      X509_VERIFY_PARAM_set_trust(param, X509_TRUST_SSL_SERVER);\n    }\n  }\n  /* Call OpenSSL standard verification function */\n  return X509_verify_cert(x509_ctx);\n}\n\n/**\n * This callback implements the \"continue on error\" flag and log the errors.\n */\nstatic int verify_cb(int preverify_ok, X509_STORE_CTX *x509_ctx)\n{\n  int err;\n  int verify;\n  SSL *ssl;\n  SSL_CTX *ctx;\n  p_context pctx;\n  lua_State *L;\n\n  /* Short-circuit optimization */\n  if (preverify_ok)\n    return 1;\n\n  ssl = X509_STORE_CTX_get_ex_data(x509_ctx,\n    SSL_get_ex_data_X509_STORE_CTX_idx());\n  ctx = SSL_get_SSL_CTX(ssl);\n  pctx = (p_context)SSL_CTX_get_app_data(ctx);\n  L = pctx->L;\n\n  /* Get verify flags */\n  luaL_getmetatable(L, \"SSL:Verify:Registry\");\n  lua_pushlightuserdata(L, (void*)ctx);\n  lua_gettable(L, -2);\n  verify = (int)lua_tonumber(L, -1);\n\n  lua_pop(L, 2); /* Remove values from stack */\n\n  err = X509_STORE_CTX_get_error(x509_ctx);\n  if (err != X509_V_OK)\n    add_cert_error(L, ssl, err, X509_STORE_CTX_get_error_depth(x509_ctx));\n\n  return (verify & LSEC_VERIFY_CONTINUE ? 1 : preverify_ok);\n}\n\n/*------------------------------ Lua Functions -------------------------------*/\n\n/**\n * Create a SSL context.\n */\nstatic int create(lua_State *L)\n{\n  p_context ctx;\n  const char *str_method;\n  const SSL_METHOD *method;\n  int vmin, vmax;\n\n  str_method = luaL_checkstring(L, 1);\n  method = str2method(str_method, &vmin, &vmax);\n  if (!method) {\n    lua_pushnil(L);\n    lua_pushfstring(L, \"invalid protocol (%s)\", str_method);\n    return 2;\n  }\n  ctx = (p_context) lua_newuserdata(L, sizeof(t_context));\n  if (!ctx) {\n    lua_pushnil(L);\n    lua_pushstring(L, \"error creating context\");\n    return 2;\n  }\n  memset(ctx, 0, sizeof(t_context));\n  ctx->context = SSL_CTX_new(method);\n  if (!ctx->context) {\n    lua_pushnil(L);\n    lua_pushfstring(L, \"error creating context (%s)\",\n      ERR_reason_error_string(ERR_get_error()));\n    return 2;\n  }\n#ifdef LSEC_API_OPENSSL_1_1_0\n  SSL_CTX_set_min_proto_version(ctx->context, vmin);\n  SSL_CTX_set_max_proto_version(ctx->context, vmax);\n#endif\n  ctx->mode = LSEC_MODE_INVALID;\n  ctx->L = L;\n  luaL_getmetatable(L, \"SSL:Context\");\n  lua_setmetatable(L, -2);\n\n  /* No session support */\n  SSL_CTX_set_session_cache_mode(ctx->context, SSL_SESS_CACHE_OFF);\n  /* Link LuaSec context with the OpenSSL context */\n  SSL_CTX_set_app_data(ctx->context, ctx);\n\n  return 1;\n}\n\n/**\n * Load the trusting certificates.\n */\nstatic int load_locations(lua_State *L)\n{\n  SSL_CTX *ctx = lsec_checkcontext(L, 1);\n  const char *cafile = luaL_optstring(L, 2, NULL);\n  const char *capath = luaL_optstring(L, 3, NULL);\n  if (SSL_CTX_load_verify_locations(ctx, cafile, capath) != 1) {\n    lua_pushboolean(L, 0);\n    lua_pushfstring(L, \"error loading CA locations (%s)\",\n      ERR_reason_error_string(ERR_get_error()));\n    return 2;\n  }\n  lua_pushboolean(L, 1);\n  return 1;\n}\n\n/**\n * Load the certificate file.\n */\nstatic int load_cert(lua_State *L)\n{\n  SSL_CTX *ctx = lsec_checkcontext(L, 1);\n  const char *filename = luaL_checkstring(L, 2);\n  if (SSL_CTX_use_certificate_chain_file(ctx, filename) != 1) {\n    lua_pushboolean(L, 0);\n    lua_pushfstring(L, \"error loading certificate (%s)\",\n      ERR_reason_error_string(ERR_get_error()));\n    return 2;\n  }\n  lua_pushboolean(L, 1);\n  return 1;\n}\n\n/**\n * Load the key file -- only in PEM format.\n */\nstatic int load_key(lua_State *L)\n{\n  int ret = 1;\n  SSL_CTX *ctx = lsec_checkcontext(L, 1);\n  const char *filename = luaL_checkstring(L, 2);\n  switch (lua_type(L, 3)) {\n  case LUA_TSTRING:\n  case LUA_TFUNCTION:\n    SSL_CTX_set_default_passwd_cb(ctx, passwd_cb);\n    SSL_CTX_set_default_passwd_cb_userdata(ctx, L);\n    /* fall through */\n  case LUA_TNIL:\n    if (SSL_CTX_use_PrivateKey_file(ctx, filename, SSL_FILETYPE_PEM) == 1)\n      lua_pushboolean(L, 1);\n    else {\n      ret = 2;\n      lua_pushboolean(L, 0);\n      lua_pushfstring(L, \"error loading private key (%s)\",\n        ERR_reason_error_string(ERR_get_error()));\n    }\n    SSL_CTX_set_default_passwd_cb(ctx, NULL);\n    SSL_CTX_set_default_passwd_cb_userdata(ctx, NULL);\n    break;\n  default:\n    lua_pushstring(L, \"invalid callback value\");\n    lua_error(L);\n  }\n  return ret;\n}\n\n/**\n * Check that the certificate public key matches the private key\n */\n\nstatic int check_key(lua_State *L)\n{\n  SSL_CTX *ctx = lsec_checkcontext(L, 1);\n  lua_pushboolean(L, SSL_CTX_check_private_key(ctx));\n  return 1;\n}\n\n/**\n * Set the cipher list.\n */\nstatic int set_cipher(lua_State *L)\n{\n  SSL_CTX *ctx = lsec_checkcontext(L, 1);\n  const char *list = luaL_checkstring(L, 2);\n  if (SSL_CTX_set_cipher_list(ctx, list) != 1) {\n    lua_pushboolean(L, 0);\n    lua_pushfstring(L, \"error setting cipher list (%s)\", ERR_reason_error_string(ERR_get_error()));\n    return 2;\n  }\n  lua_pushboolean(L, 1);\n  return 1;\n}\n\n/**\n * Set the cipher suites.\n */\nstatic int set_ciphersuites(lua_State *L)\n{\n#if defined(TLS1_3_VERSION)\n  SSL_CTX *ctx = lsec_checkcontext(L, 1);\n  const char *list = luaL_checkstring(L, 2);\n  if (SSL_CTX_set_ciphersuites(ctx, list) != 1) {\n    lua_pushboolean(L, 0);\n    lua_pushfstring(L, \"error setting cipher list (%s)\", ERR_reason_error_string(ERR_get_error()));\n    return 2;\n  }\n#endif\n  lua_pushboolean(L, 1);\n  return 1;\n}\n\n/**\n * Set the depth for certificate checking.\n */\nstatic int set_depth(lua_State *L)\n{\n  SSL_CTX *ctx = lsec_checkcontext(L, 1);\n  SSL_CTX_set_verify_depth(ctx, (int)luaL_checkinteger(L, 2));\n  lua_pushboolean(L, 1);\n  return 1;\n}\n\n/**\n * Set the handshake verify options.\n */\nstatic int set_verify(lua_State *L)\n{\n  int i;\n  const char *str;\n  int flag = 0;\n  SSL_CTX *ctx = lsec_checkcontext(L, 1);\n  int max = lua_gettop(L);\n  for (i = 2; i <= max; i++) {\n    str = luaL_checkstring(L, i);\n    if (!set_verify_flag(str, &flag)) {\n      lua_pushboolean(L, 0);\n      lua_pushfstring(L, \"invalid verify option (%s)\", str);\n      return 2;\n    }\n  }\n  if (flag) SSL_CTX_set_verify(ctx, flag, NULL);\n  lua_pushboolean(L, 1);\n  return 1;\n}\n\n/**\n * Set the protocol options.\n */\nstatic int set_options(lua_State *L)\n{\n  int i;\n  const char *str;\n  unsigned long flag = 0L;\n  SSL_CTX *ctx = lsec_checkcontext(L, 1);\n  int max = lua_gettop(L);\n  /* any option? */\n  if (max > 1) {\n    for (i = 2; i <= max; i++) {\n      str = luaL_checkstring(L, i);\n      if (!set_option_flag(str, &flag)) {\n        lua_pushboolean(L, 0);\n        lua_pushfstring(L, \"invalid option (%s)\", str);\n        return 2;\n      }\n    }\n    SSL_CTX_set_options(ctx, flag);\n  }\n  lua_pushboolean(L, 1);\n  return 1;\n}\n\n/**\n * Set the context mode.\n */\nstatic int set_mode(lua_State *L)\n{\n  p_context ctx = checkctx(L, 1);\n  const char *str = luaL_checkstring(L, 2);\n  if (!strcmp(\"server\", str)) {\n    ctx->mode = LSEC_MODE_SERVER;\n    lua_pushboolean(L, 1);\n    return 1;\n  }\n  if (!strcmp(\"client\", str)) {\n    ctx->mode = LSEC_MODE_CLIENT;\n    lua_pushboolean(L, 1);\n    return 1;\n  }\n  lua_pushboolean(L, 0);\n  lua_pushfstring(L, \"invalid mode (%s)\", str);\n  return 1;\n}\n\n/**\n * Configure DH parameters.\n */\nstatic int set_dhparam(lua_State *L)\n{\n  SSL_CTX *ctx = lsec_checkcontext(L, 1);\n  SSL_CTX_set_tmp_dh_callback(ctx, dhparam_cb);\n\n  /* Save callback */\n  luaL_getmetatable(L, \"SSL:DH:Registry\");\n  lua_pushlightuserdata(L, (void*)ctx);\n  lua_pushvalue(L, 2);\n  lua_settable(L, -3);\n\n  return 0;\n}\n\n#if !defined(OPENSSL_NO_EC)\n/**\n * Set elliptic curve.\n */\nstatic int set_curve(lua_State *L)\n{\n  long ret;\n  EC_KEY *key = NULL;\n  SSL_CTX *ctx = lsec_checkcontext(L, 1);\n  const char *str = luaL_checkstring(L, 2);\n\n  SSL_CTX_set_options(ctx, SSL_OP_SINGLE_ECDH_USE);\n\n  key = lsec_find_ec_key(L, str);\n\n  if (!key) {\n    lua_pushboolean(L, 0);\n    lua_pushfstring(L, \"elliptic curve '%s' not supported\", str);\n    return 2;\n  }\n\n  ret = SSL_CTX_set_tmp_ecdh(ctx, key);\n  /* SSL_CTX_set_tmp_ecdh takes its own reference */\n  EC_KEY_free(key);\n\n  if (!ret) {\n    lua_pushboolean(L, 0);\n    lua_pushfstring(L, \"error setting elliptic curve (%s)\",\n      ERR_reason_error_string(ERR_get_error()));\n    return 2;\n  }\n\n  lua_pushboolean(L, 1);\n  return 1;\n}\n\n/**\n * Set elliptic curves list.\n */\nstatic int set_curves_list(lua_State *L)\n{\n  SSL_CTX *ctx = lsec_checkcontext(L, 1);\n  const char *str = luaL_checkstring(L, 2);\n\n  SSL_CTX_set_options(ctx, SSL_OP_SINGLE_ECDH_USE);\n\n  if (SSL_CTX_set1_curves_list(ctx, str) != 1) {\n    lua_pushboolean(L, 0);\n    lua_pushfstring(L, \"unknown elliptic curve in \\\"%s\\\"\", str);\n    return 2;\n  }\n\n#if defined(LIBRESSL_VERSION_NUMBER) || !defined(LSEC_API_OPENSSL_1_1_0)\n  (void)SSL_CTX_set_ecdh_auto(ctx, 1);\n#endif\n\n  lua_pushboolean(L, 1);\n  return 1;\n}\n#endif\n\n/**\n * Set the protocols a client should send for ALPN.\n */\nstatic int set_alpn(lua_State *L)\n{\n  long ret;\n  size_t len;\n  p_context ctx = checkctx(L, 1);\n  const char *str = luaL_checklstring(L, 2, &len);\n\n  ret = SSL_CTX_set_alpn_protos(ctx->context, (const unsigned char*)str, len);\n  if (ret) {\n    lua_pushboolean(L, 0);\n    lua_pushfstring(L, \"error setting ALPN (%s)\", ERR_reason_error_string(ERR_get_error()));\n    return 2;\n  }\n  lua_pushboolean(L, 1);\n  return 1;\n}\n\n/**\n * This standard callback calls the server's callback in Lua sapce.\n * The server has to return a list in wire-format strings.\n * This function uses a helper function to match server and client lists.\n */\nstatic int alpn_cb(SSL *s, const unsigned char **out, unsigned char *outlen,\n                   const unsigned char *in, unsigned int inlen, void *arg)\n{\n  int ret;\n  size_t server_len;\n  const char *server;\n  p_context ctx = (p_context)arg;\n  lua_State *L = ctx->L;\n\n  luaL_getmetatable(L, \"SSL:ALPN:Registry\");\n  lua_pushlightuserdata(L, (void*)ctx->context);\n  lua_gettable(L, -2);\n\n  lua_pushlstring(L, (const char*)in, inlen);\n\n  lua_call(L, 1, 1);\n\n  if (!lua_isstring(L, -1)) {\n    lua_pop(L, 2);\n    return SSL_TLSEXT_ERR_NOACK;\n  }\n\n  // Protocol list from server in wire-format string\n  server = luaL_checklstring(L, -1, &server_len);\n  ret  = SSL_select_next_proto((unsigned char**)out, outlen, (const unsigned char*)server,\n                               server_len, in, inlen);\n  if (ret != OPENSSL_NPN_NEGOTIATED) {\n    lua_pop(L, 2);\n    return SSL_TLSEXT_ERR_NOACK;\n  }\n\n  // Copy the result because lua_pop() can collect the pointer\n  ctx->alpn = malloc(*outlen);\n  memcpy(ctx->alpn, (void*)*out, *outlen);\n  *out = (const unsigned char*)ctx->alpn;\n\n  lua_pop(L, 2);\n\n  return SSL_TLSEXT_ERR_OK;\n}\n\n/**\n * Set a callback a server can use to select the next protocol with ALPN.\n */\nstatic int set_alpn_cb(lua_State *L)\n{\n  p_context ctx = checkctx(L, 1);\n\n  luaL_getmetatable(L, \"SSL:ALPN:Registry\");\n  lua_pushlightuserdata(L, (void*)ctx->context);\n  lua_pushvalue(L, 2);\n  lua_settable(L, -3);\n\n  SSL_CTX_set_alpn_select_cb(ctx->context, alpn_cb, ctx);\n\n  lua_pushboolean(L, 1);\n  return 1;\n}\n\n#if defined(LSEC_ENABLE_DANE)\n/*\n * DANE\n */\nstatic int set_dane(lua_State *L)\n{\n  int ret;\n  SSL_CTX *ctx = lsec_checkcontext(L, 1);\n  ret = SSL_CTX_dane_enable(ctx);\n  lua_pushboolean(L, (ret > 0));\n  return 1;\n}\n#endif\n\n/**\n * Package functions\n */\nstatic luaL_Reg funcs[] = {\n  {\"create\",          create},\n  {\"locations\",       load_locations},\n  {\"loadcert\",        load_cert},\n  {\"loadkey\",         load_key},\n  {\"checkkey\",        check_key},\n  {\"setalpn\",         set_alpn},\n  {\"setalpncb\",       set_alpn_cb},\n  {\"setcipher\",       set_cipher},\n  {\"setciphersuites\", set_ciphersuites},\n  {\"setdepth\",        set_depth},\n  {\"setdhparam\",      set_dhparam},\n  {\"setverify\",       set_verify},\n  {\"setoptions\",      set_options},\n  {\"setmode\",         set_mode},\n#if !defined(OPENSSL_NO_EC)\n  {\"setcurve\",        set_curve},\n  {\"setcurveslist\",   set_curves_list},\n#endif\n#if defined(LSEC_ENABLE_DANE)\n  {\"setdane\",         set_dane},\n#endif\n  {NULL, NULL}\n};\n\n/*-------------------------------- Metamethods -------------------------------*/\n\n/**\n * Collect SSL context -- GC metamethod.\n */\nstatic int meth_destroy(lua_State *L)\n{\n  p_context ctx = checkctx(L, 1);\n  if (ctx->context) {\n    /* Clear registries */\n    luaL_getmetatable(L, \"SSL:DH:Registry\");\n    lua_pushlightuserdata(L, (void*)ctx->context);\n    lua_pushnil(L);\n    lua_settable(L, -3);\n    luaL_getmetatable(L, \"SSL:Verify:Registry\");\n    lua_pushlightuserdata(L, (void*)ctx->context);\n    lua_pushnil(L);\n    lua_settable(L, -3);\n    luaL_getmetatable(L, \"SSL:ALPN:Registry\");\n    lua_pushlightuserdata(L, (void*)ctx->context);\n    lua_pushnil(L);\n    lua_settable(L, -3);\n\n    SSL_CTX_free(ctx->context);\n    ctx->context = NULL;\n  }\n  return 0;\n}\n\n/**\n * Object information -- tostring metamethod.\n */\nstatic int meth_tostring(lua_State *L)\n{\n  p_context ctx = checkctx(L, 1);\n  lua_pushfstring(L, \"SSL context: %p\", ctx);\n  return 1;\n}\n\n/**\n * Set extra flags for handshake verification.\n */\nstatic int meth_set_verify_ext(lua_State *L)\n{\n  int i;\n  const char *str;\n  int crl_flag = 0;\n  int lsec_flag = 0;\n  SSL_CTX *ctx = lsec_checkcontext(L, 1);\n  int max = lua_gettop(L);\n  for (i = 2; i <= max; i++) {\n    str = luaL_checkstring(L, i);\n    if (!strcmp(str, \"lsec_continue\")) {\n      lsec_flag |= LSEC_VERIFY_CONTINUE;\n    } else if (!strcmp(str, \"lsec_ignore_purpose\")) {\n      lsec_flag |= LSEC_VERIFY_IGNORE_PURPOSE;\n    } else if (!strcmp(str, \"crl_check\")) {\n      crl_flag |= X509_V_FLAG_CRL_CHECK;\n    } else if (!strcmp(str, \"crl_check_chain\")) {\n      crl_flag |= X509_V_FLAG_CRL_CHECK_ALL;\n    } else {\n      lua_pushboolean(L, 0);\n      lua_pushfstring(L, \"invalid verify option (%s)\", str);\n      return 2;\n    }\n  }\n  /* Set callback? */\n  if (lsec_flag) {\n    SSL_CTX_set_verify(ctx, SSL_CTX_get_verify_mode(ctx), verify_cb);\n    SSL_CTX_set_cert_verify_callback(ctx, cert_verify_cb, (void*)ctx);\n    /* Save flag */\n    luaL_getmetatable(L, \"SSL:Verify:Registry\");\n    lua_pushlightuserdata(L, (void*)ctx);\n    lua_pushnumber(L, lsec_flag);\n    lua_settable(L, -3);\n  } else {\n    SSL_CTX_set_verify(ctx, SSL_CTX_get_verify_mode(ctx), NULL);\n    SSL_CTX_set_cert_verify_callback(ctx, NULL, NULL);\n    /* Remove flag */\n    luaL_getmetatable(L, \"SSL:Verify:Registry\");\n    lua_pushlightuserdata(L, (void*)ctx);\n    lua_pushnil(L);\n    lua_settable(L, -3);\n  }\n\n  /* X509 flag */\n  X509_STORE_set_flags(SSL_CTX_get_cert_store(ctx), crl_flag);\n\n  /* Ok */\n  lua_pushboolean(L, 1);\n  return 1;\n}\n\n/**\n * Context metamethods.\n */\nstatic luaL_Reg meta[] = {\n  {\"__close\",    meth_destroy},\n  {\"__gc\",       meth_destroy},\n  {\"__tostring\", meth_tostring},\n  {NULL, NULL}\n};\n\n/**\n * Index metamethods.\n */\nstatic luaL_Reg meta_index[] = {\n  {\"setverifyext\", meth_set_verify_ext},\n  {NULL, NULL}\n};\n\n\n/*----------------------------- Public Functions  ---------------------------*/\n\n/**\n * Retrieve the SSL context from the Lua stack.\n */\nSSL_CTX* lsec_checkcontext(lua_State *L, int idx)\n{\n  p_context ctx = checkctx(L, idx);\n  return ctx->context;\n}\n\nSSL_CTX* lsec_testcontext(lua_State *L, int idx)\n{\n  p_context ctx = testctx(L, idx);\n  return (ctx) ? ctx->context : NULL;\n}\n\n/**\n * Retrieve the mode from the context in the Lua stack.\n */\nint lsec_getmode(lua_State *L, int idx)\n{\n  p_context ctx = checkctx(L, idx);\n  return ctx->mode;\n}\n\n/*------------------------------ Initialization ------------------------------*/\n\n/**\n * Registre the module.\n */\nLSEC_API int luaopen_ssl_context(lua_State *L)\n{\n  luaL_newmetatable(L, \"SSL:DH:Registry\");      /* Keep all DH callbacks   */\n  luaL_newmetatable(L, \"SSL:ALPN:Registry\");    /* Keep all ALPN callbacks */\n  luaL_newmetatable(L, \"SSL:Verify:Registry\");  /* Keep all verify flags   */\n  luaL_newmetatable(L, \"SSL:Context\");\n  setfuncs(L, meta);\n\n  /* Create __index metamethods for context */\n  luaL_newlib(L, meta_index);\n  lua_setfield(L, -2, \"__index\");\n\n  lsec_load_curves(L);\n\n  /* Return the module */\n  luaL_newlib(L, funcs);\n  lua_pushvalue(L, -1);\n  lua_setglobal(L, \"context\");\n\n  return 1;\n}\n"
  },
  {
    "path": "src/luasec/context.h",
    "content": "#ifndef LSEC_CONTEXT_H\n#define LSEC_CONTEXT_H\n\n/*--------------------------------------------------------------------------\n * LuaSec 1.0.2\n *\n * Copyright (C) 2006-2021 Bruno Silvestre\n *\n *--------------------------------------------------------------------------*/\n\n#include \"../lua.h\"\n#include <openssl/ssl.h>\n\n#include \"compat.h\"\n\n#define LSEC_MODE_INVALID 0\n#define LSEC_MODE_SERVER  1\n#define LSEC_MODE_CLIENT  2\n\n#define LSEC_VERIFY_CONTINUE        1\n#define LSEC_VERIFY_IGNORE_PURPOSE  2\n\ntypedef struct t_context_ {\n  SSL_CTX *context;\n  lua_State *L;\n  DH *dh_param;\n  void *alpn;\n  int mode;\n} t_context;\ntypedef t_context* p_context;\n\n/* Retrieve the SSL context from the Lua stack */\nSSL_CTX *lsec_checkcontext(lua_State *L, int idx);\nSSL_CTX *lsec_testcontext(lua_State *L, int idx);\n\n/* Retrieve the mode from the context in the Lua stack */\nint lsec_getmode(lua_State *L, int idx);\n\n/* Registre the module. */\nLSEC_API int luaopen_ssl_context(lua_State *L);\n\nvoid *luasocket_testudata ( lua_State *L, int arg, const char *tname);\n\n#endif\n"
  },
  {
    "path": "src/luasec/ec.c",
    "content": "#include <openssl/objects.h>\n\n#include \"ec.h\"\n\n#ifndef OPENSSL_NO_EC\n\nEC_KEY *lsec_find_ec_key(lua_State *L, const char *str)\n{\n  int nid;\n  lua_pushstring(L, \"SSL:EC:CURVES\");\n  lua_rawget(L, LUA_REGISTRYINDEX);\n  lua_pushstring(L, str);\n  lua_rawget(L, -2);\n\n  if (!lua_isnumber(L, -1))\n    return NULL;\n\n  nid = (int)lua_tonumber(L, -1);\n  return EC_KEY_new_by_curve_name(nid);\n}\n\nvoid lsec_load_curves(lua_State *L)\n{\n  size_t i;\n  size_t size;\n  const char *name;\n  EC_builtin_curve *curves = NULL;\n\n  lua_pushstring(L, \"SSL:EC:CURVES\");\n  lua_newtable(L);\n\n  size = EC_get_builtin_curves(NULL, 0);\n  if (size > 0) {\n    curves = (EC_builtin_curve*)malloc(sizeof(EC_builtin_curve) * size);\n    EC_get_builtin_curves(curves, size);\n    for (i = 0; i < size; i++) {\n      name = OBJ_nid2sn(curves[i].nid);\n      if (name != NULL) {\n        lua_pushstring(L, name);\n        lua_pushnumber(L, curves[i].nid);\n        lua_rawset(L, -3);\n      }\n      switch (curves[i].nid) {\n      case NID_X9_62_prime256v1:\n        lua_pushstring(L, \"P-256\");\n        lua_pushnumber(L, curves[i].nid);\n        lua_rawset(L, -3);\n        break;\n      case NID_secp384r1:\n        lua_pushstring(L, \"P-384\");\n        lua_pushnumber(L, curves[i].nid);\n        lua_rawset(L, -3);\n        break;\n      case NID_secp521r1:\n        lua_pushstring(L, \"P-521\");\n        lua_pushnumber(L, curves[i].nid);\n        lua_rawset(L, -3);\n        break;\n      }\n    }\n    free(curves);\n  }\n\n  /* These are special so are manually added here */\n#ifdef NID_X25519\n  lua_pushstring(L, \"X25519\");\n  lua_pushnumber(L, NID_X25519);\n  lua_rawset(L, -3);\n#endif\n\n#ifdef NID_X448\n  lua_pushstring(L, \"X448\");\n  lua_pushnumber(L, NID_X448);\n  lua_rawset(L, -3);\n#endif\n\n  lua_rawset(L,  LUA_REGISTRYINDEX);\n}\n\nvoid lsec_get_curves(lua_State *L)\n{\n  lua_newtable(L);\n\n  lua_pushstring(L, \"SSL:EC:CURVES\");\n  lua_rawget(L, LUA_REGISTRYINDEX);\n\n  lua_pushnil(L);\n  while (lua_next(L, -2) != 0) {\n    lua_pop(L, 1);\n    lua_pushvalue(L, -1);\n    lua_pushboolean(L, 1);\n    lua_rawset(L, -5);\n  }\n  lua_pop(L, 1);\n}\n\n#else\n\nvoid lsec_load_curves(lua_State *L)\n{\n  // do nothing\n}\n\nvoid lsec_get_curves(lua_State *L)\n{\n  lua_newtable(L);\n}\n\n#endif\n"
  },
  {
    "path": "src/luasec/ec.h",
    "content": "/*--------------------------------------------------------------------------\n * LuaSec 1.0.2\n *\n * Copyright (C) 2006-2021 Bruno Silvestre\n *\n *--------------------------------------------------------------------------*/\n\n#ifndef LSEC_EC_H\n#define LSEC_EC_H\n\n#include \"../lua.h\"\n\n#ifndef OPENSSL_NO_EC\n#include <openssl/ec.h>\n\nEC_KEY *lsec_find_ec_key(lua_State *L, const char *str);\n#endif\n\nvoid lsec_get_curves(lua_State *L);\nvoid lsec_load_curves(lua_State *L);\n\n#endif\n"
  },
  {
    "path": "src/luasec/https.lua",
    "content": "----------------------------------------------------------------------------\n-- LuaSec 1.0.2\n-- Copyright (C) 2009-2021 PUC-Rio\n--\n-- Author: Pablo Musa\n-- Author: Tomas Guisasola\n---------------------------------------------------------------------------\n\nlocal try    = socket.try\n\n--\n-- Module\n--\nlocal _M = {\n  _VERSION   = \"1.0.2\",\n  _COPYRIGHT = \"LuaSec 1.0.2 - Copyright (C) 2009-2021 PUC-Rio\",\n  PORT       = 443,\n  TIMEOUT    = 60\n}\n\n-- TLS configuration\nlocal cfg = {\n  protocol = \"any\",\n  options  = {\"all\", \"no_sslv2\", \"no_sslv3\", \"no_tlsv1\"},\n  verify   = \"none\",\n}\n\n--------------------------------------------------------------------\n-- Auxiliar Functions\n--------------------------------------------------------------------\n\n-- Insert default HTTPS port.\nlocal function default_https_port(u)\n   return url.build(url.parse(u, {port = _M.PORT}))\nend\n\n-- Convert an URL to a table according to Luasocket needs.\nlocal function urlstring_totable(url, body, result_table)\n   url = {\n      url = default_https_port(url),\n      method = body and \"POST\" or \"GET\",\n      sink = ltn12.sink.table(result_table)\n   }\n   if body then\n      url.source = ltn12.source.string(body)\n      url.headers = {\n         [\"content-length\"] = #body,\n         [\"content-type\"] = \"application/x-www-form-urlencoded\",\n      }\n   end\n   return url\nend\n\n-- Forward calls to the real connection object.\nlocal function reg(conn)\n   local mt = getmetatable(conn.sock).__index\n   for name, method in pairs(mt) do\n      if type(method) == \"function\" then\n         conn[name] = function (self, ...)\n                         return method(self.sock, ...)\n                      end\n      end\n   end\nend\n\n-- Return a function which performs the SSL/TLS connection.\nlocal function tcp(params)\n   params = params or {}\n   -- Default settings\n   for k, v in pairs(cfg) do \n      params[k] = params[k] or v\n   end\n   -- Force client mode\n   params.mode = \"client\"\n   -- 'create' function for LuaSocket\n   return function ()\n      local conn = {}\n      conn.sock = try(socket.tcp())\n      local st = getmetatable(conn.sock).__index.settimeout\n      function conn:settimeout(...)\n         return st(self.sock, _M.TIMEOUT)\n      end\n      -- Replace TCP's connection function\n      function conn:connect(host, port)\n         try(self.sock:connect(host, port))\n         self.sock = try(ssl.wrap(self.sock, params))\n         self.sock:sni(host)\n         self.sock:settimeout(_M.TIMEOUT)\n         try(self.sock:dohandshake())\n         reg(self, getmetatable(self.sock))\n         return 1\n      end\n      return conn\n  end\nend\n\n--------------------------------------------------------------------\n-- Main Function\n--------------------------------------------------------------------\n\n-- Make a HTTP request over secure connection.  This function receives\n--  the same parameters of LuaSocket's HTTP module (except 'proxy' and\n--  'redirect') plus LuaSec parameters.\n--\n-- @param url mandatory (string or table)\n-- @param body optional (string)\n-- @return (string if url == string or 1), code, headers, status\n--\nlocal function request(url, body)\n  local result_table = {}\n  local stringrequest = type(url) == \"string\"\n  if stringrequest then\n    url = urlstring_totable(url, body, result_table)\n  else\n    url.url = default_https_port(url.url)\n  end\n  if http.PROXY or url.proxy then\n    return nil, \"proxy not supported\"\n  elseif url.redirect then\n    return nil, \"redirect not supported\"\n  elseif url.create then\n    return nil, \"create function not permitted\"\n  end\n  -- New 'create' function to establish a secure connection\n  url.create = tcp(url)\n  local res, code, headers, status = http.request(url)\n  if res and stringrequest then\n    return table.concat(result_table), code, headers, status\n  end\n  return res, code, headers, status\nend\n\n--------------------------------------------------------------------------------\n-- Export module\n--\n\n_M.request = request\n_M.tcp = tcp\n\nreturn _M\n"
  },
  {
    "path": "src/luasec/options.c",
    "content": "/*--------------------------------------------------------------------------\n * LuaSec 1.0.2\n *\n * Copyright (C) 2006-2021 Bruno Silvestre\n *\n *--------------------------------------------------------------------------*/\n\n#include <openssl/ssl.h>\n\n#include \"options.h\"\n\n/* If you need to generate these options again, see options.lua */\n\n\n/* \n  OpenSSL version: OpenSSL 3.0.0-beta2\n*/\n\nstatic lsec_ssl_option_t ssl_options[] = {\n#if defined(SSL_OP_ALL)\n  {\"all\", SSL_OP_ALL},\n#endif\n#if defined(SSL_OP_ALLOW_CLIENT_RENEGOTIATION)\n  {\"allow_client_renegotiation\", SSL_OP_ALLOW_CLIENT_RENEGOTIATION},\n#endif\n#if defined(SSL_OP_ALLOW_NO_DHE_KEX)\n  {\"allow_no_dhe_kex\", SSL_OP_ALLOW_NO_DHE_KEX},\n#endif\n#if defined(SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION)\n  {\"allow_unsafe_legacy_renegotiation\", SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION},\n#endif\n#if defined(SSL_OP_CIPHER_SERVER_PREFERENCE)\n  {\"cipher_server_preference\", SSL_OP_CIPHER_SERVER_PREFERENCE},\n#endif\n#if defined(SSL_OP_CISCO_ANYCONNECT)\n  {\"cisco_anyconnect\", SSL_OP_CISCO_ANYCONNECT},\n#endif\n#if defined(SSL_OP_CLEANSE_PLAINTEXT)\n  {\"cleanse_plaintext\", SSL_OP_CLEANSE_PLAINTEXT},\n#endif\n#if defined(SSL_OP_COOKIE_EXCHANGE)\n  {\"cookie_exchange\", SSL_OP_COOKIE_EXCHANGE},\n#endif\n#if defined(SSL_OP_CRYPTOPRO_TLSEXT_BUG)\n  {\"cryptopro_tlsext_bug\", SSL_OP_CRYPTOPRO_TLSEXT_BUG},\n#endif\n#if defined(SSL_OP_DISABLE_TLSEXT_CA_NAMES)\n  {\"disable_tlsext_ca_names\", SSL_OP_DISABLE_TLSEXT_CA_NAMES},\n#endif\n#if defined(SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS)\n  {\"dont_insert_empty_fragments\", SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS},\n#endif\n#if defined(SSL_OP_ENABLE_KTLS)\n  {\"enable_ktls\", SSL_OP_ENABLE_KTLS},\n#endif\n#if defined(SSL_OP_ENABLE_MIDDLEBOX_COMPAT)\n  {\"enable_middlebox_compat\", SSL_OP_ENABLE_MIDDLEBOX_COMPAT},\n#endif\n#if defined(SSL_OP_EPHEMERAL_RSA)\n  {\"ephemeral_rsa\", SSL_OP_EPHEMERAL_RSA},\n#endif\n#if defined(SSL_OP_IGNORE_UNEXPECTED_EOF)\n  {\"ignore_unexpected_eof\", SSL_OP_IGNORE_UNEXPECTED_EOF},\n#endif\n#if defined(SSL_OP_LEGACY_SERVER_CONNECT)\n  {\"legacy_server_connect\", SSL_OP_LEGACY_SERVER_CONNECT},\n#endif\n#if defined(SSL_OP_MICROSOFT_BIG_SSLV3_BUFFER)\n  {\"microsoft_big_sslv3_buffer\", SSL_OP_MICROSOFT_BIG_SSLV3_BUFFER},\n#endif\n#if defined(SSL_OP_MICROSOFT_SESS_ID_BUG)\n  {\"microsoft_sess_id_bug\", SSL_OP_MICROSOFT_SESS_ID_BUG},\n#endif\n#if defined(SSL_OP_MSIE_SSLV2_RSA_PADDING)\n  {\"msie_sslv2_rsa_padding\", SSL_OP_MSIE_SSLV2_RSA_PADDING},\n#endif\n#if defined(SSL_OP_NETSCAPE_CA_DN_BUG)\n  {\"netscape_ca_dn_bug\", SSL_OP_NETSCAPE_CA_DN_BUG},\n#endif\n#if defined(SSL_OP_NETSCAPE_CHALLENGE_BUG)\n  {\"netscape_challenge_bug\", SSL_OP_NETSCAPE_CHALLENGE_BUG},\n#endif\n#if defined(SSL_OP_NETSCAPE_DEMO_CIPHER_CHANGE_BUG)\n  {\"netscape_demo_cipher_change_bug\", SSL_OP_NETSCAPE_DEMO_CIPHER_CHANGE_BUG},\n#endif\n#if defined(SSL_OP_NETSCAPE_REUSE_CIPHER_CHANGE_BUG)\n  {\"netscape_reuse_cipher_change_bug\", SSL_OP_NETSCAPE_REUSE_CIPHER_CHANGE_BUG},\n#endif\n#if defined(SSL_OP_NO_ANTI_REPLAY)\n  {\"no_anti_replay\", SSL_OP_NO_ANTI_REPLAY},\n#endif\n#if defined(SSL_OP_NO_COMPRESSION)\n  {\"no_compression\", SSL_OP_NO_COMPRESSION},\n#endif\n#if defined(SSL_OP_NO_DTLS_MASK)\n  {\"no_dtls_mask\", SSL_OP_NO_DTLS_MASK},\n#endif\n#if defined(SSL_OP_NO_DTLSv1)\n  {\"no_dtlsv1\", SSL_OP_NO_DTLSv1},\n#endif\n#if defined(SSL_OP_NO_DTLSv1_2)\n  {\"no_dtlsv1_2\", SSL_OP_NO_DTLSv1_2},\n#endif\n#if defined(SSL_OP_NO_ENCRYPT_THEN_MAC)\n  {\"no_encrypt_then_mac\", SSL_OP_NO_ENCRYPT_THEN_MAC},\n#endif\n#if defined(SSL_OP_NO_EXTENDED_MASTER_SECRET)\n  {\"no_extended_master_secret\", SSL_OP_NO_EXTENDED_MASTER_SECRET},\n#endif\n#if defined(SSL_OP_NO_QUERY_MTU)\n  {\"no_query_mtu\", SSL_OP_NO_QUERY_MTU},\n#endif\n#if defined(SSL_OP_NO_RENEGOTIATION)\n  {\"no_renegotiation\", SSL_OP_NO_RENEGOTIATION},\n#endif\n#if defined(SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION)\n  {\"no_session_resumption_on_renegotiation\", SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION},\n#endif\n#if defined(SSL_OP_NO_SSL_MASK)\n  {\"no_ssl_mask\", SSL_OP_NO_SSL_MASK},\n#endif\n#if defined(SSL_OP_NO_SSLv2)\n  {\"no_sslv2\", SSL_OP_NO_SSLv2},\n#endif\n#if defined(SSL_OP_NO_SSLv3)\n  {\"no_sslv3\", SSL_OP_NO_SSLv3},\n#endif\n#if defined(SSL_OP_NO_TICKET)\n  {\"no_ticket\", SSL_OP_NO_TICKET},\n#endif\n#if defined(SSL_OP_NO_TLSv1)\n  {\"no_tlsv1\", SSL_OP_NO_TLSv1},\n#endif\n#if defined(SSL_OP_NO_TLSv1_1)\n  {\"no_tlsv1_1\", SSL_OP_NO_TLSv1_1},\n#endif\n#if defined(SSL_OP_NO_TLSv1_2)\n  {\"no_tlsv1_2\", SSL_OP_NO_TLSv1_2},\n#endif\n#if defined(SSL_OP_NO_TLSv1_3)\n  {\"no_tlsv1_3\", SSL_OP_NO_TLSv1_3},\n#endif\n#if defined(SSL_OP_PKCS1_CHECK_1)\n  {\"pkcs1_check_1\", SSL_OP_PKCS1_CHECK_1},\n#endif\n#if defined(SSL_OP_PKCS1_CHECK_2)\n  {\"pkcs1_check_2\", SSL_OP_PKCS1_CHECK_2},\n#endif\n#if defined(SSL_OP_PRIORITIZE_CHACHA)\n  {\"prioritize_chacha\", SSL_OP_PRIORITIZE_CHACHA},\n#endif\n#if defined(SSL_OP_SAFARI_ECDHE_ECDSA_BUG)\n  {\"safari_ecdhe_ecdsa_bug\", SSL_OP_SAFARI_ECDHE_ECDSA_BUG},\n#endif\n#if defined(SSL_OP_SINGLE_DH_USE)\n  {\"single_dh_use\", SSL_OP_SINGLE_DH_USE},\n#endif\n#if defined(SSL_OP_SINGLE_ECDH_USE)\n  {\"single_ecdh_use\", SSL_OP_SINGLE_ECDH_USE},\n#endif\n#if defined(SSL_OP_SSLEAY_080_CLIENT_DH_BUG)\n  {\"ssleay_080_client_dh_bug\", SSL_OP_SSLEAY_080_CLIENT_DH_BUG},\n#endif\n#if defined(SSL_OP_SSLREF2_REUSE_CERT_TYPE_BUG)\n  {\"sslref2_reuse_cert_type_bug\", SSL_OP_SSLREF2_REUSE_CERT_TYPE_BUG},\n#endif\n#if defined(SSL_OP_TLSEXT_PADDING)\n  {\"tlsext_padding\", SSL_OP_TLSEXT_PADDING},\n#endif\n#if defined(SSL_OP_TLS_BLOCK_PADDING_BUG)\n  {\"tls_block_padding_bug\", SSL_OP_TLS_BLOCK_PADDING_BUG},\n#endif\n#if defined(SSL_OP_TLS_D5_BUG)\n  {\"tls_d5_bug\", SSL_OP_TLS_D5_BUG},\n#endif\n#if defined(SSL_OP_TLS_ROLLBACK_BUG)\n  {\"tls_rollback_bug\", SSL_OP_TLS_ROLLBACK_BUG},\n#endif\n  {NULL, 0L}\n};\n\nLSEC_API lsec_ssl_option_t* lsec_get_ssl_options() {\n  return ssl_options;\n}\n\n"
  },
  {
    "path": "src/luasec/options.h",
    "content": "#ifndef LSEC_OPTIONS_H\n#define LSEC_OPTIONS_H\n\n/*--------------------------------------------------------------------------\n * LuaSec 1.0.2\n *\n * Copyright (C) 2006-2021 Bruno Silvestre\n *\n *--------------------------------------------------------------------------*/\n\n#include \"compat.h\"\n\nstruct lsec_ssl_option_s {\n  const char *name;\n  unsigned long code;\n};\n\ntypedef struct lsec_ssl_option_s lsec_ssl_option_t;\n\nLSEC_API lsec_ssl_option_t* lsec_get_ssl_options();\n\n#endif\n"
  },
  {
    "path": "src/luasec/options.lua",
    "content": "local function usage()\n  print(\"Usage:\")\n  print(\"* Generate options of your system:\")\n  print(\"  lua options.lua -g /path/to/ssl.h [version] > options.c\")\n  print(\"* Examples:\")\n  print(\"  lua options.lua -g /usr/include/openssl/ssl.h > options.c\\n\")\n  print(\"  lua options.lua -g /usr/include/openssl/ssl.h \\\"OpenSSL 1.1.1f\\\" > options.c\\n\")\n\n  print(\"* List options of your system:\")\n  print(\"  lua options.lua -l /path/to/ssl.h\\n\")\nend\n\n--\nlocal function printf(str, ...)\n  print(string.format(str, ...))\nend\n\nlocal function generate(options, version)\n  print([[\n/*--------------------------------------------------------------------------\n * LuaSec 1.1.1\n *\n * Copyright (C) 2006-2021 Bruno Silvestre\n *\n *--------------------------------------------------------------------------*/\n\n#include <openssl/ssl.h>\n\n#include \"options.h\"\n\n/* If you need to generate these options again, see options.lua */\n\n]])\n\n  printf([[\n/* \n  OpenSSL version: %s\n*/\n]], version)\n\n  print([[static lsec_ssl_option_t ssl_options[] = {]])\n\n  for k, option in ipairs(options) do\n    local name = string.lower(string.sub(option, 8))\n    print(string.format([[#if defined(%s)]], option))\n    print(string.format([[  {\"%s\", %s},]], name, option))\n    print([[#endif]])\n  end\n  print([[  {NULL, 0L}]])\n  print([[\n};\n\nLSEC_API lsec_ssl_option_t* lsec_get_ssl_options() {\n  return ssl_options;\n}\n]])\nend\n\nlocal function loadoptions(file)\n  local options = {}\n  local f = assert(io.open(file, \"r\"))\n  for line in f:lines() do\n    local op = string.match(line, \"define%s+(SSL_OP_BIT%()\")\n    if not op then\n      op = string.match(line, \"define%s+(SSL_OP_%S+)\")\n      if op then\n        table.insert(options, op)\n      end\n    end\n  end\n  table.sort(options, function(a,b) return a<b end)\n  return options\nend\n--\n\nlocal options\nlocal flag, file, version = ...\n\nversion = version or \"Unknown\"\n\nif not file then\n  usage()\nelseif flag == \"-g\" then\n  options = loadoptions(file)\n  generate(options, version)\nelseif flag == \"-l\" then\n  options = loadoptions(file)\n  for k, option in ipairs(options) do\n    print(option)\n  end\nelse\n  usage()\nend\n"
  },
  {
    "path": "src/luasec/ssl.c",
    "content": "/*--------------------------------------------------------------------------\n * LuaSec 1.0.2\n *\n * Copyright (C) 2014-2021 Kim Alvefur, Paul Aurich, Tobias Markmann, \n *                         Matthew Wild.\n * Copyright (C) 2006-2021 Bruno Silvestre.\n *\n *--------------------------------------------------------------------------*/\n\n#include <errno.h>\n#include <string.h>\n\n#if defined(WIN32)\n#include <winsock2.h>\n#endif\n\n#include <openssl/ssl.h>\n#include <openssl/x509v3.h>\n#include <openssl/x509_vfy.h>\n#include <openssl/err.h>\n#include <openssl/dh.h>\n\n#include \"../lua.h\"\n#include \"../lauxlib.h\"\n\n#include \"../luasocket/io.h\"\n#include \"../luasocket/buffer.h\"\n#include \"../luasocket/timeout.h\"\n#include \"../luasocket/socket.h\"\n\n#include \"x509.h\"\n#include \"context.h\"\n#include \"ssl.h\"\n\n\n#ifndef LSEC_API_OPENSSL_1_1_0\n#define SSL_is_server(s) (s->server)\n#define SSL_up_ref(ssl)  CRYPTO_add(&(ssl)->references, 1, CRYPTO_LOCK_SSL)\n#define X509_up_ref(c)   CRYPTO_add(&c->references, 1, CRYPTO_LOCK_X509)\n#endif\n\n\n/**\n * Underline socket error.\n */\nstatic int lsec_socket_error()\n{\n#if defined(WIN32)\n  return WSAGetLastError();\n#else\n#if defined(LSEC_OPENSSL_1_1_1)\n  // Bug in OpenSSL 1.1.1\n  if (errno == 0)\n    return LSEC_IO_SSL;\n#endif\n  return errno;\n#endif\n}\n\n/**\n * Map error code into string.\n */\nstatic const char *ssl_ioerror(void *ctx, int err)\n{\n  if (err == LSEC_IO_SSL) {\n    p_ssl ssl = (p_ssl) ctx;\n    switch(ssl->error) {\n    case SSL_ERROR_NONE: return \"No error\";\n    case SSL_ERROR_ZERO_RETURN: return \"closed\";\n    case SSL_ERROR_WANT_READ: return \"wantread\";\n    case SSL_ERROR_WANT_WRITE: return \"wantwrite\";\n    case SSL_ERROR_WANT_CONNECT: return \"'connect' not completed\";\n    case SSL_ERROR_WANT_ACCEPT: return \"'accept' not completed\";\n    case SSL_ERROR_WANT_X509_LOOKUP: return \"Waiting for callback\";\n    case SSL_ERROR_SYSCALL: return \"System error\";\n    case SSL_ERROR_SSL: return ERR_reason_error_string(ERR_get_error());\n    default: return \"Unknown SSL error\";\n    }\n  }\n  return socket_strerror(err);\n}\n\n/**\n * Close the connection before the GC collect the object.\n */\nstatic int meth_destroy(lua_State *L)\n{\n  p_ssl ssl = (p_ssl)luaL_checkudata(L, 1, \"SSL:Connection\");\n  if (ssl->state == LSEC_STATE_CONNECTED) {\n    socket_setblocking(&ssl->sock);\n    SSL_shutdown(ssl->ssl);\n  }\n  if (ssl->sock != SOCKET_INVALID) {\n    socket_destroy(&ssl->sock);\n  }\n  ssl->state = LSEC_STATE_CLOSED;\n  if (ssl->ssl) {\n    /* Clear the registries */\n    luaL_getmetatable(L, \"SSL:Verify:Registry\");\n    lua_pushlightuserdata(L, (void*)ssl->ssl);\n    lua_pushnil(L);\n    lua_settable(L, -3);\n    luaL_getmetatable(L, \"SSL:SNI:Registry\");\n    lua_pushlightuserdata(L, (void*)ssl->ssl);\n    lua_pushnil(L);\n    lua_settable(L, -3);\n    /* Destroy the object */\n    SSL_free(ssl->ssl);\n    ssl->ssl = NULL;\n  }\n  return 0;\n}\n\n/**\n * Perform the TLS/SSL handshake\n */\nstatic int handshake(p_ssl ssl)\n{\n  int err;\n  p_timeout tm = timeout_markstart(&ssl->tm);\n  if (ssl->state == LSEC_STATE_CLOSED)\n    return IO_CLOSED;\n  for ( ; ; ) {\n    ERR_clear_error();\n    err = SSL_do_handshake(ssl->ssl);\n    ssl->error = SSL_get_error(ssl->ssl, err);\n    switch (ssl->error) {\n    case SSL_ERROR_NONE:\n      ssl->state = LSEC_STATE_CONNECTED;\n      return IO_DONE;\n    case SSL_ERROR_WANT_READ:\n      err = socket_waitfd(&ssl->sock, WAITFD_R, tm);\n      if (err == IO_TIMEOUT) return LSEC_IO_SSL;\n      if (err != IO_DONE)    return err;\n      break;\n    case SSL_ERROR_WANT_WRITE:\n      err = socket_waitfd(&ssl->sock, WAITFD_W, tm);\n      if (err == IO_TIMEOUT) return LSEC_IO_SSL;\n      if (err != IO_DONE)    return err;\n      break;\n    case SSL_ERROR_SYSCALL:\n      if (ERR_peek_error())  {\n        ssl->error = SSL_ERROR_SSL;\n        return LSEC_IO_SSL;\n      }\n      if (err == 0)\n        return IO_CLOSED;\n      return lsec_socket_error();\n    default:\n      return LSEC_IO_SSL;\n    }\n  }\n  return IO_UNKNOWN;\n}\n\n/**\n * Send data\n */\nstatic int ssl_send(void *ctx, const char *data, size_t count, size_t *sent,\n   p_timeout tm)\n{\n  int err;\n  p_ssl ssl = (p_ssl)ctx;\n  if (ssl->state != LSEC_STATE_CONNECTED)\n    return IO_CLOSED;\n  *sent = 0;\n  for ( ; ; ) {\n    ERR_clear_error();\n    err = SSL_write(ssl->ssl, data, (int)count);\n    ssl->error = SSL_get_error(ssl->ssl, err);\n    switch (ssl->error) {\n    case SSL_ERROR_NONE:\n      *sent = err;\n      return IO_DONE;\n    case SSL_ERROR_WANT_READ: \n      err = socket_waitfd(&ssl->sock, WAITFD_R, tm);\n      if (err == IO_TIMEOUT) return LSEC_IO_SSL;\n      if (err != IO_DONE)    return err;\n      break;\n    case SSL_ERROR_WANT_WRITE:\n      err = socket_waitfd(&ssl->sock, WAITFD_W, tm);\n      if (err == IO_TIMEOUT) return LSEC_IO_SSL;\n      if (err != IO_DONE)    return err;\n      break;\n    case SSL_ERROR_SYSCALL:\n      if (ERR_peek_error())  {\n        ssl->error = SSL_ERROR_SSL;\n        return LSEC_IO_SSL;\n      }\n      if (err == 0)\n        return IO_CLOSED;\n      return lsec_socket_error();\n    default:\n      return LSEC_IO_SSL;\n    }\n  }\n  return IO_UNKNOWN;\n}\n\n/**\n * Receive data\n */\nstatic int ssl_recv(void *ctx, char *data, size_t count, size_t *got,\n  p_timeout tm)\n{\n  int err;\n  p_ssl ssl = (p_ssl)ctx;\n  *got = 0;\n  if (ssl->state != LSEC_STATE_CONNECTED)\n    return IO_CLOSED;\n  for ( ; ; ) {\n    ERR_clear_error();\n    err = SSL_read(ssl->ssl, data, (int)count);\n    ssl->error = SSL_get_error(ssl->ssl, err);\n    switch (ssl->error) {\n    case SSL_ERROR_NONE:\n      *got = err;\n      return IO_DONE;\n    case SSL_ERROR_ZERO_RETURN:\n      return IO_CLOSED;\n    case SSL_ERROR_WANT_READ:\n      err = socket_waitfd(&ssl->sock, WAITFD_R, tm);\n      if (err == IO_TIMEOUT) return LSEC_IO_SSL;\n      if (err != IO_DONE)    return err;\n      break;\n    case SSL_ERROR_WANT_WRITE:\n      err = socket_waitfd(&ssl->sock, WAITFD_W, tm);\n      if (err == IO_TIMEOUT) return LSEC_IO_SSL;\n      if (err != IO_DONE)    return err;\n      break;\n    case SSL_ERROR_SYSCALL:\n      if (ERR_peek_error())  {\n        ssl->error = SSL_ERROR_SSL;\n        return LSEC_IO_SSL;\n      }\n      if (err == 0)\n        return IO_CLOSED;\n      return lsec_socket_error();\n    default:\n      return LSEC_IO_SSL;\n    }\n  }\n  return IO_UNKNOWN;\n}\n\nstatic SSL_CTX* luaossl_testcontext(lua_State *L, int arg) {\n  SSL_CTX **ctx = luaL_testudata(L, arg, \"SSL_CTX*\");\n  if (ctx)\n    return *ctx;\n  return NULL;\n}\n\nstatic SSL* luaossl_testssl(lua_State *L, int arg) {\n  SSL **ssl = luaL_testudata(L, arg, \"SSL*\");\n  if (ssl)\n    return *ssl;\n  return NULL;\n}\n\n/**\n * Create a new TLS/SSL object and mark it as new.\n */\nstatic int meth_create(lua_State *L)\n{\n  p_ssl ssl;\n  int mode;\n  SSL_CTX *ctx;\n\n  lua_settop(L, 1);\n\n  ssl = (p_ssl)lua_newuserdata(L, sizeof(t_ssl));\n  if (!ssl) {\n    lua_pushnil(L);\n    lua_pushstring(L, \"error creating SSL object\");\n    return 2;\n  }\n\n  if ((ctx = lsec_testcontext(L, 1))) {\n    mode = lsec_getmode(L, 1);\n    if (mode == LSEC_MODE_INVALID) {\n      lua_pushnil(L);\n      lua_pushstring(L, \"invalid mode\");\n      return 2;\n    }\n    ssl->ssl = SSL_new(ctx);\n    if (!ssl->ssl) {\n      lua_pushnil(L);\n      lua_pushfstring(L, \"error creating SSL object (%s)\",\n        ERR_reason_error_string(ERR_get_error()));\n      return 2;\n    }\n  } else if ((ctx = luaossl_testcontext(L, 1))) {\n    ssl->ssl = SSL_new(ctx);\n    if (!ssl->ssl) {\n      lua_pushnil(L);\n      lua_pushfstring(L, \"error creating SSL object (%s)\",\n        ERR_reason_error_string(ERR_get_error()));\n      return 2;\n    }\n    mode = SSL_is_server(ssl->ssl) ? LSEC_MODE_SERVER : LSEC_MODE_CLIENT;\n  } else if ((ssl->ssl = luaossl_testssl(L, 1))) {\n    SSL_up_ref(ssl->ssl);\n    mode = SSL_is_server(ssl->ssl) ? LSEC_MODE_SERVER : LSEC_MODE_CLIENT;\n  } else {\n    return luaL_argerror(L, 1, \"invalid context\");\n  }\n  ssl->state = LSEC_STATE_NEW;\n  SSL_set_fd(ssl->ssl, (int)SOCKET_INVALID);\n  SSL_set_mode(ssl->ssl, SSL_MODE_ENABLE_PARTIAL_WRITE | \n    SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER);\n  SSL_set_mode(ssl->ssl, SSL_MODE_RELEASE_BUFFERS);\n  if (mode == LSEC_MODE_SERVER)\n    SSL_set_accept_state(ssl->ssl);\n  else\n    SSL_set_connect_state(ssl->ssl);\n\n  io_init(&ssl->io, (p_send)ssl_send, (p_recv)ssl_recv, \n    (p_error) ssl_ioerror, ssl);\n  timeout_init(&ssl->tm, -1, -1);\n  buffer_init(&ssl->buf, &ssl->io, &ssl->tm);\n\n  luaL_getmetatable(L, \"SSL:Connection\");\n  lua_setmetatable(L, -2);\n  return 1;\n}\n\n/**\n * Buffer send function\n */\nstatic int meth_send(lua_State *L) {\n  p_ssl ssl = (p_ssl)luaL_checkudata(L, 1, \"SSL:Connection\");\n  return buffer_meth_send(L, &ssl->buf);\n}\n\n/**\n * Buffer receive function\n */\nstatic int meth_receive(lua_State *L) {\n  p_ssl ssl = (p_ssl)luaL_checkudata(L, 1, \"SSL:Connection\");\n  return buffer_meth_receive(L, &ssl->buf);\n}\n\n/**\n * Get the buffer's statistics.\n */\nstatic int meth_getstats(lua_State *L) {\n  p_ssl ssl = (p_ssl)luaL_checkudata(L, 1, \"SSL:Connection\");\n  return buffer_meth_getstats(L, &ssl->buf);\n}\n\n/**\n * Set the buffer's statistics.\n */\nstatic int meth_setstats(lua_State *L) {\n  p_ssl ssl = (p_ssl)luaL_checkudata(L, 1, \"SSL:Connection\");\n  return buffer_meth_setstats(L, &ssl->buf);\n}\n\n/**\n * Select support methods\n */\nstatic int meth_getfd(lua_State *L)\n{\n  p_ssl ssl = (p_ssl)luaL_checkudata(L, 1, \"SSL:Connection\");\n  lua_pushnumber(L, ssl->sock);\n  return 1;\n}\n\n/**\n * Set the TLS/SSL file descriptor.\n * Call it *before* the handshake.\n */\nstatic int meth_setfd(lua_State *L)\n{\n  p_ssl ssl = (p_ssl)luaL_checkudata(L, 1, \"SSL:Connection\");\n  if (ssl->state != LSEC_STATE_NEW)\n    luaL_argerror(L, 1, \"invalid SSL object state\");\n  ssl->sock = (t_socket)luaL_checkinteger(L, 2);\n  socket_setnonblocking(&ssl->sock);\n  SSL_set_fd(ssl->ssl, (int)ssl->sock);\n  return 0;\n}\n\n/**\n * Lua handshake function.\n */\nstatic int meth_handshake(lua_State *L)\n{\n  int err;\n  p_ssl ssl = (p_ssl)luaL_checkudata(L, 1, \"SSL:Connection\");\n  p_context ctx = (p_context)SSL_CTX_get_app_data(SSL_get_SSL_CTX(ssl->ssl));\n  ctx->L = L;\n  err = handshake(ssl);\n  if (ctx->dh_param) {\n    DH_free(ctx->dh_param);\n    ctx->dh_param = NULL;\n  }\n  if (ctx->alpn) {\n    free(ctx->alpn);\n    ctx->alpn = NULL;\n  }\n  if (err == IO_DONE) {\n    lua_pushboolean(L, 1);\n    return 1;\n  }\n  lua_pushboolean(L, 0);\n  lua_pushstring(L, ssl_ioerror((void*)ssl, err));\n  return 2;\n}\n\n/**\n * Close the connection.\n */\nstatic int meth_close(lua_State *L)\n{\n  meth_destroy(L);\n  return 0;\n}\n\n/**\n * Set timeout.\n */\nstatic int meth_settimeout(lua_State *L)\n{\n  p_ssl ssl = (p_ssl)luaL_checkudata(L, 1, \"SSL:Connection\");\n  return timeout_meth_settimeout(L, &ssl->tm);\n}\n\n/**\n * Check if there is data in the buffer.\n */\nstatic int meth_dirty(lua_State *L)\n{\n  int res = 0;\n  p_ssl ssl = (p_ssl)luaL_checkudata(L, 1, \"SSL:Connection\");\n  if (ssl->state != LSEC_STATE_CLOSED)\n    res = !buffer_isempty(&ssl->buf) || SSL_pending(ssl->ssl);\n  lua_pushboolean(L, res);\n  return 1;\n}\n\n/**\n * Return the state information about the SSL object.\n */\nstatic int meth_want(lua_State *L)\n{\n  p_ssl ssl = (p_ssl)luaL_checkudata(L, 1, \"SSL:Connection\");\n  int code = (ssl->state == LSEC_STATE_CLOSED)\n             ? SSL_NOTHING\n             : SSL_want(ssl->ssl);\n  switch(code) {\n  case SSL_NOTHING: lua_pushstring(L, \"nothing\"); break;\n  case SSL_READING: lua_pushstring(L, \"read\"); break;\n  case SSL_WRITING: lua_pushstring(L, \"write\"); break;\n  case SSL_X509_LOOKUP: lua_pushstring(L, \"x509lookup\"); break;\n  }\n  return 1;\n}\n\n/**\n * Return the compression method used.\n */\nstatic int meth_compression(lua_State *L)\n{\n#ifdef OPENSSL_NO_COMP\n  const void *comp;\n#else\n  const COMP_METHOD *comp;\n#endif\n  p_ssl ssl = (p_ssl)luaL_checkudata(L, 1, \"SSL:Connection\");\n  if (ssl->state != LSEC_STATE_CONNECTED) {\n    lua_pushnil(L);\n    lua_pushstring(L, \"closed\");\n    return 2;\n  }\n  comp = SSL_get_current_compression(ssl->ssl);\n  if (comp)\n    lua_pushstring(L, SSL_COMP_get_name(comp));\n  else\n    lua_pushnil(L);\n  return 1;\n}\n\n/**\n * Return the nth certificate of the peer's chain.\n */\nstatic int meth_getpeercertificate(lua_State *L)\n{\n  int n;\n  X509 *cert;\n  STACK_OF(X509) *certs;\n  p_ssl ssl = (p_ssl)luaL_checkudata(L, 1, \"SSL:Connection\");\n  if (ssl->state != LSEC_STATE_CONNECTED) {\n    lua_pushnil(L);\n    lua_pushstring(L, \"closed\");\n    return 2;\n  }\n  /* Default to the first cert */ \n  n = (int)luaL_optinteger(L, 2, 1);                           \n  /* This function is 1-based, but OpenSSL is 0-based */\n  --n;\n  if (n < 0) {\n    lua_pushnil(L);\n    lua_pushliteral(L, \"invalid certificate index\");\n    return 2;\n  }\n  if (n == 0) {\n    cert = SSL_get_peer_certificate(ssl->ssl);\n    if (cert)\n      lsec_pushx509(L, cert);\n    else\n      lua_pushnil(L);\n    return 1;\n  }\n  /* In a server-context, the stack doesn't contain the peer cert,\n   * so adjust accordingly.\n   */\n  if (SSL_is_server(ssl->ssl))\n    --n;\n  certs = SSL_get_peer_cert_chain(ssl->ssl);\n  if (n >= sk_X509_num(certs)) {\n    lua_pushnil(L);\n    return 1;\n  }\n  cert = sk_X509_value(certs, n);\n  /* Increment the reference counting of the object. */\n  /* See SSL_get_peer_certificate() source code.     */\n  X509_up_ref(cert);\n  lsec_pushx509(L, cert);\n  return 1;\n}\n\n/**\n * Return the chain of certificate of the peer.\n */\nstatic int meth_getpeerchain(lua_State *L)\n{\n  int i;\n  int idx = 1;\n  int n_certs;\n  X509 *cert;\n  STACK_OF(X509) *certs;\n  p_ssl ssl = (p_ssl)luaL_checkudata(L, 1, \"SSL:Connection\");\n  if (ssl->state != LSEC_STATE_CONNECTED) {\n    lua_pushnil(L);\n    lua_pushstring(L, \"closed\");\n    return 2;\n  }\n  lua_newtable(L);\n  if (SSL_is_server(ssl->ssl)) {\n    lsec_pushx509(L, SSL_get_peer_certificate(ssl->ssl));\n    lua_rawseti(L, -2, idx++);\n  }\n  certs = SSL_get_peer_cert_chain(ssl->ssl);\n  n_certs = sk_X509_num(certs);\n  for (i = 0; i < n_certs; i++) {\n    cert = sk_X509_value(certs, i);\n    /* Increment the reference counting of the object. */\n    /* See SSL_get_peer_certificate() source code.     */\n    X509_up_ref(cert);\n    lsec_pushx509(L, cert);\n    lua_rawseti(L, -2, idx++);\n  }\n  return 1;\n}\n\n/**\n * Copy the table src to the table dst.\n */\nstatic void copy_error_table(lua_State *L, int src, int dst)\n{\n  lua_pushnil(L); \n  while (lua_next(L, src) != 0) {\n    if (lua_istable(L, -1)) {\n      /* Replace the table with its copy */\n      lua_newtable(L);\n      copy_error_table(L, dst+2, dst+3);\n      lua_remove(L, dst+2);\n    }\n    lua_pushvalue(L, -2);\n    lua_pushvalue(L, -2);\n    lua_rawset(L, dst);\n    /* Remove the value and leave the key */\n    lua_pop(L, 1);\n  }\n}\n\n/**\n * Return the verification state of the peer chain.\n */\nstatic int meth_getpeerverification(lua_State *L)\n{\n  long err;\n  p_ssl ssl = (p_ssl)luaL_checkudata(L, 1, \"SSL:Connection\");\n  if (ssl->state != LSEC_STATE_CONNECTED) {\n    lua_pushboolean(L, 0);\n    lua_pushstring(L, \"closed\");\n    return 2;\n  }\n  err = SSL_get_verify_result(ssl->ssl);\n  if (err == X509_V_OK) {\n    lua_pushboolean(L, 1);\n    return 1;\n  }\n  luaL_getmetatable(L, \"SSL:Verify:Registry\");\n  lua_pushlightuserdata(L, (void*)ssl->ssl);\n  lua_gettable(L, -2);\n  if (lua_isnil(L, -1))\n    lua_pushstring(L, X509_verify_cert_error_string(err));\n  else {\n    /* Copy the table of errors to avoid modifications */\n    lua_newtable(L);\n    copy_error_table(L, lua_gettop(L)-1, lua_gettop(L));\n  }\n  lua_pushboolean(L, 0);\n  lua_pushvalue(L, -2);\n  return 2;\n}\n\n/**\n * Get the latest \"Finished\" message sent out.\n */\nstatic int meth_getfinished(lua_State *L)\n{\n  size_t len = 0;\n  char *buffer = NULL;\n  p_ssl ssl = (p_ssl)luaL_checkudata(L, 1, \"SSL:Connection\");\n  if (ssl->state != LSEC_STATE_CONNECTED) {\n    lua_pushnil(L);\n    lua_pushstring(L, \"closed\");\n    return 2;\n  }\n  if ((len = SSL_get_finished(ssl->ssl, NULL, 0)) == 0)\n    return 0;\n  buffer = (char*)malloc(len);\n  if (!buffer) {\n    lua_pushnil(L);\n    lua_pushstring(L, \"out of memory\");\n    return 2;\n  }\n  SSL_get_finished(ssl->ssl, buffer, len);\n  lua_pushlstring(L, buffer, len);\n  free(buffer);\n  return 1;\n}\n\n/**\n * Gets the latest \"Finished\" message received.\n */\nstatic int meth_getpeerfinished(lua_State *L)\n{\n  size_t len = 0;\n  char *buffer = NULL;\n  p_ssl ssl = (p_ssl)luaL_checkudata(L, 1, \"SSL:Connection\");\n  if (ssl->state != LSEC_STATE_CONNECTED) {\n    lua_pushnil(L);\n    lua_pushstring(L, \"closed\");\n    return 0;\n  }\n  if ((len = SSL_get_peer_finished(ssl->ssl, NULL, 0)) == 0)\n    return 0;\n  buffer = (char*)malloc(len);\n  if (!buffer) {\n    lua_pushnil(L);\n    lua_pushstring(L, \"out of memory\");\n    return 2;\n  }\n  SSL_get_peer_finished(ssl->ssl, buffer, len);\n  lua_pushlstring(L, buffer, len);\n  free(buffer);\n  return 1;\n}\n\n/**\n * Object information -- tostring metamethod\n */\nstatic int meth_tostring(lua_State *L)\n{\n  p_ssl ssl = (p_ssl)luaL_checkudata(L, 1, \"SSL:Connection\");\n  lua_pushfstring(L, \"SSL connection: %p%s\", ssl,\n    ssl->state == LSEC_STATE_CLOSED ? \" (closed)\" : \"\");\n  return 1;\n}\n\n/**\n * Add a method in the SSL metatable.\n */\nstatic int meth_setmethod(lua_State *L)\n{\n  luaL_getmetatable(L, \"SSL:Connection\");\n  lua_pushstring(L, \"__index\");\n  lua_gettable(L, -2);\n  lua_pushvalue(L, 1);\n  lua_pushvalue(L, 2);\n  lua_settable(L, -3);\n  return 0;\n}\n\n/**\n * Return information about the connection.\n */\nstatic int meth_info(lua_State *L)\n{\n  int bits = 0;\n  int algbits = 0;\n  char buf[256] = {0};\n  const SSL_CIPHER *cipher;\n  p_ssl ssl = (p_ssl)luaL_checkudata(L, 1, \"SSL:Connection\");\n  cipher = SSL_get_current_cipher(ssl->ssl);\n  if (!cipher)\n    return 0;\n  SSL_CIPHER_description(cipher, buf, sizeof(buf));\n  bits = SSL_CIPHER_get_bits(cipher, &algbits);\n  lua_pushstring(L, buf);\n  lua_pushnumber(L, bits);\n  lua_pushnumber(L, algbits);\n  lua_pushstring(L, SSL_get_version(ssl->ssl));\n  return 4;\n}\n\nstatic int sni_cb(SSL *ssl, int *ad, void *arg)\n{\n  int strict;\n  SSL_CTX *newctx = NULL;\n  SSL_CTX *ctx = SSL_get_SSL_CTX(ssl);\n  lua_State *L = ((p_context)SSL_CTX_get_app_data(ctx))->L;\n  const char *name = SSL_get_servername(ssl, TLSEXT_NAMETYPE_host_name);\n  /* No name, use default context */\n  if (!name)\n    return SSL_TLSEXT_ERR_NOACK;\n  /* Retrieve struct from registry */\n  luaL_getmetatable(L, \"SSL:SNI:Registry\");\n  lua_pushlightuserdata(L, (void*)ssl);\n  lua_gettable(L, -2);\n  /* Strict search? */\n  lua_pushstring(L, \"strict\");\n  lua_gettable(L, -2);\n  strict = lua_toboolean(L, -1);\n  lua_pop(L, 1);\n  /* Search for the name in the map */\n  lua_pushstring(L, \"map\");\n  lua_gettable(L, -2);\n  lua_pushstring(L, name);\n  lua_gettable(L, -2);\n  if (lua_isuserdata(L, -1))\n    newctx = lsec_checkcontext(L, -1);\n  lua_pop(L, 4);\n  /* Found, use this context */\n  if (newctx) {\n    p_context pctx = (p_context)SSL_CTX_get_app_data(newctx);\n    pctx->L = L;\n    SSL_set_SSL_CTX(ssl, newctx);\n    return SSL_TLSEXT_ERR_OK;\n  }\n  /* Not found, but use initial context */\n  if (!strict)\n    return SSL_TLSEXT_ERR_OK;\n  return SSL_TLSEXT_ERR_ALERT_FATAL;\n}\n\nstatic int meth_sni(lua_State *L)\n{\n  int strict;\n  SSL_CTX *aux;\n  const char *name;\n  p_ssl ssl = (p_ssl)luaL_checkudata(L, 1, \"SSL:Connection\");\n  SSL_CTX *ctx = SSL_get_SSL_CTX(ssl->ssl);\n  p_context pctx = (p_context)SSL_CTX_get_app_data(ctx);\n  if (pctx->mode == LSEC_MODE_CLIENT) {\n    name = luaL_checkstring(L, 2);\n    SSL_set_tlsext_host_name(ssl->ssl, name);\n    return 0;\n  } else if (pctx->mode == LSEC_MODE_SERVER) {\n    luaL_checktype(L, 2, LUA_TTABLE);\n    strict = lua_toboolean(L, 3);\n    /* Check if the table contains only (string -> context) */\n    lua_pushnil(L);\n    while (lua_next(L, 2)) {\n      luaL_checkstring(L, -2);\n      aux = lsec_checkcontext(L, -1);\n      /* Set callback in every context */\n      SSL_CTX_set_tlsext_servername_callback(aux, sni_cb);\n      /* leave the next key on the stack */\n      lua_pop(L, 1);\n    }\n    /* Save table in the register */\n    luaL_getmetatable(L, \"SSL:SNI:Registry\");\n    lua_pushlightuserdata(L, (void*)ssl->ssl);\n    lua_newtable(L);\n    lua_pushstring(L, \"map\");\n    lua_pushvalue(L, 2);\n    lua_settable(L, -3);\n    lua_pushstring(L, \"strict\");\n    lua_pushboolean(L, strict);\n    lua_settable(L, -3);\n    lua_settable(L, -3);\n    /* Set callback in the default context */\n    SSL_CTX_set_tlsext_servername_callback(ctx, sni_cb);\n  }\n  return 0;\n}\n\nstatic int meth_getsniname(lua_State *L)\n{\n  p_ssl ssl = (p_ssl)luaL_checkudata(L, 1, \"SSL:Connection\");\n  const char *name = SSL_get_servername(ssl->ssl, TLSEXT_NAMETYPE_host_name);\n  if (name)\n    lua_pushstring(L, name);\n  else\n    lua_pushnil(L);\n  return 1;\n}\n\nstatic int meth_getalpn(lua_State *L)\n{\n  unsigned len;\n  const unsigned char *data;\n  p_ssl ssl = (p_ssl)luaL_checkudata(L, 1, \"SSL:Connection\");\n  SSL_get0_alpn_selected(ssl->ssl, &data, &len);\n  if (data == NULL && len == 0)\n    lua_pushnil(L);\n  else\n    lua_pushlstring(L, (const char*)data, len);\n  return 1;\n}\n\nstatic int meth_copyright(lua_State *L)\n{\n  lua_pushstring(L, \"LuaSec 1.0.2 - Copyright (C) 2006-2021 Bruno Silvestre, UFG\"\n#if defined(WITH_LUASOCKET)\n                    \"\\nLuaSocket 3.0-RC1 - Copyright (C) 2004-2013 Diego Nehab\"\n#endif\n  );\n  return 1;\n}\n\n#if defined(LSEC_ENABLE_DANE)\nstatic int meth_dane(lua_State *L)\n{\n  int ret;\n  p_ssl ssl = (p_ssl)luaL_checkudata(L, 1, \"SSL:Connection\");\n  ret = SSL_dane_enable(ssl->ssl, luaL_checkstring(L, 2));\n  lua_pushboolean(L, (ret > 0));\n  return 1;\n}\n\nstatic int meth_tlsa(lua_State *L)\n{\n  int ret;\n  size_t len;\n  p_ssl ssl = (p_ssl)luaL_checkudata(L, 1, \"SSL:Connection\");\n  uint8_t usage = (uint8_t)luaL_checkinteger(L, 2);\n  uint8_t selector = (uint8_t)luaL_checkinteger(L, 3);\n  uint8_t mtype = (uint8_t)luaL_checkinteger(L, 4);\n  unsigned char *data = (unsigned char*)luaL_checklstring(L, 5, &len);\n\n  ERR_clear_error();\n  ret = SSL_dane_tlsa_add(ssl->ssl, usage, selector, mtype, data, len);\n  lua_pushboolean(L, (ret > 0));\n\n  return 1;\n}\n#endif\n\n/*---------------------------------------------------------------------------*/\n\n/**\n * SSL methods \n */\nstatic luaL_Reg methods[] = {\n  {\"close\",               meth_close},\n  {\"getalpn\",             meth_getalpn},\n  {\"getfd\",               meth_getfd},\n  {\"getfinished\",         meth_getfinished},\n  {\"getpeercertificate\",  meth_getpeercertificate},\n  {\"getpeerchain\",        meth_getpeerchain},\n  {\"getpeerverification\", meth_getpeerverification},\n  {\"getpeerfinished\",     meth_getpeerfinished},\n  {\"getsniname\",          meth_getsniname},\n  {\"getstats\",            meth_getstats},\n  {\"setstats\",            meth_setstats},\n  {\"dirty\",               meth_dirty},\n  {\"dohandshake\",         meth_handshake},\n  {\"receive\",             meth_receive},\n  {\"send\",                meth_send},\n  {\"settimeout\",          meth_settimeout},\n  {\"sni\",                 meth_sni},\n  {\"want\",                meth_want},\n#if defined(LSEC_ENABLE_DANE)\n  {\"setdane\",             meth_dane},\n  {\"settlsa\",             meth_tlsa},\n#endif\n  {NULL,                  NULL}\n};\n\n/**\n * SSL metamethods.\n */\nstatic luaL_Reg meta[] = {\n  {\"__close\",    meth_destroy},\n  {\"__gc\",       meth_destroy},\n  {\"__tostring\", meth_tostring},\n  {NULL, NULL}\n};\n\n/**\n * SSL functions. \n */\nstatic luaL_Reg funcs[] = {\n  {\"compression\", meth_compression},\n  {\"create\",      meth_create},\n  {\"info\",        meth_info},\n  {\"setfd\",       meth_setfd},\n  {\"setmethod\",   meth_setmethod},\n  {\"copyright\",   meth_copyright},\n  {NULL,          NULL}\n};\n\n/**\n * Initialize modules.\n */\nLSEC_API int luaopen_ssl_core(lua_State *L)\n{\n#ifndef LSEC_API_OPENSSL_1_1_0\n  /* Initialize SSL */\n  if (!SSL_library_init()) {\n    lua_pushstring(L, \"unable to initialize SSL library\");\n    lua_error(L);\n  }\n  OpenSSL_add_all_algorithms();\n  SSL_load_error_strings();\n#endif\n\n#if defined(WITH_LUASOCKET)\n  /* Initialize internal library */\n  socket_open();\n#endif\n\n  luaL_newmetatable(L, \"SSL:SNI:Registry\");\n\n  /* Register the functions and tables */\n  luaL_newmetatable(L, \"SSL:Connection\");\n  setfuncs(L, meta);\n\n  luaL_newlib(L, methods);\n  lua_setfield(L, -2, \"__index\");\n\n  luaL_newlib(L, funcs);\n  lua_pushvalue(L, -1);\n  lua_setglobal(L, \"ssl\");\n\n  lua_pushstring(L, \"SOCKET_INVALID\");\n  lua_pushinteger(L, SOCKET_INVALID);\n  lua_rawset(L, -3);\n\n  return 1;\n}\n\n//------------------------------------------------------------------------------\n\n#if defined(_MSC_VER)\n\n/* Empty implementation to allow building with LuaRocks and MS compilers */\nLSEC_API int luaopen_ssl(lua_State *L) {\n  lua_pushstring(L, \"you should not call this function\");\n  lua_error(L);\n  return 0;\n}\n\n#endif\n"
  },
  {
    "path": "src/luasec/ssl.h",
    "content": "#ifndef LSEC_SSL_H\n#define LSEC_SSL_H\n\n/*--------------------------------------------------------------------------\n * LuaSec 1.0.2\n *\n * Copyright (C) 2006-2021 Bruno Silvestre\n *\n *--------------------------------------------------------------------------*/\n\n#include <openssl/ssl.h>\n#include \"../lua.h\"\n\n#include \"../luasocket/io.h\"\n#include \"../luasocket/buffer.h\"\n#include \"../luasocket/timeout.h\"\n#include \"../luasocket/socket.h\"\n\n#include \"compat.h\"\n#include \"context.h\"\n\n#define LSEC_STATE_NEW       1\n#define LSEC_STATE_CONNECTED 2\n#define LSEC_STATE_CLOSED    3\n\n#define LSEC_IO_SSL          -100\n\ntypedef struct t_ssl_ {\n  t_socket sock;\n  t_io io;\n  t_buffer buf;\n  t_timeout tm;\n  SSL *ssl;\n  int state;\n  int error;\n} t_ssl;\ntypedef t_ssl* p_ssl;\n\nLSEC_API int luaopen_ssl_core(lua_State *L);\n\n#endif\n"
  },
  {
    "path": "src/luasec/ssl.lua",
    "content": "------------------------------------------------------------------------------\n-- LuaSec 1.0.2\n--\n-- Copyright (C) 2006-2021 Bruno Silvestre\n--\n------------------------------------------------------------------------------\n\nlocal core    = ssl\n\n-- We must prevent the contexts to be collected before the connections,\n-- otherwise the C registry will be cleared.\nlocal registry = setmetatable({}, {__mode=\"k\"})\n\n--\n--\n--\nlocal function optexec(func, param, ctx)\n  if param then\n    if type(param) == \"table\" then\n      return func(ctx, unpack(param))\n    else\n      return func(ctx, param)\n    end\n  end\n  return true\nend\n\n--\n-- Convert an array of strings to wire-format\n--\nlocal function array2wireformat(array)\n   local str = \"\"\n   for k, v in ipairs(array) do\n      if type(v) ~= \"string\" then return nil end\n      local len = #v\n      if len == 0 then\n        return nil, \"invalid ALPN name (empty string)\"\n      elseif len > 255 then\n        return nil, \"invalid ALPN name (length > 255)\"\n      end\n      str = str .. string.char(len) .. v\n   end\n   if str == \"\" then return nil, \"invalid ALPN list (empty)\" end\n   return str\nend\n\n--\n-- Convert wire-string format to array\n--\nlocal function wireformat2array(str)\n   local i = 1\n   local array = {}\n   while i < #str do\n      local len = str:byte(i)\n      array[#array + 1] = str:sub(i + 1, i + len)\n      i = i + len + 1\n   end\n   return array\nend\n\n--\n--\n--\nlocal function newcontext(cfg)\n   local succ, msg, ctx\n   -- Create the context\n   ctx, msg = context.create(cfg.protocol)\n   if not ctx then return nil, msg end\n   -- Mode\n   succ, msg = context.setmode(ctx, cfg.mode)\n   if not succ then return nil, msg end\n   local certificates = cfg.certificates\n   if not certificates then\n      certificates = {\n         { certificate = cfg.certificate, key = cfg.key, password = cfg.password }\n      }\n   end\n   for _, certificate in ipairs(certificates) do\n      -- Load the key\n      if certificate.key then\n         if certificate.password and\n            type(certificate.password) ~= \"function\" and\n            type(certificate.password) ~= \"string\"\n         then\n            return nil, \"invalid password type\"\n         end\n         succ, msg = context.loadkey(ctx, certificate.key, certificate.password)\n         if not succ then return nil, msg end\n      end\n      -- Load the certificate(s)\n      if certificate.certificate then\n        succ, msg = context.loadcert(ctx, certificate.certificate)\n        if not succ then return nil, msg end\n        if certificate.key and context.checkkey then\n          succ = context.checkkey(ctx)\n          if not succ then return nil, \"private key does not match public key\" end\n        end\n      end\n   end\n   -- Load the CA certificates\n   if cfg.cafile or cfg.capath then\n      succ, msg = context.locations(ctx, cfg.cafile, cfg.capath)\n      if not succ then return nil, msg end\n   end\n   -- Set SSL ciphers\n   if cfg.ciphers then\n      succ, msg = context.setcipher(ctx, cfg.ciphers)\n      if not succ then return nil, msg end\n   end\n   -- Set SSL cipher suites\n   if cfg.ciphersuites then\n      succ, msg = context.setciphersuites(ctx, cfg.ciphersuites)\n      if not succ then return nil, msg end\n   end\n    -- Set the verification options\n   succ, msg = optexec(context.setverify, cfg.verify, ctx)\n   if not succ then return nil, msg end\n   -- Set SSL options\n   succ, msg = optexec(context.setoptions, cfg.options, ctx)\n   if not succ then return nil, msg end\n   -- Set the depth for certificate verification\n   if cfg.depth then\n      succ, msg = context.setdepth(ctx, cfg.depth)\n      if not succ then return nil, msg end\n   end\n\n   -- NOTE: Setting DH parameters and elliptic curves needs to come after\n   -- setoptions(), in case the user has specified the single_{dh,ecdh}_use\n   -- options.\n\n   -- Set DH parameters\n   if cfg.dhparam then\n      if type(cfg.dhparam) ~= \"function\" then\n         return nil, \"invalid DH parameter type\"\n      end\n      context.setdhparam(ctx, cfg.dhparam)\n   end\n   \n   -- Set elliptic curves\n   if (not config.algorithms.ec) and (cfg.curve or cfg.curveslist) then\n     return false, \"elliptic curves not supported\"\n   end\n   if config.capabilities.curves_list and cfg.curveslist then\n     succ, msg = context.setcurveslist(ctx, cfg.curveslist)\n     if not succ then return nil, msg end\n   elseif cfg.curve then\n     succ, msg = context.setcurve(ctx, cfg.curve)\n     if not succ then return nil, msg end\n   end\n\n   -- Set extra verification options\n   if cfg.verifyext and ctx.setverifyext then\n      succ, msg = optexec(ctx.setverifyext, cfg.verifyext, ctx)\n      if not succ then return nil, msg end\n   end\n\n   -- ALPN\n   if cfg.mode == \"server\" and cfg.alpn then\n      if type(cfg.alpn) == \"function\" then\n         local alpncb = cfg.alpn\n         -- This callback function has to return one value only\n         succ, msg = context.setalpncb(ctx, function(str)\n            local protocols = alpncb(wireformat2array(str))\n            if type(protocols) == \"string\" then\n               protocols = { protocols }\n            elseif type(protocols) ~= \"table\" then\n               return nil\n            end\n            return (array2wireformat(protocols))    -- use \"()\" to drop error message\n         end)\n         if not succ then return nil, msg end\n      elseif type(cfg.alpn) == \"table\" then\n         local protocols = cfg.alpn\n         -- check if array is valid before use it\n         succ, msg = array2wireformat(protocols)\n         if not succ then return nil, msg end\n         -- This callback function has to return one value only\n         succ, msg = context.setalpncb(ctx, function()\n            return (array2wireformat(protocols))    -- use \"()\" to drop error message\n         end)\n         if not succ then return nil, msg end\n      else\n         return nil, \"invalid ALPN parameter\"\n      end\n   elseif cfg.mode == \"client\" and cfg.alpn then\n      local alpn\n      if type(cfg.alpn) == \"string\" then\n         alpn, msg = array2wireformat({ cfg.alpn })\n      elseif type(cfg.alpn) == \"table\" then\n         alpn, msg = array2wireformat(cfg.alpn)\n      else\n         return nil, \"invalid ALPN parameter\"\n      end\n      if not alpn then return nil, msg end\n      succ, msg = context.setalpn(ctx, alpn)\n      if not succ then return nil, msg end\n   end\n\n   if config.capabilities.dane and cfg.dane then\n      context.setdane(ctx)\n   end\n\n   return ctx\nend\n\n--\n--\n--\nlocal function wrap(sock, cfg)\n   local ctx, msg\n   if type(cfg) == \"table\" then\n      ctx, msg = newcontext(cfg)\n      if not ctx then return nil, msg end\n   else\n      ctx = cfg\n   end\n   local s, msg = core.create(ctx)\n   if s then\n      core.setfd(s, sock:getfd())\n      sock:setfd(core.SOCKET_INVALID)\n      registry[s] = ctx\n      return s\n   end\n   return nil, msg \nend\n\n--\n-- Extract connection information.\n--\nlocal function info(ssl, field)\n  local str, comp, err, protocol\n  comp, err = core.compression(ssl)\n  if err then\n    return comp, err\n  end\n  -- Avoid parser\n  if field == \"compression\" then\n    return comp\n  end\n  local info = {compression = comp}\n  str, info.bits, info.algbits, protocol = core.info(ssl)\n  if str then\n    info.cipher, info.protocol, info.key,\n    info.authentication, info.encryption, info.mac =\n        string.match(str, \n          \"^(%S+)%s+(%S+)%s+Kx=(%S+)%s+Au=(%S+)%s+Enc=(%S+)%s+Mac=(%S+)\")\n    info.export = (string.match(str, \"%sexport%s*$\") ~= nil)\n  end\n  if protocol then\n    info.protocol = protocol\n  end\n  if field then\n    return info[field]\n  end\n  -- Empty?\n  return ( (next(info)) and info )\nend\n\n--\n-- Set method for SSL connections.\n--\ncore.setmethod(\"info\", info)\n\n--------------------------------------------------------------------------------\n-- Export module\n--\n\nlocal _M = {\n  _VERSION        = \"1.0.2\",\n  _COPYRIGHT      = core.copyright(),\n  config          = config,\n  loadcertificate = x509.load,\n  newcontext      = newcontext,\n  wrap            = wrap,\n}\n\nreturn _M\n"
  },
  {
    "path": "src/luasec/x509.c",
    "content": "/*--------------------------------------------------------------------------\n * LuaSec 1.0.2\n *\n * Copyright (C) 2014-2021 Kim Alvefur, Paul Aurich, Tobias Markmann\n *                         Matthew Wild, Bruno Silvestre.\n *\n *--------------------------------------------------------------------------*/\n\n#include <stdio.h>\n#include <string.h>\n\n#if defined(WIN32)\n#include <ws2tcpip.h>\n#include <windows.h>\n#else\n#include <sys/types.h>\n#include <sys/socket.h>\n#include <netinet/in.h>\n#include <arpa/inet.h>\n#endif\n\n#include <openssl/ssl.h>\n#include <openssl/x509v3.h>\n#include <openssl/evp.h>\n#include <openssl/err.h>\n#include <openssl/asn1.h>\n#include <openssl/bio.h>\n#include <openssl/bn.h>\n\n#include \"../lua.h\"\n#include \"../lauxlib.h\"\n\n#include \"x509.h\"\n\n\n#ifndef LSEC_API_OPENSSL_1_1_0\n#define X509_get0_notBefore   X509_get_notBefore\n#define X509_get0_notAfter    X509_get_notAfter\n#define ASN1_STRING_get0_data ASN1_STRING_data\n#endif\n\nstatic const char* hex_tab = \"0123456789abcdef\";\n\n/**\n * Push the certificate on the stack.\n */\nvoid lsec_pushx509(lua_State* L, X509 *cert)\n{\n  p_x509 cert_obj = (p_x509)lua_newuserdata(L, sizeof(t_x509));\n  cert_obj->cert = cert;\n  cert_obj->encode = LSEC_AI5_STRING;\n  luaL_getmetatable(L, \"SSL:Certificate\");\n  lua_setmetatable(L, -2);\n}\n\n/**\n * Return the OpenSSL certificate X509.\n */\nX509* lsec_checkx509(lua_State* L, int idx)\n{\n  return ((p_x509)luaL_checkudata(L, idx, \"SSL:Certificate\"))->cert;\n}\n\n/**\n * Return LuaSec certificate X509 representation.\n */\np_x509 lsec_checkp_x509(lua_State* L, int idx)\n{\n  return (p_x509)luaL_checkudata(L, idx, \"SSL:Certificate\");\n}\n\n/*---------------------------------------------------------------------------*/\n\n#if defined(LUASEC_INET_NTOP)\n/*\n * For WinXP (SP3), set the following preprocessor macros:\n *     LUASEC_INET_NTOP\n *     WINVER=0x0501\n *     _WIN32_WINNT=0x0501\n *     NTDDI_VERSION=0x05010300\n *\n * For IPv6 addresses, you need to add IPv6 Protocol to your interface.\n *\n */\nstatic const char *inet_ntop(int af, const char *src, char *dst, socklen_t size)\n{\n  int addrsize;\n  struct sockaddr    *addr;\n  struct sockaddr_in  addr4;\n  struct sockaddr_in6 addr6;\n\n  switch (af) {\n  case AF_INET:\n    memset((void*)&addr4, 0, sizeof(addr4));\n    addr4.sin_family = AF_INET;\n    memcpy((void*)&addr4.sin_addr, src, sizeof(struct in_addr));\n    addr = (struct sockaddr*)&addr4;\n    addrsize = sizeof(struct sockaddr_in);\n    break;\n  case AF_INET6:\n    memset((void*)&addr6, 0, sizeof(addr6));\n    addr6.sin6_family = AF_INET6;\n    memcpy((void*)&addr6.sin6_addr, src, sizeof(struct in6_addr));\n    addr = (struct sockaddr*)&addr6;\n    addrsize = sizeof(struct sockaddr_in6);\n    break;\n  default:\n    return NULL;\n  }\n\n  if(getnameinfo(addr, addrsize, dst, size, NULL, 0, NI_NUMERICHOST) != 0)\n    return NULL;\n  return dst;\n}\n#endif\n\n/*---------------------------------------------------------------------------*/\n\n/**\n * Convert the buffer 'in' to hexadecimal.\n */\nstatic void to_hex(const char* in, int length, char* out)\n{\n  int i;\n  for (i = 0; i < length; i++) {\n    out[i*2] = hex_tab[(in[i] >> 4) & 0xF];\n    out[i*2+1] = hex_tab[(in[i]) & 0xF];\n  }\n}\n\n/**\n * Converts the ASN1_OBJECT into a textual representation and put it\n * on the Lua stack.\n */\nstatic void push_asn1_objname(lua_State* L, ASN1_OBJECT *object, int no_name)\n{\n  char buffer[256];\n  int len = OBJ_obj2txt(buffer, sizeof(buffer), object, no_name);\n  len = (len < sizeof(buffer)) ? len : sizeof(buffer);\n  lua_pushlstring(L, buffer, len);\n}\n\n/**\n * Push the ASN1 string on the stack.\n */\nstatic void push_asn1_string(lua_State* L, ASN1_STRING *string, int encode)\n{\n  int len;\n  unsigned char *data;\n  if (!string) {\n    lua_pushnil(L);\n    return;\n  }\n  switch (encode) {\n  case LSEC_AI5_STRING:\n    lua_pushlstring(L, (char*)ASN1_STRING_get0_data(string), ASN1_STRING_length(string));\n    break;\n  case LSEC_UTF8_STRING:\n    len = ASN1_STRING_to_UTF8(&data, string);\n    if (len >= 0) {\n      lua_pushlstring(L, (char*)data, len);\n      OPENSSL_free(data);\n    }\n    else\n      lua_pushnil(L);\n  }\n}\n\n/**\n * Return a human readable time.\n */\nstatic int push_asn1_time(lua_State *L, const ASN1_UTCTIME *tm)\n{\n  char *tmp;\n  long size;\n  BIO *out = BIO_new(BIO_s_mem());\n  ASN1_TIME_print(out, tm);\n  size = BIO_get_mem_data(out, &tmp);\n  lua_pushlstring(L, tmp, size);\n  BIO_free(out);\n  return 1;\n}\n\n/**\n * Return a human readable IP address.\n */\nstatic void push_asn1_ip(lua_State *L, ASN1_STRING *string)\n{\n  int af;\n  char dst[INET6_ADDRSTRLEN];\n  unsigned char *ip = (unsigned char*)ASN1_STRING_get0_data(string);\n  switch(ASN1_STRING_length(string)) {\n  case 4:\n    af = AF_INET;\n    break;\n  case 16:\n    af = AF_INET6;\n    break;\n  default:\n    lua_pushnil(L);\n    return;\n  }\n  if(inet_ntop(af, ip, dst, INET6_ADDRSTRLEN))\n    lua_pushstring(L, dst);\n  else\n    lua_pushnil(L);\n}\n\n/**\n * \n */\nstatic int push_subtable(lua_State* L, int idx)\n{\n  lua_pushvalue(L, -1);\n  lua_gettable(L, idx-1);\n  if (lua_isnil(L, -1)) {\n    lua_pop(L, 1);\n    lua_newtable(L);\n    lua_pushvalue(L, -2);\n    lua_pushvalue(L, -2);\n    lua_settable(L, idx-3);\n    lua_replace(L, -2); /* Replace key with table */\n    return 1;\n  }\n  lua_replace(L, -2); /* Replace key with table */\n  return 0;\n}\n\n/**\n * Retrieve the general names from the object.\n */\nstatic int push_x509_name(lua_State* L, X509_NAME *name, int encode)\n{\n  int i;\n  int n_entries;\n  ASN1_OBJECT *object;\n  X509_NAME_ENTRY *entry;\n  lua_newtable(L);\n  n_entries = X509_NAME_entry_count(name);\n  for (i = 0; i < n_entries; i++) {\n    entry = X509_NAME_get_entry(name, i);\n    object = X509_NAME_ENTRY_get_object(entry);\n    lua_newtable(L);\n    push_asn1_objname(L, object, 1);\n    lua_setfield(L, -2, \"oid\");\n    push_asn1_objname(L, object, 0);\n    lua_setfield(L, -2, \"name\");\n    push_asn1_string(L, X509_NAME_ENTRY_get_data(entry), encode);\n    lua_setfield(L, -2, \"value\");\n    lua_rawseti(L, -2, i+1);\n  }\n  return 1;\n}\n\n/*---------------------------------------------------------------------------*/\n\n/**\n * Retrieve the Subject from the certificate.\n */\nstatic int meth_subject(lua_State* L)\n{\n  p_x509 px = lsec_checkp_x509(L, 1);\n  return push_x509_name(L, X509_get_subject_name(px->cert), px->encode);\n}\n\n/**\n * Retrieve the Issuer from the certificate.\n */\nstatic int meth_issuer(lua_State* L)\n{\n  p_x509 px = lsec_checkp_x509(L, 1);\n  return push_x509_name(L, X509_get_issuer_name(px->cert), px->encode);\n}\n\n/**\n * Retrieve the extensions from the certificate.\n */\nint meth_extensions(lua_State* L)\n{\n  int j;\n  int i = -1;\n  int n_general_names;\n  OTHERNAME *otherName;\n  X509_EXTENSION *extension;\n  GENERAL_NAME *general_name;\n  STACK_OF(GENERAL_NAME) *values;\n  p_x509 px  = lsec_checkp_x509(L, 1);\n  X509 *peer = px->cert;\n\n  /* Return (ret) */\n  lua_newtable(L);\n\n  while ((i = X509_get_ext_by_NID(peer, NID_subject_alt_name, i)) != -1) {\n    extension = X509_get_ext(peer, i);\n    if (extension == NULL)\n      break;\n    values = X509V3_EXT_d2i(extension);\n    if (values == NULL)\n      break;\n\n    /* Push ret[oid] */\n    push_asn1_objname(L, X509_EXTENSION_get_object(extension), 1);\n    push_subtable(L, -2);\n\n    /* Set ret[oid].name = name */\n    push_asn1_objname(L, X509_EXTENSION_get_object(extension), 0);\n    lua_setfield(L, -2, \"name\");\n\n    n_general_names = sk_GENERAL_NAME_num(values);\n    for (j = 0; j < n_general_names; j++) {\n      general_name = sk_GENERAL_NAME_value(values, j);\n      switch (general_name->type) {\n      case GEN_OTHERNAME:\n        otherName = general_name->d.otherName;\n        push_asn1_objname(L, otherName->type_id, 1);\n        if (push_subtable(L, -2)) {\n          push_asn1_objname(L, otherName->type_id, 0);\n          lua_setfield(L, -2, \"name\");\n        }\n        push_asn1_string(L, otherName->value->value.asn1_string, px->encode);\n        lua_rawseti(L, -2, lua_rawlen(L, -2) + 1);\n        lua_pop(L, 1);\n        break;\n      case GEN_DNS:\n        lua_pushstring(L, \"dNSName\");\n        push_subtable(L, -2);\n        push_asn1_string(L, general_name->d.dNSName, px->encode);\n        lua_rawseti(L, -2, lua_rawlen(L, -2) + 1);\n        lua_pop(L, 1);\n        break;\n      case GEN_EMAIL:\n        lua_pushstring(L, \"rfc822Name\");\n        push_subtable(L, -2);\n        push_asn1_string(L, general_name->d.rfc822Name, px->encode);\n        lua_rawseti(L, -2, lua_rawlen(L, -2) + 1);\n        lua_pop(L, 1);\n        break;\n      case GEN_URI:\n        lua_pushstring(L, \"uniformResourceIdentifier\");\n        push_subtable(L, -2);\n        push_asn1_string(L, general_name->d.uniformResourceIdentifier, px->encode);\n        lua_rawseti(L, -2, lua_rawlen(L, -2)+1);\n        lua_pop(L, 1);\n        break;\n      case GEN_IPADD:\n        lua_pushstring(L, \"iPAddress\");\n        push_subtable(L, -2);\n        push_asn1_ip(L, general_name->d.iPAddress);\n        lua_rawseti(L, -2, lua_rawlen(L, -2)+1);\n        lua_pop(L, 1);\n        break;\n      case GEN_X400:\n        /* x400Address   */\n        /* not supported */\n        break;\n      case GEN_DIRNAME:\n        /* directoryName */\n        /* not supported */\n        break;\n      case GEN_EDIPARTY:\n        /* ediPartyName */\n        /* not supported */\n        break;\n      case GEN_RID:\n        /* registeredID  */\n        /* not supported */\n        break;\n      }\n      GENERAL_NAME_free(general_name);\n    }\n    sk_GENERAL_NAME_free(values);\n    lua_pop(L, 1); /* ret[oid] */\n    i++;           /* Next extension */\n  }\n  return 1;\n}\n\n/**\n * Convert the certificate to PEM format.\n */\nstatic int meth_pem(lua_State* L)\n{\n  char* data;\n  long bytes;\n  X509* cert = lsec_checkx509(L, 1);\n  BIO *bio = BIO_new(BIO_s_mem());\n  if (!PEM_write_bio_X509(bio, cert)) {\n    lua_pushnil(L);\n    return 1;\n  }\n  bytes = BIO_get_mem_data(bio, &data);\n  if (bytes > 0)\n    lua_pushlstring(L, data, bytes);\n  else\n    lua_pushnil(L);\n  BIO_free(bio);\n  return 1;\n}\n\n/**\n * Extract public key in PEM format.\n */\nstatic int meth_pubkey(lua_State* L)\n{\n  char* data;\n  long bytes;\n  int ret = 1;\n  X509* cert = lsec_checkx509(L, 1);\n  BIO *bio = BIO_new(BIO_s_mem());\n  EVP_PKEY *pkey = X509_get_pubkey(cert);\n  if(PEM_write_bio_PUBKEY(bio, pkey)) {\n    bytes = BIO_get_mem_data(bio, &data);\n    if (bytes > 0) {\n      lua_pushlstring(L, data, bytes);\n      switch(EVP_PKEY_base_id(pkey)) {\n        case EVP_PKEY_RSA:\n          lua_pushstring(L, \"RSA\");\n          break;\n        case EVP_PKEY_DSA:\n          lua_pushstring(L, \"DSA\");\n          break;\n        case EVP_PKEY_DH:\n          lua_pushstring(L, \"DH\");\n          break;\n        case EVP_PKEY_EC:\n          lua_pushstring(L, \"EC\");\n          break;\n        default:\n          lua_pushstring(L, \"Unknown\");\n          break;\n      }\n      lua_pushinteger(L, EVP_PKEY_bits(pkey));\n      ret = 3;\n    }\n    else\n      lua_pushnil(L);\n  }\n  else\n    lua_pushnil(L);\n  /* Cleanup */\n  BIO_free(bio);\n  EVP_PKEY_free(pkey);\n  return ret;\n}\n\n/**\n * Compute the fingerprint.\n */\nstatic int meth_digest(lua_State* L)\n{\n  unsigned int bytes;\n  const EVP_MD *digest = NULL;\n  unsigned char buffer[EVP_MAX_MD_SIZE];\n  char hex_buffer[EVP_MAX_MD_SIZE*2];\n  X509 *cert = lsec_checkx509(L, 1);\n  const char *str = luaL_optstring(L, 2, NULL);\n  if (!str)\n    digest = EVP_sha1();\n  else {\n    if (!strcmp(str, \"sha1\"))\n      digest = EVP_sha1();\n    else if (!strcmp(str, \"sha256\"))\n      digest = EVP_sha256();\n    else if (!strcmp(str, \"sha512\"))\n      digest = EVP_sha512();\n  }\n  if (!digest) {\n    lua_pushnil(L);\n    lua_pushfstring(L, \"digest algorithm not supported (%s)\", str);\n    return 2;\n  }\n  if (!X509_digest(cert, digest, buffer, &bytes)) {\n    lua_pushnil(L);\n    lua_pushfstring(L, \"error processing the certificate (%s)\",\n      ERR_reason_error_string(ERR_get_error()));\n    return 2;\n  }\n  to_hex((char*)buffer, bytes, hex_buffer);\n  lua_pushlstring(L, hex_buffer, bytes*2);\n  return 1;\n}\n\n/**\n * Check if the certificate is valid in a given time.\n */\nstatic int meth_valid_at(lua_State* L)\n{\n  int nb, na;\n  X509* cert = lsec_checkx509(L, 1);\n  time_t time = luaL_checkinteger(L, 2);\n  nb = X509_cmp_time(X509_get0_notBefore(cert), &time);\n  time -= 1;\n  na = X509_cmp_time(X509_get0_notAfter(cert),  &time);\n  lua_pushboolean(L, nb == -1 && na == 1);\n  return 1;\n}\n\n/**\n * Return the serial number.\n */\nstatic int meth_serial(lua_State *L)\n{\n  char *tmp;\n  BIGNUM *bn;\n  ASN1_INTEGER *serial;\n  X509* cert = lsec_checkx509(L, 1);\n  serial = X509_get_serialNumber(cert);\n  bn = ASN1_INTEGER_to_BN(serial, NULL);\n  tmp = BN_bn2hex(bn);\n  lua_pushstring(L, tmp);\n  BN_free(bn);\n  OPENSSL_free(tmp);\n  return 1;\n}\n\n/**\n * Return not before date.\n */\nstatic int meth_notbefore(lua_State *L)\n{\n  X509* cert = lsec_checkx509(L, 1);\n  return push_asn1_time(L, X509_get0_notBefore(cert));\n}\n\n/**\n * Return not after date.\n */\nstatic int meth_notafter(lua_State *L)\n{\n  X509* cert = lsec_checkx509(L, 1);\n  return push_asn1_time(L, X509_get0_notAfter(cert));\n}\n\n/**\n * Check if this certificate issued some other certificate\n */\nstatic int meth_issued(lua_State* L)\n{\n  int ret, i, len;\n\n  X509_STORE_CTX* ctx = NULL;\n  X509_STORE* root = NULL;\n  STACK_OF(X509)* chain = NULL;\n\n  X509* issuer = lsec_checkx509(L, 1);\n  X509* subject = lsec_checkx509(L, 2);\n  X509* cert = NULL;\n\n  len = lua_gettop(L);\n\n  /* Check that all arguments are certificates */\n\n  for (i = 3; i <= len; i++) {\n    lsec_checkx509(L, i);\n  }\n\n  /* Before allocating things that require freeing afterwards */\n\n  chain = sk_X509_new_null();\n  ctx = X509_STORE_CTX_new();\n  root = X509_STORE_new();\n\n  if (ctx == NULL || root == NULL) {\n    lua_pushnil(L);\n    lua_pushstring(L, \"X509_STORE_new() or X509_STORE_CTX_new() error\");\n    ret = 2;\n    goto cleanup;\n  }\n\n  ret = X509_STORE_add_cert(root, issuer);\n\n  if(!ret) {\n    lua_pushnil(L);\n    lua_pushstring(L, \"X509_STORE_add_cert() error\");\n    ret = 2;\n    goto cleanup;\n  }\n\n  for (i = 3; i <= len && lua_isuserdata(L, i); i++) {\n    cert = lsec_checkx509(L, i);\n    sk_X509_push(chain, cert);\n  }\n\n  ret = X509_STORE_CTX_init(ctx, root, subject, chain);\n\n  if(!ret) {\n    lua_pushnil(L);\n    lua_pushstring(L, \"X509_STORE_CTX_init() error\");\n    ret = 2;\n    goto cleanup;\n  }\n\n  /* Actual verification */\n  if (X509_verify_cert(ctx) <= 0) {\n    ret = X509_STORE_CTX_get_error(ctx);\n    lua_pushnil(L);\n    lua_pushstring(L, X509_verify_cert_error_string(ret));\n    ret = 2;\n  } else {\n    lua_pushboolean(L, 1);\n    ret = 1;\n  }\n\ncleanup:\n\n  if (ctx != NULL) {\n    X509_STORE_CTX_free(ctx);\n  }\n\n  if (chain != NULL) {\n    X509_STORE_free(root);\n  }\n\n  sk_X509_free(chain);\n\n  return ret;\n}\n\n/**\n * Collect X509 objects.\n */\nstatic int meth_destroy(lua_State* L)\n{\n  p_x509 px = lsec_checkp_x509(L, 1);\n  if (px->cert) {\n    X509_free(px->cert);\n    px->cert = NULL;\n  }\n  return 0;\n}\n\nstatic int meth_tostring(lua_State *L)\n{\n  X509* cert = lsec_checkx509(L, 1);\n  lua_pushfstring(L, \"X509 certificate: %p\", cert);\n  return 1;\n}\n\n/**\n * Set the encode for ASN.1 string.\n */\nstatic int meth_set_encode(lua_State* L)\n{\n  int succ = 0;\n  p_x509 px = lsec_checkp_x509(L, 1);\n  const char *enc = luaL_checkstring(L, 2);\n  if (strncmp(enc, \"ai5\", 3) == 0) {\n    succ = 1;\n    px->encode = LSEC_AI5_STRING;\n  } else if (strncmp(enc, \"utf8\", 4) == 0) {\n    succ = 1;\n    px->encode = LSEC_UTF8_STRING;\n  }\n  lua_pushboolean(L, succ);\n  return 1;\n}\n\n/**\n * Get signature name.\n */\nstatic int meth_get_signature_name(lua_State* L)\n{\n  p_x509 px = lsec_checkp_x509(L, 1);\n  int nid = X509_get_signature_nid(px->cert);\n  const char *name = OBJ_nid2sn(nid);\n  if (!name)\n    lua_pushnil(L);\n  else\n    lua_pushstring(L, name);\n  return 1;\n}\n\n/*---------------------------------------------------------------------------*/\n\nstatic int load_cert(lua_State* L)\n{\n  X509 *cert;\n  size_t bytes;\n  const char* data;\n  BIO *bio = BIO_new(BIO_s_mem());\n  data = luaL_checklstring(L, 1, &bytes);\n  BIO_write(bio, data, bytes);\n  cert = PEM_read_bio_X509(bio, NULL, NULL, NULL);\n  if (cert)\n    lsec_pushx509(L, cert);\n  else\n    lua_pushnil(L);\n  BIO_free(bio);\n  return 1;\n}\n\n/*---------------------------------------------------------------------------*/\n\n/**\n * Certificate methods.\n */\nstatic luaL_Reg methods[] = {\n  {\"digest\",     meth_digest},\n  {\"setencode\",  meth_set_encode},\n  {\"extensions\", meth_extensions},\n  {\"getsignaturename\", meth_get_signature_name},\n  {\"issuer\",     meth_issuer},\n  {\"notbefore\",  meth_notbefore},\n  {\"notafter\",   meth_notafter},\n  {\"issued\",     meth_issued},\n  {\"pem\",        meth_pem},\n  {\"pubkey\",     meth_pubkey},\n  {\"serial\",     meth_serial},\n  {\"subject\",    meth_subject},\n  {\"validat\",    meth_valid_at},\n  {NULL,         NULL}\n};\n\n/**\n * X509 metamethods.\n */\nstatic luaL_Reg meta[] = {\n  {\"__close\",    meth_destroy},\n  {\"__gc\",       meth_destroy},\n  {\"__tostring\", meth_tostring},\n  {NULL, NULL}\n};\n\n/**\n * X509 functions.\n */\nstatic luaL_Reg funcs[] = {\n  {\"load\", load_cert},\n  {NULL,   NULL}\n};\n\n/*--------------------------------------------------------------------------*/\n\nLSEC_API int luaopen_ssl_x509(lua_State *L)\n{\n  /* Register the functions and tables */\n  luaL_newmetatable(L, \"SSL:Certificate\");\n  setfuncs(L, meta);\n\n  luaL_newlib(L, methods);\n  lua_setfield(L, -2, \"__index\");\n\n  luaL_newlib(L, funcs);\n  lua_pushvalue(L, -1);\n  lua_setglobal(L, \"x509\");\n\n  return 1;\n}\n"
  },
  {
    "path": "src/luasec/x509.h",
    "content": "/*--------------------------------------------------------------------------\n * LuaSec 1.0.2\n *\n * Copyright (C) 2014-2021 Kim Alvefur, Paul Aurich, Tobias Markmann\n *                         Matthew Wild, Bruno Silvestre.\n *\n *--------------------------------------------------------------------------*/\n\n#ifndef LSEC_X509_H\n#define LSEC_X509_H\n\n#include <openssl/x509v3.h>\n#include \"../lua.h\"\n\n#include \"compat.h\"\n\n/* We do not support UniversalString nor BMPString as ASN.1 String types */\nenum { LSEC_AI5_STRING, LSEC_UTF8_STRING };\n\ntypedef struct t_x509_ {\n  X509 *cert;\n  int  encode;\n} t_x509;\ntypedef t_x509* p_x509;\n\nvoid  lsec_pushx509(lua_State* L, X509* cert);\nX509* lsec_checkx509(lua_State* L, int idx);\n\nLSEC_API int luaopen_ssl_x509(lua_State *L);\n\n#endif\n"
  },
  {
    "path": "src/luasocket/auxiliar.c",
    "content": "/*=========================================================================*\\\n* Auxiliar routines for class hierarchy manipulation\n* LuaSocket toolkit\n\\*=========================================================================*/\n#include \"luasocket.h\"\n#include \"auxiliar.h\"\n#include <string.h>\n#include <stdio.h>\n\n/*-------------------------------------------------------------------------*\\\n* Initializes the module\n\\*-------------------------------------------------------------------------*/\nint auxiliar_open(lua_State *L) {\n    (void) L;\n    return 0;\n}\n\n/*-------------------------------------------------------------------------*\\\n* Creates a new class with given methods\n* Methods whose names start with __ are passed directly to the metatable.\n\\*-------------------------------------------------------------------------*/\nvoid auxiliar_newclass(lua_State *L, const char *classname, luaL_Reg *func) {\n    luaL_newmetatable(L, classname); /* mt */\n    /* create __index table to place methods */\n    lua_pushstring(L, \"__index\");    /* mt,\"__index\" */\n    lua_newtable(L);                 /* mt,\"__index\",it */\n    /* put class name into class metatable */\n    lua_pushstring(L, \"class\");      /* mt,\"__index\",it,\"class\" */\n    lua_pushstring(L, classname);    /* mt,\"__index\",it,\"class\",classname */\n    lua_rawset(L, -3);               /* mt,\"__index\",it */\n    /* pass all methods that start with _ to the metatable, and all others\n     * to the index table */\n    for (; func->name; func++) {     /* mt,\"__index\",it */\n        lua_pushstring(L, func->name);\n        lua_pushcfunction(L, func->func);\n        lua_rawset(L, func->name[0] == '_' ? -5: -3);\n    }\n    lua_rawset(L, -3);               /* mt */\n    lua_pop(L, 1);\n}\n\n/*-------------------------------------------------------------------------*\\\n* Prints the value of a class in a nice way\n\\*-------------------------------------------------------------------------*/\nint auxiliar_tostring(lua_State *L) {\n    char buf[32];\n    if (!lua_getmetatable(L, 1)) goto error;\n    lua_pushstring(L, \"__index\");\n    lua_gettable(L, -2);\n    if (!lua_istable(L, -1)) goto error;\n    lua_pushstring(L, \"class\");\n    lua_gettable(L, -2);\n    if (!lua_isstring(L, -1)) goto error;\n    sprintf(buf, \"%p\", lua_touserdata(L, 1));\n    lua_pushfstring(L, \"%s: %s\", lua_tostring(L, -1), buf);\n    return 1;\nerror:\n    lua_pushstring(L, \"invalid object passed to 'auxiliar.c:__tostring'\");\n    lua_error(L);\n    return 1;\n}\n\n/*-------------------------------------------------------------------------*\\\n* Insert class into group\n\\*-------------------------------------------------------------------------*/\nvoid auxiliar_add2group(lua_State *L, const char *classname, const char *groupname) {\n    luaL_getmetatable(L, classname);\n    lua_pushstring(L, groupname);\n    lua_pushboolean(L, 1);\n    lua_rawset(L, -3);\n    lua_pop(L, 1);\n}\n\n/*-------------------------------------------------------------------------*\\\n* Make sure argument is a boolean\n\\*-------------------------------------------------------------------------*/\nint auxiliar_checkboolean(lua_State *L, int objidx) {\n    if (!lua_isboolean(L, objidx))\n        auxiliar_typeerror(L, objidx, lua_typename(L, LUA_TBOOLEAN));\n    return lua_toboolean(L, objidx);\n}\n\n/*-------------------------------------------------------------------------*\\\n* Return userdata pointer if object belongs to a given class, abort with\n* error otherwise\n\\*-------------------------------------------------------------------------*/\nvoid *auxiliar_checkclass(lua_State *L, const char *classname, int objidx) {\n    void *data = auxiliar_getclassudata(L, classname, objidx);\n    if (!data) {\n        char msg[45];\n        sprintf(msg, \"%.35s expected\", classname);\n        luaL_argerror(L, objidx, msg);\n    }\n    return data;\n}\n\n/*-------------------------------------------------------------------------*\\\n* Return userdata pointer if object belongs to a given group, abort with\n* error otherwise\n\\*-------------------------------------------------------------------------*/\nvoid *auxiliar_checkgroup(lua_State *L, const char *groupname, int objidx) {\n    void *data = auxiliar_getgroupudata(L, groupname, objidx);\n    if (!data) {\n        char msg[45];\n        sprintf(msg, \"%.35s expected\", groupname);\n        luaL_argerror(L, objidx, msg);\n    }\n    return data;\n}\n\n/*-------------------------------------------------------------------------*\\\n* Set object class\n\\*-------------------------------------------------------------------------*/\nvoid auxiliar_setclass(lua_State *L, const char *classname, int objidx) {\n    luaL_getmetatable(L, classname);\n    if (objidx < 0) objidx--;\n    lua_setmetatable(L, objidx);\n}\n\n/*-------------------------------------------------------------------------*\\\n* Get a userdata pointer if object belongs to a given group. Return NULL\n* otherwise\n\\*-------------------------------------------------------------------------*/\nvoid *auxiliar_getgroupudata(lua_State *L, const char *groupname, int objidx) {\n    if (!lua_getmetatable(L, objidx))\n        return NULL;\n    lua_pushstring(L, groupname);\n    lua_rawget(L, -2);\n    if (lua_isnil(L, -1)) {\n        lua_pop(L, 2);\n        return NULL;\n    } else {\n        lua_pop(L, 2);\n        return lua_touserdata(L, objidx);\n    }\n}\n\n/*-------------------------------------------------------------------------*\\\n* Get a userdata pointer if object belongs to a given class. Return NULL\n* otherwise\n\\*-------------------------------------------------------------------------*/\nvoid *auxiliar_getclassudata(lua_State *L, const char *classname, int objidx) {\n    return luaL_testudata(L, objidx, classname);\n}\n\n/*-------------------------------------------------------------------------*\\\n* Throws error when argument does not have correct type.\n* Used to be part of lauxlib in Lua 5.1, was dropped from 5.2.\n\\*-------------------------------------------------------------------------*/\nint auxiliar_typeerror (lua_State *L, int narg, const char *tname) {\n  const char *msg = lua_pushfstring(L, \"%s expected, got %s\", tname,\n      luaL_typename(L, narg));\n  return luaL_argerror(L, narg, msg);\n}\n"
  },
  {
    "path": "src/luasocket/auxiliar.h",
    "content": "#ifndef AUXILIAR_H\n#define AUXILIAR_H\n/*=========================================================================*\\\n* Auxiliar routines for class hierarchy manipulation\n* LuaSocket toolkit (but completely independent of other LuaSocket modules)\n*\n* A LuaSocket class is a name associated with Lua metatables. A LuaSocket\n* group is a name associated with a class. A class can belong to any number\n* of groups. This module provides the functionality to:\n*\n*   - create new classes\n*   - add classes to groups\n*   - set the class of objects\n*   - check if an object belongs to a given class or group\n*   - get the userdata associated to objects\n*   - print objects in a pretty way\n*\n* LuaSocket class names follow the convention <module>{<class>}. Modules\n* can define any number of classes and groups. The module tcp.c, for\n* example, defines the classes tcp{master}, tcp{client} and tcp{server} and\n* the groups tcp{client,server} and tcp{any}. Module functions can then\n* perform type-checking on their arguments by either class or group.\n*\n* LuaSocket metatables define the __index metamethod as being a table. This\n* table has one field for each method supported by the class, and a field\n* \"class\" with the class name.\n*\n* The mapping from class name to the corresponding metatable and the\n* reverse mapping are done using lauxlib.\n\\*=========================================================================*/\n\n#include \"luasocket.h\"\n\n#ifndef _WIN32\n#pragma GCC visibility push(hidden)\n#endif\n\nint auxiliar_open(lua_State *L);\nvoid auxiliar_newclass(lua_State *L, const char *classname, luaL_Reg *func);\nint auxiliar_tostring(lua_State *L);\nvoid auxiliar_add2group(lua_State *L, const char *classname, const char *group);\nint auxiliar_checkboolean(lua_State *L, int objidx);\nvoid *auxiliar_checkclass(lua_State *L, const char *classname, int objidx);\nvoid *auxiliar_checkgroup(lua_State *L, const char *groupname, int objidx);\nvoid auxiliar_setclass(lua_State *L, const char *classname, int objidx);\nvoid *auxiliar_getgroupudata(lua_State *L, const char *groupname, int objidx);\nvoid *auxiliar_getclassudata(lua_State *L, const char *groupname, int objidx);\nint auxiliar_typeerror(lua_State *L, int narg, const char *tname);\n\n#ifndef _WIN32\n#pragma GCC visibility pop\n#endif\n\n#endif /* AUXILIAR_H */\n"
  },
  {
    "path": "src/luasocket/buffer.c",
    "content": "/*=========================================================================*\\\n* Input/Output interface for Lua programs\n* LuaSocket toolkit\n\\*=========================================================================*/\n#include \"luasocket.h\"\n#include \"buffer.h\"\n\n/*=========================================================================*\\\n* Internal function prototypes\n\\*=========================================================================*/\nstatic int recvraw(p_buffer buf, size_t wanted, luaL_Buffer *b);\nstatic int recvline(p_buffer buf, luaL_Buffer *b);\nstatic int recvall(p_buffer buf, luaL_Buffer *b);\nstatic int buffer_get(p_buffer buf, const char **data, size_t *count);\nstatic void buffer_skip(p_buffer buf, size_t count);\nstatic int sendraw(p_buffer buf, const char *data, size_t count, size_t *sent);\n\n/* min and max macros */\n#ifndef MIN\n#define MIN(x, y) ((x) < (y) ? x : y)\n#endif\n#ifndef MAX\n#define MAX(x, y) ((x) > (y) ? x : y)\n#endif\n\n/*=========================================================================*\\\n* Exported functions\n\\*=========================================================================*/\n/*-------------------------------------------------------------------------*\\\n* Initializes module\n\\*-------------------------------------------------------------------------*/\nint buffer_open(lua_State *L) {\n    (void) L;\n    return 0;\n}\n\n/*-------------------------------------------------------------------------*\\\n* Initializes C structure\n\\*-------------------------------------------------------------------------*/\nvoid buffer_init(p_buffer buf, p_io io, p_timeout tm) {\n    buf->first = buf->last = 0;\n    buf->io = io;\n    buf->tm = tm;\n    buf->received = buf->sent = 0;\n    buf->birthday = timeout_gettime();\n}\n\n/*-------------------------------------------------------------------------*\\\n* object:getstats() interface\n\\*-------------------------------------------------------------------------*/\nint buffer_meth_getstats(lua_State *L, p_buffer buf) {\n    lua_pushnumber(L, (lua_Number) buf->received);\n    lua_pushnumber(L, (lua_Number) buf->sent);\n    lua_pushnumber(L, timeout_gettime() - buf->birthday);\n    return 3;\n}\n\n/*-------------------------------------------------------------------------*\\\n* object:setstats() interface\n\\*-------------------------------------------------------------------------*/\nint buffer_meth_setstats(lua_State *L, p_buffer buf) {\n    buf->received = (long) luaL_optnumber(L, 2, (lua_Number) buf->received);\n    buf->sent = (long) luaL_optnumber(L, 3, (lua_Number) buf->sent);\n    if (lua_isnumber(L, 4)) buf->birthday = timeout_gettime() - lua_tonumber(L, 4);\n    lua_pushnumber(L, 1);\n    return 1;\n}\n\n/*-------------------------------------------------------------------------*\\\n* object:send() interface\n\\*-------------------------------------------------------------------------*/\nint buffer_meth_send(lua_State *L, p_buffer buf) {\n    int top = lua_gettop(L);\n    int err = IO_DONE;\n    size_t size = 0, sent = 0;\n    const char *data = luaL_checklstring(L, 2, &size);\n    long start = (long) luaL_optnumber(L, 3, 1);\n    long end = (long) luaL_optnumber(L, 4, -1);\n    timeout_markstart(buf->tm);\n    if (start < 0) start = (long) (size+start+1);\n    if (end < 0) end = (long) (size+end+1);\n    if (start < 1) start = (long) 1;\n    if (end > (long) size) end = (long) size;\n    if (start <= end) err = sendraw(buf, data+start-1, end-start+1, &sent);\n    /* check if there was an error */\n    if (err != IO_DONE) {\n        lua_pushnil(L);\n        lua_pushstring(L, buf->io->error(buf->io->ctx, err));\n        lua_pushnumber(L, (lua_Number) (sent+start-1));\n    } else {\n        lua_pushnumber(L, (lua_Number) (sent+start-1));\n        lua_pushnil(L);\n        lua_pushnil(L);\n    }\n#ifdef LUASOCKET_DEBUG\n    /* push time elapsed during operation as the last return value */\n    lua_pushnumber(L, timeout_gettime() - timeout_getstart(buf->tm));\n#endif\n    return lua_gettop(L) - top;\n}\n\n/*-------------------------------------------------------------------------*\\\n* object:receive() interface\n\\*-------------------------------------------------------------------------*/\nint buffer_meth_receive(lua_State *L, p_buffer buf) {\n    int err = IO_DONE, top = lua_gettop(L);\n    luaL_Buffer b;\n    size_t size;\n    const char *part = luaL_optlstring(L, 3, \"\", &size);\n    timeout_markstart(buf->tm);\n    /* initialize buffer with optional extra prefix\n     * (useful for concatenating previous partial results) */\n    luaL_buffinit(L, &b);\n    luaL_addlstring(&b, part, size);\n    /* receive new patterns */\n    if (!lua_isnumber(L, 2)) {\n        const char *p= luaL_optstring(L, 2, \"*l\");\n        if (p[0] == '*' && p[1] == 'l') err = recvline(buf, &b);\n        else if (p[0] == '*' && p[1] == 'a') err = recvall(buf, &b);\n        else luaL_argcheck(L, 0, 2, \"invalid receive pattern\");\n    /* get a fixed number of bytes (minus what was already partially\n     * received) */\n    } else {\n        double n = lua_tonumber(L, 2);\n        size_t wanted = (size_t) n;\n        luaL_argcheck(L, n >= 0, 2, \"invalid receive pattern\");\n        if (size == 0 || wanted > size)\n            err = recvraw(buf, wanted-size, &b);\n    }\n    /* check if there was an error */\n    if (err != IO_DONE) {\n        /* we can't push anyting in the stack before pushing the\n         * contents of the buffer. this is the reason for the complication */\n        luaL_pushresult(&b);\n        lua_pushstring(L, buf->io->error(buf->io->ctx, err));\n        lua_pushvalue(L, -2);\n        lua_pushnil(L);\n        lua_replace(L, -4);\n    } else {\n        luaL_pushresult(&b);\n        lua_pushnil(L);\n        lua_pushnil(L);\n    }\n#ifdef LUASOCKET_DEBUG\n    /* push time elapsed during operation as the last return value */\n    lua_pushnumber(L, timeout_gettime() - timeout_getstart(buf->tm));\n#endif\n    return lua_gettop(L) - top;\n}\n\n/*-------------------------------------------------------------------------*\\\n* Determines if there is any data in the read buffer\n\\*-------------------------------------------------------------------------*/\nint buffer_isempty(p_buffer buf) {\n    return buf->first >= buf->last;\n}\n\n/*=========================================================================*\\\n* Internal functions\n\\*=========================================================================*/\n/*-------------------------------------------------------------------------*\\\n* Sends a block of data (unbuffered)\n\\*-------------------------------------------------------------------------*/\n#define STEPSIZE 8192\nstatic int sendraw(p_buffer buf, const char *data, size_t count, size_t *sent) {\n    p_io io = buf->io;\n    p_timeout tm = buf->tm;\n    size_t total = 0;\n    int err = IO_DONE;\n    while (total < count && err == IO_DONE) {\n        size_t done = 0;\n        size_t step = (count-total <= STEPSIZE)? count-total: STEPSIZE;\n        err = io->send(io->ctx, data+total, step, &done, tm);\n        total += done;\n    }\n    *sent = total;\n    buf->sent += total;\n    return err;\n}\n\n/*-------------------------------------------------------------------------*\\\n* Reads a fixed number of bytes (buffered)\n\\*-------------------------------------------------------------------------*/\nstatic int recvraw(p_buffer buf, size_t wanted, luaL_Buffer *b) {\n    int err = IO_DONE;\n    size_t total = 0;\n    while (err == IO_DONE) {\n        size_t count; const char *data;\n        err = buffer_get(buf, &data, &count);\n        count = MIN(count, wanted - total);\n        luaL_addlstring(b, data, count);\n        buffer_skip(buf, count);\n        total += count;\n        if (total >= wanted) break;\n    }\n    return err;\n}\n\n/*-------------------------------------------------------------------------*\\\n* Reads everything until the connection is closed (buffered)\n\\*-------------------------------------------------------------------------*/\nstatic int recvall(p_buffer buf, luaL_Buffer *b) {\n    int err = IO_DONE;\n    size_t total = 0;\n    while (err == IO_DONE) {\n        const char *data; size_t count;\n        err = buffer_get(buf, &data, &count);\n        total += count;\n        luaL_addlstring(b, data, count);\n        buffer_skip(buf, count);\n    }\n    if (err == IO_CLOSED) {\n        if (total > 0) return IO_DONE;\n        else return IO_CLOSED;\n    } else return err;\n}\n\n/*-------------------------------------------------------------------------*\\\n* Reads a line terminated by a CR LF pair or just by a LF. The CR and LF\n* are not returned by the function and are discarded from the buffer\n\\*-------------------------------------------------------------------------*/\nstatic int recvline(p_buffer buf, luaL_Buffer *b) {\n    int err = IO_DONE;\n    while (err == IO_DONE) {\n        size_t count, pos; const char *data;\n        err = buffer_get(buf, &data, &count);\n        pos = 0;\n        while (pos < count && data[pos] != '\\n') {\n            /* we ignore all \\r's */\n            if (data[pos] != '\\r') luaL_addchar(b, data[pos]);\n            pos++;\n        }\n        if (pos < count) { /* found '\\n' */\n            buffer_skip(buf, pos+1); /* skip '\\n' too */\n            break; /* we are done */\n        } else /* reached the end of the buffer */\n            buffer_skip(buf, pos);\n    }\n    return err;\n}\n\n/*-------------------------------------------------------------------------*\\\n* Skips a given number of bytes from read buffer. No data is read from the\n* transport layer\n\\*-------------------------------------------------------------------------*/\nstatic void buffer_skip(p_buffer buf, size_t count) {\n    buf->received += count;\n    buf->first += count;\n    if (buffer_isempty(buf))\n        buf->first = buf->last = 0;\n}\n\n/*-------------------------------------------------------------------------*\\\n* Return any data available in buffer, or get more data from transport layer\n* if buffer is empty\n\\*-------------------------------------------------------------------------*/\nstatic int buffer_get(p_buffer buf, const char **data, size_t *count) {\n    int err = IO_DONE;\n    p_io io = buf->io;\n    p_timeout tm = buf->tm;\n    if (buffer_isempty(buf)) {\n        size_t got;\n        err = io->recv(io->ctx, buf->data, BUF_SIZE, &got, tm);\n        buf->first = 0;\n        buf->last = got;\n    }\n    *count = buf->last - buf->first;\n    *data = buf->data + buf->first;\n    return err;\n}\n"
  },
  {
    "path": "src/luasocket/buffer.h",
    "content": "#ifndef BUF_H\n#define BUF_H \n/*=========================================================================*\\\n* Input/Output interface for Lua programs\n* LuaSocket toolkit\n*\n* Line patterns require buffering. Reading one character at a time involves\n* too many system calls and is very slow. This module implements the\n* LuaSocket interface for input/output on connected objects, as seen by \n* Lua programs. \n*\n* Input is buffered. Output is *not* buffered because there was no simple\n* way of making sure the buffered output data would ever be sent.\n*\n* The module is built on top of the I/O abstraction defined in io.h and the\n* timeout management is done with the timeout.h interface.\n\\*=========================================================================*/\n#include \"luasocket.h\"\n#include \"io.h\"\n#include \"timeout.h\"\n\n/* buffer size in bytes */\n#define BUF_SIZE 8192\n\n/* buffer control structure */\ntypedef struct t_buffer_ {\n    double birthday;        /* throttle support info: creation time, */\n    size_t sent, received;  /* bytes sent, and bytes received */\n    p_io io;                /* IO driver used for this buffer */\n    p_timeout tm;           /* timeout management for this buffer */\n    size_t first, last;     /* index of first and last bytes of stored data */\n    char data[BUF_SIZE];    /* storage space for buffer data */\n} t_buffer;\ntypedef t_buffer *p_buffer;\n\n#ifndef _WIN32\n#pragma GCC visibility push(hidden)\n#endif\n\nint buffer_open(lua_State *L);\nvoid buffer_init(p_buffer buf, p_io io, p_timeout tm);\nint buffer_meth_getstats(lua_State *L, p_buffer buf);\nint buffer_meth_setstats(lua_State *L, p_buffer buf);\nint buffer_meth_send(lua_State *L, p_buffer buf);\nint buffer_meth_receive(lua_State *L, p_buffer buf);\nint buffer_isempty(p_buffer buf);\n\n#ifndef _WIN32\n#pragma GCC visibility pop\n#endif\n\n#endif /* BUF_H */\n"
  },
  {
    "path": "src/luasocket/compat.c",
    "content": "#include \"luasocket.h\"\n#include \"compat.h\"\n\n#if LUA_VERSION_NUM==501\n\n/*\n** Adapted from Lua 5.2\n*/\nvoid luasocket_setfuncs (lua_State *L, const luaL_Reg *l, int nup) {\n  luaL_checkstack(L, nup+1, \"too many upvalues\");\n  for (; l->name != NULL; l++) {  /* fill the table with given functions */\n    int i;\n    lua_pushstring(L, l->name);\n    for (i = 0; i < nup; i++)  /* copy upvalues to the top */\n      lua_pushvalue(L, -(nup+1));\n    lua_pushcclosure(L, l->func, nup);  /* closure with those upvalues */\n    lua_settable(L, -(nup + 3));\n  }\n  lua_pop(L, nup);  /* remove upvalues */\n}\n\n/*\n** Duplicated from Lua 5.2\n*/\nvoid *luasocket_testudata (lua_State *L, int ud, const char *tname) {\n  void *p = lua_touserdata(L, ud);\n  if (p != NULL) {  /* value is a userdata? */\n    if (lua_getmetatable(L, ud)) {  /* does it have a metatable? */\n      luaL_getmetatable(L, tname);  /* get correct metatable */\n      if (!lua_rawequal(L, -1, -2))  /* not the same? */\n        p = NULL;  /* value is a userdata with wrong metatable */\n      lua_pop(L, 2);  /* remove both metatables */\n      return p;\n    }\n  }\n  return NULL;  /* value is not a userdata with a metatable */\n}\n\n#endif\n"
  },
  {
    "path": "src/luasocket/compat.h",
    "content": "#ifndef COMPAT_H\n#define COMPAT_H\n\n#ifndef _WIN32\n#pragma GCC visibility push(hidden)\n#endif\n\nvoid luasocket_setfuncs (lua_State *L, const luaL_Reg *l, int nup);\nvoid *luasocket_testudata ( lua_State *L, int arg, const char *tname);\n\n#ifndef _WIN32\n#pragma GCC visibility pop\n#endif\n\n#define luaL_setfuncs luasocket_setfuncs\n#define luaL_testudata luasocket_testudata\n\n#endif\n"
  },
  {
    "path": "src/luasocket/except.c",
    "content": "/*=========================================================================*\\\n* Simple exception support\n* LuaSocket toolkit\n\\*=========================================================================*/\n#include \"luasocket.h\"\n#include \"except.h\"\n#include <stdio.h>\n\n#if LUA_VERSION_NUM < 502\n#define lua_pcallk(L, na, nr, err, ctx, cont) \\\n    (((void)ctx),((void)cont),lua_pcall(L, na, nr, err))\n#endif\n\n#if LUA_VERSION_NUM < 503\ntypedef int lua_KContext;\n#endif\n\n/*=========================================================================*\\\n* Internal function prototypes.\n\\*=========================================================================*/\nstatic int global_protect(lua_State *L);\nstatic int global_newtry(lua_State *L);\nstatic int protected_(lua_State *L);\nstatic int finalize(lua_State *L);\nstatic int do_nothing(lua_State *L);\n\n/* except functions */\nstatic luaL_Reg func[] = {\n    {\"newtry\",    global_newtry},\n    {\"protect\",   global_protect},\n    {NULL,        NULL}\n};\n\n/*-------------------------------------------------------------------------*\\\n* Try factory\n\\*-------------------------------------------------------------------------*/\nstatic void wrap(lua_State *L) {\n    lua_createtable(L, 1, 0);\n    lua_pushvalue(L, -2);\n    lua_rawseti(L, -2, 1);\n    lua_pushvalue(L, lua_upvalueindex(1));\n    lua_setmetatable(L, -2);\n}\n\nstatic int finalize(lua_State *L) {\n    if (!lua_toboolean(L, 1)) {\n        lua_pushvalue(L, lua_upvalueindex(2));\n        lua_call(L, 0, 0);\n        lua_settop(L, 2);\n        wrap(L);\n        lua_error(L);\n        return 0;\n    } else return lua_gettop(L);\n}\n\nstatic int do_nothing(lua_State *L) {\n    (void) L;\n    return 0;\n}\n\nstatic int global_newtry(lua_State *L) {\n    lua_settop(L, 1);\n    if (lua_isnil(L, 1)) lua_pushcfunction(L, do_nothing);\n    lua_pushvalue(L, lua_upvalueindex(1));\n    lua_insert(L, -2);\n    lua_pushcclosure(L, finalize, 2);\n    return 1;\n}\n\n/*-------------------------------------------------------------------------*\\\n* Protect factory\n\\*-------------------------------------------------------------------------*/\nstatic int unwrap(lua_State *L) {\n    if (lua_istable(L, -1) && lua_getmetatable(L, -1)) {\n        int r = lua_rawequal(L, -1, lua_upvalueindex(1));\n        lua_pop(L, 1);\n        if (r) {\n            lua_pushnil(L);\n            lua_rawgeti(L, -2, 1);\n            return 1;\n        }\n    }\n    return 0;\n}\n\nstatic int protected_finish(lua_State *L, int status, lua_KContext ctx) {\n    (void)ctx;\n    if (status != 0 && status != LUA_YIELD) {\n        if (unwrap(L)) return 2;\n        else return lua_error(L);\n    } else return lua_gettop(L);\n}\n\n#if LUA_VERSION_NUM == 502\nstatic int protected_cont(lua_State *L) {\n    int ctx = 0;\n    int status = lua_getctx(L, &ctx);\n    return protected_finish(L, status, ctx);\n}\n#else\n#define protected_cont protected_finish\n#endif\n\nstatic int protected_(lua_State *L) {\n    int status;\n    lua_pushvalue(L, lua_upvalueindex(2));\n    lua_insert(L, 1);\n    status = lua_pcallk(L, lua_gettop(L) - 1, LUA_MULTRET, 0, 0, protected_cont);\n    return protected_finish(L, status, 0);\n}\n\nstatic int global_protect(lua_State *L) {\n    lua_settop(L, 1);\n    lua_pushvalue(L, lua_upvalueindex(1));\n    lua_insert(L, 1);\n    lua_pushcclosure(L, protected_, 2);\n    return 1;\n}\n\n/*-------------------------------------------------------------------------*\\\n* Init module\n\\*-------------------------------------------------------------------------*/\nint except_open(lua_State *L) {\n    lua_newtable(L); /* metatable for wrapped exceptions */\n    lua_pushboolean(L, 0);\n    lua_setfield(L, -2, \"__metatable\");\n    luaL_setfuncs(L, func, 1);\n    return 0;\n}\n"
  },
  {
    "path": "src/luasocket/except.h",
    "content": "#ifndef EXCEPT_H\n#define EXCEPT_H\n/*=========================================================================*\\\n* Exception control\n* LuaSocket toolkit (but completely independent from other modules)\n*\n* This provides support for simple exceptions in Lua. During the\n* development of the HTTP/FTP/SMTP support, it became aparent that\n* error checking was taking a substantial amount of the coding. These\n* function greatly simplify the task of checking errors.\n*\n* The main idea is that functions should return nil as their first return\n* values when they find an error, and return an error message (or value)\n* following nil. In case of success, as long as the first value is not nil,\n* the other values don't matter.\n*\n* The idea is to nest function calls with the \"try\" function. This function\n* checks the first value, and, if it's falsy, wraps the second value in a\n* table with metatable and calls \"error\" on it. Otherwise, it returns all\n* values it received. Basically, it works like the Lua \"assert\" function,\n* but it creates errors targeted specifically at \"protect\".\n*\n* The \"newtry\" function is a factory for \"try\" functions that call a\n* finalizer in protected mode before calling \"error\".\n*\n* The \"protect\" function returns a new function that behaves exactly like\n* the function it receives, but the new function catches exceptions thrown\n* by \"try\" functions and returns nil followed by the error message instead.\n*\n* With these three functions, it's easy to write functions that throw\n* exceptions on error, but that don't interrupt the user script.\n\\*=========================================================================*/\n\n#include \"luasocket.h\"\n\n#ifndef _WIN32\n#pragma GCC visibility push(hidden)\n#endif\n\nint except_open(lua_State *L);\n\n#ifndef _WIN32\n#pragma GCC visibility pop\n#endif\n\n#endif\n"
  },
  {
    "path": "src/luasocket/ftp.lua",
    "content": "-----------------------------------------------------------------------------\n-- FTP support for the Lua language\n-- LuaSocket toolkit.\n-- Author: Diego Nehab\n-----------------------------------------------------------------------------\n\n-----------------------------------------------------------------------------\n-- Declare module and import dependencies\n-----------------------------------------------------------------------------\nlocal tp = require(\"socket.tp\")\nsocket.ftp = {}\nlocal _M = socket.ftp\n-----------------------------------------------------------------------------\n-- Program constants\n-----------------------------------------------------------------------------\n-- timeout in seconds before the program gives up on a connection\n_M.TIMEOUT = 60\n-- default port for ftp service\nlocal PORT = 21\n-- this is the default anonymous password. used when no password is\n-- provided in url. should be changed to your e-mail.\n_M.USER = \"ftp\"\n_M.PASSWORD = \"anonymous@anonymous.org\"\n\n-----------------------------------------------------------------------------\n-- Low level FTP API\n-----------------------------------------------------------------------------\nlocal metat = { __index = {} }\n\nfunction _M.open(server, port, create)\n    local tp = socket.try(tp.connect(server, port or PORT, _M.TIMEOUT, create))\n    local f = setmetatable({ tp = tp }, metat)\n    -- make sure everything gets closed in an exception\n    f.try = socket.newtry(function() f:close() end)\n    return f\nend\n\nfunction metat.__index:portconnect()\n    self.try(self.server:settimeout(_M.TIMEOUT))\n    self.data = self.try(self.server:accept())\n    self.try(self.data:settimeout(_M.TIMEOUT))\nend\n\nfunction metat.__index:pasvconnect()\n    self.data = self.try(socket.tcp())\n    self.try(self.data:settimeout(_M.TIMEOUT))\n    self.try(self.data:connect(self.pasvt.address, self.pasvt.port))\nend\n\nfunction metat.__index:login(user, password)\n    self.try(self.tp:command(\"user\", user or _M.USER))\n    local code, reply = self.try(self.tp:check{\"2..\", 331})\n    if code == 331 then\n        self.try(self.tp:command(\"pass\", password or _M.PASSWORD))\n        self.try(self.tp:check(\"2..\"))\n    end\n    return 1\nend\n\nfunction metat.__index:pasv()\n    self.try(self.tp:command(\"pasv\"))\n    local code, reply = self.try(self.tp:check(\"2..\"))\n    local pattern = \"(%d+)%D(%d+)%D(%d+)%D(%d+)%D(%d+)%D(%d+)\"\n    local a, b, c, d, p1, p2 = socket.skip(2, string.find(reply, pattern))\n    self.try(a and b and c and d and p1 and p2, reply)\n    self.pasvt = {\n        address = string.format(\"%d.%d.%d.%d\", a, b, c, d),\n        port = p1*256 + p2\n    }\n    if self.server then\n        self.server:close()\n        self.server = nil\n    end\n    return self.pasvt.address, self.pasvt.port\nend\n\nfunction metat.__index:epsv()\n    self.try(self.tp:command(\"epsv\"))\n    local code, reply = self.try(self.tp:check(\"229\"))\n    local pattern = \"%((.)(.-)%1(.-)%1(.-)%1%)\"\n    local d, prt, address, port = string.match(reply, pattern)\n    self.try(port, \"invalid epsv response\")\n    self.pasvt = {\n        address = self.tp:getpeername(),\n        port = port\n    }\n    if self.server then\n        self.server:close()\n        self.server = nil\n    end\n    return self.pasvt.address, self.pasvt.port\nend\n\n\nfunction metat.__index:port(address, port)\n    self.pasvt = nil\n    if not address then\n        address, port = self.try(self.tp:getsockname())\n        self.server = self.try(socket.bind(address, 0))\n        address, port = self.try(self.server:getsockname())\n        self.try(self.server:settimeout(_M.TIMEOUT))\n    end\n    local pl = math.mod(port, 256)\n    local ph = (port - pl)/256\n    local arg = string.gsub(string.format(\"%s,%d,%d\", address, ph, pl), \"%.\", \",\")\n    self.try(self.tp:command(\"port\", arg))\n    self.try(self.tp:check(\"2..\"))\n    return 1\nend\n\nfunction metat.__index:eprt(family, address, port)\n    self.pasvt = nil\n    if not address then\n        address, port = self.try(self.tp:getsockname())\n        self.server = self.try(socket.bind(address, 0))\n        address, port = self.try(self.server:getsockname())\n        self.try(self.server:settimeout(_M.TIMEOUT))\n    end\n    local arg = string.format(\"|%s|%s|%d|\", family, address, port)\n    self.try(self.tp:command(\"eprt\", arg))\n    self.try(self.tp:check(\"2..\"))\n    return 1\nend\n\n\nfunction metat.__index:send(sendt)\n    self.try(self.pasvt or self.server, \"need port or pasv first\")\n    -- if there is a pasvt table, we already sent a PASV command\n    -- we just get the data connection into self.data\n    if self.pasvt then self:pasvconnect() end\n    -- get the transfer argument and command\n    local argument = sendt.argument or\n        url.unescape(string.gsub(sendt.path or \"\", \"^[/\\\\]\", \"\"))\n    if argument == \"\" then argument = nil end\n    local command = sendt.command or \"stor\"\n    -- send the transfer command and check the reply\n    self.try(self.tp:command(command, argument))\n    local code, reply = self.try(self.tp:check{\"2..\", \"1..\"})\n    -- if there is not a pasvt table, then there is a server\n    -- and we already sent a PORT command\n    if not self.pasvt then self:portconnect() end\n    -- get the sink, source and step for the transfer\n    local step = sendt.step or ltn12.pump.step\n    local readt = { self.tp }\n    local checkstep = function(src, snk)\n        -- check status in control connection while downloading\n        local readyt = socket.select(readt, nil, 0)\n        if readyt[tp] then code = self.try(self.tp:check(\"2..\")) end\n        return step(src, snk)\n    end\n    local sink = socket.sink(\"close-when-done\", self.data)\n    -- transfer all data and check error\n    self.try(ltn12.pump.all(sendt.source, sink, checkstep))\n    if string.find(code, \"1..\") then self.try(self.tp:check(\"2..\")) end\n    -- done with data connection\n    self.data:close()\n    -- find out how many bytes were sent\n    local sent = socket.skip(1, self.data:getstats())\n    self.data = nil\n    return sent\nend\n\nfunction metat.__index:receive(recvt)\n    self.try(self.pasvt or self.server, \"need port or pasv first\")\n    if self.pasvt then self:pasvconnect() end\n    local argument = recvt.argument or\n        url.unescape(string.gsub(recvt.path or \"\", \"^[/\\\\]\", \"\"))\n    if argument == \"\" then argument = nil end\n    local command = recvt.command or \"retr\"\n    self.try(self.tp:command(command, argument))\n    local code,reply = self.try(self.tp:check{\"1..\", \"2..\"})\n    if (code >= 200) and (code <= 299) then\n        recvt.sink(reply)\n        return 1\n    end\n    if not self.pasvt then self:portconnect() end\n    local source = socket.source(\"until-closed\", self.data)\n    local step = recvt.step or ltn12.pump.step\n    self.try(ltn12.pump.all(source, recvt.sink, step))\n    if string.find(code, \"1..\") then self.try(self.tp:check(\"2..\")) end\n    self.data:close()\n    self.data = nil\n    return 1\nend\n\nfunction metat.__index:cwd(dir)\n    self.try(self.tp:command(\"cwd\", dir))\n    self.try(self.tp:check(250))\n    return 1\nend\n\nfunction metat.__index:type(type)\n    self.try(self.tp:command(\"type\", type))\n    self.try(self.tp:check(200))\n    return 1\nend\n\nfunction metat.__index:greet()\n    local code = self.try(self.tp:check{\"1..\", \"2..\"})\n    if string.find(code, \"1..\") then self.try(self.tp:check(\"2..\")) end\n    return 1\nend\n\nfunction metat.__index:quit()\n    self.try(self.tp:command(\"quit\"))\n    self.try(self.tp:check(\"2..\"))\n    return 1\nend\n\nfunction metat.__index:close()\n    if self.data then self.data:close() end\n    if self.server then self.server:close() end\n    return self.tp:close()\nend\n\n-----------------------------------------------------------------------------\n-- High level FTP API\n-----------------------------------------------------------------------------\nlocal function override(t)\n    if t.url then\n        local u = url.parse(t.url)\n        for i,v in pairs(t) do\n            u[i] = v\n        end\n        return u\n    else return t end\nend\n\nlocal function tput(putt)\n    putt = override(putt)\n    socket.try(putt.host, \"missing hostname\")\n    local f = _M.open(putt.host, putt.port, putt.create)\n    f:greet()\n    f:login(putt.user, putt.password)\n    if putt.type then f:type(putt.type) end\n    f:epsv()\n    local sent = f:send(putt)\n    f:quit()\n    f:close()\n    return sent\nend\n\nlocal default = {\n    path = \"/\",\n    scheme = \"ftp\"\n}\n\nlocal function genericform(u)\n    local t = socket.try(url.parse(u, default))\n    socket.try(t.scheme == \"ftp\", \"wrong scheme '\" .. t.scheme .. \"'\")\n    socket.try(t.host, \"missing hostname\")\n    local pat = \"^type=(.)$\"\n    if t.params then\n        t.type = socket.skip(2, string.find(t.params, pat))\n        socket.try(t.type == \"a\" or t.type == \"i\",\n            \"invalid type '\" .. t.type .. \"'\")\n    end\n    return t\nend\n\n_M.genericform = genericform\n\nlocal function sput(u, body)\n    local putt = genericform(u)\n    putt.source = ltn12.source.string(body)\n    return tput(putt)\nend\n\n_M.put = socket.protect(function(putt, body)\n    if type(putt) == \"string\" then return sput(putt, body)\n    else return tput(putt) end\nend)\n\nlocal function tget(gett)\n    gett = override(gett)\n    socket.try(gett.host, \"missing hostname\")\n    local f = _M.open(gett.host, gett.port, gett.create)\n    f:greet()\n    f:login(gett.user, gett.password)\n    if gett.type then f:type(gett.type) end\n    f:epsv()\n    f:receive(gett)\n    f:quit()\n    return f:close()\nend\n\nlocal function sget(u)\n    local gett = genericform(u)\n    local t = {}\n    gett.sink = ltn12.sink.table(t)\n    tget(gett)\n    return table.concat(t)\nend\n\n_M.command = socket.protect(function(cmdt)\n    cmdt = override(cmdt)\n    socket.try(cmdt.host, \"missing hostname\")\n    socket.try(cmdt.command, \"missing command\")\n    local f = _M.open(cmdt.host, cmdt.port, cmdt.create)\n    f:greet()\n    f:login(cmdt.user, cmdt.password)\n    if type(cmdt.command) == \"table\" then\n        local argument = cmdt.argument or {}\n        local check = cmdt.check or {}\n        for i,cmd in ipairs(cmdt.command) do\n            f.try(f.tp:command(cmd, argument[i]))\n            if check[i] then f.try(f.tp:check(check[i])) end\n        end\n    else\n        f.try(f.tp:command(cmdt.command, cmdt.argument))\n        if cmdt.check then f.try(f.tp:check(cmdt.check)) end\n    end\n    f:quit()\n    return f:close()\nend)\n\n_M.get = socket.protect(function(gett)\n    if type(gett) == \"string\" then return sget(gett)\n    else return tget(gett) end\nend)\n\nreturn _M\n"
  },
  {
    "path": "src/luasocket/headers.lua",
    "content": "-----------------------------------------------------------------------------\n-- Canonic header field capitalization\n-- LuaSocket toolkit.\n-- Author: Diego Nehab\n-----------------------------------------------------------------------------\nsocket.headers = {}\nlocal _M = socket.headers\n\n_M.canonic = {\n    [\"accept\"] = \"Accept\",\n    [\"accept-charset\"] = \"Accept-Charset\",\n    [\"accept-encoding\"] = \"Accept-Encoding\",\n    [\"accept-language\"] = \"Accept-Language\",\n    [\"accept-ranges\"] = \"Accept-Ranges\",\n    [\"action\"] = \"Action\",\n    [\"alternate-recipient\"] = \"Alternate-Recipient\",\n    [\"age\"] = \"Age\",\n    [\"allow\"] = \"Allow\",\n    [\"arrival-date\"] = \"Arrival-Date\",\n    [\"authorization\"] = \"Authorization\",\n    [\"bcc\"] = \"Bcc\",\n    [\"cache-control\"] = \"Cache-Control\",\n    [\"cc\"] = \"Cc\",\n    [\"comments\"] = \"Comments\",\n    [\"connection\"] = \"Connection\",\n    [\"content-description\"] = \"Content-Description\",\n    [\"content-disposition\"] = \"Content-Disposition\",\n    [\"content-encoding\"] = \"Content-Encoding\",\n    [\"content-id\"] = \"Content-ID\",\n    [\"content-language\"] = \"Content-Language\",\n    [\"content-length\"] = \"Content-Length\",\n    [\"content-location\"] = \"Content-Location\",\n    [\"content-md5\"] = \"Content-MD5\",\n    [\"content-range\"] = \"Content-Range\",\n    [\"content-transfer-encoding\"] = \"Content-Transfer-Encoding\",\n    [\"content-type\"] = \"Content-Type\",\n    [\"cookie\"] = \"Cookie\",\n    [\"date\"] = \"Date\",\n    [\"diagnostic-code\"] = \"Diagnostic-Code\",\n    [\"dsn-gateway\"] = \"DSN-Gateway\",\n    [\"etag\"] = \"ETag\",\n    [\"expect\"] = \"Expect\",\n    [\"expires\"] = \"Expires\",\n    [\"final-log-id\"] = \"Final-Log-ID\",\n    [\"final-recipient\"] = \"Final-Recipient\",\n    [\"from\"] = \"From\",\n    [\"host\"] = \"Host\",\n    [\"if-match\"] = \"If-Match\",\n    [\"if-modified-since\"] = \"If-Modified-Since\",\n    [\"if-none-match\"] = \"If-None-Match\",\n    [\"if-range\"] = \"If-Range\",\n    [\"if-unmodified-since\"] = \"If-Unmodified-Since\",\n    [\"in-reply-to\"] = \"In-Reply-To\",\n    [\"keywords\"] = \"Keywords\",\n    [\"last-attempt-date\"] = \"Last-Attempt-Date\",\n    [\"last-modified\"] = \"Last-Modified\",\n    [\"location\"] = \"Location\",\n    [\"max-forwards\"] = \"Max-Forwards\",\n    [\"message-id\"] = \"Message-ID\",\n    [\"mime-version\"] = \"MIME-Version\",\n    [\"original-envelope-id\"] = \"Original-Envelope-ID\",\n    [\"original-recipient\"] = \"Original-Recipient\",\n    [\"pragma\"] = \"Pragma\",\n    [\"proxy-authenticate\"] = \"Proxy-Authenticate\",\n    [\"proxy-authorization\"] = \"Proxy-Authorization\",\n    [\"range\"] = \"Range\",\n    [\"received\"] = \"Received\",\n    [\"received-from-mta\"] = \"Received-From-MTA\",\n    [\"references\"] = \"References\",\n    [\"referer\"] = \"Referer\",\n    [\"remote-mta\"] = \"Remote-MTA\",\n    [\"reply-to\"] = \"Reply-To\",\n    [\"reporting-mta\"] = \"Reporting-MTA\",\n    [\"resent-bcc\"] = \"Resent-Bcc\",\n    [\"resent-cc\"] = \"Resent-Cc\",\n    [\"resent-date\"] = \"Resent-Date\",\n    [\"resent-from\"] = \"Resent-From\",\n    [\"resent-message-id\"] = \"Resent-Message-ID\",\n    [\"resent-reply-to\"] = \"Resent-Reply-To\",\n    [\"resent-sender\"] = \"Resent-Sender\",\n    [\"resent-to\"] = \"Resent-To\",\n    [\"retry-after\"] = \"Retry-After\",\n    [\"return-path\"] = \"Return-Path\",\n    [\"sender\"] = \"Sender\",\n    [\"server\"] = \"Server\",\n    [\"smtp-remote-recipient\"] = \"SMTP-Remote-Recipient\",\n    [\"status\"] = \"Status\",\n    [\"subject\"] = \"Subject\",\n    [\"te\"] = \"TE\",\n    [\"to\"] = \"To\",\n    [\"trailer\"] = \"Trailer\",\n    [\"transfer-encoding\"] = \"Transfer-Encoding\",\n    [\"upgrade\"] = \"Upgrade\",\n    [\"user-agent\"] = \"User-Agent\",\n    [\"vary\"] = \"Vary\",\n    [\"via\"] = \"Via\",\n    [\"warning\"] = \"Warning\",\n    [\"will-retry-until\"] = \"Will-Retry-Until\",\n    [\"www-authenticate\"] = \"WWW-Authenticate\",\n    [\"x-mailer\"] = \"X-Mailer\",\n}\n\nreturn _M\n"
  },
  {
    "path": "src/luasocket/http.lua",
    "content": "-----------------------------------------------------------------------------\n-- HTTP/1.1 client support for the Lua language.\n-- LuaSocket toolkit.\n-- Author: Diego Nehab\n-----------------------------------------------------------------------------\n\n-----------------------------------------------------------------------------\n-- Declare module and import dependencies\n-------------------------------------------------------------------------------\nsocket.http = {}\nlocal _M = socket.http\n\n-----------------------------------------------------------------------------\n-- Program constants\n-----------------------------------------------------------------------------\n-- connection timeout in seconds\n_M.TIMEOUT = 60\n-- user agent field sent in request\n_M.USERAGENT = socket._VERSION\n\n-- supported schemes and their particulars\nlocal SCHEMES = {\n    http = {\n        port = 80\n        , create = function(t)\n            return socket.tcp end }\n    , https = {\n        port = 443\n        , create = function(t)\n            return https.tcp(t) end }}\n\n-- default scheme and port for document retrieval\nlocal SCHEME = 'http'\nlocal PORT = SCHEMES[SCHEME].port\n-----------------------------------------------------------------------------\n-- Reads MIME headers from a connection, unfolding where needed\n-----------------------------------------------------------------------------\nlocal function receiveheaders(sock, headers)\n    local line, name, value, err\n    headers = headers or {}\n    -- get first line\n    line, err = sock:receive()\n    if err then return nil, err end\n    -- headers go until a blank line is found\n    while line ~= \"\" do\n        -- get field-name and value\n        name, value = socket.skip(2, string.find(line, \"^(.-):%s*(.*)\"))\n        if not (name and value) then return nil, \"malformed reponse headers\" end\n        name = string.lower(name)\n        -- get next line (value might be folded)\n        line, err  = sock:receive()\n        if err then return nil, err end\n        -- unfold any folded values\n        while string.find(line, \"^%s\") do\n            value = value .. line\n            line = sock:receive()\n            if err then return nil, err end\n        end\n        -- save pair in table\n        if headers[name] then headers[name] = headers[name] .. \", \" .. value\n        else headers[name] = value end\n    end\n    return headers\nend\n\n-----------------------------------------------------------------------------\n-- Extra sources and sinks\n-----------------------------------------------------------------------------\nsocket.sourcet[\"http-chunked\"] = function(sock, headers)\n    return setmetatable({\n        getfd = function() return sock:getfd() end,\n        dirty = function() return sock:dirty() end\n    }, {\n        __call = function()\n            -- get chunk size, skip extention\n            local line, err = sock:receive()\n            if err then return nil, err end\n            local size = tonumber(string.gsub(line, \";.*\", \"\"), 16)\n            if not size then return nil, \"invalid chunk size\" end\n            -- was it the last chunk?\n            if size > 0 then\n                -- if not, get chunk and skip terminating CRLF\n                local chunk, err, part = sock:receive(size)\n                if chunk then sock:receive() end\n                return chunk, err\n            else\n                -- if it was, read trailers into headers table\n                headers, err = receiveheaders(sock, headers)\n                if not headers then return nil, err end\n            end\n        end\n    })\nend\n\nsocket.sinkt[\"http-chunked\"] = function(sock)\n    return setmetatable({\n        getfd = function() return sock:getfd() end,\n        dirty = function() return sock:dirty() end\n    }, {\n        __call = function(self, chunk, err)\n            if not chunk then return sock:send(\"0\\r\\n\\r\\n\") end\n            local size = string.format(\"%X\\r\\n\", string.len(chunk))\n            return sock:send(size ..  chunk .. \"\\r\\n\")\n        end\n    })\nend\n\n-----------------------------------------------------------------------------\n-- Low level HTTP API\n-----------------------------------------------------------------------------\nlocal metat = { __index = {} }\n\nfunction _M.open(host, port, create)\n    -- create socket with user connect function, or with default\n    local c = socket.try(create())\n    local h = setmetatable({ c = c }, metat)\n    -- create finalized try\n    h.try = socket.newtry(function() h:close() end)\n    -- set timeout before connecting\n    h.try(c:settimeout(_M.TIMEOUT))\n    h.try(c:connect(host, port))\n    -- here everything worked\n    return h\nend\n\nfunction metat.__index:sendrequestline(method, uri)\n    local reqline = string.format(\"%s %s HTTP/1.1\\r\\n\", method or \"GET\", uri)\n    return self.try(self.c:send(reqline))\nend\n\nfunction metat.__index:sendheaders(tosend)\n    local canonic = headers.canonic\n    local h = \"\\r\\n\"\n    for f, v in pairs(tosend) do\n        h = (canonic[f] or f) .. \": \" .. v .. \"\\r\\n\" .. h\n    end\n    self.try(self.c:send(h))\n    return 1\nend\n\nfunction metat.__index:sendbody(headers, source, step)\n    source = source or ltn12.source.empty()\n    step = step or ltn12.pump.step\n    -- if we don't know the size in advance, send chunked and hope for the best\n    local mode = \"http-chunked\"\n    if headers[\"content-length\"] then mode = \"keep-open\" end\n    return self.try(ltn12.pump.all(source, socket.sink(mode, self.c), step))\nend\n\nfunction metat.__index:receivestatusline()\n    local status,ec = self.try(self.c:receive(5))\n    -- identify HTTP/0.9 responses, which do not contain a status line\n    -- this is just a heuristic, but is what the RFC recommends\n    if status ~= \"HTTP/\" then\n        if ec == \"timeout\" then\n            return 408\n        end \n        return nil, status \n    end\n    -- otherwise proceed reading a status line\n    status = self.try(self.c:receive(\"*l\", status))\n    local code = socket.skip(2, string.find(status, \"HTTP/%d*%.%d* (%d%d%d)\"))\n    return self.try(tonumber(code), status)\nend\n\nfunction metat.__index:receiveheaders()\n    return self.try(receiveheaders(self.c))\nend\n\nfunction metat.__index:receivebody(headers, sink, step)\n    sink = sink or ltn12.sink.null()\n    step = step or ltn12.pump.step\n    local length = tonumber(headers[\"content-length\"])\n    local t = headers[\"transfer-encoding\"] -- shortcut\n    local mode = \"default\" -- connection close\n    if t and t ~= \"identity\" then mode = \"http-chunked\"\n    elseif tonumber(headers[\"content-length\"]) then mode = \"by-length\" end\n    return self.try(ltn12.pump.all(socket.source(mode, self.c, length),\n        sink, step))\nend\n\nfunction metat.__index:receive09body(status, sink, step)\n    local source = ltn12.source.rewind(socket.source(\"until-closed\", self.c))\n    source(status)\n    return self.try(ltn12.pump.all(source, sink, step))\nend\n\nfunction metat.__index:close()\n    return self.c:close()\nend\n\n-----------------------------------------------------------------------------\n-- High level HTTP API\n-----------------------------------------------------------------------------\nlocal function adjusturi(reqt)\n    local u = reqt\n    -- if there is a proxy, we need the full url. otherwise, just a part.\n    if not reqt.proxy and not _M.PROXY then\n        u = {\n           path = socket.try(reqt.path, \"invalid path 'nil'\"),\n           params = reqt.params,\n           query = reqt.query,\n           fragment = reqt.fragment\n        }\n    end\n    return url.build(u)\nend\n\nlocal function adjustproxy(reqt)\n    local proxy = reqt.proxy or _M.PROXY\n    if proxy then\n        proxy = url.parse(proxy)\n        return proxy.host, proxy.port or 3128\n    else\n        return reqt.host, reqt.port\n    end\nend\n\nlocal function adjustheaders(reqt)\n    -- default headers\n    local host = reqt.host\n    local port = tostring(reqt.port)\n    if port ~= tostring(SCHEMES[reqt.scheme].port) then\n        host = host .. ':' .. port end\n    local lower = {\n        [\"user-agent\"] = _M.USERAGENT,\n        [\"host\"] = host,\n        [\"connection\"] = \"close, TE\",\n        [\"te\"] = \"trailers\"\n    }\n    -- if we have authentication information, pass it along\n    if reqt.user and reqt.password then\n        lower[\"authorization\"] =\n            \"Basic \" ..  (mime.b64(reqt.user .. \":\" ..\n\t\turl.unescape(reqt.password)))\n    end\n    -- if we have proxy authentication information, pass it along\n    local proxy = reqt.proxy or _M.PROXY\n    if proxy then\n        proxy = url.parse(proxy)\n        if proxy.user and proxy.password then\n            lower[\"proxy-authorization\"] =\n                \"Basic \" ..  (mime.b64(proxy.user .. \":\" .. proxy.password))\n        end\n    end\n    -- override with user headers\n    for i,v in pairs(reqt.headers or lower) do\n        lower[string.lower(i)] = v\n    end\n    return lower\nend\n\n-- default url parts\nlocal default = {\n    path =\"/\"\n    , scheme = \"http\"\n}\n\nlocal function adjustrequest(reqt)\n    -- parse url if provided\n    local nreqt = reqt.url and url.parse(reqt.url, default) or {}\n    -- explicit components override url\n    for i,v in pairs(reqt) do nreqt[i] = v end\n    -- default to scheme particulars\n    local schemedefs, host, port, method\n        = SCHEMES[nreqt.scheme], nreqt.host, nreqt.port, nreqt.method\n    if not nreqt.create then nreqt.create = schemedefs.create(nreqt) end\n    if not (port and port ~= '') then nreqt.port = schemedefs.port end\n    if not (method and method ~= '') then nreqt.method = 'GET' end\n    if not (host and host ~= \"\") then\n        socket.try(nil, \"invalid host '\" .. tostring(nreqt.host) .. \"'\")\n    end\n    -- compute uri if user hasn't overriden\n    nreqt.uri = reqt.uri or adjusturi(nreqt)\n    -- adjust headers in request\n    nreqt.headers = adjustheaders(nreqt)\n    -- ajust host and port if there is a proxy\n    nreqt.host, nreqt.port = adjustproxy(nreqt)\n    return nreqt\nend\n\nlocal function shouldredirect(reqt, code, headers)\n    local location = headers.location\n    if not location then return false end\n    location = string.gsub(location, \"%s\", \"\")\n    if location == \"\" then return false end\n    local scheme = url.parse(location).scheme\n    if scheme and (not SCHEMES[scheme]) then return false end\n    -- avoid https downgrades\n    if ('https' == reqt.scheme) and ('https' ~= scheme) then return false end\n    return (reqt.redirect ~= false) and\n           (code == 301 or code == 302 or code == 303 or code == 307) and\n           (not reqt.method or reqt.method == \"GET\" or reqt.method == \"HEAD\")\n        and ((false == reqt.maxredirects)\n                or ((reqt.nredirects or 0)\n                        < (reqt.maxredirects or 5)))\nend\n\nlocal function shouldreceivebody(reqt, code)\n    if reqt.method == \"HEAD\" then return nil end\n    if code == 204 or code == 304 then return nil end\n    if code >= 100 and code < 200 then return nil end\n    return 1\nend\n\n-- forward declarations\nlocal trequest, tredirect\n\n--[[local]] function tredirect(reqt, location)\n    -- the RFC says the redirect URL has to be absolute, but some\n    -- servers do not respect that\n    local newurl = url.absolute(reqt.url, location)\n    -- if switching schemes, reset port and create function\n    if url.parse(newurl).scheme ~= reqt.scheme then\n        reqt.port = nil\n        reqt.create = nil end\n    -- make new request\n    local result, code, headers, status = trequest {\n        url = newurl,\n        source = reqt.source,\n        sink = reqt.sink,\n        headers = reqt.headers,\n        proxy = reqt.proxy,\n        maxredirects = reqt.maxredirects,\n        nredirects = (reqt.nredirects or 0) + 1,\n        create = reqt.create\n    }\n    -- pass location header back as a hint we redirected\n    headers = headers or {}\n    headers.location = headers.location or location\n    return result, code, headers, status\nend\n\n--[[local]] function trequest(reqt)\n    -- we loop until we get what we want, or\n    -- until we are sure there is no way to get it\n    local nreqt = adjustrequest(reqt)\n    local h = _M.open(nreqt.host, nreqt.port, nreqt.create)\n    -- send request line and headers\n    h:sendrequestline(nreqt.method, nreqt.uri)\n    h:sendheaders(nreqt.headers)\n    -- if there is a body, send it\n    if nreqt.source then\n        h:sendbody(nreqt.headers, nreqt.source, nreqt.step)\n    end\n    local code, status = h:receivestatusline()\n    -- if it is an HTTP/0.9 server, simply get the body and we are done\n    if not code then\n        h:receive09body(status, nreqt.sink, nreqt.step)\n        return 1, 200\n    elseif code == 408 then\n        return 1, code\n    end\n    local headers\n    -- ignore any 100-continue messages\n    while code == 100 do\n        headers = h:receiveheaders()\n        code, status = h:receivestatusline()\n    end\n    headers = h:receiveheaders()\n    -- at this point we should have a honest reply from the server\n    -- we can't redirect if we already used the source, so we report the error\n    if shouldredirect(nreqt, code, headers) and not nreqt.source then\n        h:close()\n        return tredirect(reqt, headers.location)\n    end\n    -- here we are finally done\n    if shouldreceivebody(nreqt, code) then\n        h:receivebody(headers, nreqt.sink, nreqt.step)\n    end\n    h:close()\n    return 1, code, headers, status\nend\n\n-- turns an url and a body into a generic request\nlocal function genericform(u, b)\n    local t = {}\n    local reqt = {\n        url = u,\n        sink = ltn12.sink.table(t),\n        target = t\n    }\n    if b then\n        reqt.source = ltn12.source.string(b)\n        reqt.headers = {\n            [\"content-length\"] = string.len(b),\n            [\"content-type\"] = \"application/x-www-form-urlencoded\"\n        }\n        reqt.method = \"POST\"\n    end\n    return reqt\nend\n\n_M.genericform = genericform\n\nlocal function srequest(u, b)\n    local reqt = genericform(u, b)\n    local _, code, headers, status = trequest(reqt)\n    return table.concat(reqt.target), code, headers, status\nend\n\n_M.request = socket.protect(function(reqt, body)\n    if type(reqt) == \"string\" then return srequest(reqt, body)\n    else return trequest(reqt) end\nend)\n\n_M.schemes = SCHEMES\nreturn _M\n"
  },
  {
    "path": "src/luasocket/inet.c",
    "content": "/*=========================================================================*\\\n* Internet domain functions\n* LuaSocket toolkit\n\\*=========================================================================*/\n#include \"luasocket.h\"\n#include \"inet.h\"\n#include \"../teliva.h\"\n\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n\n/*=========================================================================*\\\n* Internal function prototypes.\n\\*=========================================================================*/\nstatic int inet_global_toip(lua_State *L);\nstatic int inet_global_getaddrinfo(lua_State *L);\nstatic int inet_global_tohostname(lua_State *L);\nstatic int inet_global_getnameinfo(lua_State *L);\nstatic void inet_pushresolved(lua_State *L, struct hostent *hp);\nstatic int inet_global_gethostname(lua_State *L);\n\n/* DNS functions */\nstatic luaL_Reg func[] = {\n    { \"toip\", inet_global_toip},\n    { \"getaddrinfo\", inet_global_getaddrinfo},\n    { \"tohostname\", inet_global_tohostname},\n    { \"getnameinfo\", inet_global_getnameinfo},\n    { \"gethostname\", inet_global_gethostname},\n    { NULL, NULL}\n};\n\n/*-------------------------------------------------------------------------*\\\n* Initializes module\n\\*-------------------------------------------------------------------------*/\nint inet_open(lua_State *L)\n{\n    lua_pushstring(L, \"dns\");\n    lua_newtable(L);\n    luaL_setfuncs(L, func, 0);\n    lua_settable(L, -3);\n    return 0;\n}\n\n/*=========================================================================*\\\n* Global Lua functions\n\\*=========================================================================*/\n/*-------------------------------------------------------------------------*\\\n* Returns all information provided by the resolver given a host name\n* or ip address\n\\*-------------------------------------------------------------------------*/\nstatic int inet_gethost(const char *address, struct hostent **hp) {\n    struct in_addr addr;\n    if (inet_aton(address, &addr))\n        return socket_gethostbyaddr((char *) &addr, sizeof(addr), hp);\n    else\n        return socket_gethostbyname(address, hp);\n}\n\n/*-------------------------------------------------------------------------*\\\n* Returns all information provided by the resolver given a host name\n* or ip address\n\\*-------------------------------------------------------------------------*/\nstatic int inet_global_tohostname(lua_State *L) {\n    const char *address = luaL_checkstring(L, 1);\n    static char buffer[1024] = {0};\n    memset(buffer, '\\0', 1024);\n    snprintf(buffer, 1020, \"socket.tohostname(\\\"%s\\\")\", address);\n    append_to_audit_log(L, buffer);\n    struct hostent *hp = NULL;\n    int err = inet_gethost(address, &hp);\n    if (err != IO_DONE) {\n        lua_pushnil(L);\n        lua_pushstring(L, socket_hoststrerror(err));\n        return 2;\n    }\n    lua_pushstring(L, hp->h_name);\n    inet_pushresolved(L, hp);\n    return 2;\n}\n\nstatic int inet_global_getnameinfo(lua_State *L) {\n    char hbuf[NI_MAXHOST];\n    char sbuf[NI_MAXSERV];\n    int i, ret;\n    struct addrinfo hints;\n    struct addrinfo *resolved, *iter;\n    const char *host = luaL_optstring(L, 1, NULL);\n    const char *serv = luaL_optstring(L, 2, NULL);\n    static char buffer[1024] = {0};\n    memset(buffer, '\\0', 1024);\n    snprintf(buffer, 1020, \"socket.getnameinfo(\\\"%s\\\", \\\"%s\\\")\", host, serv);\n    append_to_audit_log(L, buffer);\n\n    if (!(host || serv))\n        luaL_error(L, \"host and serv cannot be both nil\");\n\n    memset(&hints, 0, sizeof(hints));\n    hints.ai_socktype = SOCK_STREAM;\n    hints.ai_family = AF_UNSPEC;\n\n    ret = getaddrinfo(host, serv, &hints, &resolved);\n    if (ret != 0) {\n        lua_pushnil(L);\n        lua_pushstring(L, socket_gaistrerror(ret));\n        return 2;\n    }\n\n    lua_newtable(L);\n    for (i = 1, iter = resolved; iter; i++, iter = iter->ai_next) {\n        getnameinfo(iter->ai_addr, (socklen_t) iter->ai_addrlen,\n            hbuf, host? (socklen_t) sizeof(hbuf): 0,\n            sbuf, serv? (socklen_t) sizeof(sbuf): 0, 0);\n        if (host) {\n            lua_pushnumber(L, i);\n            lua_pushstring(L, hbuf);\n            lua_settable(L, -3);\n        }\n    }\n    freeaddrinfo(resolved);\n\n    if (serv) {\n        lua_pushstring(L, sbuf);\n        return 2;\n    } else {\n        return 1;\n    }\n}\n\n/*-------------------------------------------------------------------------*\\\n* Returns all information provided by the resolver given a host name\n* or ip address\n\\*-------------------------------------------------------------------------*/\nstatic int inet_global_toip(lua_State *L)\n{\n    const char *address = luaL_checkstring(L, 1);\n    struct hostent *hp = NULL;\n    static char buffer[1024] = {0};\n    memset(buffer, '\\0', 1024);\n    snprintf(buffer, 1020, \"socket.toip(\\\"%s\\\")\", address);\n    append_to_audit_log(L, buffer);\n    int err = inet_gethost(address, &hp);\n    if (err != IO_DONE) {\n        lua_pushnil(L);\n        lua_pushstring(L, socket_hoststrerror(err));\n        return 2;\n    }\n    lua_pushstring(L, inet_ntoa(*((struct in_addr *) hp->h_addr)));\n    inet_pushresolved(L, hp);\n    return 2;\n}\n\nint inet_optfamily(lua_State* L, int narg, const char* def)\n{\n    static const char* optname[] = { \"unspec\", \"inet\", \"inet6\", NULL };\n    static int optvalue[] = { AF_UNSPEC, AF_INET, AF_INET6, 0 };\n\n    return optvalue[luaL_checkoption(L, narg, def, optname)];\n}\n\nint inet_optsocktype(lua_State* L, int narg, const char* def)\n{\n    static const char* optname[] = { \"stream\", \"dgram\", NULL };\n    static int optvalue[] = { SOCK_STREAM, SOCK_DGRAM, 0 };\n\n    return optvalue[luaL_checkoption(L, narg, def, optname)];\n}\n\nstatic int inet_global_getaddrinfo(lua_State *L)\n{\n    const char *hostname = luaL_checkstring(L, 1);\n    struct addrinfo *iterator = NULL, *resolved = NULL;\n    struct addrinfo hints;\n    int i = 1, ret = 0;\n    memset(&hints, 0, sizeof(hints));\n    hints.ai_socktype = SOCK_STREAM;\n    hints.ai_family = AF_UNSPEC;\n    ret = getaddrinfo(hostname, NULL, &hints, &resolved);\n    if (ret != 0) {\n        lua_pushnil(L);\n        lua_pushstring(L, socket_gaistrerror(ret));\n        return 2;\n    }\n    lua_newtable(L);\n    for (iterator = resolved; iterator; iterator = iterator->ai_next) {\n        char hbuf[NI_MAXHOST];\n        ret = getnameinfo(iterator->ai_addr, (socklen_t) iterator->ai_addrlen,\n            hbuf, (socklen_t) sizeof(hbuf), NULL, 0, NI_NUMERICHOST);\n        if (ret){\n          freeaddrinfo(resolved);\n          lua_pushnil(L);\n          lua_pushstring(L, socket_gaistrerror(ret));\n          return 2;\n        }\n        lua_pushnumber(L, i);\n        lua_newtable(L);\n        switch (iterator->ai_family) {\n            case AF_INET:\n                lua_pushliteral(L, \"family\");\n                lua_pushliteral(L, \"inet\");\n                lua_settable(L, -3);\n                break;\n            case AF_INET6:\n                lua_pushliteral(L, \"family\");\n                lua_pushliteral(L, \"inet6\");\n                lua_settable(L, -3);\n                break;\n            case AF_UNSPEC:\n                lua_pushliteral(L, \"family\");\n                lua_pushliteral(L, \"unspec\");\n                lua_settable(L, -3);\n                break;\n            default:\n                lua_pushliteral(L, \"family\");\n                lua_pushliteral(L, \"unknown\");\n                lua_settable(L, -3);\n                break;\n        }\n        lua_pushliteral(L, \"addr\");\n        lua_pushstring(L, hbuf);\n        lua_settable(L, -3);\n        lua_settable(L, -3);\n        i++;\n    }\n    freeaddrinfo(resolved);\n    return 1;\n}\n\n/*-------------------------------------------------------------------------*\\\n* Gets the host name\n\\*-------------------------------------------------------------------------*/\nstatic int inet_global_gethostname(lua_State *L)\n{\n    char name[257];\n    name[256] = '\\0';\n    if (gethostname(name, 256) < 0) {\n        lua_pushnil(L);\n        lua_pushstring(L, socket_strerror(errno));\n        return 2;\n    } else {\n        lua_pushstring(L, name);\n        return 1;\n    }\n}\n\n/*=========================================================================*\\\n* Lua methods\n\\*=========================================================================*/\n/*-------------------------------------------------------------------------*\\\n* Retrieves socket peer name\n\\*-------------------------------------------------------------------------*/\nint inet_meth_getpeername(lua_State *L, p_socket ps, int family)\n{\n    int err;\n    struct sockaddr_storage peer;\n    socklen_t peer_len = sizeof(peer);\n    char name[INET6_ADDRSTRLEN];\n    char port[6]; /* 65535 = 5 bytes + 0 to terminate it */\n    if (getpeername(*ps, (SA *) &peer, &peer_len) < 0) {\n        lua_pushnil(L);\n        lua_pushstring(L, socket_strerror(errno));\n        return 2;\n    }\n    err = getnameinfo((struct sockaddr *) &peer, peer_len,\n        name, INET6_ADDRSTRLEN,\n        port, sizeof(port), NI_NUMERICHOST | NI_NUMERICSERV);\n    if (err) {\n        lua_pushnil(L);\n        lua_pushstring(L, gai_strerror(err));\n        return 2;\n    }\n    lua_pushstring(L, name);\n    lua_pushinteger(L, (int) strtol(port, (char **) NULL, 10));\n    switch (family) {\n        case AF_INET: lua_pushliteral(L, \"inet\"); break;\n        case AF_INET6: lua_pushliteral(L, \"inet6\"); break;\n        case AF_UNSPEC: lua_pushliteral(L, \"unspec\"); break;\n        default: lua_pushliteral(L, \"unknown\"); break;\n    }\n    return 3;\n}\n\n/*-------------------------------------------------------------------------*\\\n* Retrieves socket local name\n\\*-------------------------------------------------------------------------*/\nint inet_meth_getsockname(lua_State *L, p_socket ps, int family)\n{\n    int err;\n    struct sockaddr_storage peer;\n    socklen_t peer_len = sizeof(peer);\n    char name[INET6_ADDRSTRLEN];\n    char port[6]; /* 65535 = 5 bytes + 0 to terminate it */\n    if (getsockname(*ps, (SA *) &peer, &peer_len) < 0) {\n        lua_pushnil(L);\n        lua_pushstring(L, socket_strerror(errno));\n        return 2;\n    }\n    err=getnameinfo((struct sockaddr *)&peer, peer_len,\n        name, INET6_ADDRSTRLEN, port, 6, NI_NUMERICHOST | NI_NUMERICSERV);\n    if (err) {\n        lua_pushnil(L);\n        lua_pushstring(L, gai_strerror(err));\n        return 2;\n    }\n    lua_pushstring(L, name);\n    lua_pushstring(L, port);\n    switch (family) {\n        case AF_INET: lua_pushliteral(L, \"inet\"); break;\n        case AF_INET6: lua_pushliteral(L, \"inet6\"); break;\n        case AF_UNSPEC: lua_pushliteral(L, \"unspec\"); break;\n        default: lua_pushliteral(L, \"unknown\"); break;\n    }\n    return 3;\n}\n\n/*=========================================================================*\\\n* Internal functions\n\\*=========================================================================*/\n/*-------------------------------------------------------------------------*\\\n* Passes all resolver information to Lua as a table\n\\*-------------------------------------------------------------------------*/\nstatic void inet_pushresolved(lua_State *L, struct hostent *hp)\n{\n    char **alias;\n    struct in_addr **addr;\n    int i, resolved;\n    lua_newtable(L); resolved = lua_gettop(L);\n    lua_pushstring(L, \"name\");\n    lua_pushstring(L, hp->h_name);\n    lua_settable(L, resolved);\n    lua_pushstring(L, \"ip\");\n    lua_pushstring(L, \"alias\");\n    i = 1;\n    alias = hp->h_aliases;\n    lua_newtable(L);\n    if (alias) {\n        while (*alias) {\n            lua_pushnumber(L, i);\n            lua_pushstring(L, *alias);\n            lua_settable(L, -3);\n            i++; alias++;\n        }\n    }\n    lua_settable(L, resolved);\n    i = 1;\n    lua_newtable(L);\n    addr = (struct in_addr **) hp->h_addr_list;\n    if (addr) {\n        while (*addr) {\n            lua_pushnumber(L, i);\n            lua_pushstring(L, inet_ntoa(**addr));\n            lua_settable(L, -3);\n            i++; addr++;\n        }\n    }\n    lua_settable(L, resolved);\n}\n\n/*-------------------------------------------------------------------------*\\\n* Tries to create a new inet socket\n\\*-------------------------------------------------------------------------*/\nconst char *inet_trycreate(p_socket ps, int family, int type, int protocol) {\n    const char *err = socket_strerror(socket_create(ps, family, type, protocol));\n    if (err == NULL && family == AF_INET6) {\n        int yes = 1;\n        setsockopt(*ps, IPPROTO_IPV6, IPV6_V6ONLY, (void *)&yes, sizeof(yes));\n    }\n    return err;\n}\n\n/*-------------------------------------------------------------------------*\\\n* \"Disconnects\" a DGRAM socket\n\\*-------------------------------------------------------------------------*/\nconst char *inet_trydisconnect(p_socket ps, int family, p_timeout tm)\n{\n    switch (family) {\n        case AF_INET: {\n            struct sockaddr_in sin;\n            memset((char *) &sin, 0, sizeof(sin));\n            sin.sin_family = AF_UNSPEC;\n            sin.sin_addr.s_addr = INADDR_ANY;\n            return socket_strerror(socket_connect(ps, (SA *) &sin,\n                sizeof(sin), tm));\n        }\n        case AF_INET6: {\n            struct sockaddr_in6 sin6;\n            struct in6_addr addrany = IN6ADDR_ANY_INIT;\n            memset((char *) &sin6, 0, sizeof(sin6));\n            sin6.sin6_family = AF_UNSPEC;\n            sin6.sin6_addr = addrany;\n            return socket_strerror(socket_connect(ps, (SA *) &sin6,\n                sizeof(sin6), tm));\n        }\n    }\n    return NULL;\n}\n\n/*-------------------------------------------------------------------------*\\\n* Tries to connect to remote address (address, port)\n\\*-------------------------------------------------------------------------*/\nconst char *inet_tryconnect(p_socket ps, int *family, const char *address,\n        const char *serv, p_timeout tm, struct addrinfo *connecthints)\n{\n    struct addrinfo *iterator = NULL, *resolved = NULL;\n    const char *err = NULL;\n    int current_family = *family;\n    /* try resolving */\n    err = socket_gaistrerror(getaddrinfo(address, serv,\n                connecthints, &resolved));\n    if (err != NULL) {\n        if (resolved) freeaddrinfo(resolved);\n        return err;\n    }\n    for (iterator = resolved; iterator; iterator = iterator->ai_next) {\n        timeout_markstart(tm);\n        /* create new socket if necessary. if there was no\n         * bind, we need to create one for every new family\n         * that shows up while iterating. if there was a\n         * bind, all families will be the same and we will\n         * not enter this branch. */\n        if (current_family != iterator->ai_family || *ps == SOCKET_INVALID) {\n            socket_destroy(ps);\n            err = inet_trycreate(ps, iterator->ai_family,\n                iterator->ai_socktype, iterator->ai_protocol);\n            if (err) continue;\n            current_family = iterator->ai_family;\n            /* set non-blocking before connect */\n            socket_setnonblocking(ps);\n        }\n        /* try connecting to remote address */\n        err = socket_strerror(socket_connect(ps, (SA *) iterator->ai_addr,\n            (socklen_t) iterator->ai_addrlen, tm));\n        /* if success or timeout is zero, break out of loop */\n        if (err == NULL || timeout_iszero(tm)) {\n            *family = current_family;\n            break;\n        }\n    }\n    freeaddrinfo(resolved);\n    /* here, if err is set, we failed */\n    return err;\n}\n\n/*-------------------------------------------------------------------------*\\\n* Tries to accept a socket\n\\*-------------------------------------------------------------------------*/\nconst char *inet_tryaccept(p_socket server, int family, p_socket client,\n    p_timeout tm) {\n    socklen_t len;\n    t_sockaddr_storage addr;\n    switch (family) {\n        case AF_INET6: len = sizeof(struct sockaddr_in6); break;\n        case AF_INET: len = sizeof(struct sockaddr_in); break;\n        default: len = sizeof(addr); break;\n    }\n    return socket_strerror(socket_accept(server, client, (SA *) &addr,\n        &len, tm));\n}\n\n/*-------------------------------------------------------------------------*\\\n* Tries to bind socket to (address, port)\n\\*-------------------------------------------------------------------------*/\nconst char *inet_trybind(p_socket ps, int *family, const char *address,\n    const char *serv, struct addrinfo *bindhints) {\n    struct addrinfo *iterator = NULL, *resolved = NULL;\n    const char *err = NULL;\n    int current_family = *family;\n    /* translate luasocket special values to C */\n    if (strcmp(address, \"*\") == 0) address = NULL;\n    if (!serv) serv = \"0\";\n    /* try resolving */\n    err = socket_gaistrerror(getaddrinfo(address, serv, bindhints, &resolved));\n    if (err) {\n        if (resolved) freeaddrinfo(resolved);\n        return err;\n    }\n    /* iterate over resolved addresses until one is good */\n    for (iterator = resolved; iterator; iterator = iterator->ai_next) {\n        if (current_family != iterator->ai_family || *ps == SOCKET_INVALID) {\n            socket_destroy(ps);\n            err = inet_trycreate(ps, iterator->ai_family,\n                        iterator->ai_socktype, iterator->ai_protocol);\n            if (err) continue;\n            current_family = iterator->ai_family;\n        }\n        /* try binding to local address */\n        err = socket_strerror(socket_bind(ps, (SA *) iterator->ai_addr,\n            (socklen_t) iterator->ai_addrlen));\n        /* keep trying unless bind succeeded */\n        if (err == NULL) {\n            *family = current_family;\n            /* set to non-blocking after bind */\n            socket_setnonblocking(ps);\n            break;\n        }\n    }\n    /* cleanup and return error */\n    freeaddrinfo(resolved);\n    /* here, if err is set, we failed */\n    return err;\n}\n\n/*-------------------------------------------------------------------------*\\\n* Some systems do not provide these so that we provide our own.\n\\*-------------------------------------------------------------------------*/\n#ifdef LUASOCKET_INET_ATON\nint inet_aton(const char *cp, struct in_addr *inp)\n{\n    unsigned int a = 0, b = 0, c = 0, d = 0;\n    int n = 0, r;\n    unsigned long int addr = 0;\n    r = sscanf(cp, \"%u.%u.%u.%u%n\", &a, &b, &c, &d, &n);\n    if (r == 0 || n == 0) return 0;\n    cp += n;\n    if (*cp) return 0;\n    if (a > 255 || b > 255 || c > 255 || d > 255) return 0;\n    if (inp) {\n        addr += a; addr <<= 8;\n        addr += b; addr <<= 8;\n        addr += c; addr <<= 8;\n        addr += d;\n        inp->s_addr = htonl(addr);\n    }\n    return 1;\n}\n#endif\n\n#ifdef LUASOCKET_INET_PTON\nint inet_pton(int af, const char *src, void *dst)\n{\n    struct addrinfo hints, *res;\n    int ret = 1;\n    memset(&hints, 0, sizeof(struct addrinfo));\n    hints.ai_family = af;\n    hints.ai_flags = AI_NUMERICHOST;\n    if (getaddrinfo(src, NULL, &hints, &res) != 0) return -1;\n    if (af == AF_INET) {\n        struct sockaddr_in *in = (struct sockaddr_in *) res->ai_addr;\n        memcpy(dst, &in->sin_addr, sizeof(in->sin_addr));\n    } else if (af == AF_INET6) {\n        struct sockaddr_in6 *in = (struct sockaddr_in6 *) res->ai_addr;\n        memcpy(dst, &in->sin6_addr, sizeof(in->sin6_addr));\n    } else {\n        ret = -1;\n    }\n    freeaddrinfo(res);\n    return ret;\n}\n\n#endif\n"
  },
  {
    "path": "src/luasocket/inet.h",
    "content": "#ifndef INET_H\n#define INET_H\n/*=========================================================================*\\\n* Internet domain functions\n* LuaSocket toolkit\n*\n* This module implements the creation and connection of internet domain\n* sockets, on top of the socket.h interface, and the interface of with the\n* resolver.\n*\n* The function inet_aton is provided for the platforms where it is not\n* available. The module also implements the interface of the internet\n* getpeername and getsockname functions as seen by Lua programs.\n*\n* The Lua functions toip and tohostname are also implemented here.\n\\*=========================================================================*/\n#include \"luasocket.h\"\n#include \"socket.h\"\n#include \"timeout.h\"\n\n#ifdef _WIN32\n#define LUASOCKET_INET_ATON\n#endif\n\n#ifndef _WIN32\n#pragma GCC visibility push(hidden)\n#endif\n\nint inet_open(lua_State *L);\n\nint inet_optfamily(lua_State* L, int narg, const char* def);\nint inet_optsocktype(lua_State* L, int narg, const char* def);\n\nint inet_meth_getpeername(lua_State *L, p_socket ps, int family);\nint inet_meth_getsockname(lua_State *L, p_socket ps, int family);\n\nconst char *inet_trycreate(p_socket ps, int family, int type, int protocol);\nconst char *inet_trydisconnect(p_socket ps, int family, p_timeout tm);\nconst char *inet_tryconnect(p_socket ps, int *family, const char *address, const char *serv, p_timeout tm, struct addrinfo *connecthints);\nconst char *inet_tryaccept(p_socket server, int family, p_socket client, p_timeout tm);\nconst char *inet_trybind(p_socket ps, int *family, const char *address, const char *serv, struct addrinfo *bindhints);\n\n#ifdef LUASOCKET_INET_ATON\nint inet_aton(const char *cp, struct in_addr *inp);\n#endif\n\n#ifdef LUASOCKET_INET_PTON\nconst char *inet_ntop(int af, const void *src, char *dst, socklen_t cnt);\nint inet_pton(int af, const char *src, void *dst);\n#endif\n\n#ifndef _WIN32\n#pragma GCC visibility pop\n#endif\n\n#endif /* INET_H */\n"
  },
  {
    "path": "src/luasocket/io.c",
    "content": "/*=========================================================================*\\\n* Input/Output abstraction\n* LuaSocket toolkit\n\\*=========================================================================*/\n#include \"luasocket.h\"\n#include \"io.h\"\n\n/*-------------------------------------------------------------------------*\\\n* Initializes C structure\n\\*-------------------------------------------------------------------------*/\nvoid io_init(p_io io, p_send send, p_recv recv, p_error error, void *ctx) {\n    io->send = send;\n    io->recv = recv;\n    io->error = error;\n    io->ctx = ctx;\n}\n\n/*-------------------------------------------------------------------------*\\\n* I/O error strings\n\\*-------------------------------------------------------------------------*/\nconst char *io_strerror(int err) {\n    switch (err) {\n        case IO_DONE: return NULL;\n        case IO_CLOSED: return \"closed\";\n        case IO_TIMEOUT: return \"timeout\";\n        default: return \"unknown error\";\n    }\n}\n"
  },
  {
    "path": "src/luasocket/io.h",
    "content": "#ifndef IO_H\n#define IO_H\n/*=========================================================================*\\\n* Input/Output abstraction\n* LuaSocket toolkit\n*\n* This module defines the interface that LuaSocket expects from the\n* transport layer for streamed input/output. The idea is that if any\n* transport implements this interface, then the buffer.c functions\n* automatically work on it.\n*\n* The module socket.h implements this interface, and thus the module tcp.h\n* is very simple.\n\\*=========================================================================*/\n#include \"luasocket.h\"\n#include \"timeout.h\"\n\n/* IO error codes */\nenum {\n    IO_DONE = 0,        /* operation completed successfully */\n    IO_TIMEOUT = -1,    /* operation timed out */\n    IO_CLOSED = -2,     /* the connection has been closed */\n\tIO_UNKNOWN = -3\n};\n\n/* interface to error message function */\ntypedef const char *(*p_error) (\n    void *ctx,          /* context needed by send */\n    int err             /* error code */\n);\n\n/* interface to send function */\ntypedef int (*p_send) (\n    void *ctx,          /* context needed by send */\n    const char *data,   /* pointer to buffer with data to send */\n    size_t count,       /* number of bytes to send from buffer */\n    size_t *sent,       /* number of bytes sent uppon return */\n    p_timeout tm        /* timeout control */\n);\n\n/* interface to recv function */\ntypedef int (*p_recv) (\n    void *ctx,          /* context needed by recv */\n    char *data,         /* pointer to buffer where data will be writen */\n    size_t count,       /* number of bytes to receive into buffer */\n    size_t *got,        /* number of bytes received uppon return */\n    p_timeout tm        /* timeout control */\n);\n\n/* IO driver definition */\ntypedef struct t_io_ {\n    void *ctx;          /* context needed by send/recv */\n    p_send send;        /* send function pointer */\n    p_recv recv;        /* receive function pointer */\n    p_error error;      /* strerror function */\n} t_io;\ntypedef t_io *p_io;\n\n#ifndef _WIN32\n#pragma GCC visibility push(hidden)\n#endif\n\nvoid io_init(p_io io, p_send send, p_recv recv, p_error error, void *ctx);\nconst char *io_strerror(int err);\n\n#ifndef _WIN32\n#pragma GCC visibility pop\n#endif\n\n#endif /* IO_H */\n"
  },
  {
    "path": "src/luasocket/ltn12.lua",
    "content": "-----------------------------------------------------------------------------\n-- LTN12 - Filters, sources, sinks and pumps.\n-- LuaSocket toolkit.\n-- Author: Diego Nehab\n-----------------------------------------------------------------------------\n\n-----------------------------------------------------------------------------\n-- Declare module\n-----------------------------------------------------------------------------\nlocal _M = {}\nlocal filter,source,sink,pump = {},{},{},{}\n\n_M.filter = filter\n_M.source = source\n_M.sink = sink\n_M.pump = pump\n\n-- 2048 seems to be better in windows...\n_M.BLOCKSIZE = 2048\n_M._VERSION = \"LTN12 1.0.3\"\n\n-----------------------------------------------------------------------------\n-- Filter stuff\n-----------------------------------------------------------------------------\n-- returns a high level filter that cycles a low-level filter\nfunction filter.cycle(low, ctx, extra)\n    assert(low)\n    return function(chunk)\n        local ret\n        ret, ctx = low(ctx, chunk, extra)\n        return ret\n    end\nend\n\n-- chains a bunch of filters together\n-- (thanks to Wim Couwenberg)\nfunction filter.chain(...)\n    local arg = {...}\n    local n = select('#',...)\n    local top, index = 1, 1\n    local retry = \"\"\n    return function(chunk)\n        retry = chunk and retry\n        while true do\n            if index == top then\n                chunk = arg[index](chunk)\n                if chunk == \"\" or top == n then return chunk\n                elseif chunk then index = index + 1\n                else\n                    top = top+1\n                    index = top\n                end\n            else\n                chunk = arg[index](chunk or \"\")\n                if chunk == \"\" then\n                    index = index - 1\n                    chunk = retry\n                elseif chunk then\n                    if index == n then return chunk\n                    else index = index + 1 end\n                else error(\"filter returned inappropriate nil\") end\n            end\n        end\n    end\nend\n\n-----------------------------------------------------------------------------\n-- Source stuff\n-----------------------------------------------------------------------------\n-- create an empty source\nlocal function empty()\n    return nil\nend\n\nfunction source.empty()\n    return empty\nend\n\n-- returns a source that just outputs an error\nfunction source.error(err)\n    return function()\n        return nil, err\n    end\nend\n\n-- creates a file source\nfunction source.file(handle, io_err)\n    if handle then\n        return function()\n            local chunk = handle:read(_M.BLOCKSIZE)\n            if not chunk then handle:close() end\n            return chunk\n        end\n    else return source.error(io_err or \"unable to open file\") end\nend\n\n-- turns a fancy source into a simple source\nfunction source.simplify(src)\n    assert(src)\n    return function()\n        local chunk, err_or_new = src()\n        src = err_or_new or src\n        if not chunk then return nil, err_or_new\n        else return chunk end\n    end\nend\n\n-- creates string source\nfunction source.string(s)\n    if s then\n        local i = 1\n        return function()\n            local chunk = string.sub(s, i, i+_M.BLOCKSIZE-1)\n            i = i + _M.BLOCKSIZE\n            if chunk ~= \"\" then return chunk\n            else return nil end\n        end\n    else return source.empty() end\nend\n\n-- creates table source\nfunction source.table(t)\n    assert('table' == type(t))\n    local i = 0\n    return function()\n        i = i + 1\n        return t[i]\n    end\nend\n\n-- creates rewindable source\nfunction source.rewind(src)\n    assert(src)\n    local t = {}\n    return function(chunk)\n        if not chunk then\n            chunk = table.remove(t)\n            if not chunk then return src()\n            else return chunk end\n        else\n            table.insert(t, chunk)\n        end\n    end\nend\n\n-- chains a source with one or several filter(s)\nfunction source.chain(src, f, ...)\n    if ... then f=filter.chain(f, ...) end\n    assert(src and f)\n    local last_in, last_out = \"\", \"\"\n    local state = \"feeding\"\n    local err\n    return function()\n        if not last_out then\n            error('source is empty!', 2)\n        end\n        while true do\n            if state == \"feeding\" then\n                last_in, err = src()\n                if err then return nil, err end\n                last_out = f(last_in)\n                if not last_out then\n                    if last_in then\n                        error('filter returned inappropriate nil')\n                    else\n                        return nil\n                    end\n                elseif last_out ~= \"\" then\n                    state = \"eating\"\n                    if last_in then last_in = \"\" end\n                    return last_out\n                end\n            else\n                last_out = f(last_in)\n                if last_out == \"\" then\n                    if last_in == \"\" then\n                        state = \"feeding\"\n                    else\n                        error('filter returned \"\"')\n                    end\n                elseif not last_out then\n                    if last_in then\n                        error('filter returned inappropriate nil')\n                    else\n                        return nil\n                    end\n                else\n                    return last_out\n                end\n            end\n        end\n    end\nend\n\n-- creates a source that produces contents of several sources, one after the\n-- other, as if they were concatenated\n-- (thanks to Wim Couwenberg)\nfunction source.cat(...)\n    local arg = {...}\n    local src = table.remove(arg, 1)\n    return function()\n        while src do\n            local chunk, err = src()\n            if chunk then return chunk end\n            if err then return nil, err end\n            src = table.remove(arg, 1)\n        end\n    end\nend\n\n-----------------------------------------------------------------------------\n-- Sink stuff\n-----------------------------------------------------------------------------\n-- creates a sink that stores into a table\nfunction sink.table(t)\n    t = t or {}\n    local f = function(chunk, err)\n        if chunk then table.insert(t, chunk) end\n        return 1\n    end\n    return f, t\nend\n\n-- turns a fancy sink into a simple sink\nfunction sink.simplify(snk)\n    assert(snk)\n    return function(chunk, err)\n        local ret, err_or_new = snk(chunk, err)\n        if not ret then return nil, err_or_new end\n        snk = err_or_new or snk\n        return 1\n    end\nend\n\n-- creates a file sink\nfunction sink.file(handle, io_err)\n    if handle then\n        return function(chunk, err)\n            if not chunk then\n                handle:close()\n                return 1\n            else return handle:write(chunk) end\n        end\n    else return sink.error(io_err or \"unable to open file\") end\nend\n\n-- creates a sink that discards data\nlocal function null()\n    return 1\nend\n\nfunction sink.null()\n    return null\nend\n\n-- creates a sink that just returns an error\nfunction sink.error(err)\n    return function()\n        return nil, err\n    end\nend\n\n-- chains a sink with one or several filter(s)\nfunction sink.chain(f, snk, ...)\n    if ... then\n        local args = { f, snk, ... }\n        snk = table.remove(args, #args)\n        f = filter.chain(unpack(args))\n    end\n    assert(f and snk)\n    return function(chunk, err)\n        if chunk ~= \"\" then\n            local filtered = f(chunk)\n            local done = chunk and \"\"\n            while true do\n                local ret, snkerr = snk(filtered, err)\n                if not ret then return nil, snkerr end\n                if filtered == done then return 1 end\n                filtered = f(done)\n            end\n        else return 1 end\n    end\nend\n\n-----------------------------------------------------------------------------\n-- Pump stuff\n-----------------------------------------------------------------------------\n-- pumps one chunk from the source to the sink\nfunction pump.step(src, snk)\n    local chunk, src_err = src()\n    local ret, snk_err = snk(chunk, src_err)\n    if chunk and ret then return 1\n    else return nil, src_err or snk_err end\nend\n\n-- pumps all data from a source to a sink, using a step function\nfunction pump.all(src, snk, step)\n    assert(src and snk)\n    step = step or pump.step\n    while true do\n        local ret, err = step(src, snk)\n        if not ret then\n            if err then return nil, err\n            else return 1 end\n        end\n    end\nend\n\nreturn _M\n"
  },
  {
    "path": "src/luasocket/luasocket.c",
    "content": "/*=========================================================================*\\\n* LuaSocket toolkit\n* Networking support for the Lua language\n* Diego Nehab\n* 26/11/1999\n*\n* This library is part of an  effort to progressively increase the network\n* connectivity  of  the Lua  language.  The  Lua interface  to  networking\n* functions follows the Sockets API  closely, trying to simplify all tasks\n* involved in setting up both  client and server connections. The provided\n* IO routines, however, follow the Lua  style, being very similar  to the\n* standard Lua read and write functions.\n\\*=========================================================================*/\n\n#include \"luasocket.h\"\n#include \"auxiliar.h\"\n#include \"except.h\"\n#include \"timeout.h\"\n#include \"buffer.h\"\n#include \"inet.h\"\n#include \"tcp.h\"\n#include \"udp.h\"\n#include \"select.h\"\n\n/*-------------------------------------------------------------------------*\\\n* Internal function prototypes\n\\*-------------------------------------------------------------------------*/\nstatic int global_skip(lua_State *L);\nstatic int global_unload(lua_State *L);\nstatic int base_open(lua_State *L);\n\n/*-------------------------------------------------------------------------*\\\n* Modules and functions\n\\*-------------------------------------------------------------------------*/\nstatic const luaL_Reg mod[] = {\n    {\"auxiliar\", auxiliar_open},\n    {\"except\", except_open},\n    {\"timeout\", timeout_open},\n    {\"buffer\", buffer_open},\n    {\"inet\", inet_open},\n    {\"tcp\", tcp_open},\n    {\"udp\", udp_open},\n    {\"select\", select_open},\n    {NULL, NULL}\n};\n\nstatic luaL_Reg func[] = {\n    {\"skip\",      global_skip},\n    {\"__unload\",  global_unload},\n    {NULL,        NULL}\n};\n\n/*-------------------------------------------------------------------------*\\\n* Skip a few arguments\n\\*-------------------------------------------------------------------------*/\nstatic int global_skip(lua_State *L) {\n    int amount = (int) luaL_checkinteger(L, 1);\n    int ret = lua_gettop(L) - amount - 1;\n    return ret >= 0 ? ret : 0;\n}\n\n/*-------------------------------------------------------------------------*\\\n* Unloads the library\n\\*-------------------------------------------------------------------------*/\nstatic int global_unload(lua_State *L) {\n    (void) L;\n    socket_close();\n    return 0;\n}\n\n/*-------------------------------------------------------------------------*\\\n* Setup basic stuff.\n\\*-------------------------------------------------------------------------*/\nstatic int base_open(lua_State *L) {\n    if (socket_open()) {\n        /* export functions (and leave namespace table on top of stack) */\n        lua_newtable(L);\n        luaL_setfuncs(L, func, 0);\n#ifdef LUASOCKET_DEBUG\n        lua_pushstring(L, \"_DEBUG\");\n        lua_pushboolean(L, 1);\n        lua_rawset(L, -3);\n#endif\n        /* make version string available to scripts */\n        lua_pushstring(L, \"_VERSION\");\n        lua_pushstring(L, LUASOCKET_VERSION);\n        lua_rawset(L, -3);\n        return 1;\n    } else {\n        lua_pushstring(L, \"unable to initialize library\");\n        lua_error(L);\n        return 0;\n    }\n}\n\n/*-------------------------------------------------------------------------*\\\n* Initializes all library modules.\n\\*-------------------------------------------------------------------------*/\nLUASOCKET_API int luaopen_socket_core(lua_State *L) {\n    int i;\n    base_open(L);\n    for (i = 0; mod[i].name; i++) mod[i].func(L);\n    lua_setglobal(L, \"socket\");\n    return 1;\n}\n"
  },
  {
    "path": "src/luasocket/luasocket.h",
    "content": "#ifndef LUASOCKET_H\n#define LUASOCKET_H\n/*=========================================================================*\\\n* LuaSocket toolkit\n* Networking support for the Lua language\n* Diego Nehab\n* 9/11/1999\n\\*=========================================================================*/\n\n/*-------------------------------------------------------------------------* \\\n* Current socket library version\n\\*-------------------------------------------------------------------------*/\n#define LUASOCKET_VERSION    \"LuaSocket 3.0-rc1\"\n#define LUASOCKET_COPYRIGHT  \"Copyright (C) 1999-2013 Diego Nehab\"\n\n/*-------------------------------------------------------------------------*\\\n* This macro prefixes all exported API functions\n\\*-------------------------------------------------------------------------*/\n#ifndef LUASOCKET_API\n#ifdef _WIN32\n#define LUASOCKET_API __declspec(dllexport)\n#else\n#define LUASOCKET_API __attribute__ ((visibility (\"default\")))\n#endif\n#endif\n\n#include \"../lua.h\"\n#include \"../lauxlib.h\"\n#include \"compat.h\"\n\n/*-------------------------------------------------------------------------*\\\n* Initializes the library.\n\\*-------------------------------------------------------------------------*/\nLUASOCKET_API int luaopen_socket_core(lua_State *L);\n\n#endif /* LUASOCKET_H */\n"
  },
  {
    "path": "src/luasocket/makefile",
    "content": "# luasocket src/makefile\n#\n# Definitions in this section can be overriden on the command line or in the\n# environment.\n#\n# These are equivalent:\n#\n#    export PLAT=linux DEBUG=DEBUG\n#    make\n#\n# and\n#\n#   make PLAT=linux DEBUG=DEBUG\n\n# PLAT: bsd linux macosx win32 win64 mingw\n# platform to build for\nPLAT?=linux\n\n# LUAV: 5.1 5.2\n# lua version to build against\nLUAV?=5.1\n\n# MYCFLAGS: to be set by user if needed\nMYCFLAGS?=\n\n# MYLDFLAGS: to be set by user if needed\nMYLDFLAGS?=\n\n# DEBUG: NODEBUG DEBUG\n# debug mode causes luasocket to collect and return timing information useful\n# for testing and debugging luasocket itself\nDEBUG?=NODEBUG\n\n# DESTDIR: (no default)\n# used by package managers to install into a temporary destination\nDESTDIR?=\n\n#------\n# Definitions below can be overridden on the make command line, but\n# shouldn't have to be.\n\n\nprint:\n\t@echo PLAT=$(PLAT)\n\t@echo DEBUG=$(DEBUG)\n\t@echo CFLAGS=$(CFLAGS)\n\t@echo LDFLAGS=$(LDFLAGS)\n\n#------\n# Supported platforms\n#\nPLATS= macosx linux win32 win64 mingw solaris\n\n#------\n# Compiler and linker settings\n# for Mac OS X\nSO_macosx=so\nO_macosx=o\nCC_macosx=gcc\nDEF_macosx= -DLUASOCKET_$(DEBUG) -DUNIX_HAS_SUN_LEN\nCFLAGS_macosx=-g -O2 -Wall -fno-common $(DEF)\nLDFLAGS_macosx= -bundle -undefined dynamic_lookup -o \nLD_macosx=gcc\nSOCKET_macosx=usocket.o\n\n#------\n# Compiler and linker settings\n# for Linux\nSO_linux=so\nO_linux=o\nCC_linux=gcc\nDEF_linux=-DLUASOCKET_$(DEBUG)\nCFLAGS_linux= -g -O2 -Wall -Wpedantic -Wextra $(DEF)\nLDFLAGS_linux= -o\nLD_linux=ld\nSOCKET_linux=usocket.o\n\n#------\n# Compiler and linker settings\n# for BSD\nSO_bsd=so\nO_bsd=o\nCC_bsd=gcc\nDEF_bsd=-DLUASOCKET_$(DEBUG) -DUNIX_HAS_SUN_LEN\nCFLAGS_bsd=-g -O2 -Wall -Wextra -Wimplicit $(DEF)\nLDFLAGS_bsd=-O -shared -fpic -o\nLD_bsd=gcc\nSOCKET_bsd=usocket.o\n\n#------\n# Compiler and linker settings\n# for Solaris\nSO_solaris=so\nO_solaris=o\nCC_solaris=gcc\nDEF_solaris=-DLUASOCKET_$(DEBUG)\nCFLAGS_solaris=-g -O2 -Wall -Wextra -Wimplicit $(DEF)\nLDFLAGS_solaris=-lnsl -lsocket -lresolv -O -shared -fpic -o \nLD_solaris=gcc\nSOCKET_solaris=usocket.o\n\n#------\n# Compiler and linker settings\n# for MingW\nSO_mingw=dll\nO_mingw=o\nCC_mingw=gcc\nDEF_mingw= -DLUASOCKET_$(DEBUG) -DWINVER=0x0501\nCFLAGS_mingw= -O2 -Wall -fno-common $(DEF)\nLDFLAGS_mingw= -shared -Wl,-s -lws2_32 -o \nLD_mingw=gcc\nSOCKET_mingw=wsocket.o\n\n\n#------\n# Compiler and linker settings\n# for Win32\nSO_win32=dll\nO_win32=obj\nCC_win32=cl\nDEF_win32= //D \"WIN32\" //D \"NDEBUG\" //D \"_WINDOWS\" //D \"_USRDLL\" \\\n     //D \"_CRT_SECURE_NO_WARNINGS\" \\\n     //D \"_WINDLL\"  \\\n     //D \"LUASOCKET_$(DEBUG)\"\nCFLAGS_win32=$(DEF) //O2 //Ot //MD //W3 //nologo\nLDFLAGS_win32= //nologo //link //NOLOGO //DLL //INCREMENTAL:NO \\\n    //MANIFEST //MANIFESTFILE:\"intermediate.manifest\" \\\n    /MANIFESTUAC:\"level='asInvoker' uiAccess='false'\" \\\n    //SUBSYSTEM:WINDOWS //OPT:REF //OPT:ICF //DYNAMICBASE:NO \\\n    //MACHINE:X86 \\\n    ws2_32.lib //OUT:\n\nLD_win32=cl\nSOCKET_win32=wsocket.obj\n\n#------\n# Compiler and linker settings\n# for Win64\nSO_win64=dll\nO_win64=obj\nCC_win64=cl\nDEF_win64= //D \"WIN32\" //D \"NDEBUG\" //D \"_WINDOWS\" //D \"_USRDLL\" \\\n     //D \"_CRT_SECURE_NO_WARNINGS\" \\\n     //D \"_WINDLL\"  \\\n     //D \"LUASOCKET_$(DEBUG)\"\nCFLAGS_win64=$(DEF) //O2 //Ot //MD //W3 //nologo\nLDFLAGS_win64= //nologo //link //NOLOGO //DLL //INCREMENTAL:NO \\\n    //MANIFEST //MANIFESTFILE:\"intermediate.manifest\" \\\n    /MANIFESTUAC:\"level='asInvoker' uiAccess='false'\" \\\n    //SUBSYSTEM:WINDOWS //OPT:REF //OPT:ICF //DYNAMICBASE:NO \\\n    ws2_32.lib //OUT:\n\nLD_win64=cl\nSOCKET_win64=wsocket.obj\n\n.SUFFIXES: .obj\n\n.c.obj:\n\t$(CC) $(CFLAGS) //Fo\"$@\" //c $<\n\n#------\n# Output file names\n#\nSO=$(SO_$(PLAT))\nO=$(O_$(PLAT))\nA=a\nSOCKET_A=socket.$(A)\nMIME_A=mime.$(A)\nUNIX_A=unix.$(A)\nSERIAL_A=serial.$(A)\nSOCKET=$(SOCKET_$(PLAT))\n\n#------\n# Settings selected for platform\n#\nCC=$(CC_$(PLAT))\nDEF=$(DEF_$(PLAT))\nAR= ar rc\nRANLIB= ranlib\nCFLAGS=$(CFLAGS_$(PLAT)) $(MYCFLAGS)\nLDFLAGS=$(LDFLAGS_$(PLAT)) $(MYLDFLAGS)\nLD=$(LD_$(PLAT))\n\n#------\n# Modules belonging to socket-core\n#\nSOCKET_OBJS= \\\n\tluasocket.$(O) \\\n\ttimeout.$(O) \\\n\tbuffer.$(O) \\\n\tio.$(O) \\\n\tauxiliar.$(O) \\\n\tcompat.$(O) \\\n\toptions.$(O) \\\n\tinet.$(O) \\\n\t$(SOCKET) \\\n\texcept.$(O) \\\n\tselect.$(O) \\\n\ttcp.$(O) \\\n\tudp.$(O)\n\n#------\n# Modules belonging mime-core\n#\nMIME_OBJS= \\\n\tmime.$(O) \\\n\tcompat.$(O)\n\n#------\n# Modules belonging unix (local domain sockets)\n#\nUNIX_OBJS=\\\n\tbuffer.$(O) \\\n\tauxiliar.$(O) \\\n\toptions.$(O) \\\n\ttimeout.$(O) \\\n\tio.$(O) \\\n\tusocket.$(O) \\\n\tunixstream.$(O) \\\n\tunixdgram.$(O) \\\n\tcompat.$(O) \\\n\tunix.$(O)\n\n#------\n# Modules belonging to serial (device streams)\n#\nSERIAL_OBJS=\\\n\tbuffer.$(O) \\\n\tcompat.$(O) \\\n\tauxiliar.$(O) \\\n\toptions.$(O) \\\n\ttimeout.$(O) \\\n\tio.$(O) \\\n\tusocket.$(O) \\\n\tserial.$(O)\n\n#------\n# Files to install\n#\nTO_SOCKET_LDIR= \\\n\thttp.lua \\\n\turl.lua \\\n\ttp.lua \\\n\tftp.lua \\\n\theaders.lua \\\n\tsmtp.lua\n\nTO_TOP_LDIR= \\\n\tltn12.lua \\\n\tsocket.lua \\\n\tmime.lua\n\n#------\n# Targets\n#\ndefault: $(PLAT)\n\n\nbsd:\n\t$(MAKE) all-unix PLAT=bsd\n\nmacosx:\n\t$(MAKE) all-unix PLAT=macosx\n\nwin32:\n\t$(MAKE) all PLAT=win32\n\nwin64:\n\t$(MAKE) all PLAT=win64\n\nlinux:\n\t$(MAKE) all-unix PLAT=linux\n\nmingw:\n\t$(MAKE) all PLAT=mingw\n\nsolaris:\n\t$(MAKE) all-unix PLAT=solaris\n\nnone:\n\t@echo \"Please run\"\n\t@echo \"   make PLATFORM\"\n\t@echo \"where PLATFORM is one of these:\"\n\t@echo \"   $(PLATS)\"\n\nall: $(SOCKET_A) $(MIME_A)\n\n$(SOCKET_A): $(SOCKET_OBJS)\n\t$(AR) $@ $(SOCKET_OBJS)\n\t$(RANLIB) $@\n\n$(MIME_A): $(MIME_OBJS)\n\t$(AR) $@ $(MIME_OBJS)\n\t$(RANLIB) $@\n\nall-unix: all $(UNIX_SO) $(SERIAL_SO)\n\n$(UNIX_A): $(UNIX_OBJS)\n\t$(AR) $@ $(UNIX_OBJS)\n\t$(RANLIB) $@\n\n$(SERIAL_A): $(SERIAL_OBJS)\n\t$(AR) $@ $(SERIAL_OBJS)\n\t$(RANLIB) $@\n\nclean:\n\trm -f $(SOCKET_A) $(SOCKET_OBJS) $(SERIAL_OBJS)\n\trm -f $(MIME_A) $(UNIX_A) $(SERIAL_A) $(MIME_OBJS) $(UNIX_OBJS)\n\n.PHONY: all $(PLATS) default clean echo none\n\n#------\n# List of dependencies\n#\ncompat.$(O): compat.c compat.h\nauxiliar.$(O): auxiliar.c auxiliar.h\nbuffer.$(O): buffer.c buffer.h io.h timeout.h\nexcept.$(O): except.c except.h\ninet.$(O): inet.c inet.h socket.h io.h timeout.h usocket.h\nio.$(O): io.c io.h timeout.h\nluasocket.$(O): luasocket.c luasocket.h auxiliar.h except.h \\\n\ttimeout.h buffer.h io.h inet.h socket.h usocket.h tcp.h \\\n\tudp.h select.h\nmime.$(O): mime.c mime.h\noptions.$(O): options.c auxiliar.h options.h socket.h io.h \\\n\ttimeout.h usocket.h inet.h\nselect.$(O): select.c socket.h io.h timeout.h usocket.h select.h\nserial.$(O): serial.c auxiliar.h socket.h io.h timeout.h usocket.h \\\n  options.h unix.h buffer.h\ntcp.$(O): tcp.c auxiliar.h socket.h io.h timeout.h usocket.h \\\n\tinet.h options.h tcp.h buffer.h\ntimeout.$(O): timeout.c auxiliar.h timeout.h\nudp.$(O): udp.c auxiliar.h socket.h io.h timeout.h usocket.h \\\n\tinet.h options.h udp.h\nunix.$(O): unix.c auxiliar.h socket.h io.h timeout.h usocket.h \\\n\toptions.h unix.h buffer.h\nusocket.$(O): usocket.c socket.h io.h timeout.h usocket.h\nwsocket.$(O): wsocket.c socket.h io.h timeout.h usocket.h\n"
  },
  {
    "path": "src/luasocket/mbox.lua",
    "content": "local _M = {}\n\nif module then\n    mbox = _M\nend \n\nfunction _M.split_message(message_s)\n    local message = {}\n    message_s = string.gsub(message_s, \"\\r\\n\", \"\\n\")\n    string.gsub(message_s, \"^(.-\\n)\\n\", function (h) message.headers = h end)\n    string.gsub(message_s, \"^.-\\n\\n(.*)\", function (b) message.body = b end)\n    if not message.body then\n        string.gsub(message_s, \"^\\n(.*)\", function (b) message.body = b end)\n    end\n    if not message.headers and not message.body then\n        message.headers = message_s\n    end\n    return message.headers or \"\", message.body or \"\"\nend\n\nfunction _M.split_headers(headers_s)\n    local headers = {}\n    headers_s = string.gsub(headers_s, \"\\r\\n\", \"\\n\")\n    headers_s = string.gsub(headers_s, \"\\n[ ]+\", \" \")\n    string.gsub(\"\\n\" .. headers_s, \"\\n([^\\n]+)\", function (h) table.insert(headers, h) end)\n    return headers\nend\n\nfunction _M.parse_header(header_s)\n    header_s = string.gsub(header_s, \"\\n[ ]+\", \" \")\n    header_s = string.gsub(header_s, \"\\n+\", \"\")\n    local _, __, name, value = string.find(header_s, \"([^%s:]-):%s*(.*)\")\n    return name, value\nend\n\nfunction _M.parse_headers(headers_s)\n    local headers_t = _M.split_headers(headers_s)\n    local headers = {}\n    for i = 1, #headers_t do\n        local name, value = _M.parse_header(headers_t[i])\n        if name then\n            name = string.lower(name)\n            if headers[name] then\n                headers[name] = headers[name] .. \", \" .. value\n            else headers[name] = value end\n        end\n    end\n    return headers\nend\n\nfunction _M.parse_from(from)\n    local _, __, name, address = string.find(from, \"^%s*(.-)%s*%<(.-)%>\")\n    if not address then\n        _, __, address = string.find(from, \"%s*(.+)%s*\")\n    end\n    name = name or \"\"\n    address = address or \"\"\n    if name == \"\" then name = address end\n    name = string.gsub(name, '\"', \"\")\n    return name, address\nend\n\nfunction _M.split_mbox(mbox_s)\n    local mbox = {}\n    mbox_s = string.gsub(mbox_s, \"\\r\\n\", \"\\n\") ..\"\\n\\nFrom \\n\"\n    local nj, i, j = 1, 1, 1\n    while 1 do\n        i, nj = string.find(mbox_s, \"\\n\\nFrom .-\\n\", j)\n        if not i then break end\n        local message = string.sub(mbox_s, j, i-1)\n        table.insert(mbox, message)\n        j = nj+1\n    end\n    return mbox\nend\n\nfunction _M.parse(mbox_s)\n    local mbox = _M.split_mbox(mbox_s)\n    for i = 1, #mbox do\n        mbox[i] = _M.parse_message(mbox[i])\n    end\n    return mbox\nend\n\nfunction _M.parse_message(message_s)\n    local message = {}\n    message.headers, message.body = _M.split_message(message_s)\n    message.headers = _M.parse_headers(message.headers)\n    return message\nend\n\nreturn _M\n"
  },
  {
    "path": "src/luasocket/mime.c",
    "content": "/*=========================================================================*\\\n* MIME support functions\n* LuaSocket toolkit\n\\*=========================================================================*/\n#include \"luasocket.h\"\n#include \"mime.h\"\n#include <string.h>\n#include <ctype.h>\n\n/*=========================================================================*\\\n* Don't want to trust escape character constants\n\\*=========================================================================*/\ntypedef unsigned char UC;\nstatic const char CRLF[] = \"\\r\\n\";\nstatic const char EQCRLF[] = \"=\\r\\n\";\n\n/*=========================================================================*\\\n* Internal function prototypes.\n\\*=========================================================================*/\nstatic int mime_global_wrp(lua_State *L);\nstatic int mime_global_b64(lua_State *L);\nstatic int mime_global_unb64(lua_State *L);\nstatic int mime_global_qp(lua_State *L);\nstatic int mime_global_unqp(lua_State *L);\nstatic int mime_global_qpwrp(lua_State *L);\nstatic int mime_global_eol(lua_State *L);\nstatic int mime_global_dot(lua_State *L);\n\nstatic size_t dot(int c, size_t state, luaL_Buffer *buffer);\n//static void b64setup(UC *base);\nstatic size_t b64encode(UC c, UC *input, size_t size, luaL_Buffer *buffer);\nstatic size_t b64pad(const UC *input, size_t size, luaL_Buffer *buffer);\nstatic size_t b64decode(UC c, UC *input, size_t size, luaL_Buffer *buffer);\n\n//static void qpsetup(UC *class, UC *unbase);\nstatic void qpquote(UC c, luaL_Buffer *buffer);\nstatic size_t qpdecode(UC c, UC *input, size_t size, luaL_Buffer *buffer);\nstatic size_t qpencode(UC c, UC *input, size_t size,\n        const char *marker, luaL_Buffer *buffer);\nstatic size_t qppad(UC *input, size_t size, luaL_Buffer *buffer);\n\n/* code support functions */\nstatic luaL_Reg func[] = {\n    { \"dot\", mime_global_dot },\n    { \"b64\", mime_global_b64 },\n    { \"eol\", mime_global_eol },\n    { \"qp\", mime_global_qp },\n    { \"qpwrp\", mime_global_qpwrp },\n    { \"unb64\", mime_global_unb64 },\n    { \"unqp\", mime_global_unqp },\n    { \"wrp\", mime_global_wrp },\n    { NULL, NULL }\n};\n\n/*-------------------------------------------------------------------------*\\\n* Quoted-printable globals\n\\*-------------------------------------------------------------------------*/\nenum {QP_PLAIN, QP_QUOTED, QP_CR, QP_IF_LAST};\n\nstatic const UC qpclass[] = {\n    QP_QUOTED, QP_QUOTED, QP_QUOTED, QP_QUOTED, QP_QUOTED, QP_QUOTED,\n    QP_QUOTED, QP_QUOTED, QP_QUOTED, QP_IF_LAST, QP_QUOTED, QP_QUOTED,\n    QP_QUOTED, QP_CR, QP_QUOTED, QP_QUOTED, QP_QUOTED, QP_QUOTED,\n    QP_QUOTED, QP_QUOTED, QP_QUOTED, QP_QUOTED, QP_QUOTED, QP_QUOTED,\n    QP_QUOTED, QP_QUOTED, QP_QUOTED, QP_QUOTED, QP_QUOTED, QP_QUOTED,\n    QP_QUOTED, QP_QUOTED, QP_IF_LAST, QP_PLAIN, QP_PLAIN, QP_PLAIN,\n    QP_PLAIN, QP_PLAIN, QP_PLAIN, QP_PLAIN, QP_PLAIN, QP_PLAIN,\n    QP_PLAIN, QP_PLAIN, QP_PLAIN, QP_PLAIN, QP_PLAIN, QP_PLAIN,\n    QP_PLAIN, QP_PLAIN, QP_PLAIN, QP_PLAIN, QP_PLAIN, QP_PLAIN,\n    QP_PLAIN, QP_PLAIN, QP_PLAIN, QP_PLAIN, QP_PLAIN, QP_PLAIN,\n    QP_PLAIN, QP_QUOTED, QP_PLAIN, QP_PLAIN, QP_PLAIN, QP_PLAIN,\n    QP_PLAIN, QP_PLAIN, QP_PLAIN, QP_PLAIN, QP_PLAIN, QP_PLAIN,\n    QP_PLAIN, QP_PLAIN, QP_PLAIN, QP_PLAIN, QP_PLAIN, QP_PLAIN,\n    QP_PLAIN, QP_PLAIN, QP_PLAIN, QP_PLAIN, QP_PLAIN, QP_PLAIN,\n    QP_PLAIN, QP_PLAIN, QP_PLAIN, QP_PLAIN, QP_PLAIN, QP_PLAIN,\n    QP_PLAIN, QP_PLAIN, QP_PLAIN, QP_PLAIN, QP_PLAIN, QP_PLAIN,\n    QP_PLAIN, QP_PLAIN, QP_PLAIN, QP_PLAIN, QP_PLAIN, QP_PLAIN,\n    QP_PLAIN, QP_PLAIN, QP_PLAIN, QP_PLAIN, QP_PLAIN, QP_PLAIN,\n    QP_PLAIN, QP_PLAIN, QP_PLAIN, QP_PLAIN, QP_PLAIN, QP_PLAIN,\n    QP_PLAIN, QP_PLAIN, QP_PLAIN, QP_PLAIN, QP_PLAIN, QP_PLAIN,\n    QP_PLAIN, QP_PLAIN, QP_PLAIN, QP_PLAIN, QP_PLAIN, QP_PLAIN,\n    QP_PLAIN, QP_QUOTED, QP_QUOTED, QP_QUOTED, QP_QUOTED, QP_QUOTED,\n    QP_QUOTED, QP_QUOTED, QP_QUOTED, QP_QUOTED, QP_QUOTED, QP_QUOTED,\n    QP_QUOTED, QP_QUOTED, QP_QUOTED, QP_QUOTED, QP_QUOTED, QP_QUOTED,\n    QP_QUOTED, QP_QUOTED, QP_QUOTED, QP_QUOTED, QP_QUOTED, QP_QUOTED,\n    QP_QUOTED, QP_QUOTED, QP_QUOTED, QP_QUOTED, QP_QUOTED, QP_QUOTED,\n    QP_QUOTED, QP_QUOTED, QP_QUOTED, QP_QUOTED, QP_QUOTED, QP_QUOTED,\n    QP_QUOTED, QP_QUOTED, QP_QUOTED, QP_QUOTED, QP_QUOTED, QP_QUOTED,\n    QP_QUOTED, QP_QUOTED, QP_QUOTED, QP_QUOTED, QP_QUOTED, QP_QUOTED,\n    QP_QUOTED, QP_QUOTED, QP_QUOTED, QP_QUOTED, QP_QUOTED, QP_QUOTED,\n    QP_QUOTED, QP_QUOTED, QP_QUOTED, QP_QUOTED, QP_QUOTED, QP_QUOTED,\n    QP_QUOTED, QP_QUOTED, QP_QUOTED, QP_QUOTED, QP_QUOTED, QP_QUOTED,\n    QP_QUOTED, QP_QUOTED, QP_QUOTED, QP_QUOTED, QP_QUOTED, QP_QUOTED,\n    QP_QUOTED, QP_QUOTED, QP_QUOTED, QP_QUOTED, QP_QUOTED, QP_QUOTED,\n    QP_QUOTED, QP_QUOTED, QP_QUOTED, QP_QUOTED, QP_QUOTED, QP_QUOTED,\n    QP_QUOTED, QP_QUOTED, QP_QUOTED, QP_QUOTED, QP_QUOTED, QP_QUOTED,\n    QP_QUOTED, QP_QUOTED, QP_QUOTED, QP_QUOTED, QP_QUOTED, QP_QUOTED,\n    QP_QUOTED, QP_QUOTED, QP_QUOTED, QP_QUOTED, QP_QUOTED, QP_QUOTED,\n    QP_QUOTED, QP_QUOTED, QP_QUOTED, QP_QUOTED, QP_QUOTED, QP_QUOTED,\n    QP_QUOTED, QP_QUOTED, QP_QUOTED, QP_QUOTED, QP_QUOTED, QP_QUOTED,\n    QP_QUOTED, QP_QUOTED, QP_QUOTED, QP_QUOTED, QP_QUOTED, QP_QUOTED,\n    QP_QUOTED, QP_QUOTED, QP_QUOTED, QP_QUOTED, QP_QUOTED, QP_QUOTED,\n    QP_QUOTED, QP_QUOTED, QP_QUOTED, QP_QUOTED\n};\n\nstatic const UC qpbase[] = \"0123456789ABCDEF\";\n\nstatic const UC qpunbase[] = {\n    255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,\n    255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,\n    255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,\n    255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,\n    255, 255, 255, 255, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 255,\n    255, 255, 255, 255, 255, 255, 10, 11, 12, 13, 14, 15,\n    255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,\n    255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,\n    255, 255, 255, 255, 10, 11, 12, 13, 14, 15, 255, 255,\n    255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,\n    255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,\n    255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,\n    255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,\n    255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,\n    255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,\n    255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,\n    255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,\n    255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,\n    255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,\n    255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,\n    255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,\n    255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,\n    255, 255, 255, 255, 255, 255, 255, 255\n};\n\n/*-------------------------------------------------------------------------*\\\n* Base64 globals\n\\*-------------------------------------------------------------------------*/\nstatic const UC b64base[] =\n        \"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/\";\n\nstatic const UC b64unbase[] = {\n    255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,\n    255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,\n    255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,\n    255, 255, 255, 255, 255, 255, 255, 62, 255, 255, 255, 63,\n    52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 255, 255, 255, 0,\n    255, 255, 255, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13,\n    14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 255, 255,\n    255, 255, 255, 255, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35,\n    36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50,\n    51, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,\n    255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,\n    255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,\n    255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,\n    255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,\n    255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,\n    255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,\n    255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,\n    255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,\n    255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,\n    255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,\n    255, 255\n};\n\n/*=========================================================================*\\\n* Exported functions\n\\*=========================================================================*/\n/*-------------------------------------------------------------------------*\\\n* Initializes module\n\\*-------------------------------------------------------------------------*/\nLUASOCKET_API int luaopen_mime_core(lua_State *L)\n{\n    lua_newtable(L);\n    luaL_setfuncs(L, func, 0);\n    lua_pushvalue(L, -1);\n    lua_setglobal(L, \"mime\");\n    /* make version string available to scripts */\n    lua_pushstring(L, \"_VERSION\");\n    lua_pushstring(L, MIME_VERSION);\n    lua_rawset(L, -3);\n    /* initialize lookup tables */\n    // qpsetup(qpclass, qpunbase);\n    // b64setup(b64unbase);\n    return 1;\n}\n\n/*=========================================================================*\\\n* Global Lua functions\n\\*=========================================================================*/\n/*-------------------------------------------------------------------------*\\\n* Incrementaly breaks a string into lines. The string can have CRLF breaks.\n* A, n = wrp(l, B, length)\n* A is a copy of B, broken into lines of at most 'length' bytes.\n* 'l' is how many bytes are left for the first line of B.\n* 'n' is the number of bytes left in the last line of A.\n\\*-------------------------------------------------------------------------*/\nstatic int mime_global_wrp(lua_State *L)\n{\n    size_t size = 0;\n    int left = (int) luaL_checknumber(L, 1);\n    const UC *input = (const UC *) luaL_optlstring(L, 2, NULL, &size);\n    const UC *last = input + size;\n    int length = (int) luaL_optnumber(L, 3, 76);\n    luaL_Buffer buffer;\n    /* end of input black-hole */\n    if (!input) {\n        /* if last line has not been terminated, add a line break */\n        if (left < length) lua_pushstring(L, CRLF);\n        /* otherwise, we are done */\n        else lua_pushnil(L);\n        lua_pushnumber(L, length);\n        return 2;\n    }\n    luaL_buffinit(L, &buffer);\n    while (input < last) {\n        switch (*input) {\n            case '\\r':\n                break;\n            case '\\n':\n                luaL_addstring(&buffer, CRLF);\n                left = length;\n                break;\n            default:\n                if (left <= 0) {\n                    left = length;\n                    luaL_addstring(&buffer, CRLF);\n                }\n                luaL_addchar(&buffer, *input);\n                left--;\n                break;\n        }\n        input++;\n    }\n    luaL_pushresult(&buffer);\n    lua_pushnumber(L, left);\n    return 2;\n}\n\n#if 0\n/*-------------------------------------------------------------------------*\\\n* Fill base64 decode map.\n\\*-------------------------------------------------------------------------*/\nstatic void b64setup(UC *unbase)\n{\n    int i;\n    for (i = 0; i <= 255; i++) unbase[i] = (UC) 255;\n    for (i = 0; i < 64; i++) unbase[b64base[i]] = (UC) i;\n    unbase['='] = 0;\n\n    printf(\"static const UC b64unbase[] = {\\n\");\n    for (int i = 0; i < 256; i++) {\n        printf(\"%d, \", unbase[i]);\n    }\n    printf(\"\\n}\\n;\");\n}\n#endif\n\n/*-------------------------------------------------------------------------*\\\n* Acumulates bytes in input buffer until 3 bytes are available.\n* Translate the 3 bytes into Base64 form and append to buffer.\n* Returns new number of bytes in buffer.\n\\*-------------------------------------------------------------------------*/\nstatic size_t b64encode(UC c, UC *input, size_t size,\n        luaL_Buffer *buffer)\n{\n    input[size++] = c;\n    if (size == 3) {\n        UC code[4];\n        unsigned long value = 0;\n        value += input[0]; value <<= 8;\n        value += input[1]; value <<= 8;\n        value += input[2];\n        code[3] = b64base[value & 0x3f]; value >>= 6;\n        code[2] = b64base[value & 0x3f]; value >>= 6;\n        code[1] = b64base[value & 0x3f]; value >>= 6;\n        code[0] = b64base[value];\n        luaL_addlstring(buffer, (char *) code, 4);\n        size = 0;\n    }\n    return size;\n}\n\n/*-------------------------------------------------------------------------*\\\n* Encodes the Base64 last 1 or 2 bytes and adds padding '='\n* Result, if any, is appended to buffer.\n* Returns 0.\n\\*-------------------------------------------------------------------------*/\nstatic size_t b64pad(const UC *input, size_t size,\n        luaL_Buffer *buffer)\n{\n    unsigned long value = 0;\n    UC code[4] = {'=', '=', '=', '='};\n    switch (size) {\n        case 1:\n            value = input[0] << 4;\n            code[1] = b64base[value & 0x3f]; value >>= 6;\n            code[0] = b64base[value];\n            luaL_addlstring(buffer, (char *) code, 4);\n            break;\n        case 2:\n            value = input[0]; value <<= 8;\n            value |= input[1]; value <<= 2;\n            code[2] = b64base[value & 0x3f]; value >>= 6;\n            code[1] = b64base[value & 0x3f]; value >>= 6;\n            code[0] = b64base[value];\n            luaL_addlstring(buffer, (char *) code, 4);\n            break;\n        default:\n            break;\n    }\n    return 0;\n}\n\n/*-------------------------------------------------------------------------*\\\n* Acumulates bytes in input buffer until 4 bytes are available.\n* Translate the 4 bytes from Base64 form and append to buffer.\n* Returns new number of bytes in buffer.\n\\*-------------------------------------------------------------------------*/\nstatic size_t b64decode(UC c, UC *input, size_t size,\n        luaL_Buffer *buffer)\n{\n    /* ignore invalid characters */\n    if (b64unbase[c] > 64) return size;\n    input[size++] = c;\n    /* decode atom */\n    if (size == 4) {\n        UC decoded[3];\n        int valid, value = 0;\n        value =  b64unbase[input[0]]; value <<= 6;\n        value |= b64unbase[input[1]]; value <<= 6;\n        value |= b64unbase[input[2]]; value <<= 6;\n        value |= b64unbase[input[3]];\n        decoded[2] = (UC) (value & 0xff); value >>= 8;\n        decoded[1] = (UC) (value & 0xff); value >>= 8;\n        decoded[0] = (UC) value;\n        /* take care of paddding */\n        valid = (input[2] == '=') ? 1 : (input[3] == '=') ? 2 : 3;\n        luaL_addlstring(buffer, (char *) decoded, valid);\n        return 0;\n    /* need more data */\n    } else return size;\n}\n\n/*-------------------------------------------------------------------------*\\\n* Incrementally applies the Base64 transfer content encoding to a string\n* A, B = b64(C, D)\n* A is the encoded version of the largest prefix of C .. D that is\n* divisible by 3. B has the remaining bytes of C .. D, *without* encoding.\n* The easiest thing would be to concatenate the two strings and\n* encode the result, but we can't afford that or Lua would dupplicate\n* every chunk we received.\n\\*-------------------------------------------------------------------------*/\nstatic int mime_global_b64(lua_State *L)\n{\n    UC atom[3];\n    size_t isize = 0, asize = 0;\n    const UC *input = (const UC *) luaL_optlstring(L, 1, NULL, &isize);\n    const UC *last = input + isize;\n    luaL_Buffer buffer;\n    /* end-of-input blackhole */\n    if (!input) {\n        lua_pushnil(L);\n        lua_pushnil(L);\n        return 2;\n    }\n    /* make sure we don't confuse buffer stuff with arguments */\n    lua_settop(L, 2);\n    /* process first part of the input */\n    luaL_buffinit(L, &buffer);\n    while (input < last)\n        asize = b64encode(*input++, atom, asize, &buffer);\n    input = (const UC *) luaL_optlstring(L, 2, NULL, &isize);\n    /* if second part is nil, we are done */\n    if (!input) {\n        size_t osize = 0;\n        asize = b64pad(atom, asize, &buffer);\n        luaL_pushresult(&buffer);\n        /* if the output is empty  and the input is nil, return nil */\n        lua_tolstring(L, -1, &osize);\n        if (osize == 0) lua_pushnil(L);\n        lua_pushnil(L);\n        return 2;\n    }\n    /* otherwise process the second part */\n    last = input + isize;\n    while (input < last)\n        asize = b64encode(*input++, atom, asize, &buffer);\n    luaL_pushresult(&buffer);\n    lua_pushlstring(L, (char *) atom, asize);\n    return 2;\n}\n\n/*-------------------------------------------------------------------------*\\\n* Incrementally removes the Base64 transfer content encoding from a string\n* A, B = b64(C, D)\n* A is the encoded version of the largest prefix of C .. D that is\n* divisible by 4. B has the remaining bytes of C .. D, *without* encoding.\n\\*-------------------------------------------------------------------------*/\nstatic int mime_global_unb64(lua_State *L)\n{\n    UC atom[4];\n    size_t isize = 0, asize = 0;\n    const UC *input = (const UC *) luaL_optlstring(L, 1, NULL, &isize);\n    const UC *last = input + isize;\n    luaL_Buffer buffer;\n    /* end-of-input blackhole */\n    if (!input) {\n        lua_pushnil(L);\n        lua_pushnil(L);\n        return 2;\n    }\n    /* make sure we don't confuse buffer stuff with arguments */\n    lua_settop(L, 2);\n    /* process first part of the input */\n    luaL_buffinit(L, &buffer);\n    while (input < last)\n        asize = b64decode(*input++, atom, asize, &buffer);\n    input = (const UC *) luaL_optlstring(L, 2, NULL, &isize);\n    /* if second is nil, we are done */\n    if (!input) {\n        size_t osize = 0;\n        luaL_pushresult(&buffer);\n        /* if the output is empty  and the input is nil, return nil */\n        lua_tolstring(L, -1, &osize);\n        if (osize == 0) lua_pushnil(L);\n        lua_pushnil(L);\n        return 2;\n    }\n    /* otherwise, process the rest of the input */\n    last = input + isize;\n    while (input < last)\n        asize = b64decode(*input++, atom, asize, &buffer);\n    luaL_pushresult(&buffer);\n    lua_pushlstring(L, (char *) atom, asize);\n    return 2;\n}\n\n/*-------------------------------------------------------------------------*\\\n* Quoted-printable encoding scheme\n* all (except CRLF in text) can be =XX\n* CLRL in not text must be =XX=XX\n* 33 through 60 inclusive can be plain\n* 62 through 126 inclusive can be plain\n* 9 and 32 can be plain, unless in the end of a line, where must be =XX\n* encoded lines must be no longer than 76 not counting CRLF\n* soft line-break are =CRLF\n* To encode one byte, we need to see the next two.\n* Worst case is when we see a space, and wonder if a CRLF is comming\n\\*-------------------------------------------------------------------------*/\n#if 0\n/*-------------------------------------------------------------------------*\\\n* Split quoted-printable characters into classes\n* Precompute reverse map for encoding\n\\*-------------------------------------------------------------------------*/\nstatic void qpsetup(UC *cl, UC *unbase)\n{\n\n    int i;\n    for (i = 0; i < 256; i++) cl[i] = QP_QUOTED;\n    for (i = 33; i <= 60; i++) cl[i] = QP_PLAIN;\n    for (i = 62; i <= 126; i++) cl[i] = QP_PLAIN;\n    cl['\\t'] = QP_IF_LAST;\n    cl[' '] = QP_IF_LAST;\n    cl['\\r'] = QP_CR;\n    for (i = 0; i < 256; i++) unbase[i] = 255;\n    unbase['0'] = 0; unbase['1'] = 1; unbase['2'] = 2;\n    unbase['3'] = 3; unbase['4'] = 4; unbase['5'] = 5;\n    unbase['6'] = 6; unbase['7'] = 7; unbase['8'] = 8;\n    unbase['9'] = 9; unbase['A'] = 10; unbase['a'] = 10;\n    unbase['B'] = 11; unbase['b'] = 11; unbase['C'] = 12;\n    unbase['c'] = 12; unbase['D'] = 13; unbase['d'] = 13;\n    unbase['E'] = 14; unbase['e'] = 14; unbase['F'] = 15;\n    unbase['f'] = 15;\n\nprintf(\"static UC qpclass[] = {\");\n    for (int i = 0; i < 256; i++) {\n        if (i % 6 == 0) {\n            printf(\"\\n    \");\n        }\n        switch(cl[i]) {\n            case QP_QUOTED:\n                printf(\"QP_QUOTED, \");\n                break;\n            case QP_PLAIN:\n                printf(\"QP_PLAIN, \");\n                break;\n            case QP_CR:\n                printf(\"QP_CR, \");\n                break;\n            case QP_IF_LAST:\n                printf(\"QP_IF_LAST, \");\n                break;\n        }\n    }\nprintf(\"\\n};\\n\");\n\nprintf(\"static const UC qpunbase[] = {\");\n    for (int i = 0; i < 256; i++) {\n        int c = qpunbase[i];\n        printf(\"%d, \", c);\n    }\nprintf(\"\\\";\\n\");\n}\n#endif\n\n/*-------------------------------------------------------------------------*\\\n* Output one character in form =XX\n\\*-------------------------------------------------------------------------*/\nstatic void qpquote(UC c, luaL_Buffer *buffer)\n{\n    luaL_addchar(buffer, '=');\n    luaL_addchar(buffer, qpbase[c >> 4]);\n    luaL_addchar(buffer, qpbase[c & 0x0F]);\n}\n\n/*-------------------------------------------------------------------------*\\\n* Accumulate characters until we are sure about how to deal with them.\n* Once we are sure, output to the buffer, in the correct form.\n\\*-------------------------------------------------------------------------*/\nstatic size_t qpencode(UC c, UC *input, size_t size,\n        const char *marker, luaL_Buffer *buffer)\n{\n    input[size++] = c;\n    /* deal with all characters we can have */\n    while (size > 0) {\n        switch (qpclass[input[0]]) {\n            /* might be the CR of a CRLF sequence */\n            case QP_CR:\n                if (size < 2) return size;\n                if (input[1] == '\\n') {\n                    luaL_addstring(buffer, marker);\n                    return 0;\n                } else qpquote(input[0], buffer);\n                break;\n            /* might be a space and that has to be quoted if last in line */\n            case QP_IF_LAST:\n                if (size < 3) return size;\n                /* if it is the last, quote it and we are done */\n                if (input[1] == '\\r' && input[2] == '\\n') {\n                    qpquote(input[0], buffer);\n                    luaL_addstring(buffer, marker);\n                    return 0;\n                } else luaL_addchar(buffer, input[0]);\n                break;\n                /* might have to be quoted always */\n            case QP_QUOTED:\n                qpquote(input[0], buffer);\n                break;\n                /* might never have to be quoted */\n            default:\n                luaL_addchar(buffer, input[0]);\n                break;\n        }\n        input[0] = input[1]; input[1] = input[2];\n        size--;\n    }\n    return 0;\n}\n\n/*-------------------------------------------------------------------------*\\\n* Deal with the final characters\n\\*-------------------------------------------------------------------------*/\nstatic size_t qppad(UC *input, size_t size, luaL_Buffer *buffer)\n{\n    size_t i;\n    for (i = 0; i < size; i++) {\n        if (qpclass[input[i]] == QP_PLAIN) luaL_addchar(buffer, input[i]);\n        else qpquote(input[i], buffer);\n    }\n    if (size > 0) luaL_addstring(buffer, EQCRLF);\n    return 0;\n}\n\n/*-------------------------------------------------------------------------*\\\n* Incrementally converts a string to quoted-printable\n* A, B = qp(C, D, marker)\n* Marker is the text to be used to replace CRLF sequences found in A.\n* A is the encoded version of the largest prefix of C .. D that\n* can be encoded without doubts.\n* B has the remaining bytes of C .. D, *without* encoding.\n\\*-------------------------------------------------------------------------*/\nstatic int mime_global_qp(lua_State *L)\n{\n    size_t asize = 0, isize = 0;\n    UC atom[3];\n    const UC *input = (const UC *) luaL_optlstring(L, 1, NULL, &isize);\n    const UC *last = input + isize;\n    const char *marker = luaL_optstring(L, 3, CRLF);\n    luaL_Buffer buffer;\n    /* end-of-input blackhole */\n    if (!input) {\n        lua_pushnil(L);\n        lua_pushnil(L);\n        return 2;\n    }\n    /* make sure we don't confuse buffer stuff with arguments */\n    lua_settop(L, 3);\n    /* process first part of input */\n    luaL_buffinit(L, &buffer);\n    while (input < last)\n        asize = qpencode(*input++, atom, asize, marker, &buffer);\n    input = (const UC *) luaL_optlstring(L, 2, NULL, &isize);\n    /* if second part is nil, we are done */\n    if (!input) {\n        asize = qppad(atom, asize, &buffer);\n        luaL_pushresult(&buffer);\n        if (!(*lua_tostring(L, -1))) lua_pushnil(L);\n        lua_pushnil(L);\n        return 2;\n    }\n    /* otherwise process rest of input */\n    last = input + isize;\n    while (input < last)\n        asize = qpencode(*input++, atom, asize, marker, &buffer);\n    luaL_pushresult(&buffer);\n    lua_pushlstring(L, (char *) atom, asize);\n    return 2;\n}\n\n/*-------------------------------------------------------------------------*\\\n* Accumulate characters until we are sure about how to deal with them.\n* Once we are sure, output the to the buffer, in the correct form.\n\\*-------------------------------------------------------------------------*/\nstatic size_t qpdecode(UC c, UC *input, size_t size, luaL_Buffer *buffer) {\n    int d;\n    input[size++] = c;\n    /* deal with all characters we can deal */\n    switch (input[0]) {\n        /* if we have an escape character */\n        case '=':\n            if (size < 3) return size;\n            /* eliminate soft line break */\n            if (input[1] == '\\r' && input[2] == '\\n') return 0;\n            /* decode quoted representation */\n            c = qpunbase[input[1]]; d = qpunbase[input[2]];\n            /* if it is an invalid, do not decode */\n            if (c > 15 || d > 15) luaL_addlstring(buffer, (char *)input, 3);\n            else luaL_addchar(buffer, (char) ((c << 4) + d));\n            return 0;\n        case '\\r':\n            if (size < 2) return size;\n            if (input[1] == '\\n') luaL_addlstring(buffer, (char *)input, 2);\n            return 0;\n        default:\n            if (input[0] == '\\t' || (input[0] > 31 && input[0] < 127))\n                luaL_addchar(buffer, input[0]);\n            return 0;\n    }\n}\n\n/*-------------------------------------------------------------------------*\\\n* Incrementally decodes a string in quoted-printable\n* A, B = qp(C, D)\n* A is the decoded version of the largest prefix of C .. D that\n* can be decoded without doubts.\n* B has the remaining bytes of C .. D, *without* decoding.\n\\*-------------------------------------------------------------------------*/\nstatic int mime_global_unqp(lua_State *L)\n{\n    size_t asize = 0, isize = 0;\n    UC atom[3];\n    const UC *input = (const UC *) luaL_optlstring(L, 1, NULL, &isize);\n    const UC *last = input + isize;\n    luaL_Buffer buffer;\n    /* end-of-input blackhole */\n    if (!input) {\n        lua_pushnil(L);\n        lua_pushnil(L);\n        return 2;\n    }\n    /* make sure we don't confuse buffer stuff with arguments */\n    lua_settop(L, 2);\n    /* process first part of input */\n    luaL_buffinit(L, &buffer);\n    while (input < last)\n        asize = qpdecode(*input++, atom, asize, &buffer);\n    input = (const UC *) luaL_optlstring(L, 2, NULL, &isize);\n    /* if second part is nil, we are done */\n    if (!input) {\n        luaL_pushresult(&buffer);\n        if (!(*lua_tostring(L, -1))) lua_pushnil(L);\n        lua_pushnil(L);\n        return 2;\n    }\n    /* otherwise process rest of input */\n    last = input + isize;\n    while (input < last)\n        asize = qpdecode(*input++, atom, asize, &buffer);\n    luaL_pushresult(&buffer);\n    lua_pushlstring(L, (char *) atom, asize);\n    return 2;\n}\n\n/*-------------------------------------------------------------------------*\\\n* Incrementally breaks a quoted-printed string into lines\n* A, n = qpwrp(l, B, length)\n* A is a copy of B, broken into lines of at most 'length' bytes.\n* 'l' is how many bytes are left for the first line of B.\n* 'n' is the number of bytes left in the last line of A.\n* There are two complications: lines can't be broken in the middle\n* of an encoded =XX, and there might be line breaks already\n\\*-------------------------------------------------------------------------*/\nstatic int mime_global_qpwrp(lua_State *L)\n{\n    size_t size = 0;\n    int left = (int) luaL_checknumber(L, 1);\n    const UC *input = (const UC *) luaL_optlstring(L, 2, NULL, &size);\n    const UC *last = input + size;\n    int length = (int) luaL_optnumber(L, 3, 76);\n    luaL_Buffer buffer;\n    /* end-of-input blackhole */\n    if (!input) {\n        if (left < length) lua_pushstring(L, EQCRLF);\n        else lua_pushnil(L);\n        lua_pushnumber(L, length);\n        return 2;\n    }\n    /* process all input */\n    luaL_buffinit(L, &buffer);\n    while (input < last) {\n        switch (*input) {\n            case '\\r':\n                break;\n            case '\\n':\n                left = length;\n                luaL_addstring(&buffer, CRLF);\n                break;\n            case '=':\n                if (left <= 3) {\n                    left = length;\n                    luaL_addstring(&buffer, EQCRLF);\n                }\n                luaL_addchar(&buffer, *input);\n                left--;\n                break;\n            default:\n                if (left <= 1) {\n                    left = length;\n                    luaL_addstring(&buffer, EQCRLF);\n                }\n                luaL_addchar(&buffer, *input);\n                left--;\n                break;\n        }\n        input++;\n    }\n    luaL_pushresult(&buffer);\n    lua_pushnumber(L, left);\n    return 2;\n}\n\n/*-------------------------------------------------------------------------*\\\n* Here is what we do: \\n, and \\r are considered candidates for line\n* break. We issue *one* new line marker if any of them is seen alone, or\n* followed by a different one. That is, \\n\\n and \\r\\r will issue two\n* end of line markers each, but \\r\\n, \\n\\r etc will only issue *one*\n* marker.  This covers Mac OS, Mac OS X, VMS, Unix and DOS, as well as\n* probably other more obscure conventions.\n*\n* c is the current character being processed\n* last is the previous character\n\\*-------------------------------------------------------------------------*/\n#define eolcandidate(c) (c == '\\r' || c == '\\n')\nstatic int eolprocess(int c, int last, const char *marker,\n        luaL_Buffer *buffer)\n{\n    if (eolcandidate(c)) {\n        if (eolcandidate(last)) {\n            if (c == last) luaL_addstring(buffer, marker);\n            return 0;\n        } else {\n            luaL_addstring(buffer, marker);\n            return c;\n        }\n    } else {\n        luaL_addchar(buffer, (char) c);\n        return 0;\n    }\n}\n\n/*-------------------------------------------------------------------------*\\\n* Converts a string to uniform EOL convention.\n* A, n = eol(o, B, marker)\n* A is the converted version of the largest prefix of B that can be\n* converted unambiguously. 'o' is the context returned by the previous\n* call. 'n' is the new context.\n\\*-------------------------------------------------------------------------*/\nstatic int mime_global_eol(lua_State *L)\n{\n    int ctx = (int) luaL_checkinteger(L, 1);\n    size_t isize = 0;\n    const char *input = luaL_optlstring(L, 2, NULL, &isize);\n    const char *last = input + isize;\n    const char *marker = luaL_optstring(L, 3, CRLF);\n    luaL_Buffer buffer;\n    luaL_buffinit(L, &buffer);\n    /* end of input blackhole */\n    if (!input) {\n       lua_pushnil(L);\n       lua_pushnumber(L, 0);\n       return 2;\n    }\n    /* process all input */\n    while (input < last)\n        ctx = eolprocess(*input++, ctx, marker, &buffer);\n    luaL_pushresult(&buffer);\n    lua_pushnumber(L, ctx);\n    return 2;\n}\n\n/*-------------------------------------------------------------------------*\\\n* Takes one byte and stuff it if needed.\n\\*-------------------------------------------------------------------------*/\nstatic size_t dot(int c, size_t state, luaL_Buffer *buffer)\n{\n    luaL_addchar(buffer, (char) c);\n    switch (c) {\n        case '\\r':\n            return 1;\n        case '\\n':\n            return (state == 1)? 2: 0;\n        case '.':\n            if (state == 2)\n                luaL_addchar(buffer, '.');\n            /* Falls through. */\n        default:\n            return 0;\n    }\n}\n\n/*-------------------------------------------------------------------------*\\\n* Incrementally applies smtp stuffing to a string\n* A, n = dot(l, D)\n\\*-------------------------------------------------------------------------*/\nstatic int mime_global_dot(lua_State *L)\n{\n    size_t isize = 0, state = (size_t) luaL_checknumber(L, 1);\n    const char *input = luaL_optlstring(L, 2, NULL, &isize);\n    const char *last = input + isize;\n    luaL_Buffer buffer;\n    /* end-of-input blackhole */\n    if (!input) {\n        lua_pushnil(L);\n        lua_pushnumber(L, 2);\n        return 2;\n    }\n    /* process all input */\n    luaL_buffinit(L, &buffer);\n    while (input < last)\n        state = dot(*input++, state, &buffer);\n    luaL_pushresult(&buffer);\n    lua_pushnumber(L, (lua_Number) state);\n    return 2;\n}\n\n"
  },
  {
    "path": "src/luasocket/mime.h",
    "content": "#ifndef MIME_H \n#define MIME_H \n/*=========================================================================*\\\n* Core MIME support\n* LuaSocket toolkit\n*\n* This module provides functions to implement transfer content encodings\n* and formatting conforming to RFC 2045. It is used by mime.lua, which\n* provide a higher level interface to this functionality. \n\\*=========================================================================*/\n#include \"luasocket.h\"\n\n/*-------------------------------------------------------------------------*\\\n* Current MIME library version\n\\*-------------------------------------------------------------------------*/\n#define MIME_VERSION    \"MIME 1.0.3\"\n#define MIME_COPYRIGHT  \"Copyright (C) 2004-2013 Diego Nehab\"\n#define MIME_AUTHORS    \"Diego Nehab\"\n\nLUASOCKET_API int luaopen_mime_core(lua_State *L);\n\n#endif /* MIME_H */\n"
  },
  {
    "path": "src/luasocket/mime.lua",
    "content": "-----------------------------------------------------------------------------\n-- MIME support for the Lua language.\n-- Author: Diego Nehab\n-- Conforming to RFCs 2045-2049\n-----------------------------------------------------------------------------\n\n-----------------------------------------------------------------------------\n-- Declare module and import dependencies\n-----------------------------------------------------------------------------\nlocal _M = mime\n\n-- encode, decode and wrap algorithm tables\nlocal encodet, decodet, wrapt = {},{},{}\n\n_M.encodet = encodet\n_M.decodet = decodet\n_M.wrapt   = wrapt\n\n-- creates a function that chooses a filter by name from a given table\nlocal function choose(table)\n    return function(name, opt1, opt2)\n        if type(name) ~= \"string\" then\n            name, opt1, opt2 = \"default\", name, opt1\n        end\n        local f = table[name or \"nil\"]\n        if not f then \n            error(\"unknown key (\" .. tostring(name) .. \")\", 3)\n        else return f(opt1, opt2) end\n    end\nend\n\n-- define the encoding filters\nencodet['base64'] = function()\n    return ltn12.filter.cycle(_M.b64, \"\")\nend\n\nencodet['quoted-printable'] = function(mode)\n    return ltn12.filter.cycle(_M.qp, \"\",\n        (mode == \"binary\") and \"=0D=0A\" or \"\\r\\n\")\nend\n\n-- define the decoding filters\ndecodet['base64'] = function()\n    return ltn12.filter.cycle(_M.unb64, \"\")\nend\n\ndecodet['quoted-printable'] = function()\n    return ltn12.filter.cycle(_M.unqp, \"\")\nend\n\nlocal function format(chunk)\n    if chunk then\n        if chunk == \"\" then return \"''\"\n        else return string.len(chunk) end\n    else return \"nil\" end\nend\n\n-- define the line-wrap filters\nwrapt['text'] = function(length)\n    length = length or 76\n    return ltn12.filter.cycle(_M.wrp, length, length)\nend\nwrapt['base64'] = wrapt['text']\nwrapt['default'] = wrapt['text']\n\nwrapt['quoted-printable'] = function()\n    return ltn12.filter.cycle(_M.qpwrp, 76, 76)\nend\n\n-- function that choose the encoding, decoding or wrap algorithm\n_M.encode = choose(encodet)\n_M.decode = choose(decodet)\n_M.wrap = choose(wrapt)\n\n-- define the end-of-line normalization filter\nfunction _M.normalize(marker)\n    return ltn12.filter.cycle(_M.eol, 0, marker)\nend\n\n-- high level stuffing filter\nfunction _M.stuff()\n    return ltn12.filter.cycle(_M.dot, 2)\nend\n\nreturn _M\n"
  },
  {
    "path": "src/luasocket/options.c",
    "content": "/*=========================================================================*\\\n* Common option interface\n* LuaSocket toolkit\n\\*=========================================================================*/\n#include \"luasocket.h\"\n#include \"auxiliar.h\"\n#include \"options.h\"\n#include \"inet.h\"\n#include <string.h>\n\n/*=========================================================================*\\\n* Internal functions prototypes\n\\*=========================================================================*/\nstatic int opt_setmembership(lua_State *L, p_socket ps, int level, int name);\nstatic int opt_ip6_setmembership(lua_State *L, p_socket ps, int level, int name);\nstatic int opt_setboolean(lua_State *L, p_socket ps, int level, int name);\nstatic int opt_getboolean(lua_State *L, p_socket ps, int level, int name);\nstatic int opt_setint(lua_State *L, p_socket ps, int level, int name);\nstatic int opt_getint(lua_State *L, p_socket ps, int level, int name);\nstatic int opt_set(lua_State *L, p_socket ps, int level, int name,\n        void *val, int len);\nstatic int opt_get(lua_State *L, p_socket ps, int level, int name,\n        void *val, int* len);\n\n/*=========================================================================*\\\n* Exported functions\n\\*=========================================================================*/\n/*-------------------------------------------------------------------------*\\\n* Calls appropriate option handler\n\\*-------------------------------------------------------------------------*/\nint opt_meth_setoption(lua_State *L, p_opt opt, p_socket ps)\n{\n    const char *name = luaL_checkstring(L, 2);      /* obj, name, ... */\n    while (opt->name && strcmp(name, opt->name))\n        opt++;\n    if (!opt->func) {\n        char msg[57];\n        sprintf(msg, \"unsupported option `%.35s'\", name);\n        luaL_argerror(L, 2, msg);\n    }\n    return opt->func(L, ps);\n}\n\nint opt_meth_getoption(lua_State *L, p_opt opt, p_socket ps)\n{\n    const char *name = luaL_checkstring(L, 2);      /* obj, name, ... */\n    while (opt->name && strcmp(name, opt->name))\n        opt++;\n    if (!opt->func) {\n        char msg[57];\n        sprintf(msg, \"unsupported option `%.35s'\", name);\n        luaL_argerror(L, 2, msg);\n    }\n    return opt->func(L, ps);\n}\n\n// -------------------------------------------------------\n/* enables reuse of local address */\nint opt_set_reuseaddr(lua_State *L, p_socket ps)\n{\n    return opt_setboolean(L, ps, SOL_SOCKET, SO_REUSEADDR);\n}\n\nint opt_get_reuseaddr(lua_State *L, p_socket ps)\n{\n    return opt_getboolean(L, ps, SOL_SOCKET, SO_REUSEADDR);\n}\n\n// -------------------------------------------------------\n/* enables reuse of local port */\nint opt_set_reuseport(lua_State *L, p_socket ps)\n{\n    return opt_setboolean(L, ps, SOL_SOCKET, SO_REUSEPORT);\n}\n\nint opt_get_reuseport(lua_State *L, p_socket ps)\n{\n    return opt_getboolean(L, ps, SOL_SOCKET, SO_REUSEPORT);\n}\n\n// -------------------------------------------------------\n/* disables the Nagle algorithm */\nint opt_set_tcp_nodelay(lua_State *L, p_socket ps)\n{\n    return opt_setboolean(L, ps, IPPROTO_TCP, TCP_NODELAY);\n}\n\nint opt_get_tcp_nodelay(lua_State *L, p_socket ps)\n{\n    return opt_getboolean(L, ps, IPPROTO_TCP, TCP_NODELAY);\n}\n\n// -------------------------------------------------------\n#ifdef TCP_KEEPIDLE\n\nint opt_get_tcp_keepidle(lua_State *L, p_socket ps)\n{\n    return opt_getint(L, ps, IPPROTO_TCP, TCP_KEEPIDLE);\n}\n\nint opt_set_tcp_keepidle(lua_State *L, p_socket ps)\n{\n    return opt_setint(L, ps, IPPROTO_TCP, TCP_KEEPIDLE);\n}\n\n#endif\n\n// -------------------------------------------------------\n#ifdef TCP_KEEPCNT\n\nint opt_get_tcp_keepcnt(lua_State *L, p_socket ps)\n{\n    return opt_getint(L, ps, IPPROTO_TCP, TCP_KEEPCNT);\n}\n\nint opt_set_tcp_keepcnt(lua_State *L, p_socket ps)\n{\n    return opt_setint(L, ps, IPPROTO_TCP, TCP_KEEPCNT);\n}\n\n#endif\n\n// -------------------------------------------------------\n#ifdef TCP_KEEPINTVL\n\nint opt_get_tcp_keepintvl(lua_State *L, p_socket ps)\n{\n    return opt_getint(L, ps, IPPROTO_TCP, TCP_KEEPINTVL);\n}\n\nint opt_set_tcp_keepintvl(lua_State *L, p_socket ps)\n{\n    return opt_setint(L, ps, IPPROTO_TCP, TCP_KEEPINTVL);\n}\n\n#endif\n\n// -------------------------------------------------------\nint opt_set_keepalive(lua_State *L, p_socket ps)\n{\n    return opt_setboolean(L, ps, SOL_SOCKET, SO_KEEPALIVE);\n}\n\nint opt_get_keepalive(lua_State *L, p_socket ps)\n{\n    return opt_getboolean(L, ps, SOL_SOCKET, SO_KEEPALIVE);\n}\n\n// -------------------------------------------------------\nint opt_set_dontroute(lua_State *L, p_socket ps)\n{\n    return opt_setboolean(L, ps, SOL_SOCKET, SO_DONTROUTE);\n}\n\nint opt_get_dontroute(lua_State *L, p_socket ps)\n{\n    return opt_getboolean(L, ps, SOL_SOCKET, SO_DONTROUTE);\n}\n\n// -------------------------------------------------------\nint opt_set_broadcast(lua_State *L, p_socket ps)\n{\n    return opt_setboolean(L, ps, SOL_SOCKET, SO_BROADCAST);\n}\n\nint opt_get_broadcast(lua_State *L, p_socket ps)\n{\n    return opt_getboolean(L, ps, SOL_SOCKET, SO_BROADCAST);\n}\n\n// -------------------------------------------------------\nint opt_set_recv_buf_size(lua_State *L, p_socket ps)\n{\n\treturn opt_setint(L, ps, SOL_SOCKET, SO_RCVBUF);\n}\n\nint opt_get_recv_buf_size(lua_State *L, p_socket ps)\n{\n\treturn opt_getint(L, ps, SOL_SOCKET, SO_RCVBUF);\n}\n\n// -------------------------------------------------------\nint opt_get_send_buf_size(lua_State *L, p_socket ps)\n{\n\treturn opt_getint(L, ps, SOL_SOCKET, SO_SNDBUF);\n}\n\nint opt_set_send_buf_size(lua_State *L, p_socket ps)\n{\n\treturn opt_setint(L, ps, SOL_SOCKET, SO_SNDBUF);\n}\n\n// -------------------------------------------------------\nint opt_set_ip6_unicast_hops(lua_State *L, p_socket ps)\n{\n  return opt_setint(L, ps, IPPROTO_IPV6, IPV6_UNICAST_HOPS);\n}\n\nint opt_get_ip6_unicast_hops(lua_State *L, p_socket ps)\n{\n  return opt_getint(L, ps, IPPROTO_IPV6, IPV6_UNICAST_HOPS);\n}\n\n// -------------------------------------------------------\nint opt_set_ip6_multicast_hops(lua_State *L, p_socket ps)\n{\n  return opt_setint(L, ps, IPPROTO_IPV6, IPV6_MULTICAST_HOPS);\n}\n\nint opt_get_ip6_multicast_hops(lua_State *L, p_socket ps)\n{\n  return opt_getint(L, ps, IPPROTO_IPV6, IPV6_MULTICAST_HOPS);\n}\n\n// -------------------------------------------------------\nint opt_set_ip_multicast_loop(lua_State *L, p_socket ps)\n{\n    return opt_setboolean(L, ps, IPPROTO_IP, IP_MULTICAST_LOOP);\n}\n\nint opt_get_ip_multicast_loop(lua_State *L, p_socket ps)\n{\n    return opt_getboolean(L, ps, IPPROTO_IP, IP_MULTICAST_LOOP);\n}\n\n// -------------------------------------------------------\nint opt_set_ip6_multicast_loop(lua_State *L, p_socket ps)\n{\n    return opt_setboolean(L, ps, IPPROTO_IPV6, IPV6_MULTICAST_LOOP);\n}\n\nint opt_get_ip6_multicast_loop(lua_State *L, p_socket ps)\n{\n    return opt_getboolean(L, ps, IPPROTO_IPV6, IPV6_MULTICAST_LOOP);\n}\n\n// -------------------------------------------------------\nint opt_set_linger(lua_State *L, p_socket ps)\n{\n    struct linger li;                      /* obj, name, table */\n    if (!lua_istable(L, 3)) auxiliar_typeerror(L,3,lua_typename(L, LUA_TTABLE));\n    lua_pushstring(L, \"on\");\n    lua_gettable(L, 3);\n    if (!lua_isboolean(L, -1))\n        luaL_argerror(L, 3, \"boolean 'on' field expected\");\n    li.l_onoff = (u_short) lua_toboolean(L, -1);\n    lua_pushstring(L, \"timeout\");\n    lua_gettable(L, 3);\n    if (!lua_isnumber(L, -1))\n        luaL_argerror(L, 3, \"number 'timeout' field expected\");\n    li.l_linger = (u_short) lua_tonumber(L, -1);\n    return opt_set(L, ps, SOL_SOCKET, SO_LINGER, (char *) &li, sizeof(li));\n}\n\nint opt_get_linger(lua_State *L, p_socket ps)\n{\n    struct linger li;                      /* obj, name */\n    int len = sizeof(li);\n    int err = opt_get(L, ps, SOL_SOCKET, SO_LINGER, (char *) &li, &len);\n    if (err)\n        return err;\n    lua_newtable(L);\n    lua_pushboolean(L, li.l_onoff);\n    lua_setfield(L, -2, \"on\");\n    lua_pushinteger(L, li.l_linger);\n    lua_setfield(L, -2, \"timeout\");\n    return 1;\n}\n\n// -------------------------------------------------------\nint opt_set_ip_multicast_ttl(lua_State *L, p_socket ps)\n{\n    return opt_setint(L, ps, IPPROTO_IP, IP_MULTICAST_TTL);\n}\n\n// -------------------------------------------------------\nint opt_set_ip_multicast_if(lua_State *L, p_socket ps)\n{\n    const char *address = luaL_checkstring(L, 3);    /* obj, name, ip */\n    struct in_addr val;\n    val.s_addr = htonl(INADDR_ANY);\n    if (strcmp(address, \"*\") && !inet_aton(address, &val))\n        luaL_argerror(L, 3, \"ip expected\");\n    return opt_set(L, ps, IPPROTO_IP, IP_MULTICAST_IF,\n        (char *) &val, sizeof(val));\n}\n\nint opt_get_ip_multicast_if(lua_State *L, p_socket ps)\n{\n    struct in_addr val;\n    socklen_t len = sizeof(val);\n    if (getsockopt(*ps, IPPROTO_IP, IP_MULTICAST_IF, (char *) &val, &len) < 0) {\n        lua_pushnil(L);\n        lua_pushstring(L, \"getsockopt failed\");\n        return 2;\n    }\n    lua_pushstring(L, inet_ntoa(val));\n    return 1;\n}\n\n// -------------------------------------------------------\nint opt_set_ip_add_membership(lua_State *L, p_socket ps)\n{\n    return opt_setmembership(L, ps, IPPROTO_IP, IP_ADD_MEMBERSHIP);\n}\n\nint opt_set_ip_drop_membersip(lua_State *L, p_socket ps)\n{\n    return opt_setmembership(L, ps, IPPROTO_IP, IP_DROP_MEMBERSHIP);\n}\n\n// -------------------------------------------------------\nint opt_set_ip6_add_membership(lua_State *L, p_socket ps)\n{\n    return opt_ip6_setmembership(L, ps, IPPROTO_IPV6, IPV6_ADD_MEMBERSHIP);\n}\n\nint opt_set_ip6_drop_membersip(lua_State *L, p_socket ps)\n{\n    return opt_ip6_setmembership(L, ps, IPPROTO_IPV6, IPV6_DROP_MEMBERSHIP);\n}\n// -------------------------------------------------------\nint opt_get_ip6_v6only(lua_State *L, p_socket ps)\n{\n    return opt_getboolean(L, ps, IPPROTO_IPV6, IPV6_V6ONLY);\n}\n\nint opt_set_ip6_v6only(lua_State *L, p_socket ps)\n{\n    return opt_setboolean(L, ps, IPPROTO_IPV6, IPV6_V6ONLY);\n}\n\n// -------------------------------------------------------\nint opt_get_error(lua_State *L, p_socket ps)\n{\n    int val = 0;\n    socklen_t len = sizeof(val);\n    if (getsockopt(*ps, SOL_SOCKET, SO_ERROR, (char *) &val, &len) < 0) {\n        lua_pushnil(L);\n        lua_pushstring(L, \"getsockopt failed\");\n        return 2;\n    }\n    lua_pushstring(L, socket_strerror(val));\n    return 1;\n}\n\n/*=========================================================================*\\\n* Auxiliar functions\n\\*=========================================================================*/\nstatic int opt_setmembership(lua_State *L, p_socket ps, int level, int name)\n{\n    struct ip_mreq val;                   /* obj, name, table */\n    if (!lua_istable(L, 3)) auxiliar_typeerror(L,3,lua_typename(L, LUA_TTABLE));\n    lua_pushstring(L, \"multiaddr\");\n    lua_gettable(L, 3);\n    if (!lua_isstring(L, -1))\n        luaL_argerror(L, 3, \"string 'multiaddr' field expected\");\n    if (!inet_aton(lua_tostring(L, -1), &val.imr_multiaddr))\n        luaL_argerror(L, 3, \"invalid 'multiaddr' ip address\");\n    lua_pushstring(L, \"interface\");\n    lua_gettable(L, 3);\n    if (!lua_isstring(L, -1))\n        luaL_argerror(L, 3, \"string 'interface' field expected\");\n    val.imr_interface.s_addr = htonl(INADDR_ANY);\n    if (strcmp(lua_tostring(L, -1), \"*\") &&\n            !inet_aton(lua_tostring(L, -1), &val.imr_interface))\n        luaL_argerror(L, 3, \"invalid 'interface' ip address\");\n    return opt_set(L, ps, level, name, (char *) &val, sizeof(val));\n}\n\nstatic int opt_ip6_setmembership(lua_State *L, p_socket ps, int level, int name)\n{\n    struct ipv6_mreq val;                   /* obj, opt-name, table */\n    memset(&val, 0, sizeof(val));\n    if (!lua_istable(L, 3)) auxiliar_typeerror(L,3,lua_typename(L, LUA_TTABLE));\n    lua_pushstring(L, \"multiaddr\");\n    lua_gettable(L, 3);\n    if (!lua_isstring(L, -1))\n        luaL_argerror(L, 3, \"string 'multiaddr' field expected\");\n    if (!inet_pton(AF_INET6, lua_tostring(L, -1), &val.ipv6mr_multiaddr))\n        luaL_argerror(L, 3, \"invalid 'multiaddr' ip address\");\n    lua_pushstring(L, \"interface\");\n    lua_gettable(L, 3);\n    /* By default we listen to interface on default route\n     * (sigh). However, interface= can override it. We should\n     * support either number, or name for it. Waiting for\n     * windows port of if_nametoindex */\n    if (!lua_isnil(L, -1)) {\n        if (lua_isnumber(L, -1)) {\n            val.ipv6mr_interface = (unsigned int) lua_tonumber(L, -1);\n        } else\n          luaL_argerror(L, -1, \"number 'interface' field expected\");\n    }\n    return opt_set(L, ps, level, name, (char *) &val, sizeof(val));\n}\n\nstatic\nint opt_get(lua_State *L, p_socket ps, int level, int name, void *val, int* len)\n{\n    socklen_t socklen = *len;\n    if (getsockopt(*ps, level, name, (char *) val, &socklen) < 0) {\n        lua_pushnil(L);\n        lua_pushstring(L, \"getsockopt failed\");\n        return 2;\n    }\n    *len = socklen;\n    return 0;\n}\n\nstatic\nint opt_set(lua_State *L, p_socket ps, int level, int name, void *val, int len)\n{\n    if (setsockopt(*ps, level, name, (char *) val, len) < 0) {\n        lua_pushnil(L);\n        lua_pushstring(L, \"setsockopt failed\");\n        return 2;\n    }\n    lua_pushnumber(L, 1);\n    return 1;\n}\n\nstatic int opt_getboolean(lua_State *L, p_socket ps, int level, int name)\n{\n    int val = 0;\n    int len = sizeof(val);\n    int err = opt_get(L, ps, level, name, (char *) &val, &len);\n    if (err)\n        return err;\n    lua_pushboolean(L, val);\n    return 1;\n}\n\nstatic int opt_setboolean(lua_State *L, p_socket ps, int level, int name)\n{\n    int val = auxiliar_checkboolean(L, 3);             /* obj, name, bool */\n    return opt_set(L, ps, level, name, (char *) &val, sizeof(val));\n}\n\nstatic int opt_getint(lua_State *L, p_socket ps, int level, int name)\n{\n    int val = 0;\n    int len = sizeof(val);\n    int err = opt_get(L, ps, level, name, (char *) &val, &len);\n    if (err)\n        return err;\n    lua_pushnumber(L, val);\n    return 1;\n}\n\nstatic int opt_setint(lua_State *L, p_socket ps, int level, int name)\n{\n    int val = (int) lua_tonumber(L, 3);             /* obj, name, int */\n    return opt_set(L, ps, level, name, (char *) &val, sizeof(val));\n}\n"
  },
  {
    "path": "src/luasocket/options.h",
    "content": "#ifndef OPTIONS_H\n#define OPTIONS_H\n/*=========================================================================*\\\n* Common option interface \n* LuaSocket toolkit\n*\n* This module provides a common interface to socket options, used mainly by\n* modules UDP and TCP. \n\\*=========================================================================*/\n\n#include \"luasocket.h\"\n#include \"socket.h\"\n\n/* option registry */\ntypedef struct t_opt {\n  const char *name;\n  int (*func)(lua_State *L, p_socket ps);\n} t_opt;\ntypedef t_opt *p_opt;\n\n#ifndef _WIN32\n#pragma GCC visibility push(hidden)\n#endif\n\nint opt_meth_setoption(lua_State *L, p_opt opt, p_socket ps);\nint opt_meth_getoption(lua_State *L, p_opt opt, p_socket ps);\n\nint opt_set_reuseaddr(lua_State *L, p_socket ps);\nint opt_get_reuseaddr(lua_State *L, p_socket ps);\n\nint opt_set_reuseport(lua_State *L, p_socket ps);\nint opt_get_reuseport(lua_State *L, p_socket ps);\n\nint opt_set_tcp_nodelay(lua_State *L, p_socket ps);\nint opt_get_tcp_nodelay(lua_State *L, p_socket ps);\n\n#ifdef TCP_KEEPIDLE\nint opt_set_tcp_keepidle(lua_State *L, p_socket ps);\nint opt_get_tcp_keepidle(lua_State *L, p_socket ps);\n#endif\n\n#ifdef TCP_KEEPCNT\nint opt_set_tcp_keepcnt(lua_State *L, p_socket ps);\nint opt_get_tcp_keepcnt(lua_State *L, p_socket ps);\n#endif\n\n#ifdef TCP_KEEPINTVL\nint opt_set_tcp_keepintvl(lua_State *L, p_socket ps);\nint opt_get_tcp_keepintvl(lua_State *L, p_socket ps);\n#endif\n\nint opt_set_keepalive(lua_State *L, p_socket ps);\nint opt_get_keepalive(lua_State *L, p_socket ps);\n\nint opt_set_dontroute(lua_State *L, p_socket ps);\nint opt_get_dontroute(lua_State *L, p_socket ps);\n\nint opt_set_broadcast(lua_State *L, p_socket ps);\nint opt_get_broadcast(lua_State *L, p_socket ps);\n\nint opt_set_recv_buf_size(lua_State *L, p_socket ps);\nint opt_get_recv_buf_size(lua_State *L, p_socket ps);\n\nint opt_set_send_buf_size(lua_State *L, p_socket ps);\nint opt_get_send_buf_size(lua_State *L, p_socket ps);\n\nint opt_set_ip6_unicast_hops(lua_State *L, p_socket ps);\nint opt_get_ip6_unicast_hops(lua_State *L, p_socket ps);\n\nint opt_set_ip6_multicast_hops(lua_State *L, p_socket ps);\nint opt_get_ip6_multicast_hops(lua_State *L, p_socket ps);\n\nint opt_set_ip_multicast_loop(lua_State *L, p_socket ps);\nint opt_get_ip_multicast_loop(lua_State *L, p_socket ps);\n\nint opt_set_ip6_multicast_loop(lua_State *L, p_socket ps);\nint opt_get_ip6_multicast_loop(lua_State *L, p_socket ps);\n\nint opt_set_linger(lua_State *L, p_socket ps);\nint opt_get_linger(lua_State *L, p_socket ps);\n\nint opt_set_ip_multicast_ttl(lua_State *L, p_socket ps);\n\nint opt_set_ip_multicast_if(lua_State *L, p_socket ps);\nint opt_get_ip_multicast_if(lua_State *L, p_socket ps);\n\nint opt_set_ip_add_membership(lua_State *L, p_socket ps);\nint opt_set_ip_drop_membersip(lua_State *L, p_socket ps);\n\nint opt_set_ip6_add_membership(lua_State *L, p_socket ps);\nint opt_set_ip6_drop_membersip(lua_State *L, p_socket ps);\n\nint opt_set_ip6_v6only(lua_State *L, p_socket ps);\nint opt_get_ip6_v6only(lua_State *L, p_socket ps);\n\nint opt_get_error(lua_State *L, p_socket ps);\n\n#ifndef _WIN32\n#pragma GCC visibility pop\n#endif\n\n#endif\n"
  },
  {
    "path": "src/luasocket/pierror.h",
    "content": "#ifndef PIERROR_H\n#define PIERROR_H\n/*=========================================================================*\\\n* Error messages\n* Defines platform independent error messages\n\\*=========================================================================*/\n\n#define PIE_HOST_NOT_FOUND \"host not found\"\n#define PIE_ADDRINUSE      \"address already in use\"\n#define PIE_ISCONN         \"already connected\"\n#define PIE_ACCESS         \"permission denied\"\n#define PIE_CONNREFUSED    \"connection refused\"\n#define PIE_CONNABORTED    \"closed\"\n#define PIE_CONNRESET      \"closed\"\n#define PIE_TIMEDOUT       \"timeout\"\n#define PIE_AGAIN          \"temporary failure in name resolution\"\n#define PIE_BADFLAGS       \"invalid value for ai_flags\"\n#define PIE_BADHINTS       \"invalid value for hints\"\n#define PIE_FAIL           \"non-recoverable failure in name resolution\"\n#define PIE_FAMILY         \"ai_family not supported\"\n#define PIE_MEMORY         \"memory allocation failure\"\n#define PIE_NONAME         \"host or service not provided, or not known\"\n#define PIE_OVERFLOW       \"argument buffer overflow\"\n#define PIE_PROTOCOL       \"resolved protocol is unknown\"\n#define PIE_SERVICE        \"service not supported for socket type\"\n#define PIE_SOCKTYPE       \"ai_socktype not supported\"\n\n#endif\n"
  },
  {
    "path": "src/luasocket/select.c",
    "content": "/*=========================================================================*\\\n* Select implementation\n* LuaSocket toolkit\n\\*=========================================================================*/\n#include \"luasocket.h\"\n\n#include \"socket.h\"\n#include \"timeout.h\"\n#include \"select.h\"\n\n#include <string.h>\n\n/*=========================================================================*\\\n* Internal function prototypes.\n\\*=========================================================================*/\nstatic t_socket getfd(lua_State *L);\nstatic int dirty(lua_State *L);\nstatic void collect_fd(lua_State *L, int tab, int itab,\n        fd_set *set, t_socket *max_fd);\nstatic int check_dirty(lua_State *L, int tab, int dtab, fd_set *set);\nstatic void return_fd(lua_State *L, fd_set *set, t_socket max_fd,\n        int itab, int tab, int start);\nstatic void make_assoc(lua_State *L, int tab);\nstatic int global_select(lua_State *L);\n\n/* functions in library namespace */\nstatic luaL_Reg func[] = {\n    {\"select\", global_select},\n    {NULL,     NULL}\n};\n\n/*-------------------------------------------------------------------------*\\\n* Initializes module\n\\*-------------------------------------------------------------------------*/\nint select_open(lua_State *L) {\n    lua_pushstring(L, \"_SETSIZE\");\n    lua_pushinteger(L, FD_SETSIZE);\n    lua_rawset(L, -3);\n    lua_pushstring(L, \"_SOCKETINVALID\");\n    lua_pushinteger(L, SOCKET_INVALID);\n    lua_rawset(L, -3);\n    luaL_setfuncs(L, func, 0);\n    return 0;\n}\n\n/*=========================================================================*\\\n* Global Lua functions\n\\*=========================================================================*/\n/*-------------------------------------------------------------------------*\\\n* Waits for a set of sockets until a condition is met or timeout.\n\\*-------------------------------------------------------------------------*/\nstatic int global_select(lua_State *L) {\n    int rtab, wtab, itab, ret, ndirty;\n    t_socket max_fd = SOCKET_INVALID;\n    fd_set rset, wset;\n    t_timeout tm;\n    double t = luaL_optnumber(L, 3, -1);\n    FD_ZERO(&rset); FD_ZERO(&wset);\n    lua_settop(L, 3);\n    lua_newtable(L); itab = lua_gettop(L);\n    lua_newtable(L); rtab = lua_gettop(L);\n    lua_newtable(L); wtab = lua_gettop(L);\n    collect_fd(L, 1, itab, &rset, &max_fd);\n    collect_fd(L, 2, itab, &wset, &max_fd);\n    ndirty = check_dirty(L, 1, rtab, &rset);\n    t = ndirty > 0? 0.0: t;\n    timeout_init(&tm, t, -1);\n    timeout_markstart(&tm);\n    ret = socket_select(max_fd+1, &rset, &wset, NULL, &tm);\n    if (ret > 0 || ndirty > 0) {\n        return_fd(L, &rset, max_fd+1, itab, rtab, ndirty);\n        return_fd(L, &wset, max_fd+1, itab, wtab, 0);\n        make_assoc(L, rtab);\n        make_assoc(L, wtab);\n        return 2;\n    } else if (ret == 0) {\n        lua_pushstring(L, \"timeout\");\n        return 3;\n    } else {\n        luaL_error(L, \"select failed\");\n        return 3;\n    }\n}\n\n/*=========================================================================*\\\n* Internal functions\n\\*=========================================================================*/\nstatic t_socket getfd(lua_State *L) {\n    t_socket fd = SOCKET_INVALID;\n    lua_pushstring(L, \"getfd\");\n    lua_gettable(L, -2);\n    if (!lua_isnil(L, -1)) {\n        lua_pushvalue(L, -2);\n        lua_call(L, 1, 1);\n        if (lua_isnumber(L, -1)) {\n            double numfd = lua_tonumber(L, -1);\n            fd = (numfd >= 0.0)? (t_socket) numfd: SOCKET_INVALID;\n        }\n    }\n    lua_pop(L, 1);\n    return fd;\n}\n\nstatic int dirty(lua_State *L) {\n    int is = 0;\n    lua_pushstring(L, \"dirty\");\n    lua_gettable(L, -2);\n    if (!lua_isnil(L, -1)) {\n        lua_pushvalue(L, -2);\n        lua_call(L, 1, 1);\n        is = lua_toboolean(L, -1);\n    }\n    lua_pop(L, 1);\n    return is;\n}\n\nstatic void collect_fd(lua_State *L, int tab, int itab,\n        fd_set *set, t_socket *max_fd) {\n    int i = 1, n = 0;\n    /* nil is the same as an empty table */\n    if (lua_isnil(L, tab)) return;\n    /* otherwise we need it to be a table */\n    luaL_checktype(L, tab, LUA_TTABLE);\n    for ( ;; ) {\n        t_socket fd;\n        lua_pushnumber(L, i);\n        lua_gettable(L, tab);\n        if (lua_isnil(L, -1)) {\n            lua_pop(L, 1);\n            break;\n        }\n        /* getfd figures out if this is a socket */\n        fd = getfd(L);\n        if (fd != SOCKET_INVALID) {\n            /* make sure we don't overflow the fd_set */\n#ifdef _WIN32\n            if (n >= FD_SETSIZE)\n                luaL_argerror(L, tab, \"too many sockets\");\n#else\n            if (fd >= FD_SETSIZE)\n                luaL_argerror(L, tab, \"descriptor too large for set size\");\n#endif\n            FD_SET(fd, set);\n            n++;\n            /* keep track of the largest descriptor so far */\n            if (*max_fd == SOCKET_INVALID || *max_fd < fd)\n                *max_fd = fd;\n            /* make sure we can map back from descriptor to the object */\n            lua_pushnumber(L, (lua_Number) fd);\n            lua_pushvalue(L, -2);\n            lua_settable(L, itab);\n        }\n        lua_pop(L, 1);\n        i = i + 1;\n    }\n}\n\nstatic int check_dirty(lua_State *L, int tab, int dtab, fd_set *set) {\n    int ndirty = 0, i = 1;\n    if (lua_isnil(L, tab))\n        return 0;\n    for ( ;; ) {\n        t_socket fd;\n        lua_pushnumber(L, i);\n        lua_gettable(L, tab);\n        if (lua_isnil(L, -1)) {\n            lua_pop(L, 1);\n            break;\n        }\n        fd = getfd(L);\n        if (fd != SOCKET_INVALID && dirty(L)) {\n            lua_pushnumber(L, ++ndirty);\n            lua_pushvalue(L, -2);\n            lua_settable(L, dtab);\n            FD_CLR(fd, set);\n        }\n        lua_pop(L, 1);\n        i = i + 1;\n    }\n    return ndirty;\n}\n\nstatic void return_fd(lua_State *L, fd_set *set, t_socket max_fd,\n        int itab, int tab, int start) {\n    t_socket fd;\n    for (fd = 0; fd < max_fd; fd++) {\n        if (FD_ISSET(fd, set)) {\n            lua_pushnumber(L, ++start);\n            lua_pushnumber(L, (lua_Number) fd);\n            lua_gettable(L, itab);\n            lua_settable(L, tab);\n        }\n    }\n}\n\nstatic void make_assoc(lua_State *L, int tab) {\n    int i = 1, atab;\n    lua_newtable(L); atab = lua_gettop(L);\n    for ( ;; ) {\n        lua_pushnumber(L, i);\n        lua_gettable(L, tab);\n        if (!lua_isnil(L, -1)) {\n            lua_pushnumber(L, i);\n            lua_pushvalue(L, -2);\n            lua_settable(L, atab);\n            lua_pushnumber(L, i);\n            lua_settable(L, atab);\n        } else {\n            lua_pop(L, 1);\n            break;\n        }\n        i = i+1;\n    }\n}\n"
  },
  {
    "path": "src/luasocket/select.h",
    "content": "#ifndef SELECT_H\n#define SELECT_H\n/*=========================================================================*\\\n* Select implementation\n* LuaSocket toolkit\n*\n* Each object that can be passed to the select function has to export \n* method getfd() which returns the descriptor to be passed to the\n* underlying select function. Another method, dirty(), should return \n* true if there is data ready for reading (required for buffered input).\n\\*=========================================================================*/\n\n#ifndef _WIN32\n#pragma GCC visibility push(hidden)\n#endif\n\nint select_open(lua_State *L);\n\n#ifndef _WIN32\n#pragma GCC visibility pop\n#endif\n\n#endif /* SELECT_H */\n"
  },
  {
    "path": "src/luasocket/serial.c",
    "content": "/*=========================================================================*\\\n* Serial stream\n* LuaSocket toolkit\n\\*=========================================================================*/\n#include \"luasocket.h\"\n\n#include \"auxiliar.h\"\n#include \"socket.h\"\n#include \"options.h\"\n#include \"unix.h\"\n\n#include <string.h>\n#include <sys/un.h>\n\n/*\nReuses userdata definition from unix.h, since it is useful for all\nstream-like objects.\n\nIf we stored the serial path for use in error messages or userdata\nprinting, we might need our own userdata definition.\n\nGroup usage is semi-inherited from unix.c, but unnecessary since we\nhave only one object type.\n*/\n\n/*=========================================================================*\\\n* Internal function prototypes\n\\*=========================================================================*/\nstatic int global_create(lua_State *L);\nstatic int meth_send(lua_State *L);\nstatic int meth_receive(lua_State *L);\nstatic int meth_close(lua_State *L);\nstatic int meth_settimeout(lua_State *L);\nstatic int meth_getfd(lua_State *L);\nstatic int meth_setfd(lua_State *L);\nstatic int meth_dirty(lua_State *L);\nstatic int meth_getstats(lua_State *L);\nstatic int meth_setstats(lua_State *L);\n\n/* serial object methods */\nstatic luaL_Reg serial_methods[] = {\n    {\"__gc\",        meth_close},\n    {\"__tostring\",  auxiliar_tostring},\n    {\"close\",       meth_close},\n    {\"dirty\",       meth_dirty},\n    {\"getfd\",       meth_getfd},\n    {\"getstats\",    meth_getstats},\n    {\"setstats\",    meth_setstats},\n    {\"receive\",     meth_receive},\n    {\"send\",        meth_send},\n    {\"setfd\",       meth_setfd},\n    {\"settimeout\",  meth_settimeout},\n    {NULL,          NULL}\n};\n\n/*-------------------------------------------------------------------------*\\\n* Initializes module\n\\*-------------------------------------------------------------------------*/\nLUASOCKET_API int luaopen_socket_serial(lua_State *L) {\n    /* create classes */\n    auxiliar_newclass(L, \"serial{client}\", serial_methods);\n    /* create class groups */\n    auxiliar_add2group(L, \"serial{client}\", \"serial{any}\");\n    lua_pushcfunction(L, global_create);\n    return 1;\n}\n\n/*=========================================================================*\\\n* Lua methods\n\\*=========================================================================*/\n/*-------------------------------------------------------------------------*\\\n* Just call buffered IO methods\n\\*-------------------------------------------------------------------------*/\nstatic int meth_send(lua_State *L) {\n    p_unix un = (p_unix) auxiliar_checkclass(L, \"serial{client}\", 1);\n    return buffer_meth_send(L, &un->buf);\n}\n\nstatic int meth_receive(lua_State *L) {\n    p_unix un = (p_unix) auxiliar_checkclass(L, \"serial{client}\", 1);\n    return buffer_meth_receive(L, &un->buf);\n}\n\nstatic int meth_getstats(lua_State *L) {\n    p_unix un = (p_unix) auxiliar_checkclass(L, \"serial{client}\", 1);\n    return buffer_meth_getstats(L, &un->buf);\n}\n\nstatic int meth_setstats(lua_State *L) {\n    p_unix un = (p_unix) auxiliar_checkclass(L, \"serial{client}\", 1);\n    return buffer_meth_setstats(L, &un->buf);\n}\n\n/*-------------------------------------------------------------------------*\\\n* Select support methods\n\\*-------------------------------------------------------------------------*/\nstatic int meth_getfd(lua_State *L) {\n    p_unix un = (p_unix) auxiliar_checkgroup(L, \"serial{any}\", 1);\n    lua_pushnumber(L, (int) un->sock);\n    return 1;\n}\n\n/* this is very dangerous, but can be handy for those that are brave enough */\nstatic int meth_setfd(lua_State *L) {\n    p_unix un = (p_unix) auxiliar_checkgroup(L, \"serial{any}\", 1);\n    un->sock = (t_socket) luaL_checknumber(L, 2);\n    return 0;\n}\n\nstatic int meth_dirty(lua_State *L) {\n    p_unix un = (p_unix) auxiliar_checkgroup(L, \"serial{any}\", 1);\n    lua_pushboolean(L, !buffer_isempty(&un->buf));\n    return 1;\n}\n\n/*-------------------------------------------------------------------------*\\\n* Closes socket used by object\n\\*-------------------------------------------------------------------------*/\nstatic int meth_close(lua_State *L)\n{\n    p_unix un = (p_unix) auxiliar_checkgroup(L, \"serial{any}\", 1);\n    socket_destroy(&un->sock);\n    lua_pushnumber(L, 1);\n    return 1;\n}\n\n\n/*-------------------------------------------------------------------------*\\\n* Just call tm methods\n\\*-------------------------------------------------------------------------*/\nstatic int meth_settimeout(lua_State *L) {\n    p_unix un = (p_unix) auxiliar_checkgroup(L, \"serial{any}\", 1);\n    return timeout_meth_settimeout(L, &un->tm);\n}\n\n/*=========================================================================*\\\n* Library functions\n\\*=========================================================================*/\n\n\n/*-------------------------------------------------------------------------*\\\n* Creates a serial object\n\\*-------------------------------------------------------------------------*/\nstatic int global_create(lua_State *L) {\n    const char* path = luaL_checkstring(L, 1);\n\n    /* allocate unix object */\n    p_unix un = (p_unix) lua_newuserdata(L, sizeof(t_unix));\n\n    /* open serial device */\n    t_socket sock = open(path, O_NOCTTY|O_RDWR);\n\n    /*printf(\"open %s on %d\\n\", path, sock);*/\n\n    if (sock < 0)  {\n        lua_pushnil(L);\n        lua_pushstring(L, socket_strerror(errno));\n        lua_pushnumber(L, errno);\n        return 3;\n    }\n    /* set its type as client object */\n    auxiliar_setclass(L, \"serial{client}\", -1);\n    /* initialize remaining structure fields */\n    socket_setnonblocking(&sock);\n    un->sock = sock;\n    io_init(&un->io, (p_send) socket_write, (p_recv) socket_read,\n            (p_error) socket_ioerror, &un->sock);\n    timeout_init(&un->tm, -1, -1);\n    buffer_init(&un->buf, &un->io, &un->tm);\n    return 1;\n}\n"
  },
  {
    "path": "src/luasocket/smtp.lua",
    "content": "-----------------------------------------------------------------------------\n-- SMTP client support for the Lua language.\n-- LuaSocket toolkit.\n-- Author: Diego Nehab\n-----------------------------------------------------------------------------\n\n-----------------------------------------------------------------------------\n-- Declare module and import dependencies\n-----------------------------------------------------------------------------\nlocal tp = require(\"socket.tp\")\n\nsocket.smtp = {}\nlocal _M = socket.smtp\n\n-----------------------------------------------------------------------------\n-- Program constants\n-----------------------------------------------------------------------------\n-- timeout for connection\n_M.TIMEOUT = 60\n-- default server used to send e-mails\n_M.SERVER = \"localhost\"\n-- default port\n_M.PORT = 25\n-- domain used in HELO command and default sendmail\n-- If we are under a CGI, try to get from environment\n_M.DOMAIN = os.getenv(\"SERVER_NAME\") or \"localhost\"\n-- default time zone (means we don't know)\n_M.ZONE = \"-0000\"\n\n---------------------------------------------------------------------------\n-- Low level SMTP API\n-----------------------------------------------------------------------------\nlocal metat = { __index = {} }\n\nfunction metat.__index:greet(domain)\n    self.try(self.tp:check(\"2..\"))\n    self.try(self.tp:command(\"EHLO\", domain or _M.DOMAIN))\n    return socket.skip(1, self.try(self.tp:check(\"2..\")))\nend\n\nfunction metat.__index:mail(from)\n    self.try(self.tp:command(\"MAIL\", \"FROM:\" .. from))\n    return self.try(self.tp:check(\"2..\"))\nend\n\nfunction metat.__index:rcpt(to)\n    self.try(self.tp:command(\"RCPT\", \"TO:\" .. to))\n    return self.try(self.tp:check(\"2..\"))\nend\n\nfunction metat.__index:data(src, step)\n    self.try(self.tp:command(\"DATA\"))\n    self.try(self.tp:check(\"3..\"))\n    self.try(self.tp:source(src, step))\n    self.try(self.tp:send(\"\\r\\n.\\r\\n\"))\n    return self.try(self.tp:check(\"2..\"))\nend\n\nfunction metat.__index:quit()\n    self.try(self.tp:command(\"QUIT\"))\n    return self.try(self.tp:check(\"2..\"))\nend\n\nfunction metat.__index:close()\n    return self.tp:close()\nend\n\nfunction metat.__index:login(user, password)\n    self.try(self.tp:command(\"AUTH\", \"LOGIN\"))\n    self.try(self.tp:check(\"3..\"))\n    self.try(self.tp:send(mime.b64(user) .. \"\\r\\n\"))\n    self.try(self.tp:check(\"3..\"))\n    self.try(self.tp:send(mime.b64(password) .. \"\\r\\n\"))\n    return self.try(self.tp:check(\"2..\"))\nend\n\nfunction metat.__index:plain(user, password)\n    local auth = \"PLAIN \" .. mime.b64(\"\\0\" .. user .. \"\\0\" .. password)\n    self.try(self.tp:command(\"AUTH\", auth))\n    return self.try(self.tp:check(\"2..\"))\nend\n\nfunction metat.__index:auth(user, password, ext)\n    if not user or not password then return 1 end\n    if string.find(ext, \"AUTH[^\\n]+LOGIN\") then\n        return self:login(user, password)\n    elseif string.find(ext, \"AUTH[^\\n]+PLAIN\") then\n        return self:plain(user, password)\n    else\n        self.try(nil, \"authentication not supported\")\n    end\nend\n\n-- send message or throw an exception\nfunction metat.__index:send(mailt)\n    self:mail(mailt.from)\n    if type(mailt.rcpt) == \"table\" then\n        for i,v in ipairs(mailt.rcpt) do\n            self:rcpt(v)\n        end\n    else\n        self:rcpt(mailt.rcpt)\n    end\n    self:data(ltn12.source.chain(mailt.source, mime.stuff()), mailt.step)\nend\n\nfunction _M.open(server, port, create)\n    local tp = socket.try(tp.connect(server or _M.SERVER, port or _M.PORT,\n        _M.TIMEOUT, create))\n    local s = setmetatable({tp = tp}, metat)\n    -- make sure tp is closed if we get an exception\n    s.try = socket.newtry(function()\n        s:close()\n    end)\n    return s\nend\n\n-- convert headers to lowercase\nlocal function lower_headers(headers)\n    local lower = {}\n    for i,v in pairs(headers or lower) do\n        lower[string.lower(i)] = v\n    end\n    return lower\nend\n\n---------------------------------------------------------------------------\n-- Multipart message source\n-----------------------------------------------------------------------------\n-- returns a hopefully unique mime boundary\nlocal seqno = 0\nlocal function newboundary()\n    seqno = seqno + 1\n    return string.format('%s%05d==%05u', os.date('%d%m%Y%H%M%S'),\n        math.random(0, 99999), seqno)\nend\n\n-- send_message forward declaration\nlocal send_message\n\n-- yield the headers all at once, it's faster\nlocal function send_headers(tosend)\n    local canonic = headers.canonic\n    local h = \"\\r\\n\"\n    for f,v in pairs(tosend) do\n        h = (canonic[f] or f) .. ': ' .. v .. \"\\r\\n\" .. h\n    end\n    coroutine.yield(h)\nend\n\n-- yield multipart message body from a multipart message table\nlocal function send_multipart(mesgt)\n    -- make sure we have our boundary and send headers\n    local bd = newboundary()\n    local headers = lower_headers(mesgt.headers or {})\n    headers['content-type'] = headers['content-type'] or 'multipart/mixed'\n    headers['content-type'] = headers['content-type'] ..\n        '; boundary=\"' ..  bd .. '\"'\n    send_headers(headers)\n    -- send preamble\n    if mesgt.body.preamble then\n        coroutine.yield(mesgt.body.preamble)\n        coroutine.yield(\"\\r\\n\")\n    end\n    -- send each part separated by a boundary\n    for i, m in ipairs(mesgt.body) do\n        coroutine.yield(\"\\r\\n--\" .. bd .. \"\\r\\n\")\n        send_message(m)\n    end\n    -- send last boundary\n    coroutine.yield(\"\\r\\n--\" .. bd .. \"--\\r\\n\\r\\n\")\n    -- send epilogue\n    if mesgt.body.epilogue then\n        coroutine.yield(mesgt.body.epilogue)\n        coroutine.yield(\"\\r\\n\")\n    end\nend\n\n-- yield message body from a source\nlocal function send_source(mesgt)\n    -- make sure we have a content-type\n    local headers = lower_headers(mesgt.headers or {})\n    headers['content-type'] = headers['content-type'] or\n        'text/plain; charset=\"iso-8859-1\"'\n    send_headers(headers)\n    -- send body from source\n    while true do\n        local chunk, err = mesgt.body()\n        if err then coroutine.yield(nil, err)\n        elseif chunk then coroutine.yield(chunk)\n        else break end\n    end\nend\n\n-- yield message body from a string\nlocal function send_string(mesgt)\n    -- make sure we have a content-type\n    local headers = lower_headers(mesgt.headers or {})\n    headers['content-type'] = headers['content-type'] or\n        'text/plain; charset=\"iso-8859-1\"'\n    send_headers(headers)\n    -- send body from string\n    coroutine.yield(mesgt.body)\nend\n\n-- message source\nfunction send_message(mesgt)\n    if type(mesgt.body) == \"table\" then send_multipart(mesgt)\n    elseif type(mesgt.body) == \"function\" then send_source(mesgt)\n    else send_string(mesgt) end\nend\n\n-- set defaul headers\nlocal function adjust_headers(mesgt)\n    local lower = lower_headers(mesgt.headers)\n    lower[\"date\"] = lower[\"date\"] or\n        os.date(\"!%a, %d %b %Y %H:%M:%S \") .. (mesgt.zone or _M.ZONE)\n    lower[\"x-mailer\"] = lower[\"x-mailer\"] or socket._VERSION\n    -- this can't be overriden\n    lower[\"mime-version\"] = \"1.0\"\n    return lower\nend\n\nfunction _M.message(mesgt)\n    mesgt.headers = adjust_headers(mesgt)\n    -- create and return message source\n    local co = coroutine.create(function() send_message(mesgt) end)\n    return function()\n        local ret, a, b = coroutine.resume(co)\n        if ret then return a, b\n        else return nil, a end\n    end\nend\n\n---------------------------------------------------------------------------\n-- High level SMTP API\n-----------------------------------------------------------------------------\n_M.send = socket.protect(function(mailt)\n    local s = _M.open(mailt.server, mailt.port, mailt.create)\n    local ext = s:greet(mailt.domain)\n    s:auth(mailt.user, mailt.password, ext)\n    s:send(mailt)\n    s:quit()\n    return s:close()\nend)\n\nreturn _M\n"
  },
  {
    "path": "src/luasocket/socket.h",
    "content": "#ifndef SOCKET_H\n#define SOCKET_H\n/*=========================================================================*\\\n* Socket compatibilization module\n* LuaSocket toolkit\n*\n* BSD Sockets and WinSock are similar, but there are a few irritating\n* differences. Also, not all *nix platforms behave the same. This module\n* (and the associated usocket.h and wsocket.h) factor these differences and\n* creates a interface compatible with the io.h module.\n\\*=========================================================================*/\n#include \"io.h\"\n\n/*=========================================================================*\\\n* Platform specific compatibilization\n\\*=========================================================================*/\n#ifdef _WIN32\n#include \"wsocket.h\"\n#else\n#include \"usocket.h\"\n#endif\n\n/*=========================================================================*\\\n* The connect and accept functions accept a timeout and their\n* implementations are somewhat complicated. We chose to move\n* the timeout control into this module for these functions in\n* order to simplify the modules that use them. \n\\*=========================================================================*/\n#include \"timeout.h\"\n\n/* convenient shorthand */\ntypedef struct sockaddr SA;\n\n/*=========================================================================*\\\n* Functions bellow implement a comfortable platform independent \n* interface to sockets\n\\*=========================================================================*/\n\n#ifndef _WIN32\n#pragma GCC visibility push(hidden)\n#endif\n\nint socket_waitfd(p_socket ps, int sw, p_timeout tm);\nint socket_open(void);\nint socket_close(void);\nvoid socket_destroy(p_socket ps);\nint socket_select(t_socket n, fd_set *rfds, fd_set *wfds, fd_set *efds, p_timeout tm);\nint socket_create(p_socket ps, int domain, int type, int protocol);\nint socket_bind(p_socket ps, SA *addr, socklen_t addr_len); \nint socket_listen(p_socket ps, int backlog);\nvoid socket_shutdown(p_socket ps, int how); \nint socket_connect(p_socket ps, SA *addr, socklen_t addr_len, p_timeout tm); \nint socket_accept(p_socket ps, p_socket pa, SA *addr, socklen_t *addr_len, p_timeout tm);\nint socket_send(p_socket ps, const char *data, size_t count, size_t *sent, p_timeout tm);\nint socket_sendto(p_socket ps, const char *data, size_t count, size_t *sent, SA *addr, socklen_t addr_len, p_timeout tm);\nint socket_recv(p_socket ps, char *data, size_t count, size_t *got, p_timeout tm);\nint socket_recvfrom(p_socket ps, char *data, size_t count, size_t *got, SA *addr, socklen_t *addr_len, p_timeout tm);\nint socket_write(p_socket ps, const char *data, size_t count, size_t *sent, p_timeout tm);\nint socket_read(p_socket ps, char *data, size_t count, size_t *got, p_timeout tm);\nvoid socket_setblocking(p_socket ps);\nvoid socket_setnonblocking(p_socket ps);\nint socket_gethostbyaddr(const char *addr, socklen_t len, struct hostent **hp);\nint socket_gethostbyname(const char *addr, struct hostent **hp);\nconst char *socket_hoststrerror(int err);\nconst char *socket_strerror(int err);\nconst char *socket_ioerror(p_socket ps, int err);\nconst char *socket_gaistrerror(int err);\n\n#ifndef _WIN32\n#pragma GCC visibility pop\n#endif\n\n#endif /* SOCKET_H */\n"
  },
  {
    "path": "src/luasocket/socket.lua",
    "content": "-----------------------------------------------------------------------------\n-- LuaSocket helper module\n-- Author: Diego Nehab\n-----------------------------------------------------------------------------\n\nlocal _M = socket\n\n-----------------------------------------------------------------------------\n-- Exported auxiliar functions\n-----------------------------------------------------------------------------\nfunction _M.connect4(address, port, laddress, lport)\n    return socket.connect(address, port, laddress, lport, \"inet\")\nend\n\nfunction _M.connect6(address, port, laddress, lport)\n    return socket.connect(address, port, laddress, lport, \"inet6\")\nend\n\nfunction _M.bind(host, port, backlog)\n    if host == \"*\" then host = \"0.0.0.0\" end\n    local addrinfo, err = socket.dns.getaddrinfo(host);\n    if not addrinfo then return nil, err end\n    local sock, res\n    err = \"no info on address\"\n    for i, alt in ipairs(addrinfo) do\n        if alt.family == \"inet\" then\n            sock, err = socket.tcp4()\n        else\n            sock, err = socket.tcp6()\n        end\n        if not sock then return nil, err end\n        sock:setoption(\"reuseaddr\", true)\n        res, err = sock:bind(alt.addr, port)\n        if not res then\n            sock:close()\n        else\n            res, err = sock:listen(backlog)\n            if not res then\n                sock:close()\n            else\n                return sock\n            end\n        end\n    end\n    return nil, err\nend\n\n_M.try = _M.newtry()\n\nfunction _M.choose(table)\n    return function(name, opt1, opt2)\n        if type(name) ~= \"string\" then\n            name, opt1, opt2 = \"default\", name, opt1\n        end\n        local f = table[name or \"nil\"]\n        if not f then error(\"unknown key (\".. tostring(name) ..\")\", 3)\n        else return f(opt1, opt2) end\n    end\nend\n\n-----------------------------------------------------------------------------\n-- Socket sources and sinks, conforming to LTN12\n-----------------------------------------------------------------------------\n-- create namespaces inside LuaSocket namespace\nlocal sourcet, sinkt = {}, {}\n_M.sourcet = sourcet\n_M.sinkt = sinkt\n\n_M.BLOCKSIZE = 2048\n\nsinkt[\"close-when-done\"] = function(sock)\n    return setmetatable({\n        getfd = function() return sock:getfd() end,\n        dirty = function() return sock:dirty() end\n    }, {\n        __call = function(self, chunk, err)\n            if not chunk then\n                sock:close()\n                return 1\n            else return sock:send(chunk) end\n        end\n    })\nend\n\nsinkt[\"keep-open\"] = function(sock)\n    return setmetatable({\n        getfd = function() return sock:getfd() end,\n        dirty = function() return sock:dirty() end\n    }, {\n        __call = function(self, chunk, err)\n            if chunk then return sock:send(chunk)\n            else return 1 end\n        end\n    })\nend\n\nsinkt[\"default\"] = sinkt[\"keep-open\"]\n\n_M.sink = _M.choose(sinkt)\n\nsourcet[\"by-length\"] = function(sock, length)\n    return setmetatable({\n        getfd = function() return sock:getfd() end,\n        dirty = function() return sock:dirty() end\n    }, {\n        __call = function()\n            if length <= 0 then return nil end\n            local size = math.min(socket.BLOCKSIZE, length)\n            local chunk, err = sock:receive(size)\n            if err then return nil, err end\n            length = length - string.len(chunk)\n            return chunk\n        end\n    })\nend\n\nsourcet[\"until-closed\"] = function(sock)\n    local done\n    return setmetatable({\n        getfd = function() return sock:getfd() end,\n        dirty = function() return sock:dirty() end\n    }, {\n        __call = function()\n            if done then return nil end\n            local chunk, err, partial = sock:receive(socket.BLOCKSIZE)\n            if not err then return chunk\n            elseif err == \"closed\" then\n                sock:close()\n                done = 1\n                return partial\n            else return nil, err end\n        end\n    })\nend\n\n\nsourcet[\"default\"] = sourcet[\"until-closed\"]\n\n_M.source = _M.choose(sourcet)\n\nreturn _M\n"
  },
  {
    "path": "src/luasocket/tcp.c",
    "content": "/*=========================================================================*\\\n* TCP object\n* LuaSocket toolkit\n\\*=========================================================================*/\n#include \"luasocket.h\"\n\n#include \"auxiliar.h\"\n#include \"socket.h\"\n#include \"inet.h\"\n#include \"options.h\"\n#include \"tcp.h\"\n#include \"../teliva.h\"\n\n#include <string.h>\n\n/*=========================================================================*\\\n* Internal function prototypes\n\\*=========================================================================*/\nstatic int global_create(lua_State *L);\nstatic int global_create4(lua_State *L);\nstatic int global_create6(lua_State *L);\nstatic int global_connect(lua_State *L);\nstatic int meth_connect(lua_State *L);\nstatic int meth_listen(lua_State *L);\nstatic int meth_getfamily(lua_State *L);\nstatic int meth_bind(lua_State *L);\nstatic int meth_send(lua_State *L);\nstatic int meth_getstats(lua_State *L);\nstatic int meth_setstats(lua_State *L);\nstatic int meth_getsockname(lua_State *L);\nstatic int meth_getpeername(lua_State *L);\nstatic int meth_shutdown(lua_State *L);\nstatic int meth_receive(lua_State *L);\nstatic int meth_accept(lua_State *L);\nstatic int meth_close(lua_State *L);\nstatic int meth_getoption(lua_State *L);\nstatic int meth_setoption(lua_State *L);\nstatic int meth_gettimeout(lua_State *L);\nstatic int meth_settimeout(lua_State *L);\nstatic int meth_getfd(lua_State *L);\nstatic int meth_setfd(lua_State *L);\nstatic int meth_dirty(lua_State *L);\n\n/* tcp object methods */\nstatic luaL_Reg tcp_methods[] = {\n    {\"__gc\",        meth_close},\n    {\"__tostring\",  auxiliar_tostring},\n    {\"accept\",      meth_accept},\n    {\"bind\",        meth_bind},\n    {\"close\",       meth_close},\n    {\"connect\",     meth_connect},\n    {\"dirty\",       meth_dirty},\n    {\"getfamily\",   meth_getfamily},\n    {\"getfd\",       meth_getfd},\n    {\"getoption\",   meth_getoption},\n    {\"getpeername\", meth_getpeername},\n    {\"getsockname\", meth_getsockname},\n    {\"getstats\",    meth_getstats},\n    {\"setstats\",    meth_setstats},\n    {\"listen\",      meth_listen},\n    {\"receive\",     meth_receive},\n    {\"send\",        meth_send},\n    {\"setfd\",       meth_setfd},\n    {\"setoption\",   meth_setoption},\n    {\"setpeername\", meth_connect},\n    {\"setsockname\", meth_bind},\n    {\"settimeout\",  meth_settimeout},\n    {\"gettimeout\",  meth_gettimeout},\n    {\"shutdown\",    meth_shutdown},\n    {NULL,          NULL}\n};\n\n/* socket option handlers */\nstatic t_opt optget[] = {\n    {\"keepalive\",   opt_get_keepalive},\n    {\"reuseaddr\",   opt_get_reuseaddr},\n    {\"reuseport\",   opt_get_reuseport},\n    {\"tcp-nodelay\", opt_get_tcp_nodelay},\n#ifdef TCP_KEEPIDLE\n    {\"tcp-keepidle\", opt_get_tcp_keepidle},\n#endif\n#ifdef TCP_KEEPCNT\n    {\"tcp-keepcnt\", opt_get_tcp_keepcnt},\n#endif\n#ifdef TCP_KEEPINTVL\n    {\"tcp-keepintvl\", opt_get_tcp_keepintvl},\n#endif\n    {\"linger\",      opt_get_linger},\n    {\"error\",       opt_get_error},\n\t{\"recv-buffer-size\",     opt_get_recv_buf_size},\n\t{\"send-buffer-size\",     opt_get_send_buf_size},\n    {NULL,          NULL}\n};\n\nstatic t_opt optset[] = {\n    {\"keepalive\",   opt_set_keepalive},\n    {\"reuseaddr\",   opt_set_reuseaddr},\n    {\"reuseport\",   opt_set_reuseport},\n    {\"tcp-nodelay\", opt_set_tcp_nodelay},\n#ifdef TCP_KEEPIDLE\n    {\"tcp-keepidle\", opt_set_tcp_keepidle},\n#endif\n#ifdef TCP_KEEPCNT\n    {\"tcp-keepcnt\", opt_set_tcp_keepcnt},\n#endif\n#ifdef TCP_KEEPINTVL\n    {\"tcp-keepintvl\", opt_set_tcp_keepintvl},\n#endif\n    {\"ipv6-v6only\", opt_set_ip6_v6only},\n    {\"linger\",      opt_set_linger},\n\t{\"recv-buffer-size\",     opt_set_recv_buf_size},\n\t{\"send-buffer-size\",     opt_set_send_buf_size},\n    {NULL,          NULL}\n};\n\n/* functions in library namespace */\nstatic luaL_Reg func[] = {\n    {\"tcp\", global_create},\n    {\"tcp4\", global_create4},\n    {\"tcp6\", global_create6},\n    {\"connect\", global_connect},\n    {NULL, NULL}\n};\n\n/*-------------------------------------------------------------------------*\\\n* Initializes module\n\\*-------------------------------------------------------------------------*/\nint tcp_open(lua_State *L)\n{\n    /* create classes */\n    auxiliar_newclass(L, \"tcp{master}\", tcp_methods);\n    auxiliar_newclass(L, \"tcp{client}\", tcp_methods);\n    auxiliar_newclass(L, \"tcp{server}\", tcp_methods);\n    /* create class groups */\n    auxiliar_add2group(L, \"tcp{master}\", \"tcp{any}\");\n    auxiliar_add2group(L, \"tcp{client}\", \"tcp{any}\");\n    auxiliar_add2group(L, \"tcp{server}\", \"tcp{any}\");\n    /* define library functions */\n    luaL_setfuncs(L, func, 0);\n    return 0;\n}\n\n/*=========================================================================*\\\n* Lua methods\n\\*=========================================================================*/\n/*-------------------------------------------------------------------------*\\\n* Just call buffered IO methods\n\\*-------------------------------------------------------------------------*/\nstatic int meth_send(lua_State *L) {\n    p_tcp tcp = (p_tcp) auxiliar_checkclass(L, \"tcp{client}\", 1);\n    return buffer_meth_send(L, &tcp->buf);\n}\n\nstatic int meth_receive(lua_State *L) {\n    p_tcp tcp = (p_tcp) auxiliar_checkclass(L, \"tcp{client}\", 1);\n    return buffer_meth_receive(L, &tcp->buf);\n}\n\nstatic int meth_getstats(lua_State *L) {\n    p_tcp tcp = (p_tcp) auxiliar_checkclass(L, \"tcp{client}\", 1);\n    return buffer_meth_getstats(L, &tcp->buf);\n}\n\nstatic int meth_setstats(lua_State *L) {\n    p_tcp tcp = (p_tcp) auxiliar_checkclass(L, \"tcp{client}\", 1);\n    return buffer_meth_setstats(L, &tcp->buf);\n}\n\n/*-------------------------------------------------------------------------*\\\n* Just call option handler\n\\*-------------------------------------------------------------------------*/\nstatic int meth_getoption(lua_State *L)\n{\n    p_tcp tcp = (p_tcp) auxiliar_checkgroup(L, \"tcp{any}\", 1);\n    return opt_meth_getoption(L, optget, &tcp->sock);\n}\n\nstatic int meth_setoption(lua_State *L)\n{\n    p_tcp tcp = (p_tcp) auxiliar_checkgroup(L, \"tcp{any}\", 1);\n    return opt_meth_setoption(L, optset, &tcp->sock);\n}\n\n/*-------------------------------------------------------------------------*\\\n* Select support methods\n\\*-------------------------------------------------------------------------*/\nstatic int meth_getfd(lua_State *L)\n{\n    p_tcp tcp = (p_tcp) auxiliar_checkgroup(L, \"tcp{any}\", 1);\n    lua_pushnumber(L, (int) tcp->sock);\n    return 1;\n}\n\n/* this is very dangerous, but can be handy for those that are brave enough */\nstatic int meth_setfd(lua_State *L)\n{\n    p_tcp tcp = (p_tcp) auxiliar_checkgroup(L, \"tcp{any}\", 1);\n    tcp->sock = (t_socket) luaL_checknumber(L, 2);\n    return 0;\n}\n\nstatic int meth_dirty(lua_State *L)\n{\n    p_tcp tcp = (p_tcp) auxiliar_checkgroup(L, \"tcp{any}\", 1);\n    lua_pushboolean(L, !buffer_isempty(&tcp->buf));\n    return 1;\n}\n\n/*-------------------------------------------------------------------------*\\\n* Waits for and returns a client object attempting connection to the\n* server object\n\\*-------------------------------------------------------------------------*/\nstatic int meth_accept(lua_State *L)\n{\n    p_tcp server = (p_tcp) auxiliar_checkclass(L, \"tcp{server}\", 1);\n    p_timeout tm = timeout_markstart(&server->tm);\n    t_socket sock;\n    append_to_audit_log(L,  \"socket.accept()\");\n    const char *err = inet_tryaccept(&server->sock, server->family, &sock, tm);\n    /* if successful, push client socket */\n    if (err == NULL) {\n        p_tcp clnt = (p_tcp) lua_newuserdata(L, sizeof(t_tcp));\n        auxiliar_setclass(L, \"tcp{client}\", -1);\n        /* initialize structure fields */\n        memset(clnt, 0, sizeof(t_tcp));\n        socket_setnonblocking(&sock);\n        clnt->sock = sock;\n        io_init(&clnt->io, (p_send) socket_send, (p_recv) socket_recv,\n                (p_error) socket_ioerror, &clnt->sock);\n        timeout_init(&clnt->tm, -1, -1);\n        buffer_init(&clnt->buf, &clnt->io, &clnt->tm);\n        clnt->family = server->family;\n        return 1;\n    } else {\n        lua_pushnil(L);\n        lua_pushstring(L, err);\n        return 2;\n    }\n}\n\n/*-------------------------------------------------------------------------*\\\n* Binds an object to an address\n\\*-------------------------------------------------------------------------*/\nstatic int meth_bind(lua_State *L) {\n    p_tcp tcp = (p_tcp) auxiliar_checkclass(L, \"tcp{master}\", 1);\n    const char *address =  luaL_checkstring(L, 2);\n    const char *port = luaL_checkstring(L, 3);\n    static char buffer[1024] = {0};\n    memset(buffer, '\\0', 1024);\n    snprintf(buffer, 1020, \"socket.bind(\\\"%s\\\", %s)\", address, port);\n    append_to_audit_log(L, buffer);\n    const char *err;\n    struct addrinfo bindhints;\n    memset(&bindhints, 0, sizeof(bindhints));\n    bindhints.ai_socktype = SOCK_STREAM;\n    bindhints.ai_family = tcp->family;\n    bindhints.ai_flags = AI_PASSIVE;\n    err = inet_trybind(&tcp->sock, &tcp->family, address, port, &bindhints);\n    if (err) {\n        lua_pushnil(L);\n        lua_pushstring(L, err);\n        return 2;\n    }\n    lua_pushnumber(L, 1);\n    return 1;\n}\n\n/*-------------------------------------------------------------------------*\\\n* Turns a master tcp object into a client object.\n\\*-------------------------------------------------------------------------*/\nstatic int meth_connect(lua_State *L) {\n    p_tcp tcp = (p_tcp) auxiliar_checkgroup(L, \"tcp{any}\", 1);\n    const char *address =  luaL_checkstring(L, 2);\n    const char *port = luaL_checkstring(L, 3);\n    struct addrinfo connecthints;\n    const char *err;\n    static char buffer[1024] = {0};\n    memset(buffer, '\\0', 1024);\n    snprintf(buffer, 1020, \"socket.connect(\\\"%s\\\", %s)\", address, port);\n    append_to_audit_log(L, buffer);\n    memset(&connecthints, 0, sizeof(connecthints));\n    connecthints.ai_socktype = SOCK_STREAM;\n    /* make sure we try to connect only to the same family */\n    connecthints.ai_family = tcp->family;\n    timeout_markstart(&tcp->tm);\n    err = inet_tryconnect(&tcp->sock, &tcp->family, address, port,\n        &tcp->tm, &connecthints);\n    /* have to set the class even if it failed due to non-blocking connects */\n    auxiliar_setclass(L, \"tcp{client}\", 1);\n    if (err) {\n        lua_pushnil(L);\n        lua_pushstring(L, err);\n        return 2;\n    }\n    lua_pushnumber(L, 1);\n    return 1;\n}\n\n/*-------------------------------------------------------------------------*\\\n* Closes socket used by object\n\\*-------------------------------------------------------------------------*/\nstatic int meth_close(lua_State *L)\n{\n    p_tcp tcp = (p_tcp) auxiliar_checkgroup(L, \"tcp{any}\", 1);\n    socket_destroy(&tcp->sock);\n    lua_pushnumber(L, 1);\n    return 1;\n}\n\n/*-------------------------------------------------------------------------*\\\n* Returns family as string\n\\*-------------------------------------------------------------------------*/\nstatic int meth_getfamily(lua_State *L)\n{\n    p_tcp tcp = (p_tcp) auxiliar_checkgroup(L, \"tcp{any}\", 1);\n    if (tcp->family == AF_INET6) {\n        lua_pushliteral(L, \"inet6\");\n        return 1;\n    } else if (tcp->family == AF_INET) {\n        lua_pushliteral(L, \"inet4\");\n        return 1;\n    } else {\n        lua_pushliteral(L, \"inet4\");\n        return 1;\n    }\n}\n\n/*-------------------------------------------------------------------------*\\\n* Puts the sockt in listen mode\n\\*-------------------------------------------------------------------------*/\nstatic int meth_listen(lua_State *L)\n{\n    append_to_audit_log(L,  \"socket.listen()\");\n    p_tcp tcp = (p_tcp) auxiliar_checkclass(L, \"tcp{master}\", 1);\n    int backlog = (int) luaL_optnumber(L, 2, 32);\n    int err = socket_listen(&tcp->sock, backlog);\n    if (err != IO_DONE) {\n        lua_pushnil(L);\n        lua_pushstring(L, socket_strerror(err));\n        return 2;\n    }\n    /* turn master object into a server object */\n    auxiliar_setclass(L, \"tcp{server}\", 1);\n    lua_pushnumber(L, 1);\n    return 1;\n}\n\n/*-------------------------------------------------------------------------*\\\n* Shuts the connection down partially\n\\*-------------------------------------------------------------------------*/\nstatic int meth_shutdown(lua_State *L)\n{\n    /* SHUT_RD,  SHUT_WR,  SHUT_RDWR  have  the value 0, 1, 2, so we can use method index directly */\n    static const char* methods[] = { \"receive\", \"send\", \"both\", NULL };\n    p_tcp tcp = (p_tcp) auxiliar_checkclass(L, \"tcp{client}\", 1);\n    int how = luaL_checkoption(L, 2, \"both\", methods);\n    socket_shutdown(&tcp->sock, how);\n    lua_pushnumber(L, 1);\n    return 1;\n}\n\n/*-------------------------------------------------------------------------*\\\n* Just call inet methods\n\\*-------------------------------------------------------------------------*/\nstatic int meth_getpeername(lua_State *L)\n{\n    append_to_audit_log(L,  \"socket.getpeername()\");\n    p_tcp tcp = (p_tcp) auxiliar_checkgroup(L, \"tcp{any}\", 1);\n    return inet_meth_getpeername(L, &tcp->sock, tcp->family);\n}\n\nstatic int meth_getsockname(lua_State *L)\n{\n    append_to_audit_log(L,  \"socket.getsockname()\");\n    p_tcp tcp = (p_tcp) auxiliar_checkgroup(L, \"tcp{any}\", 1);\n    return inet_meth_getsockname(L, &tcp->sock, tcp->family);\n}\n\n/*-------------------------------------------------------------------------*\\\n* Just call tm methods\n\\*-------------------------------------------------------------------------*/\nstatic int meth_settimeout(lua_State *L)\n{\n    p_tcp tcp = (p_tcp) auxiliar_checkgroup(L, \"tcp{any}\", 1);\n    return timeout_meth_settimeout(L, &tcp->tm);\n}\n\nstatic int meth_gettimeout(lua_State *L)\n{\n    p_tcp tcp = (p_tcp) auxiliar_checkgroup(L, \"tcp{any}\", 1);\n    return timeout_meth_gettimeout(L, &tcp->tm);\n}\n\n/*=========================================================================*\\\n* Library functions\n\\*=========================================================================*/\n/*-------------------------------------------------------------------------*\\\n* Creates a master tcp object\n\\*-------------------------------------------------------------------------*/\nstatic int tcp_create(lua_State *L, int family) {\n    p_tcp tcp = (p_tcp) lua_newuserdata(L, sizeof(t_tcp));\n    memset(tcp, 0, sizeof(t_tcp));\n    /* set its type as master object */\n    auxiliar_setclass(L, \"tcp{master}\", -1);\n    /* if family is AF_UNSPEC, we leave the socket invalid and\n     * store AF_UNSPEC into family. This will allow it to later be\n     * replaced with an AF_INET6 or AF_INET socket upon first use. */\n    tcp->sock = SOCKET_INVALID;\n    tcp->family = family;\n    io_init(&tcp->io, (p_send) socket_send, (p_recv) socket_recv,\n            (p_error) socket_ioerror, &tcp->sock);\n    timeout_init(&tcp->tm, -1, -1);\n    buffer_init(&tcp->buf, &tcp->io, &tcp->tm);\n    if (family != AF_UNSPEC) {\n        const char *err = inet_trycreate(&tcp->sock, family, SOCK_STREAM, 0);\n        if (err != NULL) {\n            lua_pushnil(L);\n            lua_pushstring(L, err);\n            return 2;\n        }\n        socket_setnonblocking(&tcp->sock);\n    }\n    return 1;\n}\n\nstatic int global_create(lua_State *L) {\n    return tcp_create(L, AF_UNSPEC);\n}\n\nstatic int global_create4(lua_State *L) {\n    return tcp_create(L, AF_INET);\n}\n\nstatic int global_create6(lua_State *L) {\n    return tcp_create(L, AF_INET6);\n}\n\nstatic int global_connect(lua_State *L) {\n    const char *remoteaddr = luaL_checkstring(L, 1);\n    const char *remoteserv = luaL_checkstring(L, 2);\n    const char *localaddr  = luaL_optstring(L, 3, NULL);\n    const char *localserv  = luaL_optstring(L, 4, \"0\");\n    int family = inet_optfamily(L, 5, \"unspec\");\n    p_tcp tcp = (p_tcp) lua_newuserdata(L, sizeof(t_tcp));\n    struct addrinfo bindhints, connecthints;\n    const char *err = NULL;\n    /* initialize tcp structure */\n    memset(tcp, 0, sizeof(t_tcp));\n    io_init(&tcp->io, (p_send) socket_send, (p_recv) socket_recv,\n            (p_error) socket_ioerror, &tcp->sock);\n    timeout_init(&tcp->tm, -1, -1);\n    buffer_init(&tcp->buf, &tcp->io, &tcp->tm);\n    tcp->sock = SOCKET_INVALID;\n    tcp->family = AF_UNSPEC;\n    /* allow user to pick local address and port */\n    memset(&bindhints, 0, sizeof(bindhints));\n    bindhints.ai_socktype = SOCK_STREAM;\n    bindhints.ai_family = family;\n    bindhints.ai_flags = AI_PASSIVE;\n    if (localaddr) {\n        err = inet_trybind(&tcp->sock, &tcp->family, localaddr,\n            localserv, &bindhints);\n        if (err) {\n            lua_pushnil(L);\n            lua_pushstring(L, err);\n            return 2;\n        }\n    }\n    /* try to connect to remote address and port */\n    memset(&connecthints, 0, sizeof(connecthints));\n    connecthints.ai_socktype = SOCK_STREAM;\n    /* make sure we try to connect only to the same family */\n    connecthints.ai_family = tcp->family;\n    err = inet_tryconnect(&tcp->sock, &tcp->family, remoteaddr, remoteserv,\n         &tcp->tm, &connecthints);\n    if (err) {\n        socket_destroy(&tcp->sock);\n        lua_pushnil(L);\n        lua_pushstring(L, err);\n        return 2;\n    }\n    auxiliar_setclass(L, \"tcp{client}\", -1);\n    return 1;\n}\n"
  },
  {
    "path": "src/luasocket/tcp.h",
    "content": "#ifndef TCP_H\n#define TCP_H\n/*=========================================================================*\\\n* TCP object\n* LuaSocket toolkit\n*\n* The tcp.h module is basicly a glue that puts together modules buffer.h,\n* timeout.h socket.h and inet.h to provide the LuaSocket TCP (AF_INET,\n* SOCK_STREAM) support.\n*\n* Three classes are defined: master, client and server. The master class is\n* a newly created tcp object, that has not been bound or connected. Server\n* objects are tcp objects bound to some local address. Client objects are\n* tcp objects either connected to some address or returned by the accept\n* method of a server object.\n\\*=========================================================================*/\n#include \"luasocket.h\"\n\n#include \"buffer.h\"\n#include \"timeout.h\"\n#include \"socket.h\"\n\ntypedef struct t_tcp_ {\n    t_socket sock;\n    t_io io;\n    t_buffer buf;\n    t_timeout tm;\n    int family;\n} t_tcp;\n\ntypedef t_tcp *p_tcp;\n\n#ifndef _WIN32\n#pragma GCC visibility push(hidden)\n#endif\n\nint tcp_open(lua_State *L);\n\n#ifndef _WIN32\n#pragma GCC visibility pop\n#endif\n\n#endif /* TCP_H */\n"
  },
  {
    "path": "src/luasocket/timeout.c",
    "content": "/*=========================================================================*\\\n* Timeout management functions\n* LuaSocket toolkit\n\\*=========================================================================*/\n#include \"luasocket.h\"\n\n#include \"auxiliar.h\"\n#include \"timeout.h\"\n\n#include <stdio.h>\n#include <limits.h>\n#include <float.h>\n\n#ifdef _WIN32\n#include <windows.h>\n#else\n#include <time.h>\n#include <sys/time.h>\n#endif\n\n/* min and max macros */\n#ifndef MIN\n#define MIN(x, y) ((x) < (y) ? x : y)\n#endif\n#ifndef MAX\n#define MAX(x, y) ((x) > (y) ? x : y)\n#endif\n\n/*=========================================================================*\\\n* Internal function prototypes\n\\*=========================================================================*/\nstatic int timeout_lua_gettime(lua_State *L);\nstatic int timeout_lua_sleep(lua_State *L);\n\nstatic luaL_Reg func[] = {\n    { \"gettime\", timeout_lua_gettime },\n    { \"sleep\", timeout_lua_sleep },\n    { NULL, NULL }\n};\n\n/*=========================================================================*\\\n* Exported functions.\n\\*=========================================================================*/\n/*-------------------------------------------------------------------------*\\\n* Initialize structure\n\\*-------------------------------------------------------------------------*/\nvoid timeout_init(p_timeout tm, double block, double total) {\n    tm->block = block;\n    tm->total = total;\n}\n\n/*-------------------------------------------------------------------------*\\\n* Determines how much time we have left for the next system call,\n* if the previous call was successful\n* Input\n*   tm: timeout control structure\n* Returns\n*   the number of ms left or -1 if there is no time limit\n\\*-------------------------------------------------------------------------*/\ndouble timeout_get(p_timeout tm) {\n    if (tm->block < 0.0 && tm->total < 0.0) {\n        return -1;\n    } else if (tm->block < 0.0) {\n        double t = tm->total - timeout_gettime() + tm->start;\n        return MAX(t, 0.0);\n    } else if (tm->total < 0.0) {\n        return tm->block;\n    } else {\n        double t = tm->total - timeout_gettime() + tm->start;\n        return MIN(tm->block, MAX(t, 0.0));\n    }\n}\n\n/*-------------------------------------------------------------------------*\\\n* Returns time since start of operation\n* Input\n*   tm: timeout control structure\n* Returns\n*   start field of structure\n\\*-------------------------------------------------------------------------*/\ndouble timeout_getstart(p_timeout tm) {\n    return tm->start;\n}\n\n/*-------------------------------------------------------------------------*\\\n* Determines how much time we have left for the next system call,\n* if the previous call was a failure\n* Input\n*   tm: timeout control structure\n* Returns\n*   the number of ms left or -1 if there is no time limit\n\\*-------------------------------------------------------------------------*/\ndouble timeout_getretry(p_timeout tm) {\n    if (tm->block < 0.0 && tm->total < 0.0) {\n        return -1;\n    } else if (tm->block < 0.0) {\n        double t = tm->total - timeout_gettime() + tm->start;\n        return MAX(t, 0.0);\n    } else if (tm->total < 0.0) {\n        double t = tm->block - timeout_gettime() + tm->start;\n        return MAX(t, 0.0);\n    } else {\n        double t = tm->total - timeout_gettime() + tm->start;\n        return MIN(tm->block, MAX(t, 0.0));\n    }\n}\n\n/*-------------------------------------------------------------------------*\\\n* Marks the operation start time in structure\n* Input\n*   tm: timeout control structure\n\\*-------------------------------------------------------------------------*/\np_timeout timeout_markstart(p_timeout tm) {\n    tm->start = timeout_gettime();\n    return tm;\n}\n\n/*-------------------------------------------------------------------------*\\\n* Gets time in s, relative to January 1, 1970 (UTC)\n* Returns\n*   time in s.\n\\*-------------------------------------------------------------------------*/\n#ifdef _WIN32\ndouble timeout_gettime(void) {\n    FILETIME ft;\n    double t;\n    GetSystemTimeAsFileTime(&ft);\n    /* Windows file time (time since January 1, 1601 (UTC)) */\n    t  = ft.dwLowDateTime/1.0e7 + ft.dwHighDateTime*(4294967296.0/1.0e7);\n    /* convert to Unix Epoch time (time since January 1, 1970 (UTC)) */\n    return (t - 11644473600.0);\n}\n#else\ndouble timeout_gettime(void) {\n    struct timeval v;\n    gettimeofday(&v, (struct timezone *) NULL);\n    /* Unix Epoch time (time since January 1, 1970 (UTC)) */\n    return v.tv_sec + v.tv_usec/1.0e6;\n}\n#endif\n\n/*-------------------------------------------------------------------------*\\\n* Initializes module\n\\*-------------------------------------------------------------------------*/\nint timeout_open(lua_State *L) {\n    luaL_setfuncs(L, func, 0);\n    return 0;\n}\n\n/*-------------------------------------------------------------------------*\\\n* Sets timeout values for IO operations\n* Lua Input: base, time [, mode]\n*   time: time out value in seconds\n*   mode: \"b\" for block timeout, \"t\" for total timeout. (default: b)\n\\*-------------------------------------------------------------------------*/\nint timeout_meth_settimeout(lua_State *L, p_timeout tm) {\n    double t = luaL_optnumber(L, 2, -1);\n    const char *mode = luaL_optstring(L, 3, \"b\");\n    switch (*mode) {\n        case 'b':\n            tm->block = t;\n            break;\n        case 'r': case 't':\n            tm->total = t;\n            break;\n        default:\n            luaL_argcheck(L, 0, 3, \"invalid timeout mode\");\n            break;\n    }\n    lua_pushnumber(L, 1);\n    return 1;\n}\n\n/*-------------------------------------------------------------------------*\\\n* Gets timeout values for IO operations\n* Lua Output: block, total\n\\*-------------------------------------------------------------------------*/\nint timeout_meth_gettimeout(lua_State *L, p_timeout tm) {\n    lua_pushnumber(L, tm->block);\n    lua_pushnumber(L, tm->total);\n    return 2;\n}\n\n/*=========================================================================*\\\n* Test support functions\n\\*=========================================================================*/\n/*-------------------------------------------------------------------------*\\\n* Returns the time the system has been up, in secconds.\n\\*-------------------------------------------------------------------------*/\nstatic int timeout_lua_gettime(lua_State *L)\n{\n    lua_pushnumber(L, timeout_gettime());\n    return 1;\n}\n\n/*-------------------------------------------------------------------------*\\\n* Sleep for n seconds.\n\\*-------------------------------------------------------------------------*/\n#ifdef _WIN32\nint timeout_lua_sleep(lua_State *L)\n{\n    double n = luaL_checknumber(L, 1);\n    if (n < 0.0) n = 0.0;\n    if (n < DBL_MAX/1000.0) n *= 1000.0;\n    if (n > INT_MAX) n = INT_MAX;\n    Sleep((int)n);\n    return 0;\n}\n#else\nint timeout_lua_sleep(lua_State *L)\n{\n    double n = luaL_checknumber(L, 1);\n    struct timespec t, r;\n    if (n < 0.0) n = 0.0;\n    if (n > INT_MAX) n = INT_MAX;\n    t.tv_sec = (int) n;\n    n -= t.tv_sec;\n    t.tv_nsec = (int) (n * 1000000000);\n    if (t.tv_nsec >= 1000000000) t.tv_nsec = 999999999;\n    while (nanosleep(&t, &r) != 0) {\n        t.tv_sec = r.tv_sec;\n        t.tv_nsec = r.tv_nsec;\n    }\n    return 0;\n}\n#endif\n"
  },
  {
    "path": "src/luasocket/timeout.h",
    "content": "#ifndef TIMEOUT_H\n#define TIMEOUT_H\n/*=========================================================================*\\\n* Timeout management functions\n* LuaSocket toolkit\n\\*=========================================================================*/\n#include \"luasocket.h\"\n\n/* timeout control structure */\ntypedef struct t_timeout_ {\n    double block;          /* maximum time for blocking calls */\n    double total;          /* total number of miliseconds for operation */\n    double start;          /* time of start of operation */\n} t_timeout;\ntypedef t_timeout *p_timeout;\n\n#ifndef _WIN32\n#pragma GCC visibility push(hidden)\n#endif\n\nvoid timeout_init(p_timeout tm, double block, double total);\ndouble timeout_get(p_timeout tm);\ndouble timeout_getstart(p_timeout tm);\ndouble timeout_getretry(p_timeout tm);\np_timeout timeout_markstart(p_timeout tm);\n\ndouble timeout_gettime(void);\n\nint timeout_open(lua_State *L);\n\nint timeout_meth_settimeout(lua_State *L, p_timeout tm);\nint timeout_meth_gettimeout(lua_State *L, p_timeout tm);\n\n#ifndef _WIN32\n#pragma GCC visibility pop\n#endif\n\n#define timeout_iszero(tm)   ((tm)->block == 0.0)\n\n#endif /* TIMEOUT_H */\n"
  },
  {
    "path": "src/luasocket/tp.lua",
    "content": "-----------------------------------------------------------------------------\n-- Unified SMTP/FTP subsystem\n-- LuaSocket toolkit.\n-- Author: Diego Nehab\n-----------------------------------------------------------------------------\n\n-----------------------------------------------------------------------------\n-- Declare module and import dependencies\n-----------------------------------------------------------------------------\nsocket.tp = {}\nlocal _M = socket.tp\n\n-----------------------------------------------------------------------------\n-- Program constants\n-----------------------------------------------------------------------------\n_M.TIMEOUT = 60\n\n-----------------------------------------------------------------------------\n-- Implementation\n-----------------------------------------------------------------------------\n-- gets server reply (works for SMTP and FTP)\nlocal function get_reply(c)\n    local code, current, sep\n    local line, err = c:receive()\n    local reply = line\n    if err then return nil, err end\n    code, sep = socket.skip(2, string.find(line, \"^(%d%d%d)(.?)\"))\n    if not code then return nil, \"invalid server reply\" end\n    if sep == \"-\" then -- reply is multiline\n        repeat\n            line, err = c:receive()\n            if err then return nil, err end\n            current, sep = socket.skip(2, string.find(line, \"^(%d%d%d)(.?)\"))\n            reply = reply .. \"\\n\" .. line\n        -- reply ends with same code\n        until code == current and sep == \" \"\n    end\n    return code, reply\nend\n\n-- metatable for sock object\nlocal metat = { __index = {} }\n\nfunction metat.__index:getpeername()\n    return self.c:getpeername()\nend\n\nfunction metat.__index:getsockname()\n    return self.c:getpeername()\nend\n\nfunction metat.__index:check(ok)\n    local code, reply = get_reply(self.c)\n    if not code then return nil, reply end\n    if type(ok) ~= \"function\" then\n        if type(ok) == \"table\" then\n            for i, v in ipairs(ok) do\n                if string.find(code, v) then\n                    return tonumber(code), reply\n                end\n            end\n            return nil, reply\n        else\n            if string.find(code, ok) then return tonumber(code), reply\n            else return nil, reply end\n        end\n    else return ok(tonumber(code), reply) end\nend\n\nfunction metat.__index:command(cmd, arg)\n    cmd = string.upper(cmd)\n    if arg then\n        return self.c:send(cmd .. \" \" .. arg.. \"\\r\\n\")\n    else\n        return self.c:send(cmd .. \"\\r\\n\")\n    end\nend\n\nfunction metat.__index:sink(snk, pat)\n    local chunk, err = self.c:receive(pat)\n    return snk(chunk, err)\nend\n\nfunction metat.__index:send(data)\n    return self.c:send(data)\nend\n\nfunction metat.__index:receive(pat)\n    return self.c:receive(pat)\nend\n\nfunction metat.__index:getfd()\n    return self.c:getfd()\nend\n\nfunction metat.__index:dirty()\n    return self.c:dirty()\nend\n\nfunction metat.__index:getcontrol()\n    return self.c\nend\n\nfunction metat.__index:source(source, step)\n    local sink = socket.sink(\"keep-open\", self.c)\n    local ret, err = ltn12.pump.all(source, sink, step or ltn12.pump.step)\n    return ret, err\nend\n\n-- closes the underlying c\nfunction metat.__index:close()\n    self.c:close()\n    return 1\nend\n\n-- connect with server and return c object\nfunction _M.connect(host, port, timeout, create)\n    local c, e = (create or socket.tcp)()\n    if not c then return nil, e end\n    c:settimeout(timeout or _M.TIMEOUT)\n    local r, e = c:connect(host, port)\n    if not r then\n        c:close()\n        return nil, e\n    end\n    return setmetatable({c = c}, metat)\nend\n\nreturn _M\n"
  },
  {
    "path": "src/luasocket/udp.c",
    "content": "/*=========================================================================*\\\n* UDP object\n* LuaSocket toolkit\n\\*=========================================================================*/\n#include \"luasocket.h\"\n\n#include \"auxiliar.h\"\n#include \"socket.h\"\n#include \"inet.h\"\n#include \"options.h\"\n#include \"udp.h\"\n\n#include <string.h>\n#include <stdlib.h>\n\n/* min and max macros */\n#ifndef MIN\n#define MIN(x, y) ((x) < (y) ? x : y)\n#endif\n#ifndef MAX\n#define MAX(x, y) ((x) > (y) ? x : y)\n#endif\n\n/*=========================================================================*\\\n* Internal function prototypes\n\\*=========================================================================*/\nstatic int global_create(lua_State *L);\nstatic int global_create4(lua_State *L);\nstatic int global_create6(lua_State *L);\nstatic int meth_send(lua_State *L);\nstatic int meth_sendto(lua_State *L);\nstatic int meth_receive(lua_State *L);\nstatic int meth_receivefrom(lua_State *L);\nstatic int meth_getfamily(lua_State *L);\nstatic int meth_getsockname(lua_State *L);\nstatic int meth_getpeername(lua_State *L);\nstatic int meth_gettimeout(lua_State *L);\nstatic int meth_setsockname(lua_State *L);\nstatic int meth_setpeername(lua_State *L);\nstatic int meth_close(lua_State *L);\nstatic int meth_setoption(lua_State *L);\nstatic int meth_getoption(lua_State *L);\nstatic int meth_settimeout(lua_State *L);\nstatic int meth_getfd(lua_State *L);\nstatic int meth_setfd(lua_State *L);\nstatic int meth_dirty(lua_State *L);\n\n/* udp object methods */\nstatic luaL_Reg udp_methods[] = {\n    {\"__gc\",        meth_close},\n    {\"__tostring\",  auxiliar_tostring},\n    {\"close\",       meth_close},\n    {\"dirty\",       meth_dirty},\n    {\"getfamily\",   meth_getfamily},\n    {\"getfd\",       meth_getfd},\n    {\"getpeername\", meth_getpeername},\n    {\"getsockname\", meth_getsockname},\n    {\"receive\",     meth_receive},\n    {\"receivefrom\", meth_receivefrom},\n    {\"send\",        meth_send},\n    {\"sendto\",      meth_sendto},\n    {\"setfd\",       meth_setfd},\n    {\"setoption\",   meth_setoption},\n    {\"getoption\",   meth_getoption},\n    {\"setpeername\", meth_setpeername},\n    {\"setsockname\", meth_setsockname},\n    {\"settimeout\",  meth_settimeout},\n    {\"gettimeout\",  meth_gettimeout},\n    {NULL,          NULL}\n};\n\n/* socket options for setoption */\nstatic t_opt optset[] = {\n    {\"dontroute\",            opt_set_dontroute},\n    {\"broadcast\",            opt_set_broadcast},\n    {\"reuseaddr\",            opt_set_reuseaddr},\n    {\"reuseport\",            opt_set_reuseport},\n    {\"ip-multicast-if\",      opt_set_ip_multicast_if},\n    {\"ip-multicast-ttl\",     opt_set_ip_multicast_ttl},\n    {\"ip-multicast-loop\",    opt_set_ip_multicast_loop},\n    {\"ip-add-membership\",    opt_set_ip_add_membership},\n    {\"ip-drop-membership\",   opt_set_ip_drop_membersip},\n    {\"ipv6-unicast-hops\",    opt_set_ip6_unicast_hops},\n    {\"ipv6-multicast-hops\",  opt_set_ip6_unicast_hops},\n    {\"ipv6-multicast-loop\",  opt_set_ip6_multicast_loop},\n    {\"ipv6-add-membership\",  opt_set_ip6_add_membership},\n    {\"ipv6-drop-membership\", opt_set_ip6_drop_membersip},\n    {\"ipv6-v6only\",          opt_set_ip6_v6only},\n\t{\"recv-buffer-size\",     opt_set_recv_buf_size},\n\t{\"send-buffer-size\",     opt_set_send_buf_size},\n    {NULL,                   NULL}\n};\n\n/* socket options for getoption */\nstatic t_opt optget[] = {\n    {\"dontroute\",            opt_get_dontroute},\n    {\"broadcast\",            opt_get_broadcast},\n    {\"reuseaddr\",            opt_get_reuseaddr},\n    {\"reuseport\",            opt_get_reuseport},\n    {\"ip-multicast-if\",      opt_get_ip_multicast_if},\n    {\"ip-multicast-loop\",    opt_get_ip_multicast_loop},\n    {\"error\",                opt_get_error},\n    {\"ipv6-unicast-hops\",    opt_get_ip6_unicast_hops},\n    {\"ipv6-multicast-hops\",  opt_get_ip6_unicast_hops},\n    {\"ipv6-multicast-loop\",  opt_get_ip6_multicast_loop},\n    {\"ipv6-v6only\",          opt_get_ip6_v6only},\n\t{\"recv-buffer-size\",     opt_get_recv_buf_size},\n\t{\"send-buffer-size\",     opt_get_send_buf_size},\n    {NULL,                   NULL}\n};\n\n/* functions in library namespace */\nstatic luaL_Reg func[] = {\n    {\"udp\", global_create},\n    {\"udp4\", global_create4},\n    {\"udp6\", global_create6},\n    {NULL, NULL}\n};\n\n/*-------------------------------------------------------------------------*\\\n* Initializes module\n\\*-------------------------------------------------------------------------*/\nint udp_open(lua_State *L) {\n    /* create classes */\n    auxiliar_newclass(L, \"udp{connected}\", udp_methods);\n    auxiliar_newclass(L, \"udp{unconnected}\", udp_methods);\n    /* create class groups */\n    auxiliar_add2group(L, \"udp{connected}\",   \"udp{any}\");\n    auxiliar_add2group(L, \"udp{unconnected}\", \"udp{any}\");\n    auxiliar_add2group(L, \"udp{connected}\",   \"select{able}\");\n    auxiliar_add2group(L, \"udp{unconnected}\", \"select{able}\");\n    /* define library functions */\n    luaL_setfuncs(L, func, 0);\n    /* export default UDP size */\n    lua_pushliteral(L, \"_DATAGRAMSIZE\");\n    lua_pushinteger(L, UDP_DATAGRAMSIZE);\n    lua_rawset(L, -3);\n    return 0;\n}\n\n/*=========================================================================*\\\n* Lua methods\n\\*=========================================================================*/\nstatic const char *udp_strerror(int err) {\n    /* a 'closed' error on an unconnected means the target address was not\n     * accepted by the transport layer */\n    if (err == IO_CLOSED) return \"refused\";\n    else return socket_strerror(err);\n}\n\n/*-------------------------------------------------------------------------*\\\n* Send data through connected udp socket\n\\*-------------------------------------------------------------------------*/\nstatic int meth_send(lua_State *L) {\n    p_udp udp = (p_udp) auxiliar_checkclass(L, \"udp{connected}\", 1);\n    p_timeout tm = &udp->tm;\n    size_t count, sent = 0;\n    int err;\n    const char *data = luaL_checklstring(L, 2, &count);\n    timeout_markstart(tm);\n    err = socket_send(&udp->sock, data, count, &sent, tm);\n    if (err != IO_DONE) {\n        lua_pushnil(L);\n        lua_pushstring(L, udp_strerror(err));\n        return 2;\n    }\n    lua_pushnumber(L, (lua_Number) sent);\n    return 1;\n}\n\n/*-------------------------------------------------------------------------*\\\n* Send data through unconnected udp socket\n\\*-------------------------------------------------------------------------*/\nstatic int meth_sendto(lua_State *L) {\n    p_udp udp = (p_udp) auxiliar_checkclass(L, \"udp{unconnected}\", 1);\n    size_t count, sent = 0;\n    const char *data = luaL_checklstring(L, 2, &count);\n    const char *ip = luaL_checkstring(L, 3);\n    const char *port = luaL_checkstring(L, 4);\n    p_timeout tm = &udp->tm;\n    int err;\n    struct addrinfo aihint;\n    struct addrinfo *ai;\n    memset(&aihint, 0, sizeof(aihint));\n    aihint.ai_family = udp->family;\n    aihint.ai_socktype = SOCK_DGRAM;\n    aihint.ai_flags = AI_NUMERICHOST;\n#ifdef AI_NUMERICSERV\n    aihint.ai_flags |= AI_NUMERICSERV;\n#endif\n    err = getaddrinfo(ip, port, &aihint, &ai);\n\tif (err) {\n        lua_pushnil(L);\n        lua_pushstring(L, gai_strerror(err));\n        return 2;\n    }\n\n    /* create socket if on first sendto if AF_UNSPEC was set */\n    if (udp->family == AF_UNSPEC && udp->sock == SOCKET_INVALID) {\n        struct addrinfo *ap;\n        const char *errstr = NULL;\n        for (ap = ai; ap != NULL; ap = ap->ai_next) {\n            errstr = inet_trycreate(&udp->sock, ap->ai_family, SOCK_DGRAM, 0);\n            if (errstr == NULL) {\n                socket_setnonblocking(&udp->sock);\n                udp->family = ap->ai_family;\n                break;\n            }\n        }\n        if (errstr != NULL) {\n            lua_pushnil(L);\n            lua_pushstring(L, errstr);\n            freeaddrinfo(ai);\n            return 2;\n        }\n    }\n\n    timeout_markstart(tm);\n    err = socket_sendto(&udp->sock, data, count, &sent, ai->ai_addr,\n        (socklen_t) ai->ai_addrlen, tm);\n    freeaddrinfo(ai);\n    if (err != IO_DONE) {\n        lua_pushnil(L);\n        lua_pushstring(L, udp_strerror(err));\n        return 2;\n    }\n    lua_pushnumber(L, (lua_Number) sent);\n    return 1;\n}\n\n/*-------------------------------------------------------------------------*\\\n* Receives data from a UDP socket\n\\*-------------------------------------------------------------------------*/\nstatic int meth_receive(lua_State *L) {\n    p_udp udp = (p_udp) auxiliar_checkgroup(L, \"udp{any}\", 1);\n    char buf[UDP_DATAGRAMSIZE];\n    size_t got, wanted = (size_t) luaL_optnumber(L, 2, sizeof(buf));\n    char *dgram = wanted > sizeof(buf)? (char *) malloc(wanted): buf;\n    int err;\n    p_timeout tm = &udp->tm;\n    timeout_markstart(tm);\n    if (!dgram) {\n        lua_pushnil(L);\n        lua_pushliteral(L, \"out of memory\");\n        return 2;\n    }\n    err = socket_recv(&udp->sock, dgram, wanted, &got, tm);\n    /* Unlike TCP, recv() of zero is not closed, but a zero-length packet. */\n    if (err != IO_DONE && err != IO_CLOSED) {\n        lua_pushnil(L);\n        lua_pushstring(L, udp_strerror(err));\n        if (wanted > sizeof(buf)) free(dgram);\n        return 2;\n    }\n    lua_pushlstring(L, dgram, got);\n    if (wanted > sizeof(buf)) free(dgram);\n    return 1;\n}\n\n/*-------------------------------------------------------------------------*\\\n* Receives data and sender from a UDP socket\n\\*-------------------------------------------------------------------------*/\nstatic int meth_receivefrom(lua_State *L) {\n    p_udp udp = (p_udp) auxiliar_checkclass(L, \"udp{unconnected}\", 1);\n    char buf[UDP_DATAGRAMSIZE];\n    size_t got, wanted = (size_t) luaL_optnumber(L, 2, sizeof(buf));\n    char *dgram = wanted > sizeof(buf)? (char *) malloc(wanted): buf;\n    struct sockaddr_storage addr;\n    socklen_t addr_len = sizeof(addr);\n    char addrstr[INET6_ADDRSTRLEN];\n    char portstr[6];\n    int err;\n    p_timeout tm = &udp->tm;\n    timeout_markstart(tm);\n    if (!dgram) {\n        lua_pushnil(L);\n        lua_pushliteral(L, \"out of memory\");\n        return 2;\n    }\n    err = socket_recvfrom(&udp->sock, dgram, wanted, &got, (SA *) &addr,\n            &addr_len, tm);\n    /* Unlike TCP, recv() of zero is not closed, but a zero-length packet. */\n    if (err != IO_DONE && err != IO_CLOSED) {\n        lua_pushnil(L);\n        lua_pushstring(L, udp_strerror(err));\n        if (wanted > sizeof(buf)) free(dgram);\n        return 2;\n    }\n    err = getnameinfo((struct sockaddr *)&addr, addr_len, addrstr,\n        INET6_ADDRSTRLEN, portstr, 6, NI_NUMERICHOST | NI_NUMERICSERV);\n\tif (err) {\n        lua_pushnil(L);\n        lua_pushstring(L, gai_strerror(err));\n        if (wanted > sizeof(buf)) free(dgram);\n        return 2;\n    }\n    lua_pushlstring(L, dgram, got);\n    lua_pushstring(L, addrstr);\n    lua_pushinteger(L, (int) strtol(portstr, (char **) NULL, 10));\n    if (wanted > sizeof(buf)) free(dgram);\n    return 3;\n}\n\n/*-------------------------------------------------------------------------*\\\n* Returns family as string\n\\*-------------------------------------------------------------------------*/\nstatic int meth_getfamily(lua_State *L) {\n    p_udp udp = (p_udp) auxiliar_checkgroup(L, \"udp{any}\", 1);\n    if (udp->family == AF_INET6) {\n        lua_pushliteral(L, \"inet6\");\n        return 1;\n    } else {\n        lua_pushliteral(L, \"inet4\");\n        return 1;\n    }\n}\n\n/*-------------------------------------------------------------------------*\\\n* Select support methods\n\\*-------------------------------------------------------------------------*/\nstatic int meth_getfd(lua_State *L) {\n    p_udp udp = (p_udp) auxiliar_checkgroup(L, \"udp{any}\", 1);\n    lua_pushnumber(L, (int) udp->sock);\n    return 1;\n}\n\n/* this is very dangerous, but can be handy for those that are brave enough */\nstatic int meth_setfd(lua_State *L) {\n    p_udp udp = (p_udp) auxiliar_checkgroup(L, \"udp{any}\", 1);\n    udp->sock = (t_socket) luaL_checknumber(L, 2);\n    return 0;\n}\n\nstatic int meth_dirty(lua_State *L) {\n    p_udp udp = (p_udp) auxiliar_checkgroup(L, \"udp{any}\", 1);\n    (void) udp;\n    lua_pushboolean(L, 0);\n    return 1;\n}\n\n/*-------------------------------------------------------------------------*\\\n* Just call inet methods\n\\*-------------------------------------------------------------------------*/\nstatic int meth_getpeername(lua_State *L) {\n    p_udp udp = (p_udp) auxiliar_checkclass(L, \"udp{connected}\", 1);\n    return inet_meth_getpeername(L, &udp->sock, udp->family);\n}\n\nstatic int meth_getsockname(lua_State *L) {\n    p_udp udp = (p_udp) auxiliar_checkgroup(L, \"udp{any}\", 1);\n    return inet_meth_getsockname(L, &udp->sock, udp->family);\n}\n\n/*-------------------------------------------------------------------------*\\\n* Just call option handler\n\\*-------------------------------------------------------------------------*/\nstatic int meth_setoption(lua_State *L) {\n    p_udp udp = (p_udp) auxiliar_checkgroup(L, \"udp{any}\", 1);\n    return opt_meth_setoption(L, optset, &udp->sock);\n}\n\n/*-------------------------------------------------------------------------*\\\n* Just call option handler\n\\*-------------------------------------------------------------------------*/\nstatic int meth_getoption(lua_State *L) {\n    p_udp udp = (p_udp) auxiliar_checkgroup(L, \"udp{any}\", 1);\n    return opt_meth_getoption(L, optget, &udp->sock);\n}\n\n/*-------------------------------------------------------------------------*\\\n* Just call tm methods\n\\*-------------------------------------------------------------------------*/\nstatic int meth_settimeout(lua_State *L) {\n    p_udp udp = (p_udp) auxiliar_checkgroup(L, \"udp{any}\", 1);\n    return timeout_meth_settimeout(L, &udp->tm);\n}\n\nstatic int meth_gettimeout(lua_State *L) {\n    p_udp udp = (p_udp) auxiliar_checkgroup(L, \"udp{any}\", 1);\n    return timeout_meth_gettimeout(L, &udp->tm);\n}\n\n/*-------------------------------------------------------------------------*\\\n* Turns a master udp object into a client object.\n\\*-------------------------------------------------------------------------*/\nstatic int meth_setpeername(lua_State *L) {\n    p_udp udp = (p_udp) auxiliar_checkgroup(L, \"udp{any}\", 1);\n    p_timeout tm = &udp->tm;\n    const char *address = luaL_checkstring(L, 2);\n    int connecting = strcmp(address, \"*\");\n    const char *port = connecting? luaL_checkstring(L, 3): \"0\";\n    struct addrinfo connecthints;\n    const char *err;\n    memset(&connecthints, 0, sizeof(connecthints));\n    connecthints.ai_socktype = SOCK_DGRAM;\n    /* make sure we try to connect only to the same family */\n    connecthints.ai_family = udp->family;\n    if (connecting) {\n        err = inet_tryconnect(&udp->sock, &udp->family, address,\n            port, tm, &connecthints);\n        if (err) {\n            lua_pushnil(L);\n            lua_pushstring(L, err);\n            return 2;\n        }\n        auxiliar_setclass(L, \"udp{connected}\", 1);\n    } else {\n        /* we ignore possible errors because Mac OS X always\n         * returns EAFNOSUPPORT */\n        inet_trydisconnect(&udp->sock, udp->family, tm);\n        auxiliar_setclass(L, \"udp{unconnected}\", 1);\n    }\n    lua_pushnumber(L, 1);\n    return 1;\n}\n\n/*-------------------------------------------------------------------------*\\\n* Closes socket used by object\n\\*-------------------------------------------------------------------------*/\nstatic int meth_close(lua_State *L) {\n    p_udp udp = (p_udp) auxiliar_checkgroup(L, \"udp{any}\", 1);\n    socket_destroy(&udp->sock);\n    lua_pushnumber(L, 1);\n    return 1;\n}\n\n/*-------------------------------------------------------------------------*\\\n* Turns a master object into a server object\n\\*-------------------------------------------------------------------------*/\nstatic int meth_setsockname(lua_State *L) {\n    p_udp udp = (p_udp) auxiliar_checkclass(L, \"udp{unconnected}\", 1);\n    const char *address =  luaL_checkstring(L, 2);\n    const char *port = luaL_checkstring(L, 3);\n    const char *err;\n    struct addrinfo bindhints;\n    memset(&bindhints, 0, sizeof(bindhints));\n    bindhints.ai_socktype = SOCK_DGRAM;\n    bindhints.ai_family = udp->family;\n    bindhints.ai_flags = AI_PASSIVE;\n    err = inet_trybind(&udp->sock, &udp->family, address, port, &bindhints);\n    if (err) {\n        lua_pushnil(L);\n        lua_pushstring(L, err);\n        return 2;\n    }\n    lua_pushnumber(L, 1);\n    return 1;\n}\n\n/*=========================================================================*\\\n* Library functions\n\\*=========================================================================*/\n/*-------------------------------------------------------------------------*\\\n* Creates a master udp object\n\\*-------------------------------------------------------------------------*/\nstatic int udp_create(lua_State *L, int family) {\n    /* allocate udp object */\n    p_udp udp = (p_udp) lua_newuserdata(L, sizeof(t_udp));\n    auxiliar_setclass(L, \"udp{unconnected}\", -1);\n    /* if family is AF_UNSPEC, we leave the socket invalid and\n     * store AF_UNSPEC into family. This will allow it to later be\n     * replaced with an AF_INET6 or AF_INET socket upon first use. */\n    udp->sock = SOCKET_INVALID;\n    timeout_init(&udp->tm, -1, -1);\n    udp->family = family;\n    if (family != AF_UNSPEC) {\n        const char *err = inet_trycreate(&udp->sock, family, SOCK_DGRAM, 0);\n        if (err != NULL) {\n            lua_pushnil(L);\n            lua_pushstring(L, err);\n            return 2;\n        }\n        socket_setnonblocking(&udp->sock);\n    }\n    return 1;\n}\n\nstatic int global_create(lua_State *L) {\n    return udp_create(L, AF_UNSPEC);\n}\n\nstatic int global_create4(lua_State *L) {\n    return udp_create(L, AF_INET);\n}\n\nstatic int global_create6(lua_State *L) {\n    return udp_create(L, AF_INET6);\n}\n"
  },
  {
    "path": "src/luasocket/udp.h",
    "content": "#ifndef UDP_H\n#define UDP_H\n/*=========================================================================*\\\n* UDP object\n* LuaSocket toolkit\n*\n* The udp.h module provides LuaSocket with support for UDP protocol\n* (AF_INET, SOCK_DGRAM).\n*\n* Two classes are defined: connected and unconnected. UDP objects are\n* originally unconnected. They can be \"connected\" to a given address\n* with a call to the setpeername function. The same function can be used to\n* break the connection.\n\\*=========================================================================*/\n#include \"luasocket.h\"\n\n#include \"timeout.h\"\n#include \"socket.h\"\n\n#define UDP_DATAGRAMSIZE 8192\n\ntypedef struct t_udp_ {\n    t_socket sock;\n    t_timeout tm;\n    int family;\n} t_udp;\ntypedef t_udp *p_udp;\n\n#ifndef _WIN32\n#pragma GCC visibility push(hidden)\n#endif\n\nint udp_open(lua_State *L);\n\n#ifndef _WIN32\n#pragma GCC visibility pop\n#endif\n\n#endif /* UDP_H */\n"
  },
  {
    "path": "src/luasocket/unix.c",
    "content": "/*=========================================================================*\\\n* Unix domain socket\n* LuaSocket toolkit\n\\*=========================================================================*/\n#include \"luasocket.h\"\n\n#include \"unixstream.h\"\n#include \"unixdgram.h\"\n\n/*-------------------------------------------------------------------------*\\\n* Modules and functions\n\\*-------------------------------------------------------------------------*/\nstatic const luaL_Reg mod[] = {\n    {\"stream\", unixstream_open},\n    {\"dgram\", unixdgram_open},\n    {NULL, NULL}\n};\n\nstatic void add_alias(lua_State *L, int index, const char *name, const char *target)\n{\n    lua_getfield(L, index, target);\n    lua_setfield(L, index, name);\n}\n\nstatic int compat_socket_unix_call(lua_State *L)\n{\n    /* Look up socket.unix.stream in the socket.unix table (which is the first\n     * argument). */\n    lua_getfield(L, 1, \"stream\");\n\n    /* Replace the stack entry for the socket.unix table with the\n     * socket.unix.stream function. */\n    lua_replace(L, 1);\n\n    /* Call socket.unix.stream, passing along any arguments. */\n    int n = lua_gettop(L);\n    lua_call(L, n-1, LUA_MULTRET);\n\n    /* Pass along the return values from socket.unix.stream. */\n    n = lua_gettop(L);\n    return n;\n}\n\n/*-------------------------------------------------------------------------*\\\n* Initializes module\n\\*-------------------------------------------------------------------------*/\nLUASOCKET_API int luaopen_socket_unix(lua_State *L)\n{\n    int i;\n    lua_newtable(L);\n    int socket_unix_table = lua_gettop(L);\n\n    for (i = 0; mod[i].name; i++)\n        mod[i].func(L);\n\n    /* Add backwards compatibility aliases \"tcp\" and \"udp\" for the \"stream\" and\n     * \"dgram\" functions. */\n    add_alias(L, socket_unix_table, \"tcp\", \"stream\");\n    add_alias(L, socket_unix_table, \"udp\", \"dgram\");\n\n    /* Add a backwards compatibility function and a metatable setup to call it\n     * for the old socket.unix() interface. */\n    lua_pushcfunction(L, compat_socket_unix_call);\n    lua_setfield(L, socket_unix_table, \"__call\");\n    lua_pushvalue(L, socket_unix_table);\n    lua_setmetatable(L, socket_unix_table);\n\n    return 1;\n}\n"
  },
  {
    "path": "src/luasocket/unix.h",
    "content": "#ifndef UNIX_H\n#define UNIX_H\n/*=========================================================================*\\\n* Unix domain object\n* LuaSocket toolkit\n*\n* This module is just an example of how to extend LuaSocket with a new \n* domain.\n\\*=========================================================================*/\n#include \"luasocket.h\"\n\n#include \"buffer.h\"\n#include \"timeout.h\"\n#include \"socket.h\"\n\ntypedef struct t_unix_ {\n    t_socket sock;\n    t_io io;\n    t_buffer buf;\n    t_timeout tm;\n} t_unix;\ntypedef t_unix *p_unix;\n\nLUASOCKET_API int luaopen_socket_unix(lua_State *L);\n\n#endif /* UNIX_H */\n"
  },
  {
    "path": "src/luasocket/unixdgram.c",
    "content": "/*=========================================================================*\\\n* Unix domain socket dgram submodule\n* LuaSocket toolkit\n\\*=========================================================================*/\n#include \"luasocket.h\"\n\n#include \"auxiliar.h\"\n#include \"socket.h\"\n#include \"options.h\"\n#include \"unix.h\"\n\n#include <string.h>\n#include <stdlib.h>\n\n#include <sys/un.h>\n\n#define UNIXDGRAM_DATAGRAMSIZE 8192\n\n// provide a SUN_LEN macro if sys/un.h doesn't (e.g. Android)\n#ifndef SUN_LEN\n#define SUN_LEN(ptr) \\\n  ((size_t) (((struct sockaddr_un *) 0)->sun_path)  \\\n   + strlen ((ptr)->sun_path))\n#endif\n\n/*=========================================================================*\\\n* Internal function prototypes\n\\*=========================================================================*/\nstatic int global_create(lua_State *L);\nstatic int meth_connect(lua_State *L);\nstatic int meth_bind(lua_State *L);\nstatic int meth_send(lua_State *L);\nstatic int meth_receive(lua_State *L);\nstatic int meth_close(lua_State *L);\nstatic int meth_setoption(lua_State *L);\nstatic int meth_settimeout(lua_State *L);\nstatic int meth_gettimeout(lua_State *L);\nstatic int meth_getfd(lua_State *L);\nstatic int meth_setfd(lua_State *L);\nstatic int meth_dirty(lua_State *L);\nstatic int meth_receivefrom(lua_State *L);\nstatic int meth_sendto(lua_State *L);\nstatic int meth_getsockname(lua_State *L);\n\nstatic const char *unixdgram_tryconnect(p_unix un, const char *path);\nstatic const char *unixdgram_trybind(p_unix un, const char *path);\n\n/* unixdgram object methods */\nstatic luaL_Reg unixdgram_methods[] = {\n    {\"__gc\",        meth_close},\n    {\"__tostring\",  auxiliar_tostring},\n    {\"bind\",        meth_bind},\n    {\"close\",       meth_close},\n    {\"connect\",     meth_connect},\n    {\"dirty\",       meth_dirty},\n    {\"getfd\",       meth_getfd},\n    {\"send\",        meth_send},\n    {\"sendto\",      meth_sendto},\n    {\"receive\",     meth_receive},\n    {\"receivefrom\", meth_receivefrom},\n    {\"setfd\",       meth_setfd},\n    {\"setoption\",   meth_setoption},\n    {\"setpeername\", meth_connect},\n    {\"setsockname\", meth_bind},\n    {\"getsockname\", meth_getsockname},\n    {\"settimeout\",  meth_settimeout},\n    {\"gettimeout\",  meth_gettimeout},\n    {NULL,          NULL}\n};\n\n/* socket option handlers */\nstatic t_opt optset[] = {\n    {\"reuseaddr\",   opt_set_reuseaddr},\n    {NULL,          NULL}\n};\n\n/* functions in library namespace */\nstatic luaL_Reg func[] = {\n    {\"dgram\", global_create},\n    {NULL, NULL}\n};\n\n/*-------------------------------------------------------------------------*\\\n* Initializes module\n\\*-------------------------------------------------------------------------*/\nint unixdgram_open(lua_State *L)\n{\n    /* create classes */\n    auxiliar_newclass(L, \"unixdgram{connected}\", unixdgram_methods);\n    auxiliar_newclass(L, \"unixdgram{unconnected}\", unixdgram_methods);\n    /* create class groups */\n    auxiliar_add2group(L, \"unixdgram{connected}\",   \"unixdgram{any}\");\n    auxiliar_add2group(L, \"unixdgram{unconnected}\", \"unixdgram{any}\");\n    auxiliar_add2group(L, \"unixdgram{connected}\",   \"select{able}\");\n    auxiliar_add2group(L, \"unixdgram{unconnected}\", \"select{able}\");\n\n    luaL_setfuncs(L, func, 0);\n    return 0;\n}\n\n/*=========================================================================*\\\n* Lua methods\n\\*=========================================================================*/\nstatic const char *unixdgram_strerror(int err)\n{\n    /* a 'closed' error on an unconnected means the target address was not\n     * accepted by the transport layer */\n    if (err == IO_CLOSED) return \"refused\";\n    else return socket_strerror(err);\n}\n\nstatic int meth_send(lua_State *L)\n{\n    p_unix un = (p_unix) auxiliar_checkclass(L, \"unixdgram{connected}\", 1);\n    p_timeout tm = &un->tm;\n    size_t count, sent = 0;\n    int err;\n    const char *data = luaL_checklstring(L, 2, &count);\n    timeout_markstart(tm);\n    err = socket_send(&un->sock, data, count, &sent, tm);\n    if (err != IO_DONE) {\n        lua_pushnil(L);\n        lua_pushstring(L, unixdgram_strerror(err));\n        return 2;\n    }\n    lua_pushnumber(L, (lua_Number) sent);\n    return 1;\n}\n\n/*-------------------------------------------------------------------------*\\\n* Send data through unconnected unixdgram socket\n\\*-------------------------------------------------------------------------*/\nstatic int meth_sendto(lua_State *L)\n{\n    p_unix un = (p_unix) auxiliar_checkclass(L, \"unixdgram{unconnected}\", 1);\n    size_t count, sent = 0;\n    const char *data = luaL_checklstring(L, 2, &count);\n    const char *path = luaL_checkstring(L, 3);\n    p_timeout tm = &un->tm;\n    int err;\n    struct sockaddr_un remote;\n    size_t len = strlen(path);\n\n    if (len >= sizeof(remote.sun_path)) {\n\t\tlua_pushnil(L);\n\t\tlua_pushstring(L, \"path too long\");\n\t\treturn 2;\n\t}\n\n    memset(&remote, 0, sizeof(remote));\n    strcpy(remote.sun_path, path);\n    remote.sun_family = AF_UNIX;\n    timeout_markstart(tm);\n#ifdef UNIX_HAS_SUN_LEN\n    remote.sun_len = sizeof(remote.sun_family) + sizeof(remote.sun_len)\n        + len + 1;\n    err = socket_sendto(&un->sock, data, count, &sent, (SA *) &remote, remote.sun_len, tm);\n#else\n    err = socket_sendto(&un->sock, data, count, &sent, (SA *) &remote,\n\t\t   \tsizeof(remote.sun_family) + len, tm);\n#endif\n    if (err != IO_DONE) {\n        lua_pushnil(L);\n        lua_pushstring(L, unixdgram_strerror(err));\n        return 2;\n    }\n    lua_pushnumber(L, (lua_Number) sent);\n    return 1;\n}\n\nstatic int meth_receive(lua_State *L) {\n    p_unix un = (p_unix) auxiliar_checkgroup(L, \"unixdgram{any}\", 1);\n    char buf[UNIXDGRAM_DATAGRAMSIZE];\n    size_t got, wanted = (size_t) luaL_optnumber(L, 2, sizeof(buf));\n    char *dgram = wanted > sizeof(buf)? (char *) malloc(wanted): buf;\n    int err;\n    p_timeout tm = &un->tm;\n    timeout_markstart(tm);\n    if (!dgram) {\n        lua_pushnil(L);\n        lua_pushliteral(L, \"out of memory\");\n        return 2;\n    }\n    err = socket_recv(&un->sock, dgram, wanted, &got, tm);\n    /* Unlike STREAM, recv() of zero is not closed, but a zero-length packet. */\n    if (err != IO_DONE && err != IO_CLOSED) {\n        lua_pushnil(L);\n        lua_pushstring(L, unixdgram_strerror(err));\n        if (wanted > sizeof(buf)) free(dgram);\n        return 2;\n    }\n    lua_pushlstring(L, dgram, got);\n    if (wanted > sizeof(buf)) free(dgram);\n    return 1;\n}\n\n/*-------------------------------------------------------------------------*\\\n* Receives data and sender from a DGRAM socket\n\\*-------------------------------------------------------------------------*/\nstatic int meth_receivefrom(lua_State *L) {\n    p_unix un = (p_unix) auxiliar_checkclass(L, \"unixdgram{unconnected}\", 1);\n    char buf[UNIXDGRAM_DATAGRAMSIZE];\n    size_t got, wanted = (size_t) luaL_optnumber(L, 2, sizeof(buf));\n    char *dgram = wanted > sizeof(buf)? (char *) malloc(wanted): buf;\n    struct sockaddr_un addr;\n    socklen_t addr_len = sizeof(addr);\n    int err;\n    p_timeout tm = &un->tm;\n    timeout_markstart(tm);\n    if (!dgram) {\n        lua_pushnil(L);\n        lua_pushliteral(L, \"out of memory\");\n        return 2;\n    }\n    addr.sun_path[0] = '\\0';\n    err = socket_recvfrom(&un->sock, dgram, wanted, &got, (SA *) &addr,\n            &addr_len, tm);\n    /* Unlike STREAM, recv() of zero is not closed, but a zero-length packet. */\n    if (err != IO_DONE && err != IO_CLOSED) {\n        lua_pushnil(L);\n        lua_pushstring(L, unixdgram_strerror(err));\n        if (wanted > sizeof(buf)) free(dgram);\n        return 2;\n    }\n\n    lua_pushlstring(L, dgram, got);\n\t/* the path may be empty, when client send without bind */\n    lua_pushstring(L, addr.sun_path);\n    if (wanted > sizeof(buf)) free(dgram);\n    return 2;\n}\n\n/*-------------------------------------------------------------------------*\\\n* Just call option handler\n\\*-------------------------------------------------------------------------*/\nstatic int meth_setoption(lua_State *L) {\n    p_unix un = (p_unix) auxiliar_checkgroup(L, \"unixdgram{any}\", 1);\n    return opt_meth_setoption(L, optset, &un->sock);\n}\n\n/*-------------------------------------------------------------------------*\\\n* Select support methods\n\\*-------------------------------------------------------------------------*/\nstatic int meth_getfd(lua_State *L) {\n    p_unix un = (p_unix) auxiliar_checkgroup(L, \"unixdgram{any}\", 1);\n    lua_pushnumber(L, (int) un->sock);\n    return 1;\n}\n\n/* this is very dangerous, but can be handy for those that are brave enough */\nstatic int meth_setfd(lua_State *L) {\n    p_unix un = (p_unix) auxiliar_checkgroup(L, \"unixdgram{any}\", 1);\n    un->sock = (t_socket) luaL_checknumber(L, 2);\n    return 0;\n}\n\nstatic int meth_dirty(lua_State *L) {\n    p_unix un = (p_unix) auxiliar_checkgroup(L, \"unixdgram{any}\", 1);\n    (void) un;\n    lua_pushboolean(L, 0);\n    return 1;\n}\n\n/*-------------------------------------------------------------------------*\\\n* Binds an object to an address\n\\*-------------------------------------------------------------------------*/\nstatic const char *unixdgram_trybind(p_unix un, const char *path) {\n    struct sockaddr_un local;\n    size_t len = strlen(path);\n    if (len >= sizeof(local.sun_path)) return \"path too long\";\n    memset(&local, 0, sizeof(local));\n    strcpy(local.sun_path, path);\n    local.sun_family = AF_UNIX;\n    size_t addrlen = SUN_LEN(&local);\n#ifdef UNIX_HAS_SUN_LEN\n    local.sun_len = addrlen + 1;\n#endif\n    int err = socket_bind(&un->sock, (SA *) &local, addrlen);\n    if (err != IO_DONE) socket_destroy(&un->sock);\n    return socket_strerror(err);\n}\n\nstatic int meth_bind(lua_State *L)\n{\n    p_unix un = (p_unix) auxiliar_checkclass(L, \"unixdgram{unconnected}\", 1);\n    const char *path =  luaL_checkstring(L, 2);\n    const char *err = unixdgram_trybind(un, path);\n    if (err) {\n        lua_pushnil(L);\n        lua_pushstring(L, err);\n        return 2;\n    }\n    lua_pushnumber(L, 1);\n    return 1;\n}\n\nstatic int meth_getsockname(lua_State *L)\n{\n    p_unix un = (p_unix) auxiliar_checkgroup(L, \"unixdgram{any}\", 1);\n    struct sockaddr_un peer = {0};\n    socklen_t peer_len = sizeof(peer);\n\n    if (getsockname(un->sock, (SA *) &peer, &peer_len) < 0) {\n        lua_pushnil(L);\n        lua_pushstring(L, socket_strerror(errno));\n        return 2;\n    }\n\n    lua_pushstring(L, peer.sun_path);\n    return 1;\n}\n\n/*-------------------------------------------------------------------------*\\\n* Turns a master unixdgram object into a client object.\n\\*-------------------------------------------------------------------------*/\nstatic const char *unixdgram_tryconnect(p_unix un, const char *path)\n{\n    struct sockaddr_un remote;\n    size_t len = strlen(path);\n    if (len >= sizeof(remote.sun_path)) return \"path too long\";\n    memset(&remote, 0, sizeof(remote));\n    strcpy(remote.sun_path, path);\n    remote.sun_family = AF_UNIX;\n    timeout_markstart(&un->tm);\n    size_t addrlen = SUN_LEN(&remote);\n#ifdef UNIX_HAS_SUN_LEN\n    remote.sun_len = addrlen + 1;\n#endif\n    int err = socket_connect(&un->sock, (SA *) &remote, addrlen, &un->tm);\n    if (err != IO_DONE) socket_destroy(&un->sock);\n    return socket_strerror(err);\n}\n\nstatic int meth_connect(lua_State *L)\n{\n    p_unix un = (p_unix) auxiliar_checkgroup(L, \"unixdgram{any}\", 1);\n    const char *path =  luaL_checkstring(L, 2);\n    const char *err = unixdgram_tryconnect(un, path);\n    if (err) {\n        lua_pushnil(L);\n        lua_pushstring(L, err);\n        return 2;\n    }\n    /* turn unconnected object into a connected object */\n    auxiliar_setclass(L, \"unixdgram{connected}\", 1);\n    lua_pushnumber(L, 1);\n    return 1;\n}\n\n/*-------------------------------------------------------------------------*\\\n* Closes socket used by object\n\\*-------------------------------------------------------------------------*/\nstatic int meth_close(lua_State *L)\n{\n    p_unix un = (p_unix) auxiliar_checkgroup(L, \"unixdgram{any}\", 1);\n    socket_destroy(&un->sock);\n    lua_pushnumber(L, 1);\n    return 1;\n}\n\n/*-------------------------------------------------------------------------*\\\n* Just call tm methods\n\\*-------------------------------------------------------------------------*/\nstatic int meth_settimeout(lua_State *L)\n{\n    p_unix un = (p_unix) auxiliar_checkgroup(L, \"unixdgram{any}\", 1);\n    return timeout_meth_settimeout(L, &un->tm);\n}\n\nstatic int meth_gettimeout(lua_State *L)\n{\n    p_unix un = (p_unix) auxiliar_checkgroup(L, \"unixdgram{any}\", 1);\n    return timeout_meth_gettimeout(L, &un->tm);\n}\n\n/*=========================================================================*\\\n* Library functions\n\\*=========================================================================*/\n/*-------------------------------------------------------------------------*\\\n* Creates a master unixdgram object\n\\*-------------------------------------------------------------------------*/\nstatic int global_create(lua_State *L)\n{\n    t_socket sock;\n    int err = socket_create(&sock, AF_UNIX, SOCK_DGRAM, 0);\n    /* try to allocate a system socket */\n    if (err == IO_DONE) {\n        /* allocate unixdgram object */\n        p_unix un = (p_unix) lua_newuserdata(L, sizeof(t_unix));\n        /* set its type as master object */\n        auxiliar_setclass(L, \"unixdgram{unconnected}\", -1);\n        /* initialize remaining structure fields */\n        socket_setnonblocking(&sock);\n        un->sock = sock;\n        io_init(&un->io, (p_send) socket_send, (p_recv) socket_recv,\n                (p_error) socket_ioerror, &un->sock);\n        timeout_init(&un->tm, -1, -1);\n        buffer_init(&un->buf, &un->io, &un->tm);\n        return 1;\n    } else {\n        lua_pushnil(L);\n        lua_pushstring(L, socket_strerror(err));\n        return 2;\n    }\n}\n"
  },
  {
    "path": "src/luasocket/unixdgram.h",
    "content": "#ifndef UNIXDGRAM_H\n#define UNIXDGRAM_H\n/*=========================================================================*\\\n* DGRAM object\n* LuaSocket toolkit\n*\n* The dgram.h module provides LuaSocket with support for DGRAM protocol\n* (AF_INET, SOCK_DGRAM).\n*\n* Two classes are defined: connected and unconnected. DGRAM objects are\n* originally unconnected. They can be \"connected\" to a given address\n* with a call to the setpeername function. The same function can be used to\n* break the connection.\n\\*=========================================================================*/\n\n#include \"unix.h\"\n\n#ifndef _WIN32\n#pragma GCC visibility push(hidden)\n#endif\n\nint unixdgram_open(lua_State *L);\n\n#ifndef _WIN32\n#pragma GCC visibility pop\n#endif\n\n#endif /* UNIXDGRAM_H */\n"
  },
  {
    "path": "src/luasocket/unixstream.c",
    "content": "/*=========================================================================*\\\n* Unix domain socket stream sub module\n* LuaSocket toolkit\n\\*=========================================================================*/\n#include \"luasocket.h\"\n\n#include \"auxiliar.h\"\n#include \"socket.h\"\n#include \"options.h\"\n#include \"unixstream.h\"\n\n#include <string.h>\n#include <sys/un.h>\n\n/*=========================================================================*\\\n* Internal function prototypes\n\\*=========================================================================*/\nstatic int global_create(lua_State *L);\nstatic int meth_connect(lua_State *L);\nstatic int meth_listen(lua_State *L);\nstatic int meth_bind(lua_State *L);\nstatic int meth_send(lua_State *L);\nstatic int meth_shutdown(lua_State *L);\nstatic int meth_receive(lua_State *L);\nstatic int meth_accept(lua_State *L);\nstatic int meth_close(lua_State *L);\nstatic int meth_setoption(lua_State *L);\nstatic int meth_settimeout(lua_State *L);\nstatic int meth_getfd(lua_State *L);\nstatic int meth_setfd(lua_State *L);\nstatic int meth_dirty(lua_State *L);\nstatic int meth_getstats(lua_State *L);\nstatic int meth_setstats(lua_State *L);\nstatic int meth_getsockname(lua_State *L);\n\nstatic const char *unixstream_tryconnect(p_unix un, const char *path);\nstatic const char *unixstream_trybind(p_unix un, const char *path);\n\n/* unixstream object methods */\nstatic luaL_Reg unixstream_methods[] = {\n    {\"__gc\",        meth_close},\n    {\"__tostring\",  auxiliar_tostring},\n    {\"accept\",      meth_accept},\n    {\"bind\",        meth_bind},\n    {\"close\",       meth_close},\n    {\"connect\",     meth_connect},\n    {\"dirty\",       meth_dirty},\n    {\"getfd\",       meth_getfd},\n    {\"getstats\",    meth_getstats},\n    {\"setstats\",    meth_setstats},\n    {\"listen\",      meth_listen},\n    {\"receive\",     meth_receive},\n    {\"send\",        meth_send},\n    {\"setfd\",       meth_setfd},\n    {\"setoption\",   meth_setoption},\n    {\"setpeername\", meth_connect},\n    {\"setsockname\", meth_bind},\n    {\"getsockname\", meth_getsockname},\n    {\"settimeout\",  meth_settimeout},\n    {\"shutdown\",    meth_shutdown},\n    {NULL,          NULL}\n};\n\n/* socket option handlers */\nstatic t_opt optset[] = {\n    {\"keepalive\",   opt_set_keepalive},\n    {\"reuseaddr\",   opt_set_reuseaddr},\n    {\"linger\",      opt_set_linger},\n    {NULL,          NULL}\n};\n\n/* functions in library namespace */\nstatic luaL_Reg func[] = {\n    {\"stream\", global_create},\n    {NULL, NULL}\n};\n\n/*-------------------------------------------------------------------------*\\\n* Initializes module\n\\*-------------------------------------------------------------------------*/\nint unixstream_open(lua_State *L)\n{\n    /* create classes */\n    auxiliar_newclass(L, \"unixstream{master}\", unixstream_methods);\n    auxiliar_newclass(L, \"unixstream{client}\", unixstream_methods);\n    auxiliar_newclass(L, \"unixstream{server}\", unixstream_methods);\n\n    /* create class groups */\n    auxiliar_add2group(L, \"unixstream{master}\", \"unixstream{any}\");\n    auxiliar_add2group(L, \"unixstream{client}\", \"unixstream{any}\");\n    auxiliar_add2group(L, \"unixstream{server}\", \"unixstream{any}\");\n\n    luaL_setfuncs(L, func, 0);\n    return 0;\n}\n\n/*=========================================================================*\\\n* Lua methods\n\\*=========================================================================*/\n/*-------------------------------------------------------------------------*\\\n* Just call buffered IO methods\n\\*-------------------------------------------------------------------------*/\nstatic int meth_send(lua_State *L) {\n    p_unix un = (p_unix) auxiliar_checkclass(L, \"unixstream{client}\", 1);\n    return buffer_meth_send(L, &un->buf);\n}\n\nstatic int meth_receive(lua_State *L) {\n    p_unix un = (p_unix) auxiliar_checkclass(L, \"unixstream{client}\", 1);\n    return buffer_meth_receive(L, &un->buf);\n}\n\nstatic int meth_getstats(lua_State *L) {\n    p_unix un = (p_unix) auxiliar_checkclass(L, \"unixstream{client}\", 1);\n    return buffer_meth_getstats(L, &un->buf);\n}\n\nstatic int meth_setstats(lua_State *L) {\n    p_unix un = (p_unix) auxiliar_checkclass(L, \"unixstream{client}\", 1);\n    return buffer_meth_setstats(L, &un->buf);\n}\n\n/*-------------------------------------------------------------------------*\\\n* Just call option handler\n\\*-------------------------------------------------------------------------*/\nstatic int meth_setoption(lua_State *L) {\n    p_unix un = (p_unix) auxiliar_checkgroup(L, \"unixstream{any}\", 1);\n    return opt_meth_setoption(L, optset, &un->sock);\n}\n\n/*-------------------------------------------------------------------------*\\\n* Select support methods\n\\*-------------------------------------------------------------------------*/\nstatic int meth_getfd(lua_State *L) {\n    p_unix un = (p_unix) auxiliar_checkgroup(L, \"unixstream{any}\", 1);\n    lua_pushnumber(L, (int) un->sock);\n    return 1;\n}\n\n/* this is very dangerous, but can be handy for those that are brave enough */\nstatic int meth_setfd(lua_State *L) {\n    p_unix un = (p_unix) auxiliar_checkgroup(L, \"unixstream{any}\", 1);\n    un->sock = (t_socket) luaL_checknumber(L, 2);\n    return 0;\n}\n\nstatic int meth_dirty(lua_State *L) {\n    p_unix un = (p_unix) auxiliar_checkgroup(L, \"unixstream{any}\", 1);\n    lua_pushboolean(L, !buffer_isempty(&un->buf));\n    return 1;\n}\n\n/*-------------------------------------------------------------------------*\\\n* Waits for and returns a client object attempting connection to the\n* server object\n\\*-------------------------------------------------------------------------*/\nstatic int meth_accept(lua_State *L) {\n    p_unix server = (p_unix) auxiliar_checkclass(L, \"unixstream{server}\", 1);\n    p_timeout tm = timeout_markstart(&server->tm);\n    t_socket sock;\n    int err = socket_accept(&server->sock, &sock, NULL, NULL, tm);\n    /* if successful, push client socket */\n    if (err == IO_DONE) {\n        p_unix clnt = (p_unix) lua_newuserdata(L, sizeof(t_unix));\n        auxiliar_setclass(L, \"unixstream{client}\", -1);\n        /* initialize structure fields */\n        socket_setnonblocking(&sock);\n        clnt->sock = sock;\n        io_init(&clnt->io, (p_send)socket_send, (p_recv)socket_recv,\n                (p_error) socket_ioerror, &clnt->sock);\n        timeout_init(&clnt->tm, -1, -1);\n        buffer_init(&clnt->buf, &clnt->io, &clnt->tm);\n        return 1;\n    } else {\n        lua_pushnil(L);\n        lua_pushstring(L, socket_strerror(err));\n        return 2;\n    }\n}\n\n/*-------------------------------------------------------------------------*\\\n* Binds an object to an address\n\\*-------------------------------------------------------------------------*/\nstatic const char *unixstream_trybind(p_unix un, const char *path) {\n    struct sockaddr_un local;\n    size_t len = strlen(path);\n    int err;\n    if (len >= sizeof(local.sun_path)) return \"path too long\";\n    memset(&local, 0, sizeof(local));\n    strcpy(local.sun_path, path);\n    local.sun_family = AF_UNIX;\n#ifdef UNIX_HAS_SUN_LEN\n    local.sun_len = sizeof(local.sun_family) + sizeof(local.sun_len)\n        + len + 1;\n    err = socket_bind(&un->sock, (SA *) &local, local.sun_len);\n\n#else\n    err = socket_bind(&un->sock, (SA *) &local,\n            sizeof(local.sun_family) + len);\n#endif\n    if (err != IO_DONE) socket_destroy(&un->sock);\n    return socket_strerror(err);\n}\n\nstatic int meth_bind(lua_State *L) {\n    p_unix un = (p_unix) auxiliar_checkclass(L, \"unixstream{master}\", 1);\n    const char *path =  luaL_checkstring(L, 2);\n    const char *err = unixstream_trybind(un, path);\n    if (err) {\n        lua_pushnil(L);\n        lua_pushstring(L, err);\n        return 2;\n    }\n    lua_pushnumber(L, 1);\n    return 1;\n}\n\nstatic int meth_getsockname(lua_State *L)\n{\n    p_unix un = (p_unix) auxiliar_checkgroup(L, \"unixstream{any}\", 1);\n    struct sockaddr_un peer = {0};\n    socklen_t peer_len = sizeof(peer);\n\n    if (getsockname(un->sock, (SA *) &peer, &peer_len) < 0) {\n        lua_pushnil(L);\n        lua_pushstring(L, socket_strerror(errno));\n        return 2;\n    }\n\n    lua_pushstring(L, peer.sun_path);\n    return 1;\n}\n\n/*-------------------------------------------------------------------------*\\\n* Turns a master unixstream object into a client object.\n\\*-------------------------------------------------------------------------*/\nstatic const char *unixstream_tryconnect(p_unix un, const char *path)\n{\n    struct sockaddr_un remote;\n    int err;\n    size_t len = strlen(path);\n    if (len >= sizeof(remote.sun_path)) return \"path too long\";\n    memset(&remote, 0, sizeof(remote));\n    strcpy(remote.sun_path, path);\n    remote.sun_family = AF_UNIX;\n    timeout_markstart(&un->tm);\n#ifdef UNIX_HAS_SUN_LEN\n    remote.sun_len = sizeof(remote.sun_family) + sizeof(remote.sun_len)\n        + len + 1;\n    err = socket_connect(&un->sock, (SA *) &remote, remote.sun_len, &un->tm);\n#else\n    err = socket_connect(&un->sock, (SA *) &remote,\n            sizeof(remote.sun_family) + len, &un->tm);\n#endif\n    if (err != IO_DONE) socket_destroy(&un->sock);\n    return socket_strerror(err);\n}\n\nstatic int meth_connect(lua_State *L)\n{\n    p_unix un = (p_unix) auxiliar_checkclass(L, \"unixstream{master}\", 1);\n    const char *path =  luaL_checkstring(L, 2);\n    const char *err = unixstream_tryconnect(un, path);\n    if (err) {\n        lua_pushnil(L);\n        lua_pushstring(L, err);\n        return 2;\n    }\n    /* turn master object into a client object */\n    auxiliar_setclass(L, \"unixstream{client}\", 1);\n    lua_pushnumber(L, 1);\n    return 1;\n}\n\n/*-------------------------------------------------------------------------*\\\n* Closes socket used by object\n\\*-------------------------------------------------------------------------*/\nstatic int meth_close(lua_State *L)\n{\n    p_unix un = (p_unix) auxiliar_checkgroup(L, \"unixstream{any}\", 1);\n    socket_destroy(&un->sock);\n    lua_pushnumber(L, 1);\n    return 1;\n}\n\n/*-------------------------------------------------------------------------*\\\n* Puts the sockt in listen mode\n\\*-------------------------------------------------------------------------*/\nstatic int meth_listen(lua_State *L)\n{\n    p_unix un = (p_unix) auxiliar_checkclass(L, \"unixstream{master}\", 1);\n    int backlog = (int) luaL_optnumber(L, 2, 32);\n    int err = socket_listen(&un->sock, backlog);\n    if (err != IO_DONE) {\n        lua_pushnil(L);\n        lua_pushstring(L, socket_strerror(err));\n        return 2;\n    }\n    /* turn master object into a server object */\n    auxiliar_setclass(L, \"unixstream{server}\", 1);\n    lua_pushnumber(L, 1);\n    return 1;\n}\n\n/*-------------------------------------------------------------------------*\\\n* Shuts the connection down partially\n\\*-------------------------------------------------------------------------*/\nstatic int meth_shutdown(lua_State *L)\n{\n    /* SHUT_RD,  SHUT_WR,  SHUT_RDWR  have  the value 0, 1, 2, so we can use method index directly */\n    static const char* methods[] = { \"receive\", \"send\", \"both\", NULL };\n    p_unix stream = (p_unix) auxiliar_checkclass(L, \"unixstream{client}\", 1);\n    int how = luaL_checkoption(L, 2, \"both\", methods);\n    socket_shutdown(&stream->sock, how);\n    lua_pushnumber(L, 1);\n    return 1;\n}\n\n/*-------------------------------------------------------------------------*\\\n* Just call tm methods\n\\*-------------------------------------------------------------------------*/\nstatic int meth_settimeout(lua_State *L) {\n    p_unix un = (p_unix) auxiliar_checkgroup(L, \"unixstream{any}\", 1);\n    return timeout_meth_settimeout(L, &un->tm);\n}\n\n/*=========================================================================*\\\n* Library functions\n\\*=========================================================================*/\n/*-------------------------------------------------------------------------*\\\n* Creates a master unixstream object\n\\*-------------------------------------------------------------------------*/\nstatic int global_create(lua_State *L) {\n    t_socket sock;\n    int err = socket_create(&sock, AF_UNIX, SOCK_STREAM, 0);\n    /* try to allocate a system socket */\n    if (err == IO_DONE) {\n        /* allocate unixstream object */\n        p_unix un = (p_unix) lua_newuserdata(L, sizeof(t_unix));\n        /* set its type as master object */\n        auxiliar_setclass(L, \"unixstream{master}\", -1);\n        /* initialize remaining structure fields */\n        socket_setnonblocking(&sock);\n        un->sock = sock;\n        io_init(&un->io, (p_send) socket_send, (p_recv) socket_recv,\n                (p_error) socket_ioerror, &un->sock);\n        timeout_init(&un->tm, -1, -1);\n        buffer_init(&un->buf, &un->io, &un->tm);\n        return 1;\n    } else {\n        lua_pushnil(L);\n        lua_pushstring(L, socket_strerror(err));\n        return 2;\n    }\n}\n"
  },
  {
    "path": "src/luasocket/unixstream.h",
    "content": "#ifndef UNIXSTREAM_H\n#define UNIXSTREAM_H\n/*=========================================================================*\\\n* UNIX STREAM object\n* LuaSocket toolkit\n*\n* The unixstream.h module is basicly a glue that puts together modules buffer.h,\n* timeout.h socket.h and inet.h to provide the LuaSocket UNIX STREAM (AF_UNIX,\n* SOCK_STREAM) support.\n*\n* Three classes are defined: master, client and server. The master class is\n* a newly created unixstream object, that has not been bound or connected. Server\n* objects are unixstream objects bound to some local address. Client objects are\n* unixstream objects either connected to some address or returned by the accept\n* method of a server object.\n\\*=========================================================================*/\n#include \"unix.h\"\n\n#ifndef _WIN32\n#pragma GCC visibility push(hidden)\n#endif\n\nint unixstream_open(lua_State *L);\n\n#ifndef _WIN32\n#pragma GCC visibility pop\n#endif\n\n#endif /* UNIXSTREAM_H */\n"
  },
  {
    "path": "src/luasocket/url.lua",
    "content": "-----------------------------------------------------------------------------\n-- URI parsing, composition and relative URL resolution\n-- LuaSocket toolkit.\n-- Author: Diego Nehab\n-----------------------------------------------------------------------------\n\n-----------------------------------------------------------------------------\n-- Declare module\n-----------------------------------------------------------------------------\nsocket.url = {}\nlocal _M = socket.url\n\n-----------------------------------------------------------------------------\n-- Module version\n-----------------------------------------------------------------------------\n_M._VERSION = \"URL 1.0.3\"\n\n-----------------------------------------------------------------------------\n-- Encodes a string into its escaped hexadecimal representation\n-- Input\n--   s: binary string to be encoded\n-- Returns\n--   escaped representation of string binary\n-----------------------------------------------------------------------------\nfunction _M.escape(s)\n    return (string.gsub(s, \"([^A-Za-z0-9_])\", function(c)\n        return string.format(\"%%%02x\", string.byte(c))\n    end))\nend\n\n-----------------------------------------------------------------------------\n-- Protects a path segment, to prevent it from interfering with the\n-- url parsing.\n-- Input\n--   s: binary string to be encoded\n-- Returns\n--   escaped representation of string binary\n-----------------------------------------------------------------------------\nlocal function make_set(t)\n    local s = {}\n    for i,v in ipairs(t) do\n        s[t[i]] = 1\n    end\n    return s\nend\n\n-- these are allowed within a path segment, along with alphanum\n-- other characters must be escaped\nlocal segment_set = make_set {\n    \"-\", \"_\", \".\", \"!\", \"~\", \"*\", \"'\", \"(\",\n    \")\", \":\", \"@\", \"&\", \"=\", \"+\", \"$\", \",\",\n}\n\nlocal function protect_segment(s)\n    return string.gsub(s, \"([^A-Za-z0-9_])\", function (c)\n        if segment_set[c] then return c\n        else return string.format(\"%%%02X\", string.byte(c)) end\n    end)\nend\n\n-----------------------------------------------------------------------------\n-- Unencodes a escaped hexadecimal string into its binary representation\n-- Input\n--   s: escaped hexadecimal string to be unencoded\n-- Returns\n--   unescaped binary representation of escaped hexadecimal  binary\n-----------------------------------------------------------------------------\nfunction _M.unescape(s)\n    return (string.gsub(s, \"%%(%x%x)\", function(hex)\n        return string.char(tonumber(hex, 16))\n    end))\nend\n\n-----------------------------------------------------------------------------\n-- Removes '..' and '.' components appropriately from a path.\n-- Input\n--   path\n-- Returns\n--   dot-normalized path\nlocal function remove_dot_components(path)\n    local marker = string.char(1)\n    repeat\n        local was = path\n        path = path:gsub('//', '/'..marker..'/', 1)\n    until path == was\n    repeat\n        local was = path\n        path = path:gsub('/%./', '/', 1)\n    until path == was\n    repeat\n        local was = path\n        path = path:gsub('[^/]+/%.%./([^/]+)', '%1', 1)\n    until path == was\n    path = path:gsub('[^/]+/%.%./*$', '')\n    path = path:gsub('/%.%.$', '/')\n    path = path:gsub('/%.$', '/')\n    path = path:gsub('^/%.%./', '/')\n    path = path:gsub(marker, '')\n    return path\nend\n\n-----------------------------------------------------------------------------\n-- Builds a path from a base path and a relative path\n-- Input\n--   base_path\n--   relative_path\n-- Returns\n--   corresponding absolute path\n-----------------------------------------------------------------------------\nlocal function absolute_path(base_path, relative_path)\n    if string.sub(relative_path, 1, 1) == \"/\" then\n      return remove_dot_components(relative_path) end\n    base_path = base_path:gsub(\"[^/]*$\", \"\")\n    if not base_path:find'/$' then base_path = base_path .. '/' end\n    local path = base_path .. relative_path\n    path = remove_dot_components(path)\n    return path\nend\n\n-----------------------------------------------------------------------------\n-- Parses a url and returns a table with all its parts according to RFC 2396\n-- The following grammar describes the names given to the URL parts\n-- <url> ::= <scheme>://<authority>/<path>;<params>?<query>#<fragment>\n-- <authority> ::= <userinfo>@<host>:<port>\n-- <userinfo> ::= <user>[:<password>]\n-- <path> :: = {<segment>/}<segment>\n-- Input\n--   url: uniform resource locator of request\n--   default: table with default values for each field\n-- Returns\n--   table with the following fields, where RFC naming conventions have\n--   been preserved:\n--     scheme, authority, userinfo, user, password, host, port,\n--     path, params, query, fragment\n-- Obs:\n--   the leading '/' in {/<path>} is considered part of <path>\n-----------------------------------------------------------------------------\nfunction _M.parse(url, default)\n    -- initialize default parameters\n    local parsed = {}\n    for i,v in pairs(default or parsed) do parsed[i] = v end\n    -- empty url is parsed to nil\n    if not url or url == \"\" then return nil, \"invalid url\" end\n    -- remove whitespace\n    -- url = string.gsub(url, \"%s\", \"\")\n    -- get scheme\n    url = string.gsub(url, \"^([%w][%w%+%-%.]*)%:\",\n        function(s) parsed.scheme = s; return \"\" end)\n    -- get authority\n    url = string.gsub(url, \"^//([^/]*)\", function(n)\n        parsed.authority = n\n        return \"\"\n    end)\n    -- get fragment\n    url = string.gsub(url, \"#(.*)$\", function(f)\n        parsed.fragment = f\n        return \"\"\n    end)\n    -- get query string\n    url = string.gsub(url, \"%?(.*)\", function(q)\n        parsed.query = q\n        return \"\"\n    end)\n    -- get params\n    url = string.gsub(url, \"%;(.*)\", function(p)\n        parsed.params = p\n        return \"\"\n    end)\n    -- path is whatever was left\n    if url ~= \"\" then parsed.path = url end\n    local authority = parsed.authority\n    if not authority then return parsed end\n    authority = string.gsub(authority,\"^([^@]*)@\",\n        function(u) parsed.userinfo = u; return \"\" end)\n    authority = string.gsub(authority, \":([^:%]]*)$\",\n        function(p) parsed.port = p; return \"\" end)\n    if authority ~= \"\" then \n        -- IPv6?\n        parsed.host = string.match(authority, \"^%[(.+)%]$\") or authority \n    end\n    local userinfo = parsed.userinfo\n    if not userinfo then return parsed end\n    userinfo = string.gsub(userinfo, \":([^:]*)$\",\n        function(p) parsed.password = p; return \"\" end)\n    parsed.user = userinfo\n    return parsed\nend\n\n-----------------------------------------------------------------------------\n-- Rebuilds a parsed URL from its components.\n-- Components are protected if any reserved or unallowed characters are found\n-- Input\n--   parsed: parsed URL, as returned by parse\n-- Returns\n--   a stringing with the corresponding URL\n-----------------------------------------------------------------------------\nfunction _M.build(parsed)\n    --local ppath = _M.parse_path(parsed.path or \"\")\n    --local url = _M.build_path(ppath)\n    local url = parsed.path or \"\"\n    if parsed.params then url = url .. \";\" .. parsed.params end\n    if parsed.query then url = url .. \"?\" .. parsed.query end\n    local authority = parsed.authority\n    if parsed.host then\n        authority = parsed.host\n        if string.find(authority, \":\") then -- IPv6?\n            authority = \"[\" .. authority .. \"]\"\n        end\n        if parsed.port then authority = authority .. \":\" .. tostring(parsed.port) end\n        local userinfo = parsed.userinfo\n        if parsed.user then\n            userinfo = parsed.user\n            if parsed.password then\n                userinfo = userinfo .. \":\" .. parsed.password\n            end\n        end\n        if userinfo then authority = userinfo .. \"@\" .. authority end\n    end\n    if authority then url = \"//\" .. authority .. url end\n    if parsed.scheme then url = parsed.scheme .. \":\" .. url end\n    if parsed.fragment then url = url .. \"#\" .. parsed.fragment end\n    -- url = string.gsub(url, \"%s\", \"\")\n    return url\nend\n\n-----------------------------------------------------------------------------\n-- Builds a absolute URL from a base and a relative URL according to RFC 2396\n-- Input\n--   base_url\n--   relative_url\n-- Returns\n--   corresponding absolute url\n-----------------------------------------------------------------------------\nfunction _M.absolute(base_url, relative_url)\n    local base_parsed\n    if type(base_url) == \"table\" then\n        base_parsed = base_url\n        base_url = _M.build(base_parsed)\n    else\n        base_parsed = _M.parse(base_url)\n    end\n    local result\n    local relative_parsed = _M.parse(relative_url)\n    if not base_parsed then\n        result = relative_url\n    elseif not relative_parsed then\n        result = base_url\n    elseif relative_parsed.scheme then\n        result = relative_url\n    else\n        relative_parsed.scheme = base_parsed.scheme\n        if not relative_parsed.authority then\n            relative_parsed.authority = base_parsed.authority\n            if not relative_parsed.path then\n                relative_parsed.path = base_parsed.path\n                if not relative_parsed.params then\n                    relative_parsed.params = base_parsed.params\n                    if not relative_parsed.query then\n                        relative_parsed.query = base_parsed.query\n                    end\n                end\n            else    \n                relative_parsed.path = absolute_path(base_parsed.path or \"\",\n                    relative_parsed.path)\n            end\n        end\n        result = _M.build(relative_parsed)\n    end\n    return remove_dot_components(result)\nend\n\n-----------------------------------------------------------------------------\n-- Breaks a path into its segments, unescaping the segments\n-- Input\n--   path\n-- Returns\n--   segment: a table with one entry per segment\n-----------------------------------------------------------------------------\nfunction _M.parse_path(path)\n    local parsed = {}\n    path = path or \"\"\n    --path = string.gsub(path, \"%s\", \"\")\n    string.gsub(path, \"([^/]+)\", function (s) table.insert(parsed, s) end)\n    for i = 1, #parsed do\n        parsed[i] = _M.unescape(parsed[i])\n    end\n    if string.sub(path, 1, 1) == \"/\" then parsed.is_absolute = 1 end\n    if string.sub(path, -1, -1) == \"/\" then parsed.is_directory = 1 end\n    return parsed\nend\n\n-----------------------------------------------------------------------------\n-- Builds a path component from its segments, escaping protected characters.\n-- Input\n--   parsed: path segments\n--   unsafe: if true, segments are not protected before path is built\n-- Returns\n--   path: corresponding path stringing\n-----------------------------------------------------------------------------\nfunction _M.build_path(parsed, unsafe)\n    local path = \"\"\n    local n = #parsed\n    if unsafe then\n        for i = 1, n-1 do\n            path = path .. parsed[i]\n            path = path .. \"/\"\n        end\n        if n > 0 then\n            path = path .. parsed[n]\n            if parsed.is_directory then path = path .. \"/\" end\n        end\n    else\n        for i = 1, n-1 do\n            path = path .. protect_segment(parsed[i])\n            path = path .. \"/\"\n        end\n        if n > 0 then\n            path = path .. protect_segment(parsed[n])\n            if parsed.is_directory then path = path .. \"/\" end\n        end\n    end\n    if parsed.is_absolute then path = \"/\" .. path end\n    return path\nend\n\nreturn _M\n"
  },
  {
    "path": "src/luasocket/usocket.c",
    "content": "/*=========================================================================*\\\n* Socket compatibilization module for Unix\n* LuaSocket toolkit\n*\n* The code is now interrupt-safe.\n* The penalty of calling select to avoid busy-wait is only paid when\n* the I/O call fail in the first place.\n\\*=========================================================================*/\n#include \"luasocket.h\"\n#include \"../teliva.h\"\n\n#include \"socket.h\"\n#include \"pierror.h\"\n\n#include <string.h>\n#include <signal.h>\n\n/*-------------------------------------------------------------------------*\\\n* Wait for readable/writable/connected socket with timeout\n\\*-------------------------------------------------------------------------*/\n#ifndef SOCKET_SELECT\n#include <sys/poll.h>\n\n#define WAITFD_R        POLLIN\n#define WAITFD_W        POLLOUT\n#define WAITFD_C        (POLLIN|POLLOUT)\nint socket_waitfd(p_socket ps, int sw, p_timeout tm) {\n    int ret;\n    struct pollfd pfd;\n    pfd.fd = *ps;\n    pfd.events = sw;\n    pfd.revents = 0;\n    if (timeout_iszero(tm)) return IO_TIMEOUT;  /* optimize timeout == 0 case */\n    do {\n        int t = (int)(timeout_getretry(tm)*1e3);\n        ret = poll(&pfd, 1, t >= 0? t: -1);\n    } while (ret == -1 && errno == EINTR);\n    if (ret == -1) return errno;\n    if (ret == 0) return IO_TIMEOUT;\n    if (sw == WAITFD_C && (pfd.revents & (POLLIN|POLLERR))) return IO_CLOSED;\n    return IO_DONE;\n}\n#else\n\n#define WAITFD_R        1\n#define WAITFD_W        2\n#define WAITFD_C        (WAITFD_R|WAITFD_W)\n\nint socket_waitfd(p_socket ps, int sw, p_timeout tm) {\n    int ret;\n    fd_set rfds, wfds, *rp, *wp;\n    struct timeval tv, *tp;\n    double t;\n    if (*ps >= FD_SETSIZE) return EINVAL;\n    if (timeout_iszero(tm)) return IO_TIMEOUT;  /* optimize timeout == 0 case */\n    do {\n        /* must set bits within loop, because select may have modifed them */\n        rp = wp = NULL;\n        if (sw & WAITFD_R) { FD_ZERO(&rfds); FD_SET(*ps, &rfds); rp = &rfds; }\n        if (sw & WAITFD_W) { FD_ZERO(&wfds); FD_SET(*ps, &wfds); wp = &wfds; }\n        t = timeout_getretry(tm);\n        tp = NULL;\n        if (t >= 0.0) {\n            tv.tv_sec = (int)t;\n            tv.tv_usec = (int)((t-tv.tv_sec)*1.0e6);\n            tp = &tv;\n        }\n        ret = select(*ps+1, rp, wp, NULL, tp);\n    } while (ret == -1 && errno == EINTR);\n    if (ret == -1) return errno;\n    if (ret == 0) return IO_TIMEOUT;\n    if (sw == WAITFD_C && FD_ISSET(*ps, &rfds)) return IO_CLOSED;\n    return IO_DONE;\n}\n#endif\n\n\n/*-------------------------------------------------------------------------*\\\n* Initializes module\n\\*-------------------------------------------------------------------------*/\nint socket_open(void) {\n    /* installs a handler to ignore sigpipe or it will crash us */\n    signal(SIGPIPE, SIG_IGN);\n    return 1;\n}\n\n/*-------------------------------------------------------------------------*\\\n* Close module\n\\*-------------------------------------------------------------------------*/\nint socket_close(void) {\n    return 1;\n}\n\n/*-------------------------------------------------------------------------*\\\n* Close and initialize socket\n\\*-------------------------------------------------------------------------*/\nvoid socket_destroy(p_socket ps) {\n    if (*ps != SOCKET_INVALID) {\n        close(*ps);\n        *ps = SOCKET_INVALID;\n    }\n}\n\n/*-------------------------------------------------------------------------*\\\n* Select with timeout control\n\\*-------------------------------------------------------------------------*/\nint socket_select(t_socket n, fd_set *rfds, fd_set *wfds, fd_set *efds,\n        p_timeout tm) {\n    int ret;\n    do {\n        struct timeval tv;\n        double t = timeout_getretry(tm);\n        tv.tv_sec = (int) t;\n        tv.tv_usec = (int) ((t - tv.tv_sec) * 1.0e6);\n        /* timeout = 0 means no wait */\n        ret = select(n, rfds, wfds, efds, t >= 0.0 ? &tv: NULL);\n    } while (ret < 0 && errno == EINTR);\n    return ret;\n}\n\n/*-------------------------------------------------------------------------*\\\n* Creates and sets up a socket\n\\*-------------------------------------------------------------------------*/\nint socket_create(p_socket ps, int domain, int type, int protocol) {\n    *ps = socket(domain, type, protocol);\n    if (*ps != SOCKET_INVALID) return IO_DONE;\n    else return errno;\n}\n\n/*-------------------------------------------------------------------------*\\\n* Binds or returns error message\n\\*-------------------------------------------------------------------------*/\nint socket_bind(p_socket ps, SA *addr, socklen_t len) {\n    int err = IO_DONE;\n    if (!net_operations_permitted) {\n      Previous_message = \"app tried to start a server; adjust its permissions (ctrl-p) if that is expected\";\n      return IO_CLOSED;\n    }\n    socket_setblocking(ps);\n    if (bind(*ps, addr, len) < 0) err = errno;\n    socket_setnonblocking(ps);\n    return err;\n}\n\n/*-------------------------------------------------------------------------*\\\n*\n\\*-------------------------------------------------------------------------*/\nint socket_listen(p_socket ps, int backlog) {\n    int err = IO_DONE;\n    if (listen(*ps, backlog)) err = errno;\n    return err;\n}\n\n/*-------------------------------------------------------------------------*\\\n*\n\\*-------------------------------------------------------------------------*/\nvoid socket_shutdown(p_socket ps, int how) {\n    shutdown(*ps, how);\n}\n\n/*-------------------------------------------------------------------------*\\\n* Connects or returns error message\n\\*-------------------------------------------------------------------------*/\nint socket_connect(p_socket ps, SA *addr, socklen_t len, p_timeout tm) {\n    int err;\n    /* avoid calling on closed sockets */\n    if (*ps == SOCKET_INVALID) return IO_CLOSED;\n    if (!net_operations_permitted) {\n      Previous_message = \"app tried to connect to a remote server; adjust its permissions (ctrl-p) if that is expected\";\n      return IO_CLOSED;\n    }\n    /* call connect until done or failed without being interrupted */\n    do if (connect(*ps, addr, len) == 0) return IO_DONE;\n    while ((err = errno) == EINTR);\n    /* if connection failed immediately, return error code */\n    if (err != EINPROGRESS && err != EAGAIN) return err;\n    /* zero timeout case optimization */\n    if (timeout_iszero(tm)) return IO_TIMEOUT;\n    /* wait until we have the result of the connection attempt or timeout */\n    err = socket_waitfd(ps, WAITFD_C, tm);\n    if (err == IO_CLOSED) {\n        if (recv(*ps, (char *) &err, 0, 0) == 0) return IO_DONE;\n        else return errno;\n    } else return err;\n}\n\n/*-------------------------------------------------------------------------*\\\n* Accept with timeout\n\\*-------------------------------------------------------------------------*/\nint socket_accept(p_socket ps, p_socket pa, SA *addr, socklen_t *len, p_timeout tm) {\n    if (*ps == SOCKET_INVALID) return IO_CLOSED;\n    for ( ;; ) {\n        int err;\n        if ((*pa = accept(*ps, addr, len)) != SOCKET_INVALID) return IO_DONE;\n        err = errno;\n        if (err == EINTR) continue;\n        if (err != EAGAIN && err != ECONNABORTED) return err;\n        if ((err = socket_waitfd(ps, WAITFD_R, tm)) != IO_DONE) return err;\n    }\n    /* can't reach here */\n    return IO_UNKNOWN;\n}\n\n/*-------------------------------------------------------------------------*\\\n* Send with timeout\n\\*-------------------------------------------------------------------------*/\nint socket_send(p_socket ps, const char *data, size_t count,\n        size_t *sent, p_timeout tm)\n{\n    int err;\n    *sent = 0;\n    /* avoid making system calls on closed sockets */\n    if (*ps == SOCKET_INVALID) return IO_CLOSED;\n    /* loop until we send something or we give up on error */\n    for ( ;; ) {\n        long put = (long) send(*ps, data, count, 0);\n        /* if we sent anything, we are done */\n        if (put >= 0) {\n            *sent = put;\n            return IO_DONE;\n        }\n        err = errno;\n        /* EPIPE means the connection was closed */\n        if (err == EPIPE) return IO_CLOSED;\n        /* EPROTOTYPE means the connection is being closed (on Yosemite!)*/\n        if (err == EPROTOTYPE) continue;\n        /* we call was interrupted, just try again */\n        if (err == EINTR) continue;\n        /* if failed fatal reason, report error */\n        if (err != EAGAIN) return err;\n        /* wait until we can send something or we timeout */\n        if ((err = socket_waitfd(ps, WAITFD_W, tm)) != IO_DONE) return err;\n    }\n    /* can't reach here */\n    return IO_UNKNOWN;\n}\n\n/*-------------------------------------------------------------------------*\\\n* Sendto with timeout\n\\*-------------------------------------------------------------------------*/\nint socket_sendto(p_socket ps, const char *data, size_t count, size_t *sent,\n        SA *addr, socklen_t len, p_timeout tm)\n{\n    int err;\n    *sent = 0;\n    if (*ps == SOCKET_INVALID) return IO_CLOSED;\n    for ( ;; ) {\n        long put = (long) sendto(*ps, data, count, 0, addr, len); \n        if (put >= 0) {\n            *sent = put;\n            return IO_DONE;\n        }\n        err = errno;\n        if (err == EPIPE) return IO_CLOSED;\n        if (err == EPROTOTYPE) continue;\n        if (err == EINTR) continue;\n        if (err != EAGAIN) return err;\n        if ((err = socket_waitfd(ps, WAITFD_W, tm)) != IO_DONE) return err;\n    }\n    return IO_UNKNOWN;\n}\n\n/*-------------------------------------------------------------------------*\\\n* Receive with timeout\n\\*-------------------------------------------------------------------------*/\nint socket_recv(p_socket ps, char *data, size_t count, size_t *got, p_timeout tm) {\n    int err;\n    *got = 0;\n    if (*ps == SOCKET_INVALID) return IO_CLOSED;\n    for ( ;; ) {\n        long taken = (long) recv(*ps, data, count, 0);\n        if (taken > 0) {\n            *got = taken;\n            return IO_DONE;\n        }\n        err = errno;\n        if (taken == 0) return IO_CLOSED;\n        if (err == EINTR) continue;\n        if (err != EAGAIN) return err;\n        if ((err = socket_waitfd(ps, WAITFD_R, tm)) != IO_DONE) return err;\n    }\n    return IO_UNKNOWN;\n}\n\n/*-------------------------------------------------------------------------*\\\n* Recvfrom with timeout\n\\*-------------------------------------------------------------------------*/\nint socket_recvfrom(p_socket ps, char *data, size_t count, size_t *got,\n        SA *addr, socklen_t *len, p_timeout tm) {\n    int err;\n    *got = 0;\n    if (*ps == SOCKET_INVALID) return IO_CLOSED;\n    for ( ;; ) {\n        long taken = (long) recvfrom(*ps, data, count, 0, addr, len);\n        if (taken > 0) {\n            *got = taken;\n            return IO_DONE;\n        }\n        err = errno;\n        if (taken == 0) return IO_CLOSED;\n        if (err == EINTR) continue;\n        if (err != EAGAIN) return err;\n        if ((err = socket_waitfd(ps, WAITFD_R, tm)) != IO_DONE) return err;\n    }\n    return IO_UNKNOWN;\n}\n\n\n/*-------------------------------------------------------------------------*\\\n* Write with timeout\n*\n* socket_read and socket_write are cut-n-paste of socket_send and socket_recv,\n* with send/recv replaced with write/read. We can't just use write/read\n* in the socket version, because behaviour when size is zero is different.\n\\*-------------------------------------------------------------------------*/\nint socket_write(p_socket ps, const char *data, size_t count,\n        size_t *sent, p_timeout tm)\n{\n    int err;\n    *sent = 0;\n    /* avoid making system calls on closed sockets */\n    if (*ps == SOCKET_INVALID) return IO_CLOSED;\n    /* loop until we send something or we give up on error */\n    for ( ;; ) {\n        long put = (long) write(*ps, data, count);\n        /* if we sent anything, we are done */\n        if (put >= 0) {\n            *sent = put;\n            return IO_DONE;\n        }\n        err = errno;\n        /* EPIPE means the connection was closed */\n        if (err == EPIPE) return IO_CLOSED;\n        /* EPROTOTYPE means the connection is being closed (on Yosemite!)*/\n        if (err == EPROTOTYPE) continue;\n        /* we call was interrupted, just try again */\n        if (err == EINTR) continue;\n        /* if failed fatal reason, report error */\n        if (err != EAGAIN) return err;\n        /* wait until we can send something or we timeout */\n        if ((err = socket_waitfd(ps, WAITFD_W, tm)) != IO_DONE) return err;\n    }\n    /* can't reach here */\n    return IO_UNKNOWN;\n}\n\n/*-------------------------------------------------------------------------*\\\n* Read with timeout\n* See note for socket_write\n\\*-------------------------------------------------------------------------*/\nint socket_read(p_socket ps, char *data, size_t count, size_t *got, p_timeout tm) {\n    int err;\n    *got = 0;\n    if (*ps == SOCKET_INVALID) return IO_CLOSED;\n    for ( ;; ) {\n        long taken = (long) read(*ps, data, count);\n        if (taken > 0) {\n            *got = taken;\n            return IO_DONE;\n        }\n        err = errno;\n        if (taken == 0) return IO_CLOSED;\n        if (err == EINTR) continue;\n        if (err != EAGAIN) return err;\n        if ((err = socket_waitfd(ps, WAITFD_R, tm)) != IO_DONE) return err;\n    }\n    return IO_UNKNOWN;\n}\n\n/*-------------------------------------------------------------------------*\\\n* Put socket into blocking mode\n\\*-------------------------------------------------------------------------*/\nvoid socket_setblocking(p_socket ps) {\n    int flags = fcntl(*ps, F_GETFL, 0);\n    flags &= (~(O_NONBLOCK));\n    fcntl(*ps, F_SETFL, flags);\n}\n\n/*-------------------------------------------------------------------------*\\\n* Put socket into non-blocking mode\n\\*-------------------------------------------------------------------------*/\nvoid socket_setnonblocking(p_socket ps) {\n    int flags = fcntl(*ps, F_GETFL, 0);\n    flags |= O_NONBLOCK;\n    fcntl(*ps, F_SETFL, flags);\n}\n\n/*-------------------------------------------------------------------------*\\\n* DNS helpers\n\\*-------------------------------------------------------------------------*/\nint socket_gethostbyaddr(const char *addr, socklen_t len, struct hostent **hp) {\n    *hp = gethostbyaddr(addr, len, AF_INET);\n    if (*hp) return IO_DONE;\n    else if (h_errno) return h_errno;\n    else if (errno) return errno;\n    else return IO_UNKNOWN;\n}\n\nint socket_gethostbyname(const char *addr, struct hostent **hp) {\n    *hp = gethostbyname(addr);\n    if (*hp) return IO_DONE;\n    else if (h_errno) return h_errno;\n    else if (errno) return errno;\n    else return IO_UNKNOWN;\n}\n\n/*-------------------------------------------------------------------------*\\\n* Error translation functions\n* Make sure important error messages are standard\n\\*-------------------------------------------------------------------------*/\nconst char *socket_hoststrerror(int err) {\n    if (err <= 0) return io_strerror(err);\n    switch (err) {\n        case HOST_NOT_FOUND: return PIE_HOST_NOT_FOUND;\n        default: return hstrerror(err);\n    }\n}\n\nconst char *socket_strerror(int err) {\n    if (err <= 0) return io_strerror(err);\n    switch (err) {\n        case EADDRINUSE: return PIE_ADDRINUSE;\n        case EISCONN: return PIE_ISCONN;\n        case EACCES: return PIE_ACCESS;\n        case ECONNREFUSED: return PIE_CONNREFUSED;\n        case ECONNABORTED: return PIE_CONNABORTED;\n        case ECONNRESET: return PIE_CONNRESET;\n        case ETIMEDOUT: return PIE_TIMEDOUT;\n        default: {\n            return strerror(err);\n        }\n    }\n}\n\nconst char *socket_ioerror(p_socket ps, int err) {\n    (void) ps;\n    return socket_strerror(err);\n}\n\nconst char *socket_gaistrerror(int err) {\n    if (err == 0) return NULL;\n    switch (err) {\n        case EAI_AGAIN: return PIE_AGAIN;\n        case EAI_BADFLAGS: return PIE_BADFLAGS;\n#ifdef EAI_BADHINTS\n        case EAI_BADHINTS: return PIE_BADHINTS;\n#endif\n        case EAI_FAIL: return PIE_FAIL;\n        case EAI_FAMILY: return PIE_FAMILY;\n        case EAI_MEMORY: return PIE_MEMORY;\n        case EAI_NONAME: return PIE_NONAME;\n#ifdef EAI_OVERFLOW\n        case EAI_OVERFLOW: return PIE_OVERFLOW;\n#endif\n#ifdef EAI_PROTOCOL\n        case EAI_PROTOCOL: return PIE_PROTOCOL;\n#endif\n        case EAI_SERVICE: return PIE_SERVICE;\n        case EAI_SOCKTYPE: return PIE_SOCKTYPE;\n        case EAI_SYSTEM: return strerror(errno);\n        default: return gai_strerror(err);\n    }\n}\n"
  },
  {
    "path": "src/luasocket/usocket.h",
    "content": "#ifndef USOCKET_H\n#define USOCKET_H\n/*=========================================================================*\\\n* Socket compatibilization module for Unix\n* LuaSocket toolkit\n\\*=========================================================================*/\n\n/*=========================================================================*\\\n* BSD include files\n\\*=========================================================================*/\n/* error codes */\n#include <errno.h>\n/* close function */\n#include <unistd.h>\n/* fnctnl function and associated constants */\n#include <fcntl.h>\n/* struct sockaddr */\n#include <sys/types.h>\n/* socket function */\n#include <sys/socket.h>\n/* struct timeval */\n#include <sys/time.h>\n/* gethostbyname and gethostbyaddr functions */\n#include <netdb.h>\n/* sigpipe handling */\n#include <signal.h>\n/* IP stuff*/\n#include <netinet/in.h>\n#include <arpa/inet.h>\n/* TCP options (nagle algorithm disable) */\n#include <netinet/tcp.h>\n#include <net/if.h>\n\n#include <sys/poll.h>\n#define WAITFD_R        POLLIN\n#define WAITFD_W        POLLOUT\n#define WAITFD_C        (POLLIN|POLLOUT)\n\n#ifndef SO_REUSEPORT\n#define SO_REUSEPORT SO_REUSEADDR\n#endif\n\n/* Some platforms use IPV6_JOIN_GROUP instead if\n * IPV6_ADD_MEMBERSHIP. The semantics are same, though. */\n#ifndef IPV6_ADD_MEMBERSHIP\n#ifdef IPV6_JOIN_GROUP\n#define IPV6_ADD_MEMBERSHIP IPV6_JOIN_GROUP\n#endif /* IPV6_JOIN_GROUP */\n#endif /* !IPV6_ADD_MEMBERSHIP */\n\n/* Same with IPV6_DROP_MEMBERSHIP / IPV6_LEAVE_GROUP. */\n#ifndef IPV6_DROP_MEMBERSHIP\n#ifdef IPV6_LEAVE_GROUP\n#define IPV6_DROP_MEMBERSHIP IPV6_LEAVE_GROUP\n#endif /* IPV6_LEAVE_GROUP */\n#endif /* !IPV6_DROP_MEMBERSHIP */\n\ntypedef int t_socket;\ntypedef t_socket *p_socket;\ntypedef struct sockaddr_storage t_sockaddr_storage;\n\n#define SOCKET_INVALID (-1)\n\n#endif /* USOCKET_H */\n"
  },
  {
    "path": "src/luasocket/wsocket.c",
    "content": "/*=========================================================================*\\\n* Socket compatibilization module for Win32\n* LuaSocket toolkit\n*\n* The penalty of calling select to avoid busy-wait is only paid when\n* the I/O call fail in the first place.\n\\*=========================================================================*/\n#include \"luasocket.h\"\n\n#include <string.h>\n\n#include \"socket.h\"\n#include \"pierror.h\"\n\n/* WinSock doesn't have a strerror... */\nstatic const char *wstrerror(int err);\n\n/*-------------------------------------------------------------------------*\\\n* Initializes module\n\\*-------------------------------------------------------------------------*/\nint socket_open(void) {\n    WSADATA wsaData;\n    WORD wVersionRequested = MAKEWORD(2, 0);\n    int err = WSAStartup(wVersionRequested, &wsaData );\n    if (err != 0) return 0;\n    if ((LOBYTE(wsaData.wVersion) != 2 || HIBYTE(wsaData.wVersion) != 0) &&\n        (LOBYTE(wsaData.wVersion) != 1 || HIBYTE(wsaData.wVersion) != 1)) {\n        WSACleanup();\n        return 0;\n    }\n    return 1;\n}\n\n/*-------------------------------------------------------------------------*\\\n* Close module\n\\*-------------------------------------------------------------------------*/\nint socket_close(void) {\n    WSACleanup();\n    return 1;\n}\n\n/*-------------------------------------------------------------------------*\\\n* Wait for readable/writable/connected socket with timeout\n\\*-------------------------------------------------------------------------*/\n#define WAITFD_R        1\n#define WAITFD_W        2\n#define WAITFD_E        4\n#define WAITFD_C        (WAITFD_E|WAITFD_W)\n\nint socket_waitfd(p_socket ps, int sw, p_timeout tm) {\n    int ret;\n    fd_set rfds, wfds, efds, *rp = NULL, *wp = NULL, *ep = NULL;\n    struct timeval tv, *tp = NULL;\n    double t;\n    if (timeout_iszero(tm)) return IO_TIMEOUT;  /* optimize timeout == 0 case */\n    if (sw & WAITFD_R) {\n        FD_ZERO(&rfds);\n        FD_SET(*ps, &rfds);\n        rp = &rfds;\n    }\n    if (sw & WAITFD_W) { FD_ZERO(&wfds); FD_SET(*ps, &wfds); wp = &wfds; }\n    if (sw & WAITFD_C) { FD_ZERO(&efds); FD_SET(*ps, &efds); ep = &efds; }\n    if ((t = timeout_get(tm)) >= 0.0) {\n        tv.tv_sec = (int) t;\n        tv.tv_usec = (int) ((t-tv.tv_sec)*1.0e6);\n        tp = &tv;\n    }\n    ret = select(0, rp, wp, ep, tp);\n    if (ret == -1) return WSAGetLastError();\n    if (ret == 0) return IO_TIMEOUT;\n    if (sw == WAITFD_C && FD_ISSET(*ps, &efds)) return IO_CLOSED;\n    return IO_DONE;\n}\n\n/*-------------------------------------------------------------------------*\\\n* Select with int timeout in ms\n\\*-------------------------------------------------------------------------*/\nint socket_select(t_socket n, fd_set *rfds, fd_set *wfds, fd_set *efds,\n        p_timeout tm) {\n    struct timeval tv;\n    double t = timeout_get(tm);\n    tv.tv_sec = (int) t;\n    tv.tv_usec = (int) ((t - tv.tv_sec) * 1.0e6);\n    if (n <= 0) {\n        Sleep((DWORD) (1000*t));\n        return 0;\n    } else return select(0, rfds, wfds, efds, t >= 0.0? &tv: NULL);\n}\n\n/*-------------------------------------------------------------------------*\\\n* Close and inutilize socket\n\\*-------------------------------------------------------------------------*/\nvoid socket_destroy(p_socket ps) {\n    if (*ps != SOCKET_INVALID) {\n        socket_setblocking(ps); /* close can take a long time on WIN32 */\n        closesocket(*ps);\n        *ps = SOCKET_INVALID;\n    }\n}\n\n/*-------------------------------------------------------------------------*\\\n*\n\\*-------------------------------------------------------------------------*/\nvoid socket_shutdown(p_socket ps, int how) {\n    socket_setblocking(ps);\n    shutdown(*ps, how);\n    socket_setnonblocking(ps);\n}\n\n/*-------------------------------------------------------------------------*\\\n* Creates and sets up a socket\n\\*-------------------------------------------------------------------------*/\nint socket_create(p_socket ps, int domain, int type, int protocol) {\n    *ps = socket(domain, type, protocol);\n    if (*ps != SOCKET_INVALID) return IO_DONE;\n    else return WSAGetLastError();\n}\n\n/*-------------------------------------------------------------------------*\\\n* Connects or returns error message\n\\*-------------------------------------------------------------------------*/\nint socket_connect(p_socket ps, SA *addr, socklen_t len, p_timeout tm) {\n    int err;\n    /* don't call on closed socket */\n    if (*ps == SOCKET_INVALID) return IO_CLOSED;\n    /* ask system to connect */\n    if (connect(*ps, addr, len) == 0) return IO_DONE;\n    /* make sure the system is trying to connect */\n    err = WSAGetLastError();\n    if (err != WSAEWOULDBLOCK && err != WSAEINPROGRESS) return err;\n    /* zero timeout case optimization */\n    if (timeout_iszero(tm)) return IO_TIMEOUT;\n    /* we wait until something happens */\n    err = socket_waitfd(ps, WAITFD_C, tm);\n    if (err == IO_CLOSED) {\n        int elen = sizeof(err);\n        /* give windows time to set the error (yes, disgusting) */\n        Sleep(10);\n        /* find out why we failed */\n        getsockopt(*ps, SOL_SOCKET, SO_ERROR, (char *)&err, &elen);\n        /* we KNOW there was an error. if 'why' is 0, we will return\n        * \"unknown error\", but it's not really our fault */\n        return err > 0? err: IO_UNKNOWN;\n    } else return err;\n\n}\n\n/*-------------------------------------------------------------------------*\\\n* Binds or returns error message\n\\*-------------------------------------------------------------------------*/\nint socket_bind(p_socket ps, SA *addr, socklen_t len) {\n    int err = IO_DONE;\n    socket_setblocking(ps);\n    if (bind(*ps, addr, len) < 0) err = WSAGetLastError();\n    socket_setnonblocking(ps);\n    return err;\n}\n\n/*-------------------------------------------------------------------------*\\\n*\n\\*-------------------------------------------------------------------------*/\nint socket_listen(p_socket ps, int backlog) {\n    int err = IO_DONE;\n    socket_setblocking(ps);\n    if (listen(*ps, backlog) < 0) err = WSAGetLastError();\n    socket_setnonblocking(ps);\n    return err;\n}\n\n/*-------------------------------------------------------------------------*\\\n* Accept with timeout\n\\*-------------------------------------------------------------------------*/\nint socket_accept(p_socket ps, p_socket pa, SA *addr, socklen_t *len,\n        p_timeout tm) {\n    if (*ps == SOCKET_INVALID) return IO_CLOSED;\n    for ( ;; ) {\n        int err;\n        /* try to get client socket */\n        if ((*pa = accept(*ps, addr, len)) != SOCKET_INVALID) return IO_DONE;\n        /* find out why we failed */\n        err = WSAGetLastError();\n        /* if we failed because there was no connectoin, keep trying */\n        if (err != WSAEWOULDBLOCK && err != WSAECONNABORTED) return err;\n        /* call select to avoid busy wait */\n        if ((err = socket_waitfd(ps, WAITFD_R, tm)) != IO_DONE) return err;\n    }\n}\n\n/*-------------------------------------------------------------------------*\\\n* Send with timeout\n* On windows, if you try to send 10MB, the OS will buffer EVERYTHING\n* this can take an awful lot of time and we will end up blocked.\n* Therefore, whoever calls this function should not pass a huge buffer.\n\\*-------------------------------------------------------------------------*/\nint socket_send(p_socket ps, const char *data, size_t count,\n        size_t *sent, p_timeout tm)\n{\n    int err;\n    *sent = 0;\n    /* avoid making system calls on closed sockets */\n    if (*ps == SOCKET_INVALID) return IO_CLOSED;\n    /* loop until we send something or we give up on error */\n    for ( ;; ) {\n        /* try to send something */\n        int put = send(*ps, data, (int) count, 0);\n        /* if we sent something, we are done */\n        if (put > 0) {\n            *sent = put;\n            return IO_DONE;\n        }\n        /* deal with failure */\n        err = WSAGetLastError();\n        /* we can only proceed if there was no serious error */\n        if (err != WSAEWOULDBLOCK) return err;\n        /* avoid busy wait */\n        if ((err = socket_waitfd(ps, WAITFD_W, tm)) != IO_DONE) return err;\n    }\n}\n\n/*-------------------------------------------------------------------------*\\\n* Sendto with timeout\n\\*-------------------------------------------------------------------------*/\nint socket_sendto(p_socket ps, const char *data, size_t count, size_t *sent,\n        SA *addr, socklen_t len, p_timeout tm)\n{\n    int err;\n    *sent = 0;\n    if (*ps == SOCKET_INVALID) return IO_CLOSED;\n    for ( ;; ) {\n        int put = sendto(*ps, data, (int) count, 0, addr, len);\n        if (put > 0) {\n            *sent = put;\n            return IO_DONE;\n        }\n        err = WSAGetLastError();\n        if (err != WSAEWOULDBLOCK) return err;\n        if ((err = socket_waitfd(ps, WAITFD_W, tm)) != IO_DONE) return err;\n    }\n}\n\n/*-------------------------------------------------------------------------*\\\n* Receive with timeout\n\\*-------------------------------------------------------------------------*/\nint socket_recv(p_socket ps, char *data, size_t count, size_t *got,\n        p_timeout tm)\n{\n    int err, prev = IO_DONE;\n    *got = 0;\n    if (*ps == SOCKET_INVALID) return IO_CLOSED;\n    for ( ;; ) {\n        int taken = recv(*ps, data, (int) count, 0);\n        if (taken > 0) {\n            *got = taken;\n            return IO_DONE;\n        }\n        if (taken == 0) return IO_CLOSED;\n        err = WSAGetLastError();\n        /* On UDP, a connreset simply means the previous send failed.\n         * So we try again.\n         * On TCP, it means our socket is now useless, so the error passes.\n         * (We will loop again, exiting because the same error will happen) */\n        if (err != WSAEWOULDBLOCK) {\n            if (err != WSAECONNRESET || prev == WSAECONNRESET) return err;\n            prev = err;\n        }\n        if ((err = socket_waitfd(ps, WAITFD_R, tm)) != IO_DONE) return err;\n    }\n}\n\n/*-------------------------------------------------------------------------*\\\n* Recvfrom with timeout\n\\*-------------------------------------------------------------------------*/\nint socket_recvfrom(p_socket ps, char *data, size_t count, size_t *got,\n        SA *addr, socklen_t *len, p_timeout tm)\n{\n    int err, prev = IO_DONE;\n    *got = 0;\n    if (*ps == SOCKET_INVALID) return IO_CLOSED;\n    for ( ;; ) {\n        int taken = recvfrom(*ps, data, (int) count, 0, addr, len);\n        if (taken > 0) {\n            *got = taken;\n            return IO_DONE;\n        }\n        if (taken == 0) return IO_CLOSED;\n        err = WSAGetLastError();\n        /* On UDP, a connreset simply means the previous send failed.\n         * So we try again.\n         * On TCP, it means our socket is now useless, so the error passes.\n         * (We will loop again, exiting because the same error will happen) */\n        if (err != WSAEWOULDBLOCK) {\n            if (err != WSAECONNRESET || prev == WSAECONNRESET) return err;\n            prev = err;\n        }\n        if ((err = socket_waitfd(ps, WAITFD_R, tm)) != IO_DONE) return err;\n    }\n}\n\n/*-------------------------------------------------------------------------*\\\n* Put socket into blocking mode\n\\*-------------------------------------------------------------------------*/\nvoid socket_setblocking(p_socket ps) {\n    u_long argp = 0;\n    ioctlsocket(*ps, FIONBIO, &argp);\n}\n\n/*-------------------------------------------------------------------------*\\\n* Put socket into non-blocking mode\n\\*-------------------------------------------------------------------------*/\nvoid socket_setnonblocking(p_socket ps) {\n    u_long argp = 1;\n    ioctlsocket(*ps, FIONBIO, &argp);\n}\n\n/*-------------------------------------------------------------------------*\\\n* DNS helpers\n\\*-------------------------------------------------------------------------*/\nint socket_gethostbyaddr(const char *addr, socklen_t len, struct hostent **hp) {\n    *hp = gethostbyaddr(addr, len, AF_INET);\n    if (*hp) return IO_DONE;\n    else return WSAGetLastError();\n}\n\nint socket_gethostbyname(const char *addr, struct hostent **hp) {\n    *hp = gethostbyname(addr);\n    if (*hp) return IO_DONE;\n    else return  WSAGetLastError();\n}\n\n/*-------------------------------------------------------------------------*\\\n* Error translation functions\n\\*-------------------------------------------------------------------------*/\nconst char *socket_hoststrerror(int err) {\n    if (err <= 0) return io_strerror(err);\n    switch (err) {\n        case WSAHOST_NOT_FOUND: return PIE_HOST_NOT_FOUND;\n        default: return wstrerror(err);\n    }\n}\n\nconst char *socket_strerror(int err) {\n    if (err <= 0) return io_strerror(err);\n    switch (err) {\n        case WSAEADDRINUSE: return PIE_ADDRINUSE;\n        case WSAECONNREFUSED : return PIE_CONNREFUSED;\n        case WSAEISCONN: return PIE_ISCONN;\n        case WSAEACCES: return PIE_ACCESS;\n        case WSAECONNABORTED: return PIE_CONNABORTED;\n        case WSAECONNRESET: return PIE_CONNRESET;\n        case WSAETIMEDOUT: return PIE_TIMEDOUT;\n        default: return wstrerror(err);\n    }\n}\n\nconst char *socket_ioerror(p_socket ps, int err) {\n    (void) ps;\n    return socket_strerror(err);\n}\n\nstatic const char *wstrerror(int err) {\n    switch (err) {\n        case WSAEINTR: return \"Interrupted function call\";\n        case WSAEACCES: return PIE_ACCESS; // \"Permission denied\";\n        case WSAEFAULT: return \"Bad address\";\n        case WSAEINVAL: return \"Invalid argument\";\n        case WSAEMFILE: return \"Too many open files\";\n        case WSAEWOULDBLOCK: return \"Resource temporarily unavailable\";\n        case WSAEINPROGRESS: return \"Operation now in progress\";\n        case WSAEALREADY: return \"Operation already in progress\";\n        case WSAENOTSOCK: return \"Socket operation on nonsocket\";\n        case WSAEDESTADDRREQ: return \"Destination address required\";\n        case WSAEMSGSIZE: return \"Message too long\";\n        case WSAEPROTOTYPE: return \"Protocol wrong type for socket\";\n        case WSAENOPROTOOPT: return \"Bad protocol option\";\n        case WSAEPROTONOSUPPORT: return \"Protocol not supported\";\n        case WSAESOCKTNOSUPPORT: return PIE_SOCKTYPE; // \"Socket type not supported\";\n        case WSAEOPNOTSUPP: return \"Operation not supported\";\n        case WSAEPFNOSUPPORT: return \"Protocol family not supported\";\n        case WSAEAFNOSUPPORT: return PIE_FAMILY; // \"Address family not supported by protocol family\";\n        case WSAEADDRINUSE: return PIE_ADDRINUSE; // \"Address already in use\";\n        case WSAEADDRNOTAVAIL: return \"Cannot assign requested address\";\n        case WSAENETDOWN: return \"Network is down\";\n        case WSAENETUNREACH: return \"Network is unreachable\";\n        case WSAENETRESET: return \"Network dropped connection on reset\";\n        case WSAECONNABORTED: return \"Software caused connection abort\";\n        case WSAECONNRESET: return PIE_CONNRESET; // \"Connection reset by peer\";\n        case WSAENOBUFS: return \"No buffer space available\";\n        case WSAEISCONN: return PIE_ISCONN; // \"Socket is already connected\";\n        case WSAENOTCONN: return \"Socket is not connected\";\n        case WSAESHUTDOWN: return \"Cannot send after socket shutdown\";\n        case WSAETIMEDOUT: return PIE_TIMEDOUT; // \"Connection timed out\";\n        case WSAECONNREFUSED: return PIE_CONNREFUSED; // \"Connection refused\";\n        case WSAEHOSTDOWN: return \"Host is down\";\n        case WSAEHOSTUNREACH: return \"No route to host\";\n        case WSAEPROCLIM: return \"Too many processes\";\n        case WSASYSNOTREADY: return \"Network subsystem is unavailable\";\n        case WSAVERNOTSUPPORTED: return \"Winsock.dll version out of range\";\n        case WSANOTINITIALISED:\n            return \"Successful WSAStartup not yet performed\";\n        case WSAEDISCON: return \"Graceful shutdown in progress\";\n        case WSAHOST_NOT_FOUND: return PIE_HOST_NOT_FOUND; // \"Host not found\";\n        case WSATRY_AGAIN: return \"Nonauthoritative host not found\";\n        case WSANO_RECOVERY: return PIE_FAIL; // \"Nonrecoverable name lookup error\";\n        case WSANO_DATA: return \"Valid name, no data record of requested type\";\n        default: return \"Unknown error\";\n    }\n}\n\nconst char *socket_gaistrerror(int err) {\n    if (err == 0) return NULL;\n    switch (err) {\n        case EAI_AGAIN: return PIE_AGAIN;\n        case EAI_BADFLAGS: return PIE_BADFLAGS;\n#ifdef EAI_BADHINTS\n        case EAI_BADHINTS: return PIE_BADHINTS;\n#endif\n        case EAI_FAIL: return PIE_FAIL;\n        case EAI_FAMILY: return PIE_FAMILY;\n        case EAI_MEMORY: return PIE_MEMORY;\n        case EAI_NONAME: return PIE_NONAME;\n#ifdef EAI_OVERFLOW\n        case EAI_OVERFLOW: return PIE_OVERFLOW;\n#endif\n#ifdef EAI_PROTOCOL\n        case EAI_PROTOCOL: return PIE_PROTOCOL;\n#endif\n        case EAI_SERVICE: return PIE_SERVICE;\n        case EAI_SOCKTYPE: return PIE_SOCKTYPE;\n#ifdef EAI_SYSTEM\n        case EAI_SYSTEM: return strerror(errno);\n#endif\n        default: return gai_strerror(err);\n    }\n}\n"
  },
  {
    "path": "src/luasocket/wsocket.h",
    "content": "#ifndef WSOCKET_H\n#define WSOCKET_H\n/*=========================================================================*\\\n* Socket compatibilization module for Win32\n* LuaSocket toolkit\n\\*=========================================================================*/\n\n/*=========================================================================*\\\n* WinSock include files\n\\*=========================================================================*/\n#include <winsock2.h>\n#include <ws2tcpip.h>\n\ntypedef int socklen_t;\ntypedef SOCKADDR_STORAGE t_sockaddr_storage;\ntypedef SOCKET t_socket;\ntypedef t_socket *p_socket;\n\n#ifndef IPV6_V6ONLY\n#define IPV6_V6ONLY 27\n#endif\n\n#define SOCKET_INVALID (INVALID_SOCKET)\n\n#ifndef SO_REUSEPORT\n#define SO_REUSEPORT SO_REUSEADDR\n#endif\n\n#ifndef AI_NUMERICSERV\n#define AI_NUMERICSERV (0)\n#endif\n\n#endif /* WSOCKET_H */\n"
  },
  {
    "path": "src/lundump.c",
    "content": "/*\n** $Id: lundump.c,v 2.7.1.4 2008/04/04 19:51:41 roberto Exp $\n** load precompiled Lua chunks\n** See Copyright Notice in lua.h\n*/\n\n#include <string.h>\n\n#define lundump_c\n#define LUA_CORE\n\n#include \"lua.h\"\n\n#include \"ldebug.h\"\n#include \"ldo.h\"\n#include \"lfunc.h\"\n#include \"lmem.h\"\n#include \"lobject.h\"\n#include \"lstring.h\"\n#include \"lundump.h\"\n#include \"lzio.h\"\n\ntypedef struct {\n lua_State* L;\n ZIO* Z;\n Mbuffer* b;\n const char* name;\n} LoadState;\n\n#ifdef LUAC_TRUST_BINARIES\n#define IF(c,s)\n#define error(S,s)\n#else\n#define IF(c,s)\t\tif (c) error(S,s)\n\nstatic void error(LoadState* S, const char* why)\n{\n luaO_pushfstring(S->L,\"%s: %s in precompiled chunk\",S->name,why);\n luaD_throw(S->L,LUA_ERRSYNTAX);\n}\n#endif\n\n#define LoadMem(S,b,n,size)\tLoadBlock(S,b,(n)*(size))\n#define\tLoadByte(S)\t\t(lu_byte)LoadChar(S)\n#define LoadVar(S,x)\t\tLoadMem(S,&x,1,sizeof(x))\n#define LoadVector(S,b,n,size)\tLoadMem(S,b,n,size)\n\nstatic void LoadBlock(LoadState* S, void* b, size_t size)\n{\n size_t r=luaZ_read(S->Z,b,size);\n IF (r!=0, \"unexpected end\");\n}\n\nstatic int LoadChar(LoadState* S)\n{\n char x;\n LoadVar(S,x);\n return x;\n}\n\nstatic int LoadInt(LoadState* S)\n{\n int x;\n LoadVar(S,x);\n IF (x<0, \"bad integer\");\n return x;\n}\n\nstatic lua_Number LoadNumber(LoadState* S)\n{\n lua_Number x;\n LoadVar(S,x);\n return x;\n}\n\nstatic TString* LoadString(LoadState* S)\n{\n size_t size;\n LoadVar(S,size);\n if (size==0)\n  return NULL;\n else\n {\n  char* s=luaZ_openspace(S->L,S->b,size);\n  LoadBlock(S,s,size);\n  return luaS_newlstr(S->L,s,size-1);\t\t/* remove trailing '\\0' */\n }\n}\n\nstatic void LoadCode(LoadState* S, Proto* f)\n{\n int n=LoadInt(S);\n f->code=luaM_newvector(S->L,n,Instruction);\n f->sizecode=n;\n LoadVector(S,f->code,n,sizeof(Instruction));\n}\n\nstatic Proto* LoadFunction(LoadState* S, TString* p);\n\nstatic void LoadConstants(LoadState* S, Proto* f)\n{\n int i,n;\n n=LoadInt(S);\n f->k=luaM_newvector(S->L,n,TValue);\n f->sizek=n;\n for (i=0; i<n; i++) setnilvalue(&f->k[i]);\n for (i=0; i<n; i++)\n {\n  TValue* o=&f->k[i];\n  int t=LoadChar(S);\n  switch (t)\n  {\n   case LUA_TNIL:\n   \tsetnilvalue(o);\n\tbreak;\n   case LUA_TBOOLEAN:\n   \tsetbvalue(o,LoadChar(S)!=0);\n\tbreak;\n   case LUA_TNUMBER:\n\tsetnvalue(o,LoadNumber(S));\n\tbreak;\n   case LUA_TSTRING:\n\tsetsvalue2n(S->L,o,LoadString(S));\n\tbreak;\n   default:\n\terror(S,\"bad constant\");\n\tbreak;\n  }\n }\n n=LoadInt(S);\n f->p=luaM_newvector(S->L,n,Proto*);\n f->sizep=n;\n for (i=0; i<n; i++) f->p[i]=NULL;\n for (i=0; i<n; i++) f->p[i]=LoadFunction(S,f->source);\n}\n\nstatic void LoadDebug(LoadState* S, Proto* f)\n{\n int i,n;\n n=LoadInt(S);\n f->lineinfo=luaM_newvector(S->L,n,int);\n f->sizelineinfo=n;\n LoadVector(S,f->lineinfo,n,sizeof(int));\n n=LoadInt(S);\n f->locvars=luaM_newvector(S->L,n,LocVar);\n f->sizelocvars=n;\n for (i=0; i<n; i++) f->locvars[i].varname=NULL;\n for (i=0; i<n; i++)\n {\n  f->locvars[i].varname=LoadString(S);\n  f->locvars[i].startpc=LoadInt(S);\n  f->locvars[i].endpc=LoadInt(S);\n }\n n=LoadInt(S);\n f->upvalues=luaM_newvector(S->L,n,TString*);\n f->sizeupvalues=n;\n for (i=0; i<n; i++) f->upvalues[i]=NULL;\n for (i=0; i<n; i++) f->upvalues[i]=LoadString(S);\n}\n\nstatic Proto* LoadFunction(LoadState* S, TString* p)\n{\n Proto* f;\n if (++S->L->nCcalls > LUAI_MAXCCALLS) error(S,\"code too deep\");\n f=luaF_newproto(S->L);\n setptvalue2s(S->L,S->L->top,f); incr_top(S->L);\n f->source=LoadString(S); if (f->source==NULL) f->source=p;\n f->linedefined=LoadInt(S);\n f->lastlinedefined=LoadInt(S);\n f->nups=LoadByte(S);\n f->numparams=LoadByte(S);\n f->is_vararg=LoadByte(S);\n f->maxstacksize=LoadByte(S);\n LoadCode(S,f);\n LoadConstants(S,f);\n LoadDebug(S,f);\n IF (!luaG_checkcode(f), \"bad code\");\n S->L->top--;\n S->L->nCcalls--;\n return f;\n}\n\nstatic void LoadHeader(LoadState* S)\n{\n char h[LUAC_HEADERSIZE];\n char s[LUAC_HEADERSIZE];\n luaU_header(h);\n LoadBlock(S,s,LUAC_HEADERSIZE);\n IF (memcmp(h,s,LUAC_HEADERSIZE)!=0, \"bad header\");\n}\n\n/*\n** load precompiled chunk\n*/\nProto* luaU_undump (lua_State* L, ZIO* Z, Mbuffer* buff, const char* name)\n{\n LoadState S;\n if (*name=='@' || *name=='=')\n  S.name=name+1;\n else if (*name==LUA_SIGNATURE[0])\n  S.name=\"binary string\";\n else\n  S.name=name;\n S.L=L;\n S.Z=Z;\n S.b=buff;\n LoadHeader(&S);\n return LoadFunction(&S,luaS_newliteral(L,\"=?\"));\n}\n\n/*\n* make header\n*/\nvoid luaU_header (char* h)\n{\n int x=1;\n memcpy(h,LUA_SIGNATURE,sizeof(LUA_SIGNATURE)-1);\n h+=sizeof(LUA_SIGNATURE)-1;\n *h++=(char)LUAC_VERSION;\n *h++=(char)LUAC_FORMAT;\n *h++=(char)*(char*)&x;\t\t\t\t/* endianness */\n *h++=(char)sizeof(int);\n *h++=(char)sizeof(size_t);\n *h++=(char)sizeof(Instruction);\n *h++=(char)sizeof(lua_Number);\n *h++=(char)(((lua_Number)0.5)==0);\t\t/* is lua_Number integral? */\n}\n"
  },
  {
    "path": "src/lundump.h",
    "content": "/*\n** $Id: lundump.h,v 1.37.1.1 2007/12/27 13:02:25 roberto Exp $\n** load precompiled Lua chunks\n** See Copyright Notice in lua.h\n*/\n\n#ifndef lundump_h\n#define lundump_h\n\n#include \"lobject.h\"\n#include \"lzio.h\"\n\n/* load one chunk; from lundump.c */\nLUAI_FUNC Proto* luaU_undump (lua_State* L, ZIO* Z, Mbuffer* buff, const char* name);\n\n/* make header; from lundump.c */\nLUAI_FUNC void luaU_header (char* h);\n\n/* dump one chunk; from ldump.c */\nLUAI_FUNC int luaU_dump (lua_State* L, const Proto* f, lua_Writer w, void* data, int strip);\n\n#ifdef luac_c\n/* print one chunk; from print.c */\nLUAI_FUNC void luaU_print (const Proto* f, int full);\n#endif\n\n/* for header of binary files -- this is Lua 5.1 */\n#define LUAC_VERSION\t\t0x51\n\n/* for header of binary files -- this is the official format */\n#define LUAC_FORMAT\t\t0\n\n/* size of header of binary files */\n#define LUAC_HEADERSIZE\t\t12\n\n#endif\n"
  },
  {
    "path": "src/lvm.c",
    "content": "/*\n** $Id: lvm.c,v 2.63.1.5 2011/08/17 20:43:11 roberto Exp $\n** Lua virtual machine\n** See Copyright Notice in lua.h\n*/\n\n\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n\n#define lvm_c\n#define LUA_CORE\n\n#include \"lua.h\"\n\n#include \"ldebug.h\"\n#include \"ldo.h\"\n#include \"lfunc.h\"\n#include \"lgc.h\"\n#include \"lobject.h\"\n#include \"lopcodes.h\"\n#include \"lstate.h\"\n#include \"lstring.h\"\n#include \"ltable.h\"\n#include \"ltm.h\"\n#include \"lvm.h\"\n\n\n\n/* limit for table tag-method chains (to avoid loops) */\n#define MAXTAGLOOP\t100\n\n\nconst TValue *luaV_tonumber (const TValue *obj, TValue *n) {\n  lua_Number num;\n  if (ttisnumber(obj)) return obj;\n  if (ttisstring(obj) && luaO_str2d(svalue(obj), &num)) {\n    setnvalue(n, num);\n    return n;\n  }\n  else\n    return NULL;\n}\n\n\nint luaV_tostring (lua_State *L, StkId obj) {\n  if (!ttisnumber(obj))\n    return 0;\n  else {\n    char s[LUAI_MAXNUMBER2STR];\n    lua_Number n = nvalue(obj);\n    lua_number2str(s, n);\n    setsvalue2s(L, obj, luaS_new(L, s));\n    return 1;\n  }\n}\n\n\nstatic void traceexec (lua_State *L, const Instruction *pc) {\n  lu_byte mask = L->hookmask;\n  const Instruction *oldpc = L->savedpc;\n  L->savedpc = pc;\n  if ((mask & LUA_MASKCOUNT) && L->hookcount == 0) {\n    resethookcount(L);\n    luaD_callhook(L, LUA_HOOKCOUNT, -1);\n  }\n  if (mask & LUA_MASKLINE) {\n    Proto *p = ci_func(L->ci)->l.p;\n    int npc = pcRel(pc, p);\n    int newline = getline(p, npc);\n    /* call linehook when enter a new function, when jump back (loop),\n       or when enter a new line */\n    if (npc == 0 || pc <= oldpc || newline != getline(p, pcRel(oldpc, p)))\n      luaD_callhook(L, LUA_HOOKLINE, newline);\n  }\n}\n\n\nstatic void callTMres (lua_State *L, StkId res, const TValue *f,\n                        const TValue *p1, const TValue *p2) {\n  ptrdiff_t result = savestack(L, res);\n  setobj2s(L, L->top, f);  /* push function */\n  setobj2s(L, L->top+1, p1);  /* 1st argument */\n  setobj2s(L, L->top+2, p2);  /* 2nd argument */\n  luaD_checkstack(L, 3);\n  L->top += 3;\n  luaD_call(L, L->top - 3, 1);\n  res = restorestack(L, result);\n  L->top--;\n  setobjs2s(L, res, L->top);\n}\n\n\n\nstatic void callTM (lua_State *L, const TValue *f, const TValue *p1,\n                    const TValue *p2, const TValue *p3) {\n  setobj2s(L, L->top, f);  /* push function */\n  setobj2s(L, L->top+1, p1);  /* 1st argument */\n  setobj2s(L, L->top+2, p2);  /* 2nd argument */\n  setobj2s(L, L->top+3, p3);  /* 3th argument */\n  luaD_checkstack(L, 4);\n  L->top += 4;\n  luaD_call(L, L->top - 4, 0);\n}\n\n\nvoid luaV_gettable (lua_State *L, const TValue *t, TValue *key, StkId val) {\n  int loop;\n  for (loop = 0; loop < MAXTAGLOOP; loop++) {\n    const TValue *tm;\n    if (ttistable(t)) {  /* `t' is a table? */\n      Table *h = hvalue(t);\n      const TValue *res = luaH_get(h, key); /* do a primitive get */\n      if (!ttisnil(res) ||  /* result is no nil? */\n          (tm = fasttm(L, h->metatable, TM_INDEX)) == NULL) { /* or no TM? */\n        setobj2s(L, val, res);\n        return;\n      }\n      /* else will try the tag method */\n    }\n    else if (ttisnil(tm = luaT_gettmbyobj(L, t, TM_INDEX)))\n      luaG_typeerror(L, t, \"index\");\n    if (ttisfunction(tm)) {\n      callTMres(L, val, tm, t, key);\n      return;\n    }\n    t = tm;  /* else repeat with `tm' */ \n  }\n  luaG_runerror(L, \"loop in gettable\");\n}\n\n\nvoid luaV_settable (lua_State *L, const TValue *t, TValue *key, StkId val) {\n  int loop;\n  TValue temp;\n  for (loop = 0; loop < MAXTAGLOOP; loop++) {\n    const TValue *tm;\n    if (ttistable(t)) {  /* `t' is a table? */\n      Table *h = hvalue(t);\n      TValue *oldval = luaH_set(L, h, key); /* do a primitive set */\n      if (!ttisnil(oldval) ||  /* result is no nil? */\n          (tm = fasttm(L, h->metatable, TM_NEWINDEX)) == NULL) { /* or no TM? */\n        setobj2t(L, oldval, val);\n        h->flags = 0;\n        luaC_barriert(L, h, val);\n        return;\n      }\n      /* else will try the tag method */\n    }\n    else if (ttisnil(tm = luaT_gettmbyobj(L, t, TM_NEWINDEX)))\n      luaG_typeerror(L, t, \"index\");\n    if (ttisfunction(tm)) {\n      callTM(L, tm, t, key, val);\n      return;\n    }\n    /* else repeat with `tm' */\n    setobj(L, &temp, tm);  /* avoid pointing inside table (may rehash) */\n    t = &temp;\n  }\n  luaG_runerror(L, \"loop in settable\");\n}\n\n\nstatic int call_binTM (lua_State *L, const TValue *p1, const TValue *p2,\n                       StkId res, TMS event) {\n  const TValue *tm = luaT_gettmbyobj(L, p1, event);  /* try first operand */\n  if (ttisnil(tm))\n    tm = luaT_gettmbyobj(L, p2, event);  /* try second operand */\n  if (ttisnil(tm)) return 0;\n  callTMres(L, res, tm, p1, p2);\n  return 1;\n}\n\n\nstatic const TValue *get_compTM (lua_State *L, Table *mt1, Table *mt2,\n                                  TMS event) {\n  const TValue *tm1 = fasttm(L, mt1, event);\n  const TValue *tm2;\n  if (tm1 == NULL) return NULL;  /* no metamethod */\n  if (mt1 == mt2) return tm1;  /* same metatables => same metamethods */\n  tm2 = fasttm(L, mt2, event);\n  if (tm2 == NULL) return NULL;  /* no metamethod */\n  if (luaO_rawequalObj(tm1, tm2))  /* same metamethods? */\n    return tm1;\n  return NULL;\n}\n\n\nstatic int call_orderTM (lua_State *L, const TValue *p1, const TValue *p2,\n                         TMS event) {\n  const TValue *tm1 = luaT_gettmbyobj(L, p1, event);\n  const TValue *tm2;\n  if (ttisnil(tm1)) return -1;  /* no metamethod? */\n  tm2 = luaT_gettmbyobj(L, p2, event);\n  if (!luaO_rawequalObj(tm1, tm2))  /* different metamethods? */\n    return -1;\n  callTMres(L, L->top, tm1, p1, p2);\n  return !l_isfalse(L->top);\n}\n\n\nstatic int l_strcmp (const TString *ls, const TString *rs) {\n  const char *l = getstr(ls);\n  size_t ll = ls->tsv.len;\n  const char *r = getstr(rs);\n  size_t lr = rs->tsv.len;\n  for (;;) {\n    int temp = strcoll(l, r);\n    if (temp != 0) return temp;\n    else {  /* strings are equal up to a `\\0' */\n      size_t len = strlen(l);  /* index of first `\\0' in both strings */\n      if (len == lr)  /* r is finished? */\n        return (len == ll) ? 0 : 1;\n      else if (len == ll)  /* l is finished? */\n        return -1;  /* l is smaller than r (because r is not finished) */\n      /* both strings longer than `len'; go on comparing (after the `\\0') */\n      len++;\n      l += len; ll -= len; r += len; lr -= len;\n    }\n  }\n}\n\n\nint luaV_lessthan (lua_State *L, const TValue *l, const TValue *r) {\n  int res;\n  if (ttype(l) != ttype(r))\n    return luaG_ordererror(L, l, r);\n  else if (ttisnumber(l))\n    return luai_numlt(nvalue(l), nvalue(r));\n  else if (ttisstring(l))\n    return l_strcmp(rawtsvalue(l), rawtsvalue(r)) < 0;\n  else if ((res = call_orderTM(L, l, r, TM_LT)) != -1)\n    return res;\n  return luaG_ordererror(L, l, r);\n}\n\n\nstatic int lessequal (lua_State *L, const TValue *l, const TValue *r) {\n  int res;\n  if (ttype(l) != ttype(r))\n    return luaG_ordererror(L, l, r);\n  else if (ttisnumber(l))\n    return luai_numle(nvalue(l), nvalue(r));\n  else if (ttisstring(l))\n    return l_strcmp(rawtsvalue(l), rawtsvalue(r)) <= 0;\n  else if ((res = call_orderTM(L, l, r, TM_LE)) != -1)  /* first try `le' */\n    return res;\n  else if ((res = call_orderTM(L, r, l, TM_LT)) != -1)  /* else try `lt' */\n    return !res;\n  return luaG_ordererror(L, l, r);\n}\n\n\nint luaV_equalval (lua_State *L, const TValue *t1, const TValue *t2) {\n  const TValue *tm;\n  lua_assert(ttype(t1) == ttype(t2));\n  switch (ttype(t1)) {\n    case LUA_TNIL: return 1;\n    case LUA_TNUMBER: return luai_numeq(nvalue(t1), nvalue(t2));\n    case LUA_TBOOLEAN: return bvalue(t1) == bvalue(t2);  /* true must be 1 !! */\n    case LUA_TLIGHTUSERDATA: return pvalue(t1) == pvalue(t2);\n    case LUA_TUSERDATA: {\n      if (uvalue(t1) == uvalue(t2)) return 1;\n      tm = get_compTM(L, uvalue(t1)->metatable, uvalue(t2)->metatable,\n                         TM_EQ);\n      break;  /* will try TM */\n    }\n    case LUA_TTABLE: {\n      if (hvalue(t1) == hvalue(t2)) return 1;\n      tm = get_compTM(L, hvalue(t1)->metatable, hvalue(t2)->metatable, TM_EQ);\n      break;  /* will try TM */\n    }\n    default: return gcvalue(t1) == gcvalue(t2);\n  }\n  if (tm == NULL) return 0;  /* no TM? */\n  callTMres(L, L->top, tm, t1, t2);  /* call TM */\n  return !l_isfalse(L->top);\n}\n\n\nvoid luaV_concat (lua_State *L, int total, int last) {\n  do {\n    StkId top = L->base + last + 1;\n    int n = 2;  /* number of elements handled in this pass (at least 2) */\n    if (!(ttisstring(top-2) || ttisnumber(top-2)) || !tostring(L, top-1)) {\n      if (!call_binTM(L, top-2, top-1, top-2, TM_CONCAT))\n        luaG_concaterror(L, top-2, top-1);\n    } else if (tsvalue(top-1)->len == 0)  /* second op is empty? */\n      (void)tostring(L, top - 2);  /* result is first op (as string) */\n    else {\n      /* at least two string values; get as many as possible */\n      size_t tl = tsvalue(top-1)->len;\n      char *buffer;\n      int i;\n      /* collect total length */\n      for (n = 1; n < total && tostring(L, top-n-1); n++) {\n        size_t l = tsvalue(top-n-1)->len;\n        if (l >= MAX_SIZET - tl) luaG_runerror(L, \"string length overflow\");\n        tl += l;\n      }\n      buffer = luaZ_openspace(L, &G(L)->buff, tl);\n      tl = 0;\n      for (i=n; i>0; i--) {  /* concat all strings */\n        size_t l = tsvalue(top-i)->len;\n        memcpy(buffer+tl, svalue(top-i), l);\n        tl += l;\n      }\n      setsvalue2s(L, top-n, luaS_newlstr(L, buffer, tl));\n    }\n    total -= n-1;  /* got `n' strings to create 1 new */\n    last -= n-1;\n  } while (total > 1);  /* repeat until only 1 result left */\n}\n\n\nstatic void Arith (lua_State *L, StkId ra, const TValue *rb,\n                   const TValue *rc, TMS op) {\n  TValue tempb, tempc;\n  const TValue *b, *c;\n  if ((b = luaV_tonumber(rb, &tempb)) != NULL &&\n      (c = luaV_tonumber(rc, &tempc)) != NULL) {\n    lua_Number nb = nvalue(b), nc = nvalue(c);\n    switch (op) {\n      case TM_ADD: setnvalue(ra, luai_numadd(nb, nc)); break;\n      case TM_SUB: setnvalue(ra, luai_numsub(nb, nc)); break;\n      case TM_MUL: setnvalue(ra, luai_nummul(nb, nc)); break;\n      case TM_DIV: setnvalue(ra, luai_numdiv(nb, nc)); break;\n      case TM_MOD: setnvalue(ra, luai_nummod(nb, nc)); break;\n      case TM_POW: setnvalue(ra, luai_numpow(nb, nc)); break;\n      case TM_UNM: setnvalue(ra, luai_numunm(nb)); break;\n      default: lua_assert(0); break;\n    }\n  }\n  else if (!call_binTM(L, rb, rc, ra, op))\n    luaG_aritherror(L, rb, rc);\n}\n\n\n\n/*\n** some macros for common tasks in `luaV_execute'\n*/\n\n#define runtime_check(L, c)\t{ if (!(c)) break; }\n\n#define RA(i)\t(base+GETARG_A(i))\n/* to be used after possible stack reallocation */\n#define RB(i)\tcheck_exp(getBMode(GET_OPCODE(i)) == OpArgR, base+GETARG_B(i))\n#define RC(i)\tcheck_exp(getCMode(GET_OPCODE(i)) == OpArgR, base+GETARG_C(i))\n#define RKB(i)\tcheck_exp(getBMode(GET_OPCODE(i)) == OpArgK, \\\n\tISK(GETARG_B(i)) ? k+INDEXK(GETARG_B(i)) : base+GETARG_B(i))\n#define RKC(i)\tcheck_exp(getCMode(GET_OPCODE(i)) == OpArgK, \\\n\tISK(GETARG_C(i)) ? k+INDEXK(GETARG_C(i)) : base+GETARG_C(i))\n#define KBx(i)\tcheck_exp(getBMode(GET_OPCODE(i)) == OpArgK, k+GETARG_Bx(i))\n\n\n#define dojump(L,pc,i)\t{(pc) += (i); luai_threadyield(L);}\n\n\n#define Protect(x)\t{ L->savedpc = pc; {x;}; base = L->base; }\n\n\n#define arith_op(op,tm) { \\\n        TValue *rb = RKB(i); \\\n        TValue *rc = RKC(i); \\\n        if (ttisnumber(rb) && ttisnumber(rc)) { \\\n          lua_Number nb = nvalue(rb), nc = nvalue(rc); \\\n          setnvalue(ra, op(nb, nc)); \\\n        } \\\n        else \\\n          Protect(Arith(L, ra, rb, rc, tm)); \\\n      }\n\n\n\nvoid luaV_execute (lua_State *L, int nexeccalls) {\n  LClosure *cl;\n  StkId base;\n  TValue *k;\n  const Instruction *pc;\n reentry:  /* entry point */\n  lua_assert(isLua(L->ci));\n  pc = L->savedpc;\n  cl = &clvalue(L->ci->func)->l;\n  base = L->base;\n  k = cl->p->k;\n  /* main loop of interpreter */\n  for (;;) {\n    const Instruction i = *pc++;\n    StkId ra;\n    if ((L->hookmask & (LUA_MASKLINE | LUA_MASKCOUNT)) &&\n        (--L->hookcount == 0 || L->hookmask & LUA_MASKLINE)) {\n      traceexec(L, pc);\n      if (L->status == LUA_YIELD) {  /* did hook yield? */\n        L->savedpc = pc - 1;\n        return;\n      }\n      base = L->base;\n    }\n    /* warning!! several calls may realloc the stack and invalidate `ra' */\n    ra = RA(i);\n    lua_assert(base == L->base && L->base == L->ci->base);\n    lua_assert(base <= L->top && L->top <= L->stack + L->stacksize);\n    lua_assert(L->top == L->ci->top || luaG_checkopenop(i));\n    switch (GET_OPCODE(i)) {\n      case OP_MOVE: {\n        setobjs2s(L, ra, RB(i));\n        continue;\n      }\n      case OP_LOADK: {\n        setobj2s(L, ra, KBx(i));\n        continue;\n      }\n      case OP_LOADBOOL: {\n        setbvalue(ra, GETARG_B(i));\n        if (GETARG_C(i)) pc++;  /* skip next instruction (if C) */\n        continue;\n      }\n      case OP_LOADNIL: {\n        TValue *rb = RB(i);\n        do {\n          setnilvalue(rb--);\n        } while (rb >= ra);\n        continue;\n      }\n      case OP_GETUPVAL: {\n        int b = GETARG_B(i);\n        setobj2s(L, ra, cl->upvals[b]->v);\n        continue;\n      }\n      case OP_GETGLOBAL: {\n        TValue g;\n        TValue *rb = KBx(i);\n        sethvalue(L, &g, cl->env);\n        lua_assert(ttisstring(rb));\n        Protect(luaV_gettable(L, &g, rb, ra));\n        continue;\n      }\n      case OP_GETTABLE: {\n        Protect(luaV_gettable(L, RB(i), RKC(i), ra));\n        continue;\n      }\n      case OP_SETGLOBAL: {\n        TValue g;\n        sethvalue(L, &g, cl->env);\n        lua_assert(ttisstring(KBx(i)));\n        Protect(luaV_settable(L, &g, KBx(i), ra));\n        continue;\n      }\n      case OP_SETUPVAL: {\n        UpVal *uv = cl->upvals[GETARG_B(i)];\n        setobj(L, uv->v, ra);\n        luaC_barrier(L, uv, ra);\n        continue;\n      }\n      case OP_SETTABLE: {\n        Protect(luaV_settable(L, ra, RKB(i), RKC(i)));\n        continue;\n      }\n      case OP_NEWTABLE: {\n        int b = GETARG_B(i);\n        int c = GETARG_C(i);\n        sethvalue(L, ra, luaH_new(L, luaO_fb2int(b), luaO_fb2int(c)));\n        Protect(luaC_checkGC(L));\n        continue;\n      }\n      case OP_SELF: {\n        StkId rb = RB(i);\n        setobjs2s(L, ra+1, rb);\n        Protect(luaV_gettable(L, rb, RKC(i), ra));\n        continue;\n      }\n      case OP_ADD: {\n        arith_op(luai_numadd, TM_ADD);\n        continue;\n      }\n      case OP_SUB: {\n        arith_op(luai_numsub, TM_SUB);\n        continue;\n      }\n      case OP_MUL: {\n        arith_op(luai_nummul, TM_MUL);\n        continue;\n      }\n      case OP_DIV: {\n        arith_op(luai_numdiv, TM_DIV);\n        continue;\n      }\n      case OP_MOD: {\n        arith_op(luai_nummod, TM_MOD);\n        continue;\n      }\n      case OP_POW: {\n        arith_op(luai_numpow, TM_POW);\n        continue;\n      }\n      case OP_UNM: {\n        TValue *rb = RB(i);\n        if (ttisnumber(rb)) {\n          lua_Number nb = nvalue(rb);\n          setnvalue(ra, luai_numunm(nb));\n        }\n        else {\n          Protect(Arith(L, ra, rb, rb, TM_UNM));\n        }\n        continue;\n      }\n      case OP_NOT: {\n        int res = l_isfalse(RB(i));  /* next assignment may change this value */\n        setbvalue(ra, res);\n        continue;\n      }\n      case OP_LEN: {\n        const TValue *rb = RB(i);\n        switch (ttype(rb)) {\n          case LUA_TTABLE: {\n            setnvalue(ra, cast_num(luaH_getn(hvalue(rb))));\n            break;\n          }\n          case LUA_TSTRING: {\n            setnvalue(ra, cast_num(tsvalue(rb)->len));\n            break;\n          }\n          default: {  /* try metamethod */\n            Protect(\n              if (!call_binTM(L, rb, luaO_nilobject, ra, TM_LEN))\n                luaG_typeerror(L, rb, \"get length of\");\n            )\n          }\n        }\n        continue;\n      }\n      case OP_CONCAT: {\n        int b = GETARG_B(i);\n        int c = GETARG_C(i);\n        Protect(luaV_concat(L, c-b+1, c); luaC_checkGC(L));\n        setobjs2s(L, RA(i), base+b);\n        continue;\n      }\n      case OP_JMP: {\n        dojump(L, pc, GETARG_sBx(i));\n        continue;\n      }\n      case OP_EQ: {\n        TValue *rb = RKB(i);\n        TValue *rc = RKC(i);\n        Protect(\n          if (equalobj(L, rb, rc) == GETARG_A(i))\n            dojump(L, pc, GETARG_sBx(*pc));\n        )\n        pc++;\n        continue;\n      }\n      case OP_LT: {\n        Protect(\n          if (luaV_lessthan(L, RKB(i), RKC(i)) == GETARG_A(i))\n            dojump(L, pc, GETARG_sBx(*pc));\n        )\n        pc++;\n        continue;\n      }\n      case OP_LE: {\n        Protect(\n          if (lessequal(L, RKB(i), RKC(i)) == GETARG_A(i))\n            dojump(L, pc, GETARG_sBx(*pc));\n        )\n        pc++;\n        continue;\n      }\n      case OP_TEST: {\n        if (l_isfalse(ra) != GETARG_C(i))\n          dojump(L, pc, GETARG_sBx(*pc));\n        pc++;\n        continue;\n      }\n      case OP_TESTSET: {\n        TValue *rb = RB(i);\n        if (l_isfalse(rb) != GETARG_C(i)) {\n          setobjs2s(L, ra, rb);\n          dojump(L, pc, GETARG_sBx(*pc));\n        }\n        pc++;\n        continue;\n      }\n      case OP_CALL: {\n        int b = GETARG_B(i);\n        int nresults = GETARG_C(i) - 1;\n        if (b != 0) L->top = ra+b;  /* else previous instruction set top */\n        L->savedpc = pc;\n        switch (luaD_precall(L, ra, nresults)) {\n          case PCRLUA: {\n            nexeccalls++;\n            goto reentry;  /* restart luaV_execute over new Lua function */\n          }\n          case PCRC: {\n            /* it was a C function (`precall' called it); adjust results */\n            if (nresults >= 0) L->top = L->ci->top;\n            base = L->base;\n            continue;\n          }\n          default: {\n            return;  /* yield */\n          }\n        }\n      }\n      case OP_TAILCALL: {\n        int b = GETARG_B(i);\n        if (b != 0) L->top = ra+b;  /* else previous instruction set top */\n        L->savedpc = pc;\n        lua_assert(GETARG_C(i) - 1 == LUA_MULTRET);\n        switch (luaD_precall(L, ra, LUA_MULTRET)) {\n          case PCRLUA: {\n            /* tail call: put new frame in place of previous one */\n            CallInfo *ci = L->ci - 1;  /* previous frame */\n            int aux;\n            StkId func = ci->func;\n            StkId pfunc = (ci+1)->func;  /* previous function index */\n            if (L->openupval) luaF_close(L, ci->base);\n            L->base = ci->base = ci->func + ((ci+1)->base - pfunc);\n            for (aux = 0; pfunc+aux < L->top; aux++)  /* move frame down */\n              setobjs2s(L, func+aux, pfunc+aux);\n            ci->top = L->top = func+aux;  /* correct top */\n            lua_assert(L->top == L->base + clvalue(func)->l.p->maxstacksize);\n            ci->savedpc = L->savedpc;\n            ci->tailcalls++;  /* one more call lost */\n            L->ci--;  /* remove new frame */\n            goto reentry;\n          }\n          case PCRC: {  /* it was a C function (`precall' called it) */\n            base = L->base;\n            continue;\n          }\n          default: {\n            return;  /* yield */\n          }\n        }\n      }\n      case OP_RETURN: {\n        int b = GETARG_B(i);\n        if (b != 0) L->top = ra+b-1;\n        if (L->openupval) luaF_close(L, base);\n        L->savedpc = pc;\n        b = luaD_poscall(L, ra);\n        if (--nexeccalls == 0)  /* was previous function running `here'? */\n          return;  /* no: return */\n        else {  /* yes: continue its execution */\n          if (b) L->top = L->ci->top;\n          lua_assert(isLua(L->ci));\n          lua_assert(GET_OPCODE(*((L->ci)->savedpc - 1)) == OP_CALL);\n          goto reentry;\n        }\n      }\n      case OP_FORLOOP: {\n        lua_Number step = nvalue(ra+2);\n        lua_Number idx = luai_numadd(nvalue(ra), step); /* increment index */\n        lua_Number limit = nvalue(ra+1);\n        if (luai_numlt(0, step) ? luai_numle(idx, limit)\n                                : luai_numle(limit, idx)) {\n          dojump(L, pc, GETARG_sBx(i));  /* jump back */\n          setnvalue(ra, idx);  /* update internal index... */\n          setnvalue(ra+3, idx);  /* ...and external index */\n        }\n        continue;\n      }\n      case OP_FORPREP: {\n        const TValue *init = ra;\n        const TValue *plimit = ra+1;\n        const TValue *pstep = ra+2;\n        L->savedpc = pc;  /* next steps may throw errors */\n        if (!tonumber(init, ra))\n          luaG_runerror(L, LUA_QL(\"for\") \" initial value must be a number\");\n        else if (!tonumber(plimit, ra+1))\n          luaG_runerror(L, LUA_QL(\"for\") \" limit must be a number\");\n        else if (!tonumber(pstep, ra+2))\n          luaG_runerror(L, LUA_QL(\"for\") \" step must be a number\");\n        setnvalue(ra, luai_numsub(nvalue(ra), nvalue(pstep)));\n        dojump(L, pc, GETARG_sBx(i));\n        continue;\n      }\n      case OP_TFORLOOP: {\n        StkId cb = ra + 3;  /* call base */\n        setobjs2s(L, cb+2, ra+2);\n        setobjs2s(L, cb+1, ra+1);\n        setobjs2s(L, cb, ra);\n        L->top = cb+3;  /* func. + 2 args (state and index) */\n        Protect(luaD_call(L, cb, GETARG_C(i)));\n        L->top = L->ci->top;\n        cb = RA(i) + 3;  /* previous call may change the stack */\n        if (!ttisnil(cb)) {  /* continue loop? */\n          setobjs2s(L, cb-1, cb);  /* save control variable */\n          dojump(L, pc, GETARG_sBx(*pc));  /* jump back */\n        }\n        pc++;\n        continue;\n      }\n      case OP_SETLIST: {\n        int n = GETARG_B(i);\n        int c = GETARG_C(i);\n        int last;\n        Table *h;\n        if (n == 0) {\n          n = cast_int(L->top - ra) - 1;\n          L->top = L->ci->top;\n        }\n        if (c == 0) c = cast_int(*pc++);\n        runtime_check(L, ttistable(ra));\n        h = hvalue(ra);\n        last = ((c-1)*LFIELDS_PER_FLUSH) + n;\n        if (last > h->sizearray)  /* needs more space? */\n          luaH_resizearray(L, h, last);  /* pre-alloc it at once */\n        for (; n > 0; n--) {\n          TValue *val = ra+n;\n          setobj2t(L, luaH_setnum(L, h, last--), val);\n          luaC_barriert(L, h, val);\n        }\n        continue;\n      }\n      case OP_CLOSE: {\n        luaF_close(L, ra);\n        continue;\n      }\n      case OP_CLOSURE: {\n        Proto *p;\n        Closure *ncl;\n        int nup, j;\n        p = cl->p->p[GETARG_Bx(i)];\n        nup = p->nups;\n        ncl = luaF_newLclosure(L, nup, cl->env);\n        ncl->l.p = p;\n        for (j=0; j<nup; j++, pc++) {\n          if (GET_OPCODE(*pc) == OP_GETUPVAL)\n            ncl->l.upvals[j] = cl->upvals[GETARG_B(*pc)];\n          else {\n            lua_assert(GET_OPCODE(*pc) == OP_MOVE);\n            ncl->l.upvals[j] = luaF_findupval(L, base + GETARG_B(*pc));\n          }\n        }\n        setclvalue(L, ra, ncl);\n        Protect(luaC_checkGC(L));\n        continue;\n      }\n      case OP_VARARG: {\n        int b = GETARG_B(i) - 1;\n        int j;\n        CallInfo *ci = L->ci;\n        int n = cast_int(ci->base - ci->func) - cl->p->numparams - 1;\n        if (b == LUA_MULTRET) {\n          Protect(luaD_checkstack(L, n));\n          ra = RA(i);  /* previous call may change the stack */\n          b = n;\n          L->top = ra + n;\n        }\n        for (j = 0; j < b; j++) {\n          if (j < n) {\n            setobjs2s(L, ra + j, ci->base - n + j);\n          }\n          else {\n            setnilvalue(ra + j);\n          }\n        }\n        continue;\n      }\n    }\n  }\n}\n\n"
  },
  {
    "path": "src/lvm.h",
    "content": "/*\n** $Id: lvm.h,v 2.5.1.1 2007/12/27 13:02:25 roberto Exp $\n** Lua virtual machine\n** See Copyright Notice in lua.h\n*/\n\n#ifndef lvm_h\n#define lvm_h\n\n\n#include \"ldo.h\"\n#include \"lobject.h\"\n#include \"ltm.h\"\n\n\n#define tostring(L,o) ((ttype(o) == LUA_TSTRING) || (luaV_tostring(L, o)))\n\n#define tonumber(o,n)\t(ttype(o) == LUA_TNUMBER || \\\n                         (((o) = luaV_tonumber(o,n)) != NULL))\n\n#define equalobj(L,o1,o2) \\\n\t(ttype(o1) == ttype(o2) && luaV_equalval(L, o1, o2))\n\n\nLUAI_FUNC int luaV_lessthan (lua_State *L, const TValue *l, const TValue *r);\nLUAI_FUNC int luaV_equalval (lua_State *L, const TValue *t1, const TValue *t2);\nLUAI_FUNC const TValue *luaV_tonumber (const TValue *obj, TValue *n);\nLUAI_FUNC int luaV_tostring (lua_State *L, StkId obj);\nLUAI_FUNC void luaV_gettable (lua_State *L, const TValue *t, TValue *key,\n                                            StkId val);\nLUAI_FUNC void luaV_settable (lua_State *L, const TValue *t, TValue *key,\n                                            StkId val);\nLUAI_FUNC void luaV_execute (lua_State *L, int nexeccalls);\nLUAI_FUNC void luaV_concat (lua_State *L, int total, int last);\n\n#endif\n"
  },
  {
    "path": "src/lzio.c",
    "content": "/*\n** $Id: lzio.c,v 1.31.1.1 2007/12/27 13:02:25 roberto Exp $\n** a generic input stream interface\n** See Copyright Notice in lua.h\n*/\n\n\n#include <string.h>\n\n#define lzio_c\n#define LUA_CORE\n\n#include \"lua.h\"\n\n#include \"llimits.h\"\n#include \"lmem.h\"\n#include \"lstate.h\"\n#include \"lzio.h\"\n\n\nint luaZ_fill (ZIO *z) {\n  size_t size;\n  lua_State *L = z->L;\n  const char *buff;\n  lua_unlock(L);\n  buff = z->reader(L, z->data, &size);\n  lua_lock(L);\n  if (buff == NULL || size == 0) return EOZ;\n  z->n = size - 1;\n  z->p = buff;\n  return char2int(*(z->p++));\n}\n\n\nint luaZ_lookahead (ZIO *z) {\n  if (z->n == 0) {\n    if (luaZ_fill(z) == EOZ)\n      return EOZ;\n    else {\n      z->n++;  /* luaZ_fill removed first byte; put back it */\n      z->p--;\n    }\n  }\n  return char2int(*z->p);\n}\n\n\nvoid luaZ_init (lua_State *L, ZIO *z, lua_Reader reader, void *data) {\n  z->L = L;\n  z->reader = reader;\n  z->data = data;\n  z->n = 0;\n  z->p = NULL;\n}\n\n\n/* --------------------------------------------------------------- read --- */\nsize_t luaZ_read (ZIO *z, void *b, size_t n) {\n  while (n) {\n    size_t m;\n    if (luaZ_lookahead(z) == EOZ)\n      return n;  /* return number of missing bytes */\n    m = (n <= z->n) ? n : z->n;  /* min. between n and z->n */\n    memcpy(b, z->p, m);\n    z->n -= m;\n    z->p += m;\n    b = (char *)b + m;\n    n -= m;\n  }\n  return 0;\n}\n\n/* ------------------------------------------------------------------------ */\nchar *luaZ_openspace (lua_State *L, Mbuffer *buff, size_t n) {\n  if (n > buff->buffsize) {\n    if (n < LUA_MINBUFFER) n = LUA_MINBUFFER;\n    luaZ_resizebuffer(L, buff, n);\n  }\n  return buff->buffer;\n}\n\n\n"
  },
  {
    "path": "src/lzio.h",
    "content": "/*\n** $Id: lzio.h,v 1.21.1.1 2007/12/27 13:02:25 roberto Exp $\n** Buffered streams\n** See Copyright Notice in lua.h\n*/\n\n\n#ifndef lzio_h\n#define lzio_h\n\n#include \"lua.h\"\n\n#include \"lmem.h\"\n\n\n#define EOZ\t(-1)\t\t\t/* end of stream */\n\ntypedef struct Zio ZIO;\n\n#define char2int(c)\tcast(int, cast(unsigned char, (c)))\n\n#define zgetc(z)  (((z)->n--)>0 ?  char2int(*(z)->p++) : luaZ_fill(z))\n\ntypedef struct Mbuffer {\n  char *buffer;\n  size_t n;\n  size_t buffsize;\n} Mbuffer;\n\n#define luaZ_initbuffer(L, buff) ((buff)->buffer = NULL, (buff)->buffsize = 0)\n\n#define luaZ_buffer(buff)\t((buff)->buffer)\n#define luaZ_sizebuffer(buff)\t((buff)->buffsize)\n#define luaZ_bufflen(buff)\t((buff)->n)\n\n#define luaZ_resetbuffer(buff) ((buff)->n = 0)\n\n\n#define luaZ_resizebuffer(L, buff, size) \\\n\t(luaM_reallocvector(L, (buff)->buffer, (buff)->buffsize, size, char), \\\n\t(buff)->buffsize = size)\n\n#define luaZ_freebuffer(L, buff)\tluaZ_resizebuffer(L, buff, 0)\n\n\nLUAI_FUNC char *luaZ_openspace (lua_State *L, Mbuffer *buff, size_t n);\nLUAI_FUNC void luaZ_init (lua_State *L, ZIO *z, lua_Reader reader,\n                                        void *data);\nLUAI_FUNC size_t luaZ_read (ZIO* z, void* b, size_t n);\t/* read next n bytes */\nLUAI_FUNC int luaZ_lookahead (ZIO *z);\n\n\n\n/* --------- Private Part ------------------ */\n\nstruct Zio {\n  size_t n;\t\t\t/* bytes still unread */\n  const char *p;\t\t/* current position in buffer */\n  lua_Reader reader;\n  void* data;\t\t\t/* additional data */\n  lua_State *L;\t\t\t/* Lua state (for reader) */\n};\n\n\nLUAI_FUNC int luaZ_fill (ZIO *z);\n\n#endif\n"
  },
  {
    "path": "src/realpath.c",
    "content": "/*\n * Copyright (c) 2003 Constantin S. Svintsoff <kostik@iclub.nsu.ru>\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n * 1. Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n * 2. Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n * 3. The names of the authors may not be used to endorse or promote\n *    products derived from this software without specific prior written\n *    permission.\n *\n * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND\n * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\n * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE\n * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\n * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS\n * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)\n * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT\n * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY\n * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF\n * SUCH DAMAGE.\n */\n\n#include <sys/cdefs.h>\n\n#include <sys/param.h>\n#include <sys/stat.h>\n\n#include <errno.h>\n#include <stdlib.h>\n#include <string.h>\n#include <unistd.h>\n\n#if !__APPLE__\nextern size_t strlcpy(char * dst, const char * src, size_t size);\nextern size_t strlcat(char * dst, const char * src, size_t size);\n#endif\n\n/*\n * Find the real name of path, by removing all \".\", \"..\" and symlink\n * components.  Returns (resolved) on success, or (NULL) on failure,\n * in which case the path which caused trouble is left in (resolved).\n */\nchar *\nrealpath(const char * __restrict path, char * __restrict resolved)\n{\n\tstruct stat sb;\n\tchar *p, *q, *s;\n\tsize_t left_len, resolved_len;\n\tunsigned symlinks;\n\tint m, slen;\n\tchar left[PATH_MAX], next_token[PATH_MAX], symlink[PATH_MAX];\n\n\tif (path == NULL) {\n\t\terrno = EINVAL;\n\t\treturn (NULL);\n\t}\n\tif (path[0] == '\\0') {\n\t\terrno = ENOENT;\n\t\treturn (NULL);\n\t}\n\tif (resolved == NULL) {\n\t\tresolved = malloc(PATH_MAX);\n\t\tif (resolved == NULL)\n\t\t\treturn (NULL);\n\t\tm = 1;\n\t} else\n\t\tm = 0;\n\tsymlinks = 0;\n\tif (path[0] == '/') {\n\t\tresolved[0] = '/';\n\t\tresolved[1] = '\\0';\n\t\tif (path[1] == '\\0')\n\t\t\treturn (resolved);\n\t\tresolved_len = 1;\n\t\tleft_len = strlcpy(left, path + 1, sizeof(left));\n\t} else {\n\t\tif (getcwd(resolved, PATH_MAX) == NULL) {\n\t\t\tif (m)\n\t\t\t\tfree(resolved);\n\t\t\telse {\n\t\t\t\tresolved[0] = '.';\n\t\t\t\tresolved[1] = '\\0';\n\t\t\t}\n\t\t\treturn (NULL);\n\t\t}\n\t\tresolved_len = strlen(resolved);\n\t\tleft_len = strlcpy(left, path, sizeof(left));\n\t}\n\tif (left_len >= sizeof(left) || resolved_len >= PATH_MAX) {\n\t\tif (m)\n\t\t\tfree(resolved);\n\t\terrno = ENAMETOOLONG;\n\t\treturn (NULL);\n\t}\n\n\t/*\n\t * Iterate over path components in `left'.\n\t */\n\twhile (left_len != 0) {\n\t\t/*\n\t\t * Extract the next path component and adjust `left'\n\t\t * and its length.\n\t\t */\n\t\tp = strchr(left, '/');\n\t\ts = p ? p : left + left_len;\n\t\tif (s - left >= sizeof(next_token)) {\n\t\t\tif (m)\n\t\t\t\tfree(resolved);\n\t\t\terrno = ENAMETOOLONG;\n\t\t\treturn (NULL);\n\t\t}\n\t\tmemcpy(next_token, left, s - left);\n\t\tnext_token[s - left] = '\\0';\n\t\tleft_len -= s - left;\n\t\tif (p != NULL)\n\t\t\tmemmove(left, s + 1, left_len + 1);\n\t\tif (resolved[resolved_len - 1] != '/') {\n\t\t\tif (resolved_len + 1 >= PATH_MAX) {\n\t\t\t\tif (m)\n\t\t\t\t\tfree(resolved);\n\t\t\t\terrno = ENAMETOOLONG;\n\t\t\t\treturn (NULL);\n\t\t\t}\n\t\t\tresolved[resolved_len++] = '/';\n\t\t\tresolved[resolved_len] = '\\0';\n\t\t}\n\t\tif (next_token[0] == '\\0') {\n\t\t\t/*\n\t\t\t * Handle consequential slashes.  The path\n\t\t\t * before slash shall point to a directory.\n\t\t\t *\n\t\t\t * Only the trailing slashes are not covered\n\t\t\t * by other checks in the loop, but we verify\n\t\t\t * the prefix for any (rare) \"//\" or \"/\\0\"\n\t\t\t * occurrence to not implement lookahead.\n\t\t\t */\n\t\t\tif (lstat(resolved, &sb) != 0) {\n\t\t\t\tif (m)\n\t\t\t\t\tfree(resolved);\n\t\t\t\treturn (NULL);\n\t\t\t}\n\t\t\tif (!S_ISDIR(sb.st_mode)) {\n\t\t\t\tif (m)\n\t\t\t\t\tfree(resolved);\n\t\t\t\terrno = ENOTDIR;\n\t\t\t\treturn (NULL);\n\t\t\t}\n\t\t\tcontinue;\n\t\t}\n\t\telse if (strcmp(next_token, \".\") == 0)\n\t\t\tcontinue;\n\t\telse if (strcmp(next_token, \"..\") == 0) {\n\t\t\t/*\n\t\t\t * Strip the last path component except when we have\n\t\t\t * single \"/\"\n\t\t\t */\n\t\t\tif (resolved_len > 1) {\n\t\t\t\tresolved[resolved_len - 1] = '\\0';\n\t\t\t\tq = strrchr(resolved, '/') + 1;\n\t\t\t\t*q = '\\0';\n\t\t\t\tresolved_len = q - resolved;\n\t\t\t}\n\t\t\tcontinue;\n\t\t}\n\n\t\t/*\n\t\t * Append the next path component and lstat() it.\n\t\t */\n\t\tresolved_len = strlcat(resolved, next_token, PATH_MAX);\n\t\tif (resolved_len >= PATH_MAX) {\n\t\t\tif (m)\n\t\t\t\tfree(resolved);\n\t\t\terrno = ENAMETOOLONG;\n\t\t\treturn (NULL);\n\t\t}\n\t\tif (lstat(resolved, &sb) != 0) {\n\t\t\tif (m)\n\t\t\t\tfree(resolved);\n\t\t\treturn (NULL);\n\t\t}\n\t\tif (S_ISLNK(sb.st_mode)) {\n\t\t\tif (symlinks++ > MAXSYMLINKS) {\n\t\t\t\tif (m)\n\t\t\t\t\tfree(resolved);\n\t\t\t\terrno = ELOOP;\n\t\t\t\treturn (NULL);\n\t\t\t}\n\t\t\tslen = readlink(resolved, symlink, sizeof(symlink) - 1);\n\t\t\tif (slen < 0) {\n\t\t\t\tif (m)\n\t\t\t\t\tfree(resolved);\n\t\t\t\treturn (NULL);\n\t\t\t}\n\t\t\tsymlink[slen] = '\\0';\n\t\t\tif (symlink[0] == '/') {\n\t\t\t\tresolved[1] = 0;\n\t\t\t\tresolved_len = 1;\n\t\t\t} else if (resolved_len > 1) {\n\t\t\t\t/* Strip the last path component. */\n\t\t\t\tresolved[resolved_len - 1] = '\\0';\n\t\t\t\tq = strrchr(resolved, '/') + 1;\n\t\t\t\t*q = '\\0';\n\t\t\t\tresolved_len = q - resolved;\n\t\t\t}\n\n\t\t\t/*\n\t\t\t * If there are any path components left, then\n\t\t\t * append them to symlink. The result is placed\n\t\t\t * in `left'.\n\t\t\t */\n\t\t\tif (p != NULL) {\n\t\t\t\tif (symlink[slen - 1] != '/') {\n\t\t\t\t\tif (slen + 1 >= sizeof(symlink)) {\n\t\t\t\t\t\tif (m)\n\t\t\t\t\t\t\tfree(resolved);\n\t\t\t\t\t\terrno = ENAMETOOLONG;\n\t\t\t\t\t\treturn (NULL);\n\t\t\t\t\t}\n\t\t\t\t\tsymlink[slen] = '/';\n\t\t\t\t\tsymlink[slen + 1] = 0;\n\t\t\t\t}\n\t\t\t\tleft_len = strlcat(symlink, left,\n\t\t\t\t    sizeof(symlink));\n\t\t\t\tif (left_len >= sizeof(left)) {\n\t\t\t\t\tif (m)\n\t\t\t\t\t\tfree(resolved);\n\t\t\t\t\terrno = ENAMETOOLONG;\n\t\t\t\t\treturn (NULL);\n\t\t\t\t}\n\t\t\t}\n\t\t\tleft_len = strlcpy(left, symlink, sizeof(left));\n\t\t}\n\t}\n\n\t/*\n\t * Remove trailing slash except when the resolved pathname\n\t * is a single \"/\".\n\t */\n\tif (resolved_len > 1 && resolved[resolved_len - 1] == '/')\n\t\tresolved[resolved_len - 1] = '\\0';\n\treturn (resolved);\n}\n"
  },
  {
    "path": "src/task.lua",
    "content": "----------------------------------------------------------------------------\n-- Go style Channels for Lua\n-- https://github.com/majek/lua-channels\n--\n-- This code is derived from libtask library by Russ Cox, mainly from\n-- channel.c. Semantically channels as implemented here are quite\n-- similar to channels from the Go language.\n--\n-- Usage (we're using unbuffered channel here):\n--\n-- local task = require('task')\n--\n-- local function counter(channel)\n--    local i = 1\n--    while true do\n--        channel:send(i)\n--        i = i + 1\n--    end\n-- end\n--\n-- local function main()\n--     local channel = task.Channel:new()\n--     task.spawn(counter, channel)\n--     assert(channel:recv() == 1)\n--     assert(channel:recv() == 2)\n--     assert(channel:recv() == 3)\n-- end\n--\n-- task.spawn(main)\n-- task.scheduler()\n--\n--\n-- This module exposes:\n--\n--  task.spawn(fun, [...]) - run fun as a coroutine with given\n--                         parameters. You should use this instead of\n--                         coroutine.create()\n--\n--  task.scheduler() - can be run only from the main thread, executes\n--                     all the stuff, resumes the coroutines that are\n--                     blocked on channels that became available. You\n--                     can only do non-blocking sends / receives from\n--                     the main thread.\n--\n--  task.Channel:new([buffer size]) - create a new channel with given size\n--\n--  task.chanalt(alts, can_block) - run alt / select / multiplex over\n--                                  the alts structure. For example:\n--\n-- task.chanalt({{c = channel_1, op = task.RECV},\n--               {c = channel_2, op = task.SEND, p = \"hello\"}}, true)\n--\n-- This will block current coroutine until it's possible to receive\n-- from channel_1 or send to channel_2. chanalt returns a number of\n-- statement from alts that succeeded (1 or 2 here) and a received\n-- value if executed statement was RECV.\n--\n-- Finally, if two alt statements can be fulfilled at the same time,\n-- we use math.random() to decide which one should go first. So it\n-- makes sense to initialize seed with something random. If you don't\n-- have access to an entropy source you can do:\n--   math.randomseed(os.time())\n-- but beware, the results of random() will predictable to a attacker.\n----------------------------------------------------------------------------\n\nlocal _M = {}\n\n-- Constants\nlocal RECV = 0x1\nlocal SEND = 0x2\nlocal NOP  = 0x3\n\n-- Global objects for scheduler\nlocal tasks_runnable = {}       -- list of coroutines ready to be resumed\n\n\n----------------------------------------------------------------------------\n--- Helpers\n\nlocal function random_choice(arr)\n   if #arr > 1 then\n      return arr[math.random(#arr)]\n   else\n      return arr[1]\n   end\nend\n\n-- Specialised Set data structure (with random element selection)\nlocal Set = {\n   new = function(self)\n      local o = {a = {}, l = {}}; setmetatable(o, self); self.__index = self\n      return o\n   end,\n\n   add = function(self, v)\n      local a, l = self.a, self.l\n      if a[v] == nil then\n         table.insert(l, v)\n         a[v] = #l\n         return true\n      end\n   end,\n\n   remove = function(self, v)\n      local a, l = self.a, self.l\n      local i = a[v]\n      if i > 0 then\n         local t = l[#l]\n         a[t], l[i] = i, t\n         a[i], l[#l] = nil, nil\n         return true\n      end\n   end,\n\n   random = function(self, v)\n      return random_choice(self.l)\n   end,\n\n   len = function(self)\n      return #self.l\n   end,\n}\n\n-- Circular Buffer data structure\nlocal CircularBuffer = {\n   new = function(self, size)\n      local o = {b = {}, slots = size + 1, size = size, l = 0, r = 0}\n      setmetatable(o, self); self.__index = self\n      return o\n   end,\n\n   len = function(self)\n      return (self.r - self.l) % self.slots\n   end,\n\n   pop = function(self)\n      assert(self.l ~= self.r)\n      local v = self.b[self.l]\n      self.l = (self.l + 1) % self.slots\n      return v\n   end,\n\n   push = function(self, v)\n      self.b[self.r] = v\n      self.r = (self.r + 1) % self.slots\n      assert(self.l ~= self.r)\n   end,\n}\n\n----------------------------------------------------------------------------\n-- Scheduling\n--\n-- Tasks ready to be run are placed on a stack and it's possible to\n-- starve a coroutine.\nlocal function scheduler()\n   local self_coro, is_main = coroutine.running()\n\n   -- We actually don't care if scheduler is run from the main\n   -- coroutine. But we do need to make sure that user doesn't do\n   -- blocking operation from it, as it can't yield.\n\n   -- Be compatible with 5.1 and 5.2\n   assert(not(self_coro ~= nil and is_main ~= true),\n          \"Scheduler must be run from the main coroutine.\")\n\n   local i = 0\n   while #tasks_runnable > 0 do\n      local co = table.remove(tasks_runnable)\n      local okay, emsg = coroutine.resume(co)\n      if not okay then\n         error(emsg)\n      end\n      i = i + 1\n   end\n   return i\nend\n\nlocal function task_ready(co)\n   table.insert(tasks_runnable, co)\nend\n\nlocal function spawn(fun, ...)\n   local args = {...}\n\n   local f = function()\n      fun(unpack(args))\n   end\n   local co = coroutine.create(f)\n   task_ready(co)\nend\n\n----------------------------------------------------------------------------\n-- Channels - chanalt and helpers\n\n-- Given two Alts from a single channel exchange data between\n-- them. It's implied that one is RECV and another is SEND. Channel\n-- may be buffered.\nlocal function altcopy(a, b)\n   local r, s, c = a, b, a.c\n   if r.op == SEND then\n      r, s = s, r\n   end\n\n   assert(s == nil or s.op == SEND)\n   assert(r == nil or r.op == RECV)\n\n   -- Channel is empty or unbuffered, copy directly\n   if s ~= nil and r and c._buf:len() == 0 then\n      r.alt_array.value = s.p\n      return\n   end\n\n   -- Otherwise it's always okay to receive and then send.\n   if r ~= nil then\n      r.alt_array.value = c._buf:pop()\n   end\n   if s ~= nil then\n      c._buf:push(s.p)\n   end\nend\n\n-- Given enqueued alt_array from a chanalt statement remove all alts\n-- from the associated channels.\nlocal function altalldequeue(alt_array)\n   for i = 1, #alt_array do\n      local a = alt_array[i]\n      if a.op == RECV or a.op == SEND then\n         a.c:_get_alts(a.op):remove(a)\n      end\n   end\nend\n\n-- Can this Alt be execed without blocking?\nlocal function altcanexec(a)\n   local c, op = a.c, a.op\n   if c._buf.size == 0 then\n      if op ~= NOP then\n         return c:_get_other_alts(op):len() > 0\n      end\n   else\n      if op == SEND then\n         return c._buf:len() < c._buf.size\n      elseif op == RECV then\n         return c._buf:len() > 0\n      end\n   end\nend\n\n-- Alt can be execed so find a counterpart Alt and exec it!\nlocal function altexec(a)\n   local c, op = a.c, a.op\n   local other_alts = c:_get_other_alts(op)\n   local other_a = other_alts:random()\n   -- other_a may be nil\n   altcopy(a, other_a)\n   if other_a ~= nil then\n      -- Disengage from channels used by the other Alt and make it ready.\n      altalldequeue(other_a.alt_array)\n      other_a.alt_array.resolved = other_a.alt_index\n      task_ready(other_a.alt_array.task)\n   end\nend\n\nfunction ischannel(x)\n    return type(x) == \"table\" and x.__index == _M.Channel\nend\n\n-- The main entry point. Call it `alt` or `select` or just a\n-- multiplexing statement. This is user facing function so make sure\n-- the parameters passed are sane.\nlocal function chanalt(alt_array, canblock)\n   assert(#alt_array)\n\n   local list_of_canexec_i = {}\n   for i = 1, #alt_array do\n      local a = alt_array[i]\n      a.alt_array = alt_array\n      a.alt_index = i\n      assert(type(a.op) == \"number\" and\n                (a.op == RECV or a.op == SEND or a.op == NOP),\n             \"op field must be RECV, SEND or NOP in alt\")\n      assert(ischannel(a.c),\n             \"pass valid channel to a c field of alt\")\n      if altcanexec(a) == true then\n         table.insert(list_of_canexec_i, i)\n      end\n   end\n\n   if #list_of_canexec_i > 0 then\n      local i = random_choice(list_of_canexec_i)\n      altexec(alt_array[i])\n      return i, alt_array.value\n   end\n\n   if canblock ~= true then\n      return nil\n   end\n\n   local self_coro, is_main = coroutine.running()\n   alt_array.task = self_coro\n   assert(self_coro ~= nil and is_main ~= true,\n          \"Unable to block from the main thread, run scheduler.\")\n\n   for i = 1, #alt_array do\n      local a = alt_array[i]\n      if a.op ~= NOP then\n         a.c:_get_alts(a.op):add(a)\n      end\n   end\n\n   -- Make sure we're not woken by someone who is not the scheduler.\n   alt_array.resolved = nil\n   coroutine.yield()\n   assert(alt_array.resolved > 0)\n\n   local r = alt_array.resolved\n   return r, alt_array.value\nend\n\n\n----------------------------------------------------------------------------\n-- Channel object\n\nlocal Channel = {\n   new = function(self, buf_size)\n      local o = {}; setmetatable(o, self); self.__index = self\n      o._buf = CircularBuffer:new(buf_size or 0)\n      o._recv_alts, o._send_alts = Set:new(), Set:new()\n      return o\n   end,\n\n   send = function(self, msg)\n      assert(chanalt({{c = self, op = SEND, p = msg}}, true) == 1)\n      return true\n   end,\n\n   recv = function(self)\n      local alts = {{c = self, op = RECV}}\n      local s, msg = chanalt(alts, true)\n      assert(s == 1)\n      return msg\n   end,\n\n   nbsend = function(self, msg)\n      local s = chanalt({{c = self, op = SEND, p = msg}}, false)\n      return s == 1\n   end,\n\n   nbrecv = function(self)\n      local s, msg = chanalt({{c = self, op = RECV}}, false)\n      return s == 1, msg\n   end,\n\n   _get_alts = function(self, op)\n      return (op == RECV) and self._recv_alts or self._send_alts\n   end,\n\n   _get_other_alts = function(self, op)\n      return (op == SEND) and self._recv_alts or self._send_alts\n   end,\n\n   __tostring = function(self)\n      return string.format(\"<Channel size=%i/%i send_alt=%i recv_alt=%i>\",\n                           self._buf:len(), self._buf.size, self._send_alts:len(),\n                           self._recv_alts:len())\n   end,\n\n   __call = function(self)\n      local function f(s, v)\n         return true, self:recv()\n      end\n      return f, nil, nil\n   end,\n}\n\n----------------------------------------------------------------------------\n-- Public interface\n\n_M.scheduler = scheduler\n_M.spawn     = spawn\n_M.Channel   = Channel\n_M.chanalt   = chanalt\n_M.RECV      = RECV\n_M.SEND      = SEND\n_M.NOP       = NOP\n\n----------------------------------------------------------------------------\n----------------------------------------------------------------------------\n-- Tests\n\nlocal task = _M\n\nfunction test_counter()\n   local done\n   local function counter(c)\n      local i = 1\n      while true do\n         c:send(i)\n         i = i + 1\n      end\n   end\n   local function main()\n      local c = task.Channel:new()\n      task.spawn(counter, c)\n      assert(c:recv() == 1)\n      assert(c:recv() == 2)\n      assert(c:recv() == 3)\n      assert(c:recv() == 4)\n      assert(c:recv() == 5)\n      done = true\n   end\n   task.spawn(main)\n   task.scheduler()\n   assert(done)\nend\n\nfunction test_nonblocking_channel()\n   local done\n   local function main()\n      local b = task.Channel:new()\n      assert(b:nbsend(1) == false)\n      assert(b:nbrecv() == false)\n\n      local c = task.Channel:new(1)\n      assert(c:nbrecv() == false)\n      assert(c:nbsend(1) == true)\n      assert(c:nbsend(1) == false)\n      local r, v = c:nbrecv()\n      assert(r == true)\n      assert(v == 1)\n      assert(c:nbrecv() == false)\n      done = true\n   end\n   task.spawn(main)\n   task.scheduler()\n   assert(done)\nend\n\nfunction test_concurrent_send_and_recv()\n   local l = {}\n   local function a(c, name)\n      -- Blocking send and recv from the same process\n      local alt = {{c = c, op = task.SEND, p = 1},\n                   {c = c, op = task.RECV}}\n      local i, v = task.chanalt(alt, true)\n      local k = string.format('%s %s', name, i == 1 and \"send\" or \"recv\")\n      l[k] = (l[k] or 0) + 1\n   end\n\n   for i = 0, 1000 do\n      -- On Mac OS X in lua 5.1 initializing seed with a\n      -- predictable value makes no sense. For all seeds from 1 to\n      -- 1000 the result of math.random(1,3) is _exactly_ the same!\n      -- So beware, when seeding!\n      -- math.randomseed(i)\n      local c = task.Channel:new()\n      task.spawn(a, c, \"a\")\n      task.spawn(a, c, \"b\")\n      task.scheduler()\n   end\n\n   -- Make sure we have randomness, that is: events occur in both\n   -- orders in 1000 runs\n   assert(l['a recv'] > 0)\n   assert(l['a send'] > 0)\n   assert(l['b recv'] > 0)\n   assert(l['b send'] > 0)\nend\n\nfunction test_channels_from_a_coroutine()\n   local done\n   local c = task.Channel:new()\n   local function a()\n      for i = 1, 100 do\n         c:send(i)\n      end\n   end\n   local function b()\n      assert(c:recv() == 1)\n      assert(c:recv() == 2)\n      assert(c:recv() == 3)\n      assert(c:recv() == 4)\n      assert(c:recv() == 5)\n      done = true\n   end\n   local a_co = coroutine.create(a)\n   local b_co = coroutine.create(b)\n   coroutine.resume(a_co)\n   coroutine.resume(b_co)\n   task.scheduler()\n   assert(done)\nend\n\nfunction test_fibonacci()\n   local done\n   local function fib(c)\n      local x, y = 0, 1\n      while true do\n         c:send(x)\n         x, y = y, x + y\n      end\n   end\n   local function main(c)\n      assert(c:recv() == 0)\n      assert(c:recv() == 1)\n      assert(c:recv() == 1)\n      assert(c:recv() == 2)\n      assert(c:recv() == 3)\n      assert(c:recv() == 5)\n      assert(c:recv() == 8)\n      assert(c:recv() == 13)\n      assert(c:recv() == 21)\n      assert(c:recv() == 34)\n      done = true\n   end\n\n   local c = task.Channel:new()\n   task.spawn(fib, c)\n   task.spawn(main, c)\n   task.scheduler()\n   assert(done)\nend\n\nfunction test_non_blocking_chanalt()\n   local done\n   local function main()\n      local c = task.Channel:new()\n      local alts = {{c = c, op = task.RECV},\n                    {c = c, op = task.NOP},\n                    {c = c, op = task.SEND, p = 1}}\n      assert(task.chanalt(alts, false) == nil)\n\n      local c = task.Channel:new(1)\n      local alts = {{c = c, op = task.RECV},\n                    {c = c, op = task.NOP},\n                    {c = c, op = task.SEND, p = 1}}\n      assert(task.chanalt(alts, false) == 3)\n      assert(task.chanalt(alts, false) == 1)\n\n      local alts = {{c = c, op = task.NOP}}\n      assert(task.chanalt(alts, false) == nil)\n\n      done = true\n   end\n   task.spawn(main)\n   task.scheduler()\n   assert(done)\nend\n\n-- Apparently it's not really a Sieve of Eratosthenes:\n--   http://www.cs.hmc.edu/~oneill/papers/Sieve-JFP.pdf\nfunction test_eratosthenes_sieve()\n   local done\n   local function counter(c)\n      local i = 2\n      while true do\n         c:send(i)\n         i = i + 1\n      end\n   end\n\n   local function filter(p, recv_ch, send_ch)\n      while true do\n         local i = recv_ch:recv()\n         if i % p ~= 0 then\n            send_ch:send(i)\n         end\n      end\n   end\n\n   local function sieve(primes_ch)\n      local c = task.Channel:new()\n      task.spawn(counter, c)\n      while true do\n         local p, newc = c:recv(), task.Channel:new()\n         primes_ch:send(p)\n         task.spawn(filter, p, c, newc)\n         c = newc\n      end\n   end\n\n   local function main()\n      local primes = task.Channel:new()\n      task.spawn(sieve, primes)\n      assert(primes:recv() == 2)\n      assert(primes:recv() == 3)\n      assert(primes:recv() == 5)\n      assert(primes:recv() == 7)\n      assert(primes:recv() == 11)\n      assert(primes:recv() == 13)\n      done = true\n   end\n\n   task.spawn(main)\n   task.scheduler()\n   assert(done)\nend\n\nfunction test_channel_as_iterator()\n   local done\n   local function counter(c)\n      local i = 2\n      while true do\n         c:send(i)\n         i = i + 1\n      end\n   end\n\n   local function main()\n      local numbers = task.Channel:new()\n      task.spawn(counter, numbers)\n      for _, j in numbers() do\n         if j == 100 then\n            break\n         end\n         done = true\n      end\n   end\n   if _VERSION == \"Lua 5.1\" then\n      -- sorry, this test doesn't work in 5.1\n      print('S')\n      done = true\n   else\n      task.spawn(main)\n      task.scheduler()\n   end\n   assert(done)\nend\n\nreturn _M\n"
  },
  {
    "path": "src/teliva.c",
    "content": "#include <assert.h>\n#include <ctype.h>\n#ifdef __NetBSD__\n#include <curses.h>\n#else\n#include <ncurses.h>\n#endif\n#include <stdlib.h>\n#include <string.h>\n#include <time.h>\n#include <unistd.h>\n\n#include \"lua.h\"\n#include \"lauxlib.h\"\n#include \"lualib.h\"\n\n#undef getstr\n#include \"lstate.h\"\n#include \"teliva.h\"\n#include \"tlv.h\"\n\nint starts_with(const char* s, const char* prefix) {\n  return strncmp(s, prefix, strlen(prefix)) == 0;\n}\n\nint contains(const char* s, const char* sub) {\n  return strstr(s, sub) != NULL;\n}\n\nint any_equal(char* const* arr, const char* s) {\n  for (int i = 0; arr[i]; ++i)\n    if (strcmp(arr[i], s) == 0)\n      return 1;\n  return 0;\n}\n\nint any_starts_with(char* const* arr, const char* s) {\n  for (int i = 0; arr[i]; ++i)\n    if (starts_with(s, arr[i]))\n      return 1;\n  return 0;\n}\n\n/*** Standard UI elements */\n\nint menu_column = 0;\n\nvoid draw_string_on_menu(const char* s) {\n  mvaddstr(LINES-1, menu_column, \" \");\n  ++menu_column;\n  mvaddstr(LINES-1, menu_column, s);\n  menu_column += strlen(s);\n  mvaddstr(LINES-1, menu_column, \" \");\n  ++menu_column;\n}\n\nvoid draw_menu_item(const char* key, const char* name) {\n  attroff(A_REVERSE);\n  draw_string_on_menu(key);\n  attron(A_REVERSE);\n  draw_string_on_menu(name);\n}\n\nstatic const char* trim(const char* in) {\n  static char result[1024];\n  int len = strlen(in);\n  assert(len < 1020);\n  const char* str = in;\n  const char* end = in+len-1;\n  while (isspace((unsigned char)*str)) {\n    ++str;\n    --len;\n  }\n  while (isspace((unsigned char)*end)) {\n    --end;\n    --len;\n  }\n  memset(result, '\\0', 1024);\n  memcpy(result, str, len);\n  return result;\n}\n\nint ask_for_permission_on_every_file_operation = 0;\nconst char* default_file_operations_predicate_body = \"return false\\n\";\nconst char* file_operations_predicate_body;\nint net_operations_permitted = false;\n\nstatic void render_permissions(lua_State* L);\nchar* Previous_message;\nstatic void draw_menu(lua_State* L) {\n  attron(A_BOLD|A_REVERSE);\n  color_set(COLOR_PAIR_MENU, NULL);\n  for (int x = 0; x < COLS; ++x)\n    mvaddch(LINES-1, x, ' ');\n  menu_column = 2;\n  draw_menu_item(\"^x\", \"exit\");\n\n  /* if app ran successfully, render any app-specific items */\n  if (Previous_message == NULL) {\n    lua_getglobal(L, \"menu\");\n    int table = lua_gettop(L);\n    if (lua_istable(L, -1)) {\n      for (int i = 1; i <= luaL_getn(L, table); ++i) {\n        lua_rawgeti(L, table, i);\n        int menu_item = lua_gettop(L);\n        lua_rawgeti(L, menu_item, 1);  /* key */\n        lua_rawgeti(L, menu_item, 2);  /* value */\n        draw_menu_item(lua_tostring(L, -2), lua_tostring(L, -1));\n        lua_pop(L, 3);\n      }\n    }\n    lua_pop(L, 1);\n  }\n  else {\n    /* otherwise render the flash message */\n    attron(COLOR_PAIR(COLOR_PAIR_ERROR));\n    addstr(\" \");\n    addstr(Previous_message);\n    addstr(\" \");\n    attroff(COLOR_PAIR(COLOR_PAIR_ERROR));\n  }\n\n  /* render stuff common to all apps on the right */\n  menu_column = COLS-37;\n  draw_menu_item(\"^u\", \"edit app\");\n  draw_menu_item(\"^p\", \"perms\");\n\n  attrset(A_NORMAL);\n  mvaddstr(LINES-1, COLS-12, \"\");\n  render_permissions(L);\n\n  attrset(A_NORMAL);\n}\n\nconst char* character_name(char c) {\n  if (c == '\\n') return \"ENTER\";\n  if (c == '\\t') return \"TAB\";\n  if (c == ' ') return \"SPACE\";\n  return \"UNKNOWN\";\n}\n\nstatic void render_permissions(lua_State* L) {\n  int file_colors = COLOR_PAIR_SAFE;\n  if (ask_for_permission_on_every_file_operation)\n    file_colors = COLOR_PAIR_WARN;\n  else if (file_operations_predicate_body && strcmp(\"return false\", trim(file_operations_predicate_body)) != 0)\n    file_colors = COLOR_PAIR_WARN;\n  int net_colors = net_operations_permitted ? COLOR_PAIR_WARN : COLOR_PAIR_SAFE;\n  if (file_colors == COLOR_PAIR_WARN && net_colors == COLOR_PAIR_WARN) {\n    file_colors = net_colors = COLOR_PAIR_RISK;\n  }\n\n  attron(COLOR_PAIR(file_colors));\n  addstr(\"file \");\n  attron(A_REVERSE);\n  addstr(\" \");\n  attroff(COLOR_PAIR(file_colors));\n\n  attron(COLOR_PAIR(net_colors));\n  addstr(\" \");\n  attroff(A_REVERSE);\n  addstr(\" net\");\n  attroff(COLOR_PAIR(net_colors));\n}\n\nvoid render_trusted_teliva_data(lua_State* L) {\n  init_pair(COLOR_PAIR_ERROR, COLOR_ERROR_FOREGROUND, COLOR_ERROR_BACKGROUND);\n  init_pair(COLOR_PAIR_MENU, COLOR_FOREGROUND, COLOR_BACKGROUND);\n  init_pair(COLOR_PAIR_SAFE, COLOR_SAFE_REVERSE, COLOR_FOREGROUND);\n  init_pair(COLOR_PAIR_WARN, COLOR_WARN_REVERSE, COLOR_FOREGROUND);\n  init_pair(COLOR_PAIR_RISK, COLOR_RISK_REVERSE, COLOR_FOREGROUND);\n  int y, x;\n  getyx(stdscr, y, x);\n  draw_menu(L);\n  mvaddstr(y, x, \"\");\n}\n\n/*** Error reporting */\n\nconst char* Previous_error = NULL;\n\n/* return final y containing text */\nstatic int render_wrapped_text(int y, int xmin, int xmax, const char* text) {\n  int x = xmin;\n  move(y, x);\n  for (int j = 0; j < strlen(text); ++j) {\n    char c = text[j];\n    if (c != '\\n') {\n      addch(text[j]);\n      ++x;\n      if (x >= xmax) {\n        ++y;\n        x = xmin;\n        move(y, x);\n      }\n    }\n    else {\n      /* newline */\n      ++y;\n      x = xmin;\n      move(y, x);\n    }\n  }\n  return y;\n}\n\nvoid render_previous_error(void) {\n  if (!Previous_error) return;\n  init_pair(COLOR_PAIR_ERROR, COLOR_ERROR_FOREGROUND, COLOR_ERROR_BACKGROUND);\n  attron(COLOR_PAIR(COLOR_PAIR_ERROR));\n  render_wrapped_text(LINES-10, COLS/2, COLS, Previous_error);\n  attroff(COLOR_PAIR(COLOR_PAIR_ERROR));\n}\n\nint report_in_developer_mode(lua_State* L, int status) {\n  if (status && !lua_isnil(L, -1)) {\n    Previous_error = strdup(lua_tostring(L, -1));  /* memory leak */\n    if (Previous_error == NULL) Previous_error = \"(error object is not a string)\";\n    lua_pop(L, 1);\n    for (int x = 0; x < COLS; ++x) {\n      mvaddch(LINES-2, x, ' ');\n      mvaddch(LINES-1, x, ' ');\n    }\n    render_previous_error();\n    mvaddstr(LINES-1, 0, \"press any key to continue\");\n    getch();\n    developer_mode(L);\n  }\n  return status;\n}\n\n/*** Developer mode, big picture view */\n\n#define CURRENT_DEFINITION_LEN 256\n\nstatic void big_picture_menu(void) {\n  attrset(A_REVERSE);\n  for (int x = 0; x < COLS; ++x)\n    mvaddch(LINES-1, x, ' ');\n  attrset(A_NORMAL);\n  menu_column = 2;\n  draw_menu_item(\"^x\", \"go back\");\n  draw_menu_item(\"^g\", \"go to highlight\");\n  draw_menu_item(\"Enter\", \"submit\");\n  draw_menu_item(\"^h\", \"backspace\");\n  draw_menu_item(\"^u\", \"clear\");\n  draw_menu_item(\"^r\", \"recent changes\");\n  draw_menu_item(\"^e\", \"recent events\");\n  attrset(A_NORMAL);\n}\n\nstatic int is_current_definition(lua_State* L, const char* definition_name, int current_history_array_index, int history_array_location, int history_array_size) {\n  /* Sequentially scan back through history_array until current_history_array_index.\n   * Is there an earlier definition of definition_name? */\n  int oldtop = lua_gettop(L);\n  int found = 0;\n  for (int i = history_array_size; i > current_history_array_index; --i) {\n    lua_rawgeti(L, history_array_location, i);\n    int t = lua_gettop(L);\n    for (lua_pushnil(L); lua_next(L, t) != 0;) {\n      lua_pop(L, 1);  // value\n      const char* curr = lua_tostring(L, -1);\n      if (strcmp(curr, definition_name) == 0) {\n        found = 1;\n        lua_pop(L, 1); // key\n        break;\n      }\n      // leave key on stack for next iteration\n    }\n    lua_pop(L, 1);  // history element\n    if (found)\n      break;\n  }\n  if(oldtop != lua_gettop(L)) {\n    endwin();\n    printf(\"%d %d\\n\", oldtop, lua_gettop(L));\n    exit(1);\n  }\n  return !found;\n}\n\nvoid draw_definition_name(const char* definition_name) {\n  attron(COLOR_PAIR(COLOR_PAIR_SELECTABLE));\n  addstr(\" \");\n  addstr(definition_name);\n  addstr(\" \");\n  attroff(COLOR_PAIR(COLOR_PAIR_SELECTABLE));\n  addstr(\"  \");\n}\n\nvoid draw_highlighted_definition_name(const char* definition_name) {\n  attron(A_REVERSE);\n  addstr(\" \");\n  addstr(definition_name);\n  addstr(\" \");\n  attroff(A_REVERSE);\n  addstr(\"  \");\n}\n\nvoid save_call_graph_depth(lua_State* L, int depth, const char* name) {\n  /* Maintain a global table mapping from function name to call-stack depth\n   * at first call to it.\n   *\n   * Won't be perfect; might get confused by shadowing locals. But we can't\n   * be perfect without a bidirectional mapping between interpreter state\n   * and source code. Which would make Lua either a lot less dynamic or a\n   * a lot more like Smalltalk. */\n  // push table\n  luaL_newmetatable(L, \"__teliva_call_graph_depth\");\n  int cgt = lua_gettop(L);\n  // if key doesn't already exist, set it\n  lua_getfield(L, cgt, name);\n  if (lua_isnil(L, -1)) {\n    lua_pushinteger(L, depth);\n    lua_setfield(L, cgt, name);\n  }\n  // clean up\n  lua_pop(L, 1);  // value\n  lua_pop(L, 1);  // table\n}\n\n/* Don't rely on this for security. It's visible to apps and so can be mutated\n * by them. */\nstatic const char* name_of_global(lua_State* L, const CallInfo* ci, int frame) {\n  const char* result = NULL;\n  Closure* func = ci_func(ci-frame);\n  int oldtop = lua_gettop(L);\n  // push table of function names\n  luaL_newmetatable(L, \"__teliva_global_name\");\n  int gt = lua_gettop(L);\n  lua_pushinteger(L, (long int)func);\n  lua_rawget(L, gt);\n  if (!lua_isnil(L, -1))\n    result = lua_tostring(L, -1);  // safe because global names are long-lived and never GC'd\n  lua_pop(L, 1);  // value\n  lua_pop(L, 1);  // table of global names\n  assert(lua_gettop(L) == oldtop);\n  return result;\n}\n\nstatic void precompute_names_of_globals(lua_State* L) {\n  int oldtop = lua_gettop(L);\n  luaL_newmetatable(L, \"__teliva_global_name\");\n  int gt = lua_gettop(L);\n  lua_pushvalue(L, LUA_GLOBALSINDEX);\n  int table = lua_gettop(L);\n  for (lua_pushnil(L); lua_next(L, table) != 0; lua_pop(L, 1)) {\n    const char* key = lua_tostring(L, -2);\n    const void* value = lua_topointer(L, -1);\n    lua_pushinteger(L, (long int)value);\n    lua_pushstring(L, key);\n    lua_rawset(L, gt);\n  }\n  lua_pop(L, 1);  // table of globals\n  lua_pop(L, 1);  // table of global names\n  assert(lua_gettop(L) == oldtop);\n}\n\nstatic void save_caller(lua_State* L, const char* name, const char* caller_name) {\n  int oldtop = lua_gettop(L);\n  // push table of caller tables\n  luaL_newmetatable(L, \"__teliva_caller\");\n  int ct = lua_gettop(L);\n  // if key doesn't already exist, map it to an empty caller table\n  lua_getfield(L, ct, name);\n  if (lua_isnil(L, -1)) {\n    lua_newtable(L);\n    lua_setfield(L, ct, name);\n  }\n  // append the caller's name to the caller table if necessary\n  lua_pop(L, 1);  // old value\n  lua_getfield(L, ct, name);  // new value = caller table\n  int curr_caller_index = lua_gettop(L);\n  lua_pushboolean(L, true);\n  lua_setfield(L, curr_caller_index, caller_name);\n  // clean up\n  lua_pop(L, 1);  // caller table\n  lua_pop(L, 1);  // table of caller tables\n  assert(lua_gettop(L) == oldtop);\n}\n\nvoid record_metadata_about_function_call (lua_State *L, CallInfo *ci) {\n  const char* function_name = name_of_global(L, ci, 0);\n  long int call_graph_depth = ci - L->base_ci;\n  /* note to self: the function pointer is at ci_func(ci) */\n  if (function_name) {\n    save_call_graph_depth(L, call_graph_depth, function_name);\n    if (call_graph_depth <= 1) return;\n    const char* caller_name = name_of_global(L, ci, 1);\n    if (caller_name)\n      save_caller(L, function_name, caller_name);\n    else if (call_graph_depth == 3)\n      save_caller(L, function_name, \"main\");\n  }\n}\n\nstatic void clear_caller(lua_State* L) {\n  int oldtop = lua_gettop(L);\n  luaL_newmetatable(L, \"__teliva_caller\");\n  int ct = lua_gettop(L);\n  lua_pushnil(L);\n  while (lua_next(L, ct) != 0) {\n    lua_pop(L, 1);  /* old value */\n    lua_pushvalue(L, -1);  /* duplicate key */\n    lua_pushnil(L);  /* new value */\n    lua_settable(L, ct);\n    /* one copy of key left for lua_next */\n  }\n  lua_pop(L, 1);\n  assert(lua_gettop(L) == oldtop);\n}\n\n/* return true if submitted */\nstatic int edit_current_definition(lua_State* L);\nstatic void recent_changes_view(lua_State* L);\nstatic const char* events_view();\nstatic int look_up_definition (lua_State* L, const char* name);\nvoid default_big_picture_view(lua_State* L) {\n  /* Without any intervening edits, big_picture_view always stably renders\n   * definitions in exactly the same spatial order, both in levels from top to\n   * bottom and in indexes within each level from left to right. */\n  int highlight_level = 0;\n  int highlight_index_within_level = 0;\n  int level_size[30] = {0};  /* number of indexes within each level */\n  char highlight[CURRENT_DEFINITION_LEN+1] = {0};\nrestart:\n  clear();\n  luaL_newmetatable(L, \"__teliva_call_graph_depth\");\n  int cgt = lua_gettop(L);\n  // segment definitions by depth\n  lua_getglobal(L, \"teliva_program\");\n  int history_array = lua_gettop(L);\n  int history_array_size = luaL_getn(L, history_array);\n\n  int y = 1;\n  attrset(A_BOLD);\n  mvaddstr(y, 0, \"Big picture\");\n  attrset(A_NORMAL);\n  y += 2;\n\n  int found = look_up_definition(L, \"doc:blurb\");\n  if (found) {\n    assert(lua_isstring(L, -1));\n    y = render_wrapped_text(y, 8, 68, lua_tostring(L, -1));\n    y += 2;\n    lua_pop(L, 1);\n  }\n\n  mvaddstr(y, 0, \"data:           \");\n  // first: data (non-functions) that's not the Teliva menu or curses variables\n  if (highlight_level < 0) highlight_level = 0;\n  int level = 0;\n  int index_within_level = 0;\n  for (int i = history_array_size; i > 0; --i) {\n    lua_rawgeti(L, history_array, i);\n    int t = lua_gettop(L);\n    for (lua_pushnil(L); lua_next(L, t) != 0; lua_pop(L, 1)) {\n      const char* definition_name = lua_tostring(L, -2);\n      if (is_special_history_key(definition_name)) continue;\n      if (starts_with(definition_name, \"doc:\")) continue;\n      lua_getglobal(L, definition_name);\n      int is_userdata = lua_isuserdata(L, -1);\n      int is_function = lua_isfunction(L, -1);\n      lua_pop(L, 1);\n      if (strcmp(definition_name, \"menu\") != 0  // required by all Teliva programs\n          && !is_function  // functions are not data\n          && !is_userdata  // including curses window objects\n                           // (unlikely to have an interesting definition)\n      ) {\n        if (is_current_definition(L, definition_name, i, history_array, history_array_size)) {\n          if (level == highlight_level && index_within_level == highlight_index_within_level) {\n            draw_highlighted_definition_name(definition_name);\n            strncpy(highlight, definition_name, CURRENT_DEFINITION_LEN);\n          } else {\n            draw_definition_name(definition_name);\n          }\n          ++index_within_level;\n        }\n      }\n    }\n    lua_pop(L, 1);  // history element\n  }\n\n  // second: menu and other userdata\n  for (int i = history_array_size; i > 0; --i) {\n    lua_rawgeti(L, history_array, i);\n    int t = lua_gettop(L);\n    for (lua_pushnil(L); lua_next(L, t) != 0; lua_pop(L, 1)) {\n      const char* definition_name = lua_tostring(L, -2);\n      if (is_special_history_key(definition_name)) continue;\n      if (starts_with(definition_name, \"doc:\")) continue;\n      lua_getglobal(L, definition_name);\n      int is_userdata = lua_isuserdata(L, -1);\n      lua_pop(L, 1);\n      if (strcmp(definition_name, \"menu\") == 0\n          || is_userdata  // including curses window objects\n      ) {\n        if (is_current_definition(L, definition_name, i, history_array, history_array_size)) {\n          if (level == highlight_level && index_within_level == highlight_index_within_level) {\n            draw_highlighted_definition_name(definition_name);\n            strncpy(highlight, definition_name, CURRENT_DEFINITION_LEN);\n          } else {\n            draw_definition_name(definition_name);\n          }\n          ++index_within_level;\n        }\n      }\n    }\n    lua_pop(L, 1);  // history element\n  }\n  level_size[level] = index_within_level;\n  level++;\n\n  // documentation (non-code) buffers\n  y += 2;\n  mvprintw(y, 0, \"prose:          \");\n  index_within_level = 0;\n  for (int i = history_array_size; i > 0; --i) {\n    lua_rawgeti(L, history_array, i);\n    int t = lua_gettop(L);\n    for (lua_pushnil(L); lua_next(L, t) != 0; lua_pop(L, 1)) {\n      const char* definition_name = lua_tostring(L, -2);\n      if (is_special_history_key(definition_name)) continue;\n      if (starts_with(definition_name, \"doc:\")) {\n        if (is_current_definition(L, definition_name, i, history_array, history_array_size)) {\n          if (level == highlight_level && index_within_level == highlight_index_within_level) {\n            draw_highlighted_definition_name(definition_name);\n            strncpy(highlight, definition_name, CURRENT_DEFINITION_LEN);\n          } else {\n            draw_definition_name(definition_name);\n          }\n          ++index_within_level;\n        }\n      }\n    }\n    lua_pop(L, 1);  // history element\n  }\n  level_size[level] = index_within_level;\n  level++;\n\n  // functions by level\n  y += 2;\n  mvprintw(y, 0, \"functions: \");\n  y++;\n  for (int depth = /*main*/2; ; ++depth) {\n    mvaddstr(y, 0, \"                \");\n    bool drew_anything = false;\n    index_within_level = 0;\n    for (int i = history_array_size; i > 0; --i) {\n      lua_rawgeti(L, history_array, i);\n      int t = lua_gettop(L);\n      for (lua_pushnil(L); lua_next(L, t) != 0; lua_pop(L, 1)) {\n        const char* definition_name = lua_tostring(L, -2);\n        if (is_special_history_key(definition_name)) continue;\n        lua_getfield(L, cgt, definition_name);\n        int definition_depth = lua_tointeger(L, -1);\n        if (definition_depth == depth) {\n          if (is_current_definition(L, definition_name, i, history_array, history_array_size)) {\n            if (level == highlight_level && index_within_level == highlight_index_within_level) {\n              draw_highlighted_definition_name(definition_name);\n              strncpy(highlight, definition_name, CURRENT_DEFINITION_LEN);\n            } else {\n              draw_definition_name(definition_name);\n            }\n            ++index_within_level;\n          }\n          drew_anything = true;\n        }\n        lua_pop(L, 1);  // depth of value\n      }\n      lua_pop(L, 1);  // history element\n    }\n    y += 2;\n    if (!drew_anything) break;\n    level_size[level] = index_within_level;\n    level++;\n  }\n\n  // unused functions\n  mvaddstr(y, 0, \"                \");\n  /* no need to level++ because the final iteration above didn't draw anything */\n  index_within_level = 0;\n  for (int i = history_array_size; i > 0; --i) {\n    lua_rawgeti(L, history_array, i);\n    int t = lua_gettop(L);\n    for (lua_pushnil(L); lua_next(L, t) != 0; lua_pop(L, 1)) {\n      const char* definition_name = lua_tostring(L, -2);\n      if (is_special_history_key(definition_name)) continue;\n      lua_getglobal(L, definition_name);\n      int is_function = lua_isfunction(L, -1);\n      lua_pop(L, 1);\n      lua_getfield(L, cgt, definition_name);\n      if (is_function && lua_isnoneornil(L, -1)) {\n        if (is_current_definition(L, definition_name, i, history_array, history_array_size)) {\n          if (level == highlight_level && index_within_level == highlight_index_within_level) {\n            draw_highlighted_definition_name(definition_name);\n            strncpy(highlight, definition_name, CURRENT_DEFINITION_LEN);\n          } else {\n            draw_definition_name(definition_name);\n          }\n          ++index_within_level;\n        }\n      }\n      lua_pop(L, 1);  // depth of value\n    }\n    lua_pop(L, 1);  // history element\n  }\n  level_size[level] = index_within_level;\n  int max_level = level;\n\n  lua_settop(L, 0);\n  render_previous_error();\n\n  char query[CURRENT_DEFINITION_LEN+1] = {0};\n  int qlen = 0;\n  while (1) {\n    big_picture_menu();\n    for (int x = 0; x < COLS; ++x)\n      mvaddch(LINES-2, x, ' ');\n//?     mvprintw(20, 60, \"%d %d\\n\", highlight_level, highlight_index_within_level);\n    mvprintw(LINES-2, 0, \"Edit: %s\", query);\n    int c = getch();\n    if (c == KEY_BACKSPACE || c == DELETE || c == CTRL_H) {\n      if (qlen != 0) query[--qlen] = '\\0';\n    } else if (c == CTRL_X) {\n      return;\n    } else if (c == ENTER) {\n      if (query[0] != '\\0') {\n        save_to_current_definition_and_editor_buffer(L, query);\n        int back_to_big_picture = edit_current_definition(L);\n        if (back_to_big_picture) goto restart;\n        return;\n      }\n    } else if (c == CTRL_U) {\n      qlen = 0;\n      query[qlen] = '\\0';\n    } else if (c == CTRL_R) {\n      recent_changes_view(L);\n      goto restart;\n    } else if (c == KEY_LEFT) {\n      highlight_index_within_level--;\n      if (highlight_index_within_level < 0) highlight_index_within_level = 0;\n      goto restart;\n    } else if (c == KEY_RIGHT) {\n      highlight_index_within_level++;\n      if (highlight_index_within_level >= level_size[highlight_level])\n        highlight_index_within_level = level_size[highlight_level]-1;\n      if (highlight_index_within_level < 0) highlight_index_within_level = 0;\n      goto restart;\n    } else if (c == KEY_UP) {\n      highlight_level--;\n      if (highlight_level < 0) highlight_level = 0;\n      if (highlight_index_within_level >= level_size[highlight_level])\n        highlight_index_within_level = level_size[highlight_level]-1;\n      if (highlight_index_within_level < 0) highlight_index_within_level = 0;\n      goto restart;\n    } else if (c == KEY_DOWN) {\n      highlight_level++;\n      if (highlight_level > max_level) highlight_level = max_level;\n      if (highlight_index_within_level >= level_size[highlight_level])\n        highlight_index_within_level = level_size[highlight_level]-1;\n      if (highlight_index_within_level < 0) highlight_index_within_level = 0;\n      goto restart;\n    } else if (c == CTRL_G) {\n      save_to_current_definition_and_editor_buffer(L, highlight);\n      int back_to_big_picture = edit_current_definition(L);\n      if (back_to_big_picture) goto restart;\n      return;\n    } else if (c == CTRL_E) {\n      const char* definition = events_view();\n      if (definition) {\n        save_to_current_definition_and_editor_buffer(L, definition);\n        int back_to_big_picture = edit_current_definition(L);\n        if (back_to_big_picture) goto restart;\n      }\n      return;\n    } else if (isprint(c)) {\n      if (qlen < CURRENT_DEFINITION_LEN) {\n          query[qlen++] = c;\n          query[qlen] = '\\0';\n      }\n    }\n  }\n  /* never gets here */\n}\n\nextern int edit(lua_State* L, char* filename, char* definition_name);\nvoid big_picture_view(lua_State* L) {\n  int oldtop = lua_gettop(L);\n  if (!look_up_definition(L, \"doc:main\")) {\n    lua_settop(L, oldtop);\n    default_big_picture_view(L);\n  } else {\n    save_to_current_definition_and_editor_buffer(L, \"doc:main\");\n    int back_to_big_picture = edit_current_definition(L);\n    if (back_to_big_picture)\n      default_big_picture_view(L);\n  }\n  lua_settop(L, oldtop);\n}\n\n/* return true if:\n *  - editor_state exists, and\n *  - editor_state is applicable to the current image\n * Implicitly loads current editor state. */\nint editor_view_in_progress(lua_State* L) {\n  FILE* in = fopen(\"teliva_editor_state\", \"r\");\n  if (in == NULL) return 0;\n  int oldtop = lua_gettop(L);\n  teliva_load_definition(L, in);\n  int t = lua_gettop(L);\n  lua_getfield(L, t, \"image\");\n  const char* image_name  = lua_tostring(L, -1);\n  int result = (strcmp(image_name, Image_name) == 0);\n  lua_pop(L, 1);  /* image value */\n  lua_setglobal(L, \"__teliva_editor_state\");\n  assert(lua_gettop(L) == oldtop);\n  return result;\n}\n\nchar Current_definition[CURRENT_DEFINITION_LEN+1] = {0};\n\nvoid draw_current_definition_name_and_callers(lua_State* L) {\n  int oldtop = lua_gettop(L);\n  mvaddstr(0, 0, \"\");\n  draw_definition_name(Current_definition);\n  luaL_newmetatable(L, \"__teliva_caller\");\n  int ct = lua_gettop(L);\n  lua_getfield(L, ct, Current_definition);\n  if (lua_isnil(L, -1)) {\n    lua_pop(L, 2);\n    assert(oldtop == lua_gettop(L));\n    return;\n  }\n  int ctc = lua_gettop(L);\n  attron(COLOR_PAIR(COLOR_PAIR_FADE));\n  addstr(\"callers: \");\n  attroff(COLOR_PAIR(COLOR_PAIR_FADE));\n  for (lua_pushnil(L); lua_next(L, ctc) != 0; lua_pop(L, 1)) {\n    const char* caller_name = lua_tostring(L, -2);\n    draw_definition_name(caller_name);\n  }\n  lua_pop(L, 2);  // caller table, __teliva_caller\n  assert(oldtop == lua_gettop(L));\n}\n\nextern int resumeEdit(lua_State* L);\nextern int editFrom(lua_State* L, char* filename, char* definition_name, int rowoff, int coloff, int cy, int cx);\nint restore_editor_view(lua_State* L) {\n  lua_getglobal(L, \"__teliva_editor_state\");\n  int editor_state_index = lua_gettop(L);\n  lua_getfield(L, editor_state_index, \"definition\");\n  const char* definition = lua_tostring(L, -1);\n  save_to_current_definition_and_editor_buffer(L, definition);\n  lua_getfield(L, editor_state_index, \"rowoff\");\n  int rowoff = lua_tointeger(L, -1);\n  lua_getfield(L, editor_state_index, \"coloff\");\n  int coloff = lua_tointeger(L, -1);\n  lua_getfield(L, editor_state_index, \"cy\");\n  int cy = lua_tointeger(L, -1);\n  lua_getfield(L, editor_state_index, \"cx\");\n  int cx = lua_tointeger(L, -1);\n  lua_settop(L, editor_state_index);\n  int back_to_big_picture = editFrom(L, \"teliva_editor_buffer\", Current_definition, rowoff, coloff, cy, cx);\n  if (starts_with(Current_definition, \"doc:\")) {\n    load_editor_buffer_to_current_definition_in_image(L);\n    return back_to_big_picture;\n  }\n  // error handling\n  int oldtop = lua_gettop(L);\n  while (1) {\n    int status;\n    status = load_editor_buffer_to_current_definition_in_image_and_reload(L);\n    if (status == 0 || lua_isnil(L, -1))\n      break;\n    Previous_error = lua_tostring(L, -1);\n    if (Previous_error == NULL) Previous_error = \"(error object is not a string)\";\n    back_to_big_picture = resumeEdit(L);\n    lua_pop(L, 1);\n  }\n  if (lua_gettop(L) != oldtop) {\n    endwin();\n    printf(\"editFrom: memory leak %d -> %d\\n\", oldtop, lua_gettop(L));\n    exit(1);\n  }\n  return back_to_big_picture;\n}\n\nchar** Argv = NULL;\nextern void cleanup_curses(void);\nvoid developer_mode(lua_State* L) {\n  /* clobber the app's ncurses colors; we'll restart the app when we rerun it. */\n  assume_default_colors(COLOR_FOREGROUND, COLOR_BACKGROUND);\n  init_pair(COLOR_PAIR_NORMAL, COLOR_FOREGROUND, COLOR_BACKGROUND);\n  init_pair(COLOR_PAIR_SELECTABLE, COLOR_SELECTABLE_FOREGROUND, COLOR_SELECTABLE_BACKGROUND);\n  init_pair(COLOR_PAIR_FADE, COLOR_FADE, COLOR_BACKGROUND);\n  init_pair(COLOR_PAIR_MENU_ALTERNATE, COLOR_MENU_ALTERNATE, COLOR_BACKGROUND);\n  init_pair(COLOR_PAIR_LUA_COMMENT, COLOR_LUA_COMMENT, COLOR_BACKGROUND);\n  init_pair(COLOR_PAIR_LUA_KEYWORD, COLOR_LUA_KEYWORD, COLOR_BACKGROUND);\n  init_pair(COLOR_PAIR_LUA_CONSTANT, COLOR_LUA_CONSTANT, COLOR_BACKGROUND);\n  init_pair(COLOR_PAIR_MATCH, COLOR_MATCH_FOREGROUND, COLOR_MATCH_BACKGROUND);\n  init_pair(COLOR_PAIR_ERROR, COLOR_ERROR_FOREGROUND, COLOR_ERROR_BACKGROUND);\n  nodelay(stdscr, 0);  /* always make getch() block in developer mode */\n  curs_set(1);  /* always display cursor in developer mode */\n  int switch_to_big_picture_view = 1;\n  if (editor_view_in_progress(L))\n    switch_to_big_picture_view = restore_editor_view(L);\n  if (switch_to_big_picture_view)\n    big_picture_view(L);\n  cleanup_curses();\n  execv(Argv[0], Argv);\n  /* never returns */\n}\n\nvoid save_editor_state(int rowoff, int coloff, int cy, int cx) {\n  if (strlen(Current_definition) == 0) return;\n  char outfilename[] = \"teliva_editor_state_XXXXXX\";\n  int outfd = mkstemp(outfilename);\n  if (outfd == -1) {\n    endwin();\n    perror(\"error in creating temporary file\");\n    abort();\n  }\n  FILE* out = fdopen(outfd, \"w\");\n  assert(out != NULL);\n  fprintf(out, \"- image: %s\\n\", Image_name);\n  fprintf(out, \"  definition: %s\\n\", Current_definition);\n  fprintf(out, \"  rowoff: %d\\n\", rowoff);\n  fprintf(out, \"  coloff: %d\\n\", coloff);\n  fprintf(out, \"  cy: %d\\n\", cy);\n  fprintf(out, \"  cx: %d\\n\", cx);\n  fclose(out);\n  rename(outfilename, \"teliva_editor_state\");\n}\n\n/* when found, return 1 and leave string on top of stack\n * when not found, return 0\n * caller is responsible for cleaning up the stack. */\nstatic int look_up_definition (lua_State* L, const char* name) {\n  lua_getglobal(L, \"teliva_program\");\n  int history_array = lua_gettop(L);\n  /* iterate over mutations in teliva_program history in reverse order */\n  int history_array_size = luaL_getn(L, history_array);\n  for (int i = history_array_size; i > 0; --i) {\n    lua_rawgeti(L, history_array, i);\n    int table = lua_gettop(L);\n    /* iterate over bindings */\n    /* really we expect only one */\n    for (lua_pushnil(L); lua_next(L, table) != 0; lua_pop(L, 1)) {\n      const char* key = lua_tostring(L, -2);\n      if (strcmp(key, \"__teliva_undo\") == 0) {\n        int next_i = lua_tointeger(L, -1);\n        assert(next_i < i);\n        i = next_i + 1;  /* account for decrement */\n        lua_pop(L, 1);\n        break;\n      }\n      if (is_special_history_key(key)) continue;\n      if (strcmp(key, name) == 0)\n        return 1;\n    }\n    lua_pop(L, 1);\n  }\n  lua_pop(L, 1);\n  return 0;\n}\n\nvoid save_to_current_definition_and_editor_buffer(lua_State* L, const char* definition) {\n  int oldtop = lua_gettop(L);\n  strncpy(Current_definition, definition, CURRENT_DEFINITION_LEN);\n  int status = look_up_definition(L, Current_definition);\n  char outfilename[] = \"teliva_editor_buffer_XXXXXX\";\n  int outfd = mkstemp(outfilename);\n  if (outfd == -1) {\n    endwin();\n    perror(\"save_to_current_definition_and_editor_buffer: error in creating temporary file\");\n    abort();\n  }\n  FILE* out = fdopen(outfd, \"w\");\n  assert(out != NULL);\n  if (status)\n    fprintf(out, \"%s\", lua_tostring(L, -1));\n  fclose(out);\n  rename(outfilename, \"teliva_editor_buffer\");\n  lua_settop(L, oldtop);\n}\n\n/* I don't understand the best way to read all of a text file.\n * I'm currently using fread, but its error handling is really designed for\n * binary data containing fixed-size records. */\nstatic void read_editor_buffer(char* out, int capacity) {\n  FILE* in = fopen(\"teliva_editor_buffer\", \"r\");\n  fread(out, capacity, 1, in);  /* TODO: handle overly large file */\n  fclose(in);\n}\n\nstatic void update_definition(lua_State* L, const char* name, char* new_contents) {\n  int oldtop = lua_gettop(L);\n  /* if contents are unmodified, return */\n  if (look_up_definition(L, name)) {\n    const char* old_contents = lua_tostring(L, -1);\n    bool contents_unmodified = (strcmp(old_contents, new_contents) == 0);\n    lua_settop(L, oldtop);\n    if (contents_unmodified) return;\n  }\n  lua_getglobal(L, \"teliva_program\");\n  int history_array = lua_gettop(L);\n  /* create a new table containing a single binding */\n  lua_createtable(L, /*number of fields per mutation*/2, 0);\n  lua_pushstring(L, new_contents);\n  assert(strlen(name) > 0);\n  lua_setfield(L, -2, name);\n  /* include timestamp at which binding was created */\n  time_t t;\n  time(&t);\n  char* time_string = ctime(&t);\n  lua_pushstring(L, time_string);\n  lua_setfield(L, -2, \"__teliva_timestamp\");\n  /* append the new table to the history of mutations */\n  int history_array_size = luaL_getn(L, history_array);\n  ++history_array_size;\n  lua_rawseti(L, history_array, history_array_size);\n  lua_settop(L, oldtop);\n}\n\nextern void save_tlv(lua_State* L, char* filename);\nvoid load_editor_buffer_to_current_definition_in_image(lua_State* L) {\n  char new_contents[8192] = {0};\n  read_editor_buffer(new_contents, 8190);\n  update_definition(L, Current_definition, new_contents);\n  save_tlv(L, Image_name);\n}\n\nextern int docall(lua_State* L, int narg, int clear);\nint load_editor_buffer_to_current_definition_in_image_and_reload(lua_State* L) {\n  char new_contents[8192] = {0};\n  read_editor_buffer(new_contents, 8190);\n  update_definition(L, Current_definition, new_contents);\n  save_tlv(L, Image_name);\n  /* reload binding */\n  return luaL_loadbuffer(L, new_contents, strlen(new_contents), Current_definition)\n      || docall(L, 0, 1);\n}\n\n/* return true if user chose to back into the big picture view */\n/* But only if there are no errors. Otherwise things can get confusing. */\nstatic int edit_current_definition(lua_State* L) {\n  int back_to_big_picture = edit(L, \"teliva_editor_buffer\", Current_definition);\n  if (starts_with(Current_definition, \"doc:\")) {\n    load_editor_buffer_to_current_definition_in_image(L);\n    return back_to_big_picture;\n  }\n  // error handling\n  int oldtop = lua_gettop(L);\n  while (1) {\n    int status;\n    status = load_editor_buffer_to_current_definition_in_image_and_reload(L);\n    if (status == 0 || lua_isnil(L, -1))\n      break;\n    Previous_error = lua_tostring(L, -1);\n    if (Previous_error == NULL) Previous_error = \"(error object is not a string)\";\n    back_to_big_picture = resumeEdit(L);\n    lua_pop(L, 1);\n  }\n  if (lua_gettop(L) != oldtop) {\n    endwin();\n    printf(\"edit_current_definition: memory leak %d -> %d\\n\", oldtop, lua_gettop(L));\n    exit(1);\n  }\n  return back_to_big_picture;\n}\n\nstatic void recent_changes_menu(int cursor, int history_array_size) {\n  attrset(A_REVERSE);\n  for (int x = 0; x < COLS; ++x)\n    mvaddch(LINES-1, x, ' ');\n  attrset(A_NORMAL);\n  menu_column = 2;\n  draw_menu_item(\"^x\", \"go back\");\n  /* draw_menu_item(\"↓|space\", \"older\"); */\n  attroff(A_REVERSE);\n  mvaddstr(LINES-1, menu_column, \" ↓\");\n  attron(COLOR_PAIR(COLOR_PAIR_MENU_ALTERNATE));\n  addstr(\"|\");\n  attroff(COLOR_PAIR(COLOR_PAIR_MENU_ALTERNATE));\n  addstr(\"space \");\n  menu_column += 9;  /* strlen isn't sufficient */\n  attron(A_REVERSE);\n  draw_string_on_menu(\"older\");\n  /* draw_menu_item(\"↑|backspace|delete|^h\", \"newer\"); */\n  attroff(A_REVERSE);\n  mvaddstr(LINES-1, menu_column, \" ↑\");\n  attron(COLOR_PAIR(COLOR_PAIR_MENU_ALTERNATE));\n  addstr(\"|\");\n  attroff(COLOR_PAIR(COLOR_PAIR_MENU_ALTERNATE));\n  addstr(\"backspace\");\n  attron(COLOR_PAIR(COLOR_PAIR_MENU_ALTERNATE));\n  addstr(\"|\");\n  attroff(COLOR_PAIR(COLOR_PAIR_MENU_ALTERNATE));\n  addstr(\"delete\");\n  attron(COLOR_PAIR(COLOR_PAIR_MENU_ALTERNATE));\n  addstr(\"|\");\n  attroff(COLOR_PAIR(COLOR_PAIR_MENU_ALTERNATE));\n  addstr(\"^h \");\n  menu_column += 23;\n  attron(A_REVERSE);\n  draw_string_on_menu(\"newer\");\n  draw_menu_item(\"^e\", \"edit note\");\n  if (cursor < history_array_size)\n    draw_menu_item(\"^u\", \"undo everything after this\");\n  attrset(A_NORMAL);\n}\n\n/* return final y containing text */\nstatic int render_wrapped_lua_text(int y, int xmin, int xmax, const char* text) {\n  int x = xmin;\n  move(y, x);\n  for (int j = 0; j < strlen(text); ++j) {\n    char c = text[j];\n    if (c == '-' && j+1 < strlen(text) && text[j+1] == '-')\n      attron(COLOR_PAIR(COLOR_PAIR_LUA_COMMENT));\n    if (c != '\\n') {\n      addch(text[j]);\n      ++x;\n      if (x >= xmax) {\n        ++y;\n        x = xmin;\n        move(y, x);\n      }\n    }\n    else {\n      /* newline */\n      ++y;\n      x = xmin;\n      move(y, x);\n      attroff(COLOR_PAIR(COLOR_PAIR_LUA_COMMENT));\n    }\n  }\n  return y;\n}\n\nstatic void render_recent_changes(lua_State* L, int start_index) {\n  clear();\n  attrset(A_BOLD);\n  mvaddstr(1, 0, \"Recent changes\");\n  attrset(A_NORMAL);\n  int oldtop = lua_gettop(L);\n  lua_getglobal(L, \"teliva_program\");\n  int history_array = lua_gettop(L);\n  int history_array_size = luaL_getn(L, history_array);\n  int y = 3;\n  attron(A_REVERSE);\n  for (int i = start_index; i > 0; --i) {\n    attron(A_BOLD);\n    mvprintw(y, 0, \"%3d. \", i);\n    attrset(A_NORMAL);\n    lua_rawgeti(L, history_array, i);\n    int t = lua_gettop(L);\n    for (lua_pushnil(L); lua_next(L, t) != 0; lua_pop(L, 1)) {\n      if (strcmp(lua_tostring(L, -2), \"__teliva_undo\") == 0) {\n        addstr(\"undo to \");\n        attron(A_BOLD);\n        printw(\"%d\", lua_tointeger(L, -1));\n        attroff(A_BOLD);\n        y++;\n        continue;\n      }\n      const char* definition_name = lua_tostring(L, -2);\n      if (is_special_history_key(definition_name)) continue;\n      addstr(definition_name);\n      /* save timestamp of binding if available */\n      lua_getfield(L, t, \"__teliva_timestamp\");\n      if (!lua_isnil(L, -1)) {\n        char buffer[128] = {0};\n        strncpy(buffer, lua_tostring(L, -1), 120);\n        if (buffer[strlen(buffer)-1] == '\\n')\n          buffer[strlen(buffer)-1] = '\\0';\n        attron(COLOR_PAIR(COLOR_PAIR_FADE));\n        printw(\"  %s\", buffer);\n        attroff(COLOR_PAIR(COLOR_PAIR_FADE));\n      }\n      lua_pop(L, 1);\n      lua_getfield(L, t, \"__teliva_note\");\n      if (!lua_isnil(L, -1)) {\n        attron(COLOR_PAIR(COLOR_PAIR_FADE));\n        printw(\"  -- %s\", lua_tostring(L, -1));\n        attroff(COLOR_PAIR(COLOR_PAIR_FADE));\n      }\n      lua_pop(L, 1);\n      y++;\n      const char* definition_contents = lua_tostring(L, -1);\n      y = render_wrapped_lua_text(y, 0, COLS, definition_contents);\n      y++;\n      if (y >= LINES-1) break;  /* leave cruft on the stack */\n    }\n    lua_settop(L, t);  /* clean up cruft on the stack */\n    lua_pop(L, 1);  // history element\n    y++;\n    if (y >= LINES-1) break;\n  }\n  lua_pop(L, 1);  // history array\n  if (lua_gettop(L) != oldtop) {\n    endwin();\n    printf(\"render_recent_changes: memory leak %d -> %d\\n\", oldtop, lua_gettop(L));\n    exit(1);\n  }\n  recent_changes_menu(start_index, history_array_size);\n  refresh();\n}\n\nstatic void add_undo_event(lua_State* L, int cursor) {\n  lua_getglobal(L, \"teliva_program\");\n  int history_array = lua_gettop(L);\n  /* create a new table containing the undo event */\n  lua_createtable(L, /*number of fields per mutation*/2, 0);\n  lua_pushinteger(L, cursor);\n  lua_setfield(L, -2, \"__teliva_undo\");\n  /* include timestamp at which event was created */\n  time_t t;\n  time(&t);\n  char* time_string = ctime(&t);\n  lua_pushstring(L, time_string);\n  lua_setfield(L, -2, \"__teliva_timestamp\");\n  /* append the new table to the history of mutations */\n  int history_array_size = luaL_getn(L, history_array);\n  ++history_array_size;\n  lua_rawseti(L, history_array, history_array_size);\n  /* clean up */\n  lua_pop(L, 1);\n}\n\nstatic void save_note_to_editor_buffer(lua_State* L, int cursor) {\n  lua_getglobal(L, \"teliva_program\");\n  lua_rawgeti(L, -1, cursor);\n  lua_getfield(L, -1, \"__teliva_note\");\n  const char* contents = lua_tostring(L, -1);\n  char outfilename[] = \"teliva_editor_buffer_XXXXXX\";\n  int outfd = mkstemp(outfilename);\n  if (outfd == -1) {\n    endwin();\n    perror(\"save_note_to_editor_buffer: error in creating temporary file\");\n    abort();\n  }\n  FILE* out = fdopen(outfd, \"w\");\n  assert(out != NULL);\n  if (contents != NULL)\n    fprintf(out, \"%s\", contents);\n  fclose(out);\n  rename(outfilename, \"teliva_editor_buffer\");\n  lua_pop(L, 3);  /* contents, table at cursor, teliva_program */\n}\n\nstatic void load_note_from_editor_buffer(lua_State* L, int cursor) {\n  lua_getglobal(L, \"teliva_program\");\n  char new_contents[8192] = {0};\n  read_editor_buffer(new_contents, 8190);\n  lua_rawgeti(L, -1, cursor);\n  lua_pushstring(L, new_contents);\n  lua_setfield(L, -2, \"__teliva_note\");\n  lua_pop(L, 2);  /* table at cursor, teliva_program */\n}\n\nextern void editNonCode(char* filename);\nstatic void recent_changes_view(lua_State* L) {\n  lua_getglobal(L, \"teliva_program\");\n  int history_array = lua_gettop(L);\n  assert(history_array == 1);\n  int history_array_size = luaL_getn(L, history_array);\n  int cursor = history_array_size;\n  lua_pop(L, 1);\n  int quit = 0;\n  while (!quit) {\n    /* refresh state after each operation so we pick up modifications */\n    render_recent_changes(L, cursor);\n    int c = getch();\n    switch (c) {\n      case CTRL_X:\n        quit = 1;\n        break;\n      case KEY_DOWN:\n      case ' ':\n        if (cursor > 1) --cursor;\n        break;\n      case KEY_UP:\n      case KEY_BACKSPACE:\n      case DELETE:\n      case CTRL_H:\n        if (cursor < history_array_size) ++cursor;\n        break;\n      case CTRL_E:\n        save_note_to_editor_buffer(L, cursor);\n        /* big picture hotkey unnecessarily available here */\n        editNonCode(\"teliva_editor_buffer\");\n        load_note_from_editor_buffer(L, cursor);\n        save_tlv(L, Image_name);\n        break;\n      case CTRL_U:\n        if (cursor < history_array_size) {\n          add_undo_event(L, cursor);\n          save_tlv(L, Image_name);\n        }\n        break;\n    }\n  }\n}\n\nstatic int binding_exists (lua_State *L, const char *name) {\n  int result = 0;\n  lua_getglobal(L, name);\n  result = !lua_isnil(L, -1);\n  lua_pop(L, 1);\n  return result;\n}\n\nextern int dostring(lua_State* L, const char* s, const char* name);\nstatic int load_definitions(lua_State* L) {\n  int status;\n  lua_getglobal(L, \"teliva_program\");\n  int history_array = lua_gettop(L);\n  /* iterate over mutations in teliva_program history in reverse order */\n  int history_array_size = luaL_getn(L, history_array);\n  for (int i = history_array_size; i > 0; --i) {\n    lua_rawgeti(L, history_array, i);\n    int table = lua_gettop(L);\n    /* iterate over bindings */\n    /* really we expect only one */\n    for (lua_pushnil(L); lua_next(L, table) != 0; lua_pop(L, 1)) {\n      const char* key = lua_tostring(L, -2);\n      if (strcmp(key, \"__teliva_undo\") == 0) {\n        int next_i = lua_tointeger(L, -1);\n        assert(next_i < i);\n        i = next_i + 1;  /* account for decrement */\n        lua_pop(L, 1);\n        break;\n      }\n      if (is_special_history_key(key)) continue;\n      if (starts_with(key, \"doc:\")) continue;\n      if (binding_exists(L, key))\n        continue;  // most recent binding trumps older ones\n      const char* value = lua_tostring(L, -1);\n      status = dostring(L, value, key);\n      if (status != 0) return report_in_developer_mode(L, status);\n    }\n    lua_pop(L, 1);\n  }\n  lua_pop(L, 1);\n  return 0;\n}\n\nstatic int run_tests(lua_State* L) {\n  clear();\n  int oldtop = lua_gettop(L);\n  lua_pushinteger(L, 0);\n  lua_setglobal(L, \"teliva_num_test_failures\");\n  lua_pushnil(L);\n  lua_setglobal(L, \"teliva_first_failure\");\n  lua_pushvalue(L, LUA_GLOBALSINDEX);\n  int table = lua_gettop(L);\n  for (lua_pushnil(L); lua_next(L, table) != 0; lua_pop(L, 1)) {\n    const char* key = lua_tostring(L, -2);\n    if (strncmp(\"test_\", key, strlen(\"test_\")) != 0) continue;\n    if (!lua_isfunction(L, -1)) continue;\n    /* uncomment these lines when it's not clear which test is failing */\n//?     addstr(key);\n//?     addstr(\"|\");\n    int status = lua_pcall(L, 0, 0, 0);\n    if (status) {\n      printw(\"E%d: %s\", status, lua_tostring(L, -1));\n      /* increment teliva_num_test_failures */\n      lua_getglobal(L, \"teliva_num_test_failures\");\n      int num_failures = lua_tointeger(L, -1);\n      lua_pop(L, 1);\n      lua_pushinteger(L, num_failures+1);\n      lua_setglobal(L, \"teliva_num_test_failures\");\n      /* if unset, set teliva_first_failure */\n      lua_getglobal(L, \"teliva_first_failure\");\n      int first_failure_clear = lua_isnil(L, -1);\n      lua_pop(L, 1);\n      if (first_failure_clear)\n        lua_setglobal(L, \"teliva_first_failure\");\n      else\n        lua_pop(L, 1);\n    }\n    lua_pushnil(L);  /* just to undo loop update */\n  }\n  lua_pop(L, 1);\n  lua_getglobal(L, \"teliva_num_test_failures\");\n  int num_failures = lua_tointeger(L, -1);\n  lua_pop(L, 1);\n  if (lua_gettop(L) != oldtop) {\n    endwin();\n    printf(\"render_recent_changes: memory leak %d -> %d\\n\", oldtop, lua_gettop(L));\n    exit(1);\n  }\n  if (num_failures == 0) return 0;\n  if (num_failures == 1)\n    addstr(\"1 failure\");\n  else\n    printw(\"%d failures\", num_failures);\n  /* take first failure back to developer mode */\n  lua_getglobal(L, \"teliva_first_failure\");\n  assert(!lua_isnil(L, -1));\n  return 1;\n}\n\nstatic void clear_call_graph_depth(lua_State* L) {\n  int oldtop = lua_gettop(L);\n  luaL_newmetatable(L, \"__teliva_call_graph_depth\");\n  int cgt = lua_gettop(L);\n  lua_pushnil(L);\n  while (lua_next(L, cgt) != 0) {\n    lua_pop(L, 1);  /* old value */\n    lua_pushvalue(L, -1);  /* duplicate key */\n    lua_pushnil(L);  /* new value */\n    lua_settable(L, cgt);\n    /* one copy of key left for lua_next */\n  }\n  lua_pop(L, 1);\n  assert(lua_gettop(L) == oldtop);\n}\n\n/*** Permissions */\n\n/* Perform privilege calculations in a whole other isolated context. */\nlua_State* trustedL = NULL;\n\nstatic int isarg(lua_State* trustedL) {\n  const char* arg = luaL_checkstring(trustedL, -1);\n  lua_pushboolean(trustedL, any_equal(Argv, arg));\n  return 1;\n}\n\nstatic int inarg(lua_State* trustedL) {\n  const char* arg = luaL_checkstring(trustedL, -1);\n  lua_pushboolean(trustedL, any_starts_with(Argv, arg));\n  return 1;\n}\n\nstatic const luaL_Reg trusted_base_funcs[] = {\n  {\"isarg\", isarg},\n  {\"inarg\", inarg},\n};\n\nvoid initialize_trustedL() {\n  trustedL = luaL_newstate();\n  lua_gc(trustedL, LUA_GCSTOP, 0);  /* stop collector during initialization */\n  luaL_openlibs(trustedL);\n  luaL_register(trustedL, \"_G\", trusted_base_funcs);\n  /* TODO: Should we include ncurses? How to debug policies? */\n  lua_gc(trustedL, LUA_GCRESTART, 0);\n}\n\nstatic const char* user_configuration_filename() {\n  const char* home = getenv(\"HOME\");\n  if (home == NULL) {\n    endwin();\n    fprintf(stderr, \"$HOME is not set; unclear where to save permissions.\\n\");\n    abort();\n  }\n  static char config_filename[1024] = {0};\n  memset(config_filename, '\\0', 1024);\n  const char* config_home = getenv(\"XDG_CONFIG_HOME\");\n  if (config_home == NULL)\n    snprintf(config_filename, 1020, \"%s/.teliva\", home);\n  else\n    snprintf(config_filename, 1020, \"%s/.teliva\", config_home);\n  return config_filename;\n}\n\nstatic int file_operation_permitted_manually(const char* filename, const char* mode) {\n  int old_y, old_x;\n  getyx(stdscr, old_y, old_x);\n  attr_t old_attrs;\n  short old_pair;\n  attr_get(&old_attrs, &old_pair, NULL);\n  attrset(A_NORMAL);\n  mvaddstr(LINES-1, 0, \"\");\n  clrtoeol();\n  attrset(A_REVERSE);\n  if (strncmp(mode, \"r\", /*strlen(\"r\") + 1 for NULL*/ 2) == 0)\n    mvprintw(LINES-1, 0, \"open file \\\"%s\\\" for reading? \", filename);\n  else\n    mvprintw(LINES-1, 0, \"open file \\\"%s\\\" for reading and writing? \", filename);\n  attrset(A_NORMAL);\n  int response = getch();\n  attr_set(old_attrs, old_pair, NULL);\n  mvaddstr(old_y, old_x, \"\");\n  return response == 'y';\n}\n\nstatic int file_operation_permitted_automatically(const char* filename, const char* mode) {\n  int oldtop = lua_gettop(trustedL);\n  lua_getglobal(trustedL, \"file_operation_permitted\");\n  lua_pushstring(trustedL, filename);\n  lua_pushboolean(trustedL, strncmp(mode, \"r\", /*strlen(\"r\") + 1 for NULL*/ 2) != 0);\n  if (lua_pcall(trustedL, 2 /*args*/, 1 /*result*/, /*errfunc*/0)) {\n    /* TODO: error handling. Or should we use errfunc above? */\n  }\n  if (!lua_isboolean(trustedL, -1)) {\n    endwin();\n    printf(\"Sorry, there's an error in permissions for this image.\\n\");\n    printf(\"Delete '%s' or try editing it by hand.\\n\", user_configuration_filename());\n    exit(1);\n  }\n  int should_allow = lua_toboolean(trustedL, -1);\n  lua_settop(trustedL, oldtop);\n  return should_allow;\n}\n\nint file_operation_permitted(const char* filename, const char* mode) {\n  if (ask_for_permission_on_every_file_operation)\n    return file_operation_permitted_manually(filename, mode);\n  else\n    return file_operation_permitted_automatically(filename, mode);\n}\n\nstatic void permissions_menu() {\n  attrset(A_REVERSE);\n  for (int x = 0; x < COLS; ++x)\n    mvaddch(LINES-1, x, ' ');\n  attrset(A_NORMAL);\n  menu_column = 2;\n  draw_menu_item(\"^x\", \"go back\");\n  if (!ask_for_permission_on_every_file_operation) {\n    draw_menu_item(\"^f\", \"edit file permissions\");\n    draw_menu_item(\"^a\", \"ask for permission on every file operation\");\n  }\n  else {\n    draw_menu_item(\"^a\", \"stop asking for permission on every file operation\");\n  }\n  draw_menu_item(\"^n\", \"toggle network permissions\");\n  attrset(A_NORMAL);\n}\n\nvoid characterize_file_operations_predicate() {\n  static const char* test_filenames[] = { \"foo\", \"/foo\", \"../foo\", NULL };\n  static const char* test_modes[] = { \"r\", \"r+\", \"w\", \"w+\", \"a\", \"a+\", NULL };\n  int num_attempts = 0;\n  int num_rejections = 0;\n  int num_errors = 0;\n  for (const char** test_filename = test_filenames; *test_filename; ++test_filename) {\n    for (const char** test_mode = test_modes; *test_mode; ++test_mode) {\n      lua_getglobal(trustedL, \"file_operation_permitted\");\n      lua_pushstring(trustedL, *test_filename);\n      lua_pushstring(trustedL, *test_mode);\n      if (lua_pcall(trustedL, 2 /*args*/, 1 /*result*/, /*errfunc*/0)) {\n        /* TODO: error handling. Or should we use errfunc above? */\n      }\n      ++num_attempts;\n      if (!lua_isboolean(trustedL, -1)) {\n        ++num_errors;\n      } else {\n        if (!lua_toboolean(trustedL, -1))\n          ++num_rejections;\n      }\n    }\n  }\n\n  if (num_errors > 0) {\n    attron(COLOR_PAIR(COLOR_PAIR_ERROR));\n    addstr(\" Throws errors some of the time. You should fix them before moving on. \");\n    attroff(COLOR_PAIR(COLOR_PAIR_ERROR));\n  }\n  else if (strcmp(\"return false\", trim(file_operations_predicate_body)) == 0) {\n    attron(COLOR_PAIR(COLOR_PAIR_SAFE));\n    addstr(\"● Rejects all file operations.\");\n    attroff(COLOR_PAIR(COLOR_PAIR_SAFE));\n  }\n  else if (strcmp(\"return true\", trim(file_operations_predicate_body)) == 0) {\n    attron(COLOR_PAIR(COLOR_PAIR_WARN));\n    addstr(\"◯ Allows all file operations.\");\n    attroff(COLOR_PAIR(COLOR_PAIR_WARN));\n  }\n  else {\n    static const char* statuses[5] = {\n      \"◯ Weakly suspected to allow all file operations.\",\n      \"◔ Weakly suspected to allow most file operations.\",\n      \"◑ Weakly suspected to allow many file operations.\",\n      \"◕ Weakly suspected to reject most file operations.\",\n      \"● Weakly suspected to reject all file operations.\",\n    };\n    attron(COLOR_PAIR(COLOR_PAIR_FADE));\n    int frac = (float)num_rejections/num_attempts*4;\n    addstr(statuses[frac]);\n    attroff(COLOR_PAIR(COLOR_PAIR_FADE));\n  }\n}\n\nstatic void render_permissions_screen() {\n  clear();\n  attrset(A_BOLD);\n  mvaddstr(1, 5, \"Permissions: What sensitive operations this app is allowed to perform\");\n  mvaddstr(2, 5, \"🚧 Be very careful granting permissions 🚧\");\n  attrset(A_NORMAL);\n\n  int y = 7;\n  mvaddstr(y, 5, \"File operations\");\n  if (!ask_for_permission_on_every_file_operation) {\n    mvaddstr(y, 30, \"function file_operation_permitted(filename, is_write)\");\n    y = render_wrapped_text(y+1, 32, COLS-5, file_operations_predicate_body);\n    mvaddstr(y, 30, \"end\");\n    y++;\n    mvaddstr(y, 30, \"\");\n    characterize_file_operations_predicate();\n  }\n  else {\n    attron(COLOR_PAIR(COLOR_PAIR_WARN));\n    attron(A_REVERSE);\n    mvaddstr(y, 30, \" always ask                    \");\n    attroff(A_REVERSE);\n    attroff(COLOR_PAIR(COLOR_PAIR_WARN));\n  }\n  y += 2;\n\n  int net_colors = net_operations_permitted ? COLOR_PAIR_WARN : COLOR_PAIR_SAFE;\n  mvaddstr(y, 5, \"Network operations\");\n  attron(COLOR_PAIR(net_colors));\n  attron(A_REVERSE);\n  switch (net_colors) {\n    case COLOR_PAIR_SAFE:\n      mvaddstr(y, 30, \" never                         \");\n      break;\n    case COLOR_PAIR_WARN:\n      mvaddstr(y, 30, \" always                        \");\n      break;\n    case COLOR_PAIR_RISK:\n      mvaddstr(y, 30, \"                               \");\n      break;\n    default:\n      abort();\n  }\n  y++;\n  attroff(A_REVERSE);\n  attroff(COLOR_PAIR(net_colors));\n  mvaddstr(y, 30, \"(No nuance available for network operations.)\");\n\n  if (!ask_for_permission_on_every_file_operation) {\n    int file_operations_safe = strcmp(\"return false\", trim(file_operations_predicate_body)) == 0;\n    int net_operations_safe = (net_operations_permitted == 0);\n    int file_operations_unsafe = strcmp(\"return true\", trim(file_operations_predicate_body)) == 0;\n    int net_operations_unsafe = (net_operations_permitted != 0);\n    if (file_operations_safe && net_operations_safe) {\n      attron(COLOR_PAIR(COLOR_PAIR_SAFE));\n      mvaddstr(5, 5, \"This app can't access private data or communicate with other computers.\");\n      attroff(COLOR_PAIR(COLOR_PAIR_SAFE));\n    }\n    else if (file_operations_safe || net_operations_safe) {\n      attron(COLOR_PAIR(COLOR_PAIR_WARN));\n      if (net_operations_safe) {\n        mvaddstr(5, 5, \"This app can access private data, but they can't leave this computer.\");\n      }\n      else {\n        mvaddstr(5, 5, \"This app can communicate with other computers, but can't access private data.\");\n      }\n      attroff(COLOR_PAIR(COLOR_PAIR_WARN));\n    }\n    else if (file_operations_unsafe && net_operations_unsafe) {\n      attron(COLOR_PAIR(COLOR_PAIR_RISK));\n      // idea: include pentagram emoji. But it isn't widely supported yet on Linux.\n      mvaddstr(5, 5, \"😈 ⚠️  Teliva can't protect you if this app does something sketchy. Consider restricting permissions. ⚠️  😈\");\n      attroff(COLOR_PAIR(COLOR_PAIR_RISK));\n    }\n    else {\n      attron(COLOR_PAIR(COLOR_PAIR_RISK));\n      mvaddstr(5, 5, \"🦮 🙈 Teliva can't tell how much it's protecting you. Consider simplifying permissions.\");\n      attroff(COLOR_PAIR(COLOR_PAIR_RISK));\n    }\n  }\n  else {\n    // ask_for_permission_on_every_file_operation is true\n    if (net_operations_permitted == 0) {\n      attron(COLOR_PAIR(COLOR_PAIR_WARN));\n      mvaddstr(5, 5, \"You're manually managing file permissions, but they can't leave this computer.\");\n      attroff(COLOR_PAIR(COLOR_PAIR_WARN));\n    }\n    else {\n      attron(COLOR_PAIR(COLOR_PAIR_WARN));\n      mvaddstr(5, 5, \"You're manually managing file permissions, and the app can access the network. Watch out for fatigue.\");\n      attroff(COLOR_PAIR(COLOR_PAIR_WARN));\n//?       attron(COLOR_PAIR(COLOR_PAIR_RISK));\n//?       mvaddstr(5, 5, \"😈 ⚠️  Manually managing file permissions on a networked app is a losing enterprise. ⚠️  😈\");\n//?       attroff(COLOR_PAIR(COLOR_PAIR_RISK));\n    }\n  }\n\n  permissions_menu();\n  refresh();\n}\n\n/* Try running the function to test for errors. If code has an error, leave it\n * on the stack and return non-zero */\nint validate_file_operations_predicate() {\n  lua_getglobal(trustedL, \"file_operation_permitted\");\n  lua_pushstring(trustedL, \"filename\");\n  lua_pushstring(trustedL, \"r\");  /* open mode */\n  if (lua_pcall(trustedL, 2 /*args*/, 1 /*result*/, /*errfunc*/0)) {\n    /* TODO: error handling. Or should we use errfunc above? */\n  }\n  int status = 1;\n  if (lua_isboolean(trustedL, -1)) {\n    lua_pop(trustedL, 1);\n    status = 0;\n  }\n  return status;\n}\n\nstatic int load_file_operations_predicate(const char* body) {\n  char buffer[1024] = {0};\n  strcpy(buffer, \"function file_operation_permitted(filename, is_write)\\n\");\n  strncat(buffer, body, 1020);\n  if (buffer[strlen(buffer)-1] != '\\n')\n    strncat(buffer, \"\\n\", 1020);\n  strncat(buffer, \"end\\n\", 1020);\n  return luaL_loadbuffer(trustedL, buffer, strlen(buffer), \"file_operation_permitted\")\n          || docall(trustedL, 0, 1)\n          || validate_file_operations_predicate();\n}\n\nextern void editFilePermissions(char* filename);\nextern void resumeFilePermissionsEdit();\nstatic void edit_file_operations_predicate_body() {\n  static char file_operations_predicate_body_buffer[512];\n  /* save to disk */\n  char outfilename[] = \"teliva_file_operations_predicate_body_XXXXXX\";\n  int outfd = mkstemp(outfilename);\n  if (outfd == -1) {\n    endwin();\n    perror(\"edit_file_operations_predicate_body: error in creating temporary file\");\n    abort();\n  }\n  FILE* out = fdopen(outfd, \"w\");\n  assert(out != NULL);\n  fprintf(out, \"%s\", file_operations_predicate_body);\n  fclose(out);\n  rename(outfilename, \"teliva_file_operations_predicate_body\");\n  Previous_error = \"\";\n  editFilePermissions(\"teliva_file_operations_predicate_body\");\n  // error handling\n  assert(trustedL);\n  int oldtop = lua_gettop(trustedL);\n  while (1) {\n    int status;\n    memset(file_operations_predicate_body_buffer, '\\0', 512);\n    FILE* in = fopen(\"teliva_file_operations_predicate_body\", \"r\");\n    fread(file_operations_predicate_body_buffer, 500, 1, in);  /* TODO: error message if file too large */\n    fclose(in);\n    status = load_file_operations_predicate(file_operations_predicate_body_buffer);\n    if (status == 0 || lua_isnil(trustedL, -1))\n      break;\n    Previous_error = lua_tostring(trustedL, -1);\n    if (Previous_error == NULL) Previous_error = \"(error object is not a string)\";\n    resumeFilePermissionsEdit();\n    lua_pop(trustedL, 1);\n  }\n  file_operations_predicate_body = file_operations_predicate_body_buffer;\n  if (lua_gettop(trustedL) != oldtop) {\n    endwin();\n    printf(\"edit_file_operations_predicate_body: memory leak %d -> %d\\n\", oldtop, lua_gettop(trustedL));\n    exit(1);\n  }\n}\n\nvoid print_file_permission_suggestions(int row) {\n  mvaddstr(row++, 0, \"-- Some ideas:\");\n  mvaddstr(row++, 0, \"--  * restrict access to a single file: return filename == 'foo'\");\n  mvaddstr(row++, 0, \"--  * restrict to reading only: return is_write == false\");\n  mvaddstr(row++, 0, \"--  * restrict to files with a fixed prefix: return string.find(filename, 'foo') == 1\");\n  mvaddstr(row++, 0, \"--  * restrict to files with a fixed extension: return filename:sub(-4) == '.txt'\");\n  mvaddstr(row++, 0, \"--  * restrict to files under some directory: return string.find(filename, 'foo/') == 1\");\n  mvaddstr(row++, 0, \"--  * restrict access only to files specified on commandline: return isarg(filename)\");\n  mvaddstr(row++, 0, \"--  * restrict access only to paths under directories specified on commandline: return inargs(filename)\");\n  mvaddstr(row++, 0, \"--\");\n  mvaddstr(row++, 0, \"-- Each of these has benefits and drawbacks.\");\n}\n\nstatic void permissions_view() {\n  while (true) {\n    render_permissions_screen();\n    int c = getch();\n    switch (c) {\n      case CTRL_X:\n        return;\n      case CTRL_F:\n        if (!ask_for_permission_on_every_file_operation)\n          edit_file_operations_predicate_body();\n        break;\n      case CTRL_A:\n        ask_for_permission_on_every_file_operation = !ask_for_permission_on_every_file_operation;\n        break;\n      case CTRL_N:\n        net_operations_permitted = !net_operations_permitted;\n        break;\n    }\n  }\n}\n\nstatic void save_permissions_to_user_configuration(lua_State* L) {\n  const char* rcfilename = user_configuration_filename();\n  FILE* in = fopen(rcfilename, \"r\");  /* can be NULL when rcfile doesn't exist */\n  char outfilename[] = \"telivarc_XXXXXX\";\n  int outfd = mkstemp(outfilename);\n  if (outfd == -1) {\n    endwin();\n    perror(\"error in creating temporary file\");\n    abort();\n  }\n  FILE* out = fdopen(outfd, \"w\");\n  assert(out != NULL);\n  /* read entries from rcfilename and write them to outfilename. If image name\n   * matches the current Image_name, ignore. */\n  int oldtop = lua_gettop(L);\n  while (in && !feof(in)) {\n    teliva_load_definition(L, in);\n    if (lua_isnil(L, -1)) break;\n    lua_getfield(L, -1, \"image_name\");\n    const char* image_name = lua_tostring(L, -1);\n    if (strcmp(image_name, Image_name) != 0) {\n      fprintf(out, \"- image_name: %s\\n\", image_name);\n      fprintf(out, \"  file_operations_predicate_body:\\n\");\n      lua_getfield(L, -2, \"file_operations_predicate_body\");\n      if (!lua_isnil(L, -1))\n        emit_multiline_string(out, lua_tostring(L, -1));\n      lua_pop(L, 1);  /* file_operations_predicate_body */\n      lua_getfield(L, -2, \"ask_for_permission_on_every_file_operation\");\n      fprintf(out, \"  ask_for_permission_on_every_file_operation: %s\\n\", lua_tostring(L, -1));\n      lua_pop(L, 1);  /* ask_for_permission_on_every_file_operation */\n      lua_getfield(L, -2, \"net_operations_permitted\");\n      fprintf(out, \"  net_operations_permitted: %s\\n\", lua_tostring(L, -1));\n      lua_pop(L, 1);  /* net_operations_permitted */\n    }\n    lua_pop(L, 1);  /* image_name */\n  }\n  lua_settop(L, oldtop);\n  fprintf(out, \"- image_name: %s\\n\", Image_name);\n  fprintf(out, \"  file_operations_predicate_body:\\n\");\n  assert(file_operations_predicate_body);\n  emit_multiline_string(out, file_operations_predicate_body);\n  fprintf(out, \"  ask_for_permission_on_every_file_operation: %d\\n\", ask_for_permission_on_every_file_operation);\n  fprintf(out, \"  net_operations_permitted: %d\\n\", net_operations_permitted);\n  fclose(out);\n  if (in) fclose(in);\n  rename(outfilename, rcfilename);\n}\n\nstatic void load_permissions_from_user_configuration(lua_State* L) {\n  static char file_operations_predicate_body_buffer[512];\n  initialize_trustedL();\n  file_operations_predicate_body = default_file_operations_predicate_body;\n  int status = load_file_operations_predicate(file_operations_predicate_body);\n  if (status != 0 && lua_isnil(trustedL, -1)) {\n    endwin();\n    printf(\"can't load default file operations predicate_body\\n\");\n    exit(1);\n  }\n  const char* rcfilename = user_configuration_filename();\n  FILE* in = fopen(rcfilename, \"r\");\n  if (in == NULL) return;\n  file_operations_predicate_body = default_file_operations_predicate_body;\n  assert(file_operations_predicate_body);\n  /* read entries from rcfilename and look for a match with the current\n   * Image_name. */\n  int oldtop = lua_gettop(L);\n  while (!feof(in)) {\n    teliva_load_definition(L, in);\n    if (lua_isnil(L, -1)) break;\n    lua_getfield(L, -1, \"image_name\");\n    const char* image_name = lua_tostring(L, -1);\n    if (strcmp(image_name, Image_name) == 0) {\n      lua_getfield(L, -2, \"file_operations_predicate_body\");\n      if (!lua_isnil(L, -1)) {\n        memset(file_operations_predicate_body_buffer, '\\0', 512);\n        strncpy(file_operations_predicate_body_buffer, lua_tostring(L, -1), 500);\n        file_operations_predicate_body = file_operations_predicate_body_buffer;\n      }\n      lua_pop(L, 1);  /* file_operations_predicate_body */\n      lua_getfield(L, -2, \"ask_for_permission_on_every_file_operation\");\n      ask_for_permission_on_every_file_operation = lua_tointeger(L, -1);\n      lua_pop(L, 1);  /* ask_for_permission_on_every_file_operation */\n      lua_getfield(L, -2, \"net_operations_permitted\");\n      net_operations_permitted = lua_tointeger(L, -1);\n      lua_pop(L, 1);  /* net_operations_permitted */\n    }\n    lua_pop(L, 1);  /* image_name */\n  }\n  lua_settop(L, oldtop);\n  fclose(in);\n  /* trusted section */\n  assert(file_operations_predicate_body);\n  status = load_file_operations_predicate(file_operations_predicate_body);\n  if (status == 0 || lua_isnil(trustedL, -1))\n    return;\n  /* TODO: more graceful error handling */\n  endwin();\n  printf(\"error in loading file operations predicate_body from %s\\n\", rcfilename);\n  exit(1);\n}\n\nvoid permissions_mode(lua_State* L) {\n  assume_default_colors(COLOR_FOREGROUND, COLOR_BACKGROUND);\n  init_pair(COLOR_PAIR_NORMAL, COLOR_FOREGROUND, COLOR_BACKGROUND);\n  init_pair(COLOR_PAIR_SELECTABLE, COLOR_SELECTABLE_FOREGROUND, COLOR_SELECTABLE_BACKGROUND);\n  init_pair(COLOR_PAIR_FADE, COLOR_FADE, COLOR_BACKGROUND);\n  init_pair(COLOR_PAIR_MENU_ALTERNATE, COLOR_MENU_ALTERNATE, COLOR_BACKGROUND);\n  init_pair(COLOR_PAIR_LUA_COMMENT, COLOR_LUA_COMMENT, COLOR_BACKGROUND);\n  init_pair(COLOR_PAIR_LUA_KEYWORD, COLOR_LUA_KEYWORD, COLOR_BACKGROUND);\n  init_pair(COLOR_PAIR_LUA_CONSTANT, COLOR_LUA_CONSTANT, COLOR_BACKGROUND);\n  init_pair(COLOR_PAIR_MATCH, COLOR_MATCH_FOREGROUND, COLOR_MATCH_BACKGROUND);\n  init_pair(COLOR_PAIR_ERROR, COLOR_ERROR_FOREGROUND, COLOR_ERROR_BACKGROUND);\n  /* permissions colors slightly different than in the menu */\n  init_pair(COLOR_PAIR_SAFE, COLOR_SAFE_NORMAL, COLOR_BACKGROUND);\n  init_pair(COLOR_PAIR_WARN, COLOR_WARN_NORMAL, COLOR_BACKGROUND);\n  init_pair(COLOR_PAIR_RISK, COLOR_RISK_NORMAL, COLOR_BACKGROUND);\n  nodelay(stdscr, 0);  /* always make getch() block in developer mode */\n  curs_set(1);  /* always display cursor in developer mode */\n  permissions_view();\n  save_permissions_to_user_configuration(L);\n  cleanup_curses();\n  execv(Argv[0], Argv);\n  /* never returns */\n}\n\n/*** (Audit) Events screen */\n\ntypedef struct {\n  char* line;\n  char* func;\n} AuditEvent;\n\n#define NAUDIT 8192\nAuditEvent audit_event[NAUDIT];\nint naudit = 0;\nint iaudit = 0;\n\nvoid append_to_audit_log(lua_State* L, const char* buffer) {\n  lua_Debug ar;\n  lua_getstack(L, 1, &ar);\n  lua_getinfo(L, \"n\", &ar);\n  if (!ar.name) return;\n  audit_event[naudit].line = strdup(buffer);\n  audit_event[naudit].func = strdup(ar.name);\n  ++naudit;\n  if (naudit >= NAUDIT)\n    naudit = 0;\n  if (naudit == iaudit) {\n    ++iaudit;\n    if (iaudit >= NAUDIT)\n      iaudit = 0;\n  }\n}\n\nstatic void events_menu() {\n  attrset(A_REVERSE);\n  for (int x = 0; x < COLS; ++x)\n    mvaddch(LINES-1, x, ' ');\n  attrset(A_NORMAL);\n  menu_column = 2;\n  draw_menu_item(\"^x\", \"go back\");\n  draw_menu_item(\"Enter\", \"go to highlight\");\n  attrset(A_NORMAL);\n}\n\nstatic void render_event(int i, int y, int cursor) {\n  mvaddstr(y, 2, \"\");\n  if (i == cursor)\n    draw_highlighted_definition_name(audit_event[i].func);\n  else\n    draw_definition_name(audit_event[i].func);\n  mvaddstr(y, 16, audit_event[i].line);\n}\n\nstatic void render_events(int cursor) {\n  clear();\n  attrset(A_BOLD);\n  mvaddstr(1, 0, \"Recent events\");\n  attrset(A_NORMAL);\n  if (iaudit == 0) {\n    /* circular buffer might not be full */\n    for (int i = 0, y = 3; i < naudit; ++i, ++y) {\n      if (i >= LINES-1) break;\n      render_event(i, y, cursor);\n    }\n  }\n  else {\n    /* circular buffer guaranteed to be full */\n    for (int i = 0, y = 3; i < NAUDIT; ++i, ++y) {\n      if (i >= LINES-1) break;\n      render_event((iaudit+i)%NAUDIT, y, cursor);\n    }\n  }\n  events_menu();\n  refresh();\n}\n\nstatic const char* events_view() {\n  int cursor = 0;\n  while (true) {\n    render_events(cursor);\n    int c = getch();\n    switch (c) {\n      case CTRL_X:\n        return NULL;\n      case KEY_UP:\n        if (cursor > 0)\n          --cursor;\n        break;\n      case KEY_DOWN:\n        if (cursor < naudit-1)\n          ++cursor;\n        break;\n      case ENTER:\n        return audit_event[cursor].func;\n    }\n  }\n}\n\n/*** Main */\n\nchar* Image_name = NULL;\nextern void set_args (lua_State *L, char **argv, int n);\nextern void load_tlv(lua_State* L, char* filename);\nint load_image(lua_State* L, char** argv, int n) {\n  int status;\n  set_args(L, argv, n);\n  /* parse and load file contents (teliva_program array) */\n  Image_name = argv[n];\n  load_tlv(L, Image_name);\n//?   save_tlv(L, Image_name);  // manual test; should always return identical result, modulo key order\n//?   exit(1);\n  status = load_definitions(L);\n  if (status != 0) return 0;\n  precompute_names_of_globals(L);\n  /* run tests */\n  status = run_tests(L);\n  if (status != 0) return report_in_developer_mode(L, status);\n  /* clear stats from running tests */\n  clear_call_graph_depth(L);\n  clear_caller(L);\n  /* initialize permissions */\n  load_permissions_from_user_configuration(L);\n  return 0;\n}\n"
  },
  {
    "path": "src/teliva.h",
    "content": "#ifndef __TELIVA_H__\n#define __TELIVA_H__\n\n/*** Some details for Teliva apps to be aware of. */\n\n/* Some names for hotkeys beyond those provided by ncurses. */\n/* TODO: expose these in the curses wrappers. */\n\nenum KEY_ACTION {\n  KEY_NULL = 0,\n  CTRL_A = 1,\n  CTRL_B = 2,\n  CTRL_C = 3,\n  CTRL_D = 4,\n  CTRL_E = 5,\n  CTRL_F = 6,\n  CTRL_G = 7,\n  CTRL_H = 8,\n  TAB = 9,\n  ENTER = 10,\n  CTRL_K = 11,\n  CTRL_L = 12,\n  CTRL_N = 14,\n  CTRL_P = 16,\n  CTRL_Q = 17,\n  CTRL_R = 18,\n  CTRL_S = 19,\n  CTRL_U = 21,\n  CTRL_X = 24,\n  CTRL_SLASH = 31,\n  CTRL_UNDERSCORE = 31,\n  DELETE = 127,\n};\n\n\n/* Colors (experimental)\n * Primary goal here: Keep text readable regardless of OS, terminal emulator\n * and color scheme. Unfortunately I don't yet have a good answer, so this\n * approach may yet change. Current approach:\n *  - Hardcode colors so that we can be sure we use legible combinations of\n *    foreground and background.\n *  - Use only the terminal palette in the range 16-255.\n *    - Not all terminals may support more than 256 colors. (I'm not yet sure\n *      everyone has even 256 colors. If you don't, please let me know:\n *      http://akkartik.name/contact)\n *    - Many terminals provide color schemes which give the ability to tweak\n *      colors 0-15. This makes it hard to assume specific combinations are\n *      legible. I'm hoping most terminal emulators don't tend to encourage\n *      messing with colors 16-255. (Please let me know if you know of\n *      counter-examples.)\n *\n * For now, you have to edit these values if you want to adjust colors in the\n * editing environment. Check out https://www.robmeerman.co.uk/unix/256colours\n * for a map of available colors. */\n\n/* Toggle between a few color schemes */\n#define COLOR_SCHEME 0\n#if COLOR_SCHEME == 0\n/* Light color scheme. */\nenum color {\n  COLOR_FOREGROUND = 238,                     /* almost black */\n  COLOR_BACKGROUND = 253,                     /* almost white */\n  COLOR_FADE = 244,                           /* closer to background */\n  COLOR_MENU_ALTERNATE = 248,\n  COLOR_SELECTABLE_FOREGROUND = 238,\n  COLOR_SELECTABLE_BACKGROUND = 250,\n  COLOR_ERROR_FOREGROUND = COLOR_BACKGROUND,\n  COLOR_ERROR_BACKGROUND = 124,               /* deep red */\n  COLOR_SAFE_NORMAL = 28,                     /* green */\n  COLOR_SAFE_REVERSE = 46,                    /* green */\n  COLOR_WARN_NORMAL = 208,                    /* orange */\n  COLOR_WARN_REVERSE = 208,                   /* orange */\n  COLOR_RISK_NORMAL = 196,                    /* red */\n  COLOR_RISK_REVERSE = 196,                   /* red */\n  COLOR_LUA_COMMENT = 27,                     /* blue */\n  COLOR_LUA_KEYWORD = 172,                    /* orange */\n  COLOR_LUA_CONSTANT = 31,                    /* cyan */\n  COLOR_MATCH_FOREGROUND = COLOR_BACKGROUND,\n  COLOR_MATCH_BACKGROUND = 28,                /* green */\n};\n#elif COLOR_SCHEME == 1\n/* Dark color scheme. */\nenum color {\n  COLOR_FOREGROUND = 253,                     /* almost white */\n  COLOR_BACKGROUND = 238,                     /* almost black */\n  COLOR_FADE = 244,                           /* closer to background */\n  COLOR_MENU_ALTERNATE = 244,\n  COLOR_SELECTABLE_FOREGROUND = 238,\n  COLOR_SELECTABLE_BACKGROUND = 250,\n  COLOR_ERROR_FOREGROUND = COLOR_FOREGROUND,\n  COLOR_ERROR_BACKGROUND = 124,               /* deep red */\n  COLOR_SAFE_NORMAL = 46,                     /* green */\n  COLOR_SAFE_REVERSE = 28,                    /* green */\n  COLOR_WARN_NORMAL = 208,                    /* orange */\n  COLOR_WARN_REVERSE = 130,                   /* orange */\n  COLOR_RISK_NORMAL = 196,                    /* red */\n  COLOR_RISK_REVERSE = 196,                   /* red */\n  COLOR_LUA_COMMENT = 39,                     /* blue */\n  COLOR_LUA_KEYWORD = 172,                    /* orange */\n  COLOR_LUA_CONSTANT = 37,                    /* cyan */\n  COLOR_MATCH_FOREGROUND = COLOR_BACKGROUND,\n  COLOR_MATCH_BACKGROUND = 28,                /* green */\n};\n#elif COLOR_SCHEME == 2\n/* Solarized dark. */\nenum color {\n  COLOR_FOREGROUND = 250,                     /* almost white */\n  COLOR_BACKGROUND = 24,                      /* dark blue-green */\n  COLOR_FADE = 246,                           /* closer to background */\n  COLOR_MENU_ALTERNATE = 244,\n  COLOR_SELECTABLE_FOREGROUND = 250,\n  COLOR_SELECTABLE_BACKGROUND = 31,\n  COLOR_ERROR_FOREGROUND = 250,\n  COLOR_ERROR_BACKGROUND = 124,               /* deep red */\n  COLOR_SAFE_NORMAL = 46,                     /* green */\n  COLOR_SAFE_REVERSE = 28,                    /* green */\n  COLOR_WARN_NORMAL = 208,                    /* orange */\n  COLOR_WARN_REVERSE = 130,                   /* orange */\n  COLOR_RISK_NORMAL = 201,                    /* red */\n  COLOR_RISK_REVERSE = 196,                   /* red */\n  COLOR_LUA_COMMENT = 45,                     /* light blue */\n  COLOR_LUA_KEYWORD = 172,                    /* orange */\n  COLOR_LUA_CONSTANT = 37,                    /* cyan */\n  COLOR_MATCH_FOREGROUND = COLOR_FOREGROUND,\n  COLOR_MATCH_BACKGROUND = 125,               /* magenta */\n};\n#endif\n\nenum color_pair {\n  COLOR_PAIR_NORMAL = 0,\n  COLOR_PAIR_SELECTABLE = 1,\n  COLOR_PAIR_FADE = 2,\n  COLOR_PAIR_MENU_ALTERNATE = 3,\n  COLOR_PAIR_LUA_COMMENT = 4,\n  COLOR_PAIR_LUA_KEYWORD = 5,\n  COLOR_PAIR_LUA_CONSTANT = 6,\n  COLOR_PAIR_MATCH = 7,\n  COLOR_PAIR_SAFE = 251,  /* reserved for teliva; apps shouldn't use it */\n  COLOR_PAIR_WARN = 252,  /* reserved for teliva; apps shouldn't use it */\n  COLOR_PAIR_RISK = 253,  /* reserved for teliva; apps shouldn't use it */\n  COLOR_PAIR_MENU = 254,  /* reserved for teliva; apps shouldn't use it */\n  COLOR_PAIR_ERROR = 255,  /* reserved for teliva; apps shouldn't use it */\n};\n\n/*** C Interface */\n\n/* Each category of primitives below shows a few options from high to low\n * levels of abstraction.\n * (Lower levels aren't complete or well-designed, just what code outside\n * teliva.c needs.) */\n\n/* Integrate with Lua VM */\nextern char** Argv;\nextern char* Previous_message;\nextern int load_image(lua_State* L, char** argv, int n);\nextern void developer_mode(lua_State* L);\nextern void permissions_mode(lua_State* L);\n\nextern void load_editor_buffer_to_current_definition_in_image(lua_State* L);\nextern int load_editor_buffer_to_current_definition_in_image_and_reload(lua_State* L);\nextern void save_to_current_definition_and_editor_buffer(lua_State* L, const char* definition);\nextern void save_editor_state(int rowoff, int coloff, int cy, int cx);\nint editor_view_in_progress(lua_State* L);\n\nextern void save_call_graph_depth(lua_State* L, int depth, const char* name);\nextern void draw_current_definition_name_and_callers(lua_State* L);\n\nextern void append_to_audit_log(lua_State* L, const char* buffer);\n\n/* Standard UI elements */\nextern void render_trusted_teliva_data(lua_State* L);\n\nextern void draw_menu_item(const char* key, const char* name);\n\nextern void draw_string_on_menu(const char* s);\n\nextern int menu_column;\n\nextern const char* character_name(char c);\n\n/* Error reporting */\n\nextern const char* Previous_error;\nextern int report_in_developer_mode(lua_State* L, int status);\n\nextern void render_previous_error(void);\n\n/* Permissions checking */\n\nextern int ask_for_permission_on_every_file_operation;\nextern int file_operation_permitted(const char* filename, const char* mode);\nextern int net_operations_permitted;\n\n/* Misc */\nextern int starts_with(const char* s, const char* prefix);\nextern int contains(const char* s, const char* sub);\n\n#endif\n"
  },
  {
    "path": "src/tlv.c",
    "content": "#include <assert.h>\n#ifdef __NetBSD__\n#include <curses.h>\n#else\n#include <ncurses.h>\n#endif\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <strings.h>\n\n#include \"lua.h\"\n#include \"lauxlib.h\"\n#include \"tlv.h\"\n\n/* If you encounter assertion failures in this file and _didn't_ manually edit\n * it, lease report the .tlv file that caused them: http://akkartik.name/contact.\n *\n * Manually edited files can have cryptic errors. Teliva's first priority is\n * to be secure, so it requires a fairly rigid file format and errors out if\n * things are even slightly amiss. */\n\n/* This code is surprisingly hairy. Estimate of buffer overflows: 2. */\n\nstatic void teliva_load_multiline_string(lua_State* L, FILE* in, char* line, int capacity) {\n  luaL_Buffer b;\n  luaL_buffinit(L, &b);\n  int expected_indent = -1;\n  while (1) {\n    if (feof(in)) break;\n    char c = fgetc(in);\n    ungetc(c, in);\n    if (c != ' ') {\n      /* new definition; signal end to caller without reading a new line */\n      strcpy(line, \"-\\n\");\n      break;\n    }\n    memset(line, '\\0', capacity);\n    if (fgets(line, capacity, in) == NULL) break;  /* eof */\n    int max = strlen(line);\n    assert(line[max-1] == '\\n');\n    int indent = 0;\n    while (indent < max-1 && line[indent] == ' ')\n      ++indent;\n    if (line[indent] != '>') break;  /* new key/value pair in definition */\n    if (expected_indent == -1)\n      expected_indent = indent;\n    else\n      assert(expected_indent == indent);\n    int start = indent+1;  /* skip '>' */\n    luaL_addstring(&b, &line[start]);  /* guaranteed to at least be null */\n  }\n  luaL_pushresult(&b);\n  /* final state of line goes out into the world */\n}\n\n/* leave a single table on stack containing the next top-level definition from the file */\nvoid teliva_load_definition(lua_State* L, FILE* in) {\n  lua_newtable(L);\n  int def_idx = lua_gettop(L);\n  char line[1024] = {'\\0'};\n  do {\n    if (feof(in) || fgets(line, 1024, in) == NULL) {\n      lua_pushnil(L);\n      return;\n    }\n  } while (line[0] == '#');  /* comment at start of file */\n  assert(line[strlen(line)-1] == '\\n');\n  do {\n    assert(line[0] == '-' || line[0] == ' ');\n    assert(line[1] == ' ');\n    /* key/value pair always indented at 0, never empty, unambiguously not a multiline string */\n    char key[512] = {'\\0'};\n    char value[1024] = {'\\0'};\n    assert(line[2] != ' ');\n    assert(line[2] != '>');\n    assert(line[2] != '\\n');\n    assert(line[2] != '\\0');\n    memset(key, 0, 512);\n    memset(value, 0, 1024);\n    sscanf(line+2, \"%s%s\", key, value);\n    assert(key[strlen(key)-1] == ':');\n    key[strlen(key)-1] = '\\0';\n    lua_pushstring(L, key);\n    if (value[0] != '\\0') {\n      lua_pushstring(L, value);  /* value string on same line */\n      char c = fgetc(in);\n      ungetc(c, in);\n      if (c == '-') {\n        strcpy(line, \"-\\n\");\n      }\n      else {\n        memset(line, '\\0', 1024);\n        fgets(line, 1024, in);\n      }\n    }\n    else {\n      teliva_load_multiline_string(L, in, line, 1024);  /* load from later lines */\n    }\n    lua_settable(L, def_idx);\n  } while (line[0] == ' ');\n}\n\nvoid load_tlv(lua_State* L, char* filename) {\n  lua_newtable(L);\n  int history_array = lua_gettop(L);\n  FILE* in = fopen(filename, \"r\");\n  if (in == NULL) {\n    endwin();\n    fprintf(stderr, \"no such file\\n\");\n    exit(1);\n  }\n  for (int i = 1; !feof(in); ++i) {\n    teliva_load_definition(L, in);\n    if (lua_isnil(L, -1)) break;\n    lua_rawseti(L, history_array, i);\n  }\n  fclose(in);\n  lua_setglobal(L, \"teliva_program\");\n}\n\nvoid emit_multiline_string(FILE* out, const char* value) {\n  fprintf(out, \"    >\");\n  for (const char* curr = value; *curr != '\\0'; ++curr) {\n    if (*curr == '\\n' && *(curr+1) != '\\0')\n      fprintf(out, \"\\n    >\");\n    else\n      fprintf(out, \"%c\", *curr);\n  }\n}\n\nstatic const char* special_history_keys[] = {\n  \"__teliva_timestamp\",\n  \"__teliva_note\",\n  \"__teliva_undo\",\n  NULL,\n};\n\n/* save key and its value at top of stack to out\n * no stack side effects */\nstatic void save_tlv_key(lua_State* L, const char* key, FILE* out) {\n  if (strcmp(key, \"__teliva_undo\") == 0) {\n    fprintf(out, \"%s: %ld\\n\", key, lua_tointeger(L, -1));\n    return;\n  }\n  const char* value = lua_tostring(L, -1);\n  if (strchr(value, ' ') || strchr(value, '\\n')) {\n    fprintf(out, \"%s:\\n\", key);\n    emit_multiline_string(out, value);\n  }\n  else {\n    fprintf(out, \"%s: %s\\n\", key, value);\n  }\n}\n\nvoid save_tlv(lua_State* L, char* filename) {\n  lua_getglobal(L, \"teliva_program\");\n  int history_array = lua_gettop(L);\n  int history_array_size = luaL_getn(L, history_array);\n  char outfilename[] = \"teliva_out_XXXXXX\";\n  int outfd = mkstemp(outfilename);\n  if (outfd == -1) {\n    endwin();\n    perror(\"save_tlv: error in creating temporary file\");\n    abort();\n  }\n  FILE *out = fdopen(outfd, \"w\");\n  fprintf(out, \"# .tlv file generated by https://github.com/akkartik/teliva\\n\");\n  fprintf(out, \"# You may edit it if you are careful; however, you may see cryptic errors if you\\n\");\n  fprintf(out, \"# violate Teliva's assumptions.\\n\");\n  fprintf(out, \"#\\n\");\n  fprintf(out, \"# .tlv files are representations of Teliva programs. Teliva programs consist of\\n\");\n  fprintf(out, \"# sequences of definitions. Each definition is a table of key/value pairs. Keys\\n\");\n  fprintf(out, \"# and values are both strings.\\n\");\n  fprintf(out, \"#\\n\");\n  fprintf(out, \"# Lines in .tlv files always follow exactly one of the following forms:\\n\");\n  fprintf(out, \"# - comment lines at the top of the file starting with '#' at column 0\\n\");\n  fprintf(out, \"# - beginnings of definitions starting with '- ' at column 0, followed by a\\n\");\n  fprintf(out, \"#   key/value pair\\n\");\n  fprintf(out, \"# - key/value pairs consisting of '  ' at column 0, containing either a\\n\");\n  fprintf(out, \"#   spaceless value on the same line, or a multi-line value\\n\");\n  fprintf(out, \"# - multiline values indented by more than 2 spaces, starting with a '>'\\n\");\n  fprintf(out, \"#\\n\");\n  fprintf(out, \"# If these constraints are violated, Teliva may unceremoniously crash. Please\\n\");\n  fprintf(out, \"# report bugs at http://akkartik.name/contact\\n\");\n  for (int i = 1;  i <= history_array_size; ++i) {\n    lua_rawgeti(L, history_array, i);\n    int table = lua_gettop(L);\n    int first = 1;\n    // standardize order of special keys\n    for (int k = 0;  special_history_keys[k];  ++k) {\n      lua_getfield(L, table, special_history_keys[k]);\n      if (!lua_isnil(L, -1)) {\n        if (first) fprintf(out, \"- \");\n        else fprintf(out, \"  \");\n        first = 0;\n        save_tlv_key(L, special_history_keys[k], out);\n      }\n      lua_pop(L, 1);\n    }\n    for (lua_pushnil(L); lua_next(L, table) != 0; lua_pop(L, 1)) {\n      if (is_special_history_key(lua_tostring(L, -2)))\n        continue;\n      if (first) fprintf(out, \"- \");\n      else fprintf(out, \"  \");\n      first = 0;\n      save_tlv_key(L, lua_tostring(L, -2), out);\n    }\n    lua_pop(L, 1);\n  }\n  fclose(out);\n  rename(outfilename, filename);\n  lua_pop(L, 1);\n}\n\nint is_special_history_key(const char* key) {\n  for (const char** curr = special_history_keys; *curr != NULL; ++curr) {\n    if (strcmp(*curr, key) == 0)\n      return 1;\n  }\n  return 0;\n}\n"
  },
  {
    "path": "src/tlv.h",
    "content": "#ifndef __TLV_H__\n#define __TLV_H__\n\n/* Helpers for working with the .tlv file format */\n\nextern void teliva_load_definition (lua_State* L, FILE* in);\nint is_special_history_key(const char* key);\n\nextern void emit_multiline_string(FILE* out, const char* value);\n\n#endif\n"
  },
  {
    "path": "src/vimrc.vim",
    "content": "set tabstop=8\nset softtabstop=2\nset shiftwidth=2\n"
  },
  {
    "path": "template.tlv",
    "content": "# .tlv file generated by https://github.com/akkartik/teliva\n# You may edit it if you are careful; however, you may see cryptic errors if you\n# violate Teliva's assumptions.\n#\n# .tlv files are representations of Teliva programs. Teliva programs consist of\n# sequences of definitions. Each definition is a table of key/value pairs. Keys\n# and values are both strings.\n#\n# Lines in .tlv files always follow exactly one of the following forms:\n# - comment lines at the top of the file starting with '#' at column 0\n# - beginnings of definitions starting with '- ' at column 0, followed by a\n#   key/value pair\n# - key/value pairs consisting of '  ' at column 0, containing either a\n#   spaceless value on the same line, or a multi-line value\n# - multiline values indented by more than 2 spaces, starting with a '>'\n#\n# If these constraints are violated, Teliva may unceremoniously crash. Please\n# report bugs at http://akkartik.name/contact\n- __teliva_timestamp: original\n  str_helpers:\n    >-- some string helpers from http://lua-users.org/wiki/StringIndexing\n    >\n    >-- index characters using []\n    >getmetatable('').__index = function(str,i)\n    >  if type(i) == 'number' then\n    >    return str:sub(i,i)\n    >  else\n    >    return string[i]\n    >  end\n    >end\n    >\n    >-- ranges using (), selected bytes using {}\n    >getmetatable('').__call = function(str,i,j)\n    >  if type(i)~='table' then\n    >    return str:sub(i,j)\n    >  else\n    >    local t={}\n    >    for k,v in ipairs(i) do\n    >      t[k]=str:sub(v,v)\n    >    end\n    >    return table.concat(t)\n    >  end\n    >end\n    >\n    >-- iterate over an ordered sequence\n    >function q(x)\n    >  if type(x) == 'string' then\n    >    return x:gmatch('.')\n    >  else\n    >    return ipairs(x)\n    >  end\n    >end\n    >\n    >-- insert within string\n    >function string.insert(str1, str2, pos)\n    >  return str1:sub(1,pos)..str2..str1:sub(pos+1)\n    >end\n    >\n    >function string.remove(s, pos)\n    >  return s:sub(1,pos-1)..s:sub(pos+1)\n    >end\n    >\n    >function string.pos(s, sub)\n    >  return string.find(s, sub, 1, true)  -- plain=true to disable regular expressions\n    >end\n    >\n    >-- TODO: backport utf-8 support from Lua 5.3\n- __teliva_timestamp: original\n  debugy:\n    >debugy = 5\n- __teliva_timestamp: original\n  dbg:\n    >-- helper for debug by print; overlay debug information towards the right\n    >-- reset debugy every time you refresh screen\n    >function dbg(window, s)\n    >  local oldy = 0\n    >  local oldx = 0\n    >  oldy, oldx = window:getyx()\n    >  window:mvaddstr(debugy, 60, s)\n    >  debugy = debugy+1\n    >  window:mvaddstr(oldy, oldx, '')\n    >end\n- __teliva_timestamp: original\n  check:\n    >function check(x, msg)\n    >  if x then\n    >    Window:addch('.')\n    >  else\n    >    print('F - '..msg)\n    >    print('  '..str(x)..' is false/nil')\n    >    teliva_num_test_failures = teliva_num_test_failures + 1\n    >    -- overlay first test failure on editors\n    >    if teliva_first_failure == nil then\n    >      teliva_first_failure = msg\n    >    end\n    >  end\n    >end\n- __teliva_timestamp: original\n  check_eq:\n    >function check_eq(x, expected, msg)\n    >  if eq(x, expected) then\n    >    Window:addch('.')\n    >  else\n    >    print('F - '..msg)\n    >    print('  expected '..str(expected)..' but got '..str(x))\n    >    teliva_num_test_failures = teliva_num_test_failures + 1\n    >    -- overlay first test failure on editors\n    >    if teliva_first_failure == nil then\n    >      teliva_first_failure = msg\n    >    end\n    >  end\n    >end\n- __teliva_timestamp: original\n  eq:\n    >function eq(a, b)\n    >  if type(a) ~= type(b) then return false end\n    >  if type(a) == 'table' then\n    >    if #a ~= #b then return false end\n    >    for k, v in pairs(a) do\n    >      if b[k] ~= v then\n    >        return false\n    >      end\n    >    end\n    >    for k, v in pairs(b) do\n    >      if a[k] ~= v then\n    >        return false\n    >      end\n    >    end\n    >    return true\n    >  end\n    >  return a == b\n    >end\n- __teliva_timestamp: original\n  str:\n    >-- smarter tostring\n    >-- slow; used only for debugging\n    >function str(x)\n    >  if type(x) == 'table' then\n    >    local result = ''\n    >    result = result..#x..'{'\n    >    for k, v in pairs(x) do\n    >      result = result..str(k)..'='..str(v)..', '\n    >    end\n    >    result = result..'}'\n    >    return result\n    >  elseif type(x) == 'string' then\n    >    return '\"'..x..'\"'\n    >  end\n    >  return tostring(x)\n    >end\n- __teliva_timestamp: original\n  find_index:\n    >function find_index(arr, x)\n    >  for n, y in ipairs(arr) do\n    >    if x == y then\n    >      return n\n    >    end\n    >  end\n    >end\n- __teliva_timestamp: original\n  trim:\n    >function trim(s)\n    >  return s:gsub('^%s*', ''):gsub('%s*$', '')\n    >end\n- __teliva_timestamp: original\n  split:\n    >function split(s, d)\n    >  result = {}\n    >  for match in (s..d):gmatch(\"(.-)\"..d) do\n    >    table.insert(result, match);\n    >  end\n    >  return result\n    >end\n- __teliva_timestamp: original\n  map:\n    >-- only for arrays\n    >function map(l, f)\n    >  result = {}\n    >  for _, x in ipairs(l) do\n    >    table.insert(result, f(x))\n    >  end\n    >  return result\n    >end\n- __teliva_timestamp: original\n  reduce:\n    >-- only for arrays\n    >function reduce(l, f, init)\n    >  result = init\n    >  for _, x in ipairs(l) do\n    >    result = f(result, x)\n    >  end\n    >  return result\n    >end\n- __teliva_timestamp: original\n  filter:\n    >function filter(h, f)\n    >  result = {}\n    >  for k, v in pairs(h) do\n    >    if f(k, v) then\n    >      result[k] = v\n    >    end\n    >  end\n    >  return result\n    >end\n- __teliva_timestamp: original\n  ifilter:\n    >-- only for arrays\n    >function ifilter(l, f)\n    >  result = {}\n    >  for _, x in ipairs(l) do\n    >    if f(x) then\n    >      table.insert(result, x)\n    >    end\n    >  end\n    >  return result\n    >end\n- __teliva_timestamp: original\n  sort_letters:\n    >function sort_letters(s)\n    >  tmp = {}\n    >  for i=1,#s do\n    >    table.insert(tmp, s[i])\n    >  end\n    >  table.sort(tmp)\n    >  local result = ''\n    >  for _, c in pairs(tmp) do\n    >    result = result..c\n    >  end\n    >  return result\n    >end\n    >\n    >function test_sort_letters(s)\n    >  check_eq(sort_letters(''), '', 'test_sort_letters: empty')\n    >  check_eq(sort_letters('ba'), 'ab', 'test_sort_letters: non-empty')\n    >  check_eq(sort_letters('abba'), 'aabb', 'test_sort_letters: duplicates')\n    >end\n- __teliva_timestamp: original\n  count_letters:\n    >-- TODO: handle unicode\n    >function count_letters(s)\n    >  local result = {}\n    >  for i=1,s:len() do\n    >    local c = s[i]\n    >    if result[c] == nil then\n    >      result[c] = 1\n    >    else\n    >      result[c] = result[c] + 1\n    >    end\n    >  end\n    >  return result\n    >end\n- __teliva_timestamp: original\n  count:\n    >-- turn an array of elements into a map from elements to their frequency\n    >-- analogous to count_letters for non-strings\n    >function count(a)\n    >  local result = {}\n    >  for i, v in ipairs(a) do\n    >    if result[v] == nil then\n    >      result[v] = 1\n    >    else\n    >      result[v] = result[v] + 1\n    >    end\n    >  end\n    >  return result\n    >end\n- __teliva_timestamp: original\n  union:\n    >function union(a, b)\n    >  for k, v in pairs(b) do\n    >    a[k] = v\n    >  end\n    >  return a\n    >end\n- __teliva_timestamp: original\n  subtract:\n    >-- set subtraction\n    >function subtract(a, b)\n    >  for k, v in pairs(b) do\n    >    a[k] = nil\n    >  end\n    >  return a\n    >end\n- __teliva_timestamp: original\n  all:\n    >-- universal quantifier on sets\n    >function all(s, f)\n    >  for k, v in pairs(s) do\n    >    if not f(k, v) then\n    >      return false\n    >    end\n    >  end\n    >  return true\n    >end\n- __teliva_timestamp: original\n  to_array:\n    >-- turn a set into an array\n    >-- drops values\n    >function to_array(h)\n    >  local result = {}\n    >  for k, _ in pairs(h) do\n    >    table.insert(result, k)\n    >  end\n    >  return result\n    >end\n- __teliva_timestamp: original\n  append:\n    >-- concatenate list 'elems' into 'l', modifying 'l' in the process\n    >function append(l, elems)\n    >  for i=1,#elems do\n    >    table.insert(l, elems[i])\n    >  end\n    >end\n- __teliva_timestamp: original\n  prepend:\n    >-- concatenate list 'elems' into the start of 'l', modifying 'l' in the process\n    >function prepend(l, elems)\n    >  for i=1,#elems do\n    >    table.insert(l, i, elems[i])\n    >  end\n    >end\n- __teliva_timestamp: original\n  all_but:\n    >function all_but(x, idx)\n    >  if type(x) == 'table' then\n    >    local result = {}\n    >    for i, elem in ipairs(x) do\n    >      if i ~= idx then\n    >        table.insert(result,elem)\n    >      end\n    >    end\n    >    return result\n    >  elseif type(x) == 'string' then\n    >    if idx < 1 then return x:sub(1) end\n    >    return x:sub(1, idx-1) .. x:sub(idx+1)\n    >  else\n    >    error('all_but: unsupported type '..type(x))\n    >  end\n    >end\n    >\n    >function test_all_but()\n    >  check_eq(all_but('', 0), '', 'all_but: empty')\n    >  check_eq(all_but('abc', 0), 'abc', 'all_but: invalid low index')\n    >  check_eq(all_but('abc', 4), 'abc', 'all_but: invalid high index')\n    >  check_eq(all_but('abc', 1), 'bc', 'all_but: first index')\n    >  check_eq(all_but('abc', 3), 'ab', 'all_but: final index')\n    >  check_eq(all_but('abc', 2), 'ac', 'all_but: middle index')\n    >end\n- __teliva_timestamp: original\n  set:\n    >function set(l)\n    >  local result = {}\n    >  for i, elem in ipairs(l) do\n    >    result[elem] = true\n    >  end\n    >  return result\n    >end\n- __teliva_timestamp: original\n  set_eq:\n    >function set_eq(l1, l2)\n    >  return eq(set(l1), set(l2))\n    >end\n    >\n    >function test_set_eq()\n    >  check(set_eq({1}, {1}), 'set_eq: identical')\n    >  check(not set_eq({1, 2}, {1, 3}), 'set_eq: different')\n    >  check(set_eq({1, 2}, {2, 1}), 'set_eq: order')\n    >  check(set_eq({1, 2, 2}, {2, 1}), 'set_eq: duplicates')\n    >end\n- __teliva_timestamp: original\n  clear:\n    >function clear(lines)\n    >  while #lines > 0 do\n    >    table.remove(lines)\n    >  end\n    >end\n- __teliva_timestamp: original\n  zap:\n    >function zap(target, src)\n    >  clear(target)\n    >  append(target, src)\n    >end\n- __teliva_timestamp: original\n  mfactorial:\n    >-- memoized version of factorial\n    >-- doesn't memoize recursive calls, but may be good enough\n    >mfactorial = memo1(factorial)\n- __teliva_timestamp: original\n  factorial:\n    >function factorial(n)\n    >  local result = 1\n    >  for i=1,n do\n    >    result = result*i\n    >  end\n    >  return result\n    >end\n- __teliva_timestamp: original\n  memo1:\n    >-- a higher-order function that takes a function of a single arg\n    >-- (that never returns nil)\n    >-- and returns a memoized version of it\n    >function memo1(f)\n    >  local memo = {}\n    >  return function(x)\n    >    if memo[x] == nil then\n    >      memo[x] = f(x)\n    >    end\n    >    return memo[x]\n    >  end\n    >end\n    >\n    >-- mfactorial doesn't seem noticeably faster\n    >function test_memo1()\n    >  for i=0,30 do\n    >    check_eq(mfactorial(i), factorial(i), 'memo1 over factorial: '..str(i))\n    >  end\n    >end\n- __teliva_timestamp: original\n  num_permutations:\n    >-- number of permutations of n distinct objects, taken r at a time\n    >function num_permutations(n, r)\n    >  return factorial(n)/factorial(n-r)\n    >end\n    >\n    >-- mfactorial doesn't seem noticeably faster\n    >function test_memo1()\n    >  for i=0,30 do\n    >    for j=0,i do\n    >      check_eq(num_permutations(i, j), mfactorial(i)/mfactorial(i-j), 'num_permutations memoizes: '..str(i)..'P'..str(j))\n    >    end\n    >  end\n    >end\n- __teliva_timestamp: original\n  menu:\n    >-- To show app-specific hotkeys in the menu bar, add hotkey/command\n    >-- arrays of strings to the menu array.\n    >menu = {}\n- __teliva_timestamp: original\n  Window:\n    >Window = curses.stdscr()\n- __teliva_timestamp: original\n  window:\n    >-- constructor for fake screen and window\n    >-- call it like this:\n    >--   local w = window{\n    >--     kbd=kbd('abc'),\n    >--     scr=scr{h=5, w=4},\n    >--   }\n    >-- eventually it'll do everything a real ncurses window can\n    >function window(h)\n    >  h.__index = h\n    >  setmetatable(h, h)\n    >  h.__index = function(table, key)\n    >    return rawget(h, key)\n    >  end\n    >  h.attrset = function(self, x)\n    >    self.scr.attrs = x\n    >  end\n    >  h.attron = function(self, x)\n    >    -- currently same as attrset since Lua 5.1 doesn't have bitwise operators\n    >    -- doesn't support multiple attrs at once\n    >--    local old = self.scr.attrs\n    >--    self.scr.attrs = old|x\n    >    self.scr.attrs = x\n    >  end\n    >  h.attroff = function(self, x)\n    >    -- currently borked since Lua 5.1 doesn't have bitwise operators\n    >    -- doesn't support multiple attrs at once\n    >--    local old = self.scr.attrs\n    >--    self.scr.attrs = old & (~x)\n    >    self.scr.attrs = curses.A_NORMAL\n    >  end\n    >  h.getch = function(self)\n    >    local c = table.remove(h.kbd, 1)\n    >    if c == nil then return c end\n    >    return string.byte(c)  -- for verisimilitude with ncurses\n    >  end\n    >  h.addch = function(self, c)\n    >    local scr = self.scr\n    >    if c == '\\n' then\n    >      scr.cursy = scr.cursy+1\n    >      scr.cursx = 0\n    >      return\n    >    end\n    >    if scr.cursy <= scr.h then\n    >      scr[scr.cursy][scr.cursx] = {data=c, attrs=scr.attrs}\n    >      scr.cursx = scr.cursx+1\n    >      if scr.cursx > scr.w then\n    >        scr.cursy = scr.cursy+1\n    >        scr.cursx = 1\n    >      end\n    >    end\n    >  end\n    >  h.addstr = function(self, s)\n    >    for i=1,s:len() do\n    >      self:addch(s[i])\n    >    end\n    >  end\n    >  h.mvaddch = function(self, y, x, c)\n    >    self.scr.cursy = y\n    >    self.scr.cursx = x\n    >    self:addch(c)\n    >  end\n    >  h.mvaddstr = function(self, y, x, s)\n    >    self.scr.cursy = y\n    >    self.scr.cursx = x\n    >    self:addstr(s)\n    >  end\n    >  h.clear = function(self)\n    >    clear_scr(self.scr)\n    >  end\n    >  h.refresh = function(self)\n    >    -- nothing\n    >  end\n    >  return h\n    >end\n- __teliva_timestamp: original\n  kbd:\n    >function kbd(keys)\n    >  local result = {}\n    >  for i=1,keys:len() do\n    >    table.insert(result, keys[i])\n    >  end\n    >  return result\n    >end\n- __teliva_timestamp: original\n  scr:\n    >function scr(props)\n    >  props.cursx = 1\n    >  props.cursy = 1\n    >  clear_scr(props)\n    >  return props\n    >end\n- __teliva_timestamp: original\n  clear_scr:\n    >function clear_scr(props)\n    >  props.cursy = 1\n    >  props.cursx = 1\n    >  for y=1,props.h do\n    >    props[y] = {}\n    >    for x=1,props.w do\n    >      props[y][x] = {data=' ', attrs=curses.A_NORMAL}\n    >    end\n    >  end\n    >  return props\n    >end\n- __teliva_timestamp: original\n  check_screen:\n    >function check_screen(window, contents, message)\n    >  local x, y = 1, 1\n    >  for i=1,contents:len() do\n    >    check_eq(window.scr[y][x].data, contents[i], message..'/'..y..','..x)\n    >    x = x+1\n    >    if x > window.scr.w then\n    >      y = y+1\n    >      x = 1\n    >    end\n    >  end\n    >end\n    >\n    >-- putting it all together, an example test of both keyboard and screen\n    >function test_check_screen()\n    >  local lines = {\n    >    c='123',\n    >    d='234',\n    >    a='345',\n    >    b='456',\n    >  }\n    >  local w = window{\n    >    kbd=kbd('abc'),\n    >    scr=scr{h=3, w=5},\n    >  }\n    >  local y = 1\n    >  while true do\n    >    local b = w:getch()\n    >    if b == nil then break end\n    >    w:mvaddstr(y, 1, lines[string.char(b)])\n    >    y = y+1\n    >  end\n    >  check_screen(w, '345  '..\n    >                  '456  '..\n    >                  '123  ',\n    >              'test_check_screen')\n    >end\n- __teliva_timestamp: original\n  check_reverse:\n    >function check_reverse(window, contents, message)\n    >  local x, y = 1, 1\n    >  for i=1,contents:len() do\n    >    if contents[i] ~= ' ' then\n    >      -- hacky version while we're without bitwise operators on Lua 5.1\n    >--      check(window.scr[y][x].attrs & curses.A_REVERSE, message..'/'..y..','..x)\n    >      check_eq(window.scr[y][x].attrs, curses.A_REVERSE, message..'/'..y..','..x)\n    >    else\n    >      -- hacky version while we're without bitwise operators on Lua 5.1\n    >--      check(window.scr[y][x].attrs & (~curses.A_REVERSE), message..'/'..y..','..x)\n    >      check(window.scr[y][x].attrs ~= curses.A_REVERSE, message..'/'..y..','..x)\n    >    end\n    >    x = x+1\n    >    if x > window.scr.w then\n    >      y = y+1\n    >      x = 1\n    >    end\n    >  end\n    >end\n- __teliva_timestamp: original\n  check_bold:\n    >function check_bold(window, contents, message)\n    >  local x, y = 1, 1\n    >  for i=1,contents:len() do\n    >    if contents[i] ~= ' ' then\n    >      -- hacky version while we're without bitwise operators on Lua 5.1\n    >--      check(window.scr[y][x].attrs & curses.A_BOLD, message..'/'..y..','..x)\n    >      check_eq(window.scr[y][x].attrs, curses.A_BOLD, message..'/'..y..','..x)\n    >    else\n    >      -- hacky version while we're without bitwise operators on Lua 5.1\n    >--      check(window.scr[y][x].attrs & (~curses.A_BOLD), message..'/'..y..','..x)\n    >      check(window.scr[y][x].attrs ~= curses.A_BOLD, message..'/'..y..','..x)\n    >    end\n    >    x = x+1\n    >    if x > window.scr.w then\n    >      y = y+1\n    >      x = 1\n    >    end\n    >  end\n    >end\n- __teliva_timestamp: original\n  check_color:\n    >-- check which parts of a screen have the given color_pair\n    >function check_color(window, cp, contents, message)\n    >  local x, y = 1, 1\n    >  for i=1,contents:len() do\n    >    if contents[i] ~= ' ' then\n    >      -- hacky version while we're without bitwise operators on Lua 5.1\n    >--      check(window.scr[y][x].attrs & curses.color_pair(cp), message..'/'..y..','..x)\n    >      check_eq(window.scr[y][x].attrs, curses.color_pair(cp), message..'/'..y..','..x)\n    >    else\n    >      -- hacky version while we're without bitwise operators on Lua 5.1\n    >--      check(window.scr[y][x].attrs & (~curses.A_BOLD), message..'/'..y..','..x)\n    >      check(window.scr[y][x].attrs ~= curses.color_pair(cp), message..'/'..y..','..x)\n    >    end\n    >    x = x+1\n    >    if x > window.scr.w then\n    >      y = y+1\n    >      x = 1\n    >    end\n    >  end\n    >end\n- __teliva_timestamp: original\n  sep:\n    >-- horizontal separator\n    >function sep(window)\n    >  local y, _ = window:getyx()\n    >  window:mvaddstr(y+1, 0, '')\n    >  local _, cols = window:getmaxyx()\n    >  for col=1,cols do\n    >    window:addstr('_')\n    >  end\n    >end\n- __teliva_timestamp: original\n  render:\n    >function render(window)\n    >  window:clear()\n    >  -- draw stuff to screen here\n    >  window:attron(curses.A_BOLD)\n    >  window:mvaddstr(1, 5, \"example app\")\n    >  window:attrset(curses.A_NORMAL)\n    >  for i=0,15 do\n    >    window:attrset(curses.color_pair(i))\n    >    window:mvaddstr(3+i, 5, \"========================\")\n    >  end\n    >  window:refresh()\n    >end\n- __teliva_timestamp: original\n  update:\n    >function update(window)\n    >  local key = window:getch()\n    >  -- process key here\n    >end\n- __teliva_timestamp: original\n  init_colors:\n    >function init_colors()\n    >  for i=0,7 do\n    >    curses.init_pair(i, i, -1)\n    >  end\n    >  curses.init_pair(8, 7, 0)\n    >  curses.init_pair(9, 7, 1)\n    >  curses.init_pair(10, 7, 2)\n    >  curses.init_pair(11, 7, 3)\n    >  curses.init_pair(12, 7, 4)\n    >  curses.init_pair(13, 7, 5)\n    >  curses.init_pair(14, 7, 6)\n    >  curses.init_pair(15, -1, 15)\n    >end\n- __teliva_timestamp: original\n  main:\n    >function main()\n    >  init_colors()\n    >\n    >  while true do\n    >    render(Window)\n    >    update(Window)\n    >  end\n    >end\n- __teliva_timestamp: original\n  doc:blurb:\n    >To show a brief description of the app on the 'big picture' screen, put the text in a special buffer called 'doc:blurb'.\n    >\n    >You can also override the default big picture screen entirely by creating a buffer called 'doc:main'.\n"
  },
  {
    "path": "toot-toot.tlv",
    "content": "# .tlv file generated by https://github.com/akkartik/teliva\n# You may edit it if you are careful; however, you may see cryptic errors if you\n# violate Teliva's assumptions.\n#\n# .tlv files are representations of Teliva programs. Teliva programs consist of\n# sequences of definitions. Each definition is a table of key/value pairs. Keys\n# and values are both strings.\n#\n# Lines in .tlv files always follow exactly one of the following forms:\n# - comment lines at the top of the file starting with '#' at column 0\n# - beginnings of definitions starting with '- ' at column 0, followed by a\n#   key/value pair\n# - key/value pairs consisting of '  ' at column 0, containing either a\n#   spaceless value on the same line, or a multi-line value\n# - multiline values indented by more than 2 spaces, starting with a '>'\n#\n# If these constraints are violated, Teliva may unceremoniously crash. Please\n# report bugs at http://akkartik.name/contact\n- __teliva_timestamp: original\n  str_helpers:\n    >-- some string helpers from http://lua-users.org/wiki/StringIndexing\n    >\n    >-- index characters using []\n    >getmetatable('').__index = function(str,i)\n    >  if type(i) == 'number' then\n    >    return str:sub(i,i)\n    >  else\n    >    return string[i]\n    >  end\n    >end\n    >\n    >-- ranges using (), selected bytes using {}\n    >getmetatable('').__call = function(str,i,j)\n    >  if type(i)~='table' then\n    >    return str:sub(i,j)\n    >  else\n    >    local t={}\n    >    for k,v in ipairs(i) do\n    >      t[k]=str:sub(v,v)\n    >    end\n    >    return table.concat(t)\n    >  end\n    >end\n    >\n    >-- iterate over an ordered sequence\n    >function q(x)\n    >  if type(x) == 'string' then\n    >    return x:gmatch('.')\n    >  else\n    >    return ipairs(x)\n    >  end\n    >end\n    >\n    >-- insert within string\n    >function string.insert(str1, str2, pos)\n    >  return str1:sub(1,pos)..str2..str1:sub(pos+1)\n    >end\n    >\n    >function string.remove(s, pos)\n    >  return s:sub(1,pos-1)..s:sub(pos+1)\n    >end\n    >\n    >function string.pos(s, sub)\n    >  return string.find(s, sub, 1, true)  -- plain=true to disable regular expressions\n    >end\n    >\n    >-- TODO: backport utf-8 support from Lua 5.3\n- __teliva_timestamp: original\n  debugy:\n    >debugy = 5\n- __teliva_timestamp: original\n  dbg:\n    >-- helper for debug by print; overlay debug information towards the right\n    >-- reset debugy every time you refresh screen\n    >function dbg(window, s)\n    >  local oldy = 0\n    >  local oldx = 0\n    >  oldy, oldx = window:getyx()\n    >  window:mvaddstr(debugy, 60, s)\n    >  debugy = debugy+1\n    >  window:mvaddstr(oldy, oldx, '')\n    >end\n- __teliva_timestamp: original\n  check:\n    >function check(x, msg)\n    >  if x then\n    >    Window:addch('.')\n    >  else\n    >    print('F - '..msg)\n    >    print('  '..str(x)..' is false/nil')\n    >    teliva_num_test_failures = teliva_num_test_failures + 1\n    >    -- overlay first test failure on editors\n    >    if teliva_first_failure == nil then\n    >      teliva_first_failure = msg\n    >    end\n    >  end\n    >end\n- __teliva_timestamp: original\n  check_eq:\n    >function check_eq(x, expected, msg)\n    >  if eq(x, expected) then\n    >    Window:addch('.')\n    >  else\n    >    print('F - '..msg)\n    >    print('  expected '..str(expected)..' but got '..str(x))\n    >    teliva_num_test_failures = teliva_num_test_failures + 1\n    >    -- overlay first test failure on editors\n    >    if teliva_first_failure == nil then\n    >      teliva_first_failure = msg\n    >    end\n    >  end\n    >end\n- __teliva_timestamp: original\n  eq:\n    >function eq(a, b)\n    >  if type(a) ~= type(b) then return false end\n    >  if type(a) == 'table' then\n    >    if #a ~= #b then return false end\n    >    for k, v in pairs(a) do\n    >      if b[k] ~= v then\n    >        return false\n    >      end\n    >    end\n    >    for k, v in pairs(b) do\n    >      if a[k] ~= v then\n    >        return false\n    >      end\n    >    end\n    >    return true\n    >  end\n    >  return a == b\n    >end\n- __teliva_timestamp: original\n  str:\n    >-- smarter tostring\n    >-- slow; used only for debugging\n    >function str(x)\n    >  if type(x) == 'table' then\n    >    local result = ''\n    >    result = result..#x..'{'\n    >    for k, v in pairs(x) do\n    >      result = result..str(k)..'='..str(v)..', '\n    >    end\n    >    result = result..'}'\n    >    return result\n    >  elseif type(x) == 'string' then\n    >    return '\"'..x..'\"'\n    >  end\n    >  return tostring(x)\n    >end\n- __teliva_timestamp: original\n  find_index:\n    >function find_index(arr, x)\n    >  for n, y in ipairs(arr) do\n    >    if x == y then\n    >      return n\n    >    end\n    >  end\n    >end\n- __teliva_timestamp: original\n  trim:\n    >function trim(s)\n    >  return s:gsub('^%s*', ''):gsub('%s*$', '')\n    >end\n- __teliva_timestamp: original\n  split:\n    >function split(s, d)\n    >  result = {}\n    >  for match in (s..d):gmatch(\"(.-)\"..d) do\n    >    table.insert(result, match);\n    >  end\n    >  return result\n    >end\n- __teliva_timestamp: original\n  map:\n    >-- only for arrays\n    >function map(l, f)\n    >  result = {}\n    >  for _, x in ipairs(l) do\n    >    table.insert(result, f(x))\n    >  end\n    >  return result\n    >end\n- __teliva_timestamp: original\n  reduce:\n    >-- only for arrays\n    >function reduce(l, f, init)\n    >  result = init\n    >  for _, x in ipairs(l) do\n    >    result = f(result, x)\n    >  end\n    >  return result\n    >end\n- __teliva_timestamp: original\n  filter:\n    >function filter(h, f)\n    >  result = {}\n    >  for k, v in pairs(h) do\n    >    if f(k, v) then\n    >      result[k] = v\n    >    end\n    >  end\n    >  return result\n    >end\n- __teliva_timestamp: original\n  ifilter:\n    >-- only for arrays\n    >function ifilter(l, f)\n    >  result = {}\n    >  for _, x in ipairs(l) do\n    >    if f(x) then\n    >      table.insert(result, x)\n    >    end\n    >  end\n    >  return result\n    >end\n- __teliva_timestamp: original\n  sort_letters:\n    >function sort_letters(s)\n    >  tmp = {}\n    >  for i=1,#s do\n    >    table.insert(tmp, s[i])\n    >  end\n    >  table.sort(tmp)\n    >  local result = ''\n    >  for _, c in pairs(tmp) do\n    >    result = result..c\n    >  end\n    >  return result\n    >end\n    >\n    >function test_sort_letters(s)\n    >  check_eq(sort_letters(''), '', 'test_sort_letters: empty')\n    >  check_eq(sort_letters('ba'), 'ab', 'test_sort_letters: non-empty')\n    >  check_eq(sort_letters('abba'), 'aabb', 'test_sort_letters: duplicates')\n    >end\n- __teliva_timestamp: original\n  count_letters:\n    >-- TODO: handle unicode\n    >function count_letters(s)\n    >  local result = {}\n    >  for i=1,s:len() do\n    >    local c = s[i]\n    >    if result[c] == nil then\n    >      result[c] = 1\n    >    else\n    >      result[c] = result[c] + 1\n    >    end\n    >  end\n    >  return result\n    >end\n- __teliva_timestamp: original\n  count:\n    >-- turn an array of elements into a map from elements to their frequency\n    >-- analogous to count_letters for non-strings\n    >function count(a)\n    >  local result = {}\n    >  for i, v in ipairs(a) do\n    >    if result[v] == nil then\n    >      result[v] = 1\n    >    else\n    >      result[v] = result[v] + 1\n    >    end\n    >  end\n    >  return result\n    >end\n- __teliva_timestamp: original\n  union:\n    >function union(a, b)\n    >  for k, v in pairs(b) do\n    >    a[k] = v\n    >  end\n    >  return a\n    >end\n- __teliva_timestamp: original\n  subtract:\n    >-- set subtraction\n    >function subtract(a, b)\n    >  for k, v in pairs(b) do\n    >    a[k] = nil\n    >  end\n    >  return a\n    >end\n- __teliva_timestamp: original\n  all:\n    >-- universal quantifier on sets\n    >function all(s, f)\n    >  for k, v in pairs(s) do\n    >    if not f(k, v) then\n    >      return false\n    >    end\n    >  end\n    >  return true\n    >end\n- __teliva_timestamp: original\n  to_array:\n    >-- turn a set into an array\n    >-- drops values\n    >function to_array(h)\n    >  local result = {}\n    >  for k, _ in pairs(h) do\n    >    table.insert(result, k)\n    >  end\n    >  return result\n    >end\n- __teliva_timestamp: original\n  append:\n    >-- concatenate list 'elems' into 'l', modifying 'l' in the process\n    >function append(l, elems)\n    >  for i=1,#elems do\n    >    table.insert(l, elems[i])\n    >  end\n    >end\n- __teliva_timestamp: original\n  prepend:\n    >-- concatenate list 'elems' into the start of 'l', modifying 'l' in the process\n    >function prepend(l, elems)\n    >  for i=1,#elems do\n    >    table.insert(l, i, elems[i])\n    >  end\n    >end\n- __teliva_timestamp: original\n  all_but:\n    >function all_but(x, idx)\n    >  if type(x) == 'table' then\n    >    local result = {}\n    >    for i, elem in ipairs(x) do\n    >      if i ~= idx then\n    >        table.insert(result,elem)\n    >      end\n    >    end\n    >    return result\n    >  elseif type(x) == 'string' then\n    >    if idx < 1 then return x:sub(1) end\n    >    return x:sub(1, idx-1) .. x:sub(idx+1)\n    >  else\n    >    error('all_but: unsupported type '..type(x))\n    >  end\n    >end\n    >\n    >function test_all_but()\n    >  check_eq(all_but('', 0), '', 'all_but: empty')\n    >  check_eq(all_but('abc', 0), 'abc', 'all_but: invalid low index')\n    >  check_eq(all_but('abc', 4), 'abc', 'all_but: invalid high index')\n    >  check_eq(all_but('abc', 1), 'bc', 'all_but: first index')\n    >  check_eq(all_but('abc', 3), 'ab', 'all_but: final index')\n    >  check_eq(all_but('abc', 2), 'ac', 'all_but: middle index')\n    >end\n- __teliva_timestamp: original\n  set:\n    >function set(l)\n    >  local result = {}\n    >  for i, elem in ipairs(l) do\n    >    result[elem] = true\n    >  end\n    >  return result\n    >end\n- __teliva_timestamp: original\n  set_eq:\n    >function set_eq(l1, l2)\n    >  return eq(set(l1), set(l2))\n    >end\n    >\n    >function test_set_eq()\n    >  check(set_eq({1}, {1}), 'set_eq: identical')\n    >  check(not set_eq({1, 2}, {1, 3}), 'set_eq: different')\n    >  check(set_eq({1, 2}, {2, 1}), 'set_eq: order')\n    >  check(set_eq({1, 2, 2}, {2, 1}), 'set_eq: duplicates')\n    >end\n- __teliva_timestamp: original\n  clear:\n    >function clear(lines)\n    >  while #lines > 0 do\n    >    table.remove(lines)\n    >  end\n    >end\n- __teliva_timestamp: original\n  zap:\n    >function zap(target, src)\n    >  clear(target)\n    >  append(target, src)\n    >end\n- __teliva_timestamp: original\n  mfactorial:\n    >-- memoized version of factorial\n    >-- doesn't memoize recursive calls, but may be good enough\n    >mfactorial = memo1(factorial)\n- __teliva_timestamp: original\n  factorial:\n    >function factorial(n)\n    >  local result = 1\n    >  for i=1,n do\n    >    result = result*i\n    >  end\n    >  return result\n    >end\n- __teliva_timestamp: original\n  memo1:\n    >-- a higher-order function that takes a function of a single arg\n    >-- (that never returns nil)\n    >-- and returns a memoized version of it\n    >function memo1(f)\n    >  local memo = {}\n    >  return function(x)\n    >    if memo[x] == nil then\n    >      memo[x] = f(x)\n    >    end\n    >    return memo[x]\n    >  end\n    >end\n    >\n    >-- mfactorial doesn't seem noticeably faster\n    >function test_memo1()\n    >  for i=0,30 do\n    >    check_eq(mfactorial(i), factorial(i), 'memo1 over factorial: '..str(i))\n    >  end\n    >end\n- __teliva_timestamp: original\n  num_permutations:\n    >-- number of permutations of n distinct objects, taken r at a time\n    >function num_permutations(n, r)\n    >  return factorial(n)/factorial(n-r)\n    >end\n    >\n    >-- mfactorial doesn't seem noticeably faster\n    >function test_memo1()\n    >  for i=0,30 do\n    >    for j=0,i do\n    >      check_eq(num_permutations(i, j), mfactorial(i)/mfactorial(i-j), 'num_permutations memoizes: '..str(i)..'P'..str(j))\n    >    end\n    >  end\n    >end\n- __teliva_timestamp: original\n  menu:\n    >-- To show app-specific hotkeys in the menu bar, add hotkey/command\n    >-- arrays of strings to the menu array.\n    >menu = {\n    >  {'^k', 'clear'},\n    >  {'^w', 'write prose to file \"toot\" (edit hotkey does NOT save)'},\n    >}\n- __teliva_timestamp: original\n  Window:\n    >Window = curses.stdscr()\n    >curses.curs_set(0)  -- we'll simulate our own cursor\n- __teliva_timestamp: original\n  main:\n    >function main()\n    >  init_colors()\n    >\n    >  while true do\n    >    render(Window)\n    >    update(Window)\n    >  end\n    >end\n- __teliva_timestamp: original\n  init_colors:\n    >function init_colors()\n    >  for i=0,7 do\n    >    curses.init_pair(i, i, -1)\n    >  end\n    >  curses.init_pair(8, 7, 0)\n    >  curses.init_pair(9, 7, 1)\n    >  curses.init_pair(10, 7, 2)\n    >  curses.init_pair(11, 7, 3)\n    >  curses.init_pair(12, 7, 4)\n    >  curses.init_pair(13, 7, 5)\n    >  curses.init_pair(14, 7, 6)\n    >  curses.init_pair(15, -1, 15)\n    >end\n- __teliva_timestamp: original\n  prose:\n    >prose = ''\n- __teliva_timestamp: original\n  cursor:\n    >cursor = 1\n- __teliva_timestamp: original\n  render:\n    >function render(window)\n    >  window:clear()\n    >  debugy = 5\n    >  local toots = split(prose, '\\n\\n===\\n\\n')\n    >  pos = 1\n    >  for i, toot in ipairs(toots) do\n    >    if i > 1 then\n    >      pos = render_delimiter(window, '\\n\\n===\\n\\n', pos, cursor)\n    >    end\n    >    pos = render_text(window, toot, pos, cursor)\n    >    print('')\n    >    window:attron(curses.A_BOLD)\n    >    window:addstr(toot:len())\n    >    window:attroff(curses.A_BOLD)\n    >  end\n    >  window:refresh()\n    >end\n- __teliva_timestamp: original\n  render_delimiter:\n    >function render_delimiter(window, s, pos, cursor)\n    >  local newpos = pos\n    >  for i=1,s:len() do\n    >    if newpos == cursor and i ~= 1 then\n    >      if s[i] == '\\n' then\n    >        -- newline at cursor = render extra space in reverse video before jumping to new line\n    >        window:attron(curses.A_REVERSE)\n    >        window:addch(' ')\n    >        window:attroff(curses.A_REVERSE)\n    >        window:addstr(s[i])\n    >      else\n    >        -- most characters at cursor = render in reverse video\n    >        window:attron(curses.A_REVERSE)\n    >        window:addstr(s[i])\n    >        window:attroff(curses.A_REVERSE)\n    >      end\n    >    else\n    >      window:addstr(s[i])\n    >    end\n    >    newpos = newpos+1\n    >  end\n    >  return newpos\n    >end\n- __teliva_timestamp: original\n  render_text:\n    >-- https://gankra.github.io/blah/text-hates-you\n    >-- https://lord.io/text-editing-hates-you-too\n    >\n    >-- manual tests:\n    >--   cursor on some character\n    >--   cursor on (within) '\\n\\n===\\n\\n' delimiter (delimiter is hardcoded; things may break if you change it)\n    >--   cursor at end of each line\n    >--   render digits\n    >\n    >-- positions serve two purposes:\n    >--   character to index into prose\n    >--   cursor-printing\n    >\n    >-- sequence of stories\n    >--   focus on rendering a single piece of text, first get that rock-solid\n    >--   split prose into toots, manage transitions between toots in response to cursor movements\n    >--   cursor movement: left/right vs up/down\n    >\n    >-- what is the ideal representation?\n    >--   prose + cursor has issues in multi-toot context. when to display cursor?\n    >function render_text(window, s, pos, cursor)\n    >  local newpos = pos\n    >--?   dbg(window, '--')\n    >  for i=1,s:len() do\n    >--?     dbg(window, tostring(newpos)..' '..tostring(string.byte(s[i])))\n    >    if newpos == cursor then\n    >--?       dbg(window, 'cursor: '..tostring(cursor))\n    >      if s[i] == '\\n' then\n    >        -- newline at cursor = render extra space in reverse video before jumping to new line\n    >        window:attron(curses.A_REVERSE)\n    >        window:addch(' ')\n    >        window:attroff(curses.A_REVERSE)\n    >        window:addstr(s[i])\n    >      else\n    >        -- most characters at cursor = render in reverse video\n    >        window:attron(curses.A_REVERSE)\n    >        window:addstr(s[i])\n    >        window:attroff(curses.A_REVERSE)\n    >      end\n    >    else\n    >      window:addstr(s[i])\n    >    end\n    >    newpos = newpos+1\n    >  end\n    >  if newpos == cursor then\n    >    window:attron(curses.A_REVERSE)\n    >    window:addch(' ')\n    >    window:attroff(curses.A_REVERSE)\n    >  end\n    >  return newpos\n    >end\n- __teliva_timestamp: original\n  update:\n    >function update(window)\n    >  local key = window:getch()\n    >  local h, w = window:getmaxyx()\n    >  if key == curses.KEY_LEFT then\n    >    if cursor > 1 then\n    >      cursor = cursor-1\n    >    end\n    >  elseif key == curses.KEY_RIGHT then\n    >    if cursor <= #prose then\n    >      cursor = cursor+1\n    >    end\n    >  elseif key == curses.KEY_DOWN then\n    >    cursor = cursor_down(prose, cursor, w)\n    >  elseif key == curses.KEY_UP then\n    >    cursor = cursor_up(prose, cursor, w)\n    >  elseif key == curses.KEY_BACKSPACE or key == 8 or key == 127 then  -- ctrl-h, ctrl-?, delete\n    >    if cursor > 1 then\n    >      cursor = cursor-1\n    >      prose = prose:remove(cursor)\n    >    end\n    >  elseif key == 11 then  -- ctrl-k\n    >    prose = ''\n    >    cursor = 1\n    >  elseif key == 23 then  -- ctrl-w\n    >    local out = io.open('toot', 'w')\n    >    if out ~= nil then\n    >      out:write(prose, '\\n')\n    >      out:close()\n    >    end\n    >  elseif key == 10 or (key >= 32 and key < 127) then\n    >    prose = prose:insert(string.char(key), cursor-1)\n    >    cursor = cursor+1\n    >  end\n    >end\n- __teliva_timestamp: original\n  cursor_down:\n    >function cursor_down(s, old_idx, width)\n    >  local max = string.len(s)\n    >  local i = 1\n    >  -- compute oldcol, the screen column of old_idx\n    >  local oldcol = 0\n    >  local col = 0\n    >  while true do\n    >    if i > max then\n    >      -- abnormal old_idx\n    >      return old_idx\n    >    end\n    >    if i == old_idx then\n    >      oldcol = col\n    >      break\n    >    end\n    >    if s[i] == '\\n' then\n    >      col = 0\n    >    else\n    >      col = col+1\n    >    end\n    >    i = i+1\n    >  end\n    >  -- skip rest of line\n    >  while true do\n    >    if i > max then\n    >      -- current line is at bottom\n    >      if col >= width then\n    >        return i\n    >      end\n    >      return old_idx\n    >    end\n    >    if s[i] == '\\n' then\n    >      break\n    >    end\n    >    if i - old_idx >= width then\n    >      return i\n    >    end\n    >    col = col+1\n    >    i = i+1\n    >  end\n    >  -- compute index at same column on next line\n    >  -- i is at a newline\n    >  i = i+1\n    >  col = 0\n    >  while true do\n    >    if i > max then\n    >      -- next line is at bottom and is too short; position at end of it\n    >      return i\n    >    end\n    >    if s[i] == '\\n' then\n    >      -- next line is too short; position at end of it\n    >      return i\n    >    end\n    >    if col == oldcol then\n    >      return i\n    >    end\n    >    col = col+1\n    >    i = i+1\n    >  end\n    >end\n    >\n    >function test_cursor_down()\n    >  -- lines that don't wrap\n    >  check_eq(cursor_down('abc\\ndef', 1, 5), 5, 'cursor_down: non-bottom line first char')\n    >  check_eq(cursor_down('abc\\ndef', 2, 5), 6, 'cursor_down: non-bottom line mid char')\n    >  check_eq(cursor_down('abc\\ndef', 3, 5), 7, 'cursor_down: non-bottom line final char')\n    >  check_eq(cursor_down('abc\\ndef', 4, 5), 8, 'cursor_down: non-bottom line end')\n    >  check_eq(cursor_down('abc\\ndef', 5, 5), 5, 'cursor_down: bottom line first char')\n    >  check_eq(cursor_down('abc\\ndef', 6, 5), 6, 'cursor_down: bottom line mid char')\n    >  check_eq(cursor_down('abc\\ndef', 7, 5), 7, 'cursor_down: bottom line final char')\n    >  check_eq(cursor_down('abc\\n\\ndef', 2, 5), 5, 'cursor_down: to shorter line')\n    >\n    >  -- within a single wrapping line\n    >  --   |abcde|  <-- wrap, no newline\n    >  --   |fgh  |\n    >  check_eq(cursor_down('abcdefgh', 1, 5), 6, 'cursor_down from wrapping line: first char')\n    >  check_eq(cursor_down('abcdefgh', 2, 5), 7, 'cursor_down from wrapping line: mid char')\n    >  check_eq(cursor_down('abcdefgh', 5, 5), 9, 'cursor_down from wrapping line: to shorter line')\n    >\n    >  -- within a single very long wrapping line\n    >  --   |abcde|  <-- wrap, no newline\n    >  --   |fghij|  <-- wrap, no newline\n    >  --   |klm  |\n    >  check_eq(cursor_down('abcdefghijklm', 1, 5), 6, 'cursor_down within wrapping line: first char')\n    >  check_eq(cursor_down('abcdefghijklm', 2, 5), 7, 'cursor_down within wrapping line: mid char')\n    >  check_eq(cursor_down('abcdefghijklm', 5, 5), 10, 'cursor_down within wrapping line: final char')\n    >end\n- __teliva_timestamp: original\n  cursor_up:\n    >function cursor_up(s, old_idx, width)\n    >  local max = string.len(s)\n    >  local i = 1\n    >  -- compute oldcol, the screen column of old_idx\n    >  local oldcol = 0\n    >  local col = 0\n    >  local newline_before_current_line = 0\n    >  while true do\n    >    if i > max or i == old_idx then\n    >      oldcol = col\n    >      break\n    >    end\n    >    if s[i] == '\\n' then\n    >      col = 0\n    >      newline_before_current_line = i\n    >    else\n    >      col = col+1\n    >      if col == width then\n    >        col = 0\n    >      end\n    >    end\n    >    i = i+1\n    >  end\n    >  -- find previous newline\n    >  i = i-col-1\n    >  if old_idx - newline_before_current_line > width then\n    >    -- we're in a wrapped line\n    >    return old_idx - width\n    >  end\n    >  -- scan back to start of previous line\n    >  if s[i] == '\\n' then\n    >    i = i-1\n    >  end\n    >  while true do\n    >    if i < 1 then\n    >      -- current line is at top\n    >      break\n    >    end\n    >    if s[i] == '\\n' then\n    >      break\n    >    end\n    >    i = i-1\n    >  end\n    >  -- i is at a newline\n    >  i = i+1\n    >  -- skip whole screen lines within previous line\n    >  while newline_before_current_line - i > width do\n    >    i = i + width\n    >  end\n    >  -- compute index at same column on previous screen line\n    >  col = 0\n    >  while true do\n    >    if i > max then\n    >      -- next line is at bottom and is too short; position at end of it\n    >      return i\n    >    end\n    >    if s[i] == '\\n' then\n    >      -- next line is too short; position at end of it\n    >      return i\n    >    end\n    >    if col == oldcol then\n    >      return i\n    >    end\n    >    col = col+1\n    >    i = i+1\n    >  end\n    >end\n    >\n    >function test_cursor_up()\n    >  -- lines that don't wrap\n    >  check_eq(cursor_up('abc\\ndef', 1, 5), 1, 'cursor_up: top line first char')\n    >  check_eq(cursor_up('abc\\ndef', 2, 5), 2, 'cursor_up: top line mid char')\n    >  check_eq(cursor_up('abc\\ndef', 3, 5), 3, 'cursor_up: top line final char')\n    >  check_eq(cursor_up('abc\\ndef', 4, 5), 4, 'cursor_up: top line end')\n    >  check_eq(cursor_up('abc\\ndef', 5, 5), 1, 'cursor_up: non-top line first char')\n    >  check_eq(cursor_up('abc\\ndef', 6, 5), 2, 'cursor_up: non-top line mid char')\n    >  check_eq(cursor_up('abc\\ndef', 7, 5), 3, 'cursor_up: non-top line final char')\n    >  check_eq(cursor_up('abc\\ndef\\n', 8, 5), 4, 'cursor_up: non-top line end')\n    >  check_eq(cursor_up('ab\\ndef\\n', 7, 5), 3, 'cursor_up: to shorter line')\n    >\n    >  -- within a single wrapping line\n    >  --   |abcde|  <-- wrap, no newline\n    >  --   |fgh  |\n    >  check_eq(cursor_up('abcdefgh', 6, 5), 1, 'cursor_up from wrapping line: first char')\n    >  check_eq(cursor_up('abcdefgh', 7, 5), 2, 'cursor_up from wrapping line: mid char')\n    >  check_eq(cursor_up('abcdefgh', 8, 5), 3, 'cursor_up from wrapping line: final char')\n    >  check_eq(cursor_up('abcdefgh', 9, 5), 4, 'cursor_up from wrapping line: wrapped line end')\n    >\n    >  -- within a single very long wrapping line\n    >  --   |abcde|  <-- wrap, no newline\n    >  --   |fghij|  <-- wrap, no newline\n    >  --   |klm  |\n    >  check_eq(cursor_up('abcdefghijklm', 11, 5), 6, 'cursor_up within wrapping line: first char')\n    >  check_eq(cursor_up('abcdefghijklm', 12, 5), 7, 'cursor_up within wrapping line: mid char')\n    >  check_eq(cursor_up('abcdefghijklm', 13, 5), 8, 'cursor_up within wrapping line: final char')\n    >  check_eq(cursor_up('abcdefghijklm', 14, 5), 9, 'cursor_up within wrapping line: wrapped line end')\n    >\n    >  -- from below to (the bottom of) a wrapping line\n    >  --   |abcde|  <-- wrap, no newline\n    >  --   |fg   |\n    >  --   |hij  |\n    >  check_eq(cursor_up('abcdefg\\nhij', 9, 5), 6, 'cursor_up to wrapping line: first char')\n    >  check_eq(cursor_up('abcdefg\\nhij', 10, 5), 7, 'cursor_up to wrapping line: mid char')\n    >  check_eq(cursor_up('abcdefg\\nhij', 11, 5), 8, 'cursor_up to wrapping line: final char')\n    >  check_eq(cursor_up('abcdefg\\nhij', 12, 5), 8, 'cursor_up to wrapping line: to shorter line')\n    >end\n- __teliva_timestamp:\n    >Thu Feb 17 19:52:30 2022\n  doc:blurb:\n    >A tiny editor (no scrolling) for composing a series of toots or tweets. Always shows character counts for current state of prose.\n    >\n    >Typing '===' on its own lines, surrounded by empty lines, partitions prose and gives all segments independent character counts. Good for threads (tweetstorms).\n- __teliva_timestamp:\n    >Fri Mar 11 09:45:27 2022\n  first_toot:\n    >first_toot = 1\n- __teliva_timestamp:\n    >Fri Mar 11 11:47:34 2022\n  update:\n    >function update(window)\n    >  local key = window:getch()\n    >  local h, w = window:getmaxyx()\n    >  if key == curses.KEY_LEFT then\n    >    if cursor > 1 then\n    >      cursor = cursor-1\n    >    end\n    >  elseif key == curses.KEY_RIGHT then\n    >    if cursor <= #prose then\n    >      cursor = cursor+1\n    >    end\n    >  elseif key == curses.KEY_DOWN then\n    >    cursor = cursor_down(prose, cursor, w)\n    >  elseif key == curses.KEY_UP then\n    >    cursor = cursor_up(prose, cursor, w)\n    >  elseif key == curses.KEY_BACKSPACE or key == 8 or key == 127 then  -- ctrl-h, ctrl-?, delete\n    >    if cursor > 1 then\n    >      cursor = cursor-1\n    >      prose = prose:remove(cursor)\n    >    end\n    >  elseif key == 6 then  -- ctrl-f\n    >    first_toot = first_toot+1\n    >  elseif key == 2 then  -- ctrl-b\n    >    if first_toot > 1 then\n    >      first_toot = first_toot-1\n    >    end\n    >  elseif key == 11 then  -- ctrl-k\n    >    prose = ''\n    >    cursor = 1\n    >  elseif key == 23 then  -- ctrl-w\n    >    local out = io.open('toot', 'w')\n    >    if out ~= nil then\n    >      out:write(prose, '\\n')\n    >      out:close()\n    >    end\n    >  elseif key == 10 or (key >= 32 and key < 127) then\n    >    prose = prose:insert(string.char(key), cursor-1)\n    >    cursor = cursor+1\n    >  end\n    >end\n- __teliva_timestamp:\n    >Fri Mar 11 11:48:43 2022\n  menu:\n    >-- To show app-specific hotkeys in the menu bar, add hotkey/command\n    >-- arrays of strings to the menu array.\n    >menu = {\n    >  {'^w', 'write to \"toot\"'},\n    >  {'^f|^b', 'scroll'},\n    >  {'^k', 'clear'},\n    >}\n- __teliva_timestamp:\n    >Sat Mar 12 08:48:44 2022\n  render:\n    >function render(window)\n    >  window:clear()\n    >  debugy = 5\n    >  local toots = split(prose, '\\n\\n===\\n\\n')\n    >  pos = 1\n    >  for i, toot in ipairs(toots) do\n    >--?     dbg(window, \"render: \"..i..\" pos \"..pos..\" cursor \"..cursor)\n    >    if i > 1 then\n    >      pos = render_delimiter(window, '\\n\\n===\\n\\n', pos, cursor)\n    >--?       dbg(window, \"delim: \"..pos..\" cursor \"..cursor)\n    >    end\n    >    if i <= first_toot then\n    >      window:clear()\n    >    end\n    >    pos = render_text(window, toot, pos, cursor)\n    >    print('')\n    >--?     dbg(window, \"text: \"..pos..\" cursor \"..cursor)\n    >    window:attron(curses.A_BOLD)\n    >    window:addstr(toot:len())\n    >    window:attroff(curses.A_BOLD)\n    >  end\n    >  window:refresh()\n    >end\n- __teliva_timestamp:\n    >Sat Mar 12 08:57:41 2022\n  doc:blurb:\n    >A tiny editor (no scrolling) for composing a series of toots or tweets.\n    >Always shows character counts for current state of prose.\n    >\n    >Typing '===' on its own lines, surrounded by empty lines, partitions prose and gives all segments independent character counts. Good for threads (tweetstorms).\n- __teliva_timestamp:\n    >Sat Mar 12 08:59:52 2022\n  __teliva_note:\n    >hacky scrolling support\n    >\n    >Since I started out rendering a toot at a time and tracking the position\n    >as I rendered each toot, the easiest way to build this was to scroll a\n    >toot at a time, always render each toot and just decide when to stop\n    >clearing the screen. This way I don't mess with the position computation\n    >logic which is carefully synced between render and cursor_up/cursor_down.\n    >\n    >But there may be a more elegant approach if I was building the current state\n    >from scratch.\n  doc:blurb:\n    >A tiny editor for composing a short series of toots or tweets. Always shows character counts for current state of prose.\n    >\n    >Typing '===' on its own lines, surrounded by empty lines, partitions prose and gives all segments independent character counts. Good for threads (tweetstorms).\n    >\n    >Scrolling support is rudimentary. Keys to scroll are independent of cursor movement, so cursor can move off the screen and confusingly 'get lost'.\n- __teliva_timestamp:\n    >Wed Mar 30 21:33:17 2022\n  update:\n    >function update(window)\n    >  local key = window:getch()\n    >  local h, w = window:getmaxyx()\n    >  if key == curses.KEY_LEFT then\n    >    if cursor > 1 then\n    >      cursor = cursor-1\n    >    end\n    >  elseif key == curses.KEY_RIGHT then\n    >    if cursor <= #prose then\n    >      cursor = cursor+1\n    >    end\n    >  elseif key == curses.KEY_DOWN then\n    >    cursor = cursor_down(prose, cursor, w)\n    >  elseif key == curses.KEY_UP then\n    >    cursor = cursor_up(prose, cursor, w)\n    >  elseif key == curses.KEY_BACKSPACE or key == 8 or key == 127 then  -- ctrl-h, ctrl-?, delete\n    >    if cursor > 1 then\n    >      cursor = cursor-1\n    >      prose = prose:remove(cursor)\n    >    end\n    >  elseif key == 6 then  -- ctrl-f\n    >    first_toot = first_toot+1\n    >  elseif key == 2 then  -- ctrl-b\n    >    if first_toot > 1 then\n    >      first_toot = first_toot-1\n    >    end\n    >  elseif key == 23 then  -- ctrl-w\n    >    local out = io.open('toot', 'w')\n    >    if out ~= nil then\n    >      out:write(prose, '\\n')\n    >      out:close()\n    >    end\n    >  elseif key == 10 or (key >= 32 and key < 127) then\n    >    prose = prose:insert(string.char(key), cursor-1)\n    >    cursor = cursor+1\n    >  end\n    >end\n- __teliva_timestamp:\n    >Wed Mar 30 21:33:44 2022\n  __teliva_note:\n    >Get rid of the ctrl-k shortcut. Makes it too easy to lose data. To clear the page just quit and restart.\n  menu:\n    >-- To show app-specific hotkeys in the menu bar, add hotkey/command\n    >-- arrays of strings to the menu array.\n    >menu = {\n    >  {'^w', 'write to \"toot\"'},\n    >  {'^f|^b', 'scroll'},\n    >}\n- __teliva_timestamp:\n    >Thu Mar 31 08:42:19 2022\n  update:\n    >function update(window)\n    >  local key = window:getch()\n    >  local h, w = window:getmaxyx()\n    >  if key == curses.KEY_LEFT then\n    >    if cursor > 1 then\n    >      cursor = cursor-1\n    >    end\n    >  elseif key == curses.KEY_RIGHT then\n    >    if cursor <= #prose then\n    >      cursor = cursor+1\n    >    end\n    >  elseif key == curses.KEY_DOWN then\n    >    cursor = cursor_down(prose, cursor, w)\n    >  elseif key == curses.KEY_UP then\n    >    cursor = cursor_up(prose, cursor, w)\n    >  elseif key == curses.KEY_BACKSPACE or key == 8 or key == 127 then  -- ctrl-h, ctrl-?, delete\n    >    if cursor > 1 then\n    >      cursor = cursor-1\n    >      prose = prose:remove(cursor)\n    >    end\n    >  elseif key == 6 then  -- ctrl-f\n    >    first_toot = first_toot+1\n    >  elseif key == 2 then  -- ctrl-b\n    >    if first_toot > 1 then\n    >      first_toot = first_toot-1\n    >    end\n    >  elseif key == 23 then  -- ctrl-w\n    >    local out = io.open(next_toot(), 'w')\n    >    if out ~= nil then\n    >      out:write(prose, '\\n')\n    >      out:close()\n    >    end\n    >  elseif key == 10 or (key >= 32 and key < 127) then\n    >    prose = prose:insert(string.char(key), cursor-1)\n    >    cursor = cursor+1\n    >  end\n    >end\n- __teliva_timestamp:\n    >Thu Mar 31 08:44:19 2022\n  next_toot:\n    >-- pick the first filename starting with 'toot' that doesn't exist yet\n    >function next_toot()\n    >  if not file_exists('toot') then return 'toot' end\n    >  local idx = 1\n    >  while true do\n    >    local curr = 'toot'..str(idx)\n    >    if not file_exists(curr) then\n    >      return curr\n    >    end\n    >    idx = idx+1\n    >  end\n    >end\n- __teliva_timestamp:\n    >Thu Mar 31 08:46:27 2022\n  file_exists:\n    >function file_exists(filename)\n    >  local f = io.open(filename, 'r')\n    >  return f ~= nil\n    >end\n"
  },
  {
    "path": "tour.md",
    "content": "# A guided tour of Teliva\n\nTeliva is an environment for running shareable little text-mode Lua apps that\nare easy to modify. This page is an in-progress guided tour through [Teliva's\nReadme](https://github.com/akkartik/teliva#readme) and Lua's reference\ndocumentation. We'll start out really slow and gradually accelerate.\n\n_Prerequisites_\n\nYou will need the following to build Teliva:\n\n* A non-mobile computer running some sort of Unix variant. Teliva has been\n  tested on Linux, OpenBSD and Mac OS X. (Other BSD variants and Windows\n  Subsystem for Linux should require minor changes at most. Please [contact me](http://akkartik.name/contact)\n  if you run into issues running Teliva on your computer.)\n* [Git](https://git-scm.com).\n* A working C toolchain.\n* Some fluency in typing commands at the terminal and interpreting their\n  output.\n\nIf you have trouble with any of this, [I'm always nearby and available to\nanswer questions](http://akkartik.name/contact). The prerequisites are just\nthings I haven't figured out how to explain yet. In particular, I want this\npage to be accessible to people who are in the process of learning\nprogramming, but I'm sure it isn't good enough yet for that. Ask me questions\nand help me improve it.\n\n## Task 1: running a Teliva app\n\nRead the first question in [the Readme](https://github.com/akkartik/teliva/blob/main/README.md).\nTry running the commands to download and build Teliva. If you have any trouble\nat this point, don't waste _any_ time thinking about it. Just [get in\ntouch](http://akkartik.name/contact).\n\nRun the simplest example app:\n\n```sh\nsrc/teliva counter.tlv\n```\n\n<img alt='screenshot of Teliva running a counter app' src='doc/counter.png'>\n\nCan you figure out what this app does, what you can do with it? There's a\nnumber on screen. Hit `enter`. The number increments by 1. Hit `ctrl-x` to\nexit (press `ctrl` and `x` on your keyboard at the same time).\n\nRun the app again. Try editing the app by hitting `ctrl-e`. You see a \"big\npicture\" view of the app. Spend a few moments scanning this page.\n\n<img alt='editing the code for the counter app' src='doc/counter-edit.png'>\n\nAll programs consist of data and _functions_: code that operates on data in\nsome way. Teliva apps always start by running the special function `main`. The\nbig picture orders functions from the _top down_. It shows `main` up top,\nfunctions that `main` calls below, functions called by _those_ functions next,\nand so on.\n\nTry browsing to some of the names visible on screen. Don't be afraid to\nexperiment. The menu at the bottom always shows the hotkeys available to you\nat any point in time. Don't worry, everything you do can be undone.\n\n## Task 2: modifying a Teliva app\n\nThe [first section of the Lua book](https://www.lua.org/pil/1.html) starts\nwith this one-line example:\n\n```lua\nprint(\"Hello World\")\n```\n\nCan you figure out where to add this line to the app's code so it's visible\nwhen the app runs?\n\nStart by looking inside `main`. Most Teliva apps tend to share a basic\nstructure in `main`:\n- some initialization, followed by\n- a loop that repeatedly:\n  * updates the screen, and then\n  * waits for the user to press a key\n\nCan you map the lines of `main` to this structure? Which function describes\nhow the app updates the screen?\n\nSome hints:\n- The function responsible for updating the screen is `render`.\n- `render` begins by clearing the screen (`window` in Teliva).\n\nAfter you make a change, can you figure out how to run your program?\n\nDoes it do what you expect? Feel free to edit your programs as often as\nnecessary. Programming is just long sessions of repeatedly editing (`ctrl-e`)\nand running (`ctrl-e`) your program.\n\nOnce you're happy with your change, try exiting Teliva and restarting it with\nthe same app. Do you still see your changes?\n\nYou could save the file `counter.tlv` anywhere you like at this point. Or\nshare it with someone else. Everything needed for the app is in that file. If\nyou get stuck or have a question, [send it to me!](http://akkartik.name/contact)\n\n## Task 3: variables and arithmetic\n\nCan you figure out how to modify this app to increment by 2 each time you\nhit `enter`? Again, don't be afraid to experiment. The menu at the bottom\nalways shows the hotkeys available to you at any point in time. Don't worry,\neverything you do can be undone.\n\nDivide the problem into two parts in your head: where to make your change and\nwhat change to make there. You've already gotten some practice selecting a\nplace to modify in Task 2. Repeat that process. Go back to the `render` page.\nWhat is the name of the _variable_ (box containing a number) that decides what\nnumber gets printed to screen? Go back to the big picture. Where is this\nvariable defined? How does it get modified when you press `enter`?\n\nIf you're stuck, some short sections from [the Lua book](https://www.lua.org/pil/contents.html)\nmight help at this point: [getting started](https://www.lua.org/pil/1.html);\n[assignment](https://www.lua.org/pil/4.1.html); [what you can do with numbers](https://www.lua.org/pil/3.1.html).\n\n(Buy the Lua book to support the creators of Lua. Teliva is a tiny molehill on\nthe mountain of awesome that is Lua.)\n\nSome hints:\n- The function responsible for processing keystrokes is `update`.\n- The variable that tracks what number to print on screen is `n`.\n"
  },
  {
    "path": "zet.tlv",
    "content": "# .tlv file generated by https://github.com/akkartik/teliva\n# You may edit it if you are careful; however, you may see cryptic errors if you\n# violate Teliva's assumptions.\n#\n# .tlv files are representations of Teliva programs. Teliva programs consist of\n# sequences of definitions. Each definition is a table of key/value pairs. Keys\n# and values are both strings.\n#\n# Lines in .tlv files always follow exactly one of the following forms:\n# - comment lines at the top of the file starting with '#' at column 0\n# - beginnings of definitions starting with '- ' at column 0, followed by a\n#   key/value pair\n# - key/value pairs consisting of '  ' at column 0, containing either a\n#   spaceless value on the same line, or a multi-line value\n# - multiline values indented by more than 2 spaces, starting with a '>'\n#\n# If these constraints are violated, Teliva may unceremoniously crash. Please\n# report bugs at http://akkartik.name/contact\n- __teliva_timestamp: original\n  str_helpers:\n    >-- some string helpers from http://lua-users.org/wiki/StringIndexing\n    >\n    >-- index characters using []\n    >getmetatable('').__index = function(str,i)\n    >  if type(i) == 'number' then\n    >    return str:sub(i,i)\n    >  else\n    >    return string[i]\n    >  end\n    >end\n    >\n    >-- ranges using (), selected bytes using {}\n    >getmetatable('').__call = function(str,i,j)\n    >  if type(i)~='table' then\n    >    return str:sub(i,j)\n    >  else\n    >    local t={}\n    >    for k,v in ipairs(i) do\n    >      t[k]=str:sub(v,v)\n    >    end\n    >    return table.concat(t)\n    >  end\n    >end\n    >\n    >-- iterate over an ordered sequence\n    >function q(x)\n    >  if type(x) == 'string' then\n    >    return x:gmatch('.')\n    >  else\n    >    return ipairs(x)\n    >  end\n    >end\n    >\n    >-- insert within string\n    >function string.insert(str1, str2, pos)\n    >  return str1:sub(1,pos)..str2..str1:sub(pos+1)\n    >end\n    >\n    >function string.remove(s, pos)\n    >  return s:sub(1,pos-1)..s:sub(pos+1)\n    >end\n    >\n    >function string.pos(s, sub)\n    >  return string.find(s, sub, 1, true)  -- plain=true to disable regular expressions\n    >end\n    >\n    >-- TODO: backport utf-8 support from Lua 5.3\n- __teliva_timestamp: original\n  debugy:\n    >debugy = 5\n- __teliva_timestamp: original\n  dbg:\n    >-- helper for debug by print; overlay debug information towards the right\n    >-- reset debugy every time you refresh screen\n    >function dbg(window, s)\n    >  local oldy = 0\n    >  local oldx = 0\n    >  oldy, oldx = window:getyx()\n    >  window:mvaddstr(debugy, 60, s)\n    >  debugy = debugy+1\n    >  window:mvaddstr(oldy, oldx, '')\n    >end\n- __teliva_timestamp: original\n  check:\n    >function check(x, msg)\n    >  if x then\n    >    Window:addch('.')\n    >  else\n    >    print('F - '..msg)\n    >    print('  '..str(x)..' is false/nil')\n    >    teliva_num_test_failures = teliva_num_test_failures + 1\n    >    -- overlay first test failure on editors\n    >    if teliva_first_failure == nil then\n    >      teliva_first_failure = msg\n    >    end\n    >  end\n    >end\n- __teliva_timestamp: original\n  check_eq:\n    >function check_eq(x, expected, msg)\n    >  if eq(x, expected) then\n    >    Window:addch('.')\n    >  else\n    >    print('F - '..msg)\n    >    print('  expected '..str(expected)..' but got '..str(x))\n    >    teliva_num_test_failures = teliva_num_test_failures + 1\n    >    -- overlay first test failure on editors\n    >    if teliva_first_failure == nil then\n    >      teliva_first_failure = msg\n    >    end\n    >  end\n    >end\n- __teliva_timestamp: original\n  eq:\n    >function eq(a, b)\n    >  if type(a) ~= type(b) then return false end\n    >  if type(a) == 'table' then\n    >    if #a ~= #b then return false end\n    >    for k, v in pairs(a) do\n    >      if b[k] ~= v then\n    >        return false\n    >      end\n    >    end\n    >    for k, v in pairs(b) do\n    >      if a[k] ~= v then\n    >        return false\n    >      end\n    >    end\n    >    return true\n    >  end\n    >  return a == b\n    >end\n- __teliva_timestamp: original\n  str:\n    >-- smarter tostring\n    >-- slow; used only for debugging\n    >function str(x)\n    >  if type(x) == 'table' then\n    >    local result = ''\n    >    result = result..#x..'{'\n    >    for k, v in pairs(x) do\n    >      result = result..str(k)..'='..str(v)..', '\n    >    end\n    >    result = result..'}'\n    >    return result\n    >  elseif type(x) == 'string' then\n    >    return '\"'..x..'\"'\n    >  end\n    >  return tostring(x)\n    >end\n- __teliva_timestamp: original\n  find_index:\n    >function find_index(arr, x)\n    >  for n, y in ipairs(arr) do\n    >    if x == y then\n    >      return n\n    >    end\n    >  end\n    >end\n- __teliva_timestamp: original\n  trim:\n    >function trim(s)\n    >  return s:gsub('^%s*', ''):gsub('%s*$', '')\n    >end\n- __teliva_timestamp: original\n  split:\n    >function split(s, d)\n    >  result = {}\n    >  for match in (s..d):gmatch(\"(.-)\"..d) do\n    >    table.insert(result, match);\n    >  end\n    >  return result\n    >end\n- __teliva_timestamp: original\n  map:\n    >-- only for arrays\n    >function map(l, f)\n    >  result = {}\n    >  for _, x in ipairs(l) do\n    >    table.insert(result, f(x))\n    >  end\n    >  return result\n    >end\n- __teliva_timestamp: original\n  reduce:\n    >-- only for arrays\n    >function reduce(l, f, init)\n    >  result = init\n    >  for _, x in ipairs(l) do\n    >    result = f(result, x)\n    >  end\n    >  return result\n    >end\n- __teliva_timestamp: original\n  filter:\n    >function filter(h, f)\n    >  result = {}\n    >  for k, v in pairs(h) do\n    >    if f(k, v) then\n    >      result[k] = v\n    >    end\n    >  end\n    >  return result\n    >end\n- __teliva_timestamp: original\n  ifilter:\n    >-- only for arrays\n    >function ifilter(l, f)\n    >  result = {}\n    >  for _, x in ipairs(l) do\n    >    if f(x) then\n    >      table.insert(result, x)\n    >    end\n    >  end\n    >  return result\n    >end\n- __teliva_timestamp: original\n  sort_letters:\n    >function sort_letters(s)\n    >  tmp = {}\n    >  for i=1,#s do\n    >    table.insert(tmp, s[i])\n    >  end\n    >  table.sort(tmp)\n    >  local result = ''\n    >  for _, c in pairs(tmp) do\n    >    result = result..c\n    >  end\n    >  return result\n    >end\n    >\n    >function test_sort_letters(s)\n    >  check_eq(sort_letters(''), '', 'test_sort_letters: empty')\n    >  check_eq(sort_letters('ba'), 'ab', 'test_sort_letters: non-empty')\n    >  check_eq(sort_letters('abba'), 'aabb', 'test_sort_letters: duplicates')\n    >end\n- __teliva_timestamp: original\n  count_letters:\n    >-- TODO: handle unicode\n    >function count_letters(s)\n    >  local result = {}\n    >  for i=1,s:len() do\n    >    local c = s[i]\n    >    if result[c] == nil then\n    >      result[c] = 1\n    >    else\n    >      result[c] = result[c] + 1\n    >    end\n    >  end\n    >  return result\n    >end\n- __teliva_timestamp: original\n  count:\n    >-- turn an array of elements into a map from elements to their frequency\n    >-- analogous to count_letters for non-strings\n    >function count(a)\n    >  local result = {}\n    >  for i, v in ipairs(a) do\n    >    if result[v] == nil then\n    >      result[v] = 1\n    >    else\n    >      result[v] = result[v] + 1\n    >    end\n    >  end\n    >  return result\n    >end\n- __teliva_timestamp: original\n  union:\n    >function union(a, b)\n    >  for k, v in pairs(b) do\n    >    a[k] = v\n    >  end\n    >  return a\n    >end\n- __teliva_timestamp: original\n  subtract:\n    >-- set subtraction\n    >function subtract(a, b)\n    >  for k, v in pairs(b) do\n    >    a[k] = nil\n    >  end\n    >  return a\n    >end\n- __teliva_timestamp: original\n  all:\n    >-- universal quantifier on sets\n    >function all(s, f)\n    >  for k, v in pairs(s) do\n    >    if not f(k, v) then\n    >      return false\n    >    end\n    >  end\n    >  return true\n    >end\n- __teliva_timestamp: original\n  to_array:\n    >-- turn a set into an array\n    >-- drops values\n    >function to_array(h)\n    >  local result = {}\n    >  for k, _ in pairs(h) do\n    >    table.insert(result, k)\n    >  end\n    >  return result\n    >end\n- __teliva_timestamp: original\n  append:\n    >-- concatenate list 'elems' into 'l', modifying 'l' in the process\n    >function append(l, elems)\n    >  for i=1,#elems do\n    >    table.insert(l, elems[i])\n    >  end\n    >end\n- __teliva_timestamp: original\n  prepend:\n    >-- concatenate list 'elems' into the start of 'l', modifying 'l' in the process\n    >function prepend(l, elems)\n    >  for i=1,#elems do\n    >    table.insert(l, i, elems[i])\n    >  end\n    >end\n- __teliva_timestamp: original\n  all_but:\n    >function all_but(x, idx)\n    >  if type(x) == 'table' then\n    >    local result = {}\n    >    for i, elem in ipairs(x) do\n    >      if i ~= idx then\n    >        table.insert(result,elem)\n    >      end\n    >    end\n    >    return result\n    >  elseif type(x) == 'string' then\n    >    if idx < 1 then return x:sub(1) end\n    >    return x:sub(1, idx-1) .. x:sub(idx+1)\n    >  else\n    >    error('all_but: unsupported type '..type(x))\n    >  end\n    >end\n    >\n    >function test_all_but()\n    >  check_eq(all_but('', 0), '', 'all_but: empty')\n    >  check_eq(all_but('abc', 0), 'abc', 'all_but: invalid low index')\n    >  check_eq(all_but('abc', 4), 'abc', 'all_but: invalid high index')\n    >  check_eq(all_but('abc', 1), 'bc', 'all_but: first index')\n    >  check_eq(all_but('abc', 3), 'ab', 'all_but: final index')\n    >  check_eq(all_but('abc', 2), 'ac', 'all_but: middle index')\n    >end\n- __teliva_timestamp: original\n  set:\n    >function set(l)\n    >  local result = {}\n    >  for i, elem in ipairs(l) do\n    >    result[elem] = true\n    >  end\n    >  return result\n    >end\n- __teliva_timestamp: original\n  set_eq:\n    >function set_eq(l1, l2)\n    >  return eq(set(l1), set(l2))\n    >end\n    >\n    >function test_set_eq()\n    >  check(set_eq({1}, {1}), 'set_eq: identical')\n    >  check(not set_eq({1, 2}, {1, 3}), 'set_eq: different')\n    >  check(set_eq({1, 2}, {2, 1}), 'set_eq: order')\n    >  check(set_eq({1, 2, 2}, {2, 1}), 'set_eq: duplicates')\n    >end\n- __teliva_timestamp: original\n  clear:\n    >function clear(lines)\n    >  while #lines > 0 do\n    >    table.remove(lines)\n    >  end\n    >end\n- __teliva_timestamp: original\n  zap:\n    >function zap(target, src)\n    >  clear(target)\n    >  append(target, src)\n    >end\n- __teliva_timestamp: original\n  mfactorial:\n    >-- memoized version of factorial\n    >-- doesn't memoize recursive calls, but may be good enough\n    >mfactorial = memo1(factorial)\n- __teliva_timestamp: original\n  factorial:\n    >function factorial(n)\n    >  local result = 1\n    >  for i=1,n do\n    >    result = result*i\n    >  end\n    >  return result\n    >end\n- __teliva_timestamp: original\n  memo1:\n    >-- a higher-order function that takes a function of a single arg\n    >-- (that never returns nil)\n    >-- and returns a memoized version of it\n    >function memo1(f)\n    >  local memo = {}\n    >  return function(x)\n    >    if memo[x] == nil then\n    >      memo[x] = f(x)\n    >    end\n    >    return memo[x]\n    >  end\n    >end\n    >\n    >-- mfactorial doesn't seem noticeably faster\n    >function test_memo1()\n    >  for i=0,30 do\n    >    check_eq(mfactorial(i), factorial(i), 'memo1 over factorial: '..str(i))\n    >  end\n    >end\n- __teliva_timestamp: original\n  num_permutations:\n    >-- number of permutations of n distinct objects, taken r at a time\n    >function num_permutations(n, r)\n    >  return factorial(n)/factorial(n-r)\n    >end\n    >\n    >-- mfactorial doesn't seem noticeably faster\n    >function test_memo1()\n    >  for i=0,30 do\n    >    for j=0,i do\n    >      check_eq(num_permutations(i, j), mfactorial(i)/mfactorial(i-j), 'num_permutations memoizes: '..str(i)..'P'..str(j))\n    >    end\n    >  end\n    >end\n- __teliva_timestamp: original\n  menu:\n    >-- To show app-specific hotkeys in the menu bar, add hotkey/command\n    >-- arrays of strings to the menu array.\n    >menu = {\n    >  {'^e', 'edit'},\n    >}\n- __teliva_timestamp: original\n  Window:\n    >Window = curses.stdscr()\n- __teliva_timestamp: original\n  window:\n    >-- constructor for fake screen and window\n    >-- call it like this:\n    >--   local w = window{\n    >--     kbd=kbd('abc'),\n    >--     scr=scr{h=5, w=4},\n    >--   }\n    >-- eventually it'll do everything a real ncurses window can\n    >function window(h)\n    >  h.__index = h\n    >  setmetatable(h, h)\n    >  h.__index = function(table, key)\n    >    return rawget(h, key)\n    >  end\n    >  h.attrset = function(self, x)\n    >    self.scr.attrs = x\n    >  end\n    >  h.attron = function(self, x)\n    >    -- currently same as attrset since Lua 5.1 doesn't have bitwise operators\n    >    -- doesn't support multiple attrs at once\n    >--    local old = self.scr.attrs\n    >--    self.scr.attrs = old|x\n    >    self.scr.attrs = x\n    >  end\n    >  h.attroff = function(self, x)\n    >    -- currently borked since Lua 5.1 doesn't have bitwise operators\n    >    -- doesn't support multiple attrs at once\n    >--    local old = self.scr.attrs\n    >--    self.scr.attrs = old & (~x)\n    >    self.scr.attrs = curses.A_NORMAL\n    >  end\n    >  h.getch = function(self)\n    >    local c = table.remove(h.kbd, 1)\n    >    if c == nil then return c end\n    >    return string.byte(c)  -- for verisimilitude with ncurses\n    >  end\n    >  h.addch = function(self, c)\n    >    local scr = self.scr\n    >    if c == '\\n' then\n    >      scr.cursy = scr.cursy+1\n    >      scr.cursx = 0\n    >      return\n    >    end\n    >    if scr.cursy <= scr.h then\n    >      scr[scr.cursy][scr.cursx] = {data=c, attrs=scr.attrs}\n    >      scr.cursx = scr.cursx+1\n    >      if scr.cursx > scr.w then\n    >        scr.cursy = scr.cursy+1\n    >        scr.cursx = 1\n    >      end\n    >    end\n    >  end\n    >  h.addstr = function(self, s)\n    >    for i=1,s:len() do\n    >      self:addch(s[i])\n    >    end\n    >  end\n    >  h.mvaddch = function(self, y, x, c)\n    >    self.scr.cursy = y\n    >    self.scr.cursx = x\n    >    self:addch(c)\n    >  end\n    >  h.mvaddstr = function(self, y, x, s)\n    >    self.scr.cursy = y\n    >    self.scr.cursx = x\n    >    self:addstr(s)\n    >  end\n    >  h.clear = function(self)\n    >    clear_scr(self.scr)\n    >  end\n    >  h.refresh = function(self)\n    >    -- nothing\n    >  end\n    >  return h\n    >end\n- __teliva_timestamp: original\n  kbd:\n    >function kbd(keys)\n    >  local result = {}\n    >  for i=1,keys:len() do\n    >    table.insert(result, keys[i])\n    >  end\n    >  return result\n    >end\n- __teliva_timestamp: original\n  scr:\n    >function scr(props)\n    >  props.cursx = 1\n    >  props.cursy = 1\n    >  clear_scr(props)\n    >  return props\n    >end\n- __teliva_timestamp: original\n  clear_scr:\n    >function clear_scr(props)\n    >  props.cursy = 1\n    >  props.cursx = 1\n    >  for y=1,props.h do\n    >    props[y] = {}\n    >    for x=1,props.w do\n    >      props[y][x] = {data=' ', attrs=curses.A_NORMAL}\n    >    end\n    >  end\n    >  return props\n    >end\n- __teliva_timestamp: original\n  check_screen:\n    >function check_screen(window, contents, message)\n    >  local x, y = 1, 1\n    >  for i=1,contents:len() do\n    >    check_eq(window.scr[y][x].data, contents[i], message..'/'..y..','..x)\n    >    x = x+1\n    >    if x > window.scr.w then\n    >      y = y+1\n    >      x = 1\n    >    end\n    >  end\n    >end\n    >\n    >-- putting it all together, an example test of both keyboard and screen\n    >function test_check_screen()\n    >  local lines = {\n    >    c='123',\n    >    d='234',\n    >    a='345',\n    >    b='456',\n    >  }\n    >  local w = window{\n    >    kbd=kbd('abc'),\n    >    scr=scr{h=3, w=5},\n    >  }\n    >  local y = 1\n    >  while true do\n    >    local b = w:getch()\n    >    if b == nil then break end\n    >    w:mvaddstr(y, 1, lines[string.char(b)])\n    >    y = y+1\n    >  end\n    >  check_screen(w, '345  '..\n    >                  '456  '..\n    >                  '123  ',\n    >              'test_check_screen')\n    >end\n- __teliva_timestamp: original\n  check_reverse:\n    >function check_reverse(window, contents, message)\n    >  local x, y = 1, 1\n    >  for i=1,contents:len() do\n    >    if contents[i] ~= ' ' then\n    >      -- hacky version while we're without bitwise operators on Lua 5.1\n    >--      check(window.scr[y][x].attrs & curses.A_REVERSE, message..'/'..y..','..x)\n    >      check_eq(window.scr[y][x].attrs, curses.A_REVERSE, message..'/'..y..','..x)\n    >    else\n    >      -- hacky version while we're without bitwise operators on Lua 5.1\n    >--      check(window.scr[y][x].attrs & (~curses.A_REVERSE), message..'/'..y..','..x)\n    >      check(window.scr[y][x].attrs ~= curses.A_REVERSE, message..'/'..y..','..x)\n    >    end\n    >    x = x+1\n    >    if x > window.scr.w then\n    >      y = y+1\n    >      x = 1\n    >    end\n    >  end\n    >end\n- __teliva_timestamp: original\n  check_bold:\n    >function check_bold(window, contents, message)\n    >  local x, y = 1, 1\n    >  for i=1,contents:len() do\n    >    if contents[i] ~= ' ' then\n    >      -- hacky version while we're without bitwise operators on Lua 5.1\n    >--      check(window.scr[y][x].attrs & curses.A_BOLD, message..'/'..y..','..x)\n    >      check_eq(window.scr[y][x].attrs, curses.A_BOLD, message..'/'..y..','..x)\n    >    else\n    >      -- hacky version while we're without bitwise operators on Lua 5.1\n    >--      check(window.scr[y][x].attrs & (~curses.A_BOLD), message..'/'..y..','..x)\n    >      check(window.scr[y][x].attrs ~= curses.A_BOLD, message..'/'..y..','..x)\n    >    end\n    >    x = x+1\n    >    if x > window.scr.w then\n    >      y = y+1\n    >      x = 1\n    >    end\n    >  end\n    >end\n- __teliva_timestamp: original\n  check_color:\n    >-- check which parts of a screen have the given color_pair\n    >function check_color(window, cp, contents, message)\n    >  local x, y = 1, 1\n    >  for i=1,contents:len() do\n    >    if contents[i] ~= ' ' then\n    >      -- hacky version while we're without bitwise operators on Lua 5.1\n    >--      check(window.scr[y][x].attrs & curses.color_pair(cp), message..'/'..y..','..x)\n    >      check_eq(window.scr[y][x].attrs, curses.color_pair(cp), message..'/'..y..','..x)\n    >    else\n    >      -- hacky version while we're without bitwise operators on Lua 5.1\n    >--      check(window.scr[y][x].attrs & (~curses.A_BOLD), message..'/'..y..','..x)\n    >      check(window.scr[y][x].attrs ~= curses.color_pair(cp), message..'/'..y..','..x)\n    >    end\n    >    x = x+1\n    >    if x > window.scr.w then\n    >      y = y+1\n    >      x = 1\n    >    end\n    >  end\n    >end\n- __teliva_timestamp: original\n  spaces:\n    >function spaces(n)\n    >  for i=1,n do\n    >    Window:addch(' ')\n    >  end\n    >end\n- __teliva_timestamp: original\n  init_colors:\n    >function init_colors()\n    >  -- light background\n    >  curses.init_pair(view_settings.current_zettel_bg, 236, 230)\n    >  curses.init_pair(1, 236, 250)\n    >  curses.init_pair(2, 236, 252)\n    >  -- dark background\n    >--?   curses.init_pair(view_settings.current_zettel_bg, 252, 130)\n    >--?   curses.init_pair(1, 252, 240)\n    >--?   curses.init_pair(2, 252, 242)\n    >end\n- __teliva_timestamp: original\n  main:\n    >function main()\n    >  init_colors()\n    >  current_zettel_id = zettels.root\n    >\n    >  while true do\n    >    render(Window)\n    >    update(Window)\n    >  end\n    >end\n- __teliva_timestamp: original\n  depth:\n    >function depth(zettel)\n    >  local result = 0\n    >  while zettel.parent do\n    >    result = result+1\n    >    zettel = zettel.parent\n    >  end\n    >  return result\n    >end\n- __teliva_timestamp: original\n  render_zettel:\n    >function render_zettel(window, bg, indent, starty, startx, zettel)\n    >  window:attrset(curses.color_pair(bg))\n    >  for y=0,view_settings.height-1 do\n    >    for x=0,view_settings.width-1 do\n    >      window:mvaddch(y+starty, x+startx, ' ')\n    >    end\n    >  end\n    >  local y, x = 0, indent+1\n    >  for i=1,#zettel.data do\n    >    local c = zettel.data[i]\n    >    if c == '\\n' then\n    >      y = y+1\n    >      x = indent+1\n    >    else\n    >      window:mvaddstr(y+starty, x+startx, c)\n    >      x = x+1\n    >      if x >= startx + view_settings.width then\n    >        y = y+1\n    >        x = indent+1\n    >      end\n    >    end\n    >    if y >= view_settings.height then\n    >      break\n    >    end\n    >  end\n    >end\n- __teliva_timestamp: original\n  current_zettel_id:\n    >current_zettel_id = ''\n- __teliva_timestamp: original\n  view_settings:\n    >view_settings = {\n    >  -- dimensions for rendering a single zettel; extra text gets truncated\n    >  width=50,\n    >  height=3,\n    >  -- spacing between zettels\n    >  hmargin=1,\n    >  vmargin=1,\n    >  --\n    >  indent=2,  -- how children of a zettel are indicated\n    >  current_zettel_bg=3,  -- color pair index initialized in init_colors\n    >}\n- __teliva_timestamp: original\n  zettels:\n    >zettels = {\n    >  root=\"a\",\n    >  a={\n    >    data=\"abc\\ndef\",\n    >    child=\"c\",\n    >    next=\"b\",\n    >  },\n    >  b={\n    >    data=\"ghi\\njklm\",\n    >    prev=\"a\",\n    >  },\n    >  c={\n    >    data=\"c\",\n    >    parent=\"a\",\n    >    next=\"d\",\n    >  },\n    >  d={\n    >    data=\"d\",\n    >    parent=\"a\",\n    >    prev=\"c\",\n    >  }\n    >}\n- __teliva_timestamp: original\n  render_state:\n    >-- some information about what's been drawn on screen\n    >render_state = {\n    >  -- where the current zettel is, in units of zettels\n    >  curr_h = 1,\n    >  curr_w = 1,\n    >  -- what zettel is at each position on screen, in units of zettels\n    >  hw2id = {},\n    >}\n- __teliva_timestamp: original\n  update:\n    >function update(window)\n    >  local key = window:getch()\n    >  local curr = zettels[current_zettel_id]\n    >  -- graph-based navigation\n    >  if key == string.byte('j') then\n    >    if curr.child then\n    >      current_zettel_id = curr.child\n    >    elseif curr.next then\n    >      current_zettel_id = curr.next\n    >    elseif curr.parent and zettels[curr.parent].next then\n    >      current_zettel_id = zettels[curr.parent].next\n    >    end\n    >  elseif key == string.byte('k') then\n    >    if curr.parent then current_zettel_id = curr.parent end\n    >  elseif key == string.byte('h') then\n    >    if curr.prev then\n    >      current_zettel_id = curr.prev\n    >    elseif curr.parent then\n    >      current_zettel_id = curr.parent\n    >    end\n    >  elseif key == string.byte('l') then\n    >    if curr.next then\n    >      current_zettel_id = curr.next\n    >    elseif curr.parent and zettels[curr.parent].next then\n    >      current_zettel_id = zettels[curr.parent].next\n    >    end\n    >  -- screen-based navigation\n    >  elseif key == curses.KEY_UP then\n    >    if render_state.curr_h > 1 then\n    >      current_zettel_id = render_state.wh2id[render_state.curr_w][render_state.curr_h - 1]\n    >    end\n    >  elseif key == curses.KEY_DOWN then\n    >    if render_state.wh2id[render_state.curr_w][render_state.curr_h + 1] then\n    >      current_zettel_id = render_state.wh2id[render_state.curr_w][render_state.curr_h + 1]\n    >    end\n    >  elseif key == curses.KEY_LEFT then\n    >    if render_state.curr_w > 1 then\n    >      current_zettel_id = render_state.wh2id[render_state.curr_w - 1][render_state.curr_h]\n    >    end\n    >  elseif key == curses.KEY_RIGHT then\n    >    if render_state.wh2id[render_state.curr_w + 1] and render_state.wh2id[render_state.curr_w + 1][render_state.curr_h] then\n    >      current_zettel_id = render_state.wh2id[render_state.curr_w + 1][render_state.curr_h]\n    >    end\n    >  --\n    >  elseif key == 5 then  -- ctrl-e\n    >    editz(window)\n    >  end\n    >end\n- __teliva_timestamp: original\n  render:\n    >function render(window)\n    >  window:clear()\n    >  local lines, cols = window:getmaxyx()\n    >  local bg=1\n    >  local y, x = 0, 0 -- units of characters (0-based)\n    >  local w, h = 1, 1 -- units of zettels (1-based)\n    >  -- render zettels depth-first, while tracking relative positions\n    >  local done = {}\n    >  local inprogress = {zettels.root}\n    >  render_state.wh2id = {{}}\n    >  while #inprogress > 0 do\n    >    local currid = table.remove(inprogress)\n    >    if not done[currid] then\n    >      done[currid] = true\n    >      table.insert(render_state.wh2id[w], currid)\n    >      local zettel = zettels[currid]\n    >      if currid == current_zettel_id then\n    >        render_state.curr_w = w\n    >        render_state.curr_h = h\n    >      end\n    >      local currbg = (currid == current_zettel_id) and view_settings.current_zettel_bg or bg\n    >      render_zettel(window, currbg, depth(zettel) * view_settings.indent, y, x, zettel)\n    >      if zettel.next then table.insert(inprogress, zettel.next) end\n    >      if zettel.child then table.insert(inprogress, zettel.child) end\n    >      bg = 3 - bg  -- toggle between color pairs 1 and 2\n    >      y = y + view_settings.height + view_settings.vmargin\n    >      h = h + 1\n    >      if y + view_settings.height > lines then\n    >        y = 0\n    >        h = 1\n    >        x = x + view_settings.width + view_settings.hmargin\n    >        w = w + 1\n    >        if x + view_settings.width > cols then break end\n    >        table.insert(render_state.wh2id, {})\n    >      end\n    >    end\n    >  end\n    >  window:mvaddstr(lines-2, 0, '')\n    >  for i=1,3 do\n    >    window:attrset(curses.color_pair(i%2+1))\n    >    window:addstr('')\n    >    spaces(view_settings.width-string.len(''))\n    >    window:attrset(curses.color_pair(0))\n    >    window:addstr(' ')  -- margin\n    >  end\n    >  window:mvaddstr(lines-1, 0, '? ')\n    >  window:refresh()\n    >end\n- __teliva_timestamp: original\n  view_settings:\n    >view_settings = {\n    >  -- dimensions for rendering a single zettel; extra text gets truncated\n    >  width=50,\n    >  height=3,\n    >  -- spacing between zettels\n    >  hmargin=1,\n    >  vmargin=1,\n    >  --\n    >  indent=2,  -- how children of a zettel are indicated\n    >  current_zettel_bg=3,  -- color pair index initialized in init_colors\n    >}\n- __teliva_timestamp: original\n  editz:\n    >function editz(window)\n    >  menu = { {'^e', 'back to browsing'},}\n    >  local top = (render_state.curr_h - 1) * (view_settings.height + view_settings.vmargin)\n    >  local bottom = top + view_settings.height\n    >  local left = (render_state.curr_w - 1) * (view_settings.width + view_settings.hmargin)\n    >  local right = left + view_settings.width\n    >  local cursor = 1\n    >  curses.curs_set(0)\n    >  local quit = false\n    >  while not quit do\n    >    editz_render(window, zettels[current_zettel_id].data, cursor, top, bottom, left, right)\n    >    quit, zettels[current_zettel_id].data, cursor = editz_update(window, zettels[current_zettel_id].data, cursor)\n    >  end\n    >  curses.curs_set(1)\n    >end\n- __teliva_timestamp: original\n  editz_render:\n    >function editz_render(window, s, cursor, top, minbottom, left, right)\n    >  local h, w = window:getmaxyx()\n    >  window:attrset(curses.color_pair(view_settings.current_zettel_bg))\n    >  for y=top,minbottom-1 do\n    >    for x=left,right-1 do\n    >      window:mvaddch(y, x, ' ')\n    >    end\n    >  end\n    >  local y, x = top, left + 1  -- left padding; TODO: indent\n    >  window:mvaddstr(y, x, '')\n    >  for i=1,s:len() do\n    >    -- render character\n    >    if i == cursor then\n    >      if s[i] == '\\n' then\n    >        -- newline at cursor = render extra space in reverse video before jumping to new line\n    >        window:attron(curses.A_REVERSE)\n    >        window:addch(' ')\n    >        window:attroff(curses.A_REVERSE)\n    >      else\n    >        -- most characters at cursor = render in reverse video\n    >        window:attron(curses.A_REVERSE)\n    >        window:addstr(s[i])\n    >        window:attroff(curses.A_REVERSE)\n    >      end\n    >    else\n    >      if s[i] ~= '\\n' then\n    >        window:addstr(s[i])\n    >      end\n    >    end\n    >    -- update cursor position\n    >    if s[i] == '\\n' then\n    >      if i == cursor then x = x + 1; end\n    >      for col=x,right-1 do window:addch(' '); end\n    >      x = left\n    >      y = y + 1\n    >      if y >= h-2 then return end\n    >      window:mvaddstr(y, x, '')\n    >      for col=x,right-1 do window:addch(' '); end\n    >      x = left + 1  -- left padding; TODO: indent\n    >      window:mvaddstr(y, x, '')\n    >    else\n    >      x = x + 1\n    >      if x >= right then\n    >        y = y + 1\n    >        if y >= h-2 then return end\n    >        x = left + 1  -- left padding; TODO: indent\n    >        window:mvaddstr(y, x, '')\n    >      end\n    >    end\n    >  end\n    >  if cursor > s:len() then\n    >    window:attron(curses.A_REVERSE)\n    >    window:addch(' ')\n    >    window:attroff(curses.A_REVERSE)\n    >  else\n    >    window:addch(' ')\n    >  end\n    >end\n- __teliva_timestamp: original\n  editz_update:\n    >function editz_update(window, prose, cursor)\n    >  local key = window:getch()\n    >  local h, w = window:getmaxyx()\n    >  if key == curses.KEY_LEFT then\n    >    if cursor > 1 then\n    >      cursor = cursor-1\n    >    end\n    >  elseif key == curses.KEY_RIGHT then\n    >    if cursor <= #prose then\n    >      cursor = cursor+1\n    >    end\n    >  elseif key == curses.KEY_DOWN then\n    >    cursor = cursor_down(prose, cursor, w)\n    >  elseif key == curses.KEY_UP then\n    >    cursor = cursor_up(prose, cursor, w)\n    >  elseif key == curses.KEY_BACKSPACE or key == 8 or key == 127 then  -- ctrl-h, ctrl-?, delete\n    >    if cursor > 1 then\n    >      cursor = cursor-1\n    >      prose = prose:remove(cursor)\n    >    end\n    >  elseif key == 5 then  -- ctrl-e\n    >    return true, prose, cursor\n    >  elseif key == 10 or (key >= 32 and key < 127) then\n    >    prose = prose:insert(string.char(key), cursor-1)\n    >    cursor = cursor+1\n    >  end\n    >  return false, prose, cursor\n    >end\n- __teliva_timestamp: original\n  cursor_down:\n    >function cursor_down(s, old_idx, width)\n    >  local max = s:len()\n    >  local i = 1\n    >  -- compute oldcol, the screen column of old_idx\n    >  local oldcol = 0\n    >  local col = 0\n    >  while true do\n    >    if i > max then\n    >      -- abnormal old_idx\n    >      return old_idx\n    >    end\n    >    if i == old_idx then\n    >      oldcol = col\n    >      break\n    >    end\n    >    if s[i] == '\\n' then\n    >      col = 0\n    >    else\n    >      col = col+1\n    >    end\n    >    i = i+1\n    >  end\n    >  -- skip rest of line\n    >  while true do\n    >    if i > max then\n    >      -- current line is at bottom\n    >      if col >= width then\n    >        return i\n    >      end\n    >      return old_idx\n    >    end\n    >    if s[i] == '\\n' then\n    >      break\n    >    end\n    >    if i - old_idx >= width then\n    >      return i\n    >    end\n    >    col = col+1\n    >    i = i+1\n    >  end\n    >  -- compute index at same column on next line\n    >  -- i is at a newline\n    >  i = i+1\n    >  col = 0\n    >  while true do\n    >    if i > max then\n    >      -- next line is at bottom and is too short; position at end of it\n    >      return i\n    >    end\n    >    if s[i] == '\\n' then\n    >      -- next line is too short; position at end of it\n    >      return i\n    >    end\n    >    if col == oldcol then\n    >      return i\n    >    end\n    >    col = col+1\n    >    i = i+1\n    >  end\n    >end\n    >\n    >function test_cursor_down()\n    >  -- lines that don't wrap\n    >  check_eq(cursor_down('abc\\ndef', 1, 5), 5, 'cursor_down: non-bottom line first char')\n    >  check_eq(cursor_down('abc\\ndef', 2, 5), 6, 'cursor_down: non-bottom line mid char')\n    >  check_eq(cursor_down('abc\\ndef', 3, 5), 7, 'cursor_down: non-bottom line final char')\n    >  check_eq(cursor_down('abc\\ndef', 4, 5), 8, 'cursor_down: non-bottom line end')\n    >  check_eq(cursor_down('abc\\ndef', 5, 5), 5, 'cursor_down: bottom line first char')\n    >  check_eq(cursor_down('abc\\ndef', 6, 5), 6, 'cursor_down: bottom line mid char')\n    >  check_eq(cursor_down('abc\\ndef', 7, 5), 7, 'cursor_down: bottom line final char')\n    >  check_eq(cursor_down('abc\\n\\ndef', 2, 5), 5, 'cursor_down: to shorter line')\n    >\n    >  -- within a single wrapping line\n    >  --   |abcde|  <-- wrap, no newline\n    >  --   |fgh  |\n    >  check_eq(cursor_down('abcdefgh', 1, 5), 6, 'cursor_down from wrapping line: first char')\n    >  check_eq(cursor_down('abcdefgh', 2, 5), 7, 'cursor_down from wrapping line: mid char')\n    >  check_eq(cursor_down('abcdefgh', 5, 5), 9, 'cursor_down from wrapping line: to shorter line')\n    >\n    >  -- within a single very long wrapping line\n    >  --   |abcde|  <-- wrap, no newline\n    >  --   |fghij|  <-- wrap, no newline\n    >  --   |klm  |\n    >  check_eq(cursor_down('abcdefghijklm', 1, 5), 6, 'cursor_down within wrapping line: first char')\n    >  check_eq(cursor_down('abcdefghijklm', 2, 5), 7, 'cursor_down within wrapping line: mid char')\n    >  check_eq(cursor_down('abcdefghijklm', 5, 5), 10, 'cursor_down within wrapping line: final char')\n    >end\n- __teliva_timestamp: original\n  __teliva_note:\n    >initial commit: show/edit zettels\n  cursor_up:\n    >function cursor_up(s, old_idx, width)\n    >  local max = s:len()\n    >  local i = 1\n    >  -- compute oldcol, the screen column of old_idx\n    >  local oldcol = 0\n    >  local col = 0\n    >  local newline_before_current_line = 0\n    >  while true do\n    >    if i > max or i == old_idx then\n    >      oldcol = col\n    >      break\n    >    end\n    >    if s[i] == '\\n' then\n    >      col = 0\n    >      newline_before_current_line = i\n    >    else\n    >      col = col+1\n    >      if col == width then\n    >        col = 0\n    >      end\n    >    end\n    >    i = i+1\n    >  end\n    >  -- find previous newline\n    >  i = i-col-1\n    >  if old_idx - newline_before_current_line > width then\n    >    -- we're in a wrapped line\n    >    return old_idx - width\n    >  end\n    >  -- scan back to start of previous line\n    >  if s[i] == '\\n' then\n    >    i = i-1\n    >  end\n    >  while true do\n    >    if i < 1 then\n    >      -- current line is at top\n    >      break\n    >    end\n    >    if s[i] == '\\n' then\n    >      break\n    >    end\n    >    i = i-1\n    >  end\n    >  -- i is at a newline\n    >  i = i+1\n    >  -- skip whole screen lines within previous line\n    >  while newline_before_current_line - i > width do\n    >    i = i + width\n    >  end\n    >  -- compute index at same column on previous screen line\n    >  col = 0\n    >  while true do\n    >    if i > max then\n    >      -- next line is at bottom and is too short; position at end of it\n    >      return i\n    >    end\n    >    if s[i] == '\\n' then\n    >      -- next line is too short; position at end of it\n    >      return i\n    >    end\n    >    if col == oldcol then\n    >      return i\n    >    end\n    >    col = col+1\n    >    i = i+1\n    >  end\n    >end\n    >\n    >function test_cursor_up()\n    >  -- lines that don't wrap\n    >  check_eq(cursor_up('abc\\ndef', 1, 5), 1, 'cursor_up: top line first char')\n    >  check_eq(cursor_up('abc\\ndef', 2, 5), 2, 'cursor_up: top line mid char')\n    >  check_eq(cursor_up('abc\\ndef', 3, 5), 3, 'cursor_up: top line final char')\n    >  check_eq(cursor_up('abc\\ndef', 4, 5), 4, 'cursor_up: top line end')\n    >  check_eq(cursor_up('abc\\ndef', 5, 5), 1, 'cursor_up: non-top line first char')\n    >  check_eq(cursor_up('abc\\ndef', 6, 5), 2, 'cursor_up: non-top line mid char')\n    >  check_eq(cursor_up('abc\\ndef', 7, 5), 3, 'cursor_up: non-top line final char')\n    >  check_eq(cursor_up('abc\\ndef\\n', 8, 5), 4, 'cursor_up: non-top line end')\n    >  check_eq(cursor_up('ab\\ndef\\n', 7, 5), 3, 'cursor_up: to shorter line')\n    >\n    >  -- within a single wrapping line\n    >  --   |abcde|  <-- wrap, no newline\n    >  --   |fgh  |\n    >  check_eq(cursor_up('abcdefgh', 6, 5), 1, 'cursor_up from wrapping line: first char')\n    >  check_eq(cursor_up('abcdefgh', 7, 5), 2, 'cursor_up from wrapping line: mid char')\n    >  check_eq(cursor_up('abcdefgh', 8, 5), 3, 'cursor_up from wrapping line: final char')\n    >  check_eq(cursor_up('abcdefgh', 9, 5), 4, 'cursor_up from wrapping line: wrapped line end')\n    >\n    >  -- within a single very long wrapping line\n    >  --   |abcde|  <-- wrap, no newline\n    >  --   |fghij|  <-- wrap, no newline\n    >  --   |klm  |\n    >  check_eq(cursor_up('abcdefghijklm', 11, 5), 6, 'cursor_up within wrapping line: first char')\n    >  check_eq(cursor_up('abcdefghijklm', 12, 5), 7, 'cursor_up within wrapping line: mid char')\n    >  check_eq(cursor_up('abcdefghijklm', 13, 5), 8, 'cursor_up within wrapping line: final char')\n    >  check_eq(cursor_up('abcdefghijklm', 14, 5), 9, 'cursor_up within wrapping line: wrapped line end')\n    >\n    >  -- from below to (the bottom of) a wrapping line\n    >  --   |abcde|  <-- wrap, no newline\n    >  --   |fg   |\n    >  --   |hij  |\n    >  check_eq(cursor_up('abcdefg\\nhij', 9, 5), 6, 'cursor_up to wrapping line: first char')\n    >  check_eq(cursor_up('abcdefg\\nhij', 10, 5), 7, 'cursor_up to wrapping line: mid char')\n    >  check_eq(cursor_up('abcdefg\\nhij', 11, 5), 8, 'cursor_up to wrapping line: final char')\n    >  check_eq(cursor_up('abcdefg\\nhij', 12, 5), 8, 'cursor_up to wrapping line: to shorter line')\n    >end\n- __teliva_timestamp:\n    >Wed Feb  9 08:15:25 2022\n  render:\n    >function render(window)\n    >  window:clear()\n    >  local lines, cols = window:getmaxyx()\n    >  local bg=1\n    >  local y, x = 0, 0 -- units of characters (0-based)\n    >  local w, h = 1, 1 -- units of zettels (1-based)\n    >  -- render zettels depth-first, while tracking relative positions\n    >  local done = {}\n    >  local inprogress = {zettels.root}\n    >  render_state.wh2id = {{}}\n    >  while #inprogress > 0 do\n    >    local currid = table.remove(inprogress)\n    >    if not done[currid] then\n    >      done[currid] = true\n    >      table.insert(render_state.wh2id[w], currid)\n    >      local zettel = zettels[currid]\n    >      if currid == current_zettel_id then\n    >        render_state.curr_w = w\n    >        render_state.curr_h = h\n    >      end\n    >      local currbg = (currid == current_zettel_id) and view_settings.current_zettel_bg or bg\n    >      render_zettel(window, currbg, depth(zettel) * view_settings.indent, y, x, zettel)\n    >      if zettel.next then table.insert(inprogress, zettel.next) end\n    >      if zettel.child then table.insert(inprogress, zettel.child) end\n    >      bg = 3 - bg  -- toggle between color pairs 1 and 2\n    >      y = y + view_settings.height + view_settings.vmargin\n    >      h = h + 1\n    >      if y + view_settings.height > lines then\n    >        y = 0\n    >        h = 1\n    >        x = x + view_settings.width + view_settings.hmargin\n    >        w = w + 1\n    >        if x + view_settings.width > cols then break end\n    >        table.insert(render_state.wh2id, {})\n    >      end\n    >    end\n    >  end\n    >  window:mvaddstr(lines-1, 0, '')\n    >  for i=1,3 do\n    >    window:attrset(curses.color_pair(i%2+1))\n    >    window:addstr('')\n    >    spaces(view_settings.width-string.len(''))\n    >    window:attrset(curses.color_pair(0))\n    >    window:addstr(' ')  -- margin\n    >  end\n    >  window:refresh()\n    >end\n- __teliva_timestamp:\n    >Wed Feb  9 08:15:35 2022\n  main:\n    >function main()\n    >  init_colors()\n    >  current_zettel_id = zettels.root\n    >\n    >  curses.curs_set(0)\n    >  while true do\n    >    render(Window)\n    >    update(Window)\n    >  end\n    >end\n- __teliva_timestamp:\n    >Wed Feb  9 08:16:24 2022\n  __teliva_note:\n    >get rid of commandline\n    >\n    >There's a reason vim hides it. Confusing to have two cursors on screen.\n  editz:\n    >function editz(window)\n    >  menu = { {'^e', 'back to browsing'},}\n    >  local top = (render_state.curr_h - 1) * (view_settings.height + view_settings.vmargin)\n    >  local bottom = top + view_settings.height\n    >  local left = (render_state.curr_w - 1) * (view_settings.width + view_settings.hmargin)\n    >  local right = left + view_settings.width\n    >  local cursor = 1\n    >  local quit = false\n    >  while not quit do\n    >    editz_render(window, zettels[current_zettel_id].data, cursor, top, bottom, left, right)\n    >    quit, zettels[current_zettel_id].data, cursor = editz_update(window, zettels[current_zettel_id].data, cursor)\n    >  end\n    >end\n- __teliva_timestamp:\n    >Wed Feb  9 08:22:20 2022\n  editz_render:\n    >function editz_render(window, s, cursor, top, minbottom, left, right)\n    >  local h, w = window:getmaxyx()\n    >  local cursor_y, cursor_x = 0, 0\n    >  window:attrset(curses.color_pair(view_settings.current_zettel_bg))\n    >  for y=top,minbottom-1 do\n    >    for x=left,right-1 do\n    >      window:mvaddch(y, x, ' ')\n    >    end\n    >  end\n    >  local y, x = top, left + 1  -- left padding; TODO: indent\n    >  window:mvaddstr(y, x, '')\n    >  for i=1,s:len() do\n    >    if i == cursor then\n    >      cursor_y = y\n    >      cursor_x = x\n    >    end\n    >    if s[i] ~= '\\n' then\n    >      window:addstr(s[i])\n    >      x = x + 1\n    >      if x >= right then\n    >        y = y + 1\n    >        if y >= h-2 then return end\n    >        x = left + 1  -- left padding; TODO: indent\n    >        window:mvaddstr(y, x, '')\n    >      end\n    >    else\n    >      for col=x+1,right-1 do window:addch(' '); end\n    >      x = left\n    >      y = y + 1\n    >      if y >= h-2 then return end\n    >      window:mvaddstr(y, x, '')\n    >      for col=x,right-1 do window:addch(' '); end\n    >      x = left + 1  -- left padding; TODO: indent\n    >      window:mvaddstr(y, x, '')\n    >    end\n    >  end\n    >  if cursor_y == 0 and cursor_x == 0 then\n    >    cursor_y = y\n    >    cursor_x = x\n    >  end\n    >  window:mvaddstr(cursor_y, cursor_x, '')\n    >end\n- __teliva_timestamp:\n    >Wed Feb  9 08:25:05 2022\n  editz:\n    >function editz(window)\n    >  local old_menu = menu\n    >  menu = { {'^e', 'back to browsing'},}\n    >  local top = (render_state.curr_h - 1) * (view_settings.height + view_settings.vmargin)\n    >  local bottom = top + view_settings.height\n    >  local left = (render_state.curr_w - 1) * (view_settings.width + view_settings.hmargin)\n    >  local right = left + view_settings.width\n    >  local cursor = zettels[current_zettel_id].data:len()+1\n    >  local quit = false\n    >  curses.curs_set(1)\n    >  while not quit do\n    >    editz_render(window, zettels[current_zettel_id].data, cursor, top, bottom, left, right)\n    >    quit, zettels[current_zettel_id].data, cursor = editz_update(window, zettels[current_zettel_id].data, cursor)\n    >  end\n    >  curses.curs_set(0)\n    >  menu = old_menu\n    >end\n- __teliva_timestamp:\n    >Wed Feb  9 08:28:13 2022\n  __teliva_note:\n    >stop simulating the cursor\n    >\n    >editz_render is now much simpler\n  editz_update:\n    >function editz_update(window, prose, cursor)\n    >  local key = window:getch()\n    >  local h, w = window:getmaxyx()\n    >  if key == curses.KEY_LEFT then\n    >    if cursor > 1 then\n    >      cursor = cursor-1\n    >    end\n    >  elseif key == curses.KEY_RIGHT then\n    >    if cursor <= #prose then\n    >      cursor = cursor+1\n    >    end\n    >  elseif key == curses.KEY_DOWN then\n    >    cursor = cursor_down(prose, cursor, w)\n    >  elseif key == curses.KEY_UP then\n    >    cursor = cursor_up(prose, cursor, w)\n    >  elseif key == curses.KEY_BACKSPACE or key == 8 or key == 127 then  -- ctrl-h, ctrl-?, delete\n    >    if cursor > 1 then\n    >      cursor = cursor-1\n    >      prose = prose:remove(cursor)\n    >    end\n    >  elseif key == 5 then  -- ctrl-e\n    >    return true, prose, cursor\n    >  elseif key == 10 or (key >= 32 and key < 127) then\n    >    prose = prose:insert(string.char(key), cursor-1)\n    >    cursor = cursor+1\n    >  end\n    >  return false, prose, cursor\n    >end\n- __teliva_timestamp:\n    >Wed Feb  9 17:55:52 2022\n  menu:\n    >-- To show app-specific hotkeys in the menu bar, add hotkey/command\n    >-- arrays of strings to the menu array.\n    >menu = {\n    >  {'j', 'child'},\n    >  {'k', 'parent'},\n    >  {'l,h', 'next/prev sib'},\n    >  {'e', 'edit'},\n    >}\n- __teliva_timestamp:\n    >Wed Feb  9 17:56:18 2022\n  __teliva_note:\n    >no need for chords once we drop the commandline\n  update:\n    >function update(window)\n    >  local key = window:getch()\n    >  local curr = zettels[current_zettel_id]\n    >  -- graph-based navigation\n    >  if key == string.byte('j') then\n    >    if curr.child then\n    >      current_zettel_id = curr.child\n    >    elseif curr.next then\n    >      current_zettel_id = curr.next\n    >    elseif curr.parent and zettels[curr.parent].next then\n    >      current_zettel_id = zettels[curr.parent].next\n    >    end\n    >  elseif key == string.byte('k') then\n    >    if curr.parent then current_zettel_id = curr.parent end\n    >  elseif key == string.byte('h') then\n    >    if curr.prev then\n    >      current_zettel_id = curr.prev\n    >    elseif curr.parent then\n    >      current_zettel_id = curr.parent\n    >    end\n    >  elseif key == string.byte('l') then\n    >    if curr.next then\n    >      current_zettel_id = curr.next\n    >    elseif curr.parent and zettels[curr.parent].next then\n    >      current_zettel_id = zettels[curr.parent].next\n    >    end\n    >  -- screen-based navigation\n    >  elseif key == curses.KEY_UP then\n    >    if render_state.curr_h > 1 then\n    >      current_zettel_id = render_state.wh2id[render_state.curr_w][render_state.curr_h - 1]\n    >    end\n    >  elseif key == curses.KEY_DOWN then\n    >    if render_state.wh2id[render_state.curr_w][render_state.curr_h + 1] then\n    >      current_zettel_id = render_state.wh2id[render_state.curr_w][render_state.curr_h + 1]\n    >    end\n    >  elseif key == curses.KEY_LEFT then\n    >    if render_state.curr_w > 1 then\n    >      current_zettel_id = render_state.wh2id[render_state.curr_w - 1][render_state.curr_h]\n    >    end\n    >  elseif key == curses.KEY_RIGHT then\n    >    if render_state.wh2id[render_state.curr_w + 1] and render_state.wh2id[render_state.curr_w + 1][render_state.curr_h] then\n    >      current_zettel_id = render_state.wh2id[render_state.curr_w + 1][render_state.curr_h]\n    >    end\n    >  --\n    >  elseif key == string.byte('e') then\n    >    local old_menu = menu\n    >    editz(window)\n    >    menu = old_menu\n    >  end\n    >end\n- __teliva_timestamp:\n    >Wed Feb  9 18:00:42 2022\n  menu:\n    >-- To show app-specific hotkeys in the menu bar, add hotkey/command\n    >-- arrays of strings to the menu array.\n    >menu = {\n    >  {'j', 'child'},\n    >  {'k', 'parent'},\n    >  {'l,h', 'next/prev sib'},\n    >  {'e', 'edit'},\n    >  {'a,b', 'insert sib'},\n    >  {'c', 'insert child'},\n    >}\n- __teliva_timestamp:\n    >Wed Feb  9 18:16:23 2022\n  zettels:\n    >zettels = {\n    >  root=\"id1\",\n    >  final=4,\n    >  id1={\n    >    data=\"this is zettel A\\n\\nit has some text\",\n    >    child=\"id3\",\n    >    next=\"id2\",\n    >  },\n    >  id2={\n    >    data=\"this is a sibling of zettel A at the top level\",\n    >    prev=\"id1\",\n    >  },\n    >  id3={\n    >    data=\"this is zettel B, a child of A\",\n    >    parent=\"id1\",\n    >    next=\"id4\",\n    >  },\n    >  id4={\n    >    data=\"this is another child of zettel A, a sibling of B\",\n    >    parent=\"id1\",\n    >    prev=\"id3\",\n    >  }\n    >}\n- __teliva_timestamp:\n    >Wed Feb  9 23:04:49 2022\n  new_id:\n    >function new_id()\n    >  zettels.final = zettels.final+1\n    >  local result = 'id'..tostring(zettels.final)\n    >  zettels[result] = {}\n    >  return result\n    >end\n- __teliva_timestamp:\n    >Wed Feb  9 23:10:57 2022\n  __teliva_note:\n    >creating new zettels\n    >\n    >feels natural to immediately start editing them\n  update:\n    >function update(window)\n    >  local key = window:getch()\n    >  local curr = zettels[current_zettel_id]\n    >  assert(curr, string.format('cursor fell off the edge of the world: %s', type(current_zettel_id)))\n    >  -- graph-based navigation\n    >  if key == string.byte('j') then\n    >    if curr.child then\n    >      current_zettel_id = curr.child\n    >    elseif curr.next then\n    >      current_zettel_id = curr.next\n    >    elseif curr.parent and zettels[curr.parent].next then\n    >      current_zettel_id = zettels[curr.parent].next\n    >    end\n    >  elseif key == string.byte('k') then\n    >    if curr.parent then current_zettel_id = curr.parent end\n    >  elseif key == string.byte('h') then\n    >    if curr.prev then\n    >      current_zettel_id = curr.prev\n    >    elseif curr.parent then\n    >      current_zettel_id = curr.parent\n    >    end\n    >  elseif key == string.byte('l') then\n    >    if curr.next then\n    >      current_zettel_id = curr.next\n    >    elseif curr.parent and zettels[curr.parent].next then\n    >      current_zettel_id = zettels[curr.parent].next\n    >    end\n    >  -- screen-based navigation\n    >  elseif key == curses.KEY_UP then\n    >    if render_state.curr_h > 1 then\n    >      current_zettel_id = render_state.wh2id[render_state.curr_w][render_state.curr_h - 1]\n    >    end\n    >  elseif key == curses.KEY_DOWN then\n    >    if render_state.wh2id[render_state.curr_w][render_state.curr_h + 1] then\n    >      current_zettel_id = render_state.wh2id[render_state.curr_w][render_state.curr_h + 1]\n    >    end\n    >  elseif key == curses.KEY_LEFT then\n    >    if render_state.curr_w > 1 then\n    >      current_zettel_id = render_state.wh2id[render_state.curr_w - 1][render_state.curr_h]\n    >    end\n    >  elseif key == curses.KEY_RIGHT then\n    >    if render_state.wh2id[render_state.curr_w + 1] and render_state.wh2id[render_state.curr_w + 1][render_state.curr_h] then\n    >      current_zettel_id = render_state.wh2id[render_state.curr_w + 1][render_state.curr_h]\n    >    end\n    >  --\n    >  elseif key == string.byte('e') then\n    >    editz(window)\n    >  elseif key == string.byte('a') then\n    >    -- insert sibling after\n    >    local old = curr.next\n    >    curr.next = new_id()\n    >    local new = zettels[curr.next]\n    >    new.data = ''\n    >    new.next = old\n    >    zettels[old].prev = curr.next\n    >    new.prev = current_zettel_id\n    >    assert(curr.parent == zettels[old].parent, 'siblings should have same parent')\n    >    new.parent = curr.parent\n    >    current_zettel_id = curr.next\n    >    render(window) -- recompute render_state\n    >    editz(window)\n    >  elseif key == string.byte('b') then\n    >    -- insert sibling before\n    >    local old = curr.prev\n    >    curr.prev = new_id()\n    >    local new = zettels[curr.prev]\n    >    new.data = ''\n    >    new.prev = old\n    >    zettels[old].next = curr.prev\n    >    new.next = current_zettel_id\n    >    assert(curr.parent == zettels[old].parent, 'siblings should have same parent')\n    >    new.parent = curr.parent\n    >    current_zettel_id = curr.prev\n    >    render(window) -- recompute render_state\n    >    editz(window)\n    >  elseif key == string.byte('c') then\n    >    -- insert child\n    >    local old = curr.child\n    >    curr.child = new_id()\n    >    local new = zettels[curr.child]\n    >    new.data = ''\n    >    new.next = old\n    >    assert(zettels[old].prev == nil, \"first child shouldn't have a previous sibling\")\n    >    zettels[old].prev = curr.child\n    >    new.parent = curr\n    >    current_zettel_id = curr.child\n    >    render(window) -- recompute render_state\n    >    editz(window)\n    >  end\n    >end\n- __teliva_timestamp:\n    >Thu Feb 10 00:01:58 2022\n  update:\n    >function update(window)\n    >  local key = window:getch()\n    >  local h, w = window:getmaxyx()\n    >  local curr = zettels[current_zettel_id]\n    >  assert(curr, string.format('cursor fell off the edge of the world: %s', type(current_zettel_id)))\n    >  -- graph-based navigation\n    >  if key == string.byte('j') then\n    >    if curr.child then\n    >      current_zettel_id = curr.child\n    >    elseif curr.next then\n    >      current_zettel_id = curr.next\n    >    elseif curr.parent and zettels[curr.parent].next then\n    >      current_zettel_id = zettels[curr.parent].next\n    >    end\n    >  elseif key == string.byte('k') then\n    >    if curr.parent then current_zettel_id = curr.parent end\n    >  elseif key == string.byte('h') then\n    >    if curr.prev then\n    >      current_zettel_id = curr.prev\n    >    elseif curr.parent then\n    >      current_zettel_id = curr.parent\n    >    end\n    >  elseif key == string.byte('l') then\n    >    if curr.next then\n    >      current_zettel_id = curr.next\n    >    elseif curr.parent and zettels[curr.parent].next then\n    >      current_zettel_id = zettels[curr.parent].next\n    >    end\n    >  -- screen-based navigation\n    >  elseif key == curses.KEY_UP then\n    >    if render_state.curr_h > 1 then\n    >      current_zettel_id = render_state.wh2id[render_state.curr_w][render_state.curr_h - 1]\n    >    end\n    >  elseif key == curses.KEY_DOWN then\n    >    if render_state.wh2id[render_state.curr_w][render_state.curr_h + 1] then\n    >      current_zettel_id = render_state.wh2id[render_state.curr_w][render_state.curr_h + 1]\n    >    end\n    >  elseif key == curses.KEY_LEFT then\n    >    if render_state.curr_w > 1 then\n    >      current_zettel_id = render_state.wh2id[render_state.curr_w - 1][render_state.curr_h]\n    >    end\n    >  elseif key == curses.KEY_RIGHT then\n    >    if render_state.wh2id[render_state.curr_w + 1] and render_state.wh2id[render_state.curr_w + 1][render_state.curr_h] then\n    >      current_zettel_id = render_state.wh2id[render_state.curr_w + 1][render_state.curr_h]\n    >    end\n    >  --\n    >  elseif key == string.byte('e') then\n    >    editz(window)\n    >  elseif key == string.byte('a') then\n    >    -- insert sibling after\n    >    local old = curr.next\n    >    curr.next = new_id()\n    >    local new = zettels[curr.next]\n    >    new.data = ''\n    >    new.next = old\n    >    zettels[old].prev = curr.next\n    >    new.prev = current_zettel_id\n    >    assert(curr.parent == zettels[old].parent, 'siblings should have same parent')\n    >    new.parent = curr.parent\n    >    current_zettel_id = curr.next\n    >    render(window) -- recompute render_state\n    >    editz(window)\n    >  elseif key == string.byte('b') then\n    >    -- insert sibling before\n    >    local old = curr.prev\n    >    curr.prev = new_id()\n    >    local new = zettels[curr.prev]\n    >    new.data = ''\n    >    new.prev = old\n    >    zettels[old].next = curr.prev\n    >    new.next = current_zettel_id\n    >    assert(curr.parent == zettels[old].parent, 'siblings should have same parent')\n    >    new.parent = curr.parent\n    >    current_zettel_id = curr.prev\n    >    render(window) -- recompute render_state\n    >    editz(window)\n    >  elseif key == string.byte('c') then\n    >    -- insert child\n    >    local old = curr.child\n    >    curr.child = new_id()\n    >    local new = zettels[curr.child]\n    >    new.data = ''\n    >    new.next = old\n    >    assert(zettels[old].prev == nil, \"first child shouldn't have a previous sibling\")\n    >    zettels[old].prev = curr.child\n    >    new.parent = curr\n    >    current_zettel_id = curr.child\n    >    render(window) -- recompute render_state\n    >    editz(window)\n    >  elseif key == string.byte('x') then\n    >    if view_settings.width > 5 then\n    >      view_settings.width = view_settings.width - 5\n    >    end\n    >  elseif key == string.byte('X') then\n    >    if view_settings.width < w-5 then\n    >      view_settings.width = view_settings.width + 5\n    >    end\n    >  elseif key == string.byte('y') then\n    >    if view_settings.height > 0 then\n    >      view_settings.height = view_settings.height - 1\n    >    end\n    >  elseif key == string.byte('Y') then\n    >    if view_settings.height < h-2 then\n    >      view_settings.height = view_settings.height + 1\n    >    end\n    >  end\n    >end\n- __teliva_timestamp:\n    >Thu Feb 10 00:02:35 2022\n  menu:\n    >-- To show app-specific hotkeys in the menu bar, add hotkey/command\n    >-- arrays of strings to the menu array.\n    >menu = {\n    >  {'j', 'child'},\n    >  {'k', 'parent'},\n    >  {'l,h', 'next/prev sib'},\n    >  {'e', 'edit'},\n    >  {'a,b', 'insert sib'},\n    >  {'c', 'insert child'},\n    >  {'x,X,y,Y', 'resize'},\n    >}\n- __teliva_timestamp:\n    >Thu Feb 10 06:57:51 2022\n  __teliva_note:\n    >squeeze menu to make way for next feature\n  menu:\n    >-- To show app-specific hotkeys in the menu bar, add hotkey/command\n    >-- arrays of strings to the menu array.\n    >menu = {\n    >  {'a,b,c', 'insert sib/child'},\n    >  {'e', 'edit'},\n    >  {'j,k,l,h', 'move to child/parent/sib'},\n    >  {'x,X,y,Y', 'resize'},\n    >}\n- __teliva_timestamp:\n    >Thu Feb 10 07:00:46 2022\n  __teliva_note:\n    >bugfix: handle missing parent/child/sib\n  update:\n    >function update(window)\n    >  local key = window:getch()\n    >  local h, w = window:getmaxyx()\n    >  local curr = zettels[current_zettel_id]\n    >  assert(curr, string.format('cursor fell off the edge of the world: %s', type(current_zettel_id)))\n    >  -- graph-based navigation\n    >  if key == string.byte('j') then\n    >    if curr.child then\n    >      current_zettel_id = curr.child\n    >    elseif curr.next then\n    >      current_zettel_id = curr.next\n    >    elseif curr.parent and zettels[curr.parent].next then\n    >      current_zettel_id = zettels[curr.parent].next\n    >    end\n    >  elseif key == string.byte('k') then\n    >    if curr.parent then current_zettel_id = curr.parent end\n    >  elseif key == string.byte('h') then\n    >    if curr.prev then\n    >      current_zettel_id = curr.prev\n    >    elseif curr.parent then\n    >      current_zettel_id = curr.parent\n    >    end\n    >  elseif key == string.byte('l') then\n    >    if curr.next then\n    >      current_zettel_id = curr.next\n    >    elseif curr.parent and zettels[curr.parent].next then\n    >      current_zettel_id = zettels[curr.parent].next\n    >    end\n    >  -- screen-based navigation\n    >  elseif key == curses.KEY_UP then\n    >    if render_state.curr_h > 1 then\n    >      current_zettel_id = render_state.wh2id[render_state.curr_w][render_state.curr_h - 1]\n    >    end\n    >  elseif key == curses.KEY_DOWN then\n    >    if render_state.wh2id[render_state.curr_w][render_state.curr_h + 1] then\n    >      current_zettel_id = render_state.wh2id[render_state.curr_w][render_state.curr_h + 1]\n    >    end\n    >  elseif key == curses.KEY_LEFT then\n    >    if render_state.curr_w > 1 then\n    >      current_zettel_id = render_state.wh2id[render_state.curr_w - 1][render_state.curr_h]\n    >    end\n    >  elseif key == curses.KEY_RIGHT then\n    >    if render_state.wh2id[render_state.curr_w + 1] and render_state.wh2id[render_state.curr_w + 1][render_state.curr_h] then\n    >      current_zettel_id = render_state.wh2id[render_state.curr_w + 1][render_state.curr_h]\n    >    end\n    >  --\n    >  elseif key == string.byte('e') then\n    >    editz(window)\n    >  elseif key == string.byte('a') then\n    >    -- insert sibling after\n    >    local old = curr.next\n    >    curr.next = new_id()\n    >    local new = zettels[curr.next]\n    >    new.data = ''\n    >    new.next = old\n    >    new.prev = current_zettel_id\n    >    if old then\n    >      zettels[old].prev = curr.next\n    >      assert(curr.parent == zettels[old].parent, 'siblings should have same parent')\n    >    end\n    >    new.parent = curr.parent\n    >    current_zettel_id = curr.next\n    >    render(window) -- recompute render_state\n    >    editz(window)\n    >  elseif key == string.byte('b') then\n    >    -- insert sibling before\n    >    local old = curr.prev\n    >    curr.prev = new_id()\n    >    local new = zettels[curr.prev]\n    >    new.data = ''\n    >    new.prev = old\n    >    new.next = current_zettel_id\n    >    if old then\n    >      zettels[old].next = curr.prev\n    >      assert(curr.parent == zettels[old].parent, 'siblings should have same parent')\n    >    end\n    >    new.parent = curr.parent\n    >    current_zettel_id = curr.prev\n    >    render(window) -- recompute render_state\n    >    editz(window)\n    >  elseif key == string.byte('c') then\n    >    -- insert child\n    >    local old = curr.child\n    >    curr.child = new_id()\n    >    local new = zettels[curr.child]\n    >    new.data = ''\n    >    new.next = old\n    >    if old then\n    >      assert(zettels[old].prev == nil, \"first child shouldn't have a previous sibling\")\n    >      zettels[old].prev = curr.child\n    >    end\n    >    new.parent = curr\n    >    current_zettel_id = curr.child\n    >    render(window) -- recompute render_state\n    >    editz(window)\n    >  elseif key == string.byte('x') then\n    >    if view_settings.width > 5 then\n    >      view_settings.width = view_settings.width - 5\n    >    end\n    >  elseif key == string.byte('X') then\n    >    if view_settings.width < w-5 then\n    >      view_settings.width = view_settings.width + 5\n    >    end\n    >  elseif key == string.byte('y') then\n    >    if view_settings.height > 0 then\n    >      view_settings.height = view_settings.height - 1\n    >    end\n    >  elseif key == string.byte('Y') then\n    >    if view_settings.height < h-2 then\n    >      view_settings.height = view_settings.height + 1\n    >    end\n    >  end\n    >end\n- __teliva_timestamp:\n    >Thu Feb 10 07:27:43 2022\n  write_zettels:\n    >function write_zettels(outfile)\n    >  outfile:write(json.encode(zettels))\n    >  outfile:close()\n    >end\n- __teliva_timestamp:\n    >Thu Feb 10 07:28:30 2022\n  read_zettels:\n    >function read_zettels(infile)\n    >  zettels = json.decode(infile:read('*a'))\n    >  infile:close()\n    >end\n- __teliva_timestamp:\n    >Thu Feb 10 07:30:25 2022\n  __teliva_note:\n    >saving/loading zettels to/from disk\n  main:\n    >function main()\n    >  init_colors()\n    >  curses.curs_set(0)  -- hide cursor except when editing\n    >\n    >  -- read zettels from disk if possible\n    >  local infile = io.open('zet', 'r')\n    >  if infile then\n    >    read_zettels(infile)\n    >  else\n    >    local outfile = io.open('zet', 'w')\n    >    if outfile then\n    >      write_zettels(outfile)\n    >    end\n    >  end\n    >  current_zettel_id = zettels.root\n    >\n    >  while true do\n    >    render(Window)\n    >    update(Window)\n    >\n    >    -- save zettels, but hold on to previous state on disk\n    >    -- until last possible second\n    >    local filename = os.tmpname()\n    >    local outfile = io.open(filename, 'w')\n    >    write_zettels(outfile)\n    >    os.rename(filename, 'zet')\n    >  end\n    >end\n- __teliva_timestamp:\n    >Thu Feb 10 07:32:46 2022\n  __teliva_note:\n    >stop writing sample zettels to disk\n    >\n    >That was just for ease of testing write_zettels()\n  main:\n    >function main()\n    >  init_colors()\n    >  curses.curs_set(0)  -- hide cursor except when editing\n    >\n    >  local infile = io.open('zet', 'r')\n    >  if infile then\n    >    read_zettels(infile)\n    >  end\n    >  current_zettel_id = zettels.root\n    >\n    >  while true do\n    >    render(Window)\n    >    update(Window)\n    >\n    >    -- save zettels, but hold on to previous state on disk\n    >    -- until last possible second\n    >    local filename = os.tmpname()\n    >    local outfile = io.open(filename, 'w')\n    >    if outfile then\n    >      write_zettels(outfile)\n    >      os.rename(filename, 'zet')\n    >    end\n    >  end\n    >end\n- __teliva_timestamp:\n    >Thu Feb 10 07:43:39 2022\n  zettels:\n    >-- initial state of the zettels\n    >-- if you came here to clear the zettels,\n    >-- delete everything (ctrl-k and ctrl-u will delete a whole line at a time)\n    >-- until it looks like this:\n    >--\n    >--   zettels = {\n    >--     root='id1',\n    >--     final=1,\n    >--     id1={\n    >--       data='',\n    >--     },\n    >--   }\n    >--\n    >-- I don't yet trust any deletion feature I create to not mess up your data.\n    >-- Besides, this is a good excuse to start making this app your own.\n    >\n    >zettels = {\n    >  root='id1',\n    >  final=5,\n    >  id1={\n    >    data='this is zettel A\\n\\nit has some text',\n    >    child='id3',\n    >    next='id2',\n    >  },\n    >  id2={\n    >    data='this is a sibling of zettel A at the top level',\n    >    prev='id1',\n    >    next='id5',\n    >  },\n    >  id3={\n    >    data='this is zettel B, a child of A',\n    >    parent='id1',\n    >    next='id4',\n    >  },\n    >  id4={\n    >    data='this is another child of zettel A, a sibling of B',\n    >    parent='id1',\n    >    prev='id3',\n    >  },\n    >  id5={\n    >    data=\"(To clean up these sample zettels, hit ctrl-u and edit 'zettels')\\n\\nI don't yet trust any deletion feature I create to not mess up your data.\\nBesides, this is a good excuse to start making this app your own.)\",\n    >    prev='id2',\n    >  },\n    >}\n- __teliva_timestamp:\n    >Thu Feb 10 20:24:13 2022\n  menu:\n    >-- To show app-specific hotkeys in the menu bar, add hotkey/command\n    >-- arrays of strings to the menu array.\n    >menu = {\n    >  {'a,b,c', 'insert'},\n    >  {'e', 'edit'},\n    >  {'j,k,l,h', 'move'},\n    >  {'x,X,y,Y', 'resize'},\n    >  {'s', 'stash'},\n    >  {'t', 'link with stash'},\n    >}\n- __teliva_timestamp:\n    >Thu Feb 10 20:25:14 2022\n  stash:\n    >stash = nil\n- __teliva_timestamp:\n    >Thu Feb 10 20:32:38 2022\n  update:\n    >function update(window)\n    >  local key = window:getch()\n    >  local h, w = window:getmaxyx()\n    >  local curr = zettels[current_zettel_id]\n    >  assert(curr, string.format('cursor fell off the edge of the world: %s', type(current_zettel_id)))\n    >  -- move along the graph\n    >  if key == string.byte('j') then\n    >    if curr.child then\n    >      current_zettel_id = curr.child\n    >    elseif curr.next then\n    >      current_zettel_id = curr.next\n    >    elseif curr.parent and zettels[curr.parent].next then\n    >      current_zettel_id = zettels[curr.parent].next\n    >    end\n    >  elseif key == string.byte('k') then\n    >    if curr.parent then current_zettel_id = curr.parent end\n    >  elseif key == string.byte('h') then\n    >    if curr.prev then\n    >      current_zettel_id = curr.prev\n    >    elseif curr.parent then\n    >      current_zettel_id = curr.parent\n    >    end\n    >  elseif key == string.byte('l') then\n    >    if curr.next then\n    >      current_zettel_id = curr.next\n    >    elseif curr.parent and zettels[curr.parent].next then\n    >      current_zettel_id = zettels[curr.parent].next\n    >    end\n    >  -- move along the screen\n    >  elseif key == curses.KEY_UP then\n    >    if render_state.curr_h > 1 then\n    >      current_zettel_id = render_state.wh2id[render_state.curr_w][render_state.curr_h - 1]\n    >    end\n    >  elseif key == curses.KEY_DOWN then\n    >    if render_state.wh2id[render_state.curr_w][render_state.curr_h + 1] then\n    >      current_zettel_id = render_state.wh2id[render_state.curr_w][render_state.curr_h + 1]\n    >    end\n    >  elseif key == curses.KEY_LEFT then\n    >    if render_state.curr_w > 1 then\n    >      current_zettel_id = render_state.wh2id[render_state.curr_w - 1][render_state.curr_h]\n    >    end\n    >  elseif key == curses.KEY_RIGHT then\n    >    if render_state.wh2id[render_state.curr_w + 1] and render_state.wh2id[render_state.curr_w + 1][render_state.curr_h] then\n    >      current_zettel_id = render_state.wh2id[render_state.curr_w + 1][render_state.curr_h]\n    >    end\n    >  -- mutations\n    >  elseif key == string.byte('e') then\n    >    editz(window)\n    >  elseif key == string.byte('a') then\n    >    -- insert sibling after\n    >    local old = curr.next\n    >    curr.next = new_id()\n    >    local new = zettels[curr.next]\n    >    new.data = ''\n    >    new.next = old\n    >    new.prev = current_zettel_id\n    >    if old then\n    >      zettels[old].prev = curr.next\n    >      assert(curr.parent == zettels[old].parent, 'siblings should have same parent')\n    >    end\n    >    new.parent = curr.parent\n    >    current_zettel_id = curr.next\n    >    render(window) -- recompute render_state\n    >    editz(window)\n    >  elseif key == string.byte('b') then\n    >    -- insert sibling before\n    >    local old = curr.prev\n    >    curr.prev = new_id()\n    >    local new = zettels[curr.prev]\n    >    new.data = ''\n    >    new.prev = old\n    >    new.next = current_zettel_id\n    >    if old then\n    >      zettels[old].next = curr.prev\n    >      assert(curr.parent == zettels[old].parent, 'siblings should have same parent')\n    >    end\n    >    new.parent = curr.parent\n    >    current_zettel_id = curr.prev\n    >    render(window) -- recompute render_state\n    >    editz(window)\n    >  elseif key == string.byte('c') then\n    >    -- insert child\n    >    local old = curr.child\n    >    curr.child = new_id()\n    >    local new = zettels[curr.child]\n    >    new.data = ''\n    >    new.next = old\n    >    if old then\n    >      assert(zettels[old].prev == nil, \"first child shouldn't have a previous sibling\")\n    >      zettels[old].prev = curr.child\n    >    end\n    >    new.parent = curr\n    >    current_zettel_id = curr.child\n    >    render(window) -- recompute render_state\n    >    editz(window)\n    >  -- cross-links\n    >  elseif key == string.byte('s') then\n    >    -- save zettel to a stash\n    >    stash = current_zettel_id\n    >  elseif key == string.byte('t') then\n    >    -- cross-link a zettel bidirectionally with what's on the stash\n    >    if curr.crosslinks then\n    >      curr.crosslinks.a = stash\n    >    else\n    >      curr.crosslinks = {a=stash}\n    >    end\n    >  -- view settings\n    >  elseif key == string.byte('x') then\n    >    if view_settings.width > 5 then\n    >      view_settings.width = view_settings.width - 5\n    >    end\n    >  elseif key == string.byte('X') then\n    >    if view_settings.width < w-5 then\n    >      view_settings.width = view_settings.width + 5\n    >    end\n    >  elseif key == string.byte('y') then\n    >    if view_settings.height > 0 then\n    >      view_settings.height = view_settings.height - 1\n    >    end\n    >  elseif key == string.byte('Y') then\n    >    if view_settings.height < h-2 then\n    >      view_settings.height = view_settings.height + 1\n    >    end\n    >  end\n    >end\n- __teliva_timestamp:\n    >Thu Feb 10 20:39:15 2022\n  render:\n    >function render(window)\n    >  window:clear()\n    >  local lines, cols = window:getmaxyx()\n    >  local bg=1\n    >  local y, x = 0, 0 -- units of characters (0-based)\n    >  local w, h = 1, 1 -- units of zettels (1-based)\n    >  -- render zettels depth-first, while tracking relative positions\n    >  local done = {}\n    >  local inprogress = {zettels.root}\n    >  render_state.wh2id = {{}}\n    >  while #inprogress > 0 do\n    >    local currid = table.remove(inprogress)\n    >    if not done[currid] then\n    >      done[currid] = true\n    >      table.insert(render_state.wh2id[w], currid)\n    >      local zettel = zettels[currid]\n    >      if currid == current_zettel_id then\n    >        render_state.curr_w = w\n    >        render_state.curr_h = h\n    >      end\n    >      local currbg = (currid == current_zettel_id) and view_settings.current_zettel_bg or bg\n    >      render_zettel(window, currbg, depth(zettel) * view_settings.indent, y, x, zettel)\n    >      if zettel.next then table.insert(inprogress, zettel.next) end\n    >      if zettel.child then table.insert(inprogress, zettel.child) end\n    >      if zettel.crosslinks then\n    >        for relation, target in pairs(zettel.crosslinks) do\n    >          table.insert(inprogress, target)\n    >        end\n    >      end\n    >      bg = 3 - bg  -- toggle between color pairs 1 and 2\n    >      y = y + view_settings.height + view_settings.vmargin\n    >      h = h + 1\n    >      if y + view_settings.height > lines then\n    >        y = 0\n    >        h = 1\n    >        x = x + view_settings.width + view_settings.hmargin\n    >        w = w + 1\n    >        if x + view_settings.width > cols then break end\n    >        table.insert(render_state.wh2id, {})\n    >      end\n    >    end\n    >  end\n    >  window:mvaddstr(lines-1, 0, '')\n    >  bg = 1\n    >  x = 0\n    >  for i=1,3 do\n    >    local zettel = nil\n    >    if i == 1 and stash then\n    >      zettel = zettels[stash]\n    >    end\n    >    render_zettel(window, bg, 0, lines-1, x, zettel)\n    >    bg = 3 - bg\n    >    x = x + view_settings.width + view_settings.hmargin\n    >  end\n    >  window:refresh()\n    >end\n- __teliva_timestamp:\n    >Thu Feb 10 20:40:08 2022\n  __teliva_note:\n    >initial support for cross-links\n    >\n    >Kinda confusing because zettels still show indent based on their\n    >hierarchical location rather than the path they're rendered in.\n  render_zettel:\n    >function render_zettel(window, bg, indent, starty, startx, zettel)\n    >  window:attrset(curses.color_pair(bg))\n    >  for y=0,view_settings.height-1 do\n    >    for x=0,view_settings.width-1 do\n    >      window:mvaddch(y+starty, x+startx, ' ')\n    >    end\n    >  end\n    >  local y, x = 0, indent+1\n    >  local data = ''\n    >  if zettel then\n    >    data = zettel.data\n    >  end\n    >  for i=1,#data do\n    >    local c = data[i]\n    >    if c == '\\n' then\n    >      y = y+1\n    >      x = indent+1\n    >    else\n    >      window:mvaddstr(y+starty, x+startx, c)\n    >      x = x+1\n    >      if x >= startx + view_settings.width then\n    >        y = y+1\n    >        x = indent+1\n    >      end\n    >    end\n    >    if y >= view_settings.height then\n    >      break\n    >    end\n    >  end\n    >end\n- __teliva_timestamp:\n    >Thu Feb 10 20:44:29 2022\n  __teliva_note:\n    >looks better after dynamically recomputing depth while rendering\n  render:\n    >function render(window)\n    >  window:clear()\n    >  local lines, cols = window:getmaxyx()\n    >  local bg=1\n    >  local y, x = 0, 0 -- units of characters (0-based)\n    >  local w, h = 1, 1 -- units of zettels (1-based)\n    >  -- render zettels depth-first, while tracking relative positions\n    >  local done = {}\n    >  local inprogress = {{id=zettels.root,depth=0}}\n    >  render_state.wh2id = {{}}\n    >  while #inprogress > 0 do\n    >    local curr = table.remove(inprogress)\n    >    if not done[curr.id] then\n    >      done[curr.id] = true\n    >      table.insert(render_state.wh2id[w], curr.id)\n    >      local zettel = zettels[curr.id]\n    >      if curr.id == current_zettel_id then\n    >        render_state.curr_w = w\n    >        render_state.curr_h = h\n    >      end\n    >      local currbg = (curr.id == current_zettel_id) and view_settings.current_zettel_bg or bg\n    >      render_zettel(window, currbg, curr.depth * view_settings.indent, y, x, zettel)\n    >      if zettel.next then table.insert(inprogress, {id=zettel.next, depth=curr.depth}) end\n    >      if zettel.child then table.insert(inprogress, {id=zettel.child, depth=curr.depth+1}) end\n    >      if zettel.crosslinks then\n    >        for relation, target in pairs(zettel.crosslinks) do\n    >          table.insert(inprogress, {id=target, depth=curr.depth+1})\n    >        end\n    >      end\n    >      bg = 3 - bg  -- toggle between color pairs 1 and 2\n    >      y = y + view_settings.height + view_settings.vmargin\n    >      h = h + 1\n    >      if y + view_settings.height > lines then\n    >        y = 0\n    >        h = 1\n    >        x = x + view_settings.width + view_settings.hmargin\n    >        w = w + 1\n    >        if x + view_settings.width > cols then break end\n    >        table.insert(render_state.wh2id, {})\n    >      end\n    >    end\n    >  end\n    >  window:mvaddstr(lines-1, 0, '')\n    >  bg = 1\n    >  x = 0\n    >  for i=1,3 do\n    >    local zettel = nil\n    >    if i == 1 and stash then\n    >      zettel = zettels[stash]\n    >    end\n    >    render_zettel(window, bg, 0, lines-1, x, zettel)\n    >    bg = 3 - bg\n    >    x = x + view_settings.width + view_settings.hmargin\n    >  end\n    >  window:refresh()\n    >end\n- __teliva_timestamp:\n    >Thu Feb 10 20:55:19 2022\n  render_zettel:\n    >function render_zettel(window, bg, indent, edge_label, starty, startx, zettel)\n    >  window:attrset(curses.color_pair(bg))\n    >  for y=0,view_settings.height-1 do\n    >    for x=0,view_settings.width-1 do\n    >      window:mvaddch(y+starty, x+startx, ' ')\n    >    end\n    >  end\n    >  if indent > 1 then\n    >    window:attrset(curses.color_pair(bg+1))  -- go from zettel color to its edge color\n    >    window:mvaddstr(starty, startx+indent-1, edge_label)\n    >    window:attrset(curses.color_pair(bg))\n    >  end\n    >  local y, x = 0, indent+1\n    >  local data = ''\n    >  if zettel then\n    >    data = zettel.data\n    >  end\n    >  for i=1,#data do\n    >    local c = data[i]\n    >    if c == '\\n' then\n    >      y = y+1\n    >      x = indent+1\n    >    else\n    >      window:mvaddstr(y+starty, x+startx, c)\n    >      x = x+1\n    >      if x >= startx + view_settings.width then\n    >        y = y+1\n    >        x = indent+1\n    >      end\n    >    end\n    >    if y >= view_settings.height then\n    >      break\n    >    end\n    >  end\n    >end\n- __teliva_timestamp:\n    >Thu Feb 10 20:58:49 2022\n  view_settings:\n    >view_settings = {\n    >  -- dimensions for rendering a single zettel; extra text gets truncated\n    >  width=50,\n    >  height=3,\n    >  -- spacing between zettels\n    >  hmargin=1,\n    >  vmargin=1,\n    >  --\n    >  indent=2,  -- how children of a zettel are indicated\n    >}\n- __teliva_timestamp:\n    >Thu Feb 10 20:59:18 2022\n  render:\n    >function render(window)\n    >  window:clear()\n    >  local lines, cols = window:getmaxyx()\n    >  local bg=3\n    >  local y, x = 0, 0 -- units of characters (0-based)\n    >  local w, h = 1, 1 -- units of zettels (1-based)\n    >  -- render zettels depth-first, while tracking relative positions\n    >  local done = {}\n    >  local inprogress = {{id=zettels.root,depth=0,edge=''}}\n    >  render_state.wh2id = {{}}\n    >  while #inprogress > 0 do\n    >    local curr = table.remove(inprogress)\n    >    if not done[curr.id] then\n    >      done[curr.id] = true\n    >      table.insert(render_state.wh2id[w], curr.id)\n    >      local zettel = zettels[curr.id]\n    >      if curr.id == current_zettel_id then\n    >        render_state.curr_w = w\n    >        render_state.curr_h = h\n    >      end\n    >      local currbg = (curr.id == current_zettel_id) and 1 or bg  -- 1 is the color pair for the current zettel\n    >      render_zettel(window, currbg, curr.depth * view_settings.indent, curr.edge, y, x, zettel)\n    >      if zettel.next then table.insert(inprogress, {id=zettel.next, depth=curr.depth, edge='|'}) end\n    >      if zettel.child then table.insert(inprogress, {id=zettel.child, depth=curr.depth+1, edge='\\\\'}) end\n    >      if zettel.crosslinks then\n    >        for relation, target in pairs(zettel.crosslinks) do\n    >          table.insert(inprogress, {id=target, depth=curr.depth+1, edge=relation})\n    >        end\n    >      end\n    >      bg = 8 - bg  -- toggle between color pairs 3 and 5\n    >      y = y + view_settings.height + view_settings.vmargin\n    >      h = h + 1\n    >      if y + view_settings.height > lines then\n    >        y = 0\n    >        h = 1\n    >        x = x + view_settings.width + view_settings.hmargin\n    >        w = w + 1\n    >        if x + view_settings.width > cols then break end\n    >        table.insert(render_state.wh2id, {})\n    >      end\n    >    end\n    >  end\n    >  window:mvaddstr(lines-1, 0, '')\n    >  bg = 3\n    >  x = 0\n    >  for i=1,3 do\n    >    local zettel = nil\n    >    if i == 1 and stash then\n    >      zettel = zettels[stash]\n    >    end\n    >    render_zettel(window, bg, 0, '', lines-1, x, zettel)\n    >    bg = 8 - bg  -- toggle between color pairs 3 and 5\n    >    x = x + view_settings.width + view_settings.hmargin\n    >  end\n    >  window:refresh()\n    >end\n- __teliva_timestamp:\n    >Thu Feb 10 21:02:41 2022\n  __teliva_note:\n    >label the incoming edge for each zettel\n    >\n    >Is it a child, sibling or other cross-link?\n  init_colors:\n    >function init_colors()\n    >  -- light background\n    >    -- current zettel\n    >  curses.init_pair(1, 236, 230)\n    >  curses.init_pair(2, 1,   230)  -- edge label for current zettel\n    >    -- non-current zettel #1\n    >  curses.init_pair(3, 236, 250)\n    >  curses.init_pair(4, 1,   250)  -- edge label for pair 3\n    >    -- non-current zettel #2\n    >  curses.init_pair(5, 236, 252)\n    >  curses.init_pair(6, 1,   252)  -- edge label for pair 5\n    >  -- dark background\n    >--?     -- current zettel\n    >--?   curses.init_pair(7, 252, 130)\n    >--?     -- other zettels\n    >--?   curses.init_pair(1, 252, 240)\n    >--?   curses.init_pair(2, 252, 242)\n    >--?     -- edge labels\n    >--?   curses.init_pair(3, 1, 240)  -- same bg as pair 1\n    >--?   curses.init_pair(4, 1, 242)  -- same bg as pair 2\n    >--?   curses.init_pair(9, 1, 130)  -- same bg as pair 7 for current zettel\n    >end\n- __teliva_timestamp:\n    >Thu Feb 10 21:11:35 2022\n  menu:\n    >-- To show app-specific hotkeys in the menu bar, add hotkey/command\n    >-- arrays of strings to the menu array.\n    >menu = {\n    >  {'a,b,c', 'insert'},\n    >  {'e', 'edit'},\n    >  {'j,k,l,h', 'move'},\n    >  {'x,X,y,Y', 'resize'},\n    >  {'s', 'stash'},\n    >  {'t', 'link with stash'},\n    >  {'z', 'scroll'},\n    >}\n- __teliva_timestamp:\n    >Thu Feb 10 21:13:19 2022\n  main:\n    >function main()\n    >  init_colors()\n    >  curses.curs_set(0)  -- hide cursor except when editing\n    >\n    >  local infile = io.open('zet', 'r')\n    >  if infile then\n    >    read_zettels(infile)\n    >  end\n    >  current_zettel_id = zettels.root  -- cursor\n    >  view_settings.first_zettel = zettels.root  -- start rendering here\n    >\n    >  while true do\n    >    render(Window)\n    >    update(Window)\n    >\n    >    -- save zettels, but hold on to previous state on disk\n    >    -- until last possible second\n    >    local filename = os.tmpname()\n    >    local outfile = io.open(filename, 'w')\n    >    if outfile then\n    >      write_zettels(outfile)\n    >      os.rename(filename, 'zet')\n    >    end\n    >  end\n    >end\n- __teliva_timestamp:\n    >Thu Feb 10 21:13:36 2022\n  render:\n    >function render(window)\n    >  window:clear()\n    >  local lines, cols = window:getmaxyx()\n    >  local bg=3\n    >  local y, x = 0, 0 -- units of characters (0-based)\n    >  local w, h = 1, 1 -- units of zettels (1-based)\n    >  -- render zettels depth-first, while tracking relative positions\n    >  local done = {}\n    >  local inprogress = {{id=view_settings.first_zettel,depth=0,edge=''}}\n    >  render_state.wh2id = {{}}\n    >  while #inprogress > 0 do\n    >    local curr = table.remove(inprogress)\n    >    if not done[curr.id] then\n    >      done[curr.id] = true\n    >      table.insert(render_state.wh2id[w], curr.id)\n    >      local zettel = zettels[curr.id]\n    >      if curr.id == current_zettel_id then\n    >        render_state.curr_w = w\n    >        render_state.curr_h = h\n    >      end\n    >      local currbg = (curr.id == current_zettel_id) and 1 or bg  -- 1 is the color pair for the current zettel\n    >      render_zettel(window, currbg, curr.depth * view_settings.indent, curr.edge, y, x, zettel)\n    >      if zettel.next then table.insert(inprogress, {id=zettel.next, depth=curr.depth, edge='|'}) end\n    >      if zettel.child then table.insert(inprogress, {id=zettel.child, depth=curr.depth+1, edge='\\\\'}) end\n    >      if zettel.crosslinks then\n    >        for relation, target in pairs(zettel.crosslinks) do\n    >          table.insert(inprogress, {id=target, depth=curr.depth+1, edge=relation})\n    >        end\n    >      end\n    >      bg = 8 - bg  -- toggle between color pairs 3 and 5\n    >      y = y + view_settings.height + view_settings.vmargin\n    >      h = h + 1\n    >      if y + view_settings.height > lines then\n    >        y = 0\n    >        h = 1\n    >        x = x + view_settings.width + view_settings.hmargin\n    >        w = w + 1\n    >        if x + view_settings.width > cols then break end\n    >        table.insert(render_state.wh2id, {})\n    >      end\n    >    end\n    >  end\n    >  window:mvaddstr(lines-1, 0, '')\n    >  bg = 3\n    >  x = 0\n    >  for i=1,3 do\n    >    local zettel = nil\n    >    if i == 1 and stash then\n    >      zettel = zettels[stash]\n    >    end\n    >    render_zettel(window, bg, 0, '', lines-1, x, zettel)\n    >    bg = 8 - bg  -- toggle between color pairs 3 and 5\n    >    x = x + view_settings.width + view_settings.hmargin\n    >  end\n    >  window:refresh()\n    >end\n- __teliva_timestamp:\n    >Thu Feb 10 21:19:26 2022\n  __teliva_note:\n    >bugfix: cross-links should be bidirectional\n  update:\n    >function update(window)\n    >  local key = window:getch()\n    >  local h, w = window:getmaxyx()\n    >  local curr = zettels[current_zettel_id]\n    >  assert(curr, string.format('cursor fell off the edge of the world: %s', type(current_zettel_id)))\n    >  -- move along the graph\n    >  if key == string.byte('j') then\n    >    if curr.child then\n    >      current_zettel_id = curr.child\n    >    elseif curr.next then\n    >      current_zettel_id = curr.next\n    >    elseif curr.parent and zettels[curr.parent].next then\n    >      current_zettel_id = zettels[curr.parent].next\n    >    end\n    >  elseif key == string.byte('k') then\n    >    if curr.parent then current_zettel_id = curr.parent end\n    >  elseif key == string.byte('h') then\n    >    if curr.prev then\n    >      current_zettel_id = curr.prev\n    >    elseif curr.parent then\n    >      current_zettel_id = curr.parent\n    >    end\n    >  elseif key == string.byte('l') then\n    >    if curr.next then\n    >      current_zettel_id = curr.next\n    >    elseif curr.parent and zettels[curr.parent].next then\n    >      current_zettel_id = zettels[curr.parent].next\n    >    end\n    >  -- move along the screen\n    >  elseif key == curses.KEY_UP then\n    >    if render_state.curr_h > 1 then\n    >      current_zettel_id = render_state.wh2id[render_state.curr_w][render_state.curr_h - 1]\n    >    end\n    >  elseif key == curses.KEY_DOWN then\n    >    if render_state.wh2id[render_state.curr_w][render_state.curr_h + 1] then\n    >      current_zettel_id = render_state.wh2id[render_state.curr_w][render_state.curr_h + 1]\n    >    end\n    >  elseif key == curses.KEY_LEFT then\n    >    if render_state.curr_w > 1 then\n    >      current_zettel_id = render_state.wh2id[render_state.curr_w - 1][render_state.curr_h]\n    >    end\n    >  elseif key == curses.KEY_RIGHT then\n    >    if render_state.wh2id[render_state.curr_w + 1] and render_state.wh2id[render_state.curr_w + 1][render_state.curr_h] then\n    >      current_zettel_id = render_state.wh2id[render_state.curr_w + 1][render_state.curr_h]\n    >    end\n    >  -- mutations\n    >  elseif key == string.byte('e') then\n    >    editz(window)\n    >  elseif key == string.byte('a') then\n    >    -- insert sibling after\n    >    local old = curr.next\n    >    curr.next = new_id()\n    >    local new = zettels[curr.next]\n    >    new.data = ''\n    >    new.next = old\n    >    new.prev = current_zettel_id\n    >    if old then\n    >      zettels[old].prev = curr.next\n    >      assert(curr.parent == zettels[old].parent, 'siblings should have same parent')\n    >    end\n    >    new.parent = curr.parent\n    >    current_zettel_id = curr.next\n    >    render(window) -- recompute render_state\n    >    editz(window)\n    >  elseif key == string.byte('b') then\n    >    -- insert sibling before\n    >    local old = curr.prev\n    >    curr.prev = new_id()\n    >    local new = zettels[curr.prev]\n    >    new.data = ''\n    >    new.prev = old\n    >    new.next = current_zettel_id\n    >    if old then\n    >      zettels[old].next = curr.prev\n    >      assert(curr.parent == zettels[old].parent, 'siblings should have same parent')\n    >    end\n    >    new.parent = curr.parent\n    >    current_zettel_id = curr.prev\n    >    render(window) -- recompute render_state\n    >    editz(window)\n    >  elseif key == string.byte('c') then\n    >    -- insert child\n    >    local old = curr.child\n    >    curr.child = new_id()\n    >    local new = zettels[curr.child]\n    >    new.data = ''\n    >    new.next = old\n    >    if old then\n    >      assert(zettels[old].prev == nil, \"first child shouldn't have a previous sibling\")\n    >      zettels[old].prev = curr.child\n    >    end\n    >    new.parent = curr\n    >    current_zettel_id = curr.child\n    >    render(window) -- recompute render_state\n    >    editz(window)\n    >  -- cross-links\n    >  elseif key == string.byte('s') then\n    >    -- save zettel to a stash\n    >    stash = current_zettel_id\n    >  elseif key == string.byte('t') then\n    >    -- cross-link a zettel bidirectionally with what's on the stash\n    >    local insert_crosslink =\n    >      function(a, rel, b_id)\n    >        if a.crosslinks == nil then\n    >          a.crosslinks = {}\n    >        end\n    >        a.crosslinks[rel] = b_id\n    >      end\n    >    insert_crosslink(curr, 'a', stash)\n    >    insert_crosslink(zettels[stash], 'a', current_zettel_id)\n    >  -- view settings\n    >  elseif key == string.byte('x') then\n    >    if view_settings.width > 5 then\n    >      view_settings.width = view_settings.width - 5\n    >    end\n    >  elseif key == string.byte('X') then\n    >    if view_settings.width < w-5 then\n    >      view_settings.width = view_settings.width + 5\n    >    end\n    >  elseif key == string.byte('y') then\n    >    if view_settings.height > 0 then\n    >      view_settings.height = view_settings.height - 1\n    >    end\n    >  elseif key == string.byte('Y') then\n    >    if view_settings.height < h-2 then\n    >      view_settings.height = view_settings.height + 1\n    >    end\n    >  elseif key == string.byte('z') then\n    >    -- scroll to show the current zettel at top of screen\n    >    -- often has the effect of zooming in on its hierarchy\n    >    view_settings.first_zettel = current_zettel_id\n    >  end\n    >end\n- __teliva_timestamp:\n    >Thu Feb 10 21:20:45 2022\n  __teliva_note:\n    >clear stash after linking\n  update:\n    >function update(window)\n    >  local key = window:getch()\n    >  local h, w = window:getmaxyx()\n    >  local curr = zettels[current_zettel_id]\n    >  assert(curr, string.format('cursor fell off the edge of the world: %s', type(current_zettel_id)))\n    >  -- move along the graph\n    >  if key == string.byte('j') then\n    >    if curr.child then\n    >      current_zettel_id = curr.child\n    >    elseif curr.next then\n    >      current_zettel_id = curr.next\n    >    elseif curr.parent and zettels[curr.parent].next then\n    >      current_zettel_id = zettels[curr.parent].next\n    >    end\n    >  elseif key == string.byte('k') then\n    >    if curr.parent then current_zettel_id = curr.parent end\n    >  elseif key == string.byte('h') then\n    >    if curr.prev then\n    >      current_zettel_id = curr.prev\n    >    elseif curr.parent then\n    >      current_zettel_id = curr.parent\n    >    end\n    >  elseif key == string.byte('l') then\n    >    if curr.next then\n    >      current_zettel_id = curr.next\n    >    elseif curr.parent and zettels[curr.parent].next then\n    >      current_zettel_id = zettels[curr.parent].next\n    >    end\n    >  -- move along the screen\n    >  elseif key == curses.KEY_UP then\n    >    if render_state.curr_h > 1 then\n    >      current_zettel_id = render_state.wh2id[render_state.curr_w][render_state.curr_h - 1]\n    >    end\n    >  elseif key == curses.KEY_DOWN then\n    >    if render_state.wh2id[render_state.curr_w][render_state.curr_h + 1] then\n    >      current_zettel_id = render_state.wh2id[render_state.curr_w][render_state.curr_h + 1]\n    >    end\n    >  elseif key == curses.KEY_LEFT then\n    >    if render_state.curr_w > 1 then\n    >      current_zettel_id = render_state.wh2id[render_state.curr_w - 1][render_state.curr_h]\n    >    end\n    >  elseif key == curses.KEY_RIGHT then\n    >    if render_state.wh2id[render_state.curr_w + 1] and render_state.wh2id[render_state.curr_w + 1][render_state.curr_h] then\n    >      current_zettel_id = render_state.wh2id[render_state.curr_w + 1][render_state.curr_h]\n    >    end\n    >  -- mutations\n    >  elseif key == string.byte('e') then\n    >    editz(window)\n    >  elseif key == string.byte('a') then\n    >    -- insert sibling after\n    >    local old = curr.next\n    >    curr.next = new_id()\n    >    local new = zettels[curr.next]\n    >    new.data = ''\n    >    new.next = old\n    >    new.prev = current_zettel_id\n    >    if old then\n    >      zettels[old].prev = curr.next\n    >      assert(curr.parent == zettels[old].parent, 'siblings should have same parent')\n    >    end\n    >    new.parent = curr.parent\n    >    current_zettel_id = curr.next\n    >    render(window) -- recompute render_state\n    >    editz(window)\n    >  elseif key == string.byte('b') then\n    >    -- insert sibling before\n    >    local old = curr.prev\n    >    curr.prev = new_id()\n    >    local new = zettels[curr.prev]\n    >    new.data = ''\n    >    new.prev = old\n    >    new.next = current_zettel_id\n    >    if old then\n    >      zettels[old].next = curr.prev\n    >      assert(curr.parent == zettels[old].parent, 'siblings should have same parent')\n    >    end\n    >    new.parent = curr.parent\n    >    current_zettel_id = curr.prev\n    >    render(window) -- recompute render_state\n    >    editz(window)\n    >  elseif key == string.byte('c') then\n    >    -- insert child\n    >    local old = curr.child\n    >    curr.child = new_id()\n    >    local new = zettels[curr.child]\n    >    new.data = ''\n    >    new.next = old\n    >    if old then\n    >      assert(zettels[old].prev == nil, \"first child shouldn't have a previous sibling\")\n    >      zettels[old].prev = curr.child\n    >    end\n    >    new.parent = curr\n    >    current_zettel_id = curr.child\n    >    render(window) -- recompute render_state\n    >    editz(window)\n    >  -- cross-links\n    >  elseif key == string.byte('s') then\n    >    -- save zettel to a stash\n    >    stash = current_zettel_id\n    >  elseif key == string.byte('t') then\n    >    -- cross-link a zettel bidirectionally with what's on the stash\n    >    local insert_crosslink =\n    >      function(a, rel, b_id)\n    >        if a.crosslinks == nil then\n    >          a.crosslinks = {}\n    >        end\n    >        a.crosslinks[rel] = b_id\n    >      end\n    >    insert_crosslink(curr, 'a', stash)\n    >    insert_crosslink(zettels[stash], 'a', current_zettel_id)\n    >    stash = nil\n    >  -- view settings\n    >  elseif key == string.byte('x') then\n    >    if view_settings.width > 5 then\n    >      view_settings.width = view_settings.width - 5\n    >    end\n    >  elseif key == string.byte('X') then\n    >    if view_settings.width < w-5 then\n    >      view_settings.width = view_settings.width + 5\n    >    end\n    >  elseif key == string.byte('y') then\n    >    if view_settings.height > 0 then\n    >      view_settings.height = view_settings.height - 1\n    >    end\n    >  elseif key == string.byte('Y') then\n    >    if view_settings.height < h-2 then\n    >      view_settings.height = view_settings.height + 1\n    >    end\n    >  elseif key == string.byte('z') then\n    >    -- scroll to show the current zettel at top of screen\n    >    -- often has the effect of zooming in on its hierarchy\n    >    view_settings.first_zettel = current_zettel_id\n    >  end\n    >end\n- __teliva_timestamp:\n    >Thu Feb 10 21:51:09 2022\n  __teliva_note:\n    >fix regression in editor\n  editz_render:\n    >function editz_render(window, s, cursor, top, minbottom, left, right)\n    >  local h, w = window:getmaxyx()\n    >  local cursor_y, cursor_x = 0, 0\n    >  window:attrset(curses.color_pair(1)) -- 1 is the color combination for the current zettel\n    >  for y=top,minbottom-1 do\n    >    for x=left,right-1 do\n    >      window:mvaddch(y, x, ' ')\n    >    end\n    >  end\n    >  local y, x = top, left + 1  -- left padding; TODO: indent\n    >  window:mvaddstr(y, x, '')\n    >  for i=1,s:len() do\n    >    if i == cursor then\n    >      cursor_y = y\n    >      cursor_x = x\n    >    end\n    >    if s[i] ~= '\\n' then\n    >      window:addstr(s[i])\n    >      x = x + 1\n    >      if x >= right then\n    >        y = y + 1\n    >        if y >= h-2 then return end\n    >        x = left + 1  -- left padding; TODO: indent\n    >        window:mvaddstr(y, x, '')\n    >      end\n    >    else\n    >      for col=x+1,right-1 do window:addch(' '); end\n    >      x = left\n    >      y = y + 1\n    >      if y >= h-2 then return end\n    >      window:mvaddstr(y, x, '')\n    >      for col=x,right-1 do window:addch(' '); end\n    >      x = left + 1  -- left padding; TODO: indent\n    >      window:mvaddstr(y, x, '')\n    >    end\n    >  end\n    >  if cursor_y == 0 and cursor_x == 0 then\n    >    cursor_y = y\n    >    cursor_x = x\n    >  end\n    >  window:mvaddstr(cursor_y, cursor_x, '')\n    >end\n- __teliva_timestamp:\n    >Fri Feb 11 01:33:31 2022\n  __teliva_note:\n    >support /tmp being on a separate volume\n    >\n    >also better error-checking\n  main:\n    >function main()\n    >  init_colors()\n    >  curses.curs_set(0)  -- hide cursor except when editing\n    >\n    >  local infile = io.open('zet', 'r')\n    >  if infile then\n    >    read_zettels(infile)\n    >  end\n    >  current_zettel_id = zettels.root  -- cursor\n    >  view_settings.first_zettel = zettels.root  -- start rendering here\n    >\n    >  while true do\n    >    render(Window)\n    >    update(Window)\n    >\n    >    -- save zettels, but hold on to previous state on disk\n    >    -- until last possible second\n    >    local outfile = io.open('teliva_tmp', 'w')\n    >    if outfile then\n    >      write_zettels(outfile)\n    >      local status, message = os.rename('teliva_tmp', 'zet')\n    >      assert(status, message)  -- unceremoniously abort, but we hopefully only lost a little\n    >    end\n    >    -- TODO: what if io.open failed for a non-sandboxing related reason?!\n    >    -- We could silently fail to save.\n    >  end\n    >end\n- __teliva_timestamp:\n    >Fri Feb 11 07:51:42 2022\n  __teliva_note:\n    >bugfix in parent link when inserting child\n  update:\n    >function update(window)\n    >  local key = window:getch()\n    >  local h, w = window:getmaxyx()\n    >  local curr = zettels[current_zettel_id]\n    >  assert(curr, string.format('cursor fell off the edge of the world: %s', type(current_zettel_id)))\n    >  -- move along the graph\n    >  if key == string.byte('j') then\n    >    if curr.child then\n    >      current_zettel_id = curr.child\n    >    elseif curr.next then\n    >      current_zettel_id = curr.next\n    >    elseif curr.parent and zettels[curr.parent].next then\n    >      current_zettel_id = zettels[curr.parent].next\n    >    end\n    >  elseif key == string.byte('k') then\n    >    if curr.parent then\n    >      current_zettel_id = curr.parent\n    >    end\n    >  elseif key == string.byte('h') then\n    >    if curr.prev then\n    >      current_zettel_id = curr.prev\n    >    elseif curr.parent then\n    >      current_zettel_id = curr.parent\n    >    end\n    >  elseif key == string.byte('l') then\n    >    if curr.next then\n    >      current_zettel_id = curr.next\n    >    elseif curr.parent and zettels[curr.parent].next then\n    >      current_zettel_id = zettels[curr.parent].next\n    >    end\n    >  -- move along the screen\n    >  elseif key == curses.KEY_UP then\n    >    if render_state.curr_h > 1 then\n    >      current_zettel_id = render_state.wh2id[render_state.curr_w][render_state.curr_h - 1]\n    >    end\n    >  elseif key == curses.KEY_DOWN then\n    >    if render_state.wh2id[render_state.curr_w][render_state.curr_h + 1] then\n    >      current_zettel_id = render_state.wh2id[render_state.curr_w][render_state.curr_h + 1]\n    >    end\n    >  elseif key == curses.KEY_LEFT then\n    >    if render_state.curr_w > 1 then\n    >      current_zettel_id = render_state.wh2id[render_state.curr_w - 1][render_state.curr_h]\n    >    end\n    >  elseif key == curses.KEY_RIGHT then\n    >    if render_state.wh2id[render_state.curr_w + 1] and render_state.wh2id[render_state.curr_w + 1][render_state.curr_h] then\n    >      current_zettel_id = render_state.wh2id[render_state.curr_w + 1][render_state.curr_h]\n    >    end\n    >  -- mutations\n    >  elseif key == string.byte('e') then\n    >    editz(window)\n    >  elseif key == string.byte('a') then\n    >    -- insert sibling after\n    >    local old = curr.next\n    >    curr.next = new_id()\n    >    local new = zettels[curr.next]\n    >    new.data = ''\n    >    new.next = old\n    >    new.prev = current_zettel_id\n    >    if old then\n    >      zettels[old].prev = curr.next\n    >      assert(curr.parent == zettels[old].parent, 'siblings should have same parent')\n    >    end\n    >    new.parent = curr.parent\n    >    current_zettel_id = curr.next\n    >    render(window) -- recompute render_state\n    >    editz(window)\n    >  elseif key == string.byte('b') then\n    >    -- insert sibling before\n    >    local old = curr.prev\n    >    curr.prev = new_id()\n    >    local new = zettels[curr.prev]\n    >    new.data = ''\n    >    new.prev = old\n    >    new.next = current_zettel_id\n    >    if old then\n    >      zettels[old].next = curr.prev\n    >      assert(curr.parent == zettels[old].parent, 'siblings should have same parent')\n    >    end\n    >    new.parent = curr.parent\n    >    current_zettel_id = curr.prev\n    >    render(window) -- recompute render_state\n    >    editz(window)\n    >  elseif key == string.byte('c') then\n    >    -- insert child\n    >    local old = curr.child\n    >    curr.child = new_id()\n    >    local new = zettels[curr.child]\n    >    new.data = ''\n    >    new.next = old\n    >    if old then\n    >      assert(zettels[old].prev == nil, \"first child shouldn't have a previous sibling\")\n    >      zettels[old].prev = curr.child\n    >    end\n    >    new.parent = current_zettel_id\n    >    current_zettel_id = curr.child\n    >    render(window) -- recompute render_state\n    >    editz(window)\n    >  -- cross-links\n    >  elseif key == string.byte('s') then\n    >    -- save zettel to a stash\n    >    stash = current_zettel_id\n    >  elseif key == string.byte('t') then\n    >    -- cross-link a zettel bidirectionally with what's on the stash\n    >    local insert_crosslink =\n    >      function(a, rel, b_id)\n    >        if a.crosslinks == nil then\n    >          a.crosslinks = {}\n    >        end\n    >        a.crosslinks[rel] = b_id\n    >      end\n    >    insert_crosslink(curr, 'a', stash)\n    >    insert_crosslink(zettels[stash], 'a', current_zettel_id)\n    >    stash = nil\n    >  -- view settings\n    >  elseif key == string.byte('x') then\n    >    if view_settings.width > 5 then\n    >      view_settings.width = view_settings.width - 5\n    >    end\n    >  elseif key == string.byte('X') then\n    >    if view_settings.width < w-5 then\n    >      view_settings.width = view_settings.width + 5\n    >    end\n    >  elseif key == string.byte('y') then\n    >    if view_settings.height > 0 then\n    >      view_settings.height = view_settings.height - 1\n    >    end\n    >  elseif key == string.byte('Y') then\n    >    if view_settings.height < h-2 then\n    >      view_settings.height = view_settings.height + 1\n    >    end\n    >  elseif key == string.byte('z') then\n    >    -- scroll to show the current zettel at top of screen\n    >    -- often has the effect of zooming in on its hierarchy\n    >    view_settings.first_zettel = current_zettel_id\n    >  end\n    >end\n- __teliva_timestamp:\n    >Sat Feb 12 15:11:15 2022\n  editz_update:\n    >function editz_update(window, prose, cursor, original_prose)\n    >  local key = window:getch()\n    >  local h, w = window:getmaxyx()\n    >  -- cursor movement\n    >  if key == curses.KEY_LEFT then\n    >    if cursor > 1 then\n    >      cursor = cursor-1\n    >    end\n    >  elseif key == curses.KEY_RIGHT then\n    >    if cursor <= #prose then\n    >      cursor = cursor+1\n    >    end\n    >  elseif key == curses.KEY_DOWN then\n    >    cursor = cursor_down(prose, cursor, w)\n    >  elseif key == curses.KEY_UP then\n    >    cursor = cursor_up(prose, cursor, w)\n    >  elseif key == curses.KEY_BACKSPACE or key == 8 or key == 127 then  -- ctrl-h, ctrl-?, delete\n    >    if cursor > 1 then\n    >      cursor = cursor-1\n    >      prose = prose:remove(cursor)\n    >    end\n    >  elseif key == 1 then  -- ctrl-a\n    >  elseif key == 12 then  -- ctrl-l\n    >  elseif key == 6 then  -- ctrl-f\n    >  elseif key == 2 then  -- ctrl-b\n    >  -- delete\n    >  elseif key == 11 then  -- ctrl-k\n    >  -- exit\n    >  elseif key == 5 then  -- ctrl-e\n    >    return true, prose, cursor\n    >  elseif key == 7 then  -- ctrl-g\n    >    return true, original_prose, cursor\n    >  -- insert\n    >  elseif key == 10 or (key >= 32 and key < 127) then\n    >    prose = prose:insert(string.char(key), cursor-1)\n    >    cursor = cursor+1\n    >  end\n    >  return false, prose, cursor\n    >end\n- __teliva_timestamp:\n    >Sat Feb 12 15:11:33 2022\n  editz:\n    >function editz(window)\n    >  local old_menu = menu\n    >  menu = {\n    >    {'^e', 'finish edit'},\n    >    {'^g', 'cancel edit'},\n    >    {'^a', '<<line'},\n    >    {'^b', '<word'},\n    >    {'^f', 'word>'},\n    >    {'^l', 'line>>'},\n    >    {'^k', 'del to line>>'},\n    >  }\n    >  local old_data = zettels[current_zettel_id].data:sub(1)\n    >  local top = (render_state.curr_h - 1) * (view_settings.height + view_settings.vmargin)\n    >  local bottom = top + view_settings.height\n    >  local left = (render_state.curr_w - 1) * (view_settings.width + view_settings.hmargin)\n    >  local right = left + view_settings.width\n    >  local cursor = zettels[current_zettel_id].data:len()+1\n    >  local quit = false\n    >  curses.curs_set(1)\n    >  while not quit do\n    >    editz_render(window, zettels[current_zettel_id].data, cursor, top, bottom, left, right)\n    >    quit, zettels[current_zettel_id].data, cursor = editz_update(window, zettels[current_zettel_id].data, cursor, old_data)\n    >  end\n    >  curses.curs_set(0)\n    >  menu = old_menu\n    >end\n- __teliva_timestamp:\n    >Sat Feb 12 15:55:10 2022\n  __teliva_note:\n    >editor: move to start of line, move/delete to end of line\n  editz_update:\n    >function editz_update(window, prose, cursor, original_prose)\n    >  local key = window:getch()\n    >  local h, w = window:getmaxyx()\n    >  -- cursor movement\n    >  if key == curses.KEY_LEFT then\n    >    if cursor > 1 then\n    >      cursor = cursor-1\n    >    end\n    >  elseif key == curses.KEY_RIGHT then\n    >    if cursor <= #prose then\n    >      cursor = cursor+1\n    >    end\n    >  elseif key == curses.KEY_DOWN then\n    >    cursor = cursor_down(prose, cursor, w)\n    >  elseif key == curses.KEY_UP then\n    >    cursor = cursor_up(prose, cursor, w)\n    >  elseif key == curses.KEY_BACKSPACE or key == 8 or key == 127 then  -- ctrl-h, ctrl-?, delete\n    >    if cursor > 1 then\n    >      cursor = cursor-1\n    >      prose = prose:remove(cursor)\n    >    end\n    >  elseif key == 1 then  -- ctrl-a\n    >    while cursor > 1 do\n    >      if prose[cursor-1] == '\\n' then break end\n    >      cursor = cursor-1\n    >    end\n    >  elseif key == 12 then  -- ctrl-l\n    >    local max = prose:len()\n    >    while cursor <= max and prose[cursor] ~= '\\n' do\n    >      cursor = cursor+1\n    >    end\n    >  elseif key == 6 then  -- ctrl-f\n    >  elseif key == 2 then  -- ctrl-b\n    >  -- delete\n    >  elseif key == 11 then  -- ctrl-k\n    >    while cursor <= prose:len() and prose[cursor] ~= '\\n' do\n    >      prose = prose:remove(cursor)\n    >    end\n    >  -- exit\n    >  elseif key == 5 then  -- ctrl-e\n    >    return true, prose, cursor\n    >  elseif key == 7 then  -- ctrl-g\n    >    return true, original_prose, cursor\n    >  -- insert\n    >  elseif key == 10 or (key >= 32 and key < 127) then\n    >    prose = prose:insert(string.char(key), cursor-1)\n    >    cursor = cursor+1\n    >  end\n    >  return false, prose, cursor\n    >end\n- __teliva_timestamp:\n    >Sat Feb 12 17:01:45 2022\n  __teliva_note:\n    >editor: word-movement shortcuts\n  editz_update:\n    >function editz_update(window, prose, cursor, original_prose)\n    >  local key = window:getch()\n    >  local h, w = window:getmaxyx()\n    >  -- cursor movement\n    >  if key == curses.KEY_LEFT then\n    >    if cursor > 1 then\n    >      cursor = cursor-1\n    >    end\n    >  elseif key == curses.KEY_RIGHT then\n    >    if cursor <= #prose then\n    >      cursor = cursor+1\n    >    end\n    >  elseif key == curses.KEY_DOWN then\n    >    cursor = cursor_down(prose, cursor, w)\n    >  elseif key == curses.KEY_UP then\n    >    cursor = cursor_up(prose, cursor, w)\n    >  elseif key == curses.KEY_BACKSPACE or key == 8 or key == 127 then  -- ctrl-h, ctrl-?, delete\n    >    if cursor > 1 then\n    >      cursor = cursor-1\n    >      prose = prose:remove(cursor)\n    >    end\n    >  elseif key == 1 then  -- ctrl-a\n    >    -- to start of line\n    >    while cursor > 1 do\n    >      if prose[cursor-1] == '\\n' then break end\n    >      cursor = cursor-1\n    >    end\n    >  elseif key == 12 then  -- ctrl-l\n    >    -- to end of line\n    >    local max = prose:len()\n    >    while cursor <= max and prose[cursor] ~= '\\n' do\n    >      cursor = cursor+1\n    >    end\n    >  elseif key == 6 then  -- ctrl-f\n    >    -- to next word\n    >    local max = prose:len()\n    >    while cursor <= max and prose[cursor]:match('%w') do\n    >      cursor = cursor+1\n    >    end\n    >    while cursor <= max and prose[cursor]:match('%W') do\n    >      cursor = cursor+1\n    >    end\n    >  elseif key == 2 then  -- ctrl-b\n    >    -- to previous word\n    >    if cursor > prose:len() then\n    >      cursor = prose:len()\n    >    end\n    >    while cursor > 1 and prose[cursor]:match('%W') do\n    >      cursor = cursor-1\n    >    end\n    >    while cursor > 1 and prose[cursor]:match('%w') do\n    >      cursor = cursor-1\n    >    end\n    >  -- delete\n    >  elseif key == 11 then  -- ctrl-k\n    >    while cursor <= prose:len() and prose[cursor] ~= '\\n' do\n    >      prose = prose:remove(cursor)\n    >    end\n    >  -- exit\n    >  elseif key == 5 then  -- ctrl-e\n    >    return true, prose, cursor\n    >  elseif key == 7 then  -- ctrl-g\n    >    return true, original_prose, cursor\n    >  -- insert\n    >  elseif key == 10 or (key >= 32 and key < 127) then\n    >    prose = prose:insert(string.char(key), cursor-1)\n    >    cursor = cursor+1\n    >  end\n    >  return false, prose, cursor\n    >end\n- __teliva_timestamp:\n    >Sat Feb 12 17:12:27 2022\n  editz_render:\n    >function editz_render(window, s, cursor, top, minbottom, left, right)\n    >  local h, w = window:getmaxyx()\n    >  local cursor_y, cursor_x = 0, 0\n    >  window:attrset(curses.color_pair(1)) -- 1 is the color combination for the current zettel\n    >  for y=top,minbottom-1 do\n    >    for x=left,right-1 do\n    >      window:mvaddch(y, x, ' ')\n    >    end\n    >  end\n    >  for x=left,right-1 do\n    >    window:mvaddch(minbottom, x, ' ')\n    >  end\n    >  local y, x = top, left + 1  -- left padding; TODO: indent\n    >  window:mvaddstr(y, x, '')\n    >  for i=1,s:len() do\n    >    if i == cursor then\n    >      cursor_y = y\n    >      cursor_x = x\n    >    end\n    >    if s[i] ~= '\\n' then\n    >      window:addstr(s[i])\n    >      x = x + 1\n    >      if x >= right then\n    >        y = y + 1\n    >        if y >= h-2 then return end\n    >        x = left + 1  -- left padding; TODO: indent\n    >        window:mvaddstr(y, x, '')\n    >      end\n    >    else\n    >      for col=x+1,right-1 do window:addch(' '); end\n    >      x = left\n    >      y = y + 1\n    >      if y >= h-2 then return end\n    >      window:mvaddstr(y, x, '')\n    >      for col=x,right-1 do window:addch(' '); end\n    >      x = left + 1  -- left padding; TODO: indent\n    >      window:mvaddstr(y, x, '')\n    >    end\n    >  end\n    >  if cursor_y == 0 and cursor_x == 0 then\n    >    cursor_y = y\n    >    cursor_x = x\n    >  end\n    >  window:mvaddstr(cursor_y, cursor_x, '')\n    >end\n- __teliva_timestamp:\n    >Sat Feb 12 17:15:15 2022\n  render_state:\n    >-- some information about what's been drawn on screen\n    >render_state = {\n    >  -- where the current zettel is, in units of zettels\n    >  curr_h = 1,\n    >  curr_w = 1,\n    >  -- what zettel is at each position on screen, in units of zettels\n    >  hw2id = {},\n    >  -- list of zettels currently displayed\n    >  displayed = {},\n    >}\n- __teliva_timestamp:\n    >Sat Feb 12 17:16:20 2022\n  render:\n    >function render(window)\n    >  window:clear()\n    >  local lines, cols = window:getmaxyx()\n    >  local bg=3\n    >  local y, x = 0, 0 -- units of characters (0-based)\n    >  local w, h = 1, 1 -- units of zettels (1-based)\n    >  -- render zettels depth-first, while tracking relative positions\n    >  local done = {}\n    >  local inprogress = {{id=view_settings.first_zettel,depth=0,edge=''}}\n    >  render_state.wh2id = {{}}\n    >  render_state.displayed = {}\n    >  while #inprogress > 0 do\n    >    local curr = table.remove(inprogress)\n    >    if not done[curr.id] then\n    >      done[curr.id] = true\n    >      render_state.displayed[curr.id] = true\n    >      table.insert(render_state.wh2id[w], curr.id)\n    >      local zettel = zettels[curr.id]\n    >      if curr.id == current_zettel_id then\n    >        render_state.curr_w = w\n    >        render_state.curr_h = h\n    >      end\n    >      local currbg = (curr.id == current_zettel_id) and 1 or bg  -- 1 is the color pair for the current zettel\n    >      render_zettel(window, currbg, curr.depth * view_settings.indent, curr.edge, y, x, zettel)\n    >      if zettel.next then table.insert(inprogress, {id=zettel.next, depth=curr.depth, edge='|'}) end\n    >      if zettel.child then table.insert(inprogress, {id=zettel.child, depth=curr.depth+1, edge='\\\\'}) end\n    >      if zettel.crosslinks then\n    >        for relation, target in pairs(zettel.crosslinks) do\n    >          table.insert(inprogress, {id=target, depth=curr.depth+1, edge=relation})\n    >        end\n    >      end\n    >      bg = 8 - bg  -- toggle between color pairs 3 and 5\n    >      y = y + view_settings.height + view_settings.vmargin\n    >      h = h + 1\n    >      if y + view_settings.height > lines then\n    >        y = 0\n    >        h = 1\n    >        x = x + view_settings.width + view_settings.hmargin\n    >        w = w + 1\n    >        if x + view_settings.width > cols then break end\n    >        table.insert(render_state.wh2id, {})\n    >      end\n    >    end\n    >  end\n    >  window:mvaddstr(lines-1, 0, '')\n    >  bg = 3\n    >  x = 0\n    >  for i=1,3 do\n    >    local zettel = nil\n    >    if i == 1 and stash then\n    >      zettel = zettels[stash]\n    >    end\n    >    render_zettel(window, bg, 0, '', lines-1, x, zettel)\n    >    bg = 8 - bg  -- toggle between color pairs 3 and 5\n    >    x = x + view_settings.width + view_settings.hmargin\n    >  end\n    >  window:refresh()\n    >end\n- __teliva_timestamp:\n    >Sat Feb 12 17:18:34 2022\n  __teliva_note:\n    >scroll as needed when moving along the graph\n  update:\n    >function update(window)\n    >  local key = window:getch()\n    >  local h, w = window:getmaxyx()\n    >  local curr = zettels[current_zettel_id]\n    >  assert(curr, string.format('cursor fell off the edge of the world: %s', type(current_zettel_id)))\n    >  -- move along the graph\n    >  if key == string.byte('j') then\n    >    if curr.child then\n    >      current_zettel_id = curr.child\n    >    elseif curr.next then\n    >      current_zettel_id = curr.next\n    >    elseif curr.parent and zettels[curr.parent].next then\n    >      current_zettel_id = zettels[curr.parent].next\n    >    end\n    >    -- scroll if necessary\n    >    if not render_state.displayed[current_zettel_id] then\n    >      view_settings.first_zettel = current_zettel_id\n    >    end\n    >  elseif key == string.byte('k') then\n    >    if curr.parent then\n    >      current_zettel_id = curr.parent\n    >    end\n    >    -- scroll if necessary\n    >    if not render_state.displayed[current_zettel_id] then\n    >      view_settings.first_zettel = current_zettel_id\n    >    end\n    >  elseif key == string.byte('h') then\n    >    if curr.prev then\n    >      current_zettel_id = curr.prev\n    >    elseif curr.parent then\n    >      current_zettel_id = curr.parent\n    >    end\n    >    -- scroll if necessary\n    >    if not render_state.displayed[current_zettel_id] then\n    >      view_settings.first_zettel = current_zettel_id\n    >    end\n    >  elseif key == string.byte('l') then\n    >    if curr.next then\n    >      current_zettel_id = curr.next\n    >    elseif curr.parent and zettels[curr.parent].next then\n    >      current_zettel_id = zettels[curr.parent].next\n    >    end\n    >    -- scroll if necessary\n    >    if not render_state.displayed[current_zettel_id] then\n    >      view_settings.first_zettel = current_zettel_id\n    >    end\n    >  -- move along the screen\n    >  elseif key == curses.KEY_UP then\n    >    if render_state.curr_h > 1 then\n    >      current_zettel_id = render_state.wh2id[render_state.curr_w][render_state.curr_h - 1]\n    >    end\n    >  elseif key == curses.KEY_DOWN then\n    >    if render_state.wh2id[render_state.curr_w][render_state.curr_h + 1] then\n    >      current_zettel_id = render_state.wh2id[render_state.curr_w][render_state.curr_h + 1]\n    >    end\n    >  elseif key == curses.KEY_LEFT then\n    >    if render_state.curr_w > 1 then\n    >      current_zettel_id = render_state.wh2id[render_state.curr_w - 1][render_state.curr_h]\n    >    end\n    >  elseif key == curses.KEY_RIGHT then\n    >    if render_state.wh2id[render_state.curr_w + 1] and render_state.wh2id[render_state.curr_w + 1][render_state.curr_h] then\n    >      current_zettel_id = render_state.wh2id[render_state.curr_w + 1][render_state.curr_h]\n    >    end\n    >  -- mutations\n    >  elseif key == string.byte('e') then\n    >    editz(window)\n    >  elseif key == string.byte('a') then\n    >    -- insert sibling after\n    >    local old = curr.next\n    >    curr.next = new_id()\n    >    local new = zettels[curr.next]\n    >    new.data = ''\n    >    new.next = old\n    >    new.prev = current_zettel_id\n    >    if old then\n    >      zettels[old].prev = curr.next\n    >      assert(curr.parent == zettels[old].parent, 'siblings should have same parent')\n    >    end\n    >    new.parent = curr.parent\n    >    current_zettel_id = curr.next\n    >    render(window) -- recompute render_state\n    >    editz(window)\n    >  elseif key == string.byte('b') then\n    >    -- insert sibling before\n    >    local old = curr.prev\n    >    curr.prev = new_id()\n    >    local new = zettels[curr.prev]\n    >    new.data = ''\n    >    new.prev = old\n    >    new.next = current_zettel_id\n    >    if old then\n    >      zettels[old].next = curr.prev\n    >      assert(curr.parent == zettels[old].parent, 'siblings should have same parent')\n    >    end\n    >    new.parent = curr.parent\n    >    current_zettel_id = curr.prev\n    >    render(window) -- recompute render_state\n    >    editz(window)\n    >  elseif key == string.byte('c') then\n    >    -- insert child\n    >    local old = curr.child\n    >    curr.child = new_id()\n    >    local new = zettels[curr.child]\n    >    new.data = ''\n    >    new.next = old\n    >    if old then\n    >      assert(zettels[old].prev == nil, \"first child shouldn't have a previous sibling\")\n    >      zettels[old].prev = curr.child\n    >    end\n    >    new.parent = current_zettel_id\n    >    current_zettel_id = curr.child\n    >    render(window) -- recompute render_state\n    >    editz(window)\n    >  -- cross-links\n    >  elseif key == string.byte('s') then\n    >    -- save zettel to a stash\n    >    stash = current_zettel_id\n    >  elseif key == string.byte('t') then\n    >    -- cross-link a zettel bidirectionally with what's on the stash\n    >    local insert_crosslink =\n    >      function(a, rel, b_id)\n    >        if a.crosslinks == nil then\n    >          a.crosslinks = {}\n    >        end\n    >        a.crosslinks[rel] = b_id\n    >      end\n    >    insert_crosslink(curr, 'a', stash)\n    >    insert_crosslink(zettels[stash], 'a', current_zettel_id)\n    >    stash = nil\n    >  -- view settings\n    >  elseif key == string.byte('x') then\n    >    if view_settings.width > 5 then\n    >      view_settings.width = view_settings.width - 5\n    >    end\n    >  elseif key == string.byte('X') then\n    >    if view_settings.width < w-5 then\n    >      view_settings.width = view_settings.width + 5\n    >    end\n    >  elseif key == string.byte('y') then\n    >    if view_settings.height > 0 then\n    >      view_settings.height = view_settings.height - 1\n    >    end\n    >  elseif key == string.byte('Y') then\n    >    if view_settings.height < h-2 then\n    >      view_settings.height = view_settings.height + 1\n    >    end\n    >  elseif key == string.byte('z') then\n    >    -- scroll to show the current zettel at top of screen\n    >    -- often has the effect of zooming in on its hierarchy\n    >    view_settings.first_zettel = current_zettel_id\n    >  end\n    >end\n- __teliva_timestamp:\n    >Sat Feb 12 17:23:33 2022\n  __teliva_note:\n    >editor 'k' shortcut: fall back to next sibling if needed\n    >\n    >Now we should be able to navigate either with j/k or h/l.\n  update:\n    >function update(window)\n    >  local key = window:getch()\n    >  local h, w = window:getmaxyx()\n    >  local curr = zettels[current_zettel_id]\n    >  assert(curr, string.format('cursor fell off the edge of the world: %s', type(current_zettel_id)))\n    >  -- move along the graph\n    >  if key == string.byte('j') then\n    >    -- child or next sibling\n    >    if curr.child then\n    >      current_zettel_id = curr.child\n    >    elseif curr.next then\n    >      current_zettel_id = curr.next\n    >    elseif curr.parent and zettels[curr.parent].next then\n    >      current_zettel_id = zettels[curr.parent].next\n    >    end\n    >    -- scroll if necessary\n    >    if not render_state.displayed[current_zettel_id] then\n    >      view_settings.first_zettel = current_zettel_id\n    >    end\n    >  elseif key == string.byte('k') then\n    >    -- parent or previous sibling\n    >    if curr.parent then\n    >      current_zettel_id = curr.parent\n    >    elseif curr.prev then\n    >      current_zettel_id = curr.prev\n    >    end\n    >    -- scroll if necessary\n    >    if not render_state.displayed[current_zettel_id] then\n    >      view_settings.first_zettel = current_zettel_id\n    >    end\n    >  elseif key == string.byte('h') then\n    >    -- previous sibling or parent\n    >    if curr.prev then\n    >      current_zettel_id = curr.prev\n    >    elseif curr.parent then\n    >      current_zettel_id = curr.parent\n    >    end\n    >    -- scroll if necessary\n    >    if not render_state.displayed[current_zettel_id] then\n    >      view_settings.first_zettel = current_zettel_id\n    >    end\n    >  elseif key == string.byte('l') then\n    >    -- next sibling or next sibling of parent\n    >    if curr.next then\n    >      current_zettel_id = curr.next\n    >    elseif curr.parent and zettels[curr.parent].next then\n    >      current_zettel_id = zettels[curr.parent].next\n    >    end\n    >    -- scroll if necessary\n    >    if not render_state.displayed[current_zettel_id] then\n    >      view_settings.first_zettel = current_zettel_id\n    >    end\n    >  -- move along the screen\n    >  elseif key == curses.KEY_UP then\n    >    if render_state.curr_h > 1 then\n    >      current_zettel_id = render_state.wh2id[render_state.curr_w][render_state.curr_h - 1]\n    >    end\n    >  elseif key == curses.KEY_DOWN then\n    >    if render_state.wh2id[render_state.curr_w][render_state.curr_h + 1] then\n    >      current_zettel_id = render_state.wh2id[render_state.curr_w][render_state.curr_h + 1]\n    >    end\n    >  elseif key == curses.KEY_LEFT then\n    >    if render_state.curr_w > 1 then\n    >      current_zettel_id = render_state.wh2id[render_state.curr_w - 1][render_state.curr_h]\n    >    end\n    >  elseif key == curses.KEY_RIGHT then\n    >    if render_state.wh2id[render_state.curr_w + 1] and render_state.wh2id[render_state.curr_w + 1][render_state.curr_h] then\n    >      current_zettel_id = render_state.wh2id[render_state.curr_w + 1][render_state.curr_h]\n    >    end\n    >  -- mutations\n    >  elseif key == string.byte('e') then\n    >    editz(window)\n    >  elseif key == string.byte('a') then\n    >    -- insert sibling after\n    >    local old = curr.next\n    >    curr.next = new_id()\n    >    local new = zettels[curr.next]\n    >    new.data = ''\n    >    new.next = old\n    >    new.prev = current_zettel_id\n    >    if old then\n    >      zettels[old].prev = curr.next\n    >      assert(curr.parent == zettels[old].parent, 'siblings should have same parent')\n    >    end\n    >    new.parent = curr.parent\n    >    current_zettel_id = curr.next\n    >    render(window) -- recompute render_state\n    >    editz(window)\n    >  elseif key == string.byte('b') then\n    >    -- insert sibling before\n    >    local old = curr.prev\n    >    curr.prev = new_id()\n    >    local new = zettels[curr.prev]\n    >    new.data = ''\n    >    new.prev = old\n    >    new.next = current_zettel_id\n    >    if old then\n    >      zettels[old].next = curr.prev\n    >      assert(curr.parent == zettels[old].parent, 'siblings should have same parent')\n    >    end\n    >    new.parent = curr.parent\n    >    current_zettel_id = curr.prev\n    >    render(window) -- recompute render_state\n    >    editz(window)\n    >  elseif key == string.byte('c') then\n    >    -- insert child\n    >    local old = curr.child\n    >    curr.child = new_id()\n    >    local new = zettels[curr.child]\n    >    new.data = ''\n    >    new.next = old\n    >    if old then\n    >      assert(zettels[old].prev == nil, \"first child shouldn't have a previous sibling\")\n    >      zettels[old].prev = curr.child\n    >    end\n    >    new.parent = current_zettel_id\n    >    current_zettel_id = curr.child\n    >    render(window) -- recompute render_state\n    >    editz(window)\n    >  -- cross-links\n    >  elseif key == string.byte('s') then\n    >    -- save zettel to a stash\n    >    stash = current_zettel_id\n    >  elseif key == string.byte('t') then\n    >    -- cross-link a zettel bidirectionally with what's on the stash\n    >    local insert_crosslink =\n    >      function(a, rel, b_id)\n    >        if a.crosslinks == nil then\n    >          a.crosslinks = {}\n    >        end\n    >        a.crosslinks[rel] = b_id\n    >      end\n    >    insert_crosslink(curr, 'a', stash)\n    >    insert_crosslink(zettels[stash], 'a', current_zettel_id)\n    >    stash = nil\n    >  -- view settings\n    >  elseif key == string.byte('x') then\n    >    if view_settings.width > 5 then\n    >      view_settings.width = view_settings.width - 5\n    >    end\n    >  elseif key == string.byte('X') then\n    >    if view_settings.width < w-5 then\n    >      view_settings.width = view_settings.width + 5\n    >    end\n    >  elseif key == string.byte('y') then\n    >    if view_settings.height > 0 then\n    >      view_settings.height = view_settings.height - 1\n    >    end\n    >  elseif key == string.byte('Y') then\n    >    if view_settings.height < h-2 then\n    >      view_settings.height = view_settings.height + 1\n    >    end\n    >  elseif key == string.byte('z') then\n    >    -- scroll to show the current zettel at top of screen\n    >    -- often has the effect of zooming in on its hierarchy\n    >    view_settings.first_zettel = current_zettel_id\n    >  end\n    >end\n- __teliva_timestamp:\n    >Sat Feb 12 17:27:18 2022\n  menu:\n    >-- To show app-specific hotkeys in the menu bar, add hotkey/command\n    >-- arrays of strings to the menu array.\n    >menu = {\n    >  {'a,b,c', 'insert'},\n    >  {'e', 'edit'},\n    >  {'j,k,l,h', 'move'},\n    >  {'<', 'back'},\n    >  {'x,X,y,Y', 'resize'},\n    >  {'s', 'stash'},\n    >  {'t', 'link with stash'},\n    >  {'z', 'scroll'},\n    >}\n- __teliva_timestamp:\n    >Sat Feb 12 17:57:15 2022\n  update:\n    >function update(window)\n    >  local key = window:getch()\n    >  local h, w = window:getmaxyx()\n    >  local curr = zettels[current_zettel_id]\n    >  assert(curr, string.format('cursor fell off the edge of the world: %s', type(current_zettel_id)))\n    >  -- read from or write to render_state.history\n    >  if key == string.byte('<') then\n    >    -- previous zettel moved to\n    >    -- does NOT undo mutations\n    >    if #render_state.history > 0 then\n    >      local previous_state = render_state.history[#render_state.history]\n    >      view_settings.first_zettel = previous_state.first_zettel\n    >      current_zettel_id = previous_state.cursor\n    >      table.remove(render_state.history)\n    >    end\n    >    return\n    >  end\n    >  if key ~= string.byte('e') then\n    >    table.insert(render_state.history, {first_zettel=view_settings.first_zettel, cursor=current_zettel_id})\n    >  end\n    >  -- move along the graph\n    >  if key == string.byte('j') then\n    >    -- child or next sibling\n    >    if curr.child then\n    >      current_zettel_id = curr.child\n    >    elseif curr.next then\n    >      current_zettel_id = curr.next\n    >    elseif curr.parent and zettels[curr.parent].next then\n    >      current_zettel_id = zettels[curr.parent].next\n    >    end\n    >    -- scroll if necessary\n    >    if not render_state.displayed[current_zettel_id] then\n    >      view_settings.first_zettel = current_zettel_id\n    >    end\n    >  elseif key == string.byte('k') then\n    >    -- parent or previous sibling\n    >    if curr.parent then\n    >      current_zettel_id = curr.parent\n    >    elseif curr.prev then\n    >      current_zettel_id = curr.prev\n    >    end\n    >    -- scroll if necessary\n    >    if not render_state.displayed[current_zettel_id] then\n    >      view_settings.first_zettel = current_zettel_id\n    >    end\n    >  elseif key == string.byte('h') then\n    >    -- previous sibling or parent\n    >    if curr.prev then\n    >      current_zettel_id = curr.prev\n    >    elseif curr.parent then\n    >      current_zettel_id = curr.parent\n    >    end\n    >    -- scroll if necessary\n    >    if not render_state.displayed[current_zettel_id] then\n    >      view_settings.first_zettel = current_zettel_id\n    >    end\n    >  elseif key == string.byte('l') then\n    >    -- next sibling or next sibling of parent\n    >    if curr.next then\n    >      current_zettel_id = curr.next\n    >    elseif curr.parent and zettels[curr.parent].next then\n    >      current_zettel_id = zettels[curr.parent].next\n    >    end\n    >    -- scroll if necessary\n    >    if not render_state.displayed[current_zettel_id] then\n    >      view_settings.first_zettel = current_zettel_id\n    >    end\n    >  -- move along the screen\n    >  elseif key == curses.KEY_UP then\n    >    if render_state.curr_h > 1 then\n    >      current_zettel_id = render_state.wh2id[render_state.curr_w][render_state.curr_h - 1]\n    >    end\n    >  elseif key == curses.KEY_DOWN then\n    >    if render_state.wh2id[render_state.curr_w][render_state.curr_h + 1] then\n    >      current_zettel_id = render_state.wh2id[render_state.curr_w][render_state.curr_h + 1]\n    >    end\n    >  elseif key == curses.KEY_LEFT then\n    >    if render_state.curr_w > 1 then\n    >      current_zettel_id = render_state.wh2id[render_state.curr_w - 1][render_state.curr_h]\n    >    end\n    >  elseif key == curses.KEY_RIGHT then\n    >    if render_state.wh2id[render_state.curr_w + 1] and render_state.wh2id[render_state.curr_w + 1][render_state.curr_h] then\n    >      current_zettel_id = render_state.wh2id[render_state.curr_w + 1][render_state.curr_h]\n    >    end\n    >  -- mutations\n    >  elseif key == string.byte('e') then\n    >    editz(window)\n    >  elseif key == string.byte('a') then\n    >    -- insert sibling after\n    >    local old = curr.next\n    >    curr.next = new_id()\n    >    local new = zettels[curr.next]\n    >    new.data = ''\n    >    new.next = old\n    >    new.prev = current_zettel_id\n    >    if old then\n    >      zettels[old].prev = curr.next\n    >      assert(curr.parent == zettels[old].parent, 'siblings should have same parent')\n    >    end\n    >    new.parent = curr.parent\n    >    current_zettel_id = curr.next\n    >    render(window) -- recompute render_state\n    >    editz(window)\n    >  elseif key == string.byte('b') then\n    >    -- insert sibling before\n    >    local old = curr.prev\n    >    curr.prev = new_id()\n    >    local new = zettels[curr.prev]\n    >    new.data = ''\n    >    new.prev = old\n    >    new.next = current_zettel_id\n    >    if old then\n    >      zettels[old].next = curr.prev\n    >      assert(curr.parent == zettels[old].parent, 'siblings should have same parent')\n    >    end\n    >    new.parent = curr.parent\n    >    current_zettel_id = curr.prev\n    >    render(window) -- recompute render_state\n    >    editz(window)\n    >  elseif key == string.byte('c') then\n    >    -- insert child\n    >    local old = curr.child\n    >    curr.child = new_id()\n    >    local new = zettels[curr.child]\n    >    new.data = ''\n    >    new.next = old\n    >    if old then\n    >      assert(zettels[old].prev == nil, \"first child shouldn't have a previous sibling\")\n    >      zettels[old].prev = curr.child\n    >    end\n    >    new.parent = current_zettel_id\n    >    current_zettel_id = curr.child\n    >    render(window) -- recompute render_state\n    >    editz(window)\n    >  -- cross-links\n    >  elseif key == string.byte('s') then\n    >    -- save zettel to a stash\n    >    stash = current_zettel_id\n    >  elseif key == string.byte('t') then\n    >    -- cross-link a zettel bidirectionally with what's on the stash\n    >    local insert_crosslink =\n    >      function(a, rel, b_id)\n    >        if a.crosslinks == nil then\n    >          a.crosslinks = {}\n    >        end\n    >        a.crosslinks[rel] = b_id\n    >      end\n    >    insert_crosslink(curr, 'a', stash)\n    >    insert_crosslink(zettels[stash], 'a', current_zettel_id)\n    >    stash = nil\n    >  -- view settings\n    >  elseif key == string.byte('x') then\n    >    if view_settings.width > 5 then\n    >      view_settings.width = view_settings.width - 5\n    >    end\n    >  elseif key == string.byte('X') then\n    >    if view_settings.width < w-5 then\n    >      view_settings.width = view_settings.width + 5\n    >    end\n    >  elseif key == string.byte('y') then\n    >    if view_settings.height > 0 then\n    >      view_settings.height = view_settings.height - 1\n    >    end\n    >  elseif key == string.byte('Y') then\n    >    if view_settings.height < h-2 then\n    >      view_settings.height = view_settings.height + 1\n    >    end\n    >  elseif key == string.byte('z') then\n    >    -- scroll to show the current zettel at top of screen\n    >    -- often has the effect of zooming in on its hierarchy\n    >    view_settings.first_zettel = current_zettel_id\n    >  end\n    >end\n- __teliva_timestamp:\n    >Sat Feb 12 17:58:01 2022\n  __teliva_note:\n    >make cursor movements less risky using a back button '<'\n  render_state:\n    >-- some information about what's been drawn on screen\n    >-- not saved between app restarts\n    >render_state = {\n    >  -- where the current zettel is, in units of zettels\n    >  curr_h = 1,\n    >  curr_w = 1,\n    >  -- what zettel is at each position on screen, in units of zettels\n    >  hw2id = {},\n    >  -- list of zettels currently displayed\n    >  displayed = {},\n    >  -- history of screen render state\n    >  history = {},  -- elems {first_zettel=view_settings.first_zettel, cursor=current_zettel_id}\n    >}\n- __teliva_timestamp:\n    >Thu Feb 17 20:15:14 2022\n  doc:blurb:\n    >A rudimentary Zettelkasten app trying to hew very close to the original analog setup, as described by abramdemski:\n    >\n    >https://www.lesswrong.com/posts/NfdHG6oHBJ8Qxc26s/the-zettelkasten-method-1\n    >\n    >The key attributes of Zettelkasten seem to be:\n    >- notes organized in small fragments called 'cards' that can't hold much text\n    >- a tree-based organization using sibling and child cards, with the ability to insert children and siblings to any card, any time\n    >- ability to cross-link any card to any other, turning the tree into a graph (but still with a strong sense of hierarchy)\n    >\n    >zet.tlv satisfies these properties, but isn't very intuitive or usable yet. Contributions appreciated.\n- __teliva_timestamp:\n    >Mon Mar  7 07:50:32 2022\n  main:\n    >function main()\n    >  init_colors()\n    >  curses.curs_set(0)  -- hide cursor except when editing\n    >\n    >  local infile = start_reading(nil, 'zet')\n    >  if infile then\n    >    read_zettels(infile)\n    >  end\n    >  current_zettel_id = zettels.root  -- cursor\n    >  view_settings.first_zettel = zettels.root  -- start rendering here\n    >\n    >  while true do\n    >    render(Window)\n    >    update(Window)\n    >\n    >    -- save zettels, but hold on to previous state on disk\n    >    -- until last possible second\n    >    local outfile = io.open('teliva_tmp', 'w')\n    >    if outfile then\n    >      write_zettels(outfile)\n    >      local status, message = os.rename('teliva_tmp', 'zet')\n    >      assert(status, message)  -- unceremoniously abort, but we hopefully only lost a little\n    >    end\n    >    -- TODO: what if io.open failed for a non-sandboxing related reason?!\n    >    -- We could silently fail to save.\n    >  end\n    >end\n- __teliva_timestamp:\n    >Mon Mar  7 07:51:06 2022\n  __teliva_note:\n    >switch to new file API for reading\n  read_zettels:\n    >function read_zettels(infile)\n    >  zettels = jsonf.decode(infile)\n    >end\n- __teliva_timestamp:\n    >Mon Mar  7 10:31:27 2022\n  main:\n    >function main()\n    >  init_colors()\n    >  curses.curs_set(0)  -- hide cursor except when editing\n    >\n    >  -- load any saved zettels\n    >  local infile = start_reading(nil, 'zet')\n    >  if infile then\n    >    read_zettels(infile)\n    >  end\n    >  current_zettel_id = zettels.root  -- cursor\n    >  view_settings.first_zettel = zettels.root  -- start rendering here\n    >\n    >  while true do\n    >    render(Window)\n    >    update(Window)\n    >\n    >    -- save zettels\n    >    local outfile = start_writing(nil, 'zet')\n    >    if outfile then\n    >      write_zettels(outfile)\n    >    end\n    >    -- TODO: what if io.open failed for a non-sandboxing related reason?!\n    >    -- We could silently fail to save.\n    >  end\n    >end\n- __teliva_timestamp:\n    >Mon Mar  7 10:32:08 2022\n  __teliva_note:\n    >switch to new file API for writing\n  write_zettels:\n    >function write_zettels(outfile)\n    >  outfile.write(json.encode(zettels))\n    >  outfile.close()\n    >end\n- __teliva_timestamp:\n    >Thu Mar 10 04:21:28 2022\n  render_zettel:\n    >function render_zettel(window, bg, indent, edge_label, starty, startx, zettel)\n    >  window:attrset(curses.color_pair(bg))\n    >  for y=0,view_settings.height-1 do\n    >    for x=0,view_settings.width-1 do\n    >      window:mvaddch(y+starty, x+startx, ' ')\n    >    end\n    >  end\n    >  if indent >= 2 then  -- need at least 2 spaces to be able to print edge_label\n    >    window:attrset(curses.color_pair(bg+1))  -- go from zettel color to its edge color\n    >    window:mvaddstr(starty, startx+indent-1, edge_label)\n    >    window:attrset(curses.color_pair(bg))\n    >  end\n    >  local y, x = 0, indent+1\n    >  local data = ''\n    >  if zettel then\n    >    data = zettel.data\n    >  end\n    >  for i=1,#data do\n    >    local c = data[i]\n    >    if c == '\\n' then\n    >      y = y+1\n    >      x = indent+1\n    >    else\n    >      window:mvaddstr(y+starty, x+startx, c)\n    >      x = x+1\n    >      if x >= startx + view_settings.width then\n    >        y = y+1\n    >        x = indent+1\n    >      end\n    >    end\n    >    if y >= view_settings.height then\n    >      break\n    >    end\n    >  end\n    >end\n    >\n    >function test_render_zettel_single_line_topleft()\n    >  local w = window{scr=scr{h=5, w=10}}\n    >  render_zettel(w, 34, 1,  -- color 34, indent 1\n    >                   '*', 1, 1, -- startx, starty\n    >                   {data='abc'})\n    >  check_screen(w,    '  abc     '..\n    >                     '          '..\n    >                     '          '..\n    >                     '          '..\n    >                     '          ',\n    >               'render_zettel: single line zettel from top-left of screen')\n    >  -- entire width is used by the single zettel\n    >  -- column 1 = margin, column 2 = indent\n    >  check_color(w, 34, '##########'..\n    >                     '##########'..\n    >                     '##########'..\n    >                     '          '..\n    >                     '          ',\n    >              'render_zettel: single line zettel from top-left of screen, background')\n    >end\n    >\n    >function test_render_zettel_from_middle_of_screen()\n    >  local w = window{scr=scr{h=5, w=10}}\n    >  render_zettel(w, 34, 1, '*', 3, 4, {data='abc'})  -- startx=3, starty=4\n    >  check_screen(w,    '          '..\n    >                     '          '..\n    >                     '     abc  '..\n    >                     '          '..\n    >                     '          ',\n    >               'render_zettel from middle of screen')\n    >  check_color(w, 34, '          '..\n    >                     '          '..\n    >                     '   #######'..\n    >                     '   #######'..\n    >                     '   #######',\n    >               'render_zettel from middle of screen, background')\n    >end\n    >\n    >function test_render_zettel_indented_prints_edge_label()\n    >  local w = window{scr=scr{h=5, w=10}}\n    >  render_zettel(w, 34, 2, '*', 3, 4, {data='abc'})  -- startx=3, starty=4\n    >  check_screen(w,    '          '..\n    >                     '          '..\n    >                     '    * abc '..\n    >                     '          '..\n    >                     '          ',\n    >               'render_zettel: indent >= 2 prints edge label')\n    >end\n    >\n    >function test_render_zettel_crops_long_lines()\n    >  local w = window{scr=scr{h=5, w=10}}\n    >  render_zettel(w, 34, 2, '*', 3, 4, {data='abc def'})  -- startx=3, starty=4\n    >  check_screen(w,    '          '..\n    >                     '          '..\n    >                     '    * abc '..\n    >                     '          '..\n    >                     '          ',\n    >               'render_zettel: crops long lines')\n    >end\n    >\n    >function test_render_zettel_multiple_lines()\n    >  local w = window{scr=scr{h=5, w=10}}\n    >  render_zettel(w, 34, 2, '*', 3, 4, {data='abc\\ndef'})  -- startx=3, starty=4\n    >  check_screen(w,    '          '..\n    >                     '          '..\n    >                     '    * abc '..\n    >                     '      def '..\n    >                     '          ',\n    >               'render_zettel: multiple lines')\n    >end\n    >\n    >function test_render_zettel_truncates_extra_lines()\n    >  local w = window{scr=scr{h=5, w=10}}\n    >  render_zettel(w, 34, 2, '*', 3, 4, {data='a\\nb\\nc\\nd'})  -- startx=3, starty=4\n    >  check_screen(w,    '          '..\n    >                     '          '..\n    >                     '    * a   '..\n    >                     '      b   '..\n    >                     '      c   ',\n    >               'render_zettel: truncates extra lines')\n    >end\n"
  }
]