summaryrefslogtreecommitdiff
path: root/backend/scripts
diff options
context:
space:
mode:
authorJörg Frings-Fürst <debian@jff-webhosting.net>2019-07-31 17:00:58 +0200
committerJörg Frings-Fürst <debian@jff-webhosting.net>2019-07-31 17:00:58 +0200
commitd087d778ffb62e2713cb848540bcc819470365bd (patch)
treed69692a274dd1c7d0672e6bb7155a0fc106f9d49 /backend/scripts
parentc8bd2513ecba169cff44c09c8058c36987357b18 (diff)
parent3759ce55ba79b8d3b9d8ed247a252273ee7dade3 (diff)
Merge branch 'feature/upstream' into develop
Diffstat (limited to 'backend/scripts')
-rwxr-xr-xbackend/scripts/pixma_gen_options.py389
1 files changed, 389 insertions, 0 deletions
diff --git a/backend/scripts/pixma_gen_options.py b/backend/scripts/pixma_gen_options.py
new file mode 100755
index 0000000..c4c75e0
--- /dev/null
+++ b/backend/scripts/pixma_gen_options.py
@@ -0,0 +1,389 @@
+#!/usr/bin/env python
+
+import sys,os,re
+
+class Error(Exception):
+ pass
+
+
+class ParseError(Error):
+ def __init__(self, errline):
+ Error.__init__(self, errline)
+
+
+class Struct:
+ pass
+
+
+def createCNameMap():
+ t = ''
+ for i in range(256):
+ if ((ord('A') <= i) and (i <= ord('Z'))) or \
+ ((ord('a') <= i) and (i <= ord('z'))) or \
+ ((ord('0') <= i) and (i <= ord('9'))):
+ t += chr(i)
+ else:
+ t += '_'
+ return t
+
+
+def seekBegin(f):
+ while True:
+ line = f.readline()
+ if not line:
+ return False
+ if line.startswith('BEGIN SANE_Option_Descriptor'):
+ return True
+
+
+def parseVerbatim(o, line):
+ words = line.split(None, 1)
+ if (len(words) < 2) or (words[1][0] != '@'):
+ return False
+ o[words[0]] = words[1]
+ return True
+
+
+def parseLine_type(o, line):
+ words = line.split(None, 2)
+ otype = words[1]
+ o['type'] = 'SANE_TYPE_' + otype.upper()
+ if otype == 'group':
+ g.ngroups += 1
+ oname = '_group_%d' % g.ngroups
+ o['size'] = 0
+ else:
+ temp = words[2]
+ idx = temp.find('[')
+ if idx == -1:
+ oname = temp
+ o['size'] = 1
+ else:
+ oname = temp[0:idx]
+ o['size'] = int(temp[idx+1:-1])
+ o['name'] = oname
+
+
+def parseLine_title(o, line):
+ o['title'] = line.split(None, 1)[1]
+
+
+def parseLine_desc(o, line):
+ o['desc'] = line.split(None, 1)[1]
+
+
+def parseLine_unit(o, line):
+ o['unit'] = 'SANE_UNIT_' + line.split(None, 1)[1].upper()
+
+
+def parseLine_default(o, line):
+ o['default'] = line.split(None, 1)[1]
+
+
+def parseLine_cap(o, line):
+ words = line.split()
+ o['cap'] = ['SANE_CAP_' + s.upper() for s in words[1:]]
+
+
+def parseLine_constraint(o, line):
+ c = line.split(None,1)[1]
+ if c[0] == '{':
+ o['constraint'] = c[1:-1].split('|')
+ elif c[0] == '(':
+ o['constraint'] = tuple(c[1:-1].split(','))
+ else:
+ sys.stderr.write('Ignored: %s\n' % line)
+
+
+def parseLine_info(o, line):
+ words = line.split()
+ o['info'] = ['SANE_INFO_' + s.upper() for s in words[1:]]
+
+def parseLine_rem(o, line):
+ pass
+
+def normalize(o):
+ if 'cname' not in o:
+ cname = o['name'].translate(cnameMap)
+ o['cname'] = cname
+ else:
+ cname = o['cname']
+ o['cname_opt'] = 'opt_' + cname
+ o['cname_con'] = 'constraint_' + cname
+ if 'title' not in o:
+ o['title'] = 'NO TITLE'
+ if 'desc' not in o:
+ o['desc'] = '@sod->title' % o
+ if 'unit' not in o:
+ o['unit'] = 'SANE_UNIT_NONE'
+ if 'constraint_type' not in o:
+ if 'constraint' not in o:
+ ct = 'SANE_CONSTRAINT_NONE'
+ elif isinstance(o['constraint'], list):
+ if o['type'] == 'SANE_TYPE_STRING':
+ ct = 'SANE_CONSTRAINT_STRING_LIST'
+ else:
+ ct = 'SANE_CONSTRAINT_WORD_LIST'
+ elif isinstance(o['constraint'], tuple):
+ ct = 'SANE_CONSTRAINT_RANGE'
+ elif isinstance(o['constraint'], str):
+ oc = o['constraint']
+ if oc.startswith('@range'):
+ ct = 'SANE_CONSTRAINT_RANGE'
+ elif oc.startswith('@word_list'):
+ ct = 'SANE_CONSTRAINT_WORD_LIST'
+ elif oc.startswith('@string_list'):
+ ct = 'SANE_CONSTRAINT_STRING_LIST'
+ o['constraint_type'] = ct
+ return o
+
+
+def parseFile(f):
+ if not seekBegin(f):
+ return None
+ options = [ {
+ 'name' : '',
+ 'cname' : 'opt_num_opts',
+ 'title' : '@SANE_TITLE_NUM_OPTIONS',
+ 'desc' : '@SANE_DESC_NUM_OPTIONS',
+ 'type' : 'SANE_TYPE_INT',
+ 'unit' : 'SANE_UNIT_NONE',
+ 'size' : 1,
+ 'cap' : ['SANE_CAP_SOFT_DETECT'],
+ 'constraint_type' : 'SANE_CONSTRAINT_NONE',
+ 'default' : '@w = ' + opt_prefix + 'last'
+ } ]
+ o = {}
+ while True:
+ line = f.readline()
+ if not line:
+ break
+ line = line.strip()
+ if not line:
+ continue
+ token = line.split(None, 1)[0].lower()
+ if token == 'end':
+ break
+ if token == 'type':
+ if 'name' in o:
+ options.append(o)
+ o = {}
+ funcName = 'parseLine_' + token
+ if funcName in globals():
+ if not parseVerbatim(o, line):
+ func = globals()[funcName]
+ func(o, line)
+ else:
+ sys.stderr.write('Skip: %s\n' % line)
+ if 'name' in o:
+ options.append(o)
+ return [normalize(o) for o in options]
+
+
+def genHeader(options):
+ print """
+typedef union {
+ SANE_Word w;
+ SANE_Int i;
+ SANE_Bool b;
+ SANE_Fixed f;
+ SANE_String s;
+ void *ptr;
+} option_value_t;
+"""
+ print 'typedef enum {'
+ for o in options:
+ print ' %(cname_opt)s,' % o
+ print ' ' + opt_prefix + 'last'
+ print '} option_t;'
+ print """
+
+typedef struct {
+ SANE_Option_Descriptor sod;
+ option_value_t val,def;
+ SANE_Word info;
+} option_descriptor_t;
+
+
+struct pixma_sane_t;
+static int build_option_descriptors(struct pixma_sane_t *ss);
+"""
+
+
+def genMinMaxRange(n, t, r):
+ if t == 'SANE_TYPE_FIXED':
+ r = ['SANE_FIX(%s)' % x for x in r]
+ print 'static const SANE_Range ' + n + ' = '
+ print ' { ' + r[0] + ',' + r[1] + ',' + r[2] + ' };'
+
+
+def genList(n, t, l):
+ if t == 'SANE_TYPE_INT':
+ etype = 'SANE_Word'
+ l = [str(len(l))] + l
+ elif t == 'SANE_TYPE_FIXED':
+ etype = 'SANE_Word'
+ l = [str(len(l))] + ['SANE_FIX(%s)' % x for x in l]
+ elif t == 'SANE_TYPE_STRING':
+ etype = 'SANE_String_Const'
+ l = ['SANE_I18N("%s")' % x for x in l] + ['NULL']
+ print 'static const %s %s[%d] = {' % (etype, n, len(l))
+ for x in l[0:-1]:
+ print '\t' + x + ','
+ print '\t' + l[-1] + ' };'
+
+
+def genConstraints(options):
+ for o in options:
+ if 'constraint' not in o: continue
+ c = o['constraint']
+ oname = o['cname_con']
+ otype = o['type']
+ if isinstance(c, tuple):
+ genMinMaxRange(oname, otype, c)
+ elif isinstance(c, list):
+ genList(oname, otype, c)
+ print
+
+def buildCodeVerbatim(o):
+ for f in ('name', 'title', 'desc', 'type', 'unit', 'size', 'cap',
+ 'constraint_type', 'constraint', 'default'):
+ if (f not in o): continue
+ temp = o[f]
+ if (not isinstance(temp,str)) or \
+ (len(temp) < 1) or (temp[0] != '@'):
+ continue
+ o['code_' + f] = temp[1:]
+
+def ccode(o):
+ buildCodeVerbatim(o)
+ if 'code_name' not in o:
+ o['code_name'] = '"' + o['name'] + '"'
+ for f in ('title', 'desc'):
+ cf = 'code_' + f
+ if cf in o: continue
+ o[cf] = 'SANE_I18N("' + o[f] + '")'
+
+ for f in ('type', 'unit', 'constraint_type'):
+ cf = 'code_' + f
+ if cf in o: continue
+ o[cf] = o[f]
+
+ if 'code_size' not in o:
+ otype = o['type']
+ osize = o['size']
+ if otype == 'SANE_TYPE_STRING':
+ code = str(osize + 1)
+ elif otype == 'SANE_TYPE_INT' or otype == 'SANE_TYPE_FIXED':
+ code = str(osize) + ' * sizeof(SANE_Word)'
+ elif otype == 'SANE_TYPE_BUTTON':
+ code = '0'
+ else:
+ code = 'sizeof(SANE_Word)'
+ o['code_size'] = code
+
+ if ('code_cap' not in o) and ('cap' in o):
+ o['code_cap'] = reduce(lambda a,b: a+'|'+b, o['cap'])
+ else:
+ o['code_cap'] = '0'
+
+ if ('code_info' not in o) and ('info' in o):
+ o['code_info'] = reduce(lambda a,b: a+'|'+b, o['info'])
+ else:
+ o['code_info'] = '0'
+
+ if ('code_default' not in o) and ('default' in o):
+ odefault = o['default']
+ otype = o['type']
+ if odefault == '_MIN':
+ rhs = 'w = sod->constraint.range->min'
+ elif odefault == '_MAX':
+ rhs = 'w = sod->constraint.range->max'
+ elif otype in ('SANE_TYPE_INT', 'SANE_TYPE_BOOL'):
+ rhs = 'w = %(default)s'
+ elif otype == 'SANE_TYPE_FIXED':
+ rhs = 'w = SANE_FIX(%(default)s)'
+ elif otype == 'SANE_TYPE_STRING':
+ rhs = 's = SANE_I18N("%(default)s")'
+ o['code_default'] = rhs % o
+ if 'code_default' in o:
+ code = ' opt->def.%(code_default)s;\n'
+ if o['constraint_type'] != 'SANE_CONSTRAINT_STRING_LIST':
+ code += ' opt->val.%(code_default)s;\n'
+ else:
+ code += ' opt->val.w = find_string_in_list' \
+ '(opt->def.s, sod->constraint.string_list);\n'
+ o['full_code_default'] = code % o
+ else:
+ o['full_code_default'] = ''
+
+ if ('code_constraint' not in o) and ('constraint' in o):
+ ct = o['constraint_type']
+ idx = len('SANE_CONSTRAINT_')
+ ctype = ct[idx:].lower()
+ if ctype == 'range':
+ rhs = '&%(cname_con)s' % o
+ else:
+ rhs = '%(cname_con)s' % o
+ o['code_constraint'] = ctype + ' = ' + rhs
+ if 'code_constraint' in o:
+ code = ' sod->constraint.%(code_constraint)s;\n'
+ o['full_code_constraint'] = code % o
+ else:
+ o['full_code_constraint'] = ''
+
+ return o
+
+def genBuildOptions(options):
+ print """
+static
+int find_string_in_list(SANE_String_Const str, const SANE_String_Const *list)
+{
+ int i;
+ for (i = 0; list[i] && strcmp(str, list[i]) != 0; i++) {}
+ return i;
+}
+
+static
+int build_option_descriptors(struct pixma_sane_t *ss)
+{
+ SANE_Option_Descriptor *sod;
+ option_descriptor_t *opt;
+
+ memset(OPT_IN_CTX, 0, sizeof(OPT_IN_CTX));"""
+
+ for o in options:
+ o = ccode(o)
+ otype = o['type']
+ code = '\n opt = &(OPT_IN_CTX[%(cname_opt)s]);\n' \
+ ' sod = &opt->sod;\n' \
+ ' sod->type = %(code_type)s;\n' \
+ ' sod->title = %(code_title)s;\n' \
+ ' sod->desc = %(code_desc)s;\n'
+ if otype != 'SANE_TYPE_GROUP':
+ code += ' sod->name = %(code_name)s;\n' \
+ ' sod->unit = %(code_unit)s;\n' \
+ ' sod->size = %(code_size)s;\n' \
+ ' sod->cap = %(code_cap)s;\n' \
+ ' sod->constraint_type = %(code_constraint_type)s;\n' \
+ '%(full_code_constraint)s' \
+ ' OPT_IN_CTX[%(cname_opt)s].info = %(code_info)s;\n' \
+ '%(full_code_default)s'
+ sys.stdout.write(code % o)
+ print
+ print ' return 0;\n'
+ print '}'
+ print
+
+g = Struct()
+g.ngroups = 0
+opt_prefix = 'opt_'
+con_prefix = 'constraint_'
+cnameMap = createCNameMap()
+options = parseFile(sys.stdin)
+print "/* Automatically generated from pixma_sane.c */"
+if (len(sys.argv) == 2) and (sys.argv[1] == 'h'):
+ genHeader(options)
+else:
+ genConstraints(options)
+ genBuildOptions(options)