Assembla home | Assembla project page
 

root/preproc.py

Revision 5, 10.2 kB (checked in by nEUrOO, 1 year ago)

--

Line 
1 """
2     This file is part of PHP-AST Project by Romain Gaucher (http://rgaucher.info).
3
4     PHP-AST is free software: you can redistribute it and/or modify
5     it under the terms of the GNU General Public License as published by
6     the Free Software Foundation, either version 3 of the License, or
7     (at your option) any later version.
8
9     Grabber is distributed in the hope that it will be useful,
10     but WITHOUT ANY WARRANTY; without even the implied warranty of
11     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12     GNU General Public License for more details.
13
14     You should have received a copy of the GNU General Public License
15     along with PHP-AST.  If not, see <http://www.gnu.org/licenses/>.
16
17                                    --------------
18
19         preproc.py is a PHP preprocessor for analysis purposes. Since PHP is a crappy language,
20         it may be really hard to handle it correctly. I am actually fixing some limitation of
21         my AST converter with this tool for real project analysis (wordpress, wikipedia etc.).
22
23         The tool is:
24         - Simplifying strings (keeps only php variables
25         - Resolve the includes
26         - Removing comments
27 """
28 import os, re, sys, string
29
30 include = ["include","require"]
31 inc_once= ["include_once","require_once"]
32 string_1 = re.compile("\"(.*)\"",re.I)
33 string_2 = re.compile("'(.*)'",re.I)
34 simpleinclude1 = re.compile(r"\"[a-zA-Z0-9_\.]+\"",re.I)
35 simpleinclude2 = re.compile(r"'[a-zA-Z0-9_\.]+'",re.I)
36 define = re.compile(r'define\((.*),(.*)\)',re.I)
37 definelist = {}
38 php_start = ["<?", "<?php"]
39 php_end   = ["?>"]
40 var = re.compile(r'$([a-zA-Z0-9_]+)', re.I)
41 letters = re.compile("[a-zA-Z0-9_]")
42 def is_alpha(c) :
43         return letters.match(c) is not None
44
45 reginc = re.compile(r'(.*)([\W\D;\s]*)(include|require)(_once)?([\s]*)(["\'\(]+)(.*)$', re.I)
46 def include_in_line(string):
47         if 'include' in string or 'require' in string:
48                 return reginc.match(string)
49         return False
50
51 #print include_in_line("include(\"../includes/geshi/geshi.php\");")
52 #sys.exit(0)
53
54 def retFirstOccurence(string, array):
55         pos = []
56         for a in array:
57                 pos.append(string.find(a))
58         return min(pos)
59
60 def replace_all(s, old, new):
61         if old == new:
62                 return s
63         elif old in new:
64                 raise ValueError("old substring can't be part of the replacement substring.")
65         while old in s:
66                 s = s.replace(old, new)
67         return s
68
69 def include_file(filename):
70         print "include <-- %s" % filename
71         if os.path.isfile(filename):
72                 return process(filename)
73         else:
74                 print " --> Cannot include the file. The file doesn't exist / File not found"
75
76 def remove_concate(string):
77         i = 0
78         state = "source"
79         out = ""
80         com_delim = None
81         while i < len(string):
82                 if state == "source":
83                         if string[i] != ".":
84                                 out += string[i]
85                                 if string[i] == '"' or string[i] == "'":
86                                         state = "string"
87                                         com_delim = string[i]
88                 elif state == "string":
89                         if string[i] == com_delim and string[i-1] != '\\':
90                                 state = "source"
91                                 com_delim = None
92                         out += string[i]
93                 i+=1
94         return out
95
96
97 def clean_string(l):
98         i = 0
99         state = "source"
100         in_buff = ""
101         com_delim = None
102         max_len = len(l)
103         while i < max_len:
104                 if state == "comment":
105                         if l[i] == '*':
106                                 if l[i+1] =='/' :
107                                         state = "source"
108                                         i += 1
109                 elif state == "string":
110                         if l[i] == com_delim:
111                                 if l[i-1] == '\\':
112                                         if l[i-2] != '\\':
113                                                 in_buff += l[i]
114                                                 state = "source"
115                                                 com_delim = None
116                                 else:
117                                         in_buff += l[i]
118                                         state = "source"
119                                         com_delim = None
120
121                                 if l[i-2] == '\\':
122                                         in_buff += l[i]
123                                         state = "source"
124                                         com_delim = None
125                         elif l[i] == '$':
126                                 in_buff += l[i]
127                                 i+=1
128                                 # add the variables
129                                 while is_alpha(l[i]) and i < max_len:
130                                         in_buff += l[i]
131                                         i+=1
132                                 if l[i] == com_delim:
133                                         in_buff += com_delim
134                                         state = "source"
135                                 else:
136                                         in_buff += ' '
137                 else:
138                         # simply in source
139                         if l[i] == '?' and l[i+1] == '>':
140                                 #in_buff += ";"
141                                 state = "html"
142                         else:
143                                 if l[i] == '/' and l[i+1] == '/':
144                                         break
145                                 elif l[i] == '/' and l[i+1] == '*':
146                                         state = "comment"
147                                 elif l[i] == '#':
148                                         break
149                                 elif state != "array" and (l[i] == '"' or l[i] == "'"):
150                                         state = "string"
151                                         com_delim = l[i]
152                                         in_buff += l[i]
153                                 elif l[i] =='[' and state != "array":
154                                         state = "array"
155                                         in_buff += l[i]
156                                 elif l[i] == ']' and state == "array":
157                                         state = "source"
158                                         in_buff += l[i]
159                                 else:
160                                         in_buff += l[i]
161                 i+=1
162         return in_buff
163
164
165 def workout_include(string, rootdir):
166         buff = []
167         once = False
168         require = False
169         if '_once' in string:
170                 once = True
171         if 'require' in string:
172                 require = True
173         lookfor = ""
174         if require:
175                 lookfor = "require"
176         else:
177                 lookfor = "include"
178         if once:
179                 lookfor += "_once"
180         pos = string.find(lookfor)
181         inStr = string[pos:]
182         studyStr = inStr[len(lookfor):inStr.find(';')]
183         # add after and before
184         savedStudyStr = studyStr
185         buff.append(string[:string.find(lookfor)])
186         studyStr = ''.join(studyStr.split())
187         if len(studyStr) > 0:
188                 if studyStr[0] == '(':
189                         studyStr = studyStr[1:]
190                         studyStr = studyStr[:len(studyStr)-1]
191                 if simpleinclude1.match(studyStr):
192                         out = simpleinclude1.search(studyStr)
193                         toInc = out.group(0)
194                         toInc = toInc[toInc.find('"')+1:toInc.rfind('"')]
195                         if os.path.isfile(rootdir + toInc):
196                                 for l in include_file(rootdir + toInc):
197                                         buff.append(l)
198                 elif simpleinclude2.match(studyStr):
199                         out = simpleinclude2.search(studyStr)
200                         toInc = out.group(0)
201                         toInc = toInc[toInc.find("'")+1:toInc.rfind("'")]
202                         if os.path.isfile(rootdir + toInc):
203                                 for l in include_file(rootdir + toInc):
204                                         buff.append(l)
205                 else:
206                         # complex case
207                         #print "\n\n<START> #########################"
208                         #print "CPLX\t",studyStr
209                         studyStr = remove_concate(studyStr)
210                         #print "CPLX\t",studyStr
211                         for k in definelist:
212                                 if studyStr.find(k) >= 0:
213                                         try:
214                                                 studyStr = replace_all(studyStr, k, definelist[k])
215                                         except ValueError:
216                                                 continue
217                         #print "CPLX\t",studyStr
218                         studyStr = replace_all(studyStr, "'",'')
219                         studyStr = replace_all(studyStr, '"','')
220                         studyStr = os.path.normpath(rootdir + studyStr)
221                         studyStr = ''.join(studyStr.split())
222                         #print "CPLX\t",studyStr
223                         if os.path.isfile(studyStr):
224                                 for l in include_file(studyStr):
225                                         buff.append(l)
226         buff.append("include ('');")
227         buff.append(string[string.find(lookfor + savedStudyStr) + len (lookfor + savedStudyStr)+1:])
228
229         buff = ''.join(buff)
230         buff = buff.split('\n')
231         obuff = []
232         for a in buff:
233                 obuff.append(a + '\n')
234         return obuff
235
236
237 def clean_spec_chars(l):
238         ret = ""
239         i = 0
240         max_len = len(l)
241         while i < max_len:
242                 if l[i] == '\\':
243                         i +=1
244                 else:
245                         ret += l[i]
246                 i+=1
247         return ret
248
249
250 def process(fname, checkIncludes = True):
251         global definelist
252         html = True
253         out_buff = []
254         in_buff = ""
255         try:
256                 f = open(fname, 'r')
257         except IOError:
258                 print "Cannot open the file", f
259                 return 1
260         listStates = ["html", "source", "string", "comment","array"]
261         state = "html"
262         com_delim = None
263         root = fname[:max(fname.rfind('/')+1, fname.rfind('\\')+1)]
264         for l in f.xreadlines():
265                 cl = clean_string(l)
266                 if checkIncludes and include_in_line(cl):
267                         buff = workout_include(l,root)
268                         for a in buff:
269                                 out_buff.append(a)
270                 else:
271                         if 'define' in l:
272                                 s = cl[l.find('define'):]
273                                 s = s[:s.find(';')]
274                                 if define.match(s):
275                                         out = define.search(s)
276                                         key = out.group(1)
277                                         val = out.group(2)
278                                         val = replace_all(val, "'",'')
279                                         val = replace_all(val, '"','')
280                                         key = replace_all(key, "'",'')
281                                         key = replace_all(key, '"','')
282                                         definelist[key] = val
283                         out_buff.append(l)
284
285         # finite state machine
286         for o in out_buff:
287                 l = o
288                 if "/*" not in o:
289                         l = clean_spec_chars(o)
290                 i,max_len = 0,len(l)
291                 while i < max_len:
292                         try:
293                                 if state == "html":
294                                         if l[i] == '<' and l[i+1] == '?':
295                                                 if l[i+2] in ('p','P') and l[i+3]in ('h','H') and l[i+4]in ('p','P'):
296                                                         i+=4
297                                                 else:
298                                                         i+=1
299                                                 state = "source"
300                                 else:
301                                         if state == "comment":
302                                                 if l[i] == '*':
303                                                         if l[i+1] =='/' :
304                                                                 state = "source"
305                                                                 i += 1
306                                         elif state == "string":
307                                                 if l[i] == com_delim:
308                                                         if l[i-1] == '\\':
309                                                                 if l[i-2] != '\\':
310                                                                         in_buff += l[i]
311                                                                         state = "source"
312                                                                         com_delim = None
313                                                         else:
314                                                                 in_buff += l[i]
315                                                                 state = "source"
316                                                                 com_delim = None
317
318                                                         if l[i-2] == '\\':
319                                                                 in_buff += l[i]
320                                                                 state = "source"
321                                                                 com_delim = None
322                                                 elif l[i] == '$':
323                                                         in_buff += l[i]
324                                                         i+=1
325                                                         # add the variables
326                                                         while is_alpha(l[i]) and i < max_len:
327                                                                 in_buff += l[i]
328                                                                 i+=1
329                                                         if l[i] == com_delim:
330                                                                 in_buff += com_delim
331                                                                 state = "source"
332                                                         else:
333                                                                 in_buff += ' '
334                                         else:
335                                                 # simply in source
336                                                 if l[i] == '?' and l[i+1] == '>':
337                                                         #in_buff += ";"
338                                                         state = "html"
339                                                 else:
340                                                         if l[i] == '/' and l[i+1] == '/':
341                                                                 break
342                                                         elif l[i] == '/' and l[i+1] == '*':
343                                                                 state = "comment"
344                                                         elif l[i] == '#':
345                                                                 break
346                                                         elif state != "array" and (l[i] == '"' or l[i] == "'"):
347                                                                 state = "string"
348                                                                 com_delim = l[i]
349                                                                 in_buff += l[i]
350                                                         elif l[i] =='[' and state != "array":
351                                                                 state = "array"
352                                                                 in_buff += l[i]
353                                                         elif l[i] == ']' and state == "array":
354                                                                 state = "source"
355                                                                 in_buff += l[i]
356                                                         else:
357                                                                 in_buff += l[i]
358                                 i += 1
359                         except IndexError:
360                                 continue
361         # remove the 'or die'
362         in_buff = srcToLower(in_buff)
363         in_buff = replace_die(in_buff)
364         in_buff = in_buff.replace("or die(\"\" . mysql_error(\"\"))", "")
365         in_buff = in_buff.replace("or die (mysql_error())", "")
366         return in_buff
367        
368
369 regword = re.compile('([$a-zA-Z0-9_]+)',re.I)
370 def myRegLower(x):
371         x = x.group()
372         if '$' in x:
373                 return x
374         return x.lower()
375 def srcToLower(buff):
376         return regword.sub(myRegLower ,buff)
377
378 regdir = re.compile(r'or([\s]+)die([\s]*)\(([$_\w\d.\'"\s]+)\)',re.I)
379 def replace_die(buff):
380         return regdir.sub(lambda x: '',buff)
381
382 def pp_file(fname):
383         s = process(fname)
384         nname = fname[:fname.rfind('.php')] + '.preproc.php'
385         out = open(nname,"w")
386         out.write("<?php\n\n")
387         out.write(s)
388         out.write("\n\n?>")
389         out.close()
390         return nname
391
392 if __name__ == "__main__":
393         fname = sys.argv[1]
394         pp_file(fname)
395
396
397
398
Note: See TracBrowser for help on using the browser.