Imported Upstream version 1.8.15
[platform/upstream/doxygen.git] / addon / doxypysql / search.py
1 #!/usr/bin/python
2
3 # python script to search through doxygen_sqlite3.db
4 #
5 # Permission to use, copy, modify, and distribute this software and its
6 # documentation under the terms of the GNU General Public License is hereby
7 # granted. No representations are made about the suitability of this software
8 # for any purpose. It is provided "as is" without express or implied warranty.
9 # See the GNU General Public License for more details.
10 #
11
12 import sqlite3
13 import sys
14 import os
15 import getopt
16 import json
17 import re
18
19 class MemberType:
20   Define="macro definition"
21   Function="function"
22   Variable="variable"
23   Typedef="typedef"
24   Enumeration="enumeration"
25   EnumValue="enumvalue"
26   Signal="signal"
27   Slot="slot"
28   Friend="friend"
29   DCOP="dcop"
30   Property="property"
31   Event="event"
32   File="file"
33
34 class RequestType:
35   References="9901"
36   Struct="9902"
37   Includers="9903"
38   Includees="9904"
39   Members="9905"
40   BaseClasses="9906"
41   SubClasses="9907"
42
43 g_use_regexp=False
44 ###############################################################################
45
46 # case-insensitive sqlite regexp function
47 def re_fn(expr, item):
48     reg = re.compile(expr, re.I)
49     return reg.search(item) is not None
50
51 def openDb(dbname):
52     if dbname == None:
53         dbname = "doxygen_sqlite3.db"
54
55     if not os.path.isfile(dbname):
56         raise BaseException("No such file %s" % dbname )
57
58     conn = sqlite3.connect(dbname)
59     conn.execute('PRAGMA temp_store = MEMORY;')
60     conn.row_factory = sqlite3.Row
61     conn.create_function("REGEXP", 2, re_fn)
62     return conn
63 ###############################################################################
64 class Finder:
65     def __init__(self,cn,name,row_type=str):
66         self.cn=cn
67         self.name=name
68         self.row_type=row_type
69
70     def match(self,row):
71         if self.row_type is int:
72             return " rowid=?"
73         else:
74             if g_use_regexp == True:
75                 return " REGEXP (?,%s)" %row
76             else:
77                 return " %s=?" %row
78
79     def fileName(self,file_id):
80         if self.cn.execute("SELECT COUNT(*) FROM path WHERE rowid=?",[file_id]).fetchone()[0] > 1:
81             sys.stderr.write("WARNING: non-uniq fileid [%s]. Considering only the first match." % file_id)
82
83         for r in self.cn.execute("SELECT * FROM path WHERE rowid=?",[file_id]).fetchall():
84                 return r['name']
85
86         return ""
87
88     def fileId(self,name):
89         if self.cn.execute("SELECT COUNT(*) FROM path WHERE"+self.match("name"),[name]).fetchone()[0] > 1:
90             sys.stderr.write("WARNING: non-uniq file name [%s]. Considering only the first match." % name)
91
92         for r in self.cn.execute("SELECT rowid FROM path WHERE"+self.match("name"),[name]).fetchall():
93                 return r[0]
94
95         return -1
96 ###############################################################################
97     def references(self):
98         o=[]
99         cur = self.cn.cursor()
100         cur.execute("SELECT rowid FROM memberdef WHERE"+self.match("name"),[self.name])
101         rowids = cur.fetchall()
102
103         if len(rowids) == 0:
104             return o
105
106         rowid = rowids[0]['rowid']
107         cur = self.cn.cursor()
108         #TODO:SELECT rowid from refid where refid=refid
109         for info in cur.execute("SELECT * FROM xrefs WHERE dst_rowid=?", [rowid]):
110             item={}
111             cur = self.cn.cursor()
112             for i2 in cur.execute("SELECT * FROM memberdef WHERE rowid=?",[info['src_rowid']]):
113                 item['name']=i2['name']
114                 item['src']=info['src_rowid']
115                 # Below no longer directly supported on this entry; can be found from either memberdef
116                 #item['file']=self.fileName(info['file_id'])
117                 #item['line']=info['line']
118
119             o.append(item)
120         return o
121 ###############################################################################
122     def function(self):
123         o=[]
124         c=self.cn.execute('SELECT * FROM memberdef WHERE'+self.match("name")+' AND kind=?',[self.name,MemberType.Function])
125         for r in c.fetchall():
126             item={}
127             item['name'] = r['name']
128             item['definition'] = r['definition']
129             item['argsstring'] = r['argsstring']
130             item['file'] = self.fileName(r['file_id'])
131             item['line'] = r['line']
132             item['detaileddescription'] = r['detaileddescription']
133             o.append(item)
134         return o
135 ###############################################################################
136     def file(self):
137         o=[]
138         for r in self.cn.execute("SELECT rowid,name FROM local_file WHERE"+self.match("name"),[self.name]).fetchall():
139             item={}
140             item['name'] = r['name']
141             item['id'] =   r['rowid']
142             o.append(item)
143         return o
144
145 ###############################################################################
146     def macro(self):
147         o=[]
148         c=self.cn.execute('SELECT * FROM memberdef WHERE'+self.match("name")+' AND kind=?',[self.name,MemberType.Define])
149         for r in c.fetchall():
150             item={}
151             item['name'] = r['name']
152             if r['argsstring']:
153                 item['argsstring'] = r['argsstring']
154             item['definition'] = r['initializer']
155             item['file'] = self.fileName(r['file_id'])
156             item['line'] = r['line']
157             o.append(item)
158         return o
159 ###############################################################################
160     def typedef(self):
161         o=[]
162         c=self.cn.execute('SELECT * FROM memberdef WHERE'+self.match("name")+' AND kind=?',[self.name,MemberType.Typedef])
163         for r in c.fetchall():
164             item={}
165             item['name'] = r['name']
166             item['definition'] = r['definition']
167             item['file'] = self.fileName(r['file_id'])
168             item['line'] = r['line']
169             o.append(item)
170         return o
171 ###############################################################################
172     def variable(self):
173         o=[]
174         c=self.cn.execute('SELECT * FROM memberdef WHERE'+self.match("name")+' AND kind=?',[self.name,MemberType.Variable])
175         for r in c.fetchall():
176             item={}
177             item['name'] = r['name']
178             item['definition'] = r['definition']
179             item['file'] = self.fileName(r['file_id'])
180             item['line'] = r['line']
181             o.append(item)
182         return o
183 ###############################################################################
184     def params(self):
185         o=[]
186         c=self.cn.execute('SELECT rowid FROM memberdef WHERE'+self.match("name"),[self.name])
187         for r in c.fetchall():
188             #a=("SELECT * FROM param where id=(SELECT param_id FROM memberdef_param where memberdef_id=?",[memberdef_id])
189             item={}
190             item['id'] = r['id']
191             o.append(item)
192         return o
193 ###############################################################################
194     def struct(self):
195         o=[]
196         c=self.cn.execute('SELECT * FROM compounddef WHERE'+self.match("name"),[self.name])
197         for r in c.fetchall():
198             item={}
199             item['name'] = r['name']
200             o.append(item)
201         return o
202 ###############################################################################
203     def includers(self):
204         o=[]
205         fid = self.fileId(self.name)
206         c=self.cn.execute('SELECT * FROM includes WHERE dst_id=?',[fid])
207         for r in c.fetchall():
208             item={}
209             item['name'] = self.fileName(r['src_id'])
210             o.append(item)
211         return o
212 ###############################################################################
213     def includees(self):
214         o=[]
215         fid = self.fileId(self.name)
216         c=self.cn.execute('SELECT * FROM includes WHERE src_id=?',[fid])
217         for r in c.fetchall():
218             item={}
219             item['name'] = self.fileName(r['dst_id'])
220             o.append(item)
221         return o
222 ###############################################################################
223     def members(self):
224         o=[]
225         c=self.cn.execute('SELECT * FROM memberdef WHERE'+self.match("scope"),[self.name])
226         for r in c.fetchall():
227             item={}
228             item['name'] = r['name']
229             item['definition'] = r['definition']
230             item['argsstring'] = r['argsstring']
231             item['file'] = self.fileName(r['file_id'])
232             item['line'] = r['line']
233             #item['documentation'] = r['documentation']
234             o.append(item)
235         return o
236 ###############################################################################
237     def baseClasses(self):
238         o=[]
239         c=self.cn.execute('SELECT compounddef.name FROM compounddef JOIN compoundref ON compounddef.rowid=compoundref.base_rowid WHERE compoundref.derived_rowid IN (SELECT rowid FROM compounddef WHERE'+self.match("name")+')',[self.name])
240         for r in c.fetchall():
241             item={}
242             item['name'] = r['name']
243             o.append(item)
244         return o
245 ###############################################################################
246     def subClasses(self):
247         o=[]
248         c=self.cn.execute('SELECT compounddef.name FROM compounddef JOIN compoundref ON compounddef.rowid=compoundref.derived_rowid WHERE compoundref.base_rowid IN (SELECT rowid FROM compounddef WHERE'+self.match("name")+')',[self.name])
249         for r in c.fetchall():
250             item={}
251             item['name'] = r['name']
252             o.append(item)
253         return o
254 ###############################################################################
255 def process(f,kind):
256     request_processors = {
257         MemberType.Function: f.function,
258         MemberType.File: f.file,
259         MemberType.Define:   f.macro,
260         MemberType.Variable: f.variable,
261         MemberType.Typedef:  f.typedef,
262         RequestType.References: f.references,
263         RequestType.Struct: f.struct,
264         RequestType.Includers: f.includers,
265         RequestType.Includees: f.includees,
266         RequestType.Members: f.members,
267         RequestType.BaseClasses: f.baseClasses,
268         RequestType.SubClasses: f.subClasses
269     }
270     return request_processors[kind]()
271 ###############################################################################
272
273 # the -H option isn't documented. It's one of the more recent additions, but it's treating refids as if they would be a string. I'm just taking a stab at updating it for now, converting to use rowid, and making other edits necessary to get it to run.
274 def processHref(cn,ref):
275     j={}
276
277     # is it in memberdef ?
278     table="memberdef"
279     if ( cn.execute("SELECT count(*) from %s WHERE rowid=?"%table,[ref] ).fetchone()[0] > 0 ):
280         for r in cn.execute("SELECT kind,rowid FROM %s WHERE rowid=?" % table,[ref]).fetchall():
281             f=Finder(cn,r['rowid'],int)
282             j=process(f,str(r['kind']))
283
284     # is it in compounddef ?
285     table="compounddef"
286     if ( cn.execute("SELECT count(*) from %s WHERE rowid=?"%table,[ref]).fetchone()[0] > 0 ):
287         for r in cn.execute("SELECT rowid FROM %s WHERE rowid=?"%table,[ref] ).fetchall():
288             f=Finder(cn,r[0],int)
289             j=process(f,RequestType.Struct)
290
291     return j
292 ###############################################################################
293 def serveCgi():
294     import cgi
295
296     print('Content-Type: application/json\n')
297
298     fieldStorage = cgi.FieldStorage()
299     form = dict((key, fieldStorage.getvalue(key)) for key in fieldStorage.keys())
300
301     if 'href' in form:
302         ref = form['href']
303     else:
304         print('{"result": null, "error": "no refid given"}')
305         sys.exit(0)
306
307     cn=openDb('doxygen_sqlite3.db')
308
309     j = processHref(cn,ref)
310
311     print(json.dumps({"result":j,"error":None}))
312 ###############################################################################
313 def usage():
314   sys.stderr.write("""Usage: search.py [Options]
315 Options:
316     -h, --help
317     -d <D>    Use database <D> for queries.
318     -f <F>    Search for definition of function <F>.
319     -m <M>    Search for definition of macro <M>.
320     -r <F>    Search for references to function <F>.
321     -t <T>    Search for definition of type <T>.
322     -v <V>    Search for definition of variable <V>.
323     -I <I>    What files are including <I>.
324     -i <i>    What files are included by <i>.
325     -B <C>    Get the base classes of class <C>.
326     -M <C>    Get all members of class <C>.
327     -S <C>    Get the sub classes of class <C>.
328     -R        Consider the search <term> to be a regex.
329 """)
330 ###############################################################################
331 def serveCli(argv):
332     try:
333         opts, args = getopt.getopt(argv, "hr:RI:i:d:f:m:t:v:H:M:B:S:F:",["help"])
334     except getopt.GetoptError:
335         usage()
336         sys.exit(1)
337
338     ref=None
339     dbname=None
340     j={}
341     global g_use_regexp
342
343     for a, o in opts:
344         if a in ('-h', '--help'):
345             usage()
346             sys.exit(0)
347         elif a in ('-d'):
348             dbname=o
349             continue
350         elif a in ('-r'):
351             kind=RequestType.References
352         elif a in ('-R'):
353             g_use_regexp=True
354             continue
355         elif a in ('-I'):
356             kind=RequestType.Includers
357         elif a in ('-i'):
358             kind=RequestType.Includees
359         elif a in ('-M'):
360             kind=RequestType.Members
361         elif a in ('-B'):
362             kind=RequestType.BaseClasses
363         elif a in ('-S'):
364             kind=RequestType.SubClasses
365         elif a in ('-f'):
366             kind=MemberType.Function
367         elif a in ('-F'):
368             # undocumented
369             # seems to fit with the lower case "search" patterns?
370             kind=MemberType.File
371         elif a in ('-m'):
372             kind=MemberType.Define
373         elif a in ('-t'):
374             kind=MemberType.Typedef
375         elif a in ('-v'):
376             kind=MemberType.Variable
377         elif a in ('-H'):
378             # undocumented
379             ref = o
380
381         cn=openDb(dbname)
382         f=Finder(cn,o)
383         if ref != None:
384           j=processHref(cn,ref)
385         else:
386           j=process(f,kind)
387         print(json.dumps(j,indent=4))
388
389
390 def main(argv):
391     if 'REQUEST_METHOD' in os.environ:
392         serveCgi()
393     else:
394         serveCli(argv)
395
396 if __name__ == '__main__':
397     main(sys.argv[1:])