1#!/usr/local/bin/python3.8
2# vim:fileencoding=utf-8
3
4'''
5Created on 13 Jan 2011
6
7@author: charles
8'''
9
10
11__license__   = 'GPL v3'
12__copyright__ = '2010, Kovid Goyal <kovid@kovidgoyal.net>'
13__docformat__ = 'restructuredtext en'
14
15import inspect, re, traceback, numbers
16from datetime import datetime, timedelta
17from math import trunc, floor, ceil, modf
18from contextlib import suppress
19
20from calibre import human_readable, prints
21from calibre.constants import DEBUG
22from calibre.ebooks.metadata import title_sort
23from calibre.utils.config import tweaks
24from calibre.utils.titlecase import titlecase
25from calibre.utils.icu import capitalize, strcmp, sort_key
26from calibre.utils.date import parse_date, format_date, now, UNDEFINED_DATE
27from calibre.utils.localization import calibre_langcode_to_name, canonicalize_lang
28from polyglot.builtins import iteritems, itervalues
29
30
31class FormatterFunctions:
32
33    error_function_body = ('def evaluate(self, formatter, kwargs, mi, locals):\n'
34                       '\treturn "' +
35                            _('Duplicate user function name {0}. '
36                              'Change the name or ensure that the functions are identical') + '"')
37
38    def __init__(self):
39        self._builtins = {}
40        self._functions = {}
41        self._functions_from_library = {}
42
43    def register_builtin(self, func_class):
44        if not isinstance(func_class, FormatterFunction):
45            raise ValueError('Class %s is not an instance of FormatterFunction'%(
46                                    func_class.__class__.__name__))
47        name = func_class.name
48        if name in self._functions:
49            raise ValueError('Name %s already used'%name)
50        self._builtins[name] = func_class
51        self._functions[name] = func_class
52        for a in func_class.aliases:
53            self._functions[a] = func_class
54
55    def _register_function(self, func_class, replace=False):
56        if not isinstance(func_class, FormatterFunction):
57            raise ValueError('Class %s is not an instance of FormatterFunction'%(
58                                    func_class.__class__.__name__))
59        name = func_class.name
60        if not replace and name in self._functions:
61            raise ValueError('Name %s already used'%name)
62        self._functions[name] = func_class
63
64    def register_functions(self, library_uuid, funcs):
65        self._functions_from_library[library_uuid] = funcs
66        self._register_functions()
67
68    def _register_functions(self):
69        for compiled_funcs in itervalues(self._functions_from_library):
70            for cls in compiled_funcs:
71                f = self._functions.get(cls.name, None)
72                replace = False
73                if f is not None:
74                    existing_body = f.program_text
75                    new_body = cls.program_text
76                    if new_body != existing_body:
77                        # Change the body of the template function to one that will
78                        # return an error message. Also change the arg count to
79                        # -1 (variable) to avoid template compilation errors
80                        replace = True
81                        func = [cls.name, '', -1, self.error_function_body.format(cls.name)]
82                        cls = compile_user_function(*func)
83                    else:
84                        continue
85                formatter_functions()._register_function(cls, replace=replace)
86
87    def unregister_functions(self, library_uuid):
88        if library_uuid in self._functions_from_library:
89            for cls in self._functions_from_library[library_uuid]:
90                self._functions.pop(cls.name, None)
91            self._functions_from_library.pop(library_uuid)
92            self._register_functions()
93
94    def get_builtins(self):
95        return self._builtins
96
97    def get_builtins_and_aliases(self):
98        res = {}
99        for f in itervalues(self._builtins):
100            res[f.name] = f
101            for a in f.aliases:
102                res[a] = f
103        return res
104
105    def get_functions(self):
106        return self._functions
107
108    def reset_to_builtins(self):
109        self._functions = {}
110        for n,c in self._builtins.items():
111            self._functions[n] = c
112            for a in c.aliases:
113                self._functions[a] = c
114
115
116_ff = FormatterFunctions()
117
118
119def formatter_functions():
120    global _ff
121    return _ff
122
123
124class FormatterFunction:
125
126    doc = _('No documentation provided')
127    name = 'no name provided'
128    category = 'Unknown'
129    arg_count = 0
130    aliases = []
131    is_python = True
132
133    def evaluate(self, formatter, kwargs, mi, locals, *args):
134        raise NotImplementedError()
135
136    def eval_(self, formatter, kwargs, mi, locals, *args):
137        ret = self.evaluate(formatter, kwargs, mi, locals, *args)
138        if isinstance(ret, (bytes, str)):
139            return ret
140        if isinstance(ret, list):
141            return ','.join(ret)
142        if isinstance(ret, (numbers.Number, bool)):
143            return str(ret)
144
145
146class BuiltinFormatterFunction(FormatterFunction):
147
148    def __init__(self):
149        formatter_functions().register_builtin(self)
150        eval_func = inspect.getmembers(self.__class__,
151                        lambda x: inspect.isfunction(x) and x.__name__ == 'evaluate')
152        try:
153            lines = [l[4:] for l in inspect.getsourcelines(eval_func[0][1])[0]]
154        except:
155            lines = []
156        self.program_text = ''.join(lines)
157
158
159class BuiltinStrcmp(BuiltinFormatterFunction):
160    name = 'strcmp'
161    arg_count = 5
162    category = 'Relational'
163    __doc__ = doc = _('strcmp(x, y, lt, eq, gt) -- does a case-insensitive comparison of x '
164            'and y as strings. Returns lt if x < y. Returns eq if x == y. '
165            'Otherwise returns gt. In many cases the lexical comparison operators '
166            '(>, <, == etc) can replace this function.')
167
168    def evaluate(self, formatter, kwargs, mi, locals, x, y, lt, eq, gt):
169        v = strcmp(x, y)
170        if v < 0:
171            return lt
172        if v == 0:
173            return eq
174        return gt
175
176
177class BuiltinCmp(BuiltinFormatterFunction):
178    name = 'cmp'
179    category = 'Relational'
180    arg_count = 5
181    __doc__ = doc =   _('cmp(x, y, lt, eq, gt) -- compares x and y after converting both to '
182            'numbers. Returns lt if x < y. Returns eq if x == y. Otherwise returns gt. '
183            'In many cases the numeric comparison operators '
184            '(>#, <#, ==# etc) can replace this function.')
185
186    def evaluate(self, formatter, kwargs, mi, locals, x, y, lt, eq, gt):
187        x = float(x if x and x != 'None' else 0)
188        y = float(y if y and y != 'None' else 0)
189        if x < y:
190            return lt
191        if x == y:
192            return eq
193        return gt
194
195
196class BuiltinFirstMatchingCmp(BuiltinFormatterFunction):
197    name = 'first_matching_cmp'
198    category = 'Relational'
199    arg_count = -1
200    __doc__ = doc =   _('first_matching_cmp(val, [cmp1, result1,]+, else_result) -- '
201            'compares "val < cmpN" in sequence, returning resultN for '
202            'the first comparison that succeeds. Returns else_result '
203            'if no comparison succeeds. Example: '
204            'first_matching_cmp(10,5,"small",10,"middle",15,"large","giant") '
205            'returns "large". The same example with a first value of 16 returns "giant".')
206
207    def evaluate(self, formatter, kwargs, mi, locals, *args):
208        if (len(args) % 2) != 0:
209            raise ValueError(_('first_matching_cmp requires an even number of arguments'))
210        val = float(args[0] if args[0] and args[0] != 'None' else 0)
211        for i in range(1, len(args) - 1, 2):
212            c = float(args[i] if args[i] and args[i] != 'None' else 0)
213            if val < c:
214                return args[i+1]
215        return args[len(args)-1]
216
217
218class BuiltinStrcat(BuiltinFormatterFunction):
219    name = 'strcat'
220    arg_count = -1
221    category = 'String manipulation'
222    __doc__ = doc = _('strcat(a [, b]*) -- can take any number of arguments. Returns the '
223            'string formed by concatenating all the arguments')
224
225    def evaluate(self, formatter, kwargs, mi, locals, *args):
226        i = 0
227        res = ''
228        for i in range(0, len(args)):
229            res += args[i]
230        return res
231
232
233class BuiltinStrlen(BuiltinFormatterFunction):
234    name = 'strlen'
235    arg_count = 1
236    category = 'String manipulation'
237    __doc__ = doc = _('strlen(a) -- Returns the length of the string passed as '
238            'the argument')
239
240    def evaluate(self, formatter, kwargs, mi, locals, a):
241        try:
242            return len(a)
243        except:
244            return -1
245
246
247class BuiltinAdd(BuiltinFormatterFunction):
248    name = 'add'
249    arg_count = -1
250    category = 'Arithmetic'
251    __doc__ = doc = _('add(x [, y]*) -- returns the sum of its arguments. '
252                      'Throws an exception if an argument is not a number. '
253                      'This function can often be '
254                      'replaced with the + operator.')
255
256    def evaluate(self, formatter, kwargs, mi, locals, *args):
257        res = 0
258        for v in args:
259            v = float(v if v and v != 'None' else 0)
260            res += v
261        return str(res)
262
263
264class BuiltinSubtract(BuiltinFormatterFunction):
265    name = 'subtract'
266    arg_count = 2
267    category = 'Arithmetic'
268    __doc__ = doc = _('subtract(x, y) -- returns x - y. Throws an exception if '
269                      'either x or y are not numbers. This function can often be '
270                      'replaced with the - operator.')
271
272    def evaluate(self, formatter, kwargs, mi, locals, x, y):
273        x = float(x if x and x != 'None' else 0)
274        y = float(y if y and y != 'None' else 0)
275        return str(x - y)
276
277
278class BuiltinMultiply(BuiltinFormatterFunction):
279    name = 'multiply'
280    arg_count = -1
281    category = 'Arithmetic'
282    __doc__ = doc = _('multiply(x [, y]*) -- returns the product of its arguments. '
283                      'Throws an exception if any argument is not a number. '
284                      'This function can often be replaced with the * operator.')
285
286    def evaluate(self, formatter, kwargs, mi, locals, *args):
287        res = 1
288        for v in args:
289            v = float(v if v and v != 'None' else 0)
290            res *= v
291        return str(res)
292
293
294class BuiltinDivide(BuiltinFormatterFunction):
295    name = 'divide'
296    arg_count = 2
297    category = 'Arithmetic'
298    __doc__ = doc = _('divide(x, y) -- returns x / y. Throws an exception if '
299                      'either x or y are not numbers.'
300                      ' This function can often be replaced with the / operator.')
301
302    def evaluate(self, formatter, kwargs, mi, locals, x, y):
303        x = float(x if x and x != 'None' else 0)
304        y = float(y if y and y != 'None' else 0)
305        return str(x / y)
306
307
308class BuiltinCeiling(BuiltinFormatterFunction):
309    name = 'ceiling'
310    arg_count = 1
311    category = 'Arithmetic'
312    __doc__ = doc = _('ceiling(x) -- returns the smallest integer greater '
313                      'than or equal to x. Throws an exception if x is '
314                      'not a number.')
315
316    def evaluate(self, formatter, kwargs, mi, locals, x):
317        x = float(x if x and x != 'None' else 0)
318        return str(int(ceil(x)))
319
320
321class BuiltinFloor(BuiltinFormatterFunction):
322    name = 'floor'
323    arg_count = 1
324    category = 'Arithmetic'
325    __doc__ = doc = _('floor(x) -- returns the largest integer less '
326                      'than or equal to x. Throws an exception if x is '
327                      'not a number.')
328
329    def evaluate(self, formatter, kwargs, mi, locals, x):
330        x = float(x if x and x != 'None' else 0)
331        return str(int(floor(x)))
332
333
334class BuiltinRound(BuiltinFormatterFunction):
335    name = 'round'
336    arg_count = 1
337    category = 'Arithmetic'
338    __doc__ = doc = _('round(x) -- returns the nearest integer to x. '
339                      'Throws an exception if x is not a number.')
340
341    def evaluate(self, formatter, kwargs, mi, locals, x):
342        x = float(x if x and x != 'None' else 0)
343        return str(int(round(x)))
344
345
346class BuiltinMod(BuiltinFormatterFunction):
347    name = 'mod'
348    arg_count = 2
349    category = 'Arithmetic'
350    __doc__ = doc = _('mod(x) -- returns floor(remainder of x / y). '
351                      'Throws an exception if either x or y is not a number.')
352
353    def evaluate(self, formatter, kwargs, mi, locals, x, y):
354        x = float(x if x and x != 'None' else 0)
355        y = float(y if y and y != 'None' else 0)
356        return str(int(x % y))
357
358
359class BuiltinFractionalPart(BuiltinFormatterFunction):
360    name = 'fractional_part'
361    arg_count = 1
362    category = 'Arithmetic'
363    __doc__ = doc = _('fractional_part(x) -- returns the value after the decimal '
364                      'point.  For example, fractional_part(3.14) returns 0.14. '
365                      'Throws an exception if x is not a number.')
366
367    def evaluate(self, formatter, kwargs, mi, locals, x):
368        x = float(x if x and x != 'None' else 0)
369        return str(modf(x)[0])
370
371
372class BuiltinTemplate(BuiltinFormatterFunction):
373    name = 'template'
374    arg_count = 1
375    category = 'Recursion'
376
377    __doc__ = doc = _('template(x) -- evaluates x as a template. The evaluation is done '
378            'in its own context, meaning that variables are not shared between '
379            'the caller and the template evaluation. Because the { and } '
380            'characters are special, you must use [[ for the { character and '
381            ']] for the } character; they are converted automatically. '
382            'For example, template(\'[[title_sort]]\') will evaluate the '
383            'template {title_sort} and return its value. Note also that '
384            'prefixes and suffixes (the `|prefix|suffix` syntax) cannot be '
385            'used in the argument to this function when using template program mode.')
386
387    def evaluate(self, formatter, kwargs, mi, locals, template):
388        template = template.replace('[[', '{').replace(']]', '}')
389        return formatter.__class__().safe_format(template, kwargs, 'TEMPLATE', mi)
390
391
392class BuiltinEval(BuiltinFormatterFunction):
393    name = 'eval'
394    arg_count = 1
395    category = 'Recursion'
396    __doc__ = doc = _('eval(template) -- evaluates the template, passing the local '
397            'variables (those \'assign\'ed to) instead of the book metadata. '
398            ' This permits using the template processor to construct complex '
399            'results from local variables. Because the { and } '
400            'characters are special, you must use [[ for the { character and '
401            ']] for the } character; they are converted automatically. '
402            'Note also that prefixes and suffixes (the `|prefix|suffix` syntax) '
403            'cannot be used in the argument to this function when using '
404            'template program mode.')
405
406    def evaluate(self, formatter, kwargs, mi, locals, template):
407        from calibre.utils.formatter import EvalFormatter
408        template = template.replace('[[', '{').replace(']]', '}')
409        return EvalFormatter().safe_format(template, locals, 'EVAL', None)
410
411
412class BuiltinAssign(BuiltinFormatterFunction):
413    name = 'assign'
414    arg_count = 2
415    category = 'Other'
416    __doc__ = doc = _('assign(id, val) -- assigns val to id, then returns val. '
417            'id must be an identifier, not an expression. '
418            'This function can often be replaced with the = operator.')
419
420    def evaluate(self, formatter, kwargs, mi, locals, target, value):
421        locals[target] = value
422        return value
423
424
425class BuiltinListSplit(BuiltinFormatterFunction):
426    name = 'list_split'
427    arg_count = 3
428    category = 'List manipulation'
429    __doc__ = doc = _('list_split(list_val, sep, id_prefix) -- splits the list_val '
430                    "into separate values using 'sep', then assigns the values "
431                    "to variables named 'id_prefix_N' where N is the position "
432                    "of the value in the list. The first item has position 0 (zero). "
433                    "The function returns the last element in the list. "
434                    "Example: split('one:two:foo', ':', 'var') is equivalent "
435                    "to var_0 = 'one'; var_1 = 'two'; var_2 = 'foo'.")
436
437    def evaluate(self, formatter, kwargs, mi, locals, list_val, sep, id_prefix):
438        l = [v.strip() for v in list_val.split(sep)]
439        res = ''
440        for i,v in enumerate(l):
441            res = locals[id_prefix+'_'+str(i)] = v
442        return res
443
444
445class BuiltinPrint(BuiltinFormatterFunction):
446    name = 'print'
447    arg_count = -1
448    category = 'Other'
449    __doc__ = doc = _('print(a[, b]*) -- prints the arguments to standard output. '
450            'Unless you start calibre from the command line (calibre-debug -g), '
451            'the output will go to a black hole.')
452
453    def evaluate(self, formatter, kwargs, mi, locals, *args):
454        print(args)
455        return ''
456
457
458class BuiltinField(BuiltinFormatterFunction):
459    name = 'field'
460    arg_count = 1
461    category = 'Get values from metadata'
462    __doc__ = doc = _('field(lookup_name) -- returns the metadata field named by lookup_name')
463
464    def evaluate(self, formatter, kwargs, mi, locals, name):
465        return formatter.get_value(name, [], kwargs)
466
467
468class BuiltinRawField(BuiltinFormatterFunction):
469    name = 'raw_field'
470    arg_count = -1
471    category = 'Get values from metadata'
472    __doc__ = doc = _('raw_field(lookup_name [, optional_default]) -- returns the '
473            'metadata field named by lookup_name without applying any formatting. '
474            'It evaluates and returns the optional second argument '
475            "'default' if the field is undefined ('None').")
476
477    def evaluate(self, formatter, kwargs, mi, locals, name, default=None):
478        res = getattr(mi, name, None)
479        if res is None and default is not None:
480            return default
481        if isinstance(res, list):
482            fm = mi.metadata_for_field(name)
483            if fm is None:
484                return ', '.join(res)
485            return fm['is_multiple']['list_to_ui'].join(res)
486        return str(res)
487
488
489class BuiltinRawList(BuiltinFormatterFunction):
490    name = 'raw_list'
491    arg_count = 2
492    category = 'Get values from metadata'
493    __doc__ = doc = _('raw_list(lookup_name, separator) -- returns the metadata list '
494            'named by lookup_name without applying any formatting or sorting and '
495            'with items separated by separator.')
496
497    def evaluate(self, formatter, kwargs, mi, locals, name, separator):
498        res = getattr(mi, name, None)
499        if not isinstance(res, list):
500            return "%s is not a list" % name
501        return separator.join(res)
502
503
504class BuiltinSubstr(BuiltinFormatterFunction):
505    name = 'substr'
506    arg_count = 3
507    category = 'String manipulation'
508    __doc__ = doc = _('substr(str, start, end) -- returns the start\'th through the end\'th '
509            'characters of str. The first character in str is the zero\'th '
510            'character. If end is negative, then it indicates that many '
511            'characters counting from the right. If end is zero, then it '
512            'indicates the last character. For example, substr(\'12345\', 1, 0) '
513            'returns \'2345\', and substr(\'12345\', 1, -1) returns \'234\'.')
514
515    def evaluate(self, formatter, kwargs, mi, locals, str_, start_, end_):
516        return str_[int(start_): len(str_) if int(end_) == 0 else int(end_)]
517
518
519class BuiltinLookup(BuiltinFormatterFunction):
520    name = 'lookup'
521    arg_count = -1
522    category = 'Iterating over values'
523    __doc__ = doc = _('lookup(val, [pattern, field,]+ else_field) -- '
524            'like switch, except the arguments are field (metadata) names, not '
525            'text. The value of the appropriate field will be fetched and used. '
526            'Note that because composite columns are fields, you can use this '
527            'function in one composite field to use the value of some other '
528            'composite field. This is extremely useful when constructing '
529            'variable save paths')
530
531    def evaluate(self, formatter, kwargs, mi, locals, val, *args):
532        if len(args) == 2:  # here for backwards compatibility
533            if val:
534                return formatter.vformat('{'+args[0].strip()+'}', [], kwargs)
535            else:
536                return formatter.vformat('{'+args[1].strip()+'}', [], kwargs)
537        if (len(args) % 2) != 1:
538            raise ValueError(_('lookup requires either 2 or an odd number of arguments'))
539        i = 0
540        while i < len(args):
541            if i + 1 >= len(args):
542                return formatter.vformat('{' + args[i].strip() + '}', [], kwargs)
543            if re.search(args[i], val, flags=re.I):
544                return formatter.vformat('{'+args[i+1].strip() + '}', [], kwargs)
545            i += 2
546
547
548class BuiltinTest(BuiltinFormatterFunction):
549    name = 'test'
550    arg_count = 3
551    category = 'If-then-else'
552    __doc__ = doc = _('test(val, text if not empty, text if empty) -- return `text if not '
553            'empty` if val is not empty, otherwise return `text if empty`')
554
555    def evaluate(self, formatter, kwargs, mi, locals, val, value_if_set, value_not_set):
556        if val:
557            return value_if_set
558        else:
559            return value_not_set
560
561
562class BuiltinContains(BuiltinFormatterFunction):
563    name = 'contains'
564    arg_count = 4
565    category = 'If-then-else'
566    __doc__ = doc = _('contains(val, pattern, text if match, text if not match) -- checks '
567            'if val contains matches for the regular expression `pattern`. '
568            'Returns `text if match` if matches are found, otherwise it returns '
569            '`text if no match`')
570
571    def evaluate(self, formatter, kwargs, mi, locals,
572                 val, test, value_if_present, value_if_not):
573        if re.search(test, val, flags=re.I):
574            return value_if_present
575        else:
576            return value_if_not
577
578
579class BuiltinSwitch(BuiltinFormatterFunction):
580    name = 'switch'
581    arg_count = -1
582    category = 'Iterating over values'
583    __doc__ = doc = _('switch(val, [pattern, value,]+ else_value) -- '
584            'for each `pattern, value` pair, checks if `val` matches '
585            'the regular expression `pattern` and if so, returns that '
586            '`value`. If no pattern matches, then `else_value` is returned. '
587            'You can have as many `pattern, value` pairs as you want')
588
589    def evaluate(self, formatter, kwargs, mi, locals, val, *args):
590        if (len(args) % 2) != 1:
591            raise ValueError(_('switch requires an odd number of arguments'))
592        i = 0
593        while i < len(args):
594            if i + 1 >= len(args):
595                return args[i]
596            if re.search(args[i], val, flags=re.I):
597                return args[i+1]
598            i += 2
599
600
601class BuiltinStrcatMax(BuiltinFormatterFunction):
602    name = 'strcat_max'
603    arg_count = -1
604    category = 'String manipulation'
605    __doc__ = doc = _('strcat_max(max, string1 [, prefix2, string2]*) -- '
606            'Returns a string formed by concatenating the arguments. The '
607            'returned value is initialized to string1. `Prefix, string` '
608            'pairs are added to the end of the value as long as the '
609            'resulting string length is less than `max`. String1 is returned '
610            'even if string1 is longer than max. You can pass as many '
611            '`prefix, string` pairs as you wish.')
612
613    def evaluate(self, formatter, kwargs, mi, locals, *args):
614        if len(args) < 2:
615            raise ValueError(_('strcat_max requires 2 or more arguments'))
616        if (len(args) % 2) != 0:
617            raise ValueError(_('strcat_max requires an even number of arguments'))
618        try:
619            max = int(args[0])
620        except:
621            raise ValueError(_('first argument to strcat_max must be an integer'))
622
623        i = 2
624        result = args[1]
625        try:
626            while i < len(args):
627                if (len(result) + len(args[i]) + len(args[i+1])) > max:
628                    break
629                result = result + args[i] + args[i+1]
630                i += 2
631        except:
632            pass
633        return result.strip()
634
635
636class BuiltinInList(BuiltinFormatterFunction):
637    name = 'in_list'
638    arg_count = -1
639    category = 'List lookup'
640    __doc__ = doc = _('in_list(val, separator, [ pattern, found_val, ]+ not_found_val) -- '
641            'treating val as a list of items separated by separator, '
642            'if the pattern matches any of the list values then return found_val.'
643            'If the pattern matches no list value then return '
644            'not_found_val. The pattern and found_value pairs can be repeated as '
645            'many times as desired. The patterns are checked in order. The '
646            'found_val for the first match is returned. '
647            'Aliases: in_list(), list_contains()')
648    aliases = ['list_contains']
649
650    def evaluate(self, formatter, kwargs, mi, locals, val, sep, *args):
651        if (len(args) % 2) != 1:
652            raise ValueError(_('in_list requires an odd number of arguments'))
653        l = [v.strip() for v in val.split(sep) if v.strip()]
654        i = 0
655        while i < len(args):
656            if i + 1 >= len(args):
657                return args[i]
658            sf = args[i]
659            fv = args[i+1]
660            if l:
661                for v in l:
662                    if re.search(sf, v, flags=re.I):
663                        return fv
664            i += 2
665
666
667class BuiltinStrInList(BuiltinFormatterFunction):
668    name = 'str_in_list'
669    arg_count = -1
670    category = 'List lookup'
671    __doc__ = doc = _('str_in_list(val, separator, [string, found_val, ]+ not_found_val) -- '
672            'treating val as a list of items separated by separator, if the '
673            'string matches any of the list values then return found_val.'
674            'If the string matches no list value then return '
675            'not_found_val. The comparison is exact match (not contains) and is '
676            'case insensitive. The string and found_value pairs can be repeated as '
677            'many times as desired. The patterns are checked in order. The '
678            'found_val for the first match is returned.')
679
680    def evaluate(self, formatter, kwargs, mi, locals, val, sep, *args):
681        if (len(args) % 2) != 1:
682            raise ValueError(_('str_in_list requires an odd number of arguments'))
683        l = [v.strip() for v in val.split(sep) if v.strip()]
684        i = 0
685        while i < len(args):
686            if i + 1 >= len(args):
687                return args[i]
688            sf = args[i]
689            fv = args[i+1]
690            c = [v.strip() for v in sf.split(sep) if v.strip()]
691            if l:
692                for v in l:
693                    for t in c:
694                        if strcmp(t, v) == 0:
695                            return fv
696            i += 2
697
698
699class BuiltinIdentifierInList(BuiltinFormatterFunction):
700    name = 'identifier_in_list'
701    arg_count = 4
702    category = 'List lookup'
703    __doc__ = doc = _('identifier_in_list(val, id, found_val, not_found_val) -- '
704            'treat val as a list of identifiers separated by commas, '
705            'comparing the string against each value in the list. An identifier '
706            'has the format "identifier:value". The id parameter should be '
707            'either "id" or "id:regexp". The first case matches if there is any '
708            'identifier with that id. The second case matches if the regexp '
709            'matches the identifier\'s value. If there is a match, '
710            'return found_val, otherwise return not_found_val.')
711
712    def evaluate(self, formatter, kwargs, mi, locals, val, ident, fv, nfv):
713        l = [v.strip() for v in val.split(',') if v.strip()]
714        (id, _, regexp) = ident.partition(':')
715        if not id:
716            return nfv
717        id += ':'
718        if l:
719            for v in l:
720                if v.startswith(id):
721                    if not regexp or re.search(regexp, v[len(id):], flags=re.I):
722                        return fv
723        return nfv
724
725
726class BuiltinRe(BuiltinFormatterFunction):
727    name = 're'
728    arg_count = 3
729    category = 'String manipulation'
730    __doc__ = doc = _('re(val, pattern, replacement) -- return val after applying '
731            'the regular expression. All instances of `pattern` are replaced '
732            'with `replacement`. As in all of calibre, these are '
733            'Python-compatible regular expressions')
734
735    def evaluate(self, formatter, kwargs, mi, locals, val, pattern, replacement):
736        return re.sub(pattern, replacement, val, flags=re.I)
737
738
739class BuiltinReGroup(BuiltinFormatterFunction):
740    name = 're_group'
741    arg_count = -1
742    category = 'String manipulation'
743    __doc__ = doc = _('re_group(val, pattern [, template_for_group]*) -- '
744            'return a string made by applying the regular expression pattern '
745            'to the val and replacing each matched instance with the string '
746            'computed by replacing each matched group by the value returned '
747            'by the corresponding template. The original matched value for the '
748            'group is available as $. In template program mode, like for '
749            'the template and the eval functions, you use [[ for { and ]] for }.'
750            ' The following example in template program mode looks for series '
751            'with more than one word and uppercases the first word: '
752            "{series:'re_group($, \"(\\S* )(.*)\", \"[[$:uppercase()]]\", \"[[$]]\")'}")
753
754    def evaluate(self, formatter, kwargs, mi, locals, val, pattern, *args):
755        from calibre.utils.formatter import EvalFormatter
756
757        def repl(mo):
758            res = ''
759            if mo and mo.lastindex:
760                for dex in range(0, mo.lastindex):
761                    gv = mo.group(dex+1)
762                    if gv is None:
763                        continue
764                    if len(args) > dex:
765                        template = args[dex].replace('[[', '{').replace(']]', '}')
766                        res += EvalFormatter().safe_format(template, {'$': gv},
767                                           'EVAL', None, strip_results=False)
768                    else:
769                        res += gv
770            return res
771        return re.sub(pattern, repl, val, flags=re.I)
772
773
774class BuiltinSwapAroundComma(BuiltinFormatterFunction):
775    name = 'swap_around_comma'
776    arg_count = 1
777    category = 'String manipulation'
778    __doc__ = doc = _('swap_around_comma(val) -- given a value of the form '
779            '"B, A", return "A B". This is most useful for converting names '
780            'in LN, FN format to FN LN. If there is no comma, the function '
781            'returns val unchanged')
782
783    def evaluate(self, formatter, kwargs, mi, locals, val):
784        return re.sub(r'^(.*?),\s*(.*$)', r'\2 \1', val, flags=re.I).strip()
785
786
787class BuiltinIfempty(BuiltinFormatterFunction):
788    name = 'ifempty'
789    arg_count = 2
790    category = 'If-then-else'
791    __doc__ = doc = _('ifempty(val, text if empty) -- return val if val is not empty, '
792            'otherwise return `text if empty`')
793
794    def evaluate(self, formatter, kwargs, mi, locals, val, value_if_empty):
795        if val:
796            return val
797        else:
798            return value_if_empty
799
800
801class BuiltinShorten(BuiltinFormatterFunction):
802    name = 'shorten'
803    arg_count = 4
804    category = 'String manipulation'
805    __doc__ = doc = _('shorten(val, left chars, middle text, right chars) -- Return a '
806            'shortened version of val, consisting of `left chars` '
807            'characters from the beginning of val, followed by '
808            '`middle text`, followed by `right chars` characters from '
809            'the end of the string. `Left chars` and `right chars` must be '
810            'integers. For example, assume the title of the book is '
811            '`Ancient English Laws in the Times of Ivanhoe`, and you want '
812            'it to fit in a space of at most 15 characters. If you use '
813            '{title:shorten(9,-,5)}, the result will be `Ancient E-anhoe`. '
814            'If the field\'s length is less than left chars + right chars + '
815            'the length of `middle text`, then the field will be used '
816            'intact. For example, the title `The Dome` would not be changed.')
817
818    def evaluate(self, formatter, kwargs, mi, locals,
819                 val, leading, center_string, trailing):
820        l = max(0, int(leading))
821        t = max(0, int(trailing))
822        if len(val) > l + len(center_string) + t:
823            return val[0:l] + center_string + ('' if t == 0 else val[-t:])
824        else:
825            return val
826
827
828class BuiltinCount(BuiltinFormatterFunction):
829    name = 'count'
830    arg_count = 2
831    category = 'List manipulation'
832    aliases = ['list_count']
833
834    __doc__ = doc = _('count(val, separator) -- interprets the value as a list of items '
835            'separated by `separator`, returning the number of items in the '
836            'list. Most lists use a comma as the separator, but authors '
837            'uses an ampersand. Examples: {tags:count(,)}, {authors:count(&)}. '
838            'Aliases: count(), list_count()')
839
840    def evaluate(self, formatter, kwargs, mi, locals, val, sep):
841        return str(len([v for v in val.split(sep) if v]))
842
843
844class BuiltinListCountMatching(BuiltinFormatterFunction):
845    name = 'list_count_matching'
846    arg_count = 3
847    category = 'List manipulation'
848    aliases = ['count_matching']
849
850    __doc__ = doc = _('list_count_matching(list, pattern, separator) -- '
851            "interprets 'list' as a list of items separated by 'separator', "
852            'returning the number of items in the list that match the regular '
853            "expression 'pattern'. Aliases: list_count_matching(), count_matching()")
854
855    def evaluate(self, formatter, kwargs, mi, locals, list_, pattern, sep):
856        res = 0
857        for v in [x.strip() for x in list_.split(sep) if x.strip()]:
858            if re.search(pattern, v, flags=re.I):
859                res += 1
860        return str(res)
861
862
863class BuiltinListitem(BuiltinFormatterFunction):
864    name = 'list_item'
865    arg_count = 3
866    category = 'List lookup'
867    __doc__ = doc = _('list_item(val, index, separator) -- interpret the value as a list of '
868            'items separated by `separator`, returning the `index`th item. '
869            'The first item is number zero. The last item can be returned '
870            'using `list_item(-1,separator)`. If the item is not in the list, '
871            'then the empty value is returned. The separator has the same '
872            'meaning as in the count function.')
873
874    def evaluate(self, formatter, kwargs, mi, locals, val, index, sep):
875        if not val:
876            return ''
877        index = int(index)
878        val = val.split(sep)
879        try:
880            return val[index].strip()
881        except:
882            return ''
883
884
885class BuiltinSelect(BuiltinFormatterFunction):
886    name = 'select'
887    arg_count = 2
888    category = 'List lookup'
889    __doc__ = doc = _('select(val, key) -- interpret the value as a comma-separated list '
890            'of items, with the items being "id:value". Find the pair with the '
891            'id equal to key, and return the corresponding value. Returns the '
892            'empty string if no match is found.'
893            )
894
895    def evaluate(self, formatter, kwargs, mi, locals, val, key):
896        if not val:
897            return ''
898        vals = [v.strip() for v in val.split(',')]
899        tkey = key+':'
900        for v in vals:
901            if v.startswith(tkey):
902                return v[len(tkey):]
903        return ''
904
905
906class BuiltinApproximateFormats(BuiltinFormatterFunction):
907    name = 'approximate_formats'
908    arg_count = 0
909    category = 'Get values from metadata'
910    __doc__ = doc = _('approximate_formats() -- return a comma-separated '
911                  'list of formats that at one point were associated with the '
912                  'book. There is no guarantee that this list is correct, '
913                  'although it probably is. '
914                  'This function can be called in template program mode using '
915                  'the template "{:\'approximate_formats()\'}". '
916                  'Note that format names are always uppercase, as in EPUB. '
917                  'This function works only in the GUI. If you want to use these values '
918                  'in save-to-disk or send-to-device templates then you '
919                  'must make a custom "Column built from other columns", use '
920                  'the function in that column\'s template, and use that '
921                  'column\'s value in your save/send templates'
922            )
923
924    def evaluate(self, formatter, kwargs, mi, locals):
925        if hasattr(mi, '_proxy_metadata'):
926            fmt_data = mi._proxy_metadata.db_approx_formats
927            if not fmt_data:
928                return ''
929            data = sorted(fmt_data)
930            return ','.join(v.upper() for v in data)
931        return _('This function can be used only in the GUI')
932
933
934class BuiltinFormatsModtimes(BuiltinFormatterFunction):
935    name = 'formats_modtimes'
936    arg_count = 1
937    category = 'Get values from metadata'
938    __doc__ = doc = _('formats_modtimes(date_format) -- return a comma-separated '
939                  'list of colon-separated items representing modification times '
940                  'for the formats of a book. The date_format parameter '
941                  'specifies how the date is to be formatted. See the '
942                  'format_date function for details. You can use the select '
943                  'function to get the mod time for a specific '
944                  'format. Note that format names are always uppercase, '
945                  'as in EPUB.'
946            )
947
948    def evaluate(self, formatter, kwargs, mi, locals, fmt):
949        fmt_data = mi.get('format_metadata', {})
950        try:
951            data = sorted(fmt_data.items(), key=lambda x:x[1]['mtime'], reverse=True)
952            return ','.join(k.upper()+':'+format_date(v['mtime'], fmt)
953                        for k,v in data)
954        except:
955            return ''
956
957
958class BuiltinFormatsSizes(BuiltinFormatterFunction):
959    name = 'formats_sizes'
960    arg_count = 0
961    category = 'Get values from metadata'
962    __doc__ = doc = _('formats_sizes() -- return a comma-separated list of '
963                      'colon-separated items representing sizes in bytes '
964                      'of the formats of a book. You can use the select '
965                      'function to get the size for a specific '
966                      'format. Note that format names are always uppercase, '
967                      'as in EPUB.'
968            )
969
970    def evaluate(self, formatter, kwargs, mi, locals):
971        fmt_data = mi.get('format_metadata', {})
972        try:
973            return ','.join(k.upper()+':'+str(v['size']) for k,v in iteritems(fmt_data))
974        except:
975            return ''
976
977
978class BuiltinFormatsPaths(BuiltinFormatterFunction):
979    name = 'formats_paths'
980    arg_count = 0
981    category = 'Get values from metadata'
982    __doc__ = doc = _('formats_paths() -- return a comma-separated list of '
983                      'colon-separated items representing full path to '
984                      'the formats of a book. You can use the select '
985                      'function to get the path for a specific '
986                      'format. Note that format names are always uppercase, '
987                      'as in EPUB.')
988
989    def evaluate(self, formatter, kwargs, mi, locals):
990        fmt_data = mi.get('format_metadata', {})
991        try:
992            return ','.join(k.upper()+':'+str(v['path']) for k,v in iteritems(fmt_data))
993        except:
994            return ''
995
996
997class BuiltinHumanReadable(BuiltinFormatterFunction):
998    name = 'human_readable'
999    arg_count = 1
1000    category = 'Formatting values'
1001    __doc__ = doc = _('human_readable(v) -- return a string '
1002                      'representing the number v in KB, MB, GB, etc.'
1003            )
1004
1005    def evaluate(self, formatter, kwargs, mi, locals, val):
1006        try:
1007            return human_readable(round(float(val)))
1008        except:
1009            return ''
1010
1011
1012class BuiltinFormatNumber(BuiltinFormatterFunction):
1013    name = 'format_number'
1014    arg_count = 2
1015    category = 'Formatting values'
1016    __doc__ = doc = _('format_number(v, template) -- format the number v using '
1017                  'a Python formatting template such as "{0:5.2f}" or '
1018                  '"{0:,d}" or "${0:5,.2f}". The field_name part of the '
1019                  'template must be a 0 (zero) (the "{0:" in the above examples). '
1020                  'See the template language and Python documentation for more '
1021                  'examples. You can leave off the leading "{0:" and trailing '
1022                  '"}" if the template contains only a format. Returns the empty '
1023                  'string if formatting fails.'
1024            )
1025
1026    def evaluate(self, formatter, kwargs, mi, locals, val, template):
1027        if val == '' or val == 'None':
1028            return ''
1029        if '{' not in template:
1030            template = '{0:' + template + '}'
1031        try:
1032            v1 = float(val)
1033        except:
1034            return ''
1035        try:  # Try formatting the value as a float
1036            return template.format(v1)
1037        except:
1038            pass
1039        try:  # Try formatting the value as an int
1040            v2 = trunc(v1)
1041            if v2 == v1:
1042                return template.format(v2)
1043        except:
1044            pass
1045        return ''
1046
1047
1048class BuiltinSublist(BuiltinFormatterFunction):
1049    name = 'sublist'
1050    arg_count = 4
1051    category = 'List manipulation'
1052    __doc__ = doc = _('sublist(val, start_index, end_index, separator) -- interpret the '
1053            'value as a list of items separated by `separator`, returning a '
1054            'new list made from the `start_index` to the `end_index` item. '
1055            'The first item is number zero. If an index is negative, then it '
1056            'counts from the end of the list. As a special case, an end_index '
1057            'of zero is assumed to be the length of the list. Examples using '
1058            'basic template mode and assuming that the tags column (which is '
1059            'comma-separated) contains "A, B, C": '
1060            '{tags:sublist(0,1,\\\\,)} returns "A". '
1061            '{tags:sublist(-1,0,\\\\,)} returns "C". '
1062            '{tags:sublist(0,-1,\\\\,)} returns "A, B".'
1063            )
1064
1065    def evaluate(self, formatter, kwargs, mi, locals, val, start_index, end_index, sep):
1066        if not val:
1067            return ''
1068        si = int(start_index)
1069        ei = int(end_index)
1070        # allow empty list items so counts are what the user expects
1071        val = [v.strip() for v in val.split(sep)]
1072
1073        if sep == ',':
1074            sep = ', '
1075        try:
1076            if ei == 0:
1077                return sep.join(val[si:])
1078            else:
1079                return sep.join(val[si:ei])
1080        except:
1081            return ''
1082
1083
1084class BuiltinSubitems(BuiltinFormatterFunction):
1085    name = 'subitems'
1086    arg_count = 3
1087    category = 'List manipulation'
1088    __doc__ = doc = _('subitems(val, start_index, end_index) -- This function is used to '
1089            'break apart lists of items such as genres. It interprets the value '
1090            'as a comma-separated list of items, where each item is a period-'
1091            'separated list. Returns a new list made by first finding all the '
1092            'period-separated items, then for each such item extracting the '
1093            '`start_index` to the `end_index` components, then combining '
1094            'the results back together. The first component in a period-'
1095            'separated list has an index of zero. If an index is negative, '
1096            'then it counts from the end of the list. As a special case, an '
1097            'end_index of zero is assumed to be the length of the list. '
1098            'Example using basic template mode and assuming a #genre value of '
1099            '"A.B.C": {#genre:subitems(0,1)} returns "A". {#genre:subitems(0,2)} '
1100            'returns "A.B". {#genre:subitems(1,0)} returns "B.C". Assuming a #genre '
1101            'value of "A.B.C, D.E.F", {#genre:subitems(0,1)} returns "A, D". '
1102            '{#genre:subitems(0,2)} returns "A.B, D.E"')
1103
1104    period_pattern = re.compile(r'(?<=[^\.\s])\.(?=[^\.\s])', re.U)
1105
1106    def evaluate(self, formatter, kwargs, mi, locals, val, start_index, end_index):
1107        if not val:
1108            return ''
1109        si = int(start_index)
1110        ei = int(end_index)
1111        has_periods = '.' in val
1112        items = [v.strip() for v in val.split(',') if v.strip()]
1113        rv = set()
1114        for item in items:
1115            if has_periods and '.' in item:
1116                components = self.period_pattern.split(item)
1117            else:
1118                components = [item]
1119            try:
1120                if ei == 0:
1121                    t = '.'.join(components[si:]).strip()
1122                else:
1123                    t = '.'.join(components[si:ei]).strip()
1124                if t:
1125                    rv.add(t)
1126            except:
1127                pass
1128        return ', '.join(sorted(rv, key=sort_key))
1129
1130
1131class BuiltinFormatDate(BuiltinFormatterFunction):
1132    name = 'format_date'
1133    arg_count = 2
1134    category = 'Formatting values'
1135    __doc__ = doc = _('format_date(val, format_string) -- format the value, '
1136            'which must be a date, using the format_string, returning a string. '
1137            'The formatting codes are: '
1138            'd    : the day as number without a leading zero (1 to 31) '
1139            'dd   : the day as number with a leading zero (01 to 31) '
1140            'ddd  : the abbreviated localized day name (e.g. "Mon" to "Sun"). '
1141            'dddd : the long localized day name (e.g. "Monday" to "Sunday"). '
1142            'M    : the month as number without a leading zero (1 to 12). '
1143            'MM   : the month as number with a leading zero (01 to 12) '
1144            'MMM  : the abbreviated localized month name (e.g. "Jan" to "Dec"). '
1145            'MMMM : the long localized month name (e.g. "January" to "December"). '
1146            'yy   : the year as two digit number (00 to 99). '
1147            'yyyy : the year as four digit number. '
1148            'h    : the hours without a leading 0 (0 to 11 or 0 to 23, depending on am/pm) '
1149            'hh   : the hours with a leading 0 (00 to 11 or 00 to 23, depending on am/pm) '
1150            'm    : the minutes without a leading 0 (0 to 59) '
1151            'mm   : the minutes with a leading 0 (00 to 59) '
1152            's    : the seconds without a leading 0 (0 to 59) '
1153            'ss   : the seconds with a leading 0 (00 to 59) '
1154            'ap   : use a 12-hour clock instead of a 24-hour clock, with "ap" replaced by the localized string for am or pm '
1155            'AP   : use a 12-hour clock instead of a 24-hour clock, with "AP" replaced by the localized string for AM or PM '
1156            'iso  : the date with time and timezone. Must be the only format present '
1157            'to_number: the date as a floating point number '
1158            'from_number[:fmt]: format the timestamp using fmt if present otherwise iso')
1159
1160    def evaluate(self, formatter, kwargs, mi, locals, val, format_string):
1161        if not val or val == 'None':
1162            return ''
1163        try:
1164            if format_string == 'to_number':
1165                s = parse_date(val).timestamp()
1166            elif format_string.startswith('from_number'):
1167                val = datetime.fromtimestamp(float(val))
1168                f = format_string[12:]
1169                s = format_date(val, f if f else 'iso')
1170            else:
1171                s = format_date(parse_date(val), format_string)
1172            return s
1173        except:
1174            s = 'BAD DATE'
1175        return s
1176
1177
1178class BuiltinUppercase(BuiltinFormatterFunction):
1179    name = 'uppercase'
1180    arg_count = 1
1181    category = 'String case changes'
1182    __doc__ = doc = _('uppercase(val) -- return val in upper case')
1183
1184    def evaluate(self, formatter, kwargs, mi, locals, val):
1185        return val.upper()
1186
1187
1188class BuiltinLowercase(BuiltinFormatterFunction):
1189    name = 'lowercase'
1190    arg_count = 1
1191    category = 'String case changes'
1192    __doc__ = doc = _('lowercase(val) -- return val in lower case')
1193
1194    def evaluate(self, formatter, kwargs, mi, locals, val):
1195        return val.lower()
1196
1197
1198class BuiltinTitlecase(BuiltinFormatterFunction):
1199    name = 'titlecase'
1200    arg_count = 1
1201    category = 'String case changes'
1202    __doc__ = doc = _('titlecase(val) -- return val in title case')
1203
1204    def evaluate(self, formatter, kwargs, mi, locals, val):
1205        return titlecase(val)
1206
1207
1208class BuiltinCapitalize(BuiltinFormatterFunction):
1209    name = 'capitalize'
1210    arg_count = 1
1211    category = 'String case changes'
1212    __doc__ = doc = _('capitalize(val) -- return val capitalized')
1213
1214    def evaluate(self, formatter, kwargs, mi, locals, val):
1215        return capitalize(val)
1216
1217
1218class BuiltinBooksize(BuiltinFormatterFunction):
1219    name = 'booksize'
1220    arg_count = 0
1221    category = 'Get values from metadata'
1222    __doc__ = doc = _('booksize() -- return value of the size field. '
1223                'This function works only in the GUI. If you want to use this value '
1224                'in save-to-disk or send-to-device templates then you '
1225                'must make a custom "Column built from other columns", use '
1226                'the function in that column\'s template, and use that '
1227                'column\'s value in your save/send templates')
1228
1229    def evaluate(self, formatter, kwargs, mi, locals):
1230        if hasattr(mi, '_proxy_metadata'):
1231            try:
1232                v = mi._proxy_metadata.book_size
1233                if v is not None:
1234                    return str(mi._proxy_metadata.book_size)
1235                return ''
1236            except:
1237                pass
1238            return ''
1239        return _('This function can be used only in the GUI')
1240
1241
1242class BuiltinOndevice(BuiltinFormatterFunction):
1243    name = 'ondevice'
1244    arg_count = 0
1245    category = 'Get values from metadata'
1246    __doc__ = doc = _('ondevice() -- return Yes if ondevice is set, otherwise return '
1247              'the empty string. This function works only in the GUI. If you want to '
1248              'use this value in save-to-disk or send-to-device templates then you '
1249              'must make a custom "Column built from other columns", use '
1250              'the function in that column\'s template, and use that '
1251              'column\'s value in your save/send templates')
1252
1253    def evaluate(self, formatter, kwargs, mi, locals):
1254        if hasattr(mi, '_proxy_metadata'):
1255            if mi._proxy_metadata.ondevice_col:
1256                return _('Yes')
1257            return ''
1258        return _('This function can be used only in the GUI')
1259
1260
1261class BuiltinAnnotationCount(BuiltinFormatterFunction):
1262    name = 'annotation_count'
1263    arg_count = 0
1264    category = 'Get values from metadata'
1265    __doc__ = doc = _('annotation_count() -- return the total number of annotations '
1266                      'of all types attached to the current book. '
1267                      'This function works only in the GUI.')
1268
1269    def evaluate(self, formatter, kwargs, mi, locals):
1270        with suppress(Exception):
1271            from calibre.gui2.ui import get_gui
1272            c = get_gui().current_db.new_api.annotation_count_for_book(mi.id)
1273            return '' if c == 0 else str(c)
1274        return _('This function can be used only in the GUI')
1275
1276
1277class BuiltinIsMarked(BuiltinFormatterFunction):
1278    name = 'is_marked'
1279    arg_count = 0
1280    category = 'Get values from metadata'
1281    __doc__ = doc = _("is_marked() -- check whether the book is 'marked' in "
1282                      "calibre. If it is then return the value of the mark, "
1283                      "either 'true' or the comma-separated list of named "
1284                      "marks. Returns '' if the book is not marked.")
1285
1286    def evaluate(self, formatter, kwargs, mi, locals):
1287        with suppress(Exception):
1288            from calibre.gui2.ui import get_gui
1289            c = get_gui().current_db.data.get_marked(mi.id)
1290            return c if c else ''
1291        return _('This function can be used only in the GUI')
1292
1293
1294class BuiltinSeriesSort(BuiltinFormatterFunction):
1295    name = 'series_sort'
1296    arg_count = 0
1297    category = 'Get values from metadata'
1298    __doc__ = doc = _('series_sort() -- return the series sort value')
1299
1300    def evaluate(self, formatter, kwargs, mi, locals):
1301        if mi.series:
1302            return title_sort(mi.series)
1303        return ''
1304
1305
1306class BuiltinHasCover(BuiltinFormatterFunction):
1307    name = 'has_cover'
1308    arg_count = 0
1309    category = 'Get values from metadata'
1310    __doc__ = doc = _('has_cover() -- return Yes if the book has a cover, '
1311                      'otherwise return the empty string')
1312
1313    def evaluate(self, formatter, kwargs, mi, locals):
1314        if mi.has_cover:
1315            return _('Yes')
1316        return ''
1317
1318
1319class BuiltinFirstNonEmpty(BuiltinFormatterFunction):
1320    name = 'first_non_empty'
1321    arg_count = -1
1322    category = 'Iterating over values'
1323    __doc__ = doc = _('first_non_empty(value [, value]*) -- '
1324            'returns the first value that is not empty. If all values are '
1325            'empty, then the empty string is returned. '
1326            'You can have as many values as you want.')
1327
1328    def evaluate(self, formatter, kwargs, mi, locals, *args):
1329        i = 0
1330        while i < len(args):
1331            if args[i]:
1332                return args[i]
1333            i += 1
1334        return ''
1335
1336
1337class BuiltinAnd(BuiltinFormatterFunction):
1338    name = 'and'
1339    arg_count = -1
1340    category = 'Boolean'
1341    __doc__ = doc = _('and(value [, value]*) -- '
1342            'returns the string "1" if all values are not empty, otherwise '
1343            'returns the empty string. This function works well with test or '
1344            'first_non_empty. You can have as many values as you want. In many '
1345            'cases the && operator can replace this function.')
1346
1347    def evaluate(self, formatter, kwargs, mi, locals, *args):
1348        i = 0
1349        while i < len(args):
1350            if not args[i]:
1351                return ''
1352            i += 1
1353        return '1'
1354
1355
1356class BuiltinOr(BuiltinFormatterFunction):
1357    name = 'or'
1358    arg_count = -1
1359    category = 'Boolean'
1360    __doc__ = doc = _('or(value [, value]*) -- '
1361            'returns the string "1" if any value is not empty, otherwise '
1362            'returns the empty string. This function works well with test or '
1363            'first_non_empty. You can have as many values as you want.  In many '
1364            'cases the || operator can replace this function.')
1365
1366    def evaluate(self, formatter, kwargs, mi, locals, *args):
1367        i = 0
1368        while i < len(args):
1369            if args[i]:
1370                return '1'
1371            i += 1
1372        return ''
1373
1374
1375class BuiltinNot(BuiltinFormatterFunction):
1376    name = 'not'
1377    arg_count = 1
1378    category = 'Boolean'
1379    __doc__ = doc = _('not(value) -- '
1380            'returns the string "1" if the value is empty, otherwise '
1381            'returns the empty string. This function works well with test or '
1382            'first_non_empty.  In many cases the ! operator can replace this '
1383            'function.')
1384
1385    def evaluate(self, formatter, kwargs, mi, locals, val):
1386        return '' if val else '1'
1387
1388
1389class BuiltinListUnion(BuiltinFormatterFunction):
1390    name = 'list_union'
1391    arg_count = 3
1392    category = 'List manipulation'
1393    __doc__ = doc = _('list_union(list1, list2, separator) -- '
1394            'return a list made by merging the items in list1 and list2, '
1395            'removing duplicate items using a case-insensitive comparison. If '
1396            'items differ in case, the one in list1 is used. '
1397            'The items in list1 and list2 are separated by separator, as are '
1398            'the items in the returned list. Aliases: list_union(), merge_lists()')
1399    aliases = ['merge_lists']
1400
1401    def evaluate(self, formatter, kwargs, mi, locals, list1, list2, separator):
1402        res = {icu_lower(l.strip()): l.strip() for l in list2.split(separator) if l.strip()}
1403        res.update({icu_lower(l.strip()): l.strip() for l in list1.split(separator) if l.strip()})
1404        if separator == ',':
1405            separator = ', '
1406        return separator.join(res.values())
1407
1408
1409class BuiltinListRemoveDuplicates(BuiltinFormatterFunction):
1410    name = 'list_remove_duplicates'
1411    arg_count = 2
1412    category = 'List manipulation'
1413    __doc__ = doc = _('list_remove_duplicates(list, separator) -- '
1414            'return a list made by removing duplicate items in the source list. '
1415            'If items differ only in case, the last of them is returned. '
1416            'The items in source list are separated by separator, as are '
1417            'the items in the returned list.')
1418
1419    def evaluate(self, formatter, kwargs, mi, locals, list_, separator):
1420        res = {icu_lower(l.strip()): l.strip() for l in list_.split(separator) if l.strip()}
1421        if separator == ',':
1422            separator = ', '
1423        return separator.join(res.values())
1424
1425
1426class BuiltinListDifference(BuiltinFormatterFunction):
1427    name = 'list_difference'
1428    arg_count = 3
1429    category = 'List manipulation'
1430    __doc__ = doc = _('list_difference(list1, list2, separator) -- '
1431            'return a list made by removing from list1 any item found in list2, '
1432            'using a case-insensitive comparison. The items in list1 and list2 '
1433            'are separated by separator, as are the items in the returned list.')
1434
1435    def evaluate(self, formatter, kwargs, mi, locals, list1, list2, separator):
1436        l1 = [l.strip() for l in list1.split(separator) if l.strip()]
1437        l2 = {icu_lower(l.strip()) for l in list2.split(separator) if l.strip()}
1438
1439        res = []
1440        for i in l1:
1441            if icu_lower(i) not in l2 and i not in res:
1442                res.append(i)
1443        if separator == ',':
1444            return ', '.join(res)
1445        return separator.join(res)
1446
1447
1448class BuiltinListIntersection(BuiltinFormatterFunction):
1449    name = 'list_intersection'
1450    arg_count = 3
1451    category = 'List manipulation'
1452    __doc__ = doc = _('list_intersection(list1, list2, separator) -- '
1453            'return a list made by removing from list1 any item not found in list2, '
1454            'using a case-insensitive comparison. The items in list1 and list2 '
1455            'are separated by separator, as are the items in the returned list.')
1456
1457    def evaluate(self, formatter, kwargs, mi, locals, list1, list2, separator):
1458        l1 = [l.strip() for l in list1.split(separator) if l.strip()]
1459        l2 = {icu_lower(l.strip()) for l in list2.split(separator) if l.strip()}
1460
1461        res = []
1462        for i in l1:
1463            if icu_lower(i) in l2 and i not in res:
1464                res.append(i)
1465        if separator == ',':
1466            return ', '.join(res)
1467        return separator.join(res)
1468
1469
1470class BuiltinListSort(BuiltinFormatterFunction):
1471    name = 'list_sort'
1472    arg_count = 3
1473    category = 'List manipulation'
1474    __doc__ = doc = _('list_sort(list, direction, separator) -- '
1475            'return list sorted using a case-insensitive sort. If direction is '
1476            'zero, the list is sorted ascending, otherwise descending. The list items '
1477            'are separated by separator, as are the items in the returned list.')
1478
1479    def evaluate(self, formatter, kwargs, mi, locals, list1, direction, separator):
1480        res = [l.strip() for l in list1.split(separator) if l.strip()]
1481        if separator == ',':
1482            return ', '.join(sorted(res, key=sort_key, reverse=direction != "0"))
1483        return separator.join(sorted(res, key=sort_key, reverse=direction != "0"))
1484
1485
1486class BuiltinListEquals(BuiltinFormatterFunction):
1487    name = 'list_equals'
1488    arg_count = 6
1489    category = 'List manipulation'
1490    __doc__ = doc = _('list_equals(list1, sep1, list2, sep2, yes_val, no_val) -- '
1491            'return yes_val if list1 and list2 contain the same items, '
1492            'otherwise return no_val. The items are determined by splitting '
1493            'each list using the appropriate separator character (sep1 or '
1494            'sep2). The order of items in the lists is not relevant. '
1495            'The comparison is case insensitive.')
1496
1497    def evaluate(self, formatter, kwargs, mi, locals, list1, sep1, list2, sep2, yes_val, no_val):
1498        s1 = {icu_lower(l.strip()) for l in list1.split(sep1) if l.strip()}
1499        s2 = {icu_lower(l.strip()) for l in list2.split(sep2) if l.strip()}
1500        if s1 == s2:
1501            return yes_val
1502        return no_val
1503
1504
1505class BuiltinListRe(BuiltinFormatterFunction):
1506    name = 'list_re'
1507    arg_count = 4
1508    category = 'List manipulation'
1509    __doc__ = doc = _('list_re(src_list, separator, include_re, opt_replace) -- '
1510            'Construct a list by first separating src_list into items using '
1511            'the separator character. For each item in the list, check if it '
1512            'matches include_re. If it does, then add it to the list to be '
1513            'returned. If opt_replace is not the empty string, then apply the '
1514            'replacement before adding the item to the returned list.')
1515
1516    def evaluate(self, formatter, kwargs, mi, locals, src_list, separator, include_re, opt_replace):
1517        l = [l.strip() for l in src_list.split(separator) if l.strip()]
1518        res = []
1519        for item in l:
1520            if re.search(include_re, item, flags=re.I) is not None:
1521                if opt_replace:
1522                    item = re.sub(include_re, opt_replace, item)
1523                for i in [t.strip() for t in item.split(separator) if t.strip()]:
1524                    if i not in res:
1525                        res.append(i)
1526        if separator == ',':
1527            return ', '.join(res)
1528        return separator.join(res)
1529
1530
1531class BuiltinListReGroup(BuiltinFormatterFunction):
1532    name = 'list_re_group'
1533    arg_count = -1
1534    category = 'List manipulation'
1535    __doc__ = doc = _('list_re_group(src_list, separator, include_re, search_re [, group_template]+) -- '
1536                      'Like list_re except replacements are not optional. It '
1537                      'uses re_group(list_item, search_re, group_template, ...) when '
1538                      'doing the replacements on the resulting list.')
1539
1540    def evaluate(self, formatter, kwargs, mi, locals, src_list, separator, include_re,
1541                 search_re, *args):
1542        from calibre.utils.formatter import EvalFormatter
1543
1544        l = [l.strip() for l in src_list.split(separator) if l.strip()]
1545        res = []
1546        for item in l:
1547            def repl(mo):
1548                newval = ''
1549                if mo and mo.lastindex:
1550                    for dex in range(0, mo.lastindex):
1551                        gv = mo.group(dex+1)
1552                        if gv is None:
1553                            continue
1554                        if len(args) > dex:
1555                            template = args[dex].replace('[[', '{').replace(']]', '}')
1556                            newval += EvalFormatter().safe_format(template, {'$': gv},
1557                                              'EVAL', None, strip_results=False)
1558                        else:
1559                            newval += gv
1560                return newval
1561            if re.search(include_re, item, flags=re.I) is not None:
1562                item = re.sub(search_re, repl, item, flags=re.I)
1563                for i in [t.strip() for t in item.split(separator) if t.strip()]:
1564                    if i not in res:
1565                        res.append(i)
1566        if separator == ',':
1567            return ', '.join(res)
1568        return separator.join(res)
1569
1570
1571class BuiltinToday(BuiltinFormatterFunction):
1572    name = 'today'
1573    arg_count = 0
1574    category = 'Date functions'
1575    __doc__ = doc = _('today() -- '
1576            'return a date string for today. This value is designed for use in '
1577            'format_date or days_between, but can be manipulated like any '
1578            'other string. The date is in ISO format.')
1579
1580    def evaluate(self, formatter, kwargs, mi, locals):
1581        return format_date(now(), 'iso')
1582
1583
1584class BuiltinDaysBetween(BuiltinFormatterFunction):
1585    name = 'days_between'
1586    arg_count = 2
1587    category = 'Date functions'
1588    __doc__ = doc = _('days_between(date1, date2) -- '
1589            'return the number of days between date1 and date2. The number is '
1590            'positive if date1 is greater than date2, otherwise negative. If '
1591            'either date1 or date2 are not dates, the function returns the '
1592            'empty string.')
1593
1594    def evaluate(self, formatter, kwargs, mi, locals, date1, date2):
1595        try:
1596            d1 = parse_date(date1)
1597            if d1 == UNDEFINED_DATE:
1598                return ''
1599            d2 = parse_date(date2)
1600            if d2 == UNDEFINED_DATE:
1601                return ''
1602        except:
1603            return ''
1604        i = d1 - d2
1605        return '%.1f'%(i.days + (i.seconds/(24.0*60.0*60.0)))
1606
1607
1608class BuiltinDateArithmetic(BuiltinFormatterFunction):
1609    name = 'date_arithmetic'
1610    arg_count = -1
1611    category = 'Date functions'
1612    __doc__ = doc = _('date_arithmetic(date, calc_spec, fmt) -- '
1613            "Calculate a new date from 'date' using 'calc_spec'. Return the "
1614            "new date formatted according to optional 'fmt': if not supplied "
1615            "then the result will be in iso format. The calc_spec is a string "
1616            "formed by concatenating pairs of 'vW' (valueWhat) where 'v' is a "
1617            "possibly-negative number and W is one of the following letters: "
1618            "s: add 'v' seconds to 'date' "
1619            "m: add 'v' minutes to 'date' "
1620            "h: add 'v' hours to 'date' "
1621            "d: add 'v' days to 'date' "
1622            "w: add 'v' weeks to 'date' "
1623            "y: add 'v' years to 'date', where a year is 365 days. "
1624            "Example: '1s3d-1m' will add 1 second, add 3 days, and subtract 1 "
1625            "minute from 'date'.")
1626
1627    calc_ops = {
1628        's': lambda v: timedelta(seconds=v),
1629        'm': lambda v: timedelta(minutes=v),
1630        'h': lambda v: timedelta(hours=v),
1631        'd': lambda v: timedelta(days=v),
1632        'w': lambda v: timedelta(weeks=v),
1633        'y': lambda v: timedelta(days=v * 365),
1634    }
1635
1636    def evaluate(self, formatter, kwargs, mi, locals, date, calc_spec, fmt=None):
1637        try:
1638            d = parse_date(date)
1639            if d == UNDEFINED_DATE:
1640                return ''
1641            while calc_spec:
1642                mo = re.match(r'([-+\d]+)([smhdwy])', calc_spec)
1643                if mo is None:
1644                    raise ValueError(
1645                        _("{0}: invalid calculation specifier '{1}'").format(
1646                            'date_arithmetic', calc_spec))
1647                d += self.calc_ops[mo[2]](int(mo[1]))
1648                calc_spec = calc_spec[len(mo[0]):]
1649            return format_date(d, fmt if fmt else 'iso')
1650        except ValueError as e:
1651            raise e
1652        except Exception as e:
1653            traceback.print_exc()
1654            raise ValueError(_("{0}: error: {1}").format('date_arithmetic', str(e)))
1655
1656
1657class BuiltinLanguageStrings(BuiltinFormatterFunction):
1658    name = 'language_strings'
1659    arg_count = 2
1660    category = 'Get values from metadata'
1661    __doc__ = doc = _('language_strings(lang_codes, localize) -- '
1662            'return the strings for the language codes passed in lang_codes. '
1663            'If localize is zero, return the strings in English. If '
1664            'localize is not zero, return the strings in the language of '
1665            'the current locale. Lang_codes is a comma-separated list.')
1666
1667    def evaluate(self, formatter, kwargs, mi, locals, lang_codes, localize):
1668        retval = []
1669        for c in [c.strip() for c in lang_codes.split(',') if c.strip()]:
1670            try:
1671                n = calibre_langcode_to_name(c, localize != '0')
1672                if n:
1673                    retval.append(n)
1674            except:
1675                pass
1676        return ', '.join(retval)
1677
1678
1679class BuiltinLanguageCodes(BuiltinFormatterFunction):
1680    name = 'language_codes'
1681    arg_count = 1
1682    category = 'Get values from metadata'
1683    __doc__ = doc = _('language_codes(lang_strings) -- '
1684            'return the language codes for the strings passed in lang_strings. '
1685            'The strings must be in the language of the current locale. '
1686            'Lang_strings is a comma-separated list.')
1687
1688    def evaluate(self, formatter, kwargs, mi, locals, lang_strings):
1689        retval = []
1690        for c in [c.strip() for c in lang_strings.split(',') if c.strip()]:
1691            try:
1692                cv = canonicalize_lang(c)
1693                if cv:
1694                    retval.append(canonicalize_lang(cv))
1695            except:
1696                pass
1697        return ', '.join(retval)
1698
1699
1700class BuiltinCurrentLibraryName(BuiltinFormatterFunction):
1701    name = 'current_library_name'
1702    arg_count = 0
1703    category = 'Get values from metadata'
1704    __doc__ = doc = _('current_library_name() -- '
1705            'return the last name on the path to the current calibre library. '
1706            'This function can be called in template program mode using the '
1707            'template "{:\'current_library_name()\'}".')
1708
1709    def evaluate(self, formatter, kwargs, mi, locals):
1710        from calibre.library import current_library_name
1711        return current_library_name()
1712
1713
1714class BuiltinCurrentLibraryPath(BuiltinFormatterFunction):
1715    name = 'current_library_path'
1716    arg_count = 0
1717    category = 'Get values from metadata'
1718    __doc__ = doc = _('current_library_path() -- '
1719                'return the path to the current calibre library. This function can '
1720                'be called in template program mode using the template '
1721                '"{:\'current_library_path()\'}".')
1722
1723    def evaluate(self, formatter, kwargs, mi, locals):
1724        from calibre.library import current_library_path
1725        return current_library_path()
1726
1727
1728class BuiltinFinishFormatting(BuiltinFormatterFunction):
1729    name = 'finish_formatting'
1730    arg_count = 4
1731    category = 'Formatting values'
1732    __doc__ = doc = _('finish_formatting(val, fmt, prefix, suffix) -- apply the '
1733                      'format, prefix, and suffix to a value in the same way as '
1734                      'done in a template like `{series_index:05.2f| - |- }`. For '
1735                      'example, the following program produces the same output '
1736                      'as the above template: '
1737                      'program: finish_formatting(field("series_index"), "05.2f", " - ", " - ")')
1738
1739    def evaluate(self, formatter, kwargs, mi, locals_, val, fmt, prefix, suffix):
1740        if not val:
1741            return val
1742        return prefix + formatter._do_format(val, fmt) + suffix
1743
1744
1745class BuiltinVirtualLibraries(BuiltinFormatterFunction):
1746    name = 'virtual_libraries'
1747    arg_count = 0
1748    category = 'Get values from metadata'
1749    __doc__ = doc = _('virtual_libraries() -- return a comma-separated list of '
1750                      'Virtual libraries that contain this book. This function '
1751                      'works only in the GUI. If you want to use these values '
1752                      'in save-to-disk or send-to-device templates then you '
1753                      'must make a custom "Column built from other columns", use '
1754                      'the function in that column\'s template, and use that '
1755                      'column\'s value in your save/send templates')
1756
1757    def evaluate(self, formatter, kwargs, mi, locals_):
1758        with suppress(Exception):
1759            from calibre.gui2.ui import get_gui
1760            a = get_gui().current_db.data.get_virtual_libraries_for_books((mi.id,))
1761            return ', '.join(a[mi.id])
1762        return _('This function can be used only in the GUI')
1763
1764
1765class BuiltinCurrentVirtualLibraryName(BuiltinFormatterFunction):
1766    name = 'current_virtual_library_name'
1767    arg_count = 0
1768    category = 'Get values from metadata'
1769    __doc__ = doc = _('current_virtual_library_name() -- '
1770            'return the name of the current virtual library if there is one, '
1771            'otherwise the empty string. Library name case is preserved. '
1772            'Example: "program: current_virtual_library_name()".')
1773
1774    def evaluate(self, formatter, kwargs, mi, locals):
1775        with suppress(Exception):
1776            from calibre.gui2.ui import get_gui
1777            return get_gui().current_db.data.get_base_restriction_name()
1778        return _('This function can be used only in the GUI')
1779
1780
1781class BuiltinUserCategories(BuiltinFormatterFunction):
1782    name = 'user_categories'
1783    arg_count = 0
1784    category = 'Get values from metadata'
1785    __doc__ = doc = _('user_categories() -- return a comma-separated list of '
1786                      'the user categories that contain this book. This function '
1787                      'works only in the GUI. If you want to use these values '
1788                      'in save-to-disk or send-to-device templates then you '
1789                      'must make a custom "Column built from other columns", use '
1790                      'the function in that column\'s template, and use that '
1791                      'column\'s value in your save/send templates')
1792
1793    def evaluate(self, formatter, kwargs, mi, locals_):
1794        if hasattr(mi, '_proxy_metadata'):
1795            cats = {k for k, v in iteritems(mi._proxy_metadata.user_categories) if v}
1796            cats = sorted(cats, key=sort_key)
1797            return ', '.join(cats)
1798        return _('This function can be used only in the GUI')
1799
1800
1801class BuiltinTransliterate(BuiltinFormatterFunction):
1802    name = 'transliterate'
1803    arg_count = 1
1804    category = 'String manipulation'
1805    __doc__ = doc = _('transliterate(a) -- Returns a string in a latin alphabet '
1806                      'formed by approximating the sound of the words in the '
1807                      'source string. For example, if the source is "{0}"'
1808                      ' the function returns "{1}".').format(
1809                          "Фёдор Миха́йлович Достоевский", 'Fiodor Mikhailovich Dostoievskii')
1810
1811    def evaluate(self, formatter, kwargs, mi, locals, source):
1812        from calibre.utils.filenames import ascii_text
1813        return ascii_text(source)
1814
1815
1816class BuiltinAuthorLinks(BuiltinFormatterFunction):
1817    name = 'author_links'
1818    arg_count = 2
1819    category = 'Get values from metadata'
1820    __doc__ = doc = _('author_links(val_separator, pair_separator) -- returns '
1821                      'a string containing a list of authors and that author\'s '
1822                      'link values in the '
1823                      'form author1 val_separator author1link pair_separator '
1824                      'author2 val_separator author2link etc. An author is '
1825                      'separated from its link value by the val_separator string '
1826                      'with no added spaces. author:linkvalue pairs are separated '
1827                      'by the pair_separator string argument with no added spaces. '
1828                      'It is up to you to choose separator strings that do '
1829                      'not occur in author names or links. An author is '
1830                      'included even if the author link is empty.')
1831
1832    def evaluate(self, formatter, kwargs, mi, locals, val_sep, pair_sep):
1833        if hasattr(mi, '_proxy_metadata'):
1834            link_data = mi._proxy_metadata.author_link_map
1835            if not link_data:
1836                return ''
1837            names = sorted(link_data.keys(), key=sort_key)
1838            return pair_sep.join(n + val_sep + link_data[n] for n in names)
1839        return _('This function can be used only in the GUI')
1840
1841
1842class BuiltinAuthorSorts(BuiltinFormatterFunction):
1843    name = 'author_sorts'
1844    arg_count = 1
1845    category = 'Get values from metadata'
1846    __doc__ = doc = _('author_sorts(val_separator) -- returns a string '
1847                      'containing a list of author\'s sort values for the '
1848                      'authors of the book. The sort is the one in the author '
1849                      'metadata (different from the author_sort in books). The '
1850                      'returned list has the form author sort 1 val_separator '
1851                      'author sort 2 etc. The author sort values in this list '
1852                      'are in the same order as the authors of the book. If '
1853                      'you want spaces around val_separator then include them '
1854                      'in the separator string')
1855
1856    def evaluate(self, formatter, kwargs, mi, locals, val_sep):
1857        sort_data = mi.author_sort_map
1858        if not sort_data:
1859            return ''
1860        names = [sort_data.get(n) for n in mi.authors if n.strip()]
1861        return val_sep.join(n for n in names)
1862
1863
1864class BuiltinConnectedDeviceName(BuiltinFormatterFunction):
1865    name = 'connected_device_name'
1866    arg_count = 1
1867    category = 'Get values from metadata'
1868    __doc__ = doc = _("connected_device_name(storage_location) -- if a device is "
1869                      "connected then return the device name, otherwise return "
1870                      "the empty string. Each storage location on a device can "
1871                      "have a different name. The location names are 'main', "
1872                      "'carda' and 'cardb'. This function works only in the GUI.")
1873
1874    def evaluate(self, formatter, kwargs, mi, locals, storage_location):
1875        with suppress(Exception):
1876            # Do the import here so that we don't entangle the GUI when using
1877            # command line functions
1878            from calibre.gui2.ui import get_gui
1879            info = get_gui().device_manager.get_current_device_information()
1880            if info is None:
1881                return ''
1882            try:
1883                if storage_location not in {'main', 'carda', 'cardb'}:
1884                    raise ValueError(
1885                         _('connected_device_name: invalid storage location "{}"'
1886                                    .format(storage_location)))
1887                info = info['info'][4]
1888                if storage_location not in info:
1889                    return ''
1890                return info[storage_location]['device_name']
1891            except Exception:
1892                traceback.print_exc()
1893                raise
1894        return _('This function can be used only in the GUI')
1895
1896
1897class BuiltinConnectedDeviceUUID(BuiltinFormatterFunction):
1898    name = 'connected_device_uuid'
1899    arg_count = 1
1900    category = 'Get values from metadata'
1901    __doc__ = doc = _("connected_device_uuid(storage_location) -- if a device is "
1902                      "connected then return the device uuid (unique id), "
1903                      "otherwise return the empty string. Each storage location "
1904                      "on a device has a different uuid. The location names are "
1905                      "'main', 'carda' and 'cardb'. This function works only in "
1906                      "the GUI.")
1907
1908    def evaluate(self, formatter, kwargs, mi, locals, storage_location):
1909        with suppress(Exception):
1910            # Do the import here so that we don't entangle the GUI when using
1911            # command line functions
1912            from calibre.gui2.ui import get_gui
1913            info = get_gui().device_manager.get_current_device_information()
1914            if info is None:
1915                return ''
1916            try:
1917                if storage_location not in {'main', 'carda', 'cardb'}:
1918                    raise ValueError(
1919                         _('connected_device_name: invalid storage location "{}"'
1920                                    .format(storage_location)))
1921                info = info['info'][4]
1922                if storage_location not in info:
1923                    return ''
1924                return info[storage_location]['device_store_uuid']
1925            except Exception:
1926                traceback.print_exc()
1927                raise
1928        return _('This function can be used only in the GUI')
1929
1930
1931class BuiltinCheckYesNo(BuiltinFormatterFunction):
1932    name = 'check_yes_no'
1933    arg_count = 4
1934    category = 'If-then-else'
1935    __doc__ = doc = _('check_yes_no(field_name, is_undefined, is_false, is_true) '
1936                      '-- checks the value of the yes/no field named by the '
1937                      'lookup key field_name for a value specified by the '
1938                      'parameters, returning "yes" if a match is found, otherwise '
1939                      'returning an empty string. Set the parameter is_undefined, '
1940                      'is_false, or is_true to 1 (the number) to check that '
1941                      'condition, otherwise set it to 0. Example: '
1942                      'check_yes_no("#bool", 1, 0, 1) returns "yes" if the '
1943                      'yes/no field "#bool" is either undefined (neither True '
1944                      'nor False) or True. More than one of is_undefined, '
1945                      'is_false, or is_true can be set to 1.  This function '
1946                      'is usually used by the test() or is_empty() functions.')
1947
1948    def evaluate(self, formatter, kwargs, mi, locals, field, is_undefined, is_false, is_true):
1949        res = getattr(mi, field, None)
1950        if res is None:
1951            if is_undefined == '1':
1952                return 'yes'
1953            return ""
1954        if not isinstance(res, bool):
1955            raise ValueError(_('check_yes_no requires the field be a Yes/No custom column'))
1956        if is_false == '1' and not res:
1957            return 'yes'
1958        if is_true == '1' and res:
1959            return 'yes'
1960        return ""
1961
1962
1963class BuiltinRatingToStars(BuiltinFormatterFunction):
1964    name = 'rating_to_stars'
1965    arg_count = 2
1966    category = 'Formatting values'
1967    __doc__ = doc = _('rating_to_stars(value, use_half_stars) '
1968                      '-- Returns the rating as string of star characters. '
1969                      'The value is a number between 0 and 5. Set use_half_stars '
1970                      'to 1 if you want half star characters for custom ratings '
1971                      'columns that support non-integer ratings, for example 2.5.')
1972
1973    def evaluate(self, formatter, kwargs, mi, locals, value, use_half_stars):
1974        if not value:
1975            return ''
1976        err_msg = _('The rating must be a number between 0 and 5')
1977        try:
1978            v = float(value) * 2
1979        except:
1980            raise ValueError(err_msg)
1981        if v < 0 or v > 10:
1982            raise ValueError(err_msg)
1983        from calibre.ebooks.metadata import rating_to_stars
1984        return rating_to_stars(v, use_half_stars == '1')
1985
1986
1987class BuiltinSwapAroundArticles(BuiltinFormatterFunction):
1988    name = 'swap_around_articles'
1989    arg_count = 2
1990    category = 'String manipulation'
1991    __doc__ = doc = _('swap_around_articles(val, separator) '
1992                      '-- returns the val with articles moved to the end. '
1993                      'The value can be a list, in which case each member '
1994                      'of the list is processed. If the value is a list then '
1995                      'you must provide the list value separator. If no '
1996                      'separator is provided then the value is treated as '
1997                      'being a single value, not a list.')
1998
1999    def evaluate(self, formatter, kwargs, mi, locals, val, separator):
2000        if not val:
2001            return ''
2002        if not separator:
2003            return title_sort(val).replace(',', ';')
2004        result = []
2005        try:
2006            for v in [x.strip() for x in val.split(separator)]:
2007                result.append(title_sort(v).replace(',', ';'))
2008        except:
2009            traceback.print_exc()
2010        return separator.join(sorted(result, key=sort_key))
2011
2012
2013class BuiltinArguments(BuiltinFormatterFunction):
2014    name = 'arguments'
2015    arg_count = -1
2016    category = 'other'
2017    __doc__ = doc = _('arguments(id[=expression] [, id[=expression]]*) '
2018                      '-- Used in a stored template to retrieve the arguments '
2019                      'passed in the call. It both declares and initializes '
2020                      'local variables, effectively parameters. The variables '
2021                      'are positional; they get the value of the value given '
2022                      'in the call in the same position. If the corresponding '
2023                      'parameter is not provided in the call then arguments '
2024                      'assigns that variable the provided default value. If '
2025                      'there is no default value then the variable is set to '
2026                      'the empty string.')
2027
2028    def evaluate(self, formatter, kwargs, mi, locals, *args):
2029        # The arguments function is implemented in-line in the formatter
2030        raise NotImplementedError()
2031
2032
2033class BuiltinGlobals(BuiltinFormatterFunction):
2034    name = 'globals'
2035    arg_count = -1
2036    category = 'other'
2037    __doc__ = doc = _('globals(id[=expression] [, id[=expression]]*) '
2038                      '-- Retrieves "global variables" that can be passed into '
2039                      'the formatter. It both declares and initializes local '
2040                      'variables with the names of the global variables passed '
2041                      'in. If the corresponding variable is not provided in '
2042                      'the passed-in globals then it assigns that variable the '
2043                      'provided default value. If there is no default value '
2044                      'then the variable is set to the empty string.')
2045
2046    def evaluate(self, formatter, kwargs, mi, locals, *args):
2047        # The globals function is implemented in-line in the formatter
2048        raise NotImplementedError()
2049
2050
2051class BuiltinSetGlobals(BuiltinFormatterFunction):
2052    name = 'set_globals'
2053    arg_count = -1
2054    category = 'other'
2055    __doc__ = doc = _('globals(id[=expression] [, id[=expression]]*) '
2056                      '-- Retrieves "global variables" that can be passed into '
2057                      'the formatter. It both declares and initializes local '
2058                      'variables with the names of the global variables passed '
2059                      'in. If the corresponding variable is not provided in '
2060                      'the passed-in globals then it assigns that variable the '
2061                      'provided default value. If there is no default value '
2062                      'then the variable is set to the empty string.')
2063
2064    def evaluate(self, formatter, kwargs, mi, locals, *args):
2065        # The globals function is implemented in-line in the formatter
2066        raise NotImplementedError()
2067
2068
2069class BuiltinFieldExists(BuiltinFormatterFunction):
2070    name = 'field_exists'
2071    arg_count = 1
2072    category = 'If-then-else'
2073    __doc__ = doc = _('field_exists(field_name) -- checks if a field '
2074                      '(column) named field_name exists, returning '
2075                      "'1' if so and '' if not.")
2076
2077    def evaluate(self, formatter, kwargs, mi, locals, field_name):
2078        if field_name.lower() in mi.all_field_keys():
2079            return '1'
2080        return ''
2081
2082
2083class BuiltinCharacter(BuiltinFormatterFunction):
2084    name = 'character'
2085    arg_count = 1
2086    category = 'String manipulation'
2087    __doc__ = doc = _('character(character_name) -- returns the '
2088                      'character named by character_name. For example, '
2089                      r"character('newline') returns a newline character ('\n'). "
2090                      "The supported character names are 'newline', 'return', "
2091                      "'tab', and 'backslash'.")
2092
2093    def evaluate(self, formatter, kwargs, mi, locals, character_name):
2094        # The globals function is implemented in-line in the formatter
2095        raise NotImplementedError()
2096
2097
2098_formatter_builtins = [
2099    BuiltinAdd(), BuiltinAnd(), BuiltinApproximateFormats(), BuiltinArguments(),
2100    BuiltinAssign(),
2101    BuiltinAuthorLinks(), BuiltinAuthorSorts(), BuiltinBooksize(),
2102    BuiltinCapitalize(), BuiltinCharacter(), BuiltinCheckYesNo(), BuiltinCeiling(),
2103    BuiltinCmp(), BuiltinConnectedDeviceName(), BuiltinConnectedDeviceUUID(), BuiltinContains(),
2104    BuiltinCount(), BuiltinCurrentLibraryName(), BuiltinCurrentLibraryPath(),
2105    BuiltinCurrentVirtualLibraryName(), BuiltinDateArithmetic(),
2106    BuiltinDaysBetween(), BuiltinDivide(), BuiltinEval(), BuiltinFirstNonEmpty(),
2107    BuiltinField(), BuiltinFieldExists(),
2108    BuiltinFinishFormatting(), BuiltinFirstMatchingCmp(), BuiltinFloor(),
2109    BuiltinFormatDate(), BuiltinFormatNumber(), BuiltinFormatsModtimes(),
2110    BuiltinFormatsPaths(), BuiltinFormatsSizes(), BuiltinFractionalPart(),
2111    BuiltinGlobals(),
2112    BuiltinHasCover(), BuiltinHumanReadable(), BuiltinIdentifierInList(),
2113    BuiltinIfempty(), BuiltinLanguageCodes(), BuiltinLanguageStrings(),
2114    BuiltinInList(), BuiltinIsMarked(), BuiltinListCountMatching(),
2115    BuiltinListDifference(), BuiltinListEquals(),
2116    BuiltinListIntersection(), BuiltinListitem(), BuiltinListRe(),
2117    BuiltinListReGroup(), BuiltinListRemoveDuplicates(), BuiltinListSort(),
2118    BuiltinListSplit(), BuiltinListUnion(),BuiltinLookup(),
2119    BuiltinLowercase(), BuiltinMod(), BuiltinMultiply(), BuiltinNot(), BuiltinOndevice(),
2120    BuiltinOr(), BuiltinPrint(), BuiltinRatingToStars(), BuiltinRawField(), BuiltinRawList(),
2121    BuiltinRe(), BuiltinReGroup(), BuiltinRound(), BuiltinSelect(), BuiltinSeriesSort(),
2122    BuiltinSetGlobals(), BuiltinShorten(), BuiltinStrcat(), BuiltinStrcatMax(),
2123    BuiltinStrcmp(), BuiltinStrInList(), BuiltinStrlen(), BuiltinSubitems(),
2124    BuiltinSublist(),BuiltinSubstr(), BuiltinSubtract(), BuiltinSwapAroundArticles(),
2125    BuiltinSwapAroundComma(), BuiltinSwitch(),
2126    BuiltinTemplate(), BuiltinTest(), BuiltinTitlecase(),
2127    BuiltinToday(), BuiltinTransliterate(), BuiltinUppercase(),
2128    BuiltinUserCategories(), BuiltinVirtualLibraries(), BuiltinAnnotationCount()
2129]
2130
2131
2132class FormatterUserFunction(FormatterFunction):
2133
2134    def __init__(self, name, doc, arg_count, program_text, is_python):
2135        self.is_python = is_python
2136        self.name = name
2137        self.doc = doc
2138        self.arg_count = arg_count
2139        self.program_text = program_text
2140        self.cached_parse_tree = None
2141
2142    def to_pref(self):
2143        return [self.name, self.doc, self.arg_count, self.program_text]
2144
2145
2146tabs = re.compile(r'^\t*')
2147
2148
2149def function_pref_is_python(pref):
2150    if isinstance(pref, list):
2151        pref = pref[3]
2152    if pref.startswith('def'):
2153        return True
2154    if pref.startswith('program'):
2155        return False
2156    raise ValueError('Unknown program type in formatter function pref')
2157
2158
2159def function_pref_name(pref):
2160    return pref[0]
2161
2162
2163def compile_user_function(name, doc, arg_count, eval_func):
2164    if not function_pref_is_python(eval_func):
2165        return FormatterUserFunction(name, doc, arg_count, eval_func, False)
2166
2167    def replace_func(mo):
2168        return mo.group().replace('\t', '    ')
2169
2170    func = '    ' + '\n    '.join([tabs.sub(replace_func, line)
2171                                   for line in eval_func.splitlines()])
2172    prog = '''
2173from calibre.utils.formatter_functions import FormatterUserFunction
2174from calibre.utils.formatter_functions import formatter_functions
2175class UserFunction(FormatterUserFunction):
2176''' + func
2177    locals_ = {}
2178    if DEBUG and tweaks.get('enable_template_debug_printing', False):
2179        print(prog)
2180    exec(prog, locals_)
2181    cls = locals_['UserFunction'](name, doc, arg_count, eval_func, True)
2182    return cls
2183
2184
2185def compile_user_template_functions(funcs):
2186    compiled_funcs = {}
2187    for func in funcs:
2188        try:
2189            # Force a name conflict to test the logic
2190            # if func[0] == 'myFunc2':
2191            #     func[0] = 'myFunc3'
2192
2193            # Compile the function so that the tab processing is done on the
2194            # source. This helps ensure that if the function already is defined
2195            # then white space differences don't cause them to compare differently
2196
2197            cls = compile_user_function(*func)
2198            cls.is_python = function_pref_is_python(func)
2199            compiled_funcs[cls.name] = cls
2200        except Exception:
2201            try:
2202                func_name = func[0]
2203            except Exception:
2204                func_name = 'Unknown'
2205            prints('**** Compilation errors in user template function "%s" ****' % func_name)
2206            traceback.print_exc(limit=10)
2207            prints('**** End compilation errors in %s "****"' % func_name)
2208    return compiled_funcs
2209
2210
2211def load_user_template_functions(library_uuid, funcs, precompiled_user_functions=None):
2212    unload_user_template_functions(library_uuid)
2213    if precompiled_user_functions:
2214        compiled_funcs = precompiled_user_functions
2215    else:
2216        compiled_funcs = compile_user_template_functions(funcs)
2217    formatter_functions().register_functions(library_uuid, list(compiled_funcs.values()))
2218
2219
2220def unload_user_template_functions(library_uuid):
2221    formatter_functions().unregister_functions(library_uuid)
2222