1#!/usr/bin/env python 2 3from __future__ import print_function 4import sys,os,re 5import functools 6 7class Error(Exception): 8 pass 9 10 11class ParseError(Error): 12 def __init__(self, errline): 13 Error.__init__(self, errline) 14 15 16class Struct: 17 pass 18 19 20def createCNameMap(): 21 t = '' 22 for i in range(256): 23 if ((ord('A') <= i) and (i <= ord('Z'))) or \ 24 ((ord('a') <= i) and (i <= ord('z'))) or \ 25 ((ord('0') <= i) and (i <= ord('9'))): 26 t += chr(i) 27 else: 28 t += '_' 29 return t 30 31 32def seekBegin(f): 33 while True: 34 line = f.readline() 35 if not line: 36 return False 37 if line.startswith('BEGIN SANE_Option_Descriptor'): 38 return True 39 40 41def parseVerbatim(o, line): 42 words = line.split(None, 1) 43 if (len(words) < 2) or (words[1][0] != '@'): 44 return False 45 o[words[0]] = words[1] 46 return True 47 48 49def parseLine_type(o, line): 50 words = line.split(None, 2) 51 otype = words[1] 52 o['type'] = 'SANE_TYPE_' + otype.upper() 53 if otype == 'group': 54 g.ngroups += 1 55 oname = '_group_%d' % g.ngroups 56 o['size'] = 0 57 else: 58 temp = words[2] 59 idx = temp.find('[') 60 if idx == -1: 61 oname = temp 62 o['size'] = 1 63 else: 64 oname = temp[0:idx] 65 o['size'] = int(temp[idx+1:-1]) 66 o['name'] = oname 67 68 69def parseLine_title(o, line): 70 o['title'] = line.split(None, 1)[1] 71 72 73def parseLine_desc(o, line): 74 o['desc'] = line.split(None, 1)[1] 75 76 77def parseLine_unit(o, line): 78 o['unit'] = 'SANE_UNIT_' + line.split(None, 1)[1].upper() 79 80 81def parseLine_default(o, line): 82 o['default'] = line.split(None, 1)[1] 83 84 85def parseLine_cap(o, line): 86 words = line.split() 87 o['cap'] = ['SANE_CAP_' + s.upper() for s in words[1:]] 88 89 90def parseLine_constraint(o, line): 91 c = line.split(None,1)[1] 92 if c[0] == '{': 93 o['constraint'] = c[1:-1].split('|') 94 elif c[0] == '(': 95 o['constraint'] = tuple(c[1:-1].split(',')) 96 else: 97 sys.stderr.write('Ignored: %s\n' % line) 98 99 100def parseLine_info(o, line): 101 words = line.split() 102 o['info'] = ['SANE_INFO_' + s.upper() for s in words[1:]] 103 104def parseLine_rem(o, line): 105 pass 106 107def normalize(o): 108 if 'cname' not in o: 109 cname = o['name'].translate(cnameMap) 110 o['cname'] = cname 111 else: 112 cname = o['cname'] 113 o['cname_opt'] = 'opt_' + cname 114 o['cname_con'] = 'constraint_' + cname 115 if 'title' not in o: 116 o['title'] = 'NO TITLE' 117 if 'desc' not in o: 118 o['desc'] = '@sod->title' % o 119 if 'unit' not in o: 120 o['unit'] = 'SANE_UNIT_NONE' 121 if 'constraint_type' not in o: 122 if 'constraint' not in o: 123 ct = 'SANE_CONSTRAINT_NONE' 124 elif isinstance(o['constraint'], list): 125 if o['type'] == 'SANE_TYPE_STRING': 126 ct = 'SANE_CONSTRAINT_STRING_LIST' 127 else: 128 ct = 'SANE_CONSTRAINT_WORD_LIST' 129 elif isinstance(o['constraint'], tuple): 130 ct = 'SANE_CONSTRAINT_RANGE' 131 elif isinstance(o['constraint'], str): 132 oc = o['constraint'] 133 if oc.startswith('@range'): 134 ct = 'SANE_CONSTRAINT_RANGE' 135 elif oc.startswith('@word_list'): 136 ct = 'SANE_CONSTRAINT_WORD_LIST' 137 elif oc.startswith('@string_list'): 138 ct = 'SANE_CONSTRAINT_STRING_LIST' 139 o['constraint_type'] = ct 140 return o 141 142 143def parseFile(f): 144 if not seekBegin(f): 145 return None 146 options = [ { 147 'name' : '', 148 'cname' : 'opt_num_opts', 149 'title' : '@SANE_TITLE_NUM_OPTIONS', 150 'desc' : '@SANE_DESC_NUM_OPTIONS', 151 'type' : 'SANE_TYPE_INT', 152 'unit' : 'SANE_UNIT_NONE', 153 'size' : 1, 154 'cap' : ['SANE_CAP_SOFT_DETECT'], 155 'constraint_type' : 'SANE_CONSTRAINT_NONE', 156 'default' : '@w = ' + opt_prefix + 'last' 157 } ] 158 o = {} 159 while True: 160 line = f.readline() 161 if not line: 162 break 163 line = line.strip() 164 if not line: 165 continue 166 token = line.split(None, 1)[0].lower() 167 if token == 'end': 168 break 169 if token == 'type': 170 if 'name' in o: 171 options.append(o) 172 o = {} 173 funcName = 'parseLine_' + token 174 if funcName in globals(): 175 if not parseVerbatim(o, line): 176 func = globals()[funcName] 177 func(o, line) 178 else: 179 sys.stderr.write('Skip: %s\n' % line) 180 if 'name' in o: 181 options.append(o) 182 return [normalize(o) for o in options] 183 184 185def genHeader(options): 186 print ("\ntypedef union {") 187 print (" SANE_Word w;") 188 print (" SANE_Int i;") 189 print (" SANE_Bool b;") 190 print (" SANE_Fixed f;") 191 print (" SANE_String s;") 192 print (" void *ptr;") 193 print ("} option_value_t;") 194 print ("\ntypedef enum {") 195 for o in options: 196 print (" %(cname_opt)s," % o) 197 print (" " + opt_prefix + "last") 198 print ("} option_t;") 199 200 print ("\ntypedef struct {") 201 print (" SANE_Option_Descriptor sod;") 202 print (" option_value_t val,def;") 203 print (" SANE_Word info;") 204 print ("} option_descriptor_t;") 205 206 print ("\nstruct pixma_sane_t;") 207 print ("static int build_option_descriptors(struct pixma_sane_t *ss);\n") 208 209 210def genMinMaxRange(n, t, r): 211 if t == 'SANE_TYPE_FIXED': 212 r = ['SANE_FIX(%s)' % x for x in r] 213 print ("static const SANE_Range " + n + " =") 214 print (" { " + r[0] + "," + r[1] + "," + r[2] + " };") 215 216 217def genList(n, t, l): 218 if t == 'SANE_TYPE_INT': 219 etype = 'SANE_Word' 220 l = [str(len(l))] + l 221 elif t == 'SANE_TYPE_FIXED': 222 etype = 'SANE_Word' 223 l = [str(len(l))] + ['SANE_FIX(%s)' % x for x in l] 224 elif t == 'SANE_TYPE_STRING': 225 etype = 'SANE_String_Const' 226 l = ['SANE_I18N("%s")' % x for x in l] + ['NULL'] 227 print ("static const %s %s[%d] = {" % (etype, n, len(l))) 228 for x in l[0:-1]: 229 print ("\t" + x + ",") 230 print ("\t" + l[-1] + " };") 231 232 233def genConstraints(options): 234 print ("") 235 for o in options: 236 if 'constraint' not in o: continue 237 c = o['constraint'] 238 oname = o['cname_con'] 239 otype = o['type'] 240 if isinstance(c, tuple): 241 genMinMaxRange(oname, otype, c) 242 elif isinstance(c, list): 243 genList(oname, otype, c) 244 245def buildCodeVerbatim(o): 246 for f in ('name', 'title', 'desc', 'type', 'unit', 'size', 'cap', 247 'constraint_type', 'constraint', 'default'): 248 if (f not in o): continue 249 temp = o[f] 250 if (not isinstance(temp,str)) or \ 251 (len(temp) < 1) or (temp[0] != '@'): 252 continue 253 o['code_' + f] = temp[1:] 254 255def ccode(o): 256 buildCodeVerbatim(o) 257 if 'code_name' not in o: 258 o['code_name'] = '"' + o['name'] + '"' 259 for f in ('title', 'desc'): 260 cf = 'code_' + f 261 if cf in o: continue 262 o[cf] = 'SANE_I18N("' + o[f] + '")' 263 264 for f in ('type', 'unit', 'constraint_type'): 265 cf = 'code_' + f 266 if cf in o: continue 267 o[cf] = o[f] 268 269 if 'code_size' not in o: 270 otype = o['type'] 271 osize = o['size'] 272 if otype == 'SANE_TYPE_STRING': 273 code = str(osize + 1) 274 elif otype == 'SANE_TYPE_INT' or otype == 'SANE_TYPE_FIXED': 275 code = str(osize) + ' * sizeof(SANE_Word)' 276 elif otype == 'SANE_TYPE_BUTTON': 277 code = '0' 278 else: 279 code = 'sizeof(SANE_Word)' 280 o['code_size'] = code 281 282 if ('code_cap' not in o) and ('cap' in o): 283 o['code_cap'] = functools.reduce(lambda a,b: a+'|'+b, o['cap']) 284 else: 285 o['code_cap'] = '0' 286 287 if ('code_info' not in o) and ('info' in o): 288 o['code_info'] = functools.reduce(lambda a,b: a+'|'+b, o['info']) 289 else: 290 o['code_info'] = '0' 291 292 if ('code_default' not in o) and ('default' in o): 293 odefault = o['default'] 294 otype = o['type'] 295 if odefault == '_MIN': 296 rhs = 'w = sod->constraint.range->min' 297 elif odefault == '_MAX': 298 rhs = 'w = sod->constraint.range->max' 299 elif otype in ('SANE_TYPE_INT', 'SANE_TYPE_BOOL'): 300 rhs = 'w = %(default)s' 301 elif otype == 'SANE_TYPE_FIXED': 302 rhs = 'w = SANE_FIX(%(default)s)' 303 elif otype == 'SANE_TYPE_STRING': 304 rhs = 's = SANE_I18N("%(default)s")' 305 o['code_default'] = rhs % o 306 if 'code_default' in o: 307 code = ' opt->def.%(code_default)s;\n' 308 if o['constraint_type'] != 'SANE_CONSTRAINT_STRING_LIST': 309 code += ' opt->val.%(code_default)s;\n' 310 else: 311 code += ' opt->val.w = find_string_in_list' \ 312 '(opt->def.s, sod->constraint.string_list);\n' 313 o['full_code_default'] = code % o 314 else: 315 o['full_code_default'] = '' 316 317 if ('code_constraint' not in o) and ('constraint' in o): 318 ct = o['constraint_type'] 319 idx = len('SANE_CONSTRAINT_') 320 ctype = ct[idx:].lower() 321 if ctype == 'range': 322 rhs = '&%(cname_con)s' % o 323 else: 324 rhs = '%(cname_con)s' % o 325 o['code_constraint'] = ctype + ' = ' + rhs 326 if 'code_constraint' in o: 327 code = ' sod->constraint.%(code_constraint)s;\n' 328 o['full_code_constraint'] = code % o 329 else: 330 o['full_code_constraint'] = '' 331 332 return o 333 334def genBuildOptions(options): 335 print ("\nstatic") 336 print ("int find_string_in_list(SANE_String_Const str, const SANE_String_Const *list)") 337 print ("{") 338 print (" int i;") 339 print (" for (i = 0; list[i] && strcmp(str, list[i]) != 0; i++) {}") 340 print (" return i;") 341 print ("}") 342 print ("") 343 print ("static") 344 print ("int build_option_descriptors(struct pixma_sane_t *ss)") 345 print ("{") 346 print (" SANE_Option_Descriptor *sod;") 347 print (" option_descriptor_t *opt;") 348 print ("") 349 print (" memset(OPT_IN_CTX, 0, sizeof(OPT_IN_CTX));") 350 351 for o in options: 352 o = ccode(o) 353 otype = o['type'] 354 code = '\n opt = &(OPT_IN_CTX[%(cname_opt)s]);\n' \ 355 ' sod = &opt->sod;\n' \ 356 ' sod->type = %(code_type)s;\n' \ 357 ' sod->title = %(code_title)s;\n' \ 358 ' sod->desc = %(code_desc)s;\n' 359 if otype != 'SANE_TYPE_GROUP': 360 code += ' sod->name = %(code_name)s;\n' \ 361 ' sod->unit = %(code_unit)s;\n' \ 362 ' sod->size = %(code_size)s;\n' \ 363 ' sod->cap = %(code_cap)s;\n' \ 364 ' sod->constraint_type = %(code_constraint_type)s;\n' \ 365 '%(full_code_constraint)s' \ 366 ' OPT_IN_CTX[%(cname_opt)s].info = %(code_info)s;\n' \ 367 '%(full_code_default)s' 368 sys.stdout.write(code % o) 369 print ("") 370 print (" return 0;") 371 print ("}\n") 372 373g = Struct() 374g.ngroups = 0 375opt_prefix = 'opt_' 376con_prefix = 'constraint_' 377cnameMap = createCNameMap() 378options = parseFile(sys.stdin) 379print ("/* DO NOT EDIT THIS FILE! */") 380print ("/* Automatically generated from pixma.c */") 381if (len(sys.argv) == 2) and (sys.argv[1] == 'h'): 382 genHeader(options) 383else: 384 genConstraints(options) 385 genBuildOptions(options) 386