1
1
mirror of https://git.shadowkat.net/izaya/OC-PsychOS2.git synced 2024-11-22 03:54:20 -05:00

Compare commits

...

8 Commits

Author SHA1 Message Date
XeonSquared
b3cfeb13ec remove replaced kernel modules 2020-05-12 12:08:01 +10:00
XeonSquared
b4ee8ed8a6 point build.sh to the right finddesc 2020-05-12 12:04:47 +10:00
XeonSquared
4f3bac551e updated finddesc to use the new doc library, allowing type annotations and building a directory of documentation 2020-05-12 12:01:44 +10:00
XeonSquared
a917016a66 fix type annotation in event.ignore 2020-05-12 11:44:05 +10:00
XeonSquared
d47a0748bd added type annotations to documentation for various libraries 2020-05-12 10:57:13 +10:00
Izaya
405ee6408d Merge pull request 'Fix the build process so that it works on the Bash shell for Git on Windows and fix markdown output' (#2) from Skye/OC-PsychOS2:git-windows-bash-fixes into master
Doesn't seem to break anything, though I wish Microsoft would fix their filesystems.

Markdown changes are a nice touch, though they'll be replaced soon.

Might think about using #!/usr/bin/env for Lua at some point.
2020-05-12 10:55:37 +10:00
Skye M
e09650276a Improve markdown output from finddesc.lua
* It didn't add spaces after the ## or #, which made them not work as headings in some renderers, so I fixed this.
* I then made it add newlines to make it look nicer without being rendered.
2020-05-12 01:49:51 +01:00
Skye M
daa2975fd6 Fix the build process so that it works on the Bash shell for Git on Windows
1. Made the Lua thing used be an optional variable, so it works for different Lua versions and locations

2. Made it work better with windows filesystems being weird with trailing dots.
2020-05-11 20:09:41 +01:00
14 changed files with 128 additions and 399 deletions

3
.gitignore vendored
View File

@ -1,5 +1,4 @@
apidoc.md
apidoc.html
*.cpio
*.af
/target
/doc

View File

@ -1,10 +1,13 @@
#!/bin/sh
#!/bin/bash
LUA=${LUA:-lua}
rm -r target/*
mkdir target &>/dev/null
lua luapreproc.lua module/init.lua target/init.lua
$LUA luapreproc.lua module/init.lua target/init.lua
echo _OSVERSION=\"PsychOS 2.0a2-$(git rev-parse --short HEAD)\" > target/version.lua
cat target/version.lua target/init.lua > target/tinit.lua
mv target/tinit.lua target/init.lua
cp -r service/ lib/ cfg/ target/
lua finddesc.lua $(find module/ -type f) $(find lib/ -type f) > apidoc.md
rm target/version.lua
rm -r doc/
$LUA finddesc.lua doc/ $(find lib/ module/ -type f|sort)
pandoc doc/apidoc.md --template=template.tex -o doc/apidoc.pdf

View File

@ -1,27 +1,59 @@
#!/usr/bin/env lua
local doc = require "lib/doc"
local tA = {...}
local docfiles = {}
for _,file in pairs(tA) do
docfiles[file] = {}
local f = io.open(file)
local lines = {}
for l in f:read("*a"):gmatch("[^\n]+") do
if l:find("function") and not l:find("local") then
lines[#lines+1] = l
local outpath = table.remove(tA,1)
print(outpath)
local function formatDocs(fd)
local rs = ""
for name,finfo in pairs(fd) do
if rs:len() > 0 then
rs = rs .. "\n\n"
end
local as = ""
for k,v in pairs(finfo.args) do
if k > 1 then
as = as .. ", "
end
as = as .. v[1]
if v[2] then
as = as .. "^"..v[2].."^"
end
end
local rt = ""
for k,v in pairs(finfo.outtypes or {}) do
if rt:len() > 0 then
rt = rt .. ", "
else
rt = ": "
end
rt = rt .. v
end
rs = string.format("%s## %s(%s)%s\n%s",rs,name,as,rt,finfo.description)
end
for k,v in pairs(lines) do
local name, args, desc = v:match("function%s+(.+)%s*%((.*)%)%s*%-%-%s*(.+)")
if name and args and desc then
docfiles[file][#docfiles[file]+1] = string.format("##%s(%s)\n%s",name,args,desc)
end
return rs
end
os.execute("mkdir -p "..outpath)
local ad = io.open(outpath.."/apidoc.md","w")
for k,v in pairs(tA) do
local fd = doc.parsefile(v)
local ds = formatDocs(fd)
print(string.format("%s: %i",v,ds:len()))
if ds and ds:len() > 0 then
os.execute("mkdir -p $(dirname \""..outpath.."/"..v.."\")")
local f = io.open(outpath.."/"..v:gsub("%.lua$",".md"),"wb")
f:write(string.format("# %s\n\n",v))
f:write(ds)
f:write("\n\n")
f:close()
ad:write(string.format("# %s\n\n",v))
ad:write(ds)
ad:write("\n\n")
end
end
for k,v in pairs(docfiles) do
if #v > 0 then
print("#"..k)
for l,m in pairs(v) do
print(m)
end
end
end
ad:close()

View File

@ -1,5 +1,5 @@
local event = {}
function event.pull(t,...) -- return an event, optionally with timeout *t* and filter *...*.
function event.pull(t,...) -- number -- -- return an event, optionally with timeout *t* and filter *...*.
local tA = {...}
if type(t) == "string" then
table.insert(tA,1,t)
@ -26,7 +26,7 @@ function event.pull(t,...) -- return an event, optionally with timeout *t* and f
return nil
end
function event.listen(e,f) -- run function *f* for every occurance of event *e*
function event.listen(e,f) -- string function -- -- run function *f* for every occurance of event *e*
os.spawn(function() while true do
local tEv = {coroutine.yield()}
if tEv[1] == e then
@ -36,7 +36,7 @@ function event.listen(e,f) -- run function *f* for every occurance of event *e*
end end,string.format("[%d] %s listener",os.pid(),e))
end
function event.ignore(e,f) -- stop function *f* running for every occurance of event *e*
function event.ignore(e,f) -- string function -- -- stop function *f* running for every occurance of event *e*
computer.pushSignal("unlisten",e,tostring(f))
end

View File

@ -12,7 +12,7 @@ net.minport = 32768
net.maxport = 65535
net.openports = {}
function net.genPacketID() -- generate a random 16-character string, for use in packet IDs
function net.genPacketID() -- -- string -- generate a random 16-character string, for use in packet IDs
local npID = ""
for i = 1, 16 do
npID = npID .. string.char(math.random(32,126))
@ -20,11 +20,11 @@ function net.genPacketID() -- generate a random 16-character string, for use in
return npID
end
function net.usend(to,port,data,npID) -- send an unreliable packet to host *to* on port *port* with data *data*, optionally with the packet ID *npID*
function net.usend(to,port,data,npID) -- string number string string -- -- send an unreliable packet to host *to* on port *port* with data *data*, optionally with the packet ID *npID*
computer.pushSignal("net_send",0,to,port,data,npID)
end
function net.rsend(to,port,data,block) -- send a reliable packet to host *to* on port *port* with data *data*, with *block* set to true to disable blocking
function net.rsend(to,port,data,block) -- string number string boolean -- boolean -- send a reliable packet to host *to* on port *port* with data *data*, with *block* set to true to disable blocking
local pid, stime = net.genPacketID(), computer.uptime() + net.streamdelay
computer.pushSignal("net_send",1,to,port,data,pid)
if block then return false end
@ -37,7 +37,7 @@ end
-- ordered packet delivery, layer 4?
function net.send(to,port,ldata) -- send arbitrary data *ldata* reliably to host *to* on port *port*
function net.send(to,port,ldata) -- string number string -- boolean -- send arbitrary data *ldata* reliably to host *to* on port *port*
local tdata = {}
if ldata:len() > net.mtu then
for i = 1, ldata:len(), net.mtu do
@ -112,7 +112,7 @@ local function socket(addr,port,sclose)
return conn
end
function net.open(to,port) -- open a socket to host *to* on port *port*
function net.open(to,port) -- string number -- buffer -- open a socket to host *to* on port *port*
if not net.rsend(to,port,"openstream") then return false, "no ack from host" end
local st = computer.uptime()+net.streamdelay
local est = false
@ -139,7 +139,7 @@ function net.open(to,port) -- open a socket to host *to* on port *port*
return socket(to,data,sclose)
end
function net.listen(port) -- listen for connections on port *port* in a blocking manner
function net.listen(port) -- number -- buffer -- listen for connections on port *port* in a blocking manner
repeat
_, from, rport, data = event.pull("net_msg")
until rport == port and data == "openstream"
@ -150,7 +150,7 @@ function net.listen(port) -- listen for connections on port *port* in a blocking
return socket(from,nport,sclose)
end
function net.flisten(port,listener) -- run function *listener* on a connection to *port*
function net.flisten(port,listener) -- number function -- function -- run function *listener* on a connection to *port*
local function helper(_,from,rport,data)
if rport == port and data == "openstream" then
local nport = math.random(net.minport,net.maxport)

View File

@ -22,7 +22,7 @@ local function saveConfig()
return true
end
function rc.load(name,force)
function rc.load(name,force) -- string boolean -- table -- Attempts to load service *name*, and if *force* is true, replaces the current instance.
if force then
rc.stop(name)
service[name] = nil
@ -35,7 +35,7 @@ function rc.load(name,force)
return res
end
function rc.stop(name,...)
function rc.stop(name,...) -- string -- boolean string -- Stops service *name*, supplying *...* to the stop function. Returns false and a reason if this fails.
if not service[name] then return false, "service not found" end
if service[name].stop then
service[name].stop(...)
@ -47,7 +47,7 @@ function rc.stop(name,...)
rc.pids[name] = nil
end
function rc.start(name,...)
function rc.start(name,...) -- string -- boolean string -- Stops service *name*, supplying *...* to the stop function. Returns false and a reason if this fails.
rc.load(name)
if not service[name] then return false, "service not found" end
local rv = {service[name].start(...)}
@ -56,19 +56,19 @@ function rc.start(name,...)
end
end
function rc.restart(name)
function rc.restart(name) -- string -- -- Restarts service *name* using rc.stop and rc.start.
rc.stop(name)
rc.start(name)
end
function rc.enable(name)
function rc.enable(name) -- string -- -- Enables service *name* being started on startup.
for k,v in pairs(cfg.enabled) do
if v == name then return false end
end
cfg.enabled[#cfg.enabled+1] = name
saveConfig()
end
function rc.disable(name)
function rc.disable(name) -- string -- -- Disables service *name* being started on startup.
local disabled = false
for k,v in pairs(cfg.enabled) do
if v == name then table.remove(cfg.enabled,k) disabled = true break end

View File

@ -27,7 +27,7 @@ function rpcf.list()
return rt
end
function rpc.call(hostname,fn,...)
function rpc.call(hostname,fn,...) -- string string -- boolean -- Calls exported function *fn* on host *hostname*, with parameters *...*, returning whatever the function returns, or false.
if hostname == "localhost" then
return rpcf[fn](...)
end
@ -44,7 +44,7 @@ function rpc.call(hostname,fn,...)
end
return false
end
function rpc.proxy(hostname,filter)
function rpc.proxy(hostname,filter) -- string string -- table -- Returns a component.proxy()-like table from the functions on *hostname* with names matching *filter*.
filter=(filter or "").."(.+)"
local fnames = rpc.call(hostname,"list")
if not fnames then return false end
@ -59,7 +59,7 @@ function rpc.proxy(hostname,filter)
end
return rt
end
function rpc.register(name,fn)
function rpc.register(name,fn) -- string function -- -- Registers a function to be exported by the RPC library.
local rpcrunning = false
for k,v in pairs(os.tasks()) do
if os.taskInfo(v).name == "rpc daemon" then

View File

@ -3,7 +3,7 @@ local local_pairs=function(tbl)
local mt=getmetatable(tbl)
return (mt and mt.__pairs or pairs)(tbl)
end
function serial.serialize(value,af) -- serialize *value* into a string. If *af* is true, allow functions. This breaks unserialization.
function serial.serialize(value,af) -- boolean -- string -- serialize *value* into a string. If *af* is true, allow functions. This breaks unserialization.
local kw={["and"]=true,["break"]=true,["do"]=true,["else"]=true,["elseif"]=true,["end"]=true,["false"]=true,["for"]=true,["function"]=true,["goto"]=true,["if"]=true,["in"]=true,["local"]=true,["nil"]=true,["not"]=true,["or"]=true,["repeat"]=true,["return"]=true,["then"]=true,["true"]=true,["until"]=true,["while"]=true}
local id="^[%a_][%w_]*$"
local ts={}
@ -40,7 +40,7 @@ function serial.serialize(value,af) -- serialize *value* into a string. If *af*
else error("ut "..t) end end
return s(value, 1)
end
function serial.unserialize(data) -- return *data*, but unserialized
function serial.unserialize(data) -- string -- -- return *data*, but unserialized
checkArg(1, data, "string")
local result, reason = load("return " .. data, "=data", _, {math={huge=math.huge}})
if not result then return nil, reason end

View File

@ -179,6 +179,7 @@ local env = {code = ""}
setmetatable(env, {__index=_env})
env:process(arg[1])
local tmpfile = os.tmpname()
if tmpfile:sub(#tmpfile) == "." then tmpfile = tmpfile:sub(1, #tmpfile - 1) end
local tmpf = io.open(tmpfile, "wb")
tmpf:write(env.code)
tmpf:close()

View File

@ -1,141 +0,0 @@
function vt100emu(gpu) -- takes GPU component proxy *gpu* and returns a function to write to it in a manner like an ANSI terminal
local colours = {0x0,0xFF0000,0x00FF00,0xFFFF00,0x0000FF,0xFF00FF,0x00B6FF,0xFFFFFF}
local mx, my = gpu.maxResolution()
local cx, cy = 1, 1
local pc = " "
local lc = ""
local mode = 0 -- 0 normal, 1 escape, 2 command
local lw = true
local sx, sy = 1,1
local cs = ""
local bg, fg = 0, 0xFFFFFF
-- setup
gpu.setResolution(mx,my)
gpu.fill(1,1,mx,my," ")
local function checkCursor()
if cx > mx and lw then
cx, cy = 1, cy+1
end
if cy > my then
gpu.copy(1,2,mx,my-1,0,-1)
gpu.fill(1,my,mx,1," ")
cy=my
end
if cy < 1 then cy = 1 end
if cx < 1 then cx = 1 end
end
local function termwrite(s)
local wb = ""
local lb, ec = nil, nil
local function flushwb()
while wb:len() > 0 do
checkCursor()
local wl = wb:sub(1,mx-cx+1)
wb = wb:sub(wl:len()+1)
gpu.set(cx, cy, wl)
cx = cx + wl:len()
end
end
local rs = ""
s=s:gsub("\8","\27[D")
pc = gpu.get(cx,cy)
gpu.setForeground(fg)
gpu.setBackground(bg)
gpu.set(cx,cy,pc)
for cc in s:gmatch(".") do
if mode == 0 then
if cc == "\n" then
flushwb()
cx,cy = 1, cy+1
elseif cc == "\t" then
wb=wb..(" "):rep(8*((cx+9)//8))
elseif cc == "\27" then
flushwb()
mode = 1
else
wb = wb .. cc
end
elseif mode == 1 then
if cc == "[" then
mode = 2
else
mode = 0
end
elseif mode == 2 then
if cc:match("[%d;]") then
cs = cs .. cc
else
mode = 0
local tA = {}
for s in cs:gmatch("%d+") do
tA[#tA+1] = tonumber(s)
end
if cc == "H" then
cx, cy = tA[1] or 1, tA[2] or 1
elseif cc == "A" then
cy = cy - (tA[1] or 1)
elseif cc == "B" then
cy = cy + (tA[1] or 1)
elseif cc == "C" then
cx = cx + (tA[1] or 1)
elseif cc == "D" then
cx = cx - (tA[1] or 1)
elseif cc == "s" then
sx, sy = cx, cy
elseif cc == "u" then
cx, cy = sx, sy
elseif cc == "n" and tA[1] == 6 then
rs = string.format("%s\27[%d;%dR",rs,cx,cy)
elseif cc == "K" and tA[1] == 1 then
gpu.fill(1,cy,cx,1," ")
elseif cc == "K" and tA[1] == 2 then
gpu.fill(cx,cy,mx,1," ")
elseif cc == "K" then
gpu.fill(1,cy,mx,1," ")
elseif cc == "J" and tA[1] == 1 then
gpu.fill(1,1,mx,cy," ")
elseif cc == "J" and tA[1] == 2 then
gpu.fill(1,1,mx,my," ")
cx, cy = 1, 1
elseif cc == "J" then
gpu.fill(1,cy,mx,my," ")
elseif cc == "m" then
for _,num in ipairs(tA) do
if num == 0 then
fg,bg,ec,lb = 0xFFFFFF,0,true,true
elseif num == 7 then
local nfg,nbg = bg, fg
fg, bg = nfg, nbg
elseif num > 29 and num < 38 then
fg = colours[num-29]
elseif num > 39 and num < 48 then
bg = colours[num-39]
elseif num == 100 then -- disable local echo
ec = false
elseif num == 101 then -- disable line mode
lb = false
end
end
gpu.setForeground(fg)
gpu.setBackground(bg)
end
cs = ""
checkCursor()
end
end
end
flushwb()
checkCursor()
pc = gpu.get(cx,cy)
gpu.setForeground(bg)
gpu.setBackground(fg)
gpu.set(cx,cy,pc)
gpu.setForeground(fg)
gpu.setBackground(bg)
return rs, lb, ec
end
return termwrite
end

View File

@ -1,5 +0,0 @@
--#include "module/vt-task.lua"
do
local r,w = vtemu(component.list("gpu")(),component.list("screen")())
devfs.register("tty0", function() return r,w,function() end end)
end

View File

@ -1,58 +0,0 @@
do
--#include "module/nvt100.lua"
function vtemu(gpua,scra) -- creates a process to handle the GPU and screen address combination *gpua*/*scra*. Returns read, write and "close" functions.
local gpu = component.proxy(gpua)
gpu.bind(scra)
local write = vt100emu(gpu)
local kba = {}
for k,v in ipairs(component.invoke(scra,"getKeyboards")) do
kba[v]=true
end
local buf, lbuf, echo = "", true, true
os.spawn(function() dprint(pcall(function()
while true do
local ty,ka,ch = coroutine.yield()
if ty == "key_down" and kba[ka] then
if ch == 13 then ch = 10 end
if ch == 8 then
if buf:len() > 0 then
if echo then write("\8 \8") end
buf = buf:sub(1,-2)
end
elseif ch > 0 then
if echo then write(string.char(ch)) end
buf=buf..string.char(ch)
end
end
end
end)) end,string.format("ttyd[%s:%s]",gpua:sub(1,8),scra:sub(1,8)))
local function bread(n)
local r
if lbuf then
while not buf:find("\n") do
coroutine.yield()
end
local n = buf:find("\n")
r, buf = buf:sub(1,n), buf:sub(n+1)
else
r = buf
buf = ""
coroutine.yield()
end
return r
end
local function bwrite(d)
local ba, lb, ec = write(d)
buf = buf .. ba
if lb ~= nil then
dprint("local buffer mode: "..tostring(lb))
lbuf = lb
end
if ec ~= nil then
dprint("echo mode: "..tostring(ec))
echo = ec
end
end
return bread, bwrite, function() io.write("\27[2J\27[H") end
end
end

View File

@ -1,148 +0,0 @@
function vt100emu(gpu) -- takes GPU component proxy *gpu* and returns a function to write to it in a manner like an ANSI terminal
local colours = {0x0,0xFF0000,0x00FF00,0xFFFF00,0x0000FF,0xFF00FF,0x00B6FF,0xFFFFFF}
local mx, my = gpu.maxResolution()
local cx, cy = 1, 1
local pc = " "
local lc = ""
local mode = "n"
local lw = true
local sx, sy = 1,1
local cs = ""
local bg, fg = 0, 0xFFFFFF
-- setup
gpu.setResolution(mx,my)
gpu.fill(1,1,mx,my," ")
local function termwrite(s)
local rs = ""
s=s:gsub("\8","\27[D")
pc = gpu.get(cx,cy)
gpu.setForeground(fg)
gpu.setBackground(bg)
gpu.set(cx,cy,pc)
for i = 1, s:len() do
local cc = s:sub(i,i)
if mode == "n" then
if cc == "\n" then -- line feed
cx, cy = 1, cy+1
elseif cc == "\r" then -- cursor home
cx = 1
elseif cc == "\27" then -- escape
mode = "e"
elseif cc == "\t" then
cx = 8*((cx+9)//8)
elseif string.byte(cc) > 31 and string.byte(cc) < 127 then -- printable, I guess
gpu.set(cx, cy, cc)
cx = cx + 1
end
elseif mode == "e" then
if cc == "[" then
mode = "v"
cs = ""
elseif cc == "D" then -- scroll down
gpu.copy(1,2,mx,my-1,0,-1)
gpu.fill(1,my,mx,1," ")
cy=cy+1
mode = "n"
elseif cc == "M" then -- scroll up
gpu.copy(1,1,mx,my-1,0,1)
gpu.fill(1,1,mx,1," ")
mode = "n"
else
mode = "n"
end
elseif mode == "v" then
mode = "n"
if cc == "s" then -- save cursor
sx, sy = cx, cy
elseif cc == "u" then -- restore cursor
cx, cy = sx, sy
elseif cc == "H" then -- cursor home or to
local tx, ty = cs:match("(%d+);(%d+)")
tx, ty = tx or "1", ty or "1"
cx, cy = tonumber(tx), tonumber(ty)
elseif cc == "A" then -- cursor up
cy = cy - (tonumber(cs) or 1)
elseif cc == "B" then -- cursor down
cy = cy + (tonumber(cs) or 1)
elseif cc == "C" then -- cursor right
cx = cx + (tonumber(cs) or 1)
elseif cc == "D" then -- cursor left
cx = cx - (tonumber(cs) or 1)
elseif cc == "h" and lc == "7" then -- enable line wrap
lw = true
elseif cc == "l" and lc == "7" then -- disable line wrap
lw = false
elseif cc == "c" then
rs = string.format("%s\27[%d;%d0c",rs,mx,my)
elseif cc == "n" and lc == "6" then
rs = string.format("%s\27[%d;%dR",rs,cx,cy)
elseif cc == "K" then
if lc == "1" then
gpu.fill(1,cy,cx,1," ")
elseif lc == "2" then
gpu.fill(cx,cy,mx,1," ")
else
gpu.fill(1,cy,mx,1," ")
end
elseif cc == "J" then
if lc == "1" then
gpu.fill(1,1,mx,cy," ")
elseif lc == "2" then
gpu.full(1,1,mx,my," ")
cx,cy = 1, 1
else
gpu.fill(1,cy,mx,my," ")
end
elseif cc == "m" then
for num in cs:gmatch("%d+") do
num=tonumber(num)
if num == 0 then
fg,bg = 0xFFFFFF,0
elseif num == 7 then
local nfg,nbg = bg, fg
fg, bg = nfg, nbg
elseif num > 29 and num < 38 then
fg = colours[num-29]
elseif num > 39 and num < 48 then
bg = colours[num-39]
end
end
gpu.setForeground(fg)
gpu.setBackground(bg)
else
cs = cs .. cc
if cc:match("[%d;]") then
mode = "v"
end
end
end
if cx > mx and lw then
cx, cy = 1, cy+1
end
if cy > my then
gpu.copy(1,2,mx,my-1,0,-1)
gpu.fill(1,my,mx,1," ")
cy=my
end
if cy < 1 then cy = 1 end
if cx < 1 then cx = 1 end
lc = cc
end
pc = gpu.get(cx,cy)
gpu.setForeground(bg)
gpu.setBackground(fg)
gpu.set(cx,cy,pc)
gpu.setForeground(fg)
gpu.setBackground(bg)
return rs
end
return termwrite
end

46
template.tex Normal file
View File

@ -0,0 +1,46 @@
\documentclass[11pt,twoside,a4paper]{article}
\usepackage{hyperref}
\usepackage{multicol}
\usepackage{standalone}
\usepackage{graphicx}
\usepackage{pdfpages}
\usepackage{listings}
\usepackage{color}
\usepackage{sectsty}
\usepackage[cm]{fullpage}
\lstset{
commentstyle=\color{cyan}, % comment style
keywordstyle=\color{cyan}, % keyword style
stringstyle=\color{red}, % string literal style
numbers=left, % where to put the line-numbers; possible values are (none, left, right)
numbersep=5pt, % how far the line-numbers are from the code
numberstyle=\tiny\color{gray}, % the style that is used for the line-numbers
}
\hypersetup{
colorlinks=true
}
\subsectionfont{\ttfamily}
% pandoc stuff
\providecommand{\tightlist}{%
\setlength{\itemsep}{0pt}\setlength{\parskip}{0pt}}
\let\stdsection\section
\renewcommand\section{\newpage\stdsection}
\lstset{basicstyle=\footnotesize\ttfamily,breaklines=true}
\newcommand{\ignore}[1]{}
\title{$title$$if(thanks)$\thanks{$thanks$}$endif$}
\author{$for(author)$$author$$sep$ \and $endfor$}
\date{}
\begin{document}
\pagenumbering{gobble}
\maketitle
\newpage
\pagenumbering{alph}
\tableofcontents
\newpage
\pagenumbering{arabic}
$body$
\end{document}