1#!/usr/bin/python
2#!/neo/opt/bin/python
3
4import sys, string, os, getopt, signal, time
5sys.path.append("../python")
6import neo_cgi, neo_util
7import cStringIO
8
9class ClearSilverChecker:
10  def __init__ (self):
11    self.context = ""
12    self.data = ""
13    self.at = 0
14    self.cmd = ""
15    self.tokens = []
16
17  def error(self, s):
18    lineno = self.lineno(self.data, self.at)
19    print "-E- [%s:%d] %s" % (self.context, lineno, s)
20    if self.cmd:
21      print "    Command is %s" % self.cmd
22    if self.tokens:
23      print "    Tokens: %s" % repr(self.tokens)
24
25  def warn(self, s):
26    lineno = self.lineno(self.data, self.at)
27    print "-W- [%s:%d] %s" % (self.context, lineno, s)
28    if self.cmd:
29      print "    Command is %s" % self.cmd
30    if self.tokens:
31      print "    Tokens: %s" % repr(self.tokens)
32
33  def check_file(self, filename):
34    print "Checking file %s" % filename
35    self.context = filename
36    try:
37      self.run_neo_cgi(filename)
38    except neo_util.ParseError, reason:
39      print "-E- %s" % str(reason)
40    self.data = open(filename, "r").read()
41    self.parse()
42
43  def run_neo_cgi(self, filename):
44    stdin = cStringIO.StringIO("")
45    stdout = cStringIO.StringIO()
46    neo_cgi.cgiWrap(stdin, stdout, {})
47    neo_cgi.IgnoreEmptyFormVars(1)
48    ncgi = neo_cgi.CGI()
49    path = os.path.dirname(filename)
50    ncgi.hdf.setValue("hdf.loadpaths.path", path)
51    ncgi.display(filename)
52    return
53
54  def lineno(self, data, i):
55    return len(string.split(data[:i], '\n'))
56
57  def parse(self):
58    self.at = 0
59    x = string.find(self.data[self.at:], '<?cs ')
60    while x >= 0:
61      self.at = x + self.at
62      ce = string.find(self.data[self.at:], '?>')
63      if ce == -1:
64	self.error("Missing ?> in expression")
65      else:
66	ce = ce + self.at
67	self.check_command(ce)
68
69      # reset these class variables
70      self.cmd = ""
71      self.tokens = []
72      self.at = self.at + 1
73      x = string.find(self.data[self.at:], '<?cs ')
74
75  def check_command(self, end):
76    cmd = self.data[self.at+5:end]
77    self.cmd = cmd
78    if cmd[0] == '/':
79      # handle end command
80      cmd = cmd[1:]
81      self.command_end(cmd)
82      return
83
84    pound = string.find(cmd, '#')
85    colon = string.find(cmd, ':')
86    bang = string.find(cmd, '!')
87    if colon == -1 and bang == -1:
88      if pound != -1:
89	#print "Found comment: %s" % cmd
90	pass
91      else:
92	self.command_begin(string.strip(cmd), "")
93    elif pound != -1 and bang != -1 and pound < bang:
94      # comment
95      #print "Found comment: %s" % cmd
96      pass
97    elif pound != -1 and colon != -1 and pound < colon:
98      # comment
99      #print "Found comment: %s" % cmd
100      pass
101    elif bang == -1:
102      arg = cmd[colon+1:]
103      cmd = cmd[:colon]
104      self.command_begin(cmd, arg)
105    elif colon == -1:
106      arg = cmd[bang+1:]
107      cmd = cmd[:bang]
108      self.command_begin(cmd, arg)
109
110  def command_end(self, cmd):
111    pass
112
113  def command_begin(self, cmd, args):
114    #print "%s -> %s" % (cmd, args)
115    if cmd == "alt":
116      self.check_expression(args)
117    elif cmd == "if":
118      self.check_expression(args)
119    elif cmd == "elif":
120      self.check_expression(args)
121    elif cmd == "else":
122      pass
123    elif cmd == "include":
124      self.check_expression(args)
125    elif cmd == "linclude":
126      self.check_expression(args)
127    elif cmd == "name":
128      self.check_expression(args)
129    elif cmd == "var":
130      self.check_expression(args)
131    elif cmd == "evar":
132      self.check_expression(args)
133    elif cmd == "lvar":
134      self.check_expression(args)
135    elif cmd == "def":
136      macro, args = self.split_macro(args)
137      if macro: self.check_expression(macro, lvalue=1)
138      if args:self.check_expression(args)
139    elif cmd == "call":
140      macro, args = self.split_macro(args)
141      if macro: self.check_expression(macro, lvalue=1)
142      if args:self.check_expression(args)
143    elif cmd == "with":
144      varname, args = self.split_equals(args)
145      if varname: self.check_expression(varname, lvalue=1)
146      if args: self.check_expression(args)
147    elif cmd == "each":
148      varname, args = self.split_equals(args)
149      if varname: self.check_expression(varname, lvalue=1)
150      if args: self.check_expression(args)
151    elif cmd == "loop":
152      varname, args = self.split_equals(args)
153      if varname: self.check_expression(varname, lvalue=1)
154      if args: self.check_expression(args)
155    elif cmd == "set":
156      varname, args = self.split_equals(args)
157      if varname: self.check_expression(varname, lvalue=1)
158      if args: self.check_expression(args)
159    else:
160      self.error("Unrecognized command %s" % cmd)
161
162  def split_equals(self, args):
163    x = string.find(args, '=')
164    if x == -1:
165      self.error("Missing equals")
166      return None, None
167    else:
168      return args[:x], args[x+1:]
169
170  def split_macro(self, args):
171    b = string.find(args, '(')
172    e = string.rfind(args, ')')
173    if b == -1:
174      self.error("Missing opening parenthesis")
175      return None, None
176    if e == -1:
177      self.error("Missing closing parenthesis")
178      return None, None
179    macro_name = args[:b]
180    args = args[b+1:e]
181    return macro_name, args
182
183  def check_expression(self, expr, lvalue=0):
184    tokens = self.tokenize_expression(expr)
185    #print repr(tokens)
186    if len(tokens) == 0:
187      self.error("Empty Expression")
188
189  _OP = 1
190  _VAR = 2
191  _VARN = 3
192  _STR = 4
193  _NUM = 5
194
195  _TOKEN_SEP = "\"?<>=!#-+|&,)*/%[]( \t\r\n"
196
197  def tokenize_expression(self, expr):
198    self.tokens = []
199    while expr:
200      #print "expr: '%s'" % expr
201      expr = string.lstrip(expr)
202      len_expr = len(expr)
203      if len_expr == 0: break
204      if expr[:2] in ["<=", ">=", "==", "!=", "||", "&&"]:
205	self.tokens.append((ClearSilverChecker._OP, expr[:2]))
206	expr = expr[2:]
207	continue
208      elif expr[0] in ["!", "?", "<", ">", "+", "-", "*", "/", "%", "(", ")", "[", "]", ".", ',']:
209	self.tokens.append((ClearSilverChecker._OP, expr[0]))
210	expr = expr[1:]
211	continue
212      elif expr[0] in ["#", "$"]:
213	x = 1
214	if expr[1] in ['+', '-']: x=2
215	while len_expr > x and expr[x] not in ClearSilverChecker._TOKEN_SEP: x=x+1
216	if x == 0:
217	  self.error("[1] Zero length token, unexpected character %s" % expr[0])
218	  x = 1
219	else:
220	  token = expr[1:x]
221	  if expr[0] == "#":
222	    try:
223	      n = int(token)
224	      t_type = ClearSilverChecker._NUM
225	    except ValueError:
226	      t_type = ClearSilverChecker._VARN
227	  else:
228	    t_type = ClearSilverChecker._VAR
229	  self.tokens.append((t_type, token))
230	expr = expr[x:]
231	continue
232      elif expr[0] in ['"', "'"]:
233	x = string.find(expr[1:], expr[0])
234	if x == -1:
235	  self.error("Missing end of string %s " % expr)
236	  break
237	else:
238	  x = x + 1
239	  self.tokens.append((ClearSilverChecker._STR, expr[1:x]))
240	  expr = expr[x+2:]
241	  continue
242      else:
243	x = 0
244	while len_expr > x and expr[x] not in ClearSilverChecker._TOKEN_SEP: x=x+1
245	if x == 0:
246	  self.error("[2] Zero length token, unexpected character %s" % expr[0])
247	  x = 1
248	else:
249	  token = expr[:x]
250	  try:
251	    n = int(token)
252	    t_type = ClearSilverChecker._NUM
253	    self.warn("This behavior changed in version 0.9: previously this was a variable name, now its a number: %s" % token)
254	  except ValueError:
255	    t_type = ClearSilverChecker._VAR
256	  self.tokens.append((t_type, token))
257	expr = expr[x:]
258	continue
259    return self.tokens
260
261  # For version 0.9, we changed two things, we should check for them
262  # both
263  #  - an all numeric expression element is now considered a number and
264  #    not an HDF variable name
265  #  - we now use boolean evaluation in places that used to use either a
266  #    special case or a numeric evaluation
267
268def usage(argv0):
269  print "%s: usage info!!" % argv0
270
271def main(argv):
272  alist, args = getopt.getopt(argv[1:], "", ["help"])
273
274  for (field, val) in alist:
275    if field == "--help":
276      usage(argv[0])
277      sys.exit(-1)
278
279  for file in args:
280    ClearSilverChecker().check_file(file)
281
282if __name__ == "__main__":
283  main(sys.argv)
284