From 9f9cd9b5c709119071b8db66228af263dff0a3ae Mon Sep 17 00:00:00 2001 From: George Hotz Date: Wed, 30 Jul 2014 14:01:44 -0700 Subject: [PATCH] moved cda into qira, started vimplugin, new configuration file --- cda/cachegen.py | 11 ++++ cda/cacheserver.py | 54 +++++++--------- cda/cda | 43 ------------- cda/static/cda.css | 22 ++++--- cda/static/cda.js | 47 ++++++++------ install.sh | 2 +- middleware/qira.py | 15 ++++- middleware/qira_analysis.py | 2 +- middleware/qira_config.py | 4 ++ middleware/qira_program.py | 117 ++++++++++++++++++++++++++--------- middleware/qira_webserver.py | 31 +++++++--- tests/vimplugin/a.out | Bin 9626 -> 10406 bytes tests/vimplugin/main.c | 5 ++ tests/vimplugin/swag.c | 8 +++ vim/qira.vim | 7 +++ webstatic/cdastatic | 1 + 16 files changed, 225 insertions(+), 144 deletions(-) delete mode 100755 cda/cda create mode 100644 middleware/qira_config.py create mode 100644 tests/vimplugin/swag.c create mode 100644 vim/qira.vim create mode 120000 webstatic/cdastatic diff --git a/cda/cachegen.py b/cda/cachegen.py index 3db65a6c..9ba06629 100644 --- a/cda/cachegen.py +++ b/cda/cachegen.py @@ -10,10 +10,14 @@ ci.Config.set_library_file(basedir+"/clang/build/Release+Asserts/lib/libclang.so import pickle from clang.cindex import CursorKind +import json +from hashlib import sha1 + # debug DEBUG = 0 # cache generated +file_cache = {} object_cache = {} xref_cache = {} @@ -115,3 +119,10 @@ def parse_file(filename, args=[]): return (care, rdat) +def parse_files(files, args=[]): + for fn in files: + print "CDA: caching",fn + file_cache[fn] = parse_file(fn) + dat = (object_cache, file_cache, xref_cache) + return dat + diff --git a/cda/cacheserver.py b/cda/cacheserver.py index a30cb996..306cce59 100644 --- a/cda/cacheserver.py +++ b/cda/cacheserver.py @@ -2,25 +2,16 @@ import os import sys import cgi -from flask import Flask,redirect, request +from flask import Flask,redirect,request,Blueprint from html import XHTML -import pickle -app = Flask(__name__, static_folder='static', static_url_path='/static') + +app = Blueprint('cda',__name__) # escape on the real def escape(s, crap=False): return s.replace("<", "<").replace(">", ">").replace(" ", " ").replace("\n", "
").replace("\t", " "*4).replace("\x00", " ") cgi.escape = escape -@app.route("/") -def index(): - h = XHTML().html - h.head.link(rel="stylesheet", href="/static/cda.css") - h.head.style('body{margin:0;padding:0;}') - h.body.iframe(src='/list', id="topframe") - h.body.iframe(src='/x/=', id="bottomframe") - return str(h) - @app.route("/list") def home(): # add files @@ -34,7 +25,7 @@ def home(): # generate html h = XHTML().html - h.head.link(rel="stylesheet", href="/static/cda.css") + h.head.link(rel="stylesheet", href="/cdastatic/cda.css") body = h.body objs = list(set(objs)) objs.sort() @@ -42,16 +33,17 @@ def home(): body.div.a(obj[1], href=obj[0], klass=obj[2]) return str(h) -@app.route("/x/") -def display_xref(xref): - xref = xref.decode("base64") +@app.route("/x/") +def display_xref(b64xref): + xref = b64xref.decode("base64") h = XHTML().html - h.head.link(rel="stylesheet", href="/static/cda.css") + h.head.link(rel="stylesheet", href="/cdastatic/cda.css") body = h.body(klass="xref") body.div.div(xref, klass="xrefstitle") if xref in xref_cache: for obj in xref_cache[xref]: - body.div.a(obj, onclick="parent.frames[0].location = '/f?"+obj+"';", klass="filelink") + linkobj = obj+","+b64xref + body.div.a(obj, onclick="parent.location = '/f?"+linkobj+"';", klass="filelink") return str(h) @app.route("/f") @@ -61,26 +53,28 @@ def display_file(): return "file "+str(path)+" not found" # generate the HTML h = XHTML().html - h.head.link(rel="stylesheet", href="/static/cda.css") - h.head.script(src="/static/socket.io.min.js") - h.head.script(src="/static/jquery-2.1.0.js") - h.head.script(src="/static/jquery.scrollTo-1.4.3.1.js") - h.head.script(src="/static/cda.js?"+os.urandom(16).encode("hex")) + h.head.link(rel="stylesheet", href="/cdastatic/cda.css") + h.head.script(src="/cdastatic/socket.io.min.js") + h.head.script(src="/cdastatic/jquery-2.1.0.js") + h.head.script(src="/cdastatic/jquery.scrollTo-1.4.3.1.js") + h.head.script(src="/cdastatic/cda.js?"+os.urandom(16).encode("hex")) body = h.body - body.div(path, id="filename") + body.div(path, id='filename') + prog = body.div(id="program") + body.iframe(id='bottomframe') # get parsed file (care, rdat) = file_cache[path] # add line numbers lc = len(rdat.split("\n")) - ln = body.div(id="ln") + ln = prog.div(id="ln") for linenum in range(lc): - ln.span("%5d \n" % (linenum+1), id="l"+str(linenum+1), onclick='location.hash='+str(linenum+1)) + ln.span("%5d \n" % (linenum+1), id="l"+str(linenum+1), onclick='go_to_line('+str(linenum+1)+')') # add the code #print object_cache - p = body.div(id="code") + p = prog.div(id="code") last = 0 for (start, end, klass, usr) in care: if last > start: @@ -104,12 +98,8 @@ def display_file(): return str(h) -def start(cache): +def set_cache(cache): global object_cache, file_cache, xref_cache - (object_cache, file_cache, xref_cache) = cache print "read",len(file_cache),"files",len(object_cache),"objects",len(xref_cache),"xrefs" - #app.run(host='127.0.0.1', debug=True, port=5000) - app.run(host='127.0.0.1', port=5000) - diff --git a/cda/cda b/cda/cda deleted file mode 100755 index 1df03a34..00000000 --- a/cda/cda +++ /dev/null @@ -1,43 +0,0 @@ -#!/usr/bin/env python2 -import os -import sys -import json -from hashlib import sha1 - -import cachegen -import cacheserver - -file_cache = {} - -if __name__ == "__main__": - try: - os.mkdir("/tmp/cdacaches") - except: - pass - - files = [] - for fn in sys.argv[1:]: - fn = os.path.realpath(sys.argv[1]) - files.append(fn) - - cachename = "/tmp/cdacaches/"+sha1(''.join(files)).hexdigest() - - if os.path.isfile(cachename): - dat = json.load(open(cachename)) - print "read cache",cachename - else: - for fn in files: - print "caching",fn - file_cache[fn] = cachegen.parse_file(fn) - dat = (cachegen.object_cache, file_cache, cachegen.xref_cache) - f = open(cachename, "wb") - json.dump(dat, f) - f.close() - print "wrote cache",cachename - - cacheserver.start(dat) - - - - - diff --git a/cda/static/cda.css b/cda/static/cda.css index e2a651e3..5451f384 100644 --- a/cda/static/cda.css +++ b/cda/static/cda.css @@ -111,20 +111,26 @@ iframe { border: 0 none; } -#topframe { - width:100%; - height: 70%; +body { + margin: 0; + padding: 0; } #bottomframe { - width:100%; - height:30%; + width: 100%; + height: 200px; border-top: 1px solid black; + position: absolute; + bottom: 0; + left: 0; + background-color: #EEEEEE; +} + +#program { + padding: 5px; } #filename { - padding-bottom: 10px; - font-size: 14px; + display: none; } - diff --git a/cda/static/cda.js b/cda/static/cda.js index 0ef65dd4..1db4325d 100644 --- a/cda/static/cda.js +++ b/cda/static/cda.js @@ -8,38 +8,49 @@ function p(s) { var highlighted = $(); $(window).on('hashchange', function() { - if (window.location.hash == "") return; - var ln = window.location.hash.substr(1); + if (location.hash == "") location.replace("#0"); + var ln = location.hash.substr(1).split(",")[0]; + var b64xref = location.hash.split(",")[1]; highlighted.removeClass("line_highlighted") highlighted = $("#l" + ln) - highlighted.addClass("line_highlighted"); - $(window).scrollTo(highlighted, {offset: -150}) - stream.emit('navigateline', $('#filename')[0].innerHTML, parseInt(ln)) + if (highlighted.length > 0) { + highlighted.addClass("line_highlighted"); + $(window).scrollTo(highlighted, {offset: -150}) + stream.emit('navigateline', $('#filename')[0].innerHTML, parseInt(ln)) + } + if (b64xref !== undefined) { + selected.removeClass('highlighted'); + selected = $(document.getElementsByName(atob(b64xref))); + selected.addClass('highlighted'); + if (frames[0].location.pathname != '/x/'+b64xref) { + frames[0].location.replace('/x/'+b64xref); + } + } }); var selected = $(); function link_click_handler(e) { - //p(e.target.getAttribute('name')); - selected.removeClass('highlighted'); - selected = $(document.getElementsByName(e.target.getAttribute('name'))); - selected.addClass('highlighted'); + var usr = e.target.getAttribute('name'); + location.replace(location.hash.split(",")[0]+","+btoa(usr)); } function link_dblclick_handler(e) { var targets = e.target.getAttribute('targets').split(" "); p(targets); - parent.frames[0].location = "/f?"+targets[0]; -} - -function xref_handler(e) { var usr = e.target.getAttribute('name'); - p("xref "+usr); - parent.frames[1].location = '/x/'+btoa(usr) - return false; + location = "/f?"+targets[0]+","+btoa(usr); +} + +function go_to_line(line) { + var b64xref = location.hash.split(",")[1]; + var newl = "#"+line; + if (b64xref !== undefined) { + newl += ","+b64xref; + } + location.replace(newl); } -// no selection window.onmousedown = function() { return false; }; // when the page loads we need to check the hash @@ -48,8 +59,6 @@ window.onload = function() { $('.link').bind('click', link_click_handler); $('.link').bind('dblclick', link_dblclick_handler); - $('.link').bind('contextmenu', xref_handler); }; - diff --git a/install.sh b/install.sh index acc0fbfc..622c659c 100755 --- a/install.sh +++ b/install.sh @@ -15,7 +15,7 @@ elif [ $(which pacman) ]; then fi echo "installing pip packages" -sudo $PIP install flask-socketio pillow pyelftools ./qiradb +sudo $PIP install html flask-socketio pillow pyelftools ./qiradb echo "making symlink" sudo ln -sf $(pwd)/qira /usr/local/bin/qira diff --git a/middleware/qira.py b/middleware/qira.py index ae126ecd..b996d17e 100755 --- a/middleware/qira.py +++ b/middleware/qira.py @@ -5,24 +5,33 @@ import socket import threading import time +import qira_config import qira_socat import qira_program import qira_webserver if __name__ == '__main__': + # define arguments parser = argparse.ArgumentParser(description = 'Analyze binary. Like "qira /bin/ls /"') parser.add_argument('-s', "--server", help="bind on port 4000. like socat", action="store_true") parser.add_argument('-t', "--tracelibraries", help="trace into all libraries", action="store_true") parser.add_argument('binary', help="path to the binary") parser.add_argument('args', nargs='*', help="arguments to the binary") + parser.add_argument("--dwarf", help="parse program dwarf data", action="store_true") + parser.add_argument("--cda", help="use CDA to view source", action="store_true") + + # parse arguments args = parser.parse_args() + if args.tracelibraries: + qira_config.TRACE_LIBRARIES = True + if args.dwarf: + qira_config.WITH_DWARF = True + if args.cda: + qira_config.WITH_CDA = True # creates the file symlink, program is constant through server run program = qira_program.Program(args.binary, args.args) - if args.tracelibraries: - program.defaultargs.append("-tracelibraries") - is_qira_running = 1 try: socket.create_connection(('127.0.0.1', qira_webserver.QIRA_WEB_PORT)) diff --git a/middleware/qira_analysis.py b/middleware/qira_analysis.py index 4b3fe839..5ac3ffaf 100755 --- a/middleware/qira_analysis.py +++ b/middleware/qira_analysis.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +import qira_config import qira_program import time diff --git a/middleware/qira_config.py b/middleware/qira_config.py new file mode 100644 index 00000000..f30ead98 --- /dev/null +++ b/middleware/qira_config.py @@ -0,0 +1,4 @@ +WITH_CDA = False +WITH_DWARF = False +TRACE_LIBRARIES = False + diff --git a/middleware/qira_program.py b/middleware/qira_program.py index f4546dcf..a5f3f5a4 100644 --- a/middleware/qira_program.py +++ b/middleware/qira_program.py @@ -1,7 +1,12 @@ +import qira_config import os import sys -import struct +from hashlib import sha1 +basedir = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(basedir+"/../cda") +import json +import struct import qiradb PPCREGS = ([], 4, True) @@ -12,6 +17,25 @@ ARMREGS = (['R0','R1','R2','R3','R4','R5','R6','R7','R8','R9','R10','R11','R12', X86REGS = (['EAX', 'ECX', 'EDX', 'EBX', 'ESP', 'EBP', 'ESI', 'EDI', 'EIP'], 4, False) X64REGS = (['RAX', 'RCX', 'RDX', 'RBX', 'RSP', 'RBP', 'RSI', 'RDI', 'RIP'], 8, False) +def cachewrap(cachedir, cachename, cachegen): + try: + os.mkdir(cachedir) + except: + pass + cachename = cachedir + "/" + cachename + if os.path.isfile(cachename): + dat = json.load(open(cachename)) + print "read cache",cachename + else: + print "cache",cachename,"not found, generating" + dat = cachegen() + if dat == None: + return None + f = open(cachename, "wb") + json.dump(dat, f) + f.close() + print "wrote cache",cachename + return dat def which(prog): import subprocess @@ -37,6 +61,8 @@ class Program: # call which to match the behavior of strace and gdb self.program = which(prog) self.args = args + self.proghash = sha1(open(prog).read()).hexdigest() + print "*** program is",self.program,"with hash",self.proghash # bring this back if self.program != "/tmp/qira_binary": @@ -48,6 +74,8 @@ class Program: # defaultargs for qira binary self.defaultargs = ["-strace", "-D", "/dev/null", "-d", "in_asm", "-singlestep"] + if qira_config.TRACE_LIBRARIES: + program.defaultargs.append("-tracelibraries") # pmaps is global, but updated by the traces self.instructions = {} @@ -158,39 +186,68 @@ class Program: os.execvp(self.qirabinary, eargs) def getdwarf(self): - self.dwarves = {} - self.rdwarves = {} - from elftools.elf.elffile import ELFFile - elf = ELFFile(open(self.program)) - if not elf.has_dwarf_info(): + (self.dwarves, self.rdwarves) = ({}, {}) + + if not qira_config.WITH_DWARF: return # DWARF IS STUPIDLY COMPLICATED - di = elf.get_dwarf_info() - for cu in di.iter_CUs(): - basedir = '' - # get the base directory - for die in cu.iter_DIEs(): - if die.tag == "DW_TAG_compile_unit": - basedir = die.attributes['DW_AT_comp_dir'].value - # get the line program? - lp = di.line_program_for_CU(cu) - dir_index = lp['file_entry'][0].dir_index - if dir_index > 0: - basedir = lp['include_directory'][dir_index-1] - # now we have the filename - filename = basedir + "/" + lp['file_entry'][0].name + def parse_dwarf(): + dwarves = {} + rdwarves = {} + + from elftools.elf.elffile import ELFFile + elf = ELFFile(open(self.program)) + if not elf.has_dwarf_info(): + return (dwarves, rdwarves) + files = [] + filename = None + di = elf.get_dwarf_info() + for cu in di.iter_CUs(): + try: + basedir = None + # get the base directory + for die in cu.iter_DIEs(): + if die.tag == "DW_TAG_compile_unit": + basedir = die.attributes['DW_AT_comp_dir'].value + "/" + if basedir == None: + continue + # get the line program? + lp = di.line_program_for_CU(cu) + dir_index = lp['file_entry'][0].dir_index + if dir_index > 0: + basedir += lp['include_directory'][dir_index-1]+"/" + # now we have the filename + filename = basedir + lp['file_entry'][0].name + files.append(filename) + lines = open(filename).read().split("\n") + print "DWARF: parsing",filename + for entry in lp.get_entries(): + s = entry.state + if s != None: + #print filename, s.line, len(lines) + dwarves[s.address] = (s.line, lines[s.line-1]) + rdwarves[filename+"#"+str(s.line)] = s.address + except Exception as e: + print "DWARF: error on",filename,"got",e + return (dwarves, rdwarves) + + (self.dwarves, self.rdwarves) = cachewrap("/tmp/dwarfcaches", self.proghash, parse_dwarf) + + # cda + if not qira_config.WITH_CDA: + return + + def parse_cda(): try: - lines = open(filename).read().split("\n") - except: - print "*** couldn't find %s for DWARF", filename - continue - for entry in lp.get_entries(): - #print entry - s = entry.state - if s != None: - self.dwarves[s.address] = (s.line, lines[s.line-1]) - self.rdwarves[(filename, s.line)] = s.address + import cachegen + return cachegen.parse_files(files) + except Exception as e: + print "CDA: cachegen failed with",e + return None + + self.cda = cachewrap("/tmp/cdacaches", self.proghash, parse_cda) + class Trace: def __init__(self, fn, forknum, r1, r2, r3): diff --git a/middleware/qira_webserver.py b/middleware/qira_webserver.py index cabedab1..28747caa 100644 --- a/middleware/qira_webserver.py +++ b/middleware/qira_webserver.py @@ -1,4 +1,9 @@ +import qira_config import os +import sys +basedir = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(basedir+"/../cda") + import qira_socat import time @@ -23,8 +28,17 @@ gevent.monkey.patch_all() # done with that app = Flask(__name__) +#app.config['DEBUG'] = True socketio = SocketIO(app) +# add cda server paths here +if qira_config.WITH_CDA: + try: + import cacheserver + app.register_blueprint(cacheserver.app) + except Exception as e: + print "CDA: load cacheserver failed with",e + def ghex(a): if a == None: return None @@ -64,7 +78,7 @@ def mwpoller(): @socketio.on('navigateline', namespace='/qira') def navigateline(fn, ln): try: - iaddr = program.rdwarves[(fn,ln)] + iaddr = program.rdwarves[fn+"#"+str(ln)] except: return print 'navigateline',fn,ln,iaddr @@ -279,7 +293,6 @@ def get_strace(forknum): # ***** generic webserver stuff ***** - @app.route('/', defaults={'path': 'index.html'}) @app.route('/') def serve(path): @@ -290,11 +303,11 @@ def serve(path): ext = path.split(".")[-1] - if ext == 'css': - path = "qira.css" - - dat = open(webstatic+path).read() - if ext == 'js' and not path.startswith('client/compatibility/') and not path.startswith('packages/'): + try: + dat = open(webstatic+path).read() + except: + return "" + if ext == 'js' and not path.startswith('client/compatibility/') and path.startswith('client/'): dat = "(function(){"+dat+"})();" if ext == 'js': @@ -309,6 +322,10 @@ def run_server(largs, lprogram): global program args = largs program = lprogram + try: + cacheserver.set_cache(program.cda) + except: + pass print "starting socketio server..." threading.Thread(target=mwpoller).start() socketio.run(app, port=QIRA_WEB_PORT) diff --git a/tests/vimplugin/a.out b/tests/vimplugin/a.out index d8e7e108fad435392b63be1107207b048261f119..5e666723525462cfb509834700b3077951e28e46 100755 GIT binary patch delta 2712 zcmZ`*Yitx%6ux(6_StrKX4~y-yQMqZE>MAXx5c(R3SHjYv?iz^J^;5^u|-~mt=LFX zA_QCk(|Ue@L_&>(9}tPPY6_YFq7)SaC?Oyu7OY7LKERL&q`00tbEi}SH<|PJ&bjB@ zd+uv?)$V(;-RFr*QNo%*3F*x47_-kQ*>#Xe_Ku%R97HfQ85FuD*l#toeyh>P=fqG0Gt(6I*QSE#qnZGVl9-4MK%GG zU>aB?`n?l)q&|0Cmsmmxwu_V7WZ6J$h={t)AhC_V+XptHJm8^7R$!#q#;@(iO_x;p zcWD|)jXZ_a9f+dD^hRdB)|bLI@++oeUvlk?t{-MPoAqEC;izQdMbPqnXGzmLx;If8=zwSY`RgFEp zVA`A@VYDnr2=$AT*($P?tGbs;%|jl?gzJ%?L;gPUe&k;wA4Gl``8KScL}9bFz|uFE zN=4x#YhnFm?3_R&ndXu=3o|mT!m4x|Zq7V}8t{u?XL-22RyXVRO^9ocu!qpL7e61| zv=%x}p|k-#h3=qjycLREQH4lR1PRH|DV7^rT>%<__mIwnL#}EXgFCK((j!K754Iy& z>x9SLVY&gHa|dVwx<+I|#I_Iy+`h~T9&tA3&qz@?OTD1UlN@aOu$DkX^|D13NEcG$ z81^tLP|*kL~_=c?FByMWrzv1fw@rAc9PrTh`xlMNPS2JLYYD(*2RLG zL$WljOc0xWHR|>jBo4|p3~|!M#mK0|jAQ0His-{)L6Pz%j8*a*6zZW-E6qf=5?Y-u zx+bMfP8*$GlUapxkSwGIEfzKwYlx(!O-&QAz&1CE%wQj-+a28G!hzfmVnoKCOW}Z0 zIkaG2xUOil(4CiswFK=%M-;-E2lCICn%UDqP2S>wR<4L zvrysgjFk~aq0v+7U2F8fs=X$gasm!|@}Q5}V6Mv!tsc$m9_D4rGJ5%E>8Ap%%8YQxe>g;1(y` z@rI!>TZReQ5l1Jtc0zM@<9F24I zS!h+mjvi}Ncj|#IwNg3BwUhd8BE%1Ou&4P>neSH>O!S2n&1Uq}Y(|#;?p9wL#e;q) zC+zUaaUJzRX>O&Xh-*dAoLfzcpewi9@f0s}T(5)d*PNf22!%=h3b^67!LuTZPMcSX(lf_xA?4f?6kHUeSryn3Av73{~3br&E!n6SL&QV0>mXh*|Xi z<1UQWoUhW8NWG#=`r(6oRnTm3IDa4YL482QBGm`H@J?Vq-3Al2b@XHC($pNYkd((` z_W>n2Bm!473}At~+H8T?VS2CxZ_?)ASlATI$~mojdq@ah&Ec&Nx`JxXwMSS7;Yv^q zvcCckPJ}($tnnWjQh>NdeE1BM7I@)D>M5^AL?$;=<`{K%B(wDhIAtEe#OoN2w{~Vu zT=&Qrx**L1_%bvXsFpRRUwH@jGcPyOZwKNs8w=lNW`@0pYb=AK8zn{Xp%yI6Y*UDv zm^)qK&Zl{7_E*AMQm2IcqQ}0On(#pEe(wL^0$=C;4_4tH?vHPbM-QFg2?o)i57H@P zZ9OE7{ju#TJTK|@*@{rTvKs&NqR}~ZHPdIjSXNS2QVu_a3Jmj~p<;`E`$6u=4gUfB C$P>N* delta 1914 zcmZuxYitx%6h3!mUsKxMnQhmdrH`GmyR_P+y9RMf5W9_O99{{VRILGF2$g{FgD4dx zA?*rkn~Js`6N(}D3?Zlq+CMb?Q7cM8epq5+sz1z1j425kFkmQA)^lg>5F(yr&N<(A z&OP^a<{mG+yJlDqtDZb?aWsHlGxEUkinQM*A?+Xesa3hj927u}z24aEb5OU>8ROct zLqi(J*@f62r;k;8ew*Es{P60}@9bGQ@sagx4KKrROMMhiNp(0Q>8uf%yc*Zbi#;HL zg!|>%$Wl?J6tZlO&nDw%@`^t5z?HzM3%dcg;jTXaVsj@q#D0>{TMmHQ%&3++ITF^4aGvo%L=dm+>`j zc;?KuZQEKy@q>I-wCt6;G3o7DIkfKYp(kz*bzUDlJGqw5^E8jn9lKpFpFcBvmzaaQ zlCx_Edy*yd(2}u$0c#JXKE)f}w3N7w3EvJ$58#`=j1<3(lfGz{j=5G|!7s!^-@rfR zvPt$RmEBD?KwBFoJ4)7`5U|0&$lFF~S?u&D8k3Z0Q37-X)z_rQs(jLhN`C+#NmLJg zyYaj~;eCU=*|BT>oDbE&EH(%8+BrE-BtVs|0ow58V4UfAgmgQO2isW|=Yw%=ROE>+ zgMPd}lwlS=9g4GVEEv^PaU;%!bmcFR=|fE|DEwx)Sa^{|@e4J!#bvta>RgdnoLt5Z zlm5Rr=j9#1B<@Hfmi!zr+3|bioxnVfzg^x1%!_F)hv9G^>%sliwb)u+*W%V>2YY*a zLGnl=T)oHD8m_jIV!qYBoq-Yjs(K@4YqF8eG!+*KUQ}J*8e0}Q$;L|e8I1PM^o*v9 z-RiQF-Hsi^b_=#!umLRU8O0K;h2wfo=@+OU=k?s3^Bolz7J`fB6dKq10wh1 zF;?5hSTh#uGK#8Nc3Bn2>vD=Akb(1c?aaXC_3g@P)lPsc7VC4$K7l?*7L8-6URPQ) zC*Ef>MuV=L6wG$)XvityuoHg@i-dLvM5oQiH|Wy&Fy3l-G@{o#X~SZb34#ydx_FCn zg1aPt;#7Sc-;a+{I~R<8YcQMx#e6l&ojp-~$xfOEF3gpzHmK*e7u+J;(VL=J&T zHcE}okp=_Y;**VUGZopQh#Uih>?C^xrxxvvS)%2gRRC-thXKziB%-lqab)y!+VT=< ziGIHs8ry^?5{A_5$8Qs@3~)Zt5&;N9l3uC1jc4VCzoG}S}MvO#}3k6+>gCrSGc!wKYoyk z7T%($Ch4Gy?Yni3avtb_Mbmji;1he^D&P-1mqA+tOxUwrqi1g_Ro%&V=(fK|fN7EO r-<^CfD!OlhB?ip$^;KN4qtpg6zOwiS+}yMYziVp58%=3!YVP|N*rbF$ diff --git a/tests/vimplugin/main.c b/tests/vimplugin/main.c index adc14ab8..fa85318e 100644 --- a/tests/vimplugin/main.c +++ b/tests/vimplugin/main.c @@ -1,3 +1,6 @@ +void swag(); +void swag2(); + int main() { int i; int j = 1; @@ -8,6 +11,8 @@ int main() { k += 1; l += i; } + swag(); + swag2(); printf("%d %d %d\n", j, k, l); } diff --git a/tests/vimplugin/swag.c b/tests/vimplugin/swag.c new file mode 100644 index 00000000..d7049042 --- /dev/null +++ b/tests/vimplugin/swag.c @@ -0,0 +1,8 @@ +void swag() { + printf("SWAG!\n"); +} + +void swag2() { + printf("SWAG2!\n"); +} + diff --git a/vim/qira.vim b/vim/qira.vim new file mode 100644 index 00000000..e4aeceb7 --- /dev/null +++ b/vim/qira.vim @@ -0,0 +1,7 @@ +if !has('python') + echo "vim must be compiled with +python" + finish +endif + +function! + diff --git a/webstatic/cdastatic b/webstatic/cdastatic new file mode 120000 index 00000000..2f2b7e84 --- /dev/null +++ b/webstatic/cdastatic @@ -0,0 +1 @@ +../cda/static/ \ No newline at end of file