#!/usr/bin/env python # -*- coding: utf-8 -*- # Copyright © 1991-1998, 2000, 2002, 2003 Progiciels Bourbeau-Pinard inc. # François Pinard <pinard@iro.umontreal.ca>, 1991-04. # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2, or (at your option) # any later version. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software Foundation, # Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. """\ Handling of boxed comments in various box styles. The user documentation for this tool may be found at: http://pymacs.progiciels-bpi.ca/rebox.html """ ## Note: a double hash comment introduces a group of functions or methods. __metatype__ = type import re, sys ## Batch specific features. def main(*arguments): refill = True style = None tabify = False verbose = False width = 79 import getopt options, arguments = getopt.getopt(arguments, 'ns:tvw:', ['help']) for option, value in options: if option == '--help': sys.stdout.write(__doc__) sys.exit(0) elif option == '-n': refill = False elif option == '-s': style = int(value) elif option == '-t': tabify = True elif option == '-v': verbose = True elif option == '-w': width = int(value) if len(arguments) == 0: text = sys.stdin.read() elif len(arguments) == 1: handle = file(arguments[0]) text = handle.read() handle.close() else: sys.stderr.write("Invalid usage, try `rebox --help' for help.\n") sys.exit(1) old_style, new_style, text, position = engine( text, style=style, width=width, refill=refill, tabify=tabify) if text is None: sys.stderr.write("* Cannot rebox to style %d.\n" % new_style) sys.exit(1) sys.stdout.write(text) if verbose: if old_style == new_style: sys.stderr.write("Reboxed with style %d.\n" % old_style) else: sys.stderr.write("Reboxed from style %d to %d.\n" % (old_style, new_style)) ## Emacs specific features. def pymacs_load_hook(): global interactions, lisp, Let, region, comment, set_default_style from Pymacs import lisp, Let emacs_rebox = Emacs_Rebox() # Declare functions for Emacs to import. interactions = {} region = emacs_rebox.region interactions[region] = 'P' comment = emacs_rebox.comment interactions[comment] = 'P' set_default_style = emacs_rebox.set_default_style class Emacs_Rebox: def __init__(self): self.default_style = None def set_default_style(self, style): """\ Set the default style to STYLE. """ self.default_style = style def region(self, flag): """\ Rebox the boxed comment in the current region, obeying FLAG. """ self.emacs_engine(flag, self.find_region) def comment(self, flag): """\ Rebox the surrounding boxed comment, obeying FLAG. """ self.emacs_engine(flag, self.find_comment) def emacs_engine(self, flag, find_limits): """\ Rebox text while obeying FLAG. Call FIND_LIMITS to discover the extent of the boxed comment. """ # `C-u -' means that box style is to be decided interactively. if flag == lisp['-']: flag = self.ask_for_style() # If FLAG is zero or negative, only change default box style. if isinstance(flag, int) and flag <= 0: self.default_style = -flag lisp.message("Default style set to %d" % -flag) return # Decide box style and refilling. if flag is None: style = self.default_style refill = True elif isinstance(flag, int): if self.default_style is None: style = flag else: style = merge_styles(self.default_style, flag) refill = True else: flag = flag.copy() if isinstance(flag, list): style = self.default_style refill = False else: lisp.error("Unexpected flag value %s" % flag) # Prepare for reboxing. lisp.message("Reboxing...") checkpoint = lisp.buffer_undo_list.value() start, end = find_limits() text = lisp.buffer_substring(start, end) width = lisp.fill_column.value() tabify = lisp.indent_tabs_mode.value() is not None point = lisp.point() if start <= point < end: position = point - start else: position = None # Rebox the text and replace it in Emacs buffer. old_style, new_style, text, position = engine( text, style=style, width=width, refill=refill, tabify=tabify, position=position) if text is None: lisp.error("Cannot rebox to style %d" % new_style) lisp.delete_region(start, end) lisp.insert(text) if position is not None: lisp.goto_char(start + position) # Collapse all operations into a single one, for Undo. self.clean_undo_after(checkpoint) # We are finished, tell the user. if old_style == new_style: lisp.message("Reboxed with style %d" % old_style) else: lisp.message("Reboxed from style %d to %d" % (old_style, new_style)) def ask_for_style(self): """\ Request the style interactively, using the minibuffer. """ language = quality = type = None while language is None: lisp.message("\ Box language is 100-none, 200-/*, 300-//, 400-#, 500-;, 600-%%") key = lisp.read_char() if key >= ord('0') and key <= ord('6'): language = key - ord('0') while quality is None: lisp.message("\ Box quality/width is 10-simple/1, 20-rounded/2, 30-starred/3 or 40-starred/4") key = lisp.read_char() if key >= ord('0') and key <= ord('4'): quality = key - ord('0') while type is None: lisp.message("\ Box type is 1-opened, 2-half-single, 3-single, 4-half-double or 5-double") key = lisp.read_char() if key >= ord('0') and key <= ord('5'): type = key - ord('0') return 100*language + 10*quality + type def find_region(self): """\ Return the limits of the region. """ return lisp.point(), lisp.mark(lisp.t) def find_comment(self): """\ Find and return the limits of the block of comments following or enclosing the cursor, or return an error if the cursor is not within such a block of comments. Extend it as far as possible in both directions. """ let = Let().push_excursion() try: # Find the start of the current or immediately following comment. lisp.beginning_of_line() lisp.skip_chars_forward(' \t\n') lisp.beginning_of_line() if not language_matcher[0](self.remainder_of_line()): temp = lisp.point() if not lisp.re_search_forward('\\*/', None, lisp.t): lisp.error("outside any comment block") lisp.re_search_backward('/\\*') if lisp.point() > temp: lisp.error("outside any comment block") temp = lisp.point() lisp.beginning_of_line() lisp.skip_chars_forward(' \t') if lisp.point() != temp: lisp.error("text before start of comment") lisp.beginning_of_line() start = lisp.point() language = guess_language(self.remainder_of_line()) # Find the end of this comment. if language == 2: lisp.search_forward('*/') if not lisp.looking_at('[ \t]*$'): lisp.error("text after end of comment") lisp.end_of_line() if lisp.eobp(): lisp.insert('\n') else: lisp.forward_char(1) end = lisp.point() # Try to extend the comment block backwards. lisp.goto_char(start) while not lisp.bobp(): if language == 2: lisp.skip_chars_backward(' \t\n') if not lisp.looking_at('[ \t]*\n[ \t]*/\\*'): break if lisp.point() < 2: break lisp.backward_char(2) if not lisp.looking_at('\\*/'): break lisp.re_search_backward('/\\*') temp = lisp.point() lisp.beginning_of_line() lisp.skip_chars_forward(' \t') if lisp.point() != temp: break lisp.beginning_of_line() else: lisp.previous_line(1) if not language_matcher[language](self.remainder_of_line()): break start = lisp.point() # Try to extend the comment block forward. lisp.goto_char(end) while language_matcher[language](self.remainder_of_line()): if language == 2: lisp.re_search_forward('[ \t]*/\\*') lisp.re_search_forward('\\*/') if lisp.looking_at('[ \t]*$'): lisp.beginning_of_line() lisp.forward_line(1) end = lisp.point() else: lisp.forward_line(1) end = lisp.point() return start, end finally: let.pops() def remainder_of_line(self): """\ Return all characters between point and end of line in Emacs buffer. """ return lisp('''\ (buffer-substring (point) (save-excursion (skip-chars-forward "^\n") (point))) ''') def clean_undo_after_old(self, checkpoint): """\ Remove all intermediate boundaries from the Undo list since CHECKPOINT. """ # Declare some Lisp functions. car = lisp.car cdr = lisp.cdr eq = lisp.eq setcdr = lisp.setcdr # Remove any `nil' delimiter recently added to the Undo list. cursor = lisp.buffer_undo_list.value() if not eq(cursor, checkpoint): tail = cdr(cursor) while not eq(tail, checkpoint): if car(tail): cursor = tail tail = cdr(cursor) else: tail = cdr(tail) setcdr(cursor, tail) def clean_undo_after(self, checkpoint): """\ Remove all intermediate boundaries from the Undo list since CHECKPOINT. """ lisp(""" (let ((undo-list %s)) (if (not (eq buffer-undo-list undo-list)) (let ((cursor buffer-undo-list)) (while (not (eq (cdr cursor) undo-list)) (if (car (cdr cursor)) (setq cursor (cdr cursor)) (setcdr cursor (cdr (cdr cursor))))))) nil) """ % (checkpoint or 'nil')) ## Reboxing main control. def engine(text, style=None, width=79, refill=True, tabify=False, position=None): """\ Add, delete or adjust a boxed comment held in TEXT, according to STYLE. STYLE values are explained at beginning of this file. Any zero attribute in STYLE indicates that the corresponding attribute should be recovered from the currently existing box. Produced lines will not go over WIDTH columns if possible, if refilling gets done. But if REFILL is false, WIDTH is ignored. If TABIFY is true, the beginning of produced lines will have spaces replace by TABs. POSITION is either None, or a character position within TEXT. Returns four values: the old box style, the new box style, the reformatted text, and either None or the adjusted value of POSITION in the new text. The reformatted text is returned as None if the requested style does not exist. """ last_line_complete = text and text[-1] == '\n' if last_line_complete: text = text[:-1] lines = text.expandtabs().split('\n') # Decide about refilling and the box style to use. new_style = 111 old_template = guess_template(lines) new_style = merge_styles(new_style, old_template.style) if style is not None: new_style = merge_styles(new_style, style) new_template = template_registry.get(new_style) # Interrupt processing if STYLE does not exist. if not new_template: return old_template.style, new_style, None, None # Remove all previous comment marks, and left margin. if position is not None: marker = Marker() marker.save_position(text, position, old_template.characters()) lines, margin = old_template.unbuild(lines) # Ensure only one white line between paragraphs. counter = 1 while counter < len(lines) - 1: if lines[counter] == '' and lines[counter-1] == '': del lines[counter] else: counter = counter + 1 # Rebuild the boxed comment. lines = new_template.build(lines, width, refill, margin) # Retabify to the left only. if tabify: for counter in range(len(lines)): tabs = len(re.match(' *', lines[counter]).group()) / 8 lines[counter] = '\t' * tabs + lines[counter][8*tabs:] # Restore the point position. text = '\n'.join(lines) if last_line_complete: text = text + '\n' if position is not None: position = marker.get_position(text, new_template.characters()) return old_template.style, new_style, text, position def guess_language(line): """\ Guess the language in use for LINE. """ for language in range(len(language_matcher) - 1, 1, -1): if language_matcher[language](line): return language return 1 def guess_template(lines): """\ Find the heaviest box template matching LINES. """ best_template = None for template in list(template_registry.values()): if best_template is None or template > best_template: if template.match(lines): best_template = template return best_template def left_margin_size(lines): """\ Return the width of the left margin for all LINES. Ignore white lines. """ margin = None for line in lines: counter = len(re.match(' *', line).group()) if counter != len(line): if margin is None or counter < margin: margin = counter if margin is None: margin = 0 return margin def merge_styles(original, update): """\ Return style attributes as per ORIGINAL, in which attributes have been overridden by non-zero corresponding style attributes from UPDATE. """ style = [original / 100, original / 10 % 10, original % 10] merge = update / 100, update / 10 % 10, update % 10 for counter in range(3): if merge[counter]: style[counter] = merge[counter] return 100*style[0] + 10*style[1] + style[2] ## Refilling logic. def refill_lines(lines, width, cached_refiller=[]): """\ Refill LINES, trying to not produce lines having more than WIDTH columns. """ if not cached_refiller: for Refiller in Refiller_Gnu_Fmt, Refiller_Textwrap, Refiller_Dumb: refiller = Refiller() new_lines = refiller.fill(lines, width) if new_lines is not None: cached_refiller.append(refiller) return new_lines return cached_refiller[0].fill(lines, width) class Refiller: available = True def fill(self, lines, width): if self.available: new_lines = [] start = 0 while start < len(lines) and not lines[start]: start = start + 1 end = start while end < len(lines): while end < len(lines) and lines[end]: end = end + 1 new_lines = new_lines + self.fill_paragraph(lines[start:end], width) while end < len(lines) and not lines[end]: end = end + 1 if end < len(lines): new_lines.append('') start = end return new_lines class Refiller_Gnu_Fmt(Refiller): """\ Use both Knuth algorithm and protection for full stops at end of sentences. """ def fill(self, lines, width): if self.available: import tempfile, os name = tempfile.mktemp() handle = file(name, 'w') handle.write('\n'.join(lines) + '\n') handle.close() handle = os.popen('fmt -cuw %d %s' % (width, name)) text = handle.read() os.remove(name) if handle.close() is None: return [line.expandtabs() for line in text.split('\n')[:-1]] class Refiller_Textwrap(Refiller): """\ No Knuth algorithm, but protection for full stops at end of sentences. """ def __init__(self): try: from textwrap import TextWrapper except ImportError: self.available = False else: self.wrapper = TextWrapper(fix_sentence_endings=1) def fill_paragraph(self, lines, width): # FIXME: This one fills indented lines more aggressively than the # dumb refiller. I'm not sure what it the best thing to do, but # ideally, all refillers should behave more or less the same way. self.wrapper.width = width prefix = ' ' * left_margin_size(lines) self.wrapper.initial_indent = prefix self.wrapper.subsequent_indent = prefix return self.wrapper.wrap(' '.join(lines)) class Refiller_Dumb(Refiller): """\ No Knuth algorithm, nor even protection for full stops at end of sentences. """ def fill_paragraph(self, lines, width): margin = left_margin_size(lines) prefix = ' ' * margin new_lines = [] new_line = '' for line in lines: counter = len(line) - len(line.lstrip()) if counter > margin: if new_line: new_lines.append(prefix + new_line) new_line = '' indent = ' ' * (counter - margin) else: indent = '' for word in line.split(): if new_line: if len(new_line) + 1 + len(word) > width: new_lines.append(prefix + new_line) new_line = word else: new_line = new_line + ' ' + word else: new_line = indent + word indent = '' if new_line: new_lines.append(prefix + new_line) return new_lines ## Marking logic. class Marker: """\ Heuristics to simulate a marker while reformatting boxes. """ def save_position(self, text, position, ignorable): """\ Given a TEXT and a POSITION in that text, save the adjusted position by faking that all IGNORABLE characters before POSITION were removed. """ ignore = {} for character in ' \t\r\n' + ignorable: ignore[character] = None counter = 0 for character in text[:position]: if character in ignore: counter = counter + 1 self.position = position - counter def get_position(self, text, ignorable, latest=0): """\ Given a TEXT, return the value that would yield the currently saved position, if it was saved by `save_position' with IGNORABLE. Unless the position lies within a series of ignorable characters, LATEST has no effect in practice. If LATEST is true, return the biggest possible value instead of the smallest. """ ignore = {} for character in ' \t\r\n' + ignorable: ignore[character] = None counter = 0 position = 0 if latest: for character in text: if character in ignore: counter = counter + 1 else: if position == self.position: break position = position + 1 elif self.position > 0: for character in text: if character in ignore: counter = counter + 1 else: position = position + 1 if position == self.position: break return position + counter ## Template processing. class Template: def __init__(self, style, weight, lines): """\ Digest and register a single template. The template is numbered STYLE, has a parsing WEIGHT, and is described by one to three LINES. STYLE should be used only once through all `declare_template' calls. One of the lines should contain the substring `box' to represent the comment to be boxed, and if three lines are given, `box' should appear in the middle one. Lines containing only spaces are implied as necessary before and after the the `box' line, so we have three lines. Normally, all three template lines should be of the same length. If the first line is shorter, it represents a start comment string to be bundled within the first line of the comment text. If the third line is shorter, it represents an end comment string to be bundled at the end of the comment text, and refilled with it. """ assert style not in template_registry, \ "Style %d defined more than once" % style self.style = style self.weight = weight # Make it exactly three lines, with `box' in the middle. start = lines[0].find('box') if start >= 0: line1 = None line2 = lines[0] if len(lines) > 1: line3 = lines[1] else: line3 = None else: start = lines[1].find('box') if start >= 0: line1 = lines[0] line2 = lines[1] if len(lines) > 2: line3 = lines[2] else: line3 = None else: assert 0, "Erroneous template for %d style" % style end = start + len('box') # Define a few booleans. self.merge_nw = line1 is not None and len(line1) < len(line2) self.merge_se = line3 is not None and len(line3) < len(line2) # Define strings at various cardinal directions. if line1 is None: self.nw = self.nn = self.ne = None elif self.merge_nw: self.nw = line1 self.nn = self.ne = None else: if start > 0: self.nw = line1[:start] else: self.nw = None if line1[start] != ' ': self.nn = line1[start] else: self.nn = None if end < len(line1): self.ne = line1[end:].rstrip() else: self.ne = None if start > 0: self.ww = line2[:start] else: self.ww = None if end < len(line2): self.ee = line2[end:] else: self.ee = None if line3 is None: self.sw = self.ss = self.se = None elif self.merge_se: self.sw = self.ss = None self.se = line3.rstrip() else: if start > 0: self.sw = line3[:start] else: self.sw = None if line3[start] != ' ': self.ss = line3[start] else: self.ss = None if end < len(line3): self.se = line3[end:].rstrip() else: self.se = None # Define parsing regexps. if self.merge_nw: self.regexp1 = re.compile(' *' + regexp_quote(self.nw) + '.*$') elif self.nw and not self.nn and not self.ne: self.regexp1 = re.compile(' *' + regexp_quote(self.nw) + '$') elif self.nw or self.nn or self.ne: self.regexp1 = re.compile( ' *' + regexp_quote(self.nw) + regexp_ruler(self.nn) + regexp_quote(self.ne) + '$') else: self.regexp1 = None if self.ww or self.ee: self.regexp2 = re.compile( ' *' + regexp_quote(self.ww) + '.*' + regexp_quote(self.ee) + '$') else: self.regexp2 = None if self.merge_se: self.regexp3 = re.compile('.*' + regexp_quote(self.se) + '$') elif self.sw and not self.ss and not self.se: self.regexp3 = re.compile(' *' + regexp_quote(self.sw) + '$') elif self.sw or self.ss or self.se: self.regexp3 = re.compile( ' *' + regexp_quote(self.sw) + regexp_ruler(self.ss) + regexp_quote(self.se) + '$') else: self.regexp3 = None # Save results. template_registry[style] = self def __cmp__(self, other): return cmp(self.weight, other.weight) def characters(self): """\ Return a string of characters which may be used to draw the box. """ characters = '' for text in (self.nw, self.nn, self.ne, self.ww, self.ee, self.sw, self.ss, self.se): if text: for character in text: if character not in characters: characters = characters + character return characters def match(self, lines): """\ Returns true if LINES exactly match this template. """ start = 0 end = len(lines) if self.regexp1 is not None: if start == end or not self.regexp1.match(lines[start]): return 0 start = start + 1 if self.regexp3 is not None: if end == 0 or not self.regexp3.match(lines[end-1]): return 0 end = end - 1 if self.regexp2 is not None: for line in lines[start:end]: if not self.regexp2.match(line): return 0 return 1 def unbuild(self, lines): """\ Remove all comment marks from LINES, as hinted by this template. Returns the cleaned up set of lines, and the size of the left margin. """ margin = left_margin_size(lines) # Remove box style marks. start = 0 end = len(lines) if self.regexp1 is not None: lines[start] = unbuild_clean(lines[start], self.regexp1) start = start + 1 if self.regexp3 is not None: lines[end-1] = unbuild_clean(lines[end-1], self.regexp3) end = end - 1 if self.regexp2 is not None: for counter in range(start, end): lines[counter] = unbuild_clean(lines[counter], self.regexp2) # Remove the left side of the box after it turned into spaces. delta = left_margin_size(lines) - margin for counter in range(len(lines)): lines[counter] = lines[counter][delta:] # Remove leading and trailing white lines. start = 0 end = len(lines) while start < end and lines[start] == '': start = start + 1 while end > start and lines[end-1] == '': end = end - 1 return lines[start:end], margin def build(self, lines, width, refill, margin): """\ Put LINES back into a boxed comment according to this template, after having refilled them if REFILL. The box should start at column MARGIN, and the total size of each line should ideally not go over WIDTH. """ # Merge a short end delimiter now, so it gets refilled with text. if self.merge_se: if lines: lines[-1] = lines[-1] + ' ' + self.se else: lines = [self.se] # Reduce WIDTH according to left and right inserts, then refill. if self.ww: width = width - len(self.ww) if self.ee: width = width - len(self.ee) if refill: lines = refill_lines(lines, width) # Reduce WIDTH further according to the current right margin, # and excluding the left margin. maximum = 0 for line in lines: if line: if line[-1] in '.!?': length = len(line) + 1 else: length = len(line) if length > maximum: maximum = length width = maximum - margin # Construct the top line. if self.merge_nw: lines[0] = ' ' * margin + self.nw + lines[0][margin:] start = 1 elif self.nw or self.nn or self.ne: if self.nn: line = self.nn * width else: line = ' ' * width if self.nw: line = self.nw + line if self.ne: line = line + self.ne lines.insert(0, (' ' * margin + line).rstrip()) start = 1 else: start = 0 # Construct all middle lines. for counter in range(start, len(lines)): line = lines[counter][margin:] line = line + ' ' * (width - len(line)) if self.ww: line = self.ww + line if self.ee: line = line + self.ee lines[counter] = (' ' * margin + line).rstrip() # Construct the bottom line. if self.sw or self.ss or self.se and not self.merge_se: if self.ss: line = self.ss * width else: line = ' ' * width if self.sw: line = self.sw + line if self.se and not self.merge_se: line = line + self.se lines.append((' ' * margin + line).rstrip()) return lines def regexp_quote(text): """\ Return a regexp matching TEXT without its surrounding space, maybe followed by spaces. If STRING is nil, return the empty regexp. Unless spaces, the text is nested within a regexp parenthetical group. """ if text is None: return '' if text == ' ' * len(text): return ' *' return '(' + re.escape(text.strip()) + ') *' def regexp_ruler(character): """\ Return a regexp matching two or more repetitions of CHARACTER, maybe followed by spaces. Is CHARACTER is nil, return the empty regexp. Unless spaces, the ruler is nested within a regexp parenthetical group. """ if character is None: return '' if character == ' ': return ' +' return '(' + re.escape(character + character) + '+) *' def unbuild_clean(line, regexp): """\ Return LINE with all parenthetical groups in REGEXP erased and replaced by an equivalent number of spaces, except for trailing spaces, which get removed. """ match = re.match(regexp, line) groups = match.groups() for counter in range(len(groups)): if groups[counter] is not None: start, end = match.span(1 + counter) line = line[:start] + ' ' * (end - start) + line[end:] return line.rstrip() ## Template data. # Matcher functions for a comment start, indexed by numeric LANGUAGE. language_matcher = [] for pattern in (r' *(/\*|//+|#+|;+|%+)', r'', # 1 r' */\*', # 2 r' *//+', # 3 r' *#+', # 4 r' *;+', # 5 r' *%+'): # 6 language_matcher.append(re.compile(pattern).match) # Template objects, indexed by numeric style. template_registry = {} def make_generic(style, weight, lines): """\ Add various language digit to STYLE and generate one template per language, all using the same WEIGHT. Replace `?' in LINES accordingly. """ for language, character in ((300, '/'), # C++ style comments (400, '#'), # scripting languages (500, ';'), # Lisp and assembler (600, '%')): # TeX and PostScript new_style = language + style if 310 < new_style <= 319: # Disallow quality 10 with C++. continue new_lines = [] for line in lines: new_lines.append(line.replace('?', character)) Template(new_style, weight, new_lines) # Generic programming language templates. make_generic(11, 115, ('? box',)) make_generic(12, 215, ('? box ?', '? --- ?')) make_generic(13, 315, ('? --- ?', '? box ?', '? --- ?')) make_generic(14, 415, ('? box ?', '???????')) make_generic(15, 515, ('???????', '? box ?', '???????')) make_generic(16, 615, ('?????', '? box', '?????')) make_generic(17, 715, ('?????', '? box', '?????')) make_generic(21, 125, ('?? box',)) make_generic(22, 225, ('?? box ??', '?? --- ??')) make_generic(23, 325, ('?? --- ??', '?? box ??', '?? --- ??')) make_generic(24, 425, ('?? box ??', '?????????')) make_generic(25, 525, ('?????????', '?? box ??', '?????????')) make_generic(26, 526, ('??????', '?? box', '??????')) make_generic(27, 527, ('??????', '?? box', '??????')) make_generic(31, 135, ('??? box',)) make_generic(32, 235, ('??? box ???', '??? --- ???')) make_generic(33, 335, ('??? --- ???', '??? box ???', '??? --- ???')) make_generic(34, 435, ('??? box ???', '???????????')) make_generic(35, 535, ('???????????', '??? box ???', '???????????')) make_generic(36, 536, ('???????', '??? box', '???????')) make_generic(37, 537, ('???????', '??? box', '???????')) make_generic(41, 145, ('???? box',)) make_generic(42, 245, ('???? box ????', '???? --- ????')) make_generic(43, 345, ('???? --- ????', '???? box ????', '???? --- ????')) make_generic(44, 445, ('???? box ????', '?????????????')) make_generic(45, 545, ('?????????????', '???? box ????', '?????????????')) make_generic(46, 546, ('????????', '???? box', '????????')) make_generic(47, 547, ('????????', '???? box', '????????')) # Textual (non programming) templates. Template(111, 113, ('box',)) Template(112, 213, ('| box |', '+-----+')) Template(113, 313, ('+-----+', '| box |', '+-----+')) Template(114, 413, ('| box |', '*=====*')) Template(115, 513, ('*=====*', '| box |', '*=====*')) Template(116, 613, ('+----', '| box', '+----')) Template(117, 713, ('*====', '| box', '*====')) Template(121, 123, ('| box |',)) Template(122, 223, ('| box |', '`-----\'')) Template(123, 323, ('.-----.', '| box |', '`-----\'')) Template(124, 423, ('| box |', '\\=====/')) Template(125, 523, ('/=====\\', '| box |', '\\=====/')) Template(126, 623, ('.----', '| box', '`----')) Template(127, 723, ('/====', '| box', '\\====')) Template(141, 143, ('| box ',)) Template(142, 243, ('* box *', '*******')) Template(143, 343, ('*******', '* box *', '*******')) Template(144, 443, ('X box X', 'XXXXXXX')) Template(145, 543, ('XXXXXXX', 'X box X', 'XXXXXXX')) Template(146, 643, ('*****', '* box', '*****')) Template(147, 743, ('XXXXX', 'X box', 'XXXXX')) # C language templates. Template(211, 118, ('/* box */',)) Template(212, 218, ('/* box */', '/* --- */')) Template(213, 318, ('/* --- */', '/* box */', '/* --- */')) Template(214, 418, ('/* box */', '/* === */')) Template(215, 518, ('/* === */', '/* box */', '/* === */')) Template(216, 618, ('/* ---', ' box', ' ---*/')) Template(217, 718, ('/* ===', ' box', ' ===*/')) Template(221, 128, ('/* ', ' box', '*/')) Template(222, 228, ('/* .', '| box |', '`----*/')) Template(223, 328, ('/*----.', '| box |', '`----*/')) Template(224, 428, ('/* \\', '| box |', '\\====*/')) Template(225, 528, ('/*====\\', '| box |', '\\====*/')) Template(226, 628, ('/*---', '| box', '`----*/')) Template(227, 728, ('/*===', '| box', '\\====*/')) Template(231, 138, ('/* ', ' | box', ' */ ')) Template(232, 238, ('/* ', ' | box | ', ' *-----*/')) Template(233, 338, ('/*-----* ', ' | box | ', ' *-----*/')) Template(234, 438, ('/* box */', '/*-----*/')) Template(235, 538, ('/*-----*/', '/* box */', '/*-----*/')) Template(236, 638, ('/*---- ', ' | box ', ' *----*/')) Template(237, 738, ('/*----', ' box', ' ----*/')) Template(241, 148, ('/* ', ' * box', ' */ ')) Template(242, 248, ('/* * ', ' * box * ', ' *******/')) Template(243, 348, ('/******* ', ' * box * ', ' *******/')) Template(244, 448, ('/* box */', '/*******/')) Template(245, 548, ('/*******/', '/* box */', '/*******/')) Template(246, 648, ('/******* ', ' * box * ', ' *******/')) Template(247, 748, ('/****', ' box', ' *****/')) Template(251, 158, ('/* ', ' * box', ' */ ')) if __name__ == '__main__': main(*sys.argv[1:])