summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--AUTHORS5
-rw-r--r--LICENSE16
-rw-r--r--Makefile67
-rw-r--r--TODO43
-rw-r--r--docs/generate.py362
-rw-r--r--docs/src/api.txt183
-rw-r--r--docs/src/cmdline.txt58
-rw-r--r--docs/src/formatterdev.txt169
-rw-r--r--docs/src/formatters.txt249
-rw-r--r--docs/src/index.txt51
-rw-r--r--docs/src/installation.txt47
-rw-r--r--docs/src/lexerdevelopment.txt482
-rw-r--r--docs/src/lexers.txt521
-rw-r--r--docs/src/quickstart.txt121
-rw-r--r--docs/src/rstdirective.txt42
-rw-r--r--docs/src/styles.txt119
-rw-r--r--docs/src/tokens.txt284
-rw-r--r--ez_setup.py221
-rw-r--r--lgpl.txt504
-rwxr-xr-xpygmentize4
-rw-r--r--pygments/__init__.py270
-rw-r--r--pygments/console.py74
-rw-r--r--pygments/formatter.py64
-rw-r--r--pygments/formatters/__init__.py73
-rw-r--r--pygments/formatters/bbcode.py99
-rw-r--r--pygments/formatters/html.py280
-rw-r--r--pygments/formatters/latex.py191
-rw-r--r--pygments/formatters/other.py73
-rw-r--r--pygments/formatters/terminal.py98
-rw-r--r--pygments/lexer.py486
-rw-r--r--pygments/lexers/__init__.py77
-rw-r--r--pygments/lexers/_luabuiltins.py256
-rw-r--r--pygments/lexers/_mapping.py102
-rw-r--r--pygments/lexers/_phpbuiltins.py3391
-rw-r--r--pygments/lexers/agile.py773
-rw-r--r--pygments/lexers/compiled.py314
-rw-r--r--pygments/lexers/dotnet.py221
-rw-r--r--pygments/lexers/other.py137
-rw-r--r--pygments/lexers/special.py75
-rw-r--r--pygments/lexers/templates.py323
-rw-r--r--pygments/lexers/text.py183
-rw-r--r--pygments/lexers/web.py332
-rw-r--r--pygments/style.py102
-rw-r--r--pygments/styles/__init__.py36
-rw-r--r--pygments/styles/autumn.py59
-rw-r--r--pygments/styles/borland.py44
-rw-r--r--pygments/styles/colorful.py75
-rw-r--r--pygments/styles/default.py70
-rw-r--r--pygments/styles/friendly.py67
-rw-r--r--pygments/styles/manni.py76
-rw-r--r--pygments/styles/murphy.py75
-rw-r--r--pygments/styles/native.py58
-rw-r--r--pygments/styles/pastie.py68
-rw-r--r--pygments/styles/perldoc.py64
-rw-r--r--pygments/styles/trac.py56
-rw-r--r--pygments/token.py156
-rw-r--r--pygments/util.py50
-rwxr-xr-xscripts/check_sources.py231
-rw-r--r--scripts/count_loc.py48
-rw-r--r--scripts/epydoc.css280
-rwxr-xr-xscripts/find_codetags.py205
-rwxr-xr-xscripts/fix_epydoc_markup.py46
-rw-r--r--scripts/pylintrc355
-rwxr-xr-xscripts/reindent.py291
-rw-r--r--setup.py31
-rw-r--r--tests/examplefiles/Intro.java1660
-rw-r--r--tests/examplefiles/ceval.c4349
-rw-r--r--tests/examplefiles/django_sample.html68
-rw-r--r--tests/examplefiles/example.c29989
-rw-r--r--tests/examplefiles/example.cpp13544
-rw-r--r--tests/examplefiles/example.pas2708
-rw-r--r--tests/examplefiles/example.rb10070
-rw-r--r--tests/examplefiles/example.rhtml561
-rw-r--r--tests/examplefiles/example.xhtml376
-rw-r--r--tests/examplefiles/example.xml7686
-rw-r--r--tests/examplefiles/fucked_up.rb77
-rw-r--r--tests/examplefiles/genshi_example.html132
-rw-r--r--tests/examplefiles/multiline_regexes.rb38
-rw-r--r--tests/examplefiles/numbers.c12
-rw-r--r--tests/examplefiles/perl5db.pl9428
-rw-r--r--tests/examplefiles/pleac.in.rb6368
-rw-r--r--tests/examplefiles/smarty_example.html209
-rw-r--r--tests/examplefiles/test.bas29
-rw-r--r--tests/examplefiles/test.boo39
-rw-r--r--tests/examplefiles/test.cs351
-rw-r--r--tests/examplefiles/test.html6342
-rw-r--r--tests/examplefiles/test.java653
-rw-r--r--tests/examplefiles/test.pas107
-rw-r--r--tests/examplefiles/test.php493
-rw-r--r--tests/examplefiles/test.rb158
-rw-r--r--tests/examplefiles/test.rhtml43
-rw-r--r--tests/run.py115
-rw-r--r--tests/test_basic_api.py12
-rw-r--r--tests/test_clexer.py35
-rw-r--r--tests/test_examplefiles.py49
-rw-r--r--tests/test_lexers.py10
96 files changed, 109694 insertions, 0 deletions
diff --git a/AUTHORS b/AUTHORS
new file mode 100644
index 00000000..aa4085e3
--- /dev/null
+++ b/AUTHORS
@@ -0,0 +1,5 @@
+Pygments is written and maintained by Georg Brandl <g.brandl@gmx.net>.
+
+Other major contributors are:
+ - Armin Ronacher <armin.ronacher@active-4.com>
+
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 00000000..76f95fdc
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,16 @@
+Pygments - Python syntax highlighting package
+Copyright (C) 2006 by the respective authors (see AUTHORS)
+
+This library is free software; you can redistribute it and/or
+modify it under the terms of the GNU Lesser General Public
+License as published by the Free Software Foundation; either
+version 2.1 of the License, or (at your option) any later version.
+
+This library 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
+Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public
+License along with this library; if not, write to the Free Software
+Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
diff --git a/Makefile b/Makefile
new file mode 100644
index 00000000..86938799
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,67 @@
+#
+# Makefile for Pygments
+# ~~~~~~~~~~~~~~~~~~~~~
+#
+# Combines scripts for common tasks.
+#
+# :copyright: 2006 by Georg Brandl.
+# :license: GNU GPL, see LICENSE for more details.
+#
+
+PYTHON ?= python
+
+export PYTHONPATH = $(shell python -c 'print ":".join(line.strip() for line in file("PYTHONPATH"))' 2>/dev/null)
+
+.PHONY: apidocs check clean clean-pyc codetags docs epydoc lexermap \
+ pylint reindent test
+
+apidocs: epydoc
+
+check:
+ @$(PYTHON) scripts/check_sources.py -i apidocs -i pygments/lexers/_mapping.py \
+ -i docs/build
+
+clean: clean-pyc
+ rm -f codetags.html
+ rm -rf apidocs
+
+clean-pyc:
+ find . -name '*.pyc' -exec rm -f {} +
+ find . -name '*.pyo' -exec rm -f {} +
+ find . -name '*~' -exec rm -f {} +
+
+codetags:
+ @$(PYTHON) scripts/find_codetags.py -i apidocs -i scripts/pylintrc \
+ -i scripts/find_codetags.py -o codetags.html .
+
+docs: docs/build
+
+docs/build: docs/src/*.txt
+ $(PYTHON) docs/generate.py html docs/build $?
+ touch docs/build
+
+epydoc:
+ @rm -rf apidocs
+ @$(PYTHON) -Wi:default_transform `which epydoc` -o apidocs --css scripts/epydoc.css \
+ --url http://trac.pocoo.org/pygments --no-frames --docformat restructuredtext \
+ -v pygments
+ @sed -i -e 's|^<br />||' \
+ -e 's|\s\+$$||' \
+ -e 's|^\s\+</pre>|</pre>|' \
+ -e 's|\(<table class="[^"]*"\) border="1"|\1|' \
+ -e 's|\(<table class="navbar" .*\) width="100%"|\1|' \
+ -e 's|<td width="15%"|<td class="spacer"|' \
+ apidocs/*.html
+ @$(PYTHON) scripts/fix_epydoc_markup.py apidocs
+
+lexermap:
+ cd pygments/lexers; $(PYTHON) _mapping.py
+
+pylint:
+ @pylint --rcfile scripts/pylintrc pygments
+
+reindent:
+ @$(PYTHON) scripts/reindent.py -r -B .
+
+test:
+ @$(PYTHON) tests/run.py
diff --git a/TODO b/TODO
new file mode 100644
index 00000000..415e0597
--- /dev/null
+++ b/TODO
@@ -0,0 +1,43 @@
+Todo
+====
+
+- allow multiple token types per regex (done, but awkwardly)
+- allow "overlay" token types (e.g. Diff + X)
+ - highlight specials: nth line, a word etc.
+ - dhtml: overlays toggleable by javascript
+
+- unit tests
+
+- docstrings?
+
+- lexers:
+ * HTML with special formatting
+ * ocaml
+ * nemerle
+ * scheme/lisp
+ * windows batch files
+ * assembler
+ * objective c
+ * bash
+ * mysql/postgresql/sqlite
+ * tcl
+ * (la)tex
+ * django templates
+
+- goto label HL support for languages that use it
+
+- add a `Punctuation` token type for symbols that are not text
+ but also not a symbol (blocks in ruby etc)
+
+- add support for function name highlighting to c++ lexer
+
+- styles should be able to define the overall background color
+
+- tell the DelphiLexer how to differ between Operators and
+ text.
+
+- review perl lexer (numerous bugs)
+
+- moin parser
+
+- add folding? would require more language-aware parsers...
diff --git a/docs/generate.py b/docs/generate.py
new file mode 100644
index 00000000..59da281c
--- /dev/null
+++ b/docs/generate.py
@@ -0,0 +1,362 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+"""
+ Generate Pygments Documentation
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+ Generates a bunch of html files containing the documentation.
+
+ :copyright: 2006 by Armin Ronacher, Georg Brandl.
+ :license: GNU LGPL, see LICENSE for more details.
+"""
+
+import os
+import sys
+from datetime import datetime
+from cgi import escape
+
+from docutils import nodes
+from docutils.parsers.rst import directives
+from docutils.core import publish_parts
+from docutils.writers import html4css1
+
+from jinja import Template, Context, StringLoader
+
+from pygments import highlight
+from pygments.lexers import get_lexer_by_name
+from pygments.formatters import HtmlFormatter
+
+
+PYGMENTS_FORMATTER = HtmlFormatter(style='friendly', cssclass='syntax')
+
+USAGE = '''\
+Usage: %s <mode> <destination> [<source.txt> ...]
+
+Generate either python or html files out of the documentation.
+
+Mode can either be python or html.\
+''' % sys.argv[0]
+
+TEMPLATE = '''\
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN"
+ "http://www.w3.org/TR/html4/strict.dtd">
+<html>
+<head>
+ <title>{{ title }} &mdash; Pygments</title>
+ <meta http-equiv="content-type" content="text/html; charset=utf-8">
+ <style type="text/css">
+ {{ style }}
+ </style>
+</head>
+<body>
+ <div id="content">
+ <h1 class="heading">Pygments</h1>
+ <h2 class="subheading">{{ title }}</h2>
+ {% if not file_id equals "index" %}
+ <a id="backlink" href="index.html">&laquo; Back To Index</a>
+ {% endif %}
+ {% if toc %}
+ <div class="toc">
+ <h2>Contents</h2>
+ <ul class="contents">
+ {% for item in toc %}
+ <li><a href="{{ item.0 }}">{{ item.1 }}</a></li>
+ {% endfor %}
+ </ul>
+ </div>
+ {% endif %}
+ {{ body }}
+ </div>
+</body>
+<!-- generated on: {{ generation_date }}
+ file id: {{ file_id }} -->
+</html>\
+'''
+
+STYLESHEET = '''\
+body {
+ background-color: #f2f2f2;
+ margin: 0;
+ padding: 0;
+ font-family: 'Georgia', serif;
+ color: #111;
+}
+
+#content {
+ background-color: white;
+ padding: 20px;
+ margin: 20px auto 20px auto;
+ max-width: 800px;
+ border: 4px solid #ddd;
+}
+
+h1 {
+ font-weight: normal;
+ font-size: 40px;
+ color: #09839A;
+}
+
+h2 {
+ font-weight: normal;
+ font-size: 30px;
+ color: #C73F00;
+}
+
+h1.heading {
+ margin: 0 0 30px 0;
+}
+
+h2.subheading {
+ margin: -30px 0 0 45px;
+}
+
+h3 {
+ margin-top: 30px;
+}
+
+table.docutils {
+ border-collapse: collapse;
+ border: 2px solid #aaa;
+ margin: 0.5em 1.5em 0.5em 1.5em;
+}
+
+table.docutils td {
+ padding: 2px;
+ border: 1px solid #ddd;
+}
+
+p, li, dd, dt, blockquote {
+ font-size: 15px;
+ color: #333;
+}
+
+p {
+ line-height: 150%;
+ margin-bottom: 0;
+ margin-top: 10px;
+}
+
+hr {
+ border-top: 1px solid #ccc;
+ border-bottom: 0;
+ border-right: 0;
+ border-left: 0;
+ margin-bottom: 10px;
+ margin-top: 20px;
+}
+
+dl {
+ margin-left: 10px;
+}
+
+li, dt {
+ margin-top: 5px;
+}
+
+dt {
+ font-weight: bold;
+}
+
+th {
+ text-align: left;
+}
+
+a {
+ color: #990000;
+}
+
+a:hover {
+ color: #c73f00;
+}
+
+pre {
+ background-color: #f0f0f0;
+ border-top: 1px solid #ccc;
+ border-bottom: 1px solid #ccc;
+ padding: 5px;
+ font-size: 13px;
+ font-family: Bitstream Vera Sans Mono,monospace;
+}
+
+tt {
+ font-size: 13px;
+ font-family: Bitstream Vera Sans Mono,monospace;
+ color: black;
+ padding: 1px 2px 1px 2px;
+ background-color: #f0f0f0;
+}
+
+cite {
+ /* abusing <cite>, it's generated by ReST for `x` */
+ font-size: 13px;
+ font-family: Bitstream Vera Sans Mono,monospace;
+ font-weight: bold;
+ font-style: normal;
+}
+
+#backlink {
+ float: right;
+ font-size: 11px;
+ color: #888;
+}
+
+div.toc {
+ margin: 0 0 10px 0;
+}
+
+div.toc h2 {
+ font-size: 20px;
+}
+'''
+
+
+def generate_documentation(data, link_style):
+ writer = DocumentationWriter(link_style)
+ parts = publish_parts(
+ data,
+ writer=writer,
+ settings_overrides={
+ 'initial_header_level': 3,
+ 'field_name_limit': 50,
+ }
+ )
+ return {
+ 'title': parts['title'].encode('utf-8'),
+ 'body': parts['body'].encode('utf-8'),
+ 'toc': parts['toc']
+ }
+
+
+def pygments_directive(name, arguments, options, content, lineno,
+ content_offset, block_text, state, state_machine):
+ try:
+ lexer = get_lexer_by_name(arguments[0])
+ except ValueError:
+ # no lexer found
+ lexer = get_lexer_by_name('text')
+ parsed = highlight(u'\n'.join(content), lexer, PYGMENTS_FORMATTER)
+ return [nodes.raw('', parsed, format="html")]
+pygments_directive.arguments = (1, 0, 1)
+pygments_directive.content = 1
+directives.register_directive('sourcecode', pygments_directive)
+
+
+class DocumentationWriter(html4css1.Writer):
+
+ def __init__(self, link_style):
+ html4css1.Writer.__init__(self)
+ self.translator_class = create_translator(link_style)
+
+ def translate(self):
+ html4css1.Writer.translate(self)
+ # generate table of contents
+ contents = self.build_contents(self.document)
+ contents_doc = self.document.copy()
+ contents_doc.children = contents
+ contents_visitor = self.translator_class(contents_doc)
+ contents_doc.walkabout(contents_visitor)
+ self.parts['toc'] = self._generated_toc
+
+ def build_contents(self, node, level=0):
+ sections = []
+ i = len(node) - 1
+ while i >= 0 and isinstance(node[i], nodes.section):
+ sections.append(node[i])
+ i -= 1
+ sections.reverse()
+ toc = []
+ for section in sections:
+ try:
+ reference = nodes.reference('', '', refid=section['ids'][0], *section[0])
+ except IndexError:
+ continue
+ ref_id = reference['refid']
+ text = escape(reference.astext().encode('utf-8'))
+ toc.append((ref_id, text))
+
+ self._generated_toc = [('#%s' % href, caption) for href, caption in toc]
+ # no further processing
+ return []
+
+
+def create_translator(link_style):
+ class Translator(html4css1.HTMLTranslator):
+ def visit_reference(self, node):
+ refuri = node.get('refuri')
+ if refuri is not None and '/' not in refuri and refuri.endswith('.txt'):
+ node['refuri'] = link_style(refuri[:-4])
+ html4css1.HTMLTranslator.visit_reference(self, node)
+ return Translator
+
+
+def handle_python(filename, fp, dst):
+ now = datetime.now()
+ title = os.path.basename(filename)[:-4]
+ content = fp.read()
+ def urlize(href):
+ # create links for the pygments webpage
+ if href == 'index.txt':
+ return '/docs/'
+ else:
+ return '/docs/%s/' % href
+ parts = generate_documentation(content, urlize)
+ result = file(os.path.join(dst, title + '.py'), 'w')
+ result.write('# -*- coding: utf-8 -*-\n')
+ result.write('"""\n Pygments Documentation - %s\n' % title)
+ result.write(' %s\n\n' % ('~' * (24 + len(title))))
+ result.write(' Generated on: %s\n"""\n\n' % now)
+ result.write('import datetime\n')
+ result.write('DATE = %r\n' % now)
+ result.write('TITLE = %r\n' % parts['title'])
+ result.write('TOC = %r\n' % parts['toc'])
+ result.write('BODY = %r\n' % parts['body'])
+ result.close()
+
+
+def handle_html(filename, fp, dst):
+ now = datetime.now()
+ title = os.path.basename(filename)[:-4]
+ content = fp.read()
+ parts = generate_documentation(content, (lambda x: './%s.html' % x))
+ result = file(os.path.join(dst, title + '.html'), 'w')
+ c = Context(parts)
+ c['style'] = STYLESHEET + PYGMENTS_FORMATTER.get_style_defs('.syntax')
+ c['generation_date'] = now
+ c['file_id'] = title
+ t = Template(TEMPLATE, StringLoader())
+ result.write(t.render(c).encode('utf-8'))
+ result.close()
+
+
+def run(handle_file, dst, sources=()):
+ path = os.path.abspath(os.path.join(os.path.dirname(__file__), 'src'))
+ if not sources:
+ sources = [os.path.join(path, fn) for fn in os.listdir(path)]
+ for fn in sources:
+ if not os.path.isfile(fn):
+ continue
+ print 'Processing %s' % fn
+ f = open(fn)
+ try:
+ handle_file(fn, f, dst)
+ finally:
+ f.close()
+
+
+def main(mode, dst='build/', *sources):
+ try:
+ handler = {
+ 'html': handle_html,
+ 'python': handle_python
+ }[mode]
+ except KeyError:
+ print 'Error: unknown mode "%s"' % mode
+ sys.exit(1)
+ run(handler, os.path.realpath(dst), sources)
+
+
+if __name__ == '__main__':
+ if len(sys.argv) == 1:
+ print USAGE
+ else:
+ main(*sys.argv[1:])
diff --git a/docs/src/api.txt b/docs/src/api.txt
new file mode 100644
index 00000000..880687f1
--- /dev/null
+++ b/docs/src/api.txt
@@ -0,0 +1,183 @@
+.. -*- mode: rst -*-
+
+====================
+The full Pygments API
+====================
+
+This page describes the Pygments API.
+
+High-level API
+==============
+
+Functions from the `pygments` module:
+
+def `lex(code, lexer):`
+ Lex `code` with the `lexer` (must be a `Lexer` instance)
+ and return an iterable of tokens. Currently, this only calls
+ `lexer.get_tokens()`.
+
+def `format(tokens, formatter, outfile=None):`
+ Format a token stream (iterable of tokens) `tokens` with the
+ `formatter` (must be a `Formatter` instance). The result is
+ written to `outfile`, or if that is ``None``, returned as a
+ string.
+
+def `highlight(code, lexer, formatter, outfile=None):`
+ This is the most high-level highlighting function.
+ It combines `lex` and `format` in one function.
+
+
+Functions from `pygments.lexers`:
+
+def `get_lexer_by_name(alias, **options):`
+ Return an instance of a `Lexer` subclass that has `alias` in its
+ aliases list. The lexer is given the `options` at its
+ instantiation.
+
+ Will raise `ValueError` if no lexer with that alias is found.
+
+def `get_lexer_for_filename(fn, **options):`
+ Return a `Lexer` subclass instance that has a filename pattern
+ matching `fn`. The lexer is given the `options` at its
+ instantiation.
+
+ Will raise `ValueError` if no lexer for that filename is found.
+
+
+Functions from `pygments.formatters`:
+
+def `get_formatter_by_name(alias, **options):`
+ Return an instance of a `Formatter` subclass that has `alias` in its
+ aliases list. The formatter is given the `options` at its
+ instantiation.
+
+ Will raise `ValueError` if no formatter with that alias is found.
+
+def `get_formatter_for_filename(fn, **options):`
+ Return a `Formatter` subclass instance that has a filename pattern
+ matching `fn`. The formatter is given the `options` at its
+ instantiation.
+
+ Will raise `ValueError` if no formatter for that filename is found.
+
+
+Lexers
+======
+
+A lexer (derived from `pygments.lexer.Lexer`) has the following functions:
+
+def `__init__(self, **options):`
+ The constructor. Takes a \*\*keywords dictionary of options.
+ Every subclass must first process its own options and then call
+ the `Lexer` constructor, since it processes the `stripnl`,
+ `stripall` and `tabsize` options.
+
+ An example looks like this:
+
+ .. sourcecode:: python
+
+ def __init__(self, **options):
+ self.compress = options.get('compress', '')
+ Lexer.__init__(self, **options)
+
+ As these options must all be specifiable as strings (due to the
+ command line usage), there are various utility functions
+ available to help with that, see `Option processing`_.
+
+def `get_tokens(self, text):`
+ This method is the basic interface of a lexer. It is called by
+ the `highlight()` function. It must process the text and return an
+ iterable of ``(tokentype, value)`` pairs from `text`.
+
+ Normally, you don't need to override this method. The default
+ implementation processes the `stripnl`, `stripall` and `tabsize`
+ options and then yields all tokens from `get_tokens_unprocessed()`,
+ with the ``index`` dropped.
+
+def `get_tokens_unprocessed(self, text):`
+ This method should process the text and return an iterable of
+ ``(index, tokentype, value)`` tuples where ``index`` is the starting
+ position of the token within the input text.
+
+ This method must be overridden by subclasses.
+
+For a list of known tokens have a look at the `Tokens`_ page.
+
+The lexer also recognizes the following attributes that are used by the
+builtin lookup mechanism.
+
+`name`
+ Full name for the lexer, in human-readable form.
+
+`aliases`
+ A list of short, unique identifiers that can be used to lookup
+ the lexer from a list.
+
+`filenames`
+ A list of `fnmatch` patterns that can be used to find a lexer for
+ a given filename.
+
+
+.. _Tokens: tokens.txt
+
+
+Formatters
+==========
+
+A formatter (derived from `pygments.formatter.Formatter`) has the following
+functions:
+
+def `__init__(self, **options):`
+ As with lexers, this constructor processes options and then must call
+ the base class `__init__`.
+
+ The `Formatter` class recognizes the options `style`, `full` and
+ `title`. It is up to the formatter class whether it uses them.
+
+def `get_style_defs(self, arg=''):`
+ This method must return statements or declarations suitable to define
+ the current style for subsequent highlighted text (e.g. CSS classes
+ in the `HTMLFormatter`).
+
+ The optional argument `arg` can be used to modify the generation and
+ is formatter dependent (it is standardized because it can be given on
+ the command line).
+
+ This method is called by the ``-S`` `command-line option`_, the `arg`
+ is then given by the ``-a`` option.
+
+def `format(self, tokensource, outfile):`
+ This method must format the tokens from the `tokensource` iterable and
+ write the formatted version to the file object `outfile`.
+
+ Formatter options can control how exactly the tokens are converted.
+
+.. _command-line option: cmdline.txt
+
+
+Option processing
+=================
+
+The `pygments.util` module has some utility functions usable for option
+processing:
+
+class `OptionError`
+ This exception will be raised by all option processing functions if
+ the type of the argument is not correct.
+
+def `get_bool_opt(options, optname, default=None):`
+ Interpret the key `optname` from the dictionary `options`
+ as a boolean and return it. Return `default` if `optname`
+ is not in `options`.
+
+ The valid string values for ``True`` are ``1``, ``yes``,
+ ``true`` and ``on``, the ones for ``False`` are ``0``,
+ ``no``, ``false`` and ``off`` (matched case-insensitively).
+
+def `get_int_opt(options, optname, default=None):`
+ As `get_bool_opt`, but interpret the value as an integer.
+
+def `get_list_opt(options, optname, default=None):`
+ If the key `optname` from the dictionary `options` is a string,
+ split it at whitespace and return it. If it is already a list
+ or a tuple, it is returned as a list.
diff --git a/docs/src/cmdline.txt b/docs/src/cmdline.txt
new file mode 100644
index 00000000..461ecb32
--- /dev/null
+++ b/docs/src/cmdline.txt
@@ -0,0 +1,58 @@
+.. -*- mode: rst -*-
+
+======================
+Command Line Interface
+======================
+
+You can use Pygments from the shell, provided you installed the `pygmentize` script::
+
+ $ pygmentize test.py
+ print "Hello World"
+
+will print the file test.py to standard output, using the Python lexer
+(inferred from the file name extension) and the terminal formatter (because
+you didn't give an explicit formatter name).
+
+If you want HTML output::
+
+ $ pygmentize -f html -l python -o test.html test.py
+
+As you can see, the -l option explicitly selects a lexer. As seen above, if you
+give an input file name and it has an extension that Pygments recognizes, you can
+omit this option.
+
+The ``-o`` option gives an output file name. If it is not given, output is
+written to stdout.
+
+The ``-f`` option selects a formatter (as with ``-l``, it can also be omitted
+if an output file name is given and has a supported extension).
+If no output file name is given and ``-f`` is omitted, the
+`TerminalFormatter` is used.
+
+The above command could therefore also be given as::
+
+ $ pygmentize -o test.html test.py
+
+Lexer and formatter options can be given using the ``-O`` option::
+
+ $ pygmentize -f html -O style=colorful,linenos=1 -l python test.py
+
+Be sure to enclose the option string in quotes if it contains any special
+shell characters, such as spaces or expansion wildcards like ``*``.
+
+There's a special ``-S`` option for generating style definitions. Usage is
+as follows::
+
+ $ pygmentize -f html -S colorful -a .syntax
+
+generates a CSS style sheet (because you selected the HTML formatter) for
+the "colorful" style prepending a ".syntax" selector to all style rules.
+
+For an explanation what ``-a`` means for `a particular formatter`_, look for
+the `arg` argument for the formatter's `get_style_defs()` method.
+
+The ``-L`` option lists all lexers and formatters, along with their short
+names and supported file name extensions.
+
+
+.. _a particular formatter: formatters.txt
diff --git a/docs/src/formatterdev.txt b/docs/src/formatterdev.txt
new file mode 100644
index 00000000..82208aa0
--- /dev/null
+++ b/docs/src/formatterdev.txt
@@ -0,0 +1,169 @@
+.. -*- mode: rst -*-
+
+========================
+Write your own formatter
+========================
+
+As well as creating `your own lexer <lexerdevelopment.txt>`_, writing a new
+formatter for Pygments is easy and straightforward.
+
+A formatter is a class that is initialized with some keyword arguments (the
+formatter options) and that must provides a `format()` method.
+Additionally a formatter should provide a `get_style_defs()` method that
+returns the style definitions from the style in a form usable for the
+formatter's output format.
+
+
+Quickstart
+==========
+
+The most basic formatter shipped with Pygments is the `NullFormatter`. It just
+sends the value of a token to the output stream:
+
+.. sourcecode:: python
+
+ from pygments.formatter import Formatter
+
+ class NullFormatter(Formatter):
+ def format(self, tokensource, outfile):
+ for ttype, value in tokensource:
+ outfile.write(value)
+
+As you can see, the `format()` method is passed two parameters: `tokensource`
+and `outfile`. The first is an iterable of ``(token_type, value)`` tuples,
+the latter a file like object with a `write()` method.
+
+Because the formatter is that basic it doesn't overwrite the `get_style_defs()`
+method.
+
+
+Styles
+======
+
+Styles aren't instantiated but their metaclass provides some class functions
+so that you can access the style definitions easily.
+
+Styles are iterable and yield tuples in the form ``(ttype, d)`` where `ttype`
+is a token and `d` is a dict with the following keys:
+
+``'color'``
+ Hexadecimal color value (eg: ``'ff0000'`` for red) or `None` if not
+ defined.
+
+``'bold'``
+ `True` if the value should be bold
+
+``'italic'``
+ `True` if the value should be italic
+
+``'underline'``
+ `True` if the value should be underlined
+
+``'bgcolor'``
+ Hexadecimal color value for the background (eg: ``'eeeeeee'`` for light
+ gray) or `None` if not defined.
+
+``'border'``
+ Hexadecimal color value for the border (eg: ``'0000aa'`` for a dark
+ blue) or `None` for no border.
+
+Additional keys might appear in the future, formatters should ignore all keys
+they don't support.
+
+
+HTML 3.2 Formatter
+==================
+
+For an more complex example, let's implement a HTML 3.2 Formatter. We don't
+use CSS but inline markup (``<u>``, ``<font>``, etc). Because this isn't good
+style this formatter isn't in the standard library ;-)
+
+.. sourcecode:: python
+
+ from pygments.formatter import Formatter
+
+ class OldHtmlFormatter(Formatter):
+
+ def __init__(self, **options):
+ Formatter.__init__(self, **options)
+
+ # create a dict of (start, end) tuples that wrap the
+ # value of a token so that we can use it in the format
+ # method later
+ self.styles = {}
+
+ # we iterate over the `_styles` attribute of a style item
+ # that contains the parsed style values.
+ for token, style in self.style:
+ start = end = ''
+ # a style item is a tuple in the following form:
+ # colors are readily specified in hex: 'RRGGBB'
+ if style['color']:
+ start += '<font color="#%s">' % color
+ end += '</font>'
+ if style['bold']:
+ start += '<b>'
+ end += '</b>'
+ if style['italic']:
+ start += '<i>'
+ end += '</i>'
+ if style['underline']:
+ start += '<u>'
+ end += '</u>'
+ self.styles[token] = (start, end)
+
+ def format(self, tokensource, outfile):
+ # lastval is a string we use for caching
+ # because it's possible that an lexer yields a number
+ # of consecutive tokens with the same token type.
+ # to minimize the size of the generated html markup we
+ # try to join the values of same-type tokens here
+ lastval = ''
+ lasttype = None
+
+ # wrap the whole output with <pre>
+ outfile.write('<pre>')
+
+ for ttype, value in tokensource:
+ # if the token type doesn't exist in the stylemap
+ # we try it with the parent of the token type
+ # eg: parent of Token.Literal.String.Double is
+ # Token.Literal.String
+ while ttype not in self.styles:
+ ttype = ttype.parent
+ if ttype == lasttype:
+ # the current token type is the same of the last
+ # iteration. cache it
+ lastval += value
+ else:
+ # not the same token as last iteration, but we
+ # have some data in the buffer. wrap it with the
+ # defined style and write it to the output file
+ if lastval:
+ stylebegin, styleend = self.styles[lasttype]
+ outfile.write(stylebegin + lastval + styleend)
+ # set lastval/lasttype to current values
+ lastval = value
+ lasttype = ttype
+
+ # if something is left in the buffer, write it to the
+ # output file, then close the opened <pre> tag
+ if lastval:
+ stylebegin, styleend = self.styles[lasttype]
+ outfile.write(stylebegin + lastval + styleend)
+ outfile.write('</pre>\n')
+
+The comments should explain it. Again, this formatter doesn't override the
+`get_style_defs()` method. If we would have used CSS classes instead of
+inline HTML markup, we would need to generate the CSS first. For that
+purpose the `get_style_defs()` method exists:
+
+
+Generating Style Definitions
+============================
+
+Some formatters like the `LatexFormatter` and the `HtmlFormatter` don't
+output inline markup but reference either macros or css classes. Because
+the definitions of those are not part of the output, the `get_style_defs()`
+method exists. It is passed one parameter (if it's used and how it's used
+is up to the formatter) and has to return a string or ``None``.
diff --git a/docs/src/formatters.txt b/docs/src/formatters.txt
new file mode 100644
index 00000000..08b52cc7
--- /dev/null
+++ b/docs/src/formatters.txt
@@ -0,0 +1,249 @@
+.. -*- mode: rst -*-
+
+====================
+Available formatters
+====================
+
+This page lists all builtin formatters.
+
+Common options
+==============
+
+The `HtmlFormatter` and `LatexFormatter` classes support these options:
+
+`style`
+ The style to use, can be a string or a Style subclass (default:
+ ``'default'``).
+
+`full`
+ Tells the formatter to output a "full" document, i.e. a complete
+ self-contained document (default: ``False``).
+
+`title`
+ If `full` is true, the title that should be used to caption the
+ document (default: ``''``).
+
+`linenos`
+ If set to ``True``, output line numbers (default: ``False``).
+
+`linenostart`
+ The line number for the first line (default: ``1``).
+
+`linenostep`
+ If set to a number n > 1, only every nth line number is printed.
+
+
+Formatter classes
+=================
+
+All these classes are importable from `pygments.formatters`.
+
+
+`HtmlFormatter`
+---------------
+
+ Formats tokens as HTML 4 ``<span>`` tags within a ``<pre>`` tag, wrapped
+ in a ``<div>`` tag. The ``<div>``'s CSS class can be set by the `cssclass`
+ option.
+
+ If the `linenos` option is given and true, the ``<pre>`` is additionally
+ wrapped inside a ``<table>`` which has one row and two cells: one
+ containing the line numbers and one containing the code. Example:
+
+ .. sourcecode:: html
+
+ <div class="highlight" >
+ <table><tr>
+ <td class="linenos" title="click to toggle"
+ onclick="with (this.firstChild.style)
+ { display = (display == '') ? 'none' : '' }">
+ <pre>1
+ 2</pre>
+ </td>
+ <td class="code">
+ <pre><span class="Ke">def </span><span class="NaFu">foo</span>(bar):
+ <span class="Ke">pass</span>
+ </pre>
+ </td>
+ </tr></table></div>
+
+ (whitespace added to improve clarity). Wrapping can be disabled using the
+ `nowrap` option.
+
+ With the `full` option, a complete HTML 4 document is output, including
+ the style definitions inside a ``<style>`` tag.
+
+ The `get_style_defs(arg='')` method of a `HtmlFormatter` returns a string
+ containing CSS rules for the CSS classes used by the formatter. The
+ argument `arg` can be used to specify additional CSS selectors that
+ are prepended to the classes. A call `fmter.get_style_defs('td .code')`
+ would result in the following CSS classes:
+
+ .. sourcecode:: css
+
+ td .code .kw { font-weight: bold; color: #00FF00 }
+ td .code .cm { color: #999999 }
+ ...
+
+ Additional options accepted by the `HtmlFormatter`:
+
+ `nowrap`
+ If set to ``True``, don't wrap the tokens at all, not even in a ``<pre>``
+ tag. This disables all other options (default: ``False``).
+
+ `noclasses`
+ If set to true, token ``<span>`` tags will not use CSS classes, but
+ inline styles. This is not recommended for larger pieces of code since
+ it increases output size by quite a bit (default: ``False``).
+
+ `classprefix`
+ Since the token types use relatively short class names, they may clash
+ with some of your own class names. In this case you can use the
+ `classprefix` option to give a string to prepend to all Pygments-generated
+ CSS class names for token types.
+ Note that this option also affects the output of `get_style_defs()`.
+
+ `cssclass`
+ CSS class for the wrapping ``<div>`` tag (default: ``'highlight'``).
+
+ `cssstyles`
+ Inline CSS styles for the wrapping ``<div>`` tag (default: ``''``).
+
+ `linenospecial`
+ If set to a number n > 0, every nth line number is given the CSS
+ class ``"special"`` (default: ``0``).
+
+ :Aliases: ``html``
+ :Filename patterns: ``*.html``, ``*.htm``
+
+
+`LatexFormatter`
+----------------
+
+ Formats tokens as LaTeX code. This needs the `fancyvrb` and `color`
+ standard packages.
+
+ Without the `full` option, code is formatted as one ``Verbatim``
+ environment, like this:
+
+ .. sourcecode:: latex
+
+ \begin{Verbatim}[commandchars=@\[\]]
+ @Can[def ]@Cax[foo](bar):
+ @Can[pass]
+ \end{Verbatim}
+
+ The command sequences used here (``@Can`` etc.) are generated from the given
+ `style` and can be retrieved using the `get_style_defs` method.
+
+ With the `full` option, a complete LaTeX document is output, including
+ the command definitions in the preamble.
+
+ The `get_style_defs(arg='')` method of a `LatexFormatter` returns a string
+ containing ``\newcommand`` commands defining the commands used inside the
+ ``Verbatim`` environments. If the argument `arg` is true,
+ ``\renewcommand`` is used instead.
+
+ Additional options accepted by the `LatexFormatter`:
+
+ `docclass`
+ If the `full` option is enabled, this is the document class to use
+ (default: ``'article'``).
+
+ `preamble`
+ If the `full` option is enabled, this can be further preamble commands,
+ e.g. ``\usepackage`` (default: ``''``).
+
+ `verboptions`
+ Additional options given to the Verbatim environment (see the *fancyvrb*
+ docs for possible values) (default: ``''``).
+
+ :Aliases: ``latex``, ``tex``
+ :Filename pattern: ``*.tex``
+
+
+`BBCodeFormatter`
+-----------------
+
+ Formats tokens with BBcodes. These formatting codes are used by many
+ bulletin boards, so you can highlight your sourcecode with pygments before
+ posting it there.
+
+ This formatter has no support for background colors and borders, as there
+ are no common BBcode tags for that.
+
+ Some board systems (e.g. phpBB) don't support colors in their [code] tag,
+ so you can't use the highlighting together with that tag.
+ Text in a [code] tag usually is shown with a monospace font (which this
+ formatter can do with the ``monofont`` option) and no spaces (which you
+ need for indentation) are removed.
+
+ The `BBCodeFormatter` accepts two additional option:
+
+ `codetag`
+ If set to true, put the output into ``[code]`` tags (default:
+ ``false``)
+
+ `monofont`
+ If set to true, add a tag to show the code with a monospace font
+ (default: ``false``).
+
+ :Aliases: ``bbcode``, ``bb``
+ :Filename pattern: None
+
+
+`TerminalFormatter`
+-------------------
+
+ Formats tokens with ANSI color sequences, for output in a text console.
+ Color sequences are terminated at newlines, so that paging the output
+ works correctly.
+
+ The `get_style_defs()` method doesn't do anything special since there is
+ no support for common styles.
+
+ The TerminalFormatter class supports only these options:
+
+ `bg`
+ Set to ``"light"`` or ``"dark"`` depending on the terminal's background
+ (default: ``"light"``).
+
+ `colorscheme`
+ A dictionary mapping token types to (lightbg, darkbg) color names or
+ ``None`` (default: ``None`` = use builtin colorscheme).
+
+ `debug`
+ If this option is true, output the string "<<ERROR>>" after each error
+ token. This is meant as a help for debugging Pygments (default: ``False``).
+
+ :Aliases: ``terminal``, ``console``
+ :Filename pattern: None
+
+
+`RawTokenFormatter`
+-------------------
+
+ Formats tokens as a raw representation for storing token streams.
+
+ The format is ``tokentype<TAB>repr(tokenstring)\n``. The output can later
+ be converted to a token stream with the `RawTokenLexer`, described in the
+ `lexer list <lexers.txt>`_.
+
+ One option is accepted:
+
+ `compress`
+ If set to ``'gz'`` or ``'bz2'``, compress the output with the given
+ compression algorithm after encoding (default: ``''``).
+
+ :Aliases: ``raw``, ``tokens``
+ :Filename pattern: ``*.raw``
+
+
+`NullFormatter`
+---------------
+
+ Just output all tokens, don't format in any way.
+
+ :Aliases: ``text``, ``null``
+ :Filename pattern: ``*.txt``
+
diff --git a/docs/src/index.txt b/docs/src/index.txt
new file mode 100644
index 00000000..33874d1c
--- /dev/null
+++ b/docs/src/index.txt
@@ -0,0 +1,51 @@
+.. -*- mode: rst -*-
+
+========
+Overview
+========
+
+Welcome to the Pygments documentation.
+
+- Starting with Pygments
+
+ - `Installation <installation.txt>`_
+
+ - `Quickstart <quickstart.txt>`_
+
+ - `Command line interface <cmdline.txt>`_
+
+- Essential to know
+
+ - `Builtin lexers <lexers.txt>`_
+
+ - `Builtin formatters <formatters.txt>`_
+
+ - `Styles <styles.txt>`_
+
+- API and more
+
+ - `API documentation <api.txt>`_
+
+ - `Builtin Tokens <tokens.txt>`_
+
+- Hacking for Pygments
+
+ - `Write your own lexer <lexerdevelopment.txt>`_
+
+ - `Write your own formatter <formatterdev.txt>`_
+
+- Hints and Tricks
+
+ - `Using Pygments in ReST documents <rstdirective.txt>`_
+
+
+--------------
+
+If you find bugs or have suggestions for the documentation, please
+look `here`_ for info on how to contact the team.
+
+You can download an offline version of this documentation from the
+`download page`_.
+
+.. _here: http://pygments.pocoo.org/contribute
+.. _download page: http://pygments.pocoo.org/download
diff --git a/docs/src/installation.txt b/docs/src/installation.txt
new file mode 100644
index 00000000..708592a8
--- /dev/null
+++ b/docs/src/installation.txt
@@ -0,0 +1,47 @@
+.. -*- mode: rst -*-
+
+============
+Installation
+============
+
+Pygments requires at least Python 2.3 to work correctly. Just to clarify:
+there *wont't* ever be support for Python versions below 2.3.
+
+
+Install the Release Version
+===========================
+
+1. download the recent tarball from the `download page`_
+2. unpack the tarball
+3. ``sudo python setup.py install``
+
+Note that the last command will automatically download and install
+`setuptools`_ if you don't already have it installed. This requires a working
+internet connection.
+
+This will install Pygments into your Python installation's site-packages directory.
+
+
+Install via easy_install
+========================
+
+You can also install the most recent Pygments version using `easy_install`_::
+
+ sudo easy_install Pygments
+
+This will install a Pygments egg in your Python installation's site-packages
+directory.
+
+
+Installing the development Version
+==================================
+
+1. Install `subversion`_
+2. ``svn co http://trac.pocoo.org/repos/pygments/trunk pygments``
+3. ``ln -s `pwd`/pygments/pygments /usr/lib/python2.X/site-packages``
+
+
+.. _download page: http://pygments.pocoo.org/download/
+.. _setuptools: http://peak.telecommunity.com/DevCenter/setuptools
+.. _easy_install: http://peak.telecommunity.com/DevCenter/EasyInstall
+.. _subversion: http://subversion.tigris.org/
diff --git a/docs/src/lexerdevelopment.txt b/docs/src/lexerdevelopment.txt
new file mode 100644
index 00000000..619c5c39
--- /dev/null
+++ b/docs/src/lexerdevelopment.txt
@@ -0,0 +1,482 @@
+.. -*- mode: rst -*-
+
+====================
+Write your own lexer
+====================
+
+If a lexer for your favorite language is missing in the Pygments package, you can
+easily write your own and extend Pygments.
+
+All you need can be found inside the `pygments.lexer` module. As you can read in
+the `API documentation <api.txt>`_, a lexer is a class that is initialized with
+some keyword arguments (the lexer options) and that provides a
+`get_tokens_unprocessed()` method which is given a string or unicode object with
+the data to parse.
+
+The `get_tokens_unprocessed()` method must return an iterator or iterable
+containing tuples in the form ``(index, token, value)``. Normally you don't need
+to do this since there are numerous base lexers you can subclass.
+
+
+RegexLexer
+==========
+
+A very powerful (but quite easy to use) lexer is the `RegexLexer`. This lexer
+base class allows you to define lexing rules in terms of *regular expressions*
+for different *states*.
+
+States are groups of regular expressions that are matched against the input
+string at the *current position*. If one of these expressions matches, a
+corresponding action is performed (normally yielding a token with a specific
+type), the current position is set to where the last match ended and the
+matching process continues with the first regex of the current state.
+
+Lexer states are kept in a state stack: each time a new state is entered, the
+new state is pushed onto the stack. The most basic lexers (like the
+`DiffLexer`) just need one state.
+
+Each state is defined as a list of tuples in the form (`regex`, `action`,
+`new_state`) where the last item is optional. In the most basic form, `action`
+is a token type (like `Name.Builtin`). That means: When `regex` matches, emit a
+token with the match text and type `tokentype` and push `new_state` on the state
+stack. If the new state is ``'#pop'``, the topmost state is popped from the
+stack instead. (To pop more than one state, use ``'#pop:2'`` and so on.)
+``'#push'`` is a synonym for pushing the current state on the
+stack.
+
+The following example shows the `DiffLexer` from the builtin lexers. Note that
+it contains some additional attributes `name`, `aliases` and `filenames` which
+aren't required for a lexer. They are used by the builtin lexer lookup
+functions.
+
+.. sourcecode:: python
+
+ from pygments.lexer import RegexLexer
+ from pygments.token import \
+ Text, Comment, Keyword, Name, String, Generic
+
+ class DiffLexer(RegexLexer):
+ name = 'Diff'
+ aliases = ['diff']
+ filenames = ['*.diff']
+
+ tokens = {
+ 'root': [
+ (r' .*\n', Text),
+ (r'\+.*\n', Generic.Inserted),
+ (r'-.*\n', Generic.Deleted),
+ (r'@.*\n', Generic.Subheading),
+ (r'Index.*\n', Generic.Heading),
+ (r'=.*\n', Generic.Heading),
+ (r'.*\n', Text),
+ ]
+ }
+
+As you can see this lexer only uses one state. When the lexer starts scanning
+the text, it first checks if the current character is a space. If this is true
+it scans everything until newline and returns the parsed data as `Text` token.
+
+If this rule doesn't match, it checks if the current char is a plus sign. And
+so on.
+
+If no rule matches at the current position, the current char is emitted as an
+`Error` token that indicates a parsing error, and the position is increased by
+1.
+
+
+Regex Flags
+===========
+
+You can either define regex flags in the regex (``r'(?x)foo bar'``) or by adding
+a `flags` attribute to your lexer class. If no attribute is defined, it defaults
+to `re.MULTILINE`. For more informations about regular expression flags see the
+`regular expressions`_ help page in the python documentation.
+
+.. _regular expressions: http://docs.python.org/lib/re-syntax.html
+
+
+Scanning multiple tokens at once
+================================
+
+Here is a more complex lexer that highlights INI files. INI files consist of
+sections, comments and key = value pairs:
+
+.. sourcecode:: python
+
+ from pygments.lexer import RegexLexer, bygroups
+
+ class IniLexer(RegexLexer):
+ name = 'INI'
+ aliases = ['ini', 'cfg']
+ filenames = ['*.ini', '*.cfg']
+
+ tokens = {
+ 'root': [
+ (r'\s+', Text),
+ (r';.*?$', Comment),
+ (r'\[.*?\]$', Keyword),
+ (r'(.*?)(\s*)(=)(\s*)(.*?)$',
+ bygroups(Name.Attribute, Text, Operator, Text, String))
+ ]
+ }
+
+The lexer first looks for whitespace, comments and section names. And later it
+looks for a line that looks like a key, value pair, seperated by an ``'='``
+sign, and optional whitespace.
+
+The `bygroups` helper makes sure that aach group is yielded with a different
+token type. First the `Name.Attribute` token, then a `Text` token for the
+optional whitespace, after that a `Operator` token for the equals sign. Then a
+`Text` token for the whitespace again. The rest of the line is returned as
+`String`.
+
+Note that for this to work, every part of the match must be inside a capturing
+group (a ``(...)``), and there must not be any nested capturing groups. If you
+nevertheless need a group, use a non-capturing group defined using this syntax:
+``r'(?:some|words|here)'`` (note the ``?:`` after the beginning parenthesis).
+
+
+Changing states
+===============
+
+Many lexers need multiple states to work as expected. For example, some
+languages allow multiline comments to be nested. Since this is a recursive
+pattern it's impossible to lex just using regular expressions.
+
+Here is the solution:
+
+.. sourcecode:: python
+
+ class ExampleLexer(RegexLexer):
+ name = 'Example Lexer with states'
+
+ tokens = {
+ 'root': [
+ (r'[^/]+', Text),
+ (r'/\*', Comment.Multiline, 'comment'),
+ (r'//.*?$', Comment.Singleline),
+ (r'/', Text)
+ ],
+ 'comment': [
+ (r'[^*/]', Comment.Multiline),
+ (r'/\*', Comment.Multiline, '#push'),
+ (r'\*/', Comment.Multiline, '#pop'),
+ (r'[*/]', Comment.Multiline)
+ ]
+ }
+
+This lexer starts lexing in the ``'root'`` state. It tries to match as much as
+possible until it finds a slash (``'/'``). If the next character after the slash
+is a star (``'*'``) the `RegexLexer` sends those two characters to the output
+stream marked as `Comment.Multiline` and continues parsing with the rules
+defined in the ``'comment'`` state.
+
+If there wasn't a star after the slash, the `RegexLexer` checks if it's a
+singleline comment (eg: followed by a second slash). If this also wasn't the
+case it must be a single slash (the separate regex for a single slash must also
+be given, else the slash would be marked as an error token).
+
+Inside the ``'comment'`` state, we do the same thing again. Scan until the lexer
+finds a star or slash. If it's the opening of a multiline comment, push the
+``'comment'`` state on the stack and continue scanning, again in the
+``'comment'`` state. Else, check if it's the end of the multiline comment. If
+yes, pop one state from the stack.
+
+Note: If you pop from an empty stack you'll get an `IndexError`. (There is an
+easy way to prevent this from happening: don't ``'#pop'`` in the root state).
+
+If the `RegexLexer` encounters a newline that is flagged as an error token, the
+stack is emptied and the lexer continues scanning in the ``'root'`` state. This
+helps producing error-tolerant highlighting for erroneous input, e.g. when a
+single-line string is not closed.
+
+
+Advanced state tricks
+=====================
+
+There are a few more things you can do with states:
+
+- You can push multiple states onto the stack if you give a tuple instead of a
+ simple string as the third item in a rule tuple. For example, if you want to
+ match a comment containing a directive, something like::
+
+ /* <processing directive> rest of comment */
+
+ you can use this rule:
+
+ .. sourcecode:: python
+
+ tokens = {
+ 'root': [
+ (r'/\* <', Comment, ('comment', 'directive')),
+ ...
+ ],
+ 'directive': [
+ (r'[^>]*', Comment.Directive),
+ (r'>', Comment, '#pop'),
+ ],
+ 'comment': [
+ (r'[^*]+', Comment),
+ (r'\*/', Comment, '#pop'),
+ (r'\*', Comment),
+ ]
+ }
+
+ When this encounters the above sample, first ``'comment'`` and ``'directive'``
+ are pushed onto the stack, then the lexer continues in the directive state
+ until it finds the closing ``>``, then it continues in the comment state until
+ the closing ``*/``. Then, both states are popped from the stack again and
+ lexing continues in the root state.
+
+
+- You can include the rules of a state in the definition of another. This is
+ done by using `include` from `pygments.lexer`:
+
+ .. sourcecode:: python
+
+ from pygments.lexer import RegexLexer, include
+
+ class ExampleLexer(RegexLexer):
+ tokens = {
+ 'comments': [
+ (r'/\*.*?\*/', Comment),
+ (r'//.*?\n', Comment),
+ ],
+ 'root': [
+ include('comments'),
+ (r'(function )(\w+)( {)',
+ (Keyword, Name, Keyword), 'function'),
+ (r'.', Text),
+ ],
+ 'function': [
+ (r'[^}/]+', Text),
+ include('comments'),
+ (r'/', Text),
+ (r'}', Keyword, '#pop'),
+ ]
+ }
+
+ This is a hypothetical lexer for a language that consist of functions and
+ comments. Because comments can occur at toplevel and in functions, we need
+ rules for comments in both states. As you can see, the `include` helper saves
+ repeating rules that occur more than once (in this example, the state
+ ``'comment'`` will never be entered by the lexer, as it's only there to be
+ included in ``'root'`` and ``'function'``).
+
+
+- Sometimes, you may want to "combine" a state from existing ones. This is
+ possible with the `combine` helper from `pygments.lexer`.
+
+ If you, instead of a new state, write ``combined('state1', 'state2')`` as the
+ third item of a rule tuple, a new anonymous state will be formed from state1
+ and state2 and if the rule matches, the lexer will enter this state.
+
+ This is not used very often, but can be helpful in some cases, such as the
+ `PythonLexer`'s string literal processing.
+
+- If you want your lexer to start lexing in a different state you can modify
+ the stack by overloading the `get_tokens_unprocessed` method:
+
+ .. sourcecode:: python
+
+ class MyLexer(RegexLexer):
+ tokens = {...}
+
+ def get_tokens_unprocessed(self, text):
+ stack = ['root', 'otherstate']
+ for item in RegexLexer.get_tokens_unprocessed(text, stack):
+ yield item
+
+ Some lexers like the `PhpLexer` use this to make the leading ``<?php``
+ preprocessor comments optional. Note that you can crash the lexer easily
+ by putting values into the stack that don't exist in the token map. Also
+ removing ``'root'`` from the stack can result in strange errors!
+
+
+Using multiple lexers
+=====================
+
+Using multiple lexers for the same input can be tricky. One of the easiest
+combination techniques is shown here: You can replace the token type entry in a
+rule tuple (the second item) with a lexer class. The matched text will then be
+lexed with that lexer, and the resulting tokens will be yielded.
+
+For example, look at this stripped-down HTML lexer:
+
+.. sourcecode:: python
+
+ from pygments.lexer import RegexLexer, bygroups, using
+
+ class HtmlLexer(RegexLexer):
+ name = 'HTML'
+ aliases = ['html']
+ filenames = ['*.html', '*.htm']
+
+ flags = re.IGNORECASE | re.DOTALL
+ tokens = {
+ 'root': [
+ ('[^<&]+', Text),
+ ('&.*?;', Name.Entity),
+ (r'<\s*script\s*', Name.Tag, ('script-content', 'tag')),
+ (r'<\s*[a-zA-Z0-9:]+', Name.Tag, 'tag'),
+ (r'<\s*/\s*[a-zA-Z0-9:]+\s*>', Name.Tag),
+ ],
+ 'script-content': [
+ (r'(.+?)(<\s*/\s*script\s*>)',
+ bygroups(using(JavascriptLexer), Name.Tag),
+ '#pop'),
+ ]
+ }
+
+Here the content of a ``<script>`` tag is passed to a newly created instance of
+a `JavascriptLexer` and not processed by the `HtmlLexer`. This is done using the
+`using` helper that takes the other lexer class as its parameter.
+
+Note the combination of `bygroups` and `using`. This makes sure that the content
+up to the ``</script>`` end tag is processed by the `JavascriptLexer`, while the
+end tag is yielded as a normal token with the `Name.Tag` type.
+
+As an additional goodie, if the lexer class is replaced by `this` (imported from
+`pygments.lexer`), the "other" lexer will be the current one (because you cannot
+refer to the current class within the code that runs at class definition time).
+
+Also note the ``(r'<\s*script\s*', Name.Tag, ('script-content', 'tag'))`` rule.
+Here, two states are pushed onto the state stack, ``'script-content'`` and
+``'tag'``. That means that first ``'tag'`` is processed, which will parse
+attributes and the closing ``>``, then the ``'tag'`` state is popped and the
+next state on top of the stack will be ``'script-content'``.
+
+Any keywords arguments passed to ``using()`` are added to the keyword arguments
+used to create the lexer.
+
+
+Delegating Lexer
+================
+
+Another approach for nested lexers is the `DelegatingLexer` which is for
+example used for the template engine lexers. It takes two lexers as
+arguments on initialisation: a `root_lexer` and a `language_lexer`.
+
+The input is processed as follows: First, the whole text is lexed with the
+`language_lexer`. All tokens yielded with a type of ``Other`` are then
+concatenated and given to the `root_lexer`. The language tokens of the
+`language_lexer` are then inserted into the `root_lexer`'s token stream
+at the appropriate positions.
+
+.. sourcecode:: python
+
+ from pygments.lexer import DelegatingLexer
+ from pygments.lexers.web import HtmlLexer, PhpLexer
+
+ class HtmlPhpLexer(DelegatingLexer):
+ def __init__(self, **options):
+ super(HtmlPhpLexer, self).__init__(HtmlLexer, PhpLexer, **options)
+
+This procedure ensures that e.g. HTML with template tags in it is highlighted
+correctly even if the template tags are put into HTML tags or attributes.
+
+If you want to change the needle token ``Other`` to something else, you can
+give the lexer another token type as the third parameter:
+
+.. sourcecode:: python
+
+ DelegatingLexer.__init__(MyLexer, OtherLexer, Text, **options)
+
+
+Callbacks
+=========
+
+Sometimes the grammar of a language is so complex that a lexer would be unable
+to parse it just by using regular expressions and stacks.
+
+For this, the `RegexLexer` allows callbacks to be given in rule tuples, instead
+of token types (`bygroups` and `using` are nothing else but preimplemented
+callbacks). The callback must be a function taking two arguments:
+
+* the lexer itself
+* the match object for the last matched rule
+
+The callback must then return an iterable of (or simply yield) ``(index,
+tokentype, value)`` tuples, which are then just passed through by
+`get_tokens_unprocessed()`. The ``index`` here is the position of the token in
+the input string, ``tokentype`` is the normal token type (like `Name.Builtin`),
+and ``value`` the associated part of the input string.
+
+You can see an example here:
+
+.. sourcecode:: python
+
+ class HypotheticLexer(RegexLexer):
+
+ def headline_callback(lexer, match):
+ yield match.start(), Generic.Headline, equal_signs + text + equal_signs
+
+ tokens = {
+ 'root': [
+ (r'(=+)(.*?)(\1)', headline_callback)
+ ]
+ }
+
+If the regex for the `headline_callback` matches, the function is called with the
+match object. Note that after the callback is done, processing continues
+normally, that is, after the end of the previous match. The callback has no
+possibility to influence the position.
+
+There are not really any simple examples for lexer callbacks, but you can see
+them in action e.g. in the `compiled.py`_ source code in the `CLexer` and
+`JavaLexer` classes.
+
+.. _compiled.py: http://trac.pocoo.org/repos/pygments/lexers/compiled.py
+
+
+The ExtendedRegexLexer class
+============================
+
+The `RegexLexer`, even with callbacks, unfortunately isn't powerful enough for
+the funky syntax rules of some languages that will go unnamed, such as Ruby.
+
+But fear not; even then you don't have to abandon the regular expression
+approach. For Pygments has a subclass of `RegexLexer`, the `ExtendedRegexLexer`.
+All features known from RegexLexers are available here too, and the tokens are
+specified in exactly the same way, *except* for one detail:
+
+The `get_tokens_unprocessed()` method holds its internal state data not as local
+variables, but in an instance of the `pygments.lexer.LexerContext` class, and
+that instance is passed to callbacks as a third argument. This means that you
+can modify the lexer state in callbacks.
+
+The `LexerContext` class has the following members:
+
+* `text` -- the input text
+* `pos` -- the current starting position that is used for matching regexes
+* `stack` -- a list containing the state stack
+* `end` -- the maximum position to which regexes are matched, this defaults to
+ the length of `text`
+
+Additionally, the `get_tokens_unprocessed()` method can be given a
+`LexerContext` instead of a string and will then process this context instead of
+creating a new one for the string argument.
+
+Note that because you can set the current position to anything in the callback,
+it won't be automatically be set by the caller after the callback is finished.
+For example, this is how the hypothetical lexer above would be written with the
+`ExtendedRegexLexer`:
+
+.. sourcecode:: python
+
+ class ExHypotheticLexer(ExtendedRegexLexer):
+
+ def headline_callback(lexer, match, ctx):
+ yield match.start(), Generic.Headline, equal_signs + text + equal_signs
+ ctx.pos = match.end()
+
+ tokens = {
+ 'root': [
+ (r'(=+)(.*?)(\1)', headline_callback)
+ ]
+ }
+
+This might sound confusing (and it can really be). But it is needed, and for an
+example look at the Ruby lexer in `agile.py`_.
+
+.. _agile.py: http://trac.pocoo.org/repos/pygments/trunk/pygments/lexers/agile.py
diff --git a/docs/src/lexers.txt b/docs/src/lexers.txt
new file mode 100644
index 00000000..5fd8b19e
--- /dev/null
+++ b/docs/src/lexers.txt
@@ -0,0 +1,521 @@
+.. -*- mode: rst -*-
+
+================
+Available lexers
+================
+
+This page lists all available builtin lexers and the options they take.
+
+Currently, **all lexers** support these options:
+
+`stripnl`
+ Strip leading and trailing newlines from the input (default: ``True``)
+
+`stripall`
+ Strip all leading and trailing whitespace from the input (default:
+ ``False``).
+
+`tabsize`
+ If given and greater than 0, expand tabs in the input (default: ``0``).
+
+
+These lexers are builtin and can be imported from
+`pygments.lexers`:
+
+
+Special lexers
+==============
+
+`TextLexer`
+
+ "Null" lexer, doesn't highlight anything.
+
+ :Aliases: ``text``
+ :Filename patterns: ``*.txt``
+
+
+`RawTokenLexer`
+
+ Recreates a token stream formatted with the `RawTokenFormatter`.
+
+ Additional option:
+
+ `compress`
+ If set to ``'gz'`` or ``'bz2'``, decompress the token stream with
+ the given compression algorithm before lexing (default: '').
+
+ :Aliases: ``raw``
+ :Filename patterns: ``*.raw``
+
+
+Agile languages
+===============
+
+`PythonLexer`
+
+ For `Python <http://www.python.org>`_ source code.
+
+ :Aliases: ``python``, ``py``
+ :Filename patterns: ``*.py``, ``*.pyw``
+
+
+`PythonConsoleLexer`
+
+ For Python console output or doctests, such as:
+
+ .. sourcecode:: pycon
+
+ >>> a = 'foo'
+ >>> print a
+ 'foo'
+ >>> 1/0
+ Traceback (most recent call last):
+ ...
+
+ :Aliases: ``pycon``
+ :Filename patterns: None
+
+
+`RubyLexer`
+
+ For `Ruby <http://www.ruby-lang.org>`_ source code.
+
+ :Aliases: ``ruby``, ``rb``
+ :Filename patterns: ``*.rb``
+
+
+`RubyConsoleLexer`
+
+ For Ruby interactive console (**irb**) output like:
+
+ .. sourcecode:: rbcon
+
+ irb(main):001:0> a = 1
+ => 1
+ irb(main):002:0> puts a
+ 1
+ => nil
+
+ :Aliases: ``rbcon``, ``irb``
+ :Filename patterns: None
+
+
+`PerlLexer`
+
+ For `Perl <http://www.perl.org>`_ source code.
+
+ :Aliases: ``perl``, ``pl``
+ :Filename patterns: ``*.pl``, ``*.pm``
+
+
+`LuaLexer`
+
+ For `Lua <http://www.lua.org>`_ source code.
+
+ Additional options:
+
+ `func_name_highlighting`
+ If given and ``True``, highlight builtin function names
+ (default: ``True``).
+ `disabled_modules`
+ If given, must be a list of module names whose function names
+ should not be highlighted. By default all modules are highlighted.
+
+ To get a list of allowed modules have a look into the
+ `_luabuiltins` module:
+
+ .. sourcecode:: pycon
+
+ >>> from pygments.lexers._luabuiltins import MODULES
+ >>> MODULES.keys()
+ ['string', 'coroutine', 'modules', 'io', 'basic', ...]
+
+ :Aliases: ``lua``
+ :Filename patterns: ``*.lua``
+
+
+Compiled languages
+==================
+
+`CLexer`
+
+ For C source code with preprocessor directives.
+
+ :Aliases: ``c``
+ :Filename patterns: ``*.c``, ``*.h``
+
+
+`CppLexer`
+
+ For C++ source code with preprocessor directives.
+
+ :Aliases: ``cpp``, ``c++``
+ :Filename patterns: ``*.cpp``, ``*.hpp``, ``*.c++``, ``*.h++``
+
+
+`DelphiLexer`
+
+ For `Delphi <http://www.borland.com/delphi/>`_
+ (Borland Object Pascal) source code.
+
+ :Aliases: ``delphi``, ``pas``, ``pascal``, ``objectpascal``
+ :Filename patterns: ``*.pas``
+
+
+`JavaLexer`
+
+ For `Java <http://www.sun.com/java/>`_ source code.
+
+ :Aliases: ``java``
+ :Filename patterns: ``*.java``
+
+
+.NET languages
+==============
+
+`CSharpLexer`
+
+ For `C# <http://msdn2.microsoft.com/en-us/vcsharp/default.aspx>`_
+ source code.
+
+ :Aliases: ``c#``, ``csharp``
+ :Filename patterns: ``*.cs``
+
+`BooLexer`
+
+ For `Boo <http://boo.codehaus.org/>`_ source code.
+
+ :Aliases: ``boo``
+ :Filename patterns: ``*.boo``
+
+`VbNetLexer`
+
+ For
+ `Visual Basic.NET <http://msdn2.microsoft.com/en-us/vbasic/default.aspx>`_
+ source code.
+
+ :Aliases: ``vbnet``, ``vb.net``
+ :Filename patterns: ``*.vb``, ``*.bas``
+
+
+Web-related languages
+=====================
+
+`JavascriptLexer`
+
+ For JavaScript source code.
+
+ :Aliases: ``js``, ``javascript``
+ :Filename patterns: ``*.js``
+
+
+`CssLexer`
+
+ For CSS (Cascading Style Sheets).
+
+ :Aliases: ``css``
+ :Filename patterns: ``*.css``
+
+
+`HtmlLexer`
+
+ For HTML 4 and XHTML 1 markup. Nested JavaScript and CSS is highlighted
+ by the appropriate lexer.
+
+ :Aliases: ``html``
+ :Filename patterns: ``*.html``, ``*.htm``, ``*.xhtml``
+
+
+`PhpLexer`
+
+ For `PHP <http://www.php.net/>`_ source code.
+ For PHP embedded in HTML, use the `HtmlPhpLexer`.
+
+ Additional options:
+
+ `startinline`
+ If given and ``True`` the lexer starts highlighting with
+ php code. (i.e.: no starting ``<?php`` required)
+ `funcnamehighlighting`
+ If given and ``True``, highlight builtin function names
+ (default: ``True``).
+ `disabledmodules`
+ If given, must be a list of module names whose function names
+ should not be highlighted. By default all modules are highlighted
+ except the special ``'unknown'`` module that includes functions
+ that are known to php but are undocumented.
+
+ To get a list of allowed modules have a look into the
+ `_phpbuiltins` module:
+
+ .. sourcecode:: pycon
+
+ >>> from pygments.lexers._phpbuiltins import MODULES
+ >>> MODULES.keys()
+ ['PHP Options/Info', 'Zip', 'dba', ...]
+
+ In fact the names of those modules match the module names from
+ the php documentation.
+
+ :Aliases: ``php``, ``php3``, ``php4``, ``php5``
+ :Filename patterns: ``*.php``, ``*.php[345]``
+
+
+`XmlLexer`
+
+ Generic lexer for XML (extensible markup language).
+
+ :Aliases: ``xml``
+ :Filename patterns: ``*.xml``
+
+
+Template languages
+==================
+
+`ErbLexer`
+
+ Generic `ERB <http://ruby-doc.org/core/classes/ERB.html>`_ (Ruby Templating)
+ lexer.
+
+ Just highlights ruby code between the preprocessor directives, other data
+ is left untouched by the lexer.
+
+ All options are also forwarded to the `RubyLexer`.
+
+ :Aliases: ``erb``
+ :Filename patterns: None
+
+
+`RhtmlLexer`
+
+ Subclass of the ERB lexer that highlights the unlexed data with the
+ html lexer.
+
+ Nested Javascript and CSS is highlighted too.
+
+ :Aliases: ``rhtml``, ``html+erb``, ``html+ruby``
+ :Filename patterns: ``*.rhtml``
+
+
+`XmlErbLexer`
+
+ Subclass of `ErbLexer` which highlights data outside preprocessor
+ directives with the `XmlLexer`.
+
+ :Aliases: ``xml+erb``, ``xml+ruby``
+ :Filename patterns: None
+
+
+`CssErbLexer`
+
+ Subclass of `ErbLexer` which highlights unlexed data with the `CssLexer`.
+
+ :Aliases: ``css+erb``, ``css+ruby``
+ :Filename patterns: None
+
+
+`JavascriptErbLexer`
+
+ Subclass of `ErbLexer` which highlights unlexed data with the
+ `JavascriptLexer`.
+
+ :Aliases: ``js+erb``, ``javascript+erb``, ``js+ruby``, ``javascript+ruby``
+ :Filename patterns: None
+
+
+`HtmlPhpLexer`
+
+ Subclass of `PhpLexer` that highlights unhandled data with the `HtmlLexer`.
+
+ Nested Javascript and CSS is highlighted too.
+
+ :Aliases: ``html+php``
+ :Filename patterns: ``*.phtml``
+
+
+`XmlPhpLexer`
+
+ Subclass of `PhpLexer` that higlights unhandled data with the `XmlLexer`.
+
+ :Aliases: ``xml+php``
+ :Filename patterns: None
+
+
+`CssPhpLexer`
+
+ Subclass of `PhpLexer` which highlights unmatched data with the `CssLexer`.
+
+ :Aliases: ``css+php``
+ :Filename patterns: None
+
+
+`JavascriptPhpLexer`
+
+ Subclass of `PhpLexer` which highlights unmatched data with the
+ `JavascriptLexer`.
+
+ :Aliases: ``js+php``, ``javascript+php``
+ :Filename patterns: None
+
+
+`DjangoLexer`
+
+ Generic `django <http://www.djangoproject.com/documentation/templates/>`_
+ template lexer.
+
+ It just highlights django code between the preprocessor directives, other
+ data is left untouched by the lexer.
+
+ :Aliases: ``django``
+ :Filename patterns: None
+
+
+`HtmlDjangoLexer`
+
+ Subclass of the `DjangoLexer` that highighlights unlexed data with the
+ `HtmlLexer`.
+
+ Nested Javascript and CSS is highlighted too.
+
+ :Aliases: ``html+django``
+ :Filename patterns: None
+
+
+`XmlDjangoLexer`
+
+ Subclass of the `DjangoLexer` that highlights unlexed data with the
+ `XmlLexer`.
+
+ :Aliases: ``xml+django``
+ :Filename patterns: None
+
+
+`CssDjangoLexer`
+
+ Subclass of the `DjangoLexer` that highlights unlexed data with the
+ `CssLexer`.
+
+ :Aliases: ``css+django``
+ :Filename patterns: None
+
+
+`JavascriptDjangoLexer`
+
+ Subclass of the `DjangoLexer` that highlights unlexed data with the
+ `JavascriptLexer`.
+
+ :Aliases: ``javascript+django``
+ :Filename patterns: None
+
+
+`SmartyLexer`
+
+ Generic `Smarty <http://smarty.php.net/>`_ template lexer.
+
+ Just highlights smarty code between the preprocessor directives, other
+ data is left untouched by the lexer.
+
+ :Aliases: ``smarty``
+ :Filename patterns: None
+
+
+`HtmlSmartyLexer`
+
+ Subclass of the `SmartyLexer` that highighlights unlexed data with the
+ `HtmlLexer`.
+
+ Nested Javascript and CSS is highlighted too.
+
+ :Aliases: ``html+smarty``
+ :Filename patterns: None
+
+
+`XmlSmartyLexer`
+
+ Subclass of the `SmartyLexer` that highlights unlexed data with the
+ `XmlLexer`.
+
+ :Aliases: ``xml+smarty``
+ :Filename patterns: None
+
+
+`CssSmartyLexer`
+
+ Subclass of the `SmartyLexer` that highlights unlexed data with the
+ `CssLexer`.
+
+ :Aliases: ``css+smarty``
+ :Filename patterns: None
+
+
+`JavascriptSmartyLexer`
+
+ Subclass of the `SmartyLexer` that highlights unlexed data with the
+ `JavascriptLexer`.
+
+ :Aliases: ``javascript+smarty``
+ :Filename patterns: None
+
+
+Other languages
+===============
+
+`SqlLexer`
+
+ Lexer for Structured Query Language. Currently, this lexer does
+ not recognize any special syntax except ANSI SQL.
+
+ :Aliases: ``sql``
+ :Filename patterns: ``*.sql``
+
+
+`BrainfuckLexer`
+
+ Lexer for the esoteric `BrainFuck <http://www.muppetlabs.com/~breadbox/bf/>`_
+ language.
+
+ :Aliases: ``brainfuck``
+ :Filename patterns: ``*.bf``, ``*.b``
+
+
+Text lexers
+===========
+
+`IniLexer`
+
+ Lexer for configuration files in INI style.
+
+ :Aliases: ``ini``, ``cfg``
+ :Filename patterns: ``*.ini``, ``*.cfg``
+
+
+`MakefileLexer`
+
+ Lexer for Makefiles.
+
+ :Aliases: ``make``, ``makefile``, ``mf``
+ :Filename patterns: ``*.mak``, ``Makefile``, ``makefile``
+
+
+`DiffLexer`
+
+ Lexer for unified or context-style diffs.
+
+ :Aliases: ``diff``
+ :Filename patterns: ``*.diff``, ``*.patch``
+
+
+`IrcLogsLexer`
+
+ Lexer for IRC logs in **irssi** or **xchat** style.
+
+ :Aliases: ``irc``
+ :Filename patterns: None
+
+
+`TexLexer`
+
+ Lexer for the TeX and LaTeX typesetting languages.
+
+ :Aliases: ``tex``, ``latex``
+ :Filename patterns: ``*.tex``, ``*.aux``, ``*.toc``
diff --git a/docs/src/quickstart.txt b/docs/src/quickstart.txt
new file mode 100644
index 00000000..749889df
--- /dev/null
+++ b/docs/src/quickstart.txt
@@ -0,0 +1,121 @@
+.. -*- mode: rst -*-
+
+==========
+Quickstart
+==========
+
+
+Pygments comes with a wide range of lexers for modern languages which are all
+accessible through the pygments.lexers package. A lexer enables Pygments to
+parse the source code into tokens which are passed to a formatter. Currently
+formatters exist for HTML, LaTeX and ANSI sequences.
+
+
+Example
+=======
+
+Here is a small example for highlighting Python code:
+
+.. sourcecode:: python
+
+ from pygments import highlight
+ from pygments.lexers import PythonLexer
+ from pygments.formatters import HtmlFormatter
+
+ code = 'print "Hello World"'
+ print highlight(code, PythonLexer(), HtmlFormatter())
+
+which prints something like this:
+
+.. sourcecode:: html
+
+ <div class="highlight">
+ <pre><span class="k">print</span> <span class="l s">&quot;Hello World&quot;</span></pre>
+ </div>
+
+
+A CSS stylesheet which contains all CSS classes possibly used in the output can be
+produced by:
+
+.. sourcecode:: python
+
+ print HtmlFormatter().get_style_defs('.highlight')
+
+The argument is used as an additional CSS selector: the output may look like
+
+.. sourcecode:: css
+
+ .highlight .k { color: #AA22FF; font-weight: bold }
+ .highlight .s { color: #BB4444 }
+ ...
+
+
+Options
+=======
+
+The `highlight()` function supports a fourth argument called `outfile`, it must be
+a file object if given. The formatted output will then be written to this file
+instead of being returned as a string.
+
+Lexers and formatters both support options. They are given to them as keyword
+arguments either to the class or to the lookup method:
+
+.. sourcecode:: python
+
+ from pygments import highlight
+ from pygments.lexers import get_lexer_by_name
+ from pygments.formatters import HtmlFormatter
+
+ lexer = get_lexer_by_name("python", stripall=True)
+ formatter = HtmlFormatter(linenos=True, cssclass="source")
+ result = highlight(code, lexer, formatter)
+
+This makes the lexer strip all leading and trailing whitespace from the input
+(`stripall` option), lets the formatter output line numbers (`linenos` option),
+and sets the wrapping ``<div>``'s class to ``source`` (instead of
+``highlight``).
+
+For an overview of builtin lexers and formatters and their options, visit the
+`lexer <lexers.txt>`_ and `formatters <formatters.txt>`_ lists.
+
+
+Lexer and formatter lookup
+==========================
+
+If you want to lookup a built-in lexer by its alias or a filename, you can use
+one of the following methods:
+
+.. sourcecode:: pycon
+
+ >>> from pygments.lexers import get_lexer_by_name, get_lexer_for_filename
+ >>> get_lexer_by_name('python')
+ <pygments.lexers.agile.PythonLexer object at 0xb7bd6d0c>
+ >>> get_lexer_for_filename('spam.py')
+ <pygments.lexers.agile.PythonLexer object at 0xb7bd6b2c>
+
+The same API is available for formatters: use `get_formatter_by_name` and
+`get_formatter_for_filename` from the `pygments.formatters` module
+for this purpose.
+
+
+Command line usage
+==================
+
+You can use Pygments from the command line, using the `pygmentize` script::
+
+ $ pygmentize test.py
+
+will highlight the Python file test.py using ANSI escape sequences
+(a.k.a. terminal colors) and print the result to standard output.
+
+To output HTML, use the ``-f`` option::
+
+ $ pygmentize -f html -o test.html test.py
+
+to write an HTML-highlighted version of test.py to the file test.html.
+
+The stylesheet can be created with::
+
+ $ pygmentize -S default -f html > style.css
+
+More options and tricks and be found in the `command line referene <cmdline.txt>`_.
diff --git a/docs/src/rstdirective.txt b/docs/src/rstdirective.txt
new file mode 100644
index 00000000..60651319
--- /dev/null
+++ b/docs/src/rstdirective.txt
@@ -0,0 +1,42 @@
+===============================
+Using Pygments in ReST documents
+===============================
+
+Many Python people use `ReST`_ for documentation their sourcecode, programs etc.
+This also means that documentation often includes sourcecode samples etc.
+
+You can easily enable Pygments support for your rst texts as long as you
+use your own build script.
+
+Just add this code to it:
+
+.. sourcecode:: python
+
+ from docutils import nodes
+ from docutils.parsers.rst import directives
+ from pygments import highlight
+ from pygments.lexers import get_lexer_by_name
+ from pygments.formatters import HtmlFormatter
+
+ PYGMENTS_FORMATTER = HtmlFormatter()
+
+ def pygments_directive(name, arguments, options, content, lineno,
+ content_offset, block_text, state, state_machine):
+ try:
+ lexer = get_lexer_by_name(arguments[0])
+ except ValueError:
+ # no lexer found
+ lexer = get_lexer_by_name('text')
+ parsed = highlight(u'\n'.join(content), lexer, PYGMENTS_FORMATTER)
+ return [nodes.raw('', parsed, format='html')]
+ pygments_directive.arguments = (1, 0, 1)
+ pygments_directive.content = 1
+ directives.register_directive('sourcecode', pygments_directive)
+
+Now you should be able to use Pygments in your rst files using this syntax::
+
+ .. sourcecode:: language
+
+ your code here
+
+.. _ReST: http://docutils.sf.net/rst.html
diff --git a/docs/src/styles.txt b/docs/src/styles.txt
new file mode 100644
index 00000000..4fd6c297
--- /dev/null
+++ b/docs/src/styles.txt
@@ -0,0 +1,119 @@
+.. -*- mode: rst -*-
+
+======
+Styles
+======
+
+Pygments comes with some builtin styles that work for both the HTML and
+LaTeX formatter.
+
+The builtin styles can be looked up with the `get_style_by_name` function:
+
+.. sourcecode:: pycon
+
+ >>> from pygments.styles import get_style_by_name
+ >>> get_style_by_name('colorful')
+ <class 'pygments.styles.colorful.ColorfulStyle'>
+
+You can pass a instance of a `Style` class to a formatter as the `style`
+option in form of a string:
+
+.. sourcecode:: pycon
+
+ >>> from pygments.styles import get_style_by_name
+ >>> HtmlFormatter(style='colorful').style
+ <class 'pygments.styles.colorful.ColorfulStyle'>
+
+Or you can also import your own style (which must be a subclass of
+`pygments.style.Style`) and pass it to the formatter:
+
+.. sourcecode:: pycon
+
+ >>> from yourapp.yourmodule import YourStyle
+ >>> HtmlFormatter(style=YourStyle).style
+ <class 'yourapp.yourmodule.YourStyle'>
+
+
+Creating Own Styles
+===================
+
+So, how to create a style? All you have to do is to subclass `Style` and
+define some styles:
+
+.. sourcecode:: python
+
+ from pygments.style import Style
+ from pygments.token import Keyword, Name, Comment, String, Error, \
+ Number, Operator, Generic
+
+ class YourStyle(Style):
+ default_style = ""
+ styles = {
+ Comment: 'italic #888',
+ Keyword: 'bold #005',
+ Name: '#f00',
+ Name.Function: '#0f0',
+ Name.Class: 'bold #0f0',
+ String: 'bg:#eee #111'
+ }
+
+That's it. There are just a few rules. When you define a style for `Name`
+the style automatically also affects `Name.Function` and so on. If you
+defined ``'bold'`` and you don't want boldface for a subtoken use ``'nobold'``.
+
+(Philosophy: the styles aren't written in CSS syntax since this way
+they can be used for a variety of formatters.)
+
+`default_style` is the style inherited by all token types.
+
+
+Style Rules
+===========
+
+Here a small overview over all allowed styles:
+
+``bold``
+ render text as bold
+``nobold``
+ don't render text as bold (to prevent subtokens behing highlighted bold)
+``italic``
+ render text italic
+``noitalic``
+ don't render text as italic
+``underline``
+ render text underlined
+``nounderline``
+ don't render text underlined
+``bg:``
+ transparent background
+``bg:#000000``
+ background color (black)
+``border:``
+ no border
+``border:#ffffff``
+ border color (white)
+``#ff0000``
+ text color (red)
+``noinherit``
+ don't inherit styles from supertoken
+
+Note that there may not be a space between ``bg:`` and the color value
+since the style definition string is split at whitespace.
+Also, using named colors is not allowed since the supported color names
+vary for different formatters.
+
+Furthermore, not all lexers might support every style.
+
+
+Builtin Styles
+==============
+
+Pygments ships some builtin styles which are maintained by the Pygments team.
+
+To get a list of known styles you can use this snippet:
+
+.. sourcecode:: pycon
+
+ >>> from pygments.styles import STYLE_MAP
+ >>> STYLE_MAP.keys()
+ ['default', 'emacs', 'friendly', 'colorful']
diff --git a/docs/src/tokens.txt b/docs/src/tokens.txt
new file mode 100644
index 00000000..47d8feea
--- /dev/null
+++ b/docs/src/tokens.txt
@@ -0,0 +1,284 @@
+.. -*- mode: rst -*-
+
+==============
+Builtin Tokens
+==============
+
+Inside the `pygments.token` module, there is a special object called `Token` that
+is used to create token types.
+
+You can create a new token type by accessing an attribute of `Token`:
+
+.. sourcecode:: pycon
+
+ >>> from pygments.token import Token
+ >>> Token.String
+ Token.String
+ >>> Token.String is Token.String
+ True
+
+Note that tokens are singletons so you can use the ``is`` operator for comparing
+token types.
+
+In principle, you can create an unlimited number of token types but nobody can
+guarantee that a style would define style rules for a token type. Because of
+that, Pygments proposes some global token types defined in the
+`pygments.token.STANDARD_TYPES` dict.
+
+For some tokens aliases are already defined:
+
+.. sourcecode:: pycon
+
+ >>> from pygments.token import String
+ >>> String
+ Token.Literal.String
+
+Inside the `pygments.token` module the following aliases are defined:
+
+=========== =============================== ==================================
+`Text` `Token.Text` for any type of text data
+`Error` `Token.Error` represents lexer errors
+`Other` `Token.Other` special token for data not
+ matched by a parser (e.g. HTML
+ markup in PHP code)
+`Keyword` `Token.Keyword` any kind of keywords
+`Name` `Token.Name` variable/function names
+`Literal` `Token.Literal` Any literals
+`String` `Token.Literal.String` string literals
+`Number` `Token.Literal.Number` number literals
+`Operator` `Token.Operator` operators (``+``, ``not`` etc)
+`Comment` `Token.Comment` any kind of comments
+`Generic` `Token.Generic` generic tokens (have a look at
+ the explanation below)
+=========== =============================== ==================================
+
+Normally you just create token types using the already defined aliases. For each
+of those token aliases, a number of subtypes exists (excluding the special tokens
+`Token.Text`, `Token.Error` and `Token.Other`)
+
+
+Keyword Tokens
+==============
+
+`Keyword`
+ For any kind of keyword (especially if it doesn't match any of the
+ subtypes of course).
+
+`Keyword.Constant`
+ For keywords that are constants (e.g. ``None`` in future Python versions).
+
+`Keyword.Declaration`
+ For keywords used for variable declaration (e.g. ``var`` in some programming
+ languages like JavaScript).
+
+`Keyword.Pseudo`
+ For keywords that aren't really keywords (e.g. ``None`` in old Python
+ versions).
+
+`Keyword.Reserved`
+ For reserved keywords.
+
+`Keyword.Type`
+ For builtin types that can't be used as identifiers (e.g. ``int``,
+ ``char`` etc. in C).
+
+
+Name Tokens
+===========
+
+`Name`
+ For any name (variable names, function names, classes).
+
+`Name.Attribute`
+ For all attributes (e.g. in HTML tags).
+
+`Name.Builtin`
+ Builtin names; names that are available in the global namespace.
+
+`Name.Builtin.Pseudo`
+ Builtin names that are implicit (e.g. ``self`` in Ruby, ``this`` in Java).
+
+`Name.Class`
+ Class names. Because no lexer can know if a name is a class or a function
+ or something else this token is meant for class declarations.
+
+`Name.Constant`
+ Token type for constants. In some languages you can recognise a token by the
+ way it's defined (the value after a ``const`` keyword for example). In
+ other languages constants are uppercase by definition (Ruby).
+
+`Name.Decorator`
+ Token type for decorators. Decorators are synatic elements in the Python
+ language. Similar syntax elements exist in C# and Java.
+
+`Name.Entity`
+ Token type for special entities. (e.g. ``&nbsp;`` in HTML).
+
+`Name.Exception`
+ Token type for exception names (e.g. ``RuntimeError`` in Python). Some languages
+ define exceptions in the function signature (Java). You can highlight
+ the name of that exception using this token then.
+
+`Name.Function`
+ Token type for function names.
+
+`Name.Label`
+ Token type for label names (e.g. in languages that support ``goto``).
+
+`Name.Namespace`
+ Token type for namespaces. (e.g. import paths in Java/Python), names following
+ the ``module``/``namespace`` keyword in other languages.
+
+`Name.Other`
+ Other names. Normally unused.
+
+`Name.Tag`
+ Tag names (in HTML/XML markup or configuration files).
+
+`Name.Variable`
+ Token type for variables. Some languages have prefixes for variable names
+ (PHP, Ruby, Perl). You can highlight them using this token.
+
+`Name.Variable.Class`
+ same as `Name.Variable` but for class variables (also static variables).
+
+`Name.Variable.Global`
+ same as `Name.Variable` but for global variables (used in Ruby, for
+ example).
+
+`Name.Variable.Instance`
+ same as `Name.Variable` but for instance variables.
+
+
+Literals
+========
+
+`Literal`
+ For any literal (if not further defined).
+
+`Literal.Date`
+ for date literals (e.g. ``42d`` in Boo).
+
+
+`String`
+ For any string literal.
+
+`String.Backtick`
+ Token type for strings enclosed in backticks.
+
+`String.Char`
+ Token type for single characters (e.g. Java, C).
+
+`String.Doc`
+ Token type for documentation strings (for example Python).
+
+`String.Double`
+ Double quoted strings.
+
+`String.Escape`
+ Token type for escape sequences in strings.
+
+`String.Heredoc`
+ Token type for "heredoc" strings (e.g. in Ruby or Perl).
+
+`String.Interpol`
+ Token type for interpolated parts in strings (e.g. ``#{foo}`` in Ruby).
+
+`String.Other`
+ Token type for any other strings (for example ``%q{foo}`` string constructs
+ in Ruby).
+
+`String.Regex`
+ Token type for regular expression literals (e.g. ``/foo/`` in JavaScript).
+
+`String.Single`
+ Token type for single quoted strings.
+
+`String.Symbol`
+ Token type for symbols (e.g. ``:foo`` in LISP or Ruby).
+
+
+`Number`
+ Token type for any number literal.
+
+`Number.Float`
+ Token type for float literals (e.g. ``42.0``).
+
+`Number.Hex`
+ Token type for hexadecimal number literals (e.g. ``0xdeadbeef``).
+
+`Number.Integer`
+ Token type for integer literals (e.g. ``42``).
+
+`Number.Integer.Long`
+ Token type for long integer literals (e.g. ``42L`` in Python).
+
+`Number.Oct`
+ Token type for octal literals.
+
+
+Operators
+=========
+
+`Operator`
+ For any punctuation operator (e.g. ``+``, ``-``).
+
+`Operator.Word`
+ For any operator that is a word (e.g. ``not``).
+
+
+Comments
+========
+
+`Comment`
+ Token type for any comment.
+
+`Comment.Multiline`
+ Token type for multiline comments.
+
+`Comment.Preproc`
+ Token type for preprocessor comments (also ``<?php``/``<%`` constructs).
+
+`Comment.Single`
+ Token type for comments that end at the end of a line (e.g. ``# foo``).
+
+
+Generic Tokens
+==============
+
+Generic tokens are for special lexers like the `DiffLexer` that doesn't really
+highlight a programming language but a patch file.
+
+
+`Generic`
+ A generic, unstyled token. Normally you don't use this token type.
+
+`Generic.Deleted`
+ Marks the token value as deleted.
+
+`Generic.Emph`
+ Marks the token value as emphasized.
+
+`Generic.Error`
+ Marks the token value as an error message.
+
+`Generic.Heading`
+ Marks the token value as headline.
+
+`Generic.Inserted`
+ Marks the token value as inserted.
+
+`Generic.Output`
+ Marks the token value as program output (e.g. for python cli lexer).
+
+`Generic.Prompt`
+ Marks the token value as command prompt (e.g. bash lexer).
+
+`Generic.Strong`
+ Marks the token value as bold (e.g. for rst lexer).
+
+`Generic.Subheading`
+ Marks the token value as subheadline.
+
+`Generic.Traceback`
+ Marks the token value as a part of an error traceback.
diff --git a/ez_setup.py b/ez_setup.py
new file mode 100644
index 00000000..548191ff
--- /dev/null
+++ b/ez_setup.py
@@ -0,0 +1,221 @@
+#!python
+"""Bootstrap setuptools installation
+
+If you want to use setuptools in your package's setup.py, just include this
+file in the same directory with it, and add this to the top of your setup.py::
+
+ from ez_setup import use_setuptools
+ use_setuptools()
+
+If you want to require a specific version of setuptools, set a download
+mirror, or use an alternate download directory, you can do so by supplying
+the appropriate options to ``use_setuptools()``.
+
+This file can also be run as a script to install or upgrade setuptools.
+"""
+import sys
+DEFAULT_VERSION = "0.6c3"
+DEFAULT_URL = "http://cheeseshop.python.org/packages/%s/s/setuptools/" % sys.version[:3]
+
+md5_data = {
+ 'setuptools-0.6b1-py2.3.egg': '8822caf901250d848b996b7f25c6e6ca',
+ 'setuptools-0.6b1-py2.4.egg': 'b79a8a403e4502fbb85ee3f1941735cb',
+ 'setuptools-0.6b2-py2.3.egg': '5657759d8a6d8fc44070a9d07272d99b',
+ 'setuptools-0.6b2-py2.4.egg': '4996a8d169d2be661fa32a6e52e4f82a',
+ 'setuptools-0.6b3-py2.3.egg': 'bb31c0fc7399a63579975cad9f5a0618',
+ 'setuptools-0.6b3-py2.4.egg': '38a8c6b3d6ecd22247f179f7da669fac',
+ 'setuptools-0.6b4-py2.3.egg': '62045a24ed4e1ebc77fe039aa4e6f7e5',
+ 'setuptools-0.6b4-py2.4.egg': '4cb2a185d228dacffb2d17f103b3b1c4',
+ 'setuptools-0.6c1-py2.3.egg': 'b3f2b5539d65cb7f74ad79127f1a908c',
+ 'setuptools-0.6c1-py2.4.egg': 'b45adeda0667d2d2ffe14009364f2a4b',
+ 'setuptools-0.6c2-py2.3.egg': 'f0064bf6aa2b7d0f3ba0b43f20817c27',
+ 'setuptools-0.6c2-py2.4.egg': '616192eec35f47e8ea16cd6a122b7277',
+ 'setuptools-0.6c3-py2.3.egg': 'f181fa125dfe85a259c9cd6f1d7b78fa',
+ 'setuptools-0.6c3-py2.4.egg': 'e0ed74682c998bfb73bf803a50e7b71e',
+ 'setuptools-0.6c3-py2.5.egg': 'abef16fdd61955514841c7c6bd98965e',
+}
+
+import sys, os
+
+def _validate_md5(egg_name, data):
+ if egg_name in md5_data:
+ from md5 import md5
+ digest = md5(data).hexdigest()
+ if digest != md5_data[egg_name]:
+ print >>sys.stderr, (
+ "md5 validation of %s failed! (Possible download problem?)"
+ % egg_name
+ )
+ sys.exit(2)
+ return data
+
+
+def use_setuptools(
+ version=DEFAULT_VERSION, download_base=DEFAULT_URL, to_dir=os.curdir,
+ download_delay=15
+):
+ """Automatically find/download setuptools and make it available on sys.path
+
+ `version` should be a valid setuptools version number that is available
+ as an egg for download under the `download_base` URL (which should end with
+ a '/'). `to_dir` is the directory where setuptools will be downloaded, if
+ it is not already available. If `download_delay` is specified, it should
+ be the number of seconds that will be paused before initiating a download,
+ should one be required. If an older version of setuptools is installed,
+ this routine will print a message to ``sys.stderr`` and raise SystemExit in
+ an attempt to abort the calling script.
+ """
+ try:
+ import setuptools
+ if setuptools.__version__ == '0.0.1':
+ print >>sys.stderr, (
+ "You have an obsolete version of setuptools installed. Please\n"
+ "remove it from your system entirely before rerunning this script."
+ )
+ sys.exit(2)
+ except ImportError:
+ egg = download_setuptools(version, download_base, to_dir, download_delay)
+ sys.path.insert(0, egg)
+ import setuptools; setuptools.bootstrap_install_from = egg
+
+ import pkg_resources
+ try:
+ pkg_resources.require("setuptools>="+version)
+
+ except pkg_resources.VersionConflict, e:
+ print >>sys.stderr, (
+ "The required version of setuptools (>=%s) is not available, and\n"
+ "can't be installed while this script is running. Please install\n"
+ " a more recent version first.\n\n(Currently using %r)"
+ ) % (version, e.args[0])
+ sys.exit(2)
+
+def download_setuptools(
+ version=DEFAULT_VERSION, download_base=DEFAULT_URL, to_dir=os.curdir,
+ delay = 15
+):
+ """Download setuptools from a specified location and return its filename
+
+ `version` should be a valid setuptools version number that is available
+ as an egg for download under the `download_base` URL (which should end
+ with a '/'). `to_dir` is the directory where the egg will be downloaded.
+ `delay` is the number of seconds to pause before an actual download attempt.
+ """
+ import urllib2, shutil
+ egg_name = "setuptools-%s-py%s.egg" % (version,sys.version[:3])
+ url = download_base + egg_name
+ saveto = os.path.join(to_dir, egg_name)
+ src = dst = None
+ if not os.path.exists(saveto): # Avoid repeated downloads
+ try:
+ from distutils import log
+ if delay:
+ log.warn("""
+---------------------------------------------------------------------------
+This script requires setuptools version %s to run (even to display
+help). I will attempt to download it for you (from
+%s), but
+you may need to enable firewall access for this script first.
+I will start the download in %d seconds.
+
+(Note: if this machine does not have network access, please obtain the file
+
+ %s
+
+and place it in this directory before rerunning this script.)
+---------------------------------------------------------------------------""",
+ version, download_base, delay, url
+ ); from time import sleep; sleep(delay)
+ log.warn("Downloading %s", url)
+ src = urllib2.urlopen(url)
+ # Read/write all in one block, so we don't create a corrupt file
+ # if the download is interrupted.
+ data = _validate_md5(egg_name, src.read())
+ dst = open(saveto,"wb"); dst.write(data)
+ finally:
+ if src: src.close()
+ if dst: dst.close()
+ return os.path.realpath(saveto)
+
+def main(argv, version=DEFAULT_VERSION):
+ """Install or upgrade setuptools and EasyInstall"""
+
+ try:
+ import setuptools
+ except ImportError:
+ egg = None
+ try:
+ egg = download_setuptools(version, delay=0)
+ sys.path.insert(0,egg)
+ from setuptools.command.easy_install import main
+ return main(list(argv)+[egg]) # we're done here
+ finally:
+ if egg and os.path.exists(egg):
+ os.unlink(egg)
+ else:
+ if setuptools.__version__ == '0.0.1':
+ # tell the user to uninstall obsolete version
+ use_setuptools(version)
+
+ req = "setuptools>="+version
+ import pkg_resources
+ try:
+ pkg_resources.require(req)
+ except pkg_resources.VersionConflict:
+ try:
+ from setuptools.command.easy_install import main
+ except ImportError:
+ from easy_install import main
+ main(list(argv)+[download_setuptools(delay=0)])
+ sys.exit(0) # try to force an exit
+ else:
+ if argv:
+ from setuptools.command.easy_install import main
+ main(argv)
+ else:
+ print "Setuptools version",version,"or greater has been installed."
+ print '(Run "ez_setup.py -U setuptools" to reinstall or upgrade.)'
+
+
+
+def update_md5(filenames):
+ """Update our built-in md5 registry"""
+
+ import re
+ from md5 import md5
+
+ for name in filenames:
+ base = os.path.basename(name)
+ f = open(name,'rb')
+ md5_data[base] = md5(f.read()).hexdigest()
+ f.close()
+
+ data = [" %r: %r,\n" % it for it in md5_data.items()]
+ data.sort()
+ repl = "".join(data)
+
+ import inspect
+ srcfile = inspect.getsourcefile(sys.modules[__name__])
+ f = open(srcfile, 'rb'); src = f.read(); f.close()
+
+ match = re.search("\nmd5_data = {\n([^}]+)}", src)
+ if not match:
+ print >>sys.stderr, "Internal error!"
+ sys.exit(2)
+
+ src = src[:match.start(1)] + repl + src[match.end(1):]
+ f = open(srcfile,'w')
+ f.write(src)
+ f.close()
+
+
+if __name__=='__main__':
+ if len(sys.argv)>2 and sys.argv[1]=='--md5update':
+ update_md5(sys.argv[2:])
+ else:
+ main(sys.argv[1:])
+
+
+
+
+
diff --git a/lgpl.txt b/lgpl.txt
new file mode 100644
index 00000000..5ab7695a
--- /dev/null
+++ b/lgpl.txt
@@ -0,0 +1,504 @@
+ GNU LESSER GENERAL PUBLIC LICENSE
+ Version 2.1, February 1999
+
+ Copyright (C) 1991, 1999 Free Software Foundation, Inc.
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+[This is the first released version of the Lesser GPL. It also counts
+ as the successor of the GNU Library Public License, version 2, hence
+ the version number 2.1.]
+
+ Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+Licenses are intended to guarantee your freedom to share and change
+free software--to make sure the software is free for all its users.
+
+ This license, the Lesser General Public License, applies to some
+specially designated software packages--typically libraries--of the
+Free Software Foundation and other authors who decide to use it. You
+can use it too, but we suggest you first think carefully about whether
+this license or the ordinary General Public License is the better
+strategy to use in any particular case, based on the explanations below.
+
+ When we speak of free software, we are referring to freedom of use,
+not price. Our General Public Licenses are designed to make sure that
+you have the freedom to distribute copies of free software (and charge
+for this service if you wish); that you receive source code or can get
+it if you want it; that you can change the software and use pieces of
+it in new free programs; and that you are informed that you can do
+these things.
+
+ To protect your rights, we need to make restrictions that forbid
+distributors to deny you these rights or to ask you to surrender these
+rights. These restrictions translate to certain responsibilities for
+you if you distribute copies of the library or if you modify it.
+
+ For example, if you distribute copies of the library, whether gratis
+or for a fee, you must give the recipients all the rights that we gave
+you. You must make sure that they, too, receive or can get the source
+code. If you link other code with the library, you must provide
+complete object files to the recipients, so that they can relink them
+with the library after making changes to the library and recompiling
+it. And you must show them these terms so they know their rights.
+
+ We protect your rights with a two-step method: (1) we copyright the
+library, and (2) we offer you this license, which gives you legal
+permission to copy, distribute and/or modify the library.
+
+ To protect each distributor, we want to make it very clear that
+there is no warranty for the free library. Also, if the library is
+modified by someone else and passed on, the recipients should know
+that what they have is not the original version, so that the original
+author's reputation will not be affected by problems that might be
+introduced by others.
+
+ Finally, software patents pose a constant threat to the existence of
+any free program. We wish to make sure that a company cannot
+effectively restrict the users of a free program by obtaining a
+restrictive license from a patent holder. Therefore, we insist that
+any patent license obtained for a version of the library must be
+consistent with the full freedom of use specified in this license.
+
+ Most GNU software, including some libraries, is covered by the
+ordinary GNU General Public License. This license, the GNU Lesser
+General Public License, applies to certain designated libraries, and
+is quite different from the ordinary General Public License. We use
+this license for certain libraries in order to permit linking those
+libraries into non-free programs.
+
+ When a program is linked with a library, whether statically or using
+a shared library, the combination of the two is legally speaking a
+combined work, a derivative of the original library. The ordinary
+General Public License therefore permits such linking only if the
+entire combination fits its criteria of freedom. The Lesser General
+Public License permits more lax criteria for linking other code with
+the library.
+
+ We call this license the "Lesser" General Public License because it
+does Less to protect the user's freedom than the ordinary General
+Public License. It also provides other free software developers Less
+of an advantage over competing non-free programs. These disadvantages
+are the reason we use the ordinary General Public License for many
+libraries. However, the Lesser license provides advantages in certain
+special circumstances.
+
+ For example, on rare occasions, there may be a special need to
+encourage the widest possible use of a certain library, so that it becomes
+a de-facto standard. To achieve this, non-free programs must be
+allowed to use the library. A more frequent case is that a free
+library does the same job as widely used non-free libraries. In this
+case, there is little to gain by limiting the free library to free
+software only, so we use the Lesser General Public License.
+
+ In other cases, permission to use a particular library in non-free
+programs enables a greater number of people to use a large body of
+free software. For example, permission to use the GNU C Library in
+non-free programs enables many more people to use the whole GNU
+operating system, as well as its variant, the GNU/Linux operating
+system.
+
+ Although the Lesser General Public License is Less protective of the
+users' freedom, it does ensure that the user of a program that is
+linked with the Library has the freedom and the wherewithal to run
+that program using a modified version of the Library.
+
+ The precise terms and conditions for copying, distribution and
+modification follow. Pay close attention to the difference between a
+"work based on the library" and a "work that uses the library". The
+former contains code derived from the library, whereas the latter must
+be combined with the library in order to run.
+
+ GNU LESSER GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License Agreement applies to any software library or other
+program which contains a notice placed by the copyright holder or
+other authorized party saying it may be distributed under the terms of
+this Lesser General Public License (also called "this License").
+Each licensee is addressed as "you".
+
+ A "library" means a collection of software functions and/or data
+prepared so as to be conveniently linked with application programs
+(which use some of those functions and data) to form executables.
+
+ The "Library", below, refers to any such software library or work
+which has been distributed under these terms. A "work based on the
+Library" means either the Library or any derivative work under
+copyright law: that is to say, a work containing the Library or a
+portion of it, either verbatim or with modifications and/or translated
+straightforwardly into another language. (Hereinafter, translation is
+included without limitation in the term "modification".)
+
+ "Source code" for a work means the preferred form of the work for
+making modifications to it. For a library, complete source code means
+all the source code for all modules it contains, plus any associated
+interface definition files, plus the scripts used to control compilation
+and installation of the library.
+
+ Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running a program using the Library is not restricted, and output from
+such a program is covered only if its contents constitute a work based
+on the Library (independent of the use of the Library in a tool for
+writing it). Whether that is true depends on what the Library does
+and what the program that uses the Library does.
+
+ 1. You may copy and distribute verbatim copies of the Library's
+complete source code as you receive it, in any medium, provided that
+you conspicuously and appropriately publish on each copy an
+appropriate copyright notice and disclaimer of warranty; keep intact
+all the notices that refer to this License and to the absence of any
+warranty; and distribute a copy of this License along with the
+Library.
+
+ You may charge a fee for the physical act of transferring a copy,
+and you may at your option offer warranty protection in exchange for a
+fee.
+
+ 2. You may modify your copy or copies of the Library or any portion
+of it, thus forming a work based on the Library, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+ a) The modified work must itself be a software library.
+
+ b) You must cause the files modified to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ c) You must cause the whole of the work to be licensed at no
+ charge to all third parties under the terms of this License.
+
+ d) If a facility in the modified Library refers to a function or a
+ table of data to be supplied by an application program that uses
+ the facility, other than as an argument passed when the facility
+ is invoked, then you must make a good faith effort to ensure that,
+ in the event an application does not supply such function or
+ table, the facility still operates, and performs whatever part of
+ its purpose remains meaningful.
+
+ (For example, a function in a library to compute square roots has
+ a purpose that is entirely well-defined independent of the
+ application. Therefore, Subsection 2d requires that any
+ application-supplied function or table used by this function must
+ be optional: if the application does not supply it, the square
+ root function must still compute square roots.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Library,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Library, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote
+it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Library.
+
+In addition, mere aggregation of another work not based on the Library
+with the Library (or with a work based on the Library) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+ 3. You may opt to apply the terms of the ordinary GNU General Public
+License instead of this License to a given copy of the Library. To do
+this, you must alter all the notices that refer to this License, so
+that they refer to the ordinary GNU General Public License, version 2,
+instead of to this License. (If a newer version than version 2 of the
+ordinary GNU General Public License has appeared, then you can specify
+that version instead if you wish.) Do not make any other change in
+these notices.
+
+ Once this change is made in a given copy, it is irreversible for
+that copy, so the ordinary GNU General Public License applies to all
+subsequent copies and derivative works made from that copy.
+
+ This option is useful when you wish to copy part of the code of
+the Library into a program that is not a library.
+
+ 4. You may copy and distribute the Library (or a portion or
+derivative of it, under Section 2) in object code or executable form
+under the terms of Sections 1 and 2 above provided that you accompany
+it with the complete corresponding machine-readable source code, which
+must be distributed under the terms of Sections 1 and 2 above on a
+medium customarily used for software interchange.
+
+ If distribution of object code is made by offering access to copy
+from a designated place, then offering equivalent access to copy the
+source code from the same place satisfies the requirement to
+distribute the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+ 5. A program that contains no derivative of any portion of the
+Library, but is designed to work with the Library by being compiled or
+linked with it, is called a "work that uses the Library". Such a
+work, in isolation, is not a derivative work of the Library, and
+therefore falls outside the scope of this License.
+
+ However, linking a "work that uses the Library" with the Library
+creates an executable that is a derivative of the Library (because it
+contains portions of the Library), rather than a "work that uses the
+library". The executable is therefore covered by this License.
+Section 6 states terms for distribution of such executables.
+
+ When a "work that uses the Library" uses material from a header file
+that is part of the Library, the object code for the work may be a
+derivative work of the Library even though the source code is not.
+Whether this is true is especially significant if the work can be
+linked without the Library, or if the work is itself a library. The
+threshold for this to be true is not precisely defined by law.
+
+ If such an object file uses only numerical parameters, data
+structure layouts and accessors, and small macros and small inline
+functions (ten lines or less in length), then the use of the object
+file is unrestricted, regardless of whether it is legally a derivative
+work. (Executables containing this object code plus portions of the
+Library will still fall under Section 6.)
+
+ Otherwise, if the work is a derivative of the Library, you may
+distribute the object code for the work under the terms of Section 6.
+Any executables containing that work also fall under Section 6,
+whether or not they are linked directly with the Library itself.
+
+ 6. As an exception to the Sections above, you may also combine or
+link a "work that uses the Library" with the Library to produce a
+work containing portions of the Library, and distribute that work
+under terms of your choice, provided that the terms permit
+modification of the work for the customer's own use and reverse
+engineering for debugging such modifications.
+
+ You must give prominent notice with each copy of the work that the
+Library is used in it and that the Library and its use are covered by
+this License. You must supply a copy of this License. If the work
+during execution displays copyright notices, you must include the
+copyright notice for the Library among them, as well as a reference
+directing the user to the copy of this License. Also, you must do one
+of these things:
+
+ a) Accompany the work with the complete corresponding
+ machine-readable source code for the Library including whatever
+ changes were used in the work (which must be distributed under
+ Sections 1 and 2 above); and, if the work is an executable linked
+ with the Library, with the complete machine-readable "work that
+ uses the Library", as object code and/or source code, so that the
+ user can modify the Library and then relink to produce a modified
+ executable containing the modified Library. (It is understood
+ that the user who changes the contents of definitions files in the
+ Library will not necessarily be able to recompile the application
+ to use the modified definitions.)
+
+ b) Use a suitable shared library mechanism for linking with the
+ Library. A suitable mechanism is one that (1) uses at run time a
+ copy of the library already present on the user's computer system,
+ rather than copying library functions into the executable, and (2)
+ will operate properly with a modified version of the library, if
+ the user installs one, as long as the modified version is
+ interface-compatible with the version that the work was made with.
+
+ c) Accompany the work with a written offer, valid for at
+ least three years, to give the same user the materials
+ specified in Subsection 6a, above, for a charge no more
+ than the cost of performing this distribution.
+
+ d) If distribution of the work is made by offering access to copy
+ from a designated place, offer equivalent access to copy the above
+ specified materials from the same place.
+
+ e) Verify that the user has already received a copy of these
+ materials or that you have already sent this user a copy.
+
+ For an executable, the required form of the "work that uses the
+Library" must include any data and utility programs needed for
+reproducing the executable from it. However, as a special exception,
+the materials to be distributed need not include anything that is
+normally distributed (in either source or binary form) with the major
+components (compiler, kernel, and so on) of the operating system on
+which the executable runs, unless that component itself accompanies
+the executable.
+
+ It may happen that this requirement contradicts the license
+restrictions of other proprietary libraries that do not normally
+accompany the operating system. Such a contradiction means you cannot
+use both them and the Library together in an executable that you
+distribute.
+
+ 7. You may place library facilities that are a work based on the
+Library side-by-side in a single library together with other library
+facilities not covered by this License, and distribute such a combined
+library, provided that the separate distribution of the work based on
+the Library and of the other library facilities is otherwise
+permitted, and provided that you do these two things:
+
+ a) Accompany the combined library with a copy of the same work
+ based on the Library, uncombined with any other library
+ facilities. This must be distributed under the terms of the
+ Sections above.
+
+ b) Give prominent notice with the combined library of the fact
+ that part of it is a work based on the Library, and explaining
+ where to find the accompanying uncombined form of the same work.
+
+ 8. You may not copy, modify, sublicense, link with, or distribute
+the Library except as expressly provided under this License. Any
+attempt otherwise to copy, modify, sublicense, link with, or
+distribute the Library is void, and will automatically terminate your
+rights under this License. However, parties who have received copies,
+or rights, from you under this License will not have their licenses
+terminated so long as such parties remain in full compliance.
+
+ 9. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Library or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Library (or any work based on the
+Library), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Library or works based on it.
+
+ 10. Each time you redistribute the Library (or any work based on the
+Library), the recipient automatically receives a license from the
+original licensor to copy, distribute, link with or modify the Library
+subject to these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties with
+this License.
+
+ 11. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Library at all. For example, if a patent
+license would not permit royalty-free redistribution of the Library by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Library.
+
+If any portion of this section is held invalid or unenforceable under any
+particular circumstance, the balance of the section is intended to apply,
+and the section as a whole is intended to apply in other circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+ 12. If the distribution and/or use of the Library is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Library under this License may add
+an explicit geographical distribution limitation excluding those countries,
+so that distribution is permitted only in or among countries not thus
+excluded. In such case, this License incorporates the limitation as if
+written in the body of this License.
+
+ 13. The Free Software Foundation may publish revised and/or new
+versions of the Lesser General Public License from time to time.
+Such new versions will be similar in spirit to the present version,
+but may differ in detail to address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Library
+specifies a version number of this License which applies to it and
+"any later version", you have the option of following the terms and
+conditions either of that version or of any later version published by
+the Free Software Foundation. If the Library does not specify a
+license version number, you may choose any version ever published by
+the Free Software Foundation.
+
+ 14. If you wish to incorporate parts of the Library into other free
+programs whose distribution conditions are incompatible with these,
+write to the author to ask for permission. For software which is
+copyrighted by the Free Software Foundation, write to the Free
+Software Foundation; we sometimes make exceptions for this. Our
+decision will be guided by the two goals of preserving the free status
+of all derivatives of our free software and of promoting the sharing
+and reuse of software generally.
+
+ NO WARRANTY
+
+ 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
+WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
+EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
+OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY
+KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
+LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
+THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+ 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
+WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
+AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU
+FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR
+CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE
+LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
+RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
+FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
+SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+DAMAGES.
+
+ END OF TERMS AND CONDITIONS
+
+ How to Apply These Terms to Your New Libraries
+
+ If you develop a new library, and you want it to be of the greatest
+possible use to the public, we recommend making it free software that
+everyone can redistribute and change. You can do so by permitting
+redistribution under these terms (or, alternatively, under the terms of the
+ordinary General Public License).
+
+ To apply these terms, attach the following notices to the library. It is
+safest to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least the
+"copyright" line and a pointer to where the full notice is found.
+
+ <one line to give the library's name and a brief idea of what it does.>
+ Copyright (C) <year> <name of author>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ This library 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
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+
+Also add information on how to contact you by electronic and paper mail.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the library, if
+necessary. Here is a sample; alter the names:
+
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the
+ library `Frob' (a library for tweaking knobs) written by James Random Hacker.
+
+ <signature of Ty Coon>, 1 April 1990
+ Ty Coon, President of Vice
+
+That's all there is to it!
+
+
diff --git a/pygmentize b/pygmentize
new file mode 100755
index 00000000..c316500a
--- /dev/null
+++ b/pygmentize
@@ -0,0 +1,4 @@
+#!/usr/bin/env python
+
+import sys, pygments
+sys.exit(pygments.cmdline_main(sys.argv)) \ No newline at end of file
diff --git a/pygments/__init__.py b/pygments/__init__.py
new file mode 100644
index 00000000..23bdbccb
--- /dev/null
+++ b/pygments/__init__.py
@@ -0,0 +1,270 @@
+# -*- coding: utf-8 -*-
+"""
+ Pygments
+ ~~~~~~~
+
+ Pygments is a syntax highlighting package written in Python.
+
+ It aims to be a generic syntax highlighter for general use in all
+ kinds of software such as forum systems, wikis or other applications
+ that need to prettify source code. Highlights are:
+
+ * a wide range of common languages and markup formats is supported
+ * special attention is paid to details, increasing quality by a fair amount
+ * support for new languages and formats are added easily
+ * a number of output formats, presently HTML, LaTeX and ANSI sequences
+ * it is usable as a command-line tool and as a library
+ * ... and it highlights even Brainfuck!
+
+
+ :copyright: 2006 by Georg Brandl, Armin Ronacher, Lukas Meuser and others.
+ :license: GNU LGPL, see LICENSE for more details.
+"""
+
+__version__ = '0.1'
+__docformat__ = 'restructuredtext'
+__license__ = 'GNU Lesser General Public License (LGPL)'
+__author__ = 'Georg Brandl <g.brandl@gmx.net>'
+__url__ = 'http://pygments.pocoo.org/'
+
+
+import sys, os
+# using StringIO because it can handle Unicode strings
+from StringIO import StringIO
+
+from pygments.util import OptionError
+from pygments.lexers import LEXERS, get_lexer_by_name, get_lexer_for_filename
+
+from pygments.formatters import FORMATTERS, get_formatter_by_name, \
+ get_formatter_for_filename, TerminalFormatter
+
+
+def lex(code, lexer):
+ """
+ Lex ``code`` with ``lexer`` and return an iterable of tokens.
+ """
+ return lexer.get_tokens(code)
+
+
+def format(tokens, formatter, outfile=None):
+ """
+ Format a tokenlist ``tokens`` with the formatter ``formatter``.
+
+ If ``outfile`` is given and a valid file object (an object
+ with a ``write`` method), the result will be written to it, otherwise
+ it is returned as a string.
+ """
+ realoutfile = outfile or StringIO()
+ formatter.format(tokens, realoutfile)
+ if not outfile:
+ return realoutfile.getvalue()
+
+
+def highlight(code, lexer, formatter, outfile=None):
+ """
+ Lex ``code`` with ``lexer`` and format it with the formatter
+ ``formatter``.
+
+ If ``outfile`` is given and a valid file object (an object
+ with a ``write`` method), the result will be written to it, otherwise
+ it is returned as a string.
+ """
+ return format(lex(code, lexer), formatter, outfile)
+
+
+def cmdline_main(args):
+ """
+ Make pygments usable as a command line utility.
+ """
+ import getopt
+
+ USAGE = """\
+Usage: %s [-l <lexer>] [-f <formatter>] [-O <options>] [-o <outfile>] [<infile>]
+ %s -S <style> -f <formatter> [-a <arg>] [-O <options>]
+ %s -L | -h | -V
+
+Highlight the input file and write the result to <outfile>.
+
+If no input file is given, use stdin, if -o is not given, use stdout.
+
+<lexer> is a lexer name (query all lexer names with -L). If -l is not
+given, the lexer is guessed from the extension of the input file name
+(this obviously doesn't work if the input is stdin).
+
+Likewise, <formatter> is a formatter name, and will be guessed from
+the extension of the output file name. If no output file is given,
+the terminal formatter will be used by default.
+
+With the -O option, you can give the lexer and formatter a comma-
+separated list of options, e.g. ``-O bg=light,python=cool``.
+
+With the -S option, print out style definitions for style <style>
+for formatter <formatter>. The argument given by -a is formatter
+dependent.
+
+The -L option lists all available lexers and formatters.
+The -h option prints this help.
+The -V option prints the package version.
+""" % ((args[0],)*3)
+
+ try:
+ opts, args = getopt.getopt(args[1:], "l:f:o:O:LhVS:a:")
+ except getopt.GetoptError:
+ print >>sys.stderr, USAGE
+ return 2
+ opts = dict(opts)
+
+ if not opts and not args:
+ print USAGE
+ return 0
+
+ if opts.pop('-h', None) is not None:
+ print USAGE
+ return 0
+
+ if opts.pop('-V', None) is not None:
+ print 'Pygments version %s, (c) 2006 by %s.' % (__version__, __author__)
+ return 0
+
+ L_opt = opts.pop('-L', None)
+ if L_opt is not None:
+ if opts or args:
+ print >>sys.stderr, USAGE
+ return 2
+
+ # print version
+ cmdline_main(['', '-V'])
+ print
+ print "Lexers:"
+ print "~~~~~~~"
+
+ info = []
+ maxlen = 0
+ for mod, fullname, names, exts in LEXERS.itervalues():
+ tup = (', '.join(names)+':', fullname,
+ exts and '(extensions ' + ', '.join(exts) + ')' or '')
+ info.append(tup)
+ if len(tup[0]) > maxlen: maxlen = len(tup[0])
+ info.sort()
+ for i in info:
+ print ('%-'+str(maxlen)+'s %s %s') % i
+
+ print
+ print "Formatters:"
+ print "~~~~~~~~~~~"
+
+ info = []
+ maxlen = 0
+ for fullname, names, exts, doc in FORMATTERS.itervalues():
+ tup = (', '.join(names)+':', doc,
+ exts and '(extensions ' + ', '.join(exts) + ')' or '')
+ info.append(tup)
+ if len(tup[0]) > maxlen: maxlen = len(tup[0])
+ info.sort()
+ for i in info:
+ print ('%-'+str(maxlen)+'s %s %s') % i
+ return 0
+
+ O_opts = {}
+ o_str = opts.pop('-O', None)
+ if o_str:
+ try:
+ o_args = o_str.split(',')
+ for o_arg in o_args:
+ o_key, o_val = o_arg.split('=')
+ O_opts[o_key] = o_val
+ except ValueError:
+ print >>sys.stderr, 'Error in -O specification.'
+ return 2
+
+ S_opt = opts.pop('-S', None)
+ a_opt = opts.pop('-a', None)
+ if S_opt is not None:
+ f_opt = opts.pop('-f', None)
+ if not f_opt:
+ print >>sys.stderr, USAGE
+ return 2
+ if opts or args:
+ print >>sys.stderr, USAGE
+ return 2
+
+ try:
+ O_opts['style'] = S_opt
+ fmter = get_formatter_by_name(f_opt, **O_opts)
+ except ValueError, err:
+ print >>sys.stderr, err
+ return 1
+
+ arg = a_opt or ''
+ print fmter.get_style_defs(arg)
+ return 0
+
+ if a_opt is not None:
+ print >>sys.stderr, USAGE
+ return 2
+
+ outfn = opts.pop('-o', None)
+ fmter = opts.pop('-f', None)
+ if fmter:
+ try:
+ fmter = get_formatter_by_name(fmter, **O_opts)
+ except (OptionError, ValueError), err:
+ print >>sys.stderr, 'Error:', err
+ return 1
+
+ if outfn:
+ if not fmter:
+ try:
+ fmter = get_formatter_for_filename(outfn, **O_opts)
+ except (OptionError, ValueError), err:
+ print >>sys.stderr, 'Error:', err
+ return 1
+ try:
+ outfile = file(outfn, 'wb')
+ except Exception, err:
+ print >>sys.stderr, 'Error: cannot open outfile:', err
+ return 1
+ else:
+ if not fmter:
+ fmter = TerminalFormatter(**O_opts)
+ outfile = sys.stdout
+
+ lexer = opts.pop('-l', None)
+ if lexer:
+ try:
+ lexer = get_lexer_by_name(lexer, **O_opts)
+ except (OptionError, ValueError), err:
+ print >>sys.stderr, 'Error:', err
+ return 1
+
+ if args:
+ infn = args[0]
+ if not lexer:
+ try:
+ lexer = get_lexer_for_filename(infn, **O_opts)
+ except (OptionError, ValueError), err:
+ print >>sys.stderr, 'Error:', err
+ return 1
+
+ try:
+ code = file(infn).read()
+ except Exception, err:
+ print >>sys.stderr, 'Error: cannot read infile:', err
+ return 1
+ else:
+ if not lexer:
+ print >>sys.stderr, 'Error: no lexer name given and reading from stdin'
+ return 2
+ code = sys.stdin.read()
+
+ try:
+ highlight(code, lexer, fmter, outfile)
+ except Exception, err:
+ raise
+ print >>sys.stderr, 'Error while highlighting:', err
+ return 1
+ return 0
+
+
+if __name__ == '__main__':
+ sys.exit(cmdline_main(sys.argv))
diff --git a/pygments/console.py b/pygments/console.py
new file mode 100644
index 00000000..b7653172
--- /dev/null
+++ b/pygments/console.py
@@ -0,0 +1,74 @@
+# -*- coding: utf-8 -*-
+"""
+ pygments.console
+ ~~~~~~~~~~~~~~~
+
+ Format colored console output.
+
+ :copyright: 2006 by Georg Brandl, Armin Ronacher.
+ :license: GNU LGPL, see LICENSE for more details.
+"""
+
+esc = "\x1b["
+
+codes = {}
+codes[""] = ""
+codes["reset"] = esc + "39;49;00m"
+
+codes["bold"] = esc + "01m"
+codes["faint"] = esc + "02m"
+codes["standout"] = esc + "03m"
+codes["underline"] = esc + "04m"
+codes["blink"] = esc + "05m"
+codes["overline"] = esc + "06m"
+
+dark_colors = ["black", "darkred", "darkgreen", "brown", "darkblue",
+ "purple", "teal", "lightgray"]
+light_colors = ["darkgray", "red", "green", "yellow", "blue",
+ "fuchsia", "turquoise", "white"]
+
+x = 30
+for d, l in zip(dark_colors, light_colors):
+ codes[d] = esc + "%im" % x
+ codes[l] = esc + "%i;01m" % x
+ x += 1
+
+del d, l, x
+
+codes["darkteal"] = codes["turquoise"]
+codes["darkyellow"] = codes["brown"]
+codes["fuscia"] = codes["fuchsia"]
+codes["white"] = codes["bold"]
+
+
+def reset_color():
+ return codes["reset"]
+
+
+def colorize(color_key, text):
+ return codes[color_key] + text + codes["reset"]
+
+
+def ansiformat(attr, text):
+ """
+ Format ``text`` with a color and/or some attributes::
+
+ color normal color
+ *color* bold color
+ _color_ underlined color
+ +color+ blinking color
+ """
+ result = []
+ if attr[:1] == attr[-1:] == '+':
+ result.append(codes['blink'])
+ attr = attr[1:-1]
+ if attr[:1] == attr[-1:] == '*':
+ result.append(codes['bold'])
+ attr = attr[1:-1]
+ if attr[:1] == attr[-1:] == '_':
+ result.append(codes['underline'])
+ attr = attr[1:-1]
+ result.append(codes[attr])
+ result.append(text)
+ result.append(codes['reset'])
+ return ''.join(result)
diff --git a/pygments/formatter.py b/pygments/formatter.py
new file mode 100644
index 00000000..168401e3
--- /dev/null
+++ b/pygments/formatter.py
@@ -0,0 +1,64 @@
+# -*- coding: utf-8 -*-
+"""
+ pygments.formatter
+ ~~~~~~~~~~~~~~~~~
+
+ Base formatter class.
+
+ :copyright: 2006 by Georg Brandl, Armin Ronacher.
+ :license: GNU LGPL, see LICENSE for more details.
+"""
+
+from pygments.util import get_bool_opt
+from pygments.style import Style
+from pygments.styles import get_style_by_name
+
+__all__ = ['Formatter']
+
+
+def _lookup_style(style):
+ if isinstance(style, basestring):
+ return get_style_by_name(style)
+ return style
+
+
+class Formatter(object):
+ """
+ Converts a token stream to text.
+
+ Options accepted:
+
+ ``style``
+ The style to use, can be a string or a Style subclass
+ (default: "default"). Not used by e.g. the
+ TerminalFormatter.
+ ``full``
+ Tells the formatter to output a "full" document, i.e.
+ a complete self-contained document. This doesn't have
+ any effect for some formatters (default: false).
+ ``title``
+ If ``full`` is true, the title that should be used to
+ caption the document (default: '').
+ """
+
+ def __init__(self, **options):
+ self.style = _lookup_style(options.get('style', 'default'))
+ self.full = get_bool_opt(options, 'full', False)
+ self.title = options.get('title', '')
+ self.options = options
+
+ def get_style_defs(self, arg=''):
+ """
+ Return the style definitions for the current style as a string.
+
+ ``arg`` is an additional argument whose meaning depends on the
+ formatter used.
+ """
+ return ''
+
+ def format(self, tokensource, outfile):
+ """
+ Format ``tokensource``, an iterable of ``(tokentype, tokenstring)``
+ tuples and write it into ``outfile``.
+ """
+ raise NotImplementedError()
diff --git a/pygments/formatters/__init__.py b/pygments/formatters/__init__.py
new file mode 100644
index 00000000..b701efb4
--- /dev/null
+++ b/pygments/formatters/__init__.py
@@ -0,0 +1,73 @@
+# -*- coding: utf-8 -*-
+"""
+ pygments.formatters
+ ~~~~~~~~~~~~~~~~~~
+
+ Pygments formatters.
+
+ :copyright: 2006 by Georg Brandl, Armin Ronacher.
+ :license: GNU LGPL, see LICENSE for more details.
+"""
+import os.path
+from pygments.formatters.html import HtmlFormatter
+from pygments.formatters.terminal import TerminalFormatter
+from pygments.formatters.latex import LatexFormatter
+from pygments.formatters.bbcode import BBCodeFormatter
+from pygments.formatters.other import NullFormatter, RawTokenFormatter
+
+
+def _doc_desc(obj):
+ res = ''
+ for line in obj.__doc__.strip().splitlines():
+ if line.strip(): res += line.strip() + " "
+ else: break
+ return res
+
+
+#: Map formatter classes to ``(longname, names, file extensions, descr)``.
+FORMATTERS = {
+ HtmlFormatter: ('HTML', ('html',), ('.htm', '.html'),
+ _doc_desc(HtmlFormatter)),
+ TerminalFormatter: ('Terminal', ('terminal', 'console'), (),
+ _doc_desc(TerminalFormatter)),
+ LatexFormatter: ('LaTeX', ('latex', 'tex'), ('.tex',),
+ _doc_desc(LatexFormatter)),
+ RawTokenFormatter: ('Raw tokens', ('raw', 'tokens'), ('.raw',),
+ _doc_desc(RawTokenFormatter)),
+ NullFormatter: ('Text only', ('text', 'null'), ('.txt',),
+ _doc_desc(NullFormatter)),
+ BBCodeFormatter: ('BBcode', ('bbcode', 'bb'), (),
+ _doc_desc(BBCodeFormatter))
+}
+
+
+_formatter_cache = {}
+
+def _init_formatter_cache():
+ if _formatter_cache: return
+ for cls, info in FORMATTERS.iteritems():
+ for alias in info[1]:
+ _formatter_cache[alias] = cls
+ for ext in info[2]:
+ _formatter_cache["/"+ext] = cls
+
+
+def get_formatter_by_name(name, **options):
+ _init_formatter_cache()
+ cls = _formatter_cache.get(name, None)
+ if not cls:
+ raise ValueError("No formatter found for name %r" % name)
+ return cls(**options)
+
+
+def get_formatter_for_filename(fn, **options):
+ _init_formatter_cache()
+ # try by filename extension
+ cls = _formatter_cache.get("/"+os.path.splitext(fn)[1], None)
+ if cls:
+ return cls(**options)
+ # try by whole file name
+ cls = _formatter_cache.get("/"+os.path.basename(fn), None)
+ if not cls:
+ raise ValueError("No formatter found for file name %r" % fn)
+ return cls(**options)
diff --git a/pygments/formatters/bbcode.py b/pygments/formatters/bbcode.py
new file mode 100644
index 00000000..61384c7f
--- /dev/null
+++ b/pygments/formatters/bbcode.py
@@ -0,0 +1,99 @@
+# -*- coding: utf-8 -*-
+"""
+ pygments.formatters.bbcode
+ ~~~~~~~~~~~~~~~~~~~~~~~~~
+
+ BBcode formatter.
+
+ :copyright: 2006 by Lukas Meuser.
+ :license: GNU LGPL, see LICENSE for more details.
+"""
+
+
+from pygments.formatter import Formatter
+from pygments.util import get_bool_opt
+
+__all__ = ['BBCodeFormatter']
+
+
+class BBCodeFormatter(Formatter):
+ """
+ Output BBCode tags with appropiate colors and formatting.
+
+ This formatter doesn't support background colors and borders, as there are
+ no common BBcodes for that.
+
+ Some board systems (e.g. phpBB) don't support colors in their [code] tag,
+ so you can't use the highlighting together with that tag.
+ Text in a [code] tag usually is shown with a monospace font (which this
+ formatter can do with the ``monofont`` option) and no spaces (which you
+ need for indentation) are removed.
+
+ Additional options accepted:
+
+ ``codetag``
+ If set to true, put the output into [code] tags (default: false).
+
+ ``monofont``
+ If set to true, add a tag to show the code with a monospace font
+ (default: false).
+ """
+
+ def __init__(self, **options):
+ Formatter.__init__(self, **options)
+ self._make_styles()
+ self._code = get_bool_opt(options, 'codetag', False)
+ self._mono = get_bool_opt(options, 'monofont', False)
+
+ def _make_styles(self):
+ self.styles = {}
+ for token, style in self.style._styles.iteritems():
+ start = end = ''
+ color, bold, italic, underline, bg, border = style
+ if color:
+ start += '[color=#%s]' % color
+ end = '[/color]' + end
+ if bold:
+ start += '[b]'
+ end = '[/b]' + end
+ if italic:
+ start += '[i]'
+ end = '[/i]' + end
+ if underline:
+ start += '[u]'
+ end = '[/u]' + end
+ # there are no common BBcodes for background-color and border
+
+ self.styles[token] = start, end
+
+ def format(self, tokensource, outfile):
+ if self._code:
+ outfile.write('[code]')
+ if self._mono:
+ outfile.write('[font=monospace]')
+
+ lastval = ''
+ lasttype = None
+
+ for ttype, value in tokensource:
+ while ttype not in self.styles:
+ ttype = ttype.parent
+ if ttype == lasttype:
+ lastval += value
+ else:
+ if lastval:
+ start, end = self.styles[lasttype]
+ outfile.write(''.join((start, lastval, end)))
+ lastval = value
+ lasttype = ttype
+
+ if lastval:
+ start, end = self.styles[lasttype]
+ outfile.write(''.join((start, lastval, end)))
+
+ if self._mono:
+ outfile.write('[/font]')
+ if self._code:
+ outfile.write('[/code]')
+ if self._code or self._mono:
+ outfile.write('\n')
diff --git a/pygments/formatters/html.py b/pygments/formatters/html.py
new file mode 100644
index 00000000..6223aba6
--- /dev/null
+++ b/pygments/formatters/html.py
@@ -0,0 +1,280 @@
+# -*- coding: utf-8 -*-
+"""
+ pygments.formatters.html
+ ~~~~~~~~~~~~~~~~~~~~~~~
+
+ Formatter for HTML output.
+
+ :copyright: 2006 by Georg Brandl, Armin Ronacher.
+ :license: GNU LGPL, see LICENSE for more details.
+"""
+import StringIO
+
+from pygments.formatter import Formatter
+from pygments.token import Token, Text, STANDARD_TYPES
+from pygments.util import get_bool_opt, get_int_opt, get_list_opt
+
+
+__all__ = ['HtmlFormatter']
+
+
+def escape_html(text):
+ """Escape &, <, > as well as single and double quotes for HTML."""
+ return text.replace('&', '&amp;'). \
+ replace('<', '&lt;'). \
+ replace('>', '&gt;'). \
+ replace('"', '&quot;'). \
+ replace("'", '&#39;')
+
+
+def get_random_id():
+ """Return a random id for javascript fields."""
+ from random import random
+ from time import time
+ try:
+ from hashlib import sha1 as sha
+ except ImportError:
+ import sha
+ sha = sha.new
+ return sha('%s|%s' % (random(), time())).hexdigest()
+
+
+def _get_ttype_class(ttype):
+ fname = STANDARD_TYPES.get(ttype)
+ if fname: return fname
+ aname = ''
+ while fname is None:
+ aname = '-' + ttype[-1] + aname
+ ttype = ttype.parent
+ fname = STANDARD_TYPES.get(ttype)
+ return fname + aname
+
+
+DOC_TEMPLATE = '''\
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN"
+ "http://www.w3.org/TR/html4/strict.dtd">
+
+<html>
+<head>
+ <title>%(title)s</title>
+ <style type="text/css">
+td.linenos { background-color: #f0f0f0; padding-right: 10px; }
+%(styledefs)s
+ </style>
+</head>
+<body>
+<h2>%(title)s</h2>
+
+%(code)s
+
+</body>
+</html>
+'''
+
+
+class HtmlFormatter(Formatter):
+ """
+ Output HTML <span> tags with appropriate classes.
+
+ Additional options accepted:
+
+ ``nowrap``
+ If set to true, don't wrap the tokens at all. This disables
+ all other options (default: False).
+ ``noclasses``
+ If set to true, token <span>s will not use CSS classes, but
+ inline styles.
+ ``classprefix``
+ Prefix for token CSS classes, is prepended to all token style
+ classes (e.g. class="o" -> class="_o" if classprefix == '_')
+ (default: '').
+ ``cssclass``
+ CSS class for the wrapping <div> (default: 'highlight').
+ ``cssstyles``
+ Inline CSS styles for the wrapping <div>. (default: '').
+ ``linenos``
+ If set to ``True``, output line numbers (default: False).
+ ``linenostart``
+ The line number for the first line (default: 1).
+ ``linenostep``
+ If set to a number n > 1, only every nth line number is printed
+ (default: 1).
+ ``linenospecial``
+ If set to a number n > 0, every nth line number is given a special
+ CSS class ``special`` (default: 0).
+ ``nobackground``
+ If set to ``True`` the formatter won't output the background color
+ for the overall element (this automatically defaults to ``False``
+ when there is no overall element [eg: no argument for the
+ `get_syntax_defs` method given]) (default: ``False``)
+ """
+
+ def __init__(self, **options):
+ Formatter.__init__(self, **options)
+ self.nowrap = get_bool_opt(options, 'nowrap', False)
+ self.noclasses = get_bool_opt(options, 'noclasses', False)
+ self.classprefix = options.get('classprefix', '')
+ self.cssclass = options.get('cssclass', 'highlight')
+ self.cssstyles = options.get('cssstyles', '')
+ self.linenos = get_bool_opt(options, 'linenos', False)
+ self.linenostart = abs(get_int_opt(options, 'linenostart', 1))
+ self.linenostep = abs(get_int_opt(options, 'linenostep', 1))
+ self.linenospecial = abs(get_int_opt(options, 'linenospecial', 0))
+ self.nobackground = get_bool_opt(options, 'nobackground', False)
+
+ self._class_cache = {}
+ self._create_stylesheet()
+
+ def _get_css_class(self, ttype):
+ """Return the css class of this token type prefixed with
+ the classprefix option."""
+ if ttype in self._class_cache:
+ return self._class_cache[ttype]
+
+ return self.classprefix + STANDARD_TYPES.get(ttype) or _get_ttype_class(ttype)
+
+ def _create_stylesheet(self):
+ t2c = self.ttype2class = {Token: ''}
+ c2s = self.class2style = {}
+ cp = self.classprefix
+ for ttype, ndef in self.style:
+ name = cp + _get_ttype_class(ttype)
+ style = ''
+ if ndef['color']:
+ style += 'color: #%s; ' % ndef['color']
+ if ndef['bold']:
+ style += 'font-weight: bold; '
+ if ndef['italic']:
+ style += 'font-style: italic; '
+ if ndef['underline']:
+ style += 'text-decoration: underline; '
+ if ndef['bgcolor']:
+ style += 'background-color: #%s; ' % ndef['bgcolor']
+ if ndef['border']:
+ style += 'border: 1px solid #%s; ' % ndef['border']
+ if style:
+ t2c[ttype] = name
+ # save len(ttype) to enable ordering the styles by
+ # hierarchy (necessary for CSS cascading rules!)
+ c2s[name] = (style[:-2], ttype, len(ttype))
+
+ def get_style_defs(self, arg=''):
+ """
+ Return CSS style definitions for the classes produced by the
+ current highlighting style. ``arg`` can be a string of selectors
+ to insert before the token type classes.
+ """
+ if arg:
+ arg += ' '
+ styles = [(level, ttype, cls, style)
+ for cls, (style, ttype, level) in self.class2style.iteritems()
+ if cls and style]
+ styles.sort()
+ lines = ['%s.%s { %s } /* %s */' % (arg, cls, style, repr(ttype)[6:])
+ for level, ttype, cls, style in styles]
+ if arg and not self.nobackground and \
+ self.style.background_color is not None:
+ text_style = ''
+ if Text in self.ttype2class:
+ text_style = ' ' + self.class2style[self.ttype2class[Text]][0]
+ lines.insert(0, '%s{ background: %s;%s }' %
+ (arg, self.style.background_color, text_style))
+ return '\n'.join(lines)
+
+ def _format_nowrap(self, tokensource, outfile, lnos=False):
+ lncount = 0
+ nocls = self.noclasses
+ # for <span style=""> lookup only
+ getcls = self.ttype2class.get
+ c2s = self.class2style
+
+ write = outfile.write
+ lspan = ''
+ for ttype, value in tokensource:
+ htmlvalue = escape_html(value)
+ if lnos:
+ lncount += value.count("\n")
+
+ if nocls:
+ cclass = getcls(ttype)
+ while cclass is None:
+ ttype = ttype.parent
+ cclass = getcls(ttype)
+ cspan = cclass and '<span style="%s">' % c2s[cclass][0]
+ else:
+ cls = self._get_css_class(ttype)
+ cspan = cls and '<span class="%s">' % cls
+
+ if cspan == lspan:
+ if not cspan:
+ write(htmlvalue)
+ else:
+ write(htmlvalue.replace('\n', '</span>\n' + cspan))
+ elif htmlvalue: # if no value, leave old span open
+ if lspan:
+ write('</span>')
+ lspan = cspan
+ if cspan:
+ htmlvalue = htmlvalue.replace('\n', '</span>\n' + cspan)
+ write(cspan + htmlvalue)
+ else:
+ write(htmlvalue)
+ if lspan:
+ write('</span>')
+ return lncount
+
+ def format(self, tokensource, outfile):
+ if self.nowrap:
+ self._format_nowrap(tokensource, outfile)
+ return
+
+ realoutfile = outfile
+ lnos = self.linenos
+ full = self.full
+
+ div = ('<div' + (self.cssclass and ' class="%s" ' % self.cssclass)
+ + (self.cssstyles and ' style="%s"' % self.cssstyles) + '>')
+ if full or lnos:
+ outfile = StringIO.StringIO()
+ else:
+ outfile.write(div)
+
+ outfile.write('<pre>')
+ lncount = self._format_nowrap(tokensource, outfile, lnos)
+ outfile.write('</pre>')
+
+ ret = ''
+ if lnos:
+ fl = self.linenostart
+ mw = len(str(lncount + fl - 1))
+ sp = self.linenospecial
+ st = self.linenostep
+ if sp:
+ ls = '\n'.join([(i%st == 0 and
+ (i%sp == 0 and '<span class="special">%*d</span>'
+ or '%*d') % (mw, i)
+ or '')
+ for i in range(fl, fl + lncount)])
+ else:
+ ls = '\n'.join([(i%st == 0 and ('%*d' % (mw, i)) or '')
+ for i in range(fl, fl + lncount)])
+
+ ret = div + ('<table><tr>'
+ '<td class="linenos" title="click to toggle" '
+ 'onclick="with (this.firstChild.style) { display = '
+ '''(display == '') ? 'none' : '' }"><pre>'''
+ + ls + '</pre></td><td class="code">')
+ ret += outfile.getvalue()
+ ret += '</td></tr></table>'
+
+ if full:
+ if not ret:
+ ret = div + outfile.getvalue() + '</div>\n'
+ realoutfile.write(DOC_TEMPLATE %
+ dict(title = self.title,
+ styledefs = self.get_style_defs('body'),
+ code = ret))
+ elif lnos:
+ realoutfile.write(ret + '</div>\n')
+ else:
+ realoutfile.write('</div>\n')
diff --git a/pygments/formatters/latex.py b/pygments/formatters/latex.py
new file mode 100644
index 00000000..84fe5d12
--- /dev/null
+++ b/pygments/formatters/latex.py
@@ -0,0 +1,191 @@
+# -*- coding: utf-8 -*-
+"""
+ pygments.formatters.latex
+ ~~~~~~~~~~~~~~~~~~~~~~~~
+
+ Formatter for LaTeX fancyvrb output.
+
+ :copyright: 2006 by Georg Brandl.
+ :license: GNU LGPL, see LICENSE for more details.
+"""
+import StringIO
+
+from pygments.formatter import Formatter
+from pygments.token import Token
+from pygments.util import get_bool_opt, get_int_opt
+
+
+__all__ = ['LatexFormatter']
+
+
+def escape_tex(text):
+ return text.replace('@', '\x00'). \
+ replace('[', '\x01'). \
+ replace(']', '\x02'). \
+ replace('\x00', '@at[]').\
+ replace('\x01', '@lb[]').\
+ replace('\x02', '@rb[]')
+
+
+DOC_TEMPLATE = r'''
+\documentclass{%(docclass)s}
+\usepackage{fancyvrb}
+\usepackage{color}
+%(preamble)s
+
+%(styledefs)s
+
+\begin{document}
+
+\section*{%(title)s}
+
+%(code)s
+\end{document}
+'''
+
+
+class LatexFormatter(Formatter):
+ """
+ Output LaTeX "color" and "fancyvrb" control sequences.
+ """
+
+ def __init__(self, **options):
+ """
+ Additional options accepted:
+
+ ``docclass``
+ If ``full`` is true, this is the document class to use (default: 'article').
+ ``preamble``
+ If ``full`` is true, this can be further preamble commands (default: '').
+ ``linenos``
+ If true, output line numbers (default: False).
+ ``linenostart``
+ The line number for the first line (default: 1).
+ ``linenostep``
+ If set to a number n > 1, only every nth line number is printed (default: 1).
+ ``verboptions``
+ Additional options given to the Verbatim environment (default: '').
+ ``nobackground``
+ If set to ``True`` the formatter won't output the background color
+ for the overall element (default: ``False``)
+ Note that light colors on dark background with this option disabled
+ won't be readable very good.
+ """
+ Formatter.__init__(self, **options)
+ self.docclass = options.get('docclass', 'article')
+ self.preamble = options.get('preamble', '')
+ self.linenos = get_bool_opt(options, 'linenos', False)
+ self.linenostart = abs(get_int_opt(options, 'linenostart', 1))
+ self.linenostep = abs(get_int_opt(options, 'linenostep', 1))
+ self.verboptions = options.get('verboptions', '')
+ self.nobackground = get_bool_opt(options, 'nobackground', False)
+
+ self._create_stylecmds()
+
+
+ def _create_stylecmds(self):
+ t2c = self.ttype2cmd = {Token: ''}
+ c2d = self.cmd2def = {}
+
+ letters = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'
+ first = iter(letters)
+ second = iter(letters)
+ firstl = first.next()
+
+ def rgbcolor(col):
+ if col:
+ return ','.join(['%.2f' %(int(col[i] + col[i + 1], 16) / 255.0)
+ for i in (0, 2, 4)])
+ else:
+ return '1,1,1'
+
+ for ttype, ndef in self.style:
+ cmndef = '#1'
+ if ndef['bold']:
+ cmndef = r'\textbf{' + cmndef + '}'
+ if ndef['italic']:
+ cmndef = r'\textit{' + cmndef + '}'
+ if ndef['underline']:
+ cmndef = r'\underline{' + cmndef + '}'
+ if ndef['color']:
+ cmndef = r'\textcolor[rgb]{%s}{%s}' % (
+ rgbcolor(ndef['color']),
+ cmndef
+ )
+ if ndef['border']:
+ cmndef = r'\fcolorbox[rgb]{%s}{%s}{%s}' % (
+ rgbcolor(ndef['border']),
+ rgbcolor(ndef['bgcolor']),
+ cmndef
+ )
+ elif ndef['bgcolor']:
+ cmndef = r'\colorbox[rgb]{%s}{%s}' % (
+ rgbcolor(ndef['bgcolor']),
+ cmndef
+ )
+ if cmndef == '#1':
+ continue
+ try:
+ alias = 'C' + firstl + second.next()
+ except StopIteration:
+ firstl = first.next()
+ second = iter(letters)
+ alias = 'C' + firstl + second.next()
+ t2c[ttype] = alias
+ c2d[alias] = cmndef
+
+ def get_style_defs(self, arg=''):
+ """
+ Return the \\newcommand sequences needed to define the commands
+ used to format text in the verbatim environment. If ``arg`` is
+ given and true, use \\renewcommand instead.
+ """
+ nc = (arg and r'\renewcommand' or r'\newcommand')
+ return '%s\\at{@}\n%s\\lb{[}\n%s\\rb{]}\n' % (nc, nc, nc) + \
+ '\n'.join(['%s\\%s[1]{%s}' % (nc, alias, cmndef)
+ for alias, cmndef in self.cmd2def.iteritems()
+ if cmndef != '#1'])
+
+ def format(self, tokensource, outfile):
+ #XXX: add support for background colors!!!!!!!111!1
+
+ if self.full:
+ realoutfile = outfile
+ outfile = StringIO.StringIO()
+
+ outfile.write(r'\begin{Verbatim}[commandchars=@\[\]')
+ if self.linenos:
+ start, step = self.linenostart, self.linenostep
+ outfile.write(',numbers=left' +
+ (start and ',firstnumber=%d' % start or '') +
+ (step and ',stepnumber=%d' % step or ''))
+ if self.verboptions:
+ outfile.write(',' + self.verboptions)
+ outfile.write(']\n')
+
+ for ttype, value in tokensource:
+ value = escape_tex(value)
+ cmd = self.ttype2cmd.get(ttype)
+ while cmd is None:
+ ttype = ttype.parent
+ cmd = self.ttype2cmd.get(ttype)
+ if cmd:
+ spl = value.split('\n')
+ for line in spl[:-1]:
+ if line:
+ outfile.write("@%s[%s]" % (cmd, line))
+ outfile.write('\n')
+ if spl[-1]:
+ outfile.write("@%s[%s]" % (cmd, spl[-1]))
+ else:
+ outfile.write(value)
+
+ outfile.write('\n\\end{Verbatim}\n')
+
+ if self.full:
+ realoutfile.write(DOC_TEMPLATE %
+ dict(docclass = self.docclass,
+ preamble = self.preamble,
+ title = self.title,
+ styledefs = self.get_style_defs(),
+ code = outfile.getvalue()))
diff --git a/pygments/formatters/other.py b/pygments/formatters/other.py
new file mode 100644
index 00000000..eb8d72fc
--- /dev/null
+++ b/pygments/formatters/other.py
@@ -0,0 +1,73 @@
+# -*- coding: utf-8 -*-
+"""
+ pygments.formatters.other
+ ~~~~~~~~~~~~~~~~~~~~~~~~
+
+ Other formatters.
+
+ :copyright: 2006 by Georg Brandl, Armin Ronacher.
+ :license: GNU LGPL, see LICENSE for more details.
+"""
+
+from pygments.formatter import Formatter
+import StringIO
+
+__all__ = ['NullFormatter', 'RawTokenFormatter']
+
+
+class NullFormatter(Formatter):
+ """
+ Output the text unchanged without any formatting.
+ """
+ def format(self, tokensource, outfile):
+ for ttype, value in tokensource:
+ outfile.write(value)
+
+
+class RawTokenFormatter(Formatter):
+ """
+ Output a raw token representation for storing token streams.
+
+ The format is ``tokentype<TAB>repr(tokenstring)``
+
+ Additional options accepted:
+
+ ``compress``
+ If set to "gz" or "bz2", compress the token stream with
+ the given compression algorithm (default: '').
+ """
+
+ def __init__(self, **options):
+ Formatter.__init__(self, **options)
+ self.compress = options.get('compress', '')
+
+ def format(self, tokensource, outfile):
+ if self.compress == 'gz':
+ import gzip
+ outfile = gzip.GzipFile('', 'wb', 9, outfile)
+ write = outfile.write
+ flush = outfile.flush
+ elif self.compress == 'bz2':
+ import bz2
+ compressor = bz2.BZ2Compressor(9)
+ def write(text):
+ outfile.write(compressor.compress(text))
+ def flush():
+ outfile.write(compressor.flush())
+ outfile.flush()
+ else:
+ write = outfile.write
+ flush = outfile.flush
+
+ lasttype = None
+ lastval = ''
+ for ttype, value in tokensource:
+ if ttype is lasttype:
+ lastval += value
+ else:
+ if lasttype:
+ write("%s\t%r\n" % (lasttype, lastval))
+ lastval = value
+ lasttype = ttype
+ write("%s\t%r\n" % (lasttype, lastval))
+ flush()
diff --git a/pygments/formatters/terminal.py b/pygments/formatters/terminal.py
new file mode 100644
index 00000000..b4b0071e
--- /dev/null
+++ b/pygments/formatters/terminal.py
@@ -0,0 +1,98 @@
+# -*- coding: utf-8 -*-
+"""
+ pygments.formatters.terminal
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+ Formatter for terminal output with ANSI sequences.
+
+ :copyright: 2006 by Georg Brandl.
+ :license: GNU LGPL, see LICENSE for more details.
+"""
+
+from pygments.formatter import Formatter
+from pygments.token import Keyword, Name, Comment, String, Error, \
+ Number, Operator, Generic, Token
+from pygments.console import ansiformat
+from pygments.util import get_bool_opt
+
+
+__all__ = ['TerminalFormatter']
+
+
+#: Map token types to a tuple of color values for light and dark
+#: backgrounds.
+TERMINAL_COLORS = {
+ Token: ('', ''),
+
+ Comment: ('lightgray', 'darkgray'),
+ Keyword: ('darkblue', 'blue'),
+ Keyword.Type: ('teal', 'turquoise'),
+ Operator.Word: ('purple', 'fuchsia'),
+ Name.Builtin: ('teal', 'turquoise'),
+ Name.Function: ('darkgreen', 'green'),
+ Name.Namespace: ('_teal_', '_turquoise_'),
+ Name.Class: ('_darkgreen_', '_green_'),
+ Name.Exception: ('teal', 'turquoise'),
+ Name.Decorator: ('darkgray', 'lightgray'),
+ Name.Variable: ('darkred', 'red'),
+ Name.Constant: ('darkred', 'red'),
+ Name.Attribute: ('teal', 'turquoise'),
+ Name.Tag: ('blue', 'blue'),
+ String: ('brown', 'brown'),
+ Number: ('darkblue', 'blue'),
+
+ Generic.Deleted: ('red', 'red'),
+ Generic.Inserted: ('darkgreen', 'green'),
+ Generic.Heading: ('**', '**'),
+ Generic.Subheading: ('*purple*', '*fuchsia*'),
+ Generic.Error: ('red', 'red'),
+
+ Error: ('_red_', '_red_'),
+}
+
+
+class TerminalFormatter(Formatter):
+ """
+ Output plain text with coloring ANSI sequences.
+ """
+
+ def __init__(self, **options):
+ """
+ Accepted options:
+
+ ``bg``
+ Set to ``'light'`` or ``'dark'`` depending on the
+ terminal's background.
+
+ ``colorscheme``
+ ``None`` or a dictionary mapping token types to
+ ``(lightbg, darkbg)`` color names.
+
+ ``debug``
+ If true, output "<<ERROR>>" after each error token.
+ """
+ Formatter.__init__(self, **options)
+ self.darkbg = options.get('bg', 'light') == 'dark'
+ self.colorscheme = options.get('colorscheme', None) or TERMINAL_COLORS
+ self.debug = get_bool_opt(options, 'debug', False)
+
+ def format(self, tokensource, outfile):
+ dbg = self.debug
+ for ttype, value in tokensource:
+ color = self.colorscheme.get(ttype)
+ while color is None:
+ ttype = ttype[:-1]
+ color = self.colorscheme.get(ttype)
+ if color:
+ color = color[self.darkbg]
+ spl = value.split('\n')
+ for line in spl[:-1]:
+ if line:
+ outfile.write(ansiformat(color, line))
+ outfile.write('\n')
+ if spl[-1]:
+ outfile.write(ansiformat(color, spl[-1]))
+ else:
+ outfile.write(value)
+ if dbg and ttype is Error:
+ outfile.write('<<ERROR>>')
diff --git a/pygments/lexer.py b/pygments/lexer.py
new file mode 100644
index 00000000..3d3ee865
--- /dev/null
+++ b/pygments/lexer.py
@@ -0,0 +1,486 @@
+# -*- coding: utf-8 -*-
+"""
+ pygments.lexer
+ ~~~~~~~~~~~~~
+
+ Base lexer classes.
+
+ :copyright: 2006 by Georg Brandl.
+ :license: GNU LGPL, see LICENSE for more details.
+"""
+import re
+
+from pygments.token import Error, Text, Other, _TokenType
+from pygments.util import get_bool_opt, get_int_opt
+
+
+__all__ = ['Lexer', 'RegexLexer', 'ExtendedRegexLexer', 'DelegatingLexer',
+ 'LexerContext', 'include', 'flags', 'bygroups', 'using', 'this']
+
+
+class Lexer(object):
+ """
+ Lexer for a specific language.
+
+ Basic options recognized:
+ ``stripnl``
+ Strip leading and trailing newlines from the input (default: True).
+ ``stripall``
+ Strip all leading and trailing whitespace from the input
+ (default: False).
+ ``tabsize``
+ If given and greater than 0, expand tabs in the input (default: 0).
+ """
+
+ #: Name of the lexer
+ name = None
+
+ #: Shortcuts for the lexer
+ aliases = []
+
+ #: fn match rules
+ filenames = []
+
+ def __init__(self, **options):
+ self.options = options
+ self.stripnl = get_bool_opt(options, 'stripnl', True)
+ self.stripall = get_bool_opt(options, 'stripall', False)
+ self.tabsize = get_int_opt(options, 'tabsize', 0)
+
+ def get_tokens(self, text):
+ """
+ Return an iterable of (tokentype, value) pairs generated from ``text``.
+
+ Also preprocess the text, i.e. expand tabs and strip it if wanted.
+ """
+ text = type(text)('\n').join(text.splitlines())
+ if self.stripall:
+ text = text.strip()
+ elif self.stripnl:
+ text = text.strip('\n')
+ if self.tabsize > 0:
+ text = text.expandtabs(self.tabsize)
+ if not text.endswith('\n'):
+ text += '\n'
+
+ for i, t, v in self.get_tokens_unprocessed(text):
+ yield t, v
+
+ def get_tokens_unprocessed(self, text):
+ """
+ Return an iterable of (tokentype, value) pairs.
+ In subclasses, implement this method as a generator to
+ maximize effectiveness.
+ """
+ raise NotImplementedError
+
+
+class DelegatingLexer(Lexer):
+ """
+ This lexer takes two lexer as arguments. A root lexer and
+ a language lexer. First everything is scanned using the language
+ lexer, afterwards all ``Other`` tokens are lexed using the root
+ lexer.
+
+ The lexers from the ``template`` lexer package use this base lexer.
+ """
+
+ def __init__(self, _root_lexer, _language_lexer, _needle=Other, **options):
+ self.root_lexer = _root_lexer(**options)
+ self.language_lexer = _language_lexer(**options)
+ self.needle = _needle
+ Lexer.__init__(self, **options)
+
+ def get_tokens_unprocessed(self, text):
+ buffered = ''
+ insertions = []
+ lng_buffer = []
+ for i, t, v in self.language_lexer.get_tokens_unprocessed(text):
+ if t is self.needle:
+ if lng_buffer:
+ insertions.append((len(buffered), lng_buffer))
+ lng_buffer = []
+ buffered += v
+ else:
+ lng_buffer.append((i, t, v))
+ # the last "\n" should always be lexed by the root lexer
+ return do_insertions(insertions,
+ self.root_lexer.get_tokens_unprocessed(buffered))
+
+
+#-------------------------------------------------------------------------------
+# RegexLexer and ExtendedRegexLexer
+#
+
+
+class include(str):
+ """
+ Indicates that a state should include rules from another state.
+ """
+ pass
+
+
+class combined(tuple):
+ """
+ Indicates a state combined from multiple states.
+ """
+
+ def __new__(cls, *args):
+ return tuple.__new__(cls, args)
+
+ def __init__(self, *args):
+ tuple.__init__(self, args)
+
+
+class _PseudoMatch(object):
+ """
+ A pseudo match object constructed from a string.
+ """
+
+ def __init__(self, start, text):
+ self._text = text
+ self._start = start
+
+ def start(self, arg=None):
+ return self._start
+
+ def end(self, arg=None):
+ return self._start + len(self._text)
+
+ def group(self, arg=None):
+ if arg:
+ raise IndexError('No such group')
+ return self._text
+
+ def groups(self):
+ return (self._text,)
+
+ def groupdict(self):
+ return {}
+
+
+def bygroups(*args):
+ """
+ Callback that yields multiple actions for each group in the match.
+ """
+ def callback(lexer, match, ctx=None):
+ for i, action in enumerate(args):
+ if type(action) is _TokenType:
+ data = match.group(i + 1)
+ if data:
+ yield match.start(i + 1), action, data
+ else:
+ if ctx:
+ ctx.pos = match.start(i+1)
+ for item in action(lexer, _PseudoMatch(match.start(i + 1),
+ match.group(i + 1)), ctx):
+ if item:
+ yield item
+ if ctx:
+ ctx.pos = match.end()
+ return callback
+
+
+class _This(object):
+ """
+ Special singleton used for indicating the caller class.
+ Used by ``using``.
+ """
+this = _This()
+
+
+def using(_other, **kwargs):
+ """
+ Callback that processes the match with a different lexer.
+
+ The keyword arguments are forwarded to the lexer.
+ """
+ if _other is this:
+ def callback(lexer, match, ctx=None):
+ s = match.start()
+ for i, t, v in lexer.get_tokens_unprocessed(match.group()):
+ yield i + s, t, v
+ if ctx:
+ ctx.pos = match.end()
+ else:
+ def callback(lexer, match, ctx=None):
+ # XXX: cache that somehow
+ kwargs.update(lexer.options)
+ lx = _other(**kwargs)
+
+ s = match.start()
+ for i, t, v in lx.get_tokens_unprocessed(match.group()):
+ yield i + s, t, v
+ if ctx:
+ ctx.pos = match.end()
+ return callback
+
+
+class RegexLexerMeta(type):
+ """
+ Metaclass for RegexLexer, creates the self._tokens attribute from
+ self.tokens on the first instantiation.
+ """
+
+ def _process_state(cls, state):
+ assert type(state) is str, "wrong state name %r" % state
+ assert state[0] != '#', "invalid state name %r" % state
+ if state in cls._tokens:
+ return cls._tokens[state]
+ tokens = cls._tokens[state] = []
+ rflags = cls.flags
+ for tdef in cls.tokens[state]:
+ if isinstance(tdef, include):
+ # it's a state reference
+ assert tdef != state, "circular state reference %r" % state
+ tokens.extend(cls._process_state(str(tdef)))
+ continue
+
+ assert type(tdef) is tuple, "wrong rule def %r" % tdef
+
+ rex = re.compile(tdef[0], rflags)
+
+ assert type(tdef[1]) is _TokenType or callable(tdef[1]), \
+ 'token type must be simple type or callable, not %r' % tdef[1]
+
+ if len(tdef) == 2:
+ new_state = None
+ else:
+ tdef2 = tdef[2]
+ if isinstance(tdef2, str):
+ # an existing state
+ if tdef2 == '#pop':
+ new_state = -1
+ elif tdef2 in cls.tokens:
+ new_state = (tdef2,)
+ elif tdef2 == '#push':
+ new_state = tdef2
+ elif tdef2[:5] == '#pop:':
+ new_state = -int(tdef2[5:])
+ else:
+ assert False, 'unknown new state %r' % tdef2
+ elif isinstance(tdef2, combined):
+ # combine a new state from existing ones
+ new_state = '_tmp_%d' % cls._tmpname
+ cls._tmpname += 1
+ itokens = []
+ for istate in tdef2:
+ assert istate != state, 'circular state ref %r' % istate
+ itokens.extend(cls._process_state(istate))
+ cls._tokens[new_state] = itokens
+ new_state = (new_state,)
+ elif isinstance(tdef2, tuple):
+ # push more than one state
+ for state in tdef2:
+ assert state in cls.tokens, \
+ 'unknown new state ' + state
+ new_state = tdef2
+ else:
+ assert False, 'unknown new state def %r' % tdef2
+ tokens.append((rex, tdef[1], new_state))
+ return tokens
+
+ def __call__(cls, *args, **kwds):
+ if not hasattr(cls, '_tokens'):
+ cls._tokens = {}
+ cls._tmpname = 0
+ for state in cls.tokens.keys():
+ cls._process_state(state)
+
+ return type.__call__(cls, *args, **kwds)
+
+
+class RegexLexer(Lexer):
+ """
+ Base for simple stateful regular expression-based lexers.
+ Simplifies the lexing process so that you need only
+ provide a list of states and regular expressions.
+ """
+ __metaclass__ = RegexLexerMeta
+
+ #: Flags for compiling the regular expressions.
+ #: Defaults to MULTILINE.
+ flags = re.MULTILINE
+
+ #: Dict of ``{'state': [(regex, tokentype, new_state), ...], ...}``
+ #:
+ #: The initial state is 'root'.
+ #: ``new_state`` can be omitted to signify no state transition.
+ #: If it is a string, the state is pushed on the stack and changed.
+ #: If it is a tuple of strings, all states are pushed on the stack and
+ #: the current state will be the topmost.
+ #: It can also be ``combined('state1', 'state2', ...)``
+ #: to signify a new, anonymous state combined from the rules of two
+ #: or more existing ones.
+ #: Furthermore, it can be '#pop' to signify going back one step in
+ #: the state stack, or '#push' to push the current state on the stack
+ #: again.
+ #:
+ #: The tuple can also be replaced with ``include('state')``, in which
+ #: case the rules from the state named by the string are included in the
+ #: current one.
+ tokens = {}
+
+ def get_tokens_unprocessed(self, text, stack=['root']):
+ """
+ Split ``text`` into (tokentype, text) pairs.
+
+ ``stack`` is the inital stack (default: ``['root']``)
+ """
+ pos = 0
+ statestack = stack[:]
+ statetokens = self._tokens[statestack[-1]]
+ while 1:
+ for rex, action, new_state in statetokens:
+ m = rex.match(text, pos)
+ if m:
+ if type(action) is _TokenType:
+ yield pos, action, m.group()
+ else:
+ for item in action(self, m):
+ yield item
+ pos = m.end()
+ if new_state is not None:
+ # state transition
+ if isinstance(new_state, tuple):
+ statestack.extend(new_state)
+ elif isinstance(new_state, int):
+ # pop
+ del statestack[new_state:]
+ elif new_state == '#push':
+ statestack.append(statestack[-1])
+ else:
+ assert False, "wrong state def: %r" % new_state
+ statetokens = self._tokens[statestack[-1]]
+ break
+ else:
+ try:
+ if text[pos] == '\n':
+ # at EOL, reset state to "root"
+ pos += 1
+ statestack = ['root']
+ statetokens = self._tokens['root']
+ yield pos, Text, '\n'
+ continue
+ yield pos, Error, text[pos]
+ pos += 1
+ except IndexError:
+ break
+
+
+class LexerContext(object):
+ """
+ A helper object that holds lexer position data.
+ """
+
+ def __init__(self, text, pos, stack=None, end=None):
+ self.text = text
+ self.pos = pos
+ self.end = end or len(text) # end=0 not supported ;-)
+ self.stack = stack or ['root']
+
+ def __repr__(self):
+ return 'LexerContext(%r, %r, %r)' % (
+ self.text, self.pos, self.stack)
+
+
+class ExtendedRegexLexer(RegexLexer):
+ """
+ A RegexLexer which additionally allows functions to be
+ specified as "token types", in which case the function will
+ be called.
+ """
+
+ def get_tokens_unprocessed(self, text=None, context=None):
+ """
+ Split ``text`` into (tokentype, text) pairs.
+ If ``context`` is given, use this lexer context instead.
+ """
+ if not context:
+ ctx = LexerContext(text, 0)
+ statetokens = self._tokens['root']
+ else:
+ ctx = context
+ statetokens = self._tokens[ctx.stack[-1]]
+ text = ctx.text
+ while 1:
+ for rex, action, new_state in statetokens:
+ m = rex.match(text, ctx.pos, ctx.end)
+ if m:
+ if type(action) is _TokenType:
+ yield ctx.pos, action, m.group()
+ ctx.pos = m.end()
+ else:
+ for item in action(self, m, ctx):
+ yield item
+ if not new_state:
+ # altered the state stack?
+ statetokens = self._tokens[ctx.stack[-1]]
+ # CAUTION: callback must set ctx.pos!
+ if new_state is not None:
+ # state transition
+ if isinstance(new_state, tuple):
+ ctx.stack.extend(new_state)
+ elif isinstance(new_state, int):
+ # pop
+ del ctx.stack[new_state:]
+ elif new_state == '#push':
+ ctx.stack.append(ctx.stack[-1])
+ else:
+ assert False, "wrong state def: %r" % new_state
+ statetokens = self._tokens[ctx.stack[-1]]
+ break
+ else:
+ try:
+ if ctx.pos >= ctx.end:
+ break
+ if text[ctx.pos] == '\n':
+ # at EOL, reset state to "root"
+ ctx.pos += 1
+ ctx.stack = ['root']
+ statetokens = self._tokens['root']
+ yield ctx.pos, Text, '\n'
+ continue
+ yield ctx.pos, Error, text[ctx.pos]
+ ctx.pos += 1
+ except IndexError:
+ break
+
+
+def do_insertions(insertions, tokens):
+ """
+ Helper for lexers which must combine the results of several
+ sublexers.
+
+ ``insertions`` is a list of ``(index, itokens)`` pairs.
+ Each ``itokens`` iterable should be inserted at position
+ ``index`` into the token stream given by the ``tokens``
+ argument.
+
+ The result is a combined token stream.
+
+ XXX: The indices yielded by this function are not correct!
+ """
+ insertions = iter(insertions)
+ try:
+ index, itokens = insertions.next()
+ except StopIteration:
+ # no insertions
+ for item in tokens:
+ yield item
+ return
+
+ insleft = True
+ for i, t, v in tokens:
+ oldi = 0
+ while insleft and i + len(v) >= index:
+ yield i, t, v[oldi:index-i]
+ for item in itokens:
+ yield item
+ oldi = index-i
+ try:
+ index, itokens = insertions.next()
+ except StopIteration:
+ insleft = False
+ break # not strictly necessary
+ yield i, t, v[oldi:]
diff --git a/pygments/lexers/__init__.py b/pygments/lexers/__init__.py
new file mode 100644
index 00000000..0ef200db
--- /dev/null
+++ b/pygments/lexers/__init__.py
@@ -0,0 +1,77 @@
+# -*- coding: utf-8 -*-
+"""
+ pygments.lexers
+ ~~~~~~~~~~~~~~
+
+ Pygments lexers.
+
+ :copyright: 2006 by Georg Brandl.
+ :license: GNU LGPL, see LICENSE for more details.
+"""
+import fnmatch
+import types
+from os.path import basename
+
+from pygments.lexers._mapping import LEXERS
+
+
+__all__ = ['get_lexer_by_name', 'get_lexer_for_filename'] + LEXERS.keys()
+
+_lexer_cache = {}
+
+
+def _load_lexers(module_name):
+ """
+ Loads a lexer (and all others in the module too)
+ """
+ mod = __import__(module_name, None, None, ['__all__'])
+ for lexer_name in mod.__all__:
+ cls = getattr(mod, lexer_name)
+ _lexer_cache[cls.name] = cls
+
+
+def get_lexer_by_name(alias, **options):
+ """
+ Get a lexer by an alias
+ """
+ for module_name, name, aliases, _ in LEXERS.itervalues():
+ if alias in aliases:
+ if name not in _lexer_cache:
+ _load_lexers(module_name)
+ return _lexer_cache[name](**options)
+ raise ValueError('no lexer for alias %r found' % alias)
+
+
+def get_lexer_for_filename(fn, **options):
+ """
+ Guess a lexer by a filename
+ """
+ fn = basename(fn)
+ for module_name, name, _, filenames in LEXERS.itervalues():
+ for filename in filenames:
+ if fnmatch.fnmatch(fn, filename):
+ if name not in _lexer_cache:
+ _load_lexers(module_name)
+ return _lexer_cache[name](**options)
+ raise ValueError('no lexer for filename %r found' % fn)
+
+
+class _automodule(types.ModuleType):
+
+ def __getattr__(self, name):
+ """Automatically import lexers."""
+ info = LEXERS.get(name)
+ if info:
+ _load_lexers(info[0])
+ cls = _lexer_cache[info[1]]
+ setattr(self, name, cls)
+ return cls
+ raise AttributeError(name)
+
+
+import sys
+oldmod = sys.modules['pygments.lexers']
+newmod = _automodule('pygments.lexers')
+newmod.__dict__.update(oldmod.__dict__)
+sys.modules['pygments.lexers'] = newmod
+del newmod.newmod, newmod.oldmod, newmod.sys, newmod.types
diff --git a/pygments/lexers/_luabuiltins.py b/pygments/lexers/_luabuiltins.py
new file mode 100644
index 00000000..69a87cfc
--- /dev/null
+++ b/pygments/lexers/_luabuiltins.py
@@ -0,0 +1,256 @@
+# -*- coding: utf-8 -*-
+"""
+ pygments.lexers._luabuiltins
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+ This file contains the names and modules of lua functions
+ It is able to re-generate itself, but for adding new functions you
+ probably have to add some callbacks (see function module_callbacks).
+
+ Do not edit the list by hand.
+
+ :copyright: 2006 by Lukas Meuser.
+ :license: GNU LGPL, see LICENSE for more details.
+"""
+
+MODULES = {'basic': ['_G',
+ '_VERSION',
+ 'assert',
+ 'collectgarbage',
+ 'dofile',
+ 'error',
+ 'getfenv',
+ 'getmetatable',
+ 'ipairs',
+ 'load',
+ 'loadfile',
+ 'loadstring',
+ 'next',
+ 'pairs',
+ 'pcall',
+ 'print',
+ 'rawequal',
+ 'rawget',
+ 'rawset',
+ 'select',
+ 'setfenv',
+ 'setmetatable',
+ 'tonumber',
+ 'tostring',
+ 'type',
+ 'unpack',
+ 'xpcall'],
+ 'coroutine': ['coroutine.create',
+ 'coroutine.resume',
+ 'coroutine.running',
+ 'coroutine.status',
+ 'coroutine.wrap',
+ 'coroutine.yield'],
+ 'debug': ['debug.debug',
+ 'debug.getfenv',
+ 'debug.gethook',
+ 'debug.getinfo',
+ 'debug.getlocal',
+ 'debug.getmetatable',
+ 'debug.getregistry',
+ 'debug.getupvalue',
+ 'debug.setfenv',
+ 'debug.sethook',
+ 'debug.setlocal',
+ 'debug.setmetatable',
+ 'debug.setupvalue',
+ 'debug.traceback'],
+ 'io': ['file:close',
+ 'file:flush',
+ 'file:lines',
+ 'file:read',
+ 'file:seek',
+ 'file:setvbuf',
+ 'file:write',
+ 'io.close',
+ 'io.flush',
+ 'io.input',
+ 'io.lines',
+ 'io.open',
+ 'io.output',
+ 'io.popen',
+ 'io.read',
+ 'io.tmpfile',
+ 'io.type',
+ 'io.write'],
+ 'math': ['math.abs',
+ 'math.acos',
+ 'math.asin',
+ 'math.atan2',
+ 'math.atan',
+ 'math.ceil',
+ 'math.cosh',
+ 'math.cos',
+ 'math.deg',
+ 'math.exp',
+ 'math.floor',
+ 'math.fmod',
+ 'math.frexp',
+ 'math.huge',
+ 'math.ldexp',
+ 'math.log10',
+ 'math.log',
+ 'math.max',
+ 'math.min',
+ 'math.modf',
+ 'math.pi',
+ 'math.pow',
+ 'math.rad',
+ 'math.random',
+ 'math.randomseed',
+ 'math.sinh',
+ 'math.sin',
+ 'math.sqrt',
+ 'math.tanh',
+ 'math.tan'],
+ 'modules': ['module',
+ 'require',
+ 'package.cpath',
+ 'package.loaded',
+ 'package.loadlib',
+ 'package.path',
+ 'package.preload',
+ 'package.seeall'],
+ 'os': ['os.clock',
+ 'os.date',
+ 'os.difftime',
+ 'os.execute',
+ 'os.exit',
+ 'os.getenv',
+ 'os.remove',
+ 'os.rename',
+ 'os.setlocale',
+ 'os.time',
+ 'os.tmpname'],
+ 'string': ['string.byte',
+ 'string.char',
+ 'string.dump',
+ 'string.find',
+ 'string.format',
+ 'string.gmatch',
+ 'string.gsub',
+ 'string.len',
+ 'string.lower',
+ 'string.match',
+ 'string.rep',
+ 'string.reverse',
+ 'string.sub',
+ 'string.upper'],
+ 'table': ['table.concat',
+ 'table.insert',
+ 'table.maxn',
+ 'table.remove',
+ 'table.sort']}
+
+if __name__ == '__main__':
+ import re
+ import urllib
+ import pprint
+
+ # you can't generally find out what module a function belongs to if you
+ # have only it's name. Because of this, here are some callback functions
+ # that recognize if a gioven function belongs to a specific module
+ def module_callbacks():
+ def is_in_coroutine_module(name):
+ return name.startswith('coroutine.')
+
+ def is_in_modules_module(name):
+ if name in ['require', 'module'] or name.startswith('package'):
+ return True
+ else:
+ return False
+
+ def is_in_string_module(name):
+ return name.startswith('string.')
+
+ def is_in_table_module(name):
+ return name.startswith('table.')
+
+ def is_in_math_module(name):
+ return name.startswith('math')
+
+ def is_in_io_module(name):
+ return name.startswith('io.') or name.startswith('file:')
+
+ def is_in_os_module(name):
+ return name.startswith('os.')
+
+ def is_in_debug_module(name):
+ return name.startswith('debug.')
+
+ return {'coroutine': is_in_coroutine_module,
+ 'modules': is_in_modules_module,
+ 'string': is_in_string_module,
+ 'table': is_in_table_module,
+ 'math': is_in_math_module,
+ 'io': is_in_io_module,
+ 'os': is_in_os_module,
+ 'debug': is_in_debug_module}
+
+
+
+ def get_newest_version():
+ f = urllib.urlopen('http://www.lua.org/manual/')
+ r = re.compile(r'^<A HREF="(\d\.\d)/">Lua \1</A>')
+ for line in f:
+ m = r.match(line)
+ if m is not None:
+ return m.groups()[0]
+
+ def get_lua_functions(version):
+ f = urllib.urlopen('http://www.lua.org/manual/%s/' % version)
+ r = re.compile(r'^<A HREF="manual.html#pdf-(.+)">\1</A>')
+ functions = []
+ for line in f:
+ m = r.match(line)
+ if m is not None:
+ functions.append(m.groups()[0])
+ return functions
+
+ def get_function_module(name):
+ for mod, cb in module_callbacks().iteritems():
+ if cb(name):
+ return mod
+ if '.' in name:
+ return name.split('.')[0]
+ else:
+ return 'basic'
+
+ def regenerate(filename, modules):
+ f = file(filename)
+ try:
+ content = f.read()
+ finally:
+ f.close()
+
+ header = content[:content.find('MODULES = {')]
+ footer = content[content.find("if __name__ == '__main__':"):]
+
+
+ f = file(filename, 'w')
+ f.write(header)
+ f.write('MODULES = %s\n\n' % pprint.pformat(modules))
+ f.write(footer)
+ f.close()
+
+ def run():
+ version = get_newest_version()
+ print '> Downloading function index for Lua %s' % version
+ functions = get_lua_functions(version)
+ print '> %d functions found:' % len(functions)
+
+ modules = {}
+ for full_function_name in functions:
+ print '>> %s' % full_function_name
+ m = get_function_module(full_function_name)
+ modules.setdefault(m, []).append(full_function_name)
+
+ regenerate(__file__, modules)
+
+
+ run()
diff --git a/pygments/lexers/_mapping.py b/pygments/lexers/_mapping.py
new file mode 100644
index 00000000..ef80adc6
--- /dev/null
+++ b/pygments/lexers/_mapping.py
@@ -0,0 +1,102 @@
+# -*- coding: utf-8 -*-
+"""
+ pygments.lexers._mapping
+ ~~~~~~~~~~~~~~~~~~~~~~~
+
+ Lexer mapping defintions. This file is generated by itself. Everytime
+ you change something on a builtin lexer defintion, run this script from
+ the lexers folder to update it.
+
+ Do not alter this file by hand!
+
+ :copyright: 2006 by Armin Ronacher, Georg Brandl.
+ :license: GNU LGPL, see LICENSE for more details.
+"""
+
+LEXERS = {
+ 'BooLexer': ('pygments.lexers.dotnet', 'Boo', ('boo',), ('*.boo',)),
+ 'BrainfuckLexer': ('pygments.lexers.other', 'Brainfuck', ('brainfuck',), ('*.bf', '*.b')),
+ 'CLexer': ('pygments.lexers.compiled', 'C', ('c',), ('*.c', '*.h')),
+ 'CSharpLexer': ('pygments.lexers.dotnet', 'C#', ('csharp', 'c#'), ('*.cs',)),
+ 'CppLexer': ('pygments.lexers.compiled', 'C++', ('cpp', 'c++'), ('*.cpp', '*.hpp', '*.c++', '*.h++')),
+ 'CssDjangoLexer': ('pygments.lexers.templates', 'CSS+Django', ('css+django',), ()),
+ 'CssErbLexer': ('pygments.lexers.templates', 'CSS+Ruby', ('css+erb', 'css+ruby'), ()),
+ 'CssLexer': ('pygments.lexers.web', 'CSS', ('css',), ('*.css',)),
+ 'CssPhpLexer': ('pygments.lexers.templates', 'CSS+PHP', ('css+php',), ()),
+ 'CssSmartyLexer': ('pygments.lexers.templates', 'CSS+Smarty', ('css+smarty',), ()),
+ 'DelphiLexer': ('pygments.lexers.compiled', 'Delphi', ('delphi', 'pas', 'pascal', 'objectpascal'), ('*.pas',)),
+ 'DiffLexer': ('pygments.lexers.text', 'Diff', ('diff',), ('*.diff', '*.patch')),
+ 'DjangoLexer': ('pygments.lexers.templates', 'django template', ('django',), ()),
+ 'ErbLexer': ('pygments.lexers.templates', 'ERB', ('erb',), ()),
+ 'HtmlDjangoLexer': ('pygments.lexers.templates', 'HTML+Django', ('html+django',), ()),
+ 'HtmlLexer': ('pygments.lexers.web', 'HTML', ('html',), ('*.html', '*.htm', '*.xhtml')),
+ 'HtmlPhpLexer': ('pygments.lexers.templates', 'HTML+PHP', ('html+php',), ('*.phtml',)),
+ 'HtmlSmartyLexer': ('pygments.lexers.templates', 'HTML+Smarty', ('html+smarty',), ()),
+ 'IniLexer': ('pygments.lexers.text', 'INI', ('ini', 'cfg'), ('*.ini', '*.cfg')),
+ 'IrcLogsLexer': ('pygments.lexers.text', 'IRC logs', ('irc',), ()),
+ 'JavaLexer': ('pygments.lexers.compiled', 'Java', ('java',), ('*.java',)),
+ 'JavascriptDjangoLexer': ('pygments.lexers.templates', 'JavaScript+Django', ('js+django', 'javascript+django'), ()),
+ 'JavascriptErbLexer': ('pygments.lexers.templates', 'JavaScript+Ruby', ('js+erb', 'javascript+erb', 'js+ruby', 'javascript+ruby'), ()),
+ 'JavascriptLexer': ('pygments.lexers.web', 'JavaScript', ('js', 'javascript'), ('*.js',)),
+ 'JavascriptPhpLexer': ('pygments.lexers.templates', 'JavaScript+PHP', ('js+php', 'javascript+php'), ()),
+ 'JavascriptSmartyLexer': ('pygments.lexers.templates', 'JavaScript+Smarty', ('js+smarty', 'javascript+smarty'), ()),
+ 'LuaLexer': ('pygments.lexers.agile', 'Lua', ('lua',), ('*.lua',)),
+ 'MakefileLexer': ('pygments.lexers.text', 'Makefile', ('make', 'makefile', 'mf'), ('*.mak', 'Makefile', 'makefile')),
+ 'PerlLexer': ('pygments.lexers.agile', 'Perl', ('perl', 'pl'), ('*.pl', '*.pm')),
+ 'PhpLexer': ('pygments.lexers.web', 'PHP', ('php', 'php3', 'php4', 'php5'), ('*.php', '*.php[345]')),
+ 'PythonConsoleLexer': ('pygments.lexers.agile', 'Python console session', ('pycon',), ()),
+ 'PythonLexer': ('pygments.lexers.agile', 'Python', ('python', 'py'), ('*.py', '*.pyw')),
+ 'RawTokenLexer': ('pygments.lexers.special', 'Raw token data', ('raw',), ('*.raw',)),
+ 'RhtmlLexer': ('pygments.lexers.templates', 'RHTML', ('rhtml', 'html+erb', 'html+ruby'), ('*.rhtml',)),
+ 'RubyConsoleLexer': ('pygments.lexers.agile', 'Ruby irb session', ('rbcon', 'irb'), ()),
+ 'RubyLexer': ('pygments.lexers.agile', 'Ruby', ('rb', 'ruby'), ('*.rb', '*.rbw', 'Rakefile', '*.rake', '*.gemspec', '*.rbx')),
+ 'SmartyLexer': ('pygments.lexers.templates', 'Smarty', ('smarty',), ()),
+ 'SqlLexer': ('pygments.lexers.other', 'SQL', ('sql',), ('*.sql',)),
+ 'TexLexer': ('pygments.lexers.text', 'TeX', ('tex', 'latex'), ('*.tex', '*.aux', '*.toc')),
+ 'TextLexer': ('pygments.lexers.special', 'Text only', ('text',), ('*.txt',)),
+ 'VbNetLexer': ('pygments.lexers.dotnet', 'VB.net', ('vb.net', 'vbnet'), ('*.vb', '*.bas')),
+ 'XmlDjangoLexer': ('pygments.lexers.templates', 'XML+Django', ('xml+django',), ()),
+ 'XmlErbLexer': ('pygments.lexers.templates', 'XML+Ruby', ('xml+erb', 'xml+ruby'), ()),
+ 'XmlLexer': ('pygments.lexers.web', 'XML', ('xml',), ('*.xml',)),
+ 'XmlPhpLexer': ('pygments.lexers.templates', 'XML+PHP', ('xml+php',), ()),
+ 'XmlSmartyLexer': ('pygments.lexers.templates', 'XML+Smarty', ('xml+smarty',), ())
+}
+
+if __name__ == '__main__':
+ import sys
+ import os
+
+ # lookup lexers
+ found_lexers = []
+ sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..', '..'))
+ for filename in os.listdir('.'):
+ if filename.endswith('.py') and not filename.startswith('_'):
+ module_name = 'pygments.lexers.%s' % filename[:-3]
+ print module_name
+ module = __import__(module_name, None, None, [''])
+ for lexer_name in module.__all__:
+ lexer = getattr(module, lexer_name)
+ found_lexers.append(
+ '%r: %r' % (lexer_name,
+ (module_name,
+ lexer.name,
+ tuple(lexer.aliases),
+ tuple(lexer.filenames))))
+ # sort them, that should make the diff files for svn smaller
+ found_lexers.sort()
+
+ # extract useful sourcecode from this file
+ f = file(__file__)
+ try:
+ content = f.read()
+ finally:
+ f.close()
+ header = content[:content.find('LEXERS = {')]
+ footer = content[content.find("if __name__ == '__main__':"):]
+
+ # write new file
+ f = file(__file__, 'w')
+ f.write(header)
+ f.write('LEXERS = {\n %s\n}\n\n' % ',\n '.join(found_lexers))
+ f.write(footer)
+ f.close()
diff --git a/pygments/lexers/_phpbuiltins.py b/pygments/lexers/_phpbuiltins.py
new file mode 100644
index 00000000..ca6076c5
--- /dev/null
+++ b/pygments/lexers/_phpbuiltins.py
@@ -0,0 +1,3391 @@
+# -*- coding: utf-8 -*-
+"""
+ pygments.lexers._phpbuiltins
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+ This file loads the function names and their modules from the
+ php webpage and generates itself.
+
+ Do not alter this file by hand!
+
+ WARNING: the generation transfers quite much data over your
+ internet connection. don't run that at home, use
+ a server ;-)
+
+ :copyright: 2006 by Armin Ronacher.
+ :license: GNU LGPL, see LICENSE for more details.
+"""
+
+
+MODULES = {'.NET': ['dotnet_load'],
+ 'APD': ['apd_breakpoint',
+ 'apd_callstack',
+ 'apd_clunk',
+ 'apd_continue',
+ 'apd_croak',
+ 'apd_dump_function_table',
+ 'apd_dump_persistent_resources',
+ 'apd_dump_regular_resources',
+ 'apd_echo',
+ 'apd_get_active_symbols',
+ 'apd_set_pprof_trace',
+ 'apd_set_session',
+ 'apd_set_session_trace',
+ 'apd_set_socket_session_trace',
+ 'override_function',
+ 'rename_function'],
+ 'Apache': ['apache_child_terminate',
+ 'apache_get_modules',
+ 'apache_get_version',
+ 'apache_getenv',
+ 'apache_lookup_uri',
+ 'apache_note',
+ 'apache_request_headers',
+ 'apache_reset_timeout',
+ 'apache_response_headers',
+ 'apache_setenv',
+ 'ascii2ebcdic',
+ 'ebcdic2ascii',
+ 'getallheaders',
+ 'virtual'],
+ 'Arrays': ['array',
+ 'array_change_key_case',
+ 'array_chunk',
+ 'array_combine',
+ 'array_count_values',
+ 'array_diff',
+ 'array_diff_assoc',
+ 'array_diff_key',
+ 'array_diff_uassoc',
+ 'array_diff_ukey',
+ 'array_fill',
+ 'array_filter',
+ 'array_flip',
+ 'array_intersect',
+ 'array_intersect_assoc',
+ 'array_intersect_key',
+ 'array_intersect_uassoc',
+ 'array_intersect_ukey',
+ 'array_key_exists',
+ 'array_keys',
+ 'array_map',
+ 'array_merge',
+ 'array_merge_recursive',
+ 'array_multisort',
+ 'array_pad',
+ 'array_pop',
+ 'array_push',
+ 'array_rand',
+ 'array_reduce',
+ 'array_reverse',
+ 'array_search',
+ 'array_shift',
+ 'array_slice',
+ 'array_splice',
+ 'array_sum',
+ 'array_udiff',
+ 'array_udiff_assoc',
+ 'array_udiff_uassoc',
+ 'array_uintersect',
+ 'array_uintersect_assoc',
+ 'array_uintersect_uassoc',
+ 'array_unique',
+ 'array_unshift',
+ 'array_values',
+ 'array_walk',
+ 'array_walk_recursive',
+ 'arsort',
+ 'asort',
+ 'compact',
+ 'count',
+ 'current',
+ 'each',
+ 'end',
+ 'extract',
+ 'in_array',
+ 'key',
+ 'krsort',
+ 'ksort',
+ 'list',
+ 'natcasesort',
+ 'natsort',
+ 'next',
+ 'pos',
+ 'prev',
+ 'range',
+ 'reset',
+ 'rsort',
+ 'shuffle',
+ 'sizeof',
+ 'sort',
+ 'uasort',
+ 'uksort',
+ 'usort'],
+ 'Aspell': ['aspell_check',
+ 'aspell_check_raw',
+ 'aspell_new',
+ 'aspell_suggest'],
+ 'BC math': ['bcadd',
+ 'bccomp',
+ 'bcdiv',
+ 'bcmod',
+ 'bcmul',
+ 'bcpow',
+ 'bcpowmod',
+ 'bcscale',
+ 'bcsqrt',
+ 'bcsub'],
+ 'Bzip2': ['bzclose',
+ 'bzcompress',
+ 'bzdecompress',
+ 'bzerrno',
+ 'bzerror',
+ 'bzerrstr',
+ 'bzflush',
+ 'bzopen',
+ 'bzread',
+ 'bzwrite'],
+ 'CCVS': ['ccvs_add',
+ 'ccvs_auth',
+ 'ccvs_command',
+ 'ccvs_count',
+ 'ccvs_delete',
+ 'ccvs_done',
+ 'ccvs_init',
+ 'ccvs_lookup',
+ 'ccvs_new',
+ 'ccvs_report',
+ 'ccvs_return',
+ 'ccvs_reverse',
+ 'ccvs_sale',
+ 'ccvs_status',
+ 'ccvs_textvalue',
+ 'ccvs_void'],
+ 'COM': ['com_addref',
+ 'com_create_guid',
+ 'com_event_sink',
+ 'com_get',
+ 'com_get_active_object',
+ 'com_invoke',
+ 'com_isenum',
+ 'com_load',
+ 'com_load_typelib',
+ 'com_message_pump',
+ 'com_print_typeinfo',
+ 'com_propget',
+ 'com_propput',
+ 'com_propset',
+ 'com_release',
+ 'com_set',
+ 'variant_abs',
+ 'variant_add',
+ 'variant_and',
+ 'variant_cast',
+ 'variant_cat',
+ 'variant_cmp',
+ 'variant_date_from_timestamp',
+ 'variant_date_to_timestamp',
+ 'variant_div',
+ 'variant_eqv',
+ 'variant_fix',
+ 'variant_get_type',
+ 'variant_idiv',
+ 'variant_imp',
+ 'variant_int',
+ 'variant_mod',
+ 'variant_mul',
+ 'variant_neg',
+ 'variant_not',
+ 'variant_or',
+ 'variant_pow',
+ 'variant_round',
+ 'variant_set',
+ 'variant_set_type',
+ 'variant_sub',
+ 'variant_xor'],
+ 'CURL': ['curl_close',
+ 'curl_copy_handle',
+ 'curl_errno',
+ 'curl_error',
+ 'curl_exec',
+ 'curl_getinfo',
+ 'curl_init',
+ 'curl_multi_add_handle',
+ 'curl_multi_close',
+ 'curl_multi_exec',
+ 'curl_multi_getcontent',
+ 'curl_multi_info_read',
+ 'curl_multi_init',
+ 'curl_multi_remove_handle',
+ 'curl_multi_select',
+ 'curl_setopt',
+ 'curl_version'],
+ 'Calendar': ['cal_days_in_month',
+ 'cal_from_jd',
+ 'cal_info',
+ 'cal_to_jd',
+ 'easter_date',
+ 'easter_days',
+ 'frenchtojd',
+ 'gregoriantojd',
+ 'jddayofweek',
+ 'jdmonthname',
+ 'jdtofrench',
+ 'jdtogregorian',
+ 'jdtojewish',
+ 'jdtojulian',
+ 'jdtounix',
+ 'jewishtojd',
+ 'juliantojd',
+ 'unixtojd'],
+ 'Classes/Objects': ['call_user_method',
+ 'call_user_method_array',
+ 'class_exists',
+ 'get_class',
+ 'get_class_methods',
+ 'get_class_vars',
+ 'get_declared_classes',
+ 'get_declared_interfaces',
+ 'get_object_vars',
+ 'get_parent_class',
+ 'interface_exists',
+ 'is_a',
+ 'is_subclass_of',
+ 'method_exists'],
+ 'Classkit': ['classkit_import',
+ 'classkit_method_add',
+ 'classkit_method_copy',
+ 'classkit_method_redefine',
+ 'classkit_method_remove',
+ 'classkit_method_rename'],
+ 'ClibPDF': ['cpdf_add_annotation',
+ 'cpdf_add_outline',
+ 'cpdf_arc',
+ 'cpdf_begin_text',
+ 'cpdf_circle',
+ 'cpdf_clip',
+ 'cpdf_close',
+ 'cpdf_closepath',
+ 'cpdf_closepath_fill_stroke',
+ 'cpdf_closepath_stroke',
+ 'cpdf_continue_text',
+ 'cpdf_curveto',
+ 'cpdf_end_text',
+ 'cpdf_fill',
+ 'cpdf_fill_stroke',
+ 'cpdf_finalize',
+ 'cpdf_finalize_page',
+ 'cpdf_global_set_document_limits',
+ 'cpdf_import_jpeg',
+ 'cpdf_lineto',
+ 'cpdf_moveto',
+ 'cpdf_newpath',
+ 'cpdf_open',
+ 'cpdf_output_buffer',
+ 'cpdf_page_init',
+ 'cpdf_place_inline_image',
+ 'cpdf_rect',
+ 'cpdf_restore',
+ 'cpdf_rlineto',
+ 'cpdf_rmoveto',
+ 'cpdf_rotate',
+ 'cpdf_rotate_text',
+ 'cpdf_save',
+ 'cpdf_save_to_file',
+ 'cpdf_scale',
+ 'cpdf_set_action_url',
+ 'cpdf_set_char_spacing',
+ 'cpdf_set_creator',
+ 'cpdf_set_current_page',
+ 'cpdf_set_font',
+ 'cpdf_set_font_directories',
+ 'cpdf_set_font_map_file',
+ 'cpdf_set_horiz_scaling',
+ 'cpdf_set_keywords',
+ 'cpdf_set_leading',
+ 'cpdf_set_page_animation',
+ 'cpdf_set_subject',
+ 'cpdf_set_text_matrix',
+ 'cpdf_set_text_pos',
+ 'cpdf_set_text_rendering',
+ 'cpdf_set_text_rise',
+ 'cpdf_set_title',
+ 'cpdf_set_viewer_preferences',
+ 'cpdf_set_word_spacing',
+ 'cpdf_setdash',
+ 'cpdf_setflat',
+ 'cpdf_setgray',
+ 'cpdf_setgray_fill',
+ 'cpdf_setgray_stroke',
+ 'cpdf_setlinecap',
+ 'cpdf_setlinejoin',
+ 'cpdf_setlinewidth',
+ 'cpdf_setmiterlimit',
+ 'cpdf_setrgbcolor',
+ 'cpdf_setrgbcolor_fill',
+ 'cpdf_setrgbcolor_stroke',
+ 'cpdf_show',
+ 'cpdf_show_xy',
+ 'cpdf_stringwidth',
+ 'cpdf_stroke',
+ 'cpdf_text',
+ 'cpdf_translate'],
+ 'Crack': ['crack_check',
+ 'crack_closedict',
+ 'crack_getlastmessage',
+ 'crack_opendict'],
+ 'Cybercash': ['cybercash_base64_decode',
+ 'cybercash_base64_encode',
+ 'cybercash_decr',
+ 'cybercash_encr'],
+ 'Cyrus IMAP': ['cyrus_authenticate',
+ 'cyrus_bind',
+ 'cyrus_close',
+ 'cyrus_connect',
+ 'cyrus_query',
+ 'cyrus_unbind'],
+ 'DB++': ['dbplus_add',
+ 'dbplus_aql',
+ 'dbplus_chdir',
+ 'dbplus_close',
+ 'dbplus_curr',
+ 'dbplus_errcode',
+ 'dbplus_errno',
+ 'dbplus_find',
+ 'dbplus_first',
+ 'dbplus_flush',
+ 'dbplus_freealllocks',
+ 'dbplus_freelock',
+ 'dbplus_freerlocks',
+ 'dbplus_getlock',
+ 'dbplus_getunique',
+ 'dbplus_info',
+ 'dbplus_last',
+ 'dbplus_lockrel',
+ 'dbplus_next',
+ 'dbplus_open',
+ 'dbplus_prev',
+ 'dbplus_rchperm',
+ 'dbplus_rcreate',
+ 'dbplus_rcrtexact',
+ 'dbplus_rcrtlike',
+ 'dbplus_resolve',
+ 'dbplus_restorepos',
+ 'dbplus_rkeys',
+ 'dbplus_ropen',
+ 'dbplus_rquery',
+ 'dbplus_rrename',
+ 'dbplus_rsecindex',
+ 'dbplus_runlink',
+ 'dbplus_rzap',
+ 'dbplus_savepos',
+ 'dbplus_setindex',
+ 'dbplus_setindexbynumber',
+ 'dbplus_sql',
+ 'dbplus_tcl',
+ 'dbplus_tremove',
+ 'dbplus_undo',
+ 'dbplus_undoprepare',
+ 'dbplus_unlockrel',
+ 'dbplus_unselect',
+ 'dbplus_update',
+ 'dbplus_xlockrel',
+ 'dbplus_xunlockrel'],
+ 'DBM': ['dblist',
+ 'dbmclose',
+ 'dbmdelete',
+ 'dbmexists',
+ 'dbmfetch',
+ 'dbmfirstkey',
+ 'dbminsert',
+ 'dbmnextkey',
+ 'dbmopen',
+ 'dbmreplace'],
+ 'DOM': ['dom_import_simplexml'],
+ 'DOM XML': ['domxml_new_doc',
+ 'domxml_open_file',
+ 'domxml_open_mem',
+ 'domxml_version',
+ 'domxml_xmltree',
+ 'domxml_xslt_stylesheet',
+ 'domxml_xslt_stylesheet_doc',
+ 'domxml_xslt_stylesheet_file',
+ 'xpath_eval',
+ 'xpath_eval_expression',
+ 'xpath_new_context',
+ 'xptr_eval',
+ 'xptr_new_context'],
+ 'Date/Time': ['checkdate',
+ 'date',
+ 'date_sunrise',
+ 'date_sunset',
+ 'getdate',
+ 'gettimeofday',
+ 'gmdate',
+ 'gmmktime',
+ 'gmstrftime',
+ 'idate',
+ 'localtime',
+ 'microtime',
+ 'mktime',
+ 'strftime',
+ 'strptime',
+ 'strtotime',
+ 'time'],
+ 'Direct IO': ['dio_close',
+ 'dio_fcntl',
+ 'dio_open',
+ 'dio_read',
+ 'dio_seek',
+ 'dio_stat',
+ 'dio_tcsetattr',
+ 'dio_truncate',
+ 'dio_write'],
+ 'Directories': ['chdir',
+ 'chroot',
+ 'closedir',
+ 'getcwd',
+ 'opendir',
+ 'readdir',
+ 'rewinddir',
+ 'scandir'],
+ 'Errors and Logging': ['debug_backtrace',
+ 'debug_print_backtrace',
+ 'error_log',
+ 'error_reporting',
+ 'restore_error_handler',
+ 'restore_exception_handler',
+ 'set_error_handler',
+ 'set_exception_handler',
+ 'trigger_error',
+ 'user_error'],
+ 'Exif': ['exif_imagetype',
+ 'exif_read_data',
+ 'exif_tagname',
+ 'exif_thumbnail',
+ 'read_exif_data'],
+ 'FDF': ['fdf_add_doc_javascript',
+ 'fdf_add_template',
+ 'fdf_close',
+ 'fdf_create',
+ 'fdf_enum_values',
+ 'fdf_errno',
+ 'fdf_error',
+ 'fdf_get_ap',
+ 'fdf_get_attachment',
+ 'fdf_get_encoding',
+ 'fdf_get_file',
+ 'fdf_get_flags',
+ 'fdf_get_opt',
+ 'fdf_get_status',
+ 'fdf_get_value',
+ 'fdf_get_version',
+ 'fdf_header',
+ 'fdf_next_field_name',
+ 'fdf_open',
+ 'fdf_open_string',
+ 'fdf_remove_item',
+ 'fdf_save',
+ 'fdf_save_string',
+ 'fdf_set_ap',
+ 'fdf_set_encoding',
+ 'fdf_set_file',
+ 'fdf_set_flags',
+ 'fdf_set_javascript_action',
+ 'fdf_set_on_import_javascript',
+ 'fdf_set_opt',
+ 'fdf_set_status',
+ 'fdf_set_submit_form_action',
+ 'fdf_set_target_frame',
+ 'fdf_set_value',
+ 'fdf_set_version'],
+ 'FTP': ['ftp_alloc',
+ 'ftp_cdup',
+ 'ftp_chdir',
+ 'ftp_chmod',
+ 'ftp_close',
+ 'ftp_connect',
+ 'ftp_delete',
+ 'ftp_exec',
+ 'ftp_fget',
+ 'ftp_fput',
+ 'ftp_get',
+ 'ftp_get_option',
+ 'ftp_login',
+ 'ftp_mdtm',
+ 'ftp_mkdir',
+ 'ftp_nb_continue',
+ 'ftp_nb_fget',
+ 'ftp_nb_fput',
+ 'ftp_nb_get',
+ 'ftp_nb_put',
+ 'ftp_nlist',
+ 'ftp_pasv',
+ 'ftp_put',
+ 'ftp_pwd',
+ 'ftp_quit',
+ 'ftp_raw',
+ 'ftp_rawlist',
+ 'ftp_rename',
+ 'ftp_rmdir',
+ 'ftp_set_option',
+ 'ftp_site',
+ 'ftp_size',
+ 'ftp_ssl_connect',
+ 'ftp_systype'],
+ 'Filesystem': ['basename',
+ 'chgrp',
+ 'chmod',
+ 'chown',
+ 'clearstatcache',
+ 'copy',
+ 'delete',
+ 'dirname',
+ 'disk_free_space',
+ 'disk_total_space',
+ 'diskfreespace',
+ 'fclose',
+ 'feof',
+ 'fflush',
+ 'fgetc',
+ 'fgetcsv',
+ 'fgets',
+ 'fgetss',
+ 'file',
+ 'file_exists',
+ 'file_get_contents',
+ 'file_put_contents',
+ 'fileatime',
+ 'filectime',
+ 'filegroup',
+ 'fileinode',
+ 'filemtime',
+ 'fileowner',
+ 'fileperms',
+ 'filesize',
+ 'filetype',
+ 'flock',
+ 'fnmatch',
+ 'fopen',
+ 'fpassthru',
+ 'fputcsv',
+ 'fputs',
+ 'fread',
+ 'fscanf',
+ 'fseek',
+ 'fstat',
+ 'ftell',
+ 'ftruncate',
+ 'fwrite',
+ 'glob',
+ 'is_dir',
+ 'is_executable',
+ 'is_file',
+ 'is_link',
+ 'is_readable',
+ 'is_uploaded_file',
+ 'is_writable',
+ 'is_writeable',
+ 'link',
+ 'linkinfo',
+ 'lstat',
+ 'mkdir',
+ 'move_uploaded_file',
+ 'parse_ini_file',
+ 'pathinfo',
+ 'pclose',
+ 'popen',
+ 'readfile',
+ 'readlink',
+ 'realpath',
+ 'rename',
+ 'rewind',
+ 'rmdir',
+ 'set_file_buffer',
+ 'stat',
+ 'symlink',
+ 'tempnam',
+ 'tmpfile',
+ 'touch',
+ 'umask',
+ 'unlink'],
+ 'Firebird/InterBase': ['ibase_add_user',
+ 'ibase_affected_rows',
+ 'ibase_backup',
+ 'ibase_blob_add',
+ 'ibase_blob_cancel',
+ 'ibase_blob_close',
+ 'ibase_blob_create',
+ 'ibase_blob_echo',
+ 'ibase_blob_get',
+ 'ibase_blob_import',
+ 'ibase_blob_info',
+ 'ibase_blob_open',
+ 'ibase_close',
+ 'ibase_commit',
+ 'ibase_commit_ret',
+ 'ibase_connect',
+ 'ibase_db_info',
+ 'ibase_delete_user',
+ 'ibase_drop_db',
+ 'ibase_errcode',
+ 'ibase_errmsg',
+ 'ibase_execute',
+ 'ibase_fetch_assoc',
+ 'ibase_fetch_object',
+ 'ibase_fetch_row',
+ 'ibase_field_info',
+ 'ibase_free_event_handler',
+ 'ibase_free_query',
+ 'ibase_free_result',
+ 'ibase_gen_id',
+ 'ibase_maintain_db',
+ 'ibase_modify_user',
+ 'ibase_name_result',
+ 'ibase_num_fields',
+ 'ibase_num_params',
+ 'ibase_param_info',
+ 'ibase_pconnect',
+ 'ibase_prepare',
+ 'ibase_query',
+ 'ibase_restore',
+ 'ibase_rollback',
+ 'ibase_rollback_ret',
+ 'ibase_server_info',
+ 'ibase_service_attach',
+ 'ibase_service_detach',
+ 'ibase_set_event_handler',
+ 'ibase_timefmt',
+ 'ibase_trans',
+ 'ibase_wait_event'],
+ 'FriBiDi': ['fribidi_log2vis'],
+ 'FrontBase': ['fbsql_affected_rows',
+ 'fbsql_autocommit',
+ 'fbsql_blob_size',
+ 'fbsql_change_user',
+ 'fbsql_clob_size',
+ 'fbsql_close',
+ 'fbsql_commit',
+ 'fbsql_connect',
+ 'fbsql_create_blob',
+ 'fbsql_create_clob',
+ 'fbsql_create_db',
+ 'fbsql_data_seek',
+ 'fbsql_database',
+ 'fbsql_database_password',
+ 'fbsql_db_query',
+ 'fbsql_db_status',
+ 'fbsql_drop_db',
+ 'fbsql_errno',
+ 'fbsql_error',
+ 'fbsql_fetch_array',
+ 'fbsql_fetch_assoc',
+ 'fbsql_fetch_field',
+ 'fbsql_fetch_lengths',
+ 'fbsql_fetch_object',
+ 'fbsql_fetch_row',
+ 'fbsql_field_flags',
+ 'fbsql_field_len',
+ 'fbsql_field_name',
+ 'fbsql_field_seek',
+ 'fbsql_field_table',
+ 'fbsql_field_type',
+ 'fbsql_free_result',
+ 'fbsql_get_autostart_info',
+ 'fbsql_hostname',
+ 'fbsql_insert_id',
+ 'fbsql_list_dbs',
+ 'fbsql_list_fields',
+ 'fbsql_list_tables',
+ 'fbsql_next_result',
+ 'fbsql_num_fields',
+ 'fbsql_num_rows',
+ 'fbsql_password',
+ 'fbsql_pconnect',
+ 'fbsql_query',
+ 'fbsql_read_blob',
+ 'fbsql_read_clob',
+ 'fbsql_result',
+ 'fbsql_rollback',
+ 'fbsql_select_db',
+ 'fbsql_set_lob_mode',
+ 'fbsql_set_password',
+ 'fbsql_set_transaction',
+ 'fbsql_start_db',
+ 'fbsql_stop_db',
+ 'fbsql_tablename',
+ 'fbsql_username',
+ 'fbsql_warnings'],
+ 'Function handling': ['call_user_func',
+ 'call_user_func_array',
+ 'create_function',
+ 'func_get_arg',
+ 'func_get_args',
+ 'func_num_args',
+ 'function_exists',
+ 'get_defined_functions',
+ 'register_shutdown_function',
+ 'register_tick_function',
+ 'unregister_tick_function'],
+ 'GMP': ['gmp_abs',
+ 'gmp_add',
+ 'gmp_and',
+ 'gmp_clrbit',
+ 'gmp_cmp',
+ 'gmp_com',
+ 'gmp_div',
+ 'gmp_div_q',
+ 'gmp_div_qr',
+ 'gmp_div_r',
+ 'gmp_divexact',
+ 'gmp_fact',
+ 'gmp_gcd',
+ 'gmp_gcdext',
+ 'gmp_hamdist',
+ 'gmp_init',
+ 'gmp_intval',
+ 'gmp_invert',
+ 'gmp_jacobi',
+ 'gmp_legendre',
+ 'gmp_mod',
+ 'gmp_mul',
+ 'gmp_neg',
+ 'gmp_or',
+ 'gmp_perfect_square',
+ 'gmp_popcount',
+ 'gmp_pow',
+ 'gmp_powm',
+ 'gmp_prob_prime',
+ 'gmp_random',
+ 'gmp_scan0',
+ 'gmp_scan1',
+ 'gmp_setbit',
+ 'gmp_sign',
+ 'gmp_sqrt',
+ 'gmp_sqrtrem',
+ 'gmp_strval',
+ 'gmp_sub',
+ 'gmp_xor'],
+ 'Hyperwave': ['hw_array2objrec',
+ 'hw_changeobject',
+ 'hw_children',
+ 'hw_childrenobj',
+ 'hw_close',
+ 'hw_connect',
+ 'hw_connection_info',
+ 'hw_cp',
+ 'hw_deleteobject',
+ 'hw_docbyanchor',
+ 'hw_docbyanchorobj',
+ 'hw_document_attributes',
+ 'hw_document_bodytag',
+ 'hw_document_content',
+ 'hw_document_setcontent',
+ 'hw_document_size',
+ 'hw_dummy',
+ 'hw_edittext',
+ 'hw_error',
+ 'hw_errormsg',
+ 'hw_free_document',
+ 'hw_getanchors',
+ 'hw_getanchorsobj',
+ 'hw_getandlock',
+ 'hw_getchildcoll',
+ 'hw_getchildcollobj',
+ 'hw_getchilddoccoll',
+ 'hw_getchilddoccollobj',
+ 'hw_getobject',
+ 'hw_getobjectbyquery',
+ 'hw_getobjectbyquerycoll',
+ 'hw_getobjectbyquerycollobj',
+ 'hw_getobjectbyqueryobj',
+ 'hw_getparents',
+ 'hw_getparentsobj',
+ 'hw_getrellink',
+ 'hw_getremote',
+ 'hw_getremotechildren',
+ 'hw_getsrcbydestobj',
+ 'hw_gettext',
+ 'hw_getusername',
+ 'hw_identify',
+ 'hw_incollections',
+ 'hw_info',
+ 'hw_inscoll',
+ 'hw_insdoc',
+ 'hw_insertanchors',
+ 'hw_insertdocument',
+ 'hw_insertobject',
+ 'hw_mapid',
+ 'hw_modifyobject',
+ 'hw_mv',
+ 'hw_new_document',
+ 'hw_objrec2array',
+ 'hw_output_document',
+ 'hw_pconnect',
+ 'hw_pipedocument',
+ 'hw_root',
+ 'hw_setlinkroot',
+ 'hw_stat',
+ 'hw_unlock',
+ 'hw_who'],
+ 'Hyperwave API': ['hwapi_hgcsp'],
+ 'IMAP': ['imap_8bit',
+ 'imap_alerts',
+ 'imap_append',
+ 'imap_base64',
+ 'imap_binary',
+ 'imap_body',
+ 'imap_bodystruct',
+ 'imap_check',
+ 'imap_clearflag_full',
+ 'imap_close',
+ 'imap_createmailbox',
+ 'imap_delete',
+ 'imap_deletemailbox',
+ 'imap_errors',
+ 'imap_expunge',
+ 'imap_fetch_overview',
+ 'imap_fetchbody',
+ 'imap_fetchheader',
+ 'imap_fetchstructure',
+ 'imap_get_quota',
+ 'imap_get_quotaroot',
+ 'imap_getacl',
+ 'imap_getmailboxes',
+ 'imap_getsubscribed',
+ 'imap_header',
+ 'imap_headerinfo',
+ 'imap_headers',
+ 'imap_last_error',
+ 'imap_list',
+ 'imap_listmailbox',
+ 'imap_listscan',
+ 'imap_listsubscribed',
+ 'imap_lsub',
+ 'imap_mail',
+ 'imap_mail_compose',
+ 'imap_mail_copy',
+ 'imap_mail_move',
+ 'imap_mailboxmsginfo',
+ 'imap_mime_header_decode',
+ 'imap_msgno',
+ 'imap_num_msg',
+ 'imap_num_recent',
+ 'imap_open',
+ 'imap_ping',
+ 'imap_qprint',
+ 'imap_renamemailbox',
+ 'imap_reopen',
+ 'imap_rfc822_parse_adrlist',
+ 'imap_rfc822_parse_headers',
+ 'imap_rfc822_write_address',
+ 'imap_scanmailbox',
+ 'imap_search',
+ 'imap_set_quota',
+ 'imap_setacl',
+ 'imap_setflag_full',
+ 'imap_sort',
+ 'imap_status',
+ 'imap_subscribe',
+ 'imap_thread',
+ 'imap_timeout',
+ 'imap_uid',
+ 'imap_undelete',
+ 'imap_unsubscribe',
+ 'imap_utf7_decode',
+ 'imap_utf7_encode',
+ 'imap_utf8'],
+ 'IRC Gateway': ['ircg_channel_mode',
+ 'ircg_disconnect',
+ 'ircg_eval_ecmascript_params',
+ 'ircg_fetch_error_msg',
+ 'ircg_get_username',
+ 'ircg_html_encode',
+ 'ircg_ignore_add',
+ 'ircg_ignore_del',
+ 'ircg_invite',
+ 'ircg_is_conn_alive',
+ 'ircg_join',
+ 'ircg_kick',
+ 'ircg_list',
+ 'ircg_lookup_format_messages',
+ 'ircg_lusers',
+ 'ircg_msg',
+ 'ircg_names',
+ 'ircg_nick',
+ 'ircg_nickname_escape',
+ 'ircg_nickname_unescape',
+ 'ircg_notice',
+ 'ircg_oper',
+ 'ircg_part',
+ 'ircg_pconnect',
+ 'ircg_register_format_messages',
+ 'ircg_set_current',
+ 'ircg_set_file',
+ 'ircg_set_on_die',
+ 'ircg_topic',
+ 'ircg_who',
+ 'ircg_whois'],
+ 'Image': ['gd_info',
+ 'getimagesize',
+ 'image2wbmp',
+ 'image_type_to_extension',
+ 'image_type_to_mime_type',
+ 'imagealphablending',
+ 'imageantialias',
+ 'imagearc',
+ 'imagechar',
+ 'imagecharup',
+ 'imagecolorallocate',
+ 'imagecolorallocatealpha',
+ 'imagecolorat',
+ 'imagecolorclosest',
+ 'imagecolorclosestalpha',
+ 'imagecolorclosesthwb',
+ 'imagecolordeallocate',
+ 'imagecolorexact',
+ 'imagecolorexactalpha',
+ 'imagecolormatch',
+ 'imagecolorresolve',
+ 'imagecolorresolvealpha',
+ 'imagecolorset',
+ 'imagecolorsforindex',
+ 'imagecolorstotal',
+ 'imagecolortransparent',
+ 'imagecopy',
+ 'imagecopymerge',
+ 'imagecopymergegray',
+ 'imagecopyresampled',
+ 'imagecopyresized',
+ 'imagecreate',
+ 'imagecreatefromgd',
+ 'imagecreatefromgd2',
+ 'imagecreatefromgd2part',
+ 'imagecreatefromgif',
+ 'imagecreatefromjpeg',
+ 'imagecreatefrompng',
+ 'imagecreatefromstring',
+ 'imagecreatefromwbmp',
+ 'imagecreatefromxbm',
+ 'imagecreatefromxpm',
+ 'imagecreatetruecolor',
+ 'imagedashedline',
+ 'imagedestroy',
+ 'imageellipse',
+ 'imagefill',
+ 'imagefilledarc',
+ 'imagefilledellipse',
+ 'imagefilledpolygon',
+ 'imagefilledrectangle',
+ 'imagefilltoborder',
+ 'imagefilter',
+ 'imagefontheight',
+ 'imagefontwidth',
+ 'imageftbbox',
+ 'imagefttext',
+ 'imagegammacorrect',
+ 'imagegd',
+ 'imagegd2',
+ 'imagegif',
+ 'imageinterlace',
+ 'imageistruecolor',
+ 'imagejpeg',
+ 'imagelayereffect',
+ 'imageline',
+ 'imageloadfont',
+ 'imagepalettecopy',
+ 'imagepng',
+ 'imagepolygon',
+ 'imagepsbbox',
+ 'imagepsencodefont',
+ 'imagepsextendfont',
+ 'imagepsfreefont',
+ 'imagepsloadfont',
+ 'imagepsslantfont',
+ 'imagepstext',
+ 'imagerectangle',
+ 'imagerotate',
+ 'imagesavealpha',
+ 'imagesetbrush',
+ 'imagesetpixel',
+ 'imagesetstyle',
+ 'imagesetthickness',
+ 'imagesettile',
+ 'imagestring',
+ 'imagestringup',
+ 'imagesx',
+ 'imagesy',
+ 'imagetruecolortopalette',
+ 'imagettfbbox',
+ 'imagettftext',
+ 'imagetypes',
+ 'imagewbmp',
+ 'imagexbm',
+ 'iptcembed',
+ 'iptcparse',
+ 'jpeg2wbmp',
+ 'png2wbmp'],
+ 'Informix': ['ifx_affected_rows',
+ 'ifx_blobinfile_mode',
+ 'ifx_byteasvarchar',
+ 'ifx_close',
+ 'ifx_connect',
+ 'ifx_copy_blob',
+ 'ifx_create_blob',
+ 'ifx_create_char',
+ 'ifx_do',
+ 'ifx_error',
+ 'ifx_errormsg',
+ 'ifx_fetch_row',
+ 'ifx_fieldproperties',
+ 'ifx_fieldtypes',
+ 'ifx_free_blob',
+ 'ifx_free_char',
+ 'ifx_free_result',
+ 'ifx_get_blob',
+ 'ifx_get_char',
+ 'ifx_getsqlca',
+ 'ifx_htmltbl_result',
+ 'ifx_nullformat',
+ 'ifx_num_fields',
+ 'ifx_num_rows',
+ 'ifx_pconnect',
+ 'ifx_prepare',
+ 'ifx_query',
+ 'ifx_textasvarchar',
+ 'ifx_update_blob',
+ 'ifx_update_char',
+ 'ifxus_close_slob',
+ 'ifxus_create_slob',
+ 'ifxus_free_slob',
+ 'ifxus_open_slob',
+ 'ifxus_read_slob',
+ 'ifxus_seek_slob',
+ 'ifxus_tell_slob',
+ 'ifxus_write_slob'],
+ 'Ingres II': ['ingres_autocommit',
+ 'ingres_close',
+ 'ingres_commit',
+ 'ingres_connect',
+ 'ingres_fetch_array',
+ 'ingres_fetch_object',
+ 'ingres_fetch_row',
+ 'ingres_field_length',
+ 'ingres_field_name',
+ 'ingres_field_nullable',
+ 'ingres_field_precision',
+ 'ingres_field_scale',
+ 'ingres_field_type',
+ 'ingres_num_fields',
+ 'ingres_num_rows',
+ 'ingres_pconnect',
+ 'ingres_query',
+ 'ingres_rollback'],
+ 'Java': ['java_last_exception_clear', 'java_last_exception_get'],
+ 'LDAP': ['ldap_8859_to_t61',
+ 'ldap_add',
+ 'ldap_bind',
+ 'ldap_close',
+ 'ldap_compare',
+ 'ldap_connect',
+ 'ldap_count_entries',
+ 'ldap_delete',
+ 'ldap_dn2ufn',
+ 'ldap_err2str',
+ 'ldap_errno',
+ 'ldap_error',
+ 'ldap_explode_dn',
+ 'ldap_first_attribute',
+ 'ldap_first_entry',
+ 'ldap_first_reference',
+ 'ldap_free_result',
+ 'ldap_get_attributes',
+ 'ldap_get_dn',
+ 'ldap_get_entries',
+ 'ldap_get_option',
+ 'ldap_get_values',
+ 'ldap_get_values_len',
+ 'ldap_list',
+ 'ldap_mod_add',
+ 'ldap_mod_del',
+ 'ldap_mod_replace',
+ 'ldap_modify',
+ 'ldap_next_attribute',
+ 'ldap_next_entry',
+ 'ldap_next_reference',
+ 'ldap_parse_reference',
+ 'ldap_parse_result',
+ 'ldap_read',
+ 'ldap_rename',
+ 'ldap_sasl_bind',
+ 'ldap_search',
+ 'ldap_set_option',
+ 'ldap_set_rebind_proc',
+ 'ldap_sort',
+ 'ldap_start_tls',
+ 'ldap_t61_to_8859',
+ 'ldap_unbind'],
+ 'LZF': ['lzf_compress', 'lzf_decompress', 'lzf_optimized_for'],
+ 'Lotus Notes': ['notes_body',
+ 'notes_copy_db',
+ 'notes_create_db',
+ 'notes_create_note',
+ 'notes_drop_db',
+ 'notes_find_note',
+ 'notes_header_info',
+ 'notes_list_msgs',
+ 'notes_mark_read',
+ 'notes_mark_unread',
+ 'notes_nav_create',
+ 'notes_search',
+ 'notes_unread',
+ 'notes_version'],
+ 'MCAL': ['mcal_append_event',
+ 'mcal_close',
+ 'mcal_create_calendar',
+ 'mcal_date_compare',
+ 'mcal_date_valid',
+ 'mcal_day_of_week',
+ 'mcal_day_of_year',
+ 'mcal_days_in_month',
+ 'mcal_delete_calendar',
+ 'mcal_delete_event',
+ 'mcal_event_add_attribute',
+ 'mcal_event_init',
+ 'mcal_event_set_alarm',
+ 'mcal_event_set_category',
+ 'mcal_event_set_class',
+ 'mcal_event_set_description',
+ 'mcal_event_set_end',
+ 'mcal_event_set_recur_daily',
+ 'mcal_event_set_recur_monthly_mday',
+ 'mcal_event_set_recur_monthly_wday',
+ 'mcal_event_set_recur_none',
+ 'mcal_event_set_recur_weekly',
+ 'mcal_event_set_recur_yearly',
+ 'mcal_event_set_start',
+ 'mcal_event_set_title',
+ 'mcal_expunge',
+ 'mcal_fetch_current_stream_event',
+ 'mcal_fetch_event',
+ 'mcal_is_leap_year',
+ 'mcal_list_alarms',
+ 'mcal_list_events',
+ 'mcal_next_recurrence',
+ 'mcal_open',
+ 'mcal_popen',
+ 'mcal_rename_calendar',
+ 'mcal_reopen',
+ 'mcal_snooze',
+ 'mcal_store_event',
+ 'mcal_time_valid',
+ 'mcal_week_of_year'],
+ 'MS SQL Server': ['mssql_bind',
+ 'mssql_close',
+ 'mssql_connect',
+ 'mssql_data_seek',
+ 'mssql_execute',
+ 'mssql_fetch_array',
+ 'mssql_fetch_assoc',
+ 'mssql_fetch_batch',
+ 'mssql_fetch_field',
+ 'mssql_fetch_object',
+ 'mssql_fetch_row',
+ 'mssql_field_length',
+ 'mssql_field_name',
+ 'mssql_field_seek',
+ 'mssql_field_type',
+ 'mssql_free_result',
+ 'mssql_free_statement',
+ 'mssql_get_last_message',
+ 'mssql_guid_string',
+ 'mssql_init',
+ 'mssql_min_error_severity',
+ 'mssql_min_message_severity',
+ 'mssql_next_result',
+ 'mssql_num_fields',
+ 'mssql_num_rows',
+ 'mssql_pconnect',
+ 'mssql_query',
+ 'mssql_result',
+ 'mssql_rows_affected',
+ 'mssql_select_db'],
+ 'Mail': ['ezmlm_hash', 'mail'],
+ 'Math': ['abs',
+ 'acos',
+ 'acosh',
+ 'asin',
+ 'asinh',
+ 'atan',
+ 'atan2',
+ 'atanh',
+ 'base_convert',
+ 'bindec',
+ 'ceil',
+ 'cos',
+ 'cosh',
+ 'decbin',
+ 'dechex',
+ 'decoct',
+ 'deg2rad',
+ 'exp',
+ 'expm1',
+ 'floor',
+ 'fmod',
+ 'getrandmax',
+ 'hexdec',
+ 'hypot',
+ 'is_finite',
+ 'is_infinite',
+ 'is_nan',
+ 'lcg_value',
+ 'log',
+ 'log10',
+ 'log1p',
+ 'max',
+ 'min',
+ 'mt_getrandmax',
+ 'mt_rand',
+ 'mt_srand',
+ 'octdec',
+ 'pi',
+ 'pow',
+ 'rad2deg',
+ 'rand',
+ 'round',
+ 'sin',
+ 'sinh',
+ 'sqrt',
+ 'srand',
+ 'tan',
+ 'tanh'],
+ 'Memcache': ['memcache_debug'],
+ 'Mimetype': ['mime_content_type'],
+ 'Ming (flash)': ['ming_setcubicthreshold',
+ 'ming_setscale',
+ 'ming_useswfversion',
+ 'swfaction',
+ 'swfbitmap',
+ 'swfbutton',
+ 'swffill',
+ 'swffont',
+ 'swfgradient',
+ 'swfmorph',
+ 'swfmovie',
+ 'swfshape',
+ 'swfsprite',
+ 'swftext',
+ 'swftextfield'],
+ 'Misc.': ['connection_aborted',
+ 'connection_status',
+ 'connection_timeout',
+ 'constant',
+ 'define',
+ 'defined',
+ 'die',
+ 'eval',
+ 'exit',
+ 'get_browser',
+ 'highlight_file',
+ 'highlight_string',
+ 'ignore_user_abort',
+ 'pack',
+ 'php_check_syntax',
+ 'php_strip_whitespace',
+ 'show_source',
+ 'sleep',
+ 'time_nanosleep',
+ 'uniqid',
+ 'unpack',
+ 'usleep'],
+ 'Msession': ['msession_connect',
+ 'msession_count',
+ 'msession_create',
+ 'msession_destroy',
+ 'msession_disconnect',
+ 'msession_find',
+ 'msession_get',
+ 'msession_get_array',
+ 'msession_get_data',
+ 'msession_inc',
+ 'msession_list',
+ 'msession_listvar',
+ 'msession_lock',
+ 'msession_plugin',
+ 'msession_randstr',
+ 'msession_set',
+ 'msession_set_array',
+ 'msession_set_data',
+ 'msession_timeout',
+ 'msession_uniq',
+ 'msession_unlock'],
+ 'Multibyte String': ['mb_convert_case',
+ 'mb_convert_encoding',
+ 'mb_convert_kana',
+ 'mb_convert_variables',
+ 'mb_decode_mimeheader',
+ 'mb_decode_numericentity',
+ 'mb_detect_encoding',
+ 'mb_detect_order',
+ 'mb_encode_mimeheader',
+ 'mb_encode_numericentity',
+ 'mb_ereg',
+ 'mb_ereg_match',
+ 'mb_ereg_replace',
+ 'mb_ereg_search',
+ 'mb_ereg_search_getpos',
+ 'mb_ereg_search_getregs',
+ 'mb_ereg_search_init',
+ 'mb_ereg_search_pos',
+ 'mb_ereg_search_regs',
+ 'mb_ereg_search_setpos',
+ 'mb_eregi',
+ 'mb_eregi_replace',
+ 'mb_get_info',
+ 'mb_http_input',
+ 'mb_http_output',
+ 'mb_internal_encoding',
+ 'mb_language',
+ 'mb_list_encodings',
+ 'mb_output_handler',
+ 'mb_parse_str',
+ 'mb_preferred_mime_name',
+ 'mb_regex_encoding',
+ 'mb_regex_set_options',
+ 'mb_send_mail',
+ 'mb_split',
+ 'mb_strcut',
+ 'mb_strimwidth',
+ 'mb_strlen',
+ 'mb_strpos',
+ 'mb_strrpos',
+ 'mb_strtolower',
+ 'mb_strtoupper',
+ 'mb_strwidth',
+ 'mb_substitute_character',
+ 'mb_substr',
+ 'mb_substr_count'],
+ 'MySQL': ['mysql_affected_rows',
+ 'mysql_change_user',
+ 'mysql_client_encoding',
+ 'mysql_close',
+ 'mysql_connect',
+ 'mysql_create_db',
+ 'mysql_data_seek',
+ 'mysql_db_name',
+ 'mysql_db_query',
+ 'mysql_drop_db',
+ 'mysql_errno',
+ 'mysql_error',
+ 'mysql_escape_string',
+ 'mysql_fetch_array',
+ 'mysql_fetch_assoc',
+ 'mysql_fetch_field',
+ 'mysql_fetch_lengths',
+ 'mysql_fetch_object',
+ 'mysql_fetch_row',
+ 'mysql_field_flags',
+ 'mysql_field_len',
+ 'mysql_field_name',
+ 'mysql_field_seek',
+ 'mysql_field_table',
+ 'mysql_field_type',
+ 'mysql_free_result',
+ 'mysql_get_client_info',
+ 'mysql_get_host_info',
+ 'mysql_get_proto_info',
+ 'mysql_get_server_info',
+ 'mysql_info',
+ 'mysql_insert_id',
+ 'mysql_list_dbs',
+ 'mysql_list_fields',
+ 'mysql_list_processes',
+ 'mysql_list_tables',
+ 'mysql_num_fields',
+ 'mysql_num_rows',
+ 'mysql_pconnect',
+ 'mysql_ping',
+ 'mysql_query',
+ 'mysql_real_escape_string',
+ 'mysql_result',
+ 'mysql_select_db',
+ 'mysql_stat',
+ 'mysql_tablename',
+ 'mysql_thread_id',
+ 'mysql_unbuffered_query'],
+ 'NSAPI': ['nsapi_request_headers', 'nsapi_response_headers', 'nsapi_virtual'],
+ 'Ncurses': ['ncurses_addch',
+ 'ncurses_addchnstr',
+ 'ncurses_addchstr',
+ 'ncurses_addnstr',
+ 'ncurses_addstr',
+ 'ncurses_assume_default_colors',
+ 'ncurses_attroff',
+ 'ncurses_attron',
+ 'ncurses_attrset',
+ 'ncurses_baudrate',
+ 'ncurses_beep',
+ 'ncurses_bkgd',
+ 'ncurses_bkgdset',
+ 'ncurses_border',
+ 'ncurses_bottom_panel',
+ 'ncurses_can_change_color',
+ 'ncurses_cbreak',
+ 'ncurses_clear',
+ 'ncurses_clrtobot',
+ 'ncurses_clrtoeol',
+ 'ncurses_color_content',
+ 'ncurses_color_set',
+ 'ncurses_curs_set',
+ 'ncurses_def_prog_mode',
+ 'ncurses_def_shell_mode',
+ 'ncurses_define_key',
+ 'ncurses_del_panel',
+ 'ncurses_delay_output',
+ 'ncurses_delch',
+ 'ncurses_deleteln',
+ 'ncurses_delwin',
+ 'ncurses_doupdate',
+ 'ncurses_echo',
+ 'ncurses_echochar',
+ 'ncurses_end',
+ 'ncurses_erase',
+ 'ncurses_erasechar',
+ 'ncurses_filter',
+ 'ncurses_flash',
+ 'ncurses_flushinp',
+ 'ncurses_getch',
+ 'ncurses_getmaxyx',
+ 'ncurses_getmouse',
+ 'ncurses_getyx',
+ 'ncurses_halfdelay',
+ 'ncurses_has_colors',
+ 'ncurses_has_ic',
+ 'ncurses_has_il',
+ 'ncurses_has_key',
+ 'ncurses_hide_panel',
+ 'ncurses_hline',
+ 'ncurses_inch',
+ 'ncurses_init',
+ 'ncurses_init_color',
+ 'ncurses_init_pair',
+ 'ncurses_insch',
+ 'ncurses_insdelln',
+ 'ncurses_insertln',
+ 'ncurses_insstr',
+ 'ncurses_instr',
+ 'ncurses_isendwin',
+ 'ncurses_keyok',
+ 'ncurses_keypad',
+ 'ncurses_killchar',
+ 'ncurses_longname',
+ 'ncurses_meta',
+ 'ncurses_mouse_trafo',
+ 'ncurses_mouseinterval',
+ 'ncurses_mousemask',
+ 'ncurses_move',
+ 'ncurses_move_panel',
+ 'ncurses_mvaddch',
+ 'ncurses_mvaddchnstr',
+ 'ncurses_mvaddchstr',
+ 'ncurses_mvaddnstr',
+ 'ncurses_mvaddstr',
+ 'ncurses_mvcur',
+ 'ncurses_mvdelch',
+ 'ncurses_mvgetch',
+ 'ncurses_mvhline',
+ 'ncurses_mvinch',
+ 'ncurses_mvvline',
+ 'ncurses_mvwaddstr',
+ 'ncurses_napms',
+ 'ncurses_new_panel',
+ 'ncurses_newpad',
+ 'ncurses_newwin',
+ 'ncurses_nl',
+ 'ncurses_nocbreak',
+ 'ncurses_noecho',
+ 'ncurses_nonl',
+ 'ncurses_noqiflush',
+ 'ncurses_noraw',
+ 'ncurses_pair_content',
+ 'ncurses_panel_above',
+ 'ncurses_panel_below',
+ 'ncurses_panel_window',
+ 'ncurses_pnoutrefresh',
+ 'ncurses_prefresh',
+ 'ncurses_putp',
+ 'ncurses_qiflush',
+ 'ncurses_raw',
+ 'ncurses_refresh',
+ 'ncurses_replace_panel',
+ 'ncurses_reset_prog_mode',
+ 'ncurses_reset_shell_mode',
+ 'ncurses_resetty',
+ 'ncurses_savetty',
+ 'ncurses_scr_dump',
+ 'ncurses_scr_init',
+ 'ncurses_scr_restore',
+ 'ncurses_scr_set',
+ 'ncurses_scrl',
+ 'ncurses_show_panel',
+ 'ncurses_slk_attr',
+ 'ncurses_slk_attroff',
+ 'ncurses_slk_attron',
+ 'ncurses_slk_attrset',
+ 'ncurses_slk_clear',
+ 'ncurses_slk_color',
+ 'ncurses_slk_init',
+ 'ncurses_slk_noutrefresh',
+ 'ncurses_slk_refresh',
+ 'ncurses_slk_restore',
+ 'ncurses_slk_set',
+ 'ncurses_slk_touch',
+ 'ncurses_standend',
+ 'ncurses_standout',
+ 'ncurses_start_color',
+ 'ncurses_termattrs',
+ 'ncurses_termname',
+ 'ncurses_timeout',
+ 'ncurses_top_panel',
+ 'ncurses_typeahead',
+ 'ncurses_ungetch',
+ 'ncurses_ungetmouse',
+ 'ncurses_update_panels',
+ 'ncurses_use_default_colors',
+ 'ncurses_use_env',
+ 'ncurses_use_extended_names',
+ 'ncurses_vidattr',
+ 'ncurses_vline',
+ 'ncurses_waddch',
+ 'ncurses_waddstr',
+ 'ncurses_wattroff',
+ 'ncurses_wattron',
+ 'ncurses_wattrset',
+ 'ncurses_wborder',
+ 'ncurses_wclear',
+ 'ncurses_wcolor_set',
+ 'ncurses_werase',
+ 'ncurses_wgetch',
+ 'ncurses_whline',
+ 'ncurses_wmouse_trafo',
+ 'ncurses_wmove',
+ 'ncurses_wnoutrefresh',
+ 'ncurses_wrefresh',
+ 'ncurses_wstandend',
+ 'ncurses_wstandout',
+ 'ncurses_wvline'],
+ 'Network': ['checkdnsrr',
+ 'closelog',
+ 'debugger_off',
+ 'debugger_on',
+ 'define_syslog_variables',
+ 'dns_check_record',
+ 'dns_get_mx',
+ 'dns_get_record',
+ 'fsockopen',
+ 'gethostbyaddr',
+ 'gethostbyname',
+ 'gethostbynamel',
+ 'getmxrr',
+ 'getprotobyname',
+ 'getprotobynumber',
+ 'getservbyname',
+ 'getservbyport',
+ 'header',
+ 'headers_list',
+ 'headers_sent',
+ 'inet_ntop',
+ 'inet_pton',
+ 'ip2long',
+ 'long2ip',
+ 'openlog',
+ 'pfsockopen',
+ 'setcookie',
+ 'setrawcookie',
+ 'socket_get_status',
+ 'socket_set_blocking',
+ 'socket_set_timeout',
+ 'syslog'],
+ 'OCI8': ['oci_bind_by_name',
+ 'oci_cancel',
+ 'oci_close',
+ 'oci_commit',
+ 'oci_connect',
+ 'oci_define_by_name',
+ 'oci_error',
+ 'oci_execute',
+ 'oci_fetch',
+ 'oci_fetch_all',
+ 'oci_fetch_array',
+ 'oci_fetch_assoc',
+ 'oci_fetch_object',
+ 'oci_fetch_row',
+ 'oci_field_is_null',
+ 'oci_field_name',
+ 'oci_field_precision',
+ 'oci_field_scale',
+ 'oci_field_size',
+ 'oci_field_type',
+ 'oci_field_type_raw',
+ 'oci_free_statement',
+ 'oci_internal_debug',
+ 'oci_lob_copy',
+ 'oci_lob_is_equal',
+ 'oci_new_collection',
+ 'oci_new_connect',
+ 'oci_new_cursor',
+ 'oci_new_descriptor',
+ 'oci_num_fields',
+ 'oci_num_rows',
+ 'oci_parse',
+ 'oci_password_change',
+ 'oci_pconnect',
+ 'oci_result',
+ 'oci_rollback',
+ 'oci_server_version',
+ 'oci_set_prefetch',
+ 'oci_statement_type',
+ 'ocibindbyname',
+ 'ocicancel',
+ 'ocicloselob',
+ 'ocicollappend',
+ 'ocicollassign',
+ 'ocicollassignelem',
+ 'ocicollgetelem',
+ 'ocicollmax',
+ 'ocicollsize',
+ 'ocicolltrim',
+ 'ocicolumnisnull',
+ 'ocicolumnname',
+ 'ocicolumnprecision',
+ 'ocicolumnscale',
+ 'ocicolumnsize',
+ 'ocicolumntype',
+ 'ocicolumntyperaw',
+ 'ocicommit',
+ 'ocidefinebyname',
+ 'ocierror',
+ 'ociexecute',
+ 'ocifetch',
+ 'ocifetchinto',
+ 'ocifetchstatement',
+ 'ocifreecollection',
+ 'ocifreecursor',
+ 'ocifreedesc',
+ 'ocifreestatement',
+ 'ociinternaldebug',
+ 'ociloadlob',
+ 'ocilogoff',
+ 'ocilogon',
+ 'ocinewcollection',
+ 'ocinewcursor',
+ 'ocinewdescriptor',
+ 'ocinlogon',
+ 'ocinumcols',
+ 'ociparse',
+ 'ociplogon',
+ 'ociresult',
+ 'ocirollback',
+ 'ocirowcount',
+ 'ocisavelob',
+ 'ocisavelobfile',
+ 'ociserverversion',
+ 'ocisetprefetch',
+ 'ocistatementtype',
+ 'ociwritelobtofile',
+ 'ociwritetemporarylob'],
+ 'ODBC': ['odbc_autocommit',
+ 'odbc_binmode',
+ 'odbc_close',
+ 'odbc_close_all',
+ 'odbc_columnprivileges',
+ 'odbc_columns',
+ 'odbc_commit',
+ 'odbc_connect',
+ 'odbc_cursor',
+ 'odbc_data_source',
+ 'odbc_do',
+ 'odbc_error',
+ 'odbc_errormsg',
+ 'odbc_exec',
+ 'odbc_execute',
+ 'odbc_fetch_array',
+ 'odbc_fetch_into',
+ 'odbc_fetch_object',
+ 'odbc_fetch_row',
+ 'odbc_field_len',
+ 'odbc_field_name',
+ 'odbc_field_num',
+ 'odbc_field_precision',
+ 'odbc_field_scale',
+ 'odbc_field_type',
+ 'odbc_foreignkeys',
+ 'odbc_free_result',
+ 'odbc_gettypeinfo',
+ 'odbc_longreadlen',
+ 'odbc_next_result',
+ 'odbc_num_fields',
+ 'odbc_num_rows',
+ 'odbc_pconnect',
+ 'odbc_prepare',
+ 'odbc_primarykeys',
+ 'odbc_procedurecolumns',
+ 'odbc_procedures',
+ 'odbc_result',
+ 'odbc_result_all',
+ 'odbc_rollback',
+ 'odbc_setoption',
+ 'odbc_specialcolumns',
+ 'odbc_statistics',
+ 'odbc_tableprivileges',
+ 'odbc_tables'],
+ 'Object Aggregation': ['aggregate',
+ 'aggregate_info',
+ 'aggregate_methods',
+ 'aggregate_methods_by_list',
+ 'aggregate_methods_by_regexp',
+ 'aggregate_properties',
+ 'aggregate_properties_by_list',
+ 'aggregate_properties_by_regexp',
+ 'aggregation_info',
+ 'deaggregate'],
+ 'Object overloading': ['overload'],
+ 'OpenSSL': ['openssl_csr_export',
+ 'openssl_csr_export_to_file',
+ 'openssl_csr_new',
+ 'openssl_csr_sign',
+ 'openssl_error_string',
+ 'openssl_free_key',
+ 'openssl_get_privatekey',
+ 'openssl_get_publickey',
+ 'openssl_open',
+ 'openssl_pkcs7_decrypt',
+ 'openssl_pkcs7_encrypt',
+ 'openssl_pkcs7_sign',
+ 'openssl_pkcs7_verify',
+ 'openssl_pkey_export',
+ 'openssl_pkey_export_to_file',
+ 'openssl_pkey_get_private',
+ 'openssl_pkey_get_public',
+ 'openssl_pkey_new',
+ 'openssl_private_decrypt',
+ 'openssl_private_encrypt',
+ 'openssl_public_decrypt',
+ 'openssl_public_encrypt',
+ 'openssl_seal',
+ 'openssl_sign',
+ 'openssl_verify',
+ 'openssl_x509_check_private_key',
+ 'openssl_x509_checkpurpose',
+ 'openssl_x509_export',
+ 'openssl_x509_export_to_file',
+ 'openssl_x509_free',
+ 'openssl_x509_parse',
+ 'openssl_x509_read'],
+ 'Oracle': ['ora_bind',
+ 'ora_close',
+ 'ora_columnname',
+ 'ora_columnsize',
+ 'ora_columntype',
+ 'ora_commit',
+ 'ora_commitoff',
+ 'ora_commiton',
+ 'ora_do',
+ 'ora_error',
+ 'ora_errorcode',
+ 'ora_exec',
+ 'ora_fetch',
+ 'ora_fetch_into',
+ 'ora_getcolumn',
+ 'ora_logoff',
+ 'ora_logon',
+ 'ora_numcols',
+ 'ora_numrows',
+ 'ora_open',
+ 'ora_parse',
+ 'ora_plogon',
+ 'ora_rollback'],
+ 'Output Control': ['flush',
+ 'ob_clean',
+ 'ob_end_clean',
+ 'ob_end_flush',
+ 'ob_flush',
+ 'ob_get_clean',
+ 'ob_get_contents',
+ 'ob_get_flush',
+ 'ob_get_length',
+ 'ob_get_level',
+ 'ob_get_status',
+ 'ob_gzhandler',
+ 'ob_implicit_flush',
+ 'ob_list_handlers',
+ 'ob_start',
+ 'output_add_rewrite_var',
+ 'output_reset_rewrite_vars'],
+ 'OvrimosSQL': ['ovrimos_close',
+ 'ovrimos_commit',
+ 'ovrimos_connect',
+ 'ovrimos_cursor',
+ 'ovrimos_exec',
+ 'ovrimos_execute',
+ 'ovrimos_fetch_into',
+ 'ovrimos_fetch_row',
+ 'ovrimos_field_len',
+ 'ovrimos_field_name',
+ 'ovrimos_field_num',
+ 'ovrimos_field_type',
+ 'ovrimos_free_result',
+ 'ovrimos_longreadlen',
+ 'ovrimos_num_fields',
+ 'ovrimos_num_rows',
+ 'ovrimos_prepare',
+ 'ovrimos_result',
+ 'ovrimos_result_all',
+ 'ovrimos_rollback'],
+ 'PCNTL': ['pcntl_alarm',
+ 'pcntl_exec',
+ 'pcntl_fork',
+ 'pcntl_getpriority',
+ 'pcntl_setpriority',
+ 'pcntl_signal',
+ 'pcntl_wait',
+ 'pcntl_waitpid',
+ 'pcntl_wexitstatus',
+ 'pcntl_wifexited',
+ 'pcntl_wifsignaled',
+ 'pcntl_wifstopped',
+ 'pcntl_wstopsig',
+ 'pcntl_wtermsig'],
+ 'PCRE': ['preg_grep',
+ 'preg_match',
+ 'preg_match_all',
+ 'preg_quote',
+ 'preg_replace',
+ 'preg_replace_callback',
+ 'preg_split'],
+ 'PDF': ['pdf_add_annotation',
+ 'pdf_add_bookmark',
+ 'pdf_add_launchlink',
+ 'pdf_add_locallink',
+ 'pdf_add_note',
+ 'pdf_add_outline',
+ 'pdf_add_pdflink',
+ 'pdf_add_thumbnail',
+ 'pdf_add_weblink',
+ 'pdf_arc',
+ 'pdf_arcn',
+ 'pdf_attach_file',
+ 'pdf_begin_page',
+ 'pdf_begin_pattern',
+ 'pdf_begin_template',
+ 'pdf_circle',
+ 'pdf_clip',
+ 'pdf_close',
+ 'pdf_close_image',
+ 'pdf_close_pdi',
+ 'pdf_close_pdi_page',
+ 'pdf_closepath',
+ 'pdf_closepath_fill_stroke',
+ 'pdf_closepath_stroke',
+ 'pdf_concat',
+ 'pdf_continue_text',
+ 'pdf_curveto',
+ 'pdf_delete',
+ 'pdf_end_page',
+ 'pdf_end_pattern',
+ 'pdf_end_template',
+ 'pdf_endpath',
+ 'pdf_fill',
+ 'pdf_fill_stroke',
+ 'pdf_findfont',
+ 'pdf_get_buffer',
+ 'pdf_get_font',
+ 'pdf_get_fontname',
+ 'pdf_get_fontsize',
+ 'pdf_get_image_height',
+ 'pdf_get_image_width',
+ 'pdf_get_majorversion',
+ 'pdf_get_minorversion',
+ 'pdf_get_parameter',
+ 'pdf_get_pdi_parameter',
+ 'pdf_get_pdi_value',
+ 'pdf_get_value',
+ 'pdf_initgraphics',
+ 'pdf_lineto',
+ 'pdf_makespotcolor',
+ 'pdf_moveto',
+ 'pdf_new',
+ 'pdf_open_ccitt',
+ 'pdf_open_file',
+ 'pdf_open_gif',
+ 'pdf_open_image',
+ 'pdf_open_image_file',
+ 'pdf_open_jpeg',
+ 'pdf_open_memory_image',
+ 'pdf_open_pdi',
+ 'pdf_open_pdi_page',
+ 'pdf_open_tiff',
+ 'pdf_place_image',
+ 'pdf_place_pdi_page',
+ 'pdf_rect',
+ 'pdf_restore',
+ 'pdf_rotate',
+ 'pdf_save',
+ 'pdf_scale',
+ 'pdf_set_border_color',
+ 'pdf_set_border_dash',
+ 'pdf_set_border_style',
+ 'pdf_set_char_spacing',
+ 'pdf_set_duration',
+ 'pdf_set_horiz_scaling',
+ 'pdf_set_info',
+ 'pdf_set_info_author',
+ 'pdf_set_info_creator',
+ 'pdf_set_info_keywords',
+ 'pdf_set_info_subject',
+ 'pdf_set_info_title',
+ 'pdf_set_leading',
+ 'pdf_set_parameter',
+ 'pdf_set_text_matrix',
+ 'pdf_set_text_pos',
+ 'pdf_set_text_rendering',
+ 'pdf_set_text_rise',
+ 'pdf_set_value',
+ 'pdf_set_word_spacing',
+ 'pdf_setcolor',
+ 'pdf_setdash',
+ 'pdf_setflat',
+ 'pdf_setfont',
+ 'pdf_setgray',
+ 'pdf_setgray_fill',
+ 'pdf_setgray_stroke',
+ 'pdf_setlinecap',
+ 'pdf_setlinejoin',
+ 'pdf_setlinewidth',
+ 'pdf_setmatrix',
+ 'pdf_setmiterlimit',
+ 'pdf_setpolydash',
+ 'pdf_setrgbcolor',
+ 'pdf_setrgbcolor_fill',
+ 'pdf_setrgbcolor_stroke',
+ 'pdf_show',
+ 'pdf_show_boxed',
+ 'pdf_show_xy',
+ 'pdf_skew',
+ 'pdf_stringwidth',
+ 'pdf_stroke',
+ 'pdf_translate'],
+ 'PHP Options/Info': ['assert',
+ 'assert_options',
+ 'dl',
+ 'extension_loaded',
+ 'get_cfg_var',
+ 'get_current_user',
+ 'get_defined_constants',
+ 'get_extension_funcs',
+ 'get_include_path',
+ 'get_included_files',
+ 'get_loaded_extensions',
+ 'get_magic_quotes_gpc',
+ 'get_magic_quotes_runtime',
+ 'get_required_files',
+ 'getenv',
+ 'getlastmod',
+ 'getmygid',
+ 'getmyinode',
+ 'getmypid',
+ 'getmyuid',
+ 'getopt',
+ 'getrusage',
+ 'ini_alter',
+ 'ini_get',
+ 'ini_get_all',
+ 'ini_restore',
+ 'ini_set',
+ 'main',
+ 'memory_get_usage',
+ 'php_ini_scanned_files',
+ 'php_logo_guid',
+ 'php_sapi_name',
+ 'php_uname',
+ 'phpcredits',
+ 'phpinfo',
+ 'phpversion',
+ 'putenv',
+ 'restore_include_path',
+ 'set_include_path',
+ 'set_magic_quotes_runtime',
+ 'set_time_limit',
+ 'version_compare',
+ 'zend_logo_guid',
+ 'zend_version'],
+ 'POSIX': ['posix_ctermid',
+ 'posix_get_last_error',
+ 'posix_getcwd',
+ 'posix_getegid',
+ 'posix_geteuid',
+ 'posix_getgid',
+ 'posix_getgrgid',
+ 'posix_getgrnam',
+ 'posix_getgroups',
+ 'posix_getlogin',
+ 'posix_getpgid',
+ 'posix_getpgrp',
+ 'posix_getpid',
+ 'posix_getppid',
+ 'posix_getpwnam',
+ 'posix_getpwuid',
+ 'posix_getrlimit',
+ 'posix_getsid',
+ 'posix_getuid',
+ 'posix_isatty',
+ 'posix_kill',
+ 'posix_mkfifo',
+ 'posix_setegid',
+ 'posix_seteuid',
+ 'posix_setgid',
+ 'posix_setpgid',
+ 'posix_setsid',
+ 'posix_setuid',
+ 'posix_strerror',
+ 'posix_times',
+ 'posix_ttyname',
+ 'posix_uname'],
+ 'POSIX Regex': ['ereg',
+ 'ereg_replace',
+ 'eregi',
+ 'eregi_replace',
+ 'split',
+ 'spliti',
+ 'sql_regcase'],
+ 'Parsekit': ['parsekit_compile_file',
+ 'parsekit_compile_string',
+ 'parsekit_func_arginfo'],
+ 'PostgreSQL': ['pg_affected_rows',
+ 'pg_cancel_query',
+ 'pg_client_encoding',
+ 'pg_close',
+ 'pg_connect',
+ 'pg_connection_busy',
+ 'pg_connection_reset',
+ 'pg_connection_status',
+ 'pg_convert',
+ 'pg_copy_from',
+ 'pg_copy_to',
+ 'pg_dbname',
+ 'pg_delete',
+ 'pg_end_copy',
+ 'pg_escape_bytea',
+ 'pg_escape_string',
+ 'pg_fetch_all',
+ 'pg_fetch_array',
+ 'pg_fetch_assoc',
+ 'pg_fetch_object',
+ 'pg_fetch_result',
+ 'pg_fetch_row',
+ 'pg_field_is_null',
+ 'pg_field_name',
+ 'pg_field_num',
+ 'pg_field_prtlen',
+ 'pg_field_size',
+ 'pg_field_type',
+ 'pg_free_result',
+ 'pg_get_notify',
+ 'pg_get_pid',
+ 'pg_get_result',
+ 'pg_host',
+ 'pg_insert',
+ 'pg_last_error',
+ 'pg_last_notice',
+ 'pg_last_oid',
+ 'pg_lo_close',
+ 'pg_lo_create',
+ 'pg_lo_export',
+ 'pg_lo_import',
+ 'pg_lo_open',
+ 'pg_lo_read',
+ 'pg_lo_read_all',
+ 'pg_lo_seek',
+ 'pg_lo_tell',
+ 'pg_lo_unlink',
+ 'pg_lo_write',
+ 'pg_meta_data',
+ 'pg_num_fields',
+ 'pg_num_rows',
+ 'pg_options',
+ 'pg_parameter_status',
+ 'pg_pconnect',
+ 'pg_ping',
+ 'pg_port',
+ 'pg_put_line',
+ 'pg_query',
+ 'pg_result_error',
+ 'pg_result_seek',
+ 'pg_result_status',
+ 'pg_select',
+ 'pg_send_query',
+ 'pg_set_client_encoding',
+ 'pg_trace',
+ 'pg_tty',
+ 'pg_unescape_bytea',
+ 'pg_untrace',
+ 'pg_update',
+ 'pg_version'],
+ 'Printer': ['printer_abort',
+ 'printer_close',
+ 'printer_create_brush',
+ 'printer_create_dc',
+ 'printer_create_font',
+ 'printer_create_pen',
+ 'printer_delete_brush',
+ 'printer_delete_dc',
+ 'printer_delete_font',
+ 'printer_delete_pen',
+ 'printer_draw_bmp',
+ 'printer_draw_chord',
+ 'printer_draw_elipse',
+ 'printer_draw_line',
+ 'printer_draw_pie',
+ 'printer_draw_rectangle',
+ 'printer_draw_roundrect',
+ 'printer_draw_text',
+ 'printer_end_doc',
+ 'printer_end_page',
+ 'printer_get_option',
+ 'printer_list',
+ 'printer_logical_fontheight',
+ 'printer_open',
+ 'printer_select_brush',
+ 'printer_select_font',
+ 'printer_select_pen',
+ 'printer_set_option',
+ 'printer_start_doc',
+ 'printer_start_page',
+ 'printer_write'],
+ 'Program Execution': ['escapeshellarg',
+ 'escapeshellcmd',
+ 'exec',
+ 'passthru',
+ 'proc_close',
+ 'proc_get_status',
+ 'proc_nice',
+ 'proc_open',
+ 'proc_terminate',
+ 'shell_exec',
+ 'system'],
+ 'Pspell': ['pspell_add_to_personal',
+ 'pspell_add_to_session',
+ 'pspell_check',
+ 'pspell_clear_session',
+ 'pspell_config_create',
+ 'pspell_config_data_dir',
+ 'pspell_config_dict_dir',
+ 'pspell_config_ignore',
+ 'pspell_config_mode',
+ 'pspell_config_personal',
+ 'pspell_config_repl',
+ 'pspell_config_runtogether',
+ 'pspell_config_save_repl',
+ 'pspell_new',
+ 'pspell_new_config',
+ 'pspell_new_personal',
+ 'pspell_save_wordlist',
+ 'pspell_store_replacement',
+ 'pspell_suggest'],
+ 'Rar': ['rar_close', 'rar_entry_get', 'rar_list', 'rar_open'],
+ 'Readline': ['readline',
+ 'readline_add_history',
+ 'readline_callback_handler_install',
+ 'readline_callback_handler_remove',
+ 'readline_callback_read_char',
+ 'readline_clear_history',
+ 'readline_completion_function',
+ 'readline_info',
+ 'readline_list_history',
+ 'readline_on_new_line',
+ 'readline_read_history',
+ 'readline_redisplay',
+ 'readline_write_history'],
+ 'Recode': ['recode', 'recode_file', 'recode_string'],
+ 'SESAM': ['sesam_affected_rows',
+ 'sesam_commit',
+ 'sesam_connect',
+ 'sesam_diagnostic',
+ 'sesam_disconnect',
+ 'sesam_errormsg',
+ 'sesam_execimm',
+ 'sesam_fetch_array',
+ 'sesam_fetch_result',
+ 'sesam_fetch_row',
+ 'sesam_field_array',
+ 'sesam_field_name',
+ 'sesam_free_result',
+ 'sesam_num_fields',
+ 'sesam_query',
+ 'sesam_rollback',
+ 'sesam_seek_row',
+ 'sesam_settransaction'],
+ 'SNMP': ['snmp_get_quick_print',
+ 'snmp_get_valueretrieval',
+ 'snmp_read_mib',
+ 'snmp_set_enum_print',
+ 'snmp_set_oid_numeric_print',
+ 'snmp_set_quick_print',
+ 'snmp_set_valueretrieval',
+ 'snmpget',
+ 'snmpgetnext',
+ 'snmprealwalk',
+ 'snmpset',
+ 'snmpwalk',
+ 'snmpwalkoid'],
+ 'SOAP': ['is_soap_fault'],
+ 'SQLite': ['sqlite_array_query',
+ 'sqlite_busy_timeout',
+ 'sqlite_changes',
+ 'sqlite_close',
+ 'sqlite_column',
+ 'sqlite_create_aggregate',
+ 'sqlite_create_function',
+ 'sqlite_current',
+ 'sqlite_error_string',
+ 'sqlite_escape_string',
+ 'sqlite_exec',
+ 'sqlite_factory',
+ 'sqlite_fetch_all',
+ 'sqlite_fetch_array',
+ 'sqlite_fetch_column_types',
+ 'sqlite_fetch_object',
+ 'sqlite_fetch_single',
+ 'sqlite_fetch_string',
+ 'sqlite_field_name',
+ 'sqlite_has_more',
+ 'sqlite_has_prev',
+ 'sqlite_last_error',
+ 'sqlite_last_insert_rowid',
+ 'sqlite_libencoding',
+ 'sqlite_libversion',
+ 'sqlite_next',
+ 'sqlite_num_fields',
+ 'sqlite_num_rows',
+ 'sqlite_open',
+ 'sqlite_popen',
+ 'sqlite_prev',
+ 'sqlite_query',
+ 'sqlite_rewind',
+ 'sqlite_seek',
+ 'sqlite_single_query',
+ 'sqlite_udf_decode_binary',
+ 'sqlite_udf_encode_binary',
+ 'sqlite_unbuffered_query'],
+ 'SWF': ['swf_actiongeturl',
+ 'swf_actiongotoframe',
+ 'swf_actiongotolabel',
+ 'swf_actionnextframe',
+ 'swf_actionplay',
+ 'swf_actionprevframe',
+ 'swf_actionsettarget',
+ 'swf_actionstop',
+ 'swf_actiontogglequality',
+ 'swf_actionwaitforframe',
+ 'swf_addbuttonrecord',
+ 'swf_addcolor',
+ 'swf_closefile',
+ 'swf_definebitmap',
+ 'swf_definefont',
+ 'swf_defineline',
+ 'swf_definepoly',
+ 'swf_definerect',
+ 'swf_definetext',
+ 'swf_endbutton',
+ 'swf_enddoaction',
+ 'swf_endshape',
+ 'swf_endsymbol',
+ 'swf_fontsize',
+ 'swf_fontslant',
+ 'swf_fonttracking',
+ 'swf_getbitmapinfo',
+ 'swf_getfontinfo',
+ 'swf_getframe',
+ 'swf_labelframe',
+ 'swf_lookat',
+ 'swf_modifyobject',
+ 'swf_mulcolor',
+ 'swf_nextid',
+ 'swf_oncondition',
+ 'swf_openfile',
+ 'swf_ortho',
+ 'swf_ortho2',
+ 'swf_perspective',
+ 'swf_placeobject',
+ 'swf_polarview',
+ 'swf_popmatrix',
+ 'swf_posround',
+ 'swf_pushmatrix',
+ 'swf_removeobject',
+ 'swf_rotate',
+ 'swf_scale',
+ 'swf_setfont',
+ 'swf_setframe',
+ 'swf_shapearc',
+ 'swf_shapecurveto',
+ 'swf_shapecurveto3',
+ 'swf_shapefillbitmapclip',
+ 'swf_shapefillbitmaptile',
+ 'swf_shapefilloff',
+ 'swf_shapefillsolid',
+ 'swf_shapelinesolid',
+ 'swf_shapelineto',
+ 'swf_shapemoveto',
+ 'swf_showframe',
+ 'swf_startbutton',
+ 'swf_startdoaction',
+ 'swf_startshape',
+ 'swf_startsymbol',
+ 'swf_textwidth',
+ 'swf_translate',
+ 'swf_viewport'],
+ 'Semaphore': ['ftok',
+ 'msg_get_queue',
+ 'msg_receive',
+ 'msg_remove_queue',
+ 'msg_send',
+ 'msg_set_queue',
+ 'msg_stat_queue',
+ 'sem_acquire',
+ 'sem_get',
+ 'sem_release',
+ 'sem_remove',
+ 'shm_attach',
+ 'shm_detach',
+ 'shm_get_var',
+ 'shm_put_var',
+ 'shm_remove',
+ 'shm_remove_var'],
+ 'Sessions': ['session_cache_expire',
+ 'session_cache_limiter',
+ 'session_commit',
+ 'session_decode',
+ 'session_destroy',
+ 'session_encode',
+ 'session_get_cookie_params',
+ 'session_id',
+ 'session_is_registered',
+ 'session_module_name',
+ 'session_name',
+ 'session_regenerate_id',
+ 'session_register',
+ 'session_save_path',
+ 'session_set_cookie_params',
+ 'session_set_save_handler',
+ 'session_start',
+ 'session_unregister',
+ 'session_unset',
+ 'session_write_close'],
+ 'SimpleXML': ['simplexml_import_dom',
+ 'simplexml_load_file',
+ 'simplexml_load_string'],
+ 'Sockets': ['socket_accept',
+ 'socket_bind',
+ 'socket_clear_error',
+ 'socket_close',
+ 'socket_connect',
+ 'socket_create',
+ 'socket_create_listen',
+ 'socket_create_pair',
+ 'socket_get_option',
+ 'socket_getpeername',
+ 'socket_getsockname',
+ 'socket_last_error',
+ 'socket_listen',
+ 'socket_read',
+ 'socket_recv',
+ 'socket_recvfrom',
+ 'socket_select',
+ 'socket_send',
+ 'socket_sendto',
+ 'socket_set_block',
+ 'socket_set_nonblock',
+ 'socket_set_option',
+ 'socket_shutdown',
+ 'socket_strerror',
+ 'socket_write'],
+ 'Streams': ['stream_context_create',
+ 'stream_context_get_default',
+ 'stream_context_get_options',
+ 'stream_context_set_option',
+ 'stream_context_set_params',
+ 'stream_copy_to_stream',
+ 'stream_filter_append',
+ 'stream_filter_prepend',
+ 'stream_filter_register',
+ 'stream_filter_remove',
+ 'stream_get_contents',
+ 'stream_get_filters',
+ 'stream_get_line',
+ 'stream_get_meta_data',
+ 'stream_get_transports',
+ 'stream_get_wrappers',
+ 'stream_register_wrapper',
+ 'stream_select',
+ 'stream_set_blocking',
+ 'stream_set_timeout',
+ 'stream_set_write_buffer',
+ 'stream_socket_accept',
+ 'stream_socket_client',
+ 'stream_socket_enable_crypto',
+ 'stream_socket_get_name',
+ 'stream_socket_pair',
+ 'stream_socket_recvfrom',
+ 'stream_socket_sendto',
+ 'stream_socket_server',
+ 'stream_wrapper_register',
+ 'stream_wrapper_restore',
+ 'stream_wrapper_unregister'],
+ 'Strings': ['addcslashes',
+ 'addslashes',
+ 'bin2hex',
+ 'chop',
+ 'chr',
+ 'chunk_split',
+ 'convert_cyr_string',
+ 'convert_uudecode',
+ 'convert_uuencode',
+ 'count_chars',
+ 'crc32',
+ 'crypt',
+ 'echo',
+ 'explode',
+ 'fprintf',
+ 'get_html_translation_table',
+ 'hebrev',
+ 'hebrevc',
+ 'html_entity_decode',
+ 'htmlentities',
+ 'htmlspecialchars',
+ 'implode',
+ 'join',
+ 'levenshtein',
+ 'localeconv',
+ 'ltrim',
+ 'md5',
+ 'md5_file',
+ 'metaphone',
+ 'money_format',
+ 'nl2br',
+ 'nl_langinfo',
+ 'number_format',
+ 'ord',
+ 'parse_str',
+ 'print',
+ 'printf',
+ 'quoted_printable_decode',
+ 'quotemeta',
+ 'rtrim',
+ 'setlocale',
+ 'sha1',
+ 'sha1_file',
+ 'similar_text',
+ 'soundex',
+ 'sprintf',
+ 'sscanf',
+ 'str_ireplace',
+ 'str_pad',
+ 'str_repeat',
+ 'str_replace',
+ 'str_rot13',
+ 'str_shuffle',
+ 'str_split',
+ 'str_word_count',
+ 'strcasecmp',
+ 'strchr',
+ 'strcmp',
+ 'strcoll',
+ 'strcspn',
+ 'strip_tags',
+ 'stripcslashes',
+ 'stripos',
+ 'stripslashes',
+ 'stristr',
+ 'strlen',
+ 'strnatcasecmp',
+ 'strnatcmp',
+ 'strncasecmp',
+ 'strncmp',
+ 'strpbrk',
+ 'strpos',
+ 'strrchr',
+ 'strrev',
+ 'strripos',
+ 'strrpos',
+ 'strspn',
+ 'strstr',
+ 'strtok',
+ 'strtolower',
+ 'strtoupper',
+ 'strtr',
+ 'substr',
+ 'substr_compare',
+ 'substr_count',
+ 'substr_replace',
+ 'trim',
+ 'ucfirst',
+ 'ucwords',
+ 'vfprintf',
+ 'vprintf',
+ 'vsprintf',
+ 'wordwrap'],
+ 'Sybase': ['sybase_affected_rows',
+ 'sybase_close',
+ 'sybase_connect',
+ 'sybase_data_seek',
+ 'sybase_deadlock_retry_count',
+ 'sybase_fetch_array',
+ 'sybase_fetch_assoc',
+ 'sybase_fetch_field',
+ 'sybase_fetch_object',
+ 'sybase_fetch_row',
+ 'sybase_field_seek',
+ 'sybase_free_result',
+ 'sybase_get_last_message',
+ 'sybase_min_client_severity',
+ 'sybase_min_error_severity',
+ 'sybase_min_message_severity',
+ 'sybase_min_server_severity',
+ 'sybase_num_fields',
+ 'sybase_num_rows',
+ 'sybase_pconnect',
+ 'sybase_query',
+ 'sybase_result',
+ 'sybase_select_db',
+ 'sybase_set_message_handler',
+ 'sybase_unbuffered_query'],
+ 'TCP Wrappers': ['tcpwrap_check'],
+ 'Tokenizer': ['token_get_all', 'token_name'],
+ 'URLs': ['base64_decode',
+ 'base64_encode',
+ 'get_headers',
+ 'get_meta_tags',
+ 'http_build_query',
+ 'parse_url',
+ 'rawurldecode',
+ 'rawurlencode',
+ 'urldecode',
+ 'urlencode'],
+ 'Variables handling': ['debug_zval_dump',
+ 'doubleval',
+ 'empty',
+ 'floatval',
+ 'get_defined_vars',
+ 'get_resource_type',
+ 'gettype',
+ 'import_request_variables',
+ 'intval',
+ 'is_array',
+ 'is_bool',
+ 'is_callable',
+ 'is_double',
+ 'is_float',
+ 'is_int',
+ 'is_integer',
+ 'is_long',
+ 'is_null',
+ 'is_numeric',
+ 'is_object',
+ 'is_real',
+ 'is_resource',
+ 'is_scalar',
+ 'is_string',
+ 'isset',
+ 'print_r',
+ 'serialize',
+ 'settype',
+ 'strval',
+ 'unserialize',
+ 'unset',
+ 'var_dump',
+ 'var_export'],
+ 'Verisign Payflow Pro': ['pfpro_cleanup',
+ 'pfpro_init',
+ 'pfpro_process',
+ 'pfpro_process_raw',
+ 'pfpro_version'],
+ 'W32api': ['w32api_deftype',
+ 'w32api_init_dtype',
+ 'w32api_invoke_function',
+ 'w32api_register_function',
+ 'w32api_set_call_method'],
+ 'WDDX': ['wddx_add_vars',
+ 'wddx_deserialize',
+ 'wddx_packet_end',
+ 'wddx_packet_start',
+ 'wddx_serialize_value',
+ 'wddx_serialize_vars'],
+ 'XML': ['utf8_decode',
+ 'utf8_encode',
+ 'xml_error_string',
+ 'xml_get_current_byte_index',
+ 'xml_get_current_column_number',
+ 'xml_get_current_line_number',
+ 'xml_get_error_code',
+ 'xml_parse',
+ 'xml_parse_into_struct',
+ 'xml_parser_create',
+ 'xml_parser_create_ns',
+ 'xml_parser_free',
+ 'xml_parser_get_option',
+ 'xml_parser_set_option',
+ 'xml_set_character_data_handler',
+ 'xml_set_default_handler',
+ 'xml_set_element_handler',
+ 'xml_set_end_namespace_decl_handler',
+ 'xml_set_external_entity_ref_handler',
+ 'xml_set_notation_decl_handler',
+ 'xml_set_object',
+ 'xml_set_processing_instruction_handler',
+ 'xml_set_start_namespace_decl_handler',
+ 'xml_set_unparsed_entity_decl_handler'],
+ 'XML-RPC': ['xmlrpc_decode',
+ 'xmlrpc_decode_request',
+ 'xmlrpc_encode',
+ 'xmlrpc_encode_request',
+ 'xmlrpc_get_type',
+ 'xmlrpc_is_fault',
+ 'xmlrpc_parse_method_descriptions',
+ 'xmlrpc_server_add_introspection_data',
+ 'xmlrpc_server_call_method',
+ 'xmlrpc_server_create',
+ 'xmlrpc_server_destroy',
+ 'xmlrpc_server_register_introspection_callback',
+ 'xmlrpc_server_register_method',
+ 'xmlrpc_set_type'],
+ 'XSL': ['xsl_xsltprocessor_get_parameter',
+ 'xsl_xsltprocessor_has_exslt_support',
+ 'xsl_xsltprocessor_import_stylesheet',
+ 'xsl_xsltprocessor_register_php_functions',
+ 'xsl_xsltprocessor_remove_parameter',
+ 'xsl_xsltprocessor_set_parameter',
+ 'xsl_xsltprocessor_transform_to_doc',
+ 'xsl_xsltprocessor_transform_to_uri',
+ 'xsl_xsltprocessor_transform_to_xml'],
+ 'XSLT': ['xslt_backend_info',
+ 'xslt_backend_name',
+ 'xslt_backend_version',
+ 'xslt_create',
+ 'xslt_errno',
+ 'xslt_error',
+ 'xslt_free',
+ 'xslt_getopt',
+ 'xslt_process',
+ 'xslt_set_base',
+ 'xslt_set_encoding',
+ 'xslt_set_error_handler',
+ 'xslt_set_log',
+ 'xslt_set_object',
+ 'xslt_set_sax_handler',
+ 'xslt_set_sax_handlers',
+ 'xslt_set_scheme_handler',
+ 'xslt_set_scheme_handlers',
+ 'xslt_setopt'],
+ 'YAZ': ['yaz_addinfo',
+ 'yaz_ccl_conf',
+ 'yaz_ccl_parse',
+ 'yaz_close',
+ 'yaz_connect',
+ 'yaz_database',
+ 'yaz_element',
+ 'yaz_errno',
+ 'yaz_error',
+ 'yaz_es_result',
+ 'yaz_get_option',
+ 'yaz_hits',
+ 'yaz_itemorder',
+ 'yaz_present',
+ 'yaz_range',
+ 'yaz_record',
+ 'yaz_scan',
+ 'yaz_scan_result',
+ 'yaz_schema',
+ 'yaz_search',
+ 'yaz_set_option',
+ 'yaz_sort',
+ 'yaz_syntax',
+ 'yaz_wait'],
+ 'YP/NIS': ['yp_all',
+ 'yp_cat',
+ 'yp_err_string',
+ 'yp_errno',
+ 'yp_first',
+ 'yp_get_default_domain',
+ 'yp_master',
+ 'yp_match',
+ 'yp_next',
+ 'yp_order'],
+ 'Zip': ['zip_close',
+ 'zip_entry_close',
+ 'zip_entry_compressedsize',
+ 'zip_entry_compressionmethod',
+ 'zip_entry_filesize',
+ 'zip_entry_name',
+ 'zip_entry_open',
+ 'zip_entry_read',
+ 'zip_open',
+ 'zip_read'],
+ 'Zlib': ['gzclose',
+ 'gzcompress',
+ 'gzdeflate',
+ 'gzencode',
+ 'gzeof',
+ 'gzfile',
+ 'gzgetc',
+ 'gzgets',
+ 'gzgetss',
+ 'gzinflate',
+ 'gzopen',
+ 'gzpassthru',
+ 'gzputs',
+ 'gzread',
+ 'gzrewind',
+ 'gzseek',
+ 'gztell',
+ 'gzuncompress',
+ 'gzwrite',
+ 'readgzfile',
+ 'zlib_get_coding_type'],
+ 'bcompiler': ['bcompiler_load',
+ 'bcompiler_load_exe',
+ 'bcompiler_parse_class',
+ 'bcompiler_read',
+ 'bcompiler_write_class',
+ 'bcompiler_write_constant',
+ 'bcompiler_write_exe_footer',
+ 'bcompiler_write_footer',
+ 'bcompiler_write_function',
+ 'bcompiler_write_functions_from_file',
+ 'bcompiler_write_header'],
+ 'ctype': ['ctype_alnum',
+ 'ctype_alpha',
+ 'ctype_cntrl',
+ 'ctype_digit',
+ 'ctype_graph',
+ 'ctype_lower',
+ 'ctype_print',
+ 'ctype_punct',
+ 'ctype_space',
+ 'ctype_upper',
+ 'ctype_xdigit'],
+ 'dBase': ['dbase_add_record',
+ 'dbase_close',
+ 'dbase_create',
+ 'dbase_delete_record',
+ 'dbase_get_header_info',
+ 'dbase_get_record',
+ 'dbase_get_record_with_names',
+ 'dbase_numfields',
+ 'dbase_numrecords',
+ 'dbase_open',
+ 'dbase_pack',
+ 'dbase_replace_record'],
+ 'dba': ['dba_close',
+ 'dba_delete',
+ 'dba_exists',
+ 'dba_fetch',
+ 'dba_firstkey',
+ 'dba_handlers',
+ 'dba_insert',
+ 'dba_key_split',
+ 'dba_list',
+ 'dba_nextkey',
+ 'dba_open',
+ 'dba_optimize',
+ 'dba_popen',
+ 'dba_replace',
+ 'dba_sync'],
+ 'dbx': ['dbx_close',
+ 'dbx_compare',
+ 'dbx_connect',
+ 'dbx_error',
+ 'dbx_escape_string',
+ 'dbx_fetch_row',
+ 'dbx_query',
+ 'dbx_sort'],
+ 'fam': ['fam_cancel_monitor',
+ 'fam_close',
+ 'fam_monitor_collection',
+ 'fam_monitor_directory',
+ 'fam_monitor_file',
+ 'fam_next_event',
+ 'fam_open',
+ 'fam_pending',
+ 'fam_resume_monitor',
+ 'fam_suspend_monitor'],
+ 'filePro': ['filepro',
+ 'filepro_fieldcount',
+ 'filepro_fieldname',
+ 'filepro_fieldtype',
+ 'filepro_fieldwidth',
+ 'filepro_retrieve',
+ 'filepro_rowcount'],
+ 'gettext': ['bind_textdomain_codeset',
+ 'bindtextdomain',
+ 'dcgettext',
+ 'dcngettext',
+ 'dgettext',
+ 'dngettext',
+ 'gettext',
+ 'ngettext',
+ 'textdomain'],
+ 'iconv': ['iconv',
+ 'iconv_get_encoding',
+ 'iconv_mime_decode',
+ 'iconv_mime_decode_headers',
+ 'iconv_mime_encode',
+ 'iconv_set_encoding',
+ 'iconv_strlen',
+ 'iconv_strpos',
+ 'iconv_strrpos',
+ 'iconv_substr',
+ 'ob_iconv_handler'],
+ 'id3': ['id3_get_frame_long_name',
+ 'id3_get_frame_short_name',
+ 'id3_get_genre_id',
+ 'id3_get_genre_list',
+ 'id3_get_genre_name',
+ 'id3_get_tag',
+ 'id3_get_version',
+ 'id3_remove_tag',
+ 'id3_set_tag'],
+ 'mSQL': ['msql',
+ 'msql_affected_rows',
+ 'msql_close',
+ 'msql_connect',
+ 'msql_create_db',
+ 'msql_createdb',
+ 'msql_data_seek',
+ 'msql_db_query',
+ 'msql_dbname',
+ 'msql_drop_db',
+ 'msql_error',
+ 'msql_fetch_array',
+ 'msql_fetch_field',
+ 'msql_fetch_object',
+ 'msql_fetch_row',
+ 'msql_field_flags',
+ 'msql_field_len',
+ 'msql_field_name',
+ 'msql_field_seek',
+ 'msql_field_table',
+ 'msql_field_type',
+ 'msql_fieldflags',
+ 'msql_fieldlen',
+ 'msql_fieldname',
+ 'msql_fieldtable',
+ 'msql_fieldtype',
+ 'msql_free_result',
+ 'msql_list_dbs',
+ 'msql_list_fields',
+ 'msql_list_tables',
+ 'msql_num_fields',
+ 'msql_num_rows',
+ 'msql_numfields',
+ 'msql_numrows',
+ 'msql_pconnect',
+ 'msql_query',
+ 'msql_regcase',
+ 'msql_result',
+ 'msql_select_db',
+ 'msql_tablename'],
+ 'mailparse': ['mailparse_determine_best_xfer_encoding',
+ 'mailparse_msg_create',
+ 'mailparse_msg_extract_part',
+ 'mailparse_msg_extract_part_file',
+ 'mailparse_msg_free',
+ 'mailparse_msg_get_part',
+ 'mailparse_msg_get_part_data',
+ 'mailparse_msg_get_structure',
+ 'mailparse_msg_parse',
+ 'mailparse_msg_parse_file',
+ 'mailparse_rfc822_parse_addresses',
+ 'mailparse_stream_encode',
+ 'mailparse_uudecode_all'],
+ 'mcrypt': ['mcrypt_cbc',
+ 'mcrypt_cfb',
+ 'mcrypt_create_iv',
+ 'mcrypt_decrypt',
+ 'mcrypt_ecb',
+ 'mcrypt_enc_get_algorithms_name',
+ 'mcrypt_enc_get_block_size',
+ 'mcrypt_enc_get_iv_size',
+ 'mcrypt_enc_get_key_size',
+ 'mcrypt_enc_get_modes_name',
+ 'mcrypt_enc_get_supported_key_sizes',
+ 'mcrypt_enc_is_block_algorithm',
+ 'mcrypt_enc_is_block_algorithm_mode',
+ 'mcrypt_enc_is_block_mode',
+ 'mcrypt_enc_self_test',
+ 'mcrypt_encrypt',
+ 'mcrypt_generic',
+ 'mcrypt_generic_deinit',
+ 'mcrypt_generic_end',
+ 'mcrypt_generic_init',
+ 'mcrypt_get_block_size',
+ 'mcrypt_get_cipher_name',
+ 'mcrypt_get_iv_size',
+ 'mcrypt_get_key_size',
+ 'mcrypt_list_algorithms',
+ 'mcrypt_list_modes',
+ 'mcrypt_module_close',
+ 'mcrypt_module_get_algo_block_size',
+ 'mcrypt_module_get_algo_key_size',
+ 'mcrypt_module_get_supported_key_sizes',
+ 'mcrypt_module_is_block_algorithm',
+ 'mcrypt_module_is_block_algorithm_mode',
+ 'mcrypt_module_is_block_mode',
+ 'mcrypt_module_open',
+ 'mcrypt_module_self_test',
+ 'mcrypt_ofb',
+ 'mdecrypt_generic'],
+ 'mhash': ['mhash',
+ 'mhash_count',
+ 'mhash_get_block_size',
+ 'mhash_get_hash_name',
+ 'mhash_keygen_s2k'],
+ 'mnoGoSearch': ['udm_add_search_limit',
+ 'udm_alloc_agent',
+ 'udm_alloc_agent_array',
+ 'udm_api_version',
+ 'udm_cat_list',
+ 'udm_cat_path',
+ 'udm_check_charset',
+ 'udm_check_stored',
+ 'udm_clear_search_limits',
+ 'udm_close_stored',
+ 'udm_crc32',
+ 'udm_errno',
+ 'udm_error',
+ 'udm_find',
+ 'udm_free_agent',
+ 'udm_free_ispell_data',
+ 'udm_free_res',
+ 'udm_get_doc_count',
+ 'udm_get_res_field',
+ 'udm_get_res_param',
+ 'udm_hash32',
+ 'udm_load_ispell_data',
+ 'udm_open_stored',
+ 'udm_set_agent_param'],
+ 'muscat': ['muscat_close',
+ 'muscat_get',
+ 'muscat_give',
+ 'muscat_setup',
+ 'muscat_setup_net'],
+ 'mysqli': ['mysqli_affected_rows',
+ 'mysqli_autocommit',
+ 'mysqli_bind_param',
+ 'mysqli_bind_result',
+ 'mysqli_change_user',
+ 'mysqli_character_set_name',
+ 'mysqli_client_encoding',
+ 'mysqli_close',
+ 'mysqli_commit',
+ 'mysqli_connect',
+ 'mysqli_connect_errno',
+ 'mysqli_connect_error',
+ 'mysqli_data_seek',
+ 'mysqli_debug',
+ 'mysqli_disable_reads_from_master',
+ 'mysqli_disable_rpl_parse',
+ 'mysqli_dump_debug_info',
+ 'mysqli_embedded_connect',
+ 'mysqli_enable_reads_from_master',
+ 'mysqli_enable_rpl_parse',
+ 'mysqli_errno',
+ 'mysqli_error',
+ 'mysqli_escape_string',
+ 'mysqli_execute',
+ 'mysqli_fetch',
+ 'mysqli_fetch_array',
+ 'mysqli_fetch_assoc',
+ 'mysqli_fetch_field',
+ 'mysqli_fetch_field_direct',
+ 'mysqli_fetch_fields',
+ 'mysqli_fetch_lengths',
+ 'mysqli_fetch_object',
+ 'mysqli_fetch_row',
+ 'mysqli_field_count',
+ 'mysqli_field_seek',
+ 'mysqli_field_tell',
+ 'mysqli_free_result',
+ 'mysqli_get_client_info',
+ 'mysqli_get_client_version',
+ 'mysqli_get_host_info',
+ 'mysqli_get_metadata',
+ 'mysqli_get_proto_info',
+ 'mysqli_get_server_info',
+ 'mysqli_get_server_version',
+ 'mysqli_info',
+ 'mysqli_init',
+ 'mysqli_insert_id',
+ 'mysqli_kill',
+ 'mysqli_master_query',
+ 'mysqli_more_results',
+ 'mysqli_multi_query',
+ 'mysqli_next_result',
+ 'mysqli_num_fields',
+ 'mysqli_num_rows',
+ 'mysqli_options',
+ 'mysqli_param_count',
+ 'mysqli_ping',
+ 'mysqli_prepare',
+ 'mysqli_query',
+ 'mysqli_real_connect',
+ 'mysqli_real_escape_string',
+ 'mysqli_real_query',
+ 'mysqli_report',
+ 'mysqli_rollback',
+ 'mysqli_rpl_parse_enabled',
+ 'mysqli_rpl_probe',
+ 'mysqli_rpl_query_type',
+ 'mysqli_select_db',
+ 'mysqli_send_long_data',
+ 'mysqli_send_query',
+ 'mysqli_server_end',
+ 'mysqli_server_init',
+ 'mysqli_set_opt',
+ 'mysqli_sqlstate',
+ 'mysqli_ssl_set',
+ 'mysqli_stat',
+ 'mysqli_stmt_affected_rows',
+ 'mysqli_stmt_bind_param',
+ 'mysqli_stmt_bind_result',
+ 'mysqli_stmt_close',
+ 'mysqli_stmt_data_seek',
+ 'mysqli_stmt_errno',
+ 'mysqli_stmt_error',
+ 'mysqli_stmt_execute',
+ 'mysqli_stmt_fetch',
+ 'mysqli_stmt_free_result',
+ 'mysqli_stmt_init',
+ 'mysqli_stmt_num_rows',
+ 'mysqli_stmt_param_count',
+ 'mysqli_stmt_prepare',
+ 'mysqli_stmt_reset',
+ 'mysqli_stmt_result_metadata',
+ 'mysqli_stmt_send_long_data',
+ 'mysqli_stmt_sqlstate',
+ 'mysqli_stmt_store_result',
+ 'mysqli_store_result',
+ 'mysqli_thread_id',
+ 'mysqli_thread_safe',
+ 'mysqli_use_result',
+ 'mysqli_warning_count'],
+ 'openal': ['openal_buffer_create',
+ 'openal_buffer_data',
+ 'openal_buffer_destroy',
+ 'openal_buffer_get',
+ 'openal_buffer_loadwav',
+ 'openal_context_create',
+ 'openal_context_current',
+ 'openal_context_destroy',
+ 'openal_context_process',
+ 'openal_context_suspend',
+ 'openal_device_close',
+ 'openal_device_open',
+ 'openal_listener_get',
+ 'openal_listener_set',
+ 'openal_source_create',
+ 'openal_source_destroy',
+ 'openal_source_get',
+ 'openal_source_pause',
+ 'openal_source_play',
+ 'openal_source_rewind',
+ 'openal_source_set',
+ 'openal_source_stop',
+ 'openal_stream'],
+ 'qtdom': ['qdom_error', 'qdom_tree'],
+ 'shmop': ['shmop_close',
+ 'shmop_delete',
+ 'shmop_open',
+ 'shmop_read',
+ 'shmop_size',
+ 'shmop_write'],
+ 'spl': ['class_implements',
+ 'class_parents',
+ 'iterator-to-array',
+ 'iterator_count',
+ 'spl_classes'],
+ 'ssh2': ['ssh2_auth_none',
+ 'ssh2_auth_password',
+ 'ssh2_auth_pubkey_file',
+ 'ssh2_connect',
+ 'ssh2_exec',
+ 'ssh2_fetch_stream',
+ 'ssh2_fingerprint',
+ 'ssh2_methods_negotiated',
+ 'ssh2_scp_recv',
+ 'ssh2_scp_send',
+ 'ssh2_sftp',
+ 'ssh2_sftp_lstat',
+ 'ssh2_sftp_mkdir',
+ 'ssh2_sftp_readlink',
+ 'ssh2_sftp_realpath',
+ 'ssh2_sftp_rename',
+ 'ssh2_sftp_rmdir',
+ 'ssh2_sftp_stat',
+ 'ssh2_sftp_symlink',
+ 'ssh2_sftp_unlink',
+ 'ssh2_shell',
+ 'ssh2_tunnel'],
+ 'tidy': ['ob_tidyhandler',
+ 'tidy_access_count',
+ 'tidy_clean_repair',
+ 'tidy_config_count',
+ 'tidy_diagnose',
+ 'tidy_error_count',
+ 'tidy_get_body',
+ 'tidy_get_config',
+ 'tidy_get_error_buffer',
+ 'tidy_get_head',
+ 'tidy_get_html',
+ 'tidy_get_html_ver',
+ 'tidy_get_output',
+ 'tidy_get_release',
+ 'tidy_get_root',
+ 'tidy_get_status',
+ 'tidy_getopt',
+ 'tidy_is_xhtml',
+ 'tidy_is_xml',
+ 'tidy_load_config',
+ 'tidy_parse_file',
+ 'tidy_parse_string',
+ 'tidy_repair_file',
+ 'tidy_repair_string',
+ 'tidy_reset_config',
+ 'tidy_save_config',
+ 'tidy_set_encoding',
+ 'tidy_setopt',
+ 'tidy_warning_count'],
+ 'unknown': ['bcompile_write_file',
+ 'com',
+ 'dir',
+ 'dotnet',
+ 'hw_api_attribute',
+ 'hw_api_content',
+ 'hw_api_object',
+ 'imagepscopyfont',
+ 'mcve_adduser',
+ 'mcve_adduserarg',
+ 'mcve_bt',
+ 'mcve_checkstatus',
+ 'mcve_chkpwd',
+ 'mcve_chngpwd',
+ 'mcve_completeauthorizations',
+ 'mcve_connect',
+ 'mcve_connectionerror',
+ 'mcve_deleteresponse',
+ 'mcve_deletetrans',
+ 'mcve_deleteusersetup',
+ 'mcve_deluser',
+ 'mcve_destroyconn',
+ 'mcve_destroyengine',
+ 'mcve_disableuser',
+ 'mcve_edituser',
+ 'mcve_enableuser',
+ 'mcve_force',
+ 'mcve_getcell',
+ 'mcve_getcellbynum',
+ 'mcve_getcommadelimited',
+ 'mcve_getheader',
+ 'mcve_getuserarg',
+ 'mcve_getuserparam',
+ 'mcve_gft',
+ 'mcve_gl',
+ 'mcve_gut',
+ 'mcve_initconn',
+ 'mcve_initengine',
+ 'mcve_initusersetup',
+ 'mcve_iscommadelimited',
+ 'mcve_liststats',
+ 'mcve_listusers',
+ 'mcve_maxconntimeout',
+ 'mcve_monitor',
+ 'mcve_numcolumns',
+ 'mcve_numrows',
+ 'mcve_override',
+ 'mcve_parsecommadelimited',
+ 'mcve_ping',
+ 'mcve_preauth',
+ 'mcve_preauthcompletion',
+ 'mcve_qc',
+ 'mcve_responseparam',
+ 'mcve_return',
+ 'mcve_returncode',
+ 'mcve_returnstatus',
+ 'mcve_sale',
+ 'mcve_setblocking',
+ 'mcve_setdropfile',
+ 'mcve_setip',
+ 'mcve_setssl',
+ 'mcve_setssl_files',
+ 'mcve_settimeout',
+ 'mcve_settle',
+ 'mcve_text_avs',
+ 'mcve_text_code',
+ 'mcve_text_cv',
+ 'mcve_transactionauth',
+ 'mcve_transactionavs',
+ 'mcve_transactionbatch',
+ 'mcve_transactioncv',
+ 'mcve_transactionid',
+ 'mcve_transactionitem',
+ 'mcve_transactionssent',
+ 'mcve_transactiontext',
+ 'mcve_transinqueue',
+ 'mcve_transnew',
+ 'mcve_transparam',
+ 'mcve_transsend',
+ 'mcve_ub',
+ 'mcve_uwait',
+ 'mcve_verifyconnection',
+ 'mcve_verifysslcert',
+ 'mcve_void',
+ 'mysqli()',
+ 'pdf_open',
+ 'pdf_open_png',
+ 'pdf_set_font',
+ 'php_register_url_stream_wrapper',
+ 'php_stream_can_cast',
+ 'php_stream_cast',
+ 'php_stream_close',
+ 'php_stream_closedir',
+ 'php_stream_copy_to_mem',
+ 'php_stream_copy_to_stream',
+ 'php_stream_eof',
+ 'php_stream_filter_register_factory',
+ 'php_stream_filter_unregister_factory',
+ 'php_stream_flush',
+ 'php_stream_fopen_from_file',
+ 'php_stream_fopen_temporary_file',
+ 'php_stream_fopen_tmpfile',
+ 'php_stream_getc',
+ 'php_stream_gets',
+ 'php_stream_is',
+ 'php_stream_is_persistent',
+ 'php_stream_make_seekable',
+ 'php_stream_open_wrapper',
+ 'php_stream_open_wrapper_as_file',
+ 'php_stream_open_wrapper_ex',
+ 'php_stream_opendir',
+ 'php_stream_passthru',
+ 'php_stream_read',
+ 'php_stream_readdir',
+ 'php_stream_rewinddir',
+ 'php_stream_seek',
+ 'php_stream_sock_open_from_socket',
+ 'php_stream_sock_open_host',
+ 'php_stream_sock_open_unix',
+ 'php_stream_stat',
+ 'php_stream_stat_path',
+ 'php_stream_tell',
+ 'php_stream_write',
+ 'php_unregister_url_stream_wrapper',
+ 'swfbutton_keypress',
+ 'swfdisplayitem',
+ 'variant'],
+ 'vpopmail': ['vpopmail_add_alias_domain',
+ 'vpopmail_add_alias_domain_ex',
+ 'vpopmail_add_domain',
+ 'vpopmail_add_domain_ex',
+ 'vpopmail_add_user',
+ 'vpopmail_alias_add',
+ 'vpopmail_alias_del',
+ 'vpopmail_alias_del_domain',
+ 'vpopmail_alias_get',
+ 'vpopmail_alias_get_all',
+ 'vpopmail_auth_user',
+ 'vpopmail_del_domain',
+ 'vpopmail_del_domain_ex',
+ 'vpopmail_del_user',
+ 'vpopmail_error',
+ 'vpopmail_passwd',
+ 'vpopmail_set_user_quota'],
+ 'xattr': ['xattr_get',
+ 'xattr_list',
+ 'xattr_remove',
+ 'xattr_set',
+ 'xattr_supported'],
+ 'xdiff': ['xdiff_file_diff',
+ 'xdiff_file_diff_binary',
+ 'xdiff_file_merge3',
+ 'xdiff_file_patch',
+ 'xdiff_file_patch_binary',
+ 'xdiff_string_diff',
+ 'xdiff_string_diff_binary',
+ 'xdiff_string_merge3',
+ 'xdiff_string_patch',
+ 'xdiff_string_patch_binary']}
+
+
+if __name__ == '__main__':
+ import pprint
+ import sys
+ import os
+ import re
+ import urllib
+ _function_re = re.compile('<B\s+CLASS="function"\s*>(.*?)\(\)</B\s*>(?uism)')
+
+ def get_php_functions():
+ f = urllib.urlopen('http://de.php.net/manual/en/index.functions.php')
+ data = f.read()
+ f.close()
+ results = set()
+ for match in _function_re.finditer(data):
+ fn = match.group(1)
+ if '-&#62;' not in fn and '::' not in fn:
+ results.add(fn)
+ # PY24: use sorted()
+ results = list(results)
+ results.sort()
+ return results
+
+ def get_function_module(function_name):
+ fn = function_name.replace('_', '-')
+ f = urllib.urlopen('http://de.php.net/manual/en/function.%s.php' % fn)
+ regex = re.compile('<li class="header up">'
+ '<a href="ref\..*?\.php">([a-zA-Z0-9\s]+)</a></li>')
+ for line in f:
+ m = regex.search(line)
+ if m is not None:
+ return m.group(1)
+
+ print '>> Downloading Function Index'
+ functions = get_php_functions()
+ total = len(functions)
+ print '%d functions found' % total
+ modules = {}
+ idx = 1
+ for function_name in get_php_functions():
+ print '>> %r (%d/%d)' % (function_name, idx, total)
+ m = get_function_module(function_name)
+ if m is None:
+ print 'NOT_FOUND'
+ m = 'unknown'
+ else:
+ print repr(m)
+ modules.setdefault(m, []).append(function_name)
+ idx += 1
+
+ # extract useful sourcecode from this file
+ f = file(__file__)
+ try:
+ content = f.read()
+ finally:
+ f.close()
+ header = content[:content.find('MODULES = {')]
+ footer = content[content.find("if __name__ == '__main__':"):]
+
+ # write new file
+ f = file(__file__, 'w')
+ f.write(header)
+ f.write('MODULES = %s\n\n' % pprint.pformat(modules))
+ f.write(footer)
+ f.close()
diff --git a/pygments/lexers/agile.py b/pygments/lexers/agile.py
new file mode 100644
index 00000000..a5d370a1
--- /dev/null
+++ b/pygments/lexers/agile.py
@@ -0,0 +1,773 @@
+# -*- coding: utf-8 -*-
+"""
+ pygments.lexers.agile
+ ~~~~~~~~~~~~~~~~~~~~
+
+ Lexers for agile languages: Python, Ruby, Perl.
+
+ :copyright: 2006 by Georg Brandl, Armin Ronacher, Lukas Meuser.
+ :license: GNU LGPL, see LICENSE for more details.
+"""
+
+import re
+try:
+ set
+except NameError:
+ from sets import Set as set
+
+from pygments.lexer import Lexer, RegexLexer, ExtendedRegexLexer, \
+ LexerContext, include, combined, do_insertions, bygroups
+from pygments.token import Error, Text, \
+ Comment, Operator, Keyword, Name, String, Number, Generic
+from pygments.util import get_bool_opt, get_list_opt
+
+
+__all__ = ['PythonLexer', 'PythonConsoleLexer', 'RubyLexer',
+ 'RubyConsoleLexer', 'PerlLexer', 'LuaLexer']
+
+line_re = re.compile('.*?\n')
+
+
+class PythonLexer(RegexLexer):
+ name = 'Python'
+ aliases = ['python', 'py']
+ filenames = ['*.py', '*.pyw']
+
+ tokens = {
+ 'root': [
+ (r'\n', Text),
+ (r'^\s*"""(.|\n)*?"""', String.Doc),
+ (r"^\s*'''(.|\n)*?'''", String.Doc),
+ (r'[^\S\n]+', Text),
+ (r'#.*$', Comment),
+ (r'[]{}:(),.;[]', Text),
+ (r'\\\n', Text),
+ (r'\\', Text),
+ (r'(in|is|and|or|not)\b', Operator.Word),
+ (r'!=|==|<<|>>|[-+/*%=<>&^|]', Operator),
+ (r'(assert|break|continue|del|elif|else|except|exec|'
+ r'finally|for|global|if|lambda|pass|print|raise|'
+ r'return|try|while|yield)\b', Keyword),
+ (r'(def)(\s+)', bygroups(Keyword, Text), 'funcname'),
+ (r'(class)(\s+)', bygroups(Keyword, Text), 'classname'),
+ (r'(from)(\s+)', bygroups(Keyword, Text), 'fromimport'),
+ (r'(import)(\s+)', bygroups(Keyword, Text), 'import'),
+ (r'@[a-zA-Z0-9.]+', Name.Decorator),
+ (r'(?<!\.)(__import__|abs|apply|basestring|bool|buffer|callable|'
+ r'chr|classmethod|cmp|coerce|compile|complex|delattr|dict|dir|'
+ r'divmod|enumerate|eval|execfile|exit|file|filter|float|getattr|'
+ r'globals|hasattr|hash|hex|id|input|int|intern|isinstance|'
+ r'issubclass|iter|len|list|locals|long|map|max|min|object|oct|'
+ r'open|ord|pow|property|range|raw_input|reduce|reload|repr|'
+ r'round|setattr|slice|staticmethod|str|sum|super|tuple|type|'
+ r'unichr|unicode|vars|xrange|zip)\b', Name.Builtin),
+ (r'(?<!\.)(self|None|Ellipsis|NotImplemented|False|True'
+ r')\b', Name.Builtin.Pseudo),
+ (r'(?<!\.)(ArithmeticError|AssertionError|AttributeError|'
+ r'DeprecationWarning|EOFError|EnvironmentError|'
+ r'Exception|FloatingPointError|FutureWarning|IOError|'
+ r'ImportError|IndentationError|IndexError|KeyError|'
+ r'KeyboardInterrupt|LookupError|MemoryError|NameError|'
+ r'NotImplemented|NotImplementedError|OSError|OverflowError|'
+ r'OverflowWarning|PendingDeprecationWarning|ReferenceError|'
+ r'RuntimeError|RuntimeWarning|StandardError|StopIteration|'
+ r'SyntaxError|SyntaxWarning|SystemError|SystemExit|TabError|'
+ r'TypeError|UnboundLocalError|UnicodeDecodeError|'
+ r'UnicodeEncodeError|UnicodeError|UnicodeTranslateError|'
+ r'UserWarning|ValueError|Warning|ZeroDivisionError'
+ r')\b', Name.Exception),
+ ('`.*?`', String.Backtick),
+ ('r"""', String, 'tdqs'),
+ ("r'''", String, 'tsqs'),
+ ('r"', String, 'dqs'),
+ ("r'", String, 'sqs'),
+ ('"""', String, combined('stringescape', 'tdqs')),
+ ("'''", String, combined('stringescape', 'tsqs')),
+ ('"', String, combined('stringescape', 'dqs')),
+ ("'", String, combined('stringescape', 'sqs')),
+ ('[a-zA-Z_][a-zA-Z0-9_]*', Name),
+ (r'(\d+\.\d*|\d*\.\d+)([eE][+-]?[0-9]+)?', Number.Float),
+ (r'0\d+', Number.Oct),
+ (r'0x[a-fA-F0-9]+', Number.Hex),
+ (r'\d+L', Number.Integer.Long),
+ (r'\d+', Number.Integer)
+ ],
+ 'funcname': [
+ ('[a-zA-Z_][a-zA-Z0-9_]*', Name.Function, '#pop')
+ ],
+ 'classname': [
+ ('[a-zA-Z_][a-zA-Z0-9_]*', Name.Class, '#pop')
+ ],
+ 'import': [
+ (r'(\s*)(as)(\s*)', bygroups(Text, Keyword, Text)),
+ (r'[a-zA-Z_][a-zA-Z0-9_.]*', Name.Namespace),
+ (r'(\s*)(,)(\s*)', bygroups(Text, Operator, Text)),
+ (r'', Text, '#pop') # all else: go back
+ ],
+ 'fromimport': [
+ (r'(\s+)(import)\b', bygroups(Text, Keyword), '#pop'),
+ (r'[a-zA-Z_.][a-zA-Z0-9_.]*', Name.Namespace),
+ ],
+ 'stringescape': [
+ (r'\\([\\abfnrtv"\']|N{.*?}|u[a-fA-F0-9]{4}|'
+ r'U[a-fA-F0-9]{8}|x[a-fA-F0-9]{2}|[0-7]{1,3})', String.Escape)
+ ],
+ 'strings': [
+ (r'%(\([a-zA-Z0-9]+\))?[-#0 +]*([0-9]+|[*])?(\.([0-9]+|[*]))?'
+ '[hlL]?[diouxXeEfFgGcrs%]', String.Interpol),
+ (r'[^\\\'"%\n]+', String),
+ # quotes, percents and backslashes must be parsed one at a time
+ ('[\'"\\\\]', String),
+ # unhandled string formatting sign
+ (r'%', String)
+ # newlines are an error (use "nl" state)
+ ],
+ 'nl': [
+ (r'\n', String)
+ ],
+ 'dqs': [
+ (r'"', String, '#pop'),
+ include('strings')
+ ],
+ 'sqs': [
+ (r"'", String, '#pop'),
+ include('strings')
+ ],
+ 'tdqs': [
+ (r'"""', String, '#pop'),
+ include('strings'),
+ include('nl')
+ ],
+ 'tsqs': [
+ (r"'''", String, '#pop'),
+ include('strings'),
+ include('nl')
+ ],
+ }
+
+
+class PythonConsoleLexer(Lexer):
+ """
+ Parses Python console output or doctests, like::
+
+ >>> a = 1
+ >>> print a
+ 1
+ """
+ name = 'Python console session'
+ aliases = ['pycon']
+
+ def get_tokens_unprocessed(self, text):
+ pylexer = PythonLexer(**self.options)
+
+ curcode = ''
+ insertions = []
+ tb = 0
+ for match in line_re.finditer(text):
+ line = match.group()
+ if line.startswith('>>> ') or line.startswith('... '):
+ tb = 0
+ insertions.append((len(curcode),
+ [(0, Generic.Prompt, line[:4])]))
+ curcode += line[4:]
+ else:
+ if curcode:
+ for item in do_insertions(insertions,
+ pylexer.get_tokens_unprocessed(curcode)):
+ yield item
+ curcode = ''
+ insertions = []
+ if line.startswith('Traceback (most recent call last):'):
+ tb = 1
+ yield match.start(), Generic.Traceback, line
+ elif tb:
+ if not line.startswith(' '):
+ tb = 0
+ yield match.start(), Generic.Traceback, line
+ else:
+ yield match.start(), Generic.Output, line
+ if curcode:
+ for item in do_insertions(insertions,
+ pylexer.get_tokens_unprocessed(curcode)):
+ yield item
+
+
+class RubyLexer(ExtendedRegexLexer):
+ name = 'Ruby'
+ aliases = ['rb', 'ruby']
+ filenames = ['*.rb', '*.rbw', 'Rakefile', '*.rake', '*.gemspec', '*.rbx']
+
+ flags = re.DOTALL | re.MULTILINE
+
+ def heredoc_callback(self, match, ctx):
+ # okay, this is the hardest part of parsing Ruby...
+ # match: 1 = <<-?, 2 = quote? 3 = name 4 = quote? 5 = rest of line
+
+ start = match.start(1)
+ yield start, Operator, match.group(1) # <<-?
+ yield match.start(2), String.Heredoc, match.group(2) # quote ", ', `
+ yield match.start(3), Name.Constant, match.group(3) # heredoc name
+ yield match.start(4), String.Heredoc, match.group(4) # quote again
+
+ heredocstack = ctx.__dict__.setdefault('heredocstack', [])
+ outermost = not bool(heredocstack)
+ heredocstack.append((match.group(1) == '<<-', match.group(3)))
+
+ ctx.pos = match.start(5)
+ ctx.end = match.end(5)
+ # this may find other heredocs
+ for i, t, v in self.get_tokens_unprocessed(context=ctx):
+ yield i+start, t, v
+ ctx.pos = match.end()
+
+ if outermost:
+ # this is the outer heredoc again, now we can process them all
+ for tolerant, hdname in heredocstack:
+ lines = []
+ for match in line_re.finditer(ctx.text, ctx.pos):
+ if tolerant:
+ check = match.group().strip()
+ else:
+ check = match.group().rstrip()
+ if check == hdname:
+ for amatch in lines:
+ yield amatch.start(), String.Heredoc, amatch.group()
+ yield match.start(), Name.Constant, match.group()
+ ctx.pos = match.end()
+ break
+ else:
+ lines.append(match)
+ else:
+ # end of heredoc not found -- error!
+ for amatch in lines:
+ yield amatch.start(), Error, amatch.group()
+ ctx.end = len(ctx.text)
+ del heredocstack[:]
+
+
+ def gen_rubystrings_rules():
+ def intp_regex_callback(self, match, ctx):
+ yield match.start(1), String.Regex, match.group(1) # begin
+ nctx = LexerContext(match.group(3), 0, ['interpolated-regex'])
+ for i, t, v in self.get_tokens_unprocessed(context=nctx):
+ yield match.start(3)+i, t, v
+ yield match.start(4), String.Regex, match.group(4) # end[mixounse]*
+ ctx.pos = match.end()
+
+ def intp_string_callback(self, match, ctx):
+ yield match.start(1), String.Other, match.group(1)
+ nctx = LexerContext(match.group(3), 0, ['interpolated-string'])
+ for i, t, v in self.get_tokens_unprocessed(context=nctx):
+ yield match.start(3)+i, t, v
+ yield match.start(4), String.Other, match.group(4) # end
+ ctx.pos = match.end()
+
+ states = {}
+ states['strings'] = [
+ # easy ones
+ (r'\:([a-zA-Z_][\w_]*[\!\?]?|\*\*?|[-+]@?|'
+ r'[/%&|^`~]|\[\]=?|<<|>>|<=?>|>=?|===?)', String.Symbol),
+ (r":'(\\\\|\\'|[^'])*'", String.Symbol),
+ (r"'(\\\\|\\'|[^'])*'", String.Single),
+ (r':"', String.Symbol, 'simple-sym'),
+ (r'"', String.Double, 'simple-string'),
+ (r'(?<!\.)`', String.Backtick, 'simple-backtick'),
+ ]
+ # double-quoted string and symbol
+
+ for name, ttype, end in ('string', String.Double, '"'), \
+ ('sym', String.Symbol, '"'), \
+ ('backtick', String.Backtick, '`'):
+ states['simple-'+name] = [
+ include('string-intp-escaped'),
+ (r'[^\\%s#]+' % end, ttype),
+ (r'[\\#]', ttype),
+ (end, ttype, '#pop'),
+ ]
+
+ # braced quoted strings
+
+ for lbrace, rbrace, name in ('\\{', '\\}', 'cb'), \
+ ('\\[', '\\]', 'sb'), \
+ ('\\(', '\\)', 'pa'), \
+ ('<', '>', 'ab'):
+ states[name+'-intp-string'] = [
+ (r'\\[\\' + lbrace + rbrace + ']', String.Other),
+ (r'(?<!\\)' + lbrace, String.Other, '#push'),
+ (r'(?<!\\)' + rbrace, String.Other, '#pop'),
+ include('string-intp-escaped'),
+ (r'[\\#' + lbrace + rbrace + ']', String.Other),
+ (r'[^\\#' + lbrace + rbrace + ']+', String.Other),
+ ]
+ states['strings'].append((r'%[QWx]?' + lbrace, String.Other,
+ name+'-intp-string'))
+ states[name+'-string'] = [
+ (r'\\[\\' + lbrace + rbrace + ']', String.Other),
+ (r'(?<!\\)' + lbrace, String.Other, '#push'),
+ (r'(?<!\\)' + rbrace, String.Other, '#pop'),
+ (r'[\\#' + lbrace + rbrace + ']', String.Other),
+ (r'[^\\#' + lbrace + rbrace + ']+', String.Other),
+ ]
+ states['strings'].append((r'%[qsw]' + lbrace, String.Other,
+ name+'-string'))
+ states[name+'-regex'] = [
+ (r'\\[\\' + lbrace + rbrace + ']', String.Regex),
+ (r'(?<!\\)' + lbrace, String.Regex, '#push'),
+ (r'(?<!\\)' + rbrace + '[mixounse]*', String.Regex, '#pop'),
+ include('string-intp'),
+ (r'[\\#' + lbrace + rbrace + ']', String.Regex),
+ (r'[^\\#' + lbrace + rbrace + ']+', String.Regex),
+ ]
+ states['strings'].append((r'%r' + lbrace, String.Regex,
+ name+'-regex'))
+
+ # these must come after %<brace>!
+ states['strings'] += [
+ # %r regex
+ (r'(%r(.))(.*?)(\2[mixounse]*)', intp_regex_callback),
+ # regular fancy strings
+ (r'%[qsw](.).*?\1', String.Other),
+ (r'(%[QWx](.))(.*?)(\2)', intp_string_callback),
+ # special forms of fancy strings after operators or
+ # in method calls with braces
+ # we need to regexes here for " " and "\t" because of bygroups()
+ (r'(?<=[-+/*%=<>&!^|~,(])(\s*)(% .*? )',
+ bygroups(Text, String.Other)),
+ (r'(?<=[-+/*%=<>&!^|~,(])(\s*)(%\t.*?\t)',
+ bygroups(Text, String.Other)),
+ # and because of fixed with lookbehinds the whole thing a
+ # second time for line startings...
+ (r'^(\s*)(% .*? )',
+ bygroups(Text, String.Other)),
+ (r'^(\s*)(%\t.*?\t)',
+ bygroups(Text, String.Other)),
+ # all regular fancy strings
+ (r'(%([^a-zA-Z0-9\s]))(.*?)(\2)', intp_string_callback),
+ ]
+
+ return states
+
+ tokens = {
+ 'root': [
+ (r'#.*?$', Comment.Single),
+ (r'=begin\n.*?\n=end', Comment.Multiline),
+ (r'(BEGIN|END|alias|begin|break|case|defined\?|'
+ r'do|else|elsif|end|ensure|for|if|in|next|redo|'
+ r'rescue|raise|retry|return|super|then|undef|unless|until|when|'
+ r'while|yield)\b', Keyword),
+ (r'(initialize|new|loop|include|extend|raise|attr_reader|'
+ r'attr_writer|attr_accessor|attr|catch|throw|private|'
+ r'module_function|public|protected|true|false|nil)\b', Keyword.Pseudo),
+ (r'(not|and|or)\b', Operator.Word),
+ (r'(autoload|block_given|const_defined|eql|equal|frozen|include|'
+ r'instance_of|is_a|iterator|kind_of|method_defined|nil|'
+ r'private_method_defined|protected_method_defined|'
+ r'public_method_defined|respond_to|tainted)\?', Name.Builtin),
+ (r'(chomp|chop|exit|gsub|sub)!', Name.Builtin),
+ (r'(?<!\.)(Array|Float|Integer|String|__id__|__send__|abort|ancestors|'
+ r'at_exit|autoload|binding|callcc|caller|'
+ r'catch|chomp|chop|class_eval|class_variables|'
+ r'clone|const_defined\?|const_get|const_missing|const_set|constants|'
+ r'display|dup|eval|exec|exit|extend|fail|fork|'
+ r'format|freeze|getc|gets|global_variables|gsub|'
+ r'hash|id|included_modules|inspect|instance_eval|'
+ r'instance_method|instance_methods|'
+ r'instance_variable_get|instance_variable_set|instance_variables|'
+ r'lambda|load|local_variables|loop|'
+ r'method|method_missing|methods|module_eval|name|'
+ r'object_id|open|p|print|printf|private_class_method|'
+ r'private_instance_methods|'
+ r'private_methods|proc|protected_instance_methods|'
+ r'protected_methods|public_class_method|'
+ r'public_instance_methods|public_methods|'
+ r'putc|puts|raise|rand|readline|readlines|require|'
+ r'scan|select|self|send|set_trace_func|singleton_methods|sleep|'
+ r'split|sprintf|srand|sub|syscall|system|taint|'
+ r'test|throw|to_a|to_s|trace_var|trap|type|untaint|untrace_var|'
+ r'warn)\b', Name.Builtin),
+ (r'__(FILE|LINE)__\b', Name.Builtin.Pseudo),
+ # normal heredocs
+ (r'(<<-?)(["`\']?)([a-zA-Z_]\w*)(\2)(.*?\n)', heredoc_callback),
+ # empty string heredocs
+ (r'(<<-?)("|\')()(\2)(.*?\n)', heredoc_callback),
+ (r'__END__', Comment.Preproc, 'end-part'),
+ # multiline regex (after keywords or assignemnts)
+ (r'(?:^|(?<=[=<>~!])|'
+ r'(?<=(?:\s|;)when\s)|'
+ r'(?<=(?:\s|;)or\s)|'
+ r'(?<=(?:\s|;)and\s)|'
+ r'(?<=(?:\s|;|\.)index\s)|'
+ r'(?<=(?:\s|;|\.)scan\s)|'
+ r'(?<=(?:\s|;|\.)sub\s)|'
+ r'(?<=(?:\s|;|\.)sub!\s)|'
+ r'(?<=(?:\s|;|\.)gsub\s)|'
+ r'(?<=(?:\s|;|\.)gsub!\s)|'
+ r'(?<=(?:\s|;|\.)match\s)|'
+ r'(?<=(?:\s|;)if\s)|'
+ r'(?<=(?:\s|;)elsif\s)|'
+ r'(?<=^when\s)|'
+ r'(?<=^index\s)|'
+ r'(?<=^scan\s)|'
+ r'(?<=^sub\s)|'
+ r'(?<=^gsub\s)|'
+ r'(?<=^sub!\s)|'
+ r'(?<=^gsub!\s)|'
+ r'(?<=^match\s)|'
+ r'(?<=^if\s)|'
+ r'(?<=^elsif\s)'
+ r')(\s*)(/)(?!=)', bygroups(Text, String.Regex), 'multiline-regex'),
+ # multiline regex (in method calls)
+ (r'(?<=\(|,)/', String.Regex, 'multiline-regex'),
+ # multiline regex (this time the funny no whitespace rule)
+ (r'(\s+)(/[^\s=])', String.Regex, 'multiline-regex'),
+ # lex numbers and ignore following regular expressions which
+ # are division operators in fact (grrrr. i hate that. any
+ # better ideas?)
+ (r'(0_?[0-7]+(?:_[0-7]+)*)(\s*)(/)?',
+ bygroups(Number.Oct, Text, Operator)),
+ (r'(0x[0-9A-Fa-f]+(?:_[0-9A-Fa-f]+)*)(\s*)(/)?',
+ bygroups(Number.Hex, Text, Operator)),
+ (r'(0b[01]+(?:_[01]+)*)(\s*)(/)?',
+ bygroups(Number.Bin, Text, Operator)),
+ (r'([\d]+(?:_\d+)*)(\s*)(/)?',
+ bygroups(Number.Integer, Text, Operator)),
+ # Names
+ (r'@@[a-zA-Z_][a-zA-Z0-9_]*', Name.Variable.Class),
+ (r'@[a-zA-Z_][a-zA-Z0-9_]*', Name.Variable.Instance),
+ (r'\$[a-zA-Z0-9_]+', Name.Variable.Global),
+ (r'\$[!@&`\'+~=/\\,;.<>_*$?:"]', Name.Variable.Global),
+ (r'\$-[0adFiIlpvw]', Name.Variable.Global),
+ (r'::', Operator),
+ include('strings'),
+ # chars
+ (r'\?(\\[MC]-)*' # modifiers
+ r'(\\([\\abefnrstv#"\']|x[a-fA-F0-9]{1,2}|[0-7]{1,3})|\S)',
+ String.Char),
+ (r'[A-Z][a-zA-Z0-9_]+', Name.Constant),
+ # this is needed because ruby attributes can look
+ # like keywords (class) or like this: ` ?!?
+ (r'(?<=\.)([a-zA-Z_]\w*[\!\?]?|[*%&^`~+-/\[<>=])', Name),
+ # module name
+ (r'(module)(\s+)([a-zA-Z_]\w*)', bygroups(Keyword, Text, Name.Namespace)),
+ # start of function name, a bit tricky
+ (r'(def)(\s+)', bygroups(Keyword, Text), 'funcname'),
+ (r'def(?=[*%&^`~+-/\[<>=])', Keyword, 'funcname'),
+ (r'(class)(\s+)', bygroups(Keyword, Text), 'classname'),
+ (r'[a-zA-Z_][\w_]*[\!\?]?', Name),
+ (r'(\[\]|\*\*|<<|>>|>=|<=|<=>|=~|={3}|'
+ r'!~|&&?|\|\||\.{1,3})', Operator),
+ (r'[-+/*%=<>&!^|~]=?', Operator),
+ (r'[\[\](){}:;,<>/?\\]', Text),
+ (r'\s+', Text)
+ ],
+ 'funcname': [
+ (r'([a-zA-Z_][\w_]*[\!\?]?|\*\*?|[-+]@?|'
+ r'[/%&|^`~]|\[\]=?|<<|>>|<=?>|>=?|===?)', Name.Function, '#pop')
+ ],
+ 'classname': [
+ (r'<<', Operator, '#pop'),
+ (r'[a-zA-Z_][\w_]*', Name.Class, '#pop')
+ ],
+ 'in-intp': [
+ ('}', String.Interpol, '#pop'),
+ include('root'),
+ ],
+ 'string-intp': [
+ (r'#{', String.Interpol, 'in-intp'),
+ (r'#@@?[a-zA-Z_][a-zA-Z0-9_]*', String.Interpol),
+ (r'#\$[a-zA-Z_][a-zA-Z0-9_]*', String.Interpol)
+ ],
+ 'string-intp-escaped': [
+ include('string-intp'),
+ (r'\\([\\abefnrstv#"\']|x[a-fA-F0-9]{1,2}|[0-7]{1,3})', String.Escape)
+ ],
+ 'interpolated-regex': [
+ include('string-intp'),
+ (r'[\\#]', String.Regex),
+ (r'[^\\#]+', String.Regex),
+ ],
+ 'interpolated-string': [
+ include('string-intp'),
+ (r'[\\#]', String.Other),
+ (r'[^\\#]+', String.Other),
+ ],
+ 'multiline-regex': [
+ include('string-intp'),
+ (r'\\/', String.Regex),
+ (r'[\\#]', String.Regex),
+ (r'[^\\/#]+', String.Regex),
+ (r'/[mixounse]*', String.Regex, '#pop'),
+ ],
+ 'end-part': [
+ (r'.+', Comment.Preproc, '#pop')
+ ]
+ }
+ tokens.update(gen_rubystrings_rules())
+
+
+class RubyConsoleLexer(Lexer):
+ """
+ Parses Ruby console output like::
+
+ irb(main):001:0> a = 1
+ => 1
+ irb(main):002:0> puts a
+ 1
+ => nil
+ """
+ name = 'Ruby irb session'
+ aliases = ['rbcon', 'irb']
+
+ _prompt_re = re.compile('irb\([a-zA-Z_][a-zA-Z0-9_]*\):\d{3}:\d+[>*] ')
+
+ def get_tokens_unprocessed(self, text):
+ rblexer = RubyLexer(**self.options)
+
+ curcode = ''
+ insertions = []
+ for match in line_re.finditer(text):
+ line = match.group()
+ m = self._prompt_re.match(line)
+ if m is not None:
+ end = m.end()
+ insertions.append((len(curcode),
+ [(0, Generic.Prompt, line[:end])]))
+ curcode += line[end:]
+ else:
+ if curcode:
+ for item in do_insertions(insertions,
+ rblexer.get_tokens_unprocessed(curcode)):
+ yield item
+ curcode = ''
+ insertions = []
+ yield match.start(), Generic.Output, line
+ if curcode:
+ for item in do_insertions(insertions,
+ rblexer.get_tokens_unprocessed(curcode)):
+ yield item
+
+
+class PerlLexer(RegexLexer):
+ name = 'Perl'
+ aliases = ['perl', 'pl']
+ filenames = ['*.pl', '*.pm']
+
+ flags = re.DOTALL | re.MULTILINE
+ # TODO: give this a perl guy who knows how to parse perl...
+ tokens = {
+ 'root': [
+ (r'\#.*?$', Comment.Single),
+ (r'=[a-zA-Z0-9]+\s+.*\n[.\n]*?\n\s*=cut', Comment.Multiline),
+ (r'(case|continue|do|else|elsif|for|foreach|if|last|my|'
+ r'next|our|redo|reset|then|unless|until|while|use|'
+ r'print|new|BEGIN|END|return)\b', Keyword),
+ (r'(eq|lt|gt|le|ge|ne|not|and|or|cmp)\b', Operator.Word),
+ (r's/(\\\\|\\/|[^/])*/(\\\\|\\/|[^/])*/[egimosx]*', String.Regex),
+ (r'm?/(\\\\|\\/|[^/\n])*/[gcimosx]*', String.Regex),
+ (r'((?<==~)|(?<=\())\s*/(\\\\|\\/|[^/])*/[gcimosx]*', String.Regex),
+ (r'\s+', Text),
+ (r'(abs|accept|alarm|atan2|bind|binmode|bless|caller|chdir|'
+ r'chmod|chomp|chop|chown|chr|chroot|close|closedir|connect|'
+ r'continue|cos|crypt|dbmclose|dbmopen|defined|delete|die|'
+ r'dump|each|endgrent|endhostent|endnetent|endprotoent|'
+ r'endpwent|endservent|eof|eval|exec|exists|exit|exp|fcntl|'
+ r'fileno|flock|fork|format|formline|getc|getgrent|getgrgid|'
+ r'getgrnam|gethostbyaddr|gethostbyname|gethostent|getlogin|'
+ r'getnetbyaddr|getnetbyname|getnetent|getpeername|getpgrp|'
+ r'getppid|getpriority|getprotobyname|getprotobynumber|'
+ r'getprotoent|getpwent|getpwnam|getpwuid|getservbyname|'
+ r'getservbyport|getservent|getsockname|getsockopt|glob|gmtime|'
+ r'goto|grep|hex|import|index|int|ioctl|join|keys|kill|last|'
+ r'lc|lcfirst|length|link|listen|local|localtime|log|lstat|'
+ r'map|mkdir|msgctl|msgget|msgrcv|msgsnd|my|next|no|oct|open|'
+ r'opendir|ord|our|pack|package|pipe|pop|pos|printf|'
+ r'prototype|push|quotemeta|rand|read|readdir|'
+ r'readline|readlink|readpipe|recv|redo|ref|rename|require|'
+ r'reverse|rewinddir|rindex|rmdir|scalar|seek|seekdir|'
+ r'select|semctl|semget|semop|send|setgrent|sethostent|setnetent|'
+ r'setpgrp|setpriority|setprotoent|setpwent|setservent|'
+ r'setsockopt|shift|shmctl|shmget|shmread|shmwrite|shutdown|'
+ r'sin|sleep|socket|socketpair|sort|splice|split|sprintf|sqrt|'
+ r'srand|stat|study|substr|symlink|syscall|sysopen|sysread|'
+ r'sysseek|system|syswrite|tell|telldir|tie|tied|time|times|tr|'
+ r'truncate|uc|ucfirst|umask|undef|unlink|unpack|unshift|untie|'
+ r'utime|values|vec|wait|waitpid|wantarray|warn|write'
+ r'|y)\b', Name.Builtin),
+ (r'((__(DATA|DIE|WARN)__)|(STD(IN|OUT|ERR)))\b', Name.Builtin.Pseudo),
+ (r'<<([a-zA-Z_][a-zA-Z0-9_]*)\n.*?\n\1\n', String),
+ (r'__END__', Comment.Preproc, 'end-part'),
+ (r'\$\^[ADEFHILMOPSTWX]', Name.Variable.Global),
+ (r"\$[\\\"\[\]'&`+*.,;=%~?@$!<>(^|/-](?!\w)", Name.Variable.Global),
+ (r'[$@%#]+', Name.Variable, 'varname'),
+ (r'0_?[0-7]+(_[0-7]+)*', Number.Oct),
+ (r'\d+', Number.Integer),
+ (r'0x[0-9A-Fa-f]+(_[0-9A-Fa-f]+)*', Number.Hex),
+ (r'0b[01]+(_[01]+)*', Number.Bin),
+ (r"'(\\\\|\\'|[^'])*'", String),
+ (r'"(\\\\|\\"|[^"])*"', String),
+ (r'`(\\\\|\\`|[^`])*`', String.Backtick),
+ (r'(q|qq|qw|qr|qx)\{', String.Other, 'cb-string'),
+ (r'(q|qq|qw|qr|qx)\(', String.Other, 'rb-string'),
+ (r'(q|qq|qw|qr|qx)\[', String.Other, 'sb-string'),
+ (r'(q|qq|qw|qr|qx)\<', String.Other, 'lt-string'),
+ (r'(q|qq|qw|qr|qx)(.)[.\n]*?\1', String.Other),
+ (r'package\s+', Keyword, 'modulename'),
+ (r'sub\s+', Keyword, 'funcname'),
+ (r'(\[\]|\*\*|::|<<|>>|>=|<=|<=>|={3}|!=|=~|'
+ r'!~|&&?|\|\||\.{1,3})', Operator),
+ (r'[-+/*%=<>&^|!\\~]=?', Operator),
+ (r'[\(\)\[\]:;,<>/\?\{\}]', Text),
+ (r'(?=\w)', Name, 'name'),
+ ],
+ 'varname': [
+ (r'\s+', Text),
+ (r'\{', Text, '#pop'), # hash syntax?
+ (r'\)|,', Text, '#pop'), # argument specifier
+ (r'[a-zA-Z0-9_]+::', Name.Namespace),
+ (r'[a-zA-Z0-9_:]+', Name.Variable, '#pop'),
+ ],
+ 'name': [
+ (r'[a-zA-Z0-9_]+::', Name.Namespace),
+ (r'[a-zA-Z0-9_:]+', Name, '#pop'),
+ (r'[A-Z_]+(?=[^a-zA-Z0-9_])', Name.Constant, '#pop'),
+ (r'(?=[^a-zA-Z0-9_])', Text, '#pop'),
+ ],
+ 'modulename': [
+ (r'[a-zA-Z_][\w_]*', Name.Namespace, '#pop')
+ ],
+ 'funcname': [
+ (r'[a-zA-Z_][\w_]*[\!\?]?', Name.Function),
+ (r'\s+', Text),
+ # argument declaration
+ (r'\([$@%]*\)\s*', Text),
+ (r'.*?{', Text, '#pop'),
+ ],
+ 'cb-string': [
+ (r'\\[\{\}\\]', String.Other),
+ (r'\\', String.Other),
+ (r'\{', String.Other, 'cb-string'),
+ (r'\}', String.Other, '#pop'),
+ (r'[^\{\}\\]+', String.Other)
+ ],
+ 'rb-string': [
+ (r'\\[\(\)\\]', String.Other),
+ (r'\\', String.Other),
+ (r'\(', String.Other, 'rb-string'),
+ (r'\)', String.Other, '#pop'),
+ (r'[^\(\)]+', String.Other)
+ ],
+ 'sb-string': [
+ (r'\\[\[\]\\]', String.Other),
+ (r'\\', String.Other),
+ (r'\[', String.Other, 'sb-string'),
+ (r'\]', String.Other, '#pop'),
+ (r'[^\[\]]+', String.Other)
+ ],
+ 'lt-string': [
+ (r'\\[\<\>\\]', String.Other),
+ (r'\\', String.Other),
+ (r'\<', String.Other, 'lt-string'),
+ (r'\>', String.Other, '#pop'),
+ (r'[^\<\>]]+', String.Other)
+ ],
+ 'end-part': [
+ (r'.+', Comment.Preproc, '#pop')
+ ]
+ }
+
+
+class LuaLexer(RegexLexer):
+ name = 'Lua'
+ aliases = ['lua']
+ filenames = ['*.lua']
+
+ tokens = {
+ 'root': [
+ ('--.*$', Comment.Single),
+
+ (r'(?i)(\d*\.\d+|\d+\.\d*)(e[+-]?\d+)?', Number.Float),
+ (r'(?i)\d+e[+-]?\d+', Number.Float),
+ ('(?i)0x[0-9a-f]*', Number.Hex),
+ (r'\d+', Number.Integer),
+
+ (r'\n', Text),
+ (r'[^\S\n]', Text),
+ (r'[\[\]\{\}\(\)\.,:;]', Text),
+
+ (r'(==|~=|<=|>=|\.\.|\.\.\.|[=+\-*/%^<>#])', Operator),
+ (r'(and|or|not)\b', Operator.Word),
+
+ ('(break|do|else|elseif|end|for|if|in|repeat|return|then|until|'
+ r'while)\b', Keyword),
+ (r'(local)\b', Keyword.Declaration),
+ (r'(true|false|nil)\b', Keyword.Constant),
+
+ (r'(function)(\s+)', bygroups(Keyword, Text), 'funcname'),
+ (r'(class)(\s+)', bygroups(Keyword, Text), 'classname'),
+
+ (r'[A-Za-z_][A-Za-z0-9_]*(\.[A-Za-z_][A-Za-z0-9_]*)?', Name),
+
+ # multiline strings
+ (r'(?s)\[(=*)\[(.*?)\]\1\]', String),
+ ("'", String.Single, combined('stringescape', 'sqs')),
+ ('"', String.Double, combined('stringescape', 'dqs'))
+ ],
+
+ 'funcname': [
+ ('[A-Za-z_][A-Za-z0-9_]*', Name.Function, '#pop'),
+ # inline function
+ ('\(', Text, '#pop'),
+ ],
+
+ 'classname': [
+ ('[A-Za-z_][A-Za-z0-9_]*', Name.Class, '#pop')
+ ],
+
+ # if I understand correctly, every character is valid in a lua string,
+ # so this state is only for later corrections
+ 'string': [
+ ('.', String)
+ ],
+
+ 'stringescape': [
+ (r'''\\([abfnrtv\\"']|\d{1,3})''', String.Escape)
+ ],
+
+ 'sqs': [
+ ("'", String, '#pop'),
+ include('string')
+ ],
+
+ 'dqs': [
+ ('"', String, '#pop'),
+ include('string')
+ ]
+ }
+
+ def __init__(self, **options):
+ self.func_name_highlighting = get_bool_opt(
+ options, 'func_name_highlighting', True)
+ self.disabled_modules = get_list_opt(options, 'disabled_module', [])
+
+ self._functions = set()
+ if self.func_name_highlighting:
+ from pygments.lexers._luabuiltins import MODULES
+ for mod, func in MODULES.iteritems():
+ if mod not in self.disabled_modules:
+ self._functions.update(func)
+ RegexLexer.__init__(self, **options)
+
+ def get_tokens_unprocessed(self, text):
+ for index, token, value in \
+ RegexLexer.get_tokens_unprocessed(self, text):
+ if token is Name:
+ if value in self._functions:
+ yield index, Name.Function, value
+ continue
+ elif '.' in value:
+ a, b = value.split('.')
+ yield index, Name, a
+ yield index + len(a), Text, '.'
+ yield index + len(a) + 1, Name, b
+ continue
+ yield index, token, value
diff --git a/pygments/lexers/compiled.py b/pygments/lexers/compiled.py
new file mode 100644
index 00000000..2655c255
--- /dev/null
+++ b/pygments/lexers/compiled.py
@@ -0,0 +1,314 @@
+# -*- coding: utf-8 -*-
+"""
+ pygments.lexers.compiled
+ ~~~~~~~~~~~~~~~~~~~~~~~
+
+ Lexers for compiled languages: C/C++, Delphi, Java.
+
+ :copyright: 2006 by Georg Brandl, Armin Ronacher, Christoph Hack.
+ :license: GNU LGPL, see LICENSE for more details.
+"""
+
+import re
+
+from pygments.lexer import RegexLexer, include, bygroups, using, this
+from pygments.token import \
+ Text, Comment, Operator, Keyword, Name, String, Number
+
+
+__all__ = ['CLexer', 'CppLexer', 'DelphiLexer', 'JavaLexer']
+
+
+class CLexer(RegexLexer):
+ name = 'C'
+ aliases = ['c']
+ filenames = ['*.c', '*.h']
+
+ #: optional Comment or Whitespace
+ _ws = r'(?:\s|//.*?\n|/[*].*?[*]/)+'
+
+ tokens = {
+ 'whitespace': [
+ (r'^\s*#if\s+0', Comment.Preproc, 'if0'),
+ (r'^\s*#', Comment.Preproc, 'macro'),
+ (r'\n', Text),
+ (r'\s+', Text),
+ (r'\\\n', Text), # line continuation
+ (r'//.*?\n', Comment),
+ (r'/[*](.|\n)*?[*]/', Comment),
+ ],
+ 'statements': [
+ (r'L?"', String, 'string'),
+ (r"L?'(\\.|\\[0-7]{1,3}|\\x[a-fA-F0-9]{1,2}|[^\\\'\n])'", String.Char),
+ (r'(0x[0-9a-fA-F]|0[0-7]+|(\d+\.\d*|\.\d+)|\d+)'
+ r'e[+-]\d+[lL]?', Number.Float),
+ (r'0x[0-9a-fA-F]+[Ll]?', Number.Hex),
+ (r'0[0-7]+[Ll]?', Number.Oct),
+ (r'(\d+\.\d*|\.\d+)', Number.Float),
+ (r'\d+', Number.Integer),
+ (r'[~!%^&*()+=|\[\]:,.<>/?-]', Text),
+ (r'(auto|break|case|const|continue|default|do|else|enum|extern|'
+ r'for|goto|if|register|return|sizeof|static|struct|switch|typedef|'
+ r'union|volatile|virtual|while)\b', Keyword),
+ (r'(int|long|float|short|double|char|unsigned|signed|void)\b',
+ Keyword.Type),
+ (r'(_{0,2}inline|naked|restrict|thread|typename)\b', Keyword.Reserved),
+ (r'__(asm|int8|based|except|int16|stdcall|cdecl|fastcall|int32|'
+ r'declspec|finally|int64|try|leave)\b', Keyword.Reserved),
+ (r'(true|false|NULL)\b', Keyword.Constant),
+ ('[a-zA-Z_][a-zA-Z0-9_]*', Name),
+ ],
+ 'root': [
+ include('whitespace'),
+ # functions
+ (r'((?:[a-zA-Z0-9_*\s])+?(?:\s|[*]))' # return arguments
+ r'([a-zA-Z_][a-zA-Z0-9_]*)' # method name
+ r'(\s*\([^;]*?\))' # signature
+ r'(' + _ws + r')({)',
+ bygroups(using(this), Name.Function, using(this), Text, Keyword),
+ 'function'),
+ # function declarations
+ (r'((?:[a-zA-Z0-9_*\s])+?(?:\s|[*]))' # return arguments
+ r'([a-zA-Z_][a-zA-Z0-9_]*)' # method name
+ r'(\s*\([^;]*?\))' # signature
+ r'(' + _ws + r')(;)',
+ bygroups(using(this), Name.Function, using(this), Text, Text)),
+ ('', Text, 'statement'),
+ ],
+ 'statement' : [
+ include('whitespace'),
+ include('statements'),
+ ('[{}]', Keyword),
+ (';', Text, '#pop'),
+ ],
+ 'function': [
+ include('whitespace'),
+ include('statements'),
+ (';', Text),
+ ('{', Keyword, '#push'),
+ ('}', Keyword, '#pop'),
+ ],
+ 'string': [
+ (r'"', String, '#pop'),
+ (r'\\([\\abfnrtv"\']|x[a-fA-F0-9]{2,4}|[0-7]{1,3})', String.Escape),
+ (r'[^\\"\n]+', String), # all other characters
+ (r'\\\n', String), # line continuation
+ (r'\\', String), # stray backslash
+ ],
+ 'macro': [
+ (r'[^/\n]+', Comment.Preproc),
+ (r'/[*](.|\n)*?[*]/', Comment),
+ (r'//.*?\n', Comment, '#pop'),
+ (r'/', Comment.Preproc),
+ (r'(?<=\\)\n', Comment.Preproc),
+ (r'\n', Comment.Preproc, '#pop'),
+ ],
+ 'if0': [
+ (r'^\s*#if.*?(?<!\\)\n', Comment, '#push'),
+ (r'^\s*#endif.*?(?<!\\)\n', Comment, '#pop'),
+ (r'.*?\n', Comment),
+ ]
+ }
+
+
+class CppLexer(RegexLexer):
+ name = 'C++'
+ aliases = ['cpp', 'c++']
+ filenames = ['*.cpp', '*.hpp', '*.c++', '*.h++']
+
+ tokens = {
+ 'root': [
+ (r'^\s*#if\s+0', Comment.Preproc, 'if0'),
+ (r'^\s*#', Comment.Preproc, 'macro'),
+ (r'\n', Text),
+ (r'\s+', Text),
+ (r'\\\n', Text), # line continuation
+ (r'//.*?\n', Comment),
+ (r'/[*](.|\n)*?[*]/', Comment),
+ (r'[{}]', Keyword),
+ (r'L?"', String, 'string'),
+ (r"L?'(\\.|\\[0-7]{1,3}|\\x[a-fA-F0-9]{1,2}|[^\\\'\n])'", String.Char),
+ (r'(0x[0-9a-fA-F]|0[0-7]+|(\d+\.\d*|\.\d+)|\d+)'
+ r'e[+-]\d+[lL]?', Number.Float),
+ (r'0x[0-9a-fA-F]+[Ll]?', Number.Hex),
+ (r'0[0-7]+[Ll]?', Number.Oct),
+ (r'(\d+\.\d*|\.\d+)', Number.Float),
+ (r'\d+', Number.Integer),
+ (r'[~!%^&*()+=|\[\]:;,.<>/?-]', Text),
+ (r'(asm|auto|break|case|catch|const|const_cast|continue|'
+ r'default|delete|do|dynamic_cast|else|enum|explicit|export|'
+ r'extern|for|friend|goto|if|mutable|namespace|new|operator|'
+ r'private|protected|public|register|reinterpret_cast|return|'
+ r'sizeof|static|static_cast|struct|switch|template|this|throw|'
+ r'throws|try|typedef|typeid|typename|union|using|volatile|'
+ r'virtual|while)\b', Keyword),
+ (r'(class)(\s+)', bygroups(Keyword, Text), 'classname'),
+ (r'(bool|int|long|float|short|double|char|unsigned|signed|'
+ r'void|wchar_t)\b', Keyword.Type),
+ (r'(_{0,2}inline|naked|thread)\b', Keyword.Reserved),
+ (r'__(asm|int8|based|except|int16|stdcall|cdecl|fastcall|int32|'
+ r'declspec|finally|int64|try|leave|wchar_t|w64|virtual_inheritance|'
+ r'uuidof|unaligned|super|single_inheritance|raise|noop|'
+ r'multiple_inheritance|m128i|m128d|m128|m64|interface|'
+ r'identifier|forceinline|event|assume)\b', Keyword.Reserved),
+ (r'(true|false|NULL)\b', Keyword.Constant),
+ ('[a-zA-Z_][a-zA-Z0-9_]*', Name),
+ ],
+ 'classname': [
+ (r'[a-zA-Z_][a-zA-Z0-9_]*', Name.Class, '#pop')
+ ],
+ 'string': [
+ (r'"', String, '#pop'),
+ (r'\\([\\abfnrtv"\']|x[a-fA-F0-9]{2,4}|[0-7]{1,3})', String.Escape),
+ (r'[^\\"\n]+', String), # all other characters
+ (r'\\\n', String), # line continuation
+ (r'\\', String), # stray backslash
+ ],
+ 'macro': [
+ (r'[^/\n]+', Comment.Preproc),
+ (r'/[*](.|\n)*?[*]/', Comment),
+ (r'//.*?\n', Comment, '#pop'),
+ (r'/', Comment.Preproc),
+ (r'(?<=\\)\n', Comment.Preproc),
+ (r'\n', Comment.Preproc, '#pop'),
+ ],
+ 'if0': [
+ (r'^\s*#if.*?(?<!\\)\n', Comment, '#push'),
+ (r'^\s*#endif.*?(?<!\\)\n', Comment, '#pop'),
+ (r'.*?\n', Comment),
+ ]
+ }
+
+
+class DelphiLexer(RegexLexer):
+ name = 'Delphi'
+ aliases = ['delphi', 'pas', 'pascal', 'objectpascal']
+ filenames = ['*.pas']
+
+ flags = re.IGNORECASE | re.MULTILINE | re.DOTALL
+ tokens = {
+ 'root': [
+ (r'\s+', Text),
+ (r'asm\b', Keyword, 'asm'),
+ (r'(uses)(\s+)', bygroups(Keyword, Text), 'uses'),
+ (r'(procedure|function)(\s+)', bygroups(Keyword, Text), 'funcname'),
+ (r'(abstract|and|array|as|assembler|at|begin|case|cdecl|'
+ r'class|const|constructor|contains|destructor|dispinterface|'
+ r'div|do|downto|else|end|except|false|far|file|finalization|'
+ r'finally|for|goto|if|implementation|in|inherited|'
+ r'initialization|inline|interface|is|label|mod|near|nil|not|'
+ r'object|of|on|or|overload|override|package|packed|pascal|'
+ r'private|program|property|protected|public|'
+ r'published|raise|record|register|repeat|requires|resourcestring|'
+ r'safecall|self|set|shl|shr|stdcall|then|threadvar|to|true|try|'
+ r'type|unit|until|uses|var|varargs|virtual|while|with|xor|'
+ r'break|assert|dec|inc)\b', Keyword),
+ (r'(AnsiString|Boolean|Byte|ByteBool|Cardinal|Char|Comp|'
+ r'Currency|Double|Extended|Int64|Integer|LongBool|LongInt|Real|'
+ r'Real48|ShortInt|ShortString|Single|SmallInt|String|WideChar|'
+ r'WideString|Word|WordBool)\b', Keyword.Type),
+ (r'\{.*?\}', Comment),
+ (r'\(\*.*?\*\)', Comment),
+ (r'//.*?\n', Comment),
+ (r"'(''|[^']*)'", String),
+ (r'\$[0-9a-fA-F]+', Number),
+ (r'\#\$?[0-9]{1,3}', Number),
+ (r'[0-9]', Number),
+ (r'[@~!%^&*()+=|\[\]:;,.<>/?-]', Text),
+ (r'[a-zA-Z_][a-zA-Z0-9_]*', Name)
+ ],
+ 'uses': [
+ (r'[a-zA-Z_][a-zA-Z0-9_.]*', Name.Namespace),
+ (r'\s*,\s*', Text),
+ (r';', Text, '#pop')
+ ],
+ 'funcname': [
+ (r'[a-zA-Z_][a-zA-Z0-9_.]*', Name.Function, '#pop')
+ ],
+ 'asm': [
+ (r'end', Keyword, '#pop'),
+ (r'\s+', Text),
+ (r'\{.*?\}', Comment),
+ (r'\(\*.*?\*\)', Comment),
+ (r'//.*?\n', Comment),
+ (r'(AAA|AAD|AAM|AAS|ADC|ADD|AND|ARPL|BOUND|BSF|BSR|BSWAP|BT|'
+ r'BTC|BTR|BTS|CALL|CBW|CDQ|CLC|CLD|CLI|CLTS|CMC|CMP|CMPSB|'
+ r'CMPSD|CMPSW|CMPXCHG|CMPXCHG486|CMPXCHG8B|CPUID|CWD|CWDE|'
+ r'DAA|DAS|DEC|DIV|EMMS|ENTER|HLT|IBTS|ICEBP|IDIV|IMUL|IN|INC|'
+ r'INSB|INSD|INSW|INT|INT01|INT03|INT1|INT3|INTO|INVD|INVLPG|'
+ r'IRET|IRETD|IRETW|JCXZ|JECXZ|JMP|LAHF|LAR|LCALL|LDS|LEA|LEAVE|'
+ r'LES|LFS|LGDT|LGS|LIDT|LJMP|LLDT|LMSW|LOADALL|LOADALL286|LOCK|'
+ r'LODSB|LODSD|LODSW|LOOP|LOOPE|LOOPNE|LOOPNZ|LOOPZ|LSL|LSS|LTR|'
+ r'MOV|MOVD|MOVQ|MOVSB|MOVSD|MOVSW|MOVSX|MOVZX|MUL|NEG|NOP|NOT|'
+ r'OR|OUT|OUTSB|OUTSD|OUTSW|POP|POPA|POPAD|POPAW|POPF|POPFD|'
+ r'POPFW|PUSH|PUSHA|PUSHAD|PUSHAW|PUSHF|PUSHFD|PUSHFW|RCL|RCR|'
+ r'RDMSR|RDPMC|RDSHR|RDTSC|REP|REPE|REPNE|REPNZ|REPZ|RET|RETF|'
+ r'RETN|ROL|ROR|RSDC|RSLDT|RSM|SAHF|SAL|SALC|SAR|SBB|SCASB|SCASD|'
+ r'SCASW|SGDT|SHL|SHLD|SHR|SHRD|SIDT|SLDT|SMI|SMINT|SMINTOLD|'
+ r'SMSW|STC|STD|STI|STOSB|STOSD|STOSW|STR|SUB|SVDC|SVLDT|SVTS|'
+ r'SYSCALL|SYSENTER|SYSEXIT|SYSRET|TEST|UD1|UD2|UMOV|VERR|VERW|'
+ r'WAIT|WBINVD|WRMSR|WRSHR|XADD|XBTS|XCHG|XLAT|XLATB|XOR|cmova|'
+ r'cmovae|cmovb|cmovbe|cmovc|cmovcxz|cmove|cmovg|cmovge|cmovl|'
+ r'cmovle|cmovna|cmovnae|cmovnb|cmovnbe|cmovnc|cmovne|cmovng|'
+ r'cmovnge|cmovnl|cmovnle|cmovno|cmovnp|cmovns|cmovnz|cmovo|'
+ r'cmovp|cmovpe|cmovpo|cmovs|cmovz|ja|jae|jb|jbe|jc|jcxz|je|jg|'
+ r'jge|jl|jle|jna|jnae|jnb|jnbe|jnc|jne|jng|jnge|jnl|jnle|jno|'
+ r'jnp|jns|jnz|jo|jp|jpe|jpo|js|jz|seta|setae|setb|setbe|setc|'
+ r'setcxz|sete|setg|setge|setl|setle|setna|setnae|setnb|setnbe|'
+ r'setnc|setne|setng|setnge|setnl|setnle|setno|setnp|setns|setnz|'
+ r'seto|setp|setpe|setpo|sets|setz)\b', Keyword),
+ ('[a-zA-Z_@][a-zA-Z0-9_]*', Name),
+ (r'\$[0-9]+', Number),
+ (r"'(''|[^']+)'", String),
+ (r'.', Text)
+ ]
+ }
+
+
+class JavaLexer(RegexLexer):
+ name = 'Java'
+ aliases = ['java']
+ filenames = ['*.java']
+
+ flags = re.MULTILINE | re.DOTALL
+
+ #: optional Comment or Whitespace
+ _ws = r'(?:\s|//.*?\n|/[*].*?[*]/)+'
+
+ tokens = {
+ 'root': [
+ # method names
+ (r'^(\s*(?:[a-zA-Z_][a-zA-Z0-9_\.]*\s+)+?)' # return arguments
+ r'([a-zA-Z_][a-zA-Z0-9_]*)' # method name
+ r'(\s*\([^;]*?\))' # signature
+ r'(?=' + _ws + # exception declaration
+ r'(?:throws\s+(?:[a-zA-Z_][a-zA-Z0-9_]*,?\s*)+)?' +
+ _ws + r'\{)',
+ bygroups(using(this), Name.Function, using(this))),
+ (r'[^\S\n]+', Text),
+ (r'//.*?\n', Comment),
+ (r'/\*.*?\*/', Comment),
+ (r'@[a-zA-Z_][a-zA-Z0-9_\.]*', Name.Decorator),
+ (r'(abstract|assert|break|case|catch|'
+ r'const|continue|default|do|else|enum|extends|final|'
+ r'finally|for|if|goto|implements|import|instanceof|'
+ r'interface|native|new|package|private|protected|public|'
+ r'return|static|strictfp|super|switch|synchronized|this|'
+ r'throw|throws|transient|try|volatile|while)\b', Keyword),
+ (r'(boolean|byte|char|double|float|int|long|short|void)\b',
+ Keyword.Type),
+ (r'(true|false|null)\b', Keyword.Constant),
+ (r'(class)(\s+)', bygroups(Keyword, Text), 'class'),
+ (r'"(\\\\|\\"|[^"])*"', String),
+ (r"'\\.'|'[^\\]'|'\\u[0-9a-f]{4}'", String.Char),
+ (r'[a-zA-Z_\$][a-zA-Z0-9_]*', Name),
+ (r'[~\^\*!%&\[\]\(\)\{\}<>\|+=:;,./?-]', Operator),
+ (r'[0-9][0-9]*\.[0-9]+([eE][0-9]+)?[fd]?', Number),
+ (r'[0-9]+L?', Number),
+ (r'0x[0-9a-f]+', Number),
+ (r'\n', Text)
+ ],
+ 'class': [
+ (r'[a-zA-Z_][a-zA-Z0-9_]*', Name.Class, '#pop')
+ ]
+ }
diff --git a/pygments/lexers/dotnet.py b/pygments/lexers/dotnet.py
new file mode 100644
index 00000000..664af07e
--- /dev/null
+++ b/pygments/lexers/dotnet.py
@@ -0,0 +1,221 @@
+# -*- coding: utf-8 -*-
+"""
+ pygments.lexers.dotnet
+ ~~~~~~~~~~~~~~~~~~~~~
+
+ .net languages
+
+ :copyright: 2006 by Armin Ronacher.
+ :license: GNU LGPL, see LICENSE for more details.
+"""
+import re
+
+from pygments.lexer import RegexLexer, bygroups, using, this
+from pygments.token import \
+ Text, Comment, Operator, Keyword, Name, String, Number, Literal
+
+__all__ = ['CSharpLexer', 'BooLexer', 'VbNetLexer']
+
+
+class CSharpLexer(RegexLexer):
+ name = 'C#'
+ aliases = ['csharp', 'c#']
+ filenames = ['*.cs']
+
+ flags = re.MULTILINE | re.DOTALL
+
+ #: optional Comment or Whitespace
+ _ws = r'(?:\s|//.*?\n|/[*].*?[*]/)+'
+
+ tokens = {
+ 'root': [
+ # method names
+ (r'^([ \t]*(?:[a-zA-Z_][a-zA-Z0-9_\.]*\s+)+?)' # return arguments
+ r'([a-zA-Z_][a-zA-Z0-9_]*)' # method name
+ r'(\s*\([^;]*?\))' # signature
+ r'(?=' + _ws + '\{)', # lookahead for {
+ bygroups(using(this), Name.Function, using(this))),
+ # properties
+ (r'^([ \t]*(?:[a-zA-Z_][a-zA-Z0-9_\.]*\s+)+?)' # return arguments
+ r'([a-zA-Z_][a-zA-Z0-9_]*)' # property name
+ r'(?=' + _ws + r'\{' + _ws + # lookahead for
+ r'(?:get|set)' + _ws + r'\{)', # get/set
+ bygroups(using(this), Name.Function)),
+ (r'^\s*\[.*?\]', Name.Attribute),
+ (r'[^\S\n]+', Text),
+ (r'\\\n', Text), # line continuation
+ (r'//.*?\n', Comment),
+ (r'/[*](.|\n)*?[*]/', Comment),
+ (r'\n', Text),
+ (r'[~!%^&*()+=|\[\]:;,.<>/?-]', Text),
+ (r'[{}]', Keyword),
+ (r'"(\\\\|\\"|[^"])*"', String),
+ (r"'\\.'|'[^\\]'", String.Char),
+ (r"[0-9](\.[0-9]*)?([eE][+-][0-9]+)?"
+ r"[flFLdD]?|0[xX][0-9a-fA-F]+[Ll]?", Number),
+ (r'#\s+(if|endif|else|elif|define|undef|'
+ r'line|error|warning|region|endregion)', Comment.Preproc),
+ (r'(abstract|case|as|base|break|case|catch|'
+ r'checked|const|continue|default|delegate|'
+ r'do|else|enum|event|explicit|extern|false|finally|'
+ r'fixed|for|foreach|goto|if|implicit|in|interface|'
+ r'internal|is|lock|nwe|null|operator|'
+ r'out|override|params|private|protected|public|readonly|'
+ r'ref|return|sealed|sizeof|stackalloc|static|'
+ r'switch|this|throw|true|try|typeof|'
+ r'unchecked|unsafe|virtual|void|while|'
+ r'get|set|new)\b', Keyword),
+ (r'(bool|byte|char|decimal|double|float|int|long|object|sbyte|'
+ r'short|string|uint|ulong|ushort)\b', Keyword.Type),
+ (r'(class|struct)(\s+)', bygroups(Keyword, Text), 'class'),
+ (r'(namespace|using)(\s+)', bygroups(Keyword, Text), 'namespace'),
+ ('[a-zA-Z_][a-zA-Z0-9_]*', Name),
+ ],
+ 'class': [
+ (r'[a-zA-Z_][a-zA-Z0-9_]*', Name.Class, '#pop')
+ ],
+ 'namespace': [
+ (r'[a-zA-Z_][a-zA-Z0-9_.]*', Name.Namespace, '#pop')
+ ]
+ }
+
+
+class BooLexer(RegexLexer):
+ name = 'Boo'
+ aliases = ['boo']
+ filenames = ['*.boo']
+
+ tokens = {
+ 'root': [
+ (r'\s+', Text),
+ (r'(#|//).*$', Comment),
+ (r'/[*]', Comment, 'comment'),
+ (r'[]{}:(),.;[]', Text),
+ (r'\\\n', Text),
+ (r'\\', Text),
+ (r'(in|is|and|or|not)\b', Operator.Word),
+ (r'/(\\\\|\\/|[^/\s])/', String.Regex),
+ (r'@/(\\\\|\\/|[^/])*/', String.Regex),
+ (r'=~|!=|==|<<|>>|[-+/*%=<>&^|]', Operator),
+ (r'(as|abstract|callable|constructor|destructor|do|import|'
+ r'enum|event|final|get|interface|internal|of|override|'
+ r'partial|private|protected|public|return|set|static|'
+ r'struct|transient|virtual|yield|super|and|break|cast|'
+ r'continue|elif|else|ensure|except|for|given|goto|if|in|'
+ r'is|isa|not|or|otherwise|pass|raise|ref|try|unless|when|'
+ r'while|from|as)\b', Keyword),
+ (r'def(?=\s+\(.*?\))', Keyword),
+ (r'(def)(\s+)', bygroups(Keyword, Text), 'funcname'),
+ (r'(class)(\s+)', bygroups(Keyword, Text), 'classname'),
+ (r'(namespace)(\s+)', bygroups(Keyword, Text), 'namespace'),
+ (r'(?<!\.)(true|false|null|self|__eval__|__switch__|array|'
+ r'assert|checked|enumerate|filter|getter|len|lock|map|'
+ r'matrix|max|min|normalArrayIndexing|print|property|range|'
+ r'rawArrayIndexing|required|typeof|unchecked|using|'
+ r'yieldAll|zip)\b', Name.Builtin),
+ ('"""(\\\\|\\"|.*?)"""', String.Double),
+ ('"(\\\\|\\"|[^"]*?)"', String.Double),
+ ("'(\\\\|\\'|[^']*?)'", String.Single),
+ ('[a-zA-Z_][a-zA-Z0-9_]*', Name),
+ (r'(\d+\.\d*|\d*\.\d+)([fF][+-]?[0-9]+)?', Number.Float),
+ (r'[0-9][0-9\.]*(m|ms|d|h|s)', Number),
+ (r'0\d+', Number.Oct),
+ (r'0x[a-fA-F0-9]+', Number.Hex),
+ (r'\d+L', Number.Integer.Long),
+ (r'\d+', Number.Integer),
+ ],
+ 'comment': [
+ ('/[*]', Comment.Multiline, '#push'),
+ ('[*]/', Comment.Multiline, '#pop'),
+ ('[^/*]', Comment.Multiline),
+ ('[*/]', Comment.Multiline)
+ ],
+ 'funcname': [
+ ('[a-zA-Z_][a-zA-Z0-9_]*', Name.Function, '#pop')
+ ],
+ 'classname': [
+ ('[a-zA-Z_][a-zA-Z0-9_]*', Name.Class, '#pop')
+ ],
+ 'namespace': [
+ ('[a-zA-Z_][a-zA-Z0-9_.]*', Name.Namespace, '#pop')
+ ]
+ }
+
+
+class VbNetLexer(RegexLexer):
+ name = 'VB.net'
+ aliases = ['vb.net', 'vbnet']
+ filenames = ['*.vb', '*.bas']
+
+ flags = re.MULTILINE | re.IGNORECASE
+ tokens = {
+ 'root': [
+ (r'^\s*<.*?>', Name.Attribute),
+ (r'\s+', Text),
+ (r'\n', Text),
+ (r'rem\b.*?\n', Comment),
+ (r"'.*?\n", Comment),
+ (r'[\(\){}!#,.:]', Text),
+ (r'#If\s.*?\sThen|#ElseIf\s.*?\sThen|#End\s+If|#Const|'
+ r'#ExternalSource.*?\n|#End\s+ExternalSource|'
+ r'#Region.*?\n|#End\s+Region|#ExternalChecksum',
+ Comment.Preproc),
+ (r'Option\s+(Strict|Explicit|Compare)\s+'
+ r'(On|Off|Binary|Text)', Keyword.Declaration),
+ (r'(?<!\.)(AddHandler|Alias|'
+ r'ByRef|ByVal|Call|Case|Catch|CBool|CByte|CChar|CDate|'
+ r'CDec|CDbl|CInt|CLng|CObj|Const|Continue|CSByte|CShort|'
+ r'CSng|CStr|CType|CUInt|CULng|CUShort|Declare|'
+ r'Default|Delegate|Dim|DirectCast|Do|Each|Else|ElseIf|'
+ r'End|EndIf|Enum|Erase|Error|Event|Exit|False|Finally|For|'
+ r'Friend|Function|Get|Global|GoSub|GoTo|Handles|If|'
+ r'Implements|Imports|Inherits|Interface|'
+ r'Let|Lib|Loop|Me|Module|MustInherit|'
+ r'MustOverride|MyBase|MyClass|Namespace|Narrowing|New|Next|'
+ r'Not|Nothing|NotInheritable|NotOverridable|Of|On|'
+ r'Operator|Option|Optional|Overloads|Overridable|'
+ r'Overrides|ParamArray|Partial|Private|Property|Protected|'
+ r'Public|RaiseEvent|ReadOnly|ReDim|RemoveHandler|Resume|'
+ r'Return|Select|Set|Shadows|Shared|Single|'
+ r'Static|Step|Stop|Structure|Sub|SyncLock|Then|'
+ r'Throw|To|True|Try|TryCast|Wend|'
+ r'Using|When|While|Widening|With|WithEvents|'
+ r'WriteOnly)\b', Keyword),
+ (r'(?<!\.)(Function|Sub|Property)(\s+)',
+ bygroups(Keyword, Text), 'funcname'),
+ (r'(?<!\.)(Class|Structure|Enum)(\s+)',
+ bygroups(Keyword, Text), 'classname'),
+ (r'(?<!\.)(Namespace|Imports)(\s+)',
+ bygroups(Keyword, Text), 'namespace'),
+ (r'(?<!\.)(Boolean|Byte|Char|Date|Decimal|Double|Integer|Long|'
+ r'Object|SByte|Short|Single|String|Variant|UInteger|ULong|'
+ r'UShort)\b', Keyword.Type),
+ (r'(?<!\.)(AddressOf|And|AndAlso|As|GetType|In|Is|IsNot|Like|Mod|'
+ r'Or|OrElse|TypeOf|Xor)\b', Operator.Word),
+ (r'&=|[*]=|/=|\\=|\^=|\+=|-=|<<=|>>=|<<|>>|:=|'
+ r'<=|>=|<>|[-&*/\\^+=<>]',
+ Operator),
+ ('"', String, 'string'),
+ ('[a-zA-Z_][a-zA-Z0-9_]*[%&@!#$]?', Name),
+ ('#.*?#', Literal.Date),
+ (r'(\d+\.\d*|\d*\.\d+)([fF][+-]?[0-9]+)?', Number.Float),
+ (r'\d+([SILDFR]|US|UI|UL)?', Number.Integer),
+ (r'&H[0-9a-f]+([SILDFR]|US|UI|UL)?', Number.Integer),
+ (r'&O[0-7]+([SILDFR]|US|UI|UL)?', Number.Integer),
+ (r'_\n', Text), # Line continuation
+ ],
+ 'string': [
+ (r'""', String),
+ (r'"C?', String, '#pop'),
+ (r'[^"]+', String),
+ ],
+ 'funcname': [
+ (r'[a-z_][a-z0-9_]*', Name.Function, '#pop')
+ ],
+ 'classname': [
+ (r'[a-z_][a-z0-9_]*', Name.Class, '#pop')
+ ],
+ 'namespace': [
+ (r'[a-z_][a-z0-9_.]*', Name.Namespace, '#pop')
+ ],
+ }
diff --git a/pygments/lexers/other.py b/pygments/lexers/other.py
new file mode 100644
index 00000000..3c0f0b13
--- /dev/null
+++ b/pygments/lexers/other.py
@@ -0,0 +1,137 @@
+# -*- coding: utf-8 -*-
+"""
+ pygments.lexers.other
+ ~~~~~~~~~~~~~~~~~~~~
+
+ Lexers for other languages: SQL, BrainFuck.
+
+ :copyright: 2006 by Georg Brandl.
+ :license: GNU LGPL, see LICENSE for more details.
+"""
+
+import re
+
+from pygments.lexer import Lexer, RegexLexer
+from pygments.token import Token, \
+ Text, Comment, Operator, Keyword, Name, String, Number
+
+
+__all__ = ['SqlLexer', 'BrainfuckLexer']
+
+
+class SqlLexer(RegexLexer):
+ name = 'SQL'
+ aliases = ['sql']
+ filenames = ['*.sql']
+
+ flags = re.IGNORECASE
+ tokens = {
+ 'root': [
+ (r'\s+', Text),
+ (r'--.*?\n', Comment.Single),
+ (r'/\*', Comment.Multiline, 'multiline-comments'),
+ (r'(ABORT|ABS|ABSOLUTE|ACCESS|ADA|ADD|ADMIN|AFTER|AGGREGATE|'
+ r'ALIAS|ALL|ALLOCATE|ALTER|ANALYSE|ANALYZE|AND|ANY|ARE|AS|'
+ r'ASC|ASENSITIVE|ASSERTION|ASSIGNMENT|ASYMMETRIC|AT|ATOMIC|'
+ r'AUTHORIZATION|AVG|BACKWARD|BEFORE|BEGIN|BETWEEN|BITVAR|'
+ r'BIT_LENGTH|BOTH|BREADTH|BY|C|CACHE|CALL|CALLED|CARDINALITY|'
+ r'CASCADE|CASCADED|CASE|CAST|CATALOG|CATALOG_NAME|CHAIN|'
+ r'CHARACTERISTICS|CHARACTER_LENGTH|CHARACTER_SET_CATALOG|'
+ r'CHARACTER_SET_NAME|CHARACTER_SET_SCHEMA|CHAR_LENGTH|CHECK|'
+ r'CHECKED|CHECKPOINT|CLASS|CLASS_ORIGIN|CLOB|CLOSE|CLUSTER|'
+ r'COALSECE|COBOL|COLLATE|COLLATION|COLLATION_CATALOG|'
+ r'COLLATION_NAME|COLLATION_SCHEMA|COLUMN|COLUMN_NAME|'
+ r'COMMAND_FUNCTION|COMMAND_FUNCTION_CODE|COMMENT|COMMIT|'
+ r'COMMITTED|COMPLETION|CONDITION_NUMBER|CONNECT|CONNECTION|'
+ r'CONNECTION_NAME|CONSTRAINT|CONSTRAINTS|CONSTRAINT_CATALOG|'
+ r'CONSTRAINT_NAME|CONSTRAINT_SCHEMA|CONSTRUCTOR|CONTAINS|'
+ r'CONTINUE|CONVERSION|CONVERT|COPY|CORRESPONTING|COUNT|'
+ r'CREATE|CREATEDB|CREATEUSER|CROSS|CUBE|CURRENT|CURRENT_DATE|'
+ r'CURRENT_PATH|CURRENT_ROLE|CURRENT_TIME|CURRENT_TIMESTAMP|'
+ r'CURRENT_USER|CURSOR|CURSOR_NAME|CYCLE|DATA|DATABASE|'
+ r'DATETIME_INTERVAL_CODE|DATETIME_INTERVAL_PRECISION|DAY|'
+ r'DEALLOCATE|DECLARE|DEFAULT|DEFAULTS|DEFERRABLE|DEFERRED|'
+ r'DEFINED|DEFINER|DELETE|DELIMITER|DELIMITERS|DEREF|DESC|'
+ r'DESCRIBE|DESCRIPTOR|DESTROY|DESTRUCTOR|DETERMINISTIC|'
+ r'DIAGNOSTICS|DICTIONARY|DISCONNECT|DISPATCH|DISTINCT|DO|'
+ r'DOMAIN|DROP|DYNAMIC|DYNAMIC_FUNCTION|DYNAMIC_FUNCTION_CODE|'
+ r'EACH|ELSE|ENCODING|ENCRYPTED|END|END-EXEC|EQUALS|ESCAPE|EVERY|'
+ r'EXCEPT|ESCEPTION|EXCLUDING|EXCLUSIVE|EXEC|EXECUTE|EXISTING|'
+ r'EXISTS|EXPLAIN|EXTERNAL|EXTRACT|FALSE|FETCH|FINAL|FIRST|FOR|'
+ r'FORCE|FOREIGN|FORTRAN|FORWARD|FOUND|FREE|FREEZE|FROM|FULL|'
+ r'FUNCTION|G|GENERAL|GENERATED|GET|GLOBAL|GO|GOTO|GRANT|GRANTED|'
+ r'GROUP|GROUPING|HANDLER|HAVING|HIERARCHY|HOLD|HOST|IDENTITY|'
+ r'IGNORE|ILIKE|IMMEDIATE|IMMUTABLE|IMPLEMENTATION|IMPLICIT|IN|'
+ r'INCLUDING|INCREMENT|INDEX|INDITCATOR|INFIX|INHERITS|INITIALIZE|'
+ r'INITIALLY|INNER|INOUT|INPUT|INSENSITIVE|INSERT|INSTANTIABLE|'
+ r'INSTEAD|INTERSECT|INTO|INVOKER|IS|ISNULL|ISOLATION|ITERATE|JOIN|'
+ r'K|KEY|KEY_MEMBER|KEY_TYPE|LANCOMPILER|LANGUAGE|LARGE|LAST|'
+ r'LATERAL|LEADING|LEFT|LENGTH|LESS|LEVEL|LIKE|LILMIT|LISTEN|LOAD|'
+ r'LOCAL|LOCALTIME|LOCALTIMESTAMP|LOCATION|LOCATOR|LOCK|LOWER|M|'
+ r'MAP|MATCH|MAX|MAXVALUE|MESSAGE_LENGTH|MESSAGE_OCTET_LENGTH|'
+ r'MESSAGE_TEXT|METHOD|MIN|MINUTE|MINVALUE|MOD|MODE|MODIFIES|'
+ r'MODIFY|MONTH|MORE|MOVE|MUMPS|NAMES|NATIONAL|NATURAL|NCHAR|'
+ r'NCLOB|NEW|NEXT|NO|NOCREATEDB|NOCREATEUSER|NONE|NOT|NOTHING|'
+ r'NOTIFY|NOTNULL|NULL|NULLABLE|NULLIF|OBJECT|OCTET_LENGTH|OF|OFF|'
+ r'OFFSET|OIDS|OLD|ON|ONLY|OPEN|OPERATION|OPERATOR|OPTION|OPTIONS|'
+ r'OR|ORDER|ORDINALITY|OUT|OUTER|OUTPUT|OVERLAPS|OVERLAY|OVERRIDING|'
+ r'OWNER|PAD|PARAMETER|PARAMETERS|PARAMETER_MODE|PARAMATER_NAME|'
+ r'PARAMATER_ORDINAL_POSITION|PARAMETER_SPECIFIC_CATALOG|'
+ r'PARAMETER_SPECIFIC_NAME|PARAMATER_SPECIFIC_SCHEMA|PARTIAL|'
+ r'PASCAL|PENDANT|PLACING|PLI|POSITION|POSTFIX|PRECISION|PREFIX|'
+ r'PREORDER|PREPARE|PRESERVE|PRIMARY|PRIOR|PRIVILEGES|PROCEDURAL|'
+ r'PROCEDURE|PUBLIC|READ|READS|RECHECK|RECURSIVE|REF|REFERENCES|'
+ r'REFERENCING|REINDEX|RELATIVE|RENAME|REPEATABLE|REPLACE|RESET|'
+ r'RESTART|RESTRICT|RESULT|RETURN|RETURNED_LENGTH|'
+ r'RETURNED_OCTET_LENGTH|RETURNED_SQLSTATE|RETURNS|REVOKE|RIGHT|'
+ r'ROLE|ROLLBACK|ROLLUP|ROUTINE|ROUTINE_CATALOG|ROUTINE_NAME|'
+ r'ROUTINE_SCHEMA|ROW|ROWS|ROW_COUNT|RULE|SAVE_POINT|SCALE|SCHEMA|'
+ r'SCHEMA_NAME|SCOPE|SCROLL|SEARCH|SECOND|SECURITY|SELECT|SELF|'
+ r'SENSITIVE|SERIALIZABLE|SERVER_NAME|SESSION|SESSION_USER|SET|'
+ r'SETOF|SETS|SHARE|SHOW|SIMILAR|SIMPLE|SIZE|SOME|SOURCE|SPACE|'
+ r'SPECIFIC|SPECIFICTYPE|SPECIFIC_NAME|SQL|SQLCODE|SQLERROR|'
+ r'SQLEXCEPTION|SQLSTATE|SQLWARNINIG|STABLE|START|STATE|STATEMENT|'
+ r'STATIC|STATISTICS|STDIN|STDOUT|STORAGE|STRICT|STRUCTURE|STYPE|'
+ r'SUBCLASS_ORIGIN|SUBLIST|SUBSTRING|SUM|SYMMETRIC|SYSID|SYSTEM|'
+ r'SYSTEM_USER|TABLE|TABLE_NAME| TEMP|TEMPLATE|TEMPORARY|TERMINATE|'
+ r'THAN|THEN|TIMESTAMP|TIMEZONE_HOUR|TIMEZONE_MINUTE|TO|TOAST|'
+ r'TRAILING|TRANSATION|TRANSACTIONS_COMMITTED|'
+ r'TRANSACTIONS_ROLLED_BACK|TRANSATION_ACTIVE|TRANSFORM|'
+ r'TRANSFORMS|TRANSLATE|TRANSLATION|TREAT|TRIGGER|TRIGGER_CATALOG|'
+ r'TRIGGER_NAME|TRIGGER_SCHEMA|TRIM|TRUE|TRUNCATE|TRUSTED|TYPE|'
+ r'UNCOMMITTED|UNDER|UNENCRYPTED|UNION|UNIQUE|UNKNOWN|UNLISTEN|'
+ r'UNNAMED|UNNEST|UNTIL|UPDATE|UPPER|USAGE|USER|'
+ r'USER_DEFINED_TYPE_CATALOG|USER_DEFINED_TYPE_NAME|'
+ r'USER_DEFINED_TYPE_SCHEMA|USING|VACUUM|VALID|VALIDATOR|VALUES|'
+ r'VARIABLE|VERBOSE|VERSION|VIEW|VOLATILE|WHEN|WHENEVER|WHERE|'
+ r'WITH|WITHOUT|WORK|WRITE|YEAR|ZONE)\b', Keyword),
+ (r'(ARRAY|BIGINT|BINARY|BIT|BLOB|BOOLEAN|CHAR|CHARACTER|DATE|'
+ r'DEC|DECIMAL|FLOAT|INT|INTEGER|INTERVAL|NUMBER|NUMERIC|REAL|'
+ r'SERIAL|SMALLINT|VARCHAR|VARYING|INT8|SERIAL8|TEXT)\b',
+ Name.Builtin),
+ (r'[+*/<>=~!@#%^&|`?^-]', Operator),
+ (r'[0-9]+', Number.Integer),
+ (r"'(''|[^'])*'", String),
+ (r'[a-zA-Z_][a-zA-Z0-9_]*', Name),
+ (r'[;:()\[\],\.]', Text)
+ ],
+ 'multiline-comments': [
+ (r'/\*', Comment.Multiline, 'multiline-comments'),
+ (r'\*/', Comment.Multiline, '#pop'),
+ (r'[^/\*]+', Comment.Multiline),
+ (r'[/*]', Comment.Multiline)
+ ]
+ }
+
+
+class BrainfuckLexer(RegexLexer):
+ name = 'Brainfuck'
+ aliases = ['brainfuck']
+ filenames = ['*.bf', '*.b']
+
+ tokens = {
+ 'root': [
+ (r'[.,+\-<>\[\]]+', Keyword),
+ (r'[^.,+\-<>\[\]]+', Comment)
+ ]
+ }
diff --git a/pygments/lexers/special.py b/pygments/lexers/special.py
new file mode 100644
index 00000000..9076ca51
--- /dev/null
+++ b/pygments/lexers/special.py
@@ -0,0 +1,75 @@
+# -*- coding: utf-8 -*-
+"""
+ pygments.lexers.special
+ ~~~~~~~~~~~~~~~~~~~~~~
+
+ Special lexers.
+
+ :copyright: 2006 by Georg Brandl.
+ :license: GNU LGPL, see LICENSE for more details.
+"""
+
+import re
+import cStringIO
+
+from pygments.lexer import Lexer, RegexLexer
+from pygments.token import Token, \
+ Text, Comment, Operator, Keyword, Name, String, Number
+
+
+__all__ = ['TextLexer', 'RawTokenLexer']
+
+
+class TextLexer(Lexer):
+ name = 'Text only'
+ aliases = ['text']
+ filenames = ['*.txt']
+
+ def get_tokens_unprocessed(self, text):
+ yield 0, Text, text
+
+
+_ttype_cache = {}
+
+line_re = re.compile('.*?\n')
+
+class RawTokenLexer(Lexer):
+ """
+ Recreate a token stream formatted with the RawTokenFormatter.
+
+ Additional options accepted:
+
+ ``compress``
+ If set to "gz" or "bz2", decompress the token stream with
+ the given compression algorithm (default: '').
+ """
+ name = 'Raw token data'
+ aliases = ['raw']
+ filenames = ['*.raw']
+
+ def __init__(self, **options):
+ self.compress = options.get('compress', '')
+ Lexer.__init__(self, **options)
+
+ def get_tokens(self, text):
+ if self.compress == 'gz':
+ import gzip
+ gzipfile = gzip.GzipFile('', 'rb', 9, cStringIO.StringIO(text))
+ text = gzipfile.read()
+ elif self.compress == 'bz2':
+ import bz2
+ text = bz2.decompress(text)
+ return Lexer.get_tokens(self, text)
+
+ def get_tokens_unprocessed(self, text):
+ for match in line_re.finditer(text):
+ ttypestr, val = match.group().split('\t', 1)
+ ttype = _ttype_cache.get(ttypestr)
+ if not ttype:
+ ttype = Token
+ ttypes = ttypestr.split('.')[1:]
+ for ttype_ in ttypes:
+ ttype = getattr(ttype, ttype_)
+ _ttype_cache[ttypestr] = ttype
+ val = val[1:-2].decode('string-escape')
+ yield 0, ttype, val
diff --git a/pygments/lexers/templates.py b/pygments/lexers/templates.py
new file mode 100644
index 00000000..3a735d15
--- /dev/null
+++ b/pygments/lexers/templates.py
@@ -0,0 +1,323 @@
+# -*- coding: utf-8 -*-
+"""
+ pygments.lexers.templates
+ ~~~~~~~~~~~~~~~~~~~~~~~~
+
+ Lexers for various template engines.
+
+ :copyright: 2006 by Armin Ronacher, Georg Brandl.
+ :license: GNU LGPL, see LICENSE for more details.
+"""
+
+import re
+try:
+ set
+except NameError:
+ from sets import Set as set
+
+from pygments.lexers.web import \
+ PhpLexer, HtmlLexer, XmlLexer, JavascriptLexer, CssLexer
+from pygments.lexer import \
+ Lexer, DelegatingLexer, RegexLexer, do_insertions, bygroups, include, using
+from pygments.token import \
+ Text, Comment, Operator, Keyword, Name, String, Number, Other
+
+__all__ = ['HtmlPhpLexer', 'XmlPhpLexer', 'CssPhpLexer',
+ 'JavascriptPhpLexer', 'ErbLexer', 'RhtmlLexer',
+ 'XmlErbLexer', 'CssErbLexer', 'JavascriptErbLexer',
+ 'SmartyLexer', 'HtmlSmartyLexer', 'XmlSmartyLexer',
+ 'CssSmartyLexer', 'JavascriptSmartyLexer', 'DjangoLexer',
+ 'HtmlDjangoLexer', 'CssDjangoLexer', 'XmlDjangoLexer',
+ 'JavascriptDjangoLexer']
+
+
+class ErbLexer(Lexer):
+ name = 'ERB'
+ aliases = ['erb']
+
+ _block_re = re.compile(r'(<%%|%%>|<%=|<%#|<%-|<%|-%>|%>|^%[^%].*?$)', re.M)
+
+ def __init__(self, **options):
+ from pygments.lexers.agile import RubyLexer
+ self.ruby_lexer = RubyLexer(**options)
+ Lexer.__init__(self, **options)
+
+ def get_tokens_unprocessed(self, text):
+ """
+ Since ERB doesn't allow "<%" and other tags inside of ruby
+ blocks we have to use a split approach here that fails for
+ that too.
+ """
+ tokens = self._block_re.split(text)
+ tokens.reverse()
+ state = idx = 0
+ try:
+ while True:
+ # text
+ if state == 0:
+ val = tokens.pop()
+ yield idx, Other, val
+ idx += len(val)
+ state = 1
+ # block starts
+ elif state == 1:
+ tag = tokens.pop()
+ # literals
+ if tag in ('<%%', '%%>'):
+ yield idx, Other, tag
+ idx += 3
+ state = 0
+ # comment
+ elif tag == '<%#':
+ yield idx, Comment.Preproc, tag
+ val = tokens.pop()
+ yield idx + 3, Comment, val
+ idx += 3 + len(val)
+ state = 2
+ # blocks or output
+ elif tag in ('<%', '<%=', '<%-'):
+ yield idx, Comment.Preproc, tag
+ idx += len(tag)
+ data = tokens.pop()
+ r_idx = 0
+ for r_idx, r_token, r_value in \
+ self.ruby_lexer.get_tokens_unprocessed(data):
+ yield r_idx + idx, r_token, r_value
+ idx += len(data)
+ state = 2
+ elif tag in ('%>', '-%>'):
+ yield idx, Error, tag
+ idx += len(tag)
+ state = 0
+ # % raw ruby statements
+ else:
+ yield idx, Comment.Preproc, tag[0]
+ r_idx = 0
+ for r_idx, r_token, r_value in \
+ self.ruby_lexer.get_tokens_unprocessed(tag[1:]):
+ yield idx + 1 + r_idx, r_token, r_value
+ idx += len(tag)
+ state = 0
+ # block ends
+ elif state == 2:
+ tag = tokens.pop()
+ if tag not in ('%>', '-%>'):
+ yield idx, Other, tag
+ else:
+ yield idx, Comment.Preproc, tag
+ idx += len(tag)
+ state = 0
+ except IndexError:
+ return
+
+
+class SmartyLexer(RegexLexer):
+ name = 'Smarty'
+ aliases = ['smarty']
+
+ flags = re.MULTILINE | re.DOTALL
+
+ tokens = {
+ # XXX: make marty delimiters customizable somehow
+ 'root': [
+ (r'[^{]+', Other),
+ (r'(\{)(\*.*?\*)(\})',
+ bygroups(Comment.Preproc, Comment, Comment.Preproc)),
+ (r'(\{php\})(.*?)(\{/php\})',
+ bygroups(Comment.Preproc, using(PhpLexer, startinline=True),
+ Comment.Preproc)),
+ (r'(\{)(/?[a-zA-Z_][a-zA-Z0-9_]*)(\s*)',
+ bygroups(Comment.Preproc, Name.Function, Text), 'smarty'),
+ (r'\{', Comment.Preproc, 'smarty')
+ ],
+ 'smarty': [
+ (r'\s+', Text),
+ (r'\}', Comment.Preproc, '#pop'),
+ (r'#[a-zA-Z_][a-zA-Z0-9_]*#', Name.Variable),
+ (r'\$[a-zA-Z_][a-zA-Z0-9_]*(\.[a-zA-Z0-9_]+)*', Name.Variable),
+ (r'[~!%^&*()+=|\[\]:;,.<>/?{}@-]', Operator),
+ ('(true|false|null)\b', Keyword.Constant),
+ (r"[0-9](\.[0-9]*)?(eE[+-][0-9])?[flFLdD]?|"
+ r"0[xX][0-9a-fA-F]+[Ll]?", Number),
+ (r'"(\\\\|\\"|[^"])*"', String.Double),
+ (r"'(\\\\|\\'|[^'])*'", String.Single),
+ (r'[a-zA-Z_][a-zA-Z0-9_]*', Name.Attribute)
+ ]
+ }
+
+
+class DjangoLexer(RegexLexer):
+ name = 'django template'
+ aliases = ['django']
+
+ tokens = {
+ 'root': [
+ (r'[^\{]+', Other),
+ (r'\{\{', Comment.Preproc, 'var'),
+ (r'(\{\%)(\s*)(comment)(\s*)(\%\})(.*?)'
+ r'(\{\%)(\s*)(endcomment)(\s*)(\%\})',
+ bygroups(Comment.Preproc, Text, Keyword, Text, Comment.Preproc,
+ Comment, Comment.Preproc, Text, Keyword, Text,
+ Comment.Preproc)),
+ (r'(\{\%)(\s*)([a-zA-Z_][a-zA-Z0-9_]*)',
+ bygroups(Comment.Preproc, Text, Keyword), 'block'),
+ (r'\{', Other)
+ ],
+ 'varnames': [
+ (r'[a-zA-Z][a-zA-Z0-9_]*(\.[a-zA-Z][a-zA-Z0-9_]*)*', Name.Variable),
+ (r"(\|)([a-zA-Z_][a-zA-Z0-9_]*)(:'(\\\\|\\'|[^'])*')",
+ bygroups(Operator, Name.Function, String.Single)),
+ (r'(\|)([a-zA-Z_][a-zA-Z0-9_]*)(:"(\\\\|\\"|[^"])*")',
+ bygroups(Operator, Name.Function, String.Double)),
+ (r'(\|)([a-zA-Z_][a-zA-Z0-9_]*)',
+ bygroups(Operator, Name.Function))
+ ],
+ 'var': [
+ (r'\s+', Text),
+ include('varnames'),
+ (r'\}\}', Comment.Preproc, '#pop')
+ ],
+ 'block': [
+ (r'\s+', Text),
+ (r'(in|as|reversed|not|count|and|or|with)\b', Keyword),
+ (r'"(\\\\|\\"|[^"])*"', String.Double),
+ (r"'(\\\\|\\'|[^'])*'", String.Single),
+ include('varnames'),
+ (r'\%\}', Comment.Preproc, '#pop'),
+ (r'.', Text)
+ ]
+ }
+
+
+class RhtmlLexer(DelegatingLexer):
+ name = 'RHTML'
+ aliases = ['rhtml', 'html+erb', 'html+ruby']
+ filenames = ['*.rhtml']
+
+ def __init__(self, **options):
+ super(RhtmlLexer, self).__init__(HtmlLexer, ErbLexer, **options)
+
+
+class XmlErbLexer(DelegatingLexer):
+ name = 'XML+Ruby'
+ aliases = ['xml+erb', 'xml+ruby']
+
+ def __init__(self, **options):
+ super(XmlErbLexer, self).__init__(XmlLexer, ErbLexer, **options)
+
+
+class CssErbLexer(DelegatingLexer):
+ name = 'CSS+Ruby'
+ aliases = ['css+erb', 'css+ruby']
+
+ def __init__(self, **options):
+ super(CssErbLexer, self).__init__(CssLexer, ErbLexer, **options)
+
+
+class JavascriptErbLexer(DelegatingLexer):
+ name = 'JavaScript+Ruby'
+ aliases = ['js+erb', 'javascript+erb', 'js+ruby', 'javascript+ruby']
+
+ def __init__(self, **options):
+ super(JavascriptErbLexer, self).__init__(JavascriptLexer, ErbLexer,
+ **options)
+
+
+class HtmlPhpLexer(DelegatingLexer):
+ name = 'HTML+PHP'
+ aliases = ['html+php']
+ filenames = ['*.phtml']
+
+ def __init__(self, **options):
+ super(HtmlPhpLexer, self).__init__(HtmlLexer, PhpLexer, **options)
+
+
+class XmlPhpLexer(DelegatingLexer):
+ name = 'XML+PHP'
+ aliases = ['xml+php']
+
+ def __init__(self, **options):
+ super(XmlPhpLexer, self).__init__(XmlLexer, PhpLexer, **options)
+
+
+class CssPhpLexer(DelegatingLexer):
+ name = 'CSS+PHP'
+ aliases = ['css+php']
+
+ def __init__(self, **options):
+ super(CssPhpLexer, self).__init__(CssLexer, PhpLexer, **options)
+
+
+class JavascriptPhpLexer(DelegatingLexer):
+ name = 'JavaScript+PHP'
+ aliases = ['js+php', 'javascript+php']
+
+ def __init__(self, **options):
+ super(JavascriptPhpLexer, self).__init__(JavascriptLexer, PhpLexer,
+ **options)
+
+
+class HtmlSmartyLexer(DelegatingLexer):
+ name = 'HTML+Smarty'
+ aliases = ['html+smarty']
+
+ def __init__(self, **options):
+ super(HtmlSmartyLexer, self).__init__(HtmlLexer, SmartyLexer, **options)
+
+
+class XmlSmartyLexer(DelegatingLexer):
+ name = 'XML+Smarty'
+ aliases = ['xml+smarty']
+
+ def __init__(self, **options):
+ super(XmlSmartyLexer, self).__init__(XmlLexer, SmartyLexer, **options)
+
+
+class CssSmartyLexer(DelegatingLexer):
+ name = 'CSS+Smarty'
+ aliases = ['css+smarty']
+
+ def __init__(self, **options):
+ super(CssSmartyLexer, self).__init__(CssLexer, SmartyLexer, **options)
+
+
+class JavascriptSmartyLexer(DelegatingLexer):
+ name = 'JavaScript+Smarty'
+ aliases = ['js+smarty', 'javascript+smarty']
+
+ def __init__(self, **options):
+ super(JavascriptSmartyLexer, self).__init__(JavascriptLexer, SmartyLexer,
+ **options)
+
+
+class HtmlDjangoLexer(DelegatingLexer):
+ name = 'HTML+Django'
+ aliases = ['html+django']
+
+ def __init__(self, **options):
+ super(HtmlDjangoLexer, self).__init__(HtmlLexer, DjangoLexer, **options)
+
+
+class XmlDjangoLexer(DelegatingLexer):
+ name = 'XML+Django'
+ aliases = ['xml+django']
+
+ def __init__(self, **options):
+ super(XmlDjangoLexer, self).__init__(XmlLexer, DjangoLexer, **options)
+
+
+class CssDjangoLexer(DelegatingLexer):
+ name = 'CSS+Django'
+ aliases = ['css+django']
+
+ def __init__(self, **options):
+ super(CssDjangoLexer, self).__init__(CssLexer, DjangoLexer, **options)
+
+
+class JavascriptDjangoLexer(DelegatingLexer):
+ name = 'JavaScript+Django'
+ aliases = ['js+django', 'javascript+django']
+
+ def __init__(self, **options):
+ super(JavascriptDjangoLexer, self).__init__(JavascriptLexer, DjangoLexer,
+ **options)
diff --git a/pygments/lexers/text.py b/pygments/lexers/text.py
new file mode 100644
index 00000000..3461000a
--- /dev/null
+++ b/pygments/lexers/text.py
@@ -0,0 +1,183 @@
+# -*- coding: utf-8 -*-
+"""
+ pygments.lexers.text
+ ~~~~~~~~~~~~~~~~~~~
+
+ Lexers for non-source code file types: Diff, Makefiles, Ini configs etc.
+
+ :copyright: 2006 by Armin Ronacher, Georg Brandl.
+ :license: GNU LGPL, see LICENSE for more details.
+"""
+
+import re
+
+from pygments.lexer import RegexLexer, bygroups, include
+from pygments.token import \
+ Text, Comment, Keyword, Name, String, Generic, Operator, Number
+
+
+__all__ = ['IniLexer', 'MakefileLexer', 'DiffLexer', 'IrcLogsLexer',
+ 'TexLexer']
+
+
+class IniLexer(RegexLexer):
+ name = 'INI'
+ aliases = ['ini', 'cfg']
+ filenames = ['*.ini', '*.cfg']
+
+ tokens = {
+ 'root': [
+ (r'\s+', Text),
+ (r';.*?$', Comment),
+ (r'\[.*?\]$', Keyword),
+ (r'(.*?)(\s*)(=)(\s*)(.*?)$',
+ bygroups(Name.Attribute, Text, Operator, Text, String))
+ ]
+ }
+
+
+class MakefileLexer(RegexLexer):
+ name = 'Makefile'
+ aliases = ['make', 'makefile', 'mf']
+ filenames = ['*.mak', 'Makefile', 'makefile']
+
+ tokens = {
+ 'root': [
+ (r'\s+', Text),
+ (r'#.*?\n', Comment),
+ (r'(cmdswitches|error|message|include|if|ifdef|ifndef|else|'
+ r'else\s*if|else\s*ifdef|else\s*ifndef|endif|undef)\b', Keyword),
+ (r'([a-zA-Z_][a-zA-Z0-9_]*)(\s*)(=)(\s*)',
+ bygroups(Name.Variable, Text, Operator, Text), 'var'),
+ (r'"(\\\\|\\"|[^"])*"', String.Double),
+ (r"'(\\\\|\\'|[^'])*'", String.Single),
+ (r'([^\n:]+)(:)([ \t]*)', bygroups(Name.Function, Operator, Text),
+ 'block-header')
+ ],
+ 'var': [
+ (r'\\\n', String),
+ (r'\n', Text, '#pop'),
+ (r'\\', String),
+ (r'[^\\\n]+', String),
+ ],
+ 'block-header': [
+ (r'[^,\n]', String),
+ (r',', Text),
+ (r'\n[\t ]+', Text, 'block'),
+ (r'\n', Text, '#pop')
+ ],
+ 'block': [
+ (r'#.*?(?=\n)', Comment),
+ (r'\n[\t ]+', Text),
+ (r'[^\n$]+', String),
+ (r'\$[A-Za-z0-9_]+', String.Interpol),
+ (r'\$\(.*?\)', String.Interpol),
+ (r'\$', String),
+ (r'\n', Text, '#pop:2'),
+ ]
+ }
+
+
+class DiffLexer(RegexLexer):
+ name = 'Diff'
+ aliases = ['diff']
+ filenames = ['*.diff', '*.patch']
+
+ tokens = {
+ 'root': [
+ (r' .*\n', Text),
+ (r'\+.*\n', Generic.Inserted),
+ (r'-.*\n', Generic.Deleted),
+ (r'!.*\n', Generic.Strong),
+ (r'@.*\n', Generic.Subheading),
+ (r'Index.*\n', Generic.Heading),
+ (r'=.*\n', Generic.Heading),
+ (r'.*\n', Text),
+ ]
+ }
+
+
+class IrcLogsLexer(RegexLexer):
+ name = 'IRC logs'
+ aliases = ['irc']
+
+ flags = re.VERBOSE | re.MULTILINE
+ timestamp = r"""
+ ( (?: \[|\()? # Opening bracket or paren for the timestamp
+ (?: # Timestamp
+ (?: (?:\d{1,4} [-/]?)+ # Date as - or /-separated groups of digits
+ [T ])? # Date/time separator: T or space
+ (?: \d?\d [:.]?)+ # Time as :/.-separated groups of 1 or 2 digits
+ )
+ (?: \]|\))?\s+ )? # Closing bracket or paren for the timestamp
+ """
+ tokens = {
+ 'root': [
+ # normal msgs
+ ("^" + timestamp + r"""
+ (\s*<.*?>\s+) # Nick """,
+ bygroups(Comment.Preproc, Name.Tag), 'msg'),
+ # /me msgs
+ ("^" + timestamp + r"""
+ (\s*[*]\s+) # Star
+ ([^\s]+\s+.*?\n) # Nick + rest of message """,
+ bygroups(Comment.Preproc, Keyword, Generic.Inserted)),
+ # join/part msgs
+ ("^" + timestamp + r"""
+ (\s*(?:[*]{3}|-!-)\s*) # Star(s)
+ ([^\s]+\s+) # Nick + Space
+ (.*?\n) # Rest of message """,
+ bygroups(Comment.Preproc, Keyword, String, Comment)),
+ (r"^.*?\n", Text),
+ ],
+ 'msg': [
+ (r"[^\s]+:", Name.Attribute), # Prefix
+ (r".*?\n", Text, '#pop'),
+ ],
+ }
+
+
+class TexLexer(RegexLexer):
+ name = 'TeX'
+ aliases = ['tex', 'latex']
+ filenames = ['*.tex', '*.aux', '*.toc']
+
+ tokens = {
+ 'general': [
+ (r'%.*?\n', Comment),
+ (r'[{}]', Name.Builtin),
+ (r'[&_^]', Name.Builtin),
+ ],
+ 'root': [
+ (r'\\\[', String.Backtick, 'displaymath'),
+ (r'\\\(', String, 'inlinemath'),
+ (r'\$\$', String.Backtick, 'displaymath'),
+ (r'\$', String, 'inlinemath'),
+ (r'\\([a-zA-Z]+|.)', Keyword, 'command'),
+ include('general'),
+ (r'[^\\$%&_^{}]+', Text),
+ ],
+ 'math': [
+ (r'\\([a-zA-Z]+|.)', Name.Variable),
+ include('general'),
+ (r'[0-9]+', Number),
+ (r'[-=!+*/()\[\]]', Operator),
+ (r'[^=!+*/()\[\]\\$%&_^{}0-9-]+', Name.Builtin),
+ ],
+ 'inlinemath': [
+ (r'\\\)', String, '#pop'),
+ (r'\$', String, '#pop'),
+ include('math'),
+ ],
+ 'displaymath': [
+ (r'\\\]', String, '#pop'),
+ (r'\$\$', String, '#pop'),
+ (r'\$', Name.Builtin),
+ include('math'),
+ ],
+ 'command': [
+ (r'\[.*?\]', Name.Attribute),
+ (r'\*', Keyword),
+ (r'', Text, '#pop'),
+ ],
+ }
diff --git a/pygments/lexers/web.py b/pygments/lexers/web.py
new file mode 100644
index 00000000..5bf0cfcb
--- /dev/null
+++ b/pygments/lexers/web.py
@@ -0,0 +1,332 @@
+# -*- coding: utf-8 -*-
+"""
+ pygments.lexers.web
+ ~~~~~~~~~~~~~~~~~~
+
+ Lexers for web-related languages: JavaScript, CSS, HTML, XML, PHP.
+
+ :copyright: 2006 by Georg Brandl, Armin Ronacher.
+ :license: GNU LGPL, see LICENSE for more details.
+"""
+
+import re
+try:
+ set
+except NameError:
+ from sets import Set as set
+
+from pygments.lexer import Lexer, RegexLexer, do_insertions, bygroups, using
+from pygments.token import \
+ Text, Comment, Operator, Keyword, Name, String, Number, Other
+from pygments.util import get_bool_opt, get_list_opt
+
+
+__all__ = ['HtmlLexer', 'XmlLexer', 'JavascriptLexer', 'CssLexer',
+ 'PhpLexer']
+
+
+class JavascriptLexer(RegexLexer):
+ name = 'JavaScript'
+ aliases = ['js', 'javascript']
+ filenames = ['*.js']
+
+ flags = re.DOTALL
+ tokens = {
+ 'root': [
+ (r'\s+', Text),
+ (r'//.*?\n', Comment),
+ (r'/\*.*?\*/', Comment),
+ (r'/(\\\\|\\/|[^/\n])*/[gim]*', String.Regex),
+ (r'[~\^\*!%&\[\]\{\}\(\)<>\|+=:;,./?-]', Operator),
+ (r'(for|in|while|do|break|return|continue|if|else|throw|try|'
+ r'catch|var|with|const|label|function|new|typeof|'
+ r'instanceof|this)\b', Keyword),
+ (r'(true|false|null|NaN|Infinity|undefined)\b', Keyword.Constant),
+ (r'(Array|Boolean|Date|Error|Function|Math|netscape|'
+ r'Number|Object|Packages|RegExp|String|sun|decodeURI|'
+ r'decodeURIComponent|encodeURI|encodeURIComponent|'
+ r'Error|eval|isFinite|isNaN|parseFloat|parseInt|document|this|'
+ r'window)\b', Name.Builtin),
+ (r'[$a-zA-Z_][a-zA-Z0-9_]*', Name.Other),
+ (r'[0-9]+', Number),
+ (r'"(\\\\|\\"|[^"])*"', String.Double),
+ (r"'(\\\\|\\'|[^'])*'", String.Single),
+ ]
+ }
+
+
+class CssLexer(RegexLexer):
+ name = 'CSS'
+ aliases = ['css']
+ filenames = ['*.css']
+
+ tokens = {
+ 'root': [
+ (r'\s+', Text),
+ (r'/\*(?:.|\n)*?\*/', Comment),
+ (r'{', Operator, 'content'),
+ (r'\:[a-zA-Z0-9_-]+', Name.Decorator),
+ (r'\.[a-zA-Z0-9_-]+', Name.Class),
+ (r'\#[a-zA-Z0-9_-]+', Name.Function),
+ (r'[a-zA-Z0-9_-]+', Name.Tag),
+ (r'[~\^\*!%&\[\]\(\)<>\|+=@:;,./?-]', Operator),
+ (r'"(\\\\|\\"|[^"])*"', String.Double),
+ (r"'(\\\\|\\'|[^'])*'", String.Single)
+ ],
+ 'content': [
+ (r'\s+', Text),
+ (r'}', Operator, '#pop'),
+ (r'url\(.*?\)', String.Other),
+ (r'^@.*?$', Comment.Preproc),
+ (r'(azimuth|background-attachment|background-color|'
+ r'background-image|background-position|background-repeat|'
+ r'background|border-bottom-color|border-bottom-style|'
+ r'border-bottom-width|border-left-color|border-left-style|'
+ r'border-left-width|border-right|border-right-color|'
+ r'border-right-style|border-right-width|border-top-color|'
+ r'border-top-style|border-top-width|border-bottom|'
+ r'border-collapse|border-left|border-width|border-color|'
+ r'border-spacing|border-style|border-top|border|caption-side|'
+ r'clear|clip|color|content|counter-increment|counter-reset|'
+ r'cue-after|cue-before|cue|cursor|direction|display|'
+ r'elevation|empty-cells|float|font-family|font-size|'
+ r'font-size-adjust|font-stretch|font-style|font-variant|'
+ r'font-weight|font|height|letter-spacing|line-height|'
+ r'list-style-type|list-style-image|list-style-position|'
+ r'list-style|margin-bottom|margin-left|margin-right|'
+ r'margin-top|margin|marker-offset|marks|max-height|max-width|'
+ r'min-height|min-width|opacity|orphans|outline|outline-color|'
+ r'outline-style|outline-width|overflow|padding-bottom|'
+ r'padding-left|padding-right|padding-top|padding|page|'
+ r'page-break-after|page-break-before|page-break-inside|'
+ r'pause-after|pause-before|pause|pitch|pitch-range|'
+ r'play-during|position|quotes|richness|right|size|'
+ r'speak-header|speak-numeral|speak-punctuation|speak|'
+ r'speech-rate|stress|table-layout|text-align|text-decoration|'
+ r'text-indent|text-shadow|text-transform|top|unicode-bidi|'
+ r'vertical-align|visibility|voice-family|volume|white-space|'
+ r'widows|width|word-spacing|z-index|bottom|left|'
+ r'above|absolute|always|armenian|aural|auto|avoid|baseline|'
+ r'behind|below|bidi-override|blink|block|bold|bolder|both|'
+ r'capitalize|center-left|center-right|center|circle|'
+ r'cjk-ideographic|close-quote|collapse|condensed|continuous|'
+ r'crop|crosshair|cross|cursive|dashed|decimal-leading-zero|'
+ r'decimal|default|digits|disc|dotted|double|e-resize|embed|'
+ r'extra-condensed|extra-expanded|expanded|fantasy|far-left|'
+ r'far-right|faster|fast|fixed|georgian|groove|hebrew|help|'
+ r'hidden|hide|higher|high|hiragana-iroha|hiragana|icon|'
+ r'inherit|inline-table|inline|inset|inside|invert|italic|'
+ r'justify|katakana-iroha|katakana|landscape|larger|large|'
+ r'left-side|leftwards|level|lighter|line-through|list-item|'
+ r'loud|lower-alpha|lower-greek|lower-roman|lowercase|ltr|'
+ r'lower|low|medium|message-box|middle|mix|monospace|'
+ r'n-resize|narrower|ne-resize|no-close-quote|no-open-quote|'
+ r'no-repeat|none|normal|nowrap|nw-resize|oblique|once|'
+ r'open-quote|outset|outside|overline|pointer|portrait|px|'
+ r'relative|repeat-x|repeat-y|repeat|rgb|ridge|right-side|'
+ r'rightwards|s-resize|sans-serif|scroll|se-resize|'
+ r'semi-condensed|semi-expanded|separate|serif|show|silent|'
+ r'slow|slower|small-caps|small-caption|smaller|soft|solid|'
+ r'spell-out|square|static|status-bar|super|sw-resize|'
+ r'table-caption|table-cell|table-column|table-column-group|'
+ r'table-footer-group|table-header-group|table-row|'
+ r'table-row-group|text|text-bottom|text-top|thick|thin|'
+ r'transparent|ultra-condensed|ultra-expanded|underline|'
+ r'upper-alpha|upper-latin|upper-roman|uppercase|url|'
+ r'visible|w-resize|wait|wider|x-fast|x-high|x-large|x-loud|'
+ r'x-low|x-small|x-soft|xx-large|xx-small|yes)\b', Keyword),
+ (r'(indigo|gold|firebrick|indianred|yellow|darkolivegreen|'
+ r'darkseagreen|mediumvioletred|mediumorchid|chartreuse|'
+ r'mediumslateblue|black|springgreen|crimson|lightsalmon|brown|'
+ r'turquoise|olivedrab|cyan|silver|skyblue|gray|darkturquoise|'
+ r'goldenrod|darkgreen|darkviolet|darkgray|lightpink|teal|'
+ r'darkmagenta|lightgoldenrodyellow|lavender|yellowgreen|thistle|'
+ r'violet|navy|orchid|blue|ghostwhite|honeydew|cornflowerblue|'
+ r'darkblue|darkkhaki|mediumpurple|cornsilk|red|bisque|slategray|'
+ r'darkcyan|khaki|wheat|deepskyblue|darkred|steelblue|aliceblue|'
+ r'gainsboro|mediumturquoise|floralwhite|coral|purple|lightgrey|'
+ r'lightcyan|darksalmon|beige|azure|lightsteelblue|oldlace|'
+ r'greenyellow|royalblue|lightseagreen|mistyrose|sienna|'
+ r'lightcoral|orangered|navajowhite|lime|palegreen|burlywood|'
+ r'seashell|mediumspringgreen|fuchsia|papayawhip|blanchedalmond|'
+ r'peru|aquamarine|white|darkslategray|ivory|dodgerblue|'
+ r'lemonchiffon|chocolate|orange|forestgreen|slateblue|olive|'
+ r'mintcream|antiquewhite|darkorange|cadetblue|moccasin|'
+ r'limegreen|saddlebrown|darkslateblue|lightskyblue|deeppink|'
+ r'plum|aqua|darkgoldenrod|maroon|sandybrown|magenta|tan|'
+ r'rosybrown|pink|lightblue|palevioletred|mediumseagreen|'
+ r'dimgray|powderblue|seagreen|snow|mediumblue|midnightblue|'
+ r'paleturquoise|palegoldenrod|whitesmoke|darkorchid|salmon|'
+ r'lightslategray|lawngreen|lightgreen|tomato|hotpink|'
+ r'lightyellow|lavenderblush|linen|mediumaquamarine|green|'
+ r'blueviolet|peachpuff)\b', Name.Builtin),
+ (r'\!important', Comment.Preproc),
+ (r'/\*(?:.|\n)*?\*/', Comment),
+ (r'\#[a-zA-Z0-9]{1,6}', Number),
+ (r'[\.-]?[0-9]*[\.]?[0-9]+(em|px|\%|pt|pc|in|mm|cm|ex)', Number),
+ (r'-?[0-9]+', Number),
+ (r'[~\^\*!%&\[\]\(\)<>\|+=@:;,./?-]', Operator),
+ (r'"(\\\\|\\"|[^"])*"', String.Double),
+ (r"'(\\\\|\\'|[^'])*'", String.Single),
+ (r'[a-zA-Z][a-zA-Z0-9]+', Name)
+ ]
+ }
+
+
+class HtmlLexer(RegexLexer):
+ name = 'HTML'
+ aliases = ['html']
+ filenames = ['*.html', '*.htm', '*.xhtml']
+
+ flags = re.IGNORECASE | re.DOTALL
+ tokens = {
+ 'root': [
+ ('[^<&]+', Text),
+ ('&.*?;', Name.Entity),
+ (r'\<\!\[CDATA\[.*?\]\]\>', Comment.Preproc),
+ ('<!--', Comment, 'comment'),
+ (r'<\?.*?\?>', Comment.Preproc),
+ ('<![^>]*>', Comment.Preproc),
+ (r'<\s*script\s*', Name.Tag, ('script-content', 'tag')),
+ (r'<\s*style\s*', Name.Tag, ('style-content', 'tag')),
+ (r'<\s*[a-zA-Z0-9:]+', Name.Tag, 'tag'),
+ (r'<\s*/\s*[a-zA-Z0-9:]+\s*>', Name.Tag),
+ ],
+ 'comment': [
+ ('[^-]+', Comment),
+ ('-->', Comment, '#pop'),
+ ('-', Comment),
+ ],
+ 'tag': [
+ (r'\s+', Text),
+ (r'[a-zA-Z0-9_:-]+\s*=', Name.Attribute, 'attr'),
+ (r'/?\s*>', Name.Tag, '#pop'),
+ ],
+ 'script-content': [
+ (r'<\s*/\s*script\s*>', Name.Tag, '#pop'),
+ (r'.+?(?=<\s*/\s*script\s*>)', using(JavascriptLexer)),
+ ],
+ 'style-content': [
+ (r'<\s*/\s*style\s*>', Name.Tag, '#pop'),
+ (r'.+?(?=<\s*/\s*style\s*>)', using(CssLexer)),
+ ],
+ 'attr': [
+ ('".*?"', String, '#pop'),
+ ("'.*?'", String, '#pop'),
+ (r'[^\s>]+', String, '#pop'),
+ ],
+ }
+
+
+class PhpLexer(RegexLexer):
+ name = 'PHP'
+ aliases = ['php', 'php3', 'php4', 'php5']
+ filenames = ['*.php', '*.php[345]']
+
+ flags = re.IGNORECASE | re.DOTALL | re.MULTILINE
+ tokens = {
+ 'root': [
+ (r'<\?(php)?', Comment.Preproc, 'php'),
+ (r'[^<]+', Other),
+ (r'<', Other)
+ ],
+ 'php': [
+ (r'\?>', Comment.Preproc, '#pop'),
+ (r'<<<([a-zA-Z_][a-zA-Z0-9_]*)\n.*?\n\1\;?\n', String),
+ (r'\s+', Text),
+ (r'#.*?\n', Comment),
+ (r'//.*?\n', Comment),
+ (r'/\*.*?\*/', Comment),
+ (r'(->|::)(\s*)([a-zA-Z_][a-zA-Z0-9_]*)',
+ bygroups(Operator, Text, Name.Attribute)),
+ (r'[~!%^&*()+=|\[\]:;,.<>/?{}@-]', Text),
+ (r'(class)(\s+)', bygroups(Keyword, Text), 'classname'),
+ (r'(function)(\s+)', bygroups(Keyword, Text), 'functionname'),
+ (r'(and|E_PARSE|old_function|E_ERROR|or|as|E_WARNING|parent|'
+ r'eval|PHP_OS|break|exit|case|extends|PHP_VERSION|cfunction|'
+ r'FALSE|print|for|require|continue|foreach|require_once|'
+ r'declare|return|default|static|do|switch|die|stdClass|'
+ r'echo|else|TRUE|elseif|var|empty|if|xor|enddeclare|include|'
+ r'virtual|endfor|include_once|while|endforeach|global|__FILE__|'
+ r'endif|list|__LINE__|endswitch|new|__sleep|endwhile|not|'
+ r'array|__wakeup|E_ALL|NULL)\b', Keyword),
+ ('(true|false|null)\b', Keyword.Constant),
+ (r'\$[a-zA-Z_][a-zA-Z0-9_]*', Name.Variable),
+ ('[a-zA-Z_][a-zA-Z0-9_]*', Name.Other),
+ (r"[0-9](\.[0-9]*)?(eE[+-][0-9])?[flFLdD]?|"
+ r"0[xX][0-9a-fA-F]+[Ll]?", Number),
+ (r'"(\\\\|\\"|[^"])*"', String.Double),
+ (r"'(\\\\|\\'|[^'])*'", String.Single)
+ ],
+ 'classname': [
+ (r'[a-zA-Z_][a-zA-Z0-9_]*', Name.Class, '#pop')
+ ],
+ 'functionname': [
+ (r'[a-zA-Z_][a-zA-Z0-9_]*', Name.Function, '#pop')
+ ]
+ }
+
+ def __init__(self, **options):
+ self.funcnamehighlighting = get_bool_opt(
+ options, 'funcnamehighlighting', True)
+ self.disabledmodules = get_list_opt(
+ options, 'disabledmodules', ['unknown'])
+ self.startinline = get_bool_opt(options, 'startinline', False)
+
+ # collect activated functions in a set
+ self._functions = set()
+ if self.funcnamehighlighting:
+ from pygments.lexers._phpbuiltins import MODULES
+ for key, value in MODULES.iteritems():
+ if key not in self.disabledmodules:
+ self._functions.update(value)
+ Lexer.__init__(self, **options)
+
+ def get_tokens_unprocessed(self, text):
+ stack = ['root']
+ if self.startinline:
+ stack.append('php')
+ for index, token, value in \
+ RegexLexer.get_tokens_unprocessed(self, text, stack):
+ if token is Name.Other:
+ if value in self._functions:
+ yield index, Name.Function, value
+ continue
+ yield index, token, value
+
+
+class XmlLexer(RegexLexer):
+ flags = re.MULTILINE | re.DOTALL
+
+ name = 'XML'
+ aliases = ['xml']
+ filenames = ['*.xml']
+
+ tokens = {
+ 'root': [
+ ('[^<&]+', Text),
+ ('&.*?;', Name.Entity),
+ (r'\<\!\[CDATA\[.*?\]\]\>', Comment.Preproc),
+ ('<!--', Comment, 'comment'),
+ (r'<\?.*?\?>', Comment.Preproc),
+ ('<![^>]*>', Comment.Preproc),
+ (r'<\s*[a-zA-Z0-9:-]+', Name.Tag, 'tag'),
+ (r'<\s*/\s*[a-zA-Z0-9:-]+\s*>', Name.Tag),
+ ],
+ 'comment': [
+ ('[^-]+', Comment),
+ ('-->', Comment, '#pop'),
+ ('-', Comment),
+ ],
+ 'tag': [
+ (r'\s+', Text),
+ (r'[a-zA-Z0-9_:-]+\s*=', Name.Attribute, 'attr'),
+ (r'/?\s*>', Name.Tag, '#pop'),
+ ],
+ 'attr': [
+ ('\s+', Text),
+ ('".*?"', String, '#pop'),
+ ("'.*?'", String, '#pop'),
+ (r'[^\s>]+', String, '#pop'),
+ ],
+ }
diff --git a/pygments/style.py b/pygments/style.py
new file mode 100644
index 00000000..d1c2b4eb
--- /dev/null
+++ b/pygments/style.py
@@ -0,0 +1,102 @@
+# -*- coding: utf-8 -*-
+"""
+ pygments.style
+ ~~~~~~~~~~~~~
+
+ Basic style object.
+
+ :copyright: 2006 by Georg Brandl.
+ :license: GNU LGPL, see LICENSE for more details.
+"""
+
+from pygments.token import Token, STANDARD_TYPES
+
+
+class StyleMeta(type):
+
+ def __new__(mcs, name, bases, dct):
+ obj = type.__new__(mcs, name, bases, dct)
+ for token in STANDARD_TYPES:
+ if not token in obj.styles:
+ obj.styles[token] = ''
+
+ def colorformat(text):
+ if text[0:1] == '#':
+ col = text[1:]
+ if len(col) == 6:
+ return col
+ elif len(col) == 3:
+ return col[0]+'0'+col[1]+'0'+col[2]+'0'
+ elif text == '':
+ return ''
+ assert False, "wrong color format %r" % text
+
+ _styles = obj._styles = {}
+
+ for ttype in obj.styles:
+ for token in ttype.split():
+ if token in _styles:
+ continue
+ ndef = _styles.get(token.parent, None)
+ styledefs = obj.styles.get(token, '').split()
+ if not ndef or token is None:
+ ndef = ['', 0, 0, 0, '', '']
+ elif 'noinherit' in styledefs and token is not Token:
+ ndef = _styles[Token][:]
+ else:
+ ndef = ndef[:]
+ _styles[token] = ndef
+ for styledef in obj.styles.get(token, '').split():
+ if styledef == 'noinherit':
+ pass
+ elif styledef == 'bold':
+ ndef[1] = 1
+ elif styledef == 'nobold':
+ ndef[1] = 0
+ elif styledef == 'italic':
+ ndef[2] = 1
+ elif styledef == 'noitalic':
+ ndef[2] = 0
+ elif styledef == 'underline':
+ ndef[3] = 1
+ elif styledef == 'nounderline':
+ ndef[3] = 0
+ elif styledef[:3] == 'bg:':
+ ndef[4] = colorformat(styledef[3:])
+ elif styledef[:7] == 'border:':
+ ndef[5] = colorformat(styledef[7:])
+ else:
+ ndef[0] = colorformat(styledef)
+
+ return obj
+
+ def style_for_token(cls, token):
+ t = cls._styles[token]
+ return {
+ 'color': t[0] or None,
+ 'bold': bool(t[1]),
+ 'italic': bool(t[2]),
+ 'underline': bool(t[3]),
+ 'bgcolor': t[4] or None,
+ 'border': t[5] or None
+ }
+
+ def list_styles(cls):
+ return list(self)
+
+ def __iter__(cls):
+ for token in cls._styles:
+ yield token, cls.style_for_token(token)
+
+ def __len__(cls):
+ return len(cls._styles)
+
+
+class Style(object):
+ __metaclass__ = StyleMeta
+
+ #: overall background color (``None`` means transparent)
+ background_color = '#ffffff'
+
+ #: Style definitions for individual token types.
+ styles = {}
diff --git a/pygments/styles/__init__.py b/pygments/styles/__init__.py
new file mode 100644
index 00000000..bfa0730d
--- /dev/null
+++ b/pygments/styles/__init__.py
@@ -0,0 +1,36 @@
+# -*- coding: utf-8 -*-
+"""
+ pygments.styles
+ ~~~~~~~~~~~~~~
+
+ Contains built-in styles.
+
+ :copyright: 2006 by Georg Brandl.
+ :license: GNU LGPL, see LICENSE for more details.
+"""
+
+#: Maps style names to 'submodule::classname'.
+STYLE_MAP = {
+ 'default': 'default::DefaultStyle',
+ 'emacs': 'default::DefaultStyle',
+ 'friendly': 'friendly::FriendlyStyle',
+ 'colorful': 'colorful::ColorfulStyle',
+ 'autumn': 'autumn::AutumnStyle',
+ 'murphy': 'murphy::MurphyStyle',
+ 'manni': 'manni::ManniStyle',
+ 'perldoc': 'perldoc::PerldocStyle',
+ 'pastie': 'pastie::PastieStyle',
+ 'borland': 'borland::BorlandStyle',
+ 'trac': 'trac::TracStyle',
+ 'native': 'native::NativeStyle'
+}
+
+
+def get_style_by_name(name):
+ if name not in STYLE_MAP:
+ raise ValueError("Style %r not found" % name)
+
+ mod, cls = STYLE_MAP[name].split('::')
+
+ mod = __import__('pygments.styles.' + mod, None, None, [cls])
+ return getattr(mod, cls)
diff --git a/pygments/styles/autumn.py b/pygments/styles/autumn.py
new file mode 100644
index 00000000..3fce11fd
--- /dev/null
+++ b/pygments/styles/autumn.py
@@ -0,0 +1,59 @@
+# -*- coding: utf-8 -*-
+"""
+ pygments.styles.autumn
+ ~~~~~~~~~~~~~~~~~~~~~
+
+ A colorful style, inspired by the terminal highlighting style.
+
+ :copyright: 2006 by Armin Ronacher.
+ :license: GNU LGPL, see LICENSE for more details.
+"""
+
+from pygments.style import Style
+from pygments.token import Keyword, Name, Comment, String, Error, \
+ Number, Operator, Generic
+
+
+class AutumnStyle(Style):
+
+ default_style = ""
+
+ styles = {
+ Comment: "italic #aaaaaa",
+ Comment.Preproc: "noitalic #4c8317",
+
+ Keyword: "#0000aa",
+ Keyword.Type: "#00aaaa",
+
+ Operator.Word: "#0000aa",
+
+ Name.Builtin: "#00aaaa",
+ Name.Function: "#00aa00",
+ Name.Class: "underline #00aa00",
+ Name.Namespace: "underline #00aaaa",
+ Name.Variable: "#aa0000",
+ Name.Constant: "#aa0000",
+ Name.Entity: "bold #800",
+ Name.Attribute: "#1e90ff",
+ Name.Tag: "bold #1e90ff",
+ Name.Decorator: "#888888",
+
+ String: "#aa5500",
+ String.Symbol: "#0000aa",
+ String.Regex: "#009999",
+
+ Number: "#009999",
+
+ Generic.Heading: "bold #000080",
+ Generic.Subheading: "bold #800080",
+ Generic.Deleted: "#aa0000",
+ Generic.Inserted: "#00aa00",
+ Generic.Error: "#aa0000",
+ Generic.Emph: "italic",
+ Generic.Strong: "bold",
+ Generic.Prompt: "#555555",
+ Generic.Output: "#888888",
+ Generic.Traceback: "#aa0000",
+
+ Error: "#F00 bg:#FAA"
+ }
diff --git a/pygments/styles/borland.py b/pygments/styles/borland.py
new file mode 100644
index 00000000..cc5fb6af
--- /dev/null
+++ b/pygments/styles/borland.py
@@ -0,0 +1,44 @@
+# -*- coding: utf-8 -*-
+"""
+ pygments.styles.borland
+ ~~~~~~~~~~~~~~~~~~~~~~
+
+ Style similar to the style used in the borland ides.
+
+ :copyright: 2006 by Armin Ronacher.
+ :license: GNU LGPL, see LICENSE for more details.
+"""
+
+from pygments.style import Style
+from pygments.token import Keyword, Name, Comment, String, Error, \
+ Number, Operator, Generic
+
+
+class BorlandStyle(Style):
+
+ default_style = ''
+
+ styles = {
+ Comment: 'italic #008800',
+ Comment.Preproc: 'noitalic',
+
+ String: '#0000FF',
+ Number: '#0000FF',
+ Keyword: 'bold',
+ Operator.Word: 'bold',
+ Name.Tag: 'bold',
+ Name.Attribute: 'italic',
+
+ Generic.Heading: '#999999',
+ Generic.Subheading: '#aaaaaa',
+ Generic.Deleted: 'bg:#ffdddd #000000',
+ Generic.Inserted: 'bg:#ddffdd #000000',
+ Generic.Error: '#aa0000',
+ Generic.Emph: 'italic',
+ Generic.Strong: 'bold',
+ Generic.Prompt: '#555555',
+ Generic.Output: '#888888',
+ Generic.Traceback: '#aa0000',
+
+ Error: 'bg:#e3d2d2 #a61717'
+ }
diff --git a/pygments/styles/colorful.py b/pygments/styles/colorful.py
new file mode 100644
index 00000000..ecb983d5
--- /dev/null
+++ b/pygments/styles/colorful.py
@@ -0,0 +1,75 @@
+# -*- coding: utf-8 -*-
+"""
+ pygments.styles.colorful
+ ~~~~~~~~~~~~~~~~~~~~~~~
+
+ A colorful style, inspired by CodeRay.
+
+ :copyright: 2006 by Georg Brandl.
+ :license: GNU LGPL, see LICENSE for more details.
+"""
+
+from pygments.style import Style
+from pygments.token import Keyword, Name, Comment, String, Error, \
+ Number, Operator, Generic
+
+
+class ColorfulStyle(Style):
+
+ default_style = ""
+
+ styles = {
+ Comment: "#888",
+ Comment.Preproc: "#579",
+
+ Keyword: "bold #080",
+ Keyword.Pseudo: "#038",
+ Keyword.Type: "#339",
+
+ Operator: "#333",
+ Operator.Word: "bold #000",
+
+ Name.Builtin: "#007020",
+ Name.Function: "bold #06B",
+ Name.Class: "bold #B06",
+ Name.Namespace: "bold #0e84b5",
+ Name.Exception: "bold #F00",
+ Name.Variable: "#963",
+ Name.Variable.Instance: "#33B",
+ Name.Variable.Class: "#369",
+ Name.Variable.Global: "bold #d70",
+ Name.Constant: "bold #036",
+ Name.Label: "bold #970",
+ Name.Entity: "bold #800",
+ Name.Attribute: "#00C",
+ Name.Tag: "#070",
+ Name.Decorator: "bold #555",
+
+ String: "bg:#fff0f0",
+ String.Char: "#04D bg:",
+ String.Doc: "#D42 bg:",
+ String.Interpol: "bg:#eee",
+ String.Escape: "bold #666",
+ String.Regex: "bg:#fff0ff #000",
+ String.Symbol: "#A60 bg:",
+ String.Other: "#D20",
+
+ Number: "bold #60E",
+ Number.Integer: "bold #00D",
+ Number.Float: "bold #60E",
+ Number.Hex: "bold #058",
+ Number.Oct: "bold #40E",
+
+ Generic.Heading: "bold #000080",
+ Generic.Subheading: "bold #800080",
+ Generic.Deleted: "#A00000",
+ Generic.Inserted: "#00A000",
+ Generic.Error: "#FF0000",
+ Generic.Emph: "italic",
+ Generic.Strong: "bold",
+ Generic.Prompt: "bold #c65d09",
+ Generic.Output: "#888",
+ Generic.Traceback: "#04D",
+
+ Error: "#F00 bg:#FAA"
+ }
diff --git a/pygments/styles/default.py b/pygments/styles/default.py
new file mode 100644
index 00000000..471cf55c
--- /dev/null
+++ b/pygments/styles/default.py
@@ -0,0 +1,70 @@
+# -*- coding: utf-8 -*-
+"""
+ pygments.styles.default
+ ~~~~~~~~~~~~~~~~~~~~~~
+
+ The default highlighting style for Pygments.
+
+ :copyright: 2006 by Georg Brandl.
+ :license: GNU LGPL, see LICENSE for more details.
+"""
+
+from pygments.style import Style
+from pygments.token import Keyword, Name, Comment, String, Error, \
+ Number, Operator, Generic
+
+
+class DefaultStyle(Style):
+ """
+ The default style (inspired by Emacs 22).
+ """
+
+ background_color = "#f2f2f2"
+ default_style = ""
+
+ styles = {
+ Comment: "italic #008800",
+ Comment.Preproc: "noitalic",
+
+ Keyword: "bold #AA22FF",
+ Keyword.Pseudo: "nobold",
+ Keyword.Type: "",
+
+ Operator: "#666666",
+ Operator.Word: "bold #AA22FF",
+
+ Name.Builtin: "#AA22FF",
+ Name.Function: "#00A000",
+ Name.Class: "#0000FF",
+ Name.Namespace: "bold #0000FF",
+ Name.Exception: "bold #D2413A",
+ Name.Variable: "#B8860B",
+ Name.Constant: "#880000",
+ Name.Label: "#A0A000",
+ Name.Entity: "bold #999999",
+ Name.Attribute: "#BB4444",
+ Name.Tag: "bold #008000",
+ Name.Decorator: "#AA22FF",
+
+ String: "#BB4444",
+ String.Doc: "italic",
+ String.Interpol: "bold #BB6688",
+ String.Escape: "bold #BB6622",
+ String.Regex: "#BB6688",
+ String.Symbol: "#B8860B",
+ String.Other: "#008000",
+ Number: "#666666",
+
+ Generic.Heading: "bold #000080",
+ Generic.Subheading: "bold #800080",
+ Generic.Deleted: "#A00000",
+ Generic.Inserted: "#00A000",
+ Generic.Error: "#FF0000",
+ Generic.Emph: "italic",
+ Generic.Strong: "bold",
+ Generic.Prompt: "bold #000080",
+ Generic.Output: "#888",
+ Generic.Traceback: "#04D",
+
+ Error: "border:#FF0000"
+ }
diff --git a/pygments/styles/friendly.py b/pygments/styles/friendly.py
new file mode 100644
index 00000000..6a11f66f
--- /dev/null
+++ b/pygments/styles/friendly.py
@@ -0,0 +1,67 @@
+# -*- coding: utf-8 -*-
+"""
+ pygments.styles.friendly
+ ~~~~~~~~~~~~~~~~~~~~~~~
+
+ A modern style based on the VIM pyte theme.
+
+ :copyright: 2006 by Georg Brandl, Armin Ronacher.
+ :license: GNU LGPL, see LICENSE for more details.
+"""
+
+from pygments.style import Style
+from pygments.token import Keyword, Name, Comment, String, Error, \
+ Number, Operator, Generic
+
+
+class FriendlyStyle(Style):
+
+ background_color = "#f0f0f0"
+ default_style = ""
+
+ styles = {
+ Comment: "italic #60a0b0",
+ Comment.Preproc: "noitalic #007020",
+
+ Keyword: "bold #007020",
+ Keyword.Pseudo: "nobold",
+ Keyword.Type: "",
+
+ Operator: "#666666",
+ Operator.Word: "bold #007020",
+
+ Name.Builtin: "#007020",
+ Name.Function: "#06287e",
+ Name.Class: "bold #0e84b5",
+ Name.Namespace: "bold #0e84b5",
+ Name.Exception: "#007020",
+ Name.Variable: "#bb60d5",
+ Name.Constant: "#60add5",
+ Name.Label: "bold #002070",
+ Name.Entity: "bold #d55537",
+ Name.Attribute: "#4070a0",
+ Name.Tag: "bold #062873",
+ Name.Decorator: "bold #555555",
+
+ String: "#4070a0",
+ String.Doc: "italic",
+ String.Interpol: "italic #70a0d0",
+ String.Escape: "bold #4070a0",
+ String.Regex: "#235388",
+ String.Symbol: "#517918",
+ String.Other: "#c65d09",
+ Number: "#40a070",
+
+ Generic.Heading: "bold #000080",
+ Generic.Subheading: "bold #800080",
+ Generic.Deleted: "#A00000",
+ Generic.Inserted: "#00A000",
+ Generic.Error: "#FF0000",
+ Generic.Emph: "italic",
+ Generic.Strong: "bold",
+ Generic.Prompt: "bold #c65d09",
+ Generic.Output: "#888",
+ Generic.Traceback: "#04D",
+
+ Error: "border:#FF0000"
+ }
diff --git a/pygments/styles/manni.py b/pygments/styles/manni.py
new file mode 100644
index 00000000..8e485355
--- /dev/null
+++ b/pygments/styles/manni.py
@@ -0,0 +1,76 @@
+# -*- coding: utf-8 -*-
+"""
+ pygments.styles.manni
+ ~~~~~~~~~~~~~~~~~~~~
+
+ A colorful style, inspired by the terminal highlighting style.
+
+ This is a port of the style used in the `php port`_ of pygments
+ by Manni. The style is called 'default' there.
+
+ By now Mannis php highlighter isn't licensed under a open source
+ license but because it uses code from the pygments library it must
+ be LGPL compatible sooner or later. ;-)
+
+ .. _php_port:: http://svn.fnord.name/manni/fnord.bb/lib/Highlighter/
+
+ :copyright: 2006 by Armin Ronacher, Manni <manni@fnord.name>.
+ :license: GNU LGPL, see LICENSE for more details.
+"""
+
+from pygments.style import Style
+from pygments.token import Keyword, Name, Comment, String, Error, \
+ Number, Operator, Generic
+
+
+class ManniStyle(Style):
+
+ background_color = '#f0f3f3'
+
+ styles = {
+ Comment: 'italic #0099FF',
+ Comment.Preproc: 'noitalic #009999',
+
+ Keyword: 'bold #006699',
+ Keyword.Pseudo: 'nobold',
+ Keyword.Type: '#007788',
+
+ Operator: '#555555',
+ Operator.Word: 'bold #000000',
+
+ Name.Builtin: '#336666',
+ Name.Function: '#CC00FF',
+ Name.Class: 'bold #00AA88',
+ Name.Namespace: 'bold #00CCFF',
+ Name.Exception: 'bold #CC0000',
+ Name.Variable: '#003333',
+ Name.Constant: '#336600',
+ Name.Label: '#9999FF',
+ Name.Entity: 'bold #999999',
+ Name.Attribute: '#330099',
+ Name.Tag: 'bold #330099',
+ Name.Decorator: '#9999FF',
+
+ String: '#CC3300',
+ String.Doc: 'italic',
+ String.Interpol: '#AA0000',
+ String.Escape: 'bold #CC3300',
+ String.Regex: '#33AAAA',
+ String.Symbol: '#FFCC33',
+ String.Other: '#CC3300',
+
+ Number: '#FF6600',
+
+ Generic.Heading: 'bold #003300',
+ Generic.Subheading: 'bold #003300',
+ Generic.Deleted: 'border:#CC0000 bg:#FFCCCC',
+ Generic.Inserted: 'border:#00CC00 bg:#CCFFCC',
+ Generic.Error: '#FF0000',
+ Generic.Emph: 'italic',
+ Generic.Strong: 'bold',
+ Generic.Prompt: 'bold #000099',
+ Generic.Output: '#AAAAAA',
+ Generic.Traceback: '#99CC66',
+
+ Error: 'bg:#FFAAAA #AA0000'
+ }
diff --git a/pygments/styles/murphy.py b/pygments/styles/murphy.py
new file mode 100644
index 00000000..c9e8dc7f
--- /dev/null
+++ b/pygments/styles/murphy.py
@@ -0,0 +1,75 @@
+# -*- coding: utf-8 -*-
+"""
+ pygments.styles.murphy
+ ~~~~~~~~~~~~~~~~~~~~~
+
+ Murphy's style from CodeRay.
+
+ :copyright: 2006 by Georg Brandl.
+ :license: GNU LGPL, see LICENSE for more details.
+"""
+
+from pygments.style import Style
+from pygments.token import Keyword, Name, Comment, String, Error, \
+ Number, Operator, Generic
+
+
+class MurphyStyle(Style):
+
+ default_style = ""
+
+ styles = {
+ Comment: "#666 italic",
+ Comment.Preproc: "#579 noitalic",
+
+ Keyword: "bold #289",
+ Keyword.Pseudo: "#08f",
+ Keyword.Type: "#66f",
+
+ Operator: "#333",
+ Operator.Word: "bold #000",
+
+ Name.Builtin: "#072",
+ Name.Function: "bold #5ed",
+ Name.Class: "bold #e9e",
+ Name.Namespace: "bold #0e84b5",
+ Name.Exception: "bold #F00",
+ Name.Variable: "#036",
+ Name.Variable.Instance: "#aaf",
+ Name.Variable.Class: "#ccf",
+ Name.Variable.Global: "#f84",
+ Name.Constant: "bold #5ed",
+ Name.Label: "bold #970",
+ Name.Entity: "#800",
+ Name.Attribute: "#007",
+ Name.Tag: "#070",
+ Name.Decorator: "bold #555",
+
+ String: "bg:#e0e0ff",
+ String.Char: "#88F bg:",
+ String.Doc: "#D42 bg:",
+ String.Interpol: "bg:#eee",
+ String.Escape: "bold #666",
+ String.Regex: "bg:#e0e0ff #000",
+ String.Symbol: "#fc8 bg:",
+ String.Other: "#f88",
+
+ Number: "bold #60E",
+ Number.Integer: "bold #66f",
+ Number.Float: "bold #60E",
+ Number.Hex: "bold #058",
+ Number.Oct: "bold #40E",
+
+ Generic.Heading: "bold #000080",
+ Generic.Subheading: "bold #800080",
+ Generic.Deleted: "#A00000",
+ Generic.Inserted: "#00A000",
+ Generic.Error: "#FF0000",
+ Generic.Emph: "italic",
+ Generic.Strong: "bold",
+ Generic.Prompt: "bold #c65d09",
+ Generic.Output: "#888",
+ Generic.Traceback: "#04D",
+
+ Error: "#F00 bg:#FAA"
+ }
diff --git a/pygments/styles/native.py b/pygments/styles/native.py
new file mode 100644
index 00000000..e50a6242
--- /dev/null
+++ b/pygments/styles/native.py
@@ -0,0 +1,58 @@
+# -*- coding: utf-8 -*-
+"""
+ pygments.styles.native
+ ~~~~~~~~~~~~~~~~~~~~~
+
+ pygments version of my "native" vim theme.
+
+ :copyright: 2006 by Armin Ronacher.
+ :license: GNU LGPL, see LICENSE for more details.
+"""
+
+from pygments.style import Style
+from pygments.token import Keyword, Name, Comment, String, Error, \
+ Number, Operator, Generic, Text, Token
+
+
+class NativeStyle(Style):
+
+ background_color = '#202020'
+
+ styles = {
+ Token: '#d0d0d0',
+
+ Comment: 'italic #999999',
+ Comment.Preproc: 'noitalic bold #cd2828',
+
+ Keyword: 'bold #6ab825',
+ Keyword.Pseudo: 'nobold',
+
+ String: '#ed9d13',
+ String.Other: '#ffa500',
+
+ Number: '#3677a9',
+
+ Name.Builtin: '#24909d',
+ Name.Variable: '#40ffff',
+ Name.Constant: '#40ffff',
+ Name.Class: 'underline #447fcf',
+ Name.Function: '#447fcf',
+ Name.Namespace: 'underline #447fcf',
+ Name.Exception: '#bbbbbb',
+ Name.Tag: 'bold #6ab825',
+ Name.Attribute: '#bbbbbb',
+ Name.Decorator: '#ffa500',
+
+ Generic.Heading: 'bold #ffffff',
+ Generic.Subheading: 'underline #ffffff',
+ Generic.Deleted: '#d22323',
+ Generic.Inserted: '#589819',
+ Generic.Error: '#d22323',
+ Generic.Emph: 'italic',
+ Generic.Strong: 'bold',
+ Generic.Prompt: '#aaaaaa',
+ Generic.Output: '#cccccc',
+ Generic.Traceback: '#d22323',
+
+ Error: 'bg:#e3d2d2 #a61717'
+ }
diff --git a/pygments/styles/pastie.py b/pygments/styles/pastie.py
new file mode 100644
index 00000000..fdee664d
--- /dev/null
+++ b/pygments/styles/pastie.py
@@ -0,0 +1,68 @@
+# -*- coding: utf-8 -*-
+"""
+ pygments.styles.pastie
+ ~~~~~~~~~~~~~~~~~~~~~
+
+ Style similar to the `pastie`_ default style.
+
+ .. _pastie: http://pastie.caboo.se/
+
+ :copyright: 2006 by Armin Ronacher.
+ :license: GNU LGPL, see LICENSE for more details.
+"""
+
+from pygments.style import Style
+from pygments.token import Keyword, Name, Comment, String, Error, \
+ Number, Operator, Generic
+
+
+class PastieStyle(Style):
+
+ default_style = ''
+
+ styles = {
+ Comment: '#888888',
+ Comment.Preproc: 'bold #cc0000',
+
+ String: 'bg:#fff0f0 #dd2200',
+ String.Regex: 'bg:#fff0ff #008800',
+ String.Other: 'bg:#f0fff0 #22bb22',
+ String.Symbol: '#aa6600',
+ String.Interpol: '#3333bb',
+ String.Escape: '#0044dd',
+
+ Operator.Word: '#008800',
+
+ Keyword: 'bold #008800',
+ Keyword.Pseudo: 'nobold',
+ Keyword.Type: '#888888',
+
+ Name.Class: 'bold #bb0066',
+ Name.Exception: 'bold #bb0066',
+ Name.Function: 'bold #0066bb',
+ Name.Module: 'bold #bb0066',
+ Name.Builtin: '#003388',
+ Name.Variable: '#336699',
+ Name.Variable.Class: '#336699',
+ Name.Variable.Instance: '#3333bb',
+ Name.Variable.Global: '#dd7700',
+ Name.Constant: 'bold #003366',
+ Name.Tag: 'bold #bb0066',
+ Name.Attribute: '#336699',
+ Name.Decorator: '#555555',
+
+ Number: 'bold #0000DD',
+
+ Generic.Heading: '#999999',
+ Generic.Subheading: '#aaaaaa',
+ Generic.Deleted: 'bg:#ffdddd #000000',
+ Generic.Inserted: 'bg:#ddffdd #000000',
+ Generic.Error: '#aa0000',
+ Generic.Emph: 'italic',
+ Generic.Strong: 'bold',
+ Generic.Prompt: '#555555',
+ Generic.Output: '#888888',
+ Generic.Traceback: '#aa0000',
+
+ Error: 'bg:#e3d2d2 #a61717'
+ }
diff --git a/pygments/styles/perldoc.py b/pygments/styles/perldoc.py
new file mode 100644
index 00000000..27c9d834
--- /dev/null
+++ b/pygments/styles/perldoc.py
@@ -0,0 +1,64 @@
+# -*- coding: utf-8 -*-
+"""
+ pygments.styles.perldoc
+ ~~~~~~~~~~~~~~~~~~~~~~
+
+ Style similar to the style used in the `perldoc`_ code blocks.
+
+ .. _perldoc: http://perldoc.perl.org/
+
+ :copyright: 2006 by Armin Ronacher.
+ :license: GNU LGPL, see LICENSE for more details.
+"""
+
+from pygments.style import Style
+from pygments.token import Keyword, Name, Comment, String, Error, \
+ Number, Operator, Generic
+
+
+class PerldocStyle(Style):
+
+ background_color = '#eeeedd'
+ default_style = ''
+
+ styles = {
+ Comment: '#228B22',
+ Comment.Preproc: '#1e889b',
+
+ String: '#CD5555',
+ String.Heredoc: '#1c7e71 italic',
+ String.Regex: '#B452CD',
+ String.Other: '#cb6c20',
+ String.Regex: '#1c7e71',
+
+ Number: '#B452CD',
+
+ Operator.Word: '#8B008B',
+
+ Keyword: '#8B008B bold',
+ Keyword.Type: '#a7a7a7',
+
+ Name.Class: '#008b45 bold',
+ Name.Exception: '#008b45 bold',
+ Name.Function: '#008b45',
+ Name.Namespace: '#008b45 underline',
+ Name.Variable: '#00688B',
+ Name.Constant: '#00688B',
+ Name.Decorator: '#707a7c',
+ Name.Tag: '#8B008B bold',
+ Name.Attribute: '#658b00',
+ Name.Builtin: '#658b00',
+
+ Generic.Heading: 'bold #000080',
+ Generic.Subheading: 'bold #800080',
+ Generic.Deleted: '#aa0000',
+ Generic.Inserted: '#00aa00',
+ Generic.Error: '#aa0000',
+ Generic.Emph: 'italic',
+ Generic.Strong: 'bold',
+ Generic.Prompt: '#555555',
+ Generic.Output: '#888888',
+ Generic.Traceback: '#aa0000',
+
+ Error: 'bg:#e3d2d2 #a61717'
+ }
diff --git a/pygments/styles/trac.py b/pygments/styles/trac.py
new file mode 100644
index 00000000..65662925
--- /dev/null
+++ b/pygments/styles/trac.py
@@ -0,0 +1,56 @@
+# -*- coding: utf-8 -*-
+"""
+ pygments.styles.trac
+ ~~~~~~~~~~~~~~~~~~~
+
+ Port of the default trac highlighter design.
+
+ :copyright: 2006 by Armin Ronacher.
+ :license: GNU LGPL, see LICENSE for more details.
+"""
+
+from pygments.style import Style
+from pygments.token import Keyword, Name, Comment, String, Error, \
+ Number, Operator, Generic
+
+
+class TracStyle(Style):
+
+ default_style = ''
+
+ styles = {
+ Comment: 'italic #999988',
+ Comment.Preproc: 'bold noitalic #999999',
+
+ String: '#bb8844',
+ String.Regex: '#808000',
+
+ Number: '#009999',
+
+ Keyword: 'bold',
+ Keyword.Type: '#445588',
+
+ Name.Builtin: '#999999',
+ Name.Function: 'bold #990000',
+ Name.Class: 'bold #445588',
+ Name.Exception: 'bold #990000',
+ Name.Namespace: '#555555',
+ Name.Variable: '#ff99ff',
+ Name.Constant: '#ff99ff',
+ Name.Tag: '#000080',
+ Name.Attribute: '#008080',
+ Name.Entity: '#800080',
+
+ Generic.Heading: '#999999',
+ Generic.Subheading: '#aaaaaa',
+ Generic.Deleted: 'bg:#ffdddd #000000',
+ Generic.Inserted: 'bg:#ddffdd #000000',
+ Generic.Error: '#aa0000',
+ Generic.Emph: 'italic',
+ Generic.Strong: 'bold',
+ Generic.Prompt: '#555555',
+ Generic.Output: '#888888',
+ Generic.Traceback: '#aa0000',
+
+ Error: 'bg:#e3d2d2 #a61717'
+ }
diff --git a/pygments/token.py b/pygments/token.py
new file mode 100644
index 00000000..ca542bc0
--- /dev/null
+++ b/pygments/token.py
@@ -0,0 +1,156 @@
+# -*- coding: utf-8 -*-
+"""
+ pygments.token
+ ~~~~~~~~~~~~~
+
+ Basic token types and the standard tokens.
+
+ :copyright: 2006 by Georg Brandl.
+ :license: GNU LGPL, see LICENSE for more details.
+"""
+
+class _TokenType(tuple):
+ parent = None
+
+ def split(self):
+ buffer = []
+ node = self
+ while node is not None:
+ buffer.append(node)
+ node = node.parent
+ buffer.reverse()
+ return buffer
+
+ def __getattr__(self, val):
+ if not val or not val[0].isupper():
+ return tuple.__getattr__(self, val)
+ new = _TokenType(self + (val,))
+ setattr(self, val, new)
+ new.parent = self
+ return new
+
+ def __hash__(self):
+ return hash(tuple(self))
+
+ def __repr__(self):
+ return 'Token' + (self and '.' or '') + '.'.join(self)
+
+
+Token = _TokenType()
+
+# Special token types
+Text = Token.Text
+Error = Token.Error
+# Text that doesn't belong to this lexer (e.g. HTML in PHP)
+Other = Token.Other
+
+# Common token types for source code
+Keyword = Token.Keyword
+Name = Token.Name
+Literal = Token.Literal
+String = Literal.String
+Number = Literal.Number
+Operator = Token.Operator
+Comment = Token.Comment
+
+# Generic types for non-source code
+Generic = Token.Generic
+
+
+# Map standard token types to short names, used in CSS class naming.
+# If you add a new item, please be sure to run this file to perform
+# a consistency check for duplicate values.
+STANDARD_TYPES = {
+ Token: '',
+
+ Text: '',
+ Error: 'err',
+ Other: 'x',
+
+ Keyword: 'k',
+ Keyword.Constant: 'kc',
+ Keyword.Declaration: 'kd',
+ Keyword.Pseudo: 'kp',
+ Keyword.Reserved: 'kr',
+ Keyword.Type: 'kt',
+
+ Name: 'n',
+ Name.Attribute: 'na',
+ Name.Builtin: 'nb',
+ Name.Builtin.Pseudo: 'bp',
+ Name.Class: 'nc',
+ Name.Constant: 'no',
+ Name.Decorator: 'nd',
+ Name.Entity: 'ni',
+ Name.Exception: 'ne',
+ Name.Function: 'nf',
+ Name.Label: 'nl',
+ Name.Namespace: 'nn',
+ Name.Other: 'nx',
+ Name.Tag: 'nt',
+ Name.Variable: 'nv',
+ Name.Variable.Class: 'vc',
+ Name.Variable.Global: 'vg',
+ Name.Variable.Instance: 'vi',
+
+ Literal: 'l',
+ Literal.Date: 'ld',
+
+ String: 's',
+ String.Backtick: 'sb',
+ String.Char: 'sc',
+ String.Doc: 'sd',
+ String.Double: 's2',
+ String.Escape: 'se',
+ String.Heredoc: 'sh',
+ String.Interpol: 'si',
+ String.Other: 'sx',
+ String.Regex: 'sr',
+ String.Single: 's1',
+ String.Symbol: 'ss',
+
+ Number: 'm',
+ Number.Float: 'mf',
+ Number.Hex: 'mh',
+ Number.Integer: 'mi',
+ Number.Integer.Long: 'il',
+ Number.Oct: 'mo',
+
+ Operator: 'o',
+ Operator.Word: 'ow',
+
+ Comment: 'c',
+ Comment.Multiline: 'cm',
+ Comment.Preproc: 'cp',
+ Comment.Single: 'c1',
+
+ Generic: 'g',
+ Generic.Deleted: 'gd',
+ Generic.Emph: 'ge',
+ Generic.Error: 'gr',
+ Generic.Heading: 'gh',
+ Generic.Inserted: 'gi',
+ Generic.Output: 'go',
+ Generic.Prompt: 'gp',
+ Generic.Strong: 'gs',
+ Generic.Subheading: 'gu',
+ Generic.Traceback: 'gt',
+}
+
+
+
+if __name__ == '__main__':
+ import sys
+ # sanity check for token name dict: no duplicate entries!
+ s = STANDARD_TYPES.copy()
+ s[Token] = '---' # Token and Text do conflict, that is okay
+ t = {}
+ for k, v in s.iteritems():
+ t.setdefault(v, []).append(k)
+ if len(t) == len(s):
+ print 'Okay!'
+ sys.exit()
+
+ for k, v in t.iteritems():
+ if len(v) > 1:
+ print "%r has more than one key: %r" % (k, v)
diff --git a/pygments/util.py b/pygments/util.py
new file mode 100644
index 00000000..65261964
--- /dev/null
+++ b/pygments/util.py
@@ -0,0 +1,50 @@
+# -*- coding: utf-8 -*-
+"""
+ pygments.util
+ ~~~~~~~~~~~~
+
+ Utility functions.
+
+ :copyright: 2006 by Georg Brandl.
+ :license: GNU LGPL, see LICENSE for more details.
+"""
+
+
+class OptionError(Exception):
+ pass
+
+
+def get_bool_opt(options, optname, default=None):
+ string = options.get(optname, default)
+ if isinstance(string, bool):
+ return string
+ elif string.lower() in ('1', 'yes', 'true', 'on'):
+ return True
+ elif string.lower() in ('0', 'no', 'false', 'off'):
+ return False
+ else:
+ raise OptionError('Invalid value %r for option %s; use '
+ '1/0, yes/no, true/false, on/off' %
+ string, optname)
+
+
+def get_int_opt(options, optname, default=None):
+ string = options.get(optname, default)
+ try:
+ return int(string)
+ except ValueError:
+ raise OptionError('Invalid value %r for option %s; you '
+ 'must give an integer value' %
+ string, optname)
+
+
+def get_list_opt(options, optname, default=None):
+ val = options.get(optname, default)
+ if isinstance(val, basestring):
+ return val.split()
+ elif isinstance(val, (list, tuple)):
+ return list(val)
+ else:
+ raise OptionError('Invalid value %r for option %s; you '
+ 'must give a list value' %
+ val, optname)
diff --git a/scripts/check_sources.py b/scripts/check_sources.py
new file mode 100755
index 00000000..2c90df8b
--- /dev/null
+++ b/scripts/check_sources.py
@@ -0,0 +1,231 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+"""
+ Checker for file headers
+ ~~~~~~~~~~~~~~~~~~~~~~~~
+
+ Make sure each Python file has a correct file header
+ including copyright and license information.
+
+ :copyright: 2006 by Georg Brandl.
+ :license: GNU GPL, see LICENSE for more details.
+"""
+
+import sys, os, re
+import getopt
+import cStringIO
+from os.path import join, splitext, abspath
+
+
+checkers = {}
+
+def checker(*suffixes, **kwds):
+ only_pkg = kwds.pop('only_pkg', False)
+ def deco(func):
+ for suffix in suffixes:
+ checkers.setdefault(suffix, []).append(func)
+ func.only_pkg = only_pkg
+ return func
+ return deco
+
+
+copyright_re = re.compile(r'^ :copyright: 200\d(-200\d)? by (\w| )+(, (\w| )+)*\.$')
+coding_re = re.compile(r'coding[:=]\s*([-\w.]+)')
+not_ix_re = re.compile(r'\bnot\s+\S+?\s+i[sn]\s\S+')
+is_const_re = re.compile(r'if.*?==\s+(None|False|True)\b')
+
+misspellings = ["developement", "adress", "verificate", # ALLOW-MISSPELLING
+ "informations"] # ALLOW-MISSPELLING
+
+
+@checker('.py')
+def check_syntax(fn, lines):
+ try:
+ compile(''.join(lines), fn, "exec")
+ except SyntaxError, err:
+ yield 0, "not compilable: %s" % err
+
+
+@checker('.py')
+def check_style_and_encoding(fn, lines):
+ encoding = 'ascii'
+ for lno, line in enumerate(lines):
+ if len(line) > 90:
+ yield lno+1, "line too long"
+ m = not_ix_re.search(line)
+ if m:
+ yield lno+1, '"' + m.group() + '"'
+ if is_const_re.search(line):
+ yield lno+1, 'using == None/True/False'
+ if lno < 2:
+ co = coding_re.search(line)
+ if co:
+ encoding = co.group(1)
+ try:
+ line.decode(encoding)
+ except UnicodeDecodeError, err:
+ yield lno+1, "not decodable: %s\n Line: %r" % (err, line)
+ except LookupError, err:
+ yield 0, "unknown encoding: %s" % encoding
+ encoding = 'latin1'
+
+
+@checker('.py', only_pkg=True)
+def check_fileheader(fn, lines):
+ # line number correction
+ c = 1
+ if lines[0:1] == ['#!/usr/bin/env python\n']:
+ lines = lines[1:]
+ c = 2
+
+ llist = []
+ docopen = False
+ for lno, l in enumerate(lines):
+ llist.append(l)
+ if lno == 0:
+ if l == '# -*- coding: rot13 -*-\n':
+ # special-case pony package
+ return
+ elif l != '# -*- coding: utf-8 -*-\n':
+ yield 1, "missing coding declaration"
+ elif lno == 1:
+ if l != '"""\n' and l != 'r"""\n':
+ yield 2, 'missing docstring begin (""")'
+ else:
+ docopen = True
+ elif docopen:
+ if l == '"""\n':
+ # end of docstring
+ if lno <= 4:
+ yield lno+c, "missing module name in docstring"
+ break
+
+ if l != "\n" and l[:4] != ' ' and docopen:
+ yield lno+c, "missing correct docstring indentation"
+
+ if lno == 2:
+ # if not in package, don't check the module name
+ modname = fn[:-3].replace('/', '.').replace('.__init__', '')
+ while modname:
+ if l.lower()[4:-1] == modname:
+ break
+ modname = '.'.join(modname.split('.')[1:])
+ else:
+ yield 3, "wrong module name in docstring heading"
+ modnamelen = len(l.strip())
+ elif lno == 3:
+ if l.strip() != modnamelen * "~":
+ yield 4, "wrong module name underline, should be ~~~...~"
+
+ else:
+ yield 0, "missing end and/or start of docstring..."
+
+ # check for copyright and license fields
+ license = llist[-2:-1]
+ if license != [" :license: GNU LGPL, see LICENSE for more details.\n"]:
+ yield 0, "no correct license info"
+
+ copyright = llist[-3:-2]
+ if not copyright or not copyright_re.match(copyright[0]):
+ yield 0, "no correct copyright info"
+
+
+@checker('.py', '.html', '.js')
+def check_whitespace_and_spelling(fn, lines):
+ for lno, line in enumerate(lines):
+ if "\t" in line:
+ yield lno+1, "OMG TABS!!!1 "
+ if line[:-1].rstrip(' \t') != line[:-1]:
+ yield lno+1, "trailing whitespace"
+ for word in misspellings:
+ if word in line and 'ALLOW-MISSPELLING' not in line:
+ yield lno+1, '"%s" used' % word
+
+
+bad_tags = ('<b>', '<i>', '<u>', '<s>', '<strike>'
+ '<center>', '<big>', '<small>', '<font')
+
+@checker('.html')
+def check_xhtml(fn, lines):
+ for lno, line in enumerate(lines):
+ for bad_tag in bad_tags:
+ if bad_tag in line:
+ yield lno+1, "used " + bad_tag
+
+
+def main(argv):
+ try:
+ gopts, args = getopt.getopt(argv[1:], "vi:")
+ except getopt.GetoptError:
+ print "Usage: %s [-v] [-i ignorepath]* [path]" % argv[0]
+ return 2
+ opts = {}
+ for opt, val in gopts:
+ if opt == '-i':
+ val = abspath(val)
+ opts.setdefault(opt, []).append(val)
+
+ if len(args) == 0:
+ path = '.'
+ elif len(args) == 1:
+ path = args[0]
+ else:
+ print "Usage: %s [-v] [-i ignorepath]* [path]" % argv[0]
+ return 2
+
+ verbose = '-v' in opts
+
+ num = 0
+ out = cStringIO.StringIO()
+
+ for root, dirs, files in os.walk(path):
+ if '.svn' in dirs:
+ dirs.remove('.svn')
+ if '-i' in opts and abspath(root) in opts['-i']:
+ del dirs[:]
+ continue
+ # XXX: awkward: for the Makefile call: don't check non-package
+ # files for file headers
+ in_pocoo_pkg = root.startswith('./pygments')
+ for fn in files:
+
+ fn = join(root, fn)
+ if fn[:2] == './': fn = fn[2:]
+
+ if '-i' in opts and abspath(fn) in opts['-i']:
+ continue
+
+ ext = splitext(fn)[1]
+ checkerlist = checkers.get(ext, None)
+ if not checkerlist:
+ continue
+
+ if verbose:
+ print "Checking %s..." % fn
+
+ try:
+ f = open(fn, 'r')
+ lines = list(f)
+ except (IOError, OSError), err:
+ print "%s: cannot open: %s" % (fn, err)
+ num += 1
+ continue
+
+ for checker in checkerlist:
+ if not in_pocoo_pkg and checker.only_pkg:
+ continue
+ for lno, msg in checker(fn, lines):
+ print >>out, "%s:%d: %s" % (fn, lno, msg)
+ num += 1
+ if verbose:
+ print
+ if num == 0:
+ print "No errors found."
+ else:
+ print out.getvalue().rstrip('\n')
+ print "%d error%s found." % (num, num > 1 and "s" or "")
+ return int(num > 0)
+
+
+if __name__ == '__main__':
+ sys.exit(main(sys.argv))
diff --git a/scripts/count_loc.py b/scripts/count_loc.py
new file mode 100644
index 00000000..5804cd6f
--- /dev/null
+++ b/scripts/count_loc.py
@@ -0,0 +1,48 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+"""
+ Source line counter
+ ~~~~~~~~~~~~~~~~~~~
+
+ Count the lines of code in pocoo, colubrid and jinja.
+ Requires an svn checkout.
+
+ :copyright: 2006 by Armin Ronacher.
+ :license: GNU GPL, see LICENSE for more details.
+"""
+from pocoo.utils.path import Path
+
+def main(root, search):
+ LOC = 0
+
+ root = Path(root).realpath()
+ offset = len(root) + 1
+
+ print '+%s+' % ('=' * 78)
+ print '| Lines of Code %s |' % (' ' * 62)
+ print '+%s+' % ('=' * 78)
+
+ for folder in search:
+ folder = Path(root).joinpath(folder).realpath()
+ for fn in folder.walk():
+ if fn.endswith('.py') or fn.endswith('.js'):
+ try:
+ fp = file(fn)
+ lines = sum(1 for l in fp.read().splitlines() if l.strip())
+ except:
+ print '%-70sskipped' % fn
+ else:
+ LOC += lines
+ print '| %-68s %7d |' % (fn[offset:], lines)
+ fp.close()
+
+ print '+%s+' % ('-' * 78)
+ print '| Total Lines of Code: %55d |' % LOC
+ print '+%s+' % ('-' * 78)
+
+if __name__ == '__main__':
+ main('../../../', [
+ 'pocoo/trunk',
+ 'colubrid/trunk',
+ 'jinja/trunk'
+ ])
diff --git a/scripts/epydoc.css b/scripts/epydoc.css
new file mode 100644
index 00000000..16d5b0df
--- /dev/null
+++ b/scripts/epydoc.css
@@ -0,0 +1,280 @@
+
+
+/* Epydoc CSS Stylesheet
+ *
+ * This stylesheet can be used to customize the appearance of epydoc's
+ * HTML output.
+ *
+ */
+
+/* Adapted for Pocoo API docs by Georg Brandl */
+
+/* Default Colors & Styles
+ * - Set the default foreground & background color with 'body'; and
+ * link colors with 'a:link' and 'a:visited'.
+ * - Use bold for decision list terms.
+ * - The heading styles defined here are used for headings *within*
+ * docstring descriptions. All headings used by epydoc itself use
+ * either class='epydoc' or class='toc' (CSS styles for both
+ * defined below).
+ */
+body { background: #ffffff; color: #000000;
+ font-family: Trebuchet MS,Tahoma,sans-serif;
+ font-size: 0.9em; line-height: 140%;
+ margin: 0; padding: 0 1.2em 1.2em 1.2em; }
+a:link { color: #C87900; text-decoration: none;
+ border-bottom: 1px solid #C87900; }
+a:visited { color: #C87900; text-decoration: none;
+ border-bottom: 1px dotted #C87900; }
+a:hover { color: #F8A900; border-bottom-color: #F8A900; }
+dt { font-weight: bold; }
+h1 { font-size: +180%; font-style: italic;
+ font-weight: bold; margin-top: 1.5em; }
+h2 { font-size: +140%; font-style: italic;
+ font-weight: bold; }
+h3 { font-size: +110%; font-style: italic;
+ font-weight: normal; }
+p { margin-top: .5em; margin-bottom: .5em; }
+hr { margin-top: 1.5em; margin-bottom: 1.5em;
+ border: 1px solid #BBB; }
+tt.literal { background: #F5FFD0; padding: 2px;
+ font-size: 110%; }
+table.rst-docutils { border: 0; }
+table.rst-docutils td { border: 0; padding: 5px 20px 5px 0px; }
+
+/* Page Header & Footer
+ * - The standard page header consists of a navigation bar (with
+ * pointers to standard pages such as 'home' and 'trees'); a
+ * breadcrumbs list, which can be used to navigate to containing
+ * classes or modules; options links, to show/hide private
+ * variables and to show/hide frames; and a page title (using
+ * <h1>). The page title may be followed by a link to the
+ * corresponding source code (using 'span.codelink').
+ * - The footer consists of a navigation bar, a timestamp, and a
+ * pointer to epydoc's homepage.
+ */
+h1.epydoc { margin-top: .4em; margin-bottom: .4em;
+ font-size: +180%; font-weight: bold;
+ font-style: normal; }
+h2.epydoc { font-size: +130%; font-weight: bold;
+ font-style: normal; }
+h3.epydoc { font-size: +115%; font-weight: bold;
+ font-style: normal; }
+table.navbar { background: #E6F8A0; color: #000000;
+ border-top: 1px solid #c0d0d0;
+ border-bottom: 1px solid #c0d0d0;
+ margin: -1px -1.2em 1em -1.2em; }
+table.navbar th { padding: 2px 7px 2px 0px; }
+th.navbar-select { background-color: transparent; }
+th.navbar-select:before { content: ">" }
+th.navbar-select:after { content: "<" }
+table.navbar a { border: 0; }
+span.breadcrumbs { font-size: 95%; font-weight: bold; }
+span.options { font-size: 80%; }
+span.codelink { font-size: 85%; }
+td.footer { font-size: 85%; }
+
+/* Table Headers
+ * - Each summary table and details section begins with a 'header'
+ * row. This row contains a section title (marked by
+ * 'span.table-header') as well as a show/hide private link
+ * (marked by 'span.options', defined above).
+ * - Summary tables that contain user-defined groups mark those
+ * groups using 'group header' rows.
+ */
+td.table-header { background: #B6C870; color: #000000;
+ border-bottom: 1px solid #FFF; }
+span.table-header { font-size: 110%; font-weight: bold; }
+th.group-header { text-align: left; font-style: italic;
+ font-size: 110%; }
+td.spacer { width: 5%; }
+
+/* Summary Tables (functions, variables, etc)
+ * - Each object is described by a single row of the table with
+ * two cells. The left cell gives the object's type, and is
+ * marked with 'code.summary-type'. The right cell gives the
+ * object's name and a summary description.
+ * - CSS styles for the table's header and group headers are
+ * defined above, under 'Table Headers'
+ */
+table.summary { border-collapse: collapse;
+ background: #E6F8A0; color: #000000;
+ margin: 1em 0 .5em 0;
+ border: 0; }
+table.summary tr { border-bottom: 1px solid #BBB; }
+td.summary a { font-weight: bold; }
+code.summary-type { font-size: 85%; }
+
+/* Details Tables (functions, variables, etc)
+ * - Each object is described in its own single-celled table.
+ * - A single-row summary table w/ table-header is used as
+ * a header for each details section (CSS style for table-header
+ * is defined above, under 'Table Headers').
+ */
+
+table.detsummary { margin-top: 2em; }
+
+table.details { border-collapse: collapse;
+ background: #E6F8A0; color: #000000;
+ border-bottom: 1px solid #BBB;
+ margin: 0; }
+table.details td { padding: .2em .2em .2em .5em; }
+table.details table td { padding: 0; }
+table.details h3 { margin: 5px 0 5px 0; font-size: 105%;
+ font-style: normal; }
+
+table.details dd { display: inline; margin-left: 5px; }
+table.details dl { margin-left: 5px; }
+
+
+/* Index tables (identifier index, term index, etc)
+ * - link-index is used for indices containing lists of links
+ * (namely, the identifier index & term index).
+ * - index-where is used in link indices for the text indicating
+ * the container/source for each link.
+ * - metadata-index is used for indices containing metadata
+ * extracted from fields (namely, the bug index & todo index).
+ */
+table.link-index { border-collapse: collapse;
+ background: #F6FFB0; color: #000000;
+ border: 1px solid #608090; }
+td.link-index { border-width: 0px; }
+span.index-where { font-size: 70%; }
+table.metadata-index { border-collapse: collapse;
+ background: #F6FFB0; color: #000000;
+ border: 1px solid #608090;
+ margin: .2em 0 0 0; }
+td.metadata-index { border-width: 1px; border-style: solid; }
+
+/* Function signatures
+ * - sig* is used for the signature in the details section.
+ * - .summary-sig* is used for the signature in the summary
+ * table, and when listing property accessor functions.
+ * */
+.sig-name { color: #006080; }
+.sig-arg { color: #008060; }
+.sig-default { color: #602000; }
+.summary-sig-name { font-weight: bold; }
+.summary-sig-arg { color: #006040; }
+.summary-sig-default { color: #501800; }
+
+/* Variable values
+ * - In the 'variable details' sections, each varaible's value is
+ * listed in a 'pre.variable' box. The width of this box is
+ * restricted to 80 chars; if the value's repr is longer than
+ * this it will be wrapped, using a backslash marked with
+ * class 'variable-linewrap'. If the value's repr is longer
+ * than 3 lines, the rest will be ellided; and an ellipsis
+ * marker ('...' marked with 'variable-ellipsis') will be used.
+ * - If the value is a string, its quote marks will be marked
+ * with 'variable-quote'.
+ * - If the variable is a regexp, it is syntax-highlighted using
+ * the re* CSS classes.
+ */
+pre.variable { padding: .5em; margin: 0;
+ background-color: #dce4ec;
+ border: 1px solid #708890; }
+.variable-linewrap { display: none; }
+.variable-ellipsis { color: #604000; font-weight: bold; }
+.variable-quote { color: #604000; font-weight: bold; }
+.re { color: #000000; }
+.re-char { color: #006030; }
+.re-op { color: #600000; }
+.re-group { color: #003060; }
+.re-ref { color: #404040; }
+
+/* Base tree
+ * - Used by class pages to display the base class hierarchy.
+ */
+pre.base-tree { font-size: 90%; margin: 1em 0 2em 0;
+ line-height: 100%;}
+
+/* Frames-based table of contents headers
+ * - Consists of two frames: one for selecting modules; and
+ * the other listing the contents of the selected module.
+ * - h1.toc is used for each frame's heading
+ * - h2.toc is used for subheadings within each frame.
+ */
+h1.toc { text-align: center; font-size: 105%;
+ margin: 0; font-weight: bold;
+ padding: 0; }
+h2.toc { font-size: 100%; font-weight: bold;
+ margin: 0.5em 0 0 -0.3em; }
+
+/* Syntax Highlighting for Source Code
+ * - doctest examples are displayed in a 'pre.py-doctest' block.
+ * If the example is in a details table entry, then it will use
+ * the colors specified by the 'table pre.py-doctest' line.
+ * - Source code listings are displayed in a 'pre.py-src' block.
+ * Each line is marked with 'span.py-line' (used to draw a line
+ * down the left margin, separating the code from the line
+ * numbers). Line numbers are displayed with 'span.py-lineno'.
+ * The expand/collapse block toggle button is displayed with
+ * 'a.py-toggle' (Note: the CSS style for 'a.py-toggle' should not
+ * modify the font size of the text.)
+ * - If a source code page is opened with an anchor, then the
+ * corresponding code block will be highlighted. The code
+ * block's header is highlighted with 'py-highlight-hdr'; and
+ * the code block's body is highlighted with 'py-highlight'.
+ * - The remaining py-* classes are used to perform syntax
+ * highlighting (py-string for string literals, py-name for names,
+ * etc.)
+ */
+pre.rst-literal-block,
+pre.py-doctest { margin-left: 1em; margin-right: 1.5em;
+ line-height: 150%;
+ background-color: #F5FFD0; padding: .5em;
+ border: 1px solid #B6C870;
+ font-size: 110%; }
+pre.py-src { border: 1px solid #BBB; margin-top: 3em;
+ background: #f0f0f0; color: #000000;
+ line-height: 150%; }
+span.py-line { margin-left: .2em; padding-left: .4em; }
+span.py-lineno { border-right: 1px solid #BBB;
+ padding: .3em .5em .3em .5em;
+ font-style: italic; font-size: 90%; }
+a.py-toggle { text-decoration: none; }
+div.py-highlight-hdr { border-top: 1px solid #BBB;
+ background: #d0e0e0; }
+div.py-highlight { border-bottom: 1px solid #BBB;
+ background: #d0e0e0; }
+.py-prompt { color: #005050; font-weight: bold;}
+.py-string { color: #006030; }
+.py-comment { color: #003060; }
+.py-keyword { color: #600000; }
+.py-output { color: #404040; }
+.py-name { color: #000050; }
+.py-name:link { color: #000050; }
+.py-name:visited { color: #000050; }
+.py-number { color: #005000; }
+.py-def-name { color: #000060; font-weight: bold; }
+.py-base-class { color: #000060; }
+.py-param { color: #000060; }
+.py-docstring { color: #006030; }
+.py-decorator { color: #804020; }
+/* Use this if you don't want links to names underlined: */
+/*a.py-name { text-decoration: none; }*/
+
+/* Graphs & Diagrams
+ * - These CSS styles are used for graphs & diagrams generated using
+ * Graphviz dot. 'img.graph-without-title' is used for bare
+ * diagrams (to remove the border created by making the image
+ * clickable).
+ */
+img.graph-without-title { border: none; }
+img.graph-with-title { border: 1px solid #000000; }
+span.graph-title { font-weight: bold; }
+span.graph-caption { }
+
+/* General-purpose classes
+ * - 'p.indent-wrapped-lines' defines a paragraph whose first line
+ * is not indented, but whose subsequent lines are.
+ * - The 'nomargin-top' class is used to remove the top margin (e.g.
+ * from lists). The 'nomargin' class is used to remove both the
+ * top and bottom margin (but not the left or right margin --
+ * for lists, that would cause the bullets to disappear.)
+ */
+p.indent-wrapped-lines { padding: 0 0 0 7em; text-indent: -7em;
+ margin: 0; }
+.nomargin-top { margin-top: 0; }
+.nomargin { margin-top: 0; margin-bottom: 0; }
diff --git a/scripts/find_codetags.py b/scripts/find_codetags.py
new file mode 100755
index 00000000..54d66b84
--- /dev/null
+++ b/scripts/find_codetags.py
@@ -0,0 +1,205 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+"""
+ Codetags finder
+ ~~~~~~~~~~~~~~~
+
+ Find code tags in specified files and/or directories
+ and create a report in HTML format.
+
+ :copyright: 2006 by Georg Brandl.
+ :license: GNU GPL, see LICENSE for more details.
+"""
+
+import sys, os, re
+import getopt
+from os.path import join, abspath, isdir, isfile
+
+
+TAGS = set(('XXX', 'TODO', 'FIXME', 'HACK'))
+
+tag_re = re.compile(
+ r'(?P<tag>\b' + r'\b|\b'.join(TAGS) + r'\b)\s*'
+ r'(?: \( (?P<who> .*? ) \) )?'
+ r'\s*:?\s* (?P<what> .*? ) \s* $',
+ re.X)
+
+binary_re = re.compile('[\x00-\x06\x0E-\x1F]')
+
+
+def escape_html(text):
+ return text.replace('&', '&amp;'). \
+ replace('<', '&lt;'). \
+ replace('>', '&gt;'). \
+ replace('"', '&quot;')
+
+def process_file(store, filename):
+ try:
+ f = open(filename, 'r')
+ except (IOError, OSError):
+ return False
+ llmatch = 0
+ try:
+ for lno, line in enumerate(f):
+ # just some random heuristics to filter out binary files
+ if lno < 100 and binary_re.search(line):
+ return False
+ m = tag_re.search(line)
+ if m:
+ store.setdefault(filename, []).append({
+ 'lno': lno+1,
+ 'tag': m.group('tag'),
+ 'who': m.group('who') or '',
+ 'what': escape_html(m.group('what')),
+ })
+ # 'what' cannot start at column 0
+ llmatch = m.start('what')
+ elif llmatch:
+ # continuation lines
+ # XXX: this is Python centric, doesn't work for
+ # JavaScript, for example.
+ if line[:llmatch].replace('#', '').isspace():
+ cont = line[llmatch:].strip()
+ if cont:
+ store[filename][-1]['what'] += ' ' + escape_html(cont)
+ continue
+ llmatch = 0
+ return True
+ finally:
+ f.close()
+
+
+def main():
+ try:
+ gopts, args = getopt.getopt(sys.argv[1:], "vo:i:")
+ except getopt.GetoptError:
+ print ("Usage: %s [-v] [-i ignoredir]* [-o reportfile.html] "
+ "path ..." % sys.argv[0])
+ return 2
+ opts = {}
+ for opt, val in gopts:
+ if opt == '-i':
+ val = abspath(val)
+ opts.setdefault(opt, []).append(val)
+
+ if not args:
+ args = ['.']
+
+ if '-o' in opts:
+ output = abspath(opts['-o'][-1])
+ else:
+ output = abspath('tags.html')
+
+ verbose = '-v' in opts
+
+ store = {}
+ gnum = 0
+ num = 0
+
+ for path in args:
+ print "Searching for code tags in %s, please wait." % path
+
+ if isfile(path):
+ gnum += 1
+ if process_file(store, path):
+ if verbose:
+ print path + ": found %d tags" % \
+ (path in store and len(store[path]) or 0)
+ num += 1
+ else:
+ if verbose:
+ print path + ": binary or not readable"
+ continue
+ elif not isdir(path):
+ continue
+
+ for root, dirs, files in os.walk(path):
+ if '-i' in opts and abspath(root) in opts['-i']:
+ del dirs[:]
+ continue
+ if '.svn' in dirs:
+ dirs.remove('.svn')
+ for fn in files:
+ gnum += 1
+ if gnum % 50 == 0 and not verbose:
+ sys.stdout.write('.')
+ sys.stdout.flush()
+
+ fn = join(root, fn)
+
+ if fn.endswith('.pyc') or fn.endswith('.pyo'):
+ continue
+ elif '-i' in opts and abspath(fn) in opts['-i']:
+ continue
+ elif abspath(fn) == output:
+ continue
+
+ if fn[:2] == './': fn = fn[2:]
+ if process_file(store, fn):
+ if verbose:
+ print fn + ": found %d tags" % \
+ (fn in store and len(store[fn]) or 0)
+ num += 1
+ else:
+ if verbose:
+ print fn + ": binary or not readable"
+ print
+
+ print "Processed %d of %d files. Found %d tags in %d files." % (
+ num, gnum, sum(len(fitem) for fitem in store.itervalues()), len(store))
+
+ if not store:
+ return 0
+
+ HTML = '''\
+<html>
+<head>
+<title>Code tags report</title>
+<style type="text/css">
+body { font-family: Trebuchet MS,Verdana,sans-serif;
+ width: 80%%; margin-left: auto; margin-right: auto; }
+table { width: 100%%; border-spacing: 0;
+ border: 1px solid #CCC; }
+th { font-weight: bold; background-color: #DDD }
+td { padding: 2px 5px 2px 5px;
+ vertical-align: top; }
+.tr0 { background-color: #EEEEEE; }
+.tr1 { background-color: #F6F6F6; }
+.tag { text-align: center; font-weight: bold; }
+.tr0 .tag { background-color: #FFEEEE; }
+.tr1 .tag { background-color: #FFDDDD; }
+.head { padding-top: 10px; font-size: 100%%; font-weight: bold }
+.XXX { color: #500; }
+.FIXME { color: red; }
+.TODO { color: #880; }
+</style>
+</head>
+<body>
+<h1>Code tags report for %s</h1>
+<table>
+<tr><th>Line</th><th>Tag</th><th>Who</th><th>Description</th></tr>
+%s
+</table>
+</body>
+</html>
+'''
+
+ TABLE = '\n<tr><td class="head" colspan="4">File: %s</td>\n'
+
+ TR = ('<tr class="tr%d"><td class="lno">%%(lno)d</td>'
+ '<td class="tag %%(tag)s">%%(tag)s</td>'
+ '<td class="who">%%(who)s</td><td class="what">%%(what)s</td></tr>')
+
+ f = file(output, 'w')
+ table = '\n'.join(TABLE % fname +
+ '\n'.join(TR % (no % 2,) % entry
+ for no, entry in enumerate(store[fname]))
+ for fname in sorted(store))
+ f.write(HTML % (', '.join(map(abspath, args)), table))
+ f.close()
+
+ print "Report written to %s." % output
+ return 0
+
+if __name__ == '__main__':
+ sys.exit(main())
diff --git a/scripts/fix_epydoc_markup.py b/scripts/fix_epydoc_markup.py
new file mode 100755
index 00000000..76bbdaed
--- /dev/null
+++ b/scripts/fix_epydoc_markup.py
@@ -0,0 +1,46 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+"""
+ fix_epydoc_tables
+ ~~~~~~~~~~~~~~~~~
+
+ Fix epydoc "summary" tables.
+
+ :copyright: 2006 by Georg Brandl.
+ :license: GNU GPL, see LICENSE for more details.
+"""
+
+import sys, os
+from os.path import join
+
+path = sys.argv[1]
+
+for fn in os.listdir(path):
+ fn = join(path, fn)
+ if not fn.endswith(".html"):
+ continue
+
+ ll = list(file(fn))
+ c = False
+ d = False
+ n = False
+
+ for i, l in enumerate(ll):
+ if "<!-- ===" in l:
+ d = ("DETAILS" in l)
+ continue
+ if l.startswith('<table class="summary"') and d:
+ ll[i] = '<table class="detsummary"' + l[len('<table class="summary"'):]
+ c = True
+ continue
+ if l.startswith('<table class="navbar"'):
+ if not n:
+ n = True
+ else:
+ ll[i] = '<div style="height: 20px">&nbsp;</div>\n' + l
+ c = True
+
+ if c:
+ f = file(fn, "w")
+ f.write(''.join(ll))
+ f.close()
diff --git a/scripts/pylintrc b/scripts/pylintrc
new file mode 100644
index 00000000..94e5ea00
--- /dev/null
+++ b/scripts/pylintrc
@@ -0,0 +1,355 @@
+# lint Python modules using external checkers.
+#
+# This is the main checker controling the other ones and the reports
+# generation. It is itself both a raw checker and an astng checker in order
+# to:
+# * handle message activation / deactivation at the module level
+# * handle some basic but necessary stats'data (number of classes, methods...)
+#
+# This checker also defines the following reports:
+# * R0001: Total errors / warnings
+# * R0002: % errors / warnings by module
+# * R0003: Messages
+# * R0004: Global evaluation
+[MASTER]
+
+# Profiled execution.
+profile=no
+
+# Add <file or directory> to the black list. It should be a base name, not a
+# path. You may set this option multiple times.
+ignore=.svn
+
+# Pickle collected data for later comparisons.
+persistent=yes
+
+# Set the cache size for astng objects.
+cache-size=500
+
+# List of plugins (as comma separated values of python modules names) to load,
+# usually to register additional checkers.
+load-plugins=
+
+
+[REPORTS]
+
+# Tells wether to display a full report or only the messages
+reports=no
+
+# Use HTML as output format instead of text
+html=no
+
+# Use a parseable text output format, so your favorite text editor will be able
+# to jump to the line corresponding to a message.
+parseable=no
+
+# Colorizes text output using ansi escape codes
+color=yes
+
+# Put messages in a separate file for each module / package specified on the
+# command line instead of printing them on stdout. Reports (if any) will be
+# written in a file name "pylint_global.[txt|html]".
+files-output=no
+
+# Python expression which should return a note less than 10 (10 is the highest
+# note).You have access to the variables errors warning, statement which
+# respectivly contain the number of errors / warnings messages and the total
+# number of statements analyzed. This is used by the global evaluation report
+# (R0004).
+evaluation=10.0 - ((float(5 * error + warning + refactor + convention) / statement) * 10)
+
+# Add a comment according to your evaluation note. This is used by the global
+# evaluation report (R0004).
+comment=no
+
+# Include message's id in output
+include-ids=no
+
+# Disable some irrelevant messages.
+disable-msg=W0142,W0613,R0201,W0704,C0111,W0703,W0603,I0011,W0122,C0321,W0631,C0203,W0511,W0221,R0401
+
+
+# checks for
+# * unused variables / imports
+# * undefined variables
+# * redefinition of variable from builtins or from an outer scope
+# * use of variable before assigment
+#
+[VARIABLES]
+
+# Enable / disable this checker
+enable-variables=yes
+
+# Tells wether we should check for unused import in __init__ files.
+init-import=no
+
+# A regular expression matching names used for dummy variables (i.e. not used).
+dummy-variables-rgx=_|dummy
+
+# List of additional names supposed to be defined in builtins. Remember that
+# you should avoid to define new builtins when possible.
+additional-builtins=
+
+
+# try to find bugs in the code using type inference
+#
+[TYPECHECK]
+
+# Enable / disable this checker
+enable-typecheck=yes
+
+# Tells wether missing members accessed in mixin class should be ignored. A
+# mixin class is detected if its name ends with "mixin" (case insensitive).
+ignore-mixin-members=yes
+
+# When zope mode is activated, consider the acquired-members option to ignore
+# access to some undefined attributes.
+zope=yes
+
+# List of members which are usually get through zope's acquisition mecanism and
+# so shouldn't trigger E0201 when accessed (need zope=yes to be considered).
+acquired-members=__subclasses__,__dict__
+
+
+# checks for :
+# * doc strings
+# * modules / classes / functions / methods / arguments / variables name
+# * number of arguments, local variables, branchs, returns and statements in
+# functions, methods
+# * required module attributes
+# * dangerous default values as arguments
+# * redefinition of function / method / class
+# * uses of the global statement
+#
+# This checker also defines the following reports:
+# * R0101: Statistics by type
+[BASIC]
+
+# Enable / disable this checker
+enable-basic=yes
+
+# Required attributes for module, separated by a comma
+required-attributes=
+#__revision__
+
+# Regular expression which should only match functions or classes name which do
+# not require a docstring
+no-docstring-rgx=__.*__
+
+# Regular expression which should only match correct module names
+module-rgx=(([a-z_][a-z0-9_]*)|([A-Z][a-zA-Z0-9]+))$
+
+# Regular expression which should only match correct module level names
+const-rgx=(([A-Z_][A-Z1-9_]*)|[A-Za-z_][a-z1-9_]*|(__.*__))$
+
+# Regular expression which should only match correct class names
+class-rgx=([A-Z_][a-zA-Z0-9]+|[a-z_]+|__metaclass__)$
+
+# Regular expression which should only match correct function names
+function-rgx=[a-z_][a-z0-9_]{1,30}$
+
+# Regular expression which should only match correct method names
+method-rgx=[a-z_][a-z0-9_]{1,30}$
+
+# Regular expression which should only match correct instance attribute names
+attr-rgx=[a-z_][a-z0-9_]{0,30}$
+
+# Regular expression which should only match correct argument names
+argument-rgx=[a-z_][a-z0-9_]{0,30}$
+
+# Regular expression which should only match correct variable names
+variable-rgx=[a-z_][a-z0-9_]{0,30}$
+
+# Regular expression which should only match correct list comprehension /
+# generator expression variable names
+inlinevar-rgx=[A-Za-z_][A-Za-z0-9_]*$
+
+# Good variable names which should always be accepted, separated by a comma
+good-names=i,j,k,ex,Run,_
+
+# Bad variable names which should always be refused, separated by a comma
+bad-names=foo,bar,baz,toto,tutu,tata
+
+# List of builtins function names that should not be used, separated by a comma
+bad-functions=apply,input
+
+
+# checks for sign of poor/misdesign:
+# * number of methods, attributes, local variables...
+# * size, complexity of functions, methods
+#
+[DESIGN]
+
+# Enable / disable this checker
+enable-design=yes
+
+# Maximum number of arguments for function / method
+max-args=12
+
+# Maximum number of locals for function / method body
+max-locals=30
+
+# Maximum number of return / yield for function / method body
+max-returns=15
+
+# Maximum number of branch for function / method body
+max-branchs=50
+
+# Maximum number of statements in function / method body
+max-statements=120
+
+# Maximum number of parents for a class (see R0901).
+max-parents=7
+
+# Maximum number of attributes for a class (see R0902).
+max-attributes=25
+
+# Minimum number of public methods for a class (see R0903).
+min-public-methods=0
+
+# Maximum number of public methods for a class (see R0904).
+max-public-methods=30
+
+
+# checks for usage of new style capabilities on old style classes and
+# other new/old styles conflicts problems
+# * use of property, __slots__, super
+# * "super" usage
+# * raising a new style class as exception
+#
+[NEWSTYLE]
+
+# Enable / disable this checker
+enable-newstyle=yes
+
+
+# checks for
+# * external modules dependencies
+# * relative / wildcard imports
+# * cyclic imports
+# * uses of deprecated modules
+#
+# This checker also defines the following reports:
+# * R0401: External dependencies
+# * R0402: Modules dependencies graph
+[IMPORTS]
+
+# Enable / disable this checker
+enable-imports=yes
+
+# Deprecated modules which should not be used, separated by a comma
+deprecated-modules=regsub,string,TERMIOS,Bastion,rexec
+
+# Create a graph of every (i.e. internal and external) dependencies in the
+# given file (report R0402 must not be disabled)
+import-graph=
+
+# Create a graph of external dependencies in the given file (report R0402 must
+# not be disabled)
+ext-import-graph=
+
+# Create a graph of internal dependencies in the given file (report R0402 must
+# not be disabled)
+int-import-graph=
+
+
+# checks for :
+# * methods without self as first argument
+# * overriden methods signature
+# * access only to existant members via self
+# * attributes not defined in the __init__ method
+# * supported interfaces implementation
+# * unreachable code
+#
+[CLASSES]
+
+# Enable / disable this checker
+enable-classes=yes
+
+# List of interface methods to ignore, separated by a comma. This is used for
+# instance to not check methods defines in Zope's Interface base class.
+ignore-iface-methods=isImplementedBy,deferred,extends,names,namesAndDescriptions,queryDescriptionFor,getBases,getDescriptionFor,getDoc,getName,getTaggedValue,getTaggedValueTags,isEqualOrExtendedBy,setTaggedValue,isImplementedByInstancesOf,adaptWith,is_implemented_by
+
+# List of method names used to declare (i.e. assign) instance attributes.
+defining-attr-methods=__init__,__new__,setUp
+
+
+# checks for
+# * excepts without exception filter
+# * string exceptions
+#
+[EXCEPTIONS]
+
+# Enable / disable this checker
+enable-exceptions=yes
+
+
+# does not check anything but gives some raw metrics :
+# * total number of lines
+# * total number of code lines
+# * total number of docstring lines
+# * total number of comments lines
+# * total number of empty lines
+#
+# This checker also defines the following reports:
+# * R0701: Raw metrics
+[METRICS]
+
+# Enable / disable this checker
+enable-metrics=yes
+
+
+# checks for similarities and duplicated code. This computation may be
+# memory / CPU intensive, so you should disable it if you experiments some
+# problems.
+#
+# This checker also defines the following reports:
+# * R0801: Duplication
+[SIMILARITIES]
+
+# Enable / disable this checker
+enable-similarities=yes
+
+# Minimum lines number of a similarity.
+min-similarity-lines=10
+
+# Ignore comments when computing similarities.
+ignore-comments=yes
+
+# Ignore docstrings when computing similarities.
+ignore-docstrings=yes
+
+
+# checks for:
+# * warning notes in the code
+# * PEP 263: source code with non ascii character but no encoding declaration
+#
+[MISCELLANEOUS]
+
+# Enable / disable this checker
+enable-miscellaneous=yes
+
+# List of note tags to take in consideration, separated by a comma.
+notes=FIXME,XXX,TODO,HACK
+
+
+# checks for :
+# * unauthorized constructions
+# * strict indentation
+# * line length
+# * use of <> instead of !=
+#
+[FORMAT]
+
+# Enable / disable this checker
+enable-format=yes
+
+# Maximum number of characters on a single line.
+max-line-length=90
+
+# Maximum number of lines in a module
+max-module-lines=1000
+
+# String used as indentation unit. This is usually " " (4 spaces) or "\t" (1
+# tab).
+indent-string=' '
diff --git a/scripts/reindent.py b/scripts/reindent.py
new file mode 100755
index 00000000..e6ee8287
--- /dev/null
+++ b/scripts/reindent.py
@@ -0,0 +1,291 @@
+#! /usr/bin/env python
+
+# Released to the public domain, by Tim Peters, 03 October 2000.
+# -B option added by Georg Brandl, 2006.
+
+"""reindent [-d][-r][-v] [ path ... ]
+
+-d (--dryrun) Dry run. Analyze, but don't make any changes to files.
+-r (--recurse) Recurse. Search for all .py files in subdirectories too.
+-B (--no-backup) Don't write .bak backup files.
+-v (--verbose) Verbose. Print informative msgs; else only names of changed files.
+-h (--help) Help. Print this usage information and exit.
+
+Change Python (.py) files to use 4-space indents and no hard tab characters.
+Also trim excess spaces and tabs from ends of lines, and remove empty lines
+at the end of files. Also ensure the last line ends with a newline.
+
+If no paths are given on the command line, reindent operates as a filter,
+reading a single source file from standard input and writing the transformed
+source to standard output. In this case, the -d, -r and -v flags are
+ignored.
+
+You can pass one or more file and/or directory paths. When a directory
+path, all .py files within the directory will be examined, and, if the -r
+option is given, likewise recursively for subdirectories.
+
+If output is not to standard output, reindent overwrites files in place,
+renaming the originals with a .bak extension. If it finds nothing to
+change, the file is left alone. If reindent does change a file, the changed
+file is a fixed-point for future runs (i.e., running reindent on the
+resulting .py file won't change it again).
+
+The hard part of reindenting is figuring out what to do with comment
+lines. So long as the input files get a clean bill of health from
+tabnanny.py, reindent should do a good job.
+"""
+
+__version__ = "1"
+
+import tokenize
+import os
+import sys
+
+verbose = 0
+recurse = 0
+dryrun = 0
+no_backup = 0
+
+def usage(msg=None):
+ if msg is not None:
+ print >> sys.stderr, msg
+ print >> sys.stderr, __doc__
+
+def errprint(*args):
+ sep = ""
+ for arg in args:
+ sys.stderr.write(sep + str(arg))
+ sep = " "
+ sys.stderr.write("\n")
+
+def main():
+ import getopt
+ global verbose, recurse, dryrun, no_backup
+
+ try:
+ opts, args = getopt.getopt(sys.argv[1:], "drvhB",
+ ["dryrun", "recurse", "verbose", "help",
+ "no-backup"])
+ except getopt.error, msg:
+ usage(msg)
+ return
+ for o, a in opts:
+ if o in ('-d', '--dryrun'):
+ dryrun += 1
+ elif o in ('-r', '--recurse'):
+ recurse += 1
+ elif o in ('-v', '--verbose'):
+ verbose += 1
+ elif o in ('-B', '--no-backup'):
+ no_backup += 1
+ elif o in ('-h', '--help'):
+ usage()
+ return
+ if not args:
+ r = Reindenter(sys.stdin)
+ r.run()
+ r.write(sys.stdout)
+ return
+ for arg in args:
+ check(arg)
+
+def check(file):
+ if os.path.isdir(file) and not os.path.islink(file):
+ if verbose:
+ print "listing directory", file
+ names = os.listdir(file)
+ for name in names:
+ fullname = os.path.join(file, name)
+ if ((recurse and os.path.isdir(fullname) and
+ not os.path.islink(fullname))
+ or name.lower().endswith(".py")):
+ check(fullname)
+ return
+
+ if verbose:
+ print "checking", file, "...",
+ try:
+ f = open(file)
+ except IOError, msg:
+ errprint("%s: I/O Error: %s" % (file, str(msg)))
+ return
+
+ r = Reindenter(f)
+ f.close()
+ if r.run():
+ if verbose:
+ print "changed."
+ if dryrun:
+ print "But this is a dry run, so leaving it alone."
+ else:
+ print "reindented", file, (dryrun and "(dry run => not really)" or "")
+ if not dryrun:
+ if not no_backup:
+ bak = file + ".bak"
+ if os.path.exists(bak):
+ os.remove(bak)
+ os.rename(file, bak)
+ if verbose:
+ print "renamed", file, "to", bak
+ f = open(file, "w")
+ r.write(f)
+ f.close()
+ if verbose:
+ print "wrote new", file
+ else:
+ if verbose:
+ print "unchanged."
+
+
+class Reindenter:
+
+ def __init__(self, f):
+ self.find_stmt = 1 # next token begins a fresh stmt?
+ self.level = 0 # current indent level
+
+ # Raw file lines.
+ self.raw = f.readlines()
+
+ # File lines, rstripped & tab-expanded. Dummy at start is so
+ # that we can use tokenize's 1-based line numbering easily.
+ # Note that a line is all-blank iff it's "\n".
+ self.lines = [line.rstrip('\n \t').expandtabs() + "\n"
+ for line in self.raw]
+ self.lines.insert(0, None)
+ self.index = 1 # index into self.lines of next line
+
+ # List of (lineno, indentlevel) pairs, one for each stmt and
+ # comment line. indentlevel is -1 for comment lines, as a
+ # signal that tokenize doesn't know what to do about them;
+ # indeed, they're our headache!
+ self.stats = []
+
+ def run(self):
+ tokenize.tokenize(self.getline, self.tokeneater)
+ # Remove trailing empty lines.
+ lines = self.lines
+ while lines and lines[-1] == "\n":
+ lines.pop()
+ # Sentinel.
+ stats = self.stats
+ stats.append((len(lines), 0))
+ # Map count of leading spaces to # we want.
+ have2want = {}
+ # Program after transformation.
+ after = self.after = []
+ # Copy over initial empty lines -- there's nothing to do until
+ # we see a line with *something* on it.
+ i = stats[0][0]
+ after.extend(lines[1:i])
+ for i in range(len(stats)-1):
+ thisstmt, thislevel = stats[i]
+ nextstmt = stats[i+1][0]
+ have = getlspace(lines[thisstmt])
+ want = thislevel * 4
+ if want < 0:
+ # A comment line.
+ if have:
+ # An indented comment line. If we saw the same
+ # indentation before, reuse what it most recently
+ # mapped to.
+ want = have2want.get(have, -1)
+ if want < 0:
+ # Then it probably belongs to the next real stmt.
+ for j in xrange(i+1, len(stats)-1):
+ jline, jlevel = stats[j]
+ if jlevel >= 0:
+ if have == getlspace(lines[jline]):
+ want = jlevel * 4
+ break
+ if want < 0: # Maybe it's a hanging
+ # comment like this one,
+ # in which case we should shift it like its base
+ # line got shifted.
+ for j in xrange(i-1, -1, -1):
+ jline, jlevel = stats[j]
+ if jlevel >= 0:
+ want = have + getlspace(after[jline-1]) - \
+ getlspace(lines[jline])
+ break
+ if want < 0:
+ # Still no luck -- leave it alone.
+ want = have
+ else:
+ want = 0
+ assert want >= 0
+ have2want[have] = want
+ diff = want - have
+ if diff == 0 or have == 0:
+ after.extend(lines[thisstmt:nextstmt])
+ else:
+ for line in lines[thisstmt:nextstmt]:
+ if diff > 0:
+ if line == "\n":
+ after.append(line)
+ else:
+ after.append(" " * diff + line)
+ else:
+ remove = min(getlspace(line), -diff)
+ after.append(line[remove:])
+ return self.raw != self.after
+
+ def write(self, f):
+ f.writelines(self.after)
+
+ # Line-getter for tokenize.
+ def getline(self):
+ if self.index >= len(self.lines):
+ line = ""
+ else:
+ line = self.lines[self.index]
+ self.index += 1
+ return line
+
+ # Line-eater for tokenize.
+ def tokeneater(self, type, token, (sline, scol), end, line,
+ INDENT=tokenize.INDENT,
+ DEDENT=tokenize.DEDENT,
+ NEWLINE=tokenize.NEWLINE,
+ COMMENT=tokenize.COMMENT,
+ NL=tokenize.NL):
+
+ if type == NEWLINE:
+ # A program statement, or ENDMARKER, will eventually follow,
+ # after some (possibly empty) run of tokens of the form
+ # (NL | COMMENT)* (INDENT | DEDENT+)?
+ self.find_stmt = 1
+
+ elif type == INDENT:
+ self.find_stmt = 1
+ self.level += 1
+
+ elif type == DEDENT:
+ self.find_stmt = 1
+ self.level -= 1
+
+ elif type == COMMENT:
+ if self.find_stmt:
+ self.stats.append((sline, -1))
+ # but we're still looking for a new stmt, so leave
+ # find_stmt alone
+
+ elif type == NL:
+ pass
+
+ elif self.find_stmt:
+ # This is the first "real token" following a NEWLINE, so it
+ # must be the first token of the next program statement, or an
+ # ENDMARKER.
+ self.find_stmt = 0
+ if line: # not endmarker
+ self.stats.append((sline, self.level))
+
+# Count number of leading blanks.
+def getlspace(line):
+ i, n = 0, len(line)
+ while i < n and line[i] == " ":
+ i += 1
+ return i
+
+if __name__ == '__main__':
+ main()
diff --git a/setup.py b/setup.py
new file mode 100644
index 00000000..130c40f6
--- /dev/null
+++ b/setup.py
@@ -0,0 +1,31 @@
+# -*- coding: utf-8 -*-
+import ez_setup
+ez_setup.use_setuptools()
+from setuptools import setup, find_packages
+
+import pygments
+
+setup(
+ name = 'Pygments',
+ version = pygments.__version__,
+ url = pygments.__url__,
+ license = pygments.__license__,
+ author = pygments.__author__,
+ description = 'Pygments is a syntax highlighting package written in Python.',
+ long_description = pygments.__doc__,
+ keywords = 'syntax highlighting',
+ packages = find_packages(),
+ scripts = ['pygmentize'],
+ platforms = 'any',
+ zip_safe = False,
+ include_package_data = True,
+ classifiers = [
+ 'License :: OSI Approved :: GNU Lesser General Public License (LGPL)',
+ 'Intended Audience :: Developers',
+ 'Intended Audience :: End Users/Desktop',
+ 'Intended Audience :: System Administrators',
+ 'Development Status :: 3 - Alpha',
+ 'Programming Language :: Python',
+ 'Operating System :: OS Independent',
+ ]
+)
diff --git a/tests/examplefiles/Intro.java b/tests/examplefiles/Intro.java
new file mode 100644
index 00000000..66d2fee2
--- /dev/null
+++ b/tests/examplefiles/Intro.java
@@ -0,0 +1,1660 @@
+/*
+ * Copyright (c) 2003 Sun Microsystems, Inc. All Rights Reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * -Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * -Redistribution in binary form must reproduct the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the distribution.
+ *
+ * Neither the name of Sun Microsystems, Inc. or the names of contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * This software is provided "AS IS," without a warranty of any kind. ALL
+ * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, INCLUDING
+ * ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE
+ * OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN AND ITS LICENSORS SHALL NOT
+ * BE LIABLE FOR ANY DAMAGES OR LIABILITIES SUFFERED BY LICENSEE AS A RESULT
+ * OF OR RELATING TO USE, MODIFICATION OR DISTRIBUTION OF THE SOFTWARE OR ITS
+ * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR ANY LOST
+ * REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL,
+ * INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY
+ * OF LIABILITY, ARISING OUT OF THE USE OF OR INABILITY TO USE SOFTWARE, EVEN
+ * IF SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
+ *
+ * You acknowledge that Software is not designed, licensed or intended for
+ * use in the design, construction, operation or maintenance of any nuclear
+ * facility.
+ */
+
+
+package java2d;
+
+import java.awt.*;
+import java.awt.event.*;
+import java.awt.geom.*;
+import java.awt.image.BufferedImage;
+import java.awt.image.DataBuffer;
+import java.awt.font.*;
+import javax.swing.*;
+import javax.swing.border.*;
+import javax.swing.table.*;
+import javax.swing.event.*;
+import java.util.Vector;
+import java.util.List;
+import java.util.Arrays;
+
+
+
+/**
+ * Introduction to the Java2Demo.
+ *
+ * @version @(#)Intro.java 1.19 03/06/26
+ * @author Brian Lichtenwalter
+ */
+public class Intro extends JPanel {
+
+ static Color black = new Color(20, 20, 20);
+ static Color white = new Color(240, 240, 255);
+ static Color red = new Color(149, 43, 42);
+ static Color blue = new Color(94, 105, 176);
+ static Color yellow = new Color(255, 255, 140);
+
+ static Surface surface;
+ private ScenesTable scenesTable;
+ private boolean doTable;
+
+
+ public Intro() {
+ EmptyBorder eb = new EmptyBorder(80,110,80,110);
+ BevelBorder bb = new BevelBorder(BevelBorder.LOWERED);
+ setBorder(new CompoundBorder(eb,bb));
+ setLayout(new BorderLayout());
+ setBackground(Color.gray);
+ setToolTipText("click for scene table");
+ add(surface = new Surface());
+ addMouseListener(new MouseAdapter() {
+ public void mouseClicked(MouseEvent e) {
+ removeAll();
+ if ((doTable = !doTable)) {
+ setToolTipText("click for animation");
+ surface.stop();
+ if (scenesTable == null) {
+ scenesTable = new ScenesTable();
+ }
+ add(scenesTable);
+ } else {
+ setToolTipText("click for scene table");
+ surface.start();
+ add(surface);
+ }
+ revalidate();
+ repaint();
+ }
+ });
+ }
+
+
+ public void start() {
+ if (!doTable) {
+ surface.start();
+ }
+ }
+
+
+ public void stop() {
+ if (!doTable) {
+ surface.stop();
+ }
+ }
+
+
+ public static void main(String argv[]) {
+ final Intro intro = new Intro();
+ WindowListener l = new WindowAdapter() {
+ public void windowClosing(WindowEvent e) {System.exit(0);}
+ public void windowDeiconified(WindowEvent e) { intro.start(); }
+ public void windowIconified(WindowEvent e) { intro.stop(); }
+ };
+ JFrame f = new JFrame("Java2D Demo - Intro");
+ f.addWindowListener(l);
+ f.getContentPane().add("Center", intro);
+ f.pack();
+ Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
+ int w = 720;
+ int h = 510;
+ f.setLocation(screenSize.width/2 - w/2, screenSize.height/2 - h/2);
+ f.setSize(w, h);
+ f.setVisible(true);
+ intro.start();
+ }
+
+
+ /**
+ * ScenesTable is the list of scenes known to the Director.
+ * Scene participation, scene name and scene pause amount columns.
+ * Global animation delay for scene's steps.
+ */
+ static class ScenesTable extends JPanel implements ActionListener, ChangeListener {
+
+ private JTable table;
+ private TableModel dataModel;
+
+ public ScenesTable() {
+ setBackground(Color.white);
+ setLayout(new BorderLayout());
+ final String[] names = { "", "Scenes", "Pause" };
+
+ dataModel = new AbstractTableModel() {
+ public int getColumnCount() { return names.length; }
+ public int getRowCount() { return surface.director.size();}
+ public Object getValueAt(int row, int col) {
+ Surface.Scene scene = (Surface.Scene) surface.director.get(row);
+ if (col == 0) {
+ return scene.participate;
+ } else if (col == 1) {
+ return scene.name;
+ } else {
+ return scene.pauseAmt;
+ }
+ }
+ public String getColumnName(int col) {return names[col]; }
+ public Class getColumnClass(int c) {
+ return getValueAt(0, c).getClass();
+ }
+ public boolean isCellEditable(int row, int col) {
+ return col != 1 ? true : false;
+ }
+ public void setValueAt(Object aValue, int row, int col) {
+ Surface.Scene scene = (Surface.Scene) surface.director.get(row);
+ if (col == 0) {
+ scene.participate = aValue;
+ } else if (col == 1) {
+ scene.name = aValue;
+ } else {
+ scene.pauseAmt = aValue;
+ }
+ }
+ };
+
+ table = new JTable(dataModel);
+ TableColumn col = table.getColumn("");
+ col.setWidth(16);
+ col.setMinWidth(16);
+ col.setMaxWidth(20);
+ col = table.getColumn("Pause");
+ col.setWidth(60);
+ col.setMinWidth(60);
+ col.setMaxWidth(60);
+ table.sizeColumnsToFit(0);
+
+ JScrollPane scrollpane = new JScrollPane(table);
+ add(scrollpane);
+
+ JPanel panel = new JPanel(new BorderLayout());
+ JButton b = new JButton("Unselect All");
+ b.setHorizontalAlignment(JButton.LEFT);
+ Font font = new Font("serif", Font.PLAIN, 10);
+ b.setFont(font);
+ b.addActionListener(this);
+ panel.add("West", b);
+
+ JSlider slider = new JSlider(JSlider.HORIZONTAL, 0, 200, (int) surface.sleepAmt);
+ slider.addChangeListener(this);
+ TitledBorder tb = new TitledBorder(new EtchedBorder());
+ tb.setTitleFont(font);
+ tb.setTitle("Anim delay = " + String.valueOf(surface.sleepAmt) + " ms");
+ slider.setBorder(tb);
+ slider.setPreferredSize(new Dimension(140,40));
+ slider.setMinimumSize(new Dimension(100,40));
+ slider.setMaximumSize(new Dimension(180,40));
+ panel.add("East", slider);
+
+ add("South", panel);
+ }
+
+
+ public void actionPerformed(ActionEvent e) {
+ JButton b = (JButton) e.getSource();
+ b.setSelected(!b.isSelected());
+ b.setText(b.isSelected() ? "Select All" : "Unselect All");
+ for (int i = 0; i < surface.director.size(); i++) {
+ Surface.Scene scene = (Surface.Scene) surface.director.get(i);
+ scene.participate = new Boolean(!b.isSelected());
+ }
+ table.tableChanged(new TableModelEvent(dataModel));
+ }
+
+
+ public void stateChanged(ChangeEvent e) {
+ JSlider slider = (JSlider) e.getSource();
+ int value = slider.getValue();
+ TitledBorder tb = (TitledBorder) slider.getBorder();
+ tb.setTitle("Anim delay = " + String.valueOf(value) + " ms");
+ surface.sleepAmt = (long) value;
+ slider.repaint();
+ }
+ } // End ScenesTable class
+
+
+
+ /**
+ * Surface is the stage where the Director plays its scenes.
+ */
+ static class Surface extends JPanel implements Runnable {
+
+ static Surface surf;
+ static Image cupanim, java_logo;
+ static BufferedImage bimg;
+ public Director director;
+ public int index;
+ public long sleepAmt = 30;
+ private Thread thread;
+
+
+ public Surface() {
+ surf = this;
+ setBackground(black);
+ setLayout(new BorderLayout());
+ addMouseListener(new MouseAdapter() {
+ public void mouseClicked(MouseEvent e) {
+ if (thread == null) start(); else stop();
+ }
+ });
+ cupanim = DemoImages.getImage("cupanim.gif", this);
+ java_logo = DemoImages.getImage("java_logo.png", this);
+ director = new Director();
+ }
+
+
+ static FontMetrics getMetrics(Font font) {
+ return surf.getFontMetrics(font);
+ }
+
+
+ public void paint(Graphics g) {
+ Dimension d = getSize();
+ if (d.width <= 0 || d.height <= 0) {
+ return;
+ }
+ if (bimg == null || bimg.getWidth() != d.width || bimg.getHeight() != d.height) {
+ bimg = getGraphicsConfiguration().createCompatibleImage(d.width, d.height);
+ // reset future scenes
+ for (int i = index+1; i < director.size(); i++) {
+ ((Scene) director.get(i)).reset(d.width, d.height);
+ }
+ }
+
+ Scene scene = (Scene) director.get(index);
+ if (scene.index <= scene.length) {
+ if (thread != null) {
+ scene.step(d.width, d.height);
+ }
+
+ Graphics2D g2 = bimg.createGraphics();
+ g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
+ RenderingHints.VALUE_ANTIALIAS_ON);
+ g2.setBackground(getBackground());
+ g2.clearRect(0, 0, d.width, d.height);
+
+ scene.render(d.width, d.height, g2);
+
+ if (thread != null) {
+ // increment scene.index after scene.render
+ scene.index++;
+ }
+ g2.dispose();
+ }
+ g.drawImage(bimg, 0, 0, this);
+ }
+
+
+
+ public void start() {
+ if (thread == null) {
+ thread = new Thread(this);
+ thread.setPriority(Thread.MIN_PRIORITY);
+ thread.setName("Intro");
+ thread.start();
+ }
+ }
+
+
+ public synchronized void stop() {
+ if (thread != null) {
+ thread.interrupt();
+ }
+ thread = null;
+ notifyAll();
+ }
+
+
+ public void reset() {
+ index = 0;
+ Dimension d = getSize();
+ for (int i = 0; i < director.size(); i++) {
+ ((Scene) director.get(i)).reset(d.width, d.height);
+ }
+ }
+
+
+ public void run() {
+
+ Thread me = Thread.currentThread();
+
+ while (thread == me && !isShowing() || getSize().width <= 0) {
+ try {
+ thread.sleep(500);
+ } catch (InterruptedException e) { return; }
+ }
+
+ if (index == 0) {
+ reset();
+ }
+
+ while (thread == me) {
+ Scene scene = (Scene) director.get(index);
+ if (((Boolean) scene.participate).booleanValue()) {
+ repaint();
+ try {
+ thread.sleep(sleepAmt);
+ } catch (InterruptedException e) { break; }
+ if (scene.index > scene.length) {
+ scene.pause(thread);
+ if (++index >= director.size()) {
+ reset();
+ }
+ }
+ } else {
+ if (++index >= director.size()) {
+ reset();
+ }
+ }
+ }
+ thread = null;
+ }
+
+
+
+ /**
+ * Part is a piece of the scene. Classes must implement Part
+ * inorder to participate in a scene.
+ */
+ interface Part {
+ public void reset(int newwidth, int newheight);
+ public void step(int w, int h);
+ public void render(int w, int h, Graphics2D g2);
+ public int getBegin();
+ public int getEnd();
+ }
+
+
+
+ /**
+ * Director is the holder of the scenes, their names & pause amounts
+ * between scenes.
+ */
+ static class Director extends Vector {
+
+ GradientPaint gp = new GradientPaint(0,40,blue,38,2,black);
+ Font f1 = new Font("serif", Font.PLAIN, 200);
+ Font f2 = new Font("serif", Font.PLAIN, 120);
+ Font f3 = new Font("serif", Font.PLAIN, 72);
+ Object parts[][][] = {
+ { { "J - scale text on gradient", "0" },
+ { new GpE(GpE.BURI, black, blue, 0, 20),
+ new TxE("J", f1, TxE.SCI, yellow, 2, 20) } },
+ { { "2 - scale & rotate text on gradient" , "0" },
+ { new GpE(GpE.BURI, blue, black, 0, 22),
+ new TxE("2", f1, TxE.RI | TxE.SCI, yellow, 2, 22) } },
+ { { "D - scale text on gradient", "0" },
+ { new GpE(GpE.BURI, black, blue, 0, 20),
+ new TxE("D", f1, TxE.SCI, yellow, 2, 20) } },
+ { { "Java2D - scale & rotate text on gradient", "1000" },
+ { new GpE(GpE.SIH, blue, black, 0, 40),
+ new TxE("Java2D", f2, TxE.RI | TxE.SCI, yellow, 0, 40) }},
+ { { "Previous scene dither dissolve out", "0"},
+ { new DdE(0, 20, 1) }},
+ { { "Graphics Features", "999" },
+ { new Temp(Temp.RECT, null, 0, 15),
+ new Temp(Temp.IMG, java_logo, 2, 15),
+ new Temp(Temp.RNA | Temp.INA, java_logo, 16, 130),
+ new Features(Features.GRAPHICS, 16, 130) }},
+ { { "Java2D - texture text on gradient", "1000"},
+ { new GpE(GpE.WI, blue, black, 0, 20),
+ new GpE(GpE.WD, blue, black, 21, 40),
+ new TpE(TpE.OI | TpE.NF, black, yellow, 4, 0, 10),
+ new TpE(TpE.OD | TpE.NF, black, yellow, 4, 11, 20),
+ new TpE(TpE.OI | TpE.NF | TpE.HAF, black, yellow,5,21,40),
+ new TxE("Java2D", f2, 0, null, 0, 40) }},
+ { { "Previous scene random close out", "0"},
+ { new CoE(CoE.RAND, 0, 20) } },
+ { { "Text Features", "999" },
+ { new Temp(Temp.RECT, null, 0, 15),
+ new Temp(Temp.IMG, java_logo, 2, 15),
+ new Temp(Temp.RNA | Temp.INA, java_logo, 16, 130),
+ new Features(Features.TEXT, 16, 130) }},
+ { { "Java2D - composite text on texture", "1000"},
+ { new TpE(TpE.RI, black, gp, 40, 0, 20),
+ new TpE(TpE.RD, black, gp, 40, 21, 40),
+ new TpE(TpE.RI, black, gp, 40, 41, 60),
+ new TxE("Java2D", f2, TxE.AC, yellow, 0, 60) }},
+ { { "Previous scene dither dissolve out", "0"},
+ { new DdE(0, 20, 4) }},
+ { { "Imaging Features", "999" },
+ { new Temp(Temp.RECT, null, 0, 15),
+ new Temp(Temp.IMG, java_logo, 2, 15),
+ new Temp(Temp.RNA | Temp.INA, java_logo, 16, 130),
+ new Features(Features.IMAGES, 16, 130) }},
+ { { "Java2D - text on gradient", "1000" },
+ { new GpE(GpE.SDH, blue, black, 0, 20),
+ new GpE(GpE.SIH, blue, black, 21, 40),
+ new GpE(GpE.SDH, blue, black, 41, 50),
+ new GpE(GpE.INC | GpE.NF, red, yellow, 0, 50),
+ new TxE("Java2D", f2, TxE.NOP, null, 0, 50) }},
+ { { "Previous scene ellipse close out", "0"},
+ { new CoE(CoE.OVAL, 0, 20) } },
+ { { "Color Features", "999" },
+ { new Temp(Temp.RECT, null, 0, 15),
+ new Temp(Temp.IMG, java_logo, 2, 15),
+ new Temp(Temp.RNA | Temp.INA, java_logo, 16, 99),
+ new Features(Features.COLOR, 16, 99) }},
+ { { "Java2D - composite and rotate text on paints", "2000" },
+ { new GpE(GpE.BURI, black, blue, 0, 20),
+ new GpE(GpE.BURD, black, blue, 21, 30),
+ new TpE(TpE.OI | TpE.HAF, black, blue, 10, 31, 40),
+ new TxE("Java2D", f2, TxE.AC | TxE.RI, yellow, 0, 40) }},
+ { { "Previous scene subimage transform out", "0" },
+ { new SiE(60, 60, 0, 40) }},
+ { { "CREDITS - transform in", "1000" },
+ { new LnE(LnE.ACI | LnE.ZOOMI | LnE.RI, 0, 60),
+ new TxE("CREDITS", f3, TxE.AC | TxE.SCI, Color.red,20,30),
+ new TxE("CREDITS", f3, TxE.SCXD, Color.red, 31, 38),
+ new TxE("CREDITS", f3, TxE.SCXI, Color.red, 39, 48),
+ new TxE("CREDITS", f3, TxE.SCXD, Color.red, 49, 54),
+ new TxE("CREDITS", f3, TxE.SCXI, Color.red, 55, 60) }},
+ { { "CREDITS - transform out", "0" },
+ { new LnE(LnE.ACD | LnE.ZOOMD | LnE.RD, 0, 45),
+ new TxE("CREDITS", f3, 0, Color.red, 0, 9),
+ new TxE("CREDITS", f3, TxE.SCD | TxE.RD, Color.red,10,30)}},
+ { { "Contributors", "1000" },
+ { new Temp(Temp.RECT, null, 0, 30),
+ new Temp(Temp.IMG, cupanim, 4, 30),
+ new Temp(Temp.RNA | Temp.INA, cupanim, 31, 200),
+ new Contributors(34, 200) } },
+ };
+
+
+ public Director() {
+ for (int i = 0; i < parts.length; i++) {
+ Vector v = new Vector();
+ for (int j = 0; j < parts[i][1].length; j++) {
+ v.addElement(parts[i][1][j]);
+ }
+ addElement(new Scene(v, parts[i][0][0], parts[i][0][1]));
+ }
+ }
+ }
+
+
+
+ /**
+ * Scene is the manager of the parts.
+ */
+ static class Scene extends Object {
+ public Object name;
+ public Object participate = new Boolean(true);
+ public Object pauseAmt;
+ public Vector parts;
+ public int index;
+ public int length;
+
+ public Scene(Vector parts, Object name, Object pauseAmt) {
+ this.name = name;
+ this.parts = parts;
+ this.pauseAmt = pauseAmt;
+ for (int i = 0; i < parts.size(); i++) {
+ if (((Part) parts.get(i)).getEnd() > length) {
+ length = ((Part) parts.get(i)).getEnd();
+ }
+ }
+ }
+
+ public void reset(int w, int h) {
+ index = 0;
+ for (int i = 0; i < parts.size(); i++) {
+ ((Part) parts.get(i)).reset(w, h);
+ }
+ }
+
+ public void step(int w, int h) {
+ for (int i = 0; i < parts.size(); i++) {
+ Part part = (Part) parts.get(i);
+ if (index >= part.getBegin() && index <= part.getEnd()) {
+ part.step(w, h);
+ }
+ }
+ }
+
+ public void render(int w, int h, Graphics2D g2) {
+ for (int i = 0; i < parts.size(); i++) {
+ Part part = (Part) parts.get(i);
+ if (index >= part.getBegin() && index <= part.getEnd()) {
+ part.render(w, h, g2);
+ }
+ }
+ }
+
+ public void pause(Thread thread) {
+ try {
+ thread.sleep(Long.parseLong((String) pauseAmt));
+ } catch (Exception e) { }
+ System.gc();
+ }
+ } // End Scene class
+
+
+
+ /**
+ * Text Effect. Transformation of characters. Clip or fill.
+ */
+ static class TxE implements Part {
+
+ static final int INC = 1;
+ static final int DEC = 2;
+ static final int R = 4; // rotate
+ static final int RI = R | INC;
+ static final int RD = R | DEC;
+ static final int SC = 8; // scale
+ static final int SCI = SC | INC;
+ static final int SCD = SC | DEC;
+ static final int SCX = 16; // scale invert x
+ static final int SCXI = SCX | SC | INC;
+ static final int SCXD = SCX | SC | DEC;
+ static final int SCY = 32; // scale invert y
+ static final int SCYI = SCY | SC | INC;
+ static final int SCYD = SCY | SC | DEC;
+ static final int AC = 64; // AlphaComposite
+ static final int CLIP = 128; // Clipping
+ static final int NOP = 512; // No Paint
+ private int beginning, ending;
+ private int type;
+ private double rIncr, sIncr;
+ private double sx, sy, rotate;
+ private Shape shapes[], txShapes[];
+ private int sw;
+ private int numRev;
+ private Paint paint;
+
+
+ public TxE(String text,
+ Font font,
+ int type,
+ Paint paint,
+ int beg,
+ int end) {
+ this.type = type;
+ this.paint = paint;
+ this.beginning = beg;
+ this.ending = end;
+
+ setIncrements(2);
+
+ char[] chars = text.toCharArray();
+ shapes = new Shape[chars.length];
+ txShapes = new Shape[chars.length];
+ FontRenderContext frc = new FontRenderContext(null,true,true);
+ TextLayout tl = new TextLayout(text, font, frc);
+ sw = (int) tl.getOutline(null).getBounds().getWidth();
+ for (int j = 0; j < chars.length; j++) {
+ String s = String.valueOf(chars[j]);
+ shapes[j] = new TextLayout(s, font, frc).getOutline(null);
+ }
+ }
+
+
+ public void setIncrements(double numRevolutions) {
+ this.numRev = (int) numRevolutions;
+ rIncr = 360.0 / ((ending - beginning) / numRevolutions);
+ sIncr = 1.0 / (ending - beginning);
+ if ((type & SCX) != 0 || (type & SCY) != 0) {
+ sIncr *= 2;
+ }
+ if ((type & DEC) != 0) {
+ rIncr = -rIncr;
+ sIncr = -sIncr;
+ }
+ }
+
+
+ public void reset(int w, int h) {
+ if (type == SCXI) {
+ sx = -1.0; sy = 1.0;
+ } else if (type == SCYI) {
+ sx = 1.0; sy = -1.0;
+ } else {
+ sx = sy = (type & DEC) != 0 ? 1.0 : 0.0;
+ }
+ rotate = 0;
+ }
+
+
+ public void step(int w, int h) {
+
+ float charWidth = w/2-sw/2;
+
+ for (int i = 0; i < shapes.length; i++) {
+ AffineTransform at = new AffineTransform();
+ Rectangle2D maxBounds = shapes[i].getBounds();
+ at.translate(charWidth, h/2+maxBounds.getHeight()/2);
+ charWidth += (float) maxBounds.getWidth() + 1;
+ Shape shape = at.createTransformedShape(shapes[i]);
+ Rectangle2D b1 = shape.getBounds2D();
+
+ if ((type & R) != 0) {
+ at.rotate(Math.toRadians(rotate));
+ }
+ if ((type & SC) != 0) {
+ at.scale(sx, sy);
+ }
+ shape = at.createTransformedShape(shapes[i]);
+ Rectangle2D b2 = shape.getBounds2D();
+
+ double xx = (b1.getX()+b1.getWidth()/2)
+ - (b2.getX()+b2.getWidth()/2);
+ double yy = (b1.getY()+b1.getHeight()/2)
+ - (b2.getY()+b2.getHeight()/2);
+ AffineTransform toCenterAT = new AffineTransform();
+ toCenterAT.translate(xx, yy);
+ toCenterAT.concatenate(at);
+ txShapes[i] = toCenterAT.createTransformedShape(shapes[i]);
+ }
+ // avoid over rotation
+ if (Math.abs(rotate) <= numRev * 360) {
+ rotate += rIncr;
+ if ((type & SCX) != 0) {
+ sx += sIncr;
+ } else if ((type & SCY) != 0) {
+ sy += sIncr;
+ } else {
+ sx += sIncr; sy += sIncr;
+ }
+ }
+ }
+
+
+ public void render(int w, int h, Graphics2D g2) {
+ Composite saveAC = null;
+ if ((type & AC) != 0 && sx > 0 && sx < 1) {
+ saveAC = g2.getComposite();
+ g2.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, (float) sx));
+ }
+ GeneralPath path = null;
+ if ((type & CLIP) != 0) {
+ path = new GeneralPath();
+ }
+ if (paint != null) {
+ g2.setPaint(paint);
+ }
+ for (int i = 0; i < txShapes.length; i++) {
+ if ((type & CLIP) != 0) {
+ path.append(txShapes[i], false);
+ } else {
+ g2.fill(txShapes[i]);
+ }
+ }
+ if ((type & CLIP) != 0) {
+ g2.clip(path);
+ }
+ if (saveAC != null) {
+ g2.setComposite(saveAC);
+ }
+ }
+
+
+ public int getBegin() {
+ return beginning;
+ }
+
+ public int getEnd() {
+ return ending;
+ }
+ } // End TxE class
+
+
+
+
+ /**
+ * GradientPaint Effect. Burst, split, horizontal and
+ * vertical gradient fill effects.
+ */
+ static class GpE implements Part {
+
+ static final int INC = 1; // increasing
+ static final int DEC = 2; // decreasing
+ static final int CNT = 4; // center
+ static final int WID = 8; // width
+ static final int WI = WID | INC;
+ static final int WD = WID | DEC;
+ static final int HEI = 16; // height
+ static final int HI = HEI | INC;
+ static final int HD = HEI | DEC;
+ static final int SPL = 32 | CNT; // split
+ static final int SIW = SPL | INC | WID;
+ static final int SDW = SPL | DEC | WID;
+ static final int SIH = SPL | INC | HEI;
+ static final int SDH = SPL | DEC | HEI;
+ static final int BUR = 64 | CNT; // burst
+ static final int BURI = BUR | INC;
+ static final int BURD = BUR | DEC;
+ static final int NF = 128; // no fill
+ private Color c1, c2;
+ private int beginning, ending;
+ private float incr, index;
+ private Vector rect = new Vector();
+ private Vector grad = new Vector();
+ private int type;
+
+
+ public GpE(int type, Color c1, Color c2, int beg, int end) {
+ this.type = type;
+ this.c1 = c1;
+ this.c2 = c2;
+ this.beginning = beg;
+ this.ending = end;
+ }
+
+
+ public void reset(int w, int h) {
+ incr = 1.0f / (ending - beginning);
+ if ((type & CNT) != 0) {
+ incr /= 2.3f;
+ }
+ if ((type & CNT) != 0 && (type & INC) != 0) {
+ index = 0.5f;
+ } else if ((type & DEC) != 0) {
+ index = 1.0f;
+ incr = -incr;
+ } else {
+ index = 0.0f;
+ }
+ index += incr;
+ }
+
+
+ public void step(int w, int h) {
+ rect.clear();
+ grad.clear();
+
+ if ((type & WID) != 0) {
+ float w2 = 0, x1 = 0, x2 = 0;
+ if ((type & SPL) != 0) {
+ w2 = w * 0.5f;
+ x1 = w * (1.0f - index);
+ x2 = w * index;
+ } else {
+ w2 = w * index;
+ x1 = x2 = w2;
+ }
+ rect.addElement(new Rectangle2D.Float(0, 0, w2, h));
+ rect.addElement(new Rectangle2D.Float(w2, 0, w-w2, h));
+ grad.addElement(new GradientPaint(0,0,c1,x1,0,c2));
+ grad.addElement(new GradientPaint(x2,0,c2,w,0,c1));
+ } else if ((type & HEI) != 0) {
+ float h2 = 0, y1 = 0, y2 = 0;
+ if ((type & SPL) != 0) {
+ h2 = h * 0.5f;
+ y1 = h * (1.0f - index);
+ y2 = h * index;
+ } else {
+ h2 = h * index;
+ y1 = y2 = h2;
+ }
+ rect.addElement(new Rectangle2D.Float(0, 0, w, h2));
+ rect.addElement(new Rectangle2D.Float(0, h2, w, h-h2));
+ grad.addElement(new GradientPaint(0,0,c1,0,y1,c2));
+ grad.addElement(new GradientPaint(0,y2,c2,0,h,c1));
+ } else if ((type & BUR) != 0) {
+
+ float w2 = w/2;
+ float h2 = h/2;
+
+ rect.addElement(new Rectangle2D.Float(0, 0, w2, h2));
+ rect.addElement(new Rectangle2D.Float(w2, 0, w2, h2));
+ rect.addElement(new Rectangle2D.Float(0, h2, w2, h2));
+ rect.addElement(new Rectangle2D.Float(w2, h2, w2, h2));
+
+ float x1 = w * (1.0f - index);
+ float x2 = w * index;
+ float y1 = h * (1.0f - index);
+ float y2 = h * index;
+
+ grad.addElement(new GradientPaint(0,0,c1,x1,y1,c2));
+ grad.addElement(new GradientPaint(w,0,c1,x2,y1,c2));
+ grad.addElement(new GradientPaint(0,h,c1,x1,y2,c2));
+ grad.addElement(new GradientPaint(w,h,c1,x2,y2,c2));
+ } else if ((type & NF) != 0) {
+ float x = w * index;
+ float y = h * index;
+ grad.addElement(new GradientPaint(0,0,c1,0,y,c2));
+ }
+
+ if ((type & INC) != 0 || (type & DEC) != 0) {
+ index += incr;
+ }
+ }
+
+
+ public void render(int w, int h, Graphics2D g2) {
+ g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
+ RenderingHints.VALUE_ANTIALIAS_OFF);
+ for (int i = 0; i < grad.size(); i++) {
+ g2.setPaint((GradientPaint) grad.get(i));
+ if ((type & NF) == 0) {
+ g2.fill((Rectangle2D) rect.get(i));
+ }
+ }
+ g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
+ RenderingHints.VALUE_ANTIALIAS_ON);
+ }
+
+ public int getBegin() {
+ return beginning;
+ }
+
+ public int getEnd() {
+ return ending;
+ }
+ } // End GpE class
+
+
+
+ /**
+ * TexturePaint Effect. Expand and collapse a texture.
+ */
+ static class TpE implements Part {
+
+ static final int INC = 1; // increasing
+ static final int DEC = 2; // decreasing
+ static final int OVAL = 4; // oval
+ static final int RECT = 8; // rectangle
+ static final int HAF = 16; // half oval or rect size
+ static final int OI = OVAL | INC;
+ static final int OD = OVAL | DEC;
+ static final int RI = RECT | INC;
+ static final int RD = RECT | DEC;
+ static final int NF = 32; // no fill
+ private Paint p1, p2;
+ private int beginning, ending;
+ private float incr, index;
+ private TexturePaint texture;
+ private int type;
+ private int size;
+ private BufferedImage bimg;
+ private Rectangle rect;
+
+
+ public TpE(int type, Paint p1, Paint p2, int size,
+ int beg, int end) {
+ this.type = type;
+ this.p1 = p1;
+ this.p2 = p2;
+ this.beginning = beg;
+ this.ending = end;
+ setTextureSize(size);
+ }
+
+
+ public void setTextureSize(int size) {
+ this.size = size;
+ bimg = new BufferedImage(size,size,BufferedImage.TYPE_INT_RGB);
+ rect = new Rectangle(0,0,size,size);
+ }
+
+
+ public void reset(int w, int h) {
+ incr = (float) (size) / (float) (ending - beginning);
+ if ((type & HAF) != 0) {
+ incr /= 2;
+ }
+ if ((type & DEC) != 0) {
+ index = size;
+ if ((type & HAF) != 0) {
+ index /= 2;
+ }
+ incr = -incr;
+ } else {
+ index = 0.0f;
+ }
+ index += incr;
+ }
+
+
+ public void step(int w, int h) {
+ Graphics2D g2 = bimg.createGraphics();
+ g2.setPaint(p1);
+ g2.fillRect(0,0,size,size);
+ g2.setPaint(p2);
+ if ((type & OVAL) != 0) {
+ g2.fill(new Ellipse2D.Float(0,0,index,index));
+ } else if ((type & RECT) != 0) {
+ g2.fill(new Rectangle2D.Float(0,0,index,index));
+ }
+ texture = new TexturePaint(bimg, rect);
+ g2.dispose();
+ index += incr;
+ }
+
+
+ public void render(int w, int h, Graphics2D g2) {
+ g2.setPaint(texture);
+ if ((type & NF) == 0) {
+ g2.fillRect(0, 0, w, h);
+ }
+ }
+
+ public int getBegin() {
+ return beginning;
+ }
+
+ public int getEnd() {
+ return ending;
+ }
+ } // End TpE class
+
+
+
+ /**
+ * Close out effect. Close out the buffered image with different
+ * geometry shapes.
+ */
+ static class CoE implements Part {
+
+ static final int WID = 1;
+ static final int HEI = 2;
+ static final int OVAL = 4;
+ static final int RECT = 8;
+ static final int RAND = 16;
+ static final int ARC = 32;
+ private int type;
+ private int beginning, ending;
+ private BufferedImage bimg;
+ private Shape shape;
+ private double zoom, extent;
+ private double zIncr, eIncr;
+ private boolean doRandom;
+
+
+ public CoE(int type, int beg, int end) {
+ this.type = type;
+ this.beginning = beg;
+ this.ending = end;
+ zIncr = -(2.0 / (ending - beginning));
+ eIncr = 360.0 / (ending - beginning);
+ doRandom = (type & RAND) != 0;
+ }
+
+
+ public void reset(int w, int h) {
+ if (doRandom) {
+ int num = (int) (Math.random() * 5.0);
+ switch (num) {
+ case 0 : type = OVAL; break;
+ case 1 : type = RECT; break;
+ case 2 : type = RECT | WID; break;
+ case 3 : type = RECT | HEI; break;
+ case 4 : type = ARC; break;
+ default : type = OVAL;
+ }
+ }
+ shape = null;
+ bimg = null;
+ extent = 360.0;
+ zoom = 2.0;
+ }
+
+
+ public void step(int w, int h) {
+ if (bimg == null) {
+ int biw = Surface.bimg.getWidth();
+ int bih = Surface.bimg.getHeight();
+ bimg = new BufferedImage(biw, bih, BufferedImage.TYPE_INT_RGB);
+ Graphics2D big = bimg.createGraphics();
+ big.drawImage(Surface.bimg, 0, 0, null);
+ }
+ double z = Math.min(w, h) * zoom;
+ if ((type & OVAL) != 0) {
+ shape = new Ellipse2D.Double(w/2-z/2,h/2-z/2,z,z);
+ } else if ((type & ARC) != 0) {
+ shape = new Arc2D.Double(-100,-100,w+200,h+200,90,extent,Arc2D.PIE);
+ extent -= eIncr;
+ } else if ((type & RECT) != 0) {
+ if ((type & WID) != 0) {
+ shape = new Rectangle2D.Double(w/2-z/2,0,z,h);
+ } else if ((type & HEI) != 0) {
+ shape = new Rectangle2D.Double(0,h/2-z/2,w,z);
+ } else {
+ shape = new Rectangle2D.Double(w/2-z/2,h/2-z/2,z,z);
+ }
+ }
+ zoom += zIncr;
+ }
+
+
+ public void render(int w, int h, Graphics2D g2) {
+ g2.clip(shape);
+ g2.drawImage(bimg, 0, 0, null);
+ }
+
+ public int getBegin() {
+ return beginning;
+ }
+
+ public int getEnd() {
+ return ending;
+ }
+ } // End CoE class
+
+
+
+ /**
+ * Dither Dissolve Effect. For each successive step in the animation,
+ * a pseudo-random starting horizontal position is chosen using list,
+ * and then the corresponding points created from xlist and ylist are
+ * blacked out for the current "chunk". The x and y chunk starting
+ * positions are each incremented by the associated chunk size, and
+ * this process is repeated for the number of "steps" in the
+ * animation, causing an equal number of pseudo-randomly picked
+ * "blocks" to be blacked out during each step of the animation.
+ */
+ static class DdE implements Part {
+
+ private int beginning, ending;
+ private BufferedImage bimg;
+ private Graphics2D big;
+ private List list, xlist, ylist;
+ private int xeNum, yeNum; // element number
+ private int xcSize, ycSize; // chunk size
+ private int inc;
+ private int blocksize;
+
+
+ public DdE(int beg, int end, int blocksize) {
+ this.beginning = beg;
+ this.ending = end;
+ this.blocksize = blocksize;
+ }
+
+ private void createShuffledLists() {
+ int width = bimg.getWidth();
+ int height = bimg.getHeight();
+ Integer xarray[] = new Integer[width];
+ Integer yarray[] = new Integer[height];
+ Integer array[] = new Integer[ending - beginning + 1];
+ for (int i = 0; i < xarray.length; i++) {
+ xarray[i] = new Integer(i);
+ }
+ for (int j = 0; j < yarray.length; j++) {
+ yarray[j] = new Integer(j);
+ }
+ for (int k = 0; k < array.length; k++) {
+ array[k] = new Integer(k);
+ }
+ java.util.Collections.shuffle(xlist = Arrays.asList(xarray));
+ java.util.Collections.shuffle(ylist = Arrays.asList(yarray));
+ java.util.Collections.shuffle(list = Arrays.asList(array));
+ }
+
+ public void reset(int w, int h) {
+ bimg = null;
+ }
+
+ public void step(int w, int h) {
+ if (bimg == null) {
+ int biw = Surface.bimg.getWidth();
+ int bih = Surface.bimg.getHeight();
+ bimg = new BufferedImage(biw, bih, BufferedImage.TYPE_INT_RGB);
+ createShuffledLists();
+ big = bimg.createGraphics();
+ big.drawImage(Surface.bimg, 0, 0, null);
+ xcSize = (xlist.size() / (ending - beginning)) + 1;
+ ycSize = (ylist.size() / (ending - beginning)) + 1;
+ xeNum = 0;
+ inc = 0;
+ }
+ xeNum = xcSize * ((Integer)list.get(inc)).intValue();
+ yeNum = -ycSize;
+ inc++;
+ }
+
+
+ public void render(int w, int h, Graphics2D g2) {
+ big.setColor(black);
+
+ for (int k = 0; k <= (ending - beginning); k++) {
+ if ((xeNum + xcSize) > xlist.size()) {
+ xeNum = 0;
+ } else {
+ xeNum += xcSize;
+ }
+ yeNum += ycSize;
+
+ for (int i = xeNum; i < xeNum+xcSize && i < xlist.size(); i++) {
+ for (int j = yeNum; j < yeNum+ycSize && j < ylist.size(); j++) {
+ int xval = ((Integer)xlist.get(i)).intValue();
+ int yval = ((Integer)ylist.get(j)).intValue();
+ if (((xval % blocksize) == 0) &&
+ ((yval % blocksize) == 0)) {
+ big.fillRect(xval, yval, blocksize, blocksize);
+ }
+ }
+ }
+ }
+
+ g2.drawImage(bimg, 0, 0, null);
+ }
+
+ public int getBegin() {
+ return beginning;
+ }
+
+ public int getEnd() {
+ return ending;
+ }
+ } // End DdE class
+
+
+ /**
+ * Subimage effect. Subimage the scene's buffered
+ * image then rotate and scale down the subimages.
+ */
+ static class SiE implements Part {
+
+ private int beginning, ending;
+ private BufferedImage bimg;
+ private double rIncr, sIncr;
+ private double scale, rotate;
+ private int siw, sih;
+ private Vector subs = new Vector(20);
+ private Vector pts = new Vector(20);
+
+
+ public SiE(int siw, int sih, int beg, int end) {
+ this.siw = siw;
+ this.sih = sih;
+ this.beginning = beg;
+ this.ending = end;
+ rIncr = 360.0 / (ending - beginning);
+ sIncr = 1.0 / (ending - beginning);
+ }
+
+
+ public void reset(int w, int h) {
+ scale = 1.0;
+ rotate = 0.0;
+ bimg = null;
+ subs.clear();
+ pts.clear();
+ }
+
+
+ public void step(int w, int h) {
+ if (bimg == null) {
+ int biw = Surface.bimg.getWidth();
+ int bih = Surface.bimg.getHeight();
+ bimg = new BufferedImage(biw, bih, BufferedImage.TYPE_INT_RGB);
+ Graphics2D big = bimg.createGraphics();
+ big.drawImage(Surface.bimg, 0, 0, null);
+ for (int x = 0; x < w && scale > 0.0; x+=siw) {
+ int ww = x+siw < w ? siw : w-x;
+ for (int y = 0; y < h; y+=sih) {
+ int hh = y+sih < h ? sih : h-y;
+ subs.addElement(bimg.getSubimage(x,y,ww,hh));
+ pts.addElement(new Point(x, y));
+ }
+ }
+ }
+
+ rotate += rIncr;
+ scale -= sIncr;
+ }
+
+
+ public void render(int w, int h, Graphics2D g2) {
+ AffineTransform saveTx = g2.getTransform();
+ g2.setColor(blue);
+ for (int i = 0; i < subs.size() && scale > 0.0; i++) {
+ BufferedImage bi = (BufferedImage) subs.get(i);
+ Point p = (Point) pts.get(i);
+ int ww = bi.getWidth();
+ int hh = bi.getHeight();
+ AffineTransform at = new AffineTransform();
+ at.rotate(Math.toRadians(rotate), p.x+ww/2, p.y+hh/2);
+ at.translate(p.x, p.y);
+ at.scale(scale, scale);
+
+ Rectangle b1 = new Rectangle(0, 0, ww, hh);
+ Shape shape = at.createTransformedShape(b1);
+ Rectangle2D b2 = shape.getBounds2D();
+ double xx = (p.x+ww/2) - (b2.getX()+b2.getWidth()/2);
+ double yy = (p.y+hh/2) - (b2.getY()+b2.getHeight()/2);
+ AffineTransform toCenterAT = new AffineTransform();
+ toCenterAT.translate(xx, yy);
+ toCenterAT.concatenate(at);
+
+ g2.setTransform(toCenterAT);
+ g2.drawImage(bi, 0, 0, null);
+ g2.draw(b1);
+ }
+ g2.setTransform(saveTx);
+ }
+
+ public int getBegin() {
+ return beginning;
+ }
+
+ public int getEnd() {
+ return ending;
+ }
+ } // End SiE class
+
+
+
+
+ /**
+ * Line Effect. Flattened ellipse with lines from the center
+ * to the edge. Expand or collapse the ellipse. Fade in or out
+ * the lines.
+ */
+ static class LnE implements Part {
+
+ static final int INC = 1;
+ static final int DEC = 2;
+ static final int R = 4; // rotate
+ static final int RI = R | INC;
+ static final int RD = R | DEC;
+ static final int ZOOM = 8; // zoom
+ static final int ZOOMI = ZOOM | INC;
+ static final int ZOOMD = ZOOM | DEC;
+ static final int AC = 32; // AlphaComposite
+ static final int ACI = 32 | INC;
+ static final int ACD = 32 | DEC;
+ private int beginning, ending;
+ private double rIncr, rotate;
+ private double zIncr, zoom;
+ private Vector pts = new Vector();
+ private float alpha, aIncr;
+ private int type;
+
+
+ public LnE(int type, int beg, int end) {
+ this.type = type;
+ this.beginning = beg;
+ this.ending = end;
+ rIncr = 360.0 / (ending - beginning);
+ aIncr = 0.9f / (ending - beginning);
+ zIncr = 2.0 / (ending - beginning);
+ if ((type & DEC) != 0) {
+ rIncr = -rIncr;
+ aIncr = -aIncr;
+ zIncr = -zIncr;
+ }
+ }
+
+
+ public void generatePts(int w, int h, double sizeF) {
+ pts.clear();
+ double size = Math.min(w, h) * sizeF;
+ Ellipse2D ellipse = new Ellipse2D.Double(w/2-size/2,h/2-size/2,size,size);
+ PathIterator pi = ellipse.getPathIterator(null, 0.8);
+ while ( !pi.isDone() ) {
+ double[] pt = new double[6];
+ switch ( pi.currentSegment(pt) ) {
+ case FlatteningPathIterator.SEG_MOVETO:
+ case FlatteningPathIterator.SEG_LINETO:
+ pts.addElement(new Point2D.Double(pt[0], pt[1]));
+ }
+ pi.next();
+ }
+ }
+
+
+ public void reset(int w, int h) {
+ if ((type & DEC) != 0) {
+ rotate = 360;
+ alpha = 1.0f;
+ zoom = 2.0;
+ } else {
+ rotate = alpha = 0;
+ zoom = 0;
+ }
+ if ((type & ZOOM) == 0) {
+ generatePts(w, h, 0.5);
+ }
+ }
+
+
+ public void step(int w, int h) {
+ if ((type & ZOOM) != 0) {
+ generatePts(w, h, zoom += zIncr);
+ }
+ if ((type & RI) != 0 || (type & RI) != 0) {
+ rotate += rIncr;
+ }
+ if ((type & ACI) != 0 || (type & ACD) != 0) {
+ alpha += aIncr;
+ }
+ }
+
+
+ public void render(int w, int h, Graphics2D g2) {
+ Composite saveAC = null;
+ if ((type & AC) != 0 && alpha >= 0 && alpha <= 1) {
+ saveAC = g2.getComposite();
+ g2.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, alpha));
+ }
+ AffineTransform saveTx = null;
+ if ((type & R) != 0) {
+ saveTx = g2.getTransform();
+ AffineTransform at = new AffineTransform();
+ at.rotate(Math.toRadians(rotate), w/2, h/2);
+ g2.setTransform(at);
+ }
+ Point2D p1 = new Point2D.Double(w/2, h/2);
+ g2.setColor(Color.yellow);
+ for (int i = 0; i < pts.size()-1; i++) {
+ g2.draw(new Line2D.Float(p1, (Point2D) pts.get(i)));
+ }
+ if (saveTx != null) {
+ g2.setTransform(saveTx);
+ }
+ if (saveAC != null) {
+ g2.setComposite(saveAC);
+ }
+ }
+
+ public int getBegin() {
+ return beginning;
+ }
+
+ public int getEnd() {
+ return ending;
+ }
+ } // End LnE class
+
+
+
+ /**
+ * Template for Features & Contributors consisting of translating
+ * blue and red rectangles and an image going from transparent to
+ * opaque.
+ */
+ static class Temp implements Part {
+ static final int NOANIM = 1;
+ static final int RECT = 2;
+ static final int RNA = RECT | NOANIM;
+ static final int IMG = 4;
+ static final int INA = IMG | NOANIM;
+ private int beginning, ending;
+ private float alpha, aIncr;
+ private int type;
+ private Rectangle rect1, rect2;
+ private int x, y, xIncr, yIncr;
+ private Image img;
+
+
+ public Temp(int type, Image img, int beg, int end) {
+ this.type = type;
+ this.img = img;
+ this.beginning = beg;
+ this.ending = end;
+ aIncr = 0.9f / (ending - beginning);
+ if ((type & NOANIM) != 0) {
+ alpha = 1.0f;
+ }
+ }
+
+
+
+ public void reset(int w, int h) {
+ rect1 = new Rectangle(8, 20, w-20, 30);
+ rect2 = new Rectangle(20, 8, 30, h-20);
+ if ((type & NOANIM) == 0) {
+ alpha = 0.0f;
+ xIncr = w / (ending - beginning);
+ yIncr = h / (ending - beginning);
+ x = w+(int)(xIncr*1.4);
+ y = h+(int)(yIncr*1.4);
+ }
+ }
+
+
+ public void step(int w, int h) {
+ if ((type & NOANIM) != 0) {
+ return;
+ }
+ if ((type & RECT) != 0) {
+ rect1.setLocation(x-=xIncr, 20);
+ rect2.setLocation(20, y-=yIncr);
+ }
+ if ((type & IMG) != 0) {
+ alpha += aIncr;
+ }
+ }
+
+
+ public void render(int w, int h, Graphics2D g2) {
+ if ((type & RECT) != 0) {
+ g2.setColor(blue);
+ g2.fill(rect1);
+ g2.setColor(red);
+ g2.fill(rect2);
+ }
+ if ((type & IMG) != 0) {
+ Composite saveAC = g2.getComposite();
+ if (alpha >= 0 && alpha <= 1) {
+ g2.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, alpha));
+ }
+ g2.drawImage(img, 30, 30, null);
+ g2.setComposite(saveAC);
+ }
+ }
+
+ public int getBegin() {
+ return beginning;
+ }
+
+ public int getEnd() {
+ return ending;
+ }
+ } // End Temp class
+
+
+
+ /**
+ * Features of Java2D. Single character advancement effect.
+ */
+ static class Features implements Part {
+
+ static final int GRAPHICS = 0;
+ static final int TEXT = 1;
+ static final int IMAGES = 2;
+ static final int COLOR = 3;
+ static Font font1 = new Font("serif", Font.BOLD, 38);
+ static Font font2 = new Font("serif", Font.PLAIN, 24);
+ static FontMetrics fm1 = Surface.getMetrics(font1);
+ static FontMetrics fm2 = Surface.getMetrics(font2);
+ static String table[][] =
+ {{ "Graphics", "Antialiased rendering", "Bezier paths",
+ "Transforms", "Compositing", "Stroking parameters" },
+ { "Text", "Extended font support",
+ "Advanced text layout", "Dynamic font loading",
+ "AttributeSets for font customization" },
+ { "Images", "Flexible image layouts",
+ "Extended imaging operations",
+ " Convolutions, Lookup Tables",
+ "RenderableImage interface"},
+ { "Color", "ICC profile support", "Color conversion",
+ "Arbitrary color spaces"} };
+ private String list[];
+ private int beginning, ending;
+ private int strH;
+ private int endIndex, listIndex;
+ private Vector v = new Vector();
+
+
+ public Features(int type, int beg, int end) {
+ list = table[type];
+ this.beginning = beg;
+ this.ending = end;
+ }
+
+
+ public void reset(int w, int h) {
+ strH = (int) (fm2.getAscent()+fm2.getDescent());
+ endIndex = 1;
+ listIndex = 0;
+ v.clear();
+ v.addElement(list[listIndex].substring(0,endIndex));
+ }
+
+
+ public void step(int w, int h) {
+ if (listIndex < list.length) {
+ if (++endIndex > list[listIndex].length()) {
+ if (++listIndex < list.length) {
+ endIndex = 1;
+ v.addElement(list[listIndex].substring(0,endIndex));
+ }
+ } else {
+ v.set(listIndex, list[listIndex].substring(0,endIndex));
+ }
+ }
+ }
+
+
+ public void render(int w, int h, Graphics2D g2) {
+ g2.setColor(white);
+ g2.setFont(font1);
+ g2.drawString((String) v.get(0), 90, 85);
+ g2.setFont(font2);
+ for (int i = 1, y = 90; i < v.size(); i++) {
+ g2.drawString((String) v.get(i), 120, y += strH);
+ }
+ }
+
+ public int getBegin() {
+ return beginning;
+ }
+
+ public int getEnd() {
+ return ending;
+ }
+ } // End Features class
+
+
+
+ /**
+ * Scrolling text of Java2D contributors.
+ */
+ static class Contributors implements Part {
+
+ static String members[] =
+ {
+ "Brian Lichtenwalter", "Jeannette Hung",
+ "Thanh Nguyen", "Jim Graham", "Jerry Evans",
+ "John Raley", "Michael Peirce", "Robert Kim",
+ "Jennifer Ball", "Deborah Adair", "Paul Charlton",
+ "Dmitry Feld", "Gregory Stone", "Richard Blanchard",
+ "Link Perry", "Phil Race", "Vincent Hardy",
+ "Parry Kejriwal", "Doug Felt", "Rekha Rangarajan",
+ "Paula Patel", "Michael Bundschuh", "Joe Warzecha",
+ "Joey Beheler", "Aastha Bhardwaj", "Daniel Rice",
+ "Chris Campbell", "Shinsuke Fukuda", "Dmitri Trembovetski",
+ "Chet Haase", "Jennifer Godinez", "Nicholas Talian",
+ "Raul Vera", "Ankit Patel", "Ilya Bagrak"
+ };
+ static Font font = new Font("serif", Font.PLAIN, 26);
+ static FontMetrics fm = Surface.getMetrics(font);
+ private int beginning, ending;
+ private int nStrs, strH, index, yh, height;
+ private Vector v = new Vector();
+ private Vector cast = new Vector(members.length+3);
+ private int counter, cntMod;
+ private GradientPaint gp;
+
+
+ public Contributors(int beg, int end) {
+ this.beginning = beg;
+ this.ending = end;
+ java.util.Arrays.sort(members);
+ cast.addElement("CONTRIBUTORS");
+ cast.addElement(" ");
+ for (int i = 0; i < members.length; i++) {
+ cast.addElement(members[i]);
+ }
+ cast.addElement(" "); cast.addElement(" ");
+ cntMod = (ending - beginning) / cast.size() - 1;
+ }
+
+
+ public void reset(int w, int h) {
+ v.clear();
+ strH = (int) (fm.getAscent()+fm.getDescent());
+ nStrs = (h-40)/strH + 1;
+ height = strH * (nStrs-1) + 48;
+ index = 0;
+ gp = new GradientPaint(0,h/2,Color.white,0,h+20,Color.black);
+ counter = 0;
+ }
+
+
+ public void step(int w, int h) {
+ if (counter++%cntMod == 0) {
+ if (index < cast.size()) {
+ v.addElement(cast.get(index));
+ }
+ if ((v.size() == nStrs || index >= cast.size()) && v.size() != 0) {
+ v.removeElementAt(0);
+ }
+ ++index;
+ }
+ }
+
+
+ public void render(int w, int h, Graphics2D g2) {
+ g2.setPaint(gp);
+ g2.setFont(font);
+ double remainder = counter%cntMod;
+ double incr = 1.0-remainder/cntMod;
+ incr = incr == 1.0 ? 0 : incr;
+ int y = (int) (incr * strH);
+
+ if (index >= cast.size()) {
+ y = yh + y;
+ } else {
+ y = yh = height - v.size() * strH + y;
+ }
+ for (int i = 0; i < v.size(); i++) {
+ String s = (String) v.get(i);
+ g2.drawString(s, w/2-fm.stringWidth(s)/2, y += strH);
+ }
+ }
+
+ public int getBegin() {
+ return beginning;
+ }
+
+ public int getEnd() {
+ return ending;
+ }
+ } // End Contributors class
+
+ } // End Surface class
+} // End Intro class
diff --git a/tests/examplefiles/ceval.c b/tests/examplefiles/ceval.c
new file mode 100644
index 00000000..99e87e81
--- /dev/null
+++ b/tests/examplefiles/ceval.c
@@ -0,0 +1,4349 @@
+
+/* Execute compiled code */
+
+/* XXX TO DO:
+ XXX speed up searching for keywords by using a dictionary
+ XXX document it!
+ */
+
+/* enable more aggressive intra-module optimizations, where available */
+#define PY_LOCAL_AGGRESSIVE
+
+#include "Python.h"
+
+#include "code.h"
+#include "frameobject.h"
+#include "eval.h"
+#include "opcode.h"
+#include "structmember.h"
+
+#include <ctype.h>
+
+#ifndef WITH_TSC
+
+#define READ_TIMESTAMP(var)
+
+#else
+
+typedef unsigned long long uint64;
+
+#if defined(__ppc__) /* <- Don't know if this is the correct symbol; this
+ section should work for GCC on any PowerPC platform,
+ irrespective of OS. POWER? Who knows :-) */
+
+#define READ_TIMESTAMP(var) ppc_getcounter(&var)
+
+static void
+ppc_getcounter(uint64 *v)
+{
+ register unsigned long tbu, tb, tbu2;
+
+ loop:
+ asm volatile ("mftbu %0" : "=r" (tbu) );
+ asm volatile ("mftb %0" : "=r" (tb) );
+ asm volatile ("mftbu %0" : "=r" (tbu2));
+ if (__builtin_expect(tbu != tbu2, 0)) goto loop;
+
+ /* The slightly peculiar way of writing the next lines is
+ compiled better by GCC than any other way I tried. */
+ ((long*)(v))[0] = tbu;
+ ((long*)(v))[1] = tb;
+}
+
+#else /* this is for linux/x86 (and probably any other GCC/x86 combo) */
+
+#define READ_TIMESTAMP(val) \
+ __asm__ __volatile__("rdtsc" : "=A" (val))
+
+#endif
+
+void dump_tsc(int opcode, int ticked, uint64 inst0, uint64 inst1,
+ uint64 loop0, uint64 loop1, uint64 intr0, uint64 intr1)
+{
+ uint64 intr, inst, loop;
+ PyThreadState *tstate = PyThreadState_Get();
+ if (!tstate->interp->tscdump)
+ return;
+ intr = intr1 - intr0;
+ inst = inst1 - inst0 - intr;
+ loop = loop1 - loop0 - intr;
+ fprintf(stderr, "opcode=%03d t=%d inst=%06lld loop=%06lld\n",
+ opcode, ticked, inst, loop);
+}
+
+#endif
+
+/* Turn this on if your compiler chokes on the big switch: */
+/* #define CASE_TOO_BIG 1 */
+
+#ifdef Py_DEBUG
+/* For debugging the interpreter: */
+#define LLTRACE 1 /* Low-level trace feature */
+#define CHECKEXC 1 /* Double-check exception checking */
+#endif
+
+typedef PyObject *(*callproc)(PyObject *, PyObject *, PyObject *);
+
+/* Forward declarations */
+#ifdef WITH_TSC
+static PyObject * call_function(PyObject ***, int, uint64*, uint64*);
+#else
+static PyObject * call_function(PyObject ***, int);
+#endif
+static PyObject * fast_function(PyObject *, PyObject ***, int, int, int);
+static PyObject * do_call(PyObject *, PyObject ***, int, int);
+static PyObject * ext_do_call(PyObject *, PyObject ***, int, int, int);
+static PyObject * update_keyword_args(PyObject *, int, PyObject ***,PyObject *);
+static PyObject * update_star_args(int, int, PyObject *, PyObject ***);
+static PyObject * load_args(PyObject ***, int);
+#define CALL_FLAG_VAR 1
+#define CALL_FLAG_KW 2
+
+#ifdef LLTRACE
+static int lltrace;
+static int prtrace(PyObject *, char *);
+#endif
+static int call_trace(Py_tracefunc, PyObject *, PyFrameObject *,
+ int, PyObject *);
+static void call_trace_protected(Py_tracefunc, PyObject *,
+ PyFrameObject *, int, PyObject *);
+static void call_exc_trace(Py_tracefunc, PyObject *, PyFrameObject *);
+static int maybe_call_line_trace(Py_tracefunc, PyObject *,
+ PyFrameObject *, int *, int *, int *);
+
+static PyObject * apply_slice(PyObject *, PyObject *, PyObject *);
+static int assign_slice(PyObject *, PyObject *,
+ PyObject *, PyObject *);
+static PyObject * cmp_outcome(int, PyObject *, PyObject *);
+static PyObject * import_from(PyObject *, PyObject *);
+static int import_all_from(PyObject *, PyObject *);
+static PyObject * build_class(PyObject *, PyObject *, PyObject *);
+static int exec_statement(PyFrameObject *,
+ PyObject *, PyObject *, PyObject *);
+static void set_exc_info(PyThreadState *, PyObject *, PyObject *, PyObject *);
+static void reset_exc_info(PyThreadState *);
+static void format_exc_check_arg(PyObject *, char *, PyObject *);
+static PyObject * string_concatenate(PyObject *, PyObject *,
+ PyFrameObject *, unsigned char *);
+
+#define NAME_ERROR_MSG \
+ "name '%.200s' is not defined"
+#define GLOBAL_NAME_ERROR_MSG \
+ "global name '%.200s' is not defined"
+#define UNBOUNDLOCAL_ERROR_MSG \
+ "local variable '%.200s' referenced before assignment"
+#define UNBOUNDFREE_ERROR_MSG \
+ "free variable '%.200s' referenced before assignment" \
+ " in enclosing scope"
+
+/* Dynamic execution profile */
+#ifdef DYNAMIC_EXECUTION_PROFILE
+#ifdef DXPAIRS
+static long dxpairs[257][256];
+#define dxp dxpairs[256]
+#else
+static long dxp[256];
+#endif
+#endif
+
+/* Function call profile */
+#ifdef CALL_PROFILE
+#define PCALL_NUM 11
+static int pcall[PCALL_NUM];
+
+#define PCALL_ALL 0
+#define PCALL_FUNCTION 1
+#define PCALL_FAST_FUNCTION 2
+#define PCALL_FASTER_FUNCTION 3
+#define PCALL_METHOD 4
+#define PCALL_BOUND_METHOD 5
+#define PCALL_CFUNCTION 6
+#define PCALL_TYPE 7
+#define PCALL_GENERATOR 8
+#define PCALL_OTHER 9
+#define PCALL_POP 10
+
+/* Notes about the statistics
+
+ PCALL_FAST stats
+
+ FAST_FUNCTION means no argument tuple needs to be created.
+ FASTER_FUNCTION means that the fast-path frame setup code is used.
+
+ If there is a method call where the call can be optimized by changing
+ the argument tuple and calling the function directly, it gets recorded
+ twice.
+
+ As a result, the relationship among the statistics appears to be
+ PCALL_ALL == PCALL_FUNCTION + PCALL_METHOD - PCALL_BOUND_METHOD +
+ PCALL_CFUNCTION + PCALL_TYPE + PCALL_GENERATOR + PCALL_OTHER
+ PCALL_FUNCTION > PCALL_FAST_FUNCTION > PCALL_FASTER_FUNCTION
+ PCALL_METHOD > PCALL_BOUND_METHOD
+*/
+
+#define PCALL(POS) pcall[POS]++
+
+PyObject *
+PyEval_GetCallStats(PyObject *self)
+{
+ return Py_BuildValue("iiiiiiiiii",
+ pcall[0], pcall[1], pcall[2], pcall[3],
+ pcall[4], pcall[5], pcall[6], pcall[7],
+ pcall[8], pcall[9]);
+}
+#else
+#define PCALL(O)
+
+PyObject *
+PyEval_GetCallStats(PyObject *self)
+{
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+#endif
+
+
+#ifdef WITH_THREAD
+
+#ifdef HAVE_ERRNO_H
+#include <errno.h>
+#endif
+#include "pythread.h"
+
+static PyThread_type_lock interpreter_lock = 0; /* This is the GIL */
+static long main_thread = 0;
+
+int
+PyEval_ThreadsInitialized(void)
+{
+ return interpreter_lock != 0;
+}
+
+void
+PyEval_InitThreads(void)
+{
+ if (interpreter_lock)
+ return;
+ interpreter_lock = PyThread_allocate_lock();
+ PyThread_acquire_lock(interpreter_lock, 1);
+ main_thread = PyThread_get_thread_ident();
+}
+
+void
+PyEval_AcquireLock(void)
+{
+ PyThread_acquire_lock(interpreter_lock, 1);
+}
+
+void
+PyEval_ReleaseLock(void)
+{
+ PyThread_release_lock(interpreter_lock);
+}
+
+void
+PyEval_AcquireThread(PyThreadState *tstate)
+{
+ if (tstate == NULL)
+ Py_FatalError("PyEval_AcquireThread: NULL new thread state");
+ /* Check someone has called PyEval_InitThreads() to create the lock */
+ assert(interpreter_lock);
+ PyThread_acquire_lock(interpreter_lock, 1);
+ if (PyThreadState_Swap(tstate) != NULL)
+ Py_FatalError(
+ "PyEval_AcquireThread: non-NULL old thread state");
+}
+
+void
+PyEval_ReleaseThread(PyThreadState *tstate)
+{
+ if (tstate == NULL)
+ Py_FatalError("PyEval_ReleaseThread: NULL thread state");
+ if (PyThreadState_Swap(NULL) != tstate)
+ Py_FatalError("PyEval_ReleaseThread: wrong thread state");
+ PyThread_release_lock(interpreter_lock);
+}
+
+/* This function is called from PyOS_AfterFork to ensure that newly
+ created child processes don't hold locks referring to threads which
+ are not running in the child process. (This could also be done using
+ pthread_atfork mechanism, at least for the pthreads implementation.) */
+
+void
+PyEval_ReInitThreads(void)
+{
+ if (!interpreter_lock)
+ return;
+ /*XXX Can't use PyThread_free_lock here because it does too
+ much error-checking. Doing this cleanly would require
+ adding a new function to each thread_*.h. Instead, just
+ create a new lock and waste a little bit of memory */
+ interpreter_lock = PyThread_allocate_lock();
+ PyThread_acquire_lock(interpreter_lock, 1);
+ main_thread = PyThread_get_thread_ident();
+}
+#endif
+
+/* Functions save_thread and restore_thread are always defined so
+ dynamically loaded modules needn't be compiled separately for use
+ with and without threads: */
+
+PyThreadState *
+PyEval_SaveThread(void)
+{
+ PyThreadState *tstate = PyThreadState_Swap(NULL);
+ if (tstate == NULL)
+ Py_FatalError("PyEval_SaveThread: NULL tstate");
+#ifdef WITH_THREAD
+ if (interpreter_lock)
+ PyThread_release_lock(interpreter_lock);
+#endif
+ return tstate;
+}
+
+void
+PyEval_RestoreThread(PyThreadState *tstate)
+{
+ if (tstate == NULL)
+ Py_FatalError("PyEval_RestoreThread: NULL tstate");
+#ifdef WITH_THREAD
+ if (interpreter_lock) {
+ int err = errno;
+ PyThread_acquire_lock(interpreter_lock, 1);
+ errno = err;
+ }
+#endif
+ PyThreadState_Swap(tstate);
+}
+
+
+/* Mechanism whereby asynchronously executing callbacks (e.g. UNIX
+ signal handlers or Mac I/O completion routines) can schedule calls
+ to a function to be called synchronously.
+ The synchronous function is called with one void* argument.
+ It should return 0 for success or -1 for failure -- failure should
+ be accompanied by an exception.
+
+ If registry succeeds, the registry function returns 0; if it fails
+ (e.g. due to too many pending calls) it returns -1 (without setting
+ an exception condition).
+
+ Note that because registry may occur from within signal handlers,
+ or other asynchronous events, calling malloc() is unsafe!
+
+#ifdef WITH_THREAD
+ Any thread can schedule pending calls, but only the main thread
+ will execute them.
+#endif
+
+ XXX WARNING! ASYNCHRONOUSLY EXECUTING CODE!
+ There are two possible race conditions:
+ (1) nested asynchronous registry calls;
+ (2) registry calls made while pending calls are being processed.
+ While (1) is very unlikely, (2) is a real possibility.
+ The current code is safe against (2), but not against (1).
+ The safety against (2) is derived from the fact that only one
+ thread (the main thread) ever takes things out of the queue.
+
+ XXX Darn! With the advent of thread state, we should have an array
+ of pending calls per thread in the thread state! Later...
+*/
+
+#define NPENDINGCALLS 32
+static struct {
+ int (*func)(void *);
+ void *arg;
+} pendingcalls[NPENDINGCALLS];
+static volatile int pendingfirst = 0;
+static volatile int pendinglast = 0;
+static volatile int things_to_do = 0;
+
+int
+Py_AddPendingCall(int (*func)(void *), void *arg)
+{
+ static volatile int busy = 0;
+ int i, j;
+ /* XXX Begin critical section */
+ /* XXX If you want this to be safe against nested
+ XXX asynchronous calls, you'll have to work harder! */
+ if (busy)
+ return -1;
+ busy = 1;
+ i = pendinglast;
+ j = (i + 1) % NPENDINGCALLS;
+ if (j == pendingfirst) {
+ busy = 0;
+ return -1; /* Queue full */
+ }
+ pendingcalls[i].func = func;
+ pendingcalls[i].arg = arg;
+ pendinglast = j;
+
+ _Py_Ticker = 0;
+ things_to_do = 1; /* Signal main loop */
+ busy = 0;
+ /* XXX End critical section */
+ return 0;
+}
+
+int
+Py_MakePendingCalls(void)
+{
+ static int busy = 0;
+#ifdef WITH_THREAD
+ if (main_thread && PyThread_get_thread_ident() != main_thread)
+ return 0;
+#endif
+ if (busy)
+ return 0;
+ busy = 1;
+ things_to_do = 0;
+ for (;;) {
+ int i;
+ int (*func)(void *);
+ void *arg;
+ i = pendingfirst;
+ if (i == pendinglast)
+ break; /* Queue empty */
+ func = pendingcalls[i].func;
+ arg = pendingcalls[i].arg;
+ pendingfirst = (i + 1) % NPENDINGCALLS;
+ if (func(arg) < 0) {
+ busy = 0;
+ things_to_do = 1; /* We're not done yet */
+ return -1;
+ }
+ }
+ busy = 0;
+ return 0;
+}
+
+
+/* The interpreter's recursion limit */
+
+#ifndef Py_DEFAULT_RECURSION_LIMIT
+#define Py_DEFAULT_RECURSION_LIMIT 1000
+#endif
+static int recursion_limit = Py_DEFAULT_RECURSION_LIMIT;
+int _Py_CheckRecursionLimit = Py_DEFAULT_RECURSION_LIMIT;
+
+int
+Py_GetRecursionLimit(void)
+{
+ return recursion_limit;
+}
+
+void
+Py_SetRecursionLimit(int new_limit)
+{
+ recursion_limit = new_limit;
+ _Py_CheckRecursionLimit = recursion_limit;
+}
+
+/* the macro Py_EnterRecursiveCall() only calls _Py_CheckRecursiveCall()
+ if the recursion_depth reaches _Py_CheckRecursionLimit.
+ If USE_STACKCHECK, the macro decrements _Py_CheckRecursionLimit
+ to guarantee that _Py_CheckRecursiveCall() is regularly called.
+ Without USE_STACKCHECK, there is no need for this. */
+int
+_Py_CheckRecursiveCall(char *where)
+{
+ PyThreadState *tstate = PyThreadState_GET();
+
+#ifdef USE_STACKCHECK
+ if (PyOS_CheckStack()) {
+ --tstate->recursion_depth;
+ PyErr_SetString(PyExc_MemoryError, "Stack overflow");
+ return -1;
+ }
+#endif
+ if (tstate->recursion_depth > recursion_limit) {
+ --tstate->recursion_depth;
+ PyErr_Format(PyExc_RuntimeError,
+ "maximum recursion depth exceeded%s",
+ where);
+ return -1;
+ }
+ _Py_CheckRecursionLimit = recursion_limit;
+ return 0;
+}
+
+/* Status code for main loop (reason for stack unwind) */
+enum why_code {
+ WHY_NOT = 0x0001, /* No error */
+ WHY_EXCEPTION = 0x0002, /* Exception occurred */
+ WHY_RERAISE = 0x0004, /* Exception re-raised by 'finally' */
+ WHY_RETURN = 0x0008, /* 'return' statement */
+ WHY_BREAK = 0x0010, /* 'break' statement */
+ WHY_CONTINUE = 0x0020, /* 'continue' statement */
+ WHY_YIELD = 0x0040 /* 'yield' operator */
+};
+
+static enum why_code do_raise(PyObject *, PyObject *, PyObject *);
+static int unpack_iterable(PyObject *, int, PyObject **);
+
+/* for manipulating the thread switch and periodic "stuff" - used to be
+ per thread, now just a pair o' globals */
+int _Py_CheckInterval = 100;
+volatile int _Py_Ticker = 100;
+
+PyObject *
+PyEval_EvalCode(PyCodeObject *co, PyObject *globals, PyObject *locals)
+{
+ /* XXX raise SystemError if globals is NULL */
+ return PyEval_EvalCodeEx(co,
+ globals, locals,
+ (PyObject **)NULL, 0,
+ (PyObject **)NULL, 0,
+ (PyObject **)NULL, 0,
+ NULL);
+}
+
+
+/* Interpreter main loop */
+
+PyObject *
+PyEval_EvalFrame(PyFrameObject *f) {
+ /* This is for backward compatibility with extension modules that
+ used this API; core interpreter code should call PyEval_EvalFrameEx() */
+ return PyEval_EvalFrameEx(f, 0);
+}
+
+PyObject *
+PyEval_EvalFrameEx(PyFrameObject *f, int throwflag)
+{
+#ifdef DXPAIRS
+ int lastopcode = 0;
+#endif
+ register PyObject **stack_pointer; /* Next free slot in value stack */
+ register unsigned char *next_instr;
+ register int opcode; /* Current opcode */
+ register int oparg; /* Current opcode argument, if any */
+ register enum why_code why; /* Reason for block stack unwind */
+ register int err; /* Error status -- nonzero if error */
+ register PyObject *x; /* Result object -- NULL if error */
+ register PyObject *v; /* Temporary objects popped off stack */
+ register PyObject *w;
+ register PyObject *u;
+ register PyObject *t;
+ register PyObject *stream = NULL; /* for PRINT opcodes */
+ register PyObject **fastlocals, **freevars;
+ PyObject *retval = NULL; /* Return value */
+ PyThreadState *tstate = PyThreadState_GET();
+ PyCodeObject *co;
+
+ /* when tracing we set things up so that
+
+ not (instr_lb <= current_bytecode_offset < instr_ub)
+
+ is true when the line being executed has changed. The
+ initial values are such as to make this false the first
+ time it is tested. */
+ int instr_ub = -1, instr_lb = 0, instr_prev = -1;
+
+ unsigned char *first_instr;
+ PyObject *names;
+ PyObject *consts;
+#if defined(Py_DEBUG) || defined(LLTRACE)
+ /* Make it easier to find out where we are with a debugger */
+ char *filename;
+#endif
+
+/* Tuple access macros */
+
+#ifndef Py_DEBUG
+#define GETITEM(v, i) PyTuple_GET_ITEM((PyTupleObject *)(v), (i))
+#else
+#define GETITEM(v, i) PyTuple_GetItem((v), (i))
+#endif
+
+#ifdef WITH_TSC
+/* Use Pentium timestamp counter to mark certain events:
+ inst0 -- beginning of switch statement for opcode dispatch
+ inst1 -- end of switch statement (may be skipped)
+ loop0 -- the top of the mainloop
+ loop1 -- place where control returns again to top of mainloop
+ (may be skipped)
+ intr1 -- beginning of long interruption
+ intr2 -- end of long interruption
+
+ Many opcodes call out to helper C functions. In some cases, the
+ time in those functions should be counted towards the time for the
+ opcode, but not in all cases. For example, a CALL_FUNCTION opcode
+ calls another Python function; there's no point in charge all the
+ bytecode executed by the called function to the caller.
+
+ It's hard to make a useful judgement statically. In the presence
+ of operator overloading, it's impossible to tell if a call will
+ execute new Python code or not.
+
+ It's a case-by-case judgement. I'll use intr1 for the following
+ cases:
+
+ EXEC_STMT
+ IMPORT_STAR
+ IMPORT_FROM
+ CALL_FUNCTION (and friends)
+
+ */
+ uint64 inst0, inst1, loop0, loop1, intr0 = 0, intr1 = 0;
+ int ticked = 0;
+
+ READ_TIMESTAMP(inst0);
+ READ_TIMESTAMP(inst1);
+ READ_TIMESTAMP(loop0);
+ READ_TIMESTAMP(loop1);
+
+ /* shut up the compiler */
+ opcode = 0;
+#endif
+
+/* Code access macros */
+
+#define INSTR_OFFSET() ((int)(next_instr - first_instr))
+#define NEXTOP() (*next_instr++)
+#define NEXTARG() (next_instr += 2, (next_instr[-1]<<8) + next_instr[-2])
+#define PEEKARG() ((next_instr[2]<<8) + next_instr[1])
+#define JUMPTO(x) (next_instr = first_instr + (x))
+#define JUMPBY(x) (next_instr += (x))
+
+/* OpCode prediction macros
+ Some opcodes tend to come in pairs thus making it possible to predict
+ the second code when the first is run. For example, COMPARE_OP is often
+ followed by JUMP_IF_FALSE or JUMP_IF_TRUE. And, those opcodes are often
+ followed by a POP_TOP.
+
+ Verifying the prediction costs a single high-speed test of register
+ variable against a constant. If the pairing was good, then the
+ processor has a high likelihood of making its own successful branch
+ prediction which results in a nearly zero overhead transition to the
+ next opcode.
+
+ A successful prediction saves a trip through the eval-loop including
+ its two unpredictable branches, the HASARG test and the switch-case.
+
+ If collecting opcode statistics, turn off prediction so that
+ statistics are accurately maintained (the predictions bypass
+ the opcode frequency counter updates).
+*/
+
+#ifdef DYNAMIC_EXECUTION_PROFILE
+#define PREDICT(op) if (0) goto PRED_##op
+#else
+#define PREDICT(op) if (*next_instr == op) goto PRED_##op
+#endif
+
+#define PREDICTED(op) PRED_##op: next_instr++
+#define PREDICTED_WITH_ARG(op) PRED_##op: oparg = PEEKARG(); next_instr += 3
+
+/* Stack manipulation macros */
+
+/* The stack can grow at most MAXINT deep, as co_nlocals and
+ co_stacksize are ints. */
+#define STACK_LEVEL() ((int)(stack_pointer - f->f_valuestack))
+#define EMPTY() (STACK_LEVEL() == 0)
+#define TOP() (stack_pointer[-1])
+#define SECOND() (stack_pointer[-2])
+#define THIRD() (stack_pointer[-3])
+#define FOURTH() (stack_pointer[-4])
+#define SET_TOP(v) (stack_pointer[-1] = (v))
+#define SET_SECOND(v) (stack_pointer[-2] = (v))
+#define SET_THIRD(v) (stack_pointer[-3] = (v))
+#define SET_FOURTH(v) (stack_pointer[-4] = (v))
+#define BASIC_STACKADJ(n) (stack_pointer += n)
+#define BASIC_PUSH(v) (*stack_pointer++ = (v))
+#define BASIC_POP() (*--stack_pointer)
+
+#ifdef LLTRACE
+#define PUSH(v) { (void)(BASIC_PUSH(v), \
+ lltrace && prtrace(TOP(), "push")); \
+ assert(STACK_LEVEL() <= co->co_stacksize); }
+#define POP() ((void)(lltrace && prtrace(TOP(), "pop")), BASIC_POP())
+#define STACKADJ(n) { (void)(BASIC_STACKADJ(n), \
+ lltrace && prtrace(TOP(), "stackadj")); \
+ assert(STACK_LEVEL() <= co->co_stacksize); }
+#define EXT_POP(STACK_POINTER) (lltrace && prtrace(*(STACK_POINTER), "ext_pop"), *--(STACK_POINTER))
+#else
+#define PUSH(v) BASIC_PUSH(v)
+#define POP() BASIC_POP()
+#define STACKADJ(n) BASIC_STACKADJ(n)
+#define EXT_POP(STACK_POINTER) (*--(STACK_POINTER))
+#endif
+
+/* Local variable macros */
+
+#define GETLOCAL(i) (fastlocals[i])
+
+/* The SETLOCAL() macro must not DECREF the local variable in-place and
+ then store the new value; it must copy the old value to a temporary
+ value, then store the new value, and then DECREF the temporary value.
+ This is because it is possible that during the DECREF the frame is
+ accessed by other code (e.g. a __del__ method or gc.collect()) and the
+ variable would be pointing to already-freed memory. */
+#define SETLOCAL(i, value) do { PyObject *tmp = GETLOCAL(i); \
+ GETLOCAL(i) = value; \
+ Py_XDECREF(tmp); } while (0)
+
+/* Start of code */
+
+ if (f == NULL)
+ return NULL;
+
+ /* push frame */
+ if (Py_EnterRecursiveCall(""))
+ return NULL;
+
+ tstate->frame = f;
+
+ if (tstate->use_tracing) {
+ if (tstate->c_tracefunc != NULL) {
+ /* tstate->c_tracefunc, if defined, is a
+ function that will be called on *every* entry
+ to a code block. Its return value, if not
+ None, is a function that will be called at
+ the start of each executed line of code.
+ (Actually, the function must return itself
+ in order to continue tracing.) The trace
+ functions are called with three arguments:
+ a pointer to the current frame, a string
+ indicating why the function is called, and
+ an argument which depends on the situation.
+ The global trace function is also called
+ whenever an exception is detected. */
+ if (call_trace(tstate->c_tracefunc, tstate->c_traceobj,
+ f, PyTrace_CALL, Py_None)) {
+ /* Trace function raised an error */
+ goto exit_eval_frame;
+ }
+ }
+ if (tstate->c_profilefunc != NULL) {
+ /* Similar for c_profilefunc, except it needn't
+ return itself and isn't called for "line" events */
+ if (call_trace(tstate->c_profilefunc,
+ tstate->c_profileobj,
+ f, PyTrace_CALL, Py_None)) {
+ /* Profile function raised an error */
+ goto exit_eval_frame;
+ }
+ }
+ }
+
+ co = f->f_code;
+ names = co->co_names;
+ consts = co->co_consts;
+ fastlocals = f->f_localsplus;
+ freevars = f->f_localsplus + co->co_nlocals;
+ first_instr = (unsigned char*) PyString_AS_STRING(co->co_code);
+ /* An explanation is in order for the next line.
+
+ f->f_lasti now refers to the index of the last instruction
+ executed. You might think this was obvious from the name, but
+ this wasn't always true before 2.3! PyFrame_New now sets
+ f->f_lasti to -1 (i.e. the index *before* the first instruction)
+ and YIELD_VALUE doesn't fiddle with f_lasti any more. So this
+ does work. Promise. */
+ next_instr = first_instr + f->f_lasti + 1;
+ stack_pointer = f->f_stacktop;
+ assert(stack_pointer != NULL);
+ f->f_stacktop = NULL; /* remains NULL unless yield suspends frame */
+
+#ifdef LLTRACE
+ lltrace = PyDict_GetItemString(f->f_globals, "__lltrace__") != NULL;
+#endif
+#if defined(Py_DEBUG) || defined(LLTRACE)
+ filename = PyString_AsString(co->co_filename);
+#endif
+
+ why = WHY_NOT;
+ err = 0;
+ x = Py_None; /* Not a reference, just anything non-NULL */
+ w = NULL;
+
+ if (throwflag) { /* support for generator.throw() */
+ why = WHY_EXCEPTION;
+ goto on_error;
+ }
+
+ for (;;) {
+#ifdef WITH_TSC
+ if (inst1 == 0) {
+ /* Almost surely, the opcode executed a break
+ or a continue, preventing inst1 from being set
+ on the way out of the loop.
+ */
+ READ_TIMESTAMP(inst1);
+ loop1 = inst1;
+ }
+ dump_tsc(opcode, ticked, inst0, inst1, loop0, loop1,
+ intr0, intr1);
+ ticked = 0;
+ inst1 = 0;
+ intr0 = 0;
+ intr1 = 0;
+ READ_TIMESTAMP(loop0);
+#endif
+ assert(stack_pointer >= f->f_valuestack); /* else underflow */
+ assert(STACK_LEVEL() <= co->co_stacksize); /* else overflow */
+
+ /* Do periodic things. Doing this every time through
+ the loop would add too much overhead, so we do it
+ only every Nth instruction. We also do it if
+ ``things_to_do'' is set, i.e. when an asynchronous
+ event needs attention (e.g. a signal handler or
+ async I/O handler); see Py_AddPendingCall() and
+ Py_MakePendingCalls() above. */
+
+ if (--_Py_Ticker < 0) {
+ if (*next_instr == SETUP_FINALLY) {
+ /* Make the last opcode before
+ a try: finally: block uninterruptable. */
+ goto fast_next_opcode;
+ }
+ _Py_Ticker = _Py_CheckInterval;
+ tstate->tick_counter++;
+#ifdef WITH_TSC
+ ticked = 1;
+#endif
+ if (things_to_do) {
+ if (Py_MakePendingCalls() < 0) {
+ why = WHY_EXCEPTION;
+ goto on_error;
+ }
+ if (things_to_do)
+ /* MakePendingCalls() didn't succeed.
+ Force early re-execution of this
+ "periodic" code, possibly after
+ a thread switch */
+ _Py_Ticker = 0;
+ }
+#ifdef WITH_THREAD
+ if (interpreter_lock) {
+ /* Give another thread a chance */
+
+ if (PyThreadState_Swap(NULL) != tstate)
+ Py_FatalError("ceval: tstate mix-up");
+ PyThread_release_lock(interpreter_lock);
+
+ /* Other threads may run now */
+
+ PyThread_acquire_lock(interpreter_lock, 1);
+ if (PyThreadState_Swap(tstate) != NULL)
+ Py_FatalError("ceval: orphan tstate");
+
+ /* Check for thread interrupts */
+
+ if (tstate->async_exc != NULL) {
+ x = tstate->async_exc;
+ tstate->async_exc = NULL;
+ PyErr_SetNone(x);
+ Py_DECREF(x);
+ why = WHY_EXCEPTION;
+ goto on_error;
+ }
+ }
+#endif
+ }
+
+ fast_next_opcode:
+ f->f_lasti = INSTR_OFFSET();
+
+ /* line-by-line tracing support */
+
+ if (tstate->c_tracefunc != NULL && !tstate->tracing) {
+ /* see maybe_call_line_trace
+ for expository comments */
+ f->f_stacktop = stack_pointer;
+
+ err = maybe_call_line_trace(tstate->c_tracefunc,
+ tstate->c_traceobj,
+ f, &instr_lb, &instr_ub,
+ &instr_prev);
+ /* Reload possibly changed frame fields */
+ JUMPTO(f->f_lasti);
+ if (f->f_stacktop != NULL) {
+ stack_pointer = f->f_stacktop;
+ f->f_stacktop = NULL;
+ }
+ if (err) {
+ /* trace function raised an exception */
+ goto on_error;
+ }
+ }
+
+ /* Extract opcode and argument */
+
+ opcode = NEXTOP();
+ oparg = 0; /* allows oparg to be stored in a register because
+ it doesn't have to be remembered across a full loop */
+ if (HAS_ARG(opcode))
+ oparg = NEXTARG();
+ dispatch_opcode:
+#ifdef DYNAMIC_EXECUTION_PROFILE
+#ifdef DXPAIRS
+ dxpairs[lastopcode][opcode]++;
+ lastopcode = opcode;
+#endif
+ dxp[opcode]++;
+#endif
+
+#ifdef LLTRACE
+ /* Instruction tracing */
+
+ if (lltrace) {
+ if (HAS_ARG(opcode)) {
+ printf("%d: %d, %d\n",
+ f->f_lasti, opcode, oparg);
+ }
+ else {
+ printf("%d: %d\n",
+ f->f_lasti, opcode);
+ }
+ }
+#endif
+
+ /* Main switch on opcode */
+ READ_TIMESTAMP(inst0);
+
+ switch (opcode) {
+
+ /* BEWARE!
+ It is essential that any operation that fails sets either
+ x to NULL, err to nonzero, or why to anything but WHY_NOT,
+ and that no operation that succeeds does this! */
+
+ /* case STOP_CODE: this is an error! */
+
+ case NOP:
+ goto fast_next_opcode;
+
+ case LOAD_FAST:
+ x = GETLOCAL(oparg);
+ if (x != NULL) {
+ Py_INCREF(x);
+ PUSH(x);
+ goto fast_next_opcode;
+ }
+ format_exc_check_arg(PyExc_UnboundLocalError,
+ UNBOUNDLOCAL_ERROR_MSG,
+ PyTuple_GetItem(co->co_varnames, oparg));
+ break;
+
+ case LOAD_CONST:
+ x = GETITEM(consts, oparg);
+ Py_INCREF(x);
+ PUSH(x);
+ goto fast_next_opcode;
+
+ PREDICTED_WITH_ARG(STORE_FAST);
+ case STORE_FAST:
+ v = POP();
+ SETLOCAL(oparg, v);
+ goto fast_next_opcode;
+
+ PREDICTED(POP_TOP);
+ case POP_TOP:
+ v = POP();
+ Py_DECREF(v);
+ goto fast_next_opcode;
+
+ case ROT_TWO:
+ v = TOP();
+ w = SECOND();
+ SET_TOP(w);
+ SET_SECOND(v);
+ goto fast_next_opcode;
+
+ case ROT_THREE:
+ v = TOP();
+ w = SECOND();
+ x = THIRD();
+ SET_TOP(w);
+ SET_SECOND(x);
+ SET_THIRD(v);
+ goto fast_next_opcode;
+
+ case ROT_FOUR:
+ u = TOP();
+ v = SECOND();
+ w = THIRD();
+ x = FOURTH();
+ SET_TOP(v);
+ SET_SECOND(w);
+ SET_THIRD(x);
+ SET_FOURTH(u);
+ goto fast_next_opcode;
+
+ case DUP_TOP:
+ v = TOP();
+ Py_INCREF(v);
+ PUSH(v);
+ goto fast_next_opcode;
+
+ case DUP_TOPX:
+ if (oparg == 2) {
+ x = TOP();
+ Py_INCREF(x);
+ w = SECOND();
+ Py_INCREF(w);
+ STACKADJ(2);
+ SET_TOP(x);
+ SET_SECOND(w);
+ goto fast_next_opcode;
+ } else if (oparg == 3) {
+ x = TOP();
+ Py_INCREF(x);
+ w = SECOND();
+ Py_INCREF(w);
+ v = THIRD();
+ Py_INCREF(v);
+ STACKADJ(3);
+ SET_TOP(x);
+ SET_SECOND(w);
+ SET_THIRD(v);
+ goto fast_next_opcode;
+ }
+ Py_FatalError("invalid argument to DUP_TOPX"
+ " (bytecode corruption?)");
+ break;
+
+ case UNARY_POSITIVE:
+ v = TOP();
+ x = PyNumber_Positive(v);
+ Py_DECREF(v);
+ SET_TOP(x);
+ if (x != NULL) continue;
+ break;
+
+ case UNARY_NEGATIVE:
+ v = TOP();
+ x = PyNumber_Negative(v);
+ Py_DECREF(v);
+ SET_TOP(x);
+ if (x != NULL) continue;
+ break;
+
+ case UNARY_NOT:
+ v = TOP();
+ err = PyObject_IsTrue(v);
+ Py_DECREF(v);
+ if (err == 0) {
+ Py_INCREF(Py_True);
+ SET_TOP(Py_True);
+ continue;
+ }
+ else if (err > 0) {
+ Py_INCREF(Py_False);
+ SET_TOP(Py_False);
+ err = 0;
+ continue;
+ }
+ STACKADJ(-1);
+ break;
+
+ case UNARY_CONVERT:
+ v = TOP();
+ x = PyObject_Repr(v);
+ Py_DECREF(v);
+ SET_TOP(x);
+ if (x != NULL) continue;
+ break;
+
+ case UNARY_INVERT:
+ v = TOP();
+ x = PyNumber_Invert(v);
+ Py_DECREF(v);
+ SET_TOP(x);
+ if (x != NULL) continue;
+ break;
+
+ case BINARY_POWER:
+ w = POP();
+ v = TOP();
+ x = PyNumber_Power(v, w, Py_None);
+ Py_DECREF(v);
+ Py_DECREF(w);
+ SET_TOP(x);
+ if (x != NULL) continue;
+ break;
+
+ case BINARY_MULTIPLY:
+ w = POP();
+ v = TOP();
+ x = PyNumber_Multiply(v, w);
+ Py_DECREF(v);
+ Py_DECREF(w);
+ SET_TOP(x);
+ if (x != NULL) continue;
+ break;
+
+ case BINARY_DIVIDE:
+ if (!_Py_QnewFlag) {
+ w = POP();
+ v = TOP();
+ x = PyNumber_Divide(v, w);
+ Py_DECREF(v);
+ Py_DECREF(w);
+ SET_TOP(x);
+ if (x != NULL) continue;
+ break;
+ }
+ /* -Qnew is in effect: fall through to
+ BINARY_TRUE_DIVIDE */
+ case BINARY_TRUE_DIVIDE:
+ w = POP();
+ v = TOP();
+ x = PyNumber_TrueDivide(v, w);
+ Py_DECREF(v);
+ Py_DECREF(w);
+ SET_TOP(x);
+ if (x != NULL) continue;
+ break;
+
+ case BINARY_FLOOR_DIVIDE:
+ w = POP();
+ v = TOP();
+ x = PyNumber_FloorDivide(v, w);
+ Py_DECREF(v);
+ Py_DECREF(w);
+ SET_TOP(x);
+ if (x != NULL) continue;
+ break;
+
+ case BINARY_MODULO:
+ w = POP();
+ v = TOP();
+ x = PyNumber_Remainder(v, w);
+ Py_DECREF(v);
+ Py_DECREF(w);
+ SET_TOP(x);
+ if (x != NULL) continue;
+ break;
+
+ case BINARY_ADD:
+ w = POP();
+ v = TOP();
+ if (PyInt_CheckExact(v) && PyInt_CheckExact(w)) {
+ /* INLINE: int + int */
+ register long a, b, i;
+ a = PyInt_AS_LONG(v);
+ b = PyInt_AS_LONG(w);
+ i = a + b;
+ if ((i^a) < 0 && (i^b) < 0)
+ goto slow_add;
+ x = PyInt_FromLong(i);
+ }
+ else if (PyString_CheckExact(v) &&
+ PyString_CheckExact(w)) {
+ x = string_concatenate(v, w, f, next_instr);
+ /* string_concatenate consumed the ref to v */
+ goto skip_decref_vx;
+ }
+ else {
+ slow_add:
+ x = PyNumber_Add(v, w);
+ }
+ Py_DECREF(v);
+ skip_decref_vx:
+ Py_DECREF(w);
+ SET_TOP(x);
+ if (x != NULL) continue;
+ break;
+
+ case BINARY_SUBTRACT:
+ w = POP();
+ v = TOP();
+ if (PyInt_CheckExact(v) && PyInt_CheckExact(w)) {
+ /* INLINE: int - int */
+ register long a, b, i;
+ a = PyInt_AS_LONG(v);
+ b = PyInt_AS_LONG(w);
+ i = a - b;
+ if ((i^a) < 0 && (i^~b) < 0)
+ goto slow_sub;
+ x = PyInt_FromLong(i);
+ }
+ else {
+ slow_sub:
+ x = PyNumber_Subtract(v, w);
+ }
+ Py_DECREF(v);
+ Py_DECREF(w);
+ SET_TOP(x);
+ if (x != NULL) continue;
+ break;
+
+ case BINARY_SUBSCR:
+ w = POP();
+ v = TOP();
+ if (PyList_CheckExact(v) && PyInt_CheckExact(w)) {
+ /* INLINE: list[int] */
+ Py_ssize_t i = PyInt_AsSsize_t(w);
+ if (i < 0)
+ i += PyList_GET_SIZE(v);
+ if (i >= 0 && i < PyList_GET_SIZE(v)) {
+ x = PyList_GET_ITEM(v, i);
+ Py_INCREF(x);
+ }
+ else
+ goto slow_get;
+ }
+ else
+ slow_get:
+ x = PyObject_GetItem(v, w);
+ Py_DECREF(v);
+ Py_DECREF(w);
+ SET_TOP(x);
+ if (x != NULL) continue;
+ break;
+
+ case BINARY_LSHIFT:
+ w = POP();
+ v = TOP();
+ x = PyNumber_Lshift(v, w);
+ Py_DECREF(v);
+ Py_DECREF(w);
+ SET_TOP(x);
+ if (x != NULL) continue;
+ break;
+
+ case BINARY_RSHIFT:
+ w = POP();
+ v = TOP();
+ x = PyNumber_Rshift(v, w);
+ Py_DECREF(v);
+ Py_DECREF(w);
+ SET_TOP(x);
+ if (x != NULL) continue;
+ break;
+
+ case BINARY_AND:
+ w = POP();
+ v = TOP();
+ x = PyNumber_And(v, w);
+ Py_DECREF(v);
+ Py_DECREF(w);
+ SET_TOP(x);
+ if (x != NULL) continue;
+ break;
+
+ case BINARY_XOR:
+ w = POP();
+ v = TOP();
+ x = PyNumber_Xor(v, w);
+ Py_DECREF(v);
+ Py_DECREF(w);
+ SET_TOP(x);
+ if (x != NULL) continue;
+ break;
+
+ case BINARY_OR:
+ w = POP();
+ v = TOP();
+ x = PyNumber_Or(v, w);
+ Py_DECREF(v);
+ Py_DECREF(w);
+ SET_TOP(x);
+ if (x != NULL) continue;
+ break;
+
+ case LIST_APPEND:
+ w = POP();
+ v = POP();
+ err = PyList_Append(v, w);
+ Py_DECREF(v);
+ Py_DECREF(w);
+ if (err == 0) {
+ PREDICT(JUMP_ABSOLUTE);
+ continue;
+ }
+ break;
+
+ case INPLACE_POWER:
+ w = POP();
+ v = TOP();
+ x = PyNumber_InPlacePower(v, w, Py_None);
+ Py_DECREF(v);
+ Py_DECREF(w);
+ SET_TOP(x);
+ if (x != NULL) continue;
+ break;
+
+ case INPLACE_MULTIPLY:
+ w = POP();
+ v = TOP();
+ x = PyNumber_InPlaceMultiply(v, w);
+ Py_DECREF(v);
+ Py_DECREF(w);
+ SET_TOP(x);
+ if (x != NULL) continue;
+ break;
+
+ case INPLACE_DIVIDE:
+ if (!_Py_QnewFlag) {
+ w = POP();
+ v = TOP();
+ x = PyNumber_InPlaceDivide(v, w);
+ Py_DECREF(v);
+ Py_DECREF(w);
+ SET_TOP(x);
+ if (x != NULL) continue;
+ break;
+ }
+ /* -Qnew is in effect: fall through to
+ INPLACE_TRUE_DIVIDE */
+ case INPLACE_TRUE_DIVIDE:
+ w = POP();
+ v = TOP();
+ x = PyNumber_InPlaceTrueDivide(v, w);
+ Py_DECREF(v);
+ Py_DECREF(w);
+ SET_TOP(x);
+ if (x != NULL) continue;
+ break;
+
+ case INPLACE_FLOOR_DIVIDE:
+ w = POP();
+ v = TOP();
+ x = PyNumber_InPlaceFloorDivide(v, w);
+ Py_DECREF(v);
+ Py_DECREF(w);
+ SET_TOP(x);
+ if (x != NULL) continue;
+ break;
+
+ case INPLACE_MODULO:
+ w = POP();
+ v = TOP();
+ x = PyNumber_InPlaceRemainder(v, w);
+ Py_DECREF(v);
+ Py_DECREF(w);
+ SET_TOP(x);
+ if (x != NULL) continue;
+ break;
+
+ case INPLACE_ADD:
+ w = POP();
+ v = TOP();
+ if (PyInt_CheckExact(v) && PyInt_CheckExact(w)) {
+ /* INLINE: int + int */
+ register long a, b, i;
+ a = PyInt_AS_LONG(v);
+ b = PyInt_AS_LONG(w);
+ i = a + b;
+ if ((i^a) < 0 && (i^b) < 0)
+ goto slow_iadd;
+ x = PyInt_FromLong(i);
+ }
+ else if (PyString_CheckExact(v) &&
+ PyString_CheckExact(w)) {
+ x = string_concatenate(v, w, f, next_instr);
+ /* string_concatenate consumed the ref to v */
+ goto skip_decref_v;
+ }
+ else {
+ slow_iadd:
+ x = PyNumber_InPlaceAdd(v, w);
+ }
+ Py_DECREF(v);
+ skip_decref_v:
+ Py_DECREF(w);
+ SET_TOP(x);
+ if (x != NULL) continue;
+ break;
+
+ case INPLACE_SUBTRACT:
+ w = POP();
+ v = TOP();
+ if (PyInt_CheckExact(v) && PyInt_CheckExact(w)) {
+ /* INLINE: int - int */
+ register long a, b, i;
+ a = PyInt_AS_LONG(v);
+ b = PyInt_AS_LONG(w);
+ i = a - b;
+ if ((i^a) < 0 && (i^~b) < 0)
+ goto slow_isub;
+ x = PyInt_FromLong(i);
+ }
+ else {
+ slow_isub:
+ x = PyNumber_InPlaceSubtract(v, w);
+ }
+ Py_DECREF(v);
+ Py_DECREF(w);
+ SET_TOP(x);
+ if (x != NULL) continue;
+ break;
+
+ case INPLACE_LSHIFT:
+ w = POP();
+ v = TOP();
+ x = PyNumber_InPlaceLshift(v, w);
+ Py_DECREF(v);
+ Py_DECREF(w);
+ SET_TOP(x);
+ if (x != NULL) continue;
+ break;
+
+ case INPLACE_RSHIFT:
+ w = POP();
+ v = TOP();
+ x = PyNumber_InPlaceRshift(v, w);
+ Py_DECREF(v);
+ Py_DECREF(w);
+ SET_TOP(x);
+ if (x != NULL) continue;
+ break;
+
+ case INPLACE_AND:
+ w = POP();
+ v = TOP();
+ x = PyNumber_InPlaceAnd(v, w);
+ Py_DECREF(v);
+ Py_DECREF(w);
+ SET_TOP(x);
+ if (x != NULL) continue;
+ break;
+
+ case INPLACE_XOR:
+ w = POP();
+ v = TOP();
+ x = PyNumber_InPlaceXor(v, w);
+ Py_DECREF(v);
+ Py_DECREF(w);
+ SET_TOP(x);
+ if (x != NULL) continue;
+ break;
+
+ case INPLACE_OR:
+ w = POP();
+ v = TOP();
+ x = PyNumber_InPlaceOr(v, w);
+ Py_DECREF(v);
+ Py_DECREF(w);
+ SET_TOP(x);
+ if (x != NULL) continue;
+ break;
+
+ case SLICE+0:
+ case SLICE+1:
+ case SLICE+2:
+ case SLICE+3:
+ if ((opcode-SLICE) & 2)
+ w = POP();
+ else
+ w = NULL;
+ if ((opcode-SLICE) & 1)
+ v = POP();
+ else
+ v = NULL;
+ u = TOP();
+ x = apply_slice(u, v, w);
+ Py_DECREF(u);
+ Py_XDECREF(v);
+ Py_XDECREF(w);
+ SET_TOP(x);
+ if (x != NULL) continue;
+ break;
+
+ case STORE_SLICE+0:
+ case STORE_SLICE+1:
+ case STORE_SLICE+2:
+ case STORE_SLICE+3:
+ if ((opcode-STORE_SLICE) & 2)
+ w = POP();
+ else
+ w = NULL;
+ if ((opcode-STORE_SLICE) & 1)
+ v = POP();
+ else
+ v = NULL;
+ u = POP();
+ t = POP();
+ err = assign_slice(u, v, w, t); /* u[v:w] = t */
+ Py_DECREF(t);
+ Py_DECREF(u);
+ Py_XDECREF(v);
+ Py_XDECREF(w);
+ if (err == 0) continue;
+ break;
+
+ case DELETE_SLICE+0:
+ case DELETE_SLICE+1:
+ case DELETE_SLICE+2:
+ case DELETE_SLICE+3:
+ if ((opcode-DELETE_SLICE) & 2)
+ w = POP();
+ else
+ w = NULL;
+ if ((opcode-DELETE_SLICE) & 1)
+ v = POP();
+ else
+ v = NULL;
+ u = POP();
+ err = assign_slice(u, v, w, (PyObject *)NULL);
+ /* del u[v:w] */
+ Py_DECREF(u);
+ Py_XDECREF(v);
+ Py_XDECREF(w);
+ if (err == 0) continue;
+ break;
+
+ case STORE_SUBSCR:
+ w = TOP();
+ v = SECOND();
+ u = THIRD();
+ STACKADJ(-3);
+ /* v[w] = u */
+ err = PyObject_SetItem(v, w, u);
+ Py_DECREF(u);
+ Py_DECREF(v);
+ Py_DECREF(w);
+ if (err == 0) continue;
+ break;
+
+ case DELETE_SUBSCR:
+ w = TOP();
+ v = SECOND();
+ STACKADJ(-2);
+ /* del v[w] */
+ err = PyObject_DelItem(v, w);
+ Py_DECREF(v);
+ Py_DECREF(w);
+ if (err == 0) continue;
+ break;
+
+ case PRINT_EXPR:
+ v = POP();
+ w = PySys_GetObject("displayhook");
+ if (w == NULL) {
+ PyErr_SetString(PyExc_RuntimeError,
+ "lost sys.displayhook");
+ err = -1;
+ x = NULL;
+ }
+ if (err == 0) {
+ x = PyTuple_Pack(1, v);
+ if (x == NULL)
+ err = -1;
+ }
+ if (err == 0) {
+ w = PyEval_CallObject(w, x);
+ Py_XDECREF(w);
+ if (w == NULL)
+ err = -1;
+ }
+ Py_DECREF(v);
+ Py_XDECREF(x);
+ break;
+
+ case PRINT_ITEM_TO:
+ w = stream = POP();
+ /* fall through to PRINT_ITEM */
+
+ case PRINT_ITEM:
+ v = POP();
+ if (stream == NULL || stream == Py_None) {
+ w = PySys_GetObject("stdout");
+ if (w == NULL) {
+ PyErr_SetString(PyExc_RuntimeError,
+ "lost sys.stdout");
+ err = -1;
+ }
+ }
+ /* PyFile_SoftSpace() can exececute arbitrary code
+ if sys.stdout is an instance with a __getattr__.
+ If __getattr__ raises an exception, w will
+ be freed, so we need to prevent that temporarily. */
+ Py_XINCREF(w);
+ if (w != NULL && PyFile_SoftSpace(w, 0))
+ err = PyFile_WriteString(" ", w);
+ if (err == 0)
+ err = PyFile_WriteObject(v, w, Py_PRINT_RAW);
+ if (err == 0) {
+ /* XXX move into writeobject() ? */
+ if (PyString_Check(v)) {
+ char *s = PyString_AS_STRING(v);
+ Py_ssize_t len = PyString_GET_SIZE(v);
+ if (len == 0 ||
+ !isspace(Py_CHARMASK(s[len-1])) ||
+ s[len-1] == ' ')
+ PyFile_SoftSpace(w, 1);
+ }
+#ifdef Py_USING_UNICODE
+ else if (PyUnicode_Check(v)) {
+ Py_UNICODE *s = PyUnicode_AS_UNICODE(v);
+ Py_ssize_t len = PyUnicode_GET_SIZE(v);
+ if (len == 0 ||
+ !Py_UNICODE_ISSPACE(s[len-1]) ||
+ s[len-1] == ' ')
+ PyFile_SoftSpace(w, 1);
+ }
+#endif
+ else
+ PyFile_SoftSpace(w, 1);
+ }
+ Py_XDECREF(w);
+ Py_DECREF(v);
+ Py_XDECREF(stream);
+ stream = NULL;
+ if (err == 0)
+ continue;
+ break;
+
+ case PRINT_NEWLINE_TO:
+ w = stream = POP();
+ /* fall through to PRINT_NEWLINE */
+
+ case PRINT_NEWLINE:
+ if (stream == NULL || stream == Py_None) {
+ w = PySys_GetObject("stdout");
+ if (w == NULL)
+ PyErr_SetString(PyExc_RuntimeError,
+ "lost sys.stdout");
+ }
+ if (w != NULL) {
+ err = PyFile_WriteString("\n", w);
+ if (err == 0)
+ PyFile_SoftSpace(w, 0);
+ }
+ Py_XDECREF(stream);
+ stream = NULL;
+ break;
+
+
+#ifdef CASE_TOO_BIG
+ default: switch (opcode) {
+#endif
+ case RAISE_VARARGS:
+ u = v = w = NULL;
+ switch (oparg) {
+ case 3:
+ u = POP(); /* traceback */
+ /* Fallthrough */
+ case 2:
+ v = POP(); /* value */
+ /* Fallthrough */
+ case 1:
+ w = POP(); /* exc */
+ case 0: /* Fallthrough */
+ why = do_raise(w, v, u);
+ break;
+ default:
+ PyErr_SetString(PyExc_SystemError,
+ "bad RAISE_VARARGS oparg");
+ why = WHY_EXCEPTION;
+ break;
+ }
+ break;
+
+ case LOAD_LOCALS:
+ if ((x = f->f_locals) != NULL) {
+ Py_INCREF(x);
+ PUSH(x);
+ continue;
+ }
+ PyErr_SetString(PyExc_SystemError, "no locals");
+ break;
+
+ case RETURN_VALUE:
+ retval = POP();
+ why = WHY_RETURN;
+ goto fast_block_end;
+
+ case YIELD_VALUE:
+ retval = POP();
+ f->f_stacktop = stack_pointer;
+ why = WHY_YIELD;
+ goto fast_yield;
+
+ case EXEC_STMT:
+ w = TOP();
+ v = SECOND();
+ u = THIRD();
+ STACKADJ(-3);
+ READ_TIMESTAMP(intr0);
+ err = exec_statement(f, u, v, w);
+ READ_TIMESTAMP(intr1);
+ Py_DECREF(u);
+ Py_DECREF(v);
+ Py_DECREF(w);
+ break;
+
+ case POP_BLOCK:
+ {
+ PyTryBlock *b = PyFrame_BlockPop(f);
+ while (STACK_LEVEL() > b->b_level) {
+ v = POP();
+ Py_DECREF(v);
+ }
+ }
+ continue;
+
+ case END_FINALLY:
+ v = POP();
+ if (PyInt_Check(v)) {
+ why = (enum why_code) PyInt_AS_LONG(v);
+ assert(why != WHY_YIELD);
+ if (why == WHY_RETURN ||
+ why == WHY_CONTINUE)
+ retval = POP();
+ }
+ else if (PyExceptionClass_Check(v) || PyString_Check(v)) {
+ w = POP();
+ u = POP();
+ PyErr_Restore(v, w, u);
+ why = WHY_RERAISE;
+ break;
+ }
+ else if (v != Py_None) {
+ PyErr_SetString(PyExc_SystemError,
+ "'finally' pops bad exception");
+ why = WHY_EXCEPTION;
+ }
+ Py_DECREF(v);
+ break;
+
+ case BUILD_CLASS:
+ u = TOP();
+ v = SECOND();
+ w = THIRD();
+ STACKADJ(-2);
+ x = build_class(u, v, w);
+ SET_TOP(x);
+ Py_DECREF(u);
+ Py_DECREF(v);
+ Py_DECREF(w);
+ break;
+
+ case STORE_NAME:
+ w = GETITEM(names, oparg);
+ v = POP();
+ if ((x = f->f_locals) != NULL) {
+ if (PyDict_CheckExact(x))
+ err = PyDict_SetItem(x, w, v);
+ else
+ err = PyObject_SetItem(x, w, v);
+ Py_DECREF(v);
+ if (err == 0) continue;
+ break;
+ }
+ PyErr_Format(PyExc_SystemError,
+ "no locals found when storing %s",
+ PyObject_REPR(w));
+ break;
+
+ case DELETE_NAME:
+ w = GETITEM(names, oparg);
+ if ((x = f->f_locals) != NULL) {
+ if ((err = PyObject_DelItem(x, w)) != 0)
+ format_exc_check_arg(PyExc_NameError,
+ NAME_ERROR_MSG ,w);
+ break;
+ }
+ PyErr_Format(PyExc_SystemError,
+ "no locals when deleting %s",
+ PyObject_REPR(w));
+ break;
+
+ PREDICTED_WITH_ARG(UNPACK_SEQUENCE);
+ case UNPACK_SEQUENCE:
+ v = POP();
+ if (PyTuple_CheckExact(v) && PyTuple_GET_SIZE(v) == oparg) {
+ PyObject **items = ((PyTupleObject *)v)->ob_item;
+ while (oparg--) {
+ w = items[oparg];
+ Py_INCREF(w);
+ PUSH(w);
+ }
+ Py_DECREF(v);
+ continue;
+ } else if (PyList_CheckExact(v) && PyList_GET_SIZE(v) == oparg) {
+ PyObject **items = ((PyListObject *)v)->ob_item;
+ while (oparg--) {
+ w = items[oparg];
+ Py_INCREF(w);
+ PUSH(w);
+ }
+ } else if (unpack_iterable(v, oparg,
+ stack_pointer + oparg))
+ stack_pointer += oparg;
+ else {
+ if (PyErr_ExceptionMatches(PyExc_TypeError))
+ PyErr_SetString(PyExc_TypeError,
+ "unpack non-sequence");
+ why = WHY_EXCEPTION;
+ }
+ Py_DECREF(v);
+ break;
+
+ case STORE_ATTR:
+ w = GETITEM(names, oparg);
+ v = TOP();
+ u = SECOND();
+ STACKADJ(-2);
+ err = PyObject_SetAttr(v, w, u); /* v.w = u */
+ Py_DECREF(v);
+ Py_DECREF(u);
+ if (err == 0) continue;
+ break;
+
+ case DELETE_ATTR:
+ w = GETITEM(names, oparg);
+ v = POP();
+ err = PyObject_SetAttr(v, w, (PyObject *)NULL);
+ /* del v.w */
+ Py_DECREF(v);
+ break;
+
+ case STORE_GLOBAL:
+ w = GETITEM(names, oparg);
+ v = POP();
+ err = PyDict_SetItem(f->f_globals, w, v);
+ Py_DECREF(v);
+ if (err == 0) continue;
+ break;
+
+ case DELETE_GLOBAL:
+ w = GETITEM(names, oparg);
+ if ((err = PyDict_DelItem(f->f_globals, w)) != 0)
+ format_exc_check_arg(
+ PyExc_NameError, GLOBAL_NAME_ERROR_MSG, w);
+ break;
+
+ case LOAD_NAME:
+ w = GETITEM(names, oparg);
+ if ((v = f->f_locals) == NULL) {
+ PyErr_Format(PyExc_SystemError,
+ "no locals when loading %s",
+ PyObject_REPR(w));
+ break;
+ }
+ if (PyDict_CheckExact(v)) {
+ x = PyDict_GetItem(v, w);
+ Py_XINCREF(x);
+ }
+ else {
+ x = PyObject_GetItem(v, w);
+ if (x == NULL && PyErr_Occurred()) {
+ if (!PyErr_ExceptionMatches(PyExc_KeyError))
+ break;
+ PyErr_Clear();
+ }
+ }
+ if (x == NULL) {
+ x = PyDict_GetItem(f->f_globals, w);
+ if (x == NULL) {
+ x = PyDict_GetItem(f->f_builtins, w);
+ if (x == NULL) {
+ format_exc_check_arg(
+ PyExc_NameError,
+ NAME_ERROR_MSG ,w);
+ break;
+ }
+ }
+ Py_INCREF(x);
+ }
+ PUSH(x);
+ continue;
+
+ case LOAD_GLOBAL:
+ w = GETITEM(names, oparg);
+ if (PyString_CheckExact(w)) {
+ /* Inline the PyDict_GetItem() calls.
+ WARNING: this is an extreme speed hack.
+ Do not try this at home. */
+ long hash = ((PyStringObject *)w)->ob_shash;
+ if (hash != -1) {
+ PyDictObject *d;
+ PyDictEntry *e;
+ d = (PyDictObject *)(f->f_globals);
+ e = d->ma_lookup(d, w, hash);
+ if (e == NULL) {
+ x = NULL;
+ break;
+ }
+ x = e->me_value;
+ if (x != NULL) {
+ Py_INCREF(x);
+ PUSH(x);
+ continue;
+ }
+ d = (PyDictObject *)(f->f_builtins);
+ e = d->ma_lookup(d, w, hash);
+ if (e == NULL) {
+ x = NULL;
+ break;
+ }
+ x = e->me_value;
+ if (x != NULL) {
+ Py_INCREF(x);
+ PUSH(x);
+ continue;
+ }
+ goto load_global_error;
+ }
+ }
+ /* This is the un-inlined version of the code above */
+ x = PyDict_GetItem(f->f_globals, w);
+ if (x == NULL) {
+ x = PyDict_GetItem(f->f_builtins, w);
+ if (x == NULL) {
+ load_global_error:
+ format_exc_check_arg(
+ PyExc_NameError,
+ GLOBAL_NAME_ERROR_MSG, w);
+ break;
+ }
+ }
+ Py_INCREF(x);
+ PUSH(x);
+ continue;
+
+ case DELETE_FAST:
+ x = GETLOCAL(oparg);
+ if (x != NULL) {
+ SETLOCAL(oparg, NULL);
+ continue;
+ }
+ format_exc_check_arg(
+ PyExc_UnboundLocalError,
+ UNBOUNDLOCAL_ERROR_MSG,
+ PyTuple_GetItem(co->co_varnames, oparg)
+ );
+ break;
+
+ case LOAD_CLOSURE:
+ x = freevars[oparg];
+ Py_INCREF(x);
+ PUSH(x);
+ if (x != NULL) continue;
+ break;
+
+ case LOAD_DEREF:
+ x = freevars[oparg];
+ w = PyCell_Get(x);
+ if (w != NULL) {
+ PUSH(w);
+ continue;
+ }
+ err = -1;
+ /* Don't stomp existing exception */
+ if (PyErr_Occurred())
+ break;
+ if (oparg < PyTuple_GET_SIZE(co->co_cellvars)) {
+ v = PyTuple_GET_ITEM(co->co_cellvars,
+ oparg);
+ format_exc_check_arg(
+ PyExc_UnboundLocalError,
+ UNBOUNDLOCAL_ERROR_MSG,
+ v);
+ } else {
+ v = PyTuple_GET_ITEM(
+ co->co_freevars,
+ oparg - PyTuple_GET_SIZE(co->co_cellvars));
+ format_exc_check_arg(
+ PyExc_NameError,
+ UNBOUNDFREE_ERROR_MSG,
+ v);
+ }
+ break;
+
+ case STORE_DEREF:
+ w = POP();
+ x = freevars[oparg];
+ PyCell_Set(x, w);
+ Py_DECREF(w);
+ continue;
+
+ case BUILD_TUPLE:
+ x = PyTuple_New(oparg);
+ if (x != NULL) {
+ for (; --oparg >= 0;) {
+ w = POP();
+ PyTuple_SET_ITEM(x, oparg, w);
+ }
+ PUSH(x);
+ continue;
+ }
+ break;
+
+ case BUILD_LIST:
+ x = PyList_New(oparg);
+ if (x != NULL) {
+ for (; --oparg >= 0;) {
+ w = POP();
+ PyList_SET_ITEM(x, oparg, w);
+ }
+ PUSH(x);
+ continue;
+ }
+ break;
+
+ case BUILD_MAP:
+ x = PyDict_New();
+ PUSH(x);
+ if (x != NULL) continue;
+ break;
+
+ case LOAD_ATTR:
+ w = GETITEM(names, oparg);
+ v = TOP();
+ x = PyObject_GetAttr(v, w);
+ Py_DECREF(v);
+ SET_TOP(x);
+ if (x != NULL) continue;
+ break;
+
+ case COMPARE_OP:
+ w = POP();
+ v = TOP();
+ if (PyInt_CheckExact(w) && PyInt_CheckExact(v)) {
+ /* INLINE: cmp(int, int) */
+ register long a, b;
+ register int res;
+ a = PyInt_AS_LONG(v);
+ b = PyInt_AS_LONG(w);
+ switch (oparg) {
+ case PyCmp_LT: res = a < b; break;
+ case PyCmp_LE: res = a <= b; break;
+ case PyCmp_EQ: res = a == b; break;
+ case PyCmp_NE: res = a != b; break;
+ case PyCmp_GT: res = a > b; break;
+ case PyCmp_GE: res = a >= b; break;
+ case PyCmp_IS: res = v == w; break;
+ case PyCmp_IS_NOT: res = v != w; break;
+ default: goto slow_compare;
+ }
+ x = res ? Py_True : Py_False;
+ Py_INCREF(x);
+ }
+ else {
+ slow_compare:
+ x = cmp_outcome(oparg, v, w);
+ }
+ Py_DECREF(v);
+ Py_DECREF(w);
+ SET_TOP(x);
+ if (x == NULL) break;
+ PREDICT(JUMP_IF_FALSE);
+ PREDICT(JUMP_IF_TRUE);
+ continue;
+
+ case IMPORT_NAME:
+ w = GETITEM(names, oparg);
+ x = PyDict_GetItemString(f->f_builtins, "__import__");
+ if (x == NULL) {
+ PyErr_SetString(PyExc_ImportError,
+ "__import__ not found");
+ break;
+ }
+ v = POP();
+ u = TOP();
+ if (PyInt_AsLong(u) != -1 || PyErr_Occurred())
+ w = PyTuple_Pack(5,
+ w,
+ f->f_globals,
+ f->f_locals == NULL ?
+ Py_None : f->f_locals,
+ v,
+ u);
+ else
+ w = PyTuple_Pack(4,
+ w,
+ f->f_globals,
+ f->f_locals == NULL ?
+ Py_None : f->f_locals,
+ v);
+ Py_DECREF(v);
+ Py_DECREF(u);
+ if (w == NULL) {
+ u = POP();
+ x = NULL;
+ break;
+ }
+ READ_TIMESTAMP(intr0);
+ x = PyEval_CallObject(x, w);
+ READ_TIMESTAMP(intr1);
+ Py_DECREF(w);
+ SET_TOP(x);
+ if (x != NULL) continue;
+ break;
+
+ case IMPORT_STAR:
+ v = POP();
+ PyFrame_FastToLocals(f);
+ if ((x = f->f_locals) == NULL) {
+ PyErr_SetString(PyExc_SystemError,
+ "no locals found during 'import *'");
+ break;
+ }
+ READ_TIMESTAMP(intr0);
+ err = import_all_from(x, v);
+ READ_TIMESTAMP(intr1);
+ PyFrame_LocalsToFast(f, 0);
+ Py_DECREF(v);
+ if (err == 0) continue;
+ break;
+
+ case IMPORT_FROM:
+ w = GETITEM(names, oparg);
+ v = TOP();
+ READ_TIMESTAMP(intr0);
+ x = import_from(v, w);
+ READ_TIMESTAMP(intr1);
+ PUSH(x);
+ if (x != NULL) continue;
+ break;
+
+ case JUMP_FORWARD:
+ JUMPBY(oparg);
+ goto fast_next_opcode;
+
+ PREDICTED_WITH_ARG(JUMP_IF_FALSE);
+ case JUMP_IF_FALSE:
+ w = TOP();
+ if (w == Py_True) {
+ PREDICT(POP_TOP);
+ goto fast_next_opcode;
+ }
+ if (w == Py_False) {
+ JUMPBY(oparg);
+ goto fast_next_opcode;
+ }
+ err = PyObject_IsTrue(w);
+ if (err > 0)
+ err = 0;
+ else if (err == 0)
+ JUMPBY(oparg);
+ else
+ break;
+ continue;
+
+ PREDICTED_WITH_ARG(JUMP_IF_TRUE);
+ case JUMP_IF_TRUE:
+ w = TOP();
+ if (w == Py_False) {
+ PREDICT(POP_TOP);
+ goto fast_next_opcode;
+ }
+ if (w == Py_True) {
+ JUMPBY(oparg);
+ goto fast_next_opcode;
+ }
+ err = PyObject_IsTrue(w);
+ if (err > 0) {
+ err = 0;
+ JUMPBY(oparg);
+ }
+ else if (err == 0)
+ ;
+ else
+ break;
+ continue;
+
+ PREDICTED_WITH_ARG(JUMP_ABSOLUTE);
+ case JUMP_ABSOLUTE:
+ JUMPTO(oparg);
+ continue;
+
+ case GET_ITER:
+ /* before: [obj]; after [getiter(obj)] */
+ v = TOP();
+ x = PyObject_GetIter(v);
+ Py_DECREF(v);
+ if (x != NULL) {
+ SET_TOP(x);
+ PREDICT(FOR_ITER);
+ continue;
+ }
+ STACKADJ(-1);
+ break;
+
+ PREDICTED_WITH_ARG(FOR_ITER);
+ case FOR_ITER:
+ /* before: [iter]; after: [iter, iter()] *or* [] */
+ v = TOP();
+ x = (*v->ob_type->tp_iternext)(v);
+ if (x != NULL) {
+ PUSH(x);
+ PREDICT(STORE_FAST);
+ PREDICT(UNPACK_SEQUENCE);
+ continue;
+ }
+ if (PyErr_Occurred()) {
+ if (!PyErr_ExceptionMatches(PyExc_StopIteration))
+ break;
+ PyErr_Clear();
+ }
+ /* iterator ended normally */
+ x = v = POP();
+ Py_DECREF(v);
+ JUMPBY(oparg);
+ continue;
+
+ case BREAK_LOOP:
+ why = WHY_BREAK;
+ goto fast_block_end;
+
+ case CONTINUE_LOOP:
+ retval = PyInt_FromLong(oparg);
+ if (!retval) {
+ x = NULL;
+ break;
+ }
+ why = WHY_CONTINUE;
+ goto fast_block_end;
+
+ case SETUP_LOOP:
+ case SETUP_EXCEPT:
+ case SETUP_FINALLY:
+ /* NOTE: If you add any new block-setup opcodes that are not try/except/finally
+ handlers, you may need to update the PyGen_NeedsFinalizing() function. */
+
+ PyFrame_BlockSetup(f, opcode, INSTR_OFFSET() + oparg,
+ STACK_LEVEL());
+ continue;
+
+ case WITH_CLEANUP:
+ {
+ /* TOP is the context.__exit__ bound method.
+ Below that are 1-3 values indicating how/why
+ we entered the finally clause:
+ - SECOND = None
+ - (SECOND, THIRD) = (WHY_{RETURN,CONTINUE}), retval
+ - SECOND = WHY_*; no retval below it
+ - (SECOND, THIRD, FOURTH) = exc_info()
+ In the last case, we must call
+ TOP(SECOND, THIRD, FOURTH)
+ otherwise we must call
+ TOP(None, None, None)
+
+ In addition, if the stack represents an exception,
+ *and* the function call returns a 'true' value, we
+ "zap" this information, to prevent END_FINALLY from
+ re-raising the exception. (But non-local gotos
+ should still be resumed.)
+ */
+
+ x = TOP();
+ u = SECOND();
+ if (PyInt_Check(u) || u == Py_None) {
+ u = v = w = Py_None;
+ }
+ else {
+ v = THIRD();
+ w = FOURTH();
+ }
+ /* XXX Not the fastest way to call it... */
+ x = PyObject_CallFunctionObjArgs(x, u, v, w, NULL);
+ if (x == NULL)
+ break; /* Go to error exit */
+ if (u != Py_None && PyObject_IsTrue(x)) {
+ /* There was an exception and a true return */
+ Py_DECREF(x);
+ x = TOP(); /* Again */
+ STACKADJ(-3);
+ Py_INCREF(Py_None);
+ SET_TOP(Py_None);
+ Py_DECREF(x);
+ Py_DECREF(u);
+ Py_DECREF(v);
+ Py_DECREF(w);
+ } else {
+ /* Let END_FINALLY do its thing */
+ Py_DECREF(x);
+ x = POP();
+ Py_DECREF(x);
+ }
+ break;
+ }
+
+ case CALL_FUNCTION:
+ {
+ PyObject **sp;
+ PCALL(PCALL_ALL);
+ sp = stack_pointer;
+#ifdef WITH_TSC
+ x = call_function(&sp, oparg, &intr0, &intr1);
+#else
+ x = call_function(&sp, oparg);
+#endif
+ stack_pointer = sp;
+ PUSH(x);
+ if (x != NULL)
+ continue;
+ break;
+ }
+
+ case CALL_FUNCTION_VAR:
+ case CALL_FUNCTION_KW:
+ case CALL_FUNCTION_VAR_KW:
+ {
+ int na = oparg & 0xff;
+ int nk = (oparg>>8) & 0xff;
+ int flags = (opcode - CALL_FUNCTION) & 3;
+ int n = na + 2 * nk;
+ PyObject **pfunc, *func, **sp;
+ PCALL(PCALL_ALL);
+ if (flags & CALL_FLAG_VAR)
+ n++;
+ if (flags & CALL_FLAG_KW)
+ n++;
+ pfunc = stack_pointer - n - 1;
+ func = *pfunc;
+
+ if (PyMethod_Check(func)
+ && PyMethod_GET_SELF(func) != NULL) {
+ PyObject *self = PyMethod_GET_SELF(func);
+ Py_INCREF(self);
+ func = PyMethod_GET_FUNCTION(func);
+ Py_INCREF(func);
+ Py_DECREF(*pfunc);
+ *pfunc = self;
+ na++;
+ n++;
+ } else
+ Py_INCREF(func);
+ sp = stack_pointer;
+ READ_TIMESTAMP(intr0);
+ x = ext_do_call(func, &sp, flags, na, nk);
+ READ_TIMESTAMP(intr1);
+ stack_pointer = sp;
+ Py_DECREF(func);
+
+ while (stack_pointer > pfunc) {
+ w = POP();
+ Py_DECREF(w);
+ }
+ PUSH(x);
+ if (x != NULL)
+ continue;
+ break;
+ }
+
+ case MAKE_FUNCTION:
+ v = POP(); /* code object */
+ x = PyFunction_New(v, f->f_globals);
+ Py_DECREF(v);
+ /* XXX Maybe this should be a separate opcode? */
+ if (x != NULL && oparg > 0) {
+ v = PyTuple_New(oparg);
+ if (v == NULL) {
+ Py_DECREF(x);
+ x = NULL;
+ break;
+ }
+ while (--oparg >= 0) {
+ w = POP();
+ PyTuple_SET_ITEM(v, oparg, w);
+ }
+ err = PyFunction_SetDefaults(x, v);
+ Py_DECREF(v);
+ }
+ PUSH(x);
+ break;
+
+ case MAKE_CLOSURE:
+ {
+ v = POP(); /* code object */
+ x = PyFunction_New(v, f->f_globals);
+ Py_DECREF(v);
+ if (x != NULL) {
+ v = POP();
+ err = PyFunction_SetClosure(x, v);
+ Py_DECREF(v);
+ }
+ if (x != NULL && oparg > 0) {
+ v = PyTuple_New(oparg);
+ if (v == NULL) {
+ Py_DECREF(x);
+ x = NULL;
+ break;
+ }
+ while (--oparg >= 0) {
+ w = POP();
+ PyTuple_SET_ITEM(v, oparg, w);
+ }
+ err = PyFunction_SetDefaults(x, v);
+ Py_DECREF(v);
+ }
+ PUSH(x);
+ break;
+ }
+
+ case BUILD_SLICE:
+ if (oparg == 3)
+ w = POP();
+ else
+ w = NULL;
+ v = POP();
+ u = TOP();
+ x = PySlice_New(u, v, w);
+ Py_DECREF(u);
+ Py_DECREF(v);
+ Py_XDECREF(w);
+ SET_TOP(x);
+ if (x != NULL) continue;
+ break;
+
+ case EXTENDED_ARG:
+ opcode = NEXTOP();
+ oparg = oparg<<16 | NEXTARG();
+ goto dispatch_opcode;
+
+ default:
+ fprintf(stderr,
+ "XXX lineno: %d, opcode: %d\n",
+ PyCode_Addr2Line(f->f_code, f->f_lasti),
+ opcode);
+ PyErr_SetString(PyExc_SystemError, "unknown opcode");
+ why = WHY_EXCEPTION;
+ break;
+
+#ifdef CASE_TOO_BIG
+ }
+#endif
+
+ } /* switch */
+
+ on_error:
+
+ READ_TIMESTAMP(inst1);
+
+ /* Quickly continue if no error occurred */
+
+ if (why == WHY_NOT) {
+ if (err == 0 && x != NULL) {
+#ifdef CHECKEXC
+ /* This check is expensive! */
+ if (PyErr_Occurred())
+ fprintf(stderr,
+ "XXX undetected error\n");
+ else {
+#endif
+ READ_TIMESTAMP(loop1);
+ continue; /* Normal, fast path */
+#ifdef CHECKEXC
+ }
+#endif
+ }
+ why = WHY_EXCEPTION;
+ x = Py_None;
+ err = 0;
+ }
+
+ /* Double-check exception status */
+
+ if (why == WHY_EXCEPTION || why == WHY_RERAISE) {
+ if (!PyErr_Occurred()) {
+ PyErr_SetString(PyExc_SystemError,
+ "error return without exception set");
+ why = WHY_EXCEPTION;
+ }
+ }
+#ifdef CHECKEXC
+ else {
+ /* This check is expensive! */
+ if (PyErr_Occurred()) {
+ char buf[1024];
+ sprintf(buf, "Stack unwind with exception "
+ "set and why=%d", why);
+ Py_FatalError(buf);
+ }
+ }
+#endif
+
+ /* Log traceback info if this is a real exception */
+
+ if (why == WHY_EXCEPTION) {
+ PyTraceBack_Here(f);
+
+ if (tstate->c_tracefunc != NULL)
+ call_exc_trace(tstate->c_tracefunc,
+ tstate->c_traceobj, f);
+ }
+
+ /* For the rest, treat WHY_RERAISE as WHY_EXCEPTION */
+
+ if (why == WHY_RERAISE)
+ why = WHY_EXCEPTION;
+
+ /* Unwind stacks if a (pseudo) exception occurred */
+
+fast_block_end:
+ while (why != WHY_NOT && f->f_iblock > 0) {
+ PyTryBlock *b = PyFrame_BlockPop(f);
+
+ assert(why != WHY_YIELD);
+ if (b->b_type == SETUP_LOOP && why == WHY_CONTINUE) {
+ /* For a continue inside a try block,
+ don't pop the block for the loop. */
+ PyFrame_BlockSetup(f, b->b_type, b->b_handler,
+ b->b_level);
+ why = WHY_NOT;
+ JUMPTO(PyInt_AS_LONG(retval));
+ Py_DECREF(retval);
+ break;
+ }
+
+ while (STACK_LEVEL() > b->b_level) {
+ v = POP();
+ Py_XDECREF(v);
+ }
+ if (b->b_type == SETUP_LOOP && why == WHY_BREAK) {
+ why = WHY_NOT;
+ JUMPTO(b->b_handler);
+ break;
+ }
+ if (b->b_type == SETUP_FINALLY ||
+ (b->b_type == SETUP_EXCEPT &&
+ why == WHY_EXCEPTION)) {
+ if (why == WHY_EXCEPTION) {
+ PyObject *exc, *val, *tb;
+ PyErr_Fetch(&exc, &val, &tb);
+ if (val == NULL) {
+ val = Py_None;
+ Py_INCREF(val);
+ }
+ /* Make the raw exception data
+ available to the handler,
+ so a program can emulate the
+ Python main loop. Don't do
+ this for 'finally'. */
+ if (b->b_type == SETUP_EXCEPT) {
+ PyErr_NormalizeException(
+ &exc, &val, &tb);
+ set_exc_info(tstate,
+ exc, val, tb);
+ }
+ if (tb == NULL) {
+ Py_INCREF(Py_None);
+ PUSH(Py_None);
+ } else
+ PUSH(tb);
+ PUSH(val);
+ PUSH(exc);
+ }
+ else {
+ if (why & (WHY_RETURN | WHY_CONTINUE))
+ PUSH(retval);
+ v = PyInt_FromLong((long)why);
+ PUSH(v);
+ }
+ why = WHY_NOT;
+ JUMPTO(b->b_handler);
+ break;
+ }
+ } /* unwind stack */
+
+ /* End the loop if we still have an error (or return) */
+
+ if (why != WHY_NOT)
+ break;
+ READ_TIMESTAMP(loop1);
+
+ } /* main loop */
+
+ assert(why != WHY_YIELD);
+ /* Pop remaining stack entries. */
+ while (!EMPTY()) {
+ v = POP();
+ Py_XDECREF(v);
+ }
+
+ if (why != WHY_RETURN)
+ retval = NULL;
+
+fast_yield:
+ if (tstate->use_tracing) {
+ if (tstate->c_tracefunc) {
+ if (why == WHY_RETURN || why == WHY_YIELD) {
+ if (call_trace(tstate->c_tracefunc,
+ tstate->c_traceobj, f,
+ PyTrace_RETURN, retval)) {
+ Py_XDECREF(retval);
+ retval = NULL;
+ why = WHY_EXCEPTION;
+ }
+ }
+ else if (why == WHY_EXCEPTION) {
+ call_trace_protected(tstate->c_tracefunc,
+ tstate->c_traceobj, f,
+ PyTrace_RETURN, NULL);
+ }
+ }
+ if (tstate->c_profilefunc) {
+ if (why == WHY_EXCEPTION)
+ call_trace_protected(tstate->c_profilefunc,
+ tstate->c_profileobj, f,
+ PyTrace_RETURN, NULL);
+ else if (call_trace(tstate->c_profilefunc,
+ tstate->c_profileobj, f,
+ PyTrace_RETURN, retval)) {
+ Py_XDECREF(retval);
+ retval = NULL;
+ why = WHY_EXCEPTION;
+ }
+ }
+ }
+
+ if (tstate->frame->f_exc_type != NULL)
+ reset_exc_info(tstate);
+ else {
+ assert(tstate->frame->f_exc_value == NULL);
+ assert(tstate->frame->f_exc_traceback == NULL);
+ }
+
+ /* pop frame */
+ exit_eval_frame:
+ Py_LeaveRecursiveCall();
+ tstate->frame = f->f_back;
+
+ return retval;
+}
+
+/* This is gonna seem *real weird*, but if you put some other code between
+ PyEval_EvalFrame() and PyEval_EvalCodeEx() you will need to adjust
+ the test in the if statements in Misc/gdbinit (pystack and pystackv). */
+
+PyObject *
+PyEval_EvalCodeEx(PyCodeObject *co, PyObject *globals, PyObject *locals,
+ PyObject **args, int argcount, PyObject **kws, int kwcount,
+ PyObject **defs, int defcount, PyObject *closure)
+{
+ register PyFrameObject *f;
+ register PyObject *retval = NULL;
+ register PyObject **fastlocals, **freevars;
+ PyThreadState *tstate = PyThreadState_GET();
+ PyObject *x, *u;
+
+ if (globals == NULL) {
+ PyErr_SetString(PyExc_SystemError,
+ "PyEval_EvalCodeEx: NULL globals");
+ return NULL;
+ }
+
+ assert(tstate != NULL);
+ assert(globals != NULL);
+ f = PyFrame_New(tstate, co, globals, locals);
+ if (f == NULL)
+ return NULL;
+
+ fastlocals = f->f_localsplus;
+ freevars = f->f_localsplus + co->co_nlocals;
+
+ if (co->co_argcount > 0 ||
+ co->co_flags & (CO_VARARGS | CO_VARKEYWORDS)) {
+ int i;
+ int n = argcount;
+ PyObject *kwdict = NULL;
+ if (co->co_flags & CO_VARKEYWORDS) {
+ kwdict = PyDict_New();
+ if (kwdict == NULL)
+ goto fail;
+ i = co->co_argcount;
+ if (co->co_flags & CO_VARARGS)
+ i++;
+ SETLOCAL(i, kwdict);
+ }
+ if (argcount > co->co_argcount) {
+ if (!(co->co_flags & CO_VARARGS)) {
+ PyErr_Format(PyExc_TypeError,
+ "%.200s() takes %s %d "
+ "%sargument%s (%d given)",
+ PyString_AsString(co->co_name),
+ defcount ? "at most" : "exactly",
+ co->co_argcount,
+ kwcount ? "non-keyword " : "",
+ co->co_argcount == 1 ? "" : "s",
+ argcount);
+ goto fail;
+ }
+ n = co->co_argcount;
+ }
+ for (i = 0; i < n; i++) {
+ x = args[i];
+ Py_INCREF(x);
+ SETLOCAL(i, x);
+ }
+ if (co->co_flags & CO_VARARGS) {
+ u = PyTuple_New(argcount - n);
+ if (u == NULL)
+ goto fail;
+ SETLOCAL(co->co_argcount, u);
+ for (i = n; i < argcount; i++) {
+ x = args[i];
+ Py_INCREF(x);
+ PyTuple_SET_ITEM(u, i-n, x);
+ }
+ }
+ for (i = 0; i < kwcount; i++) {
+ PyObject *keyword = kws[2*i];
+ PyObject *value = kws[2*i + 1];
+ int j;
+ if (keyword == NULL || !PyString_Check(keyword)) {
+ PyErr_Format(PyExc_TypeError,
+ "%.200s() keywords must be strings",
+ PyString_AsString(co->co_name));
+ goto fail;
+ }
+ /* XXX slow -- speed up using dictionary? */
+ for (j = 0; j < co->co_argcount; j++) {
+ PyObject *nm = PyTuple_GET_ITEM(
+ co->co_varnames, j);
+ int cmp = PyObject_RichCompareBool(
+ keyword, nm, Py_EQ);
+ if (cmp > 0)
+ break;
+ else if (cmp < 0)
+ goto fail;
+ }
+ /* Check errors from Compare */
+ if (PyErr_Occurred())
+ goto fail;
+ if (j >= co->co_argcount) {
+ if (kwdict == NULL) {
+ PyErr_Format(PyExc_TypeError,
+ "%.200s() got an unexpected "
+ "keyword argument '%.400s'",
+ PyString_AsString(co->co_name),
+ PyString_AsString(keyword));
+ goto fail;
+ }
+ PyDict_SetItem(kwdict, keyword, value);
+ }
+ else {
+ if (GETLOCAL(j) != NULL) {
+ PyErr_Format(PyExc_TypeError,
+ "%.200s() got multiple "
+ "values for keyword "
+ "argument '%.400s'",
+ PyString_AsString(co->co_name),
+ PyString_AsString(keyword));
+ goto fail;
+ }
+ Py_INCREF(value);
+ SETLOCAL(j, value);
+ }
+ }
+ if (argcount < co->co_argcount) {
+ int m = co->co_argcount - defcount;
+ for (i = argcount; i < m; i++) {
+ if (GETLOCAL(i) == NULL) {
+ PyErr_Format(PyExc_TypeError,
+ "%.200s() takes %s %d "
+ "%sargument%s (%d given)",
+ PyString_AsString(co->co_name),
+ ((co->co_flags & CO_VARARGS) ||
+ defcount) ? "at least"
+ : "exactly",
+ m, kwcount ? "non-keyword " : "",
+ m == 1 ? "" : "s", i);
+ goto fail;
+ }
+ }
+ if (n > m)
+ i = n - m;
+ else
+ i = 0;
+ for (; i < defcount; i++) {
+ if (GETLOCAL(m+i) == NULL) {
+ PyObject *def = defs[i];
+ Py_INCREF(def);
+ SETLOCAL(m+i, def);
+ }
+ }
+ }
+ }
+ else {
+ if (argcount > 0 || kwcount > 0) {
+ PyErr_Format(PyExc_TypeError,
+ "%.200s() takes no arguments (%d given)",
+ PyString_AsString(co->co_name),
+ argcount + kwcount);
+ goto fail;
+ }
+ }
+ /* Allocate and initialize storage for cell vars, and copy free
+ vars into frame. This isn't too efficient right now. */
+ if (PyTuple_GET_SIZE(co->co_cellvars)) {
+ int i, j, nargs, found;
+ char *cellname, *argname;
+ PyObject *c;
+
+ nargs = co->co_argcount;
+ if (co->co_flags & CO_VARARGS)
+ nargs++;
+ if (co->co_flags & CO_VARKEYWORDS)
+ nargs++;
+
+ /* Initialize each cell var, taking into account
+ cell vars that are initialized from arguments.
+
+ Should arrange for the compiler to put cellvars
+ that are arguments at the beginning of the cellvars
+ list so that we can march over it more efficiently?
+ */
+ for (i = 0; i < PyTuple_GET_SIZE(co->co_cellvars); ++i) {
+ cellname = PyString_AS_STRING(
+ PyTuple_GET_ITEM(co->co_cellvars, i));
+ found = 0;
+ for (j = 0; j < nargs; j++) {
+ argname = PyString_AS_STRING(
+ PyTuple_GET_ITEM(co->co_varnames, j));
+ if (strcmp(cellname, argname) == 0) {
+ c = PyCell_New(GETLOCAL(j));
+ if (c == NULL)
+ goto fail;
+ GETLOCAL(co->co_nlocals + i) = c;
+ found = 1;
+ break;
+ }
+ }
+ if (found == 0) {
+ c = PyCell_New(NULL);
+ if (c == NULL)
+ goto fail;
+ SETLOCAL(co->co_nlocals + i, c);
+ }
+ }
+ }
+ if (PyTuple_GET_SIZE(co->co_freevars)) {
+ int i;
+ for (i = 0; i < PyTuple_GET_SIZE(co->co_freevars); ++i) {
+ PyObject *o = PyTuple_GET_ITEM(closure, i);
+ Py_INCREF(o);
+ freevars[PyTuple_GET_SIZE(co->co_cellvars) + i] = o;
+ }
+ }
+
+ if (co->co_flags & CO_GENERATOR) {
+ /* Don't need to keep the reference to f_back, it will be set
+ * when the generator is resumed. */
+ Py_XDECREF(f->f_back);
+ f->f_back = NULL;
+
+ PCALL(PCALL_GENERATOR);
+
+ /* Create a new generator that owns the ready to run frame
+ * and return that as the value. */
+ return PyGen_New(f);
+ }
+
+ retval = PyEval_EvalFrameEx(f,0);
+
+ fail: /* Jump here from prelude on failure */
+
+ /* decref'ing the frame can cause __del__ methods to get invoked,
+ which can call back into Python. While we're done with the
+ current Python frame (f), the associated C stack is still in use,
+ so recursion_depth must be boosted for the duration.
+ */
+ assert(tstate != NULL);
+ ++tstate->recursion_depth;
+ Py_DECREF(f);
+ --tstate->recursion_depth;
+ return retval;
+}
+
+
+/* Implementation notes for set_exc_info() and reset_exc_info():
+
+- Below, 'exc_ZZZ' stands for 'exc_type', 'exc_value' and
+ 'exc_traceback'. These always travel together.
+
+- tstate->curexc_ZZZ is the "hot" exception that is set by
+ PyErr_SetString(), cleared by PyErr_Clear(), and so on.
+
+- Once an exception is caught by an except clause, it is transferred
+ from tstate->curexc_ZZZ to tstate->exc_ZZZ, from which sys.exc_info()
+ can pick it up. This is the primary task of set_exc_info().
+ XXX That can't be right: set_exc_info() doesn't look at tstate->curexc_ZZZ.
+
+- Now let me explain the complicated dance with frame->f_exc_ZZZ.
+
+ Long ago, when none of this existed, there were just a few globals:
+ one set corresponding to the "hot" exception, and one set
+ corresponding to sys.exc_ZZZ. (Actually, the latter weren't C
+ globals; they were simply stored as sys.exc_ZZZ. For backwards
+ compatibility, they still are!) The problem was that in code like
+ this:
+
+ try:
+ "something that may fail"
+ except "some exception":
+ "do something else first"
+ "print the exception from sys.exc_ZZZ."
+
+ if "do something else first" invoked something that raised and caught
+ an exception, sys.exc_ZZZ were overwritten. That was a frequent
+ cause of subtle bugs. I fixed this by changing the semantics as
+ follows:
+
+ - Within one frame, sys.exc_ZZZ will hold the last exception caught
+ *in that frame*.
+
+ - But initially, and as long as no exception is caught in a given
+ frame, sys.exc_ZZZ will hold the last exception caught in the
+ previous frame (or the frame before that, etc.).
+
+ The first bullet fixed the bug in the above example. The second
+ bullet was for backwards compatibility: it was (and is) common to
+ have a function that is called when an exception is caught, and to
+ have that function access the caught exception via sys.exc_ZZZ.
+ (Example: traceback.print_exc()).
+
+ At the same time I fixed the problem that sys.exc_ZZZ weren't
+ thread-safe, by introducing sys.exc_info() which gets it from tstate;
+ but that's really a separate improvement.
+
+ The reset_exc_info() function in ceval.c restores the tstate->exc_ZZZ
+ variables to what they were before the current frame was called. The
+ set_exc_info() function saves them on the frame so that
+ reset_exc_info() can restore them. The invariant is that
+ frame->f_exc_ZZZ is NULL iff the current frame never caught an
+ exception (where "catching" an exception applies only to successful
+ except clauses); and if the current frame ever caught an exception,
+ frame->f_exc_ZZZ is the exception that was stored in tstate->exc_ZZZ
+ at the start of the current frame.
+
+*/
+
+static void
+set_exc_info(PyThreadState *tstate,
+ PyObject *type, PyObject *value, PyObject *tb)
+{
+ PyFrameObject *frame = tstate->frame;
+ PyObject *tmp_type, *tmp_value, *tmp_tb;
+
+ assert(type != NULL);
+ assert(frame != NULL);
+ if (frame->f_exc_type == NULL) {
+ assert(frame->f_exc_value == NULL);
+ assert(frame->f_exc_traceback == NULL);
+ /* This frame didn't catch an exception before. */
+ /* Save previous exception of this thread in this frame. */
+ if (tstate->exc_type == NULL) {
+ /* XXX Why is this set to Py_None? */
+ Py_INCREF(Py_None);
+ tstate->exc_type = Py_None;
+ }
+ Py_INCREF(tstate->exc_type);
+ Py_XINCREF(tstate->exc_value);
+ Py_XINCREF(tstate->exc_traceback);
+ frame->f_exc_type = tstate->exc_type;
+ frame->f_exc_value = tstate->exc_value;
+ frame->f_exc_traceback = tstate->exc_traceback;
+ }
+ /* Set new exception for this thread. */
+ tmp_type = tstate->exc_type;
+ tmp_value = tstate->exc_value;
+ tmp_tb = tstate->exc_traceback;
+ Py_INCREF(type);
+ Py_XINCREF(value);
+ Py_XINCREF(tb);
+ tstate->exc_type = type;
+ tstate->exc_value = value;
+ tstate->exc_traceback = tb;
+ Py_XDECREF(tmp_type);
+ Py_XDECREF(tmp_value);
+ Py_XDECREF(tmp_tb);
+ /* For b/w compatibility */
+ PySys_SetObject("exc_type", type);
+ PySys_SetObject("exc_value", value);
+ PySys_SetObject("exc_traceback", tb);
+}
+
+static void
+reset_exc_info(PyThreadState *tstate)
+{
+ PyFrameObject *frame;
+ PyObject *tmp_type, *tmp_value, *tmp_tb;
+
+ /* It's a precondition that the thread state's frame caught an
+ * exception -- verify in a debug build.
+ */
+ assert(tstate != NULL);
+ frame = tstate->frame;
+ assert(frame != NULL);
+ assert(frame->f_exc_type != NULL);
+
+ /* Copy the frame's exception info back to the thread state. */
+ tmp_type = tstate->exc_type;
+ tmp_value = tstate->exc_value;
+ tmp_tb = tstate->exc_traceback;
+ Py_INCREF(frame->f_exc_type);
+ Py_XINCREF(frame->f_exc_value);
+ Py_XINCREF(frame->f_exc_traceback);
+ tstate->exc_type = frame->f_exc_type;
+ tstate->exc_value = frame->f_exc_value;
+ tstate->exc_traceback = frame->f_exc_traceback;
+ Py_XDECREF(tmp_type);
+ Py_XDECREF(tmp_value);
+ Py_XDECREF(tmp_tb);
+
+ /* For b/w compatibility */
+ PySys_SetObject("exc_type", frame->f_exc_type);
+ PySys_SetObject("exc_value", frame->f_exc_value);
+ PySys_SetObject("exc_traceback", frame->f_exc_traceback);
+
+ /* Clear the frame's exception info. */
+ tmp_type = frame->f_exc_type;
+ tmp_value = frame->f_exc_value;
+ tmp_tb = frame->f_exc_traceback;
+ frame->f_exc_type = NULL;
+ frame->f_exc_value = NULL;
+ frame->f_exc_traceback = NULL;
+ Py_DECREF(tmp_type);
+ Py_XDECREF(tmp_value);
+ Py_XDECREF(tmp_tb);
+}
+
+/* Logic for the raise statement (too complicated for inlining).
+ This *consumes* a reference count to each of its arguments. */
+static enum why_code
+do_raise(PyObject *type, PyObject *value, PyObject *tb)
+{
+ if (type == NULL) {
+ /* Reraise */
+ PyThreadState *tstate = PyThreadState_GET();
+ type = tstate->exc_type == NULL ? Py_None : tstate->exc_type;
+ value = tstate->exc_value;
+ tb = tstate->exc_traceback;
+ Py_XINCREF(type);
+ Py_XINCREF(value);
+ Py_XINCREF(tb);
+ }
+
+ /* We support the following forms of raise:
+ raise <class>, <classinstance>
+ raise <class>, <argument tuple>
+ raise <class>, None
+ raise <class>, <argument>
+ raise <classinstance>, None
+ raise <string>, <object>
+ raise <string>, None
+
+ An omitted second argument is the same as None.
+
+ In addition, raise <tuple>, <anything> is the same as
+ raising the tuple's first item (and it better have one!);
+ this rule is applied recursively.
+
+ Finally, an optional third argument can be supplied, which
+ gives the traceback to be substituted (useful when
+ re-raising an exception after examining it). */
+
+ /* First, check the traceback argument, replacing None with
+ NULL. */
+ if (tb == Py_None) {
+ Py_DECREF(tb);
+ tb = NULL;
+ }
+ else if (tb != NULL && !PyTraceBack_Check(tb)) {
+ PyErr_SetString(PyExc_TypeError,
+ "raise: arg 3 must be a traceback or None");
+ goto raise_error;
+ }
+
+ /* Next, replace a missing value with None */
+ if (value == NULL) {
+ value = Py_None;
+ Py_INCREF(value);
+ }
+
+ /* Next, repeatedly, replace a tuple exception with its first item */
+ while (PyTuple_Check(type) && PyTuple_Size(type) > 0) {
+ PyObject *tmp = type;
+ type = PyTuple_GET_ITEM(type, 0);
+ Py_INCREF(type);
+ Py_DECREF(tmp);
+ }
+
+ if (PyString_CheckExact(type)) {
+ /* Raising builtin string is deprecated but still allowed --
+ * do nothing. Raising an instance of a new-style str
+ * subclass is right out. */
+ if (PyErr_Warn(PyExc_DeprecationWarning,
+ "raising a string exception is deprecated"))
+ goto raise_error;
+ }
+ else if (PyExceptionClass_Check(type))
+ PyErr_NormalizeException(&type, &value, &tb);
+
+ else if (PyExceptionInstance_Check(type)) {
+ /* Raising an instance. The value should be a dummy. */
+ if (value != Py_None) {
+ PyErr_SetString(PyExc_TypeError,
+ "instance exception may not have a separate value");
+ goto raise_error;
+ }
+ else {
+ /* Normalize to raise <class>, <instance> */
+ Py_DECREF(value);
+ value = type;
+ type = PyExceptionInstance_Class(type);
+ Py_INCREF(type);
+ }
+ }
+ else {
+ /* Not something you can raise. You get an exception
+ anyway, just not what you specified :-) */
+ PyErr_Format(PyExc_TypeError,
+ "exceptions must be classes, instances, or "
+ "strings (deprecated), not %s",
+ type->ob_type->tp_name);
+ goto raise_error;
+ }
+ PyErr_Restore(type, value, tb);
+ if (tb == NULL)
+ return WHY_EXCEPTION;
+ else
+ return WHY_RERAISE;
+ raise_error:
+ Py_XDECREF(value);
+ Py_XDECREF(type);
+ Py_XDECREF(tb);
+ return WHY_EXCEPTION;
+}
+
+/* Iterate v argcnt times and store the results on the stack (via decreasing
+ sp). Return 1 for success, 0 if error. */
+
+static int
+unpack_iterable(PyObject *v, int argcnt, PyObject **sp)
+{
+ int i = 0;
+ PyObject *it; /* iter(v) */
+ PyObject *w;
+
+ assert(v != NULL);
+
+ it = PyObject_GetIter(v);
+ if (it == NULL)
+ goto Error;
+
+ for (; i < argcnt; i++) {
+ w = PyIter_Next(it);
+ if (w == NULL) {
+ /* Iterator done, via error or exhaustion. */
+ if (!PyErr_Occurred()) {
+ PyErr_Format(PyExc_ValueError,
+ "need more than %d value%s to unpack",
+ i, i == 1 ? "" : "s");
+ }
+ goto Error;
+ }
+ *--sp = w;
+ }
+
+ /* We better have exhausted the iterator now. */
+ w = PyIter_Next(it);
+ if (w == NULL) {
+ if (PyErr_Occurred())
+ goto Error;
+ Py_DECREF(it);
+ return 1;
+ }
+ Py_DECREF(w);
+ PyErr_SetString(PyExc_ValueError, "too many values to unpack");
+ /* fall through */
+Error:
+ for (; i > 0; i--, sp++)
+ Py_DECREF(*sp);
+ Py_XDECREF(it);
+ return 0;
+}
+
+
+#ifdef LLTRACE
+static int
+prtrace(PyObject *v, char *str)
+{
+ printf("%s ", str);
+ if (PyObject_Print(v, stdout, 0) != 0)
+ PyErr_Clear(); /* Don't know what else to do */
+ printf("\n");
+ return 1;
+}
+#endif
+
+static void
+call_exc_trace(Py_tracefunc func, PyObject *self, PyFrameObject *f)
+{
+ PyObject *type, *value, *traceback, *arg;
+ int err;
+ PyErr_Fetch(&type, &value, &traceback);
+ if (value == NULL) {
+ value = Py_None;
+ Py_INCREF(value);
+ }
+ arg = PyTuple_Pack(3, type, value, traceback);
+ if (arg == NULL) {
+ PyErr_Restore(type, value, traceback);
+ return;
+ }
+ err = call_trace(func, self, f, PyTrace_EXCEPTION, arg);
+ Py_DECREF(arg);
+ if (err == 0)
+ PyErr_Restore(type, value, traceback);
+ else {
+ Py_XDECREF(type);
+ Py_XDECREF(value);
+ Py_XDECREF(traceback);
+ }
+}
+
+static void
+call_trace_protected(Py_tracefunc func, PyObject *obj, PyFrameObject *frame,
+ int what, PyObject *arg)
+{
+ PyObject *type, *value, *traceback;
+ int err;
+ PyErr_Fetch(&type, &value, &traceback);
+ err = call_trace(func, obj, frame, what, arg);
+ if (err == 0)
+ PyErr_Restore(type, value, traceback);
+ else {
+ Py_XDECREF(type);
+ Py_XDECREF(value);
+ Py_XDECREF(traceback);
+ }
+}
+
+static int
+call_trace(Py_tracefunc func, PyObject *obj, PyFrameObject *frame,
+ int what, PyObject *arg)
+{
+ register PyThreadState *tstate = frame->f_tstate;
+ int result;
+ if (tstate->tracing)
+ return 0;
+ tstate->tracing++;
+ tstate->use_tracing = 0;
+ result = func(obj, frame, what, arg);
+ tstate->use_tracing = ((tstate->c_tracefunc != NULL)
+ || (tstate->c_profilefunc != NULL));
+ tstate->tracing--;
+ return result;
+}
+
+PyObject *
+_PyEval_CallTracing(PyObject *func, PyObject *args)
+{
+ PyFrameObject *frame = PyEval_GetFrame();
+ PyThreadState *tstate = frame->f_tstate;
+ int save_tracing = tstate->tracing;
+ int save_use_tracing = tstate->use_tracing;
+ PyObject *result;
+
+ tstate->tracing = 0;
+ tstate->use_tracing = ((tstate->c_tracefunc != NULL)
+ || (tstate->c_profilefunc != NULL));
+ result = PyObject_Call(func, args, NULL);
+ tstate->tracing = save_tracing;
+ tstate->use_tracing = save_use_tracing;
+ return result;
+}
+
+static int
+maybe_call_line_trace(Py_tracefunc func, PyObject *obj,
+ PyFrameObject *frame, int *instr_lb, int *instr_ub,
+ int *instr_prev)
+{
+ int result = 0;
+
+ /* If the last instruction executed isn't in the current
+ instruction window, reset the window. If the last
+ instruction happens to fall at the start of a line or if it
+ represents a jump backwards, call the trace function.
+ */
+ if ((frame->f_lasti < *instr_lb || frame->f_lasti >= *instr_ub)) {
+ int line;
+ PyAddrPair bounds;
+
+ line = PyCode_CheckLineNumber(frame->f_code, frame->f_lasti,
+ &bounds);
+ if (line >= 0) {
+ frame->f_lineno = line;
+ result = call_trace(func, obj, frame,
+ PyTrace_LINE, Py_None);
+ }
+ *instr_lb = bounds.ap_lower;
+ *instr_ub = bounds.ap_upper;
+ }
+ else if (frame->f_lasti <= *instr_prev) {
+ result = call_trace(func, obj, frame, PyTrace_LINE, Py_None);
+ }
+ *instr_prev = frame->f_lasti;
+ return result;
+}
+
+void
+PyEval_SetProfile(Py_tracefunc func, PyObject *arg)
+{
+ PyThreadState *tstate = PyThreadState_GET();
+ PyObject *temp = tstate->c_profileobj;
+ Py_XINCREF(arg);
+ tstate->c_profilefunc = NULL;
+ tstate->c_profileobj = NULL;
+ /* Must make sure that tracing is not ignored if 'temp' is freed */
+ tstate->use_tracing = tstate->c_tracefunc != NULL;
+ Py_XDECREF(temp);
+ tstate->c_profilefunc = func;
+ tstate->c_profileobj = arg;
+ /* Flag that tracing or profiling is turned on */
+ tstate->use_tracing = (func != NULL) || (tstate->c_tracefunc != NULL);
+}
+
+void
+PyEval_SetTrace(Py_tracefunc func, PyObject *arg)
+{
+ PyThreadState *tstate = PyThreadState_GET();
+ PyObject *temp = tstate->c_traceobj;
+ Py_XINCREF(arg);
+ tstate->c_tracefunc = NULL;
+ tstate->c_traceobj = NULL;
+ /* Must make sure that profiling is not ignored if 'temp' is freed */
+ tstate->use_tracing = tstate->c_profilefunc != NULL;
+ Py_XDECREF(temp);
+ tstate->c_tracefunc = func;
+ tstate->c_traceobj = arg;
+ /* Flag that tracing or profiling is turned on */
+ tstate->use_tracing = ((func != NULL)
+ || (tstate->c_profilefunc != NULL));
+}
+
+PyObject *
+PyEval_GetBuiltins(void)
+{
+ PyFrameObject *current_frame = PyEval_GetFrame();
+ if (current_frame == NULL)
+ return PyThreadState_GET()->interp->builtins;
+ else
+ return current_frame->f_builtins;
+}
+
+PyObject *
+PyEval_GetLocals(void)
+{
+ PyFrameObject *current_frame = PyEval_GetFrame();
+ if (current_frame == NULL)
+ return NULL;
+ PyFrame_FastToLocals(current_frame);
+ return current_frame->f_locals;
+}
+
+PyObject *
+PyEval_GetGlobals(void)
+{
+ PyFrameObject *current_frame = PyEval_GetFrame();
+ if (current_frame == NULL)
+ return NULL;
+ else
+ return current_frame->f_globals;
+}
+
+PyFrameObject *
+PyEval_GetFrame(void)
+{
+ PyThreadState *tstate = PyThreadState_GET();
+ return _PyThreadState_GetFrame(tstate);
+}
+
+int
+PyEval_GetRestricted(void)
+{
+ PyFrameObject *current_frame = PyEval_GetFrame();
+ return current_frame == NULL ? 0 : PyFrame_IsRestricted(current_frame);
+}
+
+int
+PyEval_MergeCompilerFlags(PyCompilerFlags *cf)
+{
+ PyFrameObject *current_frame = PyEval_GetFrame();
+ int result = cf->cf_flags != 0;
+
+ if (current_frame != NULL) {
+ const int codeflags = current_frame->f_code->co_flags;
+ const int compilerflags = codeflags & PyCF_MASK;
+ if (compilerflags) {
+ result = 1;
+ cf->cf_flags |= compilerflags;
+ }
+#if 0 /* future keyword */
+ if (codeflags & CO_GENERATOR_ALLOWED) {
+ result = 1;
+ cf->cf_flags |= CO_GENERATOR_ALLOWED;
+ }
+#endif
+ }
+ return result;
+}
+
+int
+Py_FlushLine(void)
+{
+ PyObject *f = PySys_GetObject("stdout");
+ if (f == NULL)
+ return 0;
+ if (!PyFile_SoftSpace(f, 0))
+ return 0;
+ return PyFile_WriteString("\n", f);
+}
+
+
+/* External interface to call any callable object.
+ The arg must be a tuple or NULL. */
+
+#undef PyEval_CallObject
+/* for backward compatibility: export this interface */
+
+PyObject *
+PyEval_CallObject(PyObject *func, PyObject *arg)
+{
+ return PyEval_CallObjectWithKeywords(func, arg, (PyObject *)NULL);
+}
+#define PyEval_CallObject(func,arg) \
+ PyEval_CallObjectWithKeywords(func, arg, (PyObject *)NULL)
+
+PyObject *
+PyEval_CallObjectWithKeywords(PyObject *func, PyObject *arg, PyObject *kw)
+{
+ PyObject *result;
+
+ if (arg == NULL) {
+ arg = PyTuple_New(0);
+ if (arg == NULL)
+ return NULL;
+ }
+ else if (!PyTuple_Check(arg)) {
+ PyErr_SetString(PyExc_TypeError,
+ "argument list must be a tuple");
+ return NULL;
+ }
+ else
+ Py_INCREF(arg);
+
+ if (kw != NULL && !PyDict_Check(kw)) {
+ PyErr_SetString(PyExc_TypeError,
+ "keyword list must be a dictionary");
+ Py_DECREF(arg);
+ return NULL;
+ }
+
+ result = PyObject_Call(func, arg, kw);
+ Py_DECREF(arg);
+ return result;
+}
+
+const char *
+PyEval_GetFuncName(PyObject *func)
+{
+ if (PyMethod_Check(func))
+ return PyEval_GetFuncName(PyMethod_GET_FUNCTION(func));
+ else if (PyFunction_Check(func))
+ return PyString_AsString(((PyFunctionObject*)func)->func_name);
+ else if (PyCFunction_Check(func))
+ return ((PyCFunctionObject*)func)->m_ml->ml_name;
+ else if (PyClass_Check(func))
+ return PyString_AsString(((PyClassObject*)func)->cl_name);
+ else if (PyInstance_Check(func)) {
+ return PyString_AsString(
+ ((PyInstanceObject*)func)->in_class->cl_name);
+ } else {
+ return func->ob_type->tp_name;
+ }
+}
+
+const char *
+PyEval_GetFuncDesc(PyObject *func)
+{
+ if (PyMethod_Check(func))
+ return "()";
+ else if (PyFunction_Check(func))
+ return "()";
+ else if (PyCFunction_Check(func))
+ return "()";
+ else if (PyClass_Check(func))
+ return " constructor";
+ else if (PyInstance_Check(func)) {
+ return " instance";
+ } else {
+ return " object";
+ }
+}
+
+static void
+err_args(PyObject *func, int flags, int nargs)
+{
+ if (flags & METH_NOARGS)
+ PyErr_Format(PyExc_TypeError,
+ "%.200s() takes no arguments (%d given)",
+ ((PyCFunctionObject *)func)->m_ml->ml_name,
+ nargs);
+ else
+ PyErr_Format(PyExc_TypeError,
+ "%.200s() takes exactly one argument (%d given)",
+ ((PyCFunctionObject *)func)->m_ml->ml_name,
+ nargs);
+}
+
+#define C_TRACE(x, call) \
+if (tstate->use_tracing && tstate->c_profilefunc) { \
+ if (call_trace(tstate->c_profilefunc, \
+ tstate->c_profileobj, \
+ tstate->frame, PyTrace_C_CALL, \
+ func)) { \
+ x = NULL; \
+ } \
+ else { \
+ x = call; \
+ if (tstate->c_profilefunc != NULL) { \
+ if (x == NULL) { \
+ call_trace_protected(tstate->c_profilefunc, \
+ tstate->c_profileobj, \
+ tstate->frame, PyTrace_C_EXCEPTION, \
+ func); \
+ /* XXX should pass (type, value, tb) */ \
+ } else { \
+ if (call_trace(tstate->c_profilefunc, \
+ tstate->c_profileobj, \
+ tstate->frame, PyTrace_C_RETURN, \
+ func)) { \
+ Py_DECREF(x); \
+ x = NULL; \
+ } \
+ } \
+ } \
+ } \
+} else { \
+ x = call; \
+ }
+
+static PyObject *
+call_function(PyObject ***pp_stack, int oparg
+#ifdef WITH_TSC
+ , uint64* pintr0, uint64* pintr1
+#endif
+ )
+{
+ int na = oparg & 0xff;
+ int nk = (oparg>>8) & 0xff;
+ int n = na + 2 * nk;
+ PyObject **pfunc = (*pp_stack) - n - 1;
+ PyObject *func = *pfunc;
+ PyObject *x, *w;
+
+ /* Always dispatch PyCFunction first, because these are
+ presumed to be the most frequent callable object.
+ */
+ if (PyCFunction_Check(func) && nk == 0) {
+ int flags = PyCFunction_GET_FLAGS(func);
+ PyThreadState *tstate = PyThreadState_GET();
+
+ PCALL(PCALL_CFUNCTION);
+ if (flags & (METH_NOARGS | METH_O)) {
+ PyCFunction meth = PyCFunction_GET_FUNCTION(func);
+ PyObject *self = PyCFunction_GET_SELF(func);
+ if (flags & METH_NOARGS && na == 0) {
+ C_TRACE(x, (*meth)(self,NULL));
+ }
+ else if (flags & METH_O && na == 1) {
+ PyObject *arg = EXT_POP(*pp_stack);
+ C_TRACE(x, (*meth)(self,arg));
+ Py_DECREF(arg);
+ }
+ else {
+ err_args(func, flags, na);
+ x = NULL;
+ }
+ }
+ else {
+ PyObject *callargs;
+ callargs = load_args(pp_stack, na);
+ READ_TIMESTAMP(*pintr0);
+ C_TRACE(x, PyCFunction_Call(func,callargs,NULL));
+ READ_TIMESTAMP(*pintr1);
+ Py_XDECREF(callargs);
+ }
+ } else {
+ if (PyMethod_Check(func) && PyMethod_GET_SELF(func) != NULL) {
+ /* optimize access to bound methods */
+ PyObject *self = PyMethod_GET_SELF(func);
+ PCALL(PCALL_METHOD);
+ PCALL(PCALL_BOUND_METHOD);
+ Py_INCREF(self);
+ func = PyMethod_GET_FUNCTION(func);
+ Py_INCREF(func);
+ Py_DECREF(*pfunc);
+ *pfunc = self;
+ na++;
+ n++;
+ } else
+ Py_INCREF(func);
+ READ_TIMESTAMP(*pintr0);
+ if (PyFunction_Check(func))
+ x = fast_function(func, pp_stack, n, na, nk);
+ else
+ x = do_call(func, pp_stack, na, nk);
+ READ_TIMESTAMP(*pintr1);
+ Py_DECREF(func);
+ }
+
+ /* Clear the stack of the function object. Also removes
+ the arguments in case they weren't consumed already
+ (fast_function() and err_args() leave them on the stack).
+ */
+ while ((*pp_stack) > pfunc) {
+ w = EXT_POP(*pp_stack);
+ Py_DECREF(w);
+ PCALL(PCALL_POP);
+ }
+ return x;
+}
+
+/* The fast_function() function optimize calls for which no argument
+ tuple is necessary; the objects are passed directly from the stack.
+ For the simplest case -- a function that takes only positional
+ arguments and is called with only positional arguments -- it
+ inlines the most primitive frame setup code from
+ PyEval_EvalCodeEx(), which vastly reduces the checks that must be
+ done before evaluating the frame.
+*/
+
+static PyObject *
+fast_function(PyObject *func, PyObject ***pp_stack, int n, int na, int nk)
+{
+ PyCodeObject *co = (PyCodeObject *)PyFunction_GET_CODE(func);
+ PyObject *globals = PyFunction_GET_GLOBALS(func);
+ PyObject *argdefs = PyFunction_GET_DEFAULTS(func);
+ PyObject **d = NULL;
+ int nd = 0;
+
+ PCALL(PCALL_FUNCTION);
+ PCALL(PCALL_FAST_FUNCTION);
+ if (argdefs == NULL && co->co_argcount == n && nk==0 &&
+ co->co_flags == (CO_OPTIMIZED | CO_NEWLOCALS | CO_NOFREE)) {
+ PyFrameObject *f;
+ PyObject *retval = NULL;
+ PyThreadState *tstate = PyThreadState_GET();
+ PyObject **fastlocals, **stack;
+ int i;
+
+ PCALL(PCALL_FASTER_FUNCTION);
+ assert(globals != NULL);
+ /* XXX Perhaps we should create a specialized
+ PyFrame_New() that doesn't take locals, but does
+ take builtins without sanity checking them.
+ */
+ assert(tstate != NULL);
+ f = PyFrame_New(tstate, co, globals, NULL);
+ if (f == NULL)
+ return NULL;
+
+ fastlocals = f->f_localsplus;
+ stack = (*pp_stack) - n;
+
+ for (i = 0; i < n; i++) {
+ Py_INCREF(*stack);
+ fastlocals[i] = *stack++;
+ }
+ retval = PyEval_EvalFrameEx(f,0);
+ ++tstate->recursion_depth;
+ Py_DECREF(f);
+ --tstate->recursion_depth;
+ return retval;
+ }
+ if (argdefs != NULL) {
+ d = &PyTuple_GET_ITEM(argdefs, 0);
+ nd = ((PyTupleObject *)argdefs)->ob_size;
+ }
+ return PyEval_EvalCodeEx(co, globals,
+ (PyObject *)NULL, (*pp_stack)-n, na,
+ (*pp_stack)-2*nk, nk, d, nd,
+ PyFunction_GET_CLOSURE(func));
+}
+
+static PyObject *
+update_keyword_args(PyObject *orig_kwdict, int nk, PyObject ***pp_stack,
+ PyObject *func)
+{
+ PyObject *kwdict = NULL;
+ if (orig_kwdict == NULL)
+ kwdict = PyDict_New();
+ else {
+ kwdict = PyDict_Copy(orig_kwdict);
+ Py_DECREF(orig_kwdict);
+ }
+ if (kwdict == NULL)
+ return NULL;
+ while (--nk >= 0) {
+ int err;
+ PyObject *value = EXT_POP(*pp_stack);
+ PyObject *key = EXT_POP(*pp_stack);
+ if (PyDict_GetItem(kwdict, key) != NULL) {
+ PyErr_Format(PyExc_TypeError,
+ "%.200s%s got multiple values "
+ "for keyword argument '%.200s'",
+ PyEval_GetFuncName(func),
+ PyEval_GetFuncDesc(func),
+ PyString_AsString(key));
+ Py_DECREF(key);
+ Py_DECREF(value);
+ Py_DECREF(kwdict);
+ return NULL;
+ }
+ err = PyDict_SetItem(kwdict, key, value);
+ Py_DECREF(key);
+ Py_DECREF(value);
+ if (err) {
+ Py_DECREF(kwdict);
+ return NULL;
+ }
+ }
+ return kwdict;
+}
+
+static PyObject *
+update_star_args(int nstack, int nstar, PyObject *stararg,
+ PyObject ***pp_stack)
+{
+ PyObject *callargs, *w;
+
+ callargs = PyTuple_New(nstack + nstar);
+ if (callargs == NULL) {
+ return NULL;
+ }
+ if (nstar) {
+ int i;
+ for (i = 0; i < nstar; i++) {
+ PyObject *a = PyTuple_GET_ITEM(stararg, i);
+ Py_INCREF(a);
+ PyTuple_SET_ITEM(callargs, nstack + i, a);
+ }
+ }
+ while (--nstack >= 0) {
+ w = EXT_POP(*pp_stack);
+ PyTuple_SET_ITEM(callargs, nstack, w);
+ }
+ return callargs;
+}
+
+static PyObject *
+load_args(PyObject ***pp_stack, int na)
+{
+ PyObject *args = PyTuple_New(na);
+ PyObject *w;
+
+ if (args == NULL)
+ return NULL;
+ while (--na >= 0) {
+ w = EXT_POP(*pp_stack);
+ PyTuple_SET_ITEM(args, na, w);
+ }
+ return args;
+}
+
+static PyObject *
+do_call(PyObject *func, PyObject ***pp_stack, int na, int nk)
+{
+ PyObject *callargs = NULL;
+ PyObject *kwdict = NULL;
+ PyObject *result = NULL;
+
+ if (nk > 0) {
+ kwdict = update_keyword_args(NULL, nk, pp_stack, func);
+ if (kwdict == NULL)
+ goto call_fail;
+ }
+ callargs = load_args(pp_stack, na);
+ if (callargs == NULL)
+ goto call_fail;
+#ifdef CALL_PROFILE
+ /* At this point, we have to look at the type of func to
+ update the call stats properly. Do it here so as to avoid
+ exposing the call stats machinery outside ceval.c
+ */
+ if (PyFunction_Check(func))
+ PCALL(PCALL_FUNCTION);
+ else if (PyMethod_Check(func))
+ PCALL(PCALL_METHOD);
+ else if (PyType_Check(func))
+ PCALL(PCALL_TYPE);
+ else
+ PCALL(PCALL_OTHER);
+#endif
+ result = PyObject_Call(func, callargs, kwdict);
+ call_fail:
+ Py_XDECREF(callargs);
+ Py_XDECREF(kwdict);
+ return result;
+}
+
+static PyObject *
+ext_do_call(PyObject *func, PyObject ***pp_stack, int flags, int na, int nk)
+{
+ int nstar = 0;
+ PyObject *callargs = NULL;
+ PyObject *stararg = NULL;
+ PyObject *kwdict = NULL;
+ PyObject *result = NULL;
+
+ if (flags & CALL_FLAG_KW) {
+ kwdict = EXT_POP(*pp_stack);
+ if (!(kwdict && PyDict_Check(kwdict))) {
+ PyErr_Format(PyExc_TypeError,
+ "%s%s argument after ** "
+ "must be a dictionary",
+ PyEval_GetFuncName(func),
+ PyEval_GetFuncDesc(func));
+ goto ext_call_fail;
+ }
+ }
+ if (flags & CALL_FLAG_VAR) {
+ stararg = EXT_POP(*pp_stack);
+ if (!PyTuple_Check(stararg)) {
+ PyObject *t = NULL;
+ t = PySequence_Tuple(stararg);
+ if (t == NULL) {
+ if (PyErr_ExceptionMatches(PyExc_TypeError)) {
+ PyErr_Format(PyExc_TypeError,
+ "%s%s argument after * "
+ "must be a sequence",
+ PyEval_GetFuncName(func),
+ PyEval_GetFuncDesc(func));
+ }
+ goto ext_call_fail;
+ }
+ Py_DECREF(stararg);
+ stararg = t;
+ }
+ nstar = PyTuple_GET_SIZE(stararg);
+ }
+ if (nk > 0) {
+ kwdict = update_keyword_args(kwdict, nk, pp_stack, func);
+ if (kwdict == NULL)
+ goto ext_call_fail;
+ }
+ callargs = update_star_args(na, nstar, stararg, pp_stack);
+ if (callargs == NULL)
+ goto ext_call_fail;
+#ifdef CALL_PROFILE
+ /* At this point, we have to look at the type of func to
+ update the call stats properly. Do it here so as to avoid
+ exposing the call stats machinery outside ceval.c
+ */
+ if (PyFunction_Check(func))
+ PCALL(PCALL_FUNCTION);
+ else if (PyMethod_Check(func))
+ PCALL(PCALL_METHOD);
+ else if (PyType_Check(func))
+ PCALL(PCALL_TYPE);
+ else
+ PCALL(PCALL_OTHER);
+#endif
+ result = PyObject_Call(func, callargs, kwdict);
+ ext_call_fail:
+ Py_XDECREF(callargs);
+ Py_XDECREF(kwdict);
+ Py_XDECREF(stararg);
+ return result;
+}
+
+/* Extract a slice index from a PyInt or PyLong or an object with the
+ nb_index slot defined, and store in *pi.
+ Silently reduce values larger than PY_SSIZE_T_MAX to PY_SSIZE_T_MAX,
+ and silently boost values less than -PY_SSIZE_T_MAX-1 to -PY_SSIZE_T_MAX-1.
+ Return 0 on error, 1 on success.
+*/
+/* Note: If v is NULL, return success without storing into *pi. This
+ is because_PyEval_SliceIndex() is called by apply_slice(), which can be
+ called by the SLICE opcode with v and/or w equal to NULL.
+*/
+int
+_PyEval_SliceIndex(PyObject *v, Py_ssize_t *pi)
+{
+ if (v != NULL) {
+ Py_ssize_t x;
+ if (PyInt_Check(v)) {
+ /* XXX(nnorwitz): I think PyInt_AS_LONG is correct,
+ however, it looks like it should be AsSsize_t.
+ There should be a comment here explaining why.
+ */
+ x = PyInt_AS_LONG(v);
+ }
+ else if (PyIndex_Check(v)) {
+ x = PyNumber_AsSsize_t(v, NULL);
+ if (x == -1 && PyErr_Occurred())
+ return 0;
+ }
+ else {
+ PyErr_SetString(PyExc_TypeError,
+ "slice indices must be integers or "
+ "None or have an __index__ method");
+ return 0;
+ }
+ *pi = x;
+ }
+ return 1;
+}
+
+#undef ISINDEX
+#define ISINDEX(x) ((x) == NULL || \
+ PyInt_Check(x) || PyLong_Check(x) || PyIndex_Check(x))
+
+static PyObject *
+apply_slice(PyObject *u, PyObject *v, PyObject *w) /* return u[v:w] */
+{
+ PyTypeObject *tp = u->ob_type;
+ PySequenceMethods *sq = tp->tp_as_sequence;
+
+ if (sq && sq->sq_slice && ISINDEX(v) && ISINDEX(w)) {
+ Py_ssize_t ilow = 0, ihigh = PY_SSIZE_T_MAX;
+ if (!_PyEval_SliceIndex(v, &ilow))
+ return NULL;
+ if (!_PyEval_SliceIndex(w, &ihigh))
+ return NULL;
+ return PySequence_GetSlice(u, ilow, ihigh);
+ }
+ else {
+ PyObject *slice = PySlice_New(v, w, NULL);
+ if (slice != NULL) {
+ PyObject *res = PyObject_GetItem(u, slice);
+ Py_DECREF(slice);
+ return res;
+ }
+ else
+ return NULL;
+ }
+}
+
+static int
+assign_slice(PyObject *u, PyObject *v, PyObject *w, PyObject *x)
+ /* u[v:w] = x */
+{
+ PyTypeObject *tp = u->ob_type;
+ PySequenceMethods *sq = tp->tp_as_sequence;
+
+ if (sq && sq->sq_slice && ISINDEX(v) && ISINDEX(w)) {
+ Py_ssize_t ilow = 0, ihigh = PY_SSIZE_T_MAX;
+ if (!_PyEval_SliceIndex(v, &ilow))
+ return -1;
+ if (!_PyEval_SliceIndex(w, &ihigh))
+ return -1;
+ if (x == NULL)
+ return PySequence_DelSlice(u, ilow, ihigh);
+ else
+ return PySequence_SetSlice(u, ilow, ihigh, x);
+ }
+ else {
+ PyObject *slice = PySlice_New(v, w, NULL);
+ if (slice != NULL) {
+ int res;
+ if (x != NULL)
+ res = PyObject_SetItem(u, slice, x);
+ else
+ res = PyObject_DelItem(u, slice);
+ Py_DECREF(slice);
+ return res;
+ }
+ else
+ return -1;
+ }
+}
+
+static PyObject *
+cmp_outcome(int op, register PyObject *v, register PyObject *w)
+{
+ int res = 0;
+ switch (op) {
+ case PyCmp_IS:
+ res = (v == w);
+ break;
+ case PyCmp_IS_NOT:
+ res = (v != w);
+ break;
+ case PyCmp_IN:
+ res = PySequence_Contains(w, v);
+ if (res < 0)
+ return NULL;
+ break;
+ case PyCmp_NOT_IN:
+ res = PySequence_Contains(w, v);
+ if (res < 0)
+ return NULL;
+ res = !res;
+ break;
+ case PyCmp_EXC_MATCH:
+ res = PyErr_GivenExceptionMatches(v, w);
+ break;
+ default:
+ return PyObject_RichCompare(v, w, op);
+ }
+ v = res ? Py_True : Py_False;
+ Py_INCREF(v);
+ return v;
+}
+
+static PyObject *
+import_from(PyObject *v, PyObject *name)
+{
+ PyObject *x;
+
+ x = PyObject_GetAttr(v, name);
+ if (x == NULL && PyErr_ExceptionMatches(PyExc_AttributeError)) {
+ PyErr_Format(PyExc_ImportError,
+ "cannot import name %.230s",
+ PyString_AsString(name));
+ }
+ return x;
+}
+
+static int
+import_all_from(PyObject *locals, PyObject *v)
+{
+ PyObject *all = PyObject_GetAttrString(v, "__all__");
+ PyObject *dict, *name, *value;
+ int skip_leading_underscores = 0;
+ int pos, err;
+
+ if (all == NULL) {
+ if (!PyErr_ExceptionMatches(PyExc_AttributeError))
+ return -1; /* Unexpected error */
+ PyErr_Clear();
+ dict = PyObject_GetAttrString(v, "__dict__");
+ if (dict == NULL) {
+ if (!PyErr_ExceptionMatches(PyExc_AttributeError))
+ return -1;
+ PyErr_SetString(PyExc_ImportError,
+ "from-import-* object has no __dict__ and no __all__");
+ return -1;
+ }
+ all = PyMapping_Keys(dict);
+ Py_DECREF(dict);
+ if (all == NULL)
+ return -1;
+ skip_leading_underscores = 1;
+ }
+
+ for (pos = 0, err = 0; ; pos++) {
+ name = PySequence_GetItem(all, pos);
+ if (name == NULL) {
+ if (!PyErr_ExceptionMatches(PyExc_IndexError))
+ err = -1;
+ else
+ PyErr_Clear();
+ break;
+ }
+ if (skip_leading_underscores &&
+ PyString_Check(name) &&
+ PyString_AS_STRING(name)[0] == '_')
+ {
+ Py_DECREF(name);
+ continue;
+ }
+ value = PyObject_GetAttr(v, name);
+ if (value == NULL)
+ err = -1;
+ else
+ err = PyDict_SetItem(locals, name, value);
+ Py_DECREF(name);
+ Py_XDECREF(value);
+ if (err != 0)
+ break;
+ }
+ Py_DECREF(all);
+ return err;
+}
+
+static PyObject *
+build_class(PyObject *methods, PyObject *bases, PyObject *name)
+{
+ PyObject *metaclass = NULL, *result, *base;
+
+ if (PyDict_Check(methods))
+ metaclass = PyDict_GetItemString(methods, "__metaclass__");
+ if (metaclass != NULL)
+ Py_INCREF(metaclass);
+ else if (PyTuple_Check(bases) && PyTuple_GET_SIZE(bases) > 0) {
+ base = PyTuple_GET_ITEM(bases, 0);
+ metaclass = PyObject_GetAttrString(base, "__class__");
+ if (metaclass == NULL) {
+ PyErr_Clear();
+ metaclass = (PyObject *)base->ob_type;
+ Py_INCREF(metaclass);
+ }
+ }
+ else {
+ PyObject *g = PyEval_GetGlobals();
+ if (g != NULL && PyDict_Check(g))
+ metaclass = PyDict_GetItemString(g, "__metaclass__");
+ if (metaclass == NULL)
+ metaclass = (PyObject *) &PyClass_Type;
+ Py_INCREF(metaclass);
+ }
+ result = PyObject_CallFunctionObjArgs(metaclass, name, bases, methods, NULL);
+ Py_DECREF(metaclass);
+ if (result == NULL && PyErr_ExceptionMatches(PyExc_TypeError)) {
+ /* A type error here likely means that the user passed
+ in a base that was not a class (such the random module
+ instead of the random.random type). Help them out with
+ by augmenting the error message with more information.*/
+
+ PyObject *ptype, *pvalue, *ptraceback;
+
+ PyErr_Fetch(&ptype, &pvalue, &ptraceback);
+ if (PyString_Check(pvalue)) {
+ PyObject *newmsg;
+ newmsg = PyString_FromFormat(
+ "Error when calling the metaclass bases\n %s",
+ PyString_AS_STRING(pvalue));
+ if (newmsg != NULL) {
+ Py_DECREF(pvalue);
+ pvalue = newmsg;
+ }
+ }
+ PyErr_Restore(ptype, pvalue, ptraceback);
+ }
+ return result;
+}
+
+static int
+exec_statement(PyFrameObject *f, PyObject *prog, PyObject *globals,
+ PyObject *locals)
+{
+ int n;
+ PyObject *v;
+ int plain = 0;
+
+ if (PyTuple_Check(prog) && globals == Py_None && locals == Py_None &&
+ ((n = PyTuple_Size(prog)) == 2 || n == 3)) {
+ /* Backward compatibility hack */
+ globals = PyTuple_GetItem(prog, 1);
+ if (n == 3)
+ locals = PyTuple_GetItem(prog, 2);
+ prog = PyTuple_GetItem(prog, 0);
+ }
+ if (globals == Py_None) {
+ globals = PyEval_GetGlobals();
+ if (locals == Py_None) {
+ locals = PyEval_GetLocals();
+ plain = 1;
+ }
+ if (!globals || !locals) {
+ PyErr_SetString(PyExc_SystemError,
+ "globals and locals cannot be NULL");
+ return -1;
+ }
+ }
+ else if (locals == Py_None)
+ locals = globals;
+ if (!PyString_Check(prog) &&
+ !PyUnicode_Check(prog) &&
+ !PyCode_Check(prog) &&
+ !PyFile_Check(prog)) {
+ PyErr_SetString(PyExc_TypeError,
+ "exec: arg 1 must be a string, file, or code object");
+ return -1;
+ }
+ if (!PyDict_Check(globals)) {
+ PyErr_SetString(PyExc_TypeError,
+ "exec: arg 2 must be a dictionary or None");
+ return -1;
+ }
+ if (!PyMapping_Check(locals)) {
+ PyErr_SetString(PyExc_TypeError,
+ "exec: arg 3 must be a mapping or None");
+ return -1;
+ }
+ if (PyDict_GetItemString(globals, "__builtins__") == NULL)
+ PyDict_SetItemString(globals, "__builtins__", f->f_builtins);
+ if (PyCode_Check(prog)) {
+ if (PyCode_GetNumFree((PyCodeObject *)prog) > 0) {
+ PyErr_SetString(PyExc_TypeError,
+ "code object passed to exec may not contain free variables");
+ return -1;
+ }
+ v = PyEval_EvalCode((PyCodeObject *) prog, globals, locals);
+ }
+ else if (PyFile_Check(prog)) {
+ FILE *fp = PyFile_AsFile(prog);
+ char *name = PyString_AsString(PyFile_Name(prog));
+ PyCompilerFlags cf;
+ cf.cf_flags = 0;
+ if (PyEval_MergeCompilerFlags(&cf))
+ v = PyRun_FileFlags(fp, name, Py_file_input, globals,
+ locals, &cf);
+ else
+ v = PyRun_File(fp, name, Py_file_input, globals,
+ locals);
+ }
+ else {
+ PyObject *tmp = NULL;
+ char *str;
+ PyCompilerFlags cf;
+ cf.cf_flags = 0;
+#ifdef Py_USING_UNICODE
+ if (PyUnicode_Check(prog)) {
+ tmp = PyUnicode_AsUTF8String(prog);
+ if (tmp == NULL)
+ return -1;
+ prog = tmp;
+ cf.cf_flags |= PyCF_SOURCE_IS_UTF8;
+ }
+#endif
+ if (PyString_AsStringAndSize(prog, &str, NULL))
+ return -1;
+ if (PyEval_MergeCompilerFlags(&cf))
+ v = PyRun_StringFlags(str, Py_file_input, globals,
+ locals, &cf);
+ else
+ v = PyRun_String(str, Py_file_input, globals, locals);
+ Py_XDECREF(tmp);
+ }
+ if (plain)
+ PyFrame_LocalsToFast(f, 0);
+ if (v == NULL)
+ return -1;
+ Py_DECREF(v);
+ return 0;
+}
+
+static void
+format_exc_check_arg(PyObject *exc, char *format_str, PyObject *obj)
+{
+ char *obj_str;
+
+ if (!obj)
+ return;
+
+ obj_str = PyString_AsString(obj);
+ if (!obj_str)
+ return;
+
+ PyErr_Format(exc, format_str, obj_str);
+}
+
+static PyObject *
+string_concatenate(PyObject *v, PyObject *w,
+ PyFrameObject *f, unsigned char *next_instr)
+{
+ /* This function implements 'variable += expr' when both arguments
+ are strings. */
+ Py_ssize_t v_len = PyString_GET_SIZE(v);
+ Py_ssize_t w_len = PyString_GET_SIZE(w);
+ Py_ssize_t new_len = v_len + w_len;
+ if (new_len < 0) {
+ PyErr_SetString(PyExc_OverflowError,
+ "strings are too large to concat");
+ return NULL;
+ }
+
+ if (v->ob_refcnt == 2) {
+ /* In the common case, there are 2 references to the value
+ * stored in 'variable' when the += is performed: one on the
+ * value stack (in 'v') and one still stored in the 'variable'.
+ * We try to delete the variable now to reduce the refcnt to 1.
+ */
+ switch (*next_instr) {
+ case STORE_FAST:
+ {
+ int oparg = PEEKARG();
+ PyObject **fastlocals = f->f_localsplus;
+ if (GETLOCAL(oparg) == v)
+ SETLOCAL(oparg, NULL);
+ break;
+ }
+ case STORE_DEREF:
+ {
+ PyObject **freevars = f->f_localsplus + f->f_code->co_nlocals;
+ PyObject *c = freevars[PEEKARG()];
+ if (PyCell_GET(c) == v)
+ PyCell_Set(c, NULL);
+ break;
+ }
+ case STORE_NAME:
+ {
+ PyObject *names = f->f_code->co_names;
+ PyObject *name = GETITEM(names, PEEKARG());
+ PyObject *locals = f->f_locals;
+ if (PyDict_CheckExact(locals) &&
+ PyDict_GetItem(locals, name) == v) {
+ if (PyDict_DelItem(locals, name) != 0) {
+ PyErr_Clear();
+ }
+ }
+ break;
+ }
+ }
+ }
+
+ if (v->ob_refcnt == 1 && !PyString_CHECK_INTERNED(v)) {
+ /* Now we own the last reference to 'v', so we can resize it
+ * in-place.
+ */
+ if (_PyString_Resize(&v, new_len) != 0) {
+ /* XXX if _PyString_Resize() fails, 'v' has been
+ * deallocated so it cannot be put back into 'variable'.
+ * The MemoryError is raised when there is no value in
+ * 'variable', which might (very remotely) be a cause
+ * of incompatibilities.
+ */
+ return NULL;
+ }
+ /* copy 'w' into the newly allocated area of 'v' */
+ memcpy(PyString_AS_STRING(v) + v_len,
+ PyString_AS_STRING(w), w_len);
+ return v;
+ }
+ else {
+ /* When in-place resizing is not an option. */
+ PyString_Concat(&v, w);
+ return v;
+ }
+}
+
+#ifdef DYNAMIC_EXECUTION_PROFILE
+
+static PyObject *
+getarray(long a[256])
+{
+ int i;
+ PyObject *l = PyList_New(256);
+ if (l == NULL) return NULL;
+ for (i = 0; i < 256; i++) {
+ PyObject *x = PyInt_FromLong(a[i]);
+ if (x == NULL) {
+ Py_DECREF(l);
+ return NULL;
+ }
+ PyList_SetItem(l, i, x);
+ }
+ for (i = 0; i < 256; i++)
+ a[i] = 0;
+ return l;
+}
+
+PyObject *
+_Py_GetDXProfile(PyObject *self, PyObject *args)
+{
+#ifndef DXPAIRS
+ return getarray(dxp);
+#else
+ int i;
+ PyObject *l = PyList_New(257);
+ if (l == NULL) return NULL;
+ for (i = 0; i < 257; i++) {
+ PyObject *x = getarray(dxpairs[i]);
+ if (x == NULL) {
+ Py_DECREF(l);
+ return NULL;
+ }
+ PyList_SetItem(l, i, x);
+ }
+ return l;
+#endif
+}
+
+#endif
diff --git a/tests/examplefiles/django_sample.html b/tests/examplefiles/django_sample.html
new file mode 100644
index 00000000..b1fdc5eb
--- /dev/null
+++ b/tests/examplefiles/django_sample.html
@@ -0,0 +1,68 @@
+{% extends "admin/base_site.html" %}
+{% load i18n admin_modify adminmedia %}
+{% block extrahead %}{{ block.super }}
+<script type="text/javascript" src="../../../jsi18n/"></script>
+{% for js in javascript_imports %}{% include_admin_script js %}{% endfor %}
+{% endblock %}
+{% block stylesheet %}{% admin_media_prefix %}css/forms.css{% endblock %}
+{% block coltype %}{% if ordered_objects %}colMS{% else %}colM{% endif %}{% endblock %}
+{% block bodyclass %}{{ opts.app_label }}-{{ opts.object_name.lower }} change-form{% endblock %}
+{% block userlinks %}<a href="../../../doc/">{% trans 'Documentation' %}</a> / <a href="../../../password_change/">{% trans 'Change password' %}</a> / <a href="../../../logout/">{% trans 'Log out' %}</a>{% endblock %}
+{% block breadcrumbs %}{% if not is_popup %}
+<div class="breadcrumbs">
+ <a href="../../../">{% trans "Home" %}</a> &rsaquo;
+ <a href="../">{{ opts.verbose_name_plural|capfirst|escape }}</a> &rsaquo;
+ {% if add %}{% trans "Add" %} {{ opts.verbose_name|escape }}{% else %}{{ original|truncatewords:"18"|escape }}{% endif %}
+</div>
+{% endif %}{% endblock %}
+{% block content %}<div id="content-main">
+{% if change %}{% if not is_popup %}
+ <ul class="object-tools"><li><a href="history/" class="historylink">{% trans "History" %}</a></li>
+ {% if has_absolute_url %}<li><a href="../../../r/{{ content_type_id }}/{{ object_id }}/" class="viewsitelink">{% trans "View on site" %}</a></li>{% endif%}
+ </ul>
+{% endif %}{% endif %}
+<form {% if has_file_field %}enctype="multipart/form-data" {% endif %}action="{{ form_url }}" method="post" id="{{ opts.module_name }}_form">{% block form_top %}{% endblock %}
+<div>
+{% if is_popup %}<input type="hidden" name="_popup" value="1" />{% endif %}
+{% if opts.admin.save_on_top %}{% submit_row %}{% endif %}
+{% if form.error_dict %}
+ <p class="errornote">
+ {% blocktrans count form.error_dict.items|length as counter %}Please correct the error below.{% plural %}Please correct the errors below.{% endblocktrans %}
+ </p>
+{% endif %}
+{% for bound_field_set in bound_field_sets %}
+ <fieldset class="module aligned {{ bound_field_set.classes }}">
+ {% if bound_field_set.name %}<h2>{{ bound_field_set.name }}</h2>{% endif %}
+ {% if bound_field_set.description %}<div class="description">{{ bound_field_set.description }}</div>{% endif %}
+ {% for bound_field_line in bound_field_set %}
+ {% admin_field_line bound_field_line %}
+ {% for bound_field in bound_field_line %}
+ {% filter_interface_script_maybe bound_field %}
+ {% endfor %}
+ {% endfor %}
+ </fieldset>
+{% endfor %}
+{% block after_field_sets %}{% endblock %}
+{% if change %}
+ {% if ordered_objects %}
+ <fieldset class="module"><h2>{% trans "Ordering" %}</h2>
+ <div class="form-row{% if form.order_.errors %} error{% endif %} ">
+ {% if form.order_.errors %}{{ form.order_.html_error_list }}{% endif %}
+ <p><label for="id_order_">{% trans "Order:" %}</label> {{ form.order_ }}</p>
+ </div></fieldset>
+ {% endif %}
+{% endif %}
+{% for related_object in inline_related_objects %}{% edit_inline related_object %}{% endfor %}
+{% block after_related_objects %}{% endblock %}
+{% submit_row %}
+{% if add %}
+ <script type="text/javascript">document.getElementById("{{ first_form_field_id }}").focus();</script>
+{% endif %}
+{% if auto_populated_fields %}
+ <script type="text/javascript">
+ {% auto_populated_field_script auto_populated_fields change %}
+ </script>
+{% endif %}
+</div>
+</form></div>
+{% endblock %}
diff --git a/tests/examplefiles/example.c b/tests/examplefiles/example.c
new file mode 100644
index 00000000..bbd1a75c
--- /dev/null
+++ b/tests/examplefiles/example.c
@@ -0,0 +1,29989 @@
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include "codegen.h"
+#include "symboltable.h"
+#include "stringbuffer.h"
+
+extern void yyerror(char* msg);
+
+static stringBuffer* staticVariableBuffer;
+static stringBuffer* classInitBuffer;
+static stringBuffer* currentMethodBuffer;
+static stringBuffer* finishedMethodsBuffer;
+static stringBuffer* mainBuffer;
+
+static int currentMethodBufferIndex;
+static int currentMethodStackSize;
+static int currentMethodStackSizeMax;
+static int currentMethodNumberOfLocals;
+
+static int classInitBufferIndex;
+static int classInitStackSize;
+static int classInitStackSizeMax;
+
+static int labelCounter = 0;
+static int global = 1;
+
+char tempString[MAX_LENGTH_OF_COMMAND];
+
+extern char* className; /* from minako-syntax.y */
+
+/* forward declarations */
+static void increaseStackby(int stackdiff);
+char convertType(int type);
+
+void codegenInit() {
+ staticVariableBuffer = newStringBuffer();
+ classInitBuffer = newStringBuffer();
+ currentMethodBuffer = 0;
+ finishedMethodsBuffer = newStringBuffer();
+ mainBuffer = newStringBuffer();
+
+ stringBufferAppend(mainBuffer, "; ------- Header --------------------------------------------");
+ sprintf(tempString, ".class public synchronized %s", className);
+ stringBufferAppend(mainBuffer, tempString);
+ stringBufferAppend(mainBuffer, ".super java/lang/Object");
+ stringBufferAppend(mainBuffer, "; -----------------------------------------------------------");
+ stringBufferAppend(mainBuffer, "");
+
+ stringBufferAppend(finishedMethodsBuffer, "; ------- Constructor ---------------------------------------");
+ stringBufferAppend(finishedMethodsBuffer, ".method public <init>()V");
+ stringBufferAppend(finishedMethodsBuffer, "\t.limit stack 1");
+ stringBufferAppend(finishedMethodsBuffer, "\t.limit locals 1");
+ stringBufferAppend(finishedMethodsBuffer, "\taload_0");
+ stringBufferAppend(finishedMethodsBuffer, "\tinvokenonvirtual java/lang/Object/<init>()V");
+ stringBufferAppend(finishedMethodsBuffer, "\treturn");
+ stringBufferAppend(finishedMethodsBuffer, ".end method");
+ stringBufferAppend(finishedMethodsBuffer, "; -----------------------------------------------------------");
+ stringBufferAppend(finishedMethodsBuffer, "");
+
+ stringBufferAppend(staticVariableBuffer, "; ------- Class Variables -----------------------------------");
+
+ stringBufferAppend(classInitBuffer, "; ------- Class Initializer ---------------------------------");
+ stringBufferAppend(classInitBuffer, ".method static <clinit>()V");
+ classInitBufferIndex = classInitBuffer->numberOfNextElement;
+ stringBufferAppend(classInitBuffer, "\t.limit locals 0");
+
+}
+
+void codegenAppendCommand(char* cmd, int stackdiff) {
+ char tempString[MAX_LENGTH_OF_COMMAND];
+ sprintf(tempString, "\t%s", cmd);
+ if (global) stringBufferAppend(classInitBuffer, tempString);
+ else stringBufferAppend(currentMethodBuffer, tempString);
+ increaseStackby(stackdiff);
+}
+
+void codegenInsertCommand(int address, char* cmd, int stackdiff) {
+ char tempString[MAX_LENGTH_OF_COMMAND];
+ sprintf(tempString, "\t%s", cmd);
+ if (global) stringBufferInsert(classInitBuffer, address, tempString);
+ else stringBufferInsert(currentMethodBuffer, address, tempString);
+ increaseStackby(stackdiff);
+}
+
+void codegenAppendLabel(int label) {
+ char tempString[MAX_LENGTH_OF_COMMAND];
+ sprintf(tempString, "Label%d:", label);
+ if (global) stringBufferAppend(classInitBuffer, tempString);
+ else stringBufferAppend(currentMethodBuffer, tempString);
+}
+
+void codegenAddVariable(char* name, int type) {
+ /*fprintf(stderr, "add variable %s(%d) global=%d ", name, convertType(type), global);*/
+ if (global) {
+ if (type == TYPE_INT) sprintf(tempString, ".field static %s %c", name, 'I');
+ else if (type == TYPE_FLOAT) sprintf(tempString, ".field static %s %c", name, 'F');
+ else if (type == TYPE_BOOLEAN) sprintf(tempString, ".field static %s %c", name, 'Z');
+ else yyerror("compiler-intern error in codegenAddGlobalVariable().\n");
+ stringBufferAppend(staticVariableBuffer, tempString);
+ }
+ else {
+ currentMethodNumberOfLocals++;
+ }
+}
+
+int codegenGetNextLabel() {
+ return labelCounter++;
+}
+
+int codegenGetCurrentAddress() {
+ if (global) return classInitBuffer->numberOfNextElement;
+ else return currentMethodBuffer->numberOfNextElement;
+}
+
+void codegenEnterFunction(symtabEntry* entry) {
+ currentMethodBuffer = newStringBuffer();
+ currentMethodStackSize = 0;
+ currentMethodStackSizeMax = 0;
+ labelCounter = 1;
+ global = 0;
+
+ if (strcmp(entry->name, "main") == 0) {
+ if (entry->idtype != TYPE_VOID) yyerror("main has to be void.\n");
+ currentMethodNumberOfLocals = 1;
+ symtabInsert(strdup("#main-param#"), TYPE_VOID, CLASS_FUNC);
+ stringBufferAppend(currentMethodBuffer, "; ------- Methode ---- void main() --------------------------");
+ stringBufferAppend(currentMethodBuffer, ".method public static main([Ljava/lang/String;)V");
+ }
+ else {
+ int i;
+ currentMethodNumberOfLocals = entry->paramIndex;
+ stringBufferAppend(currentMethodBuffer, "; ------- Methode -------------------------------------------");
+ sprintf(tempString, ".method public static %s(", entry->name);
+ for (i=entry->paramIndex-1; i>=0; i--) {
+ int type = entry->params[i]->idtype;
+ tempString[strlen(tempString)+1] = 0;
+ tempString[strlen(tempString)] = convertType(type);
+ }
+ tempString[strlen(tempString)+2] = 0;
+ tempString[strlen(tempString)+1] = convertType(entry->idtype);
+ tempString[strlen(tempString)] = ')';
+ stringBufferAppend(currentMethodBuffer, tempString);
+ }
+ currentMethodBufferIndex = currentMethodBuffer->numberOfNextElement;
+}
+
+void codegenLeaveFunction() {
+ global = 1;
+ sprintf(tempString, "\t.limit locals %d", currentMethodNumberOfLocals);
+ stringBufferInsert(currentMethodBuffer, currentMethodBufferIndex, tempString);
+ sprintf(tempString, "\t.limit stack %d", currentMethodStackSizeMax);
+ stringBufferInsert(currentMethodBuffer, currentMethodBufferIndex, tempString);
+ stringBufferAppend(currentMethodBuffer, "\treturn");
+ stringBufferAppend(currentMethodBuffer, ".end method");
+ stringBufferAppend(currentMethodBuffer, "; -----------------------------------------------------------");
+ stringBufferAppend(currentMethodBuffer, "");
+
+ stringBufferConcatenate(finishedMethodsBuffer, currentMethodBuffer);
+}
+
+
+
+void codegenFinishCode() {
+ stringBufferAppend(staticVariableBuffer, "; -----------------------------------------------------------");
+ stringBufferAppend(staticVariableBuffer, "");
+
+ sprintf(tempString, "\t.limit stack %d", classInitStackSizeMax);
+ stringBufferInsert(classInitBuffer, classInitBufferIndex, tempString);
+ stringBufferAppend(classInitBuffer, "\treturn");
+ stringBufferAppend(classInitBuffer, ".end method");
+ stringBufferAppend(classInitBuffer, "; -----------------------------------------------------------");
+
+ stringBufferConcatenate(mainBuffer, staticVariableBuffer);
+ stringBufferConcatenate(mainBuffer, finishedMethodsBuffer);
+ stringBufferConcatenate(mainBuffer, classInitBuffer);
+
+ stringBufferPrint(mainBuffer);
+}
+
+static void increaseStackby(int stackdiff) {
+ if (global) {
+ classInitStackSize += stackdiff;
+ if (classInitStackSize > classInitStackSizeMax) classInitStackSizeMax = classInitStackSize;
+ }
+ else {
+ currentMethodStackSize += stackdiff;
+ if (currentMethodStackSize > currentMethodStackSizeMax) currentMethodStackSizeMax = currentMethodStackSize;
+ }
+}
+
+char convertType(int type) {
+ switch(type) {
+ case TYPE_VOID: return 'V';
+ case TYPE_INT: return 'I';
+ case TYPE_FLOAT: return 'F';
+ case TYPE_BOOLEAN: return 'Z';
+ default: yyerror("compiler-intern error in convertType().\n");
+ }
+ return 0; /* to avoid compiler-warning */
+}
+
+
+//#include <stdlib.h>
+//#include <stdio.h>
+
+int main() {
+ int a = 12, b = 44;
+ while (a != b) {
+ if (a > b)
+ a -= b;
+ else
+ b -= a;
+ }
+ printf("%d\n%d", a, 0X0);\
+}
+
+
+/**********************************************************************
+
+ array.c -
+
+ $Author: murphy $
+ $Date: 2005-11-05 04:33:55 +0100 (Sa, 05 Nov 2005) $
+ created at: Fri Aug 6 09:46:12 JST 1993
+
+ Copyright (C) 1993-2003 Yukihiro Matsumoto
+ Copyright (C) 2000 Network Applied Communication Laboratory, Inc.
+ Copyright (C) 2000 Information-technology Promotion Agency, Japan
+
+**********************************************************************/
+
+#include "ruby.h"
+#include "util.h"
+#include "st.h"
+#include "node.h"
+
+VALUE rb_cArray, rb_cValues;
+
+static ID id_cmp;
+
+#define ARY_DEFAULT_SIZE 16
+
+
+void
+rb_mem_clear(mem, size)
+ register VALUE *mem;
+ register long size;
+{
+ while (size--) {
+ *mem++ = Qnil;
+ }
+}
+
+static inline void
+memfill(mem, size, val)
+ register VALUE *mem;
+ register long size;
+ register VALUE val;
+{
+ while (size--) {
+ *mem++ = val;
+ }
+}
+
+#define ARY_TMPLOCK FL_USER1
+
+static inline void
+rb_ary_modify_check(ary)
+ VALUE ary;
+{
+ if (OBJ_FROZEN(ary)) rb_error_frozen("array");
+ if (FL_TEST(ary, ARY_TMPLOCK))
+ rb_raise(rb_eRuntimeError, "can't modify array during iteration");
+ if (!OBJ_TAINTED(ary) && rb_safe_level() >= 4)
+ rb_raise(rb_eSecurityError, "Insecure: can't modify array");
+}
+
+static void
+rb_ary_modify(ary)
+ VALUE ary;
+{
+ VALUE *ptr;
+
+ rb_ary_modify_check(ary);
+ if (FL_TEST(ary, ELTS_SHARED)) {
+ ptr = ALLOC_N(VALUE, RARRAY(ary)->len);
+ FL_UNSET(ary, ELTS_SHARED);
+ RARRAY(ary)->aux.capa = RARRAY(ary)->len;
+ MEMCPY(ptr, RARRAY(ary)->ptr, VALUE, RARRAY(ary)->len);
+ RARRAY(ary)->ptr = ptr;
+ }
+}
+
+VALUE
+rb_ary_freeze(ary)
+ VALUE ary;
+{
+ return rb_obj_freeze(ary);
+}
+
+/*
+ * call-seq:
+ * array.frozen? -> true or false
+ *
+ * Return <code>true</code> if this array is frozen (or temporarily frozen
+ * while being sorted).
+ */
+
+static VALUE
+rb_ary_frozen_p(ary)
+ VALUE ary;
+{
+ if (OBJ_FROZEN(ary)) return Qtrue;
+ if (FL_TEST(ary, ARY_TMPLOCK)) return Qtrue;
+ return Qfalse;
+}
+
+static VALUE ary_alloc(VALUE);
+static VALUE
+ary_alloc(klass)
+ VALUE klass;
+{
+ NEWOBJ(ary, struct RArray);
+ OBJSETUP(ary, klass, T_ARRAY);
+
+ ary->len = 0;
+ ary->ptr = 0;
+ ary->aux.capa = 0;
+
+ return (VALUE)ary;
+}
+
+static VALUE
+ary_new(klass, len)
+ VALUE klass;
+ long len;
+{
+ VALUE ary;
+
+ if (len < 0) {
+ rb_raise(rb_eArgError, "negative array size (or size too big)");
+ }
+ if (len > 0 && len * sizeof(VALUE) <= len) {
+ rb_raise(rb_eArgError, "array size too big");
+ }
+ if (len == 0) len++;
+
+ ary = ary_alloc(klass);
+ RARRAY(ary)->ptr = ALLOC_N(VALUE, len);
+ RARRAY(ary)->aux.capa = len;
+
+ return ary;
+}
+
+VALUE
+rb_ary_new2(len)
+ long len;
+{
+ return ary_new(rb_cArray, len);
+}
+
+
+VALUE
+rb_ary_new()
+{
+ return rb_ary_new2(ARY_DEFAULT_SIZE);
+}
+
+#ifdef HAVE_STDARG_PROTOTYPES
+#include <stdarg.h>
+#define va_init_list(a,b) va_start(a,b)
+#else
+#include <varargs.h>
+#define va_init_list(a,b) va_start(a)
+#endif
+
+VALUE
+#ifdef HAVE_STDARG_PROTOTYPES
+rb_ary_new3(long n, ...)
+#else
+rb_ary_new3(n, va_alist)
+ long n;
+ va_dcl
+#endif
+{
+ va_list ar;
+ VALUE ary;
+ long i;
+
+ ary = rb_ary_new2(n);
+
+ va_init_list(ar, n);
+ for (i=0; i<n; i++) {
+ RARRAY(ary)->ptr[i] = va_arg(ar, VALUE);
+ }
+ va_end(ar);
+
+ RARRAY(ary)->len = n;
+ return ary;
+}
+
+VALUE
+rb_ary_new4(n, elts)
+ long n;
+ const VALUE *elts;
+{
+ VALUE ary;
+
+ ary = rb_ary_new2(n);
+ if (n > 0 && elts) {
+ MEMCPY(RARRAY(ary)->ptr, elts, VALUE, n);
+ }
+ RARRAY(ary)->len = n;
+
+ return ary;
+}
+
+VALUE
+#ifdef HAVE_STDARG_PROTOTYPES
+rb_values_new(long n, ...)
+#else
+rb_values_new(n, va_alist)
+ long n;
+ va_dcl
+#endif
+{
+ va_list ar;
+ VALUE val;
+ long i;
+
+ val = ary_new(rb_cValues, n);
+ va_init_list(ar, n);
+ for (i=0; i<n; i++) {
+ RARRAY(val)->ptr[i] = va_arg(ar, VALUE);
+ }
+ va_end(ar);
+ RARRAY(val)->len = n;
+
+ return val;
+}
+
+VALUE
+rb_values_new2(n, elts)
+ long n;
+ const VALUE *elts;
+{
+ VALUE val;
+
+ val = ary_new(rb_cValues, n);
+ if (n > 0 && elts) {
+ RARRAY(val)->len = n;
+ MEMCPY(RARRAY(val)->ptr, elts, VALUE, n);
+ }
+
+ return val;
+}
+
+static VALUE
+ary_make_shared(ary)
+ VALUE ary;
+{
+ if (!FL_TEST(ary, ELTS_SHARED)) {
+ NEWOBJ(shared, struct RArray);
+ OBJSETUP(shared, rb_cArray, T_ARRAY);
+
+ shared->len = RARRAY(ary)->len;
+ shared->ptr = RARRAY(ary)->ptr;
+ shared->aux.capa = RARRAY(ary)->aux.capa;
+ RARRAY(ary)->aux.shared = (VALUE)shared;
+ FL_SET(ary, ELTS_SHARED);
+ OBJ_FREEZE(shared);
+ return (VALUE)shared;
+ }
+ else {
+ return RARRAY(ary)->aux.shared;
+ }
+}
+
+static VALUE
+ary_shared_array(klass, ary)
+ VALUE klass, ary;
+{
+ VALUE val = ary_alloc(klass);
+
+ ary_make_shared(ary);
+ RARRAY(val)->ptr = RARRAY(ary)->ptr;
+ RARRAY(val)->len = RARRAY(ary)->len;
+ RARRAY(val)->aux.shared = RARRAY(ary)->aux.shared;
+ FL_SET(val, ELTS_SHARED);
+ return val;
+}
+
+VALUE
+rb_values_from_ary(ary)
+ VALUE ary;
+{
+ return ary_shared_array(rb_cValues, ary);
+}
+
+VALUE
+rb_ary_from_values(val)
+ VALUE val;
+{
+ return ary_shared_array(rb_cArray, val);
+}
+
+VALUE
+rb_assoc_new(car, cdr)
+ VALUE car, cdr;
+{
+ return rb_values_new(2, car, cdr);
+}
+
+static VALUE
+to_ary(ary)
+ VALUE ary;
+{
+ return rb_convert_type(ary, T_ARRAY, "Array", "to_ary");
+}
+
+static VALUE
+to_a(ary)
+ VALUE ary;
+{
+ return rb_convert_type(ary, T_ARRAY, "Array", "to_a");
+}
+
+VALUE
+rb_check_array_type(ary)
+ VALUE ary;
+{
+ return rb_check_convert_type(ary, T_ARRAY, "Array", "to_ary");
+}
+
+static VALUE rb_ary_replace _((VALUE, VALUE));
+
+/*
+ * call-seq:
+ * Array.new(size=0, obj=nil)
+ * Array.new(array)
+ * Array.new(size) {|index| block }
+ *
+ * Returns a new array. In the first form, the new array is
+ * empty. In the second it is created with _size_ copies of _obj_
+ * (that is, _size_ references to the same
+ * _obj_). The third form creates a copy of the array
+ * passed as a parameter (the array is generated by calling
+ * to_ary on the parameter). In the last form, an array
+ * of the given size is created. Each element in this array is
+ * calculated by passing the element's index to the given block and
+ * storing the return value.
+ *
+ * Array.new
+ * Array.new(2)
+ * Array.new(5, "A")
+ *
+ * # only one copy of the object is created
+ * a = Array.new(2, Hash.new)
+ * a[0]['cat'] = 'feline'
+ * a
+ * a[1]['cat'] = 'Felix'
+ * a
+ *
+ * # here multiple copies are created
+ * a = Array.new(2) { Hash.new }
+ * a[0]['cat'] = 'feline'
+ * a
+ *
+ * squares = Array.new(5) {|i| i*i}
+ * squares
+ *
+ * copy = Array.new(squares)
+ */
+
+static VALUE
+rb_ary_initialize(argc, argv, ary)
+ int argc;
+ VALUE *argv;
+ VALUE ary;
+{
+ long len;
+ VALUE size, val;
+
+ if (rb_scan_args(argc, argv, "02", &size, &val) == 0) {
+ RARRAY(ary)->len = 0;
+ if (rb_block_given_p()) {
+ rb_warning("given block not used");
+ }
+ return ary;
+ }
+
+ if (argc == 1 && !FIXNUM_P(size)) {
+ val = rb_check_array_type(size);
+ if (!NIL_P(val)) {
+ rb_ary_replace(ary, val);
+ return ary;
+ }
+ }
+
+ len = NUM2LONG(size);
+ if (len < 0) {
+ rb_raise(rb_eArgError, "negative array size");
+ }
+ if (len > 0 && len * (long)sizeof(VALUE) <= len) {
+ rb_raise(rb_eArgError, "array size too big");
+ }
+ rb_ary_modify(ary);
+ if (len > RARRAY(ary)->aux.capa) {
+ REALLOC_N(RARRAY(ary)->ptr, VALUE, len);
+ RARRAY(ary)->aux.capa = len;
+ }
+ if (rb_block_given_p()) {
+ long i;
+
+ if (argc == 2) {
+ rb_warn("block supersedes default value argument");
+ }
+ for (i=0; i<len; i++) {
+ rb_ary_store(ary, i, rb_yield(LONG2NUM(i)));
+ RARRAY(ary)->len = i + 1;
+ }
+ }
+ else {
+ memfill(RARRAY(ary)->ptr, len, val);
+ RARRAY(ary)->len = len;
+ }
+
+ return ary;
+}
+
+
+/*
+* Returns a new array populated with the given objects.
+*
+* Array.[]( 1, 'a', /^A/ )
+* Array[ 1, 'a', /^A/ ]
+* [ 1, 'a', /^A/ ]
+*/
+
+static VALUE
+rb_ary_s_create(argc, argv, klass)
+ int argc;
+ VALUE *argv;
+ VALUE klass;
+{
+ VALUE ary = ary_alloc(klass);
+
+ if (argc > 0) {
+ RARRAY(ary)->ptr = ALLOC_N(VALUE, argc);
+ MEMCPY(RARRAY(ary)->ptr, argv, VALUE, argc);
+ }
+ RARRAY(ary)->len = RARRAY(ary)->aux.capa = argc;
+
+ return ary;
+}
+
+void
+rb_ary_store(ary, idx, val)
+ VALUE ary;
+ long idx;
+ VALUE val;
+{
+ if (idx < 0) {
+ idx += RARRAY(ary)->len;
+ if (idx < 0) {
+ rb_raise(rb_eIndexError, "index %ld out of array",
+ idx - RARRAY(ary)->len);
+ }
+ }
+
+ rb_ary_modify(ary);
+ if (idx >= RARRAY(ary)->aux.capa) {
+ long new_capa = RARRAY(ary)->aux.capa / 2;
+
+ if (new_capa < ARY_DEFAULT_SIZE) {
+ new_capa = ARY_DEFAULT_SIZE;
+ }
+ new_capa += idx;
+ if (new_capa * (long)sizeof(VALUE) <= new_capa) {
+ rb_raise(rb_eArgError, "index too big");
+ }
+ REALLOC_N(RARRAY(ary)->ptr, VALUE, new_capa);
+ RARRAY(ary)->aux.capa = new_capa;
+ }
+ if (idx > RARRAY(ary)->len) {
+ rb_mem_clear(RARRAY(ary)->ptr + RARRAY(ary)->len,
+ idx-RARRAY(ary)->len + 1);
+ }
+
+ if (idx >= RARRAY(ary)->len) {
+ RARRAY(ary)->len = idx + 1;
+ }
+ RARRAY(ary)->ptr[idx] = val;
+}
+
+static VALUE
+ary_shared_first(argc, argv, ary)
+ int argc;
+ VALUE *argv;
+ VALUE ary;
+{
+ VALUE nv, result;
+ long n;
+
+ rb_scan_args(argc, argv, "1", &nv);
+ n = NUM2LONG(nv);
+ if (n > RARRAY(ary)->len) {
+ n = RARRAY(ary)->len;
+ }
+ else if (n < 0) {
+ rb_raise(rb_eArgError, "negative array size");
+ }
+ result = ary_shared_array(rb_cArray, ary);
+ RARRAY(result)->len = n;
+ return result;
+}
+
+static VALUE
+ary_shared_last(argc, argv, ary)
+ int argc;
+ VALUE *argv;
+ VALUE ary;
+{
+ VALUE result = ary_shared_first(argc, argv, ary);
+
+ RARRAY(result)->ptr += RARRAY(ary)->len - RARRAY(result)->len;
+ return result;
+}
+
+/*
+ * call-seq:
+ * array << obj -> array
+ *
+ * Append---Pushes the given object on to the end of this array. This
+ * expression returns the array itself, so several appends
+ * may be chained together.
+ *
+ * [ 1, 2 ] << "c" << "d" << [ 3, 4 ]
+ * #=> [ 1, 2, "c", "d", [ 3, 4 ] ]
+ *
+ */
+
+VALUE
+rb_ary_push(ary, item)
+ VALUE ary;
+ VALUE item;
+{
+ rb_ary_store(ary, RARRAY(ary)->len, item);
+ return ary;
+}
+
+/*
+ * call-seq:
+ * array.push(obj, ... ) -> array
+ *
+ * Append---Pushes the given object(s) on to the end of this array. This
+ * expression returns the array itself, so several appends
+ * may be chained together.
+ *
+ * a = [ "a", "b", "c" ]
+ * a.push("d", "e", "f")
+ * #=> ["a", "b", "c", "d", "e", "f"]
+ */
+
+static VALUE
+rb_ary_push_m(argc, argv, ary)
+ int argc;
+ VALUE *argv;
+ VALUE ary;
+{
+ while (argc--) {
+ rb_ary_push(ary, *argv++);
+ }
+ return ary;
+}
+
+VALUE
+rb_ary_pop(ary)
+ VALUE ary;
+{
+ rb_ary_modify_check(ary);
+ if (RARRAY(ary)->len == 0) return Qnil;
+ if (!FL_TEST(ary, ELTS_SHARED) &&
+ RARRAY(ary)->len * 2 < RARRAY(ary)->aux.capa &&
+ RARRAY(ary)->aux.capa > ARY_DEFAULT_SIZE) {
+ RARRAY(ary)->aux.capa = RARRAY(ary)->len * 2;
+ REALLOC_N(RARRAY(ary)->ptr, VALUE, RARRAY(ary)->aux.capa);
+ }
+ return RARRAY(ary)->ptr[--RARRAY(ary)->len];
+}
+
+/*
+ * call-seq:
+ * array.pop -> obj or nil
+ *
+ * Removes the last element from <i>self</i> and returns it, or
+ * <code>nil</code> if the array is empty.
+ *
+ * a = [ "a", "b", "c", "d" ]
+ * a.pop #=> "d"
+ * a.pop(2) #=> ["b", "c"]
+ * a #=> ["a"]
+ */
+
+static VALUE
+rb_ary_pop_m(argc, argv, ary)
+ int argc;
+ VALUE *argv;
+ VALUE ary;
+{
+ VALUE result;
+
+ if (argc == 0) {
+ return rb_ary_pop(ary);
+ }
+
+ rb_ary_modify_check(ary);
+
+ result = ary_shared_last(argc, argv, ary);
+ RARRAY(ary)->len -= RARRAY(result)->len;
+ return result;
+}
+
+VALUE
+rb_ary_shift(ary)
+ VALUE ary;
+{
+ VALUE top;
+
+ rb_ary_modify_check(ary);
+ if (RARRAY(ary)->len == 0) return Qnil;
+ top = RARRAY(ary)->ptr[0];
+ ary_make_shared(ary);
+ RARRAY(ary)->ptr++; /* shift ptr */
+ RARRAY(ary)->len--;
+
+ return top;
+}
+
+/*
+ * call-seq:
+ * array.shift -> obj or nil
+ *
+ * Returns the first element of <i>self</i> and removes it (shifting all
+ * other elements down by one). Returns <code>nil</code> if the array
+ * is empty.
+ *
+ * args = [ "-m", "-q", "filename" ]
+ * args.shift #=> "-m"
+ * args #=> ["-q", "filename"]
+ *
+ * args = [ "-m", "-q", "filename" ]
+ * args.shift(2) #=> ["-m", "-q"]
+ * args #=> ["filename"]
+ */
+
+static VALUE
+rb_ary_shift_m(argc, argv, ary)
+ int argc;
+ VALUE *argv;
+ VALUE ary;
+{
+ VALUE result;
+ long n;
+
+ if (argc == 0) {
+ return rb_ary_shift(ary);
+ }
+
+ rb_ary_modify_check(ary);
+
+ result = ary_shared_first(argc, argv, ary);
+ n = RARRAY(result)->len;
+ RARRAY(ary)->ptr += n;
+ RARRAY(ary)->len -= n;
+
+ return result;
+}
+
+VALUE
+rb_ary_unshift(ary, item)
+ VALUE ary, item;
+{
+ rb_ary_modify(ary);
+ if (RARRAY(ary)->len == RARRAY(ary)->aux.capa) {
+ long capa_inc = RARRAY(ary)->aux.capa / 2;
+ if (capa_inc < ARY_DEFAULT_SIZE) {
+ capa_inc = ARY_DEFAULT_SIZE;
+ }
+ RARRAY(ary)->aux.capa += capa_inc;
+ REALLOC_N(RARRAY(ary)->ptr, VALUE, RARRAY(ary)->aux.capa);
+ }
+
+ /* sliding items */
+ MEMMOVE(RARRAY(ary)->ptr + 1, RARRAY(ary)->ptr, VALUE, RARRAY(ary)->len);
+
+ RARRAY(ary)->len++;
+ RARRAY(ary)->ptr[0] = item;
+
+ return ary;
+}
+
+/*
+ * call-seq:
+ * array.unshift(obj, ...) -> array
+ *
+ * Prepends objects to the front of <i>array</i>.
+ * other elements up one.
+ *
+ * a = [ "b", "c", "d" ]
+ * a.unshift("a") #=> ["a", "b", "c", "d"]
+ * a.unshift(1, 2) #=> [ 1, 2, "a", "b", "c", "d"]
+ */
+
+static VALUE
+rb_ary_unshift_m(argc, argv, ary)
+ int argc;
+ VALUE *argv;
+ VALUE ary;
+{
+ long len = RARRAY(ary)->len;
+
+ if (argc == 0) return ary;
+
+ /* make rooms by setting the last item */
+ rb_ary_store(ary, len + argc - 1, Qnil);
+
+ /* sliding items */
+ MEMMOVE(RARRAY(ary)->ptr + argc, RARRAY(ary)->ptr, VALUE, len);
+ MEMCPY(RARRAY(ary)->ptr, argv, VALUE, argc);
+
+ return ary;
+}
+
+/* faster version - use this if you don't need to treat negative offset */
+static inline VALUE
+rb_ary_elt(ary, offset)
+ VALUE ary;
+ long offset;
+{
+ if (RARRAY(ary)->len == 0) return Qnil;
+ if (offset < 0 || RARRAY(ary)->len <= offset) {
+ return Qnil;
+ }
+ return RARRAY(ary)->ptr[offset];
+}
+
+VALUE
+rb_ary_entry(ary, offset)
+ VALUE ary;
+ long offset;
+{
+ if (offset < 0) {
+ offset += RARRAY(ary)->len;
+ }
+ return rb_ary_elt(ary, offset);
+}
+
+static VALUE
+rb_ary_subseq(ary, beg, len)
+ VALUE ary;
+ long beg, len;
+{
+ VALUE klass, ary2, shared;
+ VALUE *ptr;
+
+ if (beg > RARRAY(ary)->len) return Qnil;
+ if (beg < 0 || len < 0) return Qnil;
+
+ if (beg + len > RARRAY(ary)->len) {
+ len = RARRAY(ary)->len - beg;
+ if (len < 0)
+ len = 0;
+ }
+ klass = rb_obj_class(ary);
+ if (len == 0) return ary_new(klass, 0);
+
+ shared = ary_make_shared(ary);
+ ptr = RARRAY(ary)->ptr;
+ ary2 = ary_alloc(klass);
+ RARRAY(ary2)->ptr = ptr + beg;
+ RARRAY(ary2)->len = len;
+ RARRAY(ary2)->aux.shared = shared;
+ FL_SET(ary2, ELTS_SHARED);
+
+ return ary2;
+}
+
+/*
+ * call-seq:
+ * array[index] -> obj or nil
+ * array[start, length] -> an_array or nil
+ * array[range] -> an_array or nil
+ * array.slice(index) -> obj or nil
+ * array.slice(start, length) -> an_array or nil
+ * array.slice(range) -> an_array or nil
+ *
+ * Element Reference---Returns the element at _index_,
+ * or returns a subarray starting at _start_ and
+ * continuing for _length_ elements, or returns a subarray
+ * specified by _range_.
+ * Negative indices count backward from the end of the
+ * array (-1 is the last element). Returns nil if the index
+ * (or starting index) are out of range.
+ *
+ * a = [ "a", "b", "c", "d", "e" ]
+ * a[2] + a[0] + a[1] #=> "cab"
+ * a[6] #=> nil
+ * a[1, 2] #=> [ "b", "c" ]
+ * a[1..3] #=> [ "b", "c", "d" ]
+ * a[4..7] #=> [ "e" ]
+ * a[6..10] #=> nil
+ * a[-3, 3] #=> [ "c", "d", "e" ]
+ * # special cases
+ * a[5] #=> nil
+ * a[5, 1] #=> []
+ * a[5..10] #=> []
+ *
+ */
+
+VALUE
+rb_ary_aref(argc, argv, ary)
+ int argc;
+ VALUE *argv;
+ VALUE ary;
+{
+ VALUE arg;
+ long beg, len;
+
+ if (argc == 2) {
+ beg = NUM2LONG(argv[0]);
+ len = NUM2LONG(argv[1]);
+ if (beg < 0) {
+ beg += RARRAY(ary)->len;
+ }
+ return rb_ary_subseq(ary, beg, len);
+ }
+ if (argc != 1) {
+ rb_scan_args(argc, argv, "11", 0, 0);
+ }
+ arg = argv[0];
+ /* special case - speeding up */
+ if (FIXNUM_P(arg)) {
+ return rb_ary_entry(ary, FIX2LONG(arg));
+ }
+ /* check if idx is Range */
+ switch (rb_range_beg_len(arg, &beg, &len, RARRAY(ary)->len, 0)) {
+ case Qfalse:
+ break;
+ case Qnil:
+ return Qnil;
+ default:
+ return rb_ary_subseq(ary, beg, len);
+ }
+ return rb_ary_entry(ary, NUM2LONG(arg));
+}
+
+/*
+ * call-seq:
+ * array.at(index) -> obj or nil
+ *
+ * Returns the element at _index_. A
+ * negative index counts from the end of _self_. Returns +nil+
+ * if the index is out of range. See also <code>Array#[]</code>.
+ * (<code>Array#at</code> is slightly faster than <code>Array#[]</code>,
+ * as it does not accept ranges and so on.)
+ *
+ * a = [ "a", "b", "c", "d", "e" ]
+ * a.at(0) #=> "a"
+ * a.at(-1) #=> "e"
+ */
+
+static VALUE
+rb_ary_at(ary, pos)
+ VALUE ary, pos;
+{
+ return rb_ary_entry(ary, NUM2LONG(pos));
+}
+
+/*
+ * call-seq:
+ * array.first -> obj or nil
+ * array.first(n) -> an_array
+ *
+ * Returns the first element of the array. If the array is empty,
+ * returns <code>nil</code>.
+ *
+ * a = [ "q", "r", "s", "t" ]
+ * a.first #=> "q"
+ * a.first(2) #=> ["q", "r"]
+ */
+
+static VALUE
+rb_ary_first(argc, argv, ary)
+ int argc;
+ VALUE *argv;
+ VALUE ary;
+{
+ if (argc == 0) {
+ if (RARRAY(ary)->len == 0) return Qnil;
+ return RARRAY(ary)->ptr[0];
+ }
+ else {
+ return ary_shared_first(argc, argv, ary);
+ }
+}
+
+/*
+ * call-seq:
+ * array.last -> obj or nil
+ * array.last(n) -> an_array
+ *
+ * Returns the last element(s) of <i>self</i>. If the array is empty,
+ * the first form returns <code>nil</code>.
+ *
+ * a = [ "w", "x", "y", "z" ]
+ * a.last #=> "z"
+ * a.last(2) #=> ["y", "z"]
+ */
+
+static VALUE
+rb_ary_last(argc, argv, ary)
+ int argc;
+ VALUE *argv;
+ VALUE ary;
+{
+ if (argc == 0) {
+ if (RARRAY(ary)->len == 0) return Qnil;
+ return RARRAY(ary)->ptr[RARRAY(ary)->len-1];
+ }
+ else {
+ return ary_shared_last(argc, argv, ary);
+ }
+}
+
+/*
+ * call-seq:
+ * array.fetch(index) -> obj
+ * array.fetch(index, default ) -> obj
+ * array.fetch(index) {|index| block } -> obj
+ *
+ * Tries to return the element at position <i>index</i>. If the index
+ * lies outside the array, the first form throws an
+ * <code>IndexError</code> exception, the second form returns
+ * <i>default</i>, and the third form returns the value of invoking
+ * the block, passing in the index. Negative values of <i>index</i>
+ * count from the end of the array.
+ *
+ * a = [ 11, 22, 33, 44 ]
+ * a.fetch(1) #=> 22
+ * a.fetch(-1) #=> 44
+ * a.fetch(4, 'cat') #=> "cat"
+ * a.fetch(4) { |i| i*i } #=> 16
+ */
+
+static VALUE
+rb_ary_fetch(argc, argv, ary)
+ int argc;
+ VALUE *argv;
+ VALUE ary;
+{
+ VALUE pos, ifnone;
+ long block_given;
+ long idx;
+
+ rb_scan_args(argc, argv, "11", &pos, &ifnone);
+ block_given = rb_block_given_p();
+ if (block_given && argc == 2) {
+ rb_warn("block supersedes default value argument");
+ }
+ idx = NUM2LONG(pos);
+
+ if (idx < 0) {
+ idx += RARRAY(ary)->len;
+ }
+ if (idx < 0 || RARRAY(ary)->len <= idx) {
+ if (block_given) return rb_yield(pos);
+ if (argc == 1) {
+ rb_raise(rb_eIndexError, "index %ld out of array", idx);
+ }
+ return ifnone;
+ }
+ return RARRAY(ary)->ptr[idx];
+}
+
+/*
+ * call-seq:
+ * array.index(obj) -> int or nil
+ * array.index {|item| block} -> int or nil
+ *
+ * Returns the index of the first object in <i>self</i> such that is
+ * <code>==</code> to <i>obj</i>. If a block is given instead of an
+ * argument, returns first object for which <em>block</em> is true.
+ * Returns <code>nil</code> if no match is found.
+ *
+ * a = [ "a", "b", "c" ]
+ * a.index("b") #=> 1
+ * a.index("z") #=> nil
+ * a.index{|x|x=="b"} #=> 1
+ */
+
+static VALUE
+rb_ary_index(argc, argv, ary)
+ int argc;
+ VALUE *argv;
+ VALUE ary;
+{
+ VALUE val;
+ long i;
+
+ if (rb_scan_args(argc, argv, "01", &val) == 0) {
+ for (i=0; i<RARRAY(ary)->len; i++) {
+ if (RTEST(rb_yield(RARRAY(ary)->ptr[i]))) {
+ return LONG2NUM(i);
+ }
+ }
+ }
+ else {
+ for (i=0; i<RARRAY(ary)->len; i++) {
+ if (rb_equal(RARRAY(ary)->ptr[i], val))
+ return LONG2NUM(i);
+ }
+ }
+ return Qnil;
+}
+
+/*
+ * call-seq:
+ * array.rindex(obj) -> int or nil
+ *
+ * Returns the index of the last object in <i>array</i>
+ * <code>==</code> to <i>obj</i>. If a block is given instead of an
+ * argument, returns first object for which <em>block</em> is
+ * true. Returns <code>nil</code> if no match is found.
+ *
+ * a = [ "a", "b", "b", "b", "c" ]
+ * a.rindex("b") #=> 3
+ * a.rindex("z") #=> nil
+ * a.rindex{|x|x=="b"} #=> 3
+ */
+
+static VALUE
+rb_ary_rindex(argc, argv, ary)
+ int argc;
+ VALUE *argv;
+ VALUE ary;
+{
+ VALUE val;
+ long i = RARRAY(ary)->len;
+
+ if (rb_scan_args(argc, argv, "01", &val) == 0) {
+ while (i--) {
+ if (RTEST(rb_yield(RARRAY(ary)->ptr[i])))
+ return LONG2NUM(i);
+ if (i > RARRAY(ary)->len) {
+ i = RARRAY(ary)->len;
+ }
+ }
+ }
+ else {
+ while (i--) {
+ if (rb_equal(RARRAY(ary)->ptr[i], val))
+ return LONG2NUM(i);
+ if (i > RARRAY(ary)->len) {
+ i = RARRAY(ary)->len;
+ }
+ }
+ }
+ return Qnil;
+}
+
+VALUE
+rb_ary_to_ary(obj)
+ VALUE obj;
+{
+ if (TYPE(obj) == T_ARRAY) {
+ return obj;
+ }
+ if (rb_respond_to(obj, rb_intern("to_ary"))) {
+ return to_ary(obj);
+ }
+ return rb_ary_new3(1, obj);
+}
+
+static void
+rb_ary_splice(ary, beg, len, rpl)
+ VALUE ary;
+ long beg, len;
+ VALUE rpl;
+{
+ long rlen;
+
+ if (len < 0) rb_raise(rb_eIndexError, "negative length (%ld)", len);
+ if (beg < 0) {
+ beg += RARRAY(ary)->len;
+ if (beg < 0) {
+ beg -= RARRAY(ary)->len;
+ rb_raise(rb_eIndexError, "index %ld out of array", beg);
+ }
+ }
+ if (beg + len > RARRAY(ary)->len) {
+ len = RARRAY(ary)->len - beg;
+ }
+
+ if (rpl == Qundef) {
+ rlen = 0;
+ }
+ else {
+ rpl = rb_ary_to_ary(rpl);
+ rlen = RARRAY(rpl)->len;
+ }
+ rb_ary_modify(ary);
+
+ if (beg >= RARRAY(ary)->len) {
+ len = beg + rlen;
+ if (len >= RARRAY(ary)->aux.capa) {
+ REALLOC_N(RARRAY(ary)->ptr, VALUE, len);
+ RARRAY(ary)->aux.capa = len;
+ }
+ rb_mem_clear(RARRAY(ary)->ptr + RARRAY(ary)->len, beg - RARRAY(ary)->len);
+ if (rlen > 0) {
+ MEMCPY(RARRAY(ary)->ptr + beg, RARRAY(rpl)->ptr, VALUE, rlen);
+ }
+ RARRAY(ary)->len = len;
+ }
+ else {
+ long alen;
+
+ if (beg + len > RARRAY(ary)->len) {
+ len = RARRAY(ary)->len - beg;
+ }
+
+ alen = RARRAY(ary)->len + rlen - len;
+ if (alen >= RARRAY(ary)->aux.capa) {
+ REALLOC_N(RARRAY(ary)->ptr, VALUE, alen);
+ RARRAY(ary)->aux.capa = alen;
+ }
+
+ if (len != rlen) {
+ MEMMOVE(RARRAY(ary)->ptr + beg + rlen, RARRAY(ary)->ptr + beg + len,
+ VALUE, RARRAY(ary)->len - (beg + len));
+ RARRAY(ary)->len = alen;
+ }
+ if (rlen > 0) {
+ MEMMOVE(RARRAY(ary)->ptr + beg, RARRAY(rpl)->ptr, VALUE, rlen);
+ }
+ }
+}
+
+/*
+ * call-seq:
+ * array[index] = obj -> obj
+ * array[start, length] = obj or an_array or nil -> obj or an_array or nil
+ * array[range] = obj or an_array or nil -> obj or an_array or nil
+ *
+ * Element Assignment---Sets the element at _index_,
+ * or replaces a subarray starting at _start_ and
+ * continuing for _length_ elements, or replaces a subarray
+ * specified by _range_. If indices are greater than
+ * the current capacity of the array, the array grows
+ * automatically. A negative indices will count backward
+ * from the end of the array. Inserts elements if _length_ is
+ * zero. An +IndexError+ is raised if a negative index points
+ * past the beginning of the array. See also
+ * <code>Array#push</code>, and <code>Array#unshift</code>.
+ *
+ * a = Array.new
+ * a[4] = "4"; #=> [nil, nil, nil, nil, "4"]
+ * a[0, 3] = [ 'a', 'b', 'c' ] #=> ["a", "b", "c", nil, "4"]
+ * a[1..2] = [ 1, 2 ] #=> ["a", 1, 2, nil, "4"]
+ * a[0, 2] = "?" #=> ["?", 2, nil, "4"]
+ * a[0..2] = "A" #=> ["A", "4"]
+ * a[-1] = "Z" #=> ["A", "Z"]
+ * a[1..-1] = nil #=> ["A", nil]
+ * a[1..-1] = [] #=> ["A"]
+ */
+
+static VALUE
+rb_ary_aset(argc, argv, ary)
+ int argc;
+ VALUE *argv;
+ VALUE ary;
+{
+ long offset, beg, len;
+
+ if (argc == 3) {
+ rb_ary_splice(ary, NUM2LONG(argv[0]), NUM2LONG(argv[1]), argv[2]);
+ return argv[2];
+ }
+ if (argc != 2) {
+ rb_raise(rb_eArgError, "wrong number of arguments (%d for 2)", argc);
+ }
+ if (FIXNUM_P(argv[0])) {
+ offset = FIX2LONG(argv[0]);
+ goto fixnum;
+ }
+ if (rb_range_beg_len(argv[0], &beg, &len, RARRAY(ary)->len, 1)) {
+ /* check if idx is Range */
+ rb_ary_splice(ary, beg, len, argv[1]);
+ return argv[1];
+ }
+
+ offset = NUM2LONG(argv[0]);
+fixnum:
+ rb_ary_store(ary, offset, argv[1]);
+ return argv[1];
+}
+
+/*
+ * call-seq:
+ * array.insert(index, obj...) -> array
+ *
+ * Inserts the given values before the element with the given index
+ * (which may be negative).
+ *
+ * a = %w{ a b c d }
+ * a.insert(2, 99) #=> ["a", "b", 99, "c", "d"]
+ * a.insert(-2, 1, 2, 3) #=> ["a", "b", 99, "c", 1, 2, 3, "d"]
+ */
+
+static VALUE
+rb_ary_insert(argc, argv, ary)
+ int argc;
+ VALUE *argv;
+ VALUE ary;
+{
+ long pos;
+
+ if (argc < 1) {
+ rb_raise(rb_eArgError, "wrong number of arguments (at least 1)");
+ }
+ pos = NUM2LONG(argv[0]);
+ if (pos == -1) {
+ pos = RARRAY(ary)->len;
+ }
+ else if (pos < 0) {
+ pos++;
+ }
+
+ if (argc == 1) return ary;
+ rb_ary_splice(ary, pos, 0, rb_ary_new4(argc - 1, argv + 1));
+ return ary;
+}
+
+/*
+ * call-seq:
+ * array.each {|item| block } -> array
+ *
+ * Calls <i>block</i> once for each element in <i>self</i>, passing that
+ * element as a parameter.
+ *
+ * a = [ "a", "b", "c" ]
+ * a.each {|x| print x, " -- " }
+ *
+ * produces:
+ *
+ * a -- b -- c --
+ */
+
+VALUE
+rb_ary_each(ary)
+ VALUE ary;
+{
+ long i;
+
+ for (i=0; i<RARRAY(ary)->len; i++) {
+ rb_yield(RARRAY(ary)->ptr[i]);
+ }
+ return ary;
+}
+
+/*
+ * call-seq:
+ * array.each_index {|index| block } -> array
+ *
+ * Same as <code>Array#each</code>, but passes the index of the element
+ * instead of the element itself.
+ *
+ * a = [ "a", "b", "c" ]
+ * a.each_index {|x| print x, " -- " }
+ *
+ * produces:
+ *
+ * 0 -- 1 -- 2 --
+ */
+
+static VALUE
+rb_ary_each_index(ary)
+ VALUE ary;
+{
+ long i;
+
+ for (i=0; i<RARRAY(ary)->len; i++) {
+ rb_yield(LONG2NUM(i));
+ }
+ return ary;
+}
+
+/*
+ * call-seq:
+ * array.reverse_each {|item| block }
+ *
+ * Same as <code>Array#each</code>, but traverses <i>self</i> in reverse
+ * order.
+ *
+ * a = [ "a", "b", "c" ]
+ * a.reverse_each {|x| print x, " " }
+ *
+ * produces:
+ *
+ * c b a
+ */
+
+static VALUE
+rb_ary_reverse_each(ary)
+ VALUE ary;
+{
+ long len = RARRAY(ary)->len;
+
+ while (len--) {
+ rb_yield(RARRAY(ary)->ptr[len]);
+ if (RARRAY(ary)->len < len) {
+ len = RARRAY(ary)->len;
+ }
+ }
+ return ary;
+}
+
+/*
+ * call-seq:
+ * array.length -> int
+ *
+ * Returns the number of elements in <i>self</i>. May be zero.
+ *
+ * [ 1, 2, 3, 4, 5 ].length #=> 5
+ */
+
+static VALUE
+rb_ary_length(ary)
+ VALUE ary;
+{
+ return LONG2NUM(RARRAY(ary)->len);
+}
+
+/*
+ * call-seq:
+ * array.empty? -> true or false
+ *
+ * Returns <code>true</code> if <i>self</i> array contains no elements.
+ *
+ * [].empty? #=> true
+ */
+
+static VALUE
+rb_ary_empty_p(ary)
+ VALUE ary;
+{
+ if (RARRAY(ary)->len == 0)
+ return Qtrue;
+ return Qfalse;
+}
+
+VALUE
+rb_ary_dup(ary)
+ VALUE ary;
+{
+ VALUE dup = rb_ary_new2(RARRAY(ary)->len);
+
+ DUPSETUP(dup, ary);
+ MEMCPY(RARRAY(dup)->ptr, RARRAY(ary)->ptr, VALUE, RARRAY(ary)->len);
+ RARRAY(dup)->len = RARRAY(ary)->len;
+ return dup;
+}
+
+extern VALUE rb_output_fs;
+
+static VALUE
+recursive_join(ary, arg, recur)
+ VALUE ary;
+ VALUE *arg;
+ int recur;
+{
+ if (recur) {
+ return rb_str_new2("[...]");
+ }
+ return rb_ary_join(arg[0], arg[1]);
+}
+
+VALUE
+rb_ary_join(ary, sep)
+ VALUE ary, sep;
+{
+ long len = 1, i;
+ int taint = Qfalse;
+ VALUE result, tmp;
+
+ if (RARRAY(ary)->len == 0) return rb_str_new(0, 0);
+ if (OBJ_TAINTED(ary) || OBJ_TAINTED(sep)) taint = Qtrue;
+
+ for (i=0; i<RARRAY(ary)->len; i++) {
+ tmp = rb_check_string_type(RARRAY(ary)->ptr[i]);
+ len += NIL_P(tmp) ? 10 : RSTRING(tmp)->len;
+ }
+ if (!NIL_P(sep)) {
+ StringValue(sep);
+ len += RSTRING(sep)->len * (RARRAY(ary)->len - 1);
+ }
+ result = rb_str_buf_new(len);
+ for (i=0; i<RARRAY(ary)->len; i++) {
+ tmp = RARRAY(ary)->ptr[i];
+ switch (TYPE(tmp)) {
+ case T_STRING:
+ break;
+ case T_ARRAY:
+ {
+ VALUE args[2];
+
+ args[0] = tmp;
+ args[1] = sep;
+ tmp = rb_exec_recursive(recursive_join, ary, (VALUE)args);
+ }
+ break;
+ default:
+ tmp = rb_obj_as_string(tmp);
+ }
+ if (i > 0 && !NIL_P(sep))
+ rb_str_buf_append(result, sep);
+ rb_str_buf_append(result, tmp);
+ if (OBJ_TAINTED(tmp)) taint = Qtrue;
+ }
+
+ if (taint) OBJ_TAINT(result);
+ return result;
+}
+
+/*
+ * call-seq:
+ * array.join(sep=$,) -> str
+ *
+ * Returns a string created by converting each element of the array to
+ * a string, separated by <i>sep</i>.
+ *
+ * [ "a", "b", "c" ].join #=> "abc"
+ * [ "a", "b", "c" ].join("-") #=> "a-b-c"
+ */
+
+static VALUE
+rb_ary_join_m(argc, argv, ary)
+ int argc;
+ VALUE *argv;
+ VALUE ary;
+{
+ VALUE sep;
+
+ rb_scan_args(argc, argv, "01", &sep);
+ if (NIL_P(sep)) sep = rb_output_fs;
+
+ return rb_ary_join(ary, sep);
+}
+
+/*
+ * call-seq:
+ * array.to_s -> string
+ *
+ * Returns _self_<code>.join</code>.
+ *
+ * [ "a", "e", "i", "o" ].to_s #=> "aeio"
+ *
+ */
+
+VALUE
+rb_ary_to_s(ary)
+ VALUE ary;
+{
+ if (RARRAY(ary)->len == 0) return rb_str_new(0, 0);
+
+ return rb_ary_join(ary, rb_output_fs);
+}
+
+static VALUE
+inspect_ary(ary, dummy, recur)
+ VALUE ary;
+ VALUE dummy;
+ int recur;
+{
+ int tainted = OBJ_TAINTED(ary);
+ long i;
+ VALUE s, str;
+
+ if (recur) return rb_tainted_str_new2("[...]");
+ str = rb_str_buf_new2("[");
+ for (i=0; i<RARRAY(ary)->len; i++) {
+ s = rb_inspect(RARRAY(ary)->ptr[i]);
+ if (OBJ_TAINTED(s)) tainted = Qtrue;
+ if (i > 0) rb_str_buf_cat2(str, ", ");
+ rb_str_buf_append(str, s);
+ }
+ rb_str_buf_cat2(str, "]");
+ if (tainted) OBJ_TAINT(str);
+ return str;
+}
+
+/*
+ * call-seq:
+ * array.inspect -> string
+ *
+ * Create a printable version of <i>array</i>.
+ */
+
+static VALUE
+rb_ary_inspect(ary)
+ VALUE ary;
+{
+ if (RARRAY(ary)->len == 0) return rb_str_new2("[]");
+ return rb_exec_recursive(inspect_ary, ary, 0);
+}
+
+/*
+ * call-seq:
+ * array.to_a -> array
+ *
+ * Returns _self_. If called on a subclass of Array, converts
+ * the receiver to an Array object.
+ */
+
+static VALUE
+rb_ary_to_a(ary)
+ VALUE ary;
+{
+ if (rb_obj_class(ary) != rb_cArray) {
+ VALUE dup = rb_ary_new2(RARRAY(ary)->len);
+ rb_ary_replace(dup, ary);
+ return dup;
+ }
+ return ary;
+}
+
+/*
+ * call-seq:
+ * array.to_ary -> array
+ *
+ * Returns _self_.
+ */
+
+static VALUE
+rb_ary_to_ary_m(ary)
+ VALUE ary;
+{
+ return ary;
+}
+
+VALUE
+rb_ary_reverse(ary)
+ VALUE ary;
+{
+ VALUE *p1, *p2;
+ VALUE tmp;
+
+ rb_ary_modify(ary);
+ if (RARRAY(ary)->len > 1) {
+ p1 = RARRAY(ary)->ptr;
+ p2 = p1 + RARRAY(ary)->len - 1; /* points last item */
+
+ while (p1 < p2) {
+ tmp = *p1;
+ *p1++ = *p2;
+ *p2-- = tmp;
+ }
+ }
+ return ary;
+}
+
+/*
+ * call-seq:
+ * array.reverse! -> array
+ *
+ * Reverses _self_ in place.
+ *
+ * a = [ "a", "b", "c" ]
+ * a.reverse! #=> ["c", "b", "a"]
+ * a #=> ["c", "b", "a"]
+ */
+
+static VALUE
+rb_ary_reverse_bang(ary)
+ VALUE ary;
+{
+ return rb_ary_reverse(ary);
+}
+
+/*
+ * call-seq:
+ * array.reverse -> an_array
+ *
+ * Returns a new array containing <i>self</i>'s elements in reverse order.
+ *
+ * [ "a", "b", "c" ].reverse #=> ["c", "b", "a"]
+ * [ 1 ].reverse #=> [1]
+ */
+
+static VALUE
+rb_ary_reverse_m(ary)
+ VALUE ary;
+{
+ return rb_ary_reverse(rb_ary_dup(ary));
+}
+
+struct ary_sort_data {
+ VALUE ary;
+ VALUE *ptr;
+ long len;
+};
+
+static void
+ary_sort_check(data)
+ struct ary_sort_data *data;
+{
+ if (RARRAY(data->ary)->ptr != data->ptr || RARRAY(data->ary)->len != data->len) {
+ rb_raise(rb_eRuntimeError, "array modified during sort");
+ }
+}
+
+static int
+sort_1(a, b, data)
+ VALUE *a, *b;
+ struct ary_sort_data *data;
+{
+ VALUE retval = rb_yield_values(2, *a, *b);
+ int n;
+
+ n = rb_cmpint(retval, *a, *b);
+ ary_sort_check(data);
+ return n;
+}
+
+static int
+sort_2(ap, bp, data)
+ VALUE *ap, *bp;
+ struct ary_sort_data *data;
+{
+ VALUE retval;
+ VALUE a = *ap, b = *bp;
+ int n;
+
+ if (FIXNUM_P(a) && FIXNUM_P(b)) {
+ if ((long)a > (long)b) return 1;
+ if ((long)a < (long)b) return -1;
+ return 0;
+ }
+ if (TYPE(a) == T_STRING && TYPE(b) == T_STRING) {
+ return rb_str_cmp(a, b);
+ }
+
+ retval = rb_funcall(a, id_cmp, 1, b);
+ n = rb_cmpint(retval, a, b);
+ ary_sort_check(data);
+
+ return n;
+}
+
+static VALUE
+sort_internal(ary)
+ VALUE ary;
+{
+ struct ary_sort_data data;
+
+ data.ary = ary;
+ data.ptr = RARRAY(ary)->ptr; data.len = RARRAY(ary)->len;
+ qsort(RARRAY(ary)->ptr, RARRAY(ary)->len, sizeof(VALUE),
+ rb_block_given_p()?sort_1:sort_2, &data);
+ return ary;
+}
+
+static VALUE
+sort_unlock(ary)
+ VALUE ary;
+{
+ FL_UNSET(ary, ARY_TMPLOCK);
+ return ary;
+}
+
+/*
+ * call-seq:
+ * array.sort! -> array
+ * array.sort! {| a,b | block } -> array
+ *
+ * Sorts _self_. Comparisons for
+ * the sort will be done using the <code><=></code> operator or using
+ * an optional code block. The block implements a comparison between
+ * <i>a</i> and <i>b</i>, returning -1, 0, or +1. See also
+ * <code>Enumerable#sort_by</code>.
+ *
+ * a = [ "d", "a", "e", "c", "b" ]
+ * a.sort #=> ["a", "b", "c", "d", "e"]
+ * a.sort {|x,y| y <=> x } #=> ["e", "d", "c", "b", "a"]
+ */
+
+VALUE
+rb_ary_sort_bang(ary)
+ VALUE ary;
+{
+ rb_ary_modify(ary);
+ if (RARRAY(ary)->len > 1) {
+ FL_SET(ary, ARY_TMPLOCK); /* prohibit modification during sort */
+ rb_ensure(sort_internal, ary, sort_unlock, ary);
+ }
+ return ary;
+}
+
+/*
+ * call-seq:
+ * array.sort -> an_array
+ * array.sort {| a,b | block } -> an_array
+ *
+ * Returns a new array created by sorting <i>self</i>. Comparisons for
+ * the sort will be done using the <code><=></code> operator or using
+ * an optional code block. The block implements a comparison between
+ * <i>a</i> and <i>b</i>, returning -1, 0, or +1. See also
+ * <code>Enumerable#sort_by</code>.
+ *
+ * a = [ "d", "a", "e", "c", "b" ]
+ * a.sort #=> ["a", "b", "c", "d", "e"]
+ * a.sort {|x,y| y <=> x } #=> ["e", "d", "c", "b", "a"]
+ */
+
+VALUE
+rb_ary_sort(ary)
+ VALUE ary;
+{
+ ary = rb_ary_dup(ary);
+ rb_ary_sort_bang(ary);
+ return ary;
+}
+
+/*
+ * call-seq:
+ * array.collect {|item| block } -> an_array
+ * array.map {|item| block } -> an_array
+ *
+ * Invokes <i>block</i> once for each element of <i>self</i>. Creates a
+ * new array containing the values returned by the block.
+ * See also <code>Enumerable#collect</code>.
+ *
+ * a = [ "a", "b", "c", "d" ]
+ * a.collect {|x| x + "!" } #=> ["a!", "b!", "c!", "d!"]
+ * a #=> ["a", "b", "c", "d"]
+ */
+
+static VALUE
+rb_ary_collect(ary)
+ VALUE ary;
+{
+ long i;
+ VALUE collect;
+
+ if (!rb_block_given_p()) {
+ return rb_ary_new4(RARRAY(ary)->len, RARRAY(ary)->ptr);
+ }
+
+ collect = rb_ary_new2(RARRAY(ary)->len);
+ for (i = 0; i < RARRAY(ary)->len; i++) {
+ rb_ary_push(collect, rb_yield(RARRAY(ary)->ptr[i]));
+ }
+ return collect;
+}
+
+/*
+ * call-seq:
+ * array.collect! {|item| block } -> array
+ * array.map! {|item| block } -> array
+ *
+ * Invokes the block once for each element of _self_, replacing the
+ * element with the value returned by _block_.
+ * See also <code>Enumerable#collect</code>.
+ *
+ * a = [ "a", "b", "c", "d" ]
+ * a.collect! {|x| x + "!" }
+ * a #=> [ "a!", "b!", "c!", "d!" ]
+ */
+
+static VALUE
+rb_ary_collect_bang(ary)
+ VALUE ary;
+{
+ long i;
+
+ rb_ary_modify(ary);
+ for (i = 0; i < RARRAY(ary)->len; i++) {
+ rb_ary_store(ary, i, rb_yield(RARRAY(ary)->ptr[i]));
+ }
+ return ary;
+}
+
+VALUE
+rb_get_values_at(obj, olen, argc, argv, func)
+ VALUE obj;
+ long olen;
+ int argc;
+ VALUE *argv;
+ VALUE (*func) _((VALUE,long));
+{
+ VALUE result = rb_ary_new2(argc);
+ long beg, len, i, j;
+
+ for (i=0; i<argc; i++) {
+ if (FIXNUM_P(argv[i])) {
+ rb_ary_push(result, (*func)(obj, FIX2LONG(argv[i])));
+ continue;
+ }
+ /* check if idx is Range */
+ switch (rb_range_beg_len(argv[i], &beg, &len, olen, 0)) {
+ case Qfalse:
+ break;
+ case Qnil:
+ continue;
+ default:
+ for (j=0; j<len; j++) {
+ rb_ary_push(result, (*func)(obj, j+beg));
+ }
+ continue;
+ }
+ rb_ary_push(result, (*func)(obj, NUM2LONG(argv[i])));
+ }
+ return result;
+}
+
+/*
+ * call-seq:
+ * array.values_at(selector,... ) -> an_array
+ *
+ * Returns an array containing the elements in
+ * _self_ corresponding to the given selector(s). The selectors
+ * may be either integer indices or ranges.
+ * See also <code>Array#select</code>.
+ *
+ * a = %w{ a b c d e f }
+ * a.values_at(1, 3, 5)
+ * a.values_at(1, 3, 5, 7)
+ * a.values_at(-1, -3, -5, -7)
+ * a.values_at(1..3, 2...5)
+ */
+
+static VALUE
+rb_ary_values_at(argc, argv, ary)
+ int argc;
+ VALUE *argv;
+ VALUE ary;
+{
+ return rb_get_values_at(ary, RARRAY(ary)->len, argc, argv, rb_ary_entry);
+}
+
+/*
+ * call-seq:
+ * array.select {|item| block } -> an_array
+ *
+ * Invokes the block passing in successive elements from <i>array</i>,
+ * returning an array containing those elements for which the block
+ * returns a true value (equivalent to <code>Enumerable#select</code>).
+ *
+ * a = %w{ a b c d e f }
+ * a.select {|v| v =~ /[aeiou]/} #=> ["a", "e"]
+ */
+
+static VALUE
+rb_ary_select(ary)
+ VALUE ary;
+{
+ VALUE result;
+ long i;
+
+ result = rb_ary_new2(RARRAY(ary)->len);
+ for (i = 0; i < RARRAY(ary)->len; i++) {
+ if (RTEST(rb_yield(RARRAY(ary)->ptr[i]))) {
+ rb_ary_push(result, rb_ary_elt(ary, i));
+ }
+ }
+ return result;
+}
+
+/*
+ * call-seq:
+ * array.delete(obj) -> obj or nil
+ * array.delete(obj) { block } -> obj or nil
+ *
+ * Deletes items from <i>self</i> that are equal to <i>obj</i>. If
+ * the item is not found, returns <code>nil</code>. If the optional
+ * code block is given, returns the result of <i>block</i> if the item
+ * is not found.
+ *
+ * a = [ "a", "b", "b", "b", "c" ]
+ * a.delete("b") #=> "b"
+ * a #=> ["a", "c"]
+ * a.delete("z") #=> nil
+ * a.delete("z") { "not found" } #=> "not found"
+ */
+
+VALUE
+rb_ary_delete(ary, item)
+ VALUE ary;
+ VALUE item;
+{
+ long i1, i2;
+
+ for (i1 = i2 = 0; i1 < RARRAY(ary)->len; i1++) {
+ VALUE e = RARRAY(ary)->ptr[i1];
+
+ if (rb_equal(e, item)) continue;
+ if (i1 != i2) {
+ rb_ary_store(ary, i2, e);
+ }
+ i2++;
+ }
+ if (RARRAY(ary)->len == i2) {
+ if (rb_block_given_p()) {
+ return rb_yield(item);
+ }
+ return Qnil;
+ }
+
+ rb_ary_modify(ary);
+ if (RARRAY(ary)->len > i2) {
+ RARRAY(ary)->len = i2;
+ if (i2 * 2 < RARRAY(ary)->aux.capa &&
+ RARRAY(ary)->aux.capa > ARY_DEFAULT_SIZE) {
+ REALLOC_N(RARRAY(ary)->ptr, VALUE, i2 * 2);
+ RARRAY(ary)->aux.capa = i2 * 2;
+ }
+ }
+
+ return item;
+}
+
+VALUE
+rb_ary_delete_at(ary, pos)
+ VALUE ary;
+ long pos;
+{
+ long i, len = RARRAY(ary)->len;
+ VALUE del;
+
+ if (pos >= len) return Qnil;
+ if (pos < 0) {
+ pos += len;
+ if (pos < 0) return Qnil;
+ }
+
+ rb_ary_modify(ary);
+ del = RARRAY(ary)->ptr[pos];
+ for (i = pos + 1; i < len; i++, pos++) {
+ RARRAY(ary)->ptr[pos] = RARRAY(ary)->ptr[i];
+ }
+ RARRAY(ary)->len = pos;
+
+ return del;
+}
+
+/*
+ * call-seq:
+ * array.delete_at(index) -> obj or nil
+ *
+ * Deletes the element at the specified index, returning that element,
+ * or <code>nil</code> if the index is out of range. See also
+ * <code>Array#slice!</code>.
+ *
+ * a = %w( ant bat cat dog )
+ * a.delete_at(2) #=> "cat"
+ * a #=> ["ant", "bat", "dog"]
+ * a.delete_at(99) #=> nil
+ */
+
+static VALUE
+rb_ary_delete_at_m(ary, pos)
+ VALUE ary, pos;
+{
+ return rb_ary_delete_at(ary, NUM2LONG(pos));
+}
+
+/*
+ * call-seq:
+ * array.slice!(index) -> obj or nil
+ * array.slice!(start, length) -> sub_array or nil
+ * array.slice!(range) -> sub_array or nil
+ *
+ * Deletes the element(s) given by an index (optionally with a length)
+ * or by a range. Returns the deleted object, subarray, or
+ * <code>nil</code> if the index is out of range. Equivalent to:
+ *
+ * def slice!(*args)
+ * result = self[*args]
+ * self[*args] = nil
+ * result
+ * end
+ *
+ * a = [ "a", "b", "c" ]
+ * a.slice!(1) #=> "b"
+ * a #=> ["a", "c"]
+ * a.slice!(-1) #=> "c"
+ * a #=> ["a"]
+ * a.slice!(100) #=> nil
+ * a #=> ["a"]
+ */
+
+static VALUE
+rb_ary_slice_bang(argc, argv, ary)
+ int argc;
+ VALUE *argv;
+ VALUE ary;
+{
+ VALUE arg1, arg2;
+ long pos, len;
+
+ if (rb_scan_args(argc, argv, "11", &arg1, &arg2) == 2) {
+ pos = NUM2LONG(arg1);
+ len = NUM2LONG(arg2);
+ delete_pos_len:
+ if (pos < 0) {
+ pos = RARRAY(ary)->len + pos;
+ }
+ arg2 = rb_ary_subseq(ary, pos, len);
+ rb_ary_splice(ary, pos, len, Qundef); /* Qnil/rb_ary_new2(0) */
+ return arg2;
+ }
+
+ if (!FIXNUM_P(arg1) && rb_range_beg_len(arg1, &pos, &len, RARRAY(ary)->len, 1)) {
+ goto delete_pos_len;
+ }
+
+ return rb_ary_delete_at(ary, NUM2LONG(arg1));
+}
+
+/*
+ * call-seq:
+ * array.reject! {|item| block } -> array or nil
+ *
+ * Equivalent to <code>Array#delete_if</code>, deleting elements from
+ * _self_ for which the block evaluates to true, but returns
+ * <code>nil</code> if no changes were made. Also see
+ * <code>Enumerable#reject</code>.
+ */
+
+static VALUE
+rb_ary_reject_bang(ary)
+ VALUE ary;
+{
+ long i1, i2;
+
+ rb_ary_modify(ary);
+ for (i1 = i2 = 0; i1 < RARRAY(ary)->len; i1++) {
+ VALUE v = RARRAY(ary)->ptr[i1];
+ if (RTEST(rb_yield(v))) continue;
+ if (i1 != i2) {
+ rb_ary_store(ary, i2, v);
+ }
+ i2++;
+ }
+ if (RARRAY(ary)->len == i2) return Qnil;
+ if (i2 < RARRAY(ary)->len)
+ RARRAY(ary)->len = i2;
+
+ return ary;
+}
+
+/*
+ * call-seq:
+ * array.reject {|item| block } -> an_array
+ *
+ * Returns a new array containing the items in _self_
+ * for which the block is not true.
+ */
+
+static VALUE
+rb_ary_reject(ary)
+ VALUE ary;
+{
+ ary = rb_ary_dup(ary);
+ rb_ary_reject_bang(ary);
+ return ary;
+}
+
+/*
+ * call-seq:
+ * array.delete_if {|item| block } -> array
+ *
+ * Deletes every element of <i>self</i> for which <i>block</i> evaluates
+ * to <code>true</code>.
+ *
+ * a = [ "a", "b", "c" ]
+ * a.delete_if {|x| x >= "b" } #=> ["a"]
+ */
+
+static VALUE
+rb_ary_delete_if(ary)
+ VALUE ary;
+{
+ rb_ary_reject_bang(ary);
+ return ary;
+}
+
+/*
+ * call-seq:
+ * array.zip(arg, ...) -> an_array
+ * array.zip(arg, ...) {| arr | block } -> nil
+ *
+ * Converts any arguments to arrays, then merges elements of
+ * <i>self</i> with corresponding elements from each argument. This
+ * generates a sequence of <code>self.size</code> <em>n</em>-element
+ * arrays, where <em>n</em> is one more that the count of arguments. If
+ * the size of any argument is less than <code>enumObj.size</code>,
+ * <code>nil</code> values are supplied. If a block given, it is
+ * invoked for each output array, otherwise an array of arrays is
+ * returned.
+ *
+ * a = [ 4, 5, 6 ]
+ * b = [ 7, 8, 9 ]
+ *
+ * [1,2,3].zip(a, b) #=> [[1, 4, 7], [2, 5, 8], [3, 6, 9]]
+ * [1,2].zip(a,b) #=> [[1, 4, 7], [2, 5, 8]]
+ * a.zip([1,2],[8]) #=> [[4,1,8], [5,2,nil], [6,nil,nil]]
+ */
+
+static VALUE
+rb_ary_zip(argc, argv, ary)
+ int argc;
+ VALUE *argv;
+ VALUE ary;
+{
+ int i, j;
+ long len;
+ VALUE result;
+
+ for (i=0; i<argc; i++) {
+ argv[i] = to_a(argv[i]);
+ }
+ if (rb_block_given_p()) {
+ for (i=0; i<RARRAY(ary)->len; i++) {
+ VALUE tmp = rb_ary_new2(argc+1);
+
+ rb_ary_push(tmp, rb_ary_elt(ary, i));
+ for (j=0; j<argc; j++) {
+ rb_ary_push(tmp, rb_ary_elt(argv[j], i));
+ }
+ rb_yield(tmp);
+ }
+ return Qnil;
+ }
+ len = RARRAY(ary)->len;
+ result = rb_ary_new2(len);
+ for (i=0; i<len; i++) {
+ VALUE tmp = rb_ary_new2(argc+1);
+
+ rb_ary_push(tmp, rb_ary_elt(ary, i));
+ for (j=0; j<argc; j++) {
+ rb_ary_push(tmp, rb_ary_elt(argv[j], i));
+ }
+ rb_ary_push(result, tmp);
+ }
+ return result;
+}
+
+/*
+ * call-seq:
+ * array.transpose -> an_array
+ *
+ * Assumes that <i>self</i> is an array of arrays and transposes the
+ * rows and columns.
+ *
+ * a = [[1,2], [3,4], [5,6]]
+ * a.transpose #=> [[1, 3, 5], [2, 4, 6]]
+ */
+
+static VALUE
+rb_ary_transpose(ary)
+ VALUE ary;
+{
+ long elen = -1, alen, i, j;
+ VALUE tmp, result = 0;
+
+ alen = RARRAY(ary)->len;
+ if (alen == 0) return rb_ary_dup(ary);
+ for (i=0; i<alen; i++) {
+ tmp = to_ary(rb_ary_elt(ary, i));
+ if (elen < 0) { /* first element */
+ elen = RARRAY(tmp)->len;
+ result = rb_ary_new2(elen);
+ for (j=0; j<elen; j++) {
+ rb_ary_store(result, j, rb_ary_new2(alen));
+ }
+ }
+ else if (elen != RARRAY(tmp)->len) {
+ rb_raise(rb_eIndexError, "element size differs (%d should be %d)",
+ RARRAY(tmp)->len, elen);
+ }
+ for (j=0; j<elen; j++) {
+ rb_ary_store(rb_ary_elt(result, j), i, rb_ary_elt(tmp, j));
+ }
+ }
+ return result;
+}
+
+/*
+ * call-seq:
+ * array.replace(other_array) -> array
+ *
+ * Replaces the contents of <i>self</i> with the contents of
+ * <i>other_array</i>, truncating or expanding if necessary.
+ *
+ * a = [ "a", "b", "c", "d", "e" ]
+ * a.replace([ "x", "y", "z" ]) #=> ["x", "y", "z"]
+ * a #=> ["x", "y", "z"]
+ */
+
+static VALUE
+rb_ary_replace(copy, orig)
+ VALUE copy, orig;
+{
+ VALUE shared;
+
+ rb_ary_modify(copy);
+ orig = to_ary(orig);
+ if (copy == orig) return copy;
+ shared = ary_make_shared(orig);
+ if (RARRAY(copy)->ptr && !FL_TEST(copy, ELTS_SHARED))
+ free(RARRAY(copy)->ptr);
+ RARRAY(copy)->ptr = RARRAY(orig)->ptr;
+ RARRAY(copy)->len = RARRAY(orig)->len;
+ RARRAY(copy)->aux.shared = shared;
+ FL_SET(copy, ELTS_SHARED);
+
+ return copy;
+}
+
+/*
+ * call-seq:
+ * array.clear -> array
+ *
+ * Removes all elements from _self_.
+ *
+ * a = [ "a", "b", "c", "d", "e" ]
+ * a.clear #=> [ ]
+ */
+
+VALUE
+rb_ary_clear(ary)
+ VALUE ary;
+{
+ rb_ary_modify(ary);
+ RARRAY(ary)->len = 0;
+ if (ARY_DEFAULT_SIZE * 2 < RARRAY(ary)->aux.capa) {
+ REALLOC_N(RARRAY(ary)->ptr, VALUE, ARY_DEFAULT_SIZE * 2);
+ RARRAY(ary)->aux.capa = ARY_DEFAULT_SIZE * 2;
+ }
+ return ary;
+}
+
+/*
+ * call-seq:
+ * array.fill(obj) -> array
+ * array.fill(obj, start [, length]) -> array
+ * array.fill(obj, range ) -> array
+ * array.fill {|index| block } -> array
+ * array.fill(start [, length] ) {|index| block } -> array
+ * array.fill(range) {|index| block } -> array
+ *
+ * The first three forms set the selected elements of <i>self</i> (which
+ * may be the entire array) to <i>obj</i>. A <i>start</i> of
+ * <code>nil</code> is equivalent to zero. A <i>length</i> of
+ * <code>nil</code> is equivalent to <i>self.length</i>. The last three
+ * forms fill the array with the value of the block. The block is
+ * passed the absolute index of each element to be filled.
+ *
+ * a = [ "a", "b", "c", "d" ]
+ * a.fill("x") #=> ["x", "x", "x", "x"]
+ * a.fill("z", 2, 2) #=> ["x", "x", "z", "z"]
+ * a.fill("y", 0..1) #=> ["y", "y", "z", "z"]
+ * a.fill {|i| i*i} #=> [0, 1, 4, 9]
+ * a.fill(-2) {|i| i*i*i} #=> [0, 1, 8, 27]
+ */
+
+static VALUE
+rb_ary_fill(argc, argv, ary)
+ int argc;
+ VALUE *argv;
+ VALUE ary;
+{
+ VALUE item, arg1, arg2;
+ long beg, end, len;
+ VALUE *p, *pend;
+ int block_p = Qfalse;
+
+ if (rb_block_given_p()) {
+ block_p = Qtrue;
+ rb_scan_args(argc, argv, "02", &arg1, &arg2);
+ argc += 1; /* hackish */
+ }
+ else {
+ rb_scan_args(argc, argv, "12", &item, &arg1, &arg2);
+ }
+ switch (argc) {
+ case 1:
+ beg = 0;
+ len = RARRAY(ary)->len;
+ break;
+ case 2:
+ if (rb_range_beg_len(arg1, &beg, &len, RARRAY(ary)->len, 1)) {
+ break;
+ }
+ /* fall through */
+ case 3:
+ beg = NIL_P(arg1) ? 0 : NUM2LONG(arg1);
+ if (beg < 0) {
+ beg = RARRAY(ary)->len + beg;
+ if (beg < 0) beg = 0;
+ }
+ len = NIL_P(arg2) ? RARRAY(ary)->len - beg : NUM2LONG(arg2);
+ break;
+ }
+ rb_ary_modify(ary);
+ end = beg + len;
+ if (end > RARRAY(ary)->len) {
+ if (end >= RARRAY(ary)->aux.capa) {
+ REALLOC_N(RARRAY(ary)->ptr, VALUE, end);
+ RARRAY(ary)->aux.capa = end;
+ }
+ if (beg > RARRAY(ary)->len) {
+ rb_mem_clear(RARRAY(ary)->ptr + RARRAY(ary)->len, end - RARRAY(ary)->len);
+ }
+ RARRAY(ary)->len = end;
+ }
+
+ if (block_p) {
+ VALUE v;
+ long i;
+
+ for (i=beg; i<end; i++) {
+ v = rb_yield(LONG2NUM(i));
+ if (i>=RARRAY(ary)->len) break;
+ RARRAY(ary)->ptr[i] = v;
+ }
+ }
+ else {
+ p = RARRAY(ary)->ptr + beg;
+ pend = p + len;
+ while (p < pend) {
+ *p++ = item;
+ }
+ }
+ return ary;
+}
+
+/*
+ * call-seq:
+ * array + other_array -> an_array
+ *
+ * Concatenation---Returns a new array built by concatenating the
+ * two arrays together to produce a third array.
+ *
+ * [ 1, 2, 3 ] + [ 4, 5 ] #=> [ 1, 2, 3, 4, 5 ]
+ */
+
+VALUE
+rb_ary_plus(x, y)
+ VALUE x, y;
+{
+ VALUE z;
+ long len;
+
+ y = to_ary(y);
+ len = RARRAY(x)->len + RARRAY(y)->len;
+ z = rb_ary_new2(len);
+ MEMCPY(RARRAY(z)->ptr, RARRAY(x)->ptr, VALUE, RARRAY(x)->len);
+ MEMCPY(RARRAY(z)->ptr + RARRAY(x)->len, RARRAY(y)->ptr, VALUE, RARRAY(y)->len);
+ RARRAY(z)->len = len;
+ return z;
+}
+
+/*
+ * call-seq:
+ * array.concat(other_array) -> array
+ *
+ * Appends the elements in other_array to _self_.
+ *
+ * [ "a", "b" ].concat( ["c", "d"] ) #=> [ "a", "b", "c", "d" ]
+ */
+
+
+VALUE
+rb_ary_concat(x, y)
+ VALUE x, y;
+{
+ y = to_ary(y);
+ if (RARRAY(y)->len > 0) {
+ rb_ary_splice(x, RARRAY(x)->len, 0, y);
+ }
+ return x;
+}
+
+
+/*
+ * call-seq:
+ * array * int -> an_array
+ * array * str -> a_string
+ *
+ * Repetition---With a String argument, equivalent to
+ * self.join(str). Otherwise, returns a new array
+ * built by concatenating the _int_ copies of _self_.
+ *
+ *
+ * [ 1, 2, 3 ] * 3 #=> [ 1, 2, 3, 1, 2, 3, 1, 2, 3 ]
+ * [ 1, 2, 3 ] * "," #=> "1,2,3"
+ *
+ */
+
+static VALUE
+rb_ary_times(ary, times)
+ VALUE ary, times;
+{
+ VALUE ary2, tmp;
+ long i, len;
+
+ tmp = rb_check_string_type(times);
+ if (!NIL_P(tmp)) {
+ return rb_ary_join(ary, tmp);
+ }
+
+ len = NUM2LONG(times);
+ if (len == 0) return ary_new(rb_obj_class(ary), 0);
+ if (len < 0) {
+ rb_raise(rb_eArgError, "negative argument");
+ }
+ if (LONG_MAX/len < RARRAY(ary)->len) {
+ rb_raise(rb_eArgError, "argument too big");
+ }
+ len *= RARRAY(ary)->len;
+
+ ary2 = ary_new(rb_obj_class(ary), len);
+ RARRAY(ary2)->len = len;
+
+ for (i=0; i<len; i+=RARRAY(ary)->len) {
+ MEMCPY(RARRAY(ary2)->ptr+i, RARRAY(ary)->ptr, VALUE, RARRAY(ary)->len);
+ }
+ OBJ_INFECT(ary2, ary);
+
+ return ary2;
+}
+
+/*
+ * call-seq:
+ * array.assoc(obj) -> an_array or nil
+ *
+ * Searches through an array whose elements are also arrays
+ * comparing _obj_ with the first element of each contained array
+ * using obj.==.
+ * Returns the first contained array that matches (that
+ * is, the first associated array),
+ * or +nil+ if no match is found.
+ * See also <code>Array#rassoc</code>.
+ *
+ * s1 = [ "colors", "red", "blue", "green" ]
+ * s2 = [ "letters", "a", "b", "c" ]
+ * s3 = "foo"
+ * a = [ s1, s2, s3 ]
+ * a.assoc("letters") #=> [ "letters", "a", "b", "c" ]
+ * a.assoc("foo") #=> nil
+ */
+
+VALUE
+rb_ary_assoc(ary, key)
+ VALUE ary, key;
+{
+ long i;
+ VALUE v;
+
+ for (i = 0; i < RARRAY(ary)->len; ++i) {
+ v = RARRAY(ary)->ptr[i];
+ if (TYPE(v) == T_ARRAY &&
+ RARRAY(v)->len > 0 &&
+ rb_equal(RARRAY(v)->ptr[0], key))
+ return v;
+ }
+ return Qnil;
+}
+
+/*
+ * call-seq:
+ * array.rassoc(key) -> an_array or nil
+ *
+ * Searches through the array whose elements are also arrays. Compares
+ * <em>key</em> with the second element of each contained array using
+ * <code>==</code>. Returns the first contained array that matches. See
+ * also <code>Array#assoc</code>.
+ *
+ * a = [ [ 1, "one"], [2, "two"], [3, "three"], ["ii", "two"] ]
+ * a.rassoc("two") #=> [2, "two"]
+ * a.rassoc("four") #=> nil
+ */
+
+VALUE
+rb_ary_rassoc(ary, value)
+ VALUE ary, value;
+{
+ long i;
+ VALUE v;
+
+ for (i = 0; i < RARRAY(ary)->len; ++i) {
+ v = RARRAY(ary)->ptr[i];
+ if (TYPE(v) == T_ARRAY &&
+ RARRAY(v)->len > 1 &&
+ rb_equal(RARRAY(v)->ptr[1], value))
+ return v;
+ }
+ return Qnil;
+}
+
+/*
+ * call-seq:
+ * array == other_array -> bool
+ *
+ * Equality---Two arrays are equal if they contain the same number
+ * of elements and if each element is equal to (according to
+ * Object.==) the corresponding element in the other array.
+ *
+ * [ "a", "c" ] == [ "a", "c", 7 ] #=> false
+ * [ "a", "c", 7 ] == [ "a", "c", 7 ] #=> true
+ * [ "a", "c", 7 ] == [ "a", "d", "f" ] #=> false
+ *
+ */
+
+static VALUE
+rb_ary_equal(ary1, ary2)
+ VALUE ary1, ary2;
+{
+ long i;
+
+ if (ary1 == ary2) return Qtrue;
+ if (TYPE(ary2) != T_ARRAY) {
+ if (!rb_respond_to(ary2, rb_intern("to_ary"))) {
+ return Qfalse;
+ }
+ return rb_equal(ary2, ary1);
+ }
+ if (RARRAY(ary1)->len != RARRAY(ary2)->len) return Qfalse;
+ for (i=0; i<RARRAY(ary1)->len; i++) {
+ if (!rb_equal(rb_ary_elt(ary1, i), rb_ary_elt(ary2, i)))
+ return Qfalse;
+ }
+ return Qtrue;
+}
+
+/*
+ * call-seq:
+ * array.eql?(other) -> true or false
+ *
+ * Returns <code>true</code> if _array_ and _other_ are the same object,
+ * or are both arrays with the same content.
+ */
+
+static VALUE
+rb_ary_eql(ary1, ary2)
+ VALUE ary1, ary2;
+{
+ long i;
+
+ if (ary1 == ary2) return Qtrue;
+ if (TYPE(ary2) != T_ARRAY) return Qfalse;
+ if (RARRAY(ary1)->len != RARRAY(ary2)->len) return Qfalse;
+ for (i=0; i<RARRAY(ary1)->len; i++) {
+ if (!rb_eql(rb_ary_elt(ary1, i), rb_ary_elt(ary2, i)))
+ return Qfalse;
+ }
+ return Qtrue;
+}
+
+static VALUE
+recursive_hash(ary, dummy, recur)
+ VALUE ary, dummy;
+ int recur;
+{
+ long i, h;
+ VALUE n;
+
+ if (recur) {
+ return LONG2FIX(0);
+ }
+ h = RARRAY(ary)->len;
+ for (i=0; i<RARRAY(ary)->len; i++) {
+ h = (h << 1) | (h<0 ? 1 : 0);
+ n = rb_hash(RARRAY(ary)->ptr[i]);
+ h ^= NUM2LONG(n);
+ }
+ return LONG2FIX(h);
+}
+
+/*
+ * call-seq:
+ * array.hash -> fixnum
+ *
+ * Compute a hash-code for this array. Two arrays with the same content
+ * will have the same hash code (and will compare using <code>eql?</code>).
+ */
+
+static VALUE
+rb_ary_hash(ary)
+ VALUE ary;
+{
+ return rb_exec_recursive(recursive_hash, ary, 0);
+}
+
+/*
+ * call-seq:
+ * array.include?(obj) -> true or false
+ *
+ * Returns <code>true</code> if the given object is present in
+ * <i>self</i> (that is, if any object <code>==</code> <i>anObject</i>),
+ * <code>false</code> otherwise.
+ *
+ * a = [ "a", "b", "c" ]
+ * a.include?("b") #=> true
+ * a.include?("z") #=> false
+ */
+
+VALUE
+rb_ary_includes(ary, item)
+ VALUE ary;
+ VALUE item;
+{
+ long i;
+
+ for (i=0; i<RARRAY(ary)->len; i++) {
+ if (rb_equal(RARRAY(ary)->ptr[i], item)) {
+ return Qtrue;
+ }
+ }
+ return Qfalse;
+}
+
+
+/*
+ * call-seq:
+ * array <=> other_array -> -1, 0, +1
+ *
+ * Comparison---Returns an integer (-1, 0,
+ * or +1) if this array is less than, equal to, or greater than
+ * other_array. Each object in each array is compared
+ * (using <=>). If any value isn't
+ * equal, then that inequality is the return value. If all the
+ * values found are equal, then the return is based on a
+ * comparison of the array lengths. Thus, two arrays are
+ * ``equal'' according to <code>Array#<=></code> if and only if they have
+ * the same length and the value of each element is equal to the
+ * value of the corresponding element in the other array.
+ *
+ * [ "a", "a", "c" ] <=> [ "a", "b", "c" ] #=> -1
+ * [ 1, 2, 3, 4, 5, 6 ] <=> [ 1, 2 ] #=> +1
+ *
+ */
+
+VALUE
+rb_ary_cmp(ary1, ary2)
+ VALUE ary1, ary2;
+{
+ long i, len;
+
+ ary2 = to_ary(ary2);
+ len = RARRAY(ary1)->len;
+ if (len > RARRAY(ary2)->len) {
+ len = RARRAY(ary2)->len;
+ }
+ for (i=0; i<len; i++) {
+ VALUE v = rb_funcall(rb_ary_elt(ary1, i), id_cmp, 1, rb_ary_elt(ary2, i));
+ if (v != INT2FIX(0)) {
+ return v;
+ }
+ }
+ len = RARRAY(ary1)->len - RARRAY(ary2)->len;
+ if (len == 0) return INT2FIX(0);
+ if (len > 0) return INT2FIX(1);
+ return INT2FIX(-1);
+}
+
+static VALUE
+ary_make_hash(ary1, ary2)
+ VALUE ary1, ary2;
+{
+ VALUE hash = rb_hash_new();
+ long i;
+
+ for (i=0; i<RARRAY(ary1)->len; i++) {
+ rb_hash_aset(hash, RARRAY(ary1)->ptr[i], Qtrue);
+ }
+ if (ary2) {
+ for (i=0; i<RARRAY(ary2)->len; i++) {
+ rb_hash_aset(hash, RARRAY(ary2)->ptr[i], Qtrue);
+ }
+ }
+ return hash;
+}
+
+/*
+ * call-seq:
+ * array - other_array -> an_array
+ *
+ * Array Difference---Returns a new array that is a copy of
+ * the original array, removing any items that also appear in
+ * other_array. (If you need set-like behavior, see the
+ * library class Set.)
+ *
+ * [ 1, 1, 2, 2, 3, 3, 4, 5 ] - [ 1, 2, 4 ] #=> [ 3, 3, 5 ]
+ */
+
+static VALUE
+rb_ary_diff(ary1, ary2)
+ VALUE ary1, ary2;
+{
+ VALUE ary3, hash;
+ long i;
+
+ hash = ary_make_hash(to_ary(ary2), 0);
+ ary3 = rb_ary_new();
+
+ for (i=0; i<RARRAY(ary1)->len; i++) {
+ if (st_lookup(RHASH(hash)->tbl, RARRAY(ary1)->ptr[i], 0)) continue;
+ rb_ary_push(ary3, rb_ary_elt(ary1, i));
+ }
+ return ary3;
+}
+
+/*
+ * call-seq:
+ * array & other_array
+ *
+ * Set Intersection---Returns a new array
+ * containing elements common to the two arrays, with no duplicates.
+ *
+ * [ 1, 1, 3, 5 ] & [ 1, 2, 3 ] #=> [ 1, 3 ]
+ */
+
+
+static VALUE
+rb_ary_and(ary1, ary2)
+ VALUE ary1, ary2;
+{
+ VALUE hash, ary3, v, vv;
+ long i;
+
+ ary2 = to_ary(ary2);
+ ary3 = rb_ary_new2(RARRAY(ary1)->len < RARRAY(ary2)->len ?
+ RARRAY(ary1)->len : RARRAY(ary2)->len);
+ hash = ary_make_hash(ary2, 0);
+
+ for (i=0; i<RARRAY(ary1)->len; i++) {
+ v = vv = rb_ary_elt(ary1, i);
+ if (st_delete(RHASH(hash)->tbl, (st_data_t*)&vv, 0)) {
+ rb_ary_push(ary3, v);
+ }
+ }
+
+ return ary3;
+}
+
+/*
+ * call-seq:
+ * array | other_array -> an_array
+ *
+ * Set Union---Returns a new array by joining this array with
+ * other_array, removing duplicates.
+ *
+ * [ "a", "b", "c" ] | [ "c", "d", "a" ]
+ * #=> [ "a", "b", "c", "d" ]
+ */
+
+static VALUE
+rb_ary_or(ary1, ary2)
+ VALUE ary1, ary2;
+{
+ VALUE hash, ary3;
+ VALUE v, vv;
+ long i;
+
+ ary2 = to_ary(ary2);
+ ary3 = rb_ary_new2(RARRAY(ary1)->len+RARRAY(ary2)->len);
+ hash = ary_make_hash(ary1, ary2);
+
+ for (i=0; i<RARRAY(ary1)->len; i++) {
+ v = vv = rb_ary_elt(ary1, i);
+ if (st_delete(RHASH(hash)->tbl, (st_data_t*)&vv, 0)) {
+ rb_ary_push(ary3, v);
+ }
+ }
+ for (i=0; i<RARRAY(ary2)->len; i++) {
+ v = vv = rb_ary_elt(ary2, i);
+ if (st_delete(RHASH(hash)->tbl, (st_data_t*)&vv, 0)) {
+ rb_ary_push(ary3, v);
+ }
+ }
+ return ary3;
+}
+
+/*
+ * call-seq:
+ * array.uniq! -> array or nil
+ *
+ * Removes duplicate elements from _self_.
+ * Returns <code>nil</code> if no changes are made (that is, no
+ * duplicates are found).
+ *
+ * a = [ "a", "a", "b", "b", "c" ]
+ * a.uniq! #=> ["a", "b", "c"]
+ * b = [ "a", "b", "c" ]
+ * b.uniq! #=> nil
+ */
+
+static VALUE
+rb_ary_uniq_bang(ary)
+ VALUE ary;
+{
+ VALUE hash, v, vv;
+ long i, j;
+
+ hash = ary_make_hash(ary, 0);
+
+ if (RARRAY(ary)->len == RHASH(hash)->tbl->num_entries) {
+ return Qnil;
+ }
+ for (i=j=0; i<RARRAY(ary)->len; i++) {
+ v = vv = rb_ary_elt(ary, i);
+ if (st_delete(RHASH(hash)->tbl, (st_data_t*)&vv, 0)) {
+ rb_ary_store(ary, j++, v);
+ }
+ }
+ RARRAY(ary)->len = j;
+
+ return ary;
+}
+
+/*
+ * call-seq:
+ * array.uniq -> an_array
+ *
+ * Returns a new array by removing duplicate values in <i>self</i>.
+ *
+ * a = [ "a", "a", "b", "b", "c" ]
+ * a.uniq #=> ["a", "b", "c"]
+ */
+
+static VALUE
+rb_ary_uniq(ary)
+ VALUE ary;
+{
+ ary = rb_ary_dup(ary);
+ rb_ary_uniq_bang(ary);
+ return ary;
+}
+
+/*
+ * call-seq:
+ * array.compact! -> array or nil
+ *
+ * Removes +nil+ elements from array.
+ * Returns +nil+ if no changes were made.
+ *
+ * [ "a", nil, "b", nil, "c" ].compact! #=> [ "a", "b", "c" ]
+ * [ "a", "b", "c" ].compact! #=> nil
+ */
+
+static VALUE
+rb_ary_compact_bang(ary)
+ VALUE ary;
+{
+ VALUE *p, *t, *end;
+
+ rb_ary_modify(ary);
+ p = t = RARRAY(ary)->ptr;
+ end = p + RARRAY(ary)->len;
+
+ while (t < end) {
+ if (NIL_P(*t)) t++;
+ else *p++ = *t++;
+ }
+ if (RARRAY(ary)->len == (p - RARRAY(ary)->ptr)) {
+ return Qnil;
+ }
+ RARRAY(ary)->len = RARRAY(ary)->aux.capa = (p - RARRAY(ary)->ptr);
+ REALLOC_N(RARRAY(ary)->ptr, VALUE, RARRAY(ary)->len);
+
+ return ary;
+}
+
+/*
+ * call-seq:
+ * array.compact -> an_array
+ *
+ * Returns a copy of _self_ with all +nil+ elements removed.
+ *
+ * [ "a", nil, "b", nil, "c", nil ].compact
+ * #=> [ "a", "b", "c" ]
+ */
+
+static VALUE
+rb_ary_compact(ary)
+ VALUE ary;
+{
+ ary = rb_ary_dup(ary);
+ rb_ary_compact_bang(ary);
+ return ary;
+}
+
+/*
+ * call-seq:
+ * array.nitems -> int
+ *
+ * Returns the number of non-<code>nil</code> elements in _self_.
+ * May be zero.
+ *
+ * [ 1, nil, 3, nil, 5 ].nitems #=> 3
+ */
+
+static VALUE
+rb_ary_nitems(ary)
+ VALUE ary;
+{
+ long n = 0;
+ VALUE *p, *pend;
+
+ p = RARRAY(ary)->ptr;
+ pend = p + RARRAY(ary)->len;
+
+ while (p < pend) {
+ if (!NIL_P(*p)) n++;
+ p++;
+ }
+ return LONG2NUM(n);
+}
+
+static long
+flatten(ary, idx, ary2, memo)
+ VALUE ary;
+ long idx;
+ VALUE ary2, memo;
+{
+ VALUE id;
+ long i = idx;
+ long n, lim = idx + RARRAY(ary2)->len;
+
+ id = rb_obj_id(ary2);
+ if (rb_ary_includes(memo, id)) {
+ rb_raise(rb_eArgError, "tried to flatten recursive array");
+ }
+ rb_ary_push(memo, id);
+ rb_ary_splice(ary, idx, 1, ary2);
+ while (i < lim) {
+ VALUE tmp;
+
+ tmp = rb_check_array_type(rb_ary_elt(ary, i));
+ if (!NIL_P(tmp)) {
+ n = flatten(ary, i, tmp, memo);
+ i += n; lim += n;
+ }
+ i++;
+ }
+ rb_ary_pop(memo);
+
+ return lim - idx - 1; /* returns number of increased items */
+}
+
+/*
+ * call-seq:
+ * array.flatten! -> array or nil
+ *
+ * Flattens _self_ in place.
+ * Returns <code>nil</code> if no modifications were made (i.e.,
+ * <i>array</i> contains no subarrays.)
+ *
+ * a = [ 1, 2, [3, [4, 5] ] ]
+ * a.flatten! #=> [1, 2, 3, 4, 5]
+ * a.flatten! #=> nil
+ * a #=> [1, 2, 3, 4, 5]
+ */
+
+static VALUE
+rb_ary_flatten_bang(ary)
+ VALUE ary;
+{
+ long i = 0;
+ int mod = 0;
+ VALUE memo = Qnil;
+
+ while (i<RARRAY(ary)->len) {
+ VALUE ary2 = RARRAY(ary)->ptr[i];
+ VALUE tmp;
+
+ tmp = rb_check_array_type(ary2);
+ if (!NIL_P(tmp)) {
+ if (NIL_P(memo)) {
+ memo = rb_ary_new();
+ }
+ i += flatten(ary, i, tmp, memo);
+ mod = 1;
+ }
+ i++;
+ }
+ if (mod == 0) return Qnil;
+ return ary;
+}
+
+/*
+ * call-seq:
+ * array.flatten -> an_array
+ *
+ * Returns a new array that is a one-dimensional flattening of this
+ * array (recursively). That is, for every element that is an array,
+ * extract its elements into the new array.
+ *
+ * s = [ 1, 2, 3 ] #=> [1, 2, 3]
+ * t = [ 4, 5, 6, [7, 8] ] #=> [4, 5, 6, [7, 8]]
+ * a = [ s, t, 9, 10 ] #=> [[1, 2, 3], [4, 5, 6, [7, 8]], 9, 10]
+ * a.flatten #=> [1, 2, 3, 4, 5, 6, 7, 8, 9, 10
+ */
+
+static VALUE
+rb_ary_flatten(ary)
+ VALUE ary;
+{
+ ary = rb_ary_dup(ary);
+ rb_ary_flatten_bang(ary);
+ return ary;
+}
+
+
+/* Arrays are ordered, integer-indexed collections of any object.
+ * Array indexing starts at 0, as in C or Java. A negative index is
+ * assumed to be relative to the end of the array---that is, an index of -1
+ * indicates the last element of the array, -2 is the next to last
+ * element in the array, and so on.
+ */
+
+void
+Init_Array()
+{
+ rb_cArray = rb_define_class("Array", rb_cObject);
+ rb_include_module(rb_cArray, rb_mEnumerable);
+
+ rb_define_alloc_func(rb_cArray, ary_alloc);
+ rb_define_singleton_method(rb_cArray, "[]", rb_ary_s_create, -1);
+ rb_define_method(rb_cArray, "initialize", rb_ary_initialize, -1);
+ rb_define_method(rb_cArray, "initialize_copy", rb_ary_replace, 1);
+
+ rb_define_method(rb_cArray, "to_s", rb_ary_to_s, 0);
+ rb_define_method(rb_cArray, "inspect", rb_ary_inspect, 0);
+ rb_define_method(rb_cArray, "to_a", rb_ary_to_a, 0);
+ rb_define_method(rb_cArray, "to_ary", rb_ary_to_ary_m, 0);
+ rb_define_method(rb_cArray, "frozen?", rb_ary_frozen_p, 0);
+
+ rb_define_method(rb_cArray, "==", rb_ary_equal, 1);
+ rb_define_method(rb_cArray, "eql?", rb_ary_eql, 1);
+ rb_define_method(rb_cArray, "hash", rb_ary_hash, 0);
+
+ rb_define_method(rb_cArray, "[]", rb_ary_aref, -1);
+ rb_define_method(rb_cArray, "[]=", rb_ary_aset, -1);
+ rb_define_method(rb_cArray, "at", rb_ary_at, 1);
+ rb_define_method(rb_cArray, "fetch", rb_ary_fetch, -1);
+ rb_define_method(rb_cArray, "first", rb_ary_first, -1);
+ rb_define_method(rb_cArray, "last", rb_ary_last, -1);
+ rb_define_method(rb_cArray, "concat", rb_ary_concat, 1);
+ rb_define_method(rb_cArray, "<<", rb_ary_push, 1);
+ rb_define_method(rb_cArray, "push", rb_ary_push_m, -1);
+ rb_define_method(rb_cArray, "pop", rb_ary_pop_m, -1);
+ rb_define_method(rb_cArray, "shift", rb_ary_shift_m, -1);
+ rb_define_method(rb_cArray, "unshift", rb_ary_unshift_m, -1);
+ rb_define_method(rb_cArray, "insert", rb_ary_insert, -1);
+ rb_define_method(rb_cArray, "each", rb_ary_each, 0);
+ rb_define_method(rb_cArray, "each_index", rb_ary_each_index, 0);
+ rb_define_method(rb_cArray, "reverse_each", rb_ary_reverse_each, 0);
+ rb_define_method(rb_cArray, "length", rb_ary_length, 0);
+ rb_define_alias(rb_cArray, "size", "length");
+ rb_define_method(rb_cArray, "empty?", rb_ary_empty_p, 0);
+ rb_define_method(rb_cArray, "index", rb_ary_index, -1);
+ rb_define_method(rb_cArray, "rindex", rb_ary_rindex, -1);
+ rb_define_method(rb_cArray, "join", rb_ary_join_m, -1);
+ rb_define_method(rb_cArray, "reverse", rb_ary_reverse_m, 0);
+ rb_define_method(rb_cArray, "reverse!", rb_ary_reverse_bang, 0);
+ rb_define_method(rb_cArray, "sort", rb_ary_sort, 0);
+ rb_define_method(rb_cArray, "sort!", rb_ary_sort_bang, 0);
+ rb_define_method(rb_cArray, "collect", rb_ary_collect, 0);
+ rb_define_method(rb_cArray, "collect!", rb_ary_collect_bang, 0);
+ rb_define_method(rb_cArray, "map", rb_ary_collect, 0);
+ rb_define_method(rb_cArray, "map!", rb_ary_collect_bang, 0);
+ rb_define_method(rb_cArray, "select", rb_ary_select, 0);
+ rb_define_method(rb_cArray, "values_at", rb_ary_values_at, -1);
+ rb_define_method(rb_cArray, "delete", rb_ary_delete, 1);
+ rb_define_method(rb_cArray, "delete_at", rb_ary_delete_at_m, 1);
+ rb_define_method(rb_cArray, "delete_if", rb_ary_delete_if, 0);
+ rb_define_method(rb_cArray, "reject", rb_ary_reject, 0);
+ rb_define_method(rb_cArray, "reject!", rb_ary_reject_bang, 0);
+ rb_define_method(rb_cArray, "zip", rb_ary_zip, -1);
+ rb_define_method(rb_cArray, "transpose", rb_ary_transpose, 0);
+ rb_define_method(rb_cArray, "replace", rb_ary_replace, 1);
+ rb_define_method(rb_cArray, "clear", rb_ary_clear, 0);
+ rb_define_method(rb_cArray, "fill", rb_ary_fill, -1);
+ rb_define_method(rb_cArray, "include?", rb_ary_includes, 1);
+ rb_define_method(rb_cArray, "<=>", rb_ary_cmp, 1);
+
+ rb_define_method(rb_cArray, "slice", rb_ary_aref, -1);
+ rb_define_method(rb_cArray, "slice!", rb_ary_slice_bang, -1);
+
+ rb_define_method(rb_cArray, "assoc", rb_ary_assoc, 1);
+ rb_define_method(rb_cArray, "rassoc", rb_ary_rassoc, 1);
+
+ rb_define_method(rb_cArray, "+", rb_ary_plus, 1);
+ rb_define_method(rb_cArray, "*", rb_ary_times, 1);
+
+ rb_define_method(rb_cArray, "-", rb_ary_diff, 1);
+ rb_define_method(rb_cArray, "&", rb_ary_and, 1);
+ rb_define_method(rb_cArray, "|", rb_ary_or, 1);
+
+ rb_define_method(rb_cArray, "uniq", rb_ary_uniq, 0);
+ rb_define_method(rb_cArray, "uniq!", rb_ary_uniq_bang, 0);
+ rb_define_method(rb_cArray, "compact", rb_ary_compact, 0);
+ rb_define_method(rb_cArray, "compact!", rb_ary_compact_bang, 0);
+ rb_define_method(rb_cArray, "flatten", rb_ary_flatten, 0);
+ rb_define_method(rb_cArray, "flatten!", rb_ary_flatten_bang, 0);
+ rb_define_method(rb_cArray, "nitems", rb_ary_nitems, 0);
+
+ id_cmp = rb_intern("<=>");
+
+ rb_cValues = rb_define_class("Values", rb_cArray);
+}
+/**********************************************************************
+ ascii.c - Oniguruma (regular expression library)
+**********************************************************************/
+/*-
+ * Copyright (c) 2002-2004 K.Kosako <sndgk393 AT ybb DOT ne DOT jp>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "regenc.h"
+
+static int
+ascii_is_code_ctype(OnigCodePoint code, unsigned int ctype)
+{
+ if (code < 128)
+ return ONIGENC_IS_ASCII_CODE_CTYPE(code, ctype);
+ else
+ return FALSE;
+}
+
+OnigEncodingType OnigEncodingASCII = {
+ onigenc_single_byte_mbc_enc_len,
+ "US-ASCII", /* name */
+ 1, /* max byte length */
+ 1, /* min byte length */
+ ONIGENC_AMBIGUOUS_MATCH_ASCII_CASE,
+ {
+ (OnigCodePoint )'\\' /* esc */
+ , (OnigCodePoint )ONIG_INEFFECTIVE_META_CHAR /* anychar '.' */
+ , (OnigCodePoint )ONIG_INEFFECTIVE_META_CHAR /* anytime '*' */
+ , (OnigCodePoint )ONIG_INEFFECTIVE_META_CHAR /* zero or one time '?' */
+ , (OnigCodePoint )ONIG_INEFFECTIVE_META_CHAR /* one or more time '+' */
+ , (OnigCodePoint )ONIG_INEFFECTIVE_META_CHAR /* anychar anytime */
+ },
+ onigenc_is_mbc_newline_0x0a,
+ onigenc_single_byte_mbc_to_code,
+ onigenc_single_byte_code_to_mbclen,
+ onigenc_single_byte_code_to_mbc,
+ onigenc_ascii_mbc_to_normalize,
+ onigenc_ascii_is_mbc_ambiguous,
+ onigenc_ascii_get_all_pair_ambig_codes,
+ onigenc_nothing_get_all_comp_ambig_codes,
+ ascii_is_code_ctype,
+ onigenc_not_support_get_ctype_code_range,
+ onigenc_single_byte_left_adjust_char_head,
+ onigenc_always_true_is_allowed_reverse_match
+};
+/**********************************************************************
+
+ bignum.c -
+
+ $Author: murphy $
+ $Date: 2005-11-05 04:33:55 +0100 (Sa, 05 Nov 2005) $
+ created at: Fri Jun 10 00:48:55 JST 1994
+
+ Copyright (C) 1993-2003 Yukihiro Matsumoto
+
+**********************************************************************/
+
+#include "ruby.h"
+
+#include <math.h>
+#include <ctype.h>
+#ifdef HAVE_IEEEFP_H
+#include <ieeefp.h>
+#endif
+
+VALUE rb_cBignum;
+
+#if defined __MINGW32__
+#define USHORT _USHORT
+#endif
+
+#define BDIGITS(x) ((BDIGIT*)RBIGNUM(x)->digits)
+#define BITSPERDIG (SIZEOF_BDIGITS*CHAR_BIT)
+#define BIGRAD ((BDIGIT_DBL)1 << BITSPERDIG)
+#define DIGSPERLONG ((unsigned int)(SIZEOF_LONG/SIZEOF_BDIGITS))
+#if HAVE_LONG_LONG
+# define DIGSPERLL ((unsigned int)(SIZEOF_LONG_LONG/SIZEOF_BDIGITS))
+#endif
+#define BIGUP(x) ((BDIGIT_DBL)(x) << BITSPERDIG)
+#define BIGDN(x) RSHIFT(x,BITSPERDIG)
+#define BIGLO(x) ((BDIGIT)((x) & (BIGRAD-1)))
+#define BDIGMAX ((BDIGIT)-1)
+
+#define BIGZEROP(x) (RBIGNUM(x)->len == 0 || (RBIGNUM(x)->len == 1 && BDIGITS(x)[0] == 0))
+
+static VALUE
+bignew_1(klass, len, sign)
+ VALUE klass;
+ long len;
+ char sign;
+{
+ NEWOBJ(big, struct RBignum);
+ OBJSETUP(big, klass, T_BIGNUM);
+ big->sign = sign;
+ big->len = len;
+ big->digits = ALLOC_N(BDIGIT, len);
+
+ return (VALUE)big;
+}
+
+#define bignew(len,sign) bignew_1(rb_cBignum,len,sign)
+
+VALUE
+rb_big_clone(x)
+ VALUE x;
+{
+ VALUE z = bignew_1(CLASS_OF(x), RBIGNUM(x)->len, RBIGNUM(x)->sign);
+
+ MEMCPY(BDIGITS(z), BDIGITS(x), BDIGIT, RBIGNUM(x)->len);
+ return z;
+}
+
+static void
+get2comp(x, carry) /* get 2's complement */
+ VALUE x;
+ int carry;
+{
+ long i = RBIGNUM(x)->len;
+ BDIGIT *ds = BDIGITS(x);
+ BDIGIT_DBL num;
+
+ while (i--) ds[i] = ~ds[i];
+ i = 0; num = 1;
+ do {
+ num += ds[i];
+ ds[i++] = BIGLO(num);
+ num = BIGDN(num);
+ } while (i < RBIGNUM(x)->len);
+ if (!carry) return;
+ if ((ds[RBIGNUM(x)->len-1] & (1<<(BITSPERDIG-1))) == 0) {
+ REALLOC_N(RBIGNUM(x)->digits, BDIGIT, ++RBIGNUM(x)->len);
+ ds = BDIGITS(x);
+ ds[RBIGNUM(x)->len-1] = ~0;
+ }
+}
+
+void
+rb_big_2comp(x) /* get 2's complement */
+ VALUE x;
+{
+ get2comp(x, Qtrue);
+}
+
+static VALUE
+bignorm(x)
+ VALUE x;
+{
+ if (!FIXNUM_P(x)) {
+ long len = RBIGNUM(x)->len;
+ BDIGIT *ds = BDIGITS(x);
+
+ while (len-- && !ds[len]) ;
+ RBIGNUM(x)->len = ++len;
+
+ if (len*SIZEOF_BDIGITS <= sizeof(VALUE)) {
+ long num = 0;
+ while (len--) {
+ num = BIGUP(num) + ds[len];
+ }
+ if (num >= 0) {
+ if (RBIGNUM(x)->sign) {
+ if (POSFIXABLE(num)) return LONG2FIX(num);
+ }
+ else if (NEGFIXABLE(-(long)num)) return LONG2FIX(-(long)num);
+ }
+ }
+ }
+ return x;
+}
+
+VALUE
+rb_big_norm(x)
+ VALUE x;
+{
+ return bignorm(x);
+}
+
+VALUE
+rb_uint2big(n)
+ unsigned long n;
+{
+ BDIGIT_DBL num = n;
+ long i = 0;
+ BDIGIT *digits;
+ VALUE big;
+
+ big = bignew(DIGSPERLONG, 1);
+ digits = BDIGITS(big);
+ while (i < DIGSPERLONG) {
+ digits[i++] = BIGLO(num);
+ num = BIGDN(num);
+ }
+
+ i = DIGSPERLONG;
+ while (--i && !digits[i]) ;
+ RBIGNUM(big)->len = i+1;
+ return big;
+}
+
+VALUE
+rb_int2big(n)
+ long n;
+{
+ long neg = 0;
+ VALUE big;
+
+ if (n < 0) {
+ n = -n;
+ neg = 1;
+ }
+ big = rb_uint2big(n);
+ if (neg) {
+ RBIGNUM(big)->sign = 0;
+ }
+ return big;
+}
+
+VALUE
+rb_uint2inum(n)
+ unsigned long n;
+{
+ if (POSFIXABLE(n)) return LONG2FIX(n);
+ return rb_uint2big(n);
+}
+
+VALUE
+rb_int2inum(n)
+ long n;
+{
+ if (FIXABLE(n)) return LONG2FIX(n);
+ return rb_int2big(n);
+}
+
+#ifdef HAVE_LONG_LONG
+
+void
+rb_quad_pack(buf, val)
+ char *buf;
+ VALUE val;
+{
+ LONG_LONG q;
+
+ val = rb_to_int(val);
+ if (FIXNUM_P(val)) {
+ q = FIX2LONG(val);
+ }
+ else {
+ long len = RBIGNUM(val)->len;
+ BDIGIT *ds;
+
+ if (len > SIZEOF_LONG_LONG/SIZEOF_BDIGITS) {
+ len = SIZEOF_LONG/SIZEOF_BDIGITS;
+ }
+ ds = BDIGITS(val);
+ q = 0;
+ while (len--) {
+ q = BIGUP(q);
+ q += ds[len];
+ }
+ if (!RBIGNUM(val)->sign) q = -q;
+ }
+ memcpy(buf, (char*)&q, SIZEOF_LONG_LONG);
+}
+
+VALUE
+rb_quad_unpack(buf, sign)
+ const char *buf;
+ int sign;
+{
+ unsigned LONG_LONG q;
+ long neg = 0;
+ long i;
+ BDIGIT *digits;
+ VALUE big;
+
+ memcpy(&q, buf, SIZEOF_LONG_LONG);
+ if (sign) {
+ if (FIXABLE((LONG_LONG)q)) return LONG2FIX((LONG_LONG)q);
+ if ((LONG_LONG)q < 0) {
+ q = -(LONG_LONG)q;
+ neg = 1;
+ }
+ }
+ else {
+ if (POSFIXABLE(q)) return LONG2FIX(q);
+ }
+
+ i = 0;
+ big = bignew(DIGSPERLL, 1);
+ digits = BDIGITS(big);
+ while (i < DIGSPERLL) {
+ digits[i++] = BIGLO(q);
+ q = BIGDN(q);
+ }
+
+ i = DIGSPERLL;
+ while (i-- && !digits[i]) ;
+ RBIGNUM(big)->len = i+1;
+
+ if (neg) {
+ RBIGNUM(big)->sign = 0;
+ }
+ return bignorm(big);
+}
+
+#else
+
+#define QUAD_SIZE 8
+
+void
+rb_quad_pack(buf, val)
+ char *buf;
+ VALUE val;
+{
+ long len;
+
+ memset(buf, 0, QUAD_SIZE);
+ val = rb_to_int(val);
+ if (FIXNUM_P(val)) {
+ val = rb_int2big(FIX2LONG(val));
+ }
+ len = RBIGNUM(val)->len * SIZEOF_BDIGITS;
+ if (len > QUAD_SIZE) {
+ rb_raise(rb_eRangeError, "bignum too big to convert into `quad int'");
+ }
+ memcpy(buf, (char*)BDIGITS(val), len);
+ if (!RBIGNUM(val)->sign) {
+ len = QUAD_SIZE;
+ while (len--) {
+ *buf = ~*buf;
+ buf++;
+ }
+ }
+}
+
+#define BNEG(b) (RSHIFT(((BDIGIT*)b)[QUAD_SIZE/SIZEOF_BDIGITS-1],BITSPERDIG-1) != 0)
+
+VALUE
+rb_quad_unpack(buf, sign)
+ const char *buf;
+ int sign;
+{
+ VALUE big = bignew(QUAD_SIZE/SIZEOF_BDIGITS, 1);
+
+ memcpy((char*)BDIGITS(big), buf, QUAD_SIZE);
+ if (sign && BNEG(buf)) {
+ long len = QUAD_SIZE;
+ char *tmp = (char*)BDIGITS(big);
+
+ RBIGNUM(big)->sign = 0;
+ while (len--) {
+ *tmp = ~*tmp;
+ tmp++;
+ }
+ }
+
+ return bignorm(big);
+}
+
+#endif
+
+VALUE
+rb_cstr_to_inum(str, base, badcheck)
+ const char *str;
+ int base;
+ int badcheck;
+{
+ const char *s = str;
+ char *end;
+ char sign = 1, nondigit = 0;
+ int c;
+ BDIGIT_DBL num;
+ long len, blen = 1;
+ long i;
+ VALUE z;
+ BDIGIT *zds;
+
+ if (!str) {
+ if (badcheck) goto bad;
+ return INT2FIX(0);
+ }
+ if (badcheck) {
+ while (ISSPACE(*str)) str++;
+ }
+ else {
+ while (ISSPACE(*str) || *str == '_') str++;
+ }
+
+ if (str[0] == '+') {
+ str++;
+ }
+ else if (str[0] == '-') {
+ str++;
+ sign = 0;
+ }
+ if (str[0] == '+' || str[0] == '-') {
+ if (badcheck) goto bad;
+ return INT2FIX(0);
+ }
+ if (base <= 0) {
+ if (str[0] == '0') {
+ switch (str[1]) {
+ case 'x': case 'X':
+ base = 16;
+ break;
+ case 'b': case 'B':
+ base = 2;
+ break;
+ case 'o': case 'O':
+ base = 8;
+ break;
+ case 'd': case 'D':
+ base = 10;
+ break;
+ default:
+ base = 8;
+ }
+ }
+ else if (base < -1) {
+ base = -base;
+ }
+ else {
+ base = 10;
+ }
+ }
+ switch (base) {
+ case 2:
+ len = 1;
+ if (str[0] == '0' && (str[1] == 'b'||str[1] == 'B')) {
+ str += 2;
+ }
+ break;
+ case 3:
+ len = 2;
+ break;
+ case 8:
+ if (str[0] == '0' && (str[1] == 'o'||str[1] == 'O')) {
+ str += 2;
+ }
+ case 4: case 5: case 6: case 7:
+ len = 3;
+ break;
+ case 10:
+ if (str[0] == '0' && (str[1] == 'd'||str[1] == 'D')) {
+ str += 2;
+ }
+ case 9: case 11: case 12: case 13: case 14: case 15:
+ len = 4;
+ break;
+ case 16:
+ len = 4;
+ if (str[0] == '0' && (str[1] == 'x'||str[1] == 'X')) {
+ str += 2;
+ }
+ break;
+ default:
+ if (base < 2 || 36 < base) {
+ rb_raise(rb_eArgError, "illegal radix %d", base);
+ }
+ if (base <= 32) {
+ len = 5;
+ }
+ else {
+ len = 6;
+ }
+ break;
+ }
+ if (*str == '0') { /* squeeze preceeding 0s */
+ while (*++str == '0');
+ --str;
+ }
+ len *= strlen(str)*sizeof(char);
+
+ if (len <= (sizeof(VALUE)*CHAR_BIT)) {
+ unsigned long val = strtoul((char*)str, &end, base);
+
+ if (*end == '_') goto bigparse;
+ if (badcheck) {
+ if (end == str) goto bad; /* no number */
+ while (*end && ISSPACE(*end)) end++;
+ if (*end) goto bad; /* trailing garbage */
+ }
+
+ if (POSFIXABLE(val)) {
+ if (sign) return LONG2FIX(val);
+ else {
+ long result = -(long)val;
+ return LONG2FIX(result);
+ }
+ }
+ else {
+ VALUE big = rb_uint2big(val);
+ RBIGNUM(big)->sign = sign;
+ return bignorm(big);
+ }
+ }
+ bigparse:
+ len = (len/BITSPERDIG)+1;
+ if (badcheck && *str == '_') goto bad;
+
+ z = bignew(len, sign);
+ zds = BDIGITS(z);
+ for (i=len;i--;) zds[i]=0;
+ while (c = *str++) {
+ if (c == '_') {
+ if (badcheck) {
+ if (nondigit) goto bad;
+ nondigit = c;
+ }
+ continue;
+ }
+ else if (!ISASCII(c)) {
+ break;
+ }
+ else if (isdigit(c)) {
+ c -= '0';
+ }
+ else if (islower(c)) {
+ c -= 'a' - 10;
+ }
+ else if (isupper(c)) {
+ c -= 'A' - 10;
+ }
+ else {
+ break;
+ }
+ if (c >= base) break;
+ nondigit = 0;
+ i = 0;
+ num = c;
+ for (;;) {
+ while (i<blen) {
+ num += (BDIGIT_DBL)zds[i]*base;
+ zds[i++] = BIGLO(num);
+ num = BIGDN(num);
+ }
+ if (num) {
+ blen++;
+ continue;
+ }
+ break;
+ }
+ }
+ if (badcheck) {
+ str--;
+ if (s+1 < str && str[-1] == '_') goto bad;
+ while (*str && ISSPACE(*str)) str++;
+ if (*str) {
+ bad:
+ rb_invalid_str(s, "Integer");
+ }
+ }
+
+ return bignorm(z);
+}
+
+VALUE
+rb_str_to_inum(str, base, badcheck)
+ VALUE str;
+ int base;
+ int badcheck;
+{
+ char *s;
+ long len;
+
+ StringValue(str);
+ if (badcheck) {
+ s = StringValueCStr(str);
+ }
+ else {
+ s = RSTRING(str)->ptr;
+ }
+ if (s) {
+ len = RSTRING(str)->len;
+ if (s[len]) { /* no sentinel somehow */
+ char *p = ALLOCA_N(char, len+1);
+
+ MEMCPY(p, s, char, len);
+ p[len] = '\0';
+ s = p;
+ }
+ }
+ return rb_cstr_to_inum(s, base, badcheck);
+}
+
+#if HAVE_LONG_LONG
+
+VALUE
+rb_ull2big(n)
+ unsigned LONG_LONG n;
+{
+ BDIGIT_DBL num = n;
+ long i = 0;
+ BDIGIT *digits;
+ VALUE big;
+
+ big = bignew(DIGSPERLL, 1);
+ digits = BDIGITS(big);
+ while (i < DIGSPERLL) {
+ digits[i++] = BIGLO(num);
+ num = BIGDN(num);
+ }
+
+ i = DIGSPERLL;
+ while (i-- && !digits[i]) ;
+ RBIGNUM(big)->len = i+1;
+ return big;
+}
+
+VALUE
+rb_ll2big(n)
+ LONG_LONG n;
+{
+ long neg = 0;
+ VALUE big;
+
+ if (n < 0) {
+ n = -n;
+ neg = 1;
+ }
+ big = rb_ull2big(n);
+ if (neg) {
+ RBIGNUM(big)->sign = 0;
+ }
+ return big;
+}
+
+VALUE
+rb_ull2inum(n)
+ unsigned LONG_LONG n;
+{
+ if (POSFIXABLE(n)) return LONG2FIX(n);
+ return rb_ull2big(n);
+}
+
+VALUE
+rb_ll2inum(n)
+ LONG_LONG n;
+{
+ if (FIXABLE(n)) return LONG2FIX(n);
+ return rb_ll2big(n);
+}
+
+#endif /* HAVE_LONG_LONG */
+
+VALUE
+rb_cstr2inum(str, base)
+ const char *str;
+ int base;
+{
+ return rb_cstr_to_inum(str, base, base==0);
+}
+
+VALUE
+rb_str2inum(str, base)
+ VALUE str;
+ int base;
+{
+ return rb_str_to_inum(str, base, base==0);
+}
+
+const char ruby_digitmap[] = "0123456789abcdefghijklmnopqrstuvwxyz";
+VALUE
+rb_big2str(x, base)
+ VALUE x;
+ int base;
+{
+ volatile VALUE t;
+ BDIGIT *ds;
+ long i, j, hbase;
+ VALUE ss;
+ char *s, c;
+
+ if (FIXNUM_P(x)) {
+ return rb_fix2str(x, base);
+ }
+ i = RBIGNUM(x)->len;
+ if (BIGZEROP(x)) {
+ return rb_str_new2("0");
+ }
+ j = SIZEOF_BDIGITS*CHAR_BIT*i;
+ switch (base) {
+ case 2: break;
+ case 3:
+ j = j * 647L / 1024;
+ break;
+ case 4: case 5: case 6: case 7:
+ j /= 2;
+ break;
+ case 8: case 9:
+ j /= 3;
+ break;
+ case 10: case 11: case 12: case 13: case 14: case 15:
+ j = j * 241L / 800;
+ break;
+ case 16: case 17: case 18: case 19: case 20: case 21:
+ case 22: case 23: case 24: case 25: case 26: case 27:
+ case 28: case 29: case 30: case 31:
+ j /= 4;
+ break;
+ case 32: case 33: case 34: case 35: case 36:
+ j /= 5;
+ break;
+ default:
+ rb_raise(rb_eArgError, "illegal radix %d", base);
+ break;
+ }
+ j += 2;
+
+ hbase = base * base;
+#if SIZEOF_BDIGITS > 2
+ hbase *= hbase;
+#endif
+
+ t = rb_big_clone(x);
+ ds = BDIGITS(t);
+ ss = rb_str_new(0, j);
+ s = RSTRING(ss)->ptr;
+
+ s[0] = RBIGNUM(x)->sign ? '+' : '-';
+ while (i && j) {
+ long k = i;
+ BDIGIT_DBL num = 0;
+
+ while (k--) {
+ num = BIGUP(num) + ds[k];
+ ds[k] = (BDIGIT)(num / hbase);
+ num %= hbase;
+ }
+ if (ds[i-1] == 0) i--;
+ k = SIZEOF_BDIGITS;
+ while (k--) {
+ c = (char)(num % base);
+ s[--j] = ruby_digitmap[(int)c];
+ num /= base;
+ if (i == 0 && num == 0) break;
+ }
+ }
+ while (s[j] == '0') j++;
+ RSTRING(ss)->len -= RBIGNUM(x)->sign?j:j-1;
+ memmove(RBIGNUM(x)->sign?s:s+1, s+j, RSTRING(ss)->len);
+ s[RSTRING(ss)->len] = '\0';
+
+ return ss;
+}
+
+/*
+ * call-seq:
+ * big.to_s(base=10) => string
+ *
+ * Returns a string containing the representation of <i>big</i> radix
+ * <i>base</i> (2 through 36).
+ *
+ * 12345654321.to_s #=> "12345654321"
+ * 12345654321.to_s(2) #=> "1011011111110110111011110000110001"
+ * 12345654321.to_s(8) #=> "133766736061"
+ * 12345654321.to_s(16) #=> "2dfdbbc31"
+ * 78546939656932.to_s(36) #=> "rubyrules"
+ */
+
+static VALUE
+rb_big_to_s(argc, argv, x)
+ int argc;
+ VALUE *argv;
+ VALUE x;
+{
+ VALUE b;
+ int base;
+
+ rb_scan_args(argc, argv, "01", &b);
+ if (argc == 0) base = 10;
+ else base = NUM2INT(b);
+ return rb_big2str(x, base);
+}
+
+static unsigned long
+big2ulong(x, type, check)
+ VALUE x;
+ char *type;
+ int check;
+{
+ long len = RBIGNUM(x)->len;
+ BDIGIT_DBL num;
+ BDIGIT *ds;
+
+ if (len > SIZEOF_LONG/SIZEOF_BDIGITS) {
+ if (check)
+ rb_raise(rb_eRangeError, "bignum too big to convert into `%s'", type);
+ len = SIZEOF_LONG/SIZEOF_BDIGITS;
+ }
+ ds = BDIGITS(x);
+ num = 0;
+ while (len--) {
+ num = BIGUP(num);
+ num += ds[len];
+ }
+ return num;
+}
+
+unsigned long
+rb_big2ulong_pack(x)
+ VALUE x;
+{
+ unsigned long num = big2ulong(x, "unsigned long", Qfalse);
+ if (!RBIGNUM(x)->sign) {
+ return -num;
+ }
+ return num;
+}
+
+unsigned long
+rb_big2ulong(x)
+ VALUE x;
+{
+ unsigned long num = big2ulong(x, "unsigned long", Qtrue);
+
+ if (!RBIGNUM(x)->sign) {
+ if ((long)num < 0) {
+ rb_raise(rb_eRangeError, "bignum out of range of unsigned long");
+ }
+ return -num;
+ }
+ return num;
+}
+
+long
+rb_big2long(x)
+ VALUE x;
+{
+ unsigned long num = big2ulong(x, "long", Qtrue);
+
+ if ((long)num < 0 && (RBIGNUM(x)->sign || (long)num != LONG_MIN)) {
+ rb_raise(rb_eRangeError, "bignum too big to convert into `long'");
+ }
+ if (!RBIGNUM(x)->sign) return -(long)num;
+ return num;
+}
+
+#if HAVE_LONG_LONG
+
+static unsigned LONG_LONG
+big2ull(x, type)
+ VALUE x;
+ char *type;
+{
+ long len = RBIGNUM(x)->len;
+ BDIGIT_DBL num;
+ BDIGIT *ds;
+
+ if (len > SIZEOF_LONG_LONG/SIZEOF_BDIGITS)
+ rb_raise(rb_eRangeError, "bignum too big to convert into `%s'", type);
+ ds = BDIGITS(x);
+ num = 0;
+ while (len--) {
+ num = BIGUP(num);
+ num += ds[len];
+ }
+ return num;
+}
+
+unsigned LONG_LONG
+rb_big2ull(x)
+ VALUE x;
+{
+ unsigned LONG_LONG num = big2ull(x, "unsigned long long");
+
+ if (!RBIGNUM(x)->sign) return -num;
+ return num;
+}
+
+LONG_LONG
+rb_big2ll(x)
+ VALUE x;
+{
+ unsigned LONG_LONG num = big2ull(x, "long long");
+
+ if ((LONG_LONG)num < 0 && (RBIGNUM(x)->sign
+ || (LONG_LONG)num != LLONG_MIN)) {
+ rb_raise(rb_eRangeError, "bignum too big to convert into `long long'");
+ }
+ if (!RBIGNUM(x)->sign) return -(LONG_LONG)num;
+ return num;
+}
+
+#endif /* HAVE_LONG_LONG */
+
+static VALUE
+dbl2big(d)
+ double d;
+{
+ long i = 0;
+ BDIGIT c;
+ BDIGIT *digits;
+ VALUE z;
+ double u = (d < 0)?-d:d;
+
+ if (isinf(d)) {
+ rb_raise(rb_eFloatDomainError, d < 0 ? "-Infinity" : "Infinity");
+ }
+ if (isnan(d)) {
+ rb_raise(rb_eFloatDomainError, "NaN");
+ }
+
+ while (!POSFIXABLE(u) || 0 != (long)u) {
+ u /= (double)(BIGRAD);
+ i++;
+ }
+ z = bignew(i, d>=0);
+ digits = BDIGITS(z);
+ while (i--) {
+ u *= BIGRAD;
+ c = (BDIGIT)u;
+ u -= c;
+ digits[i] = c;
+ }
+
+ return z;
+}
+
+VALUE
+rb_dbl2big(d)
+ double d;
+{
+ return bignorm(dbl2big(d));
+}
+
+double
+rb_big2dbl(x)
+ VALUE x;
+{
+ double d = 0.0;
+ long i = RBIGNUM(x)->len;
+ BDIGIT *ds = BDIGITS(x);
+
+ while (i--) {
+ d = ds[i] + BIGRAD*d;
+ }
+ if (isinf(d)) {
+ rb_warn("Bignum out of Float range");
+ d = HUGE_VAL;
+ }
+ if (!RBIGNUM(x)->sign) d = -d;
+ return d;
+}
+
+/*
+ * call-seq:
+ * big.to_f -> float
+ *
+ * Converts <i>big</i> to a <code>Float</code>. If <i>big</i> doesn't
+ * fit in a <code>Float</code>, the result is infinity.
+ *
+ */
+
+static VALUE
+rb_big_to_f(x)
+ VALUE x;
+{
+ return rb_float_new(rb_big2dbl(x));
+}
+
+/*
+ * call-seq:
+ * big <=> numeric => -1, 0, +1
+ *
+ * Comparison---Returns -1, 0, or +1 depending on whether <i>big</i> is
+ * less than, equal to, or greater than <i>numeric</i>. This is the
+ * basis for the tests in <code>Comparable</code>.
+ *
+ */
+
+static VALUE
+rb_big_cmp(x, y)
+ VALUE x, y;
+{
+ long xlen = RBIGNUM(x)->len;
+
+ switch (TYPE(y)) {
+ case T_FIXNUM:
+ y = rb_int2big(FIX2LONG(y));
+ break;
+
+ case T_BIGNUM:
+ break;
+
+ case T_FLOAT:
+ return rb_dbl_cmp(rb_big2dbl(x), RFLOAT(y)->value);
+
+ default:
+ return rb_num_coerce_cmp(x, y);
+ }
+
+ if (RBIGNUM(x)->sign > RBIGNUM(y)->sign) return INT2FIX(1);
+ if (RBIGNUM(x)->sign < RBIGNUM(y)->sign) return INT2FIX(-1);
+ if (xlen < RBIGNUM(y)->len)
+ return (RBIGNUM(x)->sign) ? INT2FIX(-1) : INT2FIX(1);
+ if (xlen > RBIGNUM(y)->len)
+ return (RBIGNUM(x)->sign) ? INT2FIX(1) : INT2FIX(-1);
+
+ while(xlen-- && (BDIGITS(x)[xlen]==BDIGITS(y)[xlen]));
+ if (-1 == xlen) return INT2FIX(0);
+ return (BDIGITS(x)[xlen] > BDIGITS(y)[xlen]) ?
+ (RBIGNUM(x)->sign ? INT2FIX(1) : INT2FIX(-1)) :
+ (RBIGNUM(x)->sign ? INT2FIX(-1) : INT2FIX(1));
+}
+
+/*
+ * call-seq:
+ * big == obj => true or false
+ *
+ * Returns <code>true</code> only if <i>obj</i> has the same value
+ * as <i>big</i>. Contrast this with <code>Bignum#eql?</code>, which
+ * requires <i>obj</i> to be a <code>Bignum</code>.
+ *
+ * 68719476736 == 68719476736.0 #=> true
+ */
+
+static VALUE
+rb_big_eq(x, y)
+ VALUE x, y;
+{
+ switch (TYPE(y)) {
+ case T_FIXNUM:
+ y = rb_int2big(FIX2LONG(y));
+ break;
+ case T_BIGNUM:
+ break;
+ case T_FLOAT:
+ {
+ volatile double a, b;
+
+ a = RFLOAT(y)->value;
+ b = rb_big2dbl(x);
+ if (isnan(a) || isnan(b)) return Qfalse;
+ return (a == b)?Qtrue:Qfalse;
+ }
+ default:
+ return rb_equal(y, x);
+ }
+ if (RBIGNUM(x)->sign != RBIGNUM(y)->sign) return Qfalse;
+ if (RBIGNUM(x)->len != RBIGNUM(y)->len) return Qfalse;
+ if (MEMCMP(BDIGITS(x),BDIGITS(y),BDIGIT,RBIGNUM(y)->len) != 0) return Qfalse;
+ return Qtrue;
+}
+
+/*
+ * call-seq:
+ * big.eql?(obj) => true or false
+ *
+ * Returns <code>true</code> only if <i>obj</i> is a
+ * <code>Bignum</code> with the same value as <i>big</i>. Contrast this
+ * with <code>Bignum#==</code>, which performs type conversions.
+ *
+ * 68719476736.eql?(68719476736.0) #=> false
+ */
+
+static VALUE
+rb_big_eql(x, y)
+ VALUE x, y;
+{
+ if (TYPE(y) != T_BIGNUM) return Qfalse;
+ if (RBIGNUM(x)->sign != RBIGNUM(y)->sign) return Qfalse;
+ if (RBIGNUM(x)->len != RBIGNUM(y)->len) return Qfalse;
+ if (MEMCMP(BDIGITS(x),BDIGITS(y),BDIGIT,RBIGNUM(y)->len) != 0) return Qfalse;
+ return Qtrue;
+}
+
+/*
+ * call-seq:
+ * -big => other_big
+ *
+ * Unary minus (returns a new Bignum whose value is 0-big)
+ */
+
+static VALUE
+rb_big_uminus(x)
+ VALUE x;
+{
+ VALUE z = rb_big_clone(x);
+
+ RBIGNUM(z)->sign = !RBIGNUM(x)->sign;
+
+ return bignorm(z);
+}
+
+/*
+ * call-seq:
+ * ~big => integer
+ *
+ * Inverts the bits in big. As Bignums are conceptually infinite
+ * length, the result acts as if it had an infinite number of one
+ * bits to the left. In hex representations, this is displayed
+ * as two periods to the left of the digits.
+ *
+ * sprintf("%X", ~0x1122334455) #=> "..FEEDDCCBBAA"
+ */
+
+static VALUE
+rb_big_neg(x)
+ VALUE x;
+{
+ VALUE z = rb_big_clone(x);
+ long i = RBIGNUM(x)->len;
+ BDIGIT *ds = BDIGITS(z);
+
+ if (!RBIGNUM(x)->sign) get2comp(z, Qtrue);
+ while (i--) ds[i] = ~ds[i];
+ if (RBIGNUM(x)->sign) get2comp(z, Qfalse);
+ RBIGNUM(z)->sign = !RBIGNUM(z)->sign;
+
+ return bignorm(z);
+}
+
+static VALUE
+bigsub(x, y)
+ VALUE x, y;
+{
+ VALUE z = 0;
+ BDIGIT *zds;
+ BDIGIT_DBL_SIGNED num;
+ long i = RBIGNUM(x)->len;
+
+ /* if x is larger than y, swap */
+ if (RBIGNUM(x)->len < RBIGNUM(y)->len) {
+ z = x; x = y; y = z; /* swap x y */
+ }
+ else if (RBIGNUM(x)->len == RBIGNUM(y)->len) {
+ while (i > 0) {
+ i--;
+ if (BDIGITS(x)[i] > BDIGITS(y)[i]) {
+ break;
+ }
+ if (BDIGITS(x)[i] < BDIGITS(y)[i]) {
+ z = x; x = y; y = z; /* swap x y */
+ break;
+ }
+ }
+ }
+
+ z = bignew(RBIGNUM(x)->len, (z == 0)?1:0);
+ zds = BDIGITS(z);
+
+ for (i = 0, num = 0; i < RBIGNUM(y)->len; i++) {
+ num += (BDIGIT_DBL_SIGNED)BDIGITS(x)[i] - BDIGITS(y)[i];
+ zds[i] = BIGLO(num);
+ num = BIGDN(num);
+ }
+ while (num && i < RBIGNUM(x)->len) {
+ num += BDIGITS(x)[i];
+ zds[i++] = BIGLO(num);
+ num = BIGDN(num);
+ }
+ while (i < RBIGNUM(x)->len) {
+ zds[i] = BDIGITS(x)[i];
+ i++;
+ }
+
+ return z;
+}
+
+static VALUE
+bigadd(x, y, sign)
+ VALUE x, y;
+ char sign;
+{
+ VALUE z;
+ BDIGIT_DBL num;
+ long i, len;
+
+ sign = (sign == RBIGNUM(y)->sign);
+ if (RBIGNUM(x)->sign != sign) {
+ if (sign) return bigsub(y, x);
+ return bigsub(x, y);
+ }
+
+ if (RBIGNUM(x)->len > RBIGNUM(y)->len) {
+ len = RBIGNUM(x)->len + 1;
+ z = x; x = y; y = z;
+ }
+ else {
+ len = RBIGNUM(y)->len + 1;
+ }
+ z = bignew(len, sign);
+
+ len = RBIGNUM(x)->len;
+ for (i = 0, num = 0; i < len; i++) {
+ num += (BDIGIT_DBL)BDIGITS(x)[i] + BDIGITS(y)[i];
+ BDIGITS(z)[i] = BIGLO(num);
+ num = BIGDN(num);
+ }
+ len = RBIGNUM(y)->len;
+ while (num && i < len) {
+ num += BDIGITS(y)[i];
+ BDIGITS(z)[i++] = BIGLO(num);
+ num = BIGDN(num);
+ }
+ while (i < len) {
+ BDIGITS(z)[i] = BDIGITS(y)[i];
+ i++;
+ }
+ BDIGITS(z)[i] = (BDIGIT)num;
+
+ return z;
+}
+
+/*
+ * call-seq:
+ * big + other => Numeric
+ *
+ * Adds big and other, returning the result.
+ */
+
+VALUE
+rb_big_plus(x, y)
+ VALUE x, y;
+{
+ switch (TYPE(y)) {
+ case T_FIXNUM:
+ y = rb_int2big(FIX2LONG(y));
+ /* fall through */
+ case T_BIGNUM:
+ return bignorm(bigadd(x, y, 1));
+
+ case T_FLOAT:
+ return rb_float_new(rb_big2dbl(x) + RFLOAT(y)->value);
+
+ default:
+ return rb_num_coerce_bin(x, y);
+ }
+}
+
+/*
+ * call-seq:
+ * big - other => Numeric
+ *
+ * Subtracts other from big, returning the result.
+ */
+
+VALUE
+rb_big_minus(x, y)
+ VALUE x, y;
+{
+ switch (TYPE(y)) {
+ case T_FIXNUM:
+ y = rb_int2big(FIX2LONG(y));
+ /* fall through */
+ case T_BIGNUM:
+ return bignorm(bigadd(x, y, 0));
+
+ case T_FLOAT:
+ return rb_float_new(rb_big2dbl(x) - RFLOAT(y)->value);
+
+ default:
+ return rb_num_coerce_bin(x, y);
+ }
+}
+
+/*
+ * call-seq:
+ * big * other => Numeric
+ *
+ * Multiplies big and other, returning the result.
+ */
+
+VALUE
+rb_big_mul(x, y)
+ VALUE x, y;
+{
+ long i, j;
+ BDIGIT_DBL n = 0;
+ VALUE z;
+ BDIGIT *zds;
+
+ if (FIXNUM_P(x)) x = rb_int2big(FIX2LONG(x));
+ switch (TYPE(y)) {
+ case T_FIXNUM:
+ y = rb_int2big(FIX2LONG(y));
+ break;
+
+ case T_BIGNUM:
+ break;
+
+ case T_FLOAT:
+ return rb_float_new(rb_big2dbl(x) * RFLOAT(y)->value);
+
+ default:
+ return rb_num_coerce_bin(x, y);
+ }
+
+ j = RBIGNUM(x)->len + RBIGNUM(y)->len + 1;
+ z = bignew(j, RBIGNUM(x)->sign==RBIGNUM(y)->sign);
+ zds = BDIGITS(z);
+ while (j--) zds[j] = 0;
+ for (i = 0; i < RBIGNUM(x)->len; i++) {
+ BDIGIT_DBL dd = BDIGITS(x)[i];
+ if (dd == 0) continue;
+ n = 0;
+ for (j = 0; j < RBIGNUM(y)->len; j++) {
+ BDIGIT_DBL ee = n + (BDIGIT_DBL)dd * BDIGITS(y)[j];
+ n = zds[i + j] + ee;
+ if (ee) zds[i + j] = BIGLO(n);
+ n = BIGDN(n);
+ }
+ if (n) {
+ zds[i + j] = n;
+ }
+ }
+
+ return bignorm(z);
+}
+
+static void
+bigdivrem(x, y, divp, modp)
+ VALUE x, y;
+ VALUE *divp, *modp;
+{
+ long nx = RBIGNUM(x)->len, ny = RBIGNUM(y)->len;
+ long i, j;
+ VALUE yy, z;
+ BDIGIT *xds, *yds, *zds, *tds;
+ BDIGIT_DBL t2;
+ BDIGIT_DBL_SIGNED num;
+ BDIGIT dd, q;
+
+ if (BIGZEROP(y)) rb_num_zerodiv();
+ yds = BDIGITS(y);
+ if (nx < ny || (nx == ny && BDIGITS(x)[nx - 1] < BDIGITS(y)[ny - 1])) {
+ if (divp) *divp = rb_int2big(0);
+ if (modp) *modp = x;
+ return;
+ }
+ xds = BDIGITS(x);
+ if (ny == 1) {
+ dd = yds[0];
+ z = rb_big_clone(x);
+ zds = BDIGITS(z);
+ t2 = 0; i = nx;
+ while (i--) {
+ t2 = BIGUP(t2) + zds[i];
+ zds[i] = (BDIGIT)(t2 / dd);
+ t2 %= dd;
+ }
+ RBIGNUM(z)->sign = RBIGNUM(x)->sign==RBIGNUM(y)->sign;
+ if (modp) {
+ *modp = rb_uint2big((unsigned long)t2);
+ RBIGNUM(*modp)->sign = RBIGNUM(x)->sign;
+ }
+ if (divp) *divp = z;
+ return;
+ }
+ z = bignew(nx==ny?nx+2:nx+1, RBIGNUM(x)->sign==RBIGNUM(y)->sign);
+ zds = BDIGITS(z);
+ if (nx==ny) zds[nx+1] = 0;
+ while (!yds[ny-1]) ny--;
+
+ dd = 0;
+ q = yds[ny-1];
+ while ((q & (1<<(BITSPERDIG-1))) == 0) {
+ q <<= 1;
+ dd++;
+ }
+ if (dd) {
+ yy = rb_big_clone(y);
+ tds = BDIGITS(yy);
+ j = 0;
+ t2 = 0;
+ while (j<ny) {
+ t2 += (BDIGIT_DBL)yds[j]<<dd;
+ tds[j++] = BIGLO(t2);
+ t2 = BIGDN(t2);
+ }
+ yds = tds;
+ j = 0;
+ t2 = 0;
+ while (j<nx) {
+ t2 += (BDIGIT_DBL)xds[j]<<dd;
+ zds[j++] = BIGLO(t2);
+ t2 = BIGDN(t2);
+ }
+ zds[j] = (BDIGIT)t2;
+ }
+ else {
+ zds[nx] = 0;
+ j = nx;
+ while (j--) zds[j] = xds[j];
+ }
+
+ j = nx==ny?nx+1:nx;
+ do {
+ if (zds[j] == yds[ny-1]) q = BIGRAD-1;
+ else q = (BDIGIT)((BIGUP(zds[j]) + zds[j-1])/yds[ny-1]);
+ if (q) {
+ i = 0; num = 0; t2 = 0;
+ do { /* multiply and subtract */
+ BDIGIT_DBL ee;
+ t2 += (BDIGIT_DBL)yds[i] * q;
+ ee = num - BIGLO(t2);
+ num = (BDIGIT_DBL)zds[j - ny + i] + ee;
+ if (ee) zds[j - ny + i] = BIGLO(num);
+ num = BIGDN(num);
+ t2 = BIGDN(t2);
+ } while (++i < ny);
+ num += zds[j - ny + i] - t2;/* borrow from high digit; don't update */
+ while (num) { /* "add back" required */
+ i = 0; num = 0; q--;
+ do {
+ BDIGIT_DBL ee = num + yds[i];
+ num = (BDIGIT_DBL)zds[j - ny + i] + ee;
+ if (ee) zds[j - ny + i] = BIGLO(num);
+ num = BIGDN(num);
+ } while (++i < ny);
+ num--;
+ }
+ }
+ zds[j] = q;
+ } while (--j >= ny);
+ if (divp) { /* move quotient down in z */
+ *divp = rb_big_clone(z);
+ zds = BDIGITS(*divp);
+ j = (nx==ny ? nx+2 : nx+1) - ny;
+ for (i = 0;i < j;i++) zds[i] = zds[i+ny];
+ RBIGNUM(*divp)->len = i;
+ }
+ if (modp) { /* normalize remainder */
+ *modp = rb_big_clone(z);
+ zds = BDIGITS(*modp);
+ while (--ny && !zds[ny]); ++ny;
+ if (dd) {
+ t2 = 0; i = ny;
+ while(i--) {
+ t2 = (t2 | zds[i]) >> dd;
+ q = zds[i];
+ zds[i] = BIGLO(t2);
+ t2 = BIGUP(q);
+ }
+ }
+ RBIGNUM(*modp)->len = ny;
+ RBIGNUM(*modp)->sign = RBIGNUM(x)->sign;
+ }
+}
+
+static void
+bigdivmod(x, y, divp, modp)
+ VALUE x, y;
+ VALUE *divp, *modp;
+{
+ VALUE mod;
+
+ bigdivrem(x, y, divp, &mod);
+ if (RBIGNUM(x)->sign != RBIGNUM(y)->sign && !BIGZEROP(mod)) {
+ if (divp) *divp = bigadd(*divp, rb_int2big(1), 0);
+ if (modp) *modp = bigadd(mod, y, 1);
+ }
+ else {
+ if (divp) *divp = *divp;
+ if (modp) *modp = mod;
+ }
+}
+
+/*
+ * call-seq:
+ * big / other => Numeric
+ * big.div(other) => Numeric
+ *
+ * Divides big by other, returning the result.
+ */
+
+static VALUE
+rb_big_div(x, y)
+ VALUE x, y;
+{
+ VALUE z;
+
+ switch (TYPE(y)) {
+ case T_FIXNUM:
+ y = rb_int2big(FIX2LONG(y));
+ break;
+
+ case T_BIGNUM:
+ break;
+
+ case T_FLOAT:
+ return rb_float_new(rb_big2dbl(x) / RFLOAT(y)->value);
+
+ default:
+ return rb_num_coerce_bin(x, y);
+ }
+ bigdivmod(x, y, &z, 0);
+
+ return bignorm(z);
+}
+
+/*
+ * call-seq:
+ * big % other => Numeric
+ * big.modulo(other) => Numeric
+ *
+ * Returns big modulo other. See Numeric.divmod for more
+ * information.
+ */
+
+static VALUE
+rb_big_modulo(x, y)
+ VALUE x, y;
+{
+ VALUE z;
+
+ switch (TYPE(y)) {
+ case T_FIXNUM:
+ y = rb_int2big(FIX2LONG(y));
+ break;
+
+ case T_BIGNUM:
+ break;
+
+ default:
+ return rb_num_coerce_bin(x, y);
+ }
+ bigdivmod(x, y, 0, &z);
+
+ return bignorm(z);
+}
+
+/*
+ * call-seq:
+ * big.remainder(numeric) => number
+ *
+ * Returns the remainder after dividing <i>big</i> by <i>numeric</i>.
+ *
+ * -1234567890987654321.remainder(13731) #=> -6966
+ * -1234567890987654321.remainder(13731.24) #=> -9906.22531493148
+ */
+static VALUE
+rb_big_remainder(x, y)
+ VALUE x, y;
+{
+ VALUE z;
+
+ switch (TYPE(y)) {
+ case T_FIXNUM:
+ y = rb_int2big(FIX2LONG(y));
+ break;
+
+ case T_BIGNUM:
+ break;
+
+ default:
+ return rb_num_coerce_bin(x, y);
+ }
+ bigdivrem(x, y, 0, &z);
+
+ return bignorm(z);
+}
+
+/*
+ * call-seq:
+ * big.divmod(numeric) => array
+ *
+ * See <code>Numeric#divmod</code>.
+ *
+ */
+VALUE
+rb_big_divmod(x, y)
+ VALUE x, y;
+{
+ VALUE div, mod;
+
+ switch (TYPE(y)) {
+ case T_FIXNUM:
+ y = rb_int2big(FIX2LONG(y));
+ break;
+
+ case T_BIGNUM:
+ break;
+
+ default:
+ return rb_num_coerce_bin(x, y);
+ }
+ bigdivmod(x, y, &div, &mod);
+
+ return rb_assoc_new(bignorm(div), bignorm(mod));
+}
+
+/*
+ * call-seq:
+ * big.quo(numeric) -> float
+ *
+ * Returns the floating point result of dividing <i>big</i> by
+ * <i>numeric</i>.
+ *
+ * -1234567890987654321.quo(13731) #=> -89910996357705.5
+ * -1234567890987654321.quo(13731.24) #=> -89909424858035.7
+ *
+ */
+
+static VALUE
+rb_big_quo(x, y)
+ VALUE x, y;
+{
+ double dx = rb_big2dbl(x);
+ double dy;
+
+ switch (TYPE(y)) {
+ case T_FIXNUM:
+ dy = (double)FIX2LONG(y);
+ break;
+
+ case T_BIGNUM:
+ dy = rb_big2dbl(y);
+ break;
+
+ case T_FLOAT:
+ dy = RFLOAT(y)->value;
+ break;
+
+ default:
+ return rb_num_coerce_bin(x, y);
+ }
+ return rb_float_new(dx / dy);
+}
+
+/*
+ * call-seq:
+ * big ** exponent #=> numeric
+ *
+ * Raises _big_ to the _exponent_ power (which may be an integer, float,
+ * or anything that will coerce to a number). The result may be
+ * a Fixnum, Bignum, or Float
+ *
+ * 123456789 ** 2 #=> 15241578750190521
+ * 123456789 ** 1.2 #=> 5126464716.09932
+ * 123456789 ** -2 #=> 6.5610001194102e-17
+ */
+
+VALUE
+rb_big_pow(x, y)
+ VALUE x, y;
+{
+ double d;
+ long yy;
+
+ if (y == INT2FIX(0)) return INT2FIX(1);
+ switch (TYPE(y)) {
+ case T_FLOAT:
+ d = RFLOAT(y)->value;
+ break;
+
+ case T_BIGNUM:
+ rb_warn("in a**b, b may be too big");
+ d = rb_big2dbl(y);
+ break;
+
+ case T_FIXNUM:
+ yy = FIX2LONG(y);
+ if (yy > 0) {
+ VALUE z = x;
+
+ for (;;) {
+ yy -= 1;
+ if (yy == 0) break;
+ while (yy % 2 == 0) {
+ yy /= 2;
+ x = rb_big_mul(x, x);
+ }
+ z = rb_big_mul(z, x);
+ }
+ return bignorm(z);
+ }
+ d = (double)yy;
+ break;
+
+ default:
+ return rb_num_coerce_bin(x, y);
+ }
+ return rb_float_new(pow(rb_big2dbl(x), d));
+}
+
+/*
+ * call-seq:
+ * big & numeric => integer
+ *
+ * Performs bitwise +and+ between _big_ and _numeric_.
+ */
+
+VALUE
+rb_big_and(xx, yy)
+ VALUE xx, yy;
+{
+ volatile VALUE x, y, z;
+ BDIGIT *ds1, *ds2, *zds;
+ long i, l1, l2;
+ char sign;
+
+ x = xx;
+ y = rb_to_int(yy);
+ if (FIXNUM_P(y)) {
+ y = rb_int2big(FIX2LONG(y));
+ }
+ if (!RBIGNUM(y)->sign) {
+ y = rb_big_clone(y);
+ get2comp(y, Qtrue);
+ }
+ if (!RBIGNUM(x)->sign) {
+ x = rb_big_clone(x);
+ get2comp(x, Qtrue);
+ }
+ if (RBIGNUM(x)->len > RBIGNUM(y)->len) {
+ l1 = RBIGNUM(y)->len;
+ l2 = RBIGNUM(x)->len;
+ ds1 = BDIGITS(y);
+ ds2 = BDIGITS(x);
+ sign = RBIGNUM(y)->sign;
+ }
+ else {
+ l1 = RBIGNUM(x)->len;
+ l2 = RBIGNUM(y)->len;
+ ds1 = BDIGITS(x);
+ ds2 = BDIGITS(y);
+ sign = RBIGNUM(x)->sign;
+ }
+ z = bignew(l2, RBIGNUM(x)->sign || RBIGNUM(y)->sign);
+ zds = BDIGITS(z);
+
+ for (i=0; i<l1; i++) {
+ zds[i] = ds1[i] & ds2[i];
+ }
+ for (; i<l2; i++) {
+ zds[i] = sign?0:ds2[i];
+ }
+ if (!RBIGNUM(z)->sign) get2comp(z, Qfalse);
+ return bignorm(z);
+}
+
+/*
+ * call-seq:
+ * big | numeric => integer
+ *
+ * Performs bitwise +or+ between _big_ and _numeric_.
+ */
+
+VALUE
+rb_big_or(xx, yy)
+ VALUE xx, yy;
+{
+ volatile VALUE x, y, z;
+ BDIGIT *ds1, *ds2, *zds;
+ long i, l1, l2;
+ char sign;
+
+ x = xx;
+ y = rb_to_int(yy);
+ if (FIXNUM_P(y)) {
+ y = rb_int2big(FIX2LONG(y));
+ }
+
+ if (!RBIGNUM(y)->sign) {
+ y = rb_big_clone(y);
+ get2comp(y, Qtrue);
+ }
+ if (!RBIGNUM(x)->sign) {
+ x = rb_big_clone(x);
+ get2comp(x, Qtrue);
+ }
+ if (RBIGNUM(x)->len > RBIGNUM(y)->len) {
+ l1 = RBIGNUM(y)->len;
+ l2 = RBIGNUM(x)->len;
+ ds1 = BDIGITS(y);
+ ds2 = BDIGITS(x);
+ sign = RBIGNUM(y)->sign;
+ }
+ else {
+ l1 = RBIGNUM(x)->len;
+ l2 = RBIGNUM(y)->len;
+ ds1 = BDIGITS(x);
+ ds2 = BDIGITS(y);
+ sign = RBIGNUM(x)->sign;
+ }
+ z = bignew(l2, RBIGNUM(x)->sign && RBIGNUM(y)->sign);
+ zds = BDIGITS(z);
+
+ for (i=0; i<l1; i++) {
+ zds[i] = ds1[i] | ds2[i];
+ }
+ for (; i<l2; i++) {
+ zds[i] = sign?ds2[i]:(BIGRAD-1);
+ }
+ if (!RBIGNUM(z)->sign) get2comp(z, Qfalse);
+
+ return bignorm(z);
+}
+
+/*
+ * call-seq:
+ * big ^ numeric => integer
+ *
+ * Performs bitwise +exclusive or+ between _big_ and _numeric_.
+ */
+
+VALUE
+rb_big_xor(xx, yy)
+ VALUE xx, yy;
+{
+ volatile VALUE x, y;
+ VALUE z;
+ BDIGIT *ds1, *ds2, *zds;
+ long i, l1, l2;
+ char sign;
+
+ x = xx;
+ y = rb_to_int(yy);
+ if (FIXNUM_P(y)) {
+ y = rb_int2big(FIX2LONG(y));
+ }
+
+ if (!RBIGNUM(y)->sign) {
+ y = rb_big_clone(y);
+ get2comp(y, Qtrue);
+ }
+ if (!RBIGNUM(x)->sign) {
+ x = rb_big_clone(x);
+ get2comp(x, Qtrue);
+ }
+ if (RBIGNUM(x)->len > RBIGNUM(y)->len) {
+ l1 = RBIGNUM(y)->len;
+ l2 = RBIGNUM(x)->len;
+ ds1 = BDIGITS(y);
+ ds2 = BDIGITS(x);
+ sign = RBIGNUM(y)->sign;
+ }
+ else {
+ l1 = RBIGNUM(x)->len;
+ l2 = RBIGNUM(y)->len;
+ ds1 = BDIGITS(x);
+ ds2 = BDIGITS(y);
+ sign = RBIGNUM(x)->sign;
+ }
+ RBIGNUM(x)->sign = RBIGNUM(x)->sign?1:0;
+ RBIGNUM(y)->sign = RBIGNUM(y)->sign?1:0;
+ z = bignew(l2, !(RBIGNUM(x)->sign ^ RBIGNUM(y)->sign));
+ zds = BDIGITS(z);
+
+ for (i=0; i<l1; i++) {
+ zds[i] = ds1[i] ^ ds2[i];
+ }
+ for (; i<l2; i++) {
+ zds[i] = sign?ds2[i]:~ds2[i];
+ }
+ if (!RBIGNUM(z)->sign) get2comp(z, Qfalse);
+
+ return bignorm(z);
+}
+
+static VALUE rb_big_rshift _((VALUE,VALUE));
+
+/*
+ * call-seq:
+ * big << numeric => integer
+ *
+ * Shifts big left _numeric_ positions (right if _numeric_ is negative).
+ */
+
+VALUE
+rb_big_lshift(x, y)
+ VALUE x, y;
+{
+ BDIGIT *xds, *zds;
+ int shift = NUM2INT(y);
+ int s1 = shift/BITSPERDIG;
+ int s2 = shift%BITSPERDIG;
+ VALUE z;
+ BDIGIT_DBL num = 0;
+ long len, i;
+
+ if (shift < 0) return rb_big_rshift(x, INT2FIX(-shift));
+ len = RBIGNUM(x)->len;
+ z = bignew(len+s1+1, RBIGNUM(x)->sign);
+ zds = BDIGITS(z);
+ for (i=0; i<s1; i++) {
+ *zds++ = 0;
+ }
+ xds = BDIGITS(x);
+ for (i=0; i<len; i++) {
+ num = num | (BDIGIT_DBL)*xds++<<s2;
+ *zds++ = BIGLO(num);
+ num = BIGDN(num);
+ }
+ *zds = BIGLO(num);
+ return bignorm(z);
+}
+
+/*
+ * call-seq:
+ * big >> numeric => integer
+ *
+ * Shifts big right _numeric_ positions (left if _numeric_ is negative).
+ */
+
+static VALUE
+rb_big_rshift(x, y)
+ VALUE x, y;
+{
+ BDIGIT *xds, *zds;
+ int shift = NUM2INT(y);
+ long s1 = shift/BITSPERDIG;
+ long s2 = shift%BITSPERDIG;
+ VALUE z;
+ BDIGIT_DBL num = 0;
+ long i, j;
+
+ if (shift < 0) return rb_big_lshift(x, INT2FIX(-shift));
+
+ if (s1 > RBIGNUM(x)->len) {
+ if (RBIGNUM(x)->sign)
+ return INT2FIX(0);
+ else
+ return INT2FIX(-1);
+ }
+ if (!RBIGNUM(x)->sign) {
+ x = rb_big_clone(x);
+ get2comp(x, Qtrue);
+ }
+ xds = BDIGITS(x);
+ i = RBIGNUM(x)->len; j = i - s1;
+ z = bignew(j, RBIGNUM(x)->sign);
+ if (!RBIGNUM(x)->sign) {
+ num = ((BDIGIT_DBL)~0) << BITSPERDIG;
+ }
+ zds = BDIGITS(z);
+ while (i--, j--) {
+ num = (num | xds[i]) >> s2;
+ zds[j] = BIGLO(num);
+ num = BIGUP(xds[i]);
+ }
+ if (!RBIGNUM(x)->sign) {
+ get2comp(z, Qfalse);
+ }
+ return bignorm(z);
+}
+
+/*
+ * call-seq:
+ * big[n] -> 0, 1
+ *
+ * Bit Reference---Returns the <em>n</em>th bit in the (assumed) binary
+ * representation of <i>big</i>, where <i>big</i>[0] is the least
+ * significant bit.
+ *
+ * a = 9**15
+ * 50.downto(0) do |n|
+ * print a[n]
+ * end
+ *
+ * <em>produces:</em>
+ *
+ * 000101110110100000111000011110010100111100010111001
+ *
+ */
+
+static VALUE
+rb_big_aref(x, y)
+ VALUE x, y;
+{
+ BDIGIT *xds;
+ int shift;
+ long s1, s2;
+
+ if (TYPE(y) == T_BIGNUM) {
+ if (!RBIGNUM(y)->sign || RBIGNUM(x)->sign)
+ return INT2FIX(0);
+ return INT2FIX(1);
+ }
+ shift = NUM2INT(y);
+ if (shift < 0) return INT2FIX(0);
+ s1 = shift/BITSPERDIG;
+ s2 = shift%BITSPERDIG;
+
+ if (!RBIGNUM(x)->sign) {
+ if (s1 >= RBIGNUM(x)->len) return INT2FIX(1);
+ x = rb_big_clone(x);
+ get2comp(x, Qtrue);
+ }
+ else {
+ if (s1 >= RBIGNUM(x)->len) return INT2FIX(0);
+ }
+ xds = BDIGITS(x);
+ if (xds[s1] & (1<<s2))
+ return INT2FIX(1);
+ return INT2FIX(0);
+}
+
+/*
+ * call-seq:
+ * big.hash => fixnum
+ *
+ * Compute a hash based on the value of _big_.
+ */
+
+static VALUE
+rb_big_hash(x)
+ VALUE x;
+{
+ long i, len, key;
+ BDIGIT *digits;
+
+ key = 0; digits = BDIGITS(x); len = RBIGNUM(x)->len;
+ for (i=0; i<len; i++) {
+ key ^= *digits++;
+ }
+ return LONG2FIX(key);
+}
+
+/*
+ * MISSING: documentation
+ */
+
+static VALUE
+rb_big_coerce(x, y)
+ VALUE x, y;
+{
+ if (FIXNUM_P(y)) {
+ return rb_assoc_new(rb_int2big(FIX2LONG(y)), x);
+ }
+ else {
+ rb_raise(rb_eTypeError, "can't coerce %s to Bignum",
+ rb_obj_classname(y));
+ }
+ /* not reached */
+ return Qnil;
+}
+
+/*
+ * call-seq:
+ * big.abs -> aBignum
+ *
+ * Returns the absolute value of <i>big</i>.
+ *
+ * -1234567890987654321.abs #=> 1234567890987654321
+ */
+
+static VALUE
+rb_big_abs(x)
+ VALUE x;
+{
+ if (!RBIGNUM(x)->sign) {
+ x = rb_big_clone(x);
+ RBIGNUM(x)->sign = 1;
+ }
+ return x;
+}
+
+VALUE
+rb_big_rand(max, rand_buf)
+ VALUE max;
+ double *rand_buf;
+{
+ VALUE v;
+ long len = RBIGNUM(max)->len;
+
+ if (BIGZEROP(max)) {
+ return rb_float_new(rand_buf[0]);
+ }
+ v = bignew(len,1);
+ len--;
+ BDIGITS(v)[len] = BDIGITS(max)[len] * rand_buf[len];
+ while (len--) {
+ BDIGITS(v)[len] = ((BDIGIT)~0) * rand_buf[len];
+ }
+
+ return v;
+}
+
+/*
+ * call-seq:
+ * big.size -> integer
+ *
+ * Returns the number of bytes in the machine representation of
+ * <i>big</i>.
+ *
+ * (256**10 - 1).size #=> 12
+ * (256**20 - 1).size #=> 20
+ * (256**40 - 1).size #=> 40
+ */
+
+static VALUE
+rb_big_size(big)
+ VALUE big;
+{
+ return LONG2FIX(RBIGNUM(big)->len*SIZEOF_BDIGITS);
+}
+
+/*
+ * Bignum objects hold integers outside the range of
+ * Fixnum. Bignum objects are created
+ * automatically when integer calculations would otherwise overflow a
+ * Fixnum. When a calculation involving
+ * Bignum objects returns a result that will fit in a
+ * Fixnum, the result is automatically converted.
+ *
+ * For the purposes of the bitwise operations and <code>[]</code>, a
+ * Bignum is treated as if it were an infinite-length
+ * bitstring with 2's complement representation.
+ *
+ * While Fixnum values are immediate, Bignum
+ * objects are not---assignment and parameter passing work with
+ * references to objects, not the objects themselves.
+ *
+ */
+
+void
+Init_Bignum()
+{
+ rb_cBignum = rb_define_class("Bignum", rb_cInteger);
+
+ rb_define_method(rb_cBignum, "to_s", rb_big_to_s, -1);
+ rb_define_method(rb_cBignum, "coerce", rb_big_coerce, 1);
+ rb_define_method(rb_cBignum, "-@", rb_big_uminus, 0);
+ rb_define_method(rb_cBignum, "+", rb_big_plus, 1);
+ rb_define_method(rb_cBignum, "-", rb_big_minus, 1);
+ rb_define_method(rb_cBignum, "*", rb_big_mul, 1);
+ rb_define_method(rb_cBignum, "/", rb_big_div, 1);
+ rb_define_method(rb_cBignum, "%", rb_big_modulo, 1);
+ rb_define_method(rb_cBignum, "div", rb_big_div, 1);
+ rb_define_method(rb_cBignum, "divmod", rb_big_divmod, 1);
+ rb_define_method(rb_cBignum, "modulo", rb_big_modulo, 1);
+ rb_define_method(rb_cBignum, "remainder", rb_big_remainder, 1);
+ rb_define_method(rb_cBignum, "quo", rb_big_quo, 1);
+ rb_define_method(rb_cBignum, "**", rb_big_pow, 1);
+ rb_define_method(rb_cBignum, "&", rb_big_and, 1);
+ rb_define_method(rb_cBignum, "|", rb_big_or, 1);
+ rb_define_method(rb_cBignum, "^", rb_big_xor, 1);
+ rb_define_method(rb_cBignum, "~", rb_big_neg, 0);
+ rb_define_method(rb_cBignum, "<<", rb_big_lshift, 1);
+ rb_define_method(rb_cBignum, ">>", rb_big_rshift, 1);
+ rb_define_method(rb_cBignum, "[]", rb_big_aref, 1);
+
+ rb_define_method(rb_cBignum, "<=>", rb_big_cmp, 1);
+ rb_define_method(rb_cBignum, "==", rb_big_eq, 1);
+ rb_define_method(rb_cBignum, "eql?", rb_big_eql, 1);
+ rb_define_method(rb_cBignum, "hash", rb_big_hash, 0);
+ rb_define_method(rb_cBignum, "to_f", rb_big_to_f, 0);
+ rb_define_method(rb_cBignum, "abs", rb_big_abs, 0);
+ rb_define_method(rb_cBignum, "size", rb_big_size, 0);
+}
+/**********************************************************************
+
+ class.c -
+
+ $Author: murphy $
+ $Date: 2005-11-05 04:33:55 +0100 (Sa, 05 Nov 2005) $
+ created at: Tue Aug 10 15:05:44 JST 1993
+
+ Copyright (C) 1993-2003 Yukihiro Matsumoto
+
+**********************************************************************/
+
+#include "ruby.h"
+#include "rubysig.h"
+#include "node.h"
+#include "st.h"
+#include <ctype.h>
+
+extern st_table *rb_class_tbl;
+
+VALUE
+rb_class_boot(super)
+ VALUE super;
+{
+ NEWOBJ(klass, struct RClass);
+ OBJSETUP(klass, rb_cClass, T_CLASS);
+
+ klass->super = super;
+ klass->iv_tbl = 0;
+ klass->m_tbl = 0; /* safe GC */
+ klass->m_tbl = st_init_numtable();
+
+ OBJ_INFECT(klass, super);
+ return (VALUE)klass;
+}
+
+void
+rb_check_inheritable(super)
+ VALUE super;
+{
+ if (TYPE(super) != T_CLASS) {
+ rb_raise(rb_eTypeError, "superclass must be a Class (%s given)",
+ rb_obj_classname(super));
+ }
+ if (RBASIC(super)->flags & FL_SINGLETON) {
+ rb_raise(rb_eTypeError, "can't make subclass of singleton class");
+ }
+}
+
+VALUE
+rb_class_new(super)
+ VALUE super;
+{
+ Check_Type(super, T_CLASS);
+ rb_check_inheritable(super);
+ if (super == rb_cClass) {
+ rb_raise(rb_eTypeError, "can't make subclass of Class");
+ }
+ return rb_class_boot(super);
+}
+
+static int
+clone_method(mid, body, tbl)
+ ID mid;
+ NODE *body;
+ st_table *tbl;
+{
+ st_insert(tbl, mid, (st_data_t)NEW_METHOD(body->nd_body, body->nd_noex));
+ return ST_CONTINUE;
+}
+
+/* :nodoc: */
+VALUE
+rb_mod_init_copy(clone, orig)
+ VALUE clone, orig;
+{
+ rb_obj_init_copy(clone, orig);
+ if (!FL_TEST(CLASS_OF(clone), FL_SINGLETON)) {
+ RBASIC(clone)->klass = rb_singleton_class_clone(orig);
+ }
+ RCLASS(clone)->super = RCLASS(orig)->super;
+ if (RCLASS(orig)->iv_tbl) {
+ ID id;
+
+ RCLASS(clone)->iv_tbl = st_copy(RCLASS(orig)->iv_tbl);
+ id = rb_intern("__classpath__");
+ st_delete(RCLASS(clone)->iv_tbl, (st_data_t*)&id, 0);
+ id = rb_intern("__classid__");
+ st_delete(RCLASS(clone)->iv_tbl, (st_data_t*)&id, 0);
+ }
+ if (RCLASS(orig)->m_tbl) {
+ RCLASS(clone)->m_tbl = st_init_numtable();
+ st_foreach(RCLASS(orig)->m_tbl, clone_method,
+ (st_data_t)RCLASS(clone)->m_tbl);
+ }
+
+ return clone;
+}
+
+/* :nodoc: */
+VALUE
+rb_class_init_copy(clone, orig)
+ VALUE clone, orig;
+{
+ if (RCLASS(clone)->super != 0) {
+ rb_raise(rb_eTypeError, "already initialized class");
+ }
+ return rb_mod_init_copy(clone, orig);
+}
+
+VALUE
+rb_singleton_class_clone(obj)
+ VALUE obj;
+{
+ VALUE klass = RBASIC(obj)->klass;
+
+ if (!FL_TEST(klass, FL_SINGLETON))
+ return klass;
+ else {
+ /* copy singleton(unnamed) class */
+ NEWOBJ(clone, struct RClass);
+ OBJSETUP(clone, 0, RBASIC(klass)->flags);
+
+ if (BUILTIN_TYPE(obj) == T_CLASS) {
+ RBASIC(clone)->klass = (VALUE)clone;
+ }
+ else {
+ RBASIC(clone)->klass = rb_singleton_class_clone(klass);
+ }
+
+ clone->super = RCLASS(klass)->super;
+ clone->iv_tbl = 0;
+ clone->m_tbl = 0;
+ if (RCLASS(klass)->iv_tbl) {
+ clone->iv_tbl = st_copy(RCLASS(klass)->iv_tbl);
+ }
+ clone->m_tbl = st_init_numtable();
+ st_foreach(RCLASS(klass)->m_tbl, clone_method,
+ (st_data_t)clone->m_tbl);
+ rb_singleton_class_attached(RBASIC(clone)->klass, (VALUE)clone);
+ FL_SET(clone, FL_SINGLETON);
+ return (VALUE)clone;
+ }
+}
+
+void
+rb_singleton_class_attached(klass, obj)
+ VALUE klass, obj;
+{
+ if (FL_TEST(klass, FL_SINGLETON)) {
+ if (!RCLASS(klass)->iv_tbl) {
+ RCLASS(klass)->iv_tbl = st_init_numtable();
+ }
+ st_insert(RCLASS(klass)->iv_tbl, rb_intern("__attached__"), obj);
+ }
+}
+
+VALUE
+rb_make_metaclass(obj, super)
+ VALUE obj, super;
+{
+ if (BUILTIN_TYPE(obj) == T_CLASS && FL_TEST(obj, FL_SINGLETON)) {
+ return RBASIC(obj)->klass = rb_cClass;
+ }
+ else {
+ VALUE metasuper;
+ VALUE klass = rb_class_boot(super);
+
+ FL_SET(klass, FL_SINGLETON);
+ RBASIC(obj)->klass = klass;
+ rb_singleton_class_attached(klass, obj);
+
+ metasuper = RBASIC(rb_class_real(super))->klass;
+ /* metaclass of a superclass may be NULL at boot time */
+ if (metasuper) {
+ RBASIC(klass)->klass = metasuper;
+ }
+ return klass;
+ }
+}
+
+VALUE
+rb_define_class_id(id, super)
+ ID id;
+ VALUE super;
+{
+ VALUE klass;
+
+ if (!super) super = rb_cObject;
+ klass = rb_class_new(super);
+ rb_make_metaclass(klass, RBASIC(super)->klass);
+
+ return klass;
+}
+
+VALUE
+rb_class_inherited(super, klass)
+ VALUE super, klass;
+{
+ if (!super) super = rb_cObject;
+ return rb_funcall(super, rb_intern("inherited"), 1, klass);
+}
+
+VALUE
+rb_define_class(name, super)
+ const char *name;
+ VALUE super;
+{
+ VALUE klass;
+ ID id;
+
+ id = rb_intern(name);
+ if (rb_const_defined(rb_cObject, id)) {
+ klass = rb_const_get(rb_cObject, id);
+ if (TYPE(klass) != T_CLASS) {
+ rb_raise(rb_eTypeError, "%s is not a class", name);
+ }
+ if (rb_class_real(RCLASS(klass)->super) != super) {
+ rb_name_error(id, "%s is already defined", name);
+ }
+ return klass;
+ }
+ if (!super) {
+ rb_warn("no super class for `%s', Object assumed", name);
+ }
+ klass = rb_define_class_id(id, super);
+ st_add_direct(rb_class_tbl, id, klass);
+ rb_name_class(klass, id);
+ rb_const_set(rb_cObject, id, klass);
+ rb_class_inherited(super, klass);
+
+ return klass;
+}
+
+VALUE
+rb_define_class_under(outer, name, super)
+ VALUE outer;
+ const char *name;
+ VALUE super;
+{
+ VALUE klass;
+ ID id;
+
+ id = rb_intern(name);
+ if (rb_const_defined_at(outer, id)) {
+ klass = rb_const_get_at(outer, id);
+ if (TYPE(klass) != T_CLASS) {
+ rb_raise(rb_eTypeError, "%s is not a class", name);
+ }
+ if (rb_class_real(RCLASS(klass)->super) != super) {
+ rb_name_error(id, "%s is already defined", name);
+ }
+ return klass;
+ }
+ if (!super) {
+ rb_warn("no super class for `%s::%s', Object assumed",
+ rb_class2name(outer), name);
+ }
+ klass = rb_define_class_id(id, super);
+ rb_set_class_path(klass, outer, name);
+ rb_const_set(outer, id, klass);
+ rb_class_inherited(super, klass);
+
+ return klass;
+}
+
+VALUE
+rb_module_new()
+{
+ NEWOBJ(mdl, struct RClass);
+ OBJSETUP(mdl, rb_cModule, T_MODULE);
+
+ mdl->super = 0;
+ mdl->iv_tbl = 0;
+ mdl->m_tbl = 0;
+ mdl->m_tbl = st_init_numtable();
+
+ return (VALUE)mdl;
+}
+
+VALUE
+rb_define_module_id(id)
+ ID id;
+{
+ VALUE mdl;
+
+ mdl = rb_module_new();
+ rb_name_class(mdl, id);
+
+ return mdl;
+}
+
+VALUE
+rb_define_module(name)
+ const char *name;
+{
+ VALUE module;
+ ID id;
+
+ id = rb_intern(name);
+ if (rb_const_defined(rb_cObject, id)) {
+ module = rb_const_get(rb_cObject, id);
+ if (TYPE(module) == T_MODULE)
+ return module;
+ rb_raise(rb_eTypeError, "%s is not a module", rb_obj_classname(module));
+ }
+ module = rb_define_module_id(id);
+ st_add_direct(rb_class_tbl, id, module);
+ rb_const_set(rb_cObject, id, module);
+
+ return module;
+}
+
+VALUE
+rb_define_module_under(outer, name)
+ VALUE outer;
+ const char *name;
+{
+ VALUE module;
+ ID id;
+
+ id = rb_intern(name);
+ if (rb_const_defined_at(outer, id)) {
+ module = rb_const_get_at(outer, id);
+ if (TYPE(module) == T_MODULE)
+ return module;
+ rb_raise(rb_eTypeError, "%s::%s is not a module",
+ rb_class2name(outer), rb_obj_classname(module));
+ }
+ module = rb_define_module_id(id);
+ rb_const_set(outer, id, module);
+ rb_set_class_path(module, outer, name);
+
+ return module;
+}
+
+static VALUE
+include_class_new(module, super)
+ VALUE module, super;
+{
+ NEWOBJ(klass, struct RClass);
+ OBJSETUP(klass, rb_cClass, T_ICLASS);
+
+ if (BUILTIN_TYPE(module) == T_ICLASS) {
+ module = RBASIC(module)->klass;
+ }
+ if (!RCLASS(module)->iv_tbl) {
+ RCLASS(module)->iv_tbl = st_init_numtable();
+ }
+ klass->iv_tbl = RCLASS(module)->iv_tbl;
+ klass->m_tbl = RCLASS(module)->m_tbl;
+ klass->super = super;
+ if (TYPE(module) == T_ICLASS) {
+ RBASIC(klass)->klass = RBASIC(module)->klass;
+ }
+ else {
+ RBASIC(klass)->klass = module;
+ }
+ OBJ_INFECT(klass, module);
+ OBJ_INFECT(klass, super);
+
+ return (VALUE)klass;
+}
+
+void
+rb_include_module(klass, module)
+ VALUE klass, module;
+{
+ VALUE p, c;
+ int changed = 0;
+
+ rb_frozen_class_p(klass);
+ if (!OBJ_TAINTED(klass)) {
+ rb_secure(4);
+ }
+
+ if (NIL_P(module)) return;
+ if (klass == module) return;
+
+ if (TYPE(module) != T_MODULE) {
+ Check_Type(module, T_MODULE);
+ }
+
+ OBJ_INFECT(klass, module);
+ c = klass;
+ while (module) {
+ int superclass_seen = Qfalse;
+
+ if (RCLASS(klass)->m_tbl == RCLASS(module)->m_tbl)
+ rb_raise(rb_eArgError, "cyclic include detected");
+ /* ignore if the module included already in superclasses */
+ for (p = RCLASS(klass)->super; p; p = RCLASS(p)->super) {
+ switch (BUILTIN_TYPE(p)) {
+ case T_ICLASS:
+ if (RCLASS(p)->m_tbl == RCLASS(module)->m_tbl) {
+ if (!superclass_seen) {
+ c = p; /* move insertion point */
+ }
+ goto skip;
+ }
+ break;
+ case T_CLASS:
+ superclass_seen = Qtrue;
+ break;
+ }
+ }
+ c = RCLASS(c)->super = include_class_new(module, RCLASS(c)->super);
+ changed = 1;
+ skip:
+ module = RCLASS(module)->super;
+ }
+ if (changed) rb_clear_cache();
+}
+
+/*
+ * call-seq:
+ * mod.included_modules -> array
+ *
+ * Returns the list of modules included in <i>mod</i>.
+ *
+ * module Mixin
+ * end
+ *
+ * module Outer
+ * include Mixin
+ * end
+ *
+ * Mixin.included_modules #=> []
+ * Outer.included_modules #=> [Mixin]
+ */
+
+VALUE
+rb_mod_included_modules(mod)
+ VALUE mod;
+{
+ VALUE ary = rb_ary_new();
+ VALUE p;
+
+ for (p = RCLASS(mod)->super; p; p = RCLASS(p)->super) {
+ if (BUILTIN_TYPE(p) == T_ICLASS) {
+ rb_ary_push(ary, RBASIC(p)->klass);
+ }
+ }
+ return ary;
+}
+
+/*
+ * call-seq:
+ * mod.include?(module) => true or false
+ *
+ * Returns <code>true</code> if <i>module</i> is included in
+ * <i>mod</i> or one of <i>mod</i>'s ancestors.
+ *
+ * module A
+ * end
+ * class B
+ * include A
+ * end
+ * class C < B
+ * end
+ * B.include?(A) #=> true
+ * C.include?(A) #=> true
+ * A.include?(A) #=> false
+ */
+
+VALUE
+rb_mod_include_p(mod, mod2)
+ VALUE mod;
+ VALUE mod2;
+{
+ VALUE p;
+
+ Check_Type(mod2, T_MODULE);
+ for (p = RCLASS(mod)->super; p; p = RCLASS(p)->super) {
+ if (BUILTIN_TYPE(p) == T_ICLASS) {
+ if (RBASIC(p)->klass == mod2) return Qtrue;
+ }
+ }
+ return Qfalse;
+}
+
+/*
+ * call-seq:
+ * mod.ancestors -> array
+ *
+ * Returns a list of modules included in <i>mod</i> (including
+ * <i>mod</i> itself).
+ *
+ * module Mod
+ * include Math
+ * include Comparable
+ * end
+ *
+ * Mod.ancestors #=> [Mod, Comparable, Math]
+ * Math.ancestors #=> [Math]
+ */
+
+VALUE
+rb_mod_ancestors(mod)
+ VALUE mod;
+{
+ VALUE p, ary = rb_ary_new();
+
+ for (p = mod; p; p = RCLASS(p)->super) {
+ if (FL_TEST(p, FL_SINGLETON))
+ continue;
+ if (BUILTIN_TYPE(p) == T_ICLASS) {
+ rb_ary_push(ary, RBASIC(p)->klass);
+ }
+ else {
+ rb_ary_push(ary, p);
+ }
+ }
+ return ary;
+}
+
+#define VISI(x) ((x)&NOEX_MASK)
+#define VISI_CHECK(x,f) (VISI(x) == (f))
+
+static int
+ins_methods_push(name, type, ary, visi)
+ ID name;
+ long type;
+ VALUE ary;
+ long visi;
+{
+ if (type == -1) return ST_CONTINUE;
+ switch (visi) {
+ case NOEX_PRIVATE:
+ case NOEX_PROTECTED:
+ case NOEX_PUBLIC:
+ visi = (type == visi);
+ break;
+ default:
+ visi = (type != NOEX_PRIVATE);
+ break;
+ }
+ if (visi) {
+ rb_ary_push(ary, rb_str_new2(rb_id2name(name)));
+ }
+ return ST_CONTINUE;
+}
+
+static int
+ins_methods_i(name, type, ary)
+ ID name;
+ long type;
+ VALUE ary;
+{
+ return ins_methods_push(name, type, ary, -1); /* everything but private */
+}
+
+static int
+ins_methods_prot_i(name, type, ary)
+ ID name;
+ long type;
+ VALUE ary;
+{
+ return ins_methods_push(name, type, ary, NOEX_PROTECTED);
+}
+
+static int
+ins_methods_priv_i(name, type, ary)
+ ID name;
+ long type;
+ VALUE ary;
+{
+ return ins_methods_push(name, type, ary, NOEX_PRIVATE);
+}
+
+static int
+ins_methods_pub_i(name, type, ary)
+ ID name;
+ long type;
+ VALUE ary;
+{
+ return ins_methods_push(name, type, ary, NOEX_PUBLIC);
+}
+
+static int
+method_entry(key, body, list)
+ ID key;
+ NODE *body;
+ st_table *list;
+{
+ long type;
+
+ if (key == ID_ALLOCATOR) return ST_CONTINUE;
+ if (!st_lookup(list, key, 0)) {
+ if (!body->nd_body) type = -1; /* none */
+ else type = VISI(body->nd_noex);
+ st_add_direct(list, key, type);
+ }
+ return ST_CONTINUE;
+}
+
+static VALUE
+class_instance_method_list(argc, argv, mod, func)
+ int argc;
+ VALUE *argv;
+ VALUE mod;
+ int (*func) _((ID, long, VALUE));
+{
+ VALUE ary;
+ int recur;
+ st_table *list;
+
+ if (argc == 0) {
+ recur = Qtrue;
+ }
+ else {
+ VALUE r;
+ rb_scan_args(argc, argv, "01", &r);
+ recur = RTEST(r);
+ }
+
+ list = st_init_numtable();
+ for (; mod; mod = RCLASS(mod)->super) {
+ st_foreach(RCLASS(mod)->m_tbl, method_entry, (st_data_t)list);
+ if (BUILTIN_TYPE(mod) == T_ICLASS) continue;
+ if (FL_TEST(mod, FL_SINGLETON)) continue;
+ if (!recur) break;
+ }
+ ary = rb_ary_new();
+ st_foreach(list, func, ary);
+ st_free_table(list);
+
+ return ary;
+}
+
+/*
+ * call-seq:
+ * mod.instance_methods(include_super=true) => array
+ *
+ * Returns an array containing the names of public instance methods in
+ * the receiver. For a module, these are the public methods; for a
+ * class, they are the instance (not singleton) methods. With no
+ * argument, or with an argument that is <code>false</code>, the
+ * instance methods in <i>mod</i> are returned, otherwise the methods
+ * in <i>mod</i> and <i>mod</i>'s superclasses are returned.
+ *
+ * module A
+ * def method1() end
+ * end
+ * class B
+ * def method2() end
+ * end
+ * class C < B
+ * def method3() end
+ * end
+ *
+ * A.instance_methods #=> ["method1"]
+ * B.instance_methods(false) #=> ["method2"]
+ * C.instance_methods(false) #=> ["method3"]
+ * C.instance_methods(true).length #=> 43
+ */
+
+VALUE
+rb_class_instance_methods(argc, argv, mod)
+ int argc;
+ VALUE *argv;
+ VALUE mod;
+{
+ return class_instance_method_list(argc, argv, mod, ins_methods_i);
+}
+
+/*
+ * call-seq:
+ * mod.protected_instance_methods(include_super=true) => array
+ *
+ * Returns a list of the protected instance methods defined in
+ * <i>mod</i>. If the optional parameter is not <code>false</code>, the
+ * methods of any ancestors are included.
+ */
+
+VALUE
+rb_class_protected_instance_methods(argc, argv, mod)
+ int argc;
+ VALUE *argv;
+ VALUE mod;
+{
+ return class_instance_method_list(argc, argv, mod, ins_methods_prot_i);
+}
+
+/*
+ * call-seq:
+ * mod.private_instance_methods(include_super=true) => array
+ *
+ * Returns a list of the private instance methods defined in
+ * <i>mod</i>. If the optional parameter is not <code>false</code>, the
+ * methods of any ancestors are included.
+ *
+ * module Mod
+ * def method1() end
+ * private :method1
+ * def method2() end
+ * end
+ * Mod.instance_methods #=> ["method2"]
+ * Mod.private_instance_methods #=> ["method1"]
+ */
+
+VALUE
+rb_class_private_instance_methods(argc, argv, mod)
+ int argc;
+ VALUE *argv;
+ VALUE mod;
+{
+ return class_instance_method_list(argc, argv, mod, ins_methods_priv_i);
+}
+
+/*
+ * call-seq:
+ * mod.public_instance_methods(include_super=true) => array
+ *
+ * Returns a list of the public instance methods defined in <i>mod</i>.
+ * If the optional parameter is not <code>false</code>, the methods of
+ * any ancestors are included.
+ */
+
+VALUE
+rb_class_public_instance_methods(argc, argv, mod)
+ int argc;
+ VALUE *argv;
+ VALUE mod;
+{
+ return class_instance_method_list(argc, argv, mod, ins_methods_pub_i);
+}
+
+/*
+ * call-seq:
+ * obj.singleton_methods(all=true) => array
+ *
+ * Returns an array of the names of singleton methods for <i>obj</i>.
+ * If the optional <i>all</i> parameter is true, the list will include
+ * methods in modules included in <i>obj</i>.
+ *
+ * module Other
+ * def three() end
+ * end
+ *
+ * class Single
+ * def Single.four() end
+ * end
+ *
+ * a = Single.new
+ *
+ * def a.one()
+ * end
+ *
+ * class << a
+ * include Other
+ * def two()
+ * end
+ * end
+ *
+ * Single.singleton_methods #=> ["four"]
+ * a.singleton_methods(false) #=> ["two", "one"]
+ * a.singleton_methods #=> ["two", "one", "three"]
+ */
+
+VALUE
+rb_obj_singleton_methods(argc, argv, obj)
+ int argc;
+ VALUE *argv;
+ VALUE obj;
+{
+ VALUE recur, ary, klass;
+ st_table *list;
+
+ rb_scan_args(argc, argv, "01", &recur);
+ if (argc == 0) {
+ recur = Qtrue;
+ }
+ klass = CLASS_OF(obj);
+ list = st_init_numtable();
+ if (klass && FL_TEST(klass, FL_SINGLETON)) {
+ st_foreach(RCLASS(klass)->m_tbl, method_entry, (st_data_t)list);
+ klass = RCLASS(klass)->super;
+ }
+ if (RTEST(recur)) {
+ while (klass && (FL_TEST(klass, FL_SINGLETON) || TYPE(klass) == T_ICLASS)) {
+ st_foreach(RCLASS(klass)->m_tbl, method_entry, (st_data_t)list);
+ klass = RCLASS(klass)->super;
+ }
+ }
+ ary = rb_ary_new();
+ st_foreach(list, ins_methods_i, ary);
+ st_free_table(list);
+
+ return ary;
+}
+
+void
+rb_define_method_id(klass, name, func, argc)
+ VALUE klass;
+ ID name;
+ VALUE (*func)();
+ int argc;
+{
+ rb_add_method(klass, name, NEW_CFUNC(func,argc), NOEX_PUBLIC);
+}
+
+void
+rb_define_method(klass, name, func, argc)
+ VALUE klass;
+ const char *name;
+ VALUE (*func)();
+ int argc;
+{
+ rb_add_method(klass, rb_intern(name), NEW_CFUNC(func, argc), NOEX_PUBLIC);
+}
+
+void
+rb_define_protected_method(klass, name, func, argc)
+ VALUE klass;
+ const char *name;
+ VALUE (*func)();
+ int argc;
+{
+ rb_add_method(klass, rb_intern(name), NEW_CFUNC(func, argc), NOEX_PROTECTED);
+}
+
+void
+rb_define_private_method(klass, name, func, argc)
+ VALUE klass;
+ const char *name;
+ VALUE (*func)();
+ int argc;
+{
+ rb_add_method(klass, rb_intern(name), NEW_CFUNC(func, argc), NOEX_PRIVATE);
+}
+
+void
+rb_undef_method(klass, name)
+ VALUE klass;
+ const char *name;
+{
+ rb_add_method(klass, rb_intern(name), 0, NOEX_UNDEF);
+}
+
+#define SPECIAL_SINGLETON(x,c) do {\
+ if (obj == (x)) {\
+ return c;\
+ }\
+} while (0)
+
+VALUE
+rb_singleton_class(obj)
+ VALUE obj;
+{
+ VALUE klass;
+
+ if (FIXNUM_P(obj) || SYMBOL_P(obj)) {
+ rb_raise(rb_eTypeError, "can't define singleton");
+ }
+ if (rb_special_const_p(obj)) {
+ SPECIAL_SINGLETON(Qnil, rb_cNilClass);
+ SPECIAL_SINGLETON(Qfalse, rb_cFalseClass);
+ SPECIAL_SINGLETON(Qtrue, rb_cTrueClass);
+ rb_bug("unknown immediate %ld", obj);
+ }
+
+ DEFER_INTS;
+ if (FL_TEST(RBASIC(obj)->klass, FL_SINGLETON) &&
+ rb_iv_get(RBASIC(obj)->klass, "__attached__") == obj) {
+ klass = RBASIC(obj)->klass;
+ }
+ else {
+ klass = rb_make_metaclass(obj, RBASIC(obj)->klass);
+ }
+ if (OBJ_TAINTED(obj)) {
+ OBJ_TAINT(klass);
+ }
+ else {
+ FL_UNSET(klass, FL_TAINT);
+ }
+ if (OBJ_FROZEN(obj)) OBJ_FREEZE(klass);
+ ALLOW_INTS;
+
+ return klass;
+}
+
+void
+rb_define_singleton_method(obj, name, func, argc)
+ VALUE obj;
+ const char *name;
+ VALUE (*func)();
+ int argc;
+{
+ rb_define_method(rb_singleton_class(obj), name, func, argc);
+}
+
+void
+rb_define_module_function(module, name, func, argc)
+ VALUE module;
+ const char *name;
+ VALUE (*func)();
+ int argc;
+{
+ rb_define_private_method(module, name, func, argc);
+ rb_define_singleton_method(module, name, func, argc);
+}
+
+void
+rb_define_global_function(name, func, argc)
+ const char *name;
+ VALUE (*func)();
+ int argc;
+{
+ rb_define_module_function(rb_mKernel, name, func, argc);
+}
+
+void
+rb_define_alias(klass, name1, name2)
+ VALUE klass;
+ const char *name1, *name2;
+{
+ rb_alias(klass, rb_intern(name1), rb_intern(name2));
+}
+
+void
+rb_define_attr(klass, name, read, write)
+ VALUE klass;
+ const char *name;
+ int read, write;
+{
+ rb_attr(klass, rb_intern(name), read, write, Qfalse);
+}
+
+#ifdef HAVE_STDARG_PROTOTYPES
+#include <stdarg.h>
+#define va_init_list(a,b) va_start(a,b)
+#else
+#include <varargs.h>
+#define va_init_list(a,b) va_start(a)
+#endif
+
+int
+#ifdef HAVE_STDARG_PROTOTYPES
+rb_scan_args(int argc, const VALUE *argv, const char *fmt, ...)
+#else
+rb_scan_args(argc, argv, fmt, va_alist)
+ int argc;
+ const VALUE *argv;
+ const char *fmt;
+ va_dcl
+#endif
+{
+ int n, i = 0;
+ const char *p = fmt;
+ VALUE *var;
+ va_list vargs;
+
+ va_init_list(vargs, fmt);
+
+ if (*p == '*') goto rest_arg;
+
+ if (ISDIGIT(*p)) {
+ n = *p - '0';
+ if (n > argc)
+ rb_raise(rb_eArgError, "wrong number of arguments (%d for %d)", argc, n);
+ for (i=0; i<n; i++) {
+ var = va_arg(vargs, VALUE*);
+ if (var) *var = argv[i];
+ }
+ p++;
+ }
+ else {
+ goto error;
+ }
+
+ if (ISDIGIT(*p)) {
+ n = i + *p - '0';
+ for (; i<n; i++) {
+ var = va_arg(vargs, VALUE*);
+ if (argc > i) {
+ if (var) *var = argv[i];
+ }
+ else {
+ if (var) *var = Qnil;
+ }
+ }
+ p++;
+ }
+
+ if(*p == '*') {
+ rest_arg:
+ var = va_arg(vargs, VALUE*);
+ if (argc > i) {
+ if (var) *var = rb_ary_new4(argc-i, argv+i);
+ i = argc;
+ }
+ else {
+ if (var) *var = rb_ary_new();
+ }
+ p++;
+ }
+
+ if (*p == '&') {
+ var = va_arg(vargs, VALUE*);
+ if (rb_block_given_p()) {
+ *var = rb_block_proc();
+ }
+ else {
+ *var = Qnil;
+ }
+ p++;
+ }
+ va_end(vargs);
+
+ if (*p != '\0') {
+ goto error;
+ }
+
+ if (argc > i) {
+ rb_raise(rb_eArgError, "wrong number of arguments (%d for %d)", argc, i);
+ }
+
+ return argc;
+
+ error:
+ rb_fatal("bad scan arg format: %s", fmt);
+ return 0;
+}
+/**********************************************************************
+
+ compar.c -
+
+ $Author: murphy $
+ $Date: 2005-11-05 04:33:55 +0100 (Sa, 05 Nov 2005) $
+ created at: Thu Aug 26 14:39:48 JST 1993
+
+ Copyright (C) 1993-2003 Yukihiro Matsumoto
+
+**********************************************************************/
+
+#include "ruby.h"
+
+VALUE rb_mComparable;
+
+static ID cmp;
+
+int
+rb_cmpint(val, a, b)
+ VALUE val, a, b;
+{
+ if (NIL_P(val)) {
+ rb_cmperr(a, b);
+ }
+ if (FIXNUM_P(val)) return FIX2INT(val);
+ if (TYPE(val) == T_BIGNUM) {
+ if (RBIGNUM(val)->sign) return 1;
+ return -1;
+ }
+ if (RTEST(rb_funcall(val, '>', 1, INT2FIX(0)))) return 1;
+ if (RTEST(rb_funcall(val, '<', 1, INT2FIX(0)))) return -1;
+ return 0;
+}
+
+void
+rb_cmperr(x, y)
+ VALUE x, y;
+{
+ const char *classname;
+
+ if (SPECIAL_CONST_P(y)) {
+ y = rb_inspect(y);
+ classname = StringValuePtr(y);
+ }
+ else {
+ classname = rb_obj_classname(y);
+ }
+ rb_raise(rb_eArgError, "comparison of %s with %s failed",
+ rb_obj_classname(x), classname);
+}
+
+static VALUE
+cmp_eq(a)
+ VALUE *a;
+{
+ VALUE c = rb_funcall(a[0], cmp, 1, a[1]);
+
+ if (NIL_P(c)) return Qnil;
+ if (rb_cmpint(c, a[0], a[1]) == 0) return Qtrue;
+ return Qfalse;
+}
+
+static VALUE
+cmp_failed()
+{
+ return Qnil;
+}
+
+/*
+ * call-seq:
+ * obj == other => true or false
+ *
+ * Compares two objects based on the receiver's <code><=></code>
+ * method, returning true if it returns 0. Also returns true if
+ * _obj_ and _other_ are the same object.
+ */
+
+static VALUE
+cmp_equal(x, y)
+ VALUE x, y;
+{
+ VALUE a[2];
+
+ if (x == y) return Qtrue;
+
+ a[0] = x; a[1] = y;
+ return rb_rescue(cmp_eq, (VALUE)a, cmp_failed, 0);
+}
+
+/*
+ * call-seq:
+ * obj > other => true or false
+ *
+ * Compares two objects based on the receiver's <code><=></code>
+ * method, returning true if it returns 1.
+ */
+
+static VALUE
+cmp_gt(x, y)
+ VALUE x, y;
+{
+ VALUE c = rb_funcall(x, cmp, 1, y);
+
+ if (rb_cmpint(c, x, y) > 0) return Qtrue;
+ return Qfalse;
+}
+
+/*
+ * call-seq:
+ * obj >= other => true or false
+ *
+ * Compares two objects based on the receiver's <code><=></code>
+ * method, returning true if it returns 0 or 1.
+ */
+
+static VALUE
+cmp_ge(x, y)
+ VALUE x, y;
+{
+ VALUE c = rb_funcall(x, cmp, 1, y);
+
+ if (rb_cmpint(c, x, y) >= 0) return Qtrue;
+ return Qfalse;
+}
+
+/*
+ * call-seq:
+ * obj < other => true or false
+ *
+ * Compares two objects based on the receiver's <code><=></code>
+ * method, returning true if it returns -1.
+ */
+
+static VALUE
+cmp_lt(x, y)
+ VALUE x, y;
+{
+ VALUE c = rb_funcall(x, cmp, 1, y);
+
+ if (rb_cmpint(c, x, y) < 0) return Qtrue;
+ return Qfalse;
+}
+
+
+/*
+ * call-seq:
+ * obj <= other => true or false
+ *
+ * Compares two objects based on the receiver's <code><=></code>
+ * method, returning true if it returns -1 or 0.
+ */
+
+static VALUE
+cmp_le(x, y)
+ VALUE x, y;
+{
+ VALUE c = rb_funcall(x, cmp, 1, y);
+
+ if (rb_cmpint(c, x, y) <= 0) return Qtrue;
+ return Qfalse;
+}
+
+/*
+ * call-seq:
+ * obj.between?(min, max) => true or false
+ *
+ * Returns <code>false</code> if <i>obj</i> <code><=></code>
+ * <i>min</i> is less than zero or if <i>anObject</i> <code><=></code>
+ * <i>max</i> is greater than zero, <code>true</code> otherwise.
+ *
+ * 3.between?(1, 5) #=> true
+ * 6.between?(1, 5) #=> false
+ * 'cat'.between?('ant', 'dog') #=> true
+ * 'gnu'.between?('ant', 'dog') #=> false
+ *
+ */
+
+static VALUE
+cmp_between(x, min, max)
+ VALUE x, min, max;
+{
+ if (RTEST(cmp_lt(x, min))) return Qfalse;
+ if (RTEST(cmp_gt(x, max))) return Qfalse;
+ return Qtrue;
+}
+
+/*
+ * The <code>Comparable</code> mixin is used by classes whose objects
+ * may be ordered. The class must define the <code><=></code> operator,
+ * which compares the receiver against another object, returning -1, 0,
+ * or +1 depending on whether the receiver is less than, equal to, or
+ * greater than the other object. <code>Comparable</code> uses
+ * <code><=></code> to implement the conventional comparison operators
+ * (<code><</code>, <code><=</code>, <code>==</code>, <code>>=</code>,
+ * and <code>></code>) and the method <code>between?</code>.
+ *
+ * class SizeMatters
+ * include Comparable
+ * attr :str
+ * def <=>(anOther)
+ * str.size <=> anOther.str.size
+ * end
+ * def initialize(str)
+ * @str = str
+ * end
+ * def inspect
+ * @str
+ * end
+ * end
+ *
+ * s1 = SizeMatters.new("Z")
+ * s2 = SizeMatters.new("YY")
+ * s3 = SizeMatters.new("XXX")
+ * s4 = SizeMatters.new("WWWW")
+ * s5 = SizeMatters.new("VVVVV")
+ *
+ * s1 < s2 #=> true
+ * s4.between?(s1, s3) #=> false
+ * s4.between?(s3, s5) #=> true
+ * [ s3, s2, s5, s4, s1 ].sort #=> [Z, YY, XXX, WWWW, VVVVV]
+ *
+ */
+
+void
+Init_Comparable()
+{
+ rb_mComparable = rb_define_module("Comparable");
+ rb_define_method(rb_mComparable, "==", cmp_equal, 1);
+ rb_define_method(rb_mComparable, ">", cmp_gt, 1);
+ rb_define_method(rb_mComparable, ">=", cmp_ge, 1);
+ rb_define_method(rb_mComparable, "<", cmp_lt, 1);
+ rb_define_method(rb_mComparable, "<=", cmp_le, 1);
+ rb_define_method(rb_mComparable, "between?", cmp_between, 2);
+
+ cmp = rb_intern("<=>");
+}
+/**********************************************************************
+
+ dir.c -
+
+ $Author: murphy $
+ $Date: 2005-11-05 04:33:55 +0100 (Sa, 05 Nov 2005) $
+ created at: Wed Jan 5 09:51:01 JST 1994
+
+ Copyright (C) 1993-2003 Yukihiro Matsumoto
+ Copyright (C) 2000 Network Applied Communication Laboratory, Inc.
+ Copyright (C) 2000 Information-technology Promotion Agency, Japan
+
+**********************************************************************/
+
+#include "ruby.h"
+
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#if defined HAVE_DIRENT_H && !defined _WIN32
+# include <dirent.h>
+# define NAMLEN(dirent) strlen((dirent)->d_name)
+#elif defined HAVE_DIRECT_H && !defined _WIN32
+# include <direct.h>
+# define NAMLEN(dirent) strlen((dirent)->d_name)
+#else
+# define dirent direct
+# if !defined __NeXT__
+# define NAMLEN(dirent) (dirent)->d_namlen
+# else
+# /* On some versions of NextStep, d_namlen is always zero, so avoid it. */
+# define NAMLEN(dirent) strlen((dirent)->d_name)
+# endif
+# if HAVE_SYS_NDIR_H
+# include <sys/ndir.h>
+# endif
+# if HAVE_SYS_DIR_H
+# include <sys/dir.h>
+# endif
+# if HAVE_NDIR_H
+# include <ndir.h>
+# endif
+# ifdef _WIN32
+# include "win32/dir.h"
+# endif
+#endif
+
+#include <errno.h>
+
+#ifndef HAVE_STDLIB_H
+char *getenv();
+#endif
+
+#ifndef HAVE_STRING_H
+char *strchr _((char*,char));
+#endif
+
+#include <ctype.h>
+
+#include "util.h"
+
+#if !defined HAVE_LSTAT && !defined lstat
+#define lstat stat
+#endif
+
+#define FNM_NOESCAPE 0x01
+#define FNM_PATHNAME 0x02
+#define FNM_DOTMATCH 0x04
+#define FNM_CASEFOLD 0x08
+
+#define FNM_NOMATCH 1
+#define FNM_ERROR 2
+
+#define downcase(c) (nocase && ISUPPER(c) ? tolower(c) : (c))
+#define compare(c1, c2) (((unsigned char)(c1)) - ((unsigned char)(c2)))
+
+/* caution: in case *p == '\0'
+ Next(p) == p + 1 in single byte environment
+ Next(p) == p in multi byte environment
+*/
+#if defined(CharNext)
+# define Next(p) CharNext(p)
+#elif defined(DJGPP)
+# define Next(p) ((p) + mblen(p, RUBY_MBCHAR_MAXSIZE))
+#elif defined(__EMX__)
+# define Next(p) ((p) + emx_mblen(p))
+static inline int
+emx_mblen(p)
+ const char *p;
+{
+ int n = mblen(p, RUBY_MBCHAR_MAXSIZE);
+ return (n < 0) ? 1 : n;
+}
+#endif
+
+#ifndef Next /* single byte environment */
+# define Next(p) ((p) + 1)
+# define Inc(p) (++(p))
+# define Compare(p1, p2) (compare(downcase(*(p1)), downcase(*(p2))))
+#else /* multi byte environment */
+# define Inc(p) ((p) = Next(p))
+# define Compare(p1, p2) (CompareImpl(p1, p2, nocase))
+static int
+CompareImpl(p1, p2, nocase)
+ const char *p1;
+ const char *p2;
+ int nocase;
+{
+ const int len1 = Next(p1) - p1;
+ const int len2 = Next(p2) - p2;
+#ifdef _WIN32
+ char buf1[10], buf2[10]; /* large enough? */
+#endif
+
+ if (len1 < 0 || len2 < 0) {
+ rb_fatal("CompareImpl: negative len");
+ }
+
+ if (len1 == 0) return len2;
+ if (len2 == 0) return -len1;
+
+#ifdef _WIN32
+ if (nocase) {
+ if (len1 > 1) {
+ if (len1 >= sizeof(buf1)) {
+ rb_fatal("CompareImpl: too large len");
+ }
+ memcpy(buf1, p1, len1);
+ buf1[len1] = '\0';
+ CharLower(buf1);
+ p1 = buf1; /* trick */
+ }
+ if (len2 > 1) {
+ if (len2 >= sizeof(buf2)) {
+ rb_fatal("CompareImpl: too large len");
+ }
+ memcpy(buf2, p2, len2);
+ buf2[len2] = '\0';
+ CharLower(buf2);
+ p2 = buf2; /* trick */
+ }
+ }
+#endif
+ if (len1 == 1)
+ if (len2 == 1)
+ return compare(downcase(*p1), downcase(*p2));
+ else {
+ const int ret = compare(downcase(*p1), *p2);
+ return ret ? ret : -1;
+ }
+ else
+ if (len2 == 1) {
+ const int ret = compare(*p1, downcase(*p2));
+ return ret ? ret : 1;
+ }
+ else {
+ const int ret = memcmp(p1, p2, len1 < len2 ? len1 : len2);
+ return ret ? ret : len1 - len2;
+ }
+}
+#endif /* environment */
+
+static char *
+bracket(p, s, flags)
+ const char *p; /* pattern (next to '[') */
+ const char *s; /* string */
+ int flags;
+{
+ const int nocase = flags & FNM_CASEFOLD;
+ const int escape = !(flags & FNM_NOESCAPE);
+
+ int ok = 0, not = 0;
+
+ if (*p == '!' || *p == '^') {
+ not = 1;
+ p++;
+ }
+
+ while (*p != ']') {
+ const char *t1 = p;
+ if (escape && *t1 == '\\')
+ t1++;
+ if (!*t1)
+ return NULL;
+ p = Next(t1);
+ if (p[0] == '-' && p[1] != ']') {
+ const char *t2 = p + 1;
+ if (escape && *t2 == '\\')
+ t2++;
+ if (!*t2)
+ return NULL;
+ p = Next(t2);
+ if (!ok && Compare(t1, s) <= 0 && Compare(s, t2) <= 0)
+ ok = 1;
+ }
+ else
+ if (!ok && Compare(t1, s) == 0)
+ ok = 1;
+ }
+
+ return ok == not ? NULL : (char *)p + 1;
+}
+
+/* If FNM_PATHNAME is set, only path element will be matched. (upto '/' or '\0')
+ Otherwise, entire string will be matched.
+ End marker itself won't be compared.
+ And if function succeeds, *pcur reaches end marker.
+*/
+#define UNESCAPE(p) (escape && *(p) == '\\' ? (p) + 1 : (p))
+#define ISEND(p) (!*(p) || (pathname && *(p) == '/'))
+#define RETURN(val) return *pcur = p, *scur = s, (val);
+
+static int
+fnmatch_helper(pcur, scur, flags)
+ const char **pcur; /* pattern */
+ const char **scur; /* string */
+ int flags;
+{
+ const int period = !(flags & FNM_DOTMATCH);
+ const int pathname = flags & FNM_PATHNAME;
+ const int escape = !(flags & FNM_NOESCAPE);
+ const int nocase = flags & FNM_CASEFOLD;
+
+ const char *ptmp = 0;
+ const char *stmp = 0;
+
+ const char *p = *pcur;
+ const char *s = *scur;
+
+ if (period && *s == '.' && *UNESCAPE(p) != '.') /* leading period */
+ RETURN(FNM_NOMATCH);
+
+ while (1) {
+ switch (*p) {
+ case '*':
+ do { p++; } while (*p == '*');
+ if (ISEND(UNESCAPE(p))) {
+ p = UNESCAPE(p);
+ RETURN(0);
+ }
+ if (ISEND(s))
+ RETURN(FNM_NOMATCH);
+ ptmp = p;
+ stmp = s;
+ continue;
+
+ case '?':
+ if (ISEND(s))
+ RETURN(FNM_NOMATCH);
+ p++;
+ Inc(s);
+ continue;
+
+ case '[': {
+ const char *t;
+ if (ISEND(s))
+ RETURN(FNM_NOMATCH);
+ if (t = bracket(p + 1, s, flags)) {
+ p = t;
+ Inc(s);
+ continue;
+ }
+ goto failed;
+ }
+ }
+
+ /* ordinary */
+ p = UNESCAPE(p);
+ if (ISEND(s))
+ RETURN(ISEND(p) ? 0 : FNM_NOMATCH);
+ if (ISEND(p))
+ goto failed;
+ if (Compare(p, s) != 0)
+ goto failed;
+ Inc(p);
+ Inc(s);
+ continue;
+
+ failed: /* try next '*' position */
+ if (ptmp && stmp) {
+ p = ptmp;
+ Inc(stmp); /* !ISEND(*stmp) */
+ s = stmp;
+ continue;
+ }
+ RETURN(FNM_NOMATCH);
+ }
+}
+
+static int
+fnmatch(p, s, flags)
+ const char *p; /* pattern */
+ const char *s; /* string */
+ int flags;
+{
+ const int period = !(flags & FNM_DOTMATCH);
+ const int pathname = flags & FNM_PATHNAME;
+
+ const char *ptmp = 0;
+ const char *stmp = 0;
+
+ if (pathname) {
+ while (1) {
+ if (p[0] == '*' && p[1] == '*' && p[2] == '/') {
+ do { p += 3; } while (p[0] == '*' && p[1] == '*' && p[2] == '/');
+ ptmp = p;
+ stmp = s;
+ }
+ if (fnmatch_helper(&p, &s, flags) == 0) {
+ while (*s && *s != '/') Inc(s);
+ if (*p && *s) {
+ p++;
+ s++;
+ continue;
+ }
+ if (!*p && !*s)
+ return 0;
+ }
+ /* failed : try next recursion */
+ if (ptmp && stmp && !(period && *stmp == '.')) {
+ while (*stmp && *stmp != '/') Inc(stmp);
+ if (*stmp) {
+ p = ptmp;
+ stmp++;
+ s = stmp;
+ continue;
+ }
+ }
+ return FNM_NOMATCH;
+ }
+ }
+ else
+ return fnmatch_helper(&p, &s, flags);
+}
+
+VALUE rb_cDir;
+
+struct dir_data {
+ DIR *dir;
+ char *path;
+};
+
+static void
+free_dir(dir)
+ struct dir_data *dir;
+{
+ if (dir) {
+ if (dir->dir) closedir(dir->dir);
+ if (dir->path) free(dir->path);
+ }
+ free(dir);
+}
+
+static VALUE dir_close _((VALUE));
+
+static VALUE dir_s_alloc _((VALUE));
+static VALUE
+dir_s_alloc(klass)
+ VALUE klass;
+{
+ struct dir_data *dirp;
+ VALUE obj = Data_Make_Struct(klass, struct dir_data, 0, free_dir, dirp);
+
+ dirp->dir = NULL;
+ dirp->path = NULL;
+
+ return obj;
+}
+
+/*
+ * call-seq:
+ * Dir.new( string ) -> aDir
+ *
+ * Returns a new directory object for the named directory.
+ */
+static VALUE
+dir_initialize(dir, dirname)
+ VALUE dir, dirname;
+{
+ struct dir_data *dp;
+
+ FilePathValue(dirname);
+ Data_Get_Struct(dir, struct dir_data, dp);
+ if (dp->dir) closedir(dp->dir);
+ if (dp->path) free(dp->path);
+ dp->dir = NULL;
+ dp->path = NULL;
+ dp->dir = opendir(RSTRING(dirname)->ptr);
+ if (dp->dir == NULL) {
+ if (errno == EMFILE || errno == ENFILE) {
+ rb_gc();
+ dp->dir = opendir(RSTRING(dirname)->ptr);
+ }
+ if (dp->dir == NULL) {
+ rb_sys_fail(RSTRING(dirname)->ptr);
+ }
+ }
+ dp->path = strdup(RSTRING(dirname)->ptr);
+
+ return dir;
+}
+
+/*
+ * call-seq:
+ * Dir.open( string ) => aDir
+ * Dir.open( string ) {| aDir | block } => anObject
+ *
+ * With no block, <code>open</code> is a synonym for
+ * <code>Dir::new</code>. If a block is present, it is passed
+ * <i>aDir</i> as a parameter. The directory is closed at the end of
+ * the block, and <code>Dir::open</code> returns the value of the
+ * block.
+ */
+static VALUE
+dir_s_open(klass, dirname)
+ VALUE klass, dirname;
+{
+ struct dir_data *dp;
+ VALUE dir = Data_Make_Struct(klass, struct dir_data, 0, free_dir, dp);
+
+ dir_initialize(dir, dirname);
+ if (rb_block_given_p()) {
+ return rb_ensure(rb_yield, dir, dir_close, dir);
+ }
+
+ return dir;
+}
+
+static void
+dir_closed()
+{
+ rb_raise(rb_eIOError, "closed directory");
+}
+
+#define GetDIR(obj, dirp) do {\
+ Data_Get_Struct(obj, struct dir_data, dirp);\
+ if (dirp->dir == NULL) dir_closed();\
+} while (0)
+
+/*
+ * call-seq:
+ * dir.inspect => string
+ *
+ * Return a string describing this Dir object.
+ */
+static VALUE
+dir_inspect(dir)
+ VALUE dir;
+{
+ struct dir_data *dirp;
+
+ GetDIR(dir, dirp);
+ if (dirp->path) {
+ char *c = rb_obj_classname(dir);
+ int len = strlen(c) + strlen(dirp->path) + 4;
+ VALUE s = rb_str_new(0, len);
+ snprintf(RSTRING(s)->ptr, len+1, "#<%s:%s>", c, dirp->path);
+ return s;
+ }
+ return rb_funcall(dir, rb_intern("to_s"), 0, 0);
+}
+
+/*
+ * call-seq:
+ * dir.path => string or nil
+ *
+ * Returns the path parameter passed to <em>dir</em>'s constructor.
+ *
+ * d = Dir.new("..")
+ * d.path #=> ".."
+ */
+static VALUE
+dir_path(dir)
+ VALUE dir;
+{
+ struct dir_data *dirp;
+
+ GetDIR(dir, dirp);
+ if (!dirp->path) return Qnil;
+ return rb_str_new2(dirp->path);
+}
+
+/*
+ * call-seq:
+ * dir.read => string or nil
+ *
+ * Reads the next entry from <em>dir</em> and returns it as a string.
+ * Returns <code>nil</code> at the end of the stream.
+ *
+ * d = Dir.new("testdir")
+ * d.read #=> "."
+ * d.read #=> ".."
+ * d.read #=> "config.h"
+ */
+static VALUE
+dir_read(dir)
+ VALUE dir;
+{
+ struct dir_data *dirp;
+ struct dirent *dp;
+
+ GetDIR(dir, dirp);
+ errno = 0;
+ dp = readdir(dirp->dir);
+ if (dp) {
+ return rb_tainted_str_new(dp->d_name, NAMLEN(dp));
+ }
+ else if (errno == 0) { /* end of stream */
+ return Qnil;
+ }
+ else {
+ rb_sys_fail(0);
+ }
+ return Qnil; /* not reached */
+}
+
+/*
+ * call-seq:
+ * dir.each { |filename| block } => dir
+ *
+ * Calls the block once for each entry in this directory, passing the
+ * filename of each entry as a parameter to the block.
+ *
+ * d = Dir.new("testdir")
+ * d.each {|x| puts "Got #{x}" }
+ *
+ * <em>produces:</em>
+ *
+ * Got .
+ * Got ..
+ * Got config.h
+ * Got main.rb
+ */
+static VALUE
+dir_each(dir)
+ VALUE dir;
+{
+ struct dir_data *dirp;
+ struct dirent *dp;
+
+ GetDIR(dir, dirp);
+ for (dp = readdir(dirp->dir); dp != NULL; dp = readdir(dirp->dir)) {
+ rb_yield(rb_tainted_str_new(dp->d_name, NAMLEN(dp)));
+ if (dirp->dir == NULL) dir_closed();
+ }
+ return dir;
+}
+
+/*
+ * call-seq:
+ * dir.pos => integer
+ * dir.tell => integer
+ *
+ * Returns the current position in <em>dir</em>. See also
+ * <code>Dir#seek</code>.
+ *
+ * d = Dir.new("testdir")
+ * d.tell #=> 0
+ * d.read #=> "."
+ * d.tell #=> 12
+ */
+static VALUE
+dir_tell(dir)
+ VALUE dir;
+{
+#ifdef HAVE_TELLDIR
+ struct dir_data *dirp;
+ long pos;
+
+ GetDIR(dir, dirp);
+ pos = telldir(dirp->dir);
+ return rb_int2inum(pos);
+#else
+ rb_notimplement();
+#endif
+}
+
+/*
+ * call-seq:
+ * dir.seek( integer ) => dir
+ *
+ * Seeks to a particular location in <em>dir</em>. <i>integer</i>
+ * must be a value returned by <code>Dir#tell</code>.
+ *
+ * d = Dir.new("testdir") #=> #<Dir:0x401b3c40>
+ * d.read #=> "."
+ * i = d.tell #=> 12
+ * d.read #=> ".."
+ * d.seek(i) #=> #<Dir:0x401b3c40>
+ * d.read #=> ".."
+ */
+static VALUE
+dir_seek(dir, pos)
+ VALUE dir, pos;
+{
+ struct dir_data *dirp;
+ off_t p = NUM2OFFT(pos);
+
+ GetDIR(dir, dirp);
+#ifdef HAVE_SEEKDIR
+ seekdir(dirp->dir, p);
+ return dir;
+#else
+ rb_notimplement();
+#endif
+}
+
+/*
+ * call-seq:
+ * dir.pos( integer ) => integer
+ *
+ * Synonym for <code>Dir#seek</code>, but returns the position
+ * parameter.
+ *
+ * d = Dir.new("testdir") #=> #<Dir:0x401b3c40>
+ * d.read #=> "."
+ * i = d.pos #=> 12
+ * d.read #=> ".."
+ * d.pos = i #=> 12
+ * d.read #=> ".."
+ */
+static VALUE
+dir_set_pos(dir, pos)
+ VALUE dir, pos;
+{
+ dir_seek(dir, pos);
+ return pos;
+}
+
+/*
+ * call-seq:
+ * dir.rewind => dir
+ *
+ * Repositions <em>dir</em> to the first entry.
+ *
+ * d = Dir.new("testdir")
+ * d.read #=> "."
+ * d.rewind #=> #<Dir:0x401b3fb0>
+ * d.read #=> "."
+ */
+static VALUE
+dir_rewind(dir)
+ VALUE dir;
+{
+ struct dir_data *dirp;
+
+ GetDIR(dir, dirp);
+ rewinddir(dirp->dir);
+ return dir;
+}
+
+/*
+ * call-seq:
+ * dir.close => nil
+ *
+ * Closes the directory stream. Any further attempts to access
+ * <em>dir</em> will raise an <code>IOError</code>.
+ *
+ * d = Dir.new("testdir")
+ * d.close #=> nil
+ */
+static VALUE
+dir_close(dir)
+ VALUE dir;
+{
+ struct dir_data *dirp;
+
+ GetDIR(dir, dirp);
+ closedir(dirp->dir);
+ dirp->dir = NULL;
+
+ return Qnil;
+}
+
+static void
+dir_chdir(path)
+ VALUE path;
+{
+ if (chdir(RSTRING(path)->ptr) < 0)
+ rb_sys_fail(RSTRING(path)->ptr);
+}
+
+static int chdir_blocking = 0;
+static VALUE chdir_thread = Qnil;
+
+struct chdir_data {
+ VALUE old_path, new_path;
+ int done;
+};
+
+static VALUE
+chdir_yield(args)
+ struct chdir_data *args;
+{
+ dir_chdir(args->new_path);
+ args->done = Qtrue;
+ chdir_blocking++;
+ if (chdir_thread == Qnil)
+ chdir_thread = rb_thread_current();
+ return rb_yield(args->new_path);
+}
+
+static VALUE
+chdir_restore(args)
+ struct chdir_data *args;
+{
+ if (args->done) {
+ chdir_blocking--;
+ if (chdir_blocking == 0)
+ chdir_thread = Qnil;
+ dir_chdir(args->old_path);
+ }
+ return Qnil;
+}
+
+/*
+ * call-seq:
+ * Dir.chdir( [ string] ) => 0
+ * Dir.chdir( [ string] ) {| path | block } => anObject
+ *
+ * Changes the current working directory of the process to the given
+ * string. When called without an argument, changes the directory to
+ * the value of the environment variable <code>HOME</code>, or
+ * <code>LOGDIR</code>. <code>SystemCallError</code> (probably
+ * <code>Errno::ENOENT</code>) if the target directory does not exist.
+ *
+ * If a block is given, it is passed the name of the new current
+ * directory, and the block is executed with that as the current
+ * directory. The original working directory is restored when the block
+ * exits. The return value of <code>chdir</code> is the value of the
+ * block. <code>chdir</code> blocks can be nested, but in a
+ * multi-threaded program an error will be raised if a thread attempts
+ * to open a <code>chdir</code> block while another thread has one
+ * open.
+ *
+ * Dir.chdir("/var/spool/mail")
+ * puts Dir.pwd
+ * Dir.chdir("/tmp") do
+ * puts Dir.pwd
+ * Dir.chdir("/usr") do
+ * puts Dir.pwd
+ * end
+ * puts Dir.pwd
+ * end
+ * puts Dir.pwd
+ *
+ * <em>produces:</em>
+ *
+ * /var/spool/mail
+ * /tmp
+ * /usr
+ * /tmp
+ * /var/spool/mail
+ */
+static VALUE
+dir_s_chdir(argc, argv, obj)
+ int argc;
+ VALUE *argv;
+ VALUE obj;
+{
+ VALUE path = Qnil;
+
+ rb_secure(2);
+ if (rb_scan_args(argc, argv, "01", &path) == 1) {
+ FilePathValue(path);
+ }
+ else {
+ const char *dist = getenv("HOME");
+ if (!dist) {
+ dist = getenv("LOGDIR");
+ if (!dist) rb_raise(rb_eArgError, "HOME/LOGDIR not set");
+ }
+ path = rb_str_new2(dist);
+ }
+
+ if (chdir_blocking > 0) {
+ if (!rb_block_given_p() || rb_thread_current() != chdir_thread)
+ rb_warn("conflicting chdir during another chdir block");
+ }
+
+ if (rb_block_given_p()) {
+ struct chdir_data args;
+ char *cwd = my_getcwd();
+
+ args.old_path = rb_tainted_str_new2(cwd); free(cwd);
+ args.new_path = path;
+ args.done = Qfalse;
+ return rb_ensure(chdir_yield, (VALUE)&args, chdir_restore, (VALUE)&args);
+ }
+ dir_chdir(path);
+
+ return INT2FIX(0);
+}
+
+/*
+ * call-seq:
+ * Dir.getwd => string
+ * Dir.pwd => string
+ *
+ * Returns the path to the current working directory of this process as
+ * a string.
+ *
+ * Dir.chdir("/tmp") #=> 0
+ * Dir.getwd #=> "/tmp"
+ */
+static VALUE
+dir_s_getwd(dir)
+ VALUE dir;
+{
+ char *path;
+ VALUE cwd;
+
+ rb_secure(4);
+ path = my_getcwd();
+ cwd = rb_tainted_str_new2(path);
+
+ free(path);
+ return cwd;
+}
+
+static void check_dirname _((volatile VALUE *));
+static void
+check_dirname(dir)
+ volatile VALUE *dir;
+{
+ char *path, *pend;
+
+ rb_secure(2);
+ FilePathValue(*dir);
+ path = RSTRING(*dir)->ptr;
+ if (path && *(pend = rb_path_end(rb_path_skip_prefix(path)))) {
+ *dir = rb_str_new(path, pend - path);
+ }
+}
+
+/*
+ * call-seq:
+ * Dir.chroot( string ) => 0
+ *
+ * Changes this process's idea of the file system root. Only a
+ * privileged process may make this call. Not available on all
+ * platforms. On Unix systems, see <code>chroot(2)</code> for more
+ * information.
+ */
+static VALUE
+dir_s_chroot(dir, path)
+ VALUE dir, path;
+{
+#if defined(HAVE_CHROOT) && !defined(__CHECKER__)
+ check_dirname(&path);
+
+ if (chroot(RSTRING(path)->ptr) == -1)
+ rb_sys_fail(RSTRING(path)->ptr);
+
+ return INT2FIX(0);
+#else
+ rb_notimplement();
+ return Qnil; /* not reached */
+#endif
+}
+
+/*
+ * call-seq:
+ * Dir.mkdir( string [, integer] ) => 0
+ *
+ * Makes a new directory named by <i>string</i>, with permissions
+ * specified by the optional parameter <i>anInteger</i>. The
+ * permissions may be modified by the value of
+ * <code>File::umask</code>, and are ignored on NT. Raises a
+ * <code>SystemCallError</code> if the directory cannot be created. See
+ * also the discussion of permissions in the class documentation for
+ * <code>File</code>.
+ *
+ */
+static VALUE
+dir_s_mkdir(argc, argv, obj)
+ int argc;
+ VALUE *argv;
+ VALUE obj;
+{
+ VALUE path, vmode;
+ int mode;
+
+ if (rb_scan_args(argc, argv, "11", &path, &vmode) == 2) {
+ mode = NUM2INT(vmode);
+ }
+ else {
+ mode = 0777;
+ }
+
+ check_dirname(&path);
+ if (mkdir(RSTRING(path)->ptr, mode) == -1)
+ rb_sys_fail(RSTRING(path)->ptr);
+
+ return INT2FIX(0);
+}
+
+/*
+ * call-seq:
+ * Dir.delete( string ) => 0
+ * Dir.rmdir( string ) => 0
+ * Dir.unlink( string ) => 0
+ *
+ * Deletes the named directory. Raises a subclass of
+ * <code>SystemCallError</code> if the directory isn't empty.
+ */
+static VALUE
+dir_s_rmdir(obj, dir)
+ VALUE obj, dir;
+{
+ check_dirname(&dir);
+ if (rmdir(RSTRING(dir)->ptr) < 0)
+ rb_sys_fail(RSTRING(dir)->ptr);
+
+ return INT2FIX(0);
+}
+
+/* System call with warning */
+static int
+do_stat(path, pst)
+ const char *path;
+ struct stat *pst;
+{
+ int ret = stat(path, pst);
+ if (ret < 0 && errno != ENOENT)
+ rb_sys_warning(path);
+
+ return ret;
+}
+
+static int
+do_lstat(path, pst)
+ const char *path;
+ struct stat *pst;
+{
+ int ret = lstat(path, pst);
+ if (ret < 0 && errno != ENOENT)
+ rb_sys_warning(path);
+
+ return ret;
+}
+
+static DIR *
+do_opendir(path)
+ const char *path;
+{
+ DIR *dirp = opendir(path);
+ if (dirp == NULL && errno != ENOENT && errno != ENOTDIR)
+ rb_sys_warning(path);
+
+ return dirp;
+}
+
+/* Return nonzero if S has any special globbing chars in it. */
+static int
+has_magic(s, flags)
+ const char *s;
+ int flags;
+{
+ const int escape = !(flags & FNM_NOESCAPE);
+
+ register const char *p = s;
+ register char c;
+
+ while (c = *p++) {
+ switch (c) {
+ case '*':
+ case '?':
+ case '[':
+ return 1;
+
+ case '\\':
+ if (escape && !(c = *p++))
+ return 0;
+ continue;
+ }
+
+ p = Next(p-1);
+ }
+
+ return 0;
+}
+
+/* Find separator in globbing pattern. */
+static char *
+find_dirsep(s, flags)
+ const char *s;
+ int flags;
+{
+ const int escape = !(flags & FNM_NOESCAPE);
+
+ register const char *p = s;
+ register char c;
+ int open = 0;
+
+ while (c = *p++) {
+ switch (c) {
+ case '[':
+ open = 1;
+ continue;
+ case ']':
+ open = 0;
+ continue;
+
+ case '/':
+ if (!open)
+ return (char *)p-1;
+ continue;
+
+ case '\\':
+ if (escape && !(c = *p++))
+ return (char *)p-1;
+ continue;
+ }
+
+ p = Next(p-1);
+ }
+
+ return (char *)p-1;
+}
+
+/* Remove escaping baskclashes */
+static void
+remove_backslashes(p)
+ char *p;
+{
+ char *t = p;
+ char *s = p;
+
+ while (*p) {
+ if (*p == '\\') {
+ if (t != s)
+ memmove(t, s, p - s);
+ t += p - s;
+ s = ++p;
+ if (!*p) break;
+ }
+ Inc(p);
+ }
+
+ while (*p++);
+
+ if (t != s)
+ memmove(t, s, p - s); /* move '\0' too */
+}
+
+/* Globing pattern */
+enum glob_pattern_type { PLAIN, MAGICAL, RECURSIVE, MATCH_ALL, MATCH_DIR };
+
+struct glob_pattern {
+ char *str;
+ enum glob_pattern_type type;
+ struct glob_pattern *next;
+};
+
+static struct glob_pattern *
+glob_make_pattern(p, flags)
+ const char *p;
+ int flags;
+{
+ struct glob_pattern *list, *tmp, **tail = &list;
+ int dirsep = 0; /* pattern is terminated with '/' */
+
+ while (*p) {
+ tmp = ALLOC(struct glob_pattern);
+ if (p[0] == '*' && p[1] == '*' && p[2] == '/') {
+ /* fold continuous RECURSIVEs (needed in glob_helper) */
+ do { p += 3; } while (p[0] == '*' && p[1] == '*' && p[2] == '/');
+ tmp->type = RECURSIVE;
+ tmp->str = 0;
+ dirsep = 1;
+ }
+ else {
+ const char *m = find_dirsep(p, flags);
+ char *buf = ALLOC_N(char, m-p+1);
+ memcpy(buf, p, m-p);
+ buf[m-p] = '\0';
+ tmp->type = has_magic(buf, flags) ? MAGICAL : PLAIN;
+ tmp->str = buf;
+ if (*m) {
+ dirsep = 1;
+ p = m + 1;
+ }
+ else {
+ dirsep = 0;
+ p = m;
+ }
+ }
+ *tail = tmp;
+ tail = &tmp->next;
+ }
+
+ tmp = ALLOC(struct glob_pattern);
+ tmp->type = dirsep ? MATCH_DIR : MATCH_ALL;
+ tmp->str = 0;
+ *tail = tmp;
+ tmp->next = 0;
+
+ return list;
+}
+
+static void
+glob_free_pattern(list)
+ struct glob_pattern *list;
+{
+ while (list) {
+ struct glob_pattern *tmp = list;
+ list = list->next;
+ if (tmp->str)
+ free(tmp->str);
+ free(tmp);
+ }
+}
+
+static VALUE
+join_path(path, dirsep, name)
+ VALUE path;
+ int dirsep;
+ const char *name;
+{
+ long len = RSTRING(path)->len;
+ VALUE buf = rb_str_new(0, RSTRING(path)->len+strlen(name)+(dirsep?1:0));
+
+ memcpy(RSTRING(buf)->ptr, RSTRING(path)->ptr, len);
+ if (dirsep) {
+ strcpy(RSTRING(buf)->ptr+len, "/");
+ len++;
+ }
+ strcpy(RSTRING(buf)->ptr+len, name);
+ return buf;
+}
+
+enum answer { YES, NO, UNKNOWN };
+
+#ifndef S_ISDIR
+# define S_ISDIR(m) ((m & S_IFMT) == S_IFDIR)
+#endif
+
+#ifndef S_ISLNK
+# ifndef S_IFLNK
+# define S_ISLNK(m) (0)
+# else
+# define S_ISLNK(m) ((m & S_IFMT) == S_IFLNK)
+# endif
+#endif
+
+struct glob_args {
+ void (*func) _((VALUE, VALUE));
+ VALUE c;
+ VALUE v;
+};
+
+static VALUE glob_func_caller _((VALUE));
+
+static VALUE
+glob_func_caller(val)
+ VALUE val;
+{
+ struct glob_args *args = (struct glob_args *)val;
+ VALUE path = args->c;
+
+ OBJ_TAINT(path);
+ (*args->func)(path, args->v);
+ return Qnil;
+}
+
+static int
+glob_call_func(func, path, arg)
+ void (*func) _((VALUE, VALUE));
+ VALUE path;
+ VALUE arg;
+{
+ int status;
+ struct glob_args args;
+
+ args.func = func;
+ args.c = path;
+ args.v = arg;
+
+ rb_protect(glob_func_caller, (VALUE)&args, &status);
+ return status;
+}
+
+static int
+glob_helper(path, dirsep, exist, isdir, beg, end, flags, func, arg)
+ VALUE path;
+ int dirsep; /* '/' should be placed before appending child entry's name to 'path'. */
+ enum answer exist; /* Does 'path' indicate an existing entry? */
+ enum answer isdir; /* Does 'path' indicate a directory or a symlink to a directory? */
+ struct glob_pattern **beg;
+ struct glob_pattern **end;
+ int flags;
+ void (*func) _((VALUE, VALUE));
+ VALUE arg;
+{
+ struct stat st;
+ int status = 0;
+ struct glob_pattern **cur, **new_beg, **new_end;
+ int plain = 0, magical = 0, recursive = 0, match_all = 0, match_dir = 0;
+ int escape = !(flags & FNM_NOESCAPE);
+
+ for (cur = beg; cur < end; ++cur) {
+ struct glob_pattern *p = *cur;
+ if (p->type == RECURSIVE) {
+ recursive = 1;
+ p = p->next;
+ }
+ switch (p->type) {
+ case PLAIN:
+ plain = 1;
+ break;
+ case MAGICAL:
+ magical = 1;
+ break;
+ case MATCH_ALL:
+ match_all = 1;
+ break;
+ case MATCH_DIR:
+ match_dir = 1;
+ break;
+ }
+ }
+
+ if (RSTRING(path)->len > 0) {
+ if (match_all && exist == UNKNOWN) {
+ if (do_lstat(RSTRING(path)->ptr, &st) == 0) {
+ exist = YES;
+ isdir = S_ISDIR(st.st_mode) ? YES : S_ISLNK(st.st_mode) ? UNKNOWN : NO;
+ }
+ else {
+ exist = NO;
+ isdir = NO;
+ }
+ }
+
+ if (match_dir && isdir == UNKNOWN) {
+ if (do_stat(RSTRING(path)->ptr, &st) == 0) {
+ exist = YES;
+ isdir = S_ISDIR(st.st_mode) ? YES : NO;
+ }
+ else {
+ exist = NO;
+ isdir = NO;
+ }
+ }
+
+ if (match_all && exist == YES) {
+ status = glob_call_func(func, path, arg);
+ if (status) return status;
+ }
+
+ if (match_dir && isdir == YES) {
+ status = glob_call_func(func, join_path(path, dirsep, ""), arg);
+ if (status) return status;
+ }
+ }
+
+ if (exist == NO || isdir == NO) return 0;
+
+ if (magical || recursive) {
+ struct dirent *dp;
+ DIR *dirp = do_opendir(RSTRING(path)->len > 0 ? RSTRING(path)->ptr : ".");
+ if (dirp == NULL) return 0;
+
+ for (dp = readdir(dirp); dp != NULL; dp = readdir(dirp)) {
+ VALUE buf = join_path(path, dirsep, dp->d_name);
+
+ enum answer new_isdir = UNKNOWN;
+ if (recursive && strcmp(dp->d_name, ".") != 0 && strcmp(dp->d_name, "..") != 0
+ && fnmatch("*", dp->d_name, flags) == 0) {
+#ifndef _WIN32
+ if (do_lstat(RSTRING(buf)->ptr, &st) == 0)
+ new_isdir = S_ISDIR(st.st_mode) ? YES : S_ISLNK(st.st_mode) ? UNKNOWN : NO;
+ else
+ new_isdir = NO;
+#else
+ new_isdir = dp->d_isdir ? (!dp->d_isrep ? YES : UNKNOWN) : NO;
+#endif
+ }
+
+ new_beg = new_end = ALLOC_N(struct glob_pattern *, (end - beg) * 2);
+
+ for (cur = beg; cur < end; ++cur) {
+ struct glob_pattern *p = *cur;
+ if (p->type == RECURSIVE) {
+ if (new_isdir == YES) /* not symlink but real directory */
+ *new_end++ = p; /* append recursive pattern */
+ p = p->next; /* 0 times recursion */
+ }
+ if (p->type == PLAIN || p->type == MAGICAL) {
+ if (fnmatch(p->str, dp->d_name, flags) == 0)
+ *new_end++ = p->next;
+ }
+ }
+
+ status = glob_helper(buf, 1, YES, new_isdir, new_beg, new_end, flags, func, arg);
+ free(new_beg);
+ if (status) break;
+ }
+
+ closedir(dirp);
+ }
+ else if (plain) {
+ struct glob_pattern **copy_beg, **copy_end, **cur2;
+
+ copy_beg = copy_end = ALLOC_N(struct glob_pattern *, end - beg);
+ for (cur = beg; cur < end; ++cur)
+ *copy_end++ = (*cur)->type == PLAIN ? *cur : 0;
+
+ for (cur = copy_beg; cur < copy_end; ++cur) {
+ if (*cur) {
+ VALUE buf;
+ char *name;
+ name = ALLOC_N(char, strlen((*cur)->str) + 1);
+ strcpy(name, (*cur)->str);
+ if (escape) remove_backslashes(name);
+
+ new_beg = new_end = ALLOC_N(struct glob_pattern *, end - beg);
+ *new_end++ = (*cur)->next;
+ for (cur2 = cur + 1; cur2 < copy_end; ++cur2) {
+ if (*cur2 && fnmatch((*cur2)->str, name, flags) == 0) {
+ *new_end++ = (*cur2)->next;
+ *cur2 = 0;
+ }
+ }
+
+ buf = join_path(path, dirsep, name);
+ free(name);
+ status = glob_helper(buf, 1, UNKNOWN, UNKNOWN, new_beg, new_end, flags, func, arg);
+ free(new_beg);
+ if (status) break;
+ }
+ }
+
+ free(copy_beg);
+ }
+
+ return status;
+}
+
+static int
+rb_glob2(path, offset, flags, func, arg)
+ VALUE path;
+ long offset;
+ int flags;
+ void (*func) _((VALUE, VALUE));
+ VALUE arg;
+{
+ struct glob_pattern *list;
+ const char *root, *start;
+ VALUE buf;
+ int n;
+ int status;
+
+ if (flags & FNM_CASEFOLD) {
+ rb_warn("Dir.glob() ignores File::FNM_CASEFOLD");
+ }
+
+ start = root = StringValuePtr(path) + offset;
+#if defined DOSISH
+ flags |= FNM_CASEFOLD;
+ root = rb_path_skip_prefix(root);
+#else
+ flags &= ~FNM_CASEFOLD;
+#endif
+
+ if (root && *root == '/') root++;
+
+ n = root - start;
+ buf = rb_str_new(start, n);
+
+ list = glob_make_pattern(root, flags);
+ status = glob_helper(buf, 0, UNKNOWN, UNKNOWN, &list, &list + 1, flags, func, arg);
+ glob_free_pattern(list);
+
+ return status;
+}
+
+struct rb_glob_args {
+ void (*func) _((const char*, VALUE));
+ VALUE arg;
+};
+
+static VALUE
+rb_glob_caller(path, a)
+ VALUE path, a;
+{
+ struct rb_glob_args *args = (struct rb_glob_args *)a;
+ (*args->func)(RSTRING(path)->ptr, args->arg);
+ return Qnil;
+}
+
+void
+rb_glob(path, func, arg)
+ const char *path;
+ void (*func) _((const char*, VALUE));
+ VALUE arg;
+{
+ struct rb_glob_args args;
+ int status;
+
+ args.func = func;
+ args.arg = arg;
+ status = rb_glob2(rb_str_new2(path), 0, 0, rb_glob_caller, &args);
+
+ if (status) rb_jump_tag(status);
+}
+
+static void
+push_pattern(path, ary)
+ VALUE path, ary;
+{
+ rb_ary_push(ary, path);
+}
+
+static int
+push_glob(VALUE ary, VALUE s, long offset, int flags);
+
+static int
+push_glob(ary, str, offset, flags)
+ VALUE ary;
+ VALUE str;
+ long offset;
+ int flags;
+{
+ const int escape = !(flags & FNM_NOESCAPE);
+
+ const char *p = RSTRING(str)->ptr + offset;
+ const char *s = p;
+ const char *lbrace = 0, *rbrace = 0;
+ int nest = 0, status = 0;
+
+ while (*p) {
+ if (*p == '{' && nest++ == 0) {
+ lbrace = p;
+ }
+ if (*p == '}' && --nest <= 0) {
+ rbrace = p;
+ break;
+ }
+ if (*p == '\\' && escape) {
+ if (!*++p) break;
+ }
+ Inc(p);
+ }
+
+ if (lbrace && rbrace) {
+ VALUE buffer = rb_str_new(0, strlen(s));
+ char *buf;
+ long shift;
+
+ buf = RSTRING(buffer)->ptr;
+ memcpy(buf, s, lbrace-s);
+ shift = (lbrace-s);
+ p = lbrace;
+ while (p < rbrace) {
+ const char *t = ++p;
+ nest = 0;
+ while (p < rbrace && !(*p == ',' && nest == 0)) {
+ if (*p == '{') nest++;
+ if (*p == '}') nest--;
+ if (*p == '\\' && escape) {
+ if (++p == rbrace) break;
+ }
+ Inc(p);
+ }
+ memcpy(buf+shift, t, p-t);
+ strcpy(buf+shift+(p-t), rbrace+1);
+ status = push_glob(ary, buffer, offset, flags);
+ if (status) break;
+ }
+ }
+ else if (!lbrace && !rbrace) {
+ status = rb_glob2(str, offset, flags, push_pattern, ary);
+ }
+
+ return status;
+}
+
+static VALUE
+rb_push_glob(str, flags) /* '\0' is delimiter */
+ VALUE str;
+ int flags;
+{
+ long offset = 0;
+ VALUE ary;
+
+ FilePathValue(str);
+
+ ary = rb_ary_new();
+
+ while (offset < RSTRING(str)->len) {
+ int status = push_glob(ary, str, offset, flags);
+ char *p, *pend;
+ if (status) rb_jump_tag(status);
+ p = RSTRING(str)->ptr + offset;
+ p += strlen(p) + 1;
+ pend = RSTRING(str)->ptr + RSTRING(str)->len;
+ while (p < pend && !*p)
+ p++;
+ offset = p - RSTRING(str)->ptr;
+ }
+
+ if (rb_block_given_p()) {
+ rb_ary_each(ary);
+ return Qnil;
+ }
+ return ary;
+}
+
+/*
+ * call-seq:
+ * Dir[ string ] => array
+ *
+ * Equivalent to calling
+ * <em>dir</em>.<code>glob(</code><i>string,</i><code>0)</code>.
+ *
+ */
+static VALUE
+dir_s_aref(obj, str)
+ VALUE obj, str;
+{
+ return rb_push_glob(str, 0);
+}
+
+/*
+ * call-seq:
+ * Dir.glob( string, [flags] ) => array
+ * Dir.glob( string, [flags] ) {| filename | block } => nil
+ *
+ * Returns the filenames found by expanding the pattern given in
+ * <i>string</i>, either as an <i>array</i> or as parameters to the
+ * block. Note that this pattern is not a regexp (it's closer to a
+ * shell glob). See <code>File::fnmatch</code> for the meaning of
+ * the <i>flags</i> parameter. Note that case sensitivity
+ * depends on your system (so <code>File::FNM_CASEFOLD</code> is ignored)
+ *
+ * <code>*</code>:: Matches any file. Can be restricted by
+ * other values in the glob. <code>*</code>
+ * will match all files; <code>c*</code> will
+ * match all files beginning with
+ * <code>c</code>; <code>*c</code> will match
+ * all files ending with <code>c</code>; and
+ * <code>*c*</code> will match all files that
+ * have <code>c</code> in them (including at
+ * the beginning or end). Equivalent to
+ * <code>/ .* /x</code> in regexp.
+ * <code>**</code>:: Matches directories recursively.
+ * <code>?</code>:: Matches any one character. Equivalent to
+ * <code>/.{1}/</code> in regexp.
+ * <code>[set]</code>:: Matches any one character in +set+.
+ * Behaves exactly like character sets in
+ * Regexp, including set negation
+ * (<code>[^a-z]</code>).
+ * <code>{p,q}</code>:: Matches either literal <code>p</code> or
+ * literal <code>q</code>. Matching literals
+ * may be more than one character in length.
+ * More than two literals may be specified.
+ * Equivalent to pattern alternation in
+ * regexp.
+ * <code>\</code>:: Escapes the next metacharacter.
+ *
+ * Dir["config.?"] #=> ["config.h"]
+ * Dir.glob("config.?") #=> ["config.h"]
+ * Dir.glob("*.[a-z][a-z]") #=> ["main.rb"]
+ * Dir.glob("*.[^r]*") #=> ["config.h"]
+ * Dir.glob("*.{rb,h}") #=> ["main.rb", "config.h"]
+ * Dir.glob("*") #=> ["config.h", "main.rb"]
+ * Dir.glob("*", File::FNM_DOTMATCH) #=> [".", "..", "config.h", "main.rb"]
+ *
+ * rbfiles = File.join("**", "*.rb")
+ * Dir.glob(rbfiles) #=> ["main.rb",
+ * "lib/song.rb",
+ * "lib/song/karaoke.rb"]
+ * libdirs = File.join("**", "lib")
+ * Dir.glob(libdirs) #=> ["lib"]
+ *
+ * librbfiles = File.join("**", "lib", "**", "*.rb")
+ * Dir.glob(librbfiles) #=> ["lib/song.rb",
+ * "lib/song/karaoke.rb"]
+ *
+ * librbfiles = File.join("**", "lib", "*.rb")
+ * Dir.glob(librbfiles) #=> ["lib/song.rb"]
+ */
+static VALUE
+dir_s_glob(argc, argv, obj)
+ int argc;
+ VALUE *argv;
+ VALUE obj;
+{
+ VALUE str, rflags;
+ int flags;
+
+ if (rb_scan_args(argc, argv, "11", &str, &rflags) == 2)
+ flags = NUM2INT(rflags);
+ else
+ flags = 0;
+
+ return rb_push_glob(str, flags);
+}
+
+static VALUE
+dir_open_dir(path)
+ VALUE path;
+{
+ struct dir_data *dp;
+ VALUE dir = rb_funcall(rb_cDir, rb_intern("open"), 1, path);
+
+ if (TYPE(dir) != T_DATA ||
+ RDATA(dir)->dfree != (RUBY_DATA_FUNC)free_dir) {
+ rb_raise(rb_eTypeError, "wrong argument type %s (expected Dir)",
+ rb_obj_classname(dir));
+ }
+ return dir;
+}
+
+
+/*
+ * call-seq:
+ * Dir.foreach( dirname ) {| filename | block } => nil
+ *
+ * Calls the block once for each entry in the named directory, passing
+ * the filename of each entry as a parameter to the block.
+ *
+ * Dir.foreach("testdir") {|x| puts "Got #{x}" }
+ *
+ * <em>produces:</em>
+ *
+ * Got .
+ * Got ..
+ * Got config.h
+ * Got main.rb
+ *
+ */
+static VALUE
+dir_foreach(io, dirname)
+ VALUE io, dirname;
+{
+ VALUE dir;
+
+ dir = dir_open_dir(dirname);
+ rb_ensure(dir_each, dir, dir_close, dir);
+ return Qnil;
+}
+
+/*
+ * call-seq:
+ * Dir.entries( dirname ) => array
+ *
+ * Returns an array containing all of the filenames in the given
+ * directory. Will raise a <code>SystemCallError</code> if the named
+ * directory doesn't exist.
+ *
+ * Dir.entries("testdir") #=> [".", "..", "config.h", "main.rb"]
+ *
+ */
+static VALUE
+dir_entries(io, dirname)
+ VALUE io, dirname;
+{
+ VALUE dir;
+
+ dir = dir_open_dir(dirname);
+ return rb_ensure(rb_Array, dir, dir_close, dir);
+}
+
+/*
+ * call-seq:
+ * File.fnmatch( pattern, path, [flags] ) => (true or false)
+ * File.fnmatch?( pattern, path, [flags] ) => (true or false)
+ *
+ * Returns true if <i>path</i> matches against <i>pattern</i> The
+ * pattern is not a regular expression; instead it follows rules
+ * similar to shell filename globbing. It may contain the following
+ * metacharacters:
+ *
+ * <code>*</code>:: Matches any file. Can be restricted by
+ * other values in the glob. <code>*</code>
+ * will match all files; <code>c*</code> will
+ * match all files beginning with
+ * <code>c</code>; <code>*c</code> will match
+ * all files ending with <code>c</code>; and
+ * <code>*c*</code> will match all files that
+ * have <code>c</code> in them (including at
+ * the beginning or end). Equivalent to
+ * <code>/ .* /x</code> in regexp.
+ * <code>**</code>:: Matches directories recursively or files
+ * expansively.
+ * <code>?</code>:: Matches any one character. Equivalent to
+ * <code>/.{1}/</code> in regexp.
+ * <code>[set]</code>:: Matches any one character in +set+.
+ * Behaves exactly like character sets in
+ * Regexp, including set negation
+ * (<code>[^a-z]</code>).
+ * <code>\</code>:: Escapes the next metacharacter.
+ *
+ * <i>flags</i> is a bitwise OR of the <code>FNM_xxx</code>
+ * parameters. The same glob pattern and flags are used by
+ * <code>Dir::glob</code>.
+ *
+ * File.fnmatch('cat', 'cat') #=> true : match entire string
+ * File.fnmatch('cat', 'category') #=> false : only match partial string
+ * File.fnmatch('c{at,ub}s', 'cats') #=> false : { } isn't supported
+ *
+ * File.fnmatch('c?t', 'cat') #=> true : '?' match only 1 character
+ * File.fnmatch('c??t', 'cat') #=> false : ditto
+ * File.fnmatch('c*', 'cats') #=> true : '*' match 0 or more characters
+ * File.fnmatch('c*t', 'c/a/b/t') #=> true : ditto
+ * File.fnmatch('ca[a-z]', 'cat') #=> true : inclusive bracket expression
+ * File.fnmatch('ca[^t]', 'cat') #=> false : exclusive bracket expression ('^' or '!')
+ *
+ * File.fnmatch('cat', 'CAT') #=> false : case sensitive
+ * File.fnmatch('cat', 'CAT', File::FNM_CASEFOLD) #=> true : case insensitive
+ *
+ * File.fnmatch('?', '/', File::FNM_PATHNAME) #=> false : wildcard doesn't match '/' on FNM_PATHNAME
+ * File.fnmatch('*', '/', File::FNM_PATHNAME) #=> false : ditto
+ * File.fnmatch('[/]', '/', File::FNM_PATHNAME) #=> false : ditto
+ *
+ * File.fnmatch('\?', '?') #=> true : escaped wildcard becomes ordinary
+ * File.fnmatch('\a', 'a') #=> true : escaped ordinary remains ordinary
+ * File.fnmatch('\a', '\a', File::FNM_NOESCAPE) #=> true : FNM_NOESACPE makes '\' ordinary
+ * File.fnmatch('[\?]', '?') #=> true : can escape inside bracket expression
+ *
+ * File.fnmatch('*', '.profile') #=> false : wildcard doesn't match leading
+ * File.fnmatch('*', '.profile', File::FNM_DOTMATCH) #=> true period by default.
+ * File.fnmatch('.*', '.profile') #=> true
+ *
+ * rbfiles = File.join("**", "*.rb")
+ * File.fnmatch(rbfiles, 'main.rb') #=> false
+ * File.fnmatch(rbfiles, './main.rb') #=> false
+ * File.fnmatch(rbfiles, 'lib/song.rb') #=> true
+ * File.fnmatch('**.rb', 'main.rb') #=> true
+ * File.fnmatch('**.rb', './main.rb') #=> false
+ * File.fnmatch('**.rb', 'lib/song.rb') #=> true
+ * File.fnmatch('*', 'dave/.profile') #=> true
+ *
+ * File.fnmatch('* IGNORE /*', 'dave/.profile', File::FNM_PATHNAME) #=> false
+ * File.fnmatch('* IGNORE /*', 'dave/.profile', File::FNM_PATHNAME | File::FNM_DOTMATCH) #=> true
+ *
+ * File.fnmatch('** IGNORE /foo', 'a/b/c/foo', File::FNM_PATHNAME) #=> true
+ * File.fnmatch('** IGNORE /foo', '/a/b/c/foo', File::FNM_PATHNAME) #=> true
+ * File.fnmatch('** IGNORE /foo', 'c:/a/b/c/foo', File::FNM_PATHNAME) #=> true
+ * File.fnmatch('** IGNORE /foo', 'a/.b/c/foo', File::FNM_PATHNAME) #=> false
+ * File.fnmatch('** IGNORE /foo', 'a/.b/c/foo', File::FNM_PATHNAME | File::FNM_DOTMATCH) #=> true
+ */
+static VALUE
+file_s_fnmatch(argc, argv, obj)
+ int argc;
+ VALUE *argv;
+ VALUE obj;
+{
+ VALUE pattern, path;
+ VALUE rflags;
+ int flags;
+
+ if (rb_scan_args(argc, argv, "21", &pattern, &path, &rflags) == 3)
+ flags = NUM2INT(rflags);
+ else
+ flags = 0;
+
+ StringValue(pattern);
+ StringValue(path);
+
+ if (fnmatch(RSTRING(pattern)->ptr, RSTRING(path)->ptr, flags) == 0)
+ return Qtrue;
+
+ return Qfalse;
+}
+
+/*
+ * Objects of class <code>Dir</code> are directory streams representing
+ * directories in the underlying file system. They provide a variety of
+ * ways to list directories and their contents. See also
+ * <code>File</code>.
+ *
+ * The directory used in these examples contains the two regular files
+ * (<code>config.h</code> and <code>main.rb</code>), the parent
+ * directory (<code>..</code>), and the directory itself
+ * (<code>.</code>).
+ */
+void
+Init_Dir()
+{
+ rb_cDir = rb_define_class("Dir", rb_cObject);
+
+ rb_include_module(rb_cDir, rb_mEnumerable);
+
+ rb_define_alloc_func(rb_cDir, dir_s_alloc);
+ rb_define_singleton_method(rb_cDir, "open", dir_s_open, 1);
+ rb_define_singleton_method(rb_cDir, "foreach", dir_foreach, 1);
+ rb_define_singleton_method(rb_cDir, "entries", dir_entries, 1);
+
+ rb_define_method(rb_cDir,"initialize", dir_initialize, 1);
+ rb_define_method(rb_cDir,"path", dir_path, 0);
+ rb_define_method(rb_cDir,"inspect", dir_inspect, 0);
+ rb_define_method(rb_cDir,"read", dir_read, 0);
+ rb_define_method(rb_cDir,"each", dir_each, 0);
+ rb_define_method(rb_cDir,"rewind", dir_rewind, 0);
+ rb_define_method(rb_cDir,"tell", dir_tell, 0);
+ rb_define_method(rb_cDir,"seek", dir_seek, 1);
+ rb_define_method(rb_cDir,"pos", dir_tell, 0);
+ rb_define_method(rb_cDir,"pos=", dir_set_pos, 1);
+ rb_define_method(rb_cDir,"close", dir_close, 0);
+
+ rb_define_singleton_method(rb_cDir,"chdir", dir_s_chdir, -1);
+ rb_define_singleton_method(rb_cDir,"getwd", dir_s_getwd, 0);
+ rb_define_singleton_method(rb_cDir,"pwd", dir_s_getwd, 0);
+ rb_define_singleton_method(rb_cDir,"chroot", dir_s_chroot, 1);
+ rb_define_singleton_method(rb_cDir,"mkdir", dir_s_mkdir, -1);
+ rb_define_singleton_method(rb_cDir,"rmdir", dir_s_rmdir, 1);
+ rb_define_singleton_method(rb_cDir,"delete", dir_s_rmdir, 1);
+ rb_define_singleton_method(rb_cDir,"unlink", dir_s_rmdir, 1);
+
+ rb_define_singleton_method(rb_cDir,"glob", dir_s_glob, -1);
+ rb_define_singleton_method(rb_cDir,"[]", dir_s_aref, 1);
+
+ rb_define_singleton_method(rb_cFile,"fnmatch", file_s_fnmatch, -1);
+ rb_define_singleton_method(rb_cFile,"fnmatch?", file_s_fnmatch, -1);
+
+ rb_file_const("FNM_NOESCAPE", INT2FIX(FNM_NOESCAPE));
+ rb_file_const("FNM_PATHNAME", INT2FIX(FNM_PATHNAME));
+ rb_file_const("FNM_DOTMATCH", INT2FIX(FNM_DOTMATCH));
+ rb_file_const("FNM_CASEFOLD", INT2FIX(FNM_CASEFOLD));
+}
+/**********************************************************************
+
+ dln.c -
+
+ $Author: murphy $
+ $Date: 2005-11-05 04:33:55 +0100 (Sa, 05 Nov 2005) $
+ created at: Tue Jan 18 17:05:06 JST 1994
+
+ Copyright (C) 1993-2003 Yukihiro Matsumoto
+
+**********************************************************************/
+
+#include "ruby.h"
+#include "dln.h"
+
+#ifdef HAVE_STDLIB_H
+# include <stdlib.h>
+#endif
+
+#ifdef __CHECKER__
+#undef HAVE_DLOPEN
+#undef USE_DLN_A_OUT
+#undef USE_DLN_DLOPEN
+#endif
+
+#ifdef USE_DLN_A_OUT
+char *dln_argv0;
+#endif
+
+#ifdef _AIX
+#pragma alloca
+#endif
+
+#if defined(HAVE_ALLOCA_H)
+#include <alloca.h>
+#endif
+
+#ifdef HAVE_STRING_H
+# include <string.h>
+#else
+# include <strings.h>
+#endif
+
+#ifndef xmalloc
+void *xmalloc();
+void *xcalloc();
+void *xrealloc();
+#endif
+
+#include <stdio.h>
+#if defined(_WIN32) || defined(__VMS)
+#include "missing/file.h"
+#endif
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#ifndef S_ISDIR
+# define S_ISDIR(m) ((m & S_IFMT) == S_IFDIR)
+#endif
+
+#ifdef HAVE_SYS_PARAM_H
+# include <sys/param.h>
+#endif
+#ifndef MAXPATHLEN
+# define MAXPATHLEN 1024
+#endif
+
+#ifdef HAVE_UNISTD_H
+# include <unistd.h>
+#endif
+
+#ifndef _WIN32
+char *getenv();
+#endif
+
+#if defined(__VMS)
+#pragma builtins
+#include <dlfcn.h>
+#endif
+
+#ifdef __MACOS__
+# include <TextUtils.h>
+# include <CodeFragments.h>
+# include <Aliases.h>
+# include "macruby_private.h"
+#endif
+
+#ifdef __BEOS__
+# include <image.h>
+#endif
+
+int eaccess();
+
+#ifndef NO_DLN_LOAD
+
+#if defined(HAVE_DLOPEN) && !defined(USE_DLN_A_OUT) && !defined(_AIX) && !defined(__APPLE__) && !defined(_UNICOSMP)
+/* dynamic load with dlopen() */
+# define USE_DLN_DLOPEN
+#endif
+
+#ifndef FUNCNAME_PATTERN
+# if defined(__hp9000s300) || (defined(__NetBSD__) && !defined(__ELF__)) || defined(__BORLANDC__) || (defined(__FreeBSD__) && !defined(__ELF__)) || (defined(__OpenBSD__) && !defined(__ELF__)) || defined(NeXT) || defined(__WATCOMC__) || defined(__APPLE__)
+# define FUNCNAME_PATTERN "_Init_%s"
+# else
+# define FUNCNAME_PATTERN "Init_%s"
+# endif
+#endif
+
+static int
+init_funcname_len(buf, file)
+ char **buf;
+ const char *file;
+{
+ char *p;
+ const char *slash;
+ int len;
+
+ /* Load the file as an object one */
+ for (slash = file-1; *file; file++) /* Find position of last '/' */
+#ifdef __MACOS__
+ if (*file == ':') slash = file;
+#else
+ if (*file == '/') slash = file;
+#endif
+
+ len = strlen(FUNCNAME_PATTERN) + strlen(slash + 1);
+ *buf = xmalloc(len);
+ snprintf(*buf, len, FUNCNAME_PATTERN, slash + 1);
+ for (p = *buf; *p; p++) { /* Delete suffix if it exists */
+ if (*p == '.') {
+ *p = '\0'; break;
+ }
+ }
+ return p - *buf;
+}
+
+#define init_funcname(buf, file) do {\
+ int len = init_funcname_len(buf, file);\
+ char *tmp = ALLOCA_N(char, len+1);\
+ if (!tmp) {\
+ free(*buf);\
+ rb_memerror();\
+ }\
+ strcpy(tmp, *buf);\
+ free(*buf);\
+ *buf = tmp;\
+} while (0)
+
+#ifdef USE_DLN_A_OUT
+
+#ifndef LIBC_NAME
+# define LIBC_NAME "libc.a"
+#endif
+
+#ifndef DLN_DEFAULT_LIB_PATH
+# define DLN_DEFAULT_LIB_PATH "/lib:/usr/lib:/usr/local/lib:."
+#endif
+
+#include <errno.h>
+
+static int dln_errno;
+
+#define DLN_ENOEXEC ENOEXEC /* Exec format error */
+#define DLN_ECONFL 1201 /* Symbol name conflict */
+#define DLN_ENOINIT 1202 /* No initializer given */
+#define DLN_EUNDEF 1203 /* Undefine symbol remains */
+#define DLN_ENOTLIB 1204 /* Not a library file */
+#define DLN_EBADLIB 1205 /* Malformed library file */
+#define DLN_EINIT 1206 /* Not initialized */
+
+static int dln_init_p = 0;
+
+#include <ar.h>
+#include <a.out.h>
+#ifndef N_COMM
+# define N_COMM 0x12
+#endif
+#ifndef N_MAGIC
+# define N_MAGIC(x) (x).a_magic
+#endif
+
+#define INVALID_OBJECT(h) (N_MAGIC(h) != OMAGIC)
+
+#include "util.h"
+#include "st.h"
+
+static st_table *sym_tbl;
+static st_table *undef_tbl;
+
+static int load_lib();
+
+static int
+load_header(fd, hdrp, disp)
+ int fd;
+ struct exec *hdrp;
+ long disp;
+{
+ int size;
+
+ lseek(fd, disp, 0);
+ size = read(fd, hdrp, sizeof(struct exec));
+ if (size == -1) {
+ dln_errno = errno;
+ return -1;
+ }
+ if (size != sizeof(struct exec) || N_BADMAG(*hdrp)) {
+ dln_errno = DLN_ENOEXEC;
+ return -1;
+ }
+ return 0;
+}
+
+#if defined(sequent)
+#define RELOC_SYMBOL(r) ((r)->r_symbolnum)
+#define RELOC_MEMORY_SUB_P(r) ((r)->r_bsr)
+#define RELOC_PCREL_P(r) ((r)->r_pcrel || (r)->r_bsr)
+#define RELOC_TARGET_SIZE(r) ((r)->r_length)
+#endif
+
+/* Default macros */
+#ifndef RELOC_ADDRESS
+#define RELOC_ADDRESS(r) ((r)->r_address)
+#define RELOC_EXTERN_P(r) ((r)->r_extern)
+#define RELOC_SYMBOL(r) ((r)->r_symbolnum)
+#define RELOC_MEMORY_SUB_P(r) 0
+#define RELOC_PCREL_P(r) ((r)->r_pcrel)
+#define RELOC_TARGET_SIZE(r) ((r)->r_length)
+#endif
+
+#if defined(sun) && defined(sparc)
+/* Sparc (Sun 4) macros */
+# undef relocation_info
+# define relocation_info reloc_info_sparc
+# define R_RIGHTSHIFT(r) (reloc_r_rightshift[(r)->r_type])
+# define R_BITSIZE(r) (reloc_r_bitsize[(r)->r_type])
+# define R_LENGTH(r) (reloc_r_length[(r)->r_type])
+static int reloc_r_rightshift[] = {
+ 0, 0, 0, 0, 0, 0, 2, 2, 10, 0, 0, 0, 0, 0, 0,
+};
+static int reloc_r_bitsize[] = {
+ 8, 16, 32, 8, 16, 32, 30, 22, 22, 22, 13, 10, 32, 32, 16,
+};
+static int reloc_r_length[] = {
+ 0, 1, 2, 0, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+};
+# define R_PCREL(r) \
+ ((r)->r_type >= RELOC_DISP8 && (r)->r_type <= RELOC_WDISP22)
+# define R_SYMBOL(r) ((r)->r_index)
+#endif
+
+#if defined(sequent)
+#define R_SYMBOL(r) ((r)->r_symbolnum)
+#define R_MEMORY_SUB(r) ((r)->r_bsr)
+#define R_PCREL(r) ((r)->r_pcrel || (r)->r_bsr)
+#define R_LENGTH(r) ((r)->r_length)
+#endif
+
+#ifndef R_SYMBOL
+# define R_SYMBOL(r) ((r)->r_symbolnum)
+# define R_MEMORY_SUB(r) 0
+# define R_PCREL(r) ((r)->r_pcrel)
+# define R_LENGTH(r) ((r)->r_length)
+#endif
+
+static struct relocation_info *
+load_reloc(fd, hdrp, disp)
+ int fd;
+ struct exec *hdrp;
+ long disp;
+{
+ struct relocation_info *reloc;
+ int size;
+
+ lseek(fd, disp + N_TXTOFF(*hdrp) + hdrp->a_text + hdrp->a_data, 0);
+ size = hdrp->a_trsize + hdrp->a_drsize;
+ reloc = (struct relocation_info*)xmalloc(size);
+ if (reloc == NULL) {
+ dln_errno = errno;
+ return NULL;
+ }
+
+ if (read(fd, reloc, size) != size) {
+ dln_errno = errno;
+ free(reloc);
+ return NULL;
+ }
+
+ return reloc;
+}
+
+static struct nlist *
+load_sym(fd, hdrp, disp)
+ int fd;
+ struct exec *hdrp;
+ long disp;
+{
+ struct nlist * buffer;
+ struct nlist * sym;
+ struct nlist * end;
+ long displ;
+ int size;
+
+ lseek(fd, N_SYMOFF(*hdrp) + hdrp->a_syms + disp, 0);
+ if (read(fd, &size, sizeof(int)) != sizeof(int)) {
+ goto err_noexec;
+ }
+
+ buffer = (struct nlist*)xmalloc(hdrp->a_syms + size);
+ if (buffer == NULL) {
+ dln_errno = errno;
+ return NULL;
+ }
+
+ lseek(fd, disp + N_SYMOFF(*hdrp), 0);
+ if (read(fd, buffer, hdrp->a_syms + size) != hdrp->a_syms + size) {
+ free(buffer);
+ goto err_noexec;
+ }
+
+ sym = buffer;
+ end = sym + hdrp->a_syms / sizeof(struct nlist);
+ displ = (long)buffer + (long)(hdrp->a_syms);
+
+ while (sym < end) {
+ sym->n_un.n_name = (char*)sym->n_un.n_strx + displ;
+ sym++;
+ }
+ return buffer;
+
+ err_noexec:
+ dln_errno = DLN_ENOEXEC;
+ return NULL;
+}
+
+static st_table *
+sym_hash(hdrp, syms)
+ struct exec *hdrp;
+ struct nlist *syms;
+{
+ st_table *tbl;
+ struct nlist *sym = syms;
+ struct nlist *end = syms + (hdrp->a_syms / sizeof(struct nlist));
+
+ tbl = st_init_strtable();
+ if (tbl == NULL) {
+ dln_errno = errno;
+ return NULL;
+ }
+
+ while (sym < end) {
+ st_insert(tbl, sym->n_un.n_name, sym);
+ sym++;
+ }
+ return tbl;
+}
+
+static int
+dln_init(prog)
+ const char *prog;
+{
+ char *file;
+ int fd;
+ struct exec hdr;
+ struct nlist *syms;
+
+ if (dln_init_p == 1) return 0;
+
+ file = dln_find_exe(prog, NULL);
+ if (file == NULL || (fd = open(file, O_RDONLY)) < 0) {
+ dln_errno = errno;
+ return -1;
+ }
+
+ if (load_header(fd, &hdr, 0) == -1) return -1;
+ syms = load_sym(fd, &hdr, 0);
+ if (syms == NULL) {
+ close(fd);
+ return -1;
+ }
+ sym_tbl = sym_hash(&hdr, syms);
+ if (sym_tbl == NULL) { /* file may be start with #! */
+ char c = '\0';
+ char buf[MAXPATHLEN];
+ char *p;
+
+ free(syms);
+ lseek(fd, 0L, 0);
+ if (read(fd, &c, 1) == -1) {
+ dln_errno = errno;
+ return -1;
+ }
+ if (c != '#') goto err_noexec;
+ if (read(fd, &c, 1) == -1) {
+ dln_errno = errno;
+ return -1;
+ }
+ if (c != '!') goto err_noexec;
+
+ p = buf;
+ /* skip forwarding spaces */
+ while (read(fd, &c, 1) == 1) {
+ if (c == '\n') goto err_noexec;
+ if (c != '\t' && c != ' ') {
+ *p++ = c;
+ break;
+ }
+ }
+ /* read in command name */
+ while (read(fd, p, 1) == 1) {
+ if (*p == '\n' || *p == '\t' || *p == ' ') break;
+ p++;
+ if (p-buf >= MAXPATHLEN) {
+ dln_errno = ENAMETOOLONG;
+ return -1;
+ }
+ }
+ *p = '\0';
+
+ return dln_init(buf);
+ }
+ dln_init_p = 1;
+ undef_tbl = st_init_strtable();
+ close(fd);
+ return 0;
+
+ err_noexec:
+ close(fd);
+ dln_errno = DLN_ENOEXEC;
+ return -1;
+}
+
+static long
+load_text_data(fd, hdrp, bss, disp)
+ int fd;
+ struct exec *hdrp;
+ int bss;
+ long disp;
+{
+ int size;
+ unsigned char* addr;
+
+ lseek(fd, disp + N_TXTOFF(*hdrp), 0);
+ size = hdrp->a_text + hdrp->a_data;
+
+ if (bss == -1) size += hdrp->a_bss;
+ else if (bss > 1) size += bss;
+
+ addr = (unsigned char*)xmalloc(size);
+ if (addr == NULL) {
+ dln_errno = errno;
+ return 0;
+ }
+
+ if (read(fd, addr, size) != size) {
+ dln_errno = errno;
+ free(addr);
+ return 0;
+ }
+
+ if (bss == -1) {
+ memset(addr + hdrp->a_text + hdrp->a_data, 0, hdrp->a_bss);
+ }
+ else if (bss > 0) {
+ memset(addr + hdrp->a_text + hdrp->a_data, 0, bss);
+ }
+
+ return (long)addr;
+}
+
+static int
+undef_print(key, value)
+ char *key, *value;
+{
+ fprintf(stderr, " %s\n", key);
+ return ST_CONTINUE;
+}
+
+static void
+dln_print_undef()
+{
+ fprintf(stderr, " Undefined symbols:\n");
+ st_foreach(undef_tbl, undef_print, NULL);
+}
+
+static void
+dln_undefined()
+{
+ if (undef_tbl->num_entries > 0) {
+ fprintf(stderr, "dln: Calling undefined function\n");
+ dln_print_undef();
+ rb_exit(1);
+ }
+}
+
+struct undef {
+ char *name;
+ struct relocation_info reloc;
+ long base;
+ char *addr;
+ union {
+ char c;
+ short s;
+ long l;
+ } u;
+};
+
+static st_table *reloc_tbl = NULL;
+static void
+link_undef(name, base, reloc)
+ const char *name;
+ long base;
+ struct relocation_info *reloc;
+{
+ static int u_no = 0;
+ struct undef *obj;
+ char *addr = (char*)(reloc->r_address + base);
+
+ obj = (struct undef*)xmalloc(sizeof(struct undef));
+ obj->name = strdup(name);
+ obj->reloc = *reloc;
+ obj->base = base;
+ switch (R_LENGTH(reloc)) {
+ case 0: /* byte */
+ obj->u.c = *addr;
+ break;
+ case 1: /* word */
+ obj->u.s = *(short*)addr;
+ break;
+ case 2: /* long */
+ obj->u.l = *(long*)addr;
+ break;
+ }
+ if (reloc_tbl == NULL) {
+ reloc_tbl = st_init_numtable();
+ }
+ st_insert(reloc_tbl, u_no++, obj);
+}
+
+struct reloc_arg {
+ const char *name;
+ long value;
+};
+
+static int
+reloc_undef(no, undef, arg)
+ int no;
+ struct undef *undef;
+ struct reloc_arg *arg;
+{
+ int datum;
+ char *address;
+#if defined(sun) && defined(sparc)
+ unsigned int mask = 0;
+#endif
+
+ if (strcmp(arg->name, undef->name) != 0) return ST_CONTINUE;
+ address = (char*)(undef->base + undef->reloc.r_address);
+ datum = arg->value;
+
+ if (R_PCREL(&(undef->reloc))) datum -= undef->base;
+#if defined(sun) && defined(sparc)
+ datum += undef->reloc.r_addend;
+ datum >>= R_RIGHTSHIFT(&(undef->reloc));
+ mask = (1 << R_BITSIZE(&(undef->reloc))) - 1;
+ mask |= mask -1;
+ datum &= mask;
+ switch (R_LENGTH(&(undef->reloc))) {
+ case 0:
+ *address = undef->u.c;
+ *address &= ~mask;
+ *address |= datum;
+ break;
+ case 1:
+ *(short *)address = undef->u.s;
+ *(short *)address &= ~mask;
+ *(short *)address |= datum;
+ break;
+ case 2:
+ *(long *)address = undef->u.l;
+ *(long *)address &= ~mask;
+ *(long *)address |= datum;
+ break;
+ }
+#else
+ switch (R_LENGTH(&(undef->reloc))) {
+ case 0: /* byte */
+ if (R_MEMORY_SUB(&(undef->reloc)))
+ *address = datum - *address;
+ else *address = undef->u.c + datum;
+ break;
+ case 1: /* word */
+ if (R_MEMORY_SUB(&(undef->reloc)))
+ *(short*)address = datum - *(short*)address;
+ else *(short*)address = undef->u.s + datum;
+ break;
+ case 2: /* long */
+ if (R_MEMORY_SUB(&(undef->reloc)))
+ *(long*)address = datum - *(long*)address;
+ else *(long*)address = undef->u.l + datum;
+ break;
+ }
+#endif
+ free(undef->name);
+ free(undef);
+ return ST_DELETE;
+}
+
+static void
+unlink_undef(name, value)
+ const char *name;
+ long value;
+{
+ struct reloc_arg arg;
+
+ arg.name = name;
+ arg.value = value;
+ st_foreach(reloc_tbl, reloc_undef, &arg);
+}
+
+#ifdef N_INDR
+struct indr_data {
+ char *name0, *name1;
+};
+
+static int
+reloc_repl(no, undef, data)
+ int no;
+ struct undef *undef;
+ struct indr_data *data;
+{
+ if (strcmp(data->name0, undef->name) == 0) {
+ free(undef->name);
+ undef->name = strdup(data->name1);
+ }
+ return ST_CONTINUE;
+}
+#endif
+
+static int
+load_1(fd, disp, need_init)
+ int fd;
+ long disp;
+ const char *need_init;
+{
+ static char *libc = LIBC_NAME;
+ struct exec hdr;
+ struct relocation_info *reloc = NULL;
+ long block = 0;
+ long new_common = 0; /* Length of new common */
+ struct nlist *syms = NULL;
+ struct nlist *sym;
+ struct nlist *end;
+ int init_p = 0;
+
+ if (load_header(fd, &hdr, disp) == -1) return -1;
+ if (INVALID_OBJECT(hdr)) {
+ dln_errno = DLN_ENOEXEC;
+ return -1;
+ }
+ reloc = load_reloc(fd, &hdr, disp);
+ if (reloc == NULL) return -1;
+
+ syms = load_sym(fd, &hdr, disp);
+ if (syms == NULL) {
+ free(reloc);
+ return -1;
+ }
+
+ sym = syms;
+ end = syms + (hdr.a_syms / sizeof(struct nlist));
+ while (sym < end) {
+ struct nlist *old_sym;
+ int value = sym->n_value;
+
+#ifdef N_INDR
+ if (sym->n_type == (N_INDR | N_EXT)) {
+ char *key = sym->n_un.n_name;
+
+ if (st_lookup(sym_tbl, sym[1].n_un.n_name, &old_sym)) {
+ if (st_delete(undef_tbl, (st_data_t*)&key, NULL)) {
+ unlink_undef(key, old_sym->n_value);
+ free(key);
+ }
+ }
+ else {
+ struct indr_data data;
+
+ data.name0 = sym->n_un.n_name;
+ data.name1 = sym[1].n_un.n_name;
+ st_foreach(reloc_tbl, reloc_repl, &data);
+
+ st_insert(undef_tbl, strdup(sym[1].n_un.n_name), NULL);
+ if (st_delete(undef_tbl, (st_data_t*)&key, NULL)) {
+ free(key);
+ }
+ }
+ sym += 2;
+ continue;
+ }
+#endif
+ if (sym->n_type == (N_UNDF | N_EXT)) {
+ if (st_lookup(sym_tbl, sym->n_un.n_name, &old_sym) == 0) {
+ old_sym = NULL;
+ }
+
+ if (value) {
+ if (old_sym) {
+ sym->n_type = N_EXT | N_COMM;
+ sym->n_value = old_sym->n_value;
+ }
+ else {
+ int rnd =
+ value >= sizeof(double) ? sizeof(double) - 1
+ : value >= sizeof(long) ? sizeof(long) - 1
+ : sizeof(short) - 1;
+
+ sym->n_type = N_COMM;
+ new_common += rnd;
+ new_common &= ~(long)rnd;
+ sym->n_value = new_common;
+ new_common += value;
+ }
+ }
+ else {
+ if (old_sym) {
+ sym->n_type = N_EXT | N_COMM;
+ sym->n_value = old_sym->n_value;
+ }
+ else {
+ sym->n_value = (long)dln_undefined;
+ st_insert(undef_tbl, strdup(sym->n_un.n_name), NULL);
+ }
+ }
+ }
+ sym++;
+ }
+
+ block = load_text_data(fd, &hdr, hdr.a_bss + new_common, disp);
+ if (block == 0) goto err_exit;
+
+ sym = syms;
+ while (sym < end) {
+ struct nlist *new_sym;
+ char *key;
+
+ switch (sym->n_type) {
+ case N_COMM:
+ sym->n_value += hdr.a_text + hdr.a_data;
+ case N_TEXT|N_EXT:
+ case N_DATA|N_EXT:
+
+ sym->n_value += block;
+
+ if (st_lookup(sym_tbl, sym->n_un.n_name, &new_sym) != 0
+ && new_sym->n_value != (long)dln_undefined) {
+ dln_errno = DLN_ECONFL;
+ goto err_exit;
+ }
+
+ key = sym->n_un.n_name;
+ if (st_delete(undef_tbl, (st_data_t*)&key, NULL) != 0) {
+ unlink_undef(key, sym->n_value);
+ free(key);
+ }
+
+ new_sym = (struct nlist*)xmalloc(sizeof(struct nlist));
+ *new_sym = *sym;
+ new_sym->n_un.n_name = strdup(sym->n_un.n_name);
+ st_insert(sym_tbl, new_sym->n_un.n_name, new_sym);
+ break;
+
+ case N_TEXT:
+ case N_DATA:
+ sym->n_value += block;
+ break;
+ }
+ sym++;
+ }
+
+ /*
+ * First comes the text-relocation
+ */
+ {
+ struct relocation_info * rel = reloc;
+ struct relocation_info * rel_beg = reloc +
+ (hdr.a_trsize/sizeof(struct relocation_info));
+ struct relocation_info * rel_end = reloc +
+ (hdr.a_trsize+hdr.a_drsize)/sizeof(struct relocation_info);
+
+ while (rel < rel_end) {
+ char *address = (char*)(rel->r_address + block);
+ long datum = 0;
+#if defined(sun) && defined(sparc)
+ unsigned int mask = 0;
+#endif
+
+ if(rel >= rel_beg)
+ address += hdr.a_text;
+
+ if (rel->r_extern) { /* Look it up in symbol-table */
+ sym = &(syms[R_SYMBOL(rel)]);
+ switch (sym->n_type) {
+ case N_EXT|N_UNDF:
+ link_undef(sym->n_un.n_name, block, rel);
+ case N_EXT|N_COMM:
+ case N_COMM:
+ datum = sym->n_value;
+ break;
+ default:
+ goto err_exit;
+ }
+ } /* end.. look it up */
+ else { /* is static */
+ switch (R_SYMBOL(rel)) {
+ case N_TEXT:
+ case N_DATA:
+ datum = block;
+ break;
+ case N_BSS:
+ datum = block + new_common;
+ break;
+ case N_ABS:
+ break;
+ }
+ } /* end .. is static */
+ if (R_PCREL(rel)) datum -= block;
+
+#if defined(sun) && defined(sparc)
+ datum += rel->r_addend;
+ datum >>= R_RIGHTSHIFT(rel);
+ mask = (1 << R_BITSIZE(rel)) - 1;
+ mask |= mask -1;
+ datum &= mask;
+
+ switch (R_LENGTH(rel)) {
+ case 0:
+ *address &= ~mask;
+ *address |= datum;
+ break;
+ case 1:
+ *(short *)address &= ~mask;
+ *(short *)address |= datum;
+ break;
+ case 2:
+ *(long *)address &= ~mask;
+ *(long *)address |= datum;
+ break;
+ }
+#else
+ switch (R_LENGTH(rel)) {
+ case 0: /* byte */
+ if (datum < -128 || datum > 127) goto err_exit;
+ *address += datum;
+ break;
+ case 1: /* word */
+ *(short *)address += datum;
+ break;
+ case 2: /* long */
+ *(long *)address += datum;
+ break;
+ }
+#endif
+ rel++;
+ }
+ }
+
+ if (need_init) {
+ int len;
+ char **libs_to_be_linked = 0;
+ char *buf;
+
+ if (undef_tbl->num_entries > 0) {
+ if (load_lib(libc) == -1) goto err_exit;
+ }
+
+ init_funcname(&buf, need_init);
+ len = strlen(buf);
+
+ for (sym = syms; sym<end; sym++) {
+ char *name = sym->n_un.n_name;
+ if (name[0] == '_' && sym->n_value >= block) {
+ if (strcmp(name+1, "dln_libs_to_be_linked") == 0) {
+ libs_to_be_linked = (char**)sym->n_value;
+ }
+ else if (strcmp(name+1, buf) == 0) {
+ init_p = 1;
+ ((int (*)())sym->n_value)();
+ }
+ }
+ }
+ if (libs_to_be_linked && undef_tbl->num_entries > 0) {
+ while (*libs_to_be_linked) {
+ load_lib(*libs_to_be_linked);
+ libs_to_be_linked++;
+ }
+ }
+ }
+ free(reloc);
+ free(syms);
+ if (need_init) {
+ if (init_p == 0) {
+ dln_errno = DLN_ENOINIT;
+ return -1;
+ }
+ if (undef_tbl->num_entries > 0) {
+ if (load_lib(libc) == -1) goto err_exit;
+ if (undef_tbl->num_entries > 0) {
+ dln_errno = DLN_EUNDEF;
+ return -1;
+ }
+ }
+ }
+ return 0;
+
+ err_exit:
+ if (syms) free(syms);
+ if (reloc) free(reloc);
+ if (block) free((char*)block);
+ return -1;
+}
+
+static int target_offset;
+static int
+search_undef(key, value, lib_tbl)
+ const char *key;
+ int value;
+ st_table *lib_tbl;
+{
+ long offset;
+
+ if (st_lookup(lib_tbl, key, &offset) == 0) return ST_CONTINUE;
+ target_offset = offset;
+ return ST_STOP;
+}
+
+struct symdef {
+ int rb_str_index;
+ int lib_offset;
+};
+
+char *dln_librrb_ary_path = DLN_DEFAULT_LIB_PATH;
+
+static int
+load_lib(lib)
+ const char *lib;
+{
+ char *path, *file;
+ char armagic[SARMAG];
+ int fd, size;
+ struct ar_hdr ahdr;
+ st_table *lib_tbl = NULL;
+ int *data, nsym;
+ struct symdef *base;
+ char *name_base;
+
+ if (dln_init_p == 0) {
+ dln_errno = DLN_ENOINIT;
+ return -1;
+ }
+
+ if (undef_tbl->num_entries == 0) return 0;
+ dln_errno = DLN_EBADLIB;
+
+ if (lib[0] == '-' && lib[1] == 'l') {
+ char *p = alloca(strlen(lib) + 4);
+ sprintf(p, "lib%s.a", lib+2);
+ lib = p;
+ }
+
+ /* library search path: */
+ /* look for environment variable DLN_LIBRARY_PATH first. */
+ /* then variable dln_librrb_ary_path. */
+ /* if path is still NULL, use "." for path. */
+ path = getenv("DLN_LIBRARY_PATH");
+ if (path == NULL) path = dln_librrb_ary_path;
+
+ file = dln_find_file(lib, path);
+ fd = open(file, O_RDONLY);
+ if (fd == -1) goto syserr;
+ size = read(fd, armagic, SARMAG);
+ if (size == -1) goto syserr;
+
+ if (size != SARMAG) {
+ dln_errno = DLN_ENOTLIB;
+ goto badlib;
+ }
+ size = read(fd, &ahdr, sizeof(ahdr));
+ if (size == -1) goto syserr;
+ if (size != sizeof(ahdr) || sscanf(ahdr.ar_size, "%d", &size) != 1) {
+ goto badlib;
+ }
+
+ if (strncmp(ahdr.ar_name, "__.SYMDEF", 9) == 0) {
+ /* make hash table from __.SYMDEF */
+
+ lib_tbl = st_init_strtable();
+ data = (int*)xmalloc(size);
+ if (data == NULL) goto syserr;
+ size = read(fd, data, size);
+ nsym = *data / sizeof(struct symdef);
+ base = (struct symdef*)(data + 1);
+ name_base = (char*)(base + nsym) + sizeof(int);
+ while (nsym > 0) {
+ char *name = name_base + base->rb_str_index;
+
+ st_insert(lib_tbl, name, base->lib_offset + sizeof(ahdr));
+ nsym--;
+ base++;
+ }
+ for (;;) {
+ target_offset = -1;
+ st_foreach(undef_tbl, search_undef, lib_tbl);
+ if (target_offset == -1) break;
+ if (load_1(fd, target_offset, 0) == -1) {
+ st_free_table(lib_tbl);
+ free(data);
+ goto badlib;
+ }
+ if (undef_tbl->num_entries == 0) break;
+ }
+ free(data);
+ st_free_table(lib_tbl);
+ }
+ else {
+ /* linear library, need to scan (FUTURE) */
+
+ for (;;) {
+ int offset = SARMAG;
+ int found = 0;
+ struct exec hdr;
+ struct nlist *syms, *sym, *end;
+
+ while (undef_tbl->num_entries > 0) {
+ found = 0;
+ lseek(fd, offset, 0);
+ size = read(fd, &ahdr, sizeof(ahdr));
+ if (size == -1) goto syserr;
+ if (size == 0) break;
+ if (size != sizeof(ahdr)
+ || sscanf(ahdr.ar_size, "%d", &size) != 1) {
+ goto badlib;
+ }
+ offset += sizeof(ahdr);
+ if (load_header(fd, &hdr, offset) == -1)
+ goto badlib;
+ syms = load_sym(fd, &hdr, offset);
+ if (syms == NULL) goto badlib;
+ sym = syms;
+ end = syms + (hdr.a_syms / sizeof(struct nlist));
+ while (sym < end) {
+ if (sym->n_type == N_EXT|N_TEXT
+ && st_lookup(undef_tbl, sym->n_un.n_name, NULL)) {
+ break;
+ }
+ sym++;
+ }
+ if (sym < end) {
+ found++;
+ free(syms);
+ if (load_1(fd, offset, 0) == -1) {
+ goto badlib;
+ }
+ }
+ offset += size;
+ if (offset & 1) offset++;
+ }
+ if (found) break;
+ }
+ }
+ close(fd);
+ return 0;
+
+ syserr:
+ dln_errno = errno;
+ badlib:
+ if (fd >= 0) close(fd);
+ return -1;
+}
+
+static int
+load(file)
+ const char *file;
+{
+ int fd;
+ int result;
+
+ if (dln_init_p == 0) {
+ if (dln_init(dln_argv0) == -1) return -1;
+ }
+ result = strlen(file);
+ if (file[result-1] == 'a') {
+ return load_lib(file);
+ }
+
+ fd = open(file, O_RDONLY);
+ if (fd == -1) {
+ dln_errno = errno;
+ return -1;
+ }
+ result = load_1(fd, 0, file);
+ close(fd);
+
+ return result;
+}
+
+void*
+dln_sym(name)
+ const char *name;
+{
+ struct nlist *sym;
+
+ if (st_lookup(sym_tbl, name, &sym))
+ return (void*)sym->n_value;
+ return NULL;
+}
+
+#endif /* USE_DLN_A_OUT */
+
+#ifdef USE_DLN_DLOPEN
+# include <dlfcn.h>
+#endif
+
+#ifdef __hpux
+#include <errno.h>
+#include "dl.h"
+#endif
+
+#if defined(_AIX)
+#include <ctype.h> /* for isdigit() */
+#include <errno.h> /* for global errno */
+#include <sys/ldr.h>
+#endif
+
+#ifdef NeXT
+#if NS_TARGET_MAJOR < 4
+#include <mach-o/rld.h>
+#else
+#include <mach-o/dyld.h>
+#ifndef NSLINKMODULE_OPTION_BINDNOW
+#define NSLINKMODULE_OPTION_BINDNOW 1
+#endif
+#endif
+#else
+#ifdef __APPLE__
+#include <mach-o/dyld.h>
+#endif
+#endif
+
+#if defined _WIN32 && !defined __CYGWIN__
+#include <windows.h>
+#endif
+
+#ifdef _WIN32_WCE
+#undef FormatMessage
+#define FormatMessage FormatMessageA
+#undef LoadLibrary
+#define LoadLibrary LoadLibraryA
+#undef GetProcAddress
+#define GetProcAddress GetProcAddressA
+#endif
+
+static const char *
+dln_strerror()
+{
+#ifdef USE_DLN_A_OUT
+ char *strerror();
+
+ switch (dln_errno) {
+ case DLN_ECONFL:
+ return "Symbol name conflict";
+ case DLN_ENOINIT:
+ return "No initializer given";
+ case DLN_EUNDEF:
+ return "Unresolved symbols";
+ case DLN_ENOTLIB:
+ return "Not a library file";
+ case DLN_EBADLIB:
+ return "Malformed library file";
+ case DLN_EINIT:
+ return "Not initialized";
+ default:
+ return strerror(dln_errno);
+ }
+#endif
+
+#ifdef USE_DLN_DLOPEN
+ return (char*)dlerror();
+#endif
+
+#if defined _WIN32 && !defined __CYGWIN__
+ static char message[1024];
+ int error = GetLastError();
+ char *p = message;
+ p += sprintf(message, "%d: ", error);
+ FormatMessage(
+ FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
+ NULL,
+ error,
+ MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
+ p,
+ sizeof message - strlen(message),
+ NULL);
+
+ for (p = message; *p; p++) {
+ if (*p == '\n' || *p == '\r')
+ *p = ' ';
+ }
+ return message;
+#endif
+}
+
+
+#if defined(_AIX) && ! defined(_IA64)
+static void
+aix_loaderror(const char *pathname)
+{
+ char *message[8], errbuf[1024];
+ int i,j;
+
+ struct errtab {
+ int errnum;
+ char *errstr;
+ } load_errtab[] = {
+ {L_ERROR_TOOMANY, "too many errors, rest skipped."},
+ {L_ERROR_NOLIB, "can't load library:"},
+ {L_ERROR_UNDEF, "can't find symbol in library:"},
+ {L_ERROR_RLDBAD,
+ "RLD index out of range or bad relocation type:"},
+ {L_ERROR_FORMAT, "not a valid, executable xcoff file:"},
+ {L_ERROR_MEMBER,
+ "file not an archive or does not contain requested member:"},
+ {L_ERROR_TYPE, "symbol table mismatch:"},
+ {L_ERROR_ALIGN, "text alignment in file is wrong."},
+ {L_ERROR_SYSTEM, "System error:"},
+ {L_ERROR_ERRNO, NULL}
+ };
+
+#define LOAD_ERRTAB_LEN (sizeof(load_errtab)/sizeof(load_errtab[0]))
+#define ERRBUF_APPEND(s) strncat(errbuf, s, sizeof(errbuf)-strlen(errbuf)-1)
+
+ snprintf(errbuf, 1024, "load failed - %s ", pathname);
+
+ if (!loadquery(1, &message[0], sizeof(message)))
+ ERRBUF_APPEND(strerror(errno));
+ for(i = 0; message[i] && *message[i]; i++) {
+ int nerr = atoi(message[i]);
+ for (j=0; j<LOAD_ERRTAB_LEN; j++) {
+ if (nerr == load_errtab[i].errnum && load_errtab[i].errstr)
+ ERRBUF_APPEND(load_errtab[i].errstr);
+ }
+ while (isdigit(*message[i])) message[i]++;
+ ERRBUF_APPEND(message[i]);
+ ERRBUF_APPEND("\n");
+ }
+ errbuf[strlen(errbuf)-1] = '\0'; /* trim off last newline */
+ rb_loaderror(errbuf);
+ return;
+}
+#endif
+
+#endif /* NO_DLN_LOAD */
+
+void*
+dln_load(file)
+ const char *file;
+{
+#ifdef NO_DLN_LOAD
+ rb_raise(rb_eLoadError, "this executable file can't load extension libraries");
+#else
+
+#if !defined(_AIX) && !defined(NeXT)
+ const char *error = 0;
+#define DLN_ERROR() (error = dln_strerror(), strcpy(ALLOCA_N(char, strlen(error) + 1), error))
+#endif
+
+#if defined _WIN32 && !defined __CYGWIN__
+ HINSTANCE handle;
+ char winfile[MAXPATHLEN];
+ void (*init_fct)();
+ char *buf;
+
+ if (strlen(file) >= MAXPATHLEN) rb_loaderror("filename too long");
+
+ /* Load the file as an object one */
+ init_funcname(&buf, file);
+
+ strcpy(winfile, file);
+
+ /* Load file */
+ if ((handle = LoadLibrary(winfile)) == NULL) {
+ error = dln_strerror();
+ goto failed;
+ }
+
+ if ((init_fct = (void(*)())GetProcAddress(handle, buf)) == NULL) {
+ rb_loaderror("%s - %s\n%s", dln_strerror(), buf, file);
+ }
+
+ /* Call the init code */
+ (*init_fct)();
+ return handle;
+#else
+#ifdef USE_DLN_A_OUT
+ if (load(file) == -1) {
+ error = dln_strerror();
+ goto failed;
+ }
+ return 0;
+#else
+
+ char *buf;
+ /* Load the file as an object one */
+ init_funcname(&buf, file);
+
+#ifdef USE_DLN_DLOPEN
+#define DLN_DEFINED
+ {
+ void *handle;
+ void (*init_fct)();
+
+#ifndef RTLD_LAZY
+# define RTLD_LAZY 1
+#endif
+#ifdef __INTERIX
+# undef RTLD_GLOBAL
+#endif
+#ifndef RTLD_GLOBAL
+# define RTLD_GLOBAL 0
+#endif
+
+ /* Load file */
+ if ((handle = (void*)dlopen(file, RTLD_LAZY|RTLD_GLOBAL)) == NULL) {
+ error = dln_strerror();
+ goto failed;
+ }
+
+ init_fct = (void(*)())dlsym(handle, buf);
+ if (init_fct == NULL) {
+ error = DLN_ERROR();
+ dlclose(handle);
+ goto failed;
+ }
+ /* Call the init code */
+ (*init_fct)();
+
+ return handle;
+ }
+#endif /* USE_DLN_DLOPEN */
+
+#ifdef __hpux
+#define DLN_DEFINED
+ {
+ shl_t lib = NULL;
+ int flags;
+ void (*init_fct)();
+
+ flags = BIND_DEFERRED;
+ lib = shl_load(file, flags, 0);
+ if (lib == NULL) {
+ extern int errno;
+ rb_loaderror("%s - %s", strerror(errno), file);
+ }
+ shl_findsym(&lib, buf, TYPE_PROCEDURE, (void*)&init_fct);
+ if (init_fct == NULL) {
+ shl_findsym(&lib, buf, TYPE_UNDEFINED, (void*)&init_fct);
+ if (init_fct == NULL) {
+ errno = ENOSYM;
+ rb_loaderror("%s - %s", strerror(ENOSYM), file);
+ }
+ }
+ (*init_fct)();
+ return (void*)lib;
+ }
+#endif /* hpux */
+
+#if defined(_AIX) && ! defined(_IA64)
+#define DLN_DEFINED
+ {
+ void (*init_fct)();
+
+ init_fct = (void(*)())load((char*)file, 1, 0);
+ if (init_fct == NULL) {
+ aix_loaderror(file);
+ }
+ if (loadbind(0, (void*)dln_load, (void*)init_fct) == -1) {
+ aix_loaderror(file);
+ }
+ (*init_fct)();
+ return (void*)init_fct;
+ }
+#endif /* _AIX */
+
+#if defined(NeXT) || defined(__APPLE__)
+#define DLN_DEFINED
+/*----------------------------------------------------
+ By SHIROYAMA Takayuki Psi@fortune.nest.or.jp
+
+ Special Thanks...
+ Yu tomoak-i@is.aist-nara.ac.jp,
+ Mi hisho@tasihara.nest.or.jp,
+ sunshine@sunshineco.com,
+ and... Miss ARAI Akino(^^;)
+ ----------------------------------------------------*/
+#if defined(NeXT) && (NS_TARGET_MAJOR < 4)/* NeXTSTEP rld functions */
+
+ {
+ NXStream* s;
+ unsigned long init_address;
+ char *object_files[2] = {NULL, NULL};
+
+ void (*init_fct)();
+
+ object_files[0] = (char*)file;
+
+ s = NXOpenFile(2,NX_WRITEONLY);
+
+ /* Load object file, if return value ==0 , load failed*/
+ if(rld_load(s, NULL, object_files, NULL) == 0) {
+ NXFlush(s);
+ NXClose(s);
+ rb_loaderror("Failed to load %.200s", file);
+ }
+
+ /* lookup the initial function */
+ if(rld_lookup(s, buf, &init_address) == 0) {
+ NXFlush(s);
+ NXClose(s);
+ rb_loaderror("Failed to lookup Init function %.200s", file);
+ }
+
+ NXFlush(s);
+ NXClose(s);
+
+ /* Cannot call *init_address directory, so copy this value to
+ funtion pointer */
+ init_fct = (void(*)())init_address;
+ (*init_fct)();
+ return (void*)init_address;
+ }
+#else/* OPENSTEP dyld functions */
+ {
+ int dyld_result;
+ NSObjectFileImage obj_file; /* handle, but not use it */
+ /* "file" is module file name .
+ "buf" is pointer to initial function name with "_" . */
+
+ void (*init_fct)();
+
+
+ dyld_result = NSCreateObjectFileImageFromFile(file, &obj_file);
+
+ if (dyld_result != NSObjectFileImageSuccess) {
+ rb_loaderror("Failed to load %.200s", file);
+ }
+
+ NSLinkModule(obj_file, file, NSLINKMODULE_OPTION_BINDNOW);
+
+ /* lookup the initial function */
+ if(!NSIsSymbolNameDefined(buf)) {
+ rb_loaderror("Failed to lookup Init function %.200s",file);
+ }
+ init_fct = NSAddressOfSymbol(NSLookupAndBindSymbol(buf));
+ (*init_fct)();
+
+ return (void*)init_fct;
+ }
+#endif /* rld or dyld */
+#endif
+
+#ifdef __BEOS__
+# define DLN_DEFINED
+ {
+ status_t err_stat; /* BeOS error status code */
+ image_id img_id; /* extention module unique id */
+ void (*init_fct)(); /* initialize function for extention module */
+
+ /* load extention module */
+ img_id = load_add_on(file);
+ if (img_id <= 0) {
+ rb_loaderror("Failed to load %.200s", file);
+ }
+
+ /* find symbol for module initialize function. */
+ /* The Be Book KernelKit Images section described to use
+ B_SYMBOL_TYPE_TEXT for symbol of function, not
+ B_SYMBOL_TYPE_CODE. Why ? */
+ /* strcat(init_fct_symname, "__Fv"); */ /* parameter nothing. */
+ /* "__Fv" dont need! The Be Book Bug ? */
+ err_stat = get_image_symbol(img_id, buf,
+ B_SYMBOL_TYPE_TEXT, (void **)&init_fct);
+
+ if (err_stat != B_NO_ERROR) {
+ char real_name[MAXPATHLEN];
+
+ strcpy(real_name, buf);
+ strcat(real_name, "__Fv");
+ err_stat = get_image_symbol(img_id, real_name,
+ B_SYMBOL_TYPE_TEXT, (void **)&init_fct);
+ }
+
+ if ((B_BAD_IMAGE_ID == err_stat) || (B_BAD_INDEX == err_stat)) {
+ unload_add_on(img_id);
+ rb_loaderror("Failed to lookup Init function %.200s", file);
+ }
+ else if (B_NO_ERROR != err_stat) {
+ char errmsg[] = "Internal of BeOS version. %.200s (symbol_name = %s)";
+ unload_add_on(img_id);
+ rb_loaderror(errmsg, strerror(err_stat), buf);
+ }
+
+ /* call module initialize function. */
+ (*init_fct)();
+ return (void*)img_id;
+ }
+#endif /* __BEOS__*/
+
+#ifdef __MACOS__
+# define DLN_DEFINED
+ {
+ OSErr err;
+ FSSpec libspec;
+ CFragConnectionID connID;
+ Ptr mainAddr;
+ char errMessage[1024];
+ Boolean isfolder, didsomething;
+ Str63 fragname;
+ Ptr symAddr;
+ CFragSymbolClass class;
+ void (*init_fct)();
+ char fullpath[MAXPATHLEN];
+
+ strcpy(fullpath, file);
+
+ /* resolve any aliases to find the real file */
+ c2pstr(fullpath);
+ (void)FSMakeFSSpec(0, 0, fullpath, &libspec);
+ err = ResolveAliasFile(&libspec, 1, &isfolder, &didsomething);
+ if (err) {
+ rb_loaderror("Unresolved Alias - %s", file);
+ }
+
+ /* Load the fragment (or return the connID if it is already loaded */
+ fragname[0] = 0;
+ err = GetDiskFragment(&libspec, 0, 0, fragname,
+ kLoadCFrag, &connID, &mainAddr,
+ errMessage);
+ if (err) {
+ p2cstr(errMessage);
+ rb_loaderror("%s - %s",errMessage , file);
+ }
+
+ /* Locate the address of the correct init function */
+ c2pstr(buf);
+ err = FindSymbol(connID, buf, &symAddr, &class);
+ if (err) {
+ rb_loaderror("Unresolved symbols - %s" , file);
+ }
+ init_fct = (void (*)())symAddr;
+ (*init_fct)();
+ return (void*)init_fct;
+ }
+#endif /* __MACOS__ */
+
+#if defined(__VMS)
+#define DLN_DEFINED
+ {
+ void *handle, (*init_fct)();
+ char *fname, *p1, *p2;
+
+ fname = (char *)__alloca(strlen(file)+1);
+ strcpy(fname,file);
+ if (p1 = strrchr(fname,'/'))
+ fname = p1 + 1;
+ if (p2 = strrchr(fname,'.'))
+ *p2 = '\0';
+
+ if ((handle = (void*)dlopen(fname, 0)) == NULL) {
+ error = dln_strerror();
+ goto failed;
+ }
+
+ if ((init_fct = (void (*)())dlsym(handle, buf)) == NULL) {
+ error = DLN_ERROR();
+ dlclose(handle);
+ goto failed;
+ }
+ /* Call the init code */
+ (*init_fct)();
+ return handle;
+ }
+#endif /* __VMS */
+
+#ifndef DLN_DEFINED
+ rb_notimplement();
+#endif
+
+#endif /* USE_DLN_A_OUT */
+#endif
+#if !defined(_AIX) && !defined(NeXT)
+ failed:
+ rb_loaderror("%s - %s", error, file);
+#endif
+
+#endif /* NO_DLN_LOAD */
+ return 0; /* dummy return */
+}
+
+static char *dln_find_1();
+
+char *
+dln_find_exe(fname, path)
+ const char *fname;
+ const char *path;
+{
+ if (!path) {
+ path = getenv(PATH_ENV);
+ }
+
+ if (!path) {
+#if defined(MSDOS) || defined(_WIN32) || defined(__human68k__) || defined(__MACOS__)
+ path = "/usr/local/bin;/usr/ucb;/usr/bin;/bin;.";
+#else
+ path = "/usr/local/bin:/usr/ucb:/usr/bin:/bin:.";
+#endif
+ }
+ return dln_find_1(fname, path, 1);
+}
+
+char *
+dln_find_file(fname, path)
+ const char *fname;
+ const char *path;
+{
+#ifndef __MACOS__
+ if (!path) path = ".";
+ return dln_find_1(fname, path, 0);
+#else
+ if (!path) path = ".";
+ return _macruby_path_conv_posix_to_macos(dln_find_1(fname, path, 0));
+#endif
+}
+
+#if defined(__CYGWIN32__)
+const char *
+conv_to_posix_path(win32, posix, len)
+ char *win32;
+ char *posix;
+ int len;
+{
+ char *first = win32;
+ char *p = win32;
+ char *dst = posix;
+
+ for (p = win32; *p; p++)
+ if (*p == ';') {
+ *p = 0;
+ cygwin32_conv_to_posix_path(first, posix);
+ posix += strlen(posix);
+ *posix++ = ':';
+ first = p + 1;
+ *p = ';';
+ }
+ if (len < strlen(first))
+ fprintf(stderr, "PATH length too long: %s\n", first);
+ else
+ cygwin32_conv_to_posix_path(first, posix);
+ return dst;
+}
+#endif
+
+static char fbuf[MAXPATHLEN];
+
+static char *
+dln_find_1(fname, path, exe_flag)
+ char *fname;
+ char *path;
+ int exe_flag; /* non 0 if looking for executable. */
+{
+ register char *dp;
+ register char *ep;
+ register char *bp;
+ struct stat st;
+#ifdef __MACOS__
+ const char* mac_fullpath;
+#endif
+
+ if (!fname) return fname;
+ if (fname[0] == '/') return fname;
+ if (strncmp("./", fname, 2) == 0 || strncmp("../", fname, 3) == 0)
+ return fname;
+ if (exe_flag && strchr(fname, '/')) return fname;
+#ifdef DOSISH
+ if (fname[0] == '\\') return fname;
+# ifdef DOSISH_DRIVE_LETTER
+ if (strlen(fname) > 2 && fname[1] == ':') return fname;
+# endif
+ if (strncmp(".\\", fname, 2) == 0 || strncmp("..\\", fname, 3) == 0)
+ return fname;
+ if (exe_flag && strchr(fname, '\\')) return fname;
+#endif
+
+ for (dp = path;; dp = ++ep) {
+ register int l;
+ int i;
+ int fspace;
+
+ /* extract a component */
+ ep = strchr(dp, PATH_SEP[0]);
+ if (ep == NULL)
+ ep = dp+strlen(dp);
+
+ /* find the length of that component */
+ l = ep - dp;
+ bp = fbuf;
+ fspace = sizeof fbuf - 2;
+ if (l > 0) {
+ /*
+ ** If the length of the component is zero length,
+ ** start from the current directory. If the
+ ** component begins with "~", start from the
+ ** user's $HOME environment variable. Otherwise
+ ** take the path literally.
+ */
+
+ if (*dp == '~' && (l == 1 ||
+#if defined(DOSISH)
+ dp[1] == '\\' ||
+#endif
+ dp[1] == '/')) {
+ char *home;
+
+ home = getenv("HOME");
+ if (home != NULL) {
+ i = strlen(home);
+ if ((fspace -= i) < 0)
+ goto toolong;
+ memcpy(bp, home, i);
+ bp += i;
+ }
+ dp++;
+ l--;
+ }
+ if (l > 0) {
+ if ((fspace -= l) < 0)
+ goto toolong;
+ memcpy(bp, dp, l);
+ bp += l;
+ }
+
+ /* add a "/" between directory and filename */
+ if (ep[-1] != '/')
+ *bp++ = '/';
+ }
+
+ /* now append the file name */
+ i = strlen(fname);
+ if ((fspace -= i) < 0) {
+ toolong:
+ fprintf(stderr, "openpath: pathname too long (ignored)\n");
+ *bp = '\0';
+ fprintf(stderr, "\tDirectory \"%s\"\n", fbuf);
+ fprintf(stderr, "\tFile \"%s\"\n", fname);
+ goto next;
+ }
+ memcpy(bp, fname, i + 1);
+
+#ifndef __MACOS__
+ if (stat(fbuf, &st) == 0) {
+ if (exe_flag == 0) return fbuf;
+ /* looking for executable */
+ if (!S_ISDIR(st.st_mode) && eaccess(fbuf, X_OK) == 0)
+ return fbuf;
+ }
+#else
+ if (mac_fullpath = _macruby_exist_file_in_libdir_as_posix_name(fbuf)) {
+ if (exe_flag == 0) return mac_fullpath;
+ /* looking for executable */
+ if (stat(mac_fullpath, &st) == 0) {
+ if (!S_ISDIR(st.st_mode) && eaccess(mac_fullpath, X_OK) == 0)
+ return mac_fullpath;
+ }
+ }
+#endif
+#if defined(DOSISH)
+ if (exe_flag) {
+ static const char *extension[] = {
+#if defined(MSDOS)
+ ".com", ".exe", ".bat",
+#if defined(DJGPP)
+ ".btm", ".sh", ".ksh", ".pl", ".sed",
+#endif
+#elif defined(__EMX__) || defined(_WIN32)
+ ".exe", ".com", ".cmd", ".bat",
+/* end of __EMX__ or _WIN32 */
+#else
+ ".r", ".R", ".x", ".X", ".bat", ".BAT",
+/* __human68k__ */
+#endif
+ (char *) NULL
+ };
+ int j;
+
+ for (j = 0; extension[j]; j++) {
+ if (fspace < strlen(extension[j])) {
+ fprintf(stderr, "openpath: pathname too long (ignored)\n");
+ fprintf(stderr, "\tDirectory \"%.*s\"\n", (int) (bp - fbuf), fbuf);
+ fprintf(stderr, "\tFile \"%s%s\"\n", fname, extension[j]);
+ continue;
+ }
+ strcpy(bp + i, extension[j]);
+#ifndef __MACOS__
+ if (stat(fbuf, &st) == 0)
+ return fbuf;
+#else
+ if (mac_fullpath = _macruby_exist_file_in_libdir_as_posix_name(fbuf))
+ return mac_fullpath;
+
+#endif
+ }
+ }
+#endif /* MSDOS or _WIN32 or __human68k__ or __EMX__ */
+
+ next:
+ /* if not, and no other alternatives, life is bleak */
+ if (*ep == '\0') {
+ return NULL;
+ }
+
+ /* otherwise try the next component in the search path */
+ }
+}
+#define NO_DLN_LOAD 1
+#include "dln.c"
+void
+Init_ext()
+{
+}
+/**********************************************************************
+
+ enum.c -
+
+ $Author: murphy $
+ $Date: 2005-11-05 04:33:55 +0100 (Sa, 05 Nov 2005) $
+ created at: Fri Oct 1 15:15:19 JST 1993
+
+ Copyright (C) 1993-2003 Yukihiro Matsumoto
+
+**********************************************************************/
+
+#include "ruby.h"
+#include "node.h"
+#include "util.h"
+
+VALUE rb_mEnumerable;
+static ID id_each, id_eqq, id_cmp;
+
+VALUE
+rb_each(obj)
+ VALUE obj;
+{
+ return rb_funcall(obj, id_each, 0, 0);
+}
+
+static VALUE
+grep_i(i, arg)
+ VALUE i, *arg;
+{
+ if (RTEST(rb_funcall(arg[0], id_eqq, 1, i))) {
+ rb_ary_push(arg[1], i);
+ }
+ return Qnil;
+}
+
+static VALUE
+grep_iter_i(i, arg)
+ VALUE i, *arg;
+{
+ if (RTEST(rb_funcall(arg[0], id_eqq, 1, i))) {
+ rb_ary_push(arg[1], rb_yield(i));
+ }
+ return Qnil;
+}
+
+/*
+ * call-seq:
+ * enum.grep(pattern) => array
+ * enum.grep(pattern) {| obj | block } => array
+ *
+ * Returns an array of every element in <i>enum</i> for which
+ * <code>Pattern === element</code>. If the optional <em>block</em> is
+ * supplied, each matching element is passed to it, and the block's
+ * result is stored in the output array.
+ *
+ * (1..100).grep 38..44 #=> [38, 39, 40, 41, 42, 43, 44]
+ * c = IO.constants
+ * c.grep(/SEEK/) #=> ["SEEK_END", "SEEK_SET", "SEEK_CUR"]
+ * res = c.grep(/SEEK/) {|v| IO.const_get(v) }
+ * res #=> [2, 0, 1]
+ *
+ */
+
+static VALUE
+enum_grep(obj, pat)
+ VALUE obj, pat;
+{
+ VALUE ary = rb_ary_new();
+ VALUE arg[2];
+
+ arg[0] = pat;
+ arg[1] = ary;
+
+ rb_iterate(rb_each, obj, rb_block_given_p() ? grep_iter_i : grep_i, (VALUE)arg);
+
+ return ary;
+}
+
+static VALUE
+find_i(i, memo)
+ VALUE i;
+ NODE *memo;
+{
+ if (RTEST(rb_yield(i))) {
+ memo->u2.value = Qtrue;
+ memo->u1.value = i;
+ rb_iter_break();
+ }
+ return Qnil;
+}
+
+/*
+ * call-seq:
+ * enum.detect(ifnone = nil) {| obj | block } => obj or nil
+ * enum.find(ifnone = nil) {| obj | block } => obj or nil
+ *
+ * Passes each entry in <i>enum</i> to <em>block</em>. Returns the
+ * first for which <em>block</em> is not <code>false</code>. If no
+ * object matches, calls <i>ifnone</i> and returns its result when it
+ * is specified, or returns <code>nil</code>
+ *
+ * (1..10).detect {|i| i % 5 == 0 and i % 7 == 0 } #=> nil
+ * (1..100).detect {|i| i % 5 == 0 and i % 7 == 0 } #=> 35
+ *
+ */
+
+static VALUE
+enum_find(argc, argv, obj)
+ int argc;
+ VALUE* argv;
+ VALUE obj;
+{
+ NODE *memo = rb_node_newnode(NODE_MEMO, Qnil, Qfalse, 0);
+ VALUE if_none;
+
+ rb_scan_args(argc, argv, "01", &if_none);
+ rb_iterate(rb_each, obj, find_i, (VALUE)memo);
+ if (memo->u2.value) {
+ return memo->u1.value;
+ }
+ if (!NIL_P(if_none)) {
+ return rb_funcall(if_none, rb_intern("call"), 0, 0);
+ }
+ return Qnil;
+}
+
+static VALUE
+find_all_i(i, ary)
+ VALUE i, ary;
+{
+ if (RTEST(rb_yield(i))) {
+ rb_ary_push(ary, i);
+ }
+ return Qnil;
+}
+
+/*
+ * call-seq:
+ * enum.find_all {| obj | block } => array
+ * enum.select {| obj | block } => array
+ *
+ * Returns an array containing all elements of <i>enum</i> for which
+ * <em>block</em> is not <code>false</code> (see also
+ * <code>Enumerable#reject</code>).
+ *
+ * (1..10).find_all {|i| i % 3 == 0 } #=> [3, 6, 9]
+ *
+ */
+
+static VALUE
+enum_find_all(obj)
+ VALUE obj;
+{
+ VALUE ary = rb_ary_new();
+
+ rb_iterate(rb_each, obj, find_all_i, ary);
+
+ return ary;
+}
+
+static VALUE
+reject_i(i, ary)
+ VALUE i, ary;
+{
+ if (!RTEST(rb_yield(i))) {
+ rb_ary_push(ary, i);
+ }
+ return Qnil;
+}
+
+/*
+ * call-seq:
+ * enum.reject {| obj | block } => array
+ *
+ * Returns an array for all elements of <i>enum</i> for which
+ * <em>block</em> is false (see also <code>Enumerable#find_all</code>).
+ *
+ * (1..10).reject {|i| i % 3 == 0 } #=> [1, 2, 4, 5, 7, 8, 10]
+ *
+ */
+
+static VALUE
+enum_reject(obj)
+ VALUE obj;
+{
+ VALUE ary = rb_ary_new();
+
+ rb_iterate(rb_each, obj, reject_i, ary);
+
+ return ary;
+}
+
+static VALUE
+collect_i(i, ary)
+ VALUE i, ary;
+{
+ rb_ary_push(ary, rb_yield(i));
+
+ return Qnil;
+}
+
+static VALUE
+collect_all(i, ary)
+ VALUE i, ary;
+{
+ rb_ary_push(ary, i);
+
+ return Qnil;
+}
+
+/*
+ * call-seq:
+ * enum.collect {| obj | block } => array
+ * enum.map {| obj | block } => array
+ *
+ * Returns a new array with the results of running <em>block</em> once
+ * for every element in <i>enum</i>.
+ *
+ * (1..4).collect {|i| i*i } #=> [1, 4, 9, 16]
+ * (1..4).collect { "cat" } #=> ["cat", "cat", "cat", "cat"]
+ *
+ */
+
+static VALUE
+enum_collect(obj)
+ VALUE obj;
+{
+ VALUE ary = rb_ary_new();
+
+ rb_iterate(rb_each, obj, rb_block_given_p() ? collect_i : collect_all, ary);
+
+ return ary;
+}
+
+/*
+ * call-seq:
+ * enum.to_a => array
+ * enum.entries => array
+ *
+ * Returns an array containing the items in <i>enum</i>.
+ *
+ * (1..7).to_a #=> [1, 2, 3, 4, 5, 6, 7]
+ * { 'a'=>1, 'b'=>2, 'c'=>3 }.to_a #=> [["a", 1], ["b", 2], ["c", 3]]
+ */
+static VALUE
+enum_to_a(obj)
+ VALUE obj;
+{
+ VALUE ary = rb_ary_new();
+
+ rb_iterate(rb_each, obj, collect_all, ary);
+
+ return ary;
+}
+
+static VALUE
+inject_i(i, memo)
+ VALUE i;
+ NODE *memo;
+{
+ if (memo->u2.value) {
+ memo->u2.value = Qfalse;
+ memo->u1.value = i;
+ }
+ else {
+ memo->u1.value = rb_yield_values(2, memo->u1.value, i);
+ }
+ return Qnil;
+}
+
+/*
+ * call-seq:
+ * enum.inject(initial) {| memo, obj | block } => obj
+ * enum.inject {| memo, obj | block } => obj
+ *
+ * Combines the elements of <i>enum</i> by applying the block to an
+ * accumulator value (<i>memo</i>) and each element in turn. At each
+ * step, <i>memo</i> is set to the value returned by the block. The
+ * first form lets you supply an initial value for <i>memo</i>. The
+ * second form uses the first element of the collection as a the
+ * initial value (and skips that element while iterating).
+ *
+ * # Sum some numbers
+ * (5..10).inject {|sum, n| sum + n } #=> 45
+ * # Multiply some numbers
+ * (5..10).inject(1) {|product, n| product * n } #=> 151200
+ *
+ * # find the longest word
+ * longest = %w{ cat sheep bear }.inject do |memo,word|
+ * memo.length > word.length ? memo : word
+ * end
+ * longest #=> "sheep"
+ *
+ * # find the length of the longest word
+ * longest = %w{ cat sheep bear }.inject(0) do |memo,word|
+ * memo >= word.length ? memo : word.length
+ * end
+ * longest #=> 5
+ *
+ */
+
+static VALUE
+enum_inject(argc, argv, obj)
+ int argc;
+ VALUE *argv, obj;
+{
+ NODE *memo;
+ VALUE n;
+
+ if (rb_scan_args(argc, argv, "01", &n) == 1) {
+ memo = rb_node_newnode(NODE_MEMO, n, Qfalse, 0);
+ }
+ else {
+ memo = rb_node_newnode(NODE_MEMO, Qnil, Qtrue, 0);
+ }
+ rb_iterate(rb_each, obj, inject_i, (VALUE)memo);
+ n = memo->u1.value;
+ return n;
+}
+
+static VALUE
+partition_i(i, ary)
+ VALUE i, *ary;
+{
+ if (RTEST(rb_yield(i))) {
+ rb_ary_push(ary[0], i);
+ }
+ else {
+ rb_ary_push(ary[1], i);
+ }
+ return Qnil;
+}
+
+/*
+ * call-seq:
+ * enum.partition {| obj | block } => [ true_array, false_array ]
+ *
+ * Returns two arrays, the first containing the elements of
+ * <i>enum</i> for which the block evaluates to true, the second
+ * containing the rest.
+ *
+ * (1..6).partition {|i| (i&1).zero?} #=> [[2, 4, 6], [1, 3, 5]]
+ *
+ */
+
+static VALUE
+enum_partition(obj)
+ VALUE obj;
+{
+ VALUE ary[2];
+
+ ary[0] = rb_ary_new();
+ ary[1] = rb_ary_new();
+ rb_iterate(rb_each, obj, partition_i, (VALUE)ary);
+
+ return rb_assoc_new(ary[0], ary[1]);
+}
+
+/*
+ * call-seq:
+ * enum.sort => array
+ * enum.sort {| a, b | block } => array
+ *
+ * Returns an array containing the items in <i>enum</i> sorted,
+ * either according to their own <code><=></code> method, or by using
+ * the results of the supplied block. The block should return -1, 0, or
+ * +1 depending on the comparison between <i>a</i> and <i>b</i>. As of
+ * Ruby 1.8, the method <code>Enumerable#sort_by</code> implements a
+ * built-in Schwartzian Transform, useful when key computation or
+ * comparison is expensive..
+ *
+ * %w(rhea kea flea).sort #=> ["flea", "kea", "rhea"]
+ * (1..10).sort {|a,b| b <=> a} #=> [10, 9, 8, 7, 6, 5, 4, 3, 2, 1]
+ */
+
+static VALUE
+enum_sort(obj)
+ VALUE obj;
+{
+ return rb_ary_sort(enum_to_a(obj));
+}
+
+static VALUE
+sort_by_i(i, ary)
+ VALUE i, ary;
+{
+ VALUE v;
+ NODE *memo;
+
+ v = rb_yield(i);
+ if (RBASIC(ary)->klass) {
+ rb_raise(rb_eRuntimeError, "sort_by reentered");
+ }
+ memo = rb_node_newnode(NODE_MEMO, v, i, 0);
+ rb_ary_push(ary, (VALUE)memo);
+ return Qnil;
+}
+
+static int
+sort_by_cmp(aa, bb)
+ NODE **aa, **bb;
+{
+ VALUE a = aa[0]->u1.value;
+ VALUE b = bb[0]->u1.value;
+
+ return rb_cmpint(rb_funcall(a, id_cmp, 1, b), a, b);
+}
+
+/*
+ * call-seq:
+ * enum.sort_by {| obj | block } => array
+ *
+ * Sorts <i>enum</i> using a set of keys generated by mapping the
+ * values in <i>enum</i> through the given block.
+ *
+ * %w{ apple pear fig }.sort_by {|word| word.length}
+ #=> ["fig", "pear", "apple"]
+ *
+ * The current implementation of <code>sort_by</code> generates an
+ * array of tuples containing the original collection element and the
+ * mapped value. This makes <code>sort_by</code> fairly expensive when
+ * the keysets are simple
+ *
+ * require 'benchmark'
+ * include Benchmark
+ *
+ * a = (1..100000).map {rand(100000)}
+ *
+ * bm(10) do |b|
+ * b.report("Sort") { a.sort }
+ * b.report("Sort by") { a.sort_by {|a| a} }
+ * end
+ *
+ * <em>produces:</em>
+ *
+ * user system total real
+ * Sort 0.180000 0.000000 0.180000 ( 0.175469)
+ * Sort by 1.980000 0.040000 2.020000 ( 2.013586)
+ *
+ * However, consider the case where comparing the keys is a non-trivial
+ * operation. The following code sorts some files on modification time
+ * using the basic <code>sort</code> method.
+ *
+ * files = Dir["*"]
+ * sorted = files.sort {|a,b| File.new(a).mtime <=> File.new(b).mtime}
+ * sorted #=> ["mon", "tues", "wed", "thurs"]
+ *
+ * This sort is inefficient: it generates two new <code>File</code>
+ * objects during every comparison. A slightly better technique is to
+ * use the <code>Kernel#test</code> method to generate the modification
+ * times directly.
+ *
+ * files = Dir["*"]
+ * sorted = files.sort { |a,b|
+ * test(?M, a) <=> test(?M, b)
+ * }
+ * sorted #=> ["mon", "tues", "wed", "thurs"]
+ *
+ * This still generates many unnecessary <code>Time</code> objects. A
+ * more efficient technique is to cache the sort keys (modification
+ * times in this case) before the sort. Perl users often call this
+ * approach a Schwartzian Transform, after Randal Schwartz. We
+ * construct a temporary array, where each element is an array
+ * containing our sort key along with the filename. We sort this array,
+ * and then extract the filename from the result.
+ *
+ * sorted = Dir["*"].collect { |f|
+ * [test(?M, f), f]
+ * }.sort.collect { |f| f[1] }
+ * sorted #=> ["mon", "tues", "wed", "thurs"]
+ *
+ * This is exactly what <code>sort_by</code> does internally.
+ *
+ * sorted = Dir["*"].sort_by {|f| test(?M, f)}
+ * sorted #=> ["mon", "tues", "wed", "thurs"]
+ */
+
+static VALUE
+enum_sort_by(obj)
+ VALUE obj;
+{
+ VALUE ary;
+ long i;
+
+ if (TYPE(obj) == T_ARRAY) {
+ ary = rb_ary_new2(RARRAY(obj)->len);
+ }
+ else {
+ ary = rb_ary_new();
+ }
+ RBASIC(ary)->klass = 0;
+ rb_iterate(rb_each, obj, sort_by_i, ary);
+ if (RARRAY(ary)->len > 1) {
+ qsort(RARRAY(ary)->ptr, RARRAY(ary)->len, sizeof(VALUE), sort_by_cmp, 0);
+ }
+ if (RBASIC(ary)->klass) {
+ rb_raise(rb_eRuntimeError, "sort_by reentered");
+ }
+ for (i=0; i<RARRAY(ary)->len; i++) {
+ RARRAY(ary)->ptr[i] = RNODE(RARRAY(ary)->ptr[i])->u2.value;
+ }
+ RBASIC(ary)->klass = rb_cArray;
+ return ary;
+}
+
+static VALUE
+all_iter_i(i, memo)
+ VALUE i;
+ NODE *memo;
+{
+ if (!RTEST(rb_yield(i))) {
+ memo->u1.value = Qfalse;
+ rb_iter_break();
+ }
+ return Qnil;
+}
+
+static VALUE
+all_i(i, memo)
+ VALUE i;
+ NODE *memo;
+{
+ if (!RTEST(i)) {
+ memo->u1.value = Qfalse;
+ rb_iter_break();
+ }
+ return Qnil;
+}
+
+/*
+ * call-seq:
+ * enum.all? [{|obj| block } ] => true or false
+ *
+ * Passes each element of the collection to the given block. The method
+ * returns <code>true</code> if the block never returns
+ * <code>false</code> or <code>nil</code>. If the block is not given,
+ * Ruby adds an implicit block of <code>{|obj| obj}</code> (that is
+ * <code>all?</code> will return <code>true</code> only if none of the
+ * collection members are <code>false</code> or <code>nil</code>.)
+ *
+ * %w{ ant bear cat}.all? {|word| word.length >= 3} #=> true
+ * %w{ ant bear cat}.all? {|word| word.length >= 4} #=> false
+ * [ nil, true, 99 ].all? #=> false
+ *
+ */
+
+static VALUE
+enum_all(obj)
+ VALUE obj;
+{
+ VALUE result;
+ NODE *memo = rb_node_newnode(NODE_MEMO, Qnil, 0, 0);
+
+ memo->u1.value = Qtrue;
+ rb_iterate(rb_each, obj, rb_block_given_p() ? all_iter_i : all_i, (VALUE)memo);
+ result = memo->u1.value;
+ return result;
+}
+
+static VALUE
+any_iter_i(i, memo)
+ VALUE i;
+ NODE *memo;
+{
+ if (RTEST(rb_yield(i))) {
+ memo->u1.value = Qtrue;
+ rb_iter_break();
+ }
+ return Qnil;
+}
+
+static VALUE
+any_i(i, memo)
+ VALUE i;
+ NODE *memo;
+{
+ if (RTEST(i)) {
+ memo->u1.value = Qtrue;
+ rb_iter_break();
+ }
+ return Qnil;
+}
+
+/*
+ * call-seq:
+ * enum.any? [{|obj| block } ] => true or false
+ *
+ * Passes each element of the collection to the given block. The method
+ * returns <code>true</code> if the block ever returns a value other
+ * that <code>false</code> or <code>nil</code>. If the block is not
+ * given, Ruby adds an implicit block of <code>{|obj| obj}</code> (that
+ * is <code>any?</code> will return <code>true</code> if at least one
+ * of the collection members is not <code>false</code> or
+ * <code>nil</code>.
+ *
+ * %w{ ant bear cat}.any? {|word| word.length >= 3} #=> true
+ * %w{ ant bear cat}.any? {|word| word.length >= 4} #=> true
+ * [ nil, true, 99 ].any? #=> true
+ *
+ */
+
+static VALUE
+enum_any(obj)
+ VALUE obj;
+{
+ VALUE result;
+ NODE *memo = rb_node_newnode(NODE_MEMO, Qnil, 0, 0);
+
+ memo->u1.value = Qfalse;
+ rb_iterate(rb_each, obj, rb_block_given_p() ? any_iter_i : any_i, (VALUE)memo);
+ result = memo->u1.value;
+ return result;
+}
+
+static VALUE
+min_i(i, memo)
+ VALUE i;
+ NODE *memo;
+{
+ VALUE cmp;
+
+ if (NIL_P(memo->u1.value)) {
+ memo->u1.value = i;
+ }
+ else {
+ cmp = rb_funcall(i, id_cmp, 1, memo->u1.value);
+ if (rb_cmpint(cmp, i, memo->u1.value) < 0) {
+ memo->u1.value = i;
+ }
+ }
+ return Qnil;
+}
+
+static VALUE
+min_ii(i, memo)
+ VALUE i;
+ NODE *memo;
+{
+ VALUE cmp;
+
+ if (NIL_P(memo->u1.value)) {
+ memo->u1.value = i;
+ }
+ else {
+ cmp = rb_yield_values(2, i, memo->u1.value);
+ if (rb_cmpint(cmp, i, memo->u1.value) < 0) {
+ memo->u1.value = i;
+ }
+ }
+ return Qnil;
+}
+
+
+/*
+ * call-seq:
+ * enum.min => obj
+ * enum.min {| a,b | block } => obj
+ *
+ * Returns the object in <i>enum</i> with the minimum value. The
+ * first form assumes all objects implement <code>Comparable</code>;
+ * the second uses the block to return <em>a <=> b</em>.
+ *
+ * a = %w(albatross dog horse)
+ * a.min #=> "albatross"
+ * a.min {|a,b| a.length <=> b.length } #=> "dog"
+ */
+
+static VALUE
+enum_min(obj)
+ VALUE obj;
+{
+ VALUE result;
+ NODE *memo = rb_node_newnode(NODE_MEMO, Qnil, 0, 0);
+
+ rb_iterate(rb_each, obj, rb_block_given_p() ? min_ii : min_i, (VALUE)memo);
+ result = memo->u1.value;
+ return result;
+}
+
+static VALUE
+max_i(i, memo)
+ VALUE i;
+ NODE *memo;
+{
+ VALUE cmp;
+
+ if (NIL_P(memo->u1.value)) {
+ memo->u1.value = i;
+ }
+ else {
+ cmp = rb_funcall(i, id_cmp, 1, memo->u1.value);
+ if (rb_cmpint(cmp, i, memo->u1.value) > 0) {
+ memo->u1.value = i;
+ }
+ }
+ return Qnil;
+}
+
+static VALUE
+max_ii(i, memo)
+ VALUE i;
+ NODE *memo;
+{
+ VALUE cmp;
+
+ if (NIL_P(memo->u1.value)) {
+ memo->u1.value = i;
+ }
+ else {
+ cmp = rb_yield_values(2, i, memo->u1.value);
+ if (rb_cmpint(cmp, i, memo->u1.value) > 0) {
+ memo->u1.value = i;
+ }
+ }
+ return Qnil;
+}
+
+/*
+ * call-seq:
+ * enum.max => obj
+ * enum.max {|a,b| block } => obj
+ *
+ * Returns the object in _enum_ with the maximum value. The
+ * first form assumes all objects implement <code>Comparable</code>;
+ * the second uses the block to return <em>a <=> b</em>.
+ *
+ * a = %w(albatross dog horse)
+ * a.max #=> "horse"
+ * a.max {|a,b| a.length <=> b.length } #=> "albatross"
+ */
+
+static VALUE
+enum_max(obj)
+ VALUE obj;
+{
+ VALUE result;
+ NODE *memo = rb_node_newnode(NODE_MEMO, Qnil, 0, 0);
+
+ rb_iterate(rb_each, obj, rb_block_given_p() ? max_ii : max_i, (VALUE)memo);
+ result = memo->u1.value;
+ return result;
+}
+
+static VALUE
+min_by_i(i, memo)
+ VALUE i;
+ NODE *memo;
+{
+ VALUE v;
+
+ v = rb_yield(i);
+ if (NIL_P(memo->u1.value)) {
+ memo->u1.value = v;
+ memo->u2.value = i;
+ }
+ else if (rb_cmpint(rb_funcall(v, id_cmp, 1, memo->u1.value), v, memo->u1.value) < 0) {
+ memo->u1.value = v;
+ memo->u2.value = i;
+ }
+ return Qnil;
+}
+
+/*
+ * call-seq:
+ * enum.min_by {| obj| block } => obj
+ *
+ * Returns the object in <i>enum</i> that gives the minimum
+ * value from the given block.
+ *
+ * a = %w(albatross dog horse)
+ * a.min_by {|x| x.length } #=> "dog"
+ */
+
+static VALUE
+enum_min_by(obj)
+ VALUE obj;
+{
+ VALUE result;
+ NODE *memo = rb_node_newnode(NODE_MEMO, Qnil, 0, 0);
+
+ rb_iterate(rb_each, obj, min_by_i, (VALUE)memo);
+ result = memo->u2.value;
+ return result;
+}
+
+static VALUE
+max_by_i(i, memo)
+ VALUE i;
+ NODE *memo;
+{
+ VALUE v;
+
+ v = rb_yield(i);
+ if (NIL_P(memo->u1.value)) {
+ memo->u1.value = v;
+ memo->u2.value = i;
+ }
+ else if (rb_cmpint(rb_funcall(v, id_cmp, 1, memo->u1.value), v, memo->u1.value) > 0) {
+ memo->u1.value = v;
+ memo->u2.value = i;
+ }
+ return Qnil;
+}
+
+/*
+ * call-seq:
+ * enum.max_by {| obj| block } => obj
+ *
+ * Returns the object in <i>enum</i> that gives the maximum
+ * value from the given block.
+ *
+ * a = %w(albatross dog horse)
+ * a.max_by {|x| x.length } #=> "albatross"
+ */
+
+static VALUE
+enum_max_by(obj)
+ VALUE obj;
+{
+ VALUE result;
+ NODE *memo = rb_node_newnode(NODE_MEMO, Qnil, 0, 0);
+
+ rb_iterate(rb_each, obj, max_by_i, (VALUE)memo);
+ result = memo->u2.value;
+ return result;
+}
+
+static VALUE
+member_i(item, memo)
+ VALUE item;
+ NODE *memo;
+{
+ if (rb_equal(item, memo->u1.value)) {
+ memo->u2.value = Qtrue;
+ rb_iter_break();
+ }
+ return Qnil;
+}
+
+/*
+ * call-seq:
+ * enum.include?(obj) => true or false
+ * enum.member?(obj) => true or false
+ *
+ * Returns <code>true</code> if any member of <i>enum</i> equals
+ * <i>obj</i>. Equality is tested using <code>==</code>.
+ *
+ * IO.constants.include? "SEEK_SET" #=> true
+ * IO.constants.include? "SEEK_NO_FURTHER" #=> false
+ *
+ */
+
+static VALUE
+enum_member(obj, val)
+ VALUE obj, val;
+{
+ VALUE result;
+ NODE *memo = rb_node_newnode(NODE_MEMO, val, Qfalse, 0);
+
+ rb_iterate(rb_each, obj, member_i, (VALUE)memo);
+ result = memo->u2.value;
+ return result;
+}
+
+static VALUE
+each_with_index_i(val, memo)
+ VALUE val;
+ NODE *memo;
+{
+ rb_yield_values(2, val, INT2FIX(memo->u3.cnt));
+ memo->u3.cnt++;
+ return Qnil;
+}
+
+/*
+ * call-seq:
+ * enum.each_with_index {|obj, i| block } -> enum
+ *
+ * Calls <em>block</em> with two arguments, the item and its index, for
+ * each item in <i>enum</i>.
+ *
+ * hash = Hash.new
+ * %w(cat dog wombat).each_with_index {|item, index|
+ * hash[item] = index
+ * }
+ * hash #=> {"cat"=>0, "wombat"=>2, "dog"=>1}
+ *
+ */
+
+static VALUE
+enum_each_with_index(obj)
+ VALUE obj;
+{
+ NODE *memo = rb_node_newnode(NODE_MEMO, 0, 0, 0);
+
+ rb_iterate(rb_each, obj, each_with_index_i, (VALUE)memo);
+ return obj;
+}
+
+static VALUE
+zip_i(val, memo)
+ VALUE val;
+ NODE *memo;
+{
+ VALUE result = memo->u1.value;
+ VALUE args = memo->u2.value;
+ int idx = memo->u3.cnt++;
+ VALUE tmp;
+ int i;
+
+ tmp = rb_ary_new2(RARRAY(args)->len + 1);
+ rb_ary_store(tmp, 0, val);
+ for (i=0; i<RARRAY(args)->len; i++) {
+ rb_ary_push(tmp, rb_ary_entry(RARRAY(args)->ptr[i], idx));
+ }
+ if (rb_block_given_p()) {
+ rb_yield(tmp);
+ }
+ else {
+ rb_ary_push(result, tmp);
+ }
+ return Qnil;
+}
+
+/*
+ * call-seq:
+ * enum.zip(arg, ...) => array
+ * enum.zip(arg, ...) {|arr| block } => nil
+ *
+ * Converts any arguments to arrays, then merges elements of
+ * <i>enum</i> with corresponding elements from each argument. This
+ * generates a sequence of <code>enum#size</code> <em>n</em>-element
+ * arrays, where <em>n</em> is one more that the count of arguments. If
+ * the size of any argument is less than <code>enum#size</code>,
+ * <code>nil</code> values are supplied. If a block given, it is
+ * invoked for each output array, otherwise an array of arrays is
+ * returned.
+ *
+ * a = [ 4, 5, 6 ]
+ * b = [ 7, 8, 9 ]
+ *
+ * (1..3).zip(a, b) #=> [[1, 4, 7], [2, 5, 8], [3, 6, 9]]
+ * "cat\ndog".zip([1]) #=> [["cat\n", 1], ["dog", nil]]
+ * (1..3).zip #=> [[1], [2], [3]]
+ *
+ */
+
+static VALUE
+enum_zip(argc, argv, obj)
+ int argc;
+ VALUE *argv;
+ VALUE obj;
+{
+ int i;
+ VALUE result;
+ NODE *memo;
+
+ for (i=0; i<argc; i++) {
+ argv[i] = rb_convert_type(argv[i], T_ARRAY, "Array", "to_a");
+ }
+ result = rb_block_given_p() ? Qnil : rb_ary_new();
+ memo = rb_node_newnode(NODE_MEMO, result, rb_ary_new4(argc, argv), 0);
+ rb_iterate(rb_each, obj, zip_i, (VALUE)memo);
+
+ return result;
+}
+
+/*
+ * The <code>Enumerable</code> mixin provides collection classes with
+ * several traversal and searching methods, and with the ability to
+ * sort. The class must provide a method <code>each</code>, which
+ * yields successive members of the collection. If
+ * <code>Enumerable#max</code>, <code>#min</code>, or
+ * <code>#sort</code> is used, the objects in the collection must also
+ * implement a meaningful <code><=></code> operator, as these methods
+ * rely on an ordering between members of the collection.
+ */
+
+void
+Init_Enumerable()
+{
+ rb_mEnumerable = rb_define_module("Enumerable");
+
+ rb_define_method(rb_mEnumerable,"to_a", enum_to_a, 0);
+ rb_define_method(rb_mEnumerable,"entries", enum_to_a, 0);
+
+ rb_define_method(rb_mEnumerable,"sort", enum_sort, 0);
+ rb_define_method(rb_mEnumerable,"sort_by", enum_sort_by, 0);
+ rb_define_method(rb_mEnumerable,"grep", enum_grep, 1);
+ rb_define_method(rb_mEnumerable,"find", enum_find, -1);
+ rb_define_method(rb_mEnumerable,"detect", enum_find, -1);
+ rb_define_method(rb_mEnumerable,"find_all", enum_find_all, 0);
+ rb_define_method(rb_mEnumerable,"select", enum_find_all, 0);
+ rb_define_method(rb_mEnumerable,"reject", enum_reject, 0);
+ rb_define_method(rb_mEnumerable,"collect", enum_collect, 0);
+ rb_define_method(rb_mEnumerable,"map", enum_collect, 0);
+ rb_define_method(rb_mEnumerable,"inject", enum_inject, -1);
+ rb_define_method(rb_mEnumerable,"partition", enum_partition, 0);
+ rb_define_method(rb_mEnumerable,"all?", enum_all, 0);
+ rb_define_method(rb_mEnumerable,"any?", enum_any, 0);
+ rb_define_method(rb_mEnumerable,"min", enum_min, 0);
+ rb_define_method(rb_mEnumerable,"max", enum_max, 0);
+ rb_define_method(rb_mEnumerable,"min_by", enum_min_by, 0);
+ rb_define_method(rb_mEnumerable,"max_by", enum_max_by, 0);
+ rb_define_method(rb_mEnumerable,"member?", enum_member, 1);
+ rb_define_method(rb_mEnumerable,"include?", enum_member, 1);
+ rb_define_method(rb_mEnumerable,"each_with_index", enum_each_with_index, 0);
+ rb_define_method(rb_mEnumerable, "zip", enum_zip, -1);
+
+ id_eqq = rb_intern("===");
+ id_each = rb_intern("each");
+ id_cmp = rb_intern("<=>");
+}
+
+/**********************************************************************
+
+ error.c -
+
+ $Author: murphy $
+ $Date: 2005-11-05 04:33:55 +0100 (Sa, 05 Nov 2005) $
+ created at: Mon Aug 9 16:11:34 JST 1993
+
+ Copyright (C) 1993-2003 Yukihiro Matsumoto
+
+**********************************************************************/
+
+#include "ruby.h"
+#include "env.h"
+#include "st.h"
+
+#include <stdio.h>
+#ifdef HAVE_STDARG_PROTOTYPES
+#include <stdarg.h>
+#define va_init_list(a,b) va_start(a,b)
+#else
+#include <varargs.h>
+#define va_init_list(a,b) va_start(a)
+#endif
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#ifndef EXIT_SUCCESS
+#define EXIT_SUCCESS 0
+#endif
+
+extern const char ruby_version[], ruby_release_date[], ruby_platform[];
+
+int ruby_nerrs;
+
+static int
+err_position(buf, len)
+ char *buf;
+ long len;
+{
+ ruby_set_current_source();
+ if (!ruby_sourcefile) {
+ return 0;
+ }
+ else if (ruby_sourceline == 0) {
+ return snprintf(buf, len, "%s: ", ruby_sourcefile);
+ }
+ else {
+ return snprintf(buf, len, "%s:%d: ", ruby_sourcefile, ruby_sourceline);
+ }
+}
+
+static void
+err_snprintf(buf, len, fmt, args)
+ char *buf;
+ long len;
+ const char *fmt;
+ va_list args;
+{
+ long n;
+
+ n = err_position(buf, len);
+ if (len > n) {
+ vsnprintf((char*)buf+n, len-n, fmt, args);
+ }
+}
+
+static void err_append _((const char*));
+static void
+err_print(fmt, args)
+ const char *fmt;
+ va_list args;
+{
+ char buf[BUFSIZ];
+
+ err_snprintf(buf, BUFSIZ, fmt, args);
+ err_append(buf);
+}
+
+void
+#ifdef HAVE_STDARG_PROTOTYPES
+rb_compile_error(const char *fmt, ...)
+#else
+rb_compile_error(fmt, va_alist)
+ const char *fmt;
+ va_dcl
+#endif
+{
+ va_list args;
+
+ va_init_list(args, fmt);
+ err_print(fmt, args);
+ va_end(args);
+ ruby_nerrs++;
+}
+
+void
+#ifdef HAVE_STDARG_PROTOTYPES
+rb_compile_error_append(const char *fmt, ...)
+#else
+rb_compile_error_append(fmt, va_alist)
+ const char *fmt;
+ va_dcl
+#endif
+{
+ va_list args;
+ char buf[BUFSIZ];
+
+ va_init_list(args, fmt);
+ vsnprintf(buf, BUFSIZ, fmt, args);
+ va_end(args);
+ err_append(buf);
+}
+
+static void
+warn_print(fmt, args)
+ const char *fmt;
+ va_list args;
+{
+ char buf[BUFSIZ];
+ int len;
+
+ err_snprintf(buf, BUFSIZ, fmt, args);
+ len = strlen(buf);
+ buf[len++] = '\n';
+ rb_write_error2(buf, len);
+}
+
+void
+#ifdef HAVE_STDARG_PROTOTYPES
+rb_warn(const char *fmt, ...)
+#else
+rb_warn(fmt, va_alist)
+ const char *fmt;
+ va_dcl
+#endif
+{
+ char buf[BUFSIZ];
+ va_list args;
+
+ if (NIL_P(ruby_verbose)) return;
+
+ snprintf(buf, BUFSIZ, "warning: %s", fmt);
+
+ va_init_list(args, fmt);
+ warn_print(buf, args);
+ va_end(args);
+}
+
+/* rb_warning() reports only in verbose mode */
+void
+#ifdef HAVE_STDARG_PROTOTYPES
+rb_warning(const char *fmt, ...)
+#else
+rb_warning(fmt, va_alist)
+ const char *fmt;
+ va_dcl
+#endif
+{
+ char buf[BUFSIZ];
+ va_list args;
+
+ if (!RTEST(ruby_verbose)) return;
+
+ snprintf(buf, BUFSIZ, "warning: %s", fmt);
+
+ va_init_list(args, fmt);
+ warn_print(buf, args);
+ va_end(args);
+}
+
+/*
+ * call-seq:
+ * warn(msg) => nil
+ *
+ * Display the given message (followed by a newline) on STDERR unless
+ * warnings are disabled (for example with the <code>-W0</code> flag).
+ */
+
+static VALUE
+rb_warn_m(self, mesg)
+ VALUE self, mesg;
+{
+ if (!NIL_P(ruby_verbose)) {
+ rb_io_write(rb_stderr, mesg);
+ rb_io_write(rb_stderr, rb_default_rs);
+ }
+ return Qnil;
+}
+
+void
+#ifdef HAVE_STDARG_PROTOTYPES
+rb_bug(const char *fmt, ...)
+#else
+rb_bug(fmt, va_alist)
+ const char *fmt;
+ va_dcl
+#endif
+{
+ char buf[BUFSIZ];
+ va_list args;
+ FILE *out = stderr;
+ int len = err_position(buf, BUFSIZ);
+
+ if (fwrite(buf, 1, len, out) == len ||
+ fwrite(buf, 1, len, (out = stdout)) == len) {
+ fputs("[BUG] ", out);
+ va_init_list(args, fmt);
+ vfprintf(out, fmt, args);
+ va_end(args);
+ fprintf(out, "\nruby %s (%s) [%s]\n\n",
+ ruby_version, ruby_release_date, ruby_platform);
+ }
+ abort();
+}
+
+static struct types {
+ int type;
+ const char *name;
+} builtin_types[] = {
+ {T_NIL, "nil"},
+ {T_OBJECT, "Object"},
+ {T_CLASS, "Class"},
+ {T_ICLASS, "iClass"}, /* internal use: mixed-in module holder */
+ {T_MODULE, "Module"},
+ {T_FLOAT, "Float"},
+ {T_STRING, "String"},
+ {T_REGEXP, "Regexp"},
+ {T_ARRAY, "Array"},
+ {T_FIXNUM, "Fixnum"},
+ {T_HASH, "Hash"},
+ {T_STRUCT, "Struct"},
+ {T_BIGNUM, "Bignum"},
+ {T_FILE, "File"},
+ {T_TRUE, "true"},
+ {T_FALSE, "false"},
+ {T_SYMBOL, "Symbol"}, /* :symbol */
+ {T_DATA, "Data"}, /* internal use: wrapped C pointers */
+ {T_MATCH, "MatchData"}, /* data of $~ */
+ {T_VARMAP, "Varmap"}, /* internal use: dynamic variables */
+ {T_SCOPE, "Scope"}, /* internal use: variable scope */
+ {T_NODE, "Node"}, /* internal use: syntax tree node */
+ {T_UNDEF, "undef"}, /* internal use: #undef; should not happen */
+ {-1, 0}
+};
+
+void
+rb_check_type(x, t)
+ VALUE x;
+ int t;
+{
+ struct types *type = builtin_types;
+
+ if (x == Qundef) {
+ rb_bug("undef leaked to the Ruby space");
+ }
+
+ if (TYPE(x) != t) {
+ while (type->type >= 0) {
+ if (type->type == t) {
+ char *etype;
+
+ if (NIL_P(x)) {
+ etype = "nil";
+ }
+ else if (FIXNUM_P(x)) {
+ etype = "Fixnum";
+ }
+ else if (SYMBOL_P(x)) {
+ etype = "Symbol";
+ }
+ else if (rb_special_const_p(x)) {
+ etype = RSTRING(rb_obj_as_string(x))->ptr;
+ }
+ else {
+ etype = rb_obj_classname(x);
+ }
+ rb_raise(rb_eTypeError, "wrong argument type %s (expected %s)",
+ etype, type->name);
+ }
+ type++;
+ }
+ rb_bug("unknown type 0x%x (0x%x given)", t, TYPE(x));
+ }
+}
+
+/* exception classes */
+#include <errno.h>
+
+VALUE rb_eException;
+VALUE rb_eSystemExit;
+VALUE rb_eInterrupt;
+VALUE rb_eSignal;
+VALUE rb_eFatal;
+VALUE rb_eStandardError;
+VALUE rb_eRuntimeError;
+VALUE rb_eTypeError;
+VALUE rb_eArgError;
+VALUE rb_eIndexError;
+VALUE rb_eKeyError;
+VALUE rb_eRangeError;
+VALUE rb_eNameError;
+VALUE rb_eNoMethodError;
+VALUE rb_eSecurityError;
+VALUE rb_eNotImpError;
+VALUE rb_eNoMemError;
+static VALUE rb_cNameErrorMesg;
+
+VALUE rb_eScriptError;
+VALUE rb_eSyntaxError;
+VALUE rb_eLoadError;
+
+VALUE rb_eSystemCallError;
+VALUE rb_mErrno;
+static VALUE eNOERROR;
+
+VALUE
+rb_exc_new(etype, ptr, len)
+ VALUE etype;
+ const char *ptr;
+ long len;
+{
+ return rb_funcall(etype, rb_intern("new"), 1, rb_str_new(ptr, len));
+}
+
+VALUE
+rb_exc_new2(etype, s)
+ VALUE etype;
+ const char *s;
+{
+ return rb_exc_new(etype, s, strlen(s));
+}
+
+VALUE
+rb_exc_new3(etype, str)
+ VALUE etype, str;
+{
+ StringValue(str);
+ return rb_funcall(etype, rb_intern("new"), 1, str);
+}
+
+/*
+ * call-seq:
+ * Exception.new(msg = nil) => exception
+ *
+ * Construct a new Exception object, optionally passing in
+ * a message.
+ */
+
+static VALUE
+exc_initialize(argc, argv, exc)
+ int argc;
+ VALUE *argv;
+ VALUE exc;
+{
+ VALUE arg;
+
+ rb_scan_args(argc, argv, "01", &arg);
+ rb_iv_set(exc, "mesg", arg);
+ rb_iv_set(exc, "bt", Qnil);
+
+ return exc;
+}
+
+/*
+ * Document-method: exception
+ *
+ * call-seq:
+ * exc.exception(string) -> an_exception or exc
+ *
+ * With no argument, or if the argument is the same as the receiver,
+ * return the receiver. Otherwise, create a new
+ * exception object of the same class as the receiver, but with a
+ * message equal to <code>string.to_str</code>.
+ *
+ */
+
+static VALUE
+exc_exception(argc, argv, self)
+ int argc;
+ VALUE *argv;
+ VALUE self;
+{
+ VALUE exc;
+
+ if (argc == 0) return self;
+ if (argc == 1 && self == argv[0]) return self;
+ exc = rb_obj_clone(self);
+ exc_initialize(argc, argv, exc);
+
+ return exc;
+}
+
+/*
+ * call-seq:
+ * exception.to_s => string
+ *
+ * Returns exception's message (or the name of the exception if
+ * no message is set).
+ */
+
+static VALUE
+exc_to_s(exc)
+ VALUE exc;
+{
+ VALUE mesg = rb_attr_get(exc, rb_intern("mesg"));
+
+ if (NIL_P(mesg)) return rb_class_name(CLASS_OF(exc));
+ if (OBJ_TAINTED(exc)) OBJ_TAINT(mesg);
+ return mesg;
+}
+
+/*
+ * call-seq:
+ * exception.message => string
+ *
+ * Returns the result of invoking <code>exception.to_s</code>.
+ * Normally this returns the exception's message or name. By
+ * supplying a to_str method, exceptions are agreeing to
+ * be used where Strings are expected.
+ */
+
+static VALUE
+exc_message(exc)
+ VALUE exc;
+{
+ return rb_funcall(exc, rb_intern("to_s"), 0, 0);
+}
+
+/*
+ * call-seq:
+ * exception.inspect => string
+ *
+ * Return this exception's class name an message
+ */
+
+static VALUE
+exc_inspect(exc)
+ VALUE exc;
+{
+ VALUE str, klass;
+
+ klass = CLASS_OF(exc);
+ exc = rb_obj_as_string(exc);
+ if (RSTRING(exc)->len == 0) {
+ return rb_str_dup(rb_class_name(klass));
+ }
+
+ str = rb_str_buf_new2("#<");
+ klass = rb_class_name(klass);
+ rb_str_buf_append(str, klass);
+ rb_str_buf_cat(str, ": ", 2);
+ rb_str_buf_append(str, exc);
+ rb_str_buf_cat(str, ">", 1);
+
+ return str;
+}
+
+/*
+ * call-seq:
+ * exception.backtrace => array
+ *
+ * Returns any backtrace associated with the exception. The backtrace
+ * is an array of strings, each containing either ``filename:lineNo: in
+ * `method''' or ``filename:lineNo.''
+ *
+ * def a
+ * raise "boom"
+ * end
+ *
+ * def b
+ * a()
+ * end
+ *
+ * begin
+ * b()
+ * rescue => detail
+ * print detail.backtrace.join("\n")
+ * end
+ *
+ * <em>produces:</em>
+ *
+ * prog.rb:2:in `a'
+ * prog.rb:6:in `b'
+ * prog.rb:10
+*/
+
+static VALUE
+exc_backtrace(exc)
+ VALUE exc;
+{
+ ID bt = rb_intern("bt");
+
+ if (!rb_ivar_defined(exc, bt)) return Qnil;
+ return rb_ivar_get(exc, bt);
+}
+
+static VALUE
+check_backtrace(bt)
+ VALUE bt;
+{
+ long i;
+ static char *err = "backtrace must be Array of String";
+
+ if (!NIL_P(bt)) {
+ int t = TYPE(bt);
+
+ if (t == T_STRING) return rb_ary_new3(1, bt);
+ if (t != T_ARRAY) {
+ rb_raise(rb_eTypeError, err);
+ }
+ for (i=0;i<RARRAY(bt)->len;i++) {
+ if (TYPE(RARRAY(bt)->ptr[i]) != T_STRING) {
+ rb_raise(rb_eTypeError, err);
+ }
+ }
+ }
+ return bt;
+}
+
+/*
+ * call-seq:
+ * exc.set_backtrace(array) => array
+ *
+ * Sets the backtrace information associated with <i>exc</i>. The
+ * argument must be an array of <code>String</code> objects in the
+ * format described in <code>Exception#backtrace</code>.
+ *
+ */
+
+static VALUE
+exc_set_backtrace(exc, bt)
+ VALUE exc;
+ VALUE bt;
+{
+ return rb_iv_set(exc, "bt", check_backtrace(bt));
+}
+
+/*
+ * call-seq:
+ * exc == obj => true or false
+ *
+ * Equality---If <i>obj</i> is not an <code>Exception</code>, returns
+ * <code>false</code>. Otherwise, returns <code>true</code> if <i>exc</i> and
+ * <i>obj</i> share same class, messages, and backtrace.
+ */
+
+static VALUE
+exc_equal(exc, obj)
+ VALUE exc;
+ VALUE obj;
+{
+ ID id_mesg = rb_intern("mesg");
+
+ if (exc == obj) return Qtrue;
+ if (rb_obj_class(exc) != rb_obj_class(obj))
+ return Qfalse;
+ if (!rb_equal(rb_attr_get(exc, id_mesg), rb_attr_get(obj, id_mesg)))
+ return Qfalse;
+ if (!rb_equal(exc_backtrace(exc), exc_backtrace(obj)))
+ return Qfalse;
+ return Qtrue;
+}
+
+/*
+ * call-seq:
+ * SystemExit.new(status=0) => system_exit
+ *
+ * Create a new +SystemExit+ exception with the given status.
+ */
+
+static VALUE
+exit_initialize(argc, argv, exc)
+ int argc;
+ VALUE *argv;
+ VALUE exc;
+{
+ VALUE status = INT2FIX(EXIT_SUCCESS);
+ if (argc > 0 && FIXNUM_P(argv[0])) {
+ status = *argv++;
+ --argc;
+ }
+ exc_initialize(argc, argv, exc);
+ rb_iv_set(exc, "status", status);
+ return exc;
+}
+
+
+/*
+ * call-seq:
+ * system_exit.status => fixnum
+ *
+ * Return the status value associated with this system exit.
+ */
+
+static VALUE
+exit_status(exc)
+ VALUE exc;
+{
+ return rb_attr_get(exc, rb_intern("status"));
+}
+
+
+/*
+ * call-seq:
+ * system_exit.success? => true or false
+ *
+ * Returns +true+ if exiting successful, +false+ if not.
+ */
+
+static VALUE
+exit_success_p(exc)
+ VALUE exc;
+{
+ VALUE status = rb_attr_get(exc, rb_intern("status"));
+ if (NIL_P(status)) return Qtrue;
+ if (status == INT2FIX(EXIT_SUCCESS)) return Qtrue;
+ return Qfalse;
+}
+
+void
+#ifdef HAVE_STDARG_PROTOTYPES
+rb_name_error(ID id, const char *fmt, ...)
+#else
+rb_name_error(id, fmt, va_alist)
+ ID id;
+ const char *fmt;
+ va_dcl
+#endif
+{
+ VALUE exc, argv[2];
+ va_list args;
+ char buf[BUFSIZ];
+
+ va_init_list(args, fmt);
+ vsnprintf(buf, BUFSIZ, fmt, args);
+ va_end(args);
+
+ argv[0] = rb_str_new2(buf);
+ argv[1] = ID2SYM(id);
+ exc = rb_class_new_instance(2, argv, rb_eNameError);
+ rb_exc_raise(exc);
+}
+
+/*
+ * call-seq:
+ * NameError.new(msg [, name]) => name_error
+ *
+ * Construct a new NameError exception. If given the <i>name</i>
+ * parameter may subsequently be examined using the <code>NameError.name</code>
+ * method.
+ */
+
+static VALUE
+name_err_initialize(argc, argv, self)
+ int argc;
+ VALUE *argv;
+ VALUE self;
+{
+ VALUE name;
+
+ name = (argc > 1) ? argv[--argc] : Qnil;
+ exc_initialize(argc, argv, self);
+ rb_iv_set(self, "name", name);
+ return self;
+}
+
+/*
+ * call-seq:
+ * name_error.name => string or nil
+ *
+ * Return the name associated with this NameError exception.
+ */
+
+static VALUE
+name_err_name(self)
+ VALUE self;
+{
+ return rb_attr_get(self, rb_intern("name"));
+}
+
+/*
+ * call-seq:
+ * name_error.to_s => string
+ *
+ * Produce a nicely-formated string representing the +NameError+.
+ */
+
+static VALUE
+name_err_to_s(exc)
+ VALUE exc;
+{
+ VALUE mesg = rb_attr_get(exc, rb_intern("mesg"));
+ VALUE str = mesg;
+
+ if (NIL_P(mesg)) return rb_class_name(CLASS_OF(exc));
+ StringValue(str);
+ if (str != mesg) {
+ rb_iv_set(exc, "mesg", mesg = str);
+ }
+ if (OBJ_TAINTED(exc)) OBJ_TAINT(mesg);
+ return mesg;
+}
+
+/*
+ * call-seq:
+ * NoMethodError.new(msg, name [, args]) => no_method_error
+ *
+ * Construct a NoMethodError exception for a method of the given name
+ * called with the given arguments. The name may be accessed using
+ * the <code>#name</code> method on the resulting object, and the
+ * arguments using the <code>#args</code> method.
+ */
+
+static VALUE
+nometh_err_initialize(argc, argv, self)
+ int argc;
+ VALUE *argv;
+ VALUE self;
+{
+ VALUE args = (argc > 2) ? argv[--argc] : Qnil;
+ name_err_initialize(argc, argv, self);
+ rb_iv_set(self, "args", args);
+ return self;
+}
+
+/* :nodoc: */
+static void
+name_err_mesg_mark(ptr)
+ VALUE *ptr;
+{
+ rb_gc_mark_locations(ptr, ptr+3);
+}
+
+/* :nodoc: */
+static VALUE
+name_err_mesg_new(obj, mesg, recv, method)
+ VALUE obj, mesg, recv, method;
+{
+ VALUE *ptr = ALLOC_N(VALUE, 3);
+
+ ptr[0] = mesg;
+ ptr[1] = recv;
+ ptr[2] = method;
+ return Data_Wrap_Struct(rb_cNameErrorMesg, name_err_mesg_mark, -1, ptr);
+}
+
+/* :nodoc: */
+static VALUE
+name_err_mesg_equal(obj1, obj2)
+ VALUE obj1, obj2;
+{
+ VALUE *ptr1, *ptr2;
+ int i;
+
+ if (obj1 == obj2) return Qtrue;
+ if (rb_obj_class(obj2) != rb_cNameErrorMesg)
+ return Qfalse;
+
+ Data_Get_Struct(obj1, VALUE, ptr1);
+ Data_Get_Struct(obj2, VALUE, ptr2);
+ for (i=0; i<3; i++) {
+ if (!rb_equal(ptr1[i], ptr2[i]))
+ return Qfalse;
+ }
+ return Qtrue;
+}
+
+/* :nodoc: */
+static VALUE
+name_err_mesg_to_str(obj)
+ VALUE obj;
+{
+ VALUE *ptr, mesg;
+ Data_Get_Struct(obj, VALUE, ptr);
+
+ mesg = ptr[0];
+ if (NIL_P(mesg)) return Qnil;
+ else {
+ char *desc = 0;
+ VALUE d = 0, args[3];
+
+ obj = ptr[1];
+ switch (TYPE(obj)) {
+ case T_NIL:
+ desc = "nil";
+ break;
+ case T_TRUE:
+ desc = "true";
+ break;
+ case T_FALSE:
+ desc = "false";
+ break;
+ default:
+ d = rb_protect(rb_inspect, obj, 0);
+ if (NIL_P(d) || RSTRING(d)->len > 65) {
+ d = rb_any_to_s(obj);
+ }
+ desc = RSTRING(d)->ptr;
+ break;
+ }
+ if (desc && desc[0] != '#') {
+ d = rb_str_new2(desc);
+ rb_str_cat2(d, ":");
+ rb_str_cat2(d, rb_obj_classname(obj));
+ }
+ args[0] = mesg;
+ args[1] = ptr[2];
+ args[2] = d;
+ mesg = rb_f_sprintf(3, args);
+ }
+ if (OBJ_TAINTED(obj)) OBJ_TAINT(mesg);
+ return mesg;
+}
+
+/* :nodoc: */
+static VALUE
+name_err_mesg_load(klass, str)
+ VALUE klass, str;
+{
+ return str;
+}
+
+/*
+ * call-seq:
+ * no_method_error.args => obj
+ *
+ * Return the arguments passed in as the third parameter to
+ * the constructor.
+ */
+
+static VALUE
+nometh_err_args(self)
+ VALUE self;
+{
+ return rb_attr_get(self, rb_intern("args"));
+}
+
+void
+rb_invalid_str(str, type)
+ const char *str, *type;
+{
+ VALUE s = rb_str_inspect(rb_str_new2(str));
+
+ rb_raise(rb_eArgError, "invalid value for %s: %s", type, RSTRING(s)->ptr);
+}
+
+/*
+ * Document-module: Errno
+ *
+ * Ruby exception objects are subclasses of <code>Exception</code>.
+ * However, operating systems typically report errors using plain
+ * integers. Module <code>Errno</code> is created dynamically to map
+ * these operating system errors to Ruby classes, with each error
+ * number generating its own subclass of <code>SystemCallError</code>.
+ * As the subclass is created in module <code>Errno</code>, its name
+ * will start <code>Errno::</code>.
+ *
+ * The names of the <code>Errno::</code> classes depend on
+ * the environment in which Ruby runs. On a typical Unix or Windows
+ * platform, there are <code>Errno</code> classes such as
+ * <code>Errno::EACCES</code>, <code>Errno::EAGAIN</code>,
+ * <code>Errno::EINTR</code>, and so on.
+ *
+ * The integer operating system error number corresponding to a
+ * particular error is available as the class constant
+ * <code>Errno::</code><em>error</em><code>::Errno</code>.
+ *
+ * Errno::EACCES::Errno #=> 13
+ * Errno::EAGAIN::Errno #=> 11
+ * Errno::EINTR::Errno #=> 4
+ *
+ * The full list of operating system errors on your particular platform
+ * are available as the constants of <code>Errno</code>.
+ *
+ * Errno.constants #=> E2BIG, EACCES, EADDRINUSE, EADDRNOTAVAIL, ...
+ */
+
+static st_table *syserr_tbl;
+
+static VALUE
+set_syserr(n, name)
+ int n;
+ const char *name;
+{
+ VALUE error;
+
+ if (!st_lookup(syserr_tbl, n, &error)) {
+ error = rb_define_class_under(rb_mErrno, name, rb_eSystemCallError);
+ rb_define_const(error, "Errno", INT2NUM(n));
+ st_add_direct(syserr_tbl, n, error);
+ }
+ else {
+ rb_define_const(rb_mErrno, name, error);
+ }
+ return error;
+}
+
+static VALUE
+get_syserr(n)
+ int n;
+{
+ VALUE error;
+
+ if (!st_lookup(syserr_tbl, n, &error)) {
+ char name[8]; /* some Windows' errno have 5 digits. */
+
+ snprintf(name, sizeof(name), "E%03d", n);
+ error = set_syserr(n, name);
+ }
+ return error;
+}
+
+/*
+ * call-seq:
+ * SystemCallError.new(msg, errno) => system_call_error_subclass
+ *
+ * If _errno_ corresponds to a known system error code, constructs
+ * the appropriate <code>Errno</code> class for that error, otherwise
+ * constructs a generic <code>SystemCallError</code> object. The
+ * error number is subsequently available via the <code>errno</code>
+ * method.
+ */
+
+static VALUE
+syserr_initialize(argc, argv, self)
+ int argc;
+ VALUE *argv;
+ VALUE self;
+{
+#if !defined(_WIN32) && !defined(__VMS)
+ char *strerror();
+#endif
+ char *err;
+ VALUE mesg, error;
+ VALUE klass = rb_obj_class(self);
+
+ if (klass == rb_eSystemCallError) {
+ rb_scan_args(argc, argv, "11", &mesg, &error);
+ if (argc == 1 && FIXNUM_P(mesg)) {
+ error = mesg; mesg = Qnil;
+ }
+ if (!NIL_P(error) && st_lookup(syserr_tbl, NUM2LONG(error), &klass)) {
+ /* change class */
+ if (TYPE(self) != T_OBJECT) { /* insurance to avoid type crash */
+ rb_raise(rb_eTypeError, "invalid instance type");
+ }
+ RBASIC(self)->klass = klass;
+ }
+ }
+ else {
+ rb_scan_args(argc, argv, "01", &mesg);
+ error = rb_const_get(klass, rb_intern("Errno"));
+ }
+ if (!NIL_P(error)) err = strerror(NUM2LONG(error));
+ else err = "unknown error";
+ if (!NIL_P(mesg)) {
+ VALUE str = mesg;
+ StringValue(str);
+ mesg = rb_str_new(0, strlen(err)+RSTRING(str)->len+3);
+ sprintf(RSTRING(mesg)->ptr, "%s - %.*s", err,
+ (int)RSTRING(str)->len, RSTRING(str)->ptr);
+ rb_str_resize(mesg, strlen(RSTRING(mesg)->ptr));
+ }
+ else {
+ mesg = rb_str_new2(err);
+ }
+ exc_initialize(1, &mesg, self);
+ rb_iv_set(self, "errno", error);
+ return self;
+}
+
+/*
+ * call-seq:
+ * system_call_error.errno => fixnum
+ *
+ * Return this SystemCallError's error number.
+ */
+
+static VALUE
+syserr_errno(self)
+ VALUE self;
+{
+ return rb_attr_get(self, rb_intern("errno"));
+}
+
+/*
+ * call-seq:
+ * system_call_error === other => true or false
+ *
+ * Return +true+ if the receiver is a generic +SystemCallError+, or
+ * if the error numbers _self_ and _other_ are the same.
+ */
+
+static VALUE
+syserr_eqq(self, exc)
+ VALUE self, exc;
+{
+ VALUE num, e;
+
+ if (!rb_obj_is_kind_of(exc, rb_eSystemCallError)) return Qfalse;
+ if (self == rb_eSystemCallError) return Qtrue;
+
+ num = rb_attr_get(exc, rb_intern("errno"));
+ if (NIL_P(num)) {
+ VALUE klass = CLASS_OF(exc);
+
+ while (TYPE(klass) == T_ICLASS || FL_TEST(klass, FL_SINGLETON)) {
+ klass = (VALUE)RCLASS(klass)->super;
+ }
+ num = rb_const_get(klass, rb_intern("Errno"));
+ }
+ e = rb_const_get(self, rb_intern("Errno"));
+ if (FIXNUM_P(num) ? num == e : rb_equal(num, e))
+ return Qtrue;
+ return Qfalse;
+}
+
+/*
+ * call-seq:
+ * Errno.const_missing => SystemCallError
+ *
+ * Returns default SystemCallError class.
+ */
+static VALUE
+errno_missing(self, id)
+ VALUE self, id;
+{
+ return eNOERROR;
+}
+
+/*
+ * Descendents of class <code>Exception</code> are used to communicate
+ * between <code>raise</code> methods and <code>rescue</code>
+ * statements in <code>begin/end</code> blocks. <code>Exception</code>
+ * objects carry information about the exception---its type (the
+ * exception's class name), an optional descriptive string, and
+ * optional traceback information. Programs may subclass
+ * <code>Exception</code> to add additional information.
+ */
+
+void
+Init_Exception()
+{
+ rb_eException = rb_define_class("Exception", rb_cObject);
+ rb_define_singleton_method(rb_eException, "exception", rb_class_new_instance, -1);
+ rb_define_method(rb_eException, "exception", exc_exception, -1);
+ rb_define_method(rb_eException, "initialize", exc_initialize, -1);
+ rb_define_method(rb_eException, "==", exc_equal, 1);
+ rb_define_method(rb_eException, "to_s", exc_to_s, 0);
+ rb_define_method(rb_eException, "message", exc_message, 0);
+ rb_define_method(rb_eException, "inspect", exc_inspect, 0);
+ rb_define_method(rb_eException, "backtrace", exc_backtrace, 0);
+ rb_define_method(rb_eException, "set_backtrace", exc_set_backtrace, 1);
+
+ rb_eSystemExit = rb_define_class("SystemExit", rb_eException);
+ rb_define_method(rb_eSystemExit, "initialize", exit_initialize, -1);
+ rb_define_method(rb_eSystemExit, "status", exit_status, 0);
+ rb_define_method(rb_eSystemExit, "success?", exit_success_p, 0);
+
+ rb_eFatal = rb_define_class("fatal", rb_eException);
+ rb_eSignal = rb_define_class("SignalException", rb_eException);
+ rb_eInterrupt = rb_define_class("Interrupt", rb_eSignal);
+
+ rb_eStandardError = rb_define_class("StandardError", rb_eException);
+ rb_eTypeError = rb_define_class("TypeError", rb_eStandardError);
+ rb_eArgError = rb_define_class("ArgumentError", rb_eStandardError);
+ rb_eIndexError = rb_define_class("IndexError", rb_eStandardError);
+ rb_eKeyError = rb_define_class("KeyError", rb_eIndexError);
+ rb_eRangeError = rb_define_class("RangeError", rb_eStandardError);
+ rb_eNameError = rb_define_class("NameError", rb_eStandardError);
+ rb_define_method(rb_eNameError, "initialize", name_err_initialize, -1);
+ rb_define_method(rb_eNameError, "name", name_err_name, 0);
+ rb_define_method(rb_eNameError, "to_s", name_err_to_s, 0);
+ rb_cNameErrorMesg = rb_define_class_under(rb_eNameError, "message", rb_cData);
+ rb_define_singleton_method(rb_cNameErrorMesg, "!", name_err_mesg_new, 3);
+ rb_define_method(rb_cNameErrorMesg, "==", name_err_mesg_equal, 1);
+ rb_define_method(rb_cNameErrorMesg, "to_str", name_err_mesg_to_str, 0);
+ rb_define_method(rb_cNameErrorMesg, "_dump", name_err_mesg_to_str, 1);
+ rb_define_singleton_method(rb_cNameErrorMesg, "_load", name_err_mesg_load, 1);
+ rb_eNoMethodError = rb_define_class("NoMethodError", rb_eNameError);
+ rb_define_method(rb_eNoMethodError, "initialize", nometh_err_initialize, -1);
+ rb_define_method(rb_eNoMethodError, "args", nometh_err_args, 0);
+
+ rb_eScriptError = rb_define_class("ScriptError", rb_eException);
+ rb_eSyntaxError = rb_define_class("SyntaxError", rb_eScriptError);
+ rb_eLoadError = rb_define_class("LoadError", rb_eScriptError);
+ rb_eNotImpError = rb_define_class("NotImplementedError", rb_eScriptError);
+
+ rb_eRuntimeError = rb_define_class("RuntimeError", rb_eStandardError);
+ rb_eSecurityError = rb_define_class("SecurityError", rb_eStandardError);
+ rb_eNoMemError = rb_define_class("NoMemoryError", rb_eException);
+
+ syserr_tbl = st_init_numtable();
+ rb_eSystemCallError = rb_define_class("SystemCallError", rb_eStandardError);
+ rb_define_method(rb_eSystemCallError, "initialize", syserr_initialize, -1);
+ rb_define_method(rb_eSystemCallError, "errno", syserr_errno, 0);
+ rb_define_singleton_method(rb_eSystemCallError, "===", syserr_eqq, 1);
+
+ rb_mErrno = rb_define_module("Errno");
+ rb_define_singleton_method(rb_mErrno, "const_missing", errno_missing, 1);
+
+ rb_define_global_function("warn", rb_warn_m, 1);
+}
+
+void
+#ifdef HAVE_STDARG_PROTOTYPES
+rb_raise(VALUE exc, const char *fmt, ...)
+#else
+rb_raise(exc, fmt, va_alist)
+ VALUE exc;
+ const char *fmt;
+ va_dcl
+#endif
+{
+ va_list args;
+ char buf[BUFSIZ];
+
+ va_init_list(args,fmt);
+ vsnprintf(buf, BUFSIZ, fmt, args);
+ va_end(args);
+ rb_exc_raise(rb_exc_new2(exc, buf));
+}
+
+void
+#ifdef HAVE_STDARG_PROTOTYPES
+rb_loaderror(const char *fmt, ...)
+#else
+rb_loaderror(fmt, va_alist)
+ const char *fmt;
+ va_dcl
+#endif
+{
+ va_list args;
+ char buf[BUFSIZ];
+
+ va_init_list(args, fmt);
+ vsnprintf(buf, BUFSIZ, fmt, args);
+ va_end(args);
+ rb_exc_raise(rb_exc_new2(rb_eLoadError, buf));
+}
+
+void
+rb_notimplement()
+{
+ rb_raise(rb_eNotImpError,
+ "The %s() function is unimplemented on this machine",
+ rb_id2name(ruby_frame->callee));
+}
+
+void
+#ifdef HAVE_STDARG_PROTOTYPES
+rb_fatal(const char *fmt, ...)
+#else
+rb_fatal(fmt, va_alist)
+ const char *fmt;
+ va_dcl
+#endif
+{
+ va_list args;
+ char buf[BUFSIZ];
+
+ va_init_list(args, fmt);
+ vsnprintf(buf, BUFSIZ, fmt, args);
+ va_end(args);
+
+ ruby_in_eval = 0;
+ rb_exc_fatal(rb_exc_new2(rb_eFatal, buf));
+}
+
+void
+rb_sys_fail(mesg)
+ const char *mesg;
+{
+ extern int errno;
+ int n = errno;
+ VALUE arg;
+
+ errno = 0;
+ if (n == 0) {
+ rb_bug("rb_sys_fail(%s) - errno == 0", mesg ? mesg : "");
+ }
+
+ arg = mesg ? rb_str_new2(mesg) : Qnil;
+ rb_exc_raise(rb_class_new_instance(1, &arg, get_syserr(n)));
+}
+
+void
+#ifdef HAVE_STDARG_PROTOTYPES
+rb_sys_warning(const char *fmt, ...)
+#else
+rb_sys_warning(fmt, va_alist)
+ const char *fmt;
+ va_dcl
+#endif
+{
+ char buf[BUFSIZ];
+ va_list args;
+ int errno_save;
+
+ errno_save = errno;
+
+ if (!RTEST(ruby_verbose)) return;
+
+ snprintf(buf, BUFSIZ, "warning: %s", fmt);
+ snprintf(buf+strlen(buf), BUFSIZ-strlen(buf), ": %s", strerror(errno_save));
+
+ va_init_list(args, fmt);
+ warn_print(buf, args);
+ va_end(args);
+ errno = errno_save;
+}
+
+void
+rb_load_fail(path)
+ const char *path;
+{
+ rb_loaderror("%s -- %s", strerror(errno), path);
+}
+
+void
+rb_error_frozen(what)
+ const char *what;
+{
+ rb_raise(rb_eRuntimeError, "can't modify frozen %s", what);
+}
+
+void
+rb_check_frozen(obj)
+ VALUE obj;
+{
+ if (OBJ_FROZEN(obj)) rb_error_frozen(rb_obj_classname(obj));
+}
+
+void
+Init_syserr()
+{
+#ifdef EPERM
+ set_syserr(EPERM, "EPERM");
+#endif
+#ifdef ENOENT
+ set_syserr(ENOENT, "ENOENT");
+#endif
+#ifdef ESRCH
+ set_syserr(ESRCH, "ESRCH");
+#endif
+#ifdef EINTR
+ set_syserr(EINTR, "EINTR");
+#endif
+#ifdef EIO
+ set_syserr(EIO, "EIO");
+#endif
+#ifdef ENXIO
+ set_syserr(ENXIO, "ENXIO");
+#endif
+#ifdef E2BIG
+ set_syserr(E2BIG, "E2BIG");
+#endif
+#ifdef ENOEXEC
+ set_syserr(ENOEXEC, "ENOEXEC");
+#endif
+#ifdef EBADF
+ set_syserr(EBADF, "EBADF");
+#endif
+#ifdef ECHILD
+ set_syserr(ECHILD, "ECHILD");
+#endif
+#ifdef EAGAIN
+ set_syserr(EAGAIN, "EAGAIN");
+#endif
+#ifdef ENOMEM
+ set_syserr(ENOMEM, "ENOMEM");
+#endif
+#ifdef EACCES
+ set_syserr(EACCES, "EACCES");
+#endif
+#ifdef EFAULT
+ set_syserr(EFAULT, "EFAULT");
+#endif
+#ifdef ENOTBLK
+ set_syserr(ENOTBLK, "ENOTBLK");
+#endif
+#ifdef EBUSY
+ set_syserr(EBUSY, "EBUSY");
+#endif
+#ifdef EEXIST
+ set_syserr(EEXIST, "EEXIST");
+#endif
+#ifdef EXDEV
+ set_syserr(EXDEV, "EXDEV");
+#endif
+#ifdef ENODEV
+ set_syserr(ENODEV, "ENODEV");
+#endif
+#ifdef ENOTDIR
+ set_syserr(ENOTDIR, "ENOTDIR");
+#endif
+#ifdef EISDIR
+ set_syserr(EISDIR, "EISDIR");
+#endif
+#ifdef EINVAL
+ set_syserr(EINVAL, "EINVAL");
+#endif
+#ifdef ENFILE
+ set_syserr(ENFILE, "ENFILE");
+#endif
+#ifdef EMFILE
+ set_syserr(EMFILE, "EMFILE");
+#endif
+#ifdef ENOTTY
+ set_syserr(ENOTTY, "ENOTTY");
+#endif
+#ifdef ETXTBSY
+ set_syserr(ETXTBSY, "ETXTBSY");
+#endif
+#ifdef EFBIG
+ set_syserr(EFBIG, "EFBIG");
+#endif
+#ifdef ENOSPC
+ set_syserr(ENOSPC, "ENOSPC");
+#endif
+#ifdef ESPIPE
+ set_syserr(ESPIPE, "ESPIPE");
+#endif
+#ifdef EROFS
+ set_syserr(EROFS, "EROFS");
+#endif
+#ifdef EMLINK
+ set_syserr(EMLINK, "EMLINK");
+#endif
+#ifdef EPIPE
+ set_syserr(EPIPE, "EPIPE");
+#endif
+#ifdef EDOM
+ set_syserr(EDOM, "EDOM");
+#endif
+#ifdef ERANGE
+ set_syserr(ERANGE, "ERANGE");
+#endif
+#ifdef EDEADLK
+ set_syserr(EDEADLK, "EDEADLK");
+#endif
+#ifdef ENAMETOOLONG
+ set_syserr(ENAMETOOLONG, "ENAMETOOLONG");
+#endif
+#ifdef ENOLCK
+ set_syserr(ENOLCK, "ENOLCK");
+#endif
+#ifdef ENOSYS
+ set_syserr(ENOSYS, "ENOSYS");
+#endif
+#ifdef ENOTEMPTY
+ set_syserr(ENOTEMPTY, "ENOTEMPTY");
+#endif
+#ifdef ELOOP
+ set_syserr(ELOOP, "ELOOP");
+#endif
+#ifdef EWOULDBLOCK
+ set_syserr(EWOULDBLOCK, "EWOULDBLOCK");
+#endif
+#ifdef ENOMSG
+ set_syserr(ENOMSG, "ENOMSG");
+#endif
+#ifdef EIDRM
+ set_syserr(EIDRM, "EIDRM");
+#endif
+#ifdef ECHRNG
+ set_syserr(ECHRNG, "ECHRNG");
+#endif
+#ifdef EL2NSYNC
+ set_syserr(EL2NSYNC, "EL2NSYNC");
+#endif
+#ifdef EL3HLT
+ set_syserr(EL3HLT, "EL3HLT");
+#endif
+#ifdef EL3RST
+ set_syserr(EL3RST, "EL3RST");
+#endif
+#ifdef ELNRNG
+ set_syserr(ELNRNG, "ELNRNG");
+#endif
+#ifdef EUNATCH
+ set_syserr(EUNATCH, "EUNATCH");
+#endif
+#ifdef ENOCSI
+ set_syserr(ENOCSI, "ENOCSI");
+#endif
+#ifdef EL2HLT
+ set_syserr(EL2HLT, "EL2HLT");
+#endif
+#ifdef EBADE
+ set_syserr(EBADE, "EBADE");
+#endif
+#ifdef EBADR
+ set_syserr(EBADR, "EBADR");
+#endif
+#ifdef EXFULL
+ set_syserr(EXFULL, "EXFULL");
+#endif
+#ifdef ENOANO
+ set_syserr(ENOANO, "ENOANO");
+#endif
+#ifdef EBADRQC
+ set_syserr(EBADRQC, "EBADRQC");
+#endif
+#ifdef EBADSLT
+ set_syserr(EBADSLT, "EBADSLT");
+#endif
+#ifdef EDEADLOCK
+ set_syserr(EDEADLOCK, "EDEADLOCK");
+#endif
+#ifdef EBFONT
+ set_syserr(EBFONT, "EBFONT");
+#endif
+#ifdef ENOSTR
+ set_syserr(ENOSTR, "ENOSTR");
+#endif
+#ifdef ENODATA
+ set_syserr(ENODATA, "ENODATA");
+#endif
+#ifdef ETIME
+ set_syserr(ETIME, "ETIME");
+#endif
+#ifdef ENOSR
+ set_syserr(ENOSR, "ENOSR");
+#endif
+#ifdef ENONET
+ set_syserr(ENONET, "ENONET");
+#endif
+#ifdef ENOPKG
+ set_syserr(ENOPKG, "ENOPKG");
+#endif
+#ifdef EREMOTE
+ set_syserr(EREMOTE, "EREMOTE");
+#endif
+#ifdef ENOLINK
+ set_syserr(ENOLINK, "ENOLINK");
+#endif
+#ifdef EADV
+ set_syserr(EADV, "EADV");
+#endif
+#ifdef ESRMNT
+ set_syserr(ESRMNT, "ESRMNT");
+#endif
+#ifdef ECOMM
+ set_syserr(ECOMM, "ECOMM");
+#endif
+#ifdef EPROTO
+ set_syserr(EPROTO, "EPROTO");
+#endif
+#ifdef EMULTIHOP
+ set_syserr(EMULTIHOP, "EMULTIHOP");
+#endif
+#ifdef EDOTDOT
+ set_syserr(EDOTDOT, "EDOTDOT");
+#endif
+#ifdef EBADMSG
+ set_syserr(EBADMSG, "EBADMSG");
+#endif
+#ifdef EOVERFLOW
+ set_syserr(EOVERFLOW, "EOVERFLOW");
+#endif
+#ifdef ENOTUNIQ
+ set_syserr(ENOTUNIQ, "ENOTUNIQ");
+#endif
+#ifdef EBADFD
+ set_syserr(EBADFD, "EBADFD");
+#endif
+#ifdef EREMCHG
+ set_syserr(EREMCHG, "EREMCHG");
+#endif
+#ifdef ELIBACC
+ set_syserr(ELIBACC, "ELIBACC");
+#endif
+#ifdef ELIBBAD
+ set_syserr(ELIBBAD, "ELIBBAD");
+#endif
+#ifdef ELIBSCN
+ set_syserr(ELIBSCN, "ELIBSCN");
+#endif
+#ifdef ELIBMAX
+ set_syserr(ELIBMAX, "ELIBMAX");
+#endif
+#ifdef ELIBEXEC
+ set_syserr(ELIBEXEC, "ELIBEXEC");
+#endif
+#ifdef EILSEQ
+ set_syserr(EILSEQ, "EILSEQ");
+#endif
+#ifdef ERESTART
+ set_syserr(ERESTART, "ERESTART");
+#endif
+#ifdef ESTRPIPE
+ set_syserr(ESTRPIPE, "ESTRPIPE");
+#endif
+#ifdef EUSERS
+ set_syserr(EUSERS, "EUSERS");
+#endif
+#ifdef ENOTSOCK
+ set_syserr(ENOTSOCK, "ENOTSOCK");
+#endif
+#ifdef EDESTADDRREQ
+ set_syserr(EDESTADDRREQ, "EDESTADDRREQ");
+#endif
+#ifdef EMSGSIZE
+ set_syserr(EMSGSIZE, "EMSGSIZE");
+#endif
+#ifdef EPROTOTYPE
+ set_syserr(EPROTOTYPE, "EPROTOTYPE");
+#endif
+#ifdef ENOPROTOOPT
+ set_syserr(ENOPROTOOPT, "ENOPROTOOPT");
+#endif
+#ifdef EPROTONOSUPPORT
+ set_syserr(EPROTONOSUPPORT, "EPROTONOSUPPORT");
+#endif
+#ifdef ESOCKTNOSUPPORT
+ set_syserr(ESOCKTNOSUPPORT, "ESOCKTNOSUPPORT");
+#endif
+#ifdef EOPNOTSUPP
+ set_syserr(EOPNOTSUPP, "EOPNOTSUPP");
+#endif
+#ifdef EPFNOSUPPORT
+ set_syserr(EPFNOSUPPORT, "EPFNOSUPPORT");
+#endif
+#ifdef EAFNOSUPPORT
+ set_syserr(EAFNOSUPPORT, "EAFNOSUPPORT");
+#endif
+#ifdef EADDRINUSE
+ set_syserr(EADDRINUSE, "EADDRINUSE");
+#endif
+#ifdef EADDRNOTAVAIL
+ set_syserr(EADDRNOTAVAIL, "EADDRNOTAVAIL");
+#endif
+#ifdef ENETDOWN
+ set_syserr(ENETDOWN, "ENETDOWN");
+#endif
+#ifdef ENETUNREACH
+ set_syserr(ENETUNREACH, "ENETUNREACH");
+#endif
+#ifdef ENETRESET
+ set_syserr(ENETRESET, "ENETRESET");
+#endif
+#ifdef ECONNABORTED
+ set_syserr(ECONNABORTED, "ECONNABORTED");
+#endif
+#ifdef ECONNRESET
+ set_syserr(ECONNRESET, "ECONNRESET");
+#endif
+#ifdef ENOBUFS
+ set_syserr(ENOBUFS, "ENOBUFS");
+#endif
+#ifdef EISCONN
+ set_syserr(EISCONN, "EISCONN");
+#endif
+#ifdef ENOTCONN
+ set_syserr(ENOTCONN, "ENOTCONN");
+#endif
+#ifdef ESHUTDOWN
+ set_syserr(ESHUTDOWN, "ESHUTDOWN");
+#endif
+#ifdef ETOOMANYREFS
+ set_syserr(ETOOMANYREFS, "ETOOMANYREFS");
+#endif
+#ifdef ETIMEDOUT
+ set_syserr(ETIMEDOUT, "ETIMEDOUT");
+#endif
+#ifdef ECONNREFUSED
+ set_syserr(ECONNREFUSED, "ECONNREFUSED");
+#endif
+#ifdef EHOSTDOWN
+ set_syserr(EHOSTDOWN, "EHOSTDOWN");
+#endif
+#ifdef EHOSTUNREACH
+ set_syserr(EHOSTUNREACH, "EHOSTUNREACH");
+#endif
+#ifdef EALREADY
+ set_syserr(EALREADY, "EALREADY");
+#endif
+#ifdef EINPROGRESS
+ set_syserr(EINPROGRESS, "EINPROGRESS");
+#endif
+#ifdef ESTALE
+ set_syserr(ESTALE, "ESTALE");
+#endif
+#ifdef EUCLEAN
+ set_syserr(EUCLEAN, "EUCLEAN");
+#endif
+#ifdef ENOTNAM
+ set_syserr(ENOTNAM, "ENOTNAM");
+#endif
+#ifdef ENAVAIL
+ set_syserr(ENAVAIL, "ENAVAIL");
+#endif
+#ifdef EISNAM
+ set_syserr(EISNAM, "EISNAM");
+#endif
+#ifdef EREMOTEIO
+ set_syserr(EREMOTEIO, "EREMOTEIO");
+#endif
+#ifdef EDQUOT
+ set_syserr(EDQUOT, "EDQUOT");
+#endif
+ eNOERROR = set_syserr(0, "NOERROR");
+}
+
+static void
+err_append(s)
+ const char *s;
+{
+ extern VALUE ruby_errinfo;
+
+ if (ruby_in_eval) {
+ if (NIL_P(ruby_errinfo)) {
+ ruby_errinfo = rb_exc_new2(rb_eSyntaxError, s);
+ }
+ else {
+ VALUE str = rb_obj_as_string(ruby_errinfo);
+
+ rb_str_cat2(str, "\n");
+ rb_str_cat2(str, s);
+ ruby_errinfo = rb_exc_new3(rb_eSyntaxError, str);
+ }
+ }
+ else {
+ rb_write_error(s);
+ rb_write_error("\n");
+ }
+}
+/**********************************************************************
+ euc_jp.c - Oniguruma (regular expression library)
+**********************************************************************/
+/*-
+ * Copyright (c) 2002-2005 K.Kosako <sndgk393 AT ybb DOT ne DOT jp>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "regenc.h"
+
+#define eucjp_islead(c) ((UChar )((c) - 0xa1) > 0xfe - 0xa1)
+
+static int EncLen_EUCJP[] = {
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 3,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1
+};
+
+static int
+eucjp_mbc_enc_len(const UChar* p)
+{
+ return EncLen_EUCJP[*p];
+}
+
+static OnigCodePoint
+eucjp_mbc_to_code(const UChar* p, const UChar* end)
+{
+ int c, i, len;
+ OnigCodePoint n;
+
+ len = enc_len(ONIG_ENCODING_EUC_JP, p);
+ n = (OnigCodePoint )*p++;
+ if (len == 1) return n;
+
+ for (i = 1; i < len; i++) {
+ if (p >= end) break;
+ c = *p++;
+ n <<= 8; n += c;
+ }
+ return n;
+}
+
+static int
+eucjp_code_to_mbclen(OnigCodePoint code)
+{
+ if (ONIGENC_IS_CODE_ASCII(code)) return 1;
+ else if ((code & 0xff0000) != 0) return 3;
+ else if ((code & 0xff00) != 0) return 2;
+ else return 0;
+}
+
+#if 0
+static int
+eucjp_code_to_mbc_first(OnigCodePoint code)
+{
+ int first;
+
+ if ((code & 0xff0000) != 0) {
+ first = (code >> 16) & 0xff;
+ }
+ else if ((code & 0xff00) != 0) {
+ first = (code >> 8) & 0xff;
+ }
+ else {
+ return (int )code;
+ }
+ return first;
+}
+#endif
+
+static int
+eucjp_code_to_mbc(OnigCodePoint code, UChar *buf)
+{
+ UChar *p = buf;
+
+ if ((code & 0xff0000) != 0) *p++ = (UChar )(((code >> 16) & 0xff));
+ if ((code & 0xff00) != 0) *p++ = (UChar )(((code >> 8) & 0xff));
+ *p++ = (UChar )(code & 0xff);
+
+#if 1
+ if (enc_len(ONIG_ENCODING_EUC_JP, buf) != (p - buf))
+ return ONIGENCERR_INVALID_WIDE_CHAR_VALUE;
+#endif
+ return p - buf;
+}
+
+static int
+eucjp_mbc_to_normalize(OnigAmbigType flag,
+ const UChar** pp, const UChar* end, UChar* lower)
+{
+ int len;
+ const UChar* p = *pp;
+
+ if (ONIGENC_IS_MBC_ASCII(p)) {
+ if ((flag & ONIGENC_AMBIGUOUS_MATCH_ASCII_CASE) != 0) {
+ *lower = ONIGENC_ASCII_CODE_TO_LOWER_CASE(*p);
+ }
+ else {
+ *lower = *p;
+ }
+
+ (*pp)++;
+ return 1;
+ }
+ else {
+ len = enc_len(ONIG_ENCODING_EUC_JP, p);
+ if (lower != p) {
+ int i;
+ for (i = 0; i < len; i++) {
+ *lower++ = *p++;
+ }
+ }
+ (*pp) += len;
+ return len; /* return byte length of converted char to lower */
+ }
+}
+
+static int
+eucjp_is_mbc_ambiguous(OnigAmbigType flag, const UChar** pp, const UChar* end)
+{
+ return onigenc_mbn_is_mbc_ambiguous(ONIG_ENCODING_EUC_JP, flag, pp, end);
+}
+
+static int
+eucjp_is_code_ctype(OnigCodePoint code, unsigned int ctype)
+{
+ if ((ctype & ONIGENC_CTYPE_WORD) != 0) {
+ if (code < 128)
+ return ONIGENC_IS_ASCII_CODE_CTYPE(code, ctype);
+ else
+ return (eucjp_code_to_mbclen(code) > 1 ? TRUE : FALSE);
+
+ ctype &= ~ONIGENC_CTYPE_WORD;
+ if (ctype == 0) return FALSE;
+ }
+
+ if (code < 128)
+ return ONIGENC_IS_ASCII_CODE_CTYPE(code, ctype);
+ else
+ return FALSE;
+}
+
+static UChar*
+eucjp_left_adjust_char_head(const UChar* start, const UChar* s)
+{
+ /* In this encoding
+ mb-trail bytes doesn't mix with single bytes.
+ */
+ const UChar *p;
+ int len;
+
+ if (s <= start) return (UChar* )s;
+ p = s;
+
+ while (!eucjp_islead(*p) && p > start) p--;
+ len = enc_len(ONIG_ENCODING_EUC_JP, p);
+ if (p + len > s) return (UChar* )p;
+ p += len;
+ return (UChar* )(p + ((s - p) & ~1));
+}
+
+static int
+eucjp_is_allowed_reverse_match(const UChar* s, const UChar* end)
+{
+ const UChar c = *s;
+ if (c <= 0x7e || c == 0x8e || c == 0x8f)
+ return TRUE;
+ else
+ return FALSE;
+}
+
+OnigEncodingType OnigEncodingEUC_JP = {
+ eucjp_mbc_enc_len,
+ "EUC-JP", /* name */
+ 3, /* max enc length */
+ 1, /* min enc length */
+ ONIGENC_AMBIGUOUS_MATCH_ASCII_CASE,
+ {
+ (OnigCodePoint )'\\' /* esc */
+ , (OnigCodePoint )ONIG_INEFFECTIVE_META_CHAR /* anychar '.' */
+ , (OnigCodePoint )ONIG_INEFFECTIVE_META_CHAR /* anytime '*' */
+ , (OnigCodePoint )ONIG_INEFFECTIVE_META_CHAR /* zero or one time '?' */
+ , (OnigCodePoint )ONIG_INEFFECTIVE_META_CHAR /* one or more time '+' */
+ , (OnigCodePoint )ONIG_INEFFECTIVE_META_CHAR /* anychar anytime */
+ },
+ onigenc_is_mbc_newline_0x0a,
+ eucjp_mbc_to_code,
+ eucjp_code_to_mbclen,
+ eucjp_code_to_mbc,
+ eucjp_mbc_to_normalize,
+ eucjp_is_mbc_ambiguous,
+ onigenc_ascii_get_all_pair_ambig_codes,
+ onigenc_nothing_get_all_comp_ambig_codes,
+ eucjp_is_code_ctype,
+ onigenc_not_support_get_ctype_code_range,
+ eucjp_left_adjust_char_head,
+ eucjp_is_allowed_reverse_match
+};
+/**********************************************************************
+
+ eval.c -
+
+ $Author: murphy $
+ $Date: 2005-11-05 04:33:55 +0100 (Sa, 05 Nov 2005) $
+ created at: Thu Jun 10 14:22:17 JST 1993
+
+ Copyright (C) 1993-2003 Yukihiro Matsumoto
+ Copyright (C) 2000 Network Applied Communication Laboratory, Inc.
+ Copyright (C) 2000 Information-technology Promotion Agency, Japan
+
+**********************************************************************/
+
+#include "ruby.h"
+#include "node.h"
+#include "env.h"
+#include "util.h"
+#include "rubysig.h"
+
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#ifndef EXIT_SUCCESS
+#define EXIT_SUCCESS 0
+#endif
+#ifndef EXIT_FAILURE
+#define EXIT_FAILURE 1
+#endif
+
+#include <stdio.h>
+#if defined(HAVE_GETCONTEXT) && defined(HAVE_SETCONTEXT)
+#include <ucontext.h>
+#define USE_CONTEXT
+#else
+#include <setjmp.h>
+#endif
+
+#include "st.h"
+#include "dln.h"
+
+#ifdef __APPLE__
+#include <crt_externs.h>
+#endif
+
+/* Make alloca work the best possible way. */
+#ifdef __GNUC__
+# ifndef atarist
+# ifndef alloca
+# define alloca __builtin_alloca
+# endif
+# endif /* atarist */
+#else
+# ifdef HAVE_ALLOCA_H
+# include <alloca.h>
+# else
+# ifdef _AIX
+ #pragma alloca
+# else
+# ifndef alloca /* predefined by HP cc +Olibcalls */
+void *alloca ();
+# endif
+# endif /* AIX */
+# endif /* HAVE_ALLOCA_H */
+#endif /* __GNUC__ */
+
+#ifdef HAVE_STDARG_PROTOTYPES
+#include <stdarg.h>
+#define va_init_list(a,b) va_start(a,b)
+#else
+#include <varargs.h>
+#define va_init_list(a,b) va_start(a)
+#endif
+
+#ifndef HAVE_STRING_H
+char *strrchr _((const char*,const char));
+#endif
+
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#ifdef __BEOS__
+#include <net/socket.h>
+#endif
+
+#ifdef __MACOS__
+#include "macruby_private.h"
+#endif
+
+#ifdef USE_CONTEXT
+typedef struct {
+ ucontext_t context;
+ volatile int status;
+} rb_jmpbuf_t[1];
+
+#undef longjmp
+#undef setjmp
+NORETURN(static void rb_jump_context(rb_jmpbuf_t, int));
+static inline void
+rb_jump_context(env, val)
+ rb_jmpbuf_t env;
+ int val;
+{
+ env->status = val;
+ setcontext(&env->context);
+ abort(); /* ensure noreturn */
+}
+#define longjmp(env, val) rb_jump_context(env, val)
+#define setjmp(j) ((j)->status = 0, getcontext(&(j)->context), (j)->status)
+#else
+typedef jmp_buf rb_jmpbuf_t;
+#ifndef setjmp
+#ifdef HAVE__SETJMP
+#define setjmp(env) _setjmp(env)
+#define longjmp(env,val) _longjmp(env,val)
+#endif
+#endif
+#endif
+
+#include <sys/types.h>
+#include <signal.h>
+#include <errno.h>
+
+#if defined(__VMS)
+#pragma nostandard
+#endif
+
+#ifdef HAVE_SYS_SELECT_H
+#include <sys/select.h>
+#endif
+
+#include <sys/stat.h>
+
+VALUE rb_cProc;
+static VALUE rb_cBinding;
+static VALUE proc_invoke _((VALUE,VALUE,VALUE,VALUE));
+static VALUE rb_f_binding _((VALUE));
+static void rb_f_END _((void));
+static VALUE rb_f_block_given_p _((void));
+static VALUE block_pass _((VALUE,NODE*));
+static VALUE rb_cMethod;
+static VALUE method_call _((int, VALUE*, VALUE));
+static VALUE rb_cUnboundMethod;
+static VALUE umethod_bind _((VALUE, VALUE));
+static VALUE rb_mod_define_method _((int, VALUE*, VALUE));
+NORETURN(static void rb_raise_jump _((VALUE)));
+static VALUE rb_make_exception _((int argc, VALUE *argv));
+
+static int scope_vmode;
+#define SCOPE_PUBLIC 0
+#define SCOPE_PRIVATE 1
+#define SCOPE_PROTECTED 2
+#define SCOPE_MODFUNC 5
+#define SCOPE_MASK 7
+#define SCOPE_SET(f) (scope_vmode=(f))
+#define SCOPE_TEST(f) (scope_vmode&(f))
+
+NODE* ruby_current_node;
+int ruby_safe_level = 0;
+/* safe-level:
+ 0 - strings from streams/environment/ARGV are tainted (default)
+ 1 - no dangerous operation by tainted value
+ 2 - process/file operations prohibited
+ 3 - all generated objects are tainted
+ 4 - no global (non-tainted) variable modification/no direct output
+*/
+
+static VALUE safe_getter _((void));
+static void safe_setter _((VALUE val));
+
+void
+rb_secure(level)
+ int level;
+{
+ if (level <= ruby_safe_level) {
+ if (ruby_frame->callee) {
+ rb_raise(rb_eSecurityError, "Insecure operation `%s' at level %d",
+ rb_id2name(ruby_frame->callee), ruby_safe_level);
+ }
+ else {
+ rb_raise(rb_eSecurityError, "Insecure operation at level %d", ruby_safe_level);
+ }
+ }
+}
+
+void
+rb_secure_update(obj)
+ VALUE obj;
+{
+ if (!OBJ_TAINTED(obj)) rb_secure(4);
+}
+
+void
+rb_check_safe_obj(x)
+ VALUE x;
+{
+ if (ruby_safe_level > 0 && OBJ_TAINTED(x)){
+ if (ruby_frame->callee) {
+ rb_raise(rb_eSecurityError, "Insecure operation - %s",
+ rb_id2name(ruby_frame->callee));
+ }
+ else {
+ rb_raise(rb_eSecurityError, "Insecure operation: -r");
+ }
+ }
+ rb_secure(4);
+}
+
+void
+rb_check_safe_str(x)
+ VALUE x;
+{
+ rb_check_safe_obj(x);
+ if (TYPE(x)!= T_STRING) {
+ rb_raise(rb_eTypeError, "wrong argument type %s (expected String)",
+ rb_obj_classname(x));
+ }
+}
+
+NORETURN(static void print_undef _((VALUE, ID)));
+static void
+print_undef(klass, id)
+ VALUE klass;
+ ID id;
+{
+ rb_name_error(id, "undefined method `%s' for %s `%s'",
+ rb_id2name(id),
+ (TYPE(klass) == T_MODULE) ? "module" : "class",
+ rb_class2name(klass));
+}
+
+static ID removed, singleton_removed, undefined, singleton_undefined;
+
+#define CACHE_SIZE 0x800
+#define CACHE_MASK 0x7ff
+#define EXPR1(c,m) ((((c)>>3)^(m))&CACHE_MASK)
+
+struct cache_entry { /* method hash table. */
+ ID mid; /* method's id */
+ ID mid0; /* method's original id */
+ VALUE klass; /* receiver's class */
+ VALUE origin; /* where method defined */
+ NODE *method;
+ int noex;
+};
+
+static struct cache_entry cache[CACHE_SIZE];
+static int ruby_running = 0;
+
+void
+rb_clear_cache()
+{
+ struct cache_entry *ent, *end;
+
+ if (!ruby_running) return;
+ ent = cache; end = ent + CACHE_SIZE;
+ while (ent < end) {
+ ent->mid = 0;
+ ent++;
+ }
+}
+
+static void
+rb_clear_cache_for_undef(klass, id)
+ VALUE klass;
+ ID id;
+{
+ struct cache_entry *ent, *end;
+
+ if (!ruby_running) return;
+ ent = cache; end = ent + CACHE_SIZE;
+ while (ent < end) {
+ if (ent->origin == klass && ent->mid == id) {
+ ent->mid = 0;
+ }
+ ent++;
+ }
+}
+
+static void
+rb_clear_cache_by_id(id)
+ ID id;
+{
+ struct cache_entry *ent, *end;
+
+ if (!ruby_running) return;
+ ent = cache; end = ent + CACHE_SIZE;
+ while (ent < end) {
+ if (ent->mid == id) {
+ ent->mid = 0;
+ }
+ ent++;
+ }
+}
+
+void
+rb_clear_cache_by_class(klass)
+ VALUE klass;
+{
+ struct cache_entry *ent, *end;
+
+ if (!ruby_running) return;
+ ent = cache; end = ent + CACHE_SIZE;
+ while (ent < end) {
+ if (ent->klass == klass || ent->origin == klass) {
+ ent->mid = 0;
+ }
+ ent++;
+ }
+}
+
+static ID init, eqq, each, aref, aset, match, missing;
+static ID added, singleton_added;
+static ID __id__, __send__, respond_to;
+
+void
+rb_add_method(klass, mid, node, noex)
+ VALUE klass;
+ ID mid;
+ NODE *node;
+ int noex;
+{
+ NODE *body;
+
+ if (NIL_P(klass)) klass = rb_cObject;
+ if (ruby_safe_level >= 4 && (klass == rb_cObject || !OBJ_TAINTED(klass))) {
+ rb_raise(rb_eSecurityError, "Insecure: can't define method");
+ }
+ if (!FL_TEST(klass, FL_SINGLETON) &&
+ node && nd_type(node) != NODE_ZSUPER &&
+ (mid == rb_intern("initialize" )|| mid == rb_intern("initialize_copy"))) {
+ noex = NOEX_PRIVATE | noex;
+ }
+ else if (FL_TEST(klass, FL_SINGLETON) && node && nd_type(node) == NODE_CFUNC &&
+ mid == rb_intern("allocate")) {
+ rb_warn("defining %s.allocate is deprecated; use rb_define_alloc_func()",
+ rb_class2name(rb_iv_get(klass, "__attached__")));
+ mid = ID_ALLOCATOR;
+ }
+ if (OBJ_FROZEN(klass)) rb_error_frozen("class/module");
+ rb_clear_cache_by_id(mid);
+ body = NEW_METHOD(node, noex);
+ st_insert(RCLASS(klass)->m_tbl, mid, (st_data_t)body);
+ if (node && mid != ID_ALLOCATOR && ruby_running) {
+ if (FL_TEST(klass, FL_SINGLETON)) {
+ rb_funcall(rb_iv_get(klass, "__attached__"), singleton_added, 1, ID2SYM(mid));
+ }
+ else {
+ rb_funcall(klass, added, 1, ID2SYM(mid));
+ }
+ }
+}
+
+void
+rb_define_alloc_func(klass, func)
+ VALUE klass;
+ VALUE (*func) _((VALUE));
+{
+ Check_Type(klass, T_CLASS);
+ rb_add_method(CLASS_OF(klass), ID_ALLOCATOR, NEW_CFUNC(func, 0), NOEX_PRIVATE);
+}
+
+void
+rb_undef_alloc_func(klass)
+ VALUE klass;
+{
+ Check_Type(klass, T_CLASS);
+ rb_add_method(CLASS_OF(klass), ID_ALLOCATOR, 0, NOEX_UNDEF);
+}
+
+static NODE*
+search_method(klass, id, origin)
+ VALUE klass, *origin;
+ ID id;
+{
+ NODE *body;
+
+ if (!klass) return 0;
+ while (!st_lookup(RCLASS(klass)->m_tbl, id, (st_data_t *)&body)) {
+ klass = RCLASS(klass)->super;
+ if (!klass) return 0;
+ }
+
+ if (origin) *origin = klass;
+ return body;
+}
+
+static NODE*
+rb_get_method_body(klassp, idp, noexp)
+ VALUE *klassp;
+ ID *idp;
+ int *noexp;
+{
+ ID id = *idp;
+ VALUE klass = *klassp;
+ VALUE origin;
+ NODE * volatile body;
+ struct cache_entry *ent;
+
+ if ((body = search_method(klass, id, &origin)) == 0 || !body->nd_body) {
+ /* store empty info in cache */
+ ent = cache + EXPR1(klass, id);
+ ent->klass = klass;
+ ent->origin = klass;
+ ent->mid = ent->mid0 = id;
+ ent->noex = 0;
+ ent->method = 0;
+
+ return 0;
+ }
+
+ if (ruby_running) {
+ /* store in cache */
+ ent = cache + EXPR1(klass, id);
+ ent->klass = klass;
+ ent->noex = body->nd_noex;
+ if (noexp) *noexp = body->nd_noex;
+ body = body->nd_body;
+ if (nd_type(body) == NODE_FBODY) {
+ ent->mid = id;
+ *klassp = body->nd_orig;
+ ent->origin = body->nd_orig;
+ *idp = ent->mid0 = body->nd_mid;
+ body = ent->method = body->nd_head;
+ }
+ else {
+ *klassp = origin;
+ ent->origin = origin;
+ ent->mid = ent->mid0 = id;
+ ent->method = body;
+ }
+ }
+ else {
+ if (noexp) *noexp = body->nd_noex;
+ body = body->nd_body;
+ if (nd_type(body) == NODE_FBODY) {
+ *klassp = body->nd_orig;
+ *idp = body->nd_mid;
+ body = body->nd_head;
+ }
+ else {
+ *klassp = origin;
+ }
+ }
+
+ return body;
+}
+
+NODE*
+rb_method_node(klass, id)
+ VALUE klass;
+ ID id;
+{
+ int noex;
+ struct cache_entry *ent;
+
+ ent = cache + EXPR1(klass, id);
+ if (ent->mid == id && ent->klass == klass && ent->method){
+ return ent->method;
+ }
+
+ return rb_get_method_body(&klass, &id, &noex);
+}
+
+static void
+remove_method(klass, mid)
+ VALUE klass;
+ ID mid;
+{
+ NODE *body;
+
+ if (klass == rb_cObject) {
+ rb_secure(4);
+ }
+ if (ruby_safe_level >= 4 && !OBJ_TAINTED(klass)) {
+ rb_raise(rb_eSecurityError, "Insecure: can't remove method");
+ }
+ if (OBJ_FROZEN(klass)) rb_error_frozen("class/module");
+ if (mid == __id__ || mid == __send__ || mid == init) {
+ rb_warn("removing `%s' may cause serious problem", rb_id2name(mid));
+ }
+ if (!st_delete(RCLASS(klass)->m_tbl, &mid, (st_data_t *)&body) ||
+ !body->nd_body) {
+ rb_name_error(mid, "method `%s' not defined in %s",
+ rb_id2name(mid), rb_class2name(klass));
+ }
+ rb_clear_cache_for_undef(klass, mid);
+ if (FL_TEST(klass, FL_SINGLETON)) {
+ rb_funcall(rb_iv_get(klass, "__attached__"), singleton_removed, 1, ID2SYM(mid));
+ }
+ else {
+ rb_funcall(klass, removed, 1, ID2SYM(mid));
+ }
+}
+
+void
+rb_remove_method(klass, name)
+ VALUE klass;
+ const char *name;
+{
+ remove_method(klass, rb_intern(name));
+}
+
+/*
+ * call-seq:
+ * remove_method(symbol) => self
+ *
+ * Removes the method identified by _symbol_ from the current
+ * class. For an example, see <code>Module.undef_method</code>.
+ */
+
+static VALUE
+rb_mod_remove_method(argc, argv, mod)
+ int argc;
+ VALUE *argv;
+ VALUE mod;
+{
+ int i;
+
+ for (i=0; i<argc; i++) {
+ remove_method(mod, rb_to_id(argv[i]));
+ }
+ return mod;
+}
+
+#undef rb_disable_super
+#undef rb_enable_super
+
+void
+rb_disable_super(klass, name)
+ VALUE klass;
+ const char *name;
+{
+ /* obsolete - no use */
+}
+
+void
+rb_enable_super(klass, name)
+ VALUE klass;
+ const char *name;
+{
+ rb_warning("rb_enable_super() is obsolete");
+}
+
+static void
+rb_export_method(klass, name, noex)
+ VALUE klass;
+ ID name;
+ ID noex;
+{
+ NODE *body;
+ VALUE origin;
+
+ if (klass == rb_cObject) {
+ rb_secure(4);
+ }
+ body = search_method(klass, name, &origin);
+ if (!body && TYPE(klass) == T_MODULE) {
+ body = search_method(rb_cObject, name, &origin);
+ }
+ if (!body || !body->nd_body) {
+ print_undef(klass, name);
+ }
+ if (body->nd_noex != noex) {
+ if (klass == origin) {
+ body->nd_noex = noex;
+ }
+ else {
+ rb_add_method(klass, name, NEW_ZSUPER(), noex);
+ }
+ }
+}
+
+int
+rb_method_boundp(klass, id, ex)
+ VALUE klass;
+ ID id;
+ int ex;
+{
+ struct cache_entry *ent;
+ int noex;
+
+ /* is it in the method cache? */
+ ent = cache + EXPR1(klass, id);
+ if (ent->mid == id && ent->klass == klass) {
+ if (ex && (ent->noex & NOEX_PRIVATE))
+ return Qfalse;
+ if (!ent->method) return Qfalse;
+ return Qtrue;
+ }
+ if (rb_get_method_body(&klass, &id, &noex)) {
+ if (ex && (noex & NOEX_PRIVATE))
+ return Qfalse;
+ return Qtrue;
+ }
+ return Qfalse;
+}
+
+void
+rb_attr(klass, id, read, write, ex)
+ VALUE klass;
+ ID id;
+ int read, write, ex;
+{
+ const char *name;
+ char *buf;
+ ID attriv;
+ int noex;
+
+ if (!ex) noex = NOEX_PUBLIC;
+ else {
+ if (SCOPE_TEST(SCOPE_PRIVATE)) {
+ noex = NOEX_PRIVATE;
+ rb_warning((scope_vmode == SCOPE_MODFUNC) ?
+ "attribute accessor as module_function" :
+ "private attribute?");
+ }
+ else if (SCOPE_TEST(SCOPE_PROTECTED)) {
+ noex = NOEX_PROTECTED;
+ }
+ else {
+ noex = NOEX_PUBLIC;
+ }
+ }
+
+ if (!rb_is_local_id(id) && !rb_is_const_id(id)) {
+ rb_name_error(id, "invalid attribute name `%s'", rb_id2name(id));
+ }
+ name = rb_id2name(id);
+ if (!name) {
+ rb_raise(rb_eArgError, "argument needs to be symbol or string");
+ }
+ buf = ALLOCA_N(char,strlen(name)+2);
+ sprintf(buf, "@%s", name);
+ attriv = rb_intern(buf);
+ if (read) {
+ rb_add_method(klass, id, NEW_IVAR(attriv), noex);
+ }
+ if (write) {
+ rb_add_method(klass, rb_id_attrset(id), NEW_ATTRSET(attriv), noex);
+ }
+}
+
+VALUE ruby_errinfo = Qnil;
+extern int ruby_nerrs;
+
+static VALUE rb_eLocalJumpError;
+static VALUE rb_eSysStackError;
+
+extern VALUE ruby_top_self;
+
+struct FRAME *ruby_frame;
+struct SCOPE *ruby_scope;
+static struct FRAME *top_frame;
+static struct SCOPE *top_scope;
+
+static unsigned long frame_unique = 0;
+
+#define PUSH_FRAME() do { \
+ struct FRAME _frame; \
+ _frame.prev = ruby_frame; \
+ _frame.tmp = 0; \
+ _frame.node = ruby_current_node; \
+ _frame.iter = ruby_iter->iter; \
+ _frame.argc = 0; \
+ _frame.flags = 0; \
+ _frame.uniq = frame_unique++; \
+ ruby_frame = &_frame
+
+#define POP_FRAME() \
+ ruby_current_node = _frame.node; \
+ ruby_frame = _frame.prev; \
+} while (0)
+
+struct BLOCK {
+ NODE *var;
+ NODE *body;
+ VALUE self;
+ struct FRAME frame;
+ struct SCOPE *scope;
+ VALUE klass;
+ NODE *cref;
+ int iter;
+ int vmode;
+ int flags;
+ int uniq;
+ struct RVarmap *dyna_vars;
+ VALUE orig_thread;
+ VALUE wrapper;
+ VALUE block_obj;
+ struct BLOCK *outer;
+ struct BLOCK *prev;
+};
+
+#define BLOCK_D_SCOPE 1
+#define BLOCK_LAMBDA 2
+#define BLOCK_FROM_METHOD 4
+
+static struct BLOCK *ruby_block;
+static unsigned long block_unique = 0;
+
+#define PUSH_BLOCK(v,b) do { \
+ struct BLOCK _block; \
+ _block.var = (v); \
+ _block.body = (b); \
+ _block.self = self; \
+ _block.frame = *ruby_frame; \
+ _block.klass = ruby_class; \
+ _block.cref = ruby_cref; \
+ _block.frame.node = ruby_current_node;\
+ _block.scope = ruby_scope; \
+ _block.prev = ruby_block; \
+ _block.outer = ruby_block; \
+ _block.iter = ruby_iter->iter; \
+ _block.vmode = scope_vmode; \
+ _block.flags = BLOCK_D_SCOPE; \
+ _block.dyna_vars = ruby_dyna_vars; \
+ _block.wrapper = ruby_wrapper; \
+ _block.block_obj = 0; \
+ _block.uniq = (b)?block_unique++:0; \
+ if (b) { \
+ prot_tag->blkid = _block.uniq; \
+ } \
+ ruby_block = &_block
+
+#define POP_BLOCK() \
+ ruby_block = _block.prev; \
+} while (0)
+
+struct RVarmap *ruby_dyna_vars;
+#define PUSH_VARS() do { \
+ struct RVarmap * volatile _old; \
+ _old = ruby_dyna_vars; \
+ ruby_dyna_vars = 0
+
+#define POP_VARS() \
+ if (_old && (ruby_scope->flags & SCOPE_DONT_RECYCLE)) {\
+ if (RBASIC(_old)->flags) /* unless it's already recycled */ \
+ FL_SET(_old, DVAR_DONT_RECYCLE); \
+ }\
+ ruby_dyna_vars = _old; \
+} while (0)
+
+#define DVAR_DONT_RECYCLE FL_USER2
+
+static struct RVarmap*
+new_dvar(id, value, prev)
+ ID id;
+ VALUE value;
+ struct RVarmap *prev;
+{
+ NEWOBJ(vars, struct RVarmap);
+ OBJSETUP(vars, 0, T_VARMAP);
+ vars->id = id;
+ vars->val = value;
+ vars->next = prev;
+
+ return vars;
+}
+
+VALUE
+rb_dvar_defined(id)
+ ID id;
+{
+ struct RVarmap *vars = ruby_dyna_vars;
+
+ while (vars) {
+ if (vars->id == id) return Qtrue;
+ vars = vars->next;
+ }
+ return Qfalse;
+}
+
+VALUE
+rb_dvar_curr(id)
+ ID id;
+{
+ struct RVarmap *vars = ruby_dyna_vars;
+
+ while (vars) {
+ if (vars->id == 0) break;
+ if (vars->id == id) return Qtrue;
+ vars = vars->next;
+ }
+ return Qfalse;
+}
+
+VALUE
+rb_dvar_ref(id)
+ ID id;
+{
+ struct RVarmap *vars = ruby_dyna_vars;
+
+ while (vars) {
+ if (vars->id == id) {
+ return vars->val;
+ }
+ vars = vars->next;
+ }
+ return Qnil;
+}
+
+void
+rb_dvar_push(id, value)
+ ID id;
+ VALUE value;
+{
+ ruby_dyna_vars = new_dvar(id, value, ruby_dyna_vars);
+}
+
+static void
+dvar_asgn_internal(id, value, curr)
+ ID id;
+ VALUE value;
+ int curr;
+{
+ int n = 0;
+ struct RVarmap *vars = ruby_dyna_vars;
+
+ while (vars) {
+ if (curr && vars->id == 0) {
+ /* first null is a dvar header */
+ n++;
+ if (n == 2) break;
+ }
+ if (vars->id == id) {
+ vars->val = value;
+ return;
+ }
+ vars = vars->next;
+ }
+ if (!ruby_dyna_vars) {
+ ruby_dyna_vars = new_dvar(id, value, 0);
+ }
+ else {
+ vars = new_dvar(id, value, ruby_dyna_vars->next);
+ ruby_dyna_vars->next = vars;
+ }
+}
+
+static inline void
+dvar_asgn(id, value)
+ ID id;
+ VALUE value;
+{
+ dvar_asgn_internal(id, value, 0);
+}
+
+static inline void
+dvar_asgn_curr(id, value)
+ ID id;
+ VALUE value;
+{
+ dvar_asgn_internal(id, value, 1);
+}
+
+VALUE *
+rb_svar(cnt)
+ int cnt;
+{
+ struct RVarmap *vars = ruby_dyna_vars;
+ ID id;
+
+ if (!ruby_scope->local_tbl) return NULL;
+ if (cnt >= ruby_scope->local_tbl[0]) return NULL;
+ id = ruby_scope->local_tbl[cnt+1];
+ while (vars) {
+ if (vars->id == id) return &vars->val;
+ vars = vars->next;
+ }
+ if (ruby_scope->local_vars == 0) return NULL;
+ return &ruby_scope->local_vars[cnt];
+}
+
+struct iter {
+ int iter;
+ struct iter *prev;
+};
+static struct iter *ruby_iter;
+
+#define ITER_NOT 0
+#define ITER_PRE 1
+#define ITER_CUR 2
+
+#define PUSH_ITER(i) do { \
+ struct iter _iter; \
+ _iter.prev = ruby_iter; \
+ _iter.iter = (i); \
+ ruby_iter = &_iter
+
+#define POP_ITER() \
+ ruby_iter = _iter.prev; \
+} while (0)
+
+struct tag {
+ rb_jmpbuf_t buf;
+ struct FRAME *frame;
+ struct iter *iter;
+ VALUE tag;
+ VALUE retval;
+ struct SCOPE *scope;
+ VALUE dst;
+ struct tag *prev;
+ int blkid;
+};
+static struct tag *prot_tag;
+
+#define PUSH_TAG(ptag) do { \
+ struct tag _tag; \
+ _tag.retval = Qnil; \
+ _tag.frame = ruby_frame; \
+ _tag.iter = ruby_iter; \
+ _tag.prev = prot_tag; \
+ _tag.scope = ruby_scope; \
+ _tag.tag = ptag; \
+ _tag.dst = 0; \
+ _tag.blkid = 0; \
+ prot_tag = &_tag
+
+#define PROT_NONE Qfalse /* 0 */
+#define PROT_THREAD Qtrue /* 2 */
+#define PROT_FUNC INT2FIX(0) /* 1 */
+#define PROT_LOOP INT2FIX(1) /* 3 */
+#define PROT_LAMBDA INT2FIX(2) /* 5 */
+#define PROT_YIELD INT2FIX(3) /* 7 */
+#define PROT_TOP INT2FIX(4) /* 9 */
+
+#define EXEC_TAG() (FLUSH_REGISTER_WINDOWS, setjmp(prot_tag->buf))
+
+#define JUMP_TAG(st) do { \
+ ruby_frame = prot_tag->frame; \
+ ruby_iter = prot_tag->iter; \
+ longjmp(prot_tag->buf,(st)); \
+} while (0)
+
+#define POP_TAG() \
+ prot_tag = _tag.prev; \
+} while (0)
+
+#define TAG_DST() (_tag.dst == (VALUE)ruby_frame->uniq)
+
+#define TAG_RETURN 0x1
+#define TAG_BREAK 0x2
+#define TAG_NEXT 0x3
+#define TAG_RETRY 0x4
+#define TAG_REDO 0x5
+#define TAG_RAISE 0x6
+#define TAG_THROW 0x7
+#define TAG_FATAL 0x8
+#define TAG_CONTCALL 0x9
+#define TAG_THREAD 0xa
+#define TAG_MASK 0xf
+
+VALUE ruby_class;
+static VALUE ruby_wrapper; /* security wrapper */
+
+#define PUSH_CLASS(c) do { \
+ VALUE _class = ruby_class; \
+ ruby_class = (c)
+
+#define POP_CLASS() ruby_class = _class; \
+} while (0)
+
+static NODE *ruby_cref = 0;
+static NODE *top_cref;
+#define PUSH_CREF(c) ruby_cref = NEW_NODE(NODE_CREF,(c),0,ruby_cref)
+#define POP_CREF() ruby_cref = ruby_cref->nd_next
+
+#define PUSH_SCOPE() do { \
+ volatile int _vmode = scope_vmode; \
+ struct SCOPE * volatile _old; \
+ NEWOBJ(_scope, struct SCOPE); \
+ OBJSETUP(_scope, 0, T_SCOPE); \
+ _scope->local_tbl = 0; \
+ _scope->local_vars = 0; \
+ _scope->flags = 0; \
+ _old = ruby_scope; \
+ ruby_scope = _scope; \
+ scope_vmode = SCOPE_PUBLIC
+
+typedef struct thread * rb_thread_t;
+static rb_thread_t curr_thread = 0;
+static rb_thread_t main_thread;
+static void scope_dup _((struct SCOPE *));
+
+#define POP_SCOPE() \
+ if (ruby_scope->flags & SCOPE_DONT_RECYCLE) {\
+ if (_old) scope_dup(_old); \
+ } \
+ if (!(ruby_scope->flags & SCOPE_MALLOC)) {\
+ ruby_scope->local_vars = 0; \
+ ruby_scope->local_tbl = 0; \
+ if (!(ruby_scope->flags & SCOPE_DONT_RECYCLE) && \
+ ruby_scope != top_scope) { \
+ rb_gc_force_recycle((VALUE)ruby_scope);\
+ } \
+ } \
+ ruby_scope->flags |= SCOPE_NOSTACK; \
+ ruby_scope = _old; \
+ scope_vmode = _vmode; \
+} while (0)
+
+struct ruby_env {
+ struct ruby_env *prev;
+ struct FRAME *frame;
+ struct SCOPE *scope;
+ struct BLOCK *block;
+ struct iter *iter;
+ struct tag *tag;
+ NODE *cref;
+};
+
+static void push_thread_anchor _((struct ruby_env *));
+static void pop_thread_anchor _((struct ruby_env *));
+
+#define PUSH_THREAD_TAG() PUSH_TAG(PROT_THREAD); \
+ do { \
+ struct ruby_env _interp; \
+ push_thread_anchor(&_interp);
+#define POP_THREAD_TAG() \
+ pop_thread_anchor(&_interp); \
+ } while (0); \
+ POP_TAG()
+
+static VALUE rb_eval _((VALUE,NODE*));
+static VALUE eval _((VALUE,VALUE,VALUE,char*,int));
+static NODE *compile _((VALUE, char*, int));
+
+static VALUE rb_yield_0 _((VALUE, VALUE, VALUE, int, int));
+
+#define YIELD_LAMBDA_CALL 1
+#define YIELD_PROC_CALL 2
+#define YIELD_PUBLIC_DEF 4
+#define YIELD_FUNC_AVALUE 1
+#define YIELD_FUNC_SVALUE 2
+
+static VALUE rb_call _((VALUE,VALUE,ID,int,const VALUE*,int));
+static VALUE module_setup _((VALUE,NODE*));
+
+static VALUE massign _((VALUE,NODE*,VALUE,int));
+static void assign _((VALUE,NODE*,VALUE,int));
+
+typedef struct event_hook {
+ rb_event_hook_func_t func;
+ rb_event_t events;
+ struct event_hook *next;
+} rb_event_hook_t;
+
+static rb_event_hook_t *event_hooks;
+
+#define EXEC_EVENT_HOOK(event, node, self, id, klass) \
+ do { \
+ rb_event_hook_t *hook; \
+ \
+ for (hook = event_hooks; hook; hook = hook->next) { \
+ if (hook->events & event) \
+ (*hook->func)(event, node, self, id, klass); \
+ } \
+ } while (0)
+
+static VALUE trace_func = 0;
+static int tracing = 0;
+static void call_trace_func _((rb_event_t,NODE*,VALUE,ID,VALUE));
+
+#if 0
+#define SET_CURRENT_SOURCE() (ruby_sourcefile = ruby_current_node->nd_file, \
+ ruby_sourceline = nd_line(ruby_current_node))
+#else
+#define SET_CURRENT_SOURCE() ((void)0)
+#endif
+
+void
+ruby_set_current_source()
+{
+ if (ruby_current_node) {
+ ruby_sourcefile = ruby_current_node->nd_file;
+ ruby_sourceline = nd_line(ruby_current_node);
+ }
+}
+
+static void
+#ifdef HAVE_STDARG_PROTOTYPES
+warn_printf(const char *fmt, ...)
+#else
+warn_printf(fmt, va_alist)
+ const char *fmt;
+ va_dcl
+#endif
+{
+ char buf[BUFSIZ];
+ va_list args;
+
+ va_init_list(args, fmt);
+ vsnprintf(buf, BUFSIZ, fmt, args);
+ va_end(args);
+ rb_write_error(buf);
+}
+
+#define warn_print(x) rb_write_error(x)
+#define warn_print2(x,l) rb_write_error2(x,l)
+
+static void
+error_pos()
+{
+ ruby_set_current_source();
+ if (ruby_sourcefile) {
+ if (ruby_frame->callee) {
+ warn_printf("%s:%d:in `%s'", ruby_sourcefile, ruby_sourceline,
+ rb_id2name(ruby_frame->callee));
+ }
+ else if (ruby_sourceline == 0) {
+ warn_printf("%s", ruby_sourcefile);
+ }
+ else {
+ warn_printf("%s:%d", ruby_sourcefile, ruby_sourceline);
+ }
+ }
+}
+
+static VALUE
+get_backtrace(info)
+ VALUE info;
+{
+ if (NIL_P(info)) return Qnil;
+ info = rb_funcall(info, rb_intern("backtrace"), 0);
+ if (NIL_P(info)) return Qnil;
+ return rb_check_array_type(info);
+}
+
+static void
+set_backtrace(info, bt)
+ VALUE info, bt;
+{
+ rb_funcall(info, rb_intern("set_backtrace"), 1, bt);
+}
+
+static void
+error_print()
+{
+ VALUE errat = Qnil; /* OK */
+ volatile VALUE eclass, e;
+ char *einfo;
+ long elen;
+
+ if (NIL_P(ruby_errinfo)) return;
+
+ PUSH_TAG(PROT_NONE);
+ if (EXEC_TAG() == 0) {
+ errat = get_backtrace(ruby_errinfo);
+ }
+ else {
+ errat = Qnil;
+ }
+ if (EXEC_TAG()) goto error;
+ if (NIL_P(errat)){
+ ruby_set_current_source();
+ if (ruby_sourcefile)
+ warn_printf("%s:%d", ruby_sourcefile, ruby_sourceline);
+ else
+ warn_printf("%d", ruby_sourceline);
+ }
+ else if (RARRAY(errat)->len == 0) {
+ error_pos();
+ }
+ else {
+ VALUE mesg = RARRAY(errat)->ptr[0];
+
+ if (NIL_P(mesg)) error_pos();
+ else {
+ warn_print2(RSTRING(mesg)->ptr, RSTRING(mesg)->len);
+ }
+ }
+
+ eclass = CLASS_OF(ruby_errinfo);
+ if (EXEC_TAG() == 0) {
+ e = rb_funcall(ruby_errinfo, rb_intern("message"), 0, 0);
+ StringValue(e);
+ einfo = RSTRING(e)->ptr;
+ elen = RSTRING(e)->len;
+ }
+ else {
+ einfo = "";
+ elen = 0;
+ }
+ if (EXEC_TAG()) goto error;
+ if (eclass == rb_eRuntimeError && elen == 0) {
+ warn_print(": unhandled exception\n");
+ }
+ else {
+ VALUE epath;
+
+ epath = rb_class_name(eclass);
+ if (elen == 0) {
+ warn_print(": ");
+ warn_print2(RSTRING(epath)->ptr, RSTRING(epath)->len);
+ warn_print("\n");
+ }
+ else {
+ char *tail = 0;
+ long len = elen;
+
+ if (RSTRING(epath)->ptr[0] == '#') epath = 0;
+ if (tail = memchr(einfo, '\n', elen)) {
+ len = tail - einfo;
+ tail++; /* skip newline */
+ }
+ warn_print(": ");
+ warn_print2(einfo, len);
+ if (epath) {
+ warn_print(" (");
+ warn_print2(RSTRING(epath)->ptr, RSTRING(epath)->len);
+ warn_print(")\n");
+ }
+ if (tail) {
+ warn_print2(tail, elen-len-1);
+ }
+ }
+ }
+
+ if (!NIL_P(errat)) {
+ long i;
+ struct RArray *ep = RARRAY(errat);
+
+#define TRACE_MAX (TRACE_HEAD+TRACE_TAIL+5)
+#define TRACE_HEAD 8
+#define TRACE_TAIL 5
+
+ ep = RARRAY(errat);
+ for (i=1; i<ep->len; i++) {
+ if (TYPE(ep->ptr[i]) == T_STRING) {
+ warn_printf("\tfrom %s\n", RSTRING(ep->ptr[i])->ptr);
+ }
+ if (i == TRACE_HEAD && ep->len > TRACE_MAX) {
+ warn_printf("\t ... %ld levels...\n",
+ ep->len - TRACE_HEAD - TRACE_TAIL);
+ i = ep->len - TRACE_TAIL;
+ }
+ }
+ }
+ error:
+ POP_TAG();
+}
+
+#if defined(__APPLE__)
+#define environ (*_NSGetEnviron())
+#elif !defined(_WIN32) && !defined(__MACOS__) || defined(_WIN32_WCE)
+extern char **environ;
+#endif
+char **rb_origenviron;
+
+void rb_call_inits _((void));
+void Init_stack _((VALUE*));
+void Init_heap _((void));
+void Init_ext _((void));
+
+#ifdef HAVE_NATIVETHREAD
+static rb_nativethread_t ruby_thid;
+int
+is_ruby_native_thread()
+{
+ return NATIVETHREAD_EQUAL(ruby_thid, NATIVETHREAD_CURRENT());
+}
+
+# ifdef HAVE_NATIVETHREAD_KILL
+void
+ruby_native_thread_kill(sig)
+ int sig;
+{
+ NATIVETHREAD_KILL(ruby_thid, sig);
+}
+# endif
+#endif
+
+NORETURN(static void rb_thread_start_1 _((void)));
+
+void
+ruby_init()
+{
+ static int initialized = 0;
+ static struct FRAME frame;
+ static struct iter iter;
+ int state;
+
+ if (initialized)
+ return;
+ initialized = 1;
+#ifdef HAVE_NATIVETHREAD
+ ruby_thid = NATIVETHREAD_CURRENT();
+#endif
+
+ ruby_frame = top_frame = &frame;
+ ruby_iter = &iter;
+
+#ifdef __MACOS__
+ rb_origenviron = 0;
+#else
+ rb_origenviron = environ;
+#endif
+
+ Init_stack((void*)&state);
+ Init_heap();
+ PUSH_SCOPE();
+ top_scope = ruby_scope;
+ /* default visibility is private at toplevel */
+ SCOPE_SET(SCOPE_PRIVATE);
+
+ PUSH_TAG(PROT_NONE);
+ if ((state = EXEC_TAG()) == 0) {
+ rb_call_inits();
+ ruby_class = rb_cObject;
+ ruby_frame->self = ruby_top_self;
+ top_cref = rb_node_newnode(NODE_CREF,rb_cObject,0,0);
+ ruby_cref = top_cref;
+ rb_define_global_const("TOPLEVEL_BINDING", rb_f_binding(ruby_top_self));
+#ifdef __MACOS__
+ _macruby_init();
+#endif
+ ruby_prog_init();
+ ALLOW_INTS;
+ }
+ POP_TAG();
+ if (state) {
+ error_print();
+ exit(EXIT_FAILURE);
+ }
+ POP_SCOPE();
+ ruby_scope = top_scope;
+ top_scope->flags &= ~SCOPE_NOSTACK;
+ ruby_running = 1;
+}
+
+static VALUE
+eval_node(self, node)
+ VALUE self;
+ NODE *node;
+{
+ if (!node) return Qnil;
+ if (nd_type(node) == NODE_PRELUDE) {
+ rb_eval(self, node->nd_head);
+ node = node->nd_body;
+ }
+ if (!node) return Qnil;
+ return rb_eval(self, node);
+}
+
+int ruby_in_eval;
+
+static void rb_thread_cleanup _((void));
+static void rb_thread_wait_other_threads _((void));
+
+static int thread_set_raised();
+static int thread_reset_raised();
+
+static VALUE exception_error;
+static VALUE sysstack_error;
+
+static int
+error_handle(ex)
+ int ex;
+{
+ int status = EXIT_FAILURE;
+
+ if (thread_set_raised()) return EXIT_FAILURE;
+ switch (ex & TAG_MASK) {
+ case 0:
+ status = EXIT_SUCCESS;
+ break;
+
+ case TAG_RETURN:
+ error_pos();
+ warn_print(": unexpected return\n");
+ break;
+ case TAG_NEXT:
+ error_pos();
+ warn_print(": unexpected next\n");
+ break;
+ case TAG_BREAK:
+ error_pos();
+ warn_print(": unexpected break\n");
+ break;
+ case TAG_REDO:
+ error_pos();
+ warn_print(": unexpected redo\n");
+ break;
+ case TAG_RETRY:
+ error_pos();
+ warn_print(": retry outside of rescue clause\n");
+ break;
+ case TAG_THROW:
+ if (prot_tag && prot_tag->frame && prot_tag->frame->node) {
+ NODE *tag = prot_tag->frame->node;
+ warn_printf("%s:%d: uncaught throw\n",
+ tag->nd_file, nd_line(tag));
+ }
+ else {
+ error_pos();
+ warn_printf(": unexpected throw\n");
+ }
+ break;
+ case TAG_RAISE:
+ case TAG_FATAL:
+ if (rb_obj_is_kind_of(ruby_errinfo, rb_eSystemExit)) {
+ VALUE st = rb_iv_get(ruby_errinfo, "status");
+ status = NUM2INT(st);
+ }
+ else {
+ error_print();
+ }
+ break;
+ default:
+ rb_bug("Unknown longjmp status %d", ex);
+ break;
+ }
+ thread_reset_raised();
+ return status;
+}
+
+void
+ruby_options(argc, argv)
+ int argc;
+ char **argv;
+{
+ int state;
+
+#ifdef _WIN32
+ argc = rb_w32_cmdvector(GetCommandLine(), &argv);
+#endif
+
+ Init_stack((void*)&state);
+ PUSH_THREAD_TAG();
+ if ((state = EXEC_TAG()) == 0) {
+ ruby_process_options(argc, argv);
+ }
+ else {
+ if (state == TAG_THREAD) {
+ rb_thread_start_1();
+ }
+ trace_func = 0;
+ tracing = 0;
+ exit(error_handle(state));
+ }
+ POP_THREAD_TAG();
+
+#ifdef _WIN32_WCE
+ wce_FreeCommandLine();
+#endif
+}
+
+void rb_exec_end_proc _((void));
+
+static void
+ruby_finalize_0()
+{
+ PUSH_TAG(PROT_NONE);
+ if (EXEC_TAG() == 0) {
+ rb_trap_exit();
+ }
+ POP_TAG();
+ rb_exec_end_proc();
+}
+
+static void
+ruby_finalize_1()
+{
+ signal(SIGINT, SIG_DFL);
+ ruby_errinfo = 0;
+ rb_gc_call_finalizer_at_exit();
+ trace_func = 0;
+ tracing = 0;
+}
+
+void
+ruby_finalize()
+{
+ ruby_finalize_0();
+ ruby_finalize_1();
+}
+
+int
+ruby_cleanup(ex)
+ int ex;
+{
+ int state;
+ volatile VALUE err = ruby_errinfo;
+
+ ruby_safe_level = 0;
+ Init_stack((void*)&state);
+ PUSH_THREAD_TAG();
+ PUSH_ITER(ITER_NOT);
+ if ((state = EXEC_TAG()) == 0) {
+ ruby_finalize_0();
+ if (ruby_errinfo) err = ruby_errinfo;
+ rb_thread_cleanup();
+ rb_thread_wait_other_threads();
+ }
+ else if (state == TAG_THREAD) {
+ rb_thread_start_1();
+ }
+ else if (ex == 0) {
+ ex = state;
+ }
+ POP_ITER();
+ ruby_errinfo = err;
+ ex = error_handle(ex);
+ ruby_finalize_1();
+ POP_THREAD_TAG();
+
+ if (err && rb_obj_is_kind_of(err, rb_eSystemExit)) {
+ VALUE st = rb_iv_get(err, "status");
+ return NUM2INT(st);
+ }
+ return ex;
+}
+
+extern NODE *ruby_eval_tree;
+
+static void cont_call _((VALUE));
+
+static int
+ruby_exec_internal()
+{
+ int state;
+
+ PUSH_THREAD_TAG();
+ PUSH_ITER(ITER_NOT);
+ /* default visibility is private at toplevel */
+ SCOPE_SET(SCOPE_PRIVATE);
+ if ((state = EXEC_TAG()) == 0) {
+ eval_node(ruby_top_self, ruby_eval_tree);
+ }
+#if 0
+ else if (state == TAG_CONTCALL) {
+ cont_call(prot_tag->retval);
+ }
+#endif
+ else if (state == TAG_THREAD) {
+ rb_thread_start_1();
+ }
+ POP_ITER();
+ POP_THREAD_TAG();
+ return state;
+}
+
+int
+ruby_exec()
+{
+ volatile NODE *tmp;
+
+ Init_stack((void*)&tmp);
+ return ruby_exec_internal();
+}
+
+void
+ruby_stop(ex)
+ int ex;
+{
+ exit(ruby_cleanup(ex));
+}
+
+void
+ruby_run()
+{
+ int state;
+ static int ex;
+
+ if (ruby_nerrs > 0) exit(EXIT_FAILURE);
+ state = ruby_exec();
+ if (state && !ex) ex = state;
+ ruby_stop(ex);
+}
+
+static void
+compile_error(at)
+ const char *at;
+{
+ VALUE str;
+
+ ruby_nerrs = 0;
+ str = rb_str_buf_new2("compile error");
+ if (at) {
+ rb_str_buf_cat2(str, " in ");
+ rb_str_buf_cat2(str, at);
+ }
+ rb_str_buf_cat(str, "\n", 1);
+ if (!NIL_P(ruby_errinfo)) {
+ rb_str_append(str, rb_obj_as_string(ruby_errinfo));
+ }
+ rb_exc_raise(rb_exc_new3(rb_eSyntaxError, str));
+}
+
+VALUE
+rb_eval_string(str)
+ const char *str;
+{
+ VALUE v;
+ NODE *oldsrc = ruby_current_node;
+
+ ruby_current_node = 0;
+ ruby_sourcefile = rb_source_filename("(eval)");
+ v = eval(ruby_top_self, rb_str_new2(str), Qnil, 0, 0);
+ ruby_current_node = oldsrc;
+
+ return v;
+}
+
+VALUE
+rb_eval_string_protect(str, state)
+ const char *str;
+ int *state;
+{
+ return rb_protect((VALUE (*)_((VALUE)))rb_eval_string, (VALUE)str, state);
+}
+
+VALUE
+rb_eval_string_wrap(str, state)
+ const char *str;
+ int *state;
+{
+ int status;
+ VALUE self = ruby_top_self;
+ VALUE wrapper = ruby_wrapper;
+ VALUE val;
+
+ PUSH_CLASS(ruby_wrapper = rb_module_new());
+ ruby_top_self = rb_obj_clone(ruby_top_self);
+ rb_extend_object(ruby_top_self, ruby_wrapper);
+ PUSH_FRAME();
+ ruby_frame->callee = 0;
+ ruby_frame->this_func = 0;
+ ruby_frame->this_class = 0;
+ ruby_frame->self = self;
+ PUSH_CREF(ruby_wrapper);
+ PUSH_SCOPE();
+
+ val = rb_eval_string_protect(str, &status);
+ ruby_top_self = self;
+
+ POP_SCOPE();
+ POP_FRAME();
+ POP_CLASS();
+ ruby_wrapper = wrapper;
+ if (state) {
+ *state = status;
+ }
+ else if (status) {
+ JUMP_TAG(status);
+ }
+ return val;
+}
+
+NORETURN(static void localjump_error(const char*, VALUE, int));
+static void
+localjump_error(mesg, value, reason)
+ const char *mesg;
+ VALUE value;
+ int reason;
+{
+ VALUE exc = rb_exc_new2(rb_eLocalJumpError, mesg);
+ ID id;
+
+ rb_iv_set(exc, "@exit_value", value);
+ switch (reason) {
+ case TAG_BREAK:
+ id = rb_intern("break"); break;
+ case TAG_REDO:
+ id = rb_intern("redo"); break;
+ case TAG_RETRY:
+ id = rb_intern("retry"); break;
+ case TAG_NEXT:
+ id = rb_intern("next"); break;
+ case TAG_RETURN:
+ id = rb_intern("return"); break;
+ default:
+ id = rb_intern("noreason"); break;
+ }
+ rb_iv_set(exc, "@reason", ID2SYM(id));
+ rb_exc_raise(exc);
+}
+
+/*
+ * call_seq:
+ * local_jump_error.exit_value => obj
+ *
+ * Returns the exit value associated with this +LocalJumpError+.
+ */
+static VALUE
+localjump_xvalue(exc)
+ VALUE exc;
+{
+ return rb_iv_get(exc, "@exit_value");
+}
+
+/*
+ * call-seq:
+ * local_jump_error.reason => symbol
+ *
+ * The reason this block was terminated:
+ * :break, :redo, :retry, :next, :return, or :noreason.
+ */
+
+static VALUE
+localjump_reason(exc)
+ VALUE exc;
+{
+ return rb_iv_get(exc, "@reason");
+}
+
+NORETURN(static void jump_tag_but_local_jump _((int,VALUE)));
+static void
+jump_tag_but_local_jump(state, val)
+ int state;
+ VALUE val;
+{
+
+ if (val == Qundef) val = prot_tag->retval;
+ switch (state) {
+ case 0:
+ break;
+ case TAG_RETURN:
+ localjump_error("unexpected return", val, state);
+ break;
+ case TAG_BREAK:
+ localjump_error("unexpected break", val, state);
+ break;
+ case TAG_NEXT:
+ localjump_error("unexpected next", val, state);
+ break;
+ case TAG_REDO:
+ localjump_error("unexpected redo", Qnil, state);
+ break;
+ case TAG_RETRY:
+ localjump_error("retry outside of rescue clause", Qnil, state);
+ break;
+ default:
+ break;
+ }
+ JUMP_TAG(state);
+}
+
+VALUE
+rb_eval_cmd(cmd, arg, level)
+ VALUE cmd, arg;
+ int level;
+{
+ int state;
+ VALUE val = Qnil; /* OK */
+ struct SCOPE *saved_scope;
+ volatile int safe = ruby_safe_level;
+
+ if (OBJ_TAINTED(cmd)) {
+ level = 4;
+ }
+ if (TYPE(cmd) != T_STRING) {
+ PUSH_ITER(ITER_NOT);
+ PUSH_TAG(PROT_NONE);
+ ruby_safe_level = level;
+ if ((state = EXEC_TAG()) == 0) {
+ val = rb_funcall2(cmd, rb_intern("call"), RARRAY(arg)->len, RARRAY(arg)->ptr);
+ }
+ ruby_safe_level = safe;
+ POP_TAG();
+ POP_ITER();
+ if (state) JUMP_TAG(state);
+ return val;
+ }
+
+ saved_scope = ruby_scope;
+ ruby_scope = top_scope;
+ PUSH_FRAME();
+ ruby_frame->callee = 0;
+ ruby_frame->this_func = 0;
+ ruby_frame->this_class = 0;
+ ruby_frame->self = ruby_top_self;
+ PUSH_CREF(ruby_wrapper ? ruby_wrapper : rb_cObject);
+
+ ruby_safe_level = level;
+
+ PUSH_TAG(PROT_NONE);
+ if ((state = EXEC_TAG()) == 0) {
+ val = eval(ruby_top_self, cmd, Qnil, 0, 0);
+ }
+ if (ruby_scope->flags & SCOPE_DONT_RECYCLE)
+ scope_dup(saved_scope);
+ ruby_scope = saved_scope;
+ ruby_safe_level = safe;
+ POP_TAG();
+ POP_FRAME();
+
+ jump_tag_but_local_jump(state, val);
+ return val;
+}
+
+#define ruby_cbase (ruby_cref->nd_clss)
+
+static VALUE
+ev_const_defined(cref, id, self)
+ NODE *cref;
+ ID id;
+ VALUE self;
+{
+ NODE *cbase = cref;
+ VALUE result;
+
+ while (cbase && cbase->nd_next) {
+ struct RClass *klass = RCLASS(cbase->nd_clss);
+
+ if (NIL_P(klass)) return rb_const_defined(CLASS_OF(self), id);
+ if (klass->iv_tbl && st_lookup(klass->iv_tbl, id, &result)) {
+ if (result == Qundef && NIL_P(rb_autoload_p((VALUE)klass, id))) {
+ return Qfalse;
+ }
+ return Qtrue;
+ }
+ cbase = cbase->nd_next;
+ }
+ return rb_const_defined(cref->nd_clss, id);
+}
+
+static VALUE
+ev_const_get(cref, id, self)
+ NODE *cref;
+ ID id;
+ VALUE self;
+{
+ NODE *cbase = cref;
+ VALUE result;
+
+ while (cbase && cbase->nd_next) {
+ VALUE klass = cbase->nd_clss;
+
+ if (NIL_P(klass)) return rb_const_get(CLASS_OF(self), id);
+ while (RCLASS(klass)->iv_tbl && st_lookup(RCLASS(klass)->iv_tbl, id, &result)) {
+ if (result == Qundef) {
+ rb_autoload_load(klass, id);
+ continue;
+ }
+ return result;
+ }
+ cbase = cbase->nd_next;
+ }
+ return rb_const_get(cref->nd_clss, id);
+}
+
+static VALUE
+cvar_cbase()
+{
+ NODE *cref = ruby_cref;
+
+ while (cref && cref->nd_next && (NIL_P(cref->nd_clss) || FL_TEST(cref->nd_clss, FL_SINGLETON))) {
+ cref = cref->nd_next;
+ if (!cref->nd_next) {
+ rb_warn("class variable access from toplevel singleton method");
+ }
+ }
+ if (NIL_P(cref->nd_clss)) {
+ rb_raise(rb_eTypeError, "no class variables available");
+ }
+ return cref->nd_clss;
+}
+
+/*
+ * call-seq:
+ * Module.nesting => array
+ *
+ * Returns the list of +Modules+ nested at the point of call.
+ *
+ * module M1
+ * module M2
+ * $a = Module.nesting
+ * end
+ * end
+ * $a #=> [M1::M2, M1]
+ * $a[0].name #=> "M1::M2"
+ */
+
+static VALUE
+rb_mod_nesting()
+{
+ NODE *cbase = ruby_cref;
+ VALUE ary = rb_ary_new();
+
+ while (cbase && cbase->nd_next) {
+ if (!NIL_P(cbase->nd_clss)) rb_ary_push(ary, cbase->nd_clss);
+ cbase = cbase->nd_next;
+ }
+ if (ruby_wrapper && RARRAY(ary)->len == 0) {
+ rb_ary_push(ary, ruby_wrapper);
+ }
+ return ary;
+}
+
+/*
+ * call-seq:
+ * Module.constants => array
+ *
+ * Returns an array of the names of all constants defined in the
+ * system. This list includes the names of all modules and classes.
+ *
+ * p Module.constants.sort[1..5]
+ *
+ * <em>produces:</em>
+ *
+ * ["ARGV", "ArgumentError", "Array", "Bignum", "Binding"]
+ */
+
+static VALUE
+rb_mod_s_constants()
+{
+ NODE *cbase = ruby_cref;
+ void *data = 0;
+
+ while (cbase) {
+ if (!NIL_P(cbase->nd_clss)) {
+ data = rb_mod_const_at(cbase->nd_clss, data);
+ }
+ cbase = cbase->nd_next;
+ }
+
+ if (!NIL_P(ruby_cbase)) {
+ data = rb_mod_const_of(ruby_cbase, data);
+ }
+ return rb_const_list(data);
+}
+
+void
+rb_frozen_class_p(klass)
+ VALUE klass;
+{
+ char *desc = "something(?!)";
+
+ if (OBJ_FROZEN(klass)) {
+ if (FL_TEST(klass, FL_SINGLETON))
+ desc = "object";
+ else {
+ switch (TYPE(klass)) {
+ case T_MODULE:
+ case T_ICLASS:
+ desc = "module"; break;
+ case T_CLASS:
+ desc = "class"; break;
+ }
+ }
+ rb_error_frozen(desc);
+ }
+}
+
+void
+rb_undef(klass, id)
+ VALUE klass;
+ ID id;
+{
+ VALUE origin;
+ NODE *body;
+
+ if (ruby_cbase == rb_cObject && klass == rb_cObject) {
+ rb_secure(4);
+ }
+ if (ruby_safe_level >= 4 && !OBJ_TAINTED(klass)) {
+ rb_raise(rb_eSecurityError, "Insecure: can't undef `%s'", rb_id2name(id));
+ }
+ rb_frozen_class_p(klass);
+ if (id == __id__ || id == __send__ || id == init) {
+ rb_warn("undefining `%s' may cause serious problem", rb_id2name(id));
+ }
+ body = search_method(klass, id, &origin);
+ if (!body || !body->nd_body) {
+ char *s0 = " class";
+ VALUE c = klass;
+
+ if (FL_TEST(c, FL_SINGLETON)) {
+ VALUE obj = rb_iv_get(klass, "__attached__");
+
+ switch (TYPE(obj)) {
+ case T_MODULE:
+ case T_CLASS:
+ c = obj;
+ s0 = "";
+ }
+ }
+ else if (TYPE(c) == T_MODULE) {
+ s0 = " module";
+ }
+ rb_name_error(id, "undefined method `%s' for%s `%s'",
+ rb_id2name(id),s0,rb_class2name(c));
+ }
+ rb_add_method(klass, id, 0, NOEX_PUBLIC);
+ if (FL_TEST(klass, FL_SINGLETON)) {
+ rb_funcall(rb_iv_get(klass, "__attached__"),
+ singleton_undefined, 1, ID2SYM(id));
+ }
+ else {
+ rb_funcall(klass, undefined, 1, ID2SYM(id));
+ }
+}
+
+/*
+ * call-seq:
+ * undef_method(symbol) => self
+ *
+ * Prevents the current class from responding to calls to the named
+ * method. Contrast this with <code>remove_method</code>, which deletes
+ * the method from the particular class; Ruby will still search
+ * superclasses and mixed-in modules for a possible receiver.
+ *
+ * class Parent
+ * def hello
+ * puts "In parent"
+ * end
+ * end
+ * class Child < Parent
+ * def hello
+ * puts "In child"
+ * end
+ * end
+ *
+ *
+ * c = Child.new
+ * c.hello
+ *
+ *
+ * class Child
+ * remove_method :hello # remove from child, still in parent
+ * end
+ * c.hello
+ *
+ *
+ * class Child
+ * undef_method :hello # prevent any calls to 'hello'
+ * end
+ * c.hello
+ *
+ * <em>produces:</em>
+ *
+ * In child
+ * In parent
+ * prog.rb:23: undefined method `hello' for #<Child:0x401b3bb4> (NoMethodError)
+ */
+
+static VALUE
+rb_mod_undef_method(argc, argv, mod)
+ int argc;
+ VALUE *argv;
+ VALUE mod;
+{
+ int i;
+
+ for (i=0; i<argc; i++) {
+ rb_undef(mod, rb_to_id(argv[i]));
+ }
+ return mod;
+}
+
+void
+rb_alias(klass, name, def)
+ VALUE klass;
+ ID name, def;
+{
+ VALUE origin;
+ NODE *orig, *body, *node;
+ VALUE singleton = 0;
+
+ rb_frozen_class_p(klass);
+ if (name == def) return;
+ if (klass == rb_cObject) {
+ rb_secure(4);
+ }
+ orig = search_method(klass, def, &origin);
+ if (!orig || !orig->nd_body) {
+ if (TYPE(klass) == T_MODULE) {
+ orig = search_method(rb_cObject, def, &origin);
+ }
+ }
+ if (!orig || !orig->nd_body) {
+ print_undef(klass, def);
+ }
+ if (FL_TEST(klass, FL_SINGLETON)) {
+ singleton = rb_iv_get(klass, "__attached__");
+ }
+ body = orig->nd_body;
+ orig->nd_cnt++;
+ if (nd_type(body) == NODE_FBODY) { /* was alias */
+ def = body->nd_mid;
+ origin = body->nd_orig;
+ body = body->nd_head;
+ }
+
+ rb_clear_cache_by_id(name);
+ if (RTEST(ruby_verbose) && st_lookup(RCLASS(klass)->m_tbl, name, (st_data_t *)&node)) {
+ if (node->nd_cnt == 0 && node->nd_body) {
+ rb_warning("discarding old %s", rb_id2name(name));
+ }
+ }
+ st_insert(RCLASS(klass)->m_tbl, name,
+ (st_data_t)NEW_METHOD(NEW_FBODY(body, def, origin), orig->nd_noex));
+ if (singleton) {
+ rb_funcall(singleton, singleton_added, 1, ID2SYM(name));
+ }
+ else {
+ rb_funcall(klass, added, 1, ID2SYM(name));
+ }
+}
+
+/*
+ * call-seq:
+ * alias_method(new_name, old_name) => self
+ *
+ * Makes <i>new_name</i> a new copy of the method <i>old_name</i>. This can
+ * be used to retain access to methods that are overridden.
+ *
+ * module Mod
+ * alias_method :orig_exit, :exit
+ * def exit(code=0)
+ * puts "Exiting with code #{code}"
+ * orig_exit(code)
+ * end
+ * end
+ * include Mod
+ * exit(99)
+ *
+ * <em>produces:</em>
+ *
+ * Exiting with code 99
+ */
+
+static VALUE
+rb_mod_alias_method(mod, newname, oldname)
+ VALUE mod, newname, oldname;
+{
+ rb_alias(mod, rb_to_id(newname), rb_to_id(oldname));
+ return mod;
+}
+
+static NODE*
+copy_node_scope(node, rval)
+ NODE *node;
+ NODE *rval;
+{
+ NODE *copy = NEW_NODE(NODE_SCOPE,0,rval,node->nd_next);
+
+ if (node->nd_tbl) {
+ copy->nd_tbl = ALLOC_N(ID, node->nd_tbl[0]+1);
+ MEMCPY(copy->nd_tbl, node->nd_tbl, ID, node->nd_tbl[0]+1);
+ }
+ else {
+ copy->nd_tbl = 0;
+ }
+ return copy;
+}
+
+#ifdef C_ALLOCA
+# define TMP_PROTECT NODE * volatile tmp__protect_tmp=0
+# define TMP_ALLOC(n) \
+ (tmp__protect_tmp = rb_node_newnode(NODE_ALLOCA, \
+ ALLOC_N(VALUE,n),tmp__protect_tmp,n), \
+ (void*)tmp__protect_tmp->nd_head)
+#else
+# define TMP_PROTECT typedef int foobazzz
+# define TMP_ALLOC(n) ALLOCA_N(VALUE,n)
+#endif
+
+#define SETUP_ARGS0(anode,alen) do {\
+ NODE *n = anode;\
+ if (!n) {\
+ argc = 0;\
+ argv = 0;\
+ }\
+ else if (nd_type(n) == NODE_ARRAY) {\
+ argc=alen;\
+ if (argc > 0) {\
+ int i;\
+ n = anode;\
+ argv = TMP_ALLOC(argc);\
+ for (i=0;i<argc;i++) {\
+ argv[i] = rb_eval(self,n->nd_head);\
+ n=n->nd_next;\
+ }\
+ }\
+ else {\
+ argc = 0;\
+ argv = 0;\
+ }\
+ }\
+ else {\
+ VALUE args = rb_eval(self,n);\
+ if (TYPE(args) != T_ARRAY)\
+ args = rb_ary_to_ary(args);\
+ argc = RARRAY(args)->len;\
+ argv = ALLOCA_N(VALUE, argc);\
+ MEMCPY(argv, RARRAY(args)->ptr, VALUE, argc);\
+ }\
+} while (0)
+
+#define SETUP_ARGS(anode) SETUP_ARGS0(anode, anode->nd_alen)
+
+#define BEGIN_CALLARGS do {\
+ struct BLOCK *tmp_block = ruby_block;\
+ int tmp_iter = ruby_iter->iter;\
+ if (tmp_iter == ITER_PRE) {\
+ ruby_block = ruby_block->outer;\
+ tmp_iter = ITER_NOT;\
+ }\
+ PUSH_ITER(tmp_iter)
+
+#define END_CALLARGS \
+ ruby_block = tmp_block;\
+ POP_ITER();\
+} while (0)
+
+#define MATCH_DATA *rb_svar(node->nd_cnt)
+
+static const char* is_defined _((VALUE, NODE*, char*, int));
+
+static char*
+arg_defined(self, node, buf, type)
+ VALUE self;
+ NODE *node;
+ char *buf;
+ char *type;
+{
+ int argc;
+ int i;
+
+ if (!node) return type; /* no args */
+ if (nd_type(node) == NODE_ARRAY) {
+ argc=node->nd_alen;
+ if (argc > 0) {
+ for (i=0;i<argc;i++) {
+ if (!is_defined(self, node->nd_head, buf, 0))
+ return 0;
+ node = node->nd_next;
+ }
+ }
+ }
+ else if (!is_defined(self, node, buf, 0)) {
+ return 0;
+ }
+ return type;
+}
+
+static const char*
+is_defined(self, node, buf, noeval)
+ VALUE self;
+ NODE *node; /* OK */
+ char *buf;
+ int noeval;
+{
+ VALUE val; /* OK */
+ int state;
+ static const char *ex = "expression";
+
+ if (!node) return ex;
+ switch (nd_type(node)) {
+ case NODE_SUPER:
+ case NODE_ZSUPER:
+ if (ruby_frame->this_func == 0) return 0;
+ else if (ruby_frame->this_class == 0) return 0;
+ val = ruby_frame->this_class;
+ if (rb_method_boundp(RCLASS(val)->super, ruby_frame->this_func, 0)) {
+ if (nd_type(node) == NODE_SUPER) {
+ return arg_defined(self, node->nd_args, buf, "super");
+ }
+ return "super";
+ }
+ break;
+
+ case NODE_VCALL:
+ case NODE_FCALL:
+ val = self;
+ goto check_bound;
+
+ case NODE_ATTRASGN:
+ val = self;
+ if (node->nd_recv == (NODE *)1) goto check_bound;
+ case NODE_CALL:
+ if (!is_defined(self, node->nd_recv, buf, Qtrue)) return 0;
+ if (noeval) return ex;
+ val = rb_eval(self, node->nd_recv);
+ check_bound:
+ {
+ int call = nd_type(node)==NODE_CALL;
+
+ val = CLASS_OF(val);
+ if (call) {
+ int noex;
+ ID id = node->nd_mid;
+
+ if (!rb_get_method_body(&val, &id, &noex))
+ break;
+ if ((noex & NOEX_PRIVATE))
+ break;
+ if ((noex & NOEX_PROTECTED) &&
+ !rb_obj_is_kind_of(self, rb_class_real(val)))
+ break;
+ }
+ else if (!rb_method_boundp(val, node->nd_mid, call))
+ break;
+ return arg_defined(self, node->nd_args, buf,
+ nd_type(node) == NODE_ATTRASGN ?
+ "assignment" : "method");
+ }
+ break;
+
+ case NODE_MATCH2:
+ case NODE_MATCH3:
+ return "method";
+
+ case NODE_YIELD:
+ if (rb_block_given_p()) {
+ return "yield";
+ }
+ break;
+
+ case NODE_SELF:
+ return "self";
+
+ case NODE_NIL:
+ return "nil";
+
+ case NODE_TRUE:
+ return "true";
+
+ case NODE_FALSE:
+ return "false";
+
+ case NODE_ATTRSET:
+ case NODE_OP_ASGN1:
+ case NODE_OP_ASGN2:
+ case NODE_MASGN:
+ case NODE_LASGN:
+ case NODE_DASGN:
+ case NODE_DASGN_CURR:
+ case NODE_GASGN:
+ case NODE_IASGN:
+ case NODE_CDECL:
+ case NODE_CVDECL:
+ case NODE_CVASGN:
+ return "assignment";
+
+ case NODE_LVAR:
+ return "local-variable";
+ case NODE_DVAR:
+ return "local-variable(in-block)";
+
+ case NODE_GVAR:
+ if (rb_gvar_defined(node->nd_entry)) {
+ return "global-variable";
+ }
+ break;
+
+ case NODE_IVAR:
+ if (rb_ivar_defined(self, node->nd_vid)) {
+ return "instance-variable";
+ }
+ break;
+
+ case NODE_CONST:
+ if (ev_const_defined(ruby_cref, node->nd_vid, self)) {
+ return "constant";
+ }
+ break;
+
+ case NODE_CVAR:
+ if (rb_cvar_defined(cvar_cbase(), node->nd_vid)) {
+ return "class variable";
+ }
+ break;
+
+ case NODE_COLON2:
+ if (!is_defined(self, node->nd_recv, buf, Qtrue)) return 0;
+ if (noeval) return ex;
+ val = rb_eval(self, node->nd_recv);
+ switch (TYPE(val)) {
+ case T_CLASS:
+ case T_MODULE:
+ if (rb_const_defined_from(val, node->nd_mid))
+ return "constant";
+ break;
+ default:
+ if (rb_method_boundp(CLASS_OF(val), node->nd_mid, 1)) {
+ return "method";
+ }
+ }
+ break;
+
+ case NODE_COLON3:
+ if (rb_const_defined_from(rb_cObject, node->nd_mid)) {
+ return "constant";
+ }
+ break;
+
+ case NODE_NTH_REF:
+ if (RTEST(rb_reg_nth_defined(node->nd_nth, MATCH_DATA))) {
+ if (!buf) return ex;
+ sprintf(buf, "$%d", (int)node->nd_nth);
+ return buf;
+ }
+ break;
+
+ case NODE_BACK_REF:
+ if (RTEST(rb_reg_nth_defined(0, MATCH_DATA))) {
+ if (!buf) return ex;
+ sprintf(buf, "$%c", (char)node->nd_nth);
+ return buf;
+ }
+ break;
+
+ default:
+ PUSH_TAG(PROT_NONE);
+ if ((state = EXEC_TAG()) == 0) {
+ rb_eval(self, node);
+ }
+ POP_TAG();
+ if (!state) {
+ return ex;
+ }
+ ruby_errinfo = Qnil;
+ break;
+ }
+ return 0;
+}
+
+static int handle_rescue _((VALUE,NODE*));
+
+static void blk_free();
+
+static VALUE
+rb_obj_is_proc(proc)
+ VALUE proc;
+{
+ if (TYPE(proc) == T_DATA && RDATA(proc)->dfree == (RUBY_DATA_FUNC)blk_free) {
+ return Qtrue;
+ }
+ return Qfalse;
+}
+
+void
+rb_add_event_hook(func, events)
+ rb_event_hook_func_t func;
+ rb_event_t events;
+{
+ rb_event_hook_t *hook;
+
+ hook = ALLOC(rb_event_hook_t);
+ hook->func = func;
+ hook->events = events;
+ hook->next = event_hooks;
+ event_hooks = hook;
+}
+
+int
+rb_remove_event_hook(func)
+ rb_event_hook_func_t func;
+{
+ rb_event_hook_t *prev, *hook;
+
+ prev = NULL;
+ hook = event_hooks;
+ while (hook) {
+ if (hook->func == func) {
+ if (prev) {
+ prev->next = hook->next;
+ }
+ else {
+ event_hooks = hook->next;
+ }
+ xfree(hook);
+ return 0;
+ }
+ prev = hook;
+ hook = hook->next;
+ }
+ return -1;
+}
+
+/*
+ * call-seq:
+ * set_trace_func(proc) => proc
+ * set_trace_func(nil) => nil
+ *
+ * Establishes _proc_ as the handler for tracing, or disables
+ * tracing if the parameter is +nil+. _proc_ takes up
+ * to six parameters: an event name, a filename, a line number, an
+ * object id, a binding, and the name of a class. _proc_ is
+ * invoked whenever an event occurs. Events are: <code>c-call</code>
+ * (call a C-language routine), <code>c-return</code> (return from a
+ * C-language routine), <code>call</code> (call a Ruby method),
+ * <code>class</code> (start a class or module definition),
+ * <code>end</code> (finish a class or module definition),
+ * <code>line</code> (execute code on a new line), <code>raise</code>
+ * (raise an exception), and <code>return</code> (return from a Ruby
+ * method). Tracing is disabled within the context of _proc_.
+ *
+ * class Test
+ * def test
+ * a = 1
+ * b = 2
+ * end
+ * end
+ *
+ * set_trace_func proc { |event, file, line, id, binding, classname|
+ * printf "%8s %s:%-2d %10s %8s\n", event, file, line, id, classname
+ * }
+ * t = Test.new
+ * t.test
+ *
+ * line prog.rb:11 false
+ * c-call prog.rb:11 new Class
+ * c-call prog.rb:11 initialize Object
+ * c-return prog.rb:11 initialize Object
+ * c-return prog.rb:11 new Class
+ * line prog.rb:12 false
+ * call prog.rb:2 test Test
+ * line prog.rb:3 test Test
+ * line prog.rb:4 test Test
+ * return prog.rb:4 test Test
+ */
+
+
+static VALUE
+set_trace_func(obj, trace)
+ VALUE obj, trace;
+{
+ rb_event_hook_t *hook;
+
+ if (NIL_P(trace)) {
+ trace_func = 0;
+ rb_remove_event_hook(call_trace_func);
+ return Qnil;
+ }
+ if (!rb_obj_is_proc(trace)) {
+ rb_raise(rb_eTypeError, "trace_func needs to be Proc");
+ }
+ trace_func = trace;
+ for (hook = event_hooks; hook; hook = hook->next) {
+ if (hook->func == call_trace_func)
+ return trace;
+ }
+ rb_add_event_hook(call_trace_func, RUBY_EVENT_ALL);
+ return trace;
+}
+
+static char *
+get_event_name(rb_event_t event)
+{
+ switch (event) {
+ case RUBY_EVENT_LINE:
+ return "line";
+ case RUBY_EVENT_CLASS:
+ return "class";
+ case RUBY_EVENT_END:
+ return "end";
+ case RUBY_EVENT_CALL:
+ return "call";
+ case RUBY_EVENT_RETURN:
+ return "return";
+ case RUBY_EVENT_C_CALL:
+ return "c-call";
+ case RUBY_EVENT_C_RETURN:
+ return "c-return";
+ case RUBY_EVENT_RAISE:
+ return "raise";
+ default:
+ return "unknown";
+ }
+}
+
+static void
+call_trace_func(event, node, self, id, klass)
+ rb_event_t event;
+ NODE *node;
+ VALUE self;
+ ID id;
+ VALUE klass; /* OK */
+{
+ int state, raised;
+ struct FRAME *prev;
+ NODE *node_save;
+ VALUE srcfile;
+ char *event_name;
+
+ if (!trace_func) return;
+ if (tracing) return;
+ if (id == ID_ALLOCATOR) return;
+ if (!node && ruby_sourceline == 0) return;
+
+ if (!(node_save = ruby_current_node)) {
+ node_save = NEW_BEGIN(0);
+ }
+ tracing = 1;
+ prev = ruby_frame;
+ PUSH_FRAME();
+ *ruby_frame = *prev;
+ ruby_frame->prev = prev;
+ ruby_frame->iter = 0; /* blocks not available anyway */
+
+ if (node) {
+ ruby_current_node = node;
+ ruby_frame->node = node;
+ ruby_sourcefile = node->nd_file;
+ ruby_sourceline = nd_line(node);
+ }
+ if (klass) {
+ if (TYPE(klass) == T_ICLASS) {
+ klass = RBASIC(klass)->klass;
+ }
+ else if (FL_TEST(klass, FL_SINGLETON)) {
+ klass = self;
+ }
+ }
+ PUSH_TAG(PROT_NONE);
+ raised = thread_reset_raised();
+ if ((state = EXEC_TAG()) == 0) {
+ srcfile = rb_str_new2(ruby_sourcefile?ruby_sourcefile:"(ruby)");
+ event_name = get_event_name(event);
+ proc_invoke(trace_func, rb_ary_new3(6, rb_str_new2(event_name),
+ srcfile,
+ INT2FIX(ruby_sourceline),
+ id?ID2SYM(id):Qnil,
+ self ? rb_f_binding(self) : Qnil,
+ klass?klass:Qnil),
+ Qundef, 0);
+ }
+ if (raised) thread_set_raised();
+ POP_TAG();
+ POP_FRAME();
+
+ tracing = 0;
+ ruby_current_node = node_save;
+ SET_CURRENT_SOURCE();
+ if (state) JUMP_TAG(state);
+}
+
+static VALUE
+avalue_to_svalue(v)
+ VALUE v;
+{
+ VALUE tmp, top;
+
+ tmp = rb_check_array_type(v);
+ if (NIL_P(tmp)) {
+ return v;
+ }
+ if (RARRAY(tmp)->len == 0) {
+ return Qundef;
+ }
+ if (RARRAY(tmp)->len == 1) {
+ top = rb_check_array_type(RARRAY(tmp)->ptr[0]);
+ if (NIL_P(top)) {
+ return RARRAY(tmp)->ptr[0];
+ }
+ if (RARRAY(top)->len > 1) {
+ return v;
+ }
+ return top;
+ }
+ return tmp;
+}
+
+static VALUE
+svalue_to_avalue(v)
+ VALUE v;
+{
+ VALUE tmp, top;
+
+ if (v == Qundef) return rb_ary_new2(0);
+ tmp = rb_check_array_type(v);
+ if (NIL_P(tmp)) {
+ return rb_ary_new3(1, v);
+ }
+ if (RARRAY(tmp)->len == 1) {
+ top = rb_check_array_type(RARRAY(tmp)->ptr[0]);
+ if (!NIL_P(top) && RARRAY(top)->len > 1) {
+ return tmp;
+ }
+ return rb_ary_new3(1, v);
+ }
+ return tmp;
+}
+
+static VALUE
+svalue_to_mrhs(v, lhs)
+ VALUE v;
+ NODE *lhs;
+{
+ VALUE tmp;
+
+ if (v == Qundef) return rb_values_new2(0, 0);
+ tmp = rb_check_array_type(v);
+ if (NIL_P(tmp)) {
+ return rb_values_new(1, v);
+ }
+ /* no lhs means splat lhs only */
+ if (!lhs) {
+ return rb_values_new(1, v);
+ }
+ return tmp;
+}
+
+static VALUE
+avalue_splat(v)
+ VALUE v;
+{
+ if (RARRAY(v)->len == 0) {
+ return Qundef;
+ }
+ if (RARRAY(v)->len == 1) {
+ return RARRAY(v)->ptr[0];
+ }
+ return v;
+}
+
+static VALUE
+splat_value(v)
+ VALUE v;
+{
+ VALUE val;
+
+ if (NIL_P(v)) val = rb_ary_new3(1, Qnil);
+ else val = rb_Array(v);
+ return rb_values_from_ary(val);
+}
+
+static VALUE
+class_prefix(self, cpath)
+ VALUE self;
+ NODE *cpath;
+{
+ if (!cpath) {
+ rb_bug("class path missing");
+ }
+ if (cpath->nd_head) {
+ VALUE c = rb_eval(self, cpath->nd_head);
+ switch (TYPE(c)) {
+ case T_CLASS:
+ case T_MODULE:
+ break;
+ default:
+ rb_raise(rb_eTypeError, "%s is not a class/module",
+ RSTRING(rb_obj_as_string(c))->ptr);
+ }
+ return c;
+ }
+ else if (nd_type(cpath) == NODE_COLON2) {
+ return ruby_cbase;
+ }
+ else if (ruby_wrapper) {
+ return ruby_wrapper;
+ }
+ else {
+ return rb_cObject;
+ }
+}
+
+#define return_value(v) do {\
+ if ((prot_tag->retval = (v)) == Qundef) {\
+ prot_tag->retval = Qnil;\
+ }\
+} while (0)
+
+NORETURN(static void return_jump _((VALUE)));
+NORETURN(static void break_jump _((VALUE)));
+
+static VALUE
+rb_eval(self, n)
+ VALUE self;
+ NODE *n;
+{
+ NODE * volatile contnode = 0;
+ NODE * volatile node = n;
+ int state;
+ volatile VALUE result = Qnil;
+
+#define RETURN(v) do { \
+ result = (v); \
+ goto finish; \
+} while (0)
+
+ again:
+ if (!node) RETURN(Qnil);
+
+ ruby_current_node = node;
+ if (node->flags & NODE_NEWLINE) {
+ EXEC_EVENT_HOOK(RUBY_EVENT_LINE, node, self,
+ ruby_frame->this_func,
+ ruby_frame->this_class);
+ }
+ switch (nd_type(node)) {
+ case NODE_BLOCK:
+ if (contnode) {
+ result = rb_eval(self, node);
+ break;
+ }
+ contnode = node->nd_next;
+ node = node->nd_head;
+ goto again;
+
+ case NODE_POSTEXE:
+ rb_f_END();
+ nd_set_type(node, NODE_NIL); /* exec just once */
+ result = Qnil;
+ break;
+
+ /* begin .. end without clauses */
+ case NODE_BEGIN:
+ node = node->nd_body;
+ goto again;
+
+ /* nodes for speed-up(default match) */
+ case NODE_MATCH:
+ result = rb_reg_match2(node->nd_lit);
+ break;
+
+ /* nodes for speed-up(literal match) */
+ case NODE_MATCH2:
+ {
+ VALUE l = rb_eval(self,node->nd_recv);
+ VALUE r = rb_eval(self,node->nd_value);
+ result = rb_reg_match(l, r);
+ }
+ break;
+
+ /* nodes for speed-up(literal match) */
+ case NODE_MATCH3:
+ {
+ VALUE r = rb_eval(self,node->nd_recv);
+ VALUE l = rb_eval(self,node->nd_value);
+ if (TYPE(l) == T_STRING) {
+ result = rb_reg_match(r, l);
+ }
+ else {
+ result = rb_funcall(l, match, 1, r);
+ }
+ }
+ break;
+
+ /* node for speed-up(top-level loop for -n/-p) */
+ case NODE_OPT_N:
+ PUSH_TAG(PROT_LOOP);
+ switch (state = EXEC_TAG()) {
+ case 0:
+ opt_n_next:
+ while (!NIL_P(rb_gets())) {
+ opt_n_redo:
+ rb_eval(self, node->nd_body);
+ }
+ break;
+
+ case TAG_REDO:
+ state = 0;
+ goto opt_n_redo;
+ case TAG_NEXT:
+ state = 0;
+ goto opt_n_next;
+ case TAG_BREAK:
+ state = 0;
+ default:
+ break;
+ }
+ POP_TAG();
+ if (state) JUMP_TAG(state);
+ RETURN(Qnil);
+
+ case NODE_SELF:
+ RETURN(self);
+
+ case NODE_NIL:
+ RETURN(Qnil);
+
+ case NODE_TRUE:
+ RETURN(Qtrue);
+
+ case NODE_FALSE:
+ RETURN(Qfalse);
+
+ case NODE_ERRINFO:
+ RETURN(ruby_errinfo);
+
+ case NODE_IF:
+ EXEC_EVENT_HOOK(RUBY_EVENT_LINE, node, self,
+ ruby_frame->this_func,
+ ruby_frame->this_class);
+ if (RTEST(rb_eval(self, node->nd_cond))) {
+ node = node->nd_body;
+ }
+ else {
+ node = node->nd_else;
+ }
+ goto again;
+
+ case NODE_WHEN:
+ while (node) {
+ NODE *tag;
+
+ if (nd_type(node) != NODE_WHEN) goto again;
+ tag = node->nd_head;
+ while (tag) {
+ EXEC_EVENT_HOOK(RUBY_EVENT_LINE, tag, self,
+ ruby_frame->this_func,
+ ruby_frame->this_class);
+ if (tag->nd_head && nd_type(tag->nd_head) == NODE_WHEN) {
+ VALUE v = rb_eval(self, tag->nd_head->nd_head);
+ long i;
+
+ if (TYPE(v) != T_ARRAY) v = rb_ary_to_ary(v);
+ for (i=0; i<RARRAY(v)->len; i++) {
+ if (RTEST(RARRAY(v)->ptr[i])) {
+ node = node->nd_body;
+ goto again;
+ }
+ }
+ tag = tag->nd_next;
+ continue;
+ }
+ if (RTEST(rb_eval(self, tag->nd_head))) {
+ node = node->nd_body;
+ goto again;
+ }
+ tag = tag->nd_next;
+ }
+ node = node->nd_next;
+ }
+ RETURN(Qnil);
+
+ case NODE_CASE:
+ {
+ VALUE val;
+
+ val = rb_eval(self, node->nd_head);
+ node = node->nd_body;
+ while (node) {
+ NODE *tag;
+
+ if (nd_type(node) != NODE_WHEN) {
+ goto again;
+ }
+ tag = node->nd_head;
+ while (tag) {
+ EXEC_EVENT_HOOK(RUBY_EVENT_LINE, tag, self,
+ ruby_frame->this_func,
+ ruby_frame->this_class);
+ if (tag->nd_head && nd_type(tag->nd_head) == NODE_WHEN) {
+ VALUE v = rb_eval(self, tag->nd_head->nd_head);
+ long i;
+
+ if (TYPE(v) != T_ARRAY) v = rb_ary_to_ary(v);
+ for (i=0; i<RARRAY(v)->len; i++) {
+ if (RTEST(rb_funcall2(RARRAY(v)->ptr[i], eqq, 1, &val))){
+ node = node->nd_body;
+ goto again;
+ }
+ }
+ tag = tag->nd_next;
+ continue;
+ }
+ if (RTEST(rb_funcall2(rb_eval(self, tag->nd_head), eqq, 1, &val))) {
+ node = node->nd_body;
+ goto again;
+ }
+ tag = tag->nd_next;
+ }
+ node = node->nd_next;
+ }
+ }
+ RETURN(Qnil);
+
+ case NODE_WHILE:
+ PUSH_TAG(PROT_LOOP);
+ result = Qnil;
+ switch (state = EXEC_TAG()) {
+ case 0:
+ if (node->nd_state && !RTEST(rb_eval(self, node->nd_cond)))
+ goto while_out;
+ do {
+ while_redo:
+ rb_eval(self, node->nd_body);
+ while_next:
+ ;
+ } while (RTEST(rb_eval(self, node->nd_cond)));
+ break;
+
+ case TAG_REDO:
+ state = 0;
+ goto while_redo;
+ case TAG_NEXT:
+ state = 0;
+ goto while_next;
+ case TAG_BREAK:
+ if (TAG_DST()) {
+ state = 0;
+ result = prot_tag->retval;
+ }
+ /* fall through */
+ default:
+ break;
+ }
+ while_out:
+ POP_TAG();
+ if (state) JUMP_TAG(state);
+ RETURN(result);
+
+ case NODE_UNTIL:
+ PUSH_TAG(PROT_LOOP);
+ result = Qnil;
+ switch (state = EXEC_TAG()) {
+ case 0:
+ if (node->nd_state && RTEST(rb_eval(self, node->nd_cond)))
+ goto until_out;
+ do {
+ until_redo:
+ rb_eval(self, node->nd_body);
+ until_next:
+ ;
+ } while (!RTEST(rb_eval(self, node->nd_cond)));
+ break;
+
+ case TAG_REDO:
+ state = 0;
+ goto until_redo;
+ case TAG_NEXT:
+ state = 0;
+ goto until_next;
+ case TAG_BREAK:
+ if (TAG_DST()) {
+ state = 0;
+ result = prot_tag->retval;
+ }
+ /* fall through */
+ default:
+ break;
+ }
+ until_out:
+ POP_TAG();
+ if (state) JUMP_TAG(state);
+ RETURN(result);
+
+ case NODE_BLOCK_PASS:
+ result = block_pass(self, node);
+ break;
+
+ case NODE_ITER:
+ case NODE_FOR:
+ case NODE_LAMBDA:
+ {
+ PUSH_TAG(PROT_LOOP);
+ PUSH_BLOCK(node->nd_var, node->nd_body);
+
+ state = EXEC_TAG();
+ if (state == 0) {
+ iter_retry:
+ PUSH_ITER(ITER_PRE);
+ if (nd_type(node) == NODE_ITER) {
+ result = rb_eval(self, node->nd_iter);
+ }
+ else if (nd_type(node) == NODE_LAMBDA) {
+ ruby_iter->iter = ruby_frame->iter = ITER_CUR;
+ result = rb_block_proc();
+ }
+ else {
+ VALUE recv;
+
+ _block.flags &= ~BLOCK_D_SCOPE;
+ BEGIN_CALLARGS;
+ recv = rb_eval(self, node->nd_iter);
+ END_CALLARGS;
+ ruby_current_node = node;
+ SET_CURRENT_SOURCE();
+ result = rb_call(CLASS_OF(recv),recv,each,0,0,0);
+ }
+ POP_ITER();
+ }
+ else if (state == TAG_BREAK && TAG_DST()) {
+ result = prot_tag->retval;
+ state = 0;
+ }
+ else if (state == TAG_RETRY && ruby_block == &_block) {
+ state = 0;
+ goto iter_retry;
+ }
+ POP_BLOCK();
+ POP_TAG();
+ switch (state) {
+ case 0:
+ break;
+ default:
+ JUMP_TAG(state);
+ }
+ }
+ break;
+
+ case NODE_BREAK:
+ break_jump(rb_eval(self, node->nd_stts));
+ break;
+
+ case NODE_NEXT:
+ CHECK_INTS;
+ return_value(rb_eval(self, node->nd_stts));
+ JUMP_TAG(TAG_NEXT);
+ break;
+
+ case NODE_REDO:
+ CHECK_INTS;
+ JUMP_TAG(TAG_REDO);
+ break;
+
+ case NODE_RETRY:
+ CHECK_INTS;
+ JUMP_TAG(TAG_RETRY);
+ break;
+
+ case NODE_SPLAT:
+ result = splat_value(rb_eval(self, node->nd_head));
+ break;
+
+ case NODE_TO_ARY:
+ result = rb_ary_to_ary(rb_eval(self, node->nd_head));
+ break;
+
+ case NODE_SVALUE:
+ result = avalue_splat(rb_eval(self, node->nd_head));
+ if (result == Qundef) result = Qnil;
+ break;
+
+ case NODE_YIELD:
+ if (node->nd_head) {
+ result = rb_eval(self, node->nd_head);
+ ruby_current_node = node;
+ }
+ else {
+ result = Qundef; /* no arg */
+ }
+ SET_CURRENT_SOURCE();
+ result = rb_yield_0(result, 0, 0, 0, node->nd_state);
+ break;
+
+ case NODE_RESCUE:
+ {
+ volatile VALUE e_info = ruby_errinfo;
+ volatile int rescuing = 0;
+
+ PUSH_TAG(PROT_NONE);
+ if ((state = EXEC_TAG()) == 0) {
+ retry_entry:
+ result = rb_eval(self, node->nd_head);
+ }
+ else if (rescuing) {
+ if (rescuing < 0) {
+ /* in rescue argument, just reraise */
+ }
+ else if (state == TAG_RETRY) {
+ rescuing = state = 0;
+ ruby_errinfo = e_info;
+ goto retry_entry;
+ }
+ else if (state != TAG_RAISE) {
+ result = prot_tag->retval;
+ }
+ }
+ else if (state == TAG_RAISE) {
+ NODE *resq = node->nd_resq;
+
+ rescuing = -1;
+ while (resq) {
+ ruby_current_node = resq;
+ if (handle_rescue(self, resq)) {
+ state = 0;
+ rescuing = 1;
+ result = rb_eval(self, resq->nd_body);
+ break;
+ }
+ resq = resq->nd_head; /* next rescue */
+ }
+ }
+ else {
+ result = prot_tag->retval;
+ }
+ POP_TAG();
+ if (state != TAG_RAISE) ruby_errinfo = e_info;
+ if (state) {
+ if (state == TAG_NEXT) prot_tag->retval = result;
+ JUMP_TAG(state);
+ }
+ /* no exception raised */
+ if (!rescuing && (node = node->nd_else)) { /* else clause given */
+ goto again;
+ }
+ }
+ break;
+
+ case NODE_ENSURE:
+ PUSH_TAG(PROT_NONE);
+ if ((state = EXEC_TAG()) == 0) {
+ result = rb_eval(self, node->nd_head);
+ }
+ POP_TAG();
+ if (node->nd_ensr) {
+ VALUE retval = prot_tag->retval; /* save retval */
+ VALUE errinfo = ruby_errinfo;
+
+ rb_eval(self, node->nd_ensr);
+ return_value(retval);
+ ruby_errinfo = errinfo;
+ }
+ if (state) JUMP_TAG(state);
+ break;
+
+ case NODE_AND:
+ result = rb_eval(self, node->nd_1st);
+ if (!RTEST(result)) break;
+ node = node->nd_2nd;
+ goto again;
+
+ case NODE_OR:
+ result = rb_eval(self, node->nd_1st);
+ if (RTEST(result)) break;
+ node = node->nd_2nd;
+ goto again;
+
+ case NODE_NOT:
+ if (RTEST(rb_eval(self, node->nd_body))) result = Qfalse;
+ else result = Qtrue;
+ break;
+
+ case NODE_DOT2:
+ case NODE_DOT3:
+ result = rb_range_new(rb_eval(self, node->nd_beg),
+ rb_eval(self, node->nd_end),
+ nd_type(node) == NODE_DOT3);
+ break;
+
+ case NODE_FLIP2: /* like AWK */
+ {
+ VALUE *flip = rb_svar(node->nd_cnt);
+ if (!flip) rb_bug("unexpected local variable");
+ if (!RTEST(*flip)) {
+ if (RTEST(rb_eval(self, node->nd_beg))) {
+ *flip = RTEST(rb_eval(self, node->nd_end))?Qfalse:Qtrue;
+ result = Qtrue;
+ }
+ else {
+ result = Qfalse;
+ }
+ }
+ else {
+ if (RTEST(rb_eval(self, node->nd_end))) {
+ *flip = Qfalse;
+ }
+ result = Qtrue;
+ }
+ }
+ break;
+
+ case NODE_FLIP3: /* like SED */
+ {
+ VALUE *flip = rb_svar(node->nd_cnt);
+ if (!flip) rb_bug("unexpected local variable");
+ if (!RTEST(*flip)) {
+ result = RTEST(rb_eval(self, node->nd_beg)) ? Qtrue : Qfalse;
+ *flip = result;
+ }
+ else {
+ if (RTEST(rb_eval(self, node->nd_end))) {
+ *flip = Qfalse;
+ }
+ result = Qtrue;
+ }
+ }
+ break;
+
+ case NODE_RETURN:
+ return_jump(rb_eval(self, node->nd_stts));
+ break;
+
+ case NODE_ARGSCAT:
+ {
+ VALUE args = rb_eval(self, node->nd_head);
+ result = rb_ary_concat(args, splat_value(rb_eval(self, node->nd_body)));
+ }
+ break;
+
+ case NODE_ARGSPUSH:
+ {
+ VALUE args = rb_ary_dup(rb_eval(self, node->nd_head));
+ result = rb_ary_push(args, rb_eval(self, node->nd_body));
+ }
+ break;
+
+ case NODE_ATTRASGN:
+ {
+ VALUE recv;
+ int argc; VALUE *argv; /* used in SETUP_ARGS */
+ int scope;
+ TMP_PROTECT;
+
+ BEGIN_CALLARGS;
+ if (node->nd_recv == (NODE *)1) {
+ recv = self;
+ scope = 1;
+ }
+ else {
+ recv = rb_eval(self, node->nd_recv);
+ scope = 0;
+ }
+ SETUP_ARGS(node->nd_args);
+ END_CALLARGS;
+
+ ruby_current_node = node;
+ SET_CURRENT_SOURCE();
+ rb_call(CLASS_OF(recv),recv,node->nd_mid,argc,argv,scope);
+ result = argv[argc-1];
+ }
+ break;
+
+ case NODE_CALL:
+ {
+ VALUE recv;
+ int argc; VALUE *argv; /* used in SETUP_ARGS */
+ TMP_PROTECT;
+
+ BEGIN_CALLARGS;
+ recv = rb_eval(self, node->nd_recv);
+ SETUP_ARGS(node->nd_args);
+ END_CALLARGS;
+
+ ruby_current_node = node;
+ SET_CURRENT_SOURCE();
+ result = rb_call(CLASS_OF(recv),recv,node->nd_mid,argc,argv,0);
+ }
+ break;
+
+ case NODE_FCALL:
+ {
+ int argc; VALUE *argv; /* used in SETUP_ARGS */
+ TMP_PROTECT;
+
+ BEGIN_CALLARGS;
+ SETUP_ARGS(node->nd_args);
+ END_CALLARGS;
+
+ ruby_current_node = node;
+ SET_CURRENT_SOURCE();
+ result = rb_call(CLASS_OF(self),self,node->nd_mid,argc,argv,1);
+ }
+ break;
+
+ case NODE_VCALL:
+ SET_CURRENT_SOURCE();
+ result = rb_call(CLASS_OF(self),self,node->nd_mid,0,0,2);
+ break;
+
+ case NODE_SUPER:
+ case NODE_ZSUPER:
+ {
+ int argc; VALUE *argv; /* used in SETUP_ARGS */
+ TMP_PROTECT;
+
+ if (ruby_frame->this_class == 0) {
+ if (ruby_frame->this_func) {
+ rb_name_error(ruby_frame->callee,
+ "superclass method `%s' disabled",
+ rb_id2name(ruby_frame->this_func));
+ }
+ else {
+ rb_raise(rb_eNoMethodError, "super called outside of method");
+ }
+ }
+ if (nd_type(node) == NODE_ZSUPER) {
+ argc = ruby_frame->argc;
+ if (argc && ruby_frame->prev &&
+ (ruby_frame->prev->flags & FRAME_DMETH)) {
+ if (TYPE(RBASIC(ruby_scope)->klass) != T_ARRAY ||
+ RARRAY(RBASIC(ruby_scope)->klass)->len != argc) {
+ rb_raise(rb_eRuntimeError,
+ "super: specify arguments explicitly");
+ }
+ argv = RARRAY(RBASIC(ruby_scope)->klass)->ptr;
+ }
+ else {
+ argv = ruby_scope->local_vars + 2;
+ }
+ }
+ else {
+ BEGIN_CALLARGS;
+ SETUP_ARGS(node->nd_args);
+ END_CALLARGS;
+ ruby_current_node = node;
+ }
+
+ SET_CURRENT_SOURCE();
+ result = rb_call_super(argc, argv);
+ }
+ break;
+
+ case NODE_SCOPE:
+ {
+ struct FRAME frame;
+ NODE *saved_cref = 0;
+
+ frame = *ruby_frame;
+ frame.tmp = ruby_frame;
+ ruby_frame = &frame;
+
+ PUSH_SCOPE();
+ PUSH_TAG(PROT_NONE);
+ if (node->nd_rval) {
+ saved_cref = ruby_cref;
+ ruby_cref = (NODE*)node->nd_rval;
+ }
+ if (node->nd_tbl) {
+ VALUE *vars = ALLOCA_N(VALUE, node->nd_tbl[0]+1);
+ *vars++ = (VALUE)node;
+ ruby_scope->local_vars = vars;
+ rb_mem_clear(ruby_scope->local_vars, node->nd_tbl[0]);
+ ruby_scope->local_tbl = node->nd_tbl;
+ }
+ else {
+ ruby_scope->local_vars = 0;
+ ruby_scope->local_tbl = 0;
+ }
+ if ((state = EXEC_TAG()) == 0) {
+ result = rb_eval(self, node->nd_next);
+ }
+ POP_TAG();
+ POP_SCOPE();
+ ruby_frame = frame.tmp;
+ if (saved_cref)
+ ruby_cref = saved_cref;
+ if (state) JUMP_TAG(state);
+ }
+ break;
+
+ case NODE_OP_ASGN1:
+ {
+ int argc; VALUE *argv; /* used in SETUP_ARGS */
+ VALUE recv, val;
+ NODE *rval;
+ TMP_PROTECT;
+
+ recv = rb_eval(self, node->nd_recv);
+ rval = node->nd_args->nd_head;
+ SETUP_ARGS0(node->nd_args->nd_next, node->nd_args->nd_alen - 1);
+ val = rb_funcall2(recv, aref, argc-1, argv);
+ switch (node->nd_mid) {
+ case 0: /* OR */
+ if (RTEST(val)) RETURN(val);
+ val = rb_eval(self, rval);
+ break;
+ case 1: /* AND */
+ if (!RTEST(val)) RETURN(val);
+ val = rb_eval(self, rval);
+ break;
+ default:
+ val = rb_funcall(val, node->nd_mid, 1, rb_eval(self, rval));
+ }
+ argv[argc-1] = val;
+ rb_funcall2(recv, aset, argc, argv);
+ result = val;
+ }
+ break;
+
+ case NODE_OP_ASGN2:
+ {
+ ID id = node->nd_next->nd_vid;
+ VALUE recv, val;
+
+ recv = rb_eval(self, node->nd_recv);
+ val = rb_funcall(recv, id, 0);
+ switch (node->nd_next->nd_mid) {
+ case 0: /* OR */
+ if (RTEST(val)) RETURN(val);
+ val = rb_eval(self, node->nd_value);
+ break;
+ case 1: /* AND */
+ if (!RTEST(val)) RETURN(val);
+ val = rb_eval(self, node->nd_value);
+ break;
+ default:
+ val = rb_funcall(val, node->nd_next->nd_mid, 1,
+ rb_eval(self, node->nd_value));
+ }
+
+ rb_funcall2(recv, node->nd_next->nd_aid, 1, &val);
+ result = val;
+ }
+ break;
+
+ case NODE_OP_ASGN_AND:
+ result = rb_eval(self, node->nd_head);
+ if (!RTEST(result)) break;
+ node = node->nd_value;
+ goto again;
+
+ case NODE_OP_ASGN_OR:
+ if ((node->nd_aid && !is_defined(self, node->nd_head, 0, 0)) ||
+ !RTEST(result = rb_eval(self, node->nd_head))) {
+ node = node->nd_value;
+ goto again;
+ }
+ break;
+
+ case NODE_MASGN:
+ result = massign(self, node, rb_eval(self, node->nd_value), 0);
+ break;
+
+ case NODE_LASGN:
+ if (ruby_scope->local_vars == 0)
+ rb_bug("unexpected local variable assignment");
+ result = rb_eval(self, node->nd_value);
+ ruby_scope->local_vars[node->nd_cnt] = result;
+ break;
+
+ case NODE_DASGN:
+ result = rb_eval(self, node->nd_value);
+ dvar_asgn(node->nd_vid, result);
+ break;
+
+ case NODE_DASGN_CURR:
+ result = rb_eval(self, node->nd_value);
+ dvar_asgn_curr(node->nd_vid, result);
+ break;
+
+ case NODE_GASGN:
+ result = rb_eval(self, node->nd_value);
+ rb_gvar_set(node->nd_entry, result);
+ break;
+
+ case NODE_IASGN:
+ result = rb_eval(self, node->nd_value);
+ rb_ivar_set(self, node->nd_vid, result);
+ break;
+
+ case NODE_CDECL:
+ result = rb_eval(self, node->nd_value);
+ if (node->nd_vid == 0) {
+ rb_const_set(class_prefix(self, node->nd_else), node->nd_else->nd_mid, result);
+ }
+ else {
+ if (NIL_P(ruby_cbase)) {
+ rb_raise(rb_eTypeError, "no class/module to define constant");
+ }
+ rb_const_set(ruby_cbase, node->nd_vid, result);
+ }
+ break;
+
+ case NODE_CVDECL:
+ if (NIL_P(ruby_cbase)) {
+ rb_raise(rb_eTypeError, "no class/module to define class variable");
+ }
+ result = rb_eval(self, node->nd_value);
+ rb_cvar_set(cvar_cbase(), node->nd_vid, result, Qtrue);
+ break;
+
+ case NODE_CVASGN:
+ result = rb_eval(self, node->nd_value);
+ rb_cvar_set(cvar_cbase(), node->nd_vid, result, Qfalse);
+ break;
+
+ case NODE_LVAR:
+ if (ruby_scope->local_vars == 0) {
+ rb_bug("unexpected local variable");
+ }
+ result = ruby_scope->local_vars[node->nd_cnt];
+ break;
+
+ case NODE_DVAR:
+ result = rb_dvar_ref(node->nd_vid);
+ break;
+
+ case NODE_GVAR:
+ result = rb_gvar_get(node->nd_entry);
+ break;
+
+ case NODE_IVAR:
+ result = rb_ivar_get(self, node->nd_vid);
+ break;
+
+ case NODE_CONST:
+ result = ev_const_get(ruby_cref, node->nd_vid, self);
+ break;
+
+ case NODE_CVAR:
+ result = rb_cvar_get(cvar_cbase(), node->nd_vid);
+ break;
+
+ case NODE_BLOCK_ARG:
+ if (ruby_scope->local_vars == 0)
+ rb_bug("unexpected block argument");
+ if (rb_block_given_p()) {
+ result = rb_block_proc();
+ ruby_scope->local_vars[node->nd_cnt] = result;
+ }
+ else {
+ result = Qnil;
+ }
+ break;
+
+ case NODE_COLON2:
+ {
+ VALUE klass;
+
+ klass = rb_eval(self, node->nd_head);
+ if (rb_is_const_id(node->nd_mid)) {
+ switch (TYPE(klass)) {
+ case T_CLASS:
+ case T_MODULE:
+ result = rb_const_get_from(klass, node->nd_mid);
+ break;
+ default:
+ rb_raise(rb_eTypeError, "%s is not a class/module",
+ RSTRING(rb_obj_as_string(klass))->ptr);
+ break;
+ }
+ }
+ else {
+ result = rb_funcall(klass, node->nd_mid, 0, 0);
+ }
+ }
+ break;
+
+ case NODE_COLON3:
+ result = rb_const_get_from(rb_cObject, node->nd_mid);
+ break;
+
+ case NODE_NTH_REF:
+ result = rb_reg_nth_match(node->nd_nth, MATCH_DATA);
+ break;
+
+ case NODE_BACK_REF:
+ switch (node->nd_nth) {
+ case '&':
+ result = rb_reg_last_match(MATCH_DATA);
+ break;
+ case '`':
+ result = rb_reg_match_pre(MATCH_DATA);
+ break;
+ case '\'':
+ result = rb_reg_match_post(MATCH_DATA);
+ break;
+ case '+':
+ result = rb_reg_match_last(MATCH_DATA);
+ break;
+ default:
+ rb_bug("unexpected back-ref");
+ }
+ break;
+
+ case NODE_HASH:
+ {
+ NODE *list;
+ VALUE hash = rb_hash_new();
+ VALUE key, val;
+
+ list = node->nd_head;
+ while (list) {
+ key = rb_eval(self, list->nd_head);
+ list = list->nd_next;
+ if (list == 0)
+ rb_bug("odd number list for Hash");
+ val = rb_eval(self, list->nd_head);
+ list = list->nd_next;
+ rb_hash_aset(hash, key, val);
+ }
+ result = hash;
+ }
+ break;
+
+ case NODE_ZARRAY: /* zero length list */
+ result = rb_ary_new();
+ break;
+
+ case NODE_ARRAY:
+ {
+ VALUE ary;
+ long i;
+
+ i = node->nd_alen;
+ ary = rb_ary_new2(i);
+ for (i=0;node;node=node->nd_next) {
+ RARRAY(ary)->ptr[i++] = rb_eval(self, node->nd_head);
+ RARRAY(ary)->len = i;
+ }
+
+ result = ary;
+ }
+ break;
+
+ case NODE_VALUES:
+ {
+ VALUE val;
+ long i;
+
+ i = node->nd_alen;
+ val = rb_values_new2(i, 0);
+ for (i=0;node;node=node->nd_next) {
+ RARRAY(val)->ptr[i++] = rb_eval(self, node->nd_head);
+ RARRAY(val)->len = i;
+ }
+
+ result = val;
+ }
+ break;
+
+ case NODE_STR:
+ result = rb_str_new3(node->nd_lit);
+ break;
+
+ case NODE_EVSTR:
+ result = rb_obj_as_string(rb_eval(self, node->nd_body));
+ break;
+
+ case NODE_DSTR:
+ case NODE_DXSTR:
+ case NODE_DREGX:
+ case NODE_DREGX_ONCE:
+ case NODE_DSYM:
+ {
+ VALUE str, str2;
+ NODE *list = node->nd_next;
+
+ str = rb_str_new3(node->nd_lit);
+ while (list) {
+ if (list->nd_head) {
+ switch (nd_type(list->nd_head)) {
+ case NODE_STR:
+ str2 = list->nd_head->nd_lit;
+ break;
+ default:
+ str2 = rb_eval(self, list->nd_head);
+ break;
+ }
+ rb_str_append(str, str2);
+ OBJ_INFECT(str, str2);
+ }
+ list = list->nd_next;
+ }
+ switch (nd_type(node)) {
+ case NODE_DREGX:
+ result = rb_reg_new(RSTRING(str)->ptr, RSTRING(str)->len,
+ node->nd_cflag);
+ break;
+ case NODE_DREGX_ONCE: /* regexp expand once */
+ result = rb_reg_new(RSTRING(str)->ptr, RSTRING(str)->len,
+ node->nd_cflag);
+ nd_set_type(node, NODE_LIT);
+ node->nd_lit = result;
+ break;
+ case NODE_LIT:
+ /* other thread may replace NODE_DREGX_ONCE to NODE_LIT */
+ goto again;
+ case NODE_DXSTR:
+ result = rb_funcall(self, '`', 1, str);
+ break;
+ case NODE_DSYM:
+ result = rb_str_intern(str);
+ break;
+ default:
+ result = str;
+ break;
+ }
+ }
+ break;
+
+ case NODE_XSTR:
+ result = rb_funcall(self, '`', 1, rb_str_new3(node->nd_lit));
+ break;
+
+ case NODE_LIT:
+ result = node->nd_lit;
+ break;
+
+ case NODE_DEFN:
+ if (node->nd_defn) {
+ NODE *body, *defn;
+ VALUE origin;
+ int noex;
+
+ if (NIL_P(ruby_class)) {
+ rb_raise(rb_eTypeError, "no class/module to add method");
+ }
+ if (ruby_class == rb_cObject && node->nd_mid == init) {
+ rb_warn("redefining Object#initialize may cause infinite loop");
+ }
+ if (node->nd_mid == __id__ || node->nd_mid == __send__) {
+ rb_warn("redefining `%s' may cause serious problem",
+ rb_id2name(node->nd_mid));
+ }
+ rb_frozen_class_p(ruby_class);
+ body = search_method(ruby_class, node->nd_mid, &origin);
+ if (body){
+ if (RTEST(ruby_verbose) && ruby_class == origin && body->nd_cnt == 0 && body->nd_body) {
+ rb_warning("method redefined; discarding old %s", rb_id2name(node->nd_mid));
+ }
+ }
+
+ if (SCOPE_TEST(SCOPE_PRIVATE) || node->nd_mid == init) {
+ noex = NOEX_PRIVATE;
+ }
+ else if (SCOPE_TEST(SCOPE_PROTECTED)) {
+ noex = NOEX_PROTECTED;
+ }
+ else {
+ noex = NOEX_PUBLIC;
+ }
+ if (body && origin == ruby_class && body->nd_body == 0) {
+ noex |= NOEX_NOSUPER;
+ }
+
+ defn = copy_node_scope(node->nd_defn, ruby_cref);
+ rb_add_method(ruby_class, node->nd_mid, defn, noex);
+ if (scope_vmode == SCOPE_MODFUNC) {
+ rb_add_method(rb_singleton_class(ruby_class),
+ node->nd_mid, defn, NOEX_PUBLIC);
+ }
+ result = Qnil;
+ }
+ break;
+
+ case NODE_DEFS:
+ if (node->nd_defn) {
+ VALUE recv = rb_eval(self, node->nd_recv);
+ VALUE klass;
+ NODE *body = 0, *defn;
+
+ if (ruby_safe_level >= 4 && !OBJ_TAINTED(recv)) {
+ rb_raise(rb_eSecurityError, "Insecure: can't define singleton method");
+ }
+ if (FIXNUM_P(recv) || SYMBOL_P(recv)) {
+ rb_raise(rb_eTypeError,
+ "can't define singleton method \"%s\" for %s",
+ rb_id2name(node->nd_mid),
+ rb_obj_classname(recv));
+ }
+
+ if (OBJ_FROZEN(recv)) rb_error_frozen("object");
+ klass = rb_singleton_class(recv);
+ if (st_lookup(RCLASS(klass)->m_tbl, node->nd_mid, (st_data_t *)&body)) {
+ if (ruby_safe_level >= 4) {
+ rb_raise(rb_eSecurityError, "redefining method prohibited");
+ }
+ if (RTEST(ruby_verbose)) {
+ rb_warning("redefine %s", rb_id2name(node->nd_mid));
+ }
+ }
+ defn = copy_node_scope(node->nd_defn, ruby_cref);
+ rb_add_method(klass, node->nd_mid, defn,
+ NOEX_PUBLIC|(body?body->nd_noex&NOEX_UNDEF:0));
+ result = Qnil;
+ }
+ break;
+
+ case NODE_UNDEF:
+ if (NIL_P(ruby_class)) {
+ rb_raise(rb_eTypeError, "no class to undef method");
+ }
+ rb_undef(ruby_class, rb_to_id(rb_eval(self, node->u2.node)));
+ result = Qnil;
+ break;
+
+ case NODE_ALIAS:
+ if (NIL_P(ruby_class)) {
+ rb_raise(rb_eTypeError, "no class to make alias");
+ }
+ rb_alias(ruby_class, rb_to_id(rb_eval(self, node->u1.node)),
+ rb_to_id(rb_eval(self, node->u2.node)));
+ result = Qnil;
+ break;
+
+ case NODE_VALIAS:
+ rb_alias_variable(node->u1.id, node->u2.id);
+ result = Qnil;
+ break;
+
+ case NODE_CLASS:
+ {
+ VALUE super, klass, tmp, cbase;
+ ID cname;
+ int gen = Qfalse;
+
+ cbase = class_prefix(self, node->nd_cpath);
+ cname = node->nd_cpath->nd_mid;
+
+ if (NIL_P(ruby_cbase)) {
+ rb_raise(rb_eTypeError, "no outer class/module");
+ }
+ if (node->nd_super) {
+ super = rb_eval(self, node->nd_super);
+ rb_check_inheritable(super);
+ }
+ else {
+ super = 0;
+ }
+
+ if (rb_const_defined_at(cbase, cname)) {
+ klass = rb_const_get_at(cbase, cname);
+ if (TYPE(klass) != T_CLASS) {
+ rb_raise(rb_eTypeError, "%s is not a class",
+ rb_id2name(cname));
+ }
+ if (super) {
+ tmp = rb_class_real(RCLASS(klass)->super);
+ if (tmp != super) {
+ rb_raise(rb_eTypeError, "superclass mismatch for class %s",
+ rb_id2name(cname));
+ }
+ super = 0;
+ }
+ if (ruby_safe_level >= 4) {
+ rb_raise(rb_eSecurityError, "extending class prohibited");
+ }
+ }
+ else {
+ if (!super) super = rb_cObject;
+ klass = rb_define_class_id(cname, super);
+ rb_set_class_path(klass, cbase, rb_id2name(cname));
+ rb_const_set(cbase, cname, klass);
+ gen = Qtrue;
+ }
+ if (ruby_wrapper) {
+ rb_extend_object(klass, ruby_wrapper);
+ rb_include_module(klass, ruby_wrapper);
+ }
+ if (super && gen) {
+ rb_class_inherited(super, klass);
+ }
+ result = module_setup(klass, node);
+ }
+ break;
+
+ case NODE_MODULE:
+ {
+ VALUE module, cbase;
+ ID cname;
+
+ if (NIL_P(ruby_cbase)) {
+ rb_raise(rb_eTypeError, "no outer class/module");
+ }
+ cbase = class_prefix(self, node->nd_cpath);
+ cname = node->nd_cpath->nd_mid;
+ if (rb_const_defined_at(cbase, cname)) {
+ module = rb_const_get_at(cbase, cname);
+ if (TYPE(module) != T_MODULE) {
+ rb_raise(rb_eTypeError, "%s is not a module",
+ rb_id2name(cname));
+ }
+ if (ruby_safe_level >= 4) {
+ rb_raise(rb_eSecurityError, "extending module prohibited");
+ }
+ }
+ else {
+ module = rb_define_module_id(cname);
+ rb_set_class_path(module, cbase, rb_id2name(cname));
+ rb_const_set(cbase, cname, module);
+ }
+ if (ruby_wrapper) {
+ rb_extend_object(module, ruby_wrapper);
+ rb_include_module(module, ruby_wrapper);
+ }
+
+ result = module_setup(module, node);
+ }
+ break;
+
+ case NODE_SCLASS:
+ {
+ VALUE klass;
+
+ result = rb_eval(self, node->nd_recv);
+ if (FIXNUM_P(result) || SYMBOL_P(result)) {
+ rb_raise(rb_eTypeError, "no singleton class for %s",
+ rb_obj_classname(result));
+ }
+ if (ruby_safe_level >= 4 && !OBJ_TAINTED(result))
+ rb_raise(rb_eSecurityError, "Insecure: can't extend object");
+ klass = rb_singleton_class(result);
+
+ if (ruby_wrapper) {
+ rb_extend_object(klass, ruby_wrapper);
+ rb_include_module(klass, ruby_wrapper);
+ }
+
+ result = module_setup(klass, node);
+ }
+ break;
+
+ case NODE_DEFINED:
+ {
+ char buf[20];
+ const char *desc = is_defined(self, node->nd_head, buf, 0);
+
+ if (desc) result = rb_str_new2(desc);
+ else result = Qnil;
+ }
+ break;
+
+ default:
+ rb_bug("unknown node type %d", nd_type(node));
+ }
+ finish:
+ CHECK_INTS;
+ if (contnode) {
+ node = contnode;
+ contnode = 0;
+ goto again;
+ }
+ return result;
+}
+
+static VALUE
+module_setup(module, n)
+ VALUE module;
+ NODE *n;
+{
+ NODE * volatile node = n->nd_body;
+ int state;
+ struct FRAME frame;
+ VALUE result = Qnil; /* OK */
+ TMP_PROTECT;
+
+ frame = *ruby_frame;
+ frame.tmp = ruby_frame;
+ ruby_frame = &frame;
+
+ PUSH_CLASS(module);
+ PUSH_SCOPE();
+ PUSH_VARS();
+
+ if (node->nd_tbl) {
+ VALUE *vars = TMP_ALLOC(node->nd_tbl[0]+1);
+ *vars++ = (VALUE)node;
+ ruby_scope->local_vars = vars;
+ rb_mem_clear(ruby_scope->local_vars, node->nd_tbl[0]);
+ ruby_scope->local_tbl = node->nd_tbl;
+ }
+ else {
+ ruby_scope->local_vars = 0;
+ ruby_scope->local_tbl = 0;
+ }
+
+ PUSH_CREF(module);
+ PUSH_TAG(PROT_NONE);
+ if ((state = EXEC_TAG()) == 0) {
+ EXEC_EVENT_HOOK(RUBY_EVENT_CLASS, n, ruby_cbase,
+ ruby_frame->this_func, ruby_frame->this_class);
+ result = rb_eval(ruby_cbase, node->nd_next);
+ }
+ POP_TAG();
+ POP_CREF();
+ POP_VARS();
+ POP_SCOPE();
+ POP_CLASS();
+
+ ruby_frame = frame.tmp;
+ EXEC_EVENT_HOOK(RUBY_EVENT_END, n, 0, ruby_frame->this_func,
+ ruby_frame->this_class);
+ if (state) JUMP_TAG(state);
+
+ return result;
+}
+
+static NODE *basic_respond_to = 0;
+
+int
+rb_respond_to(obj, id)
+ VALUE obj;
+ ID id;
+{
+ VALUE klass = CLASS_OF(obj);
+ if (rb_method_node(klass, respond_to) == basic_respond_to &&
+ rb_method_boundp(klass, id, 0)) {
+ return Qtrue;
+ }
+ else{
+ return rb_funcall(obj, respond_to, 1, ID2SYM(id));
+ }
+ return Qfalse;
+}
+
+
+/*
+ * call-seq:
+ * obj.respond_to?(symbol, include_private=false) => true or false
+ *
+ * Returns +true+> if _obj_ responds to the given
+ * method. Private methods are included in the search only if the
+ * optional second parameter evaluates to +true+.
+ */
+
+static VALUE
+rb_obj_respond_to(argc, argv, obj)
+ int argc;
+ VALUE *argv;
+ VALUE obj;
+{
+ VALUE mid, priv;
+ ID id;
+
+ rb_scan_args(argc, argv, "11", &mid, &priv);
+ id = rb_to_id(mid);
+ if (rb_method_boundp(CLASS_OF(obj), id, !RTEST(priv))) {
+ return Qtrue;
+ }
+ return Qfalse;
+}
+
+/*
+ * call-seq:
+ * mod.method_defined?(symbol) => true or false
+ *
+ * Returns +true+ if the named method is defined by
+ * _mod_ (or its included modules and, if _mod_ is a class,
+ * its ancestors). Public and protected methods are matched.
+ *
+ * module A
+ * def method1() end
+ * end
+ * class B
+ * def method2() end
+ * end
+ * class C < B
+ * include A
+ * def method3() end
+ * end
+ *
+ * A.method_defined? :method1 #=> true
+ * C.method_defined? "method1" #=> true
+ * C.method_defined? "method2" #=> true
+ * C.method_defined? "method3" #=> true
+ * C.method_defined? "method4" #=> false
+ */
+
+static VALUE
+rb_mod_method_defined(mod, mid)
+ VALUE mod, mid;
+{
+ return rb_method_boundp(mod, rb_to_id(mid), 1);
+}
+
+#define VISI_CHECK(x,f) (((x)&NOEX_MASK) == (f))
+
+/*
+ * call-seq:
+ * mod.public_method_defined?(symbol) => true or false
+ *
+ * Returns +true+ if the named public method is defined by
+ * _mod_ (or its included modules and, if _mod_ is a class,
+ * its ancestors).
+ *
+ * module A
+ * def method1() end
+ * end
+ * class B
+ * protected
+ * def method2() end
+ * end
+ * class C < B
+ * include A
+ * def method3() end
+ * end
+ *
+ * A.method_defined? :method1 #=> true
+ * C.public_method_defined? "method1" #=> true
+ * C.public_method_defined? "method2" #=> false
+ * C.method_defined? "method2" #=> true
+ */
+
+static VALUE
+rb_mod_public_method_defined(mod, mid)
+ VALUE mod, mid;
+{
+ ID id = rb_to_id(mid);
+ int noex;
+
+ if (rb_get_method_body(&mod, &id, &noex)) {
+ if (VISI_CHECK(noex, NOEX_PUBLIC))
+ return Qtrue;
+ }
+ return Qfalse;
+}
+
+/*
+ * call-seq:
+ * mod.private_method_defined?(symbol) => true or false
+ *
+ * Returns +true+ if the named private method is defined by
+ * _ mod_ (or its included modules and, if _mod_ is a class,
+ * its ancestors).
+ *
+ * module A
+ * def method1() end
+ * end
+ * class B
+ * private
+ * def method2() end
+ * end
+ * class C < B
+ * include A
+ * def method3() end
+ * end
+ *
+ * A.method_defined? :method1 #=> true
+ * C.private_method_defined? "method1" #=> false
+ * C.private_method_defined? "method2" #=> true
+ * C.method_defined? "method2" #=> false
+ */
+
+static VALUE
+rb_mod_private_method_defined(mod, mid)
+ VALUE mod, mid;
+{
+ ID id = rb_to_id(mid);
+ int noex;
+
+ if (rb_get_method_body(&mod, &id, &noex)) {
+ if (VISI_CHECK(noex, NOEX_PRIVATE))
+ return Qtrue;
+ }
+ return Qfalse;
+}
+
+/*
+ * call-seq:
+ * mod.protected_method_defined?(symbol) => true or false
+ *
+ * Returns +true+ if the named protected method is defined
+ * by _mod_ (or its included modules and, if _mod_ is a
+ * class, its ancestors).
+ *
+ * module A
+ * def method1() end
+ * end
+ * class B
+ * protected
+ * def method2() end
+ * end
+ * class C < B
+ * include A
+ * def method3() end
+ * end
+ *
+ * A.method_defined? :method1 #=> true
+ * C.protected_method_defined? "method1" #=> false
+ * C.protected_method_defined? "method2" #=> true
+ * C.method_defined? "method2" #=> true
+ */
+
+static VALUE
+rb_mod_protected_method_defined(mod, mid)
+ VALUE mod, mid;
+{
+ ID id = rb_to_id(mid);
+ int noex;
+
+ if (rb_get_method_body(&mod, &id, &noex)) {
+ if (VISI_CHECK(noex, NOEX_PROTECTED))
+ return Qtrue;
+ }
+ return Qfalse;
+}
+
+NORETURN(static VALUE terminate_process _((int, const char *, long)));
+static VALUE
+terminate_process(status, mesg, mlen)
+ int status;
+ const char *mesg;
+ long mlen;
+{
+ VALUE args[2];
+ args[0] = INT2NUM(status);
+ args[1] = rb_str_new(mesg, mlen);
+
+ rb_exc_raise(rb_class_new_instance(2, args, rb_eSystemExit));
+}
+
+void
+rb_exit(status)
+ int status;
+{
+ if (prot_tag) {
+ terminate_process(status, "exit", 4);
+ }
+ ruby_finalize();
+ exit(status);
+}
+
+
+/*
+ * call-seq:
+ * exit(integer=0)
+ * Kernel::exit(integer=0)
+ * Process::exit(integer=0)
+ *
+ * Initiates the termination of the Ruby script by raising the
+ * <code>SystemExit</code> exception. This exception may be caught. The
+ * optional parameter is used to return a status code to the invoking
+ * environment.
+ *
+ * begin
+ * exit
+ * puts "never get here"
+ * rescue SystemExit
+ * puts "rescued a SystemExit exception"
+ * end
+ * puts "after begin block"
+ *
+ * <em>produces:</em>
+ *
+ * rescued a SystemExit exception
+ * after begin block
+ *
+ * Just prior to termination, Ruby executes any <code>at_exit</code> functions
+ * (see Kernel::at_exit) and runs any object finalizers (see
+ * ObjectSpace::define_finalizer).
+ *
+ * at_exit { puts "at_exit function" }
+ * ObjectSpace.define_finalizer("string", proc { puts "in finalizer" })
+ * exit
+ *
+ * <em>produces:</em>
+ *
+ * at_exit function
+ * in finalizer
+ */
+
+VALUE
+rb_f_exit(argc, argv)
+ int argc;
+ VALUE *argv;
+{
+ VALUE status;
+ int istatus;
+
+ rb_secure(4);
+ if (rb_scan_args(argc, argv, "01", &status) == 1) {
+ switch (status) {
+ case Qtrue:
+ istatus = EXIT_SUCCESS;
+ break;
+ case Qfalse:
+ istatus = EXIT_FAILURE;
+ break;
+ default:
+ istatus = NUM2INT(status);
+ break;
+ }
+ }
+ else {
+ istatus = EXIT_SUCCESS;
+ }
+ rb_exit(istatus);
+ return Qnil; /* not reached */
+}
+
+
+/*
+ * call-seq:
+ * abort
+ * Kernel::abort
+ * Process::abort
+ *
+ * Terminate execution immediately, effectively by calling
+ * <code>Kernel.exit(1)</code>. If _msg_ is given, it is written
+ * to STDERR prior to terminating.
+ */
+
+VALUE
+rb_f_abort(argc, argv)
+ int argc;
+ VALUE *argv;
+{
+ rb_secure(4);
+ if (argc == 0) {
+ if (!NIL_P(ruby_errinfo)) {
+ error_print();
+ }
+ rb_exit(EXIT_FAILURE);
+ }
+ else {
+ VALUE mesg;
+
+ rb_scan_args(argc, argv, "1", &mesg);
+ StringValue(argv[0]);
+ rb_io_puts(argc, argv, rb_stderr);
+ terminate_process(EXIT_FAILURE, RSTRING(argv[0])->ptr, RSTRING(argv[0])->len);
+ }
+ return Qnil; /* not reached */
+}
+
+void
+rb_iter_break()
+{
+ break_jump(Qnil);
+}
+
+NORETURN(static void rb_longjmp _((int, VALUE)));
+static VALUE make_backtrace _((void));
+
+static void
+rb_longjmp(tag, mesg)
+ int tag;
+ VALUE mesg;
+{
+ VALUE at;
+
+ if (thread_set_raised()) {
+ ruby_errinfo = exception_error;
+ JUMP_TAG(TAG_FATAL);
+ }
+ if (NIL_P(mesg)) mesg = ruby_errinfo;
+ if (NIL_P(mesg)) {
+ mesg = rb_exc_new(rb_eRuntimeError, 0, 0);
+ }
+
+ ruby_set_current_source();
+ if (ruby_sourcefile && !NIL_P(mesg)) {
+ at = get_backtrace(mesg);
+ if (NIL_P(at)) {
+ at = make_backtrace();
+ set_backtrace(mesg, at);
+ }
+ }
+ if (!NIL_P(mesg)) {
+ ruby_errinfo = mesg;
+ }
+
+ if (RTEST(ruby_debug) && !NIL_P(ruby_errinfo)
+ && !rb_obj_is_kind_of(ruby_errinfo, rb_eSystemExit)) {
+ VALUE e = ruby_errinfo;
+ int status;
+
+ PUSH_TAG(PROT_NONE);
+ if ((status = EXEC_TAG()) == 0) {
+ e = rb_obj_as_string(e);
+ warn_printf("Exception `%s' at %s:%d - %s\n",
+ rb_obj_classname(ruby_errinfo),
+ ruby_sourcefile, ruby_sourceline,
+ RSTRING(e)->ptr);
+ }
+ POP_TAG();
+ if (status == TAG_FATAL && ruby_errinfo == exception_error) {
+ ruby_errinfo = mesg;
+ }
+ else if (status) {
+ thread_reset_raised();
+ JUMP_TAG(status);
+ }
+ }
+
+ rb_trap_restore_mask();
+ if (tag != TAG_FATAL) {
+ EXEC_EVENT_HOOK(RUBY_EVENT_RAISE, ruby_current_node,
+ ruby_frame->self,
+ ruby_frame->this_func,
+ ruby_frame->this_class);
+ }
+ if (!prot_tag) {
+ error_print();
+ }
+ thread_reset_raised();
+ JUMP_TAG(tag);
+}
+
+void
+rb_exc_raise(mesg)
+ VALUE mesg;
+{
+ rb_longjmp(TAG_RAISE, mesg);
+}
+
+void
+rb_exc_fatal(mesg)
+ VALUE mesg;
+{
+ rb_longjmp(TAG_FATAL, mesg);
+}
+
+void
+rb_interrupt()
+{
+ rb_raise(rb_eInterrupt, "");
+}
+
+/*
+ * call-seq:
+ * raise
+ * raise(string)
+ * raise(exception [, string [, array]])
+ * fail
+ * fail(string)
+ * fail(exception [, string [, array]])
+ *
+ * With no arguments, raises the exception in <code>$!</code> or raises
+ * a <code>RuntimeError</code> if <code>$!</code> is +nil+.
+ * With a single +String+ argument, raises a
+ * +RuntimeError+ with the string as a message. Otherwise,
+ * the first parameter should be the name of an +Exception+
+ * class (or an object that returns an +Exception+ object when sent
+ * an +exception+ message). The optional second parameter sets the
+ * message associated with the exception, and the third parameter is an
+ * array of callback information. Exceptions are caught by the
+ * +rescue+ clause of <code>begin...end</code> blocks.
+ *
+ * raise "Failed to create socket"
+ * raise ArgumentError, "No parameters", caller
+ */
+
+static VALUE
+rb_f_raise(argc, argv)
+ int argc;
+ VALUE *argv;
+{
+ rb_raise_jump(rb_make_exception(argc, argv));
+ return Qnil; /* not reached */
+}
+
+static VALUE
+rb_make_exception(argc, argv)
+ int argc;
+ VALUE *argv;
+{
+ VALUE mesg;
+ ID exception;
+ int n;
+
+ mesg = Qnil;
+ switch (argc) {
+ case 0:
+ mesg = Qnil;
+ break;
+ case 1:
+ if (NIL_P(argv[0])) break;
+ if (TYPE(argv[0]) == T_STRING) {
+ mesg = rb_exc_new3(rb_eRuntimeError, argv[0]);
+ break;
+ }
+ n = 0;
+ goto exception_call;
+
+ case 2:
+ case 3:
+ n = 1;
+ exception_call:
+ exception = rb_intern("exception");
+ if (!rb_respond_to(argv[0], exception)) {
+ rb_raise(rb_eTypeError, "exception class/object expected");
+ }
+ mesg = rb_funcall(argv[0], exception, n, argv[1]);
+ break;
+ default:
+ rb_raise(rb_eArgError, "wrong number of arguments");
+ break;
+ }
+ if (argc > 0) {
+ if (!rb_obj_is_kind_of(mesg, rb_eException))
+ rb_raise(rb_eTypeError, "exception object expected");
+ if (argc>2)
+ set_backtrace(mesg, argv[2]);
+ }
+
+ return mesg;
+}
+
+static void
+rb_raise_jump(mesg)
+ VALUE mesg;
+{
+ if (ruby_frame != top_frame) {
+ PUSH_FRAME(); /* fake frame */
+ *ruby_frame = *_frame.prev->prev;
+ rb_longjmp(TAG_RAISE, mesg);
+ POP_FRAME();
+ }
+ rb_longjmp(TAG_RAISE, mesg);
+}
+
+void
+rb_jump_tag(tag)
+ int tag;
+{
+ JUMP_TAG(tag);
+}
+
+int
+rb_block_given_p()
+{
+ if (ruby_frame->iter == ITER_CUR && ruby_block)
+ return Qtrue;
+ return Qfalse;
+}
+
+int
+rb_iterator_p()
+{
+ return rb_block_given_p();
+}
+
+/*
+ * call-seq:
+ * block_given? => true or false
+ * iterator? => true or false
+ *
+ * Returns <code>true</code> if <code>yield</code> would execute a
+ * block in the current context. The <code>iterator?</code> form
+ * is mildly deprecated.
+ *
+ * def try
+ * if block_given?
+ * yield
+ * else
+ * "no block"
+ * end
+ * end
+ * try #=> "no block"
+ * try { "hello" } #=> "hello"
+ * try do "hello" end #=> "hello"
+ */
+
+
+static VALUE
+rb_f_block_given_p()
+{
+ if (ruby_frame->prev && ruby_frame->prev->iter == ITER_CUR && ruby_block)
+ return Qtrue;
+ return Qfalse;
+}
+
+static VALUE rb_eThreadError;
+
+NORETURN(static void proc_jump_error(int, VALUE));
+static void
+proc_jump_error(state, result)
+ int state;
+ VALUE result;
+{
+ char mesg[32];
+ char *statement;
+
+ switch (state) {
+ case TAG_BREAK:
+ statement = "break"; break;
+ case TAG_RETURN:
+ statement = "return"; break;
+ case TAG_RETRY:
+ statement = "retry"; break;
+ default:
+ statement = "local-jump"; break; /* should not happen */
+ }
+ snprintf(mesg, sizeof mesg, "%s from proc-closure", statement);
+ localjump_error(mesg, result, state);
+}
+
+NORETURN(static void return_jump(VALUE));
+static void
+return_jump(retval)
+ VALUE retval;
+{
+ struct tag *tt = prot_tag;
+ int yield = Qfalse;
+
+ if (retval == Qundef) retval = Qnil;
+ while (tt) {
+ if (tt->tag == PROT_YIELD) {
+ yield = Qtrue;
+ tt = tt->prev;
+ }
+ if ((tt->tag == PROT_FUNC && tt->frame->uniq == ruby_frame->uniq) ||
+ (tt->tag == PROT_LAMBDA && !yield))
+ {
+ tt->dst = (VALUE)tt->frame->uniq;
+ tt->retval = retval;
+ JUMP_TAG(TAG_RETURN);
+ }
+ if (tt->tag == PROT_THREAD) {
+ rb_raise(rb_eThreadError, "return can't jump across threads");
+ }
+ tt = tt->prev;
+ }
+ localjump_error("unexpected return", retval, TAG_RETURN);
+}
+
+static void
+break_jump(retval)
+ VALUE retval;
+{
+ struct tag *tt = prot_tag;
+
+ if (retval == Qundef) retval = Qnil;
+ while (tt) {
+ switch (tt->tag) {
+ case PROT_THREAD:
+ case PROT_YIELD:
+ case PROT_LOOP:
+ case PROT_LAMBDA:
+ tt->dst = (VALUE)tt->frame->uniq;
+ tt->retval = retval;
+ JUMP_TAG(TAG_BREAK);
+ break;
+ default:
+ break;
+ }
+ tt = tt->prev;
+ }
+ localjump_error("unexpected break", retval, TAG_BREAK);
+}
+
+static VALUE bmcall _((VALUE, VALUE));
+static int method_arity _((VALUE));
+
+static VALUE
+rb_yield_0(val, self, klass, flags, avalue)
+ VALUE val, self, klass; /* OK */
+ int flags, avalue;
+{
+ NODE *node, *var;
+ volatile VALUE result = Qnil;
+ volatile VALUE old_cref;
+ volatile VALUE old_wrapper;
+ struct BLOCK * volatile block;
+ struct SCOPE * volatile old_scope;
+ int old_vmode;
+ struct FRAME frame;
+ NODE *cnode = ruby_current_node;
+ int lambda = flags & YIELD_LAMBDA_CALL;
+ int state;
+
+ if (!rb_block_given_p()) {
+ localjump_error("no block given", Qnil, 0);
+ }
+
+ PUSH_VARS();
+ block = ruby_block;
+ frame = block->frame;
+ frame.prev = ruby_frame;
+ ruby_frame = &(frame);
+ old_cref = (VALUE)ruby_cref;
+ ruby_cref = block->cref;
+ old_wrapper = ruby_wrapper;
+ ruby_wrapper = block->wrapper;
+ old_scope = ruby_scope;
+ ruby_scope = block->scope;
+ old_vmode = scope_vmode;
+ scope_vmode = (flags & YIELD_PUBLIC_DEF) ? SCOPE_PUBLIC : block->vmode;
+ ruby_block = block->prev;
+ if (block->flags & BLOCK_D_SCOPE) {
+ /* put place holder for dynamic (in-block) local variables */
+ ruby_dyna_vars = new_dvar(0, 0, block->dyna_vars);
+ }
+ else {
+ /* FOR does not introduce new scope */
+ ruby_dyna_vars = block->dyna_vars;
+ }
+ PUSH_CLASS(klass ? klass : block->klass);
+ if (!klass) {
+ self = block->self;
+ }
+ node = block->body;
+ var = block->var;
+
+ if (var) {
+ PUSH_TAG(PROT_NONE);
+ if ((state = EXEC_TAG()) == 0) {
+ NODE *bvar = NULL;
+ block_var:
+ if (var == (NODE*)1) { /* no parameter || */
+ if (lambda && RARRAY(val)->len != 0) {
+ rb_raise(rb_eArgError, "wrong number of arguments (%ld for 0)",
+ RARRAY(val)->len);
+ }
+ }
+ else if (var == (NODE*)2) {
+ if (TYPE(val) == T_ARRAY && RARRAY(val)->len != 0) {
+ rb_raise(rb_eArgError, "wrong number of arguments (%ld for 0)",
+ RARRAY(val)->len);
+ }
+ }
+ else if (!bvar && nd_type(var) == NODE_BLOCK_PASS) {
+ bvar = var->nd_body;
+ var = var->nd_args;
+ goto block_var;
+ }
+ else if (nd_type(var) == NODE_MASGN) {
+ if (!avalue) {
+ val = svalue_to_mrhs(val, var->nd_head);
+ }
+ massign(self, var, val, lambda);
+ }
+ else {
+ int len = 0;
+ if (avalue) {
+ len = RARRAY(val)->len;
+ if (len == 0) {
+ goto zero_arg;
+ }
+ if (len == 1) {
+ val = RARRAY(val)->ptr[0];
+ }
+ else {
+ goto multi_values;
+ }
+ }
+ else if (val == Qundef) {
+ zero_arg:
+ val = Qnil;
+ multi_values:
+ {
+ ruby_current_node = var;
+ rb_warn("multiple values for a block parameter (%d for 1)\n\tfrom %s:%d",
+ len, cnode->nd_file, nd_line(cnode));
+ ruby_current_node = cnode;
+ }
+ }
+ assign(self, var, val, lambda);
+ }
+ if (bvar) {
+ VALUE blk;
+ if (flags & YIELD_PROC_CALL)
+ blk = block->block_obj;
+ else
+ blk = rb_block_proc();
+ assign(self, bvar, blk, 0);
+ }
+ }
+ POP_TAG();
+ if (state) goto pop_state;
+ }
+ else if (lambda && RARRAY(val)->len != 0 &&
+ (!node || nd_type(node) != NODE_IFUNC ||
+ node->nd_cfnc != bmcall)) {
+ rb_raise(rb_eArgError, "wrong number of arguments (%ld for 0)",
+ RARRAY(val)->len);
+ }
+ if (!node) {
+ state = 0;
+ goto pop_state;
+ }
+ ruby_current_node = node;
+
+ PUSH_ITER(block->iter);
+ PUSH_TAG(lambda ? PROT_NONE : PROT_YIELD);
+ if ((state = EXEC_TAG()) == 0) {
+ redo:
+ if (nd_type(node) == NODE_CFUNC || nd_type(node) == NODE_IFUNC) {
+ if (node->nd_state == YIELD_FUNC_AVALUE) {
+ if (!avalue) {
+ val = svalue_to_avalue(val);
+ }
+ }
+ else {
+ if (avalue) {
+ val = avalue_to_svalue(val);
+ }
+ if (val == Qundef && node->nd_state != YIELD_FUNC_SVALUE)
+ val = Qnil;
+ }
+ if ((block->flags&BLOCK_FROM_METHOD) && RTEST(block->block_obj)) {
+ struct BLOCK *data, _block;
+ Data_Get_Struct(block->block_obj, struct BLOCK, data);
+ _block = *data;
+ _block.outer = ruby_block;
+ _block.uniq = block_unique++;
+ ruby_block = &_block;
+ PUSH_ITER(ITER_PRE);
+ ruby_frame->iter = ITER_CUR;
+ result = (*node->nd_cfnc)(val, node->nd_tval, self);
+ POP_ITER();
+ }
+ else {
+ result = (*node->nd_cfnc)(val, node->nd_tval, self);
+ }
+ }
+ else {
+ result = rb_eval(self, node);
+ }
+ }
+ else {
+ switch (state) {
+ case TAG_REDO:
+ state = 0;
+ CHECK_INTS;
+ goto redo;
+ case TAG_NEXT:
+ state = 0;
+ result = prot_tag->retval;
+ break;
+ case TAG_BREAK:
+ if (TAG_DST()) {
+ result = prot_tag->retval;
+ }
+ else {
+ lambda = Qtrue; /* just pass TAG_BREAK */
+ }
+ break;
+ default:
+ break;
+ }
+ }
+ POP_TAG();
+ POP_ITER();
+ pop_state:
+ POP_CLASS();
+ if (ruby_dyna_vars && (block->flags & BLOCK_D_SCOPE) &&
+ !FL_TEST(ruby_dyna_vars, DVAR_DONT_RECYCLE)) {
+ struct RVarmap *vars = ruby_dyna_vars;
+
+ if (ruby_dyna_vars->id == 0) {
+ vars = ruby_dyna_vars->next;
+ rb_gc_force_recycle((VALUE)ruby_dyna_vars);
+ while (vars && vars->id != 0 && vars != block->dyna_vars) {
+ struct RVarmap *tmp = vars->next;
+ rb_gc_force_recycle((VALUE)vars);
+ vars = tmp;
+ }
+ }
+ }
+ POP_VARS();
+ ruby_block = block;
+ ruby_frame = ruby_frame->prev;
+ ruby_cref = (NODE*)old_cref;
+ ruby_wrapper = old_wrapper;
+ if (ruby_scope->flags & SCOPE_DONT_RECYCLE)
+ scope_dup(old_scope);
+ ruby_scope = old_scope;
+ scope_vmode = old_vmode;
+ switch (state) {
+ case 0:
+ break;
+ case TAG_BREAK:
+ if (!lambda) {
+ struct tag *tt = prot_tag;
+
+ while (tt) {
+ if (tt->tag == PROT_LOOP && tt->blkid == ruby_block->uniq) {
+ tt->dst = (VALUE)tt->frame->uniq;
+ tt->retval = result;
+ JUMP_TAG(TAG_BREAK);
+ }
+ tt = tt->prev;
+ }
+ proc_jump_error(TAG_BREAK, result);
+ }
+ /* fall through */
+ default:
+ JUMP_TAG(state);
+ break;
+ }
+ ruby_current_node = cnode;
+ return result;
+}
+
+VALUE
+rb_yield(val)
+ VALUE val;
+{
+ return rb_yield_0(val, 0, 0, 0, Qfalse);
+}
+
+VALUE
+#ifdef HAVE_STDARG_PROTOTYPES
+rb_yield_values(int n, ...)
+#else
+rb_yield_values(n, va_alist)
+ int n;
+ va_dcl
+#endif
+{
+ int i;
+ va_list args;
+ VALUE val;
+
+ if (n == 0) {
+ return rb_yield_0(Qundef, 0, 0, 0, Qfalse);
+ }
+ val = rb_values_new2(n, 0);
+ va_init_list(args, n);
+ for (i=0; i<n; i++) {
+ RARRAY(val)->ptr[i] = va_arg(args, VALUE);
+ }
+ RARRAY(val)->len = n;
+ va_end(args);
+ return rb_yield_0(val, 0, 0, 0, Qtrue);
+}
+
+VALUE
+rb_yield_splat(values)
+ VALUE values;
+{
+ int avalue = Qfalse;
+
+ if (TYPE(values) == T_ARRAY) {
+ if (RARRAY(values)->len == 0) {
+ values = Qundef;
+ }
+ else {
+ avalue = Qtrue;
+ }
+ }
+ return rb_yield_0(values, 0, 0, 0, avalue);
+}
+
+/*
+ * call-seq:
+ * loop {|| block }
+ *
+ * Repeatedly executes the block.
+ *
+ * loop do
+ * print "Input: "
+ * line = gets
+ * break if !line or line =~ /^qQ/
+ * # ...
+ * end
+ */
+
+static VALUE
+rb_f_loop()
+{
+ for (;;) {
+ rb_yield_0(Qundef, 0, 0, 0, Qfalse);
+ CHECK_INTS;
+ }
+ return Qnil; /* dummy */
+}
+
+static VALUE
+massign(self, node, val, pcall)
+ VALUE self;
+ NODE *node;
+ VALUE val;
+ int pcall;
+{
+ NODE *list;
+ long i = 0, len;
+
+ len = RARRAY(val)->len;
+ list = node->nd_head;
+ for (; list && i<len; i++) {
+ assign(self, list->nd_head, RARRAY(val)->ptr[i], pcall);
+ list = list->nd_next;
+ }
+ if (pcall && list) goto arg_error;
+ if (node->nd_args) {
+ if ((long)(node->nd_args) == -1) {
+ /* no check for mere `*' */
+ }
+ else if (!list && i<len) {
+ assign(self, node->nd_args, rb_ary_new4(len-i, RARRAY(val)->ptr+i), pcall);
+ }
+ else {
+ assign(self, node->nd_args, rb_ary_new2(0), pcall);
+ }
+ }
+ else if (pcall && i < len) {
+ goto arg_error;
+ }
+
+ while (list) {
+ i++;
+ assign(self, list->nd_head, Qnil, pcall);
+ list = list->nd_next;
+ }
+ return val;
+
+ arg_error:
+ while (list) {
+ i++;
+ list = list->nd_next;
+ }
+ rb_raise(rb_eArgError, "wrong number of arguments (%ld for %ld)", len, i);
+}
+
+static void
+assign(self, lhs, val, pcall)
+ VALUE self;
+ NODE *lhs;
+ VALUE val;
+ int pcall;
+{
+ ruby_current_node = lhs;
+ if (val == Qundef) {
+ rb_warning("assigning void value");
+ val = Qnil;
+ }
+ switch (nd_type(lhs)) {
+ case NODE_GASGN:
+ rb_gvar_set(lhs->nd_entry, val);
+ break;
+
+ case NODE_IASGN:
+ rb_ivar_set(self, lhs->nd_vid, val);
+ break;
+
+ case NODE_LASGN:
+ if (ruby_scope->local_vars == 0)
+ rb_bug("unexpected local variable assignment");
+ ruby_scope->local_vars[lhs->nd_cnt] = val;
+ break;
+
+ case NODE_DASGN:
+ dvar_asgn(lhs->nd_vid, val);
+ break;
+
+ case NODE_DASGN_CURR:
+ dvar_asgn_curr(lhs->nd_vid, val);
+ break;
+
+ case NODE_CDECL:
+ if (lhs->nd_vid == 0) {
+ rb_const_set(class_prefix(self, lhs->nd_else), lhs->nd_else->nd_mid, val);
+ }
+ else {
+ rb_const_set(ruby_cbase, lhs->nd_vid, val);
+ }
+ break;
+
+ case NODE_CVDECL:
+ if (RTEST(ruby_verbose) && FL_TEST(ruby_cbase, FL_SINGLETON)) {
+ rb_warn("declaring singleton class variable");
+ }
+ rb_cvar_set(cvar_cbase(), lhs->nd_vid, val, Qtrue);
+ break;
+
+ case NODE_CVASGN:
+ rb_cvar_set(cvar_cbase(), lhs->nd_vid, val, Qfalse);
+ break;
+
+ case NODE_MASGN:
+ massign(self, lhs, svalue_to_mrhs(val, lhs->nd_head), pcall);
+ break;
+
+ case NODE_CALL:
+ case NODE_ATTRASGN:
+ {
+ VALUE recv;
+ int scope;
+ if (lhs->nd_recv == (NODE *)1) {
+ recv = self;
+ scope = 1;
+ }
+ else {
+ recv = rb_eval(self, lhs->nd_recv);
+ scope = 0;
+ }
+ if (!lhs->nd_args) {
+ /* attr set */
+ ruby_current_node = lhs;
+ SET_CURRENT_SOURCE();
+ rb_call(CLASS_OF(recv), recv, lhs->nd_mid, 1, &val, scope);
+ }
+ else {
+ /* array set */
+ VALUE args;
+
+ args = rb_eval(self, lhs->nd_args);
+ rb_ary_push(args, val);
+ ruby_current_node = lhs;
+ SET_CURRENT_SOURCE();
+ rb_call(CLASS_OF(recv), recv, lhs->nd_mid,
+ RARRAY(args)->len, RARRAY(args)->ptr, scope);
+ }
+ }
+ break;
+
+ default:
+ rb_bug("bug in variable assignment");
+ break;
+ }
+}
+
+VALUE
+rb_iterate(it_proc, data1, bl_proc, data2)
+ VALUE (*it_proc) _((VALUE)), (*bl_proc)(ANYARGS);
+ VALUE data1, data2;
+{
+ int state;
+ volatile VALUE retval = Qnil;
+ NODE *node = NEW_IFUNC(bl_proc, data2);
+ VALUE self = ruby_top_self;
+
+ PUSH_ITER(ITER_PRE);
+ PUSH_TAG(PROT_LOOP);
+ PUSH_BLOCK(0, node);
+ state = EXEC_TAG();
+ if (state == 0) {
+ iter_retry:
+ retval = (*it_proc)(data1);
+ }
+ else if (state == TAG_BREAK && TAG_DST()) {
+ retval = prot_tag->retval;
+ state = 0;
+ }
+ else if (state == TAG_RETRY) {
+ state = 0;
+ goto iter_retry;
+ }
+ POP_BLOCK();
+ POP_TAG();
+ POP_ITER();
+
+ switch (state) {
+ case 0:
+ break;
+ default:
+ JUMP_TAG(state);
+ }
+ return retval;
+}
+
+static int
+handle_rescue(self, node)
+ VALUE self;
+ NODE *node;
+{
+ int argc; VALUE *argv; /* used in SETUP_ARGS */
+ TMP_PROTECT;
+
+ if (!node->nd_args) {
+ return rb_obj_is_kind_of(ruby_errinfo, rb_eStandardError);
+ }
+
+ BEGIN_CALLARGS;
+ SETUP_ARGS(node->nd_args);
+ END_CALLARGS;
+
+ while (argc--) {
+ if (!rb_obj_is_kind_of(argv[0], rb_cModule)) {
+ rb_raise(rb_eTypeError, "class or module required for rescue clause");
+ }
+ if (RTEST(rb_funcall(*argv, eqq, 1, ruby_errinfo))) return 1;
+ argv++;
+ }
+ return 0;
+}
+
+VALUE
+#ifdef HAVE_STDARG_PROTOTYPES
+rb_rescue2(VALUE (*b_proc)(ANYARGS), VALUE data1, VALUE (*r_proc)(ANYARGS), VALUE data2, ...)
+#else
+rb_rescue2(b_proc, data1, r_proc, data2, va_alist)
+ VALUE (*b_proc)(ANYARGS), (*r_proc)(ANYARGS);
+ VALUE data1, data2;
+ va_dcl
+#endif
+{
+ int state;
+ volatile VALUE result;
+ volatile VALUE e_info = ruby_errinfo;
+ va_list args;
+
+ PUSH_TAG(PROT_NONE);
+ if ((state = EXEC_TAG()) == 0) {
+ retry_entry:
+ result = (*b_proc)(data1);
+ }
+ else if (state == TAG_RAISE) {
+ int handle = Qfalse;
+ VALUE eclass;
+
+ va_init_list(args, data2);
+ while (eclass = va_arg(args, VALUE)) {
+ if (rb_obj_is_kind_of(ruby_errinfo, eclass)) {
+ handle = Qtrue;
+ break;
+ }
+ }
+ va_end(args);
+
+ if (handle) {
+ if (r_proc) {
+ PUSH_TAG(PROT_NONE);
+ if ((state = EXEC_TAG()) == 0) {
+ result = (*r_proc)(data2, ruby_errinfo);
+ }
+ POP_TAG();
+ if (state == TAG_RETRY) {
+ state = 0;
+ ruby_errinfo = Qnil;
+ goto retry_entry;
+ }
+ }
+ else {
+ result = Qnil;
+ state = 0;
+ }
+ if (state == 0) {
+ ruby_errinfo = e_info;
+ }
+ }
+ }
+ POP_TAG();
+ if (state) JUMP_TAG(state);
+
+ return result;
+}
+
+VALUE
+rb_rescue(b_proc, data1, r_proc, data2)
+ VALUE (*b_proc)(), (*r_proc)();
+ VALUE data1, data2;
+{
+ return rb_rescue2(b_proc, data1, r_proc, data2, rb_eStandardError, (VALUE)0);
+}
+
+static VALUE cont_protect;
+
+VALUE
+rb_protect(proc, data, state)
+ VALUE (*proc) _((VALUE));
+ VALUE data;
+ int *state;
+{
+ VALUE result = Qnil; /* OK */
+ int status;
+
+ PUSH_THREAD_TAG();
+ cont_protect = (VALUE)rb_node_newnode(NODE_MEMO, cont_protect, 0, 0);
+ if ((status = EXEC_TAG()) == 0) {
+ result = (*proc)(data);
+ }
+ else if (status == TAG_THREAD) {
+ rb_thread_start_1();
+ }
+ cont_protect = ((NODE *)cont_protect)->u1.value;
+ POP_THREAD_TAG();
+ if (state) {
+ *state = status;
+ }
+ if (status != 0) {
+ return Qnil;
+ }
+
+ return result;
+}
+
+VALUE
+rb_ensure(b_proc, data1, e_proc, data2)
+ VALUE (*b_proc)();
+ VALUE data1;
+ VALUE (*e_proc)();
+ VALUE data2;
+{
+ int state;
+ volatile VALUE result = Qnil;
+ VALUE retval;
+
+ PUSH_TAG(PROT_NONE);
+ if ((state = EXEC_TAG()) == 0) {
+ result = (*b_proc)(data1);
+ }
+ POP_TAG();
+ retval = prot_tag ? prot_tag->retval : Qnil; /* save retval */
+ (*e_proc)(data2);
+ if (prot_tag) return_value(retval);
+ if (state) JUMP_TAG(state);
+ return result;
+}
+
+VALUE
+rb_with_disable_interrupt(proc, data)
+ VALUE (*proc)();
+ VALUE data;
+{
+ VALUE result = Qnil; /* OK */
+ int status;
+
+ DEFER_INTS;
+ {
+ int thr_critical = rb_thread_critical;
+
+ rb_thread_critical = Qtrue;
+ PUSH_TAG(PROT_NONE);
+ if ((status = EXEC_TAG()) == 0) {
+ result = (*proc)(data);
+ }
+ POP_TAG();
+ rb_thread_critical = thr_critical;
+ }
+ ENABLE_INTS;
+ if (status) JUMP_TAG(status);
+
+ return result;
+}
+
+static inline void
+stack_check()
+{
+ static int overflowing = 0;
+
+ if (!overflowing && ruby_stack_check()) {
+ int state;
+ overflowing = 1;
+ PUSH_TAG(PROT_NONE);
+ if ((state = EXEC_TAG()) == 0) {
+ rb_exc_raise(sysstack_error);
+ }
+ POP_TAG();
+ overflowing = 0;
+ JUMP_TAG(state);
+ }
+}
+
+static int last_call_status;
+
+#define CSTAT_PRIV 1
+#define CSTAT_PROT 2
+#define CSTAT_VCALL 4
+#define CSTAT_SUPER 8
+
+/*
+ * call-seq:
+ * obj.method_missing(symbol [, *args] ) => result
+ *
+ * Invoked by Ruby when <i>obj</i> is sent a message it cannot handle.
+ * <i>symbol</i> is the symbol for the method called, and <i>args</i>
+ * are any arguments that were passed to it. By default, the interpreter
+ * raises an error when this method is called. However, it is possible
+ * to override the method to provide more dynamic behavior.
+ * The example below creates
+ * a class <code>Roman</code>, which responds to methods with names
+ * consisting of roman numerals, returning the corresponding integer
+ * values.
+ *
+ * class Roman
+ * def romanToInt(str)
+ * # ...
+ * end
+ * def method_missing(methId)
+ * str = methId.id2name
+ * romanToInt(str)
+ * end
+ * end
+ *
+ * r = Roman.new
+ * r.iv #=> 4
+ * r.xxiii #=> 23
+ * r.mm #=> 2000
+ */
+
+static VALUE
+rb_method_missing(argc, argv, obj)
+ int argc;
+ VALUE *argv;
+ VALUE obj;
+{
+ ID id;
+ VALUE exc = rb_eNoMethodError;
+ char *format = 0;
+ NODE *cnode = ruby_current_node;
+
+ if (argc == 0 || !SYMBOL_P(argv[0])) {
+ rb_raise(rb_eArgError, "no id given");
+ }
+
+ stack_check();
+
+ id = SYM2ID(argv[0]);
+
+ if (last_call_status & CSTAT_PRIV) {
+ format = "private method `%s' called for %s";
+ }
+ else if (last_call_status & CSTAT_PROT) {
+ format = "protected method `%s' called for %s";
+ }
+ else if (last_call_status & CSTAT_VCALL) {
+ format = "undefined local variable or method `%s' for %s";
+ exc = rb_eNameError;
+ }
+ else if (last_call_status & CSTAT_SUPER) {
+ format = "super: no superclass method `%s'";
+ }
+ if (!format) {
+ format = "undefined method `%s' for %s";
+ }
+
+ ruby_current_node = cnode;
+ {
+ int n = 0;
+ VALUE args[3];
+
+ args[n++] = rb_funcall(rb_const_get(exc, rb_intern("message")), '!',
+ 3, rb_str_new2(format), obj, argv[0]);
+ args[n++] = argv[0];
+ if (exc == rb_eNoMethodError) {
+ args[n++] = rb_ary_new4(argc-1, argv+1);
+ }
+ exc = rb_class_new_instance(n, args, exc);
+ ruby_frame = ruby_frame->prev; /* pop frame for "method_missing" */
+ rb_exc_raise(exc);
+ }
+
+ return Qnil; /* not reached */
+}
+
+static VALUE
+method_missing(obj, id, argc, argv, call_status)
+ VALUE obj;
+ ID id;
+ int argc;
+ const VALUE *argv;
+ int call_status;
+{
+ VALUE *nargv;
+
+ last_call_status = call_status;
+
+ if (id == missing) {
+ PUSH_FRAME();
+ rb_method_missing(argc, argv, obj);
+ POP_FRAME();
+ }
+ else if (id == ID_ALLOCATOR) {
+ rb_raise(rb_eTypeError, "allocator undefined for %s", rb_class2name(obj));
+ }
+
+ nargv = ALLOCA_N(VALUE, argc+1);
+ nargv[0] = ID2SYM(id);
+ MEMCPY(nargv+1, argv, VALUE, argc);
+
+ return rb_funcall2(obj, missing, argc+1, nargv);
+}
+
+static inline VALUE
+call_cfunc(func, recv, len, argc, argv)
+ VALUE (*func)();
+ VALUE recv;
+ int len, argc;
+ VALUE *argv;
+{
+ if (len >= 0 && argc != len) {
+ rb_raise(rb_eArgError, "wrong number of arguments (%d for %d)",
+ argc, len);
+ }
+
+ switch (len) {
+ case -2:
+ return (*func)(recv, rb_ary_new4(argc, argv));
+ break;
+ case -1:
+ return (*func)(argc, argv, recv);
+ break;
+ case 0:
+ return (*func)(recv);
+ break;
+ case 1:
+ return (*func)(recv, argv[0]);
+ break;
+ case 2:
+ return (*func)(recv, argv[0], argv[1]);
+ break;
+ case 3:
+ return (*func)(recv, argv[0], argv[1], argv[2]);
+ break;
+ case 4:
+ return (*func)(recv, argv[0], argv[1], argv[2], argv[3]);
+ break;
+ case 5:
+ return (*func)(recv, argv[0], argv[1], argv[2], argv[3], argv[4]);
+ break;
+ case 6:
+ return (*func)(recv, argv[0], argv[1], argv[2], argv[3], argv[4],
+ argv[5]);
+ break;
+ case 7:
+ return (*func)(recv, argv[0], argv[1], argv[2], argv[3], argv[4],
+ argv[5], argv[6]);
+ break;
+ case 8:
+ return (*func)(recv, argv[0], argv[1], argv[2], argv[3], argv[4],
+ argv[5], argv[6], argv[7]);
+ break;
+ case 9:
+ return (*func)(recv, argv[0], argv[1], argv[2], argv[3], argv[4],
+ argv[5], argv[6], argv[7], argv[8]);
+ break;
+ case 10:
+ return (*func)(recv, argv[0], argv[1], argv[2], argv[3], argv[4],
+ argv[5], argv[6], argv[7], argv[8], argv[9]);
+ break;
+ case 11:
+ return (*func)(recv, argv[0], argv[1], argv[2], argv[3], argv[4],
+ argv[5], argv[6], argv[7], argv[8], argv[9], argv[10]);
+ break;
+ case 12:
+ return (*func)(recv, argv[0], argv[1], argv[2], argv[3], argv[4],
+ argv[5], argv[6], argv[7], argv[8], argv[9],
+ argv[10], argv[11]);
+ break;
+ case 13:
+ return (*func)(recv, argv[0], argv[1], argv[2], argv[3], argv[4],
+ argv[5], argv[6], argv[7], argv[8], argv[9], argv[10],
+ argv[11], argv[12]);
+ break;
+ case 14:
+ return (*func)(recv, argv[0], argv[1], argv[2], argv[3], argv[4],
+ argv[5], argv[6], argv[7], argv[8], argv[9], argv[10],
+ argv[11], argv[12], argv[13]);
+ break;
+ case 15:
+ return (*func)(recv, argv[0], argv[1], argv[2], argv[3], argv[4],
+ argv[5], argv[6], argv[7], argv[8], argv[9], argv[10],
+ argv[11], argv[12], argv[13], argv[14]);
+ break;
+ default:
+ rb_raise(rb_eArgError, "too many arguments (%d)", len);
+ break;
+ }
+ return Qnil; /* not reached */
+}
+
+static VALUE
+rb_call0(klass, recv, id, oid, argc, argv, body, nosuper)
+ VALUE klass, recv;
+ ID id;
+ ID oid;
+ int argc; /* OK */
+ VALUE *argv; /* OK */
+ NODE *body; /* OK */
+ int nosuper;
+{
+ NODE *b2; /* OK */
+ volatile VALUE result = Qnil;
+ int itr;
+ static int tick;
+ volatile VALUE args;
+ TMP_PROTECT;
+
+ switch (ruby_iter->iter) {
+ case ITER_PRE:
+ itr = ITER_CUR;
+ break;
+ case ITER_CUR:
+ default:
+ itr = ITER_NOT;
+ break;
+ }
+
+ if ((++tick & 0xff) == 0) {
+ CHECK_INTS; /* better than nothing */
+ stack_check();
+ rb_gc_finalize_deferred();
+ }
+ if (argc < 0) {
+ argc = -argc-1;
+ args = rb_ary_concat(rb_ary_new4(argc, argv), splat_value(argv[argc]));
+ argc = RARRAY(args)->len;
+ argv = RARRAY(args)->ptr;
+ }
+ PUSH_ITER(itr);
+ PUSH_FRAME();
+ ruby_frame->callee = id;
+ ruby_frame->this_func = oid;
+ ruby_frame->this_class = nosuper?0:klass;
+ ruby_frame->self = recv;
+ ruby_frame->argc = argc;
+
+ switch (nd_type(body)) {
+ case NODE_CFUNC:
+ {
+ int len = body->nd_argc;
+
+ if (len < -2) {
+ rb_bug("bad argc (%d) specified for `%s(%s)'",
+ len, rb_class2name(klass), rb_id2name(id));
+ }
+ if (event_hooks) {
+ int state;
+
+ EXEC_EVENT_HOOK(RUBY_EVENT_C_CALL, ruby_current_node,
+ recv, id, klass);
+ PUSH_TAG(PROT_FUNC);
+ if ((state = EXEC_TAG()) == 0) {
+ result = call_cfunc(body->nd_cfnc, recv, len, argc, argv);
+ }
+ POP_TAG();
+ ruby_current_node = ruby_frame->node;
+ EXEC_EVENT_HOOK(RUBY_EVENT_C_RETURN, ruby_current_node,
+ recv, id, klass);
+ if (state) JUMP_TAG(state);
+ }
+ else {
+ result = call_cfunc(body->nd_cfnc, recv, len, argc, argv);
+ }
+ }
+ break;
+
+ /* for attr get/set */
+ case NODE_IVAR:
+ if (argc != 0) {
+ rb_raise(rb_eArgError, "wrong number of arguments (%d for 0)", argc);
+ }
+ result = rb_attr_get(recv, body->nd_vid);
+ break;
+
+ case NODE_ATTRSET:
+ if (argc != 1)
+ rb_raise(rb_eArgError, "wrong number of arguments (%d for 1)", argc);
+ result = rb_ivar_set(recv, body->nd_vid, argv[0]);
+ break;
+
+ case NODE_ZSUPER: /* visibility override */
+ result = rb_call_super(argc, argv);
+ break;
+
+ case NODE_BMETHOD:
+ ruby_frame->flags |= FRAME_DMETH;
+ result = proc_invoke(body->nd_cval, rb_ary_new4(argc, argv), recv, klass);
+ break;
+
+ case NODE_SCOPE:
+ {
+ int state;
+ VALUE *local_vars; /* OK */
+ NODE *saved_cref = 0;
+
+ PUSH_SCOPE();
+
+ if (body->nd_rval) {
+ saved_cref = ruby_cref;
+ ruby_cref = (NODE*)body->nd_rval;
+ }
+ PUSH_CLASS(ruby_cbase);
+ if (body->nd_tbl) {
+ local_vars = TMP_ALLOC(body->nd_tbl[0]+1);
+ *local_vars++ = (VALUE)body;
+ rb_mem_clear(local_vars, body->nd_tbl[0]);
+ ruby_scope->local_tbl = body->nd_tbl;
+ ruby_scope->local_vars = local_vars;
+ }
+ else {
+ local_vars = ruby_scope->local_vars = 0;
+ ruby_scope->local_tbl = 0;
+ }
+ b2 = body = body->nd_next;
+
+ PUSH_VARS();
+ PUSH_TAG(PROT_FUNC);
+
+ if ((state = EXEC_TAG()) == 0) {
+ NODE *node = 0;
+ int i;
+
+ if (nd_type(body) == NODE_ARGS) {
+ node = body;
+ body = 0;
+ }
+ else if (nd_type(body) == NODE_BLOCK) {
+ node = body->nd_head;
+ body = body->nd_next;
+ }
+ if (node) {
+ if (nd_type(node) != NODE_ARGS) {
+ rb_bug("no argument-node");
+ }
+
+ i = node->nd_cnt;
+ if (i > argc) {
+ rb_raise(rb_eArgError, "wrong number of arguments (%d for %d)", argc, i);
+ }
+ if ((long)node->nd_rest == -1) {
+ int opt = i;
+ NODE *optnode = node->nd_opt;
+
+ while (optnode) {
+ opt++;
+ optnode = optnode->nd_next;
+ }
+ if (opt < argc) {
+ rb_raise(rb_eArgError, "wrong number of arguments (%d for %d)",
+ argc, opt);
+ }
+ ruby_frame->argc = opt;
+ }
+
+ if (local_vars) {
+ if (i > 0) {
+ /* +2 for $_ and $~ */
+ MEMCPY(local_vars+2, argv, VALUE, i);
+ }
+ argv += i; argc -= i;
+ if (node->nd_opt) {
+ NODE *opt = node->nd_opt;
+
+ while (opt && argc) {
+ assign(recv, opt->nd_head, *argv, 1);
+ argv++; argc--;
+ opt = opt->nd_next;
+ }
+ if (opt) {
+ rb_eval(recv, opt);
+ }
+ }
+ if ((long)node->nd_rest >= 0) {
+ VALUE v;
+
+ if (argc > 0)
+ v = rb_ary_new4(argc,argv);
+ else
+ v = rb_ary_new2(0);
+ ruby_scope->local_vars[node->nd_rest] = v;
+ }
+ }
+ }
+ if ((long)node->nd_rest >= 0) {
+ ruby_frame->argc = -(ruby_frame->argc - argc)-1;
+ }
+
+ if (event_hooks) {
+ EXEC_EVENT_HOOK(RUBY_EVENT_CALL, b2, recv, id, klass);
+ }
+ result = rb_eval(recv, body);
+ }
+ else if (state == TAG_RETURN && TAG_DST()) {
+ result = prot_tag->retval;
+ state = 0;
+ }
+ POP_TAG();
+ POP_VARS();
+ POP_CLASS();
+ POP_SCOPE();
+ ruby_cref = saved_cref;
+ if (event_hooks) {
+ EXEC_EVENT_HOOK(RUBY_EVENT_RETURN, body, recv, id, klass);
+ }
+ switch (state) {
+ case 0:
+ break;
+
+ case TAG_BREAK:
+ case TAG_RETURN:
+ JUMP_TAG(state);
+ break;
+
+ case TAG_RETRY:
+ if (rb_block_given_p()) JUMP_TAG(state);
+ /* fall through */
+ default:
+ jump_tag_but_local_jump(state, result);
+ break;
+ }
+ }
+ break;
+
+ default:
+ rb_bug("unknown node type %d", nd_type(body));
+ break;
+ }
+ POP_FRAME();
+ POP_ITER();
+ return result;
+}
+
+static VALUE
+rb_call(klass, recv, mid, argc, argv, scope)
+ VALUE klass, recv;
+ ID mid;
+ int argc; /* OK */
+ const VALUE *argv; /* OK */
+ int scope;
+{
+ NODE *body; /* OK */
+ int noex;
+ ID id = mid;
+ struct cache_entry *ent;
+
+ if (!klass) {
+ rb_raise(rb_eNotImpError, "method `%s' called on terminated object (0x%lx)",
+ rb_id2name(mid), recv);
+ }
+ /* is it in the method cache? */
+ ent = cache + EXPR1(klass, mid);
+ if (ent->mid == mid && ent->klass == klass) {
+ if (!ent->method)
+ return method_missing(recv, mid, argc, argv, scope==2?CSTAT_VCALL:0);
+ klass = ent->origin;
+ id = ent->mid0;
+ noex = ent->noex;
+ body = ent->method;
+ }
+ else if ((body = rb_get_method_body(&klass, &id, &noex)) == 0) {
+ if (scope == 3) {
+ return method_missing(recv, mid, argc, argv, CSTAT_SUPER);
+ }
+ return method_missing(recv, mid, argc, argv, scope==2?CSTAT_VCALL:0);
+ }
+
+ if (mid != missing) {
+ /* receiver specified form for private method */
+ if ((noex & NOEX_PRIVATE) && scope == 0)
+ return method_missing(recv, mid, argc, argv, CSTAT_PRIV);
+
+ /* self must be kind of a specified form for protected method */
+ if ((noex & NOEX_PROTECTED)) {
+ VALUE defined_class = klass;
+
+ if (TYPE(defined_class) == T_ICLASS) {
+ defined_class = RBASIC(defined_class)->klass;
+ }
+ if (!rb_obj_is_kind_of(ruby_frame->self, rb_class_real(defined_class)))
+ return method_missing(recv, mid, argc, argv, CSTAT_PROT);
+ }
+ }
+
+ return rb_call0(klass, recv, mid, id, argc, argv, body, noex & NOEX_NOSUPER);
+}
+
+VALUE
+rb_apply(recv, mid, args)
+ VALUE recv;
+ ID mid;
+ VALUE args;
+{
+ int argc;
+ VALUE *argv;
+
+ argc = RARRAY(args)->len; /* Assigns LONG, but argc is INT */
+ argv = ALLOCA_N(VALUE, argc);
+ MEMCPY(argv, RARRAY(args)->ptr, VALUE, argc);
+ return rb_call(CLASS_OF(recv), recv, mid, argc, argv, 1);
+}
+
+/*
+ * call-seq:
+ * obj.send(symbol [, args...]) => obj
+ * obj.__send__(symbol [, args...]) => obj
+ *
+ * Invokes the method identified by _symbol_, passing it any
+ * arguments specified. You can use <code>__send__</code> if the name
+ * +send+ clashes with an existing method in _obj_.
+ *
+ * class Klass
+ * def hello(*args)
+ * "Hello " + args.join(' ')
+ * end
+ * end
+ * k = Klass.new
+ * k.send :hello, "gentle", "readers" #=> "Hello gentle readers"
+ */
+
+static VALUE
+rb_f_send(argc, argv, recv)
+ int argc;
+ VALUE *argv;
+ VALUE recv;
+{
+ VALUE vid;
+
+ if (argc == 0) rb_raise(rb_eArgError, "no method name given");
+
+ vid = *argv++; argc--;
+ PUSH_ITER(rb_block_given_p()?ITER_PRE:ITER_NOT);
+ vid = rb_call(CLASS_OF(recv), recv, rb_to_id(vid), argc, argv, 1);
+ POP_ITER();
+
+ return vid;
+}
+
+VALUE
+#ifdef HAVE_STDARG_PROTOTYPES
+rb_funcall(VALUE recv, ID mid, int n, ...)
+#else
+rb_funcall(recv, mid, n, va_alist)
+ VALUE recv;
+ ID mid;
+ int n;
+ va_dcl
+#endif
+{
+ VALUE *argv;
+ va_list ar;
+ va_init_list(ar, n);
+
+ if (n > 0) {
+ long i;
+
+ argv = ALLOCA_N(VALUE, n);
+
+ for (i=0;i<n;i++) {
+ argv[i] = va_arg(ar, VALUE);
+ }
+ va_end(ar);
+ }
+ else {
+ argv = 0;
+ }
+
+ return rb_call(CLASS_OF(recv), recv, mid, n, argv, 1);
+}
+
+VALUE
+rb_funcall2(recv, mid, argc, argv)
+ VALUE recv;
+ ID mid;
+ int argc;
+ const VALUE *argv;
+{
+ return rb_call(CLASS_OF(recv), recv, mid, argc, argv, 1);
+}
+
+VALUE
+rb_funcall3(recv, mid, argc, argv)
+ VALUE recv;
+ ID mid;
+ int argc;
+ const VALUE *argv;
+{
+ return rb_call(CLASS_OF(recv), recv, mid, argc, argv, 0);
+}
+
+VALUE
+rb_call_super(argc, argv)
+ int argc;
+ const VALUE *argv;
+{
+ VALUE result, self, klass, k;
+
+ if (ruby_frame->this_class == 0) {
+ rb_name_error(ruby_frame->callee, "calling `super' from `%s' is prohibited",
+ rb_id2name(ruby_frame->this_func));
+ }
+
+ self = ruby_frame->self;
+ klass = ruby_frame->this_class;
+
+ PUSH_ITER(ruby_iter->iter ? ITER_PRE : ITER_NOT);
+ result = rb_call(RCLASS(klass)->super, self, ruby_frame->this_func, argc, argv, 3);
+ POP_ITER();
+
+ return result;
+}
+
+static VALUE
+backtrace(lev)
+ int lev;
+{
+ struct FRAME *frame = ruby_frame;
+ char buf[BUFSIZ];
+ volatile VALUE ary;
+ NODE *n;
+
+ ary = rb_ary_new();
+ if (frame->this_func == ID_ALLOCATOR) {
+ frame = frame->prev;
+ }
+ if (lev < 0) {
+ ruby_set_current_source();
+ if (frame->this_func) {
+ snprintf(buf, BUFSIZ, "%s:%d:in `%s'",
+ ruby_sourcefile, ruby_sourceline,
+ rb_id2name(frame->this_func));
+ }
+ else if (ruby_sourceline == 0) {
+ snprintf(buf, BUFSIZ, "%s", ruby_sourcefile);
+ }
+ else {
+ snprintf(buf, BUFSIZ, "%s:%d", ruby_sourcefile, ruby_sourceline);
+ }
+ rb_ary_push(ary, rb_str_new2(buf));
+ if (lev < -1) return ary;
+ }
+ else {
+ while (lev-- > 0) {
+ frame = frame->prev;
+ if (!frame) {
+ ary = Qnil;
+ break;
+ }
+ }
+ }
+ while (frame && (n = frame->node)) {
+ if (frame->prev && frame->prev->this_func) {
+ snprintf(buf, BUFSIZ, "%s:%d:in `%s'",
+ n->nd_file, nd_line(n),
+ rb_id2name(frame->prev->this_func));
+ }
+ else {
+ snprintf(buf, BUFSIZ, "%s:%d", n->nd_file, nd_line(n));
+ }
+ rb_ary_push(ary, rb_str_new2(buf));
+ frame = frame->prev;
+ }
+
+ return ary;
+}
+
+/*
+ * call-seq:
+ * caller(start=1) => array
+ *
+ * Returns the current execution stack---an array containing strings in
+ * the form ``<em>file:line</em>'' or ``<em>file:line: in
+ * `method'</em>''. The optional _start_ parameter
+ * determines the number of initial stack entries to omit from the
+ * result.
+ *
+ * def a(skip)
+ * caller(skip)
+ * end
+ * def b(skip)
+ * a(skip)
+ * end
+ * def c(skip)
+ * b(skip)
+ * end
+ * c(0) #=> ["prog:2:in `a'", "prog:5:in `b'", "prog:8:in `c'", "prog:10"]
+ * c(1) #=> ["prog:5:in `b'", "prog:8:in `c'", "prog:11"]
+ * c(2) #=> ["prog:8:in `c'", "prog:12"]
+ * c(3) #=> ["prog:13"]
+ */
+
+static VALUE
+rb_f_caller(argc, argv)
+ int argc;
+ VALUE *argv;
+{
+ VALUE level;
+ int lev;
+
+ rb_scan_args(argc, argv, "01", &level);
+
+ if (NIL_P(level)) lev = 1;
+ else lev = NUM2INT(level);
+ if (lev < 0) rb_raise(rb_eArgError, "negative level (%d)", lev);
+
+ return backtrace(lev);
+}
+
+void
+rb_backtrace()
+{
+ long i;
+ VALUE ary;
+
+ ary = backtrace(-1);
+ for (i=0; i<RARRAY(ary)->len; i++) {
+ printf("\tfrom %s\n", RSTRING(RARRAY(ary)->ptr[i])->ptr);
+ }
+}
+
+static VALUE
+make_backtrace()
+{
+ return backtrace(-1);
+}
+
+ID
+rb_frame_this_func()
+{
+ return ruby_frame->this_func;
+}
+
+static NODE*
+compile(src, file, line)
+ VALUE src;
+ char *file;
+ int line;
+{
+ NODE *node;
+ int critical;
+
+ ruby_nerrs = 0;
+ StringValue(src);
+ critical = rb_thread_critical;
+ rb_thread_critical = Qtrue;
+ node = rb_compile_string(file, src, line);
+ rb_thread_critical = critical;
+
+ if (ruby_nerrs == 0) return node;
+ return 0;
+}
+
+static VALUE
+eval(self, src, scope, file, line)
+ VALUE self, src, scope;
+ char *file;
+ int line;
+{
+ struct BLOCK *data = NULL;
+ volatile VALUE result = Qnil;
+ struct SCOPE * volatile old_scope;
+ struct BLOCK * volatile old_block;
+ struct RVarmap * volatile old_dyna_vars;
+ VALUE volatile old_cref;
+ int volatile old_vmode;
+ volatile VALUE old_wrapper;
+ struct FRAME frame;
+ NODE *nodesave = ruby_current_node;
+ volatile int iter = ruby_frame->iter;
+ volatile int safe = ruby_safe_level;
+ int state;
+
+ if (!NIL_P(scope)) {
+ if (!rb_obj_is_proc(scope)) {
+ rb_raise(rb_eTypeError, "wrong argument type %s (expected Proc/Binding)",
+ rb_obj_classname(scope));
+ }
+
+ Data_Get_Struct(scope, struct BLOCK, data);
+ /* PUSH BLOCK from data */
+ frame = data->frame;
+ frame.tmp = ruby_frame; /* gc protection */
+ ruby_frame = &(frame);
+ old_scope = ruby_scope;
+ ruby_scope = data->scope;
+ old_block = ruby_block;
+ ruby_block = data->prev;
+ old_dyna_vars = ruby_dyna_vars;
+ ruby_dyna_vars = data->dyna_vars;
+ old_vmode = scope_vmode;
+ scope_vmode = data->vmode;
+ old_cref = (VALUE)ruby_cref;
+ ruby_cref = data->cref;
+ old_wrapper = ruby_wrapper;
+ ruby_wrapper = data->wrapper;
+ if ((file == 0 || (line == 1 && strcmp(file, "(eval)") == 0)) && data->frame.node) {
+ file = data->frame.node->nd_file;
+ if (!file) file = "__builtin__";
+ line = nd_line(data->frame.node);
+ }
+
+ self = data->self;
+ ruby_frame->iter = data->iter;
+ }
+ else {
+ if (ruby_frame->prev) {
+ ruby_frame->iter = ruby_frame->prev->iter;
+ }
+ }
+ if (file == 0) {
+ ruby_set_current_source();
+ file = ruby_sourcefile;
+ line = ruby_sourceline;
+ }
+ PUSH_CLASS(ruby_cbase);
+ ruby_in_eval++;
+ if (TYPE(ruby_class) == T_ICLASS) {
+ ruby_class = RBASIC(ruby_class)->klass;
+ }
+ PUSH_TAG(PROT_NONE);
+ if ((state = EXEC_TAG()) == 0) {
+ NODE *node;
+
+ ruby_safe_level = 0;
+ result = ruby_errinfo;
+ ruby_errinfo = Qnil;
+ node = compile(src, file, line);
+ ruby_safe_level = safe;
+ if (ruby_nerrs > 0) {
+ compile_error(0);
+ }
+ if (!NIL_P(result)) ruby_errinfo = result;
+ result = eval_node(self, node);
+ }
+ POP_TAG();
+ POP_CLASS();
+ ruby_in_eval--;
+ ruby_safe_level = safe;
+ if (!NIL_P(scope)) {
+ int dont_recycle = ruby_scope->flags & SCOPE_DONT_RECYCLE;
+
+ ruby_wrapper = old_wrapper;
+ ruby_cref = (NODE*)old_cref;
+ ruby_frame = frame.tmp;
+ ruby_scope = old_scope;
+ ruby_block = old_block;
+ ruby_dyna_vars = old_dyna_vars;
+ data->vmode = scope_vmode; /* write back visibility mode */
+ scope_vmode = old_vmode;
+ if (dont_recycle) {
+ struct tag *tag;
+ struct RVarmap *vars;
+
+ scope_dup(ruby_scope);
+ for (tag=prot_tag; tag; tag=tag->prev) {
+ scope_dup(tag->scope);
+ }
+ for (vars = ruby_dyna_vars; vars; vars = vars->next) {
+ FL_SET(vars, DVAR_DONT_RECYCLE);
+ }
+ }
+ }
+ else {
+ ruby_frame->iter = iter;
+ }
+ ruby_current_node = nodesave;
+ ruby_set_current_source();
+ if (state) {
+ if (state == TAG_RAISE) {
+ if (strcmp(file, "(eval)") == 0) {
+ VALUE mesg, errat;
+
+ errat = get_backtrace(ruby_errinfo);
+ mesg = rb_attr_get(ruby_errinfo, rb_intern("mesg"));
+ if (!NIL_P(errat) && TYPE(errat) == T_ARRAY) {
+ if (!NIL_P(mesg) && TYPE(mesg) == T_STRING) {
+ rb_str_update(mesg, 0, 0, rb_str_new2(": "));
+ rb_str_update(mesg, 0, 0, RARRAY(errat)->ptr[0]);
+ }
+ RARRAY(errat)->ptr[0] = RARRAY(backtrace(-2))->ptr[0];
+ }
+ }
+ rb_exc_raise(ruby_errinfo);
+ }
+ JUMP_TAG(state);
+ }
+
+ return result;
+}
+
+/*
+ * call-seq:
+ * eval(string [, binding [, filename [,lineno]]]) => obj
+ *
+ * Evaluates the Ruby expression(s) in <em>string</em>. If
+ * <em>binding</em> is given, the evaluation is performed in its
+ * context. The binding may be a <code>Binding</code> object or a
+ * <code>Proc</code> object. If the optional <em>filename</em> and
+ * <em>lineno</em> parameters are present, they will be used when
+ * reporting syntax errors.
+ *
+ * def getBinding(str)
+ * return binding
+ * end
+ * str = "hello"
+ * eval "str + ' Fred'" #=> "hello Fred"
+ * eval "str + ' Fred'", getBinding("bye") #=> "bye Fred"
+ */
+
+static VALUE
+rb_f_eval(argc, argv, self)
+ int argc;
+ VALUE *argv;
+ VALUE self;
+{
+ VALUE src, scope, vfile, vline;
+ char *file = "(eval)";
+ int line = 1;
+
+ rb_scan_args(argc, argv, "13", &src, &scope, &vfile, &vline);
+ if (ruby_safe_level >= 4) {
+ StringValue(src);
+ if (!NIL_P(scope) && !OBJ_TAINTED(scope)) {
+ rb_raise(rb_eSecurityError, "Insecure: can't modify trusted binding");
+ }
+ }
+ else {
+ SafeStringValue(src);
+ }
+ if (argc >= 3) {
+ StringValue(vfile);
+ }
+ if (argc >= 4) {
+ line = NUM2INT(vline);
+ }
+
+ if (!NIL_P(vfile)) file = RSTRING(vfile)->ptr;
+ if (NIL_P(scope) && ruby_frame->prev) {
+ struct FRAME *prev;
+ VALUE val;
+
+ prev = ruby_frame;
+ PUSH_FRAME();
+ *ruby_frame = *prev->prev;
+ ruby_frame->prev = prev;
+ val = eval(self, src, scope, file, line);
+ POP_FRAME();
+
+ return val;
+ }
+ return eval(self, src, scope, file, line);
+}
+
+/* function to call func under the specified class/module context */
+static VALUE
+exec_under(func, under, cbase, args)
+ VALUE (*func)();
+ VALUE under, cbase;
+ void *args;
+{
+ VALUE val = Qnil; /* OK */
+ int state;
+ int mode;
+
+ PUSH_CLASS(under);
+ PUSH_FRAME();
+ ruby_frame->self = _frame.prev->self;
+ ruby_frame->callee = _frame.prev->callee;
+ ruby_frame->this_func = _frame.prev->this_func;
+ ruby_frame->this_class = _frame.prev->this_class;
+ ruby_frame->argc = _frame.prev->argc;
+ if (cbase) {
+ PUSH_CREF(cbase);
+ }
+
+ mode = scope_vmode;
+ SCOPE_SET(SCOPE_PUBLIC);
+ PUSH_TAG(PROT_NONE);
+ if ((state = EXEC_TAG()) == 0) {
+ val = (*func)(args);
+ }
+ POP_TAG();
+ if (cbase) POP_CREF();
+ SCOPE_SET(mode);
+ POP_FRAME();
+ POP_CLASS();
+ if (state) JUMP_TAG(state);
+
+ return val;
+}
+
+static VALUE
+eval_under_i(args)
+ VALUE *args;
+{
+ return eval(args[0], args[1], Qnil, (char*)args[2], (int)args[3]);
+}
+
+/* string eval under the class/module context */
+static VALUE
+eval_under(under, self, src, file, line)
+ VALUE under, self, src;
+ const char *file;
+ int line;
+{
+ VALUE args[4];
+
+ if (ruby_safe_level >= 4) {
+ StringValue(src);
+ }
+ else {
+ SafeStringValue(src);
+ }
+ args[0] = self;
+ args[1] = src;
+ args[2] = (VALUE)file;
+ args[3] = (VALUE)line;
+ return exec_under(eval_under_i, under, under, args);
+}
+
+static VALUE
+yield_under_i(self)
+ VALUE self;
+{
+ return rb_yield_0(self, self, ruby_class, YIELD_PUBLIC_DEF, Qfalse);
+}
+
+/* block eval under the class/module context */
+static VALUE
+yield_under(under, self)
+ VALUE under, self;
+{
+ return exec_under(yield_under_i, under, 0, self);
+}
+
+static VALUE
+specific_eval(argc, argv, klass, self)
+ int argc;
+ VALUE *argv;
+ VALUE klass, self;
+{
+ if (rb_block_given_p()) {
+ if (argc > 0) {
+ rb_raise(rb_eArgError, "wrong number of arguments (%d for 0)", argc);
+ }
+ return yield_under(klass, self);
+ }
+ else {
+ char *file = "(eval)";
+ int line = 1;
+
+ if (argc == 0) {
+ rb_raise(rb_eArgError, "block not supplied");
+ }
+ else {
+ if (ruby_safe_level >= 4) {
+ StringValue(argv[0]);
+ }
+ else {
+ SafeStringValue(argv[0]);
+ }
+ if (argc > 3) {
+ rb_raise(rb_eArgError, "wrong number of arguments: %s(src) or %s{..}",
+ rb_id2name(ruby_frame->callee),
+ rb_id2name(ruby_frame->callee));
+ }
+ if (argc > 2) line = NUM2INT(argv[2]);
+ if (argc > 1) {
+ file = StringValuePtr(argv[1]);
+ }
+ }
+ return eval_under(klass, self, argv[0], file, line);
+ }
+}
+
+/*
+ * call-seq:
+ * obj.instance_eval(string [, filename [, lineno]] ) => obj
+ * obj.instance_eval {| | block } => obj
+ *
+ * Evaluates a string containing Ruby source code, or the given block,
+ * within the context of the receiver (_obj_). In order to set the
+ * context, the variable +self+ is set to _obj_ while
+ * the code is executing, giving the code access to _obj_'s
+ * instance variables. In the version of <code>instance_eval</code>
+ * that takes a +String+, the optional second and third
+ * parameters supply a filename and starting line number that are used
+ * when reporting compilation errors.
+ *
+ * class Klass
+ * def initialize
+ * @secret = 99
+ * end
+ * end
+ * k = Klass.new
+ * k.instance_eval { @secret } #=> 99
+ */
+
+VALUE
+rb_obj_instance_eval(argc, argv, self)
+ int argc;
+ VALUE *argv;
+ VALUE self;
+{
+ VALUE klass;
+
+ if (FIXNUM_P(self) || SYMBOL_P(self)) {
+ klass = Qnil;
+ }
+ else {
+ klass = rb_singleton_class(self);
+ }
+ return specific_eval(argc, argv, klass, self);
+}
+
+/*
+ * call-seq:
+ * mod.class_eval(string [, filename [, lineno]]) => obj
+ * mod.module_eval {|| block } => obj
+ *
+ * Evaluates the string or block in the context of _mod_. This can
+ * be used to add methods to a class. <code>module_eval</code> returns
+ * the result of evaluating its argument. The optional _filename_
+ * and _lineno_ parameters set the text for error messages.
+ *
+ * class Thing
+ * end
+ * a = %q{def hello() "Hello there!" end}
+ * Thing.module_eval(a)
+ * puts Thing.new.hello()
+ * Thing.module_eval("invalid code", "dummy", 123)
+ *
+ * <em>produces:</em>
+ *
+ * Hello there!
+ * dummy:123:in `module_eval': undefined local variable
+ * or method `code' for Thing:Class
+ */
+
+VALUE
+rb_mod_module_eval(argc, argv, mod)
+ int argc;
+ VALUE *argv;
+ VALUE mod;
+{
+ return specific_eval(argc, argv, mod, mod);
+}
+
+VALUE rb_load_path;
+
+NORETURN(static void load_failed _((VALUE)));
+
+void
+rb_load(fname, wrap)
+ VALUE fname;
+ int wrap;
+{
+ VALUE tmp;
+ int state;
+ volatile int prohibit_int = rb_prohibit_interrupt;
+ volatile ID callee, this_func;
+ volatile VALUE wrapper = ruby_wrapper;
+ volatile VALUE self = ruby_top_self;
+ NODE * volatile last_node;
+ NODE *saved_cref = ruby_cref;
+ TMP_PROTECT;
+
+ if (!wrap) rb_secure(4);
+ FilePathValue(fname);
+ fname = rb_str_new4(fname);
+ tmp = rb_find_file(fname);
+ if (!tmp) {
+ load_failed(fname);
+ }
+ fname = tmp;
+
+ ruby_errinfo = Qnil; /* ensure */
+ PUSH_VARS();
+ PUSH_CLASS(ruby_wrapper);
+ ruby_cref = top_cref;
+ if (!wrap) {
+ rb_secure(4); /* should alter global state */
+ ruby_class = rb_cObject;
+ ruby_wrapper = 0;
+ }
+ else {
+ /* load in anonymous module as toplevel */
+ ruby_class = ruby_wrapper = rb_module_new();
+ self = rb_obj_clone(ruby_top_self);
+ rb_extend_object(self, ruby_wrapper);
+ PUSH_CREF(ruby_wrapper);
+ }
+ PUSH_ITER(ITER_NOT);
+ PUSH_FRAME();
+ ruby_frame->callee = 0;
+ ruby_frame->this_func = 0;
+ ruby_frame->this_class = 0;
+ ruby_frame->self = self;
+ PUSH_SCOPE();
+ /* default visibility is private at loading toplevel */
+ SCOPE_SET(SCOPE_PRIVATE);
+ PUSH_TAG(PROT_NONE);
+ state = EXEC_TAG();
+ callee = ruby_frame->callee;
+ this_func = ruby_frame->this_func;
+ last_node = ruby_current_node;
+ if (!ruby_current_node && ruby_sourcefile) {
+ last_node = NEW_BEGIN(0);
+ }
+ ruby_current_node = 0;
+ if (state == 0) {
+ NODE * volatile node;
+ volatile int critical;
+
+ DEFER_INTS;
+ ruby_in_eval++;
+ critical = rb_thread_critical;
+ rb_thread_critical = Qtrue;
+ rb_load_file(RSTRING(fname)->ptr);
+ ruby_in_eval--;
+ node = ruby_eval_tree;
+ rb_thread_critical = critical;
+ ALLOW_INTS;
+ if (ruby_nerrs == 0) {
+ eval_node(self, node);
+ }
+ }
+ ruby_frame->callee = callee;
+ ruby_frame->this_func = this_func;
+ ruby_current_node = last_node;
+ ruby_sourcefile = 0;
+ ruby_set_current_source();
+ if (ruby_scope->flags == SCOPE_ALLOCA && ruby_class == rb_cObject) {
+ if (ruby_scope->local_tbl) /* toplevel was empty */
+ free(ruby_scope->local_tbl);
+ }
+ POP_TAG();
+ rb_prohibit_interrupt = prohibit_int;
+ ruby_cref = saved_cref;
+ POP_SCOPE();
+ POP_FRAME();
+ POP_ITER();
+ POP_CLASS();
+ POP_VARS();
+ ruby_wrapper = wrapper;
+ if (ruby_nerrs > 0) {
+ ruby_nerrs = 0;
+ rb_exc_raise(ruby_errinfo);
+ }
+ if (state) jump_tag_but_local_jump(state, Qundef);
+ if (!NIL_P(ruby_errinfo)) /* exception during load */
+ rb_exc_raise(ruby_errinfo);
+}
+
+void
+rb_load_protect(fname, wrap, state)
+ VALUE fname;
+ int wrap;
+ int *state;
+{
+ int status;
+
+ PUSH_THREAD_TAG();
+ if ((status = EXEC_TAG()) == 0) {
+ rb_load(fname, wrap);
+ }
+ else if (status == TAG_THREAD) {
+ rb_thread_start_1();
+ }
+ POP_THREAD_TAG();
+ if (state) *state = status;
+}
+
+/*
+ * call-seq:
+ * load(filename, wrap=false) => true
+ *
+ * Loads and executes the Ruby
+ * program in the file _filename_. If the filename does not
+ * resolve to an absolute path, the file is searched for in the library
+ * directories listed in <code>$:</code>. If the optional _wrap_
+ * parameter is +true+, the loaded script will be executed
+ * under an anonymous module, protecting the calling program's global
+ * namespace. In no circumstance will any local variables in the loaded
+ * file be propagated to the loading environment.
+ */
+
+
+static VALUE
+rb_f_load(argc, argv)
+ int argc;
+ VALUE *argv;
+{
+ VALUE fname, wrap;
+
+ rb_scan_args(argc, argv, "11", &fname, &wrap);
+ rb_load(fname, RTEST(wrap));
+ return Qtrue;
+}
+
+VALUE ruby_dln_librefs;
+static VALUE rb_features;
+static st_table *loading_tbl;
+
+#define IS_SOEXT(e) (strcmp(e, ".so") == 0 || strcmp(e, ".o") == 0)
+#ifdef DLEXT2
+#define IS_DLEXT(e) (strcmp(e, DLEXT) == 0 || strcmp(e, DLEXT2) == 0)
+#else
+#define IS_DLEXT(e) (strcmp(e, DLEXT) == 0)
+#endif
+
+static char *
+rb_feature_p(feature, ext, rb)
+ const char *feature, *ext;
+ int rb;
+{
+ VALUE v;
+ char *f, *e;
+ long i, len, elen;
+
+ if (ext) {
+ len = ext - feature;
+ elen = strlen(ext);
+ }
+ else {
+ len = strlen(feature);
+ elen = 0;
+ }
+ for (i = 0; i < RARRAY(rb_features)->len; ++i) {
+ v = RARRAY(rb_features)->ptr[i];
+ f = StringValuePtr(v);
+ if (strncmp(f, feature, len) != 0) continue;
+ if (!*(e = f + len)) {
+ if (ext) continue;
+ return e;
+ }
+ if (*e != '.') continue;
+ if ((!rb || !ext) && (IS_SOEXT(e) || IS_DLEXT(e))) {
+ return e;
+ }
+ if ((rb || !ext) && (strcmp(e, ".rb") == 0)) {
+ return e;
+ }
+ }
+ return 0;
+}
+
+static const char *const loadable_ext[] = {
+ ".rb", DLEXT,
+#ifdef DLEXT2
+ DLEXT2,
+#endif
+ 0
+};
+
+static int search_required _((VALUE, VALUE *));
+
+int
+rb_provided(feature)
+ const char *feature;
+{
+ int i;
+ char *buf;
+ VALUE fname;
+
+ if (rb_feature_p(feature, 0, Qfalse))
+ return Qtrue;
+ if (loading_tbl) {
+ if (st_lookup(loading_tbl, (st_data_t)feature, 0)) return Qtrue;
+ buf = ALLOCA_N(char, strlen(feature)+8);
+ strcpy(buf, feature);
+ for (i=0; loadable_ext[i]; i++) {
+ strcpy(buf+strlen(feature), loadable_ext[i]);
+ if (st_lookup(loading_tbl, (st_data_t)buf, 0)) return Qtrue;
+ }
+ }
+ if (search_required(rb_str_new2(feature), &fname)) {
+ feature = RSTRING(fname)->ptr;
+ if (rb_feature_p(feature, 0, Qfalse))
+ return Qtrue;
+ if (loading_tbl && st_lookup(loading_tbl, (st_data_t)feature, 0))
+ return Qtrue;
+ }
+ return Qfalse;
+}
+
+static void
+rb_provide_feature(feature)
+ VALUE feature;
+{
+ rb_ary_push(rb_features, feature);
+}
+
+void
+rb_provide(feature)
+ const char *feature;
+{
+ rb_provide_feature(rb_str_new2(feature));
+}
+
+static int
+load_wait(ftptr)
+ char *ftptr;
+{
+ st_data_t th;
+
+ if (!loading_tbl) return Qfalse;
+ if (!st_lookup(loading_tbl, (st_data_t)ftptr, &th)) return Qfalse;
+ if ((rb_thread_t)th == curr_thread) return Qtrue;
+ do {
+ CHECK_INTS;
+ rb_thread_schedule();
+ } while (st_lookup(loading_tbl, (st_data_t)ftptr, &th));
+ return Qtrue;
+}
+
+/*
+ * call-seq:
+ * require(string) => true or false
+ *
+ * Ruby tries to load the library named _string_, returning
+ * +true+ if successful. If the filename does not resolve to
+ * an absolute path, it will be searched for in the directories listed
+ * in <code>$:</code>. If the file has the extension ``.rb'', it is
+ * loaded as a source file; if the extension is ``.so'', ``.o'', or
+ * ``.dll'', or whatever the default shared library extension is on
+ * the current platform, Ruby loads the shared library as a Ruby
+ * extension. Otherwise, Ruby tries adding ``.rb'', ``.so'', and so on
+ * to the name. The name of the loaded feature is added to the array in
+ * <code>$"</code>. A feature will not be loaded if it's name already
+ * appears in <code>$"</code>. However, the file name is not converted
+ * to an absolute path, so that ``<code>require 'a';require
+ * './a'</code>'' will load <code>a.rb</code> twice.
+ *
+ * require "my-library.rb"
+ * require "db-driver"
+ */
+
+VALUE
+rb_f_require(obj, fname)
+ VALUE obj, fname;
+{
+ return rb_require_safe(fname, ruby_safe_level);
+}
+
+static int
+search_required(fname, path)
+ VALUE fname, *path;
+{
+ VALUE tmp;
+ char *ext, *ftptr;
+ int type;
+
+ *path = 0;
+ ext = strrchr(ftptr = RSTRING(fname)->ptr, '.');
+ if (ext && !strchr(ext, '/')) {
+ if (strcmp(".rb", ext) == 0) {
+ if (rb_feature_p(ftptr, ext, Qtrue)) return 'r';
+ if (tmp = rb_find_file(fname)) {
+ tmp = rb_file_expand_path(tmp, Qnil);
+ ext = strrchr(ftptr = RSTRING(tmp)->ptr, '.');
+ if (!rb_feature_p(ftptr, ext, Qtrue))
+ *path = tmp;
+ return 'r';
+ }
+ return 0;
+ }
+ else if (IS_SOEXT(ext)) {
+ if (rb_feature_p(ftptr, ext, Qfalse)) return 's';
+ tmp = rb_str_new(RSTRING(fname)->ptr, ext-RSTRING(fname)->ptr);
+#ifdef DLEXT2
+ OBJ_FREEZE(tmp);
+ if (rb_find_file_ext(&tmp, loadable_ext+1)) {
+ tmp = rb_file_expand_path(tmp, Qnil);
+ ext = strrchr(ftptr = RSTRING(tmp)->ptr, '.');
+ if (!rb_feature_p(ftptr, ext, Qfalse))
+ *path = tmp;
+ return 's';
+ }
+#else
+ rb_str_cat2(tmp, DLEXT);
+ OBJ_FREEZE(tmp);
+ if (tmp = rb_find_file(tmp)) {
+ tmp = rb_file_expand_path(tmp, Qnil);
+ ext = strrchr(ftptr = RSTRING(tmp)->ptr, '.');
+ if (!rb_feature_p(ftptr, ext, Qfalse))
+ *path = tmp;
+ return 's';
+ }
+#endif
+ }
+ else if (IS_DLEXT(ext)) {
+ if (rb_feature_p(ftptr, ext, Qfalse)) return 's';
+ if (tmp = rb_find_file(fname)) {
+ tmp = rb_file_expand_path(tmp, Qnil);
+ ext = strrchr(ftptr = RSTRING(tmp)->ptr, '.');
+ if (!rb_feature_p(ftptr, ext, Qfalse))
+ *path = tmp;
+ return 's';
+ }
+ }
+ }
+ else if (ext = rb_feature_p(ftptr, 0, Qfalse)) {
+ return (*ext && (IS_SOEXT(ext) || IS_DLEXT(ext))) ? 's' : 'r';
+ }
+ tmp = fname;
+ type = rb_find_file_ext(&tmp, loadable_ext);
+ tmp = rb_file_expand_path(tmp, Qnil);
+ switch (type) {
+ case 0:
+ ftptr = RSTRING(tmp)->ptr;
+ if ((ext = rb_feature_p(ftptr, 0, Qfalse))) {
+ type = strcmp(".rb", ext);
+ break;
+ }
+ return 0;
+
+ default:
+ ext = strrchr(ftptr = RSTRING(tmp)->ptr, '.');
+ if (rb_feature_p(ftptr, ext, !--type)) break;
+ *path = tmp;
+ }
+ return type ? 's' : 'r';
+}
+
+static void
+load_failed(fname)
+ VALUE fname;
+{
+ rb_raise(rb_eLoadError, "no such file to load -- %s", RSTRING(fname)->ptr);
+}
+
+VALUE
+rb_require_safe(fname, safe)
+ VALUE fname;
+ int safe;
+{
+ VALUE result = Qnil;
+ volatile VALUE errinfo = ruby_errinfo;
+ int state;
+ struct {
+ NODE *node;
+ ID this_func, callee;
+ int vmode, safe;
+ } volatile saved;
+ char *volatile ftptr = 0;
+
+ saved.vmode = scope_vmode;
+ saved.node = ruby_current_node;
+ saved.callee = ruby_frame->callee;
+ saved.this_func = ruby_frame->this_func;
+ saved.safe = ruby_safe_level;
+ PUSH_TAG(PROT_NONE);
+ if ((state = EXEC_TAG()) == 0) {
+ VALUE path;
+ long handle;
+ int found;
+
+ ruby_safe_level = safe;
+ FilePathValue(fname);
+ *(volatile VALUE *)&fname = rb_str_new4(fname);
+ found = search_required(fname, &path);
+ if (found) {
+ if (!path || load_wait(RSTRING(path)->ptr)) {
+ result = Qfalse;
+ }
+ else {
+ ruby_safe_level = 0;
+ switch (found) {
+ case 'r':
+ /* loading ruby library should be serialized. */
+ if (!loading_tbl) {
+ loading_tbl = st_init_strtable();
+ }
+ /* partial state */
+ ftptr = ruby_strdup(RSTRING(path)->ptr);
+ st_insert(loading_tbl, (st_data_t)ftptr, (st_data_t)curr_thread);
+ rb_load(path, 0);
+ break;
+
+ case 's':
+ ruby_current_node = 0;
+ ruby_sourcefile = rb_source_filename(RSTRING(path)->ptr);
+ ruby_sourceline = 0;
+ ruby_frame->callee = 0;
+ ruby_frame->this_func = 0;
+ SCOPE_SET(SCOPE_PUBLIC);
+ handle = (long)dln_load(RSTRING(path)->ptr);
+ rb_ary_push(ruby_dln_librefs, LONG2NUM(handle));
+ break;
+ }
+ rb_provide_feature(path);
+ result = Qtrue;
+ }
+ }
+ }
+ POP_TAG();
+ ruby_current_node = saved.node;
+ ruby_set_current_source();
+ ruby_frame->this_func = saved.this_func;
+ ruby_frame->callee = saved.callee;
+ SCOPE_SET(saved.vmode);
+ ruby_safe_level = saved.safe;
+ if (ftptr) {
+ if (st_delete(loading_tbl, (st_data_t *)&ftptr, 0)) { /* loading done */
+ free(ftptr);
+ }
+ }
+ if (state) JUMP_TAG(state);
+ if (NIL_P(result)) {
+ load_failed(fname);
+ }
+ ruby_errinfo = errinfo;
+
+ return result;
+}
+
+VALUE
+rb_require(fname)
+ const char *fname;
+{
+ VALUE fn = rb_str_new2(fname);
+ OBJ_FREEZE(fn);
+ return rb_require_safe(fn, ruby_safe_level);
+}
+
+static void
+secure_visibility(self)
+ VALUE self;
+{
+ if (ruby_safe_level >= 4 && !OBJ_TAINTED(self)) {
+ rb_raise(rb_eSecurityError, "Insecure: can't change method visibility");
+ }
+}
+
+static void
+set_method_visibility(self, argc, argv, ex)
+ VALUE self;
+ int argc;
+ VALUE *argv;
+ ID ex;
+{
+ int i;
+
+ secure_visibility(self);
+ for (i=0; i<argc; i++) {
+ rb_export_method(self, rb_to_id(argv[i]), ex);
+ }
+ rb_clear_cache_by_class(self);
+}
+
+/*
+ * call-seq:
+ * public => self
+ * public(symbol, ...) => self
+ *
+ * With no arguments, sets the default visibility for subsequently
+ * defined methods to public. With arguments, sets the named methods to
+ * have public visibility.
+ */
+
+static VALUE
+rb_mod_public(argc, argv, module)
+ int argc;
+ VALUE *argv;
+ VALUE module;
+{
+ secure_visibility(module);
+ if (argc == 0) {
+ SCOPE_SET(SCOPE_PUBLIC);
+ }
+ else {
+ set_method_visibility(module, argc, argv, NOEX_PUBLIC);
+ }
+ return module;
+}
+
+/*
+ * call-seq:
+ * protected => self
+ * protected(symbol, ...) => self
+ *
+ * With no arguments, sets the default visibility for subsequently
+ * defined methods to protected. With arguments, sets the named methods
+ * to have protected visibility.
+ */
+
+static VALUE
+rb_mod_protected(argc, argv, module)
+ int argc;
+ VALUE *argv;
+ VALUE module;
+{
+ secure_visibility(module);
+ if (argc == 0) {
+ SCOPE_SET(SCOPE_PROTECTED);
+ }
+ else {
+ set_method_visibility(module, argc, argv, NOEX_PROTECTED);
+ }
+ return module;
+}
+
+/*
+ * call-seq:
+ * private => self
+ * private(symbol, ...) => self
+ *
+ * With no arguments, sets the default visibility for subsequently
+ * defined methods to private. With arguments, sets the named methods
+ * to have private visibility.
+ *
+ * module Mod
+ * def a() end
+ * def b() end
+ * private
+ * def c() end
+ * private :a
+ * end
+ * Mod.private_instance_methods #=> ["a", "c"]
+ */
+
+static VALUE
+rb_mod_private(argc, argv, module)
+ int argc;
+ VALUE *argv;
+ VALUE module;
+{
+ secure_visibility(module);
+ if (argc == 0) {
+ SCOPE_SET(SCOPE_PRIVATE);
+ }
+ else {
+ set_method_visibility(module, argc, argv, NOEX_PRIVATE);
+ }
+ return module;
+}
+
+/*
+ * call-seq:
+ * mod.public_class_method(symbol, ...) => mod
+ *
+ * Makes a list of existing class methods public.
+ */
+
+static VALUE
+rb_mod_public_method(argc, argv, obj)
+ int argc;
+ VALUE *argv;
+ VALUE obj;
+{
+ set_method_visibility(CLASS_OF(obj), argc, argv, NOEX_PUBLIC);
+ return obj;
+}
+
+/*
+ * call-seq:
+ * mod.private_class_method(symbol, ...) => mod
+ *
+ * Makes existing class methods private. Often used to hide the default
+ * constructor <code>new</code>.
+ *
+ * class SimpleSingleton # Not thread safe
+ * private_class_method :new
+ * def SimpleSingleton.create(*args, &block)
+ * @me = new(*args, &block) if ! @me
+ * @me
+ * end
+ * end
+ */
+
+static VALUE
+rb_mod_private_method(argc, argv, obj)
+ int argc;
+ VALUE *argv;
+ VALUE obj;
+{
+ set_method_visibility(CLASS_OF(obj), argc, argv, NOEX_PRIVATE);
+ return obj;
+}
+
+/*
+ * call-seq:
+ * public
+ * public(symbol, ...)
+ *
+ * With no arguments, sets the default visibility for subsequently
+ * defined methods to public. With arguments, sets the named methods to
+ * have public visibility.
+ */
+
+static VALUE
+top_public(argc, argv)
+ int argc;
+ VALUE *argv;
+{
+ return rb_mod_public(argc, argv, rb_cObject);
+}
+
+static VALUE
+top_private(argc, argv)
+ int argc;
+ VALUE *argv;
+{
+ return rb_mod_private(argc, argv, rb_cObject);
+}
+
+/*
+ * call-seq:
+ * module_function(symbol, ...) => self
+ *
+ * Creates module functions for the named methods. These functions may
+ * be called with the module as a receiver, and also become available
+ * as instance methods to classes that mix in the module. Module
+ * functions are copies of the original, and so may be changed
+ * independently. The instance-method versions are made private. If
+ * used with no arguments, subsequently defined methods become module
+ * functions.
+ *
+ * module Mod
+ * def one
+ * "This is one"
+ * end
+ * module_function :one
+ * end
+ * class Cls
+ * include Mod
+ * def callOne
+ * one
+ * end
+ * end
+ * Mod.one #=> "This is one"
+ * c = Cls.new
+ * c.callOne #=> "This is one"
+ * module Mod
+ * def one
+ * "This is the new one"
+ * end
+ * end
+ * Mod.one #=> "This is one"
+ * c.callOne #=> "This is the new one"
+ */
+
+static VALUE
+rb_mod_modfunc(argc, argv, module)
+ int argc;
+ VALUE *argv;
+ VALUE module;
+{
+ int i;
+ ID id;
+ NODE *body;
+
+ if (TYPE(module) != T_MODULE) {
+ rb_raise(rb_eTypeError, "module_function must be called for modules");
+ }
+
+ secure_visibility(module);
+ if (argc == 0) {
+ SCOPE_SET(SCOPE_MODFUNC);
+ return module;
+ }
+
+ set_method_visibility(module, argc, argv, NOEX_PRIVATE);
+ for (i=0; i<argc; i++) {
+ VALUE m = module;
+
+ id = rb_to_id(argv[i]);
+ for (;;) {
+ body = search_method(m, id, &m);
+ if (body == 0) {
+ body = search_method(rb_cObject, id, &m);
+ }
+ if (body == 0 || body->nd_body == 0) {
+ rb_bug("undefined method `%s'; can't happen", rb_id2name(id));
+ }
+ if (nd_type(body->nd_body) != NODE_ZSUPER) {
+ break; /* normal case: need not to follow 'super' link */
+ }
+ m = RCLASS(m)->super;
+ if (!m) break;
+ }
+ rb_add_method(rb_singleton_class(module), id, body->nd_body, NOEX_PUBLIC);
+ }
+ return module;
+}
+
+/*
+ * call-seq:
+ * append_features(mod) => mod
+ *
+ * When this module is included in another, Ruby calls
+ * <code>append_features</code> in this module, passing it the
+ * receiving module in _mod_. Ruby's default implementation is
+ * to add the constants, methods, and module variables of this module
+ * to _mod_ if this module has not already been added to
+ * _mod_ or one of its ancestors. See also <code>Module#include</code>.
+ */
+
+static VALUE
+rb_mod_append_features(module, include)
+ VALUE module, include;
+{
+ switch (TYPE(include)) {
+ case T_CLASS:
+ case T_MODULE:
+ break;
+ default:
+ Check_Type(include, T_CLASS);
+ break;
+ }
+ rb_include_module(include, module);
+
+ return module;
+}
+
+/*
+ * call-seq:
+ * include(module, ...) => self
+ *
+ * Invokes <code>Module.append_features</code> on each parameter in turn.
+ */
+
+static VALUE
+rb_mod_include(argc, argv, module)
+ int argc;
+ VALUE *argv;
+ VALUE module;
+{
+ int i;
+
+ for (i=0; i<argc; i++) Check_Type(argv[i], T_MODULE);
+ while (argc--) {
+ rb_funcall(argv[argc], rb_intern("append_features"), 1, module);
+ rb_funcall(argv[argc], rb_intern("included"), 1, module);
+ }
+ return module;
+}
+
+void
+rb_obj_call_init(obj, argc, argv)
+ VALUE obj;
+ int argc;
+ VALUE *argv;
+{
+ PUSH_ITER(rb_block_given_p()?ITER_PRE:ITER_NOT);
+ rb_funcall2(obj, init, argc, argv);
+ POP_ITER();
+}
+
+void
+rb_extend_object(obj, module)
+ VALUE obj, module;
+{
+ rb_include_module(rb_singleton_class(obj), module);
+}
+
+/*
+ * call-seq:
+ * extend_object(obj) => obj
+ *
+ * Extends the specified object by adding this module's constants and
+ * methods (which are added as singleton methods). This is the callback
+ * method used by <code>Object#extend</code>.
+ *
+ * module Picky
+ * def Picky.extend_object(o)
+ * if String === o
+ * puts "Can't add Picky to a String"
+ * else
+ * puts "Picky added to #{o.class}"
+ * super
+ * end
+ * end
+ * end
+ * (s = Array.new).extend Picky # Call Object.extend
+ * (s = "quick brown fox").extend Picky
+ *
+ * <em>produces:</em>
+ *
+ * Picky added to Array
+ * Can't add Picky to a String
+ */
+
+static VALUE
+rb_mod_extend_object(mod, obj)
+ VALUE mod, obj;
+{
+ rb_extend_object(obj, mod);
+ return obj;
+}
+
+/*
+ * call-seq:
+ * obj.extend(module, ...) => obj
+ *
+ * Adds to _obj_ the instance methods from each module given as a
+ * parameter.
+ *
+ * module Mod
+ * def hello
+ * "Hello from Mod.\n"
+ * end
+ * end
+ *
+ * class Klass
+ * def hello
+ * "Hello from Klass.\n"
+ * end
+ * end
+ *
+ * k = Klass.new
+ * k.hello #=> "Hello from Klass.\n"
+ * k.extend(Mod) #=> #<Klass:0x401b3bc8>
+ * k.hello #=> "Hello from Mod.\n"
+ */
+
+static VALUE
+rb_obj_extend(argc, argv, obj)
+ int argc;
+ VALUE *argv;
+ VALUE obj;
+{
+ int i;
+
+ if (argc == 0) {
+ rb_raise(rb_eArgError, "wrong number of arguments (0 for 1)");
+ }
+ for (i=0; i<argc; i++) Check_Type(argv[i], T_MODULE);
+ while (argc--) {
+ rb_funcall(argv[argc], rb_intern("extend_object"), 1, obj);
+ rb_funcall(argv[argc], rb_intern("extended"), 1, obj);
+ }
+ return obj;
+}
+
+/*
+ * call-seq:
+ * include(module, ...) => self
+ *
+ * Invokes <code>Module.append_features</code>
+ * on each parameter in turn. Effectively adds the methods and constants
+ * in each module to the receiver.
+ */
+
+static VALUE
+top_include(argc, argv, self)
+ int argc;
+ VALUE *argv;
+ VALUE self;
+{
+ rb_secure(4);
+ if (ruby_wrapper) {
+ rb_warning("main#include in the wrapped load is effective only in wrapper module");
+ return rb_mod_include(argc, argv, ruby_wrapper);
+ }
+ return rb_mod_include(argc, argv, rb_cObject);
+}
+
+VALUE rb_f_trace_var();
+VALUE rb_f_untrace_var();
+
+static void
+errinfo_setter(val, id, var)
+ VALUE val;
+ ID id;
+ VALUE *var;
+{
+ if (!NIL_P(val) && !rb_obj_is_kind_of(val, rb_eException)) {
+ rb_raise(rb_eTypeError, "assigning non-exception to $!");
+ }
+ *var = val;
+}
+
+static VALUE
+errat_getter(id)
+ ID id;
+{
+ return get_backtrace(ruby_errinfo);
+}
+
+static void
+errat_setter(val, id, var)
+ VALUE val;
+ ID id;
+ VALUE *var;
+{
+ if (NIL_P(ruby_errinfo)) {
+ rb_raise(rb_eArgError, "$! not set");
+ }
+ set_backtrace(ruby_errinfo, val);
+}
+
+/*
+ * call-seq:
+ * local_variables => array
+ *
+ * Returns the names of the current local variables.
+ *
+ * fred = 1
+ * for i in 1..10
+ * # ...
+ * end
+ * local_variables #=> ["fred", "i"]
+ */
+
+static VALUE
+rb_f_local_variables()
+{
+ ID *tbl;
+ int n, i;
+ VALUE ary = rb_ary_new();
+ struct RVarmap *vars;
+
+ tbl = ruby_scope->local_tbl;
+ if (tbl) {
+ n = *tbl++;
+ for (i=2; i<n; i++) { /* skip first 2 ($_ and $~) */
+ if (!rb_is_local_id(tbl[i])) continue; /* skip flip states */
+ rb_ary_push(ary, rb_str_new2(rb_id2name(tbl[i])));
+ }
+ }
+
+ vars = ruby_dyna_vars;
+ while (vars) {
+ if (vars->id && rb_is_local_id(vars->id)) { /* skip $_, $~ and flip states */
+ rb_ary_push(ary, rb_str_new2(rb_id2name(vars->id)));
+ }
+ vars = vars->next;
+ }
+
+ return ary;
+}
+
+static VALUE rb_f_catch _((VALUE,VALUE));
+NORETURN(static VALUE rb_f_throw _((int,VALUE*)));
+
+struct end_proc_data {
+ void (*func)();
+ VALUE data;
+ int safe;
+ struct end_proc_data *next;
+};
+
+static struct end_proc_data *end_procs, *ephemeral_end_procs, *tmp_end_procs;
+
+void
+rb_set_end_proc(func, data)
+ void (*func) _((VALUE));
+ VALUE data;
+{
+ struct end_proc_data *link = ALLOC(struct end_proc_data);
+ struct end_proc_data **list;
+
+ if (ruby_wrapper) list = &ephemeral_end_procs;
+ else list = &end_procs;
+ link->next = *list;
+ link->func = func;
+ link->data = data;
+ link->safe = ruby_safe_level;
+ *list = link;
+}
+
+void
+rb_mark_end_proc()
+{
+ struct end_proc_data *link;
+
+ link = end_procs;
+ while (link) {
+ rb_gc_mark(link->data);
+ link = link->next;
+ }
+ link = ephemeral_end_procs;
+ while (link) {
+ rb_gc_mark(link->data);
+ link = link->next;
+ }
+ link = tmp_end_procs;
+ while (link) {
+ rb_gc_mark(link->data);
+ link = link->next;
+ }
+}
+
+static void call_end_proc _((VALUE data));
+
+static void
+call_end_proc(data)
+ VALUE data;
+{
+ PUSH_ITER(ITER_NOT);
+ PUSH_FRAME();
+ ruby_frame->self = ruby_frame->prev->self;
+ ruby_frame->node = 0;
+ ruby_frame->callee = 0;
+ ruby_frame->this_func = 0;
+ ruby_frame->this_class = 0;
+ proc_invoke(data, rb_ary_new2(0), Qundef, 0);
+ POP_FRAME();
+ POP_ITER();
+}
+
+static void
+rb_f_END()
+{
+ PUSH_FRAME();
+ ruby_frame->argc = 0;
+ ruby_frame->iter = ITER_CUR;
+ rb_set_end_proc(call_end_proc, rb_block_proc());
+ POP_FRAME();
+}
+
+/*
+ * call-seq:
+ * at_exit { block } -> proc
+ *
+ * Converts _block_ to a +Proc+ object (and therefore
+ * binds it at the point of call) and registers it for execution when
+ * the program exits. If multiple handlers are registered, they are
+ * executed in reverse order of registration.
+ *
+ * def do_at_exit(str1)
+ * at_exit { print str1 }
+ * end
+ * at_exit { puts "cruel world" }
+ * do_at_exit("goodbye ")
+ * exit
+ *
+ * <em>produces:</em>
+ *
+ * goodbye cruel world
+ */
+
+static VALUE
+rb_f_at_exit()
+{
+ VALUE proc;
+
+ if (!rb_block_given_p()) {
+ rb_raise(rb_eArgError, "called without a block");
+ }
+ proc = rb_block_proc();
+ rb_set_end_proc(call_end_proc, proc);
+ return proc;
+}
+
+void
+rb_exec_end_proc()
+{
+ struct end_proc_data *link, *tmp;
+ int status;
+ volatile int safe = ruby_safe_level;
+
+ while (ephemeral_end_procs) {
+ tmp_end_procs = link = ephemeral_end_procs;
+ ephemeral_end_procs = 0;
+ while (link) {
+ PUSH_TAG(PROT_NONE);
+ if ((status = EXEC_TAG()) == 0) {
+ ruby_safe_level = link->safe;
+ (*link->func)(link->data);
+ }
+ POP_TAG();
+ if (status) {
+ error_handle(status);
+ }
+ tmp = link;
+ tmp_end_procs = link = link->next;
+ free(tmp);
+ }
+ }
+ while (end_procs) {
+ tmp_end_procs = link = end_procs;
+ end_procs = 0;
+ while (link) {
+ PUSH_TAG(PROT_NONE);
+ if ((status = EXEC_TAG()) == 0) {
+ ruby_safe_level = link->safe;
+ (*link->func)(link->data);
+ }
+ POP_TAG();
+ if (status) {
+ error_handle(status);
+ }
+ tmp = link;
+ tmp_end_procs = link = link->next;
+ free(tmp);
+ }
+ }
+ ruby_safe_level = safe;
+}
+
+void
+Init_eval()
+{
+ init = rb_intern("initialize");
+ eqq = rb_intern("===");
+ each = rb_intern("each");
+
+ aref = rb_intern("[]");
+ aset = rb_intern("[]=");
+ match = rb_intern("=~");
+ missing = rb_intern("method_missing");
+ added = rb_intern("method_added");
+ singleton_added = rb_intern("singleton_method_added");
+ removed = rb_intern("method_removed");
+ singleton_removed = rb_intern("singleton_method_removed");
+ undefined = rb_intern("method_undefined");
+ singleton_undefined = rb_intern("singleton_method_undefined");
+
+ __id__ = rb_intern("__id__");
+ __send__ = rb_intern("__send__");
+
+ rb_global_variable((VALUE*)&top_scope);
+ rb_global_variable((VALUE*)&ruby_eval_tree);
+ rb_global_variable((VALUE*)&ruby_dyna_vars);
+
+ rb_define_virtual_variable("$@", errat_getter, errat_setter);
+ rb_define_hooked_variable("$!", &ruby_errinfo, 0, errinfo_setter);
+
+ rb_define_global_function("eval", rb_f_eval, -1);
+ rb_define_global_function("iterator?", rb_f_block_given_p, 0);
+ rb_define_global_function("block_given?", rb_f_block_given_p, 0);
+ rb_define_global_function("method_missing", rb_method_missing, -1);
+ rb_define_global_function("loop", rb_f_loop, 0);
+
+ rb_define_method(rb_mKernel, "respond_to?", rb_obj_respond_to, -1);
+ respond_to = rb_intern("respond_to?");
+ basic_respond_to = rb_method_node(rb_cObject, respond_to);
+ rb_global_variable((VALUE*)&basic_respond_to);
+
+ rb_define_global_function("raise", rb_f_raise, -1);
+ rb_define_global_function("fail", rb_f_raise, -1);
+
+ rb_define_global_function("caller", rb_f_caller, -1);
+
+ rb_define_global_function("exit", rb_f_exit, -1);
+ rb_define_global_function("abort", rb_f_abort, -1);
+
+ rb_define_global_function("at_exit", rb_f_at_exit, 0);
+
+ rb_define_global_function("catch", rb_f_catch, 1);
+ rb_define_global_function("throw", rb_f_throw, -1);
+ rb_define_global_function("global_variables", rb_f_global_variables, 0); /* in variable.c */
+ rb_define_global_function("local_variables", rb_f_local_variables, 0);
+
+ rb_define_method(rb_mKernel, "send", rb_f_send, -1);
+ rb_define_method(rb_mKernel, "__send__", rb_f_send, -1);
+ rb_define_method(rb_mKernel, "instance_eval", rb_obj_instance_eval, -1);
+
+ rb_define_private_method(rb_cModule, "append_features", rb_mod_append_features, 1);
+ rb_define_private_method(rb_cModule, "extend_object", rb_mod_extend_object, 1);
+ rb_define_private_method(rb_cModule, "include", rb_mod_include, -1);
+ rb_define_private_method(rb_cModule, "public", rb_mod_public, -1);
+ rb_define_private_method(rb_cModule, "protected", rb_mod_protected, -1);
+ rb_define_private_method(rb_cModule, "private", rb_mod_private, -1);
+ rb_define_private_method(rb_cModule, "module_function", rb_mod_modfunc, -1);
+ rb_define_method(rb_cModule, "method_defined?", rb_mod_method_defined, 1);
+ rb_define_method(rb_cModule, "public_method_defined?", rb_mod_public_method_defined, 1);
+ rb_define_method(rb_cModule, "private_method_defined?", rb_mod_private_method_defined, 1);
+ rb_define_method(rb_cModule, "protected_method_defined?", rb_mod_protected_method_defined, 1);
+ rb_define_method(rb_cModule, "public_class_method", rb_mod_public_method, -1);
+ rb_define_method(rb_cModule, "private_class_method", rb_mod_private_method, -1);
+ rb_define_method(rb_cModule, "module_eval", rb_mod_module_eval, -1);
+ rb_define_method(rb_cModule, "class_eval", rb_mod_module_eval, -1);
+
+ rb_undef_method(rb_cClass, "module_function");
+
+ rb_define_private_method(rb_cModule, "remove_method", rb_mod_remove_method, -1);
+ rb_define_private_method(rb_cModule, "undef_method", rb_mod_undef_method, -1);
+ rb_define_private_method(rb_cModule, "alias_method", rb_mod_alias_method, 2);
+ rb_define_private_method(rb_cModule, "define_method", rb_mod_define_method, -1);
+
+ rb_define_singleton_method(rb_cModule, "nesting", rb_mod_nesting, 0);
+ rb_define_singleton_method(rb_cModule, "constants", rb_mod_s_constants, 0);
+
+ rb_define_singleton_method(ruby_top_self, "include", top_include, -1);
+ rb_define_singleton_method(ruby_top_self, "public", top_public, -1);
+ rb_define_singleton_method(ruby_top_self, "private", top_private, -1);
+
+ rb_define_method(rb_mKernel, "extend", rb_obj_extend, -1);
+
+ rb_define_global_function("trace_var", rb_f_trace_var, -1); /* in variable.c */
+ rb_define_global_function("untrace_var", rb_f_untrace_var, -1); /* in variable.c */
+
+ rb_define_global_function("set_trace_func", set_trace_func, 1);
+ rb_global_variable(&trace_func);
+
+ rb_define_virtual_variable("$SAFE", safe_getter, safe_setter);
+}
+
+/*
+ * call-seq:
+ * mod.autoload(name, filename) => nil
+ *
+ * Registers _filename_ to be loaded (using <code>Kernel::require</code>)
+ * the first time that _module_ (which may be a <code>String</code> or
+ * a symbol) is accessed in the namespace of _mod_.
+ *
+ * module A
+ * end
+ * A.autoload(:B, "b")
+ * A::B.doit # autoloads "b"
+ */
+
+static VALUE
+rb_mod_autoload(mod, sym, file)
+ VALUE mod;
+ VALUE sym;
+ VALUE file;
+{
+ ID id = rb_to_id(sym);
+
+ Check_SafeStr(file);
+ rb_autoload(mod, id, RSTRING(file)->ptr);
+ return Qnil;
+}
+
+/*
+ * MISSING: documentation
+ */
+
+static VALUE
+rb_mod_autoload_p(mod, sym)
+ VALUE mod, sym;
+{
+ return rb_autoload_p(mod, rb_to_id(sym));
+}
+
+/*
+ * call-seq:
+ * autoload(module, filename) => nil
+ *
+ * Registers _filename_ to be loaded (using <code>Kernel::require</code>)
+ * the first time that _module_ (which may be a <code>String</code> or
+ * a symbol) is accessed.
+ *
+ * autoload(:MyModule, "/usr/local/lib/modules/my_module.rb")
+ */
+
+static VALUE
+rb_f_autoload(obj, sym, file)
+ VALUE obj;
+ VALUE sym;
+ VALUE file;
+{
+ return rb_mod_autoload(ruby_cbase, sym, file);
+}
+
+
+/*
+ * MISSING: documentation
+ */
+
+static VALUE
+rb_f_autoload_p(obj, sym)
+ VALUE obj;
+ VALUE sym;
+{
+ /* use ruby_cbase as same as rb_f_autoload. */
+ return rb_mod_autoload_p(ruby_cbase, sym);
+}
+
+void
+Init_load()
+{
+ rb_load_path = rb_ary_new();
+ rb_define_readonly_variable("$:", &rb_load_path);
+ rb_define_readonly_variable("$-I", &rb_load_path);
+ rb_define_readonly_variable("$LOAD_PATH", &rb_load_path);
+
+ rb_features = rb_ary_new();
+ rb_define_readonly_variable("$\"", &rb_features);
+ rb_define_readonly_variable("$LOADED_FEATURES", &rb_features);
+
+ rb_define_global_function("load", rb_f_load, -1);
+ rb_define_global_function("require", rb_f_require, 1);
+ rb_define_method(rb_cModule, "autoload", rb_mod_autoload, 2);
+ rb_define_method(rb_cModule, "autoload?", rb_mod_autoload_p, 1);
+ rb_define_global_function("autoload", rb_f_autoload, 2);
+ rb_define_global_function("autoload?", rb_f_autoload_p, 1);
+ rb_global_variable(&ruby_wrapper);
+
+ ruby_dln_librefs = rb_ary_new();
+ rb_global_variable(&ruby_dln_librefs);
+}
+
+static void
+scope_dup(scope)
+ struct SCOPE *scope;
+{
+ volatile ID *tbl;
+ VALUE *vars;
+
+ scope->flags |= SCOPE_DONT_RECYCLE;
+ if (scope->flags & SCOPE_MALLOC) return;
+
+ if (scope->local_tbl) {
+ tbl = scope->local_tbl;
+ vars = ALLOC_N(VALUE, tbl[0]+1);
+ *vars++ = scope->local_vars[-1];
+ MEMCPY(vars, scope->local_vars, VALUE, tbl[0]);
+ scope->local_vars = vars;
+ scope->flags |= SCOPE_MALLOC;
+ }
+}
+
+static void
+blk_mark(data)
+ struct BLOCK *data;
+{
+ while (data) {
+ rb_gc_mark_frame(&data->frame);
+ rb_gc_mark((VALUE)data->scope);
+ rb_gc_mark((VALUE)data->var);
+ rb_gc_mark((VALUE)data->body);
+ rb_gc_mark((VALUE)data->self);
+ rb_gc_mark((VALUE)data->dyna_vars);
+ rb_gc_mark((VALUE)data->cref);
+ rb_gc_mark(data->wrapper);
+ rb_gc_mark(data->block_obj);
+ data = data->prev;
+ }
+}
+
+static void
+frame_free(frame)
+ struct FRAME *frame;
+{
+ struct FRAME *tmp;
+
+ frame = frame->prev;
+ while (frame) {
+ tmp = frame;
+ frame = frame->prev;
+ free(tmp);
+ }
+}
+
+static void
+blk_free(data)
+ struct BLOCK *data;
+{
+ void *tmp;
+
+ while (data) {
+ frame_free(&data->frame);
+ tmp = data;
+ data = data->prev;
+ free(tmp);
+ }
+}
+
+static void
+frame_dup(frame)
+ struct FRAME *frame;
+{
+ struct FRAME *tmp;
+
+ for (;;) {
+ frame->tmp = 0; /* should not preserve tmp */
+ if (!frame->prev) break;
+ tmp = ALLOC(struct FRAME);
+ *tmp = *frame->prev;
+ frame->prev = tmp;
+ frame = tmp;
+ }
+}
+
+static void
+blk_copy_prev(block)
+ struct BLOCK *block;
+{
+ struct BLOCK *tmp;
+ struct RVarmap* vars;
+
+ while (block->prev) {
+ tmp = ALLOC_N(struct BLOCK, 1);
+ MEMCPY(tmp, block->prev, struct BLOCK, 1);
+ scope_dup(tmp->scope);
+ frame_dup(&tmp->frame);
+
+ for (vars = tmp->dyna_vars; vars; vars = vars->next) {
+ if (FL_TEST(vars, DVAR_DONT_RECYCLE)) break;
+ FL_SET(vars, DVAR_DONT_RECYCLE);
+ }
+
+ block->prev = tmp;
+ block = tmp;
+ }
+}
+
+
+static void
+blk_dup(dup, orig)
+ struct BLOCK *dup, *orig;
+{
+ MEMCPY(dup, orig, struct BLOCK, 1);
+ frame_dup(&dup->frame);
+
+ if (dup->iter) {
+ blk_copy_prev(dup);
+ }
+ else {
+ dup->prev = 0;
+ }
+}
+
+/*
+ * MISSING: documentation
+ */
+
+static VALUE
+proc_clone(self)
+ VALUE self;
+{
+ struct BLOCK *orig, *data;
+ VALUE bind;
+
+ Data_Get_Struct(self, struct BLOCK, orig);
+ bind = Data_Make_Struct(rb_obj_class(self),struct BLOCK,blk_mark,blk_free,data);
+ CLONESETUP(bind, self);
+ blk_dup(data, orig);
+
+ return bind;
+}
+
+/*
+ * MISSING: documentation
+ */
+
+static VALUE
+proc_dup(self)
+ VALUE self;
+{
+ struct BLOCK *orig, *data;
+ VALUE bind;
+
+ Data_Get_Struct(self, struct BLOCK, orig);
+ bind = Data_Make_Struct(rb_obj_class(self),struct BLOCK,blk_mark,blk_free,data);
+ blk_dup(data, orig);
+
+ return bind;
+}
+
+/*
+ * call-seq:
+ * binding -> a_binding
+ *
+ * Returns a +Binding+ object, describing the variable and
+ * method bindings at the point of call. This object can be used when
+ * calling +eval+ to execute the evaluated command in this
+ * environment. Also see the description of class +Binding+.
+ *
+ * def getBinding(param)
+ * return binding
+ * end
+ * b = getBinding("hello")
+ * eval("param", b) #=> "hello"
+ */
+
+static VALUE
+rb_f_binding(self)
+ VALUE self;
+{
+ struct BLOCK *data, *p;
+ struct RVarmap *vars;
+ VALUE bind;
+
+ PUSH_BLOCK(0,0);
+ bind = Data_Make_Struct(rb_cBinding,struct BLOCK,blk_mark,blk_free,data);
+ *data = *ruby_block;
+
+ data->orig_thread = rb_thread_current();
+ data->wrapper = ruby_wrapper;
+ data->iter = rb_f_block_given_p();
+ frame_dup(&data->frame);
+ if (ruby_frame->prev) {
+ data->frame.callee = ruby_frame->prev->callee;
+ data->frame.this_func = ruby_frame->prev->this_func;
+ data->frame.this_class = ruby_frame->prev->this_class;
+ }
+
+ if (data->iter) {
+ blk_copy_prev(data);
+ }
+ else {
+ data->prev = 0;
+ }
+
+ for (p = data; p; p = p->prev) {
+ for (vars = p->dyna_vars; vars; vars = vars->next) {
+ if (FL_TEST(vars, DVAR_DONT_RECYCLE)) break;
+ FL_SET(vars, DVAR_DONT_RECYCLE);
+ }
+ }
+ scope_dup(data->scope);
+ POP_BLOCK();
+
+ return bind;
+}
+
+/*
+ * call-seq:
+ * binding.eval(string [, filename [,lineno]]) => obj
+ *
+ * Evaluates the Ruby expression(s) in <em>string</em>, in the
+ * <em>binding</em>'s context. If the optional <em>filename</em> and
+ * <em>lineno</em> parameters are present, they will be used when
+ * reporting syntax errors.
+ *
+ * def getBinding(param)
+ * return binding
+ * end
+ * b = getBinding("hello")
+ * b.eval("param") #=> "hello"
+ */
+
+static VALUE
+bind_eval(argc, argv, bind)
+ int argc;
+ VALUE *argv;
+ VALUE bind;
+{
+ struct BLOCK *data;
+ VALUE args[4];
+
+ rb_scan_args(argc, argv, "12", &args[0], &args[2], &args[3]);
+ args[1] = bind;
+ Data_Get_Struct(bind, struct BLOCK, data);
+
+ return rb_f_eval(argc+1, args, data->self);
+}
+
+#define PROC_TSHIFT (FL_USHIFT+1)
+#define PROC_TMASK (FL_USER1|FL_USER2|FL_USER3)
+#define PROC_TMAX (PROC_TMASK >> PROC_TSHIFT)
+#define PROC_NOSAFE FL_USER4
+
+#define SAFE_LEVEL_MAX PROC_TMASK
+
+#define proc_safe_level_p(data) (!(RBASIC(data)->flags & PROC_NOSAFE))
+
+static void
+proc_save_safe_level(data)
+ VALUE data;
+{
+ int safe = ruby_safe_level;
+ if (safe > PROC_TMAX) safe = PROC_TMAX;
+ FL_SET(data, (safe << PROC_TSHIFT) & PROC_TMASK);
+}
+
+static int
+proc_get_safe_level(data)
+ VALUE data;
+{
+ return (RBASIC(data)->flags & PROC_TMASK) >> PROC_TSHIFT;
+}
+
+static void
+proc_set_safe_level(data)
+ VALUE data;
+{
+ if (!proc_safe_level_p(data)) return;
+ ruby_safe_level = proc_get_safe_level(data);
+}
+
+static VALUE
+proc_alloc(klass, proc)
+ VALUE klass;
+ int proc;
+{
+ volatile VALUE block;
+ struct BLOCK *data, *p;
+ struct RVarmap *vars;
+
+ if (!rb_block_given_p() && !rb_f_block_given_p()) {
+ rb_raise(rb_eArgError, "tried to create Proc object without a block");
+ }
+ if (proc && !rb_block_given_p()) {
+ rb_warn("tried to create Proc object without a block");
+ }
+
+ if (!proc && ruby_block->block_obj) {
+ VALUE obj = ruby_block->block_obj;
+ if (CLASS_OF(obj) != klass) {
+ obj = proc_clone(obj);
+ RBASIC(obj)->klass = klass;
+ }
+ return obj;
+ }
+ block = Data_Make_Struct(klass, struct BLOCK, blk_mark, blk_free, data);
+ *data = *ruby_block;
+
+ data->orig_thread = rb_thread_current();
+ data->wrapper = ruby_wrapper;
+ data->iter = data->prev?Qtrue:Qfalse;
+ data->block_obj = block;
+ frame_dup(&data->frame);
+ if (data->iter) {
+ blk_copy_prev(data);
+ }
+ else {
+ data->prev = 0;
+ }
+
+ for (p = data; p; p = p->prev) {
+ for (vars = p->dyna_vars; vars; vars = vars->next) {
+ if (FL_TEST(vars, DVAR_DONT_RECYCLE)) break;
+ FL_SET(vars, DVAR_DONT_RECYCLE);
+ }
+ }
+ scope_dup(data->scope);
+ proc_save_safe_level(block);
+ if (proc) {
+ data->flags |= BLOCK_LAMBDA;
+ }
+ else {
+ ruby_block->block_obj = block;
+ }
+
+ return block;
+}
+
+/*
+ * call-seq:
+ * Proc.new {|...| block } => a_proc
+ * Proc.new => a_proc
+ *
+ * Creates a new <code>Proc</code> object, bound to the current
+ * context. <code>Proc::new</code> may be called without a block only
+ * within a method with an attached block, in which case that block is
+ * converted to the <code>Proc</code> object.
+ *
+ * def proc_from
+ * Proc.new
+ * end
+ * proc = proc_from { "hello" }
+ * proc.call #=> "hello"
+ */
+
+static VALUE
+proc_s_new(argc, argv, klass)
+ int argc;
+ VALUE *argv;
+ VALUE klass;
+{
+ VALUE block = proc_alloc(klass, Qfalse);
+
+ rb_obj_call_init(block, argc, argv);
+ return block;
+}
+
+/*
+ * call-seq:
+ * proc { |...| block } => a_proc
+ *
+ * Equivalent to <code>Proc.new</code>.
+ */
+
+VALUE
+rb_block_proc()
+{
+ return proc_alloc(rb_cProc, Qfalse);
+}
+
+VALUE
+rb_f_lambda()
+{
+ rb_warn("rb_f_lambda() is deprecated; use rb_block_proc() instead");
+ return proc_alloc(rb_cProc, Qtrue);
+}
+
+/*
+ * call-seq:
+ * lambda { |...| block } => a_proc
+ *
+ * Equivalent to <code>Proc.new</code>, except the resulting Proc objects
+ * check the number of parameters passed when called.
+ */
+
+static VALUE
+proc_lambda()
+{
+ return proc_alloc(rb_cProc, Qtrue);
+}
+
+static int
+block_orphan(data)
+ struct BLOCK *data;
+{
+ if (data->scope->flags & SCOPE_NOSTACK) {
+ return 1;
+ }
+ if (data->orig_thread != rb_thread_current()) {
+ return 1;
+ }
+ return 0;
+}
+
+static VALUE
+proc_invoke(proc, args, self, klass)
+ VALUE proc, args; /* OK */
+ VALUE self, klass;
+{
+ struct BLOCK * volatile old_block;
+ struct BLOCK _block;
+ struct BLOCK *data;
+ volatile VALUE result = Qundef;
+ int state;
+ volatile int safe = ruby_safe_level;
+ volatile VALUE old_wrapper = ruby_wrapper;
+ volatile int pcall, avalue = Qtrue;
+ VALUE bvar = Qnil, tmp = args;
+
+ Data_Get_Struct(proc, struct BLOCK, data);
+ pcall = (data->flags & BLOCK_LAMBDA) ? YIELD_LAMBDA_CALL : 0;
+ if (!pcall && RARRAY(args)->len == 1) {
+ avalue = Qfalse;
+ args = RARRAY(args)->ptr[0];
+ }
+ if (rb_block_given_p() && ruby_frame->callee) {
+ if (klass != ruby_frame->this_class)
+ klass = rb_obj_class(proc);
+ bvar = rb_block_proc();
+ }
+
+ PUSH_VARS();
+ ruby_wrapper = data->wrapper;
+ ruby_dyna_vars = data->dyna_vars;
+ /* PUSH BLOCK from data */
+ old_block = ruby_block;
+ _block = *data;
+ _block.block_obj = bvar;
+ if (self != Qundef) _block.frame.self = self;
+ if (klass) _block.frame.this_class = klass;
+ _block.frame.argc = RARRAY(tmp)->len;
+ if (_block.frame.argc && (ruby_frame->flags & FRAME_DMETH)) {
+ NEWOBJ(scope, struct SCOPE);
+ OBJSETUP(scope, tmp, T_SCOPE);
+ scope->local_tbl = _block.scope->local_tbl;
+ scope->local_vars = _block.scope->local_vars;
+ _block.scope = scope;
+ }
+ ruby_block = &_block;
+
+ PUSH_ITER(ITER_CUR);
+ ruby_frame->iter = ITER_CUR;
+ PUSH_TAG((pcall&YIELD_LAMBDA_CALL) ? PROT_LAMBDA : PROT_NONE);
+ state = EXEC_TAG();
+ if (state == 0) {
+ proc_set_safe_level(proc);
+ result = rb_yield_0(args, self, (self!=Qundef)?CLASS_OF(self):0,
+ pcall | YIELD_PROC_CALL, avalue);
+ }
+ else if (TAG_DST()) {
+ result = prot_tag->retval;
+ }
+ POP_TAG();
+ POP_ITER();
+ ruby_block = old_block;
+ ruby_wrapper = old_wrapper;
+ POP_VARS();
+ if (proc_safe_level_p(proc)) ruby_safe_level = safe;
+
+ switch (state) {
+ case 0:
+ break;
+ case TAG_RETRY:
+ proc_jump_error(TAG_RETRY, Qnil); /* xxx */
+ JUMP_TAG(state);
+ break;
+ case TAG_BREAK:
+ if (!pcall && result != Qundef) {
+ proc_jump_error(state, result);
+ }
+ case TAG_RETURN:
+ if (result != Qundef) {
+ if (pcall) break;
+ return_jump(result);
+ }
+ default:
+ JUMP_TAG(state);
+ }
+ return result;
+}
+
+/* CHECKME: are the argument checking semantics correct? */
+
+/*
+ * call-seq:
+ * prc.call(params,...) => obj
+ * prc[params,...] => obj
+ *
+ * Invokes the block, setting the block's parameters to the values in
+ * <i>params</i> using something close to method calling semantics.
+ * Generates a warning if multiple values are passed to a proc that
+ * expects just one (previously this silently converted the parameters
+ * to an array).
+ *
+ * For procs created using <code>Kernel.proc</code>, generates an
+ * error if the wrong number of parameters
+ * are passed to a proc with multiple parameters. For procs created using
+ * <code>Proc.new</code>, extra parameters are silently discarded.
+ *
+ * Returns the value of the last expression evaluated in the block. See
+ * also <code>Proc#yield</code>.
+ *
+ * a_proc = Proc.new {|a, *b| b.collect {|i| i*a }}
+ * a_proc.call(9, 1, 2, 3) #=> [9, 18, 27]
+ * a_proc[9, 1, 2, 3] #=> [9, 18, 27]
+ * a_proc = Proc.new {|a,b| a}
+ * a_proc.call(1,2,3)
+ *
+ * <em>produces:</em>
+ *
+ * prog.rb:5: wrong number of arguments (3 for 2) (ArgumentError)
+ * from prog.rb:4:in `call'
+ * from prog.rb:5
+ */
+
+static VALUE
+proc_call(proc, args)
+ VALUE proc, args; /* OK */
+{
+ return proc_invoke(proc, args, Qundef, 0);
+}
+
+int
+rb_proc_arity(proc)
+ VALUE proc;
+{
+ struct BLOCK *data;
+ NODE *var, *list;
+ int n;
+
+ Data_Get_Struct(proc, struct BLOCK, data);
+ var = data->var;
+ if (var == 0) {
+ if (data->body && nd_type(data->body) == NODE_IFUNC &&
+ data->body->nd_cfnc == bmcall) {
+ return method_arity(data->body->nd_tval);
+ }
+ return 0;
+ }
+ if (var == (NODE*)1) return 0;
+ if (var == (NODE*)2) return 0;
+ if (nd_type(var) == NODE_BLOCK_ARG) {
+ var = var->nd_args;
+ if (var == (NODE*)1) return 0;
+ if (var == (NODE*)2) return 0;
+ }
+ switch (nd_type(var)) {
+ default:
+ return 1;
+ case NODE_MASGN:
+ list = var->nd_head;
+ n = 0;
+ while (list) {
+ n++;
+ list = list->nd_next;
+ }
+ if (var->nd_args) return -n-1;
+ return n;
+ }
+}
+
+/*
+ * call-seq:
+ * prc.arity -> fixnum
+ *
+ * Returns the number of arguments that would not be ignored. If the block
+ * is declared to take no arguments, returns 0. If the block is known
+ * to take exactly n arguments, returns n. If the block has optional
+ * arguments, return -n-1, where n is the number of mandatory
+ * arguments. A <code>proc</code> with no argument declarations
+ * is the same a block declaring <code>||</code> as its arguments.
+ *
+ * Proc.new {}.arity #=> 0
+ * Proc.new {||}.arity #=> 0
+ * Proc.new {|a|}.arity #=> 1
+ * Proc.new {|a,b|}.arity #=> 2
+ * Proc.new {|a,b,c|}.arity #=> 3
+ * Proc.new {|*a|}.arity #=> -1
+ * Proc.new {|a,*b|}.arity #=> -2
+ */
+
+static VALUE
+proc_arity(proc)
+ VALUE proc;
+{
+ int arity = rb_proc_arity(proc);
+ return INT2FIX(arity);
+}
+
+/*
+ * call-seq:
+ * prc == other_proc => true or false
+ *
+ * Return <code>true</code> if <i>prc</i> is the same object as
+ * <i>other_proc</i>, or if they are both procs with the same body.
+ */
+
+static VALUE
+proc_eq(self, other)
+ VALUE self, other;
+{
+ struct BLOCK *data, *data2;
+
+ if (self == other) return Qtrue;
+ if (TYPE(other) != T_DATA) return Qfalse;
+ if (RDATA(other)->dmark != (RUBY_DATA_FUNC)blk_mark) return Qfalse;
+ if (CLASS_OF(self) != CLASS_OF(other)) return Qfalse;
+ Data_Get_Struct(self, struct BLOCK, data);
+ Data_Get_Struct(other, struct BLOCK, data2);
+ if (data->body != data2->body) return Qfalse;
+ if (data->var != data2->var) return Qfalse;
+ if (data->scope != data2->scope) return Qfalse;
+ if (data->dyna_vars != data2->dyna_vars) return Qfalse;
+ if (data->flags != data2->flags) return Qfalse;
+
+ return Qtrue;
+}
+
+/*
+ * call-seq:
+ * prc.hash => integer
+ *
+ * Return hash value corresponding to proc body.
+ */
+
+static VALUE
+proc_hash(self)
+ VALUE self;
+{
+ struct BLOCK *data;
+ long hash;
+
+ Data_Get_Struct(self, struct BLOCK, data);
+ hash = (long)data->body;
+ hash ^= (long)data->var;
+ hash ^= data->frame.uniq << 16;
+ hash ^= data->flags;
+
+ return INT2FIX(hash);
+}
+
+/*
+ * call-seq:
+ * prc.to_s => string
+ *
+ * Shows the unique identifier for this proc, along with
+ * an indication of where the proc was defined.
+ */
+
+static VALUE
+proc_to_s(self)
+ VALUE self;
+{
+ struct BLOCK *data;
+ NODE *node;
+ char *cname = rb_obj_classname(self);
+ const int w = (SIZEOF_LONG * CHAR_BIT) / 4;
+ long len = strlen(cname)+6+w; /* 6:tags 16:addr */
+ VALUE str;
+
+ Data_Get_Struct(self, struct BLOCK, data);
+ if ((node = data->frame.node) || (node = data->body)) {
+ len += strlen(node->nd_file) + 2 + (SIZEOF_LONG*CHAR_BIT-NODE_LSHIFT)/3;
+ str = rb_str_new(0, len);
+ sprintf(RSTRING(str)->ptr, "#<%s:0x%.*lx@%s:%d>", cname, w, (VALUE)data->body,
+ node->nd_file, nd_line(node));
+ }
+ else {
+ str = rb_str_new(0, len);
+ sprintf(RSTRING(str)->ptr, "#<%s:0x%.*lx>", cname, w, (VALUE)data->body);
+ }
+ RSTRING(str)->len = strlen(RSTRING(str)->ptr);
+ if (OBJ_TAINTED(self)) OBJ_TAINT(str);
+
+ return str;
+}
+
+/*
+ * call-seq:
+ * prc.to_proc -> prc
+ *
+ * Part of the protocol for converting objects to <code>Proc</code>
+ * objects. Instances of class <code>Proc</code> simply return
+ * themselves.
+ */
+
+static VALUE
+proc_to_self(self)
+ VALUE self;
+{
+ return self;
+}
+
+/*
+ * call-seq:
+ * prc.binding => binding
+ *
+ * Returns the binding associated with <i>prc</i>. Note that
+ * <code>Kernel#eval</code> accepts either a <code>Proc</code> or a
+ * <code>Binding</code> object as its second parameter.
+ *
+ * def fred(param)
+ * proc {}
+ * end
+ *
+ * b = fred(99)
+ * eval("param", b.binding) #=> 99
+ * eval("param", b) #=> 99
+ */
+
+static VALUE
+proc_binding(proc)
+ VALUE proc;
+{
+ struct BLOCK *orig, *data;
+ VALUE bind;
+
+ Data_Get_Struct(proc, struct BLOCK, orig);
+ bind = Data_Make_Struct(rb_cBinding,struct BLOCK,blk_mark,blk_free,data);
+ MEMCPY(data, orig, struct BLOCK, 1);
+ frame_dup(&data->frame);
+
+ if (data->iter) {
+ blk_copy_prev(data);
+ }
+ else {
+ data->prev = 0;
+ }
+
+ return bind;
+}
+
+static VALUE
+rb_block_pass(func, arg, proc)
+ VALUE (*func) _((VALUE));
+ VALUE arg;
+ VALUE proc;
+{
+ VALUE b;
+ struct BLOCK * volatile old_block;
+ struct BLOCK _block;
+ struct BLOCK *data;
+ volatile VALUE result = Qnil;
+ int state;
+ volatile int orphan;
+ volatile int safe = ruby_safe_level;
+
+ if (NIL_P(proc)) {
+ PUSH_ITER(ITER_NOT);
+ result = (*func)(arg);
+ POP_ITER();
+ return result;
+ }
+ if (!rb_obj_is_proc(proc)) {
+ b = rb_check_convert_type(proc, T_DATA, "Proc", "to_proc");
+ if (!rb_obj_is_proc(b)) {
+ rb_raise(rb_eTypeError, "wrong argument type %s (expected Proc)",
+ rb_obj_classname(proc));
+ }
+ proc = b;
+ }
+
+ if (ruby_safe_level >= 1 && OBJ_TAINTED(proc)) {
+ if (ruby_safe_level > proc_get_safe_level(proc)) {
+ rb_raise(rb_eSecurityError, "Insecure: tainted block value");
+ }
+ }
+
+ if (ruby_block && ruby_block->block_obj == proc) {
+ PUSH_ITER(ITER_PRE);
+ result = (*func)(arg);
+ POP_ITER();
+ return result;
+ }
+
+ Data_Get_Struct(proc, struct BLOCK, data);
+ orphan = block_orphan(data);
+
+ /* PUSH BLOCK from data */
+ _block = *data;
+ _block.outer = ruby_block;
+ if (orphan) _block.uniq = block_unique++;
+ ruby_block = &_block;
+ PUSH_ITER(ITER_PRE);
+ if (ruby_frame->iter == ITER_NOT)
+ ruby_frame->iter = ITER_PRE;
+
+ PUSH_TAG(PROT_LOOP);
+ state = EXEC_TAG();
+ if (state == 0) {
+ retry:
+ proc_set_safe_level(proc);
+ if (safe > ruby_safe_level)
+ ruby_safe_level = safe;
+ result = (*func)(arg);
+ }
+ else if (state == TAG_BREAK && TAG_DST()) {
+ result = prot_tag->retval;
+ state = 0;
+ }
+ else if (state == TAG_RETRY) {
+ state = 0;
+ goto retry;
+ }
+ POP_TAG();
+ POP_ITER();
+ ruby_block = _block.outer;
+ if (proc_safe_level_p(proc)) ruby_safe_level = safe;
+
+ switch (state) {/* escape from orphan block */
+ case 0:
+ break;
+ case TAG_RETURN:
+ if (orphan) {
+ proc_jump_error(state, prot_tag->retval);
+ }
+ default:
+ JUMP_TAG(state);
+ }
+
+ return result;
+}
+
+struct block_arg {
+ VALUE self;
+ NODE *iter;
+};
+
+static VALUE
+call_block(arg)
+ struct block_arg *arg;
+{
+ return rb_eval(arg->self, arg->iter);
+}
+
+static VALUE
+block_pass(self, node)
+ VALUE self;
+ NODE *node;
+{
+ struct block_arg arg;
+ arg.self = self;
+ arg.iter = node->nd_iter;
+ return rb_block_pass((VALUE (*)_((VALUE)))call_block,
+ (VALUE)&arg, rb_eval(self, node->nd_body));
+}
+
+struct METHOD {
+ VALUE klass, rklass;
+ VALUE recv;
+ ID id, oid;
+ NODE *body;
+};
+
+static void
+bm_mark(data)
+ struct METHOD *data;
+{
+ rb_gc_mark(data->rklass);
+ rb_gc_mark(data->klass);
+ rb_gc_mark(data->recv);
+ rb_gc_mark((VALUE)data->body);
+}
+
+static VALUE
+mnew(klass, obj, id, mklass)
+ VALUE klass, obj, mklass;
+ ID id;
+{
+ VALUE method;
+ NODE *body;
+ int noex;
+ struct METHOD *data;
+ VALUE rklass = klass;
+ ID oid = id;
+
+ again:
+ if ((body = rb_get_method_body(&klass, &id, &noex)) == 0) {
+ print_undef(rklass, oid);
+ }
+
+ if (nd_type(body) == NODE_ZSUPER) {
+ klass = RCLASS(klass)->super;
+ goto again;
+ }
+
+ while (rklass != klass &&
+ (FL_TEST(rklass, FL_SINGLETON) || TYPE(rklass) == T_ICLASS)) {
+ rklass = RCLASS(rklass)->super;
+ }
+ if (TYPE(klass) == T_ICLASS) klass = RBASIC(klass)->klass;
+ method = Data_Make_Struct(mklass, struct METHOD, bm_mark, -1, data);
+ data->klass = klass;
+ data->recv = obj;
+ data->id = id;
+ data->body = body;
+ data->rklass = rklass;
+ data->oid = oid;
+ OBJ_INFECT(method, klass);
+
+ return method;
+}
+
+
+/**********************************************************************
+ *
+ * Document-class : Method
+ *
+ * Method objects are created by <code>Object#method</code>, and are
+ * associated with a particular object (not just with a class). They
+ * may be used to invoke the method within the object, and as a block
+ * associated with an iterator. They may also be unbound from one
+ * object (creating an <code>UnboundMethod</code>) and bound to
+ * another.
+ *
+ * class Thing
+ * def square(n)
+ * n*n
+ * end
+ * end
+ * thing = Thing.new
+ * meth = thing.method(:square)
+ *
+ * meth.call(9) #=> 81
+ * [ 1, 2, 3 ].collect(&meth) #=> [1, 4, 9]
+ *
+ */
+
+/*
+ * call-seq:
+ * meth == other_meth => true or false
+ *
+ * Two method objects are equal if that are bound to the same
+ * object and contain the same body.
+ */
+
+
+static VALUE
+method_eq(method, other)
+ VALUE method, other;
+{
+ struct METHOD *m1, *m2;
+
+ if (TYPE(other) != T_DATA || RDATA(other)->dmark != (RUBY_DATA_FUNC)bm_mark)
+ return Qfalse;
+ if (CLASS_OF(method) != CLASS_OF(other))
+ return Qfalse;
+
+ Data_Get_Struct(method, struct METHOD, m1);
+ Data_Get_Struct(other, struct METHOD, m2);
+
+ if (m1->klass != m2->klass || m1->rklass != m2->rklass ||
+ m1->recv != m2->recv || m1->body != m2->body)
+ return Qfalse;
+
+ return Qtrue;
+}
+
+/*
+ * call-seq:
+ * meth.hash => integer
+ *
+ * Return a hash value corresponding to the method object.
+ */
+
+static VALUE
+method_hash(method)
+ VALUE method;
+{
+ struct METHOD *m;
+ long hash;
+
+ Data_Get_Struct(method, struct METHOD, m);
+ hash = (long)m->klass;
+ hash ^= (long)m->rklass;
+ hash ^= (long)m->recv;
+ hash ^= (long)m->body;
+
+ return INT2FIX(hash);
+}
+
+/*
+ * call-seq:
+ * meth.unbind => unbound_method
+ *
+ * Dissociates <i>meth</i> from it's current receiver. The resulting
+ * <code>UnboundMethod</code> can subsequently be bound to a new object
+ * of the same class (see <code>UnboundMethod</code>).
+ */
+
+static VALUE
+method_unbind(obj)
+ VALUE obj;
+{
+ VALUE method;
+ struct METHOD *orig, *data;
+
+ Data_Get_Struct(obj, struct METHOD, orig);
+ method = Data_Make_Struct(rb_cUnboundMethod, struct METHOD, bm_mark, free, data);
+ data->klass = orig->klass;
+ data->recv = Qundef;
+ data->id = orig->id;
+ data->body = orig->body;
+ data->rklass = orig->rklass;
+ data->oid = orig->oid;
+ OBJ_INFECT(method, obj);
+
+ return method;
+}
+
+/*
+ * call-seq:
+ * obj.method(sym) => method
+ *
+ * Looks up the named method as a receiver in <i>obj</i>, returning a
+ * <code>Method</code> object (or raising <code>NameError</code>). The
+ * <code>Method</code> object acts as a closure in <i>obj</i>'s object
+ * instance, so instance variables and the value of <code>self</code>
+ * remain available.
+ *
+ * class Demo
+ * def initialize(n)
+ * @iv = n
+ * end
+ * def hello()
+ * "Hello, @iv = #{@iv}"
+ * end
+ * end
+ *
+ * k = Demo.new(99)
+ * m = k.method(:hello)
+ * m.call #=> "Hello, @iv = 99"
+ *
+ * l = Demo.new('Fred')
+ * m = l.method("hello")
+ * m.call #=> "Hello, @iv = Fred"
+ */
+
+static VALUE
+rb_obj_method(obj, vid)
+ VALUE obj;
+ VALUE vid;
+{
+ return mnew(CLASS_OF(obj), obj, rb_to_id(vid), rb_cMethod);
+}
+
+/*
+ * call-seq:
+ * mod.instance_method(symbol) => unbound_method
+ *
+ * Returns an +UnboundMethod+ representing the given
+ * instance method in _mod_.
+ *
+ * class Interpreter
+ * def do_a() print "there, "; end
+ * def do_d() print "Hello "; end
+ * def do_e() print "!\n"; end
+ * def do_v() print "Dave"; end
+ * Dispatcher = {
+ * ?a => instance_method(:do_a),
+ * ?d => instance_method(:do_d),
+ * ?e => instance_method(:do_e),
+ * ?v => instance_method(:do_v)
+ * }
+ * def interpret(string)
+ * string.each_byte {|b| Dispatcher[b].bind(self).call }
+ * end
+ * end
+ *
+ *
+ * interpreter = Interpreter.new
+ * interpreter.interpret('dave')
+ *
+ * <em>produces:</em>
+ *
+ * Hello there, Dave!
+ */
+
+static VALUE
+rb_mod_method(mod, vid)
+ VALUE mod;
+ VALUE vid;
+{
+ return mnew(mod, Qundef, rb_to_id(vid), rb_cUnboundMethod);
+}
+
+/*
+ * MISSING: documentation
+ */
+
+static VALUE
+method_clone(self)
+ VALUE self;
+{
+ VALUE clone;
+ struct METHOD *orig, *data;
+
+ Data_Get_Struct(self, struct METHOD, orig);
+ clone = Data_Make_Struct(CLASS_OF(self),struct METHOD, bm_mark, free, data);
+ CLONESETUP(clone, self);
+ *data = *orig;
+
+ return clone;
+}
+
+/*
+ * call-seq:
+ * meth.call(args, ...) => obj
+ * meth[args, ...] => obj
+ *
+ * Invokes the <i>meth</i> with the specified arguments, returning the
+ * method's return value.
+ *
+ * m = 12.method("+")
+ * m.call(3) #=> 15
+ * m.call(20) #=> 32
+ */
+
+static VALUE
+method_call(argc, argv, method)
+ int argc;
+ VALUE *argv;
+ VALUE method;
+{
+ VALUE result = Qnil; /* OK */
+ struct METHOD *data;
+ int state;
+ volatile int safe = -1;
+
+ Data_Get_Struct(method, struct METHOD, data);
+ if (data->recv == Qundef) {
+ rb_raise(rb_eTypeError, "can't call unbound method; bind first");
+ }
+ PUSH_ITER(rb_block_given_p()?ITER_PRE:ITER_NOT);
+ PUSH_TAG(PROT_NONE);
+ if (OBJ_TAINTED(method)) {
+ safe = ruby_safe_level;
+ if (ruby_safe_level < 4) ruby_safe_level = 4;
+ }
+ if ((state = EXEC_TAG()) == 0) {
+ result = rb_call0(data->klass,data->recv,data->id,data->oid,argc,argv,data->body,0);
+ }
+ POP_TAG();
+ POP_ITER();
+ if (safe >= 0) ruby_safe_level = safe;
+ if (state) JUMP_TAG(state);
+ return result;
+}
+
+/**********************************************************************
+ *
+ * Document-class: UnboundMethod
+ *
+ * Ruby supports two forms of objectified methods. Class
+ * <code>Method</code> is used to represent methods that are associated
+ * with a particular object: these method objects are bound to that
+ * object. Bound method objects for an object can be created using
+ * <code>Object#method</code>.
+ *
+ * Ruby also supports unbound methods; methods objects that are not
+ * associated with a particular object. These can be created either by
+ * calling <code>Module#instance_method</code> or by calling
+ * <code>unbind</code> on a bound method object. The result of both of
+ * these is an <code>UnboundMethod</code> object.
+ *
+ * Unbound methods can only be called after they are bound to an
+ * object. That object must be be a kind_of? the method's original
+ * class.
+ *
+ * class Square
+ * def area
+ * @side * @side
+ * end
+ * def initialize(side)
+ * @side = side
+ * end
+ * end
+ *
+ * area_un = Square.instance_method(:area)
+ *
+ * s = Square.new(12)
+ * area = area_un.bind(s)
+ * area.call #=> 144
+ *
+ * Unbound methods are a reference to the method at the time it was
+ * objectified: subsequent changes to the underlying class will not
+ * affect the unbound method.
+ *
+ * class Test
+ * def test
+ * :original
+ * end
+ * end
+ * um = Test.instance_method(:test)
+ * class Test
+ * def test
+ * :modified
+ * end
+ * end
+ * t = Test.new
+ * t.test #=> :modified
+ * um.bind(t).call #=> :original
+ *
+ */
+
+/*
+ * call-seq:
+ * umeth.bind(obj) -> method
+ *
+ * Bind <i>umeth</i> to <i>obj</i>. If <code>Klass</code> was the class
+ * from which <i>umeth</i> was obtained,
+ * <code>obj.kind_of?(Klass)</code> must be true.
+ *
+ * class A
+ * def test
+ * puts "In test, class = #{self.class}"
+ * end
+ * end
+ * class B < A
+ * end
+ * class C < B
+ * end
+ *
+ *
+ * um = B.instance_method(:test)
+ * bm = um.bind(C.new)
+ * bm.call
+ * bm = um.bind(B.new)
+ * bm.call
+ * bm = um.bind(A.new)
+ * bm.call
+ *
+ * <em>produces:</em>
+ *
+ * In test, class = C
+ * In test, class = B
+ * prog.rb:16:in `bind': bind argument must be an instance of B (TypeError)
+ * from prog.rb:16
+ */
+
+static VALUE
+umethod_bind(method, recv)
+ VALUE method, recv;
+{
+ struct METHOD *data, *bound;
+
+ Data_Get_Struct(method, struct METHOD, data);
+ if (data->rklass != CLASS_OF(recv)) {
+ if (FL_TEST(data->rklass, FL_SINGLETON)) {
+ rb_raise(rb_eTypeError, "singleton method called for a different object");
+ }
+ if(!rb_obj_is_kind_of(recv, data->rklass)) {
+ rb_raise(rb_eTypeError, "bind argument must be an instance of %s",
+ rb_class2name(data->rklass));
+ }
+ }
+
+ method = Data_Make_Struct(rb_cMethod,struct METHOD,bm_mark,free,bound);
+ *bound = *data;
+ bound->recv = recv;
+ bound->rklass = CLASS_OF(recv);
+
+ return method;
+}
+
+int
+rb_node_arity(body)
+ NODE *body;
+{
+ int n;
+
+ switch (nd_type(body)) {
+ case NODE_CFUNC:
+ if (body->nd_argc < 0) return -1;
+ return body->nd_argc;
+ case NODE_ZSUPER:
+ return -1;
+ case NODE_ATTRSET:
+ return 1;
+ case NODE_IVAR:
+ return 0;
+ case NODE_BMETHOD:
+ return rb_proc_arity(body->nd_cval);
+ case NODE_SCOPE:
+ body = body->nd_next; /* skip NODE_SCOPE */
+ if (nd_type(body) == NODE_BLOCK)
+ body = body->nd_head;
+ if (!body) return 0;
+ n = body->nd_cnt;
+ if (body->nd_opt || body->nd_rest != -1)
+ n = -n-1;
+ return n;
+ default:
+ rb_raise(rb_eArgError, "invalid node 0x%x", nd_type(body));
+ }
+}
+
+/*
+ * call-seq:
+ * meth.arity => fixnum
+ *
+ * Returns an indication of the number of arguments accepted by a
+ * method. Returns a nonnegative integer for methods that take a fixed
+ * number of arguments. For Ruby methods that take a variable number of
+ * arguments, returns -n-1, where n is the number of required
+ * arguments. For methods written in C, returns -1 if the call takes a
+ * variable number of arguments.
+ *
+ * class C
+ * def one; end
+ * def two(a); end
+ * def three(*a); end
+ * def four(a, b); end
+ * def five(a, b, *c); end
+ * def six(a, b, *c, &d); end
+ * end
+ * c = C.new
+ * c.method(:one).arity #=> 0
+ * c.method(:two).arity #=> 1
+ * c.method(:three).arity #=> -1
+ * c.method(:four).arity #=> 2
+ * c.method(:five).arity #=> -3
+ * c.method(:six).arity #=> -3
+ *
+ * "cat".method(:size).arity #=> 0
+ * "cat".method(:replace).arity #=> 1
+ * "cat".method(:squeeze).arity #=> -1
+ * "cat".method(:count).arity #=> -1
+ */
+
+static VALUE
+method_arity_m(method)
+ VALUE method;
+{
+ int n = method_arity(method);
+ return INT2FIX(n);
+}
+
+static int
+method_arity(method)
+ VALUE method;
+{
+ struct METHOD *data;
+
+ Data_Get_Struct(method, struct METHOD, data);
+ return rb_node_arity(data->body);
+}
+
+int
+rb_mod_method_arity(mod, id)
+ VALUE mod;
+ ID id;
+{
+ NODE *node = rb_method_node(mod, id);
+ return rb_node_arity(node);
+}
+
+int
+rb_obj_method_arity(obj, id)
+ VALUE obj;
+ ID id;
+{
+ return rb_mod_method_arity(CLASS_OF(obj), id);
+}
+
+/*
+ * call-seq:
+ * meth.to_s => string
+ * meth.inspect => string
+ *
+ * Show the name of the underlying method.
+ *
+ * "cat".method(:count).inspect #=> "#<Method: String#count>"
+ */
+
+static VALUE
+method_inspect(method)
+ VALUE method;
+{
+ struct METHOD *data;
+ VALUE str;
+ const char *s;
+ char *sharp = "#";
+
+ Data_Get_Struct(method, struct METHOD, data);
+ str = rb_str_buf_new2("#<");
+ s = rb_obj_classname(method);
+ rb_str_buf_cat2(str, s);
+ rb_str_buf_cat2(str, ": ");
+
+ if (FL_TEST(data->klass, FL_SINGLETON)) {
+ VALUE v = rb_iv_get(data->klass, "__attached__");
+
+ if (data->recv == Qundef) {
+ rb_str_buf_append(str, rb_inspect(data->klass));
+ }
+ else if (data->recv == v) {
+ rb_str_buf_append(str, rb_inspect(v));
+ sharp = ".";
+ }
+ else {
+ rb_str_buf_append(str, rb_inspect(data->recv));
+ rb_str_buf_cat2(str, "(");
+ rb_str_buf_append(str, rb_inspect(v));
+ rb_str_buf_cat2(str, ")");
+ sharp = ".";
+ }
+ }
+ else {
+ rb_str_buf_cat2(str, rb_class2name(data->rklass));
+ if (data->rklass != data->klass) {
+ rb_str_buf_cat2(str, "(");
+ rb_str_buf_cat2(str, rb_class2name(data->klass));
+ rb_str_buf_cat2(str, ")");
+ }
+ }
+ rb_str_buf_cat2(str, sharp);
+ rb_str_buf_cat2(str, rb_id2name(data->oid));
+ rb_str_buf_cat2(str, ">");
+
+ return str;
+}
+
+static VALUE
+mproc(method)
+ VALUE method;
+{
+ VALUE proc;
+
+ /* emulate ruby's method call */
+ PUSH_ITER(ITER_CUR);
+ PUSH_FRAME();
+ proc = rb_block_proc();
+ POP_FRAME();
+ POP_ITER();
+
+ return proc;
+}
+
+static VALUE
+bmcall(args, method)
+ VALUE args, method;
+{
+ volatile VALUE a;
+
+ a = svalue_to_avalue(args);
+ return method_call(RARRAY(a)->len, RARRAY(a)->ptr, method);
+}
+
+VALUE
+rb_proc_new(func, val)
+ VALUE (*func)(ANYARGS); /* VALUE yieldarg[, VALUE procarg] */
+ VALUE val;
+{
+ struct BLOCK *data;
+ VALUE proc = rb_iterate((VALUE(*)_((VALUE)))mproc, 0, func, val);
+
+ Data_Get_Struct(proc, struct BLOCK, data);
+ data->body->nd_state = YIELD_FUNC_AVALUE;
+ return proc;
+}
+
+/*
+ * call-seq:
+ * meth.to_proc => prc
+ *
+ * Returns a <code>Proc</code> object corresponding to this method.
+ */
+
+static VALUE
+method_proc(method)
+ VALUE method;
+{
+ VALUE proc;
+ struct METHOD *mdata;
+ struct BLOCK *bdata;
+
+ Data_Get_Struct(method, struct METHOD, mdata);
+ if (nd_type(mdata->body) == NODE_BMETHOD) {
+ return mdata->body->nd_cval;
+ }
+ proc = rb_iterate((VALUE(*)_((VALUE)))mproc, 0, bmcall, method);
+ Data_Get_Struct(proc, struct BLOCK, bdata);
+ bdata->body->nd_file = mdata->body->nd_file;
+ nd_set_line(bdata->body, nd_line(mdata->body));
+ bdata->body->nd_state = YIELD_FUNC_SVALUE;
+ bdata->flags |= BLOCK_FROM_METHOD;
+
+ return proc;
+}
+
+static VALUE
+rb_obj_is_method(m)
+ VALUE m;
+{
+ if (TYPE(m) == T_DATA && RDATA(m)->dmark == (RUBY_DATA_FUNC)bm_mark) {
+ return Qtrue;
+ }
+ return Qfalse;
+}
+
+/*
+ * call-seq:
+ * define_method(symbol, method) => new_method
+ * define_method(symbol) { block } => proc
+ *
+ * Defines an instance method in the receiver. The _method_
+ * parameter can be a +Proc+ or +Method+ object.
+ * If a block is specified, it is used as the method body. This block
+ * is evaluated using <code>instance_eval</code>, a point that is
+ * tricky to demonstrate because <code>define_method</code> is private.
+ * (This is why we resort to the +send+ hack in this example.)
+ *
+ * class A
+ * def fred
+ * puts "In Fred"
+ * end
+ * def create_method(name, &block)
+ * self.class.send(:define_method, name, &block)
+ * end
+ * define_method(:wilma) { puts "Charge it!" }
+ * end
+ * class B < A
+ * define_method(:barney, instance_method(:fred))
+ * end
+ * a = B.new
+ * a.barney
+ * a.wilma
+ * a.create_method(:betty) { p self }
+ * a.betty
+ *
+ * <em>produces:</em>
+ *
+ * In Fred
+ * Charge it!
+ * #<B:0x401b39e8>
+ */
+
+static VALUE
+rb_mod_define_method(argc, argv, mod)
+ int argc;
+ VALUE *argv;
+ VALUE mod;
+{
+ ID id;
+ VALUE body;
+ NODE *node;
+ int noex;
+
+ if (argc == 1) {
+ id = rb_to_id(argv[0]);
+ body = proc_lambda();
+ }
+ else if (argc == 2) {
+ id = rb_to_id(argv[0]);
+ body = argv[1];
+ if (!rb_obj_is_method(body) && !rb_obj_is_proc(body)) {
+ rb_raise(rb_eTypeError, "wrong argument type %s (expected Proc/Method)",
+ rb_obj_classname(body));
+ }
+ }
+ else {
+ rb_raise(rb_eArgError, "wrong number of arguments (%d for 1)", argc);
+ }
+ if (RDATA(body)->dmark == (RUBY_DATA_FUNC)bm_mark) {
+ struct METHOD *method = (struct METHOD *)DATA_PTR(body);
+ VALUE rklass = method->rklass;
+ if (rklass != mod) {
+ if (FL_TEST(rklass, FL_SINGLETON)) {
+ rb_raise(rb_eTypeError, "can't bind singleton method to a different class");
+ }
+ if (!RTEST(rb_class_inherited_p(mod, rklass))) {
+ rb_raise(rb_eTypeError, "bind argument must be a subclass of %s",
+ rb_class2name(rklass));
+ }
+ }
+ node = method->body;
+ }
+ else if (RDATA(body)->dmark == (RUBY_DATA_FUNC)blk_mark) {
+ struct BLOCK *block;
+
+ body = proc_clone(body);
+ RBASIC(body)->flags |= PROC_NOSAFE;
+ Data_Get_Struct(body, struct BLOCK, block);
+ block->frame.callee = id;
+ block->frame.this_func = id;
+ block->frame.this_class = mod;
+ node = NEW_BMETHOD(body);
+ }
+ else {
+ /* type error */
+ rb_raise(rb_eTypeError, "wrong argument type (expected Proc/Method)");
+ }
+
+ if (SCOPE_TEST(SCOPE_PRIVATE)) {
+ noex = NOEX_PRIVATE;
+ }
+ else if (SCOPE_TEST(SCOPE_PROTECTED)) {
+ noex = NOEX_PROTECTED;
+ }
+ else {
+ noex = NOEX_PUBLIC;
+ }
+ rb_add_method(mod, id, node, noex);
+ return body;
+}
+
+/*
+ * <code>Proc</code> objects are blocks of code that have been bound to
+ * a set of local variables. Once bound, the code may be called in
+ * different contexts and still access those variables.
+ *
+ * def gen_times(factor)
+ * return Proc.new {|n| n*factor }
+ * end
+ *
+ * times3 = gen_times(3)
+ * times5 = gen_times(5)
+ *
+ * times3.call(12) #=> 36
+ * times5.call(5) #=> 25
+ * times3.call(times5.call(4)) #=> 60
+ *
+ */
+
+void
+Init_Proc()
+{
+ rb_eLocalJumpError = rb_define_class("LocalJumpError", rb_eStandardError);
+ rb_define_method(rb_eLocalJumpError, "exit_value", localjump_xvalue, 0);
+ rb_define_method(rb_eLocalJumpError, "reason", localjump_reason, 0);
+
+ exception_error = rb_exc_new2(rb_eFatal, "exception reentered");
+ rb_global_variable(&exception_error);
+
+ rb_eSysStackError = rb_define_class("SystemStackError", rb_eException);
+ sysstack_error = rb_exc_new2(rb_eSysStackError, "stack level too deep");
+ OBJ_TAINT(sysstack_error);
+ rb_global_variable(&sysstack_error);
+
+ rb_cProc = rb_define_class("Proc", rb_cObject);
+ rb_undef_alloc_func(rb_cProc);
+ rb_define_singleton_method(rb_cProc, "new", proc_s_new, -1);
+
+ rb_define_method(rb_cProc, "clone", proc_clone, 0);
+ rb_define_method(rb_cProc, "dup", proc_dup, 0);
+ rb_define_method(rb_cProc, "call", proc_call, -2);
+ rb_define_method(rb_cProc, "arity", proc_arity, 0);
+ rb_define_method(rb_cProc, "[]", proc_call, -2);
+ rb_define_method(rb_cProc, "==", proc_eq, 1);
+ rb_define_method(rb_cProc, "eql?", proc_eq, 1);
+ rb_define_method(rb_cProc, "hash", proc_hash, 0);
+ rb_define_method(rb_cProc, "to_s", proc_to_s, 0);
+ rb_define_method(rb_cProc, "to_proc", proc_to_self, 0);
+ rb_define_method(rb_cProc, "binding", proc_binding, 0);
+
+ rb_define_global_function("proc", rb_block_proc, 0);
+ rb_define_global_function("lambda", proc_lambda, 0);
+
+ rb_cMethod = rb_define_class("Method", rb_cObject);
+ rb_undef_alloc_func(rb_cMethod);
+ rb_undef_method(CLASS_OF(rb_cMethod), "new");
+ rb_define_method(rb_cMethod, "==", method_eq, 1);
+ rb_define_method(rb_cMethod, "eql?", method_eq, 1);
+ rb_define_method(rb_cMethod, "hash", method_hash, 0);
+ rb_define_method(rb_cMethod, "clone", method_clone, 0);
+ rb_define_method(rb_cMethod, "call", method_call, -1);
+ rb_define_method(rb_cMethod, "[]", method_call, -1);
+ rb_define_method(rb_cMethod, "arity", method_arity_m, 0);
+ rb_define_method(rb_cMethod, "inspect", method_inspect, 0);
+ rb_define_method(rb_cMethod, "to_s", method_inspect, 0);
+ rb_define_method(rb_cMethod, "to_proc", method_proc, 0);
+ rb_define_method(rb_cMethod, "unbind", method_unbind, 0);
+ rb_define_method(rb_mKernel, "method", rb_obj_method, 1);
+
+ rb_cUnboundMethod = rb_define_class("UnboundMethod", rb_cObject);
+ rb_undef_alloc_func(rb_cUnboundMethod);
+ rb_undef_method(CLASS_OF(rb_cUnboundMethod), "new");
+ rb_define_method(rb_cUnboundMethod, "==", method_eq, 1);
+ rb_define_method(rb_cUnboundMethod, "eql?", method_eq, 1);
+ rb_define_method(rb_cUnboundMethod, "hash", method_hash, 0);
+ rb_define_method(rb_cUnboundMethod, "clone", method_clone, 0);
+ rb_define_method(rb_cUnboundMethod, "arity", method_arity_m, 0);
+ rb_define_method(rb_cUnboundMethod, "inspect", method_inspect, 0);
+ rb_define_method(rb_cUnboundMethod, "to_s", method_inspect, 0);
+ rb_define_method(rb_cUnboundMethod, "bind", umethod_bind, 1);
+ rb_define_method(rb_cModule, "instance_method", rb_mod_method, 1);
+}
+
+/*
+ * Objects of class <code>Binding</code> encapsulate the execution
+ * context at some particular place in the code and retain this context
+ * for future use. The variables, methods, value of <code>self</code>,
+ * and possibly an iterator block that can be accessed in this context
+ * are all retained. Binding objects can be created using
+ * <code>Kernel#binding</code>, and are made available to the callback
+ * of <code>Kernel#set_trace_func</code>.
+ *
+ * These binding objects can be passed as the second argument of the
+ * <code>Kernel#eval</code> method, establishing an environment for the
+ * evaluation.
+ *
+ * class Demo
+ * def initialize(n)
+ * @secret = n
+ * end
+ * def getBinding
+ * return binding()
+ * end
+ * end
+ *
+ * k1 = Demo.new(99)
+ * b1 = k1.getBinding
+ * k2 = Demo.new(-3)
+ * b2 = k2.getBinding
+ *
+ * eval("@secret", b1) #=> 99
+ * eval("@secret", b2) #=> -3
+ * eval("@secret") #=> nil
+ *
+ * Binding objects have no class-specific methods.
+ *
+ */
+
+void
+Init_Binding()
+{
+ rb_cBinding = rb_define_class("Binding", rb_cObject);
+ rb_undef_alloc_func(rb_cBinding);
+ rb_undef_method(CLASS_OF(rb_cBinding), "new");
+ rb_define_method(rb_cBinding, "clone", proc_clone, 0);
+ rb_define_method(rb_cBinding, "eval", bind_eval, -1);
+ rb_define_global_function("binding", rb_f_binding, 0);
+}
+
+#ifdef __ia64__
+#if defined(__FreeBSD__)
+/*
+ * FreeBSD/ia64 currently does not have a way for a process to get the
+ * base address for the RSE backing store, so hardcode it.
+ */
+#define __libc_ia64_register_backing_store_base (4ULL<<61)
+#else
+#ifdef HAVE_UNWIND_H
+#include <unwind.h>
+#else
+#pragma weak __libc_ia64_register_backing_store_base
+extern unsigned long __libc_ia64_register_backing_store_base;
+#endif
+#endif
+#endif
+
+/* Windows SEH refers data on the stack. */
+#undef SAVE_WIN32_EXCEPTION_LIST
+#if defined _WIN32 || defined __CYGWIN__
+#if defined __CYGWIN__
+typedef unsigned long DWORD;
+#endif
+
+static inline DWORD
+win32_get_exception_list()
+{
+ DWORD p;
+# if defined _MSC_VER
+# ifdef _M_IX86
+# define SAVE_WIN32_EXCEPTION_LIST
+# if _MSC_VER >= 1310
+ /* warning: unsafe assignment to fs:0 ... this is ok */
+# pragma warning(disable: 4733)
+# endif
+ __asm mov eax, fs:[0];
+ __asm mov p, eax;
+# endif
+# elif defined __GNUC__
+# ifdef __i386__
+# define SAVE_WIN32_EXCEPTION_LIST
+ __asm__("movl %%fs:0,%0" : "=r"(p));
+# endif
+# elif defined __BORLANDC__
+# define SAVE_WIN32_EXCEPTION_LIST
+ __emit__(0x64, 0xA1, 0, 0, 0, 0); /* mov eax, fs:[0] */
+ p = _EAX;
+# endif
+ return p;
+}
+
+static inline void
+win32_set_exception_list(p)
+ DWORD p;
+{
+# if defined _MSC_VER
+# ifdef _M_IX86
+ __asm mov eax, p;
+ __asm mov fs:[0], eax;
+# endif
+# elif defined __GNUC__
+# ifdef __i386__
+ __asm__("movl %0,%%fs:0" :: "r"(p));
+# endif
+# elif defined __BORLANDC__
+ _EAX = p;
+ __emit__(0x64, 0xA3, 0, 0, 0, 0); /* mov fs:[0], eax */
+# endif
+}
+
+#if !defined SAVE_WIN32_EXCEPTION_LIST && !defined _WIN32_WCE
+# error unsupported platform
+#endif
+#endif
+
+int rb_thread_pending = 0;
+
+VALUE rb_cThread;
+
+extern VALUE rb_last_status;
+
+enum thread_status {
+ THREAD_TO_KILL,
+ THREAD_RUNNABLE,
+ THREAD_STOPPED,
+ THREAD_KILLED,
+};
+
+#define WAIT_FD (1<<0)
+#define WAIT_SELECT (1<<1)
+#define WAIT_TIME (1<<2)
+#define WAIT_JOIN (1<<3)
+#define WAIT_PID (1<<4)
+
+/* +infty, for this purpose */
+#define DELAY_INFTY 1E30
+
+#if !defined HAVE_PAUSE
+# if defined _WIN32 && !defined __CYGWIN__
+# define pause() Sleep(INFINITE)
+# else
+# define pause() sleep(0x7fffffff)
+# endif
+#endif
+
+/* typedef struct thread * rb_thread_t; */
+
+struct thread {
+ struct thread *next, *prev;
+ rb_jmpbuf_t context;
+#ifdef SAVE_WIN32_EXCEPTION_LIST
+ DWORD win32_exception_list;
+#endif
+
+ VALUE result;
+
+ long stk_len;
+ long stk_max;
+ VALUE *stk_ptr;
+ VALUE *stk_pos;
+#ifdef __ia64__
+ VALUE *bstr_ptr;
+ long bstr_len;
+#endif
+
+ struct FRAME *frame;
+ struct SCOPE *scope;
+ struct RVarmap *dyna_vars;
+ struct BLOCK *block;
+ struct iter *iter;
+ struct tag *tag;
+ VALUE klass;
+ VALUE wrapper;
+ NODE *cref;
+ struct ruby_env *anchor;
+
+ int flags; /* misc. states (vmode/rb_trap_immediate/raised) */
+
+ NODE *node;
+
+ int tracing;
+ VALUE errinfo;
+ VALUE last_status;
+ VALUE last_line;
+ VALUE last_match;
+
+ int safe;
+
+ enum thread_status status;
+ int wait_for;
+ int fd;
+ fd_set readfds;
+ fd_set writefds;
+ fd_set exceptfds;
+ int select_value;
+ double delay;
+ rb_thread_t join;
+
+ int abort;
+ int priority;
+ VALUE thgroup;
+
+ st_table *locals;
+
+ VALUE thread;
+};
+
+#define THREAD_RAISED 0x200 /* temporary flag */
+#define THREAD_TERMINATING 0x400 /* persistent flag */
+#define THREAD_FLAGS_MASK 0x400 /* mask for persistent flags */
+
+#define FOREACH_THREAD_FROM(f,x) x = f; do { x = x->next;
+#define END_FOREACH_FROM(f,x) } while (x != f)
+
+#define FOREACH_THREAD(x) FOREACH_THREAD_FROM(curr_thread,x)
+#define END_FOREACH(x) END_FOREACH_FROM(curr_thread,x)
+
+struct thread_status_t {
+ NODE *node;
+
+ int tracing;
+ VALUE errinfo;
+ VALUE last_status;
+ VALUE last_line;
+ VALUE last_match;
+
+ int safe;
+
+ enum thread_status status;
+ int wait_for;
+ int fd;
+ fd_set readfds;
+ fd_set writefds;
+ fd_set exceptfds;
+ int select_value;
+ double delay;
+ rb_thread_t join;
+};
+
+#define THREAD_COPY_STATUS(src, dst) (void)( \
+ (dst)->node = (src)->node, \
+ \
+ (dst)->tracing = (src)->tracing, \
+ (dst)->errinfo = (src)->errinfo, \
+ (dst)->last_status = (src)->last_status, \
+ (dst)->last_line = (src)->last_line, \
+ (dst)->last_match = (src)->last_match, \
+ \
+ (dst)->safe = (src)->safe, \
+ \
+ (dst)->status = (src)->status, \
+ (dst)->wait_for = (src)->wait_for, \
+ (dst)->fd = (src)->fd, \
+ (dst)->readfds = (src)->readfds, \
+ (dst)->writefds = (src)->writefds, \
+ (dst)->exceptfds = (src)->exceptfds, \
+ (dst)->select_value = (src)->select_value, \
+ (dst)->delay = (src)->delay, \
+ (dst)->join = (src)->join, \
+ 0)
+
+static int
+thread_set_raised()
+{
+ if (curr_thread->flags & THREAD_RAISED) return 1;
+ curr_thread->flags |= THREAD_RAISED;
+ return 0;
+}
+
+static int
+thread_reset_raised()
+{
+ if (!(curr_thread->flags & THREAD_RAISED)) return 0;
+ curr_thread->flags &= ~THREAD_RAISED;
+ return 1;
+}
+
+static void rb_thread_ready _((rb_thread_t));
+
+static VALUE run_trap_eval _((VALUE));
+static VALUE
+run_trap_eval(arg)
+ VALUE arg;
+{
+ VALUE *p = (VALUE *)arg;
+ return rb_eval_cmd(p[0], p[1], (int)p[2]);
+}
+
+static VALUE
+rb_trap_eval(cmd, sig, safe)
+ VALUE cmd;
+ int sig, safe;
+{
+ int state;
+ VALUE val = Qnil; /* OK */
+ volatile struct thread_status_t save;
+ VALUE arg[3];
+
+ arg[0] = cmd;
+ arg[1] = rb_ary_new3(1, INT2FIX(sig));
+ arg[2] = (VALUE)safe;
+ THREAD_COPY_STATUS(curr_thread, &save);
+ rb_thread_ready(curr_thread);
+ PUSH_ITER(ITER_NOT);
+ val = rb_protect(run_trap_eval, (VALUE)&arg, &state);
+ POP_ITER();
+ THREAD_COPY_STATUS(&save, curr_thread);
+
+ if (state) {
+ rb_trap_immediate = 0;
+ JUMP_TAG(state);
+ }
+
+ if (curr_thread->status == THREAD_STOPPED) {
+ rb_thread_schedule();
+ }
+ errno = EINTR;
+
+ return val;
+}
+
+static const char *
+thread_status_name(status)
+ enum thread_status status;
+{
+ switch (status) {
+ case THREAD_RUNNABLE:
+ return "run";
+ case THREAD_STOPPED:
+ return "sleep";
+ case THREAD_TO_KILL:
+ return "aborting";
+ case THREAD_KILLED:
+ return "dead";
+ default:
+ return "unknown";
+ }
+}
+
+/* $SAFE accessor */
+void
+rb_set_safe_level(level)
+ int level;
+{
+ if (level > ruby_safe_level) {
+ if (level > SAFE_LEVEL_MAX) level = SAFE_LEVEL_MAX;
+ ruby_safe_level = level;
+ curr_thread->safe = level;
+ }
+}
+
+static VALUE
+safe_getter()
+{
+ return INT2NUM(ruby_safe_level);
+}
+
+static void
+safe_setter(val)
+ VALUE val;
+{
+ int level = NUM2INT(val);
+
+ if (level < ruby_safe_level) {
+ rb_raise(rb_eSecurityError, "tried to downgrade safe level from %d to %d",
+ ruby_safe_level, level);
+ }
+ if (level > SAFE_LEVEL_MAX) level = SAFE_LEVEL_MAX;
+ ruby_safe_level = level;
+ curr_thread->safe = level;
+}
+
+/* Return the current time as a floating-point number */
+static double
+timeofday()
+{
+ struct timeval tv;
+ gettimeofday(&tv, NULL);
+ return (double)tv.tv_sec + (double)tv.tv_usec * 1e-6;
+}
+
+#define STACK(addr) (th->stk_pos<(VALUE*)(addr) && (VALUE*)(addr)<th->stk_pos+th->stk_len)
+#define ADJ(addr) (void*)(STACK(addr)?(((VALUE*)(addr)-th->stk_pos)+th->stk_ptr):(VALUE*)(addr))
+
+static void
+thread_mark(th)
+ rb_thread_t th;
+{
+ struct FRAME *frame;
+ struct BLOCK *block;
+
+ rb_gc_mark(th->result);
+ rb_gc_mark(th->thread);
+ if (th->join) rb_gc_mark(th->join->thread);
+
+ rb_gc_mark(th->klass);
+ rb_gc_mark(th->wrapper);
+ rb_gc_mark((VALUE)th->cref);
+
+ rb_gc_mark((VALUE)th->scope);
+ rb_gc_mark((VALUE)th->dyna_vars);
+ rb_gc_mark(th->errinfo);
+ rb_gc_mark(th->last_line);
+ rb_gc_mark(th->last_match);
+ rb_mark_tbl(th->locals);
+ rb_gc_mark(th->thgroup);
+
+ /* mark data in copied stack */
+ if (th == curr_thread) return;
+ if (th->status == THREAD_KILLED) return;
+ if (th->stk_len == 0) return; /* stack not active, no need to mark. */
+ if (th->stk_ptr) {
+ rb_gc_mark_locations(th->stk_ptr, th->stk_ptr+th->stk_len);
+#if defined(THINK_C) || defined(__human68k__)
+ rb_gc_mark_locations(th->stk_ptr+2, th->stk_ptr+th->stk_len+2);
+#endif
+#ifdef __ia64__
+ if (th->bstr_ptr) {
+ rb_gc_mark_locations(th->bstr_ptr, th->bstr_ptr+th->bstr_len);
+ }
+#endif
+ }
+ frame = th->frame;
+ while (frame && frame != top_frame) {
+ frame = ADJ(frame);
+ rb_gc_mark_frame(frame);
+ if (frame->tmp) {
+ struct FRAME *tmp = frame->tmp;
+
+ while (tmp && tmp != top_frame) {
+ tmp = ADJ(tmp);
+ rb_gc_mark_frame(tmp);
+ tmp = tmp->prev;
+ }
+ }
+ frame = frame->prev;
+ }
+ block = th->block;
+ while (block) {
+ block = ADJ(block);
+ rb_gc_mark_frame(&block->frame);
+ block = block->prev;
+ }
+}
+
+static struct {
+ rb_thread_t thread;
+ VALUE proc, arg;
+} new_thread;
+
+void
+rb_gc_mark_threads()
+{
+ rb_thread_t th;
+
+ /* static global mark */
+ rb_gc_mark((VALUE)ruby_cref);
+
+ if (!curr_thread) return;
+ FOREACH_THREAD(th) {
+ rb_gc_mark(th->thread);
+ } END_FOREACH(th);
+ if (new_thread.thread) {
+ rb_gc_mark(new_thread.thread->thread);
+ rb_gc_mark(new_thread.proc);
+ rb_gc_mark(new_thread.arg);
+ }
+}
+
+static void
+thread_free(th)
+ rb_thread_t th;
+{
+ if (th->stk_ptr) free(th->stk_ptr);
+ th->stk_ptr = 0;
+#ifdef __ia64__
+ if (th->bstr_ptr) free(th->bstr_ptr);
+ th->bstr_ptr = 0;
+#endif
+ if (th->locals) st_free_table(th->locals);
+ if (th->status != THREAD_KILLED) {
+ if (th->prev) th->prev->next = th->next;
+ if (th->next) th->next->prev = th->prev;
+ }
+ if (th != main_thread) free(th);
+}
+
+static rb_thread_t
+rb_thread_check(data)
+ VALUE data;
+{
+ if (TYPE(data) != T_DATA || RDATA(data)->dmark != (RUBY_DATA_FUNC)thread_mark) {
+ rb_raise(rb_eTypeError, "wrong argument type %s (expected Thread)",
+ rb_obj_classname(data));
+ }
+ return (rb_thread_t)RDATA(data)->data;
+}
+
+static VALUE rb_thread_raise _((int, VALUE*, rb_thread_t));
+
+static VALUE th_raise_exception;
+static NODE *th_raise_node;
+static VALUE th_cmd;
+static int th_sig, th_safe;
+static char *th_signm;
+
+#define RESTORE_NORMAL 1
+#define RESTORE_FATAL 2
+#define RESTORE_INTERRUPT 3
+#define RESTORE_TRAP 4
+#define RESTORE_RAISE 5
+#define RESTORE_SIGNAL 6
+#define RESTORE_EXIT 7
+
+extern VALUE *rb_gc_stack_start;
+
+static void
+rb_thread_save_context(th)
+ rb_thread_t th;
+{
+ VALUE *pos;
+ int len;
+ static VALUE tval;
+
+ len = ruby_stack_length(&pos);
+ th->stk_len = 0;
+ th->stk_pos = pos;
+ if (len > th->stk_max) {
+ REALLOC_N(th->stk_ptr, VALUE, len);
+ th->stk_max = len;
+ }
+ th->stk_len = len;
+ FLUSH_REGISTER_WINDOWS;
+ MEMCPY(th->stk_ptr, th->stk_pos, VALUE, th->stk_len);
+#ifdef __ia64__
+ {
+ VALUE *top, *bot;
+#ifdef HAVE_UNWIND_H
+ _Unwind_Context *unwctx = _UNW_createContextForSelf();
+
+ _UNW_currentContext(unwctx);
+ bot = (VALUE*)(long)_UNW_getAR(unwctx, _UNW_AR_BSP);
+ top = (VALUE*)(long)_UNW_getAR(unwctx, _UNW_AR_BSPSTORE);
+ _UNW_destroyContext(unwctx);
+#else
+ ucontext_t ctx;
+
+ getcontext(&ctx);
+ bot = (VALUE*)__libc_ia64_register_backing_store_base;
+ top = (VALUE*)ctx.uc_mcontext.IA64_BSPSTORE;
+#endif
+ th->bstr_len = top - bot;
+ REALLOC_N(th->bstr_ptr, VALUE, th->bstr_len);
+ MEMCPY(th->bstr_ptr, bot, VALUE, th->bstr_len);
+ }
+#endif
+#ifdef SAVE_WIN32_EXCEPTION_LIST
+ th->win32_exception_list = win32_get_exception_list();
+#endif
+
+ th->frame = ruby_frame;
+ th->scope = ruby_scope;
+ th->klass = ruby_class;
+ th->wrapper = ruby_wrapper;
+ th->cref = ruby_cref;
+ th->dyna_vars = ruby_dyna_vars;
+ th->block = ruby_block;
+ th->flags &= THREAD_FLAGS_MASK;
+ th->flags |= (rb_trap_immediate<<8) | scope_vmode;
+ th->iter = ruby_iter;
+ th->tag = prot_tag;
+ th->tracing = tracing;
+ th->errinfo = ruby_errinfo;
+ th->last_status = rb_last_status;
+ tval = rb_lastline_get();
+ rb_lastline_set(th->last_line);
+ th->last_line = tval;
+ tval = rb_backref_get();
+ rb_backref_set(th->last_match);
+ th->last_match = tval;
+ th->safe = ruby_safe_level;
+
+ th->node = ruby_current_node;
+}
+
+static int
+rb_thread_switch(n)
+ int n;
+{
+ rb_trap_immediate = (curr_thread->flags&(1<<8))?1:0;
+ switch (n) {
+ case 0:
+ return 0;
+ case RESTORE_FATAL:
+ JUMP_TAG(TAG_FATAL);
+ break;
+ case RESTORE_INTERRUPT:
+ rb_interrupt();
+ break;
+ case RESTORE_TRAP:
+ rb_trap_eval(th_cmd, th_sig, th_safe);
+ break;
+ case RESTORE_RAISE:
+ ruby_frame->callee = 0;
+ ruby_frame->this_func = 0;
+ ruby_current_node = th_raise_node;
+ rb_raise_jump(th_raise_exception);
+ break;
+ case RESTORE_SIGNAL:
+ rb_raise(rb_eSignal, "SIG%s", th_signm);
+ break;
+ case RESTORE_EXIT:
+ ruby_errinfo = th_raise_exception;
+ ruby_current_node = th_raise_node;
+ error_print();
+ terminate_process(EXIT_FAILURE, 0, 0);
+ break;
+ case RESTORE_NORMAL:
+ default:
+ break;
+ }
+ return 1;
+}
+
+#define THREAD_SAVE_CONTEXT(th) \
+ (rb_thread_save_context(th),\
+ rb_thread_switch((FLUSH_REGISTER_WINDOWS, setjmp((th)->context))))
+
+NORETURN(static void rb_thread_restore_context _((rb_thread_t,int)));
+NOINLINE(static void stack_extend _((rb_thread_t, int)));
+
+static void
+stack_extend(th, exit)
+ rb_thread_t th;
+ int exit;
+{
+ VALUE space[1024];
+
+ memset(space, 0, 1); /* prevent array from optimization */
+ rb_thread_restore_context(th, exit);
+}
+
+static void
+rb_thread_restore_context(th, exit)
+ rb_thread_t th;
+ int exit;
+{
+ VALUE v;
+ static rb_thread_t tmp;
+ static int ex;
+ static VALUE tval;
+
+ if (!th->stk_ptr) rb_bug("unsaved context");
+
+#if STACK_GROW_DIRECTION < 0
+ if (&v > th->stk_pos) stack_extend(th, exit);
+#elif STACK_GROW_DIRECTION > 0
+ if (&v < th->stk_pos + th->stk_len) stack_extend(th, exit);
+#else
+ if (&v < rb_gc_stack_start) {
+ /* Stack grows downward */
+ if (&v > th->stk_pos) stack_extend(th, exit);
+ }
+ else {
+ /* Stack grows upward */
+ if (&v < th->stk_pos + th->stk_len) stack_extend(th, exit);
+ }
+#endif
+
+ rb_trap_immediate = 0; /* inhibit interrupts from here */
+ ruby_frame = th->frame;
+ ruby_scope = th->scope;
+ ruby_class = th->klass;
+ ruby_wrapper = th->wrapper;
+ ruby_cref = th->cref;
+ ruby_dyna_vars = th->dyna_vars;
+ ruby_block = th->block;
+ scope_vmode = th->flags&SCOPE_MASK;
+ ruby_iter = th->iter;
+ prot_tag = th->tag;
+ tracing = th->tracing;
+ ruby_errinfo = th->errinfo;
+ rb_last_status = th->last_status;
+ ruby_safe_level = th->safe;
+
+ ruby_current_node = th->node;
+
+#ifdef SAVE_WIN32_EXCEPTION_LIST
+ win32_set_exception_list(th->win32_exception_list);
+#endif
+ tmp = th;
+ ex = exit;
+ FLUSH_REGISTER_WINDOWS;
+ MEMCPY(tmp->stk_pos, tmp->stk_ptr, VALUE, tmp->stk_len);
+#ifdef __ia64__
+ {
+ VALUE *base;
+#ifdef HAVE_UNWIND_H
+ _Unwind_Context *unwctx = _UNW_createContextForSelf();
+
+ _UNW_currentContext(unwctx);
+ base = (VALUE*)(long)_UNW_getAR(unwctx, _UNW_AR_BSP);
+ _UNW_destroyContext(unwctx);
+#else
+ base = (VALUE*)__libc_ia64_register_backing_store_base;
+#endif
+ MEMCPY(base, tmp->bstr_ptr, VALUE, tmp->bstr_len);
+ }
+#endif
+
+ tval = rb_lastline_get();
+ rb_lastline_set(tmp->last_line);
+ tmp->last_line = tval;
+ tval = rb_backref_get();
+ rb_backref_set(tmp->last_match);
+ tmp->last_match = tval;
+
+ longjmp(tmp->context, ex);
+}
+
+static void
+rb_thread_ready(th)
+ rb_thread_t th;
+{
+ th->wait_for = 0;
+ if (th->status != THREAD_TO_KILL) {
+ th->status = THREAD_RUNNABLE;
+ }
+}
+
+static void
+rb_thread_die(th)
+ rb_thread_t th;
+{
+ th->thgroup = 0;
+ th->status = THREAD_KILLED;
+ if (th->stk_ptr) free(th->stk_ptr);
+ th->stk_ptr = 0;
+}
+
+static void
+rb_thread_remove(th)
+ rb_thread_t th;
+{
+ if (th->status == THREAD_KILLED) return;
+
+ rb_thread_ready(th);
+ rb_thread_die(th);
+ th->prev->next = th->next;
+ th->next->prev = th->prev;
+}
+
+static int
+rb_thread_dead(th)
+ rb_thread_t th;
+{
+ return th->status == THREAD_KILLED;
+}
+
+void
+rb_thread_fd_close(fd)
+ int fd;
+{
+ rb_thread_t th;
+
+ FOREACH_THREAD(th) {
+ if (((th->wait_for & WAIT_FD) && fd == th->fd) ||
+ ((th->wait_for & WAIT_SELECT) && (fd < th->fd) &&
+ (FD_ISSET(fd, &th->readfds) ||
+ FD_ISSET(fd, &th->writefds) ||
+ FD_ISSET(fd, &th->exceptfds)))) {
+ VALUE exc = rb_exc_new2(rb_eIOError, "stream closed");
+ rb_thread_raise(1, &exc, th);
+ }
+ }
+ END_FOREACH(th);
+}
+
+NORETURN(static void rb_thread_main_jump _((VALUE, int)));
+static void
+rb_thread_main_jump(err, tag)
+ VALUE err;
+ int tag;
+{
+ curr_thread = main_thread;
+ th_raise_exception = err;
+ th_raise_node = ruby_current_node;
+ rb_thread_restore_context(main_thread, tag);
+}
+
+NORETURN(static void rb_thread_deadlock _((void)));
+static void
+rb_thread_deadlock()
+{
+ char msg[21+SIZEOF_LONG*2];
+ VALUE e;
+
+ sprintf(msg, "Thread(0x%lx): deadlock", curr_thread->thread);
+ e = rb_exc_new2(rb_eFatal, msg);
+ if (curr_thread == main_thread) {
+ rb_exc_raise(e);
+ }
+ rb_thread_main_jump(e, RESTORE_RAISE);
+}
+
+static void
+copy_fds(dst, src, max)
+ fd_set *dst, *src;
+ int max;
+{
+ int n = 0;
+ int i;
+
+ for (i=0; i<=max; i++) {
+ if (FD_ISSET(i, src)) {
+ n = i;
+ FD_SET(i, dst);
+ }
+ }
+}
+
+static int
+match_fds(dst, src, max)
+ fd_set *dst, *src;
+ int max;
+{
+ int i;
+
+ for (i=0; i<=max; i++) {
+ if (FD_ISSET(i, src) && FD_ISSET(i, dst)) {
+ return Qtrue;
+ }
+ }
+ return Qfalse;
+}
+
+static int
+intersect_fds(src, dst, max)
+ fd_set *src, *dst;
+ int max;
+{
+ int i, n = 0;
+
+ for (i=0; i<=max; i++) {
+ if (FD_ISSET(i, dst)) {
+ if (FD_ISSET(i, src)) {
+ /* Wake up only one thread per fd. */
+ FD_CLR(i, src);
+ n++;
+ }
+ else {
+ FD_CLR(i, dst);
+ }
+ }
+ }
+ return n;
+}
+
+static int
+find_bad_fds(dst, src, max)
+ fd_set *dst, *src;
+ int max;
+{
+ int i, test = Qfalse;
+
+ for (i=0; i<=max; i++) {
+ if (FD_ISSET(i, src) && !FD_ISSET(i, dst)) {
+ FD_CLR(i, src);
+ test = Qtrue;
+ }
+ }
+ return test;
+}
+
+void
+rb_thread_schedule()
+{
+ rb_thread_t next; /* OK */
+ rb_thread_t th;
+ rb_thread_t curr;
+ int found = 0;
+
+ fd_set readfds;
+ fd_set writefds;
+ fd_set exceptfds;
+ struct timeval delay_tv, *delay_ptr;
+ double delay, now; /* OK */
+ int n, max;
+ int need_select = 0;
+ int select_timeout = 0;
+
+#ifdef HAVE_NATIVETHREAD
+ if (!is_ruby_native_thread()) {
+ rb_bug("cross-thread violation on rb_thread_schedule()");
+ }
+#endif
+ rb_thread_pending = 0;
+ if (curr_thread == curr_thread->next
+ && curr_thread->status == THREAD_RUNNABLE)
+ return;
+
+ next = 0;
+ curr = curr_thread; /* starting thread */
+
+ while (curr->status == THREAD_KILLED) {
+ curr = curr->prev;
+ }
+
+ again:
+ max = -1;
+ FD_ZERO(&readfds);
+ FD_ZERO(&writefds);
+ FD_ZERO(&exceptfds);
+ delay = DELAY_INFTY;
+ now = -1.0;
+
+ FOREACH_THREAD_FROM(curr, th) {
+ if (!found && th->status <= THREAD_RUNNABLE) {
+ found = 1;
+ }
+ if (th->status != THREAD_STOPPED) continue;
+ if (th->wait_for & WAIT_JOIN) {
+ if (rb_thread_dead(th->join)) {
+ th->status = THREAD_RUNNABLE;
+ found = 1;
+ }
+ }
+ if (th->wait_for & WAIT_FD) {
+ FD_SET(th->fd, &readfds);
+ if (max < th->fd) max = th->fd;
+ need_select = 1;
+ }
+ if (th->wait_for & WAIT_SELECT) {
+ copy_fds(&readfds, &th->readfds, th->fd);
+ copy_fds(&writefds, &th->writefds, th->fd);
+ copy_fds(&exceptfds, &th->exceptfds, th->fd);
+ if (max < th->fd) max = th->fd;
+ need_select = 1;
+ if (th->wait_for & WAIT_TIME) {
+ select_timeout = 1;
+ }
+ th->select_value = 0;
+ }
+ if (th->wait_for & WAIT_TIME) {
+ double th_delay;
+
+ if (now < 0.0) now = timeofday();
+ th_delay = th->delay - now;
+ if (th_delay <= 0.0) {
+ th->status = THREAD_RUNNABLE;
+ found = 1;
+ }
+ else if (th_delay < delay) {
+ delay = th_delay;
+ need_select = 1;
+ }
+ else if (th->delay == DELAY_INFTY) {
+ need_select = 1;
+ }
+ }
+ }
+ END_FOREACH_FROM(curr, th);
+
+ /* Do the select if needed */
+ if (need_select) {
+ /* Convert delay to a timeval */
+ /* If a thread is runnable, just poll */
+ if (found) {
+ delay_tv.tv_sec = 0;
+ delay_tv.tv_usec = 0;
+ delay_ptr = &delay_tv;
+ }
+ else if (delay == DELAY_INFTY) {
+ delay_ptr = 0;
+ }
+ else {
+ delay_tv.tv_sec = delay;
+ delay_tv.tv_usec = (delay - (double)delay_tv.tv_sec)*1e6;
+ delay_ptr = &delay_tv;
+ }
+
+ n = select(max+1, &readfds, &writefds, &exceptfds, delay_ptr);
+ if (n < 0) {
+ int e = errno;
+
+ if (rb_trap_pending) rb_trap_exec();
+ if (e == EINTR) goto again;
+#ifdef ERESTART
+ if (e == ERESTART) goto again;
+#endif
+ FOREACH_THREAD_FROM(curr, th) {
+ if (th->wait_for & WAIT_SELECT) {
+ int v = 0;
+
+ v |= find_bad_fds(&readfds, &th->readfds, th->fd);
+ v |= find_bad_fds(&writefds, &th->writefds, th->fd);
+ v |= find_bad_fds(&exceptfds, &th->exceptfds, th->fd);
+ if (v) {
+ th->select_value = n;
+ n = max;
+ }
+ }
+ }
+ END_FOREACH_FROM(curr, th);
+ }
+ if (select_timeout && n == 0) {
+ if (now < 0.0) now = timeofday();
+ FOREACH_THREAD_FROM(curr, th) {
+ if (((th->wait_for&(WAIT_SELECT|WAIT_TIME)) == (WAIT_SELECT|WAIT_TIME)) &&
+ th->delay <= now) {
+ th->status = THREAD_RUNNABLE;
+ th->wait_for = 0;
+ th->select_value = 0;
+ found = 1;
+ intersect_fds(&readfds, &th->readfds, max);
+ intersect_fds(&writefds, &th->writefds, max);
+ intersect_fds(&exceptfds, &th->exceptfds, max);
+ }
+ }
+ END_FOREACH_FROM(curr, th);
+ }
+ if (n > 0) {
+ now = -1.0;
+ /* Some descriptors are ready.
+ Make the corresponding threads runnable. */
+ FOREACH_THREAD_FROM(curr, th) {
+ if ((th->wait_for&WAIT_FD) && FD_ISSET(th->fd, &readfds)) {
+ /* Wake up only one thread per fd. */
+ FD_CLR(th->fd, &readfds);
+ th->status = THREAD_RUNNABLE;
+ th->fd = 0;
+ th->wait_for = 0;
+ found = 1;
+ }
+ if ((th->wait_for&WAIT_SELECT) &&
+ (match_fds(&readfds, &th->readfds, max) ||
+ match_fds(&writefds, &th->writefds, max) ||
+ match_fds(&exceptfds, &th->exceptfds, max))) {
+ /* Wake up only one thread per fd. */
+ th->status = THREAD_RUNNABLE;
+ th->wait_for = 0;
+ n = intersect_fds(&readfds, &th->readfds, max) +
+ intersect_fds(&writefds, &th->writefds, max) +
+ intersect_fds(&exceptfds, &th->exceptfds, max);
+ th->select_value = n;
+ found = 1;
+ }
+ }
+ END_FOREACH_FROM(curr, th);
+ }
+ /* The delays for some of the threads should have expired.
+ Go through the loop once more, to check the delays. */
+ if (!found && delay != DELAY_INFTY)
+ goto again;
+ }
+
+ FOREACH_THREAD_FROM(curr, th) {
+ if (th->status == THREAD_TO_KILL) {
+ next = th;
+ break;
+ }
+ if (th->status == THREAD_RUNNABLE && th->stk_ptr) {
+ if (!next || next->priority < th->priority)
+ next = th;
+ }
+ }
+ END_FOREACH_FROM(curr, th);
+
+ if (!next) {
+ /* raise fatal error to main thread */
+ curr_thread->node = ruby_current_node;
+ if (curr->next == curr) {
+ TRAP_BEG;
+ pause();
+ TRAP_END;
+ }
+ FOREACH_THREAD_FROM(curr, th) {
+ warn_printf("deadlock 0x%lx: %s:",
+ th->thread, thread_status_name(th->status));
+ if (th->wait_for & WAIT_FD) warn_printf("F(%d)", th->fd);
+ if (th->wait_for & WAIT_SELECT) warn_printf("S");
+ if (th->wait_for & WAIT_TIME) warn_printf("T(%f)", th->delay);
+ if (th->wait_for & WAIT_JOIN)
+ warn_printf("J(0x%lx)", th->join ? th->join->thread : 0);
+ if (th->wait_for & WAIT_PID) warn_printf("P");
+ if (!th->wait_for) warn_printf("-");
+ warn_printf(" %s - %s:%d\n",
+ th==main_thread ? "(main)" : "",
+ th->node->nd_file, nd_line(th->node));
+ }
+ END_FOREACH_FROM(curr, th);
+ next = main_thread;
+ rb_thread_ready(next);
+ next->status = THREAD_TO_KILL;
+ if (!rb_thread_dead(curr_thread)) {
+ rb_thread_save_context(curr_thread);
+ }
+ rb_thread_deadlock();
+ }
+ next->wait_for = 0;
+ if (next->status == THREAD_RUNNABLE && next == curr_thread) {
+ return;
+ }
+
+ /* context switch */
+ if (curr == curr_thread) {
+ if (THREAD_SAVE_CONTEXT(curr)) {
+ return;
+ }
+ }
+
+ curr_thread = next;
+ if (next->status == THREAD_TO_KILL) {
+ if (!(next->flags & THREAD_TERMINATING)) {
+ next->flags |= THREAD_TERMINATING;
+ /* terminate; execute ensure-clause if any */
+ rb_thread_restore_context(next, RESTORE_FATAL);
+ }
+ }
+ rb_thread_restore_context(next, RESTORE_NORMAL);
+}
+
+void
+rb_thread_wait_fd(fd)
+ int fd;
+{
+ if (rb_thread_critical) return;
+ if (curr_thread == curr_thread->next) return;
+ if (curr_thread->status == THREAD_TO_KILL) return;
+
+ curr_thread->status = THREAD_STOPPED;
+ curr_thread->fd = fd;
+ curr_thread->wait_for = WAIT_FD;
+ rb_thread_schedule();
+}
+
+int
+rb_thread_fd_writable(fd)
+ int fd;
+{
+ if (rb_thread_critical) return Qtrue;
+ if (curr_thread == curr_thread->next) return Qtrue;
+ if (curr_thread->status == THREAD_TO_KILL) return Qtrue;
+
+ curr_thread->status = THREAD_STOPPED;
+ FD_ZERO(&curr_thread->readfds);
+ FD_ZERO(&curr_thread->writefds);
+ FD_SET(fd, &curr_thread->writefds);
+ FD_ZERO(&curr_thread->exceptfds);
+ curr_thread->fd = fd+1;
+ curr_thread->wait_for = WAIT_SELECT;
+ rb_thread_schedule();
+ return Qfalse;
+}
+
+void
+rb_thread_wait_for(time)
+ struct timeval time;
+{
+ double date;
+
+ if (rb_thread_critical ||
+ curr_thread == curr_thread->next ||
+ curr_thread->status == THREAD_TO_KILL) {
+ int n;
+ int thr_critical = rb_thread_critical;
+#ifndef linux
+ double d, limit;
+ limit = timeofday()+(double)time.tv_sec+(double)time.tv_usec*1e-6;
+#endif
+ for (;;) {
+ rb_thread_critical = Qtrue;
+ TRAP_BEG;
+ n = select(0, 0, 0, 0, &time);
+ rb_thread_critical = thr_critical;
+ TRAP_END;
+ if (n == 0) return;
+ if (n < 0) {
+ switch (errno) {
+ case EINTR:
+#ifdef ERESTART
+ case ERESTART:
+#endif
+ return;
+ default:
+ rb_sys_fail("sleep");
+ }
+ }
+#ifndef linux
+ d = limit - timeofday();
+
+ time.tv_sec = (int)d;
+ time.tv_usec = (int)((d - (int)d)*1e6);
+ if (time.tv_usec < 0) {
+ time.tv_usec += (long)1e6;
+ time.tv_sec -= 1;
+ }
+ if (time.tv_sec < 0) return;
+#endif
+ }
+ }
+
+ date = timeofday() + (double)time.tv_sec + (double)time.tv_usec*1e-6;
+ curr_thread->status = THREAD_STOPPED;
+ curr_thread->delay = date;
+ curr_thread->wait_for = WAIT_TIME;
+ rb_thread_schedule();
+}
+
+void rb_thread_sleep_forever _((void));
+
+int
+rb_thread_alone()
+{
+ return curr_thread == curr_thread->next;
+}
+
+int
+rb_thread_select(max, read, write, except, timeout)
+ int max;
+ fd_set *read, *write, *except;
+ struct timeval *timeout;
+{
+ double limit;
+ int n;
+
+ if (!read && !write && !except) {
+ if (!timeout) {
+ rb_thread_sleep_forever();
+ return 0;
+ }
+ rb_thread_wait_for(*timeout);
+ return 0;
+ }
+
+ if (timeout) {
+ limit = timeofday()+
+ (double)timeout->tv_sec+(double)timeout->tv_usec*1e-6;
+ }
+
+ if (rb_thread_critical ||
+ curr_thread == curr_thread->next ||
+ curr_thread->status == THREAD_TO_KILL) {
+#ifndef linux
+ struct timeval tv, *tvp = timeout;
+
+ if (timeout) {
+ tv = *timeout;
+ tvp = &tv;
+ }
+#else
+ struct timeval *const tvp = timeout;
+#endif
+ for (;;) {
+ TRAP_BEG;
+ n = select(max, read, write, except, tvp);
+ TRAP_END;
+ if (n < 0) {
+ switch (errno) {
+ case EINTR:
+#ifdef ERESTART
+ case ERESTART:
+#endif
+#ifndef linux
+ if (timeout) {
+ double d = limit - timeofday();
+
+ tv.tv_sec = (unsigned int)d;
+ tv.tv_usec = (long)((d-(double)tv.tv_sec)*1e6);
+ if (tv.tv_sec < 0) tv.tv_sec = 0;
+ if (tv.tv_usec < 0) tv.tv_usec = 0;
+ }
+#endif
+ continue;
+ default:
+ break;
+ }
+ }
+ return n;
+ }
+ }
+
+ curr_thread->status = THREAD_STOPPED;
+ if (read) curr_thread->readfds = *read;
+ else FD_ZERO(&curr_thread->readfds);
+ if (write) curr_thread->writefds = *write;
+ else FD_ZERO(&curr_thread->writefds);
+ if (except) curr_thread->exceptfds = *except;
+ else FD_ZERO(&curr_thread->exceptfds);
+ curr_thread->fd = max;
+ curr_thread->wait_for = WAIT_SELECT;
+ if (timeout) {
+ curr_thread->delay = timeofday() +
+ (double)timeout->tv_sec + (double)timeout->tv_usec*1e-6;
+ curr_thread->wait_for |= WAIT_TIME;
+ }
+ rb_thread_schedule();
+ if (read) *read = curr_thread->readfds;
+ if (write) *write = curr_thread->writefds;
+ if (except) *except = curr_thread->exceptfds;
+ return curr_thread->select_value;
+}
+
+static int rb_thread_join _((rb_thread_t, double));
+
+static int
+rb_thread_join(th, limit)
+ rb_thread_t th;
+ double limit;
+{
+ enum thread_status last_status = THREAD_RUNNABLE;
+
+ if (rb_thread_critical) rb_thread_deadlock();
+ if (!rb_thread_dead(th)) {
+ if (th == curr_thread)
+ rb_raise(rb_eThreadError, "thread 0x%lx tried to join itself",
+ th->thread);
+ if ((th->wait_for & WAIT_JOIN) && th->join == curr_thread)
+ rb_raise(rb_eThreadError, "Thread#join: deadlock 0x%lx - mutual join(0x%lx)",
+ curr_thread->thread, th->thread);
+ if (curr_thread->status == THREAD_TO_KILL)
+ last_status = THREAD_TO_KILL;
+ if (limit == 0) return Qfalse;
+ curr_thread->status = THREAD_STOPPED;
+ curr_thread->join = th;
+ curr_thread->wait_for = WAIT_JOIN;
+ curr_thread->delay = timeofday() + limit;
+ if (limit < DELAY_INFTY) curr_thread->wait_for |= WAIT_TIME;
+ rb_thread_schedule();
+ curr_thread->status = last_status;
+ if (!rb_thread_dead(th)) return Qfalse;
+ }
+
+ if (!NIL_P(th->errinfo) && (th->flags & THREAD_RAISED)) {
+ VALUE oldbt = get_backtrace(th->errinfo);
+ VALUE errat = make_backtrace();
+ VALUE errinfo = rb_obj_dup(th->errinfo);
+
+ if (TYPE(oldbt) == T_ARRAY && RARRAY(oldbt)->len > 0) {
+ rb_ary_unshift(errat, rb_ary_entry(oldbt, 0));
+ }
+ set_backtrace(errinfo, errat);
+ rb_exc_raise(errinfo);
+ }
+
+ return Qtrue;
+}
+
+
+/*
+ * call-seq:
+ * thr.join => thr
+ * thr.join(limit) => thr
+ *
+ * The calling thread will suspend execution and run <i>thr</i>. Does not
+ * return until <i>thr</i> exits or until <i>limit</i> seconds have passed. If
+ * the time limit expires, <code>nil</code> will be returned, otherwise
+ * <i>thr</i> is returned.
+ *
+ * Any threads not joined will be killed when the main program exits. If
+ * <i>thr</i> had previously raised an exception and the
+ * <code>abort_on_exception</code> and <code>$DEBUG</code> flags are not set
+ * (so the exception has not yet been processed) it will be processed at this
+ * time.
+ *
+ * a = Thread.new { print "a"; sleep(10); print "b"; print "c" }
+ * x = Thread.new { print "x"; Thread.pass; print "y"; print "z" }
+ * x.join # Let x thread finish, a will be killed on exit.
+ *
+ * <em>produces:</em>
+ *
+ * axyz
+ *
+ * The following example illustrates the <i>limit</i> parameter.
+ *
+ * y = Thread.new { 4.times { sleep 0.1; puts 'tick... ' }}
+ * puts "Waiting" until y.join(0.15)
+ *
+ * <em>produces:</em>
+ *
+ * tick...
+ * Waiting
+ * tick...
+ * Waitingtick...
+ *
+ *
+ * tick...
+ */
+
+static VALUE
+rb_thread_join_m(argc, argv, thread)
+ int argc;
+ VALUE *argv;
+ VALUE thread;
+{
+ VALUE limit;
+ double delay = DELAY_INFTY;
+ rb_thread_t th = rb_thread_check(thread);
+
+ rb_scan_args(argc, argv, "01", &limit);
+ if (!NIL_P(limit)) delay = rb_num2dbl(limit);
+ if (!rb_thread_join(th, delay))
+ return Qnil;
+ return thread;
+}
+
+
+/*
+ * call-seq:
+ * Thread.current => thread
+ *
+ * Returns the currently executing thread.
+ *
+ * Thread.current #=> #<Thread:0x401bdf4c run>
+ */
+
+VALUE
+rb_thread_current()
+{
+ return curr_thread->thread;
+}
+
+
+/*
+ * call-seq:
+ * Thread.main => thread
+ *
+ * Returns the main thread for the process.
+ *
+ * Thread.main #=> #<Thread:0x401bdf4c run>
+ */
+
+VALUE
+rb_thread_main()
+{
+ return main_thread->thread;
+}
+
+
+/*
+ * call-seq:
+ * Thread.list => array
+ *
+ * Returns an array of <code>Thread</code> objects for all threads that are
+ * either runnable or stopped.
+ *
+ * Thread.new { sleep(200) }
+ * Thread.new { 1000000.times {|i| i*i } }
+ * Thread.new { Thread.stop }
+ * Thread.list.each {|t| p t}
+ *
+ * <em>produces:</em>
+ *
+ * #<Thread:0x401b3e84 sleep>
+ * #<Thread:0x401b3f38 run>
+ * #<Thread:0x401b3fb0 sleep>
+ * #<Thread:0x401bdf4c run>
+ */
+
+VALUE
+rb_thread_list()
+{
+ rb_thread_t th;
+ VALUE ary = rb_ary_new();
+
+ FOREACH_THREAD(th) {
+ switch (th->status) {
+ case THREAD_RUNNABLE:
+ case THREAD_STOPPED:
+ case THREAD_TO_KILL:
+ rb_ary_push(ary, th->thread);
+ default:
+ break;
+ }
+ }
+ END_FOREACH(th);
+
+ return ary;
+}
+
+
+/*
+ * call-seq:
+ * thr.wakeup => thr
+ *
+ * Marks <i>thr</i> as eligible for scheduling (it may still remain blocked on
+ * I/O, however). Does not invoke the scheduler (see <code>Thread#run</code>).
+ *
+ * c = Thread.new { Thread.stop; puts "hey!" }
+ * c.wakeup
+ *
+ * <em>produces:</em>
+ *
+ * hey!
+ */
+
+VALUE
+rb_thread_wakeup(thread)
+ VALUE thread;
+{
+ rb_thread_t th = rb_thread_check(thread);
+
+ if (th->status == THREAD_KILLED)
+ rb_raise(rb_eThreadError, "killed thread");
+ rb_thread_ready(th);
+
+ return thread;
+}
+
+
+/*
+ * call-seq:
+ * thr.run => thr
+ *
+ * Wakes up <i>thr</i>, making it eligible for scheduling. If not in a critical
+ * section, then invokes the scheduler.
+ *
+ * a = Thread.new { puts "a"; Thread.stop; puts "c" }
+ * Thread.pass
+ * puts "Got here"
+ * a.run
+ * a.join
+ *
+ * <em>produces:</em>
+ *
+ * a
+ * Got here
+ * c
+ */
+
+VALUE
+rb_thread_run(thread)
+ VALUE thread;
+{
+ rb_thread_wakeup(thread);
+ if (!rb_thread_critical) rb_thread_schedule();
+
+ return thread;
+}
+
+
+/*
+ * call-seq:
+ * thr.exit => thr or nil
+ * thr.kill => thr or nil
+ * thr.terminate => thr or nil
+ *
+ * Terminates <i>thr</i> and schedules another thread to be run. If this thread
+ * is already marked to be killed, <code>exit</code> returns the
+ * <code>Thread</code>. If this is the main thread, or the last thread, exits
+ * the process.
+ */
+
+VALUE
+rb_thread_kill(thread)
+ VALUE thread;
+{
+ rb_thread_t th = rb_thread_check(thread);
+
+ if (th != curr_thread && th->safe < 4) {
+ rb_secure(4);
+ }
+ if (th->status == THREAD_TO_KILL || th->status == THREAD_KILLED)
+ return thread;
+ if (th == th->next || th == main_thread) rb_exit(EXIT_SUCCESS);
+
+ rb_thread_ready(th);
+ th->status = THREAD_TO_KILL;
+ if (!rb_thread_critical) rb_thread_schedule();
+ return thread;
+}
+
+
+/*
+ * call-seq:
+ * Thread.kill(thread) => thread
+ *
+ * Causes the given <em>thread</em> to exit (see <code>Thread::exit</code>).
+ *
+ * count = 0
+ * a = Thread.new { loop { count += 1 } }
+ * sleep(0.1) #=> 0
+ * Thread.kill(a) #=> #<Thread:0x401b3d30 dead>
+ * count #=> 93947
+ * a.alive? #=> false
+ */
+
+static VALUE
+rb_thread_s_kill(obj, th)
+ VALUE obj, th;
+{
+ return rb_thread_kill(th);
+}
+
+
+/*
+ * call-seq:
+ * Thread.exit => thread
+ *
+ * Terminates the currently running thread and schedules another thread to be
+ * run. If this thread is already marked to be killed, <code>exit</code>
+ * returns the <code>Thread</code>. If this is the main thread, or the last
+ * thread, exit the process.
+ */
+
+static VALUE
+rb_thread_exit()
+{
+ return rb_thread_kill(curr_thread->thread);
+}
+
+
+/*
+ * call-seq:
+ * Thread.pass => nil
+ *
+ * Invokes the thread scheduler to pass execution to another thread.
+ *
+ * a = Thread.new { print "a"; Thread.pass;
+ * print "b"; Thread.pass;
+ * print "c" }
+ * b = Thread.new { print "x"; Thread.pass;
+ * print "y"; Thread.pass;
+ * print "z" }
+ * a.join
+ * b.join
+ *
+ * <em>produces:</em>
+ *
+ * axbycz
+ */
+
+static VALUE
+rb_thread_pass()
+{
+ rb_thread_schedule();
+ return Qnil;
+}
+
+
+/*
+ * call-seq:
+ * Thread.stop => nil
+ *
+ * Stops execution of the current thread, putting it into a ``sleep'' state,
+ * and schedules execution of another thread. Resets the ``critical'' condition
+ * to <code>false</code>.
+ *
+ * a = Thread.new { print "a"; Thread.stop; print "c" }
+ * Thread.pass
+ * print "b"
+ * a.run
+ * a.join
+ *
+ * <em>produces:</em>
+ *
+ * abc
+ */
+
+VALUE
+rb_thread_stop()
+{
+ enum thread_status last_status = THREAD_RUNNABLE;
+
+ rb_thread_critical = 0;
+ if (curr_thread == curr_thread->next) {
+ rb_raise(rb_eThreadError, "stopping only thread\n\tnote: use sleep to stop forever");
+ }
+ if (curr_thread->status == THREAD_TO_KILL)
+ last_status = THREAD_TO_KILL;
+ curr_thread->status = THREAD_STOPPED;
+ rb_thread_schedule();
+ curr_thread->status = last_status;
+
+ return Qnil;
+}
+
+struct timeval rb_time_timeval();
+
+void
+rb_thread_polling()
+{
+ if (curr_thread != curr_thread->next) {
+ curr_thread->status = THREAD_STOPPED;
+ curr_thread->delay = timeofday() + (double)0.06;
+ curr_thread->wait_for = WAIT_TIME;
+ rb_thread_schedule();
+ }
+}
+
+void
+rb_thread_sleep(sec)
+ int sec;
+{
+ if (curr_thread == curr_thread->next) {
+ TRAP_BEG;
+ sleep(sec);
+ TRAP_END;
+ return;
+ }
+ rb_thread_wait_for(rb_time_timeval(INT2FIX(sec)));
+}
+
+void
+rb_thread_sleep_forever()
+{
+ int thr_critical = rb_thread_critical;
+ if (curr_thread == curr_thread->next ||
+ curr_thread->status == THREAD_TO_KILL) {
+ rb_thread_critical = Qtrue;
+ TRAP_BEG;
+ pause();
+ rb_thread_critical = thr_critical;
+ TRAP_END;
+ return;
+ }
+
+ curr_thread->delay = DELAY_INFTY;
+ curr_thread->wait_for = WAIT_TIME;
+ curr_thread->status = THREAD_STOPPED;
+ rb_thread_schedule();
+}
+
+
+/*
+ * call-seq:
+ * thr.priority => integer
+ *
+ * Returns the priority of <i>thr</i>. Default is zero; higher-priority threads
+ * will run before lower-priority threads.
+ *
+ * Thread.current.priority #=> 0
+ */
+
+static VALUE
+rb_thread_priority(thread)
+ VALUE thread;
+{
+ return INT2NUM(rb_thread_check(thread)->priority);
+}
+
+
+/*
+ * call-seq:
+ * thr.priority= integer => thr
+ *
+ * Sets the priority of <i>thr</i> to <i>integer</i>. Higher-priority threads
+ * will run before lower-priority threads.
+ *
+ * count1 = count2 = 0
+ * a = Thread.new do
+ * loop { count1 += 1 }
+ * end
+ * a.priority = -1
+ *
+ * b = Thread.new do
+ * loop { count2 += 1 }
+ * end
+ * b.priority = -2
+ * sleep 1 #=> 1
+ * Thread.critical = 1
+ * count1 #=> 622504
+ * count2 #=> 5832
+ */
+
+static VALUE
+rb_thread_priority_set(thread, prio)
+ VALUE thread, prio;
+{
+ rb_thread_t th;
+
+ rb_secure(4);
+ th = rb_thread_check(thread);
+
+ th->priority = NUM2INT(prio);
+ rb_thread_schedule();
+ return prio;
+}
+
+
+/*
+ * call-seq:
+ * thr.safe_level => integer
+ *
+ * Returns the safe level in effect for <i>thr</i>. Setting thread-local safe
+ * levels can help when implementing sandboxes which run insecure code.
+ *
+ * thr = Thread.new { $SAFE = 3; sleep }
+ * Thread.current.safe_level #=> 0
+ * thr.safe_level #=> 3
+ */
+
+static VALUE
+rb_thread_safe_level(thread)
+ VALUE thread;
+{
+ rb_thread_t th;
+
+ th = rb_thread_check(thread);
+ if (th == curr_thread) {
+ return INT2NUM(ruby_safe_level);
+ }
+ return INT2NUM(th->safe);
+}
+
+static int ruby_thread_abort;
+static VALUE thgroup_default;
+
+
+/*
+ * call-seq:
+ * Thread.abort_on_exception => true or false
+ *
+ * Returns the status of the global ``abort on exception'' condition. The
+ * default is <code>false</code>. When set to <code>true</code>, or if the
+ * global <code>$DEBUG</code> flag is <code>true</code> (perhaps because the
+ * command line option <code>-d</code> was specified) all threads will abort
+ * (the process will <code>exit(0)</code>) if an exception is raised in any
+ * thread. See also <code>Thread::abort_on_exception=</code>.
+ */
+
+static VALUE
+rb_thread_s_abort_exc()
+{
+ return ruby_thread_abort?Qtrue:Qfalse;
+}
+
+
+/*
+ * call-seq:
+ * Thread.abort_on_exception= boolean => true or false
+ *
+ * When set to <code>true</code>, all threads will abort if an exception is
+ * raised. Returns the new state.
+ *
+ * Thread.abort_on_exception = true
+ * t1 = Thread.new do
+ * puts "In new thread"
+ * raise "Exception from thread"
+ * end
+ * sleep(1)
+ * puts "not reached"
+ *
+ * <em>produces:</em>
+ *
+ * In new thread
+ * prog.rb:4: Exception from thread (RuntimeError)
+ * from prog.rb:2:in `initialize'
+ * from prog.rb:2:in `new'
+ * from prog.rb:2
+ */
+
+static VALUE
+rb_thread_s_abort_exc_set(self, val)
+ VALUE self, val;
+{
+ rb_secure(4);
+ ruby_thread_abort = RTEST(val);
+ return val;
+}
+
+
+/*
+ * call-seq:
+ * thr.abort_on_exception => true or false
+ *
+ * Returns the status of the thread-local ``abort on exception'' condition for
+ * <i>thr</i>. The default is <code>false</code>. See also
+ * <code>Thread::abort_on_exception=</code>.
+ */
+
+static VALUE
+rb_thread_abort_exc(thread)
+ VALUE thread;
+{
+ return rb_thread_check(thread)->abort?Qtrue:Qfalse;
+}
+
+
+/*
+ * call-seq:
+ * thr.abort_on_exception= boolean => true or false
+ *
+ * When set to <code>true</code>, causes all threads (including the main
+ * program) to abort if an exception is raised in <i>thr</i>. The process will
+ * effectively <code>exit(0)</code>.
+ */
+
+static VALUE
+rb_thread_abort_exc_set(thread, val)
+ VALUE thread, val;
+{
+ rb_secure(4);
+ rb_thread_check(thread)->abort = RTEST(val);
+ return val;
+}
+
+
+/*
+ * call-seq:
+ * thr.group => thgrp or nil
+ *
+ * Returns the <code>ThreadGroup</code> which contains <i>thr</i>, or nil if
+ * the thread is not a member of any group.
+ *
+ * Thread.main.group #=> #<ThreadGroup:0x4029d914>
+ */
+
+VALUE
+rb_thread_group(thread)
+ VALUE thread;
+{
+ VALUE group = rb_thread_check(thread)->thgroup;
+ if (!group) {
+ group = Qnil;
+ }
+ return group;
+}
+
+#ifdef __ia64__
+# define IA64_INIT(x) x
+#else
+# define IA64_INIT(x)
+#endif
+
+#define THREAD_ALLOC(th) do {\
+ th = ALLOC(struct thread);\
+\
+ th->next = 0;\
+ th->prev = 0;\
+\
+ th->status = THREAD_RUNNABLE;\
+ th->result = 0;\
+ th->flags = 0;\
+\
+ th->stk_ptr = 0;\
+ th->stk_len = 0;\
+ th->stk_max = 0;\
+ th->wait_for = 0;\
+ IA64_INIT(th->bstr_ptr = 0);\
+ IA64_INIT(th->bstr_len = 0);\
+ FD_ZERO(&th->readfds);\
+ FD_ZERO(&th->writefds);\
+ FD_ZERO(&th->exceptfds);\
+ th->delay = 0.0;\
+ th->join = 0;\
+\
+ th->frame = 0;\
+ th->scope = 0;\
+ th->klass = 0;\
+ th->wrapper = 0;\
+ th->cref = ruby_cref;\
+ th->dyna_vars = ruby_dyna_vars;\
+ th->block = 0;\
+ th->iter = 0;\
+ th->tag = 0;\
+ th->tracing = 0;\
+ th->errinfo = Qnil;\
+ th->last_status = 0;\
+ th->last_line = 0;\
+ th->last_match = Qnil;\
+ th->abort = 0;\
+ th->priority = 0;\
+ th->thgroup = thgroup_default;\
+ th->locals = 0;\
+ th->thread = 0;\
+ th->anchor = 0;\
+} while (0)
+
+static rb_thread_t
+rb_thread_alloc(klass)
+ VALUE klass;
+{
+ rb_thread_t th;
+ struct RVarmap *vars;
+
+ THREAD_ALLOC(th);
+ th->thread = Data_Wrap_Struct(klass, thread_mark, thread_free, th);
+
+ for (vars = th->dyna_vars; vars; vars = vars->next) {
+ if (FL_TEST(vars, DVAR_DONT_RECYCLE)) break;
+ FL_SET(vars, DVAR_DONT_RECYCLE);
+ }
+ return th;
+}
+
+static int thread_init = 0;
+
+#if defined(_THREAD_SAFE)
+static void
+catch_timer(sig)
+ int sig;
+{
+#if !defined(POSIX_SIGNAL) && !defined(BSD_SIGNAL)
+ signal(sig, catch_timer);
+#endif
+ /* cause EINTR */
+}
+
+static pthread_t time_thread;
+
+static void*
+thread_timer(dummy)
+ void *dummy;
+{
+ for (;;) {
+#ifdef HAVE_NANOSLEEP
+ struct timespec req, rem;
+
+ req.tv_sec = 0;
+ req.tv_nsec = 10000000;
+ nanosleep(&req, &rem);
+#else
+ struct timeval tv;
+ tv.tv_sec = 0;
+ tv.tv_usec = 10000;
+ select(0, NULL, NULL, NULL, &tv);
+#endif
+ if (!rb_thread_critical) {
+ rb_thread_pending = 1;
+ if (rb_trap_immediate) {
+ pthread_kill(ruby_thid, SIGVTALRM);
+ }
+ }
+ }
+}
+
+void
+rb_thread_start_timer()
+{
+}
+
+void
+rb_thread_stop_timer()
+{
+}
+#elif defined(HAVE_SETITIMER)
+static void
+catch_timer(sig)
+ int sig;
+{
+#if !defined(POSIX_SIGNAL) && !defined(BSD_SIGNAL)
+ signal(sig, catch_timer);
+#endif
+ if (!rb_thread_critical) {
+ rb_thread_pending = 1;
+ }
+ /* cause EINTR */
+}
+
+void
+rb_thread_start_timer()
+{
+ struct itimerval tval;
+
+ if (!thread_init) return;
+ tval.it_interval.tv_sec = 0;
+ tval.it_interval.tv_usec = 10000;
+ tval.it_value = tval.it_interval;
+ setitimer(ITIMER_VIRTUAL, &tval, NULL);
+}
+
+void
+rb_thread_stop_timer()
+{
+ struct itimerval tval;
+
+ if (!thread_init) return;
+ tval.it_interval.tv_sec = 0;
+ tval.it_interval.tv_usec = 0;
+ tval.it_value = tval.it_interval;
+ setitimer(ITIMER_VIRTUAL, &tval, NULL);
+}
+#else /* !(_THREAD_SAFE || HAVE_SETITIMER) */
+int rb_thread_tick = THREAD_TICK;
+#endif
+
+NORETURN(static void rb_thread_terminated _((rb_thread_t, int, enum thread_status)));
+static VALUE rb_thread_yield _((VALUE, rb_thread_t));
+
+static void
+push_thread_anchor(ip)
+ struct ruby_env *ip;
+{
+ ip->tag = prot_tag;
+ ip->frame = ruby_frame;
+ ip->block = ruby_block;
+ ip->scope = ruby_scope;
+ ip->iter = ruby_iter;
+ ip->cref = ruby_cref;
+ ip->prev = curr_thread->anchor;
+ curr_thread->anchor = ip;
+}
+
+static void
+pop_thread_anchor(ip)
+ struct ruby_env *ip;
+{
+ curr_thread->anchor = ip->prev;
+}
+
+static void
+thread_insert(th)
+ rb_thread_t th;
+{
+ if (!th->next) {
+ /* merge in thread list */
+ th->prev = curr_thread;
+ curr_thread->next->prev = th;
+ th->next = curr_thread->next;
+ curr_thread->next = th;
+ th->priority = curr_thread->priority;
+ th->thgroup = curr_thread->thgroup;
+ }
+}
+
+static VALUE
+rb_thread_start_0(fn, arg, th)
+ VALUE (*fn)();
+ void *arg;
+ rb_thread_t th;
+{
+ volatile rb_thread_t th_save = th;
+ volatile VALUE thread = th->thread;
+ struct BLOCK *volatile saved_block = 0;
+ enum thread_status status;
+ int state;
+
+ if (OBJ_FROZEN(curr_thread->thgroup)) {
+ rb_raise(rb_eThreadError,
+ "can't start a new thread (frozen ThreadGroup)");
+ }
+
+ if (!thread_init) {
+ thread_init = 1;
+#if defined(HAVE_SETITIMER) || defined(_THREAD_SAFE)
+#if defined(POSIX_SIGNAL)
+ posix_signal(SIGVTALRM, catch_timer);
+#else
+ signal(SIGVTALRM, catch_timer);
+#endif
+
+#ifdef _THREAD_SAFE
+ pthread_create(&time_thread, 0, thread_timer, 0);
+#else
+ rb_thread_start_timer();
+#endif
+#endif
+ }
+
+ if (THREAD_SAVE_CONTEXT(curr_thread)) {
+ return thread;
+ }
+
+ if (fn == rb_thread_yield && curr_thread->anchor) {
+ struct ruby_env *ip = curr_thread->anchor;
+ new_thread.thread = th;
+ new_thread.proc = rb_block_proc();
+ new_thread.arg = (VALUE)arg;
+ th->anchor = ip;
+ thread_insert(th);
+ curr_thread = th;
+ longjmp((prot_tag = ip->tag)->buf, TAG_THREAD);
+ }
+
+ if (ruby_block) { /* should nail down higher blocks */
+ struct BLOCK dummy;
+
+ dummy.prev = ruby_block;
+ blk_copy_prev(&dummy);
+ saved_block = ruby_block = dummy.prev;
+ }
+ scope_dup(ruby_scope);
+
+ thread_insert(th);
+
+ PUSH_TAG(PROT_NONE);
+ if ((state = EXEC_TAG()) == 0) {
+ if (THREAD_SAVE_CONTEXT(th) == 0) {
+ curr_thread = th;
+ th->result = (*fn)(arg, th);
+ }
+ th = th_save;
+ }
+ else if (TAG_DST()) {
+ th = th_save;
+ th->result = prot_tag->retval;
+ }
+ POP_TAG();
+ status = th->status;
+
+ if (th == main_thread) ruby_stop(state);
+ rb_thread_remove(th);
+
+ if (saved_block) {
+ blk_free(saved_block);
+ }
+
+ rb_thread_terminated(th, state, status);
+ return 0; /* not reached */
+}
+
+static void
+rb_thread_terminated(th, state, status)
+ rb_thread_t th;
+ int state;
+ enum thread_status status;
+{
+ if (state && status != THREAD_TO_KILL && !NIL_P(ruby_errinfo)) {
+ th->flags |= THREAD_RAISED;
+ if (state == TAG_FATAL) {
+ /* fatal error within this thread, need to stop whole script */
+ main_thread->errinfo = ruby_errinfo;
+ rb_thread_cleanup();
+ }
+ else if (rb_obj_is_kind_of(ruby_errinfo, rb_eSystemExit)) {
+ if (th->safe >= 4) {
+ char buf[32];
+
+ sprintf(buf, "Insecure exit at level %d", th->safe);
+ th->errinfo = rb_exc_new2(rb_eSecurityError, buf);
+ }
+ else {
+ /* delegate exception to main_thread */
+ rb_thread_main_jump(ruby_errinfo, RESTORE_RAISE);
+ }
+ }
+ else if (th->safe < 4 && (ruby_thread_abort || th->abort || RTEST(ruby_debug))) {
+ /* exit on main_thread */
+ rb_thread_main_jump(ruby_errinfo, RESTORE_EXIT);
+ }
+ else {
+ th->errinfo = ruby_errinfo;
+ }
+ }
+ rb_thread_schedule();
+ ruby_stop(0); /* last thread termination */
+}
+
+static VALUE
+rb_thread_yield_0(arg)
+ VALUE arg;
+{
+ return rb_thread_yield(arg, curr_thread);
+}
+
+static void
+rb_thread_start_1()
+{
+ rb_thread_t th = new_thread.thread;
+ volatile rb_thread_t th_save = th;
+ VALUE proc = new_thread.proc;
+ VALUE arg = new_thread.arg;
+ struct ruby_env *ip = th->anchor;
+ enum thread_status status;
+ int state;
+
+ ruby_frame = ip->frame;
+ ruby_block = ip->block;
+ ruby_scope = ip->scope;
+ ruby_iter = ip->iter;
+ ruby_cref = ip->cref;
+ ruby_dyna_vars = ((struct BLOCK *)DATA_PTR(proc))->dyna_vars;
+ PUSH_FRAME();
+ *ruby_frame = *ip->frame;
+ ruby_frame->prev = ip->frame;
+ ruby_frame->iter = ITER_CUR;
+ PUSH_TAG(PROT_NONE);
+ if ((state = EXEC_TAG()) == 0) {
+ if (THREAD_SAVE_CONTEXT(th) == 0) {
+ new_thread.thread = 0;
+ th->result = rb_block_pass(rb_thread_yield_0, arg, proc);
+ }
+ th = th_save;
+ }
+ else if (TAG_DST()) {
+ th = th_save;
+ th->result = prot_tag->retval;
+ }
+ POP_TAG();
+ POP_FRAME();
+ status = th->status;
+
+ if (th == main_thread) ruby_stop(state);
+ rb_thread_remove(th);
+ rb_thread_terminated(th, state, status);
+}
+
+VALUE
+rb_thread_create(fn, arg)
+ VALUE (*fn)();
+ void *arg;
+{
+ Init_stack((VALUE*)&arg);
+ return rb_thread_start_0(fn, arg, rb_thread_alloc(rb_cThread));
+}
+
+static VALUE
+rb_thread_yield(arg, th)
+ VALUE arg;
+ rb_thread_t th;
+{
+ const ID *tbl;
+
+ scope_dup(ruby_block->scope);
+
+ tbl = ruby_scope->local_tbl;
+ if (tbl) {
+ int n = *tbl++;
+ for (tbl += 2, n -= 2; n > 0; --n) { /* skip first 2 ($_ and $~) */
+ ID id = *tbl++;
+ if (id != 0 && !rb_is_local_id(id)) /* push flip states */
+ rb_dvar_push(id, Qfalse);
+ }
+ }
+ rb_dvar_push('_', Qnil);
+ rb_dvar_push('~', Qnil);
+ ruby_block->dyna_vars = ruby_dyna_vars;
+
+ return rb_yield_0(arg, 0, 0, YIELD_LAMBDA_CALL, Qtrue);
+}
+
+/*
+ * call-seq:
+ * Thread.new([arg]*) {|args| block } => thread
+ *
+ * Creates and runs a new thread to execute the instructions given in
+ * <i>block</i>. Any arguments passed to <code>Thread::new</code> are passed
+ * into the block.
+ *
+ * x = Thread.new { sleep 0.1; print "x"; print "y"; print "z" }
+ * a = Thread.new { print "a"; print "b"; sleep 0.2; print "c" }
+ * x.join # Let the threads finish before
+ * a.join # main thread exits...
+ *
+ * <em>produces:</em>
+ *
+ * abxyzc
+ */
+
+static VALUE
+rb_thread_s_new(argc, argv, klass)
+ int argc;
+ VALUE *argv;
+ VALUE klass;
+{
+ rb_thread_t th = rb_thread_alloc(klass);
+ volatile VALUE *pos;
+
+ pos = th->stk_pos;
+ rb_obj_call_init(th->thread, argc, argv);
+ if (th->stk_pos == 0) {
+ rb_raise(rb_eThreadError, "uninitialized thread - check `%s#initialize'",
+ rb_class2name(klass));
+ }
+
+ return th->thread;
+}
+
+
+/*
+ * call-seq:
+ * Thread.new([arg]*) {|args| block } => thread
+ *
+ * Creates and runs a new thread to execute the instructions given in
+ * <i>block</i>. Any arguments passed to <code>Thread::new</code> are passed
+ * into the block.
+ *
+ * x = Thread.new { sleep 0.1; print "x"; print "y"; print "z" }
+ * a = Thread.new { print "a"; print "b"; sleep 0.2; print "c" }
+ * x.join # Let the threads finish before
+ * a.join # main thread exits...
+ *
+ * <em>produces:</em>
+ *
+ * abxyzc
+ */
+
+static VALUE
+rb_thread_initialize(thread, args)
+ VALUE thread, args;
+{
+ rb_thread_t th;
+
+ if (!rb_block_given_p()) {
+ rb_raise(rb_eThreadError, "must be called with a block");
+ }
+ th = rb_thread_check(thread);
+ if (th->stk_max) {
+ NODE *node = th->node;
+ if (!node) {
+ rb_raise(rb_eThreadError, "already initialized thread");
+ }
+ rb_raise(rb_eThreadError, "already initialized thread - %s:%d",
+ node->nd_file, nd_line(node));
+ }
+ return rb_thread_start_0(rb_thread_yield, args, th);
+}
+
+
+/*
+ * call-seq:
+ * Thread.start([args]*) {|args| block } => thread
+ * Thread.fork([args]*) {|args| block } => thread
+ *
+ * Basically the same as <code>Thread::new</code>. However, if class
+ * <code>Thread</code> is subclassed, then calling <code>start</code> in that
+ * subclass will not invoke the subclass's <code>initialize</code> method.
+ */
+
+static VALUE
+rb_thread_start(klass, args)
+ VALUE klass, args;
+{
+ if (!rb_block_given_p()) {
+ rb_raise(rb_eThreadError, "must be called with a block");
+ }
+ return rb_thread_start_0(rb_thread_yield, args, rb_thread_alloc(klass));
+}
+
+
+/*
+ * call-seq:
+ * thr.value => obj
+ *
+ * Waits for <i>thr</i> to complete (via <code>Thread#join</code>) and returns
+ * its value.
+ *
+ * a = Thread.new { 2 + 2 }
+ * a.value #=> 4
+ */
+
+static VALUE
+rb_thread_value(thread)
+ VALUE thread;
+{
+ rb_thread_t th = rb_thread_check(thread);
+
+ while (!rb_thread_join(th, DELAY_INFTY));
+
+ return th->result;
+}
+
+
+/*
+ * call-seq:
+ * thr.status => string, false or nil
+ *
+ * Returns the status of <i>thr</i>: ``<code>sleep</code>'' if <i>thr</i> is
+ * sleeping or waiting on I/O, ``<code>run</code>'' if <i>thr</i> is executing,
+ * ``<code>aborting</code>'' if <i>thr</i> is aborting, <code>false</code> if
+ * <i>thr</i> terminated normally, and <code>nil</code> if <i>thr</i>
+ * terminated with an exception.
+ *
+ * a = Thread.new { raise("die now") }
+ * b = Thread.new { Thread.stop }
+ * c = Thread.new { Thread.exit }
+ * d = Thread.new { sleep }
+ * Thread.critical = true
+ * d.kill #=> #<Thread:0x401b3678 aborting>
+ * a.status #=> nil
+ * b.status #=> "sleep"
+ * c.status #=> false
+ * d.status #=> "aborting"
+ * Thread.current.status #=> "run"
+ */
+
+static VALUE
+rb_thread_status(thread)
+ VALUE thread;
+{
+ rb_thread_t th = rb_thread_check(thread);
+
+ if (rb_thread_dead(th)) {
+ if (!NIL_P(th->errinfo) && (th->flags & THREAD_RAISED))
+ return Qnil;
+ return Qfalse;
+ }
+
+ return rb_str_new2(thread_status_name(th->status));
+}
+
+
+/*
+ * call-seq:
+ * thr.alive? => true or false
+ *
+ * Returns <code>true</code> if <i>thr</i> is running or sleeping.
+ *
+ * thr = Thread.new { }
+ * thr.join #=> #<Thread:0x401b3fb0 dead>
+ * Thread.current.alive? #=> true
+ * thr.alive? #=> false
+ */
+
+static VALUE
+rb_thread_alive_p(thread)
+ VALUE thread;
+{
+ rb_thread_t th = rb_thread_check(thread);
+
+ if (rb_thread_dead(th)) return Qfalse;
+ return Qtrue;
+}
+
+
+/*
+ * call-seq:
+ * thr.stop? => true or false
+ *
+ * Returns <code>true</code> if <i>thr</i> is dead or sleeping.
+ *
+ * a = Thread.new { Thread.stop }
+ * b = Thread.current
+ * a.stop? #=> true
+ * b.stop? #=> false
+ */
+
+static VALUE
+rb_thread_stop_p(thread)
+ VALUE thread;
+{
+ rb_thread_t th = rb_thread_check(thread);
+
+ if (rb_thread_dead(th)) return Qtrue;
+ if (th->status == THREAD_STOPPED) return Qtrue;
+ return Qfalse;
+}
+
+static void
+rb_thread_wait_other_threads()
+{
+ rb_thread_t th;
+ int found;
+
+ /* wait other threads to terminate */
+ while (curr_thread != curr_thread->next) {
+ found = 0;
+ FOREACH_THREAD(th) {
+ if (th != curr_thread && th->status != THREAD_STOPPED) {
+ found = 1;
+ break;
+ }
+ }
+ END_FOREACH(th);
+ if (!found) return;
+ rb_thread_schedule();
+ }
+}
+
+static void
+rb_thread_cleanup()
+{
+ rb_thread_t curr, th;
+
+ curr = curr_thread;
+ while (curr->status == THREAD_KILLED) {
+ curr = curr->prev;
+ }
+
+ FOREACH_THREAD_FROM(curr, th) {
+ if (th->status != THREAD_KILLED) {
+ rb_thread_ready(th);
+ if (th != main_thread) {
+ th->thgroup = 0;
+ th->priority = 0;
+ th->status = THREAD_TO_KILL;
+ RDATA(th->thread)->dfree = NULL;
+ }
+ }
+ }
+ END_FOREACH_FROM(curr, th);
+}
+
+int rb_thread_critical;
+
+
+/*
+ * call-seq:
+ * Thread.critical => true or false
+ *
+ * Returns the status of the global ``thread critical'' condition.
+ */
+
+static VALUE
+rb_thread_critical_get()
+{
+ return rb_thread_critical?Qtrue:Qfalse;
+}
+
+
+/*
+ * call-seq:
+ * Thread.critical= boolean => true or false
+ *
+ * Sets the status of the global ``thread critical'' condition and returns
+ * it. When set to <code>true</code>, prohibits scheduling of any existing
+ * thread. Does not block new threads from being created and run. Certain
+ * thread operations (such as stopping or killing a thread, sleeping in the
+ * current thread, and raising an exception) may cause a thread to be scheduled
+ * even when in a critical section. <code>Thread::critical</code> is not
+ * intended for daily use: it is primarily there to support folks writing
+ * threading libraries.
+ */
+
+static VALUE
+rb_thread_critical_set(obj, val)
+ VALUE obj, val;
+{
+ rb_thread_critical = RTEST(val);
+ return val;
+}
+
+void
+rb_thread_interrupt()
+{
+ rb_thread_critical = 0;
+ rb_thread_ready(main_thread);
+ if (curr_thread == main_thread) {
+ rb_interrupt();
+ }
+ if (!rb_thread_dead(curr_thread)) {
+ if (THREAD_SAVE_CONTEXT(curr_thread)) {
+ return;
+ }
+ }
+ curr_thread = main_thread;
+ rb_thread_restore_context(curr_thread, RESTORE_INTERRUPT);
+}
+
+void
+rb_thread_signal_raise(sig)
+ char *sig;
+{
+ if (sig == 0) return; /* should not happen */
+ rb_thread_critical = 0;
+ if (curr_thread == main_thread) {
+ rb_thread_ready(curr_thread);
+ rb_raise(rb_eSignal, "SIG%s", sig);
+ }
+ rb_thread_ready(main_thread);
+ if (!rb_thread_dead(curr_thread)) {
+ if (THREAD_SAVE_CONTEXT(curr_thread)) {
+ return;
+ }
+ }
+ th_signm = sig;
+ curr_thread = main_thread;
+ rb_thread_restore_context(curr_thread, RESTORE_SIGNAL);
+}
+
+void
+rb_thread_trap_eval(cmd, sig, safe)
+ VALUE cmd;
+ int sig, safe;
+{
+ rb_thread_critical = 0;
+ if (curr_thread == main_thread) {
+ rb_trap_eval(cmd, sig, safe);
+ return;
+ }
+ if (!rb_thread_dead(curr_thread)) {
+ if (THREAD_SAVE_CONTEXT(curr_thread)) {
+ return;
+ }
+ }
+ th_cmd = cmd;
+ th_sig = sig;
+ th_safe = safe;
+ curr_thread = main_thread;
+ rb_thread_restore_context(curr_thread, RESTORE_TRAP);
+}
+
+static VALUE
+rb_thread_raise(argc, argv, th)
+ int argc;
+ VALUE *argv;
+ rb_thread_t th;
+{
+ volatile rb_thread_t th_save = th;
+ VALUE exc;
+
+ if (!th->next) {
+ rb_raise(rb_eArgError, "unstarted thread");
+ }
+ if (rb_thread_dead(th)) return Qnil;
+ exc = rb_make_exception(argc, argv);
+ if (curr_thread == th) {
+ rb_raise_jump(exc);
+ }
+
+ if (!rb_thread_dead(curr_thread)) {
+ if (THREAD_SAVE_CONTEXT(curr_thread)) {
+ return th_save->thread;
+ }
+ }
+
+ rb_thread_ready(th);
+ curr_thread = th;
+
+ th_raise_exception = exc;
+ th_raise_node = ruby_current_node;
+ rb_thread_restore_context(curr_thread, RESTORE_RAISE);
+ return Qnil; /* not reached */
+}
+
+
+/*
+ * call-seq:
+ * thr.raise(exception)
+ *
+ * Raises an exception (see <code>Kernel::raise</code>) from <i>thr</i>. The
+ * caller does not have to be <i>thr</i>.
+ *
+ * Thread.abort_on_exception = true
+ * a = Thread.new { sleep(200) }
+ * a.raise("Gotcha")
+ *
+ * <em>produces:</em>
+ *
+ * prog.rb:3: Gotcha (RuntimeError)
+ * from prog.rb:2:in `initialize'
+ * from prog.rb:2:in `new'
+ * from prog.rb:2
+ */
+
+static VALUE
+rb_thread_raise_m(argc, argv, thread)
+ int argc;
+ VALUE *argv;
+ VALUE thread;
+{
+ rb_thread_t th = rb_thread_check(thread);
+
+ if (ruby_safe_level > th->safe) {
+ rb_secure(4);
+ }
+ rb_thread_raise(argc, argv, th);
+ return Qnil; /* not reached */
+}
+
+VALUE
+rb_thread_local_aref(thread, id)
+ VALUE thread;
+ ID id;
+{
+ rb_thread_t th;
+ VALUE val;
+
+ th = rb_thread_check(thread);
+ if (ruby_safe_level >= 4 && th != curr_thread) {
+ rb_raise(rb_eSecurityError, "Insecure: thread locals");
+ }
+ if (!th->locals) return Qnil;
+ if (st_lookup(th->locals, id, &val)) {
+ return val;
+ }
+ return Qnil;
+}
+
+
+/*
+ * call-seq:
+ * thr[sym] => obj or nil
+ *
+ * Attribute Reference---Returns the value of a thread-local variable, using
+ * either a symbol or a string name. If the specified variable does not exist,
+ * returns <code>nil</code>.
+ *
+ * a = Thread.new { Thread.current["name"] = "A"; Thread.stop }
+ * b = Thread.new { Thread.current[:name] = "B"; Thread.stop }
+ * c = Thread.new { Thread.current["name"] = "C"; Thread.stop }
+ * Thread.list.each {|x| puts "#{x.inspect}: #{x[:name]}" }
+ *
+ * <em>produces:</em>
+ *
+ * #<Thread:0x401b3b3c sleep>: C
+ * #<Thread:0x401b3bc8 sleep>: B
+ * #<Thread:0x401b3c68 sleep>: A
+ * #<Thread:0x401bdf4c run>:
+ */
+
+static VALUE
+rb_thread_aref(thread, id)
+ VALUE thread, id;
+{
+ return rb_thread_local_aref(thread, rb_to_id(id));
+}
+
+VALUE
+rb_thread_local_aset(thread, id, val)
+ VALUE thread;
+ ID id;
+ VALUE val;
+{
+ rb_thread_t th = rb_thread_check(thread);
+
+ if (ruby_safe_level >= 4 && th != curr_thread) {
+ rb_raise(rb_eSecurityError, "Insecure: can't modify thread locals");
+ }
+ if (OBJ_FROZEN(thread)) rb_error_frozen("thread locals");
+
+ if (!th->locals) {
+ th->locals = st_init_numtable();
+ }
+ if (NIL_P(val)) {
+ st_delete(th->locals, (st_data_t*)&id, 0);
+ return Qnil;
+ }
+ st_insert(th->locals, id, val);
+
+ return val;
+}
+
+
+/*
+ * call-seq:
+ * thr[sym] = obj => obj
+ *
+ * Attribute Assignment---Sets or creates the value of a thread-local variable,
+ * using either a symbol or a string. See also <code>Thread#[]</code>.
+ */
+
+static VALUE
+rb_thread_aset(thread, id, val)
+ VALUE thread, id, val;
+{
+ return rb_thread_local_aset(thread, rb_to_id(id), val);
+}
+
+
+/*
+ * call-seq:
+ * thr.key?(sym) => true or false
+ *
+ * Returns <code>true</code> if the given string (or symbol) exists as a
+ * thread-local variable.
+ *
+ * me = Thread.current
+ * me[:oliver] = "a"
+ * me.key?(:oliver) #=> true
+ * me.key?(:stanley) #=> false
+ */
+
+static VALUE
+rb_thread_key_p(thread, id)
+ VALUE thread, id;
+{
+ rb_thread_t th = rb_thread_check(thread);
+
+ if (!th->locals) return Qfalse;
+ if (st_lookup(th->locals, rb_to_id(id), 0))
+ return Qtrue;
+ return Qfalse;
+}
+
+static int
+thread_keys_i(key, value, ary)
+ ID key;
+ VALUE value, ary;
+{
+ rb_ary_push(ary, ID2SYM(key));
+ return ST_CONTINUE;
+}
+
+
+/*
+ * call-seq:
+ * thr.keys => array
+ *
+ * Returns an an array of the names of the thread-local variables (as Symbols).
+ *
+ * thr = Thread.new do
+ * Thread.current[:cat] = 'meow'
+ * Thread.current["dog"] = 'woof'
+ * end
+ * thr.join #=> #<Thread:0x401b3f10 dead>
+ * thr.keys #=> [:dog, :cat]
+ */
+
+static VALUE
+rb_thread_keys(thread)
+ VALUE thread;
+{
+ rb_thread_t th = rb_thread_check(thread);
+ VALUE ary = rb_ary_new();
+
+ if (th->locals) {
+ st_foreach(th->locals, thread_keys_i, ary);
+ }
+ return ary;
+}
+
+/*
+ * call-seq:
+ * thr.inspect => string
+ *
+ * Dump the name, id, and status of _thr_ to a string.
+ */
+
+static VALUE
+rb_thread_inspect(thread)
+ VALUE thread;
+{
+ char *cname = rb_obj_classname(thread);
+ rb_thread_t th = rb_thread_check(thread);
+ const char *status = thread_status_name(th->status);
+ VALUE str;
+
+ str = rb_str_new(0, strlen(cname)+7+16+9+1); /* 7:tags 16:addr 9:status 1:nul */
+ sprintf(RSTRING(str)->ptr, "#<%s:0x%lx %s>", cname, thread, status);
+ RSTRING(str)->len = strlen(RSTRING(str)->ptr);
+ OBJ_INFECT(str, thread);
+
+ return str;
+}
+
+void
+rb_thread_atfork()
+{
+ rb_thread_t th;
+
+ if (rb_thread_alone()) return;
+ FOREACH_THREAD(th) {
+ if (th != curr_thread) {
+ rb_thread_die(th);
+ }
+ }
+ END_FOREACH(th);
+ main_thread = curr_thread;
+ curr_thread->next = curr_thread;
+ curr_thread->prev = curr_thread;
+}
+
+
+/*
+ * Document-class: Continuation
+ *
+ * Continuation objects are generated by
+ * <code>Kernel#callcc</code>. They hold a return address and execution
+ * context, allowing a nonlocal return to the end of the
+ * <code>callcc</code> block from anywhere within a program.
+ * Continuations are somewhat analogous to a structured version of C's
+ * <code>setjmp/longjmp</code> (although they contain more state, so
+ * you might consider them closer to threads).
+ *
+ * For instance:
+ *
+ * arr = [ "Freddie", "Herbie", "Ron", "Max", "Ringo" ]
+ * callcc{|$cc|}
+ * puts(message = arr.shift)
+ * $cc.call unless message =~ /Max/
+ *
+ * <em>produces:</em>
+ *
+ * Freddie
+ * Herbie
+ * Ron
+ * Max
+ *
+ * This (somewhat contrived) example allows the inner loop to abandon
+ * processing early:
+ *
+ * callcc {|cont|
+ * for i in 0..4
+ * print "\n#{i}: "
+ * for j in i*5...(i+1)*5
+ * cont.call() if j == 17
+ * printf "%3d", j
+ * end
+ * end
+ * }
+ * print "\n"
+ *
+ * <em>produces:</em>
+ *
+ * 0: 0 1 2 3 4
+ * 1: 5 6 7 8 9
+ * 2: 10 11 12 13 14
+ * 3: 15 16
+ */
+
+static VALUE rb_cCont;
+
+/*
+ * call-seq:
+ * callcc {|cont| block } => obj
+ *
+ * Generates a <code>Continuation</code> object, which it passes to the
+ * associated block. Performing a <em>cont</em><code>.call</code> will
+ * cause the <code>callcc</code> to return (as will falling through the
+ * end of the block). The value returned by the <code>callcc</code> is
+ * the value of the block, or the value passed to
+ * <em>cont</em><code>.call</code>. See class <code>Continuation</code>
+ * for more details. Also see <code>Kernel::throw</code> for
+ * an alternative mechanism for unwinding a call stack.
+ */
+
+static VALUE
+rb_callcc(self)
+ VALUE self;
+{
+ volatile VALUE cont;
+ rb_thread_t th;
+ volatile rb_thread_t th_save;
+ struct tag *tag;
+ struct RVarmap *vars;
+
+ THREAD_ALLOC(th);
+ cont = Data_Wrap_Struct(rb_cCont, thread_mark, thread_free, th);
+
+ scope_dup(ruby_scope);
+ for (tag=prot_tag; tag; tag=tag->prev) {
+ scope_dup(tag->scope);
+ }
+ th->thread = curr_thread->thread;
+ th->thgroup = cont_protect;
+
+ for (vars = ruby_dyna_vars; vars; vars = vars->next) {
+ if (FL_TEST(vars, DVAR_DONT_RECYCLE)) break;
+ FL_SET(vars, DVAR_DONT_RECYCLE);
+ }
+ th_save = th;
+ if (THREAD_SAVE_CONTEXT(th)) {
+ return th_save->result;
+ }
+ else {
+ return rb_yield(cont);
+ }
+}
+
+/*
+ * call-seq:
+ * cont.call(args, ...)
+ * cont[args, ...]
+ *
+ * Invokes the continuation. The program continues from the end of the
+ * <code>callcc</code> block. If no arguments are given, the original
+ * <code>callcc</code> returns <code>nil</code>. If one argument is
+ * given, <code>callcc</code> returns it. Otherwise, an array
+ * containing <i>args</i> is returned.
+ *
+ * callcc {|cont| cont.call } #=> nil
+ * callcc {|cont| cont.call 1 } #=> 1
+ * callcc {|cont| cont.call 1, 2, 3 } #=> [1, 2, 3]
+ */
+
+static VALUE
+rb_cont_call(argc, argv, cont)
+ int argc;
+ VALUE *argv;
+ VALUE cont;
+{
+ rb_thread_t th = rb_thread_check(cont);
+
+ if (th->thread != curr_thread->thread) {
+ rb_raise(rb_eRuntimeError, "continuation called across threads");
+ }
+ if (th->thgroup != cont_protect) {
+ rb_raise(rb_eRuntimeError, "continuation called across trap");
+ }
+ switch (argc) {
+ case 0:
+ th->result = Qnil;
+ break;
+ case 1:
+ th->result = argv[0];
+ break;
+ default:
+ th->result = rb_ary_new4(argc, argv);
+ break;
+ }
+
+ rb_thread_restore_context(th, RESTORE_NORMAL);
+ return Qnil;
+}
+
+struct thgroup {
+ int enclosed;
+ VALUE group;
+};
+
+
+/*
+ * Document-class: ThreadGroup
+ *
+ * <code>ThreadGroup</code> provides a means of keeping track of a number of
+ * threads as a group. A <code>Thread</code> can belong to only one
+ * <code>ThreadGroup</code> at a time; adding a thread to a new group will
+ * remove it from any previous group.
+ *
+ * Newly created threads belong to the same group as the thread from which they
+ * were created.
+ */
+
+static VALUE thgroup_s_alloc _((VALUE));
+static VALUE
+thgroup_s_alloc(klass)
+ VALUE klass;
+{
+ VALUE group;
+ struct thgroup *data;
+
+ group = Data_Make_Struct(klass, struct thgroup, 0, free, data);
+ data->enclosed = 0;
+ data->group = group;
+
+ return group;
+}
+
+
+/*
+ * call-seq:
+ * thgrp.list => array
+ *
+ * Returns an array of all existing <code>Thread</code> objects that belong to
+ * this group.
+ *
+ * ThreadGroup::Default.list #=> [#<Thread:0x401bdf4c run>]
+ */
+
+static VALUE
+thgroup_list(group)
+ VALUE group;
+{
+ struct thgroup *data;
+ rb_thread_t th;
+ VALUE ary;
+
+ Data_Get_Struct(group, struct thgroup, data);
+ ary = rb_ary_new();
+
+ FOREACH_THREAD(th) {
+ if (th->thgroup == data->group) {
+ rb_ary_push(ary, th->thread);
+ }
+ }
+ END_FOREACH(th);
+
+ return ary;
+}
+
+
+/*
+ * call-seq:
+ * thgrp.enclose => thgrp
+ *
+ * Prevents threads from being added to or removed from the receiving
+ * <code>ThreadGroup</code>. New threads can still be started in an enclosed
+ * <code>ThreadGroup</code>.
+ *
+ * ThreadGroup::Default.enclose #=> #<ThreadGroup:0x4029d914>
+ * thr = Thread::new { Thread.stop } #=> #<Thread:0x402a7210 sleep>
+ * tg = ThreadGroup::new #=> #<ThreadGroup:0x402752d4>
+ * tg.add thr
+ *
+ * <em>produces:</em>
+ *
+ * ThreadError: can't move from the enclosed thread group
+ */
+
+VALUE
+thgroup_enclose(group)
+ VALUE group;
+{
+ struct thgroup *data;
+
+ Data_Get_Struct(group, struct thgroup, data);
+ data->enclosed = 1;
+
+ return group;
+}
+
+
+/*
+ * call-seq:
+ * thgrp.enclosed? => true or false
+ *
+ * Returns <code>true</code> if <em>thgrp</em> is enclosed. See also
+ * ThreadGroup#enclose.
+ */
+
+static VALUE
+thgroup_enclosed_p(group)
+ VALUE group;
+{
+ struct thgroup *data;
+
+ Data_Get_Struct(group, struct thgroup, data);
+ if (data->enclosed) return Qtrue;
+ return Qfalse;
+}
+
+
+/*
+ * call-seq:
+ * thgrp.add(thread) => thgrp
+ *
+ * Adds the given <em>thread</em> to this group, removing it from any other
+ * group to which it may have previously belonged.
+ *
+ * puts "Initial group is #{ThreadGroup::Default.list}"
+ * tg = ThreadGroup.new
+ * t1 = Thread.new { sleep }
+ * t2 = Thread.new { sleep }
+ * puts "t1 is #{t1}"
+ * puts "t2 is #{t2}"
+ * tg.add(t1)
+ * puts "Initial group now #{ThreadGroup::Default.list}"
+ * puts "tg group now #{tg.list}"
+ *
+ * <em>produces:</em>
+ *
+ * Initial group is #<Thread:0x401bdf4c>
+ * t1 is #<Thread:0x401b3c90>
+ * t2 is #<Thread:0x401b3c18>
+ * Initial group now #<Thread:0x401b3c18>#<Thread:0x401bdf4c>
+ * tg group now #<Thread:0x401b3c90>
+ */
+
+static VALUE
+thgroup_add(group, thread)
+ VALUE group, thread;
+{
+ rb_thread_t th;
+ struct thgroup *data;
+
+ rb_secure(4);
+ th = rb_thread_check(thread);
+ if (!th->next || !th->prev) {
+ rb_raise(rb_eTypeError, "wrong argument type %s (expected Thread)",
+ rb_obj_classname(thread));
+ }
+
+ if (OBJ_FROZEN(group)) {
+ rb_raise(rb_eThreadError, "can't move to the frozen thread group");
+ }
+ Data_Get_Struct(group, struct thgroup, data);
+ if (data->enclosed) {
+ rb_raise(rb_eThreadError, "can't move to the enclosed thread group");
+ }
+
+ if (!th->thgroup) {
+ return Qnil;
+ }
+ if (OBJ_FROZEN(th->thgroup)) {
+ rb_raise(rb_eThreadError, "can't move from the frozen thread group");
+ }
+ Data_Get_Struct(th->thgroup, struct thgroup, data);
+ if (data->enclosed) {
+ rb_raise(rb_eThreadError, "can't move from the enclosed thread group");
+ }
+
+ th->thgroup = group;
+ return group;
+}
+
+/* variables for recursive traversals */
+static ID recursive_key;
+static VALUE recursive_tbl;
+
+
+/*
+ * +Thread+ encapsulates the behavior of a thread of
+ * execution, including the main thread of the Ruby script.
+ *
+ * In the descriptions of the methods in this class, the parameter _sym_
+ * refers to a symbol, which is either a quoted string or a
+ * +Symbol+ (such as <code>:name</code>).
+ */
+
+void
+Init_Thread()
+{
+ VALUE cThGroup;
+
+ rb_eThreadError = rb_define_class("ThreadError", rb_eStandardError);
+ rb_cThread = rb_define_class("Thread", rb_cObject);
+ rb_undef_alloc_func(rb_cThread);
+
+ rb_define_singleton_method(rb_cThread, "new", rb_thread_s_new, -1);
+ rb_define_method(rb_cThread, "initialize", rb_thread_initialize, -2);
+ rb_define_singleton_method(rb_cThread, "start", rb_thread_start, -2);
+ rb_define_singleton_method(rb_cThread, "fork", rb_thread_start, -2);
+
+ rb_define_singleton_method(rb_cThread, "stop", rb_thread_stop, 0);
+ rb_define_singleton_method(rb_cThread, "kill", rb_thread_s_kill, 1);
+ rb_define_singleton_method(rb_cThread, "exit", rb_thread_exit, 0);
+ rb_define_singleton_method(rb_cThread, "pass", rb_thread_pass, 0);
+ rb_define_singleton_method(rb_cThread, "current", rb_thread_current, 0);
+ rb_define_singleton_method(rb_cThread, "main", rb_thread_main, 0);
+ rb_define_singleton_method(rb_cThread, "list", rb_thread_list, 0);
+
+ rb_define_singleton_method(rb_cThread, "critical", rb_thread_critical_get, 0);
+ rb_define_singleton_method(rb_cThread, "critical=", rb_thread_critical_set, 1);
+
+ rb_define_singleton_method(rb_cThread, "abort_on_exception", rb_thread_s_abort_exc, 0);
+ rb_define_singleton_method(rb_cThread, "abort_on_exception=", rb_thread_s_abort_exc_set, 1);
+
+ rb_define_method(rb_cThread, "run", rb_thread_run, 0);
+ rb_define_method(rb_cThread, "wakeup", rb_thread_wakeup, 0);
+ rb_define_method(rb_cThread, "kill", rb_thread_kill, 0);
+ rb_define_method(rb_cThread, "terminate", rb_thread_kill, 0);
+ rb_define_method(rb_cThread, "exit", rb_thread_kill, 0);
+ rb_define_method(rb_cThread, "value", rb_thread_value, 0);
+ rb_define_method(rb_cThread, "status", rb_thread_status, 0);
+ rb_define_method(rb_cThread, "join", rb_thread_join_m, -1);
+ rb_define_method(rb_cThread, "alive?", rb_thread_alive_p, 0);
+ rb_define_method(rb_cThread, "stop?", rb_thread_stop_p, 0);
+ rb_define_method(rb_cThread, "raise", rb_thread_raise_m, -1);
+
+ rb_define_method(rb_cThread, "abort_on_exception", rb_thread_abort_exc, 0);
+ rb_define_method(rb_cThread, "abort_on_exception=", rb_thread_abort_exc_set, 1);
+
+ rb_define_method(rb_cThread, "priority", rb_thread_priority, 0);
+ rb_define_method(rb_cThread, "priority=", rb_thread_priority_set, 1);
+ rb_define_method(rb_cThread, "safe_level", rb_thread_safe_level, 0);
+ rb_define_method(rb_cThread, "group", rb_thread_group, 0);
+
+ rb_define_method(rb_cThread, "[]", rb_thread_aref, 1);
+ rb_define_method(rb_cThread, "[]=", rb_thread_aset, 2);
+ rb_define_method(rb_cThread, "key?", rb_thread_key_p, 1);
+ rb_define_method(rb_cThread, "keys", rb_thread_keys, 0);
+
+ rb_define_method(rb_cThread, "inspect", rb_thread_inspect, 0);
+
+ rb_cCont = rb_define_class("Continuation", rb_cObject);
+ rb_undef_alloc_func(rb_cCont);
+ rb_undef_method(CLASS_OF(rb_cCont), "new");
+ rb_define_method(rb_cCont, "call", rb_cont_call, -1);
+ rb_define_method(rb_cCont, "[]", rb_cont_call, -1);
+ rb_define_global_function("callcc", rb_callcc, 0);
+ rb_global_variable(&cont_protect);
+
+ cThGroup = rb_define_class("ThreadGroup", rb_cObject);
+ rb_define_alloc_func(cThGroup, thgroup_s_alloc);
+ rb_define_method(cThGroup, "list", thgroup_list, 0);
+ rb_define_method(cThGroup, "enclose", thgroup_enclose, 0);
+ rb_define_method(cThGroup, "enclosed?", thgroup_enclosed_p, 0);
+ rb_define_method(cThGroup, "add", thgroup_add, 1);
+ thgroup_default = rb_obj_alloc(cThGroup);
+ rb_define_const(cThGroup, "Default", thgroup_default);
+ rb_global_variable(&thgroup_default);
+
+ /* allocate main thread */
+ main_thread = rb_thread_alloc(rb_cThread);
+ curr_thread = main_thread->prev = main_thread->next = main_thread;
+ recursive_key = rb_intern("__recursive_key__");
+}
+
+/*
+ * call-seq:
+ * catch(symbol) {| | block } > obj
+ *
+ * +catch+ executes its block. If a +throw+ is
+ * executed, Ruby searches up its stack for a +catch+ block
+ * with a tag corresponding to the +throw+'s
+ * _symbol_. If found, that block is terminated, and
+ * +catch+ returns the value given to +throw+. If
+ * +throw+ is not called, the block terminates normally, and
+ * the value of +catch+ is the value of the last expression
+ * evaluated. +catch+ expressions may be nested, and the
+ * +throw+ call need not be in lexical scope.
+ *
+ * def routine(n)
+ * puts n
+ * throw :done if n <= 0
+ * routine(n-1)
+ * end
+ *
+ *
+ * catch(:done) { routine(3) }
+ *
+ * <em>produces:</em>
+ *
+ * 3
+ * 2
+ * 1
+ * 0
+ */
+
+static VALUE
+rb_f_catch(dmy, tag)
+ VALUE dmy, tag;
+{
+ int state;
+ VALUE val = Qnil; /* OK */
+
+ tag = ID2SYM(rb_to_id(tag));
+ PUSH_TAG(tag);
+ if ((state = EXEC_TAG()) == 0) {
+ val = rb_yield_0(tag, 0, 0, 0, Qfalse);
+ }
+ else if (state == TAG_THROW && tag == prot_tag->dst) {
+ val = prot_tag->retval;
+ state = 0;
+ }
+ POP_TAG();
+ if (state) JUMP_TAG(state);
+
+ return val;
+}
+
+static VALUE
+catch_i(tag)
+ VALUE tag;
+{
+ return rb_funcall(Qnil, rb_intern("catch"), 1, tag);
+}
+
+VALUE
+rb_catch(tag, func, data)
+ const char *tag;
+ VALUE (*func)();
+ VALUE data;
+{
+ return rb_iterate((VALUE(*)_((VALUE)))catch_i, ID2SYM(rb_intern(tag)), func, data);
+}
+
+/*
+ * call-seq:
+ * throw(symbol [, obj])
+ *
+ * Transfers control to the end of the active +catch+ block
+ * waiting for _symbol_. Raises +NameError+ if there
+ * is no +catch+ block for the symbol. The optional second
+ * parameter supplies a return value for the +catch+ block,
+ * which otherwise defaults to +nil+. For examples, see
+ * <code>Kernel::catch</code>.
+ */
+
+static VALUE
+rb_f_throw(argc, argv)
+ int argc;
+ VALUE *argv;
+{
+ VALUE tag, value;
+ struct tag *tt = prot_tag;
+
+ rb_scan_args(argc, argv, "11", &tag, &value);
+ tag = ID2SYM(rb_to_id(tag));
+
+ while (tt) {
+ if (tt->tag == tag) {
+ tt->dst = tag;
+ tt->retval = value;
+ break;
+ }
+ if (tt->tag == PROT_THREAD) {
+ rb_raise(rb_eThreadError, "uncaught throw `%s' in thread 0x%lx",
+ rb_id2name(SYM2ID(tag)),
+ curr_thread);
+ }
+ tt = tt->prev;
+ }
+ if (!tt) {
+ rb_name_error(SYM2ID(tag), "uncaught throw `%s'", rb_id2name(SYM2ID(tag)));
+ }
+ rb_trap_restore_mask();
+ JUMP_TAG(TAG_THROW);
+#ifndef __GNUC__
+ return Qnil; /* not reached */
+#endif
+}
+
+void
+rb_throw(tag, val)
+ const char *tag;
+ VALUE val;
+{
+ VALUE argv[2];
+
+ argv[0] = ID2SYM(rb_intern(tag));
+ argv[1] = val;
+ rb_f_throw(2, argv);
+}
+
+static VALUE
+recursive_check(obj)
+ VALUE obj;
+{
+ VALUE hash = rb_thread_local_aref(rb_thread_current(), recursive_key);
+
+ if (NIL_P(hash) || TYPE(hash) != T_HASH) {
+ return Qfalse;
+ }
+ else {
+ VALUE list = rb_hash_aref(hash, ID2SYM(ruby_frame->this_func));
+
+ if (NIL_P(list) || TYPE(list) != T_ARRAY) return Qfalse;
+ return rb_ary_includes(list, rb_obj_id(obj));
+ }
+}
+
+static void
+recursive_push(obj)
+ VALUE obj;
+{
+ VALUE hash = rb_thread_local_aref(rb_thread_current(), recursive_key);
+ VALUE list, sym;
+
+ sym = ID2SYM(ruby_frame->this_func);
+ if (NIL_P(hash) || TYPE(hash) != T_HASH) {
+ hash = rb_hash_new();
+ rb_thread_local_aset(rb_thread_current(), recursive_key, hash);
+ list = Qnil;
+ }
+ else {
+ list = rb_hash_aref(hash, sym);
+ }
+ if (NIL_P(list) || TYPE(list) != T_ARRAY) {
+ list = rb_ary_new();
+ rb_hash_aset(hash, sym, list);
+ }
+ rb_ary_push(list, rb_obj_id(obj));
+}
+
+static void
+recursive_pop()
+{
+ VALUE hash = rb_thread_local_aref(rb_thread_current(), recursive_key);
+ VALUE list, sym;
+
+ sym = ID2SYM(ruby_frame->this_func);
+ if (NIL_P(hash) || TYPE(hash) != T_HASH) {
+ VALUE symname = rb_inspect(sym);
+ VALUE thrname = rb_inspect(rb_thread_current());
+ rb_raise(rb_eTypeError, "invalid inspect_tbl hash for %s in %s",
+ StringValuePtr(symname), StringValuePtr(thrname));
+ }
+ list = rb_hash_aref(hash, sym);
+ if (NIL_P(list) || TYPE(list) != T_ARRAY) {
+ VALUE symname = rb_inspect(sym);
+ VALUE thrname = rb_inspect(rb_thread_current());
+ rb_raise(rb_eTypeError, "invalid inspect_tbl list for %s in %s",
+ StringValuePtr(symname), StringValuePtr(thrname));
+ }
+ rb_ary_pop(list);
+}
+
+VALUE
+rb_exec_recursive(func, obj, arg)
+ VALUE (*func)(ANYARGS); /* VALUE obj, VALUE arg, int flag */
+ VALUE obj, arg;
+{
+ if (recursive_check(obj)) {
+ return (*func)(obj, arg, Qtrue);
+ }
+ else {
+ VALUE result;
+ int state;
+
+ recursive_push(obj);
+ PUSH_TAG(PROT_NONE);
+ if ((state = EXEC_TAG()) == 0) {
+ result = (*func)(obj, arg, Qfalse);
+ }
+ POP_TAG();
+ recursive_pop();
+ if (state) JUMP_TAG(state);
+ return result;
+ }
+}
+/**********************************************************************
+
+ file.c -
+
+ $Author: murphy $
+ $Date: 2005-11-05 04:33:55 +0100 (Sa, 05 Nov 2005) $
+ created at: Mon Nov 15 12:24:34 JST 1993
+
+ Copyright (C) 1993-2003 Yukihiro Matsumoto
+ Copyright (C) 2000 Network Applied Communication Laboratory, Inc.
+ Copyright (C) 2000 Information-technology Promotion Agency, Japan
+
+**********************************************************************/
+
+#ifdef _WIN32
+#include "missing/file.h"
+#endif
+
+#include "ruby.h"
+#include "rubyio.h"
+#include "rubysig.h"
+#include "util.h"
+#include "dln.h"
+
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#ifdef HAVE_SYS_FILE_H
+# include <sys/file.h>
+#else
+int flock _((int, int));
+#endif
+
+#ifdef HAVE_SYS_PARAM_H
+# include <sys/param.h>
+#endif
+#ifndef MAXPATHLEN
+# define MAXPATHLEN 1024
+#endif
+
+#include <time.h>
+
+VALUE rb_time_new _((time_t, time_t));
+
+#ifdef HAVE_UTIME_H
+#include <utime.h>
+#elif defined HAVE_SYS_UTIME_H
+#include <sys/utime.h>
+#endif
+
+#ifdef HAVE_PWD_H
+#include <pwd.h>
+#endif
+
+#ifndef HAVE_STRING_H
+char *strrchr _((const char*,const char));
+#endif
+
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#ifdef HAVE_SYS_MKDEV_H
+#include <sys/mkdev.h>
+#endif
+
+#if !defined HAVE_LSTAT && !defined lstat
+#define lstat stat
+#endif
+
+VALUE rb_cFile;
+VALUE rb_mFileTest;
+static VALUE rb_cStat;
+
+VALUE
+rb_get_path(obj)
+ VALUE obj;
+{
+ VALUE tmp;
+ static ID to_path;
+
+ rb_check_safe_obj(obj);
+ tmp = rb_check_string_type(obj);
+ if (!NIL_P(tmp)) goto exit;
+
+ if (!to_path) {
+ to_path = rb_intern("to_path");
+ }
+ if (rb_respond_to(obj, to_path)) {
+ obj = rb_funcall(obj, to_path, 0, 0);
+ }
+ tmp = rb_str_to_str(obj);
+ exit:
+ if (obj != tmp) {
+ rb_check_safe_obj(tmp);
+ }
+ return tmp;
+}
+
+static long
+apply2files(func, vargs, arg)
+ void (*func)();
+ VALUE vargs;
+ void *arg;
+{
+ long i;
+ VALUE path;
+ struct RArray *args = RARRAY(vargs);
+
+ rb_secure(4);
+ for (i=0; i<args->len; i++) {
+ path = rb_get_path(args->ptr[i]);
+ (*func)(StringValueCStr(path), arg);
+ }
+
+ return args->len;
+}
+
+/*
+ * call-seq:
+ * file.path -> filename
+ *
+ * Returns the pathname used to create <i>file</i> as a string. Does
+ * not normalize the name.
+ *
+ * File.new("testfile").path #=> "testfile"
+ * File.new("/tmp/../tmp/xxx", "w").path #=> "/tmp/../tmp/xxx"
+ *
+ */
+
+static VALUE
+rb_file_path(obj)
+ VALUE obj;
+{
+ OpenFile *fptr;
+
+ fptr = RFILE(rb_io_taint_check(obj))->fptr;
+ rb_io_check_initialized(fptr);
+ if (!fptr->path) return Qnil;
+ return rb_tainted_str_new2(fptr->path);
+}
+
+static VALUE
+stat_new_0(klass, st)
+ VALUE klass;
+ struct stat *st;
+{
+ struct stat *nst = 0;
+
+ if (st) {
+ nst = ALLOC(struct stat);
+ *nst = *st;
+ }
+ return Data_Wrap_Struct(klass, NULL, free, nst);
+}
+
+static VALUE
+stat_new(st)
+ struct stat *st;
+{
+ return stat_new_0(rb_cStat, st);
+}
+
+static struct stat*
+get_stat(self)
+ VALUE self;
+{
+ struct stat* st;
+ Data_Get_Struct(self, struct stat, st);
+ if (!st) rb_raise(rb_eTypeError, "uninitialized File::Stat");
+ return st;
+}
+
+/*
+ * call-seq:
+ * stat <=> other_stat => -1, 0, 1
+ *
+ * Compares <code>File::Stat</code> objects by comparing their
+ * respective modification times.
+ *
+ * f1 = File.new("f1", "w")
+ * sleep 1
+ * f2 = File.new("f2", "w")
+ * f1.stat <=> f2.stat #=> -1
+ */
+
+static VALUE
+rb_stat_cmp(self, other)
+ VALUE self, other;
+{
+ if (rb_obj_is_kind_of(other, rb_obj_class(self))) {
+ time_t t1 = get_stat(self)->st_mtime;
+ time_t t2 = get_stat(other)->st_mtime;
+ if (t1 == t2)
+ return INT2FIX(0);
+ else if (t1 < t2)
+ return INT2FIX(-1);
+ else
+ return INT2FIX(1);
+ }
+ return Qnil;
+}
+
+/*
+ * call-seq:
+ * stat.dev => fixnum
+ *
+ * Returns an integer representing the device on which <i>stat</i>
+ * resides.
+ *
+ * File.stat("testfile").dev #=> 774
+ */
+
+static VALUE
+rb_stat_dev(self)
+ VALUE self;
+{
+ return INT2NUM(get_stat(self)->st_dev);
+}
+
+/*
+ * call-seq:
+ * stat.dev_major => fixnum
+ *
+ * Returns the major part of <code>File_Stat#dev</code> or
+ * <code>nil</code>.
+ *
+ * File.stat("/dev/fd1").dev_major #=> 2
+ * File.stat("/dev/tty").dev_major #=> 5
+ */
+
+static VALUE
+rb_stat_dev_major(self)
+ VALUE self;
+{
+#if defined(major)
+ long dev = get_stat(self)->st_dev;
+ return ULONG2NUM(major(dev));
+#else
+ return Qnil;
+#endif
+}
+
+/*
+ * call-seq:
+ * stat.dev_minor => fixnum
+ *
+ * Returns the minor part of <code>File_Stat#dev</code> or
+ * <code>nil</code>.
+ *
+ * File.stat("/dev/fd1").dev_minor #=> 1
+ * File.stat("/dev/tty").dev_minor #=> 0
+ */
+
+static VALUE
+rb_stat_dev_minor(self)
+ VALUE self;
+{
+#if defined(minor)
+ long dev = get_stat(self)->st_dev;
+ return ULONG2NUM(minor(dev));
+#else
+ return Qnil;
+#endif
+}
+
+
+/*
+ * call-seq:
+ * stat.ino => fixnum
+ *
+ * Returns the inode number for <i>stat</i>.
+ *
+ * File.stat("testfile").ino #=> 1083669
+ *
+ */
+
+static VALUE
+rb_stat_ino(self)
+ VALUE self;
+{
+#ifdef HUGE_ST_INO
+ return ULL2NUM(get_stat(self)->st_ino);
+#else
+ return ULONG2NUM(get_stat(self)->st_ino);
+#endif
+}
+
+/*
+ * call-seq:
+ * stat.mode => fixnum
+ *
+ * Returns an integer representing the permission bits of
+ * <i>stat</i>. The meaning of the bits is platform dependent; on
+ * Unix systems, see <code>stat(2)</code>.
+ *
+ * File.chmod(0644, "testfile") #=> 1
+ * s = File.stat("testfile")
+ * sprintf("%o", s.mode) #=> "100644"
+ */
+
+static VALUE
+rb_stat_mode(self)
+ VALUE self;
+{
+#ifdef __BORLANDC__
+ return UINT2NUM((unsigned short)(get_stat(self)->st_mode));
+#else
+ return UINT2NUM(get_stat(self)->st_mode);
+#endif
+}
+
+/*
+ * call-seq:
+ * stat.nlink => fixnum
+ *
+ * Returns the number of hard links to <i>stat</i>.
+ *
+ * File.stat("testfile").nlink #=> 1
+ * File.link("testfile", "testfile.bak") #=> 0
+ * File.stat("testfile").nlink #=> 2
+ *
+ */
+
+static VALUE
+rb_stat_nlink(self)
+ VALUE self;
+{
+ return UINT2NUM(get_stat(self)->st_nlink);
+}
+
+
+/*
+ * call-seq:
+ * stat.uid => fixnum
+ *
+ * Returns the numeric user id of the owner of <i>stat</i>.
+ *
+ * File.stat("testfile").uid #=> 501
+ *
+ */
+
+static VALUE
+rb_stat_uid(self)
+ VALUE self;
+{
+ return UINT2NUM(get_stat(self)->st_uid);
+}
+
+/*
+ * call-seq:
+ * stat.gid => fixnum
+ *
+ * Returns the numeric group id of the owner of <i>stat</i>.
+ *
+ * File.stat("testfile").gid #=> 500
+ *
+ */
+
+static VALUE
+rb_stat_gid(self)
+ VALUE self;
+{
+ return UINT2NUM(get_stat(self)->st_gid);
+}
+
+
+/*
+ * call-seq:
+ * stat.rdev => fixnum or nil
+ *
+ * Returns an integer representing the device type on which
+ * <i>stat</i> resides. Returns <code>nil</code> if the operating
+ * system doesn't support this feature.
+ *
+ * File.stat("/dev/fd1").rdev #=> 513
+ * File.stat("/dev/tty").rdev #=> 1280
+ */
+
+static VALUE
+rb_stat_rdev(self)
+ VALUE self;
+{
+#ifdef HAVE_ST_RDEV
+ return ULONG2NUM(get_stat(self)->st_rdev);
+#else
+ return Qnil;
+#endif
+}
+
+/*
+ * call-seq:
+ * stat.rdev_major => fixnum
+ *
+ * Returns the major part of <code>File_Stat#rdev</code> or
+ * <code>nil</code>.
+ *
+ * File.stat("/dev/fd1").rdev_major #=> 2
+ * File.stat("/dev/tty").rdev_major #=> 5
+ */
+
+static VALUE
+rb_stat_rdev_major(self)
+ VALUE self;
+{
+#if defined(HAVE_ST_RDEV) && defined(major)
+ long rdev = get_stat(self)->st_rdev;
+ return ULONG2NUM(major(rdev));
+#else
+ return Qnil;
+#endif
+}
+
+/*
+ * call-seq:
+ * stat.rdev_minor => fixnum
+ *
+ * Returns the minor part of <code>File_Stat#rdev</code> or
+ * <code>nil</code>.
+ *
+ * File.stat("/dev/fd1").rdev_minor #=> 1
+ * File.stat("/dev/tty").rdev_minor #=> 0
+ */
+
+static VALUE
+rb_stat_rdev_minor(self)
+ VALUE self;
+{
+#if defined(HAVE_ST_RDEV) && defined(minor)
+ long rdev = get_stat(self)->st_rdev;
+ return ULONG2NUM(minor(rdev));
+#else
+ return Qnil;
+#endif
+}
+
+/*
+ * call-seq:
+ * stat.size => fixnum
+ *
+ * Returns the size of <i>stat</i> in bytes.
+ *
+ * File.stat("testfile").size #=> 66
+ */
+
+static VALUE
+rb_stat_size(self)
+ VALUE self;
+{
+ return OFFT2NUM(get_stat(self)->st_size);
+}
+
+/*
+ * call-seq:
+ * stat.blksize => integer or nil
+ *
+ * Returns the native file system's block size. Will return <code>nil</code>
+ * on platforms that don't support this information.
+ *
+ * File.stat("testfile").blksize #=> 4096
+ *
+ */
+
+static VALUE
+rb_stat_blksize(self)
+ VALUE self;
+{
+#ifdef HAVE_ST_BLKSIZE
+ return ULONG2NUM(get_stat(self)->st_blksize);
+#else
+ return Qnil;
+#endif
+}
+
+/*
+ * call-seq:
+ * stat.blocks => integer or nil
+ *
+ * Returns the number of native file system blocks allocated for this
+ * file, or <code>nil</code> if the operating system doesn't
+ * support this feature.
+ *
+ * File.stat("testfile").blocks #=> 2
+ */
+
+static VALUE
+rb_stat_blocks(self)
+ VALUE self;
+{
+#ifdef HAVE_ST_BLOCKS
+ return ULONG2NUM(get_stat(self)->st_blocks);
+#else
+ return Qnil;
+#endif
+}
+
+
+/*
+ * call-seq:
+ * stat.atime => time
+ *
+ * Returns the last access time for this file as an object of class
+ * <code>Time</code>.
+ *
+ * File.stat("testfile").atime #=> Wed Dec 31 18:00:00 CST 1969
+ *
+ */
+
+static VALUE
+rb_stat_atime(self)
+ VALUE self;
+{
+ return rb_time_new(get_stat(self)->st_atime, 0);
+}
+
+/*
+ * call-seq:
+ * stat.mtime -> aTime
+ *
+ * Returns the modification time of <i>stat</i>.
+ *
+ * File.stat("testfile").mtime #=> Wed Apr 09 08:53:14 CDT 2003
+ *
+ */
+
+static VALUE
+rb_stat_mtime(self)
+ VALUE self;
+{
+ return rb_time_new(get_stat(self)->st_mtime, 0);
+}
+
+/*
+ * call-seq:
+ * stat.ctime -> aTime
+ *
+ * Returns the change time for <i>stat</i> (that is, the time
+ * directory information about the file was changed, not the file
+ * itself).
+ *
+ * File.stat("testfile").ctime #=> Wed Apr 09 08:53:14 CDT 2003
+ *
+ */
+
+static VALUE
+rb_stat_ctime(self)
+ VALUE self;
+{
+ return rb_time_new(get_stat(self)->st_ctime, 0);
+}
+
+/*
+ * call-seq:
+ * stat.inspect => string
+ *
+ * Produce a nicely formatted description of <i>stat</i>.
+ *
+ * File.stat("/etc/passwd").inspect
+ * #=> "#<File::Stat dev=0xe000005, ino=1078078, mode=0100644,
+ * nlink=1, uid=0, gid=0, rdev=0x0, size=1374, blksize=4096,
+ * blocks=8, atime=Wed Dec 10 10:16:12 CST 2003,
+ * mtime=Fri Sep 12 15:41:41 CDT 2003,
+ * ctime=Mon Oct 27 11:20:27 CST 2003>"
+ */
+
+static VALUE
+rb_stat_inspect(self)
+ VALUE self;
+{
+ VALUE str;
+ int i;
+ static struct {
+ char *name;
+ VALUE (*func)();
+ } member[] = {
+ {"dev", rb_stat_dev},
+ {"ino", rb_stat_ino},
+ {"mode", rb_stat_mode},
+ {"nlink", rb_stat_nlink},
+ {"uid", rb_stat_uid},
+ {"gid", rb_stat_gid},
+ {"rdev", rb_stat_rdev},
+ {"size", rb_stat_size},
+ {"blksize", rb_stat_blksize},
+ {"blocks", rb_stat_blocks},
+ {"atime", rb_stat_atime},
+ {"mtime", rb_stat_mtime},
+ {"ctime", rb_stat_ctime},
+ };
+
+ str = rb_str_buf_new2("#<");
+ rb_str_buf_cat2(str, rb_obj_classname(self));
+ rb_str_buf_cat2(str, " ");
+
+ for (i = 0; i < sizeof(member)/sizeof(member[0]); i++) {
+ VALUE v;
+
+ if (i > 0) {
+ rb_str_buf_cat2(str, ", ");
+ }
+ rb_str_buf_cat2(str, member[i].name);
+ rb_str_buf_cat2(str, "=");
+ v = (*member[i].func)(self);
+ if (i == 2) { /* mode */
+ char buf[32];
+
+ sprintf(buf, "0%lo", NUM2ULONG(v));
+ rb_str_buf_cat2(str, buf);
+ }
+ else if (i == 0 || i == 6) { /* dev/rdev */
+ char buf[32];
+
+ sprintf(buf, "0x%lx", NUM2ULONG(v));
+ rb_str_buf_cat2(str, buf);
+ }
+ else {
+ rb_str_append(str, rb_inspect(v));
+ }
+ }
+ rb_str_buf_cat2(str, ">");
+ OBJ_INFECT(str, self);
+
+ return str;
+}
+
+static int
+rb_stat(file, st)
+ VALUE file;
+ struct stat *st;
+{
+ VALUE tmp;
+
+ rb_secure(2);
+ tmp = rb_check_convert_type(file, T_FILE, "IO", "to_io");
+ if (!NIL_P(tmp)) {
+ OpenFile *fptr;
+
+ GetOpenFile(tmp, fptr);
+ return fstat(fptr->fd, st);
+ }
+ FilePathValue(file);
+ return stat(StringValueCStr(file), st);
+}
+
+/*
+ * call-seq:
+ * File.stat(file_name) => stat
+ *
+ * Returns a <code>File::Stat</code> object for the named file (see
+ * <code>File::Stat</code>).
+ *
+ * File.stat("testfile").mtime #=> Tue Apr 08 12:58:04 CDT 2003
+ *
+ */
+
+static VALUE
+rb_file_s_stat(klass, fname)
+ VALUE klass, fname;
+{
+ struct stat st;
+
+ rb_secure(4);
+ FilePathValue(fname);
+ if (rb_stat(fname, &st) < 0) {
+ rb_sys_fail(StringValueCStr(fname));
+ }
+ return stat_new(&st);
+}
+
+/*
+ * call-seq:
+ * ios.stat => stat
+ *
+ * Returns status information for <em>ios</em> as an object of type
+ * <code>File::Stat</code>.
+ *
+ * f = File.new("testfile")
+ * s = f.stat
+ * "%o" % s.mode #=> "100644"
+ * s.blksize #=> 4096
+ * s.atime #=> Wed Apr 09 08:53:54 CDT 2003
+ *
+ */
+
+static VALUE
+rb_io_stat(obj)
+ VALUE obj;
+{
+ OpenFile *fptr;
+ struct stat st;
+
+ GetOpenFile(obj, fptr);
+ if (fstat(fptr->fd, &st) == -1) {
+ rb_sys_fail(fptr->path);
+ }
+ return stat_new(&st);
+}
+
+/*
+ * call-seq:
+ * File.lstat(file_name) => stat
+ *
+ * Same as <code>File::stat</code>, but does not follow the last symbolic
+ * link. Instead, reports on the link itself.
+ *
+ * File.symlink("testfile", "link2test") #=> 0
+ * File.stat("testfile").size #=> 66
+ * File.lstat("link2test").size #=> 8
+ * File.stat("link2test").size #=> 66
+ *
+ */
+
+static VALUE
+rb_file_s_lstat(klass, fname)
+ VALUE klass, fname;
+{
+#ifdef HAVE_LSTAT
+ struct stat st;
+
+ rb_secure(2);
+ FilePathValue(fname);
+ if (lstat(StringValueCStr(fname), &st) == -1) {
+ rb_sys_fail(RSTRING(fname)->ptr);
+ }
+ return stat_new(&st);
+#else
+ return rb_file_s_stat(klass, fname);
+#endif
+}
+
+
+/*
+ * call-seq:
+ * file.lstat => stat
+ *
+ * Same as <code>IO#stat</code>, but does not follow the last symbolic
+ * link. Instead, reports on the link itself.
+ *
+ * File.symlink("testfile", "link2test") #=> 0
+ * File.stat("testfile").size #=> 66
+ * f = File.new("link2test")
+ * f.lstat.size #=> 8
+ * f.stat.size #=> 66
+ */
+
+static VALUE
+rb_file_lstat(obj)
+ VALUE obj;
+{
+#ifdef HAVE_LSTAT
+ OpenFile *fptr;
+ struct stat st;
+
+ rb_secure(2);
+ GetOpenFile(obj, fptr);
+ if (!fptr->path) return Qnil;
+ if (lstat(fptr->path, &st) == -1) {
+ rb_sys_fail(fptr->path);
+ }
+ return stat_new(&st);
+#else
+ return rb_io_stat(obj);
+#endif
+}
+
+static int
+group_member(gid)
+ GETGROUPS_T gid;
+{
+#ifndef _WIN32
+ if (getgid() == gid)
+ return Qtrue;
+
+# ifdef HAVE_GETGROUPS
+# ifndef NGROUPS
+# ifdef NGROUPS_MAX
+# define NGROUPS NGROUPS_MAX
+# else
+# define NGROUPS 32
+# endif
+# endif
+ {
+ GETGROUPS_T gary[NGROUPS];
+ int anum;
+
+ anum = getgroups(NGROUPS, gary);
+ while (--anum >= 0)
+ if (gary[anum] == gid)
+ return Qtrue;
+ }
+# endif
+#endif
+ return Qfalse;
+}
+
+#ifndef S_IXUGO
+# define S_IXUGO (S_IXUSR | S_IXGRP | S_IXOTH)
+#endif
+
+int
+eaccess(path, mode)
+ const char *path;
+ int mode;
+{
+#if defined(S_IXGRP) && !defined(_WIN32) && !defined(__CYGWIN__)
+ struct stat st;
+ int euid;
+
+ if (stat(path, &st) < 0) return -1;
+
+ euid = geteuid();
+
+ if (euid == 0) {
+ /* Root can read or write any file. */
+ if (!(mode & X_OK))
+ return 0;
+
+ /* Root can execute any file that has any one of the execute
+ bits set. */
+ if (st.st_mode & S_IXUGO)
+ return 0;
+
+ return -1;
+ }
+
+ if (st.st_uid == euid) /* owner */
+ mode <<= 6;
+ else if (getegid() == st.st_gid || group_member(st.st_gid))
+ mode <<= 3;
+
+ if ((st.st_mode & mode) == mode) return 0;
+
+ return -1;
+#else
+# if _MSC_VER >= 1400
+ mode &= 6;
+# endif
+ return access(path, mode);
+#endif
+}
+
+
+/*
+ * Document-class: FileTest
+ *
+ * <code>FileTest</code> implements file test operations similar to
+ * those used in <code>File::Stat</code>. It exists as a standalone
+ * module, and its methods are also insinuated into the <code>File</code>
+ * class. (Note that this is not done by inclusion: the interpreter cheats).
+ *
+ */
+
+
+/*
+ * call-seq:
+ * File.directory?(file_name) => true or false
+ *
+ * Returns <code>true</code> if the named file is a directory,
+ * <code>false</code> otherwise.
+ *
+ * File.directory?(".")
+ */
+
+static VALUE
+test_d(obj, fname)
+ VALUE obj, fname;
+{
+#ifndef S_ISDIR
+# define S_ISDIR(m) ((m & S_IFMT) == S_IFDIR)
+#endif
+
+ struct stat st;
+
+ if (rb_stat(fname, &st) < 0) return Qfalse;
+ if (S_ISDIR(st.st_mode)) return Qtrue;
+ return Qfalse;
+}
+
+/*
+ * call-seq:
+ * File.pipe?(file_name) => true or false
+ *
+ * Returns <code>true</code> if the named file is a pipe.
+ */
+
+static VALUE
+test_p(obj, fname)
+ VALUE obj, fname;
+{
+#ifdef S_IFIFO
+# ifndef S_ISFIFO
+# define S_ISFIFO(m) ((m & S_IFMT) == S_IFIFO)
+# endif
+
+ struct stat st;
+
+ if (rb_stat(fname, &st) < 0) return Qfalse;
+ if (S_ISFIFO(st.st_mode)) return Qtrue;
+
+#endif
+ return Qfalse;
+}
+
+/*
+ * call-seq:
+ * File.symlink?(file_name) => true or false
+ *
+ * Returns <code>true</code> if the named file is a symbolic link.
+ */
+
+static VALUE
+test_l(obj, fname)
+ VALUE obj, fname;
+{
+#ifndef S_ISLNK
+# ifdef _S_ISLNK
+# define S_ISLNK(m) _S_ISLNK(m)
+# else
+# ifdef _S_IFLNK
+# define S_ISLNK(m) ((m & S_IFMT) == _S_IFLNK)
+# else
+# ifdef S_IFLNK
+# define S_ISLNK(m) ((m & S_IFMT) == S_IFLNK)
+# endif
+# endif
+# endif
+#endif
+
+#ifdef S_ISLNK
+ struct stat st;
+
+ rb_secure(2);
+ FilePathValue(fname);
+ if (lstat(StringValueCStr(fname), &st) < 0) return Qfalse;
+ if (S_ISLNK(st.st_mode)) return Qtrue;
+#endif
+
+ return Qfalse;
+}
+
+/*
+ * call-seq:
+ * File.socket?(file_name) => true or false
+ *
+ * Returns <code>true</code> if the named file is a socket.
+ */
+
+static VALUE
+test_S(obj, fname)
+ VALUE obj, fname;
+{
+#ifndef S_ISSOCK
+# ifdef _S_ISSOCK
+# define S_ISSOCK(m) _S_ISSOCK(m)
+# else
+# ifdef _S_IFSOCK
+# define S_ISSOCK(m) ((m & S_IFMT) == _S_IFSOCK)
+# else
+# ifdef S_IFSOCK
+# define S_ISSOCK(m) ((m & S_IFMT) == S_IFSOCK)
+# endif
+# endif
+# endif
+#endif
+
+#ifdef S_ISSOCK
+ struct stat st;
+
+ if (rb_stat(fname, &st) < 0) return Qfalse;
+ if (S_ISSOCK(st.st_mode)) return Qtrue;
+
+#endif
+ return Qfalse;
+}
+
+/*
+ * call-seq:
+ * File.blockdev?(file_name) => true or false
+ *
+ * Returns <code>true</code> if the named file is a block device.
+ */
+
+static VALUE
+test_b(obj, fname)
+ VALUE obj, fname;
+{
+#ifndef S_ISBLK
+# ifdef S_IFBLK
+# define S_ISBLK(m) ((m & S_IFMT) == S_IFBLK)
+# else
+# define S_ISBLK(m) (0) /* anytime false */
+# endif
+#endif
+
+#ifdef S_ISBLK
+ struct stat st;
+
+ if (rb_stat(fname, &st) < 0) return Qfalse;
+ if (S_ISBLK(st.st_mode)) return Qtrue;
+
+#endif
+ return Qfalse;
+}
+
+/*
+ * call-seq:
+ * File.chardev?(file_name) => true or false
+ *
+ * Returns <code>true</code> if the named file is a character device.
+ */
+static VALUE
+test_c(obj, fname)
+ VALUE obj, fname;
+{
+#ifndef S_ISCHR
+# define S_ISCHR(m) ((m & S_IFMT) == S_IFCHR)
+#endif
+
+ struct stat st;
+
+ if (rb_stat(fname, &st) < 0) return Qfalse;
+ if (S_ISCHR(st.st_mode)) return Qtrue;
+
+ return Qfalse;
+}
+
+
+/*
+ * call-seq:
+ * File.exist?(file_name) => true or false
+ * File.exists?(file_name) => true or false (obsolete)
+ *
+ * Return <code>true</code> if the named file exists.
+ */
+
+static VALUE
+test_e(obj, fname)
+ VALUE obj, fname;
+{
+ struct stat st;
+
+ if (rb_stat(fname, &st) < 0) return Qfalse;
+ return Qtrue;
+}
+
+/*
+ * call-seq:
+ * File.readable?(file_name) => true or false
+ *
+ * Returns <code>true</code> if the named file is readable by the effective
+ * user id of this process.
+ */
+
+static VALUE
+test_r(obj, fname)
+ VALUE obj, fname;
+{
+ rb_secure(2);
+ FilePathValue(fname);
+ if (eaccess(StringValueCStr(fname), R_OK) < 0) return Qfalse;
+ return Qtrue;
+}
+
+/*
+ * call-seq:
+ * File.readable_real?(file_name) => true or false
+ *
+ * Returns <code>true</code> if the named file is readable by the real
+ * user id of this process.
+ */
+
+static VALUE
+test_R(obj, fname)
+ VALUE obj, fname;
+{
+ rb_secure(2);
+ FilePathValue(fname);
+ if (access(StringValueCStr(fname), R_OK) < 0) return Qfalse;
+ return Qtrue;
+}
+
+#ifndef S_IRUGO
+# define S_IRUGO (S_IRUSR | S_IRGRP | S_IROTH)
+#endif
+
+#ifndef S_IWUGO
+# define S_IWUGO (S_IWUSR | S_IWGRP | S_IWOTH)
+#endif
+
+/*
+ * call-seq:
+ * File.world_readable?(file_name) => fixnum or nil
+ *
+ * If <i>file_name</i> is readable by others, returns an integer
+ * representing the file permission bits of <i>file_name</i>. Returns
+ * <code>nil</code> otherwise. The meaning of the bits is platform
+ * dependent; on Unix systems, see <code>stat(2)</code>.
+ *
+ * File.world_readable?("/etc/passwd") # => 420
+ * m = File.world_readable?("/etc/passwd")
+ * sprintf("%o", m) # => "644"
+ */
+
+static VALUE
+test_wr(obj, fname)
+ VALUE obj, fname;
+{
+#ifdef S_IROTH
+ struct stat st;
+
+ if (rb_stat(fname, &st) < 0) return Qnil;
+ if ((st.st_mode & (S_IROTH)) == S_IROTH) {
+ return UINT2NUM(st.st_mode & (S_IRUGO|S_IWUGO|S_IXUGO));
+ }
+#endif
+ return Qnil;
+}
+
+/*
+ * call-seq:
+ * File.writable?(file_name) => true or false
+ *
+ * Returns <code>true</code> if the named file is writable by the effective
+ * user id of this process.
+ */
+
+static VALUE
+test_w(obj, fname)
+ VALUE obj, fname;
+{
+ rb_secure(2);
+ FilePathValue(fname);
+ if (eaccess(StringValueCStr(fname), W_OK) < 0) return Qfalse;
+ return Qtrue;
+}
+
+/*
+ * call-seq:
+ * File.writable_real?(file_name) => true or false
+ *
+ * Returns <code>true</code> if the named file is writable by the real
+ * user id of this process.
+ */
+
+static VALUE
+test_W(obj, fname)
+ VALUE obj, fname;
+{
+ rb_secure(2);
+ FilePathValue(fname);
+ if (access(StringValueCStr(fname), W_OK) < 0) return Qfalse;
+ return Qtrue;
+}
+
+/*
+ * call-seq:
+ * File.world_writable?(file_name) => fixnum or nil
+ *
+ * If <i>file_name</i> is writable by others, returns an integer
+ * representing the file permission bits of <i>file_name</i>. Returns
+ * <code>nil</code> otherwise. The meaning of the bits is platform
+ * dependent; on Unix systems, see <code>stat(2)</code>.
+ *
+ * File.world_writable?("/tmp") #=> 511
+ * m = File.world_writable?("/tmp")
+ * sprintf("%o", m) #=> "777"
+ */
+
+static VALUE
+test_ww(obj, fname)
+ VALUE obj, fname;
+{
+#ifdef S_IWOTH
+ struct stat st;
+
+ if (rb_stat(fname, &st) < 0) return Qfalse;
+ if ((st.st_mode & (S_IWOTH)) == S_IWOTH) {
+ return UINT2NUM(st.st_mode & (S_IRUGO|S_IWUGO|S_IXUGO));
+ }
+#endif
+ return Qnil;
+}
+
+/*
+ * call-seq:
+ * File.executable?(file_name) => true or false
+ *
+ * Returns <code>true</code> if the named file is executable by the effective
+ * user id of this process.
+ */
+
+static VALUE
+test_x(obj, fname)
+ VALUE obj, fname;
+{
+ rb_secure(2);
+ FilePathValue(fname);
+ if (eaccess(StringValueCStr(fname), X_OK) < 0) return Qfalse;
+ return Qtrue;
+}
+
+/*
+ * call-seq:
+ * File.executable_real?(file_name) => true or false
+ *
+ * Returns <code>true</code> if the named file is executable by the real
+ * user id of this process.
+ */
+
+static VALUE
+test_X(obj, fname)
+ VALUE obj, fname;
+{
+ rb_secure(2);
+ FilePathValue(fname);
+ if (access(StringValueCStr(fname), X_OK) < 0) return Qfalse;
+ return Qtrue;
+}
+
+#ifndef S_ISREG
+# define S_ISREG(m) ((m & S_IFMT) == S_IFREG)
+#endif
+
+/*
+ * call-seq:
+ * File.file?(file_name) => true or false
+ *
+ * Returns <code>true</code> if the named file exists and is a
+ * regular file.
+ */
+
+static VALUE
+test_f(obj, fname)
+ VALUE obj, fname;
+{
+ struct stat st;
+
+ if (rb_stat(fname, &st) < 0) return Qfalse;
+ if (S_ISREG(st.st_mode)) return Qtrue;
+ return Qfalse;
+}
+
+/*
+ * call-seq:
+ * File.zero?(file_name) => true or false
+ *
+ * Returns <code>true</code> if the named file exists and has
+ * a zero size.
+ */
+
+static VALUE
+test_z(obj, fname)
+ VALUE obj, fname;
+{
+ struct stat st;
+
+ if (rb_stat(fname, &st) < 0) return Qfalse;
+ if (st.st_size == 0) return Qtrue;
+ return Qfalse;
+}
+
+/*
+ * call-seq:
+ * File.file?(file_name) => integer or nil
+ *
+ * Returns <code>nil</code> if <code>file_name</code> doesn't
+ * exist or has zero size, the size of the file otherwise.
+ */
+
+static VALUE
+test_s(obj, fname)
+ VALUE obj, fname;
+{
+ struct stat st;
+
+ if (rb_stat(fname, &st) < 0) return Qnil;
+ if (st.st_size == 0) return Qnil;
+ return OFFT2NUM(st.st_size);
+}
+
+/*
+ * call-seq:
+ * File.owned?(file_name) => true or false
+ *
+ * Returns <code>true</code> if the named file exists and the
+ * effective used id of the calling process is the owner of
+ * the file.
+ */
+
+static VALUE
+test_owned(obj, fname)
+ VALUE obj, fname;
+{
+ struct stat st;
+
+ if (rb_stat(fname, &st) < 0) return Qfalse;
+ if (st.st_uid == geteuid()) return Qtrue;
+ return Qfalse;
+}
+
+static VALUE
+test_rowned(obj, fname)
+ VALUE obj, fname;
+{
+ struct stat st;
+
+ if (rb_stat(fname, &st) < 0) return Qfalse;
+ if (st.st_uid == getuid()) return Qtrue;
+ return Qfalse;
+}
+
+/*
+ * call-seq:
+ * File.grpowned?(file_name) => true or false
+ *
+ * Returns <code>true</code> if the named file exists and the
+ * effective group id of the calling process is the owner of
+ * the file. Returns <code>false</code> on Windows.
+ */
+
+static VALUE
+test_grpowned(obj, fname)
+ VALUE obj, fname;
+{
+#ifndef _WIN32
+ struct stat st;
+
+ if (rb_stat(fname, &st) < 0) return Qfalse;
+ if (st.st_gid == getegid()) return Qtrue;
+#endif
+ return Qfalse;
+}
+
+#if defined(S_ISUID) || defined(S_ISGID) || defined(S_ISVTX)
+static VALUE
+check3rdbyte(fname, mode)
+ VALUE fname;
+ int mode;
+{
+ struct stat st;
+
+ rb_secure(2);
+ FilePathValue(fname);
+ if (stat(StringValueCStr(fname), &st) < 0) return Qfalse;
+ if (st.st_mode & mode) return Qtrue;
+ return Qfalse;
+}
+#endif
+
+/*
+ * call-seq:
+ * File.setuid?(file_name) => true or false
+ *
+ * Returns <code>true</code> if the named file is a has the setuid bit set.
+ */
+
+static VALUE
+test_suid(obj, fname)
+ VALUE obj, fname;
+{
+#ifdef S_ISUID
+ return check3rdbyte(fname, S_ISUID);
+#else
+ return Qfalse;
+#endif
+}
+
+/*
+ * call-seq:
+ * File.setgid?(file_name) => true or false
+ *
+ * Returns <code>true</code> if the named file is a has the setgid bit set.
+ */
+
+static VALUE
+test_sgid(obj, fname)
+ VALUE obj, fname;
+{
+#ifdef S_ISGID
+ return check3rdbyte(fname, S_ISGID);
+#else
+ return Qfalse;
+#endif
+}
+
+/*
+ * call-seq:
+ * File.sticky?(file_name) => true or false
+ *
+ * Returns <code>true</code> if the named file is a has the sticky bit set.
+ */
+
+static VALUE
+test_sticky(obj, fname)
+ VALUE obj, fname;
+{
+#ifdef S_ISVTX
+ return check3rdbyte(fname, S_ISVTX);
+#else
+ return Qnil;
+#endif
+}
+
+/*
+ * call-seq:
+ * File.size(file_name) => integer
+ *
+ * Returns the size of <code>file_name</code>.
+ */
+
+static VALUE
+rb_file_s_size(klass, fname)
+ VALUE klass, fname;
+{
+ struct stat st;
+
+ if (rb_stat(fname, &st) < 0)
+ rb_sys_fail(StringValueCStr(fname));
+ return OFFT2NUM(st.st_size);
+}
+
+static VALUE
+rb_file_ftype(st)
+ struct stat *st;
+{
+ char *t;
+
+ if (S_ISREG(st->st_mode)) {
+ t = "file";
+ }
+ else if (S_ISDIR(st->st_mode)) {
+ t = "directory";
+ }
+ else if (S_ISCHR(st->st_mode)) {
+ t = "characterSpecial";
+ }
+#ifdef S_ISBLK
+ else if (S_ISBLK(st->st_mode)) {
+ t = "blockSpecial";
+ }
+#endif
+#ifdef S_ISFIFO
+ else if (S_ISFIFO(st->st_mode)) {
+ t = "fifo";
+ }
+#endif
+#ifdef S_ISLNK
+ else if (S_ISLNK(st->st_mode)) {
+ t = "link";
+ }
+#endif
+#ifdef S_ISSOCK
+ else if (S_ISSOCK(st->st_mode)) {
+ t = "socket";
+ }
+#endif
+ else {
+ t = "unknown";
+ }
+
+ return rb_str_new2(t);
+}
+
+/*
+ * call-seq:
+ * File.ftype(file_name) => string
+ *
+ * Identifies the type of the named file; the return string is one of
+ * ``<code>file</code>'', ``<code>directory</code>'',
+ * ``<code>characterSpecial</code>'', ``<code>blockSpecial</code>'',
+ * ``<code>fifo</code>'', ``<code>link</code>'',
+ * ``<code>socket</code>'', or ``<code>unknown</code>''.
+ *
+ * File.ftype("testfile") #=> "file"
+ * File.ftype("/dev/tty") #=> "characterSpecial"
+ * File.ftype("/tmp/.X11-unix/X0") #=> "socket"
+ */
+
+static VALUE
+rb_file_s_ftype(klass, fname)
+ VALUE klass, fname;
+{
+ struct stat st;
+
+ rb_secure(2);
+ FilePathValue(fname);
+ if (lstat(StringValueCStr(fname), &st) == -1) {
+ rb_sys_fail(RSTRING(fname)->ptr);
+ }
+
+ return rb_file_ftype(&st);
+}
+
+/*
+ * call-seq:
+ * File.atime(file_name) => time
+ *
+ * Returns the last access time for the named file as a Time object).
+ *
+ * File.atime("testfile") #=> Wed Apr 09 08:51:48 CDT 2003
+ *
+ */
+
+static VALUE
+rb_file_s_atime(klass, fname)
+ VALUE klass, fname;
+{
+ struct stat st;
+
+ if (rb_stat(fname, &st) < 0)
+ rb_sys_fail(StringValueCStr(fname));
+ return rb_time_new(st.st_atime, 0);
+}
+
+/*
+ * call-seq:
+ * file.atime => time
+ *
+ * Returns the last access time (a <code>Time</code> object)
+ * for <i>file</i>, or epoch if <i>file</i> has not been accessed.
+ *
+ * File.new("testfile").atime #=> Wed Dec 31 18:00:00 CST 1969
+ *
+ */
+
+static VALUE
+rb_file_atime(obj)
+ VALUE obj;
+{
+ OpenFile *fptr;
+ struct stat st;
+
+ GetOpenFile(obj, fptr);
+ if (fstat(fptr->fd, &st) == -1) {
+ rb_sys_fail(fptr->path);
+ }
+ return rb_time_new(st.st_atime, 0);
+}
+
+/*
+ * call-seq:
+ * File.mtime(file_name) => time
+ *
+ * Returns the modification time for the named file as a Time object.
+ *
+ * File.mtime("testfile") #=> Tue Apr 08 12:58:04 CDT 2003
+ *
+ */
+
+static VALUE
+rb_file_s_mtime(klass, fname)
+ VALUE klass, fname;
+{
+ struct stat st;
+
+ if (rb_stat(fname, &st) < 0)
+ rb_sys_fail(RSTRING(fname)->ptr);
+ return rb_time_new(st.st_mtime, 0);
+}
+
+/*
+ * call-seq:
+ * file.mtime -> time
+ *
+ * Returns the modification time for <i>file</i>.
+ *
+ * File.new("testfile").mtime #=> Wed Apr 09 08:53:14 CDT 2003
+ *
+ */
+
+static VALUE
+rb_file_mtime(obj)
+ VALUE obj;
+{
+ OpenFile *fptr;
+ struct stat st;
+
+ GetOpenFile(obj, fptr);
+ if (fstat(fptr->fd, &st) == -1) {
+ rb_sys_fail(fptr->path);
+ }
+ return rb_time_new(st.st_mtime, 0);
+}
+
+/*
+ * call-seq:
+ * File.ctime(file_name) => time
+ *
+ * Returns the change time for the named file (the time at which
+ * directory information about the file was changed, not the file
+ * itself).
+ *
+ * File.ctime("testfile") #=> Wed Apr 09 08:53:13 CDT 2003
+ *
+ */
+
+static VALUE
+rb_file_s_ctime(klass, fname)
+ VALUE klass, fname;
+{
+ struct stat st;
+
+ if (rb_stat(fname, &st) < 0)
+ rb_sys_fail(RSTRING(fname)->ptr);
+ return rb_time_new(st.st_ctime, 0);
+}
+
+/*
+ * call-seq:
+ * file.ctime -> time
+ *
+ * Returns the change time for <i>file</i> (that is, the time directory
+ * information about the file was changed, not the file itself).
+ *
+ * File.new("testfile").ctime #=> Wed Apr 09 08:53:14 CDT 2003
+ *
+ */
+
+static VALUE
+rb_file_ctime(obj)
+ VALUE obj;
+{
+ OpenFile *fptr;
+ struct stat st;
+
+ GetOpenFile(obj, fptr);
+ if (fstat(fptr->fd, &st) == -1) {
+ rb_sys_fail(fptr->path);
+ }
+ return rb_time_new(st.st_ctime, 0);
+}
+
+static void
+chmod_internal(path, mode)
+ const char *path;
+ int mode;
+{
+ if (chmod(path, mode) < 0)
+ rb_sys_fail(path);
+}
+
+/*
+ * call-seq:
+ * File.chmod(mode_int, file_name, ... ) -> integer
+ *
+ * Changes permission bits on the named file(s) to the bit pattern
+ * represented by <i>mode_int</i>. Actual effects are operating system
+ * dependent (see the beginning of this section). On Unix systems, see
+ * <code>chmod(2)</code> for details. Returns the number of files
+ * processed.
+ *
+ * File.chmod(0644, "testfile", "out") #=> 2
+ */
+
+static VALUE
+rb_file_s_chmod(argc, argv)
+ int argc;
+ VALUE *argv;
+{
+ VALUE vmode;
+ VALUE rest;
+ int mode;
+ long n;
+
+ rb_secure(2);
+ rb_scan_args(argc, argv, "1*", &vmode, &rest);
+ mode = NUM2INT(vmode);
+
+ n = apply2files(chmod_internal, rest, (void *)(long)mode);
+ return LONG2FIX(n);
+}
+
+/*
+ * call-seq:
+ * file.chmod(mode_int) => 0
+ *
+ * Changes permission bits on <i>file</i> to the bit pattern
+ * represented by <i>mode_int</i>. Actual effects are platform
+ * dependent; on Unix systems, see <code>chmod(2)</code> for details.
+ * Follows symbolic links. Also see <code>File#lchmod</code>.
+ *
+ * f = File.new("out", "w");
+ * f.chmod(0644) #=> 0
+ */
+
+static VALUE
+rb_file_chmod(obj, vmode)
+ VALUE obj, vmode;
+{
+ OpenFile *fptr;
+ int mode;
+
+ rb_secure(2);
+ mode = NUM2INT(vmode);
+
+ GetOpenFile(obj, fptr);
+#ifdef HAVE_FCHMOD
+ if (fchmod(fptr->fd, mode) == -1)
+ rb_sys_fail(fptr->path);
+#else
+ if (!fptr->path) return Qnil;
+ if (chmod(fptr->path, mode) == -1)
+ rb_sys_fail(fptr->path);
+#endif
+
+ return INT2FIX(0);
+}
+
+#if defined(HAVE_LCHMOD)
+static void
+lchmod_internal(path, mode)
+ const char *path;
+ int mode;
+{
+ if (lchmod(path, mode) < 0)
+ rb_sys_fail(path);
+}
+
+/*
+ * call-seq:
+ * File.lchmod(mode_int, file_name, ...) => integer
+ *
+ * Equivalent to <code>File::chmod</code>, but does not follow symbolic
+ * links (so it will change the permissions associated with the link,
+ * not the file referenced by the link). Often not available.
+ *
+ */
+
+static VALUE
+rb_file_s_lchmod(argc, argv)
+ int argc;
+ VALUE *argv;
+{
+ VALUE vmode;
+ VALUE rest;
+ long mode, n;
+
+ rb_secure(2);
+ rb_scan_args(argc, argv, "1*", &vmode, &rest);
+ mode = NUM2INT(vmode);
+
+ n = apply2files(lchmod_internal, rest, (void *)(long)mode);
+ return LONG2FIX(n);
+}
+#else
+static VALUE
+rb_file_s_lchmod(argc, argv)
+ int argc;
+ VALUE *argv;
+{
+ rb_notimplement();
+ return Qnil; /* not reached */
+}
+#endif
+
+struct chown_args {
+ int owner, group;
+};
+
+static void
+chown_internal(path, args)
+ const char *path;
+ struct chown_args *args;
+{
+ if (chown(path, args->owner, args->group) < 0)
+ rb_sys_fail(path);
+}
+
+/*
+ * call-seq:
+ * File.chown(owner_int, group_int, file_name,... ) -> integer
+ *
+ * Changes the owner and group of the named file(s) to the given
+ * numeric owner and group id's. Only a process with superuser
+ * privileges may change the owner of a file. The current owner of a
+ * file may change the file's group to any group to which the owner
+ * belongs. A <code>nil</code> or -1 owner or group id is ignored.
+ * Returns the number of files processed.
+ *
+ * File.chown(nil, 100, "testfile")
+ *
+ */
+
+static VALUE
+rb_file_s_chown(argc, argv)
+ int argc;
+ VALUE *argv;
+{
+ VALUE o, g, rest;
+ struct chown_args arg;
+ long n;
+
+ rb_secure(2);
+ rb_scan_args(argc, argv, "2*", &o, &g, &rest);
+ if (NIL_P(o)) {
+ arg.owner = -1;
+ }
+ else {
+ arg.owner = NUM2INT(o);
+ }
+ if (NIL_P(g)) {
+ arg.group = -1;
+ }
+ else {
+ arg.group = NUM2INT(g);
+ }
+
+ n = apply2files(chown_internal, rest, &arg);
+ return LONG2FIX(n);
+}
+
+/*
+ * call-seq:
+ * file.chown(owner_int, group_int ) => 0
+ *
+ * Changes the owner and group of <i>file</i> to the given numeric
+ * owner and group id's. Only a process with superuser privileges may
+ * change the owner of a file. The current owner of a file may change
+ * the file's group to any group to which the owner belongs. A
+ * <code>nil</code> or -1 owner or group id is ignored. Follows
+ * symbolic links. See also <code>File#lchown</code>.
+ *
+ * File.new("testfile").chown(502, 1000)
+ *
+ */
+
+static VALUE
+rb_file_chown(obj, owner, group)
+ VALUE obj, owner, group;
+{
+ OpenFile *fptr;
+ int o, g;
+
+ rb_secure(2);
+ o = NUM2INT(owner);
+ g = NUM2INT(group);
+ GetOpenFile(obj, fptr);
+#if defined(DJGPP) || defined(__CYGWIN32__) || defined(_WIN32) || defined(__EMX__)
+ if (!fptr->path) return Qnil;
+ if (chown(fptr->path, o, g) == -1)
+ rb_sys_fail(fptr->path);
+#else
+ if (fchown(fptr->fd, o, g) == -1)
+ rb_sys_fail(fptr->path);
+#endif
+
+ return INT2FIX(0);
+}
+
+#if defined(HAVE_LCHOWN) && !defined(__CHECKER__)
+static void
+lchown_internal(path, args)
+ const char *path;
+ struct chown_args *args;
+{
+ if (lchown(path, args->owner, args->group) < 0)
+ rb_sys_fail(path);
+}
+
+
+/*
+ * call-seq:
+ * file.lchown(owner_int, group_int, file_name,..) => integer
+ *
+ * Equivalent to <code>File::chown</code>, but does not follow symbolic
+ * links (so it will change the owner associated with the link, not the
+ * file referenced by the link). Often not available. Returns number
+ * of files in the argument list.
+ *
+ */
+
+static VALUE
+rb_file_s_lchown(argc, argv)
+ int argc;
+ VALUE *argv;
+{
+ VALUE o, g, rest;
+ struct chown_args arg;
+ long n;
+
+ rb_secure(2);
+ rb_scan_args(argc, argv, "2*", &o, &g, &rest);
+ if (NIL_P(o)) {
+ arg.owner = -1;
+ }
+ else {
+ arg.owner = NUM2INT(o);
+ }
+ if (NIL_P(g)) {
+ arg.group = -1;
+ }
+ else {
+ arg.group = NUM2INT(g);
+ }
+
+ n = apply2files(lchown_internal, rest, &arg);
+ return LONG2FIX(n);
+}
+#else
+static VALUE
+rb_file_s_lchown(argc, argv)
+ int argc;
+ VALUE *argv;
+{
+ rb_notimplement();
+}
+#endif
+
+struct timeval rb_time_timeval();
+
+#if defined(HAVE_UTIMES) && !defined(__CHECKER__)
+
+static void
+utime_internal(path, tvp)
+ char *path;
+ struct timeval tvp[];
+{
+ if (utimes(path, tvp) < 0)
+ rb_sys_fail(path);
+}
+
+/*
+ * call-seq:
+ * File.utime(atime, mtime, file_name,...) => integer
+ *
+ * Sets the access and modification times of each
+ * named file to the first two arguments. Returns
+ * the number of file names in the argument list.
+ */
+
+static VALUE
+rb_file_s_utime(argc, argv)
+ int argc;
+ VALUE *argv;
+{
+ VALUE atime, mtime, rest;
+ struct timeval tvp[2];
+ long n;
+
+ rb_scan_args(argc, argv, "2*", &atime, &mtime, &rest);
+
+ tvp[0] = rb_time_timeval(atime);
+ tvp[1] = rb_time_timeval(mtime);
+
+ n = apply2files(utime_internal, rest, tvp);
+ return LONG2FIX(n);
+}
+
+#else
+
+#if !defined HAVE_UTIME_H && !defined HAVE_SYS_UTIME_H
+struct utimbuf {
+ long actime;
+ long modtime;
+};
+#endif
+
+static void
+utime_internal(path, utp)
+ const char *path;
+ struct utimbuf *utp;
+{
+ if (utime(path, utp) < 0)
+ rb_sys_fail(path);
+}
+
+static VALUE
+rb_file_s_utime(argc, argv)
+ int argc;
+ VALUE *argv;
+{
+ VALUE atime, mtime, rest;
+ long n;
+ struct timeval tv;
+ struct utimbuf utbuf;
+
+ rb_scan_args(argc, argv, "2*", &atime, &mtime, &rest);
+
+ tv = rb_time_timeval(atime);
+ utbuf.actime = tv.tv_sec;
+ tv = rb_time_timeval(mtime);
+ utbuf.modtime = tv.tv_sec;
+
+ n = apply2files(utime_internal, rest, &utbuf);
+ return LONG2FIX(n);
+}
+
+#endif
+
+NORETURN(static void sys_fail2 _((VALUE,VALUE)));
+static void
+sys_fail2(s1, s2)
+ VALUE s1, s2;
+{
+ char *buf;
+ int len;
+
+ len = RSTRING(s1)->len + RSTRING(s2)->len + 5;
+ buf = ALLOCA_N(char, len);
+ snprintf(buf, len, "%s or %s", RSTRING(s1)->ptr, RSTRING(s2)->ptr);
+ rb_sys_fail(buf);
+}
+
+/*
+ * call-seq:
+ * File.link(old_name, new_name) => 0
+ *
+ * Creates a new name for an existing file using a hard link. Will not
+ * overwrite <i>new_name</i> if it already exists (raising a subclass
+ * of <code>SystemCallError</code>). Not available on all platforms.
+ *
+ * File.link("testfile", ".testfile") #=> 0
+ * IO.readlines(".testfile")[0] #=> "This is line one\n"
+ */
+
+static VALUE
+rb_file_s_link(klass, from, to)
+ VALUE klass, from, to;
+{
+#ifdef HAVE_LINK
+ rb_secure(2);
+ FilePathValue(from);
+ FilePathValue(to);
+
+ if (link(StringValueCStr(from), StringValueCStr(to)) < 0) {
+ sys_fail2(from, to);
+ }
+ return INT2FIX(0);
+#else
+ rb_notimplement();
+ return Qnil; /* not reached */
+#endif
+}
+
+/*
+ * call-seq:
+ * File.symlink(old_name, new_name) => 0
+ *
+ * Creates a symbolic link called <i>new_name</i> for the existing file
+ * <i>old_name</i>. Raises a <code>NotImplemented</code> exception on
+ * platforms that do not support symbolic links.
+ *
+ * File.symlink("testfile", "link2test") #=> 0
+ *
+ */
+
+static VALUE
+rb_file_s_symlink(klass, from, to)
+ VALUE klass, from, to;
+{
+#ifdef HAVE_SYMLINK
+ rb_secure(2);
+ FilePathValue(from);
+ FilePathValue(to);
+
+ if (symlink(StringValueCStr(from), StringValueCStr(to)) < 0) {
+ sys_fail2(from, to);
+ }
+ return INT2FIX(0);
+#else
+ rb_notimplement();
+ return Qnil; /* not reached */
+#endif
+}
+
+/*
+ * call-seq:
+ * File.readlink(link_name) -> file_name
+ *
+ * Returns the name of the file referenced by the given link.
+ * Not available on all platforms.
+ *
+ * File.symlink("testfile", "link2test") #=> 0
+ * File.readlink("link2test") #=> "testfile"
+ */
+
+static VALUE
+rb_file_s_readlink(klass, path)
+ VALUE klass, path;
+{
+#ifdef HAVE_READLINK
+ char *buf;
+ int size = 100;
+ int rv;
+ VALUE v;
+
+ rb_secure(2);
+ FilePathValue(path);
+ buf = xmalloc(size);
+ while ((rv = readlink(StringValueCStr(path), buf, size)) == size) {
+ size *= 2;
+ buf = xrealloc(buf, size);
+ }
+ if (rv < 0) {
+ free(buf);
+ rb_sys_fail(RSTRING(path)->ptr);
+ }
+ v = rb_tainted_str_new(buf, rv);
+ free(buf);
+
+ return v;
+#else
+ rb_notimplement();
+ return Qnil; /* not reached */
+#endif
+}
+
+static void
+unlink_internal(path)
+ const char *path;
+{
+ if (unlink(path) < 0)
+ rb_sys_fail(path);
+}
+
+/*
+ * call-seq:
+ * File.delete(file_name, ...) => integer
+ * File.unlink(file_name, ...) => integer
+ *
+ * Deletes the named files, returning the number of names
+ * passed as arguments. Raises an exception on any error.
+ * See also <code>Dir::rmdir</code>.
+ */
+
+static VALUE
+rb_file_s_unlink(klass, args)
+ VALUE klass, args;
+{
+ long n;
+
+ rb_secure(2);
+ n = apply2files(unlink_internal, args, 0);
+ return LONG2FIX(n);
+}
+
+/*
+ * call-seq:
+ * File.rename(old_name, new_name) => 0
+ *
+ * Renames the given file to the new name. Raises a
+ * <code>SystemCallError</code> if the file cannot be renamed.
+ *
+ * File.rename("afile", "afile.bak") #=> 0
+ */
+
+static VALUE
+rb_file_s_rename(klass, from, to)
+ VALUE klass, from, to;
+{
+ const char *src, *dst;
+
+ rb_secure(2);
+ FilePathValue(from);
+ FilePathValue(to);
+ src = StringValueCStr(from);
+ dst = StringValueCStr(to);
+ if (rename(src, dst) < 0) {
+#if defined __CYGWIN__
+ extern unsigned long __attribute__((stdcall)) GetLastError();
+ errno = GetLastError(); /* This is a Cygwin bug */
+#elif defined DOSISH && !defined _WIN32
+ if (errno == EEXIST
+#if defined (__EMX__)
+ || errno == EACCES
+#endif
+ ) {
+ if (chmod(dst, 0666) == 0 &&
+ unlink(dst) == 0 &&
+ rename(src, dst) == 0)
+ return INT2FIX(0);
+ }
+#endif
+ sys_fail2(from, to);
+ }
+
+ return INT2FIX(0);
+}
+
+/*
+ * call-seq:
+ * File.umask() => integer
+ * File.umask(integer) => integer
+ *
+ * Returns the current umask value for this process. If the optional
+ * argument is given, set the umask to that value and return the
+ * previous value. Umask values are <em>subtracted</em> from the
+ * default permissions, so a umask of <code>0222</code> would make a
+ * file read-only for everyone.
+ *
+ * File.umask(0006) #=> 18
+ * File.umask #=> 6
+ */
+
+static VALUE
+rb_file_s_umask(argc, argv)
+ int argc;
+ VALUE *argv;
+{
+ int omask = 0;
+
+ rb_secure(2);
+ if (argc == 0) {
+ omask = umask(0);
+ umask(omask);
+ }
+ else if (argc == 1) {
+ omask = umask(NUM2INT(argv[0]));
+ }
+ else {
+ rb_raise(rb_eArgError, "wrong number of arguments");
+ }
+ return INT2FIX(omask);
+}
+
+#if defined DOSISH
+#define DOSISH_UNC
+#define isdirsep(x) ((x) == '/' || (x) == '\\')
+#else
+#define isdirsep(x) ((x) == '/')
+#endif
+#ifndef CharNext /* defined as CharNext[AW] on Windows. */
+# if defined(DJGPP)
+# define CharNext(p) ((p) + mblen(p, RUBY_MBCHAR_MAXSIZE))
+# else
+# define CharNext(p) ((p) + 1)
+# endif
+#endif
+
+#ifdef __CYGWIN__
+#undef DOSISH
+#define DOSISH_UNC
+#define DOSISH_DRIVE_LETTER
+#endif
+
+#ifdef DOSISH_DRIVE_LETTER
+static inline int
+has_drive_letter(buf)
+ const char *buf;
+{
+ if (ISALPHA(buf[0]) && buf[1] == ':') {
+ return 1;
+ }
+ else {
+ return 0;
+ }
+}
+
+static char*
+getcwdofdrv(drv)
+ int drv;
+{
+ char drive[4];
+ char *drvcwd, *oldcwd;
+
+ drive[0] = drv;
+ drive[1] = ':';
+ drive[2] = '\0';
+
+ /* the only way that I know to get the current directory
+ of a particular drive is to change chdir() to that drive,
+ so save the old cwd before chdir()
+ */
+ oldcwd = my_getcwd();
+ if (chdir(drive) == 0) {
+ drvcwd = my_getcwd();
+ chdir(oldcwd);
+ free(oldcwd);
+ }
+ else {
+ /* perhaps the drive is not exist. we return only drive letter */
+ drvcwd = strdup(drive);
+ }
+ return drvcwd;
+}
+#endif
+
+static inline char *
+skiproot(path)
+ const char *path;
+{
+#ifdef DOSISH_DRIVE_LETTER
+ if (has_drive_letter(path)) path += 2;
+#endif
+ while (isdirsep(*path)) path++;
+ return (char *)path;
+}
+
+#define nextdirsep rb_path_next
+char *
+rb_path_next(s)
+ const char *s;
+{
+ while (*s && !isdirsep(*s)) {
+ s = CharNext(s);
+ }
+ return (char *)s;
+}
+
+#define skipprefix rb_path_skip_prefix
+char *
+rb_path_skip_prefix(path)
+ const char *path;
+{
+#if defined(DOSISH_UNC) || defined(DOSISH_DRIVE_LETTER)
+#ifdef DOSISH_UNC
+ if (isdirsep(path[0]) && isdirsep(path[1])) {
+ if (*(path = nextdirsep(path + 2)))
+ path = nextdirsep(path + 1);
+ return (char *)path;
+ }
+#endif
+#ifdef DOSISH_DRIVE_LETTER
+ if (has_drive_letter(path))
+ return (char *)(path + 2);
+#endif
+#endif
+ return (char *)path;
+}
+
+#define strrdirsep rb_path_last_separator
+char *
+rb_path_last_separator(path)
+ const char *path;
+{
+ char *last = NULL;
+ while (*path) {
+ if (isdirsep(*path)) {
+ const char *tmp = path++;
+ while (isdirsep(*path)) path++;
+ if (!*path) break;
+ last = (char *)tmp;
+ }
+ else {
+ path = CharNext(path);
+ }
+ }
+ return last;
+}
+
+#define chompdirsep rb_path_end
+char *
+rb_path_end(path)
+ const char *path;
+{
+ while (*path) {
+ if (isdirsep(*path)) {
+ const char *last = path++;
+ while (isdirsep(*path)) path++;
+ if (!*path) return (char *)last;
+ }
+ else {
+ path = CharNext(path);
+ }
+ }
+ return (char *)path;
+}
+
+#define BUFCHECK(cond) do {\
+ long bdiff = p - buf;\
+ while (cond) {\
+ buflen *= 2;\
+ }\
+ rb_str_resize(result, buflen);\
+ buf = RSTRING(result)->ptr;\
+ p = buf + bdiff;\
+ pend = buf + buflen;\
+} while (0)
+
+#define BUFINIT() (\
+ p = buf = RSTRING(result)->ptr,\
+ buflen = RSTRING(result)->len,\
+ pend = p + buflen)
+
+#if !defined(TOLOWER)
+#define TOLOWER(c) (ISUPPER(c) ? tolower(c) : (c))
+#endif
+
+static int is_absolute_path _((const char*));
+
+static VALUE
+file_expand_path(fname, dname, result)
+ VALUE fname, dname, result;
+{
+ char *s, *buf, *b, *p, *pend, *root;
+ long buflen, dirlen;
+ int tainted;
+
+ s = StringValuePtr(fname);
+ BUFINIT();
+ tainted = OBJ_TAINTED(fname);
+
+ if (s[0] == '~') {
+ if (isdirsep(s[1]) || s[1] == '\0') {
+ char *dir = getenv("HOME");
+
+ if (!dir) {
+ rb_raise(rb_eArgError, "couldn't find HOME environment -- expanding `%s'", s);
+ }
+ dirlen = strlen(dir);
+ BUFCHECK(dirlen > buflen);
+ strcpy(buf, dir);
+#if defined DOSISH || defined __CYGWIN__
+ for (p = buf; *p; p = CharNext(p)) {
+ if (*p == '\\') {
+ *p = '/';
+ }
+ }
+#else
+ p = buf + strlen(dir);
+#endif
+ s++;
+ tainted = 1;
+ }
+ else {
+#ifdef HAVE_PWD_H
+ struct passwd *pwPtr;
+ s++;
+#endif
+ s = nextdirsep(b = s);
+ BUFCHECK(bdiff + (s-b) >= buflen);
+ memcpy(p, b, s-b);
+ p += s-b;
+ *p = '\0';
+#ifdef HAVE_PWD_H
+ pwPtr = getpwnam(buf);
+ if (!pwPtr) {
+ endpwent();
+ rb_raise(rb_eArgError, "user %s doesn't exist", buf);
+ }
+ dirlen = strlen(pwPtr->pw_dir);
+ BUFCHECK(dirlen > buflen);
+ strcpy(buf, pwPtr->pw_dir);
+ p = buf + strlen(pwPtr->pw_dir);
+ endpwent();
+#endif
+ }
+ }
+#ifdef DOSISH_DRIVE_LETTER
+ /* skip drive letter */
+ else if (has_drive_letter(s)) {
+ if (isdirsep(s[2])) {
+ /* specified drive letter, and full path */
+ /* skip drive letter */
+ BUFCHECK(bdiff + 2 >= buflen);
+ memcpy(p, s, 2);
+ p += 2;
+ s += 2;
+ }
+ else {
+ /* specified drive, but not full path */
+ int same = 0;
+ if (!NIL_P(dname)) {
+ file_expand_path(dname, Qnil, result);
+ BUFINIT();
+ if (has_drive_letter(p) && TOLOWER(p[0]) == TOLOWER(s[0])) {
+ /* ok, same drive */
+ same = 1;
+ }
+ }
+ if (!same) {
+ char *dir = getcwdofdrv(*s);
+
+ tainted = 1;
+ dirlen = strlen(dir);
+ BUFCHECK(dirlen > buflen);
+ strcpy(buf, dir);
+ free(dir);
+ }
+ p = chompdirsep(skiproot(buf));
+ s += 2;
+ }
+ }
+#endif
+ else if (!is_absolute_path(s)) {
+ if (!NIL_P(dname)) {
+ file_expand_path(dname, Qnil, result);
+ BUFINIT();
+ }
+ else {
+ char *dir = my_getcwd();
+
+ tainted = 1;
+ dirlen = strlen(dir);
+ BUFCHECK(dirlen > buflen);
+ strcpy(buf, dir);
+ free(dir);
+ }
+#if defined DOSISH || defined __CYGWIN__
+ if (isdirsep(*s)) {
+ /* specified full path, but not drive letter nor UNC */
+ /* we need to get the drive letter or UNC share name */
+ p = skipprefix(buf);
+ }
+ else
+#endif
+ p = chompdirsep(skiproot(buf));
+ }
+ else {
+ b = s;
+ do s++; while (isdirsep(*s));
+ p = buf + (s - b);
+ BUFCHECK(bdiff >= buflen);
+ memset(buf, '/', p - buf);
+ }
+ if (p > buf && p[-1] == '/')
+ --p;
+ else
+ *p = '/';
+
+ p[1] = 0;
+ root = skipprefix(buf);
+
+ b = s;
+ while (*s) {
+ switch (*s) {
+ case '.':
+ if (b == s++) { /* beginning of path element */
+ switch (*s) {
+ case '\0':
+ b = s;
+ break;
+ case '.':
+ if (*(s+1) == '\0' || isdirsep(*(s+1))) {
+ /* We must go back to the parent */
+ *p = '\0';
+ if (!(b = strrdirsep(root))) {
+ *p = '/';
+ }
+ else {
+ p = b;
+ }
+ b = ++s;
+ }
+ break;
+ case '/':
+#if defined DOSISH || defined __CYGWIN__
+ case '\\':
+#endif
+ b = ++s;
+ break;
+ default:
+ /* ordinary path element, beginning don't move */
+ break;
+ }
+ }
+ break;
+ case '/':
+#if defined DOSISH || defined __CYGWIN__
+ case '\\':
+#endif
+ if (s > b) {
+ long rootdiff = root - buf;
+ BUFCHECK(bdiff + (s-b+1) >= buflen);
+ root = buf + rootdiff;
+ memcpy(++p, b, s-b);
+ p += s-b;
+ *p = '/';
+ }
+ b = ++s;
+ break;
+ default:
+ s = CharNext(s);
+ break;
+ }
+ }
+
+ if (s > b) {
+ BUFCHECK(bdiff + (s-b) >= buflen);
+ memcpy(++p, b, s-b);
+ p += s-b;
+ }
+ if (p == skiproot(buf) - 1) p++;
+
+ if (tainted) OBJ_TAINT(result);
+ RSTRING(result)->len = p - buf;
+ *p = '\0';
+ return result;
+}
+
+VALUE
+rb_file_expand_path(fname, dname)
+ VALUE fname, dname;
+{
+ return file_expand_path(fname, dname, rb_str_new(0, MAXPATHLEN + 2));
+}
+
+/*
+ * call-seq:
+ * File.expand_path(file_name [, dir_string] ) -> abs_file_name
+ *
+ * Converts a pathname to an absolute pathname. Relative paths are
+ * referenced from the current working directory of the process unless
+ * <i>dir_string</i> is given, in which case it will be used as the
+ * starting point. The given pathname may start with a
+ * ``<code>~</code>'', which expands to the process owner's home
+ * directory (the environment variable <code>HOME</code> must be set
+ * correctly). ``<code>~</code><i>user</i>'' expands to the named
+ * user's home directory.
+ *
+ * File.expand_path("~oracle/bin") #=> "/home/oracle/bin"
+ * File.expand_path("../../bin", "/tmp/x") #=> "/bin"
+ */
+
+VALUE
+rb_file_s_expand_path(argc, argv)
+ int argc;
+ VALUE *argv;
+{
+ VALUE fname, dname;
+
+ if (argc == 1) {
+ return rb_file_expand_path(argv[0], Qnil);
+ }
+ rb_scan_args(argc, argv, "11", &fname, &dname);
+
+ return rb_file_expand_path(fname, dname);
+}
+
+static int
+rmext(p, e)
+ const char *p, *e;
+{
+ int l1, l2;
+
+ if (!e) return 0;
+
+ l1 = chompdirsep(p) - p;
+ l2 = strlen(e);
+ if (l2 == 2 && e[1] == '*') {
+ e = strrchr(p, *e);
+ if (!e) return 0;
+ return e - p;
+ }
+ if (l1 < l2) return l1;
+
+ if (strncmp(p+l1-l2, e, l2) == 0) {
+ return l1-l2;
+ }
+ return 0;
+}
+
+/*
+ * call-seq:
+ * File.basename(file_name [, suffix] ) -> base_name
+ *
+ * Returns the last component of the filename given in <i>file_name</i>,
+ * which must be formed using forward slashes (``<code>/</code>'')
+ * regardless of the separator used on the local file system. If
+ * <i>suffix</i> is given and present at the end of <i>file_name</i>,
+ * it is removed.
+ *
+ * File.basename("/home/gumby/work/ruby.rb") #=> "ruby.rb"
+ * File.basename("/home/gumby/work/ruby.rb", ".rb") #=> "ruby"
+ */
+
+static VALUE
+rb_file_s_basename(argc, argv)
+ int argc;
+ VALUE *argv;
+{
+ VALUE fname, fext, basename;
+ char *name, *p;
+ int f;
+
+ if (rb_scan_args(argc, argv, "11", &fname, &fext) == 2) {
+ StringValue(fext);
+ }
+ StringValue(fname);
+ if (RSTRING(fname)->len == 0 || !*(name = RSTRING(fname)->ptr))
+ return fname;
+ if (!*(name = skiproot(name))) {
+ p = name - 1;
+ f = 1;
+#ifdef DOSISH_DRIVE_LETTER
+ if (*p == ':') {
+ p++;
+ f = 0;
+ }
+#endif
+ }
+ else if (!(p = strrdirsep(name))) {
+ if (NIL_P(fext) || !(f = rmext(name, StringValueCStr(fext)))) {
+ f = chompdirsep(name) - name;
+ if (f == RSTRING(fname)->len) return fname;
+ }
+ p = name;
+ }
+ else {
+ while (isdirsep(*p)) p++; /* skip last / */
+ if (NIL_P(fext) || !(f = rmext(p, StringValueCStr(fext)))) {
+ f = chompdirsep(p) - p;
+ }
+ }
+ basename = rb_str_new(p, f);
+ OBJ_INFECT(basename, fname);
+ return basename;
+}
+
+/*
+ * call-seq:
+ * File.dirname(file_name ) -> dir_name
+ *
+ * Returns all components of the filename given in <i>file_name</i>
+ * except the last one. The filename must be formed using forward
+ * slashes (``<code>/</code>'') regardless of the separator used on the
+ * local file system.
+ *
+ * File.dirname("/home/gumby/work/ruby.rb") #=> "/home/gumby/work"
+ */
+
+static VALUE
+rb_file_s_dirname(klass, fname)
+ VALUE klass, fname;
+{
+ char *name, *root, *p;
+ VALUE dirname;
+
+ name = StringValueCStr(fname);
+ root = skiproot(name);
+#ifdef DOSISH_UNC
+ if (root > name + 2 && isdirsep(*name))
+ name = root - 2;
+#else
+ if (root > name + 1)
+ name = root - 1;
+#endif
+ p = strrdirsep(root);
+ if (!p) {
+ p = root;
+ }
+ if (p == name)
+ return rb_str_new2(".");
+ dirname = rb_str_new(name, p - name);
+#ifdef DOSISH_DRIVE_LETTER
+ if (root == name + 2 && name[1] == ':')
+ rb_str_cat(dirname, ".", 1);
+#endif
+ OBJ_INFECT(dirname, fname);
+ return dirname;
+}
+
+/*
+ * call-seq:
+ * File.extname(path) -> string
+ *
+ * Returns the extension (the portion of file name in <i>path</i>
+ * after the period).
+ *
+ * File.extname("test.rb") #=> ".rb"
+ * File.extname("a/b/d/test.rb") #=> ".rb"
+ * File.extname("test") #=> ""
+ * File.extname(".profile") #=> ""
+ *
+ */
+
+static VALUE
+rb_file_s_extname(klass, fname)
+ VALUE klass, fname;
+{
+ char *name, *p, *e;
+ VALUE extname;
+
+ name = StringValueCStr(fname);
+ p = strrdirsep(name); /* get the last path component */
+ if (!p)
+ p = name;
+ else
+ p++;
+
+ e = strrchr(p, '.'); /* get the last dot of the last component */
+ if (!e || e == p) /* no dot, or the only dot is first? */
+ return rb_str_new2("");
+ extname = rb_str_new(e, chompdirsep(e) - e); /* keep the dot, too! */
+ OBJ_INFECT(extname, fname);
+ return extname;
+}
+
+/*
+ * call-seq:
+ * File.path(path) -> string
+ *
+ * Returns the string representation of the path
+ *
+ * File.path("/dev/null") #=> "/dev/null"
+ * File.path(Pathname.new("/tmp")) #=> "/tmp"
+ *
+ */
+
+static VALUE
+rb_file_s_path(klass, fname)
+ VALUE klass, fname;
+{
+ return rb_get_path(fname);
+}
+
+/*
+ * call-seq:
+ * File.split(file_name) => array
+ *
+ * Splits the given string into a directory and a file component and
+ * returns them in a two-element array. See also
+ * <code>File::dirname</code> and <code>File::basename</code>.
+ *
+ * File.split("/home/gumby/.profile") #=> ["/home/gumby", ".profile"]
+ */
+
+static VALUE
+rb_file_s_split(klass, path)
+ VALUE klass, path;
+{
+ StringValue(path); /* get rid of converting twice */
+ return rb_assoc_new(rb_file_s_dirname(Qnil, path), rb_file_s_basename(1,&path));
+}
+
+static VALUE separator;
+
+static VALUE rb_file_join _((VALUE ary, VALUE sep));
+
+static VALUE
+file_inspect_join(ary, arg, recur)
+ VALUE ary;
+ VALUE *arg;
+{
+ if (recur) return rb_str_new2("[...]");
+ return rb_file_join(arg[0], arg[1]);
+}
+
+static VALUE
+rb_file_join(ary, sep)
+ VALUE ary, sep;
+{
+ long len, i;
+ int taint = 0;
+ VALUE result, tmp;
+ char *name;
+
+ if (RARRAY(ary)->len == 0) return rb_str_new(0, 0);
+ if (OBJ_TAINTED(ary)) taint = 1;
+ if (OBJ_TAINTED(sep)) taint = 1;
+
+ len = 1;
+ for (i=0; i<RARRAY(ary)->len; i++) {
+ if (TYPE(RARRAY(ary)->ptr[i]) == T_STRING) {
+ len += RSTRING(RARRAY(ary)->ptr[i])->len;
+ }
+ else {
+ len += 10;
+ }
+ }
+ if (!NIL_P(sep) && TYPE(sep) == T_STRING) {
+ len += RSTRING(sep)->len * RARRAY(ary)->len - 1;
+ }
+ result = rb_str_buf_new(len);
+ for (i=0; i<RARRAY(ary)->len; i++) {
+ tmp = RARRAY(ary)->ptr[i];
+ switch (TYPE(tmp)) {
+ case T_STRING:
+ break;
+ case T_ARRAY:
+ {
+ VALUE args[2];
+
+ args[0] = tmp;
+ args[1] = sep;
+ tmp = rb_exec_recursive(file_inspect_join, ary, (VALUE)args);
+ }
+ break;
+ default:
+ tmp = rb_obj_as_string(tmp);
+ }
+ name = StringValueCStr(result);
+ if (i > 0 && !NIL_P(sep) && !*chompdirsep(name))
+ rb_str_buf_append(result, sep);
+ rb_str_buf_append(result, tmp);
+ if (OBJ_TAINTED(tmp)) taint = 1;
+ }
+
+ if (taint) OBJ_TAINT(result);
+ return result;
+}
+
+/*
+ * call-seq:
+ * File.join(string, ...) -> path
+ *
+ * Returns a new string formed by joining the strings using
+ * <code>File::SEPARATOR</code>.
+ *
+ * File.join("usr", "mail", "gumby") #=> "usr/mail/gumby"
+ *
+ */
+
+static VALUE
+rb_file_s_join(klass, args)
+ VALUE klass, args;
+{
+ return rb_file_join(args, separator);
+}
+
+/*
+ * call-seq:
+ * File.truncate(file_name, integer) => 0
+ *
+ * Truncates the file <i>file_name</i> to be at most <i>integer</i>
+ * bytes long. Not available on all platforms.
+ *
+ * f = File.new("out", "w")
+ * f.write("1234567890") #=> 10
+ * f.close #=> nil
+ * File.truncate("out", 5) #=> 0
+ * File.size("out") #=> 5
+ *
+ */
+
+static VALUE
+rb_file_s_truncate(klass, path, len)
+ VALUE klass, path, len;
+{
+ off_t pos;
+
+ rb_secure(2);
+ pos = NUM2OFFT(len);
+ FilePathValue(path);
+#ifdef HAVE_TRUNCATE
+ if (truncate(StringValueCStr(path), pos) < 0)
+ rb_sys_fail(RSTRING(path)->ptr);
+#else
+# ifdef HAVE_CHSIZE
+ {
+ int tmpfd;
+
+# ifdef _WIN32
+ if ((tmpfd = open(StringValueCStr(path), O_RDWR)) < 0) {
+ rb_sys_fail(RSTRING(path)->ptr);
+ }
+# else
+ if ((tmpfd = open(StringValueCStr(path), 0)) < 0) {
+ rb_sys_fail(RSTRING(path)->ptr);
+ }
+# endif
+ if (chsize(tmpfd, pos) < 0) {
+ close(tmpfd);
+ rb_sys_fail(RSTRING(path)->ptr);
+ }
+ close(tmpfd);
+ }
+# else
+ rb_notimplement();
+# endif
+#endif
+ return INT2FIX(0);
+}
+
+/*
+ * call-seq:
+ * file.truncate(integer) => 0
+ *
+ * Truncates <i>file</i> to at most <i>integer</i> bytes. The file
+ * must be opened for writing. Not available on all platforms.
+ *
+ * f = File.new("out", "w")
+ * f.syswrite("1234567890") #=> 10
+ * f.truncate(5) #=> 0
+ * f.close() #=> nil
+ * File.size("out") #=> 5
+ */
+
+static VALUE
+rb_file_truncate(obj, len)
+ VALUE obj, len;
+{
+ OpenFile *fptr;
+ off_t pos;
+
+ rb_secure(2);
+ pos = NUM2OFFT(len);
+ GetOpenFile(obj, fptr);
+ if (!(fptr->mode & FMODE_WRITABLE)) {
+ rb_raise(rb_eIOError, "not opened for writing");
+ }
+ rb_io_flush(obj);
+#ifdef HAVE_TRUNCATE
+ if (ftruncate(fptr->fd, pos) < 0)
+ rb_sys_fail(fptr->path);
+#else
+# ifdef HAVE_CHSIZE
+ if (chsize(fptr->fd, pos) < 0)
+ rb_sys_fail(fptr->path);
+# else
+ rb_notimplement();
+# endif
+#endif
+ return INT2FIX(0);
+}
+
+# ifndef LOCK_SH
+# define LOCK_SH 1
+# endif
+# ifndef LOCK_EX
+# define LOCK_EX 2
+# endif
+# ifndef LOCK_NB
+# define LOCK_NB 4
+# endif
+# ifndef LOCK_UN
+# define LOCK_UN 8
+# endif
+
+#if 1
+static int
+rb_thread_flock(fd, op, fptr)
+ int fd, op;
+ OpenFile *fptr;
+{
+ if (rb_thread_alone() || (op & LOCK_NB)) {
+ return flock(fd, op);
+ }
+ op |= LOCK_NB;
+ while (flock(fd, op) < 0) {
+ switch (errno) {
+ case EAGAIN:
+ case EACCES:
+#if defined(EWOULDBLOCK) && EWOULDBLOCK != EAGAIN
+ case EWOULDBLOCK:
+#endif
+ rb_thread_polling(); /* busy wait */
+ rb_io_check_closed(fptr);
+ continue;
+ default:
+ return -1;
+ }
+ }
+ return 0;
+}
+#define flock(fd, op) rb_thread_flock(fd, op, fptr)
+#endif
+
+/*
+ * call-seq:
+ * file.flock (locking_constant ) => 0 or false
+ *
+ * Locks or unlocks a file according to <i>locking_constant</i> (a
+ * logical <em>or</em> of the values in the table below).
+ * Returns <code>false</code> if <code>File::LOCK_NB</code> is
+ * specified and the operation would otherwise have blocked. Not
+ * available on all platforms.
+ *
+ * Locking constants (in class File):
+ *
+ * LOCK_EX | Exclusive lock. Only one process may hold an
+ * | exclusive lock for a given file at a time.
+ * ----------+------------------------------------------------
+ * LOCK_NB | Don't block when locking. May be combined
+ * | with other lock options using logical or.
+ * ----------+------------------------------------------------
+ * LOCK_SH | Shared lock. Multiple processes may each hold a
+ * | shared lock for a given file at the same time.
+ * ----------+------------------------------------------------
+ * LOCK_UN | Unlock.
+ *
+ * Example:
+ *
+ * File.new("testfile").flock(File::LOCK_UN) #=> 0
+ *
+ */
+
+static VALUE
+rb_file_flock(obj, operation)
+ VALUE obj;
+ VALUE operation;
+{
+#ifndef __CHECKER__
+ OpenFile *fptr;
+ int op;
+
+ rb_secure(2);
+ op = NUM2INT(operation);
+ GetOpenFile(obj, fptr);
+
+ if (fptr->mode & FMODE_WRITABLE) {
+ rb_io_flush(obj);
+ }
+ retry:
+ if (flock(fptr->fd, op) < 0) {
+ switch (errno) {
+ case EAGAIN:
+ case EACCES:
+#if defined(EWOULDBLOCK) && EWOULDBLOCK != EAGAIN
+ case EWOULDBLOCK:
+#endif
+ return Qfalse;
+ case EINTR:
+#if defined(ERESTART)
+ case ERESTART:
+#endif
+ goto retry;
+ }
+ rb_sys_fail(fptr->path);
+ }
+#endif
+ return INT2FIX(0);
+}
+#undef flock
+
+static void
+test_check(n, argc, argv)
+ int n, argc;
+ VALUE *argv;
+{
+ int i;
+
+ rb_secure(2);
+ n+=1;
+ if (n != argc) rb_raise(rb_eArgError, "wrong number of arguments (%d for %d)", argc, n);
+ for (i=1; i<n; i++) {
+ switch (TYPE(argv[i])) {
+ case T_STRING:
+ default:
+ FilePathValue(argv[i]);
+ break;
+ case T_FILE:
+ break;
+ }
+ }
+}
+
+#define CHECK(n) test_check((n), argc, argv)
+
+/*
+ * call-seq:
+ * test(int_cmd, file1 [, file2] ) => obj
+ *
+ * Uses the integer <i>aCmd</i> to perform various tests on
+ * <i>file1</i> (first table below) or on <i>file1</i> and
+ * <i>file2</i> (second table).
+ *
+ * File tests on a single file:
+ *
+ * Test Returns Meaning
+ * ?A | Time | Last access time for file1
+ * ?b | boolean | True if file1 is a block device
+ * ?c | boolean | True if file1 is a character device
+ * ?C | Time | Last change time for file1
+ * ?d | boolean | True if file1 exists and is a directory
+ * ?e | boolean | True if file1 exists
+ * ?f | boolean | True if file1 exists and is a regular file
+ * ?g | boolean | True if files has the \CF{setgid} bit
+ * | | set (false under NT)
+ * ?G | boolean | True if file1 exists and has a group
+ * | | ownership equal to the caller's group
+ * ?k | boolean | True if file1 exists and has the sticky bit set
+ * ?l | boolean | True if files exists and is a symbolic link
+ * ?M | Time | Last modification time for file1
+ * ?o | boolean | True if files exists and is owned by
+ * | | the caller's effective uid
+ * ?O | boolean | True if file1 exists and is owned by
+ * | | the caller's real uid
+ * ?p | boolean | True if file1 exists and is a fifo
+ * ?r | boolean | True if file1 is readable by the effective
+ * | | uid/gid of the caller
+ * ?R | boolean | True if file is readable by the real
+ * | | uid/gid of the caller
+ * ?s | int/nil | If files has nonzero size, return the size,
+ * | | otherwise return nil
+ * ?S | boolean | True if file1 exists and is a socket
+ * ?u | boolean | True if file1 has the setuid bit set
+ * ?w | boolean | True if file1 exists and is writable by
+ * | | the effective uid/gid
+ * ?W | boolean | True if file1 exists and is writable by
+ * | | the real uid/gid
+ * ?x | boolean | True if file1 exists and is executable by
+ * | | the effective uid/gid
+ * ?X | boolean | True if file1 exists and is executable by
+ * | | the real uid/gid
+ * ?z | boolean | True if file1 exists and has a zero length
+ *
+ * Tests that take two files:
+ *
+ * ?- | boolean | True if file1 is a hard link to file2
+ * ?= | boolean | True if the modification times of file1
+ * | | and file2 are equal
+ * ?< | boolean | True if the modification time of file1
+ * | | is prior to that of file2
+ * ?> | boolean | True if the modification time of file1
+ * | | is after that of file2
+ */
+
+static VALUE
+rb_f_test(argc, argv)
+ int argc;
+ VALUE *argv;
+{
+ int cmd;
+
+ if (argc == 0) rb_raise(rb_eArgError, "wrong number of arguments");
+#if 0 /* 1.7 behavior? */
+ if (argc == 1) {
+ return RTEST(argv[0]) ? Qtrue : Qfalse;
+ }
+#endif
+ cmd = NUM2CHR(argv[0]);
+ if (cmd == 0) return Qfalse;
+ if (strchr("bcdefgGkloOprRsSuwWxXz", cmd)) {
+ CHECK(1);
+ switch (cmd) {
+ case 'b':
+ return test_b(0, argv[1]);
+
+ case 'c':
+ return test_c(0, argv[1]);
+
+ case 'd':
+ return test_d(0, argv[1]);
+
+ case 'a':
+ case 'e':
+ return test_e(0, argv[1]);
+
+ case 'f':
+ return test_f(0, argv[1]);
+
+ case 'g':
+ return test_sgid(0, argv[1]);
+
+ case 'G':
+ return test_grpowned(0, argv[1]);
+
+ case 'k':
+ return test_sticky(0, argv[1]);
+
+ case 'l':
+ return test_l(0, argv[1]);
+
+ case 'o':
+ return test_owned(0, argv[1]);
+
+ case 'O':
+ return test_rowned(0, argv[1]);
+
+ case 'p':
+ return test_p(0, argv[1]);
+
+ case 'r':
+ return test_r(0, argv[1]);
+
+ case 'R':
+ return test_R(0, argv[1]);
+
+ case 's':
+ return test_s(0, argv[1]);
+
+ case 'S':
+ return test_S(0, argv[1]);
+
+ case 'u':
+ return test_suid(0, argv[1]);
+
+ case 'w':
+ return test_w(0, argv[1]);
+
+ case 'W':
+ return test_W(0, argv[1]);
+
+ case 'x':
+ return test_x(0, argv[1]);
+
+ case 'X':
+ return test_X(0, argv[1]);
+
+ case 'z':
+ return test_z(0, argv[1]);
+ }
+ }
+
+ if (strchr("MAC", cmd)) {
+ struct stat st;
+
+ CHECK(1);
+ if (rb_stat(argv[1], &st) == -1) {
+ rb_sys_fail(RSTRING(argv[1])->ptr);
+ }
+
+ switch (cmd) {
+ case 'A':
+ return rb_time_new(st.st_atime, 0);
+ case 'M':
+ return rb_time_new(st.st_mtime, 0);
+ case 'C':
+ return rb_time_new(st.st_ctime, 0);
+ }
+ }
+
+ if (strchr("-=<>", cmd)) {
+ struct stat st1, st2;
+
+ CHECK(2);
+ if (rb_stat(argv[1], &st1) < 0) return Qfalse;
+ if (rb_stat(argv[2], &st2) < 0) return Qfalse;
+
+ switch (cmd) {
+ case '-':
+ if (st1.st_dev == st2.st_dev && st1.st_ino == st2.st_ino)
+ return Qtrue;
+ return Qfalse;
+
+ case '=':
+ if (st1.st_mtime == st2.st_mtime) return Qtrue;
+ return Qfalse;
+
+ case '>':
+ if (st1.st_mtime > st2.st_mtime) return Qtrue;
+ return Qfalse;
+
+ case '<':
+ if (st1.st_mtime < st2.st_mtime) return Qtrue;
+ return Qfalse;
+ }
+ }
+ /* unknown command */
+ rb_raise(rb_eArgError, "unknown command ?%c", cmd);
+ return Qnil; /* not reached */
+}
+
+
+
+/*
+ * Document-class: File::Stat
+ *
+ * Objects of class <code>File::Stat</code> encapsulate common status
+ * information for <code>File</code> objects. The information is
+ * recorded at the moment the <code>File::Stat</code> object is
+ * created; changes made to the file after that point will not be
+ * reflected. <code>File::Stat</code> objects are returned by
+ * <code>IO#stat</code>, <code>File::stat</code>,
+ * <code>File#lstat</code>, and <code>File::lstat</code>. Many of these
+ * methods return platform-specific values, and not all values are
+ * meaningful on all systems. See also <code>Kernel#test</code>.
+ */
+
+static VALUE rb_stat_s_alloc _((VALUE));
+static VALUE
+rb_stat_s_alloc(klass)
+ VALUE klass;
+{
+ return stat_new_0(klass, 0);
+}
+
+/*
+ * call-seq:
+ *
+ * File::Stat.new(file_name) => stat
+ *
+ * Create a File::Stat object for the given file name (raising an
+ * exception if the file doesn't exist).
+ */
+
+static VALUE
+rb_stat_init(obj, fname)
+ VALUE obj, fname;
+{
+ struct stat st, *nst;
+
+ rb_secure(2);
+ FilePathValue(fname);
+ if (stat(StringValueCStr(fname), &st) == -1) {
+ rb_sys_fail(RSTRING(fname)->ptr);
+ }
+ if (DATA_PTR(obj)) {
+ free(DATA_PTR(obj));
+ DATA_PTR(obj) = NULL;
+ }
+ nst = ALLOC(struct stat);
+ *nst = st;
+ DATA_PTR(obj) = nst;
+
+ return Qnil;
+}
+
+/* :nodoc: */
+static VALUE
+rb_stat_init_copy(copy, orig)
+ VALUE copy, orig;
+{
+ struct stat *nst;
+
+ if (copy == orig) return orig;
+ rb_check_frozen(copy);
+ /* need better argument type check */
+ if (!rb_obj_is_instance_of(orig, rb_obj_class(copy))) {
+ rb_raise(rb_eTypeError, "wrong argument class");
+ }
+ if (DATA_PTR(copy)) {
+ free(DATA_PTR(copy));
+ DATA_PTR(copy) = 0;
+ }
+ if (DATA_PTR(orig)) {
+ nst = ALLOC(struct stat);
+ *nst = *(struct stat*)DATA_PTR(orig);
+ DATA_PTR(copy) = nst;
+ }
+
+ return copy;
+}
+
+/*
+ * call-seq:
+ * stat.ftype => string
+ *
+ * Identifies the type of <i>stat</i>. The return string is one of:
+ * ``<code>file</code>'', ``<code>directory</code>'',
+ * ``<code>characterSpecial</code>'', ``<code>blockSpecial</code>'',
+ * ``<code>fifo</code>'', ``<code>link</code>'',
+ * ``<code>socket</code>'', or ``<code>unknown</code>''.
+ *
+ * File.stat("/dev/tty").ftype #=> "characterSpecial"
+ *
+ */
+
+static VALUE
+rb_stat_ftype(obj)
+ VALUE obj;
+{
+ return rb_file_ftype(get_stat(obj));
+}
+
+/*
+ * call-seq:
+ * stat.directory? => true or false
+ *
+ * Returns <code>true</code> if <i>stat</i> is a directory,
+ * <code>false</code> otherwise.
+ *
+ * File.stat("testfile").directory? #=> false
+ * File.stat(".").directory? #=> true
+ */
+
+static VALUE
+rb_stat_d(obj)
+ VALUE obj;
+{
+ if (S_ISDIR(get_stat(obj)->st_mode)) return Qtrue;
+ return Qfalse;
+}
+
+/*
+ * call-seq:
+ * stat.pipe? => true or false
+ *
+ * Returns <code>true</code> if the operating system supports pipes and
+ * <i>stat</i> is a pipe; <code>false</code> otherwise.
+ */
+
+static VALUE
+rb_stat_p(obj)
+ VALUE obj;
+{
+#ifdef S_IFIFO
+ if (S_ISFIFO(get_stat(obj)->st_mode)) return Qtrue;
+
+#endif
+ return Qfalse;
+}
+
+/*
+ * call-seq:
+ * stat.symlink? => true or false
+ *
+ * Returns <code>true</code> if <i>stat</i> is a symbolic link,
+ * <code>false</code> if it isn't or if the operating system doesn't
+ * support this feature. As <code>File::stat</code> automatically
+ * follows symbolic links, <code>symlink?</code> will always be
+ * <code>false</code> for an object returned by
+ * <code>File::stat</code>.
+ *
+ * File.symlink("testfile", "alink") #=> 0
+ * File.stat("alink").symlink? #=> false
+ * File.lstat("alink").symlink? #=> true
+ *
+ */
+
+static VALUE
+rb_stat_l(obj)
+ VALUE obj;
+{
+#ifdef S_ISLNK
+ if (S_ISLNK(get_stat(obj)->st_mode)) return Qtrue;
+#endif
+ return Qfalse;
+}
+
+/*
+ * call-seq:
+ * stat.socket? => true or false
+ *
+ * Returns <code>true</code> if <i>stat</i> is a socket,
+ * <code>false</code> if it isn't or if the operating system doesn't
+ * support this feature.
+ *
+ * File.stat("testfile").socket? #=> false
+ *
+ */
+
+static VALUE
+rb_stat_S(obj)
+ VALUE obj;
+{
+#ifdef S_ISSOCK
+ if (S_ISSOCK(get_stat(obj)->st_mode)) return Qtrue;
+
+#endif
+ return Qfalse;
+}
+
diff --git a/tests/examplefiles/example.cpp b/tests/examplefiles/example.cpp
new file mode 100644
index 00000000..316c1420
--- /dev/null
+++ b/tests/examplefiles/example.cpp
@@ -0,0 +1,13544 @@
+/***************************************************************************
+ ansigenerator.cpp - description
+ -------------------
+ begin : Jul 5 2004
+ copyright : (C) 2004 by André Simon
+ email : andre.simon1@gmx.de
+ ***************************************************************************/
+
+/***************************************************************************
+ * *
+ * 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 of the License, or *
+ * (at your option) any later version. *
+ * *
+ ***************************************************************************/
+
+#include "ansigenerator.h"
+
+using namespace std;
+
+namespace highlight {
+
+
+string AnsiGenerator::getOpenTag(const string&font,
+ const string&fgCol, const string&bgCol) {
+ ostringstream s;
+ s << "\033["<<font;
+ if (!fgCol.empty())
+ s<<";"<<fgCol;
+ if (!bgCol.empty())
+ s<<";"<<bgCol;
+ s << "m";
+ return s.str();
+}
+
+
+AnsiGenerator::AnsiGenerator(const string &colourTheme)
+ : CodeGenerator(colourTheme) {
+ styleTagOpen.push_back("");
+ styleTagOpen.push_back(getOpenTag("00", "31")); //str
+ styleTagOpen.push_back(getOpenTag("00", "34"));//number
+ styleTagOpen.push_back(getOpenTag("00", "34"));//sl comment
+ styleTagOpen.push_back(getOpenTag("00", "34"));//ml comment
+ styleTagOpen.push_back(getOpenTag("00", "35"));//escapeChar
+ styleTagOpen.push_back(getOpenTag("00", "35"));//directive
+ styleTagOpen.push_back(getOpenTag("01", "31"));//directive string
+ styleTagOpen.push_back(getOpenTag("00", "30"));//linenum
+ styleTagOpen.push_back(getOpenTag("01", "00"));//symbol
+
+ styleTagClose.push_back("");
+ for (int i=1;i<NUMBER_BUILTIN_STYLES; i++) {
+ styleTagClose.push_back("\033[m");
+ }
+ newLineTag = "\n";
+ spacer = " ";
+}
+
+AnsiGenerator::AnsiGenerator() {}
+AnsiGenerator::~AnsiGenerator() {}
+
+string AnsiGenerator::getHeader(const string & title) {
+ return string();
+}
+
+void AnsiGenerator::printBody() {
+ processRootState();
+}
+
+string AnsiGenerator::getFooter() {
+ return string();
+}
+
+string AnsiGenerator::maskCharacter(unsigned char c) {
+ string m;
+ m+=c;
+ return m;
+}
+
+string AnsiGenerator::getMatchingOpenTag(unsigned int styleID) {
+ return (styleID)?getOpenTag("01", "32", ""):getOpenTag("00", "33");
+}
+
+string AnsiGenerator::getMatchingCloseTag(unsigned int styleID) {
+ return "\033[m";
+}
+
+}
+/***************************************************************************
+ ansicode.h - description
+ -------------------
+ begin : Jul 5 2004
+ copyright : (C) 2004 by Andre Simon
+ email : andre.simon1@gmx.de
+ ***************************************************************************/
+
+/***************************************************************************
+ * *
+ * 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 of the License, or *
+ * (at your option) any later version. *
+ * *
+ ***************************************************************************/
+
+#ifndef ANSIGENERATOR_H
+#define ANSIGENERATOR_H
+
+#include <iostream>
+#include <fstream>
+#include <string>
+#include <sstream>
+
+#include "codegenerator.h"
+#include "charcodes.h"
+#include "version.h"
+
+namespace highlight {
+
+/**
+ \brief This class generates ANSI escape sequences.
+
+ It contains information about the resulting document structure (document
+ header and footer), the colour system, white space handling and text
+ formatting attributes.
+
+* @author Andre Simon
+*/
+
+class AnsiGenerator : public highlight::CodeGenerator
+ {
+ public:
+
+ /** Constructor
+ \param colourTheme Name of Colour theme to use
+ */
+ AnsiGenerator( const string &colourTheme);
+ AnsiGenerator();
+ ~AnsiGenerator();
+
+ /** prints document header
+ \param title Title of the document
+ */
+ string getHeader(const string & title);
+
+ /** Prints document footer*/
+ string getFooter();
+
+ /** Prints document body*/
+ void printBody();
+
+ private:
+
+ /** \return escaped character*/
+ virtual string maskCharacter(unsigned char );
+
+
+ /** gibt ANSI-"Tags" zurueck (Farbindex+bold+kursiv)*/
+ string getOpenTag(const string&font,
+ const string&fgCol, const string&bgCol="");
+
+
+
+ string getMatchingOpenTag(unsigned int styleID);
+ string getMatchingCloseTag(unsigned int styleID);
+ };
+
+}
+#endif
+/*
+ * Copyright (c) 1998,1999,2000,2001,2002 Tal Davidson. All rights reserved.
+ *
+ * ASBeautifier.cpp
+ * by Tal Davidson (davidsont@bigfoot.com)
+ * This file is a part of "Artistic Style" - an indentater and reformatter
+ * of C, C, C# and Java source files.
+ *
+ * The "Artistic Style" project, including all files needed to compile it,
+ * is free software; you can redistribute it and/or use it and/or modify it
+ * under the terms of the GNU General Public License as published
+ * by the Free Software Foundation; either version 2 of the License,
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this program.
+ *
+ * Patches:
+ * 18 March 1999 - Brian Rampel -
+ * Fixed inverse insertion of spaces vs. tabs when in -t mode.
+ * 08 may 2004
+ * applied ASBeautifier.cpp.BITFIELD.patch.bz2
+ */
+
+#include "compiler_defines.h"
+#include "ASBeautifier.h"
+
+#include <vector>
+#include <string>
+#include <cctype>
+#include <algorithm>
+#include <iostream>
+
+
+#define INIT_CONTAINER(container, value) {if ( (container) != NULL ) delete (container); (container) = (value); }
+#define DELETE_CONTAINER(container) {if ( (container) != NULL ) delete (container) ; }
+
+#ifdef USES_NAMESPACE
+using namespace std;
+#endif
+
+
+
+
+#ifdef USES_NAMESPACE
+namespace astyle
+ {
+#endif
+
+ bool ASBeautifier::calledInitStatic = false;
+
+ vector<const string*> ASBeautifier::headers;
+ vector<const string*> ASBeautifier::nonParenHeaders;
+ vector<const string*> ASBeautifier::preBlockStatements;
+ vector<const string*> ASBeautifier::assignmentOperators;
+ vector<const string*> ASBeautifier::nonAssignmentOperators;
+
+ /*
+ * initialize the static vars
+ */
+ void ASBeautifier::initStatic()
+ {
+ if (calledInitStatic)
+ return;
+
+ calledInitStatic = true;
+
+ headers.push_back(&AS_IF);
+ headers.push_back(&AS_ELSE);
+ headers.push_back(&AS_FOR);
+ headers.push_back(&AS_WHILE);
+ headers.push_back(&AS_DO);
+ headers.push_back(&AS_TRY);
+ headers.push_back(&AS_CATCH);
+ headers.push_back(&AS_FINALLY);
+ headers.push_back(&AS_SYNCHRONIZED);
+ headers.push_back(&AS_SWITCH);
+ headers.push_back(&AS_CASE);
+ headers.push_back(&AS_DEFAULT);
+ headers.push_back(&AS_FOREACH);
+ headers.push_back(&AS_LOCK);
+ headers.push_back(&AS_UNSAFE);
+ headers.push_back(&AS_FIXED);
+ headers.push_back(&AS_GET);
+ headers.push_back(&AS_SET);
+ headers.push_back(&AS_ADD);
+ headers.push_back(&AS_REMOVE);
+ //headers.push_back(&AS_PUBLIC);
+ //headers.push_back(&AS_PRIVATE);
+ //headers.push_back(&AS_PROTECTED);
+
+ //headers.push_back(&AS_OPERATOR);
+ headers.push_back(&AS_TEMPLATE);
+ headers.push_back(&AS_CONST);
+ /**/
+ headers.push_back(&AS_STATIC);
+ headers.push_back(&AS_EXTERN);
+
+ nonParenHeaders.push_back(&AS_ELSE);
+ nonParenHeaders.push_back(&AS_DO);
+ nonParenHeaders.push_back(&AS_TRY);
+ nonParenHeaders.push_back(&AS_FINALLY);
+ nonParenHeaders.push_back(&AS_STATIC);
+ nonParenHeaders.push_back(&AS_CONST);
+ nonParenHeaders.push_back(&AS_EXTERN);
+ nonParenHeaders.push_back(&AS_CASE);
+ nonParenHeaders.push_back(&AS_DEFAULT);
+ nonParenHeaders.push_back(&AS_UNSAFE);
+ nonParenHeaders.push_back(&AS_GET);
+ nonParenHeaders.push_back(&AS_SET);
+ nonParenHeaders.push_back(&AS_ADD);
+ nonParenHeaders.push_back(&AS_REMOVE);
+
+
+
+ nonParenHeaders.push_back(&AS_PUBLIC);
+ nonParenHeaders.push_back(&AS_PRIVATE);
+ nonParenHeaders.push_back(&AS_PROTECTED);
+ nonParenHeaders.push_back(&AS_TEMPLATE);
+ nonParenHeaders.push_back(&AS_CONST);
+ /// nonParenHeaders.push_back(&AS_ASM);
+
+ preBlockStatements.push_back(&AS_CLASS);
+ preBlockStatements.push_back(&AS_STRUCT);
+ preBlockStatements.push_back(&AS_UNION);
+ preBlockStatements.push_back(&AS_INTERFACE);
+ preBlockStatements.push_back(&AS_NAMESPACE);
+ preBlockStatements.push_back(&AS_THROWS);
+ preBlockStatements.push_back(&AS_EXTERN);
+
+ assignmentOperators.push_back(&AS_ASSIGN);
+ assignmentOperators.push_back(&AS_PLUS_ASSIGN);
+ assignmentOperators.push_back(&AS_MINUS_ASSIGN);
+ assignmentOperators.push_back(&AS_MULT_ASSIGN);
+ assignmentOperators.push_back(&AS_DIV_ASSIGN);
+ assignmentOperators.push_back(&AS_MOD_ASSIGN);
+ assignmentOperators.push_back(&AS_OR_ASSIGN);
+ assignmentOperators.push_back(&AS_AND_ASSIGN);
+ assignmentOperators.push_back(&AS_XOR_ASSIGN);
+ assignmentOperators.push_back(&AS_GR_GR_GR_ASSIGN);
+ assignmentOperators.push_back(&AS_GR_GR_ASSIGN);
+ assignmentOperators.push_back(&AS_LS_LS_LS_ASSIGN);
+ assignmentOperators.push_back(&AS_LS_LS_ASSIGN);
+
+ assignmentOperators.push_back(&AS_RETURN);
+
+ nonAssignmentOperators.push_back(&AS_EQUAL);
+ nonAssignmentOperators.push_back(&AS_PLUS_PLUS);
+ nonAssignmentOperators.push_back(&AS_MINUS_MINUS);
+ nonAssignmentOperators.push_back(&AS_NOT_EQUAL);
+ nonAssignmentOperators.push_back(&AS_GR_EQUAL);
+ nonAssignmentOperators.push_back(&AS_GR_GR_GR);
+ nonAssignmentOperators.push_back(&AS_GR_GR);
+ nonAssignmentOperators.push_back(&AS_LS_EQUAL);
+ nonAssignmentOperators.push_back(&AS_LS_LS_LS);
+ nonAssignmentOperators.push_back(&AS_LS_LS);
+ nonAssignmentOperators.push_back(&AS_ARROW);
+ nonAssignmentOperators.push_back(&AS_AND);
+ nonAssignmentOperators.push_back(&AS_OR);
+ }
+
+ /**
+ * ASBeautifier's constructor
+ */
+ ASBeautifier::ASBeautifier()
+ {
+ initStatic();
+
+ waitingBeautifierStack = NULL;
+ activeBeautifierStack = NULL;
+ waitingBeautifierStackLengthStack = NULL;
+ activeBeautifierStackLengthStack = NULL;
+
+ headerStack = NULL;
+ tempStacks = NULL;
+ blockParenDepthStack = NULL;
+ blockStatementStack = NULL;
+ parenStatementStack = NULL;
+ bracketBlockStateStack = NULL;
+ inStatementIndentStack = NULL;
+ inStatementIndentStackSizeStack = NULL;
+ parenIndentStack = NULL;
+ sourceIterator = NULL;
+
+ isMinimalConditinalIndentSet = false;
+ shouldForceTabIndentation = false;
+
+ setSpaceIndentation(4);
+ setMaxInStatementIndentLength(40);
+ setClassIndent(false);
+ setSwitchIndent(false);
+ setCaseIndent(false);
+ setBlockIndent(false);
+ setBracketIndent(false);
+ setNamespaceIndent(false);
+ setLabelIndent(false);
+ setEmptyLineFill(false);
+ setCStyle();
+ setPreprocessorIndent(false);
+ }
+
+ ASBeautifier::ASBeautifier(const ASBeautifier &other)
+ {
+ waitingBeautifierStack = NULL;
+ activeBeautifierStack = NULL;
+ waitingBeautifierStackLengthStack = NULL;
+ activeBeautifierStackLengthStack = NULL;
+
+ headerStack = new vector<const string*>;
+ *headerStack = *other.headerStack;
+
+ tempStacks = new vector< vector<const string*>* >;
+ vector< vector<const string*>* >::iterator iter;
+ for (iter = other.tempStacks->begin();
+ iter != other.tempStacks->end();
+ ++iter)
+ {
+ vector<const string*> *newVec = new vector<const string*>;
+ *newVec = **iter;
+ tempStacks->push_back(newVec);
+ }
+ blockParenDepthStack = new vector<int>;
+ *blockParenDepthStack = *other.blockParenDepthStack;
+
+ blockStatementStack = new vector<bool>;
+ *blockStatementStack = *other.blockStatementStack;
+
+ parenStatementStack = new vector<bool>;
+ *parenStatementStack = *other.parenStatementStack;
+
+ bracketBlockStateStack = new vector<bool>;
+ *bracketBlockStateStack = *other.bracketBlockStateStack;
+
+ inStatementIndentStack = new vector<int>;
+ *inStatementIndentStack = *other.inStatementIndentStack;
+
+ inStatementIndentStackSizeStack = new vector<int>;
+ *inStatementIndentStackSizeStack = *other.inStatementIndentStackSizeStack;
+
+ parenIndentStack = new vector<int>;
+ *parenIndentStack = *other.parenIndentStack;
+
+ sourceIterator = other.sourceIterator;
+
+ indentString = other.indentString;
+ currentHeader = other.currentHeader;
+ previousLastLineHeader = other.previousLastLineHeader;
+ immediatelyPreviousAssignmentOp = other.immediatelyPreviousAssignmentOp;
+ isInQuote = other.isInQuote;
+ isInComment = other.isInComment;
+ isInCase = other.isInCase;
+ isInQuestion = other.isInQuestion;
+ isInStatement =other. isInStatement;
+ isInHeader = other.isInHeader;
+ isCStyle = other.isCStyle;
+ isInOperator = other.isInOperator;
+ isInTemplate = other.isInTemplate;
+ isInConst = other.isInConst;
+ classIndent = other.classIndent;
+ isInClassHeader = other.isInClassHeader;
+ isInClassHeaderTab = other.isInClassHeaderTab;
+ switchIndent = other.switchIndent;
+ caseIndent = other.caseIndent;
+ namespaceIndent = other.namespaceIndent;
+ bracketIndent = other.bracketIndent;
+ blockIndent = other.blockIndent;
+ labelIndent = other.labelIndent;
+ preprocessorIndent = other.preprocessorIndent;
+ parenDepth = other.parenDepth;
+ indentLength = other.indentLength;
+ blockTabCount = other.blockTabCount;
+ leadingWhiteSpaces = other.leadingWhiteSpaces;
+ maxInStatementIndent = other.maxInStatementIndent;
+ templateDepth = other.templateDepth;
+ quoteChar = other.quoteChar;
+ prevNonSpaceCh = other.prevNonSpaceCh;
+ currentNonSpaceCh = other.currentNonSpaceCh;
+ currentNonLegalCh = other.currentNonLegalCh;
+ prevNonLegalCh = other.prevNonLegalCh;
+ isInConditional = other.isInConditional;
+ minConditionalIndent = other.minConditionalIndent;
+ prevFinalLineSpaceTabCount = other.prevFinalLineSpaceTabCount;
+ prevFinalLineTabCount = other.prevFinalLineTabCount;
+ emptyLineFill = other.emptyLineFill;
+ probationHeader = other.probationHeader;
+ isInDefine = other.isInDefine;
+ isInDefineDefinition = other.isInDefineDefinition;
+ backslashEndsPrevLine = other.backslashEndsPrevLine;
+ defineTabCount = other.defineTabCount;
+ }
+
+ /**
+ * ASBeautifier's destructor
+ */
+ ASBeautifier::~ASBeautifier()
+ {
+ DELETE_CONTAINER( headerStack );
+ DELETE_CONTAINER( tempStacks );
+ DELETE_CONTAINER( blockParenDepthStack );
+ DELETE_CONTAINER( blockStatementStack );
+ DELETE_CONTAINER( parenStatementStack );
+ DELETE_CONTAINER( bracketBlockStateStack );
+ DELETE_CONTAINER( inStatementIndentStack );
+ DELETE_CONTAINER( inStatementIndentStackSizeStack );
+ DELETE_CONTAINER( parenIndentStack );
+
+ // DELETE_CONTAINER( sourceIterator );
+ }
+
+ /**
+ * initialize the ASBeautifier.
+ *
+ * init() should be called every time a ABeautifier object is to start
+ * beautifying a NEW source file.
+ * init() recieves a pointer to a DYNAMICALLY CREATED ASSourceIterator object
+ * that will be used to iterate through the source code. This object will be
+ * deleted during the ASBeautifier's destruction, and thus should not be
+ * deleted elsewhere.
+ *
+ * @param iter a pointer to the DYNAMICALLY CREATED ASSourceIterator object.
+ */
+ void ASBeautifier::init(ASSourceIterator *iter)
+
+ {
+ sourceIterator = iter;
+ init();
+ }
+
+ /**
+ * initialize the ASBeautifier.
+ */
+ void ASBeautifier::init()
+ {
+ INIT_CONTAINER( waitingBeautifierStack, new vector<ASBeautifier*> );
+ INIT_CONTAINER( activeBeautifierStack, new vector<ASBeautifier*> );
+
+ INIT_CONTAINER( waitingBeautifierStackLengthStack, new vector<int> );
+ INIT_CONTAINER( activeBeautifierStackLengthStack, new vector<int> );
+
+ INIT_CONTAINER( headerStack, new vector<const string*> );
+ INIT_CONTAINER( tempStacks, new vector< vector<const string*>* > );
+ tempStacks->push_back(new vector<const string*>);
+
+ INIT_CONTAINER( blockParenDepthStack, new vector<int> );
+ INIT_CONTAINER( blockStatementStack, new vector<bool> );
+ INIT_CONTAINER( parenStatementStack, new vector<bool> );
+
+ INIT_CONTAINER( bracketBlockStateStack, new vector<bool> );
+ bracketBlockStateStack->push_back(true);
+
+ INIT_CONTAINER( inStatementIndentStack, new vector<int> );
+ INIT_CONTAINER( inStatementIndentStackSizeStack, new vector<int> );
+ inStatementIndentStackSizeStack->push_back(0);
+ INIT_CONTAINER( parenIndentStack, new vector<int> );
+
+ immediatelyPreviousAssignmentOp = NULL;
+ previousLastLineHeader = NULL;
+
+ isInQuote = false;
+ isInComment = false;
+ isInStatement = false;
+ isInCase = false;
+ isInQuestion = false;
+ isInClassHeader = false;
+ isInClassHeaderTab = false;
+ isInHeader = false;
+ isInOperator = false;
+ isInTemplate = false;
+ isInConst = false;
+ isInConditional = false;
+ templateDepth = 0;
+ parenDepth=0;
+ blockTabCount = 0;
+ leadingWhiteSpaces = 0;
+ prevNonSpaceCh = '{';
+ currentNonSpaceCh = '{';
+ prevNonLegalCh = '{';
+ currentNonLegalCh = '{';
+ prevFinalLineSpaceTabCount = 0;
+ prevFinalLineTabCount = 0;
+ probationHeader = NULL;
+ backslashEndsPrevLine = false;
+ isInDefine = false;
+ isInDefineDefinition = false;
+ defineTabCount = 0;
+ }
+
+ /**
+ * set indentation style to ANSI C/C++.
+ */
+ void ASBeautifier::setCStyle()
+ {
+ isCStyle = true;
+ }
+
+ /**
+ * set indentation style to Java / K&R.
+ */
+ void ASBeautifier::setJavaStyle()
+ {
+ isCStyle = false;
+ }
+
+ /**
+ * indent using one tab per indentation
+ */
+ void ASBeautifier::setTabIndentation(int length, bool forceTabs)
+ {
+ indentString = "\t";
+ indentLength = length;
+ shouldForceTabIndentation = forceTabs;
+
+ if (!isMinimalConditinalIndentSet)
+ minConditionalIndent = indentLength * 2;
+ }
+
+ /**
+
+ * indent using a number of spaces per indentation.
+ *
+ * @param length number of spaces per indent.
+ */
+ void ASBeautifier::setSpaceIndentation(int length)
+ {
+ indentString=string(length, ' ');
+ indentLength = length;
+
+ if (!isMinimalConditinalIndentSet)
+ minConditionalIndent = indentLength * 2;
+ }
+
+ /**
+ * set the maximum indentation between two lines in a multi-line statement.
+ *
+ * @param max maximum indentation length.
+ */
+ void ASBeautifier::setMaxInStatementIndentLength(int max)
+ {
+ maxInStatementIndent = max;
+ }
+
+ /**
+ * set the minimum indentation between two lines in a multi-line condition.
+ *
+ * @param min minimal indentation length.
+ */
+ void ASBeautifier::setMinConditionalIndentLength(int min)
+ {
+ minConditionalIndent = min;
+ isMinimalConditinalIndentSet = true;
+ }
+
+ /**
+ * set the state of the bracket indentation option. If true, brackets will
+ * be indented one additional indent.
+ *
+ * @param state state of option.
+ */
+ void ASBeautifier::setBracketIndent(bool state)
+ {
+ bracketIndent = state;
+ }
+
+ /**
+ * set the state of the block indentation option. If true, entire blocks
+ * will be indented one additional indent, similar to the GNU indent style.
+ *
+ * @param state state of option.
+ */
+ void ASBeautifier::setBlockIndent(bool state)
+ {
+ if (state)
+ setBracketIndent(false); // so that we don't have both bracket and block indent
+ blockIndent = state;
+ }
+
+ /**
+ * set the state of the class indentation option. If true, C++ class
+ * definitions will be indented one additional indent.
+ *
+ * @param state state of option.
+ */
+ void ASBeautifier::setClassIndent(bool state)
+ {
+ classIndent = state;
+ }
+
+ /**
+ * set the state of the switch indentation option. If true, blocks of 'switch'
+ * statements will be indented one additional indent.
+ *
+ * @param state state of option.
+ */
+ void ASBeautifier::setSwitchIndent(bool state)
+ {
+ switchIndent = state;
+ }
+
+ /**
+ * set the state of the case indentation option. If true, lines of 'case'
+ * statements will be indented one additional indent.
+ *
+ * @param state state of option.
+ */
+ void ASBeautifier::setCaseIndent(bool state)
+ {
+ caseIndent = state;
+ }
+ /**
+ * set the state of the namespace indentation option.
+ * If true, blocks of 'namespace' statements will be indented one
+ * additional indent. Otherwise, NO indentation will be added.
+ *
+ * @param state state of option.
+ */
+ void ASBeautifier::setNamespaceIndent(bool state)
+ {
+ namespaceIndent = state;
+ }
+
+ /**
+ * set the state of the label indentation option.
+ * If true, labels will be indented one indent LESS than the
+ * current indentation level.
+ * If false, labels will be flushed to the left with NO
+ * indent at all.
+ *
+ * @param state state of option.
+ */
+ void ASBeautifier::setLabelIndent(bool state)
+ {
+ labelIndent = state;
+ }
+
+ /**
+ * set the state of the preprocessor indentation option.
+ * If true, multiline #define statements will be indented.
+ *
+ * @param state state of option.
+ */
+ void ASBeautifier::setPreprocessorIndent(bool state)
+ {
+ preprocessorIndent = state;
+ }
+
+ /**
+ * set the state of the empty line fill option.
+ * If true, empty lines will be filled with the whitespace.
+ * of their previous lines.
+ * If false, these lines will remain empty.
+ *
+ * @param state state of option.
+ */
+ void ASBeautifier::setEmptyLineFill(bool state)
+ {
+ emptyLineFill = state;
+ }
+
+ /**
+ * check if there are any indented lines ready to be read by nextLine()
+ *
+ * @return are there any indented lines ready?
+ */
+ bool ASBeautifier::hasMoreLines() const
+ {
+ return sourceIterator->hasMoreLines();
+ }
+
+ /**
+ * get the next indented line.
+ *
+ * @return indented line.
+ */
+ string ASBeautifier::nextLine()
+ {
+ return beautify(sourceIterator->nextLine());
+ }
+
+ /**
+ * beautify a line of source code.
+ * every line of source code in a source code file should be sent
+ * one after the other to the beautify method.
+ *
+ * @return the indented line.
+ * @param originalLine the original unindented line.
+ */
+ string ASBeautifier::beautify(const string &originalLine)
+ {
+ string line;
+ bool isInLineComment = false;
+ bool lineStartsInComment = false;
+ bool isInClass = false;
+ bool isInSwitch = false;
+ bool isImmediatelyAfterConst = false;
+ bool isSpecialChar = false;
+
+ char ch = ' ';
+ char prevCh;
+ string outBuffer; // the newly idented line is bufferd here
+ int tabCount = 0;
+ const string *lastLineHeader = NULL;
+ bool closingBracketReached = false;
+ int spaceTabCount = 0;
+ char tempCh;
+ unsigned int headerStackSize = headerStack->size();
+ //bool isLineInStatement = isInStatement;
+ bool shouldIndentBrackettedLine = true;
+ int lineOpeningBlocksNum = 0;
+ int lineClosingBlocksNum = 0;
+ bool previousLineProbation = (probationHeader != NULL);
+ unsigned int i;
+
+ currentHeader = NULL;
+
+ lineStartsInComment = isInComment;
+
+ // handle and remove white spaces around the line:
+ // If not in comment, first find out size of white space before line,
+ // so that possible comments starting in the line continue in
+ // relation to the preliminary white-space.
+ if (!isInComment)
+ {
+ leadingWhiteSpaces = 0;
+ while (leadingWhiteSpaces<originalLine.length() && originalLine[leadingWhiteSpaces] <= 0x20)
+ leadingWhiteSpaces++;
+
+ line = trim(originalLine);
+ }
+ else
+ {
+ unsigned int trimSize;
+ for (trimSize=0;
+ trimSize < originalLine.length() && trimSize<leadingWhiteSpaces && originalLine[trimSize] <= 0x20 ;
+ trimSize++)
+ ;
+ line = originalLine.substr(trimSize);
+ }
+
+
+ if (line.length() == 0)
+ {
+ if (emptyLineFill)
+ return preLineWS(prevFinalLineSpaceTabCount, prevFinalLineTabCount);
+ else
+ return line;
+ }
+
+ // handle preprocessor commands
+
+ if (isCStyle && !isInComment && (line[0] == '#' || backslashEndsPrevLine))
+ {
+ if (line[0] == '#')
+ {
+ string preproc = trim(string(line.c_str() + 1));
+
+
+ // When finding a multi-lined #define statement, the original beautifier
+ // 1. sets its isInDefineDefinition flag
+ // 2. clones a new beautifier that will be used for the actual indentation
+ // of the #define. This clone is put into the activeBeautifierStack in order
+ // to be called for the actual indentation.
+ // The original beautifier will have isInDefineDefinition = true, isInDefine = false
+ // The cloned beautifier will have isInDefineDefinition = true, isInDefine = true
+ if (preprocessorIndent && preproc.COMPARE(0, 6, string("define")) == 0 && line[line.length() - 1] == '\\')
+ {
+ if (!isInDefineDefinition)
+ {
+ ASBeautifier *defineBeautifier;
+
+ // this is the original beautifier
+ isInDefineDefinition = true;
+
+ // push a new beautifier into the active stack
+ // this breautifier will be used for the indentation of this define
+ defineBeautifier = new ASBeautifier(*this);
+ //defineBeautifier->init();
+ //defineBeautifier->isInDefineDefinition = true;
+ //defineBeautifier->beautify("");
+ activeBeautifierStack->push_back(defineBeautifier);
+ }
+ else
+ {
+ // the is the cloned beautifier that is in charge of indenting the #define.
+ isInDefine = true;
+ }
+ }
+ else if (preproc.COMPARE(0, 2, string("if")) == 0)
+ {
+ // push a new beautifier into the stack
+ waitingBeautifierStackLengthStack->push_back(waitingBeautifierStack->size());
+ activeBeautifierStackLengthStack->push_back(activeBeautifierStack->size());
+ waitingBeautifierStack->push_back(new ASBeautifier(*this));
+ }
+ else if (preproc.COMPARE(0, 4/*2*/, string("else")) == 0)
+ {
+ if (!waitingBeautifierStack->empty())
+ {
+ // MOVE current waiting beautifier to active stack.
+ activeBeautifierStack->push_back(waitingBeautifierStack->back());
+ waitingBeautifierStack->pop_back();
+ }
+ }
+ else if (preproc.COMPARE(0, 4, string("elif")) == 0)
+ {
+ if (!waitingBeautifierStack->empty())
+ {
+ // append a COPY current waiting beautifier to active stack, WITHOUT deleting the original.
+ activeBeautifierStack->push_back( new ASBeautifier( *(waitingBeautifierStack->back()) ) );
+ }
+ }
+ else if (preproc.COMPARE(0, 5, string("endif")) == 0)
+ {
+ unsigned int stackLength;
+ ASBeautifier *beautifier;
+
+ if (!waitingBeautifierStackLengthStack->empty())
+ {
+ stackLength = waitingBeautifierStackLengthStack->back();
+ waitingBeautifierStackLengthStack->pop_back();
+ while (waitingBeautifierStack->size() > stackLength)
+ {
+ beautifier = waitingBeautifierStack->back();
+ waitingBeautifierStack->pop_back();
+ delete beautifier;
+ }
+ }
+
+ if (!activeBeautifierStackLengthStack->empty())
+ {
+ stackLength = activeBeautifierStackLengthStack->back();
+ activeBeautifierStackLengthStack->pop_back();
+ while (activeBeautifierStack->size() > stackLength)
+ {
+ beautifier = activeBeautifierStack->back();
+ activeBeautifierStack->pop_back();
+ delete beautifier;
+ }
+ }
+
+
+ }
+ }
+
+ // check if the last char is a backslash
+ if(line.length() > 0)
+ backslashEndsPrevLine = (line[line.length() - 1] == '\\');
+ else
+ backslashEndsPrevLine = false;
+
+ // check if this line ends a multi-line #define
+ // if so, use the #define's cloned beautifier for the line's indentation
+ // and then remove it from the active beautifier stack and delete it.
+ if (!backslashEndsPrevLine && isInDefineDefinition && !isInDefine)
+ {
+ string beautifiedLine;
+ ASBeautifier *defineBeautifier;
+
+ isInDefineDefinition = false;
+ defineBeautifier = activeBeautifierStack->back();
+ activeBeautifierStack->pop_back();
+
+ beautifiedLine = defineBeautifier->beautify(line);
+ delete defineBeautifier;
+ return beautifiedLine;
+ }
+
+ // unless this is a multi-line #define, return this precompiler line as is.
+ if (!isInDefine && !isInDefineDefinition)
+ return originalLine;
+ }
+
+ // if there exists any worker beautifier in the activeBeautifierStack,
+ // then use it instead of me to indent the current line.
+ if (!isInDefine && activeBeautifierStack != NULL && !activeBeautifierStack->empty())
+ {
+ return activeBeautifierStack->back()->beautify(line);
+ }
+
+ // calculate preliminary indentation based on data from past lines
+ if (!inStatementIndentStack->empty())
+ spaceTabCount = inStatementIndentStack->back();
+
+
+ for (i=0; i<headerStackSize; i++)
+ {
+ isInClass = false;
+
+ if (blockIndent || (!(i>0 && (*headerStack)[i-1] != &AS_OPEN_BRACKET
+ && (*headerStack)[i] == &AS_OPEN_BRACKET)))
+ ++tabCount;
+
+ if (isCStyle && !namespaceIndent && i >= 1
+ && (*headerStack)[i-1] == &AS_NAMESPACE
+ && (*headerStack)[i] == &AS_OPEN_BRACKET)
+ --tabCount;
+
+ if (isCStyle && i >= 1
+ && (*headerStack)[i-1] == &AS_CLASS
+ && (*headerStack)[i] == &AS_OPEN_BRACKET )
+ {
+ if (classIndent)
+ ++tabCount;
+ isInClass = true;
+ }
+
+ // is the switchIndent option is on, indent switch statements an additional indent.
+ else if (switchIndent && i > 1 &&
+ (*headerStack)[i-1] == &AS_SWITCH &&
+ (*headerStack)[i] == &AS_OPEN_BRACKET
+ )
+ {
+ ++tabCount;
+ isInSwitch = true;
+ }
+
+ }
+
+ if (!lineStartsInComment
+ && isCStyle
+ && isInClass
+ && classIndent
+ && headerStackSize >= 2
+ &&(*headerStack)[headerStackSize-2] == &AS_CLASS
+ && (*headerStack)[headerStackSize-1] == &AS_OPEN_BRACKET
+ && line[0] == '}')
+ --tabCount;
+
+ else if (!lineStartsInComment
+ && isInSwitch
+ && switchIndent
+ && headerStackSize >= 2
+ && (*headerStack)[headerStackSize-2] == &AS_SWITCH
+ && (*headerStack)[headerStackSize-1] == &AS_OPEN_BRACKET
+ && line[0] == '}')
+ --tabCount;
+
+ if (isInClassHeader)
+ {
+ isInClassHeaderTab = true;
+ tabCount += 2;
+ }
+
+ if (isInConditional)
+ {
+ --tabCount;
+ }
+
+
+ // parse characters in the current line.
+
+ for (i=0; i<line.length(); i++)
+ {
+ tempCh = line[i];
+
+ prevCh = ch;
+ ch = tempCh;
+
+ outBuffer.append(1, ch);
+
+ if (isWhiteSpace(ch))
+ continue;
+
+
+ // handle special characters (i.e. backslash+character such as \n, \t, ...)
+ if (isSpecialChar)
+ {
+ isSpecialChar = false;
+ continue;
+ }
+ if (!(isInComment || isInLineComment) && line.COMPARE(i, 2, string("\\\\")) == 0)
+ {
+ outBuffer.append(1, '\\');
+ i++;
+ continue;
+ }
+ if (!(isInComment || isInLineComment) && ch=='\\')
+ {
+ isSpecialChar = true;
+ continue;
+ }
+
+ // handle quotes (such as 'x' and "Hello Dolly")
+ if (!(isInComment || isInLineComment) && (ch=='"' || ch=='\''))
+ if (!isInQuote)
+ {
+ quoteChar = ch;
+ isInQuote = true;
+ }
+ else if (quoteChar == ch)
+ {
+ isInQuote = false;
+ isInStatement = true;
+ continue;
+ }
+ if (isInQuote)
+ continue;
+
+ // handle comments
+
+ if ( !(isInComment || isInLineComment) && line.COMPARE(i, 2, AS_OPEN_LINE_COMMENT) == 0 )
+ {
+ isInLineComment = true;
+ outBuffer.append(1, '/');
+ i++;
+ continue;
+ }
+ else if ( !(isInComment || isInLineComment) && line.COMPARE(i, 2, AS_OPEN_COMMENT) == 0 )
+ {
+ isInComment = true;
+ outBuffer.append(1, '*');
+ i++;
+ continue;
+ }
+ else if ( (isInComment || isInLineComment) && line.COMPARE(i, 2, AS_CLOSE_COMMENT) == 0 )
+ {
+ isInComment = false;
+ outBuffer.append(1, '/');
+ i++;
+ continue;
+ }
+
+ if (isInComment||isInLineComment)
+ continue;
+
+ // if we have reached this far then we are NOT in a comment or string of special character...
+
+ if (probationHeader != NULL)
+ {
+ if ( ((probationHeader == &AS_STATIC || probationHeader == &AS_CONST) && ch == '{')
+ || (probationHeader == &AS_SYNCHRONIZED && ch == '('))
+ {
+ // insert the probation header as a new header
+ isInHeader = true;
+ headerStack->push_back(probationHeader);
+
+ // handle the specific probation header
+ isInConditional = (probationHeader == &AS_SYNCHRONIZED);
+ if (probationHeader == &AS_CONST)
+ isImmediatelyAfterConst = true;
+ // isInConst = true;
+ /* TODO:
+ * There is actually no more need for the global isInConst variable.
+ * The only reason for checking const is to see if there is a const
+ * immediately before an open-bracket.
+ * Since CONST is now put into probation and is checked during itspost-char,
+ * isImmediatelyAfterConst can be set by its own...
+ */
+
+ isInStatement = false;
+ // if the probation comes from the previous line, then indent by 1 tab count.
+ if (previousLineProbation && ch == '{')
+ tabCount++;
+ previousLineProbation = false;
+ }
+
+ // dismiss the probation header
+ probationHeader = NULL;
+ }
+
+ prevNonSpaceCh = currentNonSpaceCh;
+ currentNonSpaceCh = ch;
+ if (!isLegalNameChar(ch) && ch != ',' && ch != ';' )
+ {
+ prevNonLegalCh = currentNonLegalCh;
+ currentNonLegalCh = ch;
+ }
+
+ //if (isInConst)
+ //{
+ // isInConst = false;
+ // isImmediatelyAfterConst = true;
+ //}
+
+ if (isInHeader)
+ {
+ isInHeader = false;
+ currentHeader = headerStack->back();
+ }
+ else
+ currentHeader = NULL;
+
+ if (isCStyle && isInTemplate
+ && (ch == '<' || ch == '>')
+ && findHeader(line, i, nonAssignmentOperators) == NULL) //;
+ {
+ if (ch == '<')
+ {
+ ++templateDepth;
+ }
+ else if (ch == '>')
+ {
+ if (--templateDepth <= 0)
+ {
+ if (isInTemplate)
+ ch = ';';
+ else
+ ch = 't';
+ isInTemplate = false;
+ templateDepth = 0;
+ }
+
+ }
+ }
+
+ // handle parenthesies
+ if (ch == '(' || ch == '[' || ch == ')' || ch == ']')
+ {
+ if (ch == '(' || ch == '[')
+ {
+ if (parenDepth == 0)
+ {
+ parenStatementStack->push_back(isInStatement);
+ isInStatement = true;
+ }
+ parenDepth++;
+
+ inStatementIndentStackSizeStack->push_back(inStatementIndentStack->size());
+
+ if (currentHeader != NULL)
+ registerInStatementIndent(line, i, spaceTabCount, minConditionalIndent/*indentLength*2*/, true);
+ else
+ registerInStatementIndent(line, i, spaceTabCount, 0, true);
+ }
+ else if (ch == ')' || ch == ']')
+ {
+ parenDepth--;
+ if (parenDepth == 0)
+ {
+ isInStatement = parenStatementStack->back();
+ parenStatementStack->pop_back();
+ ch = ' ';
+
+ isInConditional = false;
+ }
+
+ if (!inStatementIndentStackSizeStack->empty())
+ {
+ unsigned int previousIndentStackSize = inStatementIndentStackSizeStack->back();
+ inStatementIndentStackSizeStack->pop_back();
+ while (previousIndentStackSize < inStatementIndentStack->size())
+ inStatementIndentStack->pop_back();
+
+ if (!parenIndentStack->empty())
+ {
+ int poppedIndent = parenIndentStack->back();
+ parenIndentStack->pop_back();
+
+ if (i == 0)
+ spaceTabCount = poppedIndent;
+ }
+ }
+ }
+
+ continue;
+ }
+
+
+ if (ch == '{')
+ {
+ bool isBlockOpener = false;
+
+ // first, check if '{' is a block-opener or an static-array opener
+ isBlockOpener = ( (prevNonSpaceCh == '{' && bracketBlockStateStack->back())
+ || prevNonSpaceCh == '}'
+ || prevNonSpaceCh == ')'
+ || prevNonSpaceCh == ';'
+ || isInClassHeader
+ || isBlockOpener
+ || isImmediatelyAfterConst
+ || (isInDefine &&
+ (prevNonSpaceCh == '('
+ || prevNonSpaceCh == '_'
+ || isalnum(prevNonSpaceCh))) );
+
+ isInClassHeader = false;
+ if (!isBlockOpener && currentHeader != NULL)
+ {
+ for (unsigned int n=0; n < nonParenHeaders.size(); n++)
+ if (currentHeader == nonParenHeaders[n])
+ {
+ isBlockOpener = true;
+ break;
+ }
+ }
+ bracketBlockStateStack->push_back(isBlockOpener);
+ if (!isBlockOpener)
+ {
+ inStatementIndentStackSizeStack->push_back(inStatementIndentStack->size());
+ registerInStatementIndent(line, i, spaceTabCount, 0, true);
+ parenDepth++;
+ if (i == 0)
+ shouldIndentBrackettedLine = false;
+
+ continue;
+ }
+
+ // this bracket is a block opener...
+
+ ++lineOpeningBlocksNum;
+
+ if (isInClassHeader)
+ isInClassHeader = false;
+ if (isInClassHeaderTab)
+ {
+ isInClassHeaderTab = false;
+ tabCount -= 2;
+ }
+
+ blockParenDepthStack->push_back(parenDepth);
+ blockStatementStack->push_back(isInStatement);
+
+ inStatementIndentStackSizeStack->push_back(inStatementIndentStack->size());
+
+ blockTabCount += isInStatement? 1 : 0;
+ parenDepth = 0;
+ isInStatement = false;
+
+ tempStacks->push_back(new vector<const string*>);
+ headerStack->push_back(&AS_OPEN_BRACKET);
+ lastLineHeader = &AS_OPEN_BRACKET; // <------
+
+ continue;
+ }
+
+ //check if a header has been reached
+ if (prevCh == ' ')
+ {
+ bool isIndentableHeader = true;
+ const string *newHeader = findHeader(line, i, headers);
+ if (newHeader != NULL)
+ {
+ // if we reached here, then this is a header...
+ isInHeader = true;
+
+ vector<const string*> *lastTempStack;
+ if (tempStacks->empty())
+ lastTempStack = NULL;
+ else
+ lastTempStack = tempStacks->back();
+
+ // if a new block is opened, push a new stack into tempStacks to hold the
+ // future list of headers in the new block.
+
+ // take care of the special case: 'else if (...)'
+ if (newHeader == &AS_IF && lastLineHeader == &AS_ELSE)
+ {
+ //spaceTabCount += indentLength; // to counter the opposite addition that occurs when the 'if' is registered below...
+ headerStack->pop_back();
+ }
+
+ // take care of 'else'
+ else if (newHeader == &AS_ELSE)
+ {
+ if (lastTempStack != NULL)
+ {
+ int indexOfIf = indexOf(*lastTempStack, &AS_IF); // <---
+ if (indexOfIf != -1)
+ {
+ // recreate the header list in headerStack up to the previous 'if'
+ // from the temporary snapshot stored in lastTempStack.
+ int restackSize = lastTempStack->size() - indexOfIf - 1;
+ for (int r=0; r<restackSize; r++)
+ {
+ headerStack->push_back(lastTempStack->back());
+ lastTempStack->pop_back();
+ }
+ if (!closingBracketReached)
+ tabCount += restackSize;
+ }
+ /*
+ * If the above if is not true, i.e. no 'if' before the 'else',
+ * then nothing beautiful will come out of this...
+ * I should think about inserting an Exception here to notify the caller of this...
+ */
+ }
+ }
+
+ // check if 'while' closes a previous 'do'
+ else if (newHeader == &AS_WHILE)
+ {
+ if (lastTempStack != NULL)
+ {
+ int indexOfDo = indexOf(*lastTempStack, &AS_DO); // <---
+ if (indexOfDo != -1)
+ {
+ // recreate the header list in headerStack up to the previous 'do'
+ // from the temporary snapshot stored in lastTempStack.
+ int restackSize = lastTempStack->size() - indexOfDo - 1;
+ for (int r=0; r<restackSize; r++)
+ {
+ headerStack->push_back(lastTempStack->back());
+ lastTempStack->pop_back();
+ }
+ if (!closingBracketReached)
+ tabCount += restackSize;
+ }
+ }
+ }
+ // check if 'catch' closes a previous 'try' or 'catch'
+ else if (newHeader == &AS_CATCH || newHeader == &AS_FINALLY)
+ {
+ if (lastTempStack != NULL)
+ {
+ int indexOfTry = indexOf(*lastTempStack, &AS_TRY);
+ if (indexOfTry == -1)
+ indexOfTry = indexOf(*lastTempStack, &AS_CATCH);
+ if (indexOfTry != -1)
+ {
+ // recreate the header list in headerStack up to the previous 'try'
+ // from the temporary snapshot stored in lastTempStack.
+ int restackSize = lastTempStack->size() - indexOfTry - 1;
+ for (int r=0; r<restackSize; r++)
+ {
+ headerStack->push_back(lastTempStack->back());
+ lastTempStack->pop_back();
+ }
+
+ if (!closingBracketReached)
+ tabCount += restackSize;
+ }
+ }
+ }
+ else if (newHeader == &AS_CASE)
+ {
+ isInCase = true;
+ if (!caseIndent)
+ --tabCount;
+ }
+ else if(newHeader == &AS_DEFAULT)
+ {
+ isInCase = true;
+ if (!caseIndent)
+ --tabCount;
+ }
+ else if (newHeader == &AS_PUBLIC || newHeader == &AS_PROTECTED || newHeader == &AS_PRIVATE)
+ {
+ if (isCStyle && !isInClassHeader)
+ --tabCount;
+ isIndentableHeader = false;
+ }
+ //else if ((newHeader == &STATIC || newHeader == &SYNCHRONIZED) &&
+ // !headerStack->empty() &&
+ // (headerStack->back() == &STATIC || headerStack->back() == &SYNCHRONIZED))
+ //{
+ // isIndentableHeader = false;
+ //}
+ else if (newHeader == &AS_STATIC
+ || newHeader == &AS_SYNCHRONIZED
+ || (newHeader == &AS_CONST && isCStyle))
+ {
+ if (!headerStack->empty() &&
+ (headerStack->back() == &AS_STATIC
+ || headerStack->back() == &AS_SYNCHRONIZED
+ || headerStack->back() == &AS_CONST))
+ {
+ isIndentableHeader = false;
+ }
+ else
+ {
+ isIndentableHeader = false;
+ probationHeader = newHeader;
+ }
+ }
+ else if (newHeader == &AS_CONST)
+ {
+ // this will be entered only if NOT in C style
+ // since otherwise the CONST would be found to be a probstion header...
+
+ //if (isCStyle)
+ // isInConst = true;
+ isIndentableHeader = false;
+ }
+ /*
+ else if (newHeader == &OPERATOR)
+ {
+ if (isCStyle)
+ isInOperator = true;
+ isIndentableHeader = false;
+ }
+ */
+ else if (newHeader == &AS_TEMPLATE)
+ {
+ if (isCStyle)
+ isInTemplate = true;
+ isIndentableHeader = false;
+ }
+
+
+ if (isIndentableHeader)
+ {
+ // 3.2.99
+ //spaceTabCount-=indentLength;
+ headerStack->push_back(newHeader);
+ isInStatement = false;
+ if (indexOf(nonParenHeaders, newHeader) == -1)
+ {
+ isInConditional = true;
+ }
+ lastLineHeader = newHeader;
+ }
+ else
+ isInHeader = false;
+
+ //lastLineHeader = newHeader;
+
+ outBuffer.append(newHeader->substr(1));
+ i += newHeader->length() - 1;
+
+ continue;
+ }
+ }
+
+ if (isCStyle && !isalpha(prevCh)
+ && line.COMPARE(i, 8, AS_OPERATOR) == 0 && !isalnum(line[i+8]))
+ {
+ isInOperator = true;
+ outBuffer.append(AS_OPERATOR.substr(1));
+ i += 7;
+ continue;
+ }
+
+ if (ch == '?')
+ isInQuestion = true;
+
+
+ // special handling of 'case' statements
+ if (ch == ':')
+ {
+ if (line.length() > i+1 && line[i+1] == ':') // look for ::
+ {
+ ++i;
+ outBuffer.append(1, ':');
+ ch = ' ';
+ continue;
+ }
+
+ else if (isCStyle && isInClass && prevNonSpaceCh != ')')
+ {
+ // BEGIN Content of ASBeautifier.cpp.BITFIELD.patch:
+
+ unsigned int chIndex;
+ char nextCh = 0;
+ for (chIndex = i+1; chIndex < line.length(); chIndex++)
+ if (!isWhiteSpace(line[chIndex]))
+ break;
+ if (chIndex< line.length())
+ nextCh = line[chIndex];
+ int nWord =0;
+ for (chIndex = 0; chIndex < i; chIndex++)
+ {
+ if (!isWhiteSpace(line[chIndex]))
+ {
+ nWord ++;
+ while (!isWhiteSpace(line[++chIndex]));
+ }
+ }
+ if ((nextCh >= '0' && nextCh <= '9') || (nWord >1))
+ continue;
+ // END Content of ASBeautifier.cpp.BITFIELD.patch:
+
+ --tabCount;
+ // found a 'private:' or 'public:' inside a class definition
+ // so do nothing special
+ }
+
+ else if (isCStyle && isInClassHeader)
+ {
+
+ // found a 'class A : public B' definition
+ // so do nothing special
+ }
+
+ else if (isInQuestion)
+ {
+ isInQuestion = false;
+ }
+ else if (isCStyle && prevNonSpaceCh == ')')
+ {
+ isInClassHeader = true;
+ if (i==0)
+ tabCount += 2;
+ }
+ else
+ {
+ currentNonSpaceCh = ';'; // so that brackets after the ':' will appear as block-openers
+ if (isInCase)
+ {
+ isInCase = false;
+ ch = ';'; // from here on, treat char as ';'
+ }
+ // BEGIN content of ASBeautifier.cpp.BITFIELD.patch.bz2
+ else // bitfield or labels
+ {
+ unsigned int chIndex;
+ char nextCh = 0;
+ for (chIndex = i+1; (isCStyle && chIndex < line.length()); chIndex++)
+ if (!isWhiteSpace(line[chIndex]))
+ break;
+ if (chIndex< line.length())
+ nextCh = line[chIndex];
+
+ int nWord =0;
+ for (chIndex = 0; chIndex < i; chIndex++)
+ {
+ if (!isWhiteSpace(line[chIndex]))
+ {
+ nWord ++;
+ while (!isWhiteSpace(line[++chIndex]));
+ }
+ }
+ if (isCStyle && (nextCh >= '0' && nextCh <= '9') || (nWord >1))
+ {
+ continue;
+ }
+ // END content of ASASBeautifier.cpp.BITFIELD.patch.bz2
+
+ else // is in a label (e.g. 'label1:')
+ {
+ if (labelIndent)
+ --tabCount; // unindent label by one indent
+ else
+ tabCount = 0; // completely flush indent to left
+ }
+
+ // BEGIN content of ASASBeautifier.cpp.BITFIELD.patch.bz2
+ }
+ // END content of ASASBeautifier.cpp.BITFIELD.patch.bz2
+
+ }
+ }
+
+ if ((ch == ';' || (parenDepth>0 && ch == ',')) && !inStatementIndentStackSizeStack->empty())
+ while ((unsigned int)inStatementIndentStackSizeStack->back() + (parenDepth>0 ? 1 : 0) < inStatementIndentStack->size())
+ inStatementIndentStack->pop_back();
+
+
+ // handle ends of statements
+ if ( (ch == ';' && parenDepth == 0) || ch == '}'/* || (ch == ',' && parenDepth == 0)*/)
+ {
+ if (ch == '}')
+ {
+ // first check if this '}' closes a previous block, or a static array...
+ if (!bracketBlockStateStack->empty())
+ {
+ bool bracketBlockState = bracketBlockStateStack->back();
+ bracketBlockStateStack->pop_back();
+ if (!bracketBlockState)
+ {
+ if (!inStatementIndentStackSizeStack->empty())
+ {
+ // this bracket is a static array
+
+ unsigned int previousIndentStackSize = inStatementIndentStackSizeStack->back();
+ inStatementIndentStackSizeStack->pop_back();
+ while (previousIndentStackSize < inStatementIndentStack->size())
+ inStatementIndentStack->pop_back();
+ parenDepth--;
+ if (i == 0)
+ shouldIndentBrackettedLine = false;
+
+ if (!parenIndentStack->empty())
+ {
+ int poppedIndent = parenIndentStack->back();
+ parenIndentStack->pop_back();
+ if (i == 0)
+ spaceTabCount = poppedIndent;
+ }
+ }
+ continue;
+ }
+ }
+
+ // this bracket is block closer...
+
+ ++lineClosingBlocksNum;
+
+ if(!inStatementIndentStackSizeStack->empty())
+ inStatementIndentStackSizeStack->pop_back();
+
+ if (!blockParenDepthStack->empty())
+ {
+ parenDepth = blockParenDepthStack->back();
+ blockParenDepthStack->pop_back();
+ isInStatement = blockStatementStack->back();
+ blockStatementStack->pop_back();
+
+ if (isInStatement)
+ blockTabCount--;
+ }
+
+ closingBracketReached = true;
+ int headerPlace = indexOf(*headerStack, &AS_OPEN_BRACKET); // <---
+ if (headerPlace != -1)
+ {
+ const string *popped = headerStack->back();
+ while (popped != &AS_OPEN_BRACKET)
+ {
+ headerStack->pop_back();
+ popped = headerStack->back();
+ }
+ headerStack->pop_back();
+
+ if (!tempStacks->empty())
+ {
+ vector<const string*> *temp = tempStacks->back();
+ tempStacks->pop_back();
+ delete temp;
+ }
+ }
+
+
+ ch = ' '; // needed due to cases such as '}else{', so that headers ('else' tn tih case) will be identified...
+ }
+
+ /*
+ * Create a temporary snapshot of the current block's header-list in the
+ * uppermost inner stack in tempStacks, and clear the headerStack up to
+ * the begining of the block.
+ * Thus, the next future statement will think it comes one indent past
+ * the block's '{' unless it specifically checks for a companion-header
+ * (such as a previous 'if' for an 'else' header) within the tempStacks,
+ * and recreates the temporary snapshot by manipulating the tempStacks.
+ */
+ if (!tempStacks->back()->empty())
+ while (!tempStacks->back()->empty())
+ tempStacks->back()->pop_back();
+ while (!headerStack->empty() && headerStack->back() != &AS_OPEN_BRACKET)
+ {
+ tempStacks->back()->push_back(headerStack->back());
+ headerStack->pop_back();
+ }
+
+ if (parenDepth == 0 && ch == ';')
+ isInStatement=false;
+
+ isInClassHeader = false;
+
+ continue;
+ }
+
+
+ // check for preBlockStatements ONLY if not within parenthesies
+ // (otherwise 'struct XXX' statements would be wrongly interpreted...)
+ if (prevCh == ' ' && !isInTemplate && parenDepth == 0)
+ {
+ const string *newHeader = findHeader(line, i, preBlockStatements);
+ if (newHeader != NULL)
+ {
+ isInClassHeader = true;
+ outBuffer.append(newHeader->substr(1));
+ i += newHeader->length() - 1;
+ //if (isCStyle)
+ headerStack->push_back(newHeader);
+ }
+ }
+
+ // Handle operators
+ //
+
+ //// // PRECHECK if a '==' or '--' or '++' operator was reached.
+ //// // If not, then register an indent IF an assignment operator was reached.
+ //// // The precheck is important, so that statements such as 'i--==2' are not recognized
+ //// // to have assignment operators (here, '-=') in them . . .
+
+ const string *foundAssignmentOp = NULL;
+ const string *foundNonAssignmentOp = NULL;
+
+ immediatelyPreviousAssignmentOp = NULL;
+
+ // Check if an operator has been reached.
+ foundAssignmentOp = findHeader(line, i, assignmentOperators, false);
+ foundNonAssignmentOp = findHeader(line, i, nonAssignmentOperators, false);
+
+ // Since findHeader's boundry checking was not used above, it is possible
+ // that both an assignment op and a non-assignment op where found,
+ // e.g. '>>' and '>>='. If this is the case, treat the LONGER one as the
+ // found operator.
+ if (foundAssignmentOp != NULL && foundNonAssignmentOp != NULL)
+ if (foundAssignmentOp->length() < foundNonAssignmentOp->length())
+ foundAssignmentOp = NULL;
+ else
+ foundNonAssignmentOp = NULL;
+
+ if (foundNonAssignmentOp != NULL)
+ {
+ if (foundNonAssignmentOp->length() > 1)
+ {
+ outBuffer.append(foundNonAssignmentOp->substr(1));
+ i += foundNonAssignmentOp->length() - 1;
+ }
+ }
+
+ else if (foundAssignmentOp != NULL)
+
+ {
+ if (foundAssignmentOp->length() > 1)
+ {
+ outBuffer.append(foundAssignmentOp->substr(1));
+ i += foundAssignmentOp->length() - 1;
+ }
+
+ if (!isInOperator && !isInTemplate)
+ {
+ registerInStatementIndent(line, i, spaceTabCount, 0, false);
+ immediatelyPreviousAssignmentOp = foundAssignmentOp;
+ isInStatement = true;
+ }
+ }
+
+ /*
+ immediatelyPreviousAssignmentOp = NULL;
+ bool isNonAssingmentOperator = false;
+ for (int n = 0; n < nonAssignmentOperators.size(); n++)
+ if (line.COMPARE(i, nonAssignmentOperators[n]->length(), *(nonAssignmentOperators[n])) == 0)
+ {
+ if (nonAssignmentOperators[n]->length() > 1)
+ {
+ outBuffer.append(nonAssignmentOperators[n]->substr(1));
+ i += nonAssignmentOperators[n]->length() - 1;
+ }
+ isNonAssingmentOperator = true;
+ break;
+ }
+ if (!isNonAssingmentOperator)
+ {
+ for (int a = 0; a < assignmentOperators.size(); a++)
+ if (line.COMPARE(i, assignmentOperators[a]->length(), *(assignmentOperators[a])) == 0)
+ {
+ if (assignmentOperators[a]->length() > 1)
+ {
+ outBuffer.append(assignmentOperators[a]->substr(1));
+ i += assignmentOperators[a]->length() - 1;
+ }
+
+ if (!isInOperator && !isInTemplate)
+ {
+ registerInStatementIndent(line, i, spaceTabCount, 0, false);
+ immediatelyPreviousAssignmentOp = assignmentOperators[a];
+ isInStatement = true;
+ }
+ break;
+ }
+ }
+ */
+
+ if (isInOperator)
+ isInOperator = false;
+ }
+
+ // handle special cases of unindentation:
+
+ /*
+ * if '{' doesn't follow an immediately previous '{' in the headerStack
+ * (but rather another header such as "for" or "if", then unindent it
+ * by one indentation relative to its block.
+ */
+ // cerr << endl << lineOpeningBlocksNum << " " << lineClosingBlocksNum << " " << previousLastLineHeader << endl;
+
+ // indent #define lines with one less tab
+ //if (isInDefine)
+ // tabCount -= defineTabCount-1;
+
+
+ if (!lineStartsInComment
+ && !blockIndent
+ && outBuffer.length()>0
+ && outBuffer[0]=='{'
+ && !(lineOpeningBlocksNum > 0 && lineOpeningBlocksNum == lineClosingBlocksNum)
+ && !(headerStack->size() > 1 && (*headerStack)[headerStack->size()-2] == &AS_OPEN_BRACKET)
+ && shouldIndentBrackettedLine)
+ --tabCount;
+
+ else if (!lineStartsInComment
+ && outBuffer.length()>0
+ && outBuffer[0]=='}'
+ && shouldIndentBrackettedLine )
+ --tabCount;
+
+ // correctly indent one-line-blocks...
+ else if (!lineStartsInComment
+ && outBuffer.length()>0
+ && lineOpeningBlocksNum > 0
+ && lineOpeningBlocksNum == lineClosingBlocksNum
+ && previousLastLineHeader != NULL
+ && previousLastLineHeader != &AS_OPEN_BRACKET)
+ tabCount -= 1; //lineOpeningBlocksNum - (blockIndent ? 1 : 0);
+
+ if (tabCount < 0)
+ tabCount = 0;
+
+ // take care of extra bracket indentatation option...
+ if (bracketIndent && outBuffer.length()>0 && shouldIndentBrackettedLine)
+ if (outBuffer[0]=='{' || outBuffer[0]=='}')
+ tabCount++;
+
+
+ if (isInDefine)
+ {
+ if (outBuffer[0] == '#')
+ {
+ string preproc = trim(string(outBuffer.c_str() + 1));
+ if (preproc.COMPARE(0, 6, string("define")) == 0)
+ {
+ if (!inStatementIndentStack->empty()
+ && inStatementIndentStack->back() > 0)
+ {
+ defineTabCount = tabCount;
+ }
+ else
+ {
+ defineTabCount = tabCount - 1;
+ tabCount--;
+ }
+ }
+ }
+
+ tabCount -= defineTabCount;
+ }
+
+ if (tabCount < 0)
+ tabCount = 0;
+
+
+ // finally, insert indentations into begining of line
+
+ prevFinalLineSpaceTabCount = spaceTabCount;
+ prevFinalLineTabCount = tabCount;
+
+ if (shouldForceTabIndentation)
+ {
+ tabCount += spaceTabCount / indentLength;
+ spaceTabCount = spaceTabCount % indentLength;
+ }
+
+ outBuffer = preLineWS(spaceTabCount,tabCount) + outBuffer;
+
+ if (lastLineHeader != NULL)
+ previousLastLineHeader = lastLineHeader;
+
+ return outBuffer;
+ }
+
+
+ string ASBeautifier::preLineWS(int spaceTabCount, int tabCount)
+ {
+ string ws;
+
+ for (int i=0; i<tabCount; i++)
+ ws += indentString;
+
+ while ((spaceTabCount--) > 0)
+ ws += string(" ");
+
+ return ws;
+
+ }
+
+ /**
+ * register an in-statement indent.
+ */
+ void ASBeautifier::registerInStatementIndent(const string &line, int i, int spaceTabCount,
+ int minIndent, bool updateParenStack)
+ {
+ int inStatementIndent;
+ int remainingCharNum = line.length() - i;
+ int nextNonWSChar = 1;
+
+ nextNonWSChar = getNextProgramCharDistance(line, i);
+
+ // if indent is around the last char in the line, indent instead 2 spaces from the previous indent
+ if (nextNonWSChar == remainingCharNum)
+ {
+ int previousIndent = spaceTabCount;
+ if (!inStatementIndentStack->empty())
+ previousIndent = inStatementIndentStack->back();
+
+ inStatementIndentStack->push_back(/*2*/ indentLength + previousIndent );
+ if (updateParenStack)
+ parenIndentStack->push_back( previousIndent );
+ return;
+ }
+
+ if (updateParenStack)
+ parenIndentStack->push_back(i+spaceTabCount);
+
+ inStatementIndent = i + nextNonWSChar + spaceTabCount;
+
+ if (i + nextNonWSChar < minIndent)
+ inStatementIndent = minIndent + spaceTabCount;
+
+ if (i + nextNonWSChar > maxInStatementIndent)
+ inStatementIndent = indentLength*2 + spaceTabCount;
+
+
+
+ if (!inStatementIndentStack->empty() &&
+ inStatementIndent < inStatementIndentStack->back())
+ inStatementIndent = inStatementIndentStack->back();
+
+ inStatementIndentStack->push_back(inStatementIndent);
+ }
+
+ /**
+ * get distance to the next non-white sspace, non-comment character in the line.
+ * if no such character exists, return the length remaining to the end of the line.
+ */
+ int ASBeautifier::getNextProgramCharDistance(const string &line, int i)
+ {
+ bool inComment = false;
+ int remainingCharNum = line.length() - i;
+ int charDistance = 1;
+ int ch;
+
+ for (charDistance = 1; charDistance < remainingCharNum; charDistance++)
+ {
+ ch = line[i + charDistance];
+ if (inComment)
+ {
+ if (line.COMPARE(i + charDistance, 2, AS_CLOSE_COMMENT) == 0)
+ {
+ charDistance++;
+ inComment = false;
+ }
+ continue;
+ }
+ else if (isWhiteSpace(ch))
+ continue;
+ else if (ch == '/')
+ {
+ if (line.COMPARE(i + charDistance, 2, AS_OPEN_LINE_COMMENT) == 0)
+ return remainingCharNum;
+ else if (line.COMPARE(i + charDistance, 2, AS_OPEN_COMMENT) == 0)
+ {
+ charDistance++;
+ inComment = true;
+ }
+ }
+ else
+ return charDistance;
+ }
+
+ return charDistance;
+ }
+
+
+ /**
+ * check if a specific character can be used in a legal variable/method/class name
+ *
+ * @return legality of the char.
+ * @param ch the character to be checked.
+ */
+ bool ASBeautifier::isLegalNameChar(char ch) const
+ {
+ return (isalnum(ch) //(ch>='a' && ch<='z') || (ch>='A' && ch<='Z') || (ch>='0' && ch<='9') ||
+ || ch=='.' || ch=='_' || (!isCStyle && ch=='$') || (isCStyle && ch=='~'));
+ }
+
+
+ /**
+ * check if a specific line position contains a header, out of several possible headers.
+ *
+ * @return a pointer to the found header. if no header was found then return NULL.
+ */
+ const string *ASBeautifier::findHeader(const string &line, int i, const vector<const string*> &possibleHeaders, bool checkBoundry)
+ {
+ int maxHeaders = possibleHeaders.size();
+ const string *header = NULL;
+ int p;
+
+ for (p=0; p < maxHeaders; p++)
+ {
+ header = possibleHeaders[p];
+
+ if (line.COMPARE(i, header->length(), *header) == 0)
+ {
+ // check that this is a header and not a part of a longer word
+ // (e.g. not at its begining, not at its middle...)
+
+ int lineLength = line.length();
+ int headerEnd = i + header->length();
+ char startCh = (*header)[0]; // first char of header
+ char endCh = 0; // char just after header
+ char prevCh = 0; // char just before header
+
+ if (headerEnd < lineLength)
+ {
+ endCh = line[headerEnd];
+ }
+ if (i > 0)
+ {
+ prevCh = line[i-1];
+ }
+
+ if (!checkBoundry)
+ {
+ return header;
+ }
+ else if (prevCh != 0
+ && isLegalNameChar(startCh)
+ && isLegalNameChar(prevCh))
+ {
+ return NULL;
+ }
+ else if (headerEnd >= lineLength
+ || !isLegalNameChar(startCh)
+ || !isLegalNameChar(endCh))
+ {
+ return header;
+ }
+ else
+ {
+ return NULL;
+ }
+ }
+ }
+
+ return NULL;
+ }
+
+
+ /**
+ * check if a specific character can be used in a legal variable/method/class name
+ *
+ * @return legality of the char.
+ * @param ch the character to be checked.
+ */
+ bool ASBeautifier::isWhiteSpace(char ch) const
+ {
+ return (ch == ' ' || ch == '\t');
+ }
+
+ /**
+ * find the index number of a string element in a container of strings
+ *
+ * @return the index number of element in the ocntainer. -1 if element not found.
+ * @param container a vector of strings.
+ * @param element the element to find .
+ */
+ int ASBeautifier::indexOf(vector<const string*> &container, const string *element)
+ {
+ vector<const string*>::const_iterator where;
+
+ where= find(container.begin(), container.end(), element);
+ if (where == container.end())
+ return -1;
+ else
+ return where - container.begin();
+ }
+
+ /**
+ * trim removes the white space surrounding a line.
+ *
+ * @return the trimmed line.
+ * @param str the line to trim.
+ */
+ string ASBeautifier::trim(const string &str)
+ {
+
+ int start = 0;
+ int end = str.length() - 1;
+
+ while (start < end && isWhiteSpace(str[start]))
+ start++;
+
+ while (start <= end && isWhiteSpace(str[end]))
+ end--;
+
+ string returnStr(str, start, end+1-start);
+ return returnStr;
+ }
+
+#ifdef USES_NAMESPACE
+}
+#endif
+/*
+ * Copyright (c) 1998,1999,2000,2001,2002 Tal Davidson. All rights reserved.
+ *
+ * compiler_defines.h (1 January 1999)
+ * by Tal Davidson (davidsont@bigfoot.com)
+ * This file is a part of "Artistic Style" - an indentater and reformatter
+ * of C, C++, C# and Java source files.
+ *
+ * The "Artistic Style" project, including all files needed to compile it,
+ * is free software; you can redistribute it and/or use it and/or modify it
+ * under the terms of the GNU General Public License as published
+ * by the Free Software Foundation; either version 2 of the License,
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this program.
+ */
+
+
+#ifndef ASBEAUTIFIER_H
+#define ASBEAUTIFIER_H
+
+#include "ASResource.h"
+#include "compiler_defines.h"
+#include "ASSourceIterator.h"
+
+#include <string>
+#include <vector>
+
+
+using namespace std;
+
+namespace astyle
+ {
+
+ enum BracketMode { NONE_MODE, ATTACH_MODE, BREAK_MODE, BDAC_MODE };
+ enum BracketType { NULL_TYPE = 0,
+ DEFINITION_TYPE = 1,
+ COMMAND_TYPE = 2,
+ ARRAY_TYPE = 4,
+ SINGLE_LINE_TYPE = 8};
+
+
+ class ASBeautifier : protected ASResource
+ {
+ public:
+ ASBeautifier();
+ virtual ~ASBeautifier();
+ virtual void init(ASSourceIterator* iter); // pointer to dynamically created iterator.
+ virtual void init();
+ virtual bool hasMoreLines() const;
+ virtual string nextLine();
+ virtual string beautify(const string &line);
+ void setTabIndentation(int length = 4, bool forceTabs = false);
+ void setSpaceIndentation(int length = 4);
+ void setMaxInStatementIndentLength(int max);
+ void setMinConditionalIndentLength(int min);
+ void setClassIndent(bool state);
+ void setSwitchIndent(bool state);
+ void setCaseIndent(bool state);
+ void setBracketIndent(bool state);
+ void setBlockIndent(bool state);
+ void setNamespaceIndent(bool state);
+ void setLabelIndent(bool state);
+ void setCStyle();
+ void setJavaStyle();
+ void setEmptyLineFill(bool state);
+ void setPreprocessorIndent(bool state);
+
+
+ protected:
+ int getNextProgramCharDistance(const string &line, int i);
+ bool isLegalNameChar(char ch) const;
+ bool isWhiteSpace(char ch) const;
+ const string *findHeader(const string &line, int i,
+ const vector<const string*> &possibleHeaders,
+ bool checkBoundry = true);
+ string trim(const string &str);
+ int indexOf(vector<const string*> &container, const string *element);
+
+ private:
+ ASBeautifier(const ASBeautifier &copy);
+ void operator=(ASBeautifier&); // not to be implemented
+
+ void initStatic();
+ void registerInStatementIndent(const string &line, int i, int spaceTabCount,
+ int minIndent, bool updateParenStack);
+ string preLineWS(int spaceTabCount, int tabCount);
+
+ static vector<const string*> headers;
+ static vector<const string*> nonParenHeaders;
+ static vector<const string*> preprocessorHeaders;
+ static vector<const string*> preBlockStatements;
+ static vector<const string*> assignmentOperators;
+ static vector<const string*> nonAssignmentOperators;
+
+ static bool calledInitStatic;
+
+ ASSourceIterator *sourceIterator;
+ vector<ASBeautifier*> *waitingBeautifierStack;
+ vector<ASBeautifier*> *activeBeautifierStack;
+ vector<int> *waitingBeautifierStackLengthStack;
+ vector<int> *activeBeautifierStackLengthStack;
+ vector<const string*> *headerStack;
+ vector< vector<const string*>* > *tempStacks;
+ vector<int> *blockParenDepthStack;
+ vector<bool> *blockStatementStack;
+ vector<bool> *parenStatementStack;
+ vector<int> *inStatementIndentStack;
+ vector<int> *inStatementIndentStackSizeStack;
+ vector<int> *parenIndentStack;
+ vector<bool> *bracketBlockStateStack;
+ string indentString;
+ const string *currentHeader;
+ const string *previousLastLineHeader;
+ const string *immediatelyPreviousAssignmentOp;
+ const string *probationHeader;
+ bool isInQuote;
+ bool isInComment;
+ bool isInCase;
+ bool isInQuestion;
+ bool isInStatement;
+ bool isInHeader;
+ bool isCStyle;
+ bool isInOperator;
+ bool isInTemplate;
+ bool isInConst;
+ bool isInDefine;
+ bool isInDefineDefinition;
+ bool classIndent;
+ bool isInClassHeader;
+ bool isInClassHeaderTab;
+ bool switchIndent;
+ bool caseIndent;
+ bool namespaceIndent;
+ bool bracketIndent;
+ bool blockIndent;
+ bool labelIndent;
+ bool preprocessorIndent;
+ bool isInConditional;
+ bool isMinimalConditinalIndentSet;
+ bool shouldForceTabIndentation;
+ int minConditionalIndent;
+ int parenDepth;
+ int indentLength;
+ int blockTabCount;
+ unsigned int leadingWhiteSpaces;
+ int maxInStatementIndent;
+ int templateDepth;
+ char quoteChar;
+ char prevNonSpaceCh;
+ char currentNonSpaceCh;
+ char currentNonLegalCh;
+ char prevNonLegalCh;
+ int prevFinalLineSpaceTabCount;
+ int prevFinalLineTabCount;
+ bool emptyLineFill;
+ bool backslashEndsPrevLine;
+ int defineTabCount;
+ };
+}
+
+#endif
+/*
+ * Copyright (c) 1998,1999,2000,2001,2002 Tal Davidson. All rights reserved.
+ *
+ * ASFormatter.cpp
+ * by Tal Davidson (davidsont@bigfoot.com)
+ * This file is a part of "Artistic Style" - an indentater and reformatter
+ * of C, C++, C# and Java source files.
+ *
+ * The "Artistic Style" project, including all files needed to compile it,
+ * is free software; you can redistribute it and/or use it and/or modify it
+ * under the terms of the GNU General Public License as published
+ * by the Free Software Foundation; either version 2 of the License,
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this program.
+ *
+ *
+ * Patches:
+ * 26 November 1998 - Richard Bullington -
+ * A correction of line-breaking in headers following '}',
+
+ * was created using a variation of a patch by Richard Bullington.
+ * 08 May 2004
+ * applied ASFormatter450670.patch.bz2, ASFormatter.cpp.patch.bz2,
+ * patch1_ssvb_patch.tar.gz
+ */
+
+#include "compiler_defines.h"
+#include "ASFormatter.h"
+
+
+#include <string>
+#include <cctype>
+#include <vector>
+#include <algorithm>
+#include <iostream>
+
+
+#define INIT_CONTAINER(container, value) {if ( (container) != NULL ) delete (container); (container) = (value); }
+#define DELETE_CONTAINER(container) {if ( (container) != NULL ) delete (container) ; }
+#define IS_A(a,b) ( ((a) & (b)) == (b))
+#ifdef USES_NAMESPACE
+using namespace std;
+
+namespace astyle
+ {
+#endif
+
+
+ bool ASFormatter::calledInitStatic = false;
+ vector<const string*> ASFormatter::headers;
+ vector<const string*> ASFormatter::nonParenHeaders;
+ vector<const string*> ASFormatter::preprocessorHeaders;
+ vector<const string*> ASFormatter::preDefinitionHeaders;
+ vector<const string*> ASFormatter::preCommandHeaders;
+ vector<const string*> ASFormatter::operators;
+ vector<const string*> ASFormatter::assignmentOperators;
+
+
+ /**
+ * Constructor of ASFormatter
+ */
+ ASFormatter::ASFormatter()
+ {
+ staticInit();
+
+ preBracketHeaderStack = NULL;
+ bracketTypeStack = NULL;
+ parenStack = NULL;
+
+ sourceIterator = NULL;
+ bracketFormatMode = NONE_MODE;
+ shouldPadOperators = false;
+ shouldPadParenthesies = false;
+ shouldBreakOneLineBlocks = true;
+ shouldBreakOneLineStatements = true;
+ shouldConvertTabs = false;
+ shouldBreakBlocks = false;
+ shouldBreakClosingHeaderBlocks = false;
+ shouldBreakClosingHeaderBrackets = false;
+ shouldBreakElseIfs = false;
+ }
+
+ /**
+ * Destructor of ASFormatter
+ */
+ ASFormatter::~ASFormatter()
+ {
+ DELETE_CONTAINER( preBracketHeaderStack );
+ }
+
+ /**
+ * initialization of static data of ASFormatter.
+ */
+ void ASFormatter::staticInit()
+ {
+ if (calledInitStatic)
+ return;
+
+ calledInitStatic = true;
+
+ headers.push_back(&AS_IF);
+ headers.push_back(&AS_ELSE);
+ headers.push_back(&AS_DO);
+ headers.push_back(&AS_WHILE);
+ headers.push_back(&AS_FOR);
+ headers.push_back(&AS_SYNCHRONIZED);
+ headers.push_back(&AS_TRY);
+ headers.push_back(&AS_CATCH);
+ headers.push_back(&AS_FINALLY);
+ headers.push_back(&AS_SWITCH);
+ headers.push_back(&AS_TEMPLATE);
+ headers.push_back(&AS_FOREACH);
+ headers.push_back(&AS_LOCK);
+ headers.push_back(&AS_UNSAFE);
+ headers.push_back(&AS_FIXED);
+ headers.push_back(&AS_GET);
+ headers.push_back(&AS_SET);
+ headers.push_back(&AS_ADD);
+ headers.push_back(&AS_REMOVE);
+
+ nonParenHeaders.push_back(&AS_ELSE);
+ nonParenHeaders.push_back(&AS_DO);
+ nonParenHeaders.push_back(&AS_TRY);
+ nonParenHeaders.push_back(&AS_FINALLY);
+ nonParenHeaders.push_back(&AS_UNSAFE);
+ nonParenHeaders.push_back(&AS_GET);
+ nonParenHeaders.push_back(&AS_SET);
+ nonParenHeaders.push_back(&AS_ADD);
+ nonParenHeaders.push_back(&AS_REMOVE);
+
+ // nonParenHeaders.push_back(&AS_TEMPLATE);
+
+ preDefinitionHeaders.push_back(&AS_CLASS);
+ preDefinitionHeaders.push_back(&AS_INTERFACE);
+ preDefinitionHeaders.push_back(&AS_NAMESPACE);
+ preDefinitionHeaders.push_back(&AS_STRUCT);
+
+ preCommandHeaders.push_back(&AS_EXTERN);
+ preCommandHeaders.push_back(&AS_THROWS);
+ preCommandHeaders.push_back(&AS_CONST);
+
+ preprocessorHeaders.push_back(&AS_BAR_DEFINE);
+ //// DEVEL: removed the folowing lines
+ ////preprocessorHeaders.push_back(&AS_BAR_INCLUDE);
+ ////preprocessorHeaders.push_back(&AS_BAR_IF); // #if or #ifdef
+ ////preprocessorHeaders.push_back(&AS_BAR_EL); // #else or #elif
+ ////preprocessorHeaders.push_back(&AS_BAR_ENDIF);
+
+ operators.push_back(&AS_PLUS_ASSIGN);
+ operators.push_back(&AS_MINUS_ASSIGN);
+ operators.push_back(&AS_MULT_ASSIGN);
+ operators.push_back(&AS_DIV_ASSIGN);
+ operators.push_back(&AS_MOD_ASSIGN);
+ operators.push_back(&AS_OR_ASSIGN);
+ operators.push_back(&AS_AND_ASSIGN);
+ operators.push_back(&AS_XOR_ASSIGN);
+ operators.push_back(&AS_EQUAL);
+ operators.push_back(&AS_PLUS_PLUS);
+ operators.push_back(&AS_MINUS_MINUS);
+ operators.push_back(&AS_NOT_EQUAL);
+ operators.push_back(&AS_GR_EQUAL);
+ operators.push_back(&AS_GR_GR_GR_ASSIGN);
+ operators.push_back(&AS_GR_GR_ASSIGN);
+ operators.push_back(&AS_GR_GR_GR);
+ operators.push_back(&AS_GR_GR);
+ operators.push_back(&AS_LS_EQUAL);
+ operators.push_back(&AS_LS_LS_LS_ASSIGN);
+ operators.push_back(&AS_LS_LS_ASSIGN);
+ operators.push_back(&AS_LS_LS_LS);
+ operators.push_back(&AS_LS_LS);
+ operators.push_back(&AS_ARROW);
+ operators.push_back(&AS_AND);
+ operators.push_back(&AS_OR);
+ operators.push_back(&AS_COLON_COLON);
+
+ //// BUGFIX: removed the folowing lines
+ //// operators.push_back(&AS_PAREN_PAREN);
+ //// operators.push_back(&AS_BLPAREN_BLPAREN);
+
+ operators.push_back(&AS_PLUS);
+ operators.push_back(&AS_MINUS);
+ operators.push_back(&AS_MULT);
+ operators.push_back(&AS_DIV);
+ operators.push_back(&AS_MOD);
+ operators.push_back(&AS_QUESTION);
+ operators.push_back(&AS_COLON);
+ operators.push_back(&AS_ASSIGN);
+ operators.push_back(&AS_LS);
+ operators.push_back(&AS_GR);
+ operators.push_back(&AS_NOT);
+ operators.push_back(&AS_BIT_OR);
+ operators.push_back(&AS_BIT_AND);
+ operators.push_back(&AS_BIT_NOT);
+ operators.push_back(&AS_BIT_XOR);
+ operators.push_back(&AS_OPERATOR);
+ operators.push_back(&AS_COMMA);
+ //BEGIN Content Patch patch1_ssvb_patch.tar.gz
+ operators.push_back(&AS_SEMICOLON);
+ //END Content Patch patch1_ssvb_patch.tar.gz
+ operators.push_back(&AS_RETURN);
+
+ assignmentOperators.push_back(&AS_PLUS_ASSIGN);
+ assignmentOperators.push_back(&AS_MINUS_ASSIGN);
+ assignmentOperators.push_back(&AS_MULT_ASSIGN);
+ assignmentOperators.push_back(&AS_DIV_ASSIGN);
+ assignmentOperators.push_back(&AS_MOD_ASSIGN);
+ assignmentOperators.push_back(&AS_XOR_ASSIGN);
+ assignmentOperators.push_back(&AS_OR_ASSIGN);
+ assignmentOperators.push_back(&AS_AND_ASSIGN);
+ assignmentOperators.push_back(&AS_GR_GR_GR_ASSIGN);
+ assignmentOperators.push_back(&AS_LS_LS_LS_ASSIGN);
+ assignmentOperators.push_back(&AS_ASSIGN);
+ }
+
+ /**
+ * initialize the ASFormatter.
+ *
+ * init() should be called every time a ASFormatter object is to start
+ * formatting a NEW source file.
+ * init() recieves a pointer to a DYNAMICALLY CREATED ASSourceIterator object
+ * that will be used to iterate through the source code. This object will be
+ * deleted during the ASFormatter's destruction, and thus should not be
+ * deleted elsewhere.
+ *
+ * @param iter a pointer to the DYNAMICALLY CREATED ASSourceIterator object.
+ */
+ void ASFormatter::init(ASSourceIterator *si)
+ {
+ ASBeautifier::init(si);
+ sourceIterator = si;
+
+ INIT_CONTAINER( preBracketHeaderStack, new vector<const string*> );
+ INIT_CONTAINER( bracketTypeStack, new vector<BracketType> );
+ bracketTypeStack->push_back(DEFINITION_TYPE);
+ INIT_CONTAINER( parenStack, new vector<int> );
+ parenStack->push_back(0);
+
+ currentHeader = NULL;
+ currentLine = string("");
+ formattedLine = "";
+ currentChar = ' ';
+ previousCommandChar = ' ';
+ previousNonWSChar = ' ';
+ quoteChar = '"';
+ charNum = 0;
+ previousOperator = NULL;
+
+ isVirgin = true;
+ isInLineComment = false;
+ isInComment = false;
+ isInPreprocessor = false;
+ doesLineStartComment = false;
+ isInQuote = false;
+ isSpecialChar = false;
+ isNonParenHeader = true;
+ foundPreDefinitionHeader = false;
+ foundPreCommandHeader = false;
+ foundQuestionMark = false;
+ isInLineBreak = false;
+ endOfCodeReached = false;
+ isLineReady = false;
+ isPreviousBracketBlockRelated = true;
+ isInPotentialCalculation = false;
+ //foundOneLineBlock = false;
+ shouldReparseCurrentChar = false;
+ passedSemicolon = false;
+ passedColon = false;
+ isInTemplate = false;
+ shouldBreakLineAfterComments = false;
+ isImmediatelyPostComment = false;
+ isImmediatelyPostLineComment = false;
+ isImmediatelyPostEmptyBlock = false;
+
+ isPrependPostBlockEmptyLineRequested = false;
+ isAppendPostBlockEmptyLineRequested = false;
+ prependEmptyLine = false;
+
+ foundClosingHeader = false;
+ previousReadyFormattedLineLength = 0;
+
+ isImmediatelyPostHeader = false;
+ isInHeader = false;
+ }
+
+ /**
+ * get the next formatted line.
+ *
+ * @return formatted line.
+ */
+
+ string ASFormatter::nextLine()
+ {
+ const string *newHeader;
+ bool isCharImmediatelyPostComment = false;
+ bool isPreviousCharPostComment = false;
+ bool isCharImmediatelyPostLineComment = false;
+ bool isInVirginLine = isVirgin;
+ bool isCharImmediatelyPostOpenBlock = false;
+ bool isCharImmediatelyPostCloseBlock = false;
+ bool isCharImmediatelyPostTemplate = false;
+ bool isCharImmediatelyPostHeader = false;
+
+ if (!isFormattingEnabled())
+ return ASBeautifier::nextLine();
+
+ while (!isLineReady)
+ {
+ if (shouldReparseCurrentChar)
+ shouldReparseCurrentChar = false;
+ else if (!getNextChar())
+ {
+ breakLine();
+ return beautify(readyFormattedLine);
+ }
+ else // stuff to do when reading a new character...
+ {
+ // make sure that a virgin '{' at the begining ofthe file will be treated as a block...
+ if (isInVirginLine && currentChar == '{')
+ previousCommandChar = '{';
+ isPreviousCharPostComment = isCharImmediatelyPostComment;
+ isCharImmediatelyPostComment = false;
+ isCharImmediatelyPostTemplate = false;
+ isCharImmediatelyPostHeader = false;
+ }
+
+ if (isInLineComment)
+ {
+ appendCurrentChar();
+
+ // explicitely break a line when a line comment's end is found.
+ if (/*bracketFormatMode == ATTACH_MODE &&*/ charNum+1 == currentLine.length())
+ {
+ isInLineBreak = true;
+ isInLineComment = false;
+ isImmediatelyPostLineComment = true;
+ currentChar = 0; //make sure it is a neutral char.
+ }
+ continue;
+ }
+ else if (isInComment)
+ {
+ if (isSequenceReached(AS_CLOSE_COMMENT))
+ {
+ isInComment = false;
+ isImmediatelyPostComment = true;
+ appendSequence(AS_CLOSE_COMMENT);
+ goForward(1);
+ }
+ else
+ appendCurrentChar();
+
+ continue;
+ }
+
+ // not in line comment or comment
+
+ else if (isInQuote)
+ {
+ if (isSpecialChar)
+ {
+ isSpecialChar = false;
+ appendCurrentChar();
+ }
+ else if (currentChar == '\\')
+ {
+ isSpecialChar = true;
+ appendCurrentChar();
+ }
+ else if (quoteChar == currentChar)
+ {
+ isInQuote = false;
+ appendCurrentChar();
+ }
+ else
+ {
+ appendCurrentChar();
+ }
+
+ continue;
+ }
+
+
+
+ // handle white space - needed to simplify the rest.
+ if (isWhiteSpace(currentChar) || isInPreprocessor)
+ {
+ ////// DEVEL: if (isLegalNameChar(previousChar) && isLegalNameChar(peekNextChar()))
+ appendCurrentChar();
+ continue;
+ }
+
+ /* not in MIDDLE of quote or comment or white-space of any type ... */
+
+ if (isSequenceReached(AS_OPEN_LINE_COMMENT))
+ {
+ isInLineComment = true;
+ if (shouldPadOperators)
+ appendSpacePad();
+ appendSequence(AS_OPEN_LINE_COMMENT);
+ goForward(1);
+ continue;
+ }
+ else if (isSequenceReached(AS_OPEN_COMMENT))
+ {
+ isInComment = true;
+ if (shouldPadOperators)
+ appendSpacePad();
+ appendSequence(AS_OPEN_COMMENT);
+ goForward(1);
+ continue;
+ }
+ else if (currentChar == '"' || currentChar == '\'')
+ {
+ isInQuote = true;
+ quoteChar = currentChar;
+ //// if (shouldPadOperators) // BUGFIX: these two lines removed. seem to be unneeded, and interfere with L"
+ //// appendSpacePad(); // BUFFIX: TODO make sure the removal of these lines doesn't reopen old bugs...
+ appendCurrentChar();
+ continue;
+ }
+
+ /* not in quote or comment or white-space of any type ... */
+
+
+ // check if in preprocessor
+ // ** isInPreprocessor will be automatically reset at the begining
+ // of a new line in getnextChar()
+ if (currentChar == '#')
+ isInPreprocessor = true;
+
+ if (isInPreprocessor)
+ {
+ appendCurrentChar();
+ continue;
+ }
+
+ /* not in preprocessor ... */
+
+ if (isImmediatelyPostComment)
+ {
+ isImmediatelyPostComment = false;
+ isCharImmediatelyPostComment = true;
+ }
+
+ if (isImmediatelyPostLineComment)
+ {
+ isImmediatelyPostLineComment = false;
+ isCharImmediatelyPostLineComment = true;
+ }
+
+ if (shouldBreakLineAfterComments)
+ {
+ shouldBreakLineAfterComments = false;
+ shouldReparseCurrentChar = true;
+ breakLine();
+ continue;
+ }
+
+ // reset isImmediatelyPostHeader information
+ if (isImmediatelyPostHeader)
+ {
+ isImmediatelyPostHeader = false;
+ isCharImmediatelyPostHeader = true;
+
+ // Make sure headers are broken from their succeeding blocks
+ // (e.g.
+ // if (isFoo) DoBar();
+ // should become
+ // if (isFoo)
+ // DoBar;
+ // )
+ // But treat else if() as a special case which should not be broken!
+ if (shouldBreakOneLineStatements)
+ {
+ // if may break 'else if()'s, ythen simply break the line
+
+ if (shouldBreakElseIfs)
+ isInLineBreak = true;
+
+ else
+ {
+ // make sure 'else if()'s are not broken.
+
+ bool isInElseIf = false;
+ const string *upcomingHeader;
+
+ upcomingHeader = findHeader(headers);
+ if (currentHeader == &AS_ELSE && upcomingHeader == &AS_IF)
+ isInElseIf = true;
+
+ if (!isInElseIf)
+ isInLineBreak = true; ////BUGFIX: SHOULD NOT BE breakLine() !!!
+ }
+ }
+ }
+
+ if (passedSemicolon)
+ {
+ passedSemicolon = false;
+ if (parenStack->back() == 0)
+ {
+ shouldReparseCurrentChar = true;
+ isInLineBreak = true;
+ continue;
+ }
+ }
+
+ if (passedColon)
+ {
+ passedColon = false;
+ if (parenStack->back() == 0)
+ {
+ shouldReparseCurrentChar = true;
+ isInLineBreak = true;
+ continue;
+ }
+ }
+
+ // Check if in template declaration, e.g. foo<bar> or foo<bar,fig>
+ // If so, set isInTemplate to true
+ //
+ if (!isInTemplate && currentChar == '<')
+ {
+ int templateDepth = 0;
+ const string *oper;
+ for (unsigned int i=charNum;
+ i< currentLine.length();
+ i += (oper ? oper->length() : 1) )
+ {
+ oper = ASBeautifier::findHeader(currentLine, i, operators);
+
+ if (oper == &AS_LS)
+ {
+ templateDepth++;
+ }
+ else if (oper == &AS_GR)
+ {
+ templateDepth--;
+ if (templateDepth == 0)
+ {
+ // this is a template!
+ //
+ isInTemplate = true;
+ break;
+ }
+ }
+ else if (oper == &AS_COMMA // comma, e.g. A<int, char>
+ || oper == &AS_BIT_AND // reference, e.g. A<int&>
+ || oper == &AS_MULT // pointer, e.g. A<int*>
+ || oper == &AS_COLON_COLON) // ::, e.g. std::string
+ {
+ continue;
+ }
+ else if (!isLegalNameChar(currentLine[i]) && !isWhiteSpace(currentLine[i]))
+ {
+ // this is not a template -> leave...
+ //
+ isInTemplate = false;
+ break;
+ }
+ }
+ }
+
+
+ // handle parenthesies
+ //
+ if (currentChar == '(' || currentChar == '[' || (isInTemplate && currentChar == '<'))
+ {
+ parenStack->back()++;
+ }
+ else if (currentChar == ')' || currentChar == ']' || (isInTemplate && currentChar == '>'))
+ {
+ parenStack->back()--;
+ if (isInTemplate && parenStack->back() == 0)
+ {
+ isInTemplate = false;
+ isCharImmediatelyPostTemplate = true;
+ }
+
+ // check if this parenthesis closes a header, e.g. if (...), while (...)
+ //
+ if (isInHeader && parenStack->back() == 0)
+ {
+ isInHeader = false;
+ isImmediatelyPostHeader = true;
+ }
+
+ }
+
+ // handle brackets
+ //
+ BracketType bracketType = NULL_TYPE;
+
+ if (currentChar == '{')
+ {
+ bracketType = getBracketType();
+ foundPreDefinitionHeader = false;
+ foundPreCommandHeader = false;
+
+ bracketTypeStack->push_back(bracketType);
+ preBracketHeaderStack->push_back(currentHeader);
+ currentHeader = NULL;
+
+ isPreviousBracketBlockRelated = !IS_A(bracketType, ARRAY_TYPE);
+ }
+ else if (currentChar == '}')
+ {
+ // if a request has been made to append a post block empty line,
+ // but the block exists immediately before a closing bracket,
+ // then there is not need for the post block empty line.
+ //
+ isAppendPostBlockEmptyLineRequested = false;
+
+ if (!bracketTypeStack->empty())
+ {
+ bracketType = bracketTypeStack->back();
+ bracketTypeStack->pop_back();
+
+ isPreviousBracketBlockRelated = !IS_A(bracketType, ARRAY_TYPE);
+ }
+
+ if (!preBracketHeaderStack->empty())
+ {
+ currentHeader = preBracketHeaderStack->back();
+ preBracketHeaderStack->pop_back();
+ }
+ else
+ currentHeader = NULL;
+ }
+
+ if (!IS_A(bracketType, ARRAY_TYPE))
+ {
+
+ if (currentChar == '{')
+ {
+ parenStack->push_back(0);
+ }
+ else if (currentChar == '}')
+ {
+ if (!parenStack->empty())
+ {
+ parenStack->pop_back();
+ }
+ }
+
+ if (bracketFormatMode != NONE_MODE)
+ {
+ if (currentChar == '{')
+ {
+ if ( ( bracketFormatMode == ATTACH_MODE
+ || bracketFormatMode == BDAC_MODE && bracketTypeStack->size()>=2
+ && IS_A((*bracketTypeStack)[bracketTypeStack->size()-2], COMMAND_TYPE) /*&& isInLineBreak*/)
+ && !isCharImmediatelyPostLineComment )
+ {
+ appendSpacePad();
+ if (!isCharImmediatelyPostComment // do not attach '{' to lines that end with /**/ comments.
+ && previousCommandChar != '{'
+ && previousCommandChar != '}'
+ && previousCommandChar != ';') // '}' , ';' chars added for proper handling of '{' immediately after a '}' or ';'
+ appendCurrentChar(false);
+ else
+ appendCurrentChar(true);
+ continue;
+ }
+ else if (bracketFormatMode == BREAK_MODE
+ || bracketFormatMode == BDAC_MODE && bracketTypeStack->size()>=2
+ && IS_A((*bracketTypeStack)[bracketTypeStack->size()-2], DEFINITION_TYPE))
+ {
+ if ( shouldBreakOneLineBlocks || !IS_A(bracketType, SINGLE_LINE_TYPE) )
+ breakLine();
+ appendCurrentChar();
+ continue;
+ }
+ }
+ else if (currentChar == '}')
+ {
+ // bool origLineBreak = isInLineBreak;
+
+ // mark state of immediately after empty block
+ // this state will be used for locating brackets that appear immedately AFTER an empty block (e.g. '{} \n}').
+ if (previousCommandChar == '{')
+ isImmediatelyPostEmptyBlock = true;
+
+ if ( (!(previousCommandChar == '{' && isPreviousBracketBlockRelated) ) // this '{' does not close an empty block
+ && (shouldBreakOneLineBlocks || !IS_A(bracketType, SINGLE_LINE_TYPE)) // astyle is allowed to break on line blocks
+ && !isImmediatelyPostEmptyBlock) // this '}' does not immediately follow an empty block
+ {
+ breakLine();
+ appendCurrentChar();
+ }
+ else
+ {
+ // Content Patch ASFormatter.cpp.patch.bz2
+ // if (!isCharImmediatelyPostComment)
+ if (!isCharImmediatelyPostComment &&
+ !isCharImmediatelyPostLineComment)
+ isInLineBreak = false;
+ appendCurrentChar();
+ if (shouldBreakOneLineBlocks || !IS_A(bracketType, SINGLE_LINE_TYPE))
+ shouldBreakLineAfterComments = true;
+ }
+
+ if (shouldBreakBlocks)
+ {
+ isAppendPostBlockEmptyLineRequested =true;
+ }
+
+ continue;
+ }
+ }
+ }
+
+ if ( ( (previousCommandChar == '{'
+ && isPreviousBracketBlockRelated)
+
+ || (previousCommandChar == '}'
+ && !isImmediatelyPostEmptyBlock // <--
+ && isPreviousBracketBlockRelated
+ && !isPreviousCharPostComment // <-- Fixes wrongly appended newlines after '}' immediately after comments... 10/9/1999
+ && peekNextChar() != ' '))
+
+ && (shouldBreakOneLineBlocks || !IS_A(bracketTypeStack->back(), SINGLE_LINE_TYPE)) )
+ {
+ isCharImmediatelyPostOpenBlock = (previousCommandChar == '{');
+ isCharImmediatelyPostCloseBlock = (previousCommandChar == '}');
+
+ previousCommandChar = ' ';
+ isInLineBreak = true; //<----
+ }
+
+ // reset block handling flags
+ isImmediatelyPostEmptyBlock = false;
+
+ // look for headers
+ if (!isInTemplate)
+ {
+ if ( (newHeader = findHeader(headers)) != NULL)
+ {
+ foundClosingHeader = false;
+ const string *previousHeader;
+
+ // recognize closing headers of do..while, if..else, try..catch..finally
+ if ( (newHeader == &AS_ELSE && currentHeader == &AS_IF)
+ || (newHeader == &AS_WHILE && currentHeader == &AS_DO)
+ || (newHeader == &AS_CATCH && currentHeader == &AS_TRY)
+ || (newHeader == &AS_CATCH && currentHeader == &AS_CATCH)
+ || (newHeader == &AS_FINALLY && currentHeader == &AS_TRY)
+ || (newHeader == &AS_FINALLY && currentHeader == &AS_CATCH) )
+ foundClosingHeader = true;
+
+ previousHeader = currentHeader;
+ currentHeader = newHeader;
+
+ // If in ATTACH or LINUX bracket modes, attach closing headers (e.g. 'else', 'catch')
+ // to their preceding bracket,
+ // But do not perform the attachment if the shouldBreakClosingHeaderBrackets is set!
+ if (!shouldBreakClosingHeaderBrackets && foundClosingHeader && (bracketFormatMode == ATTACH_MODE || bracketFormatMode == BDAC_MODE) && previousNonWSChar == '}')
+ {
+ isInLineBreak = false;
+ appendSpacePad();
+
+ if (shouldBreakBlocks)
+ isAppendPostBlockEmptyLineRequested = false;
+ }
+
+ //Check if a template definition as been reached, e.g. template<class A>
+ if (newHeader == &AS_TEMPLATE)
+ {
+ isInTemplate = true;
+ }
+
+ // check if the found header is non-paren header
+ isNonParenHeader = ( find(nonParenHeaders.begin(), nonParenHeaders.end(),
+ newHeader) != nonParenHeaders.end() );
+ appendSequence(*currentHeader);
+ goForward(currentHeader->length() - 1);
+ // if padding is on, and a paren-header is found
+ // then add a space pad after it.
+ if (shouldPadOperators && !isNonParenHeader)
+ appendSpacePad();
+
+
+ // Signal that a header has been reached
+ // *** But treat a closing while() (as in do...while)
+ // as if it where NOT a header since a closing while()
+ // should never have a block after it!
+ if (!(foundClosingHeader && currentHeader == &AS_WHILE))
+ {
+ isInHeader = true;
+ if (isNonParenHeader)
+ {
+ isImmediatelyPostHeader = true;
+ isInHeader = false;
+ }
+ }
+
+ if (currentHeader == &AS_IF && previousHeader == &AS_ELSE)
+ isInLineBreak = false;
+
+ if (shouldBreakBlocks)
+ {
+ if (previousHeader == NULL
+ && !foundClosingHeader
+ && !isCharImmediatelyPostOpenBlock)
+ {
+ isPrependPostBlockEmptyLineRequested = true;
+ }
+
+ if (currentHeader == &AS_ELSE
+ || currentHeader == &AS_CATCH
+ || currentHeader == &AS_FINALLY
+ || foundClosingHeader)
+ {
+ isPrependPostBlockEmptyLineRequested = false;
+ }
+
+ if (shouldBreakClosingHeaderBlocks
+ && isCharImmediatelyPostCloseBlock)
+ {
+ isPrependPostBlockEmptyLineRequested = true;
+ }
+
+ }
+
+ continue;
+ }
+ else if ( (newHeader = findHeader(preDefinitionHeaders)) != NULL)
+ {
+ foundPreDefinitionHeader = true;
+ appendSequence(*newHeader);
+ goForward(newHeader->length() - 1);
+
+ if (shouldBreakBlocks)
+ isPrependPostBlockEmptyLineRequested = true;
+
+ continue;
+ }
+ else if ( (newHeader = findHeader(preCommandHeaders)) != NULL)
+ {
+ foundPreCommandHeader = true;
+ appendSequence(*newHeader);
+ goForward(newHeader->length() - 1);
+
+ continue;
+ }
+ }
+
+ if (previousNonWSChar == '}' || currentChar == ';')
+ {
+ if (shouldBreakOneLineStatements && currentChar == ';'
+ && (shouldBreakOneLineBlocks || !IS_A(bracketTypeStack->back(), SINGLE_LINE_TYPE)))
+ {
+ passedSemicolon = true;
+ }
+
+ if (shouldBreakBlocks && currentHeader != NULL && parenStack->back() == 0)
+ {
+ isAppendPostBlockEmptyLineRequested = true;
+ }
+
+ if (currentChar != ';')
+ currentHeader = NULL; //DEVEL: is this ok?
+
+ foundQuestionMark = false;
+ foundPreDefinitionHeader = false;
+ foundPreCommandHeader = false;
+ isInPotentialCalculation = false;
+
+ }
+
+ if (currentChar == ':'
+ && shouldBreakOneLineStatements
+ && !foundQuestionMark // not in a ... ? ... : ... sequence
+ && !foundPreDefinitionHeader // not in a definition block (e.g. class foo : public bar
+ && previousCommandChar != ')' // not immediately after closing paren of a method header, e.g. ASFormatter::ASFormatter(...) : ASBeautifier(...)
+ && previousChar != ':' // not part of '::'
+ && peekNextChar() != ':') // not part of '::'
+ {
+ passedColon = true;
+ if (shouldBreakBlocks)
+ isPrependPostBlockEmptyLineRequested = true;
+ }
+
+ if (currentChar == '?')
+ foundQuestionMark = true;
+
+ if (shouldPadOperators)
+ {
+ if ((newHeader = findHeader(operators)) != NULL)
+ {
+ bool shouldPad = (newHeader != &AS_COLON_COLON
+ && newHeader != &AS_PAREN_PAREN
+ && newHeader != &AS_BLPAREN_BLPAREN
+ && newHeader != &AS_PLUS_PLUS
+ && newHeader != &AS_MINUS_MINUS
+ && newHeader != &AS_NOT
+ && newHeader != &AS_BIT_NOT
+ && newHeader != &AS_ARROW
+ && newHeader != &AS_OPERATOR
+ && !(newHeader == &AS_MINUS && isInExponent())
+ && !(newHeader == &AS_PLUS && isInExponent())
+ && previousOperator != &AS_OPERATOR
+ && !((newHeader == &AS_MULT || newHeader == &AS_BIT_AND)
+ && isPointerOrReference())
+ && !( (isInTemplate || isCharImmediatelyPostTemplate)
+ && (newHeader == &AS_LS || newHeader == &AS_GR))
+ );
+
+ if (!isInPotentialCalculation)
+ if (find(assignmentOperators.begin(), assignmentOperators.end(), newHeader)
+ != assignmentOperators.end())
+ isInPotentialCalculation = true;
+
+ // pad before operator
+ if (shouldPad
+ && !(newHeader == &AS_COLON && !foundQuestionMark)
+ && newHeader != &AS_SEMICOLON
+ && newHeader != &AS_COMMA)
+ appendSpacePad();
+ appendSequence(*newHeader);
+ goForward(newHeader->length() - 1);
+
+ // since this block handles '()' and '[]',
+ // the parenStack must be updated here accordingly!
+ if (newHeader == &AS_PAREN_PAREN
+ || newHeader == &AS_BLPAREN_BLPAREN)
+ parenStack->back()--;
+
+ currentChar = (*newHeader)[newHeader->length() - 1];
+ // pad after operator
+ // but do not pad after a '-' that is a urinary-minus.
+ if ( shouldPad && !(newHeader == &AS_MINUS && isUrinaryMinus()) )
+ appendSpacePad();
+
+ previousOperator = newHeader;
+ continue;
+ }
+ }
+ //BEGIN Content Patch patch1_ssvb_patch.tar.gz
+ if (currentChar == '(' || currentChar == '[' )
+ isInPotentialCalculation = true;
+ //END Content Patch patch1_ssvb_patch.tar.gz
+ if (shouldPadParenthesies)
+ {
+ if (currentChar == '(' || currentChar == '[' )
+ {
+ char peekedChar = peekNextChar();
+
+ isInPotentialCalculation = true;
+ appendCurrentChar();
+ if (!(currentChar == '(' && peekedChar == ')')
+ && !(currentChar == '[' && peekedChar == ']'))
+ appendSpacePad();
+ continue;
+ }
+ else if (currentChar == ')' || currentChar == ']')
+ {
+ char peekedChar = peekNextChar();
+
+ if (!(previousChar == '(' && currentChar == ')')
+ && !(previousChar == '[' && currentChar == ']'))
+ appendSpacePad();
+
+ appendCurrentChar();
+
+ if (peekedChar != ';' && peekedChar != ',' && peekedChar != '.'
+ && !(currentChar == ']' && peekedChar == '['))
+ appendSpacePad();
+ continue;
+ }
+ }
+
+ appendCurrentChar();
+ }
+
+ // return a beautified (i.e. correctly indented) line.
+
+ string beautifiedLine;
+ int readyFormattedLineLength = trim(readyFormattedLine).length();
+
+ if (prependEmptyLine
+ && readyFormattedLineLength > 0
+ && previousReadyFormattedLineLength > 0)
+ {
+ isLineReady = true; // signal that a readyFormattedLine is still waiting
+ beautifiedLine = beautify("");
+ }
+ else
+ {
+ isLineReady = false;
+ beautifiedLine = beautify(readyFormattedLine);
+ }
+
+ prependEmptyLine = false;
+ previousReadyFormattedLineLength = readyFormattedLineLength;
+
+ return beautifiedLine;
+
+ }
+
+
+ /**
+ * check if there are any indented lines ready to be read by nextLine()
+ *
+ * @return are there any indented lines ready?
+ */
+ bool ASFormatter::hasMoreLines() const
+ {
+ if (!isFormattingEnabled())
+ return ASBeautifier::hasMoreLines();
+ else
+ return !endOfCodeReached;
+ }
+
+ /**
+ * check if formatting options are enabled, in addition to indentation.
+ *
+ * @return are formatting options enabled?
+ */
+ bool ASFormatter::isFormattingEnabled() const
+ {
+ return (bracketFormatMode != NONE_MODE
+ || shouldPadOperators
+ || shouldConvertTabs);
+ }
+
+ /**
+ * set the bracket formatting mode.
+ * options:
+ * astyle::NONE_MODE no formatting of brackets.
+ * astyle::ATTACH_MODE Java, K&R style bracket placement.
+ * astyle::BREAK_MODE ANSI C/C++ style bracket placement.
+ *
+ * @param mode the bracket formatting mode.
+ */
+ void ASFormatter::setBracketFormatMode(BracketMode mode)
+ {
+ bracketFormatMode = mode;
+ }
+
+ /**
+ * set closing header bracket breaking mode
+ * options:
+ * true brackets just before closing headers (e.g. 'else', 'catch')
+ * will be broken, even if standard brackets are attached.
+ * false closing header brackets will be treated as standard brackets.
+ *
+ * @param mode the closing header bracket breaking mode.
+ */
+ void ASFormatter::setBreakClosingHeaderBracketsMode(bool state)
+ {
+ shouldBreakClosingHeaderBrackets = state;
+ }
+
+ /**
+ * set 'else if()' breaking mode
+ * options:
+ * true 'else' headers will be broken from their succeeding 'if' headers.
+ * false 'else' headers will be attached to their succeeding 'if' headers.
+ *
+ * @param mode the 'else if()' breaking mode.
+ */
+ void ASFormatter::setBreakElseIfsMode(bool state)
+ {
+ shouldBreakElseIfs = state;
+ }
+
+ /**
+ * set operator padding mode.
+ * options:
+ * true statement operators will be padded with spaces around them.
+ * false statement operators will not be padded.
+ *
+ * @param mode the padding mode.
+ */
+ void ASFormatter::setOperatorPaddingMode(bool state)
+ {
+ shouldPadOperators = state;
+ }
+
+ /**
+ * set parentheies padding mode.
+ * options:
+ * true statement parenthesies will be padded with spaces around them.
+ * false statement parenthesies will not be padded.
+ *
+ * @param mode the padding mode.
+ */
+ void ASFormatter::setParenthesisPaddingMode(bool state)
+ {
+ shouldPadParenthesies = state;
+ }
+
+ /**
+ * set option to break/not break one-line blocks
+ *
+ * @param state true = break, false = don't break.
+ */
+ void ASFormatter::setBreakOneLineBlocksMode(bool state)
+ {
+ shouldBreakOneLineBlocks = state;
+ }
+
+ /**
+ * set option to break/not break lines consisting of multiple statements.
+ *
+ * @param state true = break, false = don't break.
+ */
+ void ASFormatter::setSingleStatementsMode(bool state)
+ {
+ shouldBreakOneLineStatements = state;
+ }
+
+ /**
+ * set option to convert tabs to spaces.
+ *
+ * @param state true = convert, false = don't convert.
+ */
+ void ASFormatter::setTabSpaceConversionMode(bool state)
+ {
+ shouldConvertTabs = state;
+ }
+
+
+ /**
+ * set option to break unrelated blocks of code with empty lines.
+ *
+ * @param state true = convert, false = don't convert.
+ */
+ void ASFormatter::setBreakBlocksMode(bool state)
+ {
+ shouldBreakBlocks = state;
+ }
+
+ /**
+ * set option to break closing header blocks of code (such as 'else', 'catch', ...) with empty lines.
+ *
+ * @param state true = convert, false = don't convert.
+ */
+ void ASFormatter::setBreakClosingHeaderBlocksMode(bool state)
+ {
+ shouldBreakClosingHeaderBlocks = state;
+ }
+
+ /**
+ * check if a specific sequence exists in the current placement of the current line
+ *
+ * @return whether sequence has been reached.
+ * @param sequence the sequence to be checked
+ */
+ bool ASFormatter::isSequenceReached(const string &sequence) const
+ {
+ return currentLine.COMPARE(charNum, sequence.length(), sequence) == 0;
+
+ }
+
+ /**
+ * jump over several characters.
+ *
+ * @param i the number of characters to jump over.
+ */
+ void ASFormatter::goForward(int i)
+ {
+ while (--i >= 0)
+ getNextChar();
+ }
+
+ /**
+ * peek at the next unread character.
+ *
+ * @return the next unread character.
+ */
+ char ASFormatter::peekNextChar() const
+ {
+ int peekNum = charNum + 1;
+ int len = currentLine.length();
+ char ch = ' ';
+
+ while (peekNum < len)
+ {
+ ch = currentLine[peekNum++];
+ if (!isWhiteSpace(ch))
+ return ch;
+ }
+
+ if (shouldConvertTabs && ch == '\t')
+ ch = ' ';
+
+ return ch;
+ }
+
+ /**
+ * check if current placement is before a comment or line-comment
+ *
+ * @return is before a comment or line-comment.
+ */
+ bool ASFormatter::isBeforeComment() const
+ {
+ int peekNum = charNum + 1;
+ int len = currentLine.length();
+ // char ch = ' ';
+ bool foundComment = false;
+
+ for (peekNum = charNum + 1;
+ peekNum < len && isWhiteSpace(currentLine[peekNum]);
+ ++peekNum)
+ ;
+
+ if (peekNum < len)
+ foundComment = ( currentLine.COMPARE(peekNum, 2, AS_OPEN_COMMENT) == 0
+ || currentLine.COMPARE(peekNum, 2, AS_OPEN_LINE_COMMENT) == 0 );
+
+ return foundComment;
+ }
+
+ /**
+ * get the next character, increasing the current placement in the process.
+ * the new character is inserted into the variable currentChar.
+ *
+ * @return whether succeded to recieve the new character.
+ */
+ bool ASFormatter::getNextChar()
+ {
+ isInLineBreak = false;
+ bool isAfterFormattedWhiteSpace = false;
+
+ if (shouldPadOperators && !isInComment && !isInLineComment
+ && !isInQuote && !doesLineStartComment && !isInPreprocessor
+ && !isBeforeComment())
+ {
+ //BEGIN Content Patch patch1_ssvb_patch.tar.gz
+ char prevchar = ' ';
+ char nextchar = peekNextChar();
+
+ int len = formattedLine.length();
+ // if (len > 0 && isWhiteSpace(formattedLine[len-1]))
+ if (len > 0) prevchar = formattedLine[len-1];
+ if (isWhiteSpace(prevchar) || prevchar == '(' || prevchar == '[' ||
+ nextchar == ')' || nextchar == ']')
+ {
+ isAfterFormattedWhiteSpace = true;
+ }
+ //END Content Patch patch1_ssvb_patch.tar.gz
+ }
+
+ previousChar = currentChar;
+ if (!isWhiteSpace(currentChar))
+ {
+ previousNonWSChar = currentChar;
+ if (!isInComment && !isInLineComment && !isInQuote
+ && !isSequenceReached(AS_OPEN_COMMENT)
+ && !isSequenceReached(AS_OPEN_LINE_COMMENT) )
+ previousCommandChar = previousNonWSChar;
+ }
+
+ unsigned int currentLineLength = currentLine.length();
+
+ if (charNum+1 < currentLineLength
+ && (!isWhiteSpace(peekNextChar()) || isInComment || isInLineComment))
+ {
+ currentChar = currentLine[++charNum];
+ if (isAfterFormattedWhiteSpace)
+ while (isWhiteSpace(currentChar) && charNum+1 < currentLineLength)
+ currentChar = currentLine[++charNum];
+
+ if (shouldConvertTabs && currentChar == '\t')
+ currentChar = ' ';
+
+ return true;
+ }
+ // BEGIN Content patch ASFormatter450670.patch.bz2
+ else if (isInLineComment && (charNum+1 == currentLineLength))
+ {
+ // fix BUG #450670
+ currentChar = ' ';
+ return true;
+ }
+ // END Content patch ASFormatter450670.patch.bz2
+ else
+ {
+ if (sourceIterator->hasMoreLines())
+ {
+ currentLine = sourceIterator->nextLine();
+ if (currentLine.length() == 0)
+ {
+ /*think*/ currentLine = string(" ");
+ }
+
+ // unless reading in the first line of the file,
+ // break a new line.
+ if (!isVirgin)
+ isInLineBreak = true;
+ else
+ isVirgin = false;
+
+ if (isInLineComment)
+ isImmediatelyPostLineComment = true;
+ isInLineComment = false;
+
+ trimNewLine();
+ currentChar = currentLine[charNum];
+
+ // check if is in preprocessor right after the line break and line trimming
+ if (previousNonWSChar != '\\')
+ isInPreprocessor = false;
+
+ if (shouldConvertTabs && currentChar == '\t')
+ currentChar = ' ';
+
+ return true;
+ }
+ else
+ {
+ endOfCodeReached = true;
+ return false;
+ }
+ }
+ }
+
+ /**
+ * jump over the leading white space in the current line,
+ * IF the line does not begin a comment or is in a preprocessor definition.
+ */
+ void ASFormatter::trimNewLine()
+ {
+ unsigned int len = currentLine.length();
+ charNum = 0;
+
+ if (isInComment || isInPreprocessor)
+ return;
+
+ while (isWhiteSpace(currentLine[charNum]) && charNum+1 < len)
+ ++charNum;
+
+ doesLineStartComment = false;
+ if (isSequenceReached(string("/*")))
+ {
+ charNum = 0;
+ doesLineStartComment = true;
+ }
+ }
+
+ /**
+ * append a character to the current formatted line.
+ * Unless disabled (via canBreakLine == false), first check if a
+ * line-break has been registered, and if so break the
+ * formatted line, and only then append the character into
+ * the next formatted line.
+ *
+ * @param ch the character to append.
+ * @param canBreakLine if true, a registered line-break
+ */
+ void ASFormatter::appendChar(char ch, bool canBreakLine)
+ {
+ if (canBreakLine && isInLineBreak)
+ breakLine();
+ formattedLine.append(1, ch);
+ }
+
+ /**
+ * append the CURRENT character (curentChar)to the current
+ * formatted line. Unless disabled (via canBreakLine == false),
+ * first check if a line-break has been registered, and if so
+ * break the formatted line, and only then append the character
+ * into the next formatted line.
+ *
+ * @param canBreakLine if true, a registered line-break
+ */
+ void ASFormatter::appendCurrentChar(bool canBreakLine)
+ {
+ appendChar(currentChar, canBreakLine);
+ }
+
+ /**
+ * append a string sequence to the current formatted line.
+ * Unless disabled (via canBreakLine == false), first check if a
+ * line-break has been registered, and if so break the
+ * formatted line, and only then append the sequence into
+ * the next formatted line.
+ *
+ * @param sequence the sequence to append.
+ * @param canBreakLine if true, a registered line-break
+ */
+ void ASFormatter::appendSequence(const string &sequence, bool canBreakLine)
+ {
+ if (canBreakLine && isInLineBreak)
+ breakLine();
+ formattedLine.append(sequence);
+ }
+
+ /**
+ * append a space to the current formattedline, UNLESS the
+ * last character is already a white-space character.
+ */
+ void ASFormatter::appendSpacePad()
+ {
+ int len = formattedLine.length();
+ if (len == 0 || !isWhiteSpace(formattedLine[len-1]))
+ formattedLine.append(1, ' ');
+ }
+
+ /**
+ * register a line break for the formatted line.
+ */
+ void ASFormatter::breakLine()
+ {
+ isLineReady = true;
+ isInLineBreak = false;
+
+ // queue an empty line prepend request if one exists
+ prependEmptyLine = isPrependPostBlockEmptyLineRequested;
+
+ readyFormattedLine = formattedLine;
+ if (isAppendPostBlockEmptyLineRequested)
+ {
+ isAppendPostBlockEmptyLineRequested = false;
+ isPrependPostBlockEmptyLineRequested = true;
+ }
+ else
+ {
+ isPrependPostBlockEmptyLineRequested = false;
+ }
+
+ formattedLine = "";
+ }
+
+ /**
+ * check if the currently reached open-bracket (i.e. '{')
+ * opens a:
+ * - a definition type block (such as a class or namespace),
+ * - a command block (such as a method block)
+ * - a static array
+ * this method takes for granted that the current character
+ * is an opening bracket.
+ *
+ * @return the type of the opened block.
+ */
+ BracketType ASFormatter::getBracketType() const
+ {
+ BracketType returnVal;
+
+ if (foundPreDefinitionHeader)
+ returnVal = DEFINITION_TYPE;
+ else
+ {
+ bool isCommandType;
+ isCommandType = ( foundPreCommandHeader
+ || ( currentHeader != NULL && isNonParenHeader )
+ || ( previousCommandChar == ')' )
+ || ( previousCommandChar == ':' && !foundQuestionMark )
+ || ( previousCommandChar == ';' )
+ || ( ( previousCommandChar == '{' || previousCommandChar == '}')
+ && isPreviousBracketBlockRelated ) );
+
+ returnVal = (isCommandType ? COMMAND_TYPE : ARRAY_TYPE);
+ }
+
+ if (isOneLineBlockReached())
+ returnVal = (BracketType) (returnVal | SINGLE_LINE_TYPE);
+
+ return returnVal;
+ }
+
+ /**
+ * check if the currently reached '*' or '&' character is
+ * a pointer-or-reference symbol, or another operator.
+ * this method takes for granted that the current character
+ * is either a '*' or '&'.
+ *
+ * @return whether current character is a reference-or-pointer
+ */
+ bool ASFormatter::isPointerOrReference() const
+ {
+ bool isPR;
+ isPR = ( !isInPotentialCalculation
+ || IS_A(bracketTypeStack->back(), DEFINITION_TYPE)
+ || (!isLegalNameChar(previousNonWSChar)
+ && previousNonWSChar != ')'
+ && previousNonWSChar != ']')
+ );
+
+ if (!isPR)
+ {
+ char nextChar = peekNextChar();
+ isPR |= (!isWhiteSpace(nextChar)
+ && nextChar != '-'
+ && nextChar != '('
+ && nextChar != '['
+ && !isLegalNameChar(nextChar));
+ }
+
+ return isPR;
+ }
+
+
+ /**
+ * check if the currently reached '-' character is
+ * a urinary minus
+ * this method takes for granted that the current character
+ * is a '-'.
+ *
+ * @return whether the current '-' is a urinary minus.
+ */
+ bool ASFormatter::isUrinaryMinus() const
+ {
+ return ( (previousOperator == &AS_RETURN || !isalnum(previousCommandChar))
+ && previousCommandChar != '.'
+ && previousCommandChar != ')'
+ && previousCommandChar != ']' );
+ }
+
+
+ /**
+ * check if the currently reached '-' or '+' character is
+ * part of an exponent, i.e. 0.2E-5.
+ * this method takes for granted that the current character
+ * is a '-' or '+'.
+ *
+ * @return whether the current '-' is in an exponent.
+ */
+ bool ASFormatter::isInExponent() const
+ {
+ int formattedLineLength = formattedLine.length();
+ if (formattedLineLength >= 2)
+ {
+ char prevPrevFormattedChar = formattedLine[formattedLineLength - 2];
+ char prevFormattedChar = formattedLine[formattedLineLength - 1];
+
+ return ( (prevFormattedChar == 'e' || prevFormattedChar == 'E')
+ && (prevPrevFormattedChar == '.' || isdigit(prevPrevFormattedChar)) );
+ }
+ else
+ return false;
+ }
+
+ /**
+ * check if a one-line bracket has been reached,
+ * i.e. if the currently reached '{' character is closed
+ * with a complimentry '}' elsewhere on the current line,
+ *.
+ * @return has a one-line bracket been reached?
+ */
+ bool ASFormatter::isOneLineBlockReached() const
+ {
+ bool isInComment = false;
+ bool isInQuote = false;
+ int bracketCount = 1;
+ int currentLineLength = currentLine.length();
+ int i = 0;
+ char ch = ' ';
+ char quoteChar = ' ';
+
+ for (i = charNum + 1; i < currentLineLength; ++i)
+ {
+ ch = currentLine[i];
+
+ if (isInComment)
+ {
+ if (currentLine.COMPARE(i, 2, "*/") == 0)
+ {
+ isInComment = false;
+ ++i;
+ }
+ continue;
+ }
+
+ if (ch == '\\')
+ {
+ ++i;
+ continue;
+ }
+
+ if (isInQuote)
+ {
+ if (ch == quoteChar)
+ isInQuote = false;
+ continue;
+ }
+
+ if (ch == '"' || ch == '\'')
+ {
+ isInQuote = true;
+ quoteChar = ch;
+ continue;
+ }
+
+ if (currentLine.COMPARE(i, 2, "//") == 0)
+ break;
+
+ if (currentLine.COMPARE(i, 2, "/*") == 0)
+ {
+ isInComment = true;
+ ++i;
+ continue;
+ }
+
+ if (ch == '{')
+ ++bracketCount;
+ else if (ch == '}')
+ --bracketCount;
+
+ if(bracketCount == 0)
+ return true;
+ }
+
+ return false;
+ }
+
+
+ /**
+ * check if one of a set of headers has been reached in the
+ * current position of the current line.
+ *
+ * @return a pointer to the found header. Or a NULL if no header has been reached.
+ * @param headers a vector of headers
+ * @param checkBoundry
+ */
+ const string *ASFormatter::findHeader(const vector<const string*> &headers, bool checkBoundry)
+ {
+ return ASBeautifier::findHeader(currentLine, charNum, headers, checkBoundry);
+ }
+
+
+
+#ifdef USES_NAMESPACE
+}
+#endif
+/*
+ * Copyright (c) 1998,1999,2000,2001,2002 Tal Davidson. All rights reserved.
+ *
+ * compiler_defines.h (1 January 1999)
+ * by Tal Davidson (davidsont@bigfoot.com)
+ * This file is a part of "Artistic Style" - an indentater and reformatter
+ * of C, C++, C# and Java source files.
+ *
+ * The "Artistic Style" project, including all files needed to compile it,
+ * is free software; you can redistribute it and/or use it and/or modify it
+ * under the terms of the GNU General Public License as published
+ * by the Free Software Foundation; either version 2 of the License,
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this program.
+ */
+
+
+#ifndef ASFORMATTER_H
+#define ASFORMATTER_H
+
+#include "ASBeautifier.h"
+//#include "enums.h"
+#include "compiler_defines.h"
+
+namespace astyle {
+
+ class ASFormatter : public ASBeautifier
+ {
+ public:
+ ASFormatter();
+ virtual ~ASFormatter();
+ virtual void init(ASSourceIterator* iter);
+ virtual bool hasMoreLines() const;
+ virtual string nextLine();
+ void setBracketFormatMode(BracketMode mode);
+ void setBreakClosingHeaderBracketsMode(bool state);
+ void setOperatorPaddingMode(bool mode);
+ void setParenthesisPaddingMode(bool mode);
+ void setBreakOneLineBlocksMode(bool state);
+ void setSingleStatementsMode(bool state);
+ void setTabSpaceConversionMode(bool state);
+ void setBreakBlocksMode(bool state);
+ void setBreakClosingHeaderBlocksMode(bool state);
+ void setBreakElseIfsMode(bool state);
+
+ private:
+ void ASformatter(ASFormatter &copy); // not to be imlpemented
+ void operator=(ASFormatter&); // not to be implemented
+ void staticInit();
+ bool isFormattingEnabled() const;
+ void goForward(int i);
+ bool getNextChar();
+ char peekNextChar() const;
+ bool isBeforeComment() const;
+ void trimNewLine();
+ BracketType getBracketType() const;
+ bool isPointerOrReference() const;
+ bool isUrinaryMinus() const;
+ bool isInExponent() const;
+ bool isOneLineBlockReached() const;
+ void appendChar(char ch, bool canBreakLine = true);
+ void appendCurrentChar(bool canBreakLine = true);
+ void appendSequence(const string &sequence, bool canBreakLine = true);
+ void appendSpacePad();
+ void breakLine();
+ inline bool isSequenceReached(const string &sequence) const;
+ const string *findHeader(const vector<const string*> &headers, bool checkBoundry = true);
+
+ static vector<const string*> headers;
+ static vector<const string*> nonParenHeaders;
+ static vector<const string*> preprocessorHeaders;
+ static vector<const string*> preDefinitionHeaders;
+ static vector<const string*> preCommandHeaders;
+ static vector<const string*> operators;
+ static vector<const string*> assignmentOperators;
+ static bool calledInitStatic;
+
+ ASSourceIterator *sourceIterator;
+ vector<const string*> *preBracketHeaderStack;
+ vector<BracketType> *bracketTypeStack;
+ vector<int> *parenStack;
+ string readyFormattedLine;
+ string currentLine;
+ string formattedLine;
+ const string *currentHeader;
+ const string *previousOperator;
+ char currentChar;
+ char previousChar;
+ char previousNonWSChar;
+ char previousCommandChar;
+ char quoteChar;
+ unsigned int charNum;
+ BracketMode bracketFormatMode;
+ bool isVirgin;
+ bool shouldPadOperators;
+ bool shouldPadParenthesies;
+ bool shouldConvertTabs;
+ bool isInLineComment;
+ bool isInComment;
+ bool isInPreprocessor;
+ bool isInTemplate; // true both in template definitions (e.g. template<class A>) and template usage (e.g. F<int>).
+ bool doesLineStartComment;
+ bool isInQuote;
+ bool isSpecialChar;
+ bool isNonParenHeader;
+ bool foundQuestionMark;
+ bool foundPreDefinitionHeader;
+ bool foundPreCommandHeader;
+ bool isInLineBreak;
+ bool isInClosingBracketLineBreak;
+ bool endOfCodeReached;
+ bool isLineReady;
+ bool isPreviousBracketBlockRelated;
+ bool isInPotentialCalculation;
+ //bool foundOneLineBlock;
+ bool shouldBreakOneLineBlocks;
+ bool shouldReparseCurrentChar;
+ bool shouldBreakOneLineStatements;
+ bool shouldBreakLineAfterComments;
+ bool shouldBreakClosingHeaderBrackets;
+ bool shouldBreakElseIfs;
+ bool passedSemicolon;
+ bool passedColon;
+ bool isImmediatelyPostComment;
+ bool isImmediatelyPostLineComment;
+ bool isImmediatelyPostEmptyBlock;
+
+ bool shouldBreakBlocks;
+ bool shouldBreakClosingHeaderBlocks;
+ bool isPrependPostBlockEmptyLineRequested;
+ bool isAppendPostBlockEmptyLineRequested;
+
+ bool prependEmptyLine;
+ bool foundClosingHeader;
+ int previousReadyFormattedLineLength;
+
+ bool isInHeader;
+ bool isImmediatelyPostHeader;
+
+ };
+
+}
+
+#endif
+/*
+* Copyright (c) 1998,1999,2000,2001,2002 Tal Davidson. All rights reserved.
+*
+* ASResource.cpp
+* by Tal Davidson (davidsont@bigfoot.com)
+* This file is a part of "Artistic Style" - an indentater and reformatter
+* of C, C, C# and Java source files.
+*
+ * The "Artistic Style" project, including all files needed to compile it,
+ * is free software; you can redistribute it and/or use it and/or modify it
+ * under the terms of the GNU General Public License as published
+ * by the Free Software Foundation; either version 2 of the License,
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this program.
+*/
+
+#include "compiler_defines.h"
+#include "ASResource.h"
+
+#include <string>
+
+
+#ifdef USES_NAMESPACE
+using namespace std;
+
+namespace astyle
+ {
+#endif
+
+ const string ASResource::AS_IF = string("if");
+ const string ASResource::AS_ELSE = string ("else");
+ const string ASResource::AS_FOR = string("for");
+ const string ASResource::AS_DO = string("do");
+ const string ASResource::AS_WHILE = string("while");
+ const string ASResource::AS_SWITCH = string ("switch");
+ const string ASResource::AS_CASE = string ("case");
+ const string ASResource::AS_DEFAULT = string("default");
+ const string ASResource::AS_CLASS = string("class");
+ const string ASResource::AS_STRUCT = string("struct");
+ const string ASResource::AS_UNION = string("union");
+ const string ASResource::AS_INTERFACE = string("interface");
+ const string ASResource::AS_NAMESPACE = string("namespace");
+ const string ASResource::AS_EXTERN = string("extern");
+ const string ASResource::AS_PUBLIC = string("public");
+ const string ASResource::AS_PROTECTED = string("protected");
+ const string ASResource::AS_PRIVATE = string("private");
+ const string ASResource::AS_STATIC = string("static");
+ const string ASResource::AS_SYNCHRONIZED = string("synchronized");
+ const string ASResource::AS_OPERATOR = string("operator");
+ const string ASResource::AS_TEMPLATE = string("template");
+ const string ASResource::AS_TRY = string("try");
+ const string ASResource::AS_CATCH = string("catch");
+ const string ASResource::AS_FINALLY = string("finally");
+ const string ASResource::AS_THROWS = string("throws");
+ const string ASResource::AS_CONST = string("const");
+
+ const string ASResource::AS_ASM = string("asm");
+
+ const string ASResource::AS_BAR_DEFINE = string("#define");
+ const string ASResource::AS_BAR_INCLUDE = string("#include");
+ const string ASResource::AS_BAR_IF = string("#if");
+ const string ASResource::AS_BAR_EL = string("#el");
+ const string ASResource::AS_BAR_ENDIF = string("#endif");
+
+ const string ASResource::AS_OPEN_BRACKET = string("{");
+ const string ASResource::AS_CLOSE_BRACKET = string("}");
+ const string ASResource::AS_OPEN_LINE_COMMENT = string("//");
+ const string ASResource::AS_OPEN_COMMENT = string("/*");
+ const string ASResource::AS_CLOSE_COMMENT = string("*/");
+
+ const string ASResource::AS_ASSIGN = string("=");
+ const string ASResource::AS_PLUS_ASSIGN = string("+=");
+ const string ASResource::AS_MINUS_ASSIGN = string("-=");
+ const string ASResource::AS_MULT_ASSIGN = string("*=");
+ const string ASResource::AS_DIV_ASSIGN = string("/=");
+ const string ASResource::AS_MOD_ASSIGN = string("%=");
+ const string ASResource::AS_OR_ASSIGN = string("|=");
+ const string ASResource::AS_AND_ASSIGN = string("&=");
+ const string ASResource::AS_XOR_ASSIGN = string("^=");
+ const string ASResource::AS_GR_GR_ASSIGN = string(">>=");
+ const string ASResource::AS_LS_LS_ASSIGN = string("<<=");
+ const string ASResource::AS_GR_GR_GR_ASSIGN = string(">>>=");
+ const string ASResource::AS_LS_LS_LS_ASSIGN = string("<<<=");
+ const string ASResource::AS_RETURN = string("return");
+
+ const string ASResource::AS_EQUAL = string("==");
+ const string ASResource::AS_PLUS_PLUS = string("++");
+ const string ASResource::AS_MINUS_MINUS = string("--");
+ const string ASResource::AS_NOT_EQUAL = string("!=");
+ const string ASResource::AS_GR_EQUAL = string(">=");
+ const string ASResource::AS_GR_GR = string(">>");
+ const string ASResource::AS_GR_GR_GR = string(">>>");
+ const string ASResource::AS_LS_EQUAL = string("<=");
+ const string ASResource::AS_LS_LS = string("<<");
+ const string ASResource::AS_LS_LS_LS = string("<<<");
+ const string ASResource::AS_ARROW = string("->");
+ const string ASResource::AS_AND = string("&&");
+ const string ASResource::AS_OR = string("||");
+ const string ASResource::AS_COLON_COLON = string("::");
+ const string ASResource::AS_PAREN_PAREN = string("()");
+ const string ASResource::AS_BLPAREN_BLPAREN = string("[]");
+
+ const string ASResource::AS_PLUS = string("+");
+ const string ASResource::AS_MINUS = string("-");
+ const string ASResource::AS_MULT = string("*");
+ const string ASResource::AS_DIV = string("/");
+ const string ASResource::AS_MOD = string("%");
+ const string ASResource::AS_GR = string(">");
+ const string ASResource::AS_LS = string("<");
+ const string ASResource::AS_NOT = string("!");
+ const string ASResource::AS_BIT_OR = string("|");
+ const string ASResource::AS_BIT_AND = string("&");
+ const string ASResource::AS_BIT_NOT = string("~");
+ const string ASResource::AS_BIT_XOR = string("^");
+ const string ASResource::AS_QUESTION = string("?");
+ const string ASResource::AS_COLON = string(":");
+ const string ASResource::AS_COMMA = string(",");
+ const string ASResource::AS_SEMICOLON = string(";");
+
+ const string ASResource::AS_FOREACH = string("foreach");
+ const string ASResource::AS_LOCK = string("lock");
+ const string ASResource::AS_UNSAFE = string("unsafe");
+ const string ASResource::AS_FIXED = string("fixed");
+ const string ASResource::AS_GET = string("get");
+ const string ASResource::AS_SET = string("set");
+ const string ASResource::AS_ADD = string("add");
+ const string ASResource::AS_REMOVE = string("remove");
+
+#ifdef USES_NAMESPACE
+}
+#endif
+
+
+/*
+ * Copyright (c) 1998,1999,2000,2001,2002 Tal Davidson. All rights reserved.
+ *
+ * compiler_defines.h (1 January 1999)
+ * by Tal Davidson (davidsont@bigfoot.com)
+ * This file is a part of "Artistic Style" - an indentater and reformatter
+ * of C, C++, C# and Java source files.
+ *
+ * The "Artistic Style" project, including all files needed to compile it,
+ * is free software; you can redistribute it and/or use it and/or modify it
+ * under the terms of the GNU General Public License as published
+ * by the Free Software Foundation; either version 2 of the License,
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this program.
+ */
+
+
+#ifndef ASRES_H
+#define ASRES_H
+
+#include "compiler_defines.h"
+#include "ASStreamIterator.h"
+
+#include <iostream>
+#include <fstream>
+#include <string>
+
+namespace astyle {
+
+class ASResource
+ {
+ public:
+ static const string AS_IF, AS_ELSE;
+ static const string AS_DO, AS_WHILE;
+ static const string AS_FOR;
+ static const string AS_SWITCH, AS_CASE, AS_DEFAULT;
+ static const string AS_TRY, AS_CATCH, AS_THROWS, AS_FINALLY;
+ static const string AS_PUBLIC, AS_PROTECTED, AS_PRIVATE;
+ static const string AS_CLASS, AS_STRUCT, AS_UNION, AS_INTERFACE, AS_NAMESPACE, AS_EXTERN;
+ static const string AS_STATIC;
+ static const string AS_CONST;
+ static const string AS_SYNCHRONIZED;
+ static const string AS_OPERATOR, AS_TEMPLATE;
+ static const string AS_OPEN_BRACKET, AS_CLOSE_BRACKET;
+ static const string AS_OPEN_LINE_COMMENT, AS_OPEN_COMMENT, AS_CLOSE_COMMENT;
+ static const string AS_BAR_DEFINE, AS_BAR_INCLUDE, AS_BAR_IF, AS_BAR_EL, AS_BAR_ENDIF;
+ static const string AS_RETURN;
+ static const string AS_ASSIGN, AS_PLUS_ASSIGN, AS_MINUS_ASSIGN, AS_MULT_ASSIGN;
+ static const string AS_DIV_ASSIGN, AS_MOD_ASSIGN, AS_XOR_ASSIGN, AS_OR_ASSIGN, AS_AND_ASSIGN;
+ static const string AS_GR_GR_ASSIGN, AS_LS_LS_ASSIGN, AS_GR_GR_GR_ASSIGN, AS_LS_LS_LS_ASSIGN;
+ static const string AS_EQUAL, AS_PLUS_PLUS, AS_MINUS_MINUS, AS_NOT_EQUAL, AS_GR_EQUAL, AS_GR_GR_GR, AS_GR_GR;
+ static const string AS_LS_EQUAL, AS_LS_LS_LS, AS_LS_LS, AS_ARROW, AS_AND, AS_OR;
+ static const string AS_COLON_COLON, AS_PAREN_PAREN, AS_BLPAREN_BLPAREN;
+ static const string AS_PLUS, AS_MINUS, AS_MULT, AS_DIV, AS_MOD, AS_GR, AS_LS;
+ static const string AS_NOT, AS_BIT_XOR, AS_BIT_OR, AS_BIT_AND, AS_BIT_NOT;
+ static const string AS_QUESTION, AS_COLON, AS_SEMICOLON, AS_COMMA;
+ static const string AS_ASM;
+ static const string AS_FOREACH, AS_LOCK, AS_UNSAFE, AS_FIXED;
+ static const string AS_GET, AS_SET, AS_ADD, AS_REMOVE;
+ };
+}
+#endif
+
+#ifndef ASSOURCEITERATOR_H
+#define ASSOURCEITERATOR_H
+
+#include <string>
+#include "compiler_defines.h"
+
+namespace astyle
+ {
+
+ class ASSourceIterator
+ {
+ public:
+ virtual bool hasMoreLines() const = 0;
+ virtual std::string nextLine() = 0;
+ };
+}
+
+#endif
+#include "compiler_defines.h"
+#include "ASStreamIterator.h"
+
+#include <iostream>
+#include <fstream>
+#include <string>
+
+using namespace astyle;
+
+ASStreamIterator::ASStreamIterator(istream *in)
+{
+ inStream = in;
+}
+
+ASStreamIterator::~ASStreamIterator()
+{
+ delete inStream;
+}
+
+
+bool ASStreamIterator::hasMoreLines() const
+ {
+ if (*inStream)
+ return true;
+ else
+ return false;
+ }
+
+/*
+string ASStreamIterator::nextLine()
+{
+ char theInChar;
+ char peekedChar;
+ int theBufferPosn = 0;
+
+ //
+ // treat '\n', '\r', '\n\r' and '\r\n' as an endline.
+ //
+ while (theBufferPosn < 2047 && inStream->get(theInChar))
+ // while not eof
+ {
+ if (theInChar != '\n' && theInChar != '\r')
+ {
+ buffer[theBufferPosn] = theInChar;
+ theBufferPosn++;
+ }
+ else
+ {
+ peekedChar = inStream->peek();
+ if (peekedChar != theInChar && (peekedChar == '\r' || peekedChar == '\n') )
+ {
+ inStream->get(theInChar);
+ }
+ break;
+ }
+ }
+ buffer[theBufferPosn] = '\0';
+
+ return string(buffer);
+}
+*/
+
+
+string ASStreamIterator::nextLine()
+{
+ char *srcPtr;
+ char *filterPtr;
+
+ inStream->getline(buffer, 2047);
+ srcPtr = filterPtr = buffer;
+
+ while (*srcPtr != 0)
+ {
+ if (*srcPtr != '\r')
+ *filterPtr++ = *srcPtr;
+ srcPtr++;
+ }
+ *filterPtr = 0;
+
+ return string(buffer);
+}
+
+/*
+ * Copyright (c) 1998,1999,2000,2001,2002 Tal Davidson. All rights reserved.
+ *
+ * compiler_defines.h (1 January 1999)
+ * by Tal Davidson (davidsont@bigfoot.com)
+ * This file is a part of "Artistic Style" - an indentater and reformatter
+ * of C, C++, C# and Java source files.
+ *
+ * The "Artistic Style" project, including all files needed to compile it,
+ * is free software; you can redistribute it and/or use it and/or modify it
+ * under the terms of the GNU General Public License as published
+ * by the Free Software Foundation; either version 2 of the License,
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this program.
+ */
+
+
+#ifndef ASSTREAMITERATOR_H
+#define ASSTREAMITERATOR_H
+
+#include "ASSourceIterator.h"
+
+using namespace std;
+
+namespace astyle
+ {
+ class ASStreamIterator :
+ public ASSourceIterator
+ {
+ public:
+ ASStreamIterator(istream *in);
+ virtual ~ASStreamIterator();
+ bool hasMoreLines() const;
+ string nextLine();
+
+ private:
+ istream * inStream;
+ char buffer[2048];
+ };
+
+}
+
+#endif
+/***************************************************************************
+ charcodes.cpp - description
+ -------------------
+ begin : Wed Nov 24 2003
+ copyright : (C) 2003 by André imon
+ email : andre.simon1@gmx.de
+ ***************************************************************************/
+
+/***************************************************************************
+ * *
+ * 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 of the License, or *
+ * (at your option) any later version. *
+ * *
+ ***************************************************************************/
+
+// FILE SHOULD BE REMOVED FROM PROJECT
+
+#ifndef CHAR_CODES
+#define CHAR_CODES
+
+#ifdef _WIN32
+
+#define AUML_LC 228
+#define OUML_LC 246
+#define UUML_LC 252
+
+#define AUML_UC 196
+#define OUML_UC 214
+#define UUML_UC 220
+
+
+#define AACUTE_LC 225
+#define EACUTE_LC 233
+#define OACUTE_LC 243
+#define UACUTE_LC 250
+
+#define AACUTE_UC 193
+#define EACUTE_UC 201
+#define OACUTE_UC 211
+#define UACUTE_UC 218
+
+#define AGRAVE_LC 224
+#define EGRAVE_LC 232
+#define OGRAVE_LC 242
+#define UGRAVE_LC 249
+
+#define AGRAVE_UC 192
+#define EGRAVE_UC 200
+#define OGRAVE_UC 210
+#define UGRAVE_UC 217
+
+#define SZLIG 223
+
+/* DOS CONSOLE CODES
+#define AUML_LC 132
+#define OUML_LC 148
+#define UUML_LC 129
+
+#define AUML_UC 142
+#define OUML_UC 153
+#define UUML_UC 154
+
+
+#define AACUTE_LC 160
+#define EACUTE_LC 130
+#define OACUTE_LC 162
+#define UACUTE_LC 163
+
+#define AACUTE_UC 181
+#define EACUTE_UC 144
+#define OACUTE_UC 224
+#define UACUTE_UC 233
+
+#define AGRAVE_LC 133
+#define EGRAVE_LC 138
+#define OGRAVE_LC 149
+#define UGRAVE_LC 151
+
+#define AGRAVE_UC 183
+#define EGRAVE_UC 212
+#define OGRAVE_UC 227
+#define UGRAVE_UC 235
+
+#define SZLIG 225
+*/
+
+#else
+
+#define AUML_LC 164
+#define OUML_LC 182
+#define UUML_LC 188
+
+#define AUML_UC 132
+#define OUML_UC 150
+#define UUML_UC 156
+
+
+#define AACUTE_LC 161
+#define EACUTE_LC 169
+#define OACUTE_LC 179
+#define UACUTE_LC 186
+
+#define AACUTE_UC 129
+#define EACUTE_UC 137
+#define OACUTE_UC 147
+#define UACUTE_UC 154
+
+#define AGRAVE_LC 160
+#define EGRAVE_LC 168
+#define OGRAVE_LC 178
+#define UGRAVE_LC 185
+
+#define AGRAVE_UC 128
+#define EGRAVE_UC 136
+#define OGRAVE_UC 146
+#define UGRAVE_UC 153
+
+#define SZLIG 159
+
+#endif
+
+#endif
+/***************************************************************************
+ cmdlineoptions.cpp - description
+ -------------------
+ begin : Sun Nov 25 2001
+ copyright : (C) 2001 by André Simon
+ email : andre.simon1@gmx.de
+ ***************************************************************************/
+
+/***************************************************************************
+ * *
+ * 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 of the License, or *
+ * (at your option) any later version. *
+ * *
+ ***************************************************************************/
+
+#include "cmdlineoptions.h"
+
+using namespace std;
+
+/* Siehe man getopt (3)
+ Konstruktor legt Optionen und Argumente fest
+*/
+CmdLineOptions::CmdLineOptions(int argc, char *argv[]):
+ numberSpaces(0),
+ wrappingStyle(highlight::WRAP_DISABLED),
+ outputType (highlight::HTML),
+ opt_language (false),
+ opt_include_style (false),
+ opt_help (false),
+ opt_version (false),
+ opt_verbose (false),
+ opt_linenumbers (false),
+ opt_style (false),
+ opt_batch_mode (false),
+ opt_fragment (false) ,
+ opt_attach_line_anchors (false),
+ opt_show_themes (false),
+ opt_show_langdefs (false),
+ opt_printindex(false),
+ opt_quiet(false),
+ opt_xslfo_fop(false),
+ opt_replacequotes(false),
+ opt_print_progress(false),
+ opt_fill_zeroes(false),
+ opt_stylepath_explicit(false),
+ opt_force_output(false),
+ configFileRead(false),
+ helpLang("en"),
+ charset("ISO-8859-1")
+{
+
+ loadConfigurationFile();
+
+ int c, option_index = 0;
+ static struct option long_options[] =
+ {
+ {OPT_OUT, 1, 0, S_OPT_OUT},
+ {OPT_IN, 1, 0, S_OPT_IN},
+ {OPT_SYNTAX, 1, 0, S_OPT_SYNTAX},
+ {OPT_VERBOSE, 0, 0, S_OPT_VERBOSE},
+ {OPT_INC_STYLE, 0, 0, S_OPT_INC_STYLE},
+ {OPT_HELP, 0, 0, S_OPT_HELP},
+ {OPT_HELPINT, 1, 0, S_OPT_HELPINT},
+ {OPT_LINENO,0,0,S_OPT_LINENO},
+ {OPT_STYLE, 1,0,S_OPT_STYLE},
+ {OPT_STYLE_OUT, 1, 0,S_OPT_STYLE_OUT},
+ {OPT_STYLE_IN, 1, 0,S_OPT_STYLE_IN},
+ {OPT_DELTABS,1,0,S_OPT_DELTABS},
+ {OPT_XHTML, 0,0,S_OPT_XHTML},
+ {OPT_RTF, 0,0,S_OPT_RTF},
+ {OPT_TEX,0, 0,S_OPT_TEX},
+ {OPT_LATEX,0, 0,S_OPT_LATEX},
+ {OPT_XSLFO,0, 0,S_OPT_XSLFO},
+ {OPT_ANSI,0, 0,S_OPT_ANSI},
+ {OPT_XML,0, 0,S_OPT_XML},
+ {OPT_BATCHREC,1,0,S_OPT_BATCHREC},
+ {OPT_FRAGMENT,0,0,S_OPT_FRAGMENT},
+ {OPT_ANCHORS, 0,0,S_OPT_ANCHORS },
+ {OPT_LISTTHEMES, 0,0,S_OPT_LISTTHEMES },
+ {OPT_LISTLANGS, 0,0,S_OPT_LISTLANGS },
+ {OPT_OUTDIR,1,0,S_OPT_OUTDIR},
+ {OPT_VERSION,0,0,0},
+ {OPT_FORMATSTYLE,1,0,S_OPT_FORMATSTYLE},
+ {OPT_DATADIR,1,0,S_OPT_DATADIR},
+ {OPT_ADDDATADIR,1,0,S_OPT_ADDDATADIR},
+ {OPT_INDEXFILE,0,0,S_OPT_INDEXFILE},
+ {OPT_WRAP,0,0,S_OPT_WRAP},
+ {OPT_WRAPSIMPLE,0,0,S_OPT_WRAPSIMPLE},
+ {OPT_QUIET,0,0,S_OPT_QUIET},
+ {OPT_REPLACE_QUOTES,0,0,S_OPT_REPLACE_QUOTES},
+ {OPT_PROGRESSBAR,0,0,S_OPT_PROGRESSBAR},
+ {OPT_FILLZEROES,0,0,S_OPT_FILLZEROES},
+ {OPT_ENCODING,1,0,S_OPT_ENCODING},
+
+ //remove as soon as APAche fixes the bug in FOP (0.20.5)
+ {OPT_FOP,0,0,S_OPT_FOP},
+
+ //deprecated
+ {OPT_CSSOUT,1,0,0},
+ {OPT_CSSIN,1,0,0},
+ {OPT_INC_CSS,0,0,0},
+ {OPT_FORCE_OUTPUT,0,0,0},
+
+ {0, 0, 0, 0}
+ };
+
+ while (1)
+ {
+ c = getopt_long (argc, argv,S_OPTIONS_STRING,long_options, &option_index);
+ if (c == -1)
+ break;
+
+ switch (c)
+ {
+ case 0: // long options
+ if (long_options[option_index].name==OPT_VERSION) {
+ opt_version = true;
+ }
+ if (long_options[option_index].name==OPT_CSSOUT) {
+ styleOutFilename=string(optarg);
+ printDeprecatedWarning(OPT_CSSOUT, OPT_STYLE_OUT);
+ }
+ if (long_options[option_index].name==OPT_CSSIN) {
+ styleInFilename=string(optarg);
+ printDeprecatedWarning(OPT_CSSIN, OPT_STYLE_IN);
+ }
+ if (long_options[option_index].name==OPT_INC_CSS) {
+ opt_include_style = true;
+ printDeprecatedWarning(OPT_INC_CSS, OPT_INC_STYLE);
+ }
+ if (long_options[option_index].name==OPT_FORCE_OUTPUT) {
+ opt_force_output = true;
+ }
+ break;
+ case S_OPT_OUT:
+ outFilename=string(optarg);
+ break;
+ case S_OPT_IN:
+ inputFileNames.push_back(string(optarg));
+ break;
+ case S_OPT_STYLE_OUT:
+ styleOutFilename=string(optarg);
+ opt_stylepath_explicit=true;
+ break;
+ case S_OPT_STYLE_IN:
+ styleInFilename=string(optarg);
+ break;
+ case S_OPT_VERBOSE:
+ opt_verbose = true;
+ break;
+ case S_OPT_QUIET:
+ opt_quiet = true;
+ break;
+ case S_OPT_INC_STYLE:
+ opt_include_style = true;
+ break;
+ case S_OPT_HELPINT:
+ helpLang=string(optarg);
+ case S_OPT_HELP:
+ opt_help = true;
+ break;
+ case S_OPT_LINENO:
+ opt_linenumbers = true;
+ break;
+ case '?':
+ //opt_help = true;
+ break;
+ case S_OPT_STYLE:
+ styleName=string(optarg);
+ opt_style = true;
+ break;
+ case S_OPT_SYNTAX:
+ language=string(optarg);
+ opt_language = true;
+ break;
+ case S_OPT_DELTABS:
+ numberSpaces = StringTools::str2int (string(optarg));
+ break;
+ case S_OPT_XHTML:
+ outputType=highlight::XHTML;
+ break;
+ case S_OPT_RTF:
+ outputType=highlight::RTF;
+ break;
+ case S_OPT_TEX:
+ outputType=highlight::TEX;
+ break;
+ case S_OPT_LATEX:
+ outputType=highlight::LATEX;
+ break;
+ case S_OPT_XSLFO:
+ outputType=highlight::XSLFO;
+ break;
+ case S_OPT_ANSI:
+ outputType=highlight::ANSI;
+ break;
+ case S_OPT_XML:
+ outputType=highlight::XML;
+ break;
+ case S_OPT_BATCHREC:
+ opt_batch_mode = true;
+ readDirectory(string(optarg));
+ break;
+ case S_OPT_FRAGMENT:
+ opt_fragment = true;
+ break;
+ case S_OPT_ANCHORS:
+ opt_attach_line_anchors = true;
+ break;
+ case S_OPT_LISTTHEMES:
+ opt_show_themes = true;
+ break;
+ case S_OPT_LISTLANGS:
+ opt_show_langdefs = true;
+ break;
+ case S_OPT_OUTDIR:
+ outDirectory = validateDirPath(string(optarg));
+ break;
+ case S_OPT_FORMATSTYLE:
+ indentScheme =string(optarg);
+ break;
+ case S_OPT_ENCODING:
+ charset =string(optarg);
+ break;
+ case S_OPT_DATADIR:
+ dataDir=validateDirPath(string(optarg));
+ break;
+ case S_OPT_ADDDATADIR:
+ additionalDataDir=validateDirPath(string(optarg));
+ break;
+ case S_OPT_INDEXFILE:
+ opt_printindex=true;
+ break;
+ case S_OPT_WRAPSIMPLE:
+ wrappingStyle = highlight::WRAP_SIMPLE;
+ break;
+ case S_OPT_WRAP:
+ wrappingStyle = highlight::WRAP_DEFAULT;
+ break;
+ case S_OPT_FOP:
+ opt_xslfo_fop=true;
+ break;
+ case S_OPT_REPLACE_QUOTES:
+ opt_replacequotes=true;
+ break;
+ case S_OPT_PROGRESSBAR:
+ opt_print_progress=true;
+ break;
+ case S_OPT_FILLZEROES:
+ opt_fill_zeroes=true;
+ break;
+ default:
+ cerr <<"higlight: Unknown option " <<c<< endl;
+ }
+ }
+
+ if (optind < argc) //still args left
+ {
+ if (inputFileNames.empty()) {
+ while (optind < argc){
+ inputFileNames.push_back(string(argv[optind++]));
+ }
+ }
+ } else if (inputFileNames.empty()) {
+ inputFileNames.push_back("");
+ }
+ if (printDebugInfo() && configFileRead) {
+ cout << "Configuration file \""<<configFilePath<<"\" was read.\n";
+ }
+}
+
+CmdLineOptions::~CmdLineOptions(){
+}
+
+const string &CmdLineOptions::getSingleOutFilename()
+ {
+ if (!inputFileNames.empty() && !outDirectory.empty()) {
+ if (outFilename.empty()) {
+ outFilename = outDirectory;
+ int delim = getSingleInFilename().find_last_of(Platform::pathSeparator)+1;
+ outFilename += getSingleInFilename().substr((delim>-1)?delim:0)
+ + getOutFileSuffix();
+ }
+ }
+ return outFilename;
+ }
+
+const string &CmdLineOptions::getSingleInFilename() const
+ {
+ return inputFileNames[0];
+ }
+
+const string &CmdLineOptions::getOutDirectory()
+ {
+ if (!outFilename.empty() && !enableBatchMode()){
+ outDirectory=getDirName(outFilename);
+ }
+ return outDirectory;
+ }
+
+const string CmdLineOptions::getStyleOutFilename() const
+ {
+ if (!styleOutFilename.empty()) return styleOutFilename;
+ return (outputType==highlight::HTML ||
+ outputType==highlight::XHTML)? "highlight.css":"highlight.sty";
+ }
+const string &CmdLineOptions::getStyleInFilename() const
+ {
+ return styleInFilename;
+ }
+int CmdLineOptions::getNumberSpaces() const
+ {
+ return numberSpaces;
+ }
+bool CmdLineOptions::printVersion()const
+ {
+ return opt_version;
+ }
+bool CmdLineOptions::printHelp()const
+ {
+ return opt_help;
+ }
+bool CmdLineOptions::printDebugInfo()const
+ {
+ return opt_verbose;
+ }
+bool CmdLineOptions::quietMode()const
+ {
+ return opt_quiet;
+ }
+bool CmdLineOptions::includeStyleDef()const
+ {
+ return opt_include_style;
+ }
+
+bool CmdLineOptions::formatSupportsExtStyle(){
+ return outputType==highlight::HTML ||
+ outputType==highlight::XHTML ||
+ outputType==highlight::LATEX ||
+ outputType==highlight::TEX;
+}
+
+bool CmdLineOptions::printLineNumbers()const
+ {
+ return opt_linenumbers;
+ }
+
+string CmdLineOptions::getStyleName()const
+ {
+ return ( ( opt_style) ? styleName+".style" : "kwrite.style" );
+ }
+bool CmdLineOptions::enableBatchMode()const{
+ return inputFileNames.size()>1 || opt_batch_mode;
+}
+bool CmdLineOptions::fragmentOutput()const{
+ return opt_fragment;
+}
+string CmdLineOptions::getOutFileSuffix()const{
+ switch (outputType){
+ case highlight::XHTML: return ".xhtml";
+ case highlight::RTF: return ".rtf";
+ case highlight::TEX:
+ case highlight::LATEX: return ".tex";
+ case highlight::XSLFO: return ".fo";
+ case highlight::XML: return ".xml";
+ default: return ".html";
+ }
+}
+string CmdLineOptions::getDirName(const string & path) {
+ size_t dirNameLength=path.rfind(Platform::pathSeparator);
+ return (dirNameLength==string::npos)?string():path.substr(0, dirNameLength+1);
+}
+bool CmdLineOptions::attachLineAnchors()const{
+ return opt_attach_line_anchors;
+}
+bool CmdLineOptions::showThemes()const{
+ return opt_show_themes;
+}
+bool CmdLineOptions::showLangdefs()const{
+ return opt_show_langdefs;
+}
+bool CmdLineOptions::outDirGiven()const{
+ return !outFilename.empty();
+}
+bool CmdLineOptions::fopCompatible() const {
+ return opt_xslfo_fop;
+}
+bool CmdLineOptions::replaceQuotes() const {
+ return opt_replacequotes;
+}
+bool CmdLineOptions::getFlag( const string& paramVal){
+ return StringTools::lowerCase(paramVal)=="true";
+}
+bool CmdLineOptions::formattingEnabled(){
+ return !indentScheme.empty();
+}
+bool CmdLineOptions::dataDirGiven()const {
+ return !dataDir.empty();
+}
+bool CmdLineOptions::additionalDataDirGiven()const {
+ return !additionalDataDir.empty();
+}
+const string &CmdLineOptions::getDataDir() const {
+ return dataDir;
+}
+const string &CmdLineOptions::getIndentScheme() const {
+ return indentScheme;
+}
+const string &CmdLineOptions::getAdditionalDataDir()const{
+ return additionalDataDir;
+}
+const string &CmdLineOptions::getLanguage() const {
+ return language;
+}
+const string&CmdLineOptions::getCharSet() const{
+ return charset;
+}
+bool CmdLineOptions::printIndexFile() const{
+ return opt_printindex && (outputType==highlight::HTML ||
+ outputType==highlight::XHTML);
+}
+bool CmdLineOptions::printProgress() const{
+ return opt_print_progress;
+}
+bool CmdLineOptions::fillLineNrZeroes() const{
+ return opt_fill_zeroes;
+}
+bool CmdLineOptions::syntaxGiven() const{
+ return opt_language;
+}
+bool CmdLineOptions::omitEncodingName() const{
+ return StringTools::lowerCase(charset)=="none";
+}
+bool CmdLineOptions::forceOutput() const{
+ return opt_force_output;
+}
+const string CmdLineOptions::getHelpLang()const{
+ return helpLang+".help";
+}
+highlight::WrapMode CmdLineOptions::getWrappingStyle() const {
+ return wrappingStyle;
+}
+const vector <string> & CmdLineOptions::getInputFileNames() const{
+ return inputFileNames;
+}
+void CmdLineOptions::readDirectory(const string & wildcard){
+ // get matching files, use recursive search
+ bool directoryOK=Platform::getDirectoryEntries(inputFileNames, wildcard, true);
+ if (!directoryOK)
+ {
+ cerr << "highlight: No files matched the pattern \""
+ << wildcard << "\"."<< endl;
+ }
+}
+void CmdLineOptions::loadConfigurationFile()
+{
+ #ifndef _WIN32
+ #ifdef CONFIG_FILE_PATH
+ configFilePath=CONFIG_FILE_PATH;
+ #else
+ char* homeEnv=getenv("HOME");
+ if (homeEnv==NULL) return;
+ configFilePath=string(homeEnv)+"/.highlightrc";
+ #endif
+ #else
+ configFilePath = Platform::getAppPath() + "highlight.conf";
+ #endif
+ ConfigurationReader presets(configFilePath);
+
+ if (presets.found())
+ {
+ string paramVal;
+ configFileRead=true;
+
+ styleOutFilename = presets.getParameter(OPT_STYLE_OUT);
+ styleInFilename = presets.getParameter(OPT_STYLE_IN);
+ styleName = presets.getParameter(OPT_STYLE);
+ opt_style = !styleName.empty();
+ language = presets.getParameter(OPT_SYNTAX);
+ opt_language = !language.empty();
+ numberSpaces = StringTools::str2int(presets.getParameter(OPT_DELTABS));
+ indentScheme = presets.getParameter(OPT_FORMATSTYLE);
+
+ paramVal = presets.getParameter(OPT_DATADIR);
+ if (!paramVal.empty()) {
+ dataDir=validateDirPath( paramVal);
+ }
+ paramVal = presets.getParameter(OPT_ADDDATADIR);
+ if (!paramVal.empty()) {
+ additionalDataDir=validateDirPath(paramVal);
+ }
+ paramVal = presets.getParameter(OPT_OUTDIR);
+ if (!paramVal.empty()) {
+ outDirectory=validateDirPath(paramVal);
+ }
+ paramVal = presets.getParameter(OPT_ENCODING);
+ if (!paramVal.empty()) {
+ charset=paramVal;
+ }
+
+ opt_include_style=getFlag(presets.getParameter(OPT_INC_STYLE));
+ opt_verbose=getFlag(presets.getParameter(OPT_VERBOSE));
+ opt_linenumbers=getFlag(presets.getParameter(OPT_LINENO));
+ opt_fragment=getFlag(presets.getParameter(OPT_FRAGMENT));
+ opt_attach_line_anchors=getFlag(presets.getParameter(OPT_ANCHORS));
+ opt_printindex=getFlag(presets.getParameter(OPT_INDEXFILE));
+ opt_quiet=getFlag(presets.getParameter(OPT_QUIET));
+ opt_xslfo_fop=getFlag(presets.getParameter(OPT_FOP));
+ opt_replacequotes=getFlag(presets.getParameter(OPT_REPLACE_QUOTES));
+ opt_print_progress=getFlag(presets.getParameter(OPT_PROGRESSBAR));
+ opt_fill_zeroes=getFlag(presets.getParameter(OPT_FILLZEROES));
+
+ if (getFlag(presets.getParameter(OPT_WRAP))) {
+ wrappingStyle=highlight::WRAP_DEFAULT;
+ }
+ if (getFlag(presets.getParameter(OPT_WRAPSIMPLE))) {
+ wrappingStyle=highlight::WRAP_SIMPLE;
+ }
+ if (getFlag(presets.getParameter(OPT_XHTML))) {
+ outputType=highlight::XHTML;
+ } else if (getFlag(presets.getParameter(OPT_RTF))) {
+ outputType=highlight::RTF;
+ } else if (getFlag(presets.getParameter(OPT_TEX))) {
+ outputType=highlight::TEX;
+ } else if (getFlag(presets.getParameter(OPT_LATEX))) {
+ outputType=highlight::LATEX;
+ } else if (getFlag(presets.getParameter(OPT_XSLFO))) {
+ outputType=highlight::XSLFO;
+ } else if (getFlag(presets.getParameter(OPT_ANSI))) {
+ outputType=highlight::ANSI;
+ } else if (getFlag(presets.getParameter(OPT_XML))) {
+ outputType=highlight::XML;
+ }
+ }
+}
+
+string CmdLineOptions::validateDirPath(const string & path){
+ return (path[path.length()-1] !=Platform::pathSeparator)?
+ path+Platform::pathSeparator : path;
+}
+
+highlight::OutputType CmdLineOptions::getOutputType() const {
+ return outputType;
+}
+
+void CmdLineOptions::printDeprecatedWarning(const char *oldOption, const char *newOption){
+ cerr << "Warning: Long option \""<<oldOption << "\" is DEPRECATED.";
+ cerr << " Use \""<<newOption << "\" instead.\n";
+}
+/***************************************************************************
+ cmdlineoptions.h - description
+ -------------------
+ begin : Sun Nov 25 2001
+ copyright : (C) 2001 by André Simon
+ email : andre.simon1@gmx.de
+ ***************************************************************************/
+
+/***************************************************************************
+ * *
+ * 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 of the License, or *
+ * (at your option) any later version. *
+ * *
+ ***************************************************************************/
+
+#ifndef CMDLINEOPTIONS_H
+#define CMDLINEOPTIONS_H
+
+#include <string>
+#include <map>
+#include <cstdlib>
+#include <iostream>
+#include <fstream>
+
+#include "platform_fs.h"
+#include "configurationreader.h"
+#include "datadir.h"
+#include "enums.h"
+
+#ifdef _WIN32
+ #include <windows.h>
+#endif
+
+// If your system does not know getopt_long, define USE_LOCAL_GETOPT
+#if defined(_WIN32) || defined(__SVR4) || defined(__sun__)
+ // some compilers don't like redefinitions...
+ #ifndef USE_LOCAL_GETOPT
+ #define USE_LOCAL_GETOPT
+ #endif
+#endif
+
+#ifdef USE_LOCAL_GETOPT
+ #include "getopt.h"
+#else
+ #include <getopt.h>
+#endif
+
+#define OPT_VERBOSE "verbose"
+#define OPT_INC_STYLE "include-style"
+#define OPT_HELP "help"
+#define OPT_LINENO "linenumbers"
+#define OPT_XHTML "xhtml"
+#define OPT_RTF "rtf"
+#define OPT_TEX "tex"
+#define OPT_LATEX "latex"
+#define OPT_XSLFO "xsl-fo"
+#define OPT_FRAGMENT "fragment"
+#define OPT_ANCHORS "anchors"
+#define OPT_LISTTHEMES "list-themes"
+#define OPT_LISTLANGS "list-langs"
+#define OPT_VERSION "version"
+#define OPT_IN "input"
+#define OPT_OUT "output"
+#define OPT_SYNTAX "syntax"
+#define OPT_STYLE "style"
+#define OPT_STYLE_OUT "style-outfile"
+#define OPT_STYLE_IN "style-infile"
+
+#define OPT_DELTABS "replace-tabs"
+#define OPT_BATCHREC "batch-recursive"
+#define OPT_OUTDIR "outdir"
+#define OPT_FORMATSTYLE "format-style"
+#define OPT_DATADIR "data-dir"
+#define OPT_ADDDATADIR "add-data-dir"
+#define OPT_INDEXFILE "print-index"
+#define OPT_HELPINT "help-int"
+#define OPT_WRAP "wrap"
+#define OPT_WRAPSIMPLE "wrap-simple"
+#define OPT_QUIET "quiet"
+#define OPT_REPLACE_QUOTES "replace-quotes"
+#define OPT_FOP "fop-compatible"
+#define OPT_PROGRESSBAR "progress"
+#define OPT_FILLZEROES "zeroes"
+#define OPT_ANSI "ansi"
+#define OPT_XML "xml"
+#define OPT_ENCODING "encoding"
+#define OPT_FORCE_OUTPUT "force"
+
+#define S_OPT_ANSI 'A'
+#define S_OPT_OUT 'o'
+#define S_OPT_IN 'i'
+#define S_OPT_SYNTAX 'S'
+#define S_OPT_VERBOSE 'v'
+#define S_OPT_INC_STYLE 'I'
+#define S_OPT_HELP 'h'
+#define S_OPT_HELPINT 'H'
+#define S_OPT_LINENO 'l'
+#define S_OPT_STYLE 's'
+#define S_OPT_STYLE_OUT 'c'
+#define S_OPT_STYLE_IN 'e'
+#define S_OPT_DELTABS 't'
+#define S_OPT_XHTML 'X'
+#define S_OPT_RTF 'R'
+#define S_OPT_TEX 'T'
+#define S_OPT_LATEX 'L'
+#define S_OPT_XSLFO 'Y'
+#define S_OPT_XML 'Z'
+#define S_OPT_BATCHREC 'B'
+#define S_OPT_FRAGMENT 'f'
+#define S_OPT_ANCHORS 'a'
+#define S_OPT_LISTTHEMES 'w'
+#define S_OPT_LISTLANGS 'p'
+#define S_OPT_OUTDIR 'O'
+
+#define S_OPT_FORMATSTYLE 'F'
+#define S_OPT_DATADIR 'D'
+#define S_OPT_ADDDATADIR 'E'
+#define S_OPT_INDEXFILE 'C'
+#define S_OPT_WRAP 'W'
+#define S_OPT_WRAPSIMPLE 'V'
+#define S_OPT_QUIET 'q'
+#define S_OPT_FOP 'g'
+#define S_OPT_REPLACE_QUOTES 'r'
+#define S_OPT_VERSION 'Q'
+#define S_OPT_PROGRESSBAR 'P'
+#define S_OPT_FILLZEROES 'z'
+#define S_OPT_ENCODING 'u'
+
+// deprecated:
+#define OPT_CSSOUT "css-outfile"
+#define OPT_CSSIN "css-infile"
+#define OPT_INC_CSS "include-css"
+
+
+#define S_OPTIONS_STRING "o:i:S:B:O:s:c:e:t:u:F:D:H:E:afghlvwpqrzACILYRTZXUV::W::P"
+
+using namespace std;
+
+/**Command line options*/
+
+class CmdLineOptions
+ {
+ public:
+
+ /**Constructor
+ \param argc Argument count
+ \param argv Argument strings
+ */
+ CmdLineOptions(int argc, char *argv[]);
+ ~CmdLineOptions();
+
+ /** \return Single output file name*/
+ const string &getSingleOutFilename();
+
+ /** \return Single input file name*/
+ const string &getSingleInFilename() const;
+
+ /** \return Output directory*/
+ const string& getOutDirectory() ;
+
+ /** \return Style output file name*/
+ const string getStyleOutFilename() const;
+
+ /** \return Style input file name*/
+ const string&getStyleInFilename() const;
+
+ /** \return Char set*/
+ const string&getCharSet() const;
+
+ /** \return Number of spaces to replace a tab*/
+ int getNumberSpaces() const;
+
+ /** \return True if version information should be printed*/
+ bool printVersion() const;
+
+ /** \return True if help information should be printed*/
+ bool printHelp() const;
+
+ /** \return True if debug information should be printed*/
+ bool printDebugInfo()const;
+
+ /** \return True if Style definition should be included in output*/
+ bool includeStyleDef() const;
+
+ /** \return True if line numbers should be printed*/
+ bool printLineNumbers() const;
+
+ /** \return colour theme name */
+ string getStyleName()const ;
+
+ /** gibt true zurck, falls deutsche Hilfe ausgegeben werden soll */
+ int helpLanguage() const;
+
+ /** \return True if batch mode is active*/
+ bool enableBatchMode() const;
+
+ /** \return True if output shluld be fragmented*/
+ bool fragmentOutput() const;
+
+ /** \return output file suffix */
+ string getOutFileSuffix() const;
+
+ /** \return True if anchors should be attached to line numbers*/
+ bool attachLineAnchors() const;
+
+ /** \return True if list of installed themes should be printed*/
+ bool showThemes() const;
+
+ /** \return True if list of installed language definitions should be printed*/
+ bool showLangdefs() const;
+
+ /** \return True if loutput directory is given*/
+ bool outDirGiven() const;
+
+ /** \return True if refomatting is enabled*/
+ bool formattingEnabled();
+
+ /** \return True if a new data directory is given*/
+ bool dataDirGiven()const;
+
+ /** \return True if an additional data directory is given*/
+ bool additionalDataDirGiven()const;
+
+ /** \return True if index file should be printed*/
+ bool printIndexFile() const;
+
+ /** \return True if quotes should be replaced by /dq in LaTeX*/
+ bool replaceQuotes() const;
+
+ /** \return Data directory*/
+ const string &getDataDir()const;
+
+ /** \return Additional data directory*/
+ const string &getAdditionalDataDir()const;
+
+ /** \return True if language syntax is given*/
+ bool syntaxGiven() const;
+
+ /** \return True if quiet mode is active*/
+ bool quietMode() const;
+
+ /** \return True if XSL-FO output should be FOP compatible*/
+ bool fopCompatible() const;
+
+ /** \return True if progress bar should be printed in batch mode */
+ bool printProgress() const;
+
+ /** \return True if line numbers are filled with leading zeroes */
+ bool fillLineNrZeroes() const;
+
+ /** \return name of help message file*/
+ const string getHelpLang() const;
+
+ /** \return programming language */
+ const string &getLanguage()const ;
+
+ /** \return Wrapping style*/
+ highlight::WrapMode getWrappingStyle() const;
+
+ /** \return List of input file names*/
+ const vector <string> & getInputFileNames() const;
+
+ /** \return Name of indentation scheme file */
+ const string &getIndentScheme() const;
+
+ /** \return Output file format */
+ highlight::OutputType getOutputType() const;
+
+ /** \return True if chosen output format supports referenced style files */
+ bool formatSupportsExtStyle();
+
+ /** \return True if style output path was defined by user*/
+ bool styleOutPathDefined() const{
+ return opt_stylepath_explicit;
+ }
+
+ /** \return True if encoding nasme should be omitted in output*/
+ bool omitEncodingName() const;
+
+ /** \return True if output should be generated if languege type is unknown*/
+ bool forceOutput() const;
+
+ private:
+
+ int numberSpaces; // number of spaces which replace a tab
+ highlight::WrapMode wrappingStyle; // line wrapping mode
+ highlight::OutputType outputType;
+
+ // name of single output file
+ string outFilename,
+ // output directory
+ outDirectory,
+ // programming language which will be loaded
+ language,
+ // name of colour theme
+ styleName,
+ // name of external style file
+ styleOutFilename,
+ // name of file to be included in external style file
+ styleInFilename,
+ // used to define data directories at runtime
+ dataDir, additionalDataDir;
+ // name of indenation scheme
+ string indentScheme;
+
+ bool opt_language;
+ bool opt_include_style;
+ bool opt_help;
+ bool opt_version ;
+ bool opt_verbose;
+ bool opt_linenumbers;
+ bool opt_style;
+ bool opt_batch_mode;
+ bool opt_fragment;
+ bool opt_attach_line_anchors;
+ bool opt_show_themes;
+ bool opt_show_langdefs;
+ bool opt_asformat_output;
+ bool opt_printindex;
+ bool opt_quiet;
+ bool opt_xslfo_fop;
+ bool opt_replacequotes;
+ bool opt_print_progress;
+ bool opt_fill_zeroes;
+ bool opt_stylepath_explicit;
+ bool opt_force_output;
+
+ bool configFileRead;
+
+ string helpLang, charset;
+ string configFilePath;
+
+ // list of all input file names
+ vector <string> inputFileNames;
+
+ /** load highlight configuration file */
+ void loadConfigurationFile();
+
+ /** \return file suffix */
+ string getFileSuffix( const string & fileName) const;
+
+ /** \return directory name of path */
+ string getDirName( const string & path);
+
+ /** get all entries in the directory defined by wildcard */
+ void readDirectory(const string & wildcard);
+
+ /** \return Boolean value of paramVal */
+ bool getFlag(const string& paramVal);
+
+ /** \return Valid path name */
+ string validateDirPath(const string & path);
+
+ void printDeprecatedWarning(const char *oldOption, const char *newOption);
+ };
+
+#endif
+/***************************************************************************
+ codeparser.cpp - description
+ -------------------
+ begin : Die Jul 9 2002
+ copyright : (C) 2002 by André Simon
+ email : andre.simon1@gmx.de
+ ***************************************************************************/
+
+/***************************************************************************
+ * *
+ * 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 of the License, or *
+ * (at your option) any later version. *
+ * *
+ ***************************************************************************/
+
+#include "codegenerator.h"
+
+#include "htmlgenerator.h"
+#include "xhtmlgenerator.h"
+#include "rtfgenerator.h"
+#include "latexgenerator.h"
+#include "texgenerator.h"
+#include "xslfogenerator.h"
+#include "xmlgenerator.h"
+#ifndef __WXMSW__
+ #include "ansigenerator.h"
+#endif
+
+
+using namespace std;
+
+namespace highlight {
+
+CodeGenerator* CodeGenerator::generator=NULL;
+
+CodeGenerator* CodeGenerator::getInstance(OutputType type,
+ const string& styleInfoPath,
+ const string& styleInPath,
+ const string& styleOutPath,
+ const string& encoding,
+ bool includeStyle,
+ bool attachAnchors,
+ bool replaceQuotes,
+ bool fopCompatible,
+ int numSpaces,
+ WrapMode lineWrappingMode,
+ bool ln,
+ bool lnz,
+ bool fragment,
+ bool omitEncoding
+ ) {
+ if (generator==NULL){
+ switch (type){
+ case TEX:
+ generator = new TexGenerator (styleInfoPath);
+ break;
+ case LATEX:
+ generator = new LatexGenerator(styleInfoPath, replaceQuotes);
+ break;
+ case RTF:
+ generator = new RtfGenerator (styleInfoPath);
+ break;
+ case XSLFO:
+ generator = new XslFoGenerator(styleInfoPath, encoding, omitEncoding,
+ fopCompatible);
+ break;
+ case XML:
+ generator = new XmlGenerator(styleInfoPath,encoding, omitEncoding);
+ break;
+ case XHTML:
+ generator = new XHtmlGenerator(styleInfoPath, encoding, omitEncoding,
+ attachAnchors);
+ break;
+ #ifndef __WXMSW__
+ case ANSI:
+ generator = new AnsiGenerator (styleInfoPath);
+ break;
+ #endif
+ default:
+ generator = new HtmlGenerator(styleInfoPath, encoding, omitEncoding,
+ attachAnchors);
+ }
+ }
+ generator->setType(type);
+ generator->setStyleInputPath(styleInPath);
+ generator->setStyleOutputPath(styleOutPath);
+ generator->setIncludeStyle(includeStyle);
+ generator->setPrintLineNumbers(ln);
+ generator->setPrintZeroes(lnz);
+ generator->setFragmentCode(fragment);
+ generator->setPreformatting(lineWrappingMode,
+ (generator->getPrintLineNumbers()) ?
+ MAX_LINE__WIDTH - LINE_NUMBER_WIDTH : MAX_LINE__WIDTH,
+ numSpaces );
+ return generator;
+}
+
+void CodeGenerator::deleteInstance(){
+ delete generator;
+ generator=NULL;
+}
+
+
+CodeGenerator::CodeGenerator():
+ in(NULL),
+ out(NULL),
+ maskWs(false),
+ excludeWs(false),
+ fragmentOutput(false),
+ showLineNumbers (false),
+ lineNumberFillZeroes(false),
+ lineNumber(0),
+ includeStyleDef(false),
+ lineIndex(0),
+ formatter(NULL),
+ preFormatter(NULL),
+ formattingEnabled(false),
+ formattingPossible(false),
+ outputType(highlight::HTML)
+{}
+
+CodeGenerator::CodeGenerator(const string &colourTheme)
+ :in(NULL),
+ out(NULL),
+ maskWs(false),
+ excludeWs(false),
+ fragmentOutput(false),
+ showLineNumbers (false),
+ lineNumberFillZeroes(false),
+ lineNumber(0),
+ includeStyleDef(false),
+ stylePath(colourTheme),
+ lineIndex(0),
+ formatter(NULL),
+ preFormatter(NULL),
+ formattingEnabled(false),
+ formattingPossible(false),
+ outputType(highlight::HTML)
+{
+ line.reserve(100);
+ docStyle.load(stylePath);
+}
+
+CodeGenerator::~CodeGenerator()
+{
+ delete preFormatter;
+ delete formatter;
+}
+
+
+/** Getter and Setter*/
+
+void CodeGenerator::setPrintLineNumbers(bool flag){
+ showLineNumbers=flag;
+}
+
+bool CodeGenerator::getPrintLineNumbers(){
+ return showLineNumbers;
+}
+
+void CodeGenerator::setPrintZeroes(bool flag){
+ lineNumberFillZeroes=flag;
+}
+
+bool CodeGenerator::getPrintZeroes(){
+ return lineNumberFillZeroes;
+}
+
+void CodeGenerator::setFragmentCode(bool flag){
+ fragmentOutput=flag;
+}
+
+void CodeGenerator::setIncludeStyle(bool flag){
+ includeStyleDef = flag;
+}
+
+void CodeGenerator::setStyleInputPath(const string& path){
+ styleInputPath = path;
+}
+void CodeGenerator::setStyleOutputPath(const string& path){
+ styleOutputPath = path;
+}
+
+const string& CodeGenerator::getStyleInputPath(){
+ return styleInputPath;
+}
+const string& CodeGenerator::getStyleOutputPath(){
+ return styleOutputPath;
+}
+
+
+bool CodeGenerator::getFragmentCode(){
+ return fragmentOutput;
+}
+
+void CodeGenerator::setStyleName(const string& s){
+ stylePath=s;
+}
+
+void CodeGenerator::setType(OutputType t){
+ outputType = t;
+}
+
+const string& CodeGenerator::getStyleName(){
+ return stylePath;
+}
+
+bool CodeGenerator::formattingDisabled(){
+ return !formattingEnabled;
+}
+
+bool CodeGenerator::formattingIsPossible(){
+ return formattingPossible;
+}
+
+void CodeGenerator::setPreformatting(WrapMode lineWrappingStyle,
+ unsigned int lineLength,
+ int numberSpaces ){
+ bool enableWrap = lineWrappingStyle!=WRAP_DISABLED;
+ bool replaceTabs = numberSpaces > 0;
+ if (enableWrap || replaceTabs) {
+ preFormatter=new PreFormatter(enableWrap, replaceTabs);
+ if (enableWrap)
+ preFormatter->setWrappingProperties(lineLength, lineWrappingStyle==WRAP_DEFAULT);
+ if (replaceTabs)
+ preFormatter->setNumberSpaces(numberSpaces);
+ }
+}
+
+/*
+WrapMode CodeGenerator::getLineWrapping(){
+ if (preFormatter==NULL) return WRAP_DISABLED;
+ return (preFormatter->indentCode()?WRAP_DEFAULT:WRAP_SIMPLE);
+}
+*/
+LanguageDefinition &CodeGenerator::getLanguage(){
+ return langInfo;
+}
+
+void CodeGenerator::reset()
+{
+ lineIndex = lineNumber = 0;
+ line.clear();
+}
+
+
+/** sucht vorwaerts ab Position searchPos Ziffer in s und liefert Integerwert
+der gefundenen Zahl zurueck.
+Im SymbolString stehen die den einzelnen Symbolen zugeordneten Konstanten
+immer HINTER diesen Symbolen*/
+State CodeGenerator::getState(const string &s, unsigned int searchPos)
+{
+ unsigned int i= searchPos+1, result=0;
+
+ // nach Ziffer in s suchen
+ do {
+ ++i;
+ } while ((i<s.length()) && !isdigit(s[i])) ;
+
+ // Zahl zusammensetzen
+ while ((i<s.length()) && isdigit(s[i])){
+ result = result *10 + (s[i]-'0');
+ ++i;
+ }
+ return ((result)? (State)result:_UNKNOWN);
+}
+
+string CodeGenerator::getIdentifier()
+{
+ --lineIndex;
+ unsigned int startPos=lineIndex;
+ char c= line[lineIndex];
+
+ while ( ( lineIndex < line.length()
+ && ( StringTools::isAlpha(c)
+ || isdigit(c))
+ || isAllowedChar(c))
+ )
+ {
+ ++lineIndex;
+ c= line[lineIndex];
+ }
+ return string(line, startPos, lineIndex - startPos);
+}
+
+string CodeGenerator::getNumber()
+{
+ --lineIndex;
+ unsigned int startPos=lineIndex;
+ char c=line[lineIndex];
+
+ while ( lineIndex < line.length() && (
+ isdigit(c)
+ // don't highlight methods applied on numbers as part of the number
+ // i.e. Ruby: 3.xxx()
+ || (c == '.' && isdigit(line[lineIndex+1]))
+ // '-' is accepted as first character
+ || (c == '-' && lineIndex == startPos)
+ || (StringTools::isAlpha(c) && line[lineIndex-1]=='0')
+ || (isxdigit(c) || c=='L' || c=='U' || c=='l' || c=='u') ))
+ {
+ ++lineIndex;
+ c= line[lineIndex];
+ }
+ return string(line,startPos, lineIndex-startPos);
+}
+
+unsigned int CodeGenerator::getLineNumber()
+{
+ return lineNumber;
+}
+
+bool CodeGenerator::readNewLine(string &newLine){
+ bool eof;
+ terminatingChar=newLine[lineIndex-1];
+ if (formattingPossible && formattingEnabled)
+ {
+ eof=!formatter->hasMoreLines();
+ if (!eof)
+ {
+ newLine = formatter->nextLine();
+ }
+ }
+ else // reformatting not enabled
+ {
+ eof = ! getline( *in, newLine);
+ }
+ return eof;
+}
+
+unsigned char CodeGenerator::getInputChar()
+{
+ bool eol = lineIndex == line.length();
+
+ if (eol) {
+ bool eof=false;
+ if (preFormatter!=NULL){
+ if (!preFormatter->hasMoreLines()) {
+ eof=readNewLine(line);
+ preFormatter->setLine(line);
+ }
+ line = preFormatter->getNextLine();
+ } else {
+ eof=readNewLine(line);
+ }
+ lineIndex=0;
+ ++lineNumber;
+ line=StringTools::trimRight(line);
+ return (eof)?'\0':'\n';
+ }
+ return line[lineIndex++];
+}
+
+State CodeGenerator::getCurrentState (bool lastStateWasNumber)
+{
+ unsigned char c;
+
+ if (token.length()==0) {
+ c=getInputChar();
+ } else {
+ lineIndex-= (token.length()-1);
+ c=token[0];
+ }
+ if (c=='\n'){
+ return _EOL; // End of line
+ }
+
+ if (c=='\0') {
+ return _EOF; // End of file
+ }
+
+ if (isspace(c)) {
+ token= c;
+ return _WS;
+ }
+
+ // numbers have to be searched before using the symbolstring,
+ // as numbers are part of this string
+ if (isdigit(c)
+ // recognize floats like .5
+ || (c=='.' && isdigit(line[lineIndex]))
+ // test if '-' belongs to a term like "1-2"
+ || ((c == '-')
+ && (!lastStateWasNumber)
+ && isdigit(StringTools::getNextNonWs(line, lineIndex))) )
+ {
+ token = getNumber();
+ return NUMBER;
+ }
+ unsigned int symbolLength;
+ size_t symbolPos;
+ bool found=false;
+ string symbols=langInfo.getSymbolString();
+
+ symbolPos = symbols.find(c);
+ // search symbols (comment delimiters, directives etc.)
+ // before keywords, because alphabetic chars may be part of symbols, too
+ while ((symbolPos!= string::npos) && (!found))
+ {
+ symbolLength=symbols.find(' ', symbolPos)-symbolPos;
+ token = symbols.substr(symbolPos, symbolLength);
+
+ // TODO Ruby =ende, =end bugfix (whitespace after symbol needs to be checked)
+
+ // Abfrage nach Leerzeichen in SymbolString verhindert falsches
+ // Erkennen von Symbolteilen:
+ if (lineIndex && token == line.substr(lineIndex-1, symbolLength)
+ && isspace(symbols[symbolPos-1]) ) {
+ found = true;
+ lineIndex += (symbolLength-1);
+ } else {
+ symbolPos = symbols.find_first_not_of(' ',symbols.find(' ',symbolPos));
+ }
+ }
+
+ // dirty workaround stuff in here
+ if (found) {
+ State foundState = getState(symbols, symbolPos);
+
+ // get the current keyword class id to apply the corresponding formatting style
+ if (foundState==KEYWORD_BEGIN || foundState==TAG_BEGIN ) {
+ currentKeywordClass=langInfo.getDelimPrefixClassID(token);
+ }
+
+ // Full line quotes must start in coloumn 1 (Fortran 77)
+ if (langInfo.isFullLineComment() && foundState==SL_COMMENT){
+ if (lineIndex==1) {
+ return SL_COMMENT;
+ }
+ }
+ // VHDL Workaround: distinguish string delimiters and event markers
+ // (same eymbol: ')
+ else if (langInfo.isVHDL()
+ && foundState==STRING && currentState!=STRING
+ && lineIndex > 1
+ &&(isdigit(line[lineIndex-2]) || isalpha(line[lineIndex-2]))){
+ c=line[lineIndex-1];
+ // do not return, continue search...
+ } else {
+ return foundState;
+ }
+ }
+
+ // Alphanumerisches Token parsen und als Keyword oder Type erkennen
+ if (StringTools::isAlpha(c) || langInfo.isPrefix(c) || isAllowedChar(c))
+ {
+ if (langInfo.isPrefix(c)){
+ token = c;
+ ++lineIndex;
+ token += getIdentifier();
+ } else {
+ token = getIdentifier();
+ }
+ string reservedWord=(langInfo.isIgnoreCase()) ?
+ StringTools::lowerCase(token):token;
+ currentKeywordClass=langInfo.isKeyword(reservedWord);
+ return (currentKeywordClass) ? KEYWORD : STANDARD;
+ }
+
+ // Character not referring to any state
+ token = c;
+ return STANDARD;
+}
+
+string CodeGenerator::maskString(const string & s)
+{
+ ostringstream ss;
+ for (unsigned int i=0;i< s.length();i++){
+ ss << maskCharacter(s[i]);
+ }
+ return ss.str();
+}
+
+void CodeGenerator::printMaskedToken(bool flushWhiteSpace)
+{
+ if(flushWhiteSpace) flushWs();
+ *out << maskString(token);
+ token.clear();
+}
+
+bool CodeGenerator::isAllowedChar(char c)
+{
+ return ( langInfo.getAllowedChars().find(c)!= string::npos);
+}
+
+bool CodeGenerator::styleFound(){
+ return docStyle.found();
+}
+
+bool CodeGenerator::printIndexFile(const vector<string> &fileList,
+ const string &outPath){
+ return true;
+}
+
+bool CodeGenerator::initIndentationScheme(const string &schemePath){
+
+ if (formatter!=NULL){
+ return true;
+ }
+
+ ConfigurationReader indentScheme(schemePath);
+ if (indentScheme.found()){
+ if (formatter==NULL) {
+ formatter=new astyle::ASFormatter();
+
+ string brackets=indentScheme.getParameter("brackets");
+ if (!brackets.empty()){
+ // Break brackets from pre-block code (i.e. ANSI C/C++ style).
+ if (brackets=="break"){
+ formatter->setBracketFormatMode(astyle::BREAK_MODE);
+ }
+ //Attach brackets to pre-block code (i.e. Java/K&R style).
+ else if (brackets=="attach"){
+ formatter->setBracketFormatMode(astyle::ATTACH_MODE);
+ }
+ // Break definition-block brackets and attach command-block brackets.
+ else if (brackets=="linux"){
+ formatter->setBracketFormatMode(astyle::BDAC_MODE);
+ }
+ // Break brackets before closing headers (e.g. 'else', 'catch', ..).
+ // Should be appended to --brackets=attach or --brackets=linux.
+ else if (brackets=="break-closing-headers"){
+ formatter->setBreakClosingHeaderBracketsMode(true);
+ }
+ }
+
+ string pad=indentScheme.getParameter("pad");
+ if (!pad.empty()){
+ //Insert space paddings around parenthesies only.
+ if (pad=="paren"){
+ formatter->setParenthesisPaddingMode(true);
+ }
+ // Insert space paddings around operators only.
+ else if (pad=="oper"){
+ formatter->setOperatorPaddingMode(true);
+ }
+ //Insert space paddings around operators AND parenthesies.
+ else if (pad=="all"){
+ formatter->setOperatorPaddingMode(true);
+ formatter->setParenthesisPaddingMode(true);
+ }
+ }
+
+ string oneLine=indentScheme.getParameter("one-line");
+ if (!oneLine.empty()){
+ // Don't break one-line blocks.
+ if (oneLine=="keep-blocks"){
+ formatter->setBreakOneLineBlocksMode(false);
+ }
+ // Don't break complex statements and multiple statements residing in a
+ // single line.
+ else if (oneLine=="keep-statements"){
+ formatter->setSingleStatementsMode(false);
+ }
+ }
+
+ // Insert empty lines around unrelated blocks, labels, classes, ...
+ string breakBlocks=indentScheme.getParameter("break-blocks");
+ if (!breakBlocks.empty()){
+ if (breakBlocks=="all"){
+ //Like --break-blocks, except also insert empty lines around closing
+ //headers (e.g. 'else', 'catch', ...).
+ formatter->setBreakClosingHeaderBlocksMode(true);
+ }
+ formatter->setBreakBlocksMode(true);
+ }
+ string trueVal="true";
+
+ // Other options...
+
+ //Indent using # spaces per indent. Not specifying # will result in a
+ //default of 4 spaces per indent.
+ string indentSpaces=indentScheme.getParameter("indent-spaces");
+
+ // Indent a minimal # spaces in a continuous conditional belonging to a
+ //conditional header.
+ string minConditionalIndent=indentScheme.getParameter("min-conditional-indent");
+
+ // Indent a maximal # spaces in a continuous statement, relatively to the
+ // previous line.
+ string maxInStatementIndent=indentScheme.getParameter("max-instatement-indent");
+
+ // Add extra indentation to '{' and '}' block brackets.
+ string indentBrackets=indentScheme.getParameter("indent-brackets");
+
+ // Add extra indentation entire blocks (including brackets).
+ string indentBlocks=indentScheme.getParameter("indent-blocks");
+
+ // Indent the contents of namespace blocks.
+ string indentNamespaces=indentScheme.getParameter("indent-namespaces");
+
+ // Indent 'class' blocks, so that the inner 'public:','protected:' and
+ // 'private: headers are indented inrelation to the class block.
+ string indentClasses=indentScheme.getParameter("indent-classes");
+
+ // Indent 'switch' blocks, so that the inner 'case XXX:' headers are
+ // indented in relation to the switch block.
+ string indentSwitches=indentScheme.getParameter("indent-switches");
+
+ // Indent 'case XXX:' lines, so that they are flush with their bodies..
+ string indentCases=indentScheme.getParameter("indent-cases");
+
+ // Indent labels so that they appear one indent less than the current
+ // indentation level, rather than being flushed completely to the left
+ // (which is the default).
+ string indentLabels=indentScheme.getParameter("indent-labels");
+
+ // Indent multi-line #define statements
+ string indentPreprocessor=indentScheme.getParameter("indent-preprocessor");
+
+ // Break 'else if()' statements into two different lines.
+ string breakElseIfs = indentScheme.getParameter("break-elseifs");
+
+ string javaStyle = indentScheme.getParameter("java-style");
+
+ // default values in ASBeautifier are false, it is ok to set them false
+ // if parameter does not exist in scheme file
+ formatter->setBracketIndent(indentBrackets==trueVal);
+ formatter->setBlockIndent(indentBlocks==trueVal);
+ formatter->setNamespaceIndent(indentNamespaces==trueVal);
+ formatter->setClassIndent(indentClasses==trueVal);
+ formatter->setSwitchIndent(indentSwitches==trueVal);
+ formatter->setCaseIndent(indentCases==trueVal);
+ formatter->setLabelIndent(indentLabels==trueVal);
+ formatter->setPreprocessorIndent(indentPreprocessor==trueVal);
+ formatter->setBreakElseIfsMode(breakElseIfs==trueVal);
+
+ if (javaStyle==trueVal){
+ formatter->setJavaStyle();
+ }
+
+ if (!indentSpaces.empty()){
+ formatter->setSpaceIndentation(StringTools::str2int(indentSpaces));
+ }
+ if (!minConditionalIndent.empty()){
+ formatter->setMinConditionalIndentLength(
+ StringTools::str2int(minConditionalIndent));
+ }
+ if (!maxInStatementIndent.empty()){
+ formatter->setMinConditionalIndentLength(
+ StringTools::str2int(maxInStatementIndent));
+ }
+ }
+ formattingEnabled=(formatter != NULL);
+ return true;
+ } else {
+ return false;
+ }
+}
+
+LoadResult CodeGenerator::initLanguage(const string& langDefPath){
+ bool reloadNecessary= langInfo.needsReload(langDefPath);
+ if (reloadNecessary){
+ bool failure = !langInfo.load(langDefPath);
+
+ if (failure) {
+ return LOAD_FAILED;
+ }
+
+ formattingPossible=langInfo.enableReformatting();
+
+ if (styleTagOpen.size()>NUMBER_BUILTIN_STYLES){
+ // remove dynamic keyword tag delimiters of the old language definition
+ vector<string>::iterator keyStyleOpenBegin =
+ styleTagOpen.begin() + NUMBER_BUILTIN_STYLES;
+ vector<string>::iterator keyStyleCloseBegin =
+ styleTagClose.begin()+ NUMBER_BUILTIN_STYLES;
+ styleTagOpen.erase(keyStyleOpenBegin, styleTagOpen.end());
+ styleTagClose.erase(keyStyleCloseBegin, styleTagClose.end());
+ }
+ // add new keyword delimiters
+ for (unsigned int i=0;i< langInfo.getKeywordClasses().size(); i++){
+ styleTagOpen.push_back(getMatchingOpenTag(i));
+ styleTagClose.push_back(getMatchingCloseTag(i));
+ }
+ }
+ return (reloadNecessary) ? LOAD_NEW : LOAD_NONE;
+}
+
+ParseError CodeGenerator::printOutput (const string & inFileName,
+ const string &outFileName)
+{
+ if (!docStyle.found()) {
+ return BAD_STYLE;
+ }
+ reset();
+
+ ParseError error=PARSE_OK;
+
+ in = (inFileName.empty()? &cin :new ifstream (inFileName.c_str()));
+ if (!in->fail()) {
+ out = (outFileName.empty()? &cout :new ofstream (outFileName.c_str()));
+ if ( out->fail()) {
+ error=BAD_OUTPUT;
+ }
+ }
+
+ if ( in->fail()){
+ error=BAD_INPUT;
+ }
+
+ if (error==PARSE_OK) {
+ if (formatter != NULL){
+ formatter->init(new astyle::ASStreamIterator(in));
+ }
+ if (! fragmentOutput){
+ *out << getHeader(inFileName);
+ }
+ printBody();
+ if (! fragmentOutput){
+ *out << getFooter();
+ }
+ }
+
+ if (!outFileName.empty()){
+ delete out; out=NULL;
+ }
+ if (!inFileName.empty()) {
+ delete in; in=NULL;
+ }
+ return error;
+}
+
+
+unsigned int CodeGenerator::getStyleID(State s, unsigned int kwClassID){
+ if (s==KEYWORD && kwClassID){
+ return NUMBER_BUILTIN_STYLES + kwClassID-1;
+ }
+ return (unsigned int) s ;
+}
+
+void CodeGenerator::closeTag(State s){
+ *out << styleTagClose[(unsigned int)s];
+ flushWs();
+ currentState=_UNKNOWN;
+}
+
+void CodeGenerator::openTag(State s){
+ *out << styleTagOpen[(unsigned int)s];
+ currentState=s;
+}
+
+void CodeGenerator::closeKWTag(unsigned int kwClassID){
+ *out << styleTagClose[getStyleID(KEYWORD, kwClassID)];
+
+ flushWs();
+ currentState=_UNKNOWN;
+}
+
+void CodeGenerator::openKWTag(unsigned int kwClassID){
+ *out << styleTagOpen[getStyleID(KEYWORD, kwClassID)];
+ currentState=KEYWORD;
+}
+
+
+///////////////////////////////////////////////////////////////////////////////
+
+void CodeGenerator::processRootState()
+{
+ if (langInfo.highlightingDisabled()){
+ string line;
+ while (getline(*in, line)){
+ *out << maskString(line) << getNewLine();
+ }
+ *out << flush;
+ return;
+ }
+
+ State state=STANDARD;
+
+ bool eof=false,
+ firstLine=true; // avoid newline before printing the first output line
+ openTag(STANDARD);
+ do {
+ // determine next state
+ state= getCurrentState(state==NUMBER);
+ // handle current state
+ switch(state)
+ {
+ case KEYWORD:
+ case KEYWORD_BEGIN:
+ closeTag(STANDARD);
+ eof=processKeywordState(state);
+ openTag(STANDARD);
+ break;
+ case NUMBER:
+ closeTag(STANDARD);
+ eof=processNumberState();
+ openTag(STANDARD);
+ break;
+ case ML_COMMENT_BEGIN:
+ closeTag(STANDARD);
+ eof=processMultiLineCommentState();
+ openTag(STANDARD);
+ break;
+ case SL_COMMENT:
+ closeTag(STANDARD);
+ eof=processSingleLineCommentState();
+ openTag(STANDARD);
+ break;
+ case STRING:
+ closeTag(STANDARD);
+ eof=processStringState(STANDARD);
+ openTag(STANDARD);
+ break;
+ case DIRECTIVE_LINE:
+ closeTag(STANDARD);
+ eof=processDirectiveState();
+ openTag(STANDARD);
+ break;
+ case TAG_BEGIN:
+ closeTag(STANDARD);
+ eof=processTagState();
+ openTag(STANDARD);
+ break;
+ case ESC_CHAR:
+ if (langInfo.allowExtEscSeq()){
+ closeTag(STANDARD);
+ eof=processEscapeCharState();
+ openTag(STANDARD);
+ } else {
+ printMaskedToken();
+ }
+ break;
+ case SYMBOL:
+ closeTag(STANDARD);
+ eof=processSymbolState();
+ openTag(STANDARD);
+ break;
+ case _EOL:
+ insertLineNumber(!firstLine);
+ firstLine=false;
+ break;
+ case _EOF:
+ eof=true;
+ break;
+ case _WS:
+ processWsState();
+ break;
+ default:
+ printMaskedToken();
+ break;
+ }
+ }
+ while (!eof);
+ closeTag(STANDARD);
+ *out << getNewLine();
+ *out << flush;
+}
+
+bool CodeGenerator::processKeywordState(State myState){
+ State newState=STANDARD;
+ unsigned int myClassID=currentKeywordClass;
+ bool eof=false,
+ exitState=false;
+
+ openKWTag(myClassID);
+ do {
+ printMaskedToken(newState!=_WS);
+ newState= getCurrentState();
+ switch(newState)
+ {
+ case _WS:
+ processWsState();
+ break;
+ case _EOL:
+ insertLineNumber();
+ exitState=true;
+ break;
+ case _EOF:
+ eof = true;
+ break;
+ case KEYWORD_END:
+ if (myState==KEYWORD_BEGIN){
+ printMaskedToken();
+ }
+ exitState=true;
+ break;
+ default:
+ exitState= myState!=KEYWORD_BEGIN
+ &&((myClassID!=currentKeywordClass)||(myState!=newState));
+ break;
+ }
+ } while ((!exitState) && (!eof));
+
+ closeKWTag(myClassID);
+
+ currentKeywordClass=0;
+ return eof;
+}
+
+bool CodeGenerator::processNumberState(){
+ State newState=STANDARD;
+ bool eof=false,
+ exitState=false;
+
+ openTag(NUMBER);
+ do {
+ printMaskedToken(newState!=_WS);
+ newState= getCurrentState(true);
+ switch(newState)
+ {
+ case _WS:
+ processWsState();
+ break;
+ case _EOL:
+ insertLineNumber();
+ exitState=true;
+ break;
+ case _EOF:
+ eof = true;
+ break;
+ default:
+ exitState=newState!=NUMBER;
+ break;
+ }
+ } while ((!exitState) && (!eof));
+
+ closeTag(NUMBER);
+ return eof;
+}
+
+bool CodeGenerator::processMultiLineCommentState()
+{
+ int commentCount=1;
+ State newState=STANDARD;
+ bool eof=false, exitState=false;
+
+ openTag(ML_COMMENT_BEGIN);
+ do {
+ printMaskedToken(newState!=_WS);
+ newState= getCurrentState();
+
+ switch(newState)
+ {
+ case _WS:
+ processWsState();
+ break;
+ case _EOL:
+ wsBuffer += styleTagClose[ML_COMMENT_BEGIN];
+ insertLineNumber();
+ wsBuffer += styleTagOpen[ML_COMMENT_BEGIN];
+ break;
+ case _EOF:
+ eof = true;
+ break;
+ case ML_COMMENT_BEGIN:
+ if (langInfo.allowNestedMLComments()) {
+ ++commentCount;
+ }
+ break;
+ case ML_COMMENT_END:
+ commentCount--;
+ if (!commentCount){
+ printMaskedToken();
+ exitState=true;
+ }
+ break;
+ default:
+ break;
+ }
+ } while ((!exitState) && (!eof));
+
+ closeTag(ML_COMMENT_BEGIN);
+ return eof;
+}
+
+bool CodeGenerator::processSingleLineCommentState()
+{
+
+ //if ( checkSpecialCmd()) return false;
+
+ State newState=STANDARD;
+ bool eof=false, exitState=false;
+
+ openTag(SL_COMMENT);
+ do {
+ printMaskedToken(newState!=_WS);
+ newState= getCurrentState();
+
+ switch(newState)
+ {
+ case _WS:
+ processWsState();
+ break;
+ case _EOL:
+ printMaskedToken();
+ insertLineNumber();
+ exitState=true;
+ break;
+ case _EOF:
+ eof = true;
+ break;
+ default:
+ break;
+ }
+ } while ((!exitState) && (!eof));
+
+ closeTag(SL_COMMENT);
+ return eof;
+}
+
+bool CodeGenerator::processDirectiveState()
+{
+ State newState=STANDARD;
+ bool eof=false, exitState=false;
+
+ openTag(DIRECTIVE_LINE);
+ do {
+ printMaskedToken(newState!=_WS);
+ newState= getCurrentState();
+ switch(newState)
+ {
+ case _WS:
+ processWsState();
+ break;
+ case DIRECTIVE_LINE_END:
+ printMaskedToken();
+ exitState=true;
+ break;
+ case _EOL:
+ printMaskedToken();
+ exitState=(terminatingChar!=langInfo.getContinuationChar());
+ if (!exitState) wsBuffer += styleTagClose[DIRECTIVE_LINE];
+ insertLineNumber();
+ if (!exitState) wsBuffer += styleTagOpen[DIRECTIVE_LINE];
+ break;
+ case ML_COMMENT_BEGIN:
+ closeTag(DIRECTIVE_LINE);
+ eof= processMultiLineCommentState();
+ openTag(DIRECTIVE_LINE);
+ break;
+ case SL_COMMENT:
+ closeTag(DIRECTIVE_LINE);
+ eof= processSingleLineCommentState();
+ openTag(DIRECTIVE_LINE);
+ exitState=true;
+ break;
+ case STRING:
+ closeTag(DIRECTIVE_LINE);
+ eof=processStringState(DIRECTIVE_LINE);
+ openTag(DIRECTIVE_LINE);
+ break;
+ case _EOF:
+ eof = true;
+ break;
+ default:
+ break;
+ }
+ } while ((!exitState) && (!eof));
+
+ closeTag(DIRECTIVE_LINE);
+ return eof;
+}
+
+bool CodeGenerator::processStringState(State oldState)
+{
+ State newState=STANDARD;
+ bool eof=false, exitState=false;
+ bool returnedFromOtherState=false;
+ // Test if character before string open delimiter token equals to the
+ // raw string prefix (Example: r" ", r""" """ in Python)
+ bool isRawString=
+ line[lineIndex-token.length()-1]==langInfo.getRawStringPrefix();
+
+ string openStringDelimiter=token;
+
+ State myState= (oldState==DIRECTIVE_LINE) ? DIRECTIVE_STRING : STRING;
+ openTag(myState);
+ do {
+ // true if last token was an escape char
+ if (!returnedFromOtherState) {
+ printMaskedToken(newState!=_WS);
+ }
+ returnedFromOtherState=false;
+ newState= getCurrentState();
+
+ switch(newState)
+ {
+ case _WS:
+ processWsState();
+ break;
+ case _EOL:
+ wsBuffer += styleTagClose[myState];
+ insertLineNumber();
+ wsBuffer += styleTagOpen[myState];
+ //exitState=true;
+ break;
+ case ML_COMMENT_END:
+ printMaskedToken();
+ break;
+ case STRING:
+ exitState= openStringDelimiter==token;
+ printMaskedToken();
+ break;
+ case ESC_CHAR:
+ if (!isRawString){
+ closeTag(myState);
+ eof=processEscapeCharState();
+ openTag(myState);
+ returnedFromOtherState=true;
+ }
+ break;
+ case _EOF:
+ eof = true;
+ break;
+ default:
+ printMaskedToken();
+ break;
+ }
+ } while ((!exitState) && (!eof));
+
+ closeTag(myState);
+ return eof;
+}
+
+bool CodeGenerator::processTagState()
+{
+ State newState=STANDARD;
+ bool eof=false, exitState=false, returnedFromOtherState=false;
+ unsigned int myKeywordClass=currentKeywordClass;
+
+ openTag(KEYWORD);
+ do {
+ if (!returnedFromOtherState) {
+ printMaskedToken(newState!=_WS);
+ }
+ returnedFromOtherState = false;
+ newState= getCurrentState();
+
+ switch(newState)
+ {
+ case _WS:
+ processWsState();
+ break;
+ case _EOL:
+ insertLineNumber();
+ exitState=true;
+ break;
+ case TAG_END:
+ printMaskedToken();
+ exitState=true;
+ break;
+ case STRING:
+ closeTag(KEYWORD);
+ eof=processStringState(KEYWORD);
+ currentKeywordClass=myKeywordClass;
+ openTag(KEYWORD);
+ returnedFromOtherState = true;
+ break;
+ case ESC_CHAR:
+ closeTag(KEYWORD);
+ eof=processEscapeCharState();
+ currentKeywordClass=myKeywordClass;
+ openTag(KEYWORD);
+ returnedFromOtherState = true;
+ break;
+ case NUMBER:
+ closeTag(KEYWORD);
+ eof=processNumberState();
+ currentKeywordClass=myKeywordClass;
+ openTag(KEYWORD);
+ returnedFromOtherState = true;
+ break;
+ case _EOF:
+ eof = true;
+ break;
+ default:
+ printMaskedToken();
+ break;
+ }
+ } while ((!exitState) && (!eof));
+
+ closeTag(KEYWORD);
+ currentKeywordClass=0;
+
+ return eof;
+}
+
+bool CodeGenerator::processSymbolState(){
+
+ State newState=STANDARD;
+ bool eof=false,
+ exitState=false;
+
+ openTag(SYMBOL);
+ do {
+ printMaskedToken(newState!=_WS);
+ newState= getCurrentState(true);
+ switch(newState)
+ {
+ case _WS:
+ processWsState();
+ break;
+ case _EOL:
+ insertLineNumber();
+ exitState=true;
+ break;
+ case _EOF:
+ eof = true;
+ break;
+ default:
+ exitState=newState!=SYMBOL;
+ break;
+ }
+ } while ((!exitState) && (!eof));
+
+ closeTag(SYMBOL);
+ return eof;
+}
+
+bool CodeGenerator::processEscapeCharState()
+{
+ State newState=STANDARD;
+ bool eof=false, exitState=false;
+
+ openTag(ESC_CHAR);
+ do {
+ printMaskedToken(newState!=_WS);
+ skipEscapeSequence();
+ newState= getCurrentState();
+ switch(newState)
+ {
+ case _EOL:
+ insertLineNumber();
+ exitState=true;
+ break;
+ case _WS:
+ processWsState();
+ --lineIndex;
+ break;
+ case _EOF:
+ eof = true;
+ break;
+ default:
+ exitState=newState!=ESC_CHAR;
+ break;
+ }
+ } while ((!exitState) && (!eof));
+
+ closeTag(ESC_CHAR);
+ return eof;
+}
+
+void CodeGenerator::skipEscapeSequence(){
+ if (lineIndex<line.length()){
+ char c=line[lineIndex];
+ int charsToSkip=1;
+ // Escape Sequenz /ooo Oktal, /x000 hex, /u00xx Java unicode
+ if (isdigit(c) ){
+ // \0 abfangen
+ while ( isdigit(line[lineIndex+charsToSkip]) && charsToSkip<4) {
+ ++charsToSkip;
+ }
+ } else if (tolower(c)=='x'){
+ charsToSkip=langInfo.isJava() ? 4 : 3;
+ } else if (tolower(c)=='u'){
+ charsToSkip=5;
+ }
+ while (charsToSkip-- && lineIndex++<line.length()){
+ *out <<maskCharacter(line[lineIndex-1]);
+ }
+ }
+}
+
+
+void CodeGenerator::processWsState()
+{
+ if (!maskWs) {
+ wsBuffer += token;
+ token.clear();
+ return;
+ }
+ flushWs();
+ int cntWs=0;
+ lineIndex--;
+
+ while (isspace(line[lineIndex]) ) {
+ ++cntWs;
+ ++lineIndex;
+ }
+
+ if (cntWs>1) {
+ unsigned int styleID=getStyleID(currentState, currentKeywordClass);
+ if (excludeWs && styleID!=_UNKNOWN) {
+ *out << styleTagClose[styleID];
+ }
+ *out << maskWsBegin;
+ for (int i=0; i<cntWs; i++){
+ *out << spacer;
+ }
+ *out << maskWsEnd;
+ if (excludeWs && styleID!=_UNKNOWN){
+ *out << styleTagOpen[styleID];
+ }
+ } else {
+ *out << token;
+ }
+ token.clear();
+}
+
+void CodeGenerator::flushWs(){
+ *out<<wsBuffer;
+ wsBuffer.clear();
+}
+
+bool CodeGenerator::isFirstNonWsChar() {
+ unsigned int i=lineIndex-1;
+ while (i--){
+ if (!isspace(line[i])){
+ return false;
+ }
+ }
+ return true;
+}
+
+string CodeGenerator::getNewLine(){
+ return newLineTag;
+}
+
+void CodeGenerator::insertLineNumber(bool insertNewLine) {
+ if (insertNewLine){
+ wsBuffer += getNewLine();
+ }
+ if (showLineNumbers) {
+ ostringstream os;
+ ostringstream numberPrefix;
+ if (lineNumberFillZeroes) {
+ os.fill('0');
+ }
+ os <<setw(LINE_NUMBER_WIDTH) << right << lineNumber;
+
+ numberPrefix << styleTagOpen[LINENUMBER]
+ << maskString(os.str()) << spacer
+ << styleTagClose[LINENUMBER];
+
+ wsBuffer += numberPrefix.str();
+ }
+}
+
+unsigned int CodeGenerator::getLineIndex(){
+ return lineIndex;
+}
+
+bool CodeGenerator::printExternalStyle(const string &outFile)
+{
+ if (!includeStyleDef && langInfo.getSyntaxHighlight()) {
+ ofstream cssOutFile(outFile.c_str());
+ if (cssOutFile) {
+ cssOutFile << styleCommentOpen
+ <<" Style definition file generated by highlight "
+ << HIGHLIGHT_VERSION << ", " << HIGHLIGHT_URL
+ << " " << styleCommentClose << "\n";
+ cssOutFile << "\n"<<styleCommentOpen<<" Highlighting theme definition: "
+ << styleCommentClose << "\n\n"
+ << getStyleDefinition()
+ << "\n";
+ cssOutFile << readUserStyleDef();
+ cssOutFile.close();
+ } else {
+ return false;
+ }
+ }
+ return true;
+}
+
+string CodeGenerator::readUserStyleDef(){
+ ostringstream ostr;
+ if (!styleInputPath.empty()){
+ ifstream userStyleDef(styleInputPath.c_str());
+ if (userStyleDef) {
+ ostr << "\n"<<styleCommentOpen<<" Content of "<<styleInputPath<<": "<<styleCommentClose<<"\n";
+ string line;
+ while (getline(userStyleDef, line)){
+ ostr << line << "\n";
+ }
+ userStyleDef.close();
+ } else {
+ ostr << styleCommentOpen<<" ERROR: Could not include "
+ << styleInputPath
+ << "."<<styleCommentClose<<"\n";
+ }
+ }
+ return ostr.str();
+}
+
+bool CodeGenerator::checkSpecialCmd(){
+ bool insertNL = (lineIndex-token.length());
+ cerr << "token: "<<token<< " index"<< lineIndex << " "<<line [ lineIndex ]<<endl;
+
+ if (line [ lineIndex ]=='!'){
+ // find cmd
+ size_t cmdPos1 = line.find('@', lineIndex+1);
+
+ cerr << "cmdPos"<<cmdPos1<<endl;
+ if(cmdPos1==string::npos) return false;
+ size_t cmdPos2=cmdPos1+1;
+ while (cmdPos2 < line.length() && StringTools::isAlpha(line[cmdPos2])) cmdPos2++;
+ cerr << "cmdPos2"<<cmdPos2<<endl;
+ cerr << line.substr(cmdPos1, cmdPos2)<<endl;
+
+ // hide comment line
+ token.clear();
+ lineIndex=line.length();
+ getInputChar(); lineNumber--;
+ if (insertNL) { lineNumber++;insertLineNumber();};
+ // end hide
+
+ return true;
+ }
+
+ return false;
+}
+
+}
+/***************************************************************************
+ codeparser.h - description
+ -------------------
+ begin : Die Jul 9 2002
+ copyright : (C) 2002 by Andre Simon
+ email : andre.simon1@gmx.de
+ ***************************************************************************/
+
+/***************************************************************************
+ * *
+ * 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 of the License, or *
+ * (at your option) any later version. *
+ * *
+ ***************************************************************************/
+
+#ifndef CODEPARSER_H
+#define CODEPARSER_H
+
+#include <iostream>
+#include <sstream>
+#include <string>
+#include <iomanip>
+#include <cctype>
+
+#include "languagedefinition.h"
+#include "documentstyle.h"
+#include "ASFormatter.h"
+#include "preformatter.h"
+#include "enums.h"
+
+
+#define NUMBER_BUILTIN_STYLES 10
+#define LINE_NUMBER_WIDTH 5
+#define MAX_LINE__WIDTH 80
+
+#define OUTPUT_FLAG_LN 1
+#define OUTPUT_FLAG_LNZ 2
+#define OUTPUT_FLAG_FRAG 4
+
+/** The highlight namespace contains all classes and data structures
+ needed for parsing input data.
+*/
+namespace highlight {
+
+/** \brief Base class for parsing. Works similar to a Turing machine.
+
+ The virtual class provides source code parsing functioality, based on
+ information stored in language definitions.<br>
+ Deriving classes have to define the output format.<br>
+ Codegenerator is a singleton class.
+
+* @author Andre Simon
+*/
+
+class CodeGenerator
+ {
+
+ public:
+
+ virtual ~CodeGenerator();
+
+ /**
+ Get appropriate Codegenerator instance
+ \param type Output file type (HTML, XHTML, RTF, LATEX, TEX, XSLFO, ANSI)
+ \param styleInfoPath Path to formatting style information
+ \param styleInPath Path to style definition input file (to be included in styleOutPath)
+ \param styleOutPath Path to style definition output file (CSS path for HTML output)
+ \param encoding Output file encoding name
+ \param includeStyle Switch to include style information in output file (only XHTML, HTML)
+ \param attachAnchors Switch to attach anchors to line numbers (only XHTML, HTML)
+ \param replaceQuotes Switch to replace quotes by \dq{} (only LATEX)
+ \param fopCompatible Switch to generate FO for Apache FOP (only XSLFO)
+ \param omitEncoding Switch to omit encoding info in output document
+ \param ln Set true if line numbers should be printed
+ \param lnz Set true if leading space of line numbers should be filled with 0's
+ \param fragment Set true if document header and footer should be omitted
+ \param numSpaces Number of spaces which replace a tab
+ \param lineWrappingMode Line wrapping mode
+ */
+ static CodeGenerator* getInstance(OutputType type,
+ const string& styleInfoPath,
+ const string& styleInPath,
+ const string& styleOutPath,
+ const string& encoding,
+ bool includeStyle,
+ bool attachAnchors,
+ bool replaceQuotes,
+ bool fopCompatible,
+ int numSpaces,
+ WrapMode lineWrappingMode,
+ bool ln,
+ bool lnz,
+ bool fragment,
+ bool omitEncoding );
+
+ /** Deletes the singleton CodeGenerator instance.
+ Call this method if getInstance was already called, or if you want to
+ free the momory after usage.*/
+ static void deleteInstance();
+
+ /**
+ Generates output
+ \param inFileName Path of input file (if empty use stdin)
+ \param outFileName Path of input file (if empty use stdout)
+
+ \return ParseError
+ */
+ ParseError printOutput(const string &inFileName, const string &outFileName);
+
+ /** \return True if document style was found */
+ bool styleFound();
+
+ /** \return True if reformatting of current input is disabled */
+ bool formattingDisabled();
+
+ /** \return True if reformatting of current input is possible */
+ bool formattingIsPossible();
+
+ /** \param langDefPath Absolute path to language definition
+ \return Failure: LOAD_FAILED; Reload necessary: LOAD_NEW,
+ no reload necessary: LOAD_NONE */
+ LoadResult initLanguage(const string& langDefPath);
+
+ /** \return Language definition*/
+ LanguageDefinition &getLanguage();
+
+ /** tell parser to output line numbers
+ \param flag true if line numbers should be printed
+ */
+ void setPrintLineNumbers(bool flag);
+
+ /** \return line number flag */
+ bool getPrintLineNumbers();
+
+
+ /** tell parser to output line numbers filled with zeroes
+ \param flag true if zeroes should be printed
+ */
+ void setPrintZeroes(bool flag);
+
+ /** \return print zeroes flag */
+ bool getPrintZeroes();
+
+ /** tell parser to omit document header and footer
+ \param flag true if output should be fragmented
+ */
+ void setFragmentCode(bool flag);
+
+ /** \return fragment flag */
+ bool getFragmentCode();
+
+ /** tell parser the style name
+ \param s path to style definition
+ */
+ void setStyleName(const string& s);
+
+ /** \return style path */
+ const string& getStyleName();
+
+ /** tell parser the wrapping mode
+ \param lineWrappingStyle wrapping style
+ \param lineLength max line length
+ \param numberSpaces number of spaces which replace a tab
+ */
+ void setPreformatting(WrapMode lineWrappingStyle, unsigned int lineLength,int numberSpaces);
+
+ /** \return wrapping style */
+ WrapMode getLineWrapping();
+
+ /** tell parser the include style definition in output
+ \param flag true if style should be included
+ */
+ void setIncludeStyle(bool flag);
+
+ /** Print style definitions to external file
+ \param outFile Path of external style definition
+ */
+ bool printExternalStyle(const string &outFile);
+
+ /** Print index file with all input file names
+ \param fileList List of output file names
+ \param outPath Output path
+ */
+ virtual bool printIndexFile(const vector<string> & fileList,
+ const string &outPath);
+
+ /** initialize source code indentation
+ \param indentSchemePath Path of indentation scheme
+ \return true id successfull
+ */
+ bool initIndentationScheme(const string&indentSchemePath);
+
+ /** Set style input path
+ \param s path to style input file
+ */
+ void setStyleInputPath(const string& path);
+
+ /** Set style output path
+ \param s path to style output file
+ */
+ void setStyleOutputPath(const string& path);
+
+/** Set output type
+ \param s output type
+ */
+ void setType(OutputType t);
+
+ /**
+ \return style input file path
+ */
+ const string& getStyleInputPath();
+
+ /**
+ \return style output file path
+ */
+ const string& getStyleOutputPath();
+
+protected:
+
+ CodeGenerator();
+
+ //! CodeGenerator Constructor
+ /**
+ \param colourTheme Name of coloring style being used
+ */
+ CodeGenerator(const string &colourTheme);
+
+ /** \param c Character to be masked
+ \return Escape sequence of output format */
+ virtual string maskCharacter(unsigned char c) = 0;
+
+ /** \param s string
+ \return Copy of s with all escaped characters */
+ string maskString(const string &s ) ;
+
+ /** \param s Symbol string
+ \param searchPos Position where search starts
+ \return Found state (integer value) */
+ State getState(const string &s, unsigned int searchPos);
+
+ /** \return Next identifier in current line of code */
+ string getIdentifier();
+
+ /** \return Next number in current line of code */
+ string getNumber();
+
+ /** Insert line number at the beginning of current output line */
+ virtual void insertLineNumber(bool insertNewLine=true);
+
+ /** Prints document footer*/
+ virtual string getFooter() = 0;
+
+ /** Prints document body*/
+ virtual void printBody() = 0;
+
+ /** prints document header
+ \param title Title of the document
+ */
+ virtual string getHeader(const string &title) = 0;
+
+ /** Get current line number
+ \return line number */
+ unsigned int getLineNumber();
+
+
+ /** Tag Delimiters for every colour style*/
+ vector <string> styleTagOpen, styleTagClose;
+
+ /** Description of document colour style*/
+ DocumentStyle docStyle;
+
+ /** Language definition*/
+ LanguageDefinition langInfo;
+
+ /** Tag for inserting line feeds*/
+ string newLineTag;
+
+ /** String that represents a white space in output */
+ string spacer;
+
+ /** file input*/
+ istream *in;
+
+ /** file output*/
+ ostream *out;
+
+ /** Tags which enclose white space indentation blocks */
+ string maskWsBegin, maskWsEnd;
+
+ /** Style comment delimiters */
+ string styleCommentOpen, styleCommentClose;
+
+ /** Test if maskWsBegin and maskWsEnd should be applied */
+ bool maskWs;
+
+ /** Test if whitespace sould always be separated from enclosing tokens */
+ bool excludeWs;
+
+ /** Test if header and footer should be omitted */
+ bool fragmentOutput;
+
+ /** Test if line numbers should be printed */
+ bool showLineNumbers;
+
+ /** Test if leading spyce of line number should be filled with zeroes*/
+ bool lineNumberFillZeroes;
+
+ /** Current line of input file*/
+ string line;
+
+ /** Current line number */
+ unsigned int lineNumber;
+
+ // Zeigt den aktuellen Zustand an
+ // wird nicht in getCurrentState gesetzt, da nur Zustände interessant
+ // sind, die als Index auf die styleCloseTags und styleOpenTags verwendet
+ // werden können
+ /** Current state*/
+ State currentState;
+
+ /** keyword class id, used to apply the corresponding keyword style*/
+ unsigned int currentKeywordClass;
+
+ /** Processes origin state */
+ void processRootState();
+
+ /** return line break sequence */
+ virtual string getNewLine();
+
+ /**
+ \param s current state
+ \return Index of style tag corresponding to the states
+ */
+ unsigned int getStyleID(State s, unsigned int kwClassID = 0);
+
+ /** \return line index */
+ unsigned int getLineIndex();
+
+ /** print all remaining white space*/
+ void flushWs();
+
+ /**
+ \return Content of user defined input style
+ */
+ string readUserStyleDef();
+
+ /**
+ \return Style definition of the chosen output format
+ */
+ virtual string getStyleDefinition() {return "";};
+
+ /** contains white space, which will be printed after a closing tag */
+ string wsBuffer;
+
+ /**
+ Flag to test if style definition should be included in output document
+ */
+ bool includeStyleDef;
+
+private:
+
+ CodeGenerator(const CodeGenerator&){}
+ CodeGenerator& operator=(CodeGenerator&){ return *this;}
+
+ static CodeGenerator* generator;
+
+ /** return matching open and close tags of the given state */
+ virtual string getMatchingOpenTag(unsigned int) = 0;
+ virtual string getMatchingCloseTag(unsigned int) = 0;
+
+ /** open a new tag, set current state to s*/
+ void openTag(State s);
+
+ /** close opened tag, clear current state */
+ void closeTag(State s);
+
+ void closeTag(unsigned int styleID);
+
+ void openTag(unsigned int styleID);
+
+ // path to style definition file
+ string stylePath;
+
+ // contains current position in line
+ unsigned int lineIndex;
+
+ /**last character of the last line*/
+ unsigned char terminatingChar;
+
+ /** Class for reformatting */
+ astyle::ASFormatter *formatter;
+
+ /** Class for line wrapping and tab replacement*/
+ PreFormatter *preFormatter;
+
+ /** Flag to test if formatting is enabled with current input document*/
+ bool formattingEnabled;
+
+
+ /** Flag to test if formatting is possible with current input document*/
+ bool formattingPossible;
+
+ /** contains the current token*/
+ string token;
+
+ string styleInputPath, styleOutputPath;
+
+ /** Resets parser to origin state, call this after every file conversion */
+ void reset();
+
+ /** read new line from in stream */
+ bool readNewLine(string &newLine);
+
+ /** return next character from in stream */
+ unsigned char getInputChar();
+
+ OutputType outputType;
+
+ /** return new state */
+ State getCurrentState ( bool lastStateWasNumber=false);
+
+ /** Methods that represent a parsing state */
+ bool processKeywordState(State myState) ;
+ bool processNumberState() ;
+ bool processMultiLineCommentState();
+ bool processSingleLineCommentState();
+ bool processStringState(State oldState);
+ bool processEscapeCharState();
+ bool processDirectiveState();
+ bool processTagState();
+ bool processSymbolState();
+ void processWsState();
+
+ /** gibt true zurck, falls c ein erlaubter Character innerhalb von Keyword
+ oder Typbezeichner ist */
+ bool isAllowedChar(char c) ;
+
+ /** returns true if curret token is the first in line and no whitespace */
+ bool isFirstNonWsChar() ;
+
+ /** print escaped token and clears it */
+ void printMaskedToken(bool flushWhiteSpace=true);
+
+ /** print escape sequence */
+ void skipEscapeSequence();
+
+ void closeKWTag(unsigned int styleID);
+ void openKWTag(unsigned int styleID);
+
+ /** look for special commands in comments, take action in derived class
+ \return true if command was found
+ */
+ bool checkSpecialCmd();
+
+ };
+}
+
+#endif
+
+/*
+ * Copyright (c) 1998,1999,2000,2001,2002 Tal Davidson. All rights reserved.
+ *
+ * compiler_defines.h (1 January 1999)
+ * by Tal Davidson (davidsont@bigfoot.com)
+ * This file is a part of "Artistic Style" - an indentater and reformatter
+ * of C, C++, C# and Java source files.
+ *
+ * The "Artistic Style" project, including all files needed to compile it,
+ * is free software; you can redistribute it and/or use it and/or modify it
+ * under the terms of the GNU General Public License as published
+ * by the Free Software Foundation; either version 2 of the License,
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this program.
+ */
+
+
+
+
+
+/*
+ * comment out the line below if your compiler does NOT understand NAMESPACES
+ */
+#define USES_NAMESPACE
+
+
+#if defined(__GNUC__) && __GNUC__ < 3
+// for G++ implementation of string.compare:
+#define COMPARE(place, length, str) compare((str), (place), (length))
+#else
+// for standard implementation of string.compare:
+#define COMPARE(place, length, str) compare((place), (length), (str))
+#endif
+
+
+// Fix by John A. McNamara
+// Get rid of annoying MSVC warnings on debug builds about lengths of
+// identifiers in template instantiations.
+#ifdef _MSC_VER
+#pragma warning( disable:4786 )
+#endif
+
+/***************************************************************************
+ configurationreader.cpp - description
+ -------------------
+ begin : Son Nov 10 2002
+ copyright : (C) 2002 by André Simon
+ email : andre.simon1@gmx.de
+ ***************************************************************************/
+
+/***************************************************************************
+ * *
+ * 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 of the License, or *
+ * (at your option) any later version. *
+ * *
+ ***************************************************************************/
+
+#include "configurationreader.h"
+
+using namespace std;
+
+ConfigurationReader::ConfigurationReader(const string & configuration_path)
+{
+ ifstream in (configuration_path.c_str());
+ fileFound=in;
+ if (fileFound) {
+ string line;
+ line.reserve(500);
+ unsigned int lineBegin;
+ size_t delimPos;
+ string paramName, paramValue;
+ while (getline(in, line)) {
+ lineBegin=line.find_first_not_of("\t ");
+ if ((line.size()>2) && (lineBegin!=string::npos)
+ && (line.at(lineBegin)!='#')) { //comment?
+ if (line[lineBegin]=='$') { // neuer Parametername?
+ delimPos=line.find("=",lineBegin)-1;
+ if (delimPos!=string::npos) {
+ paramName=StringTools::trimRight(
+ StringTools::lowerCase(line.substr(lineBegin+1, delimPos)));
+ parameterNames.push_back(paramName);
+ paramValue=line.substr(delimPos+2, line.length());
+ }
+ } else { // line belongs to last parameter
+ paramValue=line;
+ }
+ if (parameterMap[paramName].empty()) {
+ parameterMap[paramName] = paramValue;
+ } else {
+ parameterMap[paramName]+= (" "+paramValue);
+ }
+ } //if ((lineBegin!=string::npos) && (line.at(lineBegin)!='#'))
+ } //while
+ in.close();
+ } //if (in)
+}
+
+ConfigurationReader::~ConfigurationReader()
+{
+}
+
+bool ConfigurationReader::found()
+{
+ return fileFound;
+}
+
+string &ConfigurationReader::getParameter(const string & paramName)
+{
+ return parameterMap[paramName] ;
+}
+
+const char* ConfigurationReader::getCParameter(const string & paramName)
+{
+ return parameterMap[paramName].c_str() ;
+}
+
+vector<string> &ConfigurationReader::getParameterNames()
+{
+ return parameterNames;
+}
+/***************************************************************************
+ configurationreader.h - description
+ -------------------
+ begin : Son Nov 10 2002
+ copyright : (C) 2002 by Andr�Simon
+ email : andre.simon1@gmx.de
+ ***************************************************************************/
+
+/***************************************************************************
+ * *
+ * 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 of the License, or *
+ * (at your option) any later version. *
+ * *
+ ***************************************************************************/
+
+#ifndef CONFIGURATIONREADER_H
+#define CONFIGURATIONREADER_H
+
+#include <string>
+#include <sstream>
+#include <map>
+#include <iostream>
+#include <fstream>
+#include <vector>
+
+#include "stringtools.h"
+
+using namespace std;
+
+/** Maps parameter keys to values*/
+typedef map<string, string> ParameterMap;
+
+
+/** \brief Class to handle ASCII config files
+
+ Configuration file format:<br>
+ $ParamName=ParamValue<br>
+ ParamValue may be splittet over multiple lines<br>
+ ParamName is not case sensitive<br>
+ Comments start with # as the first character of a line
+
+ **/
+
+class ConfigurationReader
+ {
+ public:
+ /** Constructor
+ \param configuration_path Path to configuration file
+ */
+ ConfigurationReader(const string & configuration_path);
+ ~ConfigurationReader();
+
+ /** \param paramName Name of parameter
+ \return Value of parameter */
+ string &getParameter(const string & paramName);
+
+ /** \param paramName Name of parameter
+ \return Value of parameter */
+ const char* getCParameter(const string & paramName);
+
+ /** \return True if config file exists */
+ bool found();
+
+ /** \return List of parameter names */
+ vector<string> &getParameterNames();
+
+ private:
+ ParameterMap parameterMap;
+ bool fileFound;
+ vector<string> parameterNames;
+ };
+
+#endif
+/***************************************************************************
+ dataDir.cpp - description
+ -------------------
+ begin : Sam M� 1 2003
+ copyright : (C) 2003 by André Simon
+ email : andre.simon1@gmx.de
+ ***************************************************************************/
+
+/***************************************************************************
+ * *
+ * 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 of the License, or *
+ * (at your option) any later version. *
+ * *
+ ***************************************************************************/
+
+#include "datadir.h"
+
+using namespace std;
+
+bool DataDir::searchDataDir(const string &userDefinedDir){
+#ifndef _WIN32
+
+ bool found = false;
+ //falls kein Datenverzeichnis angegeben, startIndex auf 1 setzen
+ int searchStartIndex=(userDefinedDir.empty());
+
+ string possibleDirs[] ={ userDefinedDir,
+ #ifdef HL_DATA_DIR
+ HL_DATA_DIR,
+ #endif
+ "/usr/share/highlight/"
+ };
+
+ for (int i=searchStartIndex;i<
+ #ifdef HL_DATA_DIR
+ 3
+ #else
+ 2
+ #endif
+ ;i++)
+ {
+ if (fileExists(possibleDirs[i]))
+ {
+ dataDir=possibleDirs[i];
+ found = true;
+ }
+ if (found) {
+ break;
+ }
+ else {
+ if (!searchStartIndex)
+ cerr << "highlight: directory "
+ << userDefinedDir
+ << " specified by data-dir option not found.\n"
+ << " Searching another standard directory.\n";
+
+ }
+ }
+ return found;
+#else
+ dataDir=userDefinedDir;
+ return true;
+#endif
+
+}
+
+DataDir::DataDir()
+{
+}
+
+void DataDir::setAdditionalDataDir(const string& dir){
+ additionalDataDir=dir;
+}
+
+const string &DataDir::getDir()
+{
+ return dataDir;
+}
+
+const string DataDir::getLangDefDir()
+{
+ return dataDir+"langDefs"+Platform::pathSeparator;
+}
+
+const string DataDir::getThemeDir()
+{
+ return dataDir+"themes"+Platform::pathSeparator;
+}
+
+const string DataDir::getIndentSchemesDir()
+{
+ return dataDir+"indentSchemes"+Platform::pathSeparator;
+}
+
+
+const string DataDir::getAdditionalLangDefDir()
+{
+ return additionalDataDir+"langDefs"+Platform::pathSeparator;
+}
+
+const string DataDir::getAdditionalThemeDir()
+{
+ return additionalDataDir+"themes"+Platform::pathSeparator;
+}
+const string DataDir::getAdditionalIndentSchemesDir()
+{
+ return additionalDataDir+"indentSchemes"+Platform::pathSeparator;
+}
+
+
+const string DataDir::getHelpMsgDir()
+{
+ return dataDir+"helpmsg"+Platform::pathSeparator;
+}
+
+const string DataDir::searchForLangDef(const string & langDef){
+ if (!additionalDataDir.empty()){
+ string path=getAdditionalLangDefDir()+langDef;
+ if (fileExists(path)){
+ return path;
+ }
+ }
+ return getLangDefDir()+langDef;
+}
+
+const string DataDir::searchForTheme(const string & theme){
+ if (!additionalDataDir.empty()){
+ string path=getAdditionalThemeDir()+theme;
+ if (fileExists(path)){
+ return path;
+ }
+ }
+ return getThemeDir()+theme;
+}
+
+const string DataDir::searchForIndentScheme(const string & scheme){
+ if (!additionalDataDir.empty()){
+ string path=getAdditionalIndentSchemesDir()+scheme;
+ if (fileExists(path)){
+ return path;
+ }
+ }
+ return getIndentSchemesDir()+scheme;
+}
+
+
+bool DataDir::fileExists(const string&f){
+ ifstream file(f.c_str());
+ bool exists=!file.fail();
+ file.close();
+ return exists;
+}
+/***************************************************************************
+ datadir.h - description
+ -------------------
+ begin : Sam M� 1 2003
+ copyright : (C) 2003 by Andre Simon
+ email : andre.simon1@gmx.de
+ ***************************************************************************/
+
+/***************************************************************************
+ * *
+ * 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 of the License, or *
+ * (at your option) any later version. *
+ * *
+ ***************************************************************************/
+
+#ifndef DATADIR_H
+#define DATADIR_H
+
+#include <string>
+#include <fstream>
+#include <iostream>
+//#include "stringtools.h"
+#include "platform_fs.h"
+
+using namespace std;
+
+ /** \brief Manages access to installation directories.
+
+ Apart from the standard installation directory, one can define additional
+ search paths.
+ **/
+
+class DataDir
+ {
+ string dataDir;
+ string additionalDataDir;
+ bool fileExists(const string&f);
+
+ public:
+
+ DataDir();
+
+ /** search for a valid installation directory
+ \param userDefinedDir Directory defined by user
+ \return True if directory was found */
+ bool searchDataDir(const string &userDefinedDir);
+
+ /** add another installation directory, which is added to search path
+ \param dir Directory defined by user */
+ void setAdditionalDataDir(const string& dir);
+
+ /** \return Data installation directory */
+ const string & getDir() ;
+
+ /** \return Location of languafe definitions */
+ const string getLangDefDir() ;
+
+ /** \return Location of themes */
+ const string getThemeDir() ;
+
+ /** \return Location of indentation schemes */
+ const string getIndentSchemesDir();
+
+ /** \return User defined location of indentation schemes */
+ const string getAdditionalIndentSchemesDir();
+
+ /** \return User defined location of languafe definitions */
+ const string getAdditionalLangDefDir() ;
+
+ /** \return User defined location of themes */
+ const string getAdditionalThemeDir() ;
+
+ /** \return Location of help files */
+ const string getHelpMsgDir() ;
+
+ /** \param langDef Name of language definition
+ \return Absolute path of definiton found in a data directory */
+ const string searchForLangDef(const string & langDef);
+
+ /** \param theme Name of colour theme file
+ \return Absolute path of theme found in a data directory */
+ const string searchForTheme(const string & theme);
+
+ /** \param scheme Name of indent scheme file
+ \return Absolute path of theme found in a data directory */
+ const string searchForIndentScheme(const string & scheme);
+ };
+
+#endif
+/***************************************************************************
+ documentstyle.cpp - description
+ -------------------
+ begin : Son Nov 10 2002
+ copyright : (C) 2002 by Andre Simon
+ email : andre.simon1@gmx.de
+ ***************************************************************************/
+
+/***************************************************************************
+ * *
+ * 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 of the License, or *
+ * (at your option) any later version. *
+ * *
+ ***************************************************************************/
+
+#include "documentstyle.h"
+
+namespace highlight {
+
+DocumentStyle::DocumentStyle(const string &styleDefinitionFile)
+{
+ fileFound=load(styleDefinitionFile);
+}
+DocumentStyle::DocumentStyle():fileFound(false)
+{}
+
+bool DocumentStyle::load(const string &styleDefinitionPath)
+{
+ ConfigurationReader styleConfig(styleDefinitionPath);
+ if (styleConfig.found()){
+ fontsize = styleConfig.getParameter("fontsize");
+ bgColour.setRGBValues(styleConfig.getParameter("bgcolour"));
+ defaultElem.set(styleConfig.getParameter("defaultcolour"));
+ comment.set(styleConfig.getParameter("comment"));
+ directive.set(styleConfig.getParameter("directive"));
+ str.set(styleConfig.getParameter("string"));
+ escapeChar.set(styleConfig.getParameter("escapechar"));
+ number.set(styleConfig.getParameter("number"));
+ dstr.set(styleConfig.getParameter("string_directive"));
+ line.set(styleConfig.getParameter("line"));
+
+
+ string tmpstr;
+ // TODO: Remove this check as soon as all themes have a brackets attribute
+ tmpstr=styleConfig.getParameter("symbol");
+ if (tmpstr.empty()) {
+ tmpstr=styleConfig.getParameter("defaultcolour");
+ }
+ symbol.set(tmpstr);
+
+// TODO: Remove this check as soon as all themes have a sl-comment attribute
+ tmpstr=styleConfig.getParameter("sl-comment");
+ if (tmpstr.empty()) {
+ tmpstr=styleConfig.getParameter("comment");
+ }
+ slcomment.set(tmpstr);
+
+ string paramVal;
+ vector<string> paramNames=styleConfig.getParameterNames();
+
+ //collect keyword classes, save corresponding style definition
+ for (unsigned int i=0;i<paramNames.size();i++){
+ paramVal=paramNames[i];
+ if (paramVal.find("kw_class") != string::npos){
+ keywordStyles.insert(make_pair(StringTools::getParantheseVal(paramVal),
+ new ElementStyle(styleConfig.getParameter(paramVal))));
+ }
+ }
+
+ fileFound = true;
+ }
+ else {
+ fileFound = false;
+ }
+ return fileFound;
+}
+
+DocumentStyle::~DocumentStyle()
+{
+ for(KSIterator iter = keywordStyles.begin(); iter != keywordStyles.end(); iter++){
+ delete (*iter).second; //remove ElementStyle*
+ }
+}
+
+string& DocumentStyle::getFontSize()
+ {
+ return fontsize;
+ }
+StyleColour& DocumentStyle::getBgColour()
+ {
+ return bgColour;
+ }
+ElementStyle& DocumentStyle::getDefaultStyle()
+ {
+ return defaultElem;
+ }
+ElementStyle& DocumentStyle::getCommentStyle()
+ {
+ return comment;
+ }
+ElementStyle& DocumentStyle::getSingleLineCommentStyle()
+ {
+ return slcomment;
+ }
+
+
+ElementStyle& DocumentStyle::getStringStyle()
+ {
+ return str;
+ }
+ElementStyle& DocumentStyle::getDirectiveStringStyle()
+ {
+ return dstr;
+ }
+ElementStyle& DocumentStyle::getEscapeCharStyle()
+ {
+ return escapeChar;
+ }
+ElementStyle& DocumentStyle::getNumberStyle()
+ {
+ return number;
+ }
+ElementStyle& DocumentStyle::getDirectiveStyle()
+ {
+ return directive;
+ }
+ElementStyle& DocumentStyle::getLineStyle()
+ {
+ return line;
+ }
+ElementStyle& DocumentStyle::getSymbolStyle()
+ {
+ return symbol;
+ }
+bool DocumentStyle::found () const
+ {
+ return fileFound;
+ }
+ElementStyle& DocumentStyle::getKeywordStyle(const string &className){
+ if (!keywordStyles.count(className)) return defaultElem;
+ return *keywordStyles[className];
+}
+
+vector <string> DocumentStyle::getClassNames(){
+ vector <string> kwClassNames;
+ for(KSIterator iter = keywordStyles.begin(); iter != keywordStyles.end(); iter++){
+ kwClassNames.push_back( (*iter).first);
+ }
+ return kwClassNames;
+}
+
+KeywordStyles& DocumentStyle::getKeywordStyles(){
+ return keywordStyles;
+}
+
+}
+/***************************************************************************
+ documentstyle.h - description
+ -------------------
+ begin : Son Nov 10 2002
+ copyright : (C) 2002 by Andre Simon
+ email : andre.simon1@gmx.de
+ ***************************************************************************/
+
+/***************************************************************************
+ * *
+ * 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 of the License, or *
+ * (at your option) any later version. *
+ * *
+ ***************************************************************************/
+
+#ifndef DOCUMENTSTYLE_H
+#define DOCUMENTSTYLE_H
+
+#include <string>
+#include <iostream>
+#include "configurationreader.h"
+#include "elementstyle.h"
+#include "stylecolour.h"
+
+using namespace std;
+
+namespace highlight {
+
+/** maps keyword class names and the corresponding formatting information*/
+typedef map <string, ElementStyle*> KeywordStyles;
+
+/** iterator for keyword styles*/
+typedef KeywordStyles::iterator KSIterator;
+
+/** \brief Contains information about document formatting properties.
+
+* @author Andre Simon
+*/
+
+class DocumentStyle
+ {
+ private:
+ ElementStyle comment, slcomment, str, dstr,
+ escapeChar, number, directive, line, symbol;
+ ElementStyle defaultElem;
+ StyleColour bgColour;
+
+ string fontsize;
+ bool fileFound;
+
+ KeywordStyles keywordStyles;
+
+ public:
+ /** Constructor
+ \param styleDefinitionPath Style definition path */
+ DocumentStyle(const string & styleDefinitionPath);
+ DocumentStyle();
+ ~DocumentStyle();
+
+ /** load sytle definition
+ \param styleDefinitionFile Style definition path
+ \return True if successfull */
+ bool load(const string & styleDefinitionFile);
+
+ /** \return class names defined in the theme file */
+ vector <string> getClassNames();
+
+ /** \return keyword styles */
+ KeywordStyles& getKeywordStyles();
+
+ /** \return Font size */
+ string &getFontSize() ;
+
+ /** \return Background colour*/
+ StyleColour& getBgColour();
+
+ /** \return Style of default (unrecognized) strings */
+ ElementStyle & getDefaultStyle() ;
+
+ /** \return Comment style*/
+ ElementStyle & getCommentStyle() ;
+
+ /** \return Single line comment style*/
+ ElementStyle& getSingleLineCommentStyle() ;
+
+ /** \return Keyword style*/
+ ElementStyle & getKeywordStyle() ;
+
+ /** \return String style*/
+ ElementStyle & getStringStyle() ;
+
+ /** \return Directive line string style*/
+ ElementStyle & getDirectiveStringStyle() ;
+
+ /** \return Escape character style*/
+ ElementStyle & getEscapeCharStyle() ;
+
+ /** \return Number style*/
+ ElementStyle & getNumberStyle() ;
+
+ /** \return Directive style*/
+ ElementStyle & getDirectiveStyle() ;
+
+ /** \return Type style*/
+ ElementStyle & getTypeStyle() ;
+
+ /** \return Line number style*/
+ ElementStyle & getLineStyle() ;
+
+ /** \return Bracket style*/
+ ElementStyle & getSymbolStyle() ;
+
+ /**
+ \param className Name of keyword class
+ \return keyword style of the given className
+ */
+ ElementStyle & getKeywordStyle(const string &className);
+
+ /** \return True if language definition was found */
+ bool found() const ;
+ };
+
+}
+
+#endif
+/***************************************************************************
+ elementstyle.cpp - description
+ -------------------
+ begin : Son Nov 10 2002
+ copyright : (C) 2002 by André Simon
+ email : andre.simon1@gmx.de
+ ***************************************************************************/
+
+/***************************************************************************
+ * *
+ * 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 of the License, or *
+ * (at your option) any later version. *
+ * *
+ ***************************************************************************/
+
+#include "elementstyle.h"
+
+namespace highlight {
+
+ElementStyle::ElementStyle(StyleColour col, bool b, bool i, bool u)
+ : colour(col) , bold(b), italic(i), underline(u)
+{}
+
+ElementStyle:: ElementStyle(const string & elementStyleString)
+ : bold(false), italic(false), underline(false)
+{
+ set(elementStyleString);
+}
+
+ElementStyle::ElementStyle()
+ : bold(false), italic(false), underline(false)
+{}
+
+void ElementStyle::set(const string & elementStyleString){
+
+ istringstream valueStream(elementStyleString.c_str());
+ string r, g, b, attr;
+ valueStream >> r;
+ valueStream >> g;
+ valueStream >> b;
+ colour.setRedValue(r);
+ colour.setGreenValue(g);
+ colour.setBlueValue(b);
+ while ( valueStream >> attr)
+ {
+ if (attr=="italic")
+ {
+ italic = true;
+ }
+ else if (attr=="bold")
+ {
+ bold = true;
+ }
+ else if (attr=="underline")
+ {
+ underline = true;
+ }
+ }
+}
+
+ElementStyle::~ElementStyle()
+{}
+
+bool ElementStyle::isItalic() const
+{
+ return italic;
+}
+bool ElementStyle::isBold() const
+{
+ return bold;
+}
+bool ElementStyle::isUnderline() const
+{
+ return underline;
+}
+StyleColour ElementStyle::getColour() const
+{
+ return colour;
+}
+
+}
+/***************************************************************************
+ elementstyle.h - description
+ -------------------
+ begin : Son Nov 10 2002
+ copyright : (C) 2002 by Andre Simon
+ email : andre.simon1@gmx.de
+ ***************************************************************************/
+
+/***************************************************************************
+ * *
+ * 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 of the License, or *
+ * (at your option) any later version. *
+ * *
+ ***************************************************************************/
+
+#ifndef ELEMENTSTYLE_H
+#define ELEMENTSTYLE_H
+
+#include <sstream>
+
+#include "stylecolour.h"
+
+using namespace std;
+
+namespace highlight {
+
+/** \brief The class stores the basic text formatting properties.
+
+* @author Andre Simon
+*/
+
+class ElementStyle {
+ public:
+
+ /** Constructor
+ \param col Style colour
+ \param b Bold flag
+ \param i Italic flag
+ \param u Underline flag */
+ ElementStyle(StyleColour col, bool b, bool i, bool u);
+
+ /** Constuctor
+ \param elementStyleString String with fotmatting information */
+ ElementStyle(const string & elementStyleString);
+
+ ElementStyle();
+
+ ~ElementStyle();
+
+ /** initialize object
+ \param elementStyleString String which contains formatting attributes
+ */
+ void set(const string & elementStyleString);
+
+ /** \return True if italic */
+ bool isItalic() const;
+
+ /** \return True if bold */
+ bool isBold() const;
+
+ /** \return True if underline */
+ bool isUnderline() const;
+
+ /** \return Element colour */
+ StyleColour getColour() const;
+
+ private:
+ StyleColour colour;
+ bool bold, italic, underline;
+ };
+
+}
+
+#endif
+//
+// C++ Interface: enums
+//
+// Description:
+//
+//
+// Author: Andre Simon <andre.simon1@gmx.de>, (C) 2004
+//
+// Copyright: See COPYING file that comes with this distribution
+//
+//
+
+#ifndef ENUMS_H
+#define ENUMS_H
+
+namespace highlight {
+
+/** states which may occour during input file parsing*/
+enum State {
+ STANDARD=0,
+ STRING,
+ NUMBER,
+ SL_COMMENT,
+ ML_COMMENT_BEGIN,
+ ESC_CHAR,
+ DIRECTIVE_LINE,
+ DIRECTIVE_STRING,
+ LINENUMBER,
+ SYMBOL,
+
+ // Konstanten ab hier duefen nicht mehr als Array-Indizes benutzt werden!!
+ KEYWORD,
+ ML_COMMENT_END,
+ DIRECTIVE_LINE_END,
+ TAG_BEGIN,
+ TAG_END,
+ KEYWORD_BEGIN,
+ KEYWORD_END,
+
+ _UNKNOWN=100,
+ _EOL,
+ _EOF,
+ _WS
+} ;
+
+/** Parser return values*/
+enum ParseError{
+ PARSE_OK,
+ BAD_INPUT=1,
+ BAD_OUTPUT=2,
+ BAD_STYLE=4
+};
+
+/** line wrapping modes*/
+enum WrapMode {
+ WRAP_DISABLED,
+ WRAP_SIMPLE,
+ WRAP_DEFAULT
+};
+
+/** language definition loading results*/
+enum LoadResult{
+ LOAD_FAILED,
+ LOAD_NEW,
+ LOAD_NONE
+};
+
+/** output formats */
+enum OutputType {
+ HTML,
+ XHTML,
+ TEX,
+ LATEX,
+ RTF,
+ XSLFO,
+ XML,
+ ANSI
+};
+
+}
+
+#endif
+/* Getopt for GNU.
+ NOTE: getopt is now part of the C library, so if you don't know what
+ "Keep this file name-space clean" means, talk to roland@gnu.ai.mit.edu
+ before changing it!
+
+ Copyright (C) 1987, 88, 89, 90, 91, 92, 1993
+ Free Software Foundation, Inc.
+
+ 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, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#ifndef __STDC__
+# ifndef const
+# define const
+# endif
+#endif
+
+/* This tells Alpha OSF/1 not to define a getopt prototype in <stdio.h>. */
+#ifndef _NO_PROTO
+#define _NO_PROTO
+#endif
+
+#include <cstdio>
+#include <cstring>
+//#include "tailor.h"
+
+/* Comment out all this code if we are using the GNU C Library, and are not
+ actually compiling the library itself. This code is part of the GNU C
+ Library, but also included in many other GNU distributions. Compiling
+ and linking in this code is a waste when using the GNU C library
+ (especially if it is a shared library). Rather than having every GNU
+ program understand `configure --with-gnu-libc' and omit the object files,
+ it is simpler to just do this in the source for each such file. */
+
+#if defined (_LIBC) || !defined (__GNU_LIBRARY__)
+
+
+/* This needs to come after some library #include
+ to get __GNU_LIBRARY__ defined. */
+#ifdef __GNU_LIBRARY__
+/* Don't include stdlib.h for non-GNU C libraries because some of them
+ contain conflicting prototypes for getopt. */
+#include <stdlib.h>
+#endif /* GNU C library. */
+
+/* If GETOPT_COMPAT is defined, `+' as well as `--' can introduce a
+ long-named option. Because this is not POSIX.2 compliant, it is
+ being phased out. */
+/* #define GETOPT_COMPAT */
+
+/* This version of `getopt' appears to the caller like standard Unix `getopt'
+ but it behaves differently for the user, since it allows the user
+ to intersperse the options with the other arguments.
+
+ As `getopt' works, it permutes the elements of ARGV so that,
+ when it is done, all the options precede everything else. Thus
+ all application programs are extended to handle flexible argument order.
+
+ Setting the environment variable POSIXLY_CORRECT disables permutation.
+ Then the behavior is completely standard.
+
+ GNU application programs can use a third alternative mode in which
+ they can distinguish the relative order of options and other arguments. */
+
+#include "getopt.h"
+
+/* For communication from `getopt' to the caller.
+ When `getopt' finds an option that takes an argument,
+ the argument value is returned here.
+ Also, when `ordering' is RETURN_IN_ORDER,
+ each non-option ARGV-element is returned here. */
+
+char *optarg = 0;
+
+/* Index in ARGV of the next element to be scanned.
+ This is used for communication to and from the caller
+ and for communication between successive calls to `getopt'.
+
+ On entry to `getopt', zero means this is the first call; initialize.
+
+ When `getopt' returns EOF, this is the index of the first of the
+ non-option elements that the caller should itself scan.
+
+ Otherwise, `optind' communicates from one call to the next
+ how much of ARGV has been scanned so far. */
+
+/* XXX 1003.2 says this must be 1 before any call. */
+int optind = 0;
+
+/* The next char to be scanned in the option-element
+ in which the last option character we returned was found.
+ This allows us to pick up the scan where we left off.
+
+ If this is zero, or a null string, it means resume the scan
+ by advancing to the next ARGV-element. */
+
+static char *nextchar;
+
+/* Callers store zero here to inhibit the error message
+ for unrecognized options. */
+
+int opterr = 1;
+
+/* Set to an option character which was unrecognized.
+ This must be initialized on some systems to avoid linking in the
+ system's own getopt implementation. */
+
+#define BAD_OPTION '\0'
+int optopt = BAD_OPTION;
+
+/* Describe how to deal with options that follow non-option ARGV-elements.
+
+ If the caller did not specify anything,
+ the default is REQUIRE_ORDER if the environment variable
+ POSIXLY_CORRECT is defined, PERMUTE otherwise.
+
+ REQUIRE_ORDER means don't recognize them as options;
+ stop option processing when the first non-option is seen.
+ This is what Unix does.
+ This mode of operation is selected by either setting the environment
+ variable POSIXLY_CORRECT, or using `+' as the first character
+ of the list of option characters.
+
+ PERMUTE is the default. We permute the contents of ARGV as we scan,
+ so that eventually all the non-options are at the end. This allows options
+ to be given in any order, even with programs that were not written to
+ expect this.
+
+ RETURN_IN_ORDER is an option available to programs that were written
+ to expect options and other ARGV-elements in any order and that care about
+ the ordering of the two. We describe each non-option ARGV-element
+ as if it were the argument of an option with character code 1.
+ Using `-' as the first character of the list of option characters
+ selects this mode of operation.
+
+ The special argument `--' forces an end of option-scanning regardless
+ of the value of `ordering'. In the case of RETURN_IN_ORDER, only
+ `--' can cause `getopt' to return EOF with `optind' != ARGC. */
+
+static enum
+{
+ REQUIRE_ORDER, PERMUTE, RETURN_IN_ORDER
+} ordering;
+
+#ifdef __GNU_LIBRARY__
+/* We want to avoid inclusion of string.h with non-GNU libraries
+ because there are many ways it can cause trouble.
+ On some systems, it contains special magic macros that don't work
+ in GCC. */
+#include <string.h>
+#define my_index strchr
+#define my_strlen strlen
+#else
+
+/* Avoid depending on library functions or files
+ whose names are inconsistent. */
+
+#if __STDC__ || defined(PROTO)
+ #ifndef _WIN32
+ // Solaris compilation fix
+ extern "C" {
+ char *getenv(const char *name);
+ int strncmp(const char *s1, const char *s2, int n);
+ }
+ // extern char *getenv(const char *name);
+ // extern int strncmp(const char *s1, const char *s2, int n);
+ #endif
+ extern int strcmp (const char *s1, const char *s2);
+ static int my_strlen(const char *s);
+ static char *my_index (const char *str, int chr);
+#else
+ #ifndef _WIN32
+ extern char *getenv ();
+ #endif
+#endif
+
+static int
+my_strlen (const char *str)
+
+{
+ int n = 0;
+ while (*str++)
+ n++;
+ return n;
+}
+
+static char *
+my_index ( const char *str,
+ int chr)
+
+{
+ while (*str)
+ {
+ if (*str == chr)
+ return (char *) str;
+ str++;
+ }
+ return 0;
+}
+
+#endif /* GNU C library. */
+
+/* Handle permutation of arguments. */
+
+/* Describe the part of ARGV that contains non-options that have
+ been skipped. `first_nonopt' is the index in ARGV of the first of them;
+ `last_nonopt' is the index after the last of them. */
+
+static int first_nonopt;
+static int last_nonopt;
+
+/* Exchange two adjacent subsequences of ARGV.
+ One subsequence is elements [first_nonopt,last_nonopt)
+ which contains all the non-options that have been skipped so far.
+ The other is elements [last_nonopt,optind), which contains all
+ the options processed since those non-options were skipped.
+
+ `first_nonopt' and `last_nonopt' are relocated so that they describe
+ the new indices of the non-options in ARGV after they are moved.
+
+ To perform the swap, we first reverse the order of all elements. So
+ all options now come before all non options, but they are in the
+ wrong order. So we put back the options and non options in original
+ order by reversing them again. For example:
+ original input: a b c -x -y
+ reverse all: -y -x c b a
+ reverse options: -x -y c b a
+ reverse non options: -x -y a b c
+*/
+
+#if __STDC__ || defined(PROTO)
+static void exchange (char **argv);
+#endif
+
+static void
+exchange (char **argv)
+
+{
+ char *temp, **first, **last;
+
+ /* Reverse all the elements [first_nonopt, optind) */
+ first = &argv[first_nonopt];
+ last = &argv[optind-1];
+ while (first < last) {
+ temp = *first; *first = *last; *last = temp; first++; last--;
+ }
+ /* Put back the options in order */
+ first = &argv[first_nonopt];
+ first_nonopt += (optind - last_nonopt);
+ last = &argv[first_nonopt - 1];
+ while (first < last) {
+ temp = *first; *first = *last; *last = temp; first++; last--;
+ }
+
+ /* Put back the non options in order */
+ first = &argv[first_nonopt];
+ last_nonopt = optind;
+ last = &argv[last_nonopt-1];
+ while (first < last) {
+ temp = *first; *first = *last; *last = temp; first++; last--;
+ }
+}
+
+/* Scan elements of ARGV (whose length is ARGC) for option characters
+ given in OPTSTRING.
+
+ If an element of ARGV starts with '-', and is not exactly "-" or "--",
+ then it is an option element. The characters of this element
+ (aside from the initial '-') are option characters. If `getopt'
+ is called repeatedly, it returns successively each of the option characters
+ from each of the option elements.
+
+ If `getopt' finds another option character, it returns that character,
+ updating `optind' and `nextchar' so that the next call to `getopt' can
+ resume the scan with the following option character or ARGV-element.
+
+ If there are no more option characters, `getopt' returns `EOF'.
+ Then `optind' is the index in ARGV of the first ARGV-element
+ that is not an option. (The ARGV-elements have been permuted
+ so that those that are not options now come last.)
+
+ OPTSTRING is a string containing the legitimate option characters.
+ If an option character is seen that is not listed in OPTSTRING,
+ return BAD_OPTION after printing an error message. If you set `opterr' to
+ zero, the error message is suppressed but we still return BAD_OPTION.
+
+ If a char in OPTSTRING is followed by a colon, that means it wants an arg,
+ so the following text in the same ARGV-element, or the text of the following
+ ARGV-element, is returned in `optarg'. Two colons mean an option that
+ wants an optional arg; if there is text in the current ARGV-element,
+ it is returned in `optarg', otherwise `optarg' is set to zero.
+
+ If OPTSTRING starts with `-' or `+', it requests different methods of
+ handling the non-option ARGV-elements.
+ See the comments about RETURN_IN_ORDER and REQUIRE_ORDER, above.
+
+ Long-named options begin with `--' instead of `-'.
+ Their names may be abbreviated as long as the abbreviation is unique
+ or is an exact match for some defined option. If they have an
+ argument, it follows the option name in the same ARGV-element, separated
+ from the option name by a `=', or else the in next ARGV-element.
+ When `getopt' finds a long-named option, it returns 0 if that option's
+ `flag' field is nonzero, the value of the option's `val' field
+ if the `flag' field is zero.
+
+ The elements of ARGV aren't really const, because we permute them.
+ But we pretend they're const in the prototype to be compatible
+ with other systems.
+
+ LONGOPTS is a vector of `struct option' terminated by an
+ element containing a name which is zero.
+
+ LONGIND returns the index in LONGOPT of the long-named option found.
+ It is only valid when a long-named option has been found by the most
+ recent call.
+
+ If LONG_ONLY is nonzero, '-' as well as '--' can introduce
+ long-named options. */
+
+int
+_getopt_internal ( int argc,
+ char *const *argv,
+ const char *optstring,
+ const struct option *longopts,
+ int *longind,
+ int long_only)
+
+{
+ int option_index;
+
+ optarg = 0;
+
+ /* Initialize the internal data when the first call is made.
+ Start processing options with ARGV-element 1 (since ARGV-element 0
+ is the program name); the sequence of previously skipped
+ non-option ARGV-elements is empty. */
+
+ if (optind == 0)
+ {
+ first_nonopt = last_nonopt = optind = 1;
+
+ nextchar = NULL;
+
+ /* Determine how to handle the ordering of options and nonoptions. */
+
+ if (optstring[0] == '-')
+ {
+ ordering = RETURN_IN_ORDER;
+ ++optstring;
+ }
+ else if (optstring[0] == '+')
+ {
+ ordering = REQUIRE_ORDER;
+ ++optstring;
+ }
+ #ifndef _WIN32
+ else if (getenv ("POSIXLY_CORRECT") != NULL)
+ ordering = REQUIRE_ORDER;
+ #endif
+ else
+ ordering = PERMUTE;
+ }
+
+ if (nextchar == NULL || *nextchar == '\0')
+ {
+ if (ordering == PERMUTE)
+ {
+ /* If we have just processed some options following some non-options,
+ exchange them so that the options come first. */
+
+ if (first_nonopt != last_nonopt && last_nonopt != optind)
+ exchange ((char **) argv);
+ else if (last_nonopt != optind)
+ first_nonopt = optind;
+
+ /* Now skip any additional non-options
+ and extend the range of non-options previously skipped. */
+
+ while (optind < argc
+ && (argv[optind][0] != '-' || argv[optind][1] == '\0')
+#ifdef GETOPT_COMPAT
+ && (longopts == NULL
+ || argv[optind][0] != '+' || argv[optind][1] == '\0')
+#endif /* GETOPT_COMPAT */
+ )
+ optind++;
+ last_nonopt = optind;
+ }
+
+ /* Special ARGV-element `--' means premature end of options.
+ Skip it like a null option,
+ then exchange with previous non-options as if it were an option,
+ then skip everything else like a non-option. */
+
+ if (optind != argc && !strcmp (argv[optind], "--"))
+ {
+ optind++;
+
+ if (first_nonopt != last_nonopt && last_nonopt != optind)
+ exchange ((char **) argv);
+ else if (first_nonopt == last_nonopt)
+ first_nonopt = optind;
+ last_nonopt = argc;
+
+ optind = argc;
+ }
+
+ /* If we have done all the ARGV-elements, stop the scan
+ and back over any non-options that we skipped and permuted. */
+
+ if (optind == argc)
+ {
+ /* Set the next-arg-index to point at the non-options
+ that we previously skipped, so the caller will digest them. */
+ if (first_nonopt != last_nonopt)
+ optind = first_nonopt;
+ return EOF;
+ }
+
+ /* If we have come to a non-option and did not permute it,
+ either stop the scan or describe it to the caller and pass it by. */
+
+ if ((argv[optind][0] != '-' || argv[optind][1] == '\0')
+#ifdef GETOPT_COMPAT
+ && (longopts == NULL
+ || argv[optind][0] != '+' || argv[optind][1] == '\0')
+#endif /* GETOPT_COMPAT */
+ )
+ {
+ if (ordering == REQUIRE_ORDER)
+ return EOF;
+ optarg = argv[optind++];
+ return 1;
+ }
+
+ /* We have found another option-ARGV-element.
+ Start decoding its characters. */
+
+ nextchar = (argv[optind] + 1
+ + (longopts != NULL && argv[optind][1] == '-'));
+ }
+
+ if (longopts != NULL
+ && ((argv[optind][0] == '-'
+ && (argv[optind][1] == '-' || long_only))
+#ifdef GETOPT_COMPAT
+ || argv[optind][0] == '+'
+#endif /* GETOPT_COMPAT */
+ ))
+ {
+ const struct option *p;
+ char *s = nextchar;
+ int exact = 0;
+ int ambig = 0;
+ const struct option *pfound = NULL;
+ int indfound = 0;
+
+ while (*s && *s != '=')
+ s++;
+
+ /* Test all options for either exact match or abbreviated matches. */
+ for (p = longopts, option_index = 0; p->name;
+ p++, option_index++)
+ if (!strncmp (p->name, nextchar, s - nextchar))
+ {
+ if (s - nextchar == my_strlen (p->name))
+ {
+ /* Exact match found. */
+ pfound = p;
+ indfound = option_index;
+ exact = 1;
+ break;
+ }
+ else if (pfound == NULL)
+ {
+ /* First nonexact match found. */
+ pfound = p;
+ indfound = option_index;
+ }
+ else
+ /* Second nonexact match found. */
+ ambig = 1;
+ }
+
+ if (ambig && !exact)
+ {
+ if (opterr)
+ fprintf (stderr, "%s: option `%s' is ambiguous\n",
+ argv[0], argv[optind]);
+ nextchar += my_strlen (nextchar);
+ optind++;
+ return BAD_OPTION;
+ }
+
+ if (pfound != NULL)
+ {
+ option_index = indfound;
+ optind++;
+ if (*s)
+ {
+ /* Don't test has_arg with >, because some C compilers don't
+ allow it to be used on enums. */
+ if (pfound->has_arg)
+ optarg = s + 1;
+ else
+ {
+ if (opterr)
+ {
+ if (argv[optind - 1][1] == '-')
+ /* --option */
+ fprintf (stderr,
+ "%s: option `--%s' doesn't allow an argument\n",
+ argv[0], pfound->name);
+ else
+ /* +option or -option */
+ fprintf (stderr,
+ "%s: option `%c%s' doesn't allow an argument\n",
+ argv[0], argv[optind - 1][0], pfound->name);
+ }
+ nextchar += my_strlen (nextchar);
+ return BAD_OPTION;
+ }
+ }
+ else if (pfound->has_arg == 1)
+ {
+ if (optind < argc)
+ optarg = argv[optind++];
+ else
+ {
+ if (opterr)
+ fprintf (stderr, "%s: option `%s' requires an argument\n",
+ argv[0], argv[optind - 1]);
+ nextchar += my_strlen (nextchar);
+ return optstring[0] == ':' ? ':' : BAD_OPTION;
+ }
+ }
+ nextchar += my_strlen (nextchar);
+ if (longind != NULL)
+ *longind = option_index;
+ if (pfound->flag)
+ {
+ *(pfound->flag) = pfound->val;
+ return 0;
+ }
+ return pfound->val;
+ }
+ /* Can't find it as a long option. If this is not getopt_long_only,
+ or the option starts with '--' or is not a valid short
+ option, then it's an error.
+ Otherwise interpret it as a short option. */
+ if (!long_only || argv[optind][1] == '-'
+#ifdef GETOPT_COMPAT
+ || argv[optind][0] == '+'
+#endif /* GETOPT_COMPAT */
+ || my_index (optstring, *nextchar) == NULL)
+ {
+ if (opterr)
+ {
+ if (argv[optind][1] == '-')
+ /* --option */
+ fprintf (stderr, "%s: unrecognized option `--%s'\n",
+ argv[0], nextchar);
+ else
+ /* +option or -option */
+ fprintf (stderr, "%s: unrecognized option `%c%s'\n",
+ argv[0], argv[optind][0], nextchar);
+ }
+ nextchar = (char *) "";
+ optind++;
+ return BAD_OPTION;
+ }
+ }
+
+ /* Look at and handle the next option-character. */
+
+ {
+ char c = *nextchar++;
+ char *temp = my_index (optstring, c);
+
+ /* Increment `optind' when we start to process its last character. */
+ if (*nextchar == '\0')
+ ++optind;
+
+ if (temp == NULL || c == ':')
+ {
+ if (opterr)
+ {
+#if 0
+ if (c < 040 || c >= 0177)
+ fprintf (stderr, "%s: unrecognized option, character code 0%o\n",
+ argv[0], c);
+ else
+ fprintf (stderr, "%s: unrecognized option `-%c'\n", argv[0], c);
+#else
+ /* 1003.2 specifies the format of this message. */
+ fprintf (stderr, "%s: illegal option -- %c\n", argv[0], c);
+#endif
+ }
+ optopt = c;
+ return BAD_OPTION;
+ }
+ if (temp[1] == ':')
+ {
+ if (temp[2] == ':')
+ {
+ /* This is an option that accepts an argument optionally. */
+ if (*nextchar != '\0')
+ {
+ optarg = nextchar;
+ optind++;
+ }
+ else
+ optarg = 0;
+ nextchar = NULL;
+ }
+ else
+ {
+ /* This is an option that requires an argument. */
+ if (*nextchar != '\0')
+ {
+ optarg = nextchar;
+ /* If we end this ARGV-element by taking the rest as an arg,
+ we must advance to the next element now. */
+ optind++;
+ }
+ else if (optind == argc)
+ {
+ if (opterr)
+ {
+#if 0
+ fprintf (stderr, "%s: option `-%c' requires an argument\n",
+ argv[0], c);
+#else
+ /* 1003.2 specifies the format of this message. */
+ fprintf (stderr, "%s: option requires an argument -- %c\n",
+ argv[0], c);
+#endif
+ }
+ optopt = c;
+ if (optstring[0] == ':')
+ c = ':';
+ else
+ c = BAD_OPTION;
+ }
+ else
+ /* We already incremented `optind' once;
+ increment it again when taking next ARGV-elt as argument. */
+ optarg = argv[optind++];
+ nextchar = NULL;
+ }
+ }
+ return c;
+ }
+}
+
+int
+getopt ( int argc,
+ char *const *argv,
+ const char *optstring)
+
+{
+ return _getopt_internal (argc, argv, optstring,
+ (const struct option *) 0,
+ (int *) 0,
+ 0);
+}
+
+int
+getopt_long ( int argc,
+ char *const *argv,
+ const char *options,
+ const struct option *long_options,
+ int *opt_index)
+
+{
+ return _getopt_internal (argc, argv, options, long_options, opt_index, 0);
+}
+
+#endif /* _LIBC or not __GNU_LIBRARY__. */
+
+#ifdef TEST
+
+/* Compile with -DTEST to make an executable for use in testing
+ the above definition of `getopt'. */
+
+int
+main (argc, argv)
+ int argc;
+ char **argv;
+{
+ int c;
+ int digit_optind = 0;
+
+ while (1)
+ {
+ int this_option_optind = optind ? optind : 1;
+
+ c = getopt (argc, argv, "abc:d:0123456789");
+ if (c == EOF)
+ break;
+
+ switch (c)
+ {
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ if (digit_optind != 0 && digit_optind != this_option_optind)
+ printf ("digits occur in two different argv-elements.\n");
+ digit_optind = this_option_optind;
+ printf ("option %c\n", c);
+ break;
+
+ case 'a':
+ printf ("option a\n");
+ break;
+
+ case 'b':
+ printf ("option b\n");
+ break;
+
+ case 'c':
+ printf ("option c with value `%s'\n", optarg);
+ break;
+
+ case BAD_OPTION:
+ break;
+
+ default:
+ printf ("?? getopt returned character code 0%o ??\n", c);
+ }
+ }
+
+ if (optind < argc)
+ {
+ printf ("non-option ARGV-elements: ");
+ while (optind < argc)
+ printf ("%s ", argv[optind++]);
+ printf ("\n");
+ }
+
+ exit (0);
+}
+
+#endif /* TEST */
+
+/* Declarations for getopt.
+ Copyright (C) 1989-1994, 1996-1999, 2001 Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
+
+ The GNU C Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ The GNU C Library 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
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with the GNU C Library; if not, write to the Free
+ Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ 02111-1307 USA. */
+
+#ifndef _GETOPT_H
+
+#ifndef __need_getopt
+# define _GETOPT_H 1
+#endif
+
+/* If __GNU_LIBRARY__ is not already defined, either we are being used
+ standalone, or this is the first header included in the source file.
+ If we are being used with glibc, we need to include <features.h>, but
+ that does not exist if we are standalone. So: if __GNU_LIBRARY__ is
+ not defined, include <ctype.h>, which will pull in <features.h> for us
+ if it's from glibc. (Why ctype.h? It's guaranteed to exist and it
+ doesn't flood the namespace with stuff the way some other headers do.) */
+#if !defined __GNU_LIBRARY__
+# include <ctype.h>
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* For communication from `getopt' to the caller.
+ When `getopt' finds an option that takes an argument,
+ the argument value is returned here.
+ Also, when `ordering' is RETURN_IN_ORDER,
+ each non-option ARGV-element is returned here. */
+
+extern char *optarg;
+
+/* Index in ARGV of the next element to be scanned.
+ This is used for communication to and from the caller
+ and for communication between successive calls to `getopt'.
+
+ On entry to `getopt', zero means this is the first call; initialize.
+
+ When `getopt' returns -1, this is the index of the first of the
+ non-option elements that the caller should itself scan.
+
+ Otherwise, `optind' communicates from one call to the next
+ how much of ARGV has been scanned so far. */
+
+extern int optind;
+
+/* Callers store zero here to inhibit the error message `getopt' prints
+ for unrecognized options. */
+
+extern int opterr;
+
+/* Set to an option character which was unrecognized. */
+
+extern int optopt;
+
+#ifndef __need_getopt
+/* Describe the long-named options requested by the application.
+ The LONG_OPTIONS argument to getopt_long or getopt_long_only is a vector
+ of `struct option' terminated by an element containing a name which is
+ zero.
+
+ The field `has_arg' is:
+ no_argument (or 0) if the option does not take an argument,
+ required_argument (or 1) if the option requires an argument,
+ optional_argument (or 2) if the option takes an optional argument.
+
+ If the field `flag' is not NULL, it points to a variable that is set
+ to the value given in the field `val' when the option is found, but
+ left unchanged if the option is not found.
+
+ To have a long-named option do something other than set an `int' to
+ a compiled-in constant, such as set a value from `optarg', set the
+ option's `flag' field to zero and its `val' field to a nonzero
+ value (the equivalent single-letter option character, if there is
+ one). For long options that have a zero `flag' field, `getopt'
+ returns the contents of the `val' field. */
+
+struct option
+{
+# if (defined __STDC__ && __STDC__) || defined __cplusplus
+ const char *name;
+# else
+ char *name;
+# endif
+ /* has_arg can't be an enum because some compilers complain about
+ type mismatches in all the code that assumes it is an int. */
+ int has_arg;
+ int *flag;
+ int val;
+};
+
+/* Names for the values of the `has_arg' field of `struct option'. */
+
+# define no_argument 0
+# define required_argument 1
+# define optional_argument 2
+#endif /* need getopt */
+
+
+/* Get definitions and prototypes for functions to process the
+ arguments in ARGV (ARGC of them, minus the program name) for
+ options given in OPTS.
+
+ Return the option character from OPTS just read. Return -1 when
+ there are no more options. For unrecognized options, or options
+ missing arguments, `optopt' is set to the option letter, and '?' is
+ returned.
+
+ The OPTS string is a list of characters which are recognized option
+ letters, optionally followed by colons, specifying that that letter
+ takes an argument, to be placed in `optarg'.
+
+ If a letter in OPTS is followed by two colons, its argument is
+ optional. This behavior is specific to the GNU `getopt'.
+
+ The argument `--' causes premature termination of argument
+ scanning, explicitly telling `getopt' that there are no more
+ options.
+
+ If OPTS begins with `--', then non-option arguments are treated as
+ arguments to the option '\0'. This behavior is specific to the GNU
+ `getopt'. */
+
+#if (defined __STDC__ && __STDC__) || defined __cplusplus
+# ifdef __GNU_LIBRARY__
+/* Many other libraries have conflicting prototypes for getopt, with
+ differences in the consts, in stdlib.h. To avoid compilation
+ errors, only prototype getopt for the GNU C library. */
+extern int getopt (int ___argc, char *const *___argv, const char *__shortopts);
+# else /* not __GNU_LIBRARY__ */
+// Solaris compilation fix
+//extern int getopt ();
+# endif /* __GNU_LIBRARY__ */
+
+# ifndef __need_getopt
+extern int getopt_long (int ___argc, char *const *___argv,
+ const char *__shortopts,
+ const struct option *__longopts, int *__longind);
+extern int getopt_long_only (int ___argc, char *const *___argv,
+ const char *__shortopts,
+ const struct option *__longopts, int *__longind);
+
+/* Internal only. Users should not call this directly. */
+extern int _getopt_internal (int ___argc, char *const *___argv,
+ const char *__shortopts,
+ const struct option *__longopts, int *__longind,
+ int __long_only);
+# endif
+#else /* not __STDC__ */
+extern int getopt ();
+# ifndef __need_getopt
+extern int getopt_long ();
+extern int getopt_long_only ();
+
+extern int _getopt_internal ();
+# endif
+#endif /* __STDC__ */
+
+#ifdef __cplusplus
+}
+#endif
+
+/* Make sure we later can get all the definitions and declarations. */
+#undef __need_getopt
+
+#endif /* getopt.h */
+/***************************************************************************
+ help.cpp - description
+ -------------------
+ begin : Die Apr 23 2002
+ copyright : (C) 2002 by André Simon
+ email : andre.simon1@gmx.de
+ ***************************************************************************/
+
+/***************************************************************************
+ * *
+ * 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 of the License, or *
+ * (at your option) any later version. *
+ * *
+ ***************************************************************************/
+
+#include "help.h"
+
+namespace Help
+ {
+
+/** gibt Hilfetext auf Konsole aus */
+
+ void printHelp(const std::string & helpFilePath)
+ {
+ std::ifstream helpFile(helpFilePath.c_str());
+ std::string line;
+ if (helpFile){
+ while (getline(helpFile, line))
+ std::cout << line << "\n";
+ helpFile.close();
+ }
+ else {
+ std::cerr <<"highlight: Could not read "<< helpFilePath << "\n";
+ }
+ }
+
+}
+/***************************************************************************
+ help.h - description
+ -------------------
+ begin : Die Apr 23 2002
+ copyright : (C) 2002 by Andé Simon
+ email : andre.simon1@gmx.de
+ ***************************************************************************/
+
+/***************************************************************************
+ * *
+ * 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 of the License, or *
+ * (at your option) any later version. *
+ * *
+ ***************************************************************************/
+
+#ifndef HELP_H
+#define HELP_H
+
+#include <iostream>
+#include <fstream>
+#include <string>
+
+/**\ brief COntains methods for printing help messages
+ *@author Andre Simon
+ */
+namespace Help
+ {
+ /** print help message to stdout */
+ void printHelp(const std::string &);
+ }
+
+#endif
+/***************************************************************************
+ htmlcode.cpp - description
+ -------------------
+ begin : Wed Nov 28 2001
+ copyright : (C) 2001 by André Simon
+ email : andre.simon1@gmx.de
+ ***************************************************************************/
+
+/***************************************************************************
+ * *
+ * 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 of the License, or *
+ * (at your option) any later version. *
+ * *
+ ***************************************************************************/
+
+#include "htmlgenerator.h"
+
+using namespace std;
+
+namespace highlight {
+
+
+HtmlGenerator::HtmlGenerator(void)
+{}
+
+string HtmlGenerator::formatStyleAttributes(const string & elemName,
+ const ElementStyle & elem)
+{
+ ostringstream s;
+ s << "."<<elemName<<"\t{ color:#"
+ << (elem.getColour().getHexRedValue())
+ << (elem.getColour().getHexGreenValue())
+ << (elem.getColour().getHexBlueValue() )
+ << ( elem.isBold() ?"; font-weight:bold" :"" )
+ << ( elem.isItalic() ?"; font-style:italic" :"" )
+ << ( elem.isUnderline() ?"; text-decoration:underline" :"" )
+ << "; }\n" ;
+ return s.str();
+}
+
+string HtmlGenerator::getOpenTag(const string& styleName ){
+ return "<span class=\""+styleName+"\">";
+}
+
+HtmlGenerator::HtmlGenerator (
+ const string &cssStyle,
+ const string &enc,
+ bool omitEnc,
+ bool withAnchors)
+ : CodeGenerator( cssStyle),
+ brTag("<br>"),
+ hrTag("<hr>"),
+ idAttr("name"),
+ fileSuffix(".html"),
+ encoding(enc),
+ omitEncoding(omitEnc),
+ HTML_FOOTER(
+ "\n</body>\n</html>\n<!--HTML generated by highlight "
+ HIGHLIGHT_VERSION
+ ", "
+ HIGHLIGHT_URL
+ "-->\n"),
+ attachAnchors(withAnchors)
+{
+ styleTagOpen.push_back("");
+ styleTagOpen.push_back(getOpenTag("str"));
+ styleTagOpen.push_back(getOpenTag("num"));
+ styleTagOpen.push_back(getOpenTag("slc"));
+ styleTagOpen.push_back(getOpenTag("com"));
+ styleTagOpen.push_back(getOpenTag("esc"));
+ styleTagOpen.push_back(getOpenTag("dir"));
+ styleTagOpen.push_back(getOpenTag("dstr"));
+ styleTagOpen.push_back(getOpenTag("line"));
+ styleTagOpen.push_back(getOpenTag("sym"));
+
+ styleTagClose.push_back("");
+ for (int i=1;i<NUMBER_BUILTIN_STYLES; i++) {
+ styleTagClose.push_back("</span>");
+ }
+
+ /*assert (styleTagOpen.size()==styleTagClose.size());
+ assert (styleTagOpen.size()==NUMBER_BUILTIN_STYLES);
+*/
+ newLineTag = "\n";
+ spacer = " ";
+ styleCommentOpen="/*";
+ styleCommentClose="*/";
+}
+
+string HtmlGenerator::getStyleDefinition()
+{
+ if (styleDefinitionCache.empty()){
+ ostringstream os;
+ os << "body.hl\t{ background-color:#"
+ << (docStyle.getBgColour().getHexRedValue())
+ << (docStyle.getBgColour().getHexGreenValue())
+ << (docStyle.getBgColour().getHexBlueValue())
+ << "; }\n";
+ os << "pre.hl\t{ color:#"
+ << (docStyle.getDefaultStyle().getColour().getHexRedValue())
+ << (docStyle.getDefaultStyle().getColour().getHexGreenValue())
+ << (docStyle.getDefaultStyle().getColour().getHexBlueValue() )
+ << "; background-color:#"
+ << (docStyle.getBgColour().getHexRedValue())
+ << (docStyle.getBgColour().getHexGreenValue())
+ << (docStyle.getBgColour().getHexBlueValue())
+ << "; font-size:"
+ << docStyle.getFontSize()
+ << "pt; font-family:Courier;}\n";
+ os << formatStyleAttributes("num", docStyle.getNumberStyle())
+ << formatStyleAttributes("esc", docStyle.getEscapeCharStyle())
+ << formatStyleAttributes("str", docStyle.getStringStyle())
+ << formatStyleAttributes("dstr", docStyle.getDirectiveStringStyle())
+ << formatStyleAttributes("slc", docStyle.getSingleLineCommentStyle())
+ << formatStyleAttributes("com", docStyle.getCommentStyle())
+ << formatStyleAttributes("dir", docStyle.getDirectiveStyle())
+ << formatStyleAttributes("sym", docStyle.getSymbolStyle())
+ << formatStyleAttributes("line", docStyle.getLineStyle());
+
+ KeywordStyles styles = docStyle.getKeywordStyles();
+ for (KSIterator it=styles.begin(); it!=styles.end(); it++){
+ os << formatStyleAttributes(it->first, *(it->second));
+ }
+ styleDefinitionCache=os.str();
+ }
+ return styleDefinitionCache;
+}
+
+string HtmlGenerator::getHeader(const string &title)
+{
+ ostringstream os;
+ os << getHeaderStart((title.empty())?"Source file":title );
+ if (langInfo.getSyntaxHighlight())
+ {
+ if (includeStyleDef) //CSS-Definition in HTML-<head> einfuegen
+ {
+ os << "<style type=\"text/css\">\n";
+ os << "<!--\n";
+ os << getStyleDefinition();
+ os << CodeGenerator::readUserStyleDef();
+ os << "//-->\n";
+ os << "</style>" << endl;
+ }
+ else //Referenz auf CSS-Datei einfuegen
+ {
+ os << "<link rel=\"stylesheet\" type=\"text/css\" href=\""
+ << getStyleOutputPath()
+ << "\""
+ << ">\n";
+ }
+ }
+ os << "</head>\n<body class=\"hl\">\n<pre class=\"hl\">";
+ return os.str();
+}
+
+string HtmlGenerator::getFooter()
+{
+ return "</pre>" + HTML_FOOTER;
+}
+
+
+void HtmlGenerator::printBody()
+{
+ processRootState();
+}
+
+
+
+string HtmlGenerator::maskCharacter(unsigned char c)
+{
+ switch (c) {
+ case '<' :
+ return "&lt;";
+ break;
+ case '>' :
+ return "&gt;";
+ break;
+ case '&' :
+ return "&amp;";
+ break;
+ case '\"' :
+ return "&quot;";
+ break;
+
+ case '@' :
+ return "&#64;";
+ break;
+
+ default :
+ string m;
+ return m += c;
+ }
+}
+
+void HtmlGenerator::insertLineNumber (bool insertNewLine)
+{
+ if (insertNewLine){
+ //*out << getNewLine();
+ wsBuffer += getNewLine();
+ }
+ if (showLineNumbers) {
+ ostringstream numberPrefix;
+ if (attachAnchors) {
+ numberPrefix << "<a "
+ << idAttr
+ << "=\"l_"
+ << lineNumber
+ << "\">";
+ }
+ ostringstream os;
+ if (lineNumberFillZeroes) os.fill('0');
+ os <<setw(LINE_NUMBER_WIDTH)<<right<< lineNumber;
+ numberPrefix<< styleTagOpen[LINENUMBER]
+ << os.str()
+ << spacer
+ << styleTagClose[LINENUMBER];
+
+ if (attachAnchors) {
+ numberPrefix << "</a>";
+ }
+
+ wsBuffer += numberPrefix.str();
+ }
+}
+
+string HtmlGenerator::getHeaderStart(const string &title){
+ ostringstream header;
+ header<< "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.0 Transitional//EN\">"
+ << "\n<html>\n<head>\n";
+ if (!omitEncoding){
+ header << "<meta http-equiv=\"content-type\" content=\"text/html; charset="<<encoding<<"\">\n";
+ }
+ header << "<title>" << title <<"</title>\n";
+ return header.str();
+}
+
+bool HtmlGenerator::printIndexFile(const vector<string> &fileList,
+ const string &outPath ){
+ string suffix = fileSuffix;
+ string outFilePath = outPath + "index" + suffix;
+ ofstream indexfile(outFilePath.c_str());
+
+ if (!indexfile.fail()){
+ string inFileName;
+ string inFilePath, newInFilePath;
+ indexfile << getHeaderStart("Source Index" );
+ indexfile << "</head>\n<body>\n<h1> Source Index</h1>\n"
+ << hrTag
+ << "\n<ul>\n";
+ string::size_type pos;
+ for (unsigned int i=0; i < fileList.size(); i++){
+ pos=(fileList[i]).find_last_of(Platform::pathSeparator);
+ if (pos!=string::npos){
+ newInFilePath = (fileList[i]).substr(0, pos+1);
+ } else {
+ newInFilePath=Platform::pathSeparator;
+ }
+ if (newInFilePath!=inFilePath){
+ indexfile << "</ul>\n<h2>";
+ indexfile << newInFilePath;
+ indexfile << "</h2>\n<ul>\n";
+ inFilePath=newInFilePath;
+ }
+ inFileName = (fileList[i]).substr(pos+1);
+ indexfile << "<li><a href=\"" << inFileName << suffix << "\">";
+ indexfile << inFileName << suffix <<"</a></li>\n";
+ }
+
+ indexfile << "</ul>\n"
+ << hrTag << brTag
+ << "<small>Generated by highlight "
+ << HIGHLIGHT_VERSION
+ << ", <a href=\"" << HIGHLIGHT_URL << "\" target=\"new\">"
+ << HIGHLIGHT_URL << "</a></small>";
+ indexfile << HTML_FOOTER;
+ } else {
+ return false;
+ }
+ return true;
+}
+
+string HtmlGenerator::getMatchingOpenTag(unsigned int styleID){
+ return getOpenTag(langInfo.getKeywordClasses()[styleID]);
+ }
+
+string HtmlGenerator::getMatchingCloseTag(unsigned int styleID){
+ return "</span>";
+}
+
+}
+/***************************************************************************
+ htmlgenerator.h - description
+ -------------------
+ begin : Wed Nov 28 2001
+ copyright : (C) 2001 by Andre Simon
+ email : andre.simon1@gmx.de
+ ***************************************************************************/
+
+/***************************************************************************
+ * *
+ * 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 of the License, or *
+ * (at your option) any later version. *
+ * *
+ ***************************************************************************/
+
+
+#ifndef HTMLGENERATOR_H
+#define HTMLGENERATOR_H
+
+#include <fstream>
+#include <iostream>
+#include <string>
+#include <sstream>
+
+#include "codegenerator.h"
+#include "version.h"
+#include "stylecolour.h"
+#include "elementstyle.h"
+#include "platform_fs.h"
+
+namespace highlight {
+
+/**
+ \brief This class generates HTML.
+
+ It contains information about the resulting document structure (document
+ header and footer), the colour system, white space handling and text
+ formatting attributes.
+
+* @author Andre Simon
+*/
+
+class HtmlGenerator : public highlight::CodeGenerator
+ {
+ public:
+
+ /** Constructor
+ \param colourTheme Name of Colour theme to use
+ \param enc encoding name
+ \param omitEnc switch to omit encoding information
+ \param withAnchors Test if HTML anchors should be attached to line numbers
+ */
+ HtmlGenerator(const string &colourTheme,
+ const string &enc,
+ bool omitEnc=false,
+ bool withAnchors = false);
+
+ HtmlGenerator();
+
+ /** Destructor*/
+ virtual ~HtmlGenerator() {};
+
+ /** insert line number in the beginning of the new line
+ */
+ virtual void insertLineNumber(bool insertNewLine=true);
+
+ /** Print document header
+ \param title Title of the document
+ */
+ string getHeader(const string &title);
+
+ /** Print document body*/
+ void printBody();
+
+ /** Print document footer*/
+ string getFooter();
+
+ /** Print style definitions to external file
+ \param outFile Path of external style definition
+ */
+ bool printExternalStyle(const string &outFile);
+
+ /** Print index file with all input file names
+ \param fileList List of output file names
+ \param outPath Output path
+ */
+ bool printIndexFile(const vector<string> & fileList, const string &outPath);
+
+ protected:
+
+ /** some strings which are similar in HTML and XHTML*/
+ string brTag, hrTag, idAttr, fileSuffix;
+
+ /** Output encoding name */
+ string encoding;
+
+ /** switch to omit encoding name in file header */
+ bool omitEncoding;
+
+ /** HTML footer */
+ string HTML_FOOTER;
+
+ /** caches style definition */
+ string styleDefinitionCache;
+
+ /** \return CSS definition */
+ string getStyleDefinition();
+
+ /** \return Content of user defined style file */
+ string readUserStyleDef();
+
+ /** \param title Dociment title
+ \return Start of file header */
+ virtual string getHeaderStart(const string &title);
+
+ private:
+
+ /** \param styleName Style name
+ \return Opening tag of the given style
+ */
+ string getOpenTag(const string& styleName);
+
+ /** \return escaped character*/
+ virtual string maskCharacter(unsigned char );
+
+ /** test if anchors should be appied to line numbers*/
+ bool attachAnchors;
+
+ /**\return text formatting attributes in HTML format */
+ string formatStyleAttributes(const string & elemName, const ElementStyle & elem);
+
+ /** \param styleID Style ID
+ \return Opening tag of the given style
+ */
+ string getMatchingOpenTag(unsigned int styleID);
+
+ /** \param styleID Style ID
+ \return Closing tag of the given style
+ */
+ string getMatchingCloseTag(unsigned int styleID);
+ };
+
+}
+
+#endif
+/***************************************************************************
+ languagedefinition.cpp - description
+ -------------------
+ begin : Wed Nov 28 2001
+ copyright : (C) 2001 by Andre imon
+ email : andre.simon1@gmx.de
+ ***************************************************************************/
+
+/***************************************************************************
+ * *
+ * 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 of the License, or *
+ * (at your option) any later version. *
+ * *
+ ***************************************************************************/
+
+#include "languagedefinition.h"
+
+using namespace std;
+
+namespace highlight {
+
+LanguageDefinition::LanguageDefinition():
+ ignoreCase(false),
+ disableHighlighting(false),
+ allowExtEscape(false),
+ vhdl_mode(false),
+ java_mode(false),
+ allowNestedComments(true),
+ fullLineComment(false),
+ reformatCode(false)
+{}
+
+int LanguageDefinition::isKeyword(const string &s)
+{
+ if (s.length())
+ {
+ if (keywords.count(s)){
+ return keywords[s];
+ }
+ else if (prefixes.count(s[0])){
+ return prefixes[s[0]];
+ }
+ }
+ return 0;
+}
+
+bool LanguageDefinition::isPrefix(unsigned char c)
+{
+ return ( prefixes.count(c));
+}
+
+void LanguageDefinition::addSimpleSymbol(stringstream& symbolStream,
+ State state,
+ const string& paramValues ) {
+ istringstream valueStream(paramValues);
+ bool valExists=false;
+ string value;
+ while (valueStream >> value)
+ {
+ symbolStream << " " << value;
+ valExists = true;
+ }
+ if (valExists)
+ {
+ symbolStream << " " << state;
+ }
+}
+
+void LanguageDefinition::addDelimiterSymbol(stringstream& symbolStream,
+ State stateBegin, State stateEnd,
+ const string& paramValues,
+ unsigned int classID) {
+ istringstream valueStream(paramValues);
+ string delimPrefix, delimSuffix;
+ while (valueStream>>delimPrefix){
+ valueStream >> delimSuffix;
+ symbolStream << " "<<delimPrefix <<" " << stateBegin;
+ symbolStream <<" "<< delimSuffix<<" "<< stateEnd;
+ delimiterPrefixes.insert(make_pair(delimPrefix, classID));
+ };
+}
+
+bool LanguageDefinition::getFlag( string& paramValue){
+ return (StringTools::lowerCase(paramValue)=="true");
+}
+
+unsigned char LanguageDefinition::getSymbol(const string& paramValue){
+ istringstream valueStream(paramValue);
+ unsigned char symbol;
+ valueStream >> symbol;
+ return symbol;
+}
+
+void LanguageDefinition::addKeywords(const string &kwList,
+ int classID){
+ istringstream valueStream(kwList);
+ string keyword;
+ while (valueStream >> keyword){
+ keywords.insert(make_pair(keyword, classID));
+ }
+}
+
+unsigned int LanguageDefinition::generateNewKWClass(const string& newClassName){
+ unsigned int newClassID=0;
+ bool found=false;
+ while (newClassID<keywordClasses.size() && !found){
+ found= (newClassName==keywordClasses[newClassID++]);
+ }
+ if (!found){
+ newClassID++;
+ keywordClasses.push_back(newClassName);
+ }
+ return newClassID;
+}
+
+unsigned int LanguageDefinition::getDelimPrefixClassID(const string& prefix){
+ if (delimiterPrefixes.count(prefix)){
+ return delimiterPrefixes[prefix];
+ }
+ return 0;
+}
+
+bool LanguageDefinition::load(const string& langDefPath, bool clear)
+{
+ if (clear) reset();
+
+ ConfigurationReader langDef(langDefPath);
+ if (langDef.found())
+ {
+ currentPath=langDefPath;
+ disableHighlighting=false;
+ string token;
+ stringstream symbolStrStream;
+
+ //Stringstream zum Einlesen der Token:
+ istringstream valueStream;
+
+ addDelimiterSymbol(symbolStrStream, ML_COMMENT_BEGIN, ML_COMMENT_END,
+ langDef.getParameter("ml_comment"));
+
+ addSimpleSymbol(symbolStrStream, SL_COMMENT,
+ langDef.getParameter("sl_comment"));
+
+ addSimpleSymbol(symbolStrStream, ESC_CHAR,
+ langDef.getParameter("escchar"));
+
+ addSimpleSymbol(symbolStrStream, DIRECTIVE_LINE,
+ langDef.getParameter("directive"));
+
+ addSimpleSymbol(symbolStrStream, DIRECTIVE_LINE_END,
+ langDef.getParameter("directiveend"));
+
+ addSimpleSymbol(symbolStrStream, STRING,
+ langDef.getParameter("stringdelimiters"));
+
+ ignoreCase=getFlag(langDef.getParameter("ignorecase"));
+ allowNestedComments=getFlag(langDef.getParameter("allownestedcomments"));
+ vhdl_mode=getFlag(langDef.getParameter("vhdl_mode"));
+ java_mode=getFlag(langDef.getParameter("java_mode"));
+ disableHighlighting=getFlag(langDef.getParameter("disablehighlighting"));
+ fullLineComment=getFlag(langDef.getParameter("fl_comment"));
+ reformatCode=getFlag(langDef.getParameter("reformatting"));
+ rawStringPrefix=getSymbol(langDef.getParameter("rawstringprefix"));
+ continuationChar=getSymbol(langDef.getParameter("continuationsymbol"));
+ allowExtEscape=getFlag(langDef.getParameter("allowextescape"));
+
+ string paramName, className, classValue;
+ vector<string> paramNames=langDef.getParameterNames();
+ for (unsigned int i=0;i<paramNames.size();i++){
+ paramName=paramNames[i];
+ className=StringTools::getParantheseVal(paramName);
+ classValue=langDef.getParameter(paramName);
+ if (paramName.find("kw_list") != string::npos ){
+ addKeywords(classValue, generateNewKWClass(className));
+ }
+ if (paramName.find("kw_prefix") != string::npos){
+ prefixes.insert(make_pair(classValue[0], generateNewKWClass(className)));
+ }
+ if (paramName.find("kw_delim") != string::npos ){
+ addDelimiterSymbol(symbolStrStream, KEYWORD_BEGIN, KEYWORD_END,
+ classValue, generateNewKWClass(className));
+ }
+ if (paramName.find("tag_delim") != string::npos ){
+ addDelimiterSymbol(symbolStrStream, TAG_BEGIN, TAG_END,
+ classValue, generateNewKWClass(className));
+ }
+ }
+
+ // zuletzt einlesen, um Probleme mit Delimitern, die Zeichen der
+ // Symbolliste enthalten, zu vermeiden
+ addSimpleSymbol(symbolStrStream, SYMBOL, langDef.getParameter("symbols"));
+
+ valueStream.str(langDef.getParameter("allowedchars"));
+ while (valueStream >> token )
+ {
+ allowedChars += token;
+ }
+ symbolString = symbolStrStream.str();
+
+ string fileToInclude=langDef.getParameter("include");
+ if (!fileToInclude.empty()){
+ string::size_type Pos = langDefPath.find_last_of(Platform::pathSeparator);
+ string includeLangDefPath = langDefPath.substr(0, Pos+1) + fileToInclude;
+ load(includeLangDefPath, false);
+ }
+ return true;
+ }
+ else
+ {
+ currentPath.clear();
+ return false;
+ }
+}
+
+void LanguageDefinition::reset()
+{
+ keywords.clear();
+ keywordClasses.clear();
+ delimiterPrefixes.clear();;
+ prefixes.clear();
+ allowedChars.clear();
+ ignoreCase= false;
+ java_mode= vhdl_mode= false;
+ allowNestedComments= reformatCode = false;
+ rawStringPrefix = continuationChar = '\0';
+ disableHighlighting=false;
+ fullLineComment=false;
+}
+
+bool LanguageDefinition::isVHDL()
+{
+ return vhdl_mode;
+}
+
+bool LanguageDefinition::isJava()
+{
+ return java_mode;
+}
+
+bool LanguageDefinition::allowNestedMLComments(){
+ return allowNestedComments;
+}
+
+bool LanguageDefinition::highlightingDisabled(){
+ return disableHighlighting;
+}
+
+bool LanguageDefinition::isFullLineComment(){
+ return fullLineComment;
+}
+
+bool LanguageDefinition::needsReload(const string &langDefPath){
+ return currentPath!=langDefPath;
+}
+
+bool LanguageDefinition::enableReformatting(){
+ return reformatCode;
+}
+
+const KeywordMap& LanguageDefinition::getKeywords() const{
+ return keywords;
+}
+
+string &LanguageDefinition::getSymbolString() {
+ return symbolString;
+}
+
+unsigned char LanguageDefinition::getRawStringPrefix(){
+ return rawStringPrefix;
+}
+
+unsigned char LanguageDefinition::getContinuationChar(){
+ return continuationChar;
+}
+
+string &LanguageDefinition::getAllowedChars() {
+ return allowedChars;
+}
+
+bool LanguageDefinition::getSyntaxHighlight() {
+ return !disableHighlighting;
+}
+
+bool LanguageDefinition::isIgnoreCase() {
+ return ignoreCase;
+}
+
+const vector<string>&LanguageDefinition::getKeywordClasses() const{
+ return keywordClasses;
+}
+
+bool LanguageDefinition::allowExtEscSeq() {
+ return allowExtEscape;
+}
+
+}
+/***************************************************************************
+ languagedefinition.h - description
+ -------------------
+ begin : Wed Nov 28 2001
+ copyright : (C) 2001 by Andre Simon
+ email : andre.simon1@gmx.de
+ ***************************************************************************/
+
+/***************************************************************************
+ * *
+ * 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 of the License, or *
+ * (at your option) any later version. *
+ * *
+ ***************************************************************************/
+
+#ifndef LANGUAGEDEFINITION_H
+#define LANGUAGEDEFINITION_H
+
+#include <string>
+#include <map>
+#include <iostream>
+#include <fstream>
+#include <iterator>
+#include <sstream>
+
+#include "configurationreader.h"
+//#include "stringtools.h"
+#include "platform_fs.h"
+#include "enums.h"
+
+
+namespace highlight {
+
+/** maps keywords and the corresponding class IDs*/
+typedef map <string, int> KeywordMap;
+
+/** maps keyword prefixes and the corresponding class IDs*/
+typedef map <unsigned char, int> PrefixMap;
+
+/**\brief Contains specific data of the programming language being processed.
+
+ The load() method will only read a new language definition if the given
+ file path is not equal to the path of the current language definition.
+
+* @author Andre Simon
+*/
+
+class LanguageDefinition {
+
+ public:
+
+ LanguageDefinition();
+
+ /**\return Symbol string, containg all known symbols with the referencing state ids*/
+ string &getSymbolString();
+
+ /** \return Prefix of raw strings */
+ unsigned char getRawStringPrefix();
+
+ /** \return Continuation Character */
+ unsigned char getContinuationChar();
+
+ /** \return List of characters allowed within identifiers */
+ string &getAllowedChars();
+
+ /** \return true if syntax highlighting is enabled*/
+ bool getSyntaxHighlight();
+
+ /** \return True if language is case sensitive */
+ bool isIgnoreCase();
+
+ /** \param s String
+ \return class id of keyword, 0 if s is not a keyword */
+ int isKeyword(const string &s);
+
+
+ /** \return true if c is member of prefix list*/
+ bool isPrefix(unsigned char c);
+
+ /** Load new language definition
+ \param langDefPath Path of language definition
+ \param clear Test if former data should be deleted
+ \return True if successfull */
+ bool load(const string& langDefPath, bool clear=true);
+
+ /** \return True if programming language is VHDL */
+ bool isVHDL();
+
+ /** \return True if programming language is Java */
+ bool isJava();
+
+ /** \return True if multi line comments may be nested */
+ bool allowNestedMLComments();
+
+ /** \return True if highlighting is disabled */
+ bool highlightingDisabled();
+
+ /** \return True if single line comments must start at coloumn 1 */
+ bool isFullLineComment();
+
+ /** \return True the next load() call will load a new language definition
+ \param langDefPath Path to language definition */
+ bool needsReload(const string &langDefPath);
+
+ /** \return True if current language may be reformatted (c, c++, c#, java) */
+ bool enableReformatting();
+
+ /** \return True if escape sequences are allowed outsde of strings */
+ bool allowExtEscSeq();
+
+ /** \return Class ID of given keyword delimiter prefix
+ \param prefix Keyword delimiter prefix */
+ unsigned int getDelimPrefixClassID(const string& prefix);
+
+ /** \return keywords*/
+ const KeywordMap& getKeywords() const;
+
+ /** \return keyword classes*/
+ const vector<string>& getKeywordClasses() const;
+
+ private:
+ // string containing symbols and their IDs of the programming language
+ string symbolString;
+
+ // string with special characters that may occour in keywords
+ string allowedChars;
+
+ // path to laoed language definition
+ string currentPath;
+
+ KeywordMap keywords;
+
+ vector <string> keywordClasses;
+
+ KeywordMap delimiterPrefixes;
+
+ PrefixMap prefixes;
+
+ // keywords are not case sensitive if set
+ bool ignoreCase,
+ disableHighlighting,
+ allowExtEscape,
+
+ // switch to enable VHDL workarounds
+ vhdl_mode,
+
+ // switch to enable Java workarounds
+ java_mode,
+
+ // allow nested multi line comment blocks
+ allowNestedComments,
+
+ // single line comments have to start in coloumn 1 if set
+ fullLineComment,
+
+ // code formatting is enabled if set
+ reformatCode;
+
+ // Character, die eine Variable bzw. ein Keyword kennzeichnen
+ unsigned char rawStringPrefix,
+ continuationChar;
+
+ /** setzt Membervariablen auf Defaultwerte */
+ void reset();
+
+ // add a symbol sequencs to the symbolStream
+ void addSimpleSymbol(stringstream& symbolStream, State state,
+ const string& paramValues );
+
+ // add a delimiter symbol sequencs to the symbolStream
+ void addDelimiterSymbol(stringstream& symbolStream,
+ State stateBegin, State stateEnd,
+ const string& paramValues,
+ unsigned int classID=0);
+
+ bool getFlag( string& paramValue);
+
+ unsigned char getSymbol(const string& paramValue);
+
+ // generate a unique class ID if the class name
+ unsigned int generateNewKWClass(const string& newClassName);
+
+ // add keywords to the given class
+ void addKeywords(const string &kwList, int classID);
+
+ };
+
+}
+#endif
+/***************************************************************************
+ LatexCode.cpp - description
+ -------------------
+ begin : Mit Jul 24 2002
+ copyright : (C) 2002 by André Simon
+ email : andre.simon1@gmx.de
+ ***************************************************************************/
+
+/***************************************************************************
+ * *
+ * 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 of the License, or *
+ * (at your option) any later version. *
+ * *
+ ***************************************************************************/
+
+#include "latexgenerator.h"
+
+namespace highlight {
+
+LatexGenerator::LatexGenerator(const string &colourTheme,
+ bool replQuotes)
+ : CodeGenerator(colourTheme),
+ replaceQuotes(replQuotes)
+{
+ styleTagOpen.push_back( "\\hlstd{");
+ styleTagOpen.push_back( "\\hlstr{");
+ styleTagOpen.push_back( "\\hlnum{");
+ styleTagOpen.push_back( "\\hlslc{");
+ styleTagOpen.push_back( "\\hlcom{");
+ styleTagOpen.push_back( "\\hlesc{");
+ styleTagOpen.push_back( "\\hldir{");
+ styleTagOpen.push_back( "\\hldstr{");
+ styleTagOpen.push_back( "\\hlline{");
+ styleTagOpen.push_back( "\\hlsym{");
+
+ for (int i=0;i<NUMBER_BUILTIN_STYLES; i++){
+ styleTagClose.push_back( "}");
+ }
+
+ // avoid "Underfull \hbox (badness 10000)" warnings
+ newLineTag = "\\\\\n";
+ longLineTag = "\\hspace*{\\fill}" + newLineTag;
+
+ spacer = "\\ ";
+
+ maskWs=true;
+ maskWsBegin = "\\hlstd{";
+ maskWsEnd = "}";
+
+ excludeWs=true;
+
+ styleCommentOpen="%";
+}
+
+LatexGenerator::LatexGenerator()
+{}
+LatexGenerator::~LatexGenerator()
+{}
+
+string LatexGenerator::formatStyleAttributes(const string & elemName,
+ const ElementStyle &elem)
+{
+ ostringstream s;
+ s << "\\newcommand{\\hl"
+ << elemName
+ << "}[1]{\\textcolor[rgb]{"
+ << elem.getColour().getLatexRedValue() << ","
+ << elem.getColour().getLatexGreenValue() << ","
+ << elem.getColour().getLatexBlueValue()
+ << "}{";
+
+ if (elem.isBold())
+ s << "\\bf{";
+ if (elem.isItalic())
+ s << "\\it{";
+
+ s <<"#1";
+
+ if (elem.isBold())
+ s << "}";
+ if (elem.isItalic())
+ s << "}";
+
+ s <<"}}\n";
+ return s.str();
+}
+
+void LatexGenerator::printBody()
+{
+ *out << "\\noindent\n"
+ << "\\ttfamily\n";
+
+ processRootState();
+
+ *out << "\\mbox{}\n"
+ << "\n\\normalfont\n";
+}
+
+string LatexGenerator::getHeader(const string & title)
+{
+ ostringstream os;
+ os << "\\documentclass{article}\n"
+ << "\\usepackage{color}\n"
+ << "\\usepackage{alltt}\n";
+
+ if (langInfo.getSyntaxHighlight()) {
+ if (includeStyleDef) {
+ os << "\n"<<getStyleDefinition();
+ os << CodeGenerator::readUserStyleDef();
+ } else {
+ os << "\n\\input {"
+ << getStyleOutputPath()
+ << "}\n";
+ }
+ }
+
+ os << "\n\\title{" << title << "}\n"
+ << "\\begin{document}\n"
+ << "\\pagecolor{bgcolor}\n";
+ return os.str();
+}
+
+string LatexGenerator::getFooter()
+{
+ ostringstream os;
+ os << "\\end {document}\n"
+ << "(* LaTeX generated by highlight "
+ << HIGHLIGHT_VERSION
+ << ", "
+ << HIGHLIGHT_URL
+ << " *)\n";
+ return os.str();
+}
+
+string LatexGenerator::getNewLine(){
+ return (showLineNumbers)? newLineTag:longLineTag;
+}
+
+string LatexGenerator::maskCharacter(unsigned char c)
+{
+ switch (c)
+ {
+ case '<' :
+ return "$<$";
+ break;
+ case '>' :
+ return "$>$";
+ break;
+ case '{':
+ case '}':
+ case '&':
+ case '$':
+ case '#':
+ case '%':
+ {
+ string m;
+ m ="\\";
+ m += c;
+ return m;
+ }
+ break;
+ case '\"':
+ return (fragmentOutput && replaceQuotes)?"\\dq{}":"\"";
+ break;
+ case '_':
+ return "\\textunderscore ";
+ break;
+ case '^':
+ return "\\textasciicircum ";
+ break;
+ case '\\':
+ return "$\\backslash$";
+ break;
+ case '~':
+ return "$\\sim$";
+ break;
+ case '|':
+ return "\\textbar ";
+ break;
+ // avoid latex compilation failure if [ or * follows a line break (\\)
+ case '*':
+ case '[':
+ case ']':
+ // avoid "merging" of consecutive '-' chars when included in bold font ( \bf )
+ case '-':
+ {
+ string m;
+ m= "{";
+ m+= c;
+ m+= "}";
+ return m;
+ }
+ break;
+ case ' ':
+ return spacer;
+ break;
+ case AUML_LC:
+ return "\\\"a";
+ break;
+ case OUML_LC:
+ return "\\\"o";
+ break;
+ case UUML_LC:
+ return "\\\"u";
+ break;
+ case AUML_UC:
+ return "\\\"A";
+ break;
+ case OUML_UC:
+ return "\\\"O";
+ break;
+ case UUML_UC:
+ return "\\\"U";
+ break;
+ case AACUTE_LC:
+ return "\\'a";
+ break;
+ case EACUTE_LC:
+ return "\\'e";
+ break;
+ case OACUTE_LC:
+ return "\\'o";
+ break;
+ case UACUTE_LC:
+ return "\\'u";
+ break;
+ case AGRAVE_LC:
+ return "\\`a";
+ break;
+ case EGRAVE_LC:
+ return "\\`e";
+ break;
+ case OGRAVE_LC:
+ return "\\`o";
+ break;
+ case UGRAVE_LC:
+ return "\\`u";
+ break;
+ case AACUTE_UC:
+ return "\\'A";
+ break;
+ case EACUTE_UC:
+ return "\\'E";
+ break;
+ case OACUTE_UC:
+ return "\\'O";
+ break;
+ case UACUTE_UC:
+ return "\\'U";
+ break;
+ case AGRAVE_UC:
+ return "\\`A";
+ break;
+ case EGRAVE_UC:
+ return "\\`E";
+ break;
+ case UGRAVE_UC:
+ return "\\`O";
+ break;
+ case OGRAVE_UC:
+ return "\\`U";
+ break;
+ case SZLIG:
+ return "\\ss ";
+ break;
+ /* #ifndef _WIN32
+ // skip first byte of multibyte chracters
+ case 195:
+ return string("");
+ break;
+#endif*/
+
+ default :
+ {
+ string m;
+ return m+=c;
+ }
+ }
+}
+
+string LatexGenerator::getMatchingOpenTag(unsigned int styleID){
+ return "\\hl"+langInfo.getKeywordClasses()[styleID]+"{";
+ }
+
+string LatexGenerator::getMatchingCloseTag(unsigned int styleID){
+ return "}";
+}
+
+
+string LatexGenerator::getStyleDefinition()
+{
+ if (styleDefinitionCache.empty()){
+ ostringstream os;
+ os << formatStyleAttributes("std", docStyle.getDefaultStyle());
+ os << formatStyleAttributes("num", docStyle.getNumberStyle());
+ os << formatStyleAttributes("esc", docStyle.getEscapeCharStyle());
+ os << formatStyleAttributes("str", docStyle.getStringStyle());
+ os << formatStyleAttributes("dstr", docStyle.getDirectiveStringStyle());
+ os << formatStyleAttributes("slc", docStyle.getSingleLineCommentStyle());
+ os << formatStyleAttributes("com", docStyle.getCommentStyle());
+ os << formatStyleAttributes("dir", docStyle.getDirectiveStyle());
+ os << formatStyleAttributes("sym", docStyle.getSymbolStyle());
+ os << formatStyleAttributes("line", docStyle.getLineStyle());
+
+ KeywordStyles styles = docStyle.getKeywordStyles();
+ for (KSIterator it=styles.begin(); it!=styles.end(); it++){
+ os << formatStyleAttributes(it->first, *(it->second));
+ }
+ os << "\\definecolor{bgcolor}{rgb}{"
+ << docStyle.getBgColour().getLatexRedValue() << ","
+ << docStyle.getBgColour().getLatexGreenValue() << ","
+ << docStyle.getBgColour().getLatexBlueValue()
+ << "}\n";
+ os << "\\oddsidemargin -3mm\n\\textwidth 165,2truemm\n"
+ << "\\topmargin 0truept\n\\headheight 0truept\n"
+ << "\\headsep 0truept\n\\textheight 230truemm\n";
+
+ styleDefinitionCache=os.str();
+ }
+ return styleDefinitionCache;
+}
+
+
+}
+/***************************************************************************
+ latexgenerator.h - description
+ -------------------
+ begin : Mit Jul 24 2002
+ copyright : (C) 2002 by André Simon
+ email : andre.simon1@gmx.de
+ ***************************************************************************/
+
+/***************************************************************************
+ * *
+ * 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 of the License, or *
+ * (at your option) any later version. *
+ * *
+ ***************************************************************************/
+
+#ifndef LATEXGENERATOR_H
+#define LATEXGENERATOR_H
+
+#include <string>
+#include <iostream>
+#include <sstream>
+
+#include "codegenerator.h"
+#include "version.h"
+#include "charcodes.h"
+
+
+namespace highlight {
+
+/**
+ \brief This class generates LaTeX.
+
+ It contains information about the resulting document structure (document
+ header and footer), the colour system, white space handling and text
+ formatting attributes.
+
+* @author Andre Simon
+*/
+
+class LatexGenerator : public highlight::CodeGenerator
+ {
+ public:
+
+ /** Constructor
+ \param colourTheme Name of Colour theme to use
+ \param replQuotes Test if quotes shold be replaced by \ dq
+ */
+ LatexGenerator(const string &colourTheme,
+ bool replQuotes=false);
+ LatexGenerator();
+ ~LatexGenerator();
+
+ /** prints document header
+ \param title Title of the document
+ */
+ string getHeader(const string & title);
+
+ /** Prints document footer*/
+ string getFooter();
+
+ /** Prints document body*/
+ void printBody();
+
+ private:
+
+ string styleDefinitionCache;
+ string longLineTag;
+
+ /** \return escaped character*/
+ virtual string maskCharacter(unsigned char );
+
+ /**\return text formatting attributes in LaTeX format */
+ string formatStyleAttributes(const string & elemName,
+ const ElementStyle & elem);
+
+ /** test if double quotes should be replaced by \dq{} */
+ bool replaceQuotes;
+
+ string getNewLine();
+
+ string getStyleDefinition();
+
+ string getMatchingOpenTag(unsigned int styleID);
+ string getMatchingCloseTag(unsigned int styleID);
+ };
+
+}
+
+#endif
+/***************************************************************************
+ main.cpp - description
+ -------------------
+ begin : Die Apr 23 22:16:35 CEST 2002
+ copyright : (C) 2002-2004 by André Simon
+ email : andre.simon1@gmx.de
+
+
+ Highlight is a universal source code to HTML converter. Syntax highlighting
+ is formatted by Cascading Style Sheets. It's possible to easily enhance
+ highlight's parsing database.
+
+ ***************************************************************************/
+
+/***************************************************************************
+ * *
+ * 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 of the License, or *
+ * (at your option) any later version. *
+ * *
+ ***************************************************************************/
+
+#include "main.h"
+
+using namespace std;
+
+void HighlightApp::printVersionInfo()
+{
+ cout << "\n highlight version "
+ << HIGHLIGHT_VERSION
+ << "\n Copyright (C) 2002-2005 Andre Simon <andre.simon1@gmx.de>"
+ << "\n\n Artistic Style Classes (1.15.3)"
+ << "\n Copyright (C) 1998-2002 Tal Davidson <davidsont@bigfoot.com>"
+ << "\n\n Dirstream Classes (0.4)"
+ << "\n Copyright (C) 2002-2004 Benjamin Kaufmann <hume@c-plusplus.de>"
+ << "\n\n This software is released under the terms of the GNU General "
+ << "Public License."
+ << "\n For more information about these matters, see the file named "
+ << "COPYING.\n\n";
+ #ifdef USE_LOCAL_GETOPT
+ cout << " (Built with USE_LOCAL_GETOPT flag set.)\n";
+ #endif
+ #ifdef HL_DATA_DIR
+ cout << " (HL_DATA_DIR: \"" <<HL_DATA_DIR<< "\" )\n";
+ #endif
+}
+
+void HighlightApp::printBadInstallationInfo()
+{
+ cerr << "highlight: Data directory not found. Bad installation or wrong "
+ << OPT_DATADIR << " parameter."
+ << "\n\nCopy the highlight files into one of the directories listed "
+ << "in INSTALL.\nYou may also set the data directory with "
+ << OPT_DATADIR << " and " << OPT_ADDDATADIR << ".\n";
+}
+
+bool HighlightApp::listInstalledFiles(bool showThemes)
+{
+ vector <string> filePaths;
+ string wildcard=(showThemes)? "*.style":"*.lang";
+ unsigned int suffixLength=wildcard.length()-1;
+
+ string searchDir = ((showThemes) ? dataDir.getThemeDir():
+ dataDir.getLangDefDir()) + wildcard;
+
+ bool directoryOK = Platform::getDirectoryEntries(filePaths, searchDir, true);
+ if (!directoryOK) {
+ cerr << "highlight: Could not access directory "
+ << searchDir
+ << ", aborted.\n";
+ return false;
+ }
+
+ cout << "\n Installed "
+ << ((showThemes)? "themes":"language definitions ")
+ << "(located in "
+ << ((showThemes)?dataDir.getThemeDir():dataDir.getLangDefDir())
+ << ") :\n"
+ << endl;
+
+ sort(filePaths.begin(), filePaths.end());
+ string temp;
+
+ for (unsigned int i=0;i< filePaths.size(); i++){
+ if (showThemes)
+ temp = (filePaths[i]).substr(dataDir.getThemeDir().length());
+ else
+ temp = (filePaths[i]).substr(dataDir.getLangDefDir().length());
+ cout << " "<<temp.substr(0, temp.length()- suffixLength) << endl;
+ }
+ cout <<"\n Use name of the desired "
+ << ((showThemes)?"theme":"language")
+ << " with the --"
+ << ((showThemes)? OPT_STYLE : OPT_SYNTAX)
+ << " option.\n" << endl;
+ return true;
+}
+
+void HighlightApp::printDebugInfo(highlight::LanguageDefinition &lang,
+ const string & langDefPath)
+{
+ cerr << "\nLoading language definition: " << langDefPath;
+ cerr << "\n\nSYMBOLS: "
+ << lang.getSymbolString();
+ cerr << "\n\nKEYWORDS: ";
+ highlight::KeywordMap::iterator it;
+ highlight::KeywordMap keys=lang.getKeywords();
+ cerr << "\n\nID Keyword \n";
+ for (it=keys.begin(); it!=keys.end();it++){
+ cerr << it->second
+ << " <- \""
+ << it->first <<"\"\n";
+ }
+ cerr <<"\n";
+}
+
+string HighlightApp::getFileSuffix(const string &fileName) {
+ size_t ptPos=fileName.rfind(".");
+ return (ptPos == string::npos) ?
+ "" : fileName.substr(ptPos+1, fileName.length());
+}
+
+bool HighlightApp::loadMapConfig(const string& name, StringMap* map){
+ string extPath=dataDir.getDir() + name + ".conf";
+ ConfigurationReader config(extPath);
+ if (config.found() )
+ {
+ stringstream values;
+ string paramName, paramVal;
+ for (unsigned int i=0;i<config.getParameterNames().size();i++){
+ paramName = config.getParameterNames()[i];
+ values.str(config.getParameter(paramName)) ;
+ while (values >> paramVal) {
+ map->insert(make_pair( paramVal, paramName));
+ }
+ values.clear();
+ }
+ return true;
+ } else {
+ cerr << "highlight: Configuration file "<< extPath << " not found.\n";
+ return false;
+ }
+}
+
+
+int HighlightApp::getNumDigits(int i){
+ int res=0;
+ while (i){
+ i/=10;
+ ++res;
+ }
+ return res;
+}
+
+void HighlightApp::printProgressBar(int total, int count){
+ if (!total) return;
+ int p=100*count / total;
+ int numProgressItems=p/10;
+ cout << "\r[";
+ for (int i=0;i<10;i++){
+ cout <<((i<numProgressItems)?"#":" ");
+ }
+ cout<< "] " <<setw(3)<<p<<"%, "<<count << " / " << total << " " <<flush;
+ if (p==100) {
+ cout << endl;
+ }
+}
+
+void HighlightApp::printCurrentAction(const string&outfilePath,
+ int total, int count, int countWidth){
+ cout << "Writing file "
+ << setw(countWidth)<< count
+ << " of "
+ << total
+ << ": "
+ << outfilePath
+ << "\n";
+}
+
+void HighlightApp::printIOErrorReport(unsigned int numberErrorFiles,
+ vector<string> & fileList,
+ const string &action){
+ cerr << "highlight: Could not "
+ << action
+ << " file"
+ << ((numberErrorFiles>1)?"s":"")<<":\n";
+ copy (fileList.begin(), fileList.end(), ostream_iterator<string>(cerr, "\n"));
+ if (fileList.size() < numberErrorFiles) {
+ cerr << "... ["
+ << (numberErrorFiles - fileList.size() )
+ << " of "
+ << numberErrorFiles
+ << " failures not shown, use --"
+ << OPT_VERBOSE
+ << " switch to print all paths]\n";
+ }
+}
+
+string HighlightApp::analyzeShebang(const string& file){
+ if (scriptShebangs.empty()) loadMapConfig("scriptre", &scriptShebangs);
+ ifstream inFile(file.c_str());
+ string firstLine;
+ getline (inFile, firstLine);
+ return scriptShebangs[StringTools::trimRight(firstLine)];
+}
+
+string HighlightApp::guessFileType(const string& suffix, const string &inputFile)
+{
+ if (extensions.empty()) loadMapConfig("extensions", &extensions);
+ string fileType = (extensions.count(suffix)) ? extensions[suffix] : suffix ;
+ if (!fileType.empty()) return fileType;
+ return analyzeShebang(inputFile);
+}
+
+
+int HighlightApp::run(int argc, char**argv){
+
+ //get command line options
+ CmdLineOptions options(argc, argv);
+
+ // set data directory path, where /langDefs and /themes reside
+ string highlightRootDir = Platform::getAppPath();
+
+ // determine highlight data directory
+ if (! dataDir.searchDataDir((options.dataDirGiven())?
+ options.getDataDir(): highlightRootDir)){
+ printBadInstallationInfo();
+ return EXIT_FAILURE;
+ }
+
+ if (options.additionalDataDirGiven()){
+ dataDir.setAdditionalDataDir(options.getAdditionalDataDir());
+ }
+
+ if (options.printVersion()) {
+ printVersionInfo();
+ return EXIT_SUCCESS;
+ }
+
+ if (options.printHelp()) {
+ Help::printHelp(dataDir.getHelpMsgDir() + options.getHelpLang());
+ return EXIT_SUCCESS;
+ }
+
+ if (options.showThemes() || options.showLangdefs()) {
+ return listInstalledFiles(options.showThemes())?EXIT_SUCCESS:EXIT_FAILURE;
+ }
+
+ // list of input files
+ const vector <string> inFileList=options.getInputFileNames();
+
+ string stylePath=dataDir.searchForTheme(options.getStyleName());
+
+ highlight::CodeGenerator *generator =
+ highlight::CodeGenerator::getInstance(options.getOutputType(),
+ stylePath,
+ options.getStyleInFilename(),
+ options.getStyleOutFilename(),
+ options.getCharSet(),
+ options.includeStyleDef(),
+ options.attachLineAnchors(),
+ options.replaceQuotes(),
+ options.fopCompatible(),
+ options.getNumberSpaces(),
+ options.getWrappingStyle(),
+ options.printLineNumbers(),
+ options.fillLineNrZeroes(),
+ options.fragmentOutput(),
+ options.omitEncodingName() );
+
+ assert (generator!=NULL);
+
+ bool styleFileWanted = !options.fragmentOutput() || options.styleOutPathDefined();
+
+ if (!generator->styleFound() ) {
+ cerr << "highlight: Could not find style "
+ << stylePath
+ << ".\n";
+ highlight::CodeGenerator::deleteInstance();
+ return EXIT_FAILURE;
+ }
+
+ if (!options.getIndentScheme().empty()){
+ string indentSchemePath =
+ dataDir.searchForIndentScheme(options.getIndentScheme()+".indent");
+ if (!generator->initIndentationScheme(indentSchemePath)){
+ cerr << "highlight: Could not find indentation scheme "
+ << indentSchemePath
+ << ".\n";
+ highlight::CodeGenerator::deleteInstance();
+ return EXIT_FAILURE;
+ }
+ }
+
+ string outDirectory = options.getOutDirectory();
+ if (!outDirectory.empty() && !options.quietMode() && !dirstr::directory_exists(outDirectory) ){
+ cerr << "highlight: Output directory \""
+ << outDirectory
+ << "\" does not exist.\n";
+ return EXIT_FAILURE;
+ }
+
+ bool initError=false, IOError=false;
+
+ if ( !options.includeStyleDef()
+ && (styleFileWanted)
+ && options.formatSupportsExtStyle()) {
+ string cssOutFile=outDirectory + options.getStyleOutFilename();
+ bool success=generator->printExternalStyle (cssOutFile);
+ if (!success){
+ cerr << "highlight: Could not write " << cssOutFile <<".\n";
+ IOError = true;
+ }
+ }
+
+ if (options.printIndexFile()){
+ bool success=generator -> printIndexFile(inFileList, outDirectory);
+ if (!success){
+ cerr << "highlight: Could not write index file.\n";
+ IOError = true;
+ }
+ }
+
+ unsigned int fileCount=inFileList.size(),
+ fileCountWidth=getNumDigits(fileCount),
+ i=0,
+ numBadFormatting=0,
+ numBadInput=0,
+ numBadOutput=0;
+
+ vector<string> badFormattedFiles, badInputFiles, badOutputFiles;
+ string outFilePath;
+ string suffix, lastSuffix;
+
+ if (options.syntaxGiven()) { // user defined language definition, valid for all files
+ suffix = guessFileType(options.getLanguage());
+ }
+
+ while (i < fileCount && !initError) {
+ if (!options.syntaxGiven()) { // determine file type for each file
+ suffix = guessFileType(getFileSuffix(inFileList[i]), inFileList[i]);
+ }
+ if (suffix.empty()) {
+ if (!options.enableBatchMode() && !styleFileWanted)
+ cerr << "highlight: Undefined language definition. Use --"
+ << OPT_SYNTAX << " option.\n";
+ if (!options.forceOutput()){
+ initError = true;
+ break;
+ }
+ }
+
+ if (suffix != lastSuffix) {
+ string langDefPath=dataDir.searchForLangDef(suffix+".lang");
+ highlight::LoadResult loadRes= generator->initLanguage(langDefPath);
+ if (loadRes==highlight::LOAD_FAILED){
+ cerr << "highlight: Unknown source file extension \""
+ << suffix
+ << "\".\n";
+ if (!options.forceOutput()){
+ initError = true;
+ break;
+ }
+ }
+ if (options.printDebugInfo() && loadRes==highlight::LOAD_NEW){
+ printDebugInfo(generator->getLanguage(), langDefPath);
+ }
+ lastSuffix = suffix;
+ }
+
+ if (options.enableBatchMode()){
+ string::size_type pos=(inFileList[i]).find_last_of(Platform::pathSeparator);
+ outFilePath = outDirectory;
+ outFilePath += inFileList[i].substr(pos+1);
+ outFilePath += options.getOutFileSuffix();
+
+ if (!options.quietMode()) {
+ if (options.printProgress()){
+ printProgressBar(fileCount, i+1);
+ } else {
+ printCurrentAction(outFilePath, fileCount, i+1, fileCountWidth);
+ }
+ }
+ } else {
+ outFilePath = options.getSingleOutFilename();
+ }
+
+ highlight::ParseError error = generator->printOutput(inFileList[i], outFilePath);
+ if (error==highlight::BAD_INPUT){
+ if (numBadInput++ < IO_ERROR_REPORT_LENGTH || options.printDebugInfo()) {
+ badInputFiles.push_back(inFileList[i]);
+ }
+ } else if (error==highlight::BAD_OUTPUT){
+ if (numBadOutput++ < IO_ERROR_REPORT_LENGTH || options.printDebugInfo()) {
+ badOutputFiles.push_back(outFilePath);
+ }
+ }
+ if (options.formattingEnabled() && !generator->formattingIsPossible()){
+ if (numBadFormatting++ < IO_ERROR_REPORT_LENGTH || options.printDebugInfo()) {
+ badFormattedFiles.push_back(outFilePath);
+ }
+ }
+ ++i;
+ }
+
+ if (numBadInput){
+ printIOErrorReport(numBadInput, badInputFiles, "read input");
+ IOError = true;
+ }
+ if (numBadOutput){
+ printIOErrorReport(numBadOutput, badOutputFiles, "write output");
+ IOError = true;
+ }
+ if (numBadFormatting){
+ printIOErrorReport(numBadFormatting, badFormattedFiles, "reformat");
+ }
+
+ highlight::CodeGenerator::deleteInstance();
+ return (initError || IOError) ? EXIT_FAILURE : EXIT_SUCCESS;
+}
+
+
+int main(int argc, char **argv) {
+ HighlightApp app;
+ return app.run(argc, argv);
+}
+//
+// C++ Interface: main
+//
+// Description:
+//
+//
+// Author: Andre Simon <andre.simon1@gmx.de>, (C) 2004
+//
+// Copyright: See COPYING file that comes with this distribution
+//
+//
+
+#ifndef HIGHLIGHT_APP
+#define HIGHLIGHT_APP
+
+
+#include <iostream>
+#include <fstream>
+#include <string>
+#include <vector>
+#include <map>
+#include <iomanip>
+#include <cassert>
+
+#include "./dirstream0.4/dirstream.h"
+#include "cmdlineoptions.h"
+#include "configurationreader.h"
+#include "codegenerator.h"
+#include "help.h"
+#include "datadir.h"
+#include "version.h"
+#include "platform_fs.h"
+
+#define IO_ERROR_REPORT_LENGTH 5
+#define SHEBANG_CNT 12
+
+typedef map<string, string> StringMap;
+
+/** Main application class
+ @author Andre Simon
+*/
+
+class HighlightApp {
+
+public:
+
+ HighlightApp(){};
+ ~HighlightApp(){};
+
+ /** Start application
+ \param argc Number of command line arguments
+ \param argv values of command line arguments
+ \return EXIT_SUCCESS or EXIT_FAILURE
+ */
+ int run(int argc, char **argv);
+
+private:
+
+ DataDir dataDir;
+ StringMap extensions;
+ StringMap scriptShebangs;
+
+ /** print version info*/
+ void printVersionInfo();
+
+ /** print error message*/
+ void printBadInstallationInfo();
+
+ /** print input and output errors */
+ void printIOErrorReport(unsigned int numberErrorFiles, vector<string> & fileList, const string &action);
+
+ /** print installed files
+ \param showThemes Print installed themes if true, language definitions otherwise
+ */
+ bool listInstalledFiles(bool showThemes);
+
+ void printDebugInfo(highlight::LanguageDefinition &lang,
+ const string &langDefPath);
+
+ string getFileSuffix(const string &fileName);
+
+ string guessFileType(const string &suffix, const string &inputFile="");
+
+ int getNumDigits(int i);
+
+ void printProgressBar(int total, int count);
+ void printCurrentAction(const string&outfilePath,
+ int total, int count, int countWidth);
+
+ bool readInputFilePaths(vector<string> &fileList, string wildcard,
+ bool recursiveSearch);
+
+ string analyzeShebang(const string& file);
+ bool loadMapConfig(const string& name, StringMap* map);
+
+};
+
+#endif
+//
+// C++ Implementation: platform_fs
+//
+// Description:
+//
+//
+// Author: André Simon <andre.simon1@gmx.de>, (C) 2004
+//
+// Copyright: See COPYING file that comes with this distribution
+//
+//
+
+#include "platform_fs.h"
+#include "./dirstream0.4/dirstream.h"
+
+#include <iostream>
+
+using namespace std;
+
+namespace Platform {
+
+#ifdef _WIN32
+ #include <windows.h>
+
+ const char pathSeparator = '\\';
+ //const std::string pathSeparatorStr = "\\";
+
+ std::string getAppPath()
+ {
+ char pathAndName[MAX_PATH], path[MAX_PATH], drive[3];
+ GetModuleFileName(NULL, pathAndName, MAX_PATH);
+ _splitpath(pathAndName, drive, path, 0, 0);
+ return std::string(drive)+path;
+ }
+
+#else
+ const char pathSeparator = '/';
+ // const std::string pathSeparatorStr = "/";
+
+ std::string getAppPath()
+ {
+ return "";
+ }
+
+#endif
+
+bool getDirectoryEntries(vector<string> &fileList,
+ string wildcard,
+ bool recursiveSearch)
+{
+ if (!wildcard.empty()) {
+ string directory_path;
+ string::size_type Pos = wildcard.find_last_of(pathSeparator);
+ if (Pos == string::npos) {
+ directory_path = ".";
+ } else {
+ directory_path = wildcard.substr(0, Pos + 1);
+ wildcard = wildcard.substr(Pos + 1);
+ }
+
+ dirstr::dirstream str( directory_path.c_str(),
+ #ifdef USE_FN_MATCH
+ dirstr::pred_f(FnMatcher(wildcard.c_str(), 0)),
+ #else
+ dirstr::pattern_f(wildcard.c_str()),
+ #endif
+ (recursiveSearch)?dirstr::recursive_yes:dirstr::recursive_no);
+
+
+ for(string entry; str >> entry;) {
+ fileList.push_back(dirstr::full_path(entry));
+ //std::cout << "Entry " <<entry<<"\n";
+ }
+ }
+ return !(fileList.empty());
+}
+
+}
+
+//
+// C++ Interface: platform_fs
+//
+// Description:
+//
+//
+// Author: André Simon <andre.simon1@gmx.de>, (C) 2004
+//
+// Copyright: See COPYING file that comes with this distribution
+//
+//
+#ifndef PLATFORM_FS__H__INCLUDED
+#define PLATFORM_FS__H__INCLUDED
+
+#include <string>
+#include <iostream>
+#include <vector>
+
+#ifdef USE_FN_MATCH
+ #include <fnmatch.h>
+#endif
+
+namespace Platform
+{
+ extern const char pathSeparator;
+ //extern const std::string pathSeparatorStr;
+
+ std::string getAppPath();
+
+ /** \param fileList Vector where found entries will be stored
+ \param wildcard Directory path and wildcard
+ \param recursiveSearch Test if directory should be searched recursively */
+ bool getDirectoryEntries(std::vector<std::string> &fileList,
+ std::string wildcard,
+ bool recursiveSearch=false);
+
+#ifdef USE_FN_MATCH
+ struct FnMatcher
+ {
+ FnMatcher(const char* pattern, int flags)
+ : pattern_(pattern)
+ , flags_(flags)
+ {}
+ bool operator()(const std::string& e) const {
+ // std::cout << "pattern: "<<pattern_<< " entry: "<<e.c_str()<< " Res fn: " <<::fnmatch(pattern_, e.c_str(), FNM_PATHNAME)<< " \n";
+ return ! ::fnmatch(pattern_, e.c_str(), flags_);
+ }
+ private:
+ const char* pattern_;
+ int flags_;
+ };
+#endif
+}
+#endif
+/***************************************************************************
+ PreFormatter.cpp - description
+ -------------------
+ begin : Mo Jan 03 2005
+ copyright : (C) 2005 by André Simon
+ email : andre.simon1@gmx.de
+ ***************************************************************************/
+
+/***************************************************************************
+ * *
+ * 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 of the License, or *
+ * (at your option) any later version. *
+ * *
+ ***************************************************************************/
+
+#include "preformatter.h"
+
+namespace highlight {
+
+PreFormatter::PreFormatter(bool wrap, bool replTabs):
+ maxLineLength(80),
+ index(0),
+ numberSpaces(0),
+ wsPrefixLength(string::npos),
+ hasMore(false),
+ indentAfterOpenBraces(true),
+ redefineWsPrefix(false),
+ wrapLines(wrap),
+ replaceTabs(replTabs)
+{
+}
+
+PreFormatter::PreFormatter():
+ maxLineLength(80),
+ index(0),
+ numberSpaces(0),
+ wsPrefixLength(string::npos),
+ hasMore(false),
+ indentAfterOpenBraces(true),
+ redefineWsPrefix(false),
+ wrapLines(false),
+ replaceTabs(false)
+{
+}
+
+PreFormatter::~PreFormatter()
+{
+}
+
+bool PreFormatter::hasMoreLines(){
+ return hasMore;
+}
+
+bool PreFormatter::indentCode(){
+ return indentAfterOpenBraces;
+}
+
+void PreFormatter::setLine(const std::string newLine){
+
+ line=newLine;
+
+ if (replaceTabs && numberSpaces) {
+ size_t tabPos=line.find('\t');
+ while (tabPos!=string::npos){
+ line.replace(tabPos , 1, numberSpaces - (tabPos % numberSpaces) , ' ');
+ tabPos = line.find('\t', tabPos+1);
+ }
+ }
+
+ if (wrapLines){
+ wsPrefix.clear();
+ index=0;
+ wsPrefixLength=string::npos;
+ hasMore=true;
+ redefineWsPrefix=false;
+ }
+}
+
+std::string PreFormatter::getNextLine(){
+
+ if (!wrapLines){
+ hasMore = false;
+ return line;
+ }
+
+ if (!index && line.length() > maxLineLength){ // erster Durchlauf...
+ // wenn möglich an öffnender Klammer oder Geichheitszeichen ausrichten
+ if (indentAfterOpenBraces){
+ wsPrefixLength=line.find_first_of(INDENT_MARKERS);
+ }
+ // sonst die Einrückung der Originalzeile beibehalten
+ if (wsPrefixLength==string::npos || wsPrefixLength-index>maxLineLength){
+ wsPrefixLength=line.find_first_not_of(WS_CHARS);
+ }
+ else {
+ // wsPrefix in allen neu umgebrochenen Zeilen durch Spaces ersetzen
+ redefineWsPrefix=true;
+ // Position hinter öffnende Klammer springen
+ wsPrefixLength=line.find_first_not_of(WS_CHARS,wsPrefixLength+1);
+ }
+
+ if (wsPrefixLength!=string::npos){
+ index = wsPrefixLength;
+ // Falls Anzahl der Whitespaces am beginn der ersten zeile größer
+ // als Max. Zeilenlänge, Whitespaces verwerfen
+ if (wsPrefixLength>maxLineLength){
+ wsPrefixLength=0;
+ return string();
+ }
+ else{
+ wsPrefix=line.substr(0, wsPrefixLength);
+ }
+ }
+ // Zeile enthaelt nur Whitespace; verwerfen
+ else {
+ hasMore= false;
+ return string();
+ }
+ } else {
+ if (redefineWsPrefix){
+ wsPrefix.clear();
+ wsPrefix.append(wsPrefixLength, ' ');
+ }
+ redefineWsPrefix=false;
+ }
+
+ string resultString;
+
+ // Position, ab der rckwaerts nach Umbruchmglichkeit gesucht wird
+ unsigned int searchEndPos = maxLineLength - wsPrefixLength;
+
+ // letztes Teilstueck der Zeile ausgeben; Parsen beenden
+ if (line.length()-index < searchEndPos) {
+ hasMore=false;
+ resultString=(index>0) ? wsPrefix + line.substr(index) : line.substr(index);
+ return resultString;
+ }
+
+ // Umbrechposition suchen
+ size_t lbPos = line.find_last_of(LB_CHARS, index+searchEndPos);
+ if (lbPos <= index || lbPos == string::npos) {
+ // nichts gefunden, hart umbrechen
+ lbPos = index + searchEndPos;
+ }
+ // Einrückung der Originalzeile erhalten
+ resultString+=wsPrefix;
+ // Neue Zeile erzeugen
+ resultString += line.substr(index, lbPos-index+1);
+
+ // Whitespace am neuen Zeilenbeginn ignorieren, ausser beim ersten Durchlauf
+ //unsigned int newIndex=StringTools::getNextNonWsPos(line,lbPos+1);
+ size_t newIndex=line.find_first_not_of(WS_CHARS, lbPos+1);
+ index=(newIndex!=string::npos)?newIndex:line.length();
+
+ hasMore=index!=line.length(); // unnoetigen Leerstring vermeiden
+
+ return resultString;
+}
+
+void PreFormatter::setWrappingProperties(unsigned int maxLineLength, bool indentAfterOpenBraces){
+ this->maxLineLength = maxLineLength;
+ this->indentAfterOpenBraces = indentAfterOpenBraces;
+}
+
+void PreFormatter::setNumberSpaces(unsigned int num){
+ numberSpaces = num;
+}
+
+}
+/***************************************************************************
+ PreFormatter.cpp - description
+ -------------------
+ begin : Mo Jan 03 2005
+ copyright : (C) 2005 by André Simon
+ email : andre.simon1@gmx.de
+ ***************************************************************************/
+
+/***************************************************************************
+ * *
+ * 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 of the License, or *
+ * (at your option) any later version. *
+ * *
+ ***************************************************************************/
+
+#ifndef PreFormatter_H
+#define PreFormatter_H
+
+#define LB_CHARS " \t[](){}-+<>.:,;"
+#define WS_CHARS " \n\r\t"
+#define INDENT_MARKERS "{(="
+
+#include <string>
+#include <iostream>
+
+#include "stringtools.h"
+
+namespace highlight {
+
+/** \brief Class which provides intelligent line wrapping.
+* @author Andre Simon
+*/
+
+class PreFormatter{
+public:
+ /** Constructor
+ */
+ PreFormatter(bool wrap, bool replTabs);
+
+ PreFormatter();
+
+ ~PreFormatter();
+
+ /**
+ \return True if current line can be wrapped again
+ */
+ bool hasMoreLines();
+
+ /**
+ Sets new line to be wrapped
+ \param newline New line
+ */
+ void setLine(const std::string newline);
+
+ /**
+ The method will indent function calls and statements
+ \return Next line
+ */
+ std::string getNextLine();
+
+ /**
+ \return True if lines following open braces should be indented
+ */
+ bool indentCode();
+
+ /**
+ \param maxlength max. length of output lines
+ \param indentAfterOpenBraces set true if lines should be indented after braces
+ */
+ void setWrappingProperties(unsigned int maxlength=80, bool indentAfterOpenBraces=true);
+
+ /**
+ \param num number of spaces which replace a tab
+ */
+
+ void setNumberSpaces(unsigned int num);
+
+ /**
+ \return true if preformatting is enabled
+ */
+ bool isEnabled(){
+ return wrapLines || replaceTabs;
+ }
+
+private:
+
+ unsigned int maxLineLength;
+
+ std::string line, wsPrefix;
+ unsigned int index;
+ unsigned int numberSpaces;
+ size_t wsPrefixLength;
+ bool hasMore, indentAfterOpenBraces;
+ bool redefineWsPrefix;
+ bool wrapLines, replaceTabs;
+};
+
+}
+
+#endif
+/***************************************************************************
+ rtfcode.cpp - description
+ -------------------
+ begin : Die Jul 9 2002
+ copyright : (C) 2002 by André Simon
+ email : andre.simon1@gmx.de
+ ***************************************************************************/
+
+/***************************************************************************
+ * *
+ * 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 of the License, or *
+ * (at your option) any later version. *
+ * *
+ ***************************************************************************/
+
+#include "rtfgenerator.h"
+
+using namespace std;
+
+namespace highlight {
+
+string RtfGenerator::formatStyleAttributes( const ElementStyle & col)
+{
+ stringstream s;
+ s << "\\red"<< col.getColour().getRTFRedValue()
+ << "\\green"<<col.getColour().getRTFGreenValue()
+ << "\\blue"<<col.getColour().getRTFBlueValue()
+ << ";";
+
+ return s.str();
+}
+
+string RtfGenerator::getOpenTag(int styleNumber,const ElementStyle & elem)
+{
+ ostringstream s;
+ s << "{\\cf"<<(styleNumber+1)<<"{";
+ if (elem.isBold()) s << "\\b ";
+ if (elem.isItalic()) s << "\\i ";
+ if (elem.isUnderline()) s << "\\ul ";
+ return s.str();
+}
+
+string RtfGenerator::getCloseTag(const ElementStyle &elem)
+{
+ ostringstream s;
+ if (elem.isBold()) s << "\\b0 ";
+ if (elem.isItalic()) s << "\\i0 ";
+ if (elem.isUnderline()) s << "\\ul0 ";
+ s << "}}";
+ return s.str();
+}
+
+RtfGenerator::RtfGenerator(const string &colourTheme)
+ : CodeGenerator( colourTheme)
+{
+ styleTagOpen.push_back(getOpenTag(STANDARD, docStyle.getDefaultStyle()));
+ styleTagOpen.push_back(getOpenTag(STRING, docStyle.getStringStyle()));
+ styleTagOpen.push_back(getOpenTag(NUMBER, docStyle.getNumberStyle()));
+ styleTagOpen.push_back(getOpenTag(SL_COMMENT, docStyle.getSingleLineCommentStyle()));
+ styleTagOpen.push_back(getOpenTag(ML_COMMENT_BEGIN,docStyle.getCommentStyle()));
+ styleTagOpen.push_back(getOpenTag(ESC_CHAR, docStyle.getEscapeCharStyle()));
+ styleTagOpen.push_back(getOpenTag(DIRECTIVE_LINE, docStyle.getDirectiveStyle()));
+ styleTagOpen.push_back(getOpenTag(DIRECTIVE_STRING, docStyle.getDirectiveStringStyle()));
+ styleTagOpen.push_back(getOpenTag(LINENUMBER, docStyle.getLineStyle()));
+ styleTagOpen.push_back(getOpenTag(SYMBOL, docStyle.getSymbolStyle()));
+
+ styleTagClose.push_back(getCloseTag(docStyle.getDefaultStyle()));
+ styleTagClose.push_back(getCloseTag(docStyle.getStringStyle()));
+ styleTagClose.push_back(getCloseTag(docStyle.getNumberStyle()));
+ styleTagClose.push_back(getCloseTag(docStyle.getSingleLineCommentStyle()));
+ styleTagClose.push_back(getCloseTag(docStyle.getCommentStyle()));
+ styleTagClose.push_back(getCloseTag(docStyle.getEscapeCharStyle()));
+ styleTagClose.push_back(getCloseTag(docStyle.getDirectiveStyle()));
+ styleTagClose.push_back(getCloseTag(docStyle.getDirectiveStringStyle()));
+ styleTagClose.push_back(getCloseTag(docStyle.getLineStyle()));
+ styleTagClose.push_back(getCloseTag(docStyle.getSymbolStyle()));
+
+ newLineTag = "\\par\\pard\n";
+ spacer = " ";
+}
+
+RtfGenerator::RtfGenerator()
+{}
+RtfGenerator::~RtfGenerator()
+{}
+
+string RtfGenerator::getHeader(const string & title)
+{
+ return string();
+}
+
+void RtfGenerator::printBody()
+{
+ *out << "{\\rtf1\\ansi\\uc0 \\deff1"
+ << "{\\fonttbl{\\f1\\fmodern\\fprq1\\fcharset0 Courier;}}"
+ << "{\\colortbl;";
+
+ *out << formatStyleAttributes(docStyle.getDefaultStyle());
+
+ *out << formatStyleAttributes(docStyle.getStringStyle());
+ *out << formatStyleAttributes(docStyle.getNumberStyle());
+ *out << formatStyleAttributes(docStyle.getSingleLineCommentStyle());
+
+ *out << formatStyleAttributes(docStyle.getCommentStyle());
+ *out << formatStyleAttributes(docStyle.getEscapeCharStyle());
+ *out << formatStyleAttributes(docStyle.getDirectiveStyle());
+
+ *out << formatStyleAttributes(docStyle.getDirectiveStringStyle());
+ *out << formatStyleAttributes(docStyle.getLineStyle());
+ *out << formatStyleAttributes(docStyle.getSymbolStyle());
+
+ /* For output formats which can refer to external styles it is more safe
+ to use the colour theme's keyword class names, since the language
+ definitions (which may change during a batch conversion) do not have to define
+ all keyword classes, that are needed to highlight all input files correctly.
+ It is ok for RTF to use the language definition's class names, because RTF
+ does not refer to external styles.
+ We cannot use the theme's class names, because KSIterator returns an
+ alphabetically ordered list, which is not good because RTF is dependent
+ on the order. We access the keyword style with an ID, which is calculated
+ ignoring the alphabetic order.
+ */
+ vector<string> keywordClasses = langInfo.getKeywordClasses();
+ for (unsigned int i=0;i<keywordClasses.size();i++){
+ *out << formatStyleAttributes(docStyle.getKeywordStyle(keywordClasses[i]));
+ }
+
+ *out << "}\n{\\info }\\paperw11900\\paperh16820\\margl560\\margr560\\margt840"
+ << "\\margb840\\widowctrl\\ftnbj\\aenddoc\\formshade \\fet0\\sectd"
+ << "\\linex0\\endnhere\\plain\\f1\\fs20\n\\pard ";
+ processRootState();
+ *out << "}}"<<endl;
+}
+
+
+string RtfGenerator::getFooter()
+{
+ return string();
+}
+
+/** Gibt RTF-Code der Sonderzeichen zurueck */
+string RtfGenerator::maskCharacter(unsigned char c)
+{
+ switch (c)
+ {
+ case '}' :
+ case '{' :
+ case '\\' :
+ {
+ string m;
+ m="\\";
+ return m+=c;
+ }
+ break;
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ {
+ string m;
+ m="{";
+ m+=c;
+ m+="}";
+ return m;
+ }
+ break;
+ case AUML_LC:
+ return "\\'e4";
+ break;
+ case OUML_LC:
+ return "\\'f6";
+ break;
+ case UUML_LC:
+ return "\\'fc";
+ break;
+ case AUML_UC:
+ return "\\'c4";
+ break;
+ case OUML_UC:
+ return "\\'d6";
+ break;
+ case UUML_UC:
+ return "\\'dc";
+ break;
+
+ case AACUTE_LC:
+ return "\\'e1";
+ break;
+ case EACUTE_LC:
+ return "\\'e9";
+ break;
+ case OACUTE_LC:
+ return "\\'f3";
+ break;
+ case UACUTE_LC:
+ return "\\'fa";
+ break;
+
+ case AGRAVE_LC:
+ return "\\'e0";
+ break;
+ case EGRAVE_LC:
+ return "\\'e8";
+ break;
+ case OGRAVE_LC:
+ return "\\'f2";
+ break;
+ case UGRAVE_LC:
+ return "\\'f9";
+ break;
+
+ case AACUTE_UC:
+ return "\\'c1";
+ break;
+ case EACUTE_UC:
+ return "\\'c9";
+ break;
+ case OACUTE_UC:
+ return "\\'d3";
+ break;
+ case UACUTE_UC:
+ return "\\'da";
+ break;
+ case AGRAVE_UC:
+ return "\\'c0";
+ break;
+ case EGRAVE_UC:
+ return "\\'c8";
+ break;
+ case OGRAVE_UC:
+ return "\\'d2";
+ break;
+ case UGRAVE_UC:
+ return "\\'d9";
+ break;
+
+ case SZLIG:
+ return "\\'df";
+ break;
+ // skip first byte of multibyte chracters
+ /* #ifndef _WIN32
+ case 195:
+ return string("");
+ break;
+#endif*/
+
+ default :
+ {
+ string m;
+ return m += c;
+ }
+ }
+}
+
+string RtfGenerator::getMatchingOpenTag(unsigned int styleID){
+ return getOpenTag(KEYWORD+styleID,
+ docStyle.getKeywordStyle(langInfo.getKeywordClasses()[styleID]));
+}
+
+string RtfGenerator::getMatchingCloseTag(unsigned int styleID){
+ return getCloseTag(docStyle.getKeywordStyle(langInfo.getKeywordClasses()[styleID]));
+}
+
+
+}
+/***************************************************************************
+ rtfcode.h - description
+ -------------------
+ begin : Die Jul 9 2002
+ copyright : (C) 2002 by Andre Simon
+ email : andre.simon1@gmx.de
+ ***************************************************************************/
+
+/***************************************************************************
+ * *
+ * 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 of the License, or *
+ * (at your option) any later version. *
+ * *
+ ***************************************************************************/
+
+#ifndef RTFGENERATOR_H
+#define RTFGENERATOR_H
+
+#include <iostream>
+#include <fstream>
+#include <string>
+#include <sstream>
+
+#include "codegenerator.h"
+#include "charcodes.h"
+#include "version.h"
+
+namespace highlight {
+
+/**
+ \brief This class generates RTF.
+
+ It contains information about the resulting document structure (document
+ header and footer), the colour system, white space handling and text
+ formatting attributes.
+
+* @author Andre Simon
+*/
+
+class RtfGenerator : public highlight::CodeGenerator
+ {
+ public:
+
+ /** Constructor
+ \param colourTheme Name of Colour theme to use
+ */
+ RtfGenerator( const string &colourTheme);
+ RtfGenerator();
+ ~RtfGenerator();
+
+ /** prints document header
+ \param title Title of the document
+ */
+ string getHeader(const string & title);
+
+ /** Prints document footer*/
+ string getFooter();
+
+ /** Prints document body*/
+ void printBody();
+
+ private:
+
+ /** \return escaped character*/
+ virtual string maskCharacter(unsigned char );
+
+ /**\return text formatting attributes in RTF format */
+ string formatStyleAttributes( const ElementStyle & col);
+
+ /** gibt RTF-"Tags" zurueck (Farbindex+bold+kursiv)*/
+ string getOpenTag(int styleNumber,const ElementStyle &);
+
+ string getCloseTag(const ElementStyle &);
+
+ string getMatchingOpenTag(unsigned int styleID);
+ string getMatchingCloseTag(unsigned int styleID);
+ };
+
+}
+#endif
+/***************************************************************************
+ stringtools.cpp - description
+ -------------------
+ begin : Mon Dec 10 2001
+ copyright : (C) 2001 by André Simon
+ email : andre.simon1@gmx.de
+ ***************************************************************************/
+
+/***************************************************************************
+ * *
+ * 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 of the License, or *
+ * (at your option) any later version. *
+ * *
+ ***************************************************************************/
+
+#include "stringtools.h"
+
+#include <sstream>
+#include <iostream>
+#include <cctype>
+
+using namespace std;
+
+namespace StringTools
+ {
+// Make a lowercase copy of s:
+// (C) Bruce Eckel, Thinking in C++ Vol 2
+
+string lowerCase(const string& s)
+{
+ char* buf = new char[s.length()];
+ s.copy(buf, s.length());
+ for(unsigned int i = 0; i < s.length(); i++)
+ buf[i] = tolower(buf[i]);
+ string r(buf, s.length());
+ delete buf;
+ return r;
+}
+
+int str2int(string s)
+{
+ istringstream os(s);
+ int intVal;
+ os >> intVal;
+ return intVal;
+}
+
+ bool isAlpha(unsigned char c)
+ {
+ return (isalpha(c) || c == '_');
+ }
+
+string trimRight(const string &value)
+ {
+ string::size_type where = value.find_last_not_of(" \t\r");
+
+ if (where == string::npos)
+ // string has nothing but space
+ return string();
+
+ if (where == (value.length() - 1))
+ // string has no trailing space, don't copy its contents
+ return value;
+
+ return value.substr(0, where + 1);
+ }
+
+unsigned char getNextNonWs(const string &line, int index)
+{
+ unsigned char c;
+ do
+ {
+ c=line[index++];
+ }
+ while (isspace(c));
+ return c;
+}
+
+string getParantheseVal(const string &s){
+ string::size_type openPos=s.find('(');
+ string::size_type closePos=s.rfind(')');
+ if (openPos ==string::npos || closePos==string::npos){
+ return string();
+ }
+ return s.substr(openPos+1, closePos-openPos-1);
+
+}
+
+}
+/***************************************************************************
+ stringtools.h - description
+ -------------------
+ begin : Mon Dec 10 2001
+ copyright : (C) 2001 by André Simon
+ email : andre.simon1@gmx.de
+ ***************************************************************************/
+
+/***************************************************************************
+ * *
+ * 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 of the License, or *
+ * (at your option) any later version. *
+ * *
+ ***************************************************************************/
+
+#ifndef STRINGTOOLS_H
+#define STRINGTOOLS_H
+
+#include <string>
+
+using namespace std;
+
+/**\brief Contains methods for string manipulation
+ *@author Andre Simon
+ */
+
+namespace StringTools
+ {
+
+ /** \param s String
+ \returns lowercase string */
+ string lowerCase(const string &s);
+
+ /** \param s String
+ \returns Integer value */
+ int str2int(string s);
+
+ /** \return true if c is alpa or underscore */
+ bool isAlpha(unsigned char c);
+
+ /** \param value String
+ \return string trimmed on the left side
+ */
+ string trimRight(const string &value);
+
+ /** \return next character in line starting from index, which is no whitespace*/
+ unsigned char getNextNonWs(const string &line, int index=0);
+
+ /** \param s String, containing a opening and a closing paranthesis
+ \return value between "(", ")" */
+ string getParantheseVal(const string &s);
+
+}
+
+#endif
+/***************************************************************************
+ stylecolour.cpp - description
+ -------------------
+ begin : Die Nov 5 2002
+ copyright : (C) 2002 by André Simon
+ email : andre.simon1@gmx.de
+ ***************************************************************************/
+
+/***************************************************************************
+ * *
+ * 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 of the License, or *
+ * (at your option) any later version. *
+ * *
+ ***************************************************************************/
+
+#include "stylecolour.h"
+
+using std::string;
+
+namespace highlight {
+
+StyleColour::StyleColour(const string & r_hex, const string & g_hex, const string & b_hex)
+ : r(r_hex), g(g_hex), b(b_hex)
+{}
+StyleColour::StyleColour()
+ : r("00"), g("00"), b("00")
+{}
+
+//Parst PArameter aus style-Datei
+StyleColour::StyleColour(const string & styleColourString)
+{
+ setRGBValues(styleColourString);
+}
+
+void StyleColour::setRGBValues(const string & styleColourString){
+ //Stringstream zum Einlesen der Tokens:
+ istringstream valueStream(styleColourString.c_str());
+ valueStream >> r;
+ valueStream >> g;
+ valueStream >> b;
+}
+
+void StyleColour::setRedValue(const string & r_hex)
+{
+ r = r_hex;
+}
+
+void StyleColour::setGreenValue(const string & g_hex)
+{
+ g = g_hex;
+}
+
+void StyleColour::setBlueValue(const string & b_hex)
+{
+ b = b_hex;
+}
+
+string& StyleColour::getHexRedValue()
+{
+ return r;
+}
+string& StyleColour::getHexGreenValue()
+{
+ return g;
+}
+string& StyleColour::getHexBlueValue()
+{
+ return b;
+}
+
+
+string StyleColour::getRTFRedValue()
+{
+ return int2str(hex2dec(r));
+}
+string StyleColour::getRTFGreenValue()
+{
+ return int2str(hex2dec(g));
+}
+string StyleColour::getRTFBlueValue()
+{
+ return int2str(hex2dec(b));
+}
+
+
+string StyleColour::getLatexRedValue()
+{
+ return float2str((float)hex2dec(r)/255);
+}
+string StyleColour::getLatexGreenValue()
+{
+ return float2str((float)hex2dec(g)/255);
+}
+string StyleColour::getLatexBlueValue()
+{
+ return float2str((float)hex2dec(b)/255);
+}
+
+// Konvertieren von RGB nach CYM
+string StyleColour::getTexRedValue()
+{
+ return float2str(1-(float)hex2dec(r)/255);
+}
+string StyleColour::getTexGreenValue()
+{
+ return float2str(1-(float)hex2dec(g)/255);
+}
+string StyleColour::getTexBlueValue()
+{
+ return float2str(1-(float)hex2dec(b)/255);
+}
+
+
+string StyleColour::int2str(const int num)
+{
+ std::ostringstream outStream;
+ outStream << num;
+
+ return outStream.str();
+}
+
+string StyleColour::float2str(const double num)
+{
+ std::ostringstream outStream;
+ outStream << ( floor ( num * 100 + .5 ) / 100);
+
+ return outStream.str();
+}
+
+int StyleColour::hex2dec(const string &hexVal)
+{
+
+ if (hexVal.length() != 2)
+ return 0;
+
+ unsigned int decVal=0, koeff=16;
+
+ for (int i=0; i<2;i++ )
+ {
+ if ((hexVal[i] >= '0')&& (hexVal[i]<= '9' ))
+ {
+ decVal += (koeff * (hexVal[i]-'0'));
+
+ }
+ if ((hexVal[i] >= 'a')&& (hexVal[i]<= 'f' ))
+ {
+ decVal +=( koeff * (hexVal[i]-87));
+ }
+ if ((hexVal[i] >= 'A')&& (hexVal[i]<= 'F' ))
+ {
+ decVal += (koeff * (hexVal[i]-55));
+ }
+ koeff=1;
+ }
+ return decVal;
+}
+
+}
+
+/***************************************************************************
+ stylecolour.h - description
+ -------------------
+ begin : Die Nov 5 2002
+ copyright : (C) 2002 by Andre Simon
+ email : andre.simon1@gmx.de
+ ***************************************************************************/
+
+/***************************************************************************
+ * *
+ * 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 of the License, or *
+ * (at your option) any later version. *
+ * *
+ ***************************************************************************/
+
+#ifndef STYLECOLOUR_H
+#define STYLECOLOUR_H
+
+#include <string>
+#include <sstream>
+#include <fstream>
+#include <cmath>
+#include <sstream>
+
+using namespace std;
+
+namespace highlight {
+
+/**\brief Stores colours and returns red, green and blue values in different formats
+* @author Andre Simon
+ */
+
+class StyleColour
+ {
+ public:
+ /** Constructor
+ \param r_hex Red value in hex notation
+ \param g_hex Blue value in hex notation
+ \param b_hex Green value in hex notation
+ */
+ StyleColour(const string & r_hex, const string & g_hex, const string & b_hex);
+
+ /** Constructor
+ \param styleColourString String with rgb values
+ */
+ StyleColour(const string & styleColourString);
+
+ StyleColour();
+ ~StyleColour(){};
+
+ /** Sets red, green and blue values
+ \param styleColourString String containing colour attributes
+ */
+ void setRGBValues(const string & styleColourString);
+
+ /** Sets red value
+ \param r_hex New red value */
+ void setRedValue(const string & r_hex);
+
+ /** Sets green value
+ \param g_hex New green value */
+ void setGreenValue(const string & g_hex);
+
+ /** Sets blue value
+ \param b_hex New blue value */
+ void setBlueValue(const string & b_hex);
+
+ /** \return Red value in hex format */
+ string& getHexRedValue();
+ /** \return Green value in hex format */
+ string& getHexGreenValue();
+ /** \return Blue value in hex format */
+ string& getHexBlueValue();
+
+ /** \return Red value in latex format */
+ string getLatexRedValue();
+ /** \return Green value in latex format */
+ string getLatexGreenValue();
+ /** \return Blue value in latex format */
+ string getLatexBlueValue();
+
+ /** \return Red value in tex format */
+ string getTexRedValue();
+ /** \return Green value in tex format */
+ string getTexGreenValue();
+ /** \return Blue value in tex format */
+ string getTexBlueValue();
+
+ /** \return Red value in RTF format */
+ string getRTFRedValue();
+ /** \return Green value in RTF format */
+ string getRTFGreenValue();
+ /** \return Blue value in RTF format */
+ string getRTFBlueValue();
+
+ private:
+ string r, g, b;
+ string int2str(int);
+ string float2str(double);
+ int hex2dec(const string &hexVal);
+ };
+
+}
+
+#endif
+/***************************************************************************
+ TexGenerator.cpp - description
+ -------------------
+ begin : Mit Jul 24 2002
+ copyright : (C) 2002 by André Simon
+ email : andre.simon1@gmx.de
+ ***************************************************************************/
+
+/***************************************************************************
+ * *
+ * 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 of the License, or *
+ * (at your option) any later version. *
+ * *
+ ***************************************************************************/
+
+#include "texgenerator.h"
+
+namespace highlight {
+
+TexGenerator::TexGenerator(const string &colourTheme):
+ CodeGenerator( colourTheme)
+{
+ styleTagOpen.push_back( "{\\hlstd ");
+ styleTagOpen.push_back( "{\\hlstr ");
+ styleTagOpen.push_back( "{\\hlnum ");
+ styleTagOpen.push_back( "{\\hlslc ");
+ styleTagOpen.push_back( "{\\hlcom ");
+ styleTagOpen.push_back( "{\\hlesc ");
+ styleTagOpen.push_back( "{\\hldir ");
+ styleTagOpen.push_back( "{\\hldstr ");
+ styleTagOpen.push_back( "{\\hlline ");
+ styleTagOpen.push_back( "{\\hlsym ");
+ for (int i=0;i<NUMBER_BUILTIN_STYLES; i++) {
+ styleTagClose.push_back( "}");
+ }
+
+ /*This makes TeX to use every par it encounters (the \\leavevmode has
+ no effect when TeX is in horizontal mode and when TeX is in vertical
+ mode, it switches it to horizontal mode).*/
+ newLineTag="\\leavevmode\\par\n";
+
+ spacer = "\\ ";
+ maskWs=true;
+ excludeWs=true;
+ maskWsBegin = "{\\hlstd";
+ maskWsEnd = "}";
+ styleCommentOpen="%";
+}
+
+TexGenerator::TexGenerator()
+{}
+TexGenerator::~TexGenerator()
+{}
+
+string TexGenerator::formatStyleAttributes(const string & elemName,const ElementStyle & elem)
+{
+ ostringstream s;
+ s << "\\def\\hl"
+ << elemName
+ << "{";
+ if (elem.isBold())
+ s << "\\bf";
+ if (elem.isItalic())
+ s << "\\it";
+ s << "\\textColor{"
+ << (elem.getColour().getTexRedValue())<<" "
+ << (elem.getColour().getTexGreenValue())<<" "
+ << (elem.getColour().getTexBlueValue())<<" 0.0}}\n";
+ return s.str();
+}
+
+string TexGenerator::getHeader(const string & title)
+{
+ ostringstream os;
+
+ if (langInfo.getSyntaxHighlight()) {
+ if (includeStyleDef) {
+ os << "\n"<<getStyleDefinition();
+ os << CodeGenerator::readUserStyleDef();
+ } else {
+ os << "\\input "
+ << getStyleOutputPath()
+ << "\n\n";
+ }
+ }
+
+ return os.str();
+}
+
+void TexGenerator::printBody()
+{
+ *out << "{\n\\tt\n";
+
+ processRootState();
+ *out << "}\n";
+}
+
+string TexGenerator::getFooter()
+{
+ ostringstream os;
+ os << "\\bye\n"
+ << "% TeX generated by Highlight "
+ << HIGHLIGHT_VERSION
+ << ", "
+ << HIGHLIGHT_URL
+ << endl;
+ return os.str();
+}
+
+string TexGenerator:: maskCharacter(unsigned char c)
+{
+ switch (c)
+ {
+ case '{':
+ case '}':
+ {
+ string m;
+ m = "$\\";
+ m += c;
+ m += "$";
+ return m;
+ }
+ break;
+ case '^':
+ return "{\\bf\\^{}}";
+ break;
+ case '_':
+ return "\\_{}";
+ break;
+ case '&':
+ case '$':
+ case '#':
+ case '%':
+ {
+ string m;
+ m = "\\";
+ m += c;
+ return m;
+ }
+ break;
+ case '\\':
+ return "$\\backslash$";
+ break;
+ case ' ':
+ return spacer;
+ break;
+ case '+':
+ case '-':
+ case '<':
+ case '>':
+ case '=':
+ {
+ string m;
+ m = "$\\mathord{";
+ m += c;
+ m += "}$";
+ return m;
+ }
+ break;
+ case AUML_LC:
+ return "\\\"a";
+ break;
+ case OUML_LC:
+ return "\\\"o";
+ break;
+ case UUML_LC:
+ return "\\\"u";
+ break;
+ case AUML_UC:
+ return "\\\"A";
+ break;
+ case OUML_UC:
+ return "\\\"O";
+ break;
+ case UUML_UC:
+ return "\\\"U";
+ break;
+ case AACUTE_LC:
+ return "\\'a";
+ break;
+ case EACUTE_LC:
+ return "\\'e";
+ break;
+ case OACUTE_LC:
+ return "\\'o";
+ break;
+ case UACUTE_LC:
+ return "\\'u";
+ break;
+ case AGRAVE_LC:
+ return "\\`a";
+ break;
+ case EGRAVE_LC:
+ return "\\`e";
+ break;
+ case OGRAVE_LC:
+ return "\\`o";
+ break;
+ case UGRAVE_LC:
+ return "\\`u";
+ break;
+ case AACUTE_UC:
+ return "\\'A";
+ break;
+ case EACUTE_UC:
+ return "\\'E";
+ break;
+ case OACUTE_UC:
+ return "\\'O";
+ break;
+ case UACUTE_UC:
+ return "\\'U";
+ break;
+ case AGRAVE_UC:
+ return "\\`A";
+ break;
+ case EGRAVE_UC:
+ return "\\`E";
+ break;
+ case UGRAVE_UC:
+ return "\\`O";
+ break;
+ case OGRAVE_UC:
+ return "\\`U";
+ break;
+ case SZLIG:
+ return "\\ss ";
+ break;
+ /* #ifndef _WIN32
+ // skip first byte of multibyte chracters
+ case 195:
+ return string("");
+ break;
+#endif*/
+
+ default :
+ string m;
+ return m += c;
+ }
+}
+
+string TexGenerator::getMatchingOpenTag(unsigned int styleID){
+ return "{\\hl"+langInfo.getKeywordClasses()[styleID]+" ";
+ }
+
+string TexGenerator::getMatchingCloseTag(unsigned int styleID){
+ return "}";
+}
+
+
+string TexGenerator::getStyleDefinition()
+{
+ if (styleDefinitionCache.empty()){
+ ostringstream os;
+ os << formatStyleAttributes("std", docStyle.getDefaultStyle());
+ os << formatStyleAttributes("num", docStyle.getNumberStyle());
+ os << formatStyleAttributes("esc", docStyle.getEscapeCharStyle());
+ os << formatStyleAttributes("str", docStyle.getStringStyle());
+ os << formatStyleAttributes("dstr", docStyle.getDirectiveStringStyle());
+ os << formatStyleAttributes("slc", docStyle.getSingleLineCommentStyle());
+ os << formatStyleAttributes("com", docStyle.getCommentStyle());
+ os << formatStyleAttributes("dir", docStyle.getDirectiveStyle());
+ os << formatStyleAttributes("line", docStyle.getLineStyle());
+ os << formatStyleAttributes("sym", docStyle.getSymbolStyle());
+
+ KeywordStyles styles = docStyle.getKeywordStyles();
+ for (KSIterator it=styles.begin(); it!=styles.end(); it++){
+ os << formatStyleAttributes(it->first, *(it->second));
+ }
+
+ os << "% The special option is not supported by all dvi drivers\n"
+ << "\\special{background rgb "
+ << docStyle.getBgColour().getLatexRedValue() << " "
+ << docStyle.getBgColour().getLatexGreenValue() << " "
+ << docStyle.getBgColour().getLatexBlueValue() << "}";
+ os << "\n\\nopagenumbers\n"
+ << "\\input colordvi\n";
+ styleDefinitionCache=os.str();
+ }
+ return styleDefinitionCache;
+}
+
+
+}
+/***************************************************************************
+ texcode.h - description
+ -------------------
+ begin : Mit Jul 24 2002
+ copyright : (C) 2002 by André Simon
+ email : andre.simon1@gmx.de
+ ***************************************************************************/
+
+/***************************************************************************
+ * *
+ * 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 of the License, or *
+ * (at your option) any later version. *
+ * *
+ ***************************************************************************/
+
+#ifndef TEXGENERATOR_H
+#define TEXGENERATOR_H
+
+#include <string>
+#include <iostream>
+#include <sstream>
+
+#include "charcodes.h"
+#include "version.h"
+#include "codegenerator.h"
+
+
+namespace highlight {
+
+/**
+ \brief This class generates TeX.
+
+ It contains information about the resulting document structure (document
+ header and footer), the colour system, white space handling and text
+ formatting attributes.
+
+* @author Andre Simon
+*/
+
+class TexGenerator : public highlight::CodeGenerator
+ {
+ public:
+
+ /** Constructor
+ \param colourTheme Name of Colour theme to use
+ */
+ TexGenerator(const string &colourTheme);
+ TexGenerator();
+ ~TexGenerator();
+
+ /** prints document header
+ \param title Title of the document
+ */
+ string getHeader(const string & title);
+
+ /** Prints document footer*/
+ string getFooter();
+
+ /** Prints document body*/
+ void printBody();
+
+ private:
+
+ string styleDefinitionCache;
+
+ string getStyleDefinition();
+
+ /** \return escaped character*/
+ virtual string maskCharacter(unsigned char );
+
+ /**\return text formatting attributes in RTF format */
+ string formatStyleAttributes(const string & elemName, const ElementStyle & elem);
+
+ string getMatchingOpenTag(unsigned int styleID);
+ string getMatchingCloseTag(unsigned int styleID);
+
+ };
+
+}
+
+#endif
+/***************************************************************************
+ version.h - description
+ -------------------
+ begin : Mon March 3 2003
+ copyright : (C) 2003 by André Simon
+ email : andre.simon1@gmx.de
+ ***************************************************************************/
+
+/***************************************************************************
+ * *
+ * 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 of the License, or *
+ * (at your option) any later version. *
+ * *
+ ***************************************************************************/
+
+#ifndef VERSION_H
+#define VERSION_H
+
+#define HIGHLIGHT_VERSION "2.2-10"
+
+#define HIGHLIGHT_URL "http://www.andre-simon.de/"
+#define HIGHLIGHT_EMAIL "andre.simon1@gmx.de"
+
+#endif
+/***************************************************************************
+ htmlcode.cpp - description
+ -------------------
+ begin : Wed Nov 28 2001
+ copyright : (C) 2001 by André Simon
+ email : andre.simon1@gmx.de
+ ***************************************************************************/
+
+/***************************************************************************
+ * *
+ * 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 of the License, or *
+ * (at your option) any later version. *
+ * *
+ ***************************************************************************/
+
+#include "xhtmlgenerator.h"
+
+using namespace std;
+
+namespace highlight {
+
+XHtmlGenerator::XHtmlGenerator(void)
+{}
+
+XHtmlGenerator::XHtmlGenerator (
+ const string &cssStyle,
+ const string &enc,
+ bool omitEnc,
+ bool withAnchors)
+ : HtmlGenerator(cssStyle, enc, omitEnc, withAnchors)
+{
+ fileSuffix=".xhtml";
+ brTag="<br />";
+ hrTag="<hr />";
+ idAttr="id";
+
+ HTML_FOOTER=
+ "\n</body>\n</html>\n<!--XHTML generated by highlight "
+ HIGHLIGHT_VERSION
+ ", "
+ HIGHLIGHT_URL
+ "-->\n";
+}
+
+string XHtmlGenerator::getHeaderStart(const string &title){
+ ostringstream header;
+ header << "<?xml version=\"1.0\"";
+ if (!omitEncoding) {
+ header << " encoding=\"" << encoding << "\"";
+ }
+ header << "?>\n<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.1//EN\""
+ << " \"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd\">\n"
+ << "<html xmlns=\"http://www.w3.org/1999/xhtml\">\n"
+ << "<head>\n<title>" << title << "</title>\n";
+
+ return header.str();
+}
+
+
+string XHtmlGenerator::getHeader(const string &title)
+{
+ ostringstream osPart1;
+ osPart1 << getHeaderStart((title.empty())?"Source file":title );
+
+ if (langInfo.getSyntaxHighlight())
+ {
+ if (includeStyleDef) //CSS-Definition in HTML-<head> einfuegen
+ {
+ osPart1 << "<style type=\"text/css\">\n";
+ osPart1 << "<![CDATA[\n";
+ osPart1 << getStyleDefinition();
+ osPart1 << CodeGenerator::readUserStyleDef();
+ osPart1 << "]]>\n";
+ osPart1 << "</style>\n";
+ }
+ else //Referenz auf CSS-Datei einfuegen
+ {
+ osPart1 << "<link rel=\"stylesheet\" type=\"text/css\" href=\""
+ << getStyleOutputPath()
+ << "\""
+ << "/"
+ << ">\n";
+ }
+ }
+ osPart1 << "</head>\n<body class=\"hl\">\n<pre class=\"hl\">";
+
+ return osPart1.str();
+}
+
+}
+/***************************************************************************
+ xhtmlgenerator.h - description
+ -------------------
+ begin : Mo Jun 21 2004
+ copyright : (C) 2004 by Andre Simon
+ email : andre.simon1@gmx.de
+ ***************************************************************************/
+
+/***************************************************************************
+ * *
+ * 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 of the License, or *
+ * (at your option) any later version. *
+ * *
+ ***************************************************************************/
+
+
+#ifndef XHTMLGENERATOR_H
+#define XHTMLGENERATOR_H
+
+#include "htmlgenerator.h"
+
+namespace highlight {
+
+/**
+ \brief This class generates XHTML.
+
+ It contains information about the resulting document structure (document
+ header and footer), the colour system, white space handling and text
+ formatting attributes.
+
+* @author Andre Simon
+*/
+
+
+class XHtmlGenerator : public highlight::HtmlGenerator
+ {
+ public:
+
+ /** Constructor
+ \param colourTheme Name of Colour theme to use
+ \param enc encoding name
+ \param omitEnc switch to omit encoding information
+ \param withAnchors Test if HTML anchors should be attached to line numbers
+ */
+ XHtmlGenerator(const string &colourTheme,
+ const string &enc,
+ bool omitEnc=false,
+ bool withAnchors = false);
+
+ XHtmlGenerator();
+
+ /** Destructor*/
+ virtual ~XHtmlGenerator() {};
+
+ private:
+
+ /** prints document header
+ \param title Title of the document
+ */
+ string getHeader(const string &title);
+
+ string getHeaderStart(const string &title);
+
+ };
+
+}
+
+#endif
+/***************************************************************************
+ xmlcode.cpp - description
+ -------------------
+ begin : Do 20.01.2005
+ copyright : (C) 2005 by Andre Simon
+ email : andre.simon1@gmx.de
+ ***************************************************************************/
+
+/***************************************************************************
+ * *
+ * 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 of the License, or *
+ * (at your option) any later version. *
+ * *
+ ***************************************************************************/
+
+#include "xmlgenerator.h"
+
+using namespace std;
+namespace highlight {
+
+ XmlGenerator::XmlGenerator(const string &colourTheme,const string &enc, bool omitEnc)
+ : CodeGenerator(colourTheme),
+ encoding(enc), omitEncoding(omitEnc)
+{
+ styleTagOpen.push_back(getOpenTag("def"));
+ styleTagOpen.push_back(getOpenTag("str"));
+ styleTagOpen.push_back(getOpenTag("num"));
+ styleTagOpen.push_back(getOpenTag("slc"));
+ styleTagOpen.push_back(getOpenTag("com"));
+ styleTagOpen.push_back(getOpenTag("esc"));
+ styleTagOpen.push_back(getOpenTag("dir"));
+ styleTagOpen.push_back(getOpenTag("dstr"));
+ styleTagOpen.push_back(getOpenTag("line"));
+ styleTagOpen.push_back(getOpenTag("sym"));
+
+ styleTagClose.push_back(getCloseTag("def"));
+ styleTagClose.push_back(getCloseTag("str"));
+ styleTagClose.push_back(getCloseTag("num"));
+ styleTagClose.push_back(getCloseTag("slc"));
+ styleTagClose.push_back(getCloseTag("com"));
+ styleTagClose.push_back(getCloseTag("esc"));
+ styleTagClose.push_back(getCloseTag("dir"));
+ styleTagClose.push_back(getCloseTag("dstr"));
+ styleTagClose.push_back(getCloseTag("line"));
+ styleTagClose.push_back(getCloseTag("sym"));
+
+ spacer = " ";
+ newLineTag = "<br />\n";
+}
+
+string XmlGenerator::getStyleDefinition()
+{
+ if (styleDefinitionCache.empty()) {
+ ostringstream os;
+ os << "\n<style>\n"
+ << "\t<bgcolor value=\""
+ << (docStyle.getBgColour().getHexRedValue())
+ << (docStyle.getBgColour().getHexGreenValue())
+ << (docStyle.getBgColour().getHexBlueValue())
+ << "\" />\n"
+ << "\t<font size=\""
+ << docStyle.getFontSize()
+ << "\" family=\"Courier\" />\n";
+
+ os << formatStyleAttributes("def", docStyle.getDefaultStyle())
+ << formatStyleAttributes("num", docStyle.getNumberStyle())
+ << formatStyleAttributes("esc", docStyle.getEscapeCharStyle())
+ << formatStyleAttributes("str", docStyle.getStringStyle())
+ << formatStyleAttributes("dstr", docStyle.getDirectiveStringStyle())
+ << formatStyleAttributes("slc", docStyle.getSingleLineCommentStyle())
+ << formatStyleAttributes("com", docStyle.getCommentStyle())
+ << formatStyleAttributes("dir", docStyle.getDirectiveStyle())
+ << formatStyleAttributes("sym", docStyle.getSymbolStyle())
+ << formatStyleAttributes("line", docStyle.getLineStyle());
+
+ KeywordStyles styles = docStyle.getKeywordStyles();
+ for (KSIterator it=styles.begin(); it!=styles.end(); it++){
+ os << formatStyleAttributes(it->first, *(it->second));
+ }
+ os << "</style>\n";
+ styleDefinitionCache=os.str();
+ }
+ return styleDefinitionCache;
+}
+
+
+string XmlGenerator::formatStyleAttributes(const string & elemName,
+ const ElementStyle & elem)
+{
+ ostringstream s;
+ s << "\t<class name=\""
+ << elemName
+ <<"\" color=\""
+ << (elem.getColour().getHexRedValue())
+ << (elem.getColour().getHexGreenValue())
+ << (elem.getColour().getHexBlueValue() )
+ << "\" bold=\""
+ << ( elem.isBold() ? "true" :"false" )
+ << "\" italic=\""
+ << ( elem.isItalic() ? "true" :"false" )
+ << "\" underline=\""
+ << ( elem.isUnderline() ? "true" :"false" )
+ << "\" />\n" ;
+ return s.str();
+}
+
+
+XmlGenerator::XmlGenerator()
+{}
+XmlGenerator::~XmlGenerator()
+{}
+
+string XmlGenerator::getOpenTag(const string& styleName ){
+ return "<"+styleName+">";
+}
+
+string XmlGenerator::getCloseTag(const string& styleName ){
+ return "</"+styleName+">";
+}
+
+string XmlGenerator::getHeader(const string & title)
+{
+ ostringstream header;
+ header << "<?xml version=\"1.0\"";
+ if (!omitEncoding) {
+ header << " encoding=\"" << encoding << "\"";
+ }
+ header << "?>\n<document>" << getStyleDefinition();
+ return header.str();
+}
+
+void XmlGenerator::printBody()
+{
+ *out << "<source>\n";
+ processRootState();
+ *out << "</source>\n";
+}
+
+
+string XmlGenerator::getFooter()
+{
+ ostringstream os;
+ os <<"</document>\n";
+ os<< "<!-- XML generated by Highlight "
+ << HIGHLIGHT_VERSION
+ << ", "
+ << HIGHLIGHT_URL
+ <<" -->\n";
+ return os.str();
+}
+
+string XmlGenerator::maskCharacter(unsigned char c)
+{
+ switch (c)
+ {
+ case '<' :
+ return "&lt;";
+ break;
+ case '>' :
+ return "&gt;";
+ break;
+ case '&' :
+ return "&amp;";
+ break;
+ case '\"' :
+ return "&quot;";
+ break;
+
+// skip first byte of multibyte chracters
+ /* #ifndef _WIN32
+ case 195:
+ return string("");
+ break;
+#endif*/
+
+ default:
+ string m;
+ m += c;
+ return m;
+ }
+}
+
+/*string XmlGenerator::getNewLine(){
+ string nlStr;
+ if (currentState!=_UNKNOWN){
+ nlStr+=styleTagClose[getStyleID(currentState, currentKeywordClass)];
+ }
+ nlStr += newLineTag;
+ if (currentState!=_UNKNOWN){
+ nlStr+=styleTagOpen[getStyleID(currentState, currentKeywordClass)];
+ }
+ return nlStr;
+}
+*/
+string XmlGenerator::getMatchingOpenTag(unsigned int styleID){
+ return getOpenTag(langInfo.getKeywordClasses()[styleID]);
+}
+
+string XmlGenerator::getMatchingCloseTag(unsigned int styleID){
+ return getCloseTag(langInfo.getKeywordClasses()[styleID]);
+}
+
+}
+/***************************************************************************
+ xmlcode.h - description
+ -------------------
+ begin : Do 20.01.2005
+ copyright : (C) 2005 by Andre Simon
+ email : andre.simon1@gmx.de
+ ***************************************************************************/
+
+/***************************************************************************
+ * *
+ * 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 of the License, or *
+ * (at your option) any later version. *
+ * *
+ ***************************************************************************/
+
+#ifndef XMLGENERATOR_H
+#define XMLGENERATOR_H
+
+#include <string>
+#include <sstream>
+#include <iostream>
+
+#include "codegenerator.h"
+#include "version.h"
+
+namespace highlight {
+
+/**
+ \brief This class generates XML.
+
+ It contains information about the resulting document structure (document
+ header and footer), the colour system, white space handling and text
+ formatting attributes.
+
+* @author Andre Simon
+*/
+
+class XmlGenerator : public highlight::CodeGenerator
+ {
+ public:
+
+ /** Constructor
+ \param colourTheme Name of Colour theme to use
+ \param enc encoding name
+ \param omitEnc switch to omit encoding information
+ */
+ XmlGenerator( const string &colourTheme,const string &enc, bool omitEnc=false);
+
+ XmlGenerator();
+
+ ~XmlGenerator();
+
+ /** prints document header
+ \param title Title of the document
+ */
+ string getHeader(const string & title);
+
+ /** Prints document footer*/
+ string getFooter();
+
+ /** Prints document body*/
+ void printBody();
+
+ private:
+
+ string styleDefinitionCache, encoding;
+
+ bool omitEncoding;
+
+ string getStyleDefinition();
+
+ string formatStyleAttributes(const string &, const ElementStyle &);
+
+ /** \return escaped character*/
+ virtual string maskCharacter(unsigned char );
+
+
+// string getNewLine();
+
+ string getOpenTag(const string& );
+ string getCloseTag(const string& );
+
+ string getMatchingOpenTag(unsigned int styleID);
+ string getMatchingCloseTag(unsigned int styleID);
+ };
+
+}
+
+#endif
+/***************************************************************************
+ xslfocode.cpp - description
+ -------------------
+ begin : Do 11.12.2003
+ copyright : (C) 2003 by André Simon
+ email : andre.simon1@gmx.de
+ ***************************************************************************/
+
+/***************************************************************************
+ * *
+ * 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 of the License, or *
+ * (at your option) any later version. *
+ * *
+ ***************************************************************************/
+
+#include "xslfogenerator.h"
+
+using namespace std;
+namespace highlight {
+
+XslFoGenerator::XslFoGenerator(const string &colourTheme,
+ const string &enc,
+ bool omitEnc,
+ bool fopCompatible)
+ : CodeGenerator(colourTheme),
+ encoding(enc),
+ fopOutput(fopCompatible),
+ omitEncoding(omitEnc)
+{
+ styleTagOpen.push_back( getOpenTag(docStyle.getDefaultStyle()));
+ styleTagOpen.push_back( getOpenTag(docStyle.getStringStyle()));
+ styleTagOpen.push_back( getOpenTag(docStyle.getNumberStyle()));
+ styleTagOpen.push_back( getOpenTag(docStyle.getSingleLineCommentStyle()));
+ styleTagOpen.push_back( getOpenTag(docStyle.getCommentStyle()));
+ styleTagOpen.push_back( getOpenTag(docStyle.getEscapeCharStyle()));
+ styleTagOpen.push_back( getOpenTag(docStyle.getDirectiveStyle()));
+ styleTagOpen.push_back( getOpenTag(docStyle.getDirectiveStringStyle()));
+ styleTagOpen.push_back( getOpenTag(docStyle.getLineStyle()));
+ styleTagOpen.push_back( getOpenTag(docStyle.getSymbolStyle()));
+ snl << " <fo:block font-size=\""
+ << docStyle.getFontSize()
+ << "pt\" font-family=\"Courier\" white-space-collapse=\"false\" "
+ << "wrap-option=\"wrap\" line-height=\"12pt\" background-color=\"#"
+ << (docStyle.getBgColour().getHexRedValue())
+ << (docStyle.getBgColour().getHexGreenValue())
+ << (docStyle.getBgColour().getHexBlueValue())
+ << "\">";
+
+ for (int i=0;i<NUMBER_BUILTIN_STYLES; i++)
+ {
+ styleTagClose.push_back( "</fo:inline>");
+ }
+ if (fopOutput)
+ newLineTag ="</fo:block>\n<fo:block>";
+ else
+ newLineTag ="</fo:block>\n"+ snl.str();
+
+ spacer = " ";
+}
+
+XslFoGenerator::XslFoGenerator()
+{}
+XslFoGenerator::~XslFoGenerator()
+{}
+
+string XslFoGenerator::getOpenTag(const ElementStyle &elem)
+{
+ ostringstream s;
+ s << "<fo:inline color=\"#"
+ << (elem.getColour().getHexRedValue())
+ << (elem.getColour().getHexGreenValue())
+ << (elem.getColour().getHexBlueValue())
+ << "\"";
+ s << ( elem.isBold() ?" font-weight=\"bold\"" :"" )
+ << ( elem.isItalic() ?" font-style=\"italic\"" :"" )
+ << ( elem.isUnderline() ?" text-decoration=\"underline\"" :"" );
+ s << ">";
+ return s.str();
+}
+
+string XslFoGenerator::getHeader(const string & title)
+{
+ ostringstream os;
+ os << "<?xml version=\"1.0\"";
+ if (!omitEncoding) {
+ os << " encoding=\"" << encoding << "\"";
+ }
+ os << "?>\n<fo:root xmlns:fo=\"http://www.w3.org/1999/XSL/Format\">\n"
+ << "<fo:layout-master-set>\n"
+ << "<fo:simple-page-master master-name=\"DINA4\"\n"
+ << " page-height=\"29.7cm\"\n"
+ << " page-width=\"21cm\"\n"
+ << " margin-top=\"1cm\"\n"
+ << " margin-bottom=\"2cm\"\n"
+ << " margin-left=\"2.5cm\"\n"
+ << " margin-right=\"2.5cm\">\n"
+ << "<fo:region-body />\n"
+ << "</fo:simple-page-master>\n"
+ << "<fo:page-sequence-master master-name=\"basic\">\n"
+ << "<fo:repeatable-page-master-alternatives>\n"
+ << "<fo:conditional-page-master-reference master-reference=\"DINA4\" />\n"
+ << "</fo:repeatable-page-master-alternatives>\n"
+ << "</fo:page-sequence-master>\n"
+ << "</fo:layout-master-set>\n\n"
+ << "<fo:page-sequence master-reference=\"basic\">\n"
+ << " <fo:flow flow-name=\"xsl-region-body\">\n";
+ if (fopOutput)
+ os << snl.str()<< "<fo:block>";
+ else
+ os << snl.str();
+
+ return os.str();
+}
+
+/** gibt RTF-Text aus */
+void XslFoGenerator::printBody()
+{
+ processRootState();
+}
+
+
+string XslFoGenerator::getFooter()
+{
+ ostringstream os;
+ if (fopOutput)
+ os <<"\n</fo:block>";
+ os <<"\n</fo:block>\n </fo:flow>\n</fo:page-sequence>\n</fo:root>"<<endl
+ << "<!-- XSL-FO generated by Highlight "
+ << HIGHLIGHT_VERSION
+ << ", "
+ << HIGHLIGHT_URL
+ <<" -->\n";
+ return os.str();
+}
+
+/** Gibt RTF-Code der Sonderzeichen zurueck */
+string XslFoGenerator::maskCharacter(unsigned char c)
+{
+ switch (c)
+ {
+ case '<' :
+ return "&lt;";
+ break;
+ case '>' :
+ return "&gt;";
+ break;
+ case '&' :
+ return "&amp;";
+ break;
+ case '\"' :
+ return "&quot;";
+ break;
+
+// skip first byte of multibyte chracters
+ /*#ifndef _WIN32
+ case 195:
+ return string("");
+ break;
+#endif*/
+
+ default:
+ string m;
+ m += c;
+ return m;
+ }
+}
+
+/*string XslFoGenerator::getNewLine(){
+ string nlStr;
+
+ if (currentState!=_UNKNOWN){
+ nlStr+=styleTagClose[getStyleID(currentState, currentKeywordClass)];
+}
+ nlStr += newLineTag;
+ if (currentState!=_UNKNOWN){
+ nlStr+=styleTagOpen[getStyleID(currentState, currentKeywordClass)];
+}
+ return nlStr;
+}*/
+
+string XslFoGenerator::getMatchingOpenTag(unsigned int styleID){
+ return getOpenTag(docStyle.getKeywordStyle(langInfo.getKeywordClasses()[styleID]));
+}
+
+string XslFoGenerator::getMatchingCloseTag(unsigned int styleID){
+ return "</fo:inline>";
+}
+
+}
+/***************************************************************************
+ xslfocode.h - description
+ -------------------
+ begin : Do 11.12.2003
+ copyright : (C) 2003 by Andre Simon
+ email : andre.simon1@gmx.de
+ ***************************************************************************/
+
+/***************************************************************************
+ * *
+ * 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 of the License, or *
+ * (at your option) any later version. *
+ * *
+ ***************************************************************************/
+
+#ifndef XSLFOGENERATOR_H
+#define XSLFOGENERATOR_H
+
+
+#include <string>
+#include <sstream>
+#include <iostream>
+#include <fstream>
+
+#include "codegenerator.h"
+#include "version.h"
+
+namespace highlight {
+
+/**
+ \brief This class generates XSL-FO.
+
+ It contains information about the resulting document structure (document
+ header and footer), the colour system, white space handling and text
+ formatting attributes.
+
+* @author Andre Simon
+*/
+
+class XslFoGenerator : public highlight::CodeGenerator
+ {
+ public:
+
+ /** Constructor
+ \param colourTheme Name of Colour theme to use
+ \param enc encoding name
+ \param omitEnc switch to omit encoding information
+ \param fopCompatible Test if output should be compatible with Apache FOP 0.20.5
+ */
+ XslFoGenerator( const string &colourTheme,
+ const string &enc,
+ bool omitEnc=false,
+ bool fopCompatible=false);
+
+ XslFoGenerator();
+
+ ~XslFoGenerator();
+
+ /** prints document header
+ \param title Title of the document
+ */
+ string getHeader(const string & title);
+
+ /** Prints document footer*/
+ string getFooter();
+
+ /** Prints document body*/
+ void printBody();
+
+ private:
+ ostringstream snl;
+
+ string styleDefinition, encoding;
+ bool fopOutput, omitEncoding;
+
+ /** \return escaped character*/
+ virtual string maskCharacter(unsigned char );
+
+ string getOpenTag(const ElementStyle &);
+
+ // string getNewLine();
+
+ string getMatchingOpenTag(unsigned int styleID);
+ string getMatchingCloseTag(unsigned int styleID);
+ };
+
+}
+
+#endif
diff --git a/tests/examplefiles/example.pas b/tests/examplefiles/example.pas
new file mode 100644
index 00000000..ab11ee67
--- /dev/null
+++ b/tests/examplefiles/example.pas
@@ -0,0 +1,2708 @@
+// vim:ft=pascal
+
+unit YTools;
+
+{===============================================================================
+
+ cYcnus.YTools 1.0.3 Beta for Delphi 4+
+ by licenser and Murphy
+
+ ©2000-2003 by cYcnus
+ visit www.cYcnus.de
+
+ licenser@cYcnus.de (Heinz N. Gies)
+ murphy@cYcnus.de (Kornelius Kalnbach)
+
+ this unit is published under the terms of the GPL
+
+===============================================================================}
+
+interface
+
+uses
+ Windows, SysUtils, Classes, YTypes;
+
+const
+ BackSpace = #8;
+ Tab = #9;
+ LF = #10; //Line Feed
+ CR = #13; //Carriage Return
+ Space = #32;
+ EOLChars = [CR, LF];
+{$IFNDEF VER140}
+ sLineBreak = #13#10;
+ SwitchChars = ['/', '-'];
+{$ENDIF}
+ EOL = sLineBreak;
+ MaxCard = High(Cardinal);
+ AllChars = [#0..#255];
+ Alphabetical = ['A'..'Z', 'a'..'z'];
+ DecimalChars = ['0'..'9'];
+ AlphaNumerical = Alphabetical + DecimalChars;
+ StrangeChars = [#0..#31, #127, #129, #141..#144, #157, #158];
+
+ HexadecimalChars = DecimalChars + ['A'..'F', 'a'..'f'];
+ OctalChars = ['0'..'7'];
+ BinaryChars = ['0', '1'];
+
+ QuoteChars = ['''', '"'];
+ WildCards = ['*', '?'];
+ FileNameEnemies = WildCards + ['\', '/', ':', '<', '>', '|'];
+
+ HexChar: array[THex] of Char = (
+ '0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F');
+ LowerHexChar: array[THex] of Char = (
+ '0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f');
+ BaseNChar: array[TBaseN] of Char = (
+ '0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F','G','H',
+ 'I','J','K','L','M','N','O','P','Q','R','S','T','U','V','W','X','Y','Z');
+
+ cYcnusOverlayColor = $050001;
+
+ faFindEveryFile = faReadOnly + faHidden + faSysFile + faArchive;
+
+ platWin9x = [VER_PLATFORM_WIN32s, VER_PLATFORM_WIN32_WINDOWS];
+
+
+{ Debugging }
+procedure ClearReport(const ReportName: string);
+procedure Report(const ReportName, Text: string);
+procedure ReportFmt(const ReportName, Fmt: string; const Args: array of const);
+
+{ Params }
+procedure GetParams(Strings: TStrings); overload;
+function GetParams(const Separator: string = ' '): string; overload;
+
+function ParamNum(const S: string): Integer;
+function ParamPrefixNum(const Prefix: string): Integer;
+function Param(const S: string): Boolean;
+function ParamPrefix(const Prefix: string): Boolean;
+
+function Switch(const Switch: string; const PrefixChars: TCharSet = SwitchChars;
+ IgnoreCase: Boolean = True): Boolean;
+function GetParam(const Prefix: string = ''; const Default: string = ''): string;
+
+{ Dirs & UserName}
+function GetMyDir(FullPath: Boolean = False): string;
+function WinDir: string;
+function SysDir: string;
+function UserName: string;
+
+{ Strings & Chars}
+function FirstChar(const S: string): Char;
+function LastChar(const S: string): Char;
+
+function CharPos(C: Char; const S: string; Offset: Integer = 1): Integer; overload;
+function CharPos(C: TCharSet; const S: string; Offset: Integer = 1): Integer; overload;
+function CharPosR(C: Char; const S: string; Offset: Integer = -1): Integer;
+function PosEx(const SubStr, S: string; Offset: Integer = 1): Integer;
+function PosExText(const SubStr, S: string; Offset: Integer = 1): Integer;
+function PosExAnsiText(const SubStr, S: string; Offset: Integer = 1): Integer;
+
+function UntilChar(const S: string; Brake: Char): string; overload;
+function UntilChar(const S: string; Brake: TCharSet): string; overload;
+function UntilLastChar(const S: string; Brake: Char;
+ IgnoreNoBrake: Boolean = True): string;
+
+function FromChar(const S: string; Brake: Char): string; overload;
+function FromChar(const S: string; Brake: TCharSet): string; overload;
+function FromLastChar(const S: string; Brake: Char;
+ IgnoreNoBrake: Boolean = False): string;
+
+function BetweenChars(const S: string; Start, Finish: Char;
+ Inclusive: Boolean = False): string;
+
+function UntilStr(const S: string; Brake: string): string;
+function FromStr(const S: string; Brake: string): string;
+
+function StringWrap(const S: string; Width: Integer; const LineEnd: string = EOL): string;
+
+{ Splitting & Combining }
+function Split(const S, Separator: string; IgnoreMultiSep: Boolean = True;
+ MinCount: Integer = 0): TStrA; overload;
+procedure Split(const S, Separator: string; Strings: TStrings;
+ IgnoreMultiSep: Boolean = True); overload;
+function Split(const S: string; Separators: TCharSet;
+ IgnoreMultiSep: Boolean = True; MinCount: Integer = 0): TStrA; overload;
+
+procedure TileStr(const S: string; BrakeStart: Integer; BrakeEnd: Integer;
+ out Left, Right: string);
+
+function Join(Strings: TStrings; Separator: string = ' '): string; overload;
+function Join(StrA: TStrA; Separator: string = ' '): string; overload;
+
+function MulStr(const S: string; Count: Integer): string;
+
+{ Strings ausrichten }
+function AlignR(const S: string; Width: Integer; Filler: Char = ' '): string;
+function MaxStr(const S: string; MaxLen: Integer): string;
+
+{ Stringing }
+function TrimAll(const S: string): string;
+
+function ControlChar(C: Char): Boolean;
+function FriendlyChar(C: Char): Char;
+
+function FriendlyStr(const S: string): string; overload;
+function FriendlyStr(a: TByteA): string; overload;
+
+function Quote(const S: string; Quoter: Char = '"'): string;
+function UnQuote(const S: string): string;
+function DeQuote(const S: string): string;
+
+function StrNumerus(const Value: Integer; const Singular, Plural: string;
+ const Zero: string = '0'): string;
+
+function MakeStr(const Items: array of const; Separator: string = ''): string;
+procedure ShowText(const Items: array of const; Separator: string = '');
+
+{ Delete }
+function DeleteChars(const S: string; C: Char): string; overload;
+function DeleteChars(const S: string; C: TCharSet): string; overload;
+function ExtractChars(const S: string; C: TCharSet): string;
+
+{ Find }
+function CharCount(const S: string; C: Char): Integer;
+
+function CharIn(const S: string; C: Char): Boolean; overload;
+function CharIn(const S: string; C: TCharSet): Boolean; overload;
+
+function StrAtPos(const S: string; Pos: Integer; const Str: string): Boolean;
+function StrAtBegin(const S, Str: string): Boolean;
+function StrIn(const S, SubStr: string): Boolean; overload;
+function StrIn(A: TStrA; const S: string): Boolean; overload;
+function StrIn(SL: TStrings; const S: string): Boolean; overload;
+function StrIndex(A: TStrA; const S: string): Integer; overload;
+function StrIndex(SL: TStrings; const S: string): Integer; overload;
+
+function TextAtPos(const S: string; Pos: Integer; const Text: string): Boolean;
+function TextAtBegin(const S, Text: string): Boolean;
+function TextIn(const S, Text: string): Boolean; overload;
+function TextIn(A: TStrA; const Text: string): Boolean; overload;
+function TextIn(SL: TStrings; const Text: string): Boolean; overload;
+function TextIndex(A: TStrA; const Text: string): Integer; overload;
+function TextIndex(SL: TStrings; const Text: string): Integer; overload;
+
+{ Replace }
+function ReplaceChars(const S: string; Old, New: Char): string; overload;
+function ReplaceChars(const S: string; Old: TCharSet; New: Char): string; overload;
+
+function Replace(const S, Old, New: string): string;
+
+{ TStrings }
+function SLOfFile(const FileName: string): TStringList;
+function ContainsEmptyLines(SL: TStrings): Boolean;
+procedure DeleteEmptyLines(SL: TStrings);
+procedure DeleteCommentLines(SL: TStrings; const CommentSign: string = '//');
+procedure WriteSL(Strings: TStrings; const Prefix: string = '';
+ const Suffix: string = '');
+
+function FindLine(SL: TStrings; const S: string): Integer;
+
+procedure QuickSortSL(SL: TStringList);
+
+{ TStrA }
+function IncStrA(StrA: TStrA): Integer;
+
+{ TByteA }
+function StrOfByteA(a: TByteA): string;
+function ByteAOfStr(const S: string): TByteA;
+function ByteAOfInt(i: Integer): TByteA;
+function IntOfByteA(A: TByteA): Integer;
+function ByteAOfHex(const Hex: string): TByteA;
+
+function SameByteA(const A, B: TByteA): Boolean;
+function Reverse(a: TByteA): TByteA;
+function SaveByteA(Data: TByteA; const FileName: string; Overwrite: Boolean = True): Boolean;
+function LoadByteA(const FileName: string): TByteA;
+
+function Endian(i: Integer): Integer;
+
+{ Files }
+function SizeOfFile(const FileName: string): Integer;
+function FileEx(const FileName: string; AllowFolders: Boolean = False): Boolean;
+function LWPSolve(const Dir: string): string;
+function LWPSlash(const Dir: string): string;
+
+function ExtractDrive(const FileName: string): string;
+function ExtractPath(const FileName: string): string;
+function ExtractPrefix(const FileName: string): string;
+function ExtractSuffix(const FileName: string): string;
+
+function IsValidFileName(const FileName: string): Boolean;
+function MakeValidFileName(FileName: string; const Default: string = 'File'): string;
+
+{ Converting }
+function IsValidInteger(const S: string): Boolean;
+function IsValidCardinal(const S: string): Boolean;
+
+function StrOfBool(flag: Boolean; const TrueStr: string = 'True';
+ const FalseStr: string = 'False'): string;
+function StrOfInt(i: Integer): string;
+function CardOfStr(const S: string): Cardinal;
+
+function HexOrd(Hex: Char): THex;
+function ByteOfHex(Hex: THexByteStr): Byte;
+
+function DecOfHex(const Hex: string): string;
+function HexOfByte(b: Byte): THexByteStr;
+function HexOfCard(i: Cardinal): string; overload;
+function HexOfCard(i: Cardinal; Digits: Integer): string; overload;
+
+function PascalHexArray(a: TByteA; Name: string): string;
+
+function HexOfByteA(a: TByteA; Blocks: Integer = 1;
+ const Splitter: string = ' '): string;
+function BinOfByteA(a: TByteA; Blocks: Integer = 4;
+ const Splitter: string = ' '): string;
+
+function CardOfHex(Hex: string): Cardinal;
+function IntOfBin(Bin: string): Cardinal;
+
+function BinOfIntFill(n: cardinal; MinCount: Integer = 8): string;
+function BinOfInt(n: cardinal): string;
+
+function BaseNOfInt(I: Cardinal; B: TBaseN): string;
+function IntOfBaseN(V: string; B: TBaseN): Cardinal;
+
+{ Ranges }
+function KeepIn(i, Bottom, Top: Variant): Variant;
+function InRange(Value, Bottom, Top: Variant): Boolean;
+function InStrictRange(Value, Bottom, Top: Variant): Boolean;
+function Min(const A, B: Integer): Integer; overload;
+function Min(const A: TIntA): Integer; overload;
+function Max(const A, B: Integer): Integer; overload;
+function Max(const A: TIntA): Integer; overload;
+
+const
+ RangesSeparator = ',';
+ RangeInnerSeparator = '-';
+ RangeInfinite = '*';
+ RangeSpecialChars = [RangesSeparator, RangeInnerSeparator, RangeInfinite];
+
+function RangesOfStr(const S: string): TRanges;
+function InRanges(Ranges: TRanges; TestValue: Cardinal): Boolean;
+
+function Success(Res: Integer; ResultOnSuccess: Integer = ERROR_SUCCESS): Boolean;
+function Failure(Res: Integer; ResultOnSuccess: Integer = ERROR_SUCCESS): Boolean;
+
+function ExpandString(const S: string): string;
+
+{ Files }
+procedure DeleteFiles(const Mask: string; ScanSubDirs: Boolean = True;
+ Attributes: Integer = faFindEveryFile);
+procedure FileNew(const FileName: string);
+function DateTimeOfFileTime(const FileTime: TFileTime): TDateTime;
+
+{ FileNames }
+function GetFileNew(FileName: string; NoFloppyDrives: Boolean = True): string;
+
+{ Finding Files }
+function FindAll(Strings: TStrings; const Mask: string;
+ ScanSubDirs: Boolean = True; Attributes: Integer = faFindEveryFile;
+ FileReturn: TFileNameFunc = nil): Boolean;
+function FindAllFirst(const Mask: string; ScanSubDirs: Boolean = True;
+ Attributes: Integer = faFindEveryFile): string;
+
+function FullOSInfo: string;
+function Win32PlatformStr: string;
+function Win9x: Boolean;
+function WinNT: Boolean;
+function Win2000: Boolean;
+function WinXP: Boolean;
+
+var
+ MyDir: string = '';
+ LastSuccessRes: Integer = 0;
+
+{ Backward compatibility }
+{$IFNDEF VER130}
+function SameText(const S1, S2: string): Boolean;
+{$ENDIF}
+
+implementation
+{$IFNDEF VER140}
+uses FileCtrl;
+{$ENDIF}
+
+{$IFNDEF VER130}
+function SameText(const S1, S2: string): Boolean;
+begin
+ Result := CompareText(S1, S2) = 0;
+end;
+{$ENDIF}
+
+procedure Report(const ReportName, Text: string);
+var
+ F: TextFile;
+ FileName: string;
+begin
+ FileName := MyDir + ReportName + '.rep';
+ Assign(F, FileName);
+ try
+ if not FileExists(FileName) then
+ Rewrite(F)
+ else
+ Append(F);
+ WriteLn(F, Text);
+ finally
+ Close(F);
+ end;
+end;
+
+procedure ClearReport(const ReportName: string);
+var
+ FileName: string;
+begin
+ FileName := MyDir + ReportName + '.rep';
+ DeleteFile(FileName);
+end;
+
+procedure ReportFmt(const ReportName, Fmt: string; const Args: array of const);
+begin
+ Report(ReportName, Format(Fmt, Args));
+end;
+
+procedure GetParams(Strings: TStrings);
+var
+ P: PChar;
+ Param: string;
+
+ function GetParamStr(var P: PChar; var Param: string): Boolean;
+ var
+ Quoted: Boolean;
+ begin
+ Param := '';
+
+ repeat
+ while (P[0] <> #0) and (P[0] <= ' ') do
+ Inc(P);
+
+ Quoted := False;
+ while P[0] <> #0 do begin
+ if P[0] = '"' then begin
+ Quoted := not Quoted;
+ Inc(P);
+ Continue; end;
+ if (P[0] <= ' ') and not Quoted then
+ Break;
+ Param := Param + P[0];
+ Inc(P);
+ end;
+ until (Param <> '') or (P[0] = #0);
+
+ Result := Param <> '';
+ end;
+
+begin
+ Strings.Clear;
+ P := GetCommandLine;
+ GetParamStr(P, Param);
+ while GetParamStr(P, Param) do
+ Strings.Add(Param);
+end;
+
+function GetParams(const Separator: string = ' '): string;
+var
+ SL: TStringList;
+begin
+ SL := TStringList.Create;
+ GetParams(SL);
+ Result := Join(SL, Separator);
+ SL.Free;
+end;
+
+function Switch(const Switch: string; const PrefixChars: TCharSet = SwitchChars;
+ IgnoreCase: Boolean = True): Boolean;
+//= SysUtils.FindCmdLineSwitch
+var
+ i: Integer;
+ s: string;
+begin
+ Result := True;
+
+ for i := 1 to ParamCount do begin
+ s := ParamStr(i);
+
+ if (s <> '') and (s[1] in PrefixChars) then begin
+ //i know that always s <> '', but this is saver
+ s := Copy(s, 2, MaxInt);
+ if (s = Switch) or (IgnoreCase and (0=AnsiCompareText(s, Switch))) then
+ Exit;
+ end;
+ end;
+
+ Result := False;
+end;
+
+function ParamNum(const S: string): Integer;
+begin
+ for Result := 1 to ParamCount do
+ if 0=AnsiCompareText(ParamStr(Result), S) then
+ Exit;
+
+ Result := 0;
+end;
+
+function ParamPrefixNum(const Prefix: string): Integer;
+var
+ Len: Integer;
+begin
+ Len := Length(Prefix);
+ for Result := 1 to ParamCount do
+ if 0=AnsiCompareText(Copy(ParamStr(Result), 1, Len), Prefix) then
+ Exit;
+
+ Result := 0;
+end;
+
+function Param(const S: string): Boolean;
+begin
+ Result := ParamNum(S) > 0;
+end;
+
+function ParamPrefix(const Prefix: string): Boolean;
+begin
+ Result := ParamPrefixNum(Prefix) > 0;
+end;
+
+function GetParam(const Prefix: string = ''; const Default: string = ''): string;
+var
+ i: Integer;
+begin
+ Result := Default;
+
+ if Prefix = '' then begin
+ Result := ParamStr(1);
+ Exit; end;
+
+ i := ParamPrefixNum(Prefix);
+ if i > 0 then
+ Result := Copy(ParamStr(i), Length(Prefix) + 1, MaxInt);
+end;
+
+function GetMyDir(FullPath: Boolean = False): string;
+var
+ Buffer: array[0..260] of Char;
+begin
+ Result := '';
+ SetString(Result, Buffer, GetModuleFileName(0, Buffer, SizeOf(Buffer)));
+ if FullPath then
+ Result := GetFileNew(Result);
+ Result := ExtractPath(Result);
+end;
+
+function WinDir: string;
+var
+ Res: PChar;
+begin
+ Result := '\';
+ GetMem(Res, MAX_PATH);
+ GetWindowsDirectory(Res, MAX_PATH);
+ Result := Res + '\';
+ FreeMem(Res, MAX_PATH);
+end;
+
+function SysDir: string;
+var
+ Res: PChar;
+begin
+ Result := '\';
+ GetMem(Res, MAX_PATH);
+ GetSystemDirectory(Res, MAX_PATH);
+ Result := Res + '\';
+ FreeMem(Res, MAX_PATH);
+end;
+
+function UserName: string;
+var
+ Len: Cardinal;
+ Res: PChar;
+begin
+ Result := '';
+ GetMem(Res, MAX_PATH);
+ Len := MAX_PATH;
+ GetUserName(Res, Len);
+ Result := Res;
+ FreeMem(Res, MAX_PATH);
+end;
+
+function FirstChar(const S: string): Char;
+begin
+ if s = '' then
+ Result := #0
+ else
+ Result := s[1];
+end;
+
+function LastChar(const S: string): Char;
+begin
+ if s = '' then
+ Result := #0
+ else
+ Result := s[Length(s)];
+end;
+
+function CharPos(C: Char; const S: string; Offset: Integer = 1): Integer;
+var
+ MaxPosToSearch: Integer;
+begin
+ Result := Offset;
+ MaxPosToSearch := Length(S);
+
+ while Result <= MaxPosToSearch do begin
+ if S[Result] = C then
+ Exit;
+ Inc(Result);
+ end;
+
+ Result := 0;
+end;
+
+function CharPos(C: TCharSet; const S: string; Offset: Integer = 1): Integer;
+var
+ MaxPosToSearch: Integer;
+begin
+ Result := Offset;
+ MaxPosToSearch := Length(S);
+
+ while Result <= MaxPosToSearch do begin
+ if S[Result] in C then
+ Exit;
+ Inc(Result);
+ end;
+
+ Result := 0;
+end;
+
+function CharPosR(C: Char; const S: string; Offset: Integer = -1): Integer;
+begin
+ if Offset < 0 then
+ Result := Length(S) + 1 - Offset
+ else
+ Result := Offset;
+ if Result > Length(S) then
+ Result := Length(S);
+
+ while Result > 0 do begin
+ if S[Result] = C then
+ Exit;
+ Dec(Result);
+ end;
+end;
+
+function PosEx(const SubStr, S: string; Offset: Integer = 1): Integer;
+var
+ MaxPosToSearch, LenSubStr, i: Integer;
+begin
+ if SubStr = '' then begin
+ Result := 0;
+ Exit; end;
+
+ if Offset < 1 then
+ Result := 1
+ else
+ Result := Offset;
+
+ LenSubStr := Length(SubStr);
+ MaxPosToSearch := Length(S) - LenSubStr + 1;
+
+ while Result <= MaxPosToSearch do begin
+ if S[Result] = SubStr[1] then begin
+ i := 1;
+
+ while (i < LenSubStr)
+ and (S[Result + i] = SubStr[i + 1]) do
+ Inc(i);
+
+ if i = LenSubStr then
+ Exit;
+ end;
+ Inc(Result);
+ end;
+
+ Result := 0;
+end;
+
+function PosExText(const SubStr, S: string; Offset: Integer = 1): Integer;
+var
+ MaxPosToSearch, LenSubStr, i: Integer;
+
+ function SameChar(a, b: Char): Boolean;
+ begin
+ Result := UpCase(a) = UpCase(b)
+ end;
+
+begin
+ if SubStr = '' then begin
+ Result := 0;
+ Exit; end;
+
+ if Offset < 1 then
+ Result := 1
+ else
+ Result := Offset;
+
+ LenSubStr := Length(SubStr);
+ MaxPosToSearch := Length(S) - LenSubStr + 1;
+
+ while Result <= MaxPosToSearch do begin
+ if SameChar(S[Result], SubStr[1]) then begin
+ i := 1;
+
+ while (i < LenSubStr)
+ and (SameChar(S[Result + i], SubStr[i + 1])) do
+ Inc(i);
+
+ if i = LenSubStr then
+ Exit;
+ end;
+ Inc(Result);
+ end;
+
+ Result := 0;
+end;
+
+function PosExAnsiText(const SubStr, S: string; Offset: Integer = 1): Integer;
+var
+ MaxPosToSearch, LenSubStr, i: Integer;
+
+ function SameChar(a, b: Char): Boolean;
+ begin
+ Result := CharLower(PChar(a)) = CharLower(PChar(b));
+ end;
+
+begin
+ if SubStr = '' then begin
+ Result := 0;
+ Exit; end;
+
+ if Offset < 1 then
+ Result := 1
+ else
+ Result := Offset;
+
+ LenSubStr := Length(SubStr);
+ MaxPosToSearch := Length(S) - LenSubStr + 1;
+
+ while Result <= MaxPosToSearch do begin
+ if SameChar(S[Result], SubStr[1]) then begin
+ i := 1;
+
+ while (i < LenSubStr)
+ and (SameChar(S[Result + i], SubStr[i + 1])) do
+ Inc(i);
+
+ if i = LenSubStr then
+ Exit;
+ end;
+ Inc(Result);
+ end;
+
+ Result := 0;
+end;
+
+function UntilChar(const S: string; Brake: Char): string;
+var
+ p: Integer;
+begin
+ p := CharPos(Brake, S);
+
+ if p > 0 then
+ Result := Copy(S, 1, p - 1)
+ else
+ Result := S;
+end;
+
+function UntilChar(const S: string; Brake: TCharSet): string;
+var
+ p: Integer;
+begin
+ Result := '';
+ p := CharPos(Brake, S);
+
+ if p > 0 then
+ Result := Copy(S, 1, p - 1)
+ else
+ Result := S;
+end;
+
+function UntilLastChar(const S: string; Brake: Char;
+ IgnoreNoBrake: Boolean = True): string;
+var
+ p: Integer;
+begin
+ Result := '';
+ p := CharPosR(Brake, S);
+
+ if p > 0 then
+ Result := Copy(S, 1, p - 1)
+ else if IgnoreNoBrake then
+ Result := S;
+end;
+
+function FromChar(const S: string; Brake: Char): string;
+var
+ p: Integer;
+begin
+ Result := '';
+ p := CharPos(Brake, S);
+
+ if p > 0 then
+ Result := Copy(S, p + 1, Length(S) - p);
+end;
+
+function FromChar(const S: string; Brake: TCharSet): string;
+var
+ p: Integer;
+begin
+ Result := '';
+ p := CharPos(Brake, S);
+
+ if p > 0 then
+ Result := Copy(S, p + 1, Length(S) - p);
+end;
+
+function FromLastChar(const S: string; Brake: Char;
+ IgnoreNoBrake: Boolean = False): string;
+var
+ p: Integer;
+begin
+ Result := '';
+ p := CharPosR(Brake, S);
+
+ if p > 0 then
+ Result := Copy(S, p + 1, Length(S) - p)
+ else if IgnoreNoBrake then
+ Result := S;
+end;
+
+function BetweenChars(const S: string; Start, Finish: Char;
+ Inclusive: Boolean = False): string;
+var
+ p, fin: Integer;
+begin
+ Result := '';
+
+ p := CharPos(Start, S);
+ if p = 0 then
+ Exit;
+
+ fin := CharPos(Finish, S, p + 1);
+ if fin = 0 then
+ Exit;
+
+ if not Inclusive then begin
+ Inc(p);
+ Dec(fin);
+ end;
+
+ Result := Copy(S, p, fin - p + 1);
+end;
+
+function UntilStr(const S: string; Brake: string): string;
+var
+ p: Integer;
+begin
+ if Length(Brake) = 1 then begin
+ Result := UntilChar(S, Brake[1]);
+ Exit; end;
+
+ p := PosEx(Brake, S);
+
+ if p > 0 then
+ Result := Copy(S, 1, p - 1)
+ else
+ Result := S;
+end;
+
+function FromStr(const S: string; Brake: string): string;
+var
+ p: Integer;
+begin
+ if Length(Brake) = 1 then begin
+ Result := FromChar(S, Brake[1]);
+ Exit; end;
+
+ Result := '';
+ p := PosEx(Brake, s);
+
+ if p > 0 then begin
+ Inc(p, Length(Brake));
+ Result := Copy(S, p, Length(S) - p + 1);
+ end;
+end;
+
+function StringWrap(const S: string; Width: Integer; const LineEnd: string = EOL): string;
+var
+ i: Integer;
+begin
+ Result := '';
+ if (S = '') or (Width < 1) then
+ Exit;
+
+ i := 1;
+ while True do begin
+ Result := Result + Copy(S, i, Width);
+ Inc(i, Width);
+ if i <= Length(S) then
+ Result := Result + LineEnd
+ else
+ Exit;
+ end;
+end;
+
+function Split(const S, Separator: string; IgnoreMultiSep: Boolean = True;
+ MinCount: Integer = 0): TStrA;
+var
+ p, fin, SepLen: Integer;
+
+ procedure Add(const S: string);
+ begin
+ if IgnoreMultiSep and (S = '') then
+ Exit;
+ SetLength(Result, Length(Result) + 1);
+ Result[High(Result)] := S;
+ end;
+
+begin
+ if S = '' then begin
+ if Length(Result) < MinCount then
+ SetLength(Result, MinCount);
+ Exit; end;
+
+ Result := nil;
+ SepLen := Length(Separator);
+
+ p := 1;
+ fin := PosEx(Separator, S);
+ while fin > 0 do begin
+ Add(Copy(S, p, fin - p));
+ p := fin + SepLen;
+ fin := PosEx(Separator, S, p);
+ end;
+ Add(Copy(S, p, Length(S) - p + 1));
+
+ if Length(Result) < MinCount then
+ SetLength(Result, MinCount);
+end;
+
+procedure Split(const S, Separator: string; Strings: TStrings;
+ IgnoreMultiSep: Boolean = True);
+var
+ p, fin, SepLen: Integer;
+
+ procedure Add(const S: string);
+ begin
+ if IgnoreMultiSep and (S = '') then
+ Exit;
+ Strings.Add(S);
+ end;
+
+begin
+ if S = '' then
+ Exit;
+
+ Strings.BeginUpdate;
+ SepLen := Length(Separator);
+ p := 1;
+ fin := PosEx(Separator, S);
+ while fin > 0 do begin
+ Add(Copy(S, p, fin - p));
+ p := fin + SepLen;
+ fin := PosEx(Separator, S, p);
+ end;
+ Add(Copy(S, p, Length(S) - p + 1));
+ Strings.EndUpdate;
+end;
+
+function Split(const S: string; Separators: TCharSet;
+ IgnoreMultiSep: Boolean = True; MinCount: Integer = 0): TStrA;
+var
+ p, fin: Integer;
+
+ procedure Add(const S: string);
+ begin
+ if IgnoreMultiSep and (S = '') then
+ Exit;
+ SetLength(Result, Length(Result) + 1);
+ Result[High(Result)] := S;
+ end;
+
+begin
+ if S = '' then begin
+ if Length(Result) < MinCount then
+ SetLength(Result, MinCount);
+ Exit; end;
+
+ Result := nil;
+
+ p := 1;
+ fin := CharPos(Separators, S);
+ while fin > 0 do begin
+ Add(Copy(S, p, fin - p));
+ p := fin + 1;
+ fin := CharPos(Separators, S, p);
+ end;
+ Add(Copy(S, p, Length(S) - p + 1));
+
+ if Length(Result) < MinCount then
+ SetLength(Result, MinCount);
+end;
+
+procedure TileStr(const S: string; BrakeStart: Integer; BrakeEnd: Integer;
+ out Left, Right: string);
+begin
+ Left := Copy(S, 1, BrakeStart-1);
+ Right := Copy(S, BrakeEnd + 1, MaxInt);
+end;
+
+function Join(Strings: TStrings; Separator: string = ' '): string;
+var
+ i, imax: Integer;
+begin
+ Result := '';
+ imax := Strings.Count-1;
+ for i := 0 to imax do begin
+ Result := Result + Strings[i];
+ if i < imax then
+ Result := Result + Separator;
+ end;
+end;
+
+function Join(StrA: TStrA; Separator: string = ' '): string; overload;
+var
+ i: Integer;
+begin
+ Result := '';
+ for i := 0 to High(StrA) do begin
+ Result := Result + StrA[i];
+ if i < High(StrA) then
+ Result := Result + Separator;
+ end;
+end;
+
+function MulStr(const S: string; Count: Integer): string;
+var
+ P: PChar;
+ Len, i: Integer;
+begin
+ Result := '';
+ if Count = 0 then
+ Exit;
+
+ Len := Length(S);
+ SetLength(Result, Len * Count);
+
+ P := Pointer(Result);
+ for i := 1 to Count do begin
+ Move(Pointer(S)^, P^, Len);
+ Inc(P, Len);
+ end;
+end;
+
+function AlignR(const S: string; Width: Integer; Filler: Char = ' '): string;
+begin
+ Result := MulStr(Filler, Width - Length(S)) + S;
+end;
+
+function MaxStr(const S: string; MaxLen: Integer): string;
+var
+ Len: Integer;
+begin
+ Len := Length(S);
+ if Len <= MaxLen then begin
+ Result := S;
+ Exit end;
+
+ Result := Copy(S, 1, MaxLen - 3) + '...';
+end;
+
+function TrimAll(const S: string): string;
+var
+ i: Integer;
+begin
+ for i := 1 to Length(S) do
+ if S[i] > #32 then
+ Result := Result + S[i];
+end;
+
+function ControlChar(C: Char): Boolean;
+begin
+ Result := C in StrangeChars;
+end;
+
+function FriendlyChar(C: Char): Char;
+begin
+ case C of
+ #0: Result := '.';
+ #1..#31: Result := '?';
+ #255: Result := '#';
+ else
+ Result := C;
+ end;
+end;
+
+function FriendlyStr(const S: string): string;
+var
+ i: Integer;
+begin
+ SetLength(Result, Length(S));
+ for i := 1 to Length(S) do
+ Result[i] := FriendlyChar(S[i]);
+end;
+
+function FriendlyStr(a: TByteA): string;
+var
+ i: Integer;
+begin
+ SetLength(Result, Length(a));
+ for i := 0 to High(a) do
+ Result[i + 1] := FriendlyChar(Char(a[i]));
+end;
+
+function Quote(const S: string; Quoter: Char = '"'): string;
+begin
+ Result := S;
+
+ if FirstChar(S) <> Quoter then
+ Result := Quoter + Result;
+
+ if LastChar(S) <> Quoter then
+ Result := Result + Quoter;
+end;
+
+function DeQuote(const S: string): string;
+begin
+ Result := '';
+ if Length(S) > 2 then
+ Result := Copy(S, 2, Length(S) - 2);
+end;
+
+function UnQuote(const S: string): string;
+var
+ Start, Len: Integer;
+begin
+ Start := 1;
+ Len := Length(S);
+
+ if (S <> '') and (S[1] in ([#0..#32] + QuoteChars)) then begin
+ if (LastChar(S) = S[1]) then
+ Dec(Len);
+ Inc(Start);
+ end;
+
+ Result := Copy(S, Start, Len - Start + 1);
+end;
+
+function StrNumerus(const Value: Integer; const Singular, Plural: string;
+ const Zero: string = '0'): string;
+begin
+ if Abs(Value) = 1 then
+ Result := IntToStr(Value) + ' ' + Singular
+ else if Value = 0 then
+ Result := Zero + ' ' + Plural
+ else
+ Result := IntToStr(Value) + ' ' + Plural;
+end;
+
+function MakeStr(const Items: array of const; Separator: string = ''): string;
+const
+ BoolStrings: array[Boolean] of string = ('False', 'True');
+
+var
+ i: Integer;
+
+ function StrOfP(P: Pointer): string;
+ begin
+ if P = nil then
+ Result := '[nil]'
+ else
+ Result := '[' + IntToStr(Cardinal(P)) + ']';
+ end;
+
+ procedure Add(const S: string);
+ begin
+ Result := Result + s + Separator;
+ end;
+
+begin
+ Result := '';
+ for i := 0 to High(Items) do
+ with Items[i] do
+ case VType of
+ vtString: Add(VString^);
+ vtInteger: Add(IntToStr(VInteger));
+ vtBoolean: Add(BoolStrings[VBoolean]);
+ vtChar: Add(VChar);
+ vtPChar: Add(VPChar);
+ vtExtended: Add(FloatToStr(VExtended^));
+ vtObject: if VObject is TComponent then
+ Add(TComponent(VObject).Name)
+ else
+ Add(VObject.ClassName);
+ vtClass: Add(VClass.ClassName);
+ vtAnsiString: Add(string(VAnsiString));
+ vtCurrency: Add(CurrToStr(VCurrency^));
+ vtInt64: Add(IntToStr(VInt64^));
+ vtVariant: Add(string(VVariant^));
+
+ vtWideChar: Add(VWideChar);
+ vtPWideChar: Add(VPWideChar);
+ vtInterface: Add(StrOfP(VInterface));
+ vtPointer: Add(StrOfP(VPointer));
+ vtWideString: Add(WideString(VWideString));
+ end;
+ if Result <> '' then
+ SetLength(result, Length(Result) - Length(Separator));
+end;
+
+procedure ShowText(const Items: array of const; Separator: string = '');
+var
+ Text: string;
+begin
+ Text := MakeStr(Items, Separator);
+
+ MessageBox(0, PChar(Text), 'Info', MB_OK and MB_APPLMODAL);
+end;
+
+function DeleteChars(const S: string; C: Char): string;
+var
+ i: Integer;
+begin
+ Result := '';
+ for i := 1 to Length(S) do
+ if S[i] <> C then
+ Result := Result + S[i];
+end;
+
+function DeleteChars(const S: string; C: TCharSet): string;
+var
+ i: Integer;
+begin
+ Result := '';
+ for i := 1 to Length(S) do
+ if not (S[i] in C) then
+ Result := Result + S[i];
+end;
+
+function ExtractChars(const S: string; C: TCharSet): string;
+var
+ i: Integer;
+begin
+ Result := '';
+ for i := 1 to Length(S) do
+ if S[i] in C then
+ Result := Result + S[i];
+end;
+
+function CharCount(const S: string; C: Char): Integer;
+var
+ i: Integer;
+begin
+ Result := 0;
+ for i := 1 to Length(S) do
+ if S[i] = C then
+ Inc(Result);
+end;
+
+function StrAtPos(const S: string; Pos: Integer; const Str: string): Boolean;
+begin
+ Result := (Str <> '') and (Str = Copy(S, Pos, Length(Str)));
+end;
+
+function TextAtPos(const S: string; Pos: Integer; const Text: string): Boolean;
+begin
+ Result := (Text <> '') and SameText(Text, Copy(S, Pos, Length(Text)));
+end;
+
+function StrAtBegin(const S, Str: string): Boolean;
+begin
+ Result := StrAtPos(S, 1, Str);
+end;
+
+function TextAtBegin(const S, Text: string): Boolean;
+begin
+ Result := TextAtPos(S, 1, Text);
+end;
+
+function CharIn(const S: string; C: Char): Boolean;
+var
+ i: Integer;
+begin
+ Result := True;
+ for i := 1 to Length(S) do
+ if S[i] = C then Exit;
+ Result := False;
+end;
+
+function CharIn(const S: string; C: TCharSet): Boolean;
+var
+ i: Integer;
+begin
+ Result := False;
+ for i := 1 to Length(S) do begin
+ Result := S[i] in C;
+ if Result then
+ Exit;
+ end;
+end;
+
+function StrIn(const S, SubStr: string): Boolean;
+begin
+ Result := PosEx(SubStr, S) > 0;
+end;
+
+function StrIn(SL: TStrings; const S: string): Boolean;
+var
+ i: Integer;
+begin
+ Result := False;
+ for i := 0 to SL.Count-1 do begin
+ Result := (S = SL[i]);
+ if Result then
+ Exit;
+ end;
+end;
+
+function StrIn(A: TStrA; const S: string): Boolean;
+var
+ i: Integer;
+begin
+ Result := False;
+ for i := Low(A) to High(A) do begin
+ Result := (S = A[i]);
+ if Result then
+ Exit;
+ end;
+end;
+
+function TextIn(const S, Text: string): Boolean;
+begin
+ Result := PosExText(Text, S) > 0;
+end;
+
+function TextIn(SL: TStrings; const Text: string): Boolean;
+var
+ i: Integer;
+begin
+ Result := False;
+ for i := 0 to SL.Count-1 do begin
+ Result := SameText(Text, SL[i]);
+ if Result then
+ Exit;
+ end;
+end;
+
+function TextIn(A: TStrA; const Text: string): Boolean;
+var
+ i: Integer;
+begin
+ Result := False;
+ for i := Low(A) to High(A) do begin
+ Result := SameText(Text, A[i]);
+ if Result then
+ Exit;
+ end;
+end;
+
+function StrIndex(SL: TStrings; const S: string): Integer;
+begin
+ for Result := 0 to SL.Count-1 do
+ if S = SL[Result] then
+ Exit;
+ Result := -1;
+end;
+
+function StrIndex(A: TStrA; const S: string): Integer;
+begin
+ for Result := Low(A) to High(A) do
+ if S = A[Result] then
+ Exit;
+ Result := -1;
+end;
+
+function TextIndex(SL: TStrings; const Text: string): Integer;
+begin
+ for Result := 0 to SL.Count-1 do
+ if SameText(Text, SL[Result]) then
+ Exit;
+ Result := -1;
+end;
+
+function TextIndex(A: TStrA; const Text: string): Integer;
+begin
+ for Result := Low(A) to High(A) do
+ if SameText(Text, A[Result]) then
+ Exit;
+ Result := -1;
+end;
+
+function ReplaceChars(const S: string; Old, New: Char): string;
+var
+ i: Integer;
+begin
+ Result := S;
+ for i := 1 to Length(Result) do
+ if Result[i] = Old then
+ Result[i] := New;
+end;
+
+function ReplaceChars(const S: string; Old: TCharSet; New: Char): string;
+var
+ i: Integer;
+begin
+ Result := S;
+ for i := 1 to Length(Result) do
+ if Result[i] in Old then
+ Result[i] := New;
+end;
+
+function Replace(const S, Old, New: string): string;
+var
+ oldp, ps: Integer;
+begin
+ ps := 1;
+ Result := '';
+ while True do begin
+ oldp := ps;
+ ps := PosEx(Old, S, oldp);
+ if ps = 0 then begin
+ Result := Result + Copy(S, oldp, Length(S) - oldp + 1);
+ Exit; end;
+ Result := Result + Copy(S, oldp, ps - oldp) + New;
+ Inc(ps, Length(Old));
+ end;
+end;
+
+function SLOfFile(const FileName: string): TStringList;
+begin
+ Result := TStringList.Create;
+ if FileExists(FileName) then
+ Result.LoadFromFile(FileName);
+end;
+
+function ContainsEmptyLines(SL: TStrings): Boolean;
+begin
+ Result := StrIn(SL, '');
+end;
+
+procedure DeleteEmptyLines(SL: TStrings);
+var
+ i: Integer;
+begin
+ i := 0;
+ while i < SL.Count do begin
+ if SL[i] = '' then
+ SL.Delete(i)
+ else
+ Inc(i);
+ end;
+end;
+
+procedure DeleteCommentLines(SL: TStrings; const CommentSign: string = '//');
+var
+ i: Integer;
+begin
+ i := 0;
+ while i < SL.Count do begin
+ if (SL[i] = '') or (StrAtBegin(TrimLeft(SL[i]), CommentSign)) then
+ SL.Delete(i)
+ else
+ Inc(i);
+ end;
+end;
+
+function FindLine(SL: TStrings; const S: string): Integer;
+begin
+ for Result := 0 to SL.Count-1 do
+ if TextAtBegin(SL[Result], S) then
+ Exit;
+ Result := -1;
+end;
+
+procedure QuickSortSL(SL: TStringList);
+
+ procedure Sort(l, r: Integer);
+ var
+ i,j: Integer;
+ z,x: string;
+ begin
+ i := l;
+ j := r;
+ x := SL[(j + i) div 2];
+ repeat
+ while SL[i] < x do Inc(i);
+ while SL[j] > x do Dec(j);
+ if i <= j then begin
+ z := SL[i];
+ SL[i] := SL[j];
+ SL[j] := z;
+ Inc(i); Dec(j);
+ end;
+ until i > j;
+ if j > l then Sort(l, j);
+ if i < r then Sort(i, r);
+ end;
+
+begin
+ if SL.Count > 0 then
+ Sort(0, SL.Count-1);
+end;
+
+function IncStrA(StrA: TStrA): Integer;
+begin
+ SetLength(StrA, Length(StrA) + 1);
+ Result := High(StrA);
+end;
+
+function StrOfByteA(a: TByteA): string;
+begin
+ Result := string(Copy(a, 0, Length(a)));
+end;
+
+function ByteAOfStr(const S: string): TByteA;
+begin
+ Result := TByteA(Copy(S, 1, Length(s)));
+end;
+
+function ByteAOfInt(i: Integer): TByteA;
+begin
+ SetLength(Result, SizeOf(Integer));
+ Move(i, Pointer(Result)^, SizeOf(Integer));
+end;
+
+function IntOfByteA(A: TByteA): Integer;
+begin
+ Result := 0;
+ Move(Pointer(A)^, Result, Min(Length(A), SizeOf(Integer)));
+end;
+
+function ByteAOfHex(const Hex: string): TByteA;
+var
+ i: Integer;
+ h: string;
+begin
+ h := ExtractChars(Hex, HexadecimalChars);
+ SetLength(Result, Length(h) div 2);
+ for i := 0 to High(Result) do
+ Result[i] := ByteOfHex(Copy(h, (i shl 1) + 1, 2));
+end;
+
+function SizeOfFile(const FileName: string): Integer;
+var
+ F: file;
+begin
+ AssignFile(F, FileName);
+ {$I-}Reset(F, 1);{$I+}
+ if IOResult = 0 then begin
+ Result := FileSize(F);
+ CloseFile(F);
+ end else
+ Result := 0;
+end;
+
+function FileEx(const FileName: string; AllowFolders: Boolean = False): Boolean;
+var
+ FindData: TWin32FindData;
+begin
+ if FileName = '' then begin
+ Result := False;
+ Exit; end;
+
+ Result := (AllowFolders and DirectoryExists(FileName)) or
+ (FindFirstFile(PChar(FileName), FindData) <> INVALID_HANDLE_VALUE);
+ Result := Result and not CharIn(FileName, WildCards);
+ Result := Result and (AllowFolders
+ or ((FindData.dwFileAttributes and FILE_ATTRIBUTE_DIRECTORY) = 0));
+end;
+
+function LWPSolve(const Dir: string): string;
+begin
+ if (Dir <> '') and (Dir[Length(Dir)] = '\') then begin
+ Result := Copy(Dir, 1, Length(Dir) - 1);
+ end else
+ Result := Dir;
+end;
+
+function LWPSlash(const Dir: string): string;
+begin
+ if (Dir <> '') and (Dir[Length(Dir)] = '\') then begin
+ Result := Copy(Dir, 1, Length(Dir));
+ end else
+ Result := Dir + '\';
+end;
+
+function ExtractDrive(const FileName: string): string;
+begin
+ Result := '';
+ if (Length(FileName) >= 2) and (FileName[2] = ':') then
+ Result := UpperCase(FileName[1] + ':\');
+end;
+
+function ExtractPath(const FileName: string): string;
+var
+ p: Integer;
+begin
+ p := CharPosR('\', FileName);
+ if P > 0 then
+ Result := Copy(FileName, 1, p)
+ else
+ Result := FileName;
+end;
+
+function ExtractPrefix(const FileName: string): string;
+begin
+ Result := UntilLastChar(ExtractFileName(FileName), '.');
+end;
+
+function ExtractSuffix(const FileName: string): string;
+begin
+ Result := FromLastChar(ExtractFileName(FileName), '.');
+end;
+
+function SameByteA(const A, B: TByteA): Boolean;
+begin
+ Result := (A = B) or ((Length(A) = Length(B)) and CompareMem(A, B, Length(A)));
+end;
+
+function Reverse(A: TByteA): TByteA;
+var
+ i: Integer;
+begin
+ SetLength(Result, Length(A));
+
+ for i := 0 to High(A) do
+ Result[High(Result) - i] := A[i];
+end;
+
+function Endian(i: Integer): Integer;
+type
+ EndianArray = packed array[0..3] of Byte;
+var
+ a, b: EndianArray;
+begin
+ a := EndianArray(i);
+ b[0] := a[3];
+ b[1] := a[2];
+ b[2] := a[1];
+ b[3] := a[0];
+ Result := Integer(b);
+end;
+
+function SaveByteA(Data: TByteA; const FileName: string;
+ Overwrite: Boolean = True): Boolean;
+var
+ F: file;
+begin
+ if FileExists(FileName) and not Overwrite then begin
+ Result := False;
+ Exit end;
+
+ AssignFile(F, FileName);
+ {$I-}Rewrite(F, 1);{$I+}
+ if IOResult = 0 then begin
+ if Length(Data) > 0 then
+ BlockWrite(F, Data[0], Length(Data));
+ CloseFile(F);
+ Result := True;
+ end else
+ Result := False;
+end;
+
+function LoadByteA(const FileName: string): TByteA;
+var
+ F: file;
+begin
+ AssignFile(F, FileName);
+ {$I-}Reset(F, 1);{$I+}
+ if IOResult = 0 then begin
+ SetLength(Result, FileSize(F));
+ if Length(Result) > 0 then
+ BlockRead(F, Result[0], FileSize(F));
+ CloseFile(F);
+ end else
+ SetLength(Result, 0);
+end;
+
+function IsValidFileName(const FileName: string): Boolean;
+begin
+ Result := (FileName <> '') and not CharIn(FileName, FileNameEnemies)
+ and CharIn(Trim(FileName), AllChars - ['.']);
+end;
+
+function MakeValidFileName(FileName: string; const Default: string = 'File'): string;
+begin
+ if FileName = '' then
+ FileName := Default;
+
+ if CharIn(FileName, FileNameEnemies) then
+ Result := ReplaceChars(FileName, FileNameEnemies, '_')
+ else if not CharIn(Trim(FileName), AllChars - ['.']) then
+ Result := Default
+ else
+ Result := FileName;
+end;
+
+function IsValidInteger(const S: string): Boolean;
+{const
+ LowInt = '2147483648';
+ HighInt = '2147483647';
+var
+ len, RealLen, i, o: Integer;
+ c: Char;
+begin
+ Result := False;
+ if S = '' then
+ Exit;
+
+ len := Length(S);
+ o := 1;
+
+ if S[1] = '-' then begin
+ if len = 1 then
+ Exit;
+ Inc(o);
+ while (o <= len) and (S[o] = '0') do
+ Inc(o);
+ if o > len then
+ Exit;
+ if o < len then begin
+ RealLen := len - o + 1;
+ if RealLen > Length(LowInt) then
+ Exit
+ else if RealLen = Length(LowInt) then begin
+ for i := 1 to Length(LowInt) do begin
+ c := S[i + o - 1];
+ if (c < '0') or (c > LowInt[i]) then
+ Exit;
+ if c in ['0'..Char((Byte(LowInt[i])-1))] then
+ Break;
+ end;
+ Inc(o, i);
+ end;
+ end;
+ end else begin
+ while (o <= len) and (S[o] = '0') do
+ Inc(o);
+ if o <= len then begin
+ RealLen := len - o + 1;
+ if RealLen > Length(HighInt) then
+ Exit
+ else if RealLen = Length(HighInt) then begin
+ for i := 1 to Length(HighInt) do begin
+ c := S[i + o - 1];
+ if (c < '0') or (c > HighInt[i]) then
+ Exit;
+ if c in ['0'..Char((Byte(HighInt[i])-1))] then
+ Break;
+ end;
+ Inc(o, i);
+ end;
+ end;
+ end;
+
+ for i := o to len do
+ if not (S[i] in ['0'..'9']) then
+ Exit;
+
+ Result := True; }
+var
+ i: Int64;
+begin
+ i := StrToInt64Def(S, High(Int64));
+ Result := (i >= Low(Integer)) and (i <= High(Integer));
+end;
+
+function IsValidCardinal(const S: string): Boolean;
+{const
+ HighCard = '4294967295';
+var
+ len, RealLen, i, o: Integer;
+begin
+ Result := False;
+ if S = '' then
+ Exit;
+
+ len := Length(S);
+ o := 1;
+
+ while (o <= len) and (S[o] = '0') do
+ Inc(o);
+ if o <= len then begin
+ RealLen := len - o + 1;
+ if RealLen > Length(HighCard) then
+ Exit
+ else if RealLen = Length(HighCard) then begin
+ for i := 1 to Length(HighCard) do begin
+ if S[i + o - 1] > HighCard[i] then
+ Exit;
+ if S[i + o - 1] in ['0'..Char((Byte(HighCard[i])-1))] then
+ Break;
+ end;
+ Inc(o, i);
+ end;
+ end;
+
+ for i := o to len do
+ if not (S[i] in ['0'..'9']) then
+ Exit;
+
+ Result := True; }
+var
+ i: Int64;
+begin
+ i := StrToInt64Def(S, -1);
+ Result := (i >= 0) and (i <= High(Cardinal));
+end;
+
+function StrOfBool(flag: Boolean; const TrueStr: string = 'True';
+ const FalseStr: string = 'False'): string;
+begin
+ if Flag then
+ Result := TrueStr
+ else
+ Result := FalseStr;
+end;
+
+function StrOfInt(i: Integer): string;
+begin
+{ if i = 0 then begin
+ Result := '0';
+ Exit end;
+
+ while i > 0 do begin
+ Result := Char(Byte('0') + (i mod 10)) + Result;
+ i := i div 10;
+ end;}
+ Result := IntToStr(i);
+end;
+
+function CardOfStr(const S: string): Cardinal;
+var
+ Res: Int64;
+begin
+ Res := StrToInt64Def(S, -1);
+ if Res > High(Cardinal) then
+ Res := High(Cardinal)
+ else if Res < 0 then
+ Res := 0;
+ Result := Cardinal(Res);
+end;
+
+function HexOrd(Hex: Char): THex;
+begin
+ case Hex of
+ '0'..'9':
+ Result := Byte(Hex) - 48;
+ 'A'..'F':
+ Result := Byte(Hex) - 55;
+ 'a'..'f':
+ Result := Byte(Hex) - 87;
+ else
+ Result := 0;
+ end;
+end;
+
+function ByteOfHex(Hex: THexByteStr): Byte;
+begin
+ Result := (HexOrd(Hex[1]) shl 4) + HexOrd(Hex[2]);
+end;
+
+function DecOfHex(const Hex: string): string;
+begin
+ Result := IntToStr(CardOfHex(Hex));
+end;
+
+function HexOfByte(b: Byte): THexByteStr;
+begin
+ Result := HexChar[(b and $F0) shr 4]
+ + HexChar[ b and $0F ];
+end;
+
+{function HexOfCard2(c: Cardinal): string;
+var
+ Data: array[0..(1 shl 4) - 1] of Char;
+ i: Integer;
+begin
+ for i := 0 to (1 shl 4) - 1 do
+ if i < 10 then
+ Data[i] := Char(Ord('0') + i)
+ else
+ Data[i] := Char(Ord('A') + i - 10);
+
+ Result := Data[(c and (((1 shl (1 shl 2)) - 1) shl (7 shl 2))) shr (7 shl 2)]
+ + Data[(c and (((1 shl (1 shl 2)) - 1) shl (6 shl 2))) shr (6 shl 2)]
+ + Data[(c and (((1 shl (1 shl 2)) - 1) shl (5 shl 2))) shr (5 shl 2)]
+ + Data[(c and (((1 shl (1 shl 2)) - 1) shl (4 shl 2))) shr (4 shl 2)]
+ + Data[(c and (((1 shl (1 shl 2)) - 1) shl (3 shl 2))) shr (3 shl 2)]
+ + Data[(c and (((1 shl (1 shl 2)) - 1) shl (2 shl 2))) shr (2 shl 2)]
+ + Data[(c and (((1 shl (1 shl 2)) - 1) shl (1 shl 2))) shr (1 shl 2)]
+ + Data[(c and (((1 shl (1 shl 2)) - 1) shl (0 shl 2))) shr (0 shl 2)];
+end; }
+
+function HexOfCard(i: Cardinal): string;
+var
+ a: Cardinal;
+begin
+ Result := '';
+ while i > 0 do begin
+ a := i and $F;
+ Result := HexChar[a] + Result;
+ i := i shr 4;
+ end;
+end;
+
+function HexOfCard(i: Cardinal; Digits: Integer): string;
+var
+ a: Cardinal;
+begin
+ Result := '';
+ while i > 0 do begin
+ a := i and $F;
+ Result := HexChar[a] + Result;
+ i := i shr 4;
+ end;
+ Result := MulStr('0', Digits - Length(Result)) + Result;
+end;
+
+function PascalHexArray(a: TByteA; Name: string): string;
+var
+ i, len: Integer;
+begin
+ Result := 'const' + EOL +
+ ' ' + Name + ': array[0..' + IntToStr(High(a)) + '] of Byte = (';
+
+ len := Length(a);
+ for i := 0 to len-1 do begin
+ if (i mod 19) = 0 then
+ Result := Result + EOL + ' ' + ' ';
+ Result := Result + '$' + HexOfByte(a[i]);
+ if i < len-1 then
+ Result := Result + ',';
+ end;
+ Result := Result + EOL + ' );';
+end;
+
+function HexOfByteA(a: TByteA; Blocks: Integer = 1;
+ const Splitter: string = ' '): string;
+var
+ i: Integer;
+begin
+ Result := '';
+
+ if Blocks > 0 then
+ for i := 0 to High(a) do begin
+ Result := Result + HexOfByte(a[i]);
+ if i < High(a) then
+ if ((i+1) mod Blocks) = 0 then
+ Result := Result + Splitter;
+ end
+ else
+ for i := 0 to High(a) do
+ Result := Result + HexOfByte(a[i]);
+end;
+
+function BinOfByteA(a: TByteA; Blocks: Integer = 4;
+ const Splitter: string = ' '): string;
+var
+ i, max: Integer;
+ Bit: Boolean;
+begin
+ Result := '';
+
+ if Blocks > 0 then begin
+ max := 8 * (High(a)) + 7;
+ for i := 0 to max do begin
+ Bit := 7-(i mod 8) in TBitSet(a[i div 8]);
+ Result := Result + Char(Byte('0') + Byte(Bit));
+ if i < max then
+ if ((i+1) mod Blocks) = 0 then
+ Result := Result + Splitter;
+ end;
+ end else
+ for i := 0 to High(a) do
+ Result := Result + Char(Byte('0') + a[i] shr (i and 8));
+end;
+
+function CardOfHex(Hex: string): Cardinal;
+var
+ i: Integer;
+begin
+ Result := 0;
+ Hex := Copy(ExtractChars(Hex, HexadecimalChars), 1, 8);
+
+ for i := 1 to Length(Hex) do
+ if Hex[i] <> '0' then
+ Inc(Result, HexOrd(Hex[i]) shl ((Length(Hex) - i) shl 2));
+end;
+
+function IntOfBin(Bin: string): Cardinal;
+var
+ i: Integer;
+begin
+ Result := 0;
+ Bin := Copy(ExtractChars(Bin, BinaryChars), 1, 32);
+
+ for i := Length(Bin) downto 1 do
+ if Bin[i] = '1' then
+ Inc(Result, 1 shl (Length(Bin) - i));
+end;
+
+function BinOfInt(n: Cardinal): string;
+var
+ a: Integer;
+begin
+ if n = 0 then begin
+ Result := '0';
+ exit; end;
+
+ Result := '';
+ while n > 0 do begin
+ a := n and 1;
+ Result := Char(a + Byte('0')) + Result;
+ n := n shr 1;
+ end;
+end;
+
+function BinOfIntFill(n: Cardinal; MinCount: Integer = 8): string;
+var
+ a: Integer;
+begin
+ if n = 0 then begin
+ Result := MulStr('0', MinCount);
+ Exit; end;
+
+ Result := '';
+ while n > 0 do begin
+ a := n and 1;
+ Result := Char(a + Byte('0')) + Result;
+ n := n shr 1;
+ end;
+ Result := MulStr('0', MinCount - Length(Result)) + Result;
+end;
+
+function BaseNOfInt(I: Cardinal; B: TBaseN): string;
+var
+ a: Integer;
+begin
+ if (B < 2) or (i = 0) then begin
+ Result := '0';
+ Exit; end;
+
+ Result := '';
+ while i > 0 do begin
+ a := i mod B;
+ Result := BaseNChar[a] + Result;
+ i := i div B;
+ end;
+end;
+
+function IntOfBaseN(V: string; B: TBaseN): Cardinal;
+var
+ i: Integer;
+ F: Cardinal;
+ c: Byte;
+begin
+ Result := 0;
+ V := TrimAll(V);
+ F := 1;
+ for i := Length(V) downto 1 do begin
+ c := Byte(UpCase(V[i]));
+ case Char(c) of
+ '0'..'9': c := c - 48;
+ 'A'..'Z': c := c - 55;
+ end;
+ if c < B then
+ Result := Result + Byte(c) * F;
+ F := F * B;
+ end;
+end;
+
+function KeepIn(i, Bottom, Top: Variant): Variant;
+begin
+ Result := i;
+ if Result > Top then
+ Result := Top
+ else if Result < Bottom then
+ Result := Bottom;
+end;
+
+function InRange(Value, Bottom, Top: Variant): Boolean;
+begin
+ Result := (Value >= Bottom) and (Value <= Top);
+end;
+
+function InStrictRange(Value, Bottom, Top: Variant): Boolean;
+begin
+ Result := (Value > Bottom) and (Value < Top);
+end;
+
+function Min(const A, B: Integer): Integer;
+begin
+ if A < B then
+ Result := A
+ else
+ Result := B;
+end;
+
+function Min(const A: TIntA): Integer;
+var
+ i: Integer;
+begin
+ Result := 0;
+ if Length(A) = 0 then
+ Exit;
+
+ Result := A[0];
+ for i := 1 to High(A) do
+ if A[i] < Result then
+ Result := A[i];
+end;
+
+function Max(const A, B: Integer): Integer;
+begin
+ if A > B then
+ Result := A
+ else
+ Result := B;
+end;
+
+function Max(const A: TIntA): Integer;
+var
+ i: Integer;
+begin
+ Result := 0;
+ if Length(A) = 0 then
+ Exit;
+
+ Result := A[0];
+ for i := 1 to High(A) do
+ if A[i] > Result then
+ Result := A[i];
+end;
+
+function RangesOfStr(const S: string): TRanges;
+var
+ SL: TStringList;
+ r, b, t: string;
+ i, p: Integer;
+
+ function TryStrToCard(const S: string; out Value: Cardinal): Boolean;
+ var
+ E: Integer;
+ begin
+ Val(S, Value, E);
+ Result := E = 0;
+ end;
+
+begin
+ Result := nil;
+ SL := TStringList.Create;
+ try
+ Split(S, RangesSeparator, SL);
+ SetLength(Result, SL.Count);
+ for i := 0 to SL.Count-1 do begin
+ r := SL[i];
+ with Result[i] do begin
+ p := CharPos(RangeInnerSeparator, r);
+ Simple := p = 0; // no '-' found
+ if Simple then begin
+ if r = RangeInfinite then begin // * --> *-*
+ Simple := False;
+ Bottom := Low(Bottom);
+ Top := High(Top);
+ end else if not TryStrToCard(r, Value) then
+ Break;
+
+ end else begin
+ TileStr(r, p, p, b, t);
+
+ if b = RangeInfinite then
+ Bottom := Low(Bottom)
+ else if not TryStrToCard(b, Bottom) then
+ Break;
+
+ if t = RangeInfinite then
+ Top := High(Top)
+ else if not TryStrToCard(t, Top) then
+ Break;
+ if Bottom > Top then begin
+ p := Bottom; Bottom := Top; Top := p;
+ end;
+ end;
+ end;
+ end;
+
+ if i <> SL.Count then
+ Result := nil;
+
+ finally
+ SL.Free;
+ end;
+end;
+
+function InRanges(Ranges: TRanges; TestValue: Cardinal): Boolean;
+var
+ i: Integer;
+begin
+ Result := True;
+
+ for i := 0 to High(Ranges) do
+ with Ranges[i] do
+ if Simple then begin
+ if TestValue = Value then
+ Exit;
+ end else begin
+ if InRange(TestValue, Bottom, Top) then
+ Exit;
+ end;
+
+ Result := False;
+end;
+
+procedure WriteSL(Strings: TStrings; const Prefix: string = '';
+ const Suffix: string = '');
+var
+ i: Integer;
+begin
+ for i := 0 to Strings.Count-1 do
+ WriteLn(Prefix + Strings[i] + Suffix);
+end;
+
+function Success(Res: Integer; ResultOnSuccess: Integer = ERROR_SUCCESS): Boolean;
+begin
+ Result := (Res = ResultOnSuccess);
+ LastSuccessRes := Res;
+end;
+
+function Failure(Res: Integer; ResultOnSuccess: Integer = ERROR_SUCCESS): Boolean;
+begin
+ Result := not Success(Res, ResultOnSuccess);
+end;
+
+function ExpandString(const S: string): string;
+var
+ Len: Integer;
+ P, Res: PChar;
+begin
+ Result := '';
+ P := PChar(S);
+ Len := ExpandEnvironmentStrings(P, nil, 0);
+ if Len = 0 then
+ Exit;
+
+ GetMem(Res, Len);
+ ExpandEnvironmentStrings(P, Res, Len);
+
+ Result := Res;
+ FreeMem(Res, Len);
+end;
+
+function FindAll(Strings: TStrings; const Mask: string;
+ ScanSubDirs: Boolean = True; Attributes: Integer = faFindEveryFile;
+ FileReturn: TFileNameFunc = nil): Boolean;
+var
+ Path, FileName: string;
+
+ procedure ScanDir(const Path, FileName: string);
+ var
+ PSR: TSearchRec;
+ Res: Integer;
+
+ procedure Add(const S: string);
+ begin
+ if S <> '' then
+ Strings.Add(S);
+ end;
+
+ begin
+ Res := FindFirst(Path + FileName, Attributes, PSR);
+ while Success(Res, 0) do begin
+ if Assigned(FileReturn) then
+ Add(FileReturn(Path + PSR.Name))
+ else
+ Add(Path + PSR.Name);
+ Res := FindNext(PSR);
+ end;
+ FindClose(PSR);
+ if not ScanSubDirs then
+ Exit;
+
+ Res := FindFirst(Path + '*', faDirectory, PSR);
+ while Success(Res, 0) do begin
+ if (PSR.Attr and faDirectory > 0)
+ and (PSR.Name <> '.') and (PSR.Name <> '..') then
+ ScanDir(Path + PSR.Name + '\', FileName);
+ Res := FindNext(PSR);
+ end;
+ FindClose(PSR);
+ end;
+
+begin
+ Strings.Clear;
+ Path := ExtractPath(Mask);
+ FileName := ExtractFileName(Mask);
+ ScanDir(Path, FileName);
+ Result := Strings.Count > 0;
+end;
+
+function FindAllFirst(const Mask: string; ScanSubDirs: Boolean = True;
+ Attributes: Integer = faFindEveryFile): string;
+var
+ Path, FileName: string;
+
+ function ScanDir(const Path, FileName: string): Boolean;
+ var
+ PSR: TSearchRec;
+ Res: Integer;
+ begin
+ Result := False;
+ if Success(FindFirst(Path + FileName, Attributes, PSR), 0) then begin
+ FindAllFirst := Path + PSR.Name;
+ Result := True;
+ FindClose(PSR);
+ Exit; end;
+ if not ScanSubDirs then
+ Exit;
+
+ Res := FindFirst(Path + '*', faDirectory, PSR);
+ while not Result and Success(Res, 0) do begin
+ if (PSR.Attr and faDirectory > 0)
+ and (PSR.Name <> '.') and (PSR.Name <> '..') then
+ Result := ScanDir(Path + PSR.Name + '\', FileName);
+ Res := FindNext(PSR);
+ end;
+ FindClose(PSR);
+ end;
+begin
+ Result := '';
+ Path := ExtractPath(Mask);
+ FileName := ExtractFileName(Mask);
+ ScanDir(Path, FileName);
+end;
+
+procedure DeleteFiles(const Mask: string; ScanSubDirs: Boolean = True;
+ Attributes: Integer = faFindEveryFile);
+var
+ Path, FileName: string;
+
+ procedure ScanDir(const Path, FileName: string);
+ var
+ PSR: TSearchRec;
+ Res: Integer;
+
+ procedure TryDeleteFile(const FileName: string);
+ begin
+ try
+ DeleteFile(Path + PSR.Name);
+ except
+ end;
+ end;
+
+ begin
+ Res := FindFirst(Path + FileName, Attributes, PSR);
+ while Success(Res, 0) do begin
+ TryDeleteFile(Path + PSR.Name);
+ Res := FindNext(PSR);
+ end;
+ FindClose(PSR);
+ if not ScanSubDirs then
+ Exit;
+
+ Res := FindFirst(Path + '*', faDirectory, PSR);
+ while Success(Res, 0) do begin
+ if (PSR.Attr and faDirectory > 0)
+ and (PSR.Name <> '.') and (PSR.Name <> '..') then begin
+ ScanDir(Path + PSR.Name + '\', FileName);
+ TryDeleteFile(Path + PSR.Name);
+ end;
+ Res := FindNext(PSR);
+ end;
+ FindClose(PSR);
+ end;
+begin
+ Path := ExtractPath(Mask);
+ FileName := ExtractFileName(Mask);
+ ScanDir(Path, FileName);
+end;
+
+function GetFileNew(FileName: string; NoFloppyDrives: Boolean = True): string;
+var
+ Drive: string;
+ pf, pd, Len: Integer;
+ PSR: TSearchRec;
+begin
+ Result := '';
+ FileName := Trim(FileName);
+ if Length(FileName) < 2 then
+ Exit;
+
+ Drive := ExtractDrive(FileName);
+ if not DirectoryExists(Drive) then
+ Exit;
+
+ if NoFloppyDrives and (Drive[1] in ['A', 'B']) then
+ Exit;
+
+ Len := Length(FileName);
+ Result := Drive;
+ pf := Length(Drive) + 1;
+ while pf <= Len do begin
+ if FileName[pf] = '\' then begin
+ Result := Result + '\';
+ Inc(pf);
+ Continue; end;
+
+ pd := CharPos('\', FileName, pf);
+ if pd = 0 then begin
+ if 0=FindFirst(Result + Copy(FileName, pf, MaxInt), faFindEveryFile, PSR) then begin
+ Result := Result + PSR.Name;
+ Break; end else begin
+ FindClose(PSR);
+ if 0=FindFirst(Result + Copy(FileName, pf, MaxInt), faDirectory, PSR) then
+ Result := Result + PSR.Name + '\'
+ else
+ Result := '';
+ FindClose(PSR);
+ if Result = '' then
+ Break;
+ end;
+ end;
+
+ if 0=FindFirst(Result + Copy(FileName, pf, pd - pf), faDirectory, PSR) then
+ Result := Result + PSR.Name + '\'
+ else
+ Result := '';
+ FindClose(PSR);
+ if Result = '' then
+ Break;
+
+ pf := pd + 1;
+ end;
+
+ if (Result <> '') and not FileEx(Result, True) then
+ Result := '';
+end;
+
+function DateTimeOfFileTime(const FileTime: TFileTime): TDateTime;
+var
+ LocalFileTime: TFileTime;
+ Res: Integer;
+begin
+ Result := 0;
+
+ FileTimeToLocalFileTime(FileTime, LocalFileTime);
+ if not FileTimeToDosDateTime(LocalFileTime, LongRec(Res).Hi,
+ LongRec(Res).Lo) then
+ Res := -1;
+
+ if (Res = -1) or (Res = 0) then
+ Exit;
+ try
+ Result := FileDateToDateTime(Res);
+ except
+ end;
+end;
+
+procedure FileNew(const FileName: string);
+var
+ Handle: Integer;
+begin
+ Handle := FileCreate(FileName);
+ FileClose(Handle);
+end;
+
+function Win32PlatformStr: string;
+const
+ PlatformStrings: array[VER_PLATFORM_WIN32s..VER_PLATFORM_WIN32_NT] of string =
+ ('VER_PLATFORM_WIN32s', 'VER_PLATFORM_WIN32_WINDOWS', 'VER_PLATFORM_WIN32_NT');
+begin
+ Result := PlatformStrings[Win32Platform];
+end;
+
+function FullOSInfo: string;
+begin
+ Result := Format(
+ 'Platform: %s' + EOL +
+ 'Version: %d.%d Build %d' + EOL +
+ 'CSD: %s',
+ [
+ Win32PlatformStr,
+ Win32MajorVersion, Win32MinorVersion, Win32BuildNumber,
+ Win32CSDVersion
+ ]
+ );
+end;
+
+function Win9x: Boolean;
+begin
+ Result := Win32Platform = VER_PLATFORM_WIN32_WINDOWS;
+end;
+
+function WinNT: Boolean;
+begin
+ Result := Win32Platform = VER_PLATFORM_WIN32_NT;
+end;
+
+function Win2000: Boolean;
+begin
+ Result := (Win32Platform = VER_PLATFORM_WIN32_NT)
+ and (Win32MajorVersion = 4);
+end;
+
+function WinXP: Boolean;
+begin
+ Result := Win32MajorVersion >= 5;
+end;
+
+initialization
+ MyDir := GetMyDir;
+
+end.
+
+unit FifoStream;
+
+interface
+
+uses Classes, windows, Dialogs;
+
+const
+ DefaultChunksize = 32768; // 32kb per chunk as default.
+
+type
+ PMemChunk = ^TMemChunk;
+ TMemChunk = record
+ Filled: Longword;
+ Read: Longword;
+ Data: pointer;
+ end;
+
+ TFifo = class
+ private
+ FBuffers: TList;
+ FChunksize: Longword;
+ FCritSect: TRTLCriticalSection;
+ FIsWinNT: boolean;
+ FBytesInFifo: LongWord;
+ protected
+ function GetBytesInFifo: LongWord;
+ public
+ constructor Create;
+ destructor Destroy; override;
+ procedure Write(Data: pointer; Size: LongWord);
+ procedure Read(Buff: pointer; var ReqSize: LongWord);
+ procedure PeekData(Buff: pointer; var ReqSize: LongWord);
+ published
+ property BytesInFifo: LongWord read FBytesInFifo;
+ end;
+
+implementation
+
+constructor TFifo.Create;
+begin
+ inherited;
+ FBuffers := TList.Create;
+ // set default chunksize...
+ FChunksize := DefaultChunksize;
+ InitializeCriticalSection(FCritSect);
+end;
+
+destructor TFifo.Destroy;
+var
+ I: Integer;
+begin
+ EnterCriticalSection(FCritSect);
+ for I := 0 to FBuffers.count - 1 do
+ begin
+ FreeMem(PMemChunk(Fbuffers[I]).Data);
+ Dispose(PMemChunk(Fbuffers[I]));
+ end;
+ FBuffers.Clear;
+ FBuffers.Free;
+ LeaveCriticalSection(FCritSect);
+
+ DeleteCriticalSection(FCritSect);
+ inherited;
+end;
+
+function TFifo.GetBytesInFifo: LongWord;
+begin
+ Result := 0;
+ if FBuffers.Count = 0 then
+ begin
+ exit;
+ end
+ else
+ begin
+ if FBuffers.Count > 1 then
+ Inc(Result, (FBuffers.Count - 1) * FChunkSize);
+ Inc(Result, PMemChunk(FBuffers[Fbuffers.Count - 1]).Filled);
+ Dec(Result, PMemChunk(FBuffers[0]).Read);
+ end;
+end;
+
+procedure TFifo.Write(Data: pointer; Size: LongWord);
+var
+ Privpointer: pointer;
+ PrivSize: LongWord;
+ Chunk: PMemChunk;
+ PosInChunk: pointer;
+begin
+ if LongWord(Data) = 0 then
+ begin
+ // null pointer? somebody is trying to fool us, get out...
+ Exit;
+ end;
+ EnterCriticalSection(FCritSect);
+ PrivPointer := Data;
+ PrivSize := 0;
+ // are already buffers there?
+ if FBuffers.count > 0 then
+ begin
+ // is the last one of them not completely filled?
+ if PMemChunk(FBuffers[FBuffers.count - 1]).filled < FChunksize then
+ // not completely filled, so fill up the buffer.
+ begin
+ Chunk := PMemChunk(FBuffers[FBuffers.count - 1]);
+ // fetch chunkdata.
+ PosInChunk := Chunk.Data;
+ // move to current fill pos...
+ Inc(LongWord(PosInChunk), Chunk.Filled);
+ // can we fill the chunk completely?
+ if Size > FChunksize - Chunk.Filled then
+ begin
+ // yes we can.
+ Move(PrivPointer^, PosInChunk^, FChunksize - Chunk.Filled);
+ Inc(PrivSize, FChunksize - Chunk.Filled);
+ Inc(LongWord(PrivPointer), FChunksize - Chunk.Filled);
+ Chunk.Filled := FChunkSize;
+ end
+ else
+ // we have to less data for filling the chunk completely,
+ // just put everything in.
+ begin
+ Move(PrivPointer^, PosInChunk^, Size);
+ Inc(PrivSize, Size);
+ Inc(Chunk.Filled, Size);
+ end;
+ end;
+ end;
+ // as long as we have remaining stuff put it into new chunks.
+ while (PrivSize < Size) do
+ begin
+ new(Chunk);
+ GetMem(Chunk.Data, FChunksize);
+ Chunk.Read := 0;
+ // can we fill an entire chunk with the remaining data?
+ if Privsize + FChunksize < Size then
+ begin
+ // yes we can, so put the stuff in.
+ Move(Privpointer^, Chunk.Data^, FChunksize);
+ Inc(LongWord(PrivPointer), FChunksize);
+ Inc(PrivSize, FChunksize);
+ Chunk.Filled := FChunksize;
+ end
+ else // we have to less data to fill the entire chunk, just put the remaining stuff in.
+ begin
+ Move(Privpointer^, Chunk.Data^, Size - Privsize);
+ Chunk.Filled := Size - Privsize;
+ Inc(PrivSize, Size - Privsize);
+ end;
+ Fbuffers.Add(Chunk);
+ end;
+ if Size <> Privsize then
+ Showmessage('miscalculation in TFifo.write');
+ FBytesInFifo := GetBytesInFifo;
+ LeaveCriticalSection(FCritSect);
+end;
+
+procedure TFifo.Read(Buff: pointer; var ReqSize: LongWord);
+var
+ PrivSize: Integer;
+ Privpos: pointer;
+ Chunk: PMemChunk;
+ ChunkPos: pointer;
+begin
+ if LongWord(Buff) = 0 then
+ begin
+ // null pointer? somebody is trying to fool us, get out...
+ Exit;
+ end;
+ EnterCriticalSection(FCritSect);
+ PrivSize := 0;
+ Privpos := Buff;
+ while FBuffers.Count > 0 do
+ begin
+ Chunk := PMemChunk(FBuffers[0]);
+ ChunkPos := Chunk.data;
+ Inc(LongWord(ChunkPos), Chunk.Read);
+ // does the remaining part of the chunk fit into the buffer?
+ if PrivSize + (Chunk.Filled - Chunk.read) < ReqSize then
+ begin // yep, it fits
+ Move(ChunkPos^, Privpos^, Chunk.Filled - Chunk.read);
+ Inc(PrivSize, Chunk.Filled - Chunk.read);
+ FreeMem(Chunk.Data);
+ Dispose(Chunk);
+ FBuffers.Delete(0);
+ end
+ else // remaining part didn't fit, get as much as we can and increment the
+ // read attribute.
+ begin
+ Move(ChunkPos^, Privpos^, ReqSize - PrivSize);
+ Inc(Chunk.read, ReqSize - PrivSize);
+ Inc(PrivSize, ReqSize - PrivSize);
+ // as we filled the buffer, we'll have to break here.
+ break;
+ end;
+ end;
+ FBytesInFifo := GetBytesInFifo;
+ LeaveCriticalSection(FCritSect);
+ ReqSize := PrivSize;
+end;
+
+// read Data from Stream without removing it from the Stream...
+
+procedure TFifo.PeekData(Buff: pointer; var ReqSize: LongWord);
+var
+ PrivSize: Integer;
+ Privpos: pointer;
+ Chunk: PMemChunk;
+ ChunkPos: pointer;
+ ChunkNr: Integer;
+begin
+ if LongWord(Buff) = 0 then
+ begin
+ // null pointer? somebody is trying to fool us, get out...
+ Exit;
+ end;
+ EnterCriticalSection(FCritSect);
+ PrivSize := 0;
+ Privpos := Buff;
+ ChunkNr := 0;
+ while FBuffers.Count > ChunkNr do
+ begin
+ Chunk := PMemChunk(FBuffers[ChunkNr]);
+ ChunkPos := Chunk.data;
+ Inc(LongWord(ChunkPos), Chunk.Read);
+ // does the remaining part of the chunk fit into the buffer?
+ if PrivSize + (Chunk.Filled - Chunk.read) < ReqSize then
+ begin // yep, it fits
+ Move(ChunkPos^, Privpos^, Chunk.Filled - Chunk.read);
+ Inc(PrivSize, Chunk.Filled - Chunk.read);
+ Inc(ChunkNr);
+ end
+ else // remaining part didn't fit, get as much as we can and increment the
+ // read attribute.
+ begin
+ Move(ChunkPos^, Privpos^, ReqSize - PrivSize);
+ Inc(PrivSize, ReqSize - PrivSize);
+ // as we filled the buffer, we'll have to break here.
+ break;
+ end;
+ end;
+ LeaveCriticalSection(FCritSect);
+ ReqSize := PrivSize;
+end;
+
+end.
diff --git a/tests/examplefiles/example.rb b/tests/examplefiles/example.rb
new file mode 100644
index 00000000..53c89efe
--- /dev/null
+++ b/tests/examplefiles/example.rb
@@ -0,0 +1,10070 @@
+module CodeRay
+ module Scanners
+
+class Ruby < Scanner
+
+ RESERVED_WORDS = [
+ 'and', 'def', 'end', 'in', 'or', 'unless', 'begin',
+ 'defined?', 'ensure', 'module', 'redo', 'super', 'until',
+ 'BEGIN', 'break', 'do', 'next', 'rescue', 'then',
+ 'when', 'END', 'case', 'else', 'for', 'retry',
+ 'while', 'alias', 'class', 'elsif', 'if', 'not', 'return',
+ 'undef', 'yield',
+ ]
+
+ DEF_KEYWORDS = ['def']
+ MODULE_KEYWORDS = ['class', 'module']
+ DEF_NEW_STATE = WordList.new(:initial).
+ add(DEF_KEYWORDS, :def_expected).
+ add(MODULE_KEYWORDS, :module_expected)
+
+ WORDS_ALLOWING_REGEXP = [
+ 'and', 'or', 'not', 'while', 'until', 'unless', 'if', 'elsif', 'when'
+ ]
+ REGEXP_ALLOWED = WordList.new(false).
+ add(WORDS_ALLOWING_REGEXP, :set)
+
+ PREDEFINED_CONSTANTS = [
+ 'nil', 'true', 'false', 'self',
+ 'DATA', 'ARGV', 'ARGF', '__FILE__', '__LINE__',
+ ]
+
+ IDENT_KIND = WordList.new(:ident).
+ add(RESERVED_WORDS, :reserved).
+ add(PREDEFINED_CONSTANTS, :pre_constant)
+
+ METHOD_NAME = / #{IDENT} [?!]? /xo
+ METHOD_NAME_EX = /
+ #{METHOD_NAME} # common methods: split, foo=, empty?, gsub!
+ | \*\*? # multiplication and power
+ | [-+~]@? # plus, minus
+ | [\/%&|^`] # division, modulo or format strings, &and, |or, ^xor, `system`
+ | \[\]=? # array getter and setter
+ | <=?>? | >=? # comparison, rocket operator
+ | << | >> # append or shift left, shift right
+ | ===? # simple equality and case equality
+ /ox
+ GLOBAL_VARIABLE = / \$ (?: #{IDENT} | \d+ | [~&+`'=\/,;_.<>!@0$?*":F\\] | -[a-zA-Z_0-9] ) /ox
+
+ DOUBLEQ = / " [^"\#\\]* (?: (?: \#\{.*?\} | \#(?:$")? | \\. ) [^"\#\\]* )* "? /ox
+ SINGLEQ = / ' [^'\\]* (?: \\. [^'\\]* )* '? /ox
+ STRING = / #{SINGLEQ} | #{DOUBLEQ} /ox
+ SHELL = / ` [^`\#\\]* (?: (?: \#\{.*?\} | \#(?:$`)? | \\. ) [^`\#\\]* )* `? /ox
+ REGEXP = / \/ [^\/\#\\]* (?: (?: \#\{.*?\} | \#(?:$\/)? | \\. ) [^\/\#\\]* )* \/? /ox
+
+ DECIMAL = /\d+(?:_\d+)*/ # doesn't recognize 09 as octal error
+ OCTAL = /0_?[0-7]+(?:_[0-7]+)*/
+ HEXADECIMAL = /0x[0-9A-Fa-f]+(?:_[0-9A-Fa-f]+)*/
+ BINARY = /0b[01]+(?:_[01]+)*/
+
+ EXPONENT = / [eE] [+-]? #{DECIMAL} /ox
+ FLOAT = / #{DECIMAL} (?: #{EXPONENT} | \. #{DECIMAL} #{EXPONENT}? ) /
+ INTEGER = /#{OCTAL}|#{HEXADECIMAL}|#{BINARY}|#{DECIMAL}/
+
+ def reset
+ super
+ @regexp_allowed = false
+ end
+
+ def next_token
+ return if @scanner.eos?
+
+ kind = :error
+ if @scanner.scan(/\s+/) # in every state
+ kind = :space
+ @regexp_allowed = :set if @regexp_allowed or @scanner.matched.index(?\n) # delayed flag setting
+
+ elsif @state == :def_expected
+ if @scanner.scan(/ (?: (?:#{IDENT}(?:\.|::))* | (?:@@?|$)? #{IDENT}(?:\.|::) ) #{METHOD_NAME_EX} /ox)
+ kind = :method
+ @state = :initial
+ else
+ @scanner.getch
+ end
+ @state = :initial
+
+ elsif @state == :module_expected
+ if @scanner.scan(/<</)
+ kind = :operator
+ else
+ if @scanner.scan(/ (?: #{IDENT} (?:\.|::))* #{IDENT} /ox)
+ kind = :method
+ else
+ @scanner.getch
+ end
+ @state = :initial
+ end
+
+ elsif # state == :initial
+ # IDENTIFIERS, KEYWORDS
+ if @scanner.scan(GLOBAL_VARIABLE)
+ kind = :global_variable
+ elsif @scanner.scan(/ @@ #{IDENT} /ox)
+ kind = :class_variable
+ elsif @scanner.scan(/ @ #{IDENT} /ox)
+ kind = :instance_variable
+ elsif @scanner.scan(/ __END__\n ( (?!\#CODE\#) .* )? | \#[^\n]* | =begin(?=\s).*? \n=end(?=\s|\z)(?:[^\n]*)? /mx)
+ kind = :comment
+ elsif @scanner.scan(METHOD_NAME)
+ if @last_token_dot
+ kind = :ident
+ else
+ matched = @scanner.matched
+ kind = IDENT_KIND[matched]
+ if kind == :ident and matched =~ /^[A-Z]/
+ kind = :constant
+ elsif kind == :reserved
+ @state = DEF_NEW_STATE[matched]
+ @regexp_allowed = REGEXP_ALLOWED[matched]
+ end
+ end
+
+ elsif @scanner.scan(STRING)
+ kind = :string
+ elsif @scanner.scan(SHELL)
+ kind = :shell
+ elsif @scanner.scan(/<<
+ (?:
+ ([a-zA-Z_0-9]+)
+ (?: .*? ^\1$ | .* )
+ |
+ -([a-zA-Z_0-9]+)
+ (?: .*? ^\s*\2$ | .* )
+ |
+ (["\'`]) (.+?) \3
+ (?: .*? ^\4$ | .* )
+ |
+ - (["\'`]) (.+?) \5
+ (?: .*? ^\s*\6$ | .* )
+ )
+ /mxo)
+ kind = :string
+ elsif @scanner.scan(/\//) and @regexp_allowed
+ @scanner.unscan
+ @scanner.scan(REGEXP)
+ kind = :regexp
+/%(?:[Qqxrw](?:\([^)#\\\\]*(?:(?:#\{.*?\}|#|\\\\.)[^)#\\\\]*)*\)?|\[[^\]#\\\\]*(?:(?:#\{.*?\}|#|\\\\.)[^\]#\\\\]*)*\]?|\{[^}#\\\\]*(?:(?:#\{.*?\}|#|\\\\.)[^}#\\\\]*)*\}?|<[^>#\\\\]*(?:(?:#\{.*?\}|#|\\\\.)[^>#\\\\]*)*>?|([^a-zA-Z\\\\])(?:(?!\1)[^#\\\\])*(?:(?:#\{.*?\}|#|\\\\.)(?:(?!\1)[^#\\\\])*)*\1?)|\([^)#\\\\]*(?:(?:#\{.*?\}|#|\\\\.)[^)#\\\\]*)*\)?|\[[^\]#\\\\]*(?:(?:#\{.*?\}|#|\\\\.)[^\]#\\\\]*)*\]?|\{[^}#\\\\]*(?:(?:#\{.*?\}|#|\\\\.)[^}#\\\\]*)*\}?|<[^>#\\\\]*(?:(?:#\{.*?\}|#|\\\\.)[^>#\\\\]*)*>?|([^a-zA-Z\s\\\\])(?:(?!\2)[^#\\\\])*(?:(?:#\{.*?\}|#|\\\\.)(?:(?!\2)[^#\\\\])*)*\2?|\\\\[^#\\\\]*(?:(?:#\{.*?\}|#)[^#\\\\]*)*\\\\?)/
+ elsif @scanner.scan(/:(?:#{GLOBAL_VARIABLE}|#{METHOD_NAME_EX}|#{STRING})/ox)
+ kind = :symbol
+ elsif @scanner.scan(/
+ \? (?:
+ [^\s\\]
+ |
+ \\ (?:M-\\C-|C-\\M-|M-\\c|c\\M-|c|C-|M-))? (?: \\ (?: . | [0-7]{3} | x[0-9A-Fa-f][0-9A-Fa-f] )
+ )
+ /mox)
+ kind = :integer
+
+ elsif @scanner.scan(/ [-+*\/%=<>;,|&!()\[\]{}~?] | \.\.?\.? | ::? /x)
+ kind = :operator
+ @regexp_allowed = :set if @scanner.matched[-1,1] =~ /[~=!<>|&^,\(\[+\-\/\*%]\z/
+ elsif @scanner.scan(FLOAT)
+ kind = :float
+ elsif @scanner.scan(INTEGER)
+ kind = :integer
+ else
+ @scanner.getch
+ end
+ end
+
+ token = Token.new @scanner.matched, kind
+
+ if kind == :regexp
+ token.text << @scanner.scan(/[eimnosux]*/)
+ end
+
+ @regexp_allowed = (@regexp_allowed == :set) # delayed flag setting
+
+ token
+ end
+end
+
+register Ruby, 'ruby', 'rb'
+
+ end
+end
+class Set
+ include Enumerable
+
+ # Creates a new set containing the given objects.
+ def self.[](*ary)
+ new(ary)
+ end
+
+ # Creates a new set containing the elements of the given enumerable
+ # object.
+ #
+ # If a block is given, the elements of enum are preprocessed by the
+ # given block.
+ def initialize(enum = nil, &block) # :yields: o
+ @hash ||= Hash.new
+
+ enum.nil? and return
+
+ if block
+ enum.each { |o| add(block[o]) }
+ else
+ merge(enum)
+ end
+ end
+
+ # Copy internal hash.
+ def initialize_copy(orig)
+ @hash = orig.instance_eval{@hash}.dup
+ end
+
+ # Returns the number of elements.
+ def size
+ @hash.size
+ end
+ alias length size
+
+ # Returns true if the set contains no elements.
+ def empty?
+ @hash.empty?
+ end
+
+ # Removes all elements and returns self.
+ def clear
+ @hash.clear
+ self
+ end
+
+ # Replaces the contents of the set with the contents of the given
+ # enumerable object and returns self.
+ def replace(enum)
+ if enum.class == self.class
+ @hash.replace(enum.instance_eval { @hash })
+ else
+ enum.is_a?(Enumerable) or raise ArgumentError, "value must be enumerable"
+ clear
+ enum.each { |o| add(o) }
+ end
+
+ self
+ end
+
+ # Converts the set to an array. The order of elements is uncertain.
+ def to_a
+ @hash.keys
+ end
+
+ def flatten_merge(set, seen = Set.new)
+ set.each { |e|
+ if e.is_a?(Set)
+ if seen.include?(e_id = e.object_id)
+ raise ArgumentError, "tried to flatten recursive Set"
+ end
+
+ seen.add(e_id)
+ flatten_merge(e, seen)
+ seen.delete(e_id)
+ else
+ add(e)
+ end
+ }
+
+ self
+ end
+ protected :flatten_merge
+
+ # Returns a new set that is a copy of the set, flattening each
+ # containing set recursively.
+ def flatten
+ self.class.new.flatten_merge(self)
+ end
+
+ # Equivalent to Set#flatten, but replaces the receiver with the
+ # result in place. Returns nil if no modifications were made.
+ def flatten!
+ if detect { |e| e.is_a?(Set) }
+ replace(flatten())
+ else
+ nil
+ end
+ end
+
+ # Returns true if the set contains the given object.
+ def include?(o)
+ @hash.include?(o)
+ end
+ alias member? include?
+
+ # Returns true if the set is a superset of the given set.
+ def superset?(set)
+ set.is_a?(Set) or raise ArgumentError, "value must be a set"
+ return false if size < set.size
+ set.all? { |o| include?(o) }
+ end
+
+ # Returns true if the set is a proper superset of the given set.
+ def proper_superset?(set)
+ set.is_a?(Set) or raise ArgumentError, "value must be a set"
+ return false if size <= set.size
+ set.all? { |o| include?(o) }
+ end
+
+ # Returns true if the set is a subset of the given set.
+ def subset?(set)
+ set.is_a?(Set) or raise ArgumentError, "value must be a set"
+ return false if set.size < size
+ all? { |o| set.include?(o) }
+ end
+
+ # Returns true if the set is a proper subset of the given set.
+ def proper_subset?(set)
+ set.is_a?(Set) or raise ArgumentError, "value must be a set"
+ return false if set.size <= size
+ all? { |o| set.include?(o) }
+ end
+
+ # Calls the given block once for each element in the set, passing
+ # the element as parameter.
+ def each
+ @hash.each_key { |o| yield(o) }
+ self
+ end
+
+ # Adds the given object to the set and returns self. Use +merge+ to
+ # add several elements at once.
+ def add(o)
+ @hash[o] = true
+ self
+ end
+ alias << add
+
+ # Adds the given object to the set and returns self. If the
+ # object is already in the set, returns nil.
+ def add?(o)
+ if include?(o)
+ nil
+ else
+ add(o)
+ end
+ end
+
+ # Deletes the given object from the set and returns self. Use +subtract+ to
+ # delete several items at once.
+ def delete(o)
+ @hash.delete(o)
+ self
+ end
+
+ # Deletes the given object from the set and returns self. If the
+ # object is not in the set, returns nil.
+ def delete?(o)
+ if include?(o)
+ delete(o)
+ else
+ nil
+ end
+ end
+
+ # Deletes every element of the set for which block evaluates to
+ # true, and returns self.
+ def delete_if
+ @hash.delete_if { |o,| yield(o) }
+ self
+ end
+
+ # Do collect() destructively.
+ def collect!
+ set = self.class.new
+ each { |o| set << yield(o) }
+ replace(set)
+ end
+ alias map! collect!
+
+ # Equivalent to Set#delete_if, but returns nil if no changes were
+ # made.
+ def reject!
+ n = size
+ delete_if { |o| yield(o) }
+ size == n ? nil : self
+ end
+
+ # Merges the elements of the given enumerable object to the set and
+ # returns self.
+ def merge(enum)
+ if enum.is_a?(Set)
+ @hash.update(enum.instance_eval { @hash })
+ else
+ enum.is_a?(Enumerable) or raise ArgumentError, "value must be enumerable"
+ enum.each { |o| add(o) }
+ end
+
+ self
+ end
+
+ # Deletes every element that appears in the given enumerable object
+ # and returns self.
+ def subtract(enum)
+ enum.is_a?(Enumerable) or raise ArgumentError, "value must be enumerable"
+ enum.each { |o| delete(o) }
+ self
+ end
+
+ # Returns a new set built by merging the set and the elements of the
+ # given enumerable object.
+ def |(enum)
+ enum.is_a?(Enumerable) or raise ArgumentError, "value must be enumerable"
+ dup.merge(enum)
+ end
+ alias + | ##
+ alias union | ##
+
+ # Returns a new set built by duplicating the set, removing every
+ # element that appears in the given enumerable object.
+ def -(enum)
+ enum.is_a?(Enumerable) or raise ArgumentError, "value must be enumerable"
+ dup.subtract(enum)
+ end
+ alias difference - ##
+
+ # Returns a new array containing elements common to the set and the
+ # given enumerable object.
+ def &(enum)
+ enum.is_a?(Enumerable) or raise ArgumentError, "value must be enumerable"
+ n = self.class.new
+ enum.each { |o| n.add(o) if include?(o) }
+ n
+ end
+ alias intersection & ##
+
+ # Returns a new array containing elements exclusive between the set
+ # and the given enumerable object. (set ^ enum) is equivalent to
+ # ((set | enum) - (set & enum)).
+ def ^(enum)
+ enum.is_a?(Enumerable) or raise ArgumentError, "value must be enumerable"
+ n = dup
+ enum.each { |o| if n.include?(o) then n.delete(o) else n.add(o) end }
+ n
+ end
+
+ # Returns true if two sets are equal. The equality of each couple
+ # of elements is defined according to Object#eql?.
+ def ==(set)
+ equal?(set) and return true
+
+ set.is_a?(Set) && size == set.size or return false
+
+ hash = @hash.dup
+ set.all? { |o| hash.include?(o) }
+ end
+
+ def hash # :nodoc:
+ @hash.hash
+ end
+
+ def eql?(o) # :nodoc:
+ return false unless o.is_a?(Set)
+ @hash.eql?(o.instance_eval{@hash})
+ end
+
+ # Classifies the set by the return value of the given block and
+ # returns a hash of {value => set of elements} pairs. The block is
+ # called once for each element of the set, passing the element as
+ # parameter.
+ #
+ # e.g.:
+ #
+ # require 'set'
+ # files = Set.new(Dir.glob("*.rb"))
+ # hash = files.classify { |f| File.mtime(f).year }
+ # p hash # => {2000=>#<Set: {"a.rb", "b.rb"}>,
+ # # 2001=>#<Set: {"c.rb", "d.rb", "e.rb"}>,
+ # # 2002=>#<Set: {"f.rb"}>}
+ def classify # :yields: o
+ h = {}
+
+ each { |i|
+ x = yield(i)
+ (h[x] ||= self.class.new).add(i)
+ }
+
+ h
+ end
+
+ # Divides the set into a set of subsets according to the commonality
+ # defined by the given block.
+ #
+ # If the arity of the block is 2, elements o1 and o2 are in common
+ # if block.call(o1, o2) is true. Otherwise, elements o1 and o2 are
+ # in common if block.call(o1) == block.call(o2).
+ #
+ # e.g.:
+ #
+ # require 'set'
+ # numbers = Set[1, 3, 4, 6, 9, 10, 11]
+ # set = numbers.divide { |i,j| (i - j).abs == 1 }
+ # p set # => #<Set: {#<Set: {1}>,
+ # # #<Set: {11, 9, 10}>,
+ # # #<Set: {3, 4}>,
+ # # #<Set: {6}>}>
+ def divide(&func)
+ if func.arity == 2
+ require 'tsort'
+
+ class << dig = {} # :nodoc:
+ include TSort
+
+ alias tsort_each_node each_key
+ def tsort_each_child(node, &block)
+ fetch(node).each(&block)
+ end
+ end
+
+ each { |u|
+ dig[u] = a = []
+ each{ |v| func.call(u, v) and a << v }
+ }
+
+ set = Set.new()
+ dig.each_strongly_connected_component { |css|
+ set.add(self.class.new(css))
+ }
+ set
+ else
+ Set.new(classify(&func).values)
+ end
+ end
+
+ InspectKey = :__inspect_key__ # :nodoc:
+
+ # Returns a string containing a human-readable representation of the
+ # set. ("#<Set: {element1, element2, ...}>")
+ def inspect
+ ids = (Thread.current[InspectKey] ||= [])
+
+ if ids.include?(object_id)
+ return sprintf('#<%s: {...}>', self.class.name)
+ end
+
+ begin
+ ids << object_id
+ return sprintf('#<%s: {%s}>', self.class, to_a.inspect[1..-2])
+ ensure
+ ids.pop
+ end
+ end
+
+ def pretty_print(pp) # :nodoc:
+ pp.text sprintf('#<%s: {', self.class.name)
+ pp.nest(1) {
+ pp.seplist(self) { |o|
+ pp.pp o
+ }
+ }
+ pp.text "}>"
+ end
+
+ def pretty_print_cycle(pp) # :nodoc:
+ pp.text sprintf('#<%s: {%s}>', self.class.name, empty? ? '' : '...')
+ end
+end
+
+# SortedSet implements a set which elements are sorted in order. See Set.
+class SortedSet < Set
+ @@setup = false
+
+ class << self
+ def [](*ary) # :nodoc:
+ new(ary)
+ end
+
+ def setup # :nodoc:
+ @@setup and return
+
+ begin
+ require 'rbtree'
+
+ module_eval %{
+ def initialize(*args, &block)
+ @hash = RBTree.new
+ super
+ end
+ }
+ rescue LoadError
+ module_eval %{
+ def initialize(*args, &block)
+ @keys = nil
+ super
+ end
+
+ def clear
+ @keys = nil
+ super
+ end
+
+ def replace(enum)
+ @keys = nil
+ super
+ end
+
+ def add(o)
+ @keys = nil
+ @hash[o] = true
+ self
+ end
+ alias << add
+
+ def delete(o)
+ @keys = nil
+ @hash.delete(o)
+ self
+ end
+
+ def delete_if
+ n = @hash.size
+ @hash.delete_if { |o,| yield(o) }
+ @keys = nil if @hash.size != n
+ self
+ end
+
+ def merge(enum)
+ @keys = nil
+ super
+ end
+
+ def each
+ to_a.each { |o| yield(o) }
+ end
+
+ def to_a
+ (@keys = @hash.keys).sort! unless @keys
+ @keys
+ end
+ }
+ end
+
+ @@setup = true
+ end
+ end
+
+ def initialize(*args, &block) # :nodoc:
+ SortedSet.setup
+ initialize(*args, &block)
+ end
+end
+
+module Enumerable
+ # Makes a set from the enumerable object with given arguments.
+ def to_set(klass = Set, *args, &block)
+ klass.new(self, *args, &block)
+ end
+end
+
+# =begin
+# == RestricedSet class
+# RestricedSet implements a set with restrictions defined by a given
+# block.
+#
+# === Super class
+# Set
+#
+# === Class Methods
+# --- RestricedSet::new(enum = nil) { |o| ... }
+# --- RestricedSet::new(enum = nil) { |rset, o| ... }
+# Creates a new restricted set containing the elements of the given
+# enumerable object. Restrictions are defined by the given block.
+#
+# If the block's arity is 2, it is called with the RestrictedSet
+# itself and an object to see if the object is allowed to be put in
+# the set.
+#
+# Otherwise, the block is called with an object to see if the object
+# is allowed to be put in the set.
+#
+# === Instance Methods
+# --- restriction_proc
+# Returns the restriction procedure of the set.
+#
+# =end
+#
+# class RestricedSet < Set
+# def initialize(*args, &block)
+# @proc = block or raise ArgumentError, "missing a block"
+#
+# if @proc.arity == 2
+# instance_eval %{
+# def add(o)
+# @hash[o] = true if @proc.call(self, o)
+# self
+# end
+# alias << add
+#
+# def add?(o)
+# if include?(o) || !@proc.call(self, o)
+# nil
+# else
+# @hash[o] = true
+# self
+# end
+# end
+#
+# def replace(enum)
+# enum.is_a?(Enumerable) or raise ArgumentError, "value must be enumerable"
+# clear
+# enum.each { |o| add(o) }
+#
+# self
+# end
+#
+# def merge(enum)
+# enum.is_a?(Enumerable) or raise ArgumentError, "value must be enumerable"
+# enum.each { |o| add(o) }
+#
+# self
+# end
+# }
+# else
+# instance_eval %{
+# def add(o)
+# if @proc.call(o)
+# @hash[o] = true
+# end
+# self
+# end
+# alias << add
+#
+# def add?(o)
+# if include?(o) || !@proc.call(o)
+# nil
+# else
+# @hash[o] = true
+# self
+# end
+# end
+# }
+# end
+#
+# super(*args)
+# end
+#
+# def restriction_proc
+# @proc
+# end
+# end
+
+if $0 == __FILE__
+ eval DATA.read, nil, $0, __LINE__+4
+end
+
+# = rweb - CGI Support Library
+#
+# Author:: Johannes Barre (mailto:rweb@igels.net)
+# Copyright:: Copyright (c) 2003, 04 by Johannes Barre
+# License:: GNU Lesser General Public License (COPYING, http://www.gnu.org/copyleft/lesser.html)
+# Version:: 0.1.0
+# CVS-ID:: $Id: example.rb 39 2005-11-05 03:33:55Z murphy $
+#
+# == What is Rweb?
+# Rweb is a replacement for the cgi class included in the ruby distribution.
+#
+# == How to use
+#
+# === Basics
+#
+# This class is made to be as easy as possible to use. An example:
+#
+# require "rweb"
+#
+# web = Rweb.new
+# web.out do
+# web.puts "Hello world!"
+# end
+#
+# The visitor will get a simple "Hello World!" in his browser. Please notice,
+# that won't set html-tags for you, so you should better do something like this:
+#
+# require "rweb"
+#
+# web = Rweb.new
+# web.out do
+# web.puts "<html><body>Hello world!</body></html>"
+# end
+#
+# === Set headers
+# Of course, it's also possible to tell the browser, that the content of this
+# page is plain text instead of html code:
+#
+# require "rweb"
+#
+# web = Rweb.new
+# web.out do
+# web.header("content-type: text/plain")
+# web.puts "Hello plain world!"
+# end
+#
+# Please remember, headers can't be set after the page content has been send.
+# You have to set all nessessary headers before the first puts oder print. It's
+# possible to cache the content until everything is complete. Doing it this
+# way, you can set headers everywhere.
+#
+# If you set a header twice, the second header will replace the first one. The
+# header name is not casesensitive, it will allways converted in to the
+# capitalised form suggested by the w3c (http://w3.org)
+#
+# === Set cookies
+# Setting cookies is quite easy:
+# include 'rweb'
+#
+# web = Rweb.new
+# Cookie.new("Visits", web.cookies['visits'].to_i +1)
+# web.out do
+# web.puts "Welcome back! You visited this page #{web.cookies['visits'].to_i +1} times"
+# end
+#
+# See the class Cookie for more details.
+#
+# === Get form and cookie values
+# There are four ways to submit data from the browser to the server and your
+# ruby script: via GET, POST, cookies and file upload. Rweb doesn't support
+# file upload by now.
+#
+# include 'rweb'
+#
+# web = Rweb.new
+# web.out do
+# web.print "action: #{web.get['action']} "
+# web.puts "The value of the cookie 'visits' is #{web.cookies['visits']}"
+# web.puts "The post parameter 'test['x']' is #{web.post['test']['x']}"
+# end
+
+RWEB_VERSION = "0.1.0"
+RWEB = "rweb/#{RWEB_VERSION}"
+
+#require 'rwebcookie' -> edit by bunny :-)
+
+class Rweb
+ # All parameter submitted via the GET method are available in attribute
+ # get. This is Hash, where every parameter is available as a key-value
+ # pair.
+ #
+ # If your input tag has a name like this one, it's value will be available
+ # as web.get["fieldname"]
+ # <input name="fieldname">
+ # You can submit values as a Hash
+ # <input name="text['index']">
+ # <input name="text['index2']">
+ # will be available as
+ # web.get["text"]["index"]
+ # web.get["text"]["index2"]
+ # Integers are also possible
+ # <input name="int[2]">
+ # <input name="int[3]['hi']>
+ # will be available as
+ # web.get["int"][2]
+ # web.get["int"][3]["hi"]
+ # If you specify no index, the lowest unused index will be used:
+ # <input name="int[]"><!-- First Field -->
+ # <input name="int[]"><!-- Second one -->
+ # will be available as
+ # web.get["int"][0] # First Field
+ # web.get["int"][1] # Second one
+ # Please notice, this doesn'd work like you might expect:
+ # <input name="text[index]">
+ # It will not be available as web.get["text"]["index"] but
+ # web.get["text[index]"]
+ attr_reader :get
+
+ # All parameters submitted via POST are available in the attribute post. It
+ # works like the get attribute.
+ # <input name="text[0]">
+ # will be available as
+ # web.post["text"][0]
+ attr_reader :post
+
+ # All cookies submitted by the browser are available in cookies. This is a
+ # Hash, where every cookie is a key-value pair.
+ attr_reader :cookies
+
+ # The name of the browser identification is submitted as USER_AGENT and
+ # available in this attribute.
+ attr_reader :user_agent
+
+ # The IP address of the client.
+ attr_reader :remote_addr
+
+ # Creates a new Rweb object. This should only done once. You can set various
+ # options via the settings hash.
+ #
+ # "cache" => true: Everything you script send to the client will be cached
+ # until the end of the out block or until flush is called. This way, you
+ # can modify headers and cookies even after printing something to the client.
+ #
+ # "safe" => level: Changes the $SAFE attribute. By default, $SAFE will be set
+ # to 1. If $SAFE is already higher than this value, it won't be changed.
+ #
+ # "silend" => true: Normaly, Rweb adds automaticly a header like this
+ # "X-Powered-By: Rweb/x.x.x (Ruby/y.y.y)". With the silend option you can
+ # suppress this.
+ def initialize (settings = {})
+ # {{{
+ @header = {}
+ @cookies = {}
+ @get = {}
+ @post = {}
+
+ # Internal attributes
+ @status = nil
+ @reasonPhrase = nil
+ @setcookies = []
+ @output_started = false;
+ @output_allowed = false;
+
+ @mod_ruby = false
+ @env = ENV.to_hash
+
+ if defined?(MOD_RUBY)
+ @output_method = "mod_ruby"
+ @mod_ruby = true
+ elsif @env['SERVER_SOFTWARE'] =~ /^Microsoft-IIS/i
+ @output_method = "nph"
+ else
+ @output_method = "ph"
+ end
+
+ unless settings.is_a?(Hash)
+ raise TypeError, "settings must be a Hash"
+ end
+ @settings = settings
+
+ unless @settings.has_key?("safe")
+ @settings["safe"] = 1
+ end
+
+ if $SAFE < @settings["safe"]
+ $SAFE = @settings["safe"]
+ end
+
+ unless @settings.has_key?("cache")
+ @settings["cache"] = false
+ end
+
+ # mod_ruby sets no QUERY_STRING variable, if no GET-Parameters are given
+ unless @env.has_key?("QUERY_STRING")
+ @env["QUERY_STRING"] = ""
+ end
+
+ # Now we split the QUERY_STRING by the seperators & and ; or, if
+ # specified, settings['get seperator']
+ unless @settings.has_key?("get seperator")
+ get_args = @env['QUERY_STRING'].split(/[&;]/)
+ else
+ get_args = @env['QUERY_STRING'].split(@settings['get seperator'])
+ end
+
+ get_args.each do | arg |
+ arg_key, arg_val = arg.split(/=/, 2)
+ arg_key = Rweb::unescape(arg_key)
+ arg_val = Rweb::unescape(arg_val)
+
+ # Parse names like name[0], name['text'] or name[]
+ pattern = /^(.+)\[("[^\]]*"|'[^\]]*'|[0-9]*)\]$/
+ keys = []
+ while match = pattern.match(arg_key)
+ arg_key = match[1]
+ keys = [match[2]] + keys
+ end
+ keys = [arg_key] + keys
+
+ akt = @get
+ last = nil
+ lastkey = nil
+ keys.each do |key|
+ if key == ""
+ # No key specified (like in "test[]"), so we use the
+ # lowerst unused Integer as key
+ key = 0
+ while akt.has_key?(key)
+ key += 1
+ end
+ elsif /^[0-9]*$/ =~ key
+ # If the index is numerical convert it to an Integer
+ key = key.to_i
+ elsif key[0].chr == "'" || key[0].chr == '"'
+ key = key[1, key.length() -2]
+ end
+ if !akt.has_key?(key) || !akt[key].class == Hash
+ # create an empty Hash if there isn't already one
+ akt[key] = {}
+ end
+ last = akt
+ lastkey = key
+ akt = akt[key]
+ end
+ last[lastkey] = arg_val
+ end
+
+ if @env['REQUEST_METHOD'] == "POST"
+ if @env.has_key?("CONTENT_TYPE") && @env['CONTENT_TYPE'] == "application/x-www-form-urlencoded" && @env.has_key?('CONTENT_LENGTH')
+ unless @settings.has_key?("post seperator")
+ post_args = $stdin.read(@env['CONTENT_LENGTH'].to_i).split(/[&;]/)
+ else
+ post_args = $stdin.read(@env['CONTENT_LENGTH'].to_i).split(@settings['post seperator'])
+ end
+ post_args.each do | arg |
+ arg_key, arg_val = arg.split(/=/, 2)
+ arg_key = Rweb::unescape(arg_key)
+ arg_val = Rweb::unescape(arg_val)
+
+ # Parse names like name[0], name['text'] or name[]
+ pattern = /^(.+)\[("[^\]]*"|'[^\]]*'|[0-9]*)\]$/
+ keys = []
+ while match = pattern.match(arg_key)
+ arg_key = match[1]
+ keys = [match[2]] + keys
+ end
+ keys = [arg_key] + keys
+
+ akt = @post
+ last = nil
+ lastkey = nil
+ keys.each do |key|
+ if key == ""
+ # No key specified (like in "test[]"), so we use
+ # the lowerst unused Integer as key
+ key = 0
+ while akt.has_key?(key)
+ key += 1
+ end
+ elsif /^[0-9]*$/ =~ key
+ # If the index is numerical convert it to an Integer
+ key = key.to_i
+ elsif key[0].chr == "'" || key[0].chr == '"'
+ key = key[1, key.length() -2]
+ end
+ if !akt.has_key?(key) || !akt[key].class == Hash
+ # create an empty Hash if there isn't already one
+ akt[key] = {}
+ end
+ last = akt
+ lastkey = key
+ akt = akt[key]
+ end
+ last[lastkey] = arg_val
+ end
+ else
+ # Maybe we should print a warning here?
+ $stderr.print("Unidentified form data recived and discarded.")
+ end
+ end
+
+ if @env.has_key?("HTTP_COOKIE")
+ cookie = @env['HTTP_COOKIE'].split(/; ?/)
+ cookie.each do | c |
+ cookie_key, cookie_val = c.split(/=/, 2)
+
+ @cookies [Rweb::unescape(cookie_key)] = Rweb::unescape(cookie_val)
+ end
+ end
+
+ if defined?(@env['HTTP_USER_AGENT'])
+ @user_agent = @env['HTTP_USER_AGENT']
+ else
+ @user_agent = nil;
+ end
+
+ if defined?(@env['REMOTE_ADDR'])
+ @remote_addr = @env['REMOTE_ADDR']
+ else
+ @remote_addr = nil
+ end
+ # }}}
+ end
+
+ # Prints a String to the client. If caching is enabled, the String will
+ # buffered until the end of the out block ends.
+ def print(str = "")
+ # {{{
+ unless @output_allowed
+ raise "You just can write to output inside of a Rweb::out-block"
+ end
+
+ if @settings["cache"]
+ @buffer += [str.to_s]
+ else
+ unless @output_started
+ sendHeaders
+ end
+ $stdout.print(str)
+ end
+ nil
+ # }}}
+ end
+
+ # Prints a String to the client and adds a line break at the end. Please
+ # remember, that a line break is not visible in HTML, use the <br> HTML-Tag
+ # for this. If caching is enabled, the String will buffered until the end
+ # of the out block ends.
+ def puts(str = "")
+ # {{{
+ self.print(str + "\n")
+ # }}}
+ end
+
+ # Alias to print.
+ def write(str = "")
+ # {{{
+ self.print(str)
+ # }}}
+ end
+
+ # If caching is enabled, all cached data are send to the cliend and the
+ # cache emptied.
+ def flush
+ # {{{
+ unless @output_allowed
+ raise "You can't use flush outside of a Rweb::out-block"
+ end
+ buffer = @buffer.join
+
+ unless @output_started
+ sendHeaders
+ end
+ $stdout.print(buffer)
+
+ @buffer = []
+ # }}}
+ end
+
+ # Sends one or more header to the client. All headers are cached just
+ # before body data are send to the client. If the same header are set
+ # twice, only the last value is send.
+ #
+ # Example:
+ # web.header("Last-Modified: Mon, 16 Feb 2004 20:15:41 GMT")
+ # web.header("Location: http://www.ruby-lang.org")
+ #
+ # You can specify more than one header at the time by doing something like
+ # this:
+ # web.header("Content-Type: text/plain\nContent-Length: 383")
+ # or
+ # web.header(["Content-Type: text/plain", "Content-Length: 383"])
+ def header(str)
+ # {{{
+ if @output_started
+ raise "HTTP-Headers are already send. You can't change them after output has started!"
+ end
+ unless @output_allowed
+ raise "You just can set headers inside of a Rweb::out-block"
+ end
+ if str.is_a?Array
+ str.each do | value |
+ self.header(value)
+ end
+
+ elsif str.split(/\n/).length > 1
+ str.split(/\n/).each do | value |
+ self.header(value)
+ end
+
+ elsif str.is_a? String
+ str.gsub!(/\r/, "")
+
+ if (str =~ /^HTTP\/1\.[01] [0-9]{3} ?.*$/) == 0
+ pattern = /^HTTP\/1.[01] ([0-9]{3}) ?(.*)$/
+
+ result = pattern.match(str)
+ self.setstatus(result[0], result[1])
+ elsif (str =~ /^status: [0-9]{3} ?.*$/i) == 0
+ pattern = /^status: ([0-9]{3}) ?(.*)$/i
+
+ result = pattern.match(str)
+ self.setstatus(result[0], result[1])
+ else
+ a = str.split(/: ?/, 2)
+
+ @header[a[0].downcase] = a[1]
+ end
+ end
+ # }}}
+ end
+
+ # Changes the status of this page. There are several codes like "200 OK",
+ # "302 Found", "404 Not Found" or "500 Internal Server Error". A list of
+ # all codes is available at
+ # http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10
+ #
+ # You can just send the code number, the reason phrase will be added
+ # automaticly with the recommendations from the w3c if not specified. If
+ # you set the status twice or more, only the last status will be send.
+ # Examples:
+ # web.status("401 Unauthorized")
+ # web.status("410 Sad but true, this lonely page is gone :(")
+ # web.status(206)
+ # web.status("400")
+ #
+ # The default status is "200 OK". If a "Location" header is set, the
+ # default status is "302 Found".
+ def status(str)
+ # {{{
+ if @output_started
+ raise "HTTP-Headers are already send. You can't change them after output has started!"
+ end
+ unless @output_allowed
+ raise "You just can set headers inside of a Rweb::out-block"
+ end
+ if str.is_a?Integer
+ @status = str
+ elsif str.is_a?String
+ p1 = /^([0-9]{3}) ?(.*)$/
+ p2 = /^HTTP\/1\.[01] ([0-9]{3}) ?(.*)$/
+ p3 = /^status: ([0-9]{3}) ?(.*)$/i
+
+ if (a = p1.match(str)) == nil
+ if (a = p2.match(str)) == nil
+ if (a = p3.match(str)) == nil
+ raise ArgumentError, "Invalid argument", caller
+ end
+ end
+ end
+ @status = a[1].to_i
+ if a[2] != ""
+ @reasonPhrase = a[2]
+ else
+ @reasonPhrase = getReasonPhrase(@status)
+ end
+ else
+ raise ArgumentError, "Argument of setstatus must be integer or string", caller
+ end
+ # }}}
+ end
+
+ # Handles the output of your content and rescues all exceptions. Send all
+ # data in the block to this method. For example:
+ # web.out do
+ # web.header("Content-Type: text/plain")
+ # web.puts("Hello, plain world!")
+ # end
+ def out
+ # {{{
+ @output_allowed = true
+ @buffer = []; # We use an array as buffer, because it's more performant :)
+
+ begin
+ yield
+ rescue Exception => exception
+ $stderr.puts "Ruby exception rescued (#{exception.class}): #{exception.message}"
+ $stderr.puts exception.backtrace.join("\n")
+
+ unless @output_started
+ self.setstatus(500)
+ @header = {}
+ end
+
+ unless (@settings.has_key?("hide errors") and @settings["hide errors"] == true)
+ unless @output_started
+ self.header("Content-Type: text/html")
+ self.puts "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Strict//EN\" \"http://www.w3.org/TR/html4/strict.dtd\">"
+ self.puts "<html>"
+ self.puts "<head>"
+ self.puts "<title>500 Internal Server Error</title>"
+ self.puts "</head>"
+ self.puts "<body>"
+ end
+ if @header.has_key?("content-type") and (@header["content-type"] =~ /^text\/html/i) == 0
+ self.puts "<h1>Internal Server Error</h1>"
+ self.puts "<p>The server encountered an exception and was unable to complete your request.</p>"
+ self.puts "<p>The exception has provided the following information:</p>"
+ self.puts "<pre style=\"background: #FFCCCC; border: black solid 2px; margin-left: 2cm; margin-right: 2cm; padding: 2mm;\"><b>#{exception.class}</b>: #{exception.message} <b>on</b>"
+ self.puts
+ self.puts "#{exception.backtrace.join("\n")}</pre>"
+ self.puts "</body>"
+ self.puts "</html>"
+ else
+ self.puts "The server encountered an exception and was unable to complete your request"
+ self.puts "The exception has provided the following information:"
+ self.puts "#{exception.class}: #{exception.message}"
+ self.puts
+ self.puts exception.backtrace.join("\n")
+ end
+ end
+ end
+
+ if @settings["cache"]
+ buffer = @buffer.join
+
+ unless @output_started
+ unless @header.has_key?("content-length")
+ self.header("content-length: #{buffer.length}")
+ end
+
+ sendHeaders
+ end
+ $stdout.print(buffer)
+ elsif !@output_started
+ sendHeaders
+ end
+ @output_allowed = false;
+ # }}}
+ end
+
+ # Decodes URL encoded data, %20 for example stands for a space.
+ def Rweb.unescape(str)
+ # {{{
+ if defined? str and str.is_a? String
+ str.gsub!(/\+/, " ")
+ str.gsub(/%.{2}/) do | s |
+ s[1,2].hex.chr
+ end
+ end
+ # }}}
+ end
+
+ protected
+ def sendHeaders
+ # {{{
+
+ Cookie.disallow # no more cookies can be set or modified
+ if !(@settings.has_key?("silent") and @settings["silent"] == true) and !@header.has_key?("x-powered-by")
+ if @mod_ruby
+ header("x-powered-by: #{RWEB} (Ruby/#{RUBY_VERSION}, #{MOD_RUBY})");
+ else
+ header("x-powered-by: #{RWEB} (Ruby/#{RUBY_VERSION})");
+ end
+ end
+
+ if @output_method == "ph"
+ if ((@status == nil or @status == 200) and !@header.has_key?("content-type") and !@header.has_key?("location"))
+ header("content-type: text/html")
+ end
+
+ if @status != nil
+ $stdout.print "Status: #{@status} #{@reasonPhrase}\r\n"
+ end
+
+ @header.each do |key, value|
+ key = key *1 # "unfreeze" key :)
+ key[0] = key[0,1].upcase![0]
+
+ key = key.gsub(/-[a-z]/) do |char|
+ "-" + char[1,1].upcase
+ end
+
+ $stdout.print "#{key}: #{value}\r\n"
+ end
+ cookies = Cookie.getHttpHeader # Get all cookies as an HTTP Header
+ if cookies
+ $stdout.print cookies
+ end
+
+ $stdout.print "\r\n"
+
+ elsif @output_method == "nph"
+ elsif @output_method == "mod_ruby"
+ r = Apache.request
+
+ if ((@status == nil or @status == 200) and !@header.has_key?("content-type") and !@header.has_key?("location"))
+ header("text/html")
+ end
+
+ if @status != nil
+ r.status_line = "#{@status} #{@reasonPhrase}"
+ end
+
+ r.send_http_header
+ @header.each do |key, value|
+ key = key *1 # "unfreeze" key :)
+
+ key[0] = key[0,1].upcase![0]
+ key = key.gsub(/-[a-z]/) do |char|
+ "-" + char[1,1].upcase
+ end
+ puts "#{key}: #{value.class}"
+ #r.headers_out[key] = value
+ end
+ end
+ @output_started = true
+ # }}}
+ end
+
+ def getReasonPhrase (status)
+ # {{{
+ if status == 100
+ "Continue"
+ elsif status == 101
+ "Switching Protocols"
+ elsif status == 200
+ "OK"
+ elsif status == 201
+ "Created"
+ elsif status == 202
+ "Accepted"
+ elsif status == 203
+ "Non-Authoritative Information"
+ elsif status == 204
+ "No Content"
+ elsif status == 205
+ "Reset Content"
+ elsif status == 206
+ "Partial Content"
+ elsif status == 300
+ "Multiple Choices"
+ elsif status == 301
+ "Moved Permanently"
+ elsif status == 302
+ "Found"
+ elsif status == 303
+ "See Other"
+ elsif status == 304
+ "Not Modified"
+ elsif status == 305
+ "Use Proxy"
+ elsif status == 307
+ "Temporary Redirect"
+ elsif status == 400
+ "Bad Request"
+ elsif status == 401
+ "Unauthorized"
+ elsif status == 402
+ "Payment Required"
+ elsif status == 403
+ "Forbidden"
+ elsif status == 404
+ "Not Found"
+ elsif status == 405
+ "Method Not Allowed"
+ elsif status == 406
+ "Not Acceptable"
+ elsif status == 407
+ "Proxy Authentication Required"
+ elsif status == 408
+ "Request Time-out"
+ elsif status == 409
+ "Conflict"
+ elsif status == 410
+ "Gone"
+ elsif status == 411
+ "Length Required"
+ elsif status == 412
+ "Precondition Failed"
+ elsif status == 413
+ "Request Entity Too Large"
+ elsif status == 414
+ "Request-URI Too Large"
+ elsif status == 415
+ "Unsupported Media Type"
+ elsif status == 416
+ "Requested range not satisfiable"
+ elsif status == 417
+ "Expectation Failed"
+ elsif status == 500
+ "Internal Server Error"
+ elsif status == 501
+ "Not Implemented"
+ elsif status == 502
+ "Bad Gateway"
+ elsif status == 503
+ "Service Unavailable"
+ elsif status == 504
+ "Gateway Time-out"
+ elsif status == 505
+ "HTTP Version not supported"
+ else
+ raise "Unknown Statuscode. See http://www.w3.org/Protocols/rfc2616/rfc2616-sec6.html#sec6.1 for more information."
+ end
+ # }}}
+ end
+end
+
+class Cookie
+ attr_reader :name, :value, :maxage, :path, :domain, :secure, :comment
+
+ # Sets a cookie. Please see below for details of the attributes.
+ def initialize (name, value = nil, maxage = nil, path = nil, domain = nil, secure = false)
+ # {{{
+ # HTTP headers (Cookies are a HTTP header) can only set, while no content
+ # is send. So an exception will be raised, when @@allowed is set to false
+ # and a new cookie has set.
+ unless defined?(@@allowed)
+ @@allowed = true
+ end
+ unless @@allowed
+ raise "You can't set cookies after the HTTP headers are send."
+ end
+
+ unless defined?(@@list)
+ @@list = []
+ end
+ @@list += [self]
+
+ unless defined?(@@type)
+ @@type = "netscape"
+ end
+
+ unless name.class == String
+ raise TypeError, "The name of a cookie must be a string", caller
+ end
+ if value.class.superclass == Integer || value.class == Float
+ value = value.to_s
+ elsif value.class != String && value != nil
+ raise TypeError, "The value of a cookie must be a string, integer, float or nil", caller
+ end
+ if maxage.class == Time
+ maxage = maxage - Time.now
+ elsif !maxage.class.superclass == Integer || !maxage == nil
+ raise TypeError, "The maxage date of a cookie must be an Integer or Time object or nil.", caller
+ end
+ unless path.class == String || path == nil
+ raise TypeError, "The path of a cookie must be nil or a string", caller
+ end
+ unless domain.class == String || domain == nil
+ raise TypeError, "The value of a cookie must be nil or a string", caller
+ end
+ unless secure == true || secure == false
+ raise TypeError, "The secure field of a cookie must be true or false", caller
+ end
+
+ @name, @value, @maxage, @path, @domain, @secure = name, value, maxage, path, domain, secure
+ @comment = nil
+ # }}}
+ end
+
+ # Modifies the value of this cookie. The information you want to store. If the
+ # value is nil, the cookie will be deleted by the client.
+ #
+ # This attribute can be a String, Integer or Float object or nil.
+ def value=(value)
+ # {{{
+ if value.class.superclass == Integer || value.class == Float
+ value = value.to_s
+ elsif value.class != String && value != nil
+ raise TypeError, "The value of a cookie must be a string, integer, float or nil", caller
+ end
+ @value = value
+ # }}}
+ end
+
+ # Modifies the maxage of this cookie. This attribute defines the lifetime of
+ # the cookie, in seconds. A value of 0 means the cookie should be discarded
+ # imediatly. If it set to nil, the cookie will be deleted when the browser
+ # will be closed.
+ #
+ # Attention: This is different from other implementations like PHP, where you
+ # gives the seconds since 1/1/1970 0:00:00 GMT.
+ #
+ # This attribute must be an Integer or Time object or nil.
+ def maxage=(maxage)
+ # {{{
+ if maxage.class == Time
+ maxage = maxage - Time.now
+ elsif maxage.class.superclass == Integer || !maxage == nil
+ raise TypeError, "The maxage of a cookie must be an Interger or Time object or nil.", caller
+ end
+ @maxage = maxage
+ # }}}
+ end
+
+ # Modifies the path value of this cookie. The client will send this cookie
+ # only, if the requested document is this directory or a subdirectory of it.
+ #
+ # The value of the attribute must be a String object or nil.
+ def path=(path)
+ # {{{
+ unless path.class == String || path == nil
+ raise TypeError, "The path of a cookie must be nil or a string", caller
+ end
+ @path = path
+ # }}}
+ end
+
+ # Modifies the domain value of this cookie. The client will send this cookie
+ # only if it's connected with this domain (or a subdomain, if the first
+ # character is a dot like in ".ruby-lang.org")
+ #
+ # The value of this attribute must be a String or nil.
+ def domain=(domain)
+ # {{{
+ unless domain.class == String || domain == nil
+ raise TypeError, "The domain of a cookie must be a String or nil.", caller
+ end
+ @domain = domain
+ # }}}
+ end
+
+ # Modifies the secure flag of this cookie. If it's true, the client will only
+ # send this cookie if it is secured connected with us.
+ #
+ # The value od this attribute has to be true or false.
+ def secure=(secure)
+ # {{{
+ unless secure == true || secure == false
+ raise TypeError, "The secure field of a cookie must be true or false", caller
+ end
+ @secure = secure
+ # }}}
+ end
+
+ # Modifies the comment value of this cookie. The comment won't be send, if
+ # type is "netscape".
+ def comment=(comment)
+ # {{{
+ unless comment.class == String || comment == nil
+ raise TypeError, "The comment of a cookie must be a string or nil", caller
+ end
+ @comment = comment
+ # }}}
+ end
+
+ # Changes the type of all cookies.
+ # Allowed values are RFC2109 and netscape (default).
+ def Cookie.type=(type)
+ # {{{
+ unless @@allowed
+ raise "The cookies are allready send, so you can't change the type anymore."
+ end
+ unless type.downcase == "rfc2109" && type.downcase == "netscape"
+ raise "The type of the cookies must be \"RFC2109\" or \"netscape\"."
+ end
+ @@type = type;
+ # }}}
+ end
+
+ # After sending this message, no cookies can be set or modified. Use it, when
+ # HTTP-Headers are send. Rweb does this for you.
+ def Cookie.disallow
+ # {{{
+ @@allowed = false
+ true
+ # }}}
+ end
+
+ # Returns a HTTP header (type String) with all cookies. Rweb does this for
+ # you.
+ def Cookie.getHttpHeader
+ # {{{
+ if defined?(@@list)
+ if @@type == "netscape"
+ str = ""
+ @@list.each do |cookie|
+ if cookie.value == nil
+ cookie.maxage = 0
+ cookie.value = ""
+ end
+ # TODO: Name and value should be escaped!
+ str += "Set-Cookie: #{cookie.name}=#{cookie.value}"
+ unless cookie.maxage == nil
+ expire = Time.now + cookie.maxage
+ expire.gmtime
+ str += "; Expire=#{expire.strftime("%a, %d-%b-%Y %H:%M:%S %Z")}"
+ end
+ unless cookie.domain == nil
+ str += "; Domain=#{cookie.domain}"
+ end
+ unless cookie.path == nil
+ str += "; Path=#{cookie.path}"
+ end
+ if cookie.secure
+ str += "; Secure"
+ end
+ str += "\r\n"
+ end
+ return str
+ else # type == "RFC2109"
+ str = "Set-Cookie: "
+ comma = false;
+
+ @@list.each do |cookie|
+ if cookie.value == nil
+ cookie.maxage = 0
+ cookie.value = ""
+ end
+ if comma
+ str += ","
+ end
+ comma = true
+
+ str += "#{cookie.name}=\"#{cookie.value}\""
+ unless cookie.maxage == nil
+ str += "; Max-Age=\"#{cookie.maxage}\""
+ end
+ unless cookie.domain == nil
+ str += "; Domain=\"#{cookie.domain}\""
+ end
+ unless cookie.path == nil
+ str += "; Path=\"#{cookie.path}\""
+ end
+ if cookie.secure
+ str += "; Secure"
+ end
+ unless cookie.comment == nil
+ str += "; Comment=\"#{cookie.comment}\""
+ end
+ str += "; Version=\"1\""
+ end
+ str
+ end
+ else
+ false
+ end
+ # }}}
+ end
+end
+
+require 'strscan'
+
+module BBCode
+ DEBUG = true
+
+ use 'encoder', 'tags', 'tagstack', 'smileys'
+
+=begin
+ The Parser class takes care of the encoding.
+ It scans the given BBCode (as plain text), finds tags
+ and smilies and also makes links of urls in text.
+
+ Normal text is send directly to the encoder.
+
+ If a tag was found, an instance of a Tag subclass is created
+ to handle the case.
+
+ The @tagstack manages tag nesting and ensures valid HTML.
+=end
+
+ class Parser
+ class Attribute
+ # flatten and use only one empty_arg
+ def self.create attr
+ attr = flatten attr
+ return @@empty_attr if attr.empty?
+ new attr
+ end
+
+ private_class_method :new
+
+ # remove leading and trailing whitespace; concat lines
+ def self.flatten attr
+ attr.strip.gsub(/\n/, ' ')
+ # -> ^ and $ can only match at begin and end now
+ end
+
+ ATTRIBUTE_SCAN = /
+ (?!$) # don't match at end
+ \s*
+ ( # $1 = key
+ [^=\s\]"\\]*
+ (?:
+ (?: \\. | "[^"\\]*(?:\\.[^"\\]*)*"? )
+ [^=\s\]"\\]*
+ )*
+ )
+ (?:
+ =
+ ( # $2 = value
+ [^\s\]"\\]*
+ (?:
+ (?: \\. | "[^"\\]*(?:\\.[^"\\]*)*"? )
+ [^\s\]"\\]*
+ )*
+ )?
+ )?
+ \s*
+ /x
+
+ def self.parse source
+ source = source.dup
+ # empty_tag: the tag looks like [... /]
+ # slice!: this deletes the \s*/] at the end
+ # \s+ because [url=http://rubybb.org/forum/] is NOT an empty tag.
+ # In RubyBBCode, you can use [url=http://rubybb.org/forum/ /], and this has to be
+ # interpreted correctly.
+ empty_tag = source.sub!(/^:/, '=') or source.slice!(/\/$/)
+ debug 'PARSE: ' + source.inspect + ' => ' + empty_tag.inspect
+ #-> we have now an attr that's EITHER empty OR begins and ends with non-whitespace.
+
+ attr = Hash.new
+ attr[:flags] = []
+ source.scan(ATTRIBUTE_SCAN) { |key, value|
+ if not value
+ attr[:flags] << unescape(key)
+ else
+ next if value.empty? and key.empty?
+ attr[unescape(key)] = unescape(value)
+ end
+ }
+ debug attr.inspect
+
+ return empty_tag, attr
+ end
+
+ def self.unescape_char esc
+ esc[1]
+ end
+
+ def self.unquote qt
+ qt[1..-1].chomp('"').gsub(/\\./) { |esc| unescape_char esc }
+ end
+
+ def self.unescape str
+ str.gsub(/ (\\.) | (" [^"\\]* (?:\\.[^"\\]*)* "?) /x) {
+ if $1
+ unescape_char $1
+ else
+ unquote $2
+ end
+ }
+ end
+
+ include Enumerable
+ def each &block
+ @args.each(&block)
+ end
+
+ attr_reader :source, :args, :value
+
+ def initialize source
+ @source = source
+ debug 'Attribute#new(%p)' % source
+ @empty_tag, @attr = Attribute.parse source
+ @value = @attr[''].to_s
+ end
+
+ def empty?
+ self == @@empty_attr
+ end
+
+ def empty_tag?
+ @empty_tag
+ end
+
+ def [] *keys
+ res = @attr[*keys]
+ end
+
+ def flags
+ attr[:flags]
+ end
+
+ def to_s
+ @attr
+ end
+
+ def inspect
+ 'ATTR[' + @attr.inspect + (@empty_tag ? ' | empty tag' : '') + ']'
+ end
+ end
+ class Attribute
+ @@empty_attr = new ''
+ end
+ end
+
+ class Parser
+ def Parser.flatten str
+ # replace mac & dos newlines with unix style
+ str.gsub(/\r\n?/, "\n")
+ end
+
+ def initialize input = ''
+ # input manager
+ @scanner = StringScanner.new ''
+ # output manager
+ @encoder = Encoder.new
+ @output = ''
+ # tag manager
+ @tagstack = TagStack.new(@encoder)
+
+ @do_magic = true
+ # set the input
+ feed input
+ end
+
+ # if you want, you can feed a parser instance after creating,
+ # or even feed it repeatedly.
+ def feed food
+ @scanner.string = Parser.flatten food
+ end
+
+ # parse through the string using parse_token
+ def parse
+ parse_token until @scanner.eos?
+ @tagstack.close_all
+ @output = parse_magic @encoder.output
+ end
+
+ def output
+ @output
+ end
+
+ # ok, internals start here
+ private
+ # the default output functions. everything should use them or the tags.
+ def add_text text = @scanner.matched
+ @encoder.add_text text
+ end
+
+ # use this carefully
+ def add_html html
+ @encoder.add_html html
+ end
+
+ # highlights the text as error
+ def add_garbage garbage
+ add_html '<span class="error">' if DEBUG
+ add_text garbage
+ add_html '</span>' if DEBUG
+ end
+
+ # unknown and incorrectly nested tags are ignored and
+ # sent as plaintext (garbage in - garbage out).
+ # in debug mode, garbage is marked with lime background.
+ def garbage_out start
+ @scanner.pos = start
+ garbage = @scanner.scan(/./m)
+ debug 'GARBAGE: ' + garbage
+ add_garbage garbage
+ end
+
+ # simple text; everything but [, \[ allowed
+ SIMPLE_TEXT_SCAN_ = /
+ [^\[\\]* # normal*
+ (?: # (
+ \\.? # special
+ [^\[\\]* # normal*
+ )* # )*
+ /mx
+ SIMPLE_TEXT_SCAN = /[^\[]+/
+
+=begin
+
+ WHAT IS A TAG?
+ ==============
+
+ Tags in BBCode can be much more than just a simple [b].
+ I use many terms here to differ the parts of each tag.
+
+ Basic scheme:
+ [ code ]
+ TAG START TAG INFO TAG END
+
+ Most tags need a second tag to close the range it opened.
+ This is done with CLOSING TAGS:
+ [/code]
+ or by using empty tags that have no content and close themselfes:
+ [url=winamp.com /]
+ You surely know this from HTML.
+ These slashes define the TAG KIND = normal|closing|empty and
+ cannot be used together.
+
+ Everything between [ and ] and expluding the slashes is called the
+ TAG INFO. This info may contain:
+ - TAG ID
+ - TAG NAME including the tag id
+ - attributes
+
+ The TAG ID is the first char of the info:
+
+ TAG | ID
+ ----------+----
+ [quote] | q
+ [&plusmn] | &
+ ["[b]"] | "
+ [/url] | u
+ [---] | -
+
+ As you can see, the tag id shows the TAG TYPE, it can be a
+ normal tag, a formatting tag or an entity.
+ Therefor, the parser first scans the id to decide how to go
+ on with parsing.
+=end
+ # tag
+ # TODO more complex expression allowing
+ # [quote="[ladico]"] and [quote=\[ladico\]] to be correct tags
+ TAG_BEGIN_SCAN = /
+ \[ # tag start
+ ( \/ )? # $1 = closing tag?
+ ( [^\]] ) # $2 = tag id
+ /x
+ TAG_END_SCAN = /
+ [^\]]* # rest that was not handled
+ \]? # tag end
+ /x
+ CLOSE_TAG_SCAN = /
+ ( [^\]]* ) # $1 = the rest of the tag info
+ ( \/ )? # $2 = empty tag?
+ \]? # tag end
+ /x
+ UNCLOSED_TAG_SCAN = / \[ /x
+
+ CLASSIC_TAG_SCAN = / [a-z]* /ix
+
+ SEPARATOR_TAG_SCAN = / \** /x
+
+ FORMAT_TAG_SCAN = / -- -* /x
+
+ QUOTED_SCAN = /
+ ( # $1 = quoted text
+ [^"\\]* # normal*
+ (?: # (
+ \\. # special
+ [^"\\]* # normal*
+ )* # )*
+ )
+ "? # end quote "
+ /mx
+
+ ENTITY_SCAN = /
+ ( [^;\]]+ ) # $1 = entity code
+ ;? # optional ending semicolon
+ /ix
+
+ SMILEY_SCAN = Smileys::SMILEY_PATTERN
+
+ # this is the main parser loop that separates
+ # text - everything until "["
+ # from
+ # tags - starting with "[", ending with "]"
+ def parse_token
+ if @scanner.scan(SIMPLE_TEXT_SCAN)
+ add_text
+ else
+ handle_tag
+ end
+ end
+
+ def handle_tag
+ tag_start = @scanner.pos
+
+ unless @scanner.scan TAG_BEGIN_SCAN
+ garbage_out tag_start
+ return
+ end
+
+ closing, id = @scanner[1], @scanner[2]
+ #debug 'handle_tag(%p)' % @scanner.matched
+
+ handled =
+ case id
+
+ when /[a-z]/i
+ if @scanner.scan(CLASSIC_TAG_SCAN)
+ if handle_classic_tag(id + @scanner.matched, closing)
+ already_closed = true
+ end
+ end
+
+ when '*'
+ if @scanner.scan(SEPARATOR_TAG_SCAN)
+ handle_asterisk tag_start, id + @scanner.matched
+ true
+ end
+
+ when '-'
+ if @scanner.scan(FORMAT_TAG_SCAN)
+ #format = id + @scanner.matched
+ @encoder.add_html "\n<hr>\n"
+ true
+ end
+
+ when '"'
+ if @scanner.scan(QUOTED_SCAN)
+ @encoder.add_text unescape(@scanner[1])
+ true
+ end
+
+ when '&'
+ if @scanner.scan(ENTITY_SCAN)
+ @encoder.add_entity @scanner[1]
+ true
+ end
+
+ when Smileys::SMILEY_START_CHARSET
+ @scanner.pos = @scanner.pos - 1 # (ungetch)
+ if @scanner.scan(SMILEY_SCAN)
+ @encoder.add_html Smileys.smiley_to_image(@scanner.matched)
+ true
+ end
+
+ end # case
+
+ return garbage_out(tag_start) unless handled
+
+ @scanner.scan(TAG_END_SCAN) unless already_closed
+ end
+
+ ATTRIBUTES_SCAN = /
+ (
+ [^\]"\\]*
+ (?:
+ (?:
+ \\.
+ |
+ "
+ [^"\\]*
+ (?:
+ \\.
+ [^"\\]*
+ )*
+ "?
+ )
+ [^\]"\\]*
+ )*
+ )
+ \]?
+ /x
+
+ def handle_classic_tag name, closing
+ debug 'TAG: ' + (closing ? '/' : '') + name
+ # flatten
+ name.downcase!
+ tag_class = TAG_LIST[name]
+ return unless tag_class
+
+ #debug((opening ? 'OPEN ' : 'CLOSE ') + tag_class.name)
+
+ # create an attribute object to handle it
+ @scanner.scan(ATTRIBUTES_SCAN)
+ #debug name + ':' + @scanner[1]
+ attr = Attribute.create @scanner[1]
+ #debug 'ATTRIBUTES %p ' % attr #unless attr.empty?
+
+ #debug 'closing: %p; name=%s, attr=%p' % [closing, name, attr]
+
+ # OPEN
+ if not closing and tag = @tagstack.try_open_class(tag_class, attr)
+ #debug 'opening'
+ tag.do_open @scanner
+ # this should be done by the tag itself.
+ if attr.empty_tag?
+ tag.handle_empty
+ @tagstack.close_tag
+ elsif tag.special_content?
+ handle_special_content(tag)
+ @tagstack.close_tag
+ # # ignore asterisks directly after the opening; these are phpBBCode
+ # elsif tag.respond_to? :asterisk
+ # debug 'SKIP ASTERISKS: ' if @scanner.skip(ASTERISK_TAGS_SCAN)
+ end
+
+ # CLOSE
+ elsif @tagstack.try_close_class(tag_class)
+ #debug 'closing'
+ # GARBAGE
+ else
+ return
+ end
+
+ true
+ end
+
+ def handle_asterisk tag_start, stars
+ #debug 'ASTERISK: ' + stars.to_s
+ # rule for asterisk tags: they belong to the last tag
+ # that handles them. tags opened after this tag are closed.
+ # if no open tag uses them, all are closed.
+ tag = @tagstack.close_all_until { |tag| tag.respond_to? :asterisk }
+ unless tag and tag.asterisk stars, @scanner
+ garbage_out tag_start
+ end
+ end
+
+ def handle_special_content tag
+ scanned = @scanner.scan_until(tag.closing_tag)
+ if scanned
+ scanned.slice!(-(@scanner.matched.size)..-1)
+ else
+ scanned = @scanner.scan(/.*/m).to_s
+ end
+ #debug 'SPECIAL CONTENT: ' + scanned
+ tag.handle_content(scanned)
+ end
+
+ def unescape text
+ # input: correctly formatted quoted string (without the quotes)
+ text.gsub(/\\(?:(["\\])|.)/) { $1 or $& }
+ end
+
+
+ # MAGIC FEAUTURES
+
+ URL_PATTERN = /(?:(?:www|ftp)\.|(?>\w{3,}):\/\/)\S+/
+ EMAIL_PATTERN = /(?>[\w\-_.]+)@[\w\-\.]+\.\w+/
+
+ HAS_MAGIC = /[&@#{Smileys::SMILEY_START_CHARS}]|(?i:www|ftp)/
+
+ MAGIC_PATTERN = Regexp.new('(\W|^)(%s)' %
+ [Smileys::MAGIC_SMILEY_PATTERN, URL_PATTERN, EMAIL_PATTERN].map { |pattern|
+ pattern.to_s
+ }.join('|') )
+
+ IS_SMILEY_PATTERN = Regexp.new('^%s' % Smileys::SMILEY_START_CHARSET.to_s )
+ IS_URL_PATTERN = /^(?:(?i:www|ftp)\.|(?>\w+):\/\/)/
+ URL_STARTS_WITH_PROTOCOL = /^\w+:\/\//
+ IS_EMAIL_PATTERN = /^[\w\-_.]+@/
+
+ def to_magic text
+ # debug MAGIC_PATTERN.to_s
+ text.gsub!(MAGIC_PATTERN) {
+ magic = $2
+ $1 + case magic
+ when IS_SMILEY_PATTERN
+ Smileys.smiley_to_img magic
+ when IS_URL_PATTERN
+ last = magic.slice_punctation! # no punctation in my URL
+ href = magic
+ href.insert(0, 'http://') unless magic =~ URL_STARTS_WITH_PROTOCOL
+ '<a href="' + href + '">' + magic + '</a>' + last
+ when IS_EMAIL_PATTERN
+ last = magic.slice_punctation!
+ '<a href="mailto:' + magic + '">' + magic + '</a>' + last
+ else
+ raise '{{{' + magic + '}}}'
+ end
+ }
+ text
+ end
+
+ # handles smileys and urls
+ def parse_magic html
+ return html unless @do_magic
+ scanner = StringScanner.new html
+ out = ''
+ while scanner.rest?
+ if scanner.scan(/ < (?: a\s .*? <\/a> | pre\W .*? <\/pre> | [^>]* > ) /mx)
+ out << scanner.matched
+ elsif scanner.scan(/ [^<]+ /x)
+ out << to_magic(scanner.matched)
+
+ # this should never happen
+ elsif scanner.scan(/./m)
+ raise 'ERROR: else case reached'
+ end
+ end
+ out
+ end
+ end # Parser
+end
+
+class String
+ def slice_punctation!
+ slice!(/[.:,!\?]+$/).to_s # return '' instead of nil
+ end
+end
+
+#
+# = Grammar
+#
+# An implementation of common algorithms on grammars.
+#
+# This is used by Shinobu, a visualization tool for educating compiler-building.
+#
+# Thanks to Andreas Kunert for his wonderful LR(k) Pamphlet (German, see http://www.informatik.hu-berlin.de/~kunert/papers/lr-analyse), and Aho/Sethi/Ullman for their Dragon Book.
+#
+# Homepage:: http://shinobu.cYcnus.de (not existing yet)
+# Author:: murphy (Kornelius Kalnbach)
+# Copyright:: (cc) 2005 cYcnus
+# License:: GPL
+# Version:: 0.2.0 (2005-03-27)
+
+require 'set_hash'
+require 'ctype'
+require 'tools'
+require 'rules'
+require 'trace'
+
+require 'first'
+require 'follow'
+
+# = Grammar
+#
+# == Syntax
+#
+# === Rules
+#
+# Each line is a rule.
+# The syntax is
+#
+# left - right
+#
+# where +left+ and +right+ can be uppercase and lowercase letters,
+# and <code>-</code> can be any combination of <, >, - or whitespace.
+#
+# === Symbols
+#
+# Uppercase letters stand for meta symbols, lowercase for terminals.
+#
+# You can make epsilon-derivations by leaving <code><right></code> empty.
+#
+# === Example
+# S - Ac
+# A - Sc
+# A - b
+# A -
+class Grammar
+
+ attr_reader :tracer
+ # Creates a new Grammar.
+ # If $trace is true, the algorithms explain (textual) what they do to $stdout.
+ def initialize data, tracer = Tracer.new
+ @tracer = tracer
+ @rules = Rules.new
+ @terminals, @meta_symbols = SortedSet.new, Array.new
+ @start_symbol = nil
+ add_rules data
+ end
+
+ attr_reader :meta_symbols, :terminals, :rules, :start_symbol
+
+ alias_method :sigma, :terminals
+ alias_method :alphabet, :terminals
+ alias_method :variables, :meta_symbols
+ alias_method :nonterminals, :meta_symbols
+
+ # A string representation of the grammar for debugging.
+ def inspect productions_too = false
+ 'Grammar(meta symbols: %s; alphabet: %s; productions: [%s]; start symbol: %s)' %
+ [
+ meta_symbols.join(', '),
+ terminals.join(', '),
+ if productions_too
+ @rules.inspect
+ else
+ @rules.size
+ end,
+ start_symbol
+ ]
+ end
+
+ # Add rules to the grammar. +rules+ should be a String or respond to +scan+ in a similar way.
+ #
+ # Syntax: see Grammar.
+ def add_rules grammar
+ @rules = Rules.parse grammar do |rule|
+ @start_symbol ||= rule.left
+ @meta_symbols << rule.left
+ @terminals.merge rule.right.split('').select { |s| terminal? s }
+ end
+ @meta_symbols.uniq!
+ update
+ end
+
+ # Returns a hash acting as FIRST operator, so that
+ # <code>first["ABC"]</code> is FIRST(ABC).
+ # See http://en.wikipedia.org/wiki/LL_parser "Constructing an LL(1) parsing table" for details.
+ def first
+ first_operator
+ end
+
+ # Returns a hash acting as FOLLOW operator, so that
+ # <code>first["A"]</code> is FOLLOW(A).
+ # See http://en.wikipedia.org/wiki/LL_parser "Constructing an LL(1) parsing table" for details.
+ def follow
+ follow_operator
+ end
+
+ LLError = Class.new(Exception)
+ LLErrorType1 = Class.new(LLError)
+ LLErrorType2 = Class.new(LLError)
+
+ # Tests if the grammar is LL(1).
+ def ll1?
+ begin
+ for meta in @meta_symbols
+ first_sets = @rules[meta].map { |alpha| first[alpha] }
+ first_sets.inject(Set[]) do |already_used, another_first_set|
+ unless already_used.disjoint? another_first_set
+ raise LLErrorType1
+ end
+ already_used.merge another_first_set
+ end
+
+ if first[meta].include? EPSILON and not first[meta].disjoint? follow[meta]
+ raise LLErrorType2
+ end
+ end
+ rescue LLError
+ false
+ else
+ true
+ end
+ end
+
+private
+
+ def first_operator
+ @first ||= FirstOperator.new self
+ end
+
+ def follow_operator
+ @follow ||= FollowOperator.new self
+ end
+
+ def update
+ @first = @follow = nil
+ end
+
+end
+
+if $0 == __FILE__
+ eval DATA.read, nil, $0, __LINE__+4
+end
+
+require 'test/unit'
+
+class TestCaseGrammar < Test::Unit::TestCase
+
+ include Grammar::Symbols
+
+ def fifo s
+ Set[*s.split('')]
+ end
+
+ def test_fifo
+ assert_equal Set[], fifo('')
+ assert_equal Set[EPSILON, END_OF_INPUT, 'x', 'Y'], fifo('?xY$')
+ end
+
+ TEST_GRAMMAR_1 = <<-EOG
+S - ABCD
+A - a
+A -
+B - b
+B -
+C - c
+C -
+D - S
+D -
+ EOG
+
+ def test_symbols
+ assert EPSILON
+ assert END_OF_INPUT
+ end
+
+ def test_first_1
+ g = Grammar.new TEST_GRAMMAR_1
+
+ f = nil
+ assert_nothing_raised { f = g.first }
+ assert_equal(Set['a', EPSILON], f['A'])
+ assert_equal(Set['b', EPSILON], f['B'])
+ assert_equal(Set['c', EPSILON], f['C'])
+ assert_equal(Set['a', 'b', 'c', EPSILON], f['D'])
+ assert_equal(f['D'], f['S'])
+ end
+
+ def test_follow_1
+ g = Grammar.new TEST_GRAMMAR_1
+
+ f = nil
+ assert_nothing_raised { f = g.follow }
+ assert_equal(Set['a', 'b', 'c', END_OF_INPUT], f['A'])
+ assert_equal(Set['a', 'b', 'c', END_OF_INPUT], f['B'])
+ assert_equal(Set['a', 'b', 'c', END_OF_INPUT], f['C'])
+ assert_equal(Set[END_OF_INPUT], f['D'])
+ assert_equal(Set[END_OF_INPUT], f['S'])
+ end
+
+
+ TEST_GRAMMAR_2 = <<-EOG
+S - Ed
+E - EpT
+E - EmT
+E - T
+T - TuF
+T - TdF
+T - F
+F - i
+F - n
+F - aEz
+ EOG
+
+ def test_first_2
+ g = Grammar.new TEST_GRAMMAR_2
+
+ f = nil
+ assert_nothing_raised { f = g.first }
+ assert_equal(Set['a', 'n', 'i'], f['E'])
+ assert_equal(Set['a', 'n', 'i'], f['F'])
+ assert_equal(Set['a', 'n', 'i'], f['T'])
+ assert_equal(Set['a', 'n', 'i'], f['S'])
+ end
+
+ def test_follow_2
+ g = Grammar.new TEST_GRAMMAR_2
+
+ f = nil
+ assert_nothing_raised { f = g.follow }
+ assert_equal(Set['m', 'd', 'z', 'p'], f['E'])
+ assert_equal(Set['m', 'd', 'z', 'p', 'u'], f['F'])
+ assert_equal(Set['m', 'd', 'z', 'p', 'u'], f['T'])
+ assert_equal(Set[END_OF_INPUT], f['S'])
+ end
+
+ LLError = Grammar::LLError
+
+ TEST_GRAMMAR_3 = <<-EOG
+E - TD
+D - pTD
+D -
+T - FS
+S - uFS
+S -
+S - p
+F - aEz
+F - i
+ EOG
+
+ NoError = Class.new(Exception)
+
+ def test_first_3
+ g = Grammar.new TEST_GRAMMAR_3
+
+ # Grammar 3 is LL(1), so all first-sets must be disjoint.
+ f = nil
+ assert_nothing_raised { f = g.first }
+ assert_equal(Set['a', 'i'], f['E'])
+ assert_equal(Set[EPSILON, 'p'], f['D'])
+ assert_equal(Set['a', 'i'], f['F'])
+ assert_equal(Set['a', 'i'], f['T'])
+ assert_equal(Set[EPSILON, 'u', 'p'], f['S'])
+ for m in g.meta_symbols
+ r = g.rules[m]
+ firsts = r.map { |x| f[x] }.to_set
+ assert_nothing_raised do
+ firsts.inject(Set.new) do |already_used, another_first_set|
+ raise LLError, 'not disjoint!' unless already_used.disjoint? another_first_set
+ already_used.merge another_first_set
+ end
+ end
+ end
+ end
+
+ def test_follow_3
+ g = Grammar.new TEST_GRAMMAR_3
+
+ # Grammar 3 is not LL(1), because epsilon is in FIRST(S),
+ # but FIRST(S) and FOLLOW(S) are not disjoint.
+ f = nil
+ assert_nothing_raised { f = g.follow }
+ assert_equal(Set['z', END_OF_INPUT], f['E'])
+ assert_equal(Set['z', END_OF_INPUT], f['D'])
+ assert_equal(Set['z', 'p', 'u', END_OF_INPUT], f['F'])
+ assert_equal(Set['p', 'z', END_OF_INPUT], f['T'])
+ assert_equal(Set['p', 'z', END_OF_INPUT], f['S'])
+ for m in g.meta_symbols
+ first_m = g.first[m]
+ next unless first_m.include? EPSILON
+ assert_raise(m == 'S' ? LLError : NoError) do
+ if first_m.disjoint? f[m]
+ raise NoError # this is fun :D
+ else
+ raise LLError
+ end
+ end
+ end
+ end
+
+ TEST_GRAMMAR_3b = <<-EOG
+E - TD
+D - pTD
+D - PTD
+D -
+T - FS
+S - uFS
+S -
+F - aEz
+F - i
+P - p
+ EOG
+
+ def test_first_3b
+ g = Grammar.new TEST_GRAMMAR_3b
+
+ # Grammar 3b is NOT LL(1), since not all first-sets are disjoint.
+ f = nil
+ assert_nothing_raised { f = g.first }
+ assert_equal(Set['a', 'i'], f['E'])
+ assert_equal(Set[EPSILON, 'p'], f['D'])
+ assert_equal(Set['p'], f['P'])
+ assert_equal(Set['a', 'i'], f['F'])
+ assert_equal(Set['a', 'i'], f['T'])
+ assert_equal(Set[EPSILON, 'u'], f['S'])
+ for m in g.meta_symbols
+ r = g.rules[m]
+ firsts = r.map { |x| f[x] }
+ assert_raise(m == 'D' ? LLError : NoError) do
+ firsts.inject(Set.new) do |already_used, another_first_set|
+ raise LLError, 'not disjoint!' unless already_used.disjoint? another_first_set
+ already_used.merge another_first_set
+ end
+ raise NoError
+ end
+ end
+ end
+
+ def test_follow_3b
+ g = Grammar.new TEST_GRAMMAR_3b
+
+ # Although Grammar 3b is NOT LL(1), the FOLLOW-condition is satisfied.
+ f = nil
+ assert_nothing_raised { f = g.follow }
+ assert_equal(fifo('z$'), f['E'], 'E')
+ assert_equal(fifo('z$'), f['D'], 'D')
+ assert_equal(fifo('ai'), f['P'], 'P')
+ assert_equal(fifo('z$pu'), f['F'], 'F')
+ assert_equal(fifo('z$p'), f['T'], 'T')
+ assert_equal(fifo('z$p'), f['S'], 'S')
+ for m in g.meta_symbols
+ first_m = g.first[m]
+ next unless first_m.include? EPSILON
+ assert_raise(NoError) do
+ if first_m.disjoint? f[m]
+ raise NoError # this is fun :D
+ else
+ raise LLError
+ end
+ end
+ end
+ end
+
+ def test_ll1?
+ assert_equal false, Grammar.new(TEST_GRAMMAR_3).ll1?, 'Grammar 3'
+ assert_equal false, Grammar.new(TEST_GRAMMAR_3b).ll1?, 'Grammar 3b'
+ end
+
+ def test_new
+ assert_nothing_raised { Grammar.new '' }
+ assert_nothing_raised { Grammar.new TEST_GRAMMAR_1 }
+ assert_nothing_raised { Grammar.new TEST_GRAMMAR_2 }
+ assert_nothing_raised { Grammar.new TEST_GRAMMAR_3 }
+ assert_nothing_raised { Grammar.new TEST_GRAMMAR_1 + TEST_GRAMMAR_2 + TEST_GRAMMAR_3 }
+ assert_raise(ArgumentError) { Grammar.new 'S - ?' }
+ end
+end
+
+# vim:foldmethod=syntax
+
+#!/usr/bin/env ruby
+
+require 'fox12'
+
+include Fox
+
+class Window < FXMainWindow
+ def initialize(app)
+ super(app, app.appName + ": First Set Calculation", nil, nil, DECOR_ALL, 0, 0, 800, 600, 0, 0)
+
+ # {{{ menubar
+ menubar = FXMenuBar.new(self, LAYOUT_SIDE_TOP|LAYOUT_FILL_X)
+
+ filemenu = FXMenuPane.new(self)
+
+ FXMenuCommand.new(filemenu, "&Start\tCtl-S\tStart the application.", nil, getApp()).connect(SEL_COMMAND, method(:start))
+ FXMenuCommand.new(filemenu, "&Quit\tAlt-F4\tQuit the application.", nil, getApp(), FXApp::ID_QUIT)
+ FXMenuTitle.new(menubar, "&File", nil, filemenu)
+ # }}} menubar
+
+ # {{{ statusbar
+ @statusbar = FXStatusBar.new(self, LAYOUT_SIDE_BOTTOM|LAYOUT_FILL_X|STATUSBAR_WITH_DRAGCORNER)
+ # }}} statusbar
+
+ # {{{ window content
+ horizontalsplitt = FXSplitter.new(self, SPLITTER_VERTICAL|LAYOUT_SIDE_TOP|LAYOUT_FILL)
+
+
+ @productions = FXList.new(horizontalsplitt, nil, 0, LAYOUT_SIDE_TOP|LAYOUT_FILL_X|LAYOUT_FIX_HEIGHT|LIST_SINGLESELECT)
+ @productions.height = 100
+
+ @result = FXTable.new(horizontalsplitt, nil, 0, LAYOUT_FILL)
+ @result.height = 200
+ @result.setTableSize(2, 2, false)
+ @result.rowHeaderWidth = 0
+
+ header = @result.columnHeader
+ header.setItemText 0, 'X'
+ header.setItemText 1, 'FIRST(X)'
+ for item in header
+ item.justification = FXHeaderItem::CENTER_X
+ end
+
+ @debug = FXText.new(horizontalsplitt, nil, 0, LAYOUT_SIDE_BOTTOM|LAYOUT_FILL_X|LAYOUT_FIX_HEIGHT)
+ @debug.height = 200
+
+ # }}} window content
+ end
+
+ def load_grammar grammar
+ @tracer = FirstTracer.new(self)
+ @grammar = Grammar.new grammar, @tracer
+ @rules_indexes = Hash.new
+ @grammar.rules.each_with_index do |rule, i|
+ @productions.appendItem rule.inspect
+ @rules_indexes[rule] = i
+ end
+ end
+
+ def create
+ super
+ show(PLACEMENT_SCREEN)
+ end
+
+ def rule rule
+ @productions.selectItem @rules_indexes[rule]
+ sleep 0.1
+ end
+
+ def iterate i
+ setTitle i.to_s
+ sleep 0.1
+ end
+
+ def missing what
+ @debug.appendText what + "\n"
+ sleep 0.1
+ end
+
+ def start sender, sel, pointer
+ Thread.new do
+ begin
+ @grammar.first
+ rescue => boom
+ @debug.appendText [boom.to_s, *boom.backtrace].join("\n")
+ end
+ end
+ end
+
+end
+
+$: << 'grammar'
+require 'grammar'
+
+require 'first_tracer'
+
+app = FXApp.new("Shinobu", "cYcnus")
+
+# fenster erzeugen
+window = Window.new app
+
+unless ARGV.empty?
+ grammar = File.read(ARGV.first)
+else
+ grammar = <<-EOG1
+Z --> S
+S --> Sb
+S --> bAa
+A --> aSc
+A --> a
+A --> aSb
+ EOG1
+end
+
+window.load_grammar grammar
+
+app.create
+app.run
+
+require 'erb'
+require 'ftools'
+require 'yaml'
+require 'redcloth'
+
+module WhyTheLuckyStiff
+ class Book
+ attr_accessor :author, :title, :terms, :image, :teaser,
+ :chapters, :expansion_paks, :encoding, :credits
+ def [] x
+ @lang.fetch(x) do
+ warn warning = "[not translated: '#{x}'!]"
+ warning
+ end
+ end
+ end
+
+ def Book::load( file_name )
+ YAML::load( File.open( file_name ) )
+ end
+
+ class Section
+ attr_accessor :index, :header, :content
+ def initialize( i, h, c )
+ @index, @header, @content = i, h, RedCloth::new( c.to_s )
+ end
+ end
+
+ class Sidebar
+ attr_accessor :title, :content
+ end
+
+ YAML::add_domain_type( 'whytheluckystiff.net,2003', 'sidebar' ) do |taguri, val|
+ YAML::object_maker( Sidebar, 'title' => val.keys.first, 'content' => RedCloth::new( val.values.first ) )
+ end
+ class Chapter
+ attr_accessor :index, :title, :sections
+ def initialize( i, t, sects )
+ @index = i
+ @title = t
+ i = 0
+ @sections = sects.collect do |s|
+ if s.respond_to?( :keys )
+ i += 1
+ Section.new( i, s.keys.first, s.values.first )
+ else
+ s
+ end
+ end
+ end
+ end
+
+ YAML::add_domain_type( 'whytheluckystiff.net,2003', 'book' ) do |taguri, val|
+ ['chapters', 'expansion_paks'].each do |chaptype|
+ i = 0
+ val[chaptype].collect! do |c|
+ i += 1
+ Chapter::new( i, c.keys.first, c.values.first )
+ end
+ end
+ val['teaser'].collect! do |t|
+ Section::new( 1, t.keys.first, t.values.first )
+ end
+ val['terms'] = RedCloth::new( val['terms'] )
+ YAML::object_maker( Book, val )
+ end
+
+ class Image
+ attr_accessor :file_name
+ end
+
+ YAML::add_domain_type( 'whytheluckystiff.net,2003', 'img' ) do |taguri, val|
+ YAML::object_maker( Image, 'file_name' => "i/" + val )
+ end
+end
+
+#
+# Convert the book to HTML
+#
+if __FILE__ == $0
+ unless ARGV[0]
+ puts "Usage: #{$0} [/path/to/save/html]"
+ exit
+ end
+
+ site_path = ARGV[0]
+ book = WhyTheLuckyStiff::Book::load( 'poignant.yml' )
+ chapter = nil
+
+ # Write index page
+ index_tpl = ERB::new( File.open( 'index.erb' ).read )
+ File.open( File.join( site_path, 'index.html' ), 'w' ) do |out|
+ out << index_tpl.result
+ end
+
+ book.chapters = book.chapters[0,3] if ARGV.include? '-fast'
+
+ # Write chapter pages
+ chapter_tpl = ERB::new( File.open( 'chapter.erb' ).read )
+ book.chapters.each do |chapter|
+ File.open( File.join( site_path, "chapter-#{ chapter.index }.html" ), 'w' ) do |out|
+ out << chapter_tpl.result
+ end
+ end
+ exit if ARGV.include? '-fast'
+
+ # Write expansion pak pages
+ expak_tpl = ERB::new( File.open( 'expansion-pak.erb' ).read )
+ book.expansion_paks.each do |pak|
+ File.open( File.join( site_path, "expansion-pak-#{ pak.index }.html" ), 'w' ) do |out|
+ out << expak_tpl.result( binding )
+ end
+ end
+
+ # Write printable version
+ print_tpl = ERB::new( File.open( 'print.erb' ).read )
+ File.open( File.join( site_path, "print.html" ), 'w' ) do |out|
+ out << print_tpl.result
+ end
+
+ # Copy css + images into site
+ copy_list = ["guide.css"] +
+ Dir["i/*"].find_all { |image| image =~ /\.(gif|jpg|png)$/ }
+
+ File.makedirs( File.join( site_path, "i" ) )
+ copy_list.each do |copy_file|
+ File.copy( copy_file, File.join( site_path, copy_file ) )
+ end
+end
+
+#!/usr/bin/env ruby
+
+require 'fox'
+begin
+ require 'opengl'
+rescue LoadError
+ require 'fox/missingdep'
+ MSG = <<EOM
+ Sorry, this example depends on the OpenGL extension. Please
+ check the Ruby Application Archives for an appropriate
+ download site.
+EOM
+ missingDependency(MSG)
+end
+
+
+include Fox
+include Math
+
+Deg2Rad = Math::PI / 180
+
+D_MAX = 6
+SQUARE_SIZE = 2.0 / D_MAX
+SQUARE_DISTANCE = 4.0 / D_MAX
+AMPLITUDE = SQUARE_SIZE
+LAMBDA = D_MAX.to_f / 2
+
+class GLTestWindow < FXMainWindow
+
+ # How often our timer will fire (in milliseconds)
+ TIMER_INTERVAL = 500
+
+ # Rotate the boxes when a timer message is received
+ def onTimeout(sender, sel, ptr)
+ @angle += 10.0
+# @size = 0.5 + 0.2 * Math.cos(Deg2Rad * @angle)
+ drawScene()
+ @timer = getApp().addTimeout(TIMER_INTERVAL, method(:onTimeout))
+ end
+
+ # Rotate the boxes when a chore message is received
+ def onChore(sender, sel, ptr)
+ @angle += 10.0
+# @angle %= 360.0
+# @size = 0.5 + 0.2 * Math.cos(Deg2Rad * @angle)
+ drawScene()
+ @chore = getApp().addChore(method(:onChore))
+ end
+
+ # Draw the GL scene
+ def drawScene
+ lightPosition = [15.0, 10.0, 5.0, 1.0]
+ lightAmbient = [ 0.1, 0.1, 0.1, 1.0]
+ lightDiffuse = [ 0.9, 0.9, 0.9, 1.0]
+ redMaterial = [ 0.0, 0.0, 1.0, 1.0]
+ blueMaterial = [ 0.0, 1.0, 0.0, 1.0]
+
+ width = @glcanvas.width.to_f
+ height = @glcanvas.height.to_f
+ aspect = width/height
+
+ # Make context current
+ @glcanvas.makeCurrent()
+
+ GL.Viewport(0, 0, @glcanvas.width, @glcanvas.height)
+
+ GL.ClearColor(1.0/256, 0.0, 5.0/256, 1.0)
+ GL.Clear(GL::COLOR_BUFFER_BIT|GL::DEPTH_BUFFER_BIT)
+ GL.Enable(GL::DEPTH_TEST)
+
+ GL.Disable(GL::DITHER)
+
+ GL.MatrixMode(GL::PROJECTION)
+ GL.LoadIdentity()
+ GLU.Perspective(30.0, aspect, 1.0, 100.0)
+
+ GL.MatrixMode(GL::MODELVIEW)
+ GL.LoadIdentity()
+ GLU.LookAt(5.0, 10.0, 15.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0)
+
+ GL.ShadeModel(GL::SMOOTH)
+ GL.Light(GL::LIGHT0, GL::POSITION, lightPosition)
+ GL.Light(GL::LIGHT0, GL::AMBIENT, lightAmbient)
+ GL.Light(GL::LIGHT0, GL::DIFFUSE, lightDiffuse)
+ GL.Enable(GL::LIGHT0)
+ GL.Enable(GL::LIGHTING)
+
+ GL.Rotated(0.1*@angle, 0.0, 1.0, 0.0)
+ for x in -D_MAX..D_MAX
+ for y in -D_MAX..D_MAX
+ h1 = (x + y - 2).abs
+ h2 = (y - x + 1).abs
+ GL.PushMatrix
+ c = [1, 0, 0, 1]
+ GL.Material(GL::FRONT, GL::AMBIENT, c)
+ GL.Material(GL::FRONT, GL::DIFFUSE, c)
+
+ GL.Translated(
+ y * SQUARE_DISTANCE,
+ AMPLITUDE * h1,
+ x * SQUARE_DISTANCE
+ )
+
+ GL.Begin(GL::TRIANGLE_STRIP)
+ GL.Normal(1.0, 0.0, 0.0)
+ GL.Vertex(-SQUARE_SIZE, +SQUARE_SIZE, -SQUARE_SIZE)
+ GL.Vertex(-SQUARE_SIZE, +SQUARE_SIZE, +SQUARE_SIZE)
+ GL.Vertex(+SQUARE_SIZE, +SQUARE_SIZE, -SQUARE_SIZE)
+ GL.Vertex(+SQUARE_SIZE, +SQUARE_SIZE, +SQUARE_SIZE)
+ GL.End
+
+ GL.PopMatrix
+
+ GL.PushMatrix
+ c = [0, 0, 1, 1]
+ GL.Material(GL::FRONT, GL::AMBIENT, c)
+ GL.Material(GL::FRONT, GL::DIFFUSE, c)
+
+ GL.Translated(
+ y * SQUARE_DISTANCE,
+ AMPLITUDE * h2,
+ x * SQUARE_DISTANCE
+ )
+
+ GL.Begin(GL::TRIANGLE_STRIP)
+ GL.Normal(1.0, 0.0, 0.0)
+ GL.Vertex(-SQUARE_SIZE, +SQUARE_SIZE, -SQUARE_SIZE)
+ GL.Vertex(-SQUARE_SIZE, +SQUARE_SIZE, +SQUARE_SIZE)
+ GL.Vertex(+SQUARE_SIZE, +SQUARE_SIZE, -SQUARE_SIZE)
+ GL.Vertex(+SQUARE_SIZE, +SQUARE_SIZE, +SQUARE_SIZE)
+ GL.End
+
+ GL.PopMatrix
+
+ GL.PushMatrix
+ c = [0.0 + (x/10.0), 0.0 + (y/10.0), 0, 1]
+ GL.Material(GL::FRONT, GL::AMBIENT, c)
+ GL.Material(GL::FRONT, GL::DIFFUSE, c)
+
+ GL.Translated(
+ y * SQUARE_DISTANCE,
+ 0,
+ x * SQUARE_DISTANCE
+ )
+
+ GL.Begin(GL::TRIANGLE_STRIP)
+ GL.Normal(1.0, 0.0, 0.0)
+ GL.Vertex(-SQUARE_SIZE, +SQUARE_SIZE, -SQUARE_SIZE)
+ GL.Vertex(-SQUARE_SIZE, +SQUARE_SIZE, +SQUARE_SIZE)
+ GL.Vertex(+SQUARE_SIZE, +SQUARE_SIZE, -SQUARE_SIZE)
+ GL.Vertex(+SQUARE_SIZE, +SQUARE_SIZE, +SQUARE_SIZE)
+ GL.End
+
+ GL.PopMatrix
+ end
+ end
+
+ # Swap if it is double-buffered
+ if @glvisual.isDoubleBuffer
+ @glcanvas.swapBuffers
+ end
+
+ # Make context non-current
+ @glcanvas.makeNonCurrent
+ end
+
+ def initialize(app)
+ # Invoke the base class initializer
+ super(app, "OpenGL Test Application", nil, nil, DECOR_ALL, 0, 0, 1024, 768)
+
+ # Construct the main window elements
+ frame = FXHorizontalFrame.new(self, LAYOUT_SIDE_TOP|LAYOUT_FILL_X|LAYOUT_FILL_Y)
+ frame.padLeft, frame.padRight = 0, 0
+ frame.padTop, frame.padBottom = 0, 0
+
+ # Left pane to contain the glcanvas
+ glcanvasFrame = FXVerticalFrame.new(frame,
+ LAYOUT_FILL_X|LAYOUT_FILL_Y|LAYOUT_TOP|LAYOUT_LEFT)
+ glcanvasFrame.padLeft, glcanvasFrame.padRight = 10, 10
+ glcanvasFrame.padTop, glcanvasFrame.padBottom = 10, 10
+
+ # Label above the glcanvas
+ FXLabel.new(glcanvasFrame, "OpenGL Canvas Frame", nil,
+ JUSTIFY_CENTER_X|LAYOUT_FILL_X)
+
+ # Horizontal divider line
+ FXHorizontalSeparator.new(glcanvasFrame, SEPARATOR_GROOVE|LAYOUT_FILL_X)
+
+ # Drawing glcanvas
+ glpanel = FXVerticalFrame.new(glcanvasFrame, (FRAME_SUNKEN|FRAME_THICK|
+ LAYOUT_FILL_X|LAYOUT_FILL_Y|LAYOUT_TOP|LAYOUT_LEFT))
+ glpanel.padLeft, glpanel.padRight = 0, 0
+ glpanel.padTop, glpanel.padBottom = 0, 0
+
+ # A visual to draw OpenGL
+ @glvisual = FXGLVisual.new(getApp(), VISUAL_DOUBLEBUFFER)
+
+ # Drawing glcanvas
+ @glcanvas = FXGLCanvas.new(glpanel, @glvisual, nil, 0,
+ LAYOUT_FILL_X|LAYOUT_FILL_Y|LAYOUT_TOP|LAYOUT_LEFT)
+ @glcanvas.connect(SEL_PAINT) {
+ drawScene
+ }
+ @glcanvas.connect(SEL_CONFIGURE) {
+ if @glcanvas.makeCurrent
+ GL.Viewport(0, 0, @glcanvas.width, @glcanvas.height)
+ @glcanvas.makeNonCurrent
+ end
+ }
+
+ # Right pane for the buttons
+ buttonFrame = FXVerticalFrame.new(frame, LAYOUT_FILL_Y|LAYOUT_TOP|LAYOUT_LEFT)
+ buttonFrame.padLeft, buttonFrame.padRight = 10, 10
+ buttonFrame.padTop, buttonFrame.padBottom = 10, 10
+
+ # Label above the buttons
+ FXLabel.new(buttonFrame, "Button Frame", nil,
+ JUSTIFY_CENTER_X|LAYOUT_FILL_X)
+
+ # Horizontal divider line
+ FXHorizontalSeparator.new(buttonFrame, SEPARATOR_RIDGE|LAYOUT_FILL_X)
+
+ # Spin according to timer
+ spinTimerBtn = FXButton.new(buttonFrame,
+ "Spin &Timer\tSpin using interval timers\nNote the app
+ blocks until the interal has elapsed...", nil,
+ nil, 0, FRAME_THICK|FRAME_RAISED|LAYOUT_FILL_X|LAYOUT_TOP|LAYOUT_LEFT)
+ spinTimerBtn.padLeft, spinTimerBtn.padRight = 10, 10
+ spinTimerBtn.padTop, spinTimerBtn.padBottom = 5, 5
+ spinTimerBtn.connect(SEL_COMMAND) {
+ @spinning = true
+ @timer = getApp().addTimeout(TIMER_INTERVAL, method(:onTimeout))
+ }
+ spinTimerBtn.connect(SEL_UPDATE) { |sender, sel, ptr|
+ @spinning ? sender.disable : sender.enable
+ }
+
+ # Spin according to chore
+ spinChoreBtn = FXButton.new(buttonFrame,
+ "Spin &Chore\tSpin as fast as possible using chores\nNote even though the
+ app is very responsive, it never blocks;\nthere is always something to
+ do...", nil,
+ nil, 0, FRAME_THICK|FRAME_RAISED|LAYOUT_FILL_X|LAYOUT_TOP|LAYOUT_LEFT)
+ spinChoreBtn.padLeft, spinChoreBtn.padRight = 10, 10
+ spinChoreBtn.padTop, spinChoreBtn.padBottom = 5, 5
+ spinChoreBtn.connect(SEL_COMMAND) {
+ @spinning = true
+ @chore = getApp().addChore(method(:onChore))
+ }
+ spinChoreBtn.connect(SEL_UPDATE) { |sender, sel, ptr|
+ @spinning ? sender.disable : sender.enable
+ }
+
+ # Stop spinning
+ stopBtn = FXButton.new(buttonFrame,
+ "&Stop Spin\tStop this mad spinning, I'm getting dizzy", nil,
+ nil, 0, FRAME_THICK|FRAME_RAISED|LAYOUT_FILL_X|LAYOUT_TOP|LAYOUT_LEFT)
+ stopBtn.padLeft, stopBtn.padRight = 10, 10
+ stopBtn.padTop, stopBtn.padBottom = 5, 5
+ stopBtn.connect(SEL_COMMAND) {
+ @spinning = false
+ if @timer
+ getApp().removeTimeout(@timer)
+ @timer = nil
+ end
+ if @chore
+ getApp().removeChore(@chore)
+ @chore = nil
+ end
+ }
+ stopBtn.connect(SEL_UPDATE) { |sender, sel, ptr|
+ @spinning ? sender.enable : sender.disable
+ }
+
+ # Exit button
+ exitBtn = FXButton.new(buttonFrame, "&Exit\tExit the application", nil,
+ getApp(), FXApp::ID_QUIT,
+ FRAME_THICK|FRAME_RAISED|LAYOUT_FILL_X|LAYOUT_TOP|LAYOUT_LEFT)
+ exitBtn.padLeft, exitBtn.padRight = 10, 10
+ exitBtn.padTop, exitBtn.padBottom = 5, 5
+
+ # Make a tooltip
+ FXTooltip.new(getApp())
+
+ # Initialize private variables
+ @spinning = false
+ @chore = nil
+ @timer = nil
+ @angle = 0.0
+ @size = 0.5
+ end
+
+ # Create and initialize
+ def create
+ super
+ show(PLACEMENT_SCREEN)
+ end
+end
+
+if __FILE__ == $0
+ # Construct the application
+ application = FXApp.new("GLTest", "FoxTest")
+
+ # To ensure that the chores-based spin will run as fast as possible,
+ # we can disable the chore in FXRuby's event loop that tries to schedule
+ # other threads. This is OK for this program because there aren't any
+ # other Ruby threads running.
+
+ #application.disableThreads
+
+ # Construct the main window
+ GLTestWindow.new(application)
+
+ # Create the app's windows
+ application.create
+
+ # Run the application
+ application.run
+end
+
+class Facelet
+ attr_accessor :color
+ def initialize(color)
+ @color = color
+ end
+
+ def to_s
+ @color
+ end
+end
+
+class Edge
+ attr_accessor :facelets, :colors
+
+ def initialize(facelets)
+ @facelets = facelets
+ @colors = @facelets.map { |fl| fl.color }
+ end
+
+ def apply(edge)
+ @facelets.each_with_index { |fl, i|
+ fl.color = edge.colors[i]
+ }
+ end
+
+ def inspect
+ "\n%s %s\n%s %s %s" % facelets
+ end
+end
+
+class Side
+ attr_reader :num, :facelets
+ attr_accessor :sides
+
+ def initialize(num)
+ @num = num
+ @sides = []
+ @facelets = []
+ @fl_by_side = {}
+ end
+
+ # facelets & sides
+ # 0
+ # 0 1 2
+ # 3 3 4 5 1
+ # 6 7 8
+ # 2
+
+ def facelets=(facelets)
+ @facelets = facelets.map { |c| Facelet.new(c) }
+ init_facelet 0, 3,0
+ init_facelet 1, 0
+ init_facelet 2, 0,1
+ init_facelet 3, 3
+ init_facelet 5, 1
+ init_facelet 6, 2,3
+ init_facelet 7, 2
+ init_facelet 8, 1,2
+ end
+
+ def <=>(side)
+ self.num <=> side.num
+ end
+
+ def init_facelet(pos, *side_nums)
+ sides = side_nums.map { |num| @sides[num] }.sort
+ @fl_by_side[sides] = pos
+ end
+
+ def []=(color, *sides)
+ @facelets[@fl_by_side[sides.sort]].color = color
+ end
+
+ def values_at(*sides)
+ sides.map { |sides| @facelets[@fl_by_side[sides.sort]] }
+ end
+
+ def inspect(range=nil)
+ if range
+ @facelets.values_at(*(range.to_a)).join(' ')
+ else
+ <<-EOS.gsub(/\d/) { |num| @facelets[num.to_i] }.gsub(/[ABCD]/) { |side| @sides[side[0]-?A].num.to_s }
+ A
+ 0 1 2
+ D 3 4 5 B
+ 6 7 8
+ C
+ EOS
+ end
+ end
+
+ def get_edge(side)
+ trio = (-1..1).map { |x| (side + x) % 4 }
+ prev_side, this_side, next_side = @sides.values_at(*trio)
+ e = Edge.new(
+ self .values_at( [this_side], [this_side, next_side] ) +
+ this_side.values_at( [self, prev_side], [self ], [self, next_side] )
+ )
+ #puts 'Edge created for side %d: ' % side + e.inspect
+ e
+ end
+
+ def turn(dir)
+ #p 'turn side %d in %d' % [num, dir]
+ edges = (0..3).map { |n| get_edge n }
+ for i in 0..3
+ edges[i].apply edges[(i-dir) % 4]
+ end
+ end
+end
+
+class Cube
+ def initialize
+ @sides = []
+ %w(left front right back top bottom).each_with_index { |side, i|
+ eval("@sides[#{i}] = @#{side} = Side.new(#{i})")
+ }
+ @left.sides = [@top, @front, @bottom, @back]
+ @front.sides = [@top, @right, @bottom, @left]
+ @right.sides = [@top, @back, @bottom, @front]
+ @back.sides = [@top, @left, @bottom, @right]
+ @top.sides = [@back, @right, @front, @left]
+ @bottom.sides = [@front, @right, @back, @left]
+ end
+
+ def read_facelets(fs)
+ pattern = Regexp.new(<<-EOP.gsub(/\w/, '\w').gsub(/\s+/, '\s*'))
+ (w w w)
+ (w w w)
+ (w w w)
+(r r r) (g g g) (b b b) (o o o)
+(r r r) (g g g) (b b b) (o o o)
+(r r r) (g g g) (b b b) (o o o)
+ (y y y)
+ (y y y)
+ (y y y)
+ EOP
+ md = pattern.match(fs).to_a
+
+ @top.facelets = parse_facelets(md.values_at(1,2,3))
+ @left.facelets = parse_facelets(md.values_at(4,8,12))
+ @front.facelets = parse_facelets(md.values_at(5,9,13))
+ @right.facelets = parse_facelets(md.values_at(6,10,14))
+ @back.facelets = parse_facelets(md.values_at(7,11,15))
+ @bottom.facelets = parse_facelets(md.values_at(16,17,18))
+ end
+
+ def turn(side, dir)
+ #p 'turn %d in %d' % [side, dir]
+ @sides[side].turn(dir)
+ #puts inspect
+ end
+
+ def inspect
+ <<-EOF.gsub(/(\d):(\d)-(\d)/) { @sides[$1.to_i].inspect(Range.new($2.to_i, $3.to_i)) }
+ 4:0-2
+ 4:3-5
+ 4:6-8
+0:0-2 1:0-2 2:0-2 3:0-2
+0:3-5 1:3-5 2:3-5 3:3-5
+0:6-8 1:6-8 2:6-8 3:6-8
+ 5:0-2
+ 5:3-5
+ 5:6-8
+ EOF
+ end
+
+private
+ def parse_facelets(rows)
+ rows.join.delete(' ').split(//)
+ end
+end
+
+#$stdin = DATA
+
+gets.to_i.times do |i|
+ puts "Scenario ##{i+1}:"
+ fs = ''
+ 9.times { fs << gets }
+ cube = Cube.new
+ cube.read_facelets fs
+ gets.to_i.times do |t|
+ side, dir = gets.split.map {|s| s.to_i}
+ cube.turn(side, dir)
+ end
+ puts cube.inspect
+ puts
+end
+
+# 2004 by murphy <korny@cYcnus.de>
+# GPL
+class Scenario
+ class TimePoint
+ attr_reader :data
+ def initialize *data
+ @data = data
+ end
+
+ def [] i
+ @data[i] or 0
+ end
+
+ include Comparable
+ def <=> tp
+ r = 0
+ [@data.size, tp.data.size].max.times do |i|
+ r = self[i] <=> tp[i]
+ return r if r.nonzero?
+ end
+ 0
+ end
+
+ def - tp
+ r = []
+ [@data.size, tp.data.size].max.times do |i|
+ r << self[i] - tp[i]
+ end
+ r
+ end
+
+ def inspect
+ # 01/01/1800 00:00:00
+ '%02d/%02d/%04d %02d:%02d:%02d' % @data.values_at(1, 2, 0, 3, 4, 5)
+ end
+ end
+
+ ONE_HOUR = TimePoint.new 0, 0, 0, 1, 0, 0
+
+ APPOINTMENT_PATTERN = /
+ ( \d{4} ) \s ( \d{2} ) \s ( \d{2} ) \s ( \d{2} ) \s ( \d{2} ) \s ( \d{2} ) \s
+ ( \d{4} ) \s ( \d{2} ) \s ( \d{2} ) \s ( \d{2} ) \s ( \d{2} ) \s ( \d{2} )
+ /x
+
+ def initialize io
+ @team_size = io.gets.to_i
+ @data = [ [TimePoint.new(1800, 01, 01, 00, 00, 00), @team_size] ]
+ @team_size.times do # each team member
+ io.gets.to_i.times do # each appointment
+ m = APPOINTMENT_PATTERN.match io.gets
+ @data << [TimePoint.new(*m.captures[0,6].map { |x| x.to_i }), -1]
+ @data << [TimePoint.new(*m.captures[6,6].map { |x| x.to_i }), +1]
+ end
+ end
+ @data << [TimePoint.new(2200, 01, 01, 00, 00, 00), -@team_size]
+ end
+
+ def print_time_plan
+ n = 0
+ appointment = nil
+ no_appointment = true
+ @data.sort_by { |x| x[0] }.each do |x|
+ tp, action = *x
+ n += action
+ # at any time during the meeting, at least two team members need to be there
+ # and at most one team member is allowed to be absent
+ if n >= 2 and (@team_size - n) <= 1
+ appointment ||= tp
+ else
+ if appointment
+ # the meeting should be at least one hour in length
+ if TimePoint.new(*(tp - appointment)) >= ONE_HOUR
+ puts 'appointment possible from %p to %p' % [appointment, tp]
+ no_appointment = false
+ end
+ appointment = false
+ end
+ end
+ end
+ puts 'no appointment possible' if no_appointment
+ end
+end
+
+# read the data
+DATA.gets.to_i.times do |si| # each scenario
+ puts 'Scenario #%d:' % (si + 1)
+ sc = Scenario.new DATA
+ sc.print_time_plan
+ puts
+end
+
+#__END__
+2
+3
+3
+2002 06 28 15 00 00 2002 06 28 18 00 00 TUD Contest Practice Session
+2002 06 29 10 00 00 2002 06 29 15 00 00 TUD Contest
+2002 11 15 15 00 00 2002 11 17 23 00 00 NWERC Delft
+4
+2002 06 25 13 30 00 2002 06 25 15 30 00 FIFA World Cup Semifinal I
+2002 06 26 13 30 00 2002 06 26 15 30 00 FIFA World Cup Semifinal II
+2002 06 29 13 00 00 2002 06 29 15 00 00 FIFA World Cup Third Place
+2002 06 30 13 00 00 2002 06 30 15 00 00 FIFA World Cup Final
+1
+2002 06 01 00 00 00 2002 06 29 18 00 00 Preparation of Problem Set
+2
+1
+1800 01 01 00 00 00 2200 01 01 00 00 00 Solving Problem 8
+0
+
+require 'token_consts'
+require 'symbol'
+require 'ctype'
+require 'error'
+
+class Fixnum
+ # Treat char as a digit and return it's value as Fixnum.
+ # Returns nonsense for non-digits.
+ # Examples:
+ # <code>
+ # RUBY_VERSION[0].digit == '1.8.2'[0].digit == 1
+ # </code>
+ #
+ # <code>
+ # ?6.digit == 6
+ # </code>
+ #
+ # <code>
+ # ?A.digit == 17
+ # </code>
+ def digit
+ self - ?0
+ end
+end
+
+##
+# Stellt einen einfachen Scanner für die lexikalische Analyse der Sprache Pas-0 dar.
+#
+# @author Andreas Kunert
+# Ruby port by murphy
+class Scanner
+
+ include TokenConsts
+
+ attr_reader :line, :pos
+
+ # To allow Scanner.new without parameters.
+ DUMMY_INPUT = 'dummy file'
+ def DUMMY_INPUT.getc
+ nil
+ end
+
+ ##
+ # Erzeugt einen Scanner, der als Eingabe das übergebene IO benutzt.
+ def initialize input = DUMMY_INPUT
+ @line = 1
+ @pos = 0
+
+ begin
+ @input = input
+ @next_char = @input.getc
+ rescue IOError # TODO show the reason!
+ Error.ioError
+ raise
+ end
+ end
+
+ ##
+ # Liest das n
+ def read_next_char
+ begin
+ @pos += 1
+ @current_char = @next_char
+ @next_char = @input.getc
+ rescue IOError
+ Error.ioError
+ raise
+ end
+ end
+
+ ##
+ # Sucht das nächste Symbol, identifiziert es, instantiiert ein entsprechendes
+ # PascalSymbol-Objekt und gibt es zurück.
+ # @see Symbol
+ # @return das gefundene Symbol als PascalSymbol-Objekt
+ def get_symbol
+ current_symbol = nil
+ until current_symbol
+ read_next_char
+
+ if @current_char.alpha?
+ identifier = @current_char.chr
+ while @next_char.alpha? or @next_char.digit?
+ identifier << @next_char
+ read_next_char
+ end
+ current_symbol = handle_identifier(identifier.upcase)
+ elsif @current_char.digit?
+ current_symbol = number
+ else
+ case @current_char
+ when ?\s
+ # ignore
+ when ?\n
+ new_line
+ when nil
+ current_symbol = PascalSymbol.new EOP
+ when ?{
+ comment
+
+ when ?:
+ if @next_char == ?=
+ read_next_char
+ current_symbol = PascalSymbol.new BECOMES
+ else
+ current_symbol = PascalSymbol.new COLON
+ end
+
+ when ?<
+ if (@next_char == ?=)
+ read_next_char
+ current_symbol = PascalSymbol.new LEQSY
+ elsif (@next_char == ?>)
+ read_next_char
+ current_symbol = PascalSymbol.new NEQSY
+ else
+ current_symbol = PascalSymbol.new LSSSY
+ end
+
+ when ?>
+ if (@next_char == ?=)
+ read_next_char
+ current_symbol = PascalSymbol.new GEQSY
+ else
+ current_symbol = PascalSymbol.new GRTSY
+ end
+
+ when ?. then current_symbol = PascalSymbol.new PERIOD
+ when ?( then current_symbol = PascalSymbol.new LPARENT
+ when ?, then current_symbol = PascalSymbol.new COMMA
+ when ?* then current_symbol = PascalSymbol.new TIMES
+ when ?/ then current_symbol = PascalSymbol.new SLASH
+ when ?+ then current_symbol = PascalSymbol.new PLUS
+ when ?- then current_symbol = PascalSymbol.new MINUS
+ when ?= then current_symbol = PascalSymbol.new EQLSY
+ when ?) then current_symbol = PascalSymbol.new RPARENT
+ when ?; then current_symbol = PascalSymbol.new SEMICOLON
+ else
+ Error.error(100, @line, @pos) if @current_char > ?\s
+ end
+ end
+ end
+ current_symbol
+ end
+
+private
+ ##
+ # Versucht, in dem gegebenen String ein Schlüsselwort zu erkennen.
+ # Sollte dabei ein Keyword gefunden werden, so gibt er ein PascalSymbol-Objekt zurück, das
+ # das entsprechende Keyword repräsentiert. Ansonsten besteht die Rückgabe aus
+ # einem SymbolIdent-Objekt (abgeleitet von PascalSymbol), das den String 1:1 enthält
+ # @see symbol
+ # @return falls Keyword gefunden, zugehöriges PascalSymbol, sonst SymbolIdent
+ def handle_identifier identifier
+ if sym = KEYWORD_SYMBOLS[identifier]
+ PascalSymbol.new sym
+ else
+ SymbolIdent.new identifier
+ end
+ end
+
+ MAXINT = 2**31 - 1
+ MAXINT_DIV_10 = MAXINT / 10
+ MAXINT_MOD_10 = MAXINT % 10
+ ##
+ # Versucht, aus dem gegebenen Zeichen und den folgenden eine Zahl zusammenzusetzen.
+ # Dabei wird der relativ intuitive Algorithmus benutzt, die endgültige Zahl bei
+ # jeder weiteren Ziffer mit 10 zu multiplizieren und diese dann mit der Ziffer zu
+ # addieren. Sonderfälle bestehen dann nur noch in der Behandlung von reellen Zahlen.
+ # <BR>
+ # Treten dabei kein Punkt oder ein E auf, so gibt diese Methode ein SymbolIntCon-Objekt
+ # zurück, ansonsten (reelle Zahl) ein SymbolRealCon-Objekt. Beide Symbole enthalten
+ # jeweils die Zahlwerte.
+ # <BR>
+ # Anmerkung: Diese Funktion ist mit Hilfe der Java/Ruby-API deutlich leichter zu realisieren.
+ # Sie wurde dennoch so implementiert, um den Algorithmus zu demonstrieren
+ # @see symbol
+ # @return SymbolIntcon- oder SymbolRealcon-Objekt, das den Zahlwert enthält
+ def number
+ is_integer = true
+ integer_too_long = false
+ exponent = 0
+ exp_counter = -1
+ exp_sign = 1
+
+ integer_mantisse = @current_char.digit
+
+ while (@next_char.digit? and integer_mantisse < MAXINT_DIV_10) or
+ (integer_mantisse == MAXINT_DIV_10 and @next_char.digit <= MAXINT_MOD_10)
+ integer_mantisse *= 10
+ integer_mantisse += @next_char.digit
+ read_next_char
+ end
+
+ real_mantisse = integer_mantisse
+
+ while @next_char.digit?
+ integer_too_long = true
+ real_mantisse *= 10
+ real_mantisse += @next_char.digit
+ read_next_char
+ end
+ if @next_char == ?.
+ read_next_char
+ is_integer = false
+ unless @next_char.digit?
+ Error.error 101, @line, @pos
+ end
+ while @next_char.digit?
+ real_mantisse += @next_char.digit * (10 ** exp_counter)
+ read_next_char
+ exp_counter -= 1
+ end
+ end
+ if @next_char == ?E
+ is_integer = false
+ read_next_char
+ if @next_char == ?-
+ exp_sign = -1
+ read_next_char
+ end
+ unless @next_char.digit?
+ Error.error 101, @line, @pos
+ end
+ while @next_char.digit?
+ exponent *= 10
+ exponent += @next_char.digit
+ read_next_char
+ end
+ end
+
+ if is_integer
+ if integer_too_long
+ Error.error 102, @line, @pos
+ end
+ SymbolIntcon.new integer_mantisse
+ else
+ SymbolRealcon.new real_mantisse * (10 ** (exp_sign * exponent))
+ end
+ end
+
+ ##
+ # Sorgt für ein Überlesen von Kommentaren.
+ # Es werden einfach alle Zeichen bis zu einer schließenden Klammer eingelesen
+ # und verworfen.
+ def comment
+ while @current_char != ?}
+ forbid_eop
+ new_line if @current_char == ?\n
+ read_next_char
+ end
+ end
+
+ def new_line
+ @line += 1
+ @pos = 0
+ end
+
+ def forbid_eop
+ if eop?
+ Error.error 103, @line, @pos
+ end
+ exit
+ end
+
+ def eop?
+ @current_char.nil?
+ end
+end
+
+##
+# Läßt ein Testprogramm ablaufen.
+# Dieses erzeugt sich ein Scanner-Objekt und ruft an diesem kontinuierlich bis zum Dateiende
+# get_symbol auf.
+if $0 == __FILE__
+ scan = Scanner.new(File.new(ARGV[0] || 'test.pas'))
+ loop do
+ c = scan.get_symbol
+ puts c
+ break if c.typ == TokenConsts::EOP
+ end
+end
+# -*- ruby -*-
+
+# Local variables:
+# indent-tabs-mode: nil
+# ruby-indent-level: 4
+# End:
+
+# @@PLEAC@@_NAME
+# @@SKIP@@ Ruby
+
+# @@PLEAC@@_WEB
+# @@SKIP@@ http://www.ruby-lang.org
+
+
+# @@PLEAC@@_1.0
+string = '\n' # two characters, \ and an n
+string = 'Jon \'Maddog\' Orwant' # literal single quotes
+
+string = "\n" # a "newline" character
+string = "Jon \"Maddog\" Orwant" # literal double quotes
+
+string = %q/Jon 'Maddog' Orwant/ # literal single quotes
+
+string = %q[Jon 'Maddog' Orwant] # literal single quotes
+string = %q{Jon 'Maddog' Orwant} # literal single quotes
+string = %q(Jon 'Maddog' Orwant) # literal single quotes
+string = %q<Jon 'Maddog' Orwant> # literal single quotes
+
+a = <<"EOF"
+This is a multiline here document
+terminated by EOF on a line by itself
+EOF
+
+
+# @@PLEAC@@_1.1
+value = string[offset,count]
+value = string[offset..-1]
+
+string[offset,count] = newstring
+string[offset..-1] = newtail
+
+# in Ruby we can also specify intervals by their two offsets
+value = string[offset..offs2]
+string[offset..offs2] = newstring
+
+leading, s1, s2, trailing = data.unpack("A5 x3 A8 A8 A*")
+
+fivers = string.unpack("A5" * (string.length/5))
+
+chars = string.unpack("A1" * string.length)
+
+string = "This is what you have"
+# +012345678901234567890 Indexing forwards (left to right)
+# 109876543210987654321- Indexing backwards (right to left)
+# note that 0 means 10 or 20, etc. above
+
+first = string[0, 1] # "T"
+start = string[5, 2] # "is"
+rest = string[13..-1] # "you have"
+last = string[-1, 1] # "e"
+end_ = string[-4..-1] # "have"
+piece = string[-8, 3] # "you"
+
+string[5, 2] = "wasn't" # change "is" to "wasn't"
+string[-12..-1] = "ondrous" # "This wasn't wondrous"
+string[0, 1] = "" # delete first character
+string[-10..-1] = "" # delete last 10 characters
+
+if string[-10..-1] =~ /pattern/
+ puts "Pattern matches in last 10 characters"
+end
+
+string[0, 5].gsub!(/is/, 'at')
+
+a = "make a hat"
+a[0, 1], a[-1, 1] = a[-1, 1], a[0, 1]
+
+a = "To be or not to be"
+b = a.unpack("x6 A6")
+
+b, c = a.unpack("x6 A2 X5 A2")
+puts "#{b}\n#{c}\n"
+
+def cut2fmt(*args)
+ template = ''
+ lastpos = 1
+ for place in args
+ template += "A" + (place - lastpos).to_s + " "
+ lastpos = place
+ end
+ template += "A*"
+ return template
+end
+
+fmt = cut2fmt(8, 14, 20, 26, 30)
+
+
+# @@PLEAC@@_1.2
+# careful! "b is true" doesn't mean "b != 0" (0 is true in Ruby)
+# thus no problem of "defined" later since only nil is false
+# the following sets to `c' if `b' is nil or false
+a = b || c
+
+# if you need Perl's behaviour (setting to `c' if `b' is 0) the most
+# effective way is to use Numeric#nonzero? (thanks to Dave Thomas!)
+a = b.nonzero? || c
+
+# you will still want to use defined? in order to test
+# for scope existence of a given object
+a = defined?(b) ? b : c
+
+dir = ARGV.shift || "/tmp"
+
+
+# @@PLEAC@@_1.3
+v1, v2 = v2, v1
+
+alpha, beta, production = %w(January March August)
+alpha, beta, production = beta, production, alpha
+
+
+# @@PLEAC@@_1.4
+num = char[0]
+char = num.chr
+
+# Ruby also supports having a char from character constant
+num = ?r
+
+char = sprintf("%c", num)
+printf("Number %d is character %c\n", num, num)
+
+ascii = string.unpack("C*")
+string = ascii.pack("C*")
+
+hal = "HAL"
+ascii = hal.unpack("C*")
+# We can't use Array#each since we can't mutate a Fixnum
+ascii.collect! { |i|
+ i + 1 # add one to each ASCII value
+}
+ibm = ascii.pack("C*")
+puts ibm
+
+
+# @@PLEAC@@_1.5
+array = string.split('')
+
+array = string.unpack("C*")
+
+string.scan(/./) { |b|
+ # do something with b
+}
+
+string = "an apple a day"
+print "unique chars are: ", string.split('').uniq.sort, "\n"
+
+sum = 0
+for ascval in string.unpack("C*") # or use Array#each for a pure OO style :)
+ sum += ascval
+end
+puts "sum is #{sum & 0xffffffff}" # since Ruby will go Bignum if necessary
+
+# @@INCLUDE@@ include/ruby/slowcat.rb
+
+
+# @@PLEAC@@_1.6
+revbytes = string.reverse
+
+revwords = string.split(" ").reverse.join(" ")
+
+revwords = string.split(/(\s+)/).reverse.join
+
+# using the fact that IO is Enumerable, you can directly "select" it
+long_palindromes = File.open("/usr/share/dict/words").
+ select { |w| w.chomp!; w.reverse == w && w.length > 5 }
+
+
+# @@PLEAC@@_1.7
+while string.sub!("\t+") { ' ' * ($&.length * 8 - $`.length % 8) }
+end
+
+
+# @@PLEAC@@_1.8
+'You owe #{debt} to me'.gsub(/\#{(\w+)}/) { eval($1) }
+
+rows, cols = 24, 80
+text = %q(I am #{rows} high and #{cols} long)
+text.gsub!(/\#{(\w+)}/) { eval("#{$1}") }
+puts text
+
+'I am 17 years old'.gsub(/\d+/) { 2 * $&.to_i }
+
+
+# @@PLEAC@@_1.9
+e = "bo peep".upcase
+e.downcase!
+e.capitalize!
+
+"thIS is a loNG liNE".gsub!(/\w+/) { $&.capitalize }
+
+
+# @@PLEAC@@_1.10
+"I have #{n+1} guanacos."
+print "I have ", n+1, " guanacos."
+
+
+# @@PLEAC@@_1.11
+var = <<'EOF'.gsub(/^\s+/, '')
+ your text
+ goes here
+EOF
+
+
+# @@PLEAC@@_1.12
+string = "Folding and splicing is the work of an editor,\n"+
+ "not a mere collection of silicon\n"+
+ "and\n"+
+ "mobile electrons!"
+
+def wrap(str, max_size)
+ all = []
+ line = ''
+ for l in str.split
+ if (line+l).length >= max_size
+ all.push(line)
+ line = ''
+ end
+ line += line == '' ? l : ' ' + l
+ end
+ all.push(line).join("\n")
+end
+
+print wrap(string, 20)
+#=> Folding and
+#=> splicing is the
+#=> work of an editor,
+#=> not a mere
+#=> collection of
+#=> silicon and mobile
+#=> electrons!
+
+
+# @@PLEAC@@_1.13
+string = %q(Mom said, "Don't do that.")
+string.gsub(/['"]/) { '\\'+$& }
+string.gsub(/['"]/, '\&\&')
+string.gsub(/[^A-Z]/) { '\\'+$& }
+"is a test!".gsub(/\W/) { '\\'+$& } # no function like quotemeta?
+
+
+# @@PLEAC@@_1.14
+string.strip!
+
+
+# @@PLEAC@@_1.15
+def parse_csv(text)
+ new = text.scan(/"([^\"\\]*(?:\\.[^\"\\]*)*)",?|([^,]+),?|,/)
+ new << nil if text[-1] == ?,
+ new.flatten.compact
+end
+
+line = %q<XYZZY,"","O'Reilly, Inc","Wall, Larry","a \"glug\" bit,",5,"Error, Core Dumped">
+fields = parse_csv(line)
+fields.each_with_index { |v,i|
+ print "#{i} : #{v}\n";
+}
+
+
+# @@PLEAC@@_1.16
+# Use the soundex.rb Library from Michael Neumann.
+# http://www.s-direktnet.de/homepages/neumann/rb_prgs/Soundex.rb
+require 'Soundex'
+
+code = Text::Soundex.soundex(string)
+codes = Text::Soundex.soundex(array)
+
+# substitution function for getpwent():
+# returns an array of user entries,
+# each entry contains the username and the full name
+def login_names
+ result = []
+ File.open("/etc/passwd") { |file|
+ file.each_line { |line|
+ next if line.match(/^#/)
+ cols = line.split(":")
+ result.push([cols[0], cols[4]])
+ }
+ }
+ result
+end
+
+puts "Lookup user: "
+user = STDIN.gets
+user.chomp!
+exit unless user
+name_code = Text::Soundex.soundex(user)
+
+splitter = Regexp.new('(\w+)[^,]*\b(\w+)')
+for username, fullname in login_names do
+ firstname, lastname = splitter.match(fullname)[1,2]
+ if name_code == Text::Soundex.soundex(username)
+ || name_code == Text::Soundex.soundex(firstname)
+ || name_code == Text::Soundex.soundex(lastname)
+ then
+ puts "#{username}: #{firstname} #{lastname}"
+ end
+end
+
+
+# @@PLEAC@@_1.17
+# @@INCLUDE@@ include/ruby/fixstyle.rb
+
+
+# @@PLEAC@@_1.18
+# @@INCLUDE@@ include/ruby/psgrep.rb
+
+
+# @@PLEAC@@_2.1
+# Matz tells that you can use Integer() for strict checked conversion.
+Integer("abc")
+#=> `Integer': invalid value for Integer: "abc" (ArgumentError)
+Integer("567")
+#=> 567
+
+# You may use Float() for floating point stuff
+Integer("56.7")
+#=> `Integer': invalid value for Integer: "56.7" (ArgumentError)
+Float("56.7")
+#=> 56.7
+
+# You may also use a regexp for that
+if string =~ /^[+-]?\d+$/
+ p 'is an integer'
+else
+ p 'is not'
+end
+
+if string =~ /^-?(?:\d+(?:\.\d*)?|\.\d+)$/
+ p 'is a decimal number'
+else
+ p 'is not'
+end
+
+
+# @@PLEAC@@_2.2
+# equal(num1, num2, accuracy) : returns true if num1 and num2 are
+# equal to accuracy number of decimal places
+def equal(i, j, a)
+ sprintf("%.#{a}g", i) == sprintf("%.#{a}g", j)
+end
+
+wage = 536 # $5.36/hour
+week = 40 * wage # $214.40
+printf("One week's wage is: \$%.2f\n", week/100.0)
+
+
+# @@PLEAC@@_2.3
+num.round # rounds to integer
+
+a = 0.255
+b = sprintf("%.2f", a)
+print "Unrounded: #{a}\nRounded: #{b}\n"
+printf "Unrounded: #{a}\nRounded: %.2f\n", a
+
+print "number\tint\tfloor\tceil\n"
+a = [ 3.3 , 3.5 , 3.7, -3.3 ]
+for n in a
+ printf("% .1f\t% .1f\t% .1f\t% .1f\n", # at least I don't fake my output :)
+ n, n.to_i, n.floor, n.ceil)
+end
+
+
+# @@PLEAC@@_2.4
+def dec2bin(n)
+ [n].pack("N").unpack("B32")[0].sub(/^0+(?=\d)/, '')
+end
+
+def bin2dec(n)
+ [("0"*32+n.to_s)[-32..-1]].pack("B32").unpack("N")[0]
+end
+
+
+# @@PLEAC@@_2.5
+for i in x .. y
+ # i is set to every integer from x to y, inclusive
+end
+
+x.step(y,7) { |i|
+ # i is set to every integer from x to y, stepsize = 7
+}
+
+print "Infancy is: "
+(0..2).each { |i|
+ print i, " "
+}
+print "\n"
+
+
+# @@PLEAC@@_2.6
+# We can add conversion methods to the Integer class,
+# this makes a roman number just a representation for normal numbers.
+class Integer
+
+ @@romanlist = [["M", 1000],
+ ["CM", 900],
+ ["D", 500],
+ ["CD", 400],
+ ["C", 100],
+ ["XC", 90],
+ ["L", 50],
+ ["XL", 40],
+ ["X", 10],
+ ["IX", 9],
+ ["V", 5],
+ ["IV", 4],
+ ["I", 1]]
+
+ def to_roman
+ remains = self
+ roman = ""
+ for sym, num in @@romanlist
+ while remains >= num
+ remains -= num
+ roman << sym
+ end
+ end
+ roman
+ end
+
+ def Integer.from_roman(roman)
+ ustr = roman.upcase
+ sum = 0
+ for entry in @@romanlist
+ sym, num = entry[0], entry[1]
+ while sym == ustr[0, sym.length]
+ sum += num
+ ustr.slice!(0, sym.length)
+ end
+ end
+ sum
+ end
+
+end
+
+
+roman_fifteen = 15.to_roman
+puts "Roman for fifteen is #{roman_fifteen}"
+i = Integer.from_roman(roman_fifteen)
+puts "Converted back, #{roman_fifteen} is #{i}"
+
+# check
+for i in (1..3900)
+ r = i.to_roman
+ j = Integer.from_roman(r)
+ if i != j
+ puts "error: #{i} : #{r} - #{j}"
+ end
+end
+
+
+# @@PLEAC@@_2.7
+random = rand(y-x+1)+x
+
+chars = ["A".."Z","a".."z","0".."9"].collect { |r| r.to_a }.join + %q(!@$%^&*)
+password = (1..8).collect { chars[rand(chars.size)] }.pack("C*")
+
+
+# @@PLEAC@@_2.8
+srand # uses a combination of the time, the process id, and a sequence number
+srand(val) # for repeatable behaviour
+
+
+# @@PLEAC@@_2.9
+# from the randomr lib:
+# http://raa.ruby-lang.org/project/randomr/
+----> http://raa.ruby-lang.org/project/randomr/
+
+require 'random/mersenne_twister'
+mers = Random::MersenneTwister.new 123456789
+puts mers.rand(0) # 0.550321932544541
+puts mers.rand(10) # 2
+
+# using online sources of random data via the realrand package:
+# http://raa.ruby-lang.org/project/realrand/
+# **Note**
+# The following online services are used in this package:
+# http://www.random.org - source: atmospheric noise
+# http://www.fourmilab.ch/hotbits - source: radioactive decay timings
+# http://random.hd.org - source: entropy from local and network noise
+# Please visit the sites and respect the rules of each service.
+
+require 'random/online'
+
+generator1 = Random::RandomOrg.new
+puts generator1.randbyte(5).join(",")
+puts generator1.randnum(10, 1, 6).join(",") # Roll dice 10 times.
+
+generator2 = Random::FourmiLab.new
+puts generator2.randbyte(5).join(",")
+# randnum is not supported.
+
+generator3 = Random::EntropyPool.new
+puts generator3.randbyte(5).join(",")
+# randnum is not supported.
+
+
+# @@PLEAC@@_2.10
+def gaussian_rand
+ begin
+ u1 = 2 * rand() - 1
+ u2 = 2 * rand() - 1
+ w = u1*u1 + u2*u2
+ end while (w >= 1)
+ w = Math.sqrt((-2*Math.log(w))/w)
+ [ u2*w, u1*w ]
+end
+
+mean = 25
+sdev = 2
+salary = gaussian_rand[0] * sdev + mean
+printf("You have been hired at \$%.2f\n", salary)
+
+
+# @@PLEAC@@_2.11
+def deg2rad(d)
+ (d/180.0)*Math::PI
+end
+
+def rad2deg(r)
+ (r/Math::PI)*180
+end
+
+
+# @@PLEAC@@_2.12
+sin_val = Math.sin(angle)
+cos_val = Math.cos(angle)
+tan_val = Math.tan(angle)
+
+# AFAIK Ruby's Math module doesn't provide acos/asin
+# While we're at it, let's also define missing hyperbolic functions
+module Math
+ def Math.asin(x)
+ atan2(x, sqrt(1 - x**2))
+ end
+ def Math.acos(x)
+ atan2(sqrt(1 - x**2), x)
+ end
+ def Math.atan(x)
+ atan2(x, 1)
+ end
+ def Math.sinh(x)
+ (exp(x) - exp(-x)) / 2
+ end
+ def Math.cosh(x)
+ (exp(x) + exp(-x)) / 2
+ end
+ def Math.tanh(x)
+ sinh(x) / cosh(x)
+ end
+end
+
+# The support for Complex numbers is not built-in
+y = Math.acos(3.7)
+#=> in `sqrt': square root for negative number (ArgumentError)
+
+# There is an implementation of Complex numbers in 'complex.rb' in current
+# Ruby distro, but it doesn't support atan2 with complex args, so it doesn't
+# solve this problem.
+
+
+# @@PLEAC@@_2.13
+log_e = Math.log(val)
+log_10 = Math.log10(val)
+
+def log_base(base, val)
+ Math.log(val)/Math.log(base)
+end
+
+answer = log_base(10, 10_000)
+puts "log10(10,000) = #{answer}"
+
+
+# @@PLEAC@@_2.14
+require 'matrix.rb'
+
+a = Matrix[[3, 2, 3], [5, 9, 8]]
+b = Matrix[[4, 7], [9, 3], [8, 1]]
+c = a * b
+
+a.row_size
+a.column_size
+
+c.det
+a.transpose
+
+
+# @@PLEAC@@_2.15
+require 'complex.rb'
+require 'rational.rb'
+
+a = Complex(3, 5) # 3 + 5i
+b = Complex(2, -2) # 2 - 2i
+puts "c = #{a*b}"
+
+c = a * b
+d = 3 + 4*Complex::I
+
+printf "sqrt(#{d}) = %s\n", Math.sqrt(d)
+
+
+# @@PLEAC@@_2.16
+number = hexadecimal.hex
+number = octal.oct
+
+print "Gimme a number in decimal, octal, or hex: "
+num = gets.chomp
+exit unless defined?(num)
+num = num.oct if num =~ /^0/ # does both oct and hex
+printf "%d %x %o\n", num, num, num
+
+print "Enter file permission in octal: "
+permissions = gets.chomp
+raise "Exiting ...\n" unless defined?(permissions)
+puts "The decimal value is #{permissions.oct}"
+
+
+# @@PLEAC@@_2.17
+def commify(n)
+ n.to_s =~ /([^\.]*)(\..*)?/
+ int, dec = $1.reverse, $2 ? $2 : ""
+ while int.gsub!(/(,|\.|^)(\d{3})(\d)/, '\1\2,\3')
+ end
+ int.reverse + dec
+end
+
+
+# @@PLEAC@@_2.18
+printf "It took %d hour%s\n", time, time == 1 ? "" : "s"
+
+# dunno if an equivalent to Lingua::EN::Inflect exists...
+
+
+# @@PLEAC@@_2.19
+#-----------------------------
+#!/usr/bin/ruby
+# bigfact - calculating prime factors
+def factorize(orig)
+ factors = {}
+ factors.default = 0 # return 0 instead nil if key not found in hash
+ n = orig
+ i = 2
+ sqi = 4 # square of i
+ while sqi <= n do
+ while n.modulo(i) == 0 do
+ n /= i
+ factors[i] += 1
+ # puts "Found factor #{i}"
+ end
+ # we take advantage of the fact that (i +1)**2 = i**2 + 2*i +1
+ sqi += 2 * i + 1
+ i += 1
+ end
+
+ if (n != 1) && (n != orig)
+ factors[n] += 1
+ end
+ factors
+end
+
+def printfactorhash(orig, factorcount)
+ print format("%-10d ", orig)
+ if factorcount.length == 0
+ print "PRIME"
+ else
+ # sorts after number, because the hash keys are numbers
+ factorcount.sort.each { |factor,exponent|
+ print factor
+ if exponent > 1
+ print "**", exponent
+ end
+ print " "
+ }
+ end
+ puts
+end
+
+for arg in ARGV
+ n = arg.to_i
+ mfactors = factorize(n)
+ printfactorhash(n, mfactors)
+end
+#-----------------------------
+
+
+# @@PLEAC@@_3.0
+puts Time.now
+
+print "Today is day ", Time.now.yday, " of the current year.\n"
+print "Today is day ", Time.now.day, " of the current month.\n"
+
+
+# @@PLEAC@@_3.1
+day, month, year = Time.now.day, Time.now.month, Time.now.year
+# or
+day, month, year = Time.now.to_a[3..5]
+
+tl = Time.now.localtime
+printf("The current date is %04d %02d %02d\n", tl.year, tl.month, tl.day)
+
+Time.now.localtime.strftime("%Y-%m-%d")
+
+
+# @@PLEAC@@_3.2
+Time.local(year, month, day, hour, minute, second).tv_sec
+Time.gm(year, month, day, hour, minute, second).tv_sec
+
+
+# @@PLEAC@@_3.3
+sec, min, hour, day, month, year, wday, yday, isdst, zone = Time.at(epoch_secs).to_a
+
+
+# @@PLEAC@@_3.4
+when_ = now + difference # now -> Time ; difference -> Numeric (delta in seconds)
+then_ = now - difference
+
+
+# @@PLEAC@@_3.5
+bree = 361535725
+nat = 96201950
+
+difference = bree - nat
+puts "There were #{difference} seconds between Nat and Bree"
+
+seconds = difference % 60
+difference = (difference - seconds) / 60
+minutes = difference % 60
+difference = (difference - minutes) / 60
+hours = difference % 24
+difference = (difference - hours) / 24
+days = difference % 7
+weeks = (difference - days) / 7
+
+puts "(#{weeks} weeks, #{days} days, #{hours}:#{minutes}:#{seconds})"
+
+
+# @@PLEAC@@_3.6
+monthday, weekday, yearday = date.mday, date.wday, date.yday
+
+# AFAIK the week number is not just a division since week boundaries are on sundays
+weeknum = d.strftime("%U").to_i + 1
+
+year = 1981
+month = "jun" # or `6' if you want to emulate a broken language
+day = 16
+t = Time.mktime(year, month, day)
+print "#{month}/#{day}/#{year} was a ", t.strftime("%A"), "\n"
+
+
+# @@PLEAC@@_3.7
+yyyy, mm, dd = $1, $2, $3 if "1998-06-25" =~ /(\d+)-(\d+)-(\d+)/
+
+epoch_seconds = Time.mktime(yyyy, mm, dd).tv_sec
+
+# dunno an equivalent to Date::Manip#ParseDate
+
+
+# @@PLEAC@@_3.8
+string = Time.at(epoch_secs)
+Time.at(1234567890).gmtime # gives: Fri Feb 13 23:31:30 UTC 2009
+
+time = Time.mktime(1973, "jan", 18, 3, 45, 50)
+print "In localtime it gives: ", time.localtime, "\n"
+
+
+# @@PLEAC@@_3.9
+# Ruby provides micro-seconds in Time object
+Time.now.usec
+
+# Ruby gives the seconds in floating format when substracting two Time objects
+before = Time.now
+line = gets
+elapsed = Time.now - before
+puts "You took #{elapsed} seconds."
+
+# On my Celeron-400 with Linux-2.2.19-14mdk, average for three execs are:
+# This Ruby version: average 0.00321 sec
+# Cookbook's Perl version: average 0.00981 sec
+size = 500
+number_of_times = 100
+total_time = 0
+number_of_times.times {
+ # populate array
+ array = []
+ size.times { array << rand }
+ # sort it
+ begin_ = Time.now
+ array.sort!
+ time = Time.now - begin_
+ total_time += time
+}
+printf "On average, sorting %d random numbers takes %.5f seconds\n",
+ size, (total_time/Float(number_of_times))
+
+
+# @@PLEAC@@_3.10
+sleep(0.005) # Ruby is definitely not as broken as Perl :)
+# (may be interrupted by sending the process a SIGALRM)
+
+
+# @@PLEAC@@_3.11
+#!/usr/bin/ruby -w
+# hopdelta - feed mail header, produce lines
+# showing delay at each hop.
+require 'time'
+class MailHopDelta
+
+ def initialize(mail)
+ @head = mail.gsub(/\n\s+/,' ')
+ @topline = %w-Sender Recipient Time Delta-
+ @start_from = mail.match(/^From.*\@([^\s>]*)/)[1]
+ @date = Time.parse(mail.match(/^Date:\s+(.*)/)[1])
+ end
+
+ def out(line)
+ "%-20.20s %-20.20s %-20.20s %s" % line
+ end
+
+ def hop_date(day)
+ day.strftime("%I:%M:%S %Y/%m/%d")
+ end
+
+ def puts_hops
+ puts out(@topline)
+ puts out(['Start', @start_from, hop_date(@date),''])
+ @head.split(/\n/).reverse.grep(/^Received:/).each do |hop|
+ hop.gsub!(/\bon (.*?) (id.*)/,'; \1')
+ whence = hop.match(/;\s+(.*)$/)[1]
+ unless whence
+ warn "Bad received line: #{hop}"
+ next
+ end
+ from = $+ if hop =~ /from\s+(\S+)|\((.*?)\)/
+ by = $1 if hop =~ /by\s+(\S+\.\S+)/
+ next unless now = Time.parse(whence).localtime
+ delta = now - @date
+ puts out([from, by, hop_date(now), hop_time(delta)])
+ @date = now
+ end
+ end
+
+ def hop_time(secs)
+ sign = secs < 0 ? -1 : 1
+ days, secs = secs.abs.divmod(60 * 60 * 24)
+ hours,secs = secs.abs.divmod(60 * 60)
+ mins, secs = secs.abs.divmod(60)
+ rtn = "%3ds" % [secs * sign]
+ rtn << "%3dm" % [mins * sign] if mins != 0
+ rtn << "%3dh" % [hours * sign] if hours != 0
+ rtn << "%3dd" % [days * sign] if days != 0
+ rtn
+ end
+end
+
+$/ = ""
+mail = MailHopDelta.new(ARGF.gets).puts_hops
+
+
+# @@PLEAC@@_4.0
+single_level = [ "this", "that", "the", "other" ]
+
+# Ruby directly supports nested arrays
+double_level = [ "this", "that", [ "the", "other" ] ]
+still_single_level = [ "this", "that", [ "the", "other" ] ].flatten
+
+
+# @@PLEAC@@_4.1
+a = [ "quick", "brown", "fox" ]
+a = %w(Why are you teasing me?)
+
+lines = <<"END_OF_HERE_DOC".gsub(/^\s*(.+)/, '\1')
+ The boy stood on the burning deck,
+ It was as hot as glass.
+END_OF_HERE_DOC
+
+bigarray = IO.readlines("mydatafile").collect { |l| l.chomp }
+
+name = "Gandalf"
+banner = %Q(Speak, #{name}, and welcome!)
+
+host_info = `host #{his_host}`
+
+%x(ps #{$$})
+
+banner = 'Costs only $4.95'.split(' ')
+
+rax = %w! ( ) < > { } [ ] !
+
+
+# @@PLEAC@@_4.2
+def commify_series(a)
+ a.size == 0 ? '' :
+ a.size == 1 ? a[0] :
+ a.size == 2 ? a.join(' and ') :
+ a[0..-2].join(', ') + ', and ' + a[-1]
+end
+
+array = [ "red", "yellow", "green" ]
+
+print "I have ", array, " marbles\n"
+# -> I have redyellowgreen marbles
+
+# But unlike Perl:
+print "I have #{array} marbles\n"
+# -> I have redyellowgreen marbles
+# So, needs:
+print "I have #{array.join(' ')} marbles\n"
+# -> I have red yellow green marbles
+
+def commify_series(a)
+ sepchar = a.select { |p| p =~ /,/ } != [] ? '; ' : ', '
+ a.size == 0 ? '' :
+ a.size == 1 ? a[0] :
+ a.size == 2 ? a.join(' and ') :
+ a[0..-2].join(sepchar) + sepchar + 'and ' + a[-1]
+end
+
+
+# @@PLEAC@@_4.3
+# (note: AFAIK Ruby doesn't allow gory change of Array length)
+# grow the array by assigning nil to past the end of array
+ary[new_size-1] = nil
+# shrink the array by slicing it down
+ary.slice!(new_size..-1)
+# init the array with given size
+Array.new(number_of_elems)
+# assign to an element past the original end enlarges the array
+ary[index_new_last_elem] = value
+
+def what_about_that_array(a)
+ print "The array now has ", a.size, " elements.\n"
+ # Index of last element is not really interesting in Ruby
+ print "Element #3 is `#{a[3]}'.\n"
+end
+people = %w(Crosby Stills Nash Young)
+what_about_that_array(people)
+
+
+# @@PLEAC@@_4.4
+# OO style
+bad_users.each { |user|
+ complain(user)
+}
+# or, functional style
+for user in bad_users
+ complain(user)
+end
+
+for var in ENV.keys.sort
+ puts "#{var}=#{ENV[var]}"
+end
+
+for user in all_users
+ disk_space = get_usage(user)
+ if (disk_space > MAX_QUOTA)
+ complain(user)
+ end
+end
+
+for l in IO.popen("who").readlines
+ print l if l =~ /^gc/
+end
+
+# we can mimic the obfuscated Perl way
+while fh.gets # $_ is set to the line just read
+ chomp # $_ has a trailing \n removed, if it had one
+ split.each { |w| # $_ is split on whitespace
+ # but $_ is not set to each chunk as in Perl
+ print w.reverse
+ }
+end
+# ...or use a cleaner way
+for l in fh.readlines
+ l.chomp.split.each { |w| print w.reverse }
+end
+
+# same drawback as in problem 1.4, we can't mutate a Numeric...
+array.collect! { |v| v - 1 }
+
+a = [ .5, 3 ]; b = [ 0, 1 ]
+for ary in [ a, b ]
+ ary.collect! { |v| v * 7 }
+end
+puts "#{a.join(' ')} #{b.join(' ')}"
+
+# we can mutate Strings, cool; we need a trick for the scalar
+for ary in [ [ scalar ], array, hash.values ]
+ ary.each { |v| v.strip! } # String#strip rules :)
+end
+
+
+# @@PLEAC@@_4.5
+# not relevant in Ruby since we have always references
+for item in array
+ # do somethingh with item
+end
+
+
+# @@PLEAC@@_4.6
+unique = list.uniq
+
+# generate a list of users logged in, removing duplicates
+users = `who`.collect { |l| l =~ /(\w+)/; $1 }.sort.uniq
+puts("users logged in: #{commify_series(users)}") # see 4.2 for commify_series
+
+
+# @@PLEAC@@_4.7
+a - b
+# [ 1, 1, 2, 2, 3, 3, 3, 4, 5 ] - [ 1, 2, 4 ] -> [3, 5]
+
+
+# @@PLEAC@@_4.8
+union = a | b
+intersection = a & b
+difference = a - b
+
+
+# @@PLEAC@@_4.9
+array1.concat(array2)
+# if you will assign to another object, better use:
+new_ary = array1 + array2
+
+members = [ "Time", "Flies" ]
+initiates = [ "An", "Arrow" ]
+members += initiates
+
+members = [ "Time", "Flies" ]
+initiates = [ "An", "Arrow" ]
+members[2,0] = [ "Like", initiates ].flatten
+
+members[0] = "Fruit"
+members[3,2] = "A", "Banana"
+
+
+# @@PLEAC@@_4.10
+reversed = ary.reverse
+
+ary.reverse_each { |e|
+ # do something with e
+}
+
+descending = ary.sort.reverse
+descending = ary.sort { |a,b| b <=> a }
+
+
+# @@PLEAC@@_4.11
+# remove n elements from front of ary (shift n)
+front = ary.slice!(0, n)
+
+# remove n elements from the end of ary (pop n)
+end_ = ary.slice!(-n .. -1)
+
+# let's extend the Array class, to make that useful
+class Array
+ def shift2()
+ slice!(0 .. 1) # more symetric with pop2...
+ end
+ def pop2()
+ slice!(-2 .. -1)
+ end
+end
+
+friends = %w(Peter Paul Mary Jim Tim)
+this, that = friends.shift2
+
+beverages = %w(Dew Jolt Cola Sprite Fresca)
+pair = beverages.pop2
+
+
+# @@PLEAC@@_4.12
+# use Enumerable#detect (or the synonym Enumerable#find)
+highest_eng = employees.detect { |emp| emp.category == 'engineer' }
+
+
+# @@PLEAC@@_4.13
+# use Enumerable#select (or the synonym Enumerable#find_all)
+bigs = nums.select { |i| i > 1_000_000 }
+pigs = users.keys.select { |k| users[k] > 1e7 }
+
+matching = `who`.select { |u| u =~ /^gnat / }
+
+engineers = employees.select { |e| e.position == 'Engineer' }
+
+secondary_assistance = applicants.select { |a|
+ a.income >= 26_000 && a.income < 30_000
+}
+
+
+# @@PLEAC@@_4.14
+# normally you would have an array of Numeric (Float or
+# Fixnum or Bignum), so you would use:
+sorted = unsorted.sort
+# if you have strings representing Integers or Floats
+# you may specify another sort method:
+sorted = unsorted.sort { |a,b| a.to_f <=> b.to_f }
+
+# let's use the list of my own PID's
+`ps ux`.split("\n")[1..-1].
+ select { |i| i =~ /^#{ENV['USER']}/ }.
+ collect { |i| i.split[1] }.
+ sort { |a,b| a.to_i <=> b.to_i }.each { |i| puts i }
+puts "Select a process ID to kill:"
+pid = gets.chomp
+raise "Exiting ... \n" unless pid && pid =~ /^\d+$/
+Process.kill('TERM', pid.to_i)
+sleep 2
+Process.kill('KILL', pid.to_i)
+
+descending = unsorted.sort { |a,b| b.to_f <=> a.to_f }
+
+
+# @@PLEAC@@_4.15
+ordered = unordered.sort { |a,b| compare(a,b) }
+
+precomputed = unordered.collect { |e| [compute, e] }
+ordered_precomputed = precomputed.sort { |a,b| a[0] <=> b[0] }
+ordered = ordered_precomputed.collect { |e| e[1] }
+
+ordered = unordered.collect { |e| [compute, e] }.
+ sort { |a,b| a[0] <=> b[0] }.
+ collect { |e| e[1] }
+
+for employee in employees.sort { |a,b| a.name <=> b.name }
+ print employee.name, " earns \$ ", employee.salary, "\n"
+end
+
+# Beware! `0' is true in Ruby.
+# For chaining comparisons, you may use Numeric#nonzero?, which
+# returns num if num is not zero, nil otherwise
+sorted = employees.sort { |a,b| (a.name <=> b.name).nonzero? || b.age <=> a.age }
+
+users = []
+# getpwent is not wrapped in Ruby... let's fallback
+IO.readlines('/etc/passwd').each { |u| users << u.split(':') }
+users.sort! { |a,b| a[0] <=> b[0] }
+for user in users
+ puts user[0]
+end
+
+sorted = names.sort { |a,b| a[1, 1] <=> b[1, 1] }
+sorted = strings.sort { |a,b| a.length <=> b.length }
+
+# let's show only the compact version
+ordered = strings.collect { |e| [e.length, e] }.
+ sort { |a,b| a[0] <=> b[0] }.
+ collect { |e| e[1] }
+
+ordered = strings.collect { |e| [/\d+/.match(e)[0].to_i, e] }.
+ sort { |a,b| a[0] <=> b[0] }.
+ collect { |e| e[1] }
+
+print `cat /etc/passwd`.collect { |e| [e, e.split(':').indexes(3,2,0)].flatten }.
+ sort { |a,b| (a[1] <=> b[1]).nonzero? || (a[2] <=> b[2]).nonzero? || a[3] <=> b[3] }.
+ collect { |e| e[0] }
+
+
+# @@PLEAC@@_4.16
+circular.unshift(circular.pop) # the last shall be first
+circular.push(circular.shift) # and vice versa
+
+def grab_and_rotate(l)
+ l.push(ret = l.shift)
+ ret
+end
+
+processes = [1, 2, 3, 4, 5]
+while (1)
+ process = grab_and_rotate(processes)
+ puts "Handling process #{process}"
+ sleep 1
+end
+
+
+# @@PLEAC@@_4.17
+def fisher_yates_shuffle(a)
+ (a.size-1).downto(1) { |i|
+ j = rand(i+1)
+ a[i], a[j] = a[j], a[i] if i != j
+ }
+end
+
+def naive_shuffle(a)
+ for i in 0...a.size
+ j = rand(a.size)
+ a[i], a[j] = a[j], a[i]
+ end
+end
+
+
+# @@PLEAC@@_4.18
+#!/usr/bin/env ruby
+# example 4-2 words
+# words - gather lines, present in colums
+
+# class to encapsulate the word formatting from the input
+class WordFormatter
+ def initialize(cols)
+ @cols = cols
+ end
+
+ # helper to return the length of the longest word in the wordlist
+ def maxlen(wordlist)
+ max = 1
+ for word in wordlist
+ if word.length > max
+ max = word.length
+ end
+ end
+ max
+ end
+
+ # process the wordlist and print it formmated into columns
+ def output(wordlist)
+ collen = maxlen(wordlist) + 1
+ columns = @cols / collen
+ columns = 1 if columns == 0
+ rows = (wordlist.length + columns - 1) / columns
+ # now process each item, picking out proper piece for this position
+ 0.upto(rows * columns - 1) { |item|
+ target = (item % columns) * rows + (item / columns)
+ eol = ((item+1) % columns == 0)
+ piece = wordlist[target] || ""
+ piece = piece.ljust(collen) unless eol
+ print piece
+ puts if eol
+ }
+ # no need to finish it up, because eol is always true for the last element
+ end
+end
+
+# get nr of chars that fit in window or console, see PLEAC 15.4
+# not portable -- linux only (?)
+def getWinCharWidth()
+ buf = "\0" * 8
+ $stdout.ioctl(0x5413, buf)
+ ws_row, ws_col, ws_xpixel, ws_ypixel = buf.unpack("$4")
+ ws_col || 80
+rescue
+ 80
+end
+
+# main program
+cols = getWinCharWidth()
+formatter = WordFormatter.new(cols)
+words = readlines()
+words.collect! { |line|
+ line.chomp
+}
+formatter.output(words)
+
+
+# @@PLEAC@@_4.19
+# In ruby, Fixnum's are automatically converted to Bignum's when
+# needed, so there is no need for an extra module
+def factorial(n)
+ s = 1
+ while n > 0
+ s *= n
+ n -= 1
+ end
+ s
+end
+
+puts factorial(500)
+
+#---------------------------------------------------------
+# Example 4-3. tsc-permute
+# tsc_permute: permute each word of input
+def permute(items, perms)
+ unless items.length > 0
+ puts perms.join(" ")
+ else
+ for i in items
+ newitems = items.dup
+ newperms = perms.dup
+ newperms.unshift(newitems.delete(i))
+ permute(newitems, newperms)
+ end
+ end
+end
+# In ruby the main program must be after all definitions it is using
+permute(ARGV, [])
+
+#---------------------------------------------------------
+# mjd_permute: permute each word of input
+
+def factorial(n)
+ s = 1
+ while n > 0
+ s *= n
+ n -= 1
+ end
+ s
+end
+
+# we use a class with a class variable store the private cache
+# for the results of the factorial function.
+class Factorial
+ @@fact = [ 1 ]
+ def Factorial.compute(n)
+ if @@fact[n]
+ @@fact[n]
+ else
+ @@fact[n] = n * Factorial.compute(n - 1)
+ end
+ end
+end
+
+#---------------------------------------------------------
+# Example 4-4- mjd-permute
+# n2pat(n, len): produce the N-th pattern of length len
+
+# We must use a lower case letter as parameter N, otherwise it is
+# handled as constant Length is the length of the resulting
+# array, not the index of the last element (length -1) like in
+# the perl example.
+def n2pat(n, length)
+ pat = []
+ i = 1
+ while i <= length
+ pat.push(n % i)
+ n /= i
+ i += 1
+ end
+ pat
+end
+
+# pat2perm(pat): turn pattern returned by n2pat() into
+# permutation of integers.
+def pat2perm(pat)
+ source = (0 .. pat.length - 1).to_a
+ perm = []
+ perm.push(source.slice!(pat.pop)) while pat.length > 0
+ perm
+end
+
+def n2perm(n, len)
+ pat2perm(n2pat(n,len))
+end
+
+# In ruby the main program must be after all definitions
+while gets
+ data = split
+ # the perl solution has used $#data, which is length-1
+ num_permutations = Factorial.compute(data.length())
+ 0.upto(num_permutations - 1) do |i|
+ # in ruby we can not use an array as selector for an array
+ # but by exchanging the two arrays, we can use the collect method
+ # which returns an array with the result of all block invocations
+ permutation = n2perm(i, data.length).collect {
+ |j| data[j]
+ }
+ puts permutation.join(" ")
+ end
+end
+
+
+# @@PLEAC@@_5.0
+age = { "Nat", 24,
+ "Jules", 25,
+ "Josh", 17 }
+
+age["Nat"] = 24
+age["Jules"] = 25
+age["Josh"] = 17
+
+food_color = {
+ "Apple" => "red",
+ "Banana" => "yellow",
+ "Lemon" => "yellow",
+ "Carrot" => "orange"
+ }
+
+# In Ruby, you cannot avoid the double or simple quoting
+# while manipulatin hashes
+
+
+# @@PLEAC@@_5.1
+hash[key] = value
+
+food_color["Raspberry"] = "pink"
+puts "Known foods:", food_color.keys
+
+
+# @@PLEAC@@_5.2
+# does hash have a value for key ?
+if (hash.has_key?(key))
+ # it exists
+else
+ # it doesn't
+end
+
+[ "Banana", "Martini" ].each { |name|
+ print name, " is a ", food_color.has_key?(name) ? "food" : "drink", "\n"
+}
+
+age = {}
+age['Toddler'] = 3
+age['Unborn'] = 0
+age['Phantasm'] = nil
+
+for thing in ['Toddler', 'Unborn', 'Phantasm', 'Relic']
+ print "#{thing}: "
+ print "Has-key " if age.has_key?(thing)
+ print "True " if age[thing]
+ print "Nonzero " if age[thing] && age[thing].nonzero?
+ print "\n"
+end
+
+#=>
+# Toddler: Has-key True Nonzero
+# Unborn: Has-key True
+# Phantasm: Has-key
+# Relic:
+
+# You use Hash#has_key? when you use Perl's exists -> it checks
+# for existence of a key in a hash.
+# All Numeric are "True" in ruby, so the test doesn't have the
+# same semantics as in Perl; you would use Numeric#nonzero? to
+# achieve the same semantics (false if 0, true otherwise).
+
+
+# @@PLEAC@@_5.3
+food_color.delete("Banana")
+
+
+# @@PLEAC@@_5.4
+hash.each { |key, value|
+ # do something with key and value
+}
+
+hash.each_key { |key|
+ # do something with key
+}
+
+food_color.each { |food, color|
+ puts "#{food} is #{color}"
+}
+
+food_color.each_key { |food|
+ puts "#{food} is #{food_color[food]}"
+}
+
+# IMO this demonstrates that OO style is by far more readable
+food_color.keys.sort.each { |food|
+ puts "#{food} is #{food_color[food]}."
+}
+
+#-----------------------------
+#!/usr/bin/ruby
+# countfrom - count number of messages from each sender
+
+# Default value is 0
+from = Hash.new(0)
+while gets
+ /^From: (.*)/ and from[$1] += 1
+end
+
+# More useful to sort by number of received mail by person
+from.sort {|a,b| b[1]<=>a[1]}.each { |v|
+ puts "#{v[1]}: #{v[0]}"
+}
+#-----------------------------
+
+
+# @@PLEAC@@_5.5
+# You may use the built-in 'inspect' method this way:
+p hash
+
+# Or do it the Cookbook way:
+hash.each { |k,v| puts "#{k} => #{v}" }
+
+# Sorted by keys
+hash.sort.each { |e| puts "#{e[0]} => #{e[1]}" }
+# Sorted by values
+hash.sort{|a,b| a[1]<=>b[1]}.each { |e| puts "#{e[0]} => #{e[1]}" }
+
+
+# @@PLEAC@@_5.7
+ttys = Hash.new
+for i in `who`
+ user, tty = i.split
+ (ttys[user] ||= []) << tty # see problems_ruby for more infos
+end
+ttys.keys.sort.each { |k|
+ puts "#{k}: #{commify_series(ttys[k])}" # from 4.2
+}
+
+
+# @@PLEAC@@_5.8
+surname = { "Mickey" => "Mantle", "Babe" => "Ruth" }
+puts surname.index("Mantle")
+
+# If you really needed to 'invert' the whole hash, use Hash#invert
+
+#-----------------------------
+#!/usr/bin/ruby -w
+# foodfind - find match for food or color
+
+given = ARGV.shift or raise "usage: foodfind food_or_color"
+
+color = {
+ "Apple" => "red",
+ "Banana" => "yellow",
+ "Lemon" => "yellow",
+ "Carrot" => "orange",
+}
+
+if (color.has_key?(given))
+ puts "#{given} is a food with color #{color[given]}."
+end
+if (color.has_value?(given))
+ puts "#{color.index(given)} is a food with color #{given}."
+end
+#-----------------------------
+
+
+# @@PLEAC@@_5.9
+# Sorted by keys (Hash#sort gives an Array of pairs made of each key,value)
+food_color.sort.each { |f|
+ puts "#{f[0]} is #{f[1]}."
+}
+
+# Sorted by values
+food_color.sort { |a,b| a[1] <=> b[1] }.each { |f|
+ puts "#{f[0]} is #{f[1]}."
+}
+
+# Sorted by length of values
+food_color.sort { |a,b| a[1].length <=> b[1].length }.each { |f|
+ puts "#{f[0]} is #{f[1]}."
+}
+
+
+# @@PLEAC@@_5.10
+merged = a.clone.update(b) # because Hash#update changes object in place
+
+drink_color = { "Galliano" => "yellow", "Mai Tai" => "blue" }
+ingested_color = drink_color.clone.update(food_color)
+
+substance_color = {}
+for i in [ food_color, drink_color ]
+ i.each_key { |k|
+ if substance_color.has_key?(k)
+ puts "Warning: #{k} seen twice. Using the first definition."
+ next
+ end
+ substance_color[k] = 1
+ }
+end
+
+
+# @@PLEAC@@_5.11
+common = hash1.keys & hash2.keys
+
+this_not_that = hash1.keys - hash2.keys
+
+
+# @@PLEAC@@_5.12
+# no problem here, Ruby handles any kind of object for key-ing
+# (it takes Object#hash, which defaults to Object#id)
+
+
+# @@PLEAC@@_5.13
+# AFAIK, not possible in Ruby
+
+
+# @@PLEAC@@_5.14
+# Be careful, the following is possible only because Fixnum objects are
+# special (documentation says: there is effectively only one Fixnum object
+# instance for any given integer value).
+count = Hash.new(0)
+array.each { |e|
+ count[e] += 1
+}
+
+
+# @@PLEAC@@_5.15
+father = {
+ "Cain" , "Adam",
+ "Abel" , "Adam",
+ "Seth" , "Adam",
+ "Enoch" , "Cain",
+ "Irad" , "Enoch",
+ "Mehujael" , "Irad",
+ "Methusael" , "Mehujael",
+ "Lamech" , "Methusael",
+ "Jabal" , "Lamech",
+ "Jubal" , "Lamech",
+ "Tubalcain" , "Lamech",
+ "Enos" , "Seth",
+}
+
+while gets
+ chomp
+ begin
+ print $_, " "
+ end while $_ = father[$_]
+ puts
+end
+
+children = {}
+father.each { |k,v|
+ (children[v] ||= []) << k
+}
+while gets
+ chomp
+ puts "#{$_} begat #{(children[$_] || ['Nobody']).join(', ')}.\n"
+end
+
+includes = {}
+files.each { |f|
+ begin
+ for l in IO.readlines(f)
+ next unless l =~ /^\s*#\s*include\s*<([^>]+)>/
+ (includes[$1] ||= []) << f
+ end
+ rescue SystemCallError
+ $stderr.puts "#$! (skipping)"
+ end
+}
+
+include_free = includes.values.flatten.uniq - includes.keys
+
+
+# @@PLEAC@@_5.16
+# dutree - print sorted intented rendition of du output
+#% dutree
+#% dutree /usr
+#% dutree -a
+#% dutree -a /bin
+
+# The DuNode class collects all information about a directory,
+# and provides some convenience methods
+class DuNode
+
+ attr_reader :name
+ attr_accessor :size
+ attr_accessor :kids
+
+ def initialize(name)
+ @name = name
+ @kids = []
+ @size = 0
+ end
+
+ # support for sorting nodes with side
+ def size_compare(node2)
+ @size <=> node2.size
+ end
+
+ def basename
+ @name.sub(/.*\//, "")
+ end
+
+ #returns substring before last "/", nil if not there
+ def parent
+ p = @name.sub(/\/[^\/]+$/,"")
+ if p == @name
+ nil
+ else
+ p
+ end
+ end
+
+end
+
+# The DuTree does the acdtual work of
+# getting the input, parsing it, builging up a tree
+# and format it for output
+class Dutree
+
+ attr_reader :topdir
+
+ def initialize
+ @nodes = Hash.new
+ @dirsizes = Hash.new(0)
+ @kids = Hash.new([])
+ end
+
+ # get a node by name, create it if it does not exist yet
+ def get_create_node(name)
+ if @nodes.has_key?(name)
+ @nodes[name]
+ else
+ node = DuNode.new(name)
+ @nodes[name] = node
+ node
+ end
+ end
+
+ # run du, read in input, save sizes and kids
+ # stores last directory read in instance variable topdir
+ def input(arguments)
+ name = ""
+ cmd = "du " + arguments.join(" ")
+ IO.popen(cmd) { |pipe|
+ pipe.each { |line|
+ size, name = line.chomp.split(/\s+/, 2)
+ node = get_create_node(name)
+ node.size = size.to_i
+ @nodes[name] = node
+ parent = node.parent
+ if parent
+ get_create_node(parent).kids.push(node)
+ end
+ }
+ }
+ @topdir = @nodes[name]
+ end
+
+ # figure out how much is taken in each directory
+ # that isn't stored in the subdirectories. Add a new
+ # fake kid called "." containing that much.
+ def get_dots(node)
+ cursize = node.size
+ for kid in node.kids
+ cursize -= kid.size
+ get_dots(kid)
+ end
+ if node.size != cursize
+ newnode = get_create_node(node.name + "/.")
+ newnode.size = cursize
+ node.kids.push(newnode)
+ end
+ end
+
+ # recursively output everything
+ # passing padding and number width as well
+ # on recursive calls
+ def output(node, prefix="", width=0)
+ line = sprintf("%#{width}d %s", node.size, node.basename)
+ puts(prefix + line)
+ prefix += line.sub(/\d /, "| ")
+ prefix.gsub!(/[^|]/, " ")
+ if node.kids.length > 0 # not a bachelor node
+ kids = node.kids
+ kids.sort! { |a,b|
+ b.size_compare(a)
+ }
+ width = kids[0].size.to_s.length
+ for kid in kids
+ output(kid, prefix, width)
+ end
+ end
+ end
+
+end
+
+tree = Dutree.new
+tree.input(ARGV)
+tree.get_dots(tree.topdir)
+tree.output(tree.topdir)
+
+
+# @@PLEAC@@_6.0
+# The verbose version are match, sub, gsub, sub! and gsub!;
+# pattern needs to be a Regexp object; it yields a MatchData
+# object.
+pattern.match(string)
+string.sub(pattern, replacement)
+string.gsub(pattern, replacement)
+# As usual in Ruby, sub! does the same as sub but also modifies
+# the object, the same for gsub!/gsub.
+
+# Sugared syntax yields the position of the match (or nil if no
+# match). Note that the object at the right of the operator needs
+# not to be a Regexp object (it can be a String). The "dont
+# match" operator yields true or false.
+meadow =~ /sheep/ # position of the match, nil if no match
+meadow !~ /sheep/ # true if doesn't match, false if it does
+# There is no sugared version for the substitution
+
+meadow =~ /\bovines?\b/i and print "Here be sheep!"
+
+string = "good food"
+string.sub!(/o*/, 'e')
+
+# % echo ababacaca | ruby -ne 'puts $& if /(a|ba|b)+(a|ac)+/'
+# ababa
+
+# The "global" (or "multiple") match is handled by String#scan
+scan (/(\d+)/) {
+ puts "Found number #{$1}"
+}
+
+# String#scan yields an Array if not used with a block
+numbers = scan(/\d+/)
+
+digits = "123456789"
+nonlap = digits.scan(/(\d\d\d)/)
+yeslap = digits.scan(/(?=(\d\d\d))/)
+puts "Non-overlapping: #{nonlap.join(' ')}"
+puts "Overlapping: #{yeslap.join(' ')}";
+# Non-overlapping: 123 456 789
+# Overlapping: 123 234 345 456 567 678 789
+
+string = "And little lambs eat ivy"
+string =~ /l[^s]*s/
+puts "(#$`) (#$&) (#$')"
+# (And ) (little lambs) ( eat ivy)
+
+
+# @@PLEAC@@_6.1
+# Ruby doesn't have the same problem:
+dst = src.sub('this', 'that')
+
+progname = $0.sub('^.*/', '')
+
+bindirs = %w(/usr/bin /bin /usr/local/bin)
+libdirs = bindirs.map { |l| l.sub('bin', 'lib') }
+
+
+# @@PLEAC@@_6.3
+/\S+/ # as many non-whitespace bytes as possible
+/[A-Za-z'-]+/ # as many letters, apostrophes, and hyphens
+
+/\b([A-Za-z]+)\b/ # usually best
+/\s([A-Za-z]+)\s/ # fails at ends or w/ punctuation
+
+
+# @@PLEAC@@_6.4
+require 'socket'
+str = 'www.ruby-lang.org and www.rubygarden.org'
+re = /
+ ( # capture the hostname in $1
+ (?: # these parens for grouping only
+ (?! [-_] ) # lookahead for neither underscore nor dash
+ [\w-] + # hostname component
+ \. # and the domain dot
+ ) + # now repeat that whole thing a bunch of times
+ [A-Za-z] # next must be a letter
+ [\w-] + # now trailing domain part
+ ) # end of $1 capture
+ /x # /x for nice formatting
+
+str.gsub! re do # pass a block to execute replacement
+ host = TCPsocket.gethostbyname($1)
+ "#{$1} [#{host[3]}]"
+end
+
+puts str
+#-----------------------------
+# to match whitespace or #-characters in an extended re you need to escape
+# them.
+
+foo = 42
+str = 'blah #foo# blah'
+str.gsub! %r/ # replace
+ \# # a pound sign
+ (\w+) # the variable name
+ \# # another pound sign
+ /x do
+ eval $1 # with the value of a local variable
+ end
+puts str # => blah 42 blah
+
+
+# @@PLEAC@@_6.5
+# The 'g' modifier doesn't exist in Ruby, a regexp can't be used
+# directly in a while loop; instead, use String#scan { |match| .. }
+fish = 'One fish two fish red fish blue fish'
+WANT = 3
+count = 0
+fish.scan(/(\w+)\s+fish\b/i) {
+ if (count += 1) == WANT
+ puts "The third fish is a #{$1} one."
+ end
+}
+
+if fish =~ /(?:\w+\s+fish\s+){2}(\w+)\s+fish/i
+ puts "The third fish is a #{$1} one."
+end
+
+pond = 'One fish two fish red fish blue fish'
+# String#scan without a block gives an array of matches, each match
+# being an array of all the specified groups
+colors = pond.scan(/(\w+)\s+fish\b/i).flatten # get all matches
+color = colors[2] # then the one we want
+# or without a temporary array
+color = pond.scan(/(\w+)\s+fish\b/i).flatten[2] # just grab element 3
+puts "The third fish in the pond is #{color}."
+
+count = 0
+fishes = 'One fish two fish red fish blue fish'
+evens = fishes.scan(/(\w+)\s+fish\b/i).select { (count+=1) % 2 == 0 }
+print "Even numbered fish are #{evens.join(' ')}."
+
+count = 0
+fishes.gsub(/
+ \b # makes next \w more efficient
+ ( \w+ ) # this is what we\'ll be changing
+ (
+ \s+ fish \b
+ )
+ /x) {
+ if (count += 1) == 4
+ 'sushi' + $2
+ else
+ $1 + $2
+ end
+}
+
+pond = 'One fish two fish red fish blue fish swim here.'
+puts "Last fish is #{pond.scan(/\b(\w+)\s+fish\b/i).flatten[-1]}"
+
+/
+ A # find some pattern A
+ (?! # mustn\'t be able to find
+ .* # something
+ A # and A
+ )
+ $ # through the end of the string
+/x
+
+# The "s" perl modifier is "m" in Ruby (not very nice since there is
+# also an "m" in perl..)
+pond = "One fish two fish red fish blue fish swim here."
+if (pond =~ /
+ \b ( \w+) \s+ fish \b
+ (?! .* \b fish \b )
+ /mix)
+ puts "Last fish is #{$1}."
+else
+ puts "Failed!"
+end
+
+
+# @@PLEAC@@_6.6
+#-----------------------------
+#!/usr/bin/ruby -w
+# killtags - very bad html killer
+$/ = nil; # each read is whole file
+while file = gets() do
+ file.gsub!(/<.*?>/m,''); # strip tags (terribly)
+ puts file # print file to STDOUT
+end
+#-----------------------------
+#!/usr/bin/ruby -w
+#headerfy - change certain chapter headers to html
+$/ = ''
+while file = gets() do
+ pattern = /
+ \A # start of record
+ ( # capture in $1
+ Chapter # text string
+ \s+ # mandatory whitespace
+ \d+ # decimal number
+ \s* # optional whitespace
+ : # a real colon
+ . * # anything not a newline till end of line
+ )
+ /x
+ puts file.gsub(pattern,'<H1>\1</H1>')
+end
+#-----------------------------
+#% ruby -00pe "gsub!(/\A(Chapter\s+\d+\s*:.*)/,'<H1>\1</H1>')" datafile
+
+#!/usr/bin/ruby -w
+#-----------------------------
+for file in ARGV
+ file = File.open(ARGV.shift)
+ while file.gets('') do # each read is a paragraph
+ print "chunk #{$.} in $ARGV has <<#{$1}>>\n" while /^START(.*?)^END/m
+ end # /m activates the multiline mode
+end
+#-----------------------------
+
+# @@PLEAC@@_6.7
+#-----------------------------
+$/ = nil;
+file = File.open("datafile")
+chunks = file.gets.split(/pattern/)
+#-----------------------------
+# .Ch, .Se and .Ss divide chunks of STDIN
+chunks = gets(nil).split(/^\.(Ch|Se|Ss)$/)
+print "I read #{chunks.size} chunks.\n"
+#-----------------------------
+
+
+# @@PLEAC@@_6.8
+while gets
+ if ~/BEGIN/ .. ~/END/
+ # line falls between BEGIN and END inclusive
+ end
+end
+
+while gets
+ if ($. == firstnum) .. ($. == lastnum)
+ # operate between firstnum and lastnum line number
+ end
+end
+
+# in ruby versions prior to 1.8, the above two conditional
+# expressions could be shortened to:
+# if /BEGIN/ .. /END/
+# and
+# if firstnum .. lastnum
+# but these now only work this way from the command line
+
+#-----------------------------
+
+while gets
+ if ~/BEGIN/ ... ~/END/
+ # line falls between BEGIN and END on different lines
+ end
+end
+
+while gets
+ if ($. == first) ... ($. == last)
+ # operate between first and last line number on different lines
+ end
+end
+
+#-----------------------------
+# command-line to print lines 15 through 17 inclusive (see below)
+ruby -ne 'print if 15 .. 17' datafile
+
+# print out all <XMP> .. </XMP> displays from HTML doc
+while gets
+ print if ~%r#<XMP>#i .. ~%r#</XMP>#i;
+end
+
+# same, but as shell command
+# ruby -ne 'print if %r#<XMP>#i .. %r#</XMP>#i' document.html
+#-----------------------------
+# ruby -ne 'BEGIN { $top=3; $bottom=5 }; \
+# print if $top .. $bottom' /etc/passwd # FAILS
+# ruby -ne 'BEGIN { $top=3; $bottom=5 }; \
+# print if $. == $top .. $. == $bottom' /etc/passwd # works
+# ruby -ne 'print if 3 .. 5' /etc/passwd # also works
+#-----------------------------
+print if ~/begin/ .. ~/end/;
+print if ~/begin/ ... ~/end/;
+#-----------------------------
+while gets
+ $in_header = $. == 1 .. ~/^$/ ? true : false
+ $in_body = ~/^$/ .. ARGF.eof ? true : false
+end
+#-----------------------------
+seen = {}
+ARGF.each do |line|
+ next unless line =~ /^From:?\s/i .. line =~ /^$/;
+ line.scan(%r/([^<>(),;\s]+\@[^<>(),;\s]+)/).each do |addr|
+ puts addr unless seen[addr]
+ seen[addr] ||= 1
+ end
+end
+
+
+# @@PLEAC@@_6.9
+def glob2pat(globstr)
+ patmap = {
+ '*' => '.*',
+ '?' => '.',
+ '[' => '[',
+ ']' => ']',
+ }
+ globstr.gsub!(/(.)/) { |c| patmap[c] || Regexp::escape(c) }
+ '^' + globstr + '$'
+end
+
+
+# @@PLEAC@@_6.10
+# avoid interpolating patterns like this if the pattern
+# isn't going to change:
+pattern = ARGV.shift
+ARGF.each do |line|
+ print line if line =~ /#{pattern}/
+end
+
+# the above creates a new regex each iteration. Instead,
+# use the /o modifier so the regex is compiled only once
+
+pattern = ARGV.shift
+ARGF.each do |line|
+ print line if line =~ /#{pattern}/o
+end
+
+#-----------------------------
+
+#!/usr/bin/ruby
+# popgrep1 - grep for abbreviations of places that say "pop"
+# version 1: slow but obvious way
+popstates = %w(CO ON MI WI MN)
+ARGF.each do |line|
+ popstates.each do |state|
+ if line =~ /\b#{state}\b/
+ print line
+ last
+ end
+ end
+end
+
+#-----------------------------
+#!/usr/bin/ruby
+# popgrep2 - grep for abbreviations of places that say "pop"
+# version 2: eval strings; fast but hard to quote
+popstates = %w(CO ON MI WI MN)
+code = "ARGF.each do |line|\n"
+popstates.each do |state|
+ code += "\tif line =~ /\\b#{state}\\b/; print(line); next; end\n"
+end
+code += "end\n"
+print "CODE IS\n---\n#{code}\n---\n" if false # turn on for debugging
+eval code
+
+# CODE IS
+# ---
+# ARGF.each do |line|
+# if line =~ /\bCO\b/; print(line); next; end
+# if line =~ /\bON\b/; print(line); next; end
+# if line =~ /\bMI\b/; print(line); next; end
+# if line =~ /\bWI\b/; print(line); next; end
+# if line =~ /\bMN\b/; print(line); next; end
+# end
+#
+# ---
+
+## alternatively, the same idea as above but compiling
+## to a case statement: (not in perlcookbook)
+#!/usr/bin/ruby -w
+# popgrep2.5 - grep for abbreviations of places that say "pop"
+# version 2.5: eval strings; fast but hard to quote
+popstates = %w(CO ON MI WI MN)
+code = "ARGF.each do |line|\n case line\n"
+popstates.each do |state|
+ code += " when /\\b#{state}\\b/ : print line\n"
+end
+code += " end\nend\n"
+print "CODE IS\n---\n#{code}\n---\n" if false # turn on for debugging
+eval code
+
+# CODE IS
+# ---
+# ARGF.each do |line|
+# case line
+# when /\bCO\b/ : print line
+# when /\bON\b/ : print line
+# when /\bMI\b/ : print line
+# when /\bWI\b/ : print line
+# when /\bMN\b/ : print line
+# end
+# end
+#
+# ---
+
+# Note: (above) Ruby 1.8+ allows the 'when EXP : EXPR' on one line
+# with the colon separator.
+
+#-----------------------------
+#!/usr/bin/ruby
+# popgrep3 - grep for abbreviations of places that say "pop"
+# version3: build a match_any function
+popstates = %w(CO ON MI WI MN)
+expr = popstates.map{|e|"line =~ /\\b#{e}\\b/"}.join('||')
+eval "def match_any(line); #{expr};end"
+ARGF.each do |line|
+ print line if match_any(line)
+end
+#-----------------------------
+
+## building a match_all function is a trivial
+## substitution of && for ||
+## here is a generalized example:
+#!/usr/bin/ruby -w
+## grepauth - print lines that mention both foo and bar
+class MultiMatch
+ def initialize(*patterns)
+ _any = build_match('||',patterns)
+ _all = build_match('&&',patterns)
+ eval "def match_any(line);#{_any};end\n"
+ eval "def match_all(line);#{_all};end\n"
+ end
+ def build_match(sym,args)
+ args.map{|e|"line =~ /#{e}/"}.join(sym)
+ end
+end
+
+mm = MultiMatch.new('foo','bar')
+ARGF.each do |line|
+ print line if mm.match_all(line)
+end
+#-----------------------------
+
+#!/usr/bin/ruby
+# popgrep4 - grep for abbreviations of places that say "pop"
+# version4: pretty fast, but simple: compile all re's first:
+popstates = %w(CO ON MI WI MN)
+popstates = popstates.map{|re| %r/\b#{re}\b/}
+ARGF.each do |line|
+ popstates.each do |state_re|
+ if line =~ state_re
+ print line
+ break
+ end
+ end
+end
+
+## speeds trials on the jargon file(412): 26006 lines, 1.3MB
+## popgrep1 => 7.040s
+## popgrep2 => 0.656s
+## popgrep2.5 => 0.633s
+## popgrep3 => 0.675s
+## popgrep4 => 1.027s
+
+# unless speed is criticial, the technique in popgrep4 is a
+# reasonable balance between speed and logical simplicity.
+
+
+# @@PLEAC@@_6.11
+begin
+ print "Pattern? "
+ pat = $stdin.gets.chomp
+ Regexp.new(pat)
+rescue
+ warn "Invalid Pattern"
+ retry
+end
+
+
+# @@PLEAC@@_6.13
+# uses the 'amatch' extension found on:
+# http://raa.ruby-lang.org/project/amatch/
+require 'amatch'
+matcher = Amatch.new('balast')
+#$relative, $distance = 0, 1
+File.open('/usr/share/dict/words').each_line do |line|
+ print line if matcher.search(line) <= 1
+end
+__END__
+#CODE
+ballast
+ballasts
+balustrade
+balustrades
+blast
+blasted
+blaster
+blasters
+blasting
+blasts
+
+
+# @@PLEAC@@_6.14
+str.scan(/\G(\d)/).each do |token|
+ puts "found #{token}"
+end
+#-----------------------------
+n = " 49 here"
+n.gsub!(/\G /,'0')
+puts n
+#-----------------------------
+str = "3,4,5,9,120"
+str.scan(/\G,?(\d+)/).each do |num|
+ puts "Found number: #{num}"
+end
+#-----------------------------
+# Ruby doesn't have the String.pos or a /c re modifier like Perl
+# But it does have StringScanner in the standard library (strscn)
+# which allows similar functionality:
+
+require 'strscan'
+text = 'the year 1752 lost 10 days on the 3rd of September'
+sc = StringScanner.new(text)
+while sc.scan(/.*?(\d+)/)
+ print "found: #{sc[1]}\n"
+end
+if sc.scan(/\S+/)
+ puts "Found #{sc[0]} after last number"
+end
+#-----------------------------
+# assuming continuing from above:
+puts "The position in 'text' is: #{sc.pos}"
+sc.pos = 30
+puts "The position in 'text' is: #{sc.pos}"
+
+
+# @@PLEAC@@_6.15
+#-----------------------------
+# greedy pattern
+str.gsub!(/<.*>/m,'') # not good
+
+# non-greedy (minimal) pattern
+str.gsub!(/<.*?>/m,'') # not great
+
+
+#-----------------------------
+#<b><i>this</i> and <i>that</i> are important</b> Oh, <b><i>me too!</i></b>
+#-----------------------------
+%r{ <b><i>(.*?)</i></b> }mx
+#-----------------------------
+%r/BEGIN((?:(?!BEGIN).)*)END/
+#-----------------------------
+%r{ <b><i>( (?: (?!</b>|</i>). )* ) </i></b> }mx
+#-----------------------------
+%r{ <b><i>( (?: (?!</[ib]>). )* ) </i></b> }mx
+#-----------------------------
+%r{
+ <b><i>
+ [^<]* # stuff not possibly bad, and not possibly the end.
+ (?:
+ # at this point, we can have '<' if not part of something bad
+ (?! </?[ib]> ) # what we can't have
+ < # okay, so match the '<'
+ [^<]* # and continue with more safe stuff
+ ) *
+ </i></b>
+ }mx
+
+
+# @@PLEAC@@_6.16
+#-----------------------------
+$/ = ""
+ARGF.each do |para|
+ para.scan %r/
+ \b # start at word boundary
+ (\S+) # find chunk of non-whitespace
+ \b # until a word boundary
+ (
+ \s+ # followed by whitespace
+ \1 # and that same chunk again
+ \b # and a word boundary
+ ) + # one or more times
+ /xi do
+ puts "dup word '#{$1}' at paragraph #{$.}"
+ end
+end
+#-----------------------------
+astr = 'nobody'
+bstr = 'bodysnatcher'
+if "#{astr} #{bstr}" =~ /^(\w+)(\w+) \2(\w+)$/
+ print "#{$2} overlaps in #{$1}-#{$2}-#{$3}"
+end
+#-----------------------------
+#!/usr/bin/ruby -w
+# prime_pattern -- find prime factors of argument using patterns
+ARGV << 180
+cap = 'o' * ARGV.shift
+while cap =~ /^(oo+?)\1+$/
+ print $1.size, " "
+ cap.gsub!(/#{$1}/,'o')
+end
+puts cap.size
+#-----------------------------
+#diophantine
+# solve for 12x + 15y + 16z = 281, maximizing x
+if ('o' * 281).match(/^(o*)\1{11}(o*)\2{14}(o*)\3{15}$/)
+ x, y, z = $1.size, $2.size, $3.size
+ puts "One solution is: x=#{x}; y=#{y}; z=#{z}"
+else
+ puts "No solution."
+end
+# => One solution is: x=17; y=3; z=2
+
+#-----------------------------
+# using different quantifiers:
+('o' * 281).match(/^(o+)\1{11}(o+)\2{14}(o+)\3{15}$/)
+# => One solution is: x=17; y=3; z=2
+
+('o' * 281).match(/^(o*?)\1{11}(o*)\2{14}(o*)\3{15}$/)
+# => One solution is: x=0; y=7; z=11
+
+('o' * 281).match(/^(o+?)\1{11}(o*)\2{14}(o*)\3{15}$/)
+# => One solution is: x=1; y=3; z=14
+
+
+# @@PLEAC@@_6.17
+# alpha OR beta
+%r/alpha|beta/
+
+# alpha AND beta
+%r/(?=.*alpha)(?=.*beta)/m
+
+# alpha AND beta, no overlap
+%r/alpha.*beta|beta.*alpha/m
+
+# NOT beta
+%r/^(?:(?!beta).)*$/m
+
+# NOT bad BUT good
+%r/(?=(?:(?!BAD).)*$)GOOD/m
+#-----------------------------
+
+if !(string =~ /pattern/) # ugly
+ something()
+end
+
+if string !~ /pattern/ # preferred
+ something()
+end
+
+
+#-----------------------------
+if string =~ /pat1/ && string =~ /pat2/
+ something()
+end
+#-----------------------------
+if string =~ /pat1/ || string =~ /pat2/
+ something()
+end
+#-----------------------------
+#!/usr/bin/ruby -w
+# minigrep - trivial grep
+pat = ARGV.shift
+ARGF.each do |line|
+ print line if line =~ /#{pat}/o
+end
+#-----------------------------
+ "labelled" =~ /^(?=.*bell)(?=.*lab)/m
+#-----------------------------
+$string =~ /bell/ && $string =~ /lab/
+#-----------------------------
+$murray_hill = "blah bell blah "
+if $murray_hill =~ %r{
+ ^ # start of string
+ (?= # zero-width lookahead
+ .* # any amount of intervening stuff
+ bell # the desired bell string
+ ) # rewind, since we were only looking
+ (?= # and do the same thing
+ .* # any amount of intervening stuff
+ lab # and the lab part
+ )
+ }mx # /m means . can match newline
+
+ print "Looks like Bell Labs might be in Murray Hill!\n";
+end
+#-----------------------------
+"labelled" =~ /(?:^.*bell.*lab)|(?:^.*lab.*bell)/
+#-----------------------------
+$brand = "labelled";
+if $brand =~ %r{
+ (?: # non-capturing grouper
+ ^ .*? # any amount of stuff at the front
+ bell # look for a bell
+ .*? # followed by any amount of anything
+ lab # look for a lab
+ ) # end grouper
+ | # otherwise, try the other direction
+ (?: # non-capturing grouper
+ ^ .*? # any amount of stuff at the front
+ lab # look for a lab
+ .*? # followed by any amount of anything
+ bell # followed by a bell
+ ) # end grouper
+ }mx # /m means . can match newline
+ print "Our brand has bell and lab separate.\n";
+end
+#-----------------------------
+$map =~ /^(?:(?!waldo).)*$/s
+#-----------------------------
+$map = "the great baldo"
+if $map =~ %r{
+ ^ # start of string
+ (?: # non-capturing grouper
+ (?! # look ahead negation
+ waldo # is he ahead of us now?
+ ) # is so, the negation failed
+ . # any character (cuzza /s)
+ ) * # repeat that grouping 0 or more
+ $ # through the end of the string
+ }mx # /m means . can match newline
+ print "There's no waldo here!\n";
+end
+=begin
+ 7:15am up 206 days, 13:30, 4 users, load average: 1.04, 1.07, 1.04
+
+USER TTY FROM LOGIN@ IDLE JCPU PCPU WHAT
+
+tchrist tty1 5:16pm 36days 24:43 0.03s xinit
+
+tchrist tty2 5:19pm 6days 0.43s 0.43s -tcsh
+
+tchrist ttyp0 chthon 7:58am 3days 23.44s 0.44s -tcsh
+
+gnat ttyS4 coprolith 2:01pm 13:36m 0.30s 0.30s -tcsh
+=end
+#% w | minigrep '^(?!.*ttyp).*tchrist'
+#-----------------------------
+%r{
+ ^ # anchored to the start
+ (?! # zero-width look-ahead assertion
+ .* # any amount of anything (faster than .*?)
+ ttyp # the string you don't want to find
+ ) # end look-ahead negation; rewind to start
+ .* # any amount of anything (faster than .*?)
+ tchrist # now try to find Tom
+}x
+#-----------------------------
+#% w | grep tchrist | grep -v ttyp
+#-----------------------------
+#% grep -i 'pattern' files
+#% minigrep '(?i)pattern' files
+#-----------------------------
+
+
+# @@PLEAC@@_6.20
+ans = $stdin.gets.chomp
+re = %r/^#{Regexp.quote(ans)}/
+case
+ when "SEND" =~ re : puts "Action is send"
+ when "STOP" =~ re : puts "Action is stop"
+ when "ABORT" =~ re : puts "Action is abort"
+ when "EDIT" =~ re : puts "Action is edit"
+end
+#-----------------------------
+require 'abbrev'
+table = Abbrev.abbrev %w-send stop abort edit-
+loop do
+ print "Action: "
+ ans = $stdin.gets.chomp
+ puts "Action for #{ans} is #{table[ans.downcase]}"
+end
+
+
+#-----------------------------
+# dummy values are defined for 'file', 'PAGER', and
+# the 'invoke_editor' and 'deliver_message' methods
+# do not do anything interesting in this example.
+#!/usr/bin/ruby -w
+require 'abbrev'
+
+file = 'pleac_ruby.data'
+PAGER = 'less'
+
+def invoke_editor
+ puts "invoking editor"
+end
+
+def deliver_message
+ puts "delivering message"
+end
+
+actions = {
+ 'edit' => self.method(:invoke_editor),
+ 'send' => self.method(:deliver_message),
+ 'list' => proc {system(PAGER, file)},
+ 'abort' => proc {puts "See ya!"; exit},
+ "" => proc {puts "Unknown Command"}
+}
+
+dtable = Abbrev.abbrev(actions.keys)
+loop do
+ print "Action: "
+ ans = $stdin.gets.chomp.delete(" \t")
+ actions[ dtable[ans.downcase] || "" ].call
+end
+
+
+# @@PLEAC@@_6.19
+#-----------------------------
+# basically, the Perl Cookbook categorizes this as an
+# unsolvable problem ...
+#-----------------------------
+1 while addr.gsub!(/\([^()]*\)/,'')
+#-----------------------------
+Dear someuser@host.com,
+
+Please confirm the mail address you gave us Wed May 6 09:38:41
+MDT 1998 by replying to this message. Include the string
+"Rumpelstiltskin" in that reply, but spelled in reverse; that is,
+start with "Nik...". Once this is done, your confirmed address will
+be entered into our records.
+
+
+# @@PLEAC@@_6.21
+#-----------------------------
+#% gunzip -c ~/mail/archive.gz | urlify > archive.urlified
+#-----------------------------
+#% urlify ~/mail/*.inbox > ~/allmail.urlified
+#-----------------------------
+#!/usr/bin/ruby -w
+# urlify - wrap HTML links around URL-like constructs
+
+urls = '(https?|telnet|gopher|file|wais|ftp)';
+ltrs = '\w';
+gunk = '/#~:.?+=&%@!\-';
+punc = '.:?\-';
+any = "#{ltrs}#{gunk}#{punc}";
+
+ARGF.each do |line|
+ line.gsub! %r/
+ \b # start at word boundary
+ ( # begin $1 {
+ #{urls} : # need resource and a colon
+ [#{any}] +? # followed by on or more
+ # of any valid character, but
+ # be conservative and take only
+ # what you need to....
+ ) # end $1 }
+ (?= # look-ahead non-consumptive assertion
+ [#{punc}]* # either 0 or more punctuation
+ [^#{any}] # followed by a non-url char
+ | # or else
+ $ # then end of the string
+ )
+ /iox do
+ %Q|<A HREF="#{$1}">#{$1}</A>|
+ end
+ print line
+end
+
+
+# @@PLEAC@@_6.23
+%r/^m*(d?c{0,3}|c[dm])(l?x{0,3}|x[lc])(v?i{0,3}|i[vx])$/i
+#-----------------------------
+str.sub!(/(\S+)(\s+)(\S+)/, '\3\2\1')
+#-----------------------------
+%r/(\w+)\s*=\s*(.*)\s*$/ # keyword is $1, value is $2
+#-----------------------------
+%r/.{80,}/
+#-----------------------------
+%r|(\d+)/(\d+)/(\d+) (\d+):(\d+):(\d+)|
+#-----------------------------
+str.gsub!(%r|/usr/bin|,'/usr/local/bin')
+#-----------------------------
+str.gsub!(/%([0-9A-Fa-f][0-9A-Fa-f])/){ $1.hex.chr }
+#-----------------------------
+str.gsub!(%r{
+ /\* # Match the opening delimiter
+ .*? # Match a minimal number of characters
+ \*/ # Match the closing delimiter
+}xm,'')
+#-----------------------------
+str.sub!(/^\s+/, '')
+str.sub!(/\s+$/, '')
+
+# but really, in Ruby we'd just do:
+str.strip!
+#-----------------------------
+str.gsub!(/\\n/,"\n")
+#-----------------------------
+str.sub!(/^.*::/, '')
+#-----------------------------
+%r/^([01]?\d\d|2[0-4]\d|25[0-5])\.([01]?\d\d|2[0-4]\d|25[0-5])\.
+ ([01]?\d\d|2[0-4]\d|25[0-5])\.([01]?\d\d|2[0-4]\d|25[0-5])$/x
+#-----------------------------
+str.sub!(%r|^.*/|, '')
+#-----------------------------
+cols = ( (ENV['TERMCAP'] || " ") =~ /:co#(\d+):/ ) ? $1 : 80;
+#-----------------------------
+name = " #{$0} #{ARGV}".gsub(%r| /\S+/|, ' ')
+#-----------------------------
+require 'rbconfig'
+include Config
+raise "This isn't Linux" unless CONFIG['target_os'] =~ /linux/i;
+#-----------------------------
+str.gsub!(%r/\n\s+/, ' ')
+#-----------------------------
+nums = str.scan(/(\d+\.?\d*|\.\d+)/)
+#-----------------------------
+capwords = str.scan(%r/(\b[^\Wa-z0-9_]+\b)/)
+#-----------------------------
+lowords = str.scan(%r/(\b[^\WA-Z0-9_]+\b)/)
+#-----------------------------
+icwords = str.scan(%r/(\b[^\Wa-z0-9_][^\WA-Z0-9_]*\b)/)
+#-----------------------------
+links = str.scan(%r/<A[^>]+?HREF\s*=\s*["']?([^'" >]+?)[ '"]?>/mi)
+#-----------------------------
+initial = str =~ /^\S+\s+(\S)\S*\s+\S/ ? $1 : ""
+#-----------------------------
+str.gsub!(%r/"([^"]*)"/, %q-``\1''-)
+#-----------------------------
+
+$/ = ""
+sentences = []
+ARGF.each do |para|
+ para.gsub!(/\n/, ' ')
+ para.gsub!(/ {3,}/,' ')
+ sentences << para.scan(/(\S.*?[!?.])(?= |\Z)/)
+end
+
+#-----------------------------
+%r/(\d{4})-(\d\d)-(\d\d)/ # YYYY in $1, MM in $2, DD in $3
+#-----------------------------
+%r/ ^
+ (?:
+ 1 \s (?: \d\d\d \s)? # 1, or 1 and area code
+ | # ... or ...
+ \(\d\d\d\) \s # area code with parens
+ | # ... or ...
+ (?: \+\d\d?\d? \s)? # optional +country code
+ \d\d\d ([\s\-]) # and area code
+ )
+ \d\d\d (\s|\1) # prefix (and area code separator)
+ \d\d\d\d # exchange
+ $
+ /x
+#-----------------------------
+%r/\boh\s+my\s+gh?o(d(dess(es)?|s?)|odness|sh)\b/i
+#-----------------------------
+lines = []
+lines << $1 while input.sub!(/^([^\012\015]*)(\012\015?|\015\012?)/,'')
+
+
+# @@PLEAC@@_7.0
+# An IO object being Enumerable, we can use 'each' directly on it
+File.open("/usr/local/widgets/data").each { |line|
+ puts line if line =~ /blue/
+}
+
+logfile = File.new("/var/log/rubylog.txt", "w")
+mysub($stdin, logfile)
+
+# The method IO#readline is similar to IO#gets
+# but throws an exception when it reaches EOF
+f = File.new("bla.txt")
+begin
+ while (line = f.readline)
+ line.chomp
+ $stdout.print line if line =~ /blue/
+ end
+rescue EOFError
+ f.close
+end
+
+while $stdin.gets # reads from STDIN
+ unless (/\d/)
+ $stderr.puts "No digit found." # writes to STDERR
+ end
+ puts "Read: #{$_}" # writes to STDOUT
+end
+
+logfile = File.new("/tmp/log", "w")
+
+logfile.close
+
+# $defout (or its synonym '$>') is the destination of output
+# for Kernel#print, Kernel#puts, and family functions
+logfile = File.new("log.txt", "w")
+old = $defout
+$defout = logfile # switch to logfile for output
+puts "Countdown initiated ..."
+$defout = old # return to original output
+puts "You have 30 seconds to reach minimum safety distance."
+
+
+# @@PLEAC@@_7.1
+source = File.new(path, "r") # open file "path" for reading only
+sink = File.new(path, "w") # open file "path" for writing only
+
+source = File.open(path, File::RDONLY) # open file "path" for reading only
+sink = File.open(path, File::WRONLY) # open file "path" for writing only
+
+file = File.open(path, "r+") # open "path" for reading and writing
+file = File.open(path, flags) # open "path" with the flags "flags" (see examples below for flags)
+
+# open file "path" read only
+file = File.open(path, "r")
+file = File.open(path, File::RDONLY)
+
+# open file "path" write only, create it if it does not exist
+# truncate it to zero length if it exists
+file = File.open(path, "w")
+file = File.open(path, File::WRONLY|File::TRUNC|File::CREAT)
+file = File.open(path, File::WRONLY|File::TRUNC|File::CREAT, 0666) # with permission 0666
+
+# open file "path" write only, fails if file exists
+file = File.open(path, File::WRONLY|File::EXCL|File::CREAT)
+file = File.open(path, File::WRONLY|File::EXCL|File::CREAT, 0666)
+
+# open file "path" for appending
+file = File.open(path, "a")
+file = File.open(path, File::WRONLY|File::APPEND|File::CREAT)
+file = File.open(path, File::WRONLY|File::APPEND|File::CREAT, 0666)
+
+# open file "path" for appending only when file exists
+file = File.open(path, File::WRONLY|File::APPEND)
+
+# open file "path" for reading and writing
+file = File.open(path, "r+")
+file = File.open(path, File::RDWR)
+
+# open file for reading and writing, create a new file if it does not exist
+file = File.open(path, File::RDWR|File::CREAT)
+file = File.open(path, File::RDWR|File::CREAT, 0600)
+
+# open file "path" reading and writing, fails if file exists
+file = File.open(path, File::RDWR|File::EXCL|File::CREAT)
+file = File.open(path, File::RDWR|File::EXCL|File::CREAT, 0600)
+
+
+# @@PLEAC@@_7.2
+# No problem with Ruby since the filename doesn't contain characters with
+# special meaning; like Perl's sysopen
+File.open(filename, 'r')
+
+
+# @@PLEAC@@_7.3
+File.expand_path('~root/tmp')
+#=> "/root/tmp"
+File.expand_path('~rpcuser')
+#=> "/var/lib/nfs"
+
+# To expand ~/.. it explicitely needs the environment variable HOME
+File.expand_path('~/tmp')
+#=> "/home/gc/tmp"
+
+
+# @@PLEAC@@_7.4
+# The exception raised in Ruby reports the filename
+File.open('afile')
+
+
+# @@PLEAC@@_7.5
+# Standard Ruby distribution provides the following useful extension
+require 'tempfile'
+# With the Tempfile class, the file is automatically deleted on garbage
+# collection, so you won't need to remove it, later on.
+tf = Tempfile.new('tmp') # a name is required to create the filename
+
+# If you need to pass the filename to an external program you can use
+# File#path, but don't forget to File#flush in order to flush anything
+# living in some buffer somewhere.
+tf.flush
+system("/usr/bin/dowhatever #{tf.path}")
+
+fh = Tempfile.new('tmp')
+fh.sync = true # autoflushes
+10.times { |i| fh.puts i }
+fh.rewind
+puts 'Tmp file has: ', fh.readlines
+
+
+# @@PLEAC@@_7.6
+while (DATA.gets) do
+ # process the line
+end
+__END__
+# your data goes here
+# __DATA__ doesn't exist in Ruby
+
+#CODE
+# get info about the script (size, date of last modification)
+kilosize = DATA.stat.size / 1024
+last_modif = DATA.stat.mtime
+puts "<P>Script size is #{kilosize}"
+puts "<P>Last script update: #{last_modif}"
+__END__
+# DO NOT REMOVE THE PRECEEDING LINE.
+# Everything else in this file will be ignored.
+#CODE
+
+
+# @@PLEAC@@_7.7
+while line = gets do
+ # do something with line.
+end
+
+# or
+while gets do
+ # do something with $_
+end
+
+# or more rubyish
+$stdun.each do |line|
+ # do stuff with line
+end
+
+
+# ARGF may makes this more easy
+# this is skipped if ARGV.size==0
+ARGV.each do |filename|
+ # closing and exception handling are done by the block
+ open(filename) do |fd|
+ fd.each do |line|
+ # do stuff with line
+ end
+ end rescue abort("can't open %s" % filename)
+end
+
+# globbing is done in the Dir module
+ARGV = Dir["*.[Cch]"] if ARGV.empty?
+
+# note: optparse is the preferred way to handle this
+if (ARGV[0] == '-c')
+ chop_first += 1
+ ARGV.shift
+end
+
+
+# processing numerical options
+if ARGV[0] =~ /^-(\d+)$/
+ columns = $1
+ ARGV.shift
+end
+
+# again, better to use optparse:
+require 'optparse'
+nostdout = 0
+append = 0
+unbuffer = 0
+ignore_ints = 0
+ARGV.options do |opt|
+ opt.on('-n') { nostdout +=1 }
+ opt.on('-a') { append +=1 }
+ opt.on('-u') { unbuffer +=1 }
+ opt.on('-i') { ignore_ints +=1 }
+ opt.parse!
+end or abort("usage: " + __FILE__ + " [-ainu] [filenames]")
+
+# no need to do undef $/, we have File.read
+str = File.read(ARGV[0])
+
+# again we have File.read
+str = File.read(ARGV[0])
+
+# not sure what this should do:
+# I believe open the file, print filename, lineno and line:
+ARGF.each_with_index do |line, idx|
+ print ARGF.filename, ":", idx, ";", line
+end
+
+# print all the lines in every file passed via command line that contains login
+ARGF.each do |line|
+ puts line if line =~ /login/
+end
+#
+# even this would fit
+#%ruby -ne "print if /f/" 2.log
+#
+
+ARGF.each { |l| puts l.downcase! }
+
+#------------------
+#!/usr/bin/ruby -p
+# just like perl's -p
+$_.downcase!
+#
+
+# I don't know who should I trust.
+# perl's version splits on \w+ while python's on \w.
+
+chunks = 0
+
+File.read(ARGV[0]).split.each do |word|
+ next if word =~ /^#/
+ break if ["__DATA__", "__END__"].member? word
+ chunks += 1
+end
+
+print "Found ", chunks, " chunks\n"
+
+
+# @@PLEAC@@_7.8
+old = File.open(old_file)
+new = File.open(new_file, "w")
+while old.gets do
+ # change $_, then...
+ new.print $_
+end
+old.close
+new.close
+File.rename(old_file, "old.orig")
+File.rename(new_file, old_file)
+
+while old.gets do
+ if $. == 20 then # we are at the 20th line
+ new.puts "Extra line 1"
+ new.puts "Extra line 2"
+ end
+ new.print $_
+end
+
+while old.gets do
+ next if 20..30 # skip the 20th line to the 30th
+ # Ruby (and Perl) permit to write if 20..30
+ # instead of if (20 <= $.) and ($. <= 30)
+ new.print $_
+end
+
+
+# @@PLEAC@@_7.9
+#% ruby -i.orig -pe 'FILTER COMMAND' file1 file2 file3 ...
+#
+#-----------------------------
+##!/usr/bin/ruby -i.orig -p
+# filter commands go here
+#-----------------------------
+
+#% ruby -pi.orig -e 'gsub!(/DATE/){Time.now)'
+
+# effectively becomes:
+ARGV << 'I'
+oldfile = ""
+while gets
+ if ARGF.filename != oldfile
+ newfile = ARGF.filename
+ File.rename(newfile, newfile + ".orig")
+ $stdout = File.open(newfile,'w')
+ oldfile = newfile
+ end
+ gsub!(/DATE/){Time.now}
+ print
+end
+$stdout = STDOUT
+#-----------------------------
+#% ruby -i.old -pe 'gsub!(%r{\bhisvar\b}, 'hervar')' *.[Cchy]
+
+#-----------------------------
+# set up to iterate over the *.c files in the current directory,
+# editing in place and saving the old file with a .orig extension
+$-i = '.orig' # set up -i mode
+ARGV.replace(Dir['*.[Cchy]'])
+while gets
+ if $. == 1
+ print "This line should appear at the top of each file\n"
+ end
+ gsub!(/\b(p)earl\b/i, '\1erl') # Correct typos, preserving case
+ print
+ ARGF.close if ARGF.eof
+end
+
+
+# @@PLEAC@@_7.10
+File.open('itest', 'r+') do |f| # open file for update
+ lines = f.readlines # read into array of lines
+ lines.each do |it| # modify lines
+ it.gsub!(/foo/, 'QQQ')
+ end
+ f.pos = 0 # back to start
+ f.print lines # write out modified lines
+ f.truncate(f.pos) # truncate to new length
+end # file is automatically closed
+#-----------------------------
+File.open('itest', 'r+') do |f|
+ out = ""
+ f.each do |line|
+ out << line.gsub(/DATE/) {Time.now}
+ end
+ f.pos = 0
+ f.print out
+ f.truncate(f.pos)
+end
+
+# @@PLEAC@@_7.11
+File.open('infile', 'r+') do |f|
+ f.flock File::LOCK_EX
+ # update file
+end
+#-----------------------------
+File::LOCK_SH # shared lock (for reading)
+File::LOCK_EX # exclusive lock (for writing)
+File::LOCK_NB # non-blocking request
+File::LOCK_UN # free lock
+#-----------------------------
+unless f.flock File::LOCK_EX | File::LOCK_NB
+ warn "can't get immediate lock: blocking ..."
+ f.flock File::LOCK_EX
+end
+#-----------------------------
+File.open('numfile', File::RDWR|File::CREAT) do |f|
+ f.flock(File::LOCK_EX)
+ num = f.gets.to_i || 0
+ f.pos = 0
+ f.truncate 0
+ f.puts num + 1q
+end
+
+
+# @@PLEAC@@_7.12
+output_handle.sync = true
+# Please note that like in Perl, $stderr is already unbuffered
+#-----------------------------
+#!/usr/bin/ruby -w
+# seeme - demo stdio output buffering
+$stdout.sync = ARGV.size > 0
+print "Now you don't see it..."
+sleep 2
+puts "now you do"
+#-----------------------------
+$stderr.sync = true
+afile.sync = false
+#-----------------------------
+# assume 'remote_con' is an interactive socket handle,
+# but 'disk_file' is a handle to a regular file.
+remote_con.sync = true # unbuffer for clarity
+disk_file.sync = false # buffered for speed
+#-----------------------------
+require 'socket'
+sock = TCPSocket.new('www.ruby-lang.org', 80)
+sock.sync = true
+sock.puts "GET /en/ HTTP/1.0 \n\n"
+resp = sock.read
+print "DOC IS: #{resp}\n"
+
+
+# @@PLEAC@@_7.13
+#-----------------------------
+# assumes fh1, fh2, fh2 are oen IO objects
+nfound = select([$stdin, fh1, fh2, fh3], nil, nil, 0)
+nfound[0].each do |file|
+ case file
+ when fh1
+ # do something with fh1
+ when fh2
+ # do something with fh2
+ when fh3
+ # do something with fh3
+ end
+end
+#-----------------------------
+input_files = []
+# repeat next line for all in-files to poll
+input_files << fh1
+if nfound = select(input_files, nil, nil, 0)
+ # input ready on files in nfound[0]
+end
+
+
+# @@PLEAC@@_8.0
+#-----------------------------
+# datafile is a file or IO object
+datafile.readlines.each { |line|
+ line.chomp!
+ size = line.length
+ puts size
+}
+#-----------------------------
+datafile.readlines.each { |line|
+ puts line.chomp!.length
+}
+#-----------------------------
+lines = datafile.readlines
+#-----------------------------
+whole_file = file.read
+#-----------------------------
+# ruby -040 -e 'word = gets; puts "First word is #{word}"'
+#-----------------------------
+# ruby -ne 'BEGIN { $/="%%\n" }; $_.chomp; puts $_ if( $_=~/Unix/i)' fortune.dat
+#-----------------------------
+handle.print "one", "two", "three" # "onetwothree"
+puts "Baa baa black sheep." # sent to $stdout
+#-----------------------------
+buffer = handle.read(4096)
+rv = buffer.length
+#-----------------------------
+handle.truncate(length)
+open("/tmp#{$$}.pid", 'w') { |handle| handle.truncate(length) }
+#-----------------------------
+pos = datafile.pos # tell is an alias of pos
+puts "I'm #{pos} bytes from the start of datafile"
+#-----------------------------
+logfile.seek(0, IO::SEEK_END)
+datafile.seek(pos) # IO::SEEK_SET is the default
+out.seek(-20, IO::SEEK_CUR)
+#-----------------------------
+written = datafile.syswrite(mystring)
+raise RunTimeError unless written == mystring.length
+block = infile.sysread(256) # no equivalent to perl offset parameter in sysread
+puts "only read #{block.length} bytes" if 256 != block.length
+#-----------------------------
+pos = handle.sysseek(0, IO::SEEK_CUR) # don't change position
+
+
+# @@PLEAC@@_8.1
+while (line = fh.gets)
+ line.chomp!
+ nextline = nil
+ line.gsub!(/\\$/) { |match| nextline = fh.gets; '' }
+ if (nextline != nil)
+ line += nextline
+ redo
+ end
+ # process full record in line here
+end
+#-----------------------------
+# DISTFILES = $(DIST_COMMON) $(SOURCES) $(HEADERS) \
+# $(TEXINFOS) $(INFOS) $(MANS) $(DATA)
+# DEP_DISTFILES = $(DIST_COMMON) $(SOURCES) $(HEADERS) \
+# $(TEXINFOS) $(INFO_DEPS) $(MANS) $(DATA) \
+# $(EXTRA_DIST)
+#-----------------------------
+line.gsub!(/\\\s*$/, '') {
+ # as before
+}
+
+
+# @@PLEAC@@_8.2
+#-----------------------------
+count = `wc -l < #{filename}`
+fail "wc failed: #{$?}" if $? != 0
+count.chomp!
+#-----------------------------
+count = 0
+File.open(file, 'r') { |fh|
+ count += 1 while fh.gets
+}
+# count now holds the number of lines read
+#-----------------------------
+count = 0
+while (chunk = file.sysread(2**16))
+ count += chunk.count("\n")
+end rescue EOFError
+#-----------------------------
+File.open(filename,'r') { |fh|
+ count += 1 while fh.gets
+}
+# count now holds the number of lines read
+#-----------------------------
+# As ruby doesn't quite have an equivalent to using a for
+# statement as in perl, I threw this in
+count = File.readlines(filename).size
+#-----------------------------
+1 while file.gets
+count = $.
+#-----------------------------
+$/ = ''
+open(filename, 'r') { |fh|
+ 1 while fh.gets
+ para_count = $.
+} rescue fail("can't open #{filename}: $!")
+#-----------------------------
+
+
+# ^^PLEAC^^_8.3
+#-----------------------------
+while (gets)
+ split.each { |chunk|
+ # do something with chunk
+ }
+end
+#-----------------------------
+while (gets)
+ gsub(/(\w[\w'-]*)/) { |word|
+ # do something with word
+ }
+end
+#-----------------------------
+# Make a word frequency count
+# normally hashes can be created using {} or just Hash.new
+# but we want the default value of an entry to be 0 instead
+# of nil. (nil can't be incremented)
+seen = Hash.new(0)
+while (gets)
+ gsub(/(\w[\w'-]*)/) { |word|
+ seen[word.downcase] += 1
+ }
+end
+# output hash in a descending numeric sort of its values
+seen.sort { |a,b| b[1] <=> a[1] }.each do |k,v|
+ printf("%5d %s\n", v, k )
+end
+
+#-----------------------------
+# Line frequency count
+seen = Hash.new(0)
+while (gets)
+ seen[$_.downcase] += 1
+end
+seen.sort { |a,b| b[1] <=> a[1] }.each do |k,v|
+ printf("%5d %s\n", v, k )
+end
+#-----------------------------
+
+
+# @@PLEAC@@_8.4
+#-----------------------------
+# instead of file handle FILE, we can just
+# use a string containing the filename
+File.readlines(file).each { |line|
+ # do something with line
+}
+#-----------------------------
+File.readlines(file).reverse_each { |line|
+ # do something with line
+}
+#-----------------------------
+# the variable lines might have been created
+# this way
+# lines = File.readlines(file)
+#
+# normally one would use the reverse_each, but
+# if you insist on using a numerical index to
+# iterate over the lines array...
+(lines.size - 1).downto(0) { |i|
+ line = lines[i]
+}
+#-----------------------------
+# the second readlines argument is a the
+# record separator $/, just like perl, a blank
+# separator splits the records into paragraphs
+File.readlines(file, '').each { |paragraph|
+ # do something with paragraph
+ puts "->Paragraph #{paragraph}"
+}
+#-----------------------------
+
+
+# @@PLEAC@@_8.6
+
+$/ = "%\n";
+srand;
+
+File.open('/usr/share/fortune/humorists').each do |line|
+ adage = line if rand($.) < 1
+end
+
+puts adage;
+
+
+# @@PLEAC@@_8.10
+begin
+ fh = File.open(file, "r+")
+ addr = fh.tell unless fh.eof while fh.gets
+ fh.truncate(addr)
+rescue SystemCallError
+ $stderr.puts "#$!"
+end
+
+
+# @@PLEAC@@_9.0
+entry = File.stat("/usr/bin/vi")
+entry = File.stat("/usr/bin")
+entry = File.stat(INFILE)
+
+entry = File.stat("/usr/bin/vi")
+ctime = entry.ctime
+size = entry.size
+
+f = File.open(filename, "r")
+
+## There is no -T equivalent in Ruby, but we can still test emptiness
+if test(?s, filename)
+ puts "#{filename} doesn't have text in it."
+ exit
+end
+
+Dir.new("/usr/bin").each do |filename|
+ puts "Inside /usr/bin is something called #{filename}"
+end
+
+
+# @@PLEAC@@_9.1
+file = File.stat("filename")
+readtime, writetime = file.atime, file.mtime
+file.utime(readtime, writetime)
+
+SECONDS_PER_DAY = 60 * 60 * 24
+file = File.stat("filename")
+atime, mtime = file.atime, file.mtime
+
+atime -= 7 * SECONDS_PER_DAY
+mtime -= 7 * SECONDS_PER_DAY
+
+File.utime(atime, mtime, file)
+mtime = File.stat(file).mtime
+File.utime(Time.new, mtime, file)
+File.utime(Time.new, File.stat("testfile").mtime, file)
+
+#-----------------------------
+#!/usr/bin/ruby -w
+## uvi - vi a file without changing it's access times
+
+if ARGV.length != 1
+ puts "usage: uvi filename"
+ exit
+end
+file = ARGV[0]
+atime, mtime = File.stat(file).atime, File.stat(file).mtime
+system(ENV["EDITOR"] || "vi", file)
+File.utime(atime, mtime, file)
+#-----------------------------
+
+
+# @@PLEAC@@_9.2
+File.unlink(FILENAME)
+
+err_flg = false
+filenames.each do |file|
+ begin
+ File.unlink(file)
+ rescue
+ err_flg = $!
+ end
+end
+err_flg and raise "Couldn't unlink all of #{filenames.join(" ")}: #{err_flg}"
+
+File.unlink(file)
+
+count = filenames.length
+filenames.each do |file|
+ begin
+ File.unlink(file)
+ rescue
+ count -= 1
+ end
+end
+if count != filenames.length
+ STDERR.puts "could only delete #{count} of #{filenames.length} files"
+end
+
+
+# @@PLEAC@@_9.3
+require "ftools"
+File.copy(oldfile, newfile)
+
+infile = File.open(oldfile, "r")
+outfile = File.open(newfile, "w")
+
+blksize = infile.stat.blksize
+# This doesn't handle partial writes or ^Z
+# like the Perl version does.
+while (line = infile.read(blksize))
+ outfile.write(line)
+end
+
+infile.close
+outfile.close
+
+system("cp #{oldfile} #{newfile}") # unix
+system("copy #{oldfile} #{newfile}") # dos, vms
+
+require "ftools"
+File.copy("datafile.dat", "datafile.bak")
+File.move("datafile.new", "datafile.dat")
+
+
+# @@PLEAC@@_9.4
+$seen = {} # must use global var to be seen inside of method below
+
+def do_my_thing(filename)
+ dev, ino = File.stat(filename).dev, File.stat(filename).ino
+ unless $seen[[dev, ino]]
+ # do something with $filename because we haven't
+ # seen it before
+ end
+ $seen[[dev, ino]] = $seen[[dev, ino]].to_i + 1
+end
+
+files.each do |filename|
+ dev, ino = File.stat(filename).dev, File.stat(filename).ino
+ if !$seen.has_key?([dev, ino])
+ $seen[[dev, ino]] = []
+ end
+ $seen[[dev, ino]].push(filename)
+end
+
+$seen.keys.sort.each do |devino|
+ ino, dev = devino
+ if $seen[devino].length > 1
+ # $seen[devino] is a list of filenames for the same file
+ end
+end
+
+
+# @@PLEAC@@_9.5
+Dir.open(dirname) do |dir|
+ dir.each do |file|
+ # do something with dirname/file
+ puts file
+ end
+end
+# Dir.close is automatic
+
+# No -T equivalent in Ruby
+
+dir.each do |file|
+ next if file =~ /^\.\.?$/
+ # ...
+end
+
+def plainfiles(dir)
+ dh = Dir.open(dir)
+ dh.entries.grep(/^[^.]/).
+ map {|file| "#{dir}/#{file}"}.
+ find_all {|file| test(?f, file)}.
+ sort
+end
+
+
+# @@PLEAC@@_9.6
+list = Dir.glob("*.c")
+
+dir = Dir.open(path)
+files = dir.entries.grep(/\.c$/)
+dir.close
+
+files = Dir.glob("*.c")
+files = Dir.open(path).entries.grep(/\.[ch]$/i)
+
+dir = Dir.new(path)
+files = dir.entries.grep(/\.[ch]$/i)
+
+begin
+ d = Dir.open(dir)
+rescue Errno::ENOENT
+ raise "Couldn't open #{dir} for reading: #{$!}"
+end
+
+files = []
+d.each do |file|
+ puts file
+ next unless file =~ /\.[ch]$/i
+
+ filename = "#{dir}/#{file}"
+ # There is no -T equivalent in Ruby, but we can still test emptiness
+ files.push(filename) if test(?s, filename)
+end
+
+dirs.entries.grep(/^\d+$/).
+ map { |file| [file, "#{path}/#{file}"]} .
+ select { |file| test(?d, file[1]) }.
+ sort { |a,b| a[0] <=> b[0] }.
+ map { |file| file[1] }
+
+
+# @@PLEAC@@_9.7
+require 'find'
+Find.find(dirlist) do |file|
+ # do whatever
+end
+
+require 'find'
+argv = ARGV.empty? ? %w{.} : ARGV
+Find.find(*argv) do |file|
+ print file, (test(?d, file) ? "/\n" : "\n")
+end
+
+require 'find'
+argv = ARGV.empty? ? %w{.} : ARGV
+sum = 0
+Find.find(*argv) do |file|
+ size = test(?s, file) || 0
+ sum += size
+end
+puts "#{argv.join(' ')} contains #{sum} bytes"
+
+require 'find'
+argv = ARGV.empty? ? %w{.} : ARGV
+saved_size, saved_name = -1, ""
+Find.find(*argv) do |file|
+ size = test(?s, file) || 0
+ next unless test(?f, file) && size > saved_size
+ saved_size = size
+ saved_name = file
+end
+puts "Biggest file #{saved_name} in #{argv.join(' ')} is #{saved_size}"
+
+require 'find'
+argv = ARGV.empty? ? %w{.} : ARGV
+age, name = nil
+Find.find(*argv) do |file|
+ mtime = File.stat(file).mtime
+ next if age && age > mtime
+ age = mtime
+ name = file
+end
+puts "#{name} #{age}"
+
+#-----------------------------
+#!/usr/bin/ruby -w
+# fdirs - find all directories
+require 'find'
+argv = ARGV.empty? ? %w{.} : ARGV
+File.find(*argv) { |file| puts file if test(?d, file) }
+#-----------------------------
+
+
+# @@PLEAC@@_9.8
+require 'fileutils'
+
+puts "Usage #{$0} dir ..." if ARGV.empty?
+ARGV.each do |dir|
+ FileUtils.rmtree(dir)
+end
+
+
+# @@PLEAC@@_9.9
+require 'ftools'
+names.each do |file|
+ newname = file
+ begin
+ File.move(file, newname)
+ rescue Errno::EPERM
+ $stderr.puts "Couldn't rename #{file} to #{newname}: #{$!}"
+ end
+end
+
+require 'ftools'
+op = ARGV.empty? ? (raise "Usage: rename expr [files]\n") : ARGV.shift
+argv = ARGV.empty? ? $stdin.readlines.map { |f| f.chomp } : ARGV
+argv.each do |file|
+ was = file
+ file = eval("file.#{op}")
+ File.move(was, file) unless was == file
+end
+
+
+# @@PLEAC@@_9.10
+base = File.basename(path)
+dir = File.dirname(path)
+# ruby has no fileparse equivalent
+dir, base = File.split(path)
+ext = base.scan(/\..*$/).to_s
+
+path = '/usr/lib/libc.a'
+file = File.basename(path)
+dir = File.dirname(path)
+
+puts "dir is #{dir}, file is #{file}"
+# dir is /usr/lib, file is libc.a
+
+path = '/usr/lib/libc.a'
+dir, filename = File.split(path)
+name, ext = filename.split(/(?=\.)/)
+puts "dir is #{dir}, name is #{name}, ext is #{ext}"
+# NOTE: The Ruby code prints
+# dir is /usr/lib, name is libc, extension is .a
+# while the Perl code prints a '/' after the directory name
+# dir is /usr/lib/, name is libc, extension is .a
+
+# No fileparse_set_fstype() equivalent in ruby
+
+def extension(path)
+ ext = path.scan(/\..*$/).to_s
+ ext.sub(/^\./, "")
+end
+
+
+# @@PLEAC@@_9.11
+#-----------------------------
+#!/usr/bin/ruby -w
+# symirror - build spectral forest of symlinks
+
+require 'find'
+require 'fileutils'
+
+raise "usage: #{$0} realdir mirrordir" unless ARGV.size == 2
+
+srcdir,dstdir = ARGV
+srcmode = File::stat(srcdir).mode
+Dir.mkdir(dstdir, srcmode & 07777) unless test(?d, dstdir)
+
+# fix relative paths
+Dir.chdir(srcdir) {srcdir = Dir.pwd}
+Dir.chdir(dstdir) {dstdir = Dir.pwd}
+
+Find.find(srcdir) do |srcfile|
+ if test(?d, srcfile)
+ dest = srcfile.sub(/^#{srcdir}/, dstdir)
+ dmode = File::stat(srcfile).mode & 07777
+ Dir.mkdir(dest, dmode) unless test(?d, dest)
+ a = Dir["#{srcfile}/*"].reject{|f| test(?d, f)}
+ FileUtils.ln_s(a, dest)
+ end
+end
+
+
+# @@PLEAC@@_9.12
+# we use the Getopt/Declare library here for convenience:
+# http://raa.ruby-lang.org/project/getoptdeclare/
+#-----------------------------
+#!/usr/bin/ruby -w
+# lst - list sorted directory contents (depth first)
+
+require 'find'
+require 'etc'
+require "Getopt/Declare"
+
+# Note: in the option-spec below there must by at least one hard
+# tab in between each -option and its description. For example
+# -i <tab> read from stdin
+
+opts = Getopt::Declare.new(<<'EOPARAM')
+ ============
+ Input Format:
+ -i read from stdin
+ ============
+ Output Format:
+ -l long listing
+ -r reverse listing
+ ============
+ Sort on: (one of)
+ -m mtime (modify time - default)
+ {$sort_criteria = :mtime}
+ -u atime (access time)
+ {$sort_criteria = :atime}
+ -c ctime (inode change time)
+ {$sort_criteria = :ctime}
+ -s size
+ {$sort_criteria = :size}
+ [mutex: -m -u -c -s]
+
+EOPARAM
+
+$sort_criteria ||= :mtime
+files = {}
+DIRS = opts['-i'] ? $stdin.readlines.map{|f|f.chomp!} : ARGV
+DIRS.each do |dir|
+ Find.find(dir) do |ent|
+ files[ent] = File::stat(ent)
+ end
+end
+entries = files.keys.sort_by{|f| files[f].send($sort_criteria)}
+entries = entries.reverse unless opts['-r']
+
+entries.each do |ent|
+ unless opts['-l']
+ puts ent
+ next
+ end
+ stats = files[ent]
+ ftime = stats.send($sort_criteria == :size ? :mtime : $sort_criteria)
+ printf "%6d %04o %6d %8s %8s %8d %s %s\n",
+ stats.ino,
+ stats.mode & 07777,
+ stats.nlink,
+ ETC::PASSWD[stats.uid].name,
+ ETC::GROUP[stats.gid].name,
+ stats.size,
+ ftime.strftime("%a %b %d %H:%M:%S %Y"),
+ ent
+end
+
+
+# @@PLEAC@@_10.0
+def hello
+ $greeted += 1 # in Ruby, a variable beginning with $ is global (can be any type of course)
+ puts "hi there!"
+end
+
+# We need to initialize $greeted before it can be used, because "+=" is waiting a Numeric object
+$greeted = 0
+hello # note that appending () is optional to function calls with no parameters
+
+
+# @@PLEAC@@_10.1
+# In Ruby, parameters are named anyway
+def hypotenuse(side1, side2)
+ Math.sqrt(side1**2 + side2**2) # the sqrt function comes from the Math module
+end
+diag = hypotenuse(3, 4)
+
+puts hypotenuse(3, 4)
+
+a = [3, 4]
+print hypotenuse(*a) # the star operator will magically convert an Array into a "tuple"
+
+both = men + women
+
+# In Ruby, all objects are references, so the same problem arises; we then return a new object
+nums = [1.4, 3.5, 6.7]
+def int_all(n)
+ n.collect { |v| v.to_i }
+end
+ints = int_all(nums)
+
+nums = [1.4, 3.5, 6.7]
+def trunc_em(n)
+ n.collect! { |v| v.to_i } # the bang-version of collect modifies the object
+end
+trunc_em(nums)
+
+# Ruby has two chomp version:
+# ``chomp'' chomps the record separator and returns what's expected
+# ``chomp!'' does the same but also modifies the parameter object
+
+
+# @@PLEAC@@_10.2
+def somefunc
+ variable = something # variable is local by default
+end
+
+name, age = ARGV
+start = fetch_time
+
+a, b = pair # will succeed if pair is an Array object (like ARGV is)
+c = fetch_time
+
+# In ruby, run_check can't access a, b, or c until they are
+# explicitely defined global (using leading $), even if they are
+# both defined in the same scope
+
+def check_x(x)
+ y = "whatever"
+ run_check
+ if $condition
+ puts "got $x"
+ end
+end
+
+# The following will keep a reference to the array, though the
+# results will be slightly different from perl: the last element
+# of $global_array will be itself an array
+def save_array(ary)
+ $global_array << ary
+end
+
+# The following gives the same results as in Perl for $global_array,
+# though it doesn't illustrate anymore the way to keep a reference
+# to an object: $global_array is extended with the elements of ary
+def save_array(ary)
+ $global_array += ary
+end
+
+
+# @@PLEAC@@_10.3
+# In Ruby, AFAIK a method cannot access "local variables" defined
+# upper scope; mostly because everything is an object, so you'll
+# do the same by defining an attribute or a static attribute
+
+# In Ruby the BEGIN also exists:
+BEGIN { puts "hello from BEGIN" }
+puts "hello from main"
+BEGIN { puts "hello from 2nd BEGIN" }
+# gives:
+# hello from BEGIN
+# hello from 2nd BEGIN
+# hello from main
+
+# In Ruby, it can be written as a static method and a static
+# variable
+class Counter
+ @@counter = 0
+ def Counter.next_counter; @@counter += 1; end
+end
+
+# There is no need of BEGIN since the variable will get
+# initialized when parsing
+class Counter
+ @@counter = 42
+ def Counter.next_counter; @@counter += 1; end
+ def Counter.prev_counter; @@counter -= 1; end
+end
+
+
+# @@PLEAC@@_10.4
+# You can either get the whole trace as an array of strings, each
+# string telling which file, line and method is calling:
+caller
+
+# ...or only the last caller
+caller[0]
+
+# We need to extract just the method name of the backtrace:
+def whoami; caller()[0] =~ /in `([^']+)'/ ? $1 : '(anonymous)'; end
+def whowasi; caller()[1] =~ /in `([^']+)'/ ? $1 : '(anonymous)'; end
+
+
+# @@PLEAC@@_10.5
+# In Ruby, every value is a reference on an object, thus there is
+# no such problem
+array_diff(array1, array2)
+
+def add_vecpair(a1, a2)
+ results = []
+ a1.each_index { |i| results << (a1[i] + a2[i]) }
+ results
+end
+a = [1, 2]
+b = [5, 8]
+c = add_vecpair(a, b)
+p c
+
+# Add this to the beginning of the function to check if we were
+# given two arrays
+a1.type == Array && a2.type == Array or
+ raise "usage: add_vecpair array1 array2 (was used with: #{a1.type} #{a2.type})"
+
+
+# @@PLEAC@@_10.6
+# There is no return context in Ruby
+
+
+# @@PLEAC@@_10.7
+# Like in Perl, we need to fake with a hash, but it's dirty :-(
+def thefunc(param_args)
+ args = { 'INCREMENT' => '10s', 'FINISH' => '0', 'START' => 0 }
+ args.update(param_args)
+ if (args['INCREMENT'] =~ /m$/ )
+ # .....
+ end
+end
+
+thefunc({ 'INCREMENT' => '20s', 'START' => '+5m', 'FINISH' => '+30m' })
+thefunc({})
+
+
+# @@PLEAC@@_10.8
+# there is no "undef" direct equivalent but there is the slice equiv:
+a, c = func.indexes(0, 2)
+
+
+# @@PLEAC@@_10.9
+# Ruby has no such limitation:
+def somefunc
+ ary = []
+ hash = {}
+ # ...
+ return ary, hash
+end
+arr, dict = somefunc
+
+array_of_hashes = fn
+h1, h2, h3 = fn
+
+
+# @@PLEAC@@_10.10
+return
+# or (equivalent)
+return nil
+
+
+# @@PLEAC@@_10.11
+# You can't prototype in Ruby regarding types :-(
+# Though, you can force the number of arguments:
+def func_with_no_arg; end
+def func_with_no_arg(); end
+def func_with_one_arg(a1); end
+def func_with_two_args(a1, a2); end
+def func_with_any_number_of_args(*args); end
+
+
+# @@PLEAC@@_10.12
+raise "some message" # raise exception
+
+begin
+ val = func
+rescue Exception => msg
+ $stderr.puts "func raised an exception: #{msg}"
+end
+
+# In Ruby the rescue statement uses an exception class, every
+# exception which is not matched is still continuing
+begin
+ val = func
+rescue FullMoonError
+ ...
+end
+
+
+# @@PLEAC@@_10.13
+# Saving Global Values
+# Of course we can just save the value and restore it later:
+def print_age
+ puts "Age is #{$age}"
+end
+
+$age = 18 # global variable
+print_age()
+if condition
+ safeage = $age
+ $age = 23
+ print_age()
+ $age = safeage
+end
+
+# We can also use a method that saves the global variable and
+# restores it automatically when the block is left:
+
+def local(var)
+ eval("save = #{var.id2name}")
+ begin
+ result = yield
+ ensure
+ # we want to call this even if we got an exception
+ eval("#{var.id2name} = save")
+ end
+ result
+end
+
+condition = true
+$age = 18
+print_age()
+if condition
+ local(:$age) {
+ $age = 23
+ print_age()
+ }
+end
+print_age()
+
+# There is no need to use local() for filehandles or directory
+# handles in ruby because filehandles are normal objects.
+
+
+# @@PLEAC@@_10.14
+# In Ruby you may redefine a method [but not overload it :-(]
+# just by defining again with the same name.
+def foo; puts 'foo'; end
+def foo; puts 'bar'; end
+foo
+#=> bar
+
+# You can also take a reference to an existing method before
+# redefining a new one, using the `alias' keyword
+def foo; puts 'foo'; end
+alias foo_orig foo
+def foo; puts 'bar'; end
+foo_orig
+foo
+#=> foo
+#=> bar
+
+# AFAIK, there is no direct way to create a new method whose name
+# comes from a variable, so use "eval"
+colors = %w(red blue green yellow orange purple violet)
+colors.each { |c|
+ eval <<-EOS
+ def #{c}(*a)
+ "<FONT COLOR='#{c}'>" + a.to_s + "</FONT>"
+ end
+ EOS
+}
+
+
+# @@PLEAC@@_10.15
+def method_missing(name, *args)
+ "<FONT COLOR='#{name}'>" + args.join(' ') + "</FONT>"
+end
+puts chartreuse("stuff")
+
+
+# @@PLEAC@@_10.16
+def outer(arg)
+ x = arg + 35
+ inner = proc { x * 19 }
+ x + inner.call()
+end
+
+
+# @@PLEAC@@_10.17
+#!/usr/bin/ruby -w
+# mailsort - sort mbox by different criteria
+require 'English'
+require 'Date'
+
+# Objects of class Mail represent a single mail.
+class Mail
+ attr_accessor :no
+ attr_accessor :subject
+ attr_accessor :fulltext
+ attr_accessor :date
+
+ def initialize
+ @fulltext = ""
+ @subject = ""
+ end
+
+ def append(para)
+ @fulltext << para
+ end
+
+ # this is called if you call puts(mail)
+ def to_s
+ @fulltext
+ end
+end
+
+# represents a list of mails.
+class Mailbox < Array
+
+ Subjectpattern = Regexp.new('Subject:\s*(?:Re:\s*)*(.*)\n')
+ Datepattern = Regexp.new('Date:\s*(.*)\n')
+
+ # reads mails from open file and stores them
+ def read(file)
+ $INPUT_RECORD_SEPARATOR = '' # paragraph reads
+ msgno = -1
+ file.each { |para|
+ if para =~ /^From/
+ mail = Mail.new
+ mail.no = (msgno += 1)
+ md = Subjectpattern.match(para)
+ if md
+ mail.subject = md[1]
+ end
+ md = Datepattern.match(para)
+ if md
+ mail.date = DateTime.parse(md[1])
+ else
+ mail.date = DateTime.now
+ end
+ self.push(mail)
+ end
+ mail.append(para) if mail
+ }
+ end
+
+ def sort_by_subject_and_no
+ self.sort_by { |m|
+ [m.subject, m.no]
+ }
+ end
+
+ # sorts by a list of attributs of mail, given as symbols
+ def sort_by_attributs(*attrs)
+ # you can sort an Enumerable by an array of
+ # values, they would be compared
+ # from ary[0] to ary[n]t, say:
+ # ['b',1] > ['a',10] > ['a',9]
+ self.sort_by { |elem|
+ attrs.map { |attr|
+ elem.send(attr)
+ }
+ }
+ end
+
+end
+
+mailbox = Mailbox.new
+mailbox.read(ARGF)
+
+# print only subjects sorted by subject and number
+for m in mailbox.sort_by_subject_and_no
+ puts(m.subject)
+end
+
+# print complete mails sorted by date, then subject, then number
+for m in mailbox.sort_by_attributs(:date, :subject)
+ puts(m)
+end
+
+
+# @@PLEAC@@_11.7
+def mkcounter(count)
+ start = count
+ bundle = {
+ "NEXT" => proc { count += 1 },
+ "PREV" => proc { count -= 1 },
+ "RESET" => proc { count = start }
+ }
+ bundle["LAST"] = bundle["PREV"]
+ return bundle
+end
+
+c1 = mkcounter(20)
+c2 = mkcounter(77)
+
+puts "next c1: #{c1["NEXT"].call}" # 21
+puts "next c2: #{c2["NEXT"].call}" # 78
+puts "next c1: #{c1["NEXT"].call}" # 22
+puts "last c1: #{c1["PREV"].call}" # 21
+puts "last c1: #{c1["LAST"].call}" # 20
+puts "old c2: #{c2["RESET"].call}" # 77
+
+
+# @@PLEAC@@_11.15
+class Binary_tree
+ def initialize(val)
+ @value = val
+ @left = nil
+ @right = nil
+ end
+
+ # insert given value into proper point of
+ # provided tree. If no tree provided,
+ # use implicit pass by reference aspect of @_
+ # to fill one in for our caller.
+ def insert(val)
+ if val < @value then
+ if @left then
+ @left.insert(val)
+ else
+ @left = Binary_tree.new(val)
+ end
+ elsif val > @value then
+ if @right then
+ @right.insert(val)
+ else
+ @right = Binary_tree.new(val)
+ end
+ else
+ puts "double"
+ # do nothing, no double values
+ end
+ end
+
+ # recurse on left child,
+ # then show current value,
+ # then recurse on right child.
+ def in_order
+ @left.in_order if @left
+ print @value, " "
+ @right.in_order if @right
+ end
+
+ # show current value,
+ # then recurse on left child,
+ # then recurse on right child.
+ def pre_order
+ print @value, " "
+ @left.pre_order if @left
+ @right.pre_order if @right
+ end
+
+ # recurse on left child,
+ # then recurse on right child,
+ # then show current value.
+ def post_order
+ @left.post_order if @left
+ @right.post_order if @right
+ print @value, " "
+ end
+
+ # find out whether provided value is in the tree.
+ # if so, return the node at which the value was found.
+ # cut down search time by only looking in the correct
+ # branch, based on current value.
+ def search(val)
+ if val == @value then
+ return self
+ elsif val < @value then
+ return @left.search(val) if @left
+ return nil
+ else
+ return @right.search(val) if @right
+ return nil
+ end
+ end
+end
+
+# first generate 20 random inserts
+test = Binary_tree.new(0)
+for a in 0..20
+ test.insert(rand(1000))
+end
+
+# now dump out the tree all three ways
+print "Pre order: "; test.pre_order; puts ""
+print "In order: "; test.in_order; puts ""
+print "Post order: "; test.post_order; puts ""
+
+print "search?"
+while gets
+ print test.search($_.to_i)
+ print "\nsearch?"
+end
+
+
+# @@PLEAC@@_12.0
+# class and module names need to have the first letter capitalized
+module Alpha
+ NAME = 'first'
+end
+module Omega
+ NAME = 'last'
+end
+puts "Alpha is #{Alpha::NAME}, Omega is #{Omega::NAME}"
+
+# ruby doesn't differentiate beteen compile-time and run-time
+require 'getoptlong.rb'
+require 'getoptlong' # assumes the .rb
+require 'cards/poker.rb'
+require 'cards/poker' # assumes the .rb
+load 'cards/poker' # require only loads the file once
+
+module Cards
+ module Poker
+ @card_deck = Array.new # or @card_deck = []
+ def shuffle
+ end
+ end
+end
+
+
+# @@PLEAC@@_12.1
+# a module exports all of its functions
+module Your_Module
+ def self.function
+ # this would be called as Your_Module.function
+ end
+
+ def Your_Module.another
+ # this is the same as above, but more specific
+ end
+end
+
+# @@PLEAC@@_12.2
+begin
+ require 'nonexistent'
+rescue LoadError
+ puts "Couldn't load #{$!}" # $! contains the last error string
+end
+
+# @@PLEAC@@_12.4
+# module variables are private unless access functions are defined
+module Alpha
+ @aa = 10
+ @bb = 11
+
+ def self.put_aa
+ puts @aa
+ end
+
+ def self.bb=(val)
+ @bb = val
+ end
+end
+
+Alpha.bb = 12
+# Alpha.aa = 10 # error, no aa=method
+
+
+# @@PLEAC@@_12.5
+# caller provides a backtrace of the call stack
+module MyModule
+ def find_caller
+ caller
+ end
+
+ def find_caller2(i)
+ caller(i) # an argument limits the size of the stack returned
+ end
+end
+
+
+# @@PLEAC@@_12.6
+BEGIN {
+ $logfile = '/tmp/mylog' unless defined? $logfile
+ $LF = File.open($logfile, 'a')
+}
+
+module Logger
+ def self.logmsg(msg)
+ $LF.puts msg
+ end
+
+ logmsg('startup')
+end
+
+END {
+ Logger::logmsg('shutdown')
+ $LF.close
+}
+
+
+# @@PLEAC@@_12.7
+#-----------------------------
+# results may be different on your system
+# % ruby -e "$LOAD_PATH.each_index { |i| printf("%d %s\n", i, $LOAD_PATH[i] }
+#0 /usr/local/lib/site_ruby/1.6
+#1 /usr/local/lib/site_ruby/1.6/i386-linux
+#2 /usr/local/lib/site_ruby/
+#3 /usr/lib/ruby/1.6
+#4 /usr/lib/ruby/1.6/i136-linux
+#5 .
+#-----------------------------
+# syntax for sh, bash, ksh, or zsh
+#$ export RUBYLIB=$HOME/rubylib
+
+# syntax for csh or tcsh
+# % setenv RUBYLIB ~/rubylib
+#-----------------------------
+$LOAD_PATH.unshift "/projects/spectre/lib";
+
+
+# @@PLEAC@@_12.8
+# equivalents in ruby are mkmf, SWIG, or Ruby/DL depending on usage
+
+
+# @@PLEAC@@_12.9
+# no equivalent in ruby
+
+
+# @@PLEAC@@_12.10
+# no equivalent in ruby
+
+
+# @@PLEAC@@_12.11
+module FineTime
+ def self.time
+ # to be defined later
+ end
+end
+
+
+module FineTime
+ def self.time
+ "its a fine time"
+ end
+end
+
+puts FineTime.time #=> "its a fine time"
+
+
+# @@PLEAC@@_12.12
+def even_only(n)
+ raise "#{n} is not even" if (n & 1) != 0 # one way to test
+ # ...
+end
+def even_only(n)
+ $stderr.puts "#{n} is not even" if (n & 1) != 0
+ # ...
+end
+
+
+# @@PLEAC@@_12.17
+# The library archive for ruby is called Ruby Application archive,
+# or shorter RAA, and can be found at http://raa.ruby-lang.org.
+# A typical library is installed like this:
+# % gunzip some-module-4.54.tar.gz
+# % tar xf some-module-4.54.tar
+# % cd some-module-4.54.tar
+# % ruby install.rb config
+# % ruby install.rb setup
+# get superuser previleges here if needed for next step
+# % ruby install.rb install
+
+# Some modules use a different process,
+# you should find details in the documentation
+# Here is an example of such a different process
+# % ruby extconf.rb
+# % make
+# % make install
+
+# If you want the module installed in your own directory:
+# For ruby version specific libraries
+# % ruby install.rb config --site-ruby=~/lib
+# For version independent libraries
+# % ruby install.rb config --site-ruby-common=~/lib
+
+# Information about possible options for config
+# % ruby install.rb --help
+
+# If you have your own complete distribution
+# % ruby install.rb --prefix=path=~/ruby-private
+
+
+# @@PLEAC@@_13.0
+# Classes and objects in Ruby are rather straigthforward
+class Person
+ # Class variables (also called static attributes) are prefixed by @@
+ @@person_counter=0
+
+ # object constructor
+ def initialize(age, name, alive = true) # Default arg like in C++
+ @age, @name, @alive = age, name, alive # Object attributes are prefixed by '@'
+ @@person_counter += 1
+ # There is no '++' operator in Ruby. The '++'/'--' operators are in fact
+ # hidden assignments which affect variables, not objects. You cannot accomplish
+ # assignment via method. Since everything in Ruby is object, '++' and '--'
+ # contradict Ruby OO ideology. Instead '-=' and '+=' are used.
+ end
+
+ attr_accessor :name, :age # This creates setter and getter methods for @name
+ # and @age. See 13.3 for detailes.
+
+ # methods modifying the receiver object usually have the '!' suffix
+ def die!
+ @alive = false
+ puts "#{@name} has died at the age of #{@age}."
+ @alive
+ end
+
+ def kill(anotherPerson)
+ print @name, ' is killing ', anotherPerson.name, ".\n"
+ anotherPerson.die!
+ end
+
+ # methods used as queries
+ # usually have the '?' suffix
+ def alive?
+ @alive && true
+ end
+
+ def year_of_birth
+ Time.now.year - @age
+ end
+
+ # Class method (also called static method)
+ def Person.number_of_people
+ @@person_counter
+ end
+end
+
+# Using the class:
+# Create objects of class Person
+lecter = Person.new(47, 'Hannibal')
+starling = Person.new(29, 'Clarice', true)
+pazzi = Person.new(40, 'Rinaldo', true)
+
+# Calling a class method
+print "There are ", Person.number_of_people, " Person objects\n"
+
+print pazzi.name, ' is ', (pazzi.alive?) ? 'alive' : 'dead', ".\n"
+lecter.kill(pazzi)
+print pazzi.name, ' is ', (pazzi.alive?) ? 'alive' : 'dead', ".\n"
+
+print starling.name , ' was born in ', starling.year_of_birth, "\n"
+
+
+# @@PLEAC@@_13.1
+# If you don't need any initialisation in the constructor,
+# you don't need to write a constructor.
+class MyClass
+end
+
+class MyClass
+ def initialize
+ @start = Time.new
+ @age = 0
+ end
+end
+
+class MyClass
+ def initialize(inithash)
+ @start = Time.new
+ @age = 0
+ for key, value in inithash
+ instance_variable_set("@#{key}", value)
+ end
+ end
+end
+
+# @@PLEAC@@_13.2
+# Objects are destroyed by the garbage collector.
+# The time of destroying is not predictable.
+# The ruby garbage collector can handle circular references,
+# so there is no need to write destructor for that.
+
+# There is no direct support for destructor.
+# You can call a custom function, or more specific a proc object, when the
+# garbage collector is about to destruct the object, but it is unpredictable
+# when this occurs.
+# Also if such a finalizer object has a reference to the orignal object,
+# this may prevent the original object to get garbage collected.
+# Because of this problem the finalize method below is
+# a class method and not a instance method.
+# So if you need to free resources for an object, like
+# closing a socket or kill a spawned subprocess,
+# you should do it explicitly.
+
+class MyClass
+ def initialize
+ ObjectSpace.define_finalizer(self,
+ self.class.method(:finalize).to_proc)
+ end
+ def MyClass.finalize(id)
+ puts "Object #{id} dying at #{Time.new}"
+ end
+end
+
+# test code
+3.times {
+ MyClass.new
+}
+ObjectSpace.garbage_collect
+
+
+# @@PLEAC@@_13.3
+# You can write getter and setter methods in a natural way:
+class Person
+ def name
+ @name
+ end
+ def name=(name)
+ @name = name
+ end
+end
+
+# But there is a better and shorter way
+class Person
+ attr_reader :age
+ attr_writer :name
+ # attr_reader and attr_writer are actually methods in class Class
+ # which set getter and setter methods for you.
+end
+
+# There is also attr_accessor to create both setters and getters
+class Person
+ attr_accessor :age, :name
+end
+
+
+# @@PLEAC@@_13.4
+class Person
+ # Class variables (also called static attributes) are prefixed by @@
+ @@person_counter = 0
+
+ def Person.population
+ @@person_counter
+ end
+ def initialize
+ @@person_counter += 1
+ ObjectSpace.define_finalizer(self,
+ self.class.method(:finalize).to_proc)
+ end
+ def Person.finalize(id)
+ @@person_counter -= 1
+ end
+end
+people = []
+10.times {
+ people.push(Person.new)
+}
+printf("There are %d people alive", Person.population)
+
+
+FixedArray.class_max_bounds = 100
+alpha = FixedArray.new
+puts "Bound on alpha is #{alpha.max_bounds}"
+
+beta = FixedArray.new
+beta.max_bounds = 50 # calls the instance method
+beta.class.class_max_bounds = 50 # alternative, calls the class method
+puts "Bound on alpha is #{alpha.max_bounds}"
+
+class FixedArray
+ @@bounds = 7
+
+ def max_bounds
+ @@max_bounds
+ end
+ # instance method, which sets the class variable
+ def max_bounds=(value)
+ @@max_bounds = value
+ end
+ # class method. This can only be called on a class,
+ # but not on the instances
+ def FixedArray.class_max_bounds=(value)
+ @@max_bounds = value
+ end
+end
+
+
+# @@PLEAC@@_13.5
+PersonStruct = Struct.new("Person", :name, :age, :peers)
+# creates a class "Person::Struct", which is accessiable with the
+# constant "PersonStruct"
+p = PersonStruct.new
+p = Struct::Person.new # alternative using the classname
+p.name = "Jason Smythe"
+p.age = 13
+p.peers = ["Wilbur", "Ralph", "Fred"]
+p[:peers] = ["Wilbur", "Ralph", "Fred"] # alternative access using symbol
+p["peers"] = ["Wilbur", "Ralph", "Fred"] # alternative access using name of field
+p[2] = ["Wilbur", "Ralph", "Fred"] # alternative access using index of field
+puts "At age #{p.age}, #{p.name}'s first friend is #{p.peers[0]}"
+
+# The fields of a struct have no special type, like other ruby variables
+# you can put any objects in. Therefore the discussions how to specify
+# the types of the fields do not apply to ruby.
+
+FamilyStruct = Struct.new("Family", :head, :address, :members)
+folks = FamilyStruct.new
+folks.head = PersonStruct.new
+dad = folks.head
+dad.name = "John"
+dad.age = 34
+
+# supply of own accessor method for the struct for error checking
+class PersonStruct
+ def age=(value)
+ if !value.kind_of?(Integer)
+ raise(ArgumentError, "Age #{value} isn't an Integer")
+ elsif value > 150
+ raise(ArgumentError, "Age #{value} is unreasonable")
+ end
+ @age = value
+ end
+end
+
+
+# @@PLEAC@@_13.6
+# The ruby Object class defines a dup and a clone method.
+# The dup method is recommended for prototype object creation.
+# The default implementation makes a shallow copy,
+# but each class can override it, for example to make a deep copy.
+
+# If you want to call 'new' directly on the instances,
+# you can create a instance method "new", which returns a new duplicate.
+# This method is distinct from the class method new.
+#
+class A
+ def new
+ dup
+ end
+end
+
+ob1 = A.new
+# later on
+ob2 = ob1.new
+
+
+# @@PLEAC@@_13.7
+methname = 'flicker'
+obj.send(methname, 10) # calls obj.flicker(10)
+
+# call three methods on the object, by name
+['start', 'run', 'stop'].each do |method_string|
+ obj.send(method_string)
+end
+
+# Another way is to create a Method object
+method_obj = obj.method('flicker')
+# And then call it
+method_obj.call(10)
+
+
+# @@PLEAC@@_13.8
+# All classes in Ruby inherit from class Object
+# and thus all objects share methods defined in this class
+
+# the class of the object
+puts any_object.type
+
+# Ruby classes are actually objects of class Class and they
+# respond to methods defined in Object class as well
+
+# the superclass of this class
+puts any_object.class.superclass
+
+# ask an object whether it is an instance of particular class
+n = 4.7
+puts n.instance_of?(Float) # true
+puts n.instance_of?(Numeric) # false
+
+# ask an object whether it is an instance of class, one of the
+# superclasses of the object, or modules included in it
+puts n.kind_of?(Float) # true (the class)
+puts n.kind_of?(Numeric) # true (an ancestor class)
+puts n.kind_of?(Comparable) # true (a mixin module)
+puts n.kind_of?(String) # false
+
+# ask an object whether it can respond to a particular method
+puts n.respond_to?('+') # true
+puts n.respond_to?('length') # false
+
+# all methods an object can respond to
+'just a string'.methods.each { |m| puts m }
+
+
+# @@PLEAC@@_13.9
+# Actually any class in Ruby is inheritable
+class Person
+ attr_accessor :age, :name
+ def initialize
+ @name
+ @age
+ end
+end
+#-----------------------------
+dude = Person.new
+dude.name = 'Jason'
+dude.age = 23
+printf "%s is age %d.\n", dude.name, dude.age
+#-----------------------------
+# Inheriting from Person
+class Employee < Person
+ attr_accessor :salary
+end
+#-----------------------------
+empl = Employee.new
+empl.name = 'Jason'
+empl.age = 23
+empl.salary = 200
+printf "%s is age %d, the salary is %d.\n", empl.name, empl.age, empl.salary
+#-----------------------------
+# Any built-in class can be inherited the same way
+class WeirdString < String
+ def initialize(obj)
+ super obj
+ end
+ def +(anotherObj) # + method in this class is overridden
+ # to return the sum of string lengths
+ self.length + anotherObj.length # 'self' can be omitted
+ end
+end
+#-----------------------------
+a = WeirdString.new('hello')
+b = WeirdString.new('bye')
+
+puts a + b # the overridden +
+#=> 8
+puts a.length # method from the superclass, String
+#=> 5
+
+
+# @@PLEAC@@_13.11
+# In ruby you can override the method_missing method
+# to have a solution similar to perls AUTOLOAD.
+class Person
+
+ def initialize
+ @ok_fields = %w(name age peers parent)
+ end
+
+ def valid_attribute?(name)
+ @ok_fields.include?(name)
+ end
+
+ def method_missing(namesymbol, *params)
+ name = namesymbol.to_s
+ return if name =~ /^A-Z/
+ if name.to_s[-1] == ('='[0]) # we have a setter
+ isSetter = true
+ name.sub!(/=$/, '')
+ end
+ if valid_attribute?(name)
+ if isSetter
+ instance_variable_set("@#{name}", *params)
+ else
+ instance_variable_get("@#{name}", *params)
+ end
+ else
+ # if no annestor is responsible,
+ # the Object class will throw a NoMethodError exception
+ super(namesymbol, *params)
+ end
+ end
+
+ def new
+ kid = Person.new
+ kid.parent = self
+ kid
+ end
+
+end
+
+dad = Person.new
+dad.name = "Jason"
+dad.age = 23
+kid = dad.new
+kid.name = "Rachel"
+kid.age = 2
+puts "Kid's parent is #{kid.parent.name}"
+puts dad
+puts kid
+
+class Employee < Person
+ def initialize
+ super
+ @ok_fields.push("salary", "boss")
+ end
+ def ok_fields
+ @ok_fields
+ end
+end
+
+
+# @@PLEAC@@_13.13
+# The ruby garbage collector pretends to cope with circular structures.
+# You can test it with this code:
+class RingNode
+ attr_accessor :next
+ attr_accessor :prev
+ attr_reader :name
+
+ def initialize(aName)
+ @name = aName
+ ObjectSpace.define_finalizer(self,
+ self.class.method(:finalize).to_proc)
+ end
+
+ def RingNode.finalize(id)
+ puts "Node #{id} dying"
+ end
+
+ def RingNode.show_all_objects
+ ObjectSpace.each_object {|id|
+ puts id.name if id.class == RingNode
+ }
+ end
+end
+
+def create_test
+ a = RingNode.new("Node A")
+ b = RingNode.new("Node B")
+ c = RingNode.new("Node C")
+ a.next = b
+ b.next = c
+ c.next = a
+ a.prev = c
+ c.prev = b
+ b.prev = a
+
+ a = nil
+ b = nil
+ c = nil
+end
+
+create_test
+RingNode.show_all_objects
+ObjectSpace.garbage_collect
+puts "After garbage collection"
+RingNode.show_all_objects
+
+
+# @@PLEAC@@_13.14
+class String
+ def <=>(other)
+ self.casecmp other
+ end
+end
+
+# There is no way to directly overload the '""' (stringify)
+# operator in Ruby. However, by convention, classes which
+# can reasonably be converted to a String will define a
+# 'to_s' method as in the TimeNumber class defined below.
+# The 'puts' method will automatcally call an object's
+# 'to_s' method as is demonstrated below.
+# Furthermore, if a class defines a to_str method, an object of that
+# class can be used most any place where the interpreter is looking
+# for a String value.
+
+#---------------------------------------
+# NOTE: Ruby has a builtin Time class which would usually be used
+# to manipulate time objects, the following is supplied for
+# educational purposes to demonstrate operator overloading.
+#
+class TimeNumber
+ attr_accessor :hours,:minutes,:seconds
+ def initialize( hours, minutes, seconds)
+ @hours = hours
+ @minutes = minutes
+ @seconds = seconds
+ end
+
+ def to_s
+ return sprintf( "%d:%02d:%02d", @hours, @minutes, @seconds)
+ end
+
+ def to_str
+ to_s
+ end
+
+ def +( other)
+ seconds = @seconds + other.seconds
+ minutes = @minutes + other.minutes
+ hours = @hours + other.hours
+ if seconds >= 60
+ seconds %= 60
+ minutes += 1
+ end
+ if minutes >= 60
+ minutes %= 60
+ hours += 1
+ end
+ return TimeNumber.new(hours, minutes, seconds)
+ end
+
+ def -(other)
+ raise NotImplementedError
+ end
+
+ def *(other)
+ raise NotImplementedError
+ end
+
+ def /( other)
+ raise NotImplementedError
+ end
+end
+
+t1 = TimeNumber.new(0, 58, 59)
+sec = TimeNumber.new(0, 0, 1)
+min = TimeNumber.new(0, 1, 0)
+puts t1 + sec + min + min
+
+#-----------------------------
+# StrNum class example: Ruby's builtin String class already has the
+# capabilities outlined in StrNum Perl example, however the '*' operator
+# on Ruby's String class acts differently: It creates a string which
+# is the original string repeated N times.
+#
+# Using Ruby's String class as is in this example:
+x = "Red"; y = "Black"
+z = x+y
+r = z*3 # r is "RedBlackRedBlackRedBlack"
+puts "values are #{x}, #{y}, #{z}, and #{r}"
+print "#{x} is ", x < y ? "LT" : "GE", " #{y}\n"
+# prints:
+# values are Red, Black, RedBlack, and RedBlackRedBlackRedBlack
+# Red is GE Black
+
+#-----------------------------
+class FixNum
+ REGEX = /(\.\d*)/
+ DEFAULT_PLACES = 0
+ attr_accessor :value, :places
+ def initialize(value, places = nil)
+ @value = value
+ if places
+ @places = places
+ else
+ m = REGEX.match(value.to_s)
+ if m
+ @places = m[0].length - 1
+ else
+ @places = DEFAULT_PLACES
+ end
+ end
+ end
+
+ def +(other)
+ FixNum.new(@value + other.value, max(@places, other.places))
+ end
+
+ def *(other)
+ FixNum.new(@value * other.value, max(@places, other.places))
+ end
+
+ def /(other)
+ puts "Divide: #{@value.to_f/other.value.to_f}"
+ result = FixNum.new(@value.to_f/other.value.to_f)
+ result.places = max(result.places,other.places)
+ result
+ end
+
+ def to_s
+ sprintf("STR%s: %.*f", self.class.to_s , @places, @value) #.
+ end
+
+ def to_str
+ to_s
+ end
+
+ def to_i #convert to int
+ @value.to_i
+ end
+
+ def to_f #convert to float`
+ @value.to_f
+ end
+
+ private
+ def max(a,b)
+ a > b ? a : b
+ end
+end
+
+def demo()
+ x = FixNum.new(40)
+ y = FixNum.new(12, 0)
+
+ puts "sum of #{x} and #{y} is #{x+y}"
+ puts "product of #{x} and #{y} is #{x*y}"
+
+ z = x/y
+ puts "#{z} has #{z.places} places"
+ unless z.places
+ z.places = 2
+ end
+
+ puts "div of #{x} by #{y} is #{z}"
+ puts "square of that is #{z*z}"
+end
+
+if __FILE__ == $0
+ demo()
+end
+
+
+# @@PLEAC@@_14.1
+# There are dbm, sdbm, gdbm modules
+# and the bdb module for accessing the berkeley db
+# sdbm seem to be available on the most systems,
+# so we use it here
+#
+require "sdbm"
+SDBM.open("filename", 0666) { |dbobj|
+ # raises exception if open error
+
+ # the returned sdbm-dbobj has most of the methods of a hash
+ v = dbobj["key"]
+ dbobj["key"] = "newvalue"
+ if dbobj.has_key?("key")
+ # ...
+ end
+ dbobj.delete("key2")
+}
+# database is open only inside the block.
+
+# It is also possible to use a open .. close pair:
+dbobj = SDBM.open("filename", 0666)
+#.. do something with dbobj
+dbobj.close
+
+#!/usr/bin/ruby -w
+# userstats - generate statistics on who is logged in
+# call with usernames as argument to display the totals
+# for the given usernames, call with "ALL" to display all users
+
+require "sdbm"
+filename = '/tmp/userstats.db'
+SDBM.open(filename, 0666) { |dbobj|
+ if ARGV.length > 0
+ if ARGV[0] == "ALL"
+ # ARGV is constant, so we need the variable userlist
+ userlist = dbobj.keys().sort()
+ else
+ userlist = ARGV
+ end
+ userlist.each { |user|
+ print "#{user}\t#{dbobj[user]}\n"
+ }
+ else
+ who = `who`
+ who.split("\n").each { |line|
+ md = /^(\S+)/.match(line)
+ raise "Bad line from who: #{line}" unless md
+ # sdbm stores only strings, so "+=" doesn't work,
+ # we need to convert them expicitly back to integer.
+ if dbobj.has_key?(md[0])
+ dbobj[md[0]] = dbobj[md[0]].to_i + 1
+ else
+ dbobj[md[0]] = "1"
+ end
+ }
+ end
+}
+
+
+# @@PLEAC@@_14.2
+# using open and clear
+dbobj = SDBM.open("filename", 0666)
+dbobj.clear()
+dbobj.close()
+# deleting file and recreating it
+# the filenames depend on the flavor of dbm you use,
+# for example sdbm has two files named filename.pag and filename.dir,
+# so you need to delete both files
+begin
+ File.delete("filename")
+ # raises Exception if not exist
+ dbobj = SDBM.open("filename", 0666)
+rescue
+ # add error handling here
+end
+
+
+# @@PLEAC@@_14.3
+# sdbm2gdbm: converts sdbm database to a gdbm database
+require "sdbm"
+require "gdbm"
+
+unless ARGV.length == 2
+ fail "usage: sdbm2gdbm infile outfile"
+end
+infile = ARGV[0]
+outfile = ARGV[1]
+
+sdb = SDBM.open(infile)
+gdb = GDBM.open(outfile, 0666)
+sdb.each { |key, val|
+ gdb[key] = val
+}
+gdb.close
+sdb.close
+
+
+# @@PLEAC@@_14.4
+#!/usr/bin/ruby -w
+# dbmmerge: merges two dbm databases
+require "sdbm"
+
+unless ARGV.length == 3
+ fail "usage: dbmmerge indb1 indb2 outdb"
+end
+infile1 = ARGV[0]
+infile2 = ARGV[0]
+outfile = ARGV[2]
+
+in1 = SDBM.open(infile1, nil)
+in2 = SDBM.open(infile2, nil)
+outdb = SDBM.open(outfile, 0666)
+
+[in1, in2].each { |indb|
+ indb.each { |key, val|
+ if outdb.has_key?(key)
+ # decide which value to set.
+ # set outdb[key] if necessary
+ else
+ outdb[key] = val
+ end
+ }
+}
+in1.close
+in2.close
+outdb.close
+
+
+# @@PLEAC@@_14.7
+# we write a tie method that extends the Array class.
+# It reads the file into the memory, executes the code block
+# in which you can manipulate the array as needed, and writes
+# the array back to the file after the end of the block execution
+class Array
+ def tie(filename, flags)
+ File.open(filename, flags) { |f|
+ f.each_line { |line|
+ self.push(line.chomp)
+ }
+ yield
+ f.rewind
+ each { |line|
+ if line
+ f.puts(line)
+ else
+ f.puts ""
+ end
+ }
+ }
+ end
+end
+
+array = Array.new
+array.tie("/tmp/textfile.txt", File::RDWR|File::CREAT) {
+ array[4] = "a new line 4"
+}
+
+# The tied array can be manipulated like a normal array,
+# so there is no need for a special API, and the recno_demo program
+# to demonstrate is API is useless
+
+
+# tied array demo: show how to use array with a tied file
+filename = "db_file.txt"
+lines = Array.new
+File.unlink(filename) if File.exists?(filename)
+lines.tie(filename, File::RDWR | File::CREAT) {
+ # first create a textfile to play with
+ lines[0] = "zero"
+ lines[1] = "one"
+ lines[2] = "two"
+ lines[3] = "three"
+ lines[4] = "four"
+
+ # print the records in order.
+ # Opposed to perl, the tied array behaves exactly as a normal array
+ puts "\nOriginal"
+ for i in 0..(lines.length-1)
+ puts "#{i}: #{lines[i]}"
+ end
+
+ #use push and pop
+ a = lines.pop
+ lines.push("last")
+ puts("The last line was [#{a}]")
+
+ #use shift and unshift
+ a = lines.shift
+ lines.unshift("first")
+ puts("The first line was [#{a}]")
+
+ # add record after record 2
+ i = 2
+ lines.insert(i + 1, "Newbie")
+
+ # add record before record one
+ i = 1
+ lines.insert(i, "New One")
+
+ # delete record 3
+ lines.delete_at(3)
+
+ #now print the records in reverse order
+ puts "\nReverse"
+ (lines.length - 1).downto(0){ |i|
+ puts "#{i}: #{lines[i]}"
+ }
+
+}
+
+
+# @@PLEAC@@_14.8
+# example to store complex data in a database
+# uses marshall from the standard library
+require "sdbm"
+db = SDBM.open("pleac14-8-database", 0666)
+
+# convert the Objects into strings and back by using the Marshal module.
+# Most normal objects can be converted out of the box,
+# but not special things like procedure objects,
+# IO instance variables, singleton objects
+
+db["Tom Christiansen"] = Marshal.dump(["book author", "tchrist@perl.com"])
+db["Tom Boutell"] = Marshal.dump(["shareware author",
+"boutell@boutell.com"])
+
+name1 = "Tom Christiansen"
+name2 = "Tom Boutell"
+
+tom1 = Marshal.load(db[name1])
+tom2 = Marshal.load(db[name2])
+
+puts "Two Toming: #{tom1} #{tom2}"
+
+if tom1[0] == tom2[0] && tom1[1] == tom2[1]
+ puts "You're having runtime fun with one Tom made two."
+else
+ puts "No two Toms are ever alike"
+end
+
+# To change parts of an entry, get the whole entry, change the parts,
+# and save the whole entry back
+entry = Marshal.load(db["Tom Boutell"])
+entry[0] = "Poet Programmer"
+db["Tom Boutell"] = Marshal.dump(entry)
+db.close
+
+
+# @@PLEAC@@_14.9
+# example to make data persistent
+# uses Marshal from the standard lib
+# Stores the data in a simple file,
+# see 14.8 on how to store it in a dbm file
+
+# The BEGIN block is executed before the rest of the script
+# we use global variables here because local variables
+# will go out of scope and are not accessible from the main script
+
+BEGIN {
+ $persistent_store = "persitence.dat"
+ begin
+ File.open($persistent_store) do |f|
+ $stringvariable1 = Marshal.load(f)
+ $arrayvariable2 = Marshal.load(f)
+ end
+ rescue
+ puts "Can not open #{$persistent_store}"
+ # Initialisation if this script runs the first time
+ $stringvariable1 = ""
+ $arrayvariable2 = []
+ end
+}
+
+END {
+ File.open($persistent_store, "w+") do |f|
+ Marshal.dump($stringvariable1, f)
+ Marshal.dump($arrayvariable2, f)
+ end
+}
+
+# simple test program
+puts $stringvariable1
+puts $arrayvariable2
+$stringvariable1 = "Hello World"
+$arrayvariable2.push(5)
+puts $stringvariable1
+puts $arrayvariable2
+
+
+# @@PLEAC@@_14.10
+#!/usr/bin/ruby -w
+# Ruby has a dbi module with an architecture similar
+# to the Perl dbi module: the dbi module provides an unified
+# interface and uses specialized drivers for each dbms vendor
+#
+begin
+ DBI.connect("DBI:driver:driverspecific", "username", "auth") {
+ |dbh|
+
+ dbh.do(SQL1)
+
+ dbh.prepare(SQL2){ |sth|
+ sth.execute
+ sth.fetch {|row|
+ # ...
+ }
+ } # end of block finishes the statement handle
+ } # end of block closes the database connection
+rescue DBI::DatabaseError => e
+ puts "dbi error occurred"
+ puts "Error code: #{e.err}"
+ puts "Error message: #{e.errstr}"
+end
+
+#!/usr/bin/ruby -w
+# dbusers - example for mysql which creates a table,
+# fills it with values, retrieves the values back,
+# and finally destroys the table.
+
+require "dbi"
+
+# replacement for the User::pwnt module
+def getpwent
+ result = []
+ File.open("/etc/passwd") {|file|
+ file.each_line {|line|
+ next if line.match(/^#/)
+ cols = line.split(":")
+ result.push([cols[2], cols[0]])
+ }
+ }
+ result
+end
+
+begin
+ DBI.connect("DBI:Mysql:pleacdatabase", "pleac", "pleacpassword") {
+ |conn|
+
+ conn.do("CREATE TABLE users (uid INT, login CHAR(8))")
+
+ users = getpwent
+
+ conn.prepare("INSERT INTO users VALUES (?,?)") {|sth|
+ users.each {|entry|
+ sth.execute(entry[0], entry[1])
+ }
+ }
+
+ conn.execute("SELECT uid, login FROM users WHERE uid < 50") {|sth|
+ sth.fetch {|row|
+ puts row.collect {|col|
+ if col.nil?
+ "(null)"
+ else
+ col
+ end
+ }.join(", ")
+ }
+ }
+
+ conn.do("DROP TABLE users")
+ }
+rescue DBI::DatabaseError => e
+ puts "dbi error occurred"
+ puts "Error code: #{e.err}"
+ puts "Error message: #{e.errstr}"
+end
+
+
+# @@PLEAC@@_15.1
+# This test program demonstrates parsing program arguments.
+# It uses the optparse library, which is included with ruby 1.8
+# It handles classic unix style and gnu style options
+require 'optparse'
+
+@debugmode = false
+@verbose = false
+
+ARGV.options do |opts|
+ opts.banner = "Usage: ruby #{$0} [OPTIONS] INPUTFILES"
+
+ opts.on("-h", "--help", "show this message") {
+ puts opts
+ exit
+ }
+ # The OptionParser#on method is called with a specification of short
+ # options, of long options, a data type spezification and user help
+ # messages for this option.
+ # The method analyses the given parameter and decides what it is,
+ # so you can leave out the long option if you don't need it
+ opts.on("-v", "--[no-]verbose=[FLAG]", TrueClass, "run verbosly") {
+ |@verbose| # sets @verbose to true or false
+ }
+ opts.on("-D", "--DEBUG", TrueClass, "turns on debug mode" ){
+ |@debugmode| # sets @debugmode to true
+ }
+ opts.on("-c", "--count=NUMBER", Integer, "how many times we do it" ){
+ |@count| # sets @count to given integer
+ }
+ opts.on("-o", "--output=FILE", String, "file to write output to"){
+ |@outputfile| # sets @outputfile to given string
+ }
+ opts.parse!
+end
+
+# example to use the options in the main program
+puts "Verbose is on" if @verbose
+puts "Debugmode is on" if @debugmode
+puts "Outfile is #{@outputfile}" if defined? @outputfile
+puts "Count is #{@count}" if defined? @count
+ARGV.each { |param|
+ puts "Got parameter #{param}"
+}
+
+
+# @@PLEAC@@_15.4
+buf = "\0" * 8
+$stdout.ioctl(0x5413, buf)
+ws_row, ws_col, ws_xpixel, ws_ypixel = buf.unpack("S4")
+
+raise "You must have at least 20 characters" unless ws_col >= 20
+max = 0
+values = (1..5).collect { rand(20) } # generate an array[5] of rand values
+for i in values
+ max = i if max < i
+end
+ratio = Float(ws_col-12)/max # chars per unit
+for i in values
+ printf "%8.1f %s\n", i, "*" * (ratio*i)
+end
+
+# gives, for example:
+# 15.0 *******************************
+# 10.0 *********************
+# 5.0 **********
+# 14.0 *****************************
+# 18.0 **************************************
+
+
+# @@PLEAC@@_16.1
+output = `program args` # collect output into one multiline string
+output = `program args`.split # collect output into array, one line per
+element
+
+readme = IO.popen("ls")
+output = ""
+while readme.gets do
+ output += $_
+end
+readme.close
+
+`fsck -y /dev/rsd1a` # BAD AND SCARY in Perl because it's managed by the shell
+ # I donna in Ruby ...
+
+# so the "clean and secure" version
+readme, writeme = IO.pipe
+pid = fork {
+ # child
+ $stdout = writeme
+ readme.close
+ exec('find', '..')
+}
+# parent
+Process.waitpid(pid, 0)
+writeme.close
+while readme.gets do
+ # do something with $_
+end
+
+
+# @@PLEAC@@_16.2
+status = system("xemacs #{myfile}")
+
+status = system("xemacs", myfile)
+
+system("cmd1 args | cmd2 | cmd3 >outfile")
+system("cmd args <infile >outfile 2>errfile")
+
+# stop if the command fails
+raise "$program exited funny: #{$?}" unless system("cmd", "args1", "args2")
+
+# get the value of the signal sent to the child
+# even if it is a SIGINT or SIGQUIT
+system(arglist)
+raise "program killed by signal #{$?}" if ($? & 127) != 0
+
+pid = fork {
+ trap("SIGINT", "IGNORE")
+ exec("sleep", "10")
+}
+trap ("SIGINT") {
+ puts "Tsk tsk, no process interruptus"
+}
+Process.waitpid(pid, 0)
+
+# Ruby doesn't permit to lie to the program called by a 'system'.
+# (ie specify what return argv[0] in C, $0 in Perl/Ruby ...)
+# A (dirty) way is to create a link (under Unix), run this link and
+# erase it. Somebody has a best idea ?
+
+
+# @@PLEAC@@_16.3
+exec("archive *.data")
+
+exec("archive", "accounting.data")
+
+exec("archive accounting.data")
+
+
+# @@PLEAC@@_16.4
+# read the output of a program
+IO.popen("ls") {|readme|
+ while readme.gets do
+ # ...
+ end
+}
+# or
+readme = IO.popen("ls")
+while readme.gets do
+ # ...
+end
+readme.close
+
+# "write" in a program
+IO.popen("cmd args","w") {|pipe|
+ pipe.puts("data")
+ pipe.puts("foo")
+}
+
+# close wait for the end of the process
+read = IO.popen("sleep 10000") # child goes to sleep
+read.close # and the parent goes to lala land
+
+writeme = IO.popen("cmd args", "w")
+writeme.puts "hello" # program will get hello\n on STDIN
+writeme.close # program will get EOF on STDIN
+
+# send in a pager (eg less) all output
+$stdout = IO.popen("/usr/bin/less","w")
+print "huge string\n" * 10000
+
+
+# @@PLEAC@@_16.5
+#-----------------------------
+def head(lines = 20)
+ pid = open("|-","w")
+ if pid == nil
+ return
+ else
+ while gets() do
+ pid.print
+ lines -= 1
+ break if lines == 0
+ end
+ end
+ exit
+end
+
+head(100)
+while gets() do
+ print
+end
+#-----------------------------
+1: > Welcome to Linux, version 2.0.33 on a i686
+
+2: >
+
+3: > "The software required `Windows 95 or better',
+
+4: > so I installed Linux."
+#-----------------------------
+> 1: Welcome to Linux, Kernel version 2.0.33 on a i686
+
+> 2:
+
+> 3: "The software required `Windows 95 or better',
+
+> 4: so I installed Linux."
+#-----------------------------
+#!/usr/bin/ruby
+# qnumcat - demo additive output filters
+
+def number()
+ pid = open("|-","w")
+ if pid == nil
+ return
+ else
+ while gets() do pid.printf("%d: %s", $., $_); end
+ end
+ exit
+end
+
+def quote()
+ pid = open("|-","w")
+ if pid == nil
+ return
+ else
+ while gets() do pid.print "> #{$_}" end
+ end
+ exit
+end
+
+number()
+quote()
+
+while gets() do
+ print
+end
+$stdout.close
+exit
+
+
+# @@PLEAC@@_16.6
+ARGV.map! { |arg|
+ arg =~ /\.(gz|Z)$/ ? "|gzip -dc #{arg}" : arg
+}
+for file in ARGV
+ fh = open(file)
+ while fh.gets() do
+ # .......
+ end
+end
+#-----------------------------
+ARGV.map! { |arg|
+ arg =~ %r#^\w+://# ? "|GET #{arg}" : arg #
+}
+for file in ARGV
+ fh = open(file)
+ while fh.gets() do
+ # .......
+ end
+end
+#-----------------------------
+pwdinfo = (`domainname` =~ /^(\(none\))?$/) ? '/etc/passwd' : '|ypcat passwd';
+pwd = open(pwdinfo);
+#-----------------------------
+puts "File, please? ";
+file = gets().chomp();
+fh = open(file);
+
+
+# @@PLEAC@@_16.7
+output = `cmd 2>&1` # with backticks
+# or
+ph = open("|cmd 2>&1") # with an open pipe
+while ph.gets() { } # plus a read
+#-----------------------------
+output = `cmd 2>/dev/null` # with backticks
+# or
+ph = open("|cmd 2>/dev/null") # with an open pipe
+while ph.gets() { } # plus a read
+#-----------------------------
+output = `cmd 2>&1 1>/dev/null` # with backticks
+# or
+ph = open("|cmd 2>&1 1>/dev/null") # with an open pipe
+while ph.gets() { } # plus a read
+#-----------------------------
+output = `cmd 3>&1 1>&2 2>&3 3>&-` # with backticks
+# or
+ph = open("|cmd 3>&1 1>&2 2>&3 3>&-") # with an open pipe
+while ph.gets() { } # plus a read
+#-----------------------------
+system("program args 1>/tmp/program.stdout 2>/tmp/program.stderr")
+#-----------------------------
+output = `cmd 3>&1 1>&2 2>&3 3>&-`
+#-----------------------------
+fd3 = fd1
+fd1 = fd2
+fd2 = fd3
+fd3 = undef
+#-----------------------------
+system("prog args 1>tmpfile 2>&1")
+system("prog args 2>&1 1>tmpfile")
+#-----------------------------
+# system ("prog args 1>tmpfile 2>&1")
+fd1 = "tmpfile" # change stdout destination first
+fd2 = fd1 # now point stderr there, too
+#-----------------------------
+# system("prog args 2>&1 1>tmpfile")
+fd2 = fd1 # stderr same destination as stdout
+fd1 = "tmpfile" # but change stdout destination
+#-----------------------------
+# It is often better not to rely on the shell,
+# because of portability, possible security problems
+# and bigger resource usage. So, it is often better to use the open3 library.
+# See below for an example.
+# opening stdin, stdout, stderr
+require "open3"
+stdin, stdout, stderr = Open3.popen('cmd')
+
+
+# @@PLEAC@@_16.8
+#-----------------------------
+# Contrary to perl, we don't need to use a module in Ruby
+fh = Kernel.open("|" + program, "w+")
+fh.puts "here's your input\n"
+output = fh.gets()
+fh.close()
+#-----------------------------
+Kernel.open("|program"),"w+") # RIGHT !
+#-----------------------------
+# Ruby has already object methods for I/O handles
+#-----------------------------
+begin
+ fh = Kernel.open("|" + program_and_options, "w+")
+rescue
+ if ($@ ~= /^open/)
+ $stderr.puts "open failed : #{$!} \n #{$@} \n"
+ break
+ end
+ raise # reraise unforseen exception
+end
+
+
+# @@PLEAC@@_16.13
+#% kill -l
+#HUP INT QUIT ILL TRAP ABRT BUS FPE KILL USR1 SEGV USR2 PIPE
+#ALRM TERM CHLD CONT STOP TSTP TTIN TTOU URG XCPU XFSZ VTALRM
+#PROF WINCH POLL PWR
+#-----------------------------
+#% ruby -e 'puts Signal.list.keys.join(" ")'
+#PWR USR1 BUS USR2 TERM SEGV KILL POLL STOP SYS TRAP IOT HUP INT #
+#WINCH XCPU TTIN CLD TSTP FPE IO TTOU PROF CHLD CONT PIPE ABRT
+#VTALRM QUIT ILL XFSZ URG ALRM
+#-----------------------------
+# After that, the perl script create an hash equivalent to Signal.list,
+# and an array. The array can be obtained by :
+signame = []
+Signal.list.each { |name, i| signame[i] = name }
+
+
+# @@PLEAC@@_16.14
+Process.kill(9, pid) # send $pid a signal 9
+Process.kill(-1, Process.getpgrp()) # send whole job a signal 1
+Process.kill("USR1", $$) # send myself a SIGUSR1
+Process.kill("HUP", pid1, pid2, pid3) # send a SIGHUP to processes in @pids
+#-----------------------------
+begin
+ Process.kill(0, minion)
+ puts "#{minion} is alive!"
+rescue Errno::EPERM # changed uid
+ puts "#{minion} has escaped my control!";
+rescue Errno::ESRCH
+ puts "#{minion} is deceased."; # or zombied
+rescue
+ puts "Odd; I couldn't check the status of #{minion} : #{$!}"
+end
+
+
+# @@PLEAC@@_16.15
+Kernel.trap("QUIT", got_sig_quit) # got_sig_quit = Proc.new { puts "Quit\n" }
+trap("PIPE", "got_sig_quit") # def got_sig_pipe ...
+trap("INT") { ouch++ } # increment ouch for every SIGINT
+#-----------------------------
+trap("INT", "IGNORE") # ignore the signal INT
+#-----------------------------
+trap("STOP", "DEFAULT") # restore default STOP signal handling
+
+
+# @@PLEAC@@_16.16
+# the signal handler
+def ding
+ trap("INT", "ding")
+ puts "\aEnter your name!"
+end
+
+# prompt for name, overriding SIGINT
+def get_name
+ save = trap("INT", "ding")
+
+ puts "Kindly Stranger, please enter your name: "
+ name = gets().chomp()
+ trap("INT", save)
+ name
+end
+
+
+# @@PLEAC@@_16.21
+# implemented thanks to http://blade.nagaokaut.ac.jp/cgi-bin/scat.rb/ruby/ruby-talk/1760
+require 'timeout'
+
+# we'll do something vastly more useful than cookbook to demonstrate timeouts
+begin
+ timeout(5) {
+ waitsec = rand(10)
+ puts "Let's see if a sleep of #{waitsec} seconds is longer than 5 seconds..."
+ system("sleep #{waitsec}")
+ }
+ puts "Timeout didn't occur"
+rescue Timeout::Error
+ puts "Timed out!"
+end
+
+
+# @@PLEAC@@_17.1
+# A basic TCP client connection
+require 'socket'
+begin
+ t = TCPSocket.new('www.ruby-lang.org', 'www')
+rescue
+ puts "error: #{$!}"
+else
+ # ... do something with the socket
+ t.print "GET / HTTP/1.0\n\n"
+ answer = t.gets(nil)
+ # and terminate the connection when we're done
+ t.close
+end
+
+# Using the evil low level socket API
+require 'socket'
+# create a socket
+s = Socket.new(Socket::AF_INET, Socket::SOCK_STREAM, 0)
+# build the address of the remote machine
+sockaddr_server = [Socket::AF_INET, 80,
+ Socket.gethostbyname('www.ruby-lang.org')[3],
+ 0, 0].pack("snA4NN")
+# connect
+begin
+ s.connect(sockaddr_server)
+rescue
+ puts "error: #{$!}"
+else
+ # ... do something with the socket
+ s.print "GET / HTTP/1.0\n\n"
+ # and terminate the connection when we're done
+ s.close
+end
+
+# TCP connection with management of error (DNS)
+require 'socket'
+begin
+ client = TCPSocket.new('does not exists', 'www')
+rescue
+ puts "error: #{$!}"
+end
+
+# TCP connection with a time out
+require 'socket'
+require 'timeout'
+begin
+ timeout(1) do #the server has one second to answer
+ client = TCPSocket.new('www.host.com', 'www')
+ end
+rescue
+ puts "error: #{$!}"
+end
+
+
+# @@PLEAC@@_17.12
+require 'socket'
+
+class Preforker
+ attr_reader (:child_count)
+
+ def initialize(prefork, max_clients_per_child, port, client_handler)
+ @prefork = prefork
+ @max_clients_per_child = max_clients_per_child
+ @port = port
+ @child_count = 0
+
+ @reaper = proc {
+ trap('CHLD', @reaper)
+ pid = Process.wait
+ @child_count -= 1
+ }
+
+ @huntsman = proc {
+ trap('CHLD', 'IGNORE')
+ trap('INT', 'IGNORE')
+ Process.kill('INT', 0)
+ exit
+ }
+
+ @client_handler=client_handler
+ end
+
+ def child_handler
+ trap('INT', 'EXIT')
+ @client_handler.setUp
+ # wish: sigprocmask UNblock SIGINT
+ @max_clients_per_child.times {
+ client = @server.accept or break
+ @client_handler.handle_request(client)
+ client.close
+ }
+ @client_handler.tearDown
+ end
+
+ def make_new_child
+ # wish: sigprocmask block SIGINT
+ @child_count += 1
+ pid = fork do
+ child_handler
+ end
+ # wish: sigprocmask UNblock SIGINT
+ end
+
+ def run
+ @server = TCPserver.open(@port)
+ trap('CHLD', @reaper)
+ trap('INT', @huntsman)
+ loop {
+ (@prefork - @child_count).times { |i|
+ make_new_child
+ }
+ sleep .1
+ }
+ end
+end
+
+#-----------------------------
+#!/usr/bin/ruby
+
+require 'Preforker'
+
+class ClientHandler
+ def setUp
+ end
+
+ def tearDown
+ end
+
+ def handle_request(client)
+ # do stuff
+ end
+end
+
+server = Preforker.new(1, 100, 3102, ClientHandler.new)
+server.run
+
+
+# @@PLEAC@@_18.2
+require 'net/ftp'
+
+begin
+ ftp = Net::FTP::new("ftp.host.com")
+ ftp.login(username,password)
+ ftp.chdir(directory)
+ ftp.get(filename)
+ ftp.put(filename)
+rescue Net::FTPError
+ $stderr.print "FTP failed: " + $!
+ensure
+ ftp.close() if ftp
+end
+
+# A better solution for a local use could be :
+Net::FTP::new("ftp.host.com") do |ftp|
+ ftp.login(username,password)
+ ftp.chdir(directory)
+ ftp.get(filename)
+ ftp.put(filename)
+end
+
+# If you have only one file to get, there is a simple solution :
+require 'open-uri'
+open("ftp://www.ruby-lang.org/path/filename") do |fh|
+ # read from filehandle fh
+end
+#--------------------------------------------
+# to wait a defined time for the connection,
+# use the timeout module
+require 'timeout'
+begin
+ timeout(30){
+ ftp = Net::FTP::new("ftp.host.com")
+ ftp.debug_mode = true
+ }
+rescue Net::FTPError
+ $stderr.puts "Couldn't connect."
+rescue Timeout::Error
+ $stderr.puts "Timeout while connecting to server."
+end
+
+begin
+ ftp.login()
+rescue Net::FTPError
+ $stderr.print "Couldn't authentificate.\n"
+end
+
+begin
+ ftp.login(username)
+rescue Net::FTPError
+ $stderr.print "Still couldn't authenticate.\n"
+end
+
+begin
+ ftp.login(username, password)
+rescue Net::FTPError
+ $stderr.print "Couldn't authenticate, even with explicit
+ username and password.\n"
+end
+
+begin
+ ftp.login(username, password, account)
+rescue Net::FTPError
+ $stderr.print "No dice. It hates me.\n"
+end
+#-----------------------------
+ftp.put(localfile, remotefile)
+#-----------------------------
+# Sending data from STDIN is not directly supported
+# by the ftp library module. A possible way to do it is to use the
+# storlines method directly to send raw commands to the ftp server.
+#-----------------------------
+ftp.get(remotefile, localfile)
+#-----------------------------
+ftp.get(remotefile) { |data| puts data }
+#-----------------------------
+ftp.chdir("/pub/ruby")
+print "I'm in the directory ", ftp.pwd(), "\n"
+#-----------------------------
+ftp.mkdir("/pub/ruby/new_dir")
+#-----------------------------
+lines = ftp.ls("/pub/ruby/")
+# => ["drwxr-xr-x 2 matz users 4096 July 17 1998 1.0", ... ]
+
+latest = ftp.dir("/pub/ruby/*.tgz").sort.last
+
+ftp.nlst("/pub/ruby")
+# => ["/pub/ruby/1.0", ... ]
+#-----------------------------
+ftp.quit()
+
+
+# @@PLEAC@@_18.6
+require 'net/telnet'
+t = Net::Telnet::new( "Timeout" => 10,
+ "Prompt" => /%/,
+ "Host" => host )
+t.login(username, password)
+files = t.cmd("ls")
+t.print("top")
+process_string = t.waitfor(/\d+ processes/)
+t.close
+#-----------------------------
+/[$%#>] \z/n
+#-----------------------------
+# In case of an error, the telnet module throws an exception.
+# For control of the behavior in case of an error,
+# you just need to catch the exceptions and do your custom
+# error handling.
+#-----------------------------
+begin
+ telnet.login(username, password)
+rescue TimeoutError
+ fail "Login failed !\n"
+end
+#-----------------------------
+telnet.waitfor('/--more--/')
+#-----------------------------
+telnet.waitfor(String => 'greasy smoke', Timeout => 30)
+
+
+# @@PLEAC@@_18.7
+require 'ping'
+
+puts "#{host} is alive.\n" if Ping.pingecho(host);
+#-----------------------------
+# the ping module only use TCP ping, not ICMP even if we are root
+if Ping.pingecho("kingkong.com")
+ puts "The giant ape lives!\n";
+else
+ puts "All hail mighty Gamera, friend of children!\n";
+end
+
+
+# @@PLEAC@@_19.1
+#!/usr/local/bin/ruby -w
+# hiweb - load CGI class to decode information given by web server
+
+require 'cgi'
+
+cgi = CGI.new('html3')
+
+# get a parameter from a form
+value = cgi.params['PARAM_NAME'][0]
+
+# output a document
+cgi.out {
+ cgi.html {
+ cgi.head { cgi.title { "Howdy there!" } } +
+ cgi.body { cgi.p { "You typed: " + cgi.tt {
+ CGI.escapeHTML(value) } } }
+ }
+}
+
+require 'cgi'
+cgi = CGI.new
+who = cgi.param["Name"][0] # first param in list
+phone = cgi.param["Number"][0]
+picks = cgi.param["Choices"] # complete list
+
+print cgi.header( 'type' => 'text/plain',
+ 'expires' => Time.now + (3 * 24 * 60 * 60) )
+
+
+# @@PLEAC@@_19.3
+#!/usr/local/bin/ruby -w
+# webwhoami - show web user's id
+require 'etc'
+print "Content-Type: text/plain\n\n"
+print "Running as " + Etc.getpwuid.name + "\n"
+
+# % ruby -wc cgi-script # just check syntax
+
+# % ruby -w cgi-script # params from stdin
+# (offline mode: enter name=value pairs on standard input)
+# name=joe
+# number=10
+# ^D
+
+# % ruby -w cgi-script name=joe number=10 # run with mock form input
+# % ruby -d cgi-script name=joe number=10 # ditto, under the debugger
+
+# POST method script in csh
+# % (setenv HTTP_METHOD POST; ruby -w cgi-script name=joe number=10)
+# POST method script in sh
+# % HTTP_METHOD=POST perl -w cgi-script name=joe number=10
+
+
+# @@PLEAC@@_19.4
+# ruby has several security levels, the level "1" is similar to perls taint mode.
+# It can be switched on by providing the -T command line parameter
+# or by setting $SAFE to 1. Setting $SAFE to 2,3 or 4 restricts possible
+# harmful operations further.
+
+#!/usr/bin/ruby -T
+$SAFE = 1
+File.open(ARGV[0], "w")
+# ruby warns with:
+# taint1.rb:2:in `initialize': Insecure operation - initialize (SecurityError)
+
+$SAFE = 1
+file = ARGV[0]
+unless /^([\w.-]+)$/.match(file)
+ raise "filename #{file} has invalid characters"
+end
+file = $1
+# In ruby, even the back reference from a regular expression stays tainted.
+# you need to explicitly untaint the variable:
+file.untaint
+File.open(file, "w")
+
+# Race condition exists like in perl:
+unless File.exists(filename) # Wrong because of race condition
+ File.open(filename, "w")
+end
+
+
+
+# @@PLEAC@@_19.10
+preference_value = cgi.cookies["preference name"][0]
+
+packed_cookie = CGI::Cookie.new("name" => "preference name",
+ "value" => "whatever you'd like",
+ "expires" => Time.local(Time.now.year + 2,
+ Time.now.mon, Time.now.day, Time.now.hour, Time.now.min, Time.now.sec) )
+
+cgi.header("cookie" => [packed_cookie])
+
+#!/usr/local/bin/ruby -w
+# ic_cookies - sample CGI script that uses a cookie
+require 'cgi'
+
+cgi = CGI.new('html3')
+
+cookname = "favorite ice cream"
+favorite = cgi.params["flavor"][0]
+tasty = cgi.cookies[cookname][0] || 'mint'
+
+unless favorite
+ cgi.out {
+ cgi.html {
+ cgi.head { cgi.title { "Ice Cookies" } } +
+ cgi.body {
+ cgi.h1 { "Hello Ice Cream" } +
+ cgi.hr +
+ cgi.form {
+ cgi.p { "Please select a flavor: " +
+ cgi.text_field("flavor", tasty ) }
+ } +
+ cgi.hr
+ }
+ }
+ }
+else
+ cookie = CGI::Cookie.new( "name" => cookname,
+ "value" => favorite,
+ "expires" => Time.local(Time.now.year + 2,
+Time.now.mon, Time.now.day, Time.now.hour, Time.now.min, Time.now.sec) )
+ cgi.out("cookie" => [cookie]) {
+ cgi.html {
+ cgi.head { cgi.title { "Ice Cookies" } } +
+ cgi.body {
+ cgi.h1 { "Hello Ice Cream" } +
+ cgi.p { "You chose as your favorite flavor `#{favorite}'." }
+ }
+ }
+ }
+end
+
+
+# @@PLEAC@@_20.9
+def templatefile(filename, fillings)
+ aFile = File.new(filename, "r")
+ text = aFile.read()
+ aFile.close()
+ pattern = Regexp.new('%%(.*?)%%')
+ text.gsub!(pattern) {
+ fillings[$1] || ""
+ }
+ text
+end
+
+fields = {
+ 'username' => whats_his_name,
+ 'count' => login_count,
+ 'total' => minutes_used
+}
+puts templatefile('simple.template', fields)
+
+# @@INCOMPLETE@@
+# An example using databases is missing
+
diff --git a/tests/examplefiles/example.rhtml b/tests/examplefiles/example.rhtml
new file mode 100644
index 00000000..041bec17
--- /dev/null
+++ b/tests/examplefiles/example.rhtml
@@ -0,0 +1,561 @@
+<% @title = 'Moderatoren-Interface' %>
+
+<dl>
+ <dt><%= link_to 'Proben', :controller => '/admin/proben' %></dt>
+ <dd>Die angesetzten Proben des Orchesters</dd>
+ <dt><%= link_to 'Auftritte', :controller => '/admin/proben' %></dt>
+ <dd>Die Auftritte des Orchesters</dd>
+ <%- if @valid_user and @valid_user.admin? -%>
+ <dt><%= link_to 'Benutzer', :controller => '/admin/user' %></dt>
+ <dd>Benutzer organisieren (nur für den Admin)</dd>
+ <%- end -%>
+</dl>
+<% @title = 'Anmeldung' %>
+
+<%= render :partial => 'user_form', :object => @user %>
+<% @title = 'Administrator erstellen' %>
+
+<%= render :partial => 'user_form', :object => @user %>
+<%= form_tag %>
+<table>
+ <tr>
+ <td>Name:</td>
+ <td><%= text_field 'user', 'name' %></td>
+ </tr>
+ <tr>
+ <td>Passwort:</td>
+ <td><%= password_field 'user', 'password' %></td>
+ </tr>
+ <tr>
+ <td></td>
+ <td><%= submit_tag 'Anmelden' %></td>
+</table>
+<%= end_form_tag %>
+<% @title = 'Neuer Benutzer' -%>
+<%= error_messages_for :user %>
+<%= render :partial => 'form', :object => @user %>
+<%= form_tag %>
+<table>
+ <tr>
+ <td>Name:</td>
+ <td><%= text_field 'user', 'name' %></td>
+ </tr>
+ <tr>
+ <td>Passwort:</td>
+ <td><%= password_field 'user', 'password' %></td>
+ </tr>
+ <tr>
+ <td></td>
+ <td><%= submit_tag 'Anlegen' %></td>
+</table>
+<%= end_form_tag %>
+<% @title = 'Auftritte' %>
+
+<table cellspacing="0" summary="Auftritte: Wann treten wir Wo auf?">
+ <%= render :partial => 'head' %>
+ <%= render :partial => 'day', :collection => @days %>
+</table>
+<% day, auftritte = *day -%>
+<%
+ for auftritt in auftritte
+-%>
+
+<tr>
+ <td class="pplan_datum">
+ <%= colorize day.to_s(:dots) if day %>
+ <% if day and day.wday == 6 %><br /><em>Samstag</em><% end %>
+ </td>
+ <td class="pplan_zeit">
+ <%= colorize auftritt.time %>
+ </td>
+ <td class="pplan_stueck">
+ <%= colorize auftritt.program %>
+ <%= link_to 'E', :controller => 'admin/auftritte', :action => :edit, :id => auftritt %>
+ </td>
+ <td class="pplan_besetzung">
+ <%= colorize(auftritt.place, 'Ort: ') + '<br />' unless auftritt.place.blank? %>
+ </td>
+</tr>
+
+<%
+ day = nil
+ end
+-%>
+<tr>
+ <th scope="col" class="pplan_datum">Datum</th>
+ <th scope="col" class="pplan_zeit">Zeit</th>
+ <th scope="col" class="pplan_stueck">Programm</th>
+ <th scope="col" class="pplan_besetzung">Ort</th>
+</tr>
+<% @title = "Besetzung - #{@instrument.name}" %>
+
+<p>
+<%= pluralize(@members.size, 'Schüler spielt', 'Schüler spielen') %> <%= h @instrument.name %>:
+</p>
+
+<table class="members">
+ <%= render :partial => 'member', :collection => @members %>
+</table>
+<% @title = 'Besetzung: %d Mitglieder' % Member.count -%>
+
+<div class="page-links">
+<% if params[:action] == 'all' -%>
+<%= link_to 'seitenweise', :action => :index %>
+<% else -%>
+<%= link_to_if @member_pages.current.previous, '<<', :page => @member_pages.current.previous %>
+| <%= link_to 'alle', :action => :all %> |
+<%= link_to_if @member_pages.current.next, '>>', :page => @member_pages.current.next %>
+<% end -%>
+| <%= link_to 'Nach Instrumenten', :action => :select_instrument %>
+</div>
+
+<table class="members">
+<%= render :partial => 'member', :collection => @members %>
+</table>
+<% @title = "Besetzung - Instrument wählen" %>
+
+<ul>
+<% for instr in @instruments -%>
+<li>
+ <%= link_to h(instr.name), :action => :instrument, :id => instr.name %>
+ <span class="counter">(<%= h instr.members.size %>)</span>
+</li>
+<% end -%>
+</ul>
+<% @title = "Besetzung: #{@member.name}" -%>
+
+<dl>
+
+<dt>Instrument / Aufgabe:</dt>
+<dd><%= link_to_instruments_of @member %></dd>
+
+<dt>Geburtstag:</dt>
+<dd><%= h @member.birthday.to_s(:dots) %></dd>
+
+<dt>Adresse:</dt>
+<dd><%= h @member.street %><br /><%= h @member.plz %></dd>
+
+<dt>Telefon:</dt>
+<dd><%= h @member.phone %></dd>
+
+<dt>Email:</dt>
+<dd><%= mail_to @member.email, @member.email, :encode => 'javascript' %></dd>
+
+</dl>
+<tr class="member">
+ <td><%= link_to member.name, :action => :show, :id => member %>:
+ <%= link_to_instruments_of member %>
+ </td>
+</tr>
+<% @title = 'Arbeitsgruppen' -%>
+<p>
+ Die Arbeitsgruppen sind verantwortlich für die Organisation und Durchführung verschiedenster Aufgaben:
+</p>
+
+<ul class="liste">
+
+ <li><b>Plakate und Konzertkarten</b>
+ <ul>
+ <li>Frau Schraps</li>
+ <li>Paul-Robert Achcenich</li>
+ <li>Josefine Dahms</li>
+ </ul>
+ </li>
+
+ <li><b>Noten</b><br />
+ <ul>
+ <li>Frau Puppe</li>
+ <li>Theresa Rebin</li>
+ </ul>
+ </li>
+
+ <li><b>Programme</b><br />
+ <ul>
+ <li>?</li>
+ </ul>
+ </li>
+
+ <li><b>Instrumentenstransporte</b><br />
+ <ul>
+ <li>Frau Feldmann</li>
+ <li>Knut Müller</li>
+ <li>Patrick Wolter</li>
+ <li>Alexaner Wolf</li>
+ </ul>
+ </li>
+
+ <li><b>Internetseite</b><br />
+ <ul>
+ <li>Frau Sternbeck</li>
+ <li>Uwe Ritzschke</li>
+ <li>Paul-Robert Achcenich</li>
+ <li>Knut Müller</li>
+ <li>Alexander Wolf</li>
+ </ul>
+ </li>
+
+</ul>
+<% @title = 'Chronik' -%>
+<p>
+ Das Jugendsinfonieorchester Marzahn-Hellersdorf wurde im Januar 2005 an der
+ Musikschule Marzahn-Hellersdorf gegründet und gab im Mai 2005 sein erstes
+ umjubeltes Konzert im FEZ Wuhlheide. Das Orchester umfasst zur Zeit ca. 65
+ jugendliche Musiker und soll auf die Größe eines ausgewachsenen
+ Sinfonieorchesters erweitert werden (80-100 Musiker).
+</p>
+
+<p>
+ Als musikalischer Leiter konnte der Dirigent und Echo-Preisträger Jobst
+ Liebrecht gewonnen werden, der die Musikschule schon aus einer früheren
+ Zusammenarbeit anlässlich der Kinderoper 'Pollicino' von Hans Werner Henze
+ kennt. Das Orchester probt wöchentlich. Neben den Tuttiproben finden außerdem
+ ebenfalls wöchentlich Stimmsatzproben statt, die von Lehrkräften betreut werden.
+ Das gemeinsame Ziel ist der Aufbau eines leistungsstarken, lebendigen
+ Klangkörpers, der die Jugendlichen und die Zuhörer ganz neu und direkt für die
+ Orchestermusik begeistert und diese Musik in den sozialen Brennpunkt Marzahn-
+ Hellersdorf trägt.
+</p>
+
+<p>
+ Im Jahr sind etwa 2-3 Konzertprogramme geplant, mit denen wir in Konzertsälen
+ auftreten. Das erste Konzert des Jugendsinfonieorchesters Marzahn-Hellersdorf
+ wurde von DeutschlandRadio Kultur aufgezeichnet und in einer Sendung mit dem
+ Titel &bdquo;EINSTAND: Nicht nur auf der Strasse herumhängen&rdquo; porträtiert.
+ Wir wollen außerdem vor Ort in Marzahn und Hellersdorf in die Öffentlichkeit
+ gehen und spielen, um so für die Kultur zu werben und auch weitere Kinder und
+ Jugendliche für die Musik und fürs Mitmachen zu gewinnen. Durch die Einrichtung
+ eines zusätzlichen Vororchesters wird längerfristig versucht, die Arbeit auf ein
+ breites Fundament zu stellen, eine Werkstatt, ein musikalisches Bauhaus zu
+ gründen. Wenn die Orchesterarbeit erfolgreich angelaufen ist, sollen auch
+ übergreifende Projekte (Theater, Tanz, Chor) stattfinden.
+</p>
+
+<p>
+ Das Orchester will Musik von heute spielen in jedem Sinn, ob es sich um Stücke
+ aus der sinfonischen Tradition handelt oder um zeitgenössische Musik. Wir kennen
+ keine Berührungsängste und sind neugierig auf Musik aller Art und möchten diese
+ Neugierde mit unserem Publikum teilen.
+</p>
+<% @title = 'Dirigent - Jobst Liebrecht' -%>
+<p>
+ <%= image_tag 'jobstliebrecht.jpg', :alt => 'Jobst Liebrecht', :title => 'Jobst Liebrecht', :class => 'pic_right' %>
+ Jobst Liebrecht studierte Dirigieren an der Musikhochschule in München und bei Peter Eötvös. Sein spezielles Interesse
+ für neue Musik führte schnell zur Zusammenarbeit mit renommierten Ensembles auf dem Gebiet wie dem Ensemble Modern,
+ Frankfurt, dem Klangforum-Ensemble, Wien, dem Ensemble Köln sowie dem Ensemble United Berlin. Aufnahmen entstanden beim
+ WDR, beim DeutschlandRadio Berlin, beim BR und beim SFB. Er dirigierte u.a. das Rundfunk Sinfonieorchester Berlin, die
+ Duisburger Philharmoniker und das Münchner Kammerorchester sowie in den Opernhäusern in Halle und Giessen. Tourneen im
+ Ausland führten ihn nach Argentinien, Georgien, Südkorea und in die USA.
+</p>
+
+<p>
+ Zu den Ur- und Erstaufführungen, die er betreut hat, gehören die Opern 'Lunu' von Moritz Eggert, 'Gloria von Jaxtberg' von
+ HK Gruber sowie in Zusammenarbeit mit dem Regisseur Einar Schleef das Musiktheaterspiel 'Der Golem in Bayreuth' von Ulla
+ Berkewicz/Lesch Schmidt am Wiener Burgtheater.
+</p>
+
+<p>
+ Jobst Liebrecht war mehrere Jahre lang Assistent von Hans Werner Henze und auch immer wieder pädagogisch tätig. Seine
+ Aufnahme von Henzes Märchenoper 'Pollicino', die als CD bei Wergo erschienen ist, wurde mit dem ECHO-Preis 2004 in der
+ Sparte 'Klassik für Kinder' ausgezeichnet.
+</p>
+
+<p>
+ Als Komponist ist Jobst Liebrecht mit Liedern, Kammermusik sowie Bühnenmusiken an die Öffentlichkeit getreten.
+</p>
+<% message, backtrace = session[:boom] -%>
+<% @title = 'Fehler in Zeile %d' % [backtrace[/line\s+#(\d+)/,1]] -%>
+<div class="flash">
+<div class="error"><%= h message %></div>
+</div>
+<%= debug backtrace %>
+<% cache :action_suffix => (action = params[:action]) do -%>
+<p>
+Der Inhalt für die Aktion <%= h action.inspect %> fehlt noch.
+</p>
+<% end -%>
+<% @title = 'Schulferien Berlin' -%>
+<p>
+ Unser Orchester besteht zu einem sehr großen Teil aus Schülern und auch die
+ Musikschule, der die meisten von uns entstammen, hat in den Schulferien
+ geschlossen.<br />
+ Deshalb finden innerhalb der <strong>Berliner Ferienzeiten keine Proben</strong> statt.
+</p>
+
+<table cellspacing="0" summary="Schulferien" class="ferien">
+ <tr>
+ <th scope="col" class="ferien_zeitraum">Zeitraum</th>
+ <th scope="col" class="ferien_jahr">2006</th>
+ <th scope="col" class="ferien_jahr">2007</th>
+ <th scope="col" class="ferien_jahr">2008</th>
+ </tr>
+
+ <tr>
+ <td class="ferien_zeitraum">
+ Winter</td>
+ <td class="ferien_jahr">
+ 30.01. - 03.02.</td>
+ <td class="ferien_jahr">
+ 05.02. - 10.02.</td>
+ <td class="ferien_jahr">
+ 04.02. - 09.02.</td>
+ </tr>
+
+ <tr>
+ <td class="ferien_zeitraum">
+ Ostern/Frühjahr</td>
+ <td class="ferien_jahr">
+ 10.04. - 21.04.</td>
+ <td class="ferien_jahr">
+ 02.04. - 13.04.</td>
+ <td class="ferien_jahr">
+ 17.03. - 28.03.</td>
+ </tr>
+
+ <tr>
+ <td class="ferien_zeitraum">
+ Himmelf./Pfingsten</td>
+ <td class="ferien_jahr">
+ 30.04. / 18.05.</td>
+ <td class="ferien_jahr">
+ 30.04. / 18.05.</td>
+ <td class="ferien_jahr">
+ 02.05.</td>
+ </tr>
+
+ <tr>
+ <td class="ferien_zeitraum">
+ Sommer</td>
+ <td class="ferien_jahr">
+ 06.07. - 19.08.</td>
+ <td class="ferien_jahr">
+ 12.07. - 25.08.</td>
+ <td class="ferien_jahr">
+ 17.07. - 30.08.</td>
+ </tr>
+
+ <tr>
+ <td class="ferien_zeitraum">
+ Herbst</td>
+ <td class="ferien_jahr">
+ 02.10. - 14.10.</td>
+ <td class="ferien_jahr">
+ 15.10. - 27.10.</td>
+ <td class="ferien_jahr">
+ </td>
+ </tr>
+
+ <tr>
+ <td class="ferien_zeitraum">
+ Weihnachten</td>
+ <td class="ferien_jahr">
+ 27.12. - 05.01.07</td>
+ <td class="ferien_jahr">
+ 24.12. - 04.01.08</td>
+ <td class="ferien_jahr">
+ </td>
+ </tr>
+
+</table>
+<% @title = 'Termine' -%>
+
+<ul>
+ <li><%= link_to 'Auftritte', :controller => '/auftritte' %></li>
+ <li><%= link_to 'Schulferien', :controller => '/content', :action => :schulferien %></li>
+</ul>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="de">
+<head>
+ <%= tag 'meta', :'http-equiv' => 'content-language', :content => 'de' %>
+ <%= tag 'meta', :'http-equiv' => 'content-type', :content => 'text/html; charset=UTF-8' %>
+ <meta name="AUTHOR" content="Knut Müller, Alexander Wolf, Uwe Ritzschke, Paul-Robert Achcenich" />
+ <meta name="Publisher" content="Knut Müller, Alexander Wolf, Uwe Ritzschke, Paul-Robert Achcenich" />
+ <meta name="Copyright" content="Knut Müller, Alexander Wolf, Uwe Ritzschke, Paul-Robert Achcenich, 2006" />
+ <meta name="Keywords" content="Jugendsinfonieorchester,Marzahn,Hellersdorf,Marzahn-Hellersdorf,Berlin,Orchester,Jobst Liebrecht,FEZ,Pollicino,Moritz Eggert,Popel,Konzert" />
+ <meta name="Description" content="JSO - JugendSinfonieOrchester der Musikschule Marzahn-Hellerdorf in Berlin" />
+ <meta name="Page-topic" content="JSO Marzahn-Hellersdorf - " />
+
+ <meta name="Audience" content="Alle" />
+ <meta name="content-language" content="DE" />
+ <meta name="Page-Type" content="Homepage" />
+ <meta name="robots" content="all" />
+
+ <title>JSO<%-if @title-%> - <%= h @title %><%- end -%></title>
+ <%= stylesheet_link_tag '/rcss/main' %>
+ <%#= stylesheet_link_tag 'main' %>
+ <%= javascript_include_tag 'nospam' %>
+ <%#= javascript_include_tag :defaults %>
+</head>
+
+<body>
+
+<table style="width:100%; height:100%;" cellspacing="10">
+ <tr>
+ <td style="height:120px; width:15%;text-align:center;vertical-align:middle;">
+ <%= image_tag 'JSO-Logo.gif', :alt => 'JSO-Logo' %>
+ </td>
+ <td id="title">
+ <div id="musikschule"><a href="http://www.musikschule-marzahn-hellersdorf.de">Musikschule Marzahn Hellersdorf</a></div>
+ <div id="orchester"><strong>j</strong>ugend<strong>s</strong>infonie<strong>o</strong>rchester</div>
+ </td>
+ </tr>
+ <tr>
+ <td style="width:15%;">
+<% if valid_user -%>
+<ul>
+ <li class="menu2"><%= link_to "Logout #{valid_user.name}", :controller => '/admin/admin', :action => :logout %></li>
+</ul>
+<% end -%>
+<% cache :controller => 'menu', :action => 'main_menu' do -%>
+ <%= render_component :controller => 'menu', :action => 'index' %>
+<% end -%>
+ </td>
+ <td id="main">
+<% unless @flash.keys.empty? -%>
+<div class="flash">
+ <%- for kind, msg in @flash -%>
+ <div class="<%= h kind %>"><%= h msg %></div>
+ <%- end -%>
+</div>
+<% end -%>
+<%= content_tag 'h3', h(@title) if @title %>
+<%= @content_for_layout %>
+ </td>
+ </tr>
+ <tr>
+ <td style="height:80px; width:15%;">
+ </td>
+ <td style="height:80px;">
+ <div style="position:relative; top:80px; right:0px; text-align:right; font-size: x-small; color: #003;">
+ powered by <a href="http://rubyonrails.org">Ruby on Rails</a> <%= Rails::Info.properties.value_for 'Rails version' %> [<%= h RAILS_ENV[/^./] %>]
+ <%= image_tag 'css.png', :alt => 'valid CSS', :title => 'valid Cascading Style Sheet', :style => 'display: inline; vertical-align: middle' %>
+ <%= image_tag 'xhtml11.png', :alt => 'valid XHTML 1.1', :title => 'valid eXtensible Hypertext Markup Language 1.1', :style => 'display: inline; vertical-align: middle' %>
+ </div>
+ </td>
+ </tr>
+</table>
+
+</body>
+
+</html>
+<% @title = 'Ãœbersicht' -%>
+
+<h4>nächste Probe</h4>
+<table cellspacing="0" summary="Probenplan: Wann wird Was geprobt?" class="proben">
+ <%= render :partial => 'proben/head' %>
+ <%= render :partial => 'proben/day', :object => @next_probe %>
+</table>
+<h4><%= link_to 'weitere Proben...', :controller => 'proben' %></h4>
+
+<h4>nächster Auftritt</h4>
+<table cellspacing="0" summary="Auftritte: Wann treten wir Wo auf?" class="auftritte">
+ <%= render :partial => 'auftritte/head' %>
+ <%= render :partial => 'auftritte/day', :object => @next_auftritt %>
+</table>
+<h4><%= link_to 'mehr Auftritte...', :controller => 'auftritte' %></h4>
+<ul>
+ <%= category 'Ãœbersicht', home_url %>
+ <%= subcat 'Wer sind wir?', :wer %>
+ <%= subcat 'Dirigent' %>
+ <%= subcat 'Besetzung', url_for(:controller => '/besetzung') %>
+ <%= subcat 'Repertoire' %>
+
+ <%= category 'Termine' %>
+ <%= subcat 'Auftritte', url_for(:controller => '/auftritte', :action => :plan) %>
+ <%= subcat 'Schulferien' %>
+
+ <%= category 'Probenplan', url_for(:controller => '/proben', :action => :plan) %>
+
+ <%= category 'Organisation' %>
+ <%= subcat 'Orchesterrat' %>
+ <%= subcat 'Arbeitsgruppen' %>
+
+ <%= category 'Chronik' %>
+ <%= subcat 'Konzerte' %>
+ <%= subcat 'Audio' %>
+ <%= subcat 'Presse' %>
+
+ <%= category 'Links', '#' %>
+ <%= subcat 'Bilderseite', 'http://musikschule.iden04.de' %>
+ <%= subcat 'Musikschule', 'http://www.musikschule-marzahn-hellersdorf.de' %>
+
+ <li><br /></li>
+
+ <%= category 'Kontakt' %>
+</ul>
+<% @title = 'Probenplan' %>
+
+<table cellspacing="0" summary="Probenplan: Wann wird Was geprobt?">
+ <%= render :partial => 'head' %>
+ <%= render :partial => 'day', :collection => @days %>
+</table>
+
+<p style="font-size:14px; margin-top:-10px; padding-top:0px; padding-left:40px;">
+Ort (wenn nicht anders angegeben): Schule am Pappelhof
+</p>
+
+<%= render_partial 'raum' %>
+<% day, proben = *day -%>
+<%
+ for probe in proben
+-%>
+
+<tr>
+ <td class="pplan_datum">
+ <%= colorize day.to_s(:dots) if day %>
+ <% if day and day.wday == 6 %><br /><em>Samstag</em><% end %>
+ </td>
+ <td class="pplan_zeit">
+ <%= colorize probe.time %>
+ </td>
+ <td class="pplan_stueck">
+ <%= colorize(probe.place, 'Ort: ') + '<br />' unless probe.place.blank? %>
+ <%= colorize probe.program %>
+ <%= link_to 'E', :controller => 'admin/proben', :action => :edit, :id => probe %>
+ </td>
+ <td class="pplan_besetzung">
+ <%= h probe.instrumentation %>
+ </td>
+</tr>
+
+<%
+ day = nil
+ end
+-%>
+<tr>
+ <th scope="col" class="pplan_datum">Datum</th>
+ <th scope="col" class="pplan_zeit">Zeit</th>
+ <th scope="col" class="pplan_stueck">Stücke</th>
+ <th scope="col" class="pplan_besetzung">Besetzung</th>
+</tr>
+<h4>Probenräume</h4>
+<table cellspacing="0" summary="Probenräume: Wer probt Wo?">
+ <tr>
+ <th scope="col" class="praum_wer">Wer</th>
+ <th scope="col" class="praum_raum">Raum</th>
+ <th scope="col" class="praum_adresse">Adresse</th>
+
+ </tr>
+ <tr>
+ <td class="praum_wer">Streicher</td>
+ <td class="praum_raum">Schule am Pappelhof<br />(Raum Nr.)</td>
+ <td class="praum_adresse">(Anschrifft Pappelhofschule)</td>
+ </tr>
+ <tr>
+
+ <td class="praum_wer">Blechbläser</td>
+ <td class="praum_raum">Musikschule Marzahn<br />(Raum Nr.)</td>
+ <td class="praum_adresse">(Anschrifft Musikscule Marzahn)</td>
+ </tr>
+ <tr>
+ <td class="praum_wer">Holzbläser</td>
+
+ <td class="praum_raum">Schule am Pappelhof<br />(Raum Nr.)</td>
+ <td class="praum_adresse">(Anschrifft Pappelhofschule)</td>
+ </tr>
+ <tr>
+ <td class="praum_wer">...</td>
+ <td class="praum_raum">(Ort)<br />(Raum Nr.)</td>
+
+ <td class="praum_adresse">(Anschrifft)</td>
+ </tr>
+</table>
diff --git a/tests/examplefiles/example.xhtml b/tests/examplefiles/example.xhtml
new file mode 100644
index 00000000..a08cf753
--- /dev/null
+++ b/tests/examplefiles/example.xhtml
@@ -0,0 +1,376 @@
+<html>
+ <head>
+ <script lang="javascript" type="text/javascript">
+ // <!--
+ function toggleVisible(element) {
+ if (element.style.display == 'block') {
+ element.style.display = 'none';
+ } else {
+ element.style.display = 'block';
+ }
+ return false;
+ }
+ // -->
+ </script>
+ <title>Error</title>
+ <style>
+ .path {
+ padding: 5px;
+ font-size: 140%;
+ background: #ddd;
+ }
+ .error {
+ padding: 5px;
+ padding-top: 15px;
+ font-size: 140%;
+ color: #f00;
+ }
+ .load {
+ padding: 5px;
+ color: #555;
+ }
+ .source {
+ border: 1px solid #ccc;
+ padding: 10px;
+ margin-top: 10px; margin-bottom: 10px;
+ }
+ h2 {
+ padding-left: 5px;
+ background: #eee;
+ }
+ </style>
+ </head>
+ <body>
+ <h1>Error</h1>
+
+<?r
+ if Run.mode == :debug
+ require 'cgi'
+?>
+ <?r for error, path in @context.rendering_errors ?>
+ <div class="path"><strong>Path:</strong> #{path}</div>
+ <div class="error"><strong>#{CGI.escapeHTML(error.to_s)}</strong></div>
+ <div class="load">
+ <strong><a href="#{request.uri}">Reload</a></strong> this page.
+ Go to the <strong><a href="#{request.referer}">referer</a></strong> or the <strong><a href="/">home page</a></strong>.
+ </div>
+ <div class="source">
+ <?r
+ extract = error.source_extract.split("\n")
+ ?>
+ In file <b>'#{error.hot_file}'</b> #{error.hot_file =~ /\.xhtml$/ ? '(line numbering is aproximate due to template transformation)' : nil}:
+ <br /><br />
+ <?r
+ extract.each_with_index do |line, idx|
+ line = sanitize(line)
+ if 5 == idx
+ ?>
+ <div style="background: #eee">#{line}</div>
+ <?r else ?>
+ <div>#{line}</div>
+ <?r
+ end
+ end
+ ?>
+ </div>
+ <h2><a href="#" onclick="return toggleVisible(document.getElementById('trace'));">Stack Trace</a></h2>
+ <div id="trace" style="display: none;">
+ <?r error.backtrace.zip(error.source_for_backtrace).each_with_index do |step,step_idx| ?>
+ <div><a href="#" onclick="return toggleVisible(document.getElementById('trace_#{step_idx}'));">#{sanitize(step.first)}</a></div>
+ <div class="source" id="trace_#{step_idx}" style="display: none;">
+ <?r
+ extract = step.last.split("\n")
+ extract.each_with_index do |line, idx|
+ line = sanitize(line)
+ if 5 == idx
+ ?>
+ <div style="background: #eee">#{line}</div>
+ <?r else ?>
+ <div>#{line}</div>
+ <?r
+ end
+ end
+ ?>
+ </div>
+
+
+ <?r end ?>
+ </div>
+ <?r end ?>
+
+ <h2><a href="#" onclick="document.getElementById('request').style.display = 'block'; return false">Request</a></h2>
+ <div id="request" style="display: none">
+ <p><strong>Parameters:</strong> #{request.params.reject{ |k,v| k == :__RELOADED__ }.inspect}</p>
+ <p><strong>Cookies:</strong> #{request.cookies.inspect}</p>
+ <p><strong>Headers:</strong><br />#{request.headers.collect { |k, v| "#{k} => #{v}" }.join('<br />')}</p>
+ </div>
+
+ <h2><a href="#" onclick="document.getElementById('response').style.display = 'block'; return false">Response</a></h2>
+ <div id="response" style="display: none">
+ <p><strong>Headers:</strong> #{request.response_headers.inspect}</p>
+ <p><strong>Cookies:</strong> #{request.response_cookies.inspect}</p>
+ </div>
+
+ <h2><a href="#" onclick="document.getElementById('session').style.display = 'block'; return false">Session</a></h2>
+ <div id="session" style="display: none">
+ <p><strong>Values:</strong> #{session.inspect}</p>
+ </div>
+
+ <br /><br />
+ Powered by <a href="http://www.nitrohq.com">Nitro</a> version #{Nitro::Version}
+<?r end ?>
+ </body>
+</html>
+<SystemPage>
+ <?r base = "#{@base}/%base%" ?>
+ <h1><a href="/"> Home</a> > <a href="#@base">System</a> > <a href="#{base}/list">#{"%plural%".humanize}</a> > Edit #{"%name%".humanize} </h1>
+ <?r if @all ?>
+ <a href="#{request.uri.gsub(/\/all$/, '')}">Show editable</a>
+ #{form_for @obj, :action => "#{base}/save", :cancel => "#{base}/list", :all => true}
+ <?r else ?>
+ <a href="#{request.uri}/all">Show all</a>
+ #{form_for @obj, :action => "#{base}/save", :cancel => "#{base}/list"}
+ <?r end ?>
+</SystemPage>
+#{form_for(@%name%)}
+<SystemPage>
+ <?r base = "#{@base}/%base%" ?>
+ <h1>#{"%plural%".humanize}</h1>
+ <h2><a href="#{base}/new">New #{"%name%".humanize}</a></h2>
+ <form action="search">
+ Search #{"%plural%".humanize}: <input type="text" name="q" />&nbsp;<input type="submit" value="Search" />
+ </form>
+ <table>
+ <?r for obj in @list ?>
+ <tr>
+ <td width="100%"><a href="#{base}/edit/#{obj.oid}">#{obj.to_s}</a></td>
+ <?r if obj.respond_to?(:update_time) ?>
+ <td nowrap="1">#{obj.update_time.stamp(:db)}</td>
+ <?r end ?>
+ <td><a href="#{base}/edit/#{obj.oid}">edit</a></td>
+ <td><a href="#{base}/delete/#{obj.oid}">del</a></td>
+ </tr>
+ <?r end ?>
+ </table>
+</SystemPage>
+<SystemPage>
+ <?r base = "#{@base}/%base%" ?>
+ <h1><a href="/"> Home</a> > <a href="#@base">System</a> > #{"%plural%".humanize}</h1>
+ <a href="#{base}/new">New #{"%name%".humanize}</a>
+ <p>
+ <form action="#{base}/search">
+ Search #{"%plural%".humanize}: <input type="text" name="q" />&nbsp;<input type="submit" value="Search" />
+ </form>
+ </p>
+ <table>
+ <?r for obj in @list ?>
+ <tr>
+ <td width="100%"><a href="#{base}/edit/#{obj.oid}">#(obj.to_s)</a></td>
+ <?r if obj.respond_to?(:update_time) ?>
+ <td nowrap="1">#{obj.update_time.stamp(:db)}</td>
+ <?r end ?>
+ <td><a href="#{base}/edit/#{obj.oid}">edit</a></td>
+ <td><a href="#{base}/delete/#{obj.oid}" onclick="confirm('Are you sure?')">del</a></td>
+ </tr>
+ <?r end ?>
+ </table>
+ <div class="pager" if="@pager and @pager.navigation?">
+ #{@pager.navigation}
+ </div>
+</SystemPage>
+<SystemPage>
+ <?r base = "#{@base}/%base%" ?>
+ <h1><a href="/"> Home</a> > <a href="#@base">System</a> > <a href="#{base}/list">#{"%plural%".humanize}</a> > New #{"%name%".humanize}</h1>
+ <?r if @all ?>
+ <a href="#{request.uri.gsub(/\/all$/, '')}">Show editable</a>
+ #{form_for @obj, :action => "#{base}/save", :cancel => "#{base}/list", :all => true, :enctype => "multipart/form-data"}
+ <?r else ?>
+ <a href="#{request.uri}/all">Show all</a>
+ #{form_for @obj, :action => "#{base}/save", :cancel => "#{base}/list", :enctype => "multipart/form-data"}
+ <?r end ?>
+</SystemPage>
+<SystemPage>
+ <?r base = "#{@base}/%base%" ?>
+ <h1><a href="/"> Home</a> > <a href="#@base">System</a> > <a href="#{base}/list">#{"%plural%".humanize}</a> > Search for '#@query'</h1>
+ <p>
+ <form action="#{base}/search">
+ Search #{"%plural%".humanize}: <input type="text" name="q" />&nbsp;<input type="submit" value="Search" />
+ </form>
+ </p>
+ <?r if @list.nil? ?>
+ <p>Search method is not implemented for this object</p>
+ <?r else ?>
+ <table>
+ <?r for obj in @list ?>
+ <tr>
+ <td width="100%"><a href="#{base}/edit/#{obj.oid}">#(obj.to_s)</a></td>
+ <?r if obj.respond_to?(:update_time) ?>
+ <td nowrap="1">#{obj.update_time.stamp(:db)}</td>
+ <?r end ?>
+ <td><a href="#{base}/edit/#{obj.oid}">edit</a></td>
+ <td><a href="#{base}/delete/#{obj.oid}">del</a></td>
+ </tr>
+ <?r end ?>
+ </table>
+ <div class="pager" if="@pager and @pager.navigation?">
+ #{@pager.navigation}
+ </div>
+ <?r end ?>
+</SystemPage>
+<SystemPage>
+ <?r base = "#{@base}/%base%" ?>
+ <h1>View %name%</h1>
+ <h2><a href="#{base}/list">List of %plural%</a></h2>
+ <code>
+ #{@obj.to_yaml}
+ </code>
+</SystemPage>
+<strong>Access denied</strong>
+<SystemPage>
+ <?r base = "#{@base}/%base%" ?>
+ <h1><a href='/'>Home</a> > System</h1>
+
+ <h2>Og managed classes</h2>
+
+ <table>
+ <tr>
+ <th>Class</th>
+ <th>Count</th>
+ <th colspan="2">Cleanup</th>
+ <th>Properties</th>
+ </tr>
+ <?r for c in @classes ?>
+ <tr>
+ <td><a href="#@base/#{Scaffolding.class_to_path(c).plural}/list">#{c.name}</a></td>
+ <td>#{c.count}</td>
+ <td><a href="delete_all/#{c.name}" onclick="return confirm('Delete all instances?')">delete</a></td>
+ <td><a href="destroy/#{c.name}" onclick="return confirm('Drop the schema?')">destroy</a></td>
+ <td width="100%">#{c.properties.values.join(', ')}</td>
+ </tr>
+ <?r end ?>
+ </table>
+
+ <h2>System configuration</h2>
+
+ <table width="100%">
+ <tr>
+ <th>Name</th>
+ <th>Value</th>
+ <th>Type</th>
+ <th>Description</th>
+ </tr>
+ <?r for s in @settings ?>
+ <tr>
+ <td>#{s.owner}.<strong>#{s.name}</strong></td>
+ <td>#{s.value.inspect}</td>
+ <td>#{s.type}</td>
+ <td>#{s.options[:doc]}</td>
+ </tr>
+ <?r end ?>
+ </table>
+</SystemPage>
+
+<b><?r $include1 = true ?></b>
+<b><?r $include2 = true ?></b>
+<html>
+ <b>Test</b>
+
+<?r @tflag = true ?>
+
+<render href="blog/inc1" />
+<render href='blog/inc2' />
+
+</html>
+<html>hello</html>
+Hello #{username}
+
+how do you feel?
+
+Here is your <b>Token</b>: #{token}
+<Page title="Questions and Tips by Tags">
+ <div id="left">
+ <?r if @tags ?>
+ <h1>Questions with Tags: #{@tags.join(" ")}</h1>
+
+ <?r if @questions && @questions.size > 0 ?>
+ <?r if @qtags ?>
+ Too many results for that Tag, please reduce the number by using one of the following Tags:
+ #{cloud_of(@qtags)}
+ <?r end ?>
+ <div class="results">
+ <?r @questions.each do |q| ?>
+ <h2><a href="/question/#{q.oid}">#{q.question}</a></h2>
+ <p>
+ <?r excerpt = excerpt_with_words(q.text, @tags) ?>
+ #{excerpt}
+ </p>
+ <p style="float:right;">#{q.answers.size.to_i} answers</p>
+ <?r end ?>
+ </div>
+ <div class="pager">
+ #{@qpager.navigation}
+ </div>
+ <?r else ?>
+ <div class="results_none">
+ <h2>no question with this/these tag(s) found</h2>
+ <p><a href="/ask">Ask a question here.</a></p>
+ </div>
+ <?r end ?>
+
+ <?r if @tips && @tips.size > 0 ?>
+ <h1>Tips with Tags: #{@tags.join(" ")}</h1>
+ <?r if @ttags ?>
+ Too many results for that Tag, please reduce the number by using one of the following Tags:
+ #{cloud_of(@ttags)}
+ <?r end ?>
+ <div class="results">
+ <?r @tips.each do |t| ?>
+ <h2><a href="/tip/#{t.oid}">#{t.title}</a></h2>
+ <p>
+ <?r excerpt = excerpt_with_words(t.text, @tags) ?>
+ #{excerpt}
+ </p>
+ <?r end ?>
+ </div>
+ <div class="pager">
+ #{@tpager.navigation}
+ </div>
+ <?r end ?>
+
+ <?r if @tutorials && @tutorials.size > 0 ?>
+ <h1>Tutorials with Tags: #{@tags.join(" ")}</h1>
+ <?r if @tuttags ?>
+ Too many results for that Tag, please reduce the number by using one of the following Tags:
+ #{cloud_of(@tuttags)}
+ <?r end ?>
+ <div class="results">
+ <?r @tutorials.each do |t| ?>
+ <h2><a href="/tutorial/#{t.oid}">#{t.title}</a></h2>
+ <p>
+ <?r excerpt = excerpt_with_words(t.text, @tags) ?>
+ #{excerpt}
+ </p>
+ <?r end ?>
+ </div>
+ <div class="pager">
+ #{@tpager.navigation}
+ </div>
+ <?r end ?>
+
+
+ <?r else ?>
+ <div class="cloud">
+ <?r
+ sum = all_tags.inject(0) { |sum, t| sum + t.popularity.to_i }
+ ?>
+ <?r all_tags.each do |t| ?>
+ <a href="/tags/#{t.name}" style="font-size:#{(1+((t.popularity.to_i/sum.to_f)*2)).to_s[0..3]}em;">#{t.name}</a>
+ <?r end ?>
+ </div> <!-- #cloud -->
+ <?r end ?>
+ </div> <!-- #left -->
+
+ <render href="/right" />
+</Page>
+
+<!-- Copyright © 2006 Kashia Buch (kashia@vfemail.net), Fabian Buch (fabian@fabian-buch.de). All rights reserved. -->
diff --git a/tests/examplefiles/example.xml b/tests/examplefiles/example.xml
new file mode 100644
index 00000000..61d7ad97
--- /dev/null
+++ b/tests/examplefiles/example.xml
@@ -0,0 +1,7686 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE language SYSTEM "language.dtd">
+<language name="Ada" version="1.04" kateversion="2.1" section="Sources" extensions="*.adb;*.ads;*.ada;*.a" mimetype="text/x-adasrc">
+ <highlighting>
+ <list name="keywords">
+ <item> abort </item>
+ <item> abs </item>
+ <item> abstract </item>
+ <item> accept </item>
+ <item> access </item>
+ <item> aliased </item>
+ <item> all </item>
+ <item> and </item>
+ <item> array </item>
+ <item> at </item>
+ <item> begin </item>
+ <item> body </item>
+ <item> constant </item>
+ <item> declare </item>
+ <item> delay </item>
+ <item> delta </item>
+ <item> digits </item>
+ <item> do </item>
+ <item> else </item>
+ <item> elsif </item>
+ <item> end </item>
+ <item> entry </item>
+ <item> exception </item>
+ <item> exit </item>
+ <item> for </item>
+ <item> function </item>
+ <item> generic </item>
+ <item> goto </item>
+ <item> in </item>
+ <item> is </item>
+ <item> limited </item>
+ <item> mod </item>
+ <item> new </item>
+ <item> not </item>
+ <item> null </item>
+ <item> of </item>
+ <item> or </item>
+ <item> others </item>
+ <item> out </item>
+ <item> package </item>
+ <item> pragma </item>
+ <item> private </item>
+ <item> procedure </item>
+ <item> protected </item>
+ <item> raise </item>
+ <item> range </item>
+ <item> rem </item>
+ <item> record </item>
+ <item> renames </item>
+ <item> requeue </item>
+ <item> return </item>
+ <item> reverse </item>
+ <item> separate </item>
+ <item> subtype </item>
+ <item> tagged </item>
+ <item> task </item>
+ <item> terminate </item>
+ <item> then </item>
+ <item> type </item>
+ <item> until </item>
+ <item> use </item>
+ <item> when </item>
+ <item> while </item>
+ <item> with </item>
+ <item> xor </item>
+ </list>
+ <contexts>
+ <context attribute="Normal Text" lineEndContext="#stay" name="Default">
+ <RegExpr attribute="Keyword" context="#stay" String="^\s*if " insensitive="TRUE" beginRegion="Region1"/>
+ <StringDetect attribute="Keyword" context="#stay" String="end if" insensitive="TRUE" endRegion="Region1"/>
+ <RegExpr attribute="Keyword" context="#stay" String="^\s*case " insensitive="TRUE" beginRegion="Region2"/>
+ <StringDetect attribute="Keyword" context="#stay" String="end case" insensitive="TRUE" endRegion="Region2"/>
+ <RegExpr attribute="Keyword" context="#stay" String="\sloop\s+" insensitive="TRUE" beginRegion="Region3"/>
+ <RegExpr attribute="Keyword" context="#stay" String="\sloop$" insensitive="TRUE" beginRegion="Region3"/>
+ <StringDetect attribute="Keyword" context="#stay" String="end loop;" insensitive="TRUE" endRegion="Region3"/>
+ <RegExpr attribute="Keyword" context="#stay" String="\sselect\s+" insensitive="TRUE" beginRegion="Region4"/>
+ <RegExpr attribute="Keyword" context="#stay" String="\sselect$" insensitive="TRUE" beginRegion="Region4"/>
+ <StringDetect attribute="Keyword" context="#stay" String="end select;" insensitive="TRUE" endRegion="Region4"/>
+ <keyword attribute="Keyword" context="#stay" String="keywords"/>
+ <Float attribute="Float" context="#stay"/>
+ <Int attribute="Decimal" context="#stay"/>
+ <RegExpr attribute="Char" context="#stay" String="'.'"/>
+ <DetectChar attribute="String" context="String" char="&quot;"/>
+ <Detect2Chars attribute="Comment" context="Comment" char="-" char1="-"/>
+ </context>
+ <context attribute="String" lineEndContext="#pop" name="String">
+ <DetectChar attribute="String" context="#pop" char="&quot;"/>
+ </context>
+ <context attribute="Comment" lineEndContext="#pop" name="Comment"/>
+ </contexts>
+ <itemDatas>
+ <itemData name="Normal Text" defStyleNum="dsNormal" />
+ <itemData name="Keyword" defStyleNum="dsKeyword" />
+ <itemData name="Decimal" defStyleNum="dsDecVal" />
+ <itemData name="Base-N" defStyleNum="dsBaseN" />
+ <itemData name="Float" defStyleNum="dsFloat" />
+ <itemData name="Char" defStyleNum="dsChar" />
+ <itemData name="String" defStyleNum="dsString" />
+ <itemData name="Comment" defStyleNum="dsComment" />
+ </itemDatas>
+ </highlighting>
+ <general>
+ <comments>
+ <comment name="singleLine" start="--" />
+ </comments>
+ <keywords casesensitive="0" />
+ </general>
+</language>
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE language SYSTEM "language.dtd">
+<language name="Asm6502" version="1.03" kateversion="2.1" section="Sources" extensions="*.asm" mimetype="text/x-asm6502">
+ <highlighting>
+ <list name = "opcodes6502">
+
+
+ </list>
+
+
+
+
+
+ <contexts>
+ <context name = "Base" attribute = "Normal Text" lineEndContext = "#stay">
+
+
+ <RegExpr String= "#define.*$" attribute = "Preprocessor" context="#stay"/>
+ <RegExpr String= "#include .*$" attribute = "Preprocessor" context="#stay"/>
+ <RegExpr String= ";.*$" attribute = "Comment" context="#stay"/>
+ <RegExpr String= "\.byte" attribute = "Data Type" context="#stay"/>
+ <RegExpr String= "\.byt" attribute = "Data Type" context="#stay"/>
+ <RegExpr String= "\.word" attribute = "Data Type" context="#stay"/>
+ <RegExpr String= "\.asc" attribute = "Data Type" context="#stay"/>
+ <RegExpr String= "\.dsb" attribute = "Data Type" context="#stay"/>
+ <RegExpr String= "\.fopt" attribute = "Data Type" context="#stay"/>
+ <RegExpr String= "\.text" attribute = "Data Type" context="#stay"/>
+ <RegExpr String= "\.data" attribute = "Data Type" context="#stay"/>
+ <RegExpr String= "\.bss" attribute = "Data Type" context="#stay"/>
+ <RegExpr String= "\.zero" attribute = "Data Type" context="#stay"/>
+ <RegExpr String= "\.align" attribute = "Data Type" context="#stay"/>
+ <RegExpr String= "\$[A-Za-z0-9]*" attribute = "Hex" context="#stay"/>
+ <RegExpr String= ",x$" attribute = "Keyword" context="#stay" insensitive="TRUE"/>
+ <RegExpr String= ",y$" attribute = "Keyword" context="#stay" insensitive="TRUE"/>
+ <RegExpr String= "#" attribute = "Keyword" context="#stay" insensitive="TRUE"/>
+ <StringDetect attribute="Keyword" context="#stay" String="TAX" insensitive="TRUE"/>
+ <StringDetect attribute="Keyword" context="#stay" String="ADC" insensitive="TRUE"/>
+ <StringDetect attribute="Keyword" context="#stay" String="AND" insensitive="TRUE"/>
+ <StringDetect attribute="Keyword" context="#stay" String="ASL" insensitive="TRUE"/>
+ <StringDetect attribute="Keyword" context="#stay" String="BCC" insensitive="TRUE"/>
+ <StringDetect attribute="Keyword" context="#stay" String="BCS" insensitive="TRUE"/>
+ <StringDetect attribute="Keyword" context="#stay" String="BEQ" insensitive="TRUE"/>
+ <StringDetect attribute="Keyword" context="#stay" String="BIT" insensitive="TRUE"/>
+ <StringDetect attribute="Keyword" context="#stay" String="BMI" insensitive="TRUE"/>
+ <StringDetect attribute="Keyword" context="#stay" String="BNE" insensitive="TRUE"/>
+ <StringDetect attribute="Keyword" context="#stay" String="BPL" insensitive="TRUE"/>
+ <StringDetect attribute="Keyword" context="#stay" String="BRK" insensitive="TRUE"/>
+ <StringDetect attribute="Keyword" context="#stay" String="BVC" insensitive="TRUE"/>
+ <StringDetect attribute="Keyword" context="#stay" String="BVS" insensitive="TRUE"/>
+ <StringDetect attribute="Keyword" context="#stay" String="CLC" insensitive="TRUE"/>
+ <StringDetect attribute="Keyword" context="#stay" String="CLD" insensitive="TRUE"/>
+ <StringDetect attribute="Keyword" context="#stay" String="CLI" insensitive="TRUE"/>
+ <StringDetect attribute="Keyword" context="#stay" String="CLV" insensitive="TRUE"/>
+ <StringDetect attribute="Keyword" context="#stay" String="CMP" insensitive="TRUE"/>
+ <StringDetect attribute="Keyword" context="#stay" String="CPX" insensitive="TRUE"/>
+ <StringDetect attribute="Keyword" context="#stay" String="CPY" insensitive="TRUE"/>
+ <StringDetect attribute="Keyword" context="#stay" String="DEC" insensitive="TRUE"/>
+ <StringDetect attribute="Keyword" context="#stay" String="DEX" insensitive="TRUE"/>
+ <StringDetect attribute="Keyword" context="#stay" String="DEY" insensitive="TRUE"/>
+ <StringDetect attribute="Keyword" context="#stay" String="EOR" insensitive="TRUE"/>
+ <StringDetect attribute="Keyword" context="#stay" String="INC" insensitive="TRUE"/>
+ <StringDetect attribute="Keyword" context="#stay" String="INX" insensitive="TRUE"/>
+ <StringDetect attribute="Keyword" context="#stay" String="INY" insensitive="TRUE"/>
+ <StringDetect attribute="Keyword" context="#stay" String="JMP" insensitive="TRUE"/>
+ <StringDetect attribute="Keyword" context="#stay" String="JSR" insensitive="TRUE"/>
+ <StringDetect attribute="Keyword" context="#stay" String="LDA" insensitive="TRUE"/>
+ <StringDetect attribute="Keyword" context="#stay" String="LDX" insensitive="TRUE"/>
+ <StringDetect attribute="Keyword" context="#stay" String="LDY" insensitive="TRUE"/>
+ <StringDetect attribute="Keyword" context="#stay" String="LSR" insensitive="TRUE"/>
+ <StringDetect attribute="Keyword" context="#stay" String="NOP" insensitive="TRUE"/>
+ <StringDetect attribute="Keyword" context="#stay" String="ORA" insensitive="TRUE"/>
+ <StringDetect attribute="Keyword" context="#stay" String="PHA" insensitive="TRUE"/>
+ <StringDetect attribute="Keyword" context="#stay" String="PHP" insensitive="TRUE"/>
+ <StringDetect attribute="Keyword" context="#stay" String="PLA" insensitive="TRUE"/>
+ <StringDetect attribute="Keyword" context="#stay" String="PLP" insensitive="TRUE"/>
+ <StringDetect attribute="Keyword" context="#stay" String="ROL" insensitive="TRUE"/>
+ <StringDetect attribute="Keyword" context="#stay" String="ROR" insensitive="TRUE"/>
+ <StringDetect attribute="Keyword" context="#stay" String="RTI" insensitive="TRUE"/>
+ <StringDetect attribute="Keyword" context="#stay" String="RTS" insensitive="TRUE"/>
+ <StringDetect attribute="Keyword" context="#stay" String="SBC" insensitive="TRUE"/>
+ <StringDetect attribute="Keyword" context="#stay" String="SEC" insensitive="TRUE"/>
+ <StringDetect attribute="Keyword" context="#stay" String="SED" insensitive="TRUE"/>
+ <StringDetect attribute="Keyword" context="#stay" String="SEI" insensitive="TRUE"/>
+ <StringDetect attribute="Keyword" context="#stay" String="STA" insensitive="TRUE"/>
+ <StringDetect attribute="Keyword" context="#stay" String="STX" insensitive="TRUE"/>
+ <StringDetect attribute="Keyword" context="#stay" String="STY" insensitive="TRUE"/>
+ <StringDetect attribute="Keyword" context="#stay" String="TAY" insensitive="TRUE"/>
+ <StringDetect attribute="Keyword" context="#stay" String="TSX" insensitive="TRUE"/>
+ <StringDetect attribute="Keyword" context="#stay" String="TXA" insensitive="TRUE"/>
+ <StringDetect attribute="Keyword" context="#stay" String="TXS" insensitive="TRUE"/>
+ <StringDetect attribute="Keyword" context="#stay" String="TYA" insensitive="TRUE"/>
+
+ <keyword String = "opcodes6502" attribute = "Keyword" context = "#stay" />
+ <RegExpr String= "\*=" attribute = "Decimal" context="#stay"/>
+ <RangeDetect char = "&quot;" char1 = "&quot;" attribute = "String" context = "#stay"/>
+ <AnyChar String = "-+&lt;&gt;=;" attribute = "Operator" context = "#stay"/>
+ <Detect2Chars attribute="Comment" context="Commentar 2" char="/" char1="*" beginRegion="Comment"/>
+
+
+ </context>
+ <context attribute="Comment" lineEndContext="#stay" name="Commentar 2">
+ <Detect2Chars attribute="Comment" context="#pop" char="*" char1="/" endRegion="Comment"/>
+ </context>
+ </contexts>
+
+ <itemDatas>
+ <itemData name = "Normal Text" defStyleNum = "dsNormal"/>
+ <itemData name = "Keyword" defStyleNum = "dsKeyword"/>
+ <itemData name="Decimal" defStyleNum="dsDecVal"/>
+ <itemData name="Data Type" defStyleNum="dsDataType"/>
+ <itemData name="Hex" defStyleNum="dsBaseN"/>
+ <itemData name = "String" defStyleNum = "dsString"/>
+ <itemData name = "Comment" defStyleNum = "dsComment"/>
+ <itemData name = "Substitution" defStyleNum = "dsOthers"/>
+ <itemData name="Preprocessor" defStyleNum="dsOthers"/>
+ <itemData name = "Parameter" defStyleNum = "dsOthers"/>
+ <itemData name = "Operator" defStyleNum = "dsOthers"/>
+ <itemData name = "Command" defStyleNum = "dsNormal"/>
+ </itemDatas>
+ </highlighting>
+ <general>
+ <comments>
+ <comment name="singleLine" start=";"/>
+ <comment name="multiLine" start="/*" end="*/"/>
+ </comments>
+ <keywords casesensitive="1"/>
+ </general>
+</language>
+
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE language SYSTEM "language.dtd">
+<language name="AWK" version="0.1" kateversion="2.1" section="Scripts" extensions="*.awk" mimetype="text/x-awk">
+ <highlighting>
+ <list name="keywords">
+ <item>BEGIN</item>
+ <item>END</item>
+ <item>if</item>
+ <item>else</item>
+ <item>while</item>
+ <item>do</item>
+ <item>for</item>
+ <item>in</item>
+ <item>continue</item>
+ <item>break</item>
+ <item>print</item>
+ <item>printf</item>
+ <item>getline</item>
+ <item>function</item>
+ <item>return</item>
+ <item>next</item>
+ <item>exit</item>
+ </list>
+ <list name="builtins">
+ <item>ARGC</item>
+ <item>ARGV</item>
+ <item>CONVFMT</item>
+ <item>ENVIRON</item>
+ <item>FILENAME</item>
+ <item>FNR</item>
+ <item>FS</item>
+ <item>NF</item>
+ <item>NR</item>
+ <item>OFMT</item>
+ <item>OFS</item>
+ <item>ORS</item>
+ <item>RS</item>
+ <item>RSTART</item>
+ <item>RLENGTH</item>
+ <item>SUBSEP</item>
+ </list>
+ <list name="functions">
+ <item>gsub</item>
+ <item>index</item>
+ <item>length</item>
+ <item>match</item>
+ <item>split</item>
+ <item>sprintf</item>
+ <item>sub</item>
+ <item>substr</item>
+ <item>tolower</item>
+ <item>toupper</item>
+ <item>atan2</item>
+ <item>cos</item>
+ <item>exp</item>
+ <item>int</item>
+ <item>log</item>
+ <item>rand</item>
+ <item>sin</item>
+ <item>sqrt</item>
+ <item>srand</item>
+ <item>close</item>
+ <item>fflush</item>
+ <item>system</item>
+ </list>
+ <contexts>
+ <context name="Base" attribute="Normal" lineEndContext="#stay">
+ <keyword String="keywords" attribute="Keyword" context="#stay"/>
+ <keyword String="builtins" attribute="Builtin" context="#stay"/>
+ <keyword String="functions" attribute="Function" context="#stay"/>
+ <Int attribute="Decimal" context="#stay"/>
+ <Float attribute="Float" context="#stay"/>
+ <RegExpr String="\$[A-Za-z0-9_]+" attribute="Field" context="#stay"/>
+ <DetectChar char="&quot;" attribute="String" context="String"/>
+ <DetectChar char="/" attribute="Pattern" context="Pattern"/>
+ <RegExpr String="#.*$" attribute="Comment" context="#stay"/>
+ </context>
+ <context name="String" attribute="String" lineEndContext="#stay">
+ <DetectChar char="&quot;" attribute="String" context="#pop"/>
+ <HlCStringChar attribute="String" context="#stay"/>
+ </context>
+ <context name="Pattern" attribute="Pattern" lineEndContext="#stay">
+ <DetectChar char="/" attribute="Pattern" context="#pop"/>
+ <RegExpr String="\\." attribute="Pattern" context="#stay"/>
+ </context>
+ <context name="Field" attribute="Field" lineEndContext="#stay">
+ <RegExpr String="\$[A-Za-z0-9_]+" attribute="Field" context="#pop"/>
+ </context>
+ </contexts>
+ <itemDatas>
+ <itemData name="Normal" defStyleNum="dsNormal"/>
+ <itemData name="Keyword" defStyleNum="dsKeyword"/>
+ <itemData name="Builtin" defStyleNum="dsDataType"/>
+ <itemData name="Function" defStyleNum="dsKeyword"/>
+ <itemData name="Decimal" defStyleNum="dsDecVal"/>
+ <itemData name="Float" defStyleNum="dsFloat"/>
+ <itemData name="String" defStyleNum="dsString"/>
+ <itemData name="Comment" defStyleNum="dsComment"/>
+ <itemData name="Pattern" defStyleNum="dsString"/>
+ <itemData name="Field" defStyleNum="dsOthers"/>
+ </itemDatas>
+ </highlighting>
+ <general>
+ <comments>
+ <comment name="singleLine" start="#"/>
+ </comments>
+ <keywords casesensitive="1" />
+ </general>
+</language>
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE language SYSTEM "language.dtd">
+<language name="Bash" version="1.08" kateversion="2.2" section="Scripts" extensions="*.sh" mimetype="application/x-shellscript">
+ <highlighting>
+ <list name = "keywords">
+ <item> break </item>
+ <item> case </item>
+ <item> else </item>
+ <item> esac </item>
+ <item> exit </item>
+ <item> export </item>
+ <item> for </item>
+ <item> function </item>
+ <item> in </item>
+ <item> return </item>
+ <item> select </item>
+ <item> then </item>
+ <item> until </item>
+ <item> while </item>
+ <item> . </item>
+ <item> done </item>
+ <item> do </item>
+ <item> elif </item>
+ <item> fi </item>
+ <item> if </item>
+
+ </list>
+ <list name = "commands">
+ <item> cp </item>
+ <item> date </item>
+ <item> echo </item>
+ <item> eval </item>
+ <item> dcop </item>
+ <item> dcopstart </item>
+ <item> dcopfind </item>
+ </list>
+ <contexts>
+ <context name = "Base" attribute = "Normal Text" lineEndContext = "#stay">
+ <RegExpr attribute="Keyword" context="#stay" String="\bdone\b" insensitive="TRUE" endRegion="dodone1"/>
+ <RegExpr attribute="Keyword" context="#stay" String="\bdo\b" insensitive="TRUE" beginRegion="dodone1"/>
+ <RegExpr attribute="Keyword" context="#stay" String="\belif\b" insensitive="TRUE" beginRegion="iffi1" endRegion="iffi1"/>
+ <RegExpr attribute="Keyword" context="#stay" String="\bif\b" insensitive="TRUE" beginRegion="iffi1"/>
+ <RegExpr attribute="Keyword" context="#stay" String="\bfi\b" insensitive="TRUE" endRegion="iffi1"/>
+ <RegExpr attribute="Keyword" context="#stay" String="\bcase\b" insensitive="TRUE" beginRegion="case1"/>
+ <RegExpr attribute="Keyword" context="#stay" String="\besac\b" insensitive="TRUE" endRegion="case1"/>
+ <RegExpr attribute="Keyword" context="#stay" String="^[^()]+\)" insensitive="TRUE" beginRegion="subcase1"/>
+ <StringDetect attribute="Keyword" context="#stay" String=";;" insensitive="TRUE" endRegion="subcase1"/>
+ <DetectChar attribute="Keyword" context="#stay" char="{" beginRegion="func1"/>
+ <DetectChar attribute="Keyword" context="#stay" char="}" endRegion="func1"/>
+
+ <keyword String = "keywords" attribute = "Keyword" context = "#stay"/>
+ <keyword String = "commands" attribute = "Command" context = "#stay"/>
+ <Int attribute = "Decimal" context = "#stay"/>
+ <RegExpr String = "\$[A-Za-z0-9_?{}!]+" attribute = "Parameter" context = "#stay"/>
+ <RangeDetect char = "&quot;" char1 = "&quot;" attribute = "String" context = "#stay"/>
+ <AnyChar String = "|&lt;&gt;=;" attribute = "Operator" context = "#stay"/>
+ <DetectChar char="'" attribute = "String" context = "Single Quote"/>
+ <DetectChar char="`" attribute = "Substitution" context = "Substitution"/>
+ <Detect2Chars attribute = "Normal Text" context = "#stay" char = "\" char1 = "#"/>
+ <RegExpr String= "#.*$" attribute = "Comment" context="#stay"/>
+
+ </context>
+ <context name = "String" attribute = "String" lineEndContext="#stay">
+ <StringDetect String = "\\" attribute = "String" context = "#stay"/>
+ <StringDetect String = "\&quot;" attribute = "String" context = "#stay"/>
+ <DetectChar char = "&quot;" attribute = "String" context = "#pop"/>
+ </context>
+ <context name = "Single Quote" attribute = "String" lineEndContext="#stay">
+ <StringDetect String = "\\" attribute = "String" context = "#stay"/>
+ <StringDetect String = "\'" attribute = "String" context = "#stay"/>
+ <DetectChar char = "'" attribute = "String" context = "#pop"/>
+ </context>
+ <context name = "Substitution" attribute = "Substitution" lineEndContext="#stay">
+ <StringDetect String = "\\" attribute = "String" context = "#stay"/>
+ <StringDetect String = "\`" attribute = "String" context = "#stay"/>
+ <DetectChar char = "`" attribute = "Substitution" context = "#pop"/>
+ </context>
+ <context name = "Parameter" attribute = "Parameter" lineEndContext="#stay">
+ <RegExpr String = "\$[A-Za-z0-9_?]+" attribute = "Parameter" context="#pop"/>
+ </context>
+ </contexts>
+ <itemDatas>
+ <itemData name = "Normal Text" defStyleNum = "dsNormal"/>
+ <itemData name = "Keyword" defStyleNum = "dsKeyword"/>
+ <itemData name = "Decimal" defStyleNum = "dsDecVal"/>
+ <itemData name = "Float" defStyleNum = "dsFloat"/>
+ <itemData name = "Char" defStyleNum = "dsChar"/>
+ <itemData name = "String" defStyleNum = "dsString"/>
+ <itemData name = "Comment" defStyleNum = "dsComment"/>
+ <itemData name = "Substitution" defStyleNum = "dsOthers"/>
+ <itemData name = "Parameter" defStyleNum = "dsOthers"/>
+ <itemData name = "Operator" defStyleNum = "dsOthers"/>
+ <itemData name = "Command" defStyleNum = "dsNormal"/>
+ </itemDatas>
+ </highlighting>
+ <general>
+ <comments>
+ <comment name="singleLine" start="#"/>
+ </comments>
+ <keywords casesensitive="1"/>
+ </general>
+</language>
+
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE language>
+<language name="BibTeX" version="0.1" extensions="*.bib" section="Markup" mimetype="text/x-bib" casesensitive="1">
+ <highlighting>
+ <contexts>
+ <context name="Normal" attribute="Normal Text" lineEndContext="#stay">
+ <DetectChar char="@" attribute="Bib Item" context="Bib Item" />
+ <DetectChar char="%" attribute="Comment" context="Comment"/>
+ </context>
+
+ <context name="Bib Item" attribute="Bib Item" lineEndContext="#stay">
+ <DetectChar char="{" attribute="Normal Text" context="Ref Key" />
+ <DetectChar char="%" attribute="Comment" context="Comment"/>
+ </context>
+
+ <context name="Ref Key" attribute="Ref Key" lineEndContext="#stay">
+ <DetectChar char="," attribute="Normal Text" context="Keyword" />
+ <DetectChar char="%" attribute="Comment" context="Comment"/>
+ </context>
+
+ <context name="Keyword" attribute="Keyword" lineEndContext="#stay">
+ <RegExpr String="\\." attribute="Keyword" context="#stay"/>
+ <DetectChar char="@" attribute="Bib Item" context="Bib Item"/>
+ <DetectChar char="=" attribute="Normal Text" context="#stay"/>
+ <DetectChar char="," attribute="Normal Text" context="#stay"/>
+ <DetectChar char='"' attribute="Value" context="Value"/>
+ <DetectChar char="%" attribute="Comment" context="Comment"/>
+ </context>
+
+ <context name="Value" attribute="Value" lineEndContext="#stay">
+ <Detect2Chars char="\" char1='"' attribute="Value" context="#stay"/>
+ <DetectChar char='"' attribute="Value" context="Keyword"/>
+ </context>
+
+ <context name="Comment" attribute="5" lineEndContext="#pop">
+ </context>
+ </contexts>
+ <itemDatas>
+ <itemData name="Normal Text" defStyleNum="dsNormal"/>
+ <itemData name="Bib Item" defStyleNum="dsNormal" color="#0000ff" selColor="#ffff00" bold="1" italic="0"/>
+ <itemData name="Keyword" defStyleNum="dsNormal" color="#000000" selColor="#dddddd" bold="0" italic="0"/>
+ <itemData name="Value" defStyleNum="dsNormal" color="#aa5500" selColor="#22aaff" bold="0" italic="0"/>
+ <itemData name="Ref Key" defStyleNum="dsNormal" color="#007700" selColor="#00aa00" bold="1" italic="0"/>
+ <itemData name="Comment" defStyleNum="dsNormal" color="#aaaaaa" selColor="#222222" bold="0" italic="0"/>
+ </itemDatas>
+ </highlighting><general>
+ <comments>
+ <comment name="singleLine" start="%" />
+ </comments>
+ </general>
+</language>
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE language SYSTEM "language.dtd">
+<language name="C" version="1.09" kateversion="2.1" section="Sources" extensions="*.c;*.C;*.h" mimetype="text/x-csrc;text/x-c++src;text/x-chdr" priority="5">
+ <highlighting>
+ <list name="keywords">
+ <item> break </item>
+ <item> case </item>
+ <item> continue </item>
+ <item> default </item>
+ <item> do </item>
+ <item> else </item>
+ <item> enum </item>
+ <item> extern </item>
+ <item> for </item>
+ <item> goto </item>
+ <item> if </item>
+ <item> inline </item>
+ <item> return </item>
+ <item> sizeof </item>
+ <item> struct </item>
+ <item> switch </item>
+ <item> typedef </item>
+ <item> union </item>
+ <item> while </item>
+ </list>
+ <list name="types">
+ <item> auto </item>
+ <item> char </item>
+ <item> const </item>
+ <item> double </item>
+ <item> float </item>
+ <item> int </item>
+ <item> long </item>
+ <item> register </item>
+ <item> restrict </item>
+ <item> short </item>
+ <item> signed </item>
+ <item> static </item>
+ <item> unsigned </item>
+ <item> void </item>
+ <item> volatile </item>
+ <item> _Imaginary </item>
+ <item> _Complex </item>
+ <item> _Bool </item>
+ </list>
+ <list name="attention">
+ <item> FIXME </item>
+ <item> TODO </item>
+ <item> ### </item>
+ </list>
+ <contexts>
+ <context attribute="Normal Text" lineEndContext="#stay" name="Normal">
+ <keyword attribute="Keyword" context="#stay" String="keywords"/>
+ <keyword attribute="Data Type" context="#stay" String="types"/>
+ <DetectChar attribute="Symbol" context="#stay" char="{" beginRegion="Brace1" />
+ <DetectChar attribute="Symbol" context="#stay" char="}" endRegion="Brace1" />
+ <Float attribute="Float" context="#stay">
+ <AnyChar String="fF" attribute="Float" context="#stay"/>
+ </Float>
+ <HlCOct attribute="Octal" context="#stay"/>
+ <HlCHex attribute="Hex" context="#stay"/>
+ <Int attribute="Decimal" context="#stay" >
+ <StringDetect attribute="Decimal" context="#stay" String="ULL" insensitive="TRUE"/>
+ <StringDetect attribute="Decimal" context="#stay" String="LUL" insensitive="TRUE"/>
+ <StringDetect attribute="Decimal" context="#stay" String="LLU" insensitive="TRUE"/>
+ <StringDetect attribute="Decimal" context="#stay" String="UL" insensitive="TRUE"/>
+ <StringDetect attribute="Decimal" context="#stay" String="LU" insensitive="TRUE"/>
+ <StringDetect attribute="Decimal" context="#stay" String="LL" insensitive="TRUE"/>
+ <StringDetect attribute="Decimal" context="#stay" String="U" insensitive="TRUE"/>
+ <StringDetect attribute="Decimal" context="#stay" String="L" insensitive="TRUE"/>
+ </Int>
+ <HlCChar attribute="Char" context="#stay"/>
+ <DetectChar attribute="String" context="String" char="&quot;"/>
+ <Detect2Chars attribute="Comment" context="Commentar 1" char="/" char1="/"/>
+ <Detect2Chars attribute="Comment" context="Commentar 2" char="/" char1="*" beginRegion="Comment"/>
+ <StringDetect attribute="Preprocessor" context="Outscoped" String="#if 0" insensitive="FALSE"/>
+ <DetectChar attribute="Preprocessor" context="Preprocessor" char="#"/>
+ <RegExpr attribute="Function" context="#stay" String="\b[_\w][_\w\d]*(?=[\s]*[(])" />
+ <RegExpr attribute="Symbol" context="Member" String="[.]{1,1}" />
+ <AnyChar attribute="Symbol" context="#stay" String=":!%&amp;()+,-/.*&lt;=&gt;?[]|~^&#59;"/>
+ </context>
+ <context attribute="String" lineEndContext="#pop" name="String">
+ <LineContinue attribute="String" context="#stay"/>
+ <HlCStringChar attribute="String Char" context="#stay"/>
+ <DetectChar attribute="String" context="#pop" char="&quot;"/>
+ </context>
+ <context attribute="Normal Text" lineEndContext="#pop" name="Member">
+ <RegExpr attribute="Function" context="#pop" String="\b[_\w][_\w\d]*(?=[\s]*)" />
+ </context>
+ <context attribute="Comment" lineEndContext="#pop" name="Commentar 1">
+ <keyword attribute="Decimal" context="#stay" String="attention" />
+ </context>
+ <context attribute="Comment" lineEndContext="#stay" name="Commentar 2">
+ <Detect2Chars attribute="Comment" context="#pop" char="*" char1="/" endRegion="Comment"/>
+ <keyword attribute="Decimal" context="#stay" String="attention" />
+ </context>
+ <context attribute="Preprocessor" lineEndContext="#pop" name="Preprocessor">
+ <LineContinue attribute="Preprocessor" context="#stay"/>
+ <RegExpr attribute="Preprocessor" context="Define" String="define.*((?=\\))"/>
+ <RegExpr attribute="Preprocessor" context="#stay" String="define.*"/>
+ <RangeDetect attribute="Prep. Lib" context="#stay" char="&quot;" char1="&quot;"/>
+ <RangeDetect attribute="Prep. Lib" context="#stay" char="&lt;" char1="&gt;"/>
+ <Detect2Chars attribute="Comment" context="Commentar/Preprocessor" char="/" char1="*" beginRegion="Comment2"/>
+ </context>
+ <context attribute="Preprocessor" lineEndContext="#pop" name="Define">
+ <LineContinue attribute="Preprocessor" context="#stay"/>
+ </context>
+ <context attribute="Comment" lineEndContext="#stay" name="Commentar/Preprocessor">
+ <Detect2Chars attribute="Comment" context="#pop" char="*" char1="/" endRegion="Comment2" />
+ </context>
+ <context attribute="Normal Text" lineEndContext="#pop" name="Some Context"/>
+ <context attribute="Normal Text" lineEndContext="#pop" name="Some Context2"/>
+ <context attribute="Comment" lineEndContext="#stay" name="Outscoped" >
+ <keyword attribute="Decimal" context="#stay" String="attention" />
+ <RegExpr attribute="Comment" context="Outscoped intern" String="^#if" />
+ <RegExpr attribute="Preprocessor" context="#pop" String="#endif" />
+ <RegExpr attribute="Preprocessor" context="#pop" String="#else" />
+ </context>
+ <context attribute="Comment" lineEndContext="#stay" name="Outscoped intern">
+ <RegExpr attribute="Comment" context="Outscoped intern" String="#if" />
+ <RegExpr attribute="Comment" context="#pop" String="#endif" />
+ </context>
+ </contexts>
+ <itemDatas>
+ <itemData name="Normal Text" defStyleNum="dsNormal"/>
+ <itemData name="Keyword" defStyleNum="dsKeyword"/>
+ <itemData name="Function" defStyleNum="dsKeyword" color="#000080" selColor="#ffffff" bold="0" italic="0"/>
+ <itemData name="Data Type" defStyleNum="dsDataType"/>
+ <itemData name="Decimal" defStyleNum="dsDecVal"/>
+ <itemData name="Octal" defStyleNum="dsBaseN"/>
+ <itemData name="Hex" defStyleNum="dsBaseN"/>
+ <itemData name="Float" defStyleNum="dsFloat"/>
+ <itemData name="Char" defStyleNum="dsChar"/>
+ <itemData name="String" defStyleNum="dsString"/>
+ <itemData name="String Char" defStyleNum="dsChar"/>
+ <itemData name="Comment" defStyleNum="dsComment"/>
+ <itemData name="Symbol" defStyleNum="dsNormal"/>
+ <itemData name="Preprocessor" defStyleNum="dsOthers"/>
+ <itemData name="Prep. Lib" defStyleNum="dsOthers"/> <!--,Qt::darkYellow,Qt::yellow,false,false)); -->
+ </itemDatas>
+ </highlighting>
+ <general>
+ <comments>
+ <comment name="singleLine" start="//" />
+ <comment name="multiLine" start="/*" end="*/" />
+ </comments>
+ <keywords casesensitive="1" />
+ </general>
+</language>
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE language SYSTEM "language.dtd">
+<language name="ChangeLog" version="1.01" kateversion="2.1" section="Other" extensions="ChangeLog" mimetype="">
+ <highlighting>
+ <contexts>
+ <context attribute="Normal Text" lineEndContext="#stay" name="Normal">
+ <RegExpr attribute="Keyword" context="#stay" String="^\d\d\d\d\s*-\s*\d\d\s*-\s*\d\d.*$"/>
+ </context>
+ </contexts>
+ <itemDatas>
+ <itemData name="Normal Text" defStyleNum="dsNormal"/>
+ <itemData name="Keyword" defStyleNum="dsKeyword"/>
+ <itemData name="Identifier" defStyleNum="dsOthers"/>
+ <itemData name="Types" defStyleNum="dsDataType"/>
+ <itemData name="String" defStyleNum="dsString"/>
+ <itemData name="Comment" defStyleNum="dsComment"/>
+ </itemDatas>
+ </highlighting>
+ <general>
+ <keywords casesensitive="1" />
+ </general>
+</language>
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE language SYSTEM "language.dtd">
+<language name="Cisco" version="1.09" kateversion="2.2" section="Scripts"
+ extensions="*.cis" mimetype="text/cisco"
+ author="Raphaël GRAPINET" license="LGPL">
+ <highlighting>
+ <list name = "commands">
+ <item> aaa </item>
+ <item> access-list </item>
+ <item> address </item>
+ <item> alias </item>
+ <item> arp </item>
+ <item> async-bootp </item>
+ <item> banner </item>
+ <item> boot </item>
+ <item> bridge </item>
+ <item> buffers </item>
+ <item> busy-message </item>
+ <item> call-history-mib </item>
+ <item> cdp </item>
+ <item> chat-script </item>
+ <item> class-map </item>
+ <item> clock </item>
+ <item> cns </item>
+ <item> config-register </item>
+ <item> controller </item>
+ <item> crypto </item>
+ <item> default </item>
+ <item> default-value </item>
+ <item> dialer </item>
+ <item> dialer-list </item>
+ <item> dnsix-dmdp </item>
+ <item> dnsix-nat </item>
+ <item> downward-compatible-config </item>
+ <item> enable </item>
+ <item> end </item>
+ <item> exception </item>
+ <item> exit </item>
+ <item> file </item>
+ <item> frame-relay </item>
+ <item> help </item>
+ <item> hostname </item>
+ <item> interface </item>
+ <item> ip </item>
+ <item> isdn </item>
+ <item> isdn-mib </item>
+ <item> kerberos </item>
+ <item> key </item>
+ <item> line </item>
+ <item> logging </item>
+ <item> login-string </item>
+ <item> map-class </item>
+ <item> map-list </item>
+ <item> memory-size </item>
+ <item> menu </item>
+ <item> modemcap </item>
+ <item> multilink </item>
+ <item> netbios </item>
+ <item> no </item>
+ <item> ntp </item>
+ <item> partition </item>
+ <item> policy-map </item>
+ <item> priority-list </item>
+ <item> privilege </item>
+ <item> process-max-time </item>
+ <item> prompt </item>
+ <item> queue-list </item>
+ <item> resume-string </item>
+ <item> rlogin </item>
+ <item> rmon </item>
+ <item> route-map </item>
+ <item> router </item>
+ <item> rtr </item>
+ <item> scheduler </item>
+ <item> service </item>
+ <item> snmp-server </item>
+ <item> sntp </item>
+ <item> stackmaker </item>
+ <item> state-machine </item>
+ <item> subscriber-policy </item>
+ <item> tacacs-server </item>
+ <item> template </item>
+ <item> terminal-queue </item>
+ <item> tftp-server </item>
+ <item> time-range </item>
+ <item> username </item>
+ <item> virtual-profile </item>
+ <item> virtual-template </item>
+ <item> vpdn </item>
+ <item> vpdn-group </item>
+ <item> x25 </item>
+ <item> x29 </item>
+ </list>
+ <list name = "parameters">
+ <item> accounting </item>
+ <item> accounting-list </item>
+ <item> accounting-threshold </item>
+ <item> accounting-transits </item>
+ <item> address-pool </item>
+ <item> as-path </item>
+ <item> audit </item>
+ <item> auth-proxy </item>
+ <item> authentication </item>
+ <item> authorization </item>
+ <item> bgp-community </item>
+ <item> bootp </item>
+ <item> cef </item>
+ <item> classless </item>
+ <item> community-list </item>
+ <item> default-gateway </item>
+ <item> default-network </item>
+ <item> dhcp </item>
+ <item> dhcp-server </item>
+ <item> domain-list </item>
+ <item> domain-lookup </item>
+ <item> domain-name </item>
+ <item> dvmrp </item>
+ <item> exec-callback </item>
+ <item> extcommunity-list </item>
+ <item> finger </item>
+ <item> flow-aggregation </item>
+ <item> flow-cache </item>
+ <item> flow-export </item>
+ <item> forward-protocol </item>
+ <item> ftp </item>
+ <item> gratuitous-arps </item>
+ <item> host </item>
+ <item> host-routing </item>
+ <item> hp-host </item>
+ <item> http </item>
+ <item> icmp </item>
+ <item> inspect </item>
+ <item> local </item>
+ <item> mrm </item>
+ <item> mroute </item>
+ <item> msdp </item>
+ <item> multicast </item>
+ <item> multicast-routing </item>
+ <item> name-server </item>
+ <item> nat </item>
+ <item> new-model </item>
+ <item> ospf </item>
+ <item> password </item>
+ <item> password-encryption </item>
+ <item> pgm </item>
+ <item> pim </item>
+ <item> port-map </item>
+ <item> prefix-list </item>
+ <item> radius </item>
+ <item> rcmd </item>
+ <item> reflexive-list </item>
+ <item> route </item>
+ <item> routing </item>
+ <item> rsvp </item>
+ <item> rtcp </item>
+ <item> sap </item>
+ <item> sdr </item>
+ <item> security </item>
+ <item> source-route </item>
+ <item> subnet-zero </item>
+ <item> tacacs </item>
+ <item> tcp </item>
+ <item> tcp-small-servers </item>
+ <item> telnet </item>
+ <item> tftp </item>
+ <item> timestamps </item>
+ <item> udp-small-servers </item>
+ <item> vrf </item>
+ <item> wccp </item>
+ </list>
+ <list name = "options">
+ <item> accounting </item>
+ <item> accounting-list </item>
+ <item> accounting-threshold </item>
+ <item> accounting-transits </item>
+ <item> address-pool </item>
+ <item> as-path </item>
+ <item> audit </item>
+ <item> auth-proxy </item>
+ <item> authentication </item>
+ <item> authorization </item>
+ <item> bgp-community </item>
+ <item> bootp </item>
+ <item> cef </item>
+ <item> classless </item>
+ <item> community-list </item>
+ <item> default-gateway </item>
+ <item> default-network </item>
+ <item> dhcp </item>
+ <item> dhcp-server </item>
+ <item> domain-list </item>
+ <item> domain-lookup </item>
+ <item> domain-name </item>
+ <item> dvmrp </item>
+ <item> exec-callback </item>
+ <item> extcommunity-list </item>
+ <item> finger </item>
+ <item> flow-aggregation </item>
+ <item> flow-cache </item>
+ <item> flow-export </item>
+ <item> forward-protocol </item>
+ <item> ftp </item>
+ <item> gratuitous-arps </item>
+ <item> host </item>
+ <item> host-routing </item>
+ <item> hp-host </item>
+ <item> http </item>
+ <item> icmp </item>
+ <item> inspect </item>
+ <item> local </item>
+ <item> mrm </item>
+ <item> mroute </item>
+ <item> msdp </item>
+ <item> multicast </item>
+ <item> multicast-routing </item>
+ <item> name-server </item>
+ <item> nat </item>
+ <item> new-model </item>
+ <item> ospf </item>
+ <item> password </item>
+ <item> password-encryption </item>
+ <item> pgm </item>
+ <item> pim </item>
+ <item> port-map </item>
+ <item> prefix-list </item>
+ <item> radius </item>
+ <item> rcmd </item>
+ <item> reflexive-list </item>
+ <item> route </item>
+ <item> routing </item>
+ <item> rsvp </item>
+ <item> rtcp </item>
+ <item> sap </item>
+ <item> sdr </item>
+ <item> security </item>
+ <item> source-route </item>
+ <item> subnet-zero </item>
+ <item> tacacs </item>
+ <item> tcp </item>
+ <item> tcp-small-servers </item>
+ <item> telnet </item>
+ <item> tftp </item>
+ <item> timestamps </item>
+ <item> udp-small-servers </item>
+ <item> vrf </item>
+ <item> wccp </item>
+ </list>
+ <contexts>
+ <context name = "Base" attribute = "Normal Text" lineEndContext = "#stay">
+ <RegExpr attribute="Keyword" context="#stay" String="\bdone\b" insensitive="TRUE" endRegion="dodone1"/>
+ <RegExpr attribute="Keyword" context="#stay" String="\bdo\b" insensitive="TRUE" beginRegion="dodone1"/>
+ <RegExpr attribute="Keyword" context="#stay" String="\belif\b" insensitive="TRUE" beginRegion="iffi1" endRegion="iffi1"/>
+ <RegExpr attribute="Keyword" context="#stay" String="\bif\b" insensitive="TRUE" beginRegion="iffi1"/>
+ <RegExpr attribute="Keyword" context="#stay" String="\bfi\b" insensitive="TRUE" endRegion="iffi1"/>
+ <RegExpr attribute="Keyword" context="#stay" String="\bcase\b" insensitive="TRUE" beginRegion="case1"/>
+ <RegExpr attribute="Keyword" context="#stay" String="\besac\b" insensitive="TRUE" endRegion="case1"/>
+ <RegExpr attribute="Keyword" context="#stay" String="^[^()]+\)" insensitive="TRUE" beginRegion="subcase1"/>
+ <StringDetect attribute="Keyword" context="#stay" String=";;" insensitive="TRUE" endRegion="subcase1"/>
+ <DetectChar attribute="Keyword" context="#stay" char="{" beginRegion="func1"/>
+ <DetectChar attribute="Keyword" context="#stay" char="}" endRegion="func1"/>
+
+ <keyword String = "commands" attribute = "Command" context = "#stay"/>
+ <keyword String = "parameters" attribute = "Parameter" context = "#stay"/>
+ <keyword String = "options" attribute = "Keyword" context = "#stay"/>
+ <Int attribute = "Decimal" context = "#stay"/>
+ <RegExpr String = "\$[A-Za-z0-9_?{}!]+" attribute = "Parameter" context = "#stay"/>
+ <RangeDetect char = "&quot;" char1 = "&quot;" attribute = "String" context = "#stay"/>
+ <AnyChar String = "|&lt;&gt;=;" attribute = "Operator" context = "#stay"/>
+ <DetectChar char="'" attribute = "String" context = "Single Quote"/>
+ <DetectChar char="`" attribute = "Substitution" context = "Substitution"/>
+ <Detect2Chars attribute = "Normal Text" context = "#stay" char = "\" char1 = "#"/>
+ <RegExpr String= "#.*$" attribute = "Comment" context="#stay"/>
+
+ </context>
+ <context name = "String" attribute = "String" lineEndContext="#stay">
+ <StringDetect String = "\\" attribute = "String" context = "#stay"/>
+ <StringDetect String = "\&quot;" attribute = "String" context = "#stay"/>
+ <DetectChar char = "&quot;" attribute = "String" context = "#pop"/>
+ </context>
+ <context name = "Single Quote" attribute = "String" lineEndContext="#stay">
+ <StringDetect String = "\\" attribute = "String" context = "#stay"/>
+ <StringDetect String = "\'" attribute = "String" context = "#stay"/>
+ <DetectChar char = "'" attribute = "String" context = "#pop"/>
+ </context>
+ <context name = "Substitution" attribute = "Substitution" lineEndContext="#stay">
+ <StringDetect String = "\\" attribute = "String" context = "#stay"/>
+ <StringDetect String = "\`" attribute = "String" context = "#stay"/>
+ <DetectChar char = "`" attribute = "Substitution" context = "#pop"/>
+ </context>
+ <context name = "Parameter" attribute = "Parameter" lineEndContext="#stay">
+ <RegExpr String = "\$[A-Za-z0-9_?]+" attribute = "Parameter" context="#pop"/>
+ </context>
+ </contexts>
+ <itemDatas>
+ <itemData name = "Normal Text" defStyleNum = "dsNormal"/>
+ <itemData name = "Keyword" defStyleNum = "dsKeyword"/>
+ <itemData name = "Decimal" defStyleNum = "dsDecVal"/>
+ <itemData name = "Float" defStyleNum = "dsFloat"/>
+ <itemData name = "Char" defStyleNum = "dsChar"/>
+ <itemData name = "String" defStyleNum = "dsString"/>
+ <itemData name = "Comment" defStyleNum = "dsComment"/>
+ <itemData name = "Substitution" defStyleNum = "dsOthers"/>
+ <itemData name = "Parameter" defStyleNum = "dsOthers"/>
+ <itemData name = "Operator" defStyleNum = "dsOthers"/>
+ <itemData name = "Command" defStyleNum = "dsNormal"/>
+ </itemDatas>
+ </highlighting>
+ <general>
+ <comments>
+ <comment name="singleLine" start="!"/>
+ </comments>
+ <keywords casesensitive="0"/>
+ </general>
+</language>
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE language SYSTEM "language.dtd">
+<language name="ColdFusion" version="1.03" kateversion="2.1" section="Markup" extensions="*.cfm;*.cfc;*.cfml;*.dbm" mimetype="text/x-coldfusion">
+
+ <highlighting>
+
+ <list name="Script Keywords">
+
+ <item> if </item>
+ <item> else </item>
+ <item> for </item>
+ <item> in </item>
+ <item> while </item>
+ <item> do </item>
+ <item> continue </item>
+ <item> break </item>
+ <item> with </item>
+ <item> try </item>
+ <item> catch </item>
+ <item> switch </item>
+ <item> case </item>
+ <item> new </item>
+ <item> var </item>
+ <item> function </item>
+ <item> return </item>
+ <item> this </item>
+ <item> delete </item>
+ <item> true </item>
+ <item> false </item>
+ <item> void </item>
+ <item> throw </item>
+ <item> typeof </item>
+ <item> const </item>
+ <item> default </item>
+
+ </list>
+
+ <list name="Script Objects">
+
+ <item> Anchor </item>
+ <item> Applet </item>
+ <item> Area </item>
+ <item> Array </item>
+ <item> Boolean </item>
+ <item> Button </item>
+ <item> Checkbox </item>
+ <item> Date </item>
+ <item> Document </item>
+ <item> Event </item>
+ <item> FileUpload </item>
+ <item> Form </item>
+ <item> Frame </item>
+ <item> Function </item>
+ <item> Hidden </item>
+ <item> History </item>
+ <item> Image </item>
+ <item> Layer </item>
+ <item> Linke </item>
+ <item> Location </item>
+ <item> Math </item>
+ <item> Navigator </item>
+ <item> Number </item>
+ <item> Object </item>
+ <item> Option </item>
+ <item> Password </item>
+ <item> Radio </item>
+ <item> RegExp </item>
+ <item> Reset </item>
+ <item> Screen </item>
+ <item> Select </item>
+ <item> String </item>
+ <item> Submit </item>
+ <item> Text </item>
+ <item> Textarea </item>
+ <item> Window </item>
+
+ </list>
+
+ <list name="Script Methods">
+
+ <item> abs </item>
+ <item> acos </item>
+ <item> alert </item>
+ <item> anchor </item>
+ <item> apply </item>
+ <item> asin </item>
+ <item> atan </item>
+ <item> atan2 </item>
+ <item> back </item>
+ <item> blur </item>
+ <item> call </item>
+ <item> captureEvents </item>
+ <item> ceil </item>
+ <item> charAt </item>
+ <item> charCodeAt </item>
+ <item> clearInterval </item>
+ <item> clearTimeout </item>
+ <item> click </item>
+ <item> close </item>
+ <item> compile </item>
+ <item> concat </item>
+ <item> confirm </item>
+ <item> cos </item>
+ <item> disableExternalCapture </item>
+ <item> enableExternalCapture </item>
+ <item> eval </item>
+ <item> exec </item>
+ <item> exp </item>
+ <item> find </item>
+ <item> floor </item>
+ <item> focus </item>
+ <item> forward </item>
+ <item> fromCharCode </item>
+ <item> getDate </item>
+ <item> getDay </item>
+ <item> getFullYear </item>
+ <item> getHours </item>
+ <item> getMilliseconds </item>
+ <item> getMinutes </item>
+ <item> getMonth </item>
+ <item> getSeconds </item>
+ <item> getSelection </item>
+ <item> getTime </item>
+ <item> getTimezoneOffset </item>
+ <item> getUTCDate </item>
+ <item> getUTCDay </item>
+ <item> getUTCFullYear </item>
+ <item> getUTCHours </item>
+ <item> getUTCMilliseconds </item>
+ <item> getUTCMinutes </item>
+ <item> getUTCMonth </item>
+ <item> getUTCSeconds </item>
+ <item> go </item>
+ <item> handleEvent </item>
+ <item> home </item>
+ <item> indexOf </item>
+ <item> javaEnabled </item>
+ <item> join </item>
+ <item> lastIndexOf </item>
+ <item> link </item>
+ <item> load </item>
+ <item> log </item>
+ <item> match </item>
+ <item> max </item>
+ <item> min </item>
+ <item> moveAbove </item>
+ <item> moveBelow </item>
+ <item> moveBy </item>
+ <item> moveTo </item>
+ <item> moveToAbsolute </item>
+ <item> open </item>
+ <item> parse </item>
+ <item> plugins.refresh </item>
+ <item> pop </item>
+ <item> pow </item>
+ <item> preference </item>
+ <item> print </item>
+ <item> prompt </item>
+ <item> push </item>
+ <item> random </item>
+ <item> releaseEvents </item>
+ <item> reload </item>
+ <item> replace </item>
+ <item> reset </item>
+ <item> resizeBy </item>
+ <item> resizeTo </item>
+ <item> reverse </item>
+ <item> round </item>
+ <item> routeEvent </item>
+ <item> scrollBy </item>
+ <item> scrollTo </item>
+ <item> search </item>
+ <item> select </item>
+ <item> setDate </item>
+ <item> setFullYear </item>
+ <item> setHours </item>
+ <item> setInterval </item>
+ <item> setMilliseconds </item>
+ <item> setMinutes </item>
+ <item> setMonth </item>
+ <item> setSeconds </item>
+ <item> setTime </item>
+ <item> setTimeout </item>
+ <item> setUTCDate </item>
+ <item> setUTCFullYear </item>
+ <item> setUTCHours </item>
+ <item> setUTCMilliseconds </item>
+ <item> setUTCMinutes </item>
+ <item> setUTCMonth </item>
+ <item> setUTCSeconds </item>
+ <item> shift </item>
+ <item> sin </item>
+ <item> slice </item>
+ <item> sort </item>
+ <item> splice </item>
+ <item> split </item>
+ <item> sqrt </item>
+ <item> stop </item>
+ <item> String formatting </item>
+ <item> submit </item>
+ <item> substr </item>
+ <item> substring </item>
+ <item> taintEnabled </item>
+ <item> tan </item>
+ <item> test </item>
+ <item> toLocaleString </item>
+ <item> toLowerCase </item>
+ <item> toSource </item>
+ <item> toString </item>
+ <item> toUpperCase </item>
+ <item> toUTCString </item>
+ <item> unshift </item>
+ <item> unwatch </item>
+ <item> UTC </item>
+ <item> valueOf </item>
+ <item> watch </item>
+ <item> write </item>
+ <item> writeln </item>
+
+ </list>
+
+ <list name="CFSCRIPT Keywords">
+
+ <item> break </item>
+ <item> case </item>
+ <item> catch </item>
+ <item> continue </item>
+ <item> default </item>
+ <item> do </item>
+ <item> else </item>
+ <item> for </item>
+ <item> function </item>
+ <item> if </item>
+ <item> in </item>
+ <item> return </item>
+ <item> switch </item>
+ <item> try </item>
+ <item> var </item>
+ <item> while </item>
+
+ </list>
+
+ <list name="CFSCRIPT Functions">
+
+ <item> Abs </item>
+ <item> ACos </item>
+ <item> ArrayAppend </item>
+ <item> ArrayAvg </item>
+ <item> ArrayClear </item>
+ <item> ArrayDeleteAt </item>
+ <item> ArrayInsertAt </item>
+ <item> ArrayIsEmpty </item>
+ <item> ArrayLen </item>
+ <item> ArrayMax </item>
+ <item> ArrayMin </item>
+ <item> ArrayNew </item>
+ <item> ArrayPrepend </item>
+ <item> ArrayResize </item>
+ <item> ArraySet </item>
+ <item> ArraySort </item>
+ <item> ArraySum </item>
+ <item> ArraySwap </item>
+ <item> ArrayToList </item>
+ <item> Asc </item>
+ <item> ASin </item>
+ <item> Atn </item>
+ <item> BitAnd </item>
+ <item> BitMaskClear </item>
+ <item> BitMaskRead </item>
+ <item> BitMaskSet </item>
+ <item> BitNot </item>
+ <item> BitOr </item>
+ <item> BitSHLN </item>
+ <item> BitSHRN </item>
+ <item> BitXor </item>
+ <item> Ceiling </item>
+ <item> Chr </item>
+ <item> CJustify </item>
+ <item> Compare </item>
+ <item> CompareNoCase </item>
+ <item> Cos </item>
+ <item> CreateDate </item>
+ <item> CreateDateTime </item>
+ <item> CreateObject </item>
+ <item> CreateODBCDate </item>
+ <item> CreateODBCDateTime </item>
+ <item> CreateODBCTime </item>
+ <item> CreateTime </item>
+ <item> CreateTimeSpan </item>
+ <item> CreateUUID </item>
+ <item> DateAdd </item>
+ <item> DateCompare </item>
+ <item> DateConvert </item>
+ <item> DateDiff </item>
+ <item> DateFormat </item>
+ <item> DatePart </item>
+ <item> Day </item>
+ <item> DayOfWeek </item>
+ <item> DayOfWeekAsString </item>
+ <item> DayOfYear </item>
+ <item> DaysInMonth </item>
+ <item> DaysInYear </item>
+ <item> DE </item>
+ <item> DecimalFormat </item>
+ <item> DecrementValue </item>
+ <item> Decrypt </item>
+ <item> DeleteClientVariable </item>
+ <item> DirectoryExists </item>
+ <item> DollarFormat </item>
+ <item> Duplicate </item>
+ <item> Encrypt </item>
+ <item> Evaluate </item>
+ <item> Exp </item>
+ <item> ExpandPath </item>
+ <item> FileExists </item>
+ <item> Find </item>
+ <item> FindNoCase </item>
+ <item> FindOneOf </item>
+ <item> FirstDayOfMonth </item>
+ <item> Fix </item>
+ <item> FormatBaseN </item>
+ <item> GetAuthUser </item>
+ <item> GetBaseTagData </item>
+ <item> GetBaseTagList </item>
+ <item> GetBaseTemplatePath </item>
+ <item> GetClientVariablesList </item>
+ <item> GetCurrentTemplatePath </item>
+ <item> GetDirectoryFromPath </item>
+ <item> GetException </item>
+ <item> GetFileFromPath </item>
+ <item> GetFunctionList </item>
+ <item> GetHttpRequestData </item>
+ <item> GetHttpTimeString </item>
+ <item> GetK2ServerDocCount </item>
+ <item> GetK2ServerDocCountLimit </item>
+ <item> GetLocale </item>
+ <item> GetMetaData </item>
+ <item> GetMetricData </item>
+ <item> GetPageContext </item>
+ <item> GetProfileSections </item>
+ <item> GetProfileString </item>
+ <item> GetServiceSettings </item>
+ <item> GetTempDirectory </item>
+ <item> GetTempFile </item>
+ <item> GetTemplatePath </item>
+ <item> GetTickCount </item>
+ <item> GetTimeZoneInfo </item>
+ <item> GetToken </item>
+ <item> Hash </item>
+ <item> Hour </item>
+ <item> HTMLCodeFormat </item>
+ <item> HTMLEditFormat </item>
+ <item> IIf </item>
+ <item> IncrementValue </item>
+ <item> InputBaseN </item>
+ <item> Insert </item>
+ <item> Int </item>
+ <item> IsArray </item>
+ <item> IsBinary </item>
+ <item> IsBoolean </item>
+ <item> IsCustomFunction </item>
+ <item> IsDate </item>
+ <item> IsDebugMode </item>
+ <item> IsDefined </item>
+ <item> IsK2ServerABroker </item>
+ <item> IsK2ServerDocCountExceeded </item>
+ <item> IsK2ServerOnline </item>
+ <item> IsLeapYear </item>
+ <item> IsNumeric </item>
+ <item> IsNumericDate </item>
+ <item> IsObject </item>
+ <item> IsQuery </item>
+ <item> IsSimpleValue </item>
+ <item> IsStruct </item>
+ <item> IsUserInRole </item>
+ <item> IsWDDX </item>
+ <item> IsXmlDoc </item>
+ <item> IsXmlElement </item>
+ <item> IsXmlRoot </item>
+ <item> JavaCast </item>
+ <item> JSStringFormat </item>
+ <item> LCase </item>
+ <item> Left </item>
+ <item> Len </item>
+ <item> ListAppend </item>
+ <item> ListChangeDelims </item>
+ <item> ListContains </item>
+ <item> ListContainsNoCase </item>
+ <item> ListDeleteAt </item>
+ <item> ListFind </item>
+ <item> ListFindNoCase </item>
+ <item> ListFirst </item>
+ <item> ListGetAt </item>
+ <item> ListInsertAt </item>
+ <item> ListLast </item>
+ <item> ListLen </item>
+ <item> ListPrepend </item>
+ <item> ListQualify </item>
+ <item> ListRest </item>
+ <item> ListSetAt </item>
+ <item> ListSort </item>
+ <item> ListToArray </item>
+ <item> ListValueCount </item>
+ <item> ListValueCountNoCase </item>
+ <item> LJustify </item>
+ <item> Log </item>
+ <item> Log10 </item>
+ <item> LSCurrencyFormat </item>
+ <item> LSDateFormat </item>
+ <item> LSEuroCurrencyFormat </item>
+ <item> LSIsCurrency </item>
+ <item> LSIsDate </item>
+ <item> LSIsNumeric </item>
+ <item> LSNumberFormat </item>
+ <item> LSParseCurrency </item>
+ <item> LSParseDateTime </item>
+ <item> LSParseEuroCurrency </item>
+ <item> LSParseNumber </item>
+ <item> LSTimeFormat </item>
+ <item> LTrim </item>
+ <item> Max </item>
+ <item> Mid </item>
+ <item> Min </item>
+ <item> Minute </item>
+ <item> Month </item>
+ <item> MonthAsString </item>
+ <item> Now </item>
+ <item> NumberFormat </item>
+ <item> ParagraphFormat </item>
+ <item> ParameterExists </item>
+ <item> ParseDateTime </item>
+ <item> Pi </item>
+ <item> PreserveSingleQuotes </item>
+ <item> Quarter </item>
+ <item> QueryAddColumn </item>
+ <item> QueryAddRow </item>
+ <item> QueryNew </item>
+ <item> QuerySetCell </item>
+ <item> QuotedValueList </item>
+ <item> Rand </item>
+ <item> Randomize </item>
+ <item> RandRange </item>
+ <item> REFind </item>
+ <item> REFindNoCase </item>
+ <item> RemoveChars </item>
+ <item> RepeatString </item>
+ <item> Replace </item>
+ <item> ReplaceList </item>
+ <item> ReplaceNoCase </item>
+ <item> REReplace </item>
+ <item> REReplaceNoCase </item>
+ <item> Reverse </item>
+ <item> Right </item>
+ <item> RJustify </item>
+ <item> Round </item>
+ <item> RTrim </item>
+ <item> Second </item>
+ <item> SetEncoding </item>
+ <item> SetLocale </item>
+ <item> SetProfileString </item>
+ <item> SetVariable </item>
+ <item> Sgn </item>
+ <item> Sin </item>
+ <item> SpanExcluding </item>
+ <item> SpanIncluding </item>
+ <item> Sqr </item>
+ <item> StripCR </item>
+ <item> StructAppend </item>
+ <item> StructClear </item>
+ <item> StructCopy </item>
+ <item> StructCount </item>
+ <item> StructDelete </item>
+ <item> StructFind </item>
+ <item> StructFindKey </item>
+ <item> StructFindValue </item>
+ <item> StructGet </item>
+ <item> StructInsert </item>
+ <item> StructIsEmpty </item>
+ <item> StructKeyArray </item>
+ <item> StructKeyExists </item>
+ <item> StructKeyList </item>
+ <item> StructNew </item>
+ <item> StructSort </item>
+ <item> StructUpdate </item>
+ <item> Tan </item>
+ <item> TimeFormat </item>
+ <item> ToBase64 </item>
+ <item> ToBinary </item>
+ <item> ToString </item>
+ <item> Trim </item>
+ <item> UCase </item>
+ <item> URLDecode </item>
+ <item> URLEncodedFormat </item>
+ <item> URLSessionFormat </item>
+ <item> Val </item>
+ <item> ValueList </item>
+ <item> Week </item>
+ <item> WriteOutput </item>
+ <item> XmlChildPos </item>
+ <item> XmlElemNew </item>
+ <item> XmlFormat </item>
+ <item> XmlNew </item>
+ <item> XmlParse </item>
+ <item> XmlSearch </item>
+ <item> XmlTransform </item>
+ <item> Year </item>
+ <item> YesNoFormat </item>
+
+ </list>
+
+ <contexts>
+
+ <context attribute="Normal Text" lineEndContext="#stay" name="Normal Text">
+ <StringDetect attribute="CF Comment" context="ctxCF Comment" String="&lt;!---" />
+ <StringDetect attribute="HTML Comment" context="ctxHTML Comment" String="&lt;!--" />
+ <RegExpr attribute="Script Tags" context="ctxCFSCRIPT Tag" String="&lt;[cC][fF][sS][cC][rR][iI][pP][tT]" />
+ <RegExpr attribute="Script Tags" context="ctxSCRIPT Tag" String="&lt;[sS][cC][rR][iI][pP][tT]" />
+ <RegExpr attribute="Style Tags" context="ctxSTYLE Tag" String="&lt;[sS][tT][yY][lL][eE]" />
+ <DetectChar attribute="HTML Entities" context="ctxHTML Entities" char="&amp;" />
+ <RegExpr attribute="Custom Tags" context="ctxCustom Tag" String="&lt;\/?[cC][fF]_" />
+ <RegExpr attribute="CFX Tags" context="ctxCFX Tag" String="&lt;\/?[cC][fF][xX]_" />
+ <RegExpr attribute="CF Tags" context="ctxCF Tag" String="&lt;\/?[cC][fF]" />
+ <RegExpr attribute="Table Tags" context="ctxTable Tag" String="&lt;\/?([tT][aAhHbBfFrRdD])|([cC][aA][pP][tT])" />
+ <RegExpr attribute="Anchor Tags" context="ctxAnchor Tag" String="&lt;\/?[aA] " />
+ <RegExpr attribute="Image Tags" context="ctxImage Tag" String="&lt;\/?[iI][mM][gG] " />
+ <RegExpr attribute="Tags" context="ctxTag" String="&lt;!?\/?[a-zA-Z0-9_]+" />
+ </context>
+
+
+
+ <context name="ctxCFSCRIPT Tag" attribute="Script Tags" lineEndContext="#stay">
+ <DetectChar attribute="Script Tags" context="ctxCFSCRIPT Block" char="&gt;" />
+ <DetectChar attribute="Normal Text" context="#stay" char="=" />
+ <RegExpr attribute="Attribute Values" context="#stay" String="&quot;[^&quot;]*&quot;" />
+ <RegExpr attribute="Attribute Values" context="#stay" String="'[^']*'" />
+ </context>
+
+ <context name="ctxSCRIPT Tag" attribute="Script Tags" lineEndContext="#stay">
+ <DetectChar attribute="Script Tags" context="ctxSCRIPT Block" char="&gt;" />
+ <DetectChar attribute="Normal Text" context="#stay" char="=" />
+ <RegExpr attribute="Attribute Values" context="#stay" String="&quot;[^&quot;]*&quot;" />
+ <RegExpr attribute="Attribute Values" context="#stay" String="'[^']*'" />
+ </context>
+
+ <context name="ctxSTYLE Tag" attribute="Style Tags" lineEndContext="#stay">
+ <DetectChar attribute="Style Tags" context="ctxSTYLE Block" char="&gt;" />
+ <DetectChar attribute="Normal Text" context="#stay" char="=" />
+ <RegExpr attribute="Attribute Values" context="#stay" String="&quot;[^&quot;]*&quot;" />
+ <RegExpr attribute="Attribute Values" context="#stay" String="'[^']*'" />
+ </context>
+
+
+
+ <context name="ctxTag" attribute="Tags" lineEndContext="#stay">
+ <DetectChar attribute="Tags" context="#pop" char="&gt;" />
+ <DetectChar attribute="Normal Text" context="#stay" char="=" />
+ <RegExpr attribute="Attribute Values" context="#stay" String="&quot;[^&quot;]*&quot;" />
+ <RegExpr attribute="Attribute Values" context="#stay" String="'[^']*'" />
+ </context>
+
+ <context name="ctxTable Tag" attribute="Table Tags" lineEndContext="#stay">
+ <DetectChar attribute="Table Tags" context="#pop" char="&gt;" />
+ <DetectChar attribute="Normal Text" context="#stay" char="=" />
+ <RegExpr attribute="Attribute Values" context="#stay" String="&quot;[^&quot;]*&quot;" />
+ <RegExpr attribute="Attribute Values" context="#stay" String="'[^']*'" />
+ </context>
+
+ <context name="ctxAnchor Tag" attribute="Anchor Tags" lineEndContext="#stay">
+ <DetectChar attribute="Anchor Tags" context="#pop" char="&gt;" />
+ <DetectChar attribute="Normal Text" context="#stay" char="=" />
+ <RegExpr attribute="Attribute Values" context="#stay" String="&quot;[^&quot;]*&quot;" />
+ <RegExpr attribute="Attribute Values" context="#stay" String="'[^']*'" />
+ </context>
+
+ <context name="ctxImage Tag" attribute="Image Tags" lineEndContext="#stay">
+ <DetectChar attribute="Image Tags" context="#pop" char="&gt;" />
+ <DetectChar attribute="Normal Text" context="#stay" char="=" />
+ <RegExpr attribute="Attribute Values" context="#stay" String="&quot;[^&quot;]*&quot;" />
+ <RegExpr attribute="Attribute Values" context="#stay" String="'[^']*'" />
+ </context>
+
+ <context name="ctxCF Tag" attribute="CF Tags" lineEndContext="#stay">
+ <DetectChar attribute="CF Tags" context="#pop" char="&gt;" />
+ <DetectChar attribute="Normal Text" context="#stay" char="=" />
+ <RegExpr attribute="Attribute Values" context="#stay" String="&quot;[^&quot;]*&quot;" />
+ <RegExpr attribute="Attribute Values" context="#stay" String="'[^']*'" />
+ </context>
+
+ <context name="ctxCustom Tag" attribute="Custom Tags" lineEndContext="#stay">
+ <DetectChar attribute="Custom Tags" context="#pop" char="&gt;" />
+ <DetectChar attribute="Normal Text" context="#stay" char="=" />
+ <RegExpr attribute="Attribute Values" context="#stay" String="&quot;[^&quot;]*&quot;" />
+ <RegExpr attribute="Attribute Values" context="#stay" String="'[^']*'" />
+ </context>
+
+ <context name="ctxCFX Tag" attribute="CFX Tags" lineEndContext="#stay">
+ <DetectChar attribute="CFX Tags" context="#pop" char="&gt;" />
+ <DetectChar attribute="Normal Text" context="#stay" char="=" />
+ <RegExpr attribute="Attribute Values" context="#stay" String="&quot;[^&quot;]*&quot;" />
+ <RegExpr attribute="Attribute Values" context="#stay" String="'[^']*'" />
+ </context>
+
+
+
+ <context name="ctxHTML Comment" attribute="HTML Comment" lineEndContext="#stay">
+ <StringDetect attribute="CF Comment" context="ctxCF Comment" String="&lt;!---" />
+ <StringDetect attribute="HTML Comment" context="#pop" String="--&gt;" />
+ </context>
+
+ <context name="ctxCF Comment" attribute="CF Comment" lineEndContext="#stay">
+ <StringDetect attribute="CF Comment" context="#pop" String="---&gt;" />
+ </context>
+
+ <context name="ctxC Style Comment" attribute="Script Comment" lineEndContext="#stay">
+ <Detect2Chars attribute="Script Comment" context="#pop" char="*" char1="/" />
+ </context>
+
+ <context name="ctxOne Line Comment" attribute="Script Comment" lineEndContext="#pop" />
+
+ <context name="ctxHTML Entities" attribute="HTML Entities" lineEndContext="#pop">
+ <DetectChar attribute="HTML Entities" context="#pop" char=";" />
+ </context>
+
+
+
+ <context name="ctxCFSCRIPT Block" attribute="Normal Text" lineEndContext="#stay">
+ <Detect2Chars attribute="Script Comment" context="ctxC Style Comment" char="/" char1="*" />
+ <Detect2Chars attribute="Script Comment" context="ctxOne Line Comment" char="/" char1="/" />
+ <RegExpr attribute="Script Strings" context="#stay" String="&quot;[^&quot;]*&quot;" />
+ <RegExpr attribute="Script Strings" context="#stay" String="'[^']*'" />
+ <Int attribute="Script Numbers" context="#stay" />
+ <Float attribute="Script Numbers" context="#stay" />
+ <AnyChar attribute="Script Operators" context="#stay" String="[()[\]=+-*/]+" />
+ <AnyChar attribute="Brackets" context="#stay" String="{}" />
+
+ <keyword attribute="Script Keywords" context="#stay" String="CFSCRIPT Keywords" />
+ <keyword attribute="Script Functions" context="#stay" String="CFSCRIPT Functions" />
+ <RegExpr attribute="Script Tags" context="#pop#pop" String="&lt;/[cC][fF][sS][cC][rR][iI][pP][tT]&gt;" />
+ </context>
+
+
+
+ <context name="ctxSCRIPT Block" attribute="Normal Text" lineEndContext="#stay">
+ <Detect2Chars attribute="Script Comment" context="ctxC Style Comment" char="/" char1="*" />
+ <Detect2Chars attribute="Script Comment" context="ctxOne Line Comment" char="/" char1="/" />
+ <RegExpr attribute="Script Strings" context="#stay" String="&quot;[^&quot;]*&quot;" />
+ <RegExpr attribute="Script Strings" context="#stay" String="'[^']*'" />
+ <Int attribute="Script Numbers" context="#stay" />
+ <Float attribute="Script Numbers" context="#stay" />
+ <AnyChar attribute="Script Operators" context="#stay" String="[()[\]=+-*/]+" />
+ <AnyChar attribute="Brackets" context="#stay" String="{}" />
+
+ <keyword attribute="Script Keywords" context="#stay" String="Script Keywords" />
+ <keyword attribute="Script Objects" context="#stay" String="Script Objects" />
+ <keyword attribute="Script Functions" context="#stay" String="Script Methods" />
+ <RegExpr attribute="Script Tags" context="#pop#pop" String="&lt;/[sS][cC][rR][iI][pP][tT]&gt;" />
+ </context>
+
+
+
+ <context name="ctxSTYLE Block" attribute="Style Selectors" lineEndContext="#stay">
+ <Detect2Chars attribute="Script Comment" context="ctxC Style Comment" char="/" char1="*" />
+ <DetectChar attribute="Brackets" context="ctxStyle Properties" char="{" />
+ <RegExpr attribute="Style Tags" context="#pop#pop" String="&lt;/[sS][tT][yY][lL][eE]&gt;" />
+ </context>
+
+ <context name="ctxStyle Properties" attribute="Style Properties" lineEndContext="#stay">
+ <DetectChar attribute="Brackets" context="#pop" char="}" />
+ <Detect2Chars attribute="Script Comment" context="ctxC Style Comment" char="/" char1="*" />
+ <DetectChar attribute="Normal Text" context="ctxStyle Values" char=":" />
+ </context>
+
+ <context name="ctxStyle Values" attribute="Style Values" lineEndContext="#pop">
+ <DetectChar attribute="Normal Text" context="#pop" char=";" />
+ <DetectChar attribute="Normal Text" context="#stay" char="," />
+ <Int attribute="Numbers" context="#stay" />
+ <Float attribute="Numbers" context="#stay" />
+ <RegExpr attribute="Numbers" context="#stay" String="#([0-9a-fA-F]{3})|([0-9a-fA-F]{6})" />
+ <RegExpr attribute="Attribute Values" context="#stay" String="&quot;[^&quot;]*&quot;" />
+ <RegExpr attribute="Attribute Values" context="#stay" String="'[^']*'" />
+ </context>
+
+ </contexts>
+
+ <itemDatas>
+
+ <itemData name="Normal Text" defStyleNum="dsNormal" color="#000000" selColor="#ffffff" bold="0" italic="0" />
+ <itemData name="Tags" defStyleNum="dsNormal" color="#000080" selColor="#ffffff" bold="0" italic="0" />
+ <itemData name="Table Tags" defStyleNum="dsNormal" color="#008080" selColor="#ffffff" bold="0" italic="0" />
+ <itemData name="Script Tags" defStyleNum="dsNormal" color="#800000" selColor="#ffffff" bold="0" italic="0" />
+ <itemData name="Image Tags" defStyleNum="dsNormal" color="#800080" selColor="#ffffff" bold="0" italic="0" />
+ <itemData name="Style Tags" defStyleNum="dsNormal" color="#800080" selColor="#ffffff" bold="0" italic="0" />
+ <itemData name="Anchor Tags" defStyleNum="dsNormal" color="#008000" selColor="#ffffff" bold="0" italic="0" />
+ <itemData name="Attribute Values" defStyleNum="dsNormal" color="#0000ff" selColor="#ffffff" bold="0" italic="0" />
+ <itemData name="HTML Comment" defStyleNum="dsComment" color="#008000" selColor="#999999" bold="0" italic="0" />
+ <itemData name="CF Comment" defStyleNum="dsComment" color="#ff9900" selColor="#999999" bold="0" italic="0" />
+ <itemData name="Script Comment" defStyleNum="dsComment" color="#ff9900" selColor="#999999" bold="0" italic="0" />
+ <itemData name="CF Tags" defStyleNum="dsNormal" color="#800000" selColor="#ffffff" bold="0" italic="0" />
+ <itemData name="Custom Tags" defStyleNum="dsNormal" color="#cc6666" selColor="#ffffff" bold="0" italic="0" />
+ <itemData name="CFX Tags" defStyleNum="dsNormal" color="#008000" selColor="#ffffff" bold="0" italic="0" />
+ <itemData name="Numbers" defStyleNum="dsNormal" color="#0000ff" selColor="#ffffff" bold="0" italic="0" />
+ <itemData name="HTML Entities" defStyleNum="dsNormal" color="#000000" selColor="#ffffff" bold="1" italic="0" />
+ <itemData name="Style Selectors" defStyleNum="dsNormal" color="#ff00ff" selColor="#ffffff" bold="0" italic="0" />
+ <itemData name="Style Properties" defStyleNum="dsNormal" color="#000080" selColor="#ffffff" bold="0" italic="0" />
+ <itemData name="Style Values" defStyleNum="dsNormal" color="#ff0000" selColor="#ffffff" bold="0" italic="0" />
+ <itemData name="Brackets" defStyleNum="dsNormal" color="#000080" selColor="#ffffff" bold="1" italic="0" />
+ <itemData name="Script Numbers" defStyleNum="dsNormal" color="#ff00ff" selColor="#ffffff" bold="0" italic="0" />
+ <itemData name="Script Strings" defStyleNum="dsNormal" color="#008080" selColor="#ffffff" bold="0" italic="0" />
+ <itemData name="Script Operators" defStyleNum="dsNormal" color="#0000ff" selColor="#ffffff" bold="0" italic="0" />
+ <itemData name="Script Keywords" defStyleNum="dsNormal" color="#0000cc" selColor="#ffffff" bold="1" italic="0" />
+ <itemData name="Script Functions" defStyleNum="dsNormal" color="#000080" selColor="#ffffff" bold="1" italic="0" />
+ <itemData name="Script Objects" defStyleNum="dsNormal" color="#0000cc" selColor="#ffffff" bold="1" italic="0" />
+
+ </itemDatas>
+
+ </highlighting>
+
+ <general>
+
+ <comments>
+
+ <comment name="multiLine" start="&lt;!--" end="--&gt;" />
+ <comment name="multiLine" start="/*" end="*/" />
+ <comment name="singleLine" start="//" />
+
+ </comments>
+
+ <keywords casesensitive="0" weakDeliminator="-" />
+
+ </general>
+
+</language>
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE language SYSTEM "language.dtd">
+<!-- Author: Werner Braun <wb@o3-software.de> -->
+<!-- 29.05.2003 wb mailed to the kate-team (cullmann) -->
+<!-- 23.05.2003 wb added *.bro File-Type for GPCP Browser output-->
+<!-- 09.05.2003 wb nested comments allowed (maximum two level nesting) -->
+<!-- 07.04.2003 wb IN Problem solved -->
+<!-- 06.05.2003 wb NEW without ( -->
+<!-- 22.04.2003 wb Minor bugfixes -->
+<!-- 19.04.2003 wb Some more Enhancements -->
+<!-- 18.04.2003 wb Enhancenment for Maior / Minor Comments and Full / ReadOnly Exports, Folds for Procedures, Records, Comments -->
+<!-- 17.04.2003 wb Enhancements for Relation and Operators -->
+<language name="Component-Pascal" version="1.05" kateversion="2.1" section="Sources"
+ extensions="*.cp;*.bro" mimetype="text/x-component-pascal"
+ author="Werner Braun (wb@o3-software.de)" license="">
+ <highlighting>
+ <list name="keywords">
+ <item> BEGIN </item>
+ <item> BY </item>
+ <item> CASE </item>
+ <item> CLOSE </item>
+ <item> CONST </item>
+ <item> DO </item>
+ <item> ELSE </item>
+ <item> ELSIF </item>
+ <item> END </item>
+ <item> FOR </item>
+ <item> IF </item>
+ <item> IMPORT </item>
+ <item> LOOP </item>
+ <item> MODULE </item>
+ <item> NEW </item>
+ <item> OF </item>
+ <item> OUT </item>
+ <item> PROCEDURE </item>
+ <item> REPEAT </item>
+ <item> THEN </item>
+ <item> TO </item>
+ <item> TYPE </item>
+ <item> UNTIL </item>
+ <item> VAR </item>
+ <item> WHILE </item>
+ <item> WITH </item>
+ </list>
+ <list name="exits">
+ <item> ASSERT </item>
+ <item> EXIT </item>
+ <item> HALT </item>
+ <item> RETURN </item>
+ </list>
+ <list name="types">
+ <item> ANYPTR </item>
+ <item> ANYREC </item>
+ <item> ARRAY </item>
+ <item> BOOLEAN </item>
+ <item> SHORTCHAR </item>
+ <item> CHAR </item>
+ <item> BYTE </item>
+ <item> SHORTINT </item>
+ <item> INTEGER </item>
+ <item> LONGINT </item>
+ <item> POINTER </item>
+ <item> RECORD </item>
+ <item> SHORTREAL </item>
+ <item> REAL </item>
+ <item> SET </item>
+ </list>
+ <list name="attributes">
+ <item> ABSTRACT </item>
+ <item> EMPTY </item>
+ <item> EXTENSIBLE </item>
+ <item> LIMITED </item>
+ </list>
+ <list name="builtins">
+ <item> ABS </item>
+ <item> ASH </item>
+ <item> BITS </item>
+ <item> CAP </item>
+ <item> CHR </item>
+ <item> DEC </item>
+ <item> ENTIER </item>
+ <item> EXCL </item>
+ <item> INC </item>
+ <item> INCL </item>
+ <item> LEN </item>
+ <item> LONG </item>
+ <item> MAX </item>
+ <item> MIN </item>
+ <item> ODD </item>
+ <item> ORD </item>
+ <item> SHORT </item>
+ <item> SIZE </item>
+ </list>
+ <list name="specials">
+ <item> FALSE </item>
+ <item> INF </item>
+ <item> NIL </item>
+ <item> TRUE </item>
+ </list>
+ <contexts>
+ <context attribute="Normal Text" lineEndContext="#stay" name="Normal">
+ <StringDetect attribute="CommentMaior" context="Comment1" String="(**" beginRegion="Comment"/>
+ <Detect2Chars attribute="CommentMinor" context="Comment2" char="(" char1="*" beginRegion="Comment"/>
+ <DetectChar attribute="String" context="String1" char="&quot;" />
+ <DetectChar attribute="String" context="String2" char="'" />
+ <RegExpr attribute="Keyword" context="#stay" String="PROCEDURE\s" beginRegion="Proc"/>
+ <RegExpr attribute="Normal Text" context="#stay" String="ABSTRACT;|EMPTY;|END\s*[A-Za-z][A-Za-z0-9_]*\;" endRegion="Proc"/>
+ <RegExpr attribute="Type" context="#stay" String="RECORD" beginRegion="Rec"/>
+ <RegExpr attribute="Keyword" context="#stay" String="END" endRegion="Rec"/>
+ <StringDetect attribute="MemAlloc" context="#stay" String="NEW"/>
+ <keyword attribute="Keyword" context="#stay" String="keywords"/>
+ <keyword attribute="Exit" context="#stay" String="exits"/>
+ <keyword attribute="Type" context="#stay" String="types"/>
+ <keyword attribute="Attribute" context="#stay" String="attributes"/>
+ <keyword attribute="Builtin" context="#stay" String="builtins"/>
+ <keyword attribute="SpecialValues" context="#stay" String="specials"/>
+ <RegExpr attribute="Integer" context="#stay" String="\s[\+|\-]{0,1}[0-9]([0-9]*|[0-9A-F]*(H|L))"/>
+ <Float attribute="Float" context="#stay"/>
+ <RegExpr attribute="Char" context="#stay" String="\s[0-9][0-9A-F]*X"/>
+ <RegExpr attribute="ExportFull" context="#stay" String="[A-Za-z][A-Za-z0-9_]*\*"/>
+ <RegExpr attribute="ExportReadOnly" context="#stay" String="[A-Za-z][A-Za-z0-9_]*\-"/>
+ <RegExpr attribute="Relation" context="#stay" String="\s(=|#|&lt;|&lt;=|&gt;|&gt;=|IN\s|IS)"/>
+ <RegExpr attribute="Operator" context="#stay" String="\s(\+|\-|OR|\*|/|DIV|MOD|\&amp;)"/>
+ </context>
+ <context attribute="CommentMaior" lineEndContext="#stay" name="Comment1">
+ <Detect2Chars attribute="CommentMaior" context="#pop" char="*" char1=")" endRegion="Comment"/>
+ <Detect2Chars attribute="CommentMinor" context="CommentN" char="(" char1="*"/>
+ </context>
+ <context attribute="CommentMinor" lineEndContext="#stay" name="Comment2">
+ <Detect2Chars attribute="CommentMinor" context="#pop" char="*" char1=")" endRegion="Comment"/>
+ <Detect2Chars attribute="CommentMinor" context="CommentN" char="(" char1="*"/>
+ </context>
+ <context attribute="CommentMinor" lineEndContext="#stay" name="CommentN">
+ <Detect2Chars attribute="CommentMinor" context="#pop" char="*" char1=")"/>
+ <Detect2Chars attribute="CommentMinor" context="CommentN2" char="(" char1="*"/>
+ </context>
+ <context attribute="CommentMinor" lineEndContext="#stay" name="CommentN2">
+ <Detect2Chars attribute="CommentMinor" context="#pop" char="*" char1=")"/>
+ </context>
+ <context attribute="String" lineEndContext="#stay" name="String1">
+ <DetectChar attribute="String" context="#pop" char="&quot;"/>
+ </context>
+ <context attribute="String" lineEndContext="#stay" name="String2">
+ <DetectChar attribute="String" context="#pop" char="'"/>
+ </context>
+ </contexts>
+ <itemDatas>
+ <itemData name="Normal Text" defStyleNum="dsNormal" />
+ <itemData name="CommentMaior" defStyleNum="dsComment" />
+ <itemData name="CommentMinor" defStyleNum="dsComment" />
+ <itemData name="String" defStyleNum="dsString" />
+ <itemData name="Keyword" defStyleNum="dsKeyword" />
+ <itemData name="MemAlloc" defStyleNum="dsKeyword" />
+ <itemData name="Exit" defStyleNum="dsKeyword"/>
+ <itemData name="Type" defStyleNum="dsDataType"/>
+ <itemData name="Attribute" defStyleNum="dsOthers"/>
+ <itemData name="Builtin" defStyleNum="dsNormal"/>
+ <itemData name="Integer" defStyleNum="dsBaseN"/>
+ <itemData name="Float" defStyleNum="dsFloat" />
+ <itemData name="Char" defStyleNum="dsChar" />
+ <itemData name="SpecialValues" defStyleNum="dsDecVal"/>
+ <itemData name="ExportFull" defStyleNum="dsOthers"/>
+ <itemData name="ExportReadOnly" defStyleNum="dsOthers"/>
+ <itemData name="Relation" defStyleNum="dsNormal"/>
+ <itemData name="Operator" defStyleNum="dsNormal"/>
+ </itemDatas>
+ </highlighting>
+ <general>
+ <keywords casesensitive="1" />
+ <comments>
+ <comment name="multiLine" start="(*" end="*)" />
+ </comments>
+ </general>
+</language>
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE language SYSTEM "language.dtd">
+<language name="C++" version="1.15" kateversion="2.1" section="Sources" extensions="*.cxx;*.cpp;*.cc;*.C;*.h;*.hxx;*.hpp;*.hcc;*.moc" mimetype="text/x-c++src;text/x-c++hdr;text/x-chdr" priority="9">
+ <highlighting>
+ <list name="keywords">
+ <item> asm </item>
+ <item> break </item>
+ <item> case </item>
+ <item> catch </item>
+ <item> class </item>
+ <item> const_cast </item>
+ <item> continue </item>
+ <item> default </item>
+ <item> delete </item>
+ <item> do </item>
+ <item> dynamic_cast</item>
+ <item> else </item>
+ <item> enum </item>
+ <item> explicit </item>
+ <item> export </item>
+ <item> extern </item>
+ <item> false </item>
+ <item> friend </item>
+ <item> for </item>
+ <item> goto </item>
+ <item> if </item>
+ <item> inline </item>
+ <item> namespace </item>
+ <item> new </item>
+ <item> operator </item>
+ <item> private </item>
+ <item> protected </item>
+ <item> public </item>
+ <item> reinterpret_cast </item>
+ <item> return </item>
+ <item> sizeof </item>
+ <item> static_cast </item>
+ <item> struct </item>
+ <item> switch </item>
+ <item> template </item>
+ <item> this </item>
+ <item> throw </item>
+ <item> true </item>
+ <item> try </item>
+ <item> typedef </item>
+ <item> typeid </item>
+ <item> type_info </item>
+ <item> typename </item>
+ <item> union </item>
+ <item> using </item>
+ <item> virtual </item>
+ <item> while </item>
+
+ <item> and </item>
+ <item> and_eq </item>
+ <item> bad_cast </item>
+ <item> bad_typeid </item>
+ <item> bitand </item>
+ <item> bitor </item>
+ <item> compl </item>
+ <item> not </item>
+ <item> not_eq </item>
+ <item> or </item>
+ <item> or_eq </item>
+ <item> xor </item>
+ <item> xor_eq </item>
+ <item> except </item>
+ <item> finally </item>
+ <item> xalloc </item>
+ </list>
+ <list name="extensions">
+ <item> K_DCOP </item>
+ <item> SLOT </item>
+ <item> SIGNAL </item>
+ <item> Q_CLASSINFO </item>
+ <item> Q_ENUMS </item>
+ <item> Q_EXPORT </item>
+ <item> Q_OBJECT </item>
+ <item> Q_OVERRIDE </item>
+ <item> Q_PROPERTY </item>
+ <item> Q_SETS </item>
+ <item> TRUE </item>
+ <item> FALSE </item>
+ <item> connect </item>
+ <item> disconnect </item>
+ <item> emit </item>
+ <item> signals </item>
+ <item> slots </item>
+ </list>
+ <list name="types">
+ <item> auto </item>
+ <item> bool </item>
+ <item> char </item>
+ <item> const </item>
+ <item> double </item>
+ <item> float </item>
+ <item> int </item>
+ <item> long </item>
+ <item> mutable </item>
+ <item> register </item>
+ <item> short </item>
+ <item> signed </item>
+ <item> static </item>
+ <item> unsigned </item>
+ <item> void </item>
+ <item> volatile </item>
+ <item> uchar </item>
+ <item> uint </item>
+ <item> int8_t </item>
+ <item> int16_t </item>
+ <item> int32_t </item>
+ <item> int64_t </item>
+ <item> uint8_t </item>
+ <item> uint16_t </item>
+ <item> uint32_t </item>
+ <item> uint64_t </item>
+ <item> wchar_t </item>
+ </list>
+ <list name="attention">
+ <item> FIXME </item>
+ <item> TODO </item>
+ <item> ### </item>
+ </list>
+ <contexts>
+ <context attribute="Normal Text" lineEndContext="#stay" name="Normal">
+ <RegExpr attribute="Decimal" context="#stay" String="//\s*BEGIN.*$" beginRegion="Region1"/>
+ <RegExpr attribute="Decimal" context="#stay" String="//\s*END.*$" endRegion="Region1"/>
+ <keyword attribute="Keyword" context="#stay" String="keywords" />
+ <keyword attribute="Extensions" context="#stay" String="extensions" />
+ <keyword attribute="Data Type" context="#stay" String="types" />
+ <Float attribute="Float" context="#stay">
+ <AnyChar String="fF" attribute="Float" context="#stay"/>
+ </Float>
+ <HlCOct attribute="Octal" context="#stay"/>
+ <HlCHex attribute="Hex" context="#stay"/>
+ <Int attribute="Decimal" context="#stay">
+ <StringDetect attribute="Decimal" context="#stay" String="ULL" insensitive="TRUE"/>
+ <StringDetect attribute="Decimal" context="#stay" String="LUL" insensitive="TRUE"/>
+ <StringDetect attribute="Decimal" context="#stay" String="LLU" insensitive="TRUE"/>
+ <StringDetect attribute="Decimal" context="#stay" String="UL" insensitive="TRUE"/>
+ <StringDetect attribute="Decimal" context="#stay" String="LU" insensitive="TRUE"/>
+ <StringDetect attribute="Decimal" context="#stay" String="LL" insensitive="TRUE"/>
+ <StringDetect attribute="Decimal" context="#stay" String="U" insensitive="TRUE"/>
+ <StringDetect attribute="Decimal" context="#stay" String="L" insensitive="TRUE"/>
+ </Int>
+ <HlCChar attribute="Char" context="#stay"/>
+ <DetectChar attribute="String" context="String" char="&quot;"/>
+ <Detect2Chars attribute="Comment" context="Commentar 1" char="/" char1="/"/>
+ <Detect2Chars attribute="Comment" context="Commentar 2" char="/" char1="*" beginRegion="Comment"/>
+ <DetectChar attribute="Symbol" context="#stay" char="{" beginRegion="Brace1" />
+ <DetectChar attribute="Symbol" context="#stay" char="}" endRegion="Brace1" />
+ <StringDetect attribute="Preprocessor" context="Outscoped" String="#if 0"/>
+ <DetectChar attribute="Preprocessor" context="Preprocessor" char="#"/>
+ <RegExpr attribute="Function" context="#stay" String="\b[_\w][_\d\w]*(?=[\s]*[(])" />
+ <RegExpr attribute="Symbol" context="Member" String="([.]{1,1}|[:]{2,2})" />
+ <AnyChar attribute="Symbol" context="#stay" String=":!%&amp;()+,-/.*&lt;=&gt;?[]{|}~^&#59;"/>
+ </context>
+ <context attribute="String" lineEndContext="#pop" name="String">
+ <LineContinue attribute="String" context="#stay"/>
+ <HlCStringChar attribute="String Char" context="#stay"/>
+ <DetectChar attribute="String" context="#pop" char="&quot;"/>
+ </context>
+ <context attribute="Normal Text" lineEndContext="#pop" name="Member">
+ <RegExpr attribute="Function" context="#pop" String="\b[_\w][_\w\d]*(?=[\s]*)" />
+ </context>
+ <context attribute="Comment" lineEndContext="#pop" name="Commentar 1">
+ <keyword attribute="Decimal" context="#stay" String="attention" />
+ </context>
+ <context attribute="Comment" lineEndContext="#stay" name="Commentar 2">
+ <keyword attribute="Decimal" context="#stay" String="attention" />
+ <Detect2Chars attribute="Comment" context="#pop" char="*" char1="/" endRegion="Comment"/>
+ </context>
+ <context attribute="Preprocessor" lineEndContext="#pop" name="Preprocessor">
+ <LineContinue attribute="Preprocessor" context="#stay"/>
+ <RegExpr attribute="Preprocessor" context="Define" String="define.*((?=\\))"/>
+ <RegExpr attribute="Preprocessor" context="#stay" String="define.*"/>
+ <RangeDetect attribute="Prep. Lib" context="#stay" char="&quot;" char1="&quot;"/>
+ <RangeDetect attribute="Prep. Lib" context="#stay" char="&lt;" char1="&gt;"/>
+ <Detect2Chars attribute="Comment" context="Commentar 1" char="/" char1="/"/>
+ <Detect2Chars attribute="Comment" context="Commentar/Preprocessor" char="/" char1="*"/>
+ </context>
+ <context attribute="Preprocessor" lineEndContext="#pop" name="Define">
+ <LineContinue attribute="Preprocessor" context="#stay"/>
+ </context>
+ <context attribute="Comment" lineEndContext="#stay" name="Commentar/Preprocessor">
+ <Detect2Chars attribute="Comment" context="#pop" char="*" char1="/" />
+ </context>
+ <context attribute="Normal Text" lineEndContext="String" name="Some Context"/>
+ <context attribute="Normal Text" lineEndContext="Preprocessor" name="Some Context2"/>
+ <context attribute="Comment" lineEndContext="#stay" name="Outscoped" >
+ <keyword attribute="Decimal" context="#stay" String="attention" />
+ <RegExpr attribute="Comment" context="Outscoped intern" String="^#if" />
+ <RegExpr attribute="Preprocessor" context="#pop" String="#endif" />
+ <RegExpr attribute="Preprocessor" context="#pop" String="#else" />
+ </context>
+ <context attribute="Comment" lineEndContext="#stay" name="Outscoped intern">
+ <RegExpr attribute="Comment" context="Outscoped intern" String="#if" />
+ <RegExpr attribute="Comment" context="#pop" String="#endif" />
+ </context>
+ </contexts>
+ <itemDatas>
+ <itemData name="Normal Text" defStyleNum="dsNormal"/>
+ <itemData name="Keyword" defStyleNum="dsKeyword"/>
+ <itemData name="Extensions" defStyleNum="dsKeyword" color="#0095ff" selColor="#ffffff" bold="1" italic="0"/>
+ <itemData name="Function" defStyleNum="dsKeyword" color="#000080" selColor="#ffffff" bold="0" italic="0"/>
+ <itemData name="Data Type" defStyleNum="dsDataType"/>
+ <itemData name="Decimal" defStyleNum="dsDecVal"/>
+ <itemData name="Octal" defStyleNum="dsBaseN"/>
+ <itemData name="Hex" defStyleNum="dsBaseN"/>
+ <itemData name="Float" defStyleNum="dsFloat"/>
+ <itemData name="Char" defStyleNum="dsChar"/>
+ <itemData name="String" defStyleNum="dsString"/>
+ <itemData name="String Char" defStyleNum="dsChar"/>
+ <itemData name="Comment" defStyleNum="dsComment"/>
+ <itemData name="Symbol" defStyleNum="dsNormal"/>
+ <itemData name="Preprocessor" defStyleNum="dsOthers"/>
+ <itemData name="Prep. Lib" defStyleNum="dsOthers"/>
+ </itemDatas>
+ </highlighting>
+ <general>
+ <comments>
+ <comment name="singleLine" start="//" />
+ <comment name="multiLine" start="/*" end="*/" />
+ </comments>
+ <keywords casesensitive="1" />
+ </general>
+</language>
+<!DOCTYPE language SYSTEM "language.dtd">
+<language name="C#" version="1.08" kateversion="2.1" section="Sources" extensions="*.cs" mimetype="text/x-csharp-src;text/x-csharp-hde">
+ <highlighting>
+ <list name="keywords">
+ <item> abstract</item>
+ <item> as</item>
+ <item> base</item>
+ <item> break</item>
+ <item> case</item>
+ <item> catch</item>
+ <item> class</item>
+ <item> checked</item>
+ <item> continue</item>
+ <item> default</item>
+ <item> delegate</item>
+ <item> do</item>
+ <item> else</item>
+ <item> enum</item>
+ <item> event</item>
+ <item> explicit</item>
+ <item> extern</item>
+ <item> false</item>
+ <item> for</item>
+ <item> foreach</item>
+ <item> finally</item>
+ <item> fixed</item>
+ <item> goto</item>
+ <item> if</item>
+ <item> implicit</item>
+ <item> in</item>
+ <item> interface</item>
+ <item> internal</item>
+ <item> is</item>
+ <item> lock</item>
+ <item> namespace</item>
+ <item> new</item>
+ <item> null</item>
+ <item> operator</item>
+ <item> out</item>
+ <item> override</item>
+ <item> params</item>
+ <item> private</item>
+ <item> protected</item>
+ <item> public</item>
+ <item> readonly</item>
+ <item> ref</item>
+ <item> return</item>
+ <item> sealed</item>
+ <item> sizeof</item>
+ <item> stackalloc</item>
+ <item> static</item>
+ <item> struct</item>
+ <item> switch</item>
+ <item> this</item>
+ <item> throw</item>
+ <item> true</item>
+ <item> try</item>
+ <item> typeof</item>
+ <item> unchecked</item>
+ <item> unsafe</item>
+ <item> using</item>
+ <item> virtual</item>
+ <item> while</item>
+ <item> #if</item>
+ <item> #else</item>
+ <item> #elif</item>
+ <item> #endif</item>
+ <item> #define</item>
+ <item> #undef</item>
+ <item> #warning</item>
+ <item> #error</item>
+ <item> #line</item>
+ </list>
+ <list name="types">
+ <item> bool</item>
+ <item> byte</item>
+ <item> char</item>
+ <item> const</item>
+ <item> decimal</item>
+ <item> double</item>
+ <item> float</item>
+ <item> int</item>
+ <item> long</item>
+ <item> object</item>
+ <item> uint</item>
+ <item> ushort</item>
+ <item> ulong</item>
+ <item> sbyte</item>
+ <item> short</item>
+ <item> string</item>
+ <item> void</item>
+ </list>
+ <contexts>
+ <context attribute="Normal Text" lineEndContext="#stay" name="Normal">
+ <keyword attribute="Keyword" context="#stay" String="keywords"/>
+ <keyword attribute="Data Type" context="#stay" String="types" />
+ <Float attribute="Float" context="#stay">
+ <AnyChar String="fF" attribute="Float" context="#stay"/>
+ </Float>
+ <HlCOct attribute="Octal" context="#stay"/>
+ <HlCHex attribute="Hex" context="#stay"/>
+ <Int attribute="Decimal" context="#stay" >
+ <StringDetect attribute="Decimal" context="#stay" String="ULL" insensitive="TRUE"/>
+ <StringDetect attribute="Decimal" context="#stay" String="LUL" insensitive="TRUE"/>
+ <StringDetect attribute="Decimal" context="#stay" String="LLU" insensitive="TRUE"/>
+ <StringDetect attribute="Decimal" context="#stay" String="UL" insensitive="TRUE"/>
+ <StringDetect attribute="Decimal" context="#stay" String="LU" insensitive="TRUE"/>
+ <StringDetect attribute="Decimal" context="#stay" String="LL" insensitive="TRUE"/>
+ <StringDetect attribute="Decimal" context="#stay" String="U" insensitive="TRUE"/>
+ <StringDetect attribute="Decimal" context="#stay" String="L" insensitive="TRUE"/>
+ </Int>
+ <HlCChar attribute="Char" context="#stay"/>
+ <DetectChar attribute="String" context="String" char="&quot;"/>
+ <Detect2Chars attribute="Comment" context="Commentar 1" char="/" char1="/"/>
+ <Detect2Chars attribute="Comment" context="Commentar 2" char="/" char1="*"/>
+ <DetectChar attribute="Symbol" context="#stay" char="{" beginRegion="block1"/>
+ <DetectChar attribute="Symbol" context="#stay" char="}" endRegion="block1"/>
+ <RegExpr attribute="Decimal" context="#stay" String="#region.*$" beginRegion="Region1"/>
+ <RegExpr attribute="Decimal" context="#stay" String="#endregion.*$" endRegion="Region1"/>
+ <RegExpr attribute="Function" context="#stay" String="\b[_\w][_\w\d]*(?=[\s]*[(])" />
+ <RegExpr attribute="Symbol" context="Member" String="[.]{1,1}" />
+ <AnyChar attribute="Symbol" context="#stay" String=":!%&amp;()+,-/.*&lt;=&gt;?[]|~^&#59;"/>
+ </context>
+ <context attribute="String" lineEndContext="#pop" name="String">
+ <LineContinue attribute="String" context="#pop"/>
+ <HlCStringChar attribute="String Char" context="#stay"/>
+ <DetectChar attribute="String" context="#pop" char="&quot;"/>
+ </context>
+ <context attribute="Normal Text" lineEndContext="#pop" name="Member">
+ <RegExpr attribute="Function" context="#pop" String="\b[_\w][_\w\d]*(?=[\s]*)" />
+ </context>
+ <context attribute="Comment" lineEndContext="#pop" name="Commentar 1"/>
+ <context attribute="Comment" lineEndContext="#stay" name="Commentar 2">
+ <Detect2Chars attribute="Comment" context="#pop" char="*" char1="/"/>
+ </context>
+ </contexts>
+ <itemDatas>
+ <itemData name="Normal Text" defStyleNum="dsNormal"/>
+ <itemData name="Keyword" defStyleNum="dsKeyword"/>
+ <itemData name="Function" defStyleNum="dsKeyword" color="#000080" selColor="#ffffff" bold="0" italic="0"/>
+ <itemData name="Data Type" defStyleNum="dsDataType"/>
+ <itemData name="Decimal" defStyleNum="dsDecVal"/>
+ <itemData name="Octal" defStyleNum="dsBaseN"/>
+ <itemData name="Hex" defStyleNum="dsBaseN"/>
+ <itemData name="Float" defStyleNum="dsFloat"/>
+ <itemData name="Char" defStyleNum="dsChar"/>
+ <itemData name="String" defStyleNum="dsString"/>
+ <itemData name="String Char" defStyleNum="dsChar"/>
+ <itemData name="Comment" defStyleNum="dsComment"/>
+ <itemData name="Symbol" defStyleNum="dsNormal"/>
+ </itemDatas>
+ </highlighting>
+ <general>
+ <comments>
+ <comment name="singleLine" start="//" />
+ <comment name="multiLine" start="/*" end="*/" />
+ </comments>
+ <keywords casesensitive="1" />
+ </general>
+</language>
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE language SYSTEM "language.dtd">
+<language name="CSS" version="1.02" kateversion="2.1" section="Markup" extensions="*.css" mimetype="text/css">
+ <highlighting>
+ <list name="keywords">
+ <item> azimuth </item>
+ <item> background-attachment </item>
+ <item> background-color </item>
+ <item> background-image </item>
+ <item> background-position </item>
+ <item> background-repeat </item>
+ <item> border-collapse </item>
+ <item> border-spacing </item>
+ <item> border-top-color </item>
+ <item> border-right-color </item>
+ <item> border-bottom-color </item>
+ <item> border-left-color </item>
+ <item> border-top-style </item>
+ <item> border-right-style </item>
+ <item> border-bottom-style </item>
+ <item> border-left-style </item>
+ <item> border-top-width </item>
+ <item> border-right-width </item>
+ <item> border-bottom-width </item>
+ <item> border-left-width </item>
+ <item> bottom </item>
+ <item> caption-side </item>
+ <item> clear </item>
+ <item> clip </item>
+ <item> color </item>
+ <item> content </item>
+ <item> counter-increment </item>
+ <item> counter-reset </item>
+ <item> cue-after </item>
+ <item> cue-before </item>
+ <item> cursor </item>
+ <item> direction </item>
+ <item> display </item>
+ <item> elevation </item>
+ <item> empty-cells </item>
+ <item> float </item>
+ <item> font-family </item>
+ <item> font-size </item>
+ <item> font-size-adjust </item>
+ <item> font-stretch </item>
+ <item> font-style </item>
+ <item> font-variant </item>
+ <item> font-weight </item>
+ <item> height </item>
+ <item> left </item>
+ <item> letter-spacing </item>
+ <item> line-height </item>
+ <item> list-style-image </item>
+ <item> list-style-position </item>
+ <item> list-style-keyword </item>
+ <item> list-style-type </item>
+ <item> margin-top </item>
+ <item> margin-right </item>
+ <item> margin-bottom </item>
+ <item> margin-left </item>
+ <item> marker-offset </item>
+ <item> max-height </item>
+ <item> max-width </item>
+ <item> min-height </item>
+ <item> min-width </item>
+ <item> orphans </item>
+ <item> outline-color </item>
+ <item> outline-style </item>
+ <item> outline-width </item>
+ <item> overflow </item>
+ <item> padding-top </item>
+ <item> padding-right </item>
+ <item> padding-bottom </item>
+ <item> padding-left </item>
+ <item> page </item>
+ <item> page-break-after </item>
+ <item> page-break-before </item>
+ <item> page-break-inside </item>
+ <item> pause-after </item>
+ <item> pause-before </item>
+ <item> pitch </item>
+ <item> pitch-range </item>
+ <item> play-during </item>
+ <item> position </item>
+ <item> quotes </item>
+ <item> richness </item>
+ <item> right </item>
+ <item> size </item>
+ <item> speak </item>
+ <item> speak-header </item>
+ <item> speak-numeral </item>
+ <item> speak-punctuation </item>
+ <item> speech-rate </item>
+ <item> stress </item>
+ <item> table-layout </item>
+ <item> text-align </item>
+ <item> text-decoration </item>
+ <item> text-decoration-color </item>
+ <item> text-indent </item>
+ <item> text-shadow </item>
+ <item> text-transform </item>
+ <item> top </item>
+ <item> unicode-bidi </item>
+ <item> vertical-align </item>
+ <item> visibility </item>
+ <item> voice-family </item>
+ <item> volume </item>
+ <item> white-space </item>
+ <item> widows </item>
+ <item> width </item>
+ <item> word-spacing </item>
+ <item> z-index </item>
+ <item> background </item>
+ <item> border </item>
+ <item> border-color </item>
+ <item> border-style </item>
+ <item> border-top </item>
+ <item> border-right </item>
+ <item> border-bottom </item>
+ <item> border-left </item>
+ <item> border-width </item>
+ <item> cue </item>
+ <item> font </item>
+ <item> list-style </item>
+ <item> margin </item>
+ <item> outline </item>
+ <item> padding </item>
+ <item> pause </item>
+ <item> konq_bgpos_x </item>
+ <item> konq_bgpos_y </item>
+ </list>
+ <list name="types">
+ <item> inherit </item>
+ <item> none </item>
+ <item> hidden </item>
+ <item> dotted </item>
+ <item> dashed </item>
+ <item> solid </item>
+ <item> double </item>
+ <item> groove </item>
+ <item> ridge </item>
+ <item> inset </item>
+ <item> outset </item>
+ <item> xx-small </item>
+ <item> x-small </item>
+ <item> small </item>
+ <item> medium </item>
+ <item> large </item>
+ <item> x-large </item>
+ <item> xx-large </item>
+ <item> smaller </item>
+ <item> larger </item>
+ <item> italic </item>
+ <item> oblique </item>
+ <item> small-caps </item>
+ <item> normal </item>
+ <item> bold </item>
+ <item> bolder </item>
+ <item> lighter </item>
+ <item> light </item>
+ <item> 100 </item>
+ <item> 200 </item>
+ <item> 300 </item>
+ <item> 400 </item>
+ <item> 500 </item>
+ <item> 600 </item>
+ <item> 700 </item>
+ <item> 800 </item>
+ <item> 900 </item>
+ <item> transparent </item>
+ <item> repeat </item>
+ <item> repeat-x </item>
+ <item> repeat-y </item>
+ <item> no-repeat </item>
+ <item> baseline </item>
+ <item> sub </item>
+ <item> super </item>
+ <item> top </item>
+ <item> text-top </item>
+ <item> middle </item>
+ <item> bottom </item>
+ <item> text-bottom </item>
+ <item> left </item>
+ <item> right </item>
+ <item> center </item>
+ <item> justify </item>
+ <item> konq-center </item>
+ <item> disc </item>
+ <item> circle </item>
+ <item> square </item>
+ <item> decimal </item>
+ <item> decimal-leading-zero </item>
+ <item> lower-roman </item>
+ <item> upper-roman </item>
+ <item> lower-greek </item>
+ <item> lower-alpha </item>
+ <item> lower-latin </item>
+ <item> upper-alpha </item>
+ <item> upper-latin </item>
+ <item> hebrew </item>
+ <item> armenian </item>
+ <item> georgian </item>
+ <item> cjk-ideographic </item>
+ <item> hiragana </item>
+ <item> katakana </item>
+ <item> hiragana-iroha </item>
+ <item> katakana-iroha </item>
+ <item> inline </item>
+ <item> block </item>
+ <item> list-item </item>
+ <item> run-in </item>
+ <item> compact </item>
+ <item> marker </item>
+ <item> table </item>
+ <item> inline-table </item>
+ <item> table-row-group </item>
+ <item> table-header-group </item>
+ <item> table-footer-group </item>
+ <item> table-row </item>
+ <item> table-column-group </item>
+ <item> table-column </item>
+ <item> table-cell </item>
+ <item> table-caption </item>
+ <item> auto </item>
+ <item> crosshair </item>
+ <item> default </item>
+ <item> pointer </item>
+ <item> move </item>
+ <item> e-resize </item>
+ <item> ne-resize </item>
+ <item> nw-resize </item>
+ <item> n-resize </item>
+ <item> se-resize </item>
+ <item> sw-resize </item>
+ <item> s-resize </item>
+ <item> w-resize </item>
+ <item> text </item>
+ <item> wait </item>
+ <item> help </item>
+ <item> above </item>
+ <item> absolute </item>
+ <item> always </item>
+ <item> avoid </item>
+ <item> below </item>
+ <item> bidi-override </item>
+ <item> blink </item>
+ <item> both </item>
+ <item> capitalize </item>
+ <item> caption </item>
+ <item> close-quote </item>
+ <item> collapse </item>
+ <item> condensed </item>
+ <item> crop </item>
+ <item> cross </item>
+ <item> embed </item>
+ <item> expanded </item>
+ <item> extra-condensed </item>
+ <item> extra-expanded </item>
+ <item> fixed </item>
+ <item> hand </item>
+ <item> hide </item>
+ <item> higher </item>
+ <item> icon </item>
+ <item> inside </item>
+ <item> invert </item>
+ <item> landscape </item>
+ <item> level </item>
+ <item> line-through </item>
+ <item> loud </item>
+ <item> lower </item>
+ <item> lowercase </item>
+ <item> ltr </item>
+ <item> menu </item>
+ <item> message-box </item>
+ <item> mix </item>
+ <item> narrower </item>
+ <item> no-close-quote </item>
+ <item> no-open-quote </item>
+ <item> nowrap </item>
+ <item> open-quote </item>
+ <item> outside </item>
+ <item> overline </item>
+ <item> portrait </item>
+ <item> pre </item>
+ <item> relative </item>
+ <item> rtl </item>
+ <item> scroll </item>
+ <item> semi-condensed </item>
+ <item> semi-expanded </item>
+ <item> separate </item>
+ <item> show </item>
+ <item> small-caption </item>
+ <item> static </item>
+ <item> static-position </item>
+ <item> status-bar </item>
+ <item> thick </item>
+ <item> thin </item>
+ <item> ultra-condensed </item>
+ <item> ultra-expanded </item>
+ <item> underline </item>
+ <item> uppercase </item>
+ <item> visible </item>
+ <item> wider </item>
+ <item> break </item>
+ </list>
+ <contexts>
+ <context attribute="Normal Text" lineEndContext="#stay" name="Normal">
+ <RegExpr attribute="Notice" context="#stay" String="/\*BEGIN.*\*/" beginRegion="UserDefined" />
+ <RegExpr attribute="Notice" context="#stay" String="/\*END.*\*/" endRegion="UserDefined" />
+ <Detect2Chars attribute="Comment" context="Comment" char="/" char1="*" beginRegion="Comment" />
+ <DetectChar attribute="Normal Text" context="Rules" char="{" beginRegion="RuleSet" />
+ </context>
+ <context attribute="Normal Text" lineEndContext="#stay" name="Rules">
+ <DetectChar attribute="Normal Text" context="#pop" char="}" endRegion="RuleSet" />
+ <Detect2Chars attribute="Comment" context="Comment" char="/" char1="*" beginRegion="Comment" />
+ <keyword attribute="Keyword" context="#stay" String="keywords" />
+ <keyword attribute="Data Type" context="#stay" String="types" />
+ </context>
+ <context attribute="Comment" lineEndContext="#stay" name="Comment">
+ <Detect2Chars attribute="Comment" context="#pop" char="*" char1="/" endRegion="Comment" />
+ <RegExpr attribute="Notice" context= "#stay" String="(FIXME|TODO|NOTE)" />
+ </context>
+ </contexts>
+ <itemDatas>
+ <itemData name="Normal Text" defStyleNum="dsNormal"/>
+ <itemData name="Keyword" defStyleNum="dsKeyword"/>
+ <itemData name="Data Type" defStyleNum="dsDataType"/>
+ <itemData name="Decimal" defStyleNum="dsDecVal"/>
+ <itemData name="Float" defStyleNum="dsFloat"/>
+ <itemData name="Comment" defStyleNum="dsComment" />
+ <itemData name="Notice" defStyleNum="dsString" bold="1"/>
+ </itemDatas>
+ </highlighting>
+ <general>
+ <keywords casesensitive="1" weakDeliminator="-"/>
+ <comments>
+ <comment name="multiLine" start="/*" end="*/" />
+ </comments>
+ </general>
+</language>
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE language SYSTEM "language.dtd">
+<language name="CUE Sheet" version="0.9" kateversion="2.1" section="Other" extensions="*.cue" mimetype="application/x-cue">
+ <highlighting>
+ <list name="keywords">
+ <item> CATALOG </item>
+ <item> CDTEXTFILE </item>
+ <item> FILE </item>
+ <item> FLAGS </item>
+ <item> INDEX </item>
+ <item> ISRC </item>
+ <item> PERFORMER </item>
+ <item> PREGAP </item>
+ <item> POSTGAP </item>
+ <item> REM </item>
+ <item> SONGWRITER </item>
+ <item> TITLE </item>
+ <item> TRACK </item>
+ </list>
+ <list name="format">
+ <item> AIFF </item>
+ <item> WAVE </item>
+ <item> MP3 </item>
+ <item> BINARY </item>
+ <item> MOTOTOLA </item>
+ </list>
+ <list name="mode">
+ <item> AUDIO </item>
+ <item> CDG </item>
+ <item> CDI </item>
+ <item> MODE1 </item>
+ <item> MODE2 </item>
+ <item> RAW </item>
+ </list>
+ <list name="flags">
+ <item> 4CH </item>
+ <item> DCP </item>
+ <item> PRE </item>
+ <item> SCMS </item>
+ </list>
+ <contexts>
+ <context attribute="Normal Text" lineEndContext="#stay" name="Normal">
+ <keyword attribute="Keyword" context="#stay" String="keywords" />
+ <keyword attribute="Format" context="#stay" String="format" />
+ <keyword attribute="Mode" context="#stay" String="mode" />
+ <keyword attribute="Flags" context="#stay" String="flags" />
+ <Int attribute="Decimal" context="#stay" />
+ <DetectChar attribute="String" context="String" char="&quot;"/>
+ <DetectChar attribute="Comment" context="Comment" char=";"/>
+ </context>
+ <context attribute="String" lineEndContext="#pop" name="String">
+ <LineContinue attribute="String" context="#stay"/>
+ <DetectChar attribute="String" context="#pop" char="&quot;"/>
+ </context>
+ <context attribute="Comment" lineEndContext="#pop" name="Comment" />
+ </contexts>
+ <itemDatas>
+ <itemData name="Normal Text" defStyleNum="dsNormal"/>
+ <itemData name="Keyword" defStyleNum="dsKeyword"/>
+ <itemData name="Format" defStyleNum="dsNormal" color="#800000"/>
+ <itemData name="Mode" defStyleNum="dsNormal" color="#000080"/>
+ <itemData name="Flags" defStyleNum="dsNormal" color="#008000"/>
+ <itemData name="Decimal" defStyleNum="dsDecVal"/>
+ <itemData name="String" defStyleNum="dsString"/>
+ <itemData name="Comment" defStyleNum="dsComment"/>
+ </itemDatas>
+ </highlighting>
+ <general>
+ <comments>
+ <comment name="singleLine" start=";" />
+ </comments>
+ <keywords casesensitive="0" />
+ </general>
+</language>
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE language SYSTEM "language.dtd">
+<language name="D" version="1.1" kateversion="2.1" section="Sources" extensions="*.d;*.D" mimetype="text/x-dsrc">
+ <highlighting>
+ <list name="keywords">
+ <item> abstract </item>
+ <item> alias </item>
+ <item> align </item>
+ <item> asm </item>
+ <item> auto </item>
+
+ <item> body </item>
+ <item> break </item>
+
+ <item> case </item>
+ <item> cast </item>
+ <item> catch </item>
+ <item> class </item>
+ <item> const </item>
+ <item> continue </item>
+
+ <item> default </item>
+ <item> delegate </item>
+ <item> delete </item>
+ <item> deprecated </item>
+ <item> do </item>
+
+ <item> else </item>
+ <item> enum </item>
+ <item> export </item>
+
+ <item> false </item>
+ <item> final </item>
+ <item> finally </item>
+ <item> for </item>
+ <item> foreach </item>
+ <item> function </item>
+
+ <item> goto </item>
+
+ <item> if </item>
+ <item> in </item>
+ <item> inout </item>
+ <item> interface </item>
+ <item> invariant </item>
+
+ <item> new </item>
+ <item> null </item>
+
+ <item> override </item>
+ <item> out </item>
+
+ <item> private </item>
+ <item> protected </item>
+ <item> public </item>
+
+ <item> return </item>
+
+ <item> static </item>
+ <item> struct </item>
+ <item> super </item>
+ <item> switch </item>
+ <item> synchronized </item>
+
+ <item> this </item>
+ <item> throw </item>
+ <item> true </item>
+ <item> try </item>
+ <item> typedef </item>
+
+ <item> union </item>
+
+ <item> volatile </item>
+
+ <item> while </item>
+ <item> with </item>
+ </list>
+ <list name="modules">
+ <item> module </item>
+ <item> import </item>
+ </list>
+ <list name="types">
+ <item> void </item>
+ <item> bit </item>
+ <item> byte </item>
+ <item> ubyte </item>
+ <item> short </item>
+ <item> ushort </item>
+ <item> int </item>
+ <item> uint </item>
+ <item> long </item>
+ <item> ulong </item>
+ <item> cent </item>
+ <item> ucent </item>
+ <item> float </item>
+ <item> double </item>
+ <item> real </item>
+ <item> ireal </item>
+ <item> ifloat </item>
+ <item> idouble </item>
+ <item> creal </item>
+ <item> cfloat </item>
+ <item> cdouble </item>
+ <item> char </item>
+ <item> wchar </item>
+ <item> dchar </item>
+ </list>
+ <list name="phobos">
+ <item> printf </item>
+ </list>
+ <list name="linkage">
+ <item> extern </item>
+ </list>
+ <list name="ltypes">
+ <item> C </item>
+ <item> D </item>
+ <item> Windows </item>
+ <item> Pascal </item>
+ </list>
+ <list name="debug">
+ <item> debug </item>
+ </list>
+ <list name="assert">
+ <item> assert </item>
+ </list>
+ <list name="version">
+ <item> version </item>
+ </list>
+ <list name="vtypes">
+ <item> DigitalMars </item>
+ <item> X86 </item>
+ <item> Win32 </item>
+ <item> linux </item>
+ <item> LittleEndian </item>
+ <item> BigEndian </item>
+ <item> D_InlineAsm </item>
+ <item> none </item>
+ </list>
+ <list name="unittest">
+ <item> unittest </item>
+ </list>
+ <contexts>
+ <context attribute="Normal Text" lineEndContext="#stay" name="normal">
+ <keyword attribute="Keyword" context="#stay" String="keywords"/>
+ <keyword attribute="Module" context="ModuleName" String="modules"/>
+ <keyword attribute="Type" context="#stay" String="types"/>
+ <keyword attribute="Phobos Library" context="#stay" String="phobos"/>
+ <keyword attribute="Linkage" context="Linkage" String="linkage"/>
+ <keyword attribute="Debug" context="#stay" String="debug"/>
+ <keyword attribute="Assert" context="#stay" String="assert"/>
+ <keyword attribute="Version" context="Version" String="version"/>
+ <keyword attribute="Unit Test" context="#stay" String="unittest"/>
+
+ <Detect2Chars attribute="Wysiwyg" context="Wysiwyg" char="r" char1="&quot;"/>
+ <Detect2Chars attribute="Hex" context="Hex" char="x" char1="&quot;"/>
+
+ <RegExpr attribute="Normal Text" context="#pop" String="[_a-z][\w]*" insensitive="TRUE"/>
+
+ <RegExpr attribute="Pragmas" context="#pop" String="\#[ ]*line"/>
+
+ <RegExpr attribute="Escape String" context="#pop" String="\\[n|t|&quot;]"/>
+ <RegExpr attribute="Escape String" context="#pop" String="(\\r\\n)"/>
+ <RegExpr attribute="Escape String" context="#pop" String="\\0[0-7]+"/>
+ <RegExpr attribute="Escape String" context="#pop" String="\\u[\d]+" insensitive="TRUE"/>
+ <RegExpr attribute="Escape String" context="#pop" String="\\x[\da-fA-F]+"/>
+
+ <RegExpr attribute="Binary" context="#pop" String="0b[01]+[_01]*[ ]*\.\.[ ]*0b[01]+[_01]*(UL|LU|U|L)?" insensitive="TRUE"/>
+ <RegExpr attribute="Octal" context="#pop" String="0[0-7]+[_0-7]*[ ]*\.\.[ ]*0[0-7]+[_0-7]*(UL|LU|U|L)?" insensitive="TRUE"/>
+ <RegExpr attribute="Hex" context="#pop" String="0x[\da-f]+[_\da-f]*[ ]*\.\.[ ]*0x[\da-f]+[_\da-f]*(UL|LU|U|L)?" insensitive="TRUE"/>
+ <RegExpr attribute="Integer" context="#pop" String="[\d]+[_\d]*(UL|LU|U|L)?[ ]*\.\.[ ]*[\d]+[_\d]*(UL|LU|U|L)?" insensitive="TRUE"/>
+
+ <RegExpr attribute="Float" context="#pop" String="[\d]*[_\d]*\.[_\d]*(e-|e|e\+)?[\d]+[_\d]*(F|L|I|FI|LI|)?" insensitive="TRUE"/>
+ <RegExpr attribute="Float" context="#pop" String="[\d]*[_\d]*\.?[_\d]*(e-|e|e\+)[\d]+[_\d]*(F|L|I|FI|LI|)?" insensitive="TRUE"/>
+ <RegExpr attribute="Float" context="#pop" String="0x[\da-f]+[_\da-f]*\.[_\da-f]*(p-|p|p\+)?[\da-f]+[_\da-f]*(F|L|I|FI|LI)?" insensitive="TRUE"/>
+ <RegExpr attribute="Float" context="#pop" String="0x[\da-f]+[_\da-f]*\.?[_\da-f]*(p-|p|p\+)[\da-f]+[_\da-f]*(F|L|I|FI|LI)?" insensitive="TRUE"/>
+
+ <RegExpr attribute="Binary" context="#pop" String="0B[01]+[_01]*(UL|LU|U|L)?" insensitive="TRUE"/>
+ <RegExpr attribute="Octal" context="#pop" String="0[0-7]+[_0-7]*(UL|LU|U|L)?" insensitive="TRUE"/>
+ <RegExpr attribute="Hex" context="#pop" String="0x[\da-f]+[_\da-f]*(UL|LU|U|L)?" insensitive="TRUE"/>
+ <RegExpr attribute="Integer" context="#pop" String="[\d]+[_\d]*(UL|LU|U|L)?" insensitive="TRUE"/>
+
+ <DetectChar attribute="Char" context="Char" char="'"/>
+ <DetectChar attribute="String" context="String" char="&quot;"/>
+ <DetectChar attribute="Wysiwyg" context="Wysiwyg" char="`"/>
+
+ <Detect2Chars attribute="Comment" context="CommentLine" char="/" char1="/"/>
+ <Detect2Chars attribute="Comment" context="CommentBlockA" char="/" char1="*" beginRegion="CommentA"/>
+ <Detect2Chars attribute="Comment" context="CommentBlockB" char="/" char1="+" beginRegion="CommentB"/>
+
+ <DetectChar attribute="Normal Text" context="#stay" char="{" beginRegion="BraceA" />
+ <DetectChar attribute="Normal Text" context="#stay" char="}" endRegion="BraceA" />
+ </context>
+
+ <context attribute="Module Name" lineEndContext="#stay" name="ModuleName">
+ <DetectChar attribute="Normal Text" context="#stay" char=","/>
+ <DetectChar attribute="Normal Text" context="#pop" char=";"/>
+ <Detect2Chars attribute="Comment" context="CommentLine" char="/" char1="/"/>
+ <Detect2Chars attribute="Comment" context="CommentBlockA" char="/" char1="*" beginRegion="CommentA"/>
+ <Detect2Chars attribute="Comment" context="CommentBlockB" char="/" char1="+" beginRegion="CommentB"/>
+ </context>
+
+ <context attribute="Linkage" lineEndContext="#pop" name="Linkage">
+ <keyword attribute="Type" context="#pop" String="types"/>
+ <DetectChar attribute="Normal Text" context="#stay" char="("/>
+ <keyword attribute="Linkage Type" context="#stay" String="ltypes"/>
+ <DetectChar attribute="Normal Text" context="#pop" char=")"/>
+ <DetectChar attribute="Normal Text" context="#pop" char=";"/>
+ </context>
+ <context attribute="Version" lineEndContext="#pop" name="Version">
+ <DetectChar attribute="Normal Text" context="#pop" char="="/>
+ <DetectChar attribute="Normal Text" context="#stay" char="("/>
+ <keyword attribute="Version Type" context="#stay" String="vtypes"/>
+ <RegExpr attribute="Normal Text" context="#pop" String="\w" insensitive="TRUE"/>
+ <DetectChar attribute="Normal Text" context="#pop" char=")"/>
+ </context>
+
+ <context attribute="Wysiwyg" lineEndContext="#stay" name="Wysiwyg">
+ <DetectChar attribute="Wysiwyg" context="#pop" char="&quot;"/>
+ <DetectChar attribute="Wysiwyg" context="#pop" char="`"/>
+ </context>
+ <context attribute="Hex" lineEndContext="#stay" name="Hex">
+ <DetectChar attribute="Hex" context="#pop" char="&quot;"/>
+ </context>
+ <context attribute="Char" lineEndContext="#stay" name="Char">
+ <Detect2Chars attribute="Char" context="#stay" char="\" char1="'"/>
+ <Detect2Chars attribute="Char" context="#stay" char="\" char1="\"/>
+ <DetectChar attribute="Char" context="#pop" char="'"/>
+ </context>
+ <context attribute="String" lineEndContext="#stay" name="String">
+ <DetectChar attribute="String" context="#pop" char="&quot;"/>
+ </context>
+
+ <context attribute="Comment" lineEndContext="#pop" name="CommentLine">
+ </context>
+ <context attribute="Comment" lineEndContext="#stay" name="CommentBlockA">
+ <Detect2Chars attribute="Comment" context="#pop" char="*" char1="/" endRegion="CommentA"/>
+ </context>
+ <context attribute="Comment" lineEndContext="#stay" name="CommentBlockB">
+ <Detect2Chars attribute="Comment" context="#pop" char="+" char1="/" endRegion="CommentB"/>
+ </context>
+ </contexts>
+ <itemDatas>
+ <itemData name="Normal Text" defStyleNum="dsNormal"/>
+ <itemData name="Keyword" defStyleNum="dsKeyWord" color="#000000" selColor="#ffffff" bold="1" italic="0"/>
+ <itemData name="Module" defStyleNum="dsKeyWord" color="#0000ff" selColor="#ffffff" bold="0" italic="0"/>
+ <itemData name="Module Name" defStyleNum="dsKeyWord" color="#0000ff" selColor="#ffffff" bold="1" italic="1"/>
+ <itemData name="Type" defStyleNum="dsDataType"/>
+ <itemData name="Phobos Library" defStyleNum="dsKeyWord" color="#000000" selColor="#ffffff" bold="1" italic="1"/>
+ <itemData name="Linkage" defStyleNum="dsKeyWord" color="#ff0000" selColor="#ffffff" bold="0" italic="0"/>
+ <itemData name="Linkage Type" defStyleNum="dsDataType" color="#ff0000" selColor="#ffffff" bold="1" italic="1"/>
+ <itemData name="Debug" defStyleNum="dsKeyWord" color="#ff00ff" selColor="#ffffff" bold="0" italic="0"/>
+ <itemData name="Assert" defStyleNum="dsKeyWord" color="#0000ff" selColor="#ffffff" bold="0" italic="0"/>
+ <itemData name="Version" defStyleNum="dsKeyWord" color="#ff0000" selColor="#ffffff" bold="0" italic="0"/>
+ <itemData name="Version Type" defStyleNum="dsDataType" color="#ff0000" selColor="#ffffff" bold="1" italic="1"/>
+ <itemData name="Unit Test" defStyleNum="dsKeyWord" color="#ff00ff" selColor="#ffffff" bold="1" italic="0"/>
+ <itemData name="Pragmas" defStyleNum="dsKeyWord" color="#00aa00" selColor="#ffffff" bold="1" italic="0"/>
+
+ <itemData name="Integer" defStyleNum="dsDecVal"/>
+ <itemData name="Binary" defStyleNum="dsBaseN" color="#00dddd" selColor="#00ffff" bold="0" italic="0"/>
+ <itemData name="Octal" defStyleNum="dsBaseN" color="#00aaaa" selColor="#00ffff" bold="0" italic="0"/>
+ <itemData name="Hex" defStyleNum="dsBaseN" color="#006666" selColor="#00ffff" bold="0" italic="0"/>
+ <itemData name="Float" defStyleNum="dsFloat"/>
+
+ <itemData name="Escape String" defStyleNum="dsString" color="#00aa00" selColor="#ff0000" bold="0" italic="0"/>
+ <itemData name="String" defStyleNum="dsString" color="#00aa00" selColor="#ff0000" bold="0" italic="0"/>
+ <itemData name="Char" defStyleNum="dsChar" color="#ff00ff" selColor="#ff00ff" bold="0" italic="0"/>
+ <itemData name="Wysiwyg" defStyleNum="dsChar" color="#a100a1" selColor="#ff00ff" bold="0" italic="0"/>
+
+ <itemData name="Comment" defStyleNum="dsComment"/>
+ </itemDatas>
+ </highlighting>
+ <general>
+ <comments>
+ <comment name="singleLine" start="//"/>
+ <comment name="multiLine" start="/+" end="+/"/>
+ </comments>
+ <keywords casesensitive="TRUE"/>
+ </general>
+</language>
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE language SYSTEM "language.dtd">
+<language name=".desktop" version="1.02" kateversion="2.1" section="Other" extensions="*.desktop;*.kdelnk" mimetype="application/x-desktop">
+ <highlighting>
+ <contexts>
+ <context attribute="Key" lineEndContext="#stay" name="Normal">
+ <RegExpr String="^\[.*\]$" attribute="Section" context="#stay" beginRegion="Section" endRegion="Section" />
+ <RegExpr String="\[.*\]" attribute="Language" context="Value"/>
+ <DetectChar char="=" attribute="Normal Text" context="Value"/>
+ </context>
+ <context attribute="Normal Text" lineEndContext="#pop" name="Value"/>
+ </contexts>
+ <itemDatas>
+ <itemData name="Normal Text" defStyleNum="dsNormal"/>
+ <itemData name="Section" defStyleNum="dsKeyword"/>
+ <itemData name="Key" defStyleNum="dsDataType"/>
+ <itemData name="Language" defStyleNum="dsNormal" color="#0000FF" selColor="#ffffff" italic="0" bold="1"/>
+ </itemDatas>
+ </highlighting>
+</language>
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE language SYSTEM "language.dtd">
+<language name="Diff" version="1.01" kateversion="2.1" section="Other" extensions="*.diff;*patch" mimetype="text/x-diff">
+ <highlighting>
+
+ <contexts>
+ <context attribute="Normal Text" lineEndContext="#stay" name="Normal">
+ <RegExpr attribute="Keyword" context="Keyword" String="^(\+\+\+|\-\-\-|\*\*\*|diff|\d)" />
+ <RegExpr attribute="Added line" context="Added" String="^(\+|&gt;|!)" />
+ <RegExpr attribute="Removed line" context="Removed" String="^(\-|&lt;)" />
+ <RegExpr attribute="Data Type" context="Data" String="^\@\@" />
+ </context>
+
+ <context attribute="Keyword" lineEndContext="#pop" name="Keyword"/>
+ <context attribute="Removed line" lineEndContext="#pop" name="Removed"/>
+ <context attribute="Added line" lineEndContext="#pop" name="Added"/>
+ <context attribute="Data Type" lineEndContext="#pop" name="Data"/>
+
+ </contexts>
+
+ <itemDatas>
+ <itemData name="Normal Text" defStyleNum="dsNormal"/>
+ <itemData name="Keyword" defStyleNum="dsKeyword"/>
+ <itemData name="Data Type" defStyleNum="dsDataType"/>
+ <itemData name="Removed line" defStyleNum="dsOthers"/>
+ <itemData name="Added line" defStyleNum="dsString"/>
+ <itemData name="Comment" defStyleNum="dsComment"/>
+ </itemDatas>
+ </highlighting>
+ <general>
+ <keywords casesensitive="1" />
+ </general>
+</language>
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE language SYSTEM "language.dtd">
+<language name="E Language" version="0.1" kateversion="2.1" section="Sources" extensions="*.e" mimetype="text/x-e-src">
+ <highlighting>
+ <list name="Type">
+ <item> FALSE </item>
+ <item> MAX_INT </item>
+ <item> MIN_INT </item>
+ <item> NULL </item>
+ <item> TRUE </item>
+ <item> UNDEF </item>
+ <item> bit </item>
+ <item> bits </item>
+ <item> body </item>
+ <item> bool </item>
+ <item> byte </item>
+ <item> byte_array </item>
+ <item> continue </item>
+ <item> copy </item>
+ <item> default </item>
+ <item> external_pointer </item>
+ <item> files </item>
+ <item> file </item>
+ <item> form </item>
+ <item> global </item>
+ <item> index </item>
+ <item> init </item>
+ <item> int </item>
+ <item> it </item>
+ <item> list </item>
+ <item> load </item>
+ <item> long </item>
+ <item> me </item>
+ <item> method </item>
+ <item> module </item>
+ <item> ntv </item>
+ <item> of </item>
+ <item> pat </item>
+ <item> print </item>
+ <item> result </item>
+ <item> source_ref </item>
+ <item> string </item>
+ <item> symtab </item>
+ <item> sys </item>
+ <item> test </item>
+ <item> uint </item>
+ <item> untyped </item>
+ <item> vec </item>
+ </list>
+ <list name="Function">
+ <item> run </item>
+ <item> init </item>
+ <item> pre_generate </item>
+ <item> dut_error </item>
+ <item> pack </item>
+ <item> unpack </item>
+ <item> post_generate </item>
+ <item> pre_generate </item>
+ <item> set_config </item>
+ <item> hex </item>
+ <item> stop_run </item>
+ <item> append </item>
+ <item> size </item>
+ <item> delete </item>
+ <item> is_empty </item>
+ <item> deep_compare </item>
+ <item> deep_compare_physical </item>
+ <item> clear </item>
+ <item> pop0 </item>
+ <item> setup </item>
+ <item> crc_32 </item>
+ </list>
+ <list name="Statement">
+ <item> chars </item>
+ <item> define </item>
+ <item> extend </item>
+ <item> event </item>
+ <item> ECHO </item>
+ <item> DOECHO </item>
+ <item> import </item>
+ <item> initialize </item>
+ <item> non_terminal </item>
+ <item> struct </item>
+ <item> unit </item>
+ <item> script </item>
+ <item> testgroup </item>
+ <item> type </item>
+ </list>
+ <list name="Action">
+ <item> C </item>
+ <item> add </item>
+ <item> also </item>
+ <item> and </item>
+ <item> as </item>
+ <item> as_a </item>
+ <item> break </item>
+ <item> code </item>
+ <item> compute </item>
+ <item> computed </item>
+ <item> delayed </item>
+ <item> do </item>
+ <item> else </item>
+ <item> each </item>
+ <item> emit </item>
+ <item> empty </item>
+ <item> end </item>
+ <item> exit </item>
+ <item> finish </item>
+ <item> for </item>
+ <item> from </item>
+ <item> if </item>
+ <item> in </item>
+ <item> is </item>
+ <item> like </item>
+ <item> log </item>
+ <item> new </item>
+ <item> no </item>
+ <item> not </item>
+ <item> only </item>
+ <item> or </item>
+ <item> out </item>
+ <item> read </item>
+ <item> repeat </item>
+ <item> return </item>
+ <item> reverse </item>
+ <item> routine </item>
+ <item> step </item>
+ <item> then </item>
+ <item> to </item>
+ <item> traceable </item>
+ <item> untraceable </item>
+ <item> var </item>
+ <item> when </item>
+ <item> while </item>
+ <item> with </item>
+ <item> write </item>
+ <item> xor </item>
+ </list>
+ <list name="Generation">
+ <item> before </item>
+ <item> by </item>
+ <item> choose </item>
+ <item> gen </item>
+ <item> keep </item>
+ <item> keeping </item>
+ <item> matches </item>
+ <item> next </item>
+ <item> select </item>
+ <item> sequence </item>
+ <item> soft </item>
+ <item> using </item>
+ </list>
+ <list name="Cover">
+ <item> address </item>
+ <item> cover </item>
+ <item> error </item>
+ <item> events </item>
+ <item> event </item>
+ <item> length </item>
+ <item> kind </item>
+ <item> ranges </item>
+ <item> range </item>
+ <item> sample </item>
+ <item> text </item>
+ <item> value </item>
+ <item> item </item>
+ <item> transition </item>
+ <item> illegal </item>
+ </list>
+ <list name="Simulator">
+ <item> always </item>
+ <item> all </item>
+ <item> basic </item>
+ <item> call </item>
+ <item> cycles </item>
+ <item> cycle </item>
+ <item> clock </item>
+ <item> change </item>
+ <item> check </item>
+ <item> expect </item>
+ <item> fall </item>
+ <item> first </item>
+ <item> forever </item>
+ <item> idle </item>
+ <item> initial </item>
+ <item> negedge </item>
+ <item> others </item>
+ <item> on </item>
+ <item> posedge </item>
+ <item> rise </item>
+ <item> start </item>
+ <item> that </item>
+ <item> time </item>
+ <item> task </item>
+ <item> until </item>
+ <item> verilog </item>
+ <item> vhdl </item>
+ <item> wait </item>
+ <item> within </item>
+ </list>
+ <contexts>
+
+ <context name="out_comment" attribute="OutSide E code" lineEndContext="#stay">
+ <Detect2Chars attribute="code_begin" context="normal" char="&lt;" char1="'" />
+ </context>
+
+ <context name="normal" attribute="Normal Text" lineEndContext="#stay">
+ <DetectChar attribute="Operators" context="#stay" char="{" beginRegion="Region1"/>
+ <DetectChar attribute="Operators" context="#stay" char="}" endRegion="Region1"/>
+ <HlCHex attribute="Integer" context="#stay" />
+ <HlCOct attribute="Integer" context="#stay" />
+ <Int attribute="Integer" context="#stay" />
+ <Detect2Chars attribute="code_end" context="out_comment" char="'" char1="&gt;" />
+ <Detect2Chars attribute="Comment" context="comment" char="-" char1="-" />
+ <Detect2Chars attribute="Comment" context="comment" char="/" char1="/" />
+ <DetectChar attribute="Vector" context="string" char="&quot;" />
+ <AnyChar attribute="Operators" context="#stay" String="'[&amp;&gt;&lt;=:+\-*\|].,;" />
+ <keyword attribute="Data Type" context="#stay" String="Type"/>
+ <keyword attribute="Function" context="#stay" String="Function"/>
+ <keyword attribute="Statement" context="#stay" String="Statement"/>
+ <keyword attribute="Action" context="#stay" String="Action"/>
+ <keyword attribute="Keyword" context="#stay" String="Generation"/>
+ <keyword attribute="Keyword" context="#stay" String="Cover"/>
+ <keyword attribute="Keyword" context="#stay" String="Simulator"/>
+
+ </context>
+
+
+ <context name="comment" attribute="Comment" lineEndContext="#pop" />
+
+ <context name="string" attribute="Vector" lineEndContext="#stay" >
+ <DetectChar attribute="Vector" context="#pop" char="&quot;" />
+ </context>
+
+ </contexts>
+
+ <itemDatas>
+ <itemData name="OutSide E code" defStyleNum="dsComment"/>
+ <itemData name="Normal Text" defStyleNum="dsNormal" />
+ <itemData name="Keyword" defStyleNum="dsKeyword" />
+ <itemData name="Function" defStyleNum="dsNormal" color="#0000FF" selColor="#00ff00" bold="1" italic="0" />
+ <itemData name="Statement" defStyleNum="dsNormal" color="#8080FF" selColor="#00ff00" bold="1" italic="0" />
+ <itemData name="Action" defStyleNum="dsNormal" color="#FF8080" selColor="#00ff00" bold="1" italic="0" />
+ <itemData name="Data Type" defStyleNum="dsDataType" />
+ <itemData name="Comment" defStyleNum="dsComment" />
+ <itemData name="Integer" defStyleNum="dsDecVal" />
+ <itemData name="Bit" defStyleNum="dsDecVal" />
+ <itemData name="Vector" defStyleNum="dsString" />
+ <itemData name="Operators" defStyleNum="dsNormal" color="#404000" selColor="#00ff00" bold="0" italic="0" />
+ </itemDatas>
+
+ </highlighting>
+ <general>
+ <keywords casesensitive="1" />
+ </general>
+</language>
+
+
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE language SYSTEM "language.dtd">
+<language name="Eiffel" version="1.02" kateversion="2.1" section="Sources" extensions="*.e" mimetype="text/x-eiffel-src">
+ <highlighting>
+ <list name="keywords">
+ <item> indexing </item>
+ <item> class </item>
+ <item> inherit </item>
+ <item> creation </item>
+ <item> feature </item>
+ <item> rename </item>
+ <item> redefine </item>
+ <item> undefine </item>
+ <item> select </item>
+ <item> export </item>
+ <item> local </item>
+ <item> deferred </item>
+ <item> do </item>
+ <item> is </item>
+ <item> once </item>
+ <item> alias </item>
+ <item> external </item>
+ <item> rescue </item>
+ <item> debug </item>
+ <item> if </item>
+ <item> inspect </item>
+ <item> from </item>
+ <item> else </item>
+ <item> elseif </item>
+ <item> when </item>
+ <item> until </item>
+ <item> loop </item>
+ <item> then </item>
+ <item> obsolete </item>
+ <item> end </item>
+ </list>
+ <list name="assertions">
+ <item> check </item>
+ <item> ensure </item>
+ <item> require </item>
+ <item> variant </item>
+ <item> invariant </item>
+ </list>
+ <contexts>
+ <context attribute="Normal Text" lineEndContext="#stay" name="Normal">
+ <keyword attribute="Keyword" context="#stay" String="keywords"/>
+ <Float attribute="Float" context="#stay"/>
+ <Int attribute="Decimal" context="#stay"/>
+ <RegExpr attribute="Char" context="#stay" String="'.'"/>
+ <DetectChar attribute="String" context="String" char="&quot;"/>
+ <Detect2Chars attribute="Comment" context="Comment" char="-" char1="-"/>
+ <keyword attribute="Assertions" context="#stay" String="assertions"/>
+ </context>
+ <context attribute="String" lineEndContext="#pop" name="String">
+ <DetectChar attribute="String" context="#pop" char="&quot;"/>
+ </context>
+ <context attribute="Comment" name="Comment" lineEndContext="#pop"/>
+ </contexts>
+ <itemDatas>
+ <itemData name="Normal Text" defStyleNum="dsNormal" />
+ <itemData name="Keyword" defStyleNum="dsKeyword" />
+ <itemData name="Decimal" defStyleNum="dsDecVal" />
+ <itemData name="Float" defStyleNum="dsFloat" />
+ <itemData name="Char" defStyleNum="dsChar" />
+ <itemData name="String" defStyleNum="dsString" />
+ <itemData name="Comment" defStyleNum="dsComment" />
+ <itemData name="Assertions" defStyleNum="dsOthers" />
+ </itemDatas>
+ </highlighting>
+ <general>
+ <comments>
+ <comment name="singleLine" start="--" />
+ </comments>
+ <keywords casesensitive="1" />
+ </general>
+</language>
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE language SYSTEM "language.dtd">
+<language name="ferite" version="1.02" kateversion="2.1" section="Scripts" extensions="*.fe;*.feh" mimetype="text/x-ferite-src">
+ <highlighting>
+ <list name="keywords">
+ <item> break </item>
+ <item> case </item>
+ <item> continue </item>
+ <item> else </item>
+ <item> for </item>
+ <item> if </item>
+ <item> do </item>
+ <item> function </item>
+ <item> namespace </item>
+ <item> while </item>
+ <item> class </item>
+ <item> new </item>
+ <item> uses </item>
+ <item> global </item>
+ <item> return </item>
+ <item> self </item>
+ <item> super </item>
+ <item> null </item>
+ <item> iferr </item>
+ <item> fix </item>
+ </list>
+ <list name="types">
+ <item> number </item>
+ <item> void </item>
+ <item> string </item>
+ <item> array </item>
+ <item> object </item>
+ <item> final </item>
+ <item> static </item>
+ </list>
+ <contexts>
+ <context attribute="Normal Text" lineEndContext="#stay" name="Default" >
+ <keyword attribute="Keyword" context="#stay" String="keywords"/>
+ <keyword attribute="Data Type" context="#stay" String="types"/>
+ <Float attribute="Float" context="#stay">
+ <AnyChar String="fF" attribute="Float" context="#stay"/>
+ </Float>
+ <HlCOct attribute="Octal" context="#stay" />
+ <HlCHex attribute="Hex" context="#stay" />
+ <Int attribute="Decimal" context="#stay" >
+ <StringDetect attribute="Decimal" context="#stay" String="ULL" insensitive="TRUE"/>
+ <StringDetect attribute="Decimal" context="#stay" String="LUL" insensitive="TRUE"/>
+ <StringDetect attribute="Decimal" context="#stay" String="LLU" insensitive="TRUE"/>
+ <StringDetect attribute="Decimal" context="#stay" String="UL" insensitive="TRUE"/>
+ <StringDetect attribute="Decimal" context="#stay" String="LU" insensitive="TRUE"/>
+ <StringDetect attribute="Decimal" context="#stay" String="LL" insensitive="TRUE"/>
+ <StringDetect attribute="Decimal" context="#stay" String="U" insensitive="TRUE"/>
+ <StringDetect attribute="Decimal" context="#stay" String="L" insensitive="TRUE"/>
+ </Int>
+ <HlCChar attribute="Char" context="#stay"/>
+ <DetectChar attribute="String" context="String" char="&quot;"/>
+ <Detect2Chars attribute="Comment" context="Comment" char="/" char1="/"/>
+ <Detect2Chars attribute="Comment" context="Multiline Comment" char="/" char1="*"/>
+ <AnyChar attribute="Symbol" context="#stay" String="!%&amp;()+,-&lt;=&gt;?[]^{|}~"/>
+ <StringDetect attribute="Comment" context="Undeffed" String="#if 0" insensitive="FALSE"/>
+ <RegExpr attribute="Preprocessor" context="Preprocessor" String="^#"/>
+ </context>
+ <context attribute="String" lineEndContext="#pop" name="String">
+ <LineContinue attribute="String" context="#stay"/>
+ <HlCStringChar attribute="String Char" context="#stay"/>
+ <DetectChar attribute="String" context="#pop" char="&quot;"/>
+ </context>
+ <context attribute="Comment" lineEndContext="#pop" name="Comment">
+ <RegExpr attribute="Decimal" context="#stay" String="(FIXME|TODO)" />
+ </context>
+ <context attribute="Comment" lineEndContext="#stay" name="Multiline Comment">
+ <Detect2Chars attribute="Comment" context="#pop" char="*" char1="/"/>
+ <RegExpr attribute="Decimal" context="#stay" String="(FIXME|TODO)" />
+ </context>
+ <context attribute="Preprocessor" lineEndContext="#pop" name="Preprocessor">
+ <LineContinue attribute="Preprocessor" context="#stay"/>
+ <RangeDetect attribute="Prep. Lib" context="#stay" char="&quot;" char1="&quot;"/>
+ <RangeDetect attribute="Prep. Lib" context="#stay" char="&lt;" char1="&gt;"/>
+ <Detect2Chars attribute="Comment" context="Comment" char="/" char1="/"/>
+ <Detect2Chars attribute="Comment" context="Multiline Comment 2" char="/" char1="*"/>
+ </context>
+ <context attribute="Comment" lineEndContext="#stay" name="Multiline Comment 2">
+ <Detect2Chars attribute="Comment" context="#pop" char="*" char1="/" />
+ </context>
+ <context attribute="Normal Text" lineEndContext="#pop" name="unknown" />
+ <context attribute="Normal Text" lineEndContext="#pop" name ="unknown 2" />
+ <context attribute="Comment" lineEndContext="#stay" name="Undeffed">
+ <RegExpr attribute="Decimal" context="#stay" String="(FIXME|TODO)" />
+ <RegExpr attribute="Comment" context="#pop" String="^#endif" />
+ </context>
+ </contexts>
+ <itemDatas>
+ <itemData name="Normal Text" defStyleNum="dsNormal"/>
+ <itemData name="Keyword" defStyleNum="dsKeyword"/>
+ <itemData name="Data Type" defStyleNum="dsDataType"/>
+ <itemData name="Decimal" defStyleNum="dsDecVal"/>
+ <itemData name="Octal" defStyleNum="dsBaseN"/>
+ <itemData name="Hex" defStyleNum="dsBaseN"/>
+ <itemData name="Float" defStyleNum="dsFloat"/>
+ <itemData name="Char" defStyleNum="dsChar"/>
+ <itemData name="String" defStyleNum="dsString"/>
+ <itemData name="String Char" defStyleNum="dsChar"/>
+ <itemData name="Comment" defStyleNum="dsComment"/>
+ <itemData name="Symbol" defStyleNum="dsNormal"/>
+ <itemData name="Preprocessor" defStyleNum="dsOthers"/>
+ <itemData name="Prep. Lib" defStyleNum="dsOthers"/>
+ </itemDatas>
+ </highlighting>
+ <general>
+ <comments>
+ <comment name="singleLine" start="//" />
+ <comment name="multiLine" start="/*" end="*/" />
+ </comments>
+ <keywords casesensitive="1" />
+ </general>
+</language>
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE language SYSTEM "language.dtd">
+<language name="Fortran" version="1.04" kateversion="2.1" section="Sources" extensions="*.f;*.F;*.for;*.FOR;*.f90;*.F90;*.fpp;*.FPP" mimetype="text/x-fortran-src">
+ <highlighting>
+ <list name="keywords">
+ <item> common </item>
+ <item> continue </item>
+ <item> block </item>
+ <item> data </item>
+ <item> date </item>
+ <item> function </item>
+ <item> include </item>
+ <item> parameter </item>
+ <item> implicit </item>
+ <item> none </item>
+ <item> equivalence </item>
+ <item> if </item>
+ <item> then </item>
+ <item> else </item>
+ <item> elseif </item>
+ <item> endif </item>
+ <item> go </item>
+ <item> to </item>
+ <item> goto </item>
+ <item> program </item>
+ <item> subroutine </item>
+ <item> end </item>
+ <item> call </item>
+ <item> while </item>
+ <item> cycle </item>
+ <item> do </item>
+ <item> enddo </item>
+ <item> for </item>
+ <item> break </item>
+ <item> pause </item>
+ <item> return </item>
+ <item> stop </item>
+ </list>
+ <list name="io_functions">
+ <item> access </item>
+ <item> backspace </item>
+ <item> close </item>
+ <item> inquire </item>
+ <item> open </item>
+ <item> print </item>
+ <item> read </item>
+ <item> rewind </item>
+ <item> write </item>
+ <item> format </item>
+ </list>
+ <list name="maths">
+ <item> abs </item>
+ <item> acos </item>
+ <item> aimag </item>
+ <item> aint </item>
+ <item> alog </item>
+ <item> alog10 </item>
+ <item> amax0 </item>
+ <item> amax1 </item>
+ <item> amin0 </item>
+ <item> amin1 </item>
+ <item> amod </item>
+ <item> anint </item>
+ <item> aprime </item>
+ <item> asin </item>
+ <item> atan </item>
+ <item> atan2 </item>
+ <item> acos </item>
+ <item> cabs </item>
+ <item> cexp </item>
+ <item> clog </item>
+ <item> conjg </item>
+ <item> cos </item>
+ <item> cosh </item>
+ <item> ccos </item>
+ <item> csin </item>
+ <item> csqrt </item>
+ <item> dabs </item>
+ <item> dacos </item>
+ <item> dasin </item>
+ <item> datan </item>
+ <item> datan2 </item>
+ <item> dconjg </item>
+ <item> dcos </item>
+ <item> dcosh </item>
+ <item> dfloat </item>
+ <item> ddmim </item>
+ <item> dexp </item>
+ <item> dim </item>
+ <item> dint </item>
+ <item> dlog </item>
+ <item> dlog10 </item>
+ <item> dmax1 </item>
+ <item> dmin1 </item>
+ <item> dmod </item>
+ <item> dnint </item>
+ <item> dsign </item>
+ <item> dsin </item>
+ <item> dsinh </item>
+ <item> dsqrt </item>
+ <item> dtan </item>
+ <item> dtanh </item>
+ <item> exp </item>
+ <item> iabs </item>
+ <item> idim </item>
+ <item> index </item>
+ <item> isign </item>
+ <item> len </item>
+ <item> log </item>
+ <item> log10 </item>
+ <item> max </item>
+ <item> max0 </item>
+ <item> max1 </item>
+ <item> min </item>
+ <item> min0 </item>
+ <item> min1 </item>
+ <item> mod </item>
+ <item> rand </item>
+ <item> sign </item>
+ <item> sin </item>
+ <item> sinh </item>
+ <item> sqrt </item>
+ <item> tan </item>
+ <item> tanh </item>
+ </list>
+ <list name="types">
+ <item> character </item>
+ <item> complex </item>
+ <item> double </item>
+ <item> precision </item>
+ <item> real </item>
+ <item> real*8 </item>
+ <item> integer </item>
+ <item> logical </item>
+ <item> dimension </item>
+ <item> external </item>
+ <item> intrinsic </item>
+ <item> save </item>
+
+ <item> char </item>
+ <item> cmplx </item>
+ <item> dble </item>
+ <item> dcmplx </item>
+ <item> float </item>
+ <item> ichar </item>
+ <item> idint </item>
+ <item> ifix </item>
+ <item> int </item>
+ <item> sngl </item>
+ </list>
+ <contexts>
+ <context attribute="Normal Text" lineEndContext="#stay" name="default" >
+ <keyword attribute="Keyword" context="#stay" String="keywords"/>
+ <keyword attribute="Data Type" context="#stay" String="types"/>
+ <keyword attribute="IO Function" context="#stay" String="io_functions"/>
+ <keyword attribute="Math Intrinsic" context="#stay" String="maths"/>
+ <RegExpr attribute="Conditional" context="#stay" String="\.(and|AND|or|OR|eqv|EQV|neqv|NEQV|lt|LT|le|LE|eq|EQ|ne|NE|eqv|EQV|neqv|NEQV|ge|GE|gt|GT|not|NOT|true|TRUE|false|FALSE)\."/>
+ <RegExpr attribute="Float" context="#stay" String="[0-9]*\.[0-9]*[dD][+|-]{0,1}[0-9]*"/>
+ <Float attribute="Float" context="#stay">
+ <AnyChar attribute="Float" context="#stay" String="fFdD"/>
+ </Float>
+ <Int attribute="Decimal" context="#stay" />
+ <HlCChar attribute="Char" context="#stay"/>
+ <DetectChar attribute="String" context="strings 1" char="&quot;"/>
+ <DetectChar attribute="String" context="strings 2" char="&apos;"/>
+ <RegExpr attribute="Preprocessor" context="#stay" String="^(#|cDEC\$|CDEC\$).*$"/>
+ <RegExpr attribute="Comment" context="#stay" String="^[cC\*].*$"/>
+ <RegExpr attribute="Comment" context="#stay" String="!.*$"/>
+ <AnyChar attribute="Symbol" context="#stay" String="!%&amp;()+,-&lt;=&gt;?[]^{|}~"/>
+ </context>
+ <context attribute="String" lineEndContext="#pop" name="strings 1">
+ <HlCStringChar attribute="String Char" context="#stay"/>
+ <DetectChar attribute="String" context="#pop" char="&quot;"/>
+ </context>
+ <context attribute="String" lineEndContext="#pop" name="strings 2">
+ <HlCStringChar attribute="String Char" context="#stay"/>
+ <DetectChar attribute="String" context="#pop" char="&apos;"/>
+ </context>
+ </contexts>
+ <itemDatas>
+ <itemData name="Normal Text" defStyleNum="dsNormal"/>
+ <itemData name="Keyword" defStyleNum="dsKeyword"/>
+ <itemData name="Data Type" defStyleNum="dsDataType"/>
+ <itemData name="Decimal" defStyleNum="dsDecVal"/>
+ <itemData name="Octal" defStyleNum="dsBaseN"/>
+ <itemData name="Hex" defStyleNum="dsBaseN"/>
+ <itemData name="Float" defStyleNum="dsFloat"/>
+ <itemData name="Char" defStyleNum="dsChar"/>
+ <itemData name="String" defStyleNum="dsString"/>
+ <itemData name="String Char" defStyleNum="dsChar"/>
+ <itemData name="Comment" defStyleNum="dsComment"/>
+ <itemData name="Symbol" defStyleNum="dsNormal"/>
+ <itemData name="Preprocessor" defStyleNum="dsOthers"/>
+ <itemData name="Conditional" defStyleNum="dsOthers" color="#008000" selColor="#ffa0a0" bold="1" italic="0"/>
+ <itemData name="IO Function" defStyleNum="dsKeyword" color="#006060" selColor="#ffffff" bold="0" italic="0"/>
+ <itemData name="Math Intrinsic" defStyleNum="dsKeyword" color="#600060" selColor="#ffa0ff" bold="1" italic="0"/>
+ </itemDatas>
+ </highlighting>
+ <general>
+ <keywords casesensitive="0"/>
+ <comments>
+ <comment name="singleLine" start="c"/>
+ <comment name="singleLine" start="!"/>
+ </comments>
+ </general>
+</language>
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE language SYSTEM "language.dtd">
+<language name="GDL" version="1.0" kateversion="2.0" section="Sources" extensions="*.gdl;*.vcg;*.GDL;*.VCG" mimetype="">
+ <highlighting>
+ <list name="colors">
+ <item> white </item>
+ <item> blue </item>
+ <item> red </item>
+ <item> green </item>
+ <item> yellow </item>
+ <item> magenta </item>
+ <item> cyan </item>
+ <item> darkgrey </item>
+ <item> darkgray </item>
+ <item> darkblue </item>
+ <item> darkred </item>
+ <item> darkgreen </item>
+ <item> darkyellow </item>
+ <item> darkmagenta </item>
+ <item> darkcyan </item>
+ <item> gold </item>
+ <item> lightgrey </item>
+ <item> lightgray </item>
+ <item> lightblue </item>
+ <item> lightred </item>
+ <item> lightgreen </item>
+ <item> lightyellow </item>
+ <item> lightmagenta </item>
+ <item> lightcyan </item>
+ <item> lilac </item>
+ <item> turquoise </item>
+ <item> aquamarine </item>
+ <item> khaki </item>
+ <item> purple </item>
+ <item> yellowgreen </item>
+ <item> pink </item>
+ <item> orange </item>
+ <item> orchid </item>
+ <item> black </item>
+ </list>
+ <list name="shapes">
+ <item> box </item>
+ <item> triangle </item>
+ <item> circle </item>
+ <item> ellipse </item>
+ <item> hexagon </item>
+ <item> rhomb </item>
+ <item> rhomboid </item>
+ <item> trapeze </item>
+ <item> uptrapeze </item>
+ <item> trapezoid </item>
+ <item> uptrapezoid </item>
+ <item> lparallelogram </item>
+ <item> rparallelogram </item>
+ </list>
+ <list name="states">
+ <item> unfolded </item>
+ <item> folded </item>
+ <item> boxed </item>
+ <item> clustered </item>
+ <item> wrapped </item>
+ <item> exclusive </item>
+ <item> white </item>
+ </list>
+ <list name="algorithms">
+ <item> normal </item>
+ <item> tree </item>
+ <item> forcedir </item>
+ <item> dfs </item>
+ <item> minbackward </item>
+ <item> maxdepth </item>
+ <item> maxdepthslow </item>
+ <item> mindepth </item>
+ <item> mindepthslow </item>
+ <item> minindegree </item>
+ <item> minoutdegree </item>
+ <item> maxindegree </item>
+ <item> maxoutdegree </item>
+ <item> maxdegree </item>
+ <item> mindegree </item>
+ </list>
+ <list name="forcedir">
+ <item> attraction </item>
+ <item> repulsion </item>
+ <item> randomfactor </item>
+ <item> randomimpulse </item>
+ <item> randomrounds </item>
+ <item> tempscheme </item>
+ <item> temptreshold </item>
+ <item> tempmin </item>
+ <item> tempmax </item>
+ </list>
+ <list name="magnetic">
+ <item> no </item>
+ <item> polar </item>
+ <item> circular </item>
+ <item> polcircular </item>
+ <item> orthogonal </item>
+ </list>
+ <list name="orientation">
+ <item> toptobottom </item>
+ <item> bottomtotop </item>
+ <item> lefttoright </item>
+ <item> righttoleft </item>
+ <item> top_to_bottom </item>
+ <item> bottom_to_top </item>
+ <item> left_to_right </item>
+ <item> right_to_left </item>
+ </list>
+ <list name="lines">
+ <item> solid </item>
+ <item> continuous </item>
+ <item> dashed </item>
+ <item> dotted </item>
+ <item> double </item>
+ <item> triple </item>
+ <item> invisible </item>
+ </list>
+ <list name="fisheye">
+ <item> pfish </item>
+ <item> cfish </item>
+ <item> fpfish </item>
+ <item> fcfish </item>
+ <item> dpfish </item>
+ <item> dcfish </item>
+ </list>
+ <contexts>
+ <context name="default" attribute="Normal" lineEndContext="#stay">
+ <Detect2Chars attribute="Comment" context="ccomment" char="/" char1="/"/>
+ <Detect2Chars attribute="Comment" context="cppcomment" char="/" char1="*"/>
+ <StringDetect attribute="Keyword" context="#stay" String="focus"/>
+ <RegExpr attribute="Keyword" context="#stay" String="(graph|edge|node|region|backedge|(left|right|)(bent|)nearedge):"/>
+ <RegExpr attribute="Value" context="#stay" String="loc *:"/>
+ <StringDetect attribute="Value" context="centry" String="colorentry"/>
+ <RegExpr attribute="Value" context="arrowmode" String="arrow_?mode *:"/>
+ <RegExpr attribute="Value" context="colorid" String="(foldnode.|node.|)(text|border|)color *:"/>
+ <RegExpr attribute="Value" context="colorid" String="(foldedge.|edge.|)(arrow|backarrow|)color *:"/>
+ <RegExpr attribute="Value" context="arrow" String="(foldedge.|edge.|)(arrow|backarrow)style *:"/>
+ <RegExpr attribute="Value" context="lineid" String="(foldedge.|edge.|)linestyle *:"/>
+ <RegExpr attribute="Value" context="lineid" String="(foldnode.|node.|)borderstyle *:"/>
+ <RegExpr attribute="Value" context="fishid" String="view *:"/>
+ <RegExpr attribute="Value" context="shapeid" String="(foldnode.|node.|)shape"/>
+ <RegExpr attribute="Value" context="lquote" String="(source|target)(name|)"/>
+ <RegExpr attribute="Value" context="lquote" String="title *:"/>
+ <RegExpr attribute="Value" context="lquote" String="(foldnode.|node.|foldedge.|edge.|)label *:"/>
+ <RegExpr attribute="Value" context="fontlq" String="(foldnode.|node.|foldedge.|edge.|)fontname *:"/>
+ <RegExpr attribute="Value" context="lquote" String="infoname(1|2|3) *:"/>
+ <RegExpr attribute="Value" context="lquote" String="(foldnode.|node.|)info(1|2|3) *:"/>
+ <RegExpr attribute="Value" context="intval" String="spreadlevel *:"/>
+ <RegExpr attribute="Value" context="nodelevel" String="(foldnode.|node.|)(level|vertical_?order) *:"/>
+ <RegExpr attribute="Value" context="intval" String="(foldnode.|node.|foldedge.|edge.|)horizontal_?order *:"/>
+ <RegExpr attribute="Value" context="stateid" String="stat(e|us) *:"/>
+ <RegExpr attribute="Value" context="algid" String="layout_?algorithm *:"/>
+ <RegExpr attribute="Value" context="boolean" String="crossing_?optimization *:"/>
+ <RegExpr attribute="Value" context="boolean" String="crossing_?phase2 *:"/>
+ <RegExpr attribute="Value" context="boolean" String="(dirty_edge_|display_edge_|displayedge|late_edge_|subgraph_?)labels *:"/>
+ <RegExpr attribute="Value" context="boolean" String="s?manhatt(a|e)n_?edges *:"/>
+ <RegExpr attribute="Value" context="boolean" String="(nodes|near_?edges|edges|splines) *:"/>
+ <RegExpr attribute="Value" context="classname" String="classname"/>
+ <RegExpr attribute="Value" context="orient" String="orientation *:"/>
+ <RegExpr attribute="Value" context="nodealign" String="node_alignment *:"/>
+ <RegExpr attribute="Value" context="textmode" String="(foldnode.|node.|)textmode *:"/>
+ <RegExpr attribute="Value" context="boolean" String="equal_y_dist *:"/>
+ <RegExpr attribute="Value" context="boolean" String="equal_?ydist *:"/>
+ <RegExpr attribute="Value" context="weight" String="crossing_?weight *:"/>
+ <RegExpr attribute="Value" context="boolean" String="(fast_?|)icons *:"/>
+ <RegExpr attribute="Value" context="boolean" String="fine_?tuning *:"/>
+ <RegExpr attribute="Value" context="boolean" String="(f?straight_?|priority_)phase *:"/>
+ <RegExpr attribute="Value" context="boolean" String="ignore_?singles *:"/>
+ <RegExpr attribute="Value" context="boolean" String="(in|out|)port_?sharing *:"/>
+ <RegExpr attribute="Value" context="boolean" String="linear_?segments *:"/>
+ <RegExpr attribute="Value" context="intval" String="(foldnode.|node.|)(height|width|borderwidth|stretch|shrink) *:"/>
+ <RegExpr attribute="Value" context="intval" String="(foldedge.|edge.|)(arrowsize|backarrowsize|thickness|class|priority) *:"/>
+ <RegExpr attribute="Value" context="intval" String="anchor *:"/>
+ <RegExpr attribute="Value" context="intval" String="iconcolors *:"/>
+ <RegExpr attribute="Value" context="intval" String="hidden *:"/>
+ <RegExpr attribute="Value" context="boolean" String="energetic *:"/>
+ <RegExpr attribute="Value" context="intval" String="layout_(up|down|near|spline)factor *:"/>
+ <RegExpr attribute="Value" context="intval" String="border +(x|y) *:"/>
+ <RegExpr attribute="Value" context="intval" String="splinefactor *:"/>
+ <RegExpr attribute="Value" context="floatval" String="(gravity|tempfactor|treefactor) *:"/>
+ <RegExpr attribute="Value" context="intval" String="(xspace|xbase|xmax|xraster|x) *:"/>
+ <RegExpr attribute="Value" context="intval" String="(yspace|ybase|ymax|yraster|y) *:"/>
+ <RegExpr attribute="Value" context="intval" String="(xlraster|xlspace) *:"/>
+ <RegExpr attribute="Value" context="intval" String="magnetic_force(1|2) *:"/>
+ <RegExpr attribute="Value" context="magnor" String="magnetic_field(1|2) *:"/>
+ <RegExpr attribute="Value" context="intval" String="(a|b|c|fd|p|r|s)(max) *:"/>
+ <RegExpr attribute="Value" context="intval" String="(c|p|r)(min) *:"/>
+ <keyword attribute="Value" context="intval" String="forcedir"/>
+ <RegExpr attribute="Value" context="scaling" String="scaling *:"/>
+ <RegExpr attribute="Value" context="lquote" String="useraction(name|cmd)(1|2|3|4) *:"/>
+ </context>
+ <context name="string" attribute="String" lineEndContext="#stay">
+ <DetectChar attribute="String" context="default" char="&quot;"/>
+ <Detect2Chars attribute="Char" context="#stay" char="\" char1="&quot;"/>
+ <RegExpr attribute="Char" context="#stay" String="\\(n|a|t|b)"/>
+ <RegExpr attribute="Char" context="#stay" String="\\fi(0|1|2)[0-9][0-9]"/>
+ <RegExpr attribute="Char" context="#stay" String="\\f(u|I|b|B|n|[0-9][0-9])"/>
+ </context>
+ <context name="scaling" attribute="Normal" lineEndContext="#stay">
+ <StringDetect attribute="Datatype" context="default" String="maxspect"/>
+ <Float attribute="Float" context="default"/>
+ </context>
+ <context name="ccomment" attribute="Comment" lineEndContext="default">
+ </context>
+ <context name="cppcomment" attribute="Comment" lineEndContext="#stay">
+ <Detect2Chars attribute="Comment" context="default" char="*" char1="/"/>
+ </context>
+ <context name="colorid" attribute="Normal" lineEndContext="#stay">
+ <keyword attribute="Datatype" context="default" String="colors"/>
+ <RegExpr attribute="Datatype" context="default" String="[0-9][0-9]?"/>
+ </context>
+ <context name="shapeid" attribute="Normal" lineEndContext="#stay">
+ <keyword attribute="Datatype" context="default" String="shapes"/>
+ </context>
+ <context name="lquote" attribute="Normal" lineEndContext="#stay">
+ <DetectChar attribute="String" context="string" char="&quot;"/>
+ </context>
+ <context name="stateid" attribute="Normal" lineEndContext="#stay">
+ <keyword attribute="Datatype" context="default" String="states"/>
+ </context>
+ <context name="algid" attribute="Normal" lineEndContext="#stay">
+ <keyword attribute="Datatype" context="default" String="algorithms"/>
+ </context>
+ <context name="fishid" attribute="Normal" lineEndContext="#stay">
+ <keyword attribute="Datatype" context="default" String="fisheye"/>
+ </context>
+ <context name="boolean" attribute="Normal" lineEndContext="#stay">
+ <RegExpr attribute="Datatype" context="default" String="(yes|no)"/>
+ </context>
+ <context name="lineid" attribute="Normal" lineEndContext="#stay">
+ <keyword attribute="Datatype" context="default" String="lines"/>
+ </context>
+ <context name="intval" attribute="Normal" lineEndContext="#stay">
+ <Int attribute="Integer" context="longint"/>
+ </context>
+ <context name="longint" attribute="Normal" lineEndContext="default">
+ <Int attribute="Integer" context="longint"/>
+ <RegExpr attribute="Normal" context="default" String="\ "/>
+ </context>
+ <context name="centry" attribute="Normal" lineEndContext="default">
+ <RegExpr attribute="Value" context="cecolon" String="[0-9][0-9]?"/>
+ </context>
+ <context name="rgb" attribute="Normal" lineEndContext="#stay">
+ <RegExpr attribute="Integer" context="default" String="[0-9][0-9]?[0-9]? +[0-9][0-9]?[0-9]? +[0-9][0-9]?[0-9]?"/>
+ </context>
+ <context name="floatval" attribute="Normal" lineEndContext="#stay">
+ <Float attribute="Float" context="default"/>
+ </context>
+ <context name="fontlq" attribute="Normal" lineEndContext="#stay">
+ <DetectChar attribute="Datatype" context="fontbase" char="&quot;"/>
+ </context>
+ <context name="fontbase" attribute="Normal" lineEndContext="#stay">
+ <RegExpr attribute="Datatype" context="fontsize" String="((tim|ncen)(R|B|I|BI)|(cour|helv)(R|B|O|BO)|symb)"/>
+ </context>
+ <context name="fontsize" attribute="Normal" lineEndContext="#stay">
+ <RegExpr attribute="Datatype" context="#stay" String="(08|10|12|14|18|24)(.vcf|)"/>
+ <DetectChar attribute="Datatype" context="default" char="&quot;"/>
+ </context>
+ <context name="arrow" attribute="Normal" lineEndContext="#stay">
+ <RegExpr attribute="Datatype" context="default" String="(solid|line|none)"/>
+ </context>
+ <context name="arrowmode" attribute="Normal" lineEndContext="#stay">
+ <RegExpr attribute="Datatype" context="default" String="(free|fixed)"/>
+ </context>
+ <context name="magnor" attribute="Normal" lineEndContext="#stay">
+ <keyword attribute="Datatype" context="default" String="magnetic"/>
+ <keyword attribute="Datatype" context="default" String="orientation"/>
+ </context>
+ <context name="orient" attribute="Normal" lineEndContext="#stay">
+ <keyword attribute="Datatype" context="default" String="orientation"/>
+ </context>
+ <context name="nodealign" attribute="Normal" lineEndContext="#stay">
+ <RegExpr attribute="Datatype" context="default" String="(top|center|bottom)"/>
+ </context>
+ <context name="nodelevel" attribute="Normal" lineEndContext="#stay">
+ <StringDetect attribute="Datatype" context="default" String="maxlevel"/>
+ <Int attribute="Integer" context="longint"/>
+ </context>
+ <context name="classname" attribute="Normal" lineEndContext="#stay">
+ <RegExpr attribute="Value" context="#stay" String="[0-9]+"/>
+ <DetectChar attribute="Value" context="lquote" char=":"/>
+ </context>
+ <context name="cecolon" attribute="Normal" lineEndContext="#stay">
+ <DetectChar attribute="Value" context="rgb" char=":"/>
+ </context>
+ <context name="weight" attribute="Normal" lineEndContext="#stay">
+ <RegExpr attribute="Datatype" context="default" String="(medianbary|barymedian|bary|median)"/>
+ </context>
+ <context name="textmode" attribute="Normal" lineEndContext="#stay">
+ <RegExpr attribute="Datatype" context="default" String="(center|left_justify|right_justify)"/>
+ </context>
+ </contexts>
+ <itemDatas>
+ <itemData name="Normal" defStyleNum="dsNormal"/>
+ <itemData name="String" defStyleNum="dsString"/>
+ <itemData name="Char" defStyleNum="dsChar"/>
+ <itemData name="Keyword" defStyleNum="dsKeyword"/>
+ <itemData name="Integer" defStyleNum="dsDecVal"/>
+ <itemData name="Datatype" defStyleNum="dsDataType"/>
+ <itemData name="Comment" defStyleNum="dsComment"/>
+ <itemData name="Value" defStyleNum="dsOthers"/>
+ <itemData name="Float" defStyleNum="dsFloat"/>
+ </itemDatas>
+ </highlighting>
+ <general>
+ <comments>
+ <comment name="singleLine" start="//" />
+ <comment name="multiLine" start="/*" end="*/"/>
+ </comments>
+ <keywords casesensitive="1"/>
+ </general>
+</language>
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE language SYSTEM "language.dtd">
+<!--
+
+*************************************************************************
+* *
+* Syntax highlighting for the GNU Assembler *
+* Copyright (C) 2002, John Zaitseff *
+* *
+*************************************************************************
+
+Updated: Roland Pabel <roland@pabel.name>
+Date: 15th August, 2002
+Version: 1.01
+
+Author: John Zaitseff <J.Zaitseff@zap.org.au>
+Date: 15th April, 2002
+Version: 1.0
+
+This file contains the XML syntax highlighting description for the GNU
+Assembler, for KATE, the KDE Advanced Editor. Keywords have been taken
+directly from the GNU Assembler source code (read.c).
+
+Known problems: Floating point highlighting does not work correctly.
+
+This program, including associated files, is free software. You may
+distribute it and/or modify it under the terms of the GNU General Public
+License as published by the Free Software Foundation; either Version 2 of
+the license, 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.,
+59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+-->
+
+<language name="GNU Assembler" version="1.04" kateversion="2.1" section="Sources"
+ extensions="*.s;*.S" mimetype="text/x-asm"
+ author="John Zaitseff (J.Zaitseff@zap.org.au), Roland Pabel (roland@pabel.name)" license="GPL">
+ <highlighting>
+ <list name="keywords">
+ <item>.abort</item>
+ <item>.align</item>
+ <item>.appfile</item>
+ <item>.appline</item>
+ <item>.ascii</item>
+ <item>.asciz</item>
+ <item>.balign</item>
+ <item>.balignl</item>
+ <item>.balignw</item>
+ <item>.byte</item>
+ <item>.comm</item>
+ <item>.common.s</item>
+ <item>.common</item>
+ <item>.data</item>
+ <item>.dc.b</item>
+ <item>.dc.d</item>
+ <item>.dc.l</item>
+ <item>.dc.s</item>
+ <item>.dc.w</item>
+ <item>.dc.x</item>
+ <item>.dc</item>
+ <item>.dcb.b</item>
+ <item>.dcb.d</item>
+ <item>.dcb.l</item>
+ <item>.dcb.s</item>
+ <item>.dcb.w</item>
+ <item>.dcb.x</item>
+ <item>.dcb</item>
+ <item>.debug</item>
+ <item>.def</item>
+ <item>.desc</item>
+ <item>.dim</item>
+ <item>.double</item>
+ <item>.ds.b</item>
+ <item>.ds.d</item>
+ <item>.ds.l</item>
+ <item>.ds.p</item>
+ <item>.ds.s</item>
+ <item>.ds.w</item>
+ <item>.ds.x</item>
+ <item>.ds</item>
+ <item>.dsect</item>
+ <item>.eject</item>
+ <item>.else</item>
+ <item>.elsec</item>
+ <item>.elseif</item>
+ <item>.end</item>
+ <item>.endc</item>
+ <item>.endef</item>
+ <item>.endfunc</item>
+ <item>.endif</item>
+ <item>.endm</item>
+ <item>.endr</item>
+ <item>.equ</item>
+ <item>.equiv</item>
+ <item>.err</item>
+ <item>.exitm</item>
+ <item>.extend</item>
+ <item>.extern</item>
+ <item>.fail</item>
+ <item>.file</item>
+ <item>.fill</item>
+ <item>.float</item>
+ <item>.format</item>
+ <item>.func</item>
+ <item>.global</item>
+ <item>.globl</item>
+ <item>.hidden</item>
+ <item>.hword</item>
+ <item>.ident</item>
+ <item>.if</item>
+ <item>.ifc</item>
+ <item>.ifdef</item>
+ <item>.ifeq</item>
+ <item>.ifeqs</item>
+ <item>.ifge</item>
+ <item>.ifgt</item>
+ <item>.ifle</item>
+ <item>.iflt</item>
+ <item>.ifnc</item>
+ <item>.ifndef</item>
+ <item>.ifne</item>
+ <item>.ifnes</item>
+ <item>.ifnotdef</item>
+ <item>.include</item>
+ <item>.int</item>
+ <item>.internal</item>
+ <item>.irep</item>
+ <item>.irepc</item>
+ <item>.irp</item>
+ <item>.irpc</item>
+ <item>.lcomm</item>
+ <item>.lflags</item>
+ <item>.line</item>
+ <item>.linkonce</item>
+ <item>.list</item>
+ <item>.llen</item>
+ <item>.ln</item>
+ <item>.long</item>
+ <item>.lsym</item>
+ <item>.macro</item>
+ <item>.mexit</item>
+ <item>.name</item>
+ <item>.noformat</item>
+ <item>.nolist</item>
+ <item>.nopage</item>
+ <item>.octa</item>
+ <item>.offset</item>
+ <item>.org</item>
+ <item>.p2align</item>
+ <item>.p2alignl</item>
+ <item>.p2alignw</item>
+ <item>.page</item>
+ <item>.plen</item>
+ <item>.popsection</item>
+ <item>.previous</item>
+ <item>.print</item>
+ <item>.protected</item>
+ <item>.psize</item>
+ <item>.purgem</item>
+ <item>.pushsection</item>
+ <item>.quad</item>
+ <item>.rep</item>
+ <item>.rept</item>
+ <item>.rva</item>
+ <item>.sbttl</item>
+ <item>.scl</item>
+ <item>.sect.s</item>
+ <item>.sect</item>
+ <item>.section.s</item>
+ <item>.section</item>
+ <item>.set</item>
+ <item>.short</item>
+ <item>.single</item>
+ <item>.size</item>
+ <item>.skip</item>
+ <item>.sleb128</item>
+ <item>.space</item>
+ <item>.spc</item>
+ <item>.stabd</item>
+ <item>.stabn</item>
+ <item>.stabs</item>
+ <item>.string</item>
+ <item>.struct</item>
+ <item>.subsection</item>
+ <item>.symver</item>
+ <item>.tag</item>
+ <item>.text</item>
+ <item>.title</item>
+ <item>.ttl</item>
+ <item>.type</item>
+ <item>.uleb128</item>
+ <item>.use</item>
+ <item>.val</item>
+ <item>.version</item>
+ <item>.vtable_entry</item>
+ <item>.vtable_inherit</item>
+ <item>.weak</item>
+ <item>.word</item>
+ <item>.xcom</item>
+ <item>.xdef</item>
+ <item>.xref</item>
+ <item>.xstabs</item>
+ <item>.zero</item>
+ <!-- Directives specific to ARM -->
+ <item>.arm</item>
+ <item>.bss</item>
+ <item>.code</item>
+ <item>.even</item>
+ <item>.force_thumb</item>
+ <item>.ldouble</item>
+ <item>.loc</item>
+ <item>.ltorg</item>
+ <item>.packed</item>
+ <item>.pool</item>
+ <item>.req</item>
+ <item>.thumb</item>
+ <item>.thumb_func</item>
+ <item>.thumb_set</item>
+ </list>
+
+ <contexts>
+ <context attribute="Normal Text" lineEndContext="#stay" name="Normal">
+ <RegExpr attribute="Label" context="#stay" String="^\s*[A-Za-z0-9_.$]+:" />
+ <keyword attribute="Keyword" context="#stay" String="keywords"/>
+ <HlCOct attribute="Octal" context="#stay" />
+ <HlCHex attribute="Hex" context="#stay" />
+ <RegExpr attribute="Binary" context="#stay" String="0[bB][01]+" />
+ <Int attribute="Decimal" context="#stay" />
+ <RegExpr attribute="Float" context="#stay" String="0[fFeEdD][-+]?[0-9]*\.?[0-9]*[eE]?[-+]?[0-9]+" />
+ <RegExpr attribute="Normal Text" context="#stay" String="[A-Za-z_.$][A-Za-z0-9_.$]*" />
+ <RegExpr attribute="Char" context="#stay" String="'(\\x[0-9a-fA-F][0-9a-fA-F]?|\\[0-7]?[0-7]?[0-7]?|\\.|.)" />
+ <DetectChar attribute="String" context="String" char="&quot;" />
+ <Detect2Chars attribute="Comment" context="Commentar 1" char="/" char1="*" />
+ <DetectChar attribute="Comment" context="Commentar 2" char="@" />
+ <DetectChar attribute="Comment" context="Commentar 2" char=";" />
+ <AnyChar attribute="Symbol" context="#stay" String="!#%&amp;*()+,-&lt;=&gt;?/:[]^{|}~" />
+ <RegExpr attribute="Preprocessor" context="Preprocessor" String="^#" />
+ </context>
+ <context attribute="Comment" lineEndContext="#stay" name="Commentar 1">
+ <Detect2Chars attribute="Comment" context="#pop" char="*" char1="/" />
+ </context>
+ <context attribute="Comment" lineEndContext="#pop" name="Commentar 2" />
+ <context attribute="Preprocessor" lineEndContext="#pop" name="Preprocessor" />
+ <context attribute="String" lineEndContext="#pop" name="String">
+ <LineContinue attribute="String" context="Some Context" />
+ <HlCStringChar attribute="String Char" context="#stay" />
+ <DetectChar attribute="String" context="#pop" char="&quot;" />
+ </context>
+ <context attribute="Normal Text" lineEndContext="#pop" name="Some Context" />
+ </contexts>
+
+ <itemDatas>
+ <itemData name="Normal Text" defStyleNum="dsNormal" />
+ <itemData name="Label" defStyleNum="dsNormal" />
+ <itemData name="Keyword" defStyleNum="dsKeyword" />
+ <itemData name="Decimal" defStyleNum="dsDecVal" />
+ <itemData name="Octal" defStyleNum="dsBaseN" />
+ <itemData name="Hex" defStyleNum="dsBaseN" />
+ <itemData name="Binary" defStyleNum="dsBaseN" />
+ <itemData name="Float" defStyleNum="dsFloat" />
+ <itemData name="Char" defStyleNum="dsChar" />
+ <itemData name="String" defStyleNum="dsString" />
+ <itemData name="String Char" defStyleNum="dsChar" />
+ <itemData name="Symbol" defStyleNum="dsNormal" />
+ <itemData name="Comment" defStyleNum="dsComment" />
+ <itemData name="Preprocessor" defStyleNum="dsOthers" />
+ </itemDatas>
+ </highlighting>
+
+ <general>
+ <comments>
+ <comment name="singleLine" start=";" />
+ <comment name="singleLine" start="@" />
+ <comment name="multiLine" start="/*" end="*/" />
+ </comments>
+ <keywords casesensitive="1" weakDeliminator="_.$" />
+ </general>
+</language>
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE language SYSTEM "language.dtd">
+<!-- Haskell syntax highlighting by Marcel Martin <mmar@freenet.de> -->
+<language name="Haskell" version="1.02" kateversion="2.1" section="Sources"
+ extensions="*.hs"
+ author="Marcel Martin (mmar@freenet.de)" license="">
+ <highlighting>
+ <list name="keywords">
+ <item> case </item>
+ <item> class </item>
+ <item> data </item>
+ <item> deriving </item>
+ <item> do </item>
+ <item> else </item>
+ <item> if </item>
+ <item> in </item>
+ <item> infixl </item>
+ <item> infixr </item>
+ <item> instance </item>
+ <item> let </item>
+ <item> module </item>
+ <item> of </item>
+ <item> primitive </item>
+ <item> then </item>
+ <item> type </item>
+ <item> where </item>
+ </list>
+ <list name="infix operators">
+ <item> quot </item>
+ <item> rem </item>
+ <item> div </item>
+ <item> mod </item>
+ <item> elem </item>
+ <item> notElem </item>
+ <item> seq </item>
+ </list>
+ <list name="functions">
+ <!--
+ These operators are not handled yet.
+ <item> !! </item>
+ <item> % </item>
+ <item> && </item>
+ <item> $! </item>
+ <item> $ </item>
+ <item> * </item>
+ <item> ** </item>
+ <item> - </item>
+ <item> . </item>
+ <item> /= </item>
+ <item> < </item>
+ <item> <= </item>
+ <item> =<< </item>
+ <item> == </item>
+ <item> > </item>
+ <item> >= </item>
+ <item> >> </item>
+ <item> >>= </item>
+ <item> ^ </item>
+ <item> ^^ </item>
+ <item> ++ </item>
+ <item> || </item>
+ //-->
+
+ <item> FilePath </item>
+ <item> IOError </item>
+ <item> abs </item>
+ <item> acos </item>
+ <item> acosh </item>
+ <item> all </item>
+ <item> and </item>
+ <item> any </item>
+ <item> appendFile </item>
+ <item> approxRational </item>
+ <item> asTypeOf </item>
+ <item> asin </item>
+ <item> asinh </item>
+ <item> atan </item>
+ <item> atan2 </item>
+ <item> atanh </item>
+ <item> basicIORun </item>
+ <item> break </item>
+ <item> catch </item>
+ <item> ceiling </item>
+ <item> chr </item>
+ <item> compare </item>
+ <item> concat </item>
+ <item> concatMap </item>
+ <item> const </item>
+ <item> cos </item>
+ <item> cosh </item>
+ <item> curry </item>
+ <item> cycle </item>
+ <item> decodeFloat </item>
+ <item> denominator </item>
+ <item> digitToInt </item>
+ <item> div </item>
+ <item> divMod </item>
+ <item> drop </item>
+ <item> dropWhile </item>
+ <item> either </item>
+ <item> elem </item>
+ <item> encodeFloat </item>
+ <item> enumFrom </item>
+ <item> enumFromThen </item>
+ <item> enumFromThenTo </item>
+ <item> enumFromTo </item>
+ <item> error </item>
+ <item> even </item>
+ <item> exp </item>
+ <item> exponent </item>
+ <item> fail </item>
+ <item> filter </item>
+ <item> flip </item>
+ <item> floatDigits </item>
+ <item> floatRadix </item>
+ <item> floatRange </item>
+ <item> floor </item>
+ <item> fmap </item>
+ <item> foldl </item>
+ <item> foldl1 </item>
+ <item> foldr </item>
+ <item> foldr1 </item>
+ <item> fromDouble </item>
+ <item> fromEnum </item>
+ <item> fromInt </item>
+ <item> fromInteger </item>
+ <item> fromIntegral </item>
+ <item> fromRational </item>
+ <item> fst </item>
+ <item> gcd </item>
+ <item> getChar </item>
+ <item> getContents </item>
+ <item> getLine </item>
+ <item> head </item>
+ <item> id </item>
+ <item> inRange </item>
+ <item> index </item>
+ <item> init </item>
+ <item> intToDigit </item>
+ <item> interact </item>
+ <item> ioError </item>
+ <item> isAlpha </item>
+ <item> isAlphaNum </item>
+ <item> isAscii </item>
+ <item> isControl </item>
+ <item> isDenormalized </item>
+ <item> isDigit </item>
+ <item> isHexDigit </item>
+ <item> isIEEE </item>
+ <item> isInfinite </item>
+ <item> isLower </item>
+ <item> isNaN </item>
+ <item> isNegativeZero </item>
+ <item> isOctDigit </item>
+ <item> isPrint </item>
+ <item> isSpace </item>
+ <item> isUpper </item>
+ <item> iterate </item>
+ <item> last </item>
+ <item> lcm </item>
+ <item> length </item>
+ <item> lex </item>
+ <item> lexDigits </item>
+ <item> lexLitChar </item>
+ <item> lines </item>
+ <item> log </item>
+ <item> logBase </item>
+ <item> lookup </item>
+ <item> map </item>
+ <item> mapM </item>
+ <item> mapM_ </item>
+ <item> max </item>
+ <item> maxBound </item>
+ <item> maximum </item>
+ <item> maybe </item>
+ <item> min </item>
+ <item> minBound </item>
+ <item> minimum </item>
+ <item> mod </item>
+ <item> negate </item>
+ <item> not </item>
+ <item> notElem </item>
+ <item> null </item>
+ <item> numerator </item>
+ <item> odd </item>
+ <item> or </item>
+ <item> ord </item>
+ <item> otherwise </item>
+ <item> pi </item>
+ <item> pred </item>
+ <item> primExitWith </item>
+ <item> print </item>
+ <item> product </item>
+ <item> properFraction </item>
+ <item> putChar </item>
+ <item> putStr </item>
+ <item> putStrLn </item>
+ <item> quot </item>
+ <item> quotRem </item>
+ <item> range </item>
+ <item> rangeSize </item>
+ <item> read </item>
+ <item> readDec </item>
+ <item> readFile </item>
+ <item> readFloat </item>
+ <item> readHex </item>
+ <item> readIO </item>
+ <item> readInt </item>
+ <item> readList </item>
+ <item> readLitChar </item>
+ <item> readLn </item>
+ <item> readOct </item>
+ <item> readParen </item>
+ <item> readSigned </item>
+ <item> reads </item>
+ <item> readsPrec </item>
+ <item> realToFrac </item>
+ <item> recip </item>
+ <item> rem </item>
+ <item> repeat </item>
+ <item> replicate </item>
+ <item> return </item>
+ <item> reverse </item>
+ <item> round </item>
+ <item> scaleFloat </item>
+ <item> scanl </item>
+ <item> scanl1 </item>
+ <item> scanr </item>
+ <item> scanr1 </item>
+ <item> seq </item>
+ <item> sequence </item>
+ <item> sequence_ </item>
+ <item> show </item>
+ <item> showChar </item>
+ <item> showInt </item>
+ <item> showList </item>
+ <item> showLitChar </item>
+ <item> showParen </item>
+ <item> showSigned </item>
+ <item> showString </item>
+ <item> shows </item>
+ <item> showsPrec </item>
+ <item> significand </item>
+ <item> signum </item>
+ <item> sin </item>
+ <item> sinh </item>
+ <item> snd </item>
+ <item> span </item>
+ <item> splitAt </item>
+ <item> sqrt </item>
+ <item> subtract </item>
+ <item> succ </item>
+ <item> sum </item>
+ <item> tail </item>
+ <item> take </item>
+ <item> takeWhile </item>
+ <item> tan </item>
+ <item> tanh </item>
+ <item> threadToIOResult </item>
+ <item> toEnum </item>
+ <item> toInt </item>
+ <item> toInteger </item>
+ <item> toLower </item>
+ <item> toRational </item>
+ <item> toUpper </item>
+ <item> truncate </item>
+ <item> uncurry </item>
+ <item> undefined </item>
+ <item> unlines </item>
+ <item> until </item>
+ <item> unwords </item>
+ <item> unzip </item>
+ <item> unzip3 </item>
+ <item> userError </item>
+ <item> words </item>
+ <item> writeFile </item>
+ <item> zip </item>
+ <item> zip3 </item>
+ <item> zipWith </item>
+ <item> zipWith3 </item>
+ </list>
+ <list name="type constructors">
+ <item> Bool </item>
+ <item> Char </item>
+ <item> Double </item>
+ <item> Either </item>
+ <item> Float </item>
+ <item> IO </item>
+ <item> Integer </item>
+ <item> Int </item>
+ <item> Maybe </item>
+ <item> Ordering </item>
+ <item> Rational </item>
+ <item> Ratio </item>
+ <item> ReadS </item>
+ <item> ShowS </item>
+ <item> String </item>
+
+ </list>
+ <list name="classes">
+ <item> Bounded </item>
+ <item> Enum </item>
+ <item> Eq </item>
+ <item> Floating </item>
+ <item> Fractional </item>
+ <item> Functor </item>
+ <item> Integral </item>
+ <item> Ix </item>
+ <item> Monad </item>
+ <item> Num </item>
+ <item> Ord </item>
+ <item> Read </item>
+ <item> RealFloat </item>
+ <item> RealFrac </item>
+ <item> Real </item>
+ <item> Show </item>
+ </list>
+ <list name="data constructors">
+ <item> EQ </item>
+ <item> False </item>
+ <item> GT </item>
+ <item> Just </item>
+ <item> LT </item>
+ <item> Left </item>
+ <item> Nothing </item>
+ <item> Right </item>
+ <item> True </item>
+ </list>
+ <contexts>
+ <context attribute="Normal Text" lineEndContext="#stay" name="normal">
+ <Detect2Chars attribute="Comment" context="comment_multi_line" char="{" char1="-" />
+ <Detect2Chars attribute="Comment" context="comment_single_line" char="-" char1="-" />
+ <keyword attribute="Keyword" context="#stay" String="keywords" />
+ <keyword attribute="Class" context="#stay" String="classes" />
+ <keyword attribute="Type Constructor" context="#stay" String="type constructors" />
+ <keyword attribute="Function" context="#stay" String="functions" />
+ <keyword attribute="Data Constructor" context="#stay" String="data constructors" />
+ <DetectChar attribute="String" context="string" char="&quot;" />
+ <DetectChar attribute="Infix Operator" context="infix" char="`"/>
+ <RegExpr attribute="Normal Text" context="#stay" String="[A-Za-z0-9_][']+" />
+ <DetectChar attribute="Char" context="single_char" char="'" />
+ <RegExpr attribute="Function Definition" context="#stay" String="[a-z_]+[A-Za-z_0-9]*'*\s*::" />
+ <Float attribute="Float" context="#stay" />
+ <Int attribute="Decimal" context="#stay" />
+ </context>
+ <context attribute="Comment" lineEndContext="#pop" name="comment_single_line" />
+ <context attribute="Comment" lineEndContext="#stay" name="comment_multi_line">
+ <Detect2Chars attribute="Comment" context="#pop" char="-" char1="}" />
+ </context>
+ <context attribute="String" lineEndContext="#stay" name="string">
+ <RegExpr attribute="String" context="#stay" String="\\." />
+ <DetectChar attribute="String" context="#pop" char="&quot;" />
+ </context>
+ <context attribute="Infix Operator" lineEndContext="#stay" name="infix">
+ <DetectChar attribute="Infix Operator" context="#pop" char="`"/>
+ </context>
+ <context attribute="Char" lineEndContext="#pop" name="single_char">
+ <RegExpr attribute="Char" context="#stay" String="\\." />
+ <DetectChar attribute="Char" context="#pop" char="'" />
+ </context>
+ <context attribute="Function Definition" lineEndContext="#pop" name="function_definition">
+ <DetectChar attribute="Function Definition" context="#pop" char=";" />
+ </context>
+ </contexts>
+ <itemDatas>
+ <itemData name="Normal Text" defStyleNum="dsNormal"/>
+ <itemData name="Keyword" defStyleNum="dsKeyword"/>
+ <itemData name="Function" defStyleNum="dsOthers"/>
+ <itemData name="Function Definition" defStyleNum="dsOthers"/>
+ <itemData name="Class" defStyleNum="dsKeyword"/>
+ <itemData name="Decimal" defStyleNum="dsDecVal"/>
+ <itemData name="Float" defStyleNum="dsFloat"/>
+ <itemData name="Char" defStyleNum="dsChar"/>
+ <itemData name="String" defStyleNum="dsString"/>
+ <itemData name="Constructor" defStyleNum="dsOthers"/>
+ <itemData name="Comment" defStyleNum="dsComment"/>
+ <itemData name="Data Constructor" defStyleNum="dsKeyword"/>
+ <itemData name="Type Constructor" defStyleNum="dsDataType"/>
+ <itemData name="Infix Operator" defStyleNum="dsOthers"/>
+ </itemDatas>
+ </highlighting>
+ <general>
+ <comments>
+ <comment name="singleLine" start="--" />
+ <comment name="multiLine" start="{-" end="-}" />
+ </comments>
+ <keywords casesensitive="1" />
+ </general>
+</language>
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE language SYSTEM "language.dtd">
+<language name="HTML" version="1.05" kateversion="2.1" section="Markup" extensions="*.html;*.htm;*.shtml;*.shtm" mimetype="text/html">
+ <highlighting>
+ <contexts>
+ <context attribute="Normal Text" lineEndContext="#stay" name="normal">
+ <StringDetect attribute="Comment" context="comment" String="&lt;!--"/>
+ <StringDetect attribute="Keyword" context="javascript_hook" String="&lt;SCRIPT" insensitive="TRUE" beginRegion="Script" />
+ <StringDetect attribute="Keyword" context="CSS_hook" String="&lt;style" insensitive="TRUE" beginRegion="Style" />
+
+ <RegExpr attribute="Keyword" context="tagname" String="&lt;\s*\/?\s*[a-zA-Z0-9_]*" />
+ </context>
+
+ <context attribute="Identifier" lineEndContext="#stay" name="tagname">
+ <DetectChar attribute="Keyword" context="#pop" char="&gt;"/>
+ <RegExpr attribute="Normal Text" context="values" String="\s*=\s*"/>
+ </context>
+
+ <context attribute="Wrong Values" lineEndContext="#stay" name="values">
+ <RegExpr attribute="Types" context="#pop" String="\s*#?[a-zA-Z0-9]*" />
+ <DetectChar attribute="Types" context="string" char="&quot;" />
+ </context>
+
+ <context attribute="Types" lineEndContext="#stay" name="string">
+ <DetectChar attribute="Types" context="#pop#pop" char="&quot;" />
+ </context>
+
+ <context attribute="Comment" lineEndContext="#stay" name="comment">
+ <StringDetect attribute="Comment" context="#pop" String="--&gt;"/>
+ </context>
+
+ <context attribute="Identifier" lineEndContext="#stay" name="javascript_hook">
+ <DetectChar attribute="Keyword" context="javascript" char="&gt;"/>
+ <RegExpr attribute="Normal Text" context="values" String="\s*=\s*"/>
+ </context>
+ <context attribute="Normal Text" lineEndContext="#stay" name="javascript">
+ <Detect2Chars attribute="Comment" char="/" char1="/" context="javascript_comment"/>
+ <StringDetect attribute="Keyword" context="#pop#pop" String="&lt;/SCRIPT&gt;" insensitive="TRUE" endRegion="Script"/>
+ <IncludeRules context="##JavaScript"/>
+ </context>
+ <context attribute="Comment" lineEndContext="#pop" name="javascript_comment">
+ <StringDetect attribute="Keyword" context="#pop#pop#pop" String="&lt;/SCRIPT&gt;" insensitive="TRUE" endRegion="Script" />
+ <RegExpr attribute="Wrong Values" context="#stay" String="(FIXME|TODO)" />
+ </context>
+
+ <context attribute="Identifier" lineEndContext="#stay" name="CSS_hook">
+ <DetectChar attribute="Keyword" context="CSS" char="&gt;"/>
+ <RegExpr attribute="Normal Text" context="values" String="\s*=\s*"/>
+ </context>
+ <context attribute="Normal Text" lineEndContext="#stay" name="CSS">
+ <IncludeRules context="##CSS"/>
+ <StringDetect attribute="Keyword" context="#pop#pop" String="&lt;/style&gt;" insensitive="TRUE" endRegion="Style"/>
+ </context>
+ </contexts>
+ <itemDatas>
+ <itemData name="Normal Text" defStyleNum="dsNormal"/>
+ <itemData name="Keyword" defStyleNum="dsKeyword"/>
+ <itemData name="Identifier" defStyleNum="dsOthers"/>
+ <itemData name="Types" defStyleNum="dsDataType"/>
+ <itemData name="String" defStyleNum="dsString"/>
+ <itemData name="Comment" defStyleNum="dsComment"/>
+ <itemData name="Wrong Values" defStyleNum="dsNormal" color="#ff0000" selColor="#ffffff" italic="0" bold="1"/>
+ </itemDatas>
+ </highlighting>
+ <general>
+ <comments>
+ <comment name="multiLine" start="&lt;!--" end="//--&gt;" />
+ </comments>
+ <keywords casesensitive="0" />
+ </general>
+</language>
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE language SYSTEM "language.dtd">
+<language name="Quake, Wolfenstein, Half-Life" version="1.02" kateversion="2.1" section="Games" extensions="*.cfg" mimetype="">
+
+<highlighting>
+<list name="Commands">
+<item> ForceCloseComman </item>
+<item> _config_com_baud </item>
+<item> _config_com_modem </item>
+<item> _vid_default_mode </item>
+<item> _vid_default_mode_win </item>
+<item> _vid_wait_override </item>
+<item> _windowed_mouse </item>
+<item> addip </item>
+<item> addressbook </item>
+<item> adjust_crosshair </item>
+<item> advancedupdate </item>
+<item> allow_download </item>
+<item> allow_download_maps </item>
+<item> allow_download_models </item>
+<item> allow_download_skins </item>
+<item> allow_download_sounds </item>
+<item> allskins </item>
+<item> appenddemo </item>
+<item> autosave </item>
+<item> ban </item>
+<item> banClient </item>
+<item> banUser </item>
+<item> banid </item>
+<item> baseskin </item>
+<item> begin </item>
+<item> bf </item>
+<item> bgetmod </item>
+<item> bindlist </item>
+<item> block_switch </item>
+<item> bottomcolor </item>
+<item> buyNow </item>
+<item> buyequip </item>
+<item> cache_endgather </item>
+<item> cache_flush </item>
+<item> cache_mapchange </item>
+<item> cache_print </item>
+<item> cache_profile </item>
+<item> cache_setindex </item>
+<item> cache_startgather </item>
+<item> cache_usedfile </item>
+<item> cancelselect </item>
+<item> cd </item>
+<item> centerview </item>
+<item> changeVectors </item>
+<item> changelevel </item>
+<item> changelevel2 </item>
+<item> changing </item>
+<item> chase_active </item>
+<item> cinematic </item>
+<item> cl_deadbodyfilter </item>
+<item> cl_gibfilter </item>
+<item> cl_hightrack </item>
+<item> cl_hudswap </item>
+<item> cl_messages </item>
+<item> cl_nodelta </item>
+<item> cl_nolerp </item>
+<item> cl_nopred </item>
+<item> cl_predict_players </item>
+<item> cl_rate </item>
+<item> cl_sbar </item>
+<item> cl_sbar_separator </item>
+<item> cl_shownet </item>
+<item> cl_sidespeed </item>
+<item> cl_solid_players </item>
+<item> cl_warncmd </item>
+<item> cl_writecfg </item>
+<item> clear </item>
+<item> clearplayers </item>
+<item> clientinfo </item>
+<item> clientkick </item>
+<item> cmd </item>
+<item> cmdline </item>
+<item> cmdlist </item>
+<item> color </item>
+<item> commands </item>
+<item> condebug </item>
+<item> condump </item>
+<item> configstrings </item>
+<item> confirm_quit </item>
+<item> connect </item>
+<item> contimes </item>
+<item> coop </item>
+<item> crash </item>
+<item> credits </item>
+<item> cropimages </item>
+<item> crosshair </item>
+<item> cvar_restart </item>
+<item> cvarlist </item>
+<item> d_mipcap </item>
+<item> d_subdiv16 </item>
+<item> deathmatch </item>
+<item> delta_clear </item>
+<item> delta_stats </item>
+<item> demo </item>
+<item> demolist </item>
+<item> demomap </item>
+<item> demos </item>
+<item> developer </item>
+<item> devmap </item>
+<item> dir </item>
+<item> disconnect </item>
+<item> dlfile </item>
+<item> dmoptions </item>
+<item> download </item>
+<item> drawradar </item>
+<item> drop </item>
+<item> dropclient </item>
+<item> dumpuser </item>
+<item> edict </item>
+<item> edictcount </item>
+<item> edicts </item>
+<item> endmovie </item>
+<item> entities </item>
+<item> envmap </item>
+<item> error </item>
+<item> escape </item>
+<item> exec </item>
+<item> exit </item>
+<item> fastsprites </item>
+<item> fdir </item>
+<item> filterban </item>
+<item> firstperson </item>
+<item> floodprot </item>
+<item> floodprotmsg </item>
+<item> flush </item>
+<item> fly </item>
+<item> force_centerview </item>
+<item> fov </item>
+<item> fraglogfile </item>
+<item> freelook </item>
+<item> freeze </item>
+<item> front </item>
+<item> fs_openedList </item>
+<item> fs_referencedList </item>
+<item> fullinfo </item>
+<item> fullserverinfo </item>
+<item> game </item>
+<item> gameCompleteStatus </item>
+<item> gamedir </item>
+<item> gamemap </item>
+<item> gameversion </item>
+<item> getcertificate </item>
+<item> gfxinfo </item>
+<item> gg </item>
+<item> gib </item>
+<item> gibload </item>
+<item> gibstats </item>
+<item> give </item>
+<item> gl_affinemodels </item>
+<item> gl_clear </item>
+<item> gl_colorlights </item>
+<item> gl_constretch </item>
+<item> gl_cull </item>
+<item> gl_dlight_lightmap </item>
+<item> gl_dlight_polyblend </item>
+<item> gl_dlight_smooth </item>
+<item> gl_fb_bmodels </item>
+<item> gl_fb_models </item>
+<item> gl_finish </item>
+<item> gl_fires </item>
+<item> gl_flashblend </item>
+<item> gl_keeptjunctions </item>
+<item> gl_lerp_anim </item>
+<item> gl_lightmode </item>
+<item> gl_max_size </item>
+<item> gl_multitexture </item>
+<item> gl_nobind </item>
+<item> gl_nocolors </item>
+<item> gl_picmip </item>
+<item> gl_playermip </item>
+<item> gl_polyblend </item>
+<item> gl_reportjunctions </item>
+<item> gl_sky_clip </item>
+<item> gl_skymultipass </item>
+<item> gl_smoothmodels </item>
+<item> gl_texsort </item>
+<item> gl_texturemode </item>
+<item> gl_triplebuffer </item>
+<item> gl_ztrick </item>
+<item> globalservers </item>
+<item> god </item>
+<item> gun </item>
+<item> gun_model </item>
+<item> gun_next </item>
+<item> gun_prev </item>
+<item> gunsmoke </item>
+<item> heartbeat </item>
+<item> help </item>
+<item> hideconsole </item>
+<item> hideradar </item>
+<item> host_speeds </item>
+<item> hostname </item>
+<item> hpkextract </item>
+<item> hpklist </item>
+<item> hpkremove </item>
+<item> hpkval </item>
+<item> hud_centerid </item>
+<item> imagelist </item>
+<item> impulse </item>
+<item> imt </item>
+<item> in_bind </item>
+<item> in_paste_buffer </item>
+<item> in_restart </item>
+<item> in_unbind </item>
+<item> info </item>
+<item> interp </item>
+<item> invdrop </item>
+<item> inven </item>
+<item> invnext </item>
+<item> invnextp </item>
+<item> invnextw </item>
+<item> invprev </item>
+<item> invprevp </item>
+<item> invprevw </item>
+<item> invuse </item>
+<item> joinserver </item>
+<item> joy </item>
+<item> joy_advancedupdate </item>
+<item> joy_enable </item>
+<item> joyadvanced </item>
+<item> joyadvancedupdat </item>
+<item> joyadvancedupdate </item>
+<item> joyname </item>
+<item> joystick </item>
+<item> keys </item>
+<item> kick </item>
+<item> kill </item>
+<item> killserver </item>
+<item> lefthand </item>
+<item> link </item>
+<item> list </item>
+<item> listdemo </item>
+<item> listen </item>
+<item> listid </item>
+<item> listip </item>
+<item> listmaps </item>
+<item> load </item>
+<item> loadas8bit </item>
+<item> loadgame </item>
+<item> loading </item>
+<item> loadsky </item>
+<item> loadtranslations </item>
+<item> loc </item>
+<item> localinfo </item>
+<item> localservers </item>
+<item> log </item>
+<item> logaddress </item>
+<item> logfile </item>
+<item> lookspring </item>
+<item> lookstrafe </item>
+<item> m_filter </item>
+<item> main </item>
+<item> map </item>
+<item> map_restart </item>
+<item> maplist </item>
+<item> maps </item>
+<item> maxplayers </item>
+<item> max_smokepuffs </item>
+<item> max_shells </item>
+<item> mcache </item>
+<item> meminfo </item>
+<item> menu </item>
+<item> menu_addressbook </item>
+<item> menu_credits </item>
+<item> menu_help </item>
+<item> menu_keys </item>
+<item> menu_load </item>
+<item> menu_loadgame </item>
+<item> menu_main </item>
+<item> menu_multiplayer </item>
+<item> menu_options </item>
+<item> menu_playerconfig </item>
+<item> menu_quit </item>
+<item> menu_save </item>
+<item> menu_savegame </item>
+<item> menu_setup </item>
+<item> menu_select </item>
+<item> menu_singleplayer </item>
+<item> menu_startserver </item>
+<item> menu_video </item>
+<item> menu_dmoptions </item>
+<item> menu_game </item>
+<item> menu_joinserver </item>
+<item> messagemode </item>
+<item> messagemode2 </item>
+<item> messagemode3 </item>
+<item> messagemode4 </item>
+<item> model </item>
+<item> modelist </item>
+<item> modellist </item>
+<item> msg </item>
+<item> multiplayer </item>
+<item> music </item>
+<item> name </item>
+<item> net_stats </item>
+<item> new </item>
+<item> next </item>
+<item> nextul </item>
+<item> nightvision </item>
+<item> no_pogo_stick </item>
+<item> noaim </item>
+<item> noclip </item>
+<item> noexit </item>
+<item> nomonsters </item>
+<item> noskins </item>
+<item> nosound </item>
+<item> notarget </item>
+<item> options </item>
+<item> packet </item>
+<item> password </item>
+<item> path </item>
+<item> pausable </item>
+<item> pause </item>
+<item> paused </item>
+<item> ping </item>
+<item> pingservers </item>
+<item> play </item>
+<item> playdemo </item>
+<item> playerconfig </item>
+<item> players </item>
+<item> playvol </item>
+<item> pushlatency </item>
+<item> pointfile </item>
+<item> ppdemostart </item>
+<item> pr_boundscheck </item>
+<item> precache </item>
+<item> prespawn </item>
+<item> prev </item>
+<item> profile </item>
+<item> profilequit </item>
+<item> prog </item>
+<item> quit </item>
+<item> r_drawentities </item>
+<item> r_drawflat </item>
+<item> r_draworder </item>
+<item> r_drawviewmodel </item>
+<item> r_dspeeds </item>
+<item> r_dynamic </item>
+<item> r_fullbright </item>
+<item> r_lightmap </item>
+<item> r_netgraph </item>
+<item> r_netgraph_box </item>
+<item> r_norefresh </item>
+<item> r_novis </item>
+<item> r_numedges </item>
+<item> r_numsurfs </item>
+<item> r_particles </item>
+<item> r_polymodelstats </item>
+<item> r_reportsurfout </item>
+<item> r_shadows </item>
+<item> r_speeds </item>
+<item> r_timegraph </item>
+<item> r_wateralpha </item>
+<item> r_waterripple </item>
+<item> r_waterwarp </item>
+<item> r_zgraph </item>
+<item> rcon </item>
+<item> rcon_password </item>
+<item> reconnect </item>
+<item> record </item>
+<item> registered </item>
+<item> reload </item>
+<item> removedemo </item>
+<item> removeid </item>
+<item> removeip </item>
+<item> rerecord </item>
+<item> reset </item>
+<item> resetrcon </item>
+<item> restart </item>
+<item> retry </item>
+<item> s_disable_a3d </item>
+<item> s_enable_a3d </item>
+<item> s_info </item>
+<item> s_list </item>
+<item> s_stop </item>
+<item> samelevel </item>
+<item> save </item>
+<item> savegame </item>
+<item> savetranslations </item>
+<item> score </item>
+<item> screenshot </item>
+<item> screenshotJPEG </item>
+<item> sectorlist </item>
+<item> sendents </item>
+<item> serverinfo </item>
+<item> serverprofile </item>
+<item> serverrecord </item>
+<item> serverstatus </item>
+<item> serverstop </item>
+<item> setRecommended </item>
+<item> setdemoinfo </item>
+<item> setenv </item>
+<item> setinfo </item>
+<item> setmaster </item>
+<item> setrom </item>
+<item> shaderlist </item>
+<item> show_fps </item>
+<item> show_time </item>
+<item> showdrop </item>
+<item> showinfo </item>
+<item> showip </item>
+<item> showpackets </item>
+<item> showpause </item>
+<item> showram </item>
+<item> showturtle </item>
+<item> shutdownserver </item>
+<item> singlePlayLink </item>
+<item> sizedown </item>
+<item> sizeup </item>
+<item> skill </item>
+<item> skin </item>
+<item> skinlist </item>
+<item> skins </item>
+<item> sky </item>
+<item> skyboxlist </item>
+<item> slist </item>
+<item> slot1 </item>
+<item> slot10 </item>
+<item> slot2 </item>
+<item> slot3 </item>
+<item> slot4 </item>
+<item> slot5 </item>
+<item> slot6 </item>
+<item> slot7 </item>
+<item> slot8 </item>
+<item> slot9 </item>
+<item> snap </item>
+<item> snapall </item>
+<item> snapshot </item>
+<item> snapto </item>
+<item> snd </item>
+<item> snd_noextraupdate </item>
+<item> snd_restart </item>
+<item> snd_show </item>
+<item> soundfade </item>
+<item> soundinfo </item>
+<item> soundlist </item>
+<item> spawn </item>
+<item> spdevmap </item>
+<item> speak </item>
+<item> special </item>
+<item> specmode </item>
+<item> spectator </item>
+<item> spectator_password </item>
+<item> spk </item>
+<item> spmap </item>
+<item> startLimboMode </item>
+<item> startSingleplayer </item>
+<item> startdemos </item>
+<item> startmovie </item>
+<item> startserver </item>
+<item> stat </item>
+<item> stats </item>
+<item> status </item>
+<item> stop </item>
+<item> stopLimboMode </item>
+<item> stopdemo </item>
+<item> stoprecord </item>
+<item> stopsound </item>
+<item> stopul </item>
+<item> streamingsound </item>
+<item> stuffcmd </item>
+<item> stuffcmds </item>
+<item> sv </item>
+<item> sv_allow_log </item>
+<item> sv_allow_pings </item>
+<item> sv_allow_status </item>
+<item> sv_gamedir </item>
+<item> sv_highchars </item>
+<item> sv_mapcheck </item>
+<item> sv_nostep </item>
+<item> sv_spectatormaxspeed </item>
+<item> sv_spetalk </item>
+<item> sv_maplist </item>
+<item> swapdemo </item>
+<item> sys_cpuid </item>
+<item> sys_dead_sleep </item>
+<item> sys_extrasleep </item>
+<item> sys_nostdout </item>
+<item> systeminfo </item>
+<item> taginfo </item>
+<item> team </item>
+<item> teamplay </item>
+<item> tell </item>
+<item> test </item>
+<item> test2 </item>
+<item> time </item>
+<item> thirdperson </item>
+<item> timedemo </item>
+<item> timeleft </item>
+<item> timerefresh </item>
+<item> toggle </item>
+<item> togglebrowser </item>
+<item> togglechat </item>
+<item> toggleconsole </item>
+<item> togglemenu </item>
+<item> topcolor </item>
+<item> touchFile </item>
+<item> trackplayer </item>
+<item> ui_restart </item>
+<item> unalias </item>
+<item> unbindall </item>
+<item> updatehunkusage </item>
+<item> updatescreen </item>
+<item> upload </item>
+<item> use </item>
+<item> user </item>
+<item> userinfo </item>
+<item> users </item>
+<item> v_centerspeed </item>
+<item> v_cshift </item>
+<item> v_idlescale </item>
+<item> version </item>
+<item> vid </item>
+<item> vid_center </item>
+<item> vid_config_x </item>
+<item> vid_describecurrentmode </item>
+<item> vid_describemode </item>
+<item> vid_describemodes </item>
+<item> vid_forcemode </item>
+<item> vid_fullscreen </item>
+<item> vid_fullscreen_mode </item>
+<item> vid_minimize </item>
+<item> vid_nopageflip </item>
+<item> vid_nummodes </item>
+<item> vid_restart </item>
+<item> vid_stretch_by_2 </item>
+<item> vid_testmode </item>
+<item> vid_windowed </item>
+<item> vid_windowed_mode </item>
+<item> vid_front </item>
+<item> video </item>
+<item> viewframe </item>
+<item> viewmodel </item>
+<item> viewnext </item>
+<item> viewpos </item>
+<item> viewprev </item>
+<item> vminfo </item>
+<item> vmprofile </item>
+<item> voice_showbanned </item>
+<item> votemap </item>
+<item> vstr </item>
+<item> wait </item>
+<item> watervis </item>
+<item> wave </item>
+<item> weapon </item>
+<item> weapon_knife </item>
+<item> weaplast </item>
+<item> weapnext </item>
+<item> weapprev </item>
+<item> windowsr_drawentities </item>
+<item> writecfg </item>
+<item> writeconfig </item>
+<item> writeid </item>
+<item> writeip </item>
+<item> z_stats </item>
+</list>
+
+
+<list name="Variables">
+<item> ah </item>
+<item> ActiveAction </item>
+<item> _cl_color </item>
+<item> _cl_name </item>
+<item> _config_com_baud </item>
+<item> _config_com_irq </item>
+<item> _config_com_modem </item>
+<item> _config_com_port </item>
+<item> _config_modem_clear </item>
+<item> _config_modem_dialtype </item>
+<item> _config_modem_hangup </item>
+<item> _config_modem_init </item>
+<item> _snd_mixahead </item>
+<item> _vid_default_mode </item>
+<item> _vid_default_mode_win </item>
+<item> _vid_wait_override </item>
+<item> _windowed_mouse </item>
+<item> address </item>
+<item> adr </item>
+<item> adr0 </item>
+<item> adr1 </item>
+<item> adr2 </item>
+<item> adr3 </item>
+<item> adr4 </item>
+<item> adr5 </item>
+<item> adr6 </item>
+<item> adr7 </item>
+<item> adr8 </item>
+<item> advanced </item>
+<item> advaxisr </item>
+<item> advaxisu </item>
+<item> advaxisv </item>
+<item> advaxisx </item>
+<item> advaxisy </item>
+<item> advaxisz </item>
+<item> airaccelerate </item>
+<item> allow </item>
+<item> allow_download_players </item>
+<item> ambient_fade </item>
+<item> ambient_level </item>
+<item> anglespeedkey </item>
+<item> arch </item>
+<item> array </item>
+<item> arrays </item>
+<item> att </item>
+<item> auto </item>
+<item> autoskins </item>
+<item> b </item>
+<item> bgmbuffer </item>
+<item> bgmvolume </item>
+<item> bit </item>
+<item> bitdepth </item>
+<item> blend </item>
+<item> bob </item>
+<item> bob_pitch </item>
+<item> bob_roll </item>
+<item> bob_up </item>
+<item> bot_aasoptimize </item>
+<item> bot_challenge </item>
+<item> bot_debug </item>
+<item> bot_developer </item>
+<item> bot_enable </item>
+<item> bot_fastchat </item>
+<item> bot_forceclustering </item>
+<item> bot_forcereachability </item>
+<item> bot_forcewrite </item>
+<item> bot_grapple </item>
+<item> bot_groundonly </item>
+<item> bot_interbreedbots </item>
+<item> bot_interbreedchar </item>
+<item> bot_interbreedcycle </item>
+<item> bot_interbreedwrite </item>
+<item> bot_maxdebugpolys </item>
+<item> bot_miniplayers </item>
+<item> bot_minplayers </item>
+<item> bot_nochat </item>
+<item> bot_pause </item>
+<item> bot_reachability </item>
+<item> bot_reloadcharacters </item>
+<item> bot_report </item>
+<item> bot_rocketjump </item>
+<item> bot_saveroutingcache </item>
+<item> bot_testclusters </item>
+<item> bot_testichat </item>
+<item> bot_testrchat </item>
+<item> bot_testsolid </item>
+<item> bot_thinktime </item>
+<item> bot_visualizejumppads </item>
+<item> brighten </item>
+<item> brightness </item>
+<item> broken </item>
+<item> cd </item>
+<item> cd_loopcount </item>
+<item> cd_looptrack </item>
+<item> cd_nocd </item>
+<item> cd_plugin </item>
+<item> centermove </item>
+<item> centerspeed </item>
+<item> centertime </item>
+<item> cg_autoactivate </item>
+<item> cg_autoswitch </item>
+<item> cg_blinktime </item>
+<item> cg_bloodTime </item>
+<item> cg_bobpitch </item>
+<item> cg_bobroll </item>
+<item> cg_bobup </item>
+<item> cg_brassTime </item>
+<item> cg_cameraOrbitDelay </item>
+<item> cg_clipboardName </item>
+<item> cg_coronafardist </item>
+<item> cg_coronas </item>
+<item> cg_crosshairAlpha </item>
+<item> cg_crosshairHealth </item>
+<item> cg_crosshairSize </item>
+<item> cg_crosshairX </item>
+<item> cg_crosshairY </item>
+<item> cg_currentSelectedPlayer </item>
+<item> cg_currentSelectedPlayerName </item>
+<item> cg_cursorHints </item>
+<item> cg_cycleAllWeaps </item>
+<item> cg_deferPlayers </item>
+<item> cg_descriptiveText </item>
+<item> cg_draw2D </item>
+<item> cg_draw3dIcons </item>
+<item> cg_drawAllWeaps </item>
+<item> cg_drawAmmoWarning </item>
+<item> cg_drawAttacker </item>
+<item> cg_drawCompass </item>
+<item> cg_drawCrosshair </item>
+<item> cg_drawCrosshairNames </item>
+<item> cg_drawCrosshairPickups </item>
+<item> cg_drawFPGun </item>
+<item> cg_drawFPS </item>
+<item> cg_drawFrags </item>
+<item> cg_drawGun </item>
+<item> cg_drawIcons </item>
+<item> cg_drawNotifyText </item>
+<item> cg_drawRewards </item>
+<item> cg_drawSnapshot </item>
+<item> cg_drawSpreadScale </item>
+<item> cg_drawStatus </item>
+<item> cg_drawTeamOverlay </item>
+<item> cg_drawTimer </item>
+<item> cg_emptyswitch </item>
+<item> cg_fov </item>
+<item> cg_forcemodel </item>
+<item> cg_gibs </item>
+<item> cg_hudAlpha </item>
+<item> cg_hudFiles </item>
+<item> cg_lagometer </item>
+<item> cg_marks </item>
+<item> cg_marktime </item>
+<item> cg_noplayeranims </item>
+<item> cg_nopredict </item>
+<item> cg_noTaunt </item>
+<item> cg_noVoiceChats </item>
+<item> cg_noVoiceText </item>
+<item> cg_particleDist </item>
+<item> cg_particleLOD </item>
+<item> cg_popupLimboMenu </item>
+<item> cg_predictItems </item>
+<item> cg_quickMessageAlt </item>
+<item> cg_railTrailTime </item>
+<item> cg_recoilPitch </item>
+<item> cg_reticleBrightness </item>
+<item> cg_reticleType </item>
+<item> cg_runpitch </item>
+<item> cg_runroll </item>
+<item> cg_scorePlums </item>
+<item> cg_selectedPlayer </item>
+<item> cg_selectedPlayerName </item>
+<item> cg_shadows </item>
+<item> cg_showblood </item>
+<item> cg_simpleItems </item>
+<item> cg_skybox </item>
+<item> cg_stereoSeparation </item>
+<item> cg_teamChatHeight </item>
+<item> cg_teamChatTime </item>
+<item> cg_teamChatsOnly </item>
+<item> cg_thirdperson </item>
+<item> cg_thirdpersonrange </item>
+<item> cg_thirdPersonAngle </item>
+<item> cg_useWeapsForZoom </item>
+<item> cg_uselessNostalgia </item>
+<item> cg_viewsize </item>
+<item> cg_voiceSpriteTime </item>
+<item> cg_weaponCycleDelay </item>
+<item> cg_wolfparticles </item>
+<item> cg_zoomDefaultBinoc </item>
+<item> cg_zoomDefaultFG </item>
+<item> cg_zoomDefaultSniper </item>
+<item> cg_zoomDefaultSnooper </item>
+<item> cg_zoomStepBinoc </item>
+<item> cg_zoomStepFG </item>
+<item> cg_zoomStepSnooper </item>
+<item> cg_zoomfov </item>
+<item> cg_zoomstepsniper </item>
+<item> chase_active </item>
+<item> chase_back </item>
+<item> chase_right </item>
+<item> chase_up </item>
+<item> cheats </item>
+<item> cl </item>
+<item> cl_allowDownload </item>
+<item> cl_anglespeedkey </item>
+<item> cl_anonymous </item>
+<item> cl_autoexec </item>
+<item> cl_autoskins </item>
+<item> cl_avidemo </item>
+<item> cl_backspeed </item>
+<item> cl_blend </item>
+<item> cl_bob </item>
+<item> cl_bobcycle </item>
+<item> cl_bobup </item>
+<item> cl_bypassMouseInput </item>
+<item> cl_cacheGathering </item>
+<item> cl_camera_maxpitch </item>
+<item> cl_camera_maxyaw </item>
+<item> cl_chasecam </item>
+<item> cl_chatmode </item>
+<item> cl_conXOffset </item>
+<item> cl_crossx </item>
+<item> cl_crossy </item>
+<item> cl_cshift_bonus </item>
+<item> cl_cshift_content </item>
+<item> cl_cshift_damage </item>
+<item> cl_cshift_powerup </item>
+<item> cl_debugMove </item>
+<item> cl_debugTranslation </item>
+<item> cl_demospeed </item>
+<item> cl_entities </item>
+<item> cl_footsteps </item>
+<item> cl_forceavidemo </item>
+<item> cl_forwardspeed </item>
+<item> cl_freelook </item>
+<item> cl_freezeDemo </item>
+<item> cl_gun </item>
+<item> cl_hidefrags </item>
+<item> cl_hightrack </item>
+<item> cl_hudswap </item>
+<item> cl_language </item>
+<item> cl_lights </item>
+<item> cl_maxPing </item>
+<item> cl_maxfps </item>
+<item> cl_maxpackets </item>
+<item> cl_motd </item>
+<item> cl_motdString </item>
+<item> cl_mouseAccel </item>
+<item> cl_movespeedkey </item>
+<item> cl_nodelta </item>
+<item> cl_nofake </item>
+<item> cl_nolerp </item>
+<item> cl_nopred </item>
+<item> cl_noprint </item>
+<item> cl_noskins </item>
+<item> cl_packetdup </item>
+<item> cl_parsesay </item>
+<item> cl_particles </item>
+<item> cl_paused </item>
+<item> cl_pitchspeed </item>
+<item> cl_predict </item>
+<item> cl_predict_players </item>
+<item> cl_predict_players2 </item>
+<item> cl_observercrosshair </item>
+<item> cl_quakerc </item>
+<item> cl_rollangle </item>
+<item> cl_rollspeed </item>
+<item> cl_run </item>
+<item> cl_running </item>
+<item> cl_serverStatusResendTime </item>
+<item> cl_showfps </item>
+<item> cl_showSend </item>
+<item> cl_showServerCommands </item>
+<item> cl_showTimeDelta </item>
+<item> cl_showmiss </item>
+<item> cl_showmouserate </item>
+<item> cl_shownet </item>
+<item> cl_shownuments </item>
+<item> cl_sidespeed </item>
+<item> cl_stats </item>
+<item> cl_stereo </item>
+<item> cl_stereo_separation </item>
+<item> cl_testblend </item>
+<item> cl_testentities </item>
+<item> cl_testlights </item>
+<item> cl_testparticles </item>
+<item> cl_timeNudge </item>
+<item> cl_timeout </item>
+<item> cl_upspeed </item>
+<item> cl_verstring </item>
+<item> cl_visibleClients </item>
+<item> cl_vwep </item>
+<item> cl_waitForFire </item>
+<item> cl_wavefilerecord </item>
+<item> cl_yawspeed </item>
+<item> clear </item>
+<item> clearcolor </item>
+<item> clientport </item>
+<item> cm_playerCurveClip </item>
+<item> cmd_highchars </item>
+<item> cmd_warncmd </item>
+<item> cmdlist </item>
+<item> color </item>
+<item> color1 </item>
+<item> color2 </item>
+<item> com_blood </item>
+<item> com_buildScript </item>
+<item> com_cameraMode </item>
+<item> com_dropsim </item>
+<item> com_hunkMegs </item>
+<item> com_hunkused </item>
+<item> com_introplayed </item>
+<item> com_maxfps </item>
+<item> com_recommendedSet </item>
+<item> com_showtrace </item>
+<item> com_soundMegs </item>
+<item> com_speeds </item>
+<item> com_zoneMegs </item>
+<item> compiled </item>
+<item> con_debug </item>
+<item> con_notifytime </item>
+<item> con_restricted </item>
+<item> conspeed </item>
+<item> contrast </item>
+<item> coop </item>
+<item> crosshair </item>
+<item> crosshaircolor </item>
+<item> cull </item>
+<item> d_mipcap </item>
+<item> d_mipscale </item>
+<item> deathmatch </item>
+<item> debug_protocol </item>
+<item> debuggraph </item>
+<item> dedicated </item>
+<item> devdll </item>
+<item> developer </item>
+<item> dlabs </item>
+<item> dmflags </item>
+<item> dm </item>
+<item> down </item>
+<item> download </item>
+<item> drawall </item>
+<item> drawbuffer </item>
+<item> drawentities </item>
+<item> drawflat </item>
+<item> draworder </item>
+<item> drawworld </item>
+<item> driver </item>
+<item> dspeeds </item>
+<item> dynamic </item>
+<item> easter_eggs </item>
+<item> edgefriction </item>
+<item> empty </item>
+<item> enforcetime </item>
+<item> entities </item>
+<item> entlatency </item>
+<item> ext </item>
+<item> filter </item>
+<item> filterban </item>
+<item> finish </item>
+<item> fixedtime </item>
+<item> flashblend </item>
+<item> flood </item>
+<item> flood_msgs </item>
+<item> flood_persecond </item>
+<item> flood_waitdelay </item>
+<item> flushmap </item>
+<item> footsteps </item>
+<item> forward </item>
+<item> forwardsensitivity </item>
+<item> forwardspeed </item>
+<item> forwardthreshold </item>
+<item> fov </item>
+<item> fraglimit </item>
+<item> freelook </item>
+<item> fs_basegame </item>
+<item> fs_basepath </item>
+<item> fs_cdpath </item>
+<item> fs_copyfiles </item>
+<item> fs_debug </item>
+<item> fs_game </item>
+<item> fs_globalcfg </item>
+<item> fs_homepath </item>
+<item> fs_pluginpath </item>
+<item> fs_restrict </item>
+<item> fs_sharepath </item>
+<item> fs_skinbase </item>
+<item> fs_usercfg </item>
+<item> fs_userpath </item>
+<item> fullbright </item>
+<item> fullscreen </item>
+<item> g_allowvote </item>
+<item> g_altStopwatchMode </item>
+<item> g_arenasFile </item>
+<item> g_blueTeam </item>
+<item> g_botsFile </item>
+<item> g_complaintlimit </item>
+<item> g_currentRound </item>
+<item> g_friendlyFire </item>
+<item> g_gameskill </item>
+<item> g_gametype </item>
+<item> g_maxlives </item>
+<item> g_minGameClients </item>
+<item> g_missionStats </item>
+<item> g_nextTimeLimit </item>
+<item> g_noTeamSwitching </item>
+<item> g_redTeam </item>
+<item> g_select_empty </item>
+<item> g_spAwards </item>
+<item> g_spScores1 </item>
+<item> g_spScores2 </item>
+<item> g_spScores3 </item>
+<item> g_spScores4 </item>
+<item> g_spScores5 </item>
+<item> g_spSkill </item>
+<item> g_spVideos </item>
+<item> g_userAlliedRespawnTime </item>
+<item> g_userAxisRespawnTime </item>
+<item> g_userTimeLimit </item>
+<item> game </item>
+<item> gamecfg </item>
+<item> gamedate </item>
+<item> gamedir </item>
+<item> gamename </item>
+<item> gamestate </item>
+<item> gamma </item>
+<item> gender </item>
+<item> gender_auto </item>
+<item> gl_3dlabs_broken </item>
+<item> gl_allow_software </item>
+<item> gl_bitdepth </item>
+<item> gl_clear </item>
+<item> gl_conalpha </item>
+<item> gl_conspin </item>
+<item> gl_cshiftpercent </item>
+<item> gl_cull </item>
+<item> gl_drawbuffer </item>
+<item> gl_driver </item>
+<item> gl_dynamic </item>
+<item> gl_ext_compiled_vertex_array </item>
+<item> gl_ext_multitexture </item>
+<item> gl_ext_palettedtexture </item>
+<item> gl_ext_pointparameters </item>
+<item> gl_ext_swapinterval </item>
+<item> gl_finish </item>
+<item> gl_flashblend </item>
+<item> gl_keeptjunctions </item>
+<item> gl_lightmap </item>
+<item> gl_lightmap_align </item>
+<item> gl_lightmap_subimage </item>
+<item> gl_lockpvs </item>
+<item> gl_log </item>
+<item> gl_max_size </item>
+<item> gl_mesh_cache </item>
+<item> gl_mode </item>
+<item> gl_modulate </item>
+<item> gl_monolightmap </item>
+<item> gl_nobind </item>
+<item> gl_nocolors </item>
+<item> gl_nosubimage </item>
+<item> gl_occlusion </item>
+<item> gl_particle_att_a </item>
+<item> gl_particle_att_b </item>
+<item> gl_particle_att_c </item>
+<item> gl_particle_max_size </item>
+<item> gl_particle_min_size </item>
+<item> gl_particle_mip </item>
+<item> gl_particle_size </item>
+<item> gl_picmip </item>
+<item> gl_playermip </item>
+<item> gl_polyblend </item>
+<item> gl_reporttjunctions </item>
+<item> gl_round_down </item>
+<item> gl_saturatelighting </item>
+<item> gl_screenshot_byte_swap </item>
+<item> gl_shadows </item>
+<item> gl_showtris </item>
+<item> gl_sky_debug </item>
+<item> gl_sky_divide </item>
+<item> gl_skymip </item>
+<item> gl_smoothmodels </item>
+<item> gl_subdivide_size </item>
+<item> gl_swapinterval </item>
+<item> gl_texsort </item>
+<item> gl_texturealphamode </item>
+<item> gl_texturemode </item>
+<item> gl_texturesolidmode </item>
+<item> gl_triplebuffer </item>
+<item> gl_vertex_arrays </item>
+<item> gl_ztrick </item>
+<item> graphheight </item>
+<item> graphscale </item>
+<item> graphshift </item>
+<item> gravity </item>
+<item> gun </item>
+<item> gun_x </item>
+<item> gun_y </item>
+<item> gun_z </item>
+<item> hand </item>
+<item> handicap </item>
+<item> head </item>
+<item> headModel </item>
+<item> headmodel </item>
+<item> host </item>
+<item> host_framerate </item>
+<item> host_speeds </item>
+<item> hostname </item>
+<item> hostport </item>
+<item> hud_fastswitch </item>
+<item> in </item>
+<item> in_amp </item>
+<item> in_bind_imt </item>
+<item> in_debugjoystick </item>
+<item> in_dga </item>
+<item> in_dga_mouseaccel </item>
+<item> in_dgamouse </item>
+<item> in_grab </item>
+<item> in_joystick </item>
+<item> in_midi </item>
+<item> in_mouse </item>
+<item> in_mouse_amp </item>
+<item> in_mouse_filter </item>
+<item> in_mouse_pre_amp </item>
+<item> in_pre_amp </item>
+<item> initsound </item>
+<item> intensity </item>
+<item> ip </item>
+<item> ip_clientport </item>
+<item> ip_hostport </item>
+<item> ipx </item>
+<item> ipx_clientport </item>
+<item> ipx_hostport </item>
+<item> journal </item>
+<item> joy </item>
+<item> joy_advanced </item>
+<item> joy_advaxisr </item>
+<item> joy_advaxisu </item>
+<item> joy_advaxisv </item>
+<item> joy_advaxisx </item>
+<item> joy_advaxisy </item>
+<item> joy_advaxisz </item>
+<item> joy_amp </item>
+<item> joy_device </item>
+<item> joy_forwardsensitivity </item>
+<item> joy_forwardthreshold </item>
+<item> joy_name </item>
+<item> joy_pitchsensitivity </item>
+<item> joy_pitchthreshold </item>
+<item> joy_pre_amp </item>
+<item> joy_sensitivity </item>
+<item> joy_sidesensitivity </item>
+<item> joy_sidethreshold </item>
+<item> joy_threshold </item>
+<item> joy_upsensitivity </item>
+<item> joy_upthreshold </item>
+<item> joy_yawsensitivity </item>
+<item> joy_yawthreshold </item>
+<item> joyadvanced </item>
+<item> joyadvaxisr </item>
+<item> joyadvaxisu </item>
+<item> joyadvaxisv </item>
+<item> joyadvaxisx </item>
+<item> joyadvaxisy </item>
+<item> joyadvaxisz </item>
+<item> joyaxis1 </item>
+<item> joyaxis2 </item>
+<item> joyaxis3 </item>
+<item> joyaxis4 </item>
+<item> joyaxis5 </item>
+<item> joyaxis6 </item>
+<item> joyaxis7 </item>
+<item> joyaxis8 </item>
+<item> joyforwardsensitivity </item>
+<item> joyforwardthreshold </item>
+<item> joyname </item>
+<item> joypitchsensitivity </item>
+<item> joypitchthreshold </item>
+<item> joysidesensitivity </item>
+<item> joysidethreshold </item>
+<item> joystick </item>
+<item> joywwhack1 </item>
+<item> joywwhack2 </item>
+<item> joyyawsensitivity </item>
+<item> joyyawthreshold </item>
+<item> khz </item>
+<item> lcd_x </item>
+<item> lcd_yaw </item>
+<item> lerpmodels </item>
+<item> lightmap </item>
+<item> lights </item>
+<item> limit </item>
+<item> listen </item>
+<item> loadas </item>
+<item> loadas8bit </item>
+<item> localid </item>
+<item> lockpvs </item>
+<item> log </item>
+<item> log_stats </item>
+<item> logfile </item>
+<item> lookspring </item>
+<item> lookstrafe </item>
+<item> loopcount </item>
+<item> looptrack </item>
+<item> m_filter </item>
+<item> m_forward </item>
+<item> m_pitch </item>
+<item> m_side </item>
+<item> m_yaw </item>
+<item> mapname </item>
+<item> maps </item>
+<item> max </item>
+<item> maxclients </item>
+<item> maxedges </item>
+<item> maxentities </item>
+<item> maxfps </item>
+<item> maxplayers </item>
+<item> maxspectators </item>
+<item> maxsurfs </item>
+<item> maxvelocity </item>
+<item> min </item>
+<item> mipcap </item>
+<item> mipscale </item>
+<item> mixahead </item>
+<item> mode </item>
+<item> model </item>
+<item> models </item>
+<item> modex </item>
+<item> modulate </item>
+<item> monolightmap </item>
+<item> mouse </item>
+<item> mp_currentPlayerType </item>
+<item> mp_currentTeam </item>
+<item> mp_playerType </item>
+<item> mp_team </item>
+<item> mp_weapon </item>
+<item> mp_autokick </item>
+<item> mp_autoteambalance </item>
+<item> mp_c4timer </item>
+<item> mp_flashlight </item>
+<item> mp_footsteps </item>
+<item> mp_forcechasecam </item>
+<item> mp_freezetime </item>
+<item> mp_friendlyfire </item>
+<item> mp_hostagepenalty </item>
+<item> mp_limitteams </item>
+<item> mp_logmessages </item>
+<item> mp_mapvoteration </item>
+<item> mp_roundtime </item>
+<item> mp_timelimit </item>
+<item> mp_tkpunish </item>
+<item> msg </item>
+<item> msgs </item>
+<item> multitexture </item>
+<item> name </item>
+<item> net_graph </item>
+<item> net_ip </item>
+<item> net_messagetimeout </item>
+<item> net_noudp </item>
+<item> net_port </item>
+<item> net_qport </item>
+<item> net_restart </item>
+<item> netdosexpire </item>
+<item> netdosvalues </item>
+<item> netgraph </item>
+<item> nextdemo </item>
+<item> nextmap </item>
+<item> nextserver </item>
+<item> noalttab </item>
+<item> nobind </item>
+<item> nocd </item>
+<item> nocull </item>
+<item> nodelta </item>
+<item> noexit </item>
+<item> nomonsters </item>
+<item> norefresh </item>
+<item> noreload </item>
+<item> noskins </item>
+<item> nosound </item>
+<item> nosubimage </item>
+<item> novis </item>
+<item> palettedtexture </item>
+<item> particle </item>
+<item> particles </item>
+<item> password </item>
+<item> pausable </item>
+<item> persecond </item>
+<item> picmip </item>
+<item> pitch </item>
+<item> pitchsensitivity </item>
+<item> pitchspeed </item>
+<item> pitchthreshold </item>
+<item> playermip </item>
+<item> players </item>
+<item> pointparameters </item>
+<item> polyblend </item>
+<item> polymodelstats </item>
+<item> port </item>
+<item> pr_checkextensions </item>
+<item> pr_deadbeef_ents </item>
+<item> pr_deadbeef_locals </item>
+<item> pr_debug </item>
+<item> pr_source_path </item>
+<item> precache </item>
+<item> predict </item>
+<item> primary </item>
+<item> printspeed </item>
+<item> protocol </item>
+<item> public </item>
+<item> pushlatency </item>
+<item> qport </item>
+<item> r_aliastransadj </item>
+<item> r_aliastransbase </item>
+<item> r_allowExtensions </item>
+<item> r_allowSoftwareGL </item>
+<item> r_ambient </item>
+<item> r_ambientScale </item>
+<item> r_bonesDebug </item>
+<item> r_cache </item>
+<item> r_cacheModels </item>
+<item> r_cacheShaders </item>
+<item> r_clear </item>
+<item> r_clearcolor </item>
+<item> r_colorMipLevels </item>
+<item> r_colorbits </item>
+<item> r_compressModels </item>
+<item> r_customaspect </item>
+<item> r_customheight </item>
+<item> r_customwidth </item>
+<item> r_debugSort </item>
+<item> r_debugSurface </item>
+<item> r_debuglight </item>
+<item> r_depthbits </item>
+<item> r_detailtextures </item>
+<item> r_directedScale </item>
+<item> r_displayRefresh </item>
+<item> r_dlightBacks </item>
+<item> r_dlight_lightmap </item>
+<item> r_dlight_max </item>
+<item> r_drawBuffer </item>
+<item> r_drawSun </item>
+<item> r_drawentities </item>
+<item> r_drawexplosions </item>
+<item> r_drawflat </item>
+<item> r_draworder </item>
+<item> r_drawviewmodel </item>
+<item> r_drawworld </item>
+<item> r_dspeeds </item>
+<item> r_dynamic </item>
+<item> r_dynamiclight </item>
+<item> r_explosionclip </item>
+<item> r_exportCompressedModels </item>
+<item> r_ext_compiled_vertex_array </item>
+<item> r_ext_compress_textures </item>
+<item> r_ext_compressed_textures </item>
+<item> r_ext_gamma_control </item>
+<item> r_ext_multitexture </item>
+<item> r_ext_texture_env_add </item>
+<item> r_facePlaneCull </item>
+<item> r_fastsky </item>
+<item> r_finish </item>
+<item> r_firecolor </item>
+<item> r_flareFade </item>
+<item> r_flareSize </item>
+<item> r_flares </item>
+<item> r_fullbright </item>
+<item> r_fullscreen </item>
+<item> r_gamma </item>
+<item> r_glDriver </item>
+<item> r_glIgnoreWicked3D </item>
+<item> r_graphheight </item>
+<item> r_highQualit </item>
+<item> r_highQualityVideo </item>
+<item> r_ignore </item>
+<item> r_ignoreFastPath </item>
+<item> r_ignoreGLErrors </item>
+<item> r_ignorehwgamma </item>
+<item> r_inGameVideo </item>
+<item> r_intensity </item>
+<item> r_lastValidRenderer </item>
+<item> r_lerpmodels </item>
+<item> r_lightmap </item>
+<item> r_lightmap_components </item>
+<item> r_lockpvs </item>
+<item> r_lodCurveError </item>
+<item> r_lodbias </item>
+<item> r_lodscale </item>
+<item> r_logFile </item>
+<item> r_lowMemTextureSize </item>
+<item> r_lowMemTextureThreshold </item>
+<item> r_mapOverBrightBits </item>
+<item> r_maxedges </item>
+<item> r_maxpolys </item>
+<item> r_maxpolyverts </item>
+<item> r_maxsurfs </item>
+<item> r_measureOverdraw </item>
+<item> r_mirroralpha </item>
+<item> r_mode </item>
+<item> r_netgraph </item>
+<item> r_netgraph_alpha </item>
+<item> r_nobind </item>
+<item> r_nocull </item>
+<item> r_nocurves </item>
+<item> r_noportals </item>
+<item> r_norefresh </item>
+<item> r_novis </item>
+<item> r_numedges </item>
+<item> r_numsurfs </item>
+<item> r_offsetfactor </item>
+<item> r_offsetunits </item>
+<item> r_overBrightBits </item>
+<item> r_particles_max </item>
+<item> r_particles_style </item>
+<item> r_picmip </item>
+<item> r_picmip2 </item>
+<item> r_polymodelstats </item>
+<item> r_portalOnly </item>
+<item> r_preloadTextures </item>
+<item> r_previousglDriver </item>
+<item> r_primitives </item>
+<item> r_printShaders </item>
+<item> r_railCoreWidth </item>
+<item> r_railSegmentLength </item>
+<item> r_railWidth </item>
+<item> r_reportedgeout </item>
+<item> r_reportsurfout </item>
+<item> r_rmse </item>
+<item> r_roundImagesDown </item>
+<item> r_saveFontData </item>
+<item> r_shadows </item>
+<item> r_showImages </item>
+<item> r_showSmp </item>
+<item> r_showcluster </item>
+<item> r_shownormals </item>
+<item> r_showsky </item>
+<item> r_showtris </item>
+<item> r_simpleMipMaps </item>
+<item> r_singleShader </item>
+<item> r_skipBackEnd </item>
+<item> r_skyname </item>
+<item> r_smp </item>
+<item> r_speeds </item>
+<item> r_stencilbits </item>
+<item> r_stereo </item>
+<item> r_subdivisions </item>
+<item> r_swapInterval </item>
+<item> r_textureMode </item>
+<item> r_texturebits </item>
+<item> r_timegraph </item>
+<item> r_uiFullScreen </item>
+<item> r_verbose </item>
+<item> r_vertexLight </item>
+<item> r_wateralpha </item>
+<item> r_waterwarp </item>
+<item> r_wolffog </item>
+<item> r_zfar </item>
+<item> r_znear </item>
+<item> rate </item>
+<item> rcon </item>
+<item> rconAddress </item>
+<item> rconPassword </item>
+<item> rcon_address </item>
+<item> rcon_password </item>
+<item> reconnect </item>
+<item> ref </item>
+<item> registered </item>
+<item> reportedgeout </item>
+<item> reportsurfout </item>
+<item> roll </item>
+<item> rollangle </item>
+<item> rollspeed </item>
+<item> round </item>
+<item> run </item>
+<item> run_pitch </item>
+<item> run_roll </item>
+<item> s_compression </item>
+<item> s_defaultsound </item>
+<item> s_doppler </item>
+<item> s_initsound </item>
+<item> s_khz </item>
+<item> s_loadas8bit </item>
+<item> s_mixPreStep </item>
+<item> s_mixahead </item>
+<item> s_musicvolume </item>
+<item> s_mute </item>
+<item> s_nocompressed </item>
+<item> s_usingA3D </item>
+<item> s_primary </item>
+<item> s_separation </item>
+<item> s_show </item>
+<item> s_testsound </item>
+<item> s_volume </item>
+<item> s_wavonly </item>
+<item> samelevel </item>
+<item> saturatelighting </item>
+<item> saved1 </item>
+<item> saved2 </item>
+<item> saved3 </item>
+<item> saved4 </item>
+<item> savedgamecfg </item>
+<item> scr </item>
+<item> scr_centertime </item>
+<item> scr_consize </item>
+<item> scr_conspeed </item>
+<item> scr_drawall </item>
+<item> scr_ofsx </item>
+<item> scr_ofsy </item>
+<item> scr_ofsz </item>
+<item> scr_printspeed </item>
+<item> scr_showpause </item>
+<item> scr_showturtle </item>
+<item> scratch1 </item>
+<item> scratch2 </item>
+<item> scratch3 </item>
+<item> scratch4 </item>
+<item> screenshot </item>
+<item> select </item>
+<item> sensitivity </item>
+<item> separation </item>
+<item> server1 </item>
+<item> server10 </item>
+<item> server11 </item>
+<item> server12 </item>
+<item> server13 </item>
+<item> server14 </item>
+<item> server15 </item>
+<item> server16 </item>
+<item> server2 </item>
+<item> server3 </item>
+<item> server4 </item>
+<item> server5 </item>
+<item> server6 </item>
+<item> server7 </item>
+<item> server8 </item>
+<item> server9 </item>
+<item> serverprofile </item>
+<item> sex </item>
+<item> shadows </item>
+<item> show </item>
+<item> showclamp </item>
+<item> showdrop </item>
+<item> showmiss </item>
+<item> shownet </item>
+<item> showpackets </item>
+<item> showpause </item>
+<item> showram </item>
+<item> showtrace </item>
+<item> showtris </item>
+<item> showturtle </item>
+<item> side </item>
+<item> sidesensitivity </item>
+<item> sidespeed </item>
+<item> sidethreshold </item>
+<item> size </item>
+<item> skill </item>
+<item> skin </item>
+<item> skymip </item>
+<item> snaps </item>
+<item> snd_bits </item>
+<item> snd_device </item>
+<item> snd_interp </item>
+<item> snd_loadas8bit </item>
+<item> snd_mixahead </item>
+<item> snd_noextraupdate </item>
+<item> snd_oss_mmaped </item>
+<item> snd_output </item>
+<item> snd_phasesep </item>
+<item> snd_rate </item>
+<item> snd_render </item>
+<item> snd_show </item>
+<item> snd_stereo </item>
+<item> snd_volumesep </item>
+<item> sndbits </item>
+<item> sndchannels </item>
+<item> snddevice </item>
+<item> sndspeed </item>
+<item> software </item>
+<item> sounds </item>
+<item> spectator </item>
+<item> spectator_password </item>
+<item> speeds </item>
+<item> stats </item>
+<item> stereo </item>
+<item> stipplealpha </item>
+<item> surfcacheoverride </item>
+<item> sv </item>
+<item> sv_accelerate </item>
+<item> sv_aim </item>
+<item> sv_airaccelerate </item>
+<item> sv_allowAnonymous </item>
+<item> sv_allowDownload </item>
+<item> sv_cheats </item>
+<item> sv_enforcetime </item>
+<item> sv_floodProtect </item>
+<item> sv_fps </item>
+<item> sv_friction </item>
+<item> sv_gravity </item>
+<item> sv_hostname </item>
+<item> sv_idealpitchscale </item>
+<item> sv_keywords </item>
+<item> sv_killserver </item>
+<item> sv_mapChecksum </item>
+<item> sv_master1 </item>
+<item> sv_master2 </item>
+<item> sv_master3 </item>
+<item> sv_master4 </item>
+<item> sv_master5 </item>
+<item> sv_maxPing </item>
+<item> sv_maxRate </item>
+<item> sv_maxclients </item>
+<item> sv_maxrate </item>
+<item> sv_maxspeed </item>
+<item> sv_maxtic </item>
+<item> sv_maxvelocity </item>
+<item> sv_minPing </item>
+<item> sv_minqfversion </item>
+<item> sv_mintic </item>
+<item> sv_netdosprotect </item>
+<item> sv_noreload </item>
+<item> sv_nostep </item>
+<item> sv_onlyVisibleClients </item>
+<item> sv_padPackets </item>
+<item> sv_pakNames </item>
+<item> sv_paks </item>
+<item> sv_paused </item>
+<item> sv_phs </item>
+<item> sv_privateClients </item>
+<item> sv_privatePassword </item>
+<item> sv_progs </item>
+<item> sv_pure </item>
+<item> sv_reconnect_limit </item>
+<item> sv_reconnectlimit </item>
+<item> sv_referencedPakNames </item>
+<item> sv_referencedPaks </item>
+<item> sv_restartround </item>
+<item> sv_rollangle </item>
+<item> sv_rollspeed </item>
+<item> sv_running </item>
+<item> sv_serverid </item>
+<item> sv_showAverageBPS </item>
+<item> sv_showloss </item>
+<item> sv_spectalk </item>
+<item> sv_stopspeed </item>
+<item> sv_timefmt </item>
+<item> sv_timekick </item>
+<item> sv_timekick_fuzz </item>
+<item> sv_timekick_interval </item>
+<item> sv_timeout </item>
+<item> sv_timestamps </item>
+<item> sv_wateraccelerate </item>
+<item> sv_waterfriction </item>
+<item> sv_zombietime </item>
+<item> sw </item>
+<item> sw_allow_modex </item>
+<item> sw_clearcolor </item>
+<item> sw_drawflat </item>
+<item> sw_draworder </item>
+<item> sw_maxedges </item>
+<item> sw_maxsurfs </item>
+<item> sw_mipcap </item>
+<item> sw_mipscale </item>
+<item> sw_mode </item>
+<item> sw_polymodelstats </item>
+<item> sw_reportedgeout </item>
+<item> sw_reportsurfout </item>
+<item> sw_stipplealpha </item>
+<item> sw_surfcacheoverride </item>
+<item> sw_waterwarp </item>
+<item> swapinterval </item>
+<item> sys_cpustring </item>
+<item> sys_nostdout </item>
+<item> sys_sleep </item>
+<item> sys_ticrate </item>
+<item> team </item>
+<item> team_headmodel </item>
+<item> team_model </item>
+<item> teamplay </item>
+<item> teamtask </item>
+<item> temp1 </item>
+<item> testblend </item>
+<item> testentities </item>
+<item> testlights </item>
+<item> testparticles </item>
+<item> testsound </item>
+<item> texturealphamode </item>
+<item> texturemode </item>
+<item> texturesolidmode </item>
+<item> timedemo </item>
+<item> timegraph </item>
+<item> timelimit </item>
+<item> timeout </item>
+<item> timescale </item>
+<item> topcolor </item>
+<item> triplebuffer </item>
+<item> ttycon </item>
+<item> ui_Q3Model </item>
+<item> ui_actualNetGametype </item>
+<item> ui_bigFont </item>
+<item> ui_browserGameType </item>
+<item> ui_browserMaster </item>
+<item> ui_browserShowEmpty </item>
+<item> ui_browserShowFriendlyFire </item>
+<item> ui_browserShowFull </item>
+<item> ui_browserShowMaxlives </item>
+<item> ui_browserShowTourney </item>
+<item> ui_browserSortKey </item>
+<item> ui_cdkeychecked </item>
+<item> ui_class </item>
+<item> ui_cmd </item>
+<item> ui_ctf_capturelimit </item>
+<item> ui_ctf_friendly </item>
+<item> ui_ctf_timelimit </item>
+<item> ui_currentMap </item>
+<item> ui_currentNetMap </item>
+<item> ui_dedicated </item>
+<item> ui_ffa_fraglimit </item>
+<item> ui_ffa_timelimit </item>
+<item> ui_gametype </item>
+<item> ui_glCustom </item>
+<item> ui_isSpectator </item>
+<item> ui_joinGametype </item>
+<item> ui_limboMode </item>
+<item> ui_limboObjective </item>
+<item> ui_limboOptions </item>
+<item> ui_limboPrevOptions </item>
+<item> ui_mapIndex </item>
+<item> ui_master </item>
+<item> ui_menuFiles </item>
+<item> ui_mousePitch </item>
+<item> ui_netGametype </item>
+<item> ui_netSource </item>
+<item> ui_notebookCurrentPage </item>
+<item> ui_objective </item>
+<item> ui_prevClass </item>
+<item> ui_prevTeam </item>
+<item> ui_prevWeapon </item>
+<item> ui_serverStatusTimeOut </item>
+<item> ui_singlePlayerActive </item>
+<item> ui_smallFont </item>
+<item> ui_spSelection </item>
+<item> ui_team </item>
+<item> ui_teamArenaFirstRun </item>
+<item> ui_team_fraglimit </item>
+<item> ui_team_friendly </item>
+<item> ui_team_timelimit </item>
+<item> ui_tourney_fraglimit </item>
+<item> ui_tourney_timelimit </item>
+<item> ui_userAlliedRespawnTime </item>
+<item> ui_userAxisRespawnTime </item>
+<item> ui_userTimeLimit </item>
+<item> ui_weapon </item>
+<item> up </item>
+<item> upsensitivity </item>
+<item> upspeed </item>
+<item> upthreshold </item>
+<item> username </item>
+<item> v_centermove </item>
+<item> v_centerspeed </item>
+<item> v_idlescale </item>
+<item> v_ipitch_cycle </item>
+<item> v_ipitch_level </item>
+<item> v_iroll_cycle </item>
+<item> v_iroll_level </item>
+<item> v_iuaw_cycle </item>
+<item> v_iyaw_cycle </item>
+<item> v_iyaw_level </item>
+<item> v_kickpitch </item>
+<item> v_kickroll </item>
+<item> v_kicktime </item>
+<item> version </item>
+<item> vertex </item>
+<item> vid </item>
+<item> vid_config_x </item>
+<item> vid_config_y </item>
+<item> vid_fullscreen </item>
+<item> vid_fullscreen_mode </item>
+<item> vid_gamma </item>
+<item> vid_height </item>
+<item> vid_mode </item>
+<item> vid_nopageflip </item>
+<item> vid_ref </item>
+<item> vid_system_gamma </item>
+<item> vid_use8bit </item>
+<item> vid_wait </item>
+<item> vid_width </item>
+<item> vid_window_x </item>
+<item> vid_window_y </item>
+<item> vid_windowed_mode </item>
+<item> vid_xpos </item>
+<item> vid_ypos </item>
+<item> viewlog </item>
+<item> viewsize </item>
+<item> vm_cgame </item>
+<item> vm_game </item>
+<item> vm_ui </item>
+<item> volume </item>
+<item> vwep </item>
+<item> waitdelay </item>
+<item> waterwarp </item>
+<item> wavonly </item>
+<item> win </item>
+<item> win_noalttab </item>
+<item> win_hinstance </item>
+<item> win_wndproc </item>
+<item> xpos </item>
+<item> yaw </item>
+<item> yawsensitivity </item>
+<item> yawspeed </item>
+<item> yawthreshold </item>
+<item> ypos </item>
+<item> zombietime </item>
+<item> ztrick </item>
+</list>
+
+
+<list name="Actions">
+<item> +attack </item>
+<item> +ttack2 </item>
+<item> +alt1 </item>
+<item> +activate </item>
+<item> +back </item>
+<item> +break </item>
+<item> +button0 </item>
+<item> +button1 </item>
+<item> +button10 </item>
+<item> +button11 </item>
+<item> +button12 </item>
+<item> +button13 </item>
+<item> +button14 </item>
+<item> +button2 </item>
+<item> +button3 </item>
+<item> +button4 </item>
+<item> +button5 </item>
+<item> +button6 </item>
+<item> +button7 </item>
+<item> +button8 </item>
+<item> +button9 </item>
+<item> +camdistance </item>
+<item> +camin </item>
+<item> +cammousemove </item>
+<item> +camout </item>
+<item> +campitchdown </item>
+<item> +campitchup </item>
+<item> +camyawleft </item>
+<item> +camyawright </item>
+<item> +commandmenu </item>
+<item> +dropweapon </item>
+<item> +duck </item>
+<item> +forward </item>
+<item> +graph </item>
+<item> +jlook </item>
+<item> +jump </item>
+<item> +kick </item>
+<item> +klook </item>
+<item> +leanleft </item>
+<item> +leanright </item>
+<item> +left </item>
+<item> +lookdown </item>
+<item> +lookup </item>
+<item> +mlook </item>
+<item> +movedown </item>
+<item> +moveleft </item>
+<item> +moveright </item>
+<item> +moveup </item>
+<item> +nvgadjust </item>
+<item> +quickgren </item>
+<item> +reload </item>
+<item> +right </item>
+<item> +salute </item>
+<item> +score </item>
+<item> +showscores </item>
+<item> +speed </item>
+<item> +sprint </item>
+<item> +strafe </item>
+<item> +use </item>
+<item> +useitem </item>
+<item> +voicerecord </item>
+<item> +wbutton7 </item>
+<item> +zoom </item>
+<item> -activate </item>
+<item> -alt1 </item>
+<item> -attack </item>
+<item> -attack2 </item>
+<item> -back </item>
+<item> -break </item>
+<item> -button0 </item>
+<item> -button1 </item>
+<item> -button10 </item>
+<item> -button11 </item>
+<item> -button12 </item>
+<item> -button13 </item>
+<item> -button14 </item>
+<item> -button2 </item>
+<item> -button3 </item>
+<item> -button4 </item>
+<item> -button5 </item>
+<item> -button6 </item>
+<item> -button7 </item>
+<item> -button8 </item>
+<item> -button9 </item>
+<item> -camdistance </item>
+<item> -camin </item>
+<item> -cammousemove </item>
+<item> -camout </item>
+<item> -campitchdown </item>
+<item> -campitchup </item>
+<item> -camyawleft </item>
+<item> -camyawright </item>
+<item> -commandmenu </item>
+<item> -dropweapon </item>
+<item> -duck </item>
+<item> -forward </item>
+<item> -graph </item>
+<item> -jlook </item>
+<item> -jump </item>
+<item> -kick </item>
+<item> -klook </item>
+<item> -leanleft </item>
+<item> -leanright </item>
+<item> -left </item>
+<item> -lookdown </item>
+<item> -lookup </item>
+<item> -mlook </item>
+<item> -movedown </item>
+<item> -moveleft </item>
+<item> -moveright </item>
+<item> -moveup </item>
+<item> -nvgadjust </item>
+<item> -quickgren </item>
+<item> -reload </item>
+<item> -right </item>
+<item> -salute </item>
+<item> -score </item>
+<item> -showscores </item>
+<item> -speed </item>
+<item> -sprint </item>
+<item> -strafe </item>
+<item> -use </item>
+<item> -useitem </item>
+<item> -voicerecord </item>
+<item> -wbutton7 </item>
+<item> -zoom </item>
+</list>
+
+
+<list name="KeyTypes">
+<item> * </item>
+<item> [ </item>
+<item> ] </item>
+<item> \ </item>
+<item> / </item>
+<item> ' </item>
+<item> = </item>
+<item> - </item>
+<item> + </item>
+<item> , </item>
+<item> . </item>
+<item> ` </item>
+<item> ~ </item>
+<item> 1 </item>
+<item> 2 </item>
+<item> 3 </item>
+<item> 4 </item>
+<item> 5 </item>
+<item> 6 </item>
+<item> 7 </item>
+<item> 8 </item>
+<item> 9 </item>
+<item> 0 </item>
+<item> a </item>
+<item> b </item>
+<item> c </item>
+<item> d </item>
+<item> e </item>
+<item> f </item>
+<item> g </item>
+<item> h </item>
+<item> i </item>
+<item> j </item>
+<item> k </item>
+<item> l </item>
+<item> m </item>
+<item> n </item>
+<item> o </item>
+<item> p </item>
+<item> q </item>
+<item> r </item>
+<item> s </item>
+<item> t </item>
+<item> u </item>
+<item> v </item>
+<item> x </item>
+<item> w </item>
+<item> y </item>
+<item> z </item>
+<item> ALT </item>
+<item> AUX1 </item>
+<item> AUX10 </item>
+<item> AUX11 </item>
+<item> AUX12 </item>
+<item> AUX13 </item>
+<item> AUX14 </item>
+<item> AUX15 </item>
+<item> AUX16 </item>
+<item> AUX17 </item>
+<item> AUX18 </item>
+<item> AUX2 </item>
+<item> AUX20 </item>
+<item> AUX21 </item>
+<item> AUX22 </item>
+<item> AUX23 </item>
+<item> AUX24 </item>
+<item> AUX25 </item>
+<item> AUX26 </item>
+<item> AUX27 </item>
+<item> AUX28 </item>
+<item> AUX29 </item>
+<item> AUX3 </item>
+<item> AUX30 </item>
+<item> AUX31 </item>
+<item> AUX32 </item>
+<item> AUX4 </item>
+<item> AUX5 </item>
+<item> AUX6 </item>
+<item> AUX7 </item>
+<item> AUX8 </item>
+<item> AUX9 </item>
+<item> BACKSPACE </item>
+<item> CTRL </item>
+<item> DEL </item>
+<item> DOWNARROW </item>
+<item> END </item>
+<item> ENTER </item>
+<item> ESCAPE </item>
+<item> F1 </item>
+<item> F10 </item>
+<item> F11 </item>
+<item> F12 </item>
+<item> F2 </item>
+<item> F3 </item>
+<item> F4 </item>
+<item> F5 </item>
+<item> F6 </item>
+<item> F7 </item>
+<item> F8 </item>
+<item> F9 </item>
+<item> HOME </item>
+<item> INS </item>
+<item> JOY1 </item>
+<item> JOY2 </item>
+<item> JOY3 </item>
+<item> JOY4 </item>
+<item> KP_SLASH </item>
+<item> KP_5 </item>
+<item> KP_UPARROW </item>
+<item> KP_LEFTARROW </item>
+<item> KP_RIGHTARROW </item>
+<item> KP_DOWNARROW </item>
+<item> KP_HOME </item>
+<item> KP_END </item>
+<item> KP_PGUP </item>
+<item> KP_PGDN </item>
+<item> KP_INS </item>
+<item> KP_DEL </item>
+<item> LEFTARROW </item>
+<item> MOUSE1 </item>
+<item> MOUSE2 </item>
+<item> MOUSE3 </item>
+<item> MWHEELDOWN </item>
+<item> MWHEELUP </item>
+<item> PAUSE </item>
+<item> PGDN </item>
+<item> PGUP </item>
+<item> RIGHTARROW </item>
+<item> SEMICOLON </item>
+<item> CAPSLOCK </item>
+<item> SHIFT </item>
+<item> SPACE </item>
+<item> TAB </item>
+<item> UPARROW </item>
+</list>
+
+ <list name="Symbols">
+ <item> ; </item>
+ <item> $ </item>
+ </list>
+ <list name="BindFamily">
+ <item> bind </item>
+ <item> unbind </item>
+ </list>
+ <list name="setFamily">
+ <item> set </item>
+ <item> seta </item>
+ <item> setu </item>
+ <item> sets </item>
+ </list>
+ <list name="PrintsString">
+ <item> echo </item>
+ <item> say_team </item>
+ <item> say </item>
+ </list>
+
+ <contexts>
+ <context attribute="Normal Text" lineEndContext="#stay" name="Normal Text">
+ <keyword attribute="Command" context="Arg area" String="Commands"/>
+ <keyword attribute="Variable" context="Arg area" String="Variables"/>
+ <keyword attribute="Action" context="#stay" String="Actions"/>
+ <keyword attribute="Symbol" context="#stay" String="Symbols"/>
+ <keyword attribute="Command" context="Bind phrase" String="BindFamily"/>
+ <keyword attribute="Command" context="Alias phrase" String="setFamily"/>
+ <keyword attribute="Command" context="Echo" String="PrintsString"/>
+ <StringDetect attribute="Command" context="Alias phrase" String="alias" insensitive="TRUE"/>
+ <keyword attribute="Command" context="Echo" String="PrintsString"/>
+ <DetectChar attribute="Symbol" context="#stay" char="$"/>
+ <Detect2Chars attribute="Comment" context="Comment" char="/" char1="/"/>
+ </context>
+ <context attribute="Comment" lineEndContext="Normal Text" name="Comment"/>
+ <context attribute="String" lineEndContext="Normal Text" name="String">
+ <DetectChar attribute="String" context="#pop" char="&quot;"/>
+ </context>
+ <context attribute="Normal Text" lineEndContext="Normal Text" name="Arg area">
+ <DetectChar attribute="String" context="String" char="&quot;"/>
+ <Float attribute="Float" context="#stay"/>
+ <Int attribute="Int" context="#stay"/>
+ <DetectChar attribute="Symbol" context="#pop" char=";"/>
+ <Detect2Chars attribute="Comment" context="Comment" char="/" char1="/"/>
+ <DetectChar attribute="Symbol" context="Normal Text" char="$"/>
+ </context>
+ <context attribute="Normal Text" lineEndContext="Normal Text" name="Arg area in sub phrase">
+ <Float attribute="Float" context="#stay"/>
+ <Int attribute="Int" context="#stay"/>
+ <Detect2Chars attribute="Comment" context="Comment" char="/" char1="/"/>
+ <DetectChar attribute="Symbol" context="#pop" char=";"/>
+ </context>
+ <context attribute="Normal Text" lineEndContext="Normal Text" name="Sub phrase">
+ <keyword attribute="Command" context="Arg area in sub phrase" String="Commands"/>
+ <keyword attribute="Variable" context="Arg area in sub phrase" String="Variables"/>
+ <keyword attribute="Action" context="#stay" String="Actions"/>
+ <keyword attribute="Symbol" context="#stay" String="Symbols"/>
+ <keyword attribute="Command" context="Bind phrase" String="BindFamily"/>
+ <keyword attribute="Command" context="Normal Text" String="setFamily"/>
+ <keyword attribute="Command" context="Echo" String="PrintsString"/>
+ <StringDetect attribute="Command" context="Alias phrase" String="alias"/>
+ <Detect2Chars attribute="Comment" context="Comment" char="/" char1="/"/>
+ <DetectChar attribute="Symbol" context="#stay" char=";"/>
+ <Int attribute="Int" context="#stay"/>
+ <DetectChar attribute="Symbol" context="#pop" char=";"/>
+ </context>
+ <context attribute="Normal Text" lineEndContext="Normal Text" name="Bind phrase">
+ <keyword attribute="Hex" context="Sub phrase" String="KeyTypes"/>
+ </context>
+ <context attribute="Identifier" lineEndContext="Normal Text" name="Alias phrase">
+ <DetectChar attribute="Identifier" context="Sub phrase" char=" "/>
+ </context>
+ <context attribute="String" lineEndContext="Normal Text" name="Echo">
+ <DetectChar attribute="Symbol" context="#pop" char=";"/>
+ <Detect2Chars attribute="Comment" context="Comment" char="/" char1="/"/>
+ </context>
+ </contexts>
+ <itemDatas>
+ <itemData name="Command" defStyleNum="dsKeyword" />
+ <itemData name="Action" defStyleNum="dsNormal" color="#ffcc00" italic="1" selColor="#00ff00" bold="1" />
+ <itemData name="Variable" defStyleNum="dsNormal" color="#0000ff" italic="1" selColor="#00ff00" bold="1" />
+ <itemData name="Normal Text" defStyleNum="dsNormal" />
+ <itemData name="Comment" defStyleNum="dsComment" />
+ <itemData name="Float" defStyleNum="dsFloat" />
+ <itemData name="Int" defStyleNum="dsFloat" />
+ <itemData name="String" defStyleNum="dsNormal" color="#ff0000" italic="1" selColor="#00ff00" bold="0" />
+ <itemData name="String Char" defStyleNum="dsChar" />
+ <itemData name="Hex" defStyleNum="dsBaseN" />
+ <itemData name="Symbol" defStyleNum="dsOthers" />
+ <itemData name="Identifier" defStyleNum="dsDataType" />
+ </itemDatas>
+ </highlighting>
+ <general>
+ <comments>
+ <comment name="singleLine" start="//"/>
+ </comments>
+ <keywords weakDeliminator=",+~-=/\.[]$" additionalDeliminator="&quot;;$" casesensitive="0"/>
+ </general>
+</language>
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE language SYSTEM "language.dtd">
+<language name="IDL" version="1.03" kateversion="2.1" section="Sources" extensions="*.idl" mimetype="text/x-idl-src">
+ <highlighting>
+ <list name="keywords">
+ <item> any </item>
+ <item> attribute </item>
+ <item> case </item>
+ <item> const </item>
+ <item> context </item>
+ <item> default </item>
+ <item> enum </item>
+ <item> exception </item>
+ <item> FALSE </item>
+ <item> fixed </item>
+ <item> public </item>
+ <item> in </item>
+ <item> inout </item>
+ <item> interface </item>
+ <item> module </item>
+ <item> Object </item>
+ <item> oneway </item>
+ <item> out </item>
+ <item> raises </item>
+ <item> readonly </item>
+ <item> sequence </item>
+ <item> struct </item>
+ <item> switch </item>
+ <item> TRUE </item>
+ <item> typedef </item>
+ <item> unsigned </item>
+ <item> union </item>
+ </list>
+ <list name="types">
+ <item> boolean </item>
+ <item> char </item>
+ <item> double </item>
+ <item> float </item>
+ <item> long </item>
+ <item> octet </item>
+ <item> short </item>
+ <item> string </item>
+ <item> void </item>
+ <item> wchar </item>
+ <item> wstring </item>
+ </list>
+ <contexts>
+ <context attribute="Normal Text" lineEndContext="#stay" name="Normal">
+ <keyword attribute="Keyword" context="#stay" String="keywords" />
+ <keyword attribute="Data Type" context="#stay" String="types" />
+ <HlCOct attribute="Octal" context="#stay"/>
+ <HlCHex attribute="Hex" context="#stay"/>
+ <HlCChar attribute="Char" context="#stay"/>
+ <DetectChar attribute="String" context="String" char="&quot;"/>
+ <Detect2Chars attribute="Comment" context="Commentar 1" char="/" char1="/"/>
+ <Detect2Chars attribute="Comment" context="Commentar 2" char="/" char1="*"/>
+ <AnyChar attribute="Symbol" context="#stay" String="!%&amp;()+,-&lt;=&gt;?[]^{|}~"/>
+ <StringDetect attribute="Comment" context="Some Context3" String="#if 0" insensitive="FALSE"/>
+ <RegExpr attribute="Preprocessor" context="Preprocessor" String="^#"/>
+ </context>
+ <context attribute="String" lineEndContext="#stay" name="String">
+ <LineContinue attribute="String" context="Some Context"/>
+ <HlCStringChar attribute="String Char" context="#stay"/>
+ <DetectChar attribute="String" context="#pop" char="&quot;"/>
+ </context>
+ <context attribute="Comment" lineEndContext="#pop" name="Commentar 1">
+ <RegExpr attribute="Decimal" context="#stay" String="(FIXME|TODO)" />
+ </context>
+ <context attribute="Comment" lineEndContext="#stay" name="Commentar 2">
+ <RegExpr attribute="Decimal" context="#stay" String="(FIXME|TODO)" />
+ <Detect2Chars attribute="Comment" context="#pop" char="*" char1="/"/>
+ </context>
+ <context attribute="Preprocessor" lineEndContext="#pop" name="Preprocessor">
+ <LineContinue attribute="Preprocessor" context="Some Context2"/>
+ <RangeDetect attribute="Prep. Lib" context="#stay" char="&quot;" char1="&quot;"/>
+ <RangeDetect attribute="Prep. Lib" context="#stay" char="&lt;" char1="&gt;"/>
+ <Detect2Chars attribute="Comment" context="Commentar 1" char="/" char1="/"/>
+ <Detect2Chars attribute="Comment" context="Commentar/Preprocessor" char="/" char1="*"/>
+ </context>
+ <context attribute="Comment" lineEndContext="#stay" name="Commentar/Preprocessor">
+ <Detect2Chars attribute="Comment" context="#pop" char="*" char1="/" />
+ </context>
+ <context attribute="Normal Text" lineEndContext="#pop" name="Some Context"/>
+ <context attribute="Normal Text" lineEndContext="#pop" name="Some Context2"/>
+ <context attribute="Normal Text" lineEndContext="#stay" name="Some Context3">
+ <RegExpr attribute="Decimal" context="#stay" String="(FIXME|TODO)" />
+ <RegExpr attribute="Comment" context="#pop" String="^#endif" />
+ </context>
+ </contexts>
+ <itemDatas>
+ <itemData name="Normal Text" defStyleNum="dsNormal"/>
+ <itemData name="Keyword" defStyleNum="dsKeyword"/>
+ <itemData name="Data Type" defStyleNum="dsDataType"/>
+ <itemData name="Decimal" defStyleNum="dsDecVal"/>
+ <itemData name="Octal" defStyleNum="dsBaseN"/>
+ <itemData name="Hex" defStyleNum="dsBaseN"/>
+ <itemData name="Float" defStyleNum="dsFloat"/>
+ <itemData name="Char" defStyleNum="dsChar"/>
+ <itemData name="String" defStyleNum="dsString"/>
+ <itemData name="String Char" defStyleNum="dsChar"/>
+ <itemData name="Comment" defStyleNum="dsComment"/>
+ <itemData name="Symbol" defStyleNum="dsNormal"/>
+ <itemData name="Preprocessor" defStyleNum="dsOthers"/>
+ <itemData name="Prep. Lib" defStyleNum="dsOthers"/> <!--,Qt::darkYellow,Qt::yellow,false,false)); -->
+ </itemDatas>
+ </highlighting>
+ <general>
+ <comments>
+ <comment name="singleLine" start="//" />
+ <comment name="multiLine" start="/*" end="*/" />
+ </comments>
+ <keywords casesensitive="1" />
+ </general>
+</language>
+<!DOCTYPE language SYSTEM "language.dtd">
+<language name="ILERPG" version="1.02" kateversion="2.1" section="Sources" extensions="QRPGLESRC.*" mimetype="text/x-ilerpg-src" casesensitive="0">
+ <highlighting>
+ <list name="biffs">
+ <item> ABS </item>
+ <item> ADDR </item>
+ <item> CHAR </item>
+ <item> DECH </item>
+ <item> DECPOS </item>
+ <item> DEC </item>
+ <item> EDITC </item>
+ <item> EDITFLT </item>
+ <item> EDITW </item>
+ <item> ELEM </item>
+ <item> EOF </item>
+ <item> EQUAL </item>
+ <item> ERROR </item>
+ <item> FLOAT </item>
+ <item> FOUND </item>
+ <item> INTH </item>
+ <item> INT </item>
+ <item> LEN </item>
+ <item> NULLIND </item>
+ <item> OPEN </item>
+ <item> PADDR </item>
+ <item> PARMS </item>
+ <item> REPLACE </item>
+ <item> SCAN </item>
+ <item> SIZE </item>
+ <item> STATUS </item>
+ <item> STR </item>
+ <item> SUBST </item>
+ <item> TRIMR </item>
+ <item> TRIML </item>
+ <item> TRIM </item>
+ <item> UNSH </item>
+ <item> UNS </item>
+ </list>
+ <list name="opcodes">
+ <item> Z-ADD </item>
+ <item> Z-SUB </item>
+ <item> ACQ </item>
+ <item> ADDDUR </item>
+ <item> ADD </item>
+ <item> ALLOC </item>
+ <item> ANDGT </item>
+ <item> ANDLT </item>
+ <item> ANDEQ </item>
+ <item> ANDNE </item>
+ <item> ANDGE </item>
+ <item> ANDLE </item>
+ <item> AND </item>
+ <item> BEGSR </item>
+ <item> BITOFF </item>
+ <item> BITON </item>
+ <item> CABGT </item>
+ <item> CABLT </item>
+ <item> CABEQ </item>
+ <item> CABNE </item>
+ <item> CABGE </item>
+ <item> CABLE </item>
+ <item> CAB </item>
+ <item> CALLB </item>
+ <item> CALLP </item>
+ <item> CALL </item>
+ <item> CASGT </item>
+ <item> CASLT </item>
+ <item> CASEQ </item>
+ <item> CASNE </item>
+ <item> CASGE </item>
+ <item> CASLE </item>
+ <item> CAS </item>
+ <item> CAT </item>
+ <item> CHAIN </item>
+ <item> CHECKR </item>
+ <item> CHECK </item>
+ <item> CLEAR </item>
+ <item> CLOSE </item>
+ <item> COMMIT </item>
+ <item> COMP </item>
+ <item> DEALLOC </item>
+ <item> DEFINE </item>
+ <item> DELETE </item>
+ <item> DIV </item>
+ <item> DOWGT </item>
+ <item> DOWLT </item>
+ <item> DOWEQ </item>
+ <item> DOWNE </item>
+ <item> DOWGE </item>
+ <item> DOWLE </item>
+ <item> DOUGT </item>
+ <item> DOULT </item>
+ <item> DOUEQ </item>
+ <item> DOUNE </item>
+ <item> DOUGE </item>
+ <item> DOULE </item>
+ <item> DOU </item>
+ <item> DOW </item>
+ <item> DO </item>
+ <item> DSPLY </item>
+ <item> DUMP </item>
+ <item> ELSE </item>
+ <item> ENDCS </item>
+ <item> ENDDO </item>
+ <item> ENDIF </item>
+ <item> ENDSL </item>
+ <item> ENDSR </item>
+ <item> END </item>
+ <item> EVAL </item>
+ <item> EXCEPT </item>
+ <item> EXFMT </item>
+ <item> EXSR </item>
+ <item> EXTRCT </item>
+ <item> FEOD </item>
+ <item> FORCE </item>
+ <item> GOTO </item>
+ <item> IFGT </item>
+ <item> IFLT </item>
+ <item> IFEQ </item>
+ <item> IFNE </item>
+ <item> IFGE </item>
+ <item> IFLE </item>
+ <item> IF </item>
+ <item> IN </item>
+ <item> ITER </item>
+ <item> KFLD </item>
+ <item> KLIST </item>
+ <item> LEAVE </item>
+ <item> LOOKUP </item>
+ <item> MHHZO </item>
+ <item> MHLZO </item>
+ <item> MLHZO </item>
+ <item> MLLZO </item>
+ <item> MOVE </item>
+ <item> MOVEA </item>
+ <item> MOVEL </item>
+ <item> MULT </item>
+ <item> MVR </item>
+ <item> NEXT </item>
+ <item> OCCUR </item>
+ <item> OPEN </item>
+ <item> ORGT </item>
+ <item> ORLT </item>
+ <item> OREQ </item>
+ <item> ORNE </item>
+ <item> ORGE </item>
+ <item> ORLE </item>
+ <item> OR </item>
+ <item> OTHER </item>
+ <item> OUT </item>
+ <item> PARM </item>
+ <item> PLIST </item>
+ <item> POST </item>
+ <item> READC </item>
+ <item> READE </item>
+ <item> READPE </item>
+ <item> READP </item>
+ <item> READ </item>
+ <item> REALLOC </item>
+ <item> REL </item>
+ <item> RESET </item>
+ <item> RETURN </item>
+ <item> ROLBK </item>
+ <item> SCAN </item>
+ <item> SELECT </item>
+ <item> SETGT </item>
+ <item> SETLL </item>
+ <item> SETOFF </item>
+ <item> SETON </item>
+ <item> SHTDN </item>
+ <item> SORTA </item>
+ <item> SQRT </item>
+ <item> SUBDUR </item>
+ <item> SUBST </item>
+ <item> SUB </item>
+ <item> TAG </item>
+ <item> TESTB </item>
+ <item> TESTN </item>
+ <item> TESTZ </item>
+ <item> TEST </item>
+ <item> TIME </item>
+ <item> UNLOCK </item>
+ <item> UPDATE </item>
+ <item> WHENGT </item>
+ <item> WHENLT </item>
+ <item> WHENEQ </item>
+ <item> WHENNR </item>
+ <item> WHENGE </item>
+ <item> WHENLE </item>
+ <item> WHEN </item>
+ <item> WRITE </item>
+ <item> XFOOT </item>
+ <item> XLATE </item>
+ <item> *BLANKS </item>
+ </list>
+
+ <contexts>
+
+ <context attribute="Normal Text" lineEndContext="#stay" name="Default">
+ <RegExpr attribute="Comment" context="context3" String="[POIHFDC ]?\*"/>
+ <RegExpr attribute="Keyword" context="context1" String="[POIHFDC]"/>
+ </context>
+
+ <context attribute="Normal Text" lineEndContext="#pop" name="context1">
+ <keyword attribute="Keyword" context="#stay" String="opcodes"/>
+ <DetectChar attribute="Biff" context="context7" char="%"/>
+ <DetectChar attribute="Constant" context="context3" char="'"/>
+ <Float attribute="Float" context="#stay"/>
+ <RegExpr attribute="Hex" context="#stay" String="[Xx]'[0-9|a-f|A-f]{2,}'"/>
+
+ <Int attribute="Decimal" context="#stay">
+ <StringDetect attribute="Decimal" context="#stay" String="ULL" insensitive="TRUE"/>
+ <StringDetect attribute="Decimal" context="#stay" String="LUL" insensitive="TRUE"/>
+ <StringDetect attribute="Decimal" context="#stay" String="LLU" insensitive="TRUE"/>
+ <StringDetect attribute="Decimal" context="#stay" String="UL" insensitive="TRUE"/>
+ <StringDetect attribute="Decimal" context="#stay" String="LU" insensitive="TRUE"/>
+ <StringDetect attribute="Decimal" context="#stay" String="LL" insensitive="TRUE"/>
+ <StringDetect attribute="Decimal" context="#stay" String="U" insensitive="TRUE"/>
+ <StringDetect attribute="Decimal" context="#stay" String="L" insensitive="TRUE"/>
+ </Int>
+ </context>
+
+ <context attribute="Comment" lineEndContext="#pop" name="context3">
+ <RegExpr attribute="BoldComment" context="#stay" String="\(*(FIXME|TODO)\)*" />
+ <RegExpr attribute="BoldComment" context="#stay" String="\(*(NOTE:)\)*" />
+ <DetectChar attribute="BoldComment" context="context6" char="!"/>
+ <RegExpr attribute="DivideComment" context="#stay" String="-|="/>
+ </context>
+
+ <context attribute="Constant" lineEndContext="#stay" name="context4">
+ <DetectChar attribute="Constant" context="#pop" char="'"/>
+ </context>
+
+ <context attribute="Constant" lineEndContext="#stay" name="context5">
+ <RegExpr attribute="Normal Text" context="#stay" String="[FHDICO]"/>
+ <DetectChar attribute="Constant" context="#pop" char=" "/>
+ </context>
+ <context attribute="Keyword" lineEndContext="#pop#pop" name="context6">
+ </context>
+ <context attribute="BoldComment" lineEndContext="Default" name="context7">
+ <DetectChar attribute="BoldComment" context="context3" char="!"/>
+ </context>
+ <context attribute="Biff" lineEndContext="#pop#pop#pop" name="context8">
+ <keyword attribute="Biff" context="#pop#pop" String="biffs"/>
+ <DetectChar attribute="Biff" context="#pop#pop" char=" "/>
+ </context>
+ </contexts>
+ <itemDatas>
+ <itemData name="Normal Text" defStyleNum="dsNormal" />
+ <itemData name="Keyword" defStyleNum="dsKeyword" />
+ <itemData name="Biff" defStyleNum="dsKeyword" />
+ <itemData name="RegExpr" defStyleNum="dsBaseN" />
+ <itemData name="Symbol" defStyleNum="dsNormal"/>
+ <itemData name="Decimal" defStyleNum="dsDecVal" />
+ <itemData name="Float" defStyleNum="dsFloat"/>
+ <itemData name="Octal" defStyleNum="dsBaseN"/>
+ <itemData name="Hex" defStyleNum="dsBaseN"/>
+ <itemData name="Float" defStyleNum="dsFloat"/>
+ <itemData name="Base-N" defStyleNum="dsBaseN" />
+ <itemData name="Float" defStyleNum="dsFloat" />
+ <itemData name="Char" defStyleNum="dsChar" />
+ <itemData name="Constant" defStyleNum="dsNormal" color="#FF0000" selColor="#000000" bold="0" italic="0"/>
+ <itemData name="Fill" defStyleNum="dsNormal" color="#FF6347" selColor="#000000" bold="0" italic="0"/>
+ <itemData name="Comment" defStyleNum="dsComment"/>
+ <itemData name="BoldComment" defStyleNum="dsNormal" color="#CD853F" selColor="#808080" bold="1" italic="1"/>
+ <itemData name="DivideComment" defStyleNum="dsComment" bold="1"/>
+ <itemData name="Directive" defStyleNum="dsOthers"/>
+ </itemDatas>
+ </highlighting>
+ <general>
+ <keywords casesensitive="0" />
+ </general>
+</language>
+
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE language SYSTEM "language.dtd">
+<!-- author: Giancarlo Niccolai (giancarlo@niccolai.ws) -->
+<!-- This file is released under GPL license 2.0 or any later version at your choice. -->
+<!-- $Id: inform.xml,v 1.4 2004/02/03 20:26:06 cullmann Exp $ -->
+<language name="Inform" version="1.21" kateversion="2.1" section="Sources"
+ extensions="*.inf;*.h" mimetype="text/x-inform-src"
+ author="Giancarlo Niccolai (giancarlo@niccolai.ws)" license="GPL">
+<highlighting>
+ <list name="keywords">
+ <item> if </item>
+ <item> for </item>
+ <item> else </item>
+ <item> box </item>
+ <item> break </item>
+ <item> continue </item>
+ <item> do </item>
+ <item> until </item>
+ <item> font </item>
+ <item> give </item>
+ <item> inversion </item>
+ <item> jump </item>
+ <item> on </item>
+ <item> to </item>
+ <item> move </item>
+ <item> new_line </item>
+ <item> objectloop </item>
+ <item> print </item>
+ <item> print_ret </item>
+ <item> quit </item>
+ <item> read </item>
+ <item> remove </item>
+ <item> restore </item>
+ <item> return </item>
+ <item> rtrue </item>
+ <item> rfalse </item>
+ <item> save </item>
+ <item> spaces </item>
+ <item> spring </item>
+ <item> style </item>
+ <item> switch </item>
+ </list>
+
+ <list name="functions">
+ <item> metaclass </item>
+ <item> parent </item>
+ <item> child </item>
+ <item> children </item>
+ <item> Achieved </item>
+ <item> AddToScope </item>
+ <item> allowpushdir </item>
+ <item> CDefArt </item>
+ <item> ChangeDefault </item>
+ <item> DefArt </item>
+ <item> DoMenu </item>
+ <item> EnglishNumber </item>
+ <item> HasLightSource </item>
+ <item> InDefArt </item>
+ <item> Locale </item>
+ <item> LoopOverScope </item>
+ <item> NextWord </item>
+ <item> NextWordStopped </item>
+ <item> NounDomain </item>
+ <item> ObjectIsUntouchable </item>
+ <item> OffersLight </item>
+ <item> PlaceInScope </item>
+ <item> PlayerTo </item>
+ <item> PrintShortName </item>
+ <item> ScopeWithin </item>
+ <item> SetTime </item>
+ <item> StartDaemon </item>
+ <item> StartTimer </item>
+ <item> StopDaemon </item>
+ <item> StopTimer </item>
+ <item> TestScope </item>
+ <item> TryNumber </item>
+ <item> UnsignedCompare </item>
+ <item> WordAddress </item>
+ <item> WordLenght </item>
+ <item> WriteListFrom </item>
+ <item> YesOrNo </item>
+ <item> ZRegion </item>
+ </list>
+ <list name="actions">
+ <item> Pronouns </item>
+ <item> Quit </item>
+ <item> Restore </item>
+ <item> Save </item>
+ <item> Verify </item>
+ <item> Restart </item>
+ <item> ScriptOn </item>
+ <item> ScriptOff </item>
+ <item> NotifyOn </item>
+ <item> NotifyOff </item>
+ <item> Places </item>
+ <item> Objects </item>
+ <item> Score </item>
+ <item> FullScore </item>
+ <item> Version </item>
+ <item> LMode1 </item>
+ <item> LMode2 </item>
+ <item> LMode3 </item>
+ <item> Inv </item>
+
+
+ <item> Inv </item>
+ <item> InvTall </item>
+ <item> InvWide </item>
+ <item> Take </item>
+ <item> Drop </item>
+ <item> Empty </item>
+ <item> Enter </item>
+ <item> Exit </item>
+ <item> GetOff </item>
+ <item> Go </item>
+ <item> GoIn </item>
+ <item> Look </item>
+ <item> Examine </item>
+ <item> Search </item>
+ <item> Give </item>
+ <item> Show </item>
+ <item> Unlock </item>
+ <item> Lock </item>
+ <item> SwitchOn </item>
+ <item> SwitchOff </item>
+ <item> Open </item>
+ <item> Close </item>
+ <item> Disrobe </item>
+ <item> Wear </item>
+ <item> Eat </item>
+
+ <item> LetGo </item>
+ <item> Receive </item>
+ <item> Insert </item>
+ <item> PutOn </item>
+ <item> Transfer </item>
+ <item> Empty </item>
+ <item> EmptyT </item>
+ <item> GetOff </item>
+ <item> GoIn </item>
+ <item> Listen </item>
+ <item> Taste </item>
+ <item> Touch </item>
+
+ <item> Pull </item>
+ <item> Push </item>
+ <item> Wave </item>
+ <item> Turn </item>
+ <item> PushDir </item>
+ <item> ThrowAt </item>
+ <item> ThrownAt </item>
+ <item> JumpOn </item>
+ <item> Drink </item>
+ <item> Attack </item>
+ <item> Tie </item>
+ <item> Fill </item>
+ <item> Swing </item>
+ <item> Blow </item>
+ <item> Rub </item>
+ <item> Set </item>
+ <item> SetTo </item>
+ <item> Buy </item>
+ <item> Climb </item>
+ <item> Squeeze </item>
+ <item> Climb </item>
+ <item> Burn </item>
+ <item> Cut </item>
+ <item> Dig </item>
+
+ <item> Consult </item>
+ <item> Tell </item>
+ <item> Answer </item>
+ <item> Ask </item>
+ <item> AskFor </item>
+ <item> Kiss </item>
+
+ <item> Sleep </item>
+ <item> Sing </item>
+ <item> WaveHands </item>
+ <item> Swim </item>
+ <item> Sorry </item>
+ <item> Sing </item>
+ <item> Strong </item>
+ <item> Mild </item>
+ <item> Smell </item>
+ <item> Pray </item>
+ <item> Jump </item>
+ <item> Think </item>
+ <item> VagueGo </item>
+ <item> Yes </item>
+ <item> No </item>
+ <item> Sing </item>
+ </list>
+
+ <list name="special_vals" >
+ <item> String </item>
+ <item> Routine </item>
+ <item> bold </item>
+ <item> roman </item>
+ <item> underline </item>
+ <item> fixed </item>
+ <item> nothing </item>
+ <item> true </item>
+ <item> false </item>
+ <item> on </item>
+ <item> off </item>
+ <item> sender </item>
+ <item> self </item>
+ <item> location </item>
+ <item> score </item>
+ <item> action </item>
+ <item> actor </item>
+ <item> noun </item>
+ <item> second </item>
+ <item> the_time </item>
+ <item> consult_from </item>
+ <item> consult_words </item>
+ <item> wn </item>
+ <item> actors_location </item>
+ <item> buffer </item>
+ <item> player </item>
+ </list>
+
+ <list name="pragmas" >
+ <item> Ifdef </item>
+ <item> Ifndef </item>
+ <item> Iftrue </item>
+ <item> Iffalse </item>
+ <item> Ifnot </item>
+ <item> Endif </item>
+ <item> End </item>
+ <item> Abbreviate </item>
+ <item> Array </item>
+ <item> Attribute </item>
+ <item> Constant </item>
+ <item> Default </item>
+ <item> Extend </item>
+ <item> Global </item>
+ <item> Ifnot </item>
+ <item> Iftrue </item>
+ <item> Iffalse </item>
+ <item> Import </item>
+ <item> Include </item>
+ <item> Link </item>
+ <item> Lowstring </item>
+ <item> Message </item>
+ <item> Property </item>
+ <item> Release </item>
+ <item> Replace </item>
+ <item> Serial </item>
+ <item> Switches </item>
+ <item> Statusline </item>
+ <item> score </item>
+ <item> System_file </item>
+ <item> Verb </item>
+ </list>
+
+ <list name="inline_pragmas" >
+ <item> #ifdef </item>
+ <item> #else </item>
+ <item> #ifndef </item>
+ <item> #endif </item>
+ </list>
+
+ <list name="operators">
+ <item> has </item>
+ <item> hasn't </item>
+ <item> in </item>
+ <item> notin </item>
+ <item> provides </item>
+ <item> ofclass </item>
+ <item> or </item>
+ </list>
+
+ <list name="ClassDeclOps">
+ <item> with </item>
+ <item> private </item>
+ <item> has </item>
+ </list>
+
+ <contexts>
+ <context attribute="Normal Text" lineEndContext="#stay" name="TopLevel">
+ <DetectChar attribute="Comment" context="comment" char="!" />
+ <DetectChar attribute="String" context="string" char="&quot;" />
+ <RegExpr attribute="Function" context="funcdef" beginRegion="reg_function"
+ String="[[]\s*[a-zA-Z_]+[a-zA-Z_0-9]*" />
+
+ <RegExpr String="^ *object |^ *class " attribute="Pragma"
+ context="ClassDecl"
+ insensitive="TRUE" beginRegion="reg_class" />
+
+ <keyword attribute="Pragma" context="#stay" String="pragmas" />
+ <keyword attribute="Pragma" context="#stay" String="inline_pragmas" />
+
+ <RegExpr attribute="Operator" context="#stay" String="--?>" />
+ <RegExpr attribute="Normal Text" context="#stay" String="[a-zA-Z_]+[0-9]*" />
+ <RegExpr attribute="Number" context="#stay" String="\$[0-9a-fA-F]{1,4}" />
+ <RegExpr attribute="Number" context="#stay" String="[0-9]+" />
+ </context>
+
+ <context name="Normal" attribute="Normal Text" lineEndContext="#stay">
+ <DetectChar attribute="Comment" context="comment" char="!" />
+ <DetectChar attribute="String" context="string" char="&quot;" />
+ <DetectChar attribute="Word" context="word" char="'" />
+ <keyword attribute="Pragma" context="#stay" String="inline_pragmas" />
+ <RegExpr attribute="Operator" context="#stay" String="--?>" />
+ <keyword attribute="Keyword" context="#stay" String="keywords" />
+ <keyword attribute="Actions" context="#stay" String="actions" />
+ <keyword attribute="PredefFunction" context="#stay" String="functions" />
+ <keyword attribute="Values" context="#stay" String="special_vals" />
+ <keyword attribute="Operator" context="#stay" String="operators" />
+ <RegExpr attribute="Actions" context="#stay" String="##[a-zA-Z_]+[a-zA-Z_0-9]*" />
+ <RegExpr attribute="OpCode" context="#stay" String="@[a-zA-Z_]+[a-zA-Z_0-9]*" />
+ <RegExpr attribute="Number" context="#stay" String="\$[0-9a-fA-F]{1,4}" />
+ <RegExpr String="[a-zA-Z_]+[a-zA-Z_0-9]*" attribute="Normal Text" context="#stay" />
+ <RegExpr attribute="Number" context="#stay" String="[0-9]+" />
+ <DetectChar char="{" attribute="Keyword" context="#stay"
+ beginRegion="reg_compound" />
+ <DetectChar char="}" attribute="Keyword" context="#stay"
+ endRegion="reg_compound" />
+ <RegExpr attribute="Operator" context="#stay" String="[%&amp;()+-&lt;=&gt;{|}~]"/>
+ </context>
+
+ <context attribute="Normal Text" lineEndContext="#stay" name="PropDefVal rules">
+ <DetectChar attribute="Comment" context="comment" char="!" />
+ <DetectChar attribute="String" context="string" char="&quot;" />
+ <DetectChar attribute="Word" context="word" char="'" />
+ <DetectChar attribute="Function" context="prop_func_def" char="[" beginRegion="reg_prop_def_func" />
+ <DetectChar attribute="Operator" context="#pop" char="," />
+ <RegExpr attribute="Number" context="#stay" String="\$[0-9a-fA-F]{1,4}" />
+ <RegExpr attribute="Number" context="#stay" String="[0-9]+" />
+ </context>
+
+
+ <context name="funcdef" attribute="Normal Text" lineEndContext="#stay" >
+ <IncludeRules context="Normal" />
+ <Detect2Chars attribute="Function" context="#pop" char="]" char1=";"
+ endRegion="reg_function"/>
+ </context>
+
+ <context name="ClassDecl" attribute="Normal Text" lineEndContext="ClassDecl_1">
+ <IncludeRules context="PropDefVal rules" />
+ </context>
+
+ <context name="ClassDecl_1" attribute="Normal Text" lineEndContext="#stay">
+ <RegExpr String="^[\t ]*has " attribute="Keyword" context="has_decl" insensitive="TRUE"/>
+ <keyword String="ClassDeclOps" attribute="Keyword" context="#stay" />
+ <RegExpr String="[a-zA-Z_]+[a-zA-Z_0-9]*" context = "prop_def"
+ attribute="Function"/>
+ <DetectChar char=";" context="#pop" attribute="Pragma" endRegion="reg_class"/>
+ </context>
+
+
+ <context attribute="Word" lineEndContext="#pop" name="word">
+ <DetectChar attribute="Word" context="#pop" char="'" />
+ </context>
+
+ <context attribute="String" lineEndContext="#stay" name="string">
+ <DetectChar attribute="String" context="#pop" char="&quot;" />
+ </context>
+
+ <context name="prop_func_def" attribute="Normal Text" lineEndContext="#stay">
+ <DetectChar context="#pop" char="]" attribute="Function"
+ endRegion="reg_prop_func"/>
+ <IncludeRules context="Normal" />
+ </context>
+
+ <context name="prop_def" attribute="Normal Text" lineEndContext="#stay" >
+ <DetectChar char="," context="#pop" attribute="Function"
+ endRegion="reg_prop"/>
+ <DetectChar char=";" context="#pop#pop#pop" attribute="Pragma" endRegion="reg_class"/>
+ <DetectChar char="[" context="prop_func_def" attribute="Function"
+ beginRegion="reg_prop_func"/>
+ <IncludeRules context="PropDefVal rules" />
+ </context>
+
+ <context name="has_decl" attribute="Normal Text" lineEndContext="#stay" >
+ <DetectChar char=";" context="#pop#pop#pop" attribute="Pragma" endRegion="reg_class"/>
+ <IncludeRules context="PropDefVal rules" />
+ </context>
+
+ <context attribute="Comment" lineEndContext="#pop" name="comment"/>
+
+ </contexts>
+
+ <itemDatas>
+ <itemData name="Normal Text" defStyleNum="dsNormal"/>
+ <itemData name="Keyword" defStyleNum="dsKeyword"/>
+ <itemData name="Actions" defStyleNum="dsDataType" color="#E06060" selColor="#400000" bold="0" italic="0"/>
+ <itemData name="Operator" defStyleNum="dsBaseN" color="#A01060" selColor="#400040" bold="0" italic="0"/>
+ <itemData name="Number" defStyleNum="dsDecVal" color="#5050C0" selColor="#000000" bold="0" italic="0"/>
+ <itemData name="OpCode" defStyleNum="dsDecVal" color="#3015F0" selColor="#000000" bold="1" italic="0"/>
+ <itemData name="Values" defStyleNum="dsBaseN" color="#7020F0" selColor="#400000" bold="1" italic="0" />
+ <itemData name="Pragma" defStyleNum="dsOthers" color="#109010" selColor="#400000" bold="1" italic="0" />
+ <itemData name="PredefFunction" defStyleNum="dsNormal" color="#000090" selColor="#ffffff" bold="1" italic="0"/>
+ <itemData name="Function" defStyleNum="dsNormal" color="#000090" selColor="#ffffff" bold="0" italic="0"/>
+ <itemData name="Comment" defStyleNum="dsComment" color="#707070" selColor="#ffffff" bold="0" italic="1"/>
+ <itemData name="String" defStyleNum="dsString" color="#007000" selColor="#007000" bold="0" italic="0"/>
+ <itemData name="Word" defStyleNum="dsString" color="#007000" selColor="#007000" bold="0" italic="1"/>
+ </itemDatas>
+</highlighting>
+<general>
+ <comments>
+ <comment name="singleLine" start="!" />
+ </comments>
+ <keywords casesensitive="1" />
+</general>
+</language>
+
diff --git a/tests/examplefiles/fucked_up.rb b/tests/examplefiles/fucked_up.rb
new file mode 100644
index 00000000..b1d0ee3b
--- /dev/null
+++ b/tests/examplefiles/fucked_up.rb
@@ -0,0 +1,77 @@
+# vim:ft=ruby
+
+events = Hash.new { |h, k| h[k] = [] }
+DATA.read.split(/\n\n\n\s*/).each do |event|
+ name = event[/^.*/].sub(/http:.*/, '')
+ event[/\n.*/m].scan(/^([A-Z]{2}\S*)\s*(\S*)\s*(\S*)(\s*\S*)/) do |kind, day, daytime, comment|
+ events[ [day, daytime] ] << [kind, name + comment]
+ end
+end
+
+conflicts = 0
+events.to_a.sort_by do |(day, daytime),|
+ [%w(Mo Di Mi Do Fr).index(day) || 0, daytime]
+end.each do |(day, daytime), names|
+ if names.size > 1
+ conflicts += 1
+ print '!!! '
+ end
+ print "#{day} #{daytime}: "
+ names.each { |kind, name| puts " #{kind} #{name}" }
+ puts
+end
+
+puts '%d conflicts' % conflicts
+puts '%d SWS' % (events.inject(0) { |sum, ((day, daytime),)| sum + (daytime[/\d+$/].to_i - daytime[/^\d+/].to_i) })
+
+string = % foo # strange. huh?
+print "Escape here: \n"
+print 'Dont escape here: \n'
+
+__END__
+Informatik und Informationsgesellschaft I: Digitale Medien (32 214)
+Computer lassen ihre eigentliche Bestimmung durch Multimedia und Vernetzung erkennen: Es sind digitale Medien, die alle bisherigen Massen- und Kommunikationsmedien simulieren, kopieren oder ersetzen können. Die kurze Geschichte elektronischer Medien vom Telegramm bis zum Fernsehen wird so zur Vorgeschichte des Computers als Medium. Der Prozess der Mediatisierung der Rechnernetze soll in Technik, Theorie und Praxis untersucht werden. Das PR soll die Techniken der ortsverteilten und zeitversetzten Lehre an Hand praktischer Übungen vorführen und untersuchen.
+VL Di 15-17 wöch. RUD 25, 3.101 J. Koubek
+VL Do 15-17 wöch. RUD 25, 3.101
+UE/PR Do 17-19 wöch. RUD 25, 3.101 J.-M. Loebel
+
+
+Methoden und Modelle des Systementwurfs (32 223)
+Gute Methoden zum Entwurf und zur Verifikation von Systemen sind ein Schlüssel für gute Software. Dieses Seminar betrachtet moderne Entwurfsmethoden.
+ VL Di 09-11 wöch. RUD 26, 0’313 W. Reisig
+ VL Do 09-11 wöch. RUD 26, 0’313
+ UE Di 11-13 wöch. RUD 26, 0’313
+ PR Di 13-15 wöch. RUD 26, 0’313 D. Weinberg
+
+
+Komplexitätstheorie (32 229)
+In dieser Vorlesung untersuchen wir eine Reihe von wichtigen algorithmischen Problemstellungen aus verschiedenen Bereichen der Informatik. Unser besonderes Interesse gilt dabei der Abschätzung der Rechenressourcen, die zu ihrer Lösung aufzubringen sind. Die Vorlesung bildet eine wichtige Grundlage für weiterführende Veranstaltungen in den Bereichen Algorithmen, Kryptologie, Algorithmisches Lernen und Algorithmisches Beweisen.
+ VL Di 09-11 wöch. RUD 26, 1’303 J. Köbler
+ VL Do 09-11 wöch. RUD 26, 1’305
+ UE Do 11-13 wöch. RUD 26, 1’305
+
+
+Zuverlässige Systeme (32 234)
+Mit zunehmender Verbreitung der Computertechnologie in immer mehr Bereichen des menschlichen Lebens wird die Zuverlässigkeit solcher Systeme zu einer immer zentraleren Frage.
+Der Halbkurs "Zuverlässige Systeme" konzentriert sich auf folgende Schwerpunkte: Zuverlässigkeit, Fehlertoleranz, Responsivität, Messungen, Anwendungen, Systemmodelle und Techniken, Ausfallverhalten, Fehlermodelle, Schedulingtechniken, Software/Hardware - responsives Systemdesign, Analyse und Synthese, Bewertung, Fallstudien in Forschung und Industrie.
+Der Halbkurs kann mit dem Halbkurs "Eigenschaften mobiler und eingebetteter Systeme" zu einem Projektkurs kombiniert werden. Ein gemeinsames Projekt begleitet beide Halbkurse.
+VL Di 09-11 wöch. RUD 26, 1’308 M. Malek
+VL Do 09-11 wöch. RUD 26, 1’308
+PR n.V.
+
+
+Stochastik für InformatikerInnen (32 239)
+Grundlagen der Wahrscheinlichkeitsrechnung, Diskrete und stetige Wahrscheinlichkeitsmodelle in der Informatik, Grenzwertsätze, Simulationsverfahren, Zufallszahlen, Statistische Schätz- und Testverfahren, Markoffsche Ketten, Simulated Annealing, Probabilistische Analyse von Algorithmen.
+VL Mo 09-11 wöch. RUD 25, 3.101 W. Kössler
+VL Mi 09-11 wöch. RUD 25, 3.101
+UE Mo 11-13 wöch. RUD 25, 3.101
+ UE Mi 11-13 wöch. RUD 25. 3.101
+
+
+Geschichte der Informatik – Ausgewählte Kapitel (32 243)
+VL Mi 13-15 wöch. RUD 25, 3.113 W. Coy
+
+
+Aktuelle Themen der Theoretischen Informatik (32 260)
+In diesem Seminar sollen wichtige aktuelle Veröffentlichungen aus der theoretischen Informatik gemeinsam erarbeitet werden. Genaueres wird erst kurz vor dem Seminar entschieden. Bei Interesse wenden Sie sich bitte möglichst frühzeitig an den Veranstalter.
+ SE Fr 09-11 wöch. RUD 26, 1’307 M. Grohe 
diff --git a/tests/examplefiles/genshi_example.html b/tests/examplefiles/genshi_example.html
new file mode 100644
index 00000000..57395317
--- /dev/null
+++ b/tests/examplefiles/genshi_example.html
@@ -0,0 +1,132 @@
+<!DOCTYPE html
+ PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml"
+ xmlns:py="http://genshi.edgewall.org/"
+ xmlns:xi="http://www.w3.org/2001/XInclude">
+ <xi:include href="layout.html" />
+ <xi:include href="macros.html" />
+ <head>
+ <title>$path</title>
+ </head>
+
+ <body>
+ <div id="ctxtnav" class="nav">
+ <ul>
+ <li class="first"><a href="${href.changeset(rev, created_path)}">Last Change</a></li>
+ <li class="last"><a href="${href.log(path, rev=rev)}">Revision Log</a></li>
+ </ul>
+ </div>
+
+ <div id="searchable">
+ <div id="content" class="browser">
+
+ <h1>${browser_path_links(path_links)}</h1>
+
+ <div id="jumprev">
+ <form action="" method="get">
+ <div>
+ <label for="rev">View revision:</label>
+ <input type="text" id="rev" name="rev" value="$rev" size="6" />
+ </div>
+ </form>
+ </div>
+
+ <py:if test="dir">
+ <table class="listing" id="dirlist">
+ <thead>
+ <tr>
+ <py:def function="sortable_th(order, desc, class_, title)">
+ <th class="$class_${order == class_ and (desc and ' desc' or ' asc') or ''}">
+ <a title="Sort by $class_${order == class_ and not desc and
+ ' (descending)' or ''}"
+ href="${href.browser(path, rev=rev, order=class_,
+ desc=(class_ == order and not desc and 1 or None))}">$title</a>
+ </th>
+ </py:def>
+ ${sortable_th(dir.order, dir.desc, 'name', 'Name')}
+ ${sortable_th(dir.order, dir.desc, 'size', 'Size')}
+ <th class="rev">Rev</th>
+ ${sortable_th(dir.order, dir.desc, 'date', 'Age')}
+ <th class="change">Last Change</th>
+ </tr>
+ </thead>
+ <tbody>
+ <py:if test="chrome.links.up">
+ <tr class="even">
+ <td class="name" colspan="5">
+ <a class="parent" title="Parent Directory" href="${chrome.links.up[0].href}">../</a>
+ </td>
+ </tr>
+ </py:if>
+ <py:for each="idx, entry in enumerate(dir.entries)">
+ <py:with vars="change = dir.changes[entry.rev]">
+ <tr class="${idx % 2 and 'even' or 'odd'}">
+ <td class="name">
+ <a class="${entry.kind}"
+ title="${entry.kind == 'dir' and 'Browse Directory' or 'View File'}"
+ href="${href.browser(entry.path, rev=rev)}">$entry.name</a>
+ </td>
+ <td class="size">${sizeinfo(entry.size)}</td>
+ <td class="rev">
+ <a title="View Revision Log"
+ href="${href.log(entry.path, rev=rev)}">$entry.rev</a>
+ </td>
+ <td class="age">${dateinfo(change.date_seconds)}</td>
+ <!--! FIXME: use 'date' and therefore replace 'date' by 'date_seconds'
+ in trac.versioncontrol.web_ui.util.get_changes -->
+ <td class="change">
+ <span class="author">$change.author:</span>
+ <span class="change">$change.message</span>
+ </td>
+ </tr>
+ </py:with>
+ </py:for>
+ </tbody>
+ </table>
+ </py:if>
+
+ <table py:if="props or file" id="info" summary="Revision info">
+ <tr py:if="file">
+ <th scope="col">
+ Revision <a href="${href.changeset(rev)}">$rev</a>, ${sizeinfo(file.size)}
+ (checked in by $file.author, ${dateinfo(file.date)} ago)
+ </th>
+ </tr>
+ <tr py:if="file">
+ <td class="message">$file.message</td>
+ </tr>
+ <tr py:if="props">
+ <td colspan="2">
+ <ul class="props">
+ <li py:for="prop in props">
+ Property <strong>$prop.name</strong> set to <em><code>$prop.value</code></em>
+ </li>
+ </ul>
+ </td>
+ </tr>
+ </table>
+
+ ${preview_file(file.preview)} <!--! a <div id="preview"> containing the rendered file preview -->
+
+ <div id="help">
+ <strong>Note:</strong> See <a href="${href.wiki('TracBrowser')}">TracBrowser</a>
+ for help on using the browser.
+ </div>
+
+ <div id="anydiff">
+ <form action="${href.diff_form()}" method="get">
+ <div class="buttons">
+ <input type="hidden" name="new_path" value="$path" />
+ <input type="hidden" name="old_path" value="$path" />
+ <input type="hidden" name="new_rev" value="$rev" />
+ <input type="hidden" name="old_rev" value="$rev" />
+ <input type="submit" value="View changes..." title="Select paths and revs for Diff" />
+ </div>
+ </form>
+ </div>
+
+ </div>
+ </div>
+ </body>
+</html>
diff --git a/tests/examplefiles/multiline_regexes.rb b/tests/examplefiles/multiline_regexes.rb
new file mode 100644
index 00000000..1b1e7612
--- /dev/null
+++ b/tests/examplefiles/multiline_regexes.rb
@@ -0,0 +1,38 @@
+/
+this is a
+multiline
+regex
+/
+
+this /is a
+multiline regex too/
+
+foo = /is also
+one/
+
+also /4
+is one/
+
+this(/
+too
+/)
+
+# this not
+2 /4
+asfsadf/
+
+# this is also not one
+0x4d /25
+foo/
+
+42 and /this
+is also a multiline
+regex/
+
+
+# And here some special string cases
+foo = % blah # comment here to ensure whitespace
+foo(% blah )
+foo << % blah # stupid but has to work
+foo = % blah + % blub # wicked
+foo = %q wicked # works too
diff --git a/tests/examplefiles/numbers.c b/tests/examplefiles/numbers.c
new file mode 100644
index 00000000..80662ead
--- /dev/null
+++ b/tests/examplefiles/numbers.c
@@ -0,0 +1,12 @@
+/*
+ * Some Number Test
+ */
+
+int i = 24241424;
+float f1 = 342423423.24234;
+float f2 = 25235235.;
+float f3 = .234234;
+float f4 = 234243e+34343;
+float f5 = 24234e-234;
+int o = 0234;
+int h = 0x2342;
diff --git a/tests/examplefiles/perl5db.pl b/tests/examplefiles/perl5db.pl
new file mode 100644
index 00000000..b4edc838
--- /dev/null
+++ b/tests/examplefiles/perl5db.pl
@@ -0,0 +1,9428 @@
+
+=head1 NAME
+
+perl5db.pl - the perl debugger
+
+=head1 SYNOPSIS
+
+ perl -d your_Perl_script
+
+=head1 DESCRIPTION
+
+C<perl5db.pl> is the perl debugger. It is loaded automatically by Perl when
+you invoke a script with C<perl -d>. This documentation tries to outline the
+structure and services provided by C<perl5db.pl>, and to describe how you
+can use them.
+
+=head1 GENERAL NOTES
+
+The debugger can look pretty forbidding to many Perl programmers. There are
+a number of reasons for this, many stemming out of the debugger's history.
+
+When the debugger was first written, Perl didn't have a lot of its nicer
+features - no references, no lexical variables, no closures, no object-oriented
+programming. So a lot of the things one would normally have done using such
+features was done using global variables, globs and the C<local()> operator
+in creative ways.
+
+Some of these have survived into the current debugger; a few of the more
+interesting and still-useful idioms are noted in this section, along with notes
+on the comments themselves.
+
+=head2 Why not use more lexicals?
+
+Experienced Perl programmers will note that the debugger code tends to use
+mostly package globals rather than lexically-scoped variables. This is done
+to allow a significant amount of control of the debugger from outside the
+debugger itself.
+
+Unfortunately, though the variables are accessible, they're not well
+documented, so it's generally been a decision that hasn't made a lot of
+difference to most users. Where appropriate, comments have been added to
+make variables more accessible and usable, with the understanding that these
+I<are> debugger internals, and are therefore subject to change. Future
+development should probably attempt to replace the globals with a well-defined
+API, but for now, the variables are what we've got.
+
+=head2 Automated variable stacking via C<local()>
+
+As you may recall from reading C<perlfunc>, the C<local()> operator makes a
+temporary copy of a variable in the current scope. When the scope ends, the
+old copy is restored. This is often used in the debugger to handle the
+automatic stacking of variables during recursive calls:
+
+ sub foo {
+ local $some_global++;
+
+ # Do some stuff, then ...
+ return;
+ }
+
+What happens is that on entry to the subroutine, C<$some_global> is localized,
+then altered. When the subroutine returns, Perl automatically undoes the
+localization, restoring the previous value. Voila, automatic stack management.
+
+The debugger uses this trick a I<lot>. Of particular note is C<DB::eval>,
+which lets the debugger get control inside of C<eval>'ed code. The debugger
+localizes a saved copy of C<$@> inside the subroutine, which allows it to
+keep C<$@> safe until it C<DB::eval> returns, at which point the previous
+value of C<$@> is restored. This makes it simple (well, I<simpler>) to keep
+track of C<$@> inside C<eval>s which C<eval> other C<eval's>.
+
+In any case, watch for this pattern. It occurs fairly often.
+
+=head2 The C<^> trick
+
+This is used to cleverly reverse the sense of a logical test depending on
+the value of an auxiliary variable. For instance, the debugger's C<S>
+(search for subroutines by pattern) allows you to negate the pattern
+like this:
+
+ # Find all non-'foo' subs:
+ S !/foo/
+
+Boolean algebra states that the truth table for XOR looks like this:
+
+=over 4
+
+=item * 0 ^ 0 = 0
+
+(! not present and no match) --> false, don't print
+
+=item * 0 ^ 1 = 1
+
+(! not present and matches) --> true, print
+
+=item * 1 ^ 0 = 1
+
+(! present and no match) --> true, print
+
+=item * 1 ^ 1 = 0
+
+(! present and matches) --> false, don't print
+
+=back
+
+As you can see, the first pair applies when C<!> isn't supplied, and
+the second pair applies when it is. The XOR simply allows us to
+compact a more complicated if-then-elseif-else into a more elegant
+(but perhaps overly clever) single test. After all, it needed this
+explanation...
+
+=head2 FLAGS, FLAGS, FLAGS
+
+There is a certain C programming legacy in the debugger. Some variables,
+such as C<$single>, C<$trace>, and C<$frame>, have I<magical> values composed
+of 1, 2, 4, etc. (powers of 2) OR'ed together. This allows several pieces
+of state to be stored independently in a single scalar.
+
+A test like
+
+ if ($scalar & 4) ...
+
+is checking to see if the appropriate bit is on. Since each bit can be
+"addressed" independently in this way, C<$scalar> is acting sort of like
+an array of bits. Obviously, since the contents of C<$scalar> are just a
+bit-pattern, we can save and restore it easily (it will just look like
+a number).
+
+The problem, is of course, that this tends to leave magic numbers scattered
+all over your program whenever a bit is set, cleared, or checked. So why do
+it?
+
+=over 4
+
+=item *
+
+First, doing an arithmetical or bitwise operation on a scalar is
+just about the fastest thing you can do in Perl: C<use constant> actually
+creates a subroutine call, and array and hash lookups are much slower. Is
+this over-optimization at the expense of readability? Possibly, but the
+debugger accesses these variables a I<lot>. Any rewrite of the code will
+probably have to benchmark alternate implementations and see which is the
+best balance of readability and speed, and then document how it actually
+works.
+
+=item *
+
+Second, it's very easy to serialize a scalar number. This is done in
+the restart code; the debugger state variables are saved in C<%ENV> and then
+restored when the debugger is restarted. Having them be just numbers makes
+this trivial.
+
+=item *
+
+Third, some of these variables are being shared with the Perl core
+smack in the middle of the interpreter's execution loop. It's much faster for
+a C program (like the interpreter) to check a bit in a scalar than to access
+several different variables (or a Perl array).
+
+=back
+
+=head2 What are those C<XXX> comments for?
+
+Any comment containing C<XXX> means that the comment is either somewhat
+speculative - it's not exactly clear what a given variable or chunk of
+code is doing, or that it is incomplete - the basics may be clear, but the
+subtleties are not completely documented.
+
+Send in a patch if you can clear up, fill out, or clarify an C<XXX>.
+
+=head1 DATA STRUCTURES MAINTAINED BY CORE
+
+There are a number of special data structures provided to the debugger by
+the Perl interpreter.
+
+The array C<@{$main::{'_<'.$filename}}> (aliased locally to C<@dbline> via glob
+assignment) contains the text from C<$filename>, with each element
+corresponding to a single line of C<$filename>.
+
+The hash C<%{'_<'.$filename}> (aliased locally to C<%dbline> via glob
+assignment) contains breakpoints and actions. The keys are line numbers;
+you can set individual values, but not the whole hash. The Perl interpreter
+uses this hash to determine where breakpoints have been set. Any true value is
+considered to be a breakpoint; C<perl5db.pl> uses C<$break_condition\0$action>.
+Values are magical in numeric context: 1 if the line is breakable, 0 if not.
+
+The scalar C<${"_<$filename"}> simply contains the string C<_<$filename>.
+This is also the case for evaluated strings that contain subroutines, or
+which are currently being executed. The $filename for C<eval>ed strings looks
+like C<(eval 34)> or C<(re_eval 19)>.
+
+=head1 DEBUGGER STARTUP
+
+When C<perl5db.pl> starts, it reads an rcfile (C<perl5db.ini> for
+non-interactive sessions, C<.perldb> for interactive ones) that can set a number
+of options. In addition, this file may define a subroutine C<&afterinit>
+that will be executed (in the debugger's context) after the debugger has
+initialized itself.
+
+Next, it checks the C<PERLDB_OPTS> environment variable and treats its
+contents as the argument of a C<o> command in the debugger.
+
+=head2 STARTUP-ONLY OPTIONS
+
+The following options can only be specified at startup.
+To set them in your rcfile, add a call to
+C<&parse_options("optionName=new_value")>.
+
+=over 4
+
+=item * TTY
+
+the TTY to use for debugging i/o.
+
+=item * noTTY
+
+if set, goes in NonStop mode. On interrupt, if TTY is not set,
+uses the value of noTTY or F<$HOME/.perldbtty$$> to find TTY using
+Term::Rendezvous. Current variant is to have the name of TTY in this
+file.
+
+=item * ReadLine
+
+If false, a dummy ReadLine is used, so you can debug
+ReadLine applications.
+
+=item * NonStop
+
+if true, no i/o is performed until interrupt.
+
+=item * LineInfo
+
+file or pipe to print line number info to. If it is a
+pipe, a short "emacs like" message is used.
+
+=item * RemotePort
+
+host:port to connect to on remote host for remote debugging.
+
+=back
+
+=head3 SAMPLE RCFILE
+
+ &parse_options("NonStop=1 LineInfo=db.out");
+ sub afterinit { $trace = 1; }
+
+The script will run without human intervention, putting trace
+information into C<db.out>. (If you interrupt it, you had better
+reset C<LineInfo> to something I<interactive>!)
+
+=head1 INTERNALS DESCRIPTION
+
+=head2 DEBUGGER INTERFACE VARIABLES
+
+Perl supplies the values for C<%sub>. It effectively inserts
+a C<&DB::DB();> in front of each place that can have a
+breakpoint. At each subroutine call, it calls C<&DB::sub> with
+C<$DB::sub> set to the called subroutine. It also inserts a C<BEGIN
+{require 'perl5db.pl'}> before the first line.
+
+After each C<require>d file is compiled, but before it is executed, a
+call to C<&DB::postponed($main::{'_<'.$filename})> is done. C<$filename>
+is the expanded name of the C<require>d file (as found via C<%INC>).
+
+=head3 IMPORTANT INTERNAL VARIABLES
+
+=head4 C<$CreateTTY>
+
+Used to control when the debugger will attempt to acquire another TTY to be
+used for input.
+
+=over
+
+=item * 1 - on C<fork()>
+
+=item * 2 - debugger is started inside debugger
+
+=item * 4 - on startup
+
+=back
+
+=head4 C<$doret>
+
+The value -2 indicates that no return value should be printed.
+Any other positive value causes C<DB::sub> to print return values.
+
+=head4 C<$evalarg>
+
+The item to be eval'ed by C<DB::eval>. Used to prevent messing with the current
+contents of C<@_> when C<DB::eval> is called.
+
+=head4 C<$frame>
+
+Determines what messages (if any) will get printed when a subroutine (or eval)
+is entered or exited.
+
+=over 4
+
+=item * 0 - No enter/exit messages
+
+=item * 1 - Print I<entering> messages on subroutine entry
+
+=item * 2 - Adds exit messages on subroutine exit. If no other flag is on, acts like 1+2.
+
+=item * 4 - Extended messages: C<< <in|out> I<context>=I<fully-qualified sub name> from I<file>:I<line> >>. If no other flag is on, acts like 1+4.
+
+=item * 8 - Adds parameter information to messages, and overloaded stringify and tied FETCH is enabled on the printed arguments. Ignored if C<4> is not on.
+
+=item * 16 - Adds C<I<context> return from I<subname>: I<value>> messages on subroutine/eval exit. Ignored if C<4> is is not on.
+
+=back
+
+To get everything, use C<$frame=30> (or C<o f=30> as a debugger command).
+The debugger internally juggles the value of C<$frame> during execution to
+protect external modules that the debugger uses from getting traced.
+
+=head4 C<$level>
+
+Tracks current debugger nesting level. Used to figure out how many
+C<E<lt>E<gt>> pairs to surround the line number with when the debugger
+outputs a prompt. Also used to help determine if the program has finished
+during command parsing.
+
+=head4 C<$onetimeDump>
+
+Controls what (if anything) C<DB::eval()> will print after evaluating an
+expression.
+
+=over 4
+
+=item * C<undef> - don't print anything
+
+=item * C<dump> - use C<dumpvar.pl> to display the value returned
+
+=item * C<methods> - print the methods callable on the first item returned
+
+=back
+
+=head4 C<$onetimeDumpDepth>
+
+Controls how far down C<dumpvar.pl> will go before printing C<...> while
+dumping a structure. Numeric. If C<undef>, print all levels.
+
+=head4 C<$signal>
+
+Used to track whether or not an C<INT> signal has been detected. C<DB::DB()>,
+which is called before every statement, checks this and puts the user into
+command mode if it finds C<$signal> set to a true value.
+
+=head4 C<$single>
+
+Controls behavior during single-stepping. Stacked in C<@stack> on entry to
+each subroutine; popped again at the end of each subroutine.
+
+=over 4
+
+=item * 0 - run continuously.
+
+=item * 1 - single-step, go into subs. The C<s> command.
+
+=item * 2 - single-step, don't go into subs. The C<n> command.
+
+=item * 4 - print current sub depth (turned on to force this when C<too much
+recursion> occurs.
+
+=back
+
+=head4 C<$trace>
+
+Controls the output of trace information.
+
+=over 4
+
+=item * 1 - The C<t> command was entered to turn on tracing (every line executed is printed)
+
+=item * 2 - watch expressions are active
+
+=item * 4 - user defined a C<watchfunction()> in C<afterinit()>
+
+=back
+
+=head4 C<$slave_editor>
+
+1 if C<LINEINFO> was directed to a pipe; 0 otherwise.
+
+=head4 C<@cmdfhs>
+
+Stack of filehandles that C<DB::readline()> will read commands from.
+Manipulated by the debugger's C<source> command and C<DB::readline()> itself.
+
+=head4 C<@dbline>
+
+Local alias to the magical line array, C<@{$main::{'_<'.$filename}}> ,
+supplied by the Perl interpreter to the debugger. Contains the source.
+
+=head4 C<@old_watch>
+
+Previous values of watch expressions. First set when the expression is
+entered; reset whenever the watch expression changes.
+
+=head4 C<@saved>
+
+Saves important globals (C<$@>, C<$!>, C<$^E>, C<$,>, C<$/>, C<$\>, C<$^W>)
+so that the debugger can substitute safe values while it's running, and
+restore them when it returns control.
+
+=head4 C<@stack>
+
+Saves the current value of C<$single> on entry to a subroutine.
+Manipulated by the C<c> command to turn off tracing in all subs above the
+current one.
+
+=head4 C<@to_watch>
+
+The 'watch' expressions: to be evaluated before each line is executed.
+
+=head4 C<@typeahead>
+
+The typeahead buffer, used by C<DB::readline>.
+
+=head4 C<%alias>
+
+Command aliases. Stored as character strings to be substituted for a command
+entered.
+
+=head4 C<%break_on_load>
+
+Keys are file names, values are 1 (break when this file is loaded) or undef
+(don't break when it is loaded).
+
+=head4 C<%dbline>
+
+Keys are line numbers, values are C<condition\0action>. If used in numeric
+context, values are 0 if not breakable, 1 if breakable, no matter what is
+in the actual hash entry.
+
+=head4 C<%had_breakpoints>
+
+Keys are file names; values are bitfields:
+
+=over 4
+
+=item * 1 - file has a breakpoint in it.
+
+=item * 2 - file has an action in it.
+
+=back
+
+A zero or undefined value means this file has neither.
+
+=head4 C<%option>
+
+Stores the debugger options. These are character string values.
+
+=head4 C<%postponed>
+
+Saves breakpoints for code that hasn't been compiled yet.
+Keys are subroutine names, values are:
+
+=over 4
+
+=item * C<compile> - break when this sub is compiled
+
+=item * C<< break +0 if <condition> >> - break (conditionally) at the start of this routine. The condition will be '1' if no condition was specified.
+
+=back
+
+=head4 C<%postponed_file>
+
+This hash keeps track of breakpoints that need to be set for files that have
+not yet been compiled. Keys are filenames; values are references to hashes.
+Each of these hashes is keyed by line number, and its values are breakpoint
+definitions (C<condition\0action>).
+
+=head1 DEBUGGER INITIALIZATION
+
+The debugger's initialization actually jumps all over the place inside this
+package. This is because there are several BEGIN blocks (which of course
+execute immediately) spread through the code. Why is that?
+
+The debugger needs to be able to change some things and set some things up
+before the debugger code is compiled; most notably, the C<$deep> variable that
+C<DB::sub> uses to tell when a program has recursed deeply. In addition, the
+debugger has to turn off warnings while the debugger code is compiled, but then
+restore them to their original setting before the program being debugged begins
+executing.
+
+The first C<BEGIN> block simply turns off warnings by saving the current
+setting of C<$^W> and then setting it to zero. The second one initializes
+the debugger variables that are needed before the debugger begins executing.
+The third one puts C<$^X> back to its former value.
+
+We'll detail the second C<BEGIN> block later; just remember that if you need
+to initialize something before the debugger starts really executing, that's
+where it has to go.
+
+=cut
+
+package DB;
+
+use IO::Handle;
+
+# Debugger for Perl 5.00x; perl5db.pl patch level:
+$VERSION = 1.28;
+
+$header = "perl5db.pl version $VERSION";
+
+=head1 DEBUGGER ROUTINES
+
+=head2 C<DB::eval()>
+
+This function replaces straight C<eval()> inside the debugger; it simplifies
+the process of evaluating code in the user's context.
+
+The code to be evaluated is passed via the package global variable
+C<$DB::evalarg>; this is done to avoid fiddling with the contents of C<@_>.
+
+Before we do the C<eval()>, we preserve the current settings of C<$trace>,
+C<$single>, C<$^D> and C<$usercontext>. The latter contains the
+preserved values of C<$@>, C<$!>, C<$^E>, C<$,>, C<$/>, C<$\>, C<$^W> and the
+user's current package, grabbed when C<DB::DB> got control. This causes the
+proper context to be used when the eval is actually done. Afterward, we
+restore C<$trace>, C<$single>, and C<$^D>.
+
+Next we need to handle C<$@> without getting confused. We save C<$@> in a
+local lexical, localize C<$saved[0]> (which is where C<save()> will put
+C<$@>), and then call C<save()> to capture C<$@>, C<$!>, C<$^E>, C<$,>,
+C<$/>, C<$\>, and C<$^W>) and set C<$,>, C<$/>, C<$\>, and C<$^W> to values
+considered sane by the debugger. If there was an C<eval()> error, we print
+it on the debugger's output. If C<$onetimedump> is defined, we call
+C<dumpit> if it's set to 'dump', or C<methods> if it's set to
+'methods'. Setting it to something else causes the debugger to do the eval
+but not print the result - handy if you want to do something else with it
+(the "watch expressions" code does this to get the value of the watch
+expression but not show it unless it matters).
+
+In any case, we then return the list of output from C<eval> to the caller,
+and unwinding restores the former version of C<$@> in C<@saved> as well
+(the localization of C<$saved[0]> goes away at the end of this scope).
+
+=head3 Parameters and variables influencing execution of DB::eval()
+
+C<DB::eval> isn't parameterized in the standard way; this is to keep the
+debugger's calls to C<DB::eval()> from mucking with C<@_>, among other things.
+The variables listed below influence C<DB::eval()>'s execution directly.
+
+=over 4
+
+=item C<$evalarg> - the thing to actually be eval'ed
+
+=item C<$trace> - Current state of execution tracing
+
+=item C<$single> - Current state of single-stepping
+
+=item C<$onetimeDump> - what is to be displayed after the evaluation
+
+=item C<$onetimeDumpDepth> - how deep C<dumpit()> should go when dumping results
+
+=back
+
+The following variables are altered by C<DB::eval()> during its execution. They
+are "stacked" via C<local()>, enabling recursive calls to C<DB::eval()>.
+
+=over 4
+
+=item C<@res> - used to capture output from actual C<eval>.
+
+=item C<$otrace> - saved value of C<$trace>.
+
+=item C<$osingle> - saved value of C<$single>.
+
+=item C<$od> - saved value of C<$^D>.
+
+=item C<$saved[0]> - saved value of C<$@>.
+
+=item $\ - for output of C<$@> if there is an evaluation error.
+
+=back
+
+=head3 The problem of lexicals
+
+The context of C<DB::eval()> presents us with some problems. Obviously,
+we want to be 'sandboxed' away from the debugger's internals when we do
+the eval, but we need some way to control how punctuation variables and
+debugger globals are used.
+
+We can't use local, because the code inside C<DB::eval> can see localized
+variables; and we can't use C<my> either for the same reason. The code
+in this routine compromises and uses C<my>.
+
+After this routine is over, we don't have user code executing in the debugger's
+context, so we can use C<my> freely.
+
+=cut
+
+############################################## Begin lexical danger zone
+
+# 'my' variables used here could leak into (that is, be visible in)
+# the context that the code being evaluated is executing in. This means that
+# the code could modify the debugger's variables.
+#
+# Fiddling with the debugger's context could be Bad. We insulate things as
+# much as we can.
+
+sub eval {
+
+ # 'my' would make it visible from user code
+ # but so does local! --tchrist
+ # Remember: this localizes @DB::res, not @main::res.
+ local @res;
+ {
+
+ # Try to keep the user code from messing with us. Save these so that
+ # even if the eval'ed code changes them, we can put them back again.
+ # Needed because the user could refer directly to the debugger's
+ # package globals (and any 'my' variables in this containing scope)
+ # inside the eval(), and we want to try to stay safe.
+ local $otrace = $trace;
+ local $osingle = $single;
+ local $od = $^D;
+
+ # Untaint the incoming eval() argument.
+ { ($evalarg) = $evalarg =~ /(.*)/s; }
+
+ # $usercontext built in DB::DB near the comment
+ # "set up the context for DB::eval ..."
+ # Evaluate and save any results.
+ @res = eval "$usercontext $evalarg;\n"; # '\n' for nice recursive debug
+
+ # Restore those old values.
+ $trace = $otrace;
+ $single = $osingle;
+ $^D = $od;
+ }
+
+ # Save the current value of $@, and preserve it in the debugger's copy
+ # of the saved precious globals.
+ my $at = $@;
+
+ # Since we're only saving $@, we only have to localize the array element
+ # that it will be stored in.
+ local $saved[0]; # Preserve the old value of $@
+ eval { &DB::save };
+
+ # Now see whether we need to report an error back to the user.
+ if ($at) {
+ local $\ = '';
+ print $OUT $at;
+ }
+
+ # Display as required by the caller. $onetimeDump and $onetimedumpDepth
+ # are package globals.
+ elsif ($onetimeDump) {
+ if ( $onetimeDump eq 'dump' ) {
+ local $option{dumpDepth} = $onetimedumpDepth
+ if defined $onetimedumpDepth;
+ dumpit( $OUT, \@res );
+ }
+ elsif ( $onetimeDump eq 'methods' ) {
+ methods( $res[0] );
+ }
+ } ## end elsif ($onetimeDump)
+ @res;
+} ## end sub eval
+
+############################################## End lexical danger zone
+
+# After this point it is safe to introduce lexicals.
+# The code being debugged will be executing in its own context, and
+# can't see the inside of the debugger.
+#
+# However, one should not overdo it: leave as much control from outside as
+# possible. If you make something a lexical, it's not going to be addressable
+# from outside the debugger even if you know its name.
+
+# This file is automatically included if you do perl -d.
+# It's probably not useful to include this yourself.
+#
+# Before venturing further into these twisty passages, it is
+# wise to read the perldebguts man page or risk the ire of dragons.
+#
+# (It should be noted that perldebguts will tell you a lot about
+# the underlying mechanics of how the debugger interfaces into the
+# Perl interpreter, but not a lot about the debugger itself. The new
+# comments in this code try to address this problem.)
+
+# Note that no subroutine call is possible until &DB::sub is defined
+# (for subroutines defined outside of the package DB). In fact the same is
+# true if $deep is not defined.
+
+# Enhanced by ilya@math.ohio-state.edu (Ilya Zakharevich)
+
+# modified Perl debugger, to be run from Emacs in perldb-mode
+# Ray Lischner (uunet!mntgfx!lisch) as of 5 Nov 1990
+# Johan Vromans -- upgrade to 4.0 pl 10
+# Ilya Zakharevich -- patches after 5.001 (and some before ;-)
+
+# (We have made efforts to clarify the comments in the change log
+# in other places; some of them may seem somewhat obscure as they
+# were originally written, and explaining them away from the code
+# in question seems conterproductive.. -JM)
+
+########################################################################
+# Changes: 0.94
+# + A lot of things changed after 0.94. First of all, core now informs
+# debugger about entry into XSUBs, overloaded operators, tied operations,
+# BEGIN and END. Handy with `O f=2'.
+# + This can make debugger a little bit too verbose, please be patient
+# and report your problems promptly.
+# + Now the option frame has 3 values: 0,1,2. XXX Document!
+# + Note that if DESTROY returns a reference to the object (or object),
+# the deletion of data may be postponed until the next function call,
+# due to the need to examine the return value.
+#
+# Changes: 0.95
+# + `v' command shows versions.
+#
+# Changes: 0.96
+# + `v' command shows version of readline.
+# primitive completion works (dynamic variables, subs for `b' and `l',
+# options). Can `p %var'
+# + Better help (`h <' now works). New commands <<, >>, {, {{.
+# {dump|print}_trace() coded (to be able to do it from <<cmd).
+# + `c sub' documented.
+# + At last enough magic combined to stop after the end of debuggee.
+# + !! should work now (thanks to Emacs bracket matching an extra
+# `]' in a regexp is caught).
+# + `L', `D' and `A' span files now (as documented).
+# + Breakpoints in `require'd code are possible (used in `R').
+# + Some additional words on internal work of debugger.
+# + `b load filename' implemented.
+# + `b postpone subr' implemented.
+# + now only `q' exits debugger (overwritable on $inhibit_exit).
+# + When restarting debugger breakpoints/actions persist.
+# + Buglet: When restarting debugger only one breakpoint/action per
+# autoloaded function persists.
+#
+# Changes: 0.97: NonStop will not stop in at_exit().
+# + Option AutoTrace implemented.
+# + Trace printed differently if frames are printed too.
+# + new `inhibitExit' option.
+# + printing of a very long statement interruptible.
+# Changes: 0.98: New command `m' for printing possible methods
+# + 'l -' is a synonym for `-'.
+# + Cosmetic bugs in printing stack trace.
+# + `frame' & 8 to print "expanded args" in stack trace.
+# + Can list/break in imported subs.
+# + new `maxTraceLen' option.
+# + frame & 4 and frame & 8 granted.
+# + new command `m'
+# + nonstoppable lines do not have `:' near the line number.
+# + `b compile subname' implemented.
+# + Will not use $` any more.
+# + `-' behaves sane now.
+# Changes: 0.99: Completion for `f', `m'.
+# + `m' will remove duplicate names instead of duplicate functions.
+# + `b load' strips trailing whitespace.
+# completion ignores leading `|'; takes into account current package
+# when completing a subroutine name (same for `l').
+# Changes: 1.07: Many fixed by tchrist 13-March-2000
+# BUG FIXES:
+# + Added bare minimal security checks on perldb rc files, plus
+# comments on what else is needed.
+# + Fixed the ornaments that made "|h" completely unusable.
+# They are not used in print_help if they will hurt. Strip pod
+# if we're paging to less.
+# + Fixed mis-formatting of help messages caused by ornaments
+# to restore Larry's original formatting.
+# + Fixed many other formatting errors. The code is still suboptimal,
+# and needs a lot of work at restructuring. It's also misindented
+# in many places.
+# + Fixed bug where trying to look at an option like your pager
+# shows "1".
+# + Fixed some $? processing. Note: if you use csh or tcsh, you will
+# lose. You should consider shell escapes not using their shell,
+# or else not caring about detailed status. This should really be
+# unified into one place, too.
+# + Fixed bug where invisible trailing whitespace on commands hoses you,
+# tricking Perl into thinking you weren't calling a debugger command!
+# + Fixed bug where leading whitespace on commands hoses you. (One
+# suggests a leading semicolon or any other irrelevant non-whitespace
+# to indicate literal Perl code.)
+# + Fixed bugs that ate warnings due to wrong selected handle.
+# + Fixed a precedence bug on signal stuff.
+# + Fixed some unseemly wording.
+# + Fixed bug in help command trying to call perl method code.
+# + Fixed to call dumpvar from exception handler. SIGPIPE killed us.
+# ENHANCEMENTS:
+# + Added some comments. This code is still nasty spaghetti.
+# + Added message if you clear your pre/post command stacks which was
+# very easy to do if you just typed a bare >, <, or {. (A command
+# without an argument should *never* be a destructive action; this
+# API is fundamentally screwed up; likewise option setting, which
+# is equally buggered.)
+# + Added command stack dump on argument of "?" for >, <, or {.
+# + Added a semi-built-in doc viewer command that calls man with the
+# proper %Config::Config path (and thus gets caching, man -k, etc),
+# or else perldoc on obstreperous platforms.
+# + Added to and rearranged the help information.
+# + Detected apparent misuse of { ... } to declare a block; this used
+# to work but now is a command, and mysteriously gave no complaint.
+#
+# Changes: 1.08: Apr 25, 2001 Jon Eveland <jweveland@yahoo.com>
+# BUG FIX:
+# + This patch to perl5db.pl cleans up formatting issues on the help
+# summary (h h) screen in the debugger. Mostly columnar alignment
+# issues, plus converted the printed text to use all spaces, since
+# tabs don't seem to help much here.
+#
+# Changes: 1.09: May 19, 2001 Ilya Zakharevich <ilya@math.ohio-state.edu>
+# Minor bugs corrected;
+# + Support for auto-creation of new TTY window on startup, either
+# unconditionally, or if started as a kid of another debugger session;
+# + New `O'ption CreateTTY
+# I<CreateTTY> bits control attempts to create a new TTY on events:
+# 1: on fork()
+# 2: debugger is started inside debugger
+# 4: on startup
+# + Code to auto-create a new TTY window on OS/2 (currently one
+# extra window per session - need named pipes to have more...);
+# + Simplified interface for custom createTTY functions (with a backward
+# compatibility hack); now returns the TTY name to use; return of ''
+# means that the function reset the I/O handles itself;
+# + Better message on the semantic of custom createTTY function;
+# + Convert the existing code to create a TTY into a custom createTTY
+# function;
+# + Consistent support for TTY names of the form "TTYin,TTYout";
+# + Switch line-tracing output too to the created TTY window;
+# + make `b fork' DWIM with CORE::GLOBAL::fork;
+# + High-level debugger API cmd_*():
+# cmd_b_load($filenamepart) # b load filenamepart
+# cmd_b_line($lineno [, $cond]) # b lineno [cond]
+# cmd_b_sub($sub [, $cond]) # b sub [cond]
+# cmd_stop() # Control-C
+# cmd_d($lineno) # d lineno (B)
+# The cmd_*() API returns FALSE on failure; in this case it outputs
+# the error message to the debugging output.
+# + Low-level debugger API
+# break_on_load($filename) # b load filename
+# @files = report_break_on_load() # List files with load-breakpoints
+# breakable_line_in_filename($name, $from [, $to])
+# # First breakable line in the
+# # range $from .. $to. $to defaults
+# # to $from, and may be less than
+# # $to
+# breakable_line($from [, $to]) # Same for the current file
+# break_on_filename_line($name, $lineno [, $cond])
+# # Set breakpoint,$cond defaults to
+# # 1
+# break_on_filename_line_range($name, $from, $to [, $cond])
+# # As above, on the first
+# # breakable line in range
+# break_on_line($lineno [, $cond]) # As above, in the current file
+# break_subroutine($sub [, $cond]) # break on the first breakable line
+# ($name, $from, $to) = subroutine_filename_lines($sub)
+# # The range of lines of the text
+# The low-level API returns TRUE on success, and die()s on failure.
+#
+# Changes: 1.10: May 23, 2001 Daniel Lewart <d-lewart@uiuc.edu>
+# BUG FIXES:
+# + Fixed warnings generated by "perl -dWe 42"
+# + Corrected spelling errors
+# + Squeezed Help (h) output into 80 columns
+#
+# Changes: 1.11: May 24, 2001 David Dyck <dcd@tc.fluke.com>
+# + Made "x @INC" work like it used to
+#
+# Changes: 1.12: May 24, 2001 Daniel Lewart <d-lewart@uiuc.edu>
+# + Fixed warnings generated by "O" (Show debugger options)
+# + Fixed warnings generated by "p 42" (Print expression)
+# Changes: 1.13: Jun 19, 2001 Scott.L.Miller@compaq.com
+# + Added windowSize option
+# Changes: 1.14: Oct 9, 2001 multiple
+# + Clean up after itself on VMS (Charles Lane in 12385)
+# + Adding "@ file" syntax (Peter Scott in 12014)
+# + Debug reloading selfloaded stuff (Ilya Zakharevich in 11457)
+# + $^S and other debugger fixes (Ilya Zakharevich in 11120)
+# + Forgot a my() declaration (Ilya Zakharevich in 11085)
+# Changes: 1.15: Nov 6, 2001 Michael G Schwern <schwern@pobox.com>
+# + Updated 1.14 change log
+# + Added *dbline explainatory comments
+# + Mentioning perldebguts man page
+# Changes: 1.16: Feb 15, 2002 Mark-Jason Dominus <mjd@plover.com>
+# + $onetimeDump improvements
+# Changes: 1.17: Feb 20, 2002 Richard Foley <richard.foley@rfi.net>
+# Moved some code to cmd_[.]()'s for clarity and ease of handling,
+# rationalised the following commands and added cmd_wrapper() to
+# enable switching between old and frighteningly consistent new
+# behaviours for diehards: 'o CommandSet=pre580' (sigh...)
+# a(add), A(del) # action expr (added del by line)
+# + b(add), B(del) # break [line] (was b,D)
+# + w(add), W(del) # watch expr (was W,W)
+# # added del by expr
+# + h(summary), h h(long) # help (hh) (was h h,h)
+# + m(methods), M(modules) # ... (was m,v)
+# + o(option) # lc (was O)
+# + v(view code), V(view Variables) # ... (was w,V)
+# Changes: 1.18: Mar 17, 2002 Richard Foley <richard.foley@rfi.net>
+# + fixed missing cmd_O bug
+# Changes: 1.19: Mar 29, 2002 Spider Boardman
+# + Added missing local()s -- DB::DB is called recursively.
+# Changes: 1.20: Feb 17, 2003 Richard Foley <richard.foley@rfi.net>
+# + pre'n'post commands no longer trashed with no args
+# + watch val joined out of eval()
+# Changes: 1.21: Jun 04, 2003 Joe McMahon <mcmahon@ibiblio.org>
+# + Added comments and reformatted source. No bug fixes/enhancements.
+# + Includes cleanup by Robin Barker and Jarkko Hietaniemi.
+# Changes: 1.22 Jun 09, 2003 Alex Vandiver <alexmv@MIT.EDU>
+# + Flush stdout/stderr before the debugger prompt is printed.
+# Changes: 1.23: Dec 21, 2003 Dominique Quatravaux
+# + Fix a side-effect of bug #24674 in the perl debugger ("odd taint bug")
+# Changes: 1.24: Mar 03, 2004 Richard Foley <richard.foley@rfi.net>
+# + Added command to save all debugger commands for sourcing later.
+# + Added command to display parent inheritance tree of given class.
+# + Fixed minor newline in history bug.
+# Changes: 1.25: Apr 17, 2004 Richard Foley <richard.foley@rfi.net>
+# + Fixed option bug (setting invalid options + not recognising valid short forms)
+# Changes: 1.26: Apr 22, 2004 Richard Foley <richard.foley@rfi.net>
+# + unfork the 5.8.x and 5.9.x debuggers.
+# + whitespace and assertions call cleanup across versions
+# + H * deletes (resets) history
+# + i now handles Class + blessed objects
+# Changes: 1.27: May 09, 2004 Richard Foley <richard.foley@rfi.net>
+# + updated pod page references - clunky.
+# + removed windowid restriction for forking into an xterm.
+# + more whitespace again.
+# + wrapped restart and enabled rerun [-n] (go back n steps) command.
+# Changes: 1.28: Oct 12, 2004 Richard Foley <richard.foley@rfi.net>
+# + Added threads support (inc. e and E commands)
+####################################################################
+
+=head1 DEBUGGER INITIALIZATION
+
+The debugger starts up in phases.
+
+=head2 BASIC SETUP
+
+First, it initializes the environment it wants to run in: turning off
+warnings during its own compilation, defining variables which it will need
+to avoid warnings later, setting itself up to not exit when the program
+terminates, and defaulting to printing return values for the C<r> command.
+
+=cut
+
+# Needed for the statement after exec():
+#
+# This BEGIN block is simply used to switch off warnings during debugger
+# compiliation. Probably it would be better practice to fix the warnings,
+# but this is how it's done at the moment.
+
+BEGIN {
+ $ini_warn = $^W;
+ $^W = 0;
+} # Switch compilation warnings off until another BEGIN.
+
+# test if assertions are supported and actived:
+BEGIN {
+ $ini_assertion = eval "sub asserting_test : assertion {1}; 1";
+
+ # $ini_assertion = undef => assertions unsupported,
+ # " = 1 => assertions supported
+ # print "\$ini_assertion=$ini_assertion\n";
+}
+
+local ($^W) = 0; # Switch run-time warnings off during init.
+
+=head2 THREADS SUPPORT
+
+If we are running under a threaded Perl, we require threads and threads::shared
+if the environment variable C<PERL5DB_THREADED> is set, to enable proper
+threaded debugger control. C<-dt> can also be used to set this.
+
+Each new thread will be announced and the debugger prompt will always inform
+you of each new thread created. It will also indicate the thread id in which
+we are currently running within the prompt like this:
+
+ [tid] DB<$i>
+
+Where C<[tid]> is an integer thread id and C<$i> is the familiar debugger
+command prompt. The prompt will show: C<[0]> when running under threads, but
+not actually in a thread. C<[tid]> is consistent with C<gdb> usage.
+
+While running under threads, when you set or delete a breakpoint (etc.), this
+will apply to all threads, not just the currently running one. When you are
+in a currently executing thread, you will stay there until it completes. With
+the current implementation it is not currently possible to hop from one thread
+to another.
+
+The C<e> and C<E> commands are currently fairly minimal - see C<h e> and C<h E>.
+
+Note that threading support was built into the debugger as of Perl version
+C<5.8.6> and debugger version C<1.2.8>.
+
+=cut
+
+BEGIN {
+ # ensure we can share our non-threaded variables or no-op
+ if ($ENV{PERL5DB_THREADED}) {
+ require threads;
+ require threads::shared;
+ import threads::shared qw(share);
+ $DBGR;
+ share(\$DBGR);
+ lock($DBGR);
+ print "Threads support enabled\n";
+ } else {
+ *lock = sub(*) {};
+ *share = sub(*) {};
+ }
+}
+
+# This would probably be better done with "use vars", but that wasn't around
+# when this code was originally written. (Neither was "use strict".) And on
+# the principle of not fiddling with something that was working, this was
+# left alone.
+warn( # Do not ;-)
+ # These variables control the execution of 'dumpvar.pl'.
+ $dumpvar::hashDepth,
+ $dumpvar::arrayDepth,
+ $dumpvar::dumpDBFiles,
+ $dumpvar::dumpPackages,
+ $dumpvar::quoteHighBit,
+ $dumpvar::printUndef,
+ $dumpvar::globPrint,
+ $dumpvar::usageOnly,
+
+ # used to save @ARGV and extract any debugger-related flags.
+ @ARGS,
+
+ # used to control die() reporting in diesignal()
+ $Carp::CarpLevel,
+
+ # used to prevent multiple entries to diesignal()
+ # (if for instance diesignal() itself dies)
+ $panic,
+
+ # used to prevent the debugger from running nonstop
+ # after a restart
+ $second_time,
+ )
+ if 0;
+
+foreach my $k (keys (%INC)) {
+ &share(\$main::{'_<'.$filename});
+};
+
+# Command-line + PERLLIB:
+# Save the contents of @INC before they are modified elsewhere.
+@ini_INC = @INC;
+
+# This was an attempt to clear out the previous values of various
+# trapped errors. Apparently it didn't help. XXX More info needed!
+# $prevwarn = $prevdie = $prevbus = $prevsegv = ''; # Does not help?!
+
+# We set these variables to safe values. We don't want to blindly turn
+# off warnings, because other packages may still want them.
+$trace = $signal = $single = 0; # Uninitialized warning suppression
+ # (local $^W cannot help - other packages!).
+
+# Default to not exiting when program finishes; print the return
+# value when the 'r' command is used to return from a subroutine.
+$inhibit_exit = $option{PrintRet} = 1;
+
+=head1 OPTION PROCESSING
+
+The debugger's options are actually spread out over the debugger itself and
+C<dumpvar.pl>; some of these are variables to be set, while others are
+subs to be called with a value. To try to make this a little easier to
+manage, the debugger uses a few data structures to define what options
+are legal and how they are to be processed.
+
+First, the C<@options> array defines the I<names> of all the options that
+are to be accepted.
+
+=cut
+
+@options = qw(
+ CommandSet
+ hashDepth arrayDepth dumpDepth
+ DumpDBFiles DumpPackages DumpReused
+ compactDump veryCompact quote
+ HighBit undefPrint globPrint
+ PrintRet UsageOnly frame
+ AutoTrace TTY noTTY
+ ReadLine NonStop LineInfo
+ maxTraceLen recallCommand ShellBang
+ pager tkRunning ornaments
+ signalLevel warnLevel dieLevel
+ inhibit_exit ImmediateStop bareStringify
+ CreateTTY RemotePort windowSize
+ DollarCaretP OnlyAssertions WarnAssertions
+);
+
+@RememberOnROptions = qw(DollarCaretP OnlyAssertions);
+
+=pod
+
+Second, C<optionVars> lists the variables that each option uses to save its
+state.
+
+=cut
+
+%optionVars = (
+ hashDepth => \$dumpvar::hashDepth,
+ arrayDepth => \$dumpvar::arrayDepth,
+ CommandSet => \$CommandSet,
+ DumpDBFiles => \$dumpvar::dumpDBFiles,
+ DumpPackages => \$dumpvar::dumpPackages,
+ DumpReused => \$dumpvar::dumpReused,
+ HighBit => \$dumpvar::quoteHighBit,
+ undefPrint => \$dumpvar::printUndef,
+ globPrint => \$dumpvar::globPrint,
+ UsageOnly => \$dumpvar::usageOnly,
+ CreateTTY => \$CreateTTY,
+ bareStringify => \$dumpvar::bareStringify,
+ frame => \$frame,
+ AutoTrace => \$trace,
+ inhibit_exit => \$inhibit_exit,
+ maxTraceLen => \$maxtrace,
+ ImmediateStop => \$ImmediateStop,
+ RemotePort => \$remoteport,
+ windowSize => \$window,
+ WarnAssertions => \$warnassertions,
+);
+
+=pod
+
+Third, C<%optionAction> defines the subroutine to be called to process each
+option.
+
+=cut
+
+%optionAction = (
+ compactDump => \&dumpvar::compactDump,
+ veryCompact => \&dumpvar::veryCompact,
+ quote => \&dumpvar::quote,
+ TTY => \&TTY,
+ noTTY => \&noTTY,
+ ReadLine => \&ReadLine,
+ NonStop => \&NonStop,
+ LineInfo => \&LineInfo,
+ recallCommand => \&recallCommand,
+ ShellBang => \&shellBang,
+ pager => \&pager,
+ signalLevel => \&signalLevel,
+ warnLevel => \&warnLevel,
+ dieLevel => \&dieLevel,
+ tkRunning => \&tkRunning,
+ ornaments => \&ornaments,
+ RemotePort => \&RemotePort,
+ DollarCaretP => \&DollarCaretP,
+ OnlyAssertions=> \&OnlyAssertions,
+);
+
+=pod
+
+Last, the C<%optionRequire> notes modules that must be C<require>d if an
+option is used.
+
+=cut
+
+# Note that this list is not complete: several options not listed here
+# actually require that dumpvar.pl be loaded for them to work, but are
+# not in the table. A subsequent patch will correct this problem; for
+# the moment, we're just recommenting, and we are NOT going to change
+# function.
+%optionRequire = (
+ compactDump => 'dumpvar.pl',
+ veryCompact => 'dumpvar.pl',
+ quote => 'dumpvar.pl',
+);
+
+=pod
+
+There are a number of initialization-related variables which can be set
+by putting code to set them in a BEGIN block in the C<PERL5DB> environment
+variable. These are:
+
+=over 4
+
+=item C<$rl> - readline control XXX needs more explanation
+
+=item C<$warnLevel> - whether or not debugger takes over warning handling
+
+=item C<$dieLevel> - whether or not debugger takes over die handling
+
+=item C<$signalLevel> - whether or not debugger takes over signal handling
+
+=item C<$pre> - preprompt actions (array reference)
+
+=item C<$post> - postprompt actions (array reference)
+
+=item C<$pretype>
+
+=item C<$CreateTTY> - whether or not to create a new TTY for this debugger
+
+=item C<$CommandSet> - which command set to use (defaults to new, documented set)
+
+=back
+
+=cut
+
+# These guys may be defined in $ENV{PERL5DB} :
+$rl = 1 unless defined $rl;
+$warnLevel = 1 unless defined $warnLevel;
+$dieLevel = 1 unless defined $dieLevel;
+$signalLevel = 1 unless defined $signalLevel;
+$pre = [] unless defined $pre;
+$post = [] unless defined $post;
+$pretype = [] unless defined $pretype;
+$CreateTTY = 3 unless defined $CreateTTY;
+$CommandSet = '580' unless defined $CommandSet;
+
+share($rl);
+share($warnLevel);
+share($dieLevel);
+share($signalLevel);
+share($pre);
+share($post);
+share($pretype);
+share($rl);
+share($CreateTTY);
+share($CommandSet);
+
+=pod
+
+The default C<die>, C<warn>, and C<signal> handlers are set up.
+
+=cut
+
+warnLevel($warnLevel);
+dieLevel($dieLevel);
+signalLevel($signalLevel);
+
+=pod
+
+The pager to be used is needed next. We try to get it from the
+environment first. if it's not defined there, we try to find it in
+the Perl C<Config.pm>. If it's not there, we default to C<more>. We
+then call the C<pager()> function to save the pager name.
+
+=cut
+
+# This routine makes sure $pager is set up so that '|' can use it.
+pager(
+
+ # If PAGER is defined in the environment, use it.
+ defined $ENV{PAGER}
+ ? $ENV{PAGER}
+
+ # If not, see if Config.pm defines it.
+ : eval { require Config }
+ && defined $Config::Config{pager}
+ ? $Config::Config{pager}
+
+ # If not, fall back to 'more'.
+ : 'more'
+ )
+ unless defined $pager;
+
+=pod
+
+We set up the command to be used to access the man pages, the command
+recall character (C<!> unless otherwise defined) and the shell escape
+character (C<!> unless otherwise defined). Yes, these do conflict, and
+neither works in the debugger at the moment.
+
+=cut
+
+setman();
+
+# Set up defaults for command recall and shell escape (note:
+# these currently don't work in linemode debugging).
+&recallCommand("!") unless defined $prc;
+&shellBang("!") unless defined $psh;
+
+=pod
+
+We then set up the gigantic string containing the debugger help.
+We also set the limit on the number of arguments we'll display during a
+trace.
+
+=cut
+
+sethelp();
+
+# If we didn't get a default for the length of eval/stack trace args,
+# set it here.
+$maxtrace = 400 unless defined $maxtrace;
+
+=head2 SETTING UP THE DEBUGGER GREETING
+
+The debugger I<greeting> helps to inform the user how many debuggers are
+running, and whether the current debugger is the primary or a child.
+
+If we are the primary, we just hang onto our pid so we'll have it when
+or if we start a child debugger. If we are a child, we'll set things up
+so we'll have a unique greeting and so the parent will give us our own
+TTY later.
+
+We save the current contents of the C<PERLDB_PIDS> environment variable
+because we mess around with it. We'll also need to hang onto it because
+we'll need it if we restart.
+
+Child debuggers make a label out of the current PID structure recorded in
+PERLDB_PIDS plus the new PID. They also mark themselves as not having a TTY
+yet so the parent will give them one later via C<resetterm()>.
+
+=cut
+
+# Save the current contents of the environment; we're about to
+# much with it. We'll need this if we have to restart.
+$ini_pids = $ENV{PERLDB_PIDS};
+
+if ( defined $ENV{PERLDB_PIDS} ) {
+
+ # We're a child. Make us a label out of the current PID structure
+ # recorded in PERLDB_PIDS plus our (new) PID. Mark us as not having
+ # a term yet so the parent will give us one later via resetterm().
+ $pids = "[$ENV{PERLDB_PIDS}]";
+ $ENV{PERLDB_PIDS} .= "->$$";
+ $term_pid = -1;
+} ## end if (defined $ENV{PERLDB_PIDS...
+else {
+
+ # We're the parent PID. Initialize PERLDB_PID in case we end up with a
+ # child debugger, and mark us as the parent, so we'll know to set up
+ # more TTY's is we have to.
+ $ENV{PERLDB_PIDS} = "$$";
+ $pids = "{pid=$$}";
+ $term_pid = $$;
+}
+
+$pidprompt = '';
+
+# Sets up $emacs as a synonym for $slave_editor.
+*emacs = $slave_editor if $slave_editor; # May be used in afterinit()...
+
+=head2 READING THE RC FILE
+
+The debugger will read a file of initialization options if supplied. If
+running interactively, this is C<.perldb>; if not, it's C<perldb.ini>.
+
+=cut
+
+# As noted, this test really doesn't check accurately that the debugger
+# is running at a terminal or not.
+
+if ( -e "/dev/tty" ) { # this is the wrong metric!
+ $rcfile = ".perldb";
+}
+else {
+ $rcfile = "perldb.ini";
+}
+
+=pod
+
+The debugger does a safety test of the file to be read. It must be owned
+either by the current user or root, and must only be writable by the owner.
+
+=cut
+
+# This wraps a safety test around "do" to read and evaluate the init file.
+#
+# This isn't really safe, because there's a race
+# between checking and opening. The solution is to
+# open and fstat the handle, but then you have to read and
+# eval the contents. But then the silly thing gets
+# your lexical scope, which is unfortunate at best.
+sub safe_do {
+ my $file = shift;
+
+ # Just exactly what part of the word "CORE::" don't you understand?
+ local $SIG{__WARN__};
+ local $SIG{__DIE__};
+
+ unless ( is_safe_file($file) ) {
+ CORE::warn <<EO_GRIPE;
+perldb: Must not source insecure rcfile $file.
+ You or the superuser must be the owner, and it must not
+ be writable by anyone but its owner.
+EO_GRIPE
+ return;
+ } ## end unless (is_safe_file($file...
+
+ do $file;
+ CORE::warn("perldb: couldn't parse $file: $@") if $@;
+} ## end sub safe_do
+
+# This is the safety test itself.
+#
+# Verifies that owner is either real user or superuser and that no
+# one but owner may write to it. This function is of limited use
+# when called on a path instead of upon a handle, because there are
+# no guarantees that filename (by dirent) whose file (by ino) is
+# eventually accessed is the same as the one tested.
+# Assumes that the file's existence is not in doubt.
+sub is_safe_file {
+ my $path = shift;
+ stat($path) || return; # mysteriously vaporized
+ my ( $dev, $ino, $mode, $nlink, $uid, $gid ) = stat(_);
+
+ return 0 if $uid != 0 && $uid != $<;
+ return 0 if $mode & 022;
+ return 1;
+} ## end sub is_safe_file
+
+# If the rcfile (whichever one we decided was the right one to read)
+# exists, we safely do it.
+if ( -f $rcfile ) {
+ safe_do("./$rcfile");
+}
+
+# If there isn't one here, try the user's home directory.
+elsif ( defined $ENV{HOME} && -f "$ENV{HOME}/$rcfile" ) {
+ safe_do("$ENV{HOME}/$rcfile");
+}
+
+# Else try the login directory.
+elsif ( defined $ENV{LOGDIR} && -f "$ENV{LOGDIR}/$rcfile" ) {
+ safe_do("$ENV{LOGDIR}/$rcfile");
+}
+
+# If the PERLDB_OPTS variable has options in it, parse those out next.
+if ( defined $ENV{PERLDB_OPTS} ) {
+ parse_options( $ENV{PERLDB_OPTS} );
+}
+
+=pod
+
+The last thing we do during initialization is determine which subroutine is
+to be used to obtain a new terminal when a new debugger is started. Right now,
+the debugger only handles X Windows and OS/2.
+
+=cut
+
+# Set up the get_fork_TTY subroutine to be aliased to the proper routine.
+# Works if you're running an xterm or xterm-like window, or you're on
+# OS/2. This may need some expansion: for instance, this doesn't handle
+# OS X Terminal windows.
+
+if (
+ not defined &get_fork_TTY # no routine exists,
+ and defined $ENV{TERM} # and we know what kind
+ # of terminal this is,
+ and $ENV{TERM} eq 'xterm' # and it's an xterm,
+# and defined $ENV{WINDOWID} # and we know what window this is, <- wrong metric
+ and defined $ENV{DISPLAY} # and what display it's on,
+ )
+{
+ *get_fork_TTY = \&xterm_get_fork_TTY; # use the xterm version
+} ## end if (not defined &get_fork_TTY...
+elsif ( $^O eq 'os2' ) { # If this is OS/2,
+ *get_fork_TTY = \&os2_get_fork_TTY; # use the OS/2 version
+}
+
+# untaint $^O, which may have been tainted by the last statement.
+# see bug [perl #24674]
+$^O =~ m/^(.*)\z/;
+$^O = $1;
+
+# Here begin the unreadable code. It needs fixing.
+
+=head2 RESTART PROCESSING
+
+This section handles the restart command. When the C<R> command is invoked, it
+tries to capture all of the state it can into environment variables, and
+then sets C<PERLDB_RESTART>. When we start executing again, we check to see
+if C<PERLDB_RESTART> is there; if so, we reload all the information that
+the R command stuffed into the environment variables.
+
+ PERLDB_RESTART - flag only, contains no restart data itself.
+ PERLDB_HIST - command history, if it's available
+ PERLDB_ON_LOAD - breakpoints set by the rc file
+ PERLDB_POSTPONE - subs that have been loaded/not executed, and have actions
+ PERLDB_VISITED - files that had breakpoints
+ PERLDB_FILE_... - breakpoints for a file
+ PERLDB_OPT - active options
+ PERLDB_INC - the original @INC
+ PERLDB_PRETYPE - preprompt debugger actions
+ PERLDB_PRE - preprompt Perl code
+ PERLDB_POST - post-prompt Perl code
+ PERLDB_TYPEAHEAD - typeahead captured by readline()
+
+We chug through all these variables and plug the values saved in them
+back into the appropriate spots in the debugger.
+
+=cut
+
+if ( exists $ENV{PERLDB_RESTART} ) {
+
+ # We're restarting, so we don't need the flag that says to restart anymore.
+ delete $ENV{PERLDB_RESTART};
+
+ # $restart = 1;
+ @hist = get_list('PERLDB_HIST');
+ %break_on_load = get_list("PERLDB_ON_LOAD");
+ %postponed = get_list("PERLDB_POSTPONE");
+
+ share(@hist);
+ share(@truehist);
+ share(%break_on_load);
+ share(%postponed);
+
+ # restore breakpoints/actions
+ my @had_breakpoints = get_list("PERLDB_VISITED");
+ for ( 0 .. $#had_breakpoints ) {
+ my %pf = get_list("PERLDB_FILE_$_");
+ $postponed_file{ $had_breakpoints[$_] } = \%pf if %pf;
+ }
+
+ # restore options
+ my %opt = get_list("PERLDB_OPT");
+ my ( $opt, $val );
+ while ( ( $opt, $val ) = each %opt ) {
+ $val =~ s/[\\\']/\\$1/g;
+ parse_options("$opt'$val'");
+ }
+
+ # restore original @INC
+ @INC = get_list("PERLDB_INC");
+ @ini_INC = @INC;
+
+ # return pre/postprompt actions and typeahead buffer
+ $pretype = [ get_list("PERLDB_PRETYPE") ];
+ $pre = [ get_list("PERLDB_PRE") ];
+ $post = [ get_list("PERLDB_POST") ];
+ @typeahead = get_list( "PERLDB_TYPEAHEAD", @typeahead );
+} ## end if (exists $ENV{PERLDB_RESTART...
+
+=head2 SETTING UP THE TERMINAL
+
+Now, we'll decide how the debugger is going to interact with the user.
+If there's no TTY, we set the debugger to run non-stop; there's not going
+to be anyone there to enter commands.
+
+=cut
+
+if ($notty) {
+ $runnonstop = 1;
+ share($runnonstop);
+}
+
+=pod
+
+If there is a TTY, we have to determine who it belongs to before we can
+proceed. If this is a slave editor or graphical debugger (denoted by
+the first command-line switch being '-emacs'), we shift this off and
+set C<$rl> to 0 (XXX ostensibly to do straight reads).
+
+=cut
+
+else {
+
+ # Is Perl being run from a slave editor or graphical debugger?
+ # If so, don't use readline, and set $slave_editor = 1.
+ $slave_editor =
+ ( ( defined $main::ARGV[0] ) and ( $main::ARGV[0] eq '-emacs' ) );
+ $rl = 0, shift(@main::ARGV) if $slave_editor;
+
+ #require Term::ReadLine;
+
+=pod
+
+We then determine what the console should be on various systems:
+
+=over 4
+
+=item * Cygwin - We use C<stdin> instead of a separate device.
+
+=cut
+
+ if ( $^O eq 'cygwin' ) {
+
+ # /dev/tty is binary. use stdin for textmode
+ undef $console;
+ }
+
+=item * Unix - use C</dev/tty>.
+
+=cut
+
+ elsif ( -e "/dev/tty" ) {
+ $console = "/dev/tty";
+ }
+
+=item * Windows or MSDOS - use C<con>.
+
+=cut
+
+ elsif ( $^O eq 'dos' or -e "con" or $^O eq 'MSWin32' ) {
+ $console = "con";
+ }
+
+=item * MacOS - use C<Dev:Console:Perl Debug> if this is the MPW version; C<Dev:
+Console> if not.
+
+Note that Mac OS X returns C<darwin>, not C<MacOS>. Also note that the debugger doesn't do anything special for C<darwin>. Maybe it should.
+
+=cut
+
+ elsif ( $^O eq 'MacOS' ) {
+ if ( $MacPerl::Version !~ /MPW/ ) {
+ $console =
+ "Dev:Console:Perl Debug"; # Separate window for application
+ }
+ else {
+ $console = "Dev:Console";
+ }
+ } ## end elsif ($^O eq 'MacOS')
+
+=item * VMS - use C<sys$command>.
+
+=cut
+
+ else {
+
+ # everything else is ...
+ $console = "sys\$command";
+ }
+
+=pod
+
+=back
+
+Several other systems don't use a specific console. We C<undef $console>
+for those (Windows using a slave editor/graphical debugger, NetWare, OS/2
+with a slave editor, Epoc).
+
+=cut
+
+ if ( ( $^O eq 'MSWin32' ) and ( $slave_editor or defined $ENV{EMACS} ) ) {
+
+ # /dev/tty is binary. use stdin for textmode
+ $console = undef;
+ }
+
+ if ( $^O eq 'NetWare' ) {
+
+ # /dev/tty is binary. use stdin for textmode
+ $console = undef;
+ }
+
+ # In OS/2, we need to use STDIN to get textmode too, even though
+ # it pretty much looks like Unix otherwise.
+ if ( defined $ENV{OS2_SHELL} and ( $slave_editor or $ENV{WINDOWID} ) )
+ { # In OS/2
+ $console = undef;
+ }
+
+ # EPOC also falls into the 'got to use STDIN' camp.
+ if ( $^O eq 'epoc' ) {
+ $console = undef;
+ }
+
+=pod
+
+If there is a TTY hanging around from a parent, we use that as the console.
+
+=cut
+
+ $console = $tty if defined $tty;
+
+=head2 SOCKET HANDLING
+
+The debugger is capable of opening a socket and carrying out a debugging
+session over the socket.
+
+If C<RemotePort> was defined in the options, the debugger assumes that it
+should try to start a debugging session on that port. It builds the socket
+and then tries to connect the input and output filehandles to it.
+
+=cut
+
+ # Handle socket stuff.
+
+ if ( defined $remoteport ) {
+
+ # If RemotePort was defined in the options, connect input and output
+ # to the socket.
+ require IO::Socket;
+ $OUT = new IO::Socket::INET(
+ Timeout => '10',
+ PeerAddr => $remoteport,
+ Proto => 'tcp',
+ );
+ if ( !$OUT ) { die "Unable to connect to remote host: $remoteport\n"; }
+ $IN = $OUT;
+ } ## end if (defined $remoteport)
+
+=pod
+
+If no C<RemotePort> was defined, and we want to create a TTY on startup,
+this is probably a situation where multiple debuggers are running (for example,
+a backticked command that starts up another debugger). We create a new IN and
+OUT filehandle, and do the necessary mojo to create a new TTY if we know how
+and if we can.
+
+=cut
+
+ # Non-socket.
+ else {
+
+ # Two debuggers running (probably a system or a backtick that invokes
+ # the debugger itself under the running one). create a new IN and OUT
+ # filehandle, and do the necessary mojo to create a new tty if we
+ # know how, and we can.
+ create_IN_OUT(4) if $CreateTTY & 4;
+ if ($console) {
+
+ # If we have a console, check to see if there are separate ins and
+ # outs to open. (They are assumed identiical if not.)
+
+ my ( $i, $o ) = split /,/, $console;
+ $o = $i unless defined $o;
+
+ # read/write on in, or just read, or read on STDIN.
+ open( IN, "+<$i" )
+ || open( IN, "<$i" )
+ || open( IN, "<&STDIN" );
+
+ # read/write/create/clobber out, or write/create/clobber out,
+ # or merge with STDERR, or merge with STDOUT.
+ open( OUT, "+>$o" )
+ || open( OUT, ">$o" )
+ || open( OUT, ">&STDERR" )
+ || open( OUT, ">&STDOUT" ); # so we don't dongle stdout
+
+ } ## end if ($console)
+ elsif ( not defined $console ) {
+
+ # No console. Open STDIN.
+ open( IN, "<&STDIN" );
+
+ # merge with STDERR, or with STDOUT.
+ open( OUT, ">&STDERR" )
+ || open( OUT, ">&STDOUT" ); # so we don't dongle stdout
+ $console = 'STDIN/OUT';
+ } ## end elsif (not defined $console)
+
+ # Keep copies of the filehandles so that when the pager runs, it
+ # can close standard input without clobbering ours.
+ $IN = \*IN, $OUT = \*OUT if $console or not defined $console;
+ } ## end elsif (from if(defined $remoteport))
+
+ # Unbuffer DB::OUT. We need to see responses right away.
+ my $previous = select($OUT);
+ $| = 1; # for DB::OUT
+ select($previous);
+
+ # Line info goes to debugger output unless pointed elsewhere.
+ # Pointing elsewhere makes it possible for slave editors to
+ # keep track of file and position. We have both a filehandle
+ # and a I/O description to keep track of.
+ $LINEINFO = $OUT unless defined $LINEINFO;
+ $lineinfo = $console unless defined $lineinfo;
+ # share($LINEINFO); # <- unable to share globs
+ share($lineinfo); #
+
+=pod
+
+To finish initialization, we show the debugger greeting,
+and then call the C<afterinit()> subroutine if there is one.
+
+=cut
+
+ # Show the debugger greeting.
+ $header =~ s/.Header: ([^,]+),v(\s+\S+\s+\S+).*$/$1$2/;
+ unless ($runnonstop) {
+ local $\ = '';
+ local $, = '';
+ if ( $term_pid eq '-1' ) {
+ print $OUT "\nDaughter DB session started...\n";
+ }
+ else {
+ print $OUT "\nLoading DB routines from $header\n";
+ print $OUT (
+ "Editor support ",
+ $slave_editor ? "enabled" : "available", ".\n"
+ );
+ print $OUT
+"\nEnter h or `h h' for help, or `$doccmd perldebug' for more help.\n\n";
+ } ## end else [ if ($term_pid eq '-1')
+ } ## end unless ($runnonstop)
+} ## end else [ if ($notty)
+
+# XXX This looks like a bug to me.
+# Why copy to @ARGS and then futz with @args?
+@ARGS = @ARGV;
+for (@args) {
+ # Make sure backslashes before single quotes are stripped out, and
+ # keep args unless they are numeric (XXX why?)
+ # s/\'/\\\'/g; # removed while not justified understandably
+ # s/(.*)/'$1'/ unless /^-?[\d.]+$/; # ditto
+}
+
+# If there was an afterinit() sub defined, call it. It will get
+# executed in our scope, so it can fiddle with debugger globals.
+if ( defined &afterinit ) { # May be defined in $rcfile
+ &afterinit();
+}
+
+# Inform us about "Stack dump during die enabled ..." in dieLevel().
+$I_m_init = 1;
+
+############################################################ Subroutines
+
+=head1 SUBROUTINES
+
+=head2 DB
+
+This gigantic subroutine is the heart of the debugger. Called before every
+statement, its job is to determine if a breakpoint has been reached, and
+stop if so; read commands from the user, parse them, and execute
+them, and hen send execution off to the next statement.
+
+Note that the order in which the commands are processed is very important;
+some commands earlier in the loop will actually alter the C<$cmd> variable
+to create other commands to be executed later. This is all highly I<optimized>
+but can be confusing. Check the comments for each C<$cmd ... && do {}> to
+see what's happening in any given command.
+
+=cut
+
+sub DB {
+
+ # lock the debugger and get the thread id for the prompt
+ lock($DBGR);
+ my $tid;
+ if ($ENV{PERL5DB_THREADED}) {
+ $tid = eval { "[".threads->self->tid."]" };
+ }
+
+ # Check for whether we should be running continuously or not.
+ # _After_ the perl program is compiled, $single is set to 1:
+ if ( $single and not $second_time++ ) {
+
+ # Options say run non-stop. Run until we get an interrupt.
+ if ($runnonstop) { # Disable until signal
+ # If there's any call stack in place, turn off single
+ # stepping into subs throughout the stack.
+ for ( $i = 0 ; $i <= $stack_depth ; ) {
+ $stack[ $i++ ] &= ~1;
+ }
+
+ # And we are now no longer in single-step mode.
+ $single = 0;
+
+ # If we simply returned at this point, we wouldn't get
+ # the trace info. Fall on through.
+ # return;
+ } ## end if ($runnonstop)
+
+ elsif ($ImmediateStop) {
+
+ # We are supposed to stop here; XXX probably a break.
+ $ImmediateStop = 0; # We've processed it; turn it off
+ $signal = 1; # Simulate an interrupt to force
+ # us into the command loop
+ }
+ } ## end if ($single and not $second_time...
+
+ # If we're in single-step mode, or an interrupt (real or fake)
+ # has occurred, turn off non-stop mode.
+ $runnonstop = 0 if $single or $signal;
+
+ # Preserve current values of $@, $!, $^E, $,, $/, $\, $^W.
+ # The code being debugged may have altered them.
+ &save;
+
+ # Since DB::DB gets called after every line, we can use caller() to
+ # figure out where we last were executing. Sneaky, eh? This works because
+ # caller is returning all the extra information when called from the
+ # debugger.
+ local ( $package, $filename, $line ) = caller;
+ local $filename_ini = $filename;
+
+ # set up the context for DB::eval, so it can properly execute
+ # code on behalf of the user. We add the package in so that the
+ # code is eval'ed in the proper package (not in the debugger!).
+ local $usercontext =
+ '($@, $!, $^E, $,, $/, $\, $^W) = @saved;' . "package $package;";
+
+ # Create an alias to the active file magical array to simplify
+ # the code here.
+ local (*dbline) = $main::{ '_<' . $filename };
+
+ # we need to check for pseudofiles on Mac OS (these are files
+ # not attached to a filename, but instead stored in Dev:Pseudo)
+ if ( $^O eq 'MacOS' && $#dbline < 0 ) {
+ $filename_ini = $filename = 'Dev:Pseudo';
+ *dbline = $main::{ '_<' . $filename };
+ }
+
+ # Last line in the program.
+ local $max = $#dbline;
+
+ # if we have something here, see if we should break.
+ if ( $dbline{$line}
+ && ( ( $stop, $action ) = split( /\0/, $dbline{$line} ) ) )
+ {
+
+ # Stop if the stop criterion says to just stop.
+ if ( $stop eq '1' ) {
+ $signal |= 1;
+ }
+
+ # It's a conditional stop; eval it in the user's context and
+ # see if we should stop. If so, remove the one-time sigil.
+ elsif ($stop) {
+ $evalarg = "\$DB::signal |= 1 if do {$stop}";
+ &eval;
+ $dbline{$line} =~ s/;9($|\0)/$1/;
+ }
+ } ## end if ($dbline{$line} && ...
+
+ # Preserve the current stop-or-not, and see if any of the W
+ # (watch expressions) has changed.
+ my $was_signal = $signal;
+
+ # If we have any watch expressions ...
+ if ( $trace & 2 ) {
+ for ( my $n = 0 ; $n <= $#to_watch ; $n++ ) {
+ $evalarg = $to_watch[$n];
+ local $onetimeDump; # Tell DB::eval() to not output results
+
+ # Fix context DB::eval() wants to return an array, but
+ # we need a scalar here.
+ my ($val) = join( "', '", &eval );
+ $val = ( ( defined $val ) ? "'$val'" : 'undef' );
+
+ # Did it change?
+ if ( $val ne $old_watch[$n] ) {
+
+ # Yep! Show the difference, and fake an interrupt.
+ $signal = 1;
+ print $OUT <<EOP;
+Watchpoint $n:\t$to_watch[$n] changed:
+ old value:\t$old_watch[$n]
+ new value:\t$val
+EOP
+ $old_watch[$n] = $val;
+ } ## end if ($val ne $old_watch...
+ } ## end for (my $n = 0 ; $n <= ...
+ } ## end if ($trace & 2)
+
+=head2 C<watchfunction()>
+
+C<watchfunction()> is a function that can be defined by the user; it is a
+function which will be run on each entry to C<DB::DB>; it gets the
+current package, filename, and line as its parameters.
+
+The watchfunction can do anything it likes; it is executing in the
+debugger's context, so it has access to all of the debugger's internal
+data structures and functions.
+
+C<watchfunction()> can control the debugger's actions. Any of the following
+will cause the debugger to return control to the user's program after
+C<watchfunction()> executes:
+
+=over 4
+
+=item *
+
+Returning a false value from the C<watchfunction()> itself.
+
+=item *
+
+Altering C<$single> to a false value.
+
+=item *
+
+Altering C<$signal> to a false value.
+
+=item *
+
+Turning off the C<4> bit in C<$trace> (this also disables the
+check for C<watchfunction()>. This can be done with
+
+ $trace &= ~4;
+
+=back
+
+=cut
+
+ # If there's a user-defined DB::watchfunction, call it with the
+ # current package, filename, and line. The function executes in
+ # the DB:: package.
+ if ( $trace & 4 ) { # User-installed watch
+ return
+ if watchfunction( $package, $filename, $line )
+ and not $single
+ and not $was_signal
+ and not( $trace & ~4 );
+ } ## end if ($trace & 4)
+
+ # Pick up any alteration to $signal in the watchfunction, and
+ # turn off the signal now.
+ $was_signal = $signal;
+ $signal = 0;
+
+=head2 GETTING READY TO EXECUTE COMMANDS
+
+The debugger decides to take control if single-step mode is on, the
+C<t> command was entered, or the user generated a signal. If the program
+has fallen off the end, we set things up so that entering further commands
+won't cause trouble, and we say that the program is over.
+
+=cut
+
+ # Check to see if we should grab control ($single true,
+ # trace set appropriately, or we got a signal).
+ if ( $single || ( $trace & 1 ) || $was_signal ) {
+
+ # Yes, grab control.
+ if ($slave_editor) {
+
+ # Tell the editor to update its position.
+ $position = "\032\032$filename:$line:0\n";
+ print_lineinfo($position);
+ }
+
+=pod
+
+Special check: if we're in package C<DB::fake>, we've gone through the
+C<END> block at least once. We set up everything so that we can continue
+to enter commands and have a valid context to be in.
+
+=cut
+
+ elsif ( $package eq 'DB::fake' ) {
+
+ # Fallen off the end already.
+ $term || &setterm;
+ print_help(<<EOP);
+Debugged program terminated. Use B<q> to quit or B<R> to restart,
+ use B<o> I<inhibit_exit> to avoid stopping after program termination,
+ B<h q>, B<h R> or B<h o> to get additional info.
+EOP
+
+ # Set the DB::eval context appropriately.
+ $package = 'main';
+ $usercontext =
+ '($@, $!, $^E, $,, $/, $\, $^W) = @saved;'
+ . "package $package;"; # this won't let them modify, alas
+ } ## end elsif ($package eq 'DB::fake')
+
+=pod
+
+If the program hasn't finished executing, we scan forward to the
+next executable line, print that out, build the prompt from the file and line
+number information, and print that.
+
+=cut
+
+ else {
+
+ # Still somewhere in the midst of execution. Set up the
+ # debugger prompt.
+ $sub =~ s/\'/::/; # Swap Perl 4 package separators (') to
+ # Perl 5 ones (sorry, we don't print Klingon
+ #module names)
+
+ $prefix = $sub =~ /::/ ? "" : "${'package'}::";
+ $prefix .= "$sub($filename:";
+ $after = ( $dbline[$line] =~ /\n$/ ? '' : "\n" );
+
+ # Break up the prompt if it's really long.
+ if ( length($prefix) > 30 ) {
+ $position = "$prefix$line):\n$line:\t$dbline[$line]$after";
+ $prefix = "";
+ $infix = ":\t";
+ }
+ else {
+ $infix = "):\t";
+ $position = "$prefix$line$infix$dbline[$line]$after";
+ }
+
+ # Print current line info, indenting if necessary.
+ if ($frame) {
+ print_lineinfo( ' ' x $stack_depth,
+ "$line:\t$dbline[$line]$after" );
+ }
+ else {
+ print_lineinfo($position);
+ }
+
+ # Scan forward, stopping at either the end or the next
+ # unbreakable line.
+ for ( $i = $line + 1 ; $i <= $max && $dbline[$i] == 0 ; ++$i )
+ { #{ vi
+
+ # Drop out on null statements, block closers, and comments.
+ last if $dbline[$i] =~ /^\s*[\;\}\#\n]/;
+
+ # Drop out if the user interrupted us.
+ last if $signal;
+
+ # Append a newline if the line doesn't have one. Can happen
+ # in eval'ed text, for instance.
+ $after = ( $dbline[$i] =~ /\n$/ ? '' : "\n" );
+
+ # Next executable line.
+ $incr_pos = "$prefix$i$infix$dbline[$i]$after";
+ $position .= $incr_pos;
+ if ($frame) {
+
+ # Print it indented if tracing is on.
+ print_lineinfo( ' ' x $stack_depth,
+ "$i:\t$dbline[$i]$after" );
+ }
+ else {
+ print_lineinfo($incr_pos);
+ }
+ } ## end for ($i = $line + 1 ; $i...
+ } ## end else [ if ($slave_editor)
+ } ## end if ($single || ($trace...
+
+=pod
+
+If there's an action to be executed for the line we stopped at, execute it.
+If there are any preprompt actions, execute those as well.
+
+=cut
+
+ # If there's an action, do it now.
+ $evalarg = $action, &eval if $action;
+
+ # Are we nested another level (e.g., did we evaluate a function
+ # that had a breakpoint in it at the debugger prompt)?
+ if ( $single || $was_signal ) {
+
+ # Yes, go down a level.
+ local $level = $level + 1;
+
+ # Do any pre-prompt actions.
+ foreach $evalarg (@$pre) {
+ &eval;
+ }
+
+ # Complain about too much recursion if we passed the limit.
+ print $OUT $stack_depth . " levels deep in subroutine calls!\n"
+ if $single & 4;
+
+ # The line we're currently on. Set $incr to -1 to stay here
+ # until we get a command that tells us to advance.
+ $start = $line;
+ $incr = -1; # for backward motion.
+
+ # Tack preprompt debugger actions ahead of any actual input.
+ @typeahead = ( @$pretype, @typeahead );
+
+=head2 WHERE ARE WE?
+
+XXX Relocate this section?
+
+The debugger normally shows the line corresponding to the current line of
+execution. Sometimes, though, we want to see the next line, or to move elsewhere
+in the file. This is done via the C<$incr>, C<$start>, and C<$max> variables.
+
+C<$incr> controls by how many lines the I<current> line should move forward
+after a command is executed. If set to -1, this indicates that the I<current>
+line shouldn't change.
+
+C<$start> is the I<current> line. It is used for things like knowing where to
+move forwards or backwards from when doing an C<L> or C<-> command.
+
+C<$max> tells the debugger where the last line of the current file is. It's
+used to terminate loops most often.
+
+=head2 THE COMMAND LOOP
+
+Most of C<DB::DB> is actually a command parsing and dispatch loop. It comes
+in two parts:
+
+=over 4
+
+=item *
+
+The outer part of the loop, starting at the C<CMD> label. This loop
+reads a command and then executes it.
+
+=item *
+
+The inner part of the loop, starting at the C<PIPE> label. This part
+is wholly contained inside the C<CMD> block and only executes a command.
+Used to handle commands running inside a pager.
+
+=back
+
+So why have two labels to restart the loop? Because sometimes, it's easier to
+have a command I<generate> another command and then re-execute the loop to do
+the new command. This is faster, but perhaps a bit more convoluted.
+
+=cut
+
+ # The big command dispatch loop. It keeps running until the
+ # user yields up control again.
+ #
+ # If we have a terminal for input, and we get something back
+ # from readline(), keep on processing.
+ CMD:
+ while (
+
+ # We have a terminal, or can get one ...
+ ( $term || &setterm ),
+
+ # ... and it belogs to this PID or we get one for this PID ...
+ ( $term_pid == $$ or resetterm(1) ),
+
+ # ... and we got a line of command input ...
+ defined(
+ $cmd = &readline(
+ "$pidprompt $tid DB"
+ . ( '<' x $level )
+ . ( $#hist + 1 )
+ . ( '>' x $level ) . " "
+ )
+ )
+ )
+ {
+
+ share($cmd);
+ # ... try to execute the input as debugger commands.
+
+ # Don't stop running.
+ $single = 0;
+
+ # No signal is active.
+ $signal = 0;
+
+ # Handle continued commands (ending with \):
+ $cmd =~ s/\\$/\n/ && do {
+ $cmd .= &readline(" cont: ");
+ redo CMD;
+ };
+
+=head4 The null command
+
+A newline entered by itself means I<re-execute the last command>. We grab the
+command out of C<$laststep> (where it was recorded previously), and copy it
+back into C<$cmd> to be executed below. If there wasn't any previous command,
+we'll do nothing below (no command will match). If there was, we also save it
+in the command history and fall through to allow the command parsing to pick
+it up.
+
+=cut
+
+ # Empty input means repeat the last command.
+ $cmd =~ /^$/ && ( $cmd = $laststep );
+ chomp($cmd); # get rid of the annoying extra newline
+ push( @hist, $cmd ) if length($cmd) > 1;
+ push( @truehist, $cmd );
+ share(@hist);
+ share(@truehist);
+
+ # This is a restart point for commands that didn't arrive
+ # via direct user input. It allows us to 'redo PIPE' to
+ # re-execute command processing without reading a new command.
+ PIPE: {
+ $cmd =~ s/^\s+//s; # trim annoying leading whitespace
+ $cmd =~ s/\s+$//s; # trim annoying trailing whitespace
+ ($i) = split( /\s+/, $cmd );
+
+=head3 COMMAND ALIASES
+
+The debugger can create aliases for commands (these are stored in the
+C<%alias> hash). Before a command is executed, the command loop looks it up
+in the alias hash and substitutes the contents of the alias for the command,
+completely replacing it.
+
+=cut
+
+ # See if there's an alias for the command, and set it up if so.
+ if ( $alias{$i} ) {
+
+ # Squelch signal handling; we want to keep control here
+ # if something goes loco during the alias eval.
+ local $SIG{__DIE__};
+ local $SIG{__WARN__};
+
+ # This is a command, so we eval it in the DEBUGGER's
+ # scope! Otherwise, we can't see the special debugger
+ # variables, or get to the debugger's subs. (Well, we
+ # _could_, but why make it even more complicated?)
+ eval "\$cmd =~ $alias{$i}";
+ if ($@) {
+ local $\ = '';
+ print $OUT "Couldn't evaluate `$i' alias: $@";
+ next CMD;
+ }
+ } ## end if ($alias{$i})
+
+=head3 MAIN-LINE COMMANDS
+
+All of these commands work up to and after the program being debugged has
+terminated.
+
+=head4 C<q> - quit
+
+Quit the debugger. This entails setting the C<$fall_off_end> flag, so we don't
+try to execute further, cleaning any restart-related stuff out of the
+environment, and executing with the last value of C<$?>.
+
+=cut
+
+ $cmd =~ /^q$/ && do {
+ $fall_off_end = 1;
+ clean_ENV();
+ exit $?;
+ };
+
+=head4 C<t> - trace
+
+Turn tracing on or off. Inverts the appropriate bit in C<$trace> (q.v.).
+
+=cut
+
+ $cmd =~ /^t$/ && do {
+ $trace ^= 1;
+ local $\ = '';
+ print $OUT "Trace = "
+ . ( ( $trace & 1 ) ? "on" : "off" ) . "\n";
+ next CMD;
+ };
+
+=head4 C<S> - list subroutines matching/not matching a pattern
+
+Walks through C<%sub>, checking to see whether or not to print the name.
+
+=cut
+
+ $cmd =~ /^S(\s+(!)?(.+))?$/ && do {
+
+ $Srev = defined $2; # Reverse scan?
+ $Spatt = $3; # The pattern (if any) to use.
+ $Snocheck = !defined $1; # No args - print all subs.
+
+ # Need to make these sane here.
+ local $\ = '';
+ local $, = '';
+
+ # Search through the debugger's magical hash of subs.
+ # If $nocheck is true, just print the sub name.
+ # Otherwise, check it against the pattern. We then use
+ # the XOR trick to reverse the condition as required.
+ foreach $subname ( sort( keys %sub ) ) {
+ if ( $Snocheck or $Srev ^ ( $subname =~ /$Spatt/ ) ) {
+ print $OUT $subname, "\n";
+ }
+ }
+ next CMD;
+ };
+
+=head4 C<X> - list variables in current package
+
+Since the C<V> command actually processes this, just change this to the
+appropriate C<V> command and fall through.
+
+=cut
+
+ $cmd =~ s/^X\b/V $package/;
+
+=head4 C<V> - list variables
+
+Uses C<dumpvar.pl> to dump out the current values for selected variables.
+
+=cut
+
+ # Bare V commands get the currently-being-debugged package
+ # added.
+ $cmd =~ /^V$/ && do {
+ $cmd = "V $package";
+ };
+
+ # V - show variables in package.
+ $cmd =~ /^V\b\s*(\S+)\s*(.*)/ && do {
+
+ # Save the currently selected filehandle and
+ # force output to debugger's filehandle (dumpvar
+ # just does "print" for output).
+ local ($savout) = select($OUT);
+
+ # Grab package name and variables to dump.
+ $packname = $1;
+ @vars = split( ' ', $2 );
+
+ # If main::dumpvar isn't here, get it.
+ do 'dumpvar.pl' unless defined &main::dumpvar;
+ if ( defined &main::dumpvar ) {
+
+ # We got it. Turn off subroutine entry/exit messages
+ # for the moment, along with return values.
+ local $frame = 0;
+ local $doret = -2;
+
+ # must detect sigpipe failures - not catching
+ # then will cause the debugger to die.
+ eval {
+ &main::dumpvar(
+ $packname,
+ defined $option{dumpDepth}
+ ? $option{dumpDepth}
+ : -1, # assume -1 unless specified
+ @vars
+ );
+ };
+
+ # The die doesn't need to include the $@, because
+ # it will automatically get propagated for us.
+ if ($@) {
+ die unless $@ =~ /dumpvar print failed/;
+ }
+ } ## end if (defined &main::dumpvar)
+ else {
+
+ # Couldn't load dumpvar.
+ print $OUT "dumpvar.pl not available.\n";
+ }
+
+ # Restore the output filehandle, and go round again.
+ select($savout);
+ next CMD;
+ };
+
+=head4 C<x> - evaluate and print an expression
+
+Hands the expression off to C<DB::eval>, setting it up to print the value
+via C<dumpvar.pl> instead of just printing it directly.
+
+=cut
+
+ $cmd =~ s/^x\b/ / && do { # Remainder gets done by DB::eval()
+ $onetimeDump = 'dump'; # main::dumpvar shows the output
+
+ # handle special "x 3 blah" syntax XXX propagate
+ # doc back to special variables.
+ if ( $cmd =~ s/^\s*(\d+)(?=\s)/ / ) {
+ $onetimedumpDepth = $1;
+ }
+ };
+
+=head4 C<m> - print methods
+
+Just uses C<DB::methods> to determine what methods are available.
+
+=cut
+
+ $cmd =~ s/^m\s+([\w:]+)\s*$/ / && do {
+ methods($1);
+ next CMD;
+ };
+
+ # m expr - set up DB::eval to do the work
+ $cmd =~ s/^m\b/ / && do { # Rest gets done by DB::eval()
+ $onetimeDump = 'methods'; # method output gets used there
+ };
+
+=head4 C<f> - switch files
+
+=cut
+
+ $cmd =~ /^f\b\s*(.*)/ && do {
+ $file = $1;
+ $file =~ s/\s+$//;
+
+ # help for no arguments (old-style was return from sub).
+ if ( !$file ) {
+ print $OUT
+ "The old f command is now the r command.\n"; # hint
+ print $OUT "The new f command switches filenames.\n";
+ next CMD;
+ } ## end if (!$file)
+
+ # if not in magic file list, try a close match.
+ if ( !defined $main::{ '_<' . $file } ) {
+ if ( ($try) = grep( m#^_<.*$file#, keys %main:: ) ) {
+ {
+ $try = substr( $try, 2 );
+ print $OUT "Choosing $try matching `$file':\n";
+ $file = $try;
+ }
+ } ## end if (($try) = grep(m#^_<.*$file#...
+ } ## end if (!defined $main::{ ...
+
+ # If not successfully switched now, we failed.
+ if ( !defined $main::{ '_<' . $file } ) {
+ print $OUT "No file matching `$file' is loaded.\n";
+ next CMD;
+ }
+
+ # We switched, so switch the debugger internals around.
+ elsif ( $file ne $filename ) {
+ *dbline = $main::{ '_<' . $file };
+ $max = $#dbline;
+ $filename = $file;
+ $start = 1;
+ $cmd = "l";
+ } ## end elsif ($file ne $filename)
+
+ # We didn't switch; say we didn't.
+ else {
+ print $OUT "Already in $file.\n";
+ next CMD;
+ }
+ };
+
+=head4 C<.> - return to last-executed line.
+
+We set C<$incr> to -1 to indicate that the debugger shouldn't move ahead,
+and then we look up the line in the magical C<%dbline> hash.
+
+=cut
+
+ # . command.
+ $cmd =~ /^\.$/ && do {
+ $incr = -1; # stay at current line
+
+ # Reset everything to the old location.
+ $start = $line;
+ $filename = $filename_ini;
+ *dbline = $main::{ '_<' . $filename };
+ $max = $#dbline;
+
+ # Now where are we?
+ print_lineinfo($position);
+ next CMD;
+ };
+
+=head4 C<-> - back one window
+
+We change C<$start> to be one window back; if we go back past the first line,
+we set it to be the first line. We ser C<$incr> to put us back at the
+currently-executing line, and then put a C<l $start +> (list one window from
+C<$start>) in C<$cmd> to be executed later.
+
+=cut
+
+ # - - back a window.
+ $cmd =~ /^-$/ && do {
+
+ # back up by a window; go to 1 if back too far.
+ $start -= $incr + $window + 1;
+ $start = 1 if $start <= 0;
+ $incr = $window - 1;
+
+ # Generate and execute a "l +" command (handled below).
+ $cmd = 'l ' . ($start) . '+';
+ };
+
+=head3 PRE-580 COMMANDS VS. NEW COMMANDS: C<a, A, b, B, h, l, L, M, o, O, P, v, w, W, E<lt>, E<lt>E<lt>, {, {{>
+
+In Perl 5.8.0, a realignment of the commands was done to fix up a number of
+problems, most notably that the default case of several commands destroying
+the user's work in setting watchpoints, actions, etc. We wanted, however, to
+retain the old commands for those who were used to using them or who preferred
+them. At this point, we check for the new commands and call C<cmd_wrapper> to
+deal with them instead of processing them in-line.
+
+=cut
+
+ # All of these commands were remapped in perl 5.8.0;
+ # we send them off to the secondary dispatcher (see below).
+ $cmd =~ /^([aAbBeEhilLMoOPvwW]\b|[<>\{]{1,2})\s*(.*)/so && do {
+ &cmd_wrapper( $1, $2, $line );
+ next CMD;
+ };
+
+=head4 C<y> - List lexicals in higher scope
+
+Uses C<PadWalker> to find the lexicals supplied as arguments in a scope
+above the current one and then displays then using C<dumpvar.pl>.
+
+=cut
+
+ $cmd =~ /^y(?:\s+(\d*)\s*(.*))?$/ && do {
+
+ # See if we've got the necessary support.
+ eval { require PadWalker; PadWalker->VERSION(0.08) }
+ or &warn(
+ $@ =~ /locate/
+ ? "PadWalker module not found - please install\n"
+ : $@
+ )
+ and next CMD;
+
+ # Load up dumpvar if we don't have it. If we can, that is.
+ do 'dumpvar.pl' unless defined &main::dumpvar;
+ defined &main::dumpvar
+ or print $OUT "dumpvar.pl not available.\n"
+ and next CMD;
+
+ # Got all the modules we need. Find them and print them.
+ my @vars = split( ' ', $2 || '' );
+
+ # Find the pad.
+ my $h = eval { PadWalker::peek_my( ( $1 || 0 ) + 1 ) };
+
+ # Oops. Can't find it.
+ $@ and $@ =~ s/ at .*//, &warn($@), next CMD;
+
+ # Show the desired vars with dumplex().
+ my $savout = select($OUT);
+
+ # Have dumplex dump the lexicals.
+ dumpvar::dumplex( $_, $h->{$_},
+ defined $option{dumpDepth} ? $option{dumpDepth} : -1,
+ @vars )
+ for sort keys %$h;
+ select($savout);
+ next CMD;
+ };
+
+=head3 COMMANDS NOT WORKING AFTER PROGRAM ENDS
+
+All of the commands below this point don't work after the program being
+debugged has ended. All of them check to see if the program has ended; this
+allows the commands to be relocated without worrying about a 'line of
+demarcation' above which commands can be entered anytime, and below which
+they can't.
+
+=head4 C<n> - single step, but don't trace down into subs
+
+Done by setting C<$single> to 2, which forces subs to execute straight through
+when entered (see C<DB::sub>). We also save the C<n> command in C<$laststep>,
+so a null command knows what to re-execute.
+
+=cut
+
+ # n - next
+ $cmd =~ /^n$/ && do {
+ end_report(), next CMD if $finished and $level <= 1;
+
+ # Single step, but don't enter subs.
+ $single = 2;
+
+ # Save for empty command (repeat last).
+ $laststep = $cmd;
+ last CMD;
+ };
+
+=head4 C<s> - single-step, entering subs
+
+Sets C<$single> to 1, which causes C<DB::sub> to continue tracing inside
+subs. Also saves C<s> as C<$lastcmd>.
+
+=cut
+
+ # s - single step.
+ $cmd =~ /^s$/ && do {
+
+ # Get out and restart the command loop if program
+ # has finished.
+ end_report(), next CMD if $finished and $level <= 1;
+
+ # Single step should enter subs.
+ $single = 1;
+
+ # Save for empty command (repeat last).
+ $laststep = $cmd;
+ last CMD;
+ };
+
+=head4 C<c> - run continuously, setting an optional breakpoint
+
+Most of the code for this command is taken up with locating the optional
+breakpoint, which is either a subroutine name or a line number. We set
+the appropriate one-time-break in C<@dbline> and then turn off single-stepping
+in this and all call levels above this one.
+
+=cut
+
+ # c - start continuous execution.
+ $cmd =~ /^c\b\s*([\w:]*)\s*$/ && do {
+
+ # Hey, show's over. The debugged program finished
+ # executing already.
+ end_report(), next CMD if $finished and $level <= 1;
+
+ # Capture the place to put a one-time break.
+ $subname = $i = $1;
+
+ # Probably not needed, since we finish an interactive
+ # sub-session anyway...
+ # local $filename = $filename;
+ # local *dbline = *dbline; # XXX Would this work?!
+ #
+ # The above question wonders if localizing the alias
+ # to the magic array works or not. Since it's commented
+ # out, we'll just leave that to speculation for now.
+
+ # If the "subname" isn't all digits, we'll assume it
+ # is a subroutine name, and try to find it.
+ if ( $subname =~ /\D/ ) { # subroutine name
+ # Qualify it to the current package unless it's
+ # already qualified.
+ $subname = $package . "::" . $subname
+ unless $subname =~ /::/;
+
+ # find_sub will return "file:line_number" corresponding
+ # to where the subroutine is defined; we call find_sub,
+ # break up the return value, and assign it in one
+ # operation.
+ ( $file, $i ) = ( find_sub($subname) =~ /^(.*):(.*)$/ );
+
+ # Force the line number to be numeric.
+ $i += 0;
+
+ # If we got a line number, we found the sub.
+ if ($i) {
+
+ # Switch all the debugger's internals around so
+ # we're actually working with that file.
+ $filename = $file;
+ *dbline = $main::{ '_<' . $filename };
+
+ # Mark that there's a breakpoint in this file.
+ $had_breakpoints{$filename} |= 1;
+
+ # Scan forward to the first executable line
+ # after the 'sub whatever' line.
+ $max = $#dbline;
+ ++$i while $dbline[$i] == 0 && $i < $max;
+ } ## end if ($i)
+
+ # We didn't find a sub by that name.
+ else {
+ print $OUT "Subroutine $subname not found.\n";
+ next CMD;
+ }
+ } ## end if ($subname =~ /\D/)
+
+ # At this point, either the subname was all digits (an
+ # absolute line-break request) or we've scanned through
+ # the code following the definition of the sub, looking
+ # for an executable, which we may or may not have found.
+ #
+ # If $i (which we set $subname from) is non-zero, we
+ # got a request to break at some line somewhere. On
+ # one hand, if there wasn't any real subroutine name
+ # involved, this will be a request to break in the current
+ # file at the specified line, so we have to check to make
+ # sure that the line specified really is breakable.
+ #
+ # On the other hand, if there was a subname supplied, the
+ # preceding block has moved us to the proper file and
+ # location within that file, and then scanned forward
+ # looking for the next executable line. We have to make
+ # sure that one was found.
+ #
+ # On the gripping hand, we can't do anything unless the
+ # current value of $i points to a valid breakable line.
+ # Check that.
+ if ($i) {
+
+ # Breakable?
+ if ( $dbline[$i] == 0 ) {
+ print $OUT "Line $i not breakable.\n";
+ next CMD;
+ }
+
+ # Yes. Set up the one-time-break sigil.
+ $dbline{$i} =~ s/($|\0)/;9$1/; # add one-time-only b.p.
+ } ## end if ($i)
+
+ # Turn off stack tracing from here up.
+ for ( $i = 0 ; $i <= $stack_depth ; ) {
+ $stack[ $i++ ] &= ~1;
+ }
+ last CMD;
+ };
+
+=head4 C<r> - return from a subroutine
+
+For C<r> to work properly, the debugger has to stop execution again
+immediately after the return is executed. This is done by forcing
+single-stepping to be on in the call level above the current one. If
+we are printing return values when a C<r> is executed, set C<$doret>
+appropriately, and force us out of the command loop.
+
+=cut
+
+ # r - return from the current subroutine.
+ $cmd =~ /^r$/ && do {
+
+ # Can't do anythign if the program's over.
+ end_report(), next CMD if $finished and $level <= 1;
+
+ # Turn on stack trace.
+ $stack[$stack_depth] |= 1;
+
+ # Print return value unless the stack is empty.
+ $doret = $option{PrintRet} ? $stack_depth - 1 : -2;
+ last CMD;
+ };
+
+=head4 C<T> - stack trace
+
+Just calls C<DB::print_trace>.
+
+=cut
+
+ $cmd =~ /^T$/ && do {
+ print_trace( $OUT, 1 ); # skip DB
+ next CMD;
+ };
+
+=head4 C<w> - List window around current line.
+
+Just calls C<DB::cmd_w>.
+
+=cut
+
+ $cmd =~ /^w\b\s*(.*)/s && do { &cmd_w( 'w', $1 ); next CMD; };
+
+=head4 C<W> - watch-expression processing.
+
+Just calls C<DB::cmd_W>.
+
+=cut
+
+ $cmd =~ /^W\b\s*(.*)/s && do { &cmd_W( 'W', $1 ); next CMD; };
+
+=head4 C</> - search forward for a string in the source
+
+We take the argument and treat it as a pattern. If it turns out to be a
+bad one, we return the error we got from trying to C<eval> it and exit.
+If not, we create some code to do the search and C<eval> it so it can't
+mess us up.
+
+=cut
+
+ $cmd =~ /^\/(.*)$/ && do {
+
+ # The pattern as a string.
+ $inpat = $1;
+
+ # Remove the final slash.
+ $inpat =~ s:([^\\])/$:$1:;
+
+ # If the pattern isn't null ...
+ if ( $inpat ne "" ) {
+
+ # Turn of warn and die procesing for a bit.
+ local $SIG{__DIE__};
+ local $SIG{__WARN__};
+
+ # Create the pattern.
+ eval '$inpat =~ m' . "\a$inpat\a";
+ if ( $@ ne "" ) {
+
+ # Oops. Bad pattern. No biscuit.
+ # Print the eval error and go back for more
+ # commands.
+ print $OUT "$@";
+ next CMD;
+ }
+ $pat = $inpat;
+ } ## end if ($inpat ne "")
+
+ # Set up to stop on wrap-around.
+ $end = $start;
+
+ # Don't move off the current line.
+ $incr = -1;
+
+ # Done in eval so nothing breaks if the pattern
+ # does something weird.
+ eval '
+ for (;;) {
+ # Move ahead one line.
+ ++$start;
+
+ # Wrap if we pass the last line.
+ $start = 1 if ($start > $max);
+
+ # Stop if we have gotten back to this line again,
+ last if ($start == $end);
+
+ # A hit! (Note, though, that we are doing
+ # case-insensitive matching. Maybe a qr//
+ # expression would be better, so the user could
+ # do case-sensitive matching if desired.
+ if ($dbline[$start] =~ m' . "\a$pat\a" . 'i) {
+ if ($slave_editor) {
+ # Handle proper escaping in the slave.
+ print $OUT "\032\032$filename:$start:0\n";
+ }
+ else {
+ # Just print the line normally.
+ print $OUT "$start:\t",$dbline[$start],"\n";
+ }
+ # And quit since we found something.
+ last;
+ }
+ } ';
+
+ # If we wrapped, there never was a match.
+ print $OUT "/$pat/: not found\n" if ( $start == $end );
+ next CMD;
+ };
+
+=head4 C<?> - search backward for a string in the source
+
+Same as for C</>, except the loop runs backwards.
+
+=cut
+
+ # ? - backward pattern search.
+ $cmd =~ /^\?(.*)$/ && do {
+
+ # Get the pattern, remove trailing question mark.
+ $inpat = $1;
+ $inpat =~ s:([^\\])\?$:$1:;
+
+ # If we've got one ...
+ if ( $inpat ne "" ) {
+
+ # Turn off die & warn handlers.
+ local $SIG{__DIE__};
+ local $SIG{__WARN__};
+ eval '$inpat =~ m' . "\a$inpat\a";
+
+ if ( $@ ne "" ) {
+
+ # Ouch. Not good. Print the error.
+ print $OUT $@;
+ next CMD;
+ }
+ $pat = $inpat;
+ } ## end if ($inpat ne "")
+
+ # Where we are now is where to stop after wraparound.
+ $end = $start;
+
+ # Don't move away from this line.
+ $incr = -1;
+
+ # Search inside the eval to prevent pattern badness
+ # from killing us.
+ eval '
+ for (;;) {
+ # Back up a line.
+ --$start;
+
+ # Wrap if we pass the first line.
+
+ $start = $max if ($start <= 0);
+
+ # Quit if we get back where we started,
+ last if ($start == $end);
+
+ # Match?
+ if ($dbline[$start] =~ m' . "\a$pat\a" . 'i) {
+ if ($slave_editor) {
+ # Yep, follow slave editor requirements.
+ print $OUT "\032\032$filename:$start:0\n";
+ }
+ else {
+ # Yep, just print normally.
+ print $OUT "$start:\t",$dbline[$start],"\n";
+ }
+
+ # Found, so done.
+ last;
+ }
+ } ';
+
+ # Say we failed if the loop never found anything,
+ print $OUT "?$pat?: not found\n" if ( $start == $end );
+ next CMD;
+ };
+
+=head4 C<$rc> - Recall command
+
+Manages the commands in C<@hist> (which is created if C<Term::ReadLine> reports
+that the terminal supports history). It find the the command required, puts it
+into C<$cmd>, and redoes the loop to execute it.
+
+=cut
+
+ # $rc - recall command.
+ $cmd =~ /^$rc+\s*(-)?(\d+)?$/ && do {
+
+ # No arguments, take one thing off history.
+ pop(@hist) if length($cmd) > 1;
+
+ # Relative (- found)?
+ # Y - index back from most recent (by 1 if bare minus)
+ # N - go to that particular command slot or the last
+ # thing if nothing following.
+ $i = $1 ? ( $#hist - ( $2 || 1 ) ) : ( $2 || $#hist );
+
+ # Pick out the command desired.
+ $cmd = $hist[$i];
+
+ # Print the command to be executed and restart the loop
+ # with that command in the buffer.
+ print $OUT $cmd, "\n";
+ redo CMD;
+ };
+
+=head4 C<$sh$sh> - C<system()> command
+
+Calls the C<DB::system()> to handle the command. This keeps the C<STDIN> and
+C<STDOUT> from getting messed up.
+
+=cut
+
+ # $sh$sh - run a shell command (if it's all ASCII).
+ # Can't run shell commands with Unicode in the debugger, hmm.
+ $cmd =~ /^$sh$sh\s*([\x00-\xff]*)/ && do {
+
+ # System it.
+ &system($1);
+ next CMD;
+ };
+
+=head4 C<$rc I<pattern> $rc> - Search command history
+
+Another command to manipulate C<@hist>: this one searches it with a pattern.
+If a command is found, it is placed in C<$cmd> and executed via C<redo>.
+
+=cut
+
+ # $rc pattern $rc - find a command in the history.
+ $cmd =~ /^$rc([^$rc].*)$/ && do {
+
+ # Create the pattern to use.
+ $pat = "^$1";
+
+ # Toss off last entry if length is >1 (and it always is).
+ pop(@hist) if length($cmd) > 1;
+
+ # Look backward through the history.
+ for ( $i = $#hist ; $i ; --$i ) {
+
+ # Stop if we find it.
+ last if $hist[$i] =~ /$pat/;
+ }
+
+ if ( !$i ) {
+
+ # Never found it.
+ print $OUT "No such command!\n\n";
+ next CMD;
+ }
+
+ # Found it. Put it in the buffer, print it, and process it.
+ $cmd = $hist[$i];
+ print $OUT $cmd, "\n";
+ redo CMD;
+ };
+
+=head4 C<$sh> - Invoke a shell
+
+Uses C<DB::system> to invoke a shell.
+
+=cut
+
+ # $sh - start a shell.
+ $cmd =~ /^$sh$/ && do {
+
+ # Run the user's shell. If none defined, run Bourne.
+ # We resume execution when the shell terminates.
+ &system( $ENV{SHELL} || "/bin/sh" );
+ next CMD;
+ };
+
+=head4 C<$sh I<command>> - Force execution of a command in a shell
+
+Like the above, but the command is passed to the shell. Again, we use
+C<DB::system> to avoid problems with C<STDIN> and C<STDOUT>.
+
+=cut
+
+ # $sh command - start a shell and run a command in it.
+ $cmd =~ /^$sh\s*([\x00-\xff]*)/ && do {
+
+ # XXX: using csh or tcsh destroys sigint retvals!
+ #&system($1); # use this instead
+
+ # use the user's shell, or Bourne if none defined.
+ &system( $ENV{SHELL} || "/bin/sh", "-c", $1 );
+ next CMD;
+ };
+
+=head4 C<H> - display commands in history
+
+Prints the contents of C<@hist> (if any).
+
+=cut
+
+ $cmd =~ /^H\b\s*\*/ && do {
+ @hist = @truehist = ();
+ print $OUT "History cleansed\n";
+ next CMD;
+ };
+
+ $cmd =~ /^H\b\s*(-(\d+))?/ && do {
+
+ # Anything other than negative numbers is ignored by
+ # the (incorrect) pattern, so this test does nothing.
+ $end = $2 ? ( $#hist - $2 ) : 0;
+
+ # Set to the minimum if less than zero.
+ $hist = 0 if $hist < 0;
+
+ # Start at the end of the array.
+ # Stay in while we're still above the ending value.
+ # Tick back by one each time around the loop.
+ for ( $i = $#hist ; $i > $end ; $i-- ) {
+
+ # Print the command unless it has no arguments.
+ print $OUT "$i: ", $hist[$i], "\n"
+ unless $hist[$i] =~ /^.?$/;
+ }
+ next CMD;
+ };
+
+=head4 C<man, doc, perldoc> - look up documentation
+
+Just calls C<runman()> to print the appropriate document.
+
+=cut
+
+ # man, perldoc, doc - show manual pages.
+ $cmd =~ /^(?:man|(?:perl)?doc)\b(?:\s+([^(]*))?$/ && do {
+ runman($1);
+ next CMD;
+ };
+
+=head4 C<p> - print
+
+Builds a C<print EXPR> expression in the C<$cmd>; this will get executed at
+the bottom of the loop.
+
+=cut
+
+ # p - print (no args): print $_.
+ $cmd =~ s/^p$/print {\$DB::OUT} \$_/;
+
+ # p - print the given expression.
+ $cmd =~ s/^p\b/print {\$DB::OUT} /;
+
+=head4 C<=> - define command alias
+
+Manipulates C<%alias> to add or list command aliases.
+
+=cut
+
+ # = - set up a command alias.
+ $cmd =~ s/^=\s*// && do {
+ my @keys;
+ if ( length $cmd == 0 ) {
+
+ # No args, get current aliases.
+ @keys = sort keys %alias;
+ }
+ elsif ( my ( $k, $v ) = ( $cmd =~ /^(\S+)\s+(\S.*)/ ) ) {
+
+ # Creating a new alias. $k is alias name, $v is
+ # alias value.
+
+ # can't use $_ or kill //g state
+ for my $x ( $k, $v ) {
+
+ # Escape "alarm" characters.
+ $x =~ s/\a/\\a/g;
+ }
+
+ # Substitute key for value, using alarm chars
+ # as separators (which is why we escaped them in
+ # the command).
+ $alias{$k} = "s\a$k\a$v\a";
+
+ # Turn off standard warn and die behavior.
+ local $SIG{__DIE__};
+ local $SIG{__WARN__};
+
+ # Is it valid Perl?
+ unless ( eval "sub { s\a$k\a$v\a }; 1" ) {
+
+ # Nope. Bad alias. Say so and get out.
+ print $OUT "Can't alias $k to $v: $@\n";
+ delete $alias{$k};
+ next CMD;
+ }
+
+ # We'll only list the new one.
+ @keys = ($k);
+ } ## end elsif (my ($k, $v) = ($cmd...
+
+ # The argument is the alias to list.
+ else {
+ @keys = ($cmd);
+ }
+
+ # List aliases.
+ for my $k (@keys) {
+
+ # Messy metaquoting: Trim the substiution code off.
+ # We use control-G as the delimiter because it's not
+ # likely to appear in the alias.
+ if ( ( my $v = $alias{$k} ) =~ ss\a$k\a(.*)\a$1 ) {
+
+ # Print the alias.
+ print $OUT "$k\t= $1\n";
+ }
+ elsif ( defined $alias{$k} ) {
+
+ # Couldn't trim it off; just print the alias code.
+ print $OUT "$k\t$alias{$k}\n";
+ }
+ else {
+
+ # No such, dude.
+ print "No alias for $k\n";
+ }
+ } ## end for my $k (@keys)
+ next CMD;
+ };
+
+=head4 C<source> - read commands from a file.
+
+Opens a lexical filehandle and stacks it on C<@cmdfhs>; C<DB::readline> will
+pick it up.
+
+=cut
+
+ # source - read commands from a file (or pipe!) and execute.
+ $cmd =~ /^source\s+(.*\S)/ && do {
+ if ( open my $fh, $1 ) {
+
+ # Opened OK; stick it in the list of file handles.
+ push @cmdfhs, $fh;
+ }
+ else {
+
+ # Couldn't open it.
+ &warn("Can't execute `$1': $!\n");
+ }
+ next CMD;
+ };
+
+=head4 C<save> - send current history to a file
+
+Takes the complete history, (not the shrunken version you see with C<H>),
+and saves it to the given filename, so it can be replayed using C<source>.
+
+Note that all C<^(save|source)>'s are commented out with a view to minimise recursion.
+
+=cut
+
+ # save source - write commands to a file for later use
+ $cmd =~ /^save\s*(.*)$/ && do {
+ my $file = $1 || '.perl5dbrc'; # default?
+ if ( open my $fh, "> $file" ) {
+
+ # chomp to remove extraneous newlines from source'd files
+ chomp( my @truelist =
+ map { m/^\s*(save|source)/ ? "#$_" : $_ }
+ @truehist );
+ print $fh join( "\n", @truelist );
+ print "commands saved in $file\n";
+ }
+ else {
+ &warn("Can't save debugger commands in '$1': $!\n");
+ }
+ next CMD;
+ };
+
+=head4 C<R> - restart
+
+Restart the debugger session.
+
+=head4 C<rerun> - rerun the current session
+
+Return to any given position in the B<true>-history list
+
+=cut
+
+ # R - restart execution.
+ # rerun - controlled restart execution.
+ $cmd =~ /^(R|rerun\s*(.*))$/ && do {
+ my @args = ($1 eq 'R' ? restart() : rerun($2));
+
+ # Close all non-system fds for a clean restart. A more
+ # correct method would be to close all fds that were not
+ # open when the process started, but this seems to be
+ # hard. See "debugger 'R'estart and open database
+ # connections" on p5p.
+
+ my $max_fd = 1024; # default if POSIX can't be loaded
+ if (eval { require POSIX }) {
+ $max_fd = POSIX::sysconf(POSIX::_SC_OPEN_MAX());
+ }
+
+ if (defined $max_fd) {
+ foreach ($^F+1 .. $max_fd-1) {
+ next unless open FD_TO_CLOSE, "<&=$_";
+ close(FD_TO_CLOSE);
+ }
+ }
+
+ # And run Perl again. We use exec() to keep the
+ # PID stable (and that way $ini_pids is still valid).
+ exec(@args) || print $OUT "exec failed: $!\n";
+
+ last CMD;
+ };
+
+=head4 C<|, ||> - pipe output through the pager.
+
+For C<|>, we save C<OUT> (the debugger's output filehandle) and C<STDOUT>
+(the program's standard output). For C<||>, we only save C<OUT>. We open a
+pipe to the pager (restoring the output filehandles if this fails). If this
+is the C<|> command, we also set up a C<SIGPIPE> handler which will simply
+set C<$signal>, sending us back into the debugger.
+
+We then trim off the pipe symbols and C<redo> the command loop at the
+C<PIPE> label, causing us to evaluate the command in C<$cmd> without
+reading another.
+
+=cut
+
+ # || - run command in the pager, with output to DB::OUT.
+ $cmd =~ /^\|\|?\s*[^|]/ && do {
+ if ( $pager =~ /^\|/ ) {
+
+ # Default pager is into a pipe. Redirect I/O.
+ open( SAVEOUT, ">&STDOUT" )
+ || &warn("Can't save STDOUT");
+ open( STDOUT, ">&OUT" )
+ || &warn("Can't redirect STDOUT");
+ } ## end if ($pager =~ /^\|/)
+ else {
+
+ # Not into a pipe. STDOUT is safe.
+ open( SAVEOUT, ">&OUT" ) || &warn("Can't save DB::OUT");
+ }
+
+ # Fix up environment to record we have less if so.
+ fix_less();
+
+ unless ( $piped = open( OUT, $pager ) ) {
+
+ # Couldn't open pipe to pager.
+ &warn("Can't pipe output to `$pager'");
+ if ( $pager =~ /^\|/ ) {
+
+ # Redirect I/O back again.
+ open( OUT, ">&STDOUT" ) # XXX: lost message
+ || &warn("Can't restore DB::OUT");
+ open( STDOUT, ">&SAVEOUT" )
+ || &warn("Can't restore STDOUT");
+ close(SAVEOUT);
+ } ## end if ($pager =~ /^\|/)
+ else {
+
+ # Redirect I/O. STDOUT already safe.
+ open( OUT, ">&STDOUT" ) # XXX: lost message
+ || &warn("Can't restore DB::OUT");
+ }
+ next CMD;
+ } ## end unless ($piped = open(OUT,...
+
+ # Set up broken-pipe handler if necessary.
+ $SIG{PIPE} = \&DB::catch
+ if $pager =~ /^\|/
+ && ( "" eq $SIG{PIPE} || "DEFAULT" eq $SIG{PIPE} );
+
+ # Save current filehandle, unbuffer out, and put it back.
+ $selected = select(OUT);
+ $| = 1;
+
+ # Don't put it back if pager was a pipe.
+ select($selected), $selected = "" unless $cmd =~ /^\|\|/;
+
+ # Trim off the pipe symbols and run the command now.
+ $cmd =~ s/^\|+\s*//;
+ redo PIPE;
+ };
+
+=head3 END OF COMMAND PARSING
+
+Anything left in C<$cmd> at this point is a Perl expression that we want to
+evaluate. We'll always evaluate in the user's context, and fully qualify
+any variables we might want to address in the C<DB> package.
+
+=cut
+
+ # t - turn trace on.
+ $cmd =~ s/^t\s/\$DB::trace |= 1;\n/;
+
+ # s - single-step. Remember the last command was 's'.
+ $cmd =~ s/^s\s/\$DB::single = 1;\n/ && do { $laststep = 's' };
+
+ # n - single-step, but not into subs. Remember last command
+ # was 'n'.
+ $cmd =~ s/^n\s/\$DB::single = 2;\n/ && do { $laststep = 'n' };
+
+ } # PIPE:
+
+ # Make sure the flag that says "the debugger's running" is
+ # still on, to make sure we get control again.
+ $evalarg = "\$^D = \$^D | \$DB::db_stop;\n$cmd";
+
+ # Run *our* eval that executes in the caller's context.
+ &eval;
+
+ # Turn off the one-time-dump stuff now.
+ if ($onetimeDump) {
+ $onetimeDump = undef;
+ $onetimedumpDepth = undef;
+ }
+ elsif ( $term_pid == $$ ) {
+ STDOUT->flush();
+ STDERR->flush();
+
+ # XXX If this is the master pid, print a newline.
+ print $OUT "\n";
+ }
+ } ## end while (($term || &setterm...
+
+=head3 POST-COMMAND PROCESSING
+
+After each command, we check to see if the command output was piped anywhere.
+If so, we go through the necessary code to unhook the pipe and go back to
+our standard filehandles for input and output.
+
+=cut
+
+ continue { # CMD:
+
+ # At the end of every command:
+ if ($piped) {
+
+ # Unhook the pipe mechanism now.
+ if ( $pager =~ /^\|/ ) {
+
+ # No error from the child.
+ $? = 0;
+
+ # we cannot warn here: the handle is missing --tchrist
+ close(OUT) || print SAVEOUT "\nCan't close DB::OUT\n";
+
+ # most of the $? crud was coping with broken cshisms
+ # $? is explicitly set to 0, so this never runs.
+ if ($?) {
+ print SAVEOUT "Pager `$pager' failed: ";
+ if ( $? == -1 ) {
+ print SAVEOUT "shell returned -1\n";
+ }
+ elsif ( $? >> 8 ) {
+ print SAVEOUT ( $? & 127 )
+ ? " (SIG#" . ( $? & 127 ) . ")"
+ : "", ( $? & 128 ) ? " -- core dumped" : "", "\n";
+ }
+ else {
+ print SAVEOUT "status ", ( $? >> 8 ), "\n";
+ }
+ } ## end if ($?)
+
+ # Reopen filehandle for our output (if we can) and
+ # restore STDOUT (if we can).
+ open( OUT, ">&STDOUT" ) || &warn("Can't restore DB::OUT");
+ open( STDOUT, ">&SAVEOUT" )
+ || &warn("Can't restore STDOUT");
+
+ # Turn off pipe exception handler if necessary.
+ $SIG{PIPE} = "DEFAULT" if $SIG{PIPE} eq \&DB::catch;
+
+ # Will stop ignoring SIGPIPE if done like nohup(1)
+ # does SIGINT but Perl doesn't give us a choice.
+ } ## end if ($pager =~ /^\|/)
+ else {
+
+ # Non-piped "pager". Just restore STDOUT.
+ open( OUT, ">&SAVEOUT" ) || &warn("Can't restore DB::OUT");
+ }
+
+ # Close filehandle pager was using, restore the normal one
+ # if necessary,
+ close(SAVEOUT);
+ select($selected), $selected = "" unless $selected eq "";
+
+ # No pipes now.
+ $piped = "";
+ } ## end if ($piped)
+ } # CMD:
+
+=head3 COMMAND LOOP TERMINATION
+
+When commands have finished executing, we come here. If the user closed the
+input filehandle, we turn on C<$fall_off_end> to emulate a C<q> command. We
+evaluate any post-prompt items. We restore C<$@>, C<$!>, C<$^E>, C<$,>, C<$/>,
+C<$\>, and C<$^W>, and return a null list as expected by the Perl interpreter.
+The interpreter will then execute the next line and then return control to us
+again.
+
+=cut
+
+ # No more commands? Quit.
+ $fall_off_end = 1 unless defined $cmd; # Emulate `q' on EOF
+
+ # Evaluate post-prompt commands.
+ foreach $evalarg (@$post) {
+ &eval;
+ }
+ } # if ($single || $signal)
+
+ # Put the user's globals back where you found them.
+ ( $@, $!, $^E, $,, $/, $\, $^W ) = @saved;
+ ();
+} ## end sub DB
+
+# The following code may be executed now:
+# BEGIN {warn 4}
+
+=head2 sub
+
+C<sub> is called whenever a subroutine call happens in the program being
+debugged. The variable C<$DB::sub> contains the name of the subroutine
+being called.
+
+The core function of this subroutine is to actually call the sub in the proper
+context, capturing its output. This of course causes C<DB::DB> to get called
+again, repeating until the subroutine ends and returns control to C<DB::sub>
+again. Once control returns, C<DB::sub> figures out whether or not to dump the
+return value, and returns its captured copy of the return value as its own
+return value. The value then feeds back into the program being debugged as if
+C<DB::sub> hadn't been there at all.
+
+C<sub> does all the work of printing the subroutine entry and exit messages
+enabled by setting C<$frame>. It notes what sub the autoloader got called for,
+and also prints the return value if needed (for the C<r> command and if
+the 16 bit is set in C<$frame>).
+
+It also tracks the subroutine call depth by saving the current setting of
+C<$single> in the C<@stack> package global; if this exceeds the value in
+C<$deep>, C<sub> automatically turns on printing of the current depth by
+setting the C<4> bit in C<$single>. In any case, it keeps the current setting
+of stop/don't stop on entry to subs set as it currently is set.
+
+=head3 C<caller()> support
+
+If C<caller()> is called from the package C<DB>, it provides some
+additional data, in the following order:
+
+=over 4
+
+=item * C<$package>
+
+The package name the sub was in
+
+=item * C<$filename>
+
+The filename it was defined in
+
+=item * C<$line>
+
+The line number it was defined on
+
+=item * C<$subroutine>
+
+The subroutine name; C<(eval)> if an C<eval>().
+
+=item * C<$hasargs>
+
+1 if it has arguments, 0 if not
+
+=item * C<$wantarray>
+
+1 if array context, 0 if scalar context
+
+=item * C<$evaltext>
+
+The C<eval>() text, if any (undefined for C<eval BLOCK>)
+
+=item * C<$is_require>
+
+frame was created by a C<use> or C<require> statement
+
+=item * C<$hints>
+
+pragma information; subject to change between versions
+
+=item * C<$bitmask>
+
+pragma information; subject to change between versions
+
+=item * C<@DB::args>
+
+arguments with which the subroutine was invoked
+
+=back
+
+=cut
+
+sub sub {
+
+ # lock ourselves under threads
+ lock($DBGR);
+
+ # Whether or not the autoloader was running, a scalar to put the
+ # sub's return value in (if needed), and an array to put the sub's
+ # return value in (if needed).
+ my ( $al, $ret, @ret ) = "";
+ if ($sub =~ /^threads::new$/ && $ENV{PERL5DB_THREADED}) {
+ print "creating new thread\n";
+ }
+
+ # If the last ten characters are C'::AUTOLOAD', note we've traced
+ # into AUTOLOAD for $sub.
+ if ( length($sub) > 10 && substr( $sub, -10, 10 ) eq '::AUTOLOAD' ) {
+ $al = " for $$sub";
+ }
+
+ # We stack the stack pointer and then increment it to protect us
+ # from a situation that might unwind a whole bunch of call frames
+ # at once. Localizing the stack pointer means that it will automatically
+ # unwind the same amount when multiple stack frames are unwound.
+ local $stack_depth = $stack_depth + 1; # Protect from non-local exits
+
+ # Expand @stack.
+ $#stack = $stack_depth;
+
+ # Save current single-step setting.
+ $stack[-1] = $single;
+
+ # Turn off all flags except single-stepping.
+ $single &= 1;
+
+ # If we've gotten really deeply recursed, turn on the flag that will
+ # make us stop with the 'deep recursion' message.
+ $single |= 4 if $stack_depth == $deep;
+
+ # If frame messages are on ...
+ (
+ $frame & 4 # Extended frame entry message
+ ? (
+ print_lineinfo( ' ' x ( $stack_depth - 1 ), "in " ),
+
+ # Why -1? But it works! :-(
+ # Because print_trace will call add 1 to it and then call
+ # dump_trace; this results in our skipping -1+1 = 0 stack frames
+ # in dump_trace.
+ print_trace( $LINEINFO, -1, 1, 1, "$sub$al" )
+ )
+ : print_lineinfo( ' ' x ( $stack_depth - 1 ), "entering $sub$al\n" )
+
+ # standard frame entry message
+ )
+ if $frame;
+
+ # Determine the sub's return type,and capture approppriately.
+ if (wantarray) {
+
+ # Called in array context. call sub and capture output.
+ # DB::DB will recursively get control again if appropriate; we'll come
+ # back here when the sub is finished.
+ if ($assertion) {
+ $assertion = 0;
+ eval { @ret = &$sub; };
+ if ($@) {
+ print $OUT $@;
+ $signal = 1 unless $warnassertions;
+ }
+ }
+ else {
+ @ret = &$sub;
+ }
+
+ # Pop the single-step value back off the stack.
+ $single |= $stack[ $stack_depth-- ];
+
+ # Check for exit trace messages...
+ (
+ $frame & 4 # Extended exit message
+ ? (
+ print_lineinfo( ' ' x $stack_depth, "out " ),
+ print_trace( $LINEINFO, -1, 1, 1, "$sub$al" )
+ )
+ : print_lineinfo( ' ' x $stack_depth, "exited $sub$al\n" )
+
+ # Standard exit message
+ )
+ if $frame & 2;
+
+ # Print the return info if we need to.
+ if ( $doret eq $stack_depth or $frame & 16 ) {
+
+ # Turn off output record separator.
+ local $\ = '';
+ my $fh = ( $doret eq $stack_depth ? $OUT : $LINEINFO );
+
+ # Indent if we're printing because of $frame tracing.
+ print $fh ' ' x $stack_depth if $frame & 16;
+
+ # Print the return value.
+ print $fh "list context return from $sub:\n";
+ dumpit( $fh, \@ret );
+
+ # And don't print it again.
+ $doret = -2;
+ } ## end if ($doret eq $stack_depth...
+ # And we have to return the return value now.
+ @ret;
+ } ## end if (wantarray)
+
+ # Scalar context.
+ else {
+ if ($assertion) {
+ $assertion = 0;
+ eval {
+
+ # Save the value if it's wanted at all.
+ $ret = &$sub;
+ };
+ if ($@) {
+ print $OUT $@;
+ $signal = 1 unless $warnassertions;
+ }
+ $ret = undef unless defined wantarray;
+ }
+ else {
+ if ( defined wantarray ) {
+
+ # Save the value if it's wanted at all.
+ $ret = &$sub;
+ }
+ else {
+
+ # Void return, explicitly.
+ &$sub;
+ undef $ret;
+ }
+ } # if assertion
+
+ # Pop the single-step value off the stack.
+ $single |= $stack[ $stack_depth-- ];
+
+ # If we're doing exit messages...
+ (
+ $frame & 4 # Extended messsages
+ ? (
+ print_lineinfo( ' ' x $stack_depth, "out " ),
+ print_trace( $LINEINFO, -1, 1, 1, "$sub$al" )
+ )
+ : print_lineinfo( ' ' x $stack_depth, "exited $sub$al\n" )
+
+ # Standard messages
+ )
+ if $frame & 2;
+
+ # If we are supposed to show the return value... same as before.
+ if ( $doret eq $stack_depth or $frame & 16 and defined wantarray ) {
+ local $\ = '';
+ my $fh = ( $doret eq $stack_depth ? $OUT : $LINEINFO );
+ print $fh ( ' ' x $stack_depth ) if $frame & 16;
+ print $fh (
+ defined wantarray
+ ? "scalar context return from $sub: "
+ : "void context return from $sub\n"
+ );
+ dumpit( $fh, $ret ) if defined wantarray;
+ $doret = -2;
+ } ## end if ($doret eq $stack_depth...
+
+ # Return the appropriate scalar value.
+ $ret;
+ } ## end else [ if (wantarray)
+} ## end sub sub
+
+=head1 EXTENDED COMMAND HANDLING AND THE COMMAND API
+
+In Perl 5.8.0, there was a major realignment of the commands and what they did,
+Most of the changes were to systematize the command structure and to eliminate
+commands that threw away user input without checking.
+
+The following sections describe the code added to make it easy to support
+multiple command sets with conflicting command names. This section is a start
+at unifying all command processing to make it simpler to develop commands.
+
+Note that all the cmd_[a-zA-Z] subroutines require the command name, a line
+number, and C<$dbline> (the current line) as arguments.
+
+Support functions in this section which have multiple modes of failure C<die>
+on error; the rest simply return a false value.
+
+The user-interface functions (all of the C<cmd_*> functions) just output
+error messages.
+
+=head2 C<%set>
+
+The C<%set> hash defines the mapping from command letter to subroutine
+name suffix.
+
+C<%set> is a two-level hash, indexed by set name and then by command name.
+Note that trying to set the CommandSet to C<foobar> simply results in the
+5.8.0 command set being used, since there's no top-level entry for C<foobar>.
+
+=cut
+
+### The API section
+
+my %set = ( #
+ 'pre580' => {
+ 'a' => 'pre580_a',
+ 'A' => 'pre580_null',
+ 'b' => 'pre580_b',
+ 'B' => 'pre580_null',
+ 'd' => 'pre580_null',
+ 'D' => 'pre580_D',
+ 'h' => 'pre580_h',
+ 'M' => 'pre580_null',
+ 'O' => 'o',
+ 'o' => 'pre580_null',
+ 'v' => 'M',
+ 'w' => 'v',
+ 'W' => 'pre580_W',
+ },
+ 'pre590' => {
+ '<' => 'pre590_prepost',
+ '<<' => 'pre590_prepost',
+ '>' => 'pre590_prepost',
+ '>>' => 'pre590_prepost',
+ '{' => 'pre590_prepost',
+ '{{' => 'pre590_prepost',
+ },
+);
+
+=head2 C<cmd_wrapper()> (API)
+
+C<cmd_wrapper()> allows the debugger to switch command sets
+depending on the value of the C<CommandSet> option.
+
+It tries to look up the command in the C<%set> package-level I<lexical>
+(which means external entities can't fiddle with it) and create the name of
+the sub to call based on the value found in the hash (if it's there). I<All>
+of the commands to be handled in a set have to be added to C<%set>; if they
+aren't found, the 5.8.0 equivalent is called (if there is one).
+
+This code uses symbolic references.
+
+=cut
+
+sub cmd_wrapper {
+ my $cmd = shift;
+ my $line = shift;
+ my $dblineno = shift;
+
+ # Assemble the command subroutine's name by looking up the
+ # command set and command name in %set. If we can't find it,
+ # default to the older version of the command.
+ my $call = 'cmd_'
+ . ( $set{$CommandSet}{$cmd}
+ || ( $cmd =~ /^[<>{]+/o ? 'prepost' : $cmd ) );
+
+ # Call the command subroutine, call it by name.
+ return &$call( $cmd, $line, $dblineno );
+} ## end sub cmd_wrapper
+
+=head3 C<cmd_a> (command)
+
+The C<a> command handles pre-execution actions. These are associated with a
+particular line, so they're stored in C<%dbline>. We default to the current
+line if none is specified.
+
+=cut
+
+sub cmd_a {
+ my $cmd = shift;
+ my $line = shift || ''; # [.|line] expr
+ my $dbline = shift;
+
+ # If it's dot (here), or not all digits, use the current line.
+ $line =~ s/^(\.|(?:[^\d]))/$dbline/;
+
+ # Should be a line number followed by an expression.
+ if ( $line =~ /^\s*(\d*)\s*(\S.+)/ ) {
+ my ( $lineno, $expr ) = ( $1, $2 );
+
+ # If we have an expression ...
+ if ( length $expr ) {
+
+ # ... but the line isn't breakable, complain.
+ if ( $dbline[$lineno] == 0 ) {
+ print $OUT
+ "Line $lineno($dbline[$lineno]) does not have an action?\n";
+ }
+ else {
+
+ # It's executable. Record that the line has an action.
+ $had_breakpoints{$filename} |= 2;
+
+ # Remove any action, temp breakpoint, etc.
+ $dbline{$lineno} =~ s/\0[^\0]*//;
+
+ # Add the action to the line.
+ $dbline{$lineno} .= "\0" . action($expr);
+ }
+ } ## end if (length $expr)
+ } ## end if ($line =~ /^\s*(\d*)\s*(\S.+)/)
+ else {
+
+ # Syntax wrong.
+ print $OUT
+ "Adding an action requires an optional lineno and an expression\n"
+ ; # hint
+ }
+} ## end sub cmd_a
+
+=head3 C<cmd_A> (command)
+
+Delete actions. Similar to above, except the delete code is in a separate
+subroutine, C<delete_action>.
+
+=cut
+
+sub cmd_A {
+ my $cmd = shift;
+ my $line = shift || '';
+ my $dbline = shift;
+
+ # Dot is this line.
+ $line =~ s/^\./$dbline/;
+
+ # Call delete_action with a null param to delete them all.
+ # The '1' forces the eval to be true. It'll be false only
+ # if delete_action blows up for some reason, in which case
+ # we print $@ and get out.
+ if ( $line eq '*' ) {
+ eval { &delete_action(); 1 } or print $OUT $@ and return;
+ }
+
+ # There's a real line number. Pass it to delete_action.
+ # Error trapping is as above.
+ elsif ( $line =~ /^(\S.*)/ ) {
+ eval { &delete_action($1); 1 } or print $OUT $@ and return;
+ }
+
+ # Swing and a miss. Bad syntax.
+ else {
+ print $OUT
+ "Deleting an action requires a line number, or '*' for all\n" ; # hint
+ }
+} ## end sub cmd_A
+
+=head3 C<delete_action> (API)
+
+C<delete_action> accepts either a line number or C<undef>. If a line number
+is specified, we check for the line being executable (if it's not, it
+couldn't have had an action). If it is, we just take the action off (this
+will get any kind of an action, including breakpoints).
+
+=cut
+
+sub delete_action {
+ my $i = shift;
+ if ( defined($i) ) {
+
+ # Can there be one?
+ die "Line $i has no action .\n" if $dbline[$i] == 0;
+
+ # Nuke whatever's there.
+ $dbline{$i} =~ s/\0[^\0]*//; # \^a
+ delete $dbline{$i} if $dbline{$i} eq '';
+ }
+ else {
+ print $OUT "Deleting all actions...\n";
+ for my $file ( keys %had_breakpoints ) {
+ local *dbline = $main::{ '_<' . $file };
+ my $max = $#dbline;
+ my $was;
+ for ( $i = 1 ; $i <= $max ; $i++ ) {
+ if ( defined $dbline{$i} ) {
+ $dbline{$i} =~ s/\0[^\0]*//;
+ delete $dbline{$i} if $dbline{$i} eq '';
+ }
+ unless ( $had_breakpoints{$file} &= ~2 ) {
+ delete $had_breakpoints{$file};
+ }
+ } ## end for ($i = 1 ; $i <= $max...
+ } ## end for my $file (keys %had_breakpoints)
+ } ## end else [ if (defined($i))
+} ## end sub delete_action
+
+=head3 C<cmd_b> (command)
+
+Set breakpoints. Since breakpoints can be set in so many places, in so many
+ways, conditionally or not, the breakpoint code is kind of complex. Mostly,
+we try to parse the command type, and then shuttle it off to an appropriate
+subroutine to actually do the work of setting the breakpoint in the right
+place.
+
+=cut
+
+sub cmd_b {
+ my $cmd = shift;
+ my $line = shift; # [.|line] [cond]
+ my $dbline = shift;
+
+ # Make . the current line number if it's there..
+ $line =~ s/^\./$dbline/;
+
+ # No line number, no condition. Simple break on current line.
+ if ( $line =~ /^\s*$/ ) {
+ &cmd_b_line( $dbline, 1 );
+ }
+
+ # Break on load for a file.
+ elsif ( $line =~ /^load\b\s*(.*)/ ) {
+ my $file = $1;
+ $file =~ s/\s+$//;
+ &cmd_b_load($file);
+ }
+
+ # b compile|postpone <some sub> [<condition>]
+ # The interpreter actually traps this one for us; we just put the
+ # necessary condition in the %postponed hash.
+ elsif ( $line =~ /^(postpone|compile)\b\s*([':A-Za-z_][':\w]*)\s*(.*)/ ) {
+
+ # Capture the condition if there is one. Make it true if none.
+ my $cond = length $3 ? $3 : '1';
+
+ # Save the sub name and set $break to 1 if $1 was 'postpone', 0
+ # if it was 'compile'.
+ my ( $subname, $break ) = ( $2, $1 eq 'postpone' );
+
+ # De-Perl4-ify the name - ' separators to ::.
+ $subname =~ s/\'/::/g;
+
+ # Qualify it into the current package unless it's already qualified.
+ $subname = "${'package'}::" . $subname unless $subname =~ /::/;
+
+ # Add main if it starts with ::.
+ $subname = "main" . $subname if substr( $subname, 0, 2 ) eq "::";
+
+ # Save the break type for this sub.
+ $postponed{$subname} = $break ? "break +0 if $cond" : "compile";
+ } ## end elsif ($line =~ ...
+
+ # b <sub name> [<condition>]
+ elsif ( $line =~ /^([':A-Za-z_][':\w]*(?:\[.*\])?)\s*(.*)/ ) {
+
+ #
+ $subname = $1;
+ $cond = length $2 ? $2 : '1';
+ &cmd_b_sub( $subname, $cond );
+ }
+
+ # b <line> [<condition>].
+ elsif ( $line =~ /^(\d*)\s*(.*)/ ) {
+
+ # Capture the line. If none, it's the current line.
+ $line = $1 || $dbline;
+
+ # If there's no condition, make it '1'.
+ $cond = length $2 ? $2 : '1';
+
+ # Break on line.
+ &cmd_b_line( $line, $cond );
+ }
+
+ # Line didn't make sense.
+ else {
+ print "confused by line($line)?\n";
+ }
+} ## end sub cmd_b
+
+=head3 C<break_on_load> (API)
+
+We want to break when this file is loaded. Mark this file in the
+C<%break_on_load> hash, and note that it has a breakpoint in
+C<%had_breakpoints>.
+
+=cut
+
+sub break_on_load {
+ my $file = shift;
+ $break_on_load{$file} = 1;
+ $had_breakpoints{$file} |= 1;
+}
+
+=head3 C<report_break_on_load> (API)
+
+Gives us an array of filenames that are set to break on load. Note that
+only files with break-on-load are in here, so simply showing the keys
+suffices.
+
+=cut
+
+sub report_break_on_load {
+ sort keys %break_on_load;
+}
+
+=head3 C<cmd_b_load> (command)
+
+We take the file passed in and try to find it in C<%INC> (which maps modules
+to files they came from). We mark those files for break-on-load via
+C<break_on_load> and then report that it was done.
+
+=cut
+
+sub cmd_b_load {
+ my $file = shift;
+ my @files;
+
+ # This is a block because that way we can use a redo inside it
+ # even without there being any looping structure at all outside it.
+ {
+
+ # Save short name and full path if found.
+ push @files, $file;
+ push @files, $::INC{$file} if $::INC{$file};
+
+ # Tack on .pm and do it again unless there was a '.' in the name
+ # already.
+ $file .= '.pm', redo unless $file =~ /\./;
+ }
+
+ # Do the real work here.
+ break_on_load($_) for @files;
+
+ # All the files that have break-on-load breakpoints.
+ @files = report_break_on_load;
+
+ # Normalize for the purposes of our printing this.
+ local $\ = '';
+ local $" = ' ';
+ print $OUT "Will stop on load of `@files'.\n";
+} ## end sub cmd_b_load
+
+=head3 C<$filename_error> (API package global)
+
+Several of the functions we need to implement in the API need to work both
+on the current file and on other files. We don't want to duplicate code, so
+C<$filename_error> is used to contain the name of the file that's being
+worked on (if it's not the current one).
+
+We can now build functions in pairs: the basic function works on the current
+file, and uses C<$filename_error> as part of its error message. Since this is
+initialized to C<"">, no filename will appear when we are working on the
+current file.
+
+The second function is a wrapper which does the following:
+
+=over 4
+
+=item *
+
+Localizes C<$filename_error> and sets it to the name of the file to be processed.
+
+=item *
+
+Localizes the C<*dbline> glob and reassigns it to point to the file we want to process.
+
+=item *
+
+Calls the first function.
+
+The first function works on the I<current> file (i.e., the one we changed to),
+and prints C<$filename_error> in the error message (the name of the other file)
+if it needs to. When the functions return, C<*dbline> is restored to point
+to the actual current file (the one we're executing in) and
+C<$filename_error> is restored to C<"">. This restores everything to
+the way it was before the second function was called at all.
+
+See the comments in C<breakable_line> and C<breakable_line_in_file> for more
+details.
+
+=back
+
+=cut
+
+$filename_error = '';
+
+=head3 breakable_line(from, to) (API)
+
+The subroutine decides whether or not a line in the current file is breakable.
+It walks through C<@dbline> within the range of lines specified, looking for
+the first line that is breakable.
+
+If C<$to> is greater than C<$from>, the search moves forwards, finding the
+first line I<after> C<$to> that's breakable, if there is one.
+
+If C<$from> is greater than C<$to>, the search goes I<backwards>, finding the
+first line I<before> C<$to> that's breakable, if there is one.
+
+=cut
+
+sub breakable_line {
+
+ my ( $from, $to ) = @_;
+
+ # $i is the start point. (Where are the FORTRAN programs of yesteryear?)
+ my $i = $from;
+
+ # If there are at least 2 arguments, we're trying to search a range.
+ if ( @_ >= 2 ) {
+
+ # $delta is positive for a forward search, negative for a backward one.
+ my $delta = $from < $to ? +1 : -1;
+
+ # Keep us from running off the ends of the file.
+ my $limit = $delta > 0 ? $#dbline : 1;
+
+ # Clever test. If you're a mathematician, it's obvious why this
+ # test works. If not:
+ # If $delta is positive (going forward), $limit will be $#dbline.
+ # If $to is less than $limit, ($limit - $to) will be positive, times
+ # $delta of 1 (positive), so the result is > 0 and we should use $to
+ # as the stopping point.
+ #
+ # If $to is greater than $limit, ($limit - $to) is negative,
+ # times $delta of 1 (positive), so the result is < 0 and we should
+ # use $limit ($#dbline) as the stopping point.
+ #
+ # If $delta is negative (going backward), $limit will be 1.
+ # If $to is zero, ($limit - $to) will be 1, times $delta of -1
+ # (negative) so the result is > 0, and we use $to as the stopping
+ # point.
+ #
+ # If $to is less than zero, ($limit - $to) will be positive,
+ # times $delta of -1 (negative), so the result is not > 0, and
+ # we use $limit (1) as the stopping point.
+ #
+ # If $to is 1, ($limit - $to) will zero, times $delta of -1
+ # (negative), still giving zero; the result is not > 0, and
+ # we use $limit (1) as the stopping point.
+ #
+ # if $to is >1, ($limit - $to) will be negative, times $delta of -1
+ # (negative), giving a positive (>0) value, so we'll set $limit to
+ # $to.
+
+ $limit = $to if ( $limit - $to ) * $delta > 0;
+
+ # The real search loop.
+ # $i starts at $from (the point we want to start searching from).
+ # We move through @dbline in the appropriate direction (determined
+ # by $delta: either -1 (back) or +1 (ahead).
+ # We stay in as long as we haven't hit an executable line
+ # ($dbline[$i] == 0 means not executable) and we haven't reached
+ # the limit yet (test similar to the above).
+ $i += $delta while $dbline[$i] == 0 and ( $limit - $i ) * $delta > 0;
+
+ } ## end if (@_ >= 2)
+
+ # If $i points to a line that is executable, return that.
+ return $i unless $dbline[$i] == 0;
+
+ # Format the message and print it: no breakable lines in range.
+ my ( $pl, $upto ) = ( '', '' );
+ ( $pl, $upto ) = ( 's', "..$to" ) if @_ >= 2 and $from != $to;
+
+ # If there's a filename in filename_error, we'll see it.
+ # If not, not.
+ die "Line$pl $from$upto$filename_error not breakable\n";
+} ## end sub breakable_line
+
+=head3 breakable_line_in_filename(file, from, to) (API)
+
+Like C<breakable_line>, but look in another file.
+
+=cut
+
+sub breakable_line_in_filename {
+
+ # Capture the file name.
+ my ($f) = shift;
+
+ # Swap the magic line array over there temporarily.
+ local *dbline = $main::{ '_<' . $f };
+
+ # If there's an error, it's in this other file.
+ local $filename_error = " of `$f'";
+
+ # Find the breakable line.
+ breakable_line(@_);
+
+ # *dbline and $filename_error get restored when this block ends.
+
+} ## end sub breakable_line_in_filename
+
+=head3 break_on_line(lineno, [condition]) (API)
+
+Adds a breakpoint with the specified condition (or 1 if no condition was
+specified) to the specified line. Dies if it can't.
+
+=cut
+
+sub break_on_line {
+ my ( $i, $cond ) = @_;
+
+ # Always true if no condition supplied.
+ $cond = 1 unless @_ >= 2;
+
+ my $inii = $i;
+ my $after = '';
+ my $pl = '';
+
+ # Woops, not a breakable line. $filename_error allows us to say
+ # if it was in a different file.
+ die "Line $i$filename_error not breakable.\n" if $dbline[$i] == 0;
+
+ # Mark this file as having breakpoints in it.
+ $had_breakpoints{$filename} |= 1;
+
+ # If there is an action or condition here already ...
+ if ( $dbline{$i} ) {
+
+ # ... swap this condition for the existing one.
+ $dbline{$i} =~ s/^[^\0]*/$cond/;
+ }
+ else {
+
+ # Nothing here - just add the condition.
+ $dbline{$i} = $cond;
+ }
+} ## end sub break_on_line
+
+=head3 cmd_b_line(line, [condition]) (command)
+
+Wrapper for C<break_on_line>. Prints the failure message if it
+doesn't work.
+
+=cut
+
+sub cmd_b_line {
+ eval { break_on_line(@_); 1 } or do {
+ local $\ = '';
+ print $OUT $@ and return;
+ };
+} ## end sub cmd_b_line
+
+=head3 break_on_filename_line(file, line, [condition]) (API)
+
+Switches to the file specified and then calls C<break_on_line> to set
+the breakpoint.
+
+=cut
+
+sub break_on_filename_line {
+ my ( $f, $i, $cond ) = @_;
+
+ # Always true if condition left off.
+ $cond = 1 unless @_ >= 3;
+
+ # Switch the magical hash temporarily.
+ local *dbline = $main::{ '_<' . $f };
+
+ # Localize the variables that break_on_line uses to make its message.
+ local $filename_error = " of `$f'";
+ local $filename = $f;
+
+ # Add the breakpoint.
+ break_on_line( $i, $cond );
+} ## end sub break_on_filename_line
+
+=head3 break_on_filename_line_range(file, from, to, [condition]) (API)
+
+Switch to another file, search the range of lines specified for an
+executable one, and put a breakpoint on the first one you find.
+
+=cut
+
+sub break_on_filename_line_range {
+ my ( $f, $from, $to, $cond ) = @_;
+
+ # Find a breakable line if there is one.
+ my $i = breakable_line_in_filename( $f, $from, $to );
+
+ # Always true if missing.
+ $cond = 1 unless @_ >= 3;
+
+ # Add the breakpoint.
+ break_on_filename_line( $f, $i, $cond );
+} ## end sub break_on_filename_line_range
+
+=head3 subroutine_filename_lines(subname, [condition]) (API)
+
+Search for a subroutine within a given file. The condition is ignored.
+Uses C<find_sub> to locate the desired subroutine.
+
+=cut
+
+sub subroutine_filename_lines {
+ my ( $subname, $cond ) = @_;
+
+ # Returned value from find_sub() is fullpathname:startline-endline.
+ # The match creates the list (fullpathname, start, end). Falling off
+ # the end of the subroutine returns this implicitly.
+ find_sub($subname) =~ /^(.*):(\d+)-(\d+)$/;
+} ## end sub subroutine_filename_lines
+
+=head3 break_subroutine(subname) (API)
+
+Places a break on the first line possible in the specified subroutine. Uses
+C<subroutine_filename_lines> to find the subroutine, and
+C<break_on_filename_line_range> to place the break.
+
+=cut
+
+sub break_subroutine {
+ my $subname = shift;
+
+ # Get filename, start, and end.
+ my ( $file, $s, $e ) = subroutine_filename_lines($subname)
+ or die "Subroutine $subname not found.\n";
+
+ # Null condition changes to '1' (always true).
+ $cond = 1 unless @_ >= 2;
+
+ # Put a break the first place possible in the range of lines
+ # that make up this subroutine.
+ break_on_filename_line_range( $file, $s, $e, @_ );
+} ## end sub break_subroutine
+
+=head3 cmd_b_sub(subname, [condition]) (command)
+
+We take the incoming subroutine name and fully-qualify it as best we can.
+
+=over 4
+
+=item 1. If it's already fully-qualified, leave it alone.
+
+=item 2. Try putting it in the current package.
+
+=item 3. If it's not there, try putting it in CORE::GLOBAL if it exists there.
+
+=item 4. If it starts with '::', put it in 'main::'.
+
+=back
+
+After all this cleanup, we call C<break_subroutine> to try to set the
+breakpoint.
+
+=cut
+
+sub cmd_b_sub {
+ my ( $subname, $cond ) = @_;
+
+ # Add always-true condition if we have none.
+ $cond = 1 unless @_ >= 2;
+
+ # If the subname isn't a code reference, qualify it so that
+ # break_subroutine() will work right.
+ unless ( ref $subname eq 'CODE' ) {
+
+ # Not Perl4.
+ $subname =~ s/\'/::/g;
+ my $s = $subname;
+
+ # Put it in this package unless it's already qualified.
+ $subname = "${'package'}::" . $subname
+ unless $subname =~ /::/;
+
+ # Requalify it into CORE::GLOBAL if qualifying it into this
+ # package resulted in its not being defined, but only do so
+ # if it really is in CORE::GLOBAL.
+ $subname = "CORE::GLOBAL::$s"
+ if not defined &$subname
+ and $s !~ /::/
+ and defined &{"CORE::GLOBAL::$s"};
+
+ # Put it in package 'main' if it has a leading ::.
+ $subname = "main" . $subname if substr( $subname, 0, 2 ) eq "::";
+
+ } ## end unless (ref $subname eq 'CODE')
+
+ # Try to set the breakpoint.
+ eval { break_subroutine( $subname, $cond ); 1 } or do {
+ local $\ = '';
+ print $OUT $@ and return;
+ }
+} ## end sub cmd_b_sub
+
+=head3 C<cmd_B> - delete breakpoint(s) (command)
+
+The command mostly parses the command line and tries to turn the argument
+into a line spec. If it can't, it uses the current line. It then calls
+C<delete_breakpoint> to actually do the work.
+
+If C<*> is specified, C<cmd_B> calls C<delete_breakpoint> with no arguments,
+thereby deleting all the breakpoints.
+
+=cut
+
+sub cmd_B {
+ my $cmd = shift;
+
+ # No line spec? Use dbline.
+ # If there is one, use it if it's non-zero, or wipe it out if it is.
+ my $line = ( $_[0] =~ /^\./ ) ? $dbline : shift || '';
+ my $dbline = shift;
+
+ # If the line was dot, make the line the current one.
+ $line =~ s/^\./$dbline/;
+
+ # If it's * we're deleting all the breakpoints.
+ if ( $line eq '*' ) {
+ eval { &delete_breakpoint(); 1 } or print $OUT $@ and return;
+ }
+
+ # If there is a line spec, delete the breakpoint on that line.
+ elsif ( $line =~ /^(\S.*)/ ) {
+ eval { &delete_breakpoint( $line || $dbline ); 1 } or do {
+ local $\ = '';
+ print $OUT $@ and return;
+ };
+ } ## end elsif ($line =~ /^(\S.*)/)
+
+ # No line spec.
+ else {
+ print $OUT
+ "Deleting a breakpoint requires a line number, or '*' for all\n"
+ ; # hint
+ }
+} ## end sub cmd_B
+
+=head3 delete_breakpoint([line]) (API)
+
+This actually does the work of deleting either a single breakpoint, or all
+of them.
+
+For a single line, we look for it in C<@dbline>. If it's nonbreakable, we
+just drop out with a message saying so. If it is, we remove the condition
+part of the 'condition\0action' that says there's a breakpoint here. If,
+after we've done that, there's nothing left, we delete the corresponding
+line in C<%dbline> to signal that no action needs to be taken for this line.
+
+For all breakpoints, we iterate through the keys of C<%had_breakpoints>,
+which lists all currently-loaded files which have breakpoints. We then look
+at each line in each of these files, temporarily switching the C<%dbline>
+and C<@dbline> structures to point to the files in question, and do what
+we did in the single line case: delete the condition in C<@dbline>, and
+delete the key in C<%dbline> if nothing's left.
+
+We then wholesale delete C<%postponed>, C<%postponed_file>, and
+C<%break_on_load>, because these structures contain breakpoints for files
+and code that haven't been loaded yet. We can just kill these off because there
+are no magical debugger structures associated with them.
+
+=cut
+
+sub delete_breakpoint {
+ my $i = shift;
+
+ # If we got a line, delete just that one.
+ if ( defined($i) ) {
+
+ # Woops. This line wasn't breakable at all.
+ die "Line $i not breakable.\n" if $dbline[$i] == 0;
+
+ # Kill the condition, but leave any action.
+ $dbline{$i} =~ s/^[^\0]*//;
+
+ # Remove the entry entirely if there's no action left.
+ delete $dbline{$i} if $dbline{$i} eq '';
+ }
+
+ # No line; delete them all.
+ else {
+ print $OUT "Deleting all breakpoints...\n";
+
+ # %had_breakpoints lists every file that had at least one
+ # breakpoint in it.
+ for my $file ( keys %had_breakpoints ) {
+
+ # Switch to the desired file temporarily.
+ local *dbline = $main::{ '_<' . $file };
+
+ my $max = $#dbline;
+ my $was;
+
+ # For all lines in this file ...
+ for ( $i = 1 ; $i <= $max ; $i++ ) {
+
+ # If there's a breakpoint or action on this line ...
+ if ( defined $dbline{$i} ) {
+
+ # ... remove the breakpoint.
+ $dbline{$i} =~ s/^[^\0]+//;
+ if ( $dbline{$i} =~ s/^\0?$// ) {
+
+ # Remove the entry altogether if no action is there.
+ delete $dbline{$i};
+ }
+ } ## end if (defined $dbline{$i...
+ } ## end for ($i = 1 ; $i <= $max...
+
+ # If, after we turn off the "there were breakpoints in this file"
+ # bit, the entry in %had_breakpoints for this file is zero,
+ # we should remove this file from the hash.
+ if ( not $had_breakpoints{$file} &= ~1 ) {
+ delete $had_breakpoints{$file};
+ }
+ } ## end for my $file (keys %had_breakpoints)
+
+ # Kill off all the other breakpoints that are waiting for files that
+ # haven't been loaded yet.
+ undef %postponed;
+ undef %postponed_file;
+ undef %break_on_load;
+ } ## end else [ if (defined($i))
+} ## end sub delete_breakpoint
+
+=head3 cmd_stop (command)
+
+This is meant to be part of the new command API, but it isn't called or used
+anywhere else in the debugger. XXX It is probably meant for use in development
+of new commands.
+
+=cut
+
+sub cmd_stop { # As on ^C, but not signal-safy.
+ $signal = 1;
+}
+
+=head3 C<cmd_e> - threads
+
+Display the current thread id:
+
+ e
+
+This could be how (when implemented) to send commands to this thread id (e cmd)
+or that thread id (e tid cmd).
+
+=cut
+
+sub cmd_e {
+ my $cmd = shift;
+ my $line = shift;
+ unless (exists($INC{'threads.pm'})) {
+ print "threads not loaded($ENV{PERL5DB_THREADED})
+ please run the debugger with PERL5DB_THREADED=1 set in the environment\n";
+ } else {
+ my $tid = threads->self->tid;
+ print "thread id: $tid\n";
+ }
+} ## end sub cmd_e
+
+=head3 C<cmd_E> - list of thread ids
+
+Display the list of available thread ids:
+
+ E
+
+This could be used (when implemented) to send commands to all threads (E cmd).
+
+=cut
+
+sub cmd_E {
+ my $cmd = shift;
+ my $line = shift;
+ unless (exists($INC{'threads.pm'})) {
+ print "threads not loaded($ENV{PERL5DB_THREADED})
+ please run the debugger with PERL5DB_THREADED=1 set in the environment\n";
+ } else {
+ my $tid = threads->self->tid;
+ print "thread ids: ".join(', ',
+ map { ($tid == $_->tid ? '<'.$_->tid.'>' : $_->tid) } threads->list
+ )."\n";
+ }
+} ## end sub cmd_E
+
+=head3 C<cmd_h> - help command (command)
+
+Does the work of either
+
+=over 4
+
+=item *
+
+Showing all the debugger help
+
+=item *
+
+Showing help for a specific command
+
+=back
+
+=cut
+
+sub cmd_h {
+ my $cmd = shift;
+
+ # If we have no operand, assume null.
+ my $line = shift || '';
+
+ # 'h h'. Print the long-format help.
+ if ( $line =~ /^h\s*/ ) {
+ print_help($help);
+ }
+
+ # 'h <something>'. Search for the command and print only its help.
+ elsif ( $line =~ /^(\S.*)$/ ) {
+
+ # support long commands; otherwise bogus errors
+ # happen when you ask for h on <CR> for example
+ my $asked = $1; # the command requested
+ # (for proper error message)
+
+ my $qasked = quotemeta($asked); # for searching; we don't
+ # want to use it as a pattern.
+ # XXX: finds CR but not <CR>
+
+ # Search the help string for the command.
+ if (
+ $help =~ /^ # Start of a line
+ <? # Optional '<'
+ (?:[IB]<) # Optional markup
+ $qasked # The requested command
+ /mx
+ )
+ {
+
+ # It's there; pull it out and print it.
+ while (
+ $help =~ /^
+ (<? # Optional '<'
+ (?:[IB]<) # Optional markup
+ $qasked # The command
+ ([\s\S]*?) # Description line(s)
+ \n) # End of last description line
+ (?!\s) # Next line not starting with
+ # whitespace
+ /mgx
+ )
+ {
+ print_help($1);
+ }
+ }
+
+ # Not found; not a debugger command.
+ else {
+ print_help("B<$asked> is not a debugger command.\n");
+ }
+ } ## end elsif ($line =~ /^(\S.*)$/)
+
+ # 'h' - print the summary help.
+ else {
+ print_help($summary);
+ }
+} ## end sub cmd_h
+
+=head3 C<cmd_i> - inheritance display
+
+Display the (nested) parentage of the module or object given.
+
+=cut
+
+sub cmd_i {
+ my $cmd = shift;
+ my $line = shift;
+ eval { require Class::ISA };
+ if ($@) {
+ &warn( $@ =~ /locate/
+ ? "Class::ISA module not found - please install\n"
+ : $@ );
+ }
+ else {
+ ISA:
+ foreach my $isa ( split( /\s+/, $line ) ) {
+ $evalarg = $isa;
+ ($isa) = &eval;
+ no strict 'refs';
+ print join(
+ ', ',
+ map { # snaffled unceremoniously from Class::ISA
+ "$_"
+ . (
+ defined( ${"$_\::VERSION"} )
+ ? ' ' . ${"$_\::VERSION"}
+ : undef )
+ } Class::ISA::self_and_super_path(ref($isa) || $isa)
+ );
+ print "\n";
+ }
+ }
+} ## end sub cmd_i
+
+=head3 C<cmd_l> - list lines (command)
+
+Most of the command is taken up with transforming all the different line
+specification syntaxes into 'start-stop'. After that is done, the command
+runs a loop over C<@dbline> for the specified range of lines. It handles
+the printing of each line and any markers (C<==E<gt>> for current line,
+C<b> for break on this line, C<a> for action on this line, C<:> for this
+line breakable).
+
+We save the last line listed in the C<$start> global for further listing
+later.
+
+=cut
+
+sub cmd_l {
+ my $current_line = $line;
+ my $cmd = shift;
+ my $line = shift;
+
+ # If this is '-something', delete any spaces after the dash.
+ $line =~ s/^-\s*$/-/;
+
+ # If the line is '$something', assume this is a scalar containing a
+ # line number.
+ if ( $line =~ /^(\$.*)/s ) {
+
+ # Set up for DB::eval() - evaluate in *user* context.
+ $evalarg = $1;
+ # $evalarg = $2;
+ my ($s) = &eval;
+
+ # Ooops. Bad scalar.
+ print( $OUT "Error: $@\n" ), next CMD if $@;
+
+ # Good scalar. If it's a reference, find what it points to.
+ $s = CvGV_name($s);
+ print( $OUT "Interpreted as: $1 $s\n" );
+ $line = "$1 $s";
+
+ # Call self recursively to really do the command.
+ &cmd_l( 'l', $s );
+ } ## end if ($line =~ /^(\$.*)/s)
+
+ # l name. Try to find a sub by that name.
+ elsif ( $line =~ /^([\':A-Za-z_][\':\w]*(\[.*\])?)/s ) {
+ my $s = $subname = $1;
+
+ # De-Perl4.
+ $subname =~ s/\'/::/;
+
+ # Put it in this package unless it starts with ::.
+ $subname = $package . "::" . $subname unless $subname =~ /::/;
+
+ # Put it in CORE::GLOBAL if t doesn't start with :: and
+ # it doesn't live in this package and it lives in CORE::GLOBAL.
+ $subname = "CORE::GLOBAL::$s"
+ if not defined &$subname
+ and $s !~ /::/
+ and defined &{"CORE::GLOBAL::$s"};
+
+ # Put leading '::' names into 'main::'.
+ $subname = "main" . $subname if substr( $subname, 0, 2 ) eq "::";
+
+ # Get name:start-stop from find_sub, and break this up at
+ # colons.
+ @pieces = split( /:/, find_sub($subname) || $sub{$subname} );
+
+ # Pull off start-stop.
+ $subrange = pop @pieces;
+
+ # If the name contained colons, the split broke it up.
+ # Put it back together.
+ $file = join( ':', @pieces );
+
+ # If we're not in that file, switch over to it.
+ if ( $file ne $filename ) {
+ print $OUT "Switching to file '$file'.\n"
+ unless $slave_editor;
+
+ # Switch debugger's magic structures.
+ *dbline = $main::{ '_<' . $file };
+ $max = $#dbline;
+ $filename = $file;
+ } ## end if ($file ne $filename)
+
+ # Subrange is 'start-stop'. If this is less than a window full,
+ # swap it to 'start+', which will list a window from the start point.
+ if ($subrange) {
+ if ( eval($subrange) < -$window ) {
+ $subrange =~ s/-.*/+/;
+ }
+
+ # Call self recursively to list the range.
+ $line = $subrange;
+ &cmd_l( 'l', $subrange );
+ } ## end if ($subrange)
+
+ # Couldn't find it.
+ else {
+ print $OUT "Subroutine $subname not found.\n";
+ }
+ } ## end elsif ($line =~ /^([\':A-Za-z_][\':\w]*(\[.*\])?)/s)
+
+ # Bare 'l' command.
+ elsif ( $line =~ /^\s*$/ ) {
+
+ # Compute new range to list.
+ $incr = $window - 1;
+ $line = $start . '-' . ( $start + $incr );
+
+ # Recurse to do it.
+ &cmd_l( 'l', $line );
+ }
+
+ # l [start]+number_of_lines
+ elsif ( $line =~ /^(\d*)\+(\d*)$/ ) {
+
+ # Don't reset start for 'l +nnn'.
+ $start = $1 if $1;
+
+ # Increment for list. Use window size if not specified.
+ # (Allows 'l +' to work.)
+ $incr = $2;
+ $incr = $window - 1 unless $incr;
+
+ # Create a line range we'll understand, and recurse to do it.
+ $line = $start . '-' . ( $start + $incr );
+ &cmd_l( 'l', $line );
+ } ## end elsif ($line =~ /^(\d*)\+(\d*)$/)
+
+ # l start-stop or l start,stop
+ elsif ( $line =~ /^((-?[\d\$\.]+)([-,]([\d\$\.]+))?)?/ ) {
+
+ # Determine end point; use end of file if not specified.
+ $end = ( !defined $2 ) ? $max : ( $4 ? $4 : $2 );
+
+ # Go on to the end, and then stop.
+ $end = $max if $end > $max;
+
+ # Determine start line.
+ $i = $2;
+ $i = $line if $i eq '.';
+ $i = 1 if $i < 1;
+ $incr = $end - $i;
+
+ # If we're running under a slave editor, force it to show the lines.
+ if ($slave_editor) {
+ print $OUT "\032\032$filename:$i:0\n";
+ $i = $end;
+ }
+
+ # We're doing it ourselves. We want to show the line and special
+ # markers for:
+ # - the current line in execution
+ # - whether a line is breakable or not
+ # - whether a line has a break or not
+ # - whether a line has an action or not
+ else {
+ for ( ; $i <= $end ; $i++ ) {
+
+ # Check for breakpoints and actions.
+ my ( $stop, $action );
+ ( $stop, $action ) = split( /\0/, $dbline{$i} )
+ if $dbline{$i};
+
+ # ==> if this is the current line in execution,
+ # : if it's breakable.
+ $arrow =
+ ( $i == $current_line and $filename eq $filename_ini )
+ ? '==>'
+ : ( $dbline[$i] + 0 ? ':' : ' ' );
+
+ # Add break and action indicators.
+ $arrow .= 'b' if $stop;
+ $arrow .= 'a' if $action;
+
+ # Print the line.
+ print $OUT "$i$arrow\t", $dbline[$i];
+
+ # Move on to the next line. Drop out on an interrupt.
+ $i++, last if $signal;
+ } ## end for (; $i <= $end ; $i++)
+
+ # Line the prompt up; print a newline if the last line listed
+ # didn't have a newline.
+ print $OUT "\n" unless $dbline[ $i - 1 ] =~ /\n$/;
+ } ## end else [ if ($slave_editor)
+
+ # Save the point we last listed to in case another relative 'l'
+ # command is desired. Don't let it run off the end.
+ $start = $i;
+ $start = $max if $start > $max;
+ } ## end elsif ($line =~ /^((-?[\d\$\.]+)([-,]([\d\$\.]+))?)?/)
+} ## end sub cmd_l
+
+=head3 C<cmd_L> - list breakpoints, actions, and watch expressions (command)
+
+To list breakpoints, the command has to look determine where all of them are
+first. It starts a C<%had_breakpoints>, which tells us what all files have
+breakpoints and/or actions. For each file, we switch the C<*dbline> glob (the
+magic source and breakpoint data structures) to the file, and then look
+through C<%dbline> for lines with breakpoints and/or actions, listing them
+out. We look through C<%postponed> not-yet-compiled subroutines that have
+breakpoints, and through C<%postponed_file> for not-yet-C<require>'d files
+that have breakpoints.
+
+Watchpoints are simpler: we just list the entries in C<@to_watch>.
+
+=cut
+
+sub cmd_L {
+ my $cmd = shift;
+
+ # If no argument, list everything. Pre-5.8.0 version always lists
+ # everything
+ my $arg = shift || 'abw';
+ $arg = 'abw' unless $CommandSet eq '580'; # sigh...
+
+ # See what is wanted.
+ my $action_wanted = ( $arg =~ /a/ ) ? 1 : 0;
+ my $break_wanted = ( $arg =~ /b/ ) ? 1 : 0;
+ my $watch_wanted = ( $arg =~ /w/ ) ? 1 : 0;
+
+ # Breaks and actions are found together, so we look in the same place
+ # for both.
+ if ( $break_wanted or $action_wanted ) {
+
+ # Look in all the files with breakpoints...
+ for my $file ( keys %had_breakpoints ) {
+
+ # Temporary switch to this file.
+ local *dbline = $main::{ '_<' . $file };
+
+ # Set up to look through the whole file.
+ my $max = $#dbline;
+ my $was; # Flag: did we print something
+ # in this file?
+
+ # For each line in the file ...
+ for ( $i = 1 ; $i <= $max ; $i++ ) {
+
+ # We've got something on this line.
+ if ( defined $dbline{$i} ) {
+
+ # Print the header if we haven't.
+ print $OUT "$file:\n" unless $was++;
+
+ # Print the line.
+ print $OUT " $i:\t", $dbline[$i];
+
+ # Pull out the condition and the action.
+ ( $stop, $action ) = split( /\0/, $dbline{$i} );
+
+ # Print the break if there is one and it's wanted.
+ print $OUT " break if (", $stop, ")\n"
+ if $stop
+ and $break_wanted;
+
+ # Print the action if there is one and it's wanted.
+ print $OUT " action: ", $action, "\n"
+ if $action
+ and $action_wanted;
+
+ # Quit if the user hit interrupt.
+ last if $signal;
+ } ## end if (defined $dbline{$i...
+ } ## end for ($i = 1 ; $i <= $max...
+ } ## end for my $file (keys %had_breakpoints)
+ } ## end if ($break_wanted or $action_wanted)
+
+ # Look for breaks in not-yet-compiled subs:
+ if ( %postponed and $break_wanted ) {
+ print $OUT "Postponed breakpoints in subroutines:\n";
+ my $subname;
+ for $subname ( keys %postponed ) {
+ print $OUT " $subname\t$postponed{$subname}\n";
+ last if $signal;
+ }
+ } ## end if (%postponed and $break_wanted)
+
+ # Find files that have not-yet-loaded breaks:
+ my @have = map { # Combined keys
+ keys %{ $postponed_file{$_} }
+ } keys %postponed_file;
+
+ # If there are any, list them.
+ if ( @have and ( $break_wanted or $action_wanted ) ) {
+ print $OUT "Postponed breakpoints in files:\n";
+ my ( $file, $line );
+
+ for $file ( keys %postponed_file ) {
+ my $db = $postponed_file{$file};
+ print $OUT " $file:\n";
+ for $line ( sort { $a <=> $b } keys %$db ) {
+ print $OUT " $line:\n";
+ my ( $stop, $action ) = split( /\0/, $$db{$line} );
+ print $OUT " break if (", $stop, ")\n"
+ if $stop
+ and $break_wanted;
+ print $OUT " action: ", $action, "\n"
+ if $action
+ and $action_wanted;
+ last if $signal;
+ } ## end for $line (sort { $a <=>...
+ last if $signal;
+ } ## end for $file (keys %postponed_file)
+ } ## end if (@have and ($break_wanted...
+ if ( %break_on_load and $break_wanted ) {
+ print $OUT "Breakpoints on load:\n";
+ my $file;
+ for $file ( keys %break_on_load ) {
+ print $OUT " $file\n";
+ last if $signal;
+ }
+ } ## end if (%break_on_load and...
+ if ($watch_wanted) {
+ if ( $trace & 2 ) {
+ print $OUT "Watch-expressions:\n" if @to_watch;
+ for my $expr (@to_watch) {
+ print $OUT " $expr\n";
+ last if $signal;
+ }
+ } ## end if ($trace & 2)
+ } ## end if ($watch_wanted)
+} ## end sub cmd_L
+
+=head3 C<cmd_M> - list modules (command)
+
+Just call C<list_modules>.
+
+=cut
+
+sub cmd_M {
+ &list_modules();
+}
+
+=head3 C<cmd_o> - options (command)
+
+If this is just C<o> by itself, we list the current settings via
+C<dump_option>. If there's a nonblank value following it, we pass that on to
+C<parse_options> for processing.
+
+=cut
+
+sub cmd_o {
+ my $cmd = shift;
+ my $opt = shift || ''; # opt[=val]
+
+ # Nonblank. Try to parse and process.
+ if ( $opt =~ /^(\S.*)/ ) {
+ &parse_options($1);
+ }
+
+ # Blank. List the current option settings.
+ else {
+ for (@options) {
+ &dump_option($_);
+ }
+ }
+} ## end sub cmd_o
+
+=head3 C<cmd_O> - nonexistent in 5.8.x (command)
+
+Advises the user that the O command has been renamed.
+
+=cut
+
+sub cmd_O {
+ print $OUT "The old O command is now the o command.\n"; # hint
+ print $OUT "Use 'h' to get current command help synopsis or\n"; #
+ print $OUT "use 'o CommandSet=pre580' to revert to old usage\n"; #
+}
+
+=head3 C<cmd_v> - view window (command)
+
+Uses the C<$preview> variable set in the second C<BEGIN> block (q.v.) to
+move back a few lines to list the selected line in context. Uses C<cmd_l>
+to do the actual listing after figuring out the range of line to request.
+
+=cut
+
+sub cmd_v {
+ my $cmd = shift;
+ my $line = shift;
+
+ # Extract the line to list around. (Astute readers will have noted that
+ # this pattern will match whether or not a numeric line is specified,
+ # which means that we'll always enter this loop (though a non-numeric
+ # argument results in no action at all)).
+ if ( $line =~ /^(\d*)$/ ) {
+
+ # Total number of lines to list (a windowful).
+ $incr = $window - 1;
+
+ # Set the start to the argument given (if there was one).
+ $start = $1 if $1;
+
+ # Back up by the context amount.
+ $start -= $preview;
+
+ # Put together a linespec that cmd_l will like.
+ $line = $start . '-' . ( $start + $incr );
+
+ # List the lines.
+ &cmd_l( 'l', $line );
+ } ## end if ($line =~ /^(\d*)$/)
+} ## end sub cmd_v
+
+=head3 C<cmd_w> - add a watch expression (command)
+
+The 5.8 version of this command adds a watch expression if one is specified;
+it does nothing if entered with no operands.
+
+We extract the expression, save it, evaluate it in the user's context, and
+save the value. We'll re-evaluate it each time the debugger passes a line,
+and will stop (see the code at the top of the command loop) if the value
+of any of the expressions changes.
+
+=cut
+
+sub cmd_w {
+ my $cmd = shift;
+
+ # Null expression if no arguments.
+ my $expr = shift || '';
+
+ # If expression is not null ...
+ if ( $expr =~ /^(\S.*)/ ) {
+
+ # ... save it.
+ push @to_watch, $expr;
+
+ # Parameterize DB::eval and call it to get the expression's value
+ # in the user's context. This version can handle expressions which
+ # return a list value.
+ $evalarg = $expr;
+ my ($val) = join( ' ', &eval );
+ $val = ( defined $val ) ? "'$val'" : 'undef';
+
+ # Save the current value of the expression.
+ push @old_watch, $val;
+
+ # We are now watching expressions.
+ $trace |= 2;
+ } ## end if ($expr =~ /^(\S.*)/)
+
+ # You have to give one to get one.
+ else {
+ print $OUT "Adding a watch-expression requires an expression\n"; # hint
+ }
+} ## end sub cmd_w
+
+=head3 C<cmd_W> - delete watch expressions (command)
+
+This command accepts either a watch expression to be removed from the list
+of watch expressions, or C<*> to delete them all.
+
+If C<*> is specified, we simply empty the watch expression list and the
+watch expression value list. We also turn off the bit that says we've got
+watch expressions.
+
+If an expression (or partial expression) is specified, we pattern-match
+through the expressions and remove the ones that match. We also discard
+the corresponding values. If no watch expressions are left, we turn off
+the I<watching expressions> bit.
+
+=cut
+
+sub cmd_W {
+ my $cmd = shift;
+ my $expr = shift || '';
+
+ # Delete them all.
+ if ( $expr eq '*' ) {
+
+ # Not watching now.
+ $trace &= ~2;
+
+ print $OUT "Deleting all watch expressions ...\n";
+
+ # And all gone.
+ @to_watch = @old_watch = ();
+ }
+
+ # Delete one of them.
+ elsif ( $expr =~ /^(\S.*)/ ) {
+
+ # Where we are in the list.
+ my $i_cnt = 0;
+
+ # For each expression ...
+ foreach (@to_watch) {
+ my $val = $to_watch[$i_cnt];
+
+ # Does this one match the command argument?
+ if ( $val eq $expr ) { # =~ m/^\Q$i$/) {
+ # Yes. Turn it off, and its value too.
+ splice( @to_watch, $i_cnt, 1 );
+ splice( @old_watch, $i_cnt, 1 );
+ }
+ $i_cnt++;
+ } ## end foreach (@to_watch)
+
+ # We don't bother to turn watching off because
+ # a) we don't want to stop calling watchfunction() it it exists
+ # b) foreach over a null list doesn't do anything anyway
+
+ } ## end elsif ($expr =~ /^(\S.*)/)
+
+ # No command arguments entered.
+ else {
+ print $OUT
+ "Deleting a watch-expression requires an expression, or '*' for all\n"
+ ; # hint
+ }
+} ## end sub cmd_W
+
+### END of the API section
+
+=head1 SUPPORT ROUTINES
+
+These are general support routines that are used in a number of places
+throughout the debugger.
+
+=over 4
+
+=item cmd_P
+
+Something to do with assertions
+
+=back
+
+=cut
+
+sub cmd_P {
+ unless ($ini_assertion) {
+ print $OUT "Assertions not supported in this Perl interpreter\n";
+ } else {
+ if ( $cmd =~ /^.\b\s*([+-]?)\s*(~?)\s*(\w+(\s*\|\s*\w+)*)\s*$/ ) {
+ my ( $how, $neg, $flags ) = ( $1, $2, $3 );
+ my $acu = parse_DollarCaretP_flags($flags);
+ if ( defined $acu ) {
+ $acu = ~$acu if $neg;
+ if ( $how eq '+' ) { $^P |= $acu }
+ elsif ( $how eq '-' ) { $^P &= ~$acu }
+ else { $^P = $acu }
+ }
+
+ # else { print $OUT "undefined acu\n" }
+ }
+ my $expanded = expand_DollarCaretP_flags($^P);
+ print $OUT "Internal Perl debugger flags:\n\$^P=$expanded\n";
+ $expanded;
+ }
+}
+
+=head2 save
+
+save() saves the user's versions of globals that would mess us up in C<@saved>,
+and installs the versions we like better.
+
+=cut
+
+sub save {
+
+ # Save eval failure, command failure, extended OS error, output field
+ # separator, input record separator, output record separator and
+ # the warning setting.
+ @saved = ( $@, $!, $^E, $,, $/, $\, $^W );
+
+ $, = ""; # output field separator is null string
+ $/ = "\n"; # input record separator is newline
+ $\ = ""; # output record separator is null string
+ $^W = 0; # warnings are off
+} ## end sub save
+
+=head2 C<print_lineinfo> - show where we are now
+
+print_lineinfo prints whatever it is that it is handed; it prints it to the
+C<$LINEINFO> filehandle instead of just printing it to STDOUT. This allows
+us to feed line information to a slave editor without messing up the
+debugger output.
+
+=cut
+
+sub print_lineinfo {
+
+ # Make the terminal sensible if we're not the primary debugger.
+ resetterm(1) if $LINEINFO eq $OUT and $term_pid != $$;
+ local $\ = '';
+ local $, = '';
+ print $LINEINFO @_;
+} ## end sub print_lineinfo
+
+=head2 C<postponed_sub>
+
+Handles setting postponed breakpoints in subroutines once they're compiled.
+For breakpoints, we use C<DB::find_sub> to locate the source file and line
+range for the subroutine, then mark the file as having a breakpoint,
+temporarily switch the C<*dbline> glob over to the source file, and then
+search the given range of lines to find a breakable line. If we find one,
+we set the breakpoint on it, deleting the breakpoint from C<%postponed>.
+
+=cut
+
+# The following takes its argument via $evalarg to preserve current @_
+
+sub postponed_sub {
+
+ # Get the subroutine name.
+ my $subname = shift;
+
+ # If this is a 'break +<n> if <condition>' ...
+ if ( $postponed{$subname} =~ s/^break\s([+-]?\d+)\s+if\s// ) {
+
+ # If there's no offset, use '+0'.
+ my $offset = $1 || 0;
+
+ # find_sub's value is 'fullpath-filename:start-stop'. It's
+ # possible that the filename might have colons in it too.
+ my ( $file, $i ) = ( find_sub($subname) =~ /^(.*):(\d+)-.*$/ );
+ if ($i) {
+
+ # We got the start line. Add the offset '+<n>' from
+ # $postponed{subname}.
+ $i += $offset;
+
+ # Switch to the file this sub is in, temporarily.
+ local *dbline = $main::{ '_<' . $file };
+
+ # No warnings, please.
+ local $^W = 0; # != 0 is magical below
+
+ # This file's got a breakpoint in it.
+ $had_breakpoints{$file} |= 1;
+
+ # Last line in file.
+ my $max = $#dbline;
+
+ # Search forward until we hit a breakable line or get to
+ # the end of the file.
+ ++$i until $dbline[$i] != 0 or $i >= $max;
+
+ # Copy the breakpoint in and delete it from %postponed.
+ $dbline{$i} = delete $postponed{$subname};
+ } ## end if ($i)
+
+ # find_sub didn't find the sub.
+ else {
+ local $\ = '';
+ print $OUT "Subroutine $subname not found.\n";
+ }
+ return;
+ } ## end if ($postponed{$subname...
+ elsif ( $postponed{$subname} eq 'compile' ) { $signal = 1 }
+
+ #print $OUT "In postponed_sub for `$subname'.\n";
+} ## end sub postponed_sub
+
+=head2 C<postponed>
+
+Called after each required file is compiled, but before it is executed;
+also called if the name of a just-compiled subroutine is a key of
+C<%postponed>. Propagates saved breakpoints (from C<b compile>, C<b load>,
+etc.) into the just-compiled code.
+
+If this is a C<require>'d file, the incoming parameter is the glob
+C<*{"_<$filename"}>, with C<$filename> the name of the C<require>'d file.
+
+If it's a subroutine, the incoming parameter is the subroutine name.
+
+=cut
+
+sub postponed {
+
+ # If there's a break, process it.
+ if ($ImmediateStop) {
+
+ # Right, we've stopped. Turn it off.
+ $ImmediateStop = 0;
+
+ # Enter the command loop when DB::DB gets called.
+ $signal = 1;
+ }
+
+ # If this is a subroutine, let postponed_sub() deal with it.
+ return &postponed_sub unless ref \$_[0] eq 'GLOB';
+
+ # Not a subroutine. Deal with the file.
+ local *dbline = shift;
+ my $filename = $dbline;
+ $filename =~ s/^_<//;
+ local $\ = '';
+ $signal = 1, print $OUT "'$filename' loaded...\n"
+ if $break_on_load{$filename};
+ print_lineinfo( ' ' x $stack_depth, "Package $filename.\n" ) if $frame;
+
+ # Do we have any breakpoints to put in this file?
+ return unless $postponed_file{$filename};
+
+ # Yes. Mark this file as having breakpoints.
+ $had_breakpoints{$filename} |= 1;
+
+ # "Cannot be done: unsufficient magic" - we can't just put the
+ # breakpoints saved in %postponed_file into %dbline by assigning
+ # the whole hash; we have to do it one item at a time for the
+ # breakpoints to be set properly.
+ #%dbline = %{$postponed_file{$filename}};
+
+ # Set the breakpoints, one at a time.
+ my $key;
+
+ for $key ( keys %{ $postponed_file{$filename} } ) {
+
+ # Stash the saved breakpoint into the current file's magic line array.
+ $dbline{$key} = ${ $postponed_file{$filename} }{$key};
+ }
+
+ # This file's been compiled; discard the stored breakpoints.
+ delete $postponed_file{$filename};
+
+} ## end sub postponed
+
+=head2 C<dumpit>
+
+C<dumpit> is the debugger's wrapper around dumpvar.pl.
+
+It gets a filehandle (to which C<dumpvar.pl>'s output will be directed) and
+a reference to a variable (the thing to be dumped) as its input.
+
+The incoming filehandle is selected for output (C<dumpvar.pl> is printing to
+the currently-selected filehandle, thank you very much). The current
+values of the package globals C<$single> and C<$trace> are backed up in
+lexicals, and they are turned off (this keeps the debugger from trying
+to single-step through C<dumpvar.pl> (I think.)). C<$frame> is localized to
+preserve its current value and it is set to zero to prevent entry/exit
+messages from printing, and C<$doret> is localized as well and set to -2 to
+prevent return values from being shown.
+
+C<dumpit()> then checks to see if it needs to load C<dumpvar.pl> and
+tries to load it (note: if you have a C<dumpvar.pl> ahead of the
+installed version in C<@INC>, yours will be used instead. Possible security
+problem?).
+
+It then checks to see if the subroutine C<main::dumpValue> is now defined
+(it should have been defined by C<dumpvar.pl>). If it has, C<dumpit()>
+localizes the globals necessary for things to be sane when C<main::dumpValue()>
+is called, and picks up the variable to be dumped from the parameter list.
+
+It checks the package global C<%options> to see if there's a C<dumpDepth>
+specified. If not, -1 is assumed; if so, the supplied value gets passed on to
+C<dumpvar.pl>. This tells C<dumpvar.pl> where to leave off when dumping a
+structure: -1 means dump everything.
+
+C<dumpValue()> is then called if possible; if not, C<dumpit()>just prints a
+warning.
+
+In either case, C<$single>, C<$trace>, C<$frame>, and C<$doret> are restored
+and we then return to the caller.
+
+=cut
+
+sub dumpit {
+
+ # Save the current output filehandle and switch to the one
+ # passed in as the first parameter.
+ local ($savout) = select(shift);
+
+ # Save current settings of $single and $trace, and then turn them off.
+ my $osingle = $single;
+ my $otrace = $trace;
+ $single = $trace = 0;
+
+ # XXX Okay, what do $frame and $doret do, again?
+ local $frame = 0;
+ local $doret = -2;
+
+ # Load dumpvar.pl unless we've already got the sub we need from it.
+ unless ( defined &main::dumpValue ) {
+ do 'dumpvar.pl';
+ }
+
+ # If the load succeeded (or we already had dumpvalue()), go ahead
+ # and dump things.
+ if ( defined &main::dumpValue ) {
+ local $\ = '';
+ local $, = '';
+ local $" = ' ';
+ my $v = shift;
+ my $maxdepth = shift || $option{dumpDepth};
+ $maxdepth = -1 unless defined $maxdepth; # -1 means infinite depth
+ &main::dumpValue( $v, $maxdepth );
+ } ## end if (defined &main::dumpValue)
+
+ # Oops, couldn't load dumpvar.pl.
+ else {
+ local $\ = '';
+ print $OUT "dumpvar.pl not available.\n";
+ }
+
+ # Reset $single and $trace to their old values.
+ $single = $osingle;
+ $trace = $otrace;
+
+ # Restore the old filehandle.
+ select($savout);
+} ## end sub dumpit
+
+=head2 C<print_trace>
+
+C<print_trace>'s job is to print a stack trace. It does this via the
+C<dump_trace> routine, which actually does all the ferreting-out of the
+stack trace data. C<print_trace> takes care of formatting it nicely and
+printing it to the proper filehandle.
+
+Parameters:
+
+=over 4
+
+=item *
+
+The filehandle to print to.
+
+=item *
+
+How many frames to skip before starting trace.
+
+=item *
+
+How many frames to print.
+
+=item *
+
+A flag: if true, print a I<short> trace without filenames, line numbers, or arguments
+
+=back
+
+The original comment below seems to be noting that the traceback may not be
+correct if this routine is called in a tied method.
+
+=cut
+
+# Tied method do not create a context, so may get wrong message:
+
+sub print_trace {
+ local $\ = '';
+ my $fh = shift;
+
+ # If this is going to a slave editor, but we're not the primary
+ # debugger, reset it first.
+ resetterm(1)
+ if $fh eq $LINEINFO # slave editor
+ and $LINEINFO eq $OUT # normal output
+ and $term_pid != $$; # not the primary
+
+ # Collect the actual trace information to be formatted.
+ # This is an array of hashes of subroutine call info.
+ my @sub = dump_trace( $_[0] + 1, $_[1] );
+
+ # Grab the "short report" flag from @_.
+ my $short = $_[2]; # Print short report, next one for sub name
+
+ # Run through the traceback info, format it, and print it.
+ my $s;
+ for ( $i = 0 ; $i <= $#sub ; $i++ ) {
+
+ # Drop out if the user has lost interest and hit control-C.
+ last if $signal;
+
+ # Set the separator so arrys print nice.
+ local $" = ', ';
+
+ # Grab and stringify the arguments if they are there.
+ my $args =
+ defined $sub[$i]{args}
+ ? "(@{ $sub[$i]{args} })"
+ : '';
+
+ # Shorten them up if $maxtrace says they're too long.
+ $args = ( substr $args, 0, $maxtrace - 3 ) . '...'
+ if length $args > $maxtrace;
+
+ # Get the file name.
+ my $file = $sub[$i]{file};
+
+ # Put in a filename header if short is off.
+ $file = $file eq '-e' ? $file : "file `$file'" unless $short;
+
+ # Get the actual sub's name, and shorten to $maxtrace's requirement.
+ $s = $sub[$i]{sub};
+ $s = ( substr $s, 0, $maxtrace - 3 ) . '...' if length $s > $maxtrace;
+
+ # Short report uses trimmed file and sub names.
+ if ($short) {
+ my $sub = @_ >= 4 ? $_[3] : $s;
+ print $fh "$sub[$i]{context}=$sub$args from $file:$sub[$i]{line}\n";
+ } ## end if ($short)
+
+ # Non-short report includes full names.
+ else {
+ print $fh "$sub[$i]{context} = $s$args"
+ . " called from $file"
+ . " line $sub[$i]{line}\n";
+ }
+ } ## end for ($i = 0 ; $i <= $#sub...
+} ## end sub print_trace
+
+=head2 dump_trace(skip[,count])
+
+Actually collect the traceback information available via C<caller()>. It does
+some filtering and cleanup of the data, but mostly it just collects it to
+make C<print_trace()>'s job easier.
+
+C<skip> defines the number of stack frames to be skipped, working backwards
+from the most current. C<count> determines the total number of frames to
+be returned; all of them (well, the first 10^9) are returned if C<count>
+is omitted.
+
+This routine returns a list of hashes, from most-recent to least-recent
+stack frame. Each has the following keys and values:
+
+=over 4
+
+=item * C<context> - C<.> (null), C<$> (scalar), or C<@> (array)
+
+=item * C<sub> - subroutine name, or C<eval> information
+
+=item * C<args> - undef, or a reference to an array of arguments
+
+=item * C<file> - the file in which this item was defined (if any)
+
+=item * C<line> - the line on which it was defined
+
+=back
+
+=cut
+
+sub dump_trace {
+
+ # How many levels to skip.
+ my $skip = shift;
+
+ # How many levels to show. (1e9 is a cheap way of saying "all of them";
+ # it's unlikely that we'll have more than a billion stack frames. If you
+ # do, you've got an awfully big machine...)
+ my $count = shift || 1e9;
+
+ # We increment skip because caller(1) is the first level *back* from
+ # the current one. Add $skip to the count of frames so we have a
+ # simple stop criterion, counting from $skip to $count+$skip.
+ $skip++;
+ $count += $skip;
+
+ # These variables are used to capture output from caller();
+ my ( $p, $file, $line, $sub, $h, $context );
+
+ my ( $e, $r, @a, @sub, $args );
+
+ # XXX Okay... why'd we do that?
+ my $nothard = not $frame & 8;
+ local $frame = 0;
+
+ # Do not want to trace this.
+ my $otrace = $trace;
+ $trace = 0;
+
+ # Start out at the skip count.
+ # If we haven't reached the number of frames requested, and caller() is
+ # still returning something, stay in the loop. (If we pass the requested
+ # number of stack frames, or we run out - caller() returns nothing - we
+ # quit.
+ # Up the stack frame index to go back one more level each time.
+ for (
+ $i = $skip ;
+ $i < $count
+ and ( $p, $file, $line, $sub, $h, $context, $e, $r ) = caller($i) ;
+ $i++
+ )
+ {
+
+ # Go through the arguments and save them for later.
+ @a = ();
+ for $arg (@args) {
+ my $type;
+ if ( not defined $arg ) { # undefined parameter
+ push @a, "undef";
+ }
+
+ elsif ( $nothard and tied $arg ) { # tied parameter
+ push @a, "tied";
+ }
+ elsif ( $nothard and $type = ref $arg ) { # reference
+ push @a, "ref($type)";
+ }
+ else { # can be stringified
+ local $_ =
+ "$arg"; # Safe to stringify now - should not call f().
+
+ # Backslash any single-quotes or backslashes.
+ s/([\'\\])/\\$1/g;
+
+ # Single-quote it unless it's a number or a colon-separated
+ # name.
+ s/(.*)/'$1'/s
+ unless /^(?: -?[\d.]+ | \*[\w:]* )$/x;
+
+ # Turn high-bit characters into meta-whatever.
+ s/([\200-\377])/sprintf("M-%c",ord($1)&0177)/eg;
+
+ # Turn control characters into ^-whatever.
+ s/([\0-\37\177])/sprintf("^%c",ord($1)^64)/eg;
+
+ push( @a, $_ );
+ } ## end else [ if (not defined $arg)
+ } ## end for $arg (@args)
+
+ # If context is true, this is array (@)context.
+ # If context is false, this is scalar ($) context.
+ # If neither, context isn't defined. (This is apparently a 'can't
+ # happen' trap.)
+ $context = $context ? '@' : ( defined $context ? "\$" : '.' );
+
+ # if the sub has args ($h true), make an anonymous array of the
+ # dumped args.
+ $args = $h ? [@a] : undef;
+
+ # remove trailing newline-whitespace-semicolon-end of line sequence
+ # from the eval text, if any.
+ $e =~ s/\n\s*\;\s*\Z// if $e;
+
+ # Escape backslashed single-quotes again if necessary.
+ $e =~ s/([\\\'])/\\$1/g if $e;
+
+ # if the require flag is true, the eval text is from a require.
+ if ($r) {
+ $sub = "require '$e'";
+ }
+
+ # if it's false, the eval text is really from an eval.
+ elsif ( defined $r ) {
+ $sub = "eval '$e'";
+ }
+
+ # If the sub is '(eval)', this is a block eval, meaning we don't
+ # know what the eval'ed text actually was.
+ elsif ( $sub eq '(eval)' ) {
+ $sub = "eval {...}";
+ }
+
+ # Stick the collected information into @sub as an anonymous hash.
+ push(
+ @sub,
+ {
+ context => $context,
+ sub => $sub,
+ args => $args,
+ file => $file,
+ line => $line
+ }
+ );
+
+ # Stop processing frames if the user hit control-C.
+ last if $signal;
+ } ## end for ($i = $skip ; $i < ...
+
+ # Restore the trace value again.
+ $trace = $otrace;
+ @sub;
+} ## end sub dump_trace
+
+=head2 C<action()>
+
+C<action()> takes input provided as the argument to an add-action command,
+either pre- or post-, and makes sure it's a complete command. It doesn't do
+any fancy parsing; it just keeps reading input until it gets a string
+without a trailing backslash.
+
+=cut
+
+sub action {
+ my $action = shift;
+
+ while ( $action =~ s/\\$// ) {
+
+ # We have a backslash on the end. Read more.
+ $action .= &gets;
+ } ## end while ($action =~ s/\\$//)
+
+ # Return the assembled action.
+ $action;
+} ## end sub action
+
+=head2 unbalanced
+
+This routine mostly just packages up a regular expression to be used
+to check that the thing it's being matched against has properly-matched
+curly braces.
+
+Of note is the definition of the C<$balanced_brace_re> global via C<||=>, which
+speeds things up by only creating the qr//'ed expression once; if it's
+already defined, we don't try to define it again. A speed hack.
+
+=cut
+
+sub unbalanced {
+
+ # I hate using globals!
+ $balanced_brace_re ||= qr{
+ ^ \{
+ (?:
+ (?> [^{}] + ) # Non-parens without backtracking
+ |
+ (??{ $balanced_brace_re }) # Group with matching parens
+ ) *
+ \} $
+ }x;
+ return $_[0] !~ m/$balanced_brace_re/;
+} ## end sub unbalanced
+
+=head2 C<gets()>
+
+C<gets()> is a primitive (very primitive) routine to read continuations.
+It was devised for reading continuations for actions.
+it just reads more input with C<readline()> and returns it.
+
+=cut
+
+sub gets {
+ &readline("cont: ");
+}
+
+=head2 C<DB::system()> - handle calls to<system()> without messing up the debugger
+
+The C<system()> function assumes that it can just go ahead and use STDIN and
+STDOUT, but under the debugger, we want it to use the debugger's input and
+outout filehandles.
+
+C<DB::system()> socks away the program's STDIN and STDOUT, and then substitutes
+the debugger's IN and OUT filehandles for them. It does the C<system()> call,
+and then puts everything back again.
+
+=cut
+
+sub system {
+
+ # We save, change, then restore STDIN and STDOUT to avoid fork() since
+ # some non-Unix systems can do system() but have problems with fork().
+ open( SAVEIN, "<&STDIN" ) || &warn("Can't save STDIN");
+ open( SAVEOUT, ">&STDOUT" ) || &warn("Can't save STDOUT");
+ open( STDIN, "<&IN" ) || &warn("Can't redirect STDIN");
+ open( STDOUT, ">&OUT" ) || &warn("Can't redirect STDOUT");
+
+ # XXX: using csh or tcsh destroys sigint retvals!
+ system(@_);
+ open( STDIN, "<&SAVEIN" ) || &warn("Can't restore STDIN");
+ open( STDOUT, ">&SAVEOUT" ) || &warn("Can't restore STDOUT");
+ close(SAVEIN);
+ close(SAVEOUT);
+
+ # most of the $? crud was coping with broken cshisms
+ if ( $? >> 8 ) {
+ &warn( "(Command exited ", ( $? >> 8 ), ")\n" );
+ }
+ elsif ($?) {
+ &warn(
+ "(Command died of SIG#",
+ ( $? & 127 ),
+ ( ( $? & 128 ) ? " -- core dumped" : "" ),
+ ")", "\n"
+ );
+ } ## end elsif ($?)
+
+ return $?;
+
+} ## end sub system
+
+=head1 TTY MANAGEMENT
+
+The subs here do some of the terminal management for multiple debuggers.
+
+=head2 setterm
+
+Top-level function called when we want to set up a new terminal for use
+by the debugger.
+
+If the C<noTTY> debugger option was set, we'll either use the terminal
+supplied (the value of the C<noTTY> option), or we'll use C<Term::Rendezvous>
+to find one. If we're a forked debugger, we call C<resetterm> to try to
+get a whole new terminal if we can.
+
+In either case, we set up the terminal next. If the C<ReadLine> option was
+true, we'll get a C<Term::ReadLine> object for the current terminal and save
+the appropriate attributes. We then
+
+=cut
+
+sub setterm {
+
+ # Load Term::Readline, but quietly; don't debug it and don't trace it.
+ local $frame = 0;
+ local $doret = -2;
+ eval { require Term::ReadLine } or die $@;
+
+ # If noTTY is set, but we have a TTY name, go ahead and hook up to it.
+ if ($notty) {
+ if ($tty) {
+ my ( $i, $o ) = split $tty, /,/;
+ $o = $i unless defined $o;
+ open( IN, "<$i" ) or die "Cannot open TTY `$i' for read: $!";
+ open( OUT, ">$o" ) or die "Cannot open TTY `$o' for write: $!";
+ $IN = \*IN;
+ $OUT = \*OUT;
+ my $sel = select($OUT);
+ $| = 1;
+ select($sel);
+ } ## end if ($tty)
+
+ # We don't have a TTY - try to find one via Term::Rendezvous.
+ else {
+ eval "require Term::Rendezvous;" or die;
+
+ # See if we have anything to pass to Term::Rendezvous.
+ # Use $HOME/.perldbtty$$ if not.
+ my $rv = $ENV{PERLDB_NOTTY} || "$ENV{HOME}/.perldbtty$$";
+
+ # Rendezvous and get the filehandles.
+ my $term_rv = new Term::Rendezvous $rv;
+ $IN = $term_rv->IN;
+ $OUT = $term_rv->OUT;
+ } ## end else [ if ($tty)
+ } ## end if ($notty)
+
+ # We're a daughter debugger. Try to fork off another TTY.
+ if ( $term_pid eq '-1' ) { # In a TTY with another debugger
+ resetterm(2);
+ }
+
+ # If we shouldn't use Term::ReadLine, don't.
+ if ( !$rl ) {
+ $term = new Term::ReadLine::Stub 'perldb', $IN, $OUT;
+ }
+
+ # We're using Term::ReadLine. Get all the attributes for this terminal.
+ else {
+ $term = new Term::ReadLine 'perldb', $IN, $OUT;
+
+ $rl_attribs = $term->Attribs;
+ $rl_attribs->{basic_word_break_characters} .= '-:+/*,[])}'
+ if defined $rl_attribs->{basic_word_break_characters}
+ and index( $rl_attribs->{basic_word_break_characters}, ":" ) == -1;
+ $rl_attribs->{special_prefixes} = '$@&%';
+ $rl_attribs->{completer_word_break_characters} .= '$@&%';
+ $rl_attribs->{completion_function} = \&db_complete;
+ } ## end else [ if (!$rl)
+
+ # Set up the LINEINFO filehandle.
+ $LINEINFO = $OUT unless defined $LINEINFO;
+ $lineinfo = $console unless defined $lineinfo;
+
+ $term->MinLine(2);
+
+ if ( $term->Features->{setHistory} and "@hist" ne "?" ) {
+ $term->SetHistory(@hist);
+ }
+
+ # XXX Ornaments are turned on unconditionally, which is not
+ # always a good thing.
+ ornaments($ornaments) if defined $ornaments;
+ $term_pid = $$;
+} ## end sub setterm
+
+=head1 GET_FORK_TTY EXAMPLE FUNCTIONS
+
+When the process being debugged forks, or the process invokes a command
+via C<system()> which starts a new debugger, we need to be able to get a new
+C<IN> and C<OUT> filehandle for the new debugger. Otherwise, the two processes
+fight over the terminal, and you can never quite be sure who's going to get the
+input you're typing.
+
+C<get_fork_TTY> is a glob-aliased function which calls the real function that
+is tasked with doing all the necessary operating system mojo to get a new
+TTY (and probably another window) and to direct the new debugger to read and
+write there.
+
+The debugger provides C<get_fork_TTY> functions which work for X Windows and
+OS/2. Other systems are not supported. You are encouraged to write
+C<get_fork_TTY> functions which work for I<your> platform and contribute them.
+
+=head3 C<xterm_get_fork_TTY>
+
+This function provides the C<get_fork_TTY> function for X windows. If a
+program running under the debugger forks, a new <xterm> window is opened and
+the subsidiary debugger is directed there.
+
+The C<open()> call is of particular note here. We have the new C<xterm>
+we're spawning route file number 3 to STDOUT, and then execute the C<tty>
+command (which prints the device name of the TTY we'll want to use for input
+and output to STDOUT, then C<sleep> for a very long time, routing this output
+to file number 3. This way we can simply read from the <XT> filehandle (which
+is STDOUT from the I<commands> we ran) to get the TTY we want to use.
+
+Only works if C<xterm> is in your path and C<$ENV{DISPLAY}>, etc. are
+properly set up.
+
+=cut
+
+sub xterm_get_fork_TTY {
+ ( my $name = $0 ) =~ s,^.*[/\\],,s;
+ open XT,
+qq[3>&1 xterm -title "Daughter Perl debugger $pids $name" -e sh -c 'tty 1>&3;\
+ sleep 10000000' |];
+
+ # Get the output from 'tty' and clean it up a little.
+ my $tty = <XT>;
+ chomp $tty;
+
+ $pidprompt = ''; # Shown anyway in titlebar
+
+ # There's our new TTY.
+ return $tty;
+} ## end sub xterm_get_fork_TTY
+
+=head3 C<os2_get_fork_TTY>
+
+XXX It behooves an OS/2 expert to write the necessary documentation for this!
+
+=cut
+
+# This example function resets $IN, $OUT itself
+sub os2_get_fork_TTY {
+ local $^F = 40; # XXXX Fixme!
+ local $\ = '';
+ my ( $in1, $out1, $in2, $out2 );
+
+ # Having -d in PERL5OPT would lead to a disaster...
+ local $ENV{PERL5OPT} = $ENV{PERL5OPT} if $ENV{PERL5OPT};
+ $ENV{PERL5OPT} =~ s/(?:^|(?<=\s))-d\b// if $ENV{PERL5OPT};
+ $ENV{PERL5OPT} =~ s/(?:^|(?<=\s))-d\B/-/ if $ENV{PERL5OPT};
+ print $OUT "Making kid PERL5OPT->`$ENV{PERL5OPT}'.\n" if $ENV{PERL5OPT};
+ local $ENV{PERL5LIB} = $ENV{PERL5LIB} ? $ENV{PERL5LIB} : $ENV{PERLLIB};
+ $ENV{PERL5LIB} = '' unless defined $ENV{PERL5LIB};
+ $ENV{PERL5LIB} = join ';', @ini_INC, split /;/, $ENV{PERL5LIB};
+ ( my $name = $0 ) =~ s,^.*[/\\],,s;
+ my @args;
+
+ if (
+ pipe $in1, $out1
+ and pipe $in2, $out2
+
+ # system P_SESSION will fail if there is another process
+ # in the same session with a "dependent" asynchronous child session.
+ and @args = (
+ $rl, fileno $in1, fileno $out2, "Daughter Perl debugger $pids $name"
+ )
+ and (
+ ( $kpid = CORE::system 4, $^X, '-we',
+ <<'ES', @args ) >= 0 # P_SESSION
+END {sleep 5 unless $loaded}
+BEGIN {open STDIN, '</dev/con' or warn "reopen stdin: $!"}
+use OS2::Process;
+
+my ($rl, $in) = (shift, shift); # Read from $in and pass through
+set_title pop;
+system P_NOWAIT, $^X, '-we', <<EOS or die "Cannot start a grandkid";
+ open IN, '<&=$in' or die "open <&=$in: \$!";
+ \$| = 1; print while sysread IN, \$_, 1<<16;
+EOS
+
+my $out = shift;
+open OUT, ">&=$out" or die "Cannot open &=$out for writing: $!";
+select OUT; $| = 1;
+require Term::ReadKey if $rl;
+Term::ReadKey::ReadMode(4) if $rl; # Nodelay on kbd. Pipe is automatically nodelay...
+print while sysread STDIN, $_, 1<<($rl ? 16 : 0);
+ES
+ or warn "system P_SESSION: $!, $^E" and 0
+ )
+ and close $in1
+ and close $out2
+ )
+ {
+ $pidprompt = ''; # Shown anyway in titlebar
+ reset_IN_OUT( $in2, $out1 );
+ $tty = '*reset*';
+ return ''; # Indicate that reset_IN_OUT is called
+ } ## end if (pipe $in1, $out1 and...
+ return;
+} ## end sub os2_get_fork_TTY
+
+=head2 C<create_IN_OUT($flags)>
+
+Create a new pair of filehandles, pointing to a new TTY. If impossible,
+try to diagnose why.
+
+Flags are:
+
+=over 4
+
+=item * 1 - Don't know how to create a new TTY.
+
+=item * 2 - Debugger has forked, but we can't get a new TTY.
+
+=item * 4 - standard debugger startup is happening.
+
+=back
+
+=cut
+
+sub create_IN_OUT { # Create a window with IN/OUT handles redirected there
+
+ # If we know how to get a new TTY, do it! $in will have
+ # the TTY name if get_fork_TTY works.
+ my $in = &get_fork_TTY if defined &get_fork_TTY;
+
+ # It used to be that
+ $in = $fork_TTY if defined $fork_TTY; # Backward compatibility
+
+ if ( not defined $in ) {
+ my $why = shift;
+
+ # We don't know how.
+ print_help(<<EOP) if $why == 1;
+I<#########> Forked, but do not know how to create a new B<TTY>. I<#########>
+EOP
+
+ # Forked debugger.
+ print_help(<<EOP) if $why == 2;
+I<#########> Daughter session, do not know how to change a B<TTY>. I<#########>
+ This may be an asynchronous session, so the parent debugger may be active.
+EOP
+
+ # Note that both debuggers are fighting over the same input.
+ print_help(<<EOP) if $why != 4;
+ Since two debuggers fight for the same TTY, input is severely entangled.
+
+EOP
+ print_help(<<EOP);
+ I know how to switch the output to a different window in xterms
+ and OS/2 consoles only. For a manual switch, put the name of the created I<TTY>
+ in B<\$DB::fork_TTY>, or define a function B<DB::get_fork_TTY()> returning this.
+
+ On I<UNIX>-like systems one can get the name of a I<TTY> for the given window
+ by typing B<tty>, and disconnect the I<shell> from I<TTY> by B<sleep 1000000>.
+
+EOP
+ } ## end if (not defined $in)
+ elsif ( $in ne '' ) {
+ TTY($in);
+ }
+ else {
+ $console = ''; # Indicate no need to open-from-the-console
+ }
+ undef $fork_TTY;
+} ## end sub create_IN_OUT
+
+=head2 C<resetterm>
+
+Handles rejiggering the prompt when we've forked off a new debugger.
+
+If the new debugger happened because of a C<system()> that invoked a
+program under the debugger, the arrow between the old pid and the new
+in the prompt has I<two> dashes instead of one.
+
+We take the current list of pids and add this one to the end. If there
+isn't any list yet, we make one up out of the initial pid associated with
+the terminal and our new pid, sticking an arrow (either one-dashed or
+two dashed) in between them.
+
+If C<CreateTTY> is off, or C<resetterm> was called with no arguments,
+we don't try to create a new IN and OUT filehandle. Otherwise, we go ahead
+and try to do that.
+
+=cut
+
+sub resetterm { # We forked, so we need a different TTY
+
+ # Needs to be passed to create_IN_OUT() as well.
+ my $in = shift;
+
+ # resetterm(2): got in here because of a system() starting a debugger.
+ # resetterm(1): just forked.
+ my $systemed = $in > 1 ? '-' : '';
+
+ # If there's already a list of pids, add this to the end.
+ if ($pids) {
+ $pids =~ s/\]/$systemed->$$]/;
+ }
+
+ # No pid list. Time to make one.
+ else {
+ $pids = "[$term_pid->$$]";
+ }
+
+ # The prompt we're going to be using for this debugger.
+ $pidprompt = $pids;
+
+ # We now 0wnz this terminal.
+ $term_pid = $$;
+
+ # Just return if we're not supposed to try to create a new TTY.
+ return unless $CreateTTY & $in;
+
+ # Try to create a new IN/OUT pair.
+ create_IN_OUT($in);
+} ## end sub resetterm
+
+=head2 C<readline>
+
+First, we handle stuff in the typeahead buffer. If there is any, we shift off
+the next line, print a message saying we got it, add it to the terminal
+history (if possible), and return it.
+
+If there's nothing in the typeahead buffer, check the command filehandle stack.
+If there are any filehandles there, read from the last one, and return the line
+if we got one. If not, we pop the filehandle off and close it, and try the
+next one up the stack.
+
+If we've emptied the filehandle stack, we check to see if we've got a socket
+open, and we read that and return it if we do. If we don't, we just call the
+core C<readline()> and return its value.
+
+=cut
+
+sub readline {
+
+ # Localize to prevent it from being smashed in the program being debugged.
+ local $.;
+
+ # Pull a line out of the typeahead if there's stuff there.
+ if (@typeahead) {
+
+ # How many lines left.
+ my $left = @typeahead;
+
+ # Get the next line.
+ my $got = shift @typeahead;
+
+ # Print a message saying we got input from the typeahead.
+ local $\ = '';
+ print $OUT "auto(-$left)", shift, $got, "\n";
+
+ # Add it to the terminal history (if possible).
+ $term->AddHistory($got)
+ if length($got) > 1
+ and defined $term->Features->{addHistory};
+ return $got;
+ } ## end if (@typeahead)
+
+ # We really need to read some input. Turn off entry/exit trace and
+ # return value printing.
+ local $frame = 0;
+ local $doret = -2;
+
+ # If there are stacked filehandles to read from ...
+ while (@cmdfhs) {
+
+ # Read from the last one in the stack.
+ my $line = CORE::readline( $cmdfhs[-1] );
+
+ # If we got a line ...
+ defined $line
+ ? ( print $OUT ">> $line" and return $line ) # Echo and return
+ : close pop @cmdfhs; # Pop and close
+ } ## end while (@cmdfhs)
+
+ # Nothing on the filehandle stack. Socket?
+ if ( ref $OUT and UNIVERSAL::isa( $OUT, 'IO::Socket::INET' ) ) {
+
+ # Send anyting we have to send.
+ $OUT->write( join( '', @_ ) );
+
+ # Receive anything there is to receive.
+ my $stuff;
+ $IN->recv( $stuff, 2048 ); # XXX "what's wrong with sysread?"
+ # XXX Don't know. You tell me.
+
+ # What we got.
+ $stuff;
+ } ## end if (ref $OUT and UNIVERSAL::isa...
+
+ # No socket. Just read from the terminal.
+ else {
+ $term->readline(@_);
+ }
+} ## end sub readline
+
+=head1 OPTIONS SUPPORT ROUTINES
+
+These routines handle listing and setting option values.
+
+=head2 C<dump_option> - list the current value of an option setting
+
+This routine uses C<option_val> to look up the value for an option.
+It cleans up escaped single-quotes and then displays the option and
+its value.
+
+=cut
+
+sub dump_option {
+ my ( $opt, $val ) = @_;
+ $val = option_val( $opt, 'N/A' );
+ $val =~ s/([\\\'])/\\$1/g;
+ printf $OUT "%20s = '%s'\n", $opt, $val;
+} ## end sub dump_option
+
+sub options2remember {
+ foreach my $k (@RememberOnROptions) {
+ $option{$k} = option_val( $k, 'N/A' );
+ }
+ return %option;
+}
+
+=head2 C<option_val> - find the current value of an option
+
+This can't just be a simple hash lookup because of the indirect way that
+the option values are stored. Some are retrieved by calling a subroutine,
+some are just variables.
+
+You must supply a default value to be used in case the option isn't set.
+
+=cut
+
+sub option_val {
+ my ( $opt, $default ) = @_;
+ my $val;
+
+ # Does this option exist, and is it a variable?
+ # If so, retrieve the value via the value in %optionVars.
+ if ( defined $optionVars{$opt}
+ and defined ${ $optionVars{$opt} } )
+ {
+ $val = ${ $optionVars{$opt} };
+ }
+
+ # Does this option exist, and it's a subroutine?
+ # If so, call the subroutine via the ref in %optionAction
+ # and capture the value.
+ elsif ( defined $optionAction{$opt}
+ and defined &{ $optionAction{$opt} } )
+ {
+ $val = &{ $optionAction{$opt} }();
+ }
+
+ # If there's an action or variable for the supplied option,
+ # but no value was set, use the default.
+ elsif (defined $optionAction{$opt} and not defined $option{$opt}
+ or defined $optionVars{$opt} and not defined ${ $optionVars{$opt} } )
+ {
+ $val = $default;
+ }
+
+ # Otherwise, do the simple hash lookup.
+ else {
+ $val = $option{$opt};
+ }
+
+ # If the value isn't defined, use the default.
+ # Then return whatever the value is.
+ $val = $default unless defined $val;
+ $val;
+} ## end sub option_val
+
+=head2 C<parse_options>
+
+Handles the parsing and execution of option setting/displaying commands.
+
+An option entered by itself is assumed to be I<set me to 1> (the default value)
+if the option is a boolean one. If not, the user is prompted to enter a valid
+value or to query the current value (via C<option? >).
+
+If C<option=value> is entered, we try to extract a quoted string from the
+value (if it is quoted). If it's not, we just use the whole value as-is.
+
+We load any modules required to service this option, and then we set it: if
+it just gets stuck in a variable, we do that; if there's a subroutine to
+handle setting the option, we call that.
+
+Finally, if we're running in interactive mode, we display the effect of the
+user's command back to the terminal, skipping this if we're setting things
+during initialization.
+
+=cut
+
+sub parse_options {
+ local ($_) = @_;
+ local $\ = '';
+
+ # These options need a value. Don't allow them to be clobbered by accident.
+ my %opt_needs_val = map { ( $_ => 1 ) } qw{
+ dumpDepth arrayDepth hashDepth LineInfo maxTraceLen ornaments windowSize
+ pager quote ReadLine recallCommand RemotePort ShellBang TTY CommandSet
+ };
+
+ while (length) {
+ my $val_defaulted;
+
+ # Clean off excess leading whitespace.
+ s/^\s+// && next;
+
+ # Options are always all word characters, followed by a non-word
+ # separator.
+ s/^(\w+)(\W?)// or print( $OUT "Invalid option `$_'\n" ), last;
+ my ( $opt, $sep ) = ( $1, $2 );
+
+ # Make sure that such an option exists.
+ my $matches = grep( /^\Q$opt/ && ( $option = $_ ), @options )
+ || grep( /^\Q$opt/i && ( $option = $_ ), @options );
+
+ print( $OUT "Unknown option `$opt'\n" ), next unless $matches;
+ print( $OUT "Ambiguous option `$opt'\n" ), next if $matches > 1;
+ my $val;
+
+ # '?' as separator means query, but must have whitespace after it.
+ if ( "?" eq $sep ) {
+ print( $OUT "Option query `$opt?' followed by non-space `$_'\n" ),
+ last
+ if /^\S/;
+
+ #&dump_option($opt);
+ } ## end if ("?" eq $sep)
+
+ # Separator is whitespace (or just a carriage return).
+ # They're going for a default, which we assume is 1.
+ elsif ( $sep !~ /\S/ ) {
+ $val_defaulted = 1;
+ $val = "1"; # this is an evil default; make 'em set it!
+ }
+
+ # Separator is =. Trying to set a value.
+ elsif ( $sep eq "=" ) {
+
+ # If quoted, extract a quoted string.
+ if (s/ (["']) ( (?: \\. | (?! \1 ) [^\\] )* ) \1 //x) {
+ my $quote = $1;
+ ( $val = $2 ) =~ s/\\([$quote\\])/$1/g;
+ }
+
+ # Not quoted. Use the whole thing. Warn about 'option='.
+ else {
+ s/^(\S*)//;
+ $val = $1;
+ print OUT qq(Option better cleared using $opt=""\n)
+ unless length $val;
+ } ## end else [ if (s/ (["']) ( (?: \\. | (?! \1 ) [^\\] )* ) \1 //x)
+
+ } ## end elsif ($sep eq "=")
+
+ # "Quoted" with [], <>, or {}.
+ else { #{ to "let some poor schmuck bounce on the % key in B<vi>."
+ my ($end) =
+ "\\" . substr( ")]>}$sep", index( "([<{", $sep ), 1 ); #}
+ s/^(([^\\$end]|\\[\\$end])*)$end($|\s+)//
+ or print( $OUT "Unclosed option value `$opt$sep$_'\n" ), last;
+ ( $val = $1 ) =~ s/\\([\\$end])/$1/g;
+ } ## end else [ if ("?" eq $sep)
+
+ # Exclude non-booleans from getting set to 1 by default.
+ if ( $opt_needs_val{$option} && $val_defaulted ) {
+ my $cmd = ( $CommandSet eq '580' ) ? 'o' : 'O';
+ print $OUT
+"Option `$opt' is non-boolean. Use `$cmd $option=VAL' to set, `$cmd $option?' to query\n";
+ next;
+ } ## end if ($opt_needs_val{$option...
+
+ # Save the option value.
+ $option{$option} = $val if defined $val;
+
+ # Load any module that this option requires.
+ eval qq{
+ local \$frame = 0;
+ local \$doret = -2;
+ require '$optionRequire{$option}';
+ 1;
+ } || die # XXX: shouldn't happen
+ if defined $optionRequire{$option}
+ && defined $val;
+
+ # Set it.
+ # Stick it in the proper variable if it goes in a variable.
+ ${ $optionVars{$option} } = $val
+ if defined $optionVars{$option}
+ && defined $val;
+
+ # Call the appropriate sub if it gets set via sub.
+ &{ $optionAction{$option} }($val)
+ if defined $optionAction{$option}
+ && defined &{ $optionAction{$option} }
+ && defined $val;
+
+ # Not initialization - echo the value we set it to.
+ dump_option($option) unless $OUT eq \*STDERR;
+ } ## end while (length)
+} ## end sub parse_options
+
+=head1 RESTART SUPPORT
+
+These routines are used to store (and restore) lists of items in environment
+variables during a restart.
+
+=head2 set_list
+
+Set_list packages up items to be stored in a set of environment variables
+(VAR_n, containing the number of items, and VAR_0, VAR_1, etc., containing
+the values). Values outside the standard ASCII charset are stored by encoding
+then as hexadecimal values.
+
+=cut
+
+sub set_list {
+ my ( $stem, @list ) = @_;
+ my $val;
+
+ # VAR_n: how many we have. Scalar assignment gets the number of items.
+ $ENV{"${stem}_n"} = @list;
+
+ # Grab each item in the list, escape the backslashes, encode the non-ASCII
+ # as hex, and then save in the appropriate VAR_0, VAR_1, etc.
+ for $i ( 0 .. $#list ) {
+ $val = $list[$i];
+ $val =~ s/\\/\\\\/g;
+ $val =~ s/([\0-\37\177\200-\377])/"\\0x" . unpack('H2',$1)/eg;
+ $ENV{"${stem}_$i"} = $val;
+ } ## end for $i (0 .. $#list)
+} ## end sub set_list
+
+=head2 get_list
+
+Reverse the set_list operation: grab VAR_n to see how many we should be getting
+back, and then pull VAR_0, VAR_1. etc. back out.
+
+=cut
+
+sub get_list {
+ my $stem = shift;
+ my @list;
+ my $n = delete $ENV{"${stem}_n"};
+ my $val;
+ for $i ( 0 .. $n - 1 ) {
+ $val = delete $ENV{"${stem}_$i"};
+ $val =~ s/\\((\\)|0x(..))/ $2 ? $2 : pack('H2', $3) /ge;
+ push @list, $val;
+ }
+ @list;
+} ## end sub get_list
+
+=head1 MISCELLANEOUS SIGNAL AND I/O MANAGEMENT
+
+=head2 catch()
+
+The C<catch()> subroutine is the essence of fast and low-impact. We simply
+set an already-existing global scalar variable to a constant value. This
+avoids allocating any memory possibly in the middle of something that will
+get all confused if we do, particularly under I<unsafe signals>.
+
+=cut
+
+sub catch {
+ $signal = 1;
+ return; # Put nothing on the stack - malloc/free land!
+}
+
+=head2 C<warn()>
+
+C<warn> emits a warning, by joining together its arguments and printing
+them, with couple of fillips.
+
+If the composited message I<doesn't> end with a newline, we automatically
+add C<$!> and a newline to the end of the message. The subroutine expects $OUT
+to be set to the filehandle to be used to output warnings; it makes no
+assumptions about what filehandles are available.
+
+=cut
+
+sub warn {
+ my ($msg) = join( "", @_ );
+ $msg .= ": $!\n" unless $msg =~ /\n$/;
+ local $\ = '';
+ print $OUT $msg;
+} ## end sub warn
+
+=head1 INITIALIZATION TTY SUPPORT
+
+=head2 C<reset_IN_OUT>
+
+This routine handles restoring the debugger's input and output filehandles
+after we've tried and failed to move them elsewhere. In addition, it assigns
+the debugger's output filehandle to $LINEINFO if it was already open there.
+
+=cut
+
+sub reset_IN_OUT {
+ my $switch_li = $LINEINFO eq $OUT;
+
+ # If there's a term and it's able to get a new tty, try to get one.
+ if ( $term and $term->Features->{newTTY} ) {
+ ( $IN, $OUT ) = ( shift, shift );
+ $term->newTTY( $IN, $OUT );
+ }
+
+ # This term can't get a new tty now. Better luck later.
+ elsif ($term) {
+ &warn("Too late to set IN/OUT filehandles, enabled on next `R'!\n");
+ }
+
+ # Set the filehndles up as they were.
+ else {
+ ( $IN, $OUT ) = ( shift, shift );
+ }
+
+ # Unbuffer the output filehandle.
+ my $o = select $OUT;
+ $| = 1;
+ select $o;
+
+ # Point LINEINFO to the same output filehandle if it was there before.
+ $LINEINFO = $OUT if $switch_li;
+} ## end sub reset_IN_OUT
+
+=head1 OPTION SUPPORT ROUTINES
+
+The following routines are used to process some of the more complicated
+debugger options.
+
+=head2 C<TTY>
+
+Sets the input and output filehandles to the specified files or pipes.
+If the terminal supports switching, we go ahead and do it. If not, and
+there's already a terminal in place, we save the information to take effect
+on restart.
+
+If there's no terminal yet (for instance, during debugger initialization),
+we go ahead and set C<$console> and C<$tty> to the file indicated.
+
+=cut
+
+sub TTY {
+ if ( @_ and $term and $term->Features->{newTTY} ) {
+
+ # This terminal supports switching to a new TTY.
+ # Can be a list of two files, or on string containing both names,
+ # comma-separated.
+ # XXX Should this perhaps be an assignment from @_?
+ my ( $in, $out ) = shift;
+ if ( $in =~ /,/ ) {
+
+ # Split list apart if supplied.
+ ( $in, $out ) = split /,/, $in, 2;
+ }
+ else {
+
+ # Use the same file for both input and output.
+ $out = $in;
+ }
+
+ # Open file onto the debugger's filehandles, if you can.
+ open IN, $in or die "cannot open `$in' for read: $!";
+ open OUT, ">$out" or die "cannot open `$out' for write: $!";
+
+ # Swap to the new filehandles.
+ reset_IN_OUT( \*IN, \*OUT );
+
+ # Save the setting for later.
+ return $tty = $in;
+ } ## end if (@_ and $term and $term...
+
+ # Terminal doesn't support new TTY, or doesn't support readline.
+ # Can't do it now, try restarting.
+ &warn("Too late to set TTY, enabled on next `R'!\n") if $term and @_;
+
+ # Useful if done through PERLDB_OPTS:
+ $console = $tty = shift if @_;
+
+ # Return whatever the TTY is.
+ $tty or $console;
+} ## end sub TTY
+
+=head2 C<noTTY>
+
+Sets the C<$notty> global, controlling whether or not the debugger tries to
+get a terminal to read from. If called after a terminal is already in place,
+we save the value to use it if we're restarted.
+
+=cut
+
+sub noTTY {
+ if ($term) {
+ &warn("Too late to set noTTY, enabled on next `R'!\n") if @_;
+ }
+ $notty = shift if @_;
+ $notty;
+} ## end sub noTTY
+
+=head2 C<ReadLine>
+
+Sets the C<$rl> option variable. If 0, we use C<Term::ReadLine::Stub>
+(essentially, no C<readline> processing on this I<terminal>). Otherwise, we
+use C<Term::ReadLine>. Can't be changed after a terminal's in place; we save
+the value in case a restart is done so we can change it then.
+
+=cut
+
+sub ReadLine {
+ if ($term) {
+ &warn("Too late to set ReadLine, enabled on next `R'!\n") if @_;
+ }
+ $rl = shift if @_;
+ $rl;
+} ## end sub ReadLine
+
+=head2 C<RemotePort>
+
+Sets the port that the debugger will try to connect to when starting up.
+If the terminal's already been set up, we can't do it, but we remember the
+setting in case the user does a restart.
+
+=cut
+
+sub RemotePort {
+ if ($term) {
+ &warn("Too late to set RemotePort, enabled on next 'R'!\n") if @_;
+ }
+ $remoteport = shift if @_;
+ $remoteport;
+} ## end sub RemotePort
+
+=head2 C<tkRunning>
+
+Checks with the terminal to see if C<Tk> is running, and returns true or
+false. Returns false if the current terminal doesn't support C<readline>.
+
+=cut
+
+sub tkRunning {
+ if ( ${ $term->Features }{tkRunning} ) {
+ return $term->tkRunning(@_);
+ }
+ else {
+ local $\ = '';
+ print $OUT "tkRunning not supported by current ReadLine package.\n";
+ 0;
+ }
+} ## end sub tkRunning
+
+=head2 C<NonStop>
+
+Sets nonstop mode. If a terminal's already been set up, it's too late; the
+debugger remembers the setting in case you restart, though.
+
+=cut
+
+sub NonStop {
+ if ($term) {
+ &warn("Too late to set up NonStop mode, enabled on next `R'!\n")
+ if @_;
+ }
+ $runnonstop = shift if @_;
+ $runnonstop;
+} ## end sub NonStop
+
+sub DollarCaretP {
+ if ($term) {
+ &warn("Some flag changes could not take effect until next 'R'!\n")
+ if @_;
+ }
+ $^P = parse_DollarCaretP_flags(shift) if @_;
+ expand_DollarCaretP_flags($^P);
+}
+
+sub OnlyAssertions {
+ if ($term) {
+ &warn("Too late to set up OnlyAssertions mode, enabled on next 'R'!\n")
+ if @_;
+ }
+ if (@_) {
+ unless ( defined $ini_assertion ) {
+ if ($term) {
+ &warn("Current Perl interpreter doesn't support assertions");
+ }
+ return 0;
+ }
+ if (shift) {
+ unless ($ini_assertion) {
+ print "Assertions will be active on next 'R'!\n";
+ $ini_assertion = 1;
+ }
+ $^P &= ~$DollarCaretP_flags{PERLDBf_SUB};
+ $^P |= $DollarCaretP_flags{PERLDBf_ASSERTION};
+ }
+ else {
+ $^P |= $DollarCaretP_flags{PERLDBf_SUB};
+ }
+ }
+ !( $^P & $DollarCaretP_flags{PERLDBf_SUB} ) || 0;
+}
+
+=head2 C<pager>
+
+Set up the C<$pager> variable. Adds a pipe to the front unless there's one
+there already.
+
+=cut
+
+sub pager {
+ if (@_) {
+ $pager = shift;
+ $pager = "|" . $pager unless $pager =~ /^(\+?\>|\|)/;
+ }
+ $pager;
+} ## end sub pager
+
+=head2 C<shellBang>
+
+Sets the shell escape command, and generates a printable copy to be used
+in the help.
+
+=cut
+
+sub shellBang {
+
+ # If we got an argument, meta-quote it, and add '\b' if it
+ # ends in a word character.
+ if (@_) {
+ $sh = quotemeta shift;
+ $sh .= "\\b" if $sh =~ /\w$/;
+ }
+
+ # Generate the printable version for the help:
+ $psh = $sh; # copy it
+ $psh =~ s/\\b$//; # Take off trailing \b if any
+ $psh =~ s/\\(.)/$1/g; # De-escape
+ $psh; # return the printable version
+} ## end sub shellBang
+
+=head2 C<ornaments>
+
+If the terminal has its own ornaments, fetch them. Otherwise accept whatever
+was passed as the argument. (This means you can't override the terminal's
+ornaments.)
+
+=cut
+
+sub ornaments {
+ if ( defined $term ) {
+
+ # We don't want to show warning backtraces, but we do want die() ones.
+ local ( $warnLevel, $dieLevel ) = ( 0, 1 );
+
+ # No ornaments if the terminal doesn't support them.
+ return '' unless $term->Features->{ornaments};
+ eval { $term->ornaments(@_) } || '';
+ }
+
+ # Use what was passed in if we can't determine it ourselves.
+ else {
+ $ornaments = shift;
+ }
+} ## end sub ornaments
+
+=head2 C<recallCommand>
+
+Sets the recall command, and builds a printable version which will appear in
+the help text.
+
+=cut
+
+sub recallCommand {
+
+ # If there is input, metaquote it. Add '\b' if it ends with a word
+ # character.
+ if (@_) {
+ $rc = quotemeta shift;
+ $rc .= "\\b" if $rc =~ /\w$/;
+ }
+
+ # Build it into a printable version.
+ $prc = $rc; # Copy it
+ $prc =~ s/\\b$//; # Remove trailing \b
+ $prc =~ s/\\(.)/$1/g; # Remove escapes
+ $prc; # Return the printable version
+} ## end sub recallCommand
+
+=head2 C<LineInfo> - where the line number information goes
+
+Called with no arguments, returns the file or pipe that line info should go to.
+
+Called with an argument (a file or a pipe), it opens that onto the
+C<LINEINFO> filehandle, unbuffers the filehandle, and then returns the
+file or pipe again to the caller.
+
+=cut
+
+sub LineInfo {
+ return $lineinfo unless @_;
+ $lineinfo = shift;
+
+ # If this is a valid "thing to be opened for output", tack a
+ # '>' onto the front.
+ my $stream = ( $lineinfo =~ /^(\+?\>|\|)/ ) ? $lineinfo : ">$lineinfo";
+
+ # If this is a pipe, the stream points to a slave editor.
+ $slave_editor = ( $stream =~ /^\|/ );
+
+ # Open it up and unbuffer it.
+ open( LINEINFO, "$stream" ) || &warn("Cannot open `$stream' for write");
+ $LINEINFO = \*LINEINFO;
+ my $save = select($LINEINFO);
+ $| = 1;
+ select($save);
+
+ # Hand the file or pipe back again.
+ $lineinfo;
+} ## end sub LineInfo
+
+=head1 COMMAND SUPPORT ROUTINES
+
+These subroutines provide functionality for various commands.
+
+=head2 C<list_modules>
+
+For the C<M> command: list modules loaded and their versions.
+Essentially just runs through the keys in %INC, picks each package's
+C<$VERSION> variable, gets the file name, and formats the information
+for output.
+
+=cut
+
+sub list_modules { # versions
+ my %version;
+ my $file;
+
+ # keys are the "as-loaded" name, values are the fully-qualified path
+ # to the file itself.
+ for ( keys %INC ) {
+ $file = $_; # get the module name
+ s,\.p[lm]$,,i; # remove '.pl' or '.pm'
+ s,/,::,g; # change '/' to '::'
+ s/^perl5db$/DB/; # Special case: debugger
+ # moves to package DB
+ s/^Term::ReadLine::readline$/readline/; # simplify readline
+
+ # If the package has a $VERSION package global (as all good packages
+ # should!) decode it and save as partial message.
+ if ( defined ${ $_ . '::VERSION' } ) {
+ $version{$file} = "${ $_ . '::VERSION' } from ";
+ }
+
+ # Finish up the message with the file the package came from.
+ $version{$file} .= $INC{$file};
+ } ## end for (keys %INC)
+
+ # Hey, dumpit() formats a hash nicely, so why not use it?
+ dumpit( $OUT, \%version );
+} ## end sub list_modules
+
+=head2 C<sethelp()>
+
+Sets up the monster string used to format and print the help.
+
+=head3 HELP MESSAGE FORMAT
+
+The help message is a peculiar format unto itself; it mixes C<pod> I<ornaments>
+(C<< B<> >> C<< I<> >>) with tabs to come up with a format that's fairly
+easy to parse and portable, but which still allows the help to be a little
+nicer than just plain text.
+
+Essentially, you define the command name (usually marked up with C<< B<> >>
+and C<< I<> >>), followed by a tab, and then the descriptive text, ending in a
+newline. The descriptive text can also be marked up in the same way. If you
+need to continue the descriptive text to another line, start that line with
+just tabs and then enter the marked-up text.
+
+If you are modifying the help text, I<be careful>. The help-string parser is
+not very sophisticated, and if you don't follow these rules it will mangle the
+help beyond hope until you fix the string.
+
+=cut
+
+sub sethelp {
+
+ # XXX: make sure there are tabs between the command and explanation,
+ # or print_help will screw up your formatting if you have
+ # eeevil ornaments enabled. This is an insane mess.
+
+ $help = "
+Help is currently only available for the new 5.8 command set.
+No help is available for the old command set.
+We assume you know what you're doing if you switch to it.
+
+B<T> Stack trace.
+B<s> [I<expr>] Single step [in I<expr>].
+B<n> [I<expr>] Next, steps over subroutine calls [in I<expr>].
+<B<CR>> Repeat last B<n> or B<s> command.
+B<r> Return from current subroutine.
+B<c> [I<line>|I<sub>] Continue; optionally inserts a one-time-only breakpoint
+ at the specified position.
+B<l> I<min>B<+>I<incr> List I<incr>+1 lines starting at I<min>.
+B<l> I<min>B<->I<max> List lines I<min> through I<max>.
+B<l> I<line> List single I<line>.
+B<l> I<subname> List first window of lines from subroutine.
+B<l> I<\$var> List first window of lines from subroutine referenced by I<\$var>.
+B<l> List next window of lines.
+B<-> List previous window of lines.
+B<v> [I<line>] View window around I<line>.
+B<.> Return to the executed line.
+B<f> I<filename> Switch to viewing I<filename>. File must be already loaded.
+ I<filename> may be either the full name of the file, or a regular
+ expression matching the full file name:
+ B<f> I</home/me/foo.pl> and B<f> I<oo\\.> may access the same file.
+ Evals (with saved bodies) are considered to be filenames:
+ B<f> I<(eval 7)> and B<f> I<eval 7\\b> access the body of the 7th eval
+ (in the order of execution).
+B</>I<pattern>B</> Search forwards for I<pattern>; final B</> is optional.
+B<?>I<pattern>B<?> Search backwards for I<pattern>; final B<?> is optional.
+B<L> [I<a|b|w>] List actions and or breakpoints and or watch-expressions.
+B<S> [[B<!>]I<pattern>] List subroutine names [not] matching I<pattern>.
+B<t> Toggle trace mode.
+B<t> I<expr> Trace through execution of I<expr>.
+B<b> Sets breakpoint on current line)
+B<b> [I<line>] [I<condition>]
+ Set breakpoint; I<line> defaults to the current execution line;
+ I<condition> breaks if it evaluates to true, defaults to '1'.
+B<b> I<subname> [I<condition>]
+ Set breakpoint at first line of subroutine.
+B<b> I<\$var> Set breakpoint at first line of subroutine referenced by I<\$var>.
+B<b> B<load> I<filename> Set breakpoint on 'require'ing the given file.
+B<b> B<postpone> I<subname> [I<condition>]
+ Set breakpoint at first line of subroutine after
+ it is compiled.
+B<b> B<compile> I<subname>
+ Stop after the subroutine is compiled.
+B<B> [I<line>] Delete the breakpoint for I<line>.
+B<B> I<*> Delete all breakpoints.
+B<a> [I<line>] I<command>
+ Set an action to be done before the I<line> is executed;
+ I<line> defaults to the current execution line.
+ Sequence is: check for breakpoint/watchpoint, print line
+ if necessary, do action, prompt user if necessary,
+ execute line.
+B<a> Does nothing
+B<A> [I<line>] Delete the action for I<line>.
+B<A> I<*> Delete all actions.
+B<w> I<expr> Add a global watch-expression.
+B<w> Does nothing
+B<W> I<expr> Delete a global watch-expression.
+B<W> I<*> Delete all watch-expressions.
+B<V> [I<pkg> [I<vars>]] List some (default all) variables in package (default current).
+ Use B<~>I<pattern> and B<!>I<pattern> for positive and negative regexps.
+B<X> [I<vars>] Same as \"B<V> I<currentpackage> [I<vars>]\".
+B<x> I<expr> Evals expression in list context, dumps the result.
+B<m> I<expr> Evals expression in list context, prints methods callable
+ on the first element of the result.
+B<m> I<class> Prints methods callable via the given class.
+B<M> Show versions of loaded modules.
+B<i> I<class> Prints nested parents of given class.
+B<e> Display current thread id.
+B<E> Display all thread ids the current one will be identified: <n>.
+B<y> [I<n> [I<Vars>]] List lexicals in higher scope <n>. Vars same as B<V>.
+B<P> Something to do with assertions...
+
+B<<> ? List Perl commands to run before each prompt.
+B<<> I<expr> Define Perl command to run before each prompt.
+B<<<> I<expr> Add to the list of Perl commands to run before each prompt.
+B<< *> Delete the list of perl commands to run before each prompt.
+B<>> ? List Perl commands to run after each prompt.
+B<>> I<expr> Define Perl command to run after each prompt.
+B<>>B<>> I<expr> Add to the list of Perl commands to run after each prompt.
+B<>>B< *> Delete the list of Perl commands to run after each prompt.
+B<{> I<db_command> Define debugger command to run before each prompt.
+B<{> ? List debugger commands to run before each prompt.
+B<{{> I<db_command> Add to the list of debugger commands to run before each prompt.
+B<{ *> Delete the list of debugger commands to run before each prompt.
+B<$prc> I<number> Redo a previous command (default previous command).
+B<$prc> I<-number> Redo number'th-to-last command.
+B<$prc> I<pattern> Redo last command that started with I<pattern>.
+ See 'B<O> I<recallCommand>' too.
+B<$psh$psh> I<cmd> Run cmd in a subprocess (reads from DB::IN, writes to DB::OUT)"
+ . (
+ $rc eq $sh
+ ? ""
+ : "
+B<$psh> [I<cmd>] Run I<cmd> in subshell (forces \"\$SHELL -c 'cmd'\")."
+ ) . "
+ See 'B<O> I<shellBang>' too.
+B<source> I<file> Execute I<file> containing debugger commands (may nest).
+B<save> I<file> Save current debugger session (actual history) to I<file>.
+B<rerun> Rerun session to current position.
+B<rerun> I<n> Rerun session to numbered command.
+B<rerun> I<-n> Rerun session to number'th-to-last command.
+B<H> I<-number> Display last number commands (default all).
+B<H> I<*> Delete complete history.
+B<p> I<expr> Same as \"I<print {DB::OUT} expr>\" in current package.
+B<|>I<dbcmd> Run debugger command, piping DB::OUT to current pager.
+B<||>I<dbcmd> Same as B<|>I<dbcmd> but DB::OUT is temporarilly select()ed as well.
+B<\=> [I<alias> I<value>] Define a command alias, or list current aliases.
+I<command> Execute as a perl statement in current package.
+B<R> Pure-man-restart of debugger, some of debugger state
+ and command-line options may be lost.
+ Currently the following settings are preserved:
+ history, breakpoints and actions, debugger B<O>ptions
+ and the following command-line options: I<-w>, I<-I>, I<-e>.
+
+B<o> [I<opt>] ... Set boolean option to true
+B<o> [I<opt>B<?>] Query options
+B<o> [I<opt>B<=>I<val>] [I<opt>=B<\">I<val>B<\">] ...
+ Set options. Use quotes in spaces in value.
+ I<recallCommand>, I<ShellBang> chars used to recall command or spawn shell;
+ I<pager> program for output of \"|cmd\";
+ I<tkRunning> run Tk while prompting (with ReadLine);
+ I<signalLevel> I<warnLevel> I<dieLevel> level of verbosity;
+ I<inhibit_exit> Allows stepping off the end of the script.
+ I<ImmediateStop> Debugger should stop as early as possible.
+ I<RemotePort> Remote hostname:port for remote debugging
+ The following options affect what happens with B<V>, B<X>, and B<x> commands:
+ I<arrayDepth>, I<hashDepth> print only first N elements ('' for all);
+ I<compactDump>, I<veryCompact> change style of array and hash dump;
+ I<globPrint> whether to print contents of globs;
+ I<DumpDBFiles> dump arrays holding debugged files;
+ I<DumpPackages> dump symbol tables of packages;
+ I<DumpReused> dump contents of \"reused\" addresses;
+ I<quote>, I<HighBit>, I<undefPrint> change style of string dump;
+ I<bareStringify> Do not print the overload-stringified value;
+ Other options include:
+ I<PrintRet> affects printing of return value after B<r> command,
+ I<frame> affects printing messages on subroutine entry/exit.
+ I<AutoTrace> affects printing messages on possible breaking points.
+ I<maxTraceLen> gives max length of evals/args listed in stack trace.
+ I<ornaments> affects screen appearance of the command line.
+ I<CreateTTY> bits control attempts to create a new TTY on events:
+ 1: on fork() 2: debugger is started inside debugger
+ 4: on startup
+ During startup options are initialized from \$ENV{PERLDB_OPTS}.
+ You can put additional initialization options I<TTY>, I<noTTY>,
+ I<ReadLine>, I<NonStop>, and I<RemotePort> there (or use
+ `B<R>' after you set them).
+
+B<q> or B<^D> Quit. Set B<\$DB::finished = 0> to debug global destruction.
+B<h> Summary of debugger commands.
+B<h> [I<db_command>] Get help [on a specific debugger command], enter B<|h> to page.
+B<h h> Long help for debugger commands
+B<$doccmd> I<manpage> Runs the external doc viewer B<$doccmd> command on the
+ named Perl I<manpage>, or on B<$doccmd> itself if omitted.
+ Set B<\$DB::doccmd> to change viewer.
+
+Type `|h h' for a paged display if this was too hard to read.
+
+"; # Fix balance of vi % matching: }}}}
+
+ # note: tabs in the following section are not-so-helpful
+ $summary = <<"END_SUM";
+I<List/search source lines:> I<Control script execution:>
+ B<l> [I<ln>|I<sub>] List source code B<T> Stack trace
+ B<-> or B<.> List previous/current line B<s> [I<expr>] Single step [in expr]
+ B<v> [I<line>] View around line B<n> [I<expr>] Next, steps over subs
+ B<f> I<filename> View source in file <B<CR>/B<Enter>> Repeat last B<n> or B<s>
+ B</>I<pattern>B</> B<?>I<patt>B<?> Search forw/backw B<r> Return from subroutine
+ B<M> Show module versions B<c> [I<ln>|I<sub>] Continue until position
+I<Debugger controls:> B<L> List break/watch/actions
+ B<o> [...] Set debugger options B<t> [I<expr>] Toggle trace [trace expr]
+ B<<>[B<<>]|B<{>[B<{>]|B<>>[B<>>] [I<cmd>] Do pre/post-prompt B<b> [I<ln>|I<event>|I<sub>] [I<cnd>] Set breakpoint
+ B<$prc> [I<N>|I<pat>] Redo a previous command B<B> I<ln|*> Delete a/all breakpoints
+ B<H> [I<-num>] Display last num commands B<a> [I<ln>] I<cmd> Do cmd before line
+ B<=> [I<a> I<val>] Define/list an alias B<A> I<ln|*> Delete a/all actions
+ B<h> [I<db_cmd>] Get help on command B<w> I<expr> Add a watch expression
+ B<h h> Complete help page B<W> I<expr|*> Delete a/all watch exprs
+ B<|>[B<|>]I<db_cmd> Send output to pager B<$psh>\[B<$psh>\] I<syscmd> Run cmd in a subprocess
+ B<q> or B<^D> Quit B<R> Attempt a restart
+I<Data Examination:> B<expr> Execute perl code, also see: B<s>,B<n>,B<t> I<expr>
+ B<x>|B<m> I<expr> Evals expr in list context, dumps the result or lists methods.
+ B<p> I<expr> Print expression (uses script's current package).
+ B<S> [[B<!>]I<pat>] List subroutine names [not] matching pattern
+ B<V> [I<Pk> [I<Vars>]] List Variables in Package. Vars can be ~pattern or !pattern.
+ B<X> [I<Vars>] Same as \"B<V> I<current_package> [I<Vars>]\". B<i> I<class> inheritance tree.
+ B<y> [I<n> [I<Vars>]] List lexicals in higher scope <n>. Vars same as B<V>.
+ B<e> Display thread id B<E> Display all thread ids.
+For more help, type B<h> I<cmd_letter>, or run B<$doccmd perldebug> for all docs.
+END_SUM
+
+ # ')}}; # Fix balance of vi % matching
+
+ # and this is really numb...
+ $pre580_help = "
+B<T> Stack trace.
+B<s> [I<expr>] Single step [in I<expr>].
+B<n> [I<expr>] Next, steps over subroutine calls [in I<expr>].
+B<CR>> Repeat last B<n> or B<s> command.
+B<r> Return from current subroutine.
+B<c> [I<line>|I<sub>] Continue; optionally inserts a one-time-only breakpoint
+ at the specified position.
+B<l> I<min>B<+>I<incr> List I<incr>+1 lines starting at I<min>.
+B<l> I<min>B<->I<max> List lines I<min> through I<max>.
+B<l> I<line> List single I<line>.
+B<l> I<subname> List first window of lines from subroutine.
+B<l> I<\$var> List first window of lines from subroutine referenced by I<\$var>.
+B<l> List next window of lines.
+B<-> List previous window of lines.
+B<w> [I<line>] List window around I<line>.
+B<.> Return to the executed line.
+B<f> I<filename> Switch to viewing I<filename>. File must be already loaded.
+ I<filename> may be either the full name of the file, or a regular
+ expression matching the full file name:
+ B<f> I</home/me/foo.pl> and B<f> I<oo\\.> may access the same file.
+ Evals (with saved bodies) are considered to be filenames:
+ B<f> I<(eval 7)> and B<f> I<eval 7\\b> access the body of the 7th eval
+ (in the order of execution).
+B</>I<pattern>B</> Search forwards for I<pattern>; final B</> is optional.
+B<?>I<pattern>B<?> Search backwards for I<pattern>; final B<?> is optional.
+B<L> List all breakpoints and actions.
+B<S> [[B<!>]I<pattern>] List subroutine names [not] matching I<pattern>.
+B<t> Toggle trace mode.
+B<t> I<expr> Trace through execution of I<expr>.
+B<b> [I<line>] [I<condition>]
+ Set breakpoint; I<line> defaults to the current execution line;
+ I<condition> breaks if it evaluates to true, defaults to '1'.
+B<b> I<subname> [I<condition>]
+ Set breakpoint at first line of subroutine.
+B<b> I<\$var> Set breakpoint at first line of subroutine referenced by I<\$var>.
+B<b> B<load> I<filename> Set breakpoint on `require'ing the given file.
+B<b> B<postpone> I<subname> [I<condition>]
+ Set breakpoint at first line of subroutine after
+ it is compiled.
+B<b> B<compile> I<subname>
+ Stop after the subroutine is compiled.
+B<d> [I<line>] Delete the breakpoint for I<line>.
+B<D> Delete all breakpoints.
+B<a> [I<line>] I<command>
+ Set an action to be done before the I<line> is executed;
+ I<line> defaults to the current execution line.
+ Sequence is: check for breakpoint/watchpoint, print line
+ if necessary, do action, prompt user if necessary,
+ execute line.
+B<a> [I<line>] Delete the action for I<line>.
+B<A> Delete all actions.
+B<W> I<expr> Add a global watch-expression.
+B<W> Delete all watch-expressions.
+B<V> [I<pkg> [I<vars>]] List some (default all) variables in package (default current).
+ Use B<~>I<pattern> and B<!>I<pattern> for positive and negative regexps.
+B<X> [I<vars>] Same as \"B<V> I<currentpackage> [I<vars>]\".
+B<x> I<expr> Evals expression in list context, dumps the result.
+B<m> I<expr> Evals expression in list context, prints methods callable
+ on the first element of the result.
+B<m> I<class> Prints methods callable via the given class.
+
+B<<> ? List Perl commands to run before each prompt.
+B<<> I<expr> Define Perl command to run before each prompt.
+B<<<> I<expr> Add to the list of Perl commands to run before each prompt.
+B<>> ? List Perl commands to run after each prompt.
+B<>> I<expr> Define Perl command to run after each prompt.
+B<>>B<>> I<expr> Add to the list of Perl commands to run after each prompt.
+B<{> I<db_command> Define debugger command to run before each prompt.
+B<{> ? List debugger commands to run before each prompt.
+B<{{> I<db_command> Add to the list of debugger commands to run before each prompt.
+B<$prc> I<number> Redo a previous command (default previous command).
+B<$prc> I<-number> Redo number'th-to-last command.
+B<$prc> I<pattern> Redo last command that started with I<pattern>.
+ See 'B<O> I<recallCommand>' too.
+B<$psh$psh> I<cmd> Run cmd in a subprocess (reads from DB::IN, writes to DB::OUT)"
+ . (
+ $rc eq $sh
+ ? ""
+ : "
+B<$psh> [I<cmd>] Run I<cmd> in subshell (forces \"\$SHELL -c 'cmd'\")."
+ ) . "
+ See 'B<O> I<shellBang>' too.
+B<source> I<file> Execute I<file> containing debugger commands (may nest).
+B<H> I<-number> Display last number commands (default all).
+B<p> I<expr> Same as \"I<print {DB::OUT} expr>\" in current package.
+B<|>I<dbcmd> Run debugger command, piping DB::OUT to current pager.
+B<||>I<dbcmd> Same as B<|>I<dbcmd> but DB::OUT is temporarilly select()ed as well.
+B<\=> [I<alias> I<value>] Define a command alias, or list current aliases.
+I<command> Execute as a perl statement in current package.
+B<v> Show versions of loaded modules.
+B<R> Pure-man-restart of debugger, some of debugger state
+ and command-line options may be lost.
+ Currently the following settings are preserved:
+ history, breakpoints and actions, debugger B<O>ptions
+ and the following command-line options: I<-w>, I<-I>, I<-e>.
+
+B<O> [I<opt>] ... Set boolean option to true
+B<O> [I<opt>B<?>] Query options
+B<O> [I<opt>B<=>I<val>] [I<opt>=B<\">I<val>B<\">] ...
+ Set options. Use quotes in spaces in value.
+ I<recallCommand>, I<ShellBang> chars used to recall command or spawn shell;
+ I<pager> program for output of \"|cmd\";
+ I<tkRunning> run Tk while prompting (with ReadLine);
+ I<signalLevel> I<warnLevel> I<dieLevel> level of verbosity;
+ I<inhibit_exit> Allows stepping off the end of the script.
+ I<ImmediateStop> Debugger should stop as early as possible.
+ I<RemotePort> Remote hostname:port for remote debugging
+ The following options affect what happens with B<V>, B<X>, and B<x> commands:
+ I<arrayDepth>, I<hashDepth> print only first N elements ('' for all);
+ I<compactDump>, I<veryCompact> change style of array and hash dump;
+ I<globPrint> whether to print contents of globs;
+ I<DumpDBFiles> dump arrays holding debugged files;
+ I<DumpPackages> dump symbol tables of packages;
+ I<DumpReused> dump contents of \"reused\" addresses;
+ I<quote>, I<HighBit>, I<undefPrint> change style of string dump;
+ I<bareStringify> Do not print the overload-stringified value;
+ Other options include:
+ I<PrintRet> affects printing of return value after B<r> command,
+ I<frame> affects printing messages on subroutine entry/exit.
+ I<AutoTrace> affects printing messages on possible breaking points.
+ I<maxTraceLen> gives max length of evals/args listed in stack trace.
+ I<ornaments> affects screen appearance of the command line.
+ I<CreateTTY> bits control attempts to create a new TTY on events:
+ 1: on fork() 2: debugger is started inside debugger
+ 4: on startup
+ During startup options are initialized from \$ENV{PERLDB_OPTS}.
+ You can put additional initialization options I<TTY>, I<noTTY>,
+ I<ReadLine>, I<NonStop>, and I<RemotePort> there (or use
+ `B<R>' after you set them).
+
+B<q> or B<^D> Quit. Set B<\$DB::finished = 0> to debug global destruction.
+B<h> [I<db_command>] Get help [on a specific debugger command], enter B<|h> to page.
+B<h h> Summary of debugger commands.
+B<$doccmd> I<manpage> Runs the external doc viewer B<$doccmd> command on the
+ named Perl I<manpage>, or on B<$doccmd> itself if omitted.
+ Set B<\$DB::doccmd> to change viewer.
+
+Type `|h' for a paged display if this was too hard to read.
+
+"; # Fix balance of vi % matching: }}}}
+
+ # note: tabs in the following section are not-so-helpful
+ $pre580_summary = <<"END_SUM";
+I<List/search source lines:> I<Control script execution:>
+ B<l> [I<ln>|I<sub>] List source code B<T> Stack trace
+ B<-> or B<.> List previous/current line B<s> [I<expr>] Single step [in expr]
+ B<w> [I<line>] List around line B<n> [I<expr>] Next, steps over subs
+ B<f> I<filename> View source in file <B<CR>/B<Enter>> Repeat last B<n> or B<s>
+ B</>I<pattern>B</> B<?>I<patt>B<?> Search forw/backw B<r> Return from subroutine
+ B<v> Show versions of modules B<c> [I<ln>|I<sub>] Continue until position
+I<Debugger controls:> B<L> List break/watch/actions
+ B<O> [...] Set debugger options B<t> [I<expr>] Toggle trace [trace expr]
+ B<<>[B<<>]|B<{>[B<{>]|B<>>[B<>>] [I<cmd>] Do pre/post-prompt B<b> [I<ln>|I<event>|I<sub>] [I<cnd>] Set breakpoint
+ B<$prc> [I<N>|I<pat>] Redo a previous command B<d> [I<ln>] or B<D> Delete a/all breakpoints
+ B<H> [I<-num>] Display last num commands B<a> [I<ln>] I<cmd> Do cmd before line
+ B<=> [I<a> I<val>] Define/list an alias B<W> I<expr> Add a watch expression
+ B<h> [I<db_cmd>] Get help on command B<A> or B<W> Delete all actions/watch
+ B<|>[B<|>]I<db_cmd> Send output to pager B<$psh>\[B<$psh>\] I<syscmd> Run cmd in a subprocess
+ B<q> or B<^D> Quit B<R> Attempt a restart
+I<Data Examination:> B<expr> Execute perl code, also see: B<s>,B<n>,B<t> I<expr>
+ B<x>|B<m> I<expr> Evals expr in list context, dumps the result or lists methods.
+ B<p> I<expr> Print expression (uses script's current package).
+ B<S> [[B<!>]I<pat>] List subroutine names [not] matching pattern
+ B<V> [I<Pk> [I<Vars>]] List Variables in Package. Vars can be ~pattern or !pattern.
+ B<X> [I<Vars>] Same as \"B<V> I<current_package> [I<Vars>]\".
+ B<y> [I<n> [I<Vars>]] List lexicals in higher scope <n>. Vars same as B<V>.
+For more help, type B<h> I<cmd_letter>, or run B<$doccmd perldebug> for all docs.
+END_SUM
+
+ # ')}}; # Fix balance of vi % matching
+
+} ## end sub sethelp
+
+=head2 C<print_help()>
+
+Most of what C<print_help> does is just text formatting. It finds the
+C<B> and C<I> ornaments, cleans them off, and substitutes the proper
+terminal control characters to simulate them (courtesy of
+C<Term::ReadLine::TermCap>).
+
+=cut
+
+sub print_help {
+ local $_ = shift;
+
+ # Restore proper alignment destroyed by eeevil I<> and B<>
+ # ornaments: A pox on both their houses!
+ #
+ # A help command will have everything up to and including
+ # the first tab sequence padded into a field 16 (or if indented 20)
+ # wide. If it's wider than that, an extra space will be added.
+ s{
+ ^ # only matters at start of line
+ ( \040{4} | \t )* # some subcommands are indented
+ ( < ? # so <CR> works
+ [BI] < [^\t\n] + ) # find an eeevil ornament
+ ( \t+ ) # original separation, discarded
+ ( .* ) # this will now start (no earlier) than
+ # column 16
+ } {
+ my($leadwhite, $command, $midwhite, $text) = ($1, $2, $3, $4);
+ my $clean = $command;
+ $clean =~ s/[BI]<([^>]*)>/$1/g;
+
+ # replace with this whole string:
+ ($leadwhite ? " " x 4 : "")
+ . $command
+ . ((" " x (16 + ($leadwhite ? 4 : 0) - length($clean))) || " ")
+ . $text;
+
+ }mgex;
+
+ s{ # handle bold ornaments
+ B < ( [^>] + | > ) >
+ } {
+ $Term::ReadLine::TermCap::rl_term_set[2]
+ . $1
+ . $Term::ReadLine::TermCap::rl_term_set[3]
+ }gex;
+
+ s{ # handle italic ornaments
+ I < ( [^>] + | > ) >
+ } {
+ $Term::ReadLine::TermCap::rl_term_set[0]
+ . $1
+ . $Term::ReadLine::TermCap::rl_term_set[1]
+ }gex;
+
+ local $\ = '';
+ print $OUT $_;
+} ## end sub print_help
+
+=head2 C<fix_less>
+
+This routine does a lot of gyrations to be sure that the pager is C<less>.
+It checks for C<less> masquerading as C<more> and records the result in
+C<$ENV{LESS}> so we don't have to go through doing the stats again.
+
+=cut
+
+sub fix_less {
+
+ # We already know if this is set.
+ return if defined $ENV{LESS} && $ENV{LESS} =~ /r/;
+
+ # Pager is less for sure.
+ my $is_less = $pager =~ /\bless\b/;
+ if ( $pager =~ /\bmore\b/ ) {
+
+ # Nope, set to more. See what's out there.
+ my @st_more = stat('/usr/bin/more');
+ my @st_less = stat('/usr/bin/less');
+
+ # is it really less, pretending to be more?
+ $is_less = @st_more
+ && @st_less
+ && $st_more[0] == $st_less[0]
+ && $st_more[1] == $st_less[1];
+ } ## end if ($pager =~ /\bmore\b/)
+
+ # changes environment!
+ # 'r' added so we don't do (slow) stats again.
+ $ENV{LESS} .= 'r' if $is_less;
+} ## end sub fix_less
+
+=head1 DIE AND WARN MANAGEMENT
+
+=head2 C<diesignal>
+
+C<diesignal> is a just-drop-dead C<die> handler. It's most useful when trying
+to debug a debugger problem.
+
+It does its best to report the error that occurred, and then forces the
+program, debugger, and everything to die.
+
+=cut
+
+sub diesignal {
+
+ # No entry/exit messages.
+ local $frame = 0;
+
+ # No return value prints.
+ local $doret = -2;
+
+ # set the abort signal handling to the default (just terminate).
+ $SIG{'ABRT'} = 'DEFAULT';
+
+ # If we enter the signal handler recursively, kill myself with an
+ # abort signal (so we just terminate).
+ kill 'ABRT', $$ if $panic++;
+
+ # If we can show detailed info, do so.
+ if ( defined &Carp::longmess ) {
+
+ # Don't recursively enter the warn handler, since we're carping.
+ local $SIG{__WARN__} = '';
+
+ # Skip two levels before reporting traceback: we're skipping
+ # mydie and confess.
+ local $Carp::CarpLevel = 2; # mydie + confess
+
+ # Tell us all about it.
+ &warn( Carp::longmess("Signal @_") );
+ }
+
+ # No Carp. Tell us about the signal as best we can.
+ else {
+ local $\ = '';
+ print $DB::OUT "Got signal @_\n";
+ }
+
+ # Drop dead.
+ kill 'ABRT', $$;
+} ## end sub diesignal
+
+=head2 C<dbwarn>
+
+The debugger's own default C<$SIG{__WARN__}> handler. We load C<Carp> to
+be able to get a stack trace, and output the warning message vi C<DB::dbwarn()>.
+
+=cut
+
+sub dbwarn {
+
+ # No entry/exit trace.
+ local $frame = 0;
+
+ # No return value printing.
+ local $doret = -2;
+
+ # Turn off warn and die handling to prevent recursive entries to this
+ # routine.
+ local $SIG{__WARN__} = '';
+ local $SIG{__DIE__} = '';
+
+ # Load Carp if we can. If $^S is false (current thing being compiled isn't
+ # done yet), we may not be able to do a require.
+ eval { require Carp }
+ if defined $^S; # If error/warning during compilation,
+ # require may be broken.
+
+ # Use the core warn() unless Carp loaded OK.
+ CORE::warn( @_,
+ "\nCannot print stack trace, load with -MCarp option to see stack" ),
+ return
+ unless defined &Carp::longmess;
+
+ # Save the current values of $single and $trace, and then turn them off.
+ my ( $mysingle, $mytrace ) = ( $single, $trace );
+ $single = 0;
+ $trace = 0;
+
+ # We can call Carp::longmess without its being "debugged" (which we
+ # don't want - we just want to use it!). Capture this for later.
+ my $mess = Carp::longmess(@_);
+
+ # Restore $single and $trace to their original values.
+ ( $single, $trace ) = ( $mysingle, $mytrace );
+
+ # Use the debugger's own special way of printing warnings to print
+ # the stack trace message.
+ &warn($mess);
+} ## end sub dbwarn
+
+=head2 C<dbdie>
+
+The debugger's own C<$SIG{__DIE__}> handler. Handles providing a stack trace
+by loading C<Carp> and calling C<Carp::longmess()> to get it. We turn off
+single stepping and tracing during the call to C<Carp::longmess> to avoid
+debugging it - we just want to use it.
+
+If C<dieLevel> is zero, we let the program being debugged handle the
+exceptions. If it's 1, you get backtraces for any exception. If it's 2,
+the debugger takes over all exception handling, printing a backtrace and
+displaying the exception via its C<dbwarn()> routine.
+
+=cut
+
+sub dbdie {
+ local $frame = 0;
+ local $doret = -2;
+ local $SIG{__DIE__} = '';
+ local $SIG{__WARN__} = '';
+ my $i = 0;
+ my $ineval = 0;
+ my $sub;
+ if ( $dieLevel > 2 ) {
+ local $SIG{__WARN__} = \&dbwarn;
+ &warn(@_); # Yell no matter what
+ return;
+ }
+ if ( $dieLevel < 2 ) {
+ die @_ if $^S; # in eval propagate
+ }
+
+ # The code used to check $^S to see if compiliation of the current thing
+ # hadn't finished. We don't do it anymore, figuring eval is pretty stable.
+ eval { require Carp };
+
+ die( @_,
+ "\nCannot print stack trace, load with -MCarp option to see stack" )
+ unless defined &Carp::longmess;
+
+ # We do not want to debug this chunk (automatic disabling works
+ # inside DB::DB, but not in Carp). Save $single and $trace, turn them off,
+ # get the stack trace from Carp::longmess (if possible), restore $signal
+ # and $trace, and then die with the stack trace.
+ my ( $mysingle, $mytrace ) = ( $single, $trace );
+ $single = 0;
+ $trace = 0;
+ my $mess = "@_";
+ {
+
+ package Carp; # Do not include us in the list
+ eval { $mess = Carp::longmess(@_); };
+ }
+ ( $single, $trace ) = ( $mysingle, $mytrace );
+ die $mess;
+} ## end sub dbdie
+
+=head2 C<warnlevel()>
+
+Set the C<$DB::warnLevel> variable that stores the value of the
+C<warnLevel> option. Calling C<warnLevel()> with a positive value
+results in the debugger taking over all warning handlers. Setting
+C<warnLevel> to zero leaves any warning handlers set up by the program
+being debugged in place.
+
+=cut
+
+sub warnLevel {
+ if (@_) {
+ $prevwarn = $SIG{__WARN__} unless $warnLevel;
+ $warnLevel = shift;
+ if ($warnLevel) {
+ $SIG{__WARN__} = \&DB::dbwarn;
+ }
+ elsif ($prevwarn) {
+ $SIG{__WARN__} = $prevwarn;
+ }
+ } ## end if (@_)
+ $warnLevel;
+} ## end sub warnLevel
+
+=head2 C<dielevel>
+
+Similar to C<warnLevel>. Non-zero values for C<dieLevel> result in the
+C<DB::dbdie()> function overriding any other C<die()> handler. Setting it to
+zero lets you use your own C<die()> handler.
+
+=cut
+
+sub dieLevel {
+ local $\ = '';
+ if (@_) {
+ $prevdie = $SIG{__DIE__} unless $dieLevel;
+ $dieLevel = shift;
+ if ($dieLevel) {
+
+ # Always set it to dbdie() for non-zero values.
+ $SIG{__DIE__} = \&DB::dbdie; # if $dieLevel < 2;
+
+ # No longer exists, so don't try to use it.
+ #$SIG{__DIE__} = \&DB::diehard if $dieLevel >= 2;
+
+ # If we've finished initialization, mention that stack dumps
+ # are enabled, If dieLevel is 1, we won't stack dump if we die
+ # in an eval().
+ print $OUT "Stack dump during die enabled",
+ ( $dieLevel == 1 ? " outside of evals" : "" ), ".\n"
+ if $I_m_init;
+
+ # XXX This is probably obsolete, given that diehard() is gone.
+ print $OUT "Dump printed too.\n" if $dieLevel > 2;
+ } ## end if ($dieLevel)
+
+ # Put the old one back if there was one.
+ elsif ($prevdie) {
+ $SIG{__DIE__} = $prevdie;
+ print $OUT "Default die handler restored.\n";
+ }
+ } ## end if (@_)
+ $dieLevel;
+} ## end sub dieLevel
+
+=head2 C<signalLevel>
+
+Number three in a series: set C<signalLevel> to zero to keep your own
+signal handler for C<SIGSEGV> and/or C<SIGBUS>. Otherwise, the debugger
+takes over and handles them with C<DB::diesignal()>.
+
+=cut
+
+sub signalLevel {
+ if (@_) {
+ $prevsegv = $SIG{SEGV} unless $signalLevel;
+ $prevbus = $SIG{BUS} unless $signalLevel;
+ $signalLevel = shift;
+ if ($signalLevel) {
+ $SIG{SEGV} = \&DB::diesignal;
+ $SIG{BUS} = \&DB::diesignal;
+ }
+ else {
+ $SIG{SEGV} = $prevsegv;
+ $SIG{BUS} = $prevbus;
+ }
+ } ## end if (@_)
+ $signalLevel;
+} ## end sub signalLevel
+
+=head1 SUBROUTINE DECODING SUPPORT
+
+These subroutines are used during the C<x> and C<X> commands to try to
+produce as much information as possible about a code reference. They use
+L<Devel::Peek> to try to find the glob in which this code reference lives
+(if it does) - this allows us to actually code references which correspond
+to named subroutines (including those aliased via glob assignment).
+
+=head2 C<CvGV_name()>
+
+Wrapper for C<CvGV_name_or_bust>; tries to get the name of a reference
+via that routine. If this fails, return the reference again (when the
+reference is stringified, it'll come out as C<SOMETHING(0x...)>).
+
+=cut
+
+sub CvGV_name {
+ my $in = shift;
+ my $name = CvGV_name_or_bust($in);
+ defined $name ? $name : $in;
+}
+
+=head2 C<CvGV_name_or_bust> I<coderef>
+
+Calls L<Devel::Peek> to try to find the glob the ref lives in; returns
+C<undef> if L<Devel::Peek> can't be loaded, or if C<Devel::Peek::CvGV> can't
+find a glob for this ref.
+
+Returns C<< I<package>::I<glob name> >> if the code ref is found in a glob.
+
+=cut
+
+sub CvGV_name_or_bust {
+ my $in = shift;
+ return if $skipCvGV; # Backdoor to avoid problems if XS broken...
+ return unless ref $in;
+ $in = \&$in; # Hard reference...
+ eval { require Devel::Peek; 1 } or return;
+ my $gv = Devel::Peek::CvGV($in) or return;
+ *$gv{PACKAGE} . '::' . *$gv{NAME};
+} ## end sub CvGV_name_or_bust
+
+=head2 C<find_sub>
+
+A utility routine used in various places; finds the file where a subroutine
+was defined, and returns that filename and a line-number range.
+
+Tries to use C<@sub> first; if it can't find it there, it tries building a
+reference to the subroutine and uses C<CvGV_name_or_bust> to locate it,
+loading it into C<@sub> as a side effect (XXX I think). If it can't find it
+this way, it brute-force searches C<%sub>, checking for identical references.
+
+=cut
+
+sub find_sub {
+ my $subr = shift;
+ $sub{$subr} or do {
+ return unless defined &$subr;
+ my $name = CvGV_name_or_bust($subr);
+ my $data;
+ $data = $sub{$name} if defined $name;
+ return $data if defined $data;
+
+ # Old stupid way...
+ $subr = \&$subr; # Hard reference
+ my $s;
+ for ( keys %sub ) {
+ $s = $_, last if $subr eq \&$_;
+ }
+ $sub{$s} if $s;
+ } ## end do
+} ## end sub find_sub
+
+=head2 C<methods>
+
+A subroutine that uses the utility function C<methods_via> to find all the
+methods in the class corresponding to the current reference and in
+C<UNIVERSAL>.
+
+=cut
+
+sub methods {
+
+ # Figure out the class - either this is the class or it's a reference
+ # to something blessed into that class.
+ my $class = shift;
+ $class = ref $class if ref $class;
+
+ local %seen;
+
+ # Show the methods that this class has.
+ methods_via( $class, '', 1 );
+
+ # Show the methods that UNIVERSAL has.
+ methods_via( 'UNIVERSAL', 'UNIVERSAL', 0 );
+} ## end sub methods
+
+=head2 C<methods_via($class, $prefix, $crawl_upward)>
+
+C<methods_via> does the work of crawling up the C<@ISA> tree and reporting
+all the parent class methods. C<$class> is the name of the next class to
+try; C<$prefix> is the message prefix, which gets built up as we go up the
+C<@ISA> tree to show parentage; C<$crawl_upward> is 1 if we should try to go
+higher in the C<@ISA> tree, 0 if we should stop.
+
+=cut
+
+sub methods_via {
+
+ # If we've processed this class already, just quit.
+ my $class = shift;
+ return if $seen{$class}++;
+
+ # This is a package that is contributing the methods we're about to print.
+ my $prefix = shift;
+ my $prepend = $prefix ? "via $prefix: " : '';
+
+ my $name;
+ for $name (
+
+ # Keep if this is a defined subroutine in this class.
+ grep { defined &{ ${"${class}::"}{$_} } }
+
+ # Extract from all the symbols in this class.
+ sort keys %{"${class}::"}
+ )
+ {
+
+ # If we printed this already, skip it.
+ next if $seen{$name}++;
+
+ # Print the new method name.
+ local $\ = '';
+ local $, = '';
+ print $DB::OUT "$prepend$name\n";
+ } ## end for $name (grep { defined...
+
+ # If the $crawl_upward argument is false, just quit here.
+ return unless shift;
+
+ # $crawl_upward true: keep going up the tree.
+ # Find all the classes this one is a subclass of.
+ for $name ( @{"${class}::ISA"} ) {
+
+ # Set up the new prefix.
+ $prepend = $prefix ? $prefix . " -> $name" : $name;
+
+ # Crawl up the tree and keep trying to crawl up.
+ methods_via( $name, $prepend, 1 );
+ }
+} ## end sub methods_via
+
+=head2 C<setman> - figure out which command to use to show documentation
+
+Just checks the contents of C<$^O> and sets the C<$doccmd> global accordingly.
+
+=cut
+
+sub setman {
+ $doccmd = $^O !~ /^(?:MSWin32|VMS|os2|dos|amigaos|riscos|MacOS|NetWare)\z/s
+ ? "man" # O Happy Day!
+ : "perldoc"; # Alas, poor unfortunates
+} ## end sub setman
+
+=head2 C<runman> - run the appropriate command to show documentation
+
+Accepts a man page name; runs the appropriate command to display it (set up
+during debugger initialization). Uses C<DB::system> to avoid mucking up the
+program's STDIN and STDOUT.
+
+=cut
+
+sub runman {
+ my $page = shift;
+ unless ($page) {
+ &system("$doccmd $doccmd");
+ return;
+ }
+
+ # this way user can override, like with $doccmd="man -Mwhatever"
+ # or even just "man " to disable the path check.
+ unless ( $doccmd eq 'man' ) {
+ &system("$doccmd $page");
+ return;
+ }
+
+ $page = 'perl' if lc($page) eq 'help';
+
+ require Config;
+ my $man1dir = $Config::Config{'man1dir'};
+ my $man3dir = $Config::Config{'man3dir'};
+ for ( $man1dir, $man3dir ) { s#/[^/]*\z## if /\S/ }
+ my $manpath = '';
+ $manpath .= "$man1dir:" if $man1dir =~ /\S/;
+ $manpath .= "$man3dir:" if $man3dir =~ /\S/ && $man1dir ne $man3dir;
+ chop $manpath if $manpath;
+
+ # harmless if missing, I figure
+ my $oldpath = $ENV{MANPATH};
+ $ENV{MANPATH} = $manpath if $manpath;
+ my $nopathopt = $^O =~ /dunno what goes here/;
+ if (
+ CORE::system(
+ $doccmd,
+
+ # I just *know* there are men without -M
+ ( ( $manpath && !$nopathopt ) ? ( "-M", $manpath ) : () ),
+ split ' ', $page
+ )
+ )
+ {
+ unless ( $page =~ /^perl\w/ ) {
+# do it this way because its easier to slurp in to keep up to date - clunky though.
+my @pods = qw(
+ 5004delta
+ 5005delta
+ 561delta
+ 56delta
+ 570delta
+ 571delta
+ 572delta
+ 573delta
+ 58delta
+ 581delta
+ 582delta
+ 583delta
+ 584delta
+ 590delta
+ 591delta
+ 592delta
+ aix
+ amiga
+ apio
+ api
+ apollo
+ artistic
+ beos
+ book
+ boot
+ bot
+ bs2000
+ call
+ ce
+ cheat
+ clib
+ cn
+ compile
+ cygwin
+ data
+ dbmfilter
+ debguts
+ debtut
+ debug
+ delta
+ dgux
+ diag
+ doc
+ dos
+ dsc
+ ebcdic
+ embed
+ epoc
+ faq1
+ faq2
+ faq3
+ faq4
+ faq5
+ faq6
+ faq7
+ faq8
+ faq9
+ faq
+ filter
+ fork
+ form
+ freebsd
+ func
+ gpl
+ guts
+ hack
+ hist
+ hpux
+ hurd
+ intern
+ intro
+ iol
+ ipc
+ irix
+ jp
+ ko
+ lexwarn
+ locale
+ lol
+ machten
+ macos
+ macosx
+ mint
+ modinstall
+ modlib
+ mod
+ modstyle
+ mpeix
+ netware
+ newmod
+ number
+ obj
+ opentut
+ op
+ os2
+ os390
+ os400
+ othrtut
+ packtut
+ plan9
+ pod
+ podspec
+ port
+ qnx
+ ref
+ reftut
+ re
+ requick
+ reref
+ retut
+ run
+ sec
+ solaris
+ style
+ sub
+ syn
+ thrtut
+ tie
+ toc
+ todo
+ tooc
+ toot
+ trap
+ tru64
+ tw
+ unicode
+ uniintro
+ util
+ uts
+ var
+ vmesa
+ vms
+ vos
+ win32
+ xs
+ xstut
+);
+ if (grep { $page eq $_ } @pods) {
+ $page =~ s/^/perl/;
+ CORE::system( $doccmd,
+ ( ( $manpath && !$nopathopt ) ? ( "-M", $manpath ) : () ),
+ $page );
+ } ## end if (grep { $page eq $_...
+ } ## end unless ($page =~ /^perl\w/)
+ } ## end if (CORE::system($doccmd...
+ if ( defined $oldpath ) {
+ $ENV{MANPATH} = $manpath;
+ }
+ else {
+ delete $ENV{MANPATH};
+ }
+} ## end sub runman
+
+#use Carp; # This did break, left for debugging
+
+=head1 DEBUGGER INITIALIZATION - THE SECOND BEGIN BLOCK
+
+Because of the way the debugger interface to the Perl core is designed, any
+debugger package globals that C<DB::sub()> requires have to be defined before
+any subroutines can be called. These are defined in the second C<BEGIN> block.
+
+This block sets things up so that (basically) the world is sane
+before the debugger starts executing. We set up various variables that the
+debugger has to have set up before the Perl core starts running:
+
+=over 4
+
+=item *
+
+The debugger's own filehandles (copies of STD and STDOUT for now).
+
+=item *
+
+Characters for shell escapes, the recall command, and the history command.
+
+=item *
+
+The maximum recursion depth.
+
+=item *
+
+The size of a C<w> command's window.
+
+=item *
+
+The before-this-line context to be printed in a C<v> (view a window around this line) command.
+
+=item *
+
+The fact that we're not in a sub at all right now.
+
+=item *
+
+The default SIGINT handler for the debugger.
+
+=item *
+
+The appropriate value of the flag in C<$^D> that says the debugger is running
+
+=item *
+
+The current debugger recursion level
+
+=item *
+
+The list of postponed items and the C<$single> stack (XXX define this)
+
+=item *
+
+That we want no return values and no subroutine entry/exit trace.
+
+=back
+
+=cut
+
+# The following BEGIN is very handy if debugger goes havoc, debugging debugger?
+
+BEGIN { # This does not compile, alas. (XXX eh?)
+ $IN = \*STDIN; # For bugs before DB::OUT has been opened
+ $OUT = \*STDERR; # For errors before DB::OUT has been opened
+
+ # Define characters used by command parsing.
+ $sh = '!'; # Shell escape (does not work)
+ $rc = ','; # Recall command (does not work)
+ @hist = ('?'); # Show history (does not work)
+ @truehist = (); # Can be saved for replay (per session)
+
+ # This defines the point at which you get the 'deep recursion'
+ # warning. It MUST be defined or the debugger will not load.
+ $deep = 100;
+
+ # Number of lines around the current one that are shown in the
+ # 'w' command.
+ $window = 10;
+
+ # How much before-the-current-line context the 'v' command should
+ # use in calculating the start of the window it will display.
+ $preview = 3;
+
+ # We're not in any sub yet, but we need this to be a defined value.
+ $sub = '';
+
+ # Set up the debugger's interrupt handler. It simply sets a flag
+ # ($signal) that DB::DB() will check before each command is executed.
+ $SIG{INT} = \&DB::catch;
+
+ # The following lines supposedly, if uncommented, allow the debugger to
+ # debug itself. Perhaps we can try that someday.
+ # This may be enabled to debug debugger:
+ #$warnLevel = 1 unless defined $warnLevel;
+ #$dieLevel = 1 unless defined $dieLevel;
+ #$signalLevel = 1 unless defined $signalLevel;
+
+ # This is the flag that says "a debugger is running, please call
+ # DB::DB and DB::sub". We will turn it on forcibly before we try to
+ # execute anything in the user's context, because we always want to
+ # get control back.
+ $db_stop = 0; # Compiler warning ...
+ $db_stop = 1 << 30; # ... because this is only used in an eval() later.
+
+ # This variable records how many levels we're nested in debugging. Used
+ # Used in the debugger prompt, and in determining whether it's all over or
+ # not.
+ $level = 0; # Level of recursive debugging
+
+ # "Triggers bug (?) in perl if we postpone this until runtime."
+ # XXX No details on this yet, or whether we should fix the bug instead
+ # of work around it. Stay tuned.
+ @postponed = @stack = (0);
+
+ # Used to track the current stack depth using the auto-stacked-variable
+ # trick.
+ $stack_depth = 0; # Localized repeatedly; simple way to track $#stack
+
+ # Don't print return values on exiting a subroutine.
+ $doret = -2;
+
+ # No extry/exit tracing.
+ $frame = 0;
+
+} ## end BEGIN
+
+BEGIN { $^W = $ini_warn; } # Switch warnings back
+
+=head1 READLINE SUPPORT - COMPLETION FUNCTION
+
+=head2 db_complete
+
+C<readline> support - adds command completion to basic C<readline>.
+
+Returns a list of possible completions to C<readline> when invoked. C<readline>
+will print the longest common substring following the text already entered.
+
+If there is only a single possible completion, C<readline> will use it in full.
+
+This code uses C<map> and C<grep> heavily to create lists of possible
+completion. Think LISP in this section.
+
+=cut
+
+sub db_complete {
+
+ # Specific code for b c l V m f O, &blah, $blah, @blah, %blah
+ # $text is the text to be completed.
+ # $line is the incoming line typed by the user.
+ # $start is the start of the text to be completed in the incoming line.
+ my ( $text, $line, $start ) = @_;
+
+ # Save the initial text.
+ # The search pattern is current package, ::, extract the next qualifier
+ # Prefix and pack are set to undef.
+ my ( $itext, $search, $prefix, $pack ) =
+ ( $text, "^\Q${'package'}::\E([^:]+)\$" );
+
+=head3 C<b postpone|compile>
+
+=over 4
+
+=item *
+
+Find all the subroutines that might match in this package
+
+=item *
+
+Add C<postpone>, C<load>, and C<compile> as possibles (we may be completing the keyword itself)
+
+=item *
+
+Include all the rest of the subs that are known
+
+=item *
+
+C<grep> out the ones that match the text we have so far
+
+=item *
+
+Return this as the list of possible completions
+
+=back
+
+=cut
+
+ return sort grep /^\Q$text/, ( keys %sub ),
+ qw(postpone load compile), # subroutines
+ ( map { /$search/ ? ($1) : () } keys %sub )
+ if ( substr $line, 0, $start ) =~ /^\|*[blc]\s+((postpone|compile)\s+)?$/;
+
+=head3 C<b load>
+
+Get all the possible files from C<@INC> as it currently stands and
+select the ones that match the text so far.
+
+=cut
+
+ return sort grep /^\Q$text/, values %INC # files
+ if ( substr $line, 0, $start ) =~ /^\|*b\s+load\s+$/;
+
+=head3 C<V> (list variable) and C<m> (list modules)
+
+There are two entry points for these commands:
+
+=head4 Unqualified package names
+
+Get the top-level packages and grab everything that matches the text
+so far. For each match, recursively complete the partial packages to
+get all possible matching packages. Return this sorted list.
+
+=cut
+
+ return sort map { ( $_, db_complete( $_ . "::", "V ", 2 ) ) }
+ grep /^\Q$text/, map { /^(.*)::$/ ? ($1) : () } keys %:: # top-packages
+ if ( substr $line, 0, $start ) =~ /^\|*[Vm]\s+$/ and $text =~ /^\w*$/;
+
+=head4 Qualified package names
+
+Take a partially-qualified package and find all subpackages for it
+by getting all the subpackages for the package so far, matching all
+the subpackages against the text, and discarding all of them which
+start with 'main::'. Return this list.
+
+=cut
+
+ return sort map { ( $_, db_complete( $_ . "::", "V ", 2 ) ) }
+ grep !/^main::/, grep /^\Q$text/,
+ map { /^(.*)::$/ ? ( $prefix . "::$1" ) : () } keys %{ $prefix . '::' }
+ if ( substr $line, 0, $start ) =~ /^\|*[Vm]\s+$/
+ and $text =~ /^(.*[^:])::?(\w*)$/
+ and $prefix = $1;
+
+=head3 C<f> - switch files
+
+Here, we want to get a fully-qualified filename for the C<f> command.
+Possibilities are:
+
+=over 4
+
+=item 1. The original source file itself
+
+=item 2. A file from C<@INC>
+
+=item 3. An C<eval> (the debugger gets a C<(eval N)> fake file for each C<eval>).
+
+=back
+
+=cut
+
+ if ( $line =~ /^\|*f\s+(.*)/ ) { # Loaded files
+ # We might possibly want to switch to an eval (which has a "filename"
+ # like '(eval 9)'), so we may need to clean up the completion text
+ # before proceeding.
+ $prefix = length($1) - length($text);
+ $text = $1;
+
+=pod
+
+Under the debugger, source files are represented as C<_E<lt>/fullpath/to/file>
+(C<eval>s are C<_E<lt>(eval NNN)>) keys in C<%main::>. We pull all of these
+out of C<%main::>, add the initial source file, and extract the ones that
+match the completion text so far.
+
+=cut
+
+ return sort
+ map { substr $_, 2 + $prefix } grep /^_<\Q$text/, ( keys %main:: ),
+ $0;
+ } ## end if ($line =~ /^\|*f\s+(.*)/)
+
+=head3 Subroutine name completion
+
+We look through all of the defined subs (the keys of C<%sub>) and
+return both all the possible matches to the subroutine name plus
+all the matches qualified to the current package.
+
+=cut
+
+ if ( ( substr $text, 0, 1 ) eq '&' ) { # subroutines
+ $text = substr $text, 1;
+ $prefix = "&";
+ return sort map "$prefix$_", grep /^\Q$text/, ( keys %sub ),
+ (
+ map { /$search/ ? ($1) : () }
+ keys %sub
+ );
+ } ## end if ((substr $text, 0, ...
+
+=head3 Scalar, array, and hash completion: partially qualified package
+
+Much like the above, except we have to do a little more cleanup:
+
+=cut
+
+ if ( $text =~ /^[\$@%](.*)::(.*)/ ) { # symbols in a package
+
+=pod
+
+=over 4
+
+=item *
+
+Determine the package that the symbol is in. Put it in C<::> (effectively C<main::>) if no package is specified.
+
+=cut
+
+ $pack = ( $1 eq 'main' ? '' : $1 ) . '::';
+
+=pod
+
+=item *
+
+Figure out the prefix vs. what needs completing.
+
+=cut
+
+ $prefix = ( substr $text, 0, 1 ) . $1 . '::';
+ $text = $2;
+
+=pod
+
+=item *
+
+Look through all the symbols in the package. C<grep> out all the possible hashes/arrays/scalars, and then C<grep> the possible matches out of those. C<map> the prefix onto all the possibilities.
+
+=cut
+
+ my @out = map "$prefix$_", grep /^\Q$text/, grep /^_?[a-zA-Z]/,
+ keys %$pack;
+
+=pod
+
+=item *
+
+If there's only one hit, and it's a package qualifier, and it's not equal to the initial text, re-complete it using the symbol we actually found.
+
+=cut
+
+ if ( @out == 1 and $out[0] =~ /::$/ and $out[0] ne $itext ) {
+ return db_complete( $out[0], $line, $start );
+ }
+
+ # Return the list of possibles.
+ return sort @out;
+
+ } ## end if ($text =~ /^[\$@%](.*)::(.*)/)
+
+=pod
+
+=back
+
+=head3 Symbol completion: current package or package C<main>.
+
+=cut
+
+ if ( $text =~ /^[\$@%]/ ) { # symbols (in $package + packages in main)
+
+=pod
+
+=over 4
+
+=item *
+
+If it's C<main>, delete main to just get C<::> leading.
+
+=cut
+
+ $pack = ( $package eq 'main' ? '' : $package ) . '::';
+
+=pod
+
+=item *
+
+We set the prefix to the item's sigil, and trim off the sigil to get the text to be completed.
+
+=cut
+
+ $prefix = substr $text, 0, 1;
+ $text = substr $text, 1;
+
+=pod
+
+=item *
+
+If the package is C<::> (C<main>), create an empty list; if it's something else, create a list of all the packages known. Append whichever list to a list of all the possible symbols in the current package. C<grep> out the matches to the text entered so far, then C<map> the prefix back onto the symbols.
+
+=cut
+
+ my @out = map "$prefix$_", grep /^\Q$text/,
+ ( grep /^_?[a-zA-Z]/, keys %$pack ),
+ ( $pack eq '::' ? () : ( grep /::$/, keys %:: ) );
+
+=item *
+
+If there's only one hit, it's a package qualifier, and it's not equal to the initial text, recomplete using this symbol.
+
+=back
+
+=cut
+
+ if ( @out == 1 and $out[0] =~ /::$/ and $out[0] ne $itext ) {
+ return db_complete( $out[0], $line, $start );
+ }
+
+ # Return the list of possibles.
+ return sort @out;
+ } ## end if ($text =~ /^[\$@%]/)
+
+=head3 Options
+
+We use C<option_val()> to look up the current value of the option. If there's
+only a single value, we complete the command in such a way that it is a
+complete command for setting the option in question. If there are multiple
+possible values, we generate a command consisting of the option plus a trailing
+question mark, which, if executed, will list the current value of the option.
+
+=cut
+
+ if ( ( substr $line, 0, $start ) =~ /^\|*[oO]\b.*\s$/ )
+ { # Options after space
+ # We look for the text to be matched in the list of possible options,
+ # and fetch the current value.
+ my @out = grep /^\Q$text/, @options;
+ my $val = option_val( $out[0], undef );
+
+ # Set up a 'query option's value' command.
+ my $out = '? ';
+ if ( not defined $val or $val =~ /[\n\r]/ ) {
+
+ # There's really nothing else we can do.
+ }
+
+ # We have a value. Create a proper option-setting command.
+ elsif ( $val =~ /\s/ ) {
+
+ # XXX This may be an extraneous variable.
+ my $found;
+
+ # We'll want to quote the string (because of the embedded
+ # whtespace), but we want to make sure we don't end up with
+ # mismatched quote characters. We try several possibilities.
+ foreach $l ( split //, qq/\"\'\#\|/ ) {
+
+ # If we didn't find this quote character in the value,
+ # quote it using this quote character.
+ $out = "$l$val$l ", last if ( index $val, $l ) == -1;
+ }
+ } ## end elsif ($val =~ /\s/)
+
+ # Don't need any quotes.
+ else {
+ $out = "=$val ";
+ }
+
+ # If there were multiple possible values, return '? ', which
+ # makes the command into a query command. If there was just one,
+ # have readline append that.
+ $rl_attribs->{completer_terminator_character} =
+ ( @out == 1 ? $out : '? ' );
+
+ # Return list of possibilities.
+ return sort @out;
+ } ## end if ((substr $line, 0, ...
+
+=head3 Filename completion
+
+For entering filenames. We simply call C<readline>'s C<filename_list()>
+method with the completion text to get the possible completions.
+
+=cut
+
+ return $term->filename_list($text); # filenames
+
+} ## end sub db_complete
+
+=head1 MISCELLANEOUS SUPPORT FUNCTIONS
+
+Functions that possibly ought to be somewhere else.
+
+=head2 end_report
+
+Say we're done.
+
+=cut
+
+sub end_report {
+ local $\ = '';
+ print $OUT "Use `q' to quit or `R' to restart. `h q' for details.\n";
+}
+
+=head2 clean_ENV
+
+If we have $ini_pids, save it in the environment; else remove it from the
+environment. Used by the C<R> (restart) command.
+
+=cut
+
+sub clean_ENV {
+ if ( defined($ini_pids) ) {
+ $ENV{PERLDB_PIDS} = $ini_pids;
+ }
+ else {
+ delete( $ENV{PERLDB_PIDS} );
+ }
+} ## end sub clean_ENV
+
+# PERLDBf_... flag names from perl.h
+our ( %DollarCaretP_flags, %DollarCaretP_flags_r );
+
+BEGIN {
+ %DollarCaretP_flags = (
+ PERLDBf_SUB => 0x01, # Debug sub enter/exit
+ PERLDBf_LINE => 0x02, # Keep line #
+ PERLDBf_NOOPT => 0x04, # Switch off optimizations
+ PERLDBf_INTER => 0x08, # Preserve more data
+ PERLDBf_SUBLINE => 0x10, # Keep subr source lines
+ PERLDBf_SINGLE => 0x20, # Start with single-step on
+ PERLDBf_NONAME => 0x40, # For _SUB: no name of the subr
+ PERLDBf_GOTO => 0x80, # Report goto: call DB::goto
+ PERLDBf_NAMEEVAL => 0x100, # Informative names for evals
+ PERLDBf_NAMEANON => 0x200, # Informative names for anon subs
+ PERLDBf_ASSERTION => 0x400, # Debug assertion subs enter/exit
+ PERLDB_ALL => 0x33f, # No _NONAME, _GOTO, _ASSERTION
+ );
+
+ %DollarCaretP_flags_r = reverse %DollarCaretP_flags;
+}
+
+sub parse_DollarCaretP_flags {
+ my $flags = shift;
+ $flags =~ s/^\s+//;
+ $flags =~ s/\s+$//;
+ my $acu = 0;
+ foreach my $f ( split /\s*\|\s*/, $flags ) {
+ my $value;
+ if ( $f =~ /^0x([[:xdigit:]]+)$/ ) {
+ $value = hex $1;
+ }
+ elsif ( $f =~ /^(\d+)$/ ) {
+ $value = int $1;
+ }
+ elsif ( $f =~ /^DEFAULT$/i ) {
+ $value = $DollarCaretP_flags{PERLDB_ALL};
+ }
+ else {
+ $f =~ /^(?:PERLDBf_)?(.*)$/i;
+ $value = $DollarCaretP_flags{ 'PERLDBf_' . uc($1) };
+ unless ( defined $value ) {
+ print $OUT (
+ "Unrecognized \$^P flag '$f'!\n",
+ "Acceptable flags are: "
+ . join( ', ', sort keys %DollarCaretP_flags ),
+ ", and hexadecimal and decimal numbers.\n"
+ );
+ return undef;
+ }
+ }
+ $acu |= $value;
+ }
+ $acu;
+}
+
+sub expand_DollarCaretP_flags {
+ my $DollarCaretP = shift;
+ my @bits = (
+ map {
+ my $n = ( 1 << $_ );
+ ( $DollarCaretP & $n )
+ ? ( $DollarCaretP_flags_r{$n}
+ || sprintf( '0x%x', $n ) )
+ : ()
+ } 0 .. 31
+ );
+ return @bits ? join( '|', @bits ) : 0;
+}
+
+=over 4
+
+=item rerun
+
+Rerun the current session to:
+
+ rerun current position
+
+ rerun 4 command number 4
+
+ rerun -4 current command minus 4 (go back 4 steps)
+
+Whether this always makes sense, in the current context is unknowable, and is
+in part left as a useful exersize for the reader. This sub returns the
+appropriate arguments to rerun the current session.
+
+=cut
+
+sub rerun {
+ my $i = shift;
+ my @args;
+ pop(@truehist); # strim
+ unless (defined $truehist[$i]) {
+ print "Unable to return to non-existent command: $i\n";
+ } else {
+ $#truehist = ($i < 0 ? $#truehist + $i : $i > 0 ? $i : $#truehist);
+ my @temp = @truehist; # store
+ push(@DB::typeahead, @truehist); # saved
+ @truehist = @hist = (); # flush
+ @args = &restart(); # setup
+ &get_list("PERLDB_HIST"); # clean
+ &set_list("PERLDB_HIST", @temp); # reset
+ }
+ return @args;
+}
+
+=item restart
+
+Restarting the debugger is a complex operation that occurs in several phases.
+First, we try to reconstruct the command line that was used to invoke Perl
+and the debugger.
+
+=cut
+
+sub restart {
+ # I may not be able to resurrect you, but here goes ...
+ print $OUT
+"Warning: some settings and command-line options may be lost!\n";
+ my ( @script, @flags, $cl );
+
+ # If warn was on before, turn it on again.
+ push @flags, '-w' if $ini_warn;
+ if ( $ini_assertion and @{^ASSERTING} ) {
+ push @flags,
+ ( map { /\:\^\(\?\:(.*)\)\$\)/ ? "-A$1" : "-A$_" }
+ @{^ASSERTING} );
+ }
+
+ # Rebuild the -I flags that were on the initial
+ # command line.
+ for (@ini_INC) {
+ push @flags, '-I', $_;
+ }
+
+ # Turn on taint if it was on before.
+ push @flags, '-T' if ${^TAINT};
+
+ # Arrange for setting the old INC:
+ # Save the current @init_INC in the environment.
+ set_list( "PERLDB_INC", @ini_INC );
+
+ # If this was a perl one-liner, go to the "file"
+ # corresponding to the one-liner read all the lines
+ # out of it (except for the first one, which is going
+ # to be added back on again when 'perl -d' runs: that's
+ # the 'require perl5db.pl;' line), and add them back on
+ # to the command line to be executed.
+ if ( $0 eq '-e' ) {
+ for ( 1 .. $#{'::_<-e'} ) { # The first line is PERL5DB
+ chomp( $cl = ${'::_<-e'}[$_] );
+ push @script, '-e', $cl;
+ }
+ } ## end if ($0 eq '-e')
+
+ # Otherwise we just reuse the original name we had
+ # before.
+ else {
+ @script = $0;
+ }
+
+=pod
+
+After the command line has been reconstructed, the next step is to save
+the debugger's status in environment variables. The C<DB::set_list> routine
+is used to save aggregate variables (both hashes and arrays); scalars are
+just popped into environment variables directly.
+
+=cut
+
+ # If the terminal supported history, grab it and
+ # save that in the environment.
+ set_list( "PERLDB_HIST",
+ $term->Features->{getHistory}
+ ? $term->GetHistory
+ : @hist );
+
+ # Find all the files that were visited during this
+ # session (i.e., the debugger had magic hashes
+ # corresponding to them) and stick them in the environment.
+ my @had_breakpoints = keys %had_breakpoints;
+ set_list( "PERLDB_VISITED", @had_breakpoints );
+
+ # Save the debugger options we chose.
+ set_list( "PERLDB_OPT", %option );
+ # set_list( "PERLDB_OPT", options2remember() );
+
+ # Save the break-on-loads.
+ set_list( "PERLDB_ON_LOAD", %break_on_load );
+
+=pod
+
+The most complex part of this is the saving of all of the breakpoints. They
+can live in an awful lot of places, and we have to go through all of them,
+find the breakpoints, and then save them in the appropriate environment
+variable via C<DB::set_list>.
+
+=cut
+
+ # Go through all the breakpoints and make sure they're
+ # still valid.
+ my @hard;
+ for ( 0 .. $#had_breakpoints ) {
+
+ # We were in this file.
+ my $file = $had_breakpoints[$_];
+
+ # Grab that file's magic line hash.
+ *dbline = $main::{ '_<' . $file };
+
+ # Skip out if it doesn't exist, or if the breakpoint
+ # is in a postponed file (we'll do postponed ones
+ # later).
+ next unless %dbline or $postponed_file{$file};
+
+ # In an eval. This is a little harder, so we'll
+ # do more processing on that below.
+ ( push @hard, $file ), next
+ if $file =~ /^\(\w*eval/;
+
+ # XXX I have no idea what this is doing. Yet.
+ my @add;
+ @add = %{ $postponed_file{$file} }
+ if $postponed_file{$file};
+
+ # Save the list of all the breakpoints for this file.
+ set_list( "PERLDB_FILE_$_", %dbline, @add );
+ } ## end for (0 .. $#had_breakpoints)
+
+ # The breakpoint was inside an eval. This is a little
+ # more difficult. XXX and I don't understand it.
+ for (@hard) {
+ # Get over to the eval in question.
+ *dbline = $main::{ '_<' . $_ };
+ my ( $quoted, $sub, %subs, $line ) = quotemeta $_;
+ for $sub ( keys %sub ) {
+ next unless $sub{$sub} =~ /^$quoted:(\d+)-(\d+)$/;
+ $subs{$sub} = [ $1, $2 ];
+ }
+ unless (%subs) {
+ print $OUT
+ "No subroutines in $_, ignoring breakpoints.\n";
+ next;
+ }
+ LINES: for $line ( keys %dbline ) {
+
+ # One breakpoint per sub only:
+ my ( $offset, $sub, $found );
+ SUBS: for $sub ( keys %subs ) {
+ if (
+ $subs{$sub}->[1] >=
+ $line # Not after the subroutine
+ and (
+ not defined $offset # Not caught
+ or $offset < 0
+ )
+ )
+ { # or badly caught
+ $found = $sub;
+ $offset = $line - $subs{$sub}->[0];
+ $offset = "+$offset", last SUBS
+ if $offset >= 0;
+ } ## end if ($subs{$sub}->[1] >=...
+ } ## end for $sub (keys %subs)
+ if ( defined $offset ) {
+ $postponed{$found} =
+ "break $offset if $dbline{$line}";
+ }
+ else {
+ print $OUT
+"Breakpoint in $_:$line ignored: after all the subroutines.\n";
+ }
+ } ## end for $line (keys %dbline)
+ } ## end for (@hard)
+
+ # Save the other things that don't need to be
+ # processed.
+ set_list( "PERLDB_POSTPONE", %postponed );
+ set_list( "PERLDB_PRETYPE", @$pretype );
+ set_list( "PERLDB_PRE", @$pre );
+ set_list( "PERLDB_POST", @$post );
+ set_list( "PERLDB_TYPEAHEAD", @typeahead );
+
+ # We are oficially restarting.
+ $ENV{PERLDB_RESTART} = 1;
+
+ # We are junking all child debuggers.
+ delete $ENV{PERLDB_PIDS}; # Restore ini state
+
+ # Set this back to the initial pid.
+ $ENV{PERLDB_PIDS} = $ini_pids if defined $ini_pids;
+
+=pod
+
+After all the debugger status has been saved, we take the command we built up
+and then return it, so we can C<exec()> it. The debugger will spot the
+C<PERLDB_RESTART> environment variable and realize it needs to reload its state
+from the environment.
+
+=cut
+
+ # And run Perl again. Add the "-d" flag, all the
+ # flags we built up, the script (whether a one-liner
+ # or a file), add on the -emacs flag for a slave editor,
+ # and then the old arguments.
+
+ return ($^X, '-d', @flags, @script, ($slave_editor ? '-emacs' : ()), @ARGS);
+
+}; # end restart
+
+=back
+
+=head1 END PROCESSING - THE C<END> BLOCK
+
+Come here at the very end of processing. We want to go into a
+loop where we allow the user to enter commands and interact with the
+debugger, but we don't want anything else to execute.
+
+First we set the C<$finished> variable, so that some commands that
+shouldn't be run after the end of program quit working.
+
+We then figure out whether we're truly done (as in the user entered a C<q>
+command, or we finished execution while running nonstop). If we aren't,
+we set C<$single> to 1 (causing the debugger to get control again).
+
+We then call C<DB::fake::at_exit()>, which returns the C<Use 'q' to quit ...>
+message and returns control to the debugger. Repeat.
+
+When the user finally enters a C<q> command, C<$fall_off_end> is set to
+1 and the C<END> block simply exits with C<$single> set to 0 (don't
+break, run to completion.).
+
+=cut
+
+END {
+ $finished = 1 if $inhibit_exit; # So that some commands may be disabled.
+ $fall_off_end = 1 unless $inhibit_exit;
+
+ # Do not stop in at_exit() and destructors on exit:
+ $DB::single = !$fall_off_end && !$runnonstop;
+ DB::fake::at_exit() unless $fall_off_end or $runnonstop;
+} ## end END
+
+=head1 PRE-5.8 COMMANDS
+
+Some of the commands changed function quite a bit in the 5.8 command
+realignment, so much so that the old code had to be replaced completely.
+Because we wanted to retain the option of being able to go back to the
+former command set, we moved the old code off to this section.
+
+There's an awful lot of duplicated code here. We've duplicated the
+comments to keep things clear.
+
+=head2 Null command
+
+Does nothing. Used to I<turn off> commands.
+
+=cut
+
+sub cmd_pre580_null {
+
+ # do nothing...
+}
+
+=head2 Old C<a> command.
+
+This version added actions if you supplied them, and deleted them
+if you didn't.
+
+=cut
+
+sub cmd_pre580_a {
+ my $xcmd = shift;
+ my $cmd = shift;
+
+ # Argument supplied. Add the action.
+ if ( $cmd =~ /^(\d*)\s*(.*)/ ) {
+
+ # If the line isn't there, use the current line.
+ $i = $1 || $line;
+ $j = $2;
+
+ # If there is an action ...
+ if ( length $j ) {
+
+ # ... but the line isn't breakable, skip it.
+ if ( $dbline[$i] == 0 ) {
+ print $OUT "Line $i may not have an action.\n";
+ }
+ else {
+
+ # ... and the line is breakable:
+ # Mark that there's an action in this file.
+ $had_breakpoints{$filename} |= 2;
+
+ # Delete any current action.
+ $dbline{$i} =~ s/\0[^\0]*//;
+
+ # Add the new action, continuing the line as needed.
+ $dbline{$i} .= "\0" . action($j);
+ }
+ } ## end if (length $j)
+
+ # No action supplied.
+ else {
+
+ # Delete the action.
+ $dbline{$i} =~ s/\0[^\0]*//;
+
+ # Mark as having no break or action if nothing's left.
+ delete $dbline{$i} if $dbline{$i} eq '';
+ }
+ } ## end if ($cmd =~ /^(\d*)\s*(.*)/)
+} ## end sub cmd_pre580_a
+
+=head2 Old C<b> command
+
+Add breakpoints.
+
+=cut
+
+sub cmd_pre580_b {
+ my $xcmd = shift;
+ my $cmd = shift;
+ my $dbline = shift;
+
+ # Break on load.
+ if ( $cmd =~ /^load\b\s*(.*)/ ) {
+ my $file = $1;
+ $file =~ s/\s+$//;
+ &cmd_b_load($file);
+ }
+
+ # b compile|postpone <some sub> [<condition>]
+ # The interpreter actually traps this one for us; we just put the
+ # necessary condition in the %postponed hash.
+ elsif ( $cmd =~ /^(postpone|compile)\b\s*([':A-Za-z_][':\w]*)\s*(.*)/ ) {
+
+ # Capture the condition if there is one. Make it true if none.
+ my $cond = length $3 ? $3 : '1';
+
+ # Save the sub name and set $break to 1 if $1 was 'postpone', 0
+ # if it was 'compile'.
+ my ( $subname, $break ) = ( $2, $1 eq 'postpone' );
+
+ # De-Perl4-ify the name - ' separators to ::.
+ $subname =~ s/\'/::/g;
+
+ # Qualify it into the current package unless it's already qualified.
+ $subname = "${'package'}::" . $subname
+ unless $subname =~ /::/;
+
+ # Add main if it starts with ::.
+ $subname = "main" . $subname if substr( $subname, 0, 2 ) eq "::";
+
+ # Save the break type for this sub.
+ $postponed{$subname} = $break ? "break +0 if $cond" : "compile";
+ } ## end elsif ($cmd =~ ...
+
+ # b <sub name> [<condition>]
+ elsif ( $cmd =~ /^([':A-Za-z_][':\w]*(?:\[.*\])?)\s*(.*)/ ) {
+ my $subname = $1;
+ my $cond = length $2 ? $2 : '1';
+ &cmd_b_sub( $subname, $cond );
+ }
+
+ # b <line> [<condition>].
+ elsif ( $cmd =~ /^(\d*)\s*(.*)/ ) {
+ my $i = $1 || $dbline;
+ my $cond = length $2 ? $2 : '1';
+ &cmd_b_line( $i, $cond );
+ }
+} ## end sub cmd_pre580_b
+
+=head2 Old C<D> command.
+
+Delete all breakpoints unconditionally.
+
+=cut
+
+sub cmd_pre580_D {
+ my $xcmd = shift;
+ my $cmd = shift;
+ if ( $cmd =~ /^\s*$/ ) {
+ print $OUT "Deleting all breakpoints...\n";
+
+ # %had_breakpoints lists every file that had at least one
+ # breakpoint in it.
+ my $file;
+ for $file ( keys %had_breakpoints ) {
+
+ # Switch to the desired file temporarily.
+ local *dbline = $main::{ '_<' . $file };
+
+ my $max = $#dbline;
+ my $was;
+
+ # For all lines in this file ...
+ for ( $i = 1 ; $i <= $max ; $i++ ) {
+
+ # If there's a breakpoint or action on this line ...
+ if ( defined $dbline{$i} ) {
+
+ # ... remove the breakpoint.
+ $dbline{$i} =~ s/^[^\0]+//;
+ if ( $dbline{$i} =~ s/^\0?$// ) {
+
+ # Remove the entry altogether if no action is there.
+ delete $dbline{$i};
+ }
+ } ## end if (defined $dbline{$i...
+ } ## end for ($i = 1 ; $i <= $max...
+
+ # If, after we turn off the "there were breakpoints in this file"
+ # bit, the entry in %had_breakpoints for this file is zero,
+ # we should remove this file from the hash.
+ if ( not $had_breakpoints{$file} &= ~1 ) {
+ delete $had_breakpoints{$file};
+ }
+ } ## end for $file (keys %had_breakpoints)
+
+ # Kill off all the other breakpoints that are waiting for files that
+ # haven't been loaded yet.
+ undef %postponed;
+ undef %postponed_file;
+ undef %break_on_load;
+ } ## end if ($cmd =~ /^\s*$/)
+} ## end sub cmd_pre580_D
+
+=head2 Old C<h> command
+
+Print help. Defaults to printing the long-form help; the 5.8 version
+prints the summary by default.
+
+=cut
+
+sub cmd_pre580_h {
+ my $xcmd = shift;
+ my $cmd = shift;
+
+ # Print the *right* help, long format.
+ if ( $cmd =~ /^\s*$/ ) {
+ print_help($pre580_help);
+ }
+
+ # 'h h' - explicitly-requested summary.
+ elsif ( $cmd =~ /^h\s*/ ) {
+ print_help($pre580_summary);
+ }
+
+ # Find and print a command's help.
+ elsif ( $cmd =~ /^h\s+(\S.*)$/ ) {
+ my $asked = $1; # for proper errmsg
+ my $qasked = quotemeta($asked); # for searching
+ # XXX: finds CR but not <CR>
+ if (
+ $pre580_help =~ /^
+ <? # Optional '<'
+ (?:[IB]<) # Optional markup
+ $qasked # The command name
+ /mx
+ )
+ {
+
+ while (
+ $pre580_help =~ /^
+ ( # The command help:
+ <? # Optional '<'
+ (?:[IB]<) # Optional markup
+ $qasked # The command name
+ ([\s\S]*?) # Lines starting with tabs
+ \n # Final newline
+ )
+ (?!\s)/mgx
+ ) # Line not starting with space
+ # (Next command's help)
+ {
+ print_help($1);
+ }
+ } ## end if ($pre580_help =~ /^<?(?:[IB]<)$qasked/m)
+
+ # Help not found.
+ else {
+ print_help("B<$asked> is not a debugger command.\n");
+ }
+ } ## end elsif ($cmd =~ /^h\s+(\S.*)$/)
+} ## end sub cmd_pre580_h
+
+=head2 Old C<W> command
+
+C<W E<lt>exprE<gt>> adds a watch expression, C<W> deletes them all.
+
+=cut
+
+sub cmd_pre580_W {
+ my $xcmd = shift;
+ my $cmd = shift;
+
+ # Delete all watch expressions.
+ if ( $cmd =~ /^$/ ) {
+
+ # No watching is going on.
+ $trace &= ~2;
+
+ # Kill all the watch expressions and values.
+ @to_watch = @old_watch = ();
+ }
+
+ # Add a watch expression.
+ elsif ( $cmd =~ /^(.*)/s ) {
+
+ # add it to the list to be watched.
+ push @to_watch, $1;
+
+ # Get the current value of the expression.
+ # Doesn't handle expressions returning list values!
+ $evalarg = $1;
+ my ($val) = &eval;
+ $val = ( defined $val ) ? "'$val'" : 'undef';
+
+ # Save it.
+ push @old_watch, $val;
+
+ # We're watching stuff.
+ $trace |= 2;
+
+ } ## end elsif ($cmd =~ /^(.*)/s)
+} ## end sub cmd_pre580_W
+
+=head1 PRE-AND-POST-PROMPT COMMANDS AND ACTIONS
+
+The debugger used to have a bunch of nearly-identical code to handle
+the pre-and-post-prompt action commands. C<cmd_pre590_prepost> and
+C<cmd_prepost> unify all this into one set of code to handle the
+appropriate actions.
+
+=head2 C<cmd_pre590_prepost>
+
+A small wrapper around C<cmd_prepost>; it makes sure that the default doesn't
+do something destructive. In pre 5.8 debuggers, the default action was to
+delete all the actions.
+
+=cut
+
+sub cmd_pre590_prepost {
+ my $cmd = shift;
+ my $line = shift || '*';
+ my $dbline = shift;
+
+ return &cmd_prepost( $cmd, $line, $dbline );
+} ## end sub cmd_pre590_prepost
+
+=head2 C<cmd_prepost>
+
+Actually does all the handling for C<E<lt>>, C<E<gt>>, C<{{>, C<{>, etc.
+Since the lists of actions are all held in arrays that are pointed to by
+references anyway, all we have to do is pick the right array reference and
+then use generic code to all, delete, or list actions.
+
+=cut
+
+sub cmd_prepost {
+ my $cmd = shift;
+
+ # No action supplied defaults to 'list'.
+ my $line = shift || '?';
+
+ # Figure out what to put in the prompt.
+ my $which = '';
+
+ # Make sure we have some array or another to address later.
+ # This means that if ssome reason the tests fail, we won't be
+ # trying to stash actions or delete them from the wrong place.
+ my $aref = [];
+
+ # < - Perl code to run before prompt.
+ if ( $cmd =~ /^\</o ) {
+ $which = 'pre-perl';
+ $aref = $pre;
+ }
+
+ # > - Perl code to run after prompt.
+ elsif ( $cmd =~ /^\>/o ) {
+ $which = 'post-perl';
+ $aref = $post;
+ }
+
+ # { - first check for properly-balanced braces.
+ elsif ( $cmd =~ /^\{/o ) {
+ if ( $cmd =~ /^\{.*\}$/o && unbalanced( substr( $cmd, 1 ) ) ) {
+ print $OUT
+"$cmd is now a debugger command\nuse `;$cmd' if you mean Perl code\n";
+ }
+
+ # Properly balanced. Pre-prompt debugger actions.
+ else {
+ $which = 'pre-debugger';
+ $aref = $pretype;
+ }
+ } ## end elsif ( $cmd =~ /^\{/o )
+
+ # Did we find something that makes sense?
+ unless ($which) {
+ print $OUT "Confused by command: $cmd\n";
+ }
+
+ # Yes.
+ else {
+
+ # List actions.
+ if ( $line =~ /^\s*\?\s*$/o ) {
+ unless (@$aref) {
+
+ # Nothing there. Complain.
+ print $OUT "No $which actions.\n";
+ }
+ else {
+
+ # List the actions in the selected list.
+ print $OUT "$which commands:\n";
+ foreach my $action (@$aref) {
+ print $OUT "\t$cmd -- $action\n";
+ }
+ } ## end else
+ } ## end if ( $line =~ /^\s*\?\s*$/o)
+
+ # Might be a delete.
+ else {
+ if ( length($cmd) == 1 ) {
+ if ( $line =~ /^\s*\*\s*$/o ) {
+
+ # It's a delete. Get rid of the old actions in the
+ # selected list..
+ @$aref = ();
+ print $OUT "All $cmd actions cleared.\n";
+ }
+ else {
+
+ # Replace all the actions. (This is a <, >, or {).
+ @$aref = action($line);
+ }
+ } ## end if ( length($cmd) == 1)
+ elsif ( length($cmd) == 2 ) {
+
+ # Add the action to the line. (This is a <<, >>, or {{).
+ push @$aref, action($line);
+ }
+ else {
+
+ # <<<, >>>>, {{{{{{ ... something not a command.
+ print $OUT
+ "Confused by strange length of $which command($cmd)...\n";
+ }
+ } ## end else [ if ( $line =~ /^\s*\?\s*$/o)
+ } ## end else
+} ## end sub cmd_prepost
+
+=head1 C<DB::fake>
+
+Contains the C<at_exit> routine that the debugger uses to issue the
+C<Debugged program terminated ...> message after the program completes. See
+the C<END> block documentation for more details.
+
+=cut
+
+package DB::fake;
+
+sub at_exit {
+ "Debugged program terminated. Use `q' to quit or `R' to restart.";
+}
+
+package DB; # Do not trace this 1; below!
+
+1;
+
+
diff --git a/tests/examplefiles/pleac.in.rb b/tests/examplefiles/pleac.in.rb
new file mode 100644
index 00000000..0f967b9b
--- /dev/null
+++ b/tests/examplefiles/pleac.in.rb
@@ -0,0 +1,6368 @@
+# -*- ruby -*-
+
+# Local variables:
+# indent-tabs-mode: nil
+# ruby-indent-level: 4
+# End:
+
+# @@PLEAC@@_NAME
+# @@SKIP@@ Ruby
+
+# @@PLEAC@@_WEB
+# @@SKIP@@ http://www.ruby-lang.org
+
+
+# @@PLEAC@@_1.0
+string = '\n' # two characters, \ and an n
+string = 'Jon \'Maddog\' Orwant' # literal single quotes
+
+string = "\n" # a "newline" character
+string = "Jon \"Maddog\" Orwant" # literal double quotes
+
+string = %q/Jon 'Maddog' Orwant/ # literal single quotes
+
+string = %q[Jon 'Maddog' Orwant] # literal single quotes
+string = %q{Jon 'Maddog' Orwant} # literal single quotes
+string = %q(Jon 'Maddog' Orwant) # literal single quotes
+string = %q<Jon 'Maddog' Orwant> # literal single quotes
+
+a = <<"EOF"
+This is a multiline here document
+terminated by EOF on a line by itself
+EOF
+
+
+# @@PLEAC@@_1.1
+value = string[offset,count]
+value = string[offset..-1]
+
+string[offset,count] = newstring
+string[offset..-1] = newtail
+
+# in Ruby we can also specify intervals by their two offsets
+value = string[offset..offs2]
+string[offset..offs2] = newstring
+
+leading, s1, s2, trailing = data.unpack("A5 x3 A8 A8 A*")
+
+fivers = string.unpack("A5" * (string.length/5))
+
+chars = string.unpack("A1" * string.length)
+
+string = "This is what you have"
+# +012345678901234567890 Indexing forwards (left to right)
+# 109876543210987654321- Indexing backwards (right to left)
+# note that 0 means 10 or 20, etc. above
+
+first = string[0, 1] # "T"
+start = string[5, 2] # "is"
+rest = string[13..-1] # "you have"
+last = string[-1, 1] # "e"
+end_ = string[-4..-1] # "have"
+piece = string[-8, 3] # "you"
+
+string[5, 2] = "wasn't" # change "is" to "wasn't"
+string[-12..-1] = "ondrous" # "This wasn't wondrous"
+string[0, 1] = "" # delete first character
+string[-10..-1] = "" # delete last 10 characters
+
+if string[-10..-1] =~ /pattern/
+ puts "Pattern matches in last 10 characters"
+end
+
+string[0, 5].gsub!(/is/, 'at')
+
+a = "make a hat"
+a[0, 1], a[-1, 1] = a[-1, 1], a[0, 1]
+
+a = "To be or not to be"
+b = a.unpack("x6 A6")
+
+b, c = a.unpack("x6 A2 X5 A2")
+puts "#{b}\n#{c}\n"
+
+def cut2fmt(*args)
+ template = ''
+ lastpos = 1
+ for place in args
+ template += "A" + (place - lastpos).to_s + " "
+ lastpos = place
+ end
+ template += "A*"
+ return template
+end
+
+fmt = cut2fmt(8, 14, 20, 26, 30)
+
+
+# @@PLEAC@@_1.2
+# careful! "b is true" doesn't mean "b != 0" (0 is true in Ruby)
+# thus no problem of "defined" later since only nil is false
+# the following sets to `c' if `b' is nil or false
+a = b || c
+
+# if you need Perl's behaviour (setting to `c' if `b' is 0) the most
+# effective way is to use Numeric#nonzero? (thanks to Dave Thomas!)
+a = b.nonzero? || c
+
+# you will still want to use defined? in order to test
+# for scope existence of a given object
+a = defined?(b) ? b : c
+
+dir = ARGV.shift || "/tmp"
+
+
+# @@PLEAC@@_1.3
+v1, v2 = v2, v1
+
+alpha, beta, production = %w(January March August)
+alpha, beta, production = beta, production, alpha
+
+
+# @@PLEAC@@_1.4
+num = char[0]
+char = num.chr
+
+# Ruby also supports having a char from character constant
+num = ?r
+
+char = sprintf("%c", num)
+printf("Number %d is character %c\n", num, num)
+
+ascii = string.unpack("C*")
+string = ascii.pack("C*")
+
+hal = "HAL"
+ascii = hal.unpack("C*")
+# We can't use Array#each since we can't mutate a Fixnum
+ascii.collect! { |i|
+ i + 1 # add one to each ASCII value
+}
+ibm = ascii.pack("C*")
+puts ibm
+
+
+# @@PLEAC@@_1.5
+array = string.split('')
+
+array = string.unpack("C*")
+
+string.scan(/./) { |b|
+ # do something with b
+}
+
+string = "an apple a day"
+print "unique chars are: ", string.split('').uniq.sort, "\n"
+
+sum = 0
+for ascval in string.unpack("C*") # or use Array#each for a pure OO style :)
+ sum += ascval
+end
+puts "sum is #{sum & 0xffffffff}" # since Ruby will go Bignum if necessary
+
+# @@INCLUDE@@ include/ruby/slowcat.rb
+
+
+# @@PLEAC@@_1.6
+revbytes = string.reverse
+
+revwords = string.split(" ").reverse.join(" ")
+
+revwords = string.split(/(\s+)/).reverse.join
+
+# using the fact that IO is Enumerable, you can directly "select" it
+long_palindromes = File.open("/usr/share/dict/words").
+ select { |w| w.chomp!; w.reverse == w && w.length > 5 }
+
+
+# @@PLEAC@@_1.7
+while string.sub!("\t+") { ' ' * ($&.length * 8 - $`.length % 8) }
+end
+
+
+# @@PLEAC@@_1.8
+'You owe #{debt} to me'.gsub(/\#{(\w+)}/) { eval($1) }
+
+rows, cols = 24, 80
+text = %q(I am #{rows} high and #{cols} long)
+text.gsub!(/\#{(\w+)}/) { eval("#{$1}") }
+puts text
+
+'I am 17 years old'.gsub(/\d+/) { 2 * $&.to_i }
+
+
+# @@PLEAC@@_1.9
+e = "bo peep".upcase
+e.downcase!
+e.capitalize!
+
+"thIS is a loNG liNE".gsub!(/\w+/) { $&.capitalize }
+
+
+# @@PLEAC@@_1.10
+"I have #{n+1} guanacos."
+print "I have ", n+1, " guanacos."
+
+
+# @@PLEAC@@_1.11
+var = <<'EOF'.gsub(/^\s+/, '')
+ your text
+ goes here
+EOF
+
+
+# @@PLEAC@@_1.12
+string = "Folding and splicing is the work of an editor,\n"+
+ "not a mere collection of silicon\n"+
+ "and\n"+
+ "mobile electrons!"
+
+def wrap(str, max_size)
+ all = []
+ line = ''
+ for l in str.split
+ if (line+l).length >= max_size
+ all.push(line)
+ line = ''
+ end
+ line += line == '' ? l : ' ' + l
+ end
+ all.push(line).join("\n")
+end
+
+print wrap(string, 20)
+#=> Folding and
+#=> splicing is the
+#=> work of an editor,
+#=> not a mere
+#=> collection of
+#=> silicon and mobile
+#=> electrons!
+
+
+# @@PLEAC@@_1.13
+string = %q(Mom said, "Don't do that.")
+string.gsub(/['"]/) { '\\'+$& }
+string.gsub(/['"]/, '\&\&')
+string.gsub(/[^A-Z]/) { '\\'+$& }
+"is a test!".gsub(/\W/) { '\\'+$& } # no function like quotemeta?
+
+
+# @@PLEAC@@_1.14
+string.strip!
+
+
+# @@PLEAC@@_1.15
+def parse_csv(text)
+ new = text.scan(/"([^\"\\]*(?:\\.[^\"\\]*)*)",?|([^,]+),?|,/)
+ new << nil if text[-1] == ?,
+ new.flatten.compact
+end
+
+line = %q<XYZZY,"","O'Reilly, Inc","Wall, Larry","a \"glug\" bit,",5,"Error, Core Dumped">
+fields = parse_csv(line)
+fields.each_with_index { |v,i|
+ print "#{i} : #{v}\n";
+}
+
+
+# @@PLEAC@@_1.16
+# Use the soundex.rb Library from Michael Neumann.
+# http://www.s-direktnet.de/homepages/neumann/rb_prgs/Soundex.rb
+require 'Soundex'
+
+code = Text::Soundex.soundex(string)
+codes = Text::Soundex.soundex(array)
+
+# substitution function for getpwent():
+# returns an array of user entries,
+# each entry contains the username and the full name
+def login_names
+ result = []
+ File.open("/etc/passwd") { |file|
+ file.each_line { |line|
+ next if line.match(/^#/)
+ cols = line.split(":")
+ result.push([cols[0], cols[4]])
+ }
+ }
+ result
+end
+
+puts "Lookup user: "
+user = STDIN.gets
+user.chomp!
+exit unless user
+name_code = Text::Soundex.soundex(user)
+
+splitter = Regexp.new('(\w+)[^,]*\b(\w+)')
+for username, fullname in login_names do
+ firstname, lastname = splitter.match(fullname)[1,2]
+ if name_code == Text::Soundex.soundex(username)
+ || name_code == Text::Soundex.soundex(firstname)
+ || name_code == Text::Soundex.soundex(lastname)
+ then
+ puts "#{username}: #{firstname} #{lastname}"
+ end
+end
+
+
+# @@PLEAC@@_1.17
+# @@INCLUDE@@ include/ruby/fixstyle.rb
+
+
+# @@PLEAC@@_1.18
+# @@INCLUDE@@ include/ruby/psgrep.rb
+
+
+# @@PLEAC@@_2.1
+# Matz tells that you can use Integer() for strict checked conversion.
+Integer("abc")
+#=> `Integer': invalid value for Integer: "abc" (ArgumentError)
+Integer("567")
+#=> 567
+
+# You may use Float() for floating point stuff
+Integer("56.7")
+#=> `Integer': invalid value for Integer: "56.7" (ArgumentError)
+Float("56.7")
+#=> 56.7
+
+# You may also use a regexp for that
+if string =~ /^[+-]?\d+$/
+ p 'is an integer'
+else
+ p 'is not'
+end
+
+if string =~ /^-?(?:\d+(?:\.\d*)?|\.\d+)$/
+ p 'is a decimal number'
+else
+ p 'is not'
+end
+
+
+# @@PLEAC@@_2.2
+# equal(num1, num2, accuracy) : returns true if num1 and num2 are
+# equal to accuracy number of decimal places
+def equal(i, j, a)
+ sprintf("%.#{a}g", i) == sprintf("%.#{a}g", j)
+end
+
+wage = 536 # $5.36/hour
+week = 40 * wage # $214.40
+printf("One week's wage is: \$%.2f\n", week/100.0)
+
+
+# @@PLEAC@@_2.3
+num.round # rounds to integer
+
+a = 0.255
+b = sprintf("%.2f", a)
+print "Unrounded: #{a}\nRounded: #{b}\n"
+printf "Unrounded: #{a}\nRounded: %.2f\n", a
+
+print "number\tint\tfloor\tceil\n"
+a = [ 3.3 , 3.5 , 3.7, -3.3 ]
+for n in a
+ printf("% .1f\t% .1f\t% .1f\t% .1f\n", # at least I don't fake my output :)
+ n, n.to_i, n.floor, n.ceil)
+end
+
+
+# @@PLEAC@@_2.4
+def dec2bin(n)
+ [n].pack("N").unpack("B32")[0].sub(/^0+(?=\d)/, '')
+end
+
+def bin2dec(n)
+ [("0"*32+n.to_s)[-32..-1]].pack("B32").unpack("N")[0]
+end
+
+
+# @@PLEAC@@_2.5
+for i in x .. y
+ # i is set to every integer from x to y, inclusive
+end
+
+x.step(y,7) { |i|
+ # i is set to every integer from x to y, stepsize = 7
+}
+
+print "Infancy is: "
+(0..2).each { |i|
+ print i, " "
+}
+print "\n"
+
+
+# @@PLEAC@@_2.6
+# We can add conversion methods to the Integer class,
+# this makes a roman number just a representation for normal numbers.
+class Integer
+
+ @@romanlist = [["M", 1000],
+ ["CM", 900],
+ ["D", 500],
+ ["CD", 400],
+ ["C", 100],
+ ["XC", 90],
+ ["L", 50],
+ ["XL", 40],
+ ["X", 10],
+ ["IX", 9],
+ ["V", 5],
+ ["IV", 4],
+ ["I", 1]]
+
+ def to_roman
+ remains = self
+ roman = ""
+ for sym, num in @@romanlist
+ while remains >= num
+ remains -= num
+ roman << sym
+ end
+ end
+ roman
+ end
+
+ def Integer.from_roman(roman)
+ ustr = roman.upcase
+ sum = 0
+ for entry in @@romanlist
+ sym, num = entry[0], entry[1]
+ while sym == ustr[0, sym.length]
+ sum += num
+ ustr.slice!(0, sym.length)
+ end
+ end
+ sum
+ end
+
+end
+
+
+roman_fifteen = 15.to_roman
+puts "Roman for fifteen is #{roman_fifteen}"
+i = Integer.from_roman(roman_fifteen)
+puts "Converted back, #{roman_fifteen} is #{i}"
+
+# check
+for i in (1..3900)
+ r = i.to_roman
+ j = Integer.from_roman(r)
+ if i != j
+ puts "error: #{i} : #{r} - #{j}"
+ end
+end
+
+
+# @@PLEAC@@_2.7
+random = rand(y-x+1)+x
+
+chars = ["A".."Z","a".."z","0".."9"].collect { |r| r.to_a }.join + %q(!@$%^&*)
+password = (1..8).collect { chars[rand(chars.size)] }.pack("C*")
+
+
+# @@PLEAC@@_2.8
+srand # uses a combination of the time, the process id, and a sequence number
+srand(val) # for repeatable behaviour
+
+
+# @@PLEAC@@_2.9
+# from the randomr lib:
+# http://raa.ruby-lang.org/project/randomr/
+----> http://raa.ruby-lang.org/project/randomr/
+
+require 'random/mersenne_twister'
+mers = Random::MersenneTwister.new 123456789
+puts mers.rand(0) # 0.550321932544541
+puts mers.rand(10) # 2
+
+# using online sources of random data via the realrand package:
+# http://raa.ruby-lang.org/project/realrand/
+# **Note**
+# The following online services are used in this package:
+# http://www.random.org - source: atmospheric noise
+# http://www.fourmilab.ch/hotbits - source: radioactive decay timings
+# http://random.hd.org - source: entropy from local and network noise
+# Please visit the sites and respect the rules of each service.
+
+require 'random/online'
+
+generator1 = Random::RandomOrg.new
+puts generator1.randbyte(5).join(",")
+puts generator1.randnum(10, 1, 6).join(",") # Roll dice 10 times.
+
+generator2 = Random::FourmiLab.new
+puts generator2.randbyte(5).join(",")
+# randnum is not supported.
+
+generator3 = Random::EntropyPool.new
+puts generator3.randbyte(5).join(",")
+# randnum is not supported.
+
+
+# @@PLEAC@@_2.10
+def gaussian_rand
+ begin
+ u1 = 2 * rand() - 1
+ u2 = 2 * rand() - 1
+ w = u1*u1 + u2*u2
+ end while (w >= 1)
+ w = Math.sqrt((-2*Math.log(w))/w)
+ [ u2*w, u1*w ]
+end
+
+mean = 25
+sdev = 2
+salary = gaussian_rand[0] * sdev + mean
+printf("You have been hired at \$%.2f\n", salary)
+
+
+# @@PLEAC@@_2.11
+def deg2rad(d)
+ (d/180.0)*Math::PI
+end
+
+def rad2deg(r)
+ (r/Math::PI)*180
+end
+
+
+# @@PLEAC@@_2.12
+sin_val = Math.sin(angle)
+cos_val = Math.cos(angle)
+tan_val = Math.tan(angle)
+
+# AFAIK Ruby's Math module doesn't provide acos/asin
+# While we're at it, let's also define missing hyperbolic functions
+module Math
+ def Math.asin(x)
+ atan2(x, sqrt(1 - x**2))
+ end
+ def Math.acos(x)
+ atan2(sqrt(1 - x**2), x)
+ end
+ def Math.atan(x)
+ atan2(x, 1)
+ end
+ def Math.sinh(x)
+ (exp(x) - exp(-x)) / 2
+ end
+ def Math.cosh(x)
+ (exp(x) + exp(-x)) / 2
+ end
+ def Math.tanh(x)
+ sinh(x) / cosh(x)
+ end
+end
+
+# The support for Complex numbers is not built-in
+y = Math.acos(3.7)
+#=> in `sqrt': square root for negative number (ArgumentError)
+
+# There is an implementation of Complex numbers in 'complex.rb' in current
+# Ruby distro, but it doesn't support atan2 with complex args, so it doesn't
+# solve this problem.
+
+
+# @@PLEAC@@_2.13
+log_e = Math.log(val)
+log_10 = Math.log10(val)
+
+def log_base(base, val)
+ Math.log(val)/Math.log(base)
+end
+
+answer = log_base(10, 10_000)
+puts "log10(10,000) = #{answer}"
+
+
+# @@PLEAC@@_2.14
+require 'matrix.rb'
+
+a = Matrix[[3, 2, 3], [5, 9, 8]]
+b = Matrix[[4, 7], [9, 3], [8, 1]]
+c = a * b
+
+a.row_size
+a.column_size
+
+c.det
+a.transpose
+
+
+# @@PLEAC@@_2.15
+require 'complex.rb'
+require 'rational.rb'
+
+a = Complex(3, 5) # 3 + 5i
+b = Complex(2, -2) # 2 - 2i
+puts "c = #{a*b}"
+
+c = a * b
+d = 3 + 4*Complex::I
+
+printf "sqrt(#{d}) = %s\n", Math.sqrt(d)
+
+
+# @@PLEAC@@_2.16
+number = hexadecimal.hex
+number = octal.oct
+
+print "Gimme a number in decimal, octal, or hex: "
+num = gets.chomp
+exit unless defined?(num)
+num = num.oct if num =~ /^0/ # does both oct and hex
+printf "%d %x %o\n", num, num, num
+
+print "Enter file permission in octal: "
+permissions = gets.chomp
+raise "Exiting ...\n" unless defined?(permissions)
+puts "The decimal value is #{permissions.oct}"
+
+
+# @@PLEAC@@_2.17
+def commify(n)
+ n.to_s =~ /([^\.]*)(\..*)?/
+ int, dec = $1.reverse, $2 ? $2 : ""
+ while int.gsub!(/(,|\.|^)(\d{3})(\d)/, '\1\2,\3')
+ end
+ int.reverse + dec
+end
+
+
+# @@PLEAC@@_2.18
+printf "It took %d hour%s\n", time, time == 1 ? "" : "s"
+
+# dunno if an equivalent to Lingua::EN::Inflect exists...
+
+
+# @@PLEAC@@_2.19
+#-----------------------------
+#!/usr/bin/ruby
+# bigfact - calculating prime factors
+def factorize(orig)
+ factors = {}
+ factors.default = 0 # return 0 instead nil if key not found in hash
+ n = orig
+ i = 2
+ sqi = 4 # square of i
+ while sqi <= n do
+ while n.modulo(i) == 0 do
+ n /= i
+ factors[i] += 1
+ # puts "Found factor #{i}"
+ end
+ # we take advantage of the fact that (i +1)**2 = i**2 + 2*i +1
+ sqi += 2 * i + 1
+ i += 1
+ end
+
+ if (n != 1) && (n != orig)
+ factors[n] += 1
+ end
+ factors
+end
+
+def printfactorhash(orig, factorcount)
+ print format("%-10d ", orig)
+ if factorcount.length == 0
+ print "PRIME"
+ else
+ # sorts after number, because the hash keys are numbers
+ factorcount.sort.each { |factor,exponent|
+ print factor
+ if exponent > 1
+ print "**", exponent
+ end
+ print " "
+ }
+ end
+ puts
+end
+
+for arg in ARGV
+ n = arg.to_i
+ mfactors = factorize(n)
+ printfactorhash(n, mfactors)
+end
+#-----------------------------
+
+
+# @@PLEAC@@_3.0
+puts Time.now
+
+print "Today is day ", Time.now.yday, " of the current year.\n"
+print "Today is day ", Time.now.day, " of the current month.\n"
+
+
+# @@PLEAC@@_3.1
+day, month, year = Time.now.day, Time.now.month, Time.now.year
+# or
+day, month, year = Time.now.to_a[3..5]
+
+tl = Time.now.localtime
+printf("The current date is %04d %02d %02d\n", tl.year, tl.month, tl.day)
+
+Time.now.localtime.strftime("%Y-%m-%d")
+
+
+# @@PLEAC@@_3.2
+Time.local(year, month, day, hour, minute, second).tv_sec
+Time.gm(year, month, day, hour, minute, second).tv_sec
+
+
+# @@PLEAC@@_3.3
+sec, min, hour, day, month, year, wday, yday, isdst, zone = Time.at(epoch_secs).to_a
+
+
+# @@PLEAC@@_3.4
+when_ = now + difference # now -> Time ; difference -> Numeric (delta in seconds)
+then_ = now - difference
+
+
+# @@PLEAC@@_3.5
+bree = 361535725
+nat = 96201950
+
+difference = bree - nat
+puts "There were #{difference} seconds between Nat and Bree"
+
+seconds = difference % 60
+difference = (difference - seconds) / 60
+minutes = difference % 60
+difference = (difference - minutes) / 60
+hours = difference % 24
+difference = (difference - hours) / 24
+days = difference % 7
+weeks = (difference - days) / 7
+
+puts "(#{weeks} weeks, #{days} days, #{hours}:#{minutes}:#{seconds})"
+
+
+# @@PLEAC@@_3.6
+monthday, weekday, yearday = date.mday, date.wday, date.yday
+
+# AFAIK the week number is not just a division since week boundaries are on sundays
+weeknum = d.strftime("%U").to_i + 1
+
+year = 1981
+month = "jun" # or `6' if you want to emulate a broken language
+day = 16
+t = Time.mktime(year, month, day)
+print "#{month}/#{day}/#{year} was a ", t.strftime("%A"), "\n"
+
+
+# @@PLEAC@@_3.7
+yyyy, mm, dd = $1, $2, $3 if "1998-06-25" =~ /(\d+)-(\d+)-(\d+)/
+
+epoch_seconds = Time.mktime(yyyy, mm, dd).tv_sec
+
+# dunno an equivalent to Date::Manip#ParseDate
+
+
+# @@PLEAC@@_3.8
+string = Time.at(epoch_secs)
+Time.at(1234567890).gmtime # gives: Fri Feb 13 23:31:30 UTC 2009
+
+time = Time.mktime(1973, "jan", 18, 3, 45, 50)
+print "In localtime it gives: ", time.localtime, "\n"
+
+
+# @@PLEAC@@_3.9
+# Ruby provides micro-seconds in Time object
+Time.now.usec
+
+# Ruby gives the seconds in floating format when substracting two Time objects
+before = Time.now
+line = gets
+elapsed = Time.now - before
+puts "You took #{elapsed} seconds."
+
+# On my Celeron-400 with Linux-2.2.19-14mdk, average for three execs are:
+# This Ruby version: average 0.00321 sec
+# Cookbook's Perl version: average 0.00981 sec
+size = 500
+number_of_times = 100
+total_time = 0
+number_of_times.times {
+ # populate array
+ array = []
+ size.times { array << rand }
+ # sort it
+ begin_ = Time.now
+ array.sort!
+ time = Time.now - begin_
+ total_time += time
+}
+printf "On average, sorting %d random numbers takes %.5f seconds\n",
+ size, (total_time/Float(number_of_times))
+
+
+# @@PLEAC@@_3.10
+sleep(0.005) # Ruby is definitely not as broken as Perl :)
+# (may be interrupted by sending the process a SIGALRM)
+
+
+# @@PLEAC@@_3.11
+#!/usr/bin/ruby -w
+# hopdelta - feed mail header, produce lines
+# showing delay at each hop.
+require 'time'
+class MailHopDelta
+
+ def initialize(mail)
+ @head = mail.gsub(/\n\s+/,' ')
+ @topline = %w-Sender Recipient Time Delta-
+ @start_from = mail.match(/^From.*\@([^\s>]*)/)[1]
+ @date = Time.parse(mail.match(/^Date:\s+(.*)/)[1])
+ end
+
+ def out(line)
+ "%-20.20s %-20.20s %-20.20s %s" % line
+ end
+
+ def hop_date(day)
+ day.strftime("%I:%M:%S %Y/%m/%d")
+ end
+
+ def puts_hops
+ puts out(@topline)
+ puts out(['Start', @start_from, hop_date(@date),''])
+ @head.split(/\n/).reverse.grep(/^Received:/).each do |hop|
+ hop.gsub!(/\bon (.*?) (id.*)/,'; \1')
+ whence = hop.match(/;\s+(.*)$/)[1]
+ unless whence
+ warn "Bad received line: #{hop}"
+ next
+ end
+ from = $+ if hop =~ /from\s+(\S+)|\((.*?)\)/
+ by = $1 if hop =~ /by\s+(\S+\.\S+)/
+ next unless now = Time.parse(whence).localtime
+ delta = now - @date
+ puts out([from, by, hop_date(now), hop_time(delta)])
+ @date = now
+ end
+ end
+
+ def hop_time(secs)
+ sign = secs < 0 ? -1 : 1
+ days, secs = secs.abs.divmod(60 * 60 * 24)
+ hours,secs = secs.abs.divmod(60 * 60)
+ mins, secs = secs.abs.divmod(60)
+ rtn = "%3ds" % [secs * sign]
+ rtn << "%3dm" % [mins * sign] if mins != 0
+ rtn << "%3dh" % [hours * sign] if hours != 0
+ rtn << "%3dd" % [days * sign] if days != 0
+ rtn
+ end
+end
+
+$/ = ""
+mail = MailHopDelta.new(ARGF.gets).puts_hops
+
+
+# @@PLEAC@@_4.0
+single_level = [ "this", "that", "the", "other" ]
+
+# Ruby directly supports nested arrays
+double_level = [ "this", "that", [ "the", "other" ] ]
+still_single_level = [ "this", "that", [ "the", "other" ] ].flatten
+
+
+# @@PLEAC@@_4.1
+a = [ "quick", "brown", "fox" ]
+a = %w(Why are you teasing me?)
+
+lines = <<"END_OF_HERE_DOC".gsub(/^\s*(.+)/, '\1')
+ The boy stood on the burning deck,
+ It was as hot as glass.
+END_OF_HERE_DOC
+
+bigarray = IO.readlines("mydatafile").collect { |l| l.chomp }
+
+name = "Gandalf"
+banner = %Q(Speak, #{name}, and welcome!)
+
+host_info = `host #{his_host}`
+
+%x(ps #{$$})
+
+banner = 'Costs only $4.95'.split(' ')
+
+rax = %w! ( ) < > { } [ ] !
+
+
+# @@PLEAC@@_4.2
+def commify_series(arr)
+ return '' if not arr
+ case arr.size
+ when 0 then ''
+ when 1 then arr[0]
+ when 2 then arr.join(' and ')
+ else arr[0..-2].join(', ') + ', and ' + arr[-1]
+ end
+end
+
+array = [ "red", "yellow", "green" ]
+
+print "I have ", array, " marbles\n"
+# -> I have redyellowgreen marbles
+
+# But unlike Perl:
+print "I have #{array} marbles\n"
+# -> I have redyellowgreen marbles
+# So, needs:
+print "I have #{array.join(' ')} marbles\n"
+# -> I have red yellow green marbles
+
+#!/usr/bin/ruby
+# communify_series - show proper comma insertion in list output
+
+def commify_series(arr)
+ return '' if not arr
+ sepchar = arr.find { |p| p =~ /,/ } ? '; ' : ', '
+ case arr.size
+ when 0 then ''
+ when 1 then arr[0]
+ when 2 then arr.join(' and ')
+ else arr[0..-2].join(sepchar) + sepchar + 'and ' + arr[-1]
+ end
+end
+
+lists = [
+ [ 'just one thing' ],
+ %w(Mutt Jeff),
+ %w(Peter Paul Mary),
+ [ 'To our parents', 'Mother Theresa', 'God' ],
+ [ 'pastrami', 'ham and cheese', 'peanut butter and jelly', 'tuna' ],
+ [ 'recycle tired, old phrases', 'ponder big, happy thoughts' ],
+ [ 'recycle tired, old phrases',
+ 'ponder big, happy thoughts',
+ 'sleep and dream peacefully' ],
+]
+
+for list in lists do
+ puts "The list is: #{commify_series(list)}."
+end
+
+
+# @@PLEAC@@_4.3
+# (note: AFAIK Ruby doesn't allow gory change of Array length)
+# grow the array by assigning nil to past the end of array
+ary[new_size-1] = nil
+# shrink the array by slicing it down
+ary.slice!(new_size..-1)
+# init the array with given size
+Array.new(number_of_elems)
+# assign to an element past the original end enlarges the array
+ary[index_new_last_elem] = value
+
+def what_about_that_array(a)
+ print "The array now has ", a.size, " elements.\n"
+ # Index of last element is not really interesting in Ruby
+ print "Element #3 is `#{a[3]}'.\n"
+end
+people = %w(Crosby Stills Nash Young)
+what_about_that_array(people)
+
+
+# @@PLEAC@@_4.4
+# OO style
+bad_users.each { |user|
+ complain(user)
+}
+# or, functional style
+for user in bad_users
+ complain(user)
+end
+
+for var in ENV.keys.sort
+ puts "#{var}=#{ENV[var]}"
+end
+
+for user in all_users
+ disk_space = get_usage(user)
+ if (disk_space > MAX_QUOTA)
+ complain(user)
+ end
+end
+
+for l in IO.popen("who").readlines
+ print l if l =~ /^gc/
+end
+
+# we can mimic the obfuscated Perl way
+while fh.gets # $_ is set to the line just read
+ chomp # $_ has a trailing \n removed, if it had one
+ split.each { |w| # $_ is split on whitespace
+ # but $_ is not set to each chunk as in Perl
+ print w.reverse
+ }
+end
+# ...or use a cleaner way
+for l in fh.readlines
+ l.chomp.split.each { |w| print w.reverse }
+end
+
+# same drawback as in problem 1.4, we can't mutate a Numeric...
+array.collect! { |v| v - 1 }
+
+a = [ .5, 3 ]; b = [ 0, 1 ]
+for ary in [ a, b ]
+ ary.collect! { |v| v * 7 }
+end
+puts "#{a.join(' ')} #{b.join(' ')}"
+
+# we can mutate Strings, cool; we need a trick for the scalar
+for ary in [ [ scalar ], array, hash.values ]
+ ary.each { |v| v.strip! } # String#strip rules :)
+end
+
+
+# @@PLEAC@@_4.5
+# not relevant in Ruby since we have always references
+for item in array
+ # do somethingh with item
+end
+
+
+# @@PLEAC@@_4.6
+unique = list.uniq
+
+# generate a list of users logged in, removing duplicates
+users = `who`.collect { |l| l =~ /(\w+)/; $1 }.sort.uniq
+puts("users logged in: #{commify_series(users)}") # see 4.2 for commify_series
+
+
+# @@PLEAC@@_4.7
+a - b
+# [ 1, 1, 2, 2, 3, 3, 3, 4, 5 ] - [ 1, 2, 4 ] -> [3, 5]
+
+
+# @@PLEAC@@_4.8
+union = a | b
+intersection = a & b
+difference = a - b
+
+
+# @@PLEAC@@_4.9
+array1.concat(array2)
+# if you will assign to another object, better use:
+new_ary = array1 + array2
+
+members = [ "Time", "Flies" ]
+initiates = [ "An", "Arrow" ]
+members += initiates
+
+members = [ "Time", "Flies" ]
+initiates = [ "An", "Arrow" ]
+members[2,0] = [ "Like", initiates ].flatten
+
+members[0] = "Fruit"
+members[3,2] = "A", "Banana"
+
+
+# @@PLEAC@@_4.10
+reversed = ary.reverse
+
+ary.reverse_each { |e|
+ # do something with e
+}
+
+descending = ary.sort.reverse
+descending = ary.sort { |a,b| b <=> a }
+
+
+# @@PLEAC@@_4.11
+# remove n elements from front of ary (shift n)
+front = ary.slice!(0, n)
+
+# remove n elements from the end of ary (pop n)
+end_ = ary.slice!(-n .. -1)
+
+# let's extend the Array class, to make that useful
+class Array
+ def shift2()
+ slice!(0 .. 1) # more symetric with pop2...
+ end
+ def pop2()
+ slice!(-2 .. -1)
+ end
+end
+
+friends = %w(Peter Paul Mary Jim Tim)
+this, that = friends.shift2
+
+beverages = %w(Dew Jolt Cola Sprite Fresca)
+pair = beverages.pop2
+
+
+# @@PLEAC@@_4.12
+# use Enumerable#detect (or the synonym Enumerable#find)
+highest_eng = employees.detect { |emp| emp.category == 'engineer' }
+
+
+# @@PLEAC@@_4.13
+# use Enumerable#select (or the synonym Enumerable#find_all)
+bigs = nums.select { |i| i > 1_000_000 }
+pigs = users.keys.select { |k| users[k] > 1e7 }
+
+matching = `who`.select { |u| u =~ /^gnat / }
+
+engineers = employees.select { |e| e.position == 'Engineer' }
+
+secondary_assistance = applicants.select { |a|
+ a.income >= 26_000 && a.income < 30_000
+}
+
+
+# @@PLEAC@@_4.14
+# normally you would have an array of Numeric (Float or
+# Fixnum or Bignum), so you would use:
+sorted = unsorted.sort
+# if you have strings representing Integers or Floats
+# you may specify another sort method:
+sorted = unsorted.sort { |a,b| a.to_f <=> b.to_f }
+
+# let's use the list of my own PID's
+`ps ux`.split("\n")[1..-1].
+ select { |i| i =~ /^#{ENV['USER']}/ }.
+ collect { |i| i.split[1] }.
+ sort { |a,b| a.to_i <=> b.to_i }.each { |i| puts i }
+puts "Select a process ID to kill:"
+pid = gets.chomp
+raise "Exiting ... \n" unless pid && pid =~ /^\d+$/
+Process.kill('TERM', pid.to_i)
+sleep 2
+Process.kill('KILL', pid.to_i)
+
+descending = unsorted.sort { |a,b| b.to_f <=> a.to_f }
+
+
+# @@PLEAC@@_4.15
+ordered = unordered.sort { |a,b| compare(a,b) }
+
+precomputed = unordered.collect { |e| [compute, e] }
+ordered_precomputed = precomputed.sort { |a,b| a[0] <=> b[0] }
+ordered = ordered_precomputed.collect { |e| e[1] }
+
+ordered = unordered.collect { |e| [compute, e] }.
+ sort { |a,b| a[0] <=> b[0] }.
+ collect { |e| e[1] }
+
+for employee in employees.sort { |a,b| a.name <=> b.name }
+ print employee.name, " earns \$ ", employee.salary, "\n"
+end
+
+# Beware! `0' is true in Ruby.
+# For chaining comparisons, you may use Numeric#nonzero?, which
+# returns num if num is not zero, nil otherwise
+sorted = employees.sort { |a,b| (a.name <=> b.name).nonzero? || b.age <=> a.age }
+
+users = []
+# getpwent is not wrapped in Ruby... let's fallback
+IO.readlines('/etc/passwd').each { |u| users << u.split(':') }
+users.sort! { |a,b| a[0] <=> b[0] }
+for user in users
+ puts user[0]
+end
+
+sorted = names.sort { |a,b| a[1, 1] <=> b[1, 1] }
+sorted = strings.sort { |a,b| a.length <=> b.length }
+
+# let's show only the compact version
+ordered = strings.collect { |e| [e.length, e] }.
+ sort { |a,b| a[0] <=> b[0] }.
+ collect { |e| e[1] }
+
+ordered = strings.collect { |e| [/\d+/.match(e)[0].to_i, e] }.
+ sort { |a,b| a[0] <=> b[0] }.
+ collect { |e| e[1] }
+
+print `cat /etc/passwd`.collect { |e| [e, e.split(':').indexes(3,2,0)].flatten }.
+ sort { |a,b| (a[1] <=> b[1]).nonzero? || (a[2] <=> b[2]).nonzero? || a[3] <=> b[3] }.
+ collect { |e| e[0] }
+
+
+# @@PLEAC@@_4.16
+circular.unshift(circular.pop) # the last shall be first
+circular.push(circular.shift) # and vice versa
+
+def grab_and_rotate(l)
+ l.push(ret = l.shift)
+ ret
+end
+
+processes = [1, 2, 3, 4, 5]
+while (1)
+ process = grab_and_rotate(processes)
+ puts "Handling process #{process}"
+ sleep 1
+end
+
+
+# @@PLEAC@@_4.17
+def fisher_yates_shuffle(a)
+ (a.size-1).downto(1) { |i|
+ j = rand(i+1)
+ a[i], a[j] = a[j], a[i] if i != j
+ }
+end
+
+def naive_shuffle(a)
+ for i in 0...a.size
+ j = rand(a.size)
+ a[i], a[j] = a[j], a[i]
+ end
+end
+
+
+# @@PLEAC@@_4.18
+#!/usr/bin/env ruby
+# example 4-2 words
+# words - gather lines, present in colums
+
+# class to encapsulate the word formatting from the input
+class WordFormatter
+ def initialize(cols)
+ @cols = cols
+ end
+
+ # helper to return the length of the longest word in the wordlist
+ def maxlen(wordlist)
+ max = 1
+ for word in wordlist
+ if word.length > max
+ max = word.length
+ end
+ end
+ max
+ end
+
+ # process the wordlist and print it formmated into columns
+ def output(wordlist)
+ collen = maxlen(wordlist) + 1
+ columns = @cols / collen
+ columns = 1 if columns == 0
+ rows = (wordlist.length + columns - 1) / columns
+ # now process each item, picking out proper piece for this position
+ 0.upto(rows * columns - 1) { |item|
+ target = (item % columns) * rows + (item / columns)
+ eol = ((item+1) % columns == 0)
+ piece = wordlist[target] || ""
+ piece = piece.ljust(collen) unless eol
+ print piece
+ puts if eol
+ }
+ # no need to finish it up, because eol is always true for the last element
+ end
+end
+
+# get nr of chars that fit in window or console, see PLEAC 15.4
+# not portable -- linux only (?)
+def getWinCharWidth()
+ buf = "\0" * 8
+ $stdout.ioctl(0x5413, buf)
+ ws_row, ws_col, ws_xpixel, ws_ypixel = buf.unpack("$4")
+ ws_col || 80
+rescue
+ 80
+end
+
+# main program
+cols = getWinCharWidth()
+formatter = WordFormatter.new(cols)
+words = readlines()
+words.collect! { |line|
+ line.chomp
+}
+formatter.output(words)
+
+
+# @@PLEAC@@_4.19
+# In ruby, Fixnum's are automatically converted to Bignum's when
+# needed, so there is no need for an extra module
+def factorial(n)
+ s = 1
+ while n > 0
+ s *= n
+ n -= 1
+ end
+ s
+end
+
+puts factorial(500)
+
+#---------------------------------------------------------
+# Example 4-3. tsc-permute
+# tsc_permute: permute each word of input
+def permute(items, perms)
+ unless items.length > 0
+ puts perms.join(" ")
+ else
+ for i in items
+ newitems = items.dup
+ newperms = perms.dup
+ newperms.unshift(newitems.delete(i))
+ permute(newitems, newperms)
+ end
+ end
+end
+# In ruby the main program must be after all definitions it is using
+permute(ARGV, [])
+
+#---------------------------------------------------------
+# mjd_permute: permute each word of input
+
+def factorial(n)
+ s = 1
+ while n > 0
+ s *= n
+ n -= 1
+ end
+ s
+end
+
+# we use a class with a class variable store the private cache
+# for the results of the factorial function.
+class Factorial
+ @@fact = [ 1 ]
+ def Factorial.compute(n)
+ if @@fact[n]
+ @@fact[n]
+ else
+ @@fact[n] = n * Factorial.compute(n - 1)
+ end
+ end
+end
+
+#---------------------------------------------------------
+# Example 4-4- mjd-permute
+# n2pat(n, len): produce the N-th pattern of length len
+
+# We must use a lower case letter as parameter N, otherwise it is
+# handled as constant Length is the length of the resulting
+# array, not the index of the last element (length -1) like in
+# the perl example.
+def n2pat(n, length)
+ pat = []
+ i = 1
+ while i <= length
+ pat.push(n % i)
+ n /= i
+ i += 1
+ end
+ pat
+end
+
+# pat2perm(pat): turn pattern returned by n2pat() into
+# permutation of integers.
+def pat2perm(pat)
+ source = (0 .. pat.length - 1).to_a
+ perm = []
+ perm.push(source.slice!(pat.pop)) while pat.length > 0
+ perm
+end
+
+def n2perm(n, len)
+ pat2perm(n2pat(n,len))
+end
+
+# In ruby the main program must be after all definitions
+while gets
+ data = split
+ # the perl solution has used $#data, which is length-1
+ num_permutations = Factorial.compute(data.length())
+ 0.upto(num_permutations - 1) do |i|
+ # in ruby we can not use an array as selector for an array
+ # but by exchanging the two arrays, we can use the collect method
+ # which returns an array with the result of all block invocations
+ permutation = n2perm(i, data.length).collect {
+ |j| data[j]
+ }
+ puts permutation.join(" ")
+ end
+end
+
+
+# @@PLEAC@@_5.0
+age = { "Nat", 24,
+ "Jules", 25,
+ "Josh", 17 }
+
+age["Nat"] = 24
+age["Jules"] = 25
+age["Josh"] = 17
+
+food_color = {
+ "Apple" => "red",
+ "Banana" => "yellow",
+ "Lemon" => "yellow",
+ "Carrot" => "orange"
+ }
+
+# In Ruby, you cannot avoid the double or simple quoting
+# while manipulatin hashes
+
+
+# @@PLEAC@@_5.1
+hash[key] = value
+
+food_color["Raspberry"] = "pink"
+puts "Known foods:", food_color.keys
+
+
+# @@PLEAC@@_5.2
+# does hash have a value for key ?
+if (hash.has_key?(key))
+ # it exists
+else
+ # it doesn't
+end
+
+[ "Banana", "Martini" ].each { |name|
+ print name, " is a ", food_color.has_key?(name) ? "food" : "drink", "\n"
+}
+
+age = {}
+age['Toddler'] = 3
+age['Unborn'] = 0
+age['Phantasm'] = nil
+
+for thing in ['Toddler', 'Unborn', 'Phantasm', 'Relic']
+ print "#{thing}: "
+ print "Has-key " if age.has_key?(thing)
+ print "True " if age[thing]
+ print "Nonzero " if age[thing] && age[thing].nonzero?
+ print "\n"
+end
+
+#=>
+# Toddler: Has-key True Nonzero
+# Unborn: Has-key True
+# Phantasm: Has-key
+# Relic:
+
+# You use Hash#has_key? when you use Perl's exists -> it checks
+# for existence of a key in a hash.
+# All Numeric are "True" in ruby, so the test doesn't have the
+# same semantics as in Perl; you would use Numeric#nonzero? to
+# achieve the same semantics (false if 0, true otherwise).
+
+
+# @@PLEAC@@_5.3
+food_color.delete("Banana")
+
+
+# @@PLEAC@@_5.4
+hash.each { |key, value|
+ # do something with key and value
+}
+
+hash.each_key { |key|
+ # do something with key
+}
+
+food_color.each { |food, color|
+ puts "#{food} is #{color}"
+}
+
+food_color.each_key { |food|
+ puts "#{food} is #{food_color[food]}"
+}
+
+# IMO this demonstrates that OO style is by far more readable
+food_color.keys.sort.each { |food|
+ puts "#{food} is #{food_color[food]}."
+}
+
+#-----------------------------
+#!/usr/bin/ruby
+# countfrom - count number of messages from each sender
+
+# Default value is 0
+from = Hash.new(0)
+while gets
+ /^From: (.*)/ and from[$1] += 1
+end
+
+# More useful to sort by number of received mail by person
+from.sort {|a,b| b[1]<=>a[1]}.each { |v|
+ puts "#{v[1]}: #{v[0]}"
+}
+#-----------------------------
+
+
+# @@PLEAC@@_5.5
+# You may use the built-in 'inspect' method this way:
+p hash
+
+# Or do it the Cookbook way:
+hash.each { |k,v| puts "#{k} => #{v}" }
+
+# Sorted by keys
+hash.sort.each { |e| puts "#{e[0]} => #{e[1]}" }
+# Sorted by values
+hash.sort{|a,b| a[1]<=>b[1]}.each { |e| puts "#{e[0]} => #{e[1]}" }
+
+
+# @@PLEAC@@_5.7
+ttys = Hash.new
+for i in `who`
+ user, tty = i.split
+ (ttys[user] ||= []) << tty # see problems_ruby for more infos
+end
+ttys.keys.sort.each { |k|
+ puts "#{k}: #{commify_series(ttys[k])}" # from 4.2
+}
+
+
+# @@PLEAC@@_5.8
+surname = { "Mickey" => "Mantle", "Babe" => "Ruth" }
+puts surname.index("Mantle")
+
+# If you really needed to 'invert' the whole hash, use Hash#invert
+
+#-----------------------------
+#!/usr/bin/ruby -w
+# foodfind - find match for food or color
+
+given = ARGV.shift or raise "usage: foodfind food_or_color"
+
+color = {
+ "Apple" => "red",
+ "Banana" => "yellow",
+ "Lemon" => "yellow",
+ "Carrot" => "orange",
+}
+
+if (color.has_key?(given))
+ puts "#{given} is a food with color #{color[given]}."
+end
+if (color.has_value?(given))
+ puts "#{color.index(given)} is a food with color #{given}."
+end
+#-----------------------------
+
+
+# @@PLEAC@@_5.9
+# Sorted by keys (Hash#sort gives an Array of pairs made of each key,value)
+food_color.sort.each { |f|
+ puts "#{f[0]} is #{f[1]}."
+}
+
+# Sorted by values
+food_color.sort { |a,b| a[1] <=> b[1] }.each { |f|
+ puts "#{f[0]} is #{f[1]}."
+}
+
+# Sorted by length of values
+food_color.sort { |a,b| a[1].length <=> b[1].length }.each { |f|
+ puts "#{f[0]} is #{f[1]}."
+}
+
+
+# @@PLEAC@@_5.10
+merged = a.clone.update(b) # because Hash#update changes object in place
+
+drink_color = { "Galliano" => "yellow", "Mai Tai" => "blue" }
+ingested_color = drink_color.clone.update(food_color)
+
+substance_color = {}
+for i in [ food_color, drink_color ]
+ i.each_key { |k|
+ if substance_color.has_key?(k)
+ puts "Warning: #{k} seen twice. Using the first definition."
+ next
+ end
+ substance_color[k] = 1
+ }
+end
+
+
+# @@PLEAC@@_5.11
+common = hash1.keys & hash2.keys
+
+this_not_that = hash1.keys - hash2.keys
+
+
+# @@PLEAC@@_5.12
+# no problem here, Ruby handles any kind of object for key-ing
+# (it takes Object#hash, which defaults to Object#id)
+
+
+# @@PLEAC@@_5.13
+# AFAIK, not possible in Ruby
+
+
+# @@PLEAC@@_5.14
+# Be careful, the following is possible only because Fixnum objects are
+# special (documentation says: there is effectively only one Fixnum object
+# instance for any given integer value).
+count = Hash.new(0)
+array.each { |e|
+ count[e] += 1
+}
+
+
+# @@PLEAC@@_5.15
+father = {
+ "Cain" , "Adam",
+ "Abel" , "Adam",
+ "Seth" , "Adam",
+ "Enoch" , "Cain",
+ "Irad" , "Enoch",
+ "Mehujael" , "Irad",
+ "Methusael" , "Mehujael",
+ "Lamech" , "Methusael",
+ "Jabal" , "Lamech",
+ "Jubal" , "Lamech",
+ "Tubalcain" , "Lamech",
+ "Enos" , "Seth",
+}
+
+while gets
+ chomp
+ begin
+ print $_, " "
+ end while $_ = father[$_]
+ puts
+end
+
+children = {}
+father.each { |k,v|
+ (children[v] ||= []) << k
+}
+while gets
+ chomp
+ puts "#{$_} begat #{(children[$_] || ['Nobody']).join(', ')}.\n"
+end
+
+includes = {}
+files.each { |f|
+ begin
+ for l in IO.readlines(f)
+ next unless l =~ /^\s*#\s*include\s*<([^>]+)>/
+ (includes[$1] ||= []) << f
+ end
+ rescue SystemCallError
+ $stderr.puts "#$! (skipping)"
+ end
+}
+
+include_free = includes.values.flatten.uniq - includes.keys
+
+
+# @@PLEAC@@_5.16
+# dutree - print sorted intented rendition of du output
+#% dutree
+#% dutree /usr
+#% dutree -a
+#% dutree -a /bin
+
+# The DuNode class collects all information about a directory,
+# and provides some convenience methods
+class DuNode
+
+ attr_reader :name
+ attr_accessor :size
+ attr_accessor :kids
+
+ def initialize(name)
+ @name = name
+ @kids = []
+ @size = 0
+ end
+
+ # support for sorting nodes with side
+ def size_compare(node2)
+ @size <=> node2.size
+ end
+
+ def basename
+ @name.sub(/.*\//, "")
+ end
+
+ #returns substring before last "/", nil if not there
+ def parent
+ p = @name.sub(/\/[^\/]+$/,"")
+ if p == @name
+ nil
+ else
+ p
+ end
+ end
+
+end
+
+# The DuTree does the acdtual work of
+# getting the input, parsing it, builging up a tree
+# and format it for output
+class Dutree
+
+ attr_reader :topdir
+
+ def initialize
+ @nodes = Hash.new
+ @dirsizes = Hash.new(0)
+ @kids = Hash.new([])
+ end
+
+ # get a node by name, create it if it does not exist yet
+ def get_create_node(name)
+ if @nodes.has_key?(name)
+ @nodes[name]
+ else
+ node = DuNode.new(name)
+ @nodes[name] = node
+ node
+ end
+ end
+
+ # run du, read in input, save sizes and kids
+ # stores last directory read in instance variable topdir
+ def input(arguments)
+ name = ""
+ cmd = "du " + arguments.join(" ")
+ IO.popen(cmd) { |pipe|
+ pipe.each { |line|
+ size, name = line.chomp.split(/\s+/, 2)
+ node = get_create_node(name)
+ node.size = size.to_i
+ @nodes[name] = node
+ parent = node.parent
+ if parent
+ get_create_node(parent).kids.push(node)
+ end
+ }
+ }
+ @topdir = @nodes[name]
+ end
+
+ # figure out how much is taken in each directory
+ # that isn't stored in the subdirectories. Add a new
+ # fake kid called "." containing that much.
+ def get_dots(node)
+ cursize = node.size
+ for kid in node.kids
+ cursize -= kid.size
+ get_dots(kid)
+ end
+ if node.size != cursize
+ newnode = get_create_node(node.name + "/.")
+ newnode.size = cursize
+ node.kids.push(newnode)
+ end
+ end
+
+ # recursively output everything
+ # passing padding and number width as well
+ # on recursive calls
+ def output(node, prefix="", width=0)
+ line = sprintf("%#{width}d %s", node.size, node.basename)
+ puts(prefix + line)
+ prefix += line.sub(/\d /, "| ")
+ prefix.gsub!(/[^|]/, " ")
+ if node.kids.length > 0 # not a bachelor node
+ kids = node.kids
+ kids.sort! { |a,b|
+ b.size_compare(a)
+ }
+ width = kids[0].size.to_s.length
+ for kid in kids
+ output(kid, prefix, width)
+ end
+ end
+ end
+
+end
+
+tree = Dutree.new
+tree.input(ARGV)
+tree.get_dots(tree.topdir)
+tree.output(tree.topdir)
+
+
+# @@PLEAC@@_6.0
+# The verbose version are match, sub, gsub, sub! and gsub!;
+# pattern needs to be a Regexp object; it yields a MatchData
+# object.
+pattern.match(string)
+string.sub(pattern, replacement)
+string.gsub(pattern, replacement)
+# As usual in Ruby, sub! does the same as sub but also modifies
+# the object, the same for gsub!/gsub.
+
+# Sugared syntax yields the position of the match (or nil if no
+# match). Note that the object at the right of the operator needs
+# not to be a Regexp object (it can be a String). The "dont
+# match" operator yields true or false.
+meadow =~ /sheep/ # position of the match, nil if no match
+meadow !~ /sheep/ # true if doesn't match, false if it does
+# There is no sugared version for the substitution
+
+meadow =~ /\bovines?\b/i and print "Here be sheep!"
+
+string = "good food"
+string.sub!(/o*/, 'e')
+
+# % echo ababacaca | ruby -ne 'puts $& if /(a|ba|b)+(a|ac)+/'
+# ababa
+
+# The "global" (or "multiple") match is handled by String#scan
+scan (/(\d+)/) {
+ puts "Found number #{$1}"
+}
+
+# String#scan yields an Array if not used with a block
+numbers = scan(/\d+/)
+
+digits = "123456789"
+nonlap = digits.scan(/(\d\d\d)/)
+yeslap = digits.scan(/(?=(\d\d\d))/)
+puts "Non-overlapping: #{nonlap.join(' ')}"
+puts "Overlapping: #{yeslap.join(' ')}";
+# Non-overlapping: 123 456 789
+# Overlapping: 123 234 345 456 567 678 789
+
+string = "And little lambs eat ivy"
+string =~ /l[^s]*s/
+puts "(#$`) (#$&) (#$')"
+# (And ) (little lambs) ( eat ivy)
+
+
+# @@PLEAC@@_6.1
+# Ruby doesn't have the same problem:
+dst = src.sub('this', 'that')
+
+progname = $0.sub('^.*/', '')
+
+bindirs = %w(/usr/bin /bin /usr/local/bin)
+libdirs = bindirs.map { |l| l.sub('bin', 'lib') }
+
+
+# @@PLEAC@@_6.3
+/\S+/ # as many non-whitespace bytes as possible
+/[A-Za-z'-]+/ # as many letters, apostrophes, and hyphens
+
+/\b([A-Za-z]+)\b/ # usually best
+/\s([A-Za-z]+)\s/ # fails at ends or w/ punctuation
+
+
+# @@PLEAC@@_6.4
+require 'socket'
+str = 'www.ruby-lang.org and www.rubygarden.org'
+re = /
+ ( # capture the hostname in $1
+ (?: # these parens for grouping only
+ (?! [-_] ) # lookahead for neither underscore nor dash
+ [\w-] + # hostname component
+ \. # and the domain dot
+ ) + # now repeat that whole thing a bunch of times
+ [A-Za-z] # next must be a letter
+ [\w-] + # now trailing domain part
+ ) # end of $1 capture
+ /x # /x for nice formatting
+
+str.gsub! re do # pass a block to execute replacement
+ host = TCPsocket.gethostbyname($1)
+ "#{$1} [#{host[3]}]"
+end
+
+puts str
+#-----------------------------
+# to match whitespace or #-characters in an extended re you need to escape
+# them.
+
+foo = 42
+str = 'blah #foo# blah'
+str.gsub! %r/ # replace
+ \# # a pound sign
+ (\w+) # the variable name
+ \# # another pound sign
+ /x do
+ eval $1 # with the value of a local variable
+ end
+puts str # => blah 42 blah
+
+
+# @@PLEAC@@_6.5
+# The 'g' modifier doesn't exist in Ruby, a regexp can't be used
+# directly in a while loop; instead, use String#scan { |match| .. }
+fish = 'One fish two fish red fish blue fish'
+WANT = 3
+count = 0
+fish.scan(/(\w+)\s+fish\b/i) {
+ if (count += 1) == WANT
+ puts "The third fish is a #{$1} one."
+ end
+}
+
+if fish =~ /(?:\w+\s+fish\s+){2}(\w+)\s+fish/i
+ puts "The third fish is a #{$1} one."
+end
+
+pond = 'One fish two fish red fish blue fish'
+# String#scan without a block gives an array of matches, each match
+# being an array of all the specified groups
+colors = pond.scan(/(\w+)\s+fish\b/i).flatten # get all matches
+color = colors[2] # then the one we want
+# or without a temporary array
+color = pond.scan(/(\w+)\s+fish\b/i).flatten[2] # just grab element 3
+puts "The third fish in the pond is #{color}."
+
+count = 0
+fishes = 'One fish two fish red fish blue fish'
+evens = fishes.scan(/(\w+)\s+fish\b/i).select { (count+=1) % 2 == 0 }
+print "Even numbered fish are #{evens.join(' ')}."
+
+count = 0
+fishes.gsub(/
+ \b # makes next \w more efficient
+ ( \w+ ) # this is what we\'ll be changing
+ (
+ \s+ fish \b
+ )
+ /x) {
+ if (count += 1) == 4
+ 'sushi' + $2
+ else
+ $1 + $2
+ end
+}
+
+pond = 'One fish two fish red fish blue fish swim here.'
+puts "Last fish is #{pond.scan(/\b(\w+)\s+fish\b/i).flatten[-1]}"
+
+/
+ A # find some pattern A
+ (?! # mustn\'t be able to find
+ .* # something
+ A # and A
+ )
+ $ # through the end of the string
+/x
+
+# The "s" perl modifier is "m" in Ruby (not very nice since there is
+# also an "m" in perl..)
+pond = "One fish two fish red fish blue fish swim here."
+if (pond =~ /
+ \b ( \w+) \s+ fish \b
+ (?! .* \b fish \b )
+ /mix)
+ puts "Last fish is #{$1}."
+else
+ puts "Failed!"
+end
+
+
+# @@PLEAC@@_6.6
+#-----------------------------
+#!/usr/bin/ruby -w
+# killtags - very bad html killer
+$/ = nil; # each read is whole file
+while file = gets() do
+ file.gsub!(/<.*?>/m,''); # strip tags (terribly)
+ puts file # print file to STDOUT
+end
+#-----------------------------
+#!/usr/bin/ruby -w
+#headerfy - change certain chapter headers to html
+$/ = ''
+while file = gets() do
+ pattern = /
+ \A # start of record
+ ( # capture in $1
+ Chapter # text string
+ \s+ # mandatory whitespace
+ \d+ # decimal number
+ \s* # optional whitespace
+ : # a real colon
+ . * # anything not a newline till end of line
+ )
+ /x
+ puts file.gsub(pattern,'<H1>\1</H1>')
+end
+#-----------------------------
+#% ruby -00pe "gsub!(/\A(Chapter\s+\d+\s*:.*)/,'<H1>\1</H1>')" datafile
+
+#!/usr/bin/ruby -w
+#-----------------------------
+for file in ARGV
+ file = File.open(ARGV.shift)
+ while file.gets('') do # each read is a paragraph
+ print "chunk #{$.} in $ARGV has <<#{$1}>>\n" while /^START(.*?)^END/m
+ end # /m activates the multiline mode
+end
+#-----------------------------
+
+# @@PLEAC@@_6.7
+#-----------------------------
+$/ = nil;
+file = File.open("datafile")
+chunks = file.gets.split(/pattern/)
+#-----------------------------
+# .Ch, .Se and .Ss divide chunks of STDIN
+chunks = gets(nil).split(/^\.(Ch|Se|Ss)$/)
+print "I read #{chunks.size} chunks.\n"
+#-----------------------------
+
+
+# @@PLEAC@@_6.8
+while gets
+ if ~/BEGIN/ .. ~/END/
+ # line falls between BEGIN and END inclusive
+ end
+end
+
+while gets
+ if ($. == firstnum) .. ($. == lastnum)
+ # operate between firstnum and lastnum line number
+ end
+end
+
+# in ruby versions prior to 1.8, the above two conditional
+# expressions could be shortened to:
+# if /BEGIN/ .. /END/
+# and
+# if firstnum .. lastnum
+# but these now only work this way from the command line
+
+#-----------------------------
+
+while gets
+ if ~/BEGIN/ ... ~/END/
+ # line falls between BEGIN and END on different lines
+ end
+end
+
+while gets
+ if ($. == first) ... ($. == last)
+ # operate between first and last line number on different lines
+ end
+end
+
+#-----------------------------
+# command-line to print lines 15 through 17 inclusive (see below)
+ruby -ne 'print if 15 .. 17' datafile
+
+# print out all <XMP> .. </XMP> displays from HTML doc
+while gets
+ print if ~%r#<XMP>#i .. ~%r#</XMP>#i;
+end
+
+# same, but as shell command
+# ruby -ne 'print if %r#<XMP>#i .. %r#</XMP>#i' document.html
+#-----------------------------
+# ruby -ne 'BEGIN { $top=3; $bottom=5 }; \
+# print if $top .. $bottom' /etc/passwd # FAILS
+# ruby -ne 'BEGIN { $top=3; $bottom=5 }; \
+# print if $. == $top .. $. == $bottom' /etc/passwd # works
+# ruby -ne 'print if 3 .. 5' /etc/passwd # also works
+#-----------------------------
+print if ~/begin/ .. ~/end/;
+print if ~/begin/ ... ~/end/;
+#-----------------------------
+while gets
+ $in_header = $. == 1 .. ~/^$/ ? true : false
+ $in_body = ~/^$/ .. ARGF.eof ? true : false
+end
+#-----------------------------
+seen = {}
+ARGF.each do |line|
+ next unless line =~ /^From:?\s/i .. line =~ /^$/;
+ line.scan(%r/([^<>(),;\s]+\@[^<>(),;\s]+)/).each do |addr|
+ puts addr unless seen[addr]
+ seen[addr] ||= 1
+ end
+end
+
+
+# @@PLEAC@@_6.9
+def glob2pat(globstr)
+ patmap = {
+ '*' => '.*',
+ '?' => '.',
+ '[' => '[',
+ ']' => ']',
+ }
+ globstr.gsub!(/(.)/) { |c| patmap[c] || Regexp::escape(c) }
+ '^' + globstr + '$'
+end
+
+
+# @@PLEAC@@_6.10
+# avoid interpolating patterns like this if the pattern
+# isn't going to change:
+pattern = ARGV.shift
+ARGF.each do |line|
+ print line if line =~ /#{pattern}/
+end
+
+# the above creates a new regex each iteration. Instead,
+# use the /o modifier so the regex is compiled only once
+
+pattern = ARGV.shift
+ARGF.each do |line|
+ print line if line =~ /#{pattern}/o
+end
+
+#-----------------------------
+
+#!/usr/bin/ruby
+# popgrep1 - grep for abbreviations of places that say "pop"
+# version 1: slow but obvious way
+popstates = %w(CO ON MI WI MN)
+ARGF.each do |line|
+ popstates.each do |state|
+ if line =~ /\b#{state}\b/
+ print line
+ last
+ end
+ end
+end
+
+#-----------------------------
+#!/usr/bin/ruby
+# popgrep2 - grep for abbreviations of places that say "pop"
+# version 2: eval strings; fast but hard to quote
+popstates = %w(CO ON MI WI MN)
+code = "ARGF.each do |line|\n"
+popstates.each do |state|
+ code += "\tif line =~ /\\b#{state}\\b/; print(line); next; end\n"
+end
+code += "end\n"
+print "CODE IS\n---\n#{code}\n---\n" if false # turn on for debugging
+eval code
+
+# CODE IS
+# ---
+# ARGF.each do |line|
+# if line =~ /\bCO\b/; print(line); next; end
+# if line =~ /\bON\b/; print(line); next; end
+# if line =~ /\bMI\b/; print(line); next; end
+# if line =~ /\bWI\b/; print(line); next; end
+# if line =~ /\bMN\b/; print(line); next; end
+# end
+#
+# ---
+
+## alternatively, the same idea as above but compiling
+## to a case statement: (not in perlcookbook)
+#!/usr/bin/ruby -w
+# popgrep2.5 - grep for abbreviations of places that say "pop"
+# version 2.5: eval strings; fast but hard to quote
+popstates = %w(CO ON MI WI MN)
+code = "ARGF.each do |line|\n case line\n"
+popstates.each do |state|
+ code += " when /\\b#{state}\\b/ : print line\n"
+end
+code += " end\nend\n"
+print "CODE IS\n---\n#{code}\n---\n" if false # turn on for debugging
+eval code
+
+# CODE IS
+# ---
+# ARGF.each do |line|
+# case line
+# when /\bCO\b/ : print line
+# when /\bON\b/ : print line
+# when /\bMI\b/ : print line
+# when /\bWI\b/ : print line
+# when /\bMN\b/ : print line
+# end
+# end
+#
+# ---
+
+# Note: (above) Ruby 1.8+ allows the 'when EXP : EXPR' on one line
+# with the colon separator.
+
+#-----------------------------
+#!/usr/bin/ruby
+# popgrep3 - grep for abbreviations of places that say "pop"
+# version3: build a match_any function
+popstates = %w(CO ON MI WI MN)
+expr = popstates.map{|e|"line =~ /\\b#{e}\\b/"}.join('||')
+eval "def match_any(line); #{expr};end"
+ARGF.each do |line|
+ print line if match_any(line)
+end
+#-----------------------------
+
+## building a match_all function is a trivial
+## substitution of && for ||
+## here is a generalized example:
+#!/usr/bin/ruby -w
+## grepauth - print lines that mention both foo and bar
+class MultiMatch
+ def initialize(*patterns)
+ _any = build_match('||',patterns)
+ _all = build_match('&&',patterns)
+ eval "def match_any(line);#{_any};end\n"
+ eval "def match_all(line);#{_all};end\n"
+ end
+ def build_match(sym,args)
+ args.map{|e|"line =~ /#{e}/"}.join(sym)
+ end
+end
+
+mm = MultiMatch.new('foo','bar')
+ARGF.each do |line|
+ print line if mm.match_all(line)
+end
+#-----------------------------
+
+#!/usr/bin/ruby
+# popgrep4 - grep for abbreviations of places that say "pop"
+# version4: pretty fast, but simple: compile all re's first:
+popstates = %w(CO ON MI WI MN)
+popstates = popstates.map{|re| %r/\b#{re}\b/}
+ARGF.each do |line|
+ popstates.each do |state_re|
+ if line =~ state_re
+ print line
+ break
+ end
+ end
+end
+
+## speeds trials on the jargon file(412): 26006 lines, 1.3MB
+## popgrep1 => 7.040s
+## popgrep2 => 0.656s
+## popgrep2.5 => 0.633s
+## popgrep3 => 0.675s
+## popgrep4 => 1.027s
+
+# unless speed is criticial, the technique in popgrep4 is a
+# reasonable balance between speed and logical simplicity.
+
+
+# @@PLEAC@@_6.11
+begin
+ print "Pattern? "
+ pat = $stdin.gets.chomp
+ Regexp.new(pat)
+rescue
+ warn "Invalid Pattern"
+ retry
+end
+
+
+# @@PLEAC@@_6.13
+# uses the 'amatch' extension found on:
+# http://raa.ruby-lang.org/project/amatch/
+require 'amatch'
+matcher = Amatch.new('balast')
+#$relative, $distance = 0, 1
+File.open('/usr/share/dict/words').each_line do |line|
+ print line if matcher.search(line) <= 1
+end
+__END__
+#CODE
+ballast
+ballasts
+balustrade
+balustrades
+blast
+blasted
+blaster
+blasters
+blasting
+blasts
+
+
+# @@PLEAC@@_6.14
+str.scan(/\G(\d)/).each do |token|
+ puts "found #{token}"
+end
+#-----------------------------
+n = " 49 here"
+n.gsub!(/\G /,'0')
+puts n
+#-----------------------------
+str = "3,4,5,9,120"
+str.scan(/\G,?(\d+)/).each do |num|
+ puts "Found number: #{num}"
+end
+#-----------------------------
+# Ruby doesn't have the String.pos or a /c re modifier like Perl
+# But it does have StringScanner in the standard library (strscn)
+# which allows similar functionality:
+
+require 'strscan'
+text = 'the year 1752 lost 10 days on the 3rd of September'
+sc = StringScanner.new(text)
+while sc.scan(/.*?(\d+)/)
+ print "found: #{sc[1]}\n"
+end
+if sc.scan(/\S+/)
+ puts "Found #{sc[0]} after last number"
+end
+#-----------------------------
+# assuming continuing from above:
+puts "The position in 'text' is: #{sc.pos}"
+sc.pos = 30
+puts "The position in 'text' is: #{sc.pos}"
+
+
+# @@PLEAC@@_6.15
+#-----------------------------
+# greedy pattern
+str.gsub!(/<.*>/m,'') # not good
+
+# non-greedy (minimal) pattern
+str.gsub!(/<.*?>/m,'') # not great
+
+
+#-----------------------------
+#<b><i>this</i> and <i>that</i> are important</b> Oh, <b><i>me too!</i></b>
+#-----------------------------
+%r{ <b><i>(.*?)</i></b> }mx
+#-----------------------------
+%r/BEGIN((?:(?!BEGIN).)*)END/
+#-----------------------------
+%r{ <b><i>( (?: (?!</b>|</i>). )* ) </i></b> }mx
+#-----------------------------
+%r{ <b><i>( (?: (?!</[ib]>). )* ) </i></b> }mx
+#-----------------------------
+%r{
+ <b><i>
+ [^<]* # stuff not possibly bad, and not possibly the end.
+ (?:
+ # at this point, we can have '<' if not part of something bad
+ (?! </?[ib]> ) # what we can't have
+ < # okay, so match the '<'
+ [^<]* # and continue with more safe stuff
+ ) *
+ </i></b>
+ }mx
+
+
+# @@PLEAC@@_6.16
+#-----------------------------
+$/ = ""
+ARGF.each do |para|
+ para.scan %r/
+ \b # start at word boundary
+ (\S+) # find chunk of non-whitespace
+ \b # until a word boundary
+ (
+ \s+ # followed by whitespace
+ \1 # and that same chunk again
+ \b # and a word boundary
+ ) + # one or more times
+ /xi do
+ puts "dup word '#{$1}' at paragraph #{$.}"
+ end
+end
+#-----------------------------
+astr = 'nobody'
+bstr = 'bodysnatcher'
+if "#{astr} #{bstr}" =~ /^(\w+)(\w+) \2(\w+)$/
+ print "#{$2} overlaps in #{$1}-#{$2}-#{$3}"
+end
+#-----------------------------
+#!/usr/bin/ruby -w
+# prime_pattern -- find prime factors of argument using patterns
+ARGV << 180
+cap = 'o' * ARGV.shift
+while cap =~ /^(oo+?)\1+$/
+ print $1.size, " "
+ cap.gsub!(/#{$1}/,'o')
+end
+puts cap.size
+#-----------------------------
+#diophantine
+# solve for 12x + 15y + 16z = 281, maximizing x
+if ('o' * 281).match(/^(o*)\1{11}(o*)\2{14}(o*)\3{15}$/)
+ x, y, z = $1.size, $2.size, $3.size
+ puts "One solution is: x=#{x}; y=#{y}; z=#{z}"
+else
+ puts "No solution."
+end
+# => One solution is: x=17; y=3; z=2
+
+#-----------------------------
+# using different quantifiers:
+('o' * 281).match(/^(o+)\1{11}(o+)\2{14}(o+)\3{15}$/)
+# => One solution is: x=17; y=3; z=2
+
+('o' * 281).match(/^(o*?)\1{11}(o*)\2{14}(o*)\3{15}$/)
+# => One solution is: x=0; y=7; z=11
+
+('o' * 281).match(/^(o+?)\1{11}(o*)\2{14}(o*)\3{15}$/)
+# => One solution is: x=1; y=3; z=14
+
+
+# @@PLEAC@@_6.17
+# alpha OR beta
+%r/alpha|beta/
+
+# alpha AND beta
+%r/(?=.*alpha)(?=.*beta)/m
+
+# alpha AND beta, no overlap
+%r/alpha.*beta|beta.*alpha/m
+
+# NOT beta
+%r/^(?:(?!beta).)*$/m
+
+# NOT bad BUT good
+%r/(?=(?:(?!BAD).)*$)GOOD/m
+#-----------------------------
+
+if !(string =~ /pattern/) # ugly
+ something()
+end
+
+if string !~ /pattern/ # preferred
+ something()
+end
+
+
+#-----------------------------
+if string =~ /pat1/ && string =~ /pat2/
+ something()
+end
+#-----------------------------
+if string =~ /pat1/ || string =~ /pat2/
+ something()
+end
+#-----------------------------
+#!/usr/bin/ruby -w
+# minigrep - trivial grep
+pat = ARGV.shift
+ARGF.each do |line|
+ print line if line =~ /#{pat}/o
+end
+#-----------------------------
+ "labelled" =~ /^(?=.*bell)(?=.*lab)/m
+#-----------------------------
+$string =~ /bell/ && $string =~ /lab/
+#-----------------------------
+$murray_hill = "blah bell blah "
+if $murray_hill =~ %r{
+ ^ # start of string
+ (?= # zero-width lookahead
+ .* # any amount of intervening stuff
+ bell # the desired bell string
+ ) # rewind, since we were only looking
+ (?= # and do the same thing
+ .* # any amount of intervening stuff
+ lab # and the lab part
+ )
+ }mx # /m means . can match newline
+
+ print "Looks like Bell Labs might be in Murray Hill!\n";
+end
+#-----------------------------
+"labelled" =~ /(?:^.*bell.*lab)|(?:^.*lab.*bell)/
+#-----------------------------
+$brand = "labelled";
+if $brand =~ %r{
+ (?: # non-capturing grouper
+ ^ .*? # any amount of stuff at the front
+ bell # look for a bell
+ .*? # followed by any amount of anything
+ lab # look for a lab
+ ) # end grouper
+ | # otherwise, try the other direction
+ (?: # non-capturing grouper
+ ^ .*? # any amount of stuff at the front
+ lab # look for a lab
+ .*? # followed by any amount of anything
+ bell # followed by a bell
+ ) # end grouper
+ }mx # /m means . can match newline
+ print "Our brand has bell and lab separate.\n";
+end
+#-----------------------------
+$map =~ /^(?:(?!waldo).)*$/s
+#-----------------------------
+$map = "the great baldo"
+if $map =~ %r{
+ ^ # start of string
+ (?: # non-capturing grouper
+ (?! # look ahead negation
+ waldo # is he ahead of us now?
+ ) # is so, the negation failed
+ . # any character (cuzza /s)
+ ) * # repeat that grouping 0 or more
+ $ # through the end of the string
+ }mx # /m means . can match newline
+ print "There's no waldo here!\n";
+end
+=begin
+ 7:15am up 206 days, 13:30, 4 users, load average: 1.04, 1.07, 1.04
+
+USER TTY FROM LOGIN@ IDLE JCPU PCPU WHAT
+
+tchrist tty1 5:16pm 36days 24:43 0.03s xinit
+
+tchrist tty2 5:19pm 6days 0.43s 0.43s -tcsh
+
+tchrist ttyp0 chthon 7:58am 3days 23.44s 0.44s -tcsh
+
+gnat ttyS4 coprolith 2:01pm 13:36m 0.30s 0.30s -tcsh
+=end
+#% w | minigrep '^(?!.*ttyp).*tchrist'
+#-----------------------------
+%r{
+ ^ # anchored to the start
+ (?! # zero-width look-ahead assertion
+ .* # any amount of anything (faster than .*?)
+ ttyp # the string you don't want to find
+ ) # end look-ahead negation; rewind to start
+ .* # any amount of anything (faster than .*?)
+ tchrist # now try to find Tom
+}x
+#-----------------------------
+#% w | grep tchrist | grep -v ttyp
+#-----------------------------
+#% grep -i 'pattern' files
+#% minigrep '(?i)pattern' files
+#-----------------------------
+
+
+# @@PLEAC@@_6.20
+ans = $stdin.gets.chomp
+re = %r/^#{Regexp.quote(ans)}/
+case
+ when "SEND" =~ re : puts "Action is send"
+ when "STOP" =~ re : puts "Action is stop"
+ when "ABORT" =~ re : puts "Action is abort"
+ when "EDIT" =~ re : puts "Action is edit"
+end
+#-----------------------------
+require 'abbrev'
+table = Abbrev.abbrev %w-send stop abort edit-
+loop do
+ print "Action: "
+ ans = $stdin.gets.chomp
+ puts "Action for #{ans} is #{table[ans.downcase]}"
+end
+
+
+#-----------------------------
+# dummy values are defined for 'file', 'PAGER', and
+# the 'invoke_editor' and 'deliver_message' methods
+# do not do anything interesting in this example.
+#!/usr/bin/ruby -w
+require 'abbrev'
+
+file = 'pleac_ruby.data'
+PAGER = 'less'
+
+def invoke_editor
+ puts "invoking editor"
+end
+
+def deliver_message
+ puts "delivering message"
+end
+
+actions = {
+ 'edit' => self.method(:invoke_editor),
+ 'send' => self.method(:deliver_message),
+ 'list' => proc {system(PAGER, file)},
+ 'abort' => proc {puts "See ya!"; exit},
+ "" => proc {puts "Unknown Command"}
+}
+
+dtable = Abbrev.abbrev(actions.keys)
+loop do
+ print "Action: "
+ ans = $stdin.gets.chomp.delete(" \t")
+ actions[ dtable[ans.downcase] || "" ].call
+end
+
+
+# @@PLEAC@@_6.19
+#-----------------------------
+# basically, the Perl Cookbook categorizes this as an
+# unsolvable problem ...
+#-----------------------------
+1 while addr.gsub!(/\([^()]*\)/,'')
+#-----------------------------
+Dear someuser@host.com,
+
+Please confirm the mail address you gave us Wed May 6 09:38:41
+MDT 1998 by replying to this message. Include the string
+"Rumpelstiltskin" in that reply, but spelled in reverse; that is,
+start with "Nik...". Once this is done, your confirmed address will
+be entered into our records.
+
+
+# @@PLEAC@@_6.21
+#-----------------------------
+#% gunzip -c ~/mail/archive.gz | urlify > archive.urlified
+#-----------------------------
+#% urlify ~/mail/*.inbox > ~/allmail.urlified
+#-----------------------------
+#!/usr/bin/ruby -w
+# urlify - wrap HTML links around URL-like constructs
+
+urls = '(https?|telnet|gopher|file|wais|ftp)';
+ltrs = '\w';
+gunk = '/#~:.?+=&%@!\-';
+punc = '.:?\-';
+any = "#{ltrs}#{gunk}#{punc}";
+
+ARGF.each do |line|
+ line.gsub! %r/
+ \b # start at word boundary
+ ( # begin $1 {
+ #{urls} : # need resource and a colon
+ [#{any}] +? # followed by on or more
+ # of any valid character, but
+ # be conservative and take only
+ # what you need to....
+ ) # end $1 }
+ (?= # look-ahead non-consumptive assertion
+ [#{punc}]* # either 0 or more punctuation
+ [^#{any}] # followed by a non-url char
+ | # or else
+ $ # then end of the string
+ )
+ /iox do
+ %Q|<A HREF="#{$1}">#{$1}</A>|
+ end
+ print line
+end
+
+
+# @@PLEAC@@_6.23
+%r/^m*(d?c{0,3}|c[dm])(l?x{0,3}|x[lc])(v?i{0,3}|i[vx])$/i
+#-----------------------------
+str.sub!(/(\S+)(\s+)(\S+)/, '\3\2\1')
+#-----------------------------
+%r/(\w+)\s*=\s*(.*)\s*$/ # keyword is $1, value is $2
+#-----------------------------
+%r/.{80,}/
+#-----------------------------
+%r|(\d+)/(\d+)/(\d+) (\d+):(\d+):(\d+)|
+#-----------------------------
+str.gsub!(%r|/usr/bin|,'/usr/local/bin')
+#-----------------------------
+str.gsub!(/%([0-9A-Fa-f][0-9A-Fa-f])/){ $1.hex.chr }
+#-----------------------------
+str.gsub!(%r{
+ /\* # Match the opening delimiter
+ .*? # Match a minimal number of characters
+ \*/ # Match the closing delimiter
+}xm,'')
+#-----------------------------
+str.sub!(/^\s+/, '')
+str.sub!(/\s+$/, '')
+
+# but really, in Ruby we'd just do:
+str.strip!
+#-----------------------------
+str.gsub!(/\\n/,"\n")
+#-----------------------------
+str.sub!(/^.*::/, '')
+#-----------------------------
+%r/^([01]?\d\d|2[0-4]\d|25[0-5])\.([01]?\d\d|2[0-4]\d|25[0-5])\.
+ ([01]?\d\d|2[0-4]\d|25[0-5])\.([01]?\d\d|2[0-4]\d|25[0-5])$/x
+#-----------------------------
+str.sub!(%r|^.*/|, '')
+#-----------------------------
+cols = ( (ENV['TERMCAP'] || " ") =~ /:co#(\d+):/ ) ? $1 : 80;
+#-----------------------------
+name = " #{$0} #{ARGV}".gsub(%r| /\S+/|, ' ')
+#-----------------------------
+require 'rbconfig'
+include Config
+raise "This isn't Linux" unless CONFIG['target_os'] =~ /linux/i;
+#-----------------------------
+str.gsub!(%r/\n\s+/, ' ')
+#-----------------------------
+nums = str.scan(/(\d+\.?\d*|\.\d+)/)
+#-----------------------------
+capwords = str.scan(%r/(\b[^\Wa-z0-9_]+\b)/)
+#-----------------------------
+lowords = str.scan(%r/(\b[^\WA-Z0-9_]+\b)/)
+#-----------------------------
+icwords = str.scan(%r/(\b[^\Wa-z0-9_][^\WA-Z0-9_]*\b)/)
+#-----------------------------
+links = str.scan(%r/<A[^>]+?HREF\s*=\s*["']?([^'" >]+?)[ '"]?>/mi)
+#-----------------------------
+initial = str =~ /^\S+\s+(\S)\S*\s+\S/ ? $1 : ""
+#-----------------------------
+str.gsub!(%r/"([^"]*)"/, %q-``\1''-)
+#-----------------------------
+
+$/ = ""
+sentences = []
+ARGF.each do |para|
+ para.gsub!(/\n/, ' ')
+ para.gsub!(/ {3,}/,' ')
+ sentences << para.scan(/(\S.*?[!?.])(?= |\Z)/)
+end
+
+#-----------------------------
+%r/(\d{4})-(\d\d)-(\d\d)/ # YYYY in $1, MM in $2, DD in $3
+#-----------------------------
+%r/ ^
+ (?:
+ 1 \s (?: \d\d\d \s)? # 1, or 1 and area code
+ | # ... or ...
+ \(\d\d\d\) \s # area code with parens
+ | # ... or ...
+ (?: \+\d\d?\d? \s)? # optional +country code
+ \d\d\d ([\s\-]) # and area code
+ )
+ \d\d\d (\s|\1) # prefix (and area code separator)
+ \d\d\d\d # exchange
+ $
+ /x
+#-----------------------------
+%r/\boh\s+my\s+gh?o(d(dess(es)?|s?)|odness|sh)\b/i
+#-----------------------------
+lines = []
+lines << $1 while input.sub!(/^([^\012\015]*)(\012\015?|\015\012?)/,'')
+
+
+# @@PLEAC@@_7.0
+# An IO object being Enumerable, we can use 'each' directly on it
+File.open("/usr/local/widgets/data").each { |line|
+ puts line if line =~ /blue/
+}
+
+logfile = File.new("/var/log/rubylog.txt", "w")
+mysub($stdin, logfile)
+
+# The method IO#readline is similar to IO#gets
+# but throws an exception when it reaches EOF
+f = File.new("bla.txt")
+begin
+ while (line = f.readline)
+ line.chomp
+ $stdout.print line if line =~ /blue/
+ end
+rescue EOFError
+ f.close
+end
+
+while $stdin.gets # reads from STDIN
+ unless (/\d/)
+ $stderr.puts "No digit found." # writes to STDERR
+ end
+ puts "Read: #{$_}" # writes to STDOUT
+end
+
+logfile = File.new("/tmp/log", "w")
+
+logfile.close
+
+# $defout (or its synonym '$>') is the destination of output
+# for Kernel#print, Kernel#puts, and family functions
+logfile = File.new("log.txt", "w")
+old = $defout
+$defout = logfile # switch to logfile for output
+puts "Countdown initiated ..."
+$defout = old # return to original output
+puts "You have 30 seconds to reach minimum safety distance."
+
+
+# @@PLEAC@@_7.1
+source = File.new(path, "r") # open file "path" for reading only
+sink = File.new(path, "w") # open file "path" for writing only
+
+source = File.open(path, File::RDONLY) # open file "path" for reading only
+sink = File.open(path, File::WRONLY) # open file "path" for writing only
+
+file = File.open(path, "r+") # open "path" for reading and writing
+file = File.open(path, flags) # open "path" with the flags "flags" (see examples below for flags)
+
+# open file "path" read only
+file = File.open(path, "r")
+file = File.open(path, File::RDONLY)
+
+# open file "path" write only, create it if it does not exist
+# truncate it to zero length if it exists
+file = File.open(path, "w")
+file = File.open(path, File::WRONLY|File::TRUNC|File::CREAT)
+file = File.open(path, File::WRONLY|File::TRUNC|File::CREAT, 0666) # with permission 0666
+
+# open file "path" write only, fails if file exists
+file = File.open(path, File::WRONLY|File::EXCL|File::CREAT)
+file = File.open(path, File::WRONLY|File::EXCL|File::CREAT, 0666)
+
+# open file "path" for appending
+file = File.open(path, "a")
+file = File.open(path, File::WRONLY|File::APPEND|File::CREAT)
+file = File.open(path, File::WRONLY|File::APPEND|File::CREAT, 0666)
+
+# open file "path" for appending only when file exists
+file = File.open(path, File::WRONLY|File::APPEND)
+
+# open file "path" for reading and writing
+file = File.open(path, "r+")
+file = File.open(path, File::RDWR)
+
+# open file for reading and writing, create a new file if it does not exist
+file = File.open(path, File::RDWR|File::CREAT)
+file = File.open(path, File::RDWR|File::CREAT, 0600)
+
+# open file "path" reading and writing, fails if file exists
+file = File.open(path, File::RDWR|File::EXCL|File::CREAT)
+file = File.open(path, File::RDWR|File::EXCL|File::CREAT, 0600)
+
+
+# @@PLEAC@@_7.2
+# No problem with Ruby since the filename doesn't contain characters with
+# special meaning; like Perl's sysopen
+File.open(filename, 'r')
+
+
+# @@PLEAC@@_7.3
+File.expand_path('~root/tmp')
+#=> "/root/tmp"
+File.expand_path('~rpcuser')
+#=> "/var/lib/nfs"
+
+# To expand ~/.. it explicitely needs the environment variable HOME
+File.expand_path('~/tmp')
+#=> "/home/gc/tmp"
+
+
+# @@PLEAC@@_7.4
+# The exception raised in Ruby reports the filename
+File.open('afile')
+
+
+# @@PLEAC@@_7.5
+# Standard Ruby distribution provides the following useful extension
+require 'tempfile'
+# With the Tempfile class, the file is automatically deleted on garbage
+# collection, so you won't need to remove it, later on.
+tf = Tempfile.new('tmp') # a name is required to create the filename
+
+# If you need to pass the filename to an external program you can use
+# File#path, but don't forget to File#flush in order to flush anything
+# living in some buffer somewhere.
+tf.flush
+system("/usr/bin/dowhatever #{tf.path}")
+
+fh = Tempfile.new('tmp')
+fh.sync = true # autoflushes
+10.times { |i| fh.puts i }
+fh.rewind
+puts 'Tmp file has: ', fh.readlines
+
+
+# @@PLEAC@@_7.6
+while (DATA.gets) do
+ # process the line
+end
+__END__
+# your data goes here
+# __DATA__ doesn't exist in Ruby
+
+#CODE
+# get info about the script (size, date of last modification)
+kilosize = DATA.stat.size / 1024
+last_modif = DATA.stat.mtime
+puts "<P>Script size is #{kilosize}"
+puts "<P>Last script update: #{last_modif}"
+__END__
+# DO NOT REMOVE THE PRECEEDING LINE.
+# Everything else in this file will be ignored.
+#CODE
+
+
+# @@PLEAC@@_7.7
+while line = gets do
+ # do something with line.
+end
+
+# or
+while gets do
+ # do something with $_
+end
+
+# or more rubyish
+$stdin.each do |line|
+ # do stuff with line
+end
+
+
+# ARGF may makes this more easy
+# this is skipped if ARGV.size==0
+ARGV.each do |filename|
+ # closing and exception handling are done by the block
+ open(filename) do |fd|
+ fd.each do |line|
+ # do stuff with line
+ end
+ end rescue abort("can't open %s" % filename)
+end
+
+# globbing is done in the Dir module
+ARGV = Dir["*.[Cch]"] if ARGV.empty?
+
+# note: optparse is the preferred way to handle this
+if (ARGV[0] == '-c')
+ chop_first += 1
+ ARGV.shift
+end
+
+
+# processing numerical options
+if ARGV[0] =~ /^-(\d+)$/
+ columns = $1
+ ARGV.shift
+end
+
+# again, better to use optparse:
+require 'optparse'
+nostdout = 0
+append = 0
+unbuffer = 0
+ignore_ints = 0
+ARGV.options do |opt|
+ opt.on('-n') { nostdout +=1 }
+ opt.on('-a') { append +=1 }
+ opt.on('-u') { unbuffer +=1 }
+ opt.on('-i') { ignore_ints +=1 }
+ opt.parse!
+end or abort("usage: " + __FILE__ + " [-ainu] [filenames]")
+
+# no need to do undef $/, we have File.read
+str = File.read(ARGV[0])
+
+# again we have File.read
+str = File.read(ARGV[0])
+
+# not sure what this should do:
+# I believe open the file, print filename, lineno and line:
+ARGF.each_with_index do |line, idx|
+ print ARGF.filename, ":", idx, ";", line
+end
+
+# print all the lines in every file passed via command line that contains login
+ARGF.each do |line|
+ puts line if line =~ /login/
+end
+#
+# even this would fit
+#%ruby -ne "print if /f/" 2.log
+#
+
+ARGF.each { |l| puts l.downcase! }
+
+#------------------
+#!/usr/bin/ruby -p
+# just like perl's -p
+$_.downcase!
+#
+
+# I don't know who should I trust.
+# perl's version splits on \w+ while python's on \w.
+
+chunks = 0
+
+File.read(ARGV[0]).split.each do |word|
+ next if word =~ /^#/
+ break if ["__DATA__", "__END__"].member? word
+ chunks += 1
+end
+
+print "Found ", chunks, " chunks\n"
+
+
+# @@PLEAC@@_7.8
+old = File.open(old_file)
+new = File.open(new_file, "w")
+while old.gets do
+ # change $_, then...
+ new.print $_
+end
+old.close
+new.close
+File.rename(old_file, "old.orig")
+File.rename(new_file, old_file)
+
+while old.gets do
+ if $. == 20 then # we are at the 20th line
+ new.puts "Extra line 1"
+ new.puts "Extra line 2"
+ end
+ new.print $_
+end
+
+while old.gets do
+ next if 20..30 # skip the 20th line to the 30th
+ # Ruby (and Perl) permit to write if 20..30
+ # instead of if (20 <= $.) and ($. <= 30)
+ new.print $_
+end
+
+
+# @@PLEAC@@_7.9
+#% ruby -i.orig -pe 'FILTER COMMAND' file1 file2 file3 ...
+#
+#-----------------------------
+##!/usr/bin/ruby -i.orig -p
+# filter commands go here
+#-----------------------------
+
+#% ruby -pi.orig -e 'gsub!(/DATE/){Time.now)'
+
+# effectively becomes:
+ARGV << 'I'
+oldfile = ""
+while gets
+ if ARGF.filename != oldfile
+ newfile = ARGF.filename
+ File.rename(newfile, newfile + ".orig")
+ $stdout = File.open(newfile,'w')
+ oldfile = newfile
+ end
+ gsub!(/DATE/){Time.now}
+ print
+end
+$stdout = STDOUT
+#-----------------------------
+#% ruby -i.old -pe 'gsub!(%r{\bhisvar\b}, 'hervar')' *.[Cchy]
+
+#-----------------------------
+# set up to iterate over the *.c files in the current directory,
+# editing in place and saving the old file with a .orig extension
+$-i = '.orig' # set up -i mode
+ARGV.replace(Dir['*.[Cchy]'])
+while gets
+ if $. == 1
+ print "This line should appear at the top of each file\n"
+ end
+ gsub!(/\b(p)earl\b/i, '\1erl') # Correct typos, preserving case
+ print
+ ARGF.close if ARGF.eof
+end
+
+
+# @@PLEAC@@_7.10
+File.open('itest', 'r+') do |f| # open file for update
+ lines = f.readlines # read into array of lines
+ lines.each do |it| # modify lines
+ it.gsub!(/foo/, 'QQQ')
+ end
+ f.pos = 0 # back to start
+ f.print lines # write out modified lines
+ f.truncate(f.pos) # truncate to new length
+end # file is automatically closed
+#-----------------------------
+File.open('itest', 'r+') do |f|
+ out = ""
+ f.each do |line|
+ out << line.gsub(/DATE/) {Time.now}
+ end
+ f.pos = 0
+ f.print out
+ f.truncate(f.pos)
+end
+
+# @@PLEAC@@_7.11
+File.open('infile', 'r+') do |f|
+ f.flock File::LOCK_EX
+ # update file
+end
+#-----------------------------
+File::LOCK_SH # shared lock (for reading)
+File::LOCK_EX # exclusive lock (for writing)
+File::LOCK_NB # non-blocking request
+File::LOCK_UN # free lock
+#-----------------------------
+unless f.flock File::LOCK_EX | File::LOCK_NB
+ warn "can't get immediate lock: blocking ..."
+ f.flock File::LOCK_EX
+end
+#-----------------------------
+File.open('numfile', File::RDWR|File::CREAT) do |f|
+ f.flock(File::LOCK_EX)
+ num = f.gets.to_i || 0
+ f.pos = 0
+ f.truncate 0
+ f.puts num + 1q
+end
+
+
+# @@PLEAC@@_7.12
+output_handle.sync = true
+# Please note that like in Perl, $stderr is already unbuffered
+#-----------------------------
+#!/usr/bin/ruby -w
+# seeme - demo stdio output buffering
+$stdout.sync = ARGV.size > 0
+print "Now you don't see it..."
+sleep 2
+puts "now you do"
+#-----------------------------
+$stderr.sync = true
+afile.sync = false
+#-----------------------------
+# assume 'remote_con' is an interactive socket handle,
+# but 'disk_file' is a handle to a regular file.
+remote_con.sync = true # unbuffer for clarity
+disk_file.sync = false # buffered for speed
+#-----------------------------
+require 'socket'
+sock = TCPSocket.new('www.ruby-lang.org', 80)
+sock.sync = true
+sock.puts "GET /en/ HTTP/1.0 \n\n"
+resp = sock.read
+print "DOC IS: #{resp}\n"
+
+
+# @@PLEAC@@_7.13
+#-----------------------------
+# assumes fh1, fh2, fh2 are oen IO objects
+nfound = select([$stdin, fh1, fh2, fh3], nil, nil, 0)
+nfound[0].each do |file|
+ case file
+ when fh1
+ # do something with fh1
+ when fh2
+ # do something with fh2
+ when fh3
+ # do something with fh3
+ end
+end
+#-----------------------------
+input_files = []
+# repeat next line for all in-files to poll
+input_files << fh1
+if nfound = select(input_files, nil, nil, 0)
+ # input ready on files in nfound[0]
+end
+
+
+# @@PLEAC@@_8.0
+#-----------------------------
+# datafile is a file or IO object
+datafile.readlines.each { |line|
+ line.chomp!
+ size = line.length
+ puts size
+}
+#-----------------------------
+datafile.readlines.each { |line|
+ puts line.chomp!.length
+}
+#-----------------------------
+lines = datafile.readlines
+#-----------------------------
+whole_file = file.read
+#-----------------------------
+# ruby -040 -e 'word = gets; puts "First word is #{word}"'
+#-----------------------------
+# ruby -ne 'BEGIN { $/="%%\n" }; $_.chomp; puts $_ if( $_=~/Unix/i)' fortune.dat
+#-----------------------------
+handle.print "one", "two", "three" # "onetwothree"
+puts "Baa baa black sheep." # sent to $stdout
+#-----------------------------
+buffer = handle.read(4096)
+rv = buffer.length
+#-----------------------------
+handle.truncate(length)
+open("/tmp#{$$}.pid", 'w') { |handle| handle.truncate(length) }
+#-----------------------------
+pos = datafile.pos # tell is an alias of pos
+puts "I'm #{pos} bytes from the start of datafile"
+#-----------------------------
+logfile.seek(0, IO::SEEK_END)
+datafile.seek(pos) # IO::SEEK_SET is the default
+out.seek(-20, IO::SEEK_CUR)
+#-----------------------------
+written = datafile.syswrite(mystring)
+raise RunTimeError unless written == mystring.length
+block = infile.sysread(256) # no equivalent to perl offset parameter in sysread
+puts "only read #{block.length} bytes" if 256 != block.length
+#-----------------------------
+pos = handle.sysseek(0, IO::SEEK_CUR) # don't change position
+
+
+# @@PLEAC@@_8.1
+while (line = fh.gets)
+ line.chomp!
+ nextline = nil
+ line.gsub!(/\\$/) { |match| nextline = fh.gets; '' }
+ if (nextline != nil)
+ line += nextline
+ redo
+ end
+ # process full record in line here
+end
+#-----------------------------
+# DISTFILES = $(DIST_COMMON) $(SOURCES) $(HEADERS) \
+# $(TEXINFOS) $(INFOS) $(MANS) $(DATA)
+# DEP_DISTFILES = $(DIST_COMMON) $(SOURCES) $(HEADERS) \
+# $(TEXINFOS) $(INFO_DEPS) $(MANS) $(DATA) \
+# $(EXTRA_DIST)
+#-----------------------------
+line.gsub!(/\\\s*$/, '') {
+ # as before
+}
+
+
+# @@PLEAC@@_8.2
+#-----------------------------
+count = `wc -l < #{filename}`
+fail "wc failed: #{$?}" if $? != 0
+count.chomp!
+#-----------------------------
+count = 0
+File.open(file, 'r') { |fh|
+ count += 1 while fh.gets
+}
+# count now holds the number of lines read
+#-----------------------------
+count = 0
+while (chunk = file.sysread(2**16))
+ count += chunk.count("\n")
+end rescue EOFError
+#-----------------------------
+File.open(filename,'r') { |fh|
+ count += 1 while fh.gets
+}
+# count now holds the number of lines read
+#-----------------------------
+# As ruby doesn't quite have an equivalent to using a for
+# statement as in perl, I threw this in
+count = File.readlines(filename).size
+#-----------------------------
+1 while file.gets
+count = $.
+#-----------------------------
+$/ = ''
+open(filename, 'r') { |fh|
+ 1 while fh.gets
+ para_count = $.
+} rescue fail("can't open #{filename}: $!")
+#-----------------------------
+
+
+# ^^PLEAC^^_8.3
+#-----------------------------
+while (gets)
+ split.each { |chunk|
+ # do something with chunk
+ }
+end
+#-----------------------------
+while (gets)
+ gsub(/(\w[\w'-]*)/) { |word|
+ # do something with word
+ }
+end
+#-----------------------------
+# Make a word frequency count
+# normally hashes can be created using {} or just Hash.new
+# but we want the default value of an entry to be 0 instead
+# of nil. (nil can't be incremented)
+seen = Hash.new(0)
+while (gets)
+ gsub(/(\w[\w'-]*)/) { |word|
+ seen[word.downcase] += 1
+ }
+end
+# output hash in a descending numeric sort of its values
+seen.sort { |a,b| b[1] <=> a[1] }.each do |k,v|
+ printf("%5d %s\n", v, k )
+end
+
+#-----------------------------
+# Line frequency count
+seen = Hash.new(0)
+while (gets)
+ seen[$_.downcase] += 1
+end
+seen.sort { |a,b| b[1] <=> a[1] }.each do |k,v|
+ printf("%5d %s\n", v, k )
+end
+#-----------------------------
+
+
+# @@PLEAC@@_8.4
+#-----------------------------
+# instead of file handle FILE, we can just
+# use a string containing the filename
+File.readlines(file).each { |line|
+ # do something with line
+}
+#-----------------------------
+File.readlines(file).reverse_each { |line|
+ # do something with line
+}
+#-----------------------------
+# the variable lines might have been created
+# this way
+# lines = File.readlines(file)
+#
+# normally one would use the reverse_each, but
+# if you insist on using a numerical index to
+# iterate over the lines array...
+(lines.size - 1).downto(0) { |i|
+ line = lines[i]
+}
+#-----------------------------
+# the second readlines argument is a the
+# record separator $/, just like perl, a blank
+# separator splits the records into paragraphs
+File.readlines(file, '').each { |paragraph|
+ # do something with paragraph
+ puts "->Paragraph #{paragraph}"
+}
+#-----------------------------
+
+
+# @@PLEAC@@_8.6
+
+$/ = "%\n";
+srand;
+
+File.open('/usr/share/fortune/humorists').each do |line|
+ adage = line if rand($.) < 1
+end
+
+puts adage;
+
+
+# @@PLEAC@@_8.10
+begin
+ fh = File.open(file, "r+")
+ addr = fh.tell unless fh.eof while fh.gets
+ fh.truncate(addr)
+rescue SystemCallError
+ $stderr.puts "#$!"
+end
+
+
+# @@PLEAC@@_9.0
+entry = File.stat("/usr/bin/vi")
+entry = File.stat("/usr/bin")
+entry = File.stat(INFILE)
+
+entry = File.stat("/usr/bin/vi")
+ctime = entry.ctime
+size = entry.size
+
+f = File.open(filename, "r")
+
+## There is no -T equivalent in Ruby, but we can still test emptiness
+if test(?s, filename)
+ puts "#{filename} doesn't have text in it."
+ exit
+end
+
+Dir.new("/usr/bin").each do |filename|
+ puts "Inside /usr/bin is something called #{filename}"
+end
+
+
+# @@PLEAC@@_9.1
+file = File.stat("filename")
+readtime, writetime = file.atime, file.mtime
+file.utime(readtime, writetime)
+
+SECONDS_PER_DAY = 60 * 60 * 24
+file = File.stat("filename")
+atime, mtime = file.atime, file.mtime
+
+atime -= 7 * SECONDS_PER_DAY
+mtime -= 7 * SECONDS_PER_DAY
+
+File.utime(atime, mtime, file)
+mtime = File.stat(file).mtime
+File.utime(Time.new, mtime, file)
+File.utime(Time.new, File.stat("testfile").mtime, file)
+
+#-----------------------------
+#!/usr/bin/ruby -w
+## uvi - vi a file without changing it's access times
+
+if ARGV.length != 1
+ puts "usage: uvi filename"
+ exit
+end
+file = ARGV[0]
+atime, mtime = File.stat(file).atime, File.stat(file).mtime
+system(ENV["EDITOR"] || "vi", file)
+File.utime(atime, mtime, file)
+#-----------------------------
+
+
+# @@PLEAC@@_9.2
+File.unlink(FILENAME)
+
+err_flg = false
+filenames.each do |file|
+ begin
+ File.unlink(file)
+ rescue
+ err_flg = $!
+ end
+end
+err_flg and raise "Couldn't unlink all of #{filenames.join(" ")}: #{err_flg}"
+
+File.unlink(file)
+
+count = filenames.length
+filenames.each do |file|
+ begin
+ File.unlink(file)
+ rescue
+ count -= 1
+ end
+end
+if count != filenames.length
+ STDERR.puts "could only delete #{count} of #{filenames.length} files"
+end
+
+
+# @@PLEAC@@_9.3
+require "ftools"
+File.copy(oldfile, newfile)
+
+infile = File.open(oldfile, "r")
+outfile = File.open(newfile, "w")
+
+blksize = infile.stat.blksize
+# This doesn't handle partial writes or ^Z
+# like the Perl version does.
+while (line = infile.read(blksize))
+ outfile.write(line)
+end
+
+infile.close
+outfile.close
+
+system("cp #{oldfile} #{newfile}") # unix
+system("copy #{oldfile} #{newfile}") # dos, vms
+
+require "ftools"
+File.copy("datafile.dat", "datafile.bak")
+File.move("datafile.new", "datafile.dat")
+
+
+# @@PLEAC@@_9.4
+$seen = {} # must use global var to be seen inside of method below
+
+def do_my_thing(filename)
+ dev, ino = File.stat(filename).dev, File.stat(filename).ino
+ unless $seen[[dev, ino]]
+ # do something with $filename because we haven't
+ # seen it before
+ end
+ $seen[[dev, ino]] = $seen[[dev, ino]].to_i + 1
+end
+
+files.each do |filename|
+ dev, ino = File.stat(filename).dev, File.stat(filename).ino
+ if !$seen.has_key?([dev, ino])
+ $seen[[dev, ino]] = []
+ end
+ $seen[[dev, ino]].push(filename)
+end
+
+$seen.keys.sort.each do |devino|
+ ino, dev = devino
+ if $seen[devino].length > 1
+ # $seen[devino] is a list of filenames for the same file
+ end
+end
+
+
+# @@PLEAC@@_9.5
+Dir.open(dirname) do |dir|
+ dir.each do |file|
+ # do something with dirname/file
+ puts file
+ end
+end
+# Dir.close is automatic
+
+# No -T equivalent in Ruby
+
+dir.each do |file|
+ next if file =~ /^\.\.?$/
+ # ...
+end
+
+def plainfiles(dir)
+ dh = Dir.open(dir)
+ dh.entries.grep(/^[^.]/).
+ map {|file| "#{dir}/#{file}"}.
+ find_all {|file| test(?f, file)}.
+ sort
+end
+
+
+# @@PLEAC@@_9.6
+list = Dir.glob("*.c")
+
+dir = Dir.open(path)
+files = dir.entries.grep(/\.c$/)
+dir.close
+
+files = Dir.glob("*.c")
+files = Dir.open(path).entries.grep(/\.[ch]$/i)
+
+dir = Dir.new(path)
+files = dir.entries.grep(/\.[ch]$/i)
+
+begin
+ d = Dir.open(dir)
+rescue Errno::ENOENT
+ raise "Couldn't open #{dir} for reading: #{$!}"
+end
+
+files = []
+d.each do |file|
+ puts file
+ next unless file =~ /\.[ch]$/i
+
+ filename = "#{dir}/#{file}"
+ # There is no -T equivalent in Ruby, but we can still test emptiness
+ files.push(filename) if test(?s, filename)
+end
+
+dirs.entries.grep(/^\d+$/).
+ map { |file| [file, "#{path}/#{file}"]} .
+ select { |file| test(?d, file[1]) }.
+ sort { |a,b| a[0] <=> b[0] }.
+ map { |file| file[1] }
+
+
+# @@PLEAC@@_9.7
+require 'find'
+Find.find(dirlist) do |file|
+ # do whatever
+end
+
+require 'find'
+argv = ARGV.empty? ? %w{.} : ARGV
+Find.find(*argv) do |file|
+ print file, (test(?d, file) ? "/\n" : "\n")
+end
+
+require 'find'
+argv = ARGV.empty? ? %w{.} : ARGV
+sum = 0
+Find.find(*argv) do |file|
+ size = test(?s, file) || 0
+ sum += size
+end
+puts "#{argv.join(' ')} contains #{sum} bytes"
+
+require 'find'
+argv = ARGV.empty? ? %w{.} : ARGV
+saved_size, saved_name = -1, ""
+Find.find(*argv) do |file|
+ size = test(?s, file) || 0
+ next unless test(?f, file) && size > saved_size
+ saved_size = size
+ saved_name = file
+end
+puts "Biggest file #{saved_name} in #{argv.join(' ')} is #{saved_size}"
+
+require 'find'
+argv = ARGV.empty? ? %w{.} : ARGV
+age, name = nil
+Find.find(*argv) do |file|
+ mtime = File.stat(file).mtime
+ next if age && age > mtime
+ age = mtime
+ name = file
+end
+puts "#{name} #{age}"
+
+#-----------------------------
+#!/usr/bin/ruby -w
+# fdirs - find all directories
+require 'find'
+argv = ARGV.empty? ? %w{.} : ARGV
+File.find(*argv) { |file| puts file if test(?d, file) }
+#-----------------------------
+
+
+# @@PLEAC@@_9.8
+require 'fileutils'
+
+puts "Usage #{$0} dir ..." if ARGV.empty?
+ARGV.each do |dir|
+ FileUtils.rmtree(dir)
+end
+
+
+# @@PLEAC@@_9.9
+require 'ftools'
+names.each do |file|
+ newname = file
+ begin
+ File.move(file, newname)
+ rescue Errno::EPERM
+ $stderr.puts "Couldn't rename #{file} to #{newname}: #{$!}"
+ end
+end
+
+require 'ftools'
+op = ARGV.empty? ? (raise "Usage: rename expr [files]\n") : ARGV.shift
+argv = ARGV.empty? ? $stdin.readlines.map { |f| f.chomp } : ARGV
+argv.each do |file|
+ was = file
+ file = eval("file.#{op}")
+ File.move(was, file) unless was == file
+end
+
+
+# @@PLEAC@@_9.10
+base = File.basename(path)
+dir = File.dirname(path)
+# ruby has no fileparse equivalent
+dir, base = File.split(path)
+ext = base.scan(/\..*$/).to_s
+
+path = '/usr/lib/libc.a'
+file = File.basename(path)
+dir = File.dirname(path)
+
+puts "dir is #{dir}, file is #{file}"
+# dir is /usr/lib, file is libc.a
+
+path = '/usr/lib/libc.a'
+dir, filename = File.split(path)
+name, ext = filename.split(/(?=\.)/)
+puts "dir is #{dir}, name is #{name}, ext is #{ext}"
+# NOTE: The Ruby code prints
+# dir is /usr/lib, name is libc, extension is .a
+# while the Perl code prints a '/' after the directory name
+# dir is /usr/lib/, name is libc, extension is .a
+
+# No fileparse_set_fstype() equivalent in ruby
+
+def extension(path)
+ ext = path.scan(/\..*$/).to_s
+ ext.sub(/^\./, "")
+end
+
+
+# @@PLEAC@@_9.11
+#-----------------------------
+#!/usr/bin/ruby -w
+# symirror - build spectral forest of symlinks
+
+require 'find'
+require 'fileutils'
+
+raise "usage: #{$0} realdir mirrordir" unless ARGV.size == 2
+
+srcdir,dstdir = ARGV
+srcmode = File::stat(srcdir).mode
+Dir.mkdir(dstdir, srcmode & 07777) unless test(?d, dstdir)
+
+# fix relative paths
+Dir.chdir(srcdir) {srcdir = Dir.pwd}
+Dir.chdir(dstdir) {dstdir = Dir.pwd}
+
+Find.find(srcdir) do |srcfile|
+ if test(?d, srcfile)
+ dest = srcfile.sub(/^#{srcdir}/, dstdir)
+ dmode = File::stat(srcfile).mode & 07777
+ Dir.mkdir(dest, dmode) unless test(?d, dest)
+ a = Dir["#{srcfile}/*"].reject{|f| test(?d, f)}
+ FileUtils.ln_s(a, dest)
+ end
+end
+
+
+# @@PLEAC@@_9.12
+# we use the Getopt/Declare library here for convenience:
+# http://raa.ruby-lang.org/project/getoptdeclare/
+#-----------------------------
+#!/usr/bin/ruby -w
+# lst - list sorted directory contents (depth first)
+
+require 'find'
+require 'etc'
+require "Getopt/Declare"
+
+# Note: in the option-spec below there must by at least one hard
+# tab in between each -option and its description. For example
+# -i <tab> read from stdin
+
+opts = Getopt::Declare.new(<<'EOPARAM')
+ ============
+ Input Format:
+ -i read from stdin
+ ============
+ Output Format:
+ -l long listing
+ -r reverse listing
+ ============
+ Sort on: (one of)
+ -m mtime (modify time - default)
+ {$sort_criteria = :mtime}
+ -u atime (access time)
+ {$sort_criteria = :atime}
+ -c ctime (inode change time)
+ {$sort_criteria = :ctime}
+ -s size
+ {$sort_criteria = :size}
+ [mutex: -m -u -c -s]
+
+EOPARAM
+
+$sort_criteria ||= :mtime
+files = {}
+DIRS = opts['-i'] ? $stdin.readlines.map{|f|f.chomp!} : ARGV
+DIRS.each do |dir|
+ Find.find(dir) do |ent|
+ files[ent] = File::stat(ent)
+ end
+end
+entries = files.keys.sort_by{|f| files[f].send($sort_criteria)}
+entries = entries.reverse unless opts['-r']
+
+entries.each do |ent|
+ unless opts['-l']
+ puts ent
+ next
+ end
+ stats = files[ent]
+ ftime = stats.send($sort_criteria == :size ? :mtime : $sort_criteria)
+ printf "%6d %04o %6d %8s %8s %8d %s %s\n",
+ stats.ino,
+ stats.mode & 07777,
+ stats.nlink,
+ ETC::PASSWD[stats.uid].name,
+ ETC::GROUP[stats.gid].name,
+ stats.size,
+ ftime.strftime("%a %b %d %H:%M:%S %Y"),
+ ent
+end
+
+
+# @@PLEAC@@_10.0
+def hello
+ $greeted += 1 # in Ruby, a variable beginning with $ is global (can be any type of course)
+ puts "hi there!"
+end
+
+# We need to initialize $greeted before it can be used, because "+=" is waiting a Numeric object
+$greeted = 0
+hello # note that appending () is optional to function calls with no parameters
+
+
+# @@PLEAC@@_10.1
+# In Ruby, parameters are named anyway
+def hypotenuse(side1, side2)
+ Math.sqrt(side1**2 + side2**2) # the sqrt function comes from the Math module
+end
+diag = hypotenuse(3, 4)
+
+puts hypotenuse(3, 4)
+
+a = [3, 4]
+print hypotenuse(*a) # the star operator will magically convert an Array into a "tuple"
+
+both = men + women
+
+# In Ruby, all objects are references, so the same problem arises; we then return a new object
+nums = [1.4, 3.5, 6.7]
+def int_all(n)
+ n.collect { |v| v.to_i }
+end
+ints = int_all(nums)
+
+nums = [1.4, 3.5, 6.7]
+def trunc_em(n)
+ n.collect! { |v| v.to_i } # the bang-version of collect modifies the object
+end
+trunc_em(nums)
+
+# Ruby has two chomp version:
+# ``chomp'' chomps the record separator and returns what's expected
+# ``chomp!'' does the same but also modifies the parameter object
+
+
+# @@PLEAC@@_10.2
+def somefunc
+ variable = something # variable is local by default
+end
+
+name, age = ARGV
+start = fetch_time
+
+a, b = pair # will succeed if pair is an Array object (like ARGV is)
+c = fetch_time
+
+# In ruby, run_check can't access a, b, or c until they are
+# explicitely defined global (using leading $), even if they are
+# both defined in the same scope
+
+def check_x(x)
+ y = "whatever"
+ run_check
+ if $condition
+ puts "got $x"
+ end
+end
+
+# The following will keep a reference to the array, though the
+# results will be slightly different from perl: the last element
+# of $global_array will be itself an array
+def save_array(ary)
+ $global_array << ary
+end
+
+# The following gives the same results as in Perl for $global_array,
+# though it doesn't illustrate anymore the way to keep a reference
+# to an object: $global_array is extended with the elements of ary
+def save_array(ary)
+ $global_array += ary
+end
+
+
+# @@PLEAC@@_10.3
+# In Ruby, AFAIK a method cannot access "local variables" defined
+# upper scope; mostly because everything is an object, so you'll
+# do the same by defining an attribute or a static attribute
+
+# In Ruby the BEGIN also exists:
+BEGIN { puts "hello from BEGIN" }
+puts "hello from main"
+BEGIN { puts "hello from 2nd BEGIN" }
+# gives:
+# hello from BEGIN
+# hello from 2nd BEGIN
+# hello from main
+
+# In Ruby, it can be written as a static method and a static
+# variable
+class Counter
+ @@counter = 0
+ def Counter.next_counter; @@counter += 1; end
+end
+
+# There is no need of BEGIN since the variable will get
+# initialized when parsing
+class Counter
+ @@counter = 42
+ def Counter.next_counter; @@counter += 1; end
+ def Counter.prev_counter; @@counter -= 1; end
+end
+
+
+# @@PLEAC@@_10.4
+# You can either get the whole trace as an array of strings, each
+# string telling which file, line and method is calling:
+caller
+
+# ...or only the last caller
+caller[0]
+
+# We need to extract just the method name of the backtrace:
+def whoami; caller()[0] =~ /in `([^']+)'/ ? $1 : '(anonymous)'; end
+def whowasi; caller()[1] =~ /in `([^']+)'/ ? $1 : '(anonymous)'; end
+
+
+# @@PLEAC@@_10.5
+# In Ruby, every value is a reference on an object, thus there is
+# no such problem
+array_diff(array1, array2)
+
+def add_vecpair(a1, a2)
+ results = []
+ a1.each_index { |i| results << (a1[i] + a2[i]) }
+ results
+end
+a = [1, 2]
+b = [5, 8]
+c = add_vecpair(a, b)
+p c
+
+# Add this to the beginning of the function to check if we were
+# given two arrays
+a1.type == Array && a2.type == Array or
+ raise "usage: add_vecpair array1 array2 (was used with: #{a1.type} #{a2.type})"
+
+
+# @@PLEAC@@_10.6
+# There is no return context in Ruby
+
+
+# @@PLEAC@@_10.7
+# Like in Perl, we need to fake with a hash, but it's dirty :-(
+def thefunc(param_args)
+ args = { 'INCREMENT' => '10s', 'FINISH' => '0', 'START' => 0 }
+ args.update(param_args)
+ if (args['INCREMENT'] =~ /m$/ )
+ # .....
+ end
+end
+
+thefunc({ 'INCREMENT' => '20s', 'START' => '+5m', 'FINISH' => '+30m' })
+thefunc({})
+
+
+# @@PLEAC@@_10.8
+# there is no "undef" direct equivalent but there is the slice equiv:
+a, c = func.indexes(0, 2)
+
+
+# @@PLEAC@@_10.9
+# Ruby has no such limitation:
+def somefunc
+ ary = []
+ hash = {}
+ # ...
+ return ary, hash
+end
+arr, dict = somefunc
+
+array_of_hashes = fn
+h1, h2, h3 = fn
+
+
+# @@PLEAC@@_10.10
+return
+# or (equivalent)
+return nil
+
+
+# @@PLEAC@@_10.11
+# You can't prototype in Ruby regarding types :-(
+# Though, you can force the number of arguments:
+def func_with_no_arg; end
+def func_with_no_arg(); end
+def func_with_one_arg(a1); end
+def func_with_two_args(a1, a2); end
+def func_with_any_number_of_args(*args); end
+
+
+# @@PLEAC@@_10.12
+raise "some message" # raise exception
+
+begin
+ val = func
+rescue Exception => msg
+ $stderr.puts "func raised an exception: #{msg}"
+end
+
+# In Ruby the rescue statement uses an exception class, every
+# exception which is not matched is still continuing
+begin
+ val = func
+rescue FullMoonError
+ ...
+end
+
+
+# @@PLEAC@@_10.13
+# Saving Global Values
+# Of course we can just save the value and restore it later:
+def print_age
+ puts "Age is #{$age}"
+end
+
+$age = 18 # global variable
+print_age()
+if condition
+ safeage = $age
+ $age = 23
+ print_age()
+ $age = safeage
+end
+
+# We can also use a method that saves the global variable and
+# restores it automatically when the block is left:
+
+def local(var)
+ eval("save = #{var.id2name}")
+ begin
+ result = yield
+ ensure
+ # we want to call this even if we got an exception
+ eval("#{var.id2name} = save")
+ end
+ result
+end
+
+condition = true
+$age = 18
+print_age()
+if condition
+ local(:$age) {
+ $age = 23
+ print_age()
+ }
+end
+print_age()
+
+# There is no need to use local() for filehandles or directory
+# handles in ruby because filehandles are normal objects.
+
+
+# @@PLEAC@@_10.14
+# In Ruby you may redefine a method [but not overload it :-(]
+# just by defining again with the same name.
+def foo; puts 'foo'; end
+def foo; puts 'bar'; end
+foo
+#=> bar
+
+# You can also take a reference to an existing method before
+# redefining a new one, using the `alias' keyword
+def foo; puts 'foo'; end
+alias foo_orig foo
+def foo; puts 'bar'; end
+foo_orig
+foo
+#=> foo
+#=> bar
+
+# AFAIK, there is no direct way to create a new method whose name
+# comes from a variable, so use "eval"
+colors = %w(red blue green yellow orange purple violet)
+colors.each { |c|
+ eval <<-EOS
+ def #{c}(*a)
+ "<FONT COLOR='#{c}'>" + a.to_s + "</FONT>"
+ end
+ EOS
+}
+
+
+# @@PLEAC@@_10.15
+def method_missing(name, *args)
+ "<FONT COLOR='#{name}'>" + args.join(' ') + "</FONT>"
+end
+puts chartreuse("stuff")
+
+
+# @@PLEAC@@_10.16
+def outer(arg)
+ x = arg + 35
+ inner = proc { x * 19 }
+ x + inner.call()
+end
+
+
+# @@PLEAC@@_10.17
+#!/usr/bin/ruby -w
+# mailsort - sort mbox by different criteria
+require 'English'
+require 'Date'
+
+# Objects of class Mail represent a single mail.
+class Mail
+ attr_accessor :no
+ attr_accessor :subject
+ attr_accessor :fulltext
+ attr_accessor :date
+
+ def initialize
+ @fulltext = ""
+ @subject = ""
+ end
+
+ def append(para)
+ @fulltext << para
+ end
+
+ # this is called if you call puts(mail)
+ def to_s
+ @fulltext
+ end
+end
+
+# represents a list of mails.
+class Mailbox < Array
+
+ Subjectpattern = Regexp.new('Subject:\s*(?:Re:\s*)*(.*)\n')
+ Datepattern = Regexp.new('Date:\s*(.*)\n')
+
+ # reads mails from open file and stores them
+ def read(file)
+ $INPUT_RECORD_SEPARATOR = '' # paragraph reads
+ msgno = -1
+ file.each { |para|
+ if para =~ /^From/
+ mail = Mail.new
+ mail.no = (msgno += 1)
+ md = Subjectpattern.match(para)
+ if md
+ mail.subject = md[1]
+ end
+ md = Datepattern.match(para)
+ if md
+ mail.date = DateTime.parse(md[1])
+ else
+ mail.date = DateTime.now
+ end
+ self.push(mail)
+ end
+ mail.append(para) if mail
+ }
+ end
+
+ def sort_by_subject_and_no
+ self.sort_by { |m|
+ [m.subject, m.no]
+ }
+ end
+
+ # sorts by a list of attributs of mail, given as symbols
+ def sort_by_attributs(*attrs)
+ # you can sort an Enumerable by an array of
+ # values, they would be compared
+ # from ary[0] to ary[n]t, say:
+ # ['b',1] > ['a',10] > ['a',9]
+ self.sort_by { |elem|
+ attrs.map { |attr|
+ elem.send(attr)
+ }
+ }
+ end
+
+end
+
+mailbox = Mailbox.new
+mailbox.read(ARGF)
+
+# print only subjects sorted by subject and number
+for m in mailbox.sort_by_subject_and_no
+ puts(m.subject)
+end
+
+# print complete mails sorted by date, then subject, then number
+for m in mailbox.sort_by_attributs(:date, :subject)
+ puts(m)
+end
+
+
+# @@PLEAC@@_11.7
+def mkcounter(count)
+ start = count
+ bundle = {
+ "NEXT" => proc { count += 1 },
+ "PREV" => proc { count -= 1 },
+ "RESET" => proc { count = start }
+ }
+ bundle["LAST"] = bundle["PREV"]
+ return bundle
+end
+
+c1 = mkcounter(20)
+c2 = mkcounter(77)
+
+puts "next c1: #{c1["NEXT"].call}" # 21
+puts "next c2: #{c2["NEXT"].call}" # 78
+puts "next c1: #{c1["NEXT"].call}" # 22
+puts "last c1: #{c1["PREV"].call}" # 21
+puts "last c1: #{c1["LAST"].call}" # 20
+puts "old c2: #{c2["RESET"].call}" # 77
+
+
+# @@PLEAC@@_11.15
+class Binary_tree
+ def initialize(val)
+ @value = val
+ @left = nil
+ @right = nil
+ end
+
+ # insert given value into proper point of
+ # provided tree. If no tree provided,
+ # use implicit pass by reference aspect of @_
+ # to fill one in for our caller.
+ def insert(val)
+ if val < @value then
+ if @left then
+ @left.insert(val)
+ else
+ @left = Binary_tree.new(val)
+ end
+ elsif val > @value then
+ if @right then
+ @right.insert(val)
+ else
+ @right = Binary_tree.new(val)
+ end
+ else
+ puts "double"
+ # do nothing, no double values
+ end
+ end
+
+ # recurse on left child,
+ # then show current value,
+ # then recurse on right child.
+ def in_order
+ @left.in_order if @left
+ print @value, " "
+ @right.in_order if @right
+ end
+
+ # show current value,
+ # then recurse on left child,
+ # then recurse on right child.
+ def pre_order
+ print @value, " "
+ @left.pre_order if @left
+ @right.pre_order if @right
+ end
+
+ # recurse on left child,
+ # then recurse on right child,
+ # then show current value.
+ def post_order
+ @left.post_order if @left
+ @right.post_order if @right
+ print @value, " "
+ end
+
+ # find out whether provided value is in the tree.
+ # if so, return the node at which the value was found.
+ # cut down search time by only looking in the correct
+ # branch, based on current value.
+ def search(val)
+ if val == @value then
+ return self
+ elsif val < @value then
+ return @left.search(val) if @left
+ return nil
+ else
+ return @right.search(val) if @right
+ return nil
+ end
+ end
+end
+
+# first generate 20 random inserts
+test = Binary_tree.new(0)
+for a in 0..20
+ test.insert(rand(1000))
+end
+
+# now dump out the tree all three ways
+print "Pre order: "; test.pre_order; puts ""
+print "In order: "; test.in_order; puts ""
+print "Post order: "; test.post_order; puts ""
+
+print "search?"
+while gets
+ print test.search($_.to_i)
+ print "\nsearch?"
+end
+
+
+# @@PLEAC@@_12.0
+# class and module names need to have the first letter capitalized
+module Alpha
+ NAME = 'first'
+end
+module Omega
+ NAME = 'last'
+end
+puts "Alpha is #{Alpha::NAME}, Omega is #{Omega::NAME}"
+
+# ruby doesn't differentiate beteen compile-time and run-time
+require 'getoptlong.rb'
+require 'getoptlong' # assumes the .rb
+require 'cards/poker.rb'
+require 'cards/poker' # assumes the .rb
+load 'cards/poker' # require only loads the file once
+
+module Cards
+ module Poker
+ @card_deck = Array.new # or @card_deck = []
+ def shuffle
+ end
+ end
+end
+
+
+# @@PLEAC@@_12.1
+# a module exports all of its functions
+module Your_Module
+ def self.function
+ # this would be called as Your_Module.function
+ end
+
+ def Your_Module.another
+ # this is the same as above, but more specific
+ end
+end
+
+# @@PLEAC@@_12.2
+begin
+ require 'nonexistent'
+rescue LoadError
+ puts "Couldn't load #{$!}" # $! contains the last error string
+end
+
+# @@PLEAC@@_12.4
+# module variables are private unless access functions are defined
+module Alpha
+ @aa = 10
+ @bb = 11
+
+ def self.put_aa
+ puts @aa
+ end
+
+ def self.bb=(val)
+ @bb = val
+ end
+end
+
+Alpha.bb = 12
+# Alpha.aa = 10 # error, no aa=method
+
+
+# @@PLEAC@@_12.5
+# caller provides a backtrace of the call stack
+module MyModule
+ def find_caller
+ caller
+ end
+
+ def find_caller2(i)
+ caller(i) # an argument limits the size of the stack returned
+ end
+end
+
+
+# @@PLEAC@@_12.6
+BEGIN {
+ $logfile = '/tmp/mylog' unless defined? $logfile
+ $LF = File.open($logfile, 'a')
+}
+
+module Logger
+ def self.logmsg(msg)
+ $LF.puts msg
+ end
+
+ logmsg('startup')
+end
+
+END {
+ Logger::logmsg('shutdown')
+ $LF.close
+}
+
+
+# @@PLEAC@@_12.7
+#-----------------------------
+# results may be different on your system
+# % ruby -e "$LOAD_PATH.each_index { |i| printf("%d %s\n", i, $LOAD_PATH[i] }
+#0 /usr/local/lib/site_ruby/1.6
+#1 /usr/local/lib/site_ruby/1.6/i386-linux
+#2 /usr/local/lib/site_ruby/
+#3 /usr/lib/ruby/1.6
+#4 /usr/lib/ruby/1.6/i136-linux
+#5 .
+#-----------------------------
+# syntax for sh, bash, ksh, or zsh
+#$ export RUBYLIB=$HOME/rubylib
+
+# syntax for csh or tcsh
+# % setenv RUBYLIB ~/rubylib
+#-----------------------------
+$LOAD_PATH.unshift "/projects/spectre/lib";
+
+
+# @@PLEAC@@_12.8
+# equivalents in ruby are mkmf, SWIG, or Ruby/DL depending on usage
+
+
+# @@PLEAC@@_12.9
+# no equivalent in ruby
+
+
+# @@PLEAC@@_12.10
+# no equivalent in ruby
+
+
+# @@PLEAC@@_12.11
+module FineTime
+ def self.time
+ # to be defined later
+ end
+end
+
+
+module FineTime
+ def self.time
+ "its a fine time"
+ end
+end
+
+puts FineTime.time #=> "its a fine time"
+
+
+# @@PLEAC@@_12.12
+def even_only(n)
+ raise "#{n} is not even" if (n & 1) != 0 # one way to test
+ # ...
+end
+def even_only(n)
+ $stderr.puts "#{n} is not even" if (n & 1) != 0
+ # ...
+end
+
+
+# @@PLEAC@@_12.17
+# The library archive for ruby is called Ruby Application archive,
+# or shorter RAA, and can be found at http://raa.ruby-lang.org.
+# A typical library is installed like this:
+# % gunzip some-module-4.54.tar.gz
+# % tar xf some-module-4.54.tar
+# % cd some-module-4.54.tar
+# % ruby install.rb config
+# % ruby install.rb setup
+# get superuser previleges here if needed for next step
+# % ruby install.rb install
+
+# Some modules use a different process,
+# you should find details in the documentation
+# Here is an example of such a different process
+# % ruby extconf.rb
+# % make
+# % make install
+
+# If you want the module installed in your own directory:
+# For ruby version specific libraries
+# % ruby install.rb config --site-ruby=~/lib
+# For version independent libraries
+# % ruby install.rb config --site-ruby-common=~/lib
+
+# Information about possible options for config
+# % ruby install.rb --help
+
+# If you have your own complete distribution
+# % ruby install.rb --prefix=path=~/ruby-private
+
+
+# @@PLEAC@@_13.0
+# Classes and objects in Ruby are rather straigthforward
+class Person
+ # Class variables (also called static attributes) are prefixed by @@
+ @@person_counter=0
+
+ # object constructor
+ def initialize(age, name, alive = true) # Default arg like in C++
+ @age, @name, @alive = age, name, alive # Object attributes are prefixed by '@'
+ @@person_counter += 1
+ # There is no '++' operator in Ruby. The '++'/'--' operators are in fact
+ # hidden assignments which affect variables, not objects. You cannot accomplish
+ # assignment via method. Since everything in Ruby is object, '++' and '--'
+ # contradict Ruby OO ideology. Instead '-=' and '+=' are used.
+ end
+
+ attr_accessor :name, :age # This creates setter and getter methods for @name
+ # and @age. See 13.3 for detailes.
+
+ # methods modifying the receiver object usually have the '!' suffix
+ def die!
+ @alive = false
+ puts "#{@name} has died at the age of #{@age}."
+ @alive
+ end
+
+ def kill(anotherPerson)
+ print @name, ' is killing ', anotherPerson.name, ".\n"
+ anotherPerson.die!
+ end
+
+ # methods used as queries
+ # usually have the '?' suffix
+ def alive?
+ @alive && true
+ end
+
+ def year_of_birth
+ Time.now.year - @age
+ end
+
+ # Class method (also called static method)
+ def Person.number_of_people
+ @@person_counter
+ end
+end
+
+# Using the class:
+# Create objects of class Person
+lecter = Person.new(47, 'Hannibal')
+starling = Person.new(29, 'Clarice', true)
+pazzi = Person.new(40, 'Rinaldo', true)
+
+# Calling a class method
+print "There are ", Person.number_of_people, " Person objects\n"
+
+print pazzi.name, ' is ', (pazzi.alive?) ? 'alive' : 'dead', ".\n"
+lecter.kill(pazzi)
+print pazzi.name, ' is ', (pazzi.alive?) ? 'alive' : 'dead', ".\n"
+
+print starling.name , ' was born in ', starling.year_of_birth, "\n"
+
+
+# @@PLEAC@@_13.1
+# If you don't need any initialisation in the constructor,
+# you don't need to write a constructor.
+class MyClass
+end
+
+class MyClass
+ def initialize
+ @start = Time.new
+ @age = 0
+ end
+end
+
+class MyClass
+ def initialize(inithash)
+ @start = Time.new
+ @age = 0
+ for key, value in inithash
+ instance_variable_set("@#{key}", value)
+ end
+ end
+end
+
+# @@PLEAC@@_13.2
+# Objects are destroyed by the garbage collector.
+# The time of destroying is not predictable.
+# The ruby garbage collector can handle circular references,
+# so there is no need to write destructor for that.
+
+# There is no direct support for destructor.
+# You can call a custom function, or more specific a proc object, when the
+# garbage collector is about to destruct the object, but it is unpredictable
+# when this occurs.
+# Also if such a finalizer object has a reference to the orignal object,
+# this may prevent the original object to get garbage collected.
+# Because of this problem the finalize method below is
+# a class method and not a instance method.
+# So if you need to free resources for an object, like
+# closing a socket or kill a spawned subprocess,
+# you should do it explicitly.
+
+class MyClass
+ def initialize
+ ObjectSpace.define_finalizer(self,
+ self.class.method(:finalize).to_proc)
+ end
+ def MyClass.finalize(id)
+ puts "Object #{id} dying at #{Time.new}"
+ end
+end
+
+# test code
+3.times {
+ MyClass.new
+}
+ObjectSpace.garbage_collect
+
+
+# @@PLEAC@@_13.3
+# You can write getter and setter methods in a natural way:
+class Person
+ def name
+ @name
+ end
+ def name=(name)
+ @name = name
+ end
+end
+
+# But there is a better and shorter way
+class Person
+ attr_reader :age
+ attr_writer :name
+ # attr_reader and attr_writer are actually methods in class Class
+ # which set getter and setter methods for you.
+end
+
+# There is also attr_accessor to create both setters and getters
+class Person
+ attr_accessor :age, :name
+end
+
+
+# @@PLEAC@@_13.4
+class Person
+ # Class variables (also called static attributes) are prefixed by @@
+ @@person_counter = 0
+
+ def Person.population
+ @@person_counter
+ end
+ def initialize
+ @@person_counter += 1
+ ObjectSpace.define_finalizer(self,
+ self.class.method(:finalize).to_proc)
+ end
+ def Person.finalize(id)
+ @@person_counter -= 1
+ end
+end
+people = []
+10.times {
+ people.push(Person.new)
+}
+printf("There are %d people alive", Person.population)
+
+
+FixedArray.class_max_bounds = 100
+alpha = FixedArray.new
+puts "Bound on alpha is #{alpha.max_bounds}"
+
+beta = FixedArray.new
+beta.max_bounds = 50 # calls the instance method
+beta.class.class_max_bounds = 50 # alternative, calls the class method
+puts "Bound on alpha is #{alpha.max_bounds}"
+
+class FixedArray
+ @@bounds = 7
+
+ def max_bounds
+ @@max_bounds
+ end
+ # instance method, which sets the class variable
+ def max_bounds=(value)
+ @@max_bounds = value
+ end
+ # class method. This can only be called on a class,
+ # but not on the instances
+ def FixedArray.class_max_bounds=(value)
+ @@max_bounds = value
+ end
+end
+
+
+# @@PLEAC@@_13.5
+PersonStruct = Struct.new("Person", :name, :age, :peers)
+# creates a class "Person::Struct", which is accessiable with the
+# constant "PersonStruct"
+p = PersonStruct.new
+p = Struct::Person.new # alternative using the classname
+p.name = "Jason Smythe"
+p.age = 13
+p.peers = ["Wilbur", "Ralph", "Fred"]
+p[:peers] = ["Wilbur", "Ralph", "Fred"] # alternative access using symbol
+p["peers"] = ["Wilbur", "Ralph", "Fred"] # alternative access using name of field
+p[2] = ["Wilbur", "Ralph", "Fred"] # alternative access using index of field
+puts "At age #{p.age}, #{p.name}'s first friend is #{p.peers[0]}"
+
+# The fields of a struct have no special type, like other ruby variables
+# you can put any objects in. Therefore the discussions how to specify
+# the types of the fields do not apply to ruby.
+
+FamilyStruct = Struct.new("Family", :head, :address, :members)
+folks = FamilyStruct.new
+folks.head = PersonStruct.new
+dad = folks.head
+dad.name = "John"
+dad.age = 34
+
+# supply of own accessor method for the struct for error checking
+class PersonStruct
+ def age=(value)
+ if !value.kind_of?(Integer)
+ raise(ArgumentError, "Age #{value} isn't an Integer")
+ elsif value > 150
+ raise(ArgumentError, "Age #{value} is unreasonable")
+ end
+ @age = value
+ end
+end
+
+
+# @@PLEAC@@_13.6
+# The ruby Object class defines a dup and a clone method.
+# The dup method is recommended for prototype object creation.
+# The default implementation makes a shallow copy,
+# but each class can override it, for example to make a deep copy.
+
+# If you want to call 'new' directly on the instances,
+# you can create a instance method "new", which returns a new duplicate.
+# This method is distinct from the class method new.
+#
+class A
+ def new
+ dup
+ end
+end
+
+ob1 = A.new
+# later on
+ob2 = ob1.new
+
+
+# @@PLEAC@@_13.7
+methname = 'flicker'
+obj.send(methname, 10) # calls obj.flicker(10)
+
+# call three methods on the object, by name
+['start', 'run', 'stop'].each do |method_string|
+ obj.send(method_string)
+end
+
+# Another way is to create a Method object
+method_obj = obj.method('flicker')
+# And then call it
+method_obj.call(10)
+
+
+# @@PLEAC@@_13.8
+# All classes in Ruby inherit from class Object
+# and thus all objects share methods defined in this class
+
+# the class of the object
+puts any_object.type
+
+# Ruby classes are actually objects of class Class and they
+# respond to methods defined in Object class as well
+
+# the superclass of this class
+puts any_object.class.superclass
+
+# ask an object whether it is an instance of particular class
+n = 4.7
+puts n.instance_of?(Float) # true
+puts n.instance_of?(Numeric) # false
+
+# ask an object whether it is an instance of class, one of the
+# superclasses of the object, or modules included in it
+puts n.kind_of?(Float) # true (the class)
+puts n.kind_of?(Numeric) # true (an ancestor class)
+puts n.kind_of?(Comparable) # true (a mixin module)
+puts n.kind_of?(String) # false
+
+# ask an object whether it can respond to a particular method
+puts n.respond_to?('+') # true
+puts n.respond_to?('length') # false
+
+# all methods an object can respond to
+'just a string'.methods.each { |m| puts m }
+
+
+# @@PLEAC@@_13.9
+# Actually any class in Ruby is inheritable
+class Person
+ attr_accessor :age, :name
+ def initialize
+ @name
+ @age
+ end
+end
+#-----------------------------
+dude = Person.new
+dude.name = 'Jason'
+dude.age = 23
+printf "%s is age %d.\n", dude.name, dude.age
+#-----------------------------
+# Inheriting from Person
+class Employee < Person
+ attr_accessor :salary
+end
+#-----------------------------
+empl = Employee.new
+empl.name = 'Jason'
+empl.age = 23
+empl.salary = 200
+printf "%s is age %d, the salary is %d.\n", empl.name, empl.age, empl.salary
+#-----------------------------
+# Any built-in class can be inherited the same way
+class WeirdString < String
+ def initialize(obj)
+ super obj
+ end
+ def +(anotherObj) # + method in this class is overridden
+ # to return the sum of string lengths
+ self.length + anotherObj.length # 'self' can be omitted
+ end
+end
+#-----------------------------
+a = WeirdString.new('hello')
+b = WeirdString.new('bye')
+
+puts a + b # the overridden +
+#=> 8
+puts a.length # method from the superclass, String
+#=> 5
+
+
+# @@PLEAC@@_13.11
+# In ruby you can override the method_missing method
+# to have a solution similar to perls AUTOLOAD.
+class Person
+
+ def initialize
+ @ok_fields = %w(name age peers parent)
+ end
+
+ def valid_attribute?(name)
+ @ok_fields.include?(name)
+ end
+
+ def method_missing(namesymbol, *params)
+ name = namesymbol.to_s
+ return if name =~ /^A-Z/
+ if name.to_s[-1] == ('='[0]) # we have a setter
+ isSetter = true
+ name.sub!(/=$/, '')
+ end
+ if valid_attribute?(name)
+ if isSetter
+ instance_variable_set("@#{name}", *params)
+ else
+ instance_variable_get("@#{name}", *params)
+ end
+ else
+ # if no annestor is responsible,
+ # the Object class will throw a NoMethodError exception
+ super(namesymbol, *params)
+ end
+ end
+
+ def new
+ kid = Person.new
+ kid.parent = self
+ kid
+ end
+
+end
+
+dad = Person.new
+dad.name = "Jason"
+dad.age = 23
+kid = dad.new
+kid.name = "Rachel"
+kid.age = 2
+puts "Kid's parent is #{kid.parent.name}"
+puts dad
+puts kid
+
+class Employee < Person
+ def initialize
+ super
+ @ok_fields.push("salary", "boss")
+ end
+ def ok_fields
+ @ok_fields
+ end
+end
+
+
+# @@PLEAC@@_13.13
+# The ruby garbage collector pretends to cope with circular structures.
+# You can test it with this code:
+class RingNode
+ attr_accessor :next
+ attr_accessor :prev
+ attr_reader :name
+
+ def initialize(aName)
+ @name = aName
+ ObjectSpace.define_finalizer(self,
+ self.class.method(:finalize).to_proc)
+ end
+
+ def RingNode.finalize(id)
+ puts "Node #{id} dying"
+ end
+
+ def RingNode.show_all_objects
+ ObjectSpace.each_object {|id|
+ puts id.name if id.class == RingNode
+ }
+ end
+end
+
+def create_test
+ a = RingNode.new("Node A")
+ b = RingNode.new("Node B")
+ c = RingNode.new("Node C")
+ a.next = b
+ b.next = c
+ c.next = a
+ a.prev = c
+ c.prev = b
+ b.prev = a
+
+ a = nil
+ b = nil
+ c = nil
+end
+
+create_test
+RingNode.show_all_objects
+ObjectSpace.garbage_collect
+puts "After garbage collection"
+RingNode.show_all_objects
+
+
+# @@PLEAC@@_13.14
+class String
+ def <=>(other)
+ self.casecmp other
+ end
+end
+
+# There is no way to directly overload the '""' (stringify)
+# operator in Ruby. However, by convention, classes which
+# can reasonably be converted to a String will define a
+# 'to_s' method as in the TimeNumber class defined below.
+# The 'puts' method will automatcally call an object's
+# 'to_s' method as is demonstrated below.
+# Furthermore, if a class defines a to_str method, an object of that
+# class can be used most any place where the interpreter is looking
+# for a String value.
+
+#---------------------------------------
+# NOTE: Ruby has a builtin Time class which would usually be used
+# to manipulate time objects, the following is supplied for
+# educational purposes to demonstrate operator overloading.
+#
+class TimeNumber
+ attr_accessor :hours,:minutes,:seconds
+ def initialize( hours, minutes, seconds)
+ @hours = hours
+ @minutes = minutes
+ @seconds = seconds
+ end
+
+ def to_s
+ return sprintf( "%d:%02d:%02d", @hours, @minutes, @seconds)
+ end
+
+ def to_str
+ to_s
+ end
+
+ def +( other)
+ seconds = @seconds + other.seconds
+ minutes = @minutes + other.minutes
+ hours = @hours + other.hours
+ if seconds >= 60
+ seconds %= 60
+ minutes += 1
+ end
+ if minutes >= 60
+ minutes %= 60
+ hours += 1
+ end
+ return TimeNumber.new(hours, minutes, seconds)
+ end
+
+ def -(other)
+ raise NotImplementedError
+ end
+
+ def *(other)
+ raise NotImplementedError
+ end
+
+ def /( other)
+ raise NotImplementedError
+ end
+end
+
+t1 = TimeNumber.new(0, 58, 59)
+sec = TimeNumber.new(0, 0, 1)
+min = TimeNumber.new(0, 1, 0)
+puts t1 + sec + min + min
+
+#-----------------------------
+# StrNum class example: Ruby's builtin String class already has the
+# capabilities outlined in StrNum Perl example, however the '*' operator
+# on Ruby's String class acts differently: It creates a string which
+# is the original string repeated N times.
+#
+# Using Ruby's String class as is in this example:
+x = "Red"; y = "Black"
+z = x+y
+r = z*3 # r is "RedBlackRedBlackRedBlack"
+puts "values are #{x}, #{y}, #{z}, and #{r}"
+print "#{x} is ", x < y ? "LT" : "GE", " #{y}\n"
+# prints:
+# values are Red, Black, RedBlack, and RedBlackRedBlackRedBlack
+# Red is GE Black
+
+#-----------------------------
+class FixNum
+ REGEX = /(\.\d*)/
+ DEFAULT_PLACES = 0
+ attr_accessor :value, :places
+ def initialize(value, places = nil)
+ @value = value
+ if places
+ @places = places
+ else
+ m = REGEX.match(value.to_s)
+ if m
+ @places = m[0].length - 1
+ else
+ @places = DEFAULT_PLACES
+ end
+ end
+ end
+
+ def +(other)
+ FixNum.new(@value + other.value, max(@places, other.places))
+ end
+
+ def *(other)
+ FixNum.new(@value * other.value, max(@places, other.places))
+ end
+
+ def /(other)
+ puts "Divide: #{@value.to_f/other.value.to_f}"
+ result = FixNum.new(@value.to_f/other.value.to_f)
+ result.places = max(result.places,other.places)
+ result
+ end
+
+ def to_s
+ sprintf("STR%s: %.*f", self.class.to_s , @places, @value) #.
+ end
+
+ def to_str
+ to_s
+ end
+
+ def to_i #convert to int
+ @value.to_i
+ end
+
+ def to_f #convert to float`
+ @value.to_f
+ end
+
+ private
+ def max(a,b)
+ a > b ? a : b
+ end
+end
+
+def demo()
+ x = FixNum.new(40)
+ y = FixNum.new(12, 0)
+
+ puts "sum of #{x} and #{y} is #{x+y}"
+ puts "product of #{x} and #{y} is #{x*y}"
+
+ z = x/y
+ puts "#{z} has #{z.places} places"
+ unless z.places
+ z.places = 2
+ end
+
+ puts "div of #{x} by #{y} is #{z}"
+ puts "square of that is #{z*z}"
+end
+
+if __FILE__ == $0
+ demo()
+end
+
+
+# @@PLEAC@@_14.1
+# There are dbm, sdbm, gdbm modules
+# and the bdb module for accessing the berkeley db
+# sdbm seem to be available on the most systems,
+# so we use it here
+#
+require "sdbm"
+SDBM.open("filename", 0666) { |dbobj|
+ # raises exception if open error
+
+ # the returned sdbm-dbobj has most of the methods of a hash
+ v = dbobj["key"]
+ dbobj["key"] = "newvalue"
+ if dbobj.has_key?("key")
+ # ...
+ end
+ dbobj.delete("key2")
+}
+# database is open only inside the block.
+
+# It is also possible to use a open .. close pair:
+dbobj = SDBM.open("filename", 0666)
+#.. do something with dbobj
+dbobj.close
+
+#!/usr/bin/ruby -w
+# userstats - generate statistics on who is logged in
+# call with usernames as argument to display the totals
+# for the given usernames, call with "ALL" to display all users
+
+require "sdbm"
+filename = '/tmp/userstats.db'
+SDBM.open(filename, 0666) { |dbobj|
+ if ARGV.length > 0
+ if ARGV[0] == "ALL"
+ # ARGV is constant, so we need the variable userlist
+ userlist = dbobj.keys().sort()
+ else
+ userlist = ARGV
+ end
+ userlist.each { |user|
+ print "#{user}\t#{dbobj[user]}\n"
+ }
+ else
+ who = `who`
+ who.split("\n").each { |line|
+ md = /^(\S+)/.match(line)
+ raise "Bad line from who: #{line}" unless md
+ # sdbm stores only strings, so "+=" doesn't work,
+ # we need to convert them expicitly back to integer.
+ if dbobj.has_key?(md[0])
+ dbobj[md[0]] = dbobj[md[0]].to_i + 1
+ else
+ dbobj[md[0]] = "1"
+ end
+ }
+ end
+}
+
+
+# @@PLEAC@@_14.2
+# using open and clear
+dbobj = SDBM.open("filename", 0666)
+dbobj.clear()
+dbobj.close()
+# deleting file and recreating it
+# the filenames depend on the flavor of dbm you use,
+# for example sdbm has two files named filename.pag and filename.dir,
+# so you need to delete both files
+begin
+ File.delete("filename")
+ # raises Exception if not exist
+ dbobj = SDBM.open("filename", 0666)
+rescue
+ # add error handling here
+end
+
+
+# @@PLEAC@@_14.3
+# sdbm2gdbm: converts sdbm database to a gdbm database
+require "sdbm"
+require "gdbm"
+
+unless ARGV.length == 2
+ fail "usage: sdbm2gdbm infile outfile"
+end
+infile = ARGV[0]
+outfile = ARGV[1]
+
+sdb = SDBM.open(infile)
+gdb = GDBM.open(outfile, 0666)
+sdb.each { |key, val|
+ gdb[key] = val
+}
+gdb.close
+sdb.close
+
+
+# @@PLEAC@@_14.4
+#!/usr/bin/ruby -w
+# dbmmerge: merges two dbm databases
+require "sdbm"
+
+unless ARGV.length == 3
+ fail "usage: dbmmerge indb1 indb2 outdb"
+end
+infile1 = ARGV[0]
+infile2 = ARGV[0]
+outfile = ARGV[2]
+
+in1 = SDBM.open(infile1, nil)
+in2 = SDBM.open(infile2, nil)
+outdb = SDBM.open(outfile, 0666)
+
+[in1, in2].each { |indb|
+ indb.each { |key, val|
+ if outdb.has_key?(key)
+ # decide which value to set.
+ # set outdb[key] if necessary
+ else
+ outdb[key] = val
+ end
+ }
+}
+in1.close
+in2.close
+outdb.close
+
+
+# @@PLEAC@@_14.7
+# we write a tie method that extends the Array class.
+# It reads the file into the memory, executes the code block
+# in which you can manipulate the array as needed, and writes
+# the array back to the file after the end of the block execution
+class Array
+ def tie(filename, flags)
+ File.open(filename, flags) { |f|
+ f.each_line { |line|
+ self.push(line.chomp)
+ }
+ yield
+ f.rewind
+ each { |line|
+ if line
+ f.puts(line)
+ else
+ f.puts ""
+ end
+ }
+ }
+ end
+end
+
+array = Array.new
+array.tie("/tmp/textfile.txt", File::RDWR|File::CREAT) {
+ array[4] = "a new line 4"
+}
+
+# The tied array can be manipulated like a normal array,
+# so there is no need for a special API, and the recno_demo program
+# to demonstrate is API is useless
+
+
+# tied array demo: show how to use array with a tied file
+filename = "db_file.txt"
+lines = Array.new
+File.unlink(filename) if File.exists?(filename)
+lines.tie(filename, File::RDWR | File::CREAT) {
+ # first create a textfile to play with
+ lines[0] = "zero"
+ lines[1] = "one"
+ lines[2] = "two"
+ lines[3] = "three"
+ lines[4] = "four"
+
+ # print the records in order.
+ # Opposed to perl, the tied array behaves exactly as a normal array
+ puts "\nOriginal"
+ for i in 0..(lines.length-1)
+ puts "#{i}: #{lines[i]}"
+ end
+
+ #use push and pop
+ a = lines.pop
+ lines.push("last")
+ puts("The last line was [#{a}]")
+
+ #use shift and unshift
+ a = lines.shift
+ lines.unshift("first")
+ puts("The first line was [#{a}]")
+
+ # add record after record 2
+ i = 2
+ lines.insert(i + 1, "Newbie")
+
+ # add record before record one
+ i = 1
+ lines.insert(i, "New One")
+
+ # delete record 3
+ lines.delete_at(3)
+
+ #now print the records in reverse order
+ puts "\nReverse"
+ (lines.length - 1).downto(0){ |i|
+ puts "#{i}: #{lines[i]}"
+ }
+
+}
+
+
+# @@PLEAC@@_14.8
+# example to store complex data in a database
+# uses marshall from the standard library
+require "sdbm"
+db = SDBM.open("pleac14-8-database", 0666)
+
+# convert the Objects into strings and back by using the Marshal module.
+# Most normal objects can be converted out of the box,
+# but not special things like procedure objects,
+# IO instance variables, singleton objects
+
+db["Tom Christiansen"] = Marshal.dump(["book author", "tchrist@perl.com"])
+db["Tom Boutell"] = Marshal.dump(["shareware author",
+"boutell@boutell.com"])
+
+name1 = "Tom Christiansen"
+name2 = "Tom Boutell"
+
+tom1 = Marshal.load(db[name1])
+tom2 = Marshal.load(db[name2])
+
+puts "Two Toming: #{tom1} #{tom2}"
+
+if tom1[0] == tom2[0] && tom1[1] == tom2[1]
+ puts "You're having runtime fun with one Tom made two."
+else
+ puts "No two Toms are ever alike"
+end
+
+# To change parts of an entry, get the whole entry, change the parts,
+# and save the whole entry back
+entry = Marshal.load(db["Tom Boutell"])
+entry[0] = "Poet Programmer"
+db["Tom Boutell"] = Marshal.dump(entry)
+db.close
+
+
+# @@PLEAC@@_14.9
+# example to make data persistent
+# uses Marshal from the standard lib
+# Stores the data in a simple file,
+# see 14.8 on how to store it in a dbm file
+
+# The BEGIN block is executed before the rest of the script
+# we use global variables here because local variables
+# will go out of scope and are not accessible from the main script
+
+BEGIN {
+ $persistent_store = "persitence.dat"
+ begin
+ File.open($persistent_store) do |f|
+ $stringvariable1 = Marshal.load(f)
+ $arrayvariable2 = Marshal.load(f)
+ end
+ rescue
+ puts "Can not open #{$persistent_store}"
+ # Initialisation if this script runs the first time
+ $stringvariable1 = ""
+ $arrayvariable2 = []
+ end
+}
+
+END {
+ File.open($persistent_store, "w+") do |f|
+ Marshal.dump($stringvariable1, f)
+ Marshal.dump($arrayvariable2, f)
+ end
+}
+
+# simple test program
+puts $stringvariable1
+puts $arrayvariable2
+$stringvariable1 = "Hello World"
+$arrayvariable2.push(5)
+puts $stringvariable1
+puts $arrayvariable2
+
+
+# @@PLEAC@@_14.10
+#!/usr/bin/ruby -w
+# Ruby has a dbi module with an architecture similar
+# to the Perl dbi module: the dbi module provides an unified
+# interface and uses specialized drivers for each dbms vendor
+#
+begin
+ DBI.connect("DBI:driver:driverspecific", "username", "auth") {
+ |dbh|
+
+ dbh.do(SQL1)
+
+ dbh.prepare(SQL2){ |sth|
+ sth.execute
+ sth.fetch {|row|
+ # ...
+ }
+ } # end of block finishes the statement handle
+ } # end of block closes the database connection
+rescue DBI::DatabaseError => e
+ puts "dbi error occurred"
+ puts "Error code: #{e.err}"
+ puts "Error message: #{e.errstr}"
+end
+
+#!/usr/bin/ruby -w
+# dbusers - example for mysql which creates a table,
+# fills it with values, retrieves the values back,
+# and finally destroys the table.
+
+require "dbi"
+
+# replacement for the User::pwnt module
+def getpwent
+ result = []
+ File.open("/etc/passwd") {|file|
+ file.each_line {|line|
+ next if line.match(/^#/)
+ cols = line.split(":")
+ result.push([cols[2], cols[0]])
+ }
+ }
+ result
+end
+
+begin
+ DBI.connect("DBI:Mysql:pleacdatabase", "pleac", "pleacpassword") {
+ |conn|
+
+ conn.do("CREATE TABLE users (uid INT, login CHAR(8))")
+
+ users = getpwent
+
+ conn.prepare("INSERT INTO users VALUES (?,?)") {|sth|
+ users.each {|entry|
+ sth.execute(entry[0], entry[1])
+ }
+ }
+
+ conn.execute("SELECT uid, login FROM users WHERE uid < 50") {|sth|
+ sth.fetch {|row|
+ puts row.collect {|col|
+ if col.nil?
+ "(null)"
+ else
+ col
+ end
+ }.join(", ")
+ }
+ }
+
+ conn.do("DROP TABLE users")
+ }
+rescue DBI::DatabaseError => e
+ puts "dbi error occurred"
+ puts "Error code: #{e.err}"
+ puts "Error message: #{e.errstr}"
+end
+
+
+# @@PLEAC@@_15.1
+# This test program demonstrates parsing program arguments.
+# It uses the optparse library, which is included with ruby 1.8
+# It handles classic unix style and gnu style options
+require 'optparse'
+
+@debugmode = false
+@verbose = false
+
+ARGV.options do |opts|
+ opts.banner = "Usage: ruby #{$0} [OPTIONS] INPUTFILES"
+
+ opts.on("-h", "--help", "show this message") {
+ puts opts
+ exit
+ }
+ # The OptionParser#on method is called with a specification of short
+ # options, of long options, a data type spezification and user help
+ # messages for this option.
+ # The method analyses the given parameter and decides what it is,
+ # so you can leave out the long option if you don't need it
+ opts.on("-v", "--[no-]verbose=[FLAG]", TrueClass, "run verbosly") {
+ |@verbose| # sets @verbose to true or false
+ }
+ opts.on("-D", "--DEBUG", TrueClass, "turns on debug mode" ){
+ |@debugmode| # sets @debugmode to true
+ }
+ opts.on("-c", "--count=NUMBER", Integer, "how many times we do it" ){
+ |@count| # sets @count to given integer
+ }
+ opts.on("-o", "--output=FILE", String, "file to write output to"){
+ |@outputfile| # sets @outputfile to given string
+ }
+ opts.parse!
+end
+
+# example to use the options in the main program
+puts "Verbose is on" if @verbose
+puts "Debugmode is on" if @debugmode
+puts "Outfile is #{@outputfile}" if defined? @outputfile
+puts "Count is #{@count}" if defined? @count
+ARGV.each { |param|
+ puts "Got parameter #{param}"
+}
+
+
+# @@PLEAC@@_15.4
+buf = "\0" * 8
+$stdout.ioctl(0x5413, buf)
+ws_row, ws_col, ws_xpixel, ws_ypixel = buf.unpack("S4")
+
+raise "You must have at least 20 characters" unless ws_col >= 20
+max = 0
+values = (1..5).collect { rand(20) } # generate an array[5] of rand values
+for i in values
+ max = i if max < i
+end
+ratio = Float(ws_col-12)/max # chars per unit
+for i in values
+ printf "%8.1f %s\n", i, "*" * (ratio*i)
+end
+
+# gives, for example:
+# 15.0 *******************************
+# 10.0 *********************
+# 5.0 **********
+# 14.0 *****************************
+# 18.0 **************************************
+
+
+# @@PLEAC@@_16.1
+output = `program args` # collect output into one multiline string
+output = `program args`.split # collect output into array, one line per
+element
+
+readme = IO.popen("ls")
+output = ""
+while readme.gets do
+ output += $_
+end
+readme.close
+
+`fsck -y /dev/rsd1a` # BAD AND SCARY in Perl because it's managed by the shell
+ # I donna in Ruby ...
+
+# so the "clean and secure" version
+readme, writeme = IO.pipe
+pid = fork {
+ # child
+ $stdout = writeme
+ readme.close
+ exec('find', '..')
+}
+# parent
+Process.waitpid(pid, 0)
+writeme.close
+while readme.gets do
+ # do something with $_
+end
+
+
+# @@PLEAC@@_16.2
+status = system("xemacs #{myfile}")
+
+status = system("xemacs", myfile)
+
+system("cmd1 args | cmd2 | cmd3 >outfile")
+system("cmd args <infile >outfile 2>errfile")
+
+# stop if the command fails
+raise "$program exited funny: #{$?}" unless system("cmd", "args1", "args2")
+
+# get the value of the signal sent to the child
+# even if it is a SIGINT or SIGQUIT
+system(arglist)
+raise "program killed by signal #{$?}" if ($? & 127) != 0
+
+pid = fork {
+ trap("SIGINT", "IGNORE")
+ exec("sleep", "10")
+}
+trap ("SIGINT") {
+ puts "Tsk tsk, no process interruptus"
+}
+Process.waitpid(pid, 0)
+
+# Ruby doesn't permit to lie to the program called by a 'system'.
+# (ie specify what return argv[0] in C, $0 in Perl/Ruby ...)
+# A (dirty) way is to create a link (under Unix), run this link and
+# erase it. Somebody has a best idea ?
+
+
+# @@PLEAC@@_16.3
+exec("archive *.data")
+
+exec("archive", "accounting.data")
+
+exec("archive accounting.data")
+
+
+# @@PLEAC@@_16.4
+# read the output of a program
+IO.popen("ls") {|readme|
+ while readme.gets do
+ # ...
+ end
+}
+# or
+readme = IO.popen("ls")
+while readme.gets do
+ # ...
+end
+readme.close
+
+# "write" in a program
+IO.popen("cmd args","w") {|pipe|
+ pipe.puts("data")
+ pipe.puts("foo")
+}
+
+# close wait for the end of the process
+read = IO.popen("sleep 10000") # child goes to sleep
+read.close # and the parent goes to lala land
+
+writeme = IO.popen("cmd args", "w")
+writeme.puts "hello" # program will get hello\n on STDIN
+writeme.close # program will get EOF on STDIN
+
+# send in a pager (eg less) all output
+$stdout = IO.popen("/usr/bin/less","w")
+print "huge string\n" * 10000
+
+
+# @@PLEAC@@_16.5
+#-----------------------------
+def head(lines = 20)
+ pid = open("|-","w")
+ if pid == nil
+ return
+ else
+ while gets() do
+ pid.print
+ lines -= 1
+ break if lines == 0
+ end
+ end
+ exit
+end
+
+head(100)
+while gets() do
+ print
+end
+#-----------------------------
+1: > Welcome to Linux, version 2.0.33 on a i686
+
+2: >
+
+3: > "The software required `Windows 95 or better',
+
+4: > so I installed Linux."
+#-----------------------------
+> 1: Welcome to Linux, Kernel version 2.0.33 on a i686
+
+> 2:
+
+> 3: "The software required `Windows 95 or better',
+
+> 4: so I installed Linux."
+#-----------------------------
+#!/usr/bin/ruby
+# qnumcat - demo additive output filters
+
+def number()
+ pid = open("|-","w")
+ if pid == nil
+ return
+ else
+ while gets() do pid.printf("%d: %s", $., $_); end
+ end
+ exit
+end
+
+def quote()
+ pid = open("|-","w")
+ if pid == nil
+ return
+ else
+ while gets() do pid.print "> #{$_}" end
+ end
+ exit
+end
+
+number()
+quote()
+
+while gets() do
+ print
+end
+$stdout.close
+exit
+
+
+# @@PLEAC@@_16.6
+ARGV.map! { |arg|
+ arg =~ /\.(gz|Z)$/ ? "|gzip -dc #{arg}" : arg
+}
+for file in ARGV
+ fh = open(file)
+ while fh.gets() do
+ # .......
+ end
+end
+#-----------------------------
+ARGV.map! { |arg|
+ arg =~ %r#^\w+://# ? "|GET #{arg}" : arg #
+}
+for file in ARGV
+ fh = open(file)
+ while fh.gets() do
+ # .......
+ end
+end
+#-----------------------------
+pwdinfo = (`domainname` =~ /^(\(none\))?$/) ? '/etc/passwd' : '|ypcat passwd';
+pwd = open(pwdinfo);
+#-----------------------------
+puts "File, please? ";
+file = gets().chomp();
+fh = open(file);
+
+
+# @@PLEAC@@_16.7
+output = `cmd 2>&1` # with backticks
+# or
+ph = open("|cmd 2>&1") # with an open pipe
+while ph.gets() { } # plus a read
+#-----------------------------
+output = `cmd 2>/dev/null` # with backticks
+# or
+ph = open("|cmd 2>/dev/null") # with an open pipe
+while ph.gets() { } # plus a read
+#-----------------------------
+output = `cmd 2>&1 1>/dev/null` # with backticks
+# or
+ph = open("|cmd 2>&1 1>/dev/null") # with an open pipe
+while ph.gets() { } # plus a read
+#-----------------------------
+output = `cmd 3>&1 1>&2 2>&3 3>&-` # with backticks
+# or
+ph = open("|cmd 3>&1 1>&2 2>&3 3>&-") # with an open pipe
+while ph.gets() { } # plus a read
+#-----------------------------
+system("program args 1>/tmp/program.stdout 2>/tmp/program.stderr")
+#-----------------------------
+output = `cmd 3>&1 1>&2 2>&3 3>&-`
+#-----------------------------
+fd3 = fd1
+fd1 = fd2
+fd2 = fd3
+fd3 = nil
+#-----------------------------
+system("prog args 1>tmpfile 2>&1")
+system("prog args 2>&1 1>tmpfile")
+#-----------------------------
+# system ("prog args 1>tmpfile 2>&1")
+fd1 = "tmpfile" # change stdout destination first
+fd2 = fd1 # now point stderr there, too
+#-----------------------------
+# system("prog args 2>&1 1>tmpfile")
+fd2 = fd1 # stderr same destination as stdout
+fd1 = "tmpfile" # but change stdout destination
+#-----------------------------
+# It is often better not to rely on the shell,
+# because of portability, possible security problems
+# and bigger resource usage. So, it is often better to use the open3 library.
+# See below for an example.
+# opening stdin, stdout, stderr
+require "open3"
+stdin, stdout, stderr = Open3.popen('cmd')
+
+
+# @@PLEAC@@_16.8
+#-----------------------------
+# Contrary to perl, we don't need to use a module in Ruby
+fh = Kernel.open("|" + program, "w+")
+fh.puts "here's your input\n"
+output = fh.gets()
+fh.close()
+#-----------------------------
+Kernel.open("|program"),"w+") # RIGHT !
+#-----------------------------
+# Ruby has already object methods for I/O handles
+#-----------------------------
+begin
+ fh = Kernel.open("|" + program_and_options, "w+")
+rescue
+ if ($@ ~= /^open/)
+ $stderr.puts "open failed : #{$!} \n #{$@} \n"
+ break
+ end
+ raise # reraise unforseen exception
+end
+
+
+# @@PLEAC@@_16.13
+#% kill -l
+#HUP INT QUIT ILL TRAP ABRT BUS FPE KILL USR1 SEGV USR2 PIPE
+#ALRM TERM CHLD CONT STOP TSTP TTIN TTOU URG XCPU XFSZ VTALRM
+#PROF WINCH POLL PWR
+#-----------------------------
+#% ruby -e 'puts Signal.list.keys.join(" ")'
+#PWR USR1 BUS USR2 TERM SEGV KILL POLL STOP SYS TRAP IOT HUP INT #
+#WINCH XCPU TTIN CLD TSTP FPE IO TTOU PROF CHLD CONT PIPE ABRT
+#VTALRM QUIT ILL XFSZ URG ALRM
+#-----------------------------
+# After that, the perl script create an hash equivalent to Signal.list,
+# and an array. The array can be obtained by :
+signame = []
+Signal.list.each { |name, i| signame[i] = name }
+
+
+# @@PLEAC@@_16.14
+Process.kill(9, pid) # send $pid a signal 9
+Process.kill(-1, Process.getpgrp()) # send whole job a signal 1
+Process.kill("USR1", $$) # send myself a SIGUSR1
+Process.kill("HUP", pid1, pid2, pid3) # send a SIGHUP to processes in @pids
+#-----------------------------
+begin
+ Process.kill(0, minion)
+ puts "#{minion} is alive!"
+rescue Errno::EPERM # changed uid
+ puts "#{minion} has escaped my control!";
+rescue Errno::ESRCH
+ puts "#{minion} is deceased."; # or zombied
+rescue
+ puts "Odd; I couldn't check the status of #{minion} : #{$!}"
+end
+
+
+# @@PLEAC@@_16.15
+Kernel.trap("QUIT", got_sig_quit) # got_sig_quit = Proc.new { puts "Quit\n" }
+trap("PIPE", "got_sig_quit") # def got_sig_pipe ...
+trap("INT") { ouch++ } # increment ouch for every SIGINT
+#-----------------------------
+trap("INT", "IGNORE") # ignore the signal INT
+#-----------------------------
+trap("STOP", "DEFAULT") # restore default STOP signal handling
+
+
+# @@PLEAC@@_16.16
+# the signal handler
+def ding
+ trap("INT", "ding")
+ puts "\aEnter your name!"
+end
+
+# prompt for name, overriding SIGINT
+def get_name
+ save = trap("INT", "ding")
+
+ puts "Kindly Stranger, please enter your name: "
+ name = gets().chomp()
+ trap("INT", save)
+ name
+end
+
+
+# @@PLEAC@@_16.21
+# implemented thanks to http://blade.nagaokaut.ac.jp/cgi-bin/scat.rb/ruby/ruby-talk/1760
+require 'timeout'
+
+# we'll do something vastly more useful than cookbook to demonstrate timeouts
+begin
+ timeout(5) {
+ waitsec = rand(10)
+ puts "Let's see if a sleep of #{waitsec} seconds is longer than 5 seconds..."
+ system("sleep #{waitsec}")
+ }
+ puts "Timeout didn't occur"
+rescue Timeout::Error
+ puts "Timed out!"
+end
+
+
+# @@PLEAC@@_17.1
+# A basic TCP client connection
+require 'socket'
+begin
+ t = TCPSocket.new('www.ruby-lang.org', 'www')
+rescue
+ puts "error: #{$!}"
+else
+ # ... do something with the socket
+ t.print "GET / HTTP/1.0\n\n"
+ answer = t.gets(nil)
+ # and terminate the connection when we're done
+ t.close
+end
+
+# Using the evil low level socket API
+require 'socket'
+# create a socket
+s = Socket.new(Socket::AF_INET, Socket::SOCK_STREAM, 0)
+# build the address of the remote machine
+sockaddr_server = [Socket::AF_INET, 80,
+ Socket.gethostbyname('www.ruby-lang.org')[3],
+ 0, 0].pack("snA4NN")
+# connect
+begin
+ s.connect(sockaddr_server)
+rescue
+ puts "error: #{$!}"
+else
+ # ... do something with the socket
+ s.print "GET / HTTP/1.0\n\n"
+ # and terminate the connection when we're done
+ s.close
+end
+
+# TCP connection with management of error (DNS)
+require 'socket'
+begin
+ client = TCPSocket.new('does not exists', 'www')
+rescue
+ puts "error: #{$!}"
+end
+
+# TCP connection with a time out
+require 'socket'
+require 'timeout'
+begin
+ timeout(1) do #the server has one second to answer
+ client = TCPSocket.new('www.host.com', 'www')
+ end
+rescue
+ puts "error: #{$!}"
+end
+
+
+# @@PLEAC@@_17.12
+require 'socket'
+
+class Preforker
+ attr_reader (:child_count)
+
+ def initialize(prefork, max_clients_per_child, port, client_handler)
+ @prefork = prefork
+ @max_clients_per_child = max_clients_per_child
+ @port = port
+ @child_count = 0
+
+ @reaper = proc {
+ trap('CHLD', @reaper)
+ pid = Process.wait
+ @child_count -= 1
+ }
+
+ @huntsman = proc {
+ trap('CHLD', 'IGNORE')
+ trap('INT', 'IGNORE')
+ Process.kill('INT', 0)
+ exit
+ }
+
+ @client_handler=client_handler
+ end
+
+ def child_handler
+ trap('INT', 'EXIT')
+ @client_handler.setUp
+ # wish: sigprocmask UNblock SIGINT
+ @max_clients_per_child.times {
+ client = @server.accept or break
+ @client_handler.handle_request(client)
+ client.close
+ }
+ @client_handler.tearDown
+ end
+
+ def make_new_child
+ # wish: sigprocmask block SIGINT
+ @child_count += 1
+ pid = fork do
+ child_handler
+ end
+ # wish: sigprocmask UNblock SIGINT
+ end
+
+ def run
+ @server = TCPserver.open(@port)
+ trap('CHLD', @reaper)
+ trap('INT', @huntsman)
+ loop {
+ (@prefork - @child_count).times { |i|
+ make_new_child
+ }
+ sleep .1
+ }
+ end
+end
+
+#-----------------------------
+#!/usr/bin/ruby
+
+require 'Preforker'
+
+class ClientHandler
+ def setUp
+ end
+
+ def tearDown
+ end
+
+ def handle_request(client)
+ # do stuff
+ end
+end
+
+server = Preforker.new(1, 100, 3102, ClientHandler.new)
+server.run
+
+
+# @@PLEAC@@_18.2
+require 'net/ftp'
+
+begin
+ ftp = Net::FTP::new("ftp.host.com")
+ ftp.login(username,password)
+ ftp.chdir(directory)
+ ftp.get(filename)
+ ftp.put(filename)
+rescue Net::FTPError
+ $stderr.print "FTP failed: " + $!
+ensure
+ ftp.close() if ftp
+end
+
+# A better solution for a local use could be :
+Net::FTP::new("ftp.host.com") do |ftp|
+ ftp.login(username,password)
+ ftp.chdir(directory)
+ ftp.get(filename)
+ ftp.put(filename)
+end
+
+# If you have only one file to get, there is a simple solution :
+require 'open-uri'
+open("ftp://www.ruby-lang.org/path/filename") do |fh|
+ # read from filehandle fh
+end
+#--------------------------------------------
+# to wait a defined time for the connection,
+# use the timeout module
+require 'timeout'
+begin
+ timeout(30){
+ ftp = Net::FTP::new("ftp.host.com")
+ ftp.debug_mode = true
+ }
+rescue Net::FTPError
+ $stderr.puts "Couldn't connect."
+rescue Timeout::Error
+ $stderr.puts "Timeout while connecting to server."
+end
+
+begin
+ ftp.login()
+rescue Net::FTPError
+ $stderr.print "Couldn't authentificate.\n"
+end
+
+begin
+ ftp.login(username)
+rescue Net::FTPError
+ $stderr.print "Still couldn't authenticate.\n"
+end
+
+begin
+ ftp.login(username, password)
+rescue Net::FTPError
+ $stderr.print "Couldn't authenticate, even with explicit
+ username and password.\n"
+end
+
+begin
+ ftp.login(username, password, account)
+rescue Net::FTPError
+ $stderr.print "No dice. It hates me.\n"
+end
+#-----------------------------
+ftp.put(localfile, remotefile)
+#-----------------------------
+# Sending data from STDIN is not directly supported
+# by the ftp library module. A possible way to do it is to use the
+# storlines method directly to send raw commands to the ftp server.
+#-----------------------------
+ftp.get(remotefile, localfile)
+#-----------------------------
+ftp.get(remotefile) { |data| puts data }
+#-----------------------------
+ftp.chdir("/pub/ruby")
+print "I'm in the directory ", ftp.pwd(), "\n"
+#-----------------------------
+ftp.mkdir("/pub/ruby/new_dir")
+#-----------------------------
+lines = ftp.ls("/pub/ruby/")
+# => ["drwxr-xr-x 2 matz users 4096 July 17 1998 1.0", ... ]
+
+latest = ftp.dir("/pub/ruby/*.tgz").sort.last
+
+ftp.nlst("/pub/ruby")
+# => ["/pub/ruby/1.0", ... ]
+#-----------------------------
+ftp.quit()
+
+
+# @@PLEAC@@_18.6
+require 'net/telnet'
+t = Net::Telnet::new( "Timeout" => 10,
+ "Prompt" => /%/,
+ "Host" => host )
+t.login(username, password)
+files = t.cmd("ls")
+t.print("top")
+process_string = t.waitfor(/\d+ processes/)
+t.close
+#-----------------------------
+/[$%#>] \z/n
+#-----------------------------
+# In case of an error, the telnet module throws an exception.
+# For control of the behavior in case of an error,
+# you just need to catch the exceptions and do your custom
+# error handling.
+#-----------------------------
+begin
+ telnet.login(username, password)
+rescue TimeoutError
+ fail "Login failed !\n"
+end
+#-----------------------------
+telnet.waitfor('/--more--/')
+#-----------------------------
+telnet.waitfor(String => 'greasy smoke', Timeout => 30)
+
+
+# @@PLEAC@@_18.7
+require 'ping'
+
+puts "#{host} is alive.\n" if Ping.pingecho(host);
+#-----------------------------
+# the ping module only use TCP ping, not ICMP even if we are root
+if Ping.pingecho("kingkong.com")
+ puts "The giant ape lives!\n";
+else
+ puts "All hail mighty Gamera, friend of children!\n";
+end
+
+
+# @@PLEAC@@_19.0
+#-----------------------------
+# http://www.perl.com/CPAN/
+# http://www.perl.com:8001/bad/mojo.html
+# ftp://gatekeeper.dec.com/pub/misc/netlib.tar.Z
+# ftp://anonymous@myplace:gatekeeper.dec.com/pub/misc/netlib.tar.Z
+# file:///etc/motd
+#-----------------------------
+# http://mox.perl.com/cgi-bin/program?name=Johann&born=1685
+#-----------------------------
+# http://mox.perl.com/cgi-bin/program
+#-----------------------------
+
+
+# @@PLEAC@@_19.1
+#!/usr/local/bin/ruby -w
+# hiweb - load CGI class to decode information given by web server
+
+require 'cgi'
+
+cgi = CGI.new('html3')
+
+# get a parameter from a form
+value = cgi.params['PARAM_NAME'][0]
+
+# output a document
+cgi.out {
+ cgi.html {
+ cgi.head { cgi.title { "Howdy there!" } } +
+ cgi.body { cgi.p { "You typed: " + cgi.tt {
+ CGI.escapeHTML(value) } } }
+ }
+}
+
+require 'cgi'
+cgi = CGI.new
+who = cgi.param["Name"][0] # first param in list
+phone = cgi.param["Number"][0]
+picks = cgi.param["Choices"] # complete list
+
+print cgi.header( 'type' => 'text/plain',
+ 'expires' => Time.now + (3 * 24 * 60 * 60) )
+
+
+# @@PLEAC@@_19.3
+#!/usr/local/bin/ruby -w
+# webwhoami - show web user's id
+require 'etc'
+print "Content-Type: text/plain\n\n"
+print "Running as " + Etc.getpwuid.name + "\n"
+
+# % ruby -wc cgi-script # just check syntax
+
+# % ruby -w cgi-script # params from stdin
+# (offline mode: enter name=value pairs on standard input)
+# name=joe
+# number=10
+# ^D
+
+# % ruby -w cgi-script name=joe number=10 # run with mock form input
+# % ruby -d cgi-script name=joe number=10 # ditto, under the debugger
+
+# POST method script in csh
+# % (setenv HTTP_METHOD POST; ruby -w cgi-script name=joe number=10)
+# POST method script in sh
+# % HTTP_METHOD=POST perl -w cgi-script name=joe number=10
+
+
+# @@PLEAC@@_19.4
+# ruby has several security levels, the level "1" is similar to perls taint mode.
+# It can be switched on by providing the -T command line parameter
+# or by setting $SAFE to 1. Setting $SAFE to 2,3 or 4 restricts possible
+# harmful operations further.
+
+#!/usr/bin/ruby -T
+$SAFE = 1
+File.open(ARGV[0], "w")
+# ruby warns with:
+# taint1.rb:2:in `initialize': Insecure operation - initialize (SecurityError)
+
+$SAFE = 1
+file = ARGV[0]
+unless /^([\w.-]+)$/.match(file)
+ raise "filename #{file} has invalid characters"
+end
+file = $1
+# In ruby, even the back reference from a regular expression stays tainted.
+# you need to explicitly untaint the variable:
+file.untaint
+File.open(file, "w")
+
+# Race condition exists like in perl:
+unless File.exists(filename) # Wrong because of race condition
+ File.open(filename, "w")
+end
+
+
+# @@PLEAC@@_19.8
+url = "http://pleac.sourceforge.net/pleac_ruby/"
+print "Location: #{url}\r\n\r\n"
+exit
+
+#!/usr/bin/ruby
+require 'cgi'
+
+cgi = CGI.new
+oreo = CGI::Cookie.new('name' => 'filling',
+ 'value' => 'vanilla creme',
+ 'expires' => Time.now + (3 * 30 * 24 * 60 * 60),
+ 'domain' => '.pleac.sourceforge.net')
+
+whither = 'http://pleac.sourceforge.net/pleac_ruby/cgiprogramming.html'
+
+cgi.out('cookie' => oreo,
+ 'Location' => whither){""}
+
+#!/usr/bin/ruby
+# os_snipe - redirect to a Jargon File entry about current OS
+dir = 'http://www.elsewhere.org/jargon/html/entry'
+
+agent = ENV['HTTP_USER_AGENT']
+
+page = case
+ when agent =~ /Mac/: 'Macintrash.html'
+ when agent =~ /Win(dows )?NT/: 'evil_and_rude.html'
+ when agent =~ /Win|MSIE|WebTV/: 'Microsloth_Windows.html'
+ when agent =~ /Linux/: 'Linux.html'
+ when agent =~ /HP-UX/: 'HP-SUX.html'
+ when agent =~ /SunOS/: 'ScumOS.html'
+ else 'Appendix_B.html'
+end
+
+print "Location: #{dir}/#{page}\n\n"
+
+require 'cgi'
+cgi = CGI.new
+cgi.out('status' => '204 No response'){""}
+# this produces:
+# Status: 204 No response
+# Content-Type: text/html
+# Content-Length: 0
+# <blank line here>
+
+
+# @@PLEAC@@_19.10
+preference_value = cgi.cookies["preference name"][0]
+
+packed_cookie = CGI::Cookie.new("name" => "preference name",
+ "value" => "whatever you'd like",
+ "expires" => Time.local(Time.now.year + 2,
+ Time.now.mon, Time.now.day, Time.now.hour, Time.now.min, Time.now.sec) )
+
+cgi.header("cookie" => [packed_cookie])
+
+#!/usr/local/bin/ruby -w
+# ic_cookies - sample CGI script that uses a cookie
+require 'cgi'
+
+cgi = CGI.new('html3')
+
+cookname = "favorite ice cream"
+favorite = cgi.params["flavor"][0]
+tasty = cgi.cookies[cookname][0] || 'mint'
+
+unless favorite
+ cgi.out {
+ cgi.html {
+ cgi.head { cgi.title { "Ice Cookies" } } +
+ cgi.body {
+ cgi.h1 { "Hello Ice Cream" } +
+ cgi.hr +
+ cgi.form {
+ cgi.p { "Please select a flavor: " +
+ cgi.text_field("flavor", tasty ) }
+ } +
+ cgi.hr
+ }
+ }
+ }
+else
+ cookie = CGI::Cookie.new( "name" => cookname,
+ "value" => favorite,
+ "expires" => Time.local(Time.now.year + 2,
+Time.now.mon, Time.now.day, Time.now.hour, Time.now.min, Time.now.sec) )
+ cgi.out("cookie" => [cookie]) {
+ cgi.html {
+ cgi.head { cgi.title { "Ice Cookies" } } +
+ cgi.body {
+ cgi.h1 { "Hello Ice Cream" } +
+ cgi.p { "You chose as your favorite flavor `#{favorite}'." }
+ }
+ }
+ }
+end
+
+
+# @@PLEAC@@_20.9
+def templatefile(filename, fillings)
+ aFile = File.new(filename, "r")
+ text = aFile.read()
+ aFile.close()
+ pattern = Regexp.new('%%(.*?)%%')
+ text.gsub!(pattern) {
+ fillings[$1] || ""
+ }
+ text
+end
+
+fields = {
+ 'username' => whats_his_name,
+ 'count' => login_count,
+ 'total' => minutes_used
+}
+puts templatefile('simple.template', fields)
+
+# @@INCOMPLETE@@
+# An example using databases is missing
+
+
diff --git a/tests/examplefiles/smarty_example.html b/tests/examplefiles/smarty_example.html
new file mode 100644
index 00000000..2deb65df
--- /dev/null
+++ b/tests/examplefiles/smarty_example.html
@@ -0,0 +1,209 @@
+{php}
+ include "some/php/file.php";
+
+ foreach ($rows as $row) {
+ echo $row;
+ }
+{/php}
+
+{* smarty comment *}
+<!-- ENTRIES START -->
+ {serendipity_hookPlugin hook="entries_header" addData="$entry_id"}
+
+ {foreach from=$entries item="dategroup"}
+ <div class="serendipity_Entry_Date">
+ {if $dategroup.is_sticky}
+ <h3 class="serendipity_date">{$CONST.STICKY_POSTINGS}</h3>
+ {else}
+ <h3 class="serendipity_date">{$dategroup.date|@formatTime:DATE_FORMAT_BARSINISTER}</h3>
+ {/if}
+
+ {foreach from=$dategroup.entries item="entry"}
+ <h4 class="serendipity_title"><a href="{$entry.link}">{$entry.title}</a></h4>
+
+ <div class="serendipity_entry serendipity_entry_author_{$entry.author|@makeFilename} {if $entry.is_entry_owner}serendipity_entry_author_self{/if}">
+ {if $entry.categories}
+ <span class="serendipity_entryIcon">
+ {foreach from=$entry.categories item="category"}
+ {if $category.category_icon}
+ <a href="{$category.category_link}"><img class="serendipity_entryIcon" title="{$category.category_name|@escape}{$category.category_description|@emptyPrefix}" alt="{$category.category_name|@escape}" src="{$category.category_icon}" /></a>
+ {/if}
+ {/foreach}
+ </span>
+ {/if}
+
+ <div class="serendipity_entry_body">
+ {$entry.body}
+ </div>
+
+ {if $entry.is_extended}
+ <div class="serendipity_entry_extended"><a id="extended"></a>{$entry.extended}</div>
+ {/if}
+
+ {if $entry.has_extended and not $is_single_entry and not $entry.is_extended}<br />
+ <p class="continue_reading"><a href="{$entry.link}#extended">{$CONST.VIEW_EXTENDED_ENTRY|@sprintf:$entry.title}</a></p><br />
+ {/if}
+
+ <div class='serendipity_entryFooter'>
+ {$CONST.POSTED_BY} <a href="{$entry.link_author}">{$entry.author}</a>
+ {if $entry.categories}
+ {$CONST.IN} {foreach from=$entry.categories item="category" name="categories"}<a href="{$category.category_link}">{$category.category_name|@escape}</a>{if not $smarty.foreach.categories.last}, {/if}{/foreach}
+ {/if}
+
+ {if $entry.has_comments}
+ {if $use_popups}
+ | <a href="{$entry.link_popup_comments}" onclick="window.open(this.href, 'comments', 'width=480,height=480,scrollbars=yes'); return false;">{$entry.label_comments} ({$entry.comments})</a>
+ {else}
+ | <a href="{$entry.link}#comments">{$entry.label_comments} ({$entry.comments})</a>
+ {/if}
+ {/if}
+
+ {if $entry.has_trackbacks}
+ {if $use_popups}
+ | <a href="{$entry.link_popup_trackbacks}" onclick="window.open(this.href, 'comments', 'width=480,height=480,scrollbars=yes'); return false;">{$entry.label_trackbacks} ({$entry.trackbacks})</a>
+ {else}
+ | <a href="{$entry.link}#trackbacks">{$entry.label_trackbacks} ({$entry.trackbacks})</a>
+ {/if}
+ {/if}
+
+ {if $entry.is_entry_owner and not $is_preview}
+ | <a href="{$entry.link_edit}">{$CONST.EDIT_ENTRY}</a>
+ {/if}
+
+ {$entry.add_footer}
+ </div>
+ </div>
+ <!--
+ <rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/"
+ xmlns:dc="http://purl.org/dc/elements/1.1/">
+ <rdf:Description
+ rdf:about="{$entry.link_rdf}"
+ trackback:ping="{$entry.link_trackback}"
+ dc:title="{$entry.title}"
+ dc:identifier="{$entry.rdf_ident}" />
+ </rdf:RDF>
+ -->
+ {$entry.plugin_display_dat}
+
+ {if $is_single_entry and not $use_popups and not $is_preview}
+ {if $CONST.DATA_UNSUBSCRIBED}
+ <br /><div class="serendipity_center serendipity_msg_notice">{$CONST.DATA_UNSUBSCRIBED|@sprintf:$CONST.UNSUBSCRIBE_OK}</div><br />
+ {/if}
+
+ {if $CONST.DATA_TRACKBACK_DELETED}
+ <br /><div class="serendipity_center serendipity_msg_notice">{$CONST.DATA_TRACKBACK_DELETED|@sprintf:$CONST.TRACKBACK_DELETED}</div><br />
+ {/if}
+
+ {if $CONST.DATA_TRACKBACK_APPROVED}
+ <br /><div class="serendipity_center serendipity_msg_notice">{$CONST.DATA_TRACKBACK_APPROVED|@sprintf:$CONST.TRACKBACK_APPROVED}</div><br />
+ {/if}
+
+ {if $CONST.DATA_COMMENT_DELETED}
+ <br /><div class="serendipity_center serendipity_msg_notice">{$CONST.DATA_COMMENT_DELETED|@sprintf:$CONST.COMMENT_DELETED}</div><br />
+ {/if}
+
+ {if $CONST.DATA_COMMENT_APPROVED}
+ <br /><div class="serendipity_center serendipity_msg_notice">{$CONST.DATA_COMMENT_APPROVED|@sprintf:$CONST.COMMENT_APPROVED}</div><br />
+ {/if}
+
+ {/if}
+
+ {if $is_single_entry and not $is_preview}
+ <div class="serendipity_comments">
+ <br />
+ <a id="trackbacks"></a>
+ <div class="serendipity_commentsTitle">{$CONST.TRACKBACKS}</div>
+ <div class="serendipity_center">
+ <a rel="nofollow" style="font-weight: normal" href="{$entry.link_trackback}" onclick="alert('{$CONST.TRACKBACK_SPECIFIC_ON_CLICK|@escape:htmlall}'); return false;" title="{$CONST.TRACKBACK_SPECIFIC_ON_CLICK|@escape}">{$CONST.TRACKBACK_SPECIFIC}</a>
+ </div>
+ <br />
+ {serendipity_printTrackbacks entry=$entry.id}
+ </div>
+ <div class="serendipity_comments">
+ <br />
+ <a id="comments"></a>
+ <div class="serendipity_commentsTitle">{$CONST.COMMENTS}</div>
+ <div class="serendipity_center">{$CONST.DISPLAY_COMMENTS_AS}
+ {if $entry.viewmode eq $CONST.VIEWMODE_LINEAR}
+ ({$CONST.COMMENTS_VIEWMODE_LINEAR} | <a href="{$entry.link_viewmode_threaded}#comments">{$CONST.COMMENTS_VIEWMODE_THREADED}</a>)
+ {else}
+ (<a href="{$entry.link_viewmode_linear}#comments">{$CONST.COMMENTS_VIEWMODE_LINEAR}</a> | {$CONST.COMMENTS_VIEWMODE_THREADED})
+ {/if}
+ </div>
+ <br />
+ {serendipity_printComments entry=$entry.id mode=$entry.viewmode}
+
+ {if $entry.is_entry_owner}
+ {if $entry.allow_comments}
+ <div class="serendipity_center">(<a href="{$entry.link_deny_comments}">{$CONST.COMMENTS_DISABLE}</a>)</div>
+ {else}
+ <div class="serendipity_center">(<a href="{$entry.link_allow_comments}">{$CONST.COMMENTS_ENABLE}</a>)</div>
+ {/if}
+ {/if}
+ <a id="feedback"></a>
+
+ {foreach from=$comments_messagestack item="message"}
+ <div class="serendipity_center serendipity_msg_important">{$message}</div>
+ {/foreach}
+
+ {if $is_comment_added}
+
+ <br />
+ <div class="serendipity_center serendipity_msg_notice">{$CONST.COMMENT_ADDED}</div>
+
+ {elseif $is_comment_moderate}
+
+ <br />
+ <div class="serendipity_center serendipity_msg_notice">{$CONST.COMMENT_ADDED}<br />{$CONST.THIS_COMMENT_NEEDS_REVIEW}</div>
+
+ {elseif not $entry.allow_comments}
+
+ <br />
+ <div class="serendipity_center serendipity_msg_important">{$CONST.COMMENTS_CLOSED}</div>
+
+ {else}
+
+ <br />
+ <div class="serendipity_commentsTitle">{$CONST.ADD_COMMENT}</div>
+ {$COMMENTFORM}
+
+ {/if}
+ </div>
+ {/if}
+
+ {$entry.backend_preview}
+ {/foreach}
+ </div>
+ {foreachelse}
+ {if not $plugin_clean_page}
+ {$CONST.NO_ENTRIES_TO_PRINT}
+ {/if}
+ {/foreach}
+
+{if $footer_info}
+ <div class="serendipity_pageFooter">
+ {if $footer_info}
+ {if $footer_prev_page}
+ <span class="previous"><a href="{$footer_prev_page}">&laquo; {$CONST.PREVIOUS_PAGE}</a></span>
+ {else}
+ <span class="previous">&#160;</span>
+ {/if}
+ {else}
+ {/if}
+
+ {if $footer_info}
+ <span class="entries_info">({$footer_info})</span>
+ {/if}
+
+ {if $footer_info}
+ {if $footer_next_page}
+ <span class="next"><a href="{$footer_next_page}">{$CONST.NEXT_PAGE} &raquo;</a></span>
+ {else}
+ <span class="next">&#160;</span>
+ {/if}
+ {else}
+ {/if}
+ </div>{/if}
+ {serendipity_hookPlugin hook="entries_footer"}
+<!-- ENTRIES END -->
diff --git a/tests/examplefiles/test.bas b/tests/examplefiles/test.bas
new file mode 100644
index 00000000..af5f2574
--- /dev/null
+++ b/tests/examplefiles/test.bas
@@ -0,0 +1,29 @@
+Public Class Form1
+ Inherits System.Windows.Forms.Form
+
+ Private t As New System.Timers.Timer(2000)
+
+ Private Sub Form1_Load(ByVal sender As Object, _
+ ByVal e As System.EventArgs) Handles MyBase.Load
+
+ AddHandler t.Elapsed, AddressOf TimerFired
+ End Sub
+
+ Private Sub btnStart_Click(ByVal sender As System.Object, _
+ ByVal e As System.EventArgs) Handles btnStart.Click
+
+ t.Enabled = True
+ End Sub
+
+ Private Sub btnStop_Click(ByVal sender As System.Object, _
+ ByVal e As System.EventArgs) Handles btnStop.Click
+
+ t.Enabled = False
+ End Sub
+
+ Public Sub TimerFired(ByVal sender As Object, _
+ ByVal e As System.Timers.ElapsedEventArgs)
+
+ Label1.Text = "Signal Time = " & e.SignalTime.ToString
+ End Sub
+End Class
diff --git a/tests/examplefiles/test.boo b/tests/examplefiles/test.boo
new file mode 100644
index 00000000..d6107aa7
--- /dev/null
+++ b/tests/examplefiles/test.boo
@@ -0,0 +1,39 @@
+import System
+import Boo.Lang.Interpreter from Boo.Lang.Interpreter
+
+class ObjectInterpreter(AbstractInterpreter):
+
+ _context as object
+
+ [getter(Value)]
+ _value as object
+
+ def constructor(context):
+ _context = context
+ self.RememberLastValue = true
+
+ override def Lookup(name as string):
+ property = _context.GetType().GetProperty(name)
+ return property.PropertyType if property is not null
+
+ override def GetValue(name as string):
+ return _context.GetType().GetProperty(name).GetValue(
+ _context, null)
+
+ override def SetLastValue(value):
+ _value = value
+
+ override def SetValue(name as string, value):
+ raise InvalidOperationException()
+
+ override def Declare(name as string, type as Type):
+ raise InvalidOperationException()
+
+class Person:
+ [property(FirstName)]
+ _fname as string = ""
+
+p = Person(FirstName: "Homer")
+i = ObjectInterpreter(p)
+i.Eval('"Hello, ${FirstName.ToUpper()}!"')
+print i.Value
diff --git a/tests/examplefiles/test.cs b/tests/examplefiles/test.cs
new file mode 100644
index 00000000..ffa9bfea
--- /dev/null
+++ b/tests/examplefiles/test.cs
@@ -0,0 +1,351 @@
+////////////////////////////////////////////////////////////////////////////////
+// //
+// MIT X11 license, Copyright (c) 2005-2006 by: //
+// //
+// Authors: //
+// Michael Dominic K. <michaldominik@gmail.com> //
+// //
+// Permission is hereby granted, free of charge, to any person obtaining a //
+// copy of this software and associated documentation files (the "Software"), //
+// to deal in the Software without restriction, including without limitation //
+// the rights to use, copy, modify, merge, publish, distribute, sublicense, //
+// and/or sell copies of the Software, and to permit persons to whom the //
+// Software is furnished to do so, subject to the following conditions: //
+// //
+// The above copyright notice and this permission notice shall be included //
+// in all copies or substantial portions of the Software. //
+// //
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS //
+// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF //
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN //
+// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, //
+// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR //
+// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE //
+// USE OR OTHER DEALINGS IN THE SOFTWARE. //
+// //
+////////////////////////////////////////////////////////////////////////////////
+
+namespace Diva.Core {
+
+ using System;
+ using Widgets;
+ using System.Xml;
+ using Util;
+ using System.Collections.Generic;
+ using System.Collections;
+ using Basics;
+
+ public class OpenerTask : Task, IBoilProvider {
+
+ // Private structs ////////////////////////////////////////////
+
+ struct ObjectInfo {
+
+ public ObjectContainer Container;
+ public int[] Depends;
+ public string SystemType;
+ public int RefId;
+
+ /* CONSTRUCTOR */
+ public ObjectInfo (ObjectContainer container)
+ {
+ Container = container;
+ Depends = container.Depends.ToArray ();
+ SystemType = container.SystemType;
+ RefId = container.RefId;
+ }
+
+ public override string ToString ()
+ {
+ return String.Format ("Type: {0} Deps count: {1} Id: {2}",
+ SystemType, Depends.Length, RefId);
+ }
+
+ public bool IsUnBoilable (IBoilProvider provider)
+ {
+ if (Depends.Length == 0)
+ return true;
+
+ foreach (int id in Depends)
+ if (! (provider.Contains (id)))
+ return false;
+
+ return true;
+ }
+
+ }
+
+ // Enums //////////////////////////////////////////////////////
+
+ enum OpenerTaskStep { Init, Header, ProjectInfoRead, ObjectListRead,
+ ObjectListParse, ObjectListUnBoil, FindRoots,
+ Finished };
+
+ // Fields /////////////////////////////////////////////////////
+
+ string fileName; // Filename we're reading
+ XmlDocument xmlDocument; // Our document
+ //XmlNode projectInfoNode; // <projectinfo> node
+ IEnumerator objectsEnumerator; // Enumerator
+ List <ObjectInfo> objectsList; // Objects list
+ ObjectListContainer objectListContainer;
+ OpenerTaskStep currentStep; // Our current step
+
+ Dictionary <int, object> idToObject; // Id -> object
+ Dictionary <object, int> objectToId; // Object -> Id
+
+ string projectName = String.Empty;
+ string projectDirectory = String.Empty;
+ TagList projectTagList;
+ StuffList projectStuffList;
+ TrackList projectTrackList;
+ ClipList projectClipList;
+ MediaItemList projectMediaItemList;
+ Commander projectCommander;
+ Gdv.Pipeline projectPipeline;
+ Gdv.ProjectFormat projectFormat;
+
+ // Properties /////////////////////////////////////////////////
+
+ public string ProjectName {
+ get { return projectName; }
+ }
+
+ public string ProjectDirectory {
+ get { return projectDirectory; }
+ }
+
+ public TagList ProjectTagList {
+ get { return projectTagList; }
+ }
+
+ public StuffList ProjectStuffList {
+ get { return projectStuffList; }
+ }
+
+ public TrackList ProjectTrackList {
+ get { return projectTrackList; }
+ }
+
+ public ClipList ProjectClipList {
+ get { return projectClipList; }
+ }
+
+ public MediaItemList ProjectMediaItemList {
+ get { return projectMediaItemList; }
+ }
+
+ public Commander ProjectCommander {
+ get { return projectCommander; }
+ }
+
+ public Gdv.Pipeline ProjectPipeline {
+ get { return projectPipeline; }
+ }
+
+ public Gdv.ProjectFormat ProjectFormat {
+ get { return projectFormat; }
+ }
+
+ // Public methods /////////////////////////////////////////////
+
+ /* CONSTRUCTOR */
+ public OpenerTask (string fileName)
+ {
+ this.fileName = fileName;
+ }
+
+ public override void Reset ()
+ {
+ objectToId = new Dictionary <object, int> ();
+ idToObject = new Dictionary <int, object> ();
+
+ xmlDocument = null;
+ //projectInfoNode = null;
+
+ currentStep = OpenerTaskStep.Init;
+
+ base.Reset ();
+ }
+
+ public int GetIdForObject (object o)
+ {
+ return objectToId [o];
+ }
+
+ public object GetObjectForId (int id)
+ {
+ return idToObject [id];
+ }
+
+ public bool Contains (int id)
+ {
+ return idToObject.ContainsKey (id);
+ }
+
+ // Private methods ////////////////////////////////////////////
+
+ protected override TaskStatus ExecuteStep (int s)
+ {
+ bool cont = true;
+
+ // Main
+ switch (currentStep) {
+
+ case OpenerTaskStep.Init:
+ objectsList = new List <ObjectInfo> ();
+ xmlDocument = new XmlDocument ();
+ xmlDocument.Load (fileName);
+ currentStep = OpenerTaskStep.Header;
+ break;
+
+ case OpenerTaskStep.Header:
+ //ReadHeader ();
+ currentStep = OpenerTaskStep.ProjectInfoRead;
+ break;
+
+ case OpenerTaskStep.ProjectInfoRead:
+ foreach (XmlNode node in xmlDocument.DocumentElement.ChildNodes)
+ if (node.Name == "projectinfo")
+ ResolveProjectInfoNode (node);
+
+ // FIXME: Fail if not found/not resolved
+ currentStep = OpenerTaskStep.ObjectListRead;
+ break;
+
+ case OpenerTaskStep.ObjectListRead:
+ foreach (XmlNode node in xmlDocument.DocumentElement.ChildNodes)
+ if (node.Name == "objectlist")
+ objectListContainer = (ObjectListContainer)
+ DataFactory.MakeDataElement (node as XmlElement);
+
+ if (objectListContainer == null)
+ throw new Exception ("ObjectListContainer not found!");
+
+ currentStep = OpenerTaskStep.ObjectListParse;
+ break;
+
+ case OpenerTaskStep.ObjectListParse:
+ bool flush = EnumerateSomeObjects ();
+ if (flush)
+ currentStep = OpenerTaskStep.ObjectListUnBoil;
+ break;
+
+ case OpenerTaskStep.ObjectListUnBoil:
+ bool done = UnBoilSomeObjects ();
+ if (done)
+ currentStep = OpenerTaskStep.FindRoots;
+ break;
+
+
+ case OpenerTaskStep.FindRoots:
+ projectTrackList = (TrackList) FindRoot ("tracklist");
+ projectTagList = (TagList) FindRoot ("taglist");
+ projectStuffList = (StuffList) FindRoot ("stufflist");
+ projectClipList = (ClipList) FindRoot ("cliplist");
+ projectMediaItemList = (MediaItemList) FindRoot ("mediaitemlist");
+ projectPipeline = (Gdv.Pipeline) FindRoot ("pipeline");
+ projectCommander = (Commander) FindRoot ("commander");
+ projectFormat = (Gdv.ProjectFormat) FindRoot ("projectformat");
+
+ currentStep = OpenerTaskStep.Finished;
+ break;
+
+ case OpenerTaskStep.Finished:
+ cont = false;
+ break;
+
+ default:
+ break;
+ }
+
+ // Post
+ if (cont)
+ return TaskStatus.Running;
+ else
+ return TaskStatus.Done;
+ }
+
+ /*
+ void ReadHeader ()
+ {
+ // FIXME: Read all the attributes from the <divaproject> element
+ }*/
+
+ void ResolveProjectInfoNode (XmlNode node)
+ {
+ foreach (XmlNode childNode in node) {
+
+ switch (childNode.Name) {
+
+ case "name":
+ projectName = childNode.FirstChild.Value;
+ break;
+
+ case "directory":
+ projectDirectory = childNode.FirstChild.Value;
+ break;
+
+ // FIXME: Duration etc.
+ }
+ }
+ }
+
+ bool EnumerateSomeObjects ()
+ {
+ if (objectsEnumerator == null)
+ objectsEnumerator = objectListContainer.FindAllObjects ().GetEnumerator ();
+
+ for (int i = 0; i < 10; i++) {
+ if (objectsEnumerator.MoveNext () == false)
+ return true;
+
+ ObjectContainer container = (ObjectContainer)
+ objectsEnumerator.Current;
+
+ ObjectInfo newInfo = new ObjectInfo (container);
+ objectsList.Add (newInfo);
+ }
+
+ return false;
+ }
+
+ ObjectInfo GetNextCandidate ()
+ {
+ foreach (ObjectInfo objInfo in objectsList)
+ if (objInfo.IsUnBoilable (this))
+ return objInfo;
+
+ throw new Exception ("FIXME: No more unboilable objects found. Recursive?");
+ }
+
+ bool UnBoilSomeObjects ()
+ {
+ for (int i = 0; i < 5; i++) {
+ // All unboiled
+ if (objectsList.Count == 0)
+ return true;
+
+ ObjectInfo objInfo = GetNextCandidate ();
+
+ object o = BoilFactory.UnBoil (objInfo.Container, this);
+ objectsList.Remove (objInfo);
+
+ // Add
+ idToObject [objInfo.RefId] = o;
+ objectToId [o] = objInfo.RefId;
+
+ }
+
+ return false;
+ }
+
+ object FindRoot (string rootString)
+ {
+ ObjectContainer container = objectListContainer.FindObjectContainer (rootString);
+ return idToObject [container.RefId];
+ }
+
+ }
+
+}
diff --git a/tests/examplefiles/test.html b/tests/examplefiles/test.html
new file mode 100644
index 00000000..4527a3fb
--- /dev/null
+++ b/tests/examplefiles/test.html
@@ -0,0 +1,6342 @@
+<html>
+<head>
+<script>
+ function initCodeBlock(id); {
+ var el = document.getElementById(id);
+ }
+</script>
+<style>
+/*
+ * Pocoo highlight package "green" style
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ * based on the pyte vim theme
+ *
+ * :copyright: 2006 by Georg Brandl.
+ * :license: GNU GPL, see LICENSE for more details.
+ */
+
+.syntax { border: 1px solid #d0d0d0; background-color: #f0f0f0;
+ margin-left: 10px; margin-right: 10px; }
+
+.syntaxheader { margin-top: 15px; margin-bottom: 0px;
+ text-align: right; font-size: 11px;
+ border-bottom: 0; padding: 3px; }
+
+.linenos { float: left; display: block; }
+.linenos pre { padding-right: 7px; padding-left: 7px;
+ color: #666; }
+
+pre.syntax { padding: 5px; margin-top: 0px; }
+
+.syntax .cm { color: #60a0b0; font-style: italic; } /* comments */
+.syntax .cm-proc { color: #007020; font-style: normal; } /* preproc */
+.syntax .kw { color: #007020; font-weight: bold; } /* keywords */
+.syntax .kw-pseudo { font-weight: normal; } /* pseudo keywords */
+.syntax .op { color: #666666; } /* operators */
+.syntax .op-word { color: #007020; font-weight: bold; } /* word operators */
+.syntax .bn { color: #007020; } /* builtins */
+.syntax .fun { color: #06287e; } /* func name */
+.syntax .cls { color: #0e84b5; font-weight: bold; } /* class names */
+.syntax .exc { color: #007020; } /* exceptions */
+.syntax .var { color: #bb60d5; } /* variables */
+.syntax .const { color: #60add5; } /* constants */
+.syntax .entity { color: #d55537; font-weight: bold; } /* entities */
+.syntax .attr { color: #4070a0; } /* attributes */
+.syntax .tag { color: #062873; font-weight: bold; } /* tag names */
+.syntax .deco { color: #555555; font-weight: bold; } /* decorators */
+.syntax .st { color: #4070a0; } /* strings */
+.syntax .st-int { color: #70a0d0; font-style: italic; } /* interpolated str */
+.syntax .st-esc { color: #4070a0; font-weight: bold; } /* escaped str */
+.syntax .st-re { color: #235388; } /* regular expr */
+.syntax .st-sym { color: #517918; } /* symbols */
+.syntax .st-oth { color: #c65d09; } /* other strings */
+.syntax .nb { color: #40a070; } /* numbers */
+
+.syntax .gen-hd { font-weight: bold; color: blue; } /* headings */
+.syntax .gen-sh { font-weight: bold; color: purple; } /* subheadings */
+.syntax .gen-del { color: red; } /* deleted text */
+.syntax .gen-ins { color: green; } /* inserted text */
+.syntax .gen-em { font-style: italic; } /* emphasized text */
+.syntax .gen-sr { font-weight: bold; } /* strong emph. text */
+
+.syntax .err { border: 1px solid red; } /* parser errors */
+</style>
+</head>
+<body>
+<pre id="code-block" class="syntax"><span class="cm"># -*- coding: utf-8 -*-</span>
+<span class="st st-db">&quot;&quot;&quot;</span><span class="st">
+ pocoo.pkg.core.acl
+ ~~~~~~~~~~~~~~~~~~
+
+ Pocoo ACL System.
+
+ :copyright: 2006 by Armin Ronacher.
+ :license: GNU GPL, see LICENSE for more details.
+</span><span class="st st-db">&quot;&quot;&quot;</span>
+
+<span class="kw">from </span><span class="cls">pocoo.db</span><span class="kw"> import</span> <span class="name">meta</span>
+
+<span class="kw">from </span><span class="cls">pocoo.pkg.core.forum</span><span class="kw"> import</span> <span class="name">Site</span>, <span class="name">Forum</span>, <span class="name">Thread</span>
+<span class="kw">from </span><span class="cls">pocoo.pkg.core.user</span><span class="kw"> import</span> <span class="name">User</span>, <span class="name">Group</span>
+
+<span class="kw">from </span><span class="cls">pocoo.pkg.core.db</span><span class="kw"> import</span> <span class="name">users</span>, <span class="name">groups</span>, <span class="name">group_members</span>, <span class="name">privileges</span>, \
+ <span class="name">forums</span>, <span class="name">posts</span>, <span class="name">acl_mapping</span>, <span class="name">acl_subjects</span>, <span class="name">acl_objects</span>
+
+
+<span class="kw">class </span><span class="cls">AclManager</span>(<span class="bn">object</span>):
+ <span class="st st-db">&quot;&quot;&quot;</span><span class="st">
+ Manager object to manage ALCs.
+ </span><span class="st st-db">&quot;&quot;&quot;</span>
+ <span class="name">STRONG_NO</span> <span class="op">=</span> <span class="op">-</span><span class="nb nb-int">1</span>
+
+ <span class="name">WEAK_NO</span> <span class="op">=</span> <span class="nb nb-int">0</span>
+ <span class="name">WEAK_YES</span> <span class="op">=</span> <span class="nb nb-int">1</span>
+ <span class="name">STRONG_YES</span> <span class="op">=</span> <span class="nb nb-int">2</span>
+
+ <span class="kw">def </span><span class="fun">__init__</span>(<span class="bn bn-pseudo">self</span>, <span class="name">ctx</span>, <span class="name">subject</span>):
+ <span class="bn bn-pseudo">self</span>.<span class="name">ctx</span> <span class="op">=</span> <span class="name">ctx</span>
+
+ <span class="bn bn-pseudo">self</span>.<span class="name">subject</span> <span class="op">=</span> <span class="name">subject</span>
+ <span class="kw">if</span> <span class="bn">isinstance</span>(<span class="name">subject</span>, <span class="name">User</span>):
+ <span class="bn bn-pseudo">self</span>.<span class="name">_type</span> <span class="op">=</span> <span class="st st-sg">&#39;</span><span class="st">user</span><span class="st st-sg">&#39;</span>
+
+ <span class="kw">elif</span> <span class="bn">isinstance</span>(<span class="name">subject</span>, <span class="name">Group</span>):
+ <span class="bn bn-pseudo">self</span>.<span class="name">_type</span> <span class="op">=</span> <span class="st st-sg">&#39;</span><span class="st">group</span><span class="st st-sg">&#39;</span>
+
+ <span class="kw">else</span>:
+ <span class="kw">raise</span> <span class="exc">ValueError</span>(<span class="st st-sg">&#39;</span><span class="st">neither user or group specified</span><span class="st st-sg">&#39;</span>)
+
+ <span class="kw">def </span><span class="fun">allow</span>(<span class="bn bn-pseudo">self</span>, <span class="name">privilege</span>, <span class="name">obj</span>, <span class="name">force</span><span class="op">=</span><span class="bn bn-pseudo">False</span>):
+ <span class="st st-db">&quot;&quot;&quot;</span><span class="st">Allows the subject privilege on obj.</span><span class="st st-db">&quot;&quot;&quot;</span>
+
+ <span class="kw">return</span> <span class="bn bn-pseudo">self</span>.<span class="name">_set</span>(<span class="name">privilege</span>, <span class="name">obj</span>, <span class="nb nb-int">1</span> <span class="op">+</span> <span class="bn">bool</span>(<span class="name">force</span>))
+
+ <span class="kw">def </span><span class="fun">default</span>(<span class="bn bn-pseudo">self</span>, <span class="name">privilege</span>, <span class="name">obj</span>):
+ <span class="st st-db">&quot;&quot;&quot;</span><span class="st">Sets the state for privilege on obj back to weak yes.</span><span class="st st-db">&quot;&quot;&quot;</span>
+
+ <span class="kw">return</span> <span class="bn bn-pseudo">self</span>.<span class="name">_set</span>(<span class="name">privilege</span>, <span class="name">obj</span>, <span class="nb nb-int">0</span>)
+
+ <span class="kw">def </span><span class="fun">deny</span>(<span class="bn bn-pseudo">self</span>, <span class="name">privilege</span>, <span class="name">obj</span>, <span class="name">force</span><span class="op">=</span><span class="bn bn-pseudo">False</span>):
+ <span class="st st-db">&quot;&quot;&quot;</span><span class="st">Denies the subject privilege on obj.</span><span class="st st-db">&quot;&quot;&quot;</span>
+
+ <span class="kw">return</span> <span class="bn bn-pseudo">self</span>.<span class="name">_set</span>(<span class="name">privilege</span>, <span class="name">obj</span>, <span class="op">-</span><span class="nb nb-int">1</span> <span class="op">-</span> <span class="bn">bool</span>(<span class="name">force</span>))
+
+ <span class="kw">def </span><span class="fun">can_access</span>(<span class="bn bn-pseudo">self</span>, <span class="name">privilege</span>, <span class="name">obj</span>):
+ <span class="st st-db">&quot;&quot;&quot;</span><span class="st">Checks if the current subject with the required privilege
+ somehow. Either directly or when the subject is a user and
+ one of its groups can access it.</span><span class="st st-db">&quot;&quot;&quot;</span>
+
+ <span class="cm">#XXX: maybe this could be one big query instead of 4</span>
+ <span class="cm">#XXX: this currently does not work correctly, therefore return True</span>
+ <span class="kw">return</span> <span class="bn bn-pseudo">True</span>
+
+ <span class="kw">if</span> <span class="op op-word">not</span> <span class="bn">isinstance</span>(<span class="name">obj</span>, (<span class="name">Forum</span>, <span class="name">Thread</span>, <span class="name">Site</span>.<span class="name">__class__</span>)):
+ <span class="kw">raise</span> <span class="exc">TypeError</span>(<span class="st st-sg">&#39;</span><span class="st">obj must be a forum, thread or site</span><span class="st st-sg">&#39;</span>)
+ <span class="name">privilege</span> <span class="op">=</span> <span class="name">privilege</span>.<span class="name">upper</span>()
+ <span class="name">s</span> <span class="op">=</span> <span class="bn bn-pseudo">self</span>.<span class="name">_get_subject_join</span>().<span class="name">alias</span>(<span class="st st-sg">&#39;</span><span class="st">s</span><span class="st st-sg">&#39;</span>).<span class="name">c</span>
+
+ <span class="kw">def </span><span class="fun">do_check</span>(<span class="name">obj</span>, <span class="name">tendency</span>):
+ <span class="name">db</span> <span class="op">=</span> <span class="bn bn-pseudo">self</span>.<span class="name">ctx</span>.<span class="name">engine</span>
+
+ <span class="name">o</span> <span class="op">=</span> <span class="bn bn-pseudo">self</span>.<span class="name">_get_object_join</span>(<span class="name">obj</span>).<span class="name">alias</span>(<span class="st st-sg">&#39;</span><span class="st">o</span><span class="st st-sg">&#39;</span>).<span class="name">c</span>
+
+ <span class="cm"># self check</span>
+ <span class="name">r</span> <span class="op">=</span> <span class="name">db</span>.<span class="name">execute</span>(<span class="name">meta</span>.<span class="name">select</span>([<span class="name">acl_mapping</span>.<span class="name">c</span>.<span class="name">state</span>],
+ (<span class="name">acl_mapping</span>.<span class="name">c</span>.<span class="name">priv_id</span> <span class="op">==</span> <span class="name">privileges</span>.<span class="name">c</span>.<span class="name">priv_id</span>) <span class="op">&amp;</span>
+
+ (<span class="name">acl_mapping</span>.<span class="name">c</span>.<span class="name">subject_id</span> <span class="op">==</span> <span class="name">s</span>.<span class="name">subject_id</span>) <span class="op">&amp;</span>
+ (<span class="name">acl_mapping</span>.<span class="name">c</span>.<span class="name">object_id</span> <span class="op">==</span> <span class="name">o</span>.<span class="name">object_id</span>) <span class="op">&amp;</span>
+
+ (<span class="name">privileges</span>.<span class="name">c</span>.<span class="name">name</span> <span class="op">==</span> <span class="name">privilege</span>)
+ ))
+ <span class="name">row</span> <span class="op">=</span> <span class="name">r</span>.<span class="name">fetchone</span>()
+ <span class="kw">if</span> <span class="name">row</span> <span class="op op-word">is</span> <span class="op op-word">not</span> <span class="bn bn-pseudo">None</span>:
+ <span class="kw">if</span> <span class="name">row</span>[<span class="st st-sg">&#39;</span><span class="st">state</span><span class="st st-sg">&#39;</span>] <span class="op op-word">in</span> (<span class="bn bn-pseudo">self</span>.<span class="name">STRONG_NO</span>, <span class="bn bn-pseudo">self</span>.<span class="name">STRONG_YES</span>):
+ <span class="kw">return</span> <span class="name">row</span>[<span class="st st-sg">&#39;</span><span class="st">state</span><span class="st st-sg">&#39;</span>] <span class="op">==</span> <span class="bn bn-pseudo">self</span>.<span class="name">STRONG_YES</span>
+
+ <span class="name">tendency</span> <span class="op">=</span> <span class="name">row</span>[<span class="st st-sg">&#39;</span><span class="st">state</span><span class="st st-sg">&#39;</span>]
+
+ <span class="cm"># if the controlled subject is a user check all groups</span>
+ <span class="kw">if</span> <span class="bn">isinstance</span>(<span class="bn bn-pseudo">self</span>.<span class="name">subject</span>, <span class="name">User</span>):
+ <span class="name">r</span> <span class="op">=</span> <span class="name">db</span>.<span class="name">execute</span>(<span class="name">meta</span>.<span class="name">select</span>([<span class="name">acl_mapping</span>.<span class="name">c</span>.<span class="name">state</span>],
+ (<span class="name">acl_mapping</span>.<span class="name">c</span>.<span class="name">object_id</span> <span class="op">==</span> <span class="name">o</span>.<span class="name">object_id</span>) <span class="op">&amp;</span>
+
+ (<span class="name">acl_mapping</span>.<span class="name">c</span>.<span class="name">subject_id</span> <span class="op">==</span> <span class="name">groups</span>.<span class="name">c</span>.<span class="name">subject_id</span>) <span class="op">&amp;</span>
+
+ (<span class="name">groups</span>.<span class="name">c</span>.<span class="name">group_id</span> <span class="op">==</span> <span class="name">group_members</span>.<span class="name">c</span>.<span class="name">group_id</span>) <span class="op">&amp;</span>
+
+ (<span class="name">group_members</span>.<span class="name">c</span>.<span class="name">user_id</span> <span class="op">==</span> <span class="bn bn-pseudo">self</span>.<span class="name">subject</span>.<span class="name">user_id</span>)
+ ))
+ <span class="kw">while</span> <span class="bn bn-pseudo">True</span>:
+ <span class="name">row</span> <span class="op">=</span> <span class="name">r</span>.<span class="name">fetchone</span>()
+ <span class="kw">if</span> <span class="name">row</span> <span class="op op-word">is</span> <span class="bn bn-pseudo">None</span>:
+ <span class="kw">break</span>
+
+ <span class="name">state</span> <span class="op">=</span> <span class="name">row</span>[<span class="nb nb-int">0</span>]
+ <span class="kw">if</span> <span class="name">state</span> <span class="op op-word">in</span> (<span class="bn bn-pseudo">self</span>.<span class="name">STRONG_YES</span>, <span class="bn bn-pseudo">self</span>.<span class="name">STRONG_NO</span>):
+ <span class="kw">return</span> <span class="name">state</span> <span class="op">==</span> <span class="bn bn-pseudo">self</span>.<span class="name">STRONG_YES</span>
+
+ <span class="kw">if</span> <span class="name">tendency</span> <span class="op op-word">is</span> <span class="bn bn-pseudo">None</span>:
+ <span class="name">tendency</span> <span class="op">=</span> <span class="name">state</span>
+ <span class="kw">elif</span> <span class="name">tendency</span> <span class="op">==</span> <span class="bn bn-pseudo">self</span>.<span class="name">WEAK_NO</span> <span class="op op-word">and</span> <span class="name">state</span> <span class="op">==</span> <span class="bn bn-pseudo">self</span>.<span class="name">WEAK_YES</span>:
+ <span class="name">tendency</span> <span class="op">=</span> <span class="bn bn-pseudo">self</span>.<span class="name">WEAK_YES</span>
+
+ <span class="cm"># check related objects</span>
+ <span class="kw">if</span> <span class="bn">isinstance</span>(<span class="name">obj</span>, <span class="name">Thread</span>):
+ <span class="kw">return</span> <span class="name">do_check</span>(<span class="name">obj</span>.<span class="name">forum</span>, <span class="name">tendency</span>)
+ <span class="kw">elif</span> <span class="bn">isinstance</span>(<span class="name">obj</span>, <span class="name">Forum</span>):
+ <span class="kw">return</span> <span class="name">do_check</span>(<span class="name">Site</span>, <span class="name">tendency</span>)
+ <span class="kw">else</span>:
+ <span class="kw">return</span> <span class="name">tendency</span>
+
+ <span class="kw">return</span> <span class="name">do_check</span>(<span class="name">obj</span>, <span class="bn bn-pseudo">None</span>) <span class="op op-word">in</span> (<span class="bn bn-pseudo">self</span>.<span class="name">WEAK_YES</span>, <span class="bn bn-pseudo">self</span>.<span class="name">STRONG_YES</span>)
+
+ <span class="kw">def </span><span class="fun">_set</span>(<span class="bn bn-pseudo">self</span>, <span class="name">privilege</span>, <span class="name">obj</span>, <span class="name">state</span>):
+ <span class="st st-db">&quot;&quot;&quot;</span><span class="st">Helper functions for settings privileges.</span><span class="st st-db">&quot;&quot;&quot;</span>
+
+ <span class="name">privilege</span> <span class="op">=</span> <span class="name">privilege</span>.<span class="name">upper</span>()
+ <span class="kw">if</span> <span class="bn bn-pseudo">self</span>.<span class="name">subject</span>.<span class="name">subject_id</span> <span class="op op-word">is</span> <span class="bn bn-pseudo">None</span>:
+ <span class="bn bn-pseudo">self</span>.<span class="name">_bootstrap</span>()
+ <span class="kw">if</span> <span class="name">obj</span>.<span class="name">object_id</span> <span class="op op-word">is</span> <span class="bn bn-pseudo">None</span>:
+ <span class="bn bn-pseudo">self</span>.<span class="name">_bootstrap_object</span>(<span class="name">obj</span>)
+ <span class="cm"># special state &quot;0&quot; which means delete</span>
+
+ <span class="kw">if</span> <span class="op op-word">not</span> <span class="name">state</span>:
+ <span class="name">p</span> <span class="op">=</span> <span class="name">meta</span>.<span class="name">select</span>([<span class="name">privileges</span>.<span class="name">c</span>.<span class="name">priv_id</span>], <span class="name">privileges</span>.<span class="name">c</span>.<span class="name">name</span> <span class="op">==</span> <span class="name">privilege</span>)
+ <span class="bn bn-pseudo">self</span>.<span class="name">ctx</span>.<span class="name">engine</span>.<span class="name">execute</span>(<span class="name">acl_mapping</span>.<span class="name">delete</span>(
+ (<span class="name">acl_mapping</span>.<span class="name">c</span>.<span class="name">priv_id</span> <span class="op">==</span> <span class="name">p</span>.<span class="name">c</span>.<span class="name">priv_id</span>) <span class="op">&amp;</span>
+
+ (<span class="name">acl_mapping</span>.<span class="name">c</span>.<span class="name">subject_id</span> <span class="op">==</span> <span class="bn bn-pseudo">self</span>.<span class="name">subject</span>.<span class="name">subject_id</span>) <span class="op">&amp;</span>
+
+ (<span class="name">acl_mapping</span>.<span class="name">c</span>.<span class="name">object_id</span> <span class="op">==</span> <span class="name">obj</span>.<span class="name">object_id</span>)
+ ))
+ <span class="kw">return</span>
+ <span class="cm"># touch privilege and check existing mapping</span>
+
+ <span class="name">priv_id</span> <span class="op">=</span> <span class="bn bn-pseudo">self</span>.<span class="name">_fetch_privilege</span>(<span class="name">privilege</span>)
+ <span class="name">r</span> <span class="op">=</span> <span class="bn bn-pseudo">self</span>.<span class="name">ctx</span>.<span class="name">engine</span>.<span class="name">execute</span>(<span class="name">meta</span>.<span class="name">select</span>([<span class="name">acl_mapping</span>.<span class="name">c</span>.<span class="name">state</span>],
+ (<span class="name">acl_mapping</span>.<span class="name">c</span>.<span class="name">priv_id</span> <span class="op">==</span> <span class="name">priv_id</span>) <span class="op">&amp;</span>
+
+ (<span class="name">acl_mapping</span>.<span class="name">c</span>.<span class="name">subject_id</span> <span class="op">==</span> <span class="bn bn-pseudo">self</span>.<span class="name">subject</span>.<span class="name">subject_id</span>) <span class="op">&amp;</span>
+
+ (<span class="name">acl_mapping</span>.<span class="name">c</span>.<span class="name">object_id</span> <span class="op">==</span> <span class="name">obj</span>.<span class="name">object_id</span>)
+ ))
+ <span class="name">row</span> <span class="op">=</span> <span class="name">r</span>.<span class="name">fetchone</span>()
+ <span class="kw">if</span> <span class="name">row</span> <span class="op op-word">is</span> <span class="op op-word">not</span> <span class="bn bn-pseudo">None</span>:
+ <span class="cm"># this rule exists already</span>
+
+ <span class="kw">if</span> <span class="name">row</span>[<span class="st st-sg">&#39;</span><span class="st">state</span><span class="st st-sg">&#39;</span>] <span class="op">==</span> <span class="name">state</span>:
+ <span class="kw">return</span>
+ <span class="cm"># goddamn, same rule - different state, delete old first</span>
+ <span class="bn bn-pseudo">self</span>.<span class="name">_set</span>(<span class="name">privilege</span>, <span class="name">obj</span>, <span class="nb nb-int">0</span>)
+ <span class="cm"># insert new rule</span>
+
+ <span class="bn bn-pseudo">self</span>.<span class="name">ctx</span>.<span class="name">engine</span>.<span class="name">execute</span>(<span class="name">acl_mapping</span>.<span class="name">insert</span>(),
+ <span class="name">priv_id</span> <span class="op">=</span> <span class="name">priv_id</span>,
+ <span class="name">subject_id</span> <span class="op">=</span> <span class="bn bn-pseudo">self</span>.<span class="name">subject</span>.<span class="name">subject_id</span>,
+ <span class="name">object_id</span> <span class="op">=</span> <span class="name">obj</span>.<span class="name">object_id</span>,
+ <span class="name">state</span> <span class="op">=</span> <span class="name">state</span>
+
+ )
+
+ <span class="kw">def </span><span class="fun">_bootstrap</span>(<span class="bn bn-pseudo">self</span>):
+ <span class="st st-db">&quot;&quot;&quot;</span><span class="st">This method is automatically called when subject_id is
+ None and an subject_id is required.</span><span class="st st-db">&quot;&quot;&quot;</span>
+ <span class="name">r</span> <span class="op">=</span> <span class="bn bn-pseudo">self</span>.<span class="name">ctx</span>.<span class="name">engine</span>.<span class="name">execute</span>(<span class="name">acl_subjects</span>.<span class="name">insert</span>(),
+ <span class="name">subject_type</span> <span class="op">=</span> <span class="bn bn-pseudo">self</span>.<span class="name">_type</span>
+
+ )
+ <span class="bn bn-pseudo">self</span>.<span class="name">subject</span>.<span class="name">subject_id</span> <span class="op">=</span> <span class="name">r</span>.<span class="name">last_inserted_ids</span>()[<span class="nb nb-int">0</span>]
+ <span class="bn bn-pseudo">self</span>.<span class="name">subject</span>.<span class="name">save</span>()
+
+ <span class="kw">def </span><span class="fun">_bootstrap_object</span>(<span class="bn bn-pseudo">self</span>, <span class="name">obj</span>):
+ <span class="st st-db">&quot;&quot;&quot;</span><span class="st">Like _bootstrap but works for objects.</span><span class="st st-db">&quot;&quot;&quot;</span>
+
+ <span class="name">objtype</span> <span class="op">=</span> <span class="bn bn-pseudo">self</span>.<span class="name">_get_object_type</span>(<span class="name">obj</span>)
+ <span class="name">r</span> <span class="op">=</span> <span class="bn bn-pseudo">self</span>.<span class="name">ctx</span>.<span class="name">engine</span>.<span class="name">execute</span>(<span class="name">acl_objects</span>.<span class="name">insert</span>(),
+ <span class="name">object_type</span> <span class="op">=</span> <span class="name">objtype</span>
+
+ )
+ <span class="name">obj</span>.<span class="name">object_id</span> <span class="op">=</span> <span class="name">r</span>.<span class="name">last_inserted_ids</span>()[<span class="nb nb-int">0</span>]
+ <span class="name">obj</span>.<span class="name">save</span>()
+
+ <span class="kw">def </span><span class="fun">_get_object_type</span>(<span class="bn bn-pseudo">self</span>, <span class="name">obj</span>):
+ <span class="kw">if</span> <span class="bn">isinstance</span>(<span class="name">obj</span>, <span class="name">Forum</span>):
+ <span class="kw">return</span> <span class="st st-sg">&#39;</span><span class="st">forum</span><span class="st st-sg">&#39;</span>
+
+ <span class="kw">elif</span> <span class="bn">isinstance</span>(<span class="name">obj</span>, <span class="name">Thread</span>):
+ <span class="kw">return</span> <span class="st st-sg">&#39;</span><span class="st">thread</span><span class="st st-sg">&#39;</span>
+ <span class="kw">elif</span> <span class="name">obj</span> <span class="op op-word">is</span> <span class="name">Site</span>:
+ <span class="kw">return</span> <span class="st st-sg">&#39;</span><span class="st">site</span><span class="st st-sg">&#39;</span>
+
+ <span class="kw">raise</span> <span class="exc">TypeError</span>(<span class="st st-sg">&#39;</span><span class="st">obj isn</span><span class="st st-esc">\&#39;</span><span class="st">t a forum or thread</span><span class="st st-sg">&#39;</span>)
+
+ <span class="kw">def </span><span class="fun">_get_object_join</span>(<span class="bn bn-pseudo">self</span>, <span class="name">obj</span>):
+ <span class="st st-db">&quot;&quot;&quot;</span><span class="st">Returns a subjoin for the object id.</span><span class="st st-db">&quot;&quot;&quot;</span>
+
+ <span class="name">t</span> <span class="op">=</span> <span class="bn bn-pseudo">self</span>.<span class="name">_get_object_type</span>(<span class="name">obj</span>)
+ <span class="kw">if</span> <span class="name">t</span> <span class="op">==</span> <span class="st st-sg">&#39;</span><span class="st">forum</span><span class="st st-sg">&#39;</span>:
+ <span class="kw">return</span> <span class="name">meta</span>.<span class="name">select</span>([<span class="name">forums</span>.<span class="name">c</span>.<span class="name">object_id</span>],
+ <span class="name">forums</span>.<span class="name">c</span>.<span class="name">forum_id</span> <span class="op">==</span> <span class="name">obj</span>.<span class="name">forum_id</span>
+
+ )
+ <span class="kw">elif</span> <span class="name">t</span> <span class="op">==</span> <span class="st st-sg">&#39;</span><span class="st">thread</span><span class="st st-sg">&#39;</span>:
+ <span class="kw">return</span> <span class="name">meta</span>.<span class="name">select</span>([<span class="name">posts</span>.<span class="name">c</span>.<span class="name">object_id</span>],
+ <span class="name">posts</span>.<span class="name">c</span>.<span class="name">post_id</span> <span class="op">==</span> <span class="name">obj</span>.<span class="name">post_id</span>
+
+ )
+ <span class="kw">else</span>:
+ <span class="cm"># XXX: it works ^^</span>
+ <span class="cm"># i really want something like meta.select(&#39;0 as group_id&#39;)</span>
+ <span class="kw">class </span><span class="cls">Fake</span>(<span class="bn">object</span>):
+ <span class="kw">def </span><span class="fun">alias</span>(<span class="bn bn-pseudo">self</span>, <span class="name">n</span>):
+ <span class="kw">class </span><span class="cls">_C</span>(<span class="bn">object</span>):
+ <span class="kw">class </span><span class="cls">c</span>(<span class="bn">object</span>):
+ <span class="name">object_id</span> <span class="op">=</span> <span class="nb nb-int">0</span>
+
+ <span class="kw">return</span> <span class="name">_C</span>
+ <span class="kw">return</span> <span class="name">Fake</span>()
+
+ <span class="kw">def </span><span class="fun">_get_subject_join</span>(<span class="bn bn-pseudo">self</span>):
+ <span class="st st-db">&quot;&quot;&quot;</span><span class="st">Returns a subjoin for the subject id.</span><span class="st st-db">&quot;&quot;&quot;</span>
+
+ <span class="kw">if</span> <span class="bn bn-pseudo">self</span>.<span class="name">_type</span> <span class="op">==</span> <span class="st st-sg">&#39;</span><span class="st">user</span><span class="st st-sg">&#39;</span>:
+ <span class="kw">return</span> <span class="name">meta</span>.<span class="name">select</span>([<span class="name">users</span>.<span class="name">c</span>.<span class="name">subject_id</span>],
+ <span class="name">users</span>.<span class="name">c</span>.<span class="name">user_id</span> <span class="op">==</span> <span class="bn bn-pseudo">self</span>.<span class="name">subject</span>.<span class="name">user_id</span>
+
+ )
+ <span class="kw">return</span> <span class="name">meta</span>.<span class="name">select</span>([<span class="name">groups</span>.<span class="name">c</span>.<span class="name">subject_id</span>],
+ <span class="name">groups</span>.<span class="name">c</span>.<span class="name">group_id</span> <span class="op">==</span> <span class="bn bn-pseudo">self</span>.<span class="name">subject</span>.<span class="name">group_id</span>
+
+ )
+
+ <span class="kw">def </span><span class="fun">_fetch_privilege</span>(<span class="bn bn-pseudo">self</span>, <span class="name">name</span>):
+ <span class="st st-db">&quot;&quot;&quot;</span><span class="st">Returns the priv_id for the given privilege. If it
+ doesn</span><span class="st st-esc">\&#39;</span><span class="st">t exist by now the system will create a new
+ privilege.</span><span class="st st-db">&quot;&quot;&quot;</span>
+ <span class="name">r</span> <span class="op">=</span> <span class="bn bn-pseudo">self</span>.<span class="name">ctx</span>.<span class="name">engine</span>.<span class="name">execute</span>(<span class="name">meta</span>.<span class="name">select</span>([<span class="name">privileges</span>.<span class="name">c</span>.<span class="name">priv_id</span>],
+ <span class="name">privileges</span>.<span class="name">c</span>.<span class="name">name</span> <span class="op">==</span> <span class="name">name</span>
+
+ ))
+ <span class="name">row</span> <span class="op">=</span> <span class="name">r</span>.<span class="name">fetchone</span>()
+ <span class="kw">if</span> <span class="name">row</span> <span class="op op-word">is</span> <span class="op op-word">not</span> <span class="bn bn-pseudo">None</span>:
+ <span class="kw">return</span> <span class="name">row</span>[<span class="nb nb-int">0</span>]
+ <span class="name">r</span> <span class="op">=</span> <span class="bn bn-pseudo">self</span>.<span class="name">ctx</span>.<span class="name">engine</span>.<span class="name">execute</span>(<span class="name">privileges</span>.<span class="name">insert</span>(),
+ <span class="name">name</span> <span class="op">=</span> <span class="name">name</span>
+
+ )
+ <span class="kw">return</span> <span class="name">r</span>.<span class="name">last_inserted_ids</span>()[<span class="nb nb-int">0</span>]
+
+ <span class="kw">def </span><span class="fun">__repr__</span>(<span class="bn bn-pseudo">self</span>):
+ <span class="kw">if</span> <span class="bn bn-pseudo">self</span>.<span class="name">_type</span> <span class="op">==</span> <span class="st st-sg">&#39;</span><span class="st">user</span><span class="st st-sg">&#39;</span>:
+ <span class="name">id_</span> <span class="op">=</span> <span class="bn bn-pseudo">self</span>.<span class="name">subject</span>.<span class="name">user_id</span>
+
+ <span class="kw">else</span>:
+ <span class="name">id_</span> <span class="op">=</span> <span class="bn bn-pseudo">self</span>.<span class="name">subject</span>.<span class="name">group_id</span>
+ <span class="kw">if</span> <span class="bn bn-pseudo">self</span>.<span class="name">subject</span>.<span class="name">subject_id</span> <span class="op op-word">is</span> <span class="bn bn-pseudo">None</span>:
+ <span class="kw">return</span> <span class="st st-sg">&#39;</span><span class="st">&lt;</span><span class="st st-int">%s</span><span class="st"> </span><span class="st st-int">%s</span><span class="st">:</span><span class="st st-int">%d</span><span class="st"> inactive&gt;</span><span class="st st-sg">&#39;</span> <span class="op">%</span> (
+ <span class="bn bn-pseudo">self</span>.<span class="name">__class__</span>.<span class="name">__name__</span>,
+ <span class="bn bn-pseudo">self</span>.<span class="name">_type</span>,
+ <span class="name">id_</span>
+
+ )
+ <span class="kw">return</span> <span class="st st-sg">&#39;</span><span class="st">&lt;</span><span class="st st-int">%s</span><span class="st"> </span><span class="st st-int">%s</span><span class="st">:</span><span class="st st-int">%d</span><span class="st"> active as </span><span class="st st-int">%d</span><span class="st">&gt;</span><span class="st st-sg">&#39;</span> <span class="op">%</span> (
+ <span class="bn bn-pseudo">self</span>.<span class="name">__class__</span>.<span class="name">__name__</span>,
+ <span class="bn bn-pseudo">self</span>.<span class="name">_type</span>,
+ <span class="name">id_</span>,
+ <span class="bn bn-pseudo">self</span>.<span class="name">subject</span>.<span class="name">subject_id</span>
+
+ )
+<span class="cm"># -*- coding: utf-8 -*-</span>
+<span class="st st-db">&quot;&quot;&quot;</span><span class="st">
+ pocoo.pkg.core.auth
+ ~~~~~~~~~~~~~~~~~~~
+
+ Default authentication module.
+
+ :copyright: 2006 by Armin Ronacher.
+ :license: GNU GPL, see LICENSE for more details.
+</span><span class="st st-db">&quot;&quot;&quot;</span>
+
+<span class="kw">from </span><span class="cls">datetime</span><span class="kw"> import</span> <span class="name">datetime</span>
+<span class="kw">from </span><span class="cls">pocoo.context</span><span class="kw"> import</span> <span class="name">Component</span>
+
+<span class="kw">from </span><span class="cls">pocoo.utils.net</span><span class="kw"> import</span> <span class="name">IP</span>
+<span class="kw">from </span><span class="cls">pocoo.application</span><span class="kw"> import</span> <span class="name">RequestWrapper</span>
+<span class="kw">from </span><span class="cls">pocoo.settings</span><span class="kw"> import</span> <span class="name">cfg</span>
+
+<span class="kw">from </span><span class="cls">pocoo.pkg.core.user</span><span class="kw"> import</span> <span class="name">User</span>, <span class="name">check_login_data</span>
+
+
+<span class="kw">class </span><span class="cls">AuthProvider</span>(<span class="name">Component</span>):
+
+ <span class="deco">@property</span>
+
+ <span class="kw">def </span><span class="fun">auth_name</span>(<span class="bn bn-pseudo">self</span>):
+ <span class="st st-db">&quot;&quot;&quot;</span><span class="st">
+ has to return the name of the auth module for the configuration
+ file. This name defaults to the classname.
+ </span><span class="st st-db">&quot;&quot;&quot;</span>
+ <span class="kw">return</span> <span class="bn bn-pseudo">self</span>.<span class="name">__class__</span>.<span class="name">__name__</span>
+
+ <span class="kw">def </span><span class="fun">get_user</span>(<span class="bn bn-pseudo">self</span>, <span class="name">req</span>):
+ <span class="st st-db">&quot;&quot;&quot;</span><span class="st">
+ This method should either return a valid `User object`_ or ``None``.
+
+ .. _User object: pocoo.pkg.core.user
+ </span><span class="st st-db">&quot;&quot;&quot;</span>
+
+ <span class="kw">def </span><span class="fun">get_user_id</span>(<span class="bn bn-pseudo">self</span>, <span class="name">session_dict</span>):
+ <span class="st st-db">&quot;&quot;&quot;</span><span class="st">
+
+ This method should either return the user_id of the user or ``None``.
+ </span><span class="st st-db">&quot;&quot;&quot;</span>
+
+ <span class="kw">def </span><span class="fun">do_login</span>(<span class="bn bn-pseudo">self</span>, <span class="name">req</span>, <span class="name">username</span>, <span class="name">password</span>):
+ <span class="st st-db">&quot;&quot;&quot;</span><span class="st">
+
+ This method should update the user session so that the auth provider
+ can recognize the user in the ``get_user`` method.
+ It has to return a valid ``HttpResponse``, for redirecting to external
+ login scripts or ``False``, to display an error message (login failed).
+ If it returns ``True`` pocoo will redirect to the last visited page.
+ </span><span class="st st-db">&quot;&quot;&quot;</span>
+
+ <span class="kw">def </span><span class="fun">do_logout</span>(<span class="bn bn-pseudo">self</span>, <span class="name">req</span>):
+ <span class="st st-db">&quot;&quot;&quot;</span><span class="st">
+ This method should return a valid ``Response`` for redirecting
+ to external scripts or ``None``.
+ </span><span class="st st-db">&quot;&quot;&quot;</span>
+
+<span class="kw">class </span><span class="cls">SessionAuth</span>(<span class="name">AuthProvider</span>):
+
+ <span class="kw">def </span><span class="fun">get_user</span>(<span class="bn bn-pseudo">self</span>, <span class="name">req</span>):
+ <span class="kw">try</span>:
+ <span class="name">user_id</span> <span class="op">=</span> <span class="name">req</span>.<span class="name">session</span>[<span class="st st-sg">&#39;</span><span class="st">user_id</span><span class="st st-sg">&#39;</span>]
+ <span class="kw">return</span> <span class="name">User</span>(<span class="bn bn-pseudo">self</span>.<span class="name">ctx</span>, <span class="name">user_id</span>)
+ <span class="kw">except</span> (<span class="exc">KeyError</span>, <span class="name">User</span>.<span class="name">NotFound</span>):
+ <span class="kw">return</span> <span class="bn bn-pseudo">None</span>
+
+ <span class="kw">def </span><span class="fun">do_login</span>(<span class="bn bn-pseudo">self</span>, <span class="name">req</span>, <span class="name">username</span>, <span class="name">password</span>):
+ <span class="name">user_id</span> <span class="op">=</span> <span class="name">check_login_data</span>(<span class="name">req</span>.<span class="name">ctx</span>, <span class="name">username</span>, <span class="name">password</span>)
+ <span class="kw">if</span> <span class="name">user_id</span> <span class="op op-word">is</span> <span class="op op-word">not</span> <span class="bn bn-pseudo">None</span>:
+ <span class="name">req</span>.<span class="name">session</span>[<span class="st st-sg">&#39;</span><span class="st">user_id</span><span class="st st-sg">&#39;</span>] <span class="op">=</span> <span class="name">user_id</span>
+
+ <span class="kw">return</span> <span class="bn bn-pseudo">True</span>
+ <span class="kw">return</span> <span class="bn bn-pseudo">False</span>
+
+ <span class="kw">def </span><span class="fun">do_logout</span>(<span class="bn bn-pseudo">self</span>, <span class="name">req</span>):
+ <span class="kw">if</span> <span class="st st-sg">&#39;</span><span class="st">user_id</span><span class="st st-sg">&#39;</span> <span class="op op-word">in</span> <span class="name">req</span>.<span class="name">session</span>:
+ <span class="name">req</span>.<span class="name">session</span>.<span class="name">pop</span>(<span class="st st-sg">&#39;</span><span class="st">user_id</span><span class="st st-sg">&#39;</span>)
+
+ <span class="kw">def </span><span class="fun">get_user_id</span>(<span class="bn bn-pseudo">self</span>, <span class="name">session_dict</span>):
+ <span class="kw">return</span> <span class="name">session_dict</span>.<span class="name">get</span>(<span class="st st-sg">&#39;</span><span class="st">user_id</span><span class="st st-sg">&#39;</span>)
+
+
+
+<span class="kw">class </span><span class="cls">AuthWrapper</span>(<span class="name">RequestWrapper</span>):
+
+ <span class="kw">def </span><span class="fun">get_priority</span>(<span class="bn bn-pseudo">self</span>):
+ <span class="cm"># after SessionWrapper</span>
+ <span class="kw">return</span> <span class="nb nb-int">3</span>
+
+ <span class="kw">def </span><span class="fun">process_request</span>(<span class="bn bn-pseudo">self</span>, <span class="name">req</span>):
+ <span class="cm"># XXX: what to do with uid?</span>
+ <span class="name">uid</span> <span class="op">=</span> <span class="name">req</span>.<span class="name">session</span>.<span class="name">get</span>(<span class="st st-sg">&#39;</span><span class="st">user_id</span><span class="st st-sg">&#39;</span>, <span class="op">-</span><span class="nb nb-int">1</span>)
+ <span class="name">req</span>.<span class="name">auth</span> <span class="op">=</span> <span class="name">AuthController</span>(<span class="name">req</span>)
+ <span class="name">req</span>.<span class="name">user</span> <span class="op">=</span> <span class="name">req</span>.<span class="name">auth</span>.<span class="name">get_user</span>()
+
+ <span class="kw">def </span><span class="fun">process_response</span>(<span class="bn bn-pseudo">self</span>, <span class="name">req</span>, <span class="name">resp</span>):
+ <span class="kw">return</span> <span class="name">resp</span>
+
+
+<span class="kw">def </span><span class="fun">get_auth_provider_mapping</span>(<span class="name">ctx</span>):
+ <span class="st st-db">&quot;&quot;&quot;</span><span class="st">Returns a list of auth providers.</span><span class="st st-db">&quot;&quot;&quot;</span>
+ <span class="name">providers</span> <span class="op">=</span> {}
+ <span class="kw">for</span> <span class="name">comp</span> <span class="op op-word">in</span> <span class="name">ctx</span>.<span class="name">get_components</span>(<span class="name">AuthProvider</span>):
+ <span class="name">providers</span>[<span class="name">comp</span>.<span class="name">auth_name</span>] <span class="op">=</span> <span class="name">comp</span>
+
+ <span class="kw">return</span> <span class="name">providers</span>
+
+
+<span class="kw">def </span><span class="fun">get_auth_provider</span>(<span class="name">ctx</span>):
+ <span class="st st-db">&quot;&quot;&quot;</span><span class="st">Returns the enabled auth provider.</span><span class="st st-db">&quot;&quot;&quot;</span>
+ <span class="kw">if</span> <span class="st st-sg">&#39;</span><span class="st">auth/provider</span><span class="st st-sg">&#39;</span> <span class="op op-word">not</span> <span class="op op-word">in</span> <span class="name">ctx</span>.<span class="name">_cache</span>:
+ <span class="name">providers</span> <span class="op">=</span> <span class="name">get_auth_provider_mapping</span>(<span class="name">ctx</span>)
+ <span class="name">provider</span> <span class="op">=</span> <span class="name">providers</span>[<span class="name">ctx</span>.<span class="name">cfg</span>.<span class="name">get</span>(<span class="st st-sg">&#39;</span><span class="st">general</span><span class="st st-sg">&#39;</span>, <span class="st st-sg">&#39;</span><span class="st">auth_module</span><span class="st st-sg">&#39;</span>)]
+ <span class="name">ctx</span>.<span class="name">_cache</span>[<span class="st st-sg">&#39;</span><span class="st">auth/provider</span><span class="st st-sg">&#39;</span>] <span class="op">=</span> <span class="name">provider</span>
+
+ <span class="kw">return</span> <span class="name">ctx</span>.<span class="name">_cache</span>[<span class="st st-sg">&#39;</span><span class="st">auth/provider</span><span class="st st-sg">&#39;</span>]
+
+
+<span class="kw">class </span><span class="cls">AuthController</span>(<span class="bn">object</span>):
+ <span class="name">auth_provider</span> <span class="op">=</span> <span class="name">cfg</span>.<span class="name">str</span>(<span class="st st-sg">&#39;</span><span class="st">general</span><span class="st st-sg">&#39;</span>, <span class="st st-sg">&#39;</span><span class="st">auth_module</span><span class="st st-sg">&#39;</span>)
+
+ <span class="kw">def </span><span class="fun">__init__</span>(<span class="bn bn-pseudo">self</span>, <span class="name">req</span>):
+ <span class="bn bn-pseudo">self</span>.<span class="name">ctx</span> <span class="op">=</span> <span class="name">req</span>.<span class="name">ctx</span>
+
+ <span class="bn bn-pseudo">self</span>.<span class="name">req</span> <span class="op">=</span> <span class="name">req</span>
+ <span class="bn bn-pseudo">self</span>.<span class="name">provider</span> <span class="op">=</span> <span class="name">get_auth_provider</span>(<span class="name">req</span>.<span class="name">ctx</span>)
+
+ <span class="kw">def </span><span class="fun">get_user</span>(<span class="bn bn-pseudo">self</span>):
+ <span class="st st-db">&quot;&quot;&quot;</span><span class="st">
+
+ Returns the user for this request
+ </span><span class="st st-db">&quot;&quot;&quot;</span>
+ <span class="name">user</span> <span class="op">=</span> <span class="bn bn-pseudo">self</span>.<span class="name">provider</span>.<span class="name">get_user</span>(<span class="bn bn-pseudo">self</span>.<span class="name">req</span>)
+ <span class="kw">if</span> <span class="name">user</span> <span class="op op-word">is</span> <span class="op op-word">not</span> <span class="bn bn-pseudo">None</span>:
+ <span class="name">user</span>.<span class="name">ip</span> <span class="op">=</span> <span class="name">IP</span>(<span class="bn bn-pseudo">self</span>.<span class="name">req</span>.<span class="name">environ</span>[<span class="st st-sg">&#39;</span><span class="st">REMOTE_ADDR</span><span class="st st-sg">&#39;</span>])
+ <span class="kw">return</span> <span class="name">user</span>
+
+ <span class="cm"># return anonymous user</span>
+ <span class="kw">return</span> <span class="name">User</span>(<span class="bn bn-pseudo">self</span>.<span class="name">ctx</span>, <span class="op">-</span><span class="nb nb-int">1</span>)
+
+ <span class="kw">def </span><span class="fun">do_login</span>(<span class="bn bn-pseudo">self</span>, <span class="name">username</span>, <span class="name">password</span>):
+ <span class="st st-db">&quot;&quot;&quot;</span><span class="st">
+
+ Returns a valid ``Response``, for redirecting to external
+ login scripts or ``False``, to display an error message (login failed).
+ If it returns ``True`` pocoo should redirect to the last visited page.
+ </span><span class="st st-db">&quot;&quot;&quot;</span>
+ <span class="name">rv</span> <span class="op">=</span> <span class="bn bn-pseudo">self</span>.<span class="name">provider</span>.<span class="name">do_login</span>(<span class="bn bn-pseudo">self</span>.<span class="name">req</span>, <span class="name">username</span>, <span class="name">password</span>)
+ <span class="kw">if</span> <span class="name">rv</span> <span class="op op-word">is</span> <span class="op op-word">not</span> <span class="bn bn-pseudo">False</span>:
+ <span class="bn bn-pseudo">self</span>.<span class="name">req</span>.<span class="name">user</span> <span class="op">=</span> <span class="bn bn-pseudo">self</span>.<span class="name">get_user</span>()
+ <span class="kw">return</span> <span class="name">rv</span>
+
+ <span class="kw">return</span> <span class="bn bn-pseudo">False</span>
+
+ <span class="kw">def </span><span class="fun">do_logout</span>(<span class="bn bn-pseudo">self</span>):
+ <span class="st st-db">&quot;&quot;&quot;</span><span class="st">
+ Loggs the user out. Can eiter return None or a Response for
+ external redirects.
+ </span><span class="st st-db">&quot;&quot;&quot;</span>
+ <span class="cm"># update last login time</span>
+
+ <span class="bn bn-pseudo">self</span>.<span class="name">req</span>.<span class="name">user</span>.<span class="name">last_login</span> <span class="op">=</span> <span class="name">datetime</span>.<span class="name">now</span>()
+ <span class="bn bn-pseudo">self</span>.<span class="name">req</span>.<span class="name">user</span>.<span class="name">save</span>()
+ <span class="bn bn-pseudo">self</span>.<span class="name">provider</span>.<span class="name">do_logout</span>(<span class="bn bn-pseudo">self</span>.<span class="name">req</span>)
+ <span class="cm">#XXX: maybe a bit slow</span>
+
+ <span class="bn bn-pseudo">self</span>.<span class="name">req</span>.<span class="name">user</span> <span class="op">=</span> <span class="bn bn-pseudo">self</span>.<span class="name">get_user</span>()
+<span class="cm"># -*- coding: utf-8 -*-</span>
+<span class="st st-db">&quot;&quot;&quot;</span><span class="st">
+ pocoo.pkg.core.bbcode
+ ~~~~~~~~~~~~~~~~~~~~~
+
+ Pocoo BBCode parser.
+
+ :copyright: 2006 by Georg Brandl, Armin Ronacher.
+ :license: GNU GPL, see LICENSE for more details.
+
+</span><span class="st st-db">&quot;&quot;&quot;</span>
+<span class="kw">import </span><span class="cls">re</span>
+
+<span class="kw">from </span><span class="cls">pocoo</span><span class="kw"> import</span> <span class="name">Component</span>
+<span class="kw">from </span><span class="cls">pocoo.pkg.core.textfmt</span><span class="kw"> import</span> <span class="name">MarkupFormat</span>
+
+<span class="kw">from </span><span class="cls">pocoo.pkg.core.smilies</span><span class="kw"> import</span> <span class="name">get_smiley_buttons</span>, <span class="name">replace_smilies</span>
+<span class="kw">from </span><span class="cls">pocoo.utils.html</span><span class="kw"> import</span> <span class="name">escape_html</span>, <span class="name">translate_color</span>
+
+<span class="kw">from </span><span class="cls">pocoo.utils.activecache</span><span class="kw"> import</span> <span class="name">Node</span>, <span class="name">CallbackNode</span>, <span class="name">NodeList</span>
+
+<span class="name">tag_re</span> <span class="op">=</span> <span class="name">re</span>.<span class="name">compile</span>(<span class="st st-sg">r&#39;</span><span class="st">(\[(/?[a-zA-Z0-9]+)(?:=(&amp;quot;.+?&amp;quot;|.+?))?\])</span><span class="st st-sg">&#39;</span>)
+
+
+
+<span class="kw">class </span><span class="cls">EndOfText</span>(<span class="exc">Exception</span>):
+ <span class="st st-db">&quot;&quot;&quot;</span><span class="st">Raise when the end of the text is reached.</span><span class="st st-db">&quot;&quot;&quot;</span>
+
+
+<span class="kw">class </span><span class="cls">TokenList</span>(<span class="bn">list</span>):
+ <span class="st st-db">&quot;&quot;&quot;</span><span class="st">A subclass of a list for tokens which allows to flatten
+ the tokens so that the original bbcode is the return value.</span><span class="st st-db">&quot;&quot;&quot;</span>
+
+ <span class="kw">def </span><span class="fun">flatten</span>(<span class="bn bn-pseudo">self</span>):
+ <span class="kw">return</span> <span class="name">u</span><span class="st st-sg">&#39;&#39;</span>.<span class="name">join</span>(<span class="name">token</span>.<span class="name">raw</span> <span class="kw">for</span> <span class="name">token</span> <span class="op op-word">in</span> <span class="bn bn-pseudo">self</span>)
+
+ <span class="kw">def </span><span class="fun">__repr__</span>(<span class="bn bn-pseudo">self</span>):
+ <span class="kw">return</span> <span class="st st-sg">&#39;</span><span class="st">&lt;</span><span class="st st-int">%s</span><span class="st"> </span><span class="st st-int">%s</span><span class="st">&gt;</span><span class="st st-sg">&#39;</span> <span class="op">%</span> (
+ <span class="bn bn-pseudo">self</span>.<span class="name">__class__</span>.<span class="name">__name__</span>,
+ <span class="bn">list</span>.<span class="name">__repr__</span>(<span class="bn bn-pseudo">self</span>)
+ )
+
+
+
+<span class="kw">class </span><span class="cls">Token</span>(<span class="bn">object</span>):
+ <span class="st st-db">&quot;&quot;&quot;</span><span class="st">Token Baseclass</span><span class="st st-db">&quot;&quot;&quot;</span>
+
+ <span class="kw">def </span><span class="fun">__repr__</span>(<span class="bn bn-pseudo">self</span>):
+ <span class="kw">return</span> <span class="st st-sg">&#39;</span><span class="st">&lt;</span><span class="st st-int">%s</span><span class="st"> </span><span class="st st-int">%s</span><span class="st">&gt;</span><span class="st st-sg">&#39;</span> <span class="op">%</span> (
+ <span class="bn bn-pseudo">self</span>.<span class="name">__class__</span>.<span class="name">__name__</span>,
+ <span class="bn bn-pseudo">self</span>.<span class="name">raw</span>
+
+ )
+
+
+<span class="kw">class </span><span class="cls">TextToken</span>(<span class="name">Token</span>):
+ <span class="st st-db">&quot;&quot;&quot;</span><span class="st">A token for plain text.</span><span class="st st-db">&quot;&quot;&quot;</span>
+
+ <span class="kw">def </span><span class="fun">__init__</span>(<span class="bn bn-pseudo">self</span>, <span class="name">data</span>):
+ <span class="bn bn-pseudo">self</span>.<span class="name">data</span> <span class="op">=</span> <span class="bn bn-pseudo">self</span>.<span class="name">raw</span> <span class="op">=</span> <span class="name">data</span>
+
+
+<span class="kw">class </span><span class="cls">TagToken</span>(<span class="name">Token</span>):
+ <span class="st st-db">&quot;&quot;&quot;</span><span class="st">A token for tags.</span><span class="st st-db">&quot;&quot;&quot;</span>
+
+ <span class="kw">def </span><span class="fun">__init__</span>(<span class="bn bn-pseudo">self</span>, <span class="name">raw</span>, <span class="name">tagname</span>, <span class="name">attr</span>):
+ <span class="bn bn-pseudo">self</span>.<span class="name">raw</span> <span class="op">=</span> <span class="name">raw</span>
+
+ <span class="bn bn-pseudo">self</span>.<span class="name">name</span> <span class="op">=</span> <span class="name">tagname</span>
+ <span class="bn bn-pseudo">self</span>.<span class="name">attr</span> <span class="op">=</span> <span class="name">attr</span>
+
+
+<span class="kw">class </span><span class="cls">Parser</span>(<span class="bn">object</span>):
+ <span class="st st-db">&quot;&quot;&quot;</span><span class="st">
+ BBCode Parser Class
+ </span><span class="st st-db">&quot;&quot;&quot;</span>
+
+ <span class="kw">def </span><span class="fun">__init__</span>(<span class="bn bn-pseudo">self</span>, <span class="name">ctx</span>, <span class="name">text</span>, <span class="name">handlers</span>, <span class="name">allowed_tags</span>):
+ <span class="bn bn-pseudo">self</span>.<span class="name">ctx</span> <span class="op">=</span> <span class="name">ctx</span>
+
+ <span class="bn bn-pseudo">self</span>.<span class="name">_tokens</span> <span class="op">=</span> <span class="name">tag_re</span>.<span class="name">split</span>(<span class="name">text</span>)
+ <span class="bn bn-pseudo">self</span>.<span class="name">_tokens</span>.<span class="name">reverse</span>()
+ <span class="bn bn-pseudo">self</span>.<span class="name">_is_text</span> <span class="op">=</span> <span class="bn bn-pseudo">True</span>
+
+ <span class="bn bn-pseudo">self</span>.<span class="name">_cache</span> <span class="op">=</span> []
+ <span class="bn bn-pseudo">self</span>.<span class="name">_handlers</span> <span class="op">=</span> <span class="name">handlers</span>
+ <span class="bn bn-pseudo">self</span>.<span class="name">_allowed_tags</span> <span class="op">=</span> <span class="name">allowed_tags</span>
+
+ <span class="kw">def </span><span class="fun">tag_allowed</span>(<span class="bn bn-pseudo">self</span>, <span class="name">tagname</span>):
+ <span class="st st-db">&quot;&quot;&quot;</span><span class="st">
+ Check if a tagname is allowed for this parser.
+ </span><span class="st st-db">&quot;&quot;&quot;</span>
+ <span class="kw">if</span> <span class="bn bn-pseudo">self</span>.<span class="name">_allowed_tags</span> <span class="op op-word">is</span> <span class="bn bn-pseudo">None</span>:
+ <span class="kw">return</span> <span class="bn bn-pseudo">True</span>
+
+ <span class="kw">return</span> <span class="name">tagname</span> <span class="op op-word">in</span> <span class="bn bn-pseudo">self</span>.<span class="name">_allowed_tags</span>
+
+ <span class="kw">def </span><span class="fun">get_next_token</span>(<span class="bn bn-pseudo">self</span>):
+ <span class="st st-db">&quot;&quot;&quot;</span><span class="st">
+
+ Fetch the next raw token from the text
+ Raise ``EndOfText`` if not further token exists.
+ </span><span class="st st-db">&quot;&quot;&quot;</span>
+ <span class="kw">if</span> <span class="bn bn-pseudo">self</span>.<span class="name">_cache</span>:
+ <span class="kw">return</span> <span class="bn bn-pseudo">self</span>.<span class="name">_cache</span>.<span class="name">pop</span>()
+ <span class="name">get_token</span> <span class="op">=</span> <span class="bn bn-pseudo">self</span>.<span class="name">_tokens</span>.<span class="name">pop</span>
+
+ <span class="kw">if</span> <span class="op op-word">not</span> <span class="bn bn-pseudo">self</span>.<span class="name">_tokens</span>:
+ <span class="kw">raise</span> <span class="name">EndOfText</span>()
+ <span class="kw">if</span> <span class="bn bn-pseudo">self</span>.<span class="name">_is_text</span>:
+ <span class="bn bn-pseudo">self</span>.<span class="name">_is_text</span> <span class="op">=</span> <span class="bn bn-pseudo">False</span>
+
+ <span class="kw">return</span> <span class="name">TextToken</span>(<span class="name">get_token</span>())
+ <span class="kw">else</span>:
+ <span class="bn bn-pseudo">self</span>.<span class="name">_is_text</span> <span class="op">=</span> <span class="bn bn-pseudo">True</span>
+
+ <span class="name">raw</span> <span class="op">=</span> <span class="name">get_token</span>()
+ <span class="name">tagname</span> <span class="op">=</span> <span class="name">get_token</span>().<span class="name">lower</span>()
+ <span class="name">attr</span> <span class="op">=</span> <span class="name">get_token</span>()
+ <span class="kw">if</span> <span class="name">attr</span> <span class="op op-word">and</span> <span class="name">attr</span>[:<span class="nb nb-int">6</span>] <span class="op">==</span> <span class="name">attr</span>[<span class="op">-</span><span class="nb nb-int">6</span>:] <span class="op">==</span> <span class="st st-sg">&#39;</span><span class="st">&amp;quot;</span><span class="st st-sg">&#39;</span>:
+ <span class="name">attr</span> <span class="op">=</span> <span class="name">attr</span>[<span class="nb nb-int">6</span>:<span class="op">-</span><span class="nb nb-int">6</span>]
+ <span class="kw">return</span> <span class="name">TagToken</span>(<span class="name">raw</span>, <span class="name">tagname</span>, <span class="name">attr</span>)
+
+ <span class="kw">def </span><span class="fun">push_token</span>(<span class="bn bn-pseudo">self</span>, <span class="name">token</span>):
+ <span class="st st-db">&quot;&quot;&quot;</span><span class="st">
+
+ Pushes the last fetched token in a cache so that the next time
+ you call ``get_next_token`` returns the pushed token.
+ </span><span class="st st-db">&quot;&quot;&quot;</span>
+ <span class="bn bn-pseudo">self</span>.<span class="name">_cache</span>.<span class="name">append</span>(<span class="name">token</span>)
+
+ <span class="kw">def </span><span class="fun">parse</span>(<span class="bn bn-pseudo">self</span>, <span class="name">needle</span><span class="op">=</span><span class="bn bn-pseudo">None</span>, <span class="name">preserve_needle</span><span class="op">=</span><span class="bn bn-pseudo">False</span>):
+ <span class="st st-db">&quot;&quot;&quot;</span><span class="st">
+
+ Parses the text until ``needle`` or the end of text if not defined.
+ If it finds the needle it will delete the needle token. If you want
+ the needle token too set ``preserve_needle`` to ``True``.
+
+ In comparison with the ``get_tokens`` method this method will call
+ the node handlers for each node.
+ </span><span class="st st-db">&quot;&quot;&quot;</span>
+ <span class="name">result</span> <span class="op">=</span> <span class="name">NodeList</span>()
+ <span class="kw">try</span>:
+ <span class="kw">while</span> <span class="bn bn-pseudo">True</span>:
+ <span class="name">token</span> <span class="op">=</span> <span class="bn bn-pseudo">self</span>.<span class="name">get_next_token</span>()
+ <span class="kw">if</span> <span class="bn">isinstance</span>(<span class="name">token</span>, <span class="name">TagToken</span>) <span class="op op-word">and</span> <span class="name">token</span>.<span class="name">name</span> <span class="op">==</span> <span class="name">needle</span>:
+ <span class="kw">if</span> <span class="name">preserve_needle</span>:
+ <span class="bn bn-pseudo">self</span>.<span class="name">push_token</span>(<span class="name">token</span>)
+ <span class="kw">break</span>
+
+ <span class="name">result</span>.<span class="name">append</span>(<span class="bn bn-pseudo">self</span>.<span class="name">get_node</span>(<span class="name">token</span>))
+ <span class="kw">except</span> <span class="name">EndOfText</span>:
+ <span class="kw">pass</span>
+
+ <span class="kw">return</span> <span class="name">result</span>
+
+ <span class="kw">def </span><span class="fun">get_tokens</span>(<span class="bn bn-pseudo">self</span>, <span class="name">needle</span><span class="op">=</span><span class="bn bn-pseudo">None</span>, <span class="name">preserve_needle</span><span class="op">=</span><span class="bn bn-pseudo">False</span>):
+ <span class="st st-db">&quot;&quot;&quot;</span><span class="st">
+
+ Like ``parse`` but returns an unparsed TokenList. Basically you
+ would never need this method except for preserved areas like
+ Code blocks etc.
+ </span><span class="st st-db">&quot;&quot;&quot;</span>
+ <span class="name">result</span> <span class="op">=</span> <span class="name">TokenList</span>()
+ <span class="kw">try</span>:
+ <span class="kw">while</span> <span class="bn bn-pseudo">True</span>:
+ <span class="name">token</span> <span class="op">=</span> <span class="bn bn-pseudo">self</span>.<span class="name">get_next_token</span>()
+ <span class="kw">if</span> <span class="bn">isinstance</span>(<span class="name">token</span>, <span class="name">TagToken</span>) <span class="op op-word">and</span> <span class="name">token</span>.<span class="name">name</span> <span class="op">==</span> <span class="name">needle</span>:
+ <span class="kw">if</span> <span class="name">preserve_needle</span>:
+ <span class="bn bn-pseudo">self</span>.<span class="name">push_token</span>(<span class="name">token</span>)
+ <span class="kw">break</span>
+
+ <span class="name">result</span>.<span class="name">append</span>(<span class="name">token</span>)
+ <span class="kw">except</span> <span class="name">EndOfText</span>:
+ <span class="kw">pass</span>
+ <span class="kw">return</span> <span class="name">result</span>
+
+ <span class="kw">def </span><span class="fun">get_node</span>(<span class="bn bn-pseudo">self</span>, <span class="name">token</span>):
+ <span class="st st-db">&quot;&quot;&quot;</span><span class="st">
+ Return the node for a token. If the token was a ``TextToken``
+ the resulting node will call ``get_text_node`` which returns a
+ </span><span class="st st-esc">\n</span><span class="st"> to &lt;br/&gt; replaced version of the token value wrapped in a
+ plain ``Node``. In all other cases it will try to lookup the node
+ in the list of registered token handlers.
+
+ If this fails it wraps the raw token value in a ``Node``.
+ </span><span class="st st-db">&quot;&quot;&quot;</span>
+
+ <span class="kw">if</span> <span class="bn">isinstance</span>(<span class="name">token</span>, <span class="name">TextToken</span>):
+ <span class="kw">return</span> <span class="bn bn-pseudo">self</span>.<span class="name">get_text_node</span>(<span class="name">token</span>.<span class="name">data</span>)
+ <span class="kw">if</span> <span class="bn bn-pseudo">self</span>.<span class="name">tag_allowed</span>(<span class="name">token</span>.<span class="name">name</span>):
+ <span class="kw">for</span> <span class="name">handler</span> <span class="op op-word">in</span> <span class="bn bn-pseudo">self</span>.<span class="name">_handlers</span>:
+ <span class="name">rv</span> <span class="op">=</span> <span class="name">handler</span>.<span class="name">get_node</span>(<span class="name">token</span>, <span class="bn bn-pseudo">self</span>)
+ <span class="kw">if</span> <span class="name">rv</span> <span class="op op-word">is</span> <span class="op op-word">not</span> <span class="bn bn-pseudo">None</span>:
+ <span class="kw">if</span> <span class="bn">isinstance</span>(<span class="name">rv</span>, <span class="name">Node</span>):
+ <span class="kw">return</span> <span class="name">rv</span>
+
+ <span class="kw">return</span> <span class="name">Node</span>(<span class="name">rv</span>)
+ <span class="kw">return</span> <span class="bn bn-pseudo">self</span>.<span class="name">get_text_node</span>(<span class="name">token</span>.<span class="name">raw</span>)
+
+ <span class="kw">def </span><span class="fun">get_text_node</span>(<span class="bn bn-pseudo">self</span>, <span class="name">data</span>):
+ <span class="st st-db">&quot;&quot;&quot;</span><span class="st">
+
+ Newline replaces the text and wraps it in an ``Node``.
+ </span><span class="st st-db">&quot;&quot;&quot;</span>
+ <span class="name">text</span> <span class="op">=</span> <span class="name">replace_smilies</span>(<span class="bn bn-pseudo">self</span>.<span class="name">ctx</span>, <span class="name">data</span>)
+ <span class="kw">return</span> <span class="name">Node</span>(<span class="name">re</span>.<span class="name">sub</span>(<span class="st st-sg">r&#39;</span><span class="st">\r?\n</span><span class="st st-sg">&#39;</span>, <span class="st st-sg">&#39;</span><span class="st">&lt;br /&gt;</span><span class="st st-esc">\n</span><span class="st st-sg">&#39;</span>, <span class="name">text</span>))
+
+ <span class="kw">def </span><span class="fun">wrap_render</span>(<span class="bn bn-pseudo">self</span>, <span class="name">tag</span>, <span class="name">parse_until</span>):
+ <span class="st st-db">&quot;&quot;&quot;</span><span class="st">
+
+ Renders untile ``parse_until`` and wraps it in the html tag ``tag``.
+ </span><span class="st st-db">&quot;&quot;&quot;</span>
+ <span class="kw">return</span> <span class="name">NodeList</span>(<span class="name">Node</span>(<span class="st st-sg">&#39;</span><span class="st">&lt;</span><span class="st st-int">%s</span><span class="st">&gt;</span><span class="st st-sg">&#39;</span> <span class="op">%</span> <span class="name">tag</span>), <span class="bn bn-pseudo">self</span>.<span class="name">parse</span>(<span class="name">parse_until</span>),
+ <span class="name">Node</span>(<span class="st st-sg">&#39;</span><span class="st">&lt;/</span><span class="st st-int">%s</span><span class="st">&gt;</span><span class="st st-sg">&#39;</span> <span class="op">%</span> <span class="name">tag</span>))
+
+ <span class="kw">def </span><span class="fun">joined_render</span>(<span class="bn bn-pseudo">self</span>, <span class="op">*</span><span class="name">args</span>):
+ <span class="st st-db">&quot;&quot;&quot;</span><span class="st">
+
+ Takes a number of arguments which are either strings, unicode objects
+ or nodes. It creates a new newlist, iterates over all arguments and
+ converts all to nodes if not happened by now.
+ </span><span class="st st-db">&quot;&quot;&quot;</span>
+ <span class="name">result</span> <span class="op">=</span> <span class="name">NodeList</span>()
+ <span class="kw">for</span> <span class="name">arg</span> <span class="op op-word">in</span> <span class="name">args</span>:
+ <span class="kw">if</span> <span class="bn">isinstance</span>(<span class="name">arg</span>, <span class="name">Node</span>):
+ <span class="name">result</span>.<span class="name">append</span>(<span class="name">arg</span>)
+ <span class="kw">else</span>:
+ <span class="name">result</span>.<span class="name">append</span>(<span class="name">Node</span>(<span class="name">arg</span>))
+ <span class="kw">return</span> <span class="name">result</span>
+
+ <span class="kw">def </span><span class="fun">callback</span>(<span class="bn bn-pseudo">self</span>, <span class="name">callback</span>, <span class="name">data</span>):
+ <span class="st st-db">&quot;&quot;&quot;</span><span class="st">
+ Returns a new ``CallbackNode``. Don&#39;t create callback nodes on your
+ own, this method might do some further magic in the future.
+ </span><span class="st st-db">&quot;&quot;&quot;</span>
+ <span class="kw">return</span> <span class="name">CallbackNode</span>(<span class="name">callback</span>, <span class="op">*</span><span class="name">data</span>)
+
+
+
+<span class="kw">class </span><span class="cls">BBCodeTagProvider</span>(<span class="name">Component</span>):
+ <span class="cm">#: list of handled tags</span>
+ <span class="name">tags</span> <span class="op">=</span> []
+
+ <span class="cm">#: list of callbacks</span>
+ <span class="name">callbacks</span> <span class="op">=</span> []
+
+ <span class="kw">def </span><span class="fun">get_node</span>(<span class="bn bn-pseudo">self</span>, <span class="name">token</span>, <span class="name">parser</span>):
+ <span class="st st-db">&quot;&quot;&quot;</span><span class="st">
+
+ Is called when a tag is found. It must return a valid ``Node``
+ or a string which is automatically wrapped into a plain ``Node``.
+ </span><span class="st st-db">&quot;&quot;&quot;</span>
+
+ <span class="kw">def </span><span class="fun">render_callback</span>(<span class="bn bn-pseudo">self</span>, <span class="name">req</span>, <span class="name">callback</span>, <span class="name">data</span>):
+ <span class="st st-db">&quot;&quot;&quot;</span><span class="st">
+
+ Has to handle a callback for ``callback`` with ``data`` and return a
+ string
+ </span><span class="st st-db">&quot;&quot;&quot;</span>
+ <span class="kw">return</span> <span class="name">u</span><span class="st st-sg">&#39;&#39;</span>
+
+ <span class="kw">def </span><span class="fun">get_buttons</span>(<span class="bn bn-pseudo">self</span>, <span class="name">req</span>):
+ <span class="st st-db">&quot;&quot;&quot;</span><span class="st">
+
+ Return a valid button definition for &quot;tagname&quot; or
+ None if no button is required.
+
+ A valid button definition is a dict in the following
+ form::
+
+ {&#39;name&#39;: _(&#39;Bold&#39;),
+ &#39;description&#39;: _(&#39;Insert bold text&#39;),
+ &#39;icon&#39;: self.ctx.make_url(&#39;!cobalt/...&#39;),
+ &#39;insert&#39;: &#39;[b]{text}[/b]&#39;}
+ </span><span class="st st-db">&quot;&quot;&quot;</span>
+
+ <span class="kw">return</span> ()
+
+
+<span class="kw">class </span><span class="cls">BBCode</span>(<span class="name">MarkupFormat</span>):
+ <span class="st st-db">&quot;&quot;&quot;</span><span class="st">
+ BBCode markup format.
+ </span><span class="st st-db">&quot;&quot;&quot;</span>
+ <span class="name">name</span> <span class="op">=</span> <span class="st st-sg">&#39;</span><span class="st">bbcode</span><span class="st st-sg">&#39;</span>
+
+ <span class="name">editor_javascript</span> <span class="op">=</span> <span class="st st-sg">&#39;</span><span class="st">!cobalt/core/pocoo/app/BBCodeEditor.js</span><span class="st st-sg">&#39;</span>
+
+ <span class="kw">def </span><span class="fun">__init__</span>(<span class="bn bn-pseudo">self</span>, <span class="name">ctx</span>):
+ <span class="bn">super</span>(<span class="name">BBCode</span>, <span class="bn bn-pseudo">self</span>).<span class="name">__init__</span>(<span class="name">ctx</span>)
+ <span class="bn bn-pseudo">self</span>.<span class="name">handlers</span> <span class="op">=</span> {}
+ <span class="bn bn-pseudo">self</span>.<span class="name">callbacks</span> <span class="op">=</span> {}
+ <span class="kw">for</span> <span class="name">comp</span> <span class="op op-word">in</span> <span class="name">ctx</span>.<span class="name">get_components</span>(<span class="name">BBCodeTagProvider</span>):
+ <span class="kw">for</span> <span class="name">tag</span> <span class="op op-word">in</span> <span class="name">comp</span>.<span class="name">tags</span>:
+ <span class="bn bn-pseudo">self</span>.<span class="name">handlers</span>.<span class="name">setdefault</span>(<span class="name">tag</span>, []).<span class="name">append</span>(<span class="name">comp</span>)
+ <span class="kw">for</span> <span class="name">callback</span> <span class="op op-word">in</span> <span class="name">comp</span>.<span class="name">callbacks</span>:
+ <span class="bn bn-pseudo">self</span>.<span class="name">callbacks</span>[<span class="name">callback</span>] <span class="op">=</span> <span class="name">comp</span>
+
+ <span class="kw">def </span><span class="fun">get_signature_tags</span>(<span class="bn bn-pseudo">self</span>):
+ <span class="st st-db">&quot;&quot;&quot;</span><span class="st">Returns the allowed signature tags or None if all</span><span class="st st-db">&quot;&quot;&quot;</span>
+ <span class="kw">if</span> <span class="op op-word">not</span> <span class="bn">hasattr</span>(<span class="bn bn-pseudo">self</span>, <span class="st st-sg">&#39;</span><span class="st">_signature_tags</span><span class="st st-sg">&#39;</span>):
+ <span class="name">r</span> <span class="op">=</span> <span class="bn bn-pseudo">self</span>.<span class="name">ctx</span>.<span class="name">cfg</span>.<span class="name">get</span>(<span class="st st-sg">&#39;</span><span class="st">board</span><span class="st st-sg">&#39;</span>, <span class="st st-sg">&#39;</span><span class="st">bbcode_signature_tags</span><span class="st st-sg">&#39;</span>, <span class="st st-sg">&#39;</span><span class="st">ALL</span><span class="st st-sg">&#39;</span>)
+ <span class="kw">if</span> <span class="name">r</span> <span class="op">==</span> <span class="st st-sg">&#39;</span><span class="st">ALL</span><span class="st st-sg">&#39;</span>:
+ <span class="bn bn-pseudo">self</span>.<span class="name">_signature_tags</span> <span class="op">=</span> <span class="bn bn-pseudo">None</span>
+
+ <span class="kw">else</span>:
+ <span class="bn bn-pseudo">self</span>.<span class="name">_signature_tags</span> <span class="op">=</span> [<span class="name">s</span>.<span class="name">strip</span>().<span class="name">lower</span>() <span class="kw">for</span> <span class="name">s</span> <span class="op op-word">in</span> <span class="name">r</span>.<span class="name">split</span>(<span class="st st-sg">&#39;</span><span class="st">,</span><span class="st st-sg">&#39;</span>)]
+ <span class="kw">return</span> <span class="bn bn-pseudo">self</span>.<span class="name">_signature_tags</span>
+
+ <span class="kw">def </span><span class="fun">parse</span>(<span class="bn bn-pseudo">self</span>, <span class="name">text</span>, <span class="name">signature</span>):
+ <span class="name">handlers</span> <span class="op">=</span> <span class="bn bn-pseudo">self</span>.<span class="name">ctx</span>.<span class="name">get_components</span>(<span class="name">BBCodeTagProvider</span>)
+ <span class="name">allowed_tags</span> <span class="op">=</span> <span class="bn bn-pseudo">None</span>
+
+ <span class="kw">if</span> <span class="name">signature</span>:
+ <span class="name">allowed_tags</span> <span class="op">=</span> <span class="bn bn-pseudo">self</span>.<span class="name">get_signature_tags</span>()
+ <span class="name">p</span> <span class="op">=</span> <span class="name">Parser</span>(<span class="bn bn-pseudo">self</span>.<span class="name">ctx</span>, <span class="name">escape_html</span>(<span class="name">text</span>), <span class="name">handlers</span>, <span class="name">allowed_tags</span>)
+ <span class="kw">return</span> <span class="name">p</span>.<span class="name">parse</span>()
+
+ <span class="kw">def </span><span class="fun">render_callback</span>(<span class="bn bn-pseudo">self</span>, <span class="name">req</span>, <span class="name">callback</span>, <span class="name">data</span>):
+ <span class="st st-db">&quot;&quot;&quot;</span><span class="st">Redirect the callback to the BBCode Provider.</span><span class="st st-db">&quot;&quot;&quot;</span>
+
+ <span class="kw">for</span> <span class="name">comp</span> <span class="op op-word">in</span> <span class="bn bn-pseudo">self</span>.<span class="name">ctx</span>.<span class="name">get_components</span>(<span class="name">BBCodeTagProvider</span>):
+ <span class="name">rv</span> <span class="op">=</span> <span class="name">comp</span>.<span class="name">render_callback</span>(<span class="name">req</span>, <span class="name">callback</span>, <span class="name">data</span>)
+ <span class="kw">if</span> <span class="name">rv</span> <span class="op op-word">is</span> <span class="op op-word">not</span> <span class="bn bn-pseudo">None</span>:
+ <span class="kw">return</span> <span class="name">rv</span>
+
+ <span class="kw">raise</span> <span class="exc">Exception</span>(<span class="st st-sg">&#39;</span><span class="st">unhandled callback </span><span class="st st-int">%r</span><span class="st st-sg">&#39;</span> <span class="op">%</span> <span class="name">callback</span>)
+
+ <span class="kw">def </span><span class="fun">quote_text</span>(<span class="bn bn-pseudo">self</span>, <span class="name">req</span>, <span class="name">text</span>, <span class="name">username</span><span class="op">=</span><span class="bn bn-pseudo">None</span>):
+ <span class="kw">if</span> <span class="name">username</span> <span class="op op-word">is</span> <span class="bn bn-pseudo">None</span>:
+ <span class="kw">return</span> <span class="st st-sg">&#39;</span><span class="st">[quote]</span><span class="st st-int">%s</span><span class="st">[/quote]</span><span class="st st-sg">&#39;</span> <span class="op">%</span> <span class="name">text</span>
+
+ <span class="kw">return</span> <span class="st st-sg">&#39;</span><span class="st">[quote=&quot;</span><span class="st st-int">%s</span><span class="st">&quot;]</span><span class="st st-int">%s</span><span class="st">[/quote]</span><span class="st st-sg">&#39;</span> <span class="op">%</span> (<span class="name">username</span>, <span class="name">text</span>)
+
+ <span class="kw">def </span><span class="fun">get_editor_options</span>(<span class="bn bn-pseudo">self</span>, <span class="name">req</span>, <span class="name">signature</span>):
+ <span class="name">buttons</span> <span class="op">=</span> []
+ <span class="kw">if</span> <span class="name">signature</span>:
+ <span class="name">signature_tags</span> <span class="op">=</span> <span class="bn bn-pseudo">self</span>.<span class="name">get_signature_tags</span>()
+ <span class="kw">for</span> <span class="name">comp</span> <span class="op op-word">in</span> <span class="bn bn-pseudo">self</span>.<span class="name">ctx</span>.<span class="name">get_components</span>(<span class="name">BBCodeTagProvider</span>):
+ <span class="kw">for</span> <span class="name">button</span> <span class="op op-word">in</span> <span class="name">comp</span>.<span class="name">get_buttons</span>(<span class="name">req</span>):
+ <span class="kw">if</span> <span class="name">signature</span> <span class="op op-word">and</span> <span class="name">button</span>[<span class="st st-sg">&#39;</span><span class="st">tagname</span><span class="st st-sg">&#39;</span>] <span class="op op-word">not</span> <span class="op op-word">in</span> <span class="name">signature_tags</span>:
+ <span class="kw">continue</span>
+
+ <span class="name">buttons</span>.<span class="name">append</span>(<span class="name">button</span>)
+ <span class="kw">return</span> {
+ <span class="st st-sg">&#39;</span><span class="st">buttons</span><span class="st st-sg">&#39;</span>: <span class="name">buttons</span>,
+ <span class="st st-sg">&#39;</span><span class="st">smilies</span><span class="st st-sg">&#39;</span>: <span class="name">get_smiley_buttons</span>(<span class="name">req</span>.<span class="name">ctx</span>)
+ }
+
+
+
+<span class="kw">class </span><span class="cls">BasicBBCodeTagProvider</span>(<span class="name">BBCodeTagProvider</span>):
+ <span class="name">tags</span> <span class="op">=</span> [<span class="st st-sg">&#39;</span><span class="st">b</span><span class="st st-sg">&#39;</span>, <span class="st st-sg">&#39;</span><span class="st">i</span><span class="st st-sg">&#39;</span>, <span class="st st-sg">&#39;</span><span class="st">u</span><span class="st st-sg">&#39;</span>, <span class="st st-sg">&#39;</span><span class="st">s</span><span class="st st-sg">&#39;</span>, <span class="st st-sg">&#39;</span><span class="st">url</span><span class="st st-sg">&#39;</span>, <span class="st st-sg">&#39;</span><span class="st">email</span><span class="st st-sg">&#39;</span>, <span class="st st-sg">&#39;</span><span class="st">color</span><span class="st st-sg">&#39;</span>, <span class="st st-sg">&#39;</span><span class="st">size</span><span class="st st-sg">&#39;</span>,
+ <span class="st st-sg">&#39;</span><span class="st">code</span><span class="st st-sg">&#39;</span>, <span class="st st-sg">&#39;</span><span class="st">quote</span><span class="st st-sg">&#39;</span>, <span class="st st-sg">&#39;</span><span class="st">list</span><span class="st st-sg">&#39;</span>]
+ <span class="name">callbacks</span> <span class="op">=</span> [<span class="st st-sg">&#39;</span><span class="st">quote</span><span class="st st-sg">&#39;</span>, <span class="st st-sg">&#39;</span><span class="st">list</span><span class="st st-sg">&#39;</span>]
+
+ <span class="kw">def </span><span class="fun">get_node</span>(<span class="bn bn-pseudo">self</span>, <span class="name">token</span>, <span class="name">parser</span>):
+ <span class="name">ctx</span> <span class="op">=</span> <span class="bn bn-pseudo">self</span>.<span class="name">ctx</span>
+
+ <span class="kw">if</span> <span class="name">token</span>.<span class="name">name</span> <span class="op">==</span> <span class="st st-sg">&#39;</span><span class="st">b</span><span class="st st-sg">&#39;</span>:
+ <span class="kw">if</span> <span class="name">token</span>.<span class="name">attr</span>:
+ <span class="kw">return</span>
+
+ <span class="kw">return</span> <span class="name">parser</span>.<span class="name">wrap_render</span>(<span class="st st-sg">&#39;</span><span class="st">strong</span><span class="st st-sg">&#39;</span>, <span class="st st-sg">&#39;</span><span class="st">/b</span><span class="st st-sg">&#39;</span>)
+ <span class="kw">if</span> <span class="name">token</span>.<span class="name">name</span> <span class="op">==</span> <span class="st st-sg">&#39;</span><span class="st">i</span><span class="st st-sg">&#39;</span>:
+ <span class="kw">if</span> <span class="name">token</span>.<span class="name">attr</span>:
+ <span class="kw">return</span>
+
+ <span class="kw">return</span> <span class="name">parser</span>.<span class="name">wrap_render</span>(<span class="st st-sg">&#39;</span><span class="st">em</span><span class="st st-sg">&#39;</span>, <span class="st st-sg">&#39;</span><span class="st">/i</span><span class="st st-sg">&#39;</span>)
+ <span class="kw">if</span> <span class="name">token</span>.<span class="name">name</span> <span class="op">==</span> <span class="st st-sg">&#39;</span><span class="st">u</span><span class="st st-sg">&#39;</span>:
+ <span class="kw">if</span> <span class="name">token</span>.<span class="name">attr</span>:
+ <span class="kw">return</span>
+
+ <span class="kw">return</span> <span class="name">parser</span>.<span class="name">wrap_render</span>(<span class="st st-sg">&#39;</span><span class="st">ins</span><span class="st st-sg">&#39;</span>, <span class="st st-sg">&#39;</span><span class="st">/u</span><span class="st st-sg">&#39;</span>)
+ <span class="kw">if</span> <span class="name">token</span>.<span class="name">name</span> <span class="op">==</span> <span class="st st-sg">&#39;</span><span class="st">s</span><span class="st st-sg">&#39;</span>:
+ <span class="kw">if</span> <span class="name">token</span>.<span class="name">attr</span>:
+ <span class="kw">return</span>
+
+ <span class="kw">return</span> <span class="name">parser</span>.<span class="name">wrap_render</span>(<span class="st st-sg">&#39;</span><span class="st">del</span><span class="st st-sg">&#39;</span>, <span class="st st-sg">&#39;</span><span class="st">/s</span><span class="st st-sg">&#39;</span>)
+ <span class="kw">if</span> <span class="name">token</span>.<span class="name">name</span> <span class="op">==</span> <span class="st st-sg">&#39;</span><span class="st">url</span><span class="st st-sg">&#39;</span>:
+ <span class="kw">if</span> <span class="name">token</span>.<span class="name">attr</span>:
+ <span class="name">content</span> <span class="op">=</span> <span class="name">parser</span>.<span class="name">parse</span>(<span class="st st-sg">&#39;</span><span class="st">/url</span><span class="st st-sg">&#39;</span>)
+ <span class="name">url</span> <span class="op">=</span> <span class="name">token</span>.<span class="name">attr</span>
+
+ <span class="kw">else</span>:
+ <span class="name">tokenlist</span> <span class="op">=</span> <span class="name">parser</span>.<span class="name">get_tokens</span>(<span class="st st-sg">&#39;</span><span class="st">/url</span><span class="st st-sg">&#39;</span>)
+ <span class="name">content</span> <span class="op">=</span> <span class="name">url</span> <span class="op">=</span> <span class="name">tokenlist</span>.<span class="name">flatten</span>()
+ <span class="kw">if</span> <span class="name">url</span>.<span class="name">startswith</span>(<span class="st st-sg">&#39;</span><span class="st">javascript:</span><span class="st st-sg">&#39;</span>):
+ <span class="name">url</span> <span class="op">=</span> <span class="name">url</span>[<span class="nb nb-int">11</span>:]
+ <span class="kw">return</span> <span class="name">parser</span>.<span class="name">joined_render</span>(<span class="st st-sg">&#39;</span><span class="st">&lt;a href=&quot;</span><span class="st st-sg">&#39;</span>, <span class="name">url</span>, <span class="st st-sg">&#39;</span><span class="st">&quot;&gt;</span><span class="st st-sg">&#39;</span>, <span class="name">content</span>, <span class="st st-sg">&#39;</span><span class="st">&lt;/a&gt;</span><span class="st st-sg">&#39;</span>)
+ <span class="kw">if</span> <span class="name">token</span>.<span class="name">name</span> <span class="op">==</span> <span class="st st-sg">&#39;</span><span class="st">email</span><span class="st st-sg">&#39;</span>:
+ <span class="kw">if</span> <span class="name">token</span>.<span class="name">attr</span>:
+ <span class="name">content</span> <span class="op">=</span> <span class="name">parser</span>.<span class="name">parse</span>(<span class="st st-sg">&#39;</span><span class="st">/email</span><span class="st st-sg">&#39;</span>)
+ <span class="name">mail</span> <span class="op">=</span> <span class="name">token</span>.<span class="name">attr</span>
+
+ <span class="kw">else</span>:
+ <span class="name">tokenlist</span> <span class="op">=</span> <span class="name">parser</span>.<span class="name">get_tokens</span>(<span class="st st-sg">&#39;</span><span class="st">/email</span><span class="st st-sg">&#39;</span>)
+ <span class="name">mail</span> <span class="op">=</span> <span class="name">content</span> <span class="op">=</span> <span class="name">tokenlist</span>.<span class="name">flatten</span>()
+ <span class="kw">return</span> <span class="name">parser</span>.<span class="name">joined_render</span>(<span class="st st-sg">&#39;</span><span class="st">&lt;a href=&quot;mailto:&quot;</span><span class="st st-sg">&#39;</span>, <span class="name">mail</span>, <span class="st st-sg">&#39;</span><span class="st">&quot;&gt;</span><span class="st st-sg">&#39;</span>,
+ <span class="name">content</span>, <span class="st st-sg">&#39;</span><span class="st">&lt;/a&gt;</span><span class="st st-sg">&#39;</span>)
+ <span class="kw">if</span> <span class="name">token</span>.<span class="name">name</span> <span class="op">==</span> <span class="st st-sg">&#39;</span><span class="st">color</span><span class="st st-sg">&#39;</span>:
+ <span class="name">content</span> <span class="op">=</span> <span class="name">parser</span>.<span class="name">parse</span>(<span class="st st-sg">&#39;</span><span class="st">/color</span><span class="st st-sg">&#39;</span>)
+ <span class="kw">try</span>:
+ <span class="name">color</span> <span class="op">=</span> <span class="name">translate_color</span>(<span class="name">token</span>.<span class="name">attr</span>)
+ <span class="kw">except</span> <span class="exc">ValueError</span>:
+ <span class="kw">return</span> <span class="name">token</span>.<span class="name">raw</span>
+
+ <span class="kw">return</span> <span class="name">parser</span>.<span class="name">joined_render</span>(<span class="st st-sg">&#39;</span><span class="st">&lt;span style=&quot;color: </span><span class="st st-sg">&#39;</span>, <span class="name">color</span>, <span class="st st-sg">&#39;</span><span class="st">&quot;&gt;</span><span class="st st-sg">&#39;</span>,
+ <span class="name">content</span>, <span class="st st-sg">&#39;</span><span class="st">&lt;/span&gt;</span><span class="st st-sg">&#39;</span>)
+ <span class="kw">if</span> <span class="name">token</span>.<span class="name">name</span> <span class="op">==</span> <span class="st st-sg">&#39;</span><span class="st">size</span><span class="st st-sg">&#39;</span>:
+ <span class="name">content</span> <span class="op">=</span> <span class="name">parser</span>.<span class="name">parse</span>(<span class="st st-sg">&#39;</span><span class="st">/size</span><span class="st st-sg">&#39;</span>)
+ <span class="kw">if</span> <span class="op op-word">not</span> <span class="name">token</span>.<span class="name">attr</span> <span class="op op-word">or</span> <span class="op op-word">not</span> <span class="name">token</span>.<span class="name">attr</span>.<span class="name">isdigit</span>() <span class="op op-word">or</span> <span class="bn">len</span>(<span class="name">token</span>.<span class="name">attr</span>) <span class="op">&gt;</span> <span class="nb nb-int">2</span>:
+ <span class="kw">return</span> <span class="name">token</span>.<span class="name">raw</span>
+
+ <span class="kw">return</span> <span class="name">parser</span>.<span class="name">joined_render</span>(<span class="st st-sg">&#39;</span><span class="st">&lt;span style=&quot;font-size: </span><span class="st st-sg">&#39;</span>, <span class="name">token</span>.<span class="name">attr</span>,
+ <span class="st st-sg">&#39;</span><span class="st">px&quot;&gt;</span><span class="st st-sg">&#39;</span>, <span class="name">content</span>, <span class="st st-sg">&#39;</span><span class="st">&lt;/span&gt;</span><span class="st st-sg">&#39;</span>)
+ <span class="kw">if</span> <span class="name">token</span>.<span class="name">name</span> <span class="op">==</span> <span class="st st-sg">&#39;</span><span class="st">img</span><span class="st st-sg">&#39;</span>:
+ <span class="kw">if</span> <span class="name">token</span>.<span class="name">attr</span>:
+ <span class="kw">return</span>
+
+ <span class="name">tokenlist</span> <span class="op">=</span> <span class="name">parser</span>.<span class="name">get_tokens</span>(<span class="st st-sg">&#39;</span><span class="st">/img</span><span class="st st-sg">&#39;</span>)
+ <span class="name">url</span> <span class="op">=</span> <span class="name">tokenlist</span>.<span class="name">flatten</span>()
+ <span class="kw">if</span> <span class="name">url</span>.<span class="name">startswith</span>(<span class="st st-sg">&#39;</span><span class="st">javascript:</span><span class="st st-sg">&#39;</span>):
+ <span class="name">url</span> <span class="op">=</span> <span class="name">url</span>[<span class="nb nb-int">11</span>:]
+ <span class="kw">return</span> <span class="name">u</span><span class="st st-sg">&#39;</span><span class="st">&lt;img src=&quot;</span><span class="st st-int">%s</span><span class="st">&quot; /&gt;</span><span class="st st-sg">&#39;</span> <span class="op">%</span> <span class="name">url</span>
+
+ <span class="kw">if</span> <span class="name">token</span>.<span class="name">name</span> <span class="op">==</span> <span class="st st-sg">&#39;</span><span class="st">code</span><span class="st st-sg">&#39;</span>:
+ <span class="kw">if</span> <span class="name">token</span>.<span class="name">attr</span>:
+ <span class="kw">return</span>
+
+ <span class="kw">return</span> <span class="name">u</span><span class="st st-sg">&#39;</span><span class="st">&lt;pre&gt;</span><span class="st st-int">%s</span><span class="st">&lt;/pre&gt;</span><span class="st st-sg">&#39;</span> <span class="op">%</span> <span class="name">parser</span>.<span class="name">get_tokens</span>(<span class="st st-sg">&#39;</span><span class="st">/code</span><span class="st st-sg">&#39;</span>).<span class="name">flatten</span>()
+ <span class="kw">if</span> <span class="name">token</span>.<span class="name">name</span> <span class="op">==</span> <span class="st st-sg">&#39;</span><span class="st">quote</span><span class="st st-sg">&#39;</span>:
+ <span class="kw">return</span> <span class="name">parser</span>.<span class="name">callback</span>(<span class="st st-sg">&#39;</span><span class="st">quote</span><span class="st st-sg">&#39;</span>, (<span class="name">token</span>.<span class="name">attr</span> <span class="op op-word">or</span> <span class="name">u</span><span class="st st-sg">&#39;&#39;</span>,
+ <span class="name">parser</span>.<span class="name">parse</span>(<span class="st st-sg">&#39;</span><span class="st">/quote</span><span class="st st-sg">&#39;</span>)))
+ <span class="kw">if</span> <span class="name">token</span>.<span class="name">name</span> <span class="op">==</span> <span class="st st-sg">&#39;</span><span class="st">list</span><span class="st st-sg">&#39;</span>:
+ <span class="kw">return</span> <span class="name">parser</span>.<span class="name">callback</span>(<span class="st st-sg">&#39;</span><span class="st">list</span><span class="st st-sg">&#39;</span>, (<span class="name">token</span>.<span class="name">attr</span> <span class="op op-word">or</span> <span class="name">u</span><span class="st st-sg">&#39;</span><span class="st">*</span><span class="st st-sg">&#39;</span>,
+ <span class="name">parser</span>.<span class="name">parse</span>(<span class="st st-sg">&#39;</span><span class="st">/list</span><span class="st st-sg">&#39;</span>)))
+
+ <span class="kw">def </span><span class="fun">render_callback</span>(<span class="bn bn-pseudo">self</span>, <span class="name">req</span>, <span class="name">callback</span>, <span class="name">data</span>):
+ <span class="kw">if</span> <span class="name">callback</span> <span class="op">==</span> <span class="st st-sg">&#39;</span><span class="st">quote</span><span class="st st-sg">&#39;</span>:
+ <span class="name">_</span> <span class="op">=</span> <span class="name">req</span>.<span class="name">gettext</span>
+
+ <span class="name">written</span>, <span class="name">body</span> <span class="op">=</span> <span class="name">data</span>
+ <span class="kw">if</span> <span class="name">written</span>:
+ <span class="kw">if</span> <span class="op op-word">not</span> <span class="name">written</span>.<span class="name">endswith</span>(<span class="st st-sg">&#39;</span><span class="st">:</span><span class="st st-sg">&#39;</span>):
+ <span class="name">written</span> <span class="op">=</span> (<span class="name">_</span>(<span class="st st-sg">&#39;</span><span class="st st-int">%s</span><span class="st"> wrote</span><span class="st st-sg">&#39;</span>) <span class="op">%</span> <span class="name">written</span>) <span class="op">+</span> <span class="name">u</span><span class="st st-sg">&#39;</span><span class="st">:</span><span class="st st-sg">&#39;</span>
+
+ <span class="name">written</span> <span class="op">=</span> <span class="name">u</span><span class="st st-sg">&#39;</span><span class="st">&lt;div class=&quot;written_by&quot;&gt;</span><span class="st st-int">%s</span><span class="st">&lt;/div&gt;</span><span class="st st-sg">&#39;</span> <span class="op">%</span> <span class="name">written</span>
+ <span class="kw">return</span> <span class="name">u</span><span class="st st-sg">&#39;</span><span class="st">&lt;blockquote&gt;</span><span class="st st-int">%s%s</span><span class="st">&lt;/blockquote&gt;</span><span class="st st-sg">&#39;</span> <span class="op">%</span> (
+ <span class="name">written</span>, <span class="name">body</span>.<span class="name">render</span>(<span class="name">req</span>, <span class="bn bn-pseudo">self</span>)
+ )
+ <span class="kw">if</span> <span class="name">callback</span> <span class="op">==</span> <span class="st st-sg">&#39;</span><span class="st">list</span><span class="st st-sg">&#39;</span>:
+ <span class="bn">type</span>, <span class="name">body</span> <span class="op">=</span> <span class="name">data</span>
+
+ <span class="name">lines</span> <span class="op">=</span> []
+ <span class="kw">for</span> <span class="name">line</span> <span class="op op-word">in</span> <span class="name">re</span>.<span class="name">split</span>(<span class="st st-sg">r&#39;</span><span class="st">^\s*\[\*\](?m)</span><span class="st st-sg">&#39;</span>, <span class="name">body</span>.<span class="name">render</span>(<span class="name">req</span>, <span class="bn bn-pseudo">self</span>)):
+ <span class="name">line</span> <span class="op">=</span> <span class="name">line</span>.<span class="name">strip</span>()
+ <span class="kw">if</span> <span class="name">line</span>:
+ <span class="name">lines</span>.<span class="name">append</span>(<span class="name">u</span><span class="st st-sg">&#39;</span><span class="st">&lt;li&gt;</span><span class="st st-int">%s</span><span class="st">&lt;/li&gt;</span><span class="st st-sg">&#39;</span> <span class="op">%</span> <span class="name">line</span>)
+ <span class="kw">return</span> <span class="name">u</span><span class="st st-sg">&#39;</span><span class="st">&lt;ul&gt;</span><span class="st st-int">%s</span><span class="st">&lt;/ul&gt;</span><span class="st st-sg">&#39;</span> <span class="op">%</span> <span class="name">u</span><span class="st st-sg">&#39;</span><span class="st st-esc">\n</span><span class="st st-sg">&#39;</span>.<span class="name">join</span>(<span class="name">lines</span>)
+
+ <span class="kw">def </span><span class="fun">get_buttons</span>(<span class="bn bn-pseudo">self</span>, <span class="name">req</span>):
+ <span class="name">_</span> <span class="op">=</span> <span class="name">req</span>.<span class="name">gettext</span>
+
+ <span class="name">make_url</span> <span class="op">=</span> <span class="bn bn-pseudo">self</span>.<span class="name">ctx</span>.<span class="name">make_url</span>
+ <span class="cm">#XXX: themeable</span>
+ <span class="name">icon_url</span> <span class="op">=</span> <span class="kw">lambda</span> <span class="name">x</span>: <span class="name">make_url</span>(<span class="st st-sg">&#39;</span><span class="st">!cobalt/core/default/img/bbcode/</span><span class="st st-sg">&#39;</span> <span class="op">+</span> <span class="name">x</span>)
+
+ <span class="kw">return</span> [
+ {<span class="st st-sg">&#39;</span><span class="st">tagname</span><span class="st st-sg">&#39;</span>: <span class="st st-sg">&#39;</span><span class="st">b</span><span class="st st-sg">&#39;</span>,
+ <span class="st st-sg">&#39;</span><span class="st">name</span><span class="st st-sg">&#39;</span>: <span class="name">_</span>(<span class="st st-sg">&#39;</span><span class="st">Bold</span><span class="st st-sg">&#39;</span>),
+ <span class="st st-sg">&#39;</span><span class="st">description</span><span class="st st-sg">&#39;</span>: <span class="name">_</span>(<span class="st st-sg">&#39;</span><span class="st">Insert bold text</span><span class="st st-sg">&#39;</span>),
+ <span class="st st-sg">&#39;</span><span class="st">insert</span><span class="st st-sg">&#39;</span>: <span class="st st-sg">&#39;</span><span class="st">[b]{text}[/b]</span><span class="st st-sg">&#39;</span>,
+ <span class="st st-sg">&#39;</span><span class="st">icon</span><span class="st st-sg">&#39;</span>: <span class="name">icon_url</span>(<span class="st st-sg">&#39;</span><span class="st">bold.png</span><span class="st st-sg">&#39;</span>)},
+ {<span class="st st-sg">&#39;</span><span class="st">tagname</span><span class="st st-sg">&#39;</span>: <span class="st st-sg">&#39;</span><span class="st">i</span><span class="st st-sg">&#39;</span>,
+ <span class="st st-sg">&#39;</span><span class="st">name</span><span class="st st-sg">&#39;</span>: <span class="name">_</span>(<span class="st st-sg">&#39;</span><span class="st">Italic</span><span class="st st-sg">&#39;</span>),
+ <span class="st st-sg">&#39;</span><span class="st">description</span><span class="st st-sg">&#39;</span>: <span class="name">_</span>(<span class="st st-sg">&#39;</span><span class="st">Insert italic text</span><span class="st st-sg">&#39;</span>),
+ <span class="st st-sg">&#39;</span><span class="st">insert</span><span class="st st-sg">&#39;</span>: <span class="st st-sg">&#39;</span><span class="st">[i]{text}[/i]</span><span class="st st-sg">&#39;</span>,
+ <span class="st st-sg">&#39;</span><span class="st">icon</span><span class="st st-sg">&#39;</span>: <span class="name">icon_url</span>(<span class="st st-sg">&#39;</span><span class="st">italic.png</span><span class="st st-sg">&#39;</span>)},
+ {<span class="st st-sg">&#39;</span><span class="st">tagname</span><span class="st st-sg">&#39;</span>: <span class="st st-sg">&#39;</span><span class="st">u</span><span class="st st-sg">&#39;</span>,
+ <span class="st st-sg">&#39;</span><span class="st">name</span><span class="st st-sg">&#39;</span>: <span class="name">_</span>(<span class="st st-sg">&#39;</span><span class="st">Underline</span><span class="st st-sg">&#39;</span>),
+ <span class="st st-sg">&#39;</span><span class="st">description</span><span class="st st-sg">&#39;</span>: <span class="name">_</span>(<span class="st st-sg">&#39;</span><span class="st">Insert underlined text</span><span class="st st-sg">&#39;</span>),
+ <span class="st st-sg">&#39;</span><span class="st">insert</span><span class="st st-sg">&#39;</span>: <span class="st st-sg">&#39;</span><span class="st">[u]{text}[/u]</span><span class="st st-sg">&#39;</span>,
+ <span class="st st-sg">&#39;</span><span class="st">icon</span><span class="st st-sg">&#39;</span>: <span class="name">icon_url</span>(<span class="st st-sg">&#39;</span><span class="st">underline.png</span><span class="st st-sg">&#39;</span>)},
+ {<span class="st st-sg">&#39;</span><span class="st">tagname</span><span class="st st-sg">&#39;</span>: <span class="st st-sg">&#39;</span><span class="st">s</span><span class="st st-sg">&#39;</span>,
+ <span class="st st-sg">&#39;</span><span class="st">name</span><span class="st st-sg">&#39;</span>: <span class="name">_</span>(<span class="st st-sg">&#39;</span><span class="st">Strikethrough</span><span class="st st-sg">&#39;</span>),
+ <span class="st st-sg">&#39;</span><span class="st">description</span><span class="st st-sg">&#39;</span>: <span class="name">_</span>(<span class="st st-sg">&#39;</span><span class="st">Insert striked text</span><span class="st st-sg">&#39;</span>),
+ <span class="st st-sg">&#39;</span><span class="st">insert</span><span class="st st-sg">&#39;</span>: <span class="st st-sg">&#39;</span><span class="st">[i]{text}[/i]</span><span class="st st-sg">&#39;</span>,
+ <span class="st st-sg">&#39;</span><span class="st">icon</span><span class="st st-sg">&#39;</span>: <span class="name">icon_url</span>(<span class="st st-sg">&#39;</span><span class="st">strikethrough.png</span><span class="st st-sg">&#39;</span>)},
+ {<span class="st st-sg">&#39;</span><span class="st">tagname</span><span class="st st-sg">&#39;</span>: <span class="st st-sg">&#39;</span><span class="st">size</span><span class="st st-sg">&#39;</span>,
+ <span class="st st-sg">&#39;</span><span class="st">name</span><span class="st st-sg">&#39;</span>: <span class="name">_</span>(<span class="st st-sg">&#39;</span><span class="st">Font Size</span><span class="st st-sg">&#39;</span>),
+ <span class="st st-sg">&#39;</span><span class="st">description</span><span class="st st-sg">&#39;</span>: <span class="name">_</span>(<span class="st st-sg">&#39;</span><span class="st">Change the font size</span><span class="st st-sg">&#39;</span>),
+ <span class="st st-sg">&#39;</span><span class="st">insert</span><span class="st st-sg">&#39;</span>: <span class="st st-sg">&#39;</span><span class="st">[size={attr}]{text}[/size]</span><span class="st st-sg">&#39;</span>,
+ <span class="st st-sg">&#39;</span><span class="st">values</span><span class="st st-sg">&#39;</span>: [
+ (<span class="nb nb-int">8</span>, <span class="name">_</span>(<span class="st st-sg">&#39;</span><span class="st">Tiny</span><span class="st st-sg">&#39;</span>)),
+ (<span class="nb nb-int">11</span>, <span class="name">_</span>(<span class="st st-sg">&#39;</span><span class="st">Small</span><span class="st st-sg">&#39;</span>)),
+ (<span class="nb nb-int">13</span>, <span class="name">_</span>(<span class="st st-sg">&#39;</span><span class="st">Normal</span><span class="st st-sg">&#39;</span>)),
+ (<span class="nb nb-int">18</span>, <span class="name">_</span>(<span class="st st-sg">&#39;</span><span class="st">Big</span><span class="st st-sg">&#39;</span>)),
+ (<span class="nb nb-int">24</span>, <span class="name">_</span>(<span class="st st-sg">&#39;</span><span class="st">Huge</span><span class="st st-sg">&#39;</span>))
+ ]},
+ {<span class="st st-sg">&#39;</span><span class="st">tagname</span><span class="st st-sg">&#39;</span>: <span class="st st-sg">&#39;</span><span class="st">color</span><span class="st st-sg">&#39;</span>,
+ <span class="st st-sg">&#39;</span><span class="st">name</span><span class="st st-sg">&#39;</span>: <span class="name">_</span>(<span class="st st-sg">&#39;</span><span class="st">Font Color</span><span class="st st-sg">&#39;</span>),
+ <span class="st st-sg">&#39;</span><span class="st">description</span><span class="st st-sg">&#39;</span>: <span class="name">_</span>(<span class="st st-sg">&#39;</span><span class="st">Change Font Color</span><span class="st st-sg">&#39;</span>),
+ <span class="st st-sg">&#39;</span><span class="st">insert</span><span class="st st-sg">&#39;</span>: <span class="st st-sg">&#39;</span><span class="st">[color={attr}]{text}[/size]</span><span class="st st-sg">&#39;</span>,
+ <span class="st st-sg">&#39;</span><span class="st">values</span><span class="st st-sg">&#39;</span>: [
+ (<span class="st st-sg">&#39;</span><span class="st">black</span><span class="st st-sg">&#39;</span>, <span class="name">_</span>(<span class="st st-sg">&#39;</span><span class="st">Black</span><span class="st st-sg">&#39;</span>)),
+ (<span class="st st-sg">&#39;</span><span class="st">blue</span><span class="st st-sg">&#39;</span>, <span class="name">_</span>(<span class="st st-sg">&#39;</span><span class="st">Blue</span><span class="st st-sg">&#39;</span>)),
+ (<span class="st st-sg">&#39;</span><span class="st">brown</span><span class="st st-sg">&#39;</span>, <span class="name">_</span>(<span class="st st-sg">&#39;</span><span class="st">Brown</span><span class="st st-sg">&#39;</span>)),
+ (<span class="st st-sg">&#39;</span><span class="st">cyan</span><span class="st st-sg">&#39;</span>, <span class="name">_</span>(<span class="st st-sg">&#39;</span><span class="st">Cyan</span><span class="st st-sg">&#39;</span>)),
+ (<span class="st st-sg">&#39;</span><span class="st">gray</span><span class="st st-sg">&#39;</span>, <span class="name">_</span>(<span class="st st-sg">&#39;</span><span class="st">Gray</span><span class="st st-sg">&#39;</span>)),
+ (<span class="st st-sg">&#39;</span><span class="st">green</span><span class="st st-sg">&#39;</span>, <span class="name">_</span>(<span class="st st-sg">&#39;</span><span class="st">Green</span><span class="st st-sg">&#39;</span>)),
+ (<span class="st st-sg">&#39;</span><span class="st">magenta</span><span class="st st-sg">&#39;</span>, <span class="name">_</span>(<span class="st st-sg">&#39;</span><span class="st">Magenta</span><span class="st st-sg">&#39;</span>)),
+ (<span class="st st-sg">&#39;</span><span class="st">purple</span><span class="st st-sg">&#39;</span>, <span class="name">_</span>(<span class="st st-sg">&#39;</span><span class="st">Purple</span><span class="st st-sg">&#39;</span>)),
+ (<span class="st st-sg">&#39;</span><span class="st">red</span><span class="st st-sg">&#39;</span>, <span class="name">_</span>(<span class="st st-sg">&#39;</span><span class="st">Red</span><span class="st st-sg">&#39;</span>)),
+ (<span class="st st-sg">&#39;</span><span class="st">white</span><span class="st st-sg">&#39;</span>, <span class="name">_</span>(<span class="st st-sg">&#39;</span><span class="st">White</span><span class="st st-sg">&#39;</span>)),
+ (<span class="st st-sg">&#39;</span><span class="st">yellow</span><span class="st st-sg">&#39;</span>, <span class="name">_</span>(<span class="st st-sg">&#39;</span><span class="st">Yellow</span><span class="st st-sg">&#39;</span>))
+ ]},
+ {<span class="st st-sg">&#39;</span><span class="st">tagname</span><span class="st st-sg">&#39;</span>: <span class="st st-sg">&#39;</span><span class="st">url</span><span class="st st-sg">&#39;</span>,
+ <span class="st st-sg">&#39;</span><span class="st">name</span><span class="st st-sg">&#39;</span>: <span class="name">_</span>(<span class="st st-sg">&#39;</span><span class="st">Link</span><span class="st st-sg">&#39;</span>),
+ <span class="st st-sg">&#39;</span><span class="st">description</span><span class="st st-sg">&#39;</span>: <span class="name">_</span>(<span class="st st-sg">&#39;</span><span class="st">Create a Link</span><span class="st st-sg">&#39;</span>),
+ <span class="st st-sg">&#39;</span><span class="st">icon</span><span class="st st-sg">&#39;</span>: <span class="name">icon_url</span>(<span class="st st-sg">&#39;</span><span class="st">link.png</span><span class="st st-sg">&#39;</span>),
+ <span class="st st-sg">&#39;</span><span class="st">insert</span><span class="st st-sg">&#39;</span>: <span class="st st-sg">&#39;</span><span class="st">[url]{text}[/url]</span><span class="st st-sg">&#39;</span>},
+ {<span class="st st-sg">&#39;</span><span class="st">tagname</span><span class="st st-sg">&#39;</span>: <span class="st st-sg">&#39;</span><span class="st">img</span><span class="st st-sg">&#39;</span>,
+ <span class="st st-sg">&#39;</span><span class="st">name</span><span class="st st-sg">&#39;</span>: <span class="name">_</span>(<span class="st st-sg">&#39;</span><span class="st">Image</span><span class="st st-sg">&#39;</span>),
+ <span class="st st-sg">&#39;</span><span class="st">description</span><span class="st st-sg">&#39;</span>: <span class="name">_</span>(<span class="st st-sg">&#39;</span><span class="st">Insert an image</span><span class="st st-sg">&#39;</span>),
+ <span class="st st-sg">&#39;</span><span class="st">icon</span><span class="st st-sg">&#39;</span>: <span class="name">icon_url</span>(<span class="st st-sg">&#39;</span><span class="st">img.png</span><span class="st st-sg">&#39;</span>),
+ <span class="st st-sg">&#39;</span><span class="st">insert</span><span class="st st-sg">&#39;</span>: <span class="st st-sg">&#39;</span><span class="st">[img]{text}[/img]</span><span class="st st-sg">&#39;</span>},
+ {<span class="st st-sg">&#39;</span><span class="st">tagname</span><span class="st st-sg">&#39;</span>: <span class="st st-sg">&#39;</span><span class="st">code</span><span class="st st-sg">&#39;</span>,
+ <span class="st st-sg">&#39;</span><span class="st">name</span><span class="st st-sg">&#39;</span>: <span class="name">_</span>(<span class="st st-sg">&#39;</span><span class="st">Code</span><span class="st st-sg">&#39;</span>),
+ <span class="st st-sg">&#39;</span><span class="st">description</span><span class="st st-sg">&#39;</span>: <span class="name">_</span>(<span class="st st-sg">&#39;</span><span class="st">Insert a codeblock</span><span class="st st-sg">&#39;</span>),
+ <span class="st st-sg">&#39;</span><span class="st">icon</span><span class="st st-sg">&#39;</span>: <span class="name">icon_url</span>(<span class="st st-sg">&#39;</span><span class="st">code.png</span><span class="st st-sg">&#39;</span>),
+ <span class="st st-sg">&#39;</span><span class="st">insert</span><span class="st st-sg">&#39;</span>: <span class="st st-sg">&#39;</span><span class="st">[code]{text}[/code]</span><span class="st st-sg">&#39;</span>},
+ {<span class="st st-sg">&#39;</span><span class="st">tagname</span><span class="st st-sg">&#39;</span>: <span class="st st-sg">&#39;</span><span class="st">quote</span><span class="st st-sg">&#39;</span>,
+ <span class="st st-sg">&#39;</span><span class="st">name</span><span class="st st-sg">&#39;</span>: <span class="name">_</span>(<span class="st st-sg">&#39;</span><span class="st">Quote</span><span class="st st-sg">&#39;</span>),
+ <span class="st st-sg">&#39;</span><span class="st">description</span><span class="st st-sg">&#39;</span>: <span class="name">_</span>(<span class="st st-sg">&#39;</span><span class="st">Insert a blockquote</span><span class="st st-sg">&#39;</span>),
+ <span class="st st-sg">&#39;</span><span class="st">icon</span><span class="st st-sg">&#39;</span>: <span class="name">icon_url</span>(<span class="st st-sg">&#39;</span><span class="st">quote.png</span><span class="st st-sg">&#39;</span>),
+ <span class="st st-sg">&#39;</span><span class="st">insert</span><span class="st st-sg">&#39;</span>: <span class="st st-sg">&#39;</span><span class="st">[quote]{text}[/quote]</span><span class="st st-sg">&#39;</span>}
+ ]
+
+<span class="cm"># -*- coding: utf-8 -*-</span>
+<span class="st st-db">&quot;&quot;&quot;</span><span class="st">
+ pocoo.pkg.core.cache
+ ~~~~~~~~~~~~~~~~~~~~
+
+ Provides a very simple caching system for persistent processes.
+
+ :copyright: 2006 by Armin Ronacher.
+ :license: GNU GPL, see LICENSE for more details.
+</span><span class="st st-db">&quot;&quot;&quot;</span>
+<span class="kw">from </span><span class="cls">pocoo.application</span><span class="kw"> import</span> <span class="name">RequestWrapper</span>
+<span class="kw">from </span><span class="cls">pocoo.exceptions</span><span class="kw"> import</span> <span class="name">PocooRuntimeError</span>
+
+<span class="kw">from </span><span class="cls">pocoo.utils.cache</span><span class="kw"> import</span> <span class="name">Cache</span>
+
+<span class="cm"># This is currently unused.</span>
+
+<span class="kw">class </span><span class="cls">CacheSystem</span>(<span class="name">RequestWrapper</span>):
+
+ <span class="kw">def </span><span class="fun">__init__</span>(<span class="bn bn-pseudo">self</span>, <span class="name">ctx</span>):
+ <span class="bn bn-pseudo">self</span>.<span class="name">cache</span> <span class="op">=</span> <span class="name">Cache</span>(<span class="name">autoprune</span><span class="op">=</span><span class="name">ctx</span>.<span class="name">cfg</span>.<span class="name">get</span>(<span class="st st-sg">&#39;</span><span class="st">cache</span><span class="st st-sg">&#39;</span>, <span class="st st-sg">&#39;</span><span class="st">autoprune</span><span class="st st-sg">&#39;</span>, <span class="bn bn-pseudo">False</span>))
+ <span class="bn bn-pseudo">self</span>.<span class="name">uri2key</span> <span class="op">=</span> {}
+ <span class="name">RequestWrapper</span>.<span class="name">__init__</span>(<span class="bn bn-pseudo">self</span>, <span class="name">ctx</span>)
+
+ <span class="kw">def </span><span class="fun">get_priority</span>(<span class="bn bn-pseudo">self</span>):
+ <span class="cm"># caching has highest priority</span>
+
+ <span class="kw">return</span> <span class="nb nb-int">1</span>
+
+ <span class="kw">def </span><span class="fun">process_request</span>(<span class="bn bn-pseudo">self</span>, <span class="name">req</span>):
+ <span class="name">req</span>.<span class="name">cache_control</span> <span class="op">=</span> <span class="bn bn-pseudo">None</span>
+
+ <span class="name">req</span>.<span class="name">cache</span> <span class="op">=</span> <span class="bn bn-pseudo">self</span>.<span class="name">cache</span>
+
+ <span class="kw">if</span> <span class="name">req</span>.<span class="name">environ</span>[<span class="st st-sg">&#39;</span><span class="st">REQUEST_METHOD</span><span class="st st-sg">&#39;</span>] <span class="op">!=</span> <span class="st st-sg">&#39;</span><span class="st">GET</span><span class="st st-sg">&#39;</span>:
+ <span class="kw">return</span>
+
+ <span class="kw">if</span> <span class="name">req</span>.<span class="name">environ</span>[<span class="st st-sg">&#39;</span><span class="st">REQUEST_URI</span><span class="st st-sg">&#39;</span>] <span class="op op-word">not</span> <span class="op op-word">in</span> <span class="bn bn-pseudo">self</span>.<span class="name">uri2key</span>:
+ <span class="kw">return</span>
+
+ <span class="name">key</span> <span class="op">=</span> <span class="bn bn-pseudo">self</span>.<span class="name">uri2key</span>[<span class="name">req</span>.<span class="name">environ</span>[<span class="st st-sg">&#39;</span><span class="st">REQUEST_URI</span><span class="st st-sg">&#39;</span>]]
+ <span class="kw">return</span> <span class="bn bn-pseudo">self</span>.<span class="name">cache</span>.<span class="name">fetch</span>(<span class="name">key</span>, <span class="bn bn-pseudo">None</span>)
+
+ <span class="kw">def </span><span class="fun">process_response</span>(<span class="bn bn-pseudo">self</span>, <span class="name">req</span>, <span class="name">resp</span>):
+ <span class="kw">if</span> <span class="op op-word">not</span> <span class="name">req</span>.<span class="name">cache_control</span>:
+ <span class="kw">return</span> <span class="name">resp</span>
+
+ <span class="name">action</span>, <span class="name">key</span> <span class="op">=</span> <span class="name">req</span>.<span class="name">cache_control</span>
+ <span class="kw">if</span> <span class="name">action</span> <span class="op">==</span> <span class="st st-sg">&#39;</span><span class="st">set</span><span class="st st-sg">&#39;</span>:
+ <span class="bn bn-pseudo">self</span>.<span class="name">cache</span>.<span class="name">dump</span>(<span class="name">key</span>, <span class="name">resp</span>)
+ <span class="bn bn-pseudo">self</span>.<span class="name">uri2key</span>[<span class="name">req</span>.<span class="name">environ</span>[<span class="st st-sg">&#39;</span><span class="st">REQUEST_URI</span><span class="st st-sg">&#39;</span>]] <span class="op">=</span> <span class="name">key</span>
+
+ <span class="kw">elif</span> <span class="name">action</span> <span class="op">==</span> <span class="st st-sg">&#39;</span><span class="st">update</span><span class="st st-sg">&#39;</span>:
+ <span class="kw">if</span> <span class="bn">isinstance</span>(<span class="name">key</span>, <span class="bn">basestring</span>):
+ <span class="bn bn-pseudo">self</span>.<span class="name">cache</span>.<span class="name">remove</span>(<span class="name">key</span>)
+ <span class="kw">else</span>:
+ <span class="kw">for</span> <span class="name">k</span> <span class="op op-word">in</span> <span class="name">key</span>:
+ <span class="bn bn-pseudo">self</span>.<span class="name">cache</span>.<span class="name">remove</span>(<span class="name">k</span>)
+ <span class="kw">else</span>:
+ <span class="kw">raise</span> <span class="name">PocooRuntimeError</span>(<span class="st st-sg">&#39;</span><span class="st">req.cache_control invalid</span><span class="st st-sg">&#39;</span>)
+
+ <span class="kw">return</span> <span class="name">resp</span>
+
+<span class="cm"># -*- coding: utf-8 -*-</span>
+<span class="st st-db">&quot;&quot;&quot;</span><span class="st">
+ pocoo.pkg.core.captcha
+ ~~~~~~~~~~~~~~~~~~~~~~
+
+ Captcha URL Handler.
+
+ Displays a random captcha picture (debugging only).
+
+ :copyright: 2006 by Armin Ronacher.
+ :license: GNU GPL, see LICENSE for more details.
+</span><span class="st st-db">&quot;&quot;&quot;</span>
+<span class="kw">from </span><span class="cls">pocoo.application</span><span class="kw"> import</span> <span class="name">RequestHandler</span>
+<span class="kw">from </span><span class="cls">pocoo.http</span><span class="kw"> import</span> <span class="name">Response</span>
+
+
+<span class="kw">class </span><span class="cls">CaptchaImage</span>(<span class="name">RequestHandler</span>):
+ <span class="name">handler_regexes</span> <span class="op">=</span> [<span class="st st-sg">&#39;</span><span class="st">!captcha$</span><span class="st st-sg">&#39;</span>]
+
+ <span class="kw">def </span><span class="fun">handle_request</span>(<span class="bn bn-pseudo">self</span>, <span class="name">req</span>):
+ <span class="kw">from </span><span class="cls">pocoo.utils.captcha</span><span class="kw"> import</span> <span class="name">Captcha</span>
+
+ <span class="name">c</span> <span class="op">=</span> <span class="name">Captcha</span>()
+ <span class="name">response</span> <span class="op">=</span> <span class="name">Response</span>(<span class="name">c</span>.<span class="name">generate_image</span>())
+ <span class="name">response</span>[<span class="st st-sg">&#39;</span><span class="st">Content-Type</span><span class="st st-sg">&#39;</span>] <span class="op">=</span> <span class="st st-sg">&#39;</span><span class="st">image/png</span><span class="st st-sg">&#39;</span>
+
+ <span class="kw">return</span> <span class="name">response</span>
+<span class="cm"># -*- coding: utf-8 -*-</span>
+<span class="st st-db">&quot;&quot;&quot;</span><span class="st">
+ pocoo.pkg.core.cobalt
+ ~~~~~~~~~~~~~~~~~~~~~
+
+ Provides static content serving like mozilla&#39;s chrome:// scheme.
+
+ :copyright: 2006 by Armin Ronacher, Georg Brandl.
+ :license: GNU GPL, see LICENSE for more details.
+</span><span class="st st-db">&quot;&quot;&quot;</span>
+<span class="kw">import </span><span class="cls">os</span>
+<span class="kw">import </span><span class="cls">time</span>
+
+<span class="kw">from </span><span class="cls">mimetypes</span><span class="kw"> import</span> <span class="name">guess_type</span>
+<span class="kw">from </span><span class="cls">pocoo.template</span><span class="kw"> import</span> <span class="name">FileRequirements</span>
+
+
+<span class="kw">class </span><span class="cls">CobaltMiddleware</span>(<span class="bn">object</span>):
+ <span class="st st-db">&quot;&quot;&quot;</span><span class="st">
+
+ The Cobalt middleware serves static files.
+ </span><span class="st st-db">&quot;&quot;&quot;</span>
+
+ <span class="kw">def </span><span class="fun">__init__</span>(<span class="bn bn-pseudo">self</span>, <span class="name">app</span>, <span class="name">ctx</span>):
+ <span class="bn bn-pseudo">self</span>.<span class="name">app</span> <span class="op">=</span> <span class="name">app</span>
+
+ <span class="bn bn-pseudo">self</span>.<span class="name">ctx</span> <span class="op">=</span> <span class="name">ctx</span>
+ <span class="bn bn-pseudo">self</span>.<span class="name">cache_enabled</span> <span class="op">=</span> <span class="name">ctx</span>.<span class="name">cfg</span>.<span class="name">get_bool</span>(<span class="st st-sg">&#39;</span><span class="st">cache</span><span class="st st-sg">&#39;</span>, <span class="st st-sg">&#39;</span><span class="st">static_cache</span><span class="st st-sg">&#39;</span>)
+
+ <span class="kw">def </span><span class="fun">get_stylesheet_imports</span>(<span class="bn bn-pseudo">self</span>):
+ <span class="kw">if</span> <span class="op op-word">not</span> <span class="bn bn-pseudo">self</span>.<span class="name">cache_enabled</span> <span class="op op-word">or</span> <span class="st st-sg">&#39;</span><span class="st">cobalt/stylesheet_imports</span><span class="st st-sg">&#39;</span> <span class="op op-word">not</span> <span class="op op-word">in</span> <span class="bn bn-pseudo">self</span>.<span class="name">ctx</span>.<span class="name">_cache</span>:
+ <span class="name">handled</span> <span class="op">=</span> <span class="name">set</span>()
+ <span class="name">lines</span> <span class="op">=</span> []
+ <span class="kw">for</span> <span class="name">comp</span> <span class="op op-word">in</span> <span class="bn bn-pseudo">self</span>.<span class="name">ctx</span>.<span class="name">get_components</span>(<span class="name">FileRequirements</span>):
+ <span class="kw">for</span> <span class="name">name</span> <span class="op op-word">in</span> <span class="name">comp</span>.<span class="name">get_stylesheet_imports</span>():
+ <span class="name">item</span> <span class="op">=</span> (<span class="name">comp</span>.<span class="name">package</span>, <span class="name">name</span>)
+ <span class="kw">if</span> <span class="name">item</span> <span class="op op-word">in</span> <span class="name">handled</span>:
+ <span class="kw">continue</span>
+
+ <span class="name">handled</span>.<span class="name">add</span>(<span class="name">item</span>)
+ <span class="name">url</span> <span class="op">=</span> <span class="st st-sg">&#39;</span><span class="st">!cobalt/</span><span class="st st-int">%s</span><span class="st">/</span><span class="st st-int">%s</span><span class="st st-sg">&#39;</span> <span class="op">%</span> <span class="name">item</span>
+
+ <span class="name">lines</span>.<span class="name">append</span>(<span class="st st-sg">&#39;</span><span class="st">@import url(</span><span class="st st-int">%s</span><span class="st">);</span><span class="st st-sg">&#39;</span> <span class="op">%</span> <span class="bn">str</span>(<span class="bn bn-pseudo">self</span>.<span class="name">ctx</span>.<span class="name">make_url</span>(<span class="name">url</span>)))
+ <span class="bn bn-pseudo">self</span>.<span class="name">ctx</span>.<span class="name">_cache</span>[<span class="st st-sg">&#39;</span><span class="st">cobalt/stylesheet_imports</span><span class="st st-sg">&#39;</span>] <span class="op">=</span> <span class="st st-sg">&#39;</span><span class="st st-esc">\n</span><span class="st st-sg">&#39;</span>.<span class="name">join</span>(<span class="name">lines</span>)
+ <span class="kw">return</span> <span class="bn bn-pseudo">self</span>.<span class="name">ctx</span>.<span class="name">_cache</span>[<span class="st st-sg">&#39;</span><span class="st">cobalt/stylesheet_imports</span><span class="st st-sg">&#39;</span>]
+
+ <span class="kw">def </span><span class="fun">get_javascript_imports</span>(<span class="bn bn-pseudo">self</span>):
+ <span class="kw">if</span> <span class="op op-word">not</span> <span class="bn bn-pseudo">self</span>.<span class="name">cache_enabled</span> <span class="op op-word">or</span> <span class="st st-sg">&#39;</span><span class="st">cobalt/javascript_imports</span><span class="st st-sg">&#39;</span> <span class="op op-word">not</span> <span class="op op-word">in</span> <span class="bn bn-pseudo">self</span>.<span class="name">ctx</span>.<span class="name">_cache</span>:
+ <span class="name">handled</span> <span class="op">=</span> <span class="name">set</span>()
+ <span class="name">lines</span> <span class="op">=</span> []
+ <span class="name">onload</span> <span class="op">=</span> []
+ <span class="kw">for</span> <span class="name">comp</span> <span class="op op-word">in</span> <span class="bn bn-pseudo">self</span>.<span class="name">ctx</span>.<span class="name">get_components</span>(<span class="name">FileRequirements</span>):
+ <span class="kw">for</span> <span class="name">name</span> <span class="op op-word">in</span> <span class="name">comp</span>.<span class="name">get_javascript_imports</span>():
+ <span class="name">item</span> <span class="op">=</span> (<span class="name">comp</span>.<span class="name">package</span>, <span class="name">name</span>)
+ <span class="kw">if</span> <span class="name">item</span> <span class="op op-word">in</span> <span class="name">handled</span>:
+ <span class="kw">continue</span>
+
+ <span class="name">handled</span>.<span class="name">add</span>(<span class="name">item</span>)
+ <span class="name">imp</span> <span class="op">=</span> <span class="bn bn-pseudo">self</span>.<span class="name">ctx</span>.<span class="name">pkgmanager</span>.<span class="name">importers</span>[<span class="name">comp</span>.<span class="name">package</span>]
+ <span class="name">lines</span>.<span class="name">append</span>(<span class="name">imp</span>.<span class="name">get_data</span>(<span class="name">os</span>.<span class="name">path</span>.<span class="name">join</span>(<span class="st st-sg">&#39;</span><span class="st">static</span><span class="st st-sg">&#39;</span>, <span class="name">name</span>)))
+ <span class="bn bn-pseudo">self</span>.<span class="name">ctx</span>.<span class="name">_cache</span>[<span class="st st-sg">&#39;</span><span class="st">cobalt/javascript_imports</span><span class="st st-sg">&#39;</span>] <span class="op">=</span> <span class="st st-sg">&#39;</span><span class="st st-esc">\n\n</span><span class="st st-sg">&#39;</span>.<span class="name">join</span>(<span class="name">lines</span>)
+ <span class="kw">return</span> <span class="bn bn-pseudo">self</span>.<span class="name">ctx</span>.<span class="name">_cache</span>[<span class="st st-sg">&#39;</span><span class="st">cobalt/javascript_imports</span><span class="st st-sg">&#39;</span>]
+
+ <span class="kw">def </span><span class="fun">__call__</span>(<span class="bn bn-pseudo">self</span>, <span class="name">environ</span>, <span class="name">start_response</span>):
+ <span class="name">path</span> <span class="op">=</span> <span class="name">environ</span>.<span class="name">get</span>(<span class="st st-sg">&#39;</span><span class="st">PATH_INFO</span><span class="st st-sg">&#39;</span>, <span class="st st-sg">&#39;</span><span class="st">/</span><span class="st st-sg">&#39;</span>)
+ <span class="kw">if</span> <span class="name">path</span>.<span class="name">startswith</span>(<span class="st st-sg">&#39;</span><span class="st">/!cobalt/</span><span class="st st-sg">&#39;</span>):
+ <span class="name">mime_type</span> <span class="op">=</span> <span class="bn bn-pseudo">None</span>
+
+ <span class="kw">try</span>:
+ <span class="name">pkgname</span>, <span class="name">fname</span> <span class="op">=</span> <span class="name">path</span>[<span class="nb nb-int">9</span>:].<span class="name">split</span>(<span class="st st-sg">&#39;</span><span class="st">/</span><span class="st st-sg">&#39;</span>, <span class="nb nb-int">1</span>)
+ <span class="kw">if</span> <span class="name">pkgname</span> <span class="op">==</span> <span class="st st-sg">&#39;</span><span class="st">_import_</span><span class="st st-sg">&#39;</span>:
+ <span class="kw">if</span> <span class="name">fname</span> <span class="op">==</span> <span class="st st-sg">&#39;</span><span class="st">styles.css</span><span class="st st-sg">&#39;</span>:
+ <span class="name">mime_type</span> <span class="op">=</span> <span class="st st-sg">&#39;</span><span class="st">text/css</span><span class="st st-sg">&#39;</span>
+
+ <span class="name">content</span> <span class="op">=</span> <span class="bn bn-pseudo">self</span>.<span class="name">get_stylesheet_imports</span>()
+ <span class="kw">elif</span> <span class="name">fname</span> <span class="op">==</span> <span class="st st-sg">&#39;</span><span class="st">script.js</span><span class="st st-sg">&#39;</span>:
+ <span class="name">mime_type</span> <span class="op">=</span> <span class="st st-sg">&#39;</span><span class="st">application/x-javascript</span><span class="st st-sg">&#39;</span>
+
+ <span class="name">content</span> <span class="op">=</span> <span class="bn bn-pseudo">self</span>.<span class="name">get_javascript_imports</span>()
+ <span class="kw">else</span>:
+ <span class="name">guessed_type</span> <span class="op">=</span> <span class="name">guess_type</span>(<span class="name">fname</span>)
+ <span class="name">mime_type</span> <span class="op">=</span> <span class="name">guessed_type</span>[<span class="nb nb-int">0</span>] <span class="op op-word">or</span> <span class="st st-sg">&#39;</span><span class="st">text/plain</span><span class="st st-sg">&#39;</span>
+
+ <span class="name">imp</span> <span class="op">=</span> <span class="bn bn-pseudo">self</span>.<span class="name">ctx</span>.<span class="name">pkgmanager</span>.<span class="name">importers</span>[<span class="name">pkgname</span>]
+ <span class="name">content</span> <span class="op">=</span> <span class="name">imp</span>.<span class="name">get_data</span>(<span class="name">os</span>.<span class="name">path</span>.<span class="name">join</span>(<span class="st st-sg">&#39;</span><span class="st">static</span><span class="st st-sg">&#39;</span>, <span class="name">fname</span>))
+ <span class="kw">if</span> <span class="name">mime_type</span> <span class="op op-word">is</span> <span class="op op-word">not</span> <span class="bn bn-pseudo">None</span>:
+ <span class="name">expiry</span> <span class="op">=</span> <span class="name">time</span>.<span class="name">time</span>() <span class="op">+</span> <span class="nb nb-int">3600</span> <span class="cm"># cache for one hour</span>
+
+ <span class="name">expiry</span> <span class="op">=</span> <span class="name">time</span>.<span class="name">asctime</span>(<span class="name">time</span>.<span class="name">gmtime</span>(<span class="name">expiry</span>))
+ <span class="name">headers</span> <span class="op">=</span> [(<span class="st st-sg">&#39;</span><span class="st">Content-Type</span><span class="st st-sg">&#39;</span>, <span class="name">mime_type</span>),
+ (<span class="st st-sg">&#39;</span><span class="st">Cache-Control</span><span class="st st-sg">&#39;</span>, <span class="st st-sg">&#39;</span><span class="st">public</span><span class="st st-sg">&#39;</span>),
+ (<span class="st st-sg">&#39;</span><span class="st">Expires</span><span class="st st-sg">&#39;</span>, <span class="name">expiry</span>)]
+ <span class="name">start_response</span>(<span class="st st-sg">&#39;</span><span class="st">200 OK</span><span class="st st-sg">&#39;</span>, <span class="name">headers</span>)
+ <span class="kw">if</span> <span class="name">environ</span>.<span class="name">get</span>(<span class="st st-sg">&#39;</span><span class="st">REQUEST_METHOD</span><span class="st st-sg">&#39;</span>, <span class="st st-sg">&#39;</span><span class="st">GET</span><span class="st st-sg">&#39;</span>) <span class="op">==</span> <span class="st st-sg">&#39;</span><span class="st">HEAD</span><span class="st st-sg">&#39;</span>:
+ <span class="kw">return</span> []
+ <span class="kw">else</span>:
+ <span class="kw">return</span> [<span class="name">content</span>]
+ <span class="kw">except</span> (<span class="exc">ValueError</span>, <span class="exc">KeyError</span>, <span class="exc">IOError</span>):
+ <span class="cm"># XXX: output custom error message?</span>
+
+ <span class="kw">pass</span>
+ <span class="kw">return</span> <span class="bn bn-pseudo">self</span>.<span class="name">app</span>(<span class="name">environ</span>, <span class="name">start_response</span>)
+<span class="cm"># -*- coding: utf-8 -*-</span>
+<span class="st st-db">&quot;&quot;&quot;</span><span class="st">
+
+ pocoo.pkg.core.db
+ ~~~~~~~~~~~~~~~~~
+
+ Pocoo core database definition.
+
+ :copyright: 2006 by Armin Ronacher, Georg Brandl.
+ :license: GNU GPL, see LICENSE for more details.
+</span><span class="st st-db">&quot;&quot;&quot;</span>
+<span class="kw">from </span><span class="cls">pocoo.db</span><span class="kw"> import</span> <span class="name">meta</span>, <span class="name">DatabaseObserver</span>
+
+
+<span class="name">ANONYMOUS_USER_ID</span> <span class="op">=</span> <span class="op">-</span><span class="nb nb-int">1</span>
+
+<span class="name">DEFAULT_USER_ID</span> <span class="op">=</span> <span class="nb nb-int">0</span>
+
+
+<span class="name">sessions</span> <span class="op">=</span> <span class="name">meta</span>.<span class="name">Table</span>(<span class="st st-sg">&#39;</span><span class="st">core_sessions</span><span class="st st-sg">&#39;</span>,
+ <span class="name">meta</span>.<span class="name">Column</span>(<span class="st st-sg">&#39;</span><span class="st">session_key</span><span class="st st-sg">&#39;</span>, <span class="name">meta</span>.<span class="name">Unicode</span>(<span class="nb nb-int">40</span>), <span class="name">primary_key</span><span class="op">=</span><span class="bn bn-pseudo">True</span>),
+ <span class="name">meta</span>.<span class="name">Column</span>(<span class="st st-sg">&#39;</span><span class="st">ip_addr</span><span class="st st-sg">&#39;</span>, <span class="name">meta</span>.<span class="name">Unicode</span>(<span class="nb nb-int">15</span>)),
+ <span class="name">meta</span>.<span class="name">Column</span>(<span class="st st-sg">&#39;</span><span class="st">expires</span><span class="st st-sg">&#39;</span>, <span class="name">meta</span>.<span class="name">DateTime</span>),
+ <span class="name">meta</span>.<span class="name">Column</span>(<span class="st st-sg">&#39;</span><span class="st">last_reload</span><span class="st st-sg">&#39;</span>, <span class="name">meta</span>.<span class="name">DateTime</span>),
+ <span class="name">meta</span>.<span class="name">Column</span>(<span class="st st-sg">&#39;</span><span class="st">data</span><span class="st st-sg">&#39;</span>, <span class="name">meta</span>.<span class="name">Pickled</span>(<span class="bn">dict</span>)),
+ <span class="name">meta</span>.<span class="name">Column</span>(<span class="st st-sg">&#39;</span><span class="st">action</span><span class="st st-sg">&#39;</span>, <span class="name">meta</span>.<span class="name">Unicode</span>),
+)
+
+
+<span class="name">users</span> <span class="op">=</span> <span class="name">meta</span>.<span class="name">Table</span>(<span class="st st-sg">&#39;</span><span class="st">core_users</span><span class="st st-sg">&#39;</span>,
+ <span class="name">meta</span>.<span class="name">Column</span>(<span class="st st-sg">&#39;</span><span class="st">user_id</span><span class="st st-sg">&#39;</span>, <span class="name">meta</span>.<span class="name">Integer</span>, <span class="name">primary_key</span><span class="op">=</span><span class="bn bn-pseudo">True</span>),
+ <span class="name">meta</span>.<span class="name">Column</span>(<span class="st st-sg">&#39;</span><span class="st">subject_id</span><span class="st st-sg">&#39;</span>, <span class="name">meta</span>.<span class="name">Integer</span>,
+ <span class="name">meta</span>.<span class="name">ForeignKey</span>(<span class="st st-sg">&#39;</span><span class="st">core_acl_subjects.subject_id</span><span class="st st-sg">&#39;</span>)),
+ <span class="name">meta</span>.<span class="name">Column</span>(<span class="st st-sg">&#39;</span><span class="st">username</span><span class="st st-sg">&#39;</span>, <span class="name">meta</span>.<span class="name">Unicode</span>(<span class="nb nb-int">40</span>)),
+ <span class="name">meta</span>.<span class="name">Column</span>(<span class="st st-sg">&#39;</span><span class="st">email</span><span class="st st-sg">&#39;</span>, <span class="name">meta</span>.<span class="name">Unicode</span>(<span class="nb nb-int">250</span>)),
+ <span class="name">meta</span>.<span class="name">Column</span>(<span class="st st-sg">&#39;</span><span class="st">pwhash</span><span class="st st-sg">&#39;</span>, <span class="name">meta</span>.<span class="name">Unicode</span>(<span class="nb nb-int">60</span>)),
+ <span class="name">meta</span>.<span class="name">Column</span>(<span class="st st-sg">&#39;</span><span class="st">act_key</span><span class="st st-sg">&#39;</span>, <span class="name">meta</span>.<span class="name">Unicode</span>(<span class="nb nb-int">8</span>)),
+ <span class="name">meta</span>.<span class="name">Column</span>(<span class="st st-sg">&#39;</span><span class="st">language</span><span class="st st-sg">&#39;</span>, <span class="name">meta</span>.<span class="name">Unicode</span>(<span class="nb nb-int">2</span>)),
+ <span class="name">meta</span>.<span class="name">Column</span>(<span class="st st-sg">&#39;</span><span class="st">profile</span><span class="st st-sg">&#39;</span>, <span class="name">meta</span>.<span class="name">Pickled</span>(<span class="bn">dict</span>)),
+ <span class="name">meta</span>.<span class="name">Column</span>(<span class="st st-sg">&#39;</span><span class="st">settings</span><span class="st st-sg">&#39;</span>, <span class="name">meta</span>.<span class="name">Pickled</span>(<span class="bn">dict</span>)),
+ <span class="name">meta</span>.<span class="name">Column</span>(<span class="st st-sg">&#39;</span><span class="st">last_login</span><span class="st st-sg">&#39;</span>, <span class="name">meta</span>.<span class="name">DateTime</span>),
+ <span class="name">meta</span>.<span class="name">Column</span>(<span class="st st-sg">&#39;</span><span class="st">register_date</span><span class="st st-sg">&#39;</span>, <span class="name">meta</span>.<span class="name">DateTime</span>),
+ <span class="name">meta</span>.<span class="name">Column</span>(<span class="st st-sg">&#39;</span><span class="st">post_count</span><span class="st st-sg">&#39;</span>, <span class="name">meta</span>.<span class="name">Integer</span>),
+ <span class="name">meta</span>.<span class="name">Column</span>(<span class="st st-sg">&#39;</span><span class="st">read_threads</span><span class="st st-sg">&#39;</span>, <span class="name">meta</span>.<span class="name">Binary</span>),
+ <span class="name">meta</span>.<span class="name">Column</span>(<span class="st st-sg">&#39;</span><span class="st">read_posts</span><span class="st st-sg">&#39;</span>, <span class="name">meta</span>.<span class="name">Binary</span>),
+)
+
+
+<span class="name">groups</span> <span class="op">=</span> <span class="name">meta</span>.<span class="name">Table</span>(<span class="st st-sg">&#39;</span><span class="st">core_groups</span><span class="st st-sg">&#39;</span>,
+ <span class="name">meta</span>.<span class="name">Column</span>(<span class="st st-sg">&#39;</span><span class="st">group_id</span><span class="st st-sg">&#39;</span>, <span class="name">meta</span>.<span class="name">Integer</span>, <span class="name">primary_key</span><span class="op">=</span><span class="bn bn-pseudo">True</span>),
+ <span class="name">meta</span>.<span class="name">Column</span>(<span class="st st-sg">&#39;</span><span class="st">subject_id</span><span class="st st-sg">&#39;</span>, <span class="name">meta</span>.<span class="name">Integer</span>,
+ <span class="name">meta</span>.<span class="name">ForeignKey</span>(<span class="st st-sg">&#39;</span><span class="st">core_acl_subjects.subject_id</span><span class="st st-sg">&#39;</span>)),
+ <span class="name">meta</span>.<span class="name">Column</span>(<span class="st st-sg">&#39;</span><span class="st">name</span><span class="st st-sg">&#39;</span>, <span class="name">meta</span>.<span class="name">Unicode</span>(<span class="nb nb-int">40</span>)),
+ <span class="name">meta</span>.<span class="name">Column</span>(<span class="st st-sg">&#39;</span><span class="st">public</span><span class="st st-sg">&#39;</span>, <span class="name">meta</span>.<span class="name">Boolean</span>),
+ <span class="name">meta</span>.<span class="name">Column</span>(<span class="st st-sg">&#39;</span><span class="st">hidden</span><span class="st st-sg">&#39;</span>, <span class="name">meta</span>.<span class="name">Boolean</span>)
+)
+
+
+<span class="name">group_members</span> <span class="op">=</span> <span class="name">meta</span>.<span class="name">Table</span>(<span class="st st-sg">&#39;</span><span class="st">core_group_members</span><span class="st st-sg">&#39;</span>,
+ <span class="name">meta</span>.<span class="name">Column</span>(<span class="st st-sg">&#39;</span><span class="st">user_id</span><span class="st st-sg">&#39;</span>, <span class="name">meta</span>.<span class="name">Integer</span>,
+ <span class="name">meta</span>.<span class="name">ForeignKey</span>(<span class="st st-sg">&#39;</span><span class="st">core_users.user_id</span><span class="st st-sg">&#39;</span>)),
+ <span class="name">meta</span>.<span class="name">Column</span>(<span class="st st-sg">&#39;</span><span class="st">group_id</span><span class="st st-sg">&#39;</span>, <span class="name">meta</span>.<span class="name">Integer</span>,
+ <span class="name">meta</span>.<span class="name">ForeignKey</span>(<span class="st st-sg">&#39;</span><span class="st">core_groups.group_id</span><span class="st st-sg">&#39;</span>))
+)
+
+
+<span class="name">forums</span> <span class="op">=</span> <span class="name">meta</span>.<span class="name">Table</span>(<span class="st st-sg">&#39;</span><span class="st">core_forums</span><span class="st st-sg">&#39;</span>,
+ <span class="name">meta</span>.<span class="name">Column</span>(<span class="st st-sg">&#39;</span><span class="st">forum_id</span><span class="st st-sg">&#39;</span>, <span class="name">meta</span>.<span class="name">Integer</span>, <span class="name">primary_key</span><span class="op">=</span><span class="bn bn-pseudo">True</span>),
+ <span class="name">meta</span>.<span class="name">Column</span>(<span class="st st-sg">&#39;</span><span class="st">parent_id</span><span class="st st-sg">&#39;</span>, <span class="name">meta</span>.<span class="name">Integer</span>,
+ <span class="name">meta</span>.<span class="name">ForeignKey</span>(<span class="st st-sg">&#39;</span><span class="st">core_forums.forum_id</span><span class="st st-sg">&#39;</span>)),
+ <span class="name">meta</span>.<span class="name">Column</span>(<span class="st st-sg">&#39;</span><span class="st">object_id</span><span class="st st-sg">&#39;</span>, <span class="name">meta</span>.<span class="name">Integer</span>,
+ <span class="name">meta</span>.<span class="name">ForeignKey</span>(<span class="st st-sg">&#39;</span><span class="st">core_acl_objects.object_id</span><span class="st st-sg">&#39;</span>)),
+ <span class="name">meta</span>.<span class="name">Column</span>(<span class="st st-sg">&#39;</span><span class="st">name</span><span class="st st-sg">&#39;</span>, <span class="name">meta</span>.<span class="name">Unicode</span>(<span class="nb nb-int">100</span>)),
+ <span class="name">meta</span>.<span class="name">Column</span>(<span class="st st-sg">&#39;</span><span class="st">description</span><span class="st st-sg">&#39;</span>, <span class="name">meta</span>.<span class="name">Unicode</span>),
+ <span class="name">meta</span>.<span class="name">Column</span>(<span class="st st-sg">&#39;</span><span class="st">position</span><span class="st st-sg">&#39;</span>, <span class="name">meta</span>.<span class="name">Integer</span>),
+ <span class="name">meta</span>.<span class="name">Column</span>(<span class="st st-sg">&#39;</span><span class="st">link</span><span class="st st-sg">&#39;</span>, <span class="name">meta</span>.<span class="name">Unicode</span>(<span class="nb nb-int">100</span>)),
+ <span class="cm">#XXX: foreign key doesn&#39;t work</span>
+
+ <span class="name">meta</span>.<span class="name">Column</span>(<span class="st st-sg">&#39;</span><span class="st">last_post_id</span><span class="st st-sg">&#39;</span>, <span class="name">meta</span>.<span class="name">Integer</span>),
+ <span class="name">meta</span>.<span class="name">Column</span>(<span class="st st-sg">&#39;</span><span class="st">post_count</span><span class="st st-sg">&#39;</span>, <span class="name">meta</span>.<span class="name">Integer</span>),
+ <span class="name">meta</span>.<span class="name">Column</span>(<span class="st st-sg">&#39;</span><span class="st">thread_count</span><span class="st st-sg">&#39;</span>, <span class="name">meta</span>.<span class="name">Integer</span>)
+)
+
+
+<span class="name">posts</span> <span class="op">=</span> <span class="name">meta</span>.<span class="name">Table</span>(<span class="st st-sg">&#39;</span><span class="st">core_posts</span><span class="st st-sg">&#39;</span>,
+ <span class="name">meta</span>.<span class="name">Column</span>(<span class="st st-sg">&#39;</span><span class="st">post_id</span><span class="st st-sg">&#39;</span>, <span class="name">meta</span>.<span class="name">Integer</span>, <span class="name">primary_key</span><span class="op">=</span><span class="bn bn-pseudo">True</span>),
+ <span class="name">meta</span>.<span class="name">Column</span>(<span class="st st-sg">&#39;</span><span class="st">forum_id</span><span class="st st-sg">&#39;</span>, <span class="name">meta</span>.<span class="name">Integer</span>,
+ <span class="name">meta</span>.<span class="name">ForeignKey</span>(<span class="st st-sg">&#39;</span><span class="st">core_forums.forum_id</span><span class="st st-sg">&#39;</span>)),
+ <span class="name">meta</span>.<span class="name">Column</span>(<span class="st st-sg">&#39;</span><span class="st">parent_id</span><span class="st st-sg">&#39;</span>, <span class="name">meta</span>.<span class="name">Integer</span>,
+ <span class="name">meta</span>.<span class="name">ForeignKey</span>(<span class="st st-sg">&#39;</span><span class="st">core_posts.post_id</span><span class="st st-sg">&#39;</span>)),
+ <span class="name">meta</span>.<span class="name">Column</span>(<span class="st st-sg">&#39;</span><span class="st">root_post_id</span><span class="st st-sg">&#39;</span>, <span class="name">meta</span>.<span class="name">Integer</span>,
+ <span class="name">meta</span>.<span class="name">ForeignKey</span>(<span class="st st-sg">&#39;</span><span class="st">core_posts.post_id</span><span class="st st-sg">&#39;</span>)),
+ <span class="name">meta</span>.<span class="name">Column</span>(<span class="st st-sg">&#39;</span><span class="st">object_id</span><span class="st st-sg">&#39;</span>, <span class="name">meta</span>.<span class="name">Integer</span>,
+ <span class="name">meta</span>.<span class="name">ForeignKey</span>(<span class="st st-sg">&#39;</span><span class="st">core_acl_objects.object_id</span><span class="st st-sg">&#39;</span>)),
+ <span class="name">meta</span>.<span class="name">Column</span>(<span class="st st-sg">&#39;</span><span class="st">post_count</span><span class="st st-sg">&#39;</span>, <span class="name">meta</span>.<span class="name">Integer</span>),
+ <span class="name">meta</span>.<span class="name">Column</span>(<span class="st st-sg">&#39;</span><span class="st">view_count</span><span class="st st-sg">&#39;</span>, <span class="name">meta</span>.<span class="name">Integer</span>),
+ <span class="name">meta</span>.<span class="name">Column</span>(<span class="st st-sg">&#39;</span><span class="st">author_id</span><span class="st st-sg">&#39;</span>, <span class="name">meta</span>.<span class="name">Integer</span>,
+ <span class="name">meta</span>.<span class="name">ForeignKey</span>(<span class="st st-sg">&#39;</span><span class="st">core_users.user_id</span><span class="st st-sg">&#39;</span>)),
+ <span class="name">meta</span>.<span class="name">Column</span>(<span class="st st-sg">&#39;</span><span class="st">username</span><span class="st st-sg">&#39;</span>, <span class="name">meta</span>.<span class="name">Unicode</span>(<span class="nb nb-int">200</span>)),
+ <span class="name">meta</span>.<span class="name">Column</span>(<span class="st st-sg">&#39;</span><span class="st">title</span><span class="st st-sg">&#39;</span>, <span class="name">meta</span>.<span class="name">Unicode</span>(<span class="nb nb-int">200</span>)),
+ <span class="name">meta</span>.<span class="name">Column</span>(<span class="st st-sg">&#39;</span><span class="st">text</span><span class="st st-sg">&#39;</span>, <span class="name">meta</span>.<span class="name">Unicode</span>),
+ <span class="name">meta</span>.<span class="name">Column</span>(<span class="st st-sg">&#39;</span><span class="st">timestamp</span><span class="st st-sg">&#39;</span>, <span class="name">meta</span>.<span class="name">DateTime</span>)
+)
+
+
+<span class="name">privileges</span> <span class="op">=</span> <span class="name">meta</span>.<span class="name">Table</span>(<span class="st st-sg">&#39;</span><span class="st">core_privileges</span><span class="st st-sg">&#39;</span>,
+ <span class="name">meta</span>.<span class="name">Column</span>(<span class="st st-sg">&#39;</span><span class="st">priv_id</span><span class="st st-sg">&#39;</span>, <span class="name">meta</span>.<span class="name">Integer</span>, <span class="name">primary_key</span><span class="op">=</span><span class="bn bn-pseudo">True</span>),
+ <span class="name">meta</span>.<span class="name">Column</span>(<span class="st st-sg">&#39;</span><span class="st">name</span><span class="st st-sg">&#39;</span>, <span class="name">meta</span>.<span class="name">Unicode</span>(<span class="nb nb-int">100</span>))
+)
+
+
+<span class="name">acl_mapping</span> <span class="op">=</span> <span class="name">meta</span>.<span class="name">Table</span>(<span class="st st-sg">&#39;</span><span class="st">core_acl_mapping</span><span class="st st-sg">&#39;</span>,
+ <span class="name">meta</span>.<span class="name">Column</span>(<span class="st st-sg">&#39;</span><span class="st">priv_id</span><span class="st st-sg">&#39;</span>, <span class="name">meta</span>.<span class="name">Integer</span>,
+ <span class="name">meta</span>.<span class="name">ForeignKey</span>(<span class="st st-sg">&#39;</span><span class="st">core_privileges.priv_id</span><span class="st st-sg">&#39;</span>)),
+ <span class="name">meta</span>.<span class="name">Column</span>(<span class="st st-sg">&#39;</span><span class="st">subject_id</span><span class="st st-sg">&#39;</span>, <span class="name">meta</span>.<span class="name">Integer</span>,
+ <span class="name">meta</span>.<span class="name">ForeignKey</span>(<span class="st st-sg">&#39;</span><span class="st">core_acl_subjects.subject_id</span><span class="st st-sg">&#39;</span>)),
+ <span class="name">meta</span>.<span class="name">Column</span>(<span class="st st-sg">&#39;</span><span class="st">object_id</span><span class="st st-sg">&#39;</span>, <span class="name">meta</span>.<span class="name">Integer</span>,
+ <span class="name">meta</span>.<span class="name">ForeignKey</span>(<span class="st st-sg">&#39;</span><span class="st">core_acl_objects.object_id</span><span class="st st-sg">&#39;</span>)),
+ <span class="name">meta</span>.<span class="name">Column</span>(<span class="st st-sg">&#39;</span><span class="st">state</span><span class="st st-sg">&#39;</span>, <span class="name">meta</span>.<span class="name">Integer</span>)
+)
+
+
+<span class="name">acl_subjects</span> <span class="op">=</span> <span class="name">meta</span>.<span class="name">Table</span>(<span class="st st-sg">&#39;</span><span class="st">core_acl_subjects</span><span class="st st-sg">&#39;</span>,
+ <span class="name">meta</span>.<span class="name">Column</span>(<span class="st st-sg">&#39;</span><span class="st">subject_id</span><span class="st st-sg">&#39;</span>, <span class="name">meta</span>.<span class="name">Integer</span>, <span class="name">primary_key</span><span class="op">=</span><span class="bn bn-pseudo">True</span>),
+ <span class="name">meta</span>.<span class="name">Column</span>(<span class="st st-sg">&#39;</span><span class="st">subject_type</span><span class="st st-sg">&#39;</span>, <span class="name">meta</span>.<span class="name">String</span>(<span class="nb nb-int">10</span>))
+)
+
+
+<span class="name">acl_objects</span> <span class="op">=</span> <span class="name">meta</span>.<span class="name">Table</span>(<span class="st st-sg">&#39;</span><span class="st">core_acl_objects</span><span class="st st-sg">&#39;</span>,
+ <span class="name">meta</span>.<span class="name">Column</span>(<span class="st st-sg">&#39;</span><span class="st">object_id</span><span class="st st-sg">&#39;</span>, <span class="name">meta</span>.<span class="name">Integer</span>, <span class="name">primary_key</span><span class="op">=</span><span class="bn bn-pseudo">True</span>),
+ <span class="name">meta</span>.<span class="name">Column</span>(<span class="st st-sg">&#39;</span><span class="st">object_type</span><span class="st st-sg">&#39;</span>, <span class="name">meta</span>.<span class="name">String</span>(<span class="nb nb-int">10</span>))
+)
+
+
+<span class="name">acl_subject_join</span> <span class="op">=</span> <span class="name">meta</span>.<span class="name">polymorphic_union</span>({
+ <span class="st st-sg">&#39;</span><span class="st">user</span><span class="st st-sg">&#39;</span>: <span class="name">users</span>,
+ <span class="st st-sg">&#39;</span><span class="st">group</span><span class="st st-sg">&#39;</span>: <span class="name">groups</span>
+}, <span class="st st-sg">&#39;</span><span class="st">subject_type</span><span class="st st-sg">&#39;</span>)
+
+
+<span class="name">acl_object_join</span> <span class="op">=</span> <span class="name">meta</span>.<span class="name">polymorphic_union</span>({
+ <span class="st st-sg">&#39;</span><span class="st">forum</span><span class="st st-sg">&#39;</span>: <span class="name">forums</span>,
+ <span class="st st-sg">&#39;</span><span class="st">post</span><span class="st st-sg">&#39;</span>: <span class="name">posts</span>
+}, <span class="st st-sg">&#39;</span><span class="st">object_type</span><span class="st st-sg">&#39;</span>)
+
+
+<span class="kw">class </span><span class="cls">CoreTableObserver</span>(<span class="name">DatabaseObserver</span>):
+
+ <span class="kw">def </span><span class="fun">after_table_creation</span>(<span class="bn bn-pseudo">self</span>, <span class="name">table</span>):
+ <span class="kw">if</span> <span class="name">table</span> <span class="op op-word">is</span> <span class="name">users</span>:
+ <span class="name">e</span> <span class="op">=</span> <span class="bn bn-pseudo">self</span>.<span class="name">ctx</span>.<span class="name">engine</span>.<span class="name">execute</span>
+
+ <span class="name">e</span>(<span class="name">users</span>.<span class="name">insert</span>(), <span class="name">user_id</span><span class="op">=</span><span class="name">ANONYMOUS_USER_ID</span>, <span class="name">username</span><span class="op">=</span><span class="st st-sg">&#39;</span><span class="st">anonymous</span><span class="st st-sg">&#39;</span>)
+ <span class="name">e</span>(<span class="name">users</span>.<span class="name">insert</span>(), <span class="name">user_id</span><span class="op">=</span><span class="name">DEFAULT_USER_ID</span>, <span class="name">username</span><span class="op">=</span><span class="st st-sg">&#39;</span><span class="st">default</span><span class="st st-sg">&#39;</span>)
+
+<span class="cm"># -*- coding: utf-8 -*-</span>
+<span class="st st-db">&quot;&quot;&quot;</span><span class="st">
+ pocoo.pkg.core.feeds
+ ~~~~~~~~~~~~~~~~~~~~
+
+ Provides RSS Feeds.
+
+ :copyright: 2006 by Armin Ronacher.
+ :license: GNU GPL, see LICENSE for more details.
+</span><span class="st st-db">&quot;&quot;&quot;</span>
+<span class="kw">from </span><span class="cls">pocoo</span><span class="kw"> import</span> <span class="name">Component</span>
+<span class="kw">from </span><span class="cls">pocoo.http</span><span class="kw"> import</span> <span class="name">PageNotFound</span>, <span class="name">Response</span>
+
+<span class="kw">from </span><span class="cls">pocoo.application</span><span class="kw"> import</span> <span class="name">RequestHandler</span>
+<span class="kw">from </span><span class="cls">pocoo.db</span><span class="kw"> import</span> <span class="name">meta</span>
+<span class="kw">from </span><span class="cls">pocoo.utils.feed</span><span class="kw"> import</span> <span class="name">Feed</span>
+
+<span class="kw">from </span><span class="cls">pocoo.pkg.core.db</span><span class="kw"> import</span> <span class="name">forums</span>, <span class="name">posts</span>, <span class="name">users</span>
+<span class="kw">from </span><span class="cls">pocoo.pkg.core.textfmt</span><span class="kw"> import</span> <span class="name">parse_and_render</span>
+
+
+<span class="kw">class </span><span class="cls">FeedProvider</span>(<span class="name">Component</span>):
+ <span class="cm">#: identifier for this feed. must be lowercase</span>
+ <span class="name">identifier</span> <span class="op">=</span> <span class="st st-sg">&#39;</span><span class="st">unknown</span><span class="st st-sg">&#39;</span>
+
+ <span class="kw">def </span><span class="fun">get_feed</span>(<span class="bn bn-pseudo">self</span>, <span class="name">req</span>, <span class="name">parameter</span>):
+ <span class="st st-db">&quot;&quot;&quot;</span><span class="st">
+ Return a dict in the following form::
+
+ {&#39;title&#39;: &#39;Title of this feed&#39;,
+ &#39;description&#39;: &#39;Description of this feed&#39;,
+ &#39;items&#39;: [{
+ &#39;title&#39;: &#39;title of this item&#39;,
+ &#39;link&#39;: &#39;relative link of this item&#39;,
+ &#39;author&#39;: &#39;author of this item&#39;,
+ &#39;description&#39;: &#39;description of this item&#39;,
+ &#39;pub_date&#39;: &#39;date of this item&#39;
+
+ }]}
+
+ Can raise a `FeedNotFound` exception.
+ </span><span class="st st-db">&quot;&quot;&quot;</span>
+
+ <span class="deco">@property</span>
+ <span class="kw">def </span><span class="fun">url</span>(<span class="bn bn-pseudo">self</span>):
+ <span class="kw">return</span> <span class="bn bn-pseudo">self</span>.<span class="name">ctx</span>.<span class="name">make_url</span>(<span class="st st-sg">&#39;</span><span class="st">feeds/</span><span class="st st-int">%s</span><span class="st">.xml</span><span class="st st-sg">&#39;</span> <span class="op">%</span> <span class="bn bn-pseudo">self</span>.<span class="name">identifier</span>)
+
+
+
+<span class="kw">class </span><span class="cls">FeedNotFound</span>(<span class="exc">Exception</span>):
+ <span class="kw">pass</span>
+
+
+<span class="kw">class </span><span class="cls">ThreadFeed</span>(<span class="name">FeedProvider</span>):
+ <span class="name">identifier</span> <span class="op">=</span> <span class="st st-sg">&#39;</span><span class="st">thread</span><span class="st st-sg">&#39;</span>
+
+ <span class="kw">def </span><span class="fun">get_feed</span>(<span class="bn bn-pseudo">self</span>, <span class="name">req</span>, <span class="name">post_id</span>):
+ <span class="name">_</span> <span class="op">=</span> <span class="name">req</span>.<span class="name">gettext</span>
+
+ <span class="kw">try</span>:
+ <span class="name">post_id</span> <span class="op">=</span> <span class="bn">int</span>(<span class="name">post_id</span>)
+ <span class="kw">except</span>:
+ <span class="kw">raise</span> <span class="name">FeedNotFound</span>()
+ <span class="name">row</span> <span class="op">=</span> <span class="bn bn-pseudo">self</span>.<span class="name">ctx</span>.<span class="name">engine</span>.<span class="name">execute</span>(<span class="name">meta</span>.<span class="name">select</span>([<span class="name">posts</span>.<span class="name">c</span>.<span class="name">root_post_id</span>],
+ (<span class="name">posts</span>.<span class="name">c</span>.<span class="name">post_id</span> <span class="op">==</span> <span class="name">post_id</span>)
+ )).<span class="name">fetchone</span>()
+ <span class="kw">if</span> <span class="name">row</span> <span class="op op-word">is</span> <span class="bn bn-pseudo">None</span>:
+ <span class="kw">raise</span> <span class="name">FeedNotFound</span>()
+ <span class="name">root_post_id</span> <span class="op">=</span> <span class="name">row</span>[<span class="nb nb-int">0</span>]
+ <span class="cm"># select data</span>
+
+ <span class="name">result</span> <span class="op">=</span> <span class="bn bn-pseudo">self</span>.<span class="name">ctx</span>.<span class="name">engine</span>.<span class="name">execute</span>(<span class="name">meta</span>.<span class="name">select</span>(
+ [<span class="name">posts</span>.<span class="name">c</span>.<span class="name">post_id</span>, <span class="name">posts</span>.<span class="name">c</span>.<span class="name">title</span>, <span class="name">posts</span>.<span class="name">c</span>.<span class="name">text</span>,
+ <span class="name">posts</span>.<span class="name">c</span>.<span class="name">timestamp</span>, <span class="name">users</span>.<span class="name">c</span>.<span class="name">username</span>],
+ (<span class="name">posts</span>.<span class="name">c</span>.<span class="name">root_post_id</span> <span class="op">==</span> <span class="name">root_post_id</span>) <span class="op">&amp;</span>
+
+ (<span class="name">users</span>.<span class="name">c</span>.<span class="name">user_id</span> <span class="op">==</span> <span class="name">posts</span>.<span class="name">c</span>.<span class="name">author_id</span>),
+ <span class="name">order_by</span><span class="op">=</span>[<span class="name">meta</span>.<span class="name">desc</span>(<span class="name">posts</span>.<span class="name">c</span>.<span class="name">post_id</span>)],
+ <span class="name">limit</span><span class="op">=</span><span class="nb nb-int">10</span>
+
+ ))
+ <span class="kw">return</span> {
+ <span class="st st-sg">&#39;</span><span class="st">title</span><span class="st st-sg">&#39;</span>: <span class="name">_</span>(<span class="st st-sg">&#39;</span><span class="st">Last Posts in Thread </span><span class="st st-int">%d</span><span class="st st-sg">&#39;</span>) <span class="op">%</span> <span class="name">root_post_id</span>,
+ <span class="st st-sg">&#39;</span><span class="st">description</span><span class="st st-sg">&#39;</span>: <span class="name">_</span>(<span class="st st-sg">&#39;</span><span class="st">The last 10 posts in Thread </span><span class="st st-int">%d</span><span class="st st-sg">&#39;</span>) <span class="op">%</span> <span class="name">root_post_id</span>,
+ <span class="st st-sg">&#39;</span><span class="st">items</span><span class="st st-sg">&#39;</span>: [{
+ <span class="st st-sg">&#39;</span><span class="st">title</span><span class="st st-sg">&#39;</span>: <span class="name">post</span>[<span class="st st-sg">&#39;</span><span class="st">title</span><span class="st st-sg">&#39;</span>],
+ <span class="st st-sg">&#39;</span><span class="st">link</span><span class="st st-sg">&#39;</span>: <span class="st st-sg">&#39;</span><span class="st">post/</span><span class="st st-int">%d</span><span class="st st-sg">&#39;</span> <span class="op">%</span> <span class="name">post</span>[<span class="st st-sg">&#39;</span><span class="st">post_id</span><span class="st st-sg">&#39;</span>],
+ <span class="st st-sg">&#39;</span><span class="st">description</span><span class="st st-sg">&#39;</span>: <span class="name">parse_and_render</span>(<span class="name">req</span>, <span class="name">post</span>[<span class="st st-sg">&#39;</span><span class="st">text</span><span class="st st-sg">&#39;</span>]),
+ <span class="st st-sg">&#39;</span><span class="st">author</span><span class="st st-sg">&#39;</span>: <span class="name">post</span>[<span class="st st-sg">&#39;</span><span class="st">username</span><span class="st st-sg">&#39;</span>],
+ <span class="st st-sg">&#39;</span><span class="st">pub_date</span><span class="st st-sg">&#39;</span>: <span class="name">post</span>[<span class="st st-sg">&#39;</span><span class="st">timestamp</span><span class="st st-sg">&#39;</span>]
+ } <span class="kw">for</span> <span class="name">post</span> <span class="op op-word">in</span> <span class="name">result</span>]
+ }
+
+
+
+<span class="kw">class </span><span class="cls">ForumFeed</span>(<span class="name">FeedProvider</span>):
+ <span class="name">identifier</span> <span class="op">=</span> <span class="st st-sg">&#39;</span><span class="st">forum</span><span class="st st-sg">&#39;</span>
+
+ <span class="kw">def </span><span class="fun">get_feed</span>(<span class="bn bn-pseudo">self</span>, <span class="name">req</span>, <span class="name">forum_id</span>):
+ <span class="name">_</span> <span class="op">=</span> <span class="name">req</span>.<span class="name">gettext</span>
+
+ <span class="kw">try</span>:
+ <span class="name">forum_id</span> <span class="op">=</span> <span class="bn">int</span>(<span class="name">forum_id</span>)
+ <span class="kw">except</span>:
+ <span class="kw">raise</span> <span class="name">FeedNotFound</span>()
+ <span class="kw">if</span> <span class="bn bn-pseudo">self</span>.<span class="name">ctx</span>.<span class="name">engine</span>.<span class="name">execute</span>(<span class="name">meta</span>.<span class="name">select</span>([<span class="name">forums</span>.<span class="name">c</span>.<span class="name">forum_id</span>],
+ (<span class="name">forums</span>.<span class="name">c</span>.<span class="name">forum_id</span> <span class="op">==</span> <span class="name">forum_id</span>)
+ )).<span class="name">fetchone</span>() <span class="op op-word">is</span> <span class="bn bn-pseudo">None</span>:
+ <span class="kw">raise</span> <span class="name">FeedNotFound</span>()
+ <span class="cm"># select data</span>
+
+ <span class="name">result</span> <span class="op">=</span> <span class="bn bn-pseudo">self</span>.<span class="name">ctx</span>.<span class="name">engine</span>.<span class="name">execute</span>(<span class="name">meta</span>.<span class="name">select</span>(
+ [<span class="name">posts</span>.<span class="name">c</span>.<span class="name">post_id</span>, <span class="name">posts</span>.<span class="name">c</span>.<span class="name">title</span>, <span class="name">posts</span>.<span class="name">c</span>.<span class="name">text</span>,
+ <span class="name">posts</span>.<span class="name">c</span>.<span class="name">timestamp</span>, <span class="name">users</span>.<span class="name">c</span>.<span class="name">username</span>],
+ (<span class="name">posts</span>.<span class="name">c</span>.<span class="name">forum_id</span> <span class="op">==</span> <span class="name">forum_id</span>) <span class="op">&amp;</span>
+
+ (<span class="name">users</span>.<span class="name">c</span>.<span class="name">user_id</span> <span class="op">==</span> <span class="name">posts</span>.<span class="name">c</span>.<span class="name">author_id</span>),
+ <span class="name">order_by</span><span class="op">=</span>[<span class="name">meta</span>.<span class="name">desc</span>(<span class="name">posts</span>.<span class="name">c</span>.<span class="name">post_id</span>)],
+ <span class="name">limit</span><span class="op">=</span><span class="nb nb-int">10</span>
+
+ ))
+ <span class="kw">return</span> {
+ <span class="st st-sg">&#39;</span><span class="st">title</span><span class="st st-sg">&#39;</span>: <span class="name">_</span>(<span class="st st-sg">&#39;</span><span class="st">Last Posts in Forum </span><span class="st st-int">%d</span><span class="st st-sg">&#39;</span>) <span class="op">%</span> <span class="name">forum_id</span>,
+ <span class="st st-sg">&#39;</span><span class="st">description</span><span class="st st-sg">&#39;</span>: <span class="name">_</span>(<span class="st st-sg">&#39;</span><span class="st">The last 10 posts of forum </span><span class="st st-int">%d</span><span class="st st-sg">&#39;</span>) <span class="op">%</span> <span class="name">forum_id</span>,
+ <span class="st st-sg">&#39;</span><span class="st">items</span><span class="st st-sg">&#39;</span>: [{
+ <span class="st st-sg">&#39;</span><span class="st">title</span><span class="st st-sg">&#39;</span>: <span class="name">post</span>[<span class="st st-sg">&#39;</span><span class="st">title</span><span class="st st-sg">&#39;</span>],
+ <span class="st st-sg">&#39;</span><span class="st">link</span><span class="st st-sg">&#39;</span>: <span class="st st-sg">&#39;</span><span class="st">post/</span><span class="st st-int">%d</span><span class="st st-sg">&#39;</span> <span class="op">%</span> <span class="name">post</span>[<span class="st st-sg">&#39;</span><span class="st">post_id</span><span class="st st-sg">&#39;</span>],
+ <span class="st st-sg">&#39;</span><span class="st">description</span><span class="st st-sg">&#39;</span>: <span class="name">parse_and_render</span>(<span class="name">req</span>, <span class="name">post</span>[<span class="st st-sg">&#39;</span><span class="st">text</span><span class="st st-sg">&#39;</span>]),
+ <span class="st st-sg">&#39;</span><span class="st">author</span><span class="st st-sg">&#39;</span>: <span class="name">post</span>[<span class="st st-sg">&#39;</span><span class="st">username</span><span class="st st-sg">&#39;</span>],
+ <span class="st st-sg">&#39;</span><span class="st">pub_date</span><span class="st st-sg">&#39;</span>: <span class="name">post</span>[<span class="st st-sg">&#39;</span><span class="st">timestamp</span><span class="st st-sg">&#39;</span>]
+ } <span class="kw">for</span> <span class="name">post</span> <span class="op op-word">in</span> <span class="name">result</span>]
+ }
+
+
+
+<span class="kw">class </span><span class="cls">RecentChangesFeed</span>(<span class="name">FeedProvider</span>):
+ <span class="name">identifier</span> <span class="op">=</span> <span class="st st-sg">&#39;</span><span class="st">recent</span><span class="st st-sg">&#39;</span>
+
+ <span class="kw">def </span><span class="fun">get_title</span>(<span class="bn bn-pseudo">self</span>, <span class="name">req</span>):
+ <span class="name">_</span> <span class="op">=</span> <span class="name">req</span>.<span class="name">gettext</span>
+
+ <span class="kw">return</span> <span class="name">_</span>(<span class="st st-sg">&#39;</span><span class="st">Recent Changes</span><span class="st st-sg">&#39;</span>)
+
+ <span class="kw">def </span><span class="fun">get_description</span>(<span class="bn bn-pseudo">self</span>, <span class="name">req</span>):
+ <span class="name">_</span> <span class="op">=</span> <span class="name">req</span>.<span class="name">gettext</span>
+
+ <span class="kw">return</span> <span class="name">_</span>(<span class="st st-sg">&#39;</span><span class="st">The recent posts</span><span class="st st-sg">&#39;</span>)
+
+ <span class="kw">def </span><span class="fun">get_feed</span>(<span class="bn bn-pseudo">self</span>, <span class="name">req</span>, <span class="name">parameter</span>):
+ <span class="name">_</span> <span class="op">=</span> <span class="name">req</span>.<span class="name">gettext</span>
+
+ <span class="kw">if</span> <span class="name">parameter</span>:
+ <span class="kw">raise</span> <span class="name">FeedNotFound</span>()
+ <span class="name">result</span> <span class="op">=</span> <span class="bn bn-pseudo">self</span>.<span class="name">ctx</span>.<span class="name">engine</span>.<span class="name">execute</span>(<span class="name">meta</span>.<span class="name">select</span>(
+ [<span class="name">posts</span>.<span class="name">c</span>.<span class="name">post_id</span>, <span class="name">posts</span>.<span class="name">c</span>.<span class="name">title</span>, <span class="name">posts</span>.<span class="name">c</span>.<span class="name">text</span>,
+ <span class="name">posts</span>.<span class="name">c</span>.<span class="name">timestamp</span>, <span class="name">users</span>.<span class="name">c</span>.<span class="name">username</span>],
+ (<span class="name">users</span>.<span class="name">c</span>.<span class="name">user_id</span> <span class="op">==</span> <span class="name">posts</span>.<span class="name">c</span>.<span class="name">author_id</span>),
+ <span class="name">order_by</span><span class="op">=</span>[<span class="name">meta</span>.<span class="name">desc</span>(<span class="name">posts</span>.<span class="name">c</span>.<span class="name">post_id</span>)],
+ <span class="name">limit</span><span class="op">=</span><span class="nb nb-int">10</span>
+
+ ))
+ <span class="kw">return</span> {
+ <span class="st st-sg">&#39;</span><span class="st">title</span><span class="st st-sg">&#39;</span>: <span class="name">_</span>(<span class="st st-sg">&#39;</span><span class="st">Recent Changes</span><span class="st st-sg">&#39;</span>),
+ <span class="st st-sg">&#39;</span><span class="st">description</span><span class="st st-sg">&#39;</span>: <span class="name">_</span>(<span class="st st-sg">&#39;</span><span class="st">The most recent posts</span><span class="st st-sg">&#39;</span>),
+ <span class="st st-sg">&#39;</span><span class="st">items</span><span class="st st-sg">&#39;</span>: [{
+ <span class="st st-sg">&#39;</span><span class="st">title</span><span class="st st-sg">&#39;</span>: <span class="name">post</span>[<span class="st st-sg">&#39;</span><span class="st">title</span><span class="st st-sg">&#39;</span>],
+ <span class="st st-sg">&#39;</span><span class="st">link</span><span class="st st-sg">&#39;</span>: <span class="st st-sg">&#39;</span><span class="st">post/</span><span class="st st-int">%d</span><span class="st st-sg">&#39;</span> <span class="op">%</span> <span class="name">post</span>[<span class="st st-sg">&#39;</span><span class="st">post_id</span><span class="st st-sg">&#39;</span>],
+ <span class="st st-sg">&#39;</span><span class="st">description</span><span class="st st-sg">&#39;</span>: <span class="name">parse_and_render</span>(<span class="name">req</span>, <span class="name">post</span>[<span class="st st-sg">&#39;</span><span class="st">text</span><span class="st st-sg">&#39;</span>]),
+ <span class="st st-sg">&#39;</span><span class="st">author</span><span class="st st-sg">&#39;</span>: <span class="name">post</span>[<span class="st st-sg">&#39;</span><span class="st">username</span><span class="st st-sg">&#39;</span>],
+ <span class="st st-sg">&#39;</span><span class="st">pub_date</span><span class="st st-sg">&#39;</span>: <span class="name">post</span>[<span class="st st-sg">&#39;</span><span class="st">timestamp</span><span class="st st-sg">&#39;</span>]
+ } <span class="kw">for</span> <span class="name">post</span> <span class="op op-word">in</span> <span class="name">result</span>]
+ }
+
+
+
+<span class="kw">class </span><span class="cls">FeedDisplay</span>(<span class="name">RequestHandler</span>):
+ <span class="name">handler_regexes</span> <span class="op">=</span> [
+ <span class="st st-sg">r&#39;</span><span class="st">^feeds/(?P&lt;feed&gt;[a-z0-9_-]+)\.xml$</span><span class="st st-sg">&#39;</span>,
+ <span class="st st-sg">r&#39;</span><span class="st">^feeds/(?P&lt;feed&gt;[a-z0-9_-]+)/(?P&lt;parameter&gt;.+)\.xml$</span><span class="st st-sg">&#39;</span>
+
+ ]
+
+ <span class="kw">def </span><span class="fun">handle_request</span>(<span class="bn bn-pseudo">self</span>, <span class="name">req</span>, <span class="name">feed</span>, <span class="name">parameter</span><span class="op">=</span><span class="bn bn-pseudo">None</span>):
+ <span class="kw">for</span> <span class="name">feed_provider</span> <span class="op op-word">in</span> <span class="bn bn-pseudo">self</span>.<span class="name">ctx</span>.<span class="name">get_components</span>(<span class="name">FeedProvider</span>):
+ <span class="kw">if</span> <span class="name">feed_provider</span>.<span class="name">identifier</span> <span class="op">==</span> <span class="name">feed</span>:
+ <span class="name">data</span> <span class="op">=</span> <span class="name">feed_provider</span>.<span class="name">get_feed</span>(<span class="name">req</span>, <span class="name">parameter</span>)
+ <span class="name">feed</span> <span class="op">=</span> <span class="name">Feed</span>(<span class="bn bn-pseudo">self</span>.<span class="name">ctx</span>, <span class="name">data</span>[<span class="st st-sg">&#39;</span><span class="st">title</span><span class="st st-sg">&#39;</span>],
+ <span class="name">data</span>[<span class="st st-sg">&#39;</span><span class="st">description</span><span class="st st-sg">&#39;</span>],
+ <span class="bn bn-pseudo">self</span>.<span class="name">ctx</span>.<span class="name">make_external_url</span>(<span class="st st-sg">&#39;&#39;</span>))
+ <span class="kw">try</span>:
+ <span class="kw">for</span> <span class="name">item</span> <span class="op op-word">in</span> <span class="name">data</span>[<span class="st st-sg">&#39;</span><span class="st">items</span><span class="st st-sg">&#39;</span>]:
+ <span class="name">feed</span>.<span class="name">add_item</span>(<span class="op">**</span><span class="name">item</span>)
+ <span class="kw">except</span> <span class="name">FeedNotFound</span>:
+ <span class="kw">return</span> <span class="name">PageNotFound</span>()
+ <span class="name">resp</span> <span class="op">=</span> <span class="name">Response</span>(<span class="name">feed</span>.<span class="name">generate</span>())
+ <span class="name">resp</span>[<span class="st st-sg">&#39;</span><span class="st">Content-Type</span><span class="st st-sg">&#39;</span>] <span class="op">=</span> <span class="st st-sg">&#39;</span><span class="st">text/xml</span><span class="st st-sg">&#39;</span>
+
+ <span class="kw">return</span> <span class="name">resp</span>
+ <span class="kw">return</span> <span class="name">PageNotFound</span>()
+<span class="cm"># -*- coding: utf-8 -*-</span>
+<span class="st st-db">&quot;&quot;&quot;</span><span class="st">
+ pocoo.pkg.core.forum
+ ~~~~~~~~~~~~~~~~~~~~
+
+ Forum Utilities.
+
+ :copyright: 2006 by Armin Ronacher, Benjamin Wiegand.
+ :license: GNU GPL, see LICENSE for more details.
+</span><span class="st st-db">&quot;&quot;&quot;</span>
+<span class="kw">from </span><span class="cls">datetime</span><span class="kw"> import</span> <span class="name">datetime</span>
+
+<span class="kw">from </span><span class="cls">math</span><span class="kw"> import</span> <span class="name">ceil</span>
+
+<span class="kw">from </span><span class="cls">pocoo</span><span class="kw"> import</span> <span class="name">Component</span>
+<span class="kw">from </span><span class="cls">pocoo.db</span><span class="kw"> import</span> <span class="name">meta</span>, <span class="name">DatabaseModel</span>, <span class="name">lazy_column</span>
+
+<span class="kw">from </span><span class="cls">pocoo.pkg.core.user</span><span class="kw"> import</span> <span class="name">User</span>
+<span class="kw">from </span><span class="cls">pocoo.pkg.core.db</span><span class="kw"> import</span> <span class="name">forums</span>, <span class="name">posts</span>, <span class="name">users</span>, <span class="name">ANONYMOUS_USER_ID</span>
+
+<span class="kw">from </span><span class="cls">pocoo.pkg.core.template</span><span class="kw"> import</span> <span class="name">Pagination</span>, <span class="name">LazyPagination</span>
+<span class="kw">from </span><span class="cls">pocoo.pkg.core.textfmt</span><span class="kw"> import</span> <span class="name">parse_and_render</span>, <span class="name">quote_text</span>
+
+<span class="kw">from </span><span class="cls">pocoo.utils.uri</span><span class="kw"> import</span> <span class="name">urlencode</span>
+<span class="kw">from </span><span class="cls">pocoo.utils.iterators</span><span class="kw"> import</span> <span class="name">inciter</span>
+
+<span class="cm"># for default arguments in Thread</span>
+
+<span class="name">_missing</span> <span class="op">=</span> <span class="bn">object</span>()
+
+
+<span class="kw">class </span><span class="cls">PostProcessor</span>(<span class="name">Component</span>):
+ <span class="st st-db">&quot;&quot;&quot;</span><span class="st">
+ Process a posting before it is stored in the database.
+ </span><span class="st st-db">&quot;&quot;&quot;</span>
+
+ <span class="kw">def </span><span class="fun">process_post</span>(<span class="bn bn-pseudo">self</span>, <span class="name">text</span>, <span class="name">title</span>, <span class="name">reason</span>):
+ <span class="st st-db">&quot;&quot;&quot;</span><span class="st">
+
+ Process a posting.
+
+ :param text: Text of the posting, possibly changed by
+ another PostProcessor.
+ :param title: The subject of the posting
+ :param reason: Can be ``&#39;new&#39;`` or ``&#39;edit&#39;``.
+
+ :returns:
+ * ``True``: store the posting as-is, or
+ * ``False``: refuse to store the posting, or
+ * a string: use as the new posting text, or
+ * a tuple: (text, title) for the posting
+ </span><span class="st st-db">&quot;&quot;&quot;</span>
+ <span class="kw">return</span> <span class="bn bn-pseudo">True</span>
+
+
+<span class="kw">def </span><span class="fun">apply_post_processors</span>(<span class="name">ctx</span>, <span class="name">text</span>, <span class="name">title</span>, <span class="name">reason</span>):
+ <span class="st st-db">&quot;&quot;&quot;</span><span class="st">
+
+ Apply all `PostProcessor` components to the posting.
+
+ Return (``text``, ``title``) tuple.
+ </span><span class="st st-db">&quot;&quot;&quot;</span>
+ <span class="kw">for</span> <span class="name">comp</span> <span class="op op-word">in</span> <span class="name">ctx</span>.<span class="name">get_components</span>(<span class="name">PostProcessor</span>):
+ <span class="name">rv</span> <span class="op">=</span> <span class="name">comp</span>.<span class="name">process_post</span>(<span class="name">text</span>, <span class="name">title</span>, <span class="st st-sg">&#39;</span><span class="st">new</span><span class="st st-sg">&#39;</span>)
+ <span class="kw">if</span> <span class="op op-word">not</span> <span class="name">rv</span>:
+ <span class="kw">raise</span> <span class="exc">ValueError</span>(<span class="st st-sg">&#39;</span><span class="st">creation of posting denied</span><span class="st st-sg">&#39;</span>)
+ <span class="kw">elif</span> <span class="bn">isinstance</span>(<span class="name">rv</span>, <span class="bn">basestring</span>):
+ <span class="name">text</span> <span class="op">=</span> <span class="bn">unicode</span>(<span class="name">rv</span>)
+ <span class="kw">elif</span> <span class="bn">isinstance</span>(<span class="name">rv</span>, <span class="bn">tuple</span>):
+ <span class="name">text</span> <span class="op">=</span> <span class="bn">unicode</span>(<span class="name">rv</span>[<span class="nb nb-int">0</span>])
+ <span class="name">title</span> <span class="op">=</span> <span class="bn">unicode</span>(<span class="name">rv</span>[<span class="nb nb-int">1</span>])
+ <span class="kw">return</span> <span class="name">text</span>, <span class="name">title</span>
+
+
+<span class="kw">def </span><span class="fun">get_forum_index</span>(<span class="name">req</span>):
+ <span class="st st-db">&quot;&quot;&quot;</span><span class="st">
+ Return a list of dicts with forum information so that
+ the template can use it.
+
+ If the request object has an identified user object attached
+ the returned dict will include status information. (read,
+ unread)
+ </span><span class="st st-db">&quot;&quot;&quot;</span>
+ <span class="name">ctx</span> <span class="op">=</span> <span class="name">req</span>.<span class="name">ctx</span>
+
+ <span class="name">f</span> <span class="op">=</span> <span class="name">forums</span>.<span class="name">c</span>
+ <span class="name">p</span> <span class="op">=</span> <span class="name">posts</span>.<span class="name">c</span>
+
+ <span class="name">u</span> <span class="op">=</span> <span class="name">users</span>.<span class="name">c</span>
+ <span class="name">columns</span> <span class="op">=</span> [<span class="name">f</span>.<span class="name">forum_id</span>, <span class="name">f</span>.<span class="name">description</span>, <span class="name">f</span>.<span class="name">name</span>, <span class="name">f</span>.<span class="name">link</span>, <span class="name">f</span>.<span class="name">post_count</span>,
+ <span class="name">f</span>.<span class="name">thread_count</span>]
+
+ <span class="name">categories</span> <span class="op">=</span> []
+ <span class="kw">def </span><span class="fun">do</span>(<span class="name">con</span>):
+ <span class="kw">for</span> <span class="name">category</span> <span class="op op-word">in</span> <span class="name">con</span>.<span class="name">execute</span>(<span class="name">meta</span>.<span class="name">select</span>(<span class="name">columns</span>,
+ <span class="name">f</span>.<span class="name">parent_id</span> <span class="op">==</span> <span class="bn bn-pseudo">None</span>)):
+ <span class="name">category</span> <span class="op">=</span> <span class="bn">dict</span>(<span class="name">category</span>)
+ <span class="name">category</span>[<span class="st st-sg">&#39;</span><span class="st">is_external_link</span><span class="st st-sg">&#39;</span>] <span class="op">=</span> <span class="bn">bool</span>(<span class="name">category</span>.<span class="name">pop</span>(<span class="st st-sg">&#39;</span><span class="st">link</span><span class="st st-sg">&#39;</span>))
+ <span class="name">category</span>[<span class="st st-sg">&#39;</span><span class="st">url</span><span class="st st-sg">&#39;</span>] <span class="op">=</span> <span class="name">ctx</span>.<span class="name">make_url</span>(<span class="st st-sg">&#39;</span><span class="st">forum</span><span class="st st-sg">&#39;</span>, <span class="name">category</span>[<span class="st st-sg">&#39;</span><span class="st">forum_id</span><span class="st st-sg">&#39;</span>])
+ <span class="name">forums</span> <span class="op">=</span> []
+ <span class="kw">for</span> <span class="name">forum</span> <span class="op op-word">in</span> <span class="name">con</span>.<span class="name">execute</span>(<span class="name">meta</span>.<span class="name">select</span>(<span class="name">columns</span> <span class="op">+</span> [<span class="name">f</span>.<span class="name">last_post_id</span>],
+ <span class="name">f</span>.<span class="name">parent_id</span> <span class="op">==</span> <span class="name">category</span>[<span class="st st-sg">&#39;</span><span class="st">forum_id</span><span class="st st-sg">&#39;</span>])):
+ <span class="name">forum</span> <span class="op">=</span> <span class="bn">dict</span>(<span class="name">forum</span>)
+ <span class="name">forum</span>[<span class="st st-sg">&#39;</span><span class="st">is_external_link</span><span class="st st-sg">&#39;</span>] <span class="op">=</span> <span class="bn">bool</span>(<span class="name">forum</span>.<span class="name">pop</span>(<span class="st st-sg">&#39;</span><span class="st">link</span><span class="st st-sg">&#39;</span>))
+ <span class="name">forum</span>[<span class="st st-sg">&#39;</span><span class="st">url</span><span class="st st-sg">&#39;</span>] <span class="op">=</span> <span class="name">ctx</span>.<span class="name">make_url</span>(<span class="st st-sg">&#39;</span><span class="st">forum</span><span class="st st-sg">&#39;</span>, <span class="name">forum</span>[<span class="st st-sg">&#39;</span><span class="st">forum_id</span><span class="st st-sg">&#39;</span>])
+ <span class="cm"># get last post</span>
+
+ <span class="name">last_post_id</span> <span class="op">=</span> <span class="name">forum</span>.<span class="name">pop</span>(<span class="st st-sg">&#39;</span><span class="st">last_post_id</span><span class="st st-sg">&#39;</span>)
+ <span class="kw">if</span> <span class="name">last_post_id</span> <span class="op op-word">is</span> <span class="op op-word">not</span> <span class="bn bn-pseudo">None</span>:
+ <span class="name">result</span> <span class="op">=</span> <span class="name">con</span>.<span class="name">execute</span>(<span class="name">meta</span>.<span class="name">select</span>([<span class="name">u</span>.<span class="name">user_id</span>, <span class="name">u</span>.<span class="name">username</span>,
+ <span class="name">p</span>.<span class="name">post_id</span>, <span class="name">p</span>.<span class="name">title</span>,
+ <span class="name">p</span>.<span class="name">timestamp</span>,
+ <span class="name">p</span>.<span class="name">username</span>.<span class="name">label</span>(<span class="st st-sg">&#39;</span><span class="st">guestname</span><span class="st st-sg">&#39;</span>)],
+ (<span class="name">p</span>.<span class="name">post_id</span> <span class="op">==</span> <span class="name">last_post_id</span>) <span class="op">&amp;</span>
+
+ (<span class="name">p</span>.<span class="name">post_id</span> <span class="op">!=</span> <span class="bn bn-pseudo">None</span>) <span class="op">&amp;</span>
+ (<span class="name">u</span>.<span class="name">user_id</span> <span class="op">==</span> <span class="name">p</span>.<span class="name">author_id</span>)
+ ))
+ <span class="name">last_post</span> <span class="op">=</span> <span class="bn">dict</span>(<span class="name">result</span>.<span class="name">fetchone</span>())
+ <span class="name">username</span> <span class="op">=</span> <span class="name">urlencode</span>(<span class="name">last_post</span>[<span class="st st-sg">&#39;</span><span class="st">username</span><span class="st st-sg">&#39;</span>])
+ <span class="name">last_post</span>[<span class="st st-sg">&#39;</span><span class="st">author</span><span class="st st-sg">&#39;</span>] <span class="op">=</span> {
+ <span class="st st-sg">&#39;</span><span class="st">user_id</span><span class="st st-sg">&#39;</span>: <span class="name">last_post</span>[<span class="st st-sg">&#39;</span><span class="st">user_id</span><span class="st st-sg">&#39;</span>],
+ <span class="st st-sg">&#39;</span><span class="st">registered</span><span class="st st-sg">&#39;</span>: <span class="name">last_post</span>.<span class="name">pop</span>(<span class="st st-sg">&#39;</span><span class="st">user_id</span><span class="st st-sg">&#39;</span>) <span class="op">&gt;</span> <span class="nb nb-int">0</span>,
+ <span class="st st-sg">&#39;</span><span class="st">username</span><span class="st st-sg">&#39;</span>: <span class="name">last_post</span>.<span class="name">pop</span>(<span class="st st-sg">&#39;</span><span class="st">guestname</span><span class="st st-sg">&#39;</span>) <span class="op op-word">or</span>\
+ <span class="name">last_post</span>.<span class="name">pop</span>(<span class="st st-sg">&#39;</span><span class="st">username</span><span class="st st-sg">&#39;</span>),
+ <span class="st st-sg">&#39;</span><span class="st">url</span><span class="st st-sg">&#39;</span>: <span class="name">ctx</span>.<span class="name">make_url</span>(<span class="st st-sg">&#39;</span><span class="st">users</span><span class="st st-sg">&#39;</span>, <span class="name">username</span>)
+ }
+ <span class="name">last_post</span>[<span class="st st-sg">&#39;</span><span class="st">url</span><span class="st st-sg">&#39;</span>] <span class="op">=</span> <span class="name">ctx</span>.<span class="name">make_url</span>(<span class="st st-sg">&#39;</span><span class="st">post</span><span class="st st-sg">&#39;</span>, <span class="name">last_post</span>[<span class="st st-sg">&#39;</span><span class="st">post_id</span><span class="st st-sg">&#39;</span>])
+ <span class="kw">else</span>:
+ <span class="name">last_post</span> <span class="op">=</span> <span class="bn bn-pseudo">None</span>
+
+ <span class="name">forum</span>[<span class="st st-sg">&#39;</span><span class="st">last_post</span><span class="st st-sg">&#39;</span>] <span class="op">=</span> <span class="name">last_post</span>
+ <span class="name">subforums</span> <span class="op">=</span> []
+ <span class="kw">for</span> <span class="name">sf</span> <span class="op op-word">in</span> <span class="name">con</span>.<span class="name">execute</span>(<span class="name">meta</span>.<span class="name">select</span>([<span class="name">f</span>.<span class="name">forum_id</span>, <span class="name">f</span>.<span class="name">name</span>, <span class="name">f</span>.<span class="name">link</span>],
+ <span class="name">f</span>.<span class="name">parent_id</span> <span class="op">==</span> <span class="name">forum</span>[<span class="st st-sg">&#39;</span><span class="st">forum_id</span><span class="st st-sg">&#39;</span>])):
+ <span class="name">sf</span> <span class="op">=</span> <span class="bn">dict</span>(<span class="name">sf</span>)
+ <span class="name">sf</span>[<span class="st st-sg">&#39;</span><span class="st">is_external_url</span><span class="st st-sg">&#39;</span>] <span class="op">=</span> <span class="bn">bool</span>(<span class="name">sf</span>[<span class="st st-sg">&#39;</span><span class="st">forum_id</span><span class="st st-sg">&#39;</span>])
+ <span class="name">sf</span>[<span class="st st-sg">&#39;</span><span class="st">url</span><span class="st st-sg">&#39;</span>] <span class="op">=</span> <span class="name">ctx</span>.<span class="name">make_url</span>(<span class="st st-sg">&#39;</span><span class="st">forum</span><span class="st st-sg">&#39;</span>, <span class="name">sf</span>.<span class="name">pop</span>(<span class="st st-sg">&#39;</span><span class="st">forum_id</span><span class="st st-sg">&#39;</span>))
+ <span class="name">subforums</span>.<span class="name">append</span>(<span class="name">sf</span>)
+ <span class="name">forum</span>[<span class="st st-sg">&#39;</span><span class="st">subforums</span><span class="st st-sg">&#39;</span>] <span class="op">=</span> <span class="name">subforums</span>
+
+ <span class="name">forums</span>.<span class="name">append</span>(<span class="name">forum</span>)
+ <span class="name">category</span>[<span class="st st-sg">&#39;</span><span class="st">forums</span><span class="st st-sg">&#39;</span>] <span class="op">=</span> <span class="name">forums</span>
+ <span class="name">categories</span>.<span class="name">append</span>(<span class="name">category</span>)
+ <span class="name">ctx</span>.<span class="name">engine</span>.<span class="name">transaction</span>(<span class="name">do</span>)
+ <span class="kw">return</span> <span class="name">categories</span>
+
+
+<span class="kw">def </span><span class="fun">get_forum</span>(<span class="name">req</span>, <span class="name">forum_id</span>, <span class="name">page</span><span class="op">=</span><span class="nb nb-int">1</span>):
+ <span class="st st-db">&quot;&quot;&quot;</span><span class="st">
+ Return a list of dicts so that the template can use it.
+
+ Return ``None`` if the forum does not exist.
+ </span><span class="st st-db">&quot;&quot;&quot;</span>
+
+ <span class="name">ctx</span> <span class="op">=</span> <span class="name">req</span>.<span class="name">ctx</span>
+ <span class="name">f</span> <span class="op">=</span> <span class="name">forums</span>.<span class="name">c</span>
+
+ <span class="name">p</span> <span class="op">=</span> <span class="name">posts</span>.<span class="name">c</span>
+ <span class="name">u</span> <span class="op">=</span> <span class="name">users</span>.<span class="name">c</span>
+
+ <span class="name">columns</span> <span class="op">=</span> [<span class="name">f</span>.<span class="name">forum_id</span>, <span class="name">f</span>.<span class="name">description</span>, <span class="name">f</span>.<span class="name">name</span>, <span class="name">f</span>.<span class="name">link</span>, <span class="name">f</span>.<span class="name">post_count</span>,
+ <span class="name">f</span>.<span class="name">thread_count</span>]
+ <span class="name">forum_columns</span> <span class="op">=</span> [<span class="name">f</span>.<span class="name">forum_id</span>, <span class="name">f</span>.<span class="name">name</span>, <span class="name">f</span>.<span class="name">description</span>, <span class="name">f</span>.<span class="name">link</span>,
+ <span class="name">f</span>.<span class="name">post_count</span>, <span class="name">f</span>.<span class="name">thread_count</span>, <span class="name">f</span>.<span class="name">last_post_id</span>]
+ <span class="name">sf_columns</span> <span class="op">=</span> [<span class="name">f</span>.<span class="name">forum_id</span>, <span class="name">f</span>.<span class="name">name</span>, <span class="name">f</span>.<span class="name">link</span>]
+ <span class="name">thread_columns</span> <span class="op">=</span> [<span class="name">p</span>.<span class="name">post_id</span>, <span class="name">p</span>.<span class="name">title</span>, <span class="name">p</span>.<span class="name">timestamp</span>, <span class="name">u</span>.<span class="name">user_id</span>, <span class="name">u</span>.<span class="name">username</span>,
+ <span class="name">p</span>.<span class="name">post_count</span>, <span class="name">p</span>.<span class="name">view_count</span>, <span class="name">p</span>.<span class="name">username</span>.<span class="name">label</span>(<span class="st st-sg">&#39;</span><span class="st">guestname</span><span class="st st-sg">&#39;</span>)]
+ <span class="kw">def </span><span class="fun">do</span>(<span class="name">con</span>):
+ <span class="name">category</span> <span class="op">=</span> <span class="name">con</span>.<span class="name">execute</span>(<span class="name">meta</span>.<span class="name">select</span>(<span class="name">columns</span>,
+ <span class="name">f</span>.<span class="name">forum_id</span> <span class="op">==</span> <span class="name">forum_id</span>
+
+ )).<span class="name">fetchone</span>()
+ <span class="kw">if</span> <span class="name">category</span> <span class="op op-word">is</span> <span class="bn bn-pseudo">None</span>:
+ <span class="kw">return</span>
+ <span class="name">category</span> <span class="op">=</span> <span class="bn">dict</span>(<span class="name">category</span>)
+ <span class="name">category</span>[<span class="st st-sg">&#39;</span><span class="st">url</span><span class="st st-sg">&#39;</span>] <span class="op">=</span> <span class="name">ctx</span>.<span class="name">make_url</span>(<span class="st st-sg">&#39;</span><span class="st">forum</span><span class="st st-sg">&#39;</span>, <span class="name">category</span>[<span class="st st-sg">&#39;</span><span class="st">forum_id</span><span class="st st-sg">&#39;</span>])
+ <span class="cm"># wo don&#39;t pop link here so that the ForumPage request handler</span>
+
+ <span class="cm"># can use the link key for redirecting. That means we don&#39;t</span>
+ <span class="cm"># need a second query. But template designers shouldn&#39;t</span>
+ <span class="cm"># ever access {{ forum.link }}</span>
+ <span class="name">category</span>[<span class="st st-sg">&#39;</span><span class="st">is_external_url</span><span class="st st-sg">&#39;</span>] <span class="op">=</span> <span class="bn">bool</span>(<span class="name">category</span>[<span class="st st-sg">&#39;</span><span class="st">link</span><span class="st st-sg">&#39;</span>])
+ <span class="name">forums</span> <span class="op">=</span> []
+ <span class="kw">for</span> <span class="name">forum</span> <span class="op op-word">in</span> <span class="name">con</span>.<span class="name">execute</span>(<span class="name">meta</span>.<span class="name">select</span>(<span class="name">forum_columns</span>,
+ <span class="name">f</span>.<span class="name">parent_id</span> <span class="op">==</span> <span class="name">category</span>[<span class="st st-sg">&#39;</span><span class="st">forum_id</span><span class="st st-sg">&#39;</span>])):
+ <span class="name">forum</span> <span class="op">=</span> <span class="bn">dict</span>(<span class="name">forum</span>)
+ <span class="name">forum</span>[<span class="st st-sg">&#39;</span><span class="st">url</span><span class="st st-sg">&#39;</span>] <span class="op">=</span> <span class="name">ctx</span>.<span class="name">make_url</span>(<span class="st st-sg">&#39;</span><span class="st">forum</span><span class="st st-sg">&#39;</span>, <span class="name">forum</span>[<span class="st st-sg">&#39;</span><span class="st">forum_id</span><span class="st st-sg">&#39;</span>])
+ <span class="cm"># wo don&#39;t pop that here so that the ForumPage request handler</span>
+
+ <span class="cm"># can use the link key for redirecting. That means we don&#39;t</span>
+ <span class="cm"># need a second query. But template designers shouldn&#39;t</span>
+ <span class="cm"># ever access {{ forum.link }}</span>
+ <span class="name">forum</span>[<span class="st st-sg">&#39;</span><span class="st">is_external_link</span><span class="st st-sg">&#39;</span>] <span class="op">=</span> <span class="bn">bool</span>(<span class="name">forum</span>.<span class="name">pop</span>(<span class="st st-sg">&#39;</span><span class="st">link</span><span class="st st-sg">&#39;</span>))
+ <span class="name">subforums</span> <span class="op">=</span> []
+ <span class="kw">for</span> <span class="name">sf</span> <span class="op op-word">in</span> <span class="name">con</span>.<span class="name">execute</span>(<span class="name">meta</span>.<span class="name">select</span>(<span class="name">sf_columns</span>,
+ <span class="name">f</span>.<span class="name">parent_id</span> <span class="op">==</span> <span class="name">forum</span>[<span class="st st-sg">&#39;</span><span class="st">forum_id</span><span class="st st-sg">&#39;</span>])):
+ <span class="name">sf</span> <span class="op">=</span> <span class="bn">dict</span>(<span class="name">sf</span>)
+ <span class="name">sf</span>[<span class="st st-sg">&#39;</span><span class="st">is_external_link</span><span class="st st-sg">&#39;</span>] <span class="op">=</span> <span class="bn">bool</span>(<span class="name">sf</span>.<span class="name">pop</span>(<span class="st st-sg">&#39;</span><span class="st">link</span><span class="st st-sg">&#39;</span>))
+ <span class="name">sf</span>[<span class="st st-sg">&#39;</span><span class="st">url</span><span class="st st-sg">&#39;</span>] <span class="op">=</span> <span class="name">ctx</span>.<span class="name">make_url</span>(<span class="st st-sg">&#39;</span><span class="st">forum</span><span class="st st-sg">&#39;</span>, <span class="name">sf</span>[<span class="st st-sg">&#39;</span><span class="st">forum_id</span><span class="st st-sg">&#39;</span>])
+ <span class="name">subforums</span>.<span class="name">append</span>(<span class="name">sf</span>)
+ <span class="name">forum</span>[<span class="st st-sg">&#39;</span><span class="st">subforums</span><span class="st st-sg">&#39;</span>] <span class="op">=</span> <span class="name">subforums</span>
+
+ <span class="cm"># get last post</span>
+ <span class="name">last_post_id</span> <span class="op">=</span> <span class="name">forum</span>.<span class="name">pop</span>(<span class="st st-sg">&#39;</span><span class="st">last_post_id</span><span class="st st-sg">&#39;</span>)
+ <span class="kw">if</span> <span class="name">last_post_id</span> <span class="op op-word">is</span> <span class="op op-word">not</span> <span class="bn bn-pseudo">None</span>:
+ <span class="name">result</span> <span class="op">=</span> <span class="name">con</span>.<span class="name">execute</span>(<span class="name">meta</span>.<span class="name">select</span>([<span class="name">u</span>.<span class="name">user_id</span>, <span class="name">u</span>.<span class="name">username</span>,
+ <span class="name">p</span>.<span class="name">post_id</span>, <span class="name">p</span>.<span class="name">title</span>,
+ <span class="name">p</span>.<span class="name">username</span>.<span class="name">label</span>(<span class="st st-sg">&#39;</span><span class="st">guestname</span><span class="st st-sg">&#39;</span>),
+ <span class="name">p</span>.<span class="name">timestamp</span>],
+ (<span class="name">p</span>.<span class="name">post_id</span> <span class="op">==</span> <span class="name">last_post_id</span>) <span class="op">&amp;</span>
+
+ (<span class="name">p</span>.<span class="name">post_id</span> <span class="op">!=</span> <span class="bn bn-pseudo">None</span>) <span class="op">&amp;</span>
+ (<span class="name">u</span>.<span class="name">user_id</span> <span class="op">==</span> <span class="name">p</span>.<span class="name">author_id</span>)
+ ))
+ <span class="name">last_post</span> <span class="op">=</span> <span class="name">result</span>.<span class="name">fetchone</span>()
+ <span class="name">last_post</span> <span class="op">=</span> <span class="bn">dict</span>(<span class="name">last_post</span>)
+ <span class="name">username</span> <span class="op">=</span> <span class="name">urlencode</span>(<span class="name">last_post</span>[<span class="st st-sg">&#39;</span><span class="st">username</span><span class="st st-sg">&#39;</span>])
+ <span class="name">last_post</span>[<span class="st st-sg">&#39;</span><span class="st">author</span><span class="st st-sg">&#39;</span>] <span class="op">=</span> {
+ <span class="st st-sg">&#39;</span><span class="st">registered</span><span class="st st-sg">&#39;</span>: <span class="name">last_post</span>[<span class="st st-sg">&#39;</span><span class="st">user_id</span><span class="st st-sg">&#39;</span>] <span class="op">&gt;</span> <span class="nb nb-int">0</span>,
+ <span class="st st-sg">&#39;</span><span class="st">user_id</span><span class="st st-sg">&#39;</span>: <span class="name">last_post</span>.<span class="name">pop</span>(<span class="st st-sg">&#39;</span><span class="st">user_id</span><span class="st st-sg">&#39;</span>),
+ <span class="st st-sg">&#39;</span><span class="st">username</span><span class="st st-sg">&#39;</span>: <span class="name">last_post</span>.<span class="name">pop</span>(<span class="st st-sg">&#39;</span><span class="st">guestname</span><span class="st st-sg">&#39;</span>) <span class="op op-word">or</span>\
+ <span class="name">last_post</span>.<span class="name">pop</span>(<span class="st st-sg">&#39;</span><span class="st">username</span><span class="st st-sg">&#39;</span>),
+ <span class="st st-sg">&#39;</span><span class="st">url</span><span class="st st-sg">&#39;</span>: <span class="name">ctx</span>.<span class="name">make_url</span>(<span class="st st-sg">&#39;</span><span class="st">users</span><span class="st st-sg">&#39;</span>, <span class="name">username</span>),
+ }
+ <span class="name">last_post</span>[<span class="st st-sg">&#39;</span><span class="st">url</span><span class="st st-sg">&#39;</span>] <span class="op">=</span> <span class="name">ctx</span>.<span class="name">make_url</span>(<span class="st st-sg">&#39;</span><span class="st">post</span><span class="st st-sg">&#39;</span>, <span class="name">last_post</span>[<span class="st st-sg">&#39;</span><span class="st">post_id</span><span class="st st-sg">&#39;</span>])
+ <span class="kw">else</span>:
+ <span class="name">last_post</span> <span class="op">=</span> <span class="bn bn-pseudo">None</span>
+
+ <span class="name">forum</span>[<span class="st st-sg">&#39;</span><span class="st">last_post</span><span class="st st-sg">&#39;</span>] <span class="op">=</span> <span class="name">last_post</span>
+ <span class="name">forums</span>.<span class="name">append</span>(<span class="name">forum</span>)
+ <span class="name">category</span>[<span class="st st-sg">&#39;</span><span class="st">forums</span><span class="st st-sg">&#39;</span>] <span class="op">=</span> <span class="name">forums</span>
+
+ <span class="cm"># pagination</span>
+ <span class="kw">def </span><span class="fun">get_page_link</span>(<span class="name">number</span>):
+ <span class="name">link</span> <span class="op">=</span> <span class="st st-sg">&#39;</span><span class="st">forum/</span><span class="st st-int">%d</span><span class="st st-sg">&#39;</span> <span class="op">%</span> <span class="name">forum_id</span>
+
+ <span class="kw">if</span> <span class="name">number</span> <span class="op">&gt;</span> <span class="nb nb-int">1</span>:
+ <span class="name">link</span> <span class="op">+=</span> <span class="st st-sg">&#39;</span><span class="st">?page=</span><span class="st st-int">%d</span><span class="st st-sg">&#39;</span> <span class="op">%</span> <span class="name">number</span>
+
+ <span class="kw">return</span> <span class="name">link</span>
+ <span class="name">threads_per_page</span> <span class="op">=</span> <span class="name">get_threads_per_page</span>(<span class="name">req</span>)
+ <span class="name">page_count</span> <span class="op">=</span> <span class="bn">int</span>(<span class="name">ceil</span>(<span class="name">category</span>[<span class="st st-sg">&#39;</span><span class="st">thread_count</span><span class="st st-sg">&#39;</span>] <span class="op">/</span> (<span class="name">threads_per_page</span> <span class="op">*</span> <span class="nb nb-flt">1.0</span>)))
+ <span class="name">pagination</span> <span class="op">=</span> <span class="name">LazyPagination</span>(<span class="name">req</span>, <span class="name">page</span>, <span class="name">threads_per_page</span>, <span class="name">page_count</span>,
+ <span class="name">get_page_link</span>)
+
+ <span class="name">threads</span> <span class="op">=</span> []
+ <span class="kw">for</span> <span class="name">thread</span> <span class="op op-word">in</span> <span class="name">con</span>.<span class="name">execute</span>(<span class="name">meta</span>.<span class="name">select</span>(<span class="name">thread_columns</span>,
+ (<span class="name">p</span>.<span class="name">forum_id</span> <span class="op">==</span> <span class="name">category</span>[<span class="st st-sg">&#39;</span><span class="st">forum_id</span><span class="st st-sg">&#39;</span>]) <span class="op">&amp;</span>
+
+ (<span class="name">p</span>.<span class="name">parent_id</span> <span class="op">==</span> <span class="bn bn-pseudo">None</span>) <span class="op">&amp;</span>
+ (<span class="name">u</span>.<span class="name">user_id</span> <span class="op">==</span> <span class="name">p</span>.<span class="name">author_id</span>),
+ <span class="name">order_by</span><span class="op">=</span>[<span class="name">meta</span>.<span class="name">desc</span>(<span class="name">p</span>.<span class="name">post_id</span>)],
+ <span class="name">limit</span><span class="op">=</span><span class="name">threads_per_page</span>,
+ <span class="name">offset</span><span class="op">=</span><span class="name">threads_per_page</span> <span class="op">*</span> (<span class="name">page</span> <span class="op">-</span> <span class="nb nb-int">1</span>)
+ )):
+ <span class="name">thread</span> <span class="op">=</span> <span class="bn">dict</span>(<span class="name">thread</span>)
+ <span class="name">thread</span>[<span class="st st-sg">&#39;</span><span class="st">url</span><span class="st st-sg">&#39;</span>] <span class="op">=</span> <span class="name">ctx</span>.<span class="name">make_url</span>(<span class="st st-sg">&#39;</span><span class="st">post</span><span class="st st-sg">&#39;</span>, <span class="name">thread</span>[<span class="st st-sg">&#39;</span><span class="st">post_id</span><span class="st st-sg">&#39;</span>])
+ <span class="name">thread</span>[<span class="st st-sg">&#39;</span><span class="st">author</span><span class="st st-sg">&#39;</span>] <span class="op">=</span> {
+ <span class="st st-sg">&#39;</span><span class="st">registered</span><span class="st st-sg">&#39;</span>: <span class="name">thread</span>[<span class="st st-sg">&#39;</span><span class="st">user_id</span><span class="st st-sg">&#39;</span>] <span class="op">&gt;</span> <span class="nb nb-int">0</span>,
+ <span class="st st-sg">&#39;</span><span class="st">user_id</span><span class="st st-sg">&#39;</span>: <span class="name">thread</span>.<span class="name">pop</span>(<span class="st st-sg">&#39;</span><span class="st">user_id</span><span class="st st-sg">&#39;</span>),
+ <span class="st st-sg">&#39;</span><span class="st">username</span><span class="st st-sg">&#39;</span>: <span class="name">thread</span>.<span class="name">pop</span>(<span class="st st-sg">&#39;</span><span class="st">guestname</span><span class="st st-sg">&#39;</span>) <span class="op op-word">or</span> <span class="name">thread</span>[<span class="st st-sg">&#39;</span><span class="st">username</span><span class="st st-sg">&#39;</span>],
+ <span class="st st-sg">&#39;</span><span class="st">url</span><span class="st st-sg">&#39;</span>: <span class="name">ctx</span>.<span class="name">make_url</span>(<span class="st st-sg">&#39;</span><span class="st">users</span><span class="st st-sg">&#39;</span>, <span class="name">urlencode</span>(<span class="name">thread</span>.<span class="name">pop</span>(<span class="st st-sg">&#39;</span><span class="st">username</span><span class="st st-sg">&#39;</span>)))
+ }
+ <span class="cm"># get last post</span>
+
+ <span class="name">result</span> <span class="op">=</span> <span class="name">con</span>.<span class="name">execute</span>(<span class="name">meta</span>.<span class="name">select</span>([<span class="name">u</span>.<span class="name">user_id</span>, <span class="name">u</span>.<span class="name">username</span>, <span class="name">p</span>.<span class="name">post_id</span>,
+ <span class="name">p</span>.<span class="name">title</span>, <span class="name">p</span>.<span class="name">timestamp</span>,
+ <span class="name">p</span>.<span class="name">username</span>.<span class="name">label</span>(<span class="st st-sg">&#39;</span><span class="st">guestname</span><span class="st st-sg">&#39;</span>)],
+ (<span class="name">p</span>.<span class="name">root_post_id</span> <span class="op">==</span> <span class="name">thread</span>[<span class="st st-sg">&#39;</span><span class="st">post_id</span><span class="st st-sg">&#39;</span>]) <span class="op">&amp;</span>
+
+ (<span class="name">u</span>.<span class="name">user_id</span> <span class="op">==</span> <span class="name">p</span>.<span class="name">author_id</span>),
+ <span class="name">order_by</span><span class="op">=</span>[<span class="name">meta</span>.<span class="name">desc</span>(<span class="name">p</span>.<span class="name">post_id</span>)],
+ <span class="name">limit</span><span class="op">=</span><span class="nb nb-int">1</span>
+
+ ))
+ <span class="name">last_post</span> <span class="op">=</span> <span class="name">result</span>.<span class="name">fetchone</span>()
+ <span class="kw">if</span> <span class="name">last_post</span> <span class="op op-word">is</span> <span class="op op-word">not</span> <span class="bn bn-pseudo">None</span>:
+ <span class="name">last_post</span> <span class="op">=</span> <span class="bn">dict</span>(<span class="name">last_post</span>)
+ <span class="name">username</span> <span class="op">=</span> <span class="name">last_post</span>.<span class="name">pop</span>(<span class="st st-sg">&#39;</span><span class="st">username</span><span class="st st-sg">&#39;</span>)
+ <span class="name">last_post</span>[<span class="st st-sg">&#39;</span><span class="st">author</span><span class="st st-sg">&#39;</span>] <span class="op">=</span> {
+ <span class="st st-sg">&#39;</span><span class="st">registered</span><span class="st st-sg">&#39;</span>: <span class="name">last_post</span>[<span class="st st-sg">&#39;</span><span class="st">user_id</span><span class="st st-sg">&#39;</span>] <span class="op">&gt;</span> <span class="nb nb-int">0</span>,
+ <span class="st st-sg">&#39;</span><span class="st">user_id</span><span class="st st-sg">&#39;</span>: <span class="name">last_post</span>.<span class="name">pop</span>(<span class="st st-sg">&#39;</span><span class="st">user_id</span><span class="st st-sg">&#39;</span>),
+ <span class="st st-sg">&#39;</span><span class="st">username</span><span class="st st-sg">&#39;</span>: <span class="name">last_post</span>.<span class="name">pop</span>(<span class="st st-sg">&#39;</span><span class="st">guestname</span><span class="st st-sg">&#39;</span>) <span class="op op-word">or</span> <span class="name">username</span>,
+ <span class="st st-sg">&#39;</span><span class="st">url</span><span class="st st-sg">&#39;</span>: <span class="name">ctx</span>.<span class="name">make_url</span>(<span class="st st-sg">&#39;</span><span class="st">users</span><span class="st st-sg">&#39;</span>, <span class="name">urlencode</span>(<span class="name">username</span>)),
+ }
+ <span class="name">last_post</span>[<span class="st st-sg">&#39;</span><span class="st">url</span><span class="st st-sg">&#39;</span>] <span class="op">=</span> <span class="name">ctx</span>.<span class="name">make_url</span>(<span class="st st-sg">&#39;</span><span class="st">post</span><span class="st st-sg">&#39;</span>, <span class="name">last_post</span>[<span class="st st-sg">&#39;</span><span class="st">post_id</span><span class="st st-sg">&#39;</span>])
+ <span class="name">thread</span>[<span class="st st-sg">&#39;</span><span class="st">last_post</span><span class="st st-sg">&#39;</span>] <span class="op">=</span> <span class="name">last_post</span>
+
+ <span class="name">threads</span>.<span class="name">append</span>(<span class="name">thread</span>)
+ <span class="name">category</span>[<span class="st st-sg">&#39;</span><span class="st">threads</span><span class="st st-sg">&#39;</span>] <span class="op">=</span> <span class="name">threads</span>
+ <span class="name">category</span>[<span class="st st-sg">&#39;</span><span class="st">pagination</span><span class="st st-sg">&#39;</span>] <span class="op">=</span> <span class="name">pagination</span>
+
+ <span class="kw">return</span> <span class="name">category</span>
+ <span class="kw">return</span> <span class="name">ctx</span>.<span class="name">engine</span>.<span class="name">transaction</span>(<span class="name">do</span>)
+
+
+<span class="kw">def </span><span class="fun">get_forum_pathbar</span>(<span class="name">ctx</span>, <span class="name">forum_id</span>):
+ <span class="st st-db">&quot;&quot;&quot;</span><span class="st">Return the pathbar for a given forum.</span><span class="st st-db">&quot;&quot;&quot;</span>
+
+ <span class="name">f</span> <span class="op">=</span> <span class="name">forums</span>.<span class="name">c</span>
+ <span class="name">pathbar</span> <span class="op">=</span> []
+ <span class="kw">def </span><span class="fun">do</span>(<span class="name">con</span>, <span class="name">fid</span><span class="op">=</span><span class="bn bn-pseudo">None</span>):
+ <span class="kw">if</span> <span class="name">fid</span> <span class="op op-word">is</span> <span class="bn bn-pseudo">None</span>:
+ <span class="name">fid</span> <span class="op">=</span> <span class="name">forum_id</span>
+
+ <span class="name">row</span> <span class="op">=</span> <span class="name">con</span>.<span class="name">execute</span>(<span class="name">meta</span>.<span class="name">select</span>([<span class="name">f</span>.<span class="name">parent_id</span>, <span class="name">f</span>.<span class="name">name</span>],
+ <span class="name">forums</span>.<span class="name">c</span>.<span class="name">forum_id</span> <span class="op">==</span> <span class="name">fid</span>
+
+ )).<span class="name">fetchone</span>()
+ <span class="kw">if</span> <span class="name">row</span> <span class="op op-word">is</span> <span class="op op-word">not</span> <span class="bn bn-pseudo">None</span>:
+ <span class="name">l</span> <span class="op">=</span> <span class="st st-sg">&#39;</span><span class="st">forum/</span><span class="st st-int">%d</span><span class="st st-sg">&#39;</span> <span class="op">%</span> <span class="name">fid</span>
+
+ <span class="name">pathbar</span>.<span class="name">append</span>({
+ <span class="st st-sg">&#39;</span><span class="st">url</span><span class="st st-sg">&#39;</span>: <span class="name">ctx</span>.<span class="name">make_url</span>(<span class="name">l</span>),
+ <span class="st st-sg">&#39;</span><span class="st">forum_id</span><span class="st st-sg">&#39;</span>: <span class="name">fid</span>,
+ <span class="st st-sg">&#39;</span><span class="st">name</span><span class="st st-sg">&#39;</span>: <span class="name">row</span>[<span class="st st-sg">&#39;</span><span class="st">name</span><span class="st st-sg">&#39;</span>]
+ })
+ <span class="kw">if</span> <span class="name">row</span>[<span class="st st-sg">&#39;</span><span class="st">parent_id</span><span class="st st-sg">&#39;</span>] <span class="op op-word">is</span> <span class="op op-word">not</span> <span class="bn bn-pseudo">None</span>:
+ <span class="name">do</span>(<span class="name">con</span>, <span class="name">row</span>[<span class="st st-sg">&#39;</span><span class="st">parent_id</span><span class="st st-sg">&#39;</span>])
+ <span class="name">ctx</span>.<span class="name">engine</span>.<span class="name">transaction</span>(<span class="name">do</span>)
+ <span class="name">pathbar</span>.<span class="name">reverse</span>()
+ <span class="kw">return</span> <span class="name">pathbar</span>
+
+
+<span class="kw">def </span><span class="fun">get_post_pathbar</span>(<span class="name">ctx</span>, <span class="name">post_id</span>):
+ <span class="st st-db">&quot;&quot;&quot;</span><span class="st">Returns the pathbar for a given post including all forums and subforums</span><span class="st st-db">&quot;&quot;&quot;</span>
+ <span class="name">thread</span> <span class="op">=</span> <span class="name">Thread</span>.<span class="name">by_child</span>(<span class="name">ctx</span>, <span class="name">post_id</span>)
+ <span class="name">pathbar</span> <span class="op">=</span> <span class="name">get_forum_pathbar</span>(<span class="name">ctx</span>, <span class="name">thread</span>.<span class="name">forum_id</span>)
+ <span class="name">post_list</span> <span class="op">=</span> [ <span class="name">thread</span>.<span class="name">root_post_id</span> ]
+ <span class="name">p</span> <span class="op">=</span> <span class="name">posts</span>.<span class="name">c</span>
+
+ <span class="kw">if</span> <span class="name">thread</span>.<span class="name">root_post_id</span> <span class="op">!=</span> <span class="bn">int</span>(<span class="name">post_id</span>):
+ <span class="name">post_list</span>.<span class="name">append</span>(<span class="name">post_id</span>)
+
+ <span class="kw">def </span><span class="fun">do</span>(<span class="name">con</span>):
+ <span class="kw">for</span> <span class="bn">id</span> <span class="op op-word">in</span> <span class="name">post_list</span>:
+ <span class="name">row</span> <span class="op">=</span> <span class="name">con</span>.<span class="name">execute</span>(<span class="name">meta</span>.<span class="name">select</span>([<span class="name">p</span>.<span class="name">title</span>],
+ <span class="name">p</span>.<span class="name">post_id</span> <span class="op">==</span> <span class="bn">id</span>
+
+ )).<span class="name">fetchone</span>()
+ <span class="name">pathbar</span>.<span class="name">append</span>({
+ <span class="st st-sg">&#39;</span><span class="st">url</span><span class="st st-sg">&#39;</span>: <span class="name">ctx</span>.<span class="name">make_url</span>(<span class="st st-sg">&#39;</span><span class="st">post/</span><span class="st st-int">%s</span><span class="st st-sg">&#39;</span> <span class="op">%</span> <span class="bn">id</span>),
+ <span class="st st-sg">&#39;</span><span class="st">name</span><span class="st st-sg">&#39;</span>: <span class="name">row</span>[<span class="st st-db">&quot;</span><span class="st">title</span><span class="st st-db">&quot;</span>]
+ })
+ <span class="name">ctx</span>.<span class="name">engine</span>.<span class="name">transaction</span>(<span class="name">do</span>)
+ <span class="kw">return</span> <span class="name">pathbar</span>
+
+
+<span class="kw">def </span><span class="fun">get_post_tree</span>(<span class="name">req</span>, <span class="name">post_id</span>, <span class="name">inc_view_count</span><span class="op">=</span><span class="bn bn-pseudo">True</span>):
+ <span class="st st-db">&quot;&quot;&quot;</span><span class="st">
+ Return a dict with the thread information and a tree of posts.
+
+ Per default it will increment the view counter of the
+ thread requested. If you don&#39;t want that set ``inc_view_count``
+ to ``False``.
+ </span><span class="st st-db">&quot;&quot;&quot;</span>
+
+ <span class="name">ctx</span> <span class="op">=</span> <span class="name">req</span>.<span class="name">ctx</span>
+ <span class="name">p</span> <span class="op">=</span> <span class="name">posts</span>.<span class="name">c</span>
+
+ <span class="name">u</span> <span class="op">=</span> <span class="name">users</span>.<span class="name">c</span>
+ <span class="name">f</span> <span class="op">=</span> <span class="name">forums</span>.<span class="name">c</span>
+
+ <span class="cm"># load the post requested and lookup root_post_id</span>
+ <span class="name">result</span> <span class="op">=</span> <span class="name">ctx</span>.<span class="name">engine</span>.<span class="name">execute</span>(<span class="name">meta</span>.<span class="name">select</span>([<span class="name">p</span>.<span class="name">root_post_id</span>, <span class="name">p</span>.<span class="name">post_id</span>, <span class="name">p</span>.<span class="name">title</span>,
+ <span class="name">p</span>.<span class="name">text</span>, <span class="name">p</span>.<span class="name">timestamp</span>, <span class="name">u</span>.<span class="name">user_id</span>,
+ <span class="name">u</span>.<span class="name">register_date</span>,
+ <span class="name">u</span>.<span class="name">username</span>, <span class="name">u</span>.<span class="name">profile</span>, <span class="name">u</span>.<span class="name">post_count</span>,
+ <span class="name">p</span>.<span class="name">username</span>.<span class="name">label</span>(<span class="st st-sg">&#39;</span><span class="st">guestname</span><span class="st st-sg">&#39;</span>)],
+ (<span class="name">p</span>.<span class="name">post_id</span> <span class="op">==</span> <span class="name">post_id</span>) <span class="op">&amp;</span>
+
+ (<span class="name">u</span>.<span class="name">user_id</span> <span class="op">==</span> <span class="name">p</span>.<span class="name">author_id</span>)
+ ))
+ <span class="name">row</span> <span class="op">=</span> <span class="name">result</span>.<span class="name">fetchone</span>()
+ <span class="kw">if</span> <span class="name">row</span> <span class="op op-word">is</span> <span class="bn bn-pseudo">None</span>:
+ <span class="cm"># XXX: need error return here</span>
+
+ <span class="kw">return</span>
+ <span class="name">post</span> <span class="op">=</span> <span class="bn">dict</span>(<span class="name">row</span>)
+ <span class="name">post</span>[<span class="st st-sg">&#39;</span><span class="st">url</span><span class="st st-sg">&#39;</span>] <span class="op">=</span> <span class="name">ctx</span>.<span class="name">make_url</span>(<span class="st st-sg">&#39;</span><span class="st">post</span><span class="st st-sg">&#39;</span>, <span class="name">row</span>[<span class="st st-sg">&#39;</span><span class="st">post_id</span><span class="st st-sg">&#39;</span>])
+ <span class="name">post</span>[<span class="st st-sg">&#39;</span><span class="st">author</span><span class="st st-sg">&#39;</span>] <span class="op">=</span> {
+ <span class="st st-sg">&#39;</span><span class="st">user_id</span><span class="st st-sg">&#39;</span>: <span class="name">post</span>[<span class="st st-sg">&#39;</span><span class="st">user_id</span><span class="st st-sg">&#39;</span>],
+ <span class="st st-sg">&#39;</span><span class="st">username</span><span class="st st-sg">&#39;</span>: <span class="name">post</span>.<span class="name">pop</span>(<span class="st st-sg">&#39;</span><span class="st">guestname</span><span class="st st-sg">&#39;</span>) <span class="op op-word">or</span> <span class="name">post</span>[<span class="st st-sg">&#39;</span><span class="st">username</span><span class="st st-sg">&#39;</span>],
+ <span class="st st-sg">&#39;</span><span class="st">self</span><span class="st st-sg">&#39;</span>: <span class="name">req</span>.<span class="name">user</span>.<span class="name">user_id</span> <span class="op">==</span> <span class="name">post</span>[<span class="st st-sg">&#39;</span><span class="st">user_id</span><span class="st st-sg">&#39;</span>],
+ <span class="st st-sg">&#39;</span><span class="st">registered</span><span class="st st-sg">&#39;</span>: <span class="name">post</span>.<span class="name">pop</span>(<span class="st st-sg">&#39;</span><span class="st">user_id</span><span class="st st-sg">&#39;</span>) <span class="op">&gt;</span> <span class="nb nb-int">0</span>,
+ <span class="st st-sg">&#39;</span><span class="st">register_date</span><span class="st st-sg">&#39;</span>: <span class="name">post</span>.<span class="name">pop</span>(<span class="st st-sg">&#39;</span><span class="st">register_date</span><span class="st st-sg">&#39;</span>),
+ <span class="st st-sg">&#39;</span><span class="st">url</span><span class="st st-sg">&#39;</span>: <span class="name">ctx</span>.<span class="name">make_url</span>(<span class="st st-sg">&#39;</span><span class="st">users</span><span class="st st-sg">&#39;</span>, <span class="name">urlencode</span>(<span class="name">post</span>.<span class="name">pop</span>(<span class="st st-sg">&#39;</span><span class="st">username</span><span class="st st-sg">&#39;</span>))),
+ <span class="st st-sg">&#39;</span><span class="st">profile</span><span class="st st-sg">&#39;</span>: <span class="name">post</span>.<span class="name">pop</span>(<span class="st st-sg">&#39;</span><span class="st">profile</span><span class="st st-sg">&#39;</span>),
+ <span class="st st-sg">&#39;</span><span class="st">post_count</span><span class="st st-sg">&#39;</span>: <span class="name">post</span>.<span class="name">pop</span>(<span class="st st-sg">&#39;</span><span class="st">post_count</span><span class="st st-sg">&#39;</span>),
+ }
+ <span class="name">signature</span> <span class="op">=</span> <span class="bn bn-pseudo">None</span>
+
+ <span class="kw">if</span> <span class="name">post</span>[<span class="st st-sg">&#39;</span><span class="st">author</span><span class="st st-sg">&#39;</span>][<span class="st st-sg">&#39;</span><span class="st">profile</span><span class="st st-sg">&#39;</span>].<span class="name">get</span>(<span class="st st-sg">&#39;</span><span class="st">signature</span><span class="st st-sg">&#39;</span>):
+ <span class="name">signature</span> <span class="op">=</span> <span class="name">parse_and_render</span>(<span class="name">req</span>, <span class="name">post</span>[<span class="st st-sg">&#39;</span><span class="st">author</span><span class="st st-sg">&#39;</span>][<span class="st st-sg">&#39;</span><span class="st">profile</span><span class="st st-sg">&#39;</span>][<span class="st st-sg">&#39;</span><span class="st">signature</span><span class="st st-sg">&#39;</span>],
+ <span class="name">signature</span><span class="op">=</span><span class="bn bn-pseudo">True</span>)
+ <span class="name">post</span>[<span class="st st-sg">&#39;</span><span class="st">author</span><span class="st st-sg">&#39;</span>][<span class="st st-sg">&#39;</span><span class="st">signature</span><span class="st st-sg">&#39;</span>] <span class="op">=</span> <span class="name">signature</span>
+
+ <span class="cm">#XXX: cache here</span>
+ <span class="name">post</span>[<span class="st st-sg">&#39;</span><span class="st">parsed_text</span><span class="st st-sg">&#39;</span>] <span class="op">=</span> <span class="name">parse_and_render</span>(<span class="name">req</span>, <span class="name">post</span>[<span class="st st-sg">&#39;</span><span class="st">text</span><span class="st st-sg">&#39;</span>])
+ <span class="name">root_post_id</span> <span class="op">=</span> <span class="name">post</span>.<span class="name">pop</span>(<span class="st st-sg">&#39;</span><span class="st">root_post_id</span><span class="st st-sg">&#39;</span>)
+
+ <span class="name">result</span> <span class="op">=</span> <span class="name">ctx</span>.<span class="name">engine</span>.<span class="name">execute</span>(<span class="name">meta</span>.<span class="name">select</span>([<span class="name">p</span>.<span class="name">post_id</span>, <span class="name">p</span>.<span class="name">root_post_id</span>, <span class="name">p</span>.<span class="name">title</span>,
+ <span class="name">p</span>.<span class="name">parent_id</span>, <span class="name">p</span>.<span class="name">timestamp</span>, <span class="name">u</span>.<span class="name">username</span>,
+ <span class="name">u</span>.<span class="name">user_id</span>, <span class="name">p</span>.<span class="name">username</span>.<span class="name">label</span>(<span class="st st-sg">&#39;</span><span class="st">guestname</span><span class="st st-sg">&#39;</span>)],
+ (<span class="name">p</span>.<span class="name">root_post_id</span> <span class="op">==</span> <span class="name">root_post_id</span>) <span class="op">&amp;</span>
+
+ (<span class="name">u</span>.<span class="name">user_id</span> <span class="op">==</span> <span class="name">p</span>.<span class="name">author_id</span>)
+ ))
+
+ <span class="kw">def </span><span class="fun">prepare</span>(<span class="name">row</span>):
+ <span class="name">d</span> <span class="op">=</span> <span class="bn">dict</span>(<span class="name">row</span>)
+ <span class="name">d</span>[<span class="st st-sg">&#39;</span><span class="st">author</span><span class="st st-sg">&#39;</span>] <span class="op">=</span> {
+ <span class="st st-sg">&#39;</span><span class="st">user_id</span><span class="st st-sg">&#39;</span>: <span class="name">d</span>[<span class="st st-sg">&#39;</span><span class="st">user_id</span><span class="st st-sg">&#39;</span>],
+ <span class="st st-sg">&#39;</span><span class="st">username</span><span class="st st-sg">&#39;</span>: <span class="name">d</span>.<span class="name">pop</span>(<span class="st st-sg">&#39;</span><span class="st">guestname</span><span class="st st-sg">&#39;</span>) <span class="op op-word">or</span> <span class="name">d</span>[<span class="st st-sg">&#39;</span><span class="st">username</span><span class="st st-sg">&#39;</span>],
+ <span class="st st-sg">&#39;</span><span class="st">self</span><span class="st st-sg">&#39;</span>: <span class="name">req</span>.<span class="name">user</span>.<span class="name">user_id</span> <span class="op">==</span> <span class="name">d</span>[<span class="st st-sg">&#39;</span><span class="st">user_id</span><span class="st st-sg">&#39;</span>],
+ <span class="st st-sg">&#39;</span><span class="st">registered</span><span class="st st-sg">&#39;</span>: <span class="name">d</span>.<span class="name">pop</span>(<span class="st st-sg">&#39;</span><span class="st">user_id</span><span class="st st-sg">&#39;</span>) <span class="op">&gt;</span> <span class="nb nb-int">0</span>,
+ <span class="st st-sg">&#39;</span><span class="st">url</span><span class="st st-sg">&#39;</span>: <span class="name">ctx</span>.<span class="name">make_url</span>(<span class="st st-sg">&#39;</span><span class="st">users</span><span class="st st-sg">&#39;</span>, <span class="name">urlencode</span>(<span class="name">d</span>.<span class="name">pop</span>(<span class="st st-sg">&#39;</span><span class="st">username</span><span class="st st-sg">&#39;</span>))),
+ }
+ <span class="name">d</span>[<span class="st st-sg">&#39;</span><span class="st">active</span><span class="st st-sg">&#39;</span>] <span class="op">=</span> <span class="name">d</span>[<span class="st st-sg">&#39;</span><span class="st">post_id</span><span class="st st-sg">&#39;</span>] <span class="op">==</span> <span class="name">post_id</span>
+
+ <span class="name">d</span>[<span class="st st-sg">&#39;</span><span class="st">url</span><span class="st st-sg">&#39;</span>] <span class="op">=</span> <span class="name">ctx</span>.<span class="name">make_url</span>(<span class="st st-sg">&#39;</span><span class="st">post</span><span class="st st-sg">&#39;</span>, <span class="name">row</span>[<span class="st st-sg">&#39;</span><span class="st">post_id</span><span class="st st-sg">&#39;</span>])
+ <span class="kw">return</span> <span class="name">d</span>
+
+ <span class="cm"># map threads by their parents and prepare the context</span>
+ <span class="name">mapping</span> <span class="op">=</span> {}
+ <span class="name">flat_posts</span> <span class="op">=</span> []
+ <span class="kw">for</span> <span class="name">row</span> <span class="op op-word">in</span> <span class="name">result</span>:
+ <span class="name">tmp</span> <span class="op">=</span> <span class="name">prepare</span>(<span class="name">row</span>)
+ <span class="name">mapping</span>.<span class="name">setdefault</span>(<span class="name">row</span>[<span class="st st-sg">&#39;</span><span class="st">parent_id</span><span class="st st-sg">&#39;</span>], []).<span class="name">append</span>(<span class="name">tmp</span>)
+ <span class="name">flat_posts</span>.<span class="name">append</span>(<span class="name">tmp</span>)
+ <span class="name">root</span> <span class="op">=</span> <span class="name">mapping</span>.<span class="name">pop</span>(<span class="bn bn-pseudo">None</span>, <span class="bn bn-pseudo">None</span>)
+ <span class="kw">if</span> <span class="name">root</span> <span class="op op-word">is</span> <span class="bn bn-pseudo">None</span>:
+ <span class="kw">return</span>
+
+ <span class="kw">assert</span> <span class="bn">len</span>(<span class="name">root</span>) <span class="op">==</span> <span class="nb nb-int">1</span>, <span class="st st-sg">&#39;</span><span class="st">something went seriously wrong</span><span class="st st-sg">&#39;</span>
+
+ <span class="cm"># reassamble thread</span>
+
+ <span class="kw">def </span><span class="fun">reassamble</span>(<span class="name">nodes</span>):
+ <span class="kw">for</span> <span class="name">node</span> <span class="op op-word">in</span> <span class="name">nodes</span>:
+ <span class="name">node</span>[<span class="st st-sg">&#39;</span><span class="st">children</span><span class="st st-sg">&#39;</span>] <span class="op">=</span> <span class="name">n</span> <span class="op">=</span> <span class="name">mapping</span>.<span class="name">pop</span>(<span class="name">node</span>[<span class="st st-sg">&#39;</span><span class="st">post_id</span><span class="st st-sg">&#39;</span>], [])
+ <span class="name">reassamble</span>(<span class="name">n</span>)
+ <span class="name">reassamble</span>(<span class="name">root</span>)
+
+ <span class="cm"># increment view_count</span>
+
+ <span class="kw">if</span> <span class="name">inc_view_count</span>:
+ <span class="name">result</span> <span class="op">=</span> <span class="name">ctx</span>.<span class="name">engine</span>.<span class="name">execute</span>(<span class="name">meta</span>.<span class="name">select</span>([<span class="name">p</span>.<span class="name">view_count</span>],
+ <span class="name">p</span>.<span class="name">post_id</span> <span class="op">==</span> <span class="name">root_post_id</span>))
+ <span class="name">ctx</span>.<span class="name">engine</span>.<span class="name">execute</span>(<span class="name">posts</span>.<span class="name">update</span>(<span class="name">p</span>.<span class="name">post_id</span> <span class="op">==</span> <span class="name">root_post_id</span>),
+ <span class="name">view_count</span> <span class="op">=</span> <span class="name">result</span>.<span class="name">fetchone</span>()[<span class="nb nb-int">0</span>] <span class="op">+</span> <span class="nb nb-int">1</span>
+
+ )
+
+
+ <span class="cm"># fetch overall information for whole thread</span>
+ <span class="name">result</span> <span class="op">=</span> <span class="name">ctx</span>.<span class="name">engine</span>.<span class="name">execute</span>(<span class="name">meta</span>.<span class="name">select</span>([<span class="name">p</span>.<span class="name">post_id</span>, <span class="name">p</span>.<span class="name">title</span>, <span class="name">p</span>.<span class="name">forum_id</span>,
+ <span class="name">p</span>.<span class="name">timestamp</span>, <span class="name">u</span>.<span class="name">user_id</span>, <span class="name">u</span>.<span class="name">username</span>,
+ <span class="name">f</span>.<span class="name">name</span>, <span class="name">p</span>.<span class="name">username</span>.<span class="name">label</span>(<span class="st st-sg">&#39;</span><span class="st">guestname</span><span class="st st-sg">&#39;</span>)],
+ (<span class="name">p</span>.<span class="name">post_id</span> <span class="op">==</span> <span class="name">root_post_id</span>) <span class="op">&amp;</span>
+
+ (<span class="name">u</span>.<span class="name">user_id</span> <span class="op">==</span> <span class="name">p</span>.<span class="name">author_id</span>) <span class="op">&amp;</span>
+ (<span class="name">f</span>.<span class="name">forum_id</span> <span class="op">==</span> <span class="name">p</span>.<span class="name">forum_id</span>)
+ ))
+ <span class="name">row</span> <span class="op">=</span> <span class="name">result</span>.<span class="name">fetchone</span>()
+ <span class="kw">return</span> {
+ <span class="st st-sg">&#39;</span><span class="st">post_id</span><span class="st st-sg">&#39;</span>: <span class="name">row</span>[<span class="st st-sg">&#39;</span><span class="st">post_id</span><span class="st st-sg">&#39;</span>],
+ <span class="st st-sg">&#39;</span><span class="st">url</span><span class="st st-sg">&#39;</span>: <span class="name">ctx</span>.<span class="name">make_url</span>(<span class="st st-sg">&#39;</span><span class="st">post</span><span class="st st-sg">&#39;</span>, <span class="name">row</span>[<span class="st st-sg">&#39;</span><span class="st">post_id</span><span class="st st-sg">&#39;</span>]),
+ <span class="st st-sg">&#39;</span><span class="st">title</span><span class="st st-sg">&#39;</span>: <span class="name">row</span>[<span class="st st-sg">&#39;</span><span class="st">title</span><span class="st st-sg">&#39;</span>],
+ <span class="st st-sg">&#39;</span><span class="st">timestamp</span><span class="st st-sg">&#39;</span>: <span class="name">row</span>[<span class="st st-sg">&#39;</span><span class="st">timestamp</span><span class="st st-sg">&#39;</span>],
+ <span class="st st-sg">&#39;</span><span class="st">forum</span><span class="st st-sg">&#39;</span>: {
+ <span class="st st-sg">&#39;</span><span class="st">forum_id</span><span class="st st-sg">&#39;</span>: <span class="name">row</span>[<span class="st st-sg">&#39;</span><span class="st">forum_id</span><span class="st st-sg">&#39;</span>],
+ <span class="st st-sg">&#39;</span><span class="st">name</span><span class="st st-sg">&#39;</span>: <span class="name">row</span>[<span class="st st-sg">&#39;</span><span class="st">name</span><span class="st st-sg">&#39;</span>],
+ <span class="st st-sg">&#39;</span><span class="st">url</span><span class="st st-sg">&#39;</span>: <span class="name">ctx</span>.<span class="name">make_url</span>(<span class="st st-sg">&#39;</span><span class="st">forum</span><span class="st st-sg">&#39;</span>, <span class="name">row</span>[<span class="st st-sg">&#39;</span><span class="st">forum_id</span><span class="st st-sg">&#39;</span>]),
+ },
+ <span class="st st-sg">&#39;</span><span class="st">author</span><span class="st st-sg">&#39;</span>: {
+ <span class="st st-sg">&#39;</span><span class="st">user_id</span><span class="st st-sg">&#39;</span>: <span class="name">row</span>[<span class="st st-sg">&#39;</span><span class="st">user_id</span><span class="st st-sg">&#39;</span>],
+ <span class="st st-sg">&#39;</span><span class="st">registered</span><span class="st st-sg">&#39;</span>: <span class="name">row</span>[<span class="st st-sg">&#39;</span><span class="st">user_id</span><span class="st st-sg">&#39;</span>] <span class="op">&gt;</span> <span class="nb nb-int">0</span>,
+ <span class="st st-sg">&#39;</span><span class="st">username</span><span class="st st-sg">&#39;</span>: <span class="name">row</span>[<span class="st st-sg">&#39;</span><span class="st">guestname</span><span class="st st-sg">&#39;</span>] <span class="op op-word">or</span> <span class="name">row</span>[<span class="st st-sg">&#39;</span><span class="st">username</span><span class="st st-sg">&#39;</span>],
+ <span class="st st-sg">&#39;</span><span class="st">url</span><span class="st st-sg">&#39;</span>: <span class="name">ctx</span>.<span class="name">make_url</span>(<span class="st st-sg">&#39;</span><span class="st">users</span><span class="st st-sg">&#39;</span>, <span class="name">urlencode</span>(<span class="name">row</span>[<span class="st st-sg">&#39;</span><span class="st">username</span><span class="st st-sg">&#39;</span>])),
+ },
+ <span class="st st-sg">&#39;</span><span class="st">posts</span><span class="st st-sg">&#39;</span>: <span class="name">root</span>,
+ <span class="st st-sg">&#39;</span><span class="st">post</span><span class="st st-sg">&#39;</span>: <span class="name">post</span>
+
+ }
+
+
+<span class="kw">def </span><span class="fun">get_post</span>(<span class="name">req</span>, <span class="name">post_id</span>):
+ <span class="st st-db">&quot;&quot;&quot;</span><span class="st">
+ Return exactly one post. If the post does not exist the result
+ will be ``None``.
+ </span><span class="st st-db">&quot;&quot;&quot;</span>
+ <span class="name">ctx</span> <span class="op">=</span> <span class="name">req</span>.<span class="name">ctx</span>
+
+ <span class="name">p</span> <span class="op">=</span> <span class="name">posts</span>.<span class="name">c</span>
+ <span class="name">u</span> <span class="op">=</span> <span class="name">users</span>.<span class="name">c</span>
+
+ <span class="name">r</span> <span class="op">=</span> <span class="name">ctx</span>.<span class="name">engine</span>.<span class="name">execute</span>(<span class="name">meta</span>.<span class="name">select</span>([<span class="name">p</span>.<span class="name">post_id</span>, <span class="name">p</span>.<span class="name">title</span>, <span class="name">p</span>.<span class="name">text</span>, <span class="name">p</span>.<span class="name">timestamp</span>,
+ <span class="name">u</span>.<span class="name">user_id</span>, <span class="name">u</span>.<span class="name">username</span>, <span class="name">u</span>.<span class="name">profile</span>,
+ <span class="name">u</span>.<span class="name">register_date</span>,
+ <span class="name">u</span>.<span class="name">post_count</span>, <span class="name">p</span>.<span class="name">username</span>.<span class="name">label</span>(<span class="st st-sg">&#39;</span><span class="st">guestname</span><span class="st st-sg">&#39;</span>)],
+ (<span class="name">p</span>.<span class="name">post_id</span> <span class="op">==</span> <span class="name">post_id</span>) <span class="op">&amp;</span>
+
+ (<span class="name">u</span>.<span class="name">user_id</span> <span class="op">==</span> <span class="name">p</span>.<span class="name">author_id</span>)
+ ))
+ <span class="name">row</span> <span class="op">=</span> <span class="name">r</span>.<span class="name">fetchone</span>()
+ <span class="kw">if</span> <span class="name">row</span> <span class="op op-word">is</span> <span class="bn bn-pseudo">None</span>:
+ <span class="kw">return</span>
+
+ <span class="name">post</span> <span class="op">=</span> <span class="bn">dict</span>(<span class="name">row</span>)
+ <span class="name">post</span>[<span class="st st-sg">&#39;</span><span class="st">url</span><span class="st st-sg">&#39;</span>] <span class="op">=</span> <span class="name">ctx</span>.<span class="name">make_url</span>(<span class="st st-sg">&#39;</span><span class="st">post</span><span class="st st-sg">&#39;</span>, <span class="name">post</span>[<span class="st st-sg">&#39;</span><span class="st">post_id</span><span class="st st-sg">&#39;</span>])
+ <span class="name">post</span>[<span class="st st-sg">&#39;</span><span class="st">author</span><span class="st st-sg">&#39;</span>] <span class="op">=</span> {
+ <span class="st st-sg">&#39;</span><span class="st">user_id</span><span class="st st-sg">&#39;</span>: <span class="name">post</span>[<span class="st st-sg">&#39;</span><span class="st">user_id</span><span class="st st-sg">&#39;</span>],
+ <span class="st st-sg">&#39;</span><span class="st">username</span><span class="st st-sg">&#39;</span>: <span class="name">post</span>.<span class="name">pop</span>(<span class="st st-sg">&#39;</span><span class="st">guestname</span><span class="st st-sg">&#39;</span>) <span class="op op-word">or</span> <span class="name">post</span>[<span class="st st-sg">&#39;</span><span class="st">username</span><span class="st st-sg">&#39;</span>],
+ <span class="st st-sg">&#39;</span><span class="st">self</span><span class="st st-sg">&#39;</span>: <span class="name">req</span>.<span class="name">user</span>.<span class="name">user_id</span> <span class="op">==</span> <span class="name">post</span>[<span class="st st-sg">&#39;</span><span class="st">user_id</span><span class="st st-sg">&#39;</span>],
+ <span class="st st-sg">&#39;</span><span class="st">registered</span><span class="st st-sg">&#39;</span>: <span class="name">post</span>.<span class="name">pop</span>(<span class="st st-sg">&#39;</span><span class="st">user_id</span><span class="st st-sg">&#39;</span>) <span class="op">&gt;</span> <span class="nb nb-int">0</span>,
+ <span class="st st-sg">&#39;</span><span class="st">register_date</span><span class="st st-sg">&#39;</span>: <span class="name">post</span>.<span class="name">pop</span>(<span class="st st-sg">&#39;</span><span class="st">register_date</span><span class="st st-sg">&#39;</span>),
+ <span class="st st-sg">&#39;</span><span class="st">url</span><span class="st st-sg">&#39;</span>: <span class="name">ctx</span>.<span class="name">make_url</span>(<span class="st st-sg">&#39;</span><span class="st">users</span><span class="st st-sg">&#39;</span>, <span class="name">urlencode</span>(<span class="name">post</span>.<span class="name">pop</span>(<span class="st st-sg">&#39;</span><span class="st">username</span><span class="st st-sg">&#39;</span>))),
+ <span class="st st-sg">&#39;</span><span class="st">profile</span><span class="st st-sg">&#39;</span>: <span class="name">post</span>.<span class="name">pop</span>(<span class="st st-sg">&#39;</span><span class="st">profile</span><span class="st st-sg">&#39;</span>),
+ <span class="st st-sg">&#39;</span><span class="st">post_count</span><span class="st st-sg">&#39;</span>: <span class="name">post</span>.<span class="name">pop</span>(<span class="st st-sg">&#39;</span><span class="st">post_count</span><span class="st st-sg">&#39;</span>)
+ }
+ <span class="name">signature</span> <span class="op">=</span> <span class="bn bn-pseudo">None</span>
+
+ <span class="kw">if</span> <span class="name">post</span>[<span class="st st-sg">&#39;</span><span class="st">author</span><span class="st st-sg">&#39;</span>][<span class="st st-sg">&#39;</span><span class="st">profile</span><span class="st st-sg">&#39;</span>].<span class="name">get</span>(<span class="st st-sg">&#39;</span><span class="st">signature</span><span class="st st-sg">&#39;</span>):
+ <span class="name">signature</span> <span class="op">=</span> <span class="name">parse_and_render</span>(<span class="name">req</span>, <span class="name">post</span>[<span class="st st-sg">&#39;</span><span class="st">author</span><span class="st st-sg">&#39;</span>][<span class="st st-sg">&#39;</span><span class="st">profile</span><span class="st st-sg">&#39;</span>][<span class="st st-sg">&#39;</span><span class="st">signature</span><span class="st st-sg">&#39;</span>])
+ <span class="name">post</span>[<span class="st st-sg">&#39;</span><span class="st">author</span><span class="st st-sg">&#39;</span>][<span class="st st-sg">&#39;</span><span class="st">signature</span><span class="st st-sg">&#39;</span>] <span class="op">=</span> <span class="name">signature</span>
+
+ <span class="cm">#XXX: cache here</span>
+ <span class="name">post</span>[<span class="st st-sg">&#39;</span><span class="st">parsed_text</span><span class="st st-sg">&#39;</span>] <span class="op">=</span> <span class="name">parse_and_render</span>(<span class="name">req</span>, <span class="name">post</span>[<span class="st st-sg">&#39;</span><span class="st">text</span><span class="st st-sg">&#39;</span>])
+ <span class="kw">return</span> <span class="name">post</span>
+
+
+<span class="kw">def </span><span class="fun">get_last_posts</span>(<span class="name">req</span>, <span class="name">root_post_id</span>, <span class="name">n</span><span class="op">=</span><span class="nb nb-int">1</span>, <span class="name">offset</span><span class="op">=</span><span class="nb nb-int">0</span>):
+ <span class="st st-db">&quot;&quot;&quot;</span><span class="st">
+
+ Returns a flat view of the n latest posts that are
+ children of root_post_id.
+ </span><span class="st st-db">&quot;&quot;&quot;</span>
+ <span class="name">p</span> <span class="op">=</span> <span class="name">posts</span>.<span class="name">c</span>
+ <span class="name">u</span> <span class="op">=</span> <span class="name">users</span>.<span class="name">c</span>
+
+ <span class="name">result</span> <span class="op">=</span> <span class="name">req</span>.<span class="name">ctx</span>.<span class="name">engine</span>.<span class="name">execute</span>(<span class="name">meta</span>.<span class="name">select</span>([<span class="name">p</span>.<span class="name">post_id</span>, <span class="name">p</span>.<span class="name">title</span>, <span class="name">p</span>.<span class="name">text</span>,
+ <span class="name">p</span>.<span class="name">timestamp</span>, <span class="name">u</span>.<span class="name">username</span>,
+ <span class="name">u</span>.<span class="name">user_id</span>, <span class="name">u</span>.<span class="name">profile</span>,
+ <span class="name">u</span>.<span class="name">post_count</span>, <span class="name">u</span>.<span class="name">register_date</span>,
+ <span class="name">p</span>.<span class="name">username</span>.<span class="name">label</span>(<span class="st st-sg">&#39;</span><span class="st">guestname</span><span class="st st-sg">&#39;</span>)],
+ (<span class="name">p</span>.<span class="name">root_post_id</span> <span class="op">==</span> <span class="name">root_post_id</span>) <span class="op">&amp;</span>
+
+ (<span class="name">u</span>.<span class="name">user_id</span> <span class="op">==</span> <span class="name">p</span>.<span class="name">author_id</span>),
+ <span class="name">order_by</span><span class="op">=</span>[<span class="name">meta</span>.<span class="name">desc</span>(<span class="name">p</span>.<span class="name">post_id</span>)],
+ <span class="name">limit</span><span class="op">=</span><span class="name">n</span>,
+ <span class="name">offset</span><span class="op">=</span><span class="name">offset</span>
+
+ ))
+
+ <span class="kw">def </span><span class="fun">prepare</span>(<span class="name">row</span>):
+ <span class="name">d</span> <span class="op">=</span> <span class="bn">dict</span>(<span class="name">row</span>)
+ <span class="name">user_id</span> <span class="op">=</span> <span class="name">d</span>.<span class="name">pop</span>(<span class="st st-sg">&#39;</span><span class="st">user_id</span><span class="st st-sg">&#39;</span>)
+ <span class="name">d</span>[<span class="st st-sg">&#39;</span><span class="st">url</span><span class="st st-sg">&#39;</span>] <span class="op">=</span> <span class="name">req</span>.<span class="name">ctx</span>.<span class="name">make_url</span>(<span class="st st-sg">&#39;</span><span class="st">post</span><span class="st st-sg">&#39;</span>, <span class="name">row</span>[<span class="st st-sg">&#39;</span><span class="st">post_id</span><span class="st st-sg">&#39;</span>])
+ <span class="name">d</span>[<span class="st st-sg">&#39;</span><span class="st">author</span><span class="st st-sg">&#39;</span>] <span class="op">=</span> {
+ <span class="st st-sg">&#39;</span><span class="st">user_id</span><span class="st st-sg">&#39;</span>: <span class="name">user_id</span>,
+ <span class="st st-sg">&#39;</span><span class="st">registered</span><span class="st st-sg">&#39;</span>: <span class="name">user_id</span> <span class="op">&gt;</span> <span class="nb nb-int">0</span>,
+ <span class="st st-sg">&#39;</span><span class="st">username</span><span class="st st-sg">&#39;</span>: <span class="name">d</span>.<span class="name">pop</span>(<span class="st st-sg">&#39;</span><span class="st">guestname</span><span class="st st-sg">&#39;</span>) <span class="op op-word">or</span> <span class="name">d</span>.<span class="name">pop</span>(<span class="st st-sg">&#39;</span><span class="st">username</span><span class="st st-sg">&#39;</span>),
+ <span class="st st-sg">&#39;</span><span class="st">self</span><span class="st st-sg">&#39;</span>: <span class="name">req</span>.<span class="name">user</span>.<span class="name">user_id</span> <span class="op">==</span> <span class="name">user_id</span>,
+ <span class="st st-sg">&#39;</span><span class="st">profile</span><span class="st st-sg">&#39;</span>: <span class="name">d</span>.<span class="name">pop</span>(<span class="st st-sg">&#39;</span><span class="st">profile</span><span class="st st-sg">&#39;</span>),
+ <span class="st st-sg">&#39;</span><span class="st">post_count</span><span class="st st-sg">&#39;</span>: <span class="name">d</span>.<span class="name">pop</span>(<span class="st st-sg">&#39;</span><span class="st">post_count</span><span class="st st-sg">&#39;</span>),
+ <span class="st st-sg">&#39;</span><span class="st">register_date</span><span class="st st-sg">&#39;</span>: <span class="name">d</span>.<span class="name">pop</span>(<span class="st st-sg">&#39;</span><span class="st">register_date</span><span class="st st-sg">&#39;</span>),
+ <span class="st st-sg">&#39;</span><span class="st">url</span><span class="st st-sg">&#39;</span>: <span class="name">req</span>.<span class="name">ctx</span>.<span class="name">make_url</span>(<span class="st st-sg">&#39;</span><span class="st">users</span><span class="st st-sg">&#39;</span>, <span class="name">urlencode</span>(<span class="name">row</span>[<span class="st st-sg">&#39;</span><span class="st">username</span><span class="st st-sg">&#39;</span>])),
+ }
+ <span class="name">signature</span> <span class="op">=</span> <span class="bn bn-pseudo">None</span>
+
+ <span class="kw">if</span> <span class="name">d</span>[<span class="st st-sg">&#39;</span><span class="st">author</span><span class="st st-sg">&#39;</span>][<span class="st st-sg">&#39;</span><span class="st">profile</span><span class="st st-sg">&#39;</span>].<span class="name">get</span>(<span class="st st-sg">&#39;</span><span class="st">signature</span><span class="st st-sg">&#39;</span>):
+ <span class="name">signature</span> <span class="op">=</span> <span class="name">parse_and_render</span>(<span class="name">req</span>, <span class="name">d</span>[<span class="st st-sg">&#39;</span><span class="st">author</span><span class="st st-sg">&#39;</span>][<span class="st st-sg">&#39;</span><span class="st">profile</span><span class="st st-sg">&#39;</span>][<span class="st st-sg">&#39;</span><span class="st">signature</span><span class="st st-sg">&#39;</span>],
+ <span class="name">signature</span><span class="op">=</span><span class="bn bn-pseudo">True</span>)
+ <span class="name">d</span>[<span class="st st-sg">&#39;</span><span class="st">author</span><span class="st st-sg">&#39;</span>][<span class="st st-sg">&#39;</span><span class="st">signature</span><span class="st st-sg">&#39;</span>] <span class="op">=</span> <span class="name">signature</span>
+
+ <span class="cm">#XXX: this doesn&#39;t cache by now</span>
+ <span class="name">d</span>[<span class="st st-sg">&#39;</span><span class="st">parsed_text</span><span class="st st-sg">&#39;</span>] <span class="op">=</span> <span class="name">parse_and_render</span>(<span class="name">req</span>, <span class="name">d</span>[<span class="st st-sg">&#39;</span><span class="st">text</span><span class="st st-sg">&#39;</span>])
+ <span class="kw">return</span> <span class="name">d</span>
+
+ <span class="name">post_list</span> <span class="op">=</span> [<span class="name">prepare</span>(<span class="name">row</span>) <span class="kw">for</span> <span class="name">row</span> <span class="op op-word">in</span> <span class="name">result</span>]
+ <span class="kw">return</span> <span class="name">post_list</span>
+
+
+<span class="kw">def </span><span class="fun">get_flat_view</span>(<span class="name">req</span>, <span class="name">post_id</span>, <span class="name">inc_view_count</span><span class="op">=</span><span class="bn bn-pseudo">True</span>, <span class="name">order</span><span class="op">=</span><span class="st st-sg">&#39;</span><span class="st">asc</span><span class="st st-sg">&#39;</span>):
+ <span class="st st-db">&quot;&quot;&quot;</span><span class="st">
+
+ Returns the flat view of an post and the next n posts so
+ that the template can render a page. n is the number of
+ posts per page defined in either the user settings or the
+ global forum configuration.
+
+ Per default it will increment the view counter of the
+ thread requested. If you don&#39;t want that set ``inc_view_count``
+ to ``False``.
+
+ If you want to get the latest post first, set ``order``
+ to ``&#39;desc&#39;``.
+ </span><span class="st st-db">&quot;&quot;&quot;</span>
+ <span class="name">ctx</span> <span class="op">=</span> <span class="name">req</span>.<span class="name">ctx</span>
+ <span class="name">p</span> <span class="op">=</span> <span class="name">posts</span>.<span class="name">c</span>
+
+ <span class="name">f</span> <span class="op">=</span> <span class="name">forums</span>.<span class="name">c</span>
+ <span class="name">u</span> <span class="op">=</span> <span class="name">users</span>.<span class="name">c</span>
+
+ <span class="cm"># find root_post_id</span>
+ <span class="name">result</span> <span class="op">=</span> <span class="name">ctx</span>.<span class="name">engine</span>.<span class="name">execute</span>(<span class="name">meta</span>.<span class="name">select</span>([<span class="name">p</span>.<span class="name">root_post_id</span>],
+ <span class="name">p</span>.<span class="name">post_id</span> <span class="op">==</span> <span class="name">post_id</span>
+
+ ))
+ <span class="cm"># XXX: This raises TypeError on failure.</span>
+ <span class="name">root_post_id</span> <span class="op">=</span> <span class="name">result</span>.<span class="name">fetchone</span>()[<span class="nb nb-int">0</span>]
+
+ <span class="cm"># select all post ids to calculate the position of the post on a page</span>
+
+ <span class="cm"># the purpose of this calculation is to find the first and last post</span>
+ <span class="cm"># on the page if the post_id given the function</span>
+ <span class="name">result</span> <span class="op">=</span> <span class="name">ctx</span>.<span class="name">engine</span>.<span class="name">execute</span>(<span class="name">meta</span>.<span class="name">select</span>([<span class="name">p</span>.<span class="name">post_id</span>],
+ <span class="name">p</span>.<span class="name">root_post_id</span> <span class="op">==</span> <span class="name">root_post_id</span>
+
+ ))
+ <span class="name">posts_per_page</span> <span class="op">=</span> <span class="name">get_posts_per_page</span>(<span class="name">req</span>)
+ <span class="name">postlist</span> <span class="op">=</span> [<span class="name">row</span>[<span class="nb nb-int">0</span>] <span class="kw">for</span> <span class="name">row</span> <span class="op op-word">in</span> <span class="name">result</span>]
+ <span class="name">post_index</span> <span class="op">=</span> <span class="name">postlist</span>.<span class="name">index</span>(<span class="name">post_id</span>)
+ <span class="name">page</span> <span class="op">=</span> <span class="name">post_index</span> <span class="op">//</span> <span class="name">posts_per_page</span>
+
+ <span class="name">page_start</span> <span class="op">=</span> <span class="name">page</span> <span class="op">*</span> <span class="name">posts_per_page</span>
+ <span class="name">post_range_low</span> <span class="op">=</span> <span class="name">postlist</span>[<span class="name">page_start</span>]
+ <span class="name">post_range_high</span> <span class="op">=</span> <span class="name">postlist</span>[<span class="name">page_start</span>:<span class="name">page_start</span> <span class="op">+</span> <span class="name">posts_per_page</span>][<span class="op">-</span><span class="nb nb-int">1</span>]
+
+ <span class="name">pagination</span> <span class="op">=</span> <span class="name">Pagination</span>(<span class="name">req</span>, <span class="name">postlist</span>, <span class="name">page_start</span>, <span class="name">posts_per_page</span>,
+ <span class="kw">lambda</span> <span class="name">x</span>: <span class="st st-sg">&#39;</span><span class="st">post/</span><span class="st st-int">%d</span><span class="st st-sg">&#39;</span> <span class="op">%</span> <span class="name">x</span>)
+
+ <span class="name">orderfunc</span> <span class="op">=</span> (<span class="name">order</span> <span class="op">==</span> <span class="st st-sg">&#39;</span><span class="st">desc</span><span class="st st-sg">&#39;</span> <span class="op op-word">and</span> <span class="name">meta</span>.<span class="name">desc</span> <span class="op op-word">or</span> <span class="name">meta</span>.<span class="name">asc</span>)
+ <span class="cm"># select matching posts</span>
+
+ <span class="name">result</span> <span class="op">=</span> <span class="name">ctx</span>.<span class="name">engine</span>.<span class="name">execute</span>(<span class="name">meta</span>.<span class="name">select</span>([<span class="name">p</span>.<span class="name">post_id</span>, <span class="name">p</span>.<span class="name">root_post_id</span>, <span class="name">p</span>.<span class="name">title</span>,
+ <span class="name">p</span>.<span class="name">forum_id</span>, <span class="name">p</span>.<span class="name">parent_id</span>, <span class="name">p</span>.<span class="name">text</span>,
+ <span class="name">p</span>.<span class="name">timestamp</span>, <span class="name">u</span>.<span class="name">username</span>, <span class="name">u</span>.<span class="name">user_id</span>,
+ <span class="name">u</span>.<span class="name">profile</span>, <span class="name">u</span>.<span class="name">post_count</span>, <span class="name">u</span>.<span class="name">register_date</span>,
+ <span class="name">p</span>.<span class="name">username</span>.<span class="name">label</span>(<span class="st st-sg">&#39;</span><span class="st">guestname</span><span class="st st-sg">&#39;</span>)],
+ (<span class="name">p</span>.<span class="name">root_post_id</span> <span class="op">==</span> <span class="name">root_post_id</span>) <span class="op">&amp;</span>
+
+ (<span class="name">p</span>.<span class="name">post_id</span> <span class="op">&gt;=</span> <span class="name">post_range_low</span>) <span class="op">&amp;</span>
+ (<span class="name">p</span>.<span class="name">post_id</span> <span class="op">&lt;=</span> <span class="name">post_range_high</span>) <span class="op">&amp;</span>
+
+ (<span class="name">u</span>.<span class="name">user_id</span> <span class="op">==</span> <span class="name">p</span>.<span class="name">author_id</span>),
+ <span class="name">order_by</span><span class="op">=</span>[<span class="name">orderfunc</span>(<span class="name">p</span>.<span class="name">post_id</span>)]
+ ))
+
+ <span class="kw">def </span><span class="fun">prepare</span>(<span class="name">number</span>, <span class="name">row</span>):
+ <span class="name">d</span> <span class="op">=</span> <span class="bn">dict</span>(<span class="name">row</span>)
+ <span class="name">user_id</span> <span class="op">=</span> <span class="name">d</span>.<span class="name">pop</span>(<span class="st st-sg">&#39;</span><span class="st">user_id</span><span class="st st-sg">&#39;</span>)
+ <span class="name">d</span>[<span class="st st-sg">&#39;</span><span class="st">post_number</span><span class="st st-sg">&#39;</span>] <span class="op">=</span> <span class="name">number</span>
+
+ <span class="name">d</span>[<span class="st st-sg">&#39;</span><span class="st">url</span><span class="st st-sg">&#39;</span>] <span class="op">=</span> <span class="name">ctx</span>.<span class="name">make_url</span>(<span class="st st-sg">&#39;</span><span class="st">post</span><span class="st st-sg">&#39;</span>, <span class="name">row</span>[<span class="st st-sg">&#39;</span><span class="st">post_id</span><span class="st st-sg">&#39;</span>])
+ <span class="name">d</span>[<span class="st st-sg">&#39;</span><span class="st">author</span><span class="st st-sg">&#39;</span>] <span class="op">=</span> {
+ <span class="st st-sg">&#39;</span><span class="st">user_id</span><span class="st st-sg">&#39;</span>: <span class="name">user_id</span>,
+ <span class="st st-sg">&#39;</span><span class="st">registered</span><span class="st st-sg">&#39;</span>: <span class="name">user_id</span> <span class="op">&gt;</span> <span class="nb nb-int">0</span>,
+ <span class="st st-sg">&#39;</span><span class="st">username</span><span class="st st-sg">&#39;</span>: <span class="name">d</span>.<span class="name">pop</span>(<span class="st st-sg">&#39;</span><span class="st">guestname</span><span class="st st-sg">&#39;</span>) <span class="op op-word">or</span> <span class="name">d</span>.<span class="name">pop</span>(<span class="st st-sg">&#39;</span><span class="st">username</span><span class="st st-sg">&#39;</span>),
+ <span class="st st-sg">&#39;</span><span class="st">self</span><span class="st st-sg">&#39;</span>: <span class="name">req</span>.<span class="name">user</span>.<span class="name">user_id</span> <span class="op">==</span> <span class="name">user_id</span>,
+ <span class="st st-sg">&#39;</span><span class="st">profile</span><span class="st st-sg">&#39;</span>: <span class="name">d</span>.<span class="name">pop</span>(<span class="st st-sg">&#39;</span><span class="st">profile</span><span class="st st-sg">&#39;</span>),
+ <span class="st st-sg">&#39;</span><span class="st">post_count</span><span class="st st-sg">&#39;</span>: <span class="name">d</span>.<span class="name">pop</span>(<span class="st st-sg">&#39;</span><span class="st">post_count</span><span class="st st-sg">&#39;</span>),
+ <span class="st st-sg">&#39;</span><span class="st">register_date</span><span class="st st-sg">&#39;</span>: <span class="name">d</span>.<span class="name">pop</span>(<span class="st st-sg">&#39;</span><span class="st">register_date</span><span class="st st-sg">&#39;</span>),
+ <span class="st st-sg">&#39;</span><span class="st">url</span><span class="st st-sg">&#39;</span>: <span class="name">ctx</span>.<span class="name">make_url</span>(<span class="st st-sg">&#39;</span><span class="st">users</span><span class="st st-sg">&#39;</span>, <span class="name">urlencode</span>(<span class="name">row</span>[<span class="st st-sg">&#39;</span><span class="st">username</span><span class="st st-sg">&#39;</span>])),
+ }
+ <span class="name">signature</span> <span class="op">=</span> <span class="bn bn-pseudo">None</span>
+
+ <span class="kw">if</span> <span class="name">d</span>[<span class="st st-sg">&#39;</span><span class="st">author</span><span class="st st-sg">&#39;</span>][<span class="st st-sg">&#39;</span><span class="st">profile</span><span class="st st-sg">&#39;</span>].<span class="name">get</span>(<span class="st st-sg">&#39;</span><span class="st">signature</span><span class="st st-sg">&#39;</span>):
+ <span class="name">signature</span> <span class="op">=</span> <span class="name">parse_and_render</span>(<span class="name">req</span>, <span class="name">d</span>[<span class="st st-sg">&#39;</span><span class="st">author</span><span class="st st-sg">&#39;</span>][<span class="st st-sg">&#39;</span><span class="st">profile</span><span class="st st-sg">&#39;</span>][<span class="st st-sg">&#39;</span><span class="st">signature</span><span class="st st-sg">&#39;</span>],
+ <span class="name">signature</span><span class="op">=</span><span class="bn bn-pseudo">True</span>)
+ <span class="name">d</span>[<span class="st st-sg">&#39;</span><span class="st">author</span><span class="st st-sg">&#39;</span>][<span class="st st-sg">&#39;</span><span class="st">signature</span><span class="st st-sg">&#39;</span>] <span class="op">=</span> <span class="name">signature</span>
+
+ <span class="cm">#XXX: this doesn&#39;t cache by now</span>
+ <span class="name">d</span>[<span class="st st-sg">&#39;</span><span class="st">parsed_text</span><span class="st st-sg">&#39;</span>] <span class="op">=</span> <span class="name">parse_and_render</span>(<span class="name">req</span>, <span class="name">d</span>[<span class="st st-sg">&#39;</span><span class="st">text</span><span class="st st-sg">&#39;</span>])
+ <span class="kw">return</span> <span class="name">d</span>
+
+ <span class="name">real_posts</span> <span class="op">=</span> [<span class="name">prepare</span>(<span class="name">num</span>, <span class="name">row</span>) <span class="kw">for</span> <span class="name">num</span>, <span class="name">row</span> <span class="op op-word">in</span> <span class="name">inciter</span>(<span class="name">result</span>, <span class="name">page_start</span>)]
+
+ <span class="cm"># increment view_count</span>
+
+ <span class="kw">if</span> <span class="name">inc_view_count</span>:
+ <span class="name">result</span> <span class="op">=</span> <span class="name">ctx</span>.<span class="name">engine</span>.<span class="name">execute</span>(<span class="name">meta</span>.<span class="name">select</span>([<span class="name">p</span>.<span class="name">view_count</span>],
+ <span class="name">p</span>.<span class="name">post_id</span> <span class="op">==</span> <span class="name">root_post_id</span>))
+ <span class="name">ctx</span>.<span class="name">engine</span>.<span class="name">execute</span>(<span class="name">posts</span>.<span class="name">update</span>(<span class="name">p</span>.<span class="name">post_id</span> <span class="op">==</span> <span class="name">root_post_id</span>),
+ <span class="name">view_count</span> <span class="op">=</span> <span class="name">result</span>.<span class="name">fetchone</span>()[<span class="nb nb-int">0</span>] <span class="op">+</span> <span class="nb nb-int">1</span>
+
+ )
+
+ <span class="cm"># and another query for the overview page</span>
+ <span class="name">result</span> <span class="op">=</span> <span class="name">ctx</span>.<span class="name">engine</span>.<span class="name">execute</span>(<span class="name">meta</span>.<span class="name">select</span>([<span class="name">p</span>.<span class="name">post_id</span>, <span class="name">p</span>.<span class="name">title</span>, <span class="name">p</span>.<span class="name">forum_id</span>,
+ <span class="name">p</span>.<span class="name">timestamp</span>, <span class="name">u</span>.<span class="name">user_id</span>, <span class="name">u</span>.<span class="name">username</span>,
+ <span class="name">f</span>.<span class="name">name</span>, <span class="name">p</span>.<span class="name">username</span>.<span class="name">label</span>(<span class="st st-sg">&#39;</span><span class="st">guestname</span><span class="st st-sg">&#39;</span>)],
+ (<span class="name">p</span>.<span class="name">post_id</span> <span class="op">==</span> <span class="name">root_post_id</span>) <span class="op">&amp;</span>
+
+ (<span class="name">u</span>.<span class="name">user_id</span> <span class="op">==</span> <span class="name">p</span>.<span class="name">author_id</span>) <span class="op">&amp;</span>
+ (<span class="name">f</span>.<span class="name">forum_id</span> <span class="op">==</span> <span class="name">p</span>.<span class="name">forum_id</span>)
+ ))
+ <span class="name">row</span> <span class="op">=</span> <span class="name">result</span>.<span class="name">fetchone</span>()
+ <span class="kw">return</span> {
+ <span class="st st-sg">&#39;</span><span class="st">post_id</span><span class="st st-sg">&#39;</span>: <span class="name">row</span>[<span class="st st-sg">&#39;</span><span class="st">post_id</span><span class="st st-sg">&#39;</span>],
+ <span class="st st-sg">&#39;</span><span class="st">url</span><span class="st st-sg">&#39;</span>: <span class="name">ctx</span>.<span class="name">make_url</span>(<span class="st st-sg">&#39;</span><span class="st">post</span><span class="st st-sg">&#39;</span>, <span class="name">row</span>[<span class="st st-sg">&#39;</span><span class="st">post_id</span><span class="st st-sg">&#39;</span>]),
+ <span class="st st-sg">&#39;</span><span class="st">title</span><span class="st st-sg">&#39;</span>: <span class="name">row</span>[<span class="st st-sg">&#39;</span><span class="st">title</span><span class="st st-sg">&#39;</span>],
+ <span class="st st-sg">&#39;</span><span class="st">timestamp</span><span class="st st-sg">&#39;</span>: <span class="name">row</span>[<span class="st st-sg">&#39;</span><span class="st">timestamp</span><span class="st st-sg">&#39;</span>],
+ <span class="st st-sg">&#39;</span><span class="st">forum</span><span class="st st-sg">&#39;</span>: {
+ <span class="st st-sg">&#39;</span><span class="st">forum_id</span><span class="st st-sg">&#39;</span>: <span class="name">row</span>[<span class="st st-sg">&#39;</span><span class="st">forum_id</span><span class="st st-sg">&#39;</span>],
+ <span class="st st-sg">&#39;</span><span class="st">name</span><span class="st st-sg">&#39;</span>: <span class="name">row</span>[<span class="st st-sg">&#39;</span><span class="st">name</span><span class="st st-sg">&#39;</span>],
+ <span class="st st-sg">&#39;</span><span class="st">url</span><span class="st st-sg">&#39;</span>: <span class="name">ctx</span>.<span class="name">make_url</span>(<span class="st st-sg">&#39;</span><span class="st">forum</span><span class="st st-sg">&#39;</span>, <span class="name">row</span>[<span class="st st-sg">&#39;</span><span class="st">forum_id</span><span class="st st-sg">&#39;</span>]),
+ },
+ <span class="st st-sg">&#39;</span><span class="st">author</span><span class="st st-sg">&#39;</span>: {
+ <span class="st st-sg">&#39;</span><span class="st">user_id</span><span class="st st-sg">&#39;</span>: <span class="name">row</span>[<span class="st st-sg">&#39;</span><span class="st">user_id</span><span class="st st-sg">&#39;</span>],
+ <span class="st st-sg">&#39;</span><span class="st">username</span><span class="st st-sg">&#39;</span>: <span class="name">row</span>[<span class="st st-sg">&#39;</span><span class="st">guestname</span><span class="st st-sg">&#39;</span>] <span class="op op-word">or</span> <span class="name">row</span>[<span class="st st-sg">&#39;</span><span class="st">username</span><span class="st st-sg">&#39;</span>],
+ <span class="st st-sg">&#39;</span><span class="st">url</span><span class="st st-sg">&#39;</span>: <span class="name">ctx</span>.<span class="name">make_url</span>(<span class="st st-sg">&#39;</span><span class="st">users</span><span class="st st-sg">&#39;</span>, <span class="name">urlencode</span>(<span class="name">row</span>[<span class="st st-sg">&#39;</span><span class="st">username</span><span class="st st-sg">&#39;</span>])),
+ },
+ <span class="st st-sg">&#39;</span><span class="st">pagination</span><span class="st st-sg">&#39;</span>: <span class="name">pagination</span>,
+ <span class="st st-sg">&#39;</span><span class="st">posts</span><span class="st st-sg">&#39;</span>: <span class="name">real_posts</span>
+
+ }
+
+
+<span class="kw">def </span><span class="fun">get_last_thread_change</span>(<span class="name">req</span>, <span class="name">post_id</span>):
+ <span class="st st-db">&quot;&quot;&quot;</span><span class="st">
+ Return the timestamp of the last change in the thread.
+ ``post_id`` must be the root_post_id, there is no further
+ check done.
+
+ Return ``None`` if something in the query went wrong (eg.
+ no thread with the requested root_post_id exists)
+ </span><span class="st st-db">&quot;&quot;&quot;</span>
+ <span class="cm">#XXX: doesn&#39;t cover edits</span>
+
+ <span class="name">result</span> <span class="op">=</span> <span class="name">req</span>.<span class="name">ctx</span>.<span class="name">engine</span>.<span class="name">execute</span>(<span class="name">meta</span>.<span class="name">select</span>([<span class="name">posts</span>.<span class="name">c</span>.<span class="name">timestamp</span>],
+ (<span class="name">posts</span>.<span class="name">c</span>.<span class="name">root_post_id</span> <span class="op">==</span> <span class="name">post_id</span>),
+ <span class="name">order_by</span><span class="op">=</span>[<span class="name">meta</span>.<span class="name">desc</span>(<span class="name">posts</span>.<span class="name">c</span>.<span class="name">post_id</span>)],
+ <span class="name">limit</span><span class="op">=</span><span class="nb nb-int">1</span>
+
+ ))
+ <span class="name">row</span> <span class="op">=</span> <span class="name">result</span>.<span class="name">fetchone</span>()
+ <span class="kw">if</span> <span class="name">row</span> <span class="op op-word">is</span> <span class="bn bn-pseudo">None</span>:
+ <span class="kw">return</span>
+
+ <span class="kw">return</span> <span class="name">row</span>[<span class="nb nb-int">0</span>]
+
+
+<span class="kw">def </span><span class="fun">get_posts_per_page</span>(<span class="name">req</span>):
+ <span class="st st-db">&quot;&quot;&quot;</span><span class="st">
+ Return the number of posts a user wishes to display on the
+ flat view page.
+ </span><span class="st st-db">&quot;&quot;&quot;</span>
+ <span class="kw">try</span>:
+ <span class="name">posts_per_page</span> <span class="op">=</span> <span class="name">req</span>.<span class="name">user</span>.<span class="name">settings</span>[<span class="st st-sg">&#39;</span><span class="st">posts_per_page</span><span class="st st-sg">&#39;</span>]
+ <span class="kw">if</span> <span class="name">posts_per_page</span> <span class="op op-word">is</span> <span class="op op-word">not</span> <span class="bn bn-pseudo">None</span>:
+ <span class="kw">return</span> <span class="name">posts_per_page</span>
+
+ <span class="kw">except</span> <span class="exc">KeyError</span>:
+ <span class="kw">pass</span>
+ <span class="kw">return</span> <span class="name">req</span>.<span class="name">ctx</span>.<span class="name">cfg</span>.<span class="name">get_int</span>(<span class="st st-sg">&#39;</span><span class="st">board</span><span class="st st-sg">&#39;</span>, <span class="st st-sg">&#39;</span><span class="st">posts_per_page</span><span class="st st-sg">&#39;</span>, <span class="nb nb-int">15</span>)
+
+
+
+<span class="kw">def </span><span class="fun">get_threads_per_page</span>(<span class="name">req</span>):
+ <span class="st st-db">&quot;&quot;&quot;</span><span class="st">
+ Return the number of posts a users whishes to display on the
+ viewforum page.
+ </span><span class="st st-db">&quot;&quot;&quot;</span>
+ <span class="kw">try</span>:
+ <span class="name">threads_per_page</span> <span class="op">=</span> <span class="name">req</span>.<span class="name">user</span>.<span class="name">settings</span>[<span class="st st-sg">&#39;</span><span class="st">threads_per_page</span><span class="st st-sg">&#39;</span>]
+ <span class="kw">if</span> <span class="name">threads_per_page</span> <span class="op op-word">is</span> <span class="op op-word">not</span> <span class="bn bn-pseudo">None</span>:
+ <span class="kw">return</span> <span class="name">threads_per_page</span>
+
+ <span class="kw">except</span> <span class="exc">KeyError</span>:
+ <span class="kw">pass</span>
+ <span class="kw">return</span> <span class="name">req</span>.<span class="name">ctx</span>.<span class="name">cfg</span>.<span class="name">get_int</span>(<span class="st st-sg">&#39;</span><span class="st">board</span><span class="st st-sg">&#39;</span>, <span class="st st-sg">&#39;</span><span class="st">threads_per_page</span><span class="st st-sg">&#39;</span>, <span class="nb nb-int">20</span>)
+
+
+
+<span class="kw">def </span><span class="fun">get_view_mode</span>(<span class="name">req</span>):
+ <span class="st st-db">&quot;&quot;&quot;</span><span class="st">
+ Return the display mode a user has defined in the user settings
+ or fall back to the default mode from the pocoo.conf.
+
+ :return: either ``&#39;flat&#39;`` or ``&#39;threaded&#39;``.
+ </span><span class="st st-db">&quot;&quot;&quot;</span>
+ <span class="name">val</span> <span class="op">=</span> <span class="name">req</span>.<span class="name">user</span>.<span class="name">settings</span>.<span class="name">get</span>(<span class="st st-sg">&#39;</span><span class="st">view_mode</span><span class="st st-sg">&#39;</span>)
+ <span class="kw">if</span> <span class="name">val</span> <span class="op op-word">in</span> (<span class="st st-sg">&#39;</span><span class="st">flat</span><span class="st st-sg">&#39;</span>, <span class="st st-sg">&#39;</span><span class="st">threaded</span><span class="st st-sg">&#39;</span>):
+ <span class="kw">return</span> <span class="name">val</span>
+
+ <span class="name">val</span> <span class="op">=</span> <span class="name">req</span>.<span class="name">ctx</span>.<span class="name">cfg</span>.<span class="name">get</span>(<span class="st st-sg">&#39;</span><span class="st">board</span><span class="st st-sg">&#39;</span>, <span class="st st-sg">&#39;</span><span class="st">default_view</span><span class="st st-sg">&#39;</span>, <span class="bn bn-pseudo">None</span>)
+ <span class="kw">return</span> (<span class="name">val</span> <span class="op op-word">in</span> (<span class="st st-sg">&#39;</span><span class="st">flat</span><span class="st st-sg">&#39;</span>, <span class="st st-sg">&#39;</span><span class="st">threaded</span><span class="st st-sg">&#39;</span>)) <span class="op op-word">and</span> <span class="name">val</span> <span class="op op-word">or</span> <span class="st st-sg">&#39;</span><span class="st">threaded</span><span class="st st-sg">&#39;</span>
+
+
+<span class="kw">def </span><span class="fun">quote_post</span>(<span class="name">req</span>, <span class="name">post_id</span>):
+ <span class="st st-db">&quot;&quot;&quot;</span><span class="st">
+ Return a tuple in the form ``(text, title)`` which is useful
+ for replying and quoting existing posts. The title is
+ prefixed with a local representation of &#39;Re:&#39; and the text
+ is quoted with the selected markup.
+ </span><span class="st st-db">&quot;&quot;&quot;</span>
+ <span class="name">p</span> <span class="op">=</span> <span class="name">posts</span>.<span class="name">c</span>
+
+ <span class="name">u</span> <span class="op">=</span> <span class="name">users</span>.<span class="name">c</span>
+ <span class="name">_</span> <span class="op">=</span> <span class="name">req</span>.<span class="name">gettext</span>
+
+ <span class="name">result</span> <span class="op">=</span> <span class="name">req</span>.<span class="name">ctx</span>.<span class="name">engine</span>.<span class="name">execute</span>(<span class="name">meta</span>.<span class="name">select</span>([<span class="name">p</span>.<span class="name">title</span>, <span class="name">p</span>.<span class="name">text</span>, <span class="name">u</span>.<span class="name">username</span>],
+ (<span class="name">p</span>.<span class="name">post_id</span> <span class="op">==</span> <span class="name">post_id</span>) <span class="op">&amp;</span>
+
+ (<span class="name">u</span>.<span class="name">user_id</span> <span class="op">==</span> <span class="name">p</span>.<span class="name">author_id</span>)
+ ))
+ <span class="name">row</span> <span class="op">=</span> <span class="name">result</span>.<span class="name">fetchone</span>()
+ <span class="kw">if</span> <span class="name">row</span> <span class="op op-word">is</span> <span class="bn bn-pseudo">None</span>:
+ <span class="cm"># XXX: ValueError?</span>
+
+ <span class="kw">raise</span> <span class="exc">ValueError</span>(<span class="st st-sg">&#39;</span><span class="st">post </span><span class="st st-int">%s</span><span class="st"> does not exist</span><span class="st st-sg">&#39;</span>)
+
+ <span class="name">suffix</span> <span class="op">=</span> <span class="name">_</span>(<span class="st st-sg">&#39;</span><span class="st">Re:</span><span class="st st-sg">&#39;</span>)
+ <span class="name">title</span> <span class="op">=</span> <span class="name">row</span>[<span class="st st-sg">&#39;</span><span class="st">title</span><span class="st st-sg">&#39;</span>]
+ <span class="kw">if</span> <span class="op op-word">not</span> <span class="name">title</span>.<span class="name">startswith</span>(<span class="name">suffix</span>):
+ <span class="name">title</span> <span class="op">=</span> <span class="name">u</span><span class="st st-sg">&#39;</span><span class="st st-int">%s</span><span class="st"> </span><span class="st st-int">%s</span><span class="st st-sg">&#39;</span> <span class="op">%</span> (<span class="name">suffix</span>, <span class="name">title</span>)
+ <span class="name">text</span> <span class="op">=</span> <span class="name">quote_text</span>(<span class="name">req</span>, <span class="name">row</span>[<span class="st st-sg">&#39;</span><span class="st">text</span><span class="st st-sg">&#39;</span>], <span class="name">row</span>[<span class="st st-sg">&#39;</span><span class="st">username</span><span class="st st-sg">&#39;</span>])
+ <span class="kw">return</span> <span class="name">text</span>, <span class="name">title</span>
+
+
+<span class="kw">def </span><span class="fun">edit_post</span>(<span class="name">req</span>, <span class="name">post_id</span>):
+ <span class="st st-db">&quot;&quot;&quot;</span><span class="st">
+ Return a tuple in the form (``text``, ``title``, ``username``)
+ for the edit view.
+
+ :see: `quote_post`
+ </span><span class="st st-db">&quot;&quot;&quot;</span>
+ <span class="name">p</span> <span class="op">=</span> <span class="name">posts</span>.<span class="name">c</span>
+
+ <span class="name">result</span> <span class="op">=</span> <span class="name">req</span>.<span class="name">ctx</span>.<span class="name">engine</span>.<span class="name">execute</span>(<span class="name">meta</span>.<span class="name">select</span>([<span class="name">p</span>.<span class="name">text</span>, <span class="name">p</span>.<span class="name">title</span>, <span class="name">p</span>.<span class="name">username</span>],
+ (<span class="name">p</span>.<span class="name">post_id</span> <span class="op">==</span> <span class="name">post_id</span>)
+ ))
+ <span class="name">row</span> <span class="op">=</span> <span class="name">result</span>.<span class="name">fetchone</span>()
+ <span class="kw">if</span> <span class="name">row</span> <span class="op op-word">is</span> <span class="bn bn-pseudo">None</span>:
+ <span class="cm"># XXX: ValueError?</span>
+
+ <span class="kw">raise</span> <span class="exc">ValueError</span>(<span class="st st-sg">&#39;</span><span class="st">post </span><span class="st st-int">%s</span><span class="st"> does not exist</span><span class="st st-sg">&#39;</span>)
+ <span class="kw">return</span> <span class="bn">tuple</span>(<span class="name">row</span>)
+
+
+<span class="kw">class </span><span class="cls">_Site</span>(<span class="bn">object</span>):
+ <span class="st st-db">&quot;&quot;&quot;</span><span class="st">A special singleton representing a whole site.</span><span class="st st-db">&quot;&quot;&quot;</span>
+
+ <span class="name">object_id</span> <span class="op">=</span> <span class="nb nb-int">0</span>
+
+ <span class="kw">def </span><span class="fun">__repr__</span>(<span class="bn bn-pseudo">self</span>):
+ <span class="kw">return</span> <span class="st st-sg">&#39;</span><span class="st">&lt;</span><span class="st st-int">%s</span><span class="st">&gt;</span><span class="st st-sg">&#39;</span> <span class="op">%</span> <span class="bn bn-pseudo">self</span>.<span class="name">__class__</span>.<span class="name">__name__</span>
+
+<span class="name">Site</span> <span class="op">=</span> <span class="name">_Site</span>()
+
+
+<span class="kw">class </span><span class="cls">Forum</span>(<span class="name">DatabaseModel</span>):
+ <span class="st st-db">&quot;&quot;&quot;</span><span class="st">
+ This class represents one forum. Don&#39;t pass instances of this
+ class to templates, therefore there are some other functions
+ in this module.
+
+ The main purpose of this class is the creation and management
+ of forums. You can also use this class for the ACL functions.
+ </span><span class="st st-db">&quot;&quot;&quot;</span>
+
+ <span class="kw">def </span><span class="fun">__init__</span>(<span class="bn bn-pseudo">self</span>, <span class="name">ctx</span>, <span class="name">forum_id</span>):
+ <span class="bn bn-pseudo">self</span>.<span class="name">ctx</span> <span class="op">=</span> <span class="name">ctx</span>
+
+ <span class="bn bn-pseudo">self</span>.<span class="name">forum_id</span> <span class="op">=</span> <span class="name">forum_id</span>
+ <span class="bn">super</span>(<span class="name">Forum</span>, <span class="bn bn-pseudo">self</span>).<span class="name">__init__</span>(<span class="name">ctx</span>, <span class="name">forums</span>, <span class="st st-sg">&#39;</span><span class="st">forum_id</span><span class="st st-sg">&#39;</span>)
+
+ <span class="name">parent_id</span> <span class="op">=</span> <span class="name">lazy_column</span>(<span class="st st-sg">&#39;</span><span class="st">parent_id</span><span class="st st-sg">&#39;</span>)
+ <span class="name">object_id</span> <span class="op">=</span> <span class="name">lazy_column</span>(<span class="st st-sg">&#39;</span><span class="st">object_id</span><span class="st st-sg">&#39;</span>)
+ <span class="name">name</span> <span class="op">=</span> <span class="name">lazy_column</span>(<span class="st st-sg">&#39;</span><span class="st">name</span><span class="st st-sg">&#39;</span>)
+ <span class="name">description</span> <span class="op">=</span> <span class="name">lazy_column</span>(<span class="st st-sg">&#39;</span><span class="st">description</span><span class="st st-sg">&#39;</span>)
+ <span class="name">position</span> <span class="op">=</span> <span class="name">lazy_column</span>(<span class="st st-sg">&#39;</span><span class="st">position</span><span class="st st-sg">&#39;</span>)
+ <span class="name">link</span> <span class="op">=</span> <span class="name">lazy_column</span>(<span class="st st-sg">&#39;</span><span class="st">link</span><span class="st st-sg">&#39;</span>)
+
+ <span class="deco">@staticmethod</span>
+
+ <span class="kw">def </span><span class="fun">create</span>(<span class="name">ctx</span>, <span class="name">name</span>, <span class="name">description</span><span class="op">=</span><span class="st st-db">&quot;&quot;</span>, <span class="name">parent</span><span class="op">=</span><span class="bn bn-pseudo">None</span>, <span class="name">position</span><span class="op">=</span><span class="bn bn-pseudo">None</span>,
+ <span class="name">link</span><span class="op">=</span><span class="bn bn-pseudo">None</span>):
+ <span class="st st-db">&quot;&quot;&quot;</span><span class="st">Create a new forum.</span><span class="st st-db">&quot;&quot;&quot;</span>
+
+ <span class="kw">if</span> <span class="bn">isinstance</span>(<span class="name">parent</span>, <span class="name">Forum</span>):
+ <span class="name">parent</span> <span class="op">=</span> <span class="name">parent</span>.<span class="name">forum_id</span>
+
+ <span class="name">result</span> <span class="op">=</span> <span class="name">ctx</span>.<span class="name">engine</span>.<span class="name">execute</span>(<span class="name">forums</span>.<span class="name">insert</span>(),
+ <span class="name">parent_id</span><span class="op">=</span><span class="name">parent</span>,
+ <span class="name">name</span><span class="op">=</span><span class="name">name</span>,
+ <span class="name">description</span><span class="op">=</span><span class="name">description</span>,
+ <span class="name">position</span><span class="op">=</span><span class="name">position</span>,
+ <span class="name">link</span><span class="op">=</span><span class="name">link</span>,
+ <span class="name">post_count</span><span class="op">=</span><span class="nb nb-int">0</span>,
+ <span class="name">thread_count</span><span class="op">=</span><span class="nb nb-int">0</span>
+
+ )
+ <span class="kw">return</span> <span class="name">Forum</span>(<span class="name">ctx</span>, <span class="name">result</span>.<span class="name">last_inserted_ids</span>()[<span class="nb nb-int">0</span>])
+
+ <span class="kw">def </span><span class="fun">parent_get</span>(<span class="bn bn-pseudo">self</span>):
+ <span class="kw">return</span> <span class="name">Forum</span>(<span class="bn bn-pseudo">self</span>.<span class="name">ctx</span>, <span class="bn bn-pseudo">self</span>.<span class="name">parent_id</span>)
+ <span class="kw">def </span><span class="fun">parent_set</span>(<span class="bn bn-pseudo">self</span>, <span class="name">value</span>):
+ <span class="kw">if</span> <span class="name">value</span> <span class="op op-word">is</span> <span class="bn bn-pseudo">None</span>:
+ <span class="bn bn-pseudo">self</span>.<span class="name">parent_id</span> <span class="op">=</span> <span class="bn bn-pseudo">None</span>
+
+ <span class="kw">if</span> <span class="bn">isinstance</span>(<span class="name">value</span>, <span class="name">Forum</span>):
+ <span class="bn bn-pseudo">self</span>.<span class="name">parent_id</span> <span class="op">=</span> <span class="name">value</span>.<span class="name">forum_id</span>
+
+ <span class="name">parent</span> <span class="op">=</span> <span class="bn">property</span>(<span class="name">parent_get</span>, <span class="name">parent_set</span>)
+ <span class="kw">del</span> <span class="name">parent_get</span>, <span class="name">parent_set</span>
+
+ <span class="kw">def </span><span class="fun">__repr__</span>(<span class="bn bn-pseudo">self</span>):
+ <span class="kw">return</span> <span class="st st-sg">&#39;</span><span class="st">&lt;</span><span class="st st-int">%s</span><span class="st"> </span><span class="st st-int">%d</span><span class="st">: </span><span class="st st-int">%r</span><span class="st">&gt;</span><span class="st st-sg">&#39;</span> <span class="op">%</span> (
+ <span class="bn bn-pseudo">self</span>.<span class="name">__class__</span>.<span class="name">__name__</span>,
+ <span class="bn bn-pseudo">self</span>.<span class="name">forum_id</span>,
+ <span class="bn bn-pseudo">self</span>.<span class="name">name</span>
+
+ )
+
+
+<span class="kw">class </span><span class="cls">Thread</span>(<span class="name">DatabaseModel</span>):
+ <span class="st st-db">&quot;&quot;&quot;</span><span class="st">
+ This class represents a root post with all of its children.
+ You can use this class to manage a thread, add a new reply
+ or edit one of its children.
+ </span><span class="st st-db">&quot;&quot;&quot;</span>
+
+ <span class="kw">def </span><span class="fun">__init__</span>(<span class="bn bn-pseudo">self</span>, <span class="name">ctx</span>, <span class="name">root_post_id</span>):
+ <span class="bn bn-pseudo">self</span>.<span class="name">ctx</span> <span class="op">=</span> <span class="name">ctx</span>
+
+ <span class="bn bn-pseudo">self</span>.<span class="name">post_id</span> <span class="op">=</span> <span class="name">root_post_id</span>
+ <span class="bn">super</span>(<span class="name">Thread</span>, <span class="bn bn-pseudo">self</span>).<span class="name">__init__</span>(<span class="name">ctx</span>, <span class="name">posts</span>, <span class="st st-sg">&#39;</span><span class="st">post_id</span><span class="st st-sg">&#39;</span>,
+ <span class="name">posts</span>.<span class="name">c</span>.<span class="name">parent_id</span> <span class="op">==</span> <span class="bn bn-pseudo">None</span>
+
+ )
+
+ <span class="name">forum_id</span> <span class="op">=</span> <span class="name">lazy_column</span>(<span class="st st-sg">&#39;</span><span class="st">forum_id</span><span class="st st-sg">&#39;</span>)
+ <span class="name">parent_id</span> <span class="op">=</span> <span class="name">lazy_column</span>(<span class="st st-sg">&#39;</span><span class="st">parent_id</span><span class="st st-sg">&#39;</span>)
+ <span class="name">root_post_id</span> <span class="op">=</span> <span class="name">lazy_column</span>(<span class="st st-sg">&#39;</span><span class="st">root_post_id</span><span class="st st-sg">&#39;</span>)
+ <span class="name">object_id</span> <span class="op">=</span> <span class="name">lazy_column</span>(<span class="st st-sg">&#39;</span><span class="st">object_id</span><span class="st st-sg">&#39;</span>)
+ <span class="name">author_id</span> <span class="op">=</span> <span class="name">lazy_column</span>(<span class="st st-sg">&#39;</span><span class="st">author_id</span><span class="st st-sg">&#39;</span>)
+ <span class="name">title</span> <span class="op">=</span> <span class="name">lazy_column</span>(<span class="st st-sg">&#39;</span><span class="st">title</span><span class="st st-sg">&#39;</span>)
+ <span class="name">text</span> <span class="op">=</span> <span class="name">lazy_column</span>(<span class="st st-sg">&#39;</span><span class="st">text</span><span class="st st-sg">&#39;</span>)
+ <span class="name">timestamp</span> <span class="op">=</span> <span class="name">lazy_column</span>(<span class="st st-sg">&#39;</span><span class="st">timestamp</span><span class="st st-sg">&#39;</span>)
+
+ <span class="deco">@staticmethod</span>
+
+ <span class="kw">def </span><span class="fun">create</span>(<span class="name">ctx</span>, <span class="name">forum</span>, <span class="name">author</span>, <span class="name">title</span>, <span class="name">text</span>, <span class="name">timestamp</span><span class="op">=</span><span class="bn bn-pseudo">None</span>):
+ <span class="st st-db">&quot;&quot;&quot;</span><span class="st">Create a new thread.
+ If author is a string it will be an anonymous posting.</span><span class="st st-db">&quot;&quot;&quot;</span>
+
+ <span class="name">username</span> <span class="op">=</span> <span class="bn bn-pseudo">None</span>
+ <span class="kw">if</span> <span class="bn">isinstance</span>(<span class="name">forum</span>, <span class="name">Forum</span>):
+ <span class="name">forum</span> <span class="op">=</span> <span class="name">forum</span>.<span class="name">forum_id</span>
+
+ <span class="kw">if</span> <span class="bn">isinstance</span>(<span class="name">author</span>, <span class="name">User</span>):
+ <span class="name">author</span> <span class="op">=</span> <span class="name">author</span>.<span class="name">user_id</span>
+
+ <span class="kw">elif</span> <span class="bn">isinstance</span>(<span class="name">author</span>, <span class="bn">basestring</span>):
+ <span class="name">username</span> <span class="op">=</span> <span class="name">author</span>
+ <span class="name">author</span> <span class="op">=</span> <span class="name">ANONYMOUS_USER_ID</span>
+
+ <span class="kw">if</span> <span class="name">timestamp</span> <span class="op op-word">is</span> <span class="bn bn-pseudo">None</span>:
+ <span class="name">timestamp</span> <span class="op">=</span> <span class="name">datetime</span>.<span class="name">utcnow</span>()
+ <span class="kw">def </span><span class="fun">do</span>(<span class="name">con</span>):
+ <span class="name">result</span> <span class="op">=</span> <span class="name">con</span>.<span class="name">execute</span>(<span class="name">posts</span>.<span class="name">insert</span>(),
+ <span class="name">forum_id</span> <span class="op">=</span> <span class="name">forum</span>,
+ <span class="name">author_id</span> <span class="op">=</span> <span class="name">author</span>,
+ <span class="name">username</span> <span class="op">=</span> <span class="name">username</span>,
+ <span class="name">title</span> <span class="op">=</span> <span class="name">title</span>,
+ <span class="name">text</span> <span class="op">=</span> <span class="name">text</span>,
+ <span class="name">timestamp</span> <span class="op">=</span> <span class="name">timestamp</span>,
+ <span class="name">post_count</span> <span class="op">=</span> <span class="nb nb-int">1</span>,
+ <span class="name">view_count</span> <span class="op">=</span> <span class="nb nb-int">0</span>
+
+ )
+ <span class="name">thread_id</span> <span class="op">=</span> <span class="name">result</span>.<span class="name">last_inserted_ids</span>()[<span class="op">-</span><span class="nb nb-int">1</span>]
+ <span class="cm"># increment author post count</span>
+ <span class="kw">if</span> <span class="name">author</span> <span class="op">&gt;</span> <span class="op">-</span><span class="nb nb-int">1</span>:
+ <span class="name">old</span> <span class="op">=</span> <span class="name">meta</span>.<span class="name">select</span>([<span class="name">users</span>.<span class="name">c</span>.<span class="name">post_count</span>], <span class="name">users</span>.<span class="name">c</span>.<span class="name">user_id</span> <span class="op">==</span> <span class="name">author</span>)
+ <span class="name">con</span>.<span class="name">execute</span>(<span class="name">users</span>.<span class="name">update</span>(<span class="name">users</span>.<span class="name">c</span>.<span class="name">user_id</span> <span class="op">==</span> <span class="name">author</span>),
+ <span class="name">post_count</span> <span class="op">=</span> <span class="name">con</span>.<span class="name">execute</span>(<span class="name">old</span>).<span class="name">fetchone</span>()[<span class="nb nb-int">0</span>] <span class="op">+</span> <span class="nb nb-int">1</span>
+
+ )
+ <span class="cm"># increment forum post and thread count</span>
+ <span class="name">old</span> <span class="op">=</span> <span class="name">meta</span>.<span class="name">select</span>([<span class="name">forums</span>.<span class="name">c</span>.<span class="name">post_count</span>, <span class="name">forums</span>.<span class="name">c</span>.<span class="name">thread_count</span>],
+ <span class="name">forums</span>.<span class="name">c</span>.<span class="name">forum_id</span> <span class="op">==</span> <span class="name">forum</span>)
+ <span class="name">row</span> <span class="op">=</span> <span class="name">con</span>.<span class="name">execute</span>(<span class="name">old</span>).<span class="name">fetchone</span>()
+ <span class="name">con</span>.<span class="name">execute</span>(<span class="name">forums</span>.<span class="name">update</span>(<span class="name">forums</span>.<span class="name">c</span>.<span class="name">forum_id</span> <span class="op">==</span> <span class="name">forum</span>),
+ <span class="name">post_count</span> <span class="op">=</span> <span class="name">row</span>[<span class="nb nb-int">0</span>] <span class="op">+</span> <span class="nb nb-int">1</span>,
+ <span class="name">thread_count</span> <span class="op">=</span> <span class="name">row</span>[<span class="nb nb-int">1</span>] <span class="op">+</span> <span class="nb nb-int">1</span>,
+ <span class="name">last_post_id</span> <span class="op">=</span> <span class="name">thread_id</span>
+
+ )
+ <span class="kw">return</span> <span class="name">thread_id</span>
+ <span class="name">thread_id</span> <span class="op">=</span> <span class="name">ctx</span>.<span class="name">engine</span>.<span class="name">transaction</span>(<span class="name">do</span>)
+ <span class="cm"># XXX: this feels a bit strange</span>
+
+ <span class="name">t</span> <span class="op">=</span> <span class="name">Thread</span>(<span class="name">ctx</span>, <span class="name">thread_id</span>)
+ <span class="name">t</span>.<span class="name">root_post_id</span> <span class="op">=</span> <span class="name">t</span>.<span class="name">post_id</span>
+
+ <span class="name">t</span>.<span class="name">save</span>()
+ <span class="kw">return</span> <span class="name">t</span>
+
+ <span class="deco">@staticmethod</span>
+ <span class="kw">def </span><span class="fun">by_child</span>(<span class="name">ctx</span>, <span class="name">post_id</span>):
+ <span class="st st-db">&quot;&quot;&quot;</span><span class="st">
+
+ Return the thread of a given ``post_id``.
+ </span><span class="st st-db">&quot;&quot;&quot;</span>
+ <span class="name">result</span> <span class="op">=</span> <span class="name">ctx</span>.<span class="name">engine</span>.<span class="name">execute</span>(<span class="name">meta</span>.<span class="name">select</span>([<span class="name">posts</span>.<span class="name">c</span>.<span class="name">root_post_id</span>],
+ (<span class="name">posts</span>.<span class="name">c</span>.<span class="name">post_id</span> <span class="op">==</span> <span class="name">post_id</span>)
+ ))
+ <span class="name">row</span> <span class="op">=</span> <span class="name">result</span>.<span class="name">fetchone</span>()
+ <span class="kw">if</span> <span class="name">row</span> <span class="op op-word">is</span> <span class="bn bn-pseudo">None</span>:
+ <span class="cm"># XXX: ValueError?</span>
+
+ <span class="kw">raise</span> <span class="exc">ValueError</span>(<span class="st st-sg">&#39;</span><span class="st">post </span><span class="st st-int">%s</span><span class="st"> does not exist</span><span class="st st-sg">&#39;</span> <span class="op">%</span> <span class="name">post_id</span>)
+ <span class="kw">return</span> <span class="name">Thread</span>(<span class="name">ctx</span>, <span class="name">row</span>[<span class="nb nb-int">0</span>])
+
+ <span class="kw">def </span><span class="fun">reply</span>(<span class="bn bn-pseudo">self</span>, <span class="name">post_id</span>, <span class="name">author</span>, <span class="name">title</span>, <span class="name">text</span>, <span class="name">timestamp</span><span class="op">=</span><span class="bn bn-pseudo">None</span>,
+ <span class="name">no_processor</span><span class="op">=</span><span class="bn bn-pseudo">False</span>):
+ <span class="st st-db">&quot;&quot;&quot;</span><span class="st">
+
+ Reply to post ``post_id`` which is a child of the thread.
+ Return the id of the new post.
+
+ If ``author`` is a string it will be an anonymous posting.
+ </span><span class="st st-db">&quot;&quot;&quot;</span>
+ <span class="name">username</span> <span class="op">=</span> <span class="bn bn-pseudo">None</span>
+ <span class="kw">if</span> <span class="name">post_id</span> <span class="op op-word">is</span> <span class="bn bn-pseudo">None</span>:
+ <span class="name">post_id</span> <span class="op">=</span> <span class="bn bn-pseudo">self</span>.<span class="name">post_id</span>
+
+ <span class="kw">if</span> <span class="bn">isinstance</span>(<span class="name">author</span>, <span class="name">User</span>):
+ <span class="name">author</span> <span class="op">=</span> <span class="name">author</span>.<span class="name">user_id</span>
+
+ <span class="kw">elif</span> <span class="bn">isinstance</span>(<span class="name">author</span>, <span class="bn">basestring</span>):
+ <span class="name">username</span> <span class="op">=</span> <span class="name">author</span>
+ <span class="name">author</span> <span class="op">=</span> <span class="name">ANONYMOUS_USER_ID</span>
+
+ <span class="kw">if</span> <span class="name">timestamp</span> <span class="op op-word">is</span> <span class="bn bn-pseudo">None</span>:
+ <span class="name">timestamp</span> <span class="op">=</span> <span class="name">datetime</span>.<span class="name">utcnow</span>()
+ <span class="kw">if</span> <span class="op op-word">not</span> <span class="name">no_processor</span>:
+ <span class="name">text</span>, <span class="name">title</span> <span class="op">=</span> <span class="name">apply_post_processors</span>(<span class="bn bn-pseudo">self</span>.<span class="name">ctx</span>, <span class="name">text</span>,
+ <span class="name">title</span>, <span class="st st-sg">&#39;</span><span class="st">new</span><span class="st st-sg">&#39;</span>)
+
+ <span class="kw">def </span><span class="fun">do</span>(<span class="name">con</span>):
+ <span class="name">result</span> <span class="op">=</span> <span class="name">con</span>.<span class="name">execute</span>(<span class="name">meta</span>.<span class="name">select</span>([<span class="name">posts</span>.<span class="name">c</span>.<span class="name">root_post_id</span>],
+ <span class="name">posts</span>.<span class="name">c</span>.<span class="name">post_id</span> <span class="op">==</span> <span class="name">post_id</span>
+
+ ))
+ <span class="name">row</span> <span class="op">=</span> <span class="name">result</span>.<span class="name">fetchone</span>()
+ <span class="kw">if</span> <span class="name">row</span> <span class="op op-word">is</span> <span class="bn bn-pseudo">None</span> <span class="op op-word">or</span> <span class="name">row</span>[<span class="nb nb-int">0</span>] <span class="op">!=</span> <span class="bn">int</span>(<span class="bn bn-pseudo">self</span>.<span class="name">post_id</span>):
+ <span class="cm"># XXX: ValueError?</span>
+
+ <span class="kw">raise</span> <span class="exc">ValueError</span>(<span class="st st-sg">&#39;</span><span class="st">The post either does not exist or does not </span><span class="st st-sg">&#39;</span>
+ <span class="st st-sg">&#39;</span><span class="st">belong to this thread</span><span class="st st-sg">&#39;</span>)
+ <span class="name">new_post_id</span> <span class="op">=</span> <span class="name">con</span>.<span class="name">execute</span>(<span class="name">posts</span>.<span class="name">insert</span>(),
+ <span class="name">forum_id</span> <span class="op">=</span> <span class="bn bn-pseudo">self</span>.<span class="name">forum_id</span>,
+ <span class="name">parent_id</span> <span class="op">=</span> <span class="name">post_id</span>,
+ <span class="name">root_post_id</span> <span class="op">=</span> <span class="bn bn-pseudo">self</span>.<span class="name">post_id</span>,
+ <span class="name">author_id</span> <span class="op">=</span> <span class="name">author</span>,
+ <span class="name">username</span> <span class="op">=</span> <span class="name">username</span>,
+ <span class="name">title</span> <span class="op">=</span> <span class="name">title</span>,
+ <span class="name">text</span> <span class="op">=</span> <span class="name">text</span>,
+ <span class="name">timestamp</span> <span class="op">=</span> <span class="name">timestamp</span>
+
+ ).<span class="name">last_inserted_ids</span>()[<span class="nb nb-int">0</span>]
+
+ <span class="cm"># increment author post count</span>
+ <span class="kw">if</span> <span class="name">author</span> <span class="op">&gt;</span> <span class="op">-</span><span class="nb nb-int">1</span>:
+ <span class="name">old</span> <span class="op">=</span> <span class="name">meta</span>.<span class="name">select</span>([<span class="name">users</span>.<span class="name">c</span>.<span class="name">post_count</span>], <span class="name">users</span>.<span class="name">c</span>.<span class="name">user_id</span> <span class="op">==</span> <span class="name">author</span>)
+ <span class="name">con</span>.<span class="name">execute</span>(<span class="name">users</span>.<span class="name">update</span>(<span class="name">users</span>.<span class="name">c</span>.<span class="name">user_id</span> <span class="op">==</span> <span class="name">author</span>),
+ <span class="name">post_count</span> <span class="op">=</span> <span class="name">con</span>.<span class="name">execute</span>(<span class="name">old</span>).<span class="name">fetchone</span>()[<span class="nb nb-int">0</span>] <span class="op">+</span> <span class="nb nb-int">1</span>
+
+ )
+ <span class="cm"># increment forum post count and update last_post_id</span>
+ <span class="name">old</span> <span class="op">=</span> <span class="name">meta</span>.<span class="name">select</span>([<span class="name">forums</span>.<span class="name">c</span>.<span class="name">post_count</span>],
+ <span class="name">forums</span>.<span class="name">c</span>.<span class="name">forum_id</span> <span class="op">==</span> <span class="bn bn-pseudo">self</span>.<span class="name">forum_id</span>)
+ <span class="name">con</span>.<span class="name">execute</span>(<span class="name">forums</span>.<span class="name">update</span>(<span class="name">forums</span>.<span class="name">c</span>.<span class="name">forum_id</span> <span class="op">==</span> <span class="bn bn-pseudo">self</span>.<span class="name">forum_id</span>),
+ <span class="name">post_count</span> <span class="op">=</span> <span class="name">con</span>.<span class="name">execute</span>(<span class="name">old</span>).<span class="name">fetchone</span>()[<span class="nb nb-int">0</span>] <span class="op">+</span> <span class="nb nb-int">1</span>,
+ <span class="name">last_post_id</span> <span class="op">=</span> <span class="name">new_post_id</span>
+
+ )
+ <span class="cm"># increment thread post count</span>
+ <span class="name">old</span> <span class="op">=</span> <span class="name">meta</span>.<span class="name">select</span>([<span class="name">posts</span>.<span class="name">c</span>.<span class="name">post_count</span>],
+ <span class="name">posts</span>.<span class="name">c</span>.<span class="name">post_id</span> <span class="op">==</span> <span class="bn bn-pseudo">self</span>.<span class="name">post_id</span>)
+ <span class="name">con</span>.<span class="name">execute</span>(<span class="name">posts</span>.<span class="name">update</span>(<span class="name">posts</span>.<span class="name">c</span>.<span class="name">post_id</span> <span class="op">==</span> <span class="bn bn-pseudo">self</span>.<span class="name">post_id</span>),
+ <span class="name">post_count</span> <span class="op">=</span> <span class="name">con</span>.<span class="name">execute</span>(<span class="name">old</span>).<span class="name">fetchone</span>()[<span class="nb nb-int">0</span>] <span class="op">+</span> <span class="nb nb-int">1</span>
+
+ )
+ <span class="kw">return</span> <span class="name">new_post_id</span>
+ <span class="kw">return</span> <span class="bn bn-pseudo">self</span>.<span class="name">ctx</span>.<span class="name">engine</span>.<span class="name">transaction</span>(<span class="name">do</span>)
+
+ <span class="kw">def </span><span class="fun">edit_reply</span>(<span class="bn bn-pseudo">self</span>, <span class="name">post_id</span>, <span class="name">author</span><span class="op">=</span><span class="name">_missing</span>, <span class="name">title</span><span class="op">=</span><span class="name">_missing</span>,
+ <span class="name">text</span><span class="op">=</span><span class="name">_missing</span>, <span class="name">timestamp</span><span class="op">=</span><span class="name">_missing</span>,
+ <span class="name">no_processor</span><span class="op">=</span><span class="bn bn-pseudo">False</span>):
+ <span class="st st-db">&quot;&quot;&quot;</span><span class="st">Edit a reply.</span><span class="st st-db">&quot;&quot;&quot;</span>
+
+ <span class="name">d</span> <span class="op">=</span> {}
+ <span class="kw">if</span> <span class="name">author</span> <span class="op op-word">is</span> <span class="op op-word">not</span> <span class="name">_missing</span>:
+ <span class="kw">if</span> <span class="bn">isinstance</span>(<span class="name">author</span>, <span class="name">User</span>):
+ <span class="name">d</span>[<span class="st st-sg">&#39;</span><span class="st">author_id</span><span class="st st-sg">&#39;</span>] <span class="op">=</span> <span class="name">author</span>.<span class="name">user_id</span> <span class="cm"># pylint: disable-msg=E1101</span>
+
+ <span class="kw">else</span>:
+ <span class="name">d</span>[<span class="st st-sg">&#39;</span><span class="st">author_id</span><span class="st st-sg">&#39;</span>] <span class="op">=</span> <span class="name">author</span>
+ <span class="kw">if</span> <span class="name">title</span> <span class="op op-word">is</span> <span class="op op-word">not</span> <span class="name">_missing</span>:
+ <span class="name">d</span>[<span class="st st-sg">&#39;</span><span class="st">title</span><span class="st st-sg">&#39;</span>] <span class="op">=</span> <span class="name">title</span>
+
+ <span class="kw">if</span> <span class="name">text</span> <span class="op op-word">is</span> <span class="op op-word">not</span> <span class="name">_missing</span>:
+ <span class="name">d</span>[<span class="st st-sg">&#39;</span><span class="st">text</span><span class="st st-sg">&#39;</span>] <span class="op">=</span> <span class="name">text</span>
+
+ <span class="kw">if</span> <span class="name">timestamp</span> <span class="op op-word">is</span> <span class="op op-word">not</span> <span class="name">_missing</span>:
+ <span class="name">d</span>[<span class="st st-sg">&#39;</span><span class="st">timestamp</span><span class="st st-sg">&#39;</span>] <span class="op">=</span> <span class="name">timestamp</span>
+
+ <span class="kw">if</span> <span class="op op-word">not</span> <span class="name">no_processor</span> <span class="op op-word">and</span> <span class="st st-sg">&#39;</span><span class="st">title</span><span class="st st-sg">&#39;</span> <span class="op op-word">in</span> <span class="name">d</span> <span class="op op-word">and</span> <span class="st st-sg">&#39;</span><span class="st">text</span><span class="st st-sg">&#39;</span> <span class="op op-word">in</span> <span class="name">d</span>:
+ <span class="name">rv</span> <span class="op">=</span> <span class="name">apply_post_processors</span>(<span class="bn bn-pseudo">self</span>.<span class="name">ctx</span>, <span class="name">d</span>[<span class="st st-sg">&#39;</span><span class="st">text</span><span class="st st-sg">&#39;</span>], <span class="name">d</span>[<span class="st st-sg">&#39;</span><span class="st">title</span><span class="st st-sg">&#39;</span>], <span class="st st-sg">&#39;</span><span class="st">edit</span><span class="st st-sg">&#39;</span>)
+ <span class="name">d</span>[<span class="st st-sg">&#39;</span><span class="st">text</span><span class="st st-sg">&#39;</span>], <span class="name">d</span>[<span class="st st-sg">&#39;</span><span class="st">title</span><span class="st st-sg">&#39;</span>] <span class="op">=</span> <span class="name">rv</span>
+
+ <span class="bn bn-pseudo">self</span>.<span class="name">ctx</span>.<span class="name">engine</span>.<span class="name">execute</span>(<span class="name">posts</span>.<span class="name">update</span>(<span class="name">posts</span>.<span class="name">c</span>.<span class="name">post_id</span> <span class="op">==</span> <span class="name">post_id</span>), <span class="op">**</span><span class="name">d</span>)
+
+ <span class="kw">def </span><span class="fun">has_child</span>(<span class="bn bn-pseudo">self</span>, <span class="name">post_id</span>):
+ <span class="st st-db">&quot;&quot;&quot;</span><span class="st">Check if a post_id is a child of this thread.</span><span class="st st-db">&quot;&quot;&quot;</span>
+
+ <span class="name">result</span> <span class="op">=</span> <span class="bn bn-pseudo">self</span>.<span class="name">ctx</span>.<span class="name">engine</span>.<span class="name">execute</span>(<span class="name">meta</span>.<span class="name">select</span>([<span class="name">posts</span>.<span class="name">c</span>.<span class="name">root_post_id</span>],
+ <span class="name">posts</span>.<span class="name">c</span>.<span class="name">post_id</span> <span class="op">==</span> <span class="name">post_id</span>
+
+ ))
+ <span class="name">row</span> <span class="op">=</span> <span class="name">result</span>.<span class="name">fetchone</span>()
+ <span class="kw">return</span> <span class="name">row</span> <span class="op op-word">is</span> <span class="op op-word">not</span> <span class="bn bn-pseudo">None</span> <span class="op op-word">and</span> <span class="name">row</span>[<span class="st st-sg">&#39;</span><span class="st">root_post_id</span><span class="st st-sg">&#39;</span>] <span class="op">==</span> <span class="bn bn-pseudo">self</span>.<span class="name">post_id</span>
+
+ <span class="kw">def </span><span class="fun">get_post_list</span>(<span class="bn bn-pseudo">self</span>):
+ <span class="st st-db">&quot;&quot;&quot;</span><span class="st">
+ Return a flat list of all posts in this thread sorted by their post_id.
+ </span><span class="st st-db">&quot;&quot;&quot;</span>
+ <span class="name">result</span> <span class="op">=</span> <span class="bn bn-pseudo">self</span>.<span class="name">ctx</span>.<span class="name">engine</span>.<span class="name">execute</span>(<span class="name">posts</span>.<span class="name">select</span>(
+ <span class="name">posts</span>.<span class="name">c</span>.<span class="name">root_post_id</span> <span class="op">==</span> <span class="bn bn-pseudo">self</span>.<span class="name">post_id</span>
+
+ ))
+ <span class="kw">return</span> <span class="bn">map</span>(<span class="bn">dict</span>, <span class="name">result</span>)
+
+ <span class="kw">def </span><span class="fun">count_children</span>(<span class="bn bn-pseudo">self</span>):
+ <span class="st st-db">&quot;&quot;&quot;</span><span class="st">
+ Return the number of direct or indirect children of this thread.
+ </span><span class="st st-db">&quot;&quot;&quot;</span>
+
+ <span class="name">p</span> <span class="op">=</span> <span class="name">posts</span>.<span class="name">c</span>
+ <span class="name">result</span> <span class="op">=</span> <span class="bn bn-pseudo">self</span>.<span class="name">ctx</span>.<span class="name">engine</span>.<span class="name">execute</span>(<span class="name">meta</span>.<span class="name">select</span>([<span class="name">meta</span>.<span class="name">func</span>.<span class="name">count</span>(<span class="name">p</span>.<span class="name">post_id</span>)],
+ <span class="name">p</span>.<span class="name">root_post_id</span> <span class="op">==</span> <span class="bn bn-pseudo">self</span>.<span class="name">post_id</span>
+
+ ))
+ <span class="kw">return</span> <span class="name">result</span>.<span class="name">fetchone</span>()[<span class="nb nb-int">0</span>]
+
+ <span class="name">__len__</span> <span class="op">=</span> <span class="name">count_children</span>
+
+ <span class="kw">def </span><span class="fun">forum_get</span>(<span class="bn bn-pseudo">self</span>):
+ <span class="kw">if</span> <span class="bn bn-pseudo">self</span>.<span class="name">forum_id</span> <span class="op op-word">is</span> <span class="op op-word">not</span> <span class="bn bn-pseudo">None</span>:
+ <span class="kw">return</span> <span class="name">Forum</span>(<span class="bn bn-pseudo">self</span>.<span class="name">ctx</span>, <span class="bn bn-pseudo">self</span>.<span class="name">forum_id</span>)
+ <span class="kw">def </span><span class="fun">forum_set</span>(<span class="bn bn-pseudo">self</span>, <span class="name">value</span>):
+ <span class="kw">if</span> <span class="op op-word">not</span> <span class="bn">isinstance</span>(<span class="name">value</span>, <span class="name">Forum</span>):
+ <span class="kw">raise</span> <span class="exc">TypeError</span>(<span class="st st-sg">&#39;</span><span class="st">Can only set Forum instances</span><span class="st st-sg">&#39;</span>)
+ <span class="bn bn-pseudo">self</span>.<span class="name">forum_id</span> <span class="op">=</span> <span class="name">value</span>.<span class="name">forum_id</span>
+
+ <span class="name">forum</span> <span class="op">=</span> <span class="bn">property</span>(<span class="name">forum_get</span>, <span class="name">forum_set</span>)
+ <span class="kw">del</span> <span class="name">forum_get</span>, <span class="name">forum_set</span>
+
+ <span class="kw">def </span><span class="fun">__eq__</span>(<span class="bn bn-pseudo">self</span>, <span class="name">other</span>):
+ <span class="kw">return</span> <span class="bn bn-pseudo">self</span>.<span class="name">post_id</span> <span class="op">==</span> <span class="name">other</span>.<span class="name">post_id</span>
+
+ <span class="kw">def </span><span class="fun">__ne__</span>(<span class="bn bn-pseudo">self</span>, <span class="name">other</span>):
+ <span class="kw">return</span> <span class="op op-word">not</span> <span class="bn bn-pseudo">self</span>.<span class="name">__eq__</span>(<span class="name">other</span>)
+
+ <span class="kw">def </span><span class="fun">__repr__</span>(<span class="bn bn-pseudo">self</span>):
+ <span class="kw">return</span> <span class="st st-sg">&#39;</span><span class="st">&lt;</span><span class="st st-int">%s</span><span class="st"> </span><span class="st st-int">%d</span><span class="st">: </span><span class="st st-int">%r</span><span class="st">&gt;</span><span class="st st-sg">&#39;</span> <span class="op">%</span> (
+ <span class="bn bn-pseudo">self</span>.<span class="name">__class__</span>.<span class="name">__name__</span>,
+ <span class="bn bn-pseudo">self</span>.<span class="name">post_id</span>,
+ <span class="bn bn-pseudo">self</span>.<span class="name">title</span>
+
+ )
+<span class="cm"># -*- coding: utf-8 -*-</span>
+<span class="st st-db">&quot;&quot;&quot;</span><span class="st">
+ pocoo.pkg.core.i18n
+ ~~~~~~~~~~~~~~~~~~~
+
+ Pocoo internationalization components.
+
+ :copyright: 2006 by Armin Ronacher.
+ :license: GNU GPL, see LICENSE for more details.
+</span><span class="st st-db">&quot;&quot;&quot;</span>
+<span class="kw">import </span><span class="cls">gettext</span>
+<span class="kw">from </span><span class="cls">pocoo.application</span><span class="kw"> import</span> <span class="name">RequestWrapper</span>
+
+<span class="kw">from </span><span class="cls">jinja.nodes</span><span class="kw"> import</span> <span class="name">Node</span>, <span class="name">KeywordNode</span>, <span class="name">VariableNode</span>, <span class="name">ValueNode</span>, <span class="name">CollectionNode</span>
+<span class="kw">from </span><span class="cls">jinja.base</span><span class="kw"> import</span> <span class="name">TOKEN_TEXT</span>, <span class="name">TOKEN_VAR</span>
+
+<span class="kw">from </span><span class="cls">jinja.exceptions</span><span class="kw"> import</span> <span class="name">TemplateSyntaxError</span>
+<span class="kw">from </span><span class="cls">cStringIO</span><span class="kw"> import</span> <span class="name">StringIO</span>
+
+
+<span class="kw">class </span><span class="cls">TranslatableTag</span>(<span class="name">Node</span>):
+ <span class="st st-db">&quot;&quot;&quot;</span><span class="st">
+
+ Translatable Tag
+ ================
+
+ Usage::
+
+ {% trans %}
+ somestring
+ {% trans %}
+
+ Or::
+
+ {% trans &quot;string&quot; %}
+ </span><span class="st st-db">&quot;&quot;&quot;</span>
+
+ <span class="name">rules</span> <span class="op">=</span> {
+ <span class="st st-sg">&#39;</span><span class="st">long</span><span class="st st-sg">&#39;</span>: [<span class="name">KeywordNode</span>(<span class="st st-sg">&#39;</span><span class="st">trans</span><span class="st st-sg">&#39;</span>)],
+ <span class="st st-sg">&#39;</span><span class="st">plural</span><span class="st st-sg">&#39;</span>: [<span class="name">KeywordNode</span>(<span class="st st-sg">&#39;</span><span class="st">trans</span><span class="st st-sg">&#39;</span>), <span class="name">KeywordNode</span>(<span class="st st-sg">&#39;</span><span class="st">pluralizing</span><span class="st st-sg">&#39;</span>),
+ <span class="name">VariableNode</span>()],
+ <span class="st st-sg">&#39;</span><span class="st">short</span><span class="st st-sg">&#39;</span>: [<span class="name">KeywordNode</span>(<span class="st st-sg">&#39;</span><span class="st">trans</span><span class="st st-sg">&#39;</span>), <span class="name">ValueNode</span>(), <span class="name">CollectionNode</span>()]
+ }
+
+ <span class="kw">def </span><span class="fun">__init__</span>(<span class="bn bn-pseudo">self</span>, <span class="name">parser</span>, <span class="name">matched_tag</span>, <span class="name">handler_args</span>, <span class="name">stack</span>):
+ <span class="bn bn-pseudo">self</span>.<span class="name">_body_pl</span> <span class="op">=</span> []
+ <span class="bn bn-pseudo">self</span>.<span class="name">_vars_pl</span> <span class="op">=</span> {}
+ <span class="kw">if</span> <span class="name">matched_tag</span> <span class="op">==</span> <span class="st st-sg">&#39;</span><span class="st">short</span><span class="st st-sg">&#39;</span>:
+ <span class="bn bn-pseudo">self</span>.<span class="name">_body_sg</span> <span class="op">=</span> [<span class="name">handler_args</span>[<span class="nb nb-int">1</span>].<span class="name">resolve</span>()]
+ <span class="bn bn-pseudo">self</span>.<span class="name">_vars_sg</span> <span class="op">=</span> <span class="name">handler_args</span>[<span class="nb nb-int">2</span>]
+ <span class="kw">else</span>:
+ <span class="bn bn-pseudo">self</span>.<span class="name">_body_sg</span>, <span class="bn bn-pseudo">self</span>.<span class="name">_vars_sg</span>, <span class="bn bn-pseudo">self</span>.<span class="name">_body_pl</span>, <span class="bn bn-pseudo">self</span>.<span class="name">_vars_pl</span> <span class="op">=</span> \
+ <span class="bn bn-pseudo">self</span>.<span class="name">_forkparse</span>(<span class="name">parser</span>)
+ <span class="kw">if</span> <span class="name">matched_tag</span> <span class="op">==</span> <span class="st st-sg">&#39;</span><span class="st">plural</span><span class="st st-sg">&#39;</span>:
+ <span class="bn bn-pseudo">self</span>.<span class="name">_indicator</span> <span class="op">=</span> <span class="name">handler_args</span>[<span class="nb nb-int">2</span>]
+ <span class="bn bn-pseudo">self</span>.<span class="name">msgid</span> <span class="op">=</span> (<span class="st st-sg">&#39;&#39;</span>.<span class="name">join</span>(<span class="bn bn-pseudo">self</span>.<span class="name">_body_sg</span>)).<span class="name">strip</span>()
+ <span class="bn bn-pseudo">self</span>.<span class="name">msgid_plural</span> <span class="op">=</span> (<span class="st st-sg">&#39;&#39;</span>.<span class="name">join</span>(<span class="bn bn-pseudo">self</span>.<span class="name">_body_pl</span>)).<span class="name">strip</span>()
+ <span class="name">Node</span>.<span class="name">__init__</span>(<span class="bn bn-pseudo">self</span>)
+
+ <span class="kw">def </span><span class="fun">_forkparse</span>(<span class="bn bn-pseudo">self</span>, <span class="name">parser</span>):
+ <span class="name">lib</span> <span class="op">=</span> <span class="name">parser</span>.<span class="name">library</span>
+
+ <span class="name">sg</span> <span class="op">=</span> []; <span class="name">vars_sg</span> <span class="op">=</span> {}
+ <span class="name">pl</span> <span class="op">=</span> []; <span class="name">vars_pl</span> <span class="op">=</span> {}
+ <span class="name">out</span> <span class="op">=</span> <span class="name">sg</span>; <span class="name">vars_out</span> <span class="op">=</span> <span class="name">vars_sg</span>
+
+ <span class="kw">while</span> <span class="name">parser</span>.<span class="name">tokens</span>:
+ <span class="name">token</span> <span class="op">=</span> <span class="name">parser</span>.<span class="name">pop_token</span>()
+ <span class="kw">if</span> <span class="name">token</span>.<span class="name">token_type</span> <span class="op">==</span> <span class="name">TOKEN_VAR</span>:
+ <span class="name">var</span> <span class="op">=</span> <span class="name">lib</span>.<span class="name">parse</span>(<span class="name">parser</span>, <span class="name">u</span><span class="st st-sg">&#39;</span><span class="st">print </span><span class="st st-int">%s</span><span class="st st-sg">&#39;</span> <span class="op">%</span> <span class="name">token</span>.<span class="name">contents</span>)
+ <span class="name">var_key</span> <span class="op">=</span> <span class="name">token</span>.<span class="name">contents</span>.<span class="name">split</span>(<span class="st st-sg">&#39;</span><span class="st">|</span><span class="st st-sg">&#39;</span>)[<span class="nb nb-int">0</span>].<span class="name">strip</span>()
+ <span class="name">vars_out</span>[<span class="name">var_key</span>] <span class="op">=</span> <span class="name">var</span>
+
+ <span class="name">out</span>.<span class="name">append</span>(<span class="st st-sg">&#39;</span><span class="st st-int">%%</span><span class="st">(</span><span class="st st-int">%s</span><span class="st">)s</span><span class="st st-sg">&#39;</span> <span class="op">%</span> <span class="name">var_key</span>)
+ <span class="kw">elif</span> <span class="name">token</span>.<span class="name">token_type</span> <span class="op">==</span> <span class="name">TOKEN_TEXT</span>:
+ <span class="name">lines</span> <span class="op">=</span> <span class="name">token</span>.<span class="name">contents</span>.<span class="name">splitlines</span>()
+ <span class="cm"># XXX: this protects whitespaces between different tokens (really?)</span>
+
+ <span class="name">text</span> <span class="op">=</span> <span class="name">u</span><span class="st st-sg">&#39;</span><span class="st st-esc">\n</span><span class="st st-sg">&#39;</span>.<span class="name">join</span>(<span class="name">lines</span>[:<span class="nb nb-int">1</span>] <span class="op">+</span> [<span class="name">line</span>.<span class="name">lstrip</span>() <span class="kw">for</span> <span class="name">line</span> <span class="op op-word">in</span> <span class="name">lines</span>[<span class="nb nb-int">1</span>:]])
+ <span class="name">out</span>.<span class="name">append</span>(<span class="name">text</span>)
+ <span class="kw">else</span>:
+ <span class="kw">if</span> <span class="name">token</span>.<span class="name">contents</span> <span class="op">==</span> <span class="st st-sg">&#39;</span><span class="st">plural</span><span class="st st-sg">&#39;</span>:
+ <span class="kw">if</span> <span class="name">out</span> <span class="op op-word">is</span> <span class="name">sg</span>:
+ <span class="name">out</span> <span class="op">=</span> <span class="name">pl</span>
+
+ <span class="name">vars_out</span> <span class="op">=</span> <span class="name">vars_pl</span>
+ <span class="kw">else</span>:
+ <span class="kw">raise</span> <span class="name">TemplateSyntaxError</span>(<span class="st st-sg">&#39;</span><span class="st">plural used twice</span><span class="st st-sg">&#39;</span>)
+ <span class="kw">elif</span> <span class="name">token</span>.<span class="name">contents</span> <span class="op">==</span> <span class="st st-sg">&#39;</span><span class="st">endtrans</span><span class="st st-sg">&#39;</span>:
+ <span class="kw">break</span>
+
+ <span class="kw">else</span>:
+ <span class="kw">raise</span> <span class="name">TemplateSyntaxError</span>(<span class="st st-sg">&#39;</span><span class="st">you can</span><span class="st st-esc">\&#39;</span><span class="st">t use blocks inside of a </span><span class="st st-sg">&#39;</span>
+ <span class="st st-sg">&#39;</span><span class="st">`trans` tag.</span><span class="st st-sg">&#39;</span>)
+ <span class="kw">return</span> <span class="name">sg</span>, <span class="name">vars_sg</span>, <span class="name">pl</span>, <span class="name">vars_pl</span>
+
+ <span class="kw">def </span><span class="fun">render</span>(<span class="bn bn-pseudo">self</span>, <span class="name">context</span>):
+ <span class="name">req</span> <span class="op">=</span> <span class="name">context</span>[<span class="st st-sg">&#39;</span><span class="st">REQUEST</span><span class="st st-sg">&#39;</span>]
+ <span class="kw">if</span> <span class="bn bn-pseudo">self</span>.<span class="name">msgid_plural</span>:
+ <span class="name">var</span> <span class="op">=</span> <span class="nb nb-int">1</span>
+
+ <span class="kw">if</span> <span class="bn">hasattr</span>(<span class="bn bn-pseudo">self</span>, <span class="st st-sg">&#39;</span><span class="st">_indicator</span><span class="st st-sg">&#39;</span>):
+ <span class="name">var</span> <span class="op">=</span> <span class="bn bn-pseudo">self</span>.<span class="name">_indicator</span>.<span class="name">resolve</span>(<span class="name">context</span>)
+ <span class="name">rv</span> <span class="op">=</span> <span class="name">req</span>.<span class="name">gettext</span>(<span class="bn bn-pseudo">self</span>.<span class="name">msgid</span>, <span class="bn bn-pseudo">self</span>.<span class="name">msgid_plural</span>, <span class="name">var</span>)
+ <span class="kw">else</span>:
+ <span class="name">rv</span> <span class="op">=</span> <span class="name">req</span>.<span class="name">gettext</span>(<span class="bn bn-pseudo">self</span>.<span class="name">msgid</span>)
+ <span class="kw">if</span> <span class="bn">isinstance</span>(<span class="bn bn-pseudo">self</span>.<span class="name">_vars_sg</span>, <span class="bn">list</span>):
+ <span class="kw">return</span> <span class="name">rv</span> <span class="op">%</span> <span class="bn">tuple</span>(<span class="name">v</span>.<span class="name">render</span>(<span class="name">context</span>) <span class="kw">for</span> <span class="name">v</span> <span class="op op-word">in</span> <span class="bn bn-pseudo">self</span>.<span class="name">_vars_sg</span>)
+ <span class="name">args</span> <span class="op">=</span> <span class="bn">dict</span>((<span class="name">n</span>, <span class="name">v</span>.<span class="name">render</span>(<span class="name">context</span>)) <span class="kw">for</span> <span class="name">n</span>, <span class="name">v</span> <span class="op op-word">in</span> <span class="bn bn-pseudo">self</span>.<span class="name">_vars_sg</span>.<span class="name">iteritems</span>())
+ <span class="name">args</span>.<span class="name">update</span>(<span class="bn bn-pseudo">self</span>.<span class="name">_vars_pl</span>)
+ <span class="kw">return</span> <span class="name">rv</span> <span class="op">%</span> <span class="name">args</span>
+
+
+<span class="kw">def </span><span class="fun">load_translations</span>(<span class="name">ctx</span>, <span class="name">lng</span>):
+ <span class="st st-db">&quot;&quot;&quot;</span><span class="st">
+ loads all available translations for the given language.
+ </span><span class="st st-db">&quot;&quot;&quot;</span>
+ <span class="name">result</span> <span class="op">=</span> []
+ <span class="kw">for</span> <span class="name">res</span> <span class="op op-word">in</span> <span class="name">ctx</span>.<span class="name">pkgmanager</span>.<span class="name">get_resources</span>(<span class="st st-sg">&#39;</span><span class="st">i18n</span><span class="st st-sg">&#39;</span>, <span class="st st-sg">&#39;&#39;</span>, <span class="name">lng</span>):
+ <span class="name">f</span> <span class="op">=</span> <span class="name">StringIO</span>(<span class="name">res</span>)
+ <span class="name">result</span>.<span class="name">append</span>(<span class="name">gettext</span>.<span class="name">GNUTranslations</span>(<span class="name">f</span>))
+ <span class="kw">return</span> <span class="name">result</span>
+
+
+<span class="kw">def </span><span class="fun">parse_http_accept_language</span>(<span class="name">s</span>):
+ <span class="st st-db">&quot;&quot;&quot;</span><span class="st">
+ Return the accepted languages as set in the user browser.
+ </span><span class="st st-db">&quot;&quot;&quot;</span>
+ <span class="name">result</span> <span class="op">=</span> []
+ <span class="kw">for</span> <span class="name">item</span> <span class="op op-word">in</span> <span class="name">s</span>.<span class="name">split</span>(<span class="st st-sg">&#39;</span><span class="st">,</span><span class="st st-sg">&#39;</span>):
+ <span class="name">lng</span> <span class="op">=</span> <span class="name">item</span>.<span class="name">split</span>(<span class="st st-sg">&#39;</span><span class="st">;</span><span class="st st-sg">&#39;</span>, <span class="nb nb-int">1</span>)[<span class="nb nb-int">0</span>]
+ <span class="name">lng</span> <span class="op">=</span> <span class="name">lng</span>.<span class="name">lower</span>()
+ <span class="name">result</span>.<span class="name">append</span>(<span class="name">lng</span>)
+ <span class="kw">if</span> <span class="st st-sg">&#39;</span><span class="st">-</span><span class="st st-sg">&#39;</span> <span class="op op-word">in</span> <span class="name">lng</span>:
+ <span class="name">result</span>.<span class="name">append</span>(<span class="name">lng</span>.<span class="name">split</span>(<span class="st st-sg">&#39;</span><span class="st">-</span><span class="st st-sg">&#39;</span>)[<span class="nb nb-int">0</span>])
+ <span class="kw">return</span> <span class="name">result</span>
+
+
+<span class="kw">def </span><span class="fun">get_request_languages</span>(<span class="name">req</span>):
+ <span class="st st-db">&quot;&quot;&quot;</span><span class="st">
+ Return the list of languages a request could handle.
+ </span><span class="st st-db">&quot;&quot;&quot;</span>
+ <span class="kw">if</span> <span class="bn">hasattr</span>(<span class="name">req</span>, <span class="st st-sg">&#39;</span><span class="st">user</span><span class="st st-sg">&#39;</span>) <span class="op op-word">and</span> <span class="name">req</span>.<span class="name">user</span> <span class="op op-word">is</span> <span class="op op-word">not</span> <span class="bn bn-pseudo">None</span> \
+ <span class="op op-word">and</span> <span class="name">req</span>.<span class="name">user</span>.<span class="name">language</span>:
+ <span class="name">lng</span> <span class="op">=</span> [<span class="name">req</span>.<span class="name">user</span>.<span class="name">language</span>]
+ <span class="kw">else</span>:
+ <span class="name">lng</span> <span class="op">=</span> []
+ <span class="cm"># before checking the HTTP_ACCEPT_LANGUAGE check for</span>
+
+ <span class="cm"># a forced language.</span>
+ <span class="name">forced</span> <span class="op">=</span> <span class="name">req</span>.<span class="name">ctx</span>.<span class="name">cfg</span>.<span class="name">get</span>(<span class="st st-sg">&#39;</span><span class="st">general</span><span class="st st-sg">&#39;</span>, <span class="st st-sg">&#39;</span><span class="st">language</span><span class="st st-sg">&#39;</span>, <span class="st st-sg">&#39;&#39;</span>)
+ <span class="kw">if</span> <span class="name">forced</span> <span class="op op-word">and</span> <span class="name">forced</span> <span class="op">!=</span> <span class="st st-sg">&#39;</span><span class="st">auto</span><span class="st st-sg">&#39;</span> <span class="op op-word">and</span> <span class="name">forced</span> <span class="op op-word">not</span> <span class="op op-word">in</span> <span class="name">lng</span>:
+ <span class="name">lng</span>.<span class="name">append</span>(<span class="name">forced</span>)
+ <span class="cm"># now prase the HTTP_ACCEPT_LANGUAGE string and add</span>
+
+ <span class="cm"># the languages to the list of accepted languages.</span>
+ <span class="name">language_string</span> <span class="op">=</span> <span class="name">req</span>.<span class="name">environ</span>.<span class="name">get</span>(<span class="st st-sg">&#39;</span><span class="st">HTTP_ACCEPT_LANGUAGE</span><span class="st st-sg">&#39;</span>, <span class="st st-sg">&#39;</span><span class="st">en</span><span class="st st-sg">&#39;</span>)
+ <span class="kw">for</span> <span class="name">item</span> <span class="op op-word">in</span> <span class="name">parse_http_accept_language</span>(<span class="name">language_string</span>):
+ <span class="kw">if</span> <span class="name">item</span> <span class="op op-word">not</span> <span class="op op-word">in</span> <span class="name">lng</span>:
+ <span class="name">lng</span>.<span class="name">append</span>(<span class="name">item</span>)
+ <span class="cm"># add &quot;en&quot; if not set</span>
+
+ <span class="kw">if</span> <span class="st st-sg">&#39;</span><span class="st">en</span><span class="st st-sg">&#39;</span> <span class="op op-word">not</span> <span class="op op-word">in</span> <span class="name">lng</span>:
+ <span class="name">lng</span>.<span class="name">append</span>(<span class="st st-sg">&#39;</span><span class="st">en</span><span class="st st-sg">&#39;</span>)
+ <span class="kw">return</span> <span class="name">lng</span>
+
+
+<span class="kw">class </span><span class="cls">Translator</span>(<span class="bn">object</span>):
+ <span class="st st-db">&quot;&quot;&quot;</span><span class="st">
+ A callable that allows you to use pluralized and
+ non pluralized strings. A translator instance always
+ exists on the request object as ``req.gettext``::
+
+ _ = req.gettext
+ _(&#39;Hello World!&#39;, &#39;Hello Worlds!&#39;, 2)
+
+ The example above defines a singular and pluralized
+ version of &quot;Hello World&quot;. The number two tells the
+ gettext system that we have two worlds in that case.
+ Some languages provide more than just one plural form
+ so this number allows it to decide which plural form
+ to use.
+
+ If you just have a singular string you can use this::
+
+ _ = req.gettext
+ _(&#39;This is just a small example&#39;)
+
+ Strings marked as ``_()`` automagically get tracked
+ by the gettext translation generator script.
+ </span><span class="st st-db">&quot;&quot;&quot;</span>
+
+ <span class="kw">def </span><span class="fun">__init__</span>(<span class="bn bn-pseudo">self</span>, <span class="name">translations</span>, <span class="name">languages</span>):
+ <span class="cm">#XXX: should we cache that?</span>
+ <span class="bn bn-pseudo">self</span>.<span class="name">translator</span> <span class="op">=</span> <span class="name">gettext</span>.<span class="name">NullTranslations</span>()
+ <span class="kw">for</span> <span class="name">lng</span> <span class="op op-word">in</span> <span class="name">languages</span>:
+ <span class="kw">for</span> <span class="name">translation</span> <span class="op op-word">in</span> <span class="name">translations</span>[<span class="name">lng</span>]:
+ <span class="bn bn-pseudo">self</span>.<span class="name">translator</span>.<span class="name">add_fallback</span>(<span class="name">translation</span>)
+ <span class="bn bn-pseudo">self</span>.<span class="name">languages</span> <span class="op">=</span> <span class="name">languages</span>
+
+ <span class="kw">def </span><span class="fun">__call__</span>(<span class="bn bn-pseudo">self</span>, <span class="name">msg</span>, <span class="name">plural</span><span class="op">=</span><span class="bn bn-pseudo">None</span>, <span class="name">n</span><span class="op">=</span><span class="nb nb-int">1</span>):
+ <span class="kw">if</span> <span class="name">plural</span> <span class="op op-word">is</span> <span class="bn bn-pseudo">None</span>:
+ <span class="kw">return</span> <span class="bn bn-pseudo">self</span>.<span class="name">translator</span>.<span class="name">ugettext</span>(<span class="name">msg</span>)
+ <span class="kw">return</span> <span class="bn bn-pseudo">self</span>.<span class="name">translator</span>.<span class="name">ungettext</span>(<span class="name">msg</span>, <span class="name">plural</span>, <span class="name">n</span>)
+
+ <span class="kw">def </span><span class="fun">__repr__</span>(<span class="bn bn-pseudo">self</span>):
+ <span class="kw">return</span> <span class="st st-sg">&#39;</span><span class="st">&lt;</span><span class="st st-int">%s</span><span class="st"> </span><span class="st st-int">%r</span><span class="st">&gt;</span><span class="st st-sg">&#39;</span> <span class="op">%</span> (
+ <span class="bn bn-pseudo">self</span>.<span class="name">__class__</span>.<span class="name">__name__</span>,
+ <span class="bn bn-pseudo">self</span>.<span class="name">languages</span>
+
+ )
+
+
+<span class="name">dummy_translate</span> <span class="op">=</span> <span class="name">Translator</span>({}, [])
+
+
+<span class="kw">class </span><span class="cls">I18nWrapper</span>(<span class="name">RequestWrapper</span>):
+
+ <span class="kw">def </span><span class="fun">__init__</span>(<span class="bn bn-pseudo">self</span>, <span class="name">ctx</span>):
+ <span class="bn">super</span>(<span class="name">I18nWrapper</span>, <span class="bn bn-pseudo">self</span>).<span class="name">__init__</span>(<span class="name">ctx</span>)
+ <span class="bn bn-pseudo">self</span>.<span class="name">translations</span> <span class="op">=</span> {}
+
+ <span class="kw">def </span><span class="fun">get_priority</span>(<span class="bn bn-pseudo">self</span>):
+ <span class="kw">return</span> <span class="nb nb-int">4</span>
+
+ <span class="kw">def </span><span class="fun">process_request</span>(<span class="bn bn-pseudo">self</span>, <span class="name">req</span>):
+ <span class="st st-db">&quot;&quot;&quot;</span><span class="st">Attach a gettext and dummy translate method to the request.</span><span class="st st-db">&quot;&quot;&quot;</span>
+ <span class="name">languages</span> <span class="op">=</span> <span class="name">get_request_languages</span>(<span class="name">req</span>)
+ <span class="name">req</span>.<span class="name">accept_languages</span> <span class="op">=</span> <span class="name">languages</span>
+
+ <span class="kw">for</span> <span class="name">lng</span> <span class="op op-word">in</span> <span class="name">languages</span>:
+ <span class="kw">if</span> <span class="name">lng</span> <span class="op op-word">not</span> <span class="op op-word">in</span> <span class="bn bn-pseudo">self</span>.<span class="name">translations</span>:
+ <span class="bn bn-pseudo">self</span>.<span class="name">translations</span>[<span class="name">lng</span>] <span class="op">=</span> <span class="name">load_translations</span>(<span class="bn bn-pseudo">self</span>.<span class="name">ctx</span>, <span class="name">lng</span>)
+ <span class="name">req</span>.<span class="name">gettext</span> <span class="op">=</span> <span class="name">Translator</span>(<span class="bn bn-pseudo">self</span>.<span class="name">translations</span>, <span class="name">languages</span>)
+ <span class="name">req</span>.<span class="name">dummy_translate</span> <span class="op">=</span> <span class="name">dummy_translate</span>
+
+ <span class="kw">def </span><span class="fun">process_response</span>(<span class="bn bn-pseudo">self</span>, <span class="name">req</span>, <span class="name">resp</span>):
+ <span class="kw">return</span> <span class="name">resp</span>
+<span class="cm"># -*- coding: utf-8 -*-</span>
+
+<span class="st st-db">&quot;&quot;&quot;</span><span class="st">
+ pocoo.pkg.core
+ ~~~~~~~~~~~~~~
+
+ The Pocoo core component.
+
+ :copyright: 2006 by the Pocoo team.
+ :license: GNU GPL, see LICENSE for more details.
+</span><span class="st st-db">&quot;&quot;&quot;</span>
+<span class="cm"># -*- coding: utf-8 -*-</span>
+<span class="st st-db">&quot;&quot;&quot;</span><span class="st">
+ pocoo.pkg.core.l10n
+ ~~~~~~~~~~~~~~~~~~~
+
+ Pocoo localisation module.
+
+ :copyright: 2006 by Armin Ronacher.
+ :license: GNU GPL, see LICENSE for more details.
+</span><span class="st st-db">&quot;&quot;&quot;</span>
+<span class="kw">from </span><span class="cls">pocoo.utils.text</span><span class="kw"> import</span> <span class="name">split_format</span>
+
+<span class="kw">from </span><span class="cls">datetime</span><span class="kw"> import</span> <span class="name">datetime</span>
+<span class="kw">import </span><span class="cls">time</span>
+<span class="kw">from </span><span class="cls">calendar</span><span class="kw"> import</span> <span class="name">monthrange</span>
+
+<span class="cm"># Dateformat Constants</span>
+<span class="name">DEFAULT_DATE_FORMAT</span> <span class="op">=</span> <span class="st st-sg">&#39;</span><span class="st">%a, </span><span class="st st-int">%d</span><span class="st"> %b %Y</span><span class="st st-sg">&#39;</span>
+<span class="name">DEFAULT_TIME_FORMAT</span> <span class="op">=</span> <span class="st st-sg">&#39;</span><span class="st">%H:%M</span><span class="st st-sg">&#39;</span>
+
+<span class="name">DEFAULT_DATETIME_FORMAT</span> <span class="op">=</span> <span class="st st-sg">&#39;</span><span class="st">%a, </span><span class="st st-int">%d</span><span class="st"> %b %Y %H:%M</span><span class="st st-sg">&#39;</span>
+
+<span class="cm"># Timedelta Constants</span>
+<span class="name">TIME_DELTA_UNITS</span> <span class="op">=</span> [
+ (<span class="nb nb-int">3600</span> <span class="op">*</span> <span class="nb nb-int">24</span> <span class="op">*</span> <span class="nb nb-int">365</span>, <span class="st st-sg">&#39;</span><span class="st">y</span><span class="st st-sg">&#39;</span>),
+ (<span class="nb nb-int">3600</span> <span class="op">*</span> <span class="nb nb-int">24</span> <span class="op">*</span> <span class="nb nb-int">30</span>, <span class="st st-sg">&#39;</span><span class="st">M</span><span class="st st-sg">&#39;</span>),
+ (<span class="nb nb-int">3600</span> <span class="op">*</span> <span class="nb nb-int">24</span> <span class="op">*</span> <span class="nb nb-int">7</span>, <span class="st st-sg">&#39;</span><span class="st">w</span><span class="st st-sg">&#39;</span>),
+ (<span class="nb nb-int">3600</span> <span class="op">*</span> <span class="nb nb-int">24</span>, <span class="st st-sg">&#39;</span><span class="st">d</span><span class="st st-sg">&#39;</span>),
+ (<span class="nb nb-int">3600</span>, <span class="st st-sg">&#39;</span><span class="st">h</span><span class="st st-sg">&#39;</span>),
+ (<span class="nb nb-int">60</span>, <span class="st st-sg">&#39;</span><span class="st">m</span><span class="st st-sg">&#39;</span>)
+]
+
+
+<span class="cm"># Gettext Helper</span>
+<span class="name">_</span> <span class="op">=</span> <span class="kw">lambda</span> <span class="name">x</span>: <span class="name">x</span>
+
+<span class="kw">class </span><span class="cls">DateFormatter</span>(<span class="bn">object</span>):
+ <span class="st st-db">&quot;&quot;&quot;</span><span class="st">
+
+ TODO: write documentation about the various format codes.
+ </span><span class="st st-db">&quot;&quot;&quot;</span>
+ <span class="cm"># XXX: use &quot;caching&quot; of lists with static strings (weekdays...)</span>
+
+ <span class="cm"># allow format_X method names</span>
+ <span class="cm"># pylint: disable-msg=C0103</span>
+
+ <span class="name">WEEKDAYS_ABBR</span> <span class="op">=</span> [<span class="name">_</span>(<span class="st st-sg">&#39;</span><span class="st">Mon</span><span class="st st-sg">&#39;</span>), <span class="name">_</span>(<span class="st st-sg">&#39;</span><span class="st">Tue</span><span class="st st-sg">&#39;</span>), <span class="name">_</span>(<span class="st st-sg">&#39;</span><span class="st">Wed</span><span class="st st-sg">&#39;</span>), <span class="name">_</span>(<span class="st st-sg">&#39;</span><span class="st">Thu</span><span class="st st-sg">&#39;</span>), <span class="name">_</span>(<span class="st st-sg">&#39;</span><span class="st">Fri</span><span class="st st-sg">&#39;</span>),
+ <span class="name">_</span>(<span class="st st-sg">&#39;</span><span class="st">Sat</span><span class="st st-sg">&#39;</span>), <span class="name">_</span>(<span class="st st-sg">&#39;</span><span class="st">Sun</span><span class="st st-sg">&#39;</span>)]
+ <span class="name">WEEKDAYS_FULL</span> <span class="op">=</span> [<span class="name">_</span>(<span class="st st-sg">&#39;</span><span class="st">Monday</span><span class="st st-sg">&#39;</span>), <span class="name">_</span>(<span class="st st-sg">&#39;</span><span class="st">Tuesday</span><span class="st st-sg">&#39;</span>), <span class="name">_</span>(<span class="st st-sg">&#39;</span><span class="st">Wednesday</span><span class="st st-sg">&#39;</span>),
+ <span class="name">_</span>(<span class="st st-sg">&#39;</span><span class="st">Thursday</span><span class="st st-sg">&#39;</span>), <span class="name">_</span>(<span class="st st-sg">&#39;</span><span class="st">Friday</span><span class="st st-sg">&#39;</span>), <span class="name">_</span>(<span class="st st-sg">&#39;</span><span class="st">Saturday</span><span class="st st-sg">&#39;</span>),
+ <span class="name">_</span>(<span class="st st-sg">&#39;</span><span class="st">Sunday</span><span class="st st-sg">&#39;</span>)]
+ <span class="name">MONTHS_ABBR</span> <span class="op">=</span> [<span class="name">_</span>(<span class="st st-sg">&#39;</span><span class="st">Jan</span><span class="st st-sg">&#39;</span>), <span class="name">_</span>(<span class="st st-sg">&#39;</span><span class="st">Feb</span><span class="st st-sg">&#39;</span>), <span class="name">_</span>(<span class="st st-sg">&#39;</span><span class="st">Mar</span><span class="st st-sg">&#39;</span>), <span class="name">_</span>(<span class="st st-sg">&#39;</span><span class="st">Apr</span><span class="st st-sg">&#39;</span>), <span class="name">_</span>(<span class="st st-sg">&#39;</span><span class="st">May:abbr</span><span class="st st-sg">&#39;</span>),
+ <span class="name">_</span>(<span class="st st-sg">&#39;</span><span class="st">Jun</span><span class="st st-sg">&#39;</span>), <span class="name">_</span>(<span class="st st-sg">&#39;</span><span class="st">Jul</span><span class="st st-sg">&#39;</span>), <span class="name">_</span>(<span class="st st-sg">&#39;</span><span class="st">Aug</span><span class="st st-sg">&#39;</span>), <span class="name">_</span>(<span class="st st-sg">&#39;</span><span class="st">Sep</span><span class="st st-sg">&#39;</span>), <span class="name">_</span>(<span class="st st-sg">&#39;</span><span class="st">Oct</span><span class="st st-sg">&#39;</span>),
+ <span class="name">_</span>(<span class="st st-sg">&#39;</span><span class="st">Nov</span><span class="st st-sg">&#39;</span>), <span class="name">_</span>(<span class="st st-sg">&#39;</span><span class="st">Dec</span><span class="st st-sg">&#39;</span>)]
+ <span class="name">MONTHS_FULL</span> <span class="op">=</span> [<span class="name">_</span>(<span class="st st-sg">&#39;</span><span class="st">January</span><span class="st st-sg">&#39;</span>), <span class="name">_</span>(<span class="st st-sg">&#39;</span><span class="st">February</span><span class="st st-sg">&#39;</span>), <span class="name">_</span>(<span class="st st-sg">&#39;</span><span class="st">March</span><span class="st st-sg">&#39;</span>), <span class="name">_</span>(<span class="st st-sg">&#39;</span><span class="st">April</span><span class="st st-sg">&#39;</span>),
+ <span class="name">_</span>(<span class="st st-sg">&#39;</span><span class="st">May:full</span><span class="st st-sg">&#39;</span>), <span class="name">_</span>(<span class="st st-sg">&#39;</span><span class="st">June</span><span class="st st-sg">&#39;</span>), <span class="name">_</span>(<span class="st st-sg">&#39;</span><span class="st">July</span><span class="st st-sg">&#39;</span>), <span class="name">_</span>(<span class="st st-sg">&#39;</span><span class="st">August</span><span class="st st-sg">&#39;</span>),
+ <span class="name">_</span>(<span class="st st-sg">&#39;</span><span class="st">September</span><span class="st st-sg">&#39;</span>), <span class="name">_</span>(<span class="st st-sg">&#39;</span><span class="st">October</span><span class="st st-sg">&#39;</span>), <span class="name">_</span>(<span class="st st-sg">&#39;</span><span class="st">November</span><span class="st st-sg">&#39;</span>),
+ <span class="name">_</span>(<span class="st st-sg">&#39;</span><span class="st">December</span><span class="st st-sg">&#39;</span>)]
+
+ <span class="kw">def </span><span class="fun">__init__</span>(<span class="bn bn-pseudo">self</span>, <span class="name">req</span>, <span class="name">dateobj</span>):
+ <span class="bn bn-pseudo">self</span>.<span class="name">req</span> <span class="op">=</span> <span class="name">req</span>
+
+ <span class="kw">if</span> <span class="bn">isinstance</span>(<span class="name">dateobj</span>, <span class="name">datetime</span>):
+ <span class="bn bn-pseudo">self</span>.<span class="name">date</span> <span class="op">=</span> <span class="name">dateobj</span>
+
+ <span class="kw">elif</span> <span class="bn">isinstance</span>(<span class="name">dateobj</span>, <span class="bn">int</span>):
+ <span class="bn bn-pseudo">self</span>.<span class="name">date</span> <span class="op">=</span> <span class="name">datetime</span>.<span class="name">utcfromtimestamp</span>(<span class="name">dateobj</span>)
+ <span class="kw">elif</span> <span class="bn">isinstance</span>(<span class="name">dateobj</span>, <span class="name">time</span>.<span class="name">struct_time</span>):
+ <span class="bn bn-pseudo">self</span>.<span class="name">date</span> <span class="op">=</span> <span class="name">datetime</span>(<span class="name">dateobj</span>[:<span class="nb nb-int">7</span>])
+ <span class="kw">elif</span> <span class="name">dateobj</span> <span class="op op-word">is</span> <span class="bn bn-pseudo">None</span>:
+ <span class="bn bn-pseudo">self</span>.<span class="name">date</span> <span class="op">=</span> <span class="name">datetime</span>(<span class="nb nb-int">1</span>, <span class="nb nb-int">1</span>, <span class="nb nb-int">1</span>)
+ <span class="kw">else</span>:
+ <span class="kw">raise</span> <span class="exc">TypeError</span>(<span class="st st-sg">&#39;</span><span class="st st-int">%r</span><span class="st"> is not a valid time object</span><span class="st st-sg">&#39;</span> <span class="op">%</span> <span class="name">dateobj</span>)
+
+ <span class="kw">def </span><span class="fun">format</span>(<span class="bn bn-pseudo">self</span>, <span class="name">formatstring</span>):
+ <span class="name">bits</span> <span class="op">=</span> []
+ <span class="kw">for</span> <span class="name">bit</span> <span class="op op-word">in</span> <span class="name">split_format</span>(<span class="name">formatstring</span>):
+ <span class="kw">if</span> <span class="name">bit</span>.<span class="name">startswith</span>(<span class="st st-sg">&#39;</span><span class="st">%</span><span class="st st-sg">&#39;</span>):
+ <span class="name">handler</span> <span class="op">=</span> <span class="bn">getattr</span>(<span class="bn bn-pseudo">self</span>, <span class="st st-sg">&#39;</span><span class="st">format_</span><span class="st st-sg">&#39;</span> <span class="op">+</span> <span class="name">bit</span>[<span class="nb nb-int">1</span>], <span class="bn bn-pseudo">None</span>)
+ <span class="kw">if</span> <span class="name">handler</span> <span class="op op-word">is</span> <span class="op op-word">not</span> <span class="bn bn-pseudo">None</span>:
+ <span class="name">bits</span>.<span class="name">append</span>(<span class="name">handler</span>())
+ <span class="kw">else</span>:
+ <span class="name">bits</span>.<span class="name">append</span>(<span class="name">bit</span>)
+ <span class="kw">else</span>:
+ <span class="name">bits</span>.<span class="name">append</span>(<span class="name">bit</span>)
+ <span class="kw">return</span> <span class="name">u</span><span class="st st-sg">&#39;&#39;</span>.<span class="name">join</span>(<span class="name">bits</span>)
+
+ <span class="kw">def </span><span class="fun">format_a</span>(<span class="bn bn-pseudo">self</span>):
+ <span class="st st-db">&quot;&quot;&quot;</span><span class="st">abbreviated weekday name.</span><span class="st st-db">&quot;&quot;&quot;</span>
+
+ <span class="kw">return</span> <span class="bn bn-pseudo">self</span>.<span class="name">req</span>.<span class="name">gettext</span>(<span class="bn bn-pseudo">self</span>.<span class="name">WEEKDAYS_ABBR</span>[<span class="bn bn-pseudo">self</span>.<span class="name">date</span>.<span class="name">weekday</span>()])
+
+ <span class="kw">def </span><span class="fun">format_A</span>(<span class="bn bn-pseudo">self</span>):
+ <span class="st st-db">&quot;&quot;&quot;</span><span class="st">full weekday name.</span><span class="st st-db">&quot;&quot;&quot;</span>
+
+ <span class="kw">return</span> <span class="bn bn-pseudo">self</span>.<span class="name">req</span>.<span class="name">gettext</span>(<span class="bn bn-pseudo">self</span>.<span class="name">WEEKDAYS_FULL</span>[<span class="bn bn-pseudo">self</span>.<span class="name">date</span>.<span class="name">weekday</span>()])
+
+ <span class="kw">def </span><span class="fun">format_b</span>(<span class="bn bn-pseudo">self</span>):
+ <span class="st st-db">&quot;&quot;&quot;</span><span class="st">abbreviated month name.</span><span class="st st-db">&quot;&quot;&quot;</span>
+
+ <span class="kw">return</span> <span class="bn bn-pseudo">self</span>.<span class="name">req</span>.<span class="name">gettext</span>(<span class="bn bn-pseudo">self</span>.<span class="name">MONTHS_ABBR</span>[<span class="bn bn-pseudo">self</span>.<span class="name">date</span>.<span class="name">month</span> <span class="op">-</span> <span class="nb nb-int">1</span>])
+
+ <span class="kw">def </span><span class="fun">format_B</span>(<span class="bn bn-pseudo">self</span>):
+ <span class="st st-db">&quot;&quot;&quot;</span><span class="st">full month name.</span><span class="st st-db">&quot;&quot;&quot;</span>
+
+ <span class="kw">return</span> <span class="bn bn-pseudo">self</span>.<span class="name">req</span>.<span class="name">gettext</span>(<span class="bn bn-pseudo">self</span>.<span class="name">MONTHS_FULL</span>[<span class="bn bn-pseudo">self</span>.<span class="name">date</span>.<span class="name">month</span> <span class="op">-</span> <span class="nb nb-int">1</span>])
+
+ <span class="kw">def </span><span class="fun">format_d</span>(<span class="bn bn-pseudo">self</span>):
+ <span class="st st-db">&quot;&quot;&quot;</span><span class="st">Day of the month as a decimal number [01,31].</span><span class="st st-db">&quot;&quot;&quot;</span>
+
+ <span class="kw">return</span> <span class="bn">unicode</span>(<span class="bn bn-pseudo">self</span>.<span class="name">date</span>.<span class="name">day</span>)
+
+ <span class="kw">def </span><span class="fun">format_H</span>(<span class="bn bn-pseudo">self</span>):
+ <span class="st st-db">&quot;&quot;&quot;</span><span class="st">Hour (24-hour clock) as a decimal number [00,23].</span><span class="st st-db">&quot;&quot;&quot;</span>
+
+ <span class="kw">return</span> <span class="bn">unicode</span>(<span class="bn bn-pseudo">self</span>.<span class="name">date</span>.<span class="name">hour</span>)
+
+ <span class="kw">def </span><span class="fun">format_I</span>(<span class="bn bn-pseudo">self</span>):
+ <span class="st st-db">&quot;&quot;&quot;</span><span class="st">Hour (12-hour clock) as a decimal number [01,12].</span><span class="st st-db">&quot;&quot;&quot;</span>
+
+ <span class="kw">return</span> <span class="bn">unicode</span>(<span class="bn bn-pseudo">self</span>.<span class="name">date</span>.<span class="name">hour</span> <span class="op">%</span> <span class="nb nb-int">12</span>)
+
+ <span class="kw">def </span><span class="fun">format_j</span>(<span class="bn bn-pseudo">self</span>):
+ <span class="st st-db">&quot;&quot;&quot;</span><span class="st">Day of the year as a decimal number [001,366].</span><span class="st st-db">&quot;&quot;&quot;</span>
+
+ <span class="kw">return</span> <span class="bn">unicode</span>(<span class="bn bn-pseudo">self</span>.<span class="name">date</span>.<span class="name">strftime</span>(<span class="st st-sg">&#39;</span><span class="st">%j</span><span class="st st-sg">&#39;</span>))
+
+ <span class="kw">def </span><span class="fun">format_J</span>(<span class="bn bn-pseudo">self</span>):
+ <span class="st st-db">&quot;&quot;&quot;</span><span class="st">Day of the year as decimal number [1,366].</span><span class="st st-db">&quot;&quot;&quot;</span>
+
+ <span class="kw">return</span> <span class="bn">unicode</span>(<span class="bn">int</span>(<span class="bn bn-pseudo">self</span>.<span class="name">date</span>.<span class="name">strftime</span>(<span class="st st-sg">&#39;</span><span class="st">%j</span><span class="st st-sg">&#39;</span>)))
+
+ <span class="kw">def </span><span class="fun">format_m</span>(<span class="bn bn-pseudo">self</span>):
+ <span class="st st-db">&quot;&quot;&quot;</span><span class="st">Month as a decimal number [01,12].</span><span class="st st-db">&quot;&quot;&quot;</span>
+
+ <span class="kw">return</span> <span class="name">u</span><span class="st st-sg">&#39;</span><span class="st st-int">%02d</span><span class="st st-sg">&#39;</span> <span class="op">%</span> <span class="bn bn-pseudo">self</span>.<span class="name">date</span>.<span class="name">month</span>
+
+ <span class="kw">def </span><span class="fun">format_n</span>(<span class="bn bn-pseudo">self</span>):
+ <span class="st st-db">&quot;&quot;&quot;</span><span class="st">Month as a decimal number [1,12].</span><span class="st st-db">&quot;&quot;&quot;</span>
+
+ <span class="kw">return</span> <span class="bn">unicode</span>(<span class="bn bn-pseudo">self</span>.<span class="name">date</span>.<span class="name">month</span>)
+
+ <span class="kw">def </span><span class="fun">format_M</span>(<span class="bn bn-pseudo">self</span>):
+ <span class="st st-db">&quot;&quot;&quot;</span><span class="st">Minute as a decimal number [00,59].</span><span class="st st-db">&quot;&quot;&quot;</span>
+
+ <span class="kw">return</span> <span class="name">u</span><span class="st st-sg">&#39;</span><span class="st st-int">%02d</span><span class="st st-sg">&#39;</span> <span class="op">%</span> <span class="bn bn-pseudo">self</span>.<span class="name">date</span>.<span class="name">minute</span>
+
+ <span class="kw">def </span><span class="fun">format_N</span>(<span class="bn bn-pseudo">self</span>):
+ <span class="st st-db">&quot;&quot;&quot;</span><span class="st">Minute as a decimal number [0,59].</span><span class="st st-db">&quot;&quot;&quot;</span>
+
+ <span class="kw">return</span> <span class="bn">unicode</span>(<span class="bn bn-pseudo">self</span>.<span class="name">date</span>.<span class="name">minute</span>)
+
+ <span class="kw">def </span><span class="fun">format_p</span>(<span class="bn bn-pseudo">self</span>):
+ <span class="st st-db">&quot;&quot;&quot;</span><span class="st">Locale&#39;s equivalent of either AM or PM.</span><span class="st st-db">&quot;&quot;&quot;</span>
+
+ <span class="name">_</span> <span class="op">=</span> <span class="bn bn-pseudo">self</span>.<span class="name">req</span>.<span class="name">gettext</span>
+ <span class="kw">if</span> <span class="bn bn-pseudo">self</span>.<span class="name">date</span>.<span class="name">hour</span> <span class="op">&gt;</span> <span class="nb nb-int">11</span>:
+ <span class="kw">return</span> <span class="name">_</span>(<span class="st st-sg">&#39;</span><span class="st">PM</span><span class="st st-sg">&#39;</span>)
+ <span class="kw">return</span> <span class="name">_</span>(<span class="st st-sg">&#39;</span><span class="st">AM</span><span class="st st-sg">&#39;</span>)
+
+ <span class="kw">def </span><span class="fun">format_P</span>(<span class="bn bn-pseudo">self</span>):
+ <span class="st st-db">&quot;&quot;&quot;</span><span class="st">Locale&#39;s equivalent of either a.m. or p.m.</span><span class="st st-db">&quot;&quot;&quot;</span>
+
+ <span class="name">_</span> <span class="op">=</span> <span class="bn bn-pseudo">self</span>.<span class="name">req</span>.<span class="name">gettext</span>
+ <span class="kw">if</span> <span class="bn bn-pseudo">self</span>.<span class="name">date</span>.<span class="name">hour</span> <span class="op">&gt;</span> <span class="nb nb-int">11</span>:
+ <span class="kw">return</span> <span class="name">_</span>(<span class="st st-sg">&#39;</span><span class="st">p.m.</span><span class="st st-sg">&#39;</span>)
+ <span class="kw">return</span> <span class="name">_</span>(<span class="st st-sg">&#39;</span><span class="st">a.m.</span><span class="st st-sg">&#39;</span>)
+
+ <span class="kw">def </span><span class="fun">format_s</span>(<span class="bn bn-pseudo">self</span>):
+ <span class="st st-db">&quot;&quot;&quot;</span><span class="st">Second as a decimal number [0,61].</span><span class="st st-db">&quot;&quot;&quot;</span>
+
+ <span class="kw">return</span> <span class="bn">unicode</span>(<span class="bn bn-pseudo">self</span>.<span class="name">date</span>.<span class="name">second</span>)
+
+ <span class="kw">def </span><span class="fun">format_S</span>(<span class="bn bn-pseudo">self</span>):
+ <span class="st st-db">&quot;&quot;&quot;</span><span class="st">Second as a decimal number [00,61].</span><span class="st st-db">&quot;&quot;&quot;</span>
+
+ <span class="kw">return</span> <span class="name">u</span><span class="st st-sg">&#39;</span><span class="st st-int">%02d</span><span class="st st-sg">&#39;</span> <span class="op">%</span> <span class="bn bn-pseudo">self</span>.<span class="name">date</span>.<span class="name">second</span>
+
+ <span class="kw">def </span><span class="fun">format_U</span>(<span class="bn bn-pseudo">self</span>):
+ <span class="st st-db">&quot;&quot;&quot;</span><span class="st">Week number of the year (Sunday as the first day of the week)
+ as a decimal number [00,53]. All days in a new year preceding the
+ first Sunday are considered to be in week 0.</span><span class="st st-db">&quot;&quot;&quot;</span>
+
+ <span class="kw">return</span> <span class="bn">unicode</span>(<span class="bn bn-pseudo">self</span>.<span class="name">date</span>.<span class="name">strftime</span>(<span class="st st-sg">&#39;</span><span class="st">%U</span><span class="st st-sg">&#39;</span>))
+
+ <span class="kw">def </span><span class="fun">format_u</span>(<span class="bn bn-pseudo">self</span>):
+ <span class="st st-db">&quot;&quot;&quot;</span><span class="st">Week number of the year (Sunday as the first day of the week)
+ as a decimal number [0,53]. All days in a new year preceding the
+ first Sunday are considered to be in week 0.</span><span class="st st-db">&quot;&quot;&quot;</span>
+
+ <span class="kw">return</span> <span class="bn">unicode</span>(<span class="bn">int</span>(<span class="bn bn-pseudo">self</span>.<span class="name">date</span>.<span class="name">strftime</span>(<span class="st st-sg">&#39;</span><span class="st">%U</span><span class="st st-sg">&#39;</span>)))
+
+ <span class="kw">def </span><span class="fun">format_w</span>(<span class="bn bn-pseudo">self</span>):
+ <span class="st st-db">&quot;&quot;&quot;</span><span class="st">Weekday as a decimal number [0(Sunday),6].</span><span class="st st-db">&quot;&quot;&quot;</span>
+
+ <span class="kw">return</span> <span class="bn">unicode</span>(<span class="bn bn-pseudo">self</span>.<span class="name">date</span>.<span class="name">strftime</span>(<span class="st st-sg">&#39;</span><span class="st">%w</span><span class="st st-sg">&#39;</span>))
+
+ <span class="kw">def </span><span class="fun">format_z</span>(<span class="bn bn-pseudo">self</span>):
+ <span class="st st-db">&quot;&quot;&quot;</span><span class="st">Weekday as a decimal number [0(Monday),6].</span><span class="st st-db">&quot;&quot;&quot;</span>
+
+ <span class="cm">#XXX: anyone something better than z?</span>
+ <span class="kw">return</span> <span class="bn">unicode</span>(<span class="bn bn-pseudo">self</span>.<span class="name">date</span>.<span class="name">weekday</span>)
+
+ <span class="kw">def </span><span class="fun">format_W</span>(<span class="bn bn-pseudo">self</span>):
+ <span class="st st-db">&quot;&quot;&quot;</span><span class="st">Week number of the year (Monday as the first day of the week)
+ as a decimal number [00,53]. All days in a new year preceding the
+ first Monday are considered to be in week 0.</span><span class="st st-db">&quot;&quot;&quot;</span>
+
+ <span class="kw">return</span> <span class="bn">unicode</span>(<span class="bn bn-pseudo">self</span>.<span class="name">date</span>.<span class="name">strftime</span>(<span class="st st-sg">&#39;</span><span class="st">%W</span><span class="st st-sg">&#39;</span>))
+
+ <span class="kw">def </span><span class="fun">format_v</span>(<span class="bn bn-pseudo">self</span>):
+ <span class="st st-db">&quot;&quot;&quot;</span><span class="st">Week number of the year (Monday as the first day of the week)
+ as a decimal number [0,53]. All days in a new year preceding the
+ first Monday are considered to be in week 0.</span><span class="st st-db">&quot;&quot;&quot;</span>
+
+ <span class="kw">return</span> <span class="bn">unicode</span>(<span class="bn">int</span>(<span class="bn bn-pseudo">self</span>.<span class="name">date</span>.<span class="name">strftime</span>(<span class="st st-sg">&#39;</span><span class="st">%W</span><span class="st st-sg">&#39;</span>)))
+
+ <span class="kw">def </span><span class="fun">format_y</span>(<span class="bn bn-pseudo">self</span>):
+ <span class="st st-db">&quot;&quot;&quot;</span><span class="st">Year without century as a decimal number [00,99].</span><span class="st st-db">&quot;&quot;&quot;</span>
+
+ <span class="kw">return</span> <span class="bn">unicode</span>(<span class="bn bn-pseudo">self</span>.<span class="name">date</span>.<span class="name">strftime</span>(<span class="st st-sg">&#39;</span><span class="st">%y</span><span class="st st-sg">&#39;</span>))
+
+ <span class="kw">def </span><span class="fun">format_Y</span>(<span class="bn bn-pseudo">self</span>):
+ <span class="st st-db">&quot;&quot;&quot;</span><span class="st">Year with century as a decimal number.</span><span class="st st-db">&quot;&quot;&quot;</span>
+
+ <span class="kw">return</span> <span class="bn">unicode</span>(<span class="bn bn-pseudo">self</span>.<span class="name">date</span>.<span class="name">year</span>)
+
+ <span class="kw">def </span><span class="fun">format_r</span>(<span class="bn bn-pseudo">self</span>):
+ <span class="st st-db">&quot;&quot;&quot;</span><span class="st">English ordinal suffix for the day of the month, 2 characters;
+ i.e. &#39;st&#39;, &#39;nd&#39;, &#39;rd&#39; or &#39;th&#39;</span><span class="st st-db">&quot;&quot;&quot;</span>
+
+ <span class="name">_</span> <span class="op">=</span> <span class="bn bn-pseudo">self</span>.<span class="name">req</span>.<span class="name">gettext</span>
+ <span class="kw">if</span> <span class="bn bn-pseudo">self</span>.<span class="name">date</span>.<span class="name">day</span> <span class="op op-word">in</span> (<span class="nb nb-int">11</span>, <span class="nb nb-int">12</span>, <span class="nb nb-int">13</span>):
+ <span class="kw">return</span> <span class="name">_</span>(<span class="st st-sg">&#39;</span><span class="st">th</span><span class="st st-sg">&#39;</span>)
+ <span class="name">last</span> <span class="op">=</span> <span class="bn bn-pseudo">self</span>.<span class="name">date</span>.<span class="name">day</span> <span class="op">%</span> <span class="nb nb-int">10</span>
+
+ <span class="kw">if</span> <span class="name">last</span> <span class="op">==</span> <span class="nb nb-int">1</span>:
+ <span class="kw">return</span> <span class="name">_</span>(<span class="st st-sg">&#39;</span><span class="st">st</span><span class="st st-sg">&#39;</span>)
+ <span class="kw">if</span> <span class="name">last</span> <span class="op">==</span> <span class="nb nb-int">2</span>:
+ <span class="kw">return</span> <span class="name">_</span>(<span class="st st-sg">&#39;</span><span class="st">nd</span><span class="st st-sg">&#39;</span>)
+ <span class="kw">if</span> <span class="name">last</span> <span class="op">==</span> <span class="nb nb-int">3</span>:
+ <span class="kw">return</span> <span class="name">_</span>(<span class="st st-sg">&#39;</span><span class="st">rd</span><span class="st st-sg">&#39;</span>)
+ <span class="kw">return</span> <span class="name">_</span>(<span class="st st-sg">&#39;</span><span class="st">th</span><span class="st st-sg">&#39;</span>)
+
+ <span class="kw">def </span><span class="fun">format_t</span>(<span class="bn bn-pseudo">self</span>):
+ <span class="st st-db">&quot;&quot;&quot;</span><span class="st">Number of days in the given month; i.e. &#39;28&#39; to &#39;31&#39;</span><span class="st st-db">&quot;&quot;&quot;</span>
+
+ <span class="kw">return</span> <span class="st st-sg">&#39;</span><span class="st st-int">%02d</span><span class="st st-sg">&#39;</span> <span class="op">%</span> <span class="name">monthrange</span>(<span class="bn bn-pseudo">self</span>.<span class="name">date</span>.<span class="name">year</span>, <span class="bn bn-pseudo">self</span>.<span class="name">date</span>.<span class="name">month</span>)[<span class="nb nb-int">1</span>]
+
+ <span class="kw">def </span><span class="fun">__repr__</span>(<span class="bn bn-pseudo">self</span>):
+ <span class="kw">return</span> <span class="st st-sg">&#39;</span><span class="st">&lt;</span><span class="st st-int">%s</span><span class="st">: [</span><span class="st st-int">%s</span><span class="st">]&gt;</span><span class="st st-sg">&#39;</span> <span class="op">%</span> (
+ <span class="bn bn-pseudo">self</span>.<span class="name">__class__</span>.<span class="name">__name__</span>,
+ <span class="st st-sg">&#39;</span><span class="st">, </span><span class="st st-sg">&#39;</span>.<span class="name">join</span>(<span class="bn">str</span>(<span class="name">i</span>[<span class="nb nb-int">7</span>:]) <span class="kw">for</span> <span class="name">i</span> <span class="op op-word">in</span> <span class="bn">dir</span>(<span class="bn bn-pseudo">self</span>) <span class="kw">if</span> <span class="name">i</span>.<span class="name">startswith</span>(<span class="st st-sg">&#39;</span><span class="st">format_</span><span class="st st-sg">&#39;</span>))
+ )
+
+
+
+<span class="kw">def </span><span class="fun">format_timedelta</span>(<span class="name">req</span>, <span class="name">time1</span><span class="op">=</span><span class="bn bn-pseudo">None</span>, <span class="name">time2</span><span class="op">=</span><span class="bn bn-pseudo">None</span>):
+ <span class="st st-db">&quot;&quot;&quot;</span><span class="st">
+ Format the difference between two datetime or unix timestamp objects::
+
+ &gt;&gt;&gt; from pocoo.pkg.core.l10n import timedeltaformat
+ &gt;&gt;&gt; now = datetime.now()
+ &gt;&gt;&gt; timedeltaformat(req, now)
+ u&#39;6 seconds ago&#39;
+
+ </span><span class="st st-db">&quot;&quot;&quot;</span>
+ <span class="name">_</span> <span class="op">=</span> <span class="name">req</span>.<span class="name">gettext</span>
+ <span class="kw">if</span> <span class="name">time1</span> <span class="op op-word">is</span> <span class="bn bn-pseudo">None</span>:
+ <span class="name">time1</span> <span class="op">=</span> <span class="name">datetime</span>.<span class="name">utcnow</span>()
+ <span class="kw">if</span> <span class="bn">isinstance</span>(<span class="name">time1</span>, <span class="name">datetime</span>):
+ <span class="name">time1</span> <span class="op">=</span> <span class="name">time</span>.<span class="name">mktime</span>(<span class="name">time1</span>.<span class="name">timetuple</span>()) <span class="op">+</span> <span class="name">time1</span>.<span class="name">microsecond</span> <span class="op">/</span> <span class="nb nb-int">1</span><span class="name">e6</span>
+
+ <span class="kw">if</span> <span class="name">time2</span> <span class="op op-word">is</span> <span class="bn bn-pseudo">None</span>:
+ <span class="name">time2</span> <span class="op">=</span> <span class="name">datetime</span>.<span class="name">utcnow</span>()
+ <span class="kw">if</span> <span class="bn">isinstance</span>(<span class="name">time2</span>, <span class="name">datetime</span>):
+ <span class="name">time2</span> <span class="op">=</span> <span class="name">time</span>.<span class="name">mktime</span>(<span class="name">time2</span>.<span class="name">timetuple</span>()) <span class="op">+</span> <span class="name">time2</span>.<span class="name">microsecond</span> <span class="op">/</span> <span class="nb nb-int">1</span><span class="name">e6</span>
+
+ <span class="kw">if</span> <span class="name">time1</span> <span class="op">&gt;</span> <span class="name">time2</span>:
+ <span class="name">tmpl</span> <span class="op">=</span> <span class="name">_</span>(<span class="st st-sg">&#39;</span><span class="st st-int">%d</span><span class="st"> </span><span class="st st-int">%s</span><span class="st"> in the future</span><span class="st st-sg">&#39;</span>)
+ <span class="kw">else</span>:
+ <span class="name">tmpl</span> <span class="op">=</span> <span class="name">_</span>(<span class="st st-sg">&#39;</span><span class="st st-int">%d</span><span class="st"> </span><span class="st st-int">%s</span><span class="st"> ago</span><span class="st st-sg">&#39;</span>)
+ <span class="kw">def </span><span class="fun">trans</span>(<span class="name">s</span>, <span class="name">entity</span>):
+ <span class="kw">if</span> <span class="name">entity</span> <span class="op">==</span> <span class="st st-sg">&#39;</span><span class="st">s</span><span class="st st-sg">&#39;</span>:
+ <span class="name">e</span> <span class="op">=</span> <span class="name">_</span>(<span class="st st-sg">&#39;</span><span class="st">second</span><span class="st st-sg">&#39;</span>, <span class="st st-sg">&#39;</span><span class="st">seconds</span><span class="st st-sg">&#39;</span>, <span class="name">s</span>)
+ <span class="kw">elif</span> <span class="name">entity</span> <span class="op">==</span> <span class="st st-sg">&#39;</span><span class="st">m</span><span class="st st-sg">&#39;</span>:
+ <span class="name">e</span> <span class="op">=</span> <span class="name">_</span>(<span class="st st-sg">&#39;</span><span class="st">minute</span><span class="st st-sg">&#39;</span>, <span class="st st-sg">&#39;</span><span class="st">minutes</span><span class="st st-sg">&#39;</span>, <span class="name">s</span>)
+ <span class="kw">elif</span> <span class="name">entity</span> <span class="op">==</span> <span class="st st-sg">&#39;</span><span class="st">h</span><span class="st st-sg">&#39;</span>:
+ <span class="name">e</span> <span class="op">=</span> <span class="name">_</span>(<span class="st st-sg">&#39;</span><span class="st">hour</span><span class="st st-sg">&#39;</span>, <span class="st st-sg">&#39;</span><span class="st">hours</span><span class="st st-sg">&#39;</span>, <span class="name">s</span>)
+ <span class="kw">elif</span> <span class="name">entity</span> <span class="op">==</span> <span class="st st-sg">&#39;</span><span class="st">d</span><span class="st st-sg">&#39;</span>:
+ <span class="name">e</span> <span class="op">=</span> <span class="name">_</span>(<span class="st st-sg">&#39;</span><span class="st">day</span><span class="st st-sg">&#39;</span>, <span class="st st-sg">&#39;</span><span class="st">days</span><span class="st st-sg">&#39;</span>, <span class="name">s</span>)
+ <span class="kw">elif</span> <span class="name">entity</span> <span class="op">==</span> <span class="st st-sg">&#39;</span><span class="st">w</span><span class="st st-sg">&#39;</span>:
+ <span class="name">e</span> <span class="op">=</span> <span class="name">_</span>(<span class="st st-sg">&#39;</span><span class="st">week</span><span class="st st-sg">&#39;</span>, <span class="st st-sg">&#39;</span><span class="st">weeks</span><span class="st st-sg">&#39;</span>, <span class="name">s</span>)
+ <span class="kw">elif</span> <span class="name">entity</span> <span class="op">==</span> <span class="st st-sg">&#39;</span><span class="st">M</span><span class="st st-sg">&#39;</span>:
+ <span class="name">e</span> <span class="op">=</span> <span class="name">_</span>(<span class="st st-sg">&#39;</span><span class="st">month</span><span class="st st-sg">&#39;</span>, <span class="st st-sg">&#39;</span><span class="st">months</span><span class="st st-sg">&#39;</span>, <span class="name">s</span>)
+ <span class="kw">else</span>:
+ <span class="name">e</span> <span class="op">=</span> <span class="name">_</span>(<span class="st st-sg">&#39;</span><span class="st">year</span><span class="st st-sg">&#39;</span>, <span class="st st-sg">&#39;</span><span class="st">years</span><span class="st st-sg">&#39;</span>, <span class="name">s</span>)
+ <span class="kw">return</span> <span class="name">tmpl</span> <span class="op">%</span> (<span class="name">s</span>, <span class="name">e</span>)
+ <span class="name">diff</span> <span class="op">=</span> <span class="bn">abs</span>(<span class="bn">int</span>(<span class="name">time2</span> <span class="op">-</span> <span class="name">time1</span>))
+ <span class="kw">for</span> <span class="name">u</span>, <span class="name">e</span> <span class="op op-word">in</span> <span class="name">TIME_DELTA_UNITS</span>:
+ <span class="name">r</span> <span class="op">=</span> <span class="name">diff</span> <span class="op">/</span> <span class="bn">float</span>(<span class="name">u</span>)
+ <span class="kw">if</span> <span class="name">r</span> <span class="op">&gt;=</span> <span class="nb nb-flt">0.9</span>:
+ <span class="name">s</span> <span class="op">=</span> <span class="bn">int</span>(<span class="bn">round</span>(<span class="name">r</span>))
+ <span class="kw">return</span> <span class="name">trans</span>(<span class="name">s</span>, <span class="name">e</span>)
+ <span class="kw">return</span> <span class="name">trans</span>(<span class="name">diff</span>, <span class="st st-sg">&#39;</span><span class="st">s</span><span class="st st-sg">&#39;</span>)
+
+
+
+<span class="kw">def </span><span class="fun">dateformat</span>(<span class="name">date</span>, <span class="name">context</span>):
+ <span class="cm">#XXX: load default string from i10n language file</span>
+ <span class="name">req</span> <span class="op">=</span> <span class="name">context</span>[<span class="st st-sg">&#39;</span><span class="st">REQUEST</span><span class="st st-sg">&#39;</span>]
+ <span class="name">formatstr</span> <span class="op">=</span> <span class="name">req</span>.<span class="name">user</span>.<span class="name">settings</span>.<span class="name">get</span>(<span class="st st-sg">&#39;</span><span class="st">dateformat</span><span class="st st-sg">&#39;</span>)
+ <span class="kw">if</span> <span class="name">formatstr</span> <span class="op op-word">is</span> <span class="bn bn-pseudo">None</span>:
+ <span class="name">formatstr</span> <span class="op">=</span> <span class="name">req</span>.<span class="name">ctx</span>.<span class="name">cfg</span>.<span class="name">get</span>(<span class="st st-sg">&#39;</span><span class="st">general</span><span class="st st-sg">&#39;</span>, <span class="st st-sg">&#39;</span><span class="st">dateformat</span><span class="st st-sg">&#39;</span>,
+ <span class="name">DEFAULT_DATE_FORMAT</span>)
+ <span class="name">f</span> <span class="op">=</span> <span class="name">DateFormatter</span>(<span class="name">req</span>, <span class="name">date</span> <span class="op op-word">or</span> <span class="bn bn-pseudo">None</span>)
+ <span class="kw">return</span> <span class="name">f</span>.<span class="name">format</span>(<span class="name">formatstr</span>)
+
+
+
+<span class="kw">def </span><span class="fun">timeformat</span>(<span class="name">date</span>, <span class="name">context</span>):
+ <span class="cm">#XXX: load default string from l10n language file</span>
+ <span class="name">req</span> <span class="op">=</span> <span class="name">context</span>[<span class="st st-sg">&#39;</span><span class="st">REQUEST</span><span class="st st-sg">&#39;</span>]
+ <span class="name">formatstr</span> <span class="op">=</span> <span class="name">req</span>.<span class="name">user</span>.<span class="name">settings</span>.<span class="name">get</span>(<span class="st st-sg">&#39;</span><span class="st">timeformat</span><span class="st st-sg">&#39;</span>)
+ <span class="kw">if</span> <span class="name">formatstr</span> <span class="op op-word">is</span> <span class="bn bn-pseudo">None</span>:
+ <span class="name">formatstr</span> <span class="op">=</span> <span class="name">req</span>.<span class="name">ctx</span>.<span class="name">cfg</span>.<span class="name">get</span>(<span class="st st-sg">&#39;</span><span class="st">general</span><span class="st st-sg">&#39;</span>, <span class="st st-sg">&#39;</span><span class="st">timeformat</span><span class="st st-sg">&#39;</span>,
+ <span class="name">DEFAULT_TIME_FORMAT</span>)
+ <span class="name">f</span> <span class="op">=</span> <span class="name">DateFormatter</span>(<span class="name">req</span>, <span class="name">date</span> <span class="op op-word">or</span> <span class="bn bn-pseudo">None</span>)
+ <span class="kw">return</span> <span class="name">f</span>.<span class="name">format</span>(<span class="name">formatstr</span>)
+
+
+
+<span class="kw">def </span><span class="fun">datetimeformat</span>(<span class="name">date</span>, <span class="name">context</span>):
+ <span class="cm">#XXX: load default string from l10n language file</span>
+ <span class="name">req</span> <span class="op">=</span> <span class="name">context</span>[<span class="st st-sg">&#39;</span><span class="st">REQUEST</span><span class="st st-sg">&#39;</span>]
+ <span class="name">formatstr</span> <span class="op">=</span> <span class="name">req</span>.<span class="name">user</span>.<span class="name">settings</span>.<span class="name">get</span>(<span class="st st-sg">&#39;</span><span class="st">datetimeformat</span><span class="st st-sg">&#39;</span>)
+ <span class="kw">if</span> <span class="name">formatstr</span> <span class="op op-word">is</span> <span class="bn bn-pseudo">None</span>:
+ <span class="name">formatstr</span> <span class="op">=</span> <span class="name">req</span>.<span class="name">ctx</span>.<span class="name">cfg</span>.<span class="name">get</span>(<span class="st st-sg">&#39;</span><span class="st">general</span><span class="st st-sg">&#39;</span>, <span class="st st-sg">&#39;</span><span class="st">datetimeformat</span><span class="st st-sg">&#39;</span>,
+ <span class="name">DEFAULT_DATETIME_FORMAT</span>)
+ <span class="name">f</span> <span class="op">=</span> <span class="name">DateFormatter</span>(<span class="name">req</span>, <span class="name">date</span> <span class="op op-word">or</span> <span class="bn bn-pseudo">None</span>)
+ <span class="kw">return</span> <span class="name">f</span>.<span class="name">format</span>(<span class="name">formatstr</span>)
+
+
+
+<span class="kw">def </span><span class="fun">timedeltaformat</span>(<span class="name">date</span>, <span class="name">context</span>, <span class="name">obj2</span><span class="op">=</span><span class="bn bn-pseudo">None</span>):
+ <span class="name">req</span> <span class="op">=</span> <span class="name">context</span>[<span class="st st-sg">&#39;</span><span class="st">REQUEST</span><span class="st st-sg">&#39;</span>]
+ <span class="kw">return</span> <span class="name">format_timedelta</span>(<span class="name">req</span>, <span class="name">date</span> <span class="op op-word">or</span> <span class="bn bn-pseudo">None</span>, <span class="name">obj2</span> <span class="op op-word">or</span> <span class="bn bn-pseudo">None</span>)
+
+<span class="cm"># -*- coding: utf-8 -*-</span>
+<span class="st st-db">&quot;&quot;&quot;</span><span class="st">
+ pocoo.pkg.core.pages
+ ~~~~~~~~~~~~~~~~~~~~
+
+ Pocoo core pages.
+
+ :copyright: 2006 by Armin Ronacher, Benjamin Wiegand.
+ :license: GNU GPL, see LICENSE for more details.
+</span><span class="st st-db">&quot;&quot;&quot;</span>
+
+<span class="kw">from </span><span class="cls">pocoo.http</span><span class="kw"> import</span> <span class="name">Response</span>, <span class="name">HttpRedirect</span>, <span class="name">TemplateResponse</span>, \
+ <span class="name">AccessDeniedResponse</span>, <span class="name">PageNotFound</span>
+
+<span class="kw">from </span><span class="cls">pocoo.settings</span><span class="kw"> import</span> <span class="name">cfg</span>
+<span class="kw">from </span><span class="cls">pocoo.template</span><span class="kw"> import</span> <span class="name">PagePublisher</span>, <span class="name">render_template</span>
+<span class="kw">from </span><span class="cls">pocoo.application</span><span class="kw"> import</span> <span class="name">RequestHandler</span>
+
+<span class="kw">from </span><span class="cls">pocoo.utils.mail</span><span class="kw"> import</span> <span class="name">Email</span>
+<span class="kw">from </span><span class="cls">pocoo.utils.net</span><span class="kw"> import</span> <span class="name">make_url_context_external</span>
+<span class="kw">from </span><span class="cls">pocoo.utils.form</span><span class="kw"> import</span> <span class="name">Form</span>, <span class="name">TextField</span>, <span class="name">TextArea</span>, <span class="name">SelectBox</span>, <span class="name">CheckBox</span>
+
+<span class="kw">from </span><span class="cls">pocoo.utils.validators</span><span class="kw"> import</span> <span class="name">isNotEmpty</span>, <span class="name">isSameValue</span>, <span class="name">isEmail</span>, \
+ <span class="name">checkTextLength</span>, <span class="name">isOneLetter</span>, <span class="name">mayEmpty</span>, <span class="name">coppaIsChecked</span>
+
+<span class="kw">from </span><span class="cls">pocoo.utils.json</span><span class="kw"> import</span> <span class="name">parse_datetime</span>
+
+<span class="kw">from </span><span class="cls">pocoo.pkg.core.remotecall</span><span class="kw"> import</span> <span class="name">RemoteCallable</span>, <span class="name">export</span>
+
+<span class="kw">from </span><span class="cls">pocoo.pkg.core.validators</span><span class="kw"> import</span> <span class="name">isAvailableUsername</span>, <span class="name">isStrongPassword</span>, \
+ <span class="name">isExistingUsername</span>, <span class="name">isAnonymousUsername</span>
+<span class="kw">from </span><span class="cls">pocoo.pkg.core.usersettings</span><span class="kw"> import</span> <span class="name">UserSettingsPage</span>
+
+<span class="kw">from </span><span class="cls">pocoo.pkg.core.db</span><span class="kw"> import</span> <span class="name">users</span>
+<span class="kw">from </span><span class="cls">pocoo.pkg.core.forum</span><span class="kw"> import</span> <span class="name">get_forum_index</span>, <span class="name">get_forum</span>, <span class="name">get_post_tree</span>, \
+ <span class="name">get_forum_pathbar</span>, <span class="name">get_view_mode</span>, <span class="name">get_flat_view</span>, <span class="name">quote_post</span>, <span class="name">edit_post</span>, \
+ <span class="name">get_post</span>, <span class="name">Thread</span>, <span class="name">get_last_posts</span>, <span class="name">get_last_thread_change</span>, <span class="name">get_post_pathbar</span>
+
+<span class="kw">from </span><span class="cls">pocoo.pkg.core.user</span><span class="kw"> import</span> <span class="name">User</span>, <span class="name">get_user_list</span>, \
+ <span class="name">get_user</span>, <span class="name">get_user_avatar</span>, <span class="name">reset_password</span>
+<span class="kw">from </span><span class="cls">pocoo.pkg.core.session</span><span class="kw"> import</span> <span class="name">get_active_sessions</span>, <span class="name">get_sessions_by_action</span>
+
+<span class="kw">from </span><span class="cls">pocoo.pkg.core.textfmt</span><span class="kw"> import</span> <span class="name">parse_and_render</span>, <span class="name">get_editor</span>
+
+<span class="name">_</span> <span class="op">=</span> <span class="kw">lambda</span> <span class="name">x</span>: <span class="name">x</span>
+
+
+<span class="kw">class </span><span class="cls">IndexPage</span>(<span class="name">RequestHandler</span>, <span class="name">PagePublisher</span>, <span class="name">RemoteCallable</span>):
+ <span class="name">page_name</span> <span class="op">=</span> <span class="st st-sg">&#39;</span><span class="st">index</span><span class="st st-sg">&#39;</span>
+
+ <span class="name">relative_url</span> <span class="op">=</span> <span class="st st-sg">&#39;&#39;</span>
+ <span class="name">handler_regexes</span> <span class="op">=</span> [<span class="name">u</span><span class="st st-sg">&#39;</span><span class="st">$</span><span class="st st-sg">&#39;</span>]
+
+ <span class="kw">def </span><span class="fun">handle_request</span>(<span class="bn bn-pseudo">self</span>, <span class="name">req</span>):
+ <span class="kw">return</span> <span class="name">TemplateResponse</span>(<span class="st st-sg">&#39;</span><span class="st">index.html</span><span class="st st-sg">&#39;</span>,
+ <span class="name">categories</span><span class="op">=</span><span class="name">get_forum_index</span>(<span class="name">req</span>),
+ <span class="name">feed_url</span><span class="op">=</span><span class="bn bn-pseudo">self</span>.<span class="name">ctx</span>.<span class="name">make_url</span>(<span class="st st-sg">&#39;</span><span class="st">feeds/recent.xml</span><span class="st st-sg">&#39;</span>)
+ )
+
+
+
+<span class="kw">class </span><span class="cls">ForumPage</span>(<span class="name">RequestHandler</span>):
+ <span class="name">handler_regexes</span> <span class="op">=</span> [<span class="st st-sg">r&#39;</span><span class="st">forum/(?P&lt;forum_id&gt;\d+)$</span><span class="st st-sg">&#39;</span>]
+
+ <span class="kw">def </span><span class="fun">handle_request</span>(<span class="bn bn-pseudo">self</span>, <span class="name">req</span>, <span class="name">forum_id</span>):
+ <span class="name">forum_id</span> <span class="op">=</span> <span class="bn">int</span>(<span class="name">forum_id</span>)
+ <span class="kw">try</span>:
+ <span class="name">page</span> <span class="op">=</span> <span class="bn">int</span>(<span class="name">req</span>.<span class="name">args</span>.<span class="name">get</span>(<span class="st st-sg">&#39;</span><span class="st">page</span><span class="st st-sg">&#39;</span>))
+ <span class="kw">except</span> (<span class="exc">TypeError</span>, <span class="exc">ValueError</span>):
+ <span class="name">page</span> <span class="op">=</span> <span class="nb nb-int">1</span>
+
+ <span class="name">forum</span> <span class="op">=</span> <span class="name">get_forum</span>(<span class="name">req</span>, <span class="name">forum_id</span>, <span class="name">page</span>)
+ <span class="kw">if</span> <span class="name">forum</span> <span class="op op-word">is</span> <span class="bn bn-pseudo">None</span>:
+ <span class="kw">return</span> <span class="name">PageNotFound</span>()
+ <span class="cm"># Redirect if the forum is a link</span>
+
+ <span class="kw">if</span> <span class="name">forum</span>[<span class="st st-sg">&#39;</span><span class="st">is_external_url</span><span class="st st-sg">&#39;</span>]:
+ <span class="kw">return</span> <span class="name">HttpRedirect</span>(<span class="name">forum</span>[<span class="st st-sg">&#39;</span><span class="st">link</span><span class="st st-sg">&#39;</span>], <span class="name">local</span><span class="op">=</span><span class="bn bn-pseudo">False</span>)
+ <span class="kw">return</span> <span class="name">TemplateResponse</span>(<span class="st st-sg">&#39;</span><span class="st">viewforum.html</span><span class="st st-sg">&#39;</span>,
+ <span class="name">forum</span><span class="op">=</span><span class="name">forum</span>,
+ <span class="name">pathbar</span><span class="op">=</span><span class="name">get_forum_pathbar</span>(<span class="bn bn-pseudo">self</span>.<span class="name">ctx</span>, <span class="name">forum_id</span>),
+ <span class="name">feed_url</span><span class="op">=</span><span class="bn bn-pseudo">self</span>.<span class="name">ctx</span>.<span class="name">make_url</span>(<span class="st st-sg">&#39;</span><span class="st">feeds/forum/</span><span class="st st-int">%d</span><span class="st">.xml</span><span class="st st-sg">&#39;</span> <span class="op">%</span> <span class="name">forum_id</span>)
+ )
+
+
+
+<span class="kw">class </span><span class="cls">PostPage</span>(<span class="name">RequestHandler</span>, <span class="name">RemoteCallable</span>):
+ <span class="name">handler_regexes</span> <span class="op">=</span> [<span class="st st-sg">r&#39;</span><span class="st">post/(?P&lt;post_id&gt;\d+)$</span><span class="st st-sg">&#39;</span>]
+
+ <span class="kw">def </span><span class="fun">handle_request</span>(<span class="bn bn-pseudo">self</span>, <span class="name">req</span>, <span class="name">post_id</span>):
+ <span class="name">view</span> <span class="op">=</span> <span class="name">get_view_mode</span>(<span class="name">req</span>)
+ <span class="kw">if</span> <span class="name">view</span> <span class="op">==</span> <span class="st st-sg">&#39;</span><span class="st">flat</span><span class="st st-sg">&#39;</span>:
+ <span class="kw">return</span> <span class="bn bn-pseudo">self</span>.<span class="name">_flat_view</span>(<span class="name">req</span>, <span class="bn">int</span>(<span class="name">post_id</span>))
+ <span class="kw">else</span>:
+ <span class="kw">return</span> <span class="bn bn-pseudo">self</span>.<span class="name">_threaded_view</span>(<span class="name">req</span>, <span class="bn">int</span>(<span class="name">post_id</span>))
+
+ <span class="kw">def </span><span class="fun">_flat_view</span>(<span class="bn bn-pseudo">self</span>, <span class="name">req</span>, <span class="name">post_id</span>):
+ <span class="name">topic</span> <span class="op">=</span> <span class="name">get_flat_view</span>(<span class="name">req</span>, <span class="name">post_id</span>)
+ <span class="kw">if</span> <span class="name">topic</span> <span class="op op-word">is</span> <span class="bn bn-pseudo">None</span>:
+ <span class="kw">return</span> <span class="name">PageNotFound</span>()
+ <span class="kw">return</span> <span class="name">TemplateResponse</span>(<span class="st st-sg">&#39;</span><span class="st">viewtopic.html</span><span class="st st-sg">&#39;</span>,
+ <span class="name">topic</span><span class="op">=</span><span class="name">topic</span>,
+ <span class="name">pathbar</span><span class="op">=</span><span class="name">get_post_pathbar</span>(<span class="bn bn-pseudo">self</span>.<span class="name">ctx</span>, <span class="name">topic</span>[<span class="st st-sg">&#39;</span><span class="st">posts</span><span class="st st-sg">&#39;</span>][<span class="nb nb-int">0</span>][<span class="st st-sg">&#39;</span><span class="st">post_id</span><span class="st st-sg">&#39;</span>]),
+ <span class="name">feed_url</span><span class="op">=</span><span class="bn bn-pseudo">self</span>.<span class="name">ctx</span>.<span class="name">make_url</span>(<span class="st st-sg">&#39;</span><span class="st">feeds/thread/</span><span class="st st-int">%d</span><span class="st">.xml</span><span class="st st-sg">&#39;</span> <span class="op">%</span> <span class="name">post_id</span>)
+ )
+
+ <span class="kw">def </span><span class="fun">_threaded_view</span>(<span class="bn bn-pseudo">self</span>, <span class="name">req</span>, <span class="name">post_id</span>):
+ <span class="name">thread</span> <span class="op">=</span> <span class="name">get_post_tree</span>(<span class="name">req</span>, <span class="name">post_id</span>)
+ <span class="kw">if</span> <span class="name">thread</span> <span class="op op-word">is</span> <span class="bn bn-pseudo">None</span>:
+ <span class="kw">return</span> <span class="name">PageNotFound</span>()
+ <span class="kw">return</span> <span class="name">TemplateResponse</span>(<span class="st st-sg">&#39;</span><span class="st">viewthread.html</span><span class="st st-sg">&#39;</span>,
+ <span class="name">thread</span><span class="op">=</span><span class="name">thread</span>,
+ <span class="name">pathbar</span><span class="op">=</span><span class="name">get_post_pathbar</span>(<span class="bn bn-pseudo">self</span>.<span class="name">ctx</span>, <span class="name">thread</span>[<span class="st st-sg">&#39;</span><span class="st">posts</span><span class="st st-sg">&#39;</span>][<span class="nb nb-int">0</span>][<span class="st st-sg">&#39;</span><span class="st">post_id</span><span class="st st-sg">&#39;</span>]),
+ <span class="name">feed_url</span><span class="op">=</span><span class="bn bn-pseudo">self</span>.<span class="name">ctx</span>.<span class="name">make_url</span>(<span class="st st-sg">&#39;</span><span class="st">feeds/thread/</span><span class="st st-int">%d</span><span class="st">.xml</span><span class="st st-sg">&#39;</span> <span class="op">%</span> <span class="name">post_id</span>)
+ )
+
+ <span class="deco">@export</span>(<span class="st st-sg">&#39;</span><span class="st">thread.get_post</span><span class="st st-sg">&#39;</span>)
+ <span class="kw">def </span><span class="fun">_get_post</span>(<span class="bn bn-pseudo">self</span>, <span class="name">req</span>, <span class="name">post_id</span>):
+ <span class="name">post</span> <span class="op">=</span> <span class="name">get_post</span>(<span class="name">req</span>, <span class="name">post_id</span>)
+ <span class="kw">if</span> <span class="name">post</span> <span class="op op-word">is</span> <span class="bn bn-pseudo">None</span>:
+ <span class="kw">return</span>
+
+ <span class="kw">return</span> <span class="name">render_template</span>(<span class="name">req</span>, <span class="st st-sg">&#39;</span><span class="st">partial/post.html</span><span class="st st-sg">&#39;</span>, {
+ <span class="st st-sg">&#39;</span><span class="st">post</span><span class="st st-sg">&#39;</span>: <span class="name">post</span>
+ })
+
+ <span class="deco">@export</span>(<span class="st st-sg">&#39;</span><span class="st">thread.tree_requires_update</span><span class="st st-sg">&#39;</span>)
+ <span class="kw">def </span><span class="fun">_tree_requires_update</span>(<span class="bn bn-pseudo">self</span>, <span class="name">req</span>, <span class="name">post_id</span>, <span class="name">last_update</span>):
+ <span class="name">last_update</span> <span class="op">=</span> <span class="name">parse_datetime</span>(<span class="name">last_update</span>)
+ <span class="name">last_thread_change</span> <span class="op">=</span> <span class="name">get_last_thread_change</span>(<span class="name">req</span>, <span class="name">post_id</span>)
+ <span class="kw">print</span> <span class="name">last_thread_change</span>, <span class="name">last_update</span>
+
+ <span class="kw">return</span> <span class="name">last_thread_change</span> <span class="op op-word">is</span> <span class="op op-word">not</span> <span class="bn bn-pseudo">None</span> <span class="op op-word">and</span>\
+ <span class="name">last_thread_change</span> <span class="op">&gt;</span> <span class="name">last_update</span>
+
+ <span class="deco">@export</span>(<span class="st st-sg">&#39;</span><span class="st">thread.get_tree</span><span class="st st-sg">&#39;</span>)
+ <span class="kw">def </span><span class="fun">_get_tree</span>(<span class="bn bn-pseudo">self</span>, <span class="name">req</span>, <span class="name">post_id</span>):
+ <span class="kw">return</span> <span class="name">render_template</span>(<span class="name">req</span>, <span class="st st-sg">&#39;</span><span class="st">partial/tree.html</span><span class="st st-sg">&#39;</span>, {
+ <span class="st st-sg">&#39;</span><span class="st">posts</span><span class="st st-sg">&#39;</span>: <span class="name">get_post_tree</span>(<span class="name">req</span>, <span class="name">post_id</span>)[<span class="st st-sg">&#39;</span><span class="st">posts</span><span class="st st-sg">&#39;</span>]
+ })
+
+
+
+<span class="kw">class </span><span class="cls">LoginPage</span>(<span class="name">RequestHandler</span>, <span class="name">PagePublisher</span>):
+ <span class="name">page_name</span> <span class="op">=</span> <span class="st st-sg">&#39;</span><span class="st">login</span><span class="st st-sg">&#39;</span>
+ <span class="name">relative_url</span> <span class="op">=</span> <span class="st st-sg">&#39;</span><span class="st">login</span><span class="st st-sg">&#39;</span>
+
+ <span class="name">handler_regexes</span> <span class="op">=</span> [<span class="st st-sg">&#39;</span><span class="st">^login$</span><span class="st st-sg">&#39;</span>]
+
+ <span class="kw">def </span><span class="fun">handle_request</span>(<span class="bn bn-pseudo">self</span>, <span class="name">req</span>):
+ <span class="name">next</span> <span class="op">=</span> <span class="name">req</span>.<span class="name">values</span>.<span class="name">get</span>(<span class="st st-sg">&#39;</span><span class="st">next</span><span class="st st-sg">&#39;</span>, <span class="bn bn-pseudo">None</span>)
+ <span class="kw">if</span> <span class="name">next</span> <span class="op op-word">is</span> <span class="bn bn-pseudo">None</span>:
+ <span class="name">next</span> <span class="op">=</span> <span class="name">req</span>.<span class="name">environ</span>.<span class="name">get</span>(<span class="st st-sg">&#39;</span><span class="st">HTTP_REFERER</span><span class="st st-sg">&#39;</span>)
+ <span class="kw">if</span> <span class="op op-word">not</span> <span class="name">next</span>:
+ <span class="name">next</span> <span class="op">=</span> <span class="name">u</span><span class="st st-sg">&#39;&#39;</span>
+
+ <span class="name">_</span> <span class="op">=</span> <span class="name">req</span>.<span class="name">gettext</span>
+ <span class="name">msg</span> <span class="op">=</span> <span class="name">u</span><span class="st st-sg">&#39;&#39;</span>
+ <span class="kw">if</span> <span class="name">req</span>.<span class="name">user</span>.<span class="name">identified</span>:
+ <span class="kw">return</span> <span class="name">HttpRedirect</span>(<span class="name">next</span>)
+ <span class="name">form</span> <span class="op">=</span> <span class="name">Form</span>(<span class="name">req</span>, <span class="bn bn-pseudo">self</span>, <span class="st st-sg">&#39;</span><span class="st">POST</span><span class="st st-sg">&#39;</span>,
+ <span class="name">TextField</span>(<span class="st st-sg">&#39;</span><span class="st">username</span><span class="st st-sg">&#39;</span>, <span class="name">validator</span><span class="op">=</span><span class="name">isNotEmpty</span>()),
+ <span class="name">TextField</span>(<span class="st st-sg">&#39;</span><span class="st">password</span><span class="st st-sg">&#39;</span>, <span class="name">validator</span><span class="op">=</span><span class="name">isNotEmpty</span>())
+ )
+ <span class="kw">if</span> <span class="name">req</span>.<span class="name">method</span> <span class="op">==</span> <span class="st st-sg">&#39;</span><span class="st">POST</span><span class="st st-sg">&#39;</span>:
+ <span class="name">form</span>.<span class="name">update</span>(<span class="name">req</span>.<span class="name">form</span>, <span class="name">prefix</span><span class="op">=</span><span class="st st-sg">&#39;</span><span class="st">f_</span><span class="st st-sg">&#39;</span>)
+ <span class="kw">if</span> <span class="op op-word">not</span> <span class="name">form</span>.<span class="name">has_errors</span>:
+ <span class="name">d</span> <span class="op">=</span> <span class="name">form</span>.<span class="name">to_dict</span>()
+ <span class="name">login</span> <span class="op">=</span> <span class="name">req</span>.<span class="name">auth</span>.<span class="name">do_login</span>(<span class="name">d</span>[<span class="st st-sg">&#39;</span><span class="st">username</span><span class="st st-sg">&#39;</span>], <span class="name">d</span>[<span class="st st-sg">&#39;</span><span class="st">password</span><span class="st st-sg">&#39;</span>])
+ <span class="kw">if</span> <span class="name">login</span>:
+ <span class="kw">if</span> <span class="bn">isinstance</span>(<span class="name">login</span>, <span class="name">Response</span>):
+ <span class="kw">return</span> <span class="name">login</span>
+
+ <span class="kw">return</span> <span class="name">TemplateResponse</span>(<span class="st st-sg">&#39;</span><span class="st">messages/login.html</span><span class="st st-sg">&#39;</span>,
+ <span class="name">username</span><span class="op">=</span><span class="name">d</span>[<span class="st st-sg">&#39;</span><span class="st">username</span><span class="st st-sg">&#39;</span>],
+ <span class="name">next</span><span class="op">=</span><span class="name">next</span>
+
+ )
+ <span class="kw">else</span>:
+ <span class="name">msg</span> <span class="op">=</span> <span class="name">_</span>(<span class="st st-sg">&#39;</span><span class="st">Login failed. You may have entered an invalid </span><span class="st st-sg">&#39;</span>
+ <span class="st st-sg">&#39;</span><span class="st">username or password or your account is not </span><span class="st st-sg">&#39;</span>
+ <span class="st st-sg">&#39;</span><span class="st">activated yet.</span><span class="st st-sg">&#39;</span>)
+ <span class="kw">return</span> <span class="name">TemplateResponse</span>(<span class="st st-sg">&#39;</span><span class="st">login.html</span><span class="st st-sg">&#39;</span>,
+ <span class="name">msg</span><span class="op">=</span><span class="name">msg</span>,
+ <span class="name">form</span><span class="op">=</span><span class="name">form</span>.<span class="name">generate</span>(<span class="name">prefix</span><span class="op">=</span><span class="st st-sg">&#39;</span><span class="st">f_</span><span class="st st-sg">&#39;</span>),
+ <span class="name">next</span><span class="op">=</span><span class="name">next</span>
+
+ )
+
+
+<span class="kw">class </span><span class="cls">LogoutPage</span>(<span class="name">RequestHandler</span>, <span class="name">PagePublisher</span>):
+ <span class="name">page_name</span> <span class="op">=</span> <span class="st st-sg">&#39;</span><span class="st">logout</span><span class="st st-sg">&#39;</span>
+ <span class="name">relative_url</span> <span class="op">=</span> <span class="st st-sg">&#39;</span><span class="st">logout</span><span class="st st-sg">&#39;</span>
+
+ <span class="name">handler_regexes</span> <span class="op">=</span> [<span class="st st-sg">r&#39;</span><span class="st">logout$</span><span class="st st-sg">&#39;</span>]
+
+ <span class="kw">def </span><span class="fun">handle_request</span>(<span class="bn bn-pseudo">self</span>, <span class="name">req</span>):
+ <span class="name">back</span> <span class="op">=</span> <span class="name">req</span>.<span class="name">environ</span>.<span class="name">get</span>(<span class="st st-sg">&#39;</span><span class="st">HTTP_REFERER</span><span class="st st-sg">&#39;</span>, <span class="name">u</span><span class="st st-sg">&#39;&#39;</span>)
+ <span class="kw">try</span>:
+ <span class="name">back</span> <span class="op">=</span> <span class="name">make_url_context_external</span>(<span class="bn bn-pseudo">self</span>.<span class="name">ctx</span>, <span class="name">back</span>)
+ <span class="kw">except</span> <span class="exc">ValueError</span>:
+ <span class="name">back</span> <span class="op">=</span> <span class="bn bn-pseudo">None</span>
+
+ <span class="name">username</span> <span class="op">=</span> <span class="name">req</span>.<span class="name">user</span>.<span class="name">username</span>
+ <span class="name">req</span>.<span class="name">auth</span>.<span class="name">do_logout</span>()
+ <span class="kw">return</span> <span class="name">TemplateResponse</span>(<span class="st st-sg">&#39;</span><span class="st">messages/logout.html</span><span class="st st-sg">&#39;</span>,
+ <span class="name">username</span><span class="op">=</span><span class="name">username</span>,
+ <span class="name">back</span><span class="op">=</span><span class="name">back</span>
+
+ )
+
+
+<span class="kw">class </span><span class="cls">RegisterPage</span>(<span class="name">RequestHandler</span>, <span class="name">PagePublisher</span>):
+ <span class="name">page_name</span> <span class="op">=</span> <span class="st st-sg">&#39;</span><span class="st">register</span><span class="st st-sg">&#39;</span>
+ <span class="name">relative_url</span> <span class="op">=</span> <span class="st st-sg">&#39;</span><span class="st">register</span><span class="st st-sg">&#39;</span>
+
+ <span class="name">handler_regexes</span> <span class="op">=</span> [<span class="st st-sg">&#39;</span><span class="st">register$</span><span class="st st-sg">&#39;</span>]
+
+ <span class="kw">def </span><span class="fun">handle_request</span>(<span class="bn bn-pseudo">self</span>, <span class="name">req</span>):
+ <span class="name">coppa</span> <span class="op">=</span> <span class="name">req</span>.<span class="name">ctx</span>.<span class="name">cfg</span>.<span class="name">get_bool</span>(<span class="st st-sg">&#39;</span><span class="st">board</span><span class="st st-sg">&#39;</span>, <span class="st st-sg">&#39;</span><span class="st">enable_coppa</span><span class="st st-sg">&#39;</span>)
+ <span class="kw">if</span> <span class="name">req</span>.<span class="name">user</span>.<span class="name">identified</span>:
+ <span class="kw">return</span> <span class="name">HttpRedirect</span>(<span class="st st-sg">&#39;&#39;</span>)
+ <span class="kw">if</span> <span class="st st-sg">&#39;</span><span class="st">activate</span><span class="st st-sg">&#39;</span> <span class="op op-word">in</span> <span class="name">req</span>.<span class="name">args</span> <span class="op op-word">and</span> <span class="st st-sg">&#39;</span><span class="st">user</span><span class="st st-sg">&#39;</span> <span class="op op-word">in</span> <span class="name">req</span>.<span class="name">args</span>:
+ <span class="kw">return</span> <span class="bn bn-pseudo">self</span>.<span class="name">activation</span>(<span class="name">req</span>)
+ <span class="name">_</span> <span class="op">=</span> <span class="name">req</span>.<span class="name">gettext</span>
+
+ <span class="name">form</span> <span class="op">=</span> <span class="name">Form</span>(<span class="name">req</span>, <span class="bn bn-pseudo">self</span>, <span class="st st-sg">&#39;</span><span class="st">POST</span><span class="st st-sg">&#39;</span>,
+ <span class="name">TextField</span>(<span class="st st-sg">&#39;</span><span class="st">username</span><span class="st st-sg">&#39;</span>, <span class="name">validator</span><span class="op">=</span><span class="name">isAvailableUsername</span>()),
+ <span class="name">TextField</span>(<span class="st st-sg">&#39;</span><span class="st">password</span><span class="st st-sg">&#39;</span>, <span class="name">validator</span><span class="op">=</span><span class="name">isStrongPassword</span>()),
+ <span class="name">TextField</span>(<span class="st st-sg">&#39;</span><span class="st">password2</span><span class="st st-sg">&#39;</span>, <span class="name">validator</span><span class="op">=</span><span class="name">isSameValue</span>(<span class="st st-sg">&#39;</span><span class="st">password</span><span class="st st-sg">&#39;</span>,
+ <span class="name">_</span>(<span class="st st-sg">&#39;</span><span class="st">The passwords don</span><span class="st st-esc">\&#39;</span><span class="st">t match.</span><span class="st st-sg">&#39;</span>))),
+ <span class="name">TextField</span>(<span class="st st-sg">&#39;</span><span class="st">email</span><span class="st st-sg">&#39;</span>, <span class="name">validator</span><span class="op">=</span><span class="name">isEmail</span>()),
+ <span class="name">CheckBox</span>(<span class="st st-sg">&#39;</span><span class="st">coppa</span><span class="st st-sg">&#39;</span>, <span class="name">validator</span><span class="op">=</span><span class="name">coppaIsChecked</span>(<span class="name">coppa</span>)),
+ )
+ <span class="kw">if</span> <span class="name">req</span>.<span class="name">method</span> <span class="op">==</span> <span class="st st-sg">&#39;</span><span class="st">POST</span><span class="st st-sg">&#39;</span>:
+ <span class="name">form</span>.<span class="name">update</span>(<span class="name">req</span>.<span class="name">form</span>, <span class="name">prefix</span><span class="op">=</span><span class="st st-sg">&#39;</span><span class="st">f_</span><span class="st st-sg">&#39;</span>)
+ <span class="name">d</span> <span class="op">=</span> <span class="name">form</span>.<span class="name">to_dict</span>()
+ <span class="kw">if</span> <span class="op op-word">not</span> <span class="name">form</span>.<span class="name">has_errors</span>:
+ <span class="name">requires_activation</span> <span class="op">=</span> <span class="name">req</span>.<span class="name">ctx</span>.<span class="name">cfg</span>.<span class="name">get_bool</span>(<span class="st st-sg">&#39;</span><span class="st">board</span><span class="st st-sg">&#39;</span>, <span class="st st-sg">&#39;</span><span class="st">email_verification</span><span class="st st-sg">&#39;</span>)
+ <span class="name">user</span> <span class="op">=</span> <span class="name">User</span>.<span class="name">create</span>(<span class="name">req</span>.<span class="name">ctx</span>,
+ <span class="name">d</span>[<span class="st st-sg">&#39;</span><span class="st">username</span><span class="st st-sg">&#39;</span>],
+ <span class="name">d</span>[<span class="st st-sg">&#39;</span><span class="st">password</span><span class="st st-sg">&#39;</span>],
+ <span class="name">d</span>[<span class="st st-sg">&#39;</span><span class="st">email</span><span class="st st-sg">&#39;</span>],
+ <span class="name">requires_activation</span>
+
+ )
+ <span class="kw">if</span> <span class="name">requires_activation</span>:
+ <span class="name">link</span> <span class="op">=</span> <span class="bn bn-pseudo">self</span>.<span class="name">ctx</span>.<span class="name">make_external_url</span>(<span class="st st-sg">&#39;</span><span class="st">register?user=</span><span class="st st-int">%s</span><span class="st">&amp;key</span><span class="st st-int">%s</span><span class="st st-sg">&#39;</span> <span class="op">%</span>
+
+ (<span class="name">user</span>.<span class="name">user_id</span>, <span class="name">user</span>.<span class="name">act_key</span>))
+ <span class="name">txt</span> <span class="op">=</span> <span class="name">render_template</span>(<span class="name">req</span>, <span class="st st-sg">&#39;</span><span class="st">mails/welcome_verification.txt</span><span class="st st-sg">&#39;</span>, {
+ <span class="st st-sg">&#39;</span><span class="st">username</span><span class="st st-sg">&#39;</span>: <span class="name">d</span>[<span class="st st-sg">&#39;</span><span class="st">username</span><span class="st st-sg">&#39;</span>],
+ <span class="st st-sg">&#39;</span><span class="st">password</span><span class="st st-sg">&#39;</span>: <span class="name">d</span>[<span class="st st-sg">&#39;</span><span class="st">password</span><span class="st st-sg">&#39;</span>],
+ <span class="st st-sg">&#39;</span><span class="st">forum_name</span><span class="st st-sg">&#39;</span>: <span class="name">req</span>.<span class="name">ctx</span>.<span class="name">cfg</span>.<span class="name">get</span>(<span class="st st-sg">&#39;</span><span class="st">board</span><span class="st st-sg">&#39;</span>, <span class="st st-sg">&#39;</span><span class="st">title</span><span class="st st-sg">&#39;</span>),
+ <span class="st st-sg">&#39;</span><span class="st">activate_link</span><span class="st st-sg">&#39;</span>: <span class="name">link</span>
+
+ })
+ <span class="name">mail</span> <span class="op">=</span> <span class="name">Email</span>(<span class="name">req</span>.<span class="name">ctx</span>, <span class="name">_</span>(<span class="st st-sg">&#39;</span><span class="st">Welcome to the </span><span class="st st-int">%s</span><span class="st st-sg">&#39;</span>) <span class="op">%</span>
+
+ <span class="name">req</span>.<span class="name">ctx</span>.<span class="name">cfg</span>.<span class="name">get</span>(<span class="st st-sg">&#39;</span><span class="st">board</span><span class="st st-sg">&#39;</span>, <span class="st st-sg">&#39;</span><span class="st">title</span><span class="st st-sg">&#39;</span>),
+ <span class="name">d</span>[<span class="st st-sg">&#39;</span><span class="st">email</span><span class="st st-sg">&#39;</span>], <span class="name">txt</span>)
+ <span class="name">mail</span>.<span class="name">send</span>()
+ <span class="kw">return</span> <span class="name">TemplateResponse</span>(<span class="st st-sg">&#39;</span><span class="st">messages/register.html</span><span class="st st-sg">&#39;</span>,
+ <span class="name">requires_activation</span><span class="op">=</span><span class="name">requires_activation</span>,
+ <span class="name">username</span><span class="op">=</span><span class="name">d</span>[<span class="st st-sg">&#39;</span><span class="st">username</span><span class="st st-sg">&#39;</span>],
+ <span class="name">email</span><span class="op">=</span><span class="name">d</span>[<span class="st st-sg">&#39;</span><span class="st">email</span><span class="st st-sg">&#39;</span>]
+ )
+ <span class="kw">return</span> <span class="name">TemplateResponse</span>(<span class="st st-sg">&#39;</span><span class="st">register.html</span><span class="st st-sg">&#39;</span>,
+ <span class="name">form</span><span class="op">=</span><span class="name">form</span>.<span class="name">generate</span>(<span class="name">prefix</span><span class="op">=</span><span class="st st-sg">&#39;</span><span class="st">f_</span><span class="st st-sg">&#39;</span>),
+ )
+
+ <span class="kw">def </span><span class="fun">activation</span>(<span class="bn bn-pseudo">self</span>, <span class="name">req</span>):
+ <span class="name">_</span> <span class="op">=</span> <span class="name">req</span>.<span class="name">gettext</span>
+
+ <span class="name">uid</span> <span class="op">=</span> <span class="bn">int</span>(<span class="name">req</span>.<span class="name">args</span>[<span class="st st-sg">&#39;</span><span class="st">user</span><span class="st st-sg">&#39;</span>])
+ <span class="name">activated</span> <span class="op">=</span> <span class="bn bn-pseudo">False</span>
+
+ <span class="name">username</span> <span class="op">=</span> <span class="name">u</span><span class="st st-sg">&#39;&#39;</span>
+ <span class="kw">try</span>:
+ <span class="name">user</span> <span class="op">=</span> <span class="name">User</span>(<span class="name">req</span>.<span class="name">ctx</span>, <span class="name">uid</span>)
+ <span class="name">key</span> <span class="op">=</span> <span class="name">user</span>.<span class="name">act_key</span>
+
+ <span class="kw">except</span> <span class="exc">KeyError</span>:
+ <span class="kw">pass</span>
+ <span class="kw">else</span>:
+ <span class="kw">if</span> <span class="op op-word">not</span> <span class="name">user</span>.<span class="name">active</span> <span class="op op-word">and</span> <span class="name">req</span>.<span class="name">args</span>[<span class="st st-sg">&#39;</span><span class="st">activate</span><span class="st st-sg">&#39;</span>] <span class="op">==</span> <span class="name">key</span>:
+ <span class="name">user</span>.<span class="name">activate</span>()
+ <span class="name">activated</span> <span class="op">=</span> <span class="bn bn-pseudo">True</span>
+
+ <span class="name">username</span> <span class="op">=</span> <span class="name">user</span>.<span class="name">username</span>
+ <span class="kw">return</span> <span class="name">TemplateResponse</span>(<span class="st st-sg">&#39;</span><span class="st">messages/activation.html</span><span class="st st-sg">&#39;</span>,
+ <span class="name">username</span><span class="op">=</span><span class="name">username</span>,
+ <span class="name">activated</span><span class="op">=</span><span class="name">activated</span>
+
+ )
+
+
+<span class="kw">class </span><span class="cls">NewPostPage</span>(<span class="name">RequestHandler</span>, <span class="name">RemoteCallable</span>):
+ <span class="name">handler_regexes</span> <span class="op">=</span> [
+ (<span class="st st-sg">r&#39;</span><span class="st">post/(?P&lt;post_id&gt;\d+)/reply$</span><span class="st st-sg">&#39;</span>,
+ {<span class="st st-sg">&#39;</span><span class="st">action</span><span class="st st-sg">&#39;</span>:<span class="st st-sg">&#39;</span><span class="st">reply</span><span class="st st-sg">&#39;</span>}),
+ (<span class="st st-sg">r&#39;</span><span class="st">post/(?P&lt;post_id&gt;\d+)/quote$</span><span class="st st-sg">&#39;</span>,
+ {<span class="st st-sg">&#39;</span><span class="st">action</span><span class="st st-sg">&#39;</span>:<span class="st st-sg">&#39;</span><span class="st">quote</span><span class="st st-sg">&#39;</span>}),
+ (<span class="st st-sg">r&#39;</span><span class="st">post/(?P&lt;post_id&gt;\d+)/edit$</span><span class="st st-sg">&#39;</span>,
+ {<span class="st st-sg">&#39;</span><span class="st">action</span><span class="st st-sg">&#39;</span>:<span class="st st-sg">&#39;</span><span class="st">edit</span><span class="st st-sg">&#39;</span>})
+ ]
+
+ <span class="kw">def </span><span class="fun">handle_request</span>(<span class="bn bn-pseudo">self</span>, <span class="name">req</span>, <span class="name">action</span>, <span class="name">post_id</span>):
+ <span class="kw">try</span>:
+ <span class="name">thread</span> <span class="op">=</span> <span class="name">Thread</span>.<span class="name">by_child</span>(<span class="bn bn-pseudo">self</span>.<span class="name">ctx</span>, <span class="name">post_id</span>)
+ <span class="kw">except</span> <span class="exc">ValueError</span>:
+ <span class="kw">return</span> <span class="name">PageNotFound</span>()
+ <span class="name">_</span> <span class="op">=</span> <span class="name">req</span>.<span class="name">gettext</span>
+
+ <span class="cm"># note: quote_post can raise a ValueError when a post does</span>
+ <span class="cm"># not exist. but since we check for that by calling</span>
+ <span class="cm"># Thread.by_child this shouldn&#39;t happen.</span>
+ <span class="name">username</span> <span class="op">=</span> <span class="name">_</span>(<span class="st st-sg">&#39;</span><span class="st">anonymous</span><span class="st st-sg">&#39;</span>)
+ <span class="kw">if</span> <span class="name">action</span> <span class="op">==</span> <span class="st st-sg">&#39;</span><span class="st">reply</span><span class="st st-sg">&#39;</span>:
+ <span class="name">mode</span> <span class="op">=</span> <span class="st st-sg">&#39;</span><span class="st">reply</span><span class="st st-sg">&#39;</span>
+
+ <span class="kw">if</span> <span class="op op-word">not</span> <span class="name">req</span>.<span class="name">user</span>.<span class="name">acl</span>.<span class="name">can_access</span>(<span class="st st-sg">&#39;</span><span class="st">REPLY_POST</span><span class="st st-sg">&#39;</span>, <span class="name">thread</span>):
+ <span class="kw">return</span> <span class="name">AccessDeniedResponse</span>()
+ <span class="name">text</span> <span class="op">=</span> <span class="name">u</span><span class="st st-sg">&#39;&#39;</span>
+
+ <span class="name">_</span>, <span class="name">title</span> <span class="op">=</span> <span class="name">quote_post</span>(<span class="name">req</span>, <span class="name">post_id</span>)
+ <span class="kw">elif</span> <span class="name">action</span> <span class="op">==</span> <span class="st st-sg">&#39;</span><span class="st">quote</span><span class="st st-sg">&#39;</span>:
+ <span class="name">mode</span> <span class="op">=</span> <span class="st st-sg">&#39;</span><span class="st">reply</span><span class="st st-sg">&#39;</span>
+
+ <span class="cm">#XXX: maybe we could use REPLY_POST here too</span>
+ <span class="kw">if</span> <span class="op op-word">not</span> <span class="name">req</span>.<span class="name">user</span>.<span class="name">acl</span>.<span class="name">can_access</span>(<span class="st st-sg">&#39;</span><span class="st">QUOTE_POST</span><span class="st st-sg">&#39;</span>, <span class="name">thread</span>):
+ <span class="kw">return</span> <span class="name">AccessDeniedResponse</span>()
+ <span class="name">text</span>, <span class="name">title</span> <span class="op">=</span> <span class="name">quote_post</span>(<span class="name">req</span>, <span class="name">post_id</span>)
+ <span class="kw">else</span>:
+ <span class="name">mode</span> <span class="op">=</span> <span class="st st-sg">&#39;</span><span class="st">edit</span><span class="st st-sg">&#39;</span>
+
+ <span class="kw">if</span> <span class="op op-word">not</span> <span class="name">req</span>.<span class="name">user</span>.<span class="name">acl</span>.<span class="name">can_access</span>(<span class="st st-sg">&#39;</span><span class="st">EDIT_POST</span><span class="st st-sg">&#39;</span>, <span class="name">thread</span>):
+ <span class="kw">return</span> <span class="name">AccessDeniedResponse</span>()
+ <span class="name">text</span>, <span class="name">title</span>, <span class="name">username</span> <span class="op">=</span> <span class="name">edit_post</span>(<span class="name">req</span>, <span class="name">post_id</span>)
+
+ <span class="name">form</span> <span class="op">=</span> <span class="name">Form</span>(<span class="name">req</span>, <span class="name">req</span>.<span class="name">environ</span>[<span class="st st-sg">&#39;</span><span class="st">APPLICATION_REQUEST</span><span class="st st-sg">&#39;</span>], <span class="st st-sg">&#39;</span><span class="st">POST</span><span class="st st-sg">&#39;</span>,
+ <span class="name">TextField</span>(<span class="st st-sg">&#39;</span><span class="st">username</span><span class="st st-sg">&#39;</span>, <span class="name">validator</span><span class="op">=</span><span class="name">isAnonymousUsername</span>(),
+ <span class="name">default</span><span class="op">=</span><span class="name">username</span>),
+ <span class="name">TextField</span>(<span class="st st-sg">&#39;</span><span class="st">title</span><span class="st st-sg">&#39;</span>, <span class="name">validator</span><span class="op">=</span><span class="name">checkTextLength</span>(<span class="nb nb-int">3</span>, <span class="nb nb-int">60</span>),
+ <span class="name">default</span><span class="op">=</span><span class="name">title</span>),
+ <span class="name">TextArea</span>(<span class="st st-sg">&#39;</span><span class="st">text</span><span class="st st-sg">&#39;</span>, <span class="name">validator</span><span class="op">=</span><span class="name">checkTextLength</span>(<span class="nb nb-int">3</span>, <span class="nb nb-int">10000</span>),
+ <span class="name">default</span><span class="op">=</span><span class="name">text</span>)
+ )
+ <span class="kw">if</span> <span class="name">req</span>.<span class="name">method</span> <span class="op">==</span> <span class="st st-sg">&#39;</span><span class="st">POST</span><span class="st st-sg">&#39;</span>:
+ <span class="name">form</span>.<span class="name">update</span>(<span class="name">req</span>.<span class="name">form</span>, <span class="name">prefix</span><span class="op">=</span><span class="st st-sg">&#39;</span><span class="st">f_</span><span class="st st-sg">&#39;</span>)
+ <span class="kw">if</span> <span class="op op-word">not</span> <span class="name">form</span>.<span class="name">has_errors</span>:
+ <span class="name">d</span> <span class="op">=</span> <span class="name">form</span>.<span class="name">to_dict</span>()
+ <span class="kw">if</span> <span class="name">req</span>.<span class="name">user</span>.<span class="name">identified</span>:
+ <span class="name">author</span> <span class="op">=</span> <span class="name">req</span>.<span class="name">user</span>.<span class="name">user_id</span>
+
+ <span class="kw">else</span>:
+ <span class="name">author</span> <span class="op">=</span> <span class="name">d</span>[<span class="st st-sg">&#39;</span><span class="st">username</span><span class="st st-sg">&#39;</span>]
+ <span class="kw">if</span> <span class="name">action</span> <span class="op op-word">in</span> (<span class="st st-sg">&#39;</span><span class="st">quote</span><span class="st st-sg">&#39;</span>, <span class="st st-sg">&#39;</span><span class="st">reply</span><span class="st st-sg">&#39;</span>):
+ <span class="name">new_post_id</span> <span class="op">=</span> <span class="name">thread</span>.<span class="name">reply</span>(
+ <span class="name">post_id</span> <span class="op">=</span> <span class="bn">int</span>(<span class="name">post_id</span>),
+ <span class="name">author</span> <span class="op">=</span> <span class="name">author</span>,
+ <span class="name">title</span> <span class="op">=</span> <span class="name">d</span>[<span class="st st-sg">&#39;</span><span class="st">title</span><span class="st st-sg">&#39;</span>],
+ <span class="name">text</span> <span class="op">=</span> <span class="name">d</span>[<span class="st st-sg">&#39;</span><span class="st">text</span><span class="st st-sg">&#39;</span>]
+ )
+ <span class="kw">elif</span> <span class="name">action</span> <span class="op">==</span> <span class="st st-sg">&#39;</span><span class="st">edit</span><span class="st st-sg">&#39;</span>:
+ <span class="name">new_post_id</span> <span class="op">=</span> <span class="bn">int</span>(<span class="name">post_id</span>)
+ <span class="name">thread</span>.<span class="name">edit_reply</span>(
+ <span class="name">post_id</span> <span class="op">=</span> <span class="name">new_post_id</span>,
+ <span class="name">author</span> <span class="op">=</span> <span class="name">author</span>,
+ <span class="name">title</span> <span class="op">=</span> <span class="name">d</span>[<span class="st st-sg">&#39;</span><span class="st">title</span><span class="st st-sg">&#39;</span>],
+ <span class="name">text</span> <span class="op">=</span> <span class="name">d</span>[<span class="st st-sg">&#39;</span><span class="st">text</span><span class="st st-sg">&#39;</span>]
+ )
+ <span class="kw">return</span> <span class="name">TemplateResponse</span>(<span class="st st-sg">&#39;</span><span class="st">messages/post.html</span><span class="st st-sg">&#39;</span>,
+ <span class="name">mode</span> <span class="op">=</span> <span class="name">mode</span>,
+ <span class="name">post</span> <span class="op">=</span> {
+ <span class="st st-sg">&#39;</span><span class="st">id</span><span class="st st-sg">&#39;</span>: <span class="name">new_post_id</span>,
+ <span class="st st-sg">&#39;</span><span class="st">title</span><span class="st st-sg">&#39;</span>: <span class="name">d</span>[<span class="st st-sg">&#39;</span><span class="st">title</span><span class="st st-sg">&#39;</span>],
+ <span class="st st-sg">&#39;</span><span class="st">url</span><span class="st st-sg">&#39;</span>: <span class="bn bn-pseudo">self</span>.<span class="name">ctx</span>.<span class="name">make_url</span>(<span class="st st-sg">&#39;</span><span class="st">post</span><span class="st st-sg">&#39;</span>, <span class="name">new_post_id</span>),
+ }
+ )
+ <span class="name">js</span>, <span class="name">options</span> <span class="op">=</span> <span class="name">get_editor</span>(<span class="name">req</span>)
+ <span class="name">latest_posts</span> <span class="op">=</span> <span class="name">get_last_posts</span>(<span class="name">req</span>, <span class="name">thread</span>.<span class="name">root_post_id</span>, <span class="nb nb-int">5</span>)
+ <span class="kw">return</span> <span class="name">TemplateResponse</span>(<span class="st st-sg">&#39;</span><span class="st">newpost.html</span><span class="st st-sg">&#39;</span>,
+ <span class="name">mode</span> <span class="op">=</span> <span class="name">mode</span>,
+ <span class="name">form</span> <span class="op">=</span> <span class="name">form</span>.<span class="name">generate</span>(<span class="name">prefix</span><span class="op">=</span><span class="st st-sg">&#39;</span><span class="st">f_</span><span class="st st-sg">&#39;</span>),
+ <span class="name">show_username_entry</span> <span class="op">=</span> <span class="op op-word">not</span> <span class="name">req</span>.<span class="name">user</span>.<span class="name">identified</span>,
+ <span class="name">editor_options</span> <span class="op">=</span> <span class="name">options</span>,
+ <span class="name">editor_javascript</span> <span class="op">=</span> <span class="name">js</span>,
+ <span class="name">post_id</span> <span class="op">=</span> <span class="name">thread</span>.<span class="name">root_post_id</span>,
+ <span class="name">pathbar</span><span class="op">=</span><span class="name">get_post_pathbar</span>(<span class="name">req</span>.<span class="name">ctx</span>, <span class="name">post_id</span>),
+ <span class="name">latest_posts</span> <span class="op">=</span> <span class="name">latest_posts</span>
+
+ )
+
+ <span class="deco">@export</span>(<span class="st st-db">&quot;</span><span class="st">newpost.preview</span><span class="st st-db">&quot;</span>)
+ <span class="kw">def </span><span class="fun">preview</span>(<span class="bn bn-pseudo">self</span>, <span class="name">req</span>, <span class="name">text</span>):
+ <span class="kw">return</span> <span class="name">parse_and_render</span>(<span class="name">req</span>, <span class="name">text</span>)
+
+ <span class="deco">@export</span>(<span class="st st-db">&quot;</span><span class="st">newpost.check</span><span class="st st-db">&quot;</span>)
+ <span class="kw">def </span><span class="fun">check</span>(<span class="bn bn-pseudo">self</span>, <span class="name">req</span>, <span class="name">root_post_id</span>, <span class="name">latest_post</span>, <span class="name">path</span>):
+ <span class="cm"># check for new posts</span>
+
+ <span class="name">new_posts</span> <span class="op">=</span> <span class="bn bn-pseudo">False</span>
+ <span class="name">post</span> <span class="op">=</span> <span class="name">get_last_posts</span>(<span class="name">req</span>, <span class="name">root_post_id</span>, <span class="nb nb-int">1</span>)[<span class="nb nb-int">0</span>]
+ <span class="name">post_list</span> <span class="op">=</span> []
+ <span class="kw">if</span> <span class="name">latest_post</span> <span class="op">!=</span> <span class="name">post</span>[<span class="st st-db">&quot;</span><span class="st">post_id</span><span class="st st-db">&quot;</span>]:
+ <span class="name">z</span> <span class="op">=</span> <span class="nb nb-int">1</span>
+
+ <span class="kw">while</span> <span class="name">post</span>[<span class="st st-db">&quot;</span><span class="st">post_id</span><span class="st st-db">&quot;</span>] <span class="op">!=</span> <span class="name">latest_post</span> <span class="op op-word">and</span> <span class="name">z</span> <span class="op">&lt;</span> <span class="nb nb-int">5</span>:
+ <span class="name">post_list</span>.<span class="name">append</span>(<span class="name">post</span>)
+ <span class="name">post</span> <span class="op">=</span> <span class="name">get_last_posts</span>(<span class="name">req</span>, <span class="name">root_post_id</span>, <span class="nb nb-int">1</span>, <span class="name">z</span>)[<span class="nb nb-int">0</span>]
+ <span class="name">z</span> <span class="op">+=</span> <span class="nb nb-int">1</span>
+
+ <span class="name">new_posts</span> <span class="op">=</span> {
+ <span class="st st-sg">&#39;</span><span class="st">html</span><span class="st st-sg">&#39;</span>: <span class="name">render_template</span>(<span class="name">req</span>, <span class="st st-sg">&#39;</span><span class="st">latestposts.html</span><span class="st st-sg">&#39;</span>, {<span class="st st-sg">&#39;</span><span class="st">posts</span><span class="st st-sg">&#39;</span>: <span class="name">post_list</span>}),
+ <span class="st st-sg">&#39;</span><span class="st">last_post</span><span class="st st-sg">&#39;</span>: <span class="name">post_list</span>[<span class="nb nb-int">0</span>][<span class="st st-db">&quot;</span><span class="st">post_id</span><span class="st st-db">&quot;</span>]
+ }
+
+ <span class="cm"># check for new editors</span>
+
+ <span class="name">new_editors</span> <span class="op">=</span> <span class="name">get_sessions_by_action</span>(<span class="name">req</span>.<span class="name">ctx</span>, <span class="name">path</span>) <span class="op op-word">or</span> <span class="bn bn-pseudo">False</span>
+
+ <span class="kw">return</span> (<span class="name">post_list</span> <span class="op op-word">or</span> <span class="name">new_editors</span>) <span class="op op-word">and</span> {
+ <span class="st st-sg">&#39;</span><span class="st">posts</span><span class="st st-sg">&#39;</span>: <span class="name">new_posts</span>,
+ <span class="st st-sg">&#39;</span><span class="st">editors</span><span class="st st-sg">&#39;</span>: <span class="name">new_editors</span>
+
+ } <span class="op op-word">or</span> <span class="bn bn-pseudo">False</span>
+
+
+<span class="kw">class </span><span class="cls">NewThreadPage</span>(<span class="name">RequestHandler</span>):
+ <span class="name">handler_regexes</span> <span class="op">=</span> [<span class="st st-sg">r&#39;</span><span class="st">forum/(?P&lt;forum_id&gt;\d+)/new$</span><span class="st st-sg">&#39;</span>]
+
+ <span class="kw">def </span><span class="fun">handle_request</span>(<span class="bn bn-pseudo">self</span>, <span class="name">req</span>, <span class="name">forum_id</span>):
+ <span class="cm"># TODO: Check whether user is allowed to do this</span>
+
+ <span class="name">_</span> <span class="op">=</span> <span class="name">req</span>.<span class="name">gettext</span>
+ <span class="name">forum_id</span> <span class="op">=</span> <span class="bn">int</span>(<span class="name">forum_id</span>)
+ <span class="name">form</span> <span class="op">=</span> <span class="name">Form</span>(<span class="name">req</span>, <span class="name">req</span>.<span class="name">environ</span>[<span class="st st-sg">&#39;</span><span class="st">APPLICATION_REQUEST</span><span class="st st-sg">&#39;</span>], <span class="st st-sg">&#39;</span><span class="st">POST</span><span class="st st-sg">&#39;</span>,
+ <span class="name">TextField</span>(<span class="st st-sg">&#39;</span><span class="st">username</span><span class="st st-sg">&#39;</span>, <span class="name">validator</span><span class="op">=</span><span class="name">isAnonymousUsername</span>(),
+ <span class="name">default</span><span class="op">=</span><span class="name">_</span>(<span class="st st-sg">&#39;</span><span class="st">anonymous</span><span class="st st-sg">&#39;</span>)),
+ <span class="name">TextField</span>(<span class="st st-sg">&#39;</span><span class="st">title</span><span class="st st-sg">&#39;</span>, <span class="name">validator</span><span class="op">=</span><span class="name">checkTextLength</span>(<span class="nb nb-int">3</span>, <span class="nb nb-int">60</span>)),
+ <span class="name">TextArea</span>(<span class="st st-sg">&#39;</span><span class="st">text</span><span class="st st-sg">&#39;</span>, <span class="name">validator</span><span class="op">=</span><span class="name">checkTextLength</span>(<span class="nb nb-int">3</span>, <span class="nb nb-int">10000</span>))
+ )
+ <span class="kw">if</span> <span class="name">req</span>.<span class="name">method</span> <span class="op">==</span> <span class="st st-sg">&#39;</span><span class="st">POST</span><span class="st st-sg">&#39;</span>:
+ <span class="name">form</span>.<span class="name">update</span>(<span class="name">req</span>.<span class="name">form</span>, <span class="name">prefix</span><span class="op">=</span><span class="st st-sg">&#39;</span><span class="st">f_</span><span class="st st-sg">&#39;</span>)
+ <span class="kw">if</span> <span class="op op-word">not</span> <span class="name">form</span>.<span class="name">has_errors</span>:
+ <span class="name">d</span> <span class="op">=</span> <span class="name">form</span>.<span class="name">to_dict</span>()
+ <span class="kw">if</span> <span class="name">req</span>.<span class="name">user</span>.<span class="name">identified</span>:
+ <span class="name">author</span> <span class="op">=</span> <span class="name">req</span>.<span class="name">user</span>.<span class="name">user_id</span>
+
+ <span class="kw">else</span>:
+ <span class="name">author</span> <span class="op">=</span> <span class="name">d</span>[<span class="st st-sg">&#39;</span><span class="st">username</span><span class="st st-sg">&#39;</span>]
+ <span class="name">thread</span> <span class="op">=</span> <span class="name">Thread</span>.<span class="name">create</span>(<span class="name">req</span>.<span class="name">ctx</span>, <span class="name">forum_id</span>, <span class="name">author</span>,
+ <span class="name">d</span>[<span class="st st-sg">&#39;</span><span class="st">title</span><span class="st st-sg">&#39;</span>], <span class="name">d</span>[<span class="st st-sg">&#39;</span><span class="st">text</span><span class="st st-sg">&#39;</span>])
+ <span class="kw">return</span> <span class="name">TemplateResponse</span>(<span class="st st-sg">&#39;</span><span class="st">messages/thread.html</span><span class="st st-sg">&#39;</span>,
+ <span class="name">thread</span> <span class="op">=</span> {
+ <span class="st st-sg">&#39;</span><span class="st">title</span><span class="st st-sg">&#39;</span>: <span class="name">thread</span>.<span class="name">title</span>,
+ <span class="st st-sg">&#39;</span><span class="st">url</span><span class="st st-sg">&#39;</span>: <span class="name">req</span>.<span class="name">ctx</span>.<span class="name">make_url</span>(<span class="st st-sg">&#39;</span><span class="st">post</span><span class="st st-sg">&#39;</span>, <span class="name">thread</span>.<span class="name">root_post_id</span>),
+ }
+ )
+
+ <span class="name">js</span>, <span class="name">options</span> <span class="op">=</span> <span class="name">get_editor</span>(<span class="name">req</span>)
+ <span class="kw">return</span> <span class="name">TemplateResponse</span>(<span class="st st-sg">&#39;</span><span class="st">newthread.html</span><span class="st st-sg">&#39;</span>,
+ <span class="name">form</span> <span class="op">=</span> <span class="name">form</span>.<span class="name">generate</span>(<span class="name">prefix</span><span class="op">=</span><span class="st st-sg">&#39;</span><span class="st">f_</span><span class="st st-sg">&#39;</span>),
+ <span class="name">show_username_entry</span> <span class="op">=</span> <span class="op op-word">not</span> <span class="name">req</span>.<span class="name">user</span>.<span class="name">identified</span>,
+ <span class="name">editor_options</span> <span class="op">=</span> <span class="name">options</span>,
+ <span class="name">editor_javascript</span> <span class="op">=</span> <span class="name">js</span>,
+ <span class="name">pathbar</span> <span class="op">=</span> <span class="name">get_forum_pathbar</span>(<span class="bn bn-pseudo">self</span>.<span class="name">ctx</span>, <span class="name">forum_id</span>)
+ )
+
+
+
+<span class="kw">class </span><span class="cls">MemberListPage</span>(<span class="name">RequestHandler</span>, <span class="name">PagePublisher</span>):
+ <span class="name">page_name</span> <span class="op">=</span> <span class="st st-sg">&#39;</span><span class="st">memberlist</span><span class="st st-sg">&#39;</span>
+ <span class="name">relative_url</span> <span class="op">=</span> <span class="st st-sg">&#39;</span><span class="st">users/</span><span class="st st-sg">&#39;</span>
+
+ <span class="name">handler_regexes</span> <span class="op">=</span> [<span class="st st-sg">r&#39;</span><span class="st">users/$</span><span class="st st-sg">&#39;</span>]
+
+ <span class="kw">def </span><span class="fun">handle_request</span>(<span class="bn bn-pseudo">self</span>, <span class="name">req</span>):
+ <span class="name">_</span> <span class="op">=</span> <span class="name">req</span>.<span class="name">gettext</span>
+
+ <span class="name">generate</span> <span class="op">=</span> <span class="bn bn-pseudo">False</span>
+
+ <span class="name">form</span> <span class="op">=</span> <span class="name">Form</span>(<span class="name">req</span>, <span class="bn bn-pseudo">self</span>, <span class="st st-sg">&#39;</span><span class="st">GET</span><span class="st st-sg">&#39;</span>,
+ <span class="name">TextField</span>(<span class="st st-sg">&#39;</span><span class="st">letter</span><span class="st st-sg">&#39;</span>, <span class="name">validator</span><span class="op">=</span><span class="name">mayEmpty</span>(<span class="name">isOneLetter</span>())),
+ <span class="name">SelectBox</span>(<span class="st st-sg">&#39;</span><span class="st">order_by</span><span class="st st-sg">&#39;</span>, [
+ (<span class="st st-sg">&#39;</span><span class="st">user_id</span><span class="st st-sg">&#39;</span>, <span class="name">_</span>(<span class="st st-sg">&#39;</span><span class="st">User ID</span><span class="st st-sg">&#39;</span>)),
+ (<span class="st st-sg">&#39;</span><span class="st">username</span><span class="st st-sg">&#39;</span>, <span class="name">_</span>(<span class="st st-sg">&#39;</span><span class="st">Username</span><span class="st st-sg">&#39;</span>)),
+ (<span class="st st-sg">&#39;</span><span class="st">register_date</span><span class="st st-sg">&#39;</span>, <span class="name">_</span>(<span class="st st-sg">&#39;</span><span class="st">Register date</span><span class="st st-sg">&#39;</span>)),
+ (<span class="st st-sg">&#39;</span><span class="st">post_count</span><span class="st st-sg">&#39;</span>, <span class="name">_</span>(<span class="st st-sg">&#39;</span><span class="st">Number of Posts</span><span class="st st-sg">&#39;</span>))
+ ], <span class="name">default</span><span class="op">=</span><span class="st st-sg">&#39;</span><span class="st">user_id</span><span class="st st-sg">&#39;</span>),
+ <span class="name">SelectBox</span>(<span class="st st-sg">&#39;</span><span class="st">direction</span><span class="st st-sg">&#39;</span>, [
+ (<span class="st st-sg">&#39;</span><span class="st">desc</span><span class="st st-sg">&#39;</span>, <span class="name">_</span>(<span class="st st-sg">&#39;</span><span class="st">Descending</span><span class="st st-sg">&#39;</span>)),
+ (<span class="st st-sg">&#39;</span><span class="st">asc</span><span class="st st-sg">&#39;</span>, <span class="name">_</span>(<span class="st st-sg">&#39;</span><span class="st">Ascending</span><span class="st st-sg">&#39;</span>))
+ ], <span class="name">default</span><span class="op">=</span><span class="st st-sg">&#39;</span><span class="st">asc</span><span class="st st-sg">&#39;</span>)
+ )
+
+ <span class="kw">if</span> <span class="st st-sg">&#39;</span><span class="st">letter</span><span class="st st-sg">&#39;</span> <span class="op op-word">in</span> <span class="name">req</span>.<span class="name">args</span> <span class="op op-word">or</span> <span class="st st-sg">&#39;</span><span class="st">order_by</span><span class="st st-sg">&#39;</span> <span class="op op-word">in</span> <span class="name">req</span>.<span class="name">args</span> \
+ <span class="op op-word">or</span> <span class="st st-sg">&#39;</span><span class="st">direction</span><span class="st st-sg">&#39;</span> <span class="op op-word">in</span> <span class="name">req</span>.<span class="name">args</span>:
+ <span class="name">form</span>.<span class="name">update</span>(<span class="name">req</span>.<span class="name">args</span>)
+ <span class="kw">if</span> <span class="op op-word">not</span> <span class="name">form</span>.<span class="name">has_errors</span>:
+ <span class="name">generate</span> <span class="op">=</span> <span class="bn bn-pseudo">True</span>
+
+ <span class="kw">else</span>:
+ <span class="name">generate</span> <span class="op">=</span> <span class="bn bn-pseudo">True</span>
+ <span class="kw">if</span> <span class="name">generate</span>:
+ <span class="name">d</span> <span class="op">=</span> <span class="name">form</span>.<span class="name">to_dict</span>()
+ <span class="name">userlist</span> <span class="op">=</span> <span class="name">get_user_list</span>(<span class="bn bn-pseudo">self</span>.<span class="name">ctx</span>,
+ <span class="name">order_by</span><span class="op">=</span><span class="bn">getattr</span>(<span class="name">users</span>.<span class="name">c</span>, <span class="name">d</span>[<span class="st st-sg">&#39;</span><span class="st">order_by</span><span class="st st-sg">&#39;</span>]),
+ <span class="name">order</span><span class="op">=</span><span class="name">d</span>[<span class="st st-sg">&#39;</span><span class="st">direction</span><span class="st st-sg">&#39;</span>],
+ <span class="name">letter</span><span class="op">=</span><span class="name">d</span>[<span class="st st-sg">&#39;</span><span class="st">letter</span><span class="st st-sg">&#39;</span>] <span class="op op-word">or</span> <span class="bn bn-pseudo">None</span>,
+ <span class="name">hide_internal</span><span class="op">=</span><span class="bn bn-pseudo">True</span>
+
+ )
+ <span class="kw">else</span>:
+ <span class="name">userlist</span> <span class="op">=</span> []
+
+ <span class="kw">return</span> <span class="name">TemplateResponse</span>(<span class="st st-sg">&#39;</span><span class="st">memberlist.html</span><span class="st st-sg">&#39;</span>,
+ <span class="bn">list</span><span class="op">=</span><span class="name">userlist</span>,
+ <span class="name">form</span><span class="op">=</span><span class="name">form</span>.<span class="name">generate</span>()
+ )
+
+
+
+<span class="kw">class </span><span class="cls">UserPage</span>(<span class="name">RequestHandler</span>):
+ <span class="name">handler_regexes</span> <span class="op">=</span> [<span class="st st-sg">r&#39;</span><span class="st">users/(?P&lt;username&gt;.+)$</span><span class="st st-sg">&#39;</span>]
+
+ <span class="kw">def </span><span class="fun">handle_request</span>(<span class="bn bn-pseudo">self</span>, <span class="name">req</span>, <span class="name">username</span>):
+ <span class="cm"># TODO: add a permission CAN_VIEW_PROFILE</span>
+
+ <span class="cm"># check avatar</span>
+ <span class="kw">if</span> <span class="name">req</span>.<span class="name">args</span>.<span class="name">get</span>(<span class="st st-sg">&#39;</span><span class="st">show</span><span class="st st-sg">&#39;</span>) <span class="op">==</span> <span class="st st-sg">&#39;</span><span class="st">avatar</span><span class="st st-sg">&#39;</span>:
+ <span class="name">avatar</span> <span class="op">=</span> <span class="name">get_user_avatar</span>(<span class="name">req</span>, <span class="name">username</span>)
+ <span class="kw">if</span> <span class="name">avatar</span> <span class="op op-word">is</span> <span class="bn bn-pseudo">None</span>:
+ <span class="kw">return</span> <span class="name">PageNotFound</span>()
+ <span class="name">resp</span> <span class="op">=</span> <span class="name">Response</span>(<span class="name">avatar</span>)
+ <span class="name">resp</span>[<span class="st st-sg">&#39;</span><span class="st">Content-Type</span><span class="st st-sg">&#39;</span>] <span class="op">=</span> <span class="st st-sg">&#39;</span><span class="st">image/png</span><span class="st st-sg">&#39;</span>
+
+ <span class="kw">return</span> <span class="name">resp</span>
+ <span class="cm"># display user</span>
+ <span class="name">user</span> <span class="op">=</span> <span class="name">get_user</span>(<span class="name">req</span>, <span class="name">username</span>)
+ <span class="kw">if</span> <span class="name">user</span> <span class="op op-word">is</span> <span class="bn bn-pseudo">None</span>:
+ <span class="kw">return</span> <span class="name">PageNotFound</span>()
+ <span class="kw">return</span> <span class="name">TemplateResponse</span>(<span class="st st-sg">&#39;</span><span class="st">user.html</span><span class="st st-sg">&#39;</span>,
+ <span class="name">person</span> <span class="op">=</span> <span class="name">user</span>
+
+ )
+
+
+<span class="kw">class </span><span class="cls">GotoPage</span>(<span class="name">RequestHandler</span>):
+ <span class="name">handler_regexes</span> <span class="op">=</span> [<span class="st st-sg">&#39;</span><span class="st">goto$</span><span class="st st-sg">&#39;</span>]
+
+ <span class="kw">def </span><span class="fun">handle_request</span>(<span class="bn bn-pseudo">self</span>, <span class="name">req</span>):
+ <span class="kw">if</span> <span class="st st-sg">&#39;</span><span class="st">post</span><span class="st st-sg">&#39;</span> <span class="op op-word">in</span> <span class="name">req</span>.<span class="name">args</span>:
+ <span class="name">post_id</span> <span class="op">=</span> <span class="name">req</span>.<span class="name">args</span>[<span class="st st-sg">&#39;</span><span class="st">post</span><span class="st st-sg">&#39;</span>]
+ <span class="name">post</span> <span class="op">=</span> <span class="name">get_post</span>(<span class="name">req</span>.<span class="name">ctx</span>, <span class="name">post_id</span>)
+ <span class="kw">while</span> <span class="name">post</span>.<span class="name">parent</span>: <span class="cm">#get root_post</span>
+
+ <span class="name">post</span> <span class="op">=</span> <span class="name">post</span>.<span class="name">parent</span> <span class="op op-word">or</span> <span class="name">post</span>
+ <span class="name">thread</span> <span class="op">=</span> <span class="name">req</span>.<span class="name">db</span>.<span class="name">get</span>(<span class="name">Thread</span>, <span class="name">Thread</span>.<span class="name">c</span>.<span class="name">root_post_id</span> <span class="op">==</span> <span class="name">post</span>.<span class="name">post_id</span>)
+ <span class="kw">return</span> <span class="name">HttpRedirect</span>(<span class="name">thread</span>.<span class="name">url</span>)
+ <span class="kw">else</span>:
+ <span class="kw">return</span> <span class="name">HttpRedirect</span>(<span class="bn bn-pseudo">self</span>.<span class="name">ctx</span>.<span class="name">make_external_url</span>(<span class="st st-sg">&#39;</span><span class="st">/</span><span class="st st-sg">&#39;</span>))
+
+
+
+<span class="kw">class </span><span class="cls">SplitPage</span>(<span class="name">RequestHandler</span>, <span class="name">RemoteCallable</span>):
+ <span class="name">handler_regexes</span> <span class="op">=</span> [<span class="st st-sg">r&#39;</span><span class="st">post/(?P&lt;post_id&gt;\d+)/split$</span><span class="st st-sg">&#39;</span>]
+
+ <span class="kw">def </span><span class="fun">handle_request</span>(<span class="bn bn-pseudo">self</span>, <span class="name">req</span>, <span class="name">post_id</span>):
+ <span class="kw">try</span>:
+ <span class="name">thread</span> <span class="op">=</span> <span class="name">Thread</span>.<span class="name">by_child</span>(<span class="name">req</span>.<span class="name">ctx</span>, <span class="name">post_id</span>)
+ <span class="kw">except</span> <span class="exc">ValueError</span>:
+ <span class="kw">return</span> <span class="name">PageNotFound</span>()
+ <span class="kw">if</span> <span class="op op-word">not</span> <span class="name">req</span>.<span class="name">user</span>.<span class="name">acl</span>.<span class="name">can_access</span>(<span class="st st-sg">&#39;</span><span class="st">MODERATOR</span><span class="st st-sg">&#39;</span>, <span class="name">thread</span>.<span class="name">forum_id</span>):
+ <span class="kw">return</span> <span class="name">AccessDeniedResponse</span>()
+ <span class="kw">return</span> <span class="name">TemplateResponse</span>(<span class="st st-sg">&#39;</span><span class="st">split.html</span><span class="st st-sg">&#39;</span>,
+ <span class="name">thread</span> <span class="op">=</span> {<span class="st st-sg">&#39;</span><span class="st">root_post_id</span><span class="st st-sg">&#39;</span>:<span class="name">thread</span>.<span class="name">root_post_id</span>, <span class="st st-sg">&#39;</span><span class="st">title</span><span class="st st-sg">&#39;</span>:<span class="name">thread</span>.<span class="name">title</span>}
+ )
+
+ <span class="deco">@export</span>(<span class="st st-sg">&#39;</span><span class="st">split.get_post_tree</span><span class="st st-sg">&#39;</span>)
+ <span class="kw">def </span><span class="fun">split</span>(<span class="bn bn-pseudo">self</span>, <span class="name">req</span>, <span class="name">root_post_id</span>):
+ <span class="kw">def </span><span class="fun">iterate_items</span>(<span class="name">post_list</span>):
+ <span class="name">tmp</span> <span class="op">=</span> []
+ <span class="kw">for</span> <span class="name">post</span> <span class="op op-word">in</span> <span class="name">post_list</span>:
+ <span class="name">tmp</span>.<span class="name">append</span>({
+ <span class="st st-sg">&#39;</span><span class="st">post_id</span><span class="st st-sg">&#39;</span>: <span class="name">post</span>[<span class="st st-db">&quot;</span><span class="st">post_id</span><span class="st st-db">&quot;</span>],
+ <span class="st st-sg">&#39;</span><span class="st">title</span><span class="st st-sg">&#39;</span>: <span class="name">post</span>[<span class="st st-db">&quot;</span><span class="st">title</span><span class="st st-db">&quot;</span>],
+ <span class="st st-sg">&#39;</span><span class="st">children</span><span class="st st-sg">&#39;</span>: <span class="st st-db">&quot;</span><span class="st">children</span><span class="st st-db">&quot;</span> <span class="op op-word">in</span> <span class="name">post</span> <span class="op op-word">and</span> \
+ <span class="name">iterate_items</span>(<span class="name">post</span>[<span class="st st-db">&quot;</span><span class="st">children</span><span class="st st-db">&quot;</span>]) <span class="op op-word">or</span> [],
+ <span class="st st-sg">&#39;</span><span class="st">parent_id</span><span class="st st-sg">&#39;</span>: <span class="name">post</span>[<span class="st st-db">&quot;</span><span class="st">parent_id</span><span class="st st-db">&quot;</span>]
+ })
+ <span class="kw">return</span> <span class="name">tmp</span>
+
+ <span class="name">post_tree</span> <span class="op">=</span> <span class="name">get_post_tree</span>(<span class="name">req</span>, <span class="name">root_post_id</span>)
+
+ <span class="kw">if</span> <span class="op op-word">not</span> <span class="name">req</span>.<span class="name">user</span>.<span class="name">acl</span>.<span class="name">can_access</span>(<span class="st st-sg">&#39;</span><span class="st">MODERATOR</span><span class="st st-sg">&#39;</span>, <span class="name">post_tree</span>[<span class="st st-db">&quot;</span><span class="st">forum</span><span class="st st-db">&quot;</span>][<span class="st st-db">&quot;</span><span class="st">forum_id</span><span class="st st-db">&quot;</span>]):
+ <span class="kw">return</span> <span class="name">AccessDeniedResponse</span>()
+
+ <span class="kw">return</span> {
+ <span class="st st-sg">&#39;</span><span class="st">posts</span><span class="st st-sg">&#39;</span>:<span class="name">iterate_items</span>(<span class="name">post_tree</span>[<span class="st st-db">&quot;</span><span class="st">posts</span><span class="st st-db">&quot;</span>]),
+ <span class="st st-sg">&#39;</span><span class="st">thread</span><span class="st st-sg">&#39;</span>:{
+ <span class="st st-sg">&#39;</span><span class="st">forum_id</span><span class="st st-sg">&#39;</span>:<span class="name">post_tree</span>[<span class="st st-db">&quot;</span><span class="st">forum</span><span class="st st-db">&quot;</span>][<span class="st st-db">&quot;</span><span class="st">forum_id</span><span class="st st-db">&quot;</span>],
+ <span class="st st-sg">&#39;</span><span class="st">root_post_id</span><span class="st st-sg">&#39;</span>:<span class="name">post_tree</span>[<span class="st st-db">&quot;</span><span class="st">post_id</span><span class="st st-db">&quot;</span>]
+ }
+ }
+
+ <span class="deco">@export</span>(<span class="st st-sg">&#39;</span><span class="st">split.commit</span><span class="st st-sg">&#39;</span>)
+ <span class="kw">def </span><span class="fun">commit</span>(<span class="bn bn-pseudo">self</span>, <span class="name">req</span>, <span class="name">changes</span>):
+ <span class="cm"># TODO: Check whether operation is allowed at every subforum of a thread</span>
+
+ <span class="cm"># the user changes</span>
+ <span class="kw">for</span> <span class="name">tmp</span> <span class="op op-word">in</span> <span class="name">changes</span>.<span class="name">iteritems</span>():
+ <span class="name">change</span> <span class="op">=</span> <span class="name">tmp</span>[<span class="nb nb-int">1</span>]
+ <span class="bn">id</span> <span class="op">=</span> <span class="name">change</span>[<span class="st st-sg">&#39;</span><span class="st">id</span><span class="st st-sg">&#39;</span>]
+ <span class="name">new_pid</span> <span class="op">=</span> <span class="name">change</span>[<span class="st st-sg">&#39;</span><span class="st">new_pid</span><span class="st st-sg">&#39;</span>]
+ <span class="name">old_pid</span> <span class="op">=</span> <span class="name">change</span>[<span class="st st-sg">&#39;</span><span class="st">old_pid</span><span class="st st-sg">&#39;</span>]
+ <span class="name">act_tid</span> <span class="op">=</span> <span class="name">change</span>[<span class="st st-sg">&#39;</span><span class="st">act_tid</span><span class="st st-sg">&#39;</span>]
+ <span class="name">old_tid</span> <span class="op">=</span> <span class="name">change</span>[<span class="st st-sg">&#39;</span><span class="st">old_tid</span><span class="st st-sg">&#39;</span>]
+ <span class="name">post</span> <span class="op">=</span> <span class="name">req</span>.<span class="name">db</span>.<span class="name">get</span>(<span class="name">Post</span>, <span class="name">Post</span>.<span class="name">c</span>.<span class="name">post_id</span> <span class="op">==</span> <span class="bn">id</span>)
+ <span class="kw">if</span> <span class="name">new_pid</span> <span class="op">!=</span> <span class="op">-</span><span class="nb nb-int">1</span>:
+ <span class="name">post</span>.<span class="name">parent_id</span> <span class="op">=</span> <span class="name">new_pid</span>
+
+ <span class="kw">else</span>:
+ <span class="kw">if</span> <span class="name">act_tid</span> <span class="op">==</span> <span class="op">-</span><span class="nb nb-int">1</span>:
+ <span class="cm">#XXX: forum_id</span>
+ <span class="name">thread</span> <span class="op">=</span> <span class="name">Thread</span>(
+ <span class="name">name</span> <span class="op">=</span> <span class="name">post</span>.<span class="name">name</span>,
+ <span class="name">forum_id</span> <span class="op">=</span> <span class="nb nb-int">1</span>,
+ <span class="name">root_post_id</span> <span class="op">=</span> <span class="name">post</span>.<span class="name">post_id</span>
+
+ )
+ <span class="name">req</span>.<span class="name">db</span>.<span class="name">save</span>(<span class="name">thread</span>)
+ <span class="kw">else</span>:
+ <span class="name">thread</span> <span class="op">=</span> <span class="name">req</span>.<span class="name">db</span>.<span class="name">get</span>(<span class="name">Thread</span>, <span class="name">Thread</span>.<span class="name">c</span>.<span class="name">thread_id</span> <span class="op">==</span> <span class="name">act_tid</span>)
+ <span class="name">thread</span>.<span class="name">name</span> <span class="op">=</span> <span class="name">post</span>.<span class="name">name</span>
+
+ <span class="name">post</span>.<span class="name">parent_id</span> <span class="op">=</span> <span class="name">thread</span>.<span class="name">thread_id</span>
+ <span class="name">req</span>.<span class="name">db</span>.<span class="name">flush</span>()
+ <span class="kw">return</span> <span class="st st-db">&quot;&quot;</span>
+
+
+<span class="kw">class </span><span class="cls">WhoIsOnlinePage</span>(<span class="name">RequestHandler</span>, <span class="name">PagePublisher</span>):
+ <span class="name">page_name</span> <span class="op">=</span> <span class="st st-sg">&#39;</span><span class="st">whoisonline</span><span class="st st-sg">&#39;</span>
+ <span class="name">relative_url</span> <span class="op">=</span> <span class="st st-sg">&#39;</span><span class="st">whoisonline</span><span class="st st-sg">&#39;</span>
+
+ <span class="name">handler_regexes</span> <span class="op">=</span> [<span class="st st-sg">r&#39;</span><span class="st">whoisonline$</span><span class="st st-sg">&#39;</span>]
+
+ <span class="kw">def </span><span class="fun">handle_request</span>(<span class="bn bn-pseudo">self</span>, <span class="name">req</span>):
+ <span class="name">sessions</span>, <span class="name">users</span>, <span class="name">guests</span>, <span class="name">total</span> <span class="op">=</span> <span class="name">get_active_sessions</span>(<span class="name">req</span>.<span class="name">ctx</span>)
+ <span class="kw">return</span> <span class="name">TemplateResponse</span>(<span class="st st-sg">&#39;</span><span class="st">whoisonline.html</span><span class="st st-sg">&#39;</span>,
+ <span class="name">sessions</span><span class="op">=</span><span class="name">sessions</span>,
+ <span class="name">user_count</span><span class="op">=</span><span class="name">users</span>,
+ <span class="name">guest_count</span><span class="op">=</span><span class="name">guests</span>,
+ <span class="name">total_count</span><span class="op">=</span><span class="name">total</span>
+
+ )
+
+
+<span class="kw">class </span><span class="cls">UserSettings</span>(<span class="name">RequestHandler</span>, <span class="name">PagePublisher</span>):
+ <span class="name">page_name</span> <span class="op">=</span> <span class="st st-sg">&#39;</span><span class="st">settings</span><span class="st st-sg">&#39;</span>
+ <span class="name">relative_url</span> <span class="op">=</span> <span class="st st-sg">&#39;</span><span class="st">settings/</span><span class="st st-sg">&#39;</span>
+
+ <span class="name">handler_regexes</span> <span class="op">=</span> [
+ <span class="st st-sg">r&#39;</span><span class="st">settings/$</span><span class="st st-sg">&#39;</span>,
+ <span class="st st-sg">r&#39;</span><span class="st">settings/(?P&lt;page&gt;.+?)$</span><span class="st st-sg">&#39;</span>
+ ]
+ <span class="name">allow_username_change</span> <span class="op">=</span> <span class="name">cfg</span>.<span class="name">bool</span>(<span class="st st-sg">&#39;</span><span class="st">security</span><span class="st st-sg">&#39;</span>, <span class="st st-sg">&#39;</span><span class="st">username_change</span><span class="st st-sg">&#39;</span>, <span class="bn bn-pseudo">False</span>)
+
+ <span class="kw">def </span><span class="fun">handle_request</span>(<span class="bn bn-pseudo">self</span>, <span class="name">req</span>, <span class="name">page</span><span class="op">=</span><span class="bn bn-pseudo">None</span>):
+ <span class="name">req</span>.<span class="name">user</span>.<span class="name">assert_logged_in</span>()
+ <span class="name">sidebar</span> <span class="op">=</span> []
+ <span class="name">missing</span> <span class="op">=</span> <span class="bn">object</span>()
+ <span class="name">page_result</span> <span class="op">=</span> <span class="name">missing</span>
+
+ <span class="kw">for</span> <span class="name">comp</span> <span class="op op-word">in</span> <span class="bn bn-pseudo">self</span>.<span class="name">ctx</span>.<span class="name">get_components</span>(<span class="name">UserSettingsPage</span>):
+ <span class="name">caption</span> <span class="op">=</span> <span class="name">comp</span>.<span class="name">get_settings_link_title</span>(<span class="name">req</span>)
+ <span class="cm"># if the caption is None the plugin doesn&#39;t want to</span>
+
+ <span class="cm"># be visible in the sidebar, skip</span>
+ <span class="kw">if</span> <span class="name">caption</span> <span class="op op-word">is</span> <span class="bn bn-pseudo">None</span>:
+ <span class="kw">continue</span>
+ <span class="name">active</span> <span class="op">=</span> <span class="name">comp</span>.<span class="name">settings_page_identifier</span> <span class="op">==</span> <span class="name">page</span>
+
+ <span class="kw">if</span> <span class="name">active</span>:
+ <span class="name">page_result</span> <span class="op">=</span> <span class="name">comp</span>.<span class="name">get_settings_page</span>(<span class="name">req</span>)
+ <span class="cm"># the page really wants to output data on its own</span>
+
+ <span class="cm"># we don&#39;t need to create a sidebar, stop here</span>
+ <span class="kw">if</span> <span class="bn">isinstance</span>(<span class="name">page_result</span>, <span class="name">Response</span>):
+ <span class="kw">return</span> <span class="name">page_result</span>
+
+ <span class="name">sidebar</span>.<span class="name">append</span>({
+ <span class="st st-sg">&#39;</span><span class="st">url</span><span class="st st-sg">&#39;</span>: <span class="name">comp</span>.<span class="name">url</span>,
+ <span class="st st-sg">&#39;</span><span class="st">caption</span><span class="st st-sg">&#39;</span>: <span class="name">caption</span>,
+ <span class="st st-sg">&#39;</span><span class="st">active</span><span class="st st-sg">&#39;</span>: <span class="name">active</span>,
+ <span class="st st-sg">&#39;</span><span class="st">identifier</span><span class="st st-sg">&#39;</span>: <span class="name">comp</span>.<span class="name">settings_page_identifier</span>
+
+ })
+ <span class="cm"># display error page</span>
+ <span class="kw">if</span> <span class="name">page_result</span> <span class="op op-word">is</span> <span class="name">missing</span> <span class="op op-word">and</span> <span class="name">page</span> <span class="op op-word">is</span> <span class="op op-word">not</span> <span class="bn bn-pseudo">None</span>:
+ <span class="kw">return</span> <span class="name">PageNotFound</span>()
+ <span class="cm"># we have found a page, display it, but first sort the sidebar</span>
+
+ <span class="name">sidebar</span>.<span class="name">sort</span>(<span class="name">key</span><span class="op">=</span><span class="kw">lambda</span> <span class="name">x</span>: <span class="name">x</span>[<span class="st st-sg">&#39;</span><span class="st">caption</span><span class="st st-sg">&#39;</span>].<span class="name">lower</span>())
+ <span class="kw">if</span> <span class="name">page</span> <span class="op op-word">is</span> <span class="bn bn-pseudo">None</span>:
+ <span class="kw">return</span> <span class="name">TemplateResponse</span>(<span class="st st-sg">&#39;</span><span class="st">settings/index.html</span><span class="st st-sg">&#39;</span>,
+ <span class="name">setting_pages</span> <span class="op">=</span> <span class="name">sidebar</span>
+
+ )
+ <span class="name">template</span>, <span class="name">context</span> <span class="op">=</span> <span class="name">page_result</span>
+ <span class="name">context</span>[<span class="st st-sg">&#39;</span><span class="st">setting_pages</span><span class="st st-sg">&#39;</span>] <span class="op">=</span> <span class="name">sidebar</span>
+
+ <span class="kw">return</span> <span class="name">TemplateResponse</span>(<span class="name">template</span>, <span class="op">**</span><span class="name">context</span>)
+
+
+<span class="kw">class </span><span class="cls">LostPasswordPage</span>(<span class="name">RequestHandler</span>, <span class="name">PagePublisher</span>):
+ <span class="name">page_name</span> <span class="op">=</span> <span class="st st-sg">&#39;</span><span class="st">lostpassword</span><span class="st st-sg">&#39;</span>
+
+ <span class="name">relative_url</span> <span class="op">=</span> <span class="st st-sg">&#39;</span><span class="st">lostpassword</span><span class="st st-sg">&#39;</span>
+ <span class="name">handler_regexes</span> <span class="op">=</span> [<span class="st st-sg">&#39;</span><span class="st">lostpassword$</span><span class="st st-sg">&#39;</span>]
+
+ <span class="kw">def </span><span class="fun">handle_request</span>(<span class="bn bn-pseudo">self</span>, <span class="name">req</span>):
+ <span class="cm"># if the user is identified he has probably not lost has password ;)</span>
+
+ <span class="kw">if</span> <span class="name">req</span>.<span class="name">user</span>.<span class="name">identified</span>:
+ <span class="kw">return</span> <span class="name">HttpRedirect</span>(<span class="bn bn-pseudo">self</span>.<span class="name">ctx</span>.<span class="name">make_external_url</span>(<span class="st st-sg">&#39;&#39;</span>))
+
+ <span class="name">_</span> <span class="op">=</span> <span class="name">req</span>.<span class="name">gettext</span>
+
+ <span class="name">ctx</span> <span class="op">=</span> <span class="bn bn-pseudo">self</span>.<span class="name">ctx</span>
+ <span class="name">msg</span> <span class="op">=</span> <span class="bn bn-pseudo">None</span>
+ <span class="name">form</span> <span class="op">=</span> <span class="name">Form</span>(<span class="name">req</span>, <span class="bn bn-pseudo">self</span>, <span class="st st-sg">&#39;</span><span class="st">POST</span><span class="st st-sg">&#39;</span>,
+ <span class="name">TextField</span>(<span class="st st-sg">&#39;</span><span class="st">username</span><span class="st st-sg">&#39;</span>, <span class="name">validator</span><span class="op">=</span><span class="name">isExistingUsername</span>()),
+ <span class="name">TextField</span>(<span class="st st-sg">&#39;</span><span class="st">email</span><span class="st st-sg">&#39;</span>, <span class="name">validator</span><span class="op">=</span><span class="name">isEmail</span>())
+ )
+
+ <span class="kw">if</span> <span class="name">req</span>.<span class="name">method</span> <span class="op">==</span> <span class="st st-sg">&#39;</span><span class="st">POST</span><span class="st st-sg">&#39;</span>:
+ <span class="name">form</span>.<span class="name">update</span>(<span class="name">req</span>.<span class="name">form</span>, <span class="name">prefix</span><span class="op">=</span><span class="st st-sg">&#39;</span><span class="st">f_</span><span class="st st-sg">&#39;</span>)
+ <span class="kw">if</span> <span class="op op-word">not</span> <span class="name">form</span>.<span class="name">has_errors</span>:
+ <span class="name">d</span> <span class="op">=</span> <span class="name">form</span>.<span class="name">to_dict</span>()
+ <span class="name">password</span> <span class="op">=</span> <span class="name">reset_password</span>(<span class="name">ctx</span>, <span class="op">**</span><span class="name">d</span>)
+ <span class="kw">if</span> <span class="name">password</span> <span class="op op-word">is</span> <span class="op op-word">not</span> <span class="bn bn-pseudo">None</span>:
+ <span class="name">text</span> <span class="op">=</span> <span class="name">render_template</span>(<span class="name">req</span>, <span class="st st-sg">&#39;</span><span class="st">mails/new_password.txt</span><span class="st st-sg">&#39;</span>, {
+ <span class="st st-sg">&#39;</span><span class="st">username</span><span class="st st-sg">&#39;</span>: <span class="name">d</span>[<span class="st st-sg">&#39;</span><span class="st">username</span><span class="st st-sg">&#39;</span>],
+ <span class="st st-sg">&#39;</span><span class="st">password</span><span class="st st-sg">&#39;</span>: <span class="name">password</span>
+
+ })
+ <span class="name">mail</span> <span class="op">=</span> <span class="name">Email</span>(<span class="name">ctx</span>, <span class="name">_</span>(<span class="st st-sg">&#39;</span><span class="st">New password</span><span class="st st-sg">&#39;</span>), <span class="name">d</span>[<span class="st st-sg">&#39;</span><span class="st">email</span><span class="st st-sg">&#39;</span>], <span class="name">text</span>)
+ <span class="name">mail</span>.<span class="name">send</span>()
+ <span class="kw">return</span> <span class="name">TemplateResponse</span>(<span class="st st-sg">&#39;</span><span class="st">messages/password.html</span><span class="st st-sg">&#39;</span>, <span class="op">**</span><span class="name">d</span>)
+ <span class="kw">else</span>:
+ <span class="name">msg</span> <span class="op">=</span> <span class="name">_</span>(<span class="st st-sg">&#39;</span><span class="st">Creation of a new password failed. Username </span><span class="st st-sg">&#39;</span>
+
+ <span class="st st-sg">&#39;</span><span class="st">or email address is invalid.</span><span class="st st-sg">&#39;</span>)
+
+ <span class="kw">return</span> <span class="name">TemplateResponse</span>(<span class="st st-sg">&#39;</span><span class="st">lostpassword.html</span><span class="st st-sg">&#39;</span>,
+ <span class="name">form</span> <span class="op">=</span> <span class="name">form</span>.<span class="name">generate</span>(<span class="name">prefix</span><span class="op">=</span><span class="st st-sg">&#39;</span><span class="st">f_</span><span class="st st-sg">&#39;</span>),
+ <span class="name">msg</span> <span class="op">=</span> <span class="name">msg</span>
+
+ )
+<span class="cm"># -*- coding: utf-8 -*-</span>
+<span class="st st-db">&quot;&quot;&quot;</span><span class="st">
+ pocoo.pkg.core.remotecall
+ ~~~~~~~~~~~~~~~~~~~~~~~~~
+
+ Pocoo remote call support.
+
+
+ Remote Call Implementation
+ ==========================
+
+ The Pocoo XMLRPC/JSONRPC interface works like this::
+
+ import time
+ from pocoo.pkg.core.remotecall import RemoteCallable, export
+
+ class MyClass(RemoteCallable):
+
+ @export(&quot;test.hello_world&quot;)
+ def say(self, req, name=&#39;World&#39;):
+ return &#39;Hello </span><span class="st st-int">%s</span><span class="st">!&#39; % name
+
+ @export(&quot;test.get_servertime&quot;)
+ def servertime(self, req):
+ return time.time()
+
+ By now only jsonrpc is available. You can query the jsonrpc interface
+ under ``/!jsonrpc``. The exported names are ``packagename.&lt;name&gt;``.
+ So for the example above the method names are (assumed that the module
+ is in package ``core``)::
+
+ core.test.hello_world
+
+ and::
+
+ core.test.get_servertime
+
+
+ JavaScript Query
+ =================
+
+ You can query those functions using the following syntax::
+
+ var rpc = new pocoo.lib.async.RPC(&#39;!jsonrpc&#39;);
+ var method = rpc.getMethod(&#39;methodname&#39;);
+ method(arguments, kwarguments, callback);
+
+ The query for the example above would be::
+
+ var rpc = new pocoo.lib.async.RPC(&#39;!jsonrpc&#39;);
+ var method = rpc.getMethod(&#39;core.test.hello_world&#39;);
+ method([&quot;Benjamin&quot;], {}, function (result) {
+ alert(result);
+ // alerts &quot;Hello Benjamin!&quot;
+
+ });
+
+
+ :copyright: 2006 by Armin Ronacher.
+ :license: GNU GPL, see LICENSE for more details.
+</span><span class="st st-db">&quot;&quot;&quot;</span>
+<span class="kw">import </span><span class="cls">new</span>
+<span class="kw">from </span><span class="cls">types</span><span class="kw"> import</span> <span class="name">FunctionType</span>
+<span class="kw">from </span><span class="cls">pocoo</span><span class="kw"> import</span> <span class="name">Component</span>, <span class="name">ComponentMeta</span>
+
+<span class="kw">from </span><span class="cls">pocoo.http</span><span class="kw"> import</span> <span class="name">DirectResponse</span>
+<span class="kw">from </span><span class="cls">pocoo.application</span><span class="kw"> import</span> <span class="name">RequestHandler</span>
+<span class="kw">from </span><span class="cls">pocoo.http</span><span class="kw"> import</span> <span class="name">Response</span>
+
+<span class="kw">from </span><span class="cls">pocoo.utils</span><span class="kw"> import</span> <span class="name">json</span>
+
+
+<span class="kw">class </span><span class="cls">_RemoteCallableMeta</span>(<span class="name">ComponentMeta</span>):
+
+ <span class="kw">def </span><span class="fun">__new__</span>(<span class="name">cls</span>, <span class="name">name</span>, <span class="name">bases</span>, <span class="name">dct</span>):
+ <span class="name">rpc_exports</span> <span class="op">=</span> {}
+ <span class="name">result</span> <span class="op">=</span> <span class="name">ComponentMeta</span>.<span class="name">__new__</span>(<span class="name">cls</span>, <span class="name">name</span>, <span class="name">bases</span>, <span class="name">dct</span>)
+ <span class="kw">for</span> <span class="name">name</span>, <span class="name">ref</span> <span class="op op-word">in</span> <span class="name">dct</span>.<span class="name">iteritems</span>():
+ <span class="kw">if</span> <span class="bn">isinstance</span>(<span class="name">ref</span>, <span class="name">FunctionType</span>) <span class="op op-word">and</span> \
+ <span class="bn">getattr</span>(<span class="name">ref</span>, <span class="st st-sg">&#39;</span><span class="st">rpc_exported</span><span class="st st-sg">&#39;</span>, <span class="bn bn-pseudo">False</span>):
+ <span class="name">name</span> <span class="op">=</span> <span class="st st-sg">&#39;</span><span class="st st-int">%s</span><span class="st">.</span><span class="st st-int">%s</span><span class="st st-sg">&#39;</span> <span class="op">%</span> (<span class="name">ref</span>.<span class="name">__module__</span>.<span class="name">split</span>(<span class="st st-sg">&#39;</span><span class="st">.</span><span class="st st-sg">&#39;</span>)[<span class="nb nb-int">2</span>],
+ <span class="name">ref</span>.<span class="name">rpc_name</span>)
+ <span class="name">rpc_exports</span>[<span class="name">name</span>] <span class="op">=</span> <span class="name">ref</span>
+
+ <span class="name">result</span>.<span class="name">rpc_exports</span> <span class="op">=</span> <span class="name">rpc_exports</span>
+ <span class="kw">return</span> <span class="name">result</span>
+
+
+<span class="kw">class </span><span class="cls">RemoteCallable</span>(<span class="name">Component</span>):
+ <span class="st st-db">&quot;&quot;&quot;</span><span class="st">
+
+ Components inheriting from this base component can export methods
+ for jsonrpc if they are decorated using `export`.
+
+ Example::
+
+ from pocoo.pkg.core.remotecall import RemoteCallable, export
+
+ class MyExport(RemoteCallable):
+ </span><span class="st st-db">&quot;&quot;&quot;</span>
+ <span class="name">__metaclass__</span> <span class="op">=</span> <span class="name">_RemoteCallableMeta</span>
+
+
+<span class="kw">class </span><span class="cls">RemoteCallManager</span>(<span class="name">RequestHandler</span>):
+
+ <span class="name">handler_regexes</span> <span class="op">=</span> [<span class="st st-sg">r&#39;</span><span class="st">!jsonrpc$</span><span class="st st-sg">&#39;</span>]
+
+ <span class="kw">def </span><span class="fun">__init__</span>(<span class="bn bn-pseudo">self</span>, <span class="name">ctx</span>):
+ <span class="bn">super</span>(<span class="name">RemoteCallManager</span>, <span class="bn bn-pseudo">self</span>).<span class="name">__init__</span>(<span class="name">ctx</span>)
+ <span class="bn bn-pseudo">self</span>.<span class="name">_mapping</span> <span class="op">=</span> <span class="bn bn-pseudo">None</span>
+
+ <span class="kw">def </span><span class="fun">handle_request</span>(<span class="bn bn-pseudo">self</span>, <span class="name">req</span>):
+ <span class="cm"># TODO: once jsonrpc1.1 is finished: update error codes</span>
+ <span class="kw">if</span> <span class="bn bn-pseudo">self</span>.<span class="name">_mapping</span> <span class="op op-word">is</span> <span class="bn bn-pseudo">None</span>:
+ <span class="bn bn-pseudo">self</span>.<span class="name">_mapping</span> <span class="op">=</span> {}
+ <span class="kw">for</span> <span class="name">comp</span> <span class="op op-word">in</span> <span class="bn bn-pseudo">self</span>.<span class="name">ctx</span>.<span class="name">get_components</span>(<span class="name">RemoteCallable</span>):
+ <span class="kw">for</span> <span class="name">name</span>, <span class="name">ref</span> <span class="op op-word">in</span> <span class="name">comp</span>.<span class="name">rpc_exports</span>.<span class="name">iteritems</span>():
+ <span class="name">handler</span> <span class="op">=</span> <span class="name">new</span>.<span class="name">instancemethod</span>(<span class="name">ref</span>, <span class="name">comp</span>, <span class="name">comp</span>.<span class="name">__class__</span>)
+ <span class="bn bn-pseudo">self</span>.<span class="name">_mapping</span>[<span class="name">name</span>] <span class="op">=</span> <span class="name">handler</span>
+
+ <span class="bn">id</span> <span class="op">=</span> <span class="bn bn-pseudo">None</span>
+ <span class="kw">try</span>:
+ <span class="name">method</span>, <span class="name">args</span>, <span class="name">kwargs</span>, <span class="bn">id</span> <span class="op">=</span> <span class="name">json</span>.<span class="name">parse_jsonrpc_request</span>(<span class="name">req</span>.<span class="name">data</span>)
+ <span class="name">handler</span> <span class="op">=</span> <span class="bn bn-pseudo">self</span>.<span class="name">_mapping</span>[<span class="name">method</span>]
+ <span class="name">json_data</span> <span class="op">=</span> {
+ <span class="st st-sg">&#39;</span><span class="st">version</span><span class="st st-sg">&#39;</span>: <span class="st st-sg">&#39;</span><span class="st">1.1</span><span class="st st-sg">&#39;</span>,
+ <span class="st st-sg">&#39;</span><span class="st">result</span><span class="st st-sg">&#39;</span>: <span class="name">handler</span>(<span class="name">req</span>, <span class="op">*</span><span class="name">args</span>, <span class="op">**</span><span class="name">kwargs</span>)
+ }
+ <span class="kw">if</span> <span class="bn">id</span> <span class="op op-word">is</span> <span class="op op-word">not</span> <span class="bn bn-pseudo">None</span>:
+ <span class="name">json_data</span>[<span class="st st-sg">&#39;</span><span class="st">id</span><span class="st st-sg">&#39;</span>] <span class="op">=</span> <span class="bn">id</span>
+
+ <span class="kw">except</span> <span class="name">DirectResponse</span>, <span class="name">e</span>:
+ <span class="kw">return</span> <span class="name">e</span>.<span class="name">args</span>[<span class="nb nb-int">0</span>]
+ <span class="kw">except</span> <span class="exc">Exception</span>, <span class="name">e</span>:
+ <span class="name">error</span> <span class="op">=</span> {
+ <span class="st st-sg">&#39;</span><span class="st">msg</span><span class="st st-sg">&#39;</span>: <span class="bn">str</span>(<span class="name">e</span>),
+ <span class="st st-sg">&#39;</span><span class="st">type</span><span class="st st-sg">&#39;</span>: <span class="name">e</span>.<span class="name">__class__</span>.<span class="name">__name__</span>
+
+ }
+ <span class="kw">for</span> <span class="name">name</span>, <span class="name">ref</span> <span class="op op-word">in</span> <span class="name">e</span>.<span class="name">__dict__</span>.<span class="name">iteritems</span>():
+ <span class="kw">if</span> <span class="op op-word">not</span> <span class="name">name</span>.<span class="name">startswith</span>(<span class="st st-sg">&#39;</span><span class="st">_</span><span class="st st-sg">&#39;</span>) <span class="op op-word">and</span>\
+ <span class="bn">isinstance</span>(<span class="name">ref</span>, (<span class="bn">str</span>, <span class="bn">unicode</span>, <span class="bn">int</span>, <span class="bn">float</span>, <span class="bn">tuple</span>,
+ <span class="bn">list</span>, <span class="bn">dict</span>)):
+ <span class="name">error</span>[<span class="name">name</span>] <span class="op">=</span> <span class="name">ref</span>
+
+ <span class="name">json_data</span> <span class="op">=</span> {
+ <span class="st st-sg">&#39;</span><span class="st">version</span><span class="st st-sg">&#39;</span>: <span class="st st-sg">&#39;</span><span class="st">1.1</span><span class="st st-sg">&#39;</span>,
+ <span class="st st-sg">&#39;</span><span class="st">error</span><span class="st st-sg">&#39;</span>: {
+ <span class="st st-sg">&#39;</span><span class="st">name</span><span class="st st-sg">&#39;</span>: <span class="st st-sg">&#39;</span><span class="st">JSONRPCError</span><span class="st st-sg">&#39;</span>,
+ <span class="st st-sg">&#39;</span><span class="st">code</span><span class="st st-sg">&#39;</span>: <span class="nb nb-oct">000</span>,
+ <span class="st st-sg">&#39;</span><span class="st">message</span><span class="st st-sg">&#39;</span>: <span class="st st-sg">&#39;</span><span class="st">An error occurred parsing the request object.</span><span class="st st-sg">&#39;</span>,
+ <span class="st st-sg">&#39;</span><span class="st">error</span><span class="st st-sg">&#39;</span>: <span class="name">error</span>
+
+ }
+ }
+ <span class="kw">if</span> <span class="bn">id</span> <span class="op op-word">is</span> <span class="op op-word">not</span> <span class="bn bn-pseudo">None</span>:
+ <span class="name">json_data</span>[<span class="st st-sg">&#39;</span><span class="st">id</span><span class="st st-sg">&#39;</span>] <span class="op">=</span> <span class="bn">id</span>
+
+ <span class="kw">return</span> <span class="name">Response</span>(<span class="name">json</span>.<span class="name">dumps</span>(<span class="name">json_data</span>),
+ [(<span class="st st-sg">&#39;</span><span class="st">Content-Type</span><span class="st st-sg">&#39;</span>, <span class="st st-sg">&#39;</span><span class="st">text/javascript</span><span class="st st-sg">&#39;</span>)])
+
+
+<span class="kw">def </span><span class="fun">export</span>(<span class="name">name</span>):
+ <span class="st st-db">&quot;&quot;&quot;</span><span class="st">
+
+ Exports a function in a RemoteCallable component.
+ </span><span class="st st-db">&quot;&quot;&quot;</span>
+ <span class="kw">def </span><span class="fun">wrapped</span>(<span class="name">f</span>):
+ <span class="name">f</span>.<span class="name">rpc_exported</span> <span class="op">=</span> <span class="bn bn-pseudo">True</span>
+
+ <span class="name">f</span>.<span class="name">rpc_name</span> <span class="op">=</span> <span class="name">name</span>
+ <span class="kw">return</span> <span class="name">f</span>
+ <span class="kw">return</span> <span class="name">wrapped</span>
+
+<span class="cm"># -*- coding: utf-8 -*-</span>
+<span class="st st-db">&quot;&quot;&quot;</span><span class="st">
+ pocoo.pkg.core.restformat
+ ~~~~~~~~~~~~~~~~~~~~~~~~~
+
+ Pocoo ReST Parser.
+
+ A thin wrapper around the docutils ReST parser.
+
+ :copyright: 2006 by Georg Brandl.
+ :license: GNU GPL, see LICENSE for more details.
+</span><span class="st st-db">&quot;&quot;&quot;</span>
+
+<span class="kw">from </span><span class="cls">pocoo.pkg.core.textfmt</span><span class="kw"> import</span> <span class="name">MarkupFormat</span>
+<span class="kw">from </span><span class="cls">pocoo.utils.html</span><span class="kw"> import</span> <span class="name">escape_html</span>
+
+<span class="kw">from </span><span class="cls">pocoo.pkg.core.smilies</span><span class="kw"> import</span> <span class="name">replace_smilies</span>
+
+
+<span class="kw">class </span><span class="cls">ReST</span>(<span class="name">MarkupFormat</span>):
+ <span class="st st-db">&quot;&quot;&quot;</span><span class="st">
+ ReST markup format.
+ </span><span class="st st-db">&quot;&quot;&quot;</span>
+
+ <span class="name">name</span> <span class="op">=</span> <span class="st st-sg">&#39;</span><span class="st">rest</span><span class="st st-sg">&#39;</span>
+
+ <span class="kw">def </span><span class="fun">parse</span>(<span class="bn bn-pseudo">self</span>, <span class="name">text</span>, <span class="name">signature</span>):
+ <span class="kw">from </span><span class="cls">docutils.core</span><span class="kw"> import</span> <span class="name">publish_parts</span>
+
+ <span class="kw">try</span>:
+ <span class="name">parts</span> <span class="op">=</span> <span class="name">publish_parts</span>(<span class="name">source</span><span class="op">=</span><span class="name">text</span>, <span class="name">writer_name</span><span class="op">=</span><span class="st st-sg">&#39;</span><span class="st">html4css1</span><span class="st st-sg">&#39;</span>)
+ <span class="name">text</span> <span class="op">=</span> <span class="name">parts</span>[<span class="st st-sg">&#39;</span><span class="st">fragment</span><span class="st st-sg">&#39;</span>].<span class="name">strip</span>()
+ <span class="kw">except</span> <span class="exc">Exception</span>:
+ <span class="cm"># TODO: figure out which exceptions this can raise</span>
+
+ <span class="name">text</span> <span class="op">=</span> <span class="name">escape_html</span>(<span class="name">text</span>).<span class="name">strip</span>()
+ <span class="cm">#XXX: render smilies just in text blocks</span>
+ <span class="kw">return</span> <span class="name">replace_smilies</span>(<span class="bn bn-pseudo">self</span>.<span class="name">ctx</span>, <span class="name">text</span>)
+
+ <span class="kw">def </span><span class="fun">quote_text</span>(<span class="bn bn-pseudo">self</span>, <span class="name">req</span>, <span class="name">text</span>, <span class="name">username</span><span class="op">=</span><span class="bn bn-pseudo">None</span>):
+ <span class="name">lines</span> <span class="op">=</span> [<span class="name">u</span><span class="st st-sg">&#39;</span><span class="st"> </span><span class="st st-int">%s</span><span class="st st-sg">&#39;</span> <span class="op">%</span> <span class="name">line</span> <span class="kw">for</span> <span class="name">line</span> <span class="op op-word">in</span> <span class="name">text</span>.<span class="name">splitlines</span>()]
+ <span class="kw">if</span> <span class="name">username</span> <span class="op op-word">is</span> <span class="op op-word">not</span> <span class="bn bn-pseudo">None</span>:
+ <span class="name">_</span> <span class="op">=</span> <span class="name">req</span>.<span class="name">gettext</span>
+
+ <span class="name">lines</span>.<span class="name">insert</span>(<span class="nb nb-int">0</span>, (<span class="name">u</span><span class="st st-sg">&#39;</span><span class="st"> **</span><span class="st st-int">%s</span><span class="st">**:</span><span class="st st-sg">&#39;</span> <span class="op">%</span> <span class="name">_</span>(<span class="st st-sg">&#39;</span><span class="st st-int">%s</span><span class="st"> wrote</span><span class="st st-sg">&#39;</span>)) <span class="op">%</span> <span class="name">username</span>)
+ <span class="kw">return</span> <span class="name">u</span><span class="st st-sg">&#39;</span><span class="st st-esc">\n</span><span class="st st-sg">&#39;</span>.<span class="name">join</span>(<span class="name">lines</span>)
+
+<span class="cm"># -*- coding: utf-8 -*-</span>
+<span class="st st-db">&quot;&quot;&quot;</span><span class="st">
+ pocoo.pkg.core.search
+ ~~~~~~~~~~~~~~~~~~~~~
+
+ Pocoo Search System
+
+ :copyright: 2006 by Armin Ronacher.
+ :license: GNU GPL, see LICENSE for more details.
+</span><span class="st st-db">&quot;&quot;&quot;</span>
+<span class="kw">from </span><span class="cls">pocoo</span><span class="kw"> import</span> <span class="name">Component</span>
+<span class="kw">from </span><span class="cls">pocoo.application</span><span class="kw"> import</span> <span class="name">LinkableMixin</span>
+
+<span class="kw">from </span><span class="cls">shlex</span><span class="kw"> import</span> <span class="name">split</span>
+
+
+<span class="cm">#XXX: some kind of early draft</span>
+<span class="kw">class </span><span class="cls">Query</span>(<span class="bn">object</span>):
+ <span class="st st-db">&quot;&quot;&quot;</span><span class="st">
+
+ Search Query Object
+ </span><span class="st st-db">&quot;&quot;&quot;</span>
+
+ <span class="kw">def </span><span class="fun">__init__</span>(<span class="bn bn-pseudo">self</span>, <span class="name">rule</span>):
+ <span class="bn bn-pseudo">self</span>.<span class="name">text</span> <span class="op">=</span> <span class="name">rule</span>
+
+ <span class="bn bn-pseudo">self</span>.<span class="name">required</span> <span class="op">=</span> []
+ <span class="bn bn-pseudo">self</span>.<span class="name">unwanted</span> <span class="op">=</span> []
+ <span class="bn bn-pseudo">self</span>.<span class="name">optional</span> <span class="op">=</span> []
+ <span class="name">next</span> <span class="op">=</span> <span class="bn bn-pseudo">None</span>
+
+ <span class="name">first</span> <span class="op">=</span> <span class="bn bn-pseudo">True</span>
+ <span class="kw">for</span> <span class="name">token</span> <span class="op op-word">in</span> <span class="name">split</span>(<span class="name">rule</span>):
+ <span class="kw">if</span> <span class="op op-word">not</span> <span class="name">next</span> <span class="op op-word">is</span> <span class="bn bn-pseudo">None</span>:
+ <span class="bn">getattr</span>(<span class="bn bn-pseudo">self</span>, <span class="name">next</span>).<span class="name">append</span>(<span class="name">token</span>)
+ <span class="name">next</span> <span class="op">=</span> <span class="bn bn-pseudo">None</span>
+
+ <span class="kw">if</span> <span class="name">token</span> <span class="op">==</span> <span class="st st-sg">&#39;</span><span class="st">NOT</span><span class="st st-sg">&#39;</span>:
+ <span class="name">next</span> <span class="op">=</span> <span class="st st-sg">&#39;</span><span class="st">unwanted</span><span class="st st-sg">&#39;</span>
+ <span class="kw">elif</span> <span class="name">token</span> <span class="op">==</span> <span class="st st-sg">&#39;</span><span class="st">AND</span><span class="st st-sg">&#39;</span>:
+ <span class="name">next</span> <span class="op">=</span> <span class="st st-sg">&#39;</span><span class="st">required</span><span class="st st-sg">&#39;</span>
+
+ <span class="kw">elif</span> <span class="name">token</span> <span class="op">==</span> <span class="st st-sg">&#39;</span><span class="st">OR</span><span class="st st-sg">&#39;</span>:
+ <span class="name">next</span> <span class="op">=</span> <span class="st st-sg">&#39;</span><span class="st">optional</span><span class="st st-sg">&#39;</span>
+ <span class="kw">else</span>:
+ <span class="kw">if</span> <span class="name">token</span>.<span class="name">startswith</span>(<span class="st st-sg">&#39;</span><span class="st">+</span><span class="st st-sg">&#39;</span>):
+ <span class="name">token</span> <span class="op">=</span> <span class="name">token</span>[<span class="nb nb-int">1</span>:]
+ <span class="bn bn-pseudo">self</span>.<span class="name">required</span>.<span class="name">append</span>(<span class="name">token</span>)
+ <span class="kw">elif</span> <span class="name">token</span>.<span class="name">startswith</span>(<span class="st st-sg">&#39;</span><span class="st">-</span><span class="st st-sg">&#39;</span>):
+ <span class="name">token</span> <span class="op">=</span> <span class="name">token</span>[<span class="nb nb-int">1</span>:]
+ <span class="bn bn-pseudo">self</span>.<span class="name">unwanted</span>.<span class="name">append</span>(<span class="name">token</span>)
+ <span class="kw">elif</span> <span class="name">first</span>:
+ <span class="bn bn-pseudo">self</span>.<span class="name">required</span>.<span class="name">append</span>(<span class="name">token</span>)
+ <span class="name">first</span> <span class="op">=</span> <span class="bn bn-pseudo">False</span>
+
+ <span class="kw">else</span>:
+ <span class="bn bn-pseudo">self</span>.<span class="name">optional</span>.<span class="name">append</span>(<span class="name">token</span>)
+ <span class="name">first</span> <span class="op">=</span> <span class="bn bn-pseudo">False</span>
+
+ <span class="bn bn-pseudo">self</span>.<span class="name">words</span> <span class="op">=</span> <span class="bn bn-pseudo">self</span>.<span class="name">required</span> <span class="op">+</span> <span class="bn bn-pseudo">self</span>.<span class="name">unwanted</span> <span class="op">+</span> <span class="bn bn-pseudo">self</span>.<span class="name">optional</span>
+
+ <span class="kw">def </span><span class="fun">__iter__</span>(<span class="bn bn-pseudo">self</span>):
+ <span class="kw">return</span> <span class="bn">iter</span>(<span class="bn bn-pseudo">self</span>.<span class="name">words</span>)
+
+ <span class="kw">def </span><span class="fun">__len__</span>(<span class="bn bn-pseudo">self</span>):
+ <span class="kw">return</span> <span class="bn">len</span>(<span class="bn bn-pseudo">self</span>.<span class="name">words</span>)
+
+ <span class="kw">def </span><span class="fun">is_required</span>(<span class="bn bn-pseudo">self</span>, <span class="name">word</span>):
+ <span class="kw">return</span> <span class="name">word</span> <span class="op op-word">in</span> <span class="bn bn-pseudo">self</span>.<span class="name">required</span>
+
+ <span class="kw">def </span><span class="fun">is_unwanted</span>(<span class="bn bn-pseudo">self</span>, <span class="name">word</span>):
+ <span class="kw">return</span> <span class="name">word</span> <span class="op op-word">in</span> <span class="bn bn-pseudo">self</span>.<span class="name">unwanted</span>
+
+ <span class="kw">def </span><span class="fun">is_optional</span>(<span class="bn bn-pseudo">self</span>, <span class="name">word</span>):
+ <span class="kw">return</span> <span class="name">word</span> <span class="op op-word">in</span> <span class="bn bn-pseudo">self</span>.<span class="name">optional</span>
+
+ <span class="kw">def </span><span class="fun">__repr__</span>(<span class="bn bn-pseudo">self</span>):
+ <span class="kw">return</span> <span class="st st-sg">&#39;</span><span class="st">&lt;</span><span class="st st-int">%s</span><span class="st"> </span><span class="st st-int">%r</span><span class="st">&gt;</span><span class="st st-sg">&#39;</span> <span class="op">%</span> (
+ <span class="bn bn-pseudo">self</span>.<span class="name">__class__</span>.<span class="name">__name__</span>,
+ <span class="bn bn-pseudo">self</span>.<span class="name">text</span>
+
+ )
+
+
+<span class="cm">#XXX: early draft</span>
+<span class="kw">class </span><span class="cls">Result</span>(<span class="bn">object</span>, <span class="name">LinkableMixin</span>):
+ <span class="st st-db">&quot;&quot;&quot;</span><span class="st">
+ Search result object.
+ </span><span class="st st-db">&quot;&quot;&quot;</span>
+
+ <span class="kw">def </span><span class="fun">__init__</span>(<span class="bn bn-pseudo">self</span>, <span class="name">ctx</span>, <span class="name">title</span>, <span class="name">relative_url</span>, <span class="name">description</span>):
+ <span class="bn bn-pseudo">self</span>.<span class="name">ctx</span> <span class="op">=</span> <span class="name">ctx</span>
+
+ <span class="bn bn-pseudo">self</span>.<span class="name">title</span> <span class="op">=</span> <span class="name">title</span>
+ <span class="bn bn-pseudo">self</span>.<span class="name">relative_url</span> <span class="op">=</span> <span class="name">relative_url</span>
+
+ <span class="bn bn-pseudo">self</span>.<span class="name">description</span> <span class="op">=</span> <span class="name">description</span>
+
+ <span class="kw">def </span><span class="fun">__repr__</span>(<span class="bn bn-pseudo">self</span>):
+ <span class="kw">return</span> <span class="st st-sg">&#39;</span><span class="st">&lt;</span><span class="st st-int">%s</span><span class="st"> </span><span class="st st-int">%r</span><span class="st">&gt;</span><span class="st st-sg">&#39;</span> <span class="op">%</span> (
+ <span class="bn bn-pseudo">self</span>.<span class="name">__class__</span>.<span class="name">__name__</span>,
+ <span class="bn bn-pseudo">self</span>.<span class="name">title</span>
+
+ )
+
+
+<span class="kw">class </span><span class="cls">SearchProvider</span>(<span class="name">Component</span>):
+ <span class="cm">#: name of the search provider</span>
+ <span class="name">name</span> <span class="op">=</span> <span class="bn bn-pseudo">None</span>
+
+ <span class="kw">def </span><span class="fun">get_title</span>(<span class="bn bn-pseudo">self</span>, <span class="name">req</span>):
+ <span class="st st-db">&quot;&quot;&quot;</span><span class="st">Return a translated version of the search provider name.</span><span class="st st-db">&quot;&quot;&quot;</span>
+ <span class="kw">return</span> <span class="bn bn-pseudo">self</span>.<span class="name">__class__</span>.<span class="name">__name__</span>
+
+ <span class="kw">def </span><span class="fun">can_search</span>(<span class="bn bn-pseudo">self</span>, <span class="name">req</span>):
+ <span class="st st-db">&quot;&quot;&quot;</span><span class="st">Return True if a user is allowed to use this search provider.</span><span class="st st-db">&quot;&quot;&quot;</span>
+ <span class="kw">return</span> <span class="bn bn-pseudo">True</span>
+
+ <span class="kw">def </span><span class="fun">query</span>(<span class="bn bn-pseudo">self</span>, <span class="name">query</span>):
+ <span class="st st-db">&quot;&quot;&quot;</span><span class="st">Perform a query, has to return an iterable of result objects.</span><span class="st st-db">&quot;&quot;&quot;</span>
+ <span class="kw">return</span> ()
+<span class="cm"># -*- coding: utf-8 -*-</span>
+<span class="st st-db">&quot;&quot;&quot;</span><span class="st">
+
+ pocoo.pkg.core.session
+ ~~~~~~~~~~~~~~~~~~~~~~
+
+ Pocoo session handling.
+
+ :copyright: 2006 by Georg Brandl, Armin Ronacher.
+ :license: GNU GPL, see LICENSE for more details.
+</span><span class="st st-db">&quot;&quot;&quot;</span>
+
+<span class="kw">import </span><span class="cls">md5</span>
+<span class="kw">import </span><span class="cls">time</span>
+<span class="kw">import </span><span class="cls">random</span>
+
+<span class="kw">from </span><span class="cls">datetime</span><span class="kw"> import</span> <span class="name">datetime</span>, <span class="name">timedelta</span>
+
+<span class="kw">from </span><span class="cls">pocoo.application</span><span class="kw"> import</span> <span class="name">RequestWrapper</span>
+<span class="kw">from </span><span class="cls">pocoo.settings</span><span class="kw"> import</span> <span class="name">cfg</span>
+<span class="kw">from </span><span class="cls">pocoo.db</span><span class="kw"> import</span> <span class="name">meta</span>
+
+<span class="kw">from </span><span class="cls">pocoo.utils.uri</span><span class="kw"> import</span> <span class="name">urlencode</span>
+<span class="kw">from </span><span class="cls">pocoo.pkg.core.db</span><span class="kw"> import</span> <span class="name">sessions</span>, <span class="name">users</span>
+<span class="kw">from </span><span class="cls">pocoo.pkg.core.auth</span><span class="kw"> import</span> <span class="name">get_auth_provider</span>
+
+
+<span class="kw">def </span><span class="fun">get_active_sessions</span>(<span class="name">ctx</span>, <span class="name">delta</span><span class="op">=</span><span class="name">timedelta</span>(<span class="name">minutes</span><span class="op">=</span><span class="nb nb-int">5</span>)):
+ <span class="st st-db">&quot;&quot;&quot;</span><span class="st">
+ Return a tuple in the following form::
+
+ (sessions, user_count, guest_count, total)
+
+ sessions is a dict for the template with all relevant
+ information about the sessions, user_count is the
+ amount of all logged in users, guest_count is the
+ amount of all anonymous users and total is the total
+ number of all sessions being active.
+ </span><span class="st st-db">&quot;&quot;&quot;</span>
+
+ <span class="name">provider</span> <span class="op">=</span> <span class="name">get_auth_provider</span>(<span class="name">ctx</span>)
+ <span class="name">now</span> <span class="op">=</span> <span class="name">datetime</span>.<span class="name">utcnow</span>()
+ <span class="kw">def </span><span class="fun">do</span>(<span class="name">con</span>):
+ <span class="name">session_list</span> <span class="op">=</span> []
+ <span class="name">user_count</span> <span class="op">=</span> <span class="nb nb-int">0</span>
+
+ <span class="name">guest_count</span> <span class="op">=</span> <span class="nb nb-int">0</span>
+ <span class="name">r</span> <span class="op">=</span> <span class="name">con</span>.<span class="name">execute</span>(<span class="name">sessions</span>.<span class="name">select</span>(
+ <span class="name">sessions</span>.<span class="name">c</span>.<span class="name">last_reload</span> <span class="op">&gt;</span> <span class="name">now</span> <span class="op">-</span> <span class="name">delta</span>
+
+ ))
+ <span class="kw">while</span> <span class="bn bn-pseudo">True</span>:
+ <span class="name">row</span> <span class="op">=</span> <span class="name">r</span>.<span class="name">fetchone</span>()
+ <span class="kw">if</span> <span class="name">row</span> <span class="op op-word">is</span> <span class="bn bn-pseudo">None</span>:
+ <span class="kw">break</span>
+
+ <span class="name">user</span> <span class="op">=</span> <span class="name">con</span>.<span class="name">execute</span>(<span class="name">meta</span>.<span class="name">select</span>([<span class="name">users</span>.<span class="name">c</span>.<span class="name">user_id</span>, <span class="name">users</span>.<span class="name">c</span>.<span class="name">username</span>],
+ <span class="name">users</span>.<span class="name">c</span>.<span class="name">user_id</span> <span class="op">==</span> <span class="name">provider</span>.<span class="name">get_user_id</span>(<span class="name">row</span>[<span class="st st-sg">&#39;</span><span class="st">data</span><span class="st st-sg">&#39;</span>])
+ )).<span class="name">fetchone</span>()
+ <span class="kw">if</span> <span class="name">user</span> <span class="op op-word">is</span> <span class="bn bn-pseudo">None</span> <span class="op op-word">or</span> <span class="name">user</span>[<span class="st st-sg">&#39;</span><span class="st">user_id</span><span class="st st-sg">&#39;</span>] <span class="op">&lt;</span> <span class="nb nb-int">1</span>:
+ <span class="name">guest_count</span> <span class="op">+=</span> <span class="nb nb-int">1</span>
+
+ <span class="name">user_data</span> <span class="op">=</span> <span class="bn bn-pseudo">None</span>
+ <span class="kw">else</span>:
+ <span class="name">user_data</span> <span class="op">=</span> {
+ <span class="st st-sg">&#39;</span><span class="st">username</span><span class="st st-sg">&#39;</span>: <span class="name">user</span>[<span class="st st-sg">&#39;</span><span class="st">username</span><span class="st st-sg">&#39;</span>],
+ <span class="st st-sg">&#39;</span><span class="st">user_id</span><span class="st st-sg">&#39;</span>: <span class="name">user</span>[<span class="st st-sg">&#39;</span><span class="st">user_id</span><span class="st st-sg">&#39;</span>],
+ <span class="st st-sg">&#39;</span><span class="st">url</span><span class="st st-sg">&#39;</span>: <span class="name">ctx</span>.<span class="name">make_url</span>(<span class="st st-sg">&#39;</span><span class="st">users</span><span class="st st-sg">&#39;</span>, <span class="name">urlencode</span>(<span class="name">user</span>[<span class="st st-sg">&#39;</span><span class="st">username</span><span class="st st-sg">&#39;</span>])),
+ }
+ <span class="name">user_count</span> <span class="op">+=</span> <span class="nb nb-int">1</span>
+
+ <span class="name">session_list</span>.<span class="name">append</span>({
+ <span class="st st-sg">&#39;</span><span class="st">last_reload</span><span class="st st-sg">&#39;</span>: <span class="name">row</span>[<span class="st st-sg">&#39;</span><span class="st">last_reload</span><span class="st st-sg">&#39;</span>],
+ <span class="st st-sg">&#39;</span><span class="st">user</span><span class="st st-sg">&#39;</span>: <span class="name">user_data</span>
+ })
+ <span class="name">session_list</span>.<span class="name">sort</span>(<span class="name">key</span><span class="op">=</span><span class="kw">lambda</span> <span class="name">x</span>: <span class="name">x</span>[<span class="st st-sg">&#39;</span><span class="st">last_reload</span><span class="st st-sg">&#39;</span>])
+ <span class="kw">return</span> <span class="name">session_list</span>, <span class="name">user_count</span>, <span class="name">guest_count</span>, <span class="name">user_count</span> <span class="op">+</span> <span class="name">guest_count</span>
+
+ <span class="kw">return</span> <span class="name">ctx</span>.<span class="name">engine</span>.<span class="name">transaction</span>(<span class="name">do</span>)
+
+
+<span class="kw">def </span><span class="fun">get_sessions_by_action</span>(<span class="name">ctx</span>, <span class="name">url</span>):
+ <span class="name">provider</span> <span class="op">=</span> <span class="name">get_auth_provider</span>(<span class="name">ctx</span>)
+ <span class="kw">def </span><span class="fun">do</span>(<span class="name">con</span>):
+ <span class="name">session_list</span> <span class="op">=</span> []
+ <span class="name">r</span> <span class="op">=</span> <span class="name">con</span>.<span class="name">execute</span>(<span class="name">sessions</span>.<span class="name">select</span>(
+ <span class="name">sessions</span>.<span class="name">c</span>.<span class="name">action</span> <span class="op">==</span> <span class="name">url</span>
+
+ ))
+ <span class="kw">while</span> <span class="bn bn-pseudo">True</span>:
+ <span class="name">row</span> <span class="op">=</span> <span class="name">r</span>.<span class="name">fetchone</span>()
+ <span class="kw">if</span> <span class="name">row</span> <span class="op op-word">is</span> <span class="bn bn-pseudo">None</span>:
+ <span class="kw">break</span>
+
+ <span class="name">user</span> <span class="op">=</span> <span class="name">con</span>.<span class="name">execute</span>(<span class="name">meta</span>.<span class="name">select</span>([<span class="name">users</span>.<span class="name">c</span>.<span class="name">user_id</span>, <span class="name">users</span>.<span class="name">c</span>.<span class="name">username</span>],
+ <span class="name">users</span>.<span class="name">c</span>.<span class="name">user_id</span> <span class="op">==</span> <span class="name">provider</span>.<span class="name">get_user_id</span>(<span class="name">row</span>[<span class="st st-sg">&#39;</span><span class="st">data</span><span class="st st-sg">&#39;</span>])
+ )).<span class="name">fetchone</span>()
+ <span class="kw">if</span> <span class="name">user</span> <span class="op op-word">is</span> <span class="bn bn-pseudo">None</span> <span class="op op-word">or</span> <span class="name">user</span>[<span class="st st-sg">&#39;</span><span class="st">user_id</span><span class="st st-sg">&#39;</span>] <span class="op">&lt;</span> <span class="nb nb-int">1</span>:
+ <span class="name">user_data</span> <span class="op">=</span> <span class="bn bn-pseudo">None</span>
+
+ <span class="kw">else</span>:
+ <span class="name">user_data</span> <span class="op">=</span> {
+ <span class="st st-sg">&#39;</span><span class="st">username</span><span class="st st-sg">&#39;</span>: <span class="name">user</span>[<span class="st st-sg">&#39;</span><span class="st">username</span><span class="st st-sg">&#39;</span>],
+ <span class="st st-sg">&#39;</span><span class="st">url</span><span class="st st-sg">&#39;</span>: <span class="name">ctx</span>.<span class="name">make_url</span>(<span class="st st-sg">&#39;</span><span class="st">users</span><span class="st st-sg">&#39;</span>, <span class="name">urlencode</span>(<span class="name">user</span>[<span class="st st-sg">&#39;</span><span class="st">username</span><span class="st st-sg">&#39;</span>])),
+ }
+ <span class="name">session_list</span>.<span class="name">append</span>(<span class="name">user_data</span>)
+ <span class="kw">return</span> <span class="name">session_list</span>
+
+ <span class="kw">return</span> <span class="name">ctx</span>.<span class="name">engine</span>.<span class="name">transaction</span>(<span class="name">do</span>)
+
+
+<span class="kw">class </span><span class="cls">Session</span>(<span class="bn">dict</span>):
+ <span class="st st-db">&quot;&quot;&quot;</span><span class="st">Session Model</span><span class="st st-db">&quot;&quot;&quot;</span>
+
+ <span class="kw">def </span><span class="fun">__init__</span>(<span class="bn bn-pseudo">self</span>, <span class="name">ctx</span>, <span class="name">sid</span>, <span class="name">ip</span>, <span class="name">path</span>):
+ <span class="bn bn-pseudo">self</span>.<span class="name">ctx</span> <span class="op">=</span> <span class="name">ctx</span>
+
+ <span class="bn bn-pseudo">self</span>.<span class="name">path</span> <span class="op">=</span> <span class="name">path</span>
+ <span class="name">r</span> <span class="op">=</span> <span class="name">ctx</span>.<span class="name">engine</span>.<span class="name">execute</span>(<span class="name">sessions</span>.<span class="name">select</span>(
+ (<span class="name">sessions</span>.<span class="name">c</span>.<span class="name">session_key</span> <span class="op">==</span> <span class="name">sid</span>) <span class="op">&amp;</span>
+
+ (<span class="name">sessions</span>.<span class="name">c</span>.<span class="name">ip_addr</span> <span class="op">==</span> <span class="name">ip</span>) <span class="op">&amp;</span>
+ (<span class="name">sessions</span>.<span class="name">c</span>.<span class="name">expires</span> <span class="op">&gt;=</span> <span class="name">datetime</span>.<span class="name">utcnow</span>())
+ ))
+ <span class="name">data</span> <span class="op">=</span> <span class="name">r</span>.<span class="name">fetchone</span>()
+ <span class="kw">if</span> <span class="name">data</span> <span class="op op-word">is</span> <span class="bn bn-pseudo">None</span>:
+ <span class="bn">super</span>(<span class="name">Session</span>, <span class="bn bn-pseudo">self</span>).<span class="name">__init__</span>()
+ <span class="bn bn-pseudo">self</span>.<span class="name">sid</span> <span class="op">=</span> <span class="bn bn-pseudo">None</span>
+
+ <span class="kw">else</span>:
+ <span class="bn">super</span>(<span class="name">Session</span>, <span class="bn bn-pseudo">self</span>).<span class="name">__init__</span>(<span class="name">data</span>[<span class="st st-sg">&#39;</span><span class="st">data</span><span class="st st-sg">&#39;</span>])
+ <span class="bn bn-pseudo">self</span>.<span class="name">sid</span> <span class="op">=</span> <span class="name">sid</span>
+
+ <span class="bn bn-pseudo">self</span>.<span class="name">ip</span> <span class="op">=</span> <span class="name">ip</span>
+
+ <span class="kw">def </span><span class="fun">__hash__</span>(<span class="bn bn-pseudo">self</span>):
+ <span class="kw">return</span> <span class="bn">hash</span>(<span class="bn bn-pseudo">self</span>.<span class="name">sid</span>)
+
+ <span class="kw">def </span><span class="fun">to_dict</span>(<span class="bn bn-pseudo">self</span>):
+ <span class="st st-db">&quot;&quot;&quot;</span><span class="st">Return the session data as normal dict.</span><span class="st st-db">&quot;&quot;&quot;</span>
+
+ <span class="kw">return</span> <span class="bn">dict</span>(<span class="bn bn-pseudo">self</span>)
+
+ <span class="kw">def </span><span class="fun">save</span>(<span class="bn bn-pseudo">self</span>, <span class="name">cookie_expire</span>):
+ <span class="st st-db">&quot;&quot;&quot;</span><span class="st">Save changes back to the database and updates
+ expires and last_reload. Returns a datetime object
+ with the time of the session expire.</span><span class="st st-db">&quot;&quot;&quot;</span>
+ <span class="name">now</span> <span class="op">=</span> <span class="name">datetime</span>.<span class="name">utcnow</span>()
+ <span class="name">expires</span> <span class="op">=</span> <span class="name">now</span> <span class="op">+</span> <span class="name">timedelta</span>(<span class="name">seconds</span><span class="op">=</span><span class="name">cookie_expire</span>)
+ <span class="kw">if</span> <span class="op op-word">not</span> <span class="bn bn-pseudo">self</span>.<span class="name">sid</span>:
+ <span class="kw">while</span> <span class="bn bn-pseudo">True</span>:
+ <span class="name">hashval</span> <span class="op">=</span> <span class="name">md5</span>.<span class="name">new</span>(<span class="st st-sg">&#39;</span><span class="st st-int">%s</span><span class="st">|</span><span class="st st-int">%s</span><span class="st st-sg">&#39;</span> <span class="op">%</span> (<span class="name">random</span>.<span class="name">random</span>(), <span class="name">time</span>.<span class="name">time</span>()))
+ <span class="name">sid</span> <span class="op">=</span> <span class="name">hashval</span>.<span class="name">hexdigest</span>()
+ <span class="name">r</span> <span class="op">=</span> <span class="bn bn-pseudo">self</span>.<span class="name">ctx</span>.<span class="name">engine</span>.<span class="name">execute</span>(<span class="name">sessions</span>.<span class="name">select</span>(
+ <span class="name">sessions</span>.<span class="name">c</span>.<span class="name">session_key</span> <span class="op">==</span> <span class="name">sid</span>
+
+ ))
+ <span class="kw">if</span> <span class="name">r</span>.<span class="name">fetchone</span>() <span class="op op-word">is</span> <span class="bn bn-pseudo">None</span>:
+ <span class="kw">break</span>
+ <span class="bn bn-pseudo">self</span>.<span class="name">sid</span> <span class="op">=</span> <span class="name">sid</span>
+
+ <span class="bn bn-pseudo">self</span>.<span class="name">ctx</span>.<span class="name">engine</span>.<span class="name">execute</span>(<span class="name">sessions</span>.<span class="name">insert</span>(),
+ <span class="name">session_key</span> <span class="op">=</span> <span class="bn bn-pseudo">self</span>.<span class="name">sid</span>,
+ <span class="name">ip_addr</span> <span class="op">=</span> <span class="bn bn-pseudo">self</span>.<span class="name">ip</span>,
+ <span class="name">expires</span> <span class="op">=</span> <span class="name">expires</span>,
+ <span class="name">last_reload</span> <span class="op">=</span> <span class="name">now</span>,
+ <span class="name">action</span> <span class="op">=</span> <span class="bn bn-pseudo">self</span>.<span class="name">path</span>,
+ <span class="name">data</span> <span class="op">=</span> <span class="bn bn-pseudo">self</span>.<span class="name">to_dict</span>()
+ )
+ <span class="kw">else</span>:
+ <span class="name">q</span> <span class="op">=</span> <span class="name">sessions</span>.<span class="name">c</span>.<span class="name">session_key</span> <span class="op">==</span> <span class="bn bn-pseudo">self</span>.<span class="name">sid</span>
+
+ <span class="bn bn-pseudo">self</span>.<span class="name">ctx</span>.<span class="name">engine</span>.<span class="name">execute</span>(<span class="name">sessions</span>.<span class="name">update</span>(<span class="name">q</span>),
+ <span class="name">expires</span> <span class="op">=</span> <span class="name">expires</span>,
+ <span class="name">last_reload</span> <span class="op">=</span> <span class="name">now</span>,
+ <span class="name">action</span> <span class="op">=</span> <span class="bn bn-pseudo">self</span>.<span class="name">path</span>,
+ <span class="name">data</span> <span class="op">=</span> <span class="bn bn-pseudo">self</span>.<span class="name">to_dict</span>()
+ )
+ <span class="kw">return</span> <span class="name">expires</span>
+
+ <span class="kw">def </span><span class="fun">__repr__</span>(<span class="bn bn-pseudo">self</span>):
+ <span class="kw">return</span> <span class="st st-sg">&#39;</span><span class="st">&lt;</span><span class="st st-int">%s</span><span class="st"> </span><span class="st st-int">%s</span><span class="st">: </span><span class="st st-int">%r</span><span class="st">&gt;</span><span class="st st-sg">&#39;</span> <span class="op">%</span> (
+ <span class="bn bn-pseudo">self</span>.<span class="name">__class__</span>.<span class="name">__name__</span>,
+ <span class="bn bn-pseudo">self</span>.<span class="name">sid</span>,
+ <span class="bn">dict</span>.<span class="name">__repr__</span>(<span class="bn bn-pseudo">self</span>)
+ )
+
+
+
+<span class="kw">class </span><span class="cls">SessionWrapper</span>(<span class="name">RequestWrapper</span>):
+ <span class="st st-db">&quot;&quot;&quot;</span><span class="st">
+ SessionWrapper loads/stores request.session.
+ </span><span class="st st-db">&quot;&quot;&quot;</span>
+ <span class="name">cookie_name</span> <span class="op">=</span> <span class="name">cfg</span>.<span class="name">str</span>(<span class="st st-sg">&#39;</span><span class="st">board</span><span class="st st-sg">&#39;</span>, <span class="st st-sg">&#39;</span><span class="st">cookiename</span><span class="st st-sg">&#39;</span>, <span class="st st-sg">&#39;</span><span class="st">SESSION</span><span class="st st-sg">&#39;</span>)
+ <span class="name">cookie_expires</span> <span class="op">=</span> <span class="name">cfg</span>.<span class="name">int</span>(<span class="st st-sg">&#39;</span><span class="st">board</span><span class="st st-sg">&#39;</span>, <span class="st st-sg">&#39;</span><span class="st">cookieexpires</span><span class="st st-sg">&#39;</span>, <span class="nb nb-int">7200</span>)
+
+ <span class="kw">def </span><span class="fun">get_priority</span>(<span class="bn bn-pseudo">self</span>):
+ <span class="cm"># request.session should be set before anything else,</span>
+
+ <span class="cm"># but after a possible caching system.</span>
+ <span class="kw">return</span> <span class="nb nb-int">2</span>
+
+ <span class="kw">def </span><span class="fun">process_request</span>(<span class="bn bn-pseudo">self</span>, <span class="name">req</span>):
+ <span class="name">cookie</span> <span class="op">=</span> <span class="name">req</span>.<span class="name">cookies</span>.<span class="name">get</span>(<span class="bn bn-pseudo">self</span>.<span class="name">cookie_name</span>, <span class="bn bn-pseudo">None</span>)
+ <span class="name">sid</span> <span class="op">=</span> <span class="name">cookie</span> <span class="op op-word">and</span> <span class="name">cookie</span>.<span class="name">value</span> <span class="op op-word">or</span> <span class="bn bn-pseudo">None</span>
+
+ <span class="name">ip</span> <span class="op">=</span> <span class="name">req</span>.<span class="name">environ</span>[<span class="st st-sg">&#39;</span><span class="st">REMOTE_ADDR</span><span class="st st-sg">&#39;</span>]
+ <span class="name">req</span>.<span class="name">session</span> <span class="op">=</span> <span class="name">Session</span>(<span class="bn bn-pseudo">self</span>.<span class="name">ctx</span>, <span class="name">sid</span>, <span class="name">ip</span>, <span class="name">req</span>.<span class="name">path</span>)
+
+ <span class="kw">def </span><span class="fun">process_response</span>(<span class="bn bn-pseudo">self</span>, <span class="name">req</span>, <span class="name">resp</span>):
+ <span class="name">expires</span> <span class="op">=</span> <span class="name">req</span>.<span class="name">session</span>.<span class="name">save</span>(<span class="bn bn-pseudo">self</span>.<span class="name">cookie_expires</span>)
+ <span class="name">resp</span>.<span class="name">set_cookie</span>(<span class="bn">str</span>(<span class="bn bn-pseudo">self</span>.<span class="name">cookie_name</span>), <span class="name">req</span>.<span class="name">session</span>.<span class="name">sid</span>,
+ <span class="name">max_age</span><span class="op">=</span><span class="bn bn-pseudo">self</span>.<span class="name">cookie_expires</span>, <span class="name">expires</span><span class="op">=</span><span class="name">expires</span>)
+ <span class="kw">return</span> <span class="name">resp</span>
+
+<span class="cm"># -*- coding: utf-8 -*-</span>
+<span class="st st-db">&quot;&quot;&quot;</span><span class="st">
+ pocoo.pkg.core.simplehtml
+ ~~~~~~~~~~~~~~~~~~~~~~~~~
+
+ This module provides a MarkupFormat that allows the user to use
+ HTML as markup but strips dangerous tags like ``&lt;script&gt;`` and others.
+
+ It uses the ``secure_html`` method from the ``html`` util.
+
+ :copyright: 2006 by Armin Ronacher.
+ :license: GNU GPL, see LICENSE for more details.
+</span><span class="st st-db">&quot;&quot;&quot;</span>
+
+<span class="kw">from </span><span class="cls">pocoo.utils.html</span><span class="kw"> import</span> <span class="name">secure_html</span>
+<span class="kw">from </span><span class="cls">pocoo.pkg.core.textfmt</span><span class="kw"> import</span> <span class="name">MarkupFormat</span>
+
+
+<span class="kw">class </span><span class="cls">SimpleHTML</span>(<span class="name">MarkupFormat</span>):
+ <span class="name">name</span> <span class="op">=</span> <span class="st st-sg">&#39;</span><span class="st">simplehtml</span><span class="st st-sg">&#39;</span>
+
+ <span class="kw">def </span><span class="fun">parse</span>(<span class="bn bn-pseudo">self</span>, <span class="name">text</span>):
+ <span class="kw">return</span> <span class="name">secure_html</span>(<span class="name">text</span>)
+
+ <span class="kw">def </span><span class="fun">quote_text</span>(<span class="bn bn-pseudo">self</span>, <span class="name">req</span>, <span class="name">text</span>, <span class="name">username</span><span class="op">=</span><span class="bn bn-pseudo">None</span>):
+ <span class="kw">if</span> <span class="name">username</span> <span class="op op-word">is</span> <span class="op op-word">not</span> <span class="bn bn-pseudo">None</span>:
+ <span class="name">_</span> <span class="op">=</span> <span class="name">req</span>.<span class="name">gettext</span>
+
+ <span class="name">text</span> <span class="op">=</span> <span class="name">u</span><span class="st st-sg">&#39;</span><span class="st">&lt;em&gt;</span><span class="st st-int">%s</span><span class="st">:&lt;/em&gt;&lt;br /&gt;</span><span class="st st-int">%s</span><span class="st st-sg">&#39;</span> <span class="op">%</span> ((<span class="name">_</span>(<span class="st st-sg">&#39;</span><span class="st st-int">%s</span><span class="st"> wrote</span><span class="st st-sg">&#39;</span>) <span class="op">%</span> <span class="name">username</span>), <span class="name">text</span>)
+ <span class="kw">return</span> <span class="name">u</span><span class="st st-sg">&#39;</span><span class="st">&lt;blockquote&gt;</span><span class="st st-int">%s</span><span class="st">&lt;/blockquote&gt;</span><span class="st st-sg">&#39;</span> <span class="op">%</span> <span class="name">text</span>
+
+<span class="cm"># -*- coding: utf-8 -*-</span>
+<span class="st st-db">&quot;&quot;&quot;</span><span class="st">
+ pocoo.pkg.core.smilies
+ ~~~~~~~~~~~~~~~~~~~~~~
+
+ Pocoo smilies parser.
+
+ :copyright: 2006 by Benjamin Wiegand, Georg Brandl.
+ :license: GNU GPL, see LICENSE for more details.
+</span><span class="st st-db">&quot;&quot;&quot;</span>
+<span class="kw">from </span><span class="cls">pocoo</span><span class="kw"> import</span> <span class="name">Component</span>
+<span class="kw">from </span><span class="cls">pocoo.utils.html</span><span class="kw"> import</span> <span class="name">escape_html</span>
+
+
+<span class="kw">def </span><span class="fun">replace_smilies</span>(<span class="name">ctx</span>, <span class="name">text</span>):
+ <span class="st st-db">&quot;&quot;&quot;</span><span class="st">
+ Replace smilies in ``text``, using all providers listed in the
+ board config.
+ </span><span class="st st-db">&quot;&quot;&quot;</span>
+ <span class="name">smiley_providers</span> <span class="op">=</span> <span class="name">ctx</span>.<span class="name">cfg</span>.<span class="name">get</span>(<span class="st st-sg">&#39;</span><span class="st">board</span><span class="st st-sg">&#39;</span>, <span class="st st-sg">&#39;</span><span class="st">smiley_providers</span><span class="st st-sg">&#39;</span>, [<span class="st st-sg">&#39;</span><span class="st">default</span><span class="st st-sg">&#39;</span>])
+ <span class="kw">for</span> <span class="name">provider</span> <span class="op op-word">in</span> <span class="name">ctx</span>.<span class="name">get_components</span>(<span class="name">SmileyProvider</span>):
+ <span class="kw">if</span> <span class="name">provider</span>.<span class="name">name</span> <span class="op op-word">not</span> <span class="op op-word">in</span> <span class="name">smiley_providers</span>:
+ <span class="kw">continue</span>
+
+ <span class="kw">for</span> <span class="name">smiley</span> <span class="op op-word">in</span> <span class="name">provider</span>.<span class="name">smilies</span>:
+ <span class="name">text</span> <span class="op">=</span> <span class="name">text</span>.<span class="name">replace</span>(<span class="name">smiley</span>[<span class="nb nb-int">0</span>], <span class="name">provider</span>.<span class="name">render_smiley</span>(<span class="name">smiley</span>))
+ <span class="kw">return</span> <span class="name">text</span>
+
+
+<span class="kw">def </span><span class="fun">get_smiley_buttons</span>(<span class="name">ctx</span>):
+ <span class="st st-db">&quot;&quot;&quot;</span><span class="st">
+ Return a list of button dictionaries usable for the BBCodeEditor
+ JavaScript app.
+ </span><span class="st st-db">&quot;&quot;&quot;</span>
+ <span class="name">res</span> <span class="op">=</span> []
+ <span class="name">smiley_providers</span> <span class="op">=</span> <span class="name">ctx</span>.<span class="name">cfg</span>.<span class="name">get</span>(<span class="st st-sg">&#39;</span><span class="st">board</span><span class="st st-sg">&#39;</span>, <span class="st st-sg">&#39;</span><span class="st">smiley_providers</span><span class="st st-sg">&#39;</span>, [<span class="st st-sg">&#39;</span><span class="st">default</span><span class="st st-sg">&#39;</span>])
+ <span class="kw">for</span> <span class="name">provider</span> <span class="op op-word">in</span> <span class="name">ctx</span>.<span class="name">get_components</span>(<span class="name">SmileyProvider</span>):
+ <span class="kw">if</span> <span class="name">provider</span>.<span class="name">name</span> <span class="op op-word">not</span> <span class="op op-word">in</span> <span class="name">smiley_providers</span>:
+ <span class="kw">continue</span>
+
+ <span class="kw">for</span> <span class="name">smiley</span> <span class="op op-word">in</span> <span class="name">provider</span>.<span class="name">smilies</span>:
+ <span class="name">res</span>.<span class="name">append</span>(<span class="name">smiley</span> <span class="op">+</span> (<span class="name">ctx</span>.<span class="name">make_url</span>(<span class="name">provider</span>.<span class="name">smiley_dir</span>),))
+ <span class="kw">return</span> <span class="name">res</span>
+
+
+<span class="kw">class </span><span class="cls">SmileyProvider</span>(<span class="name">Component</span>):
+ <span class="st st-db">&quot;&quot;&quot;</span><span class="st">
+ A SmileyProvider maps small text strings to images.
+ </span><span class="st st-db">&quot;&quot;&quot;</span>
+
+ <span class="cm">#: List of smilies this component provides, in the form</span>
+ <span class="cm">#: ``(&#39;textform&#39;, &#39;imagename&#39;)``.</span>
+
+ <span class="cm">#: ``imagename`` is relative to the ``smiley_dir`` below.</span>
+ <span class="name">smilies</span> <span class="op">=</span> []
+
+ <span class="cm">#: Directory where smilies of this provider can be found.</span>
+ <span class="cm">#: Must be relative to the forum root, e.g.</span>
+ <span class="cm">#: ``!cobalt/pkgname/default/img/smilies/``.</span>
+
+ <span class="name">smiley_dir</span> <span class="op">=</span> <span class="st st-db">&quot;&quot;</span>
+
+ <span class="cm">#: Name (can be overwritten, must be lowercase).</span>
+ <span class="cm">#: Used for the configuration setting.</span>
+ <span class="deco">@property</span>
+ <span class="kw">def </span><span class="fun">name</span>(<span class="bn bn-pseudo">self</span>):
+ <span class="kw">return</span> <span class="bn bn-pseudo">self</span>.<span class="name">__class__</span>.<span class="name">__name__</span>.<span class="name">lower</span>()
+
+ <span class="kw">def </span><span class="fun">render_smiley</span>(<span class="bn bn-pseudo">self</span>, <span class="name">smiley</span>):
+ <span class="st st-db">&quot;&quot;&quot;</span><span class="st">
+
+ Render a smiley. This doesn&#39;t need to be overridden in normal
+ cases.
+
+ :return: HTML for the smiley image.
+ </span><span class="st st-db">&quot;&quot;&quot;</span>
+ <span class="kw">return</span> <span class="st st-sg">&#39;</span><span class="st">&lt;img src=&quot;</span><span class="st st-int">%s</span><span class="st">&quot; alt=&quot;</span><span class="st st-int">%s</span><span class="st">&quot; /&gt;</span><span class="st st-sg">&#39;</span> <span class="op">%</span> (
+ <span class="bn bn-pseudo">self</span>.<span class="name">ctx</span>.<span class="name">make_url</span>(<span class="bn bn-pseudo">self</span>.<span class="name">smiley_dir</span>, <span class="name">smiley</span>[<span class="nb nb-int">1</span>]),
+ <span class="name">escape_html</span>(<span class="name">smiley</span>[<span class="nb nb-int">0</span>])
+ )
+
+
+
+<span class="kw">class </span><span class="cls">Default</span>(<span class="name">SmileyProvider</span>):
+ <span class="st st-db">&quot;&quot;&quot;</span><span class="st">
+ Default Pocoo smilies.
+ </span><span class="st st-db">&quot;&quot;&quot;</span>
+ <span class="name">smilies</span> <span class="op">=</span> [
+ (<span class="st st-sg">&#39;</span><span class="st">;-)</span><span class="st st-sg">&#39;</span>, <span class="st st-sg">&#39;</span><span class="st">wink.png</span><span class="st st-sg">&#39;</span>),
+ (<span class="st st-sg">&#39;</span><span class="st">:(</span><span class="st st-sg">&#39;</span>, <span class="st st-sg">&#39;</span><span class="st">sad.png</span><span class="st st-sg">&#39;</span>),
+ (<span class="st st-sg">&#39;</span><span class="st">:-)</span><span class="st st-sg">&#39;</span>, <span class="st st-sg">&#39;</span><span class="st">smile.png</span><span class="st st-sg">&#39;</span>),
+ (<span class="st st-sg">&#39;</span><span class="st">:D</span><span class="st st-sg">&#39;</span>, <span class="st st-sg">&#39;</span><span class="st">grin.png</span><span class="st st-sg">&#39;</span>),
+ ]
+
+ <span class="name">smiley_dir</span> <span class="op">=</span> <span class="st st-sg">&#39;</span><span class="st">!cobalt/core/default/img/smilies/</span><span class="st st-sg">&#39;</span>
+
+<span class="cm"># -*- coding: utf-8 -*-</span>
+<span class="st st-db">&quot;&quot;&quot;</span><span class="st">
+ pocoo.pkg.core.template
+ ~~~~~~~~~~~~~~~~~~~~~~~
+
+ Templating helpers.
+
+ :copyright: 2006 by Georg Brandl, Armin Ronacher.
+ :license: GNU GPL, see LICENSE for more details.
+</span><span class="st st-db">&quot;&quot;&quot;</span>
+<span class="kw">import </span><span class="cls">math</span>
+<span class="kw">import </span><span class="cls">jinja.exceptions</span>
+<span class="kw">from </span><span class="cls">jinja.nodes</span><span class="kw"> import</span> <span class="name">Node</span>, <span class="name">KeywordNode</span>, <span class="name">VariableNode</span>, <span class="name">CollectionNode</span>
+
+<span class="kw">from </span><span class="cls">pocoo.template</span><span class="kw"> import</span> <span class="name">FileRequirements</span>
+
+
+<span class="kw">class </span><span class="cls">BaseThemeRequirements</span>(<span class="name">FileRequirements</span>):
+
+ <span class="kw">def </span><span class="fun">get_javascript_imports</span>(<span class="bn bn-pseudo">self</span>):
+ <span class="kw">return</span> (
+ <span class="st st-sg">&#39;</span><span class="st">AJS/AJS.js</span><span class="st st-sg">&#39;</span>,
+ <span class="st st-sg">&#39;</span><span class="st">pocoo/lib/async.js</span><span class="st st-sg">&#39;</span>,
+ <span class="st st-sg">&#39;</span><span class="st">pocoo/lib/dom.js</span><span class="st st-sg">&#39;</span>,
+ <span class="st st-sg">&#39;</span><span class="st">pocoo/lib/effects.js</span><span class="st st-sg">&#39;</span>,
+ )
+
+ <span class="kw">def </span><span class="fun">get_stylesheet_imports</span>(<span class="bn bn-pseudo">self</span>):
+ <span class="kw">return</span> (
+ <span class="st st-sg">&#39;</span><span class="st">default/style/screen.css</span><span class="st st-sg">&#39;</span>,
+ )
+
+
+
+<span class="kw">class </span><span class="cls">Pagination</span>(<span class="bn">object</span>):
+ <span class="st st-db">&quot;&quot;&quot;</span><span class="st">
+ Pagination Controller. Push this into the template so that
+ the template designer has an object he can use for generating
+ an pagination.
+
+ Normally you are looking for the ``LazyPagination`` class, this
+ one is just for posts or if you have many single items which
+ are combined to a thread etc. The idea is that you don&#39;t have
+ and offset information in the url.
+ </span><span class="st st-db">&quot;&quot;&quot;</span>
+
+ <span class="kw">def </span><span class="fun">__init__</span>(<span class="bn bn-pseudo">self</span>, <span class="name">req</span>, <span class="name">idlist</span>, <span class="name">start</span>, <span class="name">per_page</span>, <span class="name">link</span>):
+ <span class="st st-db">&quot;&quot;&quot;</span><span class="st">
+
+ :param req: the request
+ :param idlist: a list of all ids for the pagination
+ :param start: the position of the first item on the page
+ :param per_page: number of items on each page
+ :param link: function which takes one id and returns a link for it
+
+ Example::
+
+ p = Pagination(req, range(50), 20, 20, lambda x: &#39;post/</span><span class="st st-int">%d</span><span class="st">&#39; </span><span class="st st-int">% x</span><span class="st">)
+
+ In this example the first page would contain the posts 0-19,
+ the second 20-39 and the first 40-50.
+ </span><span class="st st-db">&quot;&quot;&quot;</span>
+ <span class="bn bn-pseudo">self</span>.<span class="name">req</span> <span class="op">=</span> <span class="name">req</span>
+
+ <span class="bn bn-pseudo">self</span>.<span class="name">idlist</span> <span class="op">=</span> <span class="name">idlist</span>
+ <span class="bn bn-pseudo">self</span>.<span class="name">start</span> <span class="op">=</span> <span class="name">start</span>
+
+ <span class="bn bn-pseudo">self</span>.<span class="name">per_page</span> <span class="op">=</span> <span class="name">per_page</span>
+ <span class="bn bn-pseudo">self</span>.<span class="name">link</span> <span class="op">=</span> <span class="name">link</span>
+
+ <span class="kw">def </span><span class="fun">generate</span>(<span class="bn bn-pseudo">self</span>, <span class="name">normal</span>, <span class="name">active</span>, <span class="name">commata</span>, <span class="name">ellipsis</span>, <span class="name">threshold</span>):
+ <span class="st st-db">&quot;&quot;&quot;</span><span class="st">
+
+ Generate a Pagination of the data defined in the constructor.
+
+ :param normal: inserted string when the page isn&#39;t active
+ :param active: inserted string when the page is active
+ :param commata: inserted between links
+ :param ellipsis: inserted as space indicator for skiped links
+ :param threshold: number of links around start/current page and
+ end before they will be replaced by an ellipsis
+
+ ``normal`` and ``active`` can contain out of the following formatting
+ chars:
+
+ ====================== ==================================
+ ``</span><span class="st st-int">%(page)s</span><span class="st">`` number of the current page
+ ``</span><span class="st st-int">%(url)s</span><span class="st">`` absolute url of the current page
+ ====================== ==================================
+
+ Example::
+
+ p.generate(&#39;&lt;a href=&quot;</span><span class="st st-int">%(url)s</span><span class="st">&quot;&gt;</span><span class="st st-int">%(page)s</span><span class="st">&lt;/a&gt;&#39;,
+ &#39;&lt;strong&gt;</span><span class="st st-int">%(page)s</span><span class="st">&lt;/strong&gt;&#39;,
+ &#39;, &#39;,
+ &#39; ... &#39;,
+ 3)
+ </span><span class="st st-db">&quot;&quot;&quot;</span>
+
+ <span class="name">current_page_number</span> <span class="op">=</span> <span class="bn bn-pseudo">self</span>.<span class="name">start</span> <span class="op">/</span> <span class="bn bn-pseudo">self</span>.<span class="name">per_page</span> <span class="op">+</span> <span class="nb nb-int">1</span>
+
+ <span class="name">number_of_pages</span> <span class="op">=</span> <span class="bn">int</span>(<span class="name">math</span>.<span class="name">ceil</span>(<span class="bn">len</span>(<span class="bn bn-pseudo">self</span>.<span class="name">idlist</span>) <span class="op">/</span> (<span class="bn bn-pseudo">self</span>.<span class="name">per_page</span> <span class="op">*</span> <span class="nb nb-flt">1.0</span>)))
+ <span class="name">was_space</span> <span class="op">=</span> <span class="bn bn-pseudo">False</span>
+
+ <span class="name">result</span> <span class="op">=</span> []
+ <span class="kw">for</span> <span class="name">idx</span> <span class="op op-word">in</span> <span class="bn">range</span>(<span class="nb nb-int">1</span>, <span class="name">number_of_pages</span> <span class="op">+</span> <span class="nb nb-int">1</span>):
+ <span class="kw">if</span> <span class="name">idx</span> <span class="op">&lt;=</span> <span class="name">threshold</span> <span class="op op-word">or</span> <span class="name">idx</span> <span class="op">&gt;</span> <span class="name">number_of_pages</span> <span class="op">-</span> <span class="name">threshold</span> <span class="op op-word">or</span>\
+ <span class="bn">abs</span>(<span class="name">current_page_number</span> <span class="op">-</span> <span class="name">idx</span>) <span class="op">&lt;</span> <span class="name">math</span>.<span class="name">ceil</span>(<span class="name">threshold</span> <span class="op">/</span> <span class="nb nb-flt">2.0</span>):
+ <span class="kw">if</span> <span class="name">result</span> <span class="op op-word">and</span> <span class="name">result</span>[<span class="op">-</span><span class="nb nb-int">1</span>] <span class="op">!=</span> <span class="name">ellipsis</span>:
+ <span class="name">result</span>.<span class="name">append</span>(<span class="name">commata</span>)
+ <span class="name">was_space</span> <span class="op">=</span> <span class="bn bn-pseudo">False</span>
+
+ <span class="kw">if</span> <span class="name">idx</span> <span class="op">==</span> <span class="name">current_page_number</span>:
+ <span class="name">schema</span> <span class="op">=</span> <span class="name">active</span>
+ <span class="kw">else</span>:
+ <span class="name">schema</span> <span class="op">=</span> <span class="name">normal</span>
+
+ <span class="name">url</span> <span class="op">=</span> <span class="bn bn-pseudo">self</span>.<span class="name">link</span>(<span class="bn bn-pseudo">self</span>.<span class="name">idlist</span>[(<span class="name">idx</span> <span class="op">-</span> <span class="nb nb-int">1</span>) <span class="op">*</span> <span class="bn bn-pseudo">self</span>.<span class="name">per_page</span>])
+ <span class="name">result</span>.<span class="name">append</span>(<span class="name">schema</span> <span class="op">%</span> {
+ <span class="st st-sg">&#39;</span><span class="st">page</span><span class="st st-sg">&#39;</span>: <span class="name">idx</span>,
+ <span class="st st-sg">&#39;</span><span class="st">url</span><span class="st st-sg">&#39;</span>: <span class="bn bn-pseudo">self</span>.<span class="name">req</span>.<span class="name">ctx</span>.<span class="name">make_url</span>(<span class="name">url</span>),
+ })
+ <span class="kw">else</span>:
+ <span class="kw">if</span> <span class="op op-word">not</span> <span class="name">was_space</span>:
+ <span class="name">was_space</span> <span class="op">=</span> <span class="bn bn-pseudo">True</span>
+
+ <span class="name">result</span>.<span class="name">append</span>(<span class="name">ellipsis</span>)
+ <span class="kw">return</span> <span class="name">u</span><span class="st st-sg">&#39;&#39;</span>.<span class="name">join</span>(<span class="name">result</span>)
+
+ <span class="kw">def </span><span class="fun">__repr__</span>(<span class="bn bn-pseudo">self</span>):
+ <span class="kw">return</span> <span class="st st-sg">&#39;</span><span class="st">&lt;</span><span class="st st-int">%s</span><span class="st"> </span><span class="st st-int">%r</span><span class="st">&gt;</span><span class="st st-sg">&#39;</span> <span class="op">%</span> (
+ <span class="bn bn-pseudo">self</span>.<span class="name">__class__</span>.<span class="name">__name__</span>,
+ <span class="bn">str</span>(<span class="bn bn-pseudo">self</span>)
+ )
+
+ <span class="kw">def </span><span class="fun">__str__</span>(<span class="bn bn-pseudo">self</span>):
+ <span class="kw">return</span> <span class="bn bn-pseudo">self</span>.<span class="name">generate</span>(<span class="st st-sg">&#39;</span><span class="st st-int">%(page)s</span><span class="st st-sg">&#39;</span>, <span class="st st-sg">&#39;</span><span class="st">[</span><span class="st st-int">%(page)s</span><span class="st">]</span><span class="st st-sg">&#39;</span>, <span class="st st-sg">&#39;</span><span class="st">, </span><span class="st st-sg">&#39;</span>, <span class="st st-sg">&#39;</span><span class="st"> ... </span><span class="st st-sg">&#39;</span>, <span class="nb nb-int">3</span>)
+
+
+
+<span class="kw">class </span><span class="cls">LazyPagination</span>(<span class="bn">object</span>):
+ <span class="st st-db">&quot;&quot;&quot;</span><span class="st">
+ A lazy pagination controller. Doesn&#39;t require a id list like
+ the ``Pagination`` controller.
+
+ The ``LazyPagination`` is a cheep to calculate pagination based
+ on the current page, the total amount of pages and the information
+ about what a link looks like and how many items you have per page.
+ </span><span class="st st-db">&quot;&quot;&quot;</span>
+
+ <span class="kw">def </span><span class="fun">__init__</span>(<span class="bn bn-pseudo">self</span>, <span class="name">req</span>, <span class="name">page</span>, <span class="name">per_page</span>, <span class="name">pages</span>, <span class="name">link</span>):
+ <span class="bn bn-pseudo">self</span>.<span class="name">req</span> <span class="op">=</span> <span class="name">req</span>
+
+ <span class="bn bn-pseudo">self</span>.<span class="name">page</span> <span class="op">=</span> <span class="name">page</span>
+ <span class="bn bn-pseudo">self</span>.<span class="name">per_page</span> <span class="op">=</span> <span class="name">per_page</span>
+
+ <span class="bn bn-pseudo">self</span>.<span class="name">pages</span> <span class="op">=</span> <span class="name">pages</span>
+ <span class="bn bn-pseudo">self</span>.<span class="name">link</span> <span class="op">=</span> <span class="name">link</span>
+
+ <span class="kw">def </span><span class="fun">generate</span>(<span class="bn bn-pseudo">self</span>, <span class="name">normal</span>, <span class="name">active</span>, <span class="name">commata</span>, <span class="name">ellipsis</span>, <span class="name">threshold</span>):
+ <span class="st st-db">&quot;&quot;&quot;</span><span class="st">
+
+ Generate a Pagination of the data defined in the constructor.
+
+ :param normal: inserted string when the page isn&#39;t active
+ :param active: inserted string when the page is active
+ :param commata: inserted between links
+ :param ellipsis: inserted as space indicator for skiped links
+ :param threshold: number of links around start/current page and
+ end before they will be replaced by an ellipsis
+
+ ``normal`` and ``active`` can contain out of the following formatting
+ chars:
+
+ ====================== ==================================
+ ``</span><span class="st st-int">%(page)s</span><span class="st">`` number of the current page
+ ``</span><span class="st st-int">%(url)s</span><span class="st">`` absolute url of the current page
+ ====================== ==================================
+
+ Example::
+
+ p.generate(&#39;&lt;a href=&quot;</span><span class="st st-int">%(url)s</span><span class="st">&quot;&gt;</span><span class="st st-int">%(page)s</span><span class="st">&lt;/a&gt;&#39;,
+ &#39;&lt;strong&gt;</span><span class="st st-int">%(page)s</span><span class="st">&lt;/strong&gt;&#39;,
+ &#39;, &#39;,
+ &#39; ... &#39;,
+ 3)
+ </span><span class="st st-db">&quot;&quot;&quot;</span>
+
+ <span class="name">was_space</span> <span class="op">=</span> <span class="bn bn-pseudo">False</span>
+ <span class="name">result</span> <span class="op">=</span> []
+ <span class="kw">for</span> <span class="name">idx</span> <span class="op op-word">in</span> <span class="bn">range</span>(<span class="nb nb-int">1</span>, <span class="bn bn-pseudo">self</span>.<span class="name">pages</span> <span class="op">+</span> <span class="nb nb-int">1</span>):
+ <span class="kw">if</span> <span class="name">idx</span> <span class="op">&lt;=</span> <span class="name">threshold</span> <span class="op op-word">or</span> <span class="name">idx</span> <span class="op">&gt;</span> <span class="bn bn-pseudo">self</span>.<span class="name">pages</span> <span class="op">-</span> <span class="name">threshold</span> <span class="op op-word">or</span>\
+ <span class="bn">abs</span>(<span class="bn bn-pseudo">self</span>.<span class="name">page</span> <span class="op">-</span> <span class="name">idx</span>) <span class="op">&lt;</span> <span class="name">math</span>.<span class="name">ceil</span>(<span class="name">threshold</span> <span class="op">/</span> <span class="nb nb-flt">2.0</span>):
+ <span class="kw">if</span> <span class="name">result</span> <span class="op op-word">and</span> <span class="name">result</span>[<span class="op">-</span><span class="nb nb-int">1</span>] <span class="op">!=</span> <span class="name">ellipsis</span>:
+ <span class="name">result</span>.<span class="name">append</span>(<span class="name">commata</span>)
+ <span class="name">was_space</span> <span class="op">=</span> <span class="bn bn-pseudo">False</span>
+
+ <span class="kw">if</span> <span class="name">idx</span> <span class="op">==</span> <span class="bn bn-pseudo">self</span>.<span class="name">page</span>:
+ <span class="name">schema</span> <span class="op">=</span> <span class="name">active</span>
+
+ <span class="kw">else</span>:
+ <span class="name">schema</span> <span class="op">=</span> <span class="name">normal</span>
+ <span class="name">url</span> <span class="op">=</span> <span class="bn bn-pseudo">self</span>.<span class="name">link</span>(<span class="name">idx</span>)
+ <span class="name">result</span>.<span class="name">append</span>(<span class="name">schema</span> <span class="op">%</span> {
+ <span class="st st-sg">&#39;</span><span class="st">page</span><span class="st st-sg">&#39;</span>: <span class="name">idx</span>,
+ <span class="st st-sg">&#39;</span><span class="st">url</span><span class="st st-sg">&#39;</span>: <span class="bn bn-pseudo">self</span>.<span class="name">req</span>.<span class="name">ctx</span>.<span class="name">make_url</span>(<span class="name">url</span>),
+ })
+ <span class="kw">else</span>:
+ <span class="kw">if</span> <span class="op op-word">not</span> <span class="name">was_space</span>:
+ <span class="name">was_space</span> <span class="op">=</span> <span class="bn bn-pseudo">True</span>
+
+ <span class="name">result</span>.<span class="name">append</span>(<span class="name">ellipsis</span>)
+ <span class="kw">return</span> <span class="name">u</span><span class="st st-sg">&#39;&#39;</span>.<span class="name">join</span>(<span class="name">result</span>)
+
+ <span class="kw">def </span><span class="fun">__str__</span>(<span class="bn bn-pseudo">self</span>):
+ <span class="kw">return</span> <span class="bn bn-pseudo">self</span>.<span class="name">generate</span>(<span class="st st-sg">&#39;</span><span class="st st-int">%(page)s</span><span class="st st-sg">&#39;</span>, <span class="st st-sg">&#39;</span><span class="st">[</span><span class="st st-int">%(page)s</span><span class="st">]</span><span class="st st-sg">&#39;</span>, <span class="st st-sg">&#39;</span><span class="st">, </span><span class="st st-sg">&#39;</span>, <span class="st st-sg">&#39;</span><span class="st"> ... </span><span class="st st-sg">&#39;</span>, <span class="nb nb-int">3</span>)
+
+
+
+<span class="kw">class </span><span class="cls">PaginationTag</span>(<span class="name">Node</span>):
+ <span class="st st-db">&quot;&quot;&quot;</span><span class="st">
+ Generates a pagination. requires a Pagination object at first
+ argument::
+
+ {% paginate forum.pagination
+ &#39;&lt;a href=&quot;</span><span class="st st-int">%(link)s</span><span class="st">&quot;&gt;</span><span class="st st-int">%(page)s</span><span class="st">&lt;/a&gt;&#39;,
+ &#39;&lt;strong&gt;</span><span class="st st-int">%(page)s</span><span class="st">&lt;/strong&gt;&#39;,
+ &#39;, &#39;,
+ &#39;...&#39;
+
+ %}
+ </span><span class="st st-db">&quot;&quot;&quot;</span>
+ <span class="name">rules</span> <span class="op">=</span> {
+ <span class="st st-sg">&#39;</span><span class="st">default</span><span class="st st-sg">&#39;</span>: [<span class="name">KeywordNode</span>(<span class="st st-sg">&#39;</span><span class="st">paginate</span><span class="st st-sg">&#39;</span>), <span class="name">VariableNode</span>(),
+ <span class="name">CollectionNode</span>()]
+ }
+ <span class="kw">def </span><span class="fun">__init__</span>(<span class="bn bn-pseudo">self</span>, <span class="name">parser</span>, <span class="name">matched_tag</span>, <span class="name">handler_args</span>, <span class="name">stack</span>):
+ <span class="bn bn-pseudo">self</span>.<span class="name">_variable</span> <span class="op">=</span> <span class="name">handler_args</span>[<span class="nb nb-int">1</span>]
+ <span class="bn bn-pseudo">self</span>.<span class="name">_arguments</span> <span class="op">=</span> <span class="name">handler_args</span>[<span class="nb nb-int">2</span>]
+ <span class="name">Node</span>.<span class="name">__init__</span>(<span class="bn bn-pseudo">self</span>)
+
+ <span class="kw">def </span><span class="fun">render</span>(<span class="bn bn-pseudo">self</span>, <span class="name">context</span>):
+ <span class="name">pagination_controller</span> <span class="op">=</span> <span class="bn bn-pseudo">self</span>.<span class="name">_variable</span>.<span class="name">resolve</span>(<span class="name">context</span>)
+ <span class="kw">if</span> <span class="bn">len</span>(<span class="bn bn-pseudo">self</span>.<span class="name">_arguments</span>) <span class="op">&lt;</span> <span class="nb nb-int">4</span>:
+ <span class="kw">raise</span> <span class="name">jinja</span>.<span class="name">exceptions</span>.<span class="name">TemplateRuntimeError</span>(<span class="st st-sg">&#39;</span><span class="st">invalid argument count </span><span class="st st-sg">&#39;</span>
+
+ <span class="st st-sg">&#39;</span><span class="st">for {% paginate %}</span><span class="st st-sg">&#39;</span>)
+ <span class="name">link_scheme</span> <span class="op">=</span> <span class="bn bn-pseudo">self</span>.<span class="name">_arguments</span>[<span class="nb nb-int">0</span>].<span class="name">resolve</span>(<span class="name">context</span>)
+ <span class="name">active_scheme</span> <span class="op">=</span> <span class="bn bn-pseudo">self</span>.<span class="name">_arguments</span>[<span class="nb nb-int">1</span>].<span class="name">resolve</span>(<span class="name">context</span>)
+ <span class="name">commata</span> <span class="op">=</span> <span class="bn bn-pseudo">self</span>.<span class="name">_arguments</span>[<span class="nb nb-int">2</span>].<span class="name">resolve</span>(<span class="name">context</span>)
+ <span class="name">ellipsis</span> <span class="op">=</span> <span class="bn bn-pseudo">self</span>.<span class="name">_arguments</span>[<span class="nb nb-int">3</span>].<span class="name">resolve</span>(<span class="name">context</span>)
+ <span class="kw">if</span> <span class="bn">len</span>(<span class="bn bn-pseudo">self</span>.<span class="name">_arguments</span>) <span class="op">==</span> <span class="nb nb-int">5</span>:
+ <span class="name">threshold</span> <span class="op">=</span> <span class="bn bn-pseudo">self</span>.<span class="name">_arguments</span>[<span class="nb nb-int">4</span>].<span class="name">resolve</span>(<span class="name">context</span>)
+ <span class="kw">else</span>:
+ <span class="name">threshold</span> <span class="op">=</span> <span class="nb nb-int">3</span>
+
+ <span class="kw">return</span> <span class="name">pagination_controller</span>.<span class="name">generate</span>(<span class="name">link_scheme</span>, <span class="name">active_scheme</span>,
+ <span class="name">commata</span>, <span class="name">ellipsis</span>, <span class="name">threshold</span>)
+
+ <span class="kw">def </span><span class="fun">__repr__</span>(<span class="bn bn-pseudo">self</span>):
+ <span class="kw">return</span> <span class="st st-sg">&#39;</span><span class="st">&lt;</span><span class="st st-int">%s</span><span class="st">&gt;</span><span class="st st-sg">&#39;</span> <span class="op">%</span> <span class="bn bn-pseudo">self</span>.<span class="name">__class__</span>.<span class="name">__name__</span>
+
+<span class="cm"># -*- coding: utf-8 -*-</span>
+<span class="st st-db">&quot;&quot;&quot;</span><span class="st">
+ pocoo.pkg.core.textfmt
+ ~~~~~~~~~~~~~~~~~~~~~~
+
+ Pocoo text processing interfaces.
+
+ :copyright: 2006 by Georg Brandl, Armin Ronacher.
+ :license: GNU GPL, see LICENSE for more details.
+</span><span class="st st-db">&quot;&quot;&quot;</span>
+<span class="kw">import </span><span class="cls">re</span>
+<span class="kw">from </span><span class="cls">xml.sax.saxutils</span><span class="kw"> import</span> <span class="name">quoteattr</span>
+
+<span class="kw">from </span><span class="cls">pocoo.http</span><span class="kw"> import</span> <span class="name">Request</span>
+<span class="kw">from </span><span class="cls">pocoo.utils.html</span><span class="kw"> import</span> <span class="name">nl2br</span>, <span class="name">escape_html</span>, <span class="name">unescape_html</span>, <span class="name">urlize</span>
+
+<span class="kw">from </span><span class="cls">pocoo</span><span class="kw"> import</span> <span class="name">Component</span>
+<span class="kw">from </span><span class="cls">pocoo.pkg.core.smilies</span><span class="kw"> import</span> <span class="name">replace_smilies</span>
+<span class="kw">from </span><span class="cls">pocoo.utils.activecache</span><span class="kw"> import</span> <span class="name">Node</span>, <span class="name">load_cache</span>, <span class="name">generate_cache</span>
+
+<span class="kw">from </span><span class="cls">pocoo.utils</span><span class="kw"> import</span> <span class="name">json</span>
+
+
+<span class="name">frozen_translation_re</span> <span class="op">=</span> <span class="name">re</span>.<span class="name">compile</span>(<span class="st st-sg">r&#39;</span><span class="st">&lt;trans(?: value=&quot;(.*?)&quot;)?&gt;(.*?)&lt;/trans&gt;</span><span class="st st-sg">&#39;</span>)
+
+
+
+<span class="kw">class </span><span class="cls">MarkupFormat</span>(<span class="name">Component</span>):
+ <span class="st st-db">&quot;&quot;&quot;</span><span class="st">
+ A text markup format, such as BBCode or HTML.
+ </span><span class="st st-db">&quot;&quot;&quot;</span>
+
+ <span class="cm">#: The relative path to the javascript file for the editor.</span>
+ <span class="name">editor_javascript</span> <span class="op">=</span> <span class="st st-sg">&#39;</span><span class="st">!cobalt/core/PocooLib/BaseEditor.js</span><span class="st st-sg">&#39;</span>
+
+ <span class="cm">#: Name for this format. must be lowercase</span>
+ <span class="deco">@property</span>
+ <span class="kw">def </span><span class="fun">name</span>(<span class="bn bn-pseudo">self</span>):
+ <span class="kw">return</span> <span class="bn bn-pseudo">self</span>.<span class="name">__class__</span>.<span class="name">__name__</span>.<span class="name">lower</span>()
+
+ <span class="kw">def </span><span class="fun">get_title</span>(<span class="bn bn-pseudo">self</span>, <span class="name">req</span>):
+ <span class="st st-db">&quot;&quot;&quot;</span><span class="st">
+
+ Title of this formatter.
+ </span><span class="st st-db">&quot;&quot;&quot;</span>
+ <span class="kw">return</span> <span class="bn bn-pseudo">self</span>.<span class="name">name</span>.<span class="name">title</span>()
+
+ <span class="kw">def </span><span class="fun">parse</span>(<span class="bn bn-pseudo">self</span>, <span class="name">text</span>, <span class="name">signature</span>):
+ <span class="st st-db">&quot;&quot;&quot;</span><span class="st">
+
+ This method has to return either an activecache Node or an
+ string/Unicode object.
+
+ :param signature: If this is ``True`` the pocoo wants to parse
+ a signature. For BBCode there could be special
+ rules like allowed and disallowed tags etc.
+ </span><span class="st st-db">&quot;&quot;&quot;</span>
+
+ <span class="kw">def </span><span class="fun">render_callback</span>(<span class="bn bn-pseudo">self</span>, <span class="name">req</span>, <span class="name">callback</span>, <span class="name">data</span>):
+ <span class="st st-db">&quot;&quot;&quot;</span><span class="st">
+
+ If the ``parse`` method returns an activecache `CallbackNode`
+ this method will be called for the define callback.
+ </span><span class="st st-db">&quot;&quot;&quot;</span>
+ <span class="kw">return</span> <span class="name">u</span><span class="st st-sg">&#39;&#39;</span>
+
+ <span class="kw">def </span><span class="fun">quote_text</span>(<span class="bn bn-pseudo">self</span>, <span class="name">req</span>, <span class="name">text</span>, <span class="name">username</span><span class="op">=</span><span class="bn bn-pseudo">None</span>):
+ <span class="st st-db">&quot;&quot;&quot;</span><span class="st">
+
+ This has to quote a given text. For example a BBCode
+ Markup Formatter should wrap the text in [quote].
+ </span><span class="st st-db">&quot;&quot;&quot;</span>
+ <span class="kw">return</span> <span class="name">text</span>
+
+ <span class="kw">def </span><span class="fun">get_editor_options</span>(<span class="bn bn-pseudo">self</span>, <span class="name">req</span>, <span class="name">signature</span>):
+ <span class="st st-db">&quot;&quot;&quot;</span><span class="st">
+
+ This method has to return a dict of JSON
+ serializable values which get passed to the
+ function ``initEditor()`` which is defined
+ in the editor javascript file.
+
+ If `signature` is ``True`` pocoo requested an editor for the
+ signature and wants just the editor definitions which are
+ relevant for an working signature editor. See the ``BBCode``
+ text formatter for an example.
+ </span><span class="st st-db">&quot;&quot;&quot;</span>
+ <span class="kw">return</span> {}
+
+
+<span class="kw">class </span><span class="cls">PlainText</span>(<span class="name">MarkupFormat</span>):
+ <span class="st st-db">&quot;&quot;&quot;</span><span class="st">
+ This parser just breaks lines and creates links.
+ </span><span class="st st-db">&quot;&quot;&quot;</span>
+ <span class="name">name</span> <span class="op">=</span> <span class="st st-sg">&#39;</span><span class="st">plain</span><span class="st st-sg">&#39;</span>
+
+ <span class="kw">def </span><span class="fun">parse</span>(<span class="bn bn-pseudo">self</span>, <span class="name">text</span>, <span class="name">signature</span>):
+ <span class="name">text</span> <span class="op">=</span> <span class="name">escape_html</span>(<span class="name">text</span>)
+ <span class="name">text</span> <span class="op">=</span> <span class="name">nl2br</span>(<span class="name">text</span>)
+ <span class="name">text</span> <span class="op">=</span> <span class="name">replace_smilies</span>(<span class="bn bn-pseudo">self</span>.<span class="name">ctx</span>, <span class="name">text</span>)
+ <span class="kw">return</span> <span class="name">urlize</span>(<span class="name">text</span>, <span class="nb nb-int">50</span>, <span class="bn bn-pseudo">True</span>)
+
+ <span class="kw">def </span><span class="fun">quote_text</span>(<span class="bn bn-pseudo">self</span>, <span class="name">req</span>, <span class="name">text</span>, <span class="name">username</span><span class="op">=</span><span class="bn bn-pseudo">None</span>):
+ <span class="name">lines</span> <span class="op">=</span> [<span class="name">u</span><span class="st st-sg">&#39;</span><span class="st">&gt; </span><span class="st st-int">%s</span><span class="st st-sg">&#39;</span> <span class="op">%</span> <span class="name">line</span> <span class="kw">for</span> <span class="name">line</span> <span class="op op-word">in</span> <span class="name">text</span>.<span class="name">splitlines</span>()]
+ <span class="kw">if</span> <span class="name">username</span> <span class="op op-word">is</span> <span class="op op-word">not</span> <span class="bn bn-pseudo">None</span>:
+ <span class="name">_</span> <span class="op">=</span> <span class="name">req</span>.<span class="name">gettext</span>
+
+ <span class="name">lines</span>.<span class="name">insert</span>(<span class="nb nb-int">0</span>, (<span class="name">_</span>(<span class="st st-sg">&#39;</span><span class="st st-int">%s</span><span class="st"> wrote</span><span class="st st-sg">&#39;</span>) <span class="op">+</span> <span class="name">u</span><span class="st st-sg">&#39;</span><span class="st">:</span><span class="st st-sg">&#39;</span>) <span class="op">%</span> <span class="name">username</span>)
+ <span class="kw">return</span> <span class="name">u</span><span class="st st-sg">&#39;</span><span class="st st-esc">\n</span><span class="st st-sg">&#39;</span>.<span class="name">join</span>(<span class="name">lines</span>)
+
+
+
+<span class="kw">def </span><span class="fun">parse</span>(<span class="name">ctx</span>, <span class="name">text</span>, <span class="name">signature</span><span class="op">=</span><span class="bn bn-pseudo">False</span>, <span class="name">syntax_parser</span><span class="op">=</span><span class="bn bn-pseudo">None</span>):
+ <span class="st st-db">&quot;&quot;&quot;</span><span class="st">
+ Parses an text and returns a marshalled object. You then can
+ put such an string into the database or push it to the
+ render function.
+ </span><span class="st st-db">&quot;&quot;&quot;</span>
+
+ <span class="kw">if</span> <span class="op op-word">not</span> <span class="bn">isinstance</span>(<span class="name">text</span>, <span class="bn">unicode</span>):
+ <span class="name">text</span> <span class="op">=</span> <span class="bn">unicode</span>(<span class="name">text</span>)
+ <span class="kw">if</span> <span class="name">syntax_parser</span> <span class="op op-word">is</span> <span class="bn bn-pseudo">None</span>:
+ <span class="name">syntax_parser</span> <span class="op">=</span> <span class="name">ctx</span>.<span class="name">cfg</span>.<span class="name">get</span>(<span class="st st-sg">&#39;</span><span class="st">board</span><span class="st st-sg">&#39;</span>, <span class="st st-sg">&#39;</span><span class="st">syntax_parser</span><span class="st st-sg">&#39;</span>, <span class="st st-sg">&#39;</span><span class="st">plain</span><span class="st st-sg">&#39;</span>)
+ <span class="name">syntax_parser</span> <span class="op">=</span> <span class="name">syntax_parser</span>.<span class="name">lower</span>()
+ <span class="kw">for</span> <span class="name">comp</span> <span class="op op-word">in</span> <span class="name">ctx</span>.<span class="name">get_components</span>(<span class="name">MarkupFormat</span>):
+ <span class="kw">if</span> <span class="name">comp</span>.<span class="name">name</span> <span class="op">==</span> <span class="name">syntax_parser</span>:
+ <span class="name">node</span> <span class="op">=</span> <span class="name">comp</span>.<span class="name">parse</span>(<span class="name">text</span>, <span class="name">signature</span>)
+ <span class="kw">if</span> <span class="bn">isinstance</span>(<span class="name">node</span>, <span class="bn">basestring</span>):
+ <span class="name">node</span> <span class="op">=</span> <span class="name">Node</span>(<span class="name">node</span>)
+ <span class="kw">return</span> <span class="name">generate_cache</span>(<span class="name">node</span>, <span class="name">syntax_parser</span>)
+ <span class="kw">else</span>:
+ <span class="kw">raise</span> <span class="exc">ValueError</span>(<span class="st st-sg">&#39;</span><span class="st">Parser &quot;</span><span class="st st-int">%s</span><span class="st">&quot; not found</span><span class="st st-sg">&#39;</span> <span class="op">%</span> <span class="name">syntax_parser</span>)
+
+
+
+<span class="kw">def </span><span class="fun">render</span>(<span class="name">req</span>, <span class="name">data</span>):
+ <span class="st st-db">&quot;&quot;&quot;</span><span class="st">
+ Renders a parsed data
+ </span><span class="st st-db">&quot;&quot;&quot;</span>
+ <span class="name">node</span>, <span class="name">syntax_parser</span> <span class="op">=</span> <span class="name">load_cache</span>(<span class="name">data</span>)
+ <span class="kw">for</span> <span class="name">comp</span> <span class="op op-word">in</span> <span class="name">req</span>.<span class="name">ctx</span>.<span class="name">get_components</span>(<span class="name">MarkupFormat</span>):
+ <span class="kw">if</span> <span class="name">comp</span>.<span class="name">name</span> <span class="op">==</span> <span class="name">syntax_parser</span>:
+ <span class="kw">break</span>
+
+ <span class="kw">else</span>:
+ <span class="kw">raise</span> <span class="exc">ValueError</span>(<span class="st st-sg">&#39;</span><span class="st">syntax parser </span><span class="st st-int">%r</span><span class="st"> not found</span><span class="st st-sg">&#39;</span> <span class="op">%</span> <span class="name">syntax_parser</span>)
+ <span class="kw">return</span> <span class="name">node</span>.<span class="name">render</span>(<span class="name">req</span>, <span class="name">comp</span>)
+
+
+
+<span class="kw">def </span><span class="fun">parse_and_render</span>(<span class="name">req</span>, <span class="name">text</span>, <span class="name">signature</span><span class="op">=</span><span class="bn bn-pseudo">False</span>, <span class="name">syntax_parser</span><span class="op">=</span><span class="bn bn-pseudo">None</span>):
+ <span class="st st-db">&quot;&quot;&quot;</span><span class="st">
+ Parses and renders a text.
+ </span><span class="st st-db">&quot;&quot;&quot;</span>
+
+ <span class="kw">if</span> <span class="op op-word">not</span> <span class="bn">isinstance</span>(<span class="name">text</span>, <span class="bn">unicode</span>):
+ <span class="name">text</span> <span class="op">=</span> <span class="bn">unicode</span>(<span class="name">text</span>)
+ <span class="kw">if</span> <span class="name">syntax_parser</span> <span class="op op-word">is</span> <span class="bn bn-pseudo">None</span>:
+ <span class="name">syntax_parser</span> <span class="op">=</span> <span class="name">req</span>.<span class="name">ctx</span>.<span class="name">cfg</span>.<span class="name">get</span>(<span class="st st-sg">&#39;</span><span class="st">board</span><span class="st st-sg">&#39;</span>, <span class="st st-sg">&#39;</span><span class="st">syntax_parser</span><span class="st st-sg">&#39;</span>, <span class="st st-sg">&#39;</span><span class="st">plain</span><span class="st st-sg">&#39;</span>)
+ <span class="name">syntax_parser</span> <span class="op">=</span> <span class="name">syntax_parser</span>.<span class="name">lower</span>()
+ <span class="kw">for</span> <span class="name">comp</span> <span class="op op-word">in</span> <span class="name">req</span>.<span class="name">ctx</span>.<span class="name">get_components</span>(<span class="name">MarkupFormat</span>):
+ <span class="kw">if</span> <span class="name">comp</span>.<span class="name">name</span> <span class="op">==</span> <span class="name">syntax_parser</span>:
+ <span class="name">node</span> <span class="op">=</span> <span class="name">comp</span>.<span class="name">parse</span>(<span class="name">text</span>, <span class="name">signature</span>)
+ <span class="kw">if</span> <span class="bn">isinstance</span>(<span class="name">node</span>, <span class="bn">basestring</span>):
+ <span class="name">node</span> <span class="op">=</span> <span class="name">Node</span>(<span class="name">node</span>)
+ <span class="kw">return</span> <span class="name">node</span>.<span class="name">render</span>(<span class="name">req</span>, <span class="name">comp</span>)
+ <span class="kw">else</span>:
+ <span class="kw">raise</span> <span class="exc">ValueError</span>(<span class="st st-sg">&#39;</span><span class="st">Parser &quot;</span><span class="st st-int">%s</span><span class="st">&quot; not found</span><span class="st st-sg">&#39;</span> <span class="op">%</span> <span class="name">syntax_parser</span>)
+
+
+
+<span class="kw">def </span><span class="fun">get_editor</span>(<span class="name">req</span>, <span class="name">signature</span><span class="op">=</span><span class="bn bn-pseudo">False</span>, <span class="name">syntax_parser</span><span class="op">=</span><span class="bn bn-pseudo">None</span>):
+ <span class="st st-db">&quot;&quot;&quot;</span><span class="st">
+ Return a tuple in the form (javascript_file, options)
+ for the template. Both of them are strings which can be
+ used directly in the template::
+
+ &lt;script type=&quot;text/javascript&quot; src=&quot;{{ editorjs }}&quot;&gt;&lt;/script&gt;
+
+ &lt;script type=&quot;text/javascript&quot;&gt;
+ initEditor(&#39;id_of_textarea&#39;, &#39;id_of_toolbar&#39;, {{ options }});
+ &lt;/script&gt;
+
+ If you set `signature` to ``True`` it will just display the
+ buttons which are relevant for the signature editor.
+ </span><span class="st st-db">&quot;&quot;&quot;</span>
+ <span class="kw">if</span> <span class="name">syntax_parser</span> <span class="op op-word">is</span> <span class="bn bn-pseudo">None</span>:
+ <span class="name">syntax_parser</span> <span class="op">=</span> <span class="name">req</span>.<span class="name">ctx</span>.<span class="name">cfg</span>.<span class="name">get</span>(<span class="st st-sg">&#39;</span><span class="st">board</span><span class="st st-sg">&#39;</span>, <span class="st st-sg">&#39;</span><span class="st">syntax_parser</span><span class="st st-sg">&#39;</span>, <span class="st st-sg">&#39;</span><span class="st">plain</span><span class="st st-sg">&#39;</span>).<span class="name">lower</span>()
+ <span class="kw">for</span> <span class="name">comp</span> <span class="op op-word">in</span> <span class="name">req</span>.<span class="name">ctx</span>.<span class="name">get_components</span>(<span class="name">MarkupFormat</span>):
+ <span class="kw">if</span> <span class="name">comp</span>.<span class="name">name</span> <span class="op">==</span> <span class="name">syntax_parser</span>:
+ <span class="name">js</span> <span class="op">=</span> <span class="name">req</span>.<span class="name">ctx</span>.<span class="name">make_url</span>(<span class="name">comp</span>.<span class="name">editor_javascript</span>)
+ <span class="name">options</span> <span class="op">=</span> <span class="name">json</span>.<span class="name">dumps</span>(<span class="name">comp</span>.<span class="name">get_editor_options</span>(<span class="name">req</span>, <span class="name">signature</span>))
+ <span class="kw">return</span> <span class="name">js</span>, <span class="name">options</span>
+
+ <span class="kw">raise</span> <span class="exc">ValueError</span>(<span class="st st-sg">&#39;</span><span class="st">Parser &quot;</span><span class="st st-int">%s</span><span class="st">&quot; not found</span><span class="st st-sg">&#39;</span> <span class="op">%</span> <span class="name">syntax_parser</span>)
+
+
+<span class="kw">def </span><span class="fun">quote_text</span>(<span class="name">req</span>, <span class="name">text</span>, <span class="name">username</span><span class="op">=</span><span class="bn bn-pseudo">None</span>, <span class="name">syntax_parser</span><span class="op">=</span><span class="bn bn-pseudo">None</span>):
+ <span class="st st-db">&quot;&quot;&quot;</span><span class="st">Quote ``text`` with the style defined in the markup parser.</span><span class="st st-db">&quot;&quot;&quot;</span>
+
+ <span class="kw">if</span> <span class="name">syntax_parser</span> <span class="op op-word">is</span> <span class="bn bn-pseudo">None</span>:
+ <span class="name">syntax_parser</span> <span class="op">=</span> <span class="name">req</span>.<span class="name">ctx</span>.<span class="name">cfg</span>.<span class="name">get</span>(<span class="st st-sg">&#39;</span><span class="st">board</span><span class="st st-sg">&#39;</span>, <span class="st st-sg">&#39;</span><span class="st">syntax_parser</span><span class="st st-sg">&#39;</span>, <span class="st st-sg">&#39;</span><span class="st">plain</span><span class="st st-sg">&#39;</span>).<span class="name">lower</span>()
+ <span class="kw">for</span> <span class="name">comp</span> <span class="op op-word">in</span> <span class="name">req</span>.<span class="name">ctx</span>.<span class="name">get_components</span>(<span class="name">MarkupFormat</span>):
+ <span class="kw">if</span> <span class="name">comp</span>.<span class="name">name</span> <span class="op">==</span> <span class="name">syntax_parser</span>:
+ <span class="kw">return</span> <span class="name">comp</span>.<span class="name">quote_text</span>(<span class="name">req</span>, <span class="name">text</span>, <span class="name">username</span>)
+ <span class="kw">raise</span> <span class="exc">ValueError</span>(<span class="st st-sg">&#39;</span><span class="st">Parser &quot;</span><span class="st st-int">%s</span><span class="st">&quot; not found</span><span class="st st-sg">&#39;</span> <span class="op">%</span> <span class="name">syntax_parser</span>)
+
+
+
+<span class="kw">def </span><span class="fun">get_markup_formatters</span>(<span class="name">req</span>):
+ <span class="st st-db">&quot;&quot;&quot;</span><span class="st">
+ Return a list of known formatters
+ </span><span class="st st-db">&quot;&quot;&quot;</span>
+ <span class="name">result</span> <span class="op">=</span> []
+ <span class="kw">for</span> <span class="name">comp</span> <span class="op op-word">in</span> <span class="name">req</span>.<span class="name">ctx</span>.<span class="name">get_components</span>(<span class="name">MarkupFormat</span>):
+ <span class="name">result</span>.<span class="name">append</span>((<span class="name">comp</span>.<span class="name">name</span>, <span class="name">comp</span>.<span class="name">get_title</span>(<span class="name">req</span>)))
+ <span class="name">result</span>.<span class="name">sort</span>(<span class="name">key</span><span class="op">=</span><span class="kw">lambda</span> <span class="name">x</span>: <span class="name">x</span>[<span class="nb nb-int">1</span>].<span class="name">lower</span>())
+ <span class="kw">return</span> <span class="name">result</span>
+
+<span class="cm"># -*- coding: utf-8 -*-</span>
+<span class="st st-db">&quot;&quot;&quot;</span><span class="st">
+ pocoo.pkg.core.user
+ ~~~~~~~~~~~~~~~~~~~
+
+ User model and utilities.
+
+ :copyright: 2006 by Armin Ronacher.
+ :license: GNU GPL, see LICENSE for more details.
+</span><span class="st st-db">&quot;&quot;&quot;</span>
+
+<span class="kw">from </span><span class="cls">os</span><span class="kw"> import</span> <span class="name">path</span>
+<span class="kw">from </span><span class="cls">datetime</span><span class="kw"> import</span> <span class="name">datetime</span>
+
+<span class="kw">from </span><span class="cls">pocoo.db</span><span class="kw"> import</span> <span class="name">meta</span>, <span class="name">DatabaseModel</span>, <span class="name">lazy_column</span>
+<span class="kw">from </span><span class="cls">pocoo.http</span><span class="kw"> import</span> <span class="name">DirectResponse</span>, <span class="name">AccessDeniedResponse</span>
+
+<span class="kw">from </span><span class="cls">pocoo.application</span><span class="kw"> import</span> <span class="name">LinkableMixin</span>
+<span class="kw">from </span><span class="cls">pocoo.utils.text</span><span class="kw"> import</span> <span class="name">gen_password</span>, <span class="name">gen_activation_key</span>, <span class="name">gen_pwhash</span>, \
+ <span class="name">check_pwhash</span>
+
+<span class="kw">from </span><span class="cls">pocoo.utils.uri</span><span class="kw"> import</span> <span class="name">urlencode</span>
+
+<span class="kw">from </span><span class="cls">pocoo.pkg.core.db</span><span class="kw"> import</span> <span class="name">users</span>, <span class="name">groups</span>, <span class="name">posts</span>, <span class="name">group_members</span>
+
+
+<span class="kw">def </span><span class="fun">get_all_usernames</span>(<span class="name">ctx</span>):
+ <span class="st st-db">&quot;&quot;&quot;</span><span class="st">Return a list of all usernames.</span><span class="st st-db">&quot;&quot;&quot;</span>
+ <span class="name">result</span> <span class="op">=</span> <span class="name">ctx</span>.<span class="name">engine</span>.<span class="name">execute</span>(<span class="name">meta</span>.<span class="name">select</span>([<span class="name">users</span>.<span class="name">c</span>.<span class="name">username</span>]))
+ <span class="kw">return</span> [<span class="name">row</span>[<span class="nb nb-int">0</span>] <span class="kw">for</span> <span class="name">row</span> <span class="op op-word">in</span> <span class="name">result</span>]
+
+
+
+<span class="kw">def </span><span class="fun">get_id_username_mapping</span>(<span class="name">ctx</span>):
+ <span class="st st-db">&quot;&quot;&quot;</span><span class="st">Return a ``user_id`` -&gt; ``username`` mapping.</span><span class="st st-db">&quot;&quot;&quot;</span>
+ <span class="name">result</span> <span class="op">=</span> <span class="name">ctx</span>.<span class="name">engine</span>.<span class="name">execute</span>(<span class="name">meta</span>.<span class="name">select</span>([<span class="name">users</span>.<span class="name">c</span>.<span class="name">user_id</span>, <span class="name">users</span>.<span class="name">c</span>.<span class="name">username</span>]))
+ <span class="kw">return</span> <span class="bn">dict</span>(<span class="bn">tuple</span>(<span class="name">row</span>) <span class="kw">for</span> <span class="name">row</span> <span class="op op-word">in</span> <span class="name">result</span>)
+
+
+
+<span class="kw">def </span><span class="fun">get_user_list</span>(<span class="name">ctx</span>, <span class="name">order_by</span><span class="op">=</span><span class="name">users</span>.<span class="name">c</span>.<span class="name">username</span>, <span class="name">order</span><span class="op">=</span><span class="st st-sg">&#39;</span><span class="st">asc</span><span class="st st-sg">&#39;</span>, <span class="name">letter</span><span class="op">=</span><span class="bn bn-pseudo">None</span>,
+ <span class="name">hide_internal</span><span class="op">=</span><span class="bn bn-pseudo">False</span>):
+ <span class="st st-db">&quot;&quot;&quot;</span><span class="st">Return a list of all users with public details.</span><span class="st st-db">&quot;&quot;&quot;</span>
+
+ <span class="cm">#XXX: sorting with databases sucks :-/ if sorted by username, resort</span>
+ <span class="cm"># with python, and btw, this is ugly</span>
+ <span class="name">u</span> <span class="op">=</span> <span class="name">users</span>.<span class="name">c</span>
+ <span class="name">q</span> <span class="op">=</span> []
+ <span class="kw">if</span> <span class="name">letter</span>:
+ <span class="name">q</span>.<span class="name">append</span>(<span class="name">u</span>.<span class="name">username</span>.<span class="name">startswith</span>(<span class="name">letter</span>))
+ <span class="kw">if</span> <span class="name">hide_internal</span>:
+ <span class="name">q</span>.<span class="name">append</span>((<span class="name">u</span>.<span class="name">username</span> <span class="op">!=</span> <span class="st st-sg">&#39;</span><span class="st">default</span><span class="st st-sg">&#39;</span>) <span class="op">&amp;</span> (<span class="name">u</span>.<span class="name">username</span> <span class="op">!=</span> <span class="st st-sg">&#39;</span><span class="st">anonymous</span><span class="st st-sg">&#39;</span>))
+ <span class="kw">try</span>:
+ <span class="name">order</span> <span class="op">=</span> {
+ <span class="st st-sg">&#39;</span><span class="st">desc</span><span class="st st-sg">&#39;</span>: <span class="name">meta</span>.<span class="name">desc</span>,
+ <span class="st st-sg">&#39;</span><span class="st">asc</span><span class="st st-sg">&#39;</span>: <span class="name">meta</span>.<span class="name">asc</span>
+
+ }[<span class="name">order</span>]
+ <span class="kw">except</span> <span class="exc">KeyError</span>:
+ <span class="kw">raise</span> <span class="exc">ValueError</span>(<span class="st st-sg">&#39;</span><span class="st">order must be either </span><span class="st st-esc">\&#39;</span><span class="st">asc</span><span class="st st-esc">\&#39;</span><span class="st"> or </span><span class="st st-esc">\&#39;</span><span class="st">desc</span><span class="st st-esc">\&#39;</span><span class="st st-sg">&#39;</span>)
+ <span class="name">result</span> <span class="op">=</span> <span class="name">ctx</span>.<span class="name">engine</span>.<span class="name">execute</span>(<span class="name">meta</span>.<span class="name">select</span>([<span class="name">u</span>.<span class="name">user_id</span>, <span class="name">u</span>.<span class="name">username</span>, <span class="name">u</span>.<span class="name">email</span>,
+ <span class="name">u</span>.<span class="name">profile</span>, <span class="name">u</span>.<span class="name">last_login</span>,
+ <span class="name">u</span>.<span class="name">register_date</span>, <span class="name">u</span>.<span class="name">post_count</span>],
+ <span class="name">meta</span>.<span class="name">and_</span>(<span class="op">*</span><span class="name">q</span>),
+ <span class="name">order_by</span><span class="op">=</span>[<span class="name">order</span>(<span class="name">order_by</span>)]))
+ <span class="kw">def </span><span class="fun">prepare</span>(<span class="name">row</span>):
+ <span class="name">d</span> <span class="op">=</span> <span class="bn">dict</span>(<span class="name">row</span>)
+ <span class="name">d</span>[<span class="st st-sg">&#39;</span><span class="st">url</span><span class="st st-sg">&#39;</span>] <span class="op">=</span> <span class="name">ctx</span>.<span class="name">make_url</span>(<span class="st st-sg">&#39;</span><span class="st">users</span><span class="st st-sg">&#39;</span>, <span class="name">urlencode</span>(<span class="name">row</span>[<span class="st st-sg">&#39;</span><span class="st">username</span><span class="st st-sg">&#39;</span>]))
+ <span class="kw">return</span> <span class="name">d</span>
+
+ <span class="kw">return</span> [<span class="name">prepare</span>(<span class="name">row</span>) <span class="kw">for</span> <span class="name">row</span> <span class="op op-word">in</span> <span class="name">result</span>]
+
+
+<span class="kw">def </span><span class="fun">get_all_groupnames</span>(<span class="name">ctx</span>):
+ <span class="st st-db">&quot;&quot;&quot;</span><span class="st">Return a list of groupnames.</span><span class="st st-db">&quot;&quot;&quot;</span>
+
+ <span class="name">result</span> <span class="op">=</span> <span class="name">ctx</span>.<span class="name">engine</span>.<span class="name">execute</span>(<span class="name">meta</span>.<span class="name">select</span>([<span class="name">groups</span>.<span class="name">c</span>.<span class="name">name</span>]))
+ <span class="kw">return</span> [<span class="name">row</span>[<span class="nb nb-int">0</span>] <span class="kw">for</span> <span class="name">row</span> <span class="op op-word">in</span> <span class="name">result</span>]
+
+
+
+<span class="kw">def </span><span class="fun">get_id_groupname_mapping</span>(<span class="name">ctx</span>):
+ <span class="st st-db">&quot;&quot;&quot;</span><span class="st">Return a group_id -&gt; groupname mapping.</span><span class="st st-db">&quot;&quot;&quot;</span>
+ <span class="name">result</span> <span class="op">=</span> <span class="name">ctx</span>.<span class="name">engine</span>.<span class="name">execute</span>(<span class="name">meta</span>.<span class="name">select</span>([<span class="name">groups</span>.<span class="name">c</span>.<span class="name">group_id</span>, <span class="name">groups</span>.<span class="name">c</span>.<span class="name">name</span>]))
+ <span class="kw">return</span> <span class="bn">dict</span>(<span class="bn">tuple</span>(<span class="name">row</span>) <span class="kw">for</span> <span class="name">row</span> <span class="op op-word">in</span> <span class="name">result</span>)
+
+
+
+<span class="kw">def </span><span class="fun">get_group_list</span>(<span class="name">ctx</span>, <span class="name">order_by</span><span class="op">=</span><span class="name">groups</span>.<span class="name">c</span>.<span class="name">name</span>, <span class="name">order</span><span class="op">=</span><span class="st st-sg">&#39;</span><span class="st">asc</span><span class="st st-sg">&#39;</span>, <span class="name">letter</span><span class="op">=</span><span class="bn bn-pseudo">None</span>,
+ <span class="name">show_hidden</span><span class="op">=</span><span class="bn bn-pseudo">True</span>):
+ <span class="st st-db">&quot;&quot;&quot;</span><span class="st">Return a list of all groups with public details.</span><span class="st st-db">&quot;&quot;&quot;</span>
+
+ <span class="name">g</span> <span class="op">=</span> <span class="name">groups</span>.<span class="name">c</span>
+ <span class="name">q</span> <span class="op">=</span> []
+ <span class="kw">if</span> <span class="name">letter</span>:
+ <span class="name">q</span>.<span class="name">append</span>(<span class="name">g</span>.<span class="name">name</span>.<span class="name">startswith</span>(<span class="name">letter</span>))
+ <span class="kw">if</span> <span class="op op-word">not</span> <span class="name">show_hidden</span>:
+ <span class="name">q</span>.<span class="name">append</span>(<span class="name">g</span>.<span class="name">hidden</span> <span class="op">==</span> <span class="bn bn-pseudo">True</span>)
+ <span class="kw">try</span>:
+ <span class="name">order</span> <span class="op">=</span> {
+ <span class="st st-sg">&#39;</span><span class="st">desc</span><span class="st st-sg">&#39;</span>: <span class="name">meta</span>.<span class="name">desc</span>,
+ <span class="st st-sg">&#39;</span><span class="st">asc</span><span class="st st-sg">&#39;</span>: <span class="name">meta</span>.<span class="name">asc</span>
+
+ }[<span class="name">order</span>]
+ <span class="kw">except</span> <span class="exc">KeyError</span>:
+ <span class="kw">raise</span> <span class="exc">ValueError</span>(<span class="st st-sg">&#39;</span><span class="st">order must be either </span><span class="st st-esc">\&#39;</span><span class="st">asc</span><span class="st st-esc">\&#39;</span><span class="st"> or </span><span class="st st-esc">\&#39;</span><span class="st">desc</span><span class="st st-esc">\&#39;</span><span class="st st-sg">&#39;</span>)
+ <span class="name">result</span> <span class="op">=</span> <span class="name">ctx</span>.<span class="name">engine</span>.<span class="name">execute</span>(<span class="name">meta</span>.<span class="name">select</span>([<span class="name">g</span>.<span class="name">group_id</span>, <span class="name">g</span>.<span class="name">name</span>, <span class="name">g</span>.<span class="name">public</span>,
+ <span class="name">g</span>.<span class="name">hidden</span>], <span class="name">meta</span>.<span class="name">and_</span>(<span class="op">*</span><span class="name">q</span>),
+ <span class="name">order_by</span><span class="op">=</span>[<span class="name">order</span>(<span class="name">order_by</span>)]))
+ <span class="kw">def </span><span class="fun">prepare</span>(<span class="name">row</span>):
+ <span class="name">d</span> <span class="op">=</span> <span class="bn">dict</span>(<span class="name">row</span>)
+ <span class="name">d</span>[<span class="st st-sg">&#39;</span><span class="st">url</span><span class="st st-sg">&#39;</span>] <span class="op">=</span> <span class="name">ctx</span>.<span class="name">make_url</span>(<span class="st st-sg">&#39;</span><span class="st">groups</span><span class="st st-sg">&#39;</span>, <span class="name">urlencode</span>(<span class="name">row</span>[<span class="st st-sg">&#39;</span><span class="st">name</span><span class="st st-sg">&#39;</span>]))
+ <span class="kw">return</span> <span class="name">d</span>
+
+ <span class="kw">return</span> [<span class="name">prepare</span>(<span class="name">row</span>) <span class="kw">for</span> <span class="name">row</span> <span class="op op-word">in</span> <span class="name">result</span>]
+
+
+<span class="kw">def </span><span class="fun">get_user</span>(<span class="name">req</span>, <span class="name">user_id_or_name</span>):
+ <span class="st st-db">&quot;&quot;&quot;</span><span class="st">
+
+ Return a dict of user information for the template.
+ </span><span class="st st-db">&quot;&quot;&quot;</span>
+ <span class="name">ctx</span> <span class="op">=</span> <span class="name">req</span>.<span class="name">ctx</span>
+ <span class="name">u</span> <span class="op">=</span> <span class="name">users</span>.<span class="name">c</span>
+
+ <span class="kw">if</span> <span class="bn">isinstance</span>(<span class="name">user_id_or_name</span>, (<span class="bn">int</span>, <span class="bn">long</span>)):
+ <span class="name">q</span> <span class="op">=</span> (<span class="name">u</span>.<span class="name">user_id</span> <span class="op">==</span> <span class="name">user_id_or_name</span>)
+ <span class="kw">else</span>:
+ <span class="name">q</span> <span class="op">=</span> (<span class="name">u</span>.<span class="name">username</span> <span class="op">==</span> <span class="name">user_id_or_name</span>)
+ <span class="name">result</span> <span class="op">=</span> <span class="name">ctx</span>.<span class="name">engine</span>.<span class="name">execute</span>(<span class="name">meta</span>.<span class="name">select</span>([<span class="name">u</span>.<span class="name">user_id</span>, <span class="name">u</span>.<span class="name">username</span>, <span class="name">u</span>.<span class="name">email</span>,
+ <span class="name">u</span>.<span class="name">profile</span>, <span class="name">u</span>.<span class="name">settings</span>, <span class="name">u</span>.<span class="name">last_login</span>,
+ <span class="name">u</span>.<span class="name">register_date</span>, <span class="name">u</span>.<span class="name">post_count</span>], <span class="name">q</span>))
+ <span class="name">row</span> <span class="op">=</span> <span class="name">result</span>.<span class="name">fetchone</span>()
+ <span class="kw">if</span> <span class="name">row</span> <span class="op op-word">is</span> <span class="bn bn-pseudo">None</span>:
+ <span class="cm"># XXX: error return needed</span>
+
+ <span class="kw">return</span>
+ <span class="name">user</span> <span class="op">=</span> <span class="bn">dict</span>(<span class="name">row</span>)
+ <span class="name">user</span>[<span class="st st-sg">&#39;</span><span class="st">url</span><span class="st st-sg">&#39;</span>] <span class="op">=</span> <span class="name">ctx</span>.<span class="name">make_url</span>(<span class="st st-sg">&#39;</span><span class="st">users</span><span class="st st-sg">&#39;</span>, <span class="name">urlencode</span>(<span class="name">row</span>[<span class="st st-sg">&#39;</span><span class="st">username</span><span class="st st-sg">&#39;</span>]))
+ <span class="kw">return</span> <span class="name">user</span>
+
+
+<span class="kw">def </span><span class="fun">get_user_avatar</span>(<span class="name">req</span>, <span class="name">user_id_or_name</span>):
+ <span class="st st-db">&quot;&quot;&quot;</span><span class="st">
+ Return the user avatar.
+ </span><span class="st st-db">&quot;&quot;&quot;</span>
+ <span class="kw">if</span> <span class="bn">isinstance</span>(<span class="name">user_id_or_name</span>, (<span class="bn">int</span>, <span class="bn">long</span>)):
+ <span class="name">q</span> <span class="op">=</span> (<span class="name">users</span>.<span class="name">c</span>.<span class="name">user_id</span> <span class="op">==</span> <span class="name">user_id_or_name</span>)
+ <span class="kw">else</span>:
+ <span class="name">q</span> <span class="op">=</span> (<span class="name">users</span>.<span class="name">c</span>.<span class="name">username</span> <span class="op">==</span> <span class="name">user_id_or_name</span>)
+ <span class="name">result</span> <span class="op">=</span> <span class="name">req</span>.<span class="name">ctx</span>.<span class="name">engine</span>.<span class="name">execute</span>(<span class="name">meta</span>.<span class="name">select</span>([<span class="name">users</span>.<span class="name">c</span>.<span class="name">user_id</span>], <span class="name">q</span>))
+ <span class="name">row</span> <span class="op">=</span> <span class="name">result</span>.<span class="name">fetchone</span>()
+ <span class="kw">if</span> <span class="name">row</span> <span class="op op-word">is</span> <span class="op op-word">not</span> <span class="bn bn-pseudo">None</span>:
+ <span class="name">fn</span> <span class="op">=</span> <span class="name">path</span>.<span class="name">join</span>(<span class="name">req</span>.<span class="name">ctx</span>.<span class="name">cfg</span>.<span class="name">root</span>, <span class="st st-sg">&#39;</span><span class="st">avatars</span><span class="st st-sg">&#39;</span>, <span class="st st-sg">&#39;</span><span class="st st-int">%d</span><span class="st">.png</span><span class="st st-sg">&#39;</span> <span class="op">%</span> <span class="name">row</span>[<span class="nb nb-int">0</span>])
+ <span class="kw">if</span> <span class="name">path</span>.<span class="name">exists</span>(<span class="name">fn</span>):
+ <span class="name">f</span> <span class="op">=</span> <span class="bn">file</span>(<span class="name">fn</span>, <span class="st st-sg">&#39;</span><span class="st">rb</span><span class="st st-sg">&#39;</span>)
+ <span class="name">result</span> <span class="op">=</span> <span class="name">f</span>.<span class="name">read</span>()
+ <span class="name">f</span>.<span class="name">close</span>()
+ <span class="kw">return</span> <span class="name">result</span>
+
+
+<span class="kw">def </span><span class="fun">get_id_by_name</span>(<span class="name">ctx</span>, <span class="name">name</span>):
+ <span class="name">u</span> <span class="op">=</span> <span class="name">users</span>.<span class="name">c</span>
+
+ <span class="name">result</span> <span class="op">=</span> <span class="name">ctx</span>.<span class="name">engine</span>.<span class="name">execute</span>(<span class="name">meta</span>.<span class="name">select</span>([<span class="name">u</span>.<span class="name">user_id</span>],
+ <span class="name">u</span>.<span class="name">username</span> <span class="op">==</span> <span class="name">name</span>))
+ <span class="name">row</span> <span class="op">=</span> <span class="name">result</span>.<span class="name">fetchone</span>()
+ <span class="kw">if</span> <span class="op op-word">not</span> <span class="name">row</span>:
+ <span class="kw">raise</span> <span class="name">UserNotFound</span>
+
+ <span class="kw">return</span> <span class="name">row</span>[<span class="nb nb-int">0</span>]
+
+
+<span class="kw">def </span><span class="fun">check_login_data</span>(<span class="name">ctx</span>, <span class="name">username</span>, <span class="name">password</span>):
+ <span class="st st-db">&quot;&quot;&quot;</span><span class="st">
+
+ Check if a username and password was found in the
+ database. Returns None if the user does not exist
+ or the password is incorrect, otherwise it returns
+ the user id.
+ </span><span class="st st-db">&quot;&quot;&quot;</span>
+ <span class="name">u</span> <span class="op">=</span> <span class="name">users</span>.<span class="name">c</span>
+ <span class="name">result</span> <span class="op">=</span> <span class="name">ctx</span>.<span class="name">engine</span>.<span class="name">execute</span>(<span class="name">meta</span>.<span class="name">select</span>([<span class="name">u</span>.<span class="name">user_id</span>, <span class="name">u</span>.<span class="name">pwhash</span>, <span class="name">u</span>.<span class="name">act_key</span>],
+ <span class="name">users</span>.<span class="name">c</span>.<span class="name">username</span> <span class="op">==</span> <span class="name">username</span>
+
+ ))
+ <span class="name">row</span> <span class="op">=</span> <span class="name">result</span>.<span class="name">fetchone</span>()
+ <span class="kw">if</span> <span class="name">row</span> <span class="op op-word">is</span> <span class="op op-word">not</span> <span class="bn bn-pseudo">None</span> <span class="op op-word">and</span> <span class="op op-word">not</span> <span class="name">row</span>[<span class="st st-sg">&#39;</span><span class="st">act_key</span><span class="st st-sg">&#39;</span>] <span class="op op-word">and</span> \
+ <span class="name">check_pwhash</span>(<span class="name">row</span>[<span class="st st-sg">&#39;</span><span class="st">pwhash</span><span class="st st-sg">&#39;</span>], <span class="name">password</span>):
+ <span class="kw">return</span> <span class="name">row</span>[<span class="st st-sg">&#39;</span><span class="st">user_id</span><span class="st st-sg">&#39;</span>]
+
+
+
+<span class="kw">def </span><span class="fun">sync_post_count</span>(<span class="name">ctx</span>, <span class="name">user_id</span>):
+ <span class="st st-db">&quot;&quot;&quot;</span><span class="st">Sync the user post count with the post tables.</span><span class="st st-db">&quot;&quot;&quot;</span>
+ <span class="kw">def </span><span class="fun">do</span>(<span class="name">con</span>):
+ <span class="name">result</span> <span class="op">=</span> <span class="name">con</span>.<span class="name">execute</span>(<span class="name">meta</span>.<span class="name">select</span>([<span class="name">meta</span>.<span class="name">func</span>.<span class="name">count</span>(<span class="name">posts</span>.<span class="name">c</span>.<span class="name">post_id</span>)],
+ <span class="name">posts</span>.<span class="name">c</span>.<span class="name">author_id</span> <span class="op">==</span> <span class="name">user_id</span>
+
+ ))
+ <span class="name">con</span>.<span class="name">execute</span>(<span class="name">users</span>.<span class="name">update</span>(<span class="name">users</span>.<span class="name">c</span>.<span class="name">user_id</span> <span class="op">==</span> <span class="name">user_id</span>),
+ <span class="name">post_count</span> <span class="op">=</span> <span class="name">result</span>.<span class="name">fetchone</span>()[<span class="nb nb-int">0</span>]
+ )
+ <span class="name">ctx</span>.<span class="name">engine</span>.<span class="name">transaction</span>(<span class="name">do</span>)
+
+
+
+<span class="kw">def </span><span class="fun">reset_password</span>(<span class="name">ctx</span>, <span class="name">username</span>, <span class="name">email</span>):
+ <span class="st st-db">&quot;&quot;&quot;</span><span class="st">
+ Reset the password and returns the new password
+ if the email matched the username.
+ If not it will return None.
+ </span><span class="st st-db">&quot;&quot;&quot;</span>
+ <span class="name">password</span> <span class="op">=</span> <span class="name">gen_password</span>()
+ <span class="name">result</span> <span class="op">=</span> <span class="name">ctx</span>.<span class="name">engine</span>.<span class="name">execute</span>(<span class="name">users</span>.<span class="name">update</span>((<span class="name">users</span>.<span class="name">c</span>.<span class="name">username</span> <span class="op">==</span> <span class="name">username</span>) <span class="op">&amp;</span>
+
+ (<span class="name">users</span>.<span class="name">c</span>.<span class="name">email</span> <span class="op">==</span> <span class="name">email</span>)),
+ <span class="name">pwhash</span> <span class="op">=</span> <span class="name">gen_pwhash</span>(<span class="name">password</span>)
+ )
+ <span class="kw">if</span> <span class="name">result</span>.<span class="name">rowcount</span>:
+ <span class="kw">return</span> <span class="name">password</span>
+
+
+<span class="kw">class </span><span class="cls">UserNotFound</span>(<span class="exc">Exception</span>):
+ <span class="kw">pass</span>
+
+
+<span class="kw">class </span><span class="cls">User</span>(<span class="name">DatabaseModel</span>, <span class="name">LinkableMixin</span>):
+ <span class="name">NotFound</span> <span class="op">=</span> <span class="name">UserNotFound</span>
+
+ <span class="kw">def </span><span class="fun">__init__</span>(<span class="bn bn-pseudo">self</span>, <span class="name">ctx</span>, <span class="name">user_id</span>):
+ <span class="bn bn-pseudo">self</span>.<span class="name">ctx</span> <span class="op">=</span> <span class="name">ctx</span>
+
+ <span class="bn bn-pseudo">self</span>.<span class="name">user_id</span> <span class="op">=</span> <span class="name">user_id</span>
+ <span class="bn">super</span>(<span class="name">User</span>, <span class="bn bn-pseudo">self</span>).<span class="name">__init__</span>(<span class="name">ctx</span>, <span class="name">users</span>, <span class="st st-sg">&#39;</span><span class="st">user_id</span><span class="st st-sg">&#39;</span>)
+
+ <span class="kw">from </span><span class="cls">pocoo.pkg.core.acl</span><span class="kw"> import</span> <span class="name">AclManager</span>
+
+ <span class="bn bn-pseudo">self</span>.<span class="name">acl</span> <span class="op">=</span> <span class="name">AclManager</span>(<span class="name">ctx</span>, <span class="bn bn-pseudo">self</span>)
+
+ <span class="name">subject_id</span> <span class="op">=</span> <span class="name">lazy_column</span>(<span class="st st-sg">&#39;</span><span class="st">subject_id</span><span class="st st-sg">&#39;</span>)
+ <span class="name">username</span> <span class="op">=</span> <span class="name">lazy_column</span>(<span class="st st-sg">&#39;</span><span class="st">username</span><span class="st st-sg">&#39;</span>)
+ <span class="name">email</span> <span class="op">=</span> <span class="name">lazy_column</span>(<span class="st st-sg">&#39;</span><span class="st">email</span><span class="st st-sg">&#39;</span>)
+ <span class="name">pwhash</span> <span class="op">=</span> <span class="name">lazy_column</span>(<span class="st st-sg">&#39;</span><span class="st">pwhash</span><span class="st st-sg">&#39;</span>)
+ <span class="name">act_key</span> <span class="op">=</span> <span class="name">lazy_column</span>(<span class="st st-sg">&#39;</span><span class="st">act_key</span><span class="st st-sg">&#39;</span>)
+ <span class="name">language</span> <span class="op">=</span> <span class="name">lazy_column</span>(<span class="st st-sg">&#39;</span><span class="st">language</span><span class="st st-sg">&#39;</span>)
+ <span class="name">profile</span> <span class="op">=</span> <span class="name">lazy_column</span>(<span class="st st-sg">&#39;</span><span class="st">profile</span><span class="st st-sg">&#39;</span>)
+ <span class="name">settings</span> <span class="op">=</span> <span class="name">lazy_column</span>(<span class="st st-sg">&#39;</span><span class="st">settings</span><span class="st st-sg">&#39;</span>)
+ <span class="name">last_login</span> <span class="op">=</span> <span class="name">lazy_column</span>(<span class="st st-sg">&#39;</span><span class="st">last_login</span><span class="st st-sg">&#39;</span>)
+ <span class="name">register_date</span> <span class="op">=</span> <span class="name">lazy_column</span>(<span class="st st-sg">&#39;</span><span class="st">register_date</span><span class="st st-sg">&#39;</span>)
+ <span class="name">post_count</span> <span class="op">=</span> <span class="name">lazy_column</span>(<span class="st st-sg">&#39;</span><span class="st">post_count</span><span class="st st-sg">&#39;</span>)
+ <span class="name">read_threads</span> <span class="op">=</span> <span class="name">lazy_column</span>(<span class="st st-sg">&#39;</span><span class="st">read_threads</span><span class="st st-sg">&#39;</span>)
+ <span class="name">read_posts</span> <span class="op">=</span> <span class="name">lazy_column</span>(<span class="st st-sg">&#39;</span><span class="st">read_posts</span><span class="st st-sg">&#39;</span>)
+
+ <span class="deco">@property</span>
+
+ <span class="kw">def </span><span class="fun">relative_url</span>(<span class="bn bn-pseudo">self</span>):
+ <span class="kw">return</span> <span class="st st-sg">&#39;</span><span class="st">users/</span><span class="st st-int">%s</span><span class="st st-sg">&#39;</span> <span class="op">%</span> <span class="bn bn-pseudo">self</span>.<span class="name">username</span>
+
+ <span class="deco">@staticmethod</span>
+ <span class="kw">def </span><span class="fun">create</span>(<span class="name">ctx</span>, <span class="name">username</span>, <span class="name">password</span>, <span class="name">email</span>, <span class="name">activate</span><span class="op">=</span><span class="bn bn-pseudo">False</span>):
+ <span class="st st-db">&quot;&quot;&quot;</span><span class="st">Creates a new user.</span><span class="st st-db">&quot;&quot;&quot;</span>
+
+ <span class="kw">if</span> <span class="name">activate</span>:
+ <span class="name">act_key</span> <span class="op">=</span> <span class="name">gen_activation_key</span>()
+ <span class="kw">else</span>:
+ <span class="name">act_key</span> <span class="op">=</span> <span class="bn bn-pseudo">None</span>
+
+ <span class="name">result</span> <span class="op">=</span> <span class="name">ctx</span>.<span class="name">engine</span>.<span class="name">execute</span>(<span class="name">users</span>.<span class="name">insert</span>(),
+ <span class="name">username</span><span class="op">=</span><span class="name">username</span>,
+ <span class="name">email</span><span class="op">=</span><span class="name">email</span>,
+ <span class="name">act_key</span><span class="op">=</span><span class="name">act_key</span>,
+ <span class="name">pwhash</span><span class="op">=</span><span class="name">gen_pwhash</span>(<span class="name">password</span>),
+ <span class="name">register_date</span><span class="op">=</span><span class="name">datetime</span>.<span class="name">utcnow</span>(),
+ <span class="name">post_count</span><span class="op">=</span><span class="nb nb-int">0</span>
+
+ )
+ <span class="kw">return</span> <span class="name">User</span>(<span class="name">ctx</span>, <span class="name">result</span>.<span class="name">last_inserted_ids</span>()[<span class="nb nb-int">0</span>])
+
+ <span class="kw">def </span><span class="fun">check_password</span>(<span class="bn bn-pseudo">self</span>, <span class="name">password</span>):
+ <span class="kw">if</span> <span class="op op-word">not</span> <span class="bn bn-pseudo">self</span>.<span class="name">exists</span>:
+ <span class="kw">return</span> <span class="bn bn-pseudo">False</span>
+
+ <span class="kw">return</span> <span class="name">check_pwhash</span>(<span class="bn bn-pseudo">self</span>.<span class="name">pwhash</span>, <span class="name">password</span>)
+
+ <span class="kw">def </span><span class="fun">set_password</span>(<span class="bn bn-pseudo">self</span>, <span class="name">password</span>):
+ <span class="bn bn-pseudo">self</span>.<span class="name">pwhash</span> <span class="op">=</span> <span class="name">gen_pwhash</span>(<span class="name">password</span>)
+
+ <span class="kw">def </span><span class="fun">assert_logged_in</span>(<span class="bn bn-pseudo">self</span>):
+ <span class="st st-db">&quot;&quot;&quot;</span><span class="st">
+
+ Check if the user is logged in and raise a DirectResponse
+ exception with an AccessDeniedResponse to display the user a
+ login or &quot;missing permission&quot; page.
+ </span><span class="st st-db">&quot;&quot;&quot;</span>
+ <span class="kw">if</span> <span class="op op-word">not</span> <span class="bn bn-pseudo">self</span>.<span class="name">identified</span>:
+ <span class="kw">raise</span> <span class="name">DirectResponse</span>(<span class="name">AccessDeniedResponse</span>())
+
+ <span class="kw">def </span><span class="fun">iter_groups</span>(<span class="bn bn-pseudo">self</span>):
+ <span class="st st-db">&quot;&quot;&quot;</span><span class="st">
+
+ Return a generator for all groups this user has joined.
+ </span><span class="st st-db">&quot;&quot;&quot;</span>
+ <span class="name">result</span> <span class="op">=</span> <span class="bn bn-pseudo">self</span>.<span class="name">ctx</span>.<span class="name">engine</span>.<span class="name">execute</span>(<span class="name">meta</span>.<span class="name">select</span>([<span class="name">groups</span>.<span class="name">c</span>.<span class="name">group_id</span>],
+ (<span class="name">users</span>.<span class="name">c</span>.<span class="name">user_id</span> <span class="op">==</span> <span class="bn bn-pseudo">self</span>.<span class="name">user_id</span>) <span class="op">&amp;</span>
+
+ (<span class="name">group_members</span>.<span class="name">c</span>.<span class="name">group_id</span> <span class="op">==</span> <span class="name">groups</span>.<span class="name">c</span>.<span class="name">group_id</span>) <span class="op">&amp;</span>
+
+ (<span class="name">group_members</span>.<span class="name">c</span>.<span class="name">user_id</span> <span class="op">==</span> <span class="name">users</span>.<span class="name">c</span>.<span class="name">user_id</span>)
+ ))
+ <span class="kw">for</span> <span class="name">row</span> <span class="op op-word">in</span> <span class="name">result</span>:
+ <span class="kw">yield</span> <span class="name">Group</span>(<span class="bn bn-pseudo">self</span>.<span class="name">ctx</span>, <span class="name">row</span>[<span class="nb nb-int">0</span>])
+
+ <span class="kw">def </span><span class="fun">activate</span>(<span class="bn bn-pseudo">self</span>):
+ <span class="bn bn-pseudo">self</span>.<span class="name">act_key</span> <span class="op">=</span> <span class="bn bn-pseudo">None</span>
+
+ <span class="kw">def </span><span class="fun">deactivate</span>(<span class="bn bn-pseudo">self</span>):
+ <span class="bn bn-pseudo">self</span>.<span class="name">act_key</span> <span class="op">=</span> <span class="name">gen_activation_key</span>()
+
+ <span class="deco">@property</span>
+
+ <span class="kw">def </span><span class="fun">identified</span>(<span class="bn bn-pseudo">self</span>):
+ <span class="kw">return</span> <span class="bn bn-pseudo">self</span>.<span class="name">user_id</span> <span class="op">&gt;</span> <span class="nb nb-int">0</span>
+
+ <span class="deco">@property</span>
+
+ <span class="kw">def </span><span class="fun">active</span>(<span class="bn bn-pseudo">self</span>):
+ <span class="kw">return</span> <span class="op op-word">not</span> <span class="bn bn-pseudo">self</span>.<span class="name">act_key</span>
+
+ <span class="deco">@property</span>
+
+ <span class="kw">def </span><span class="fun">groups</span>(<span class="bn bn-pseudo">self</span>):
+ <span class="kw">return</span> <span class="bn">list</span>(<span class="bn bn-pseudo">self</span>.<span class="name">iter_groups</span>())
+
+ <span class="deco">@property</span>
+ <span class="kw">def </span><span class="fun">admin</span>(<span class="bn bn-pseudo">self</span>):
+ <span class="cm">#TODO: from ACL</span>
+
+ <span class="kw">return</span> <span class="bn bn-pseudo">True</span>
+
+ <span class="kw">def </span><span class="fun">__eq__</span>(<span class="bn bn-pseudo">self</span>, <span class="name">other</span>):
+ <span class="kw">return</span> <span class="bn bn-pseudo">self</span>.<span class="name">user_id</span> <span class="op">==</span> <span class="name">other</span>.<span class="name">user_id</span>
+
+ <span class="kw">def </span><span class="fun">__ne__</span>(<span class="bn bn-pseudo">self</span>, <span class="name">other</span>):
+ <span class="kw">return</span> <span class="op op-word">not</span> <span class="bn bn-pseudo">self</span>.<span class="name">__eq__</span>(<span class="name">other</span>)
+
+ <span class="kw">def </span><span class="fun">__repr__</span>(<span class="bn bn-pseudo">self</span>):
+ <span class="kw">return</span> <span class="st st-sg">&#39;</span><span class="st">&lt;</span><span class="st st-int">%s</span><span class="st"> </span><span class="st st-int">%d</span><span class="st">: </span><span class="st st-int">%r</span><span class="st">&gt;</span><span class="st st-sg">&#39;</span> <span class="op">%</span> (
+ <span class="bn bn-pseudo">self</span>.<span class="name">__class__</span>.<span class="name">__name__</span>,
+ <span class="bn bn-pseudo">self</span>.<span class="name">user_id</span>,
+ <span class="bn bn-pseudo">self</span>.<span class="name">username</span>
+
+ )
+
+
+<span class="kw">class </span><span class="cls">GroupNotFound</span>(<span class="exc">Exception</span>):
+ <span class="kw">pass</span>
+
+
+<span class="kw">class </span><span class="cls">Group</span>(<span class="name">DatabaseModel</span>):
+
+ <span class="kw">def </span><span class="fun">__init__</span>(<span class="bn bn-pseudo">self</span>, <span class="name">ctx</span>, <span class="name">group_id</span>):
+ <span class="bn bn-pseudo">self</span>.<span class="name">ctx</span> <span class="op">=</span> <span class="name">ctx</span>
+
+ <span class="bn bn-pseudo">self</span>.<span class="name">group_id</span> <span class="op">=</span> <span class="name">group_id</span>
+ <span class="bn">super</span>(<span class="name">Group</span>, <span class="bn bn-pseudo">self</span>).<span class="name">__init__</span>(<span class="name">ctx</span>, <span class="name">groups</span>, <span class="st st-sg">&#39;</span><span class="st">group_id</span><span class="st st-sg">&#39;</span>)
+
+ <span class="kw">from </span><span class="cls">pocoo.pkg.core.acl</span><span class="kw"> import</span> <span class="name">AclManager</span>
+
+ <span class="bn bn-pseudo">self</span>.<span class="name">acl</span> <span class="op">=</span> <span class="name">AclManager</span>(<span class="name">ctx</span>, <span class="bn bn-pseudo">self</span>)
+
+ <span class="name">subject_id</span> <span class="op">=</span> <span class="name">lazy_column</span>(<span class="st st-sg">&#39;</span><span class="st">subject_id</span><span class="st st-sg">&#39;</span>)
+ <span class="name">name</span> <span class="op">=</span> <span class="name">lazy_column</span>(<span class="st st-sg">&#39;</span><span class="st">name</span><span class="st st-sg">&#39;</span>)
+ <span class="name">public</span> <span class="op">=</span> <span class="name">lazy_column</span>(<span class="st st-sg">&#39;</span><span class="st">public</span><span class="st st-sg">&#39;</span>)
+ <span class="name">hidden</span> <span class="op">=</span> <span class="name">lazy_column</span>(<span class="st st-sg">&#39;</span><span class="st">hidden</span><span class="st st-sg">&#39;</span>)
+
+ <span class="deco">@staticmethod</span>
+
+ <span class="kw">def </span><span class="fun">create</span>(<span class="name">ctx</span>, <span class="name">name</span>, <span class="name">public</span><span class="op">=</span><span class="bn bn-pseudo">True</span>, <span class="name">hidden</span><span class="op">=</span><span class="bn bn-pseudo">False</span>):
+ <span class="st st-db">&quot;&quot;&quot;</span><span class="st">Create a new usergroup.</span><span class="st st-db">&quot;&quot;&quot;</span>
+
+ <span class="name">result</span> <span class="op">=</span> <span class="name">ctx</span>.<span class="name">engine</span>.<span class="name">execute</span>(<span class="name">groups</span>.<span class="name">insert</span>(),
+ <span class="name">name</span><span class="op">=</span><span class="name">name</span>,
+ <span class="name">public</span><span class="op">=</span><span class="name">public</span>,
+ <span class="name">hidden</span><span class="op">=</span><span class="name">hidden</span>
+
+ )
+ <span class="kw">return</span> <span class="name">Group</span>(<span class="name">ctx</span>, <span class="name">result</span>.<span class="name">last_inserted_ids</span>()[<span class="nb nb-int">0</span>])
+
+ <span class="deco">@property</span>
+ <span class="kw">def </span><span class="fun">members</span>(<span class="bn bn-pseudo">self</span>):
+ <span class="kw">return</span> <span class="bn">list</span>(<span class="bn bn-pseudo">self</span>.<span class="name">iter_members</span>())
+
+ <span class="kw">def </span><span class="fun">iter_members</span>(<span class="bn bn-pseudo">self</span>):
+ <span class="st st-db">&quot;&quot;&quot;</span><span class="st">Return a generator for all group members.</span><span class="st st-db">&quot;&quot;&quot;</span>
+
+ <span class="name">result</span> <span class="op">=</span> <span class="bn bn-pseudo">self</span>.<span class="name">ctx</span>.<span class="name">engine</span>.<span class="name">execute</span>(<span class="name">meta</span>.<span class="name">select</span>([<span class="name">users</span>.<span class="name">c</span>.<span class="name">user_id</span>],
+ (<span class="name">groups</span>.<span class="name">c</span>.<span class="name">group_id</span> <span class="op">==</span> <span class="bn bn-pseudo">self</span>.<span class="name">group_id</span>) <span class="op">&amp;</span>
+
+ (<span class="name">group_members</span>.<span class="name">c</span>.<span class="name">group_id</span> <span class="op">==</span> <span class="name">groups</span>.<span class="name">c</span>.<span class="name">group_id</span>) <span class="op">&amp;</span>
+
+ (<span class="name">group_members</span>.<span class="name">c</span>.<span class="name">user_id</span> <span class="op">==</span> <span class="name">users</span>.<span class="name">c</span>.<span class="name">user_id</span>)
+ ))
+ <span class="kw">for</span> <span class="name">row</span> <span class="op op-word">in</span> <span class="name">result</span>:
+ <span class="kw">yield</span> <span class="name">User</span>(<span class="bn bn-pseudo">self</span>.<span class="name">ctx</span>, <span class="name">row</span>[<span class="nb nb-int">0</span>])
+
+ <span class="kw">def </span><span class="fun">add_member</span>(<span class="bn bn-pseudo">self</span>, <span class="name">user</span>):
+ <span class="st st-db">&quot;&quot;&quot;</span><span class="st">Add a new member to the group.</span><span class="st st-db">&quot;&quot;&quot;</span>
+
+ <span class="kw">if</span> <span class="bn">isinstance</span>(<span class="name">user</span>, <span class="name">User</span>):
+ <span class="name">user_id</span> <span class="op">=</span> <span class="name">user</span>.<span class="name">user_id</span>
+
+ <span class="kw">else</span>:
+ <span class="name">user_id</span> <span class="op">=</span> <span class="name">User</span>(<span class="bn bn-pseudo">self</span>.<span class="name">ctx</span>, <span class="name">user</span>).<span class="name">user_id</span>
+
+ <span class="cm"># check if the user is already a member of the group</span>
+ <span class="name">result</span> <span class="op">=</span> <span class="bn bn-pseudo">self</span>.<span class="name">ctx</span>.<span class="name">engine</span>.<span class="name">execute</span>(<span class="name">meta</span>.<span class="name">select</span>([<span class="name">group_members</span>.<span class="name">c</span>.<span class="name">user_id</span>],
+ (<span class="name">group_members</span>.<span class="name">c</span>.<span class="name">user_id</span> <span class="op">==</span> <span class="name">user_id</span>) <span class="op">&amp;</span>
+
+ (<span class="name">group_members</span>.<span class="name">c</span>.<span class="name">group_id</span> <span class="op">==</span> <span class="bn bn-pseudo">self</span>.<span class="name">group_id</span>)
+ ))
+ <span class="kw">if</span> <span class="name">result</span>.<span class="name">fetchone</span>() <span class="op op-word">is</span> <span class="op op-word">not</span> <span class="bn bn-pseudo">None</span>:
+ <span class="kw">raise</span> <span class="exc">ValueError</span>(<span class="st st-sg">&#39;</span><span class="st">The user </span><span class="st st-int">%d</span><span class="st"> is alreay a member of this group</span><span class="st st-sg">&#39;</span> <span class="op">%</span>
+
+ <span class="name">user_id</span>)
+ <span class="bn bn-pseudo">self</span>.<span class="name">ctx</span>.<span class="name">engine</span>.<span class="name">execute</span>(<span class="name">group_members</span>.<span class="name">insert</span>(),
+ <span class="name">user_id</span> <span class="op">=</span> <span class="name">user_id</span>,
+ <span class="name">group_id</span> <span class="op">=</span> <span class="bn bn-pseudo">self</span>.<span class="name">group_id</span>
+
+ )
+
+ <span class="kw">def </span><span class="fun">remove_member</span>(<span class="bn bn-pseudo">self</span>, <span class="name">user</span>):
+ <span class="st st-db">&quot;&quot;&quot;</span><span class="st">Remove a member from the group.</span><span class="st st-db">&quot;&quot;&quot;</span>
+ <span class="kw">if</span> <span class="bn">isinstance</span>(<span class="name">user</span>, <span class="name">User</span>):
+ <span class="name">user_id</span> <span class="op">=</span> <span class="name">user</span>.<span class="name">user_id</span>
+
+ <span class="kw">else</span>:
+ <span class="name">user_id</span> <span class="op">=</span> <span class="name">User</span>(<span class="bn bn-pseudo">self</span>.<span class="name">ctx</span>, <span class="name">user</span>).<span class="name">user_id</span>
+
+ <span class="cm"># check if the user is not in the group</span>
+ <span class="name">result</span> <span class="op">=</span> <span class="bn bn-pseudo">self</span>.<span class="name">ctx</span>.<span class="name">engine</span>.<span class="name">execute</span>(<span class="name">meta</span>.<span class="name">select</span>([<span class="name">group_members</span>.<span class="name">c</span>.<span class="name">user_id</span>],
+ (<span class="name">group_members</span>.<span class="name">c</span>.<span class="name">user_id</span> <span class="op">==</span> <span class="name">user_id</span>) <span class="op">&amp;</span>
+
+ (<span class="name">group_members</span>.<span class="name">c</span>.<span class="name">group_id</span> <span class="op">==</span> <span class="bn bn-pseudo">self</span>.<span class="name">group_id</span>)
+ ))
+ <span class="kw">if</span> <span class="name">result</span>.<span class="name">fetchone</span>() <span class="op op-word">is</span> <span class="bn bn-pseudo">None</span>:
+ <span class="kw">raise</span> <span class="exc">ValueError</span>(<span class="st st-sg">&#39;</span><span class="st">The user </span><span class="st st-int">%d</span><span class="st"> is not a member of this group</span><span class="st st-sg">&#39;</span> <span class="op">%</span>
+
+ <span class="name">user_id</span>)
+ <span class="bn bn-pseudo">self</span>.<span class="name">ctx</span>.<span class="name">engine</span>.<span class="name">execute</span>(<span class="name">group_members</span>.<span class="name">delete</span>(
+ (<span class="name">group_members</span>.<span class="name">c</span>.<span class="name">group_id</span> <span class="op">==</span> <span class="bn bn-pseudo">self</span>.<span class="name">group_id</span>) <span class="op">&amp;</span>
+
+ (<span class="name">group_members</span>.<span class="name">c</span>.<span class="name">user_id</span> <span class="op">==</span> <span class="name">user_id</span>)
+ ))
+
+ <span class="kw">def </span><span class="fun">is_member</span>(<span class="bn bn-pseudo">self</span>, <span class="name">user</span>):
+ <span class="st st-db">&quot;&quot;&quot;</span><span class="st">Check if a user is a member of this group.</span><span class="st st-db">&quot;&quot;&quot;</span>
+
+ <span class="kw">if</span> <span class="bn">isinstance</span>(<span class="name">user</span>, <span class="name">User</span>):
+ <span class="name">user_id</span> <span class="op">=</span> <span class="name">user</span>.<span class="name">user_id</span>
+
+ <span class="kw">else</span>:
+ <span class="name">user_id</span> <span class="op">=</span> <span class="name">User</span>(<span class="bn bn-pseudo">self</span>.<span class="name">ctx</span>, <span class="name">user</span>).<span class="name">user_id</span>
+
+ <span class="name">result</span> <span class="op">=</span> <span class="bn bn-pseudo">self</span>.<span class="name">ctx</span>.<span class="name">engine</span>.<span class="name">execute</span>(<span class="name">meta</span>.<span class="name">select</span>([<span class="name">group_members</span>.<span class="name">c</span>.<span class="name">user_id</span>],
+ (<span class="name">group_members</span>.<span class="name">c</span>.<span class="name">user_id</span> <span class="op">==</span> <span class="name">user_id</span>) <span class="op">&amp;</span>
+
+ (<span class="name">group_members</span>.<span class="name">c</span>.<span class="name">group_id</span> <span class="op">==</span> <span class="bn bn-pseudo">self</span>.<span class="name">group_id</span>)
+ ))
+ <span class="kw">return</span> <span class="name">result</span>.<span class="name">fetchone</span>() <span class="op op-word">is</span> <span class="op op-word">not</span> <span class="bn bn-pseudo">None</span>
+
+ <span class="kw">def </span><span class="fun">__eq__</span>(<span class="bn bn-pseudo">self</span>, <span class="name">other</span>):
+ <span class="kw">return</span> <span class="bn bn-pseudo">self</span>.<span class="name">group_id</span> <span class="op">==</span> <span class="name">other</span>.<span class="name">group_id</span>
+
+ <span class="kw">def </span><span class="fun">__ne__</span>(<span class="bn bn-pseudo">self</span>, <span class="name">other</span>):
+ <span class="kw">return</span> <span class="op op-word">not</span> <span class="bn bn-pseudo">self</span>.<span class="name">__eq__</span>(<span class="name">other</span>)
+
+ <span class="kw">def </span><span class="fun">__repr__</span>(<span class="bn bn-pseudo">self</span>):
+ <span class="kw">return</span> <span class="st st-sg">&#39;</span><span class="st">&lt;</span><span class="st st-int">%s</span><span class="st"> </span><span class="st st-int">%d</span><span class="st">: </span><span class="st st-int">%r</span><span class="st">&gt;</span><span class="st st-sg">&#39;</span> <span class="op">%</span> (
+ <span class="bn bn-pseudo">self</span>.<span class="name">__class__</span>.<span class="name">__name__</span>,
+ <span class="bn bn-pseudo">self</span>.<span class="name">group_id</span>,
+ <span class="bn bn-pseudo">self</span>.<span class="name">name</span>
+
+ )
+<span class="cm"># -*- coding: utf-8 -*-</span>
+<span class="st st-db">&quot;&quot;&quot;</span><span class="st">
+ pocoo.pkg.core.usersettings
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+ User Settings support.
+
+ :copyright: 2006 by Armin Ronacher, Lukas Meuser.
+ :license: GNU GPL, see LICENSE for more details.
+</span><span class="st st-db">&quot;&quot;&quot;</span>
+<span class="kw">import </span><span class="cls">os</span>
+<span class="kw">from </span><span class="cls">os</span><span class="kw"> import</span> <span class="name">path</span>
+
+<span class="kw">from </span><span class="cls">pocoo</span><span class="kw"> import</span> <span class="name">Component</span>
+<span class="kw">from </span><span class="cls">pocoo.http</span><span class="kw"> import</span> <span class="name">PageNotFound</span>
+<span class="kw">from </span><span class="cls">pocoo.application</span><span class="kw"> import</span> <span class="name">LinkableMixin</span>
+
+<span class="kw">from </span><span class="cls">pocoo.utils.image</span><span class="kw"> import</span> <span class="name">resize_image</span>
+<span class="kw">from </span><span class="cls">pocoo.utils.uri</span><span class="kw"> import</span> <span class="name">urlencode</span>
+<span class="kw">from </span><span class="cls">pocoo.utils.form</span><span class="kw"> import</span> <span class="name">Form</span>, <span class="name">TextField</span>, <span class="name">FileField</span>, <span class="name">TextArea</span>, <span class="name">CheckBox</span>, \
+ <span class="name">SelectBox</span>
+
+<span class="kw">from </span><span class="cls">pocoo.utils.validators</span><span class="kw"> import</span> <span class="name">isSameValue</span>, <span class="name">isEmail</span>, <span class="name">isExistingUrl</span>, \
+ <span class="name">checkTextLength</span>, <span class="name">mayEmpty</span>, <span class="name">checkIfOtherNotBlank</span>, <span class="name">isSupportedImage</span>, \
+ <span class="name">doMultiCheck</span>, <span class="name">isInRange</span>, <span class="name">isInteger</span>
+
+<span class="kw">from </span><span class="cls">pocoo.pkg.core.validators</span><span class="kw"> import</span> <span class="name">isStrongPassword</span>, <span class="name">isIcqMessengerId</span>, \
+ <span class="name">isMsnMessengerId</span>, <span class="name">isJabberMessengerId</span>
+<span class="kw">from </span><span class="cls">pocoo.pkg.core.textfmt</span><span class="kw"> import</span> <span class="name">get_editor</span>
+
+
+<span class="kw">class </span><span class="cls">UserSettingsPage</span>(<span class="name">Component</span>, <span class="name">LinkableMixin</span>):
+
+ <span class="deco">@property</span>
+ <span class="kw">def </span><span class="fun">settings_page_identifier</span>(<span class="bn bn-pseudo">self</span>):
+ <span class="st st-db">&quot;&quot;&quot;</span><span class="st">The name of the page which is also the url
+ under which the page will be visible::
+
+ /settings/$SETTINGS_PAGE_IDENTIFIER$</span><span class="st st-db">&quot;&quot;&quot;</span>
+
+ <span class="kw">return</span> <span class="bn bn-pseudo">self</span>.<span class="name">__class__</span>.<span class="name">__name__</span>
+
+ <span class="deco">@property</span>
+ <span class="kw">def </span><span class="fun">relative_url</span>(<span class="bn bn-pseudo">self</span>):
+ <span class="kw">return</span> <span class="st st-sg">&#39;</span><span class="st">settings/</span><span class="st st-int">%s</span><span class="st st-sg">&#39;</span> <span class="op">%</span> <span class="bn bn-pseudo">self</span>.<span class="name">settings_page_identifier</span>
+
+ <span class="kw">def </span><span class="fun">get_settings_link_title</span>(<span class="bn bn-pseudo">self</span>, <span class="name">req</span>):
+ <span class="st st-db">&quot;&quot;&quot;</span><span class="st">Has to return a text for the link title in the
+ settings sidebar (this musn&#39;t be a sidebar, in fact
+ it depends on the template.
+
+ If the method returns ``None`` the template wont
+ render this link.</span><span class="st st-db">&quot;&quot;&quot;</span>
+
+ <span class="kw">def </span><span class="fun">get_settings_page</span>(<span class="bn bn-pseudo">self</span>, <span class="name">req</span>):
+ <span class="st st-db">&quot;&quot;&quot;</span><span class="st">This method automatically gets called when a
+ user requests this settings page. It must either
+ return a valid Response object or a tuple in the
+ form (template, context) where template is a string
+ with the template filename and context is a dict
+ which automatically gets updated with the generated
+ sidebar so that templates can access it.</span><span class="st st-db">&quot;&quot;&quot;</span>
+
+
+
+<span class="kw">class </span><span class="cls">UserSignatureSettings</span>(<span class="name">UserSettingsPage</span>):
+ <span class="st st-db">&quot;&quot;&quot;</span><span class="st">
+ This page allows the user to create / edit his signature
+ using an editor.
+ </span><span class="st st-db">&quot;&quot;&quot;</span>
+ <span class="name">settings_page_identifier</span> <span class="op">=</span> <span class="st st-sg">&#39;</span><span class="st">signature</span><span class="st st-sg">&#39;</span>
+
+ <span class="kw">def </span><span class="fun">get_settings_link_title</span>(<span class="bn bn-pseudo">self</span>, <span class="name">req</span>):
+ <span class="name">_</span> <span class="op">=</span> <span class="name">req</span>.<span class="name">gettext</span>
+
+ <span class="kw">return</span> <span class="name">_</span>(<span class="st st-sg">&#39;</span><span class="st">Signature</span><span class="st st-sg">&#39;</span>)
+
+ <span class="kw">def </span><span class="fun">get_settings_page</span>(<span class="bn bn-pseudo">self</span>, <span class="name">req</span>):
+ <span class="name">_</span> <span class="op">=</span> <span class="name">req</span>.<span class="name">gettext</span>
+
+ <span class="name">get_setting</span> <span class="op">=</span> <span class="kw">lambda</span> <span class="name">x</span>: <span class="name">req</span>.<span class="name">user</span>.<span class="name">profile</span>.<span class="name">get</span>(<span class="name">x</span>, <span class="name">u</span><span class="st st-sg">&#39;&#39;</span>)
+ <span class="name">msg</span> <span class="op">=</span> <span class="bn bn-pseudo">None</span>
+
+ <span class="name">form</span> <span class="op">=</span> <span class="name">Form</span>(<span class="name">req</span>, <span class="bn bn-pseudo">self</span>, <span class="st st-sg">&#39;</span><span class="st">POST</span><span class="st st-sg">&#39;</span>,
+ <span class="name">TextArea</span>(<span class="st st-sg">&#39;</span><span class="st">signature</span><span class="st st-sg">&#39;</span>,
+ <span class="name">default</span><span class="op">=</span><span class="name">get_setting</span>(<span class="st st-sg">&#39;</span><span class="st">signature</span><span class="st st-sg">&#39;</span>),
+ <span class="name">validator</span><span class="op">=</span><span class="name">checkTextLength</span>(<span class="nb nb-int">0</span>, <span class="nb nb-int">255</span>) <span class="cm"># from config!</span>
+
+ )
+ )
+ <span class="kw">if</span> <span class="name">req</span>.<span class="name">method</span> <span class="op">==</span> <span class="st st-sg">&#39;</span><span class="st">POST</span><span class="st st-sg">&#39;</span>:
+ <span class="name">form</span>.<span class="name">update</span>(<span class="name">req</span>.<span class="name">form</span>, <span class="name">prefix</span><span class="op">=</span><span class="st st-sg">&#39;</span><span class="st">f_</span><span class="st st-sg">&#39;</span>)
+ <span class="kw">if</span> <span class="op op-word">not</span> <span class="name">form</span>.<span class="name">has_errors</span>:
+ <span class="name">d</span> <span class="op">=</span> <span class="name">form</span>.<span class="name">to_dict</span>()
+ <span class="name">req</span>.<span class="name">user</span>.<span class="name">profile</span>.<span class="name">update</span>(<span class="name">d</span>)
+ <span class="name">req</span>.<span class="name">user</span>.<span class="name">save</span>()
+ <span class="name">msg</span> <span class="op">=</span> <span class="name">_</span>(<span class="st st-sg">&#39;</span><span class="st">Signature saved</span><span class="st st-sg">&#39;</span>)
+
+ <span class="name">js</span>, <span class="name">options</span> <span class="op">=</span> <span class="name">get_editor</span>(<span class="name">req</span>, <span class="name">signature</span><span class="op">=</span><span class="bn bn-pseudo">True</span>)
+ <span class="kw">return</span> <span class="st st-sg">&#39;</span><span class="st">settings/signature.html</span><span class="st st-sg">&#39;</span>, {
+ <span class="st st-sg">&#39;</span><span class="st">form</span><span class="st st-sg">&#39;</span>: <span class="name">form</span>.<span class="name">generate</span>(<span class="name">prefix</span><span class="op">=</span><span class="st st-sg">&#39;</span><span class="st">f_</span><span class="st st-sg">&#39;</span>),
+ <span class="st st-sg">&#39;</span><span class="st">msg</span><span class="st st-sg">&#39;</span>: <span class="name">msg</span>,
+ <span class="st st-sg">&#39;</span><span class="st">editor_options</span><span class="st st-sg">&#39;</span>: <span class="name">options</span>,
+ <span class="st st-sg">&#39;</span><span class="st">editor_javascript</span><span class="st st-sg">&#39;</span>:<span class="name">js</span>
+
+ }
+
+
+<span class="kw">class </span><span class="cls">UserProfileSettings</span>(<span class="name">UserSettingsPage</span>):
+ <span class="st st-db">&quot;&quot;&quot;</span><span class="st">
+ This page allows the user to edit his public information.
+
+ XXX: make this more flexible -- later (LATER!!!!)
+ </span><span class="st st-db">&quot;&quot;&quot;</span>
+ <span class="name">settings_page_identifier</span> <span class="op">=</span> <span class="st st-sg">&#39;</span><span class="st">profile</span><span class="st st-sg">&#39;</span>
+
+ <span class="kw">def </span><span class="fun">get_settings_link_title</span>(<span class="bn bn-pseudo">self</span>, <span class="name">req</span>):
+ <span class="name">_</span> <span class="op">=</span> <span class="name">req</span>.<span class="name">gettext</span>
+
+ <span class="kw">return</span> <span class="name">_</span>(<span class="st st-sg">&#39;</span><span class="st">Profile</span><span class="st st-sg">&#39;</span>)
+
+ <span class="kw">def </span><span class="fun">get_settings_page</span>(<span class="bn bn-pseudo">self</span>, <span class="name">req</span>):
+ <span class="name">_</span> <span class="op">=</span> <span class="name">req</span>.<span class="name">gettext</span>
+
+ <span class="name">get_setting</span> <span class="op">=</span> <span class="kw">lambda</span> <span class="name">x</span>: <span class="name">req</span>.<span class="name">user</span>.<span class="name">profile</span>.<span class="name">get</span>(<span class="name">x</span>, <span class="name">u</span><span class="st st-sg">&#39;&#39;</span>)
+ <span class="name">msg</span> <span class="op">=</span> <span class="bn bn-pseudo">None</span>
+
+ <span class="name">form</span> <span class="op">=</span> <span class="name">Form</span>(<span class="name">req</span>, <span class="bn bn-pseudo">self</span>, <span class="st st-sg">&#39;</span><span class="st">POST</span><span class="st st-sg">&#39;</span>,
+ <span class="cm"># general information</span>
+ <span class="name">TextField</span>(<span class="st st-sg">&#39;</span><span class="st">new_password</span><span class="st st-sg">&#39;</span>,
+ <span class="name">validator</span><span class="op">=</span><span class="name">mayEmpty</span>(<span class="name">isStrongPassword</span>())
+ ),
+ <span class="name">TextField</span>(<span class="st st-sg">&#39;</span><span class="st">new_password2</span><span class="st st-sg">&#39;</span>,
+ <span class="name">validator</span><span class="op">=</span><span class="name">checkIfOtherNotBlank</span>(<span class="st st-sg">&#39;</span><span class="st">new_password</span><span class="st st-sg">&#39;</span>,
+ <span class="name">isSameValue</span>(<span class="st st-sg">&#39;</span><span class="st">new_password</span><span class="st st-sg">&#39;</span>,
+ <span class="name">_</span>(<span class="st st-sg">&#39;</span><span class="st">The two passwords must match</span><span class="st st-sg">&#39;</span>))
+ )
+ ),
+ <span class="name">TextField</span>(<span class="st st-sg">&#39;</span><span class="st">email</span><span class="st st-sg">&#39;</span>,
+ <span class="name">default</span><span class="op">=</span><span class="name">req</span>.<span class="name">user</span>.<span class="name">email</span>,
+ <span class="name">validator</span><span class="op">=</span><span class="name">isEmail</span>()
+ ),
+ <span class="name">CheckBox</span>(<span class="st st-sg">&#39;</span><span class="st">show_email</span><span class="st st-sg">&#39;</span>,
+ <span class="name">default</span><span class="op">=</span><span class="name">req</span>.<span class="name">user</span>.<span class="name">settings</span>.<span class="name">get</span>(<span class="st st-sg">&#39;</span><span class="st">show_email</span><span class="st st-sg">&#39;</span>) <span class="op op-word">or</span> <span class="bn bn-pseudo">False</span>
+
+ ),
+ <span class="cm"># instant messengers</span>
+ <span class="name">TextField</span>(<span class="st st-sg">&#39;</span><span class="st">aol</span><span class="st st-sg">&#39;</span>,
+ <span class="name">default</span><span class="op">=</span><span class="name">get_setting</span>(<span class="st st-sg">&#39;</span><span class="st">aol</span><span class="st st-sg">&#39;</span>),
+ <span class="name">validator</span><span class="op">=</span><span class="name">mayEmpty</span>()
+ ), <span class="cm"># need a validator</span>
+
+ <span class="name">TextField</span>(<span class="st st-sg">&#39;</span><span class="st">icq</span><span class="st st-sg">&#39;</span>,
+ <span class="name">default</span><span class="op">=</span><span class="name">get_setting</span>(<span class="st st-sg">&#39;</span><span class="st">icq</span><span class="st st-sg">&#39;</span>),
+ <span class="name">validator</span><span class="op">=</span><span class="name">mayEmpty</span>(<span class="name">isIcqMessengerId</span>())
+ ), <span class="cm"># need a validator</span>
+
+ <span class="name">TextField</span>(<span class="st st-sg">&#39;</span><span class="st">jabber</span><span class="st st-sg">&#39;</span>,
+ <span class="name">default</span><span class="op">=</span><span class="name">get_setting</span>(<span class="st st-sg">&#39;</span><span class="st">jabber</span><span class="st st-sg">&#39;</span>),
+ <span class="name">validator</span><span class="op">=</span><span class="name">mayEmpty</span>(<span class="name">isJabberMessengerId</span>())
+ ), <span class="cm"># need a validator</span>
+
+ <span class="name">TextField</span>(<span class="st st-sg">&#39;</span><span class="st">msn</span><span class="st st-sg">&#39;</span>,
+ <span class="name">default</span><span class="op">=</span><span class="name">get_setting</span>(<span class="st st-sg">&#39;</span><span class="st">msn</span><span class="st st-sg">&#39;</span>),
+ <span class="name">validator</span><span class="op">=</span><span class="name">mayEmpty</span>(<span class="name">isMsnMessengerId</span>())
+ ), <span class="cm"># need a validator</span>
+
+ <span class="name">TextField</span>(<span class="st st-sg">&#39;</span><span class="st">yahoo</span><span class="st st-sg">&#39;</span>,
+ <span class="name">default</span><span class="op">=</span><span class="name">get_setting</span>(<span class="st st-sg">&#39;</span><span class="st">yahoo</span><span class="st st-sg">&#39;</span>),
+ <span class="name">validator</span><span class="op">=</span><span class="name">mayEmpty</span>()
+ ), <span class="cm"># need a validator</span>
+
+ <span class="name">TextField</span>(<span class="st st-sg">&#39;</span><span class="st">website</span><span class="st st-sg">&#39;</span>,
+ <span class="name">default</span><span class="op">=</span><span class="name">get_setting</span>(<span class="st st-sg">&#39;</span><span class="st">website</span><span class="st st-sg">&#39;</span>),
+ <span class="name">validator</span><span class="op">=</span><span class="name">mayEmpty</span>(<span class="name">isExistingUrl</span>())
+ ),
+ <span class="name">TextField</span>(<span class="st st-sg">&#39;</span><span class="st">location</span><span class="st st-sg">&#39;</span>,
+ <span class="name">default</span><span class="op">=</span><span class="name">get_setting</span>(<span class="st st-sg">&#39;</span><span class="st">location</span><span class="st st-sg">&#39;</span>),
+ <span class="name">validator</span><span class="op">=</span><span class="name">checkTextLength</span>(<span class="nb nb-int">0</span>, <span class="nb nb-int">255</span>),
+ ),
+ <span class="name">TextArea</span>(<span class="st st-sg">&#39;</span><span class="st">interests</span><span class="st st-sg">&#39;</span>,
+ <span class="name">default</span><span class="op">=</span><span class="name">get_setting</span>(<span class="st st-sg">&#39;</span><span class="st">interests</span><span class="st st-sg">&#39;</span>),
+ <span class="name">validator</span><span class="op">=</span><span class="name">checkTextLength</span>(<span class="nb nb-int">0</span>, <span class="nb nb-int">512</span>)
+ )
+ )
+ <span class="kw">if</span> <span class="name">req</span>.<span class="name">method</span> <span class="op">==</span> <span class="st st-sg">&#39;</span><span class="st">POST</span><span class="st st-sg">&#39;</span>:
+ <span class="name">form</span>.<span class="name">update</span>(<span class="name">req</span>.<span class="name">form</span>, <span class="name">prefix</span><span class="op">=</span><span class="st st-sg">&#39;</span><span class="st">f_</span><span class="st st-sg">&#39;</span>)
+ <span class="kw">if</span> <span class="op op-word">not</span> <span class="name">form</span>.<span class="name">has_errors</span>:
+ <span class="name">d</span> <span class="op">=</span> <span class="name">form</span>.<span class="name">to_dict</span>()
+ <span class="cm"># set special setting values</span>
+
+ <span class="kw">if</span> <span class="name">d</span>[<span class="st st-sg">&#39;</span><span class="st">new_password</span><span class="st st-sg">&#39;</span>]:
+ <span class="name">req</span>.<span class="name">user</span>.<span class="name">set_password</span>(<span class="name">d</span>.<span class="name">pop</span>(<span class="st st-sg">&#39;</span><span class="st">new_password</span><span class="st st-sg">&#39;</span>))
+ <span class="kw">else</span>:
+ <span class="kw">del</span> <span class="name">d</span>[<span class="st st-sg">&#39;</span><span class="st">new_password</span><span class="st st-sg">&#39;</span>]
+ <span class="kw">del</span> <span class="name">d</span>[<span class="st st-sg">&#39;</span><span class="st">new_password2</span><span class="st st-sg">&#39;</span>]
+ <span class="name">req</span>.<span class="name">user</span>.<span class="name">email</span> <span class="op">=</span> <span class="name">d</span>.<span class="name">pop</span>(<span class="st st-sg">&#39;</span><span class="st">email</span><span class="st st-sg">&#39;</span>)
+ <span class="name">req</span>.<span class="name">user</span>.<span class="name">settings</span>.<span class="name">update</span>({<span class="st st-sg">&#39;</span><span class="st">show_email</span><span class="st st-sg">&#39;</span>: <span class="name">d</span>.<span class="name">pop</span>(<span class="st st-sg">&#39;</span><span class="st">show_email</span><span class="st st-sg">&#39;</span>)})
+ <span class="cm"># update other profile fields</span>
+
+ <span class="name">req</span>.<span class="name">user</span>.<span class="name">profile</span>.<span class="name">update</span>(<span class="name">d</span>)
+ <span class="name">req</span>.<span class="name">user</span>.<span class="name">save</span>()
+ <span class="name">msg</span> <span class="op">=</span> <span class="name">_</span>(<span class="st st-sg">&#39;</span><span class="st">Settings saved</span><span class="st st-sg">&#39;</span>)
+ <span class="kw">return</span> <span class="st st-sg">&#39;</span><span class="st">settings/profile.html</span><span class="st st-sg">&#39;</span>, {
+ <span class="st st-sg">&#39;</span><span class="st">form</span><span class="st st-sg">&#39;</span>: <span class="name">form</span>.<span class="name">generate</span>(<span class="name">prefix</span><span class="op">=</span><span class="st st-sg">&#39;</span><span class="st">f_</span><span class="st st-sg">&#39;</span>),
+ <span class="st st-sg">&#39;</span><span class="st">msg</span><span class="st st-sg">&#39;</span>: <span class="name">msg</span>
+
+ }
+
+
+<span class="kw">class </span><span class="cls">AvatarSettings</span>(<span class="name">UserSettingsPage</span>):
+ <span class="st st-db">&quot;&quot;&quot;</span><span class="st">
+ This page allows the user to update his avatar
+ </span><span class="st st-db">&quot;&quot;&quot;</span>
+ <span class="name">settings_page_identifier</span> <span class="op">=</span> <span class="st st-sg">&#39;</span><span class="st">avatar</span><span class="st st-sg">&#39;</span>
+
+ <span class="kw">def </span><span class="fun">get_settings_link_title</span>(<span class="bn bn-pseudo">self</span>, <span class="name">req</span>):
+ <span class="kw">if</span> <span class="name">req</span>.<span class="name">ctx</span>.<span class="name">cfg</span>.<span class="name">get_bool</span>(<span class="st st-sg">&#39;</span><span class="st">board</span><span class="st st-sg">&#39;</span>, <span class="st st-sg">&#39;</span><span class="st">allow_avatars</span><span class="st st-sg">&#39;</span>, <span class="bn bn-pseudo">True</span>):
+ <span class="name">_</span> <span class="op">=</span> <span class="name">req</span>.<span class="name">gettext</span>
+
+ <span class="kw">return</span> <span class="name">_</span>(<span class="st st-sg">&#39;</span><span class="st">Avatar</span><span class="st st-sg">&#39;</span>)
+
+ <span class="kw">def </span><span class="fun">get_settings_page</span>(<span class="bn bn-pseudo">self</span>, <span class="name">req</span>):
+ <span class="kw">if</span> <span class="op op-word">not</span> <span class="name">req</span>.<span class="name">ctx</span>.<span class="name">cfg</span>.<span class="name">get_bool</span>(<span class="st st-sg">&#39;</span><span class="st">board</span><span class="st st-sg">&#39;</span>, <span class="st st-sg">&#39;</span><span class="st">allow_avatars</span><span class="st st-sg">&#39;</span>, <span class="bn bn-pseudo">True</span>):
+ <span class="kw">return</span> <span class="name">PageNotFound</span>()
+ <span class="name">_</span> <span class="op">=</span> <span class="name">req</span>.<span class="name">gettext</span>
+
+ <span class="name">msg</span> <span class="op">=</span> <span class="bn bn-pseudo">None</span>
+
+ <span class="kw">def </span><span class="fun">make_small_thumbnail</span>(<span class="name">value</span>):
+ <span class="kw">if</span> <span class="op op-word">not</span> <span class="name">value</span>:
+ <span class="kw">return</span>
+
+ <span class="name">dim</span> <span class="op">=</span> <span class="bn bn-pseudo">self</span>.<span class="name">ctx</span>.<span class="name">cfg</span>.<span class="name">get_int</span>(<span class="st st-sg">&#39;</span><span class="st">board</span><span class="st st-sg">&#39;</span>, <span class="st st-sg">&#39;</span><span class="st">avatar_dimension</span><span class="st st-sg">&#39;</span>, <span class="nb nb-int">80</span>)
+ <span class="kw">return</span> <span class="name">resize_image</span>(<span class="name">value</span>, <span class="name">dim</span>, <span class="name">dim</span>, <span class="st st-sg">&#39;</span><span class="st">image/png</span><span class="st st-sg">&#39;</span>)
+
+ <span class="name">form</span> <span class="op">=</span> <span class="name">Form</span>(<span class="name">req</span>, <span class="bn bn-pseudo">self</span>, <span class="st st-sg">&#39;</span><span class="st">POST</span><span class="st st-sg">&#39;</span>,
+ <span class="name">FileField</span>(<span class="st st-sg">&#39;</span><span class="st">avatar</span><span class="st st-sg">&#39;</span>,
+ <span class="name">validator</span><span class="op">=</span><span class="name">mayEmpty</span>(<span class="name">isSupportedImage</span>()),
+ <span class="name">manipulator</span><span class="op">=</span><span class="name">make_small_thumbnail</span>
+
+ ),
+ <span class="name">CheckBox</span>(<span class="st st-sg">&#39;</span><span class="st">delete_avatar</span><span class="st st-sg">&#39;</span>)
+ )
+ <span class="name">avatar</span> <span class="op">=</span> <span class="bn bn-pseudo">None</span>
+ <span class="kw">if</span> <span class="name">req</span>.<span class="name">method</span> <span class="op">==</span> <span class="st st-sg">&#39;</span><span class="st">POST</span><span class="st st-sg">&#39;</span>:
+ <span class="name">form</span>.<span class="name">update</span>(<span class="name">req</span>.<span class="name">files</span>, <span class="name">prefix</span><span class="op">=</span><span class="st st-sg">&#39;</span><span class="st">f_</span><span class="st st-sg">&#39;</span>)
+ <span class="name">form</span>.<span class="name">update</span>(<span class="name">req</span>.<span class="name">form</span>, <span class="name">prefix</span><span class="op">=</span><span class="st st-sg">&#39;</span><span class="st">f_</span><span class="st st-sg">&#39;</span>)
+ <span class="kw">if</span> <span class="op op-word">not</span> <span class="name">form</span>.<span class="name">has_errors</span>:
+ <span class="name">d</span> <span class="op">=</span> <span class="name">form</span>.<span class="name">to_dict</span>()
+ <span class="name">uid</span> <span class="op">=</span> <span class="name">req</span>.<span class="name">user</span>.<span class="name">user_id</span>
+
+ <span class="name">fn</span> <span class="op">=</span> <span class="name">path</span>.<span class="name">join</span>(<span class="bn bn-pseudo">self</span>.<span class="name">ctx</span>.<span class="name">cfg</span>.<span class="name">root</span>, <span class="st st-sg">&#39;</span><span class="st">avatars</span><span class="st st-sg">&#39;</span>, <span class="st st-sg">&#39;</span><span class="st st-int">%d</span><span class="st">.png</span><span class="st st-sg">&#39;</span> <span class="op">%</span> <span class="name">uid</span>)
+ <span class="kw">if</span> <span class="name">d</span>[<span class="st st-sg">&#39;</span><span class="st">delete_avatar</span><span class="st st-sg">&#39;</span>]:
+ <span class="name">req</span>.<span class="name">user</span>.<span class="name">profile</span>[<span class="st st-sg">&#39;</span><span class="st">avatar</span><span class="st st-sg">&#39;</span>] <span class="op">=</span> <span class="bn bn-pseudo">None</span>
+
+ <span class="kw">if</span> <span class="name">path</span>.<span class="name">exists</span>(<span class="name">fn</span>):
+ <span class="name">os</span>.<span class="name">unlink</span>(<span class="name">fn</span>)
+ <span class="kw">elif</span> <span class="name">d</span>[<span class="st st-sg">&#39;</span><span class="st">avatar</span><span class="st st-sg">&#39;</span>]:
+ <span class="name">uid</span> <span class="op">=</span> <span class="name">req</span>.<span class="name">user</span>.<span class="name">user_id</span>
+
+ <span class="name">f</span> <span class="op">=</span> <span class="bn">file</span>(<span class="name">fn</span>, <span class="st st-sg">&#39;</span><span class="st">wb</span><span class="st st-sg">&#39;</span>)
+ <span class="name">f</span>.<span class="name">write</span>(<span class="name">d</span>[<span class="st st-sg">&#39;</span><span class="st">avatar</span><span class="st st-sg">&#39;</span>])
+ <span class="name">f</span>.<span class="name">close</span>()
+ <span class="name">req</span>.<span class="name">user</span>.<span class="name">profile</span>[<span class="st st-sg">&#39;</span><span class="st">avatar</span><span class="st st-sg">&#39;</span>] <span class="op">=</span> <span class="bn bn-pseudo">self</span>.<span class="name">ctx</span>.<span class="name">make_url</span>(
+ <span class="st st-sg">&#39;</span><span class="st">users</span><span class="st st-sg">&#39;</span>, <span class="name">urlencode</span>(<span class="name">req</span>.<span class="name">user</span>.<span class="name">username</span>), <span class="name">show</span><span class="op">=</span><span class="st st-sg">&#39;</span><span class="st">avatar</span><span class="st st-sg">&#39;</span>)
+ <span class="name">req</span>.<span class="name">user</span>.<span class="name">save</span>()
+ <span class="name">msg</span> <span class="op">=</span> <span class="name">_</span>(<span class="st st-sg">&#39;</span><span class="st">Settings saved</span><span class="st st-sg">&#39;</span>)
+ <span class="cm"># TODO: support for linked avatars and gravatars</span>
+
+ <span class="kw">elif</span> <span class="name">req</span>.<span class="name">user</span>.<span class="name">profile</span>.<span class="name">get</span>(<span class="st st-sg">&#39;</span><span class="st">avatar</span><span class="st st-sg">&#39;</span>):
+ <span class="name">avatar</span> <span class="op">=</span> <span class="bn bn-pseudo">self</span>.<span class="name">ctx</span>.<span class="name">make_url</span>(<span class="st st-sg">&#39;</span><span class="st">users</span><span class="st st-sg">&#39;</span>, <span class="name">urlencode</span>(<span class="name">req</span>.<span class="name">user</span>.<span class="name">username</span>),
+ <span class="name">show</span><span class="op">=</span><span class="st st-sg">&#39;</span><span class="st">avatar</span><span class="st st-sg">&#39;</span>)
+ <span class="kw">return</span> <span class="st st-sg">&#39;</span><span class="st">settings/avatar.html</span><span class="st st-sg">&#39;</span>, {
+ <span class="st st-sg">&#39;</span><span class="st">form</span><span class="st st-sg">&#39;</span>: <span class="name">form</span>.<span class="name">generate</span>(<span class="name">prefix</span><span class="op">=</span><span class="st st-sg">&#39;</span><span class="st">f_</span><span class="st st-sg">&#39;</span>),
+ <span class="st st-sg">&#39;</span><span class="st">avatar</span><span class="st st-sg">&#39;</span>: <span class="name">avatar</span>,
+ <span class="st st-sg">&#39;</span><span class="st">msg</span><span class="st st-sg">&#39;</span>: <span class="name">msg</span>
+
+ }
+
+
+<span class="kw">class </span><span class="cls">UserForumSettings</span>(<span class="name">UserSettingsPage</span>):
+ <span class="st st-db">&quot;&quot;&quot;</span><span class="st">
+ This page allows the user to update his forum view settings
+ (ie. view mode and posts and threads per page)
+ </span><span class="st st-db">&quot;&quot;&quot;</span>
+
+ <span class="name">settings_page_identifier</span> <span class="op">=</span> <span class="st st-sg">&#39;</span><span class="st">forum</span><span class="st st-sg">&#39;</span>
+
+ <span class="kw">def </span><span class="fun">get_settings_link_title</span>(<span class="bn bn-pseudo">self</span>, <span class="name">req</span>):
+ <span class="name">_</span> <span class="op">=</span> <span class="name">req</span>.<span class="name">gettext</span>
+
+ <span class="kw">return</span> <span class="name">_</span>(<span class="st st-sg">&#39;</span><span class="st">Forum Settings</span><span class="st st-sg">&#39;</span>)
+
+ <span class="kw">def </span><span class="fun">get_settings_page</span>(<span class="bn bn-pseudo">self</span>, <span class="name">req</span>):
+ <span class="kw">def </span><span class="fun">int_or_none</span>(<span class="name">x</span>):
+ <span class="st st-db">&quot;</span><span class="st">manipulator which returns an int or None</span><span class="st st-db">&quot;</span>
+
+ <span class="kw">try</span>:
+ <span class="kw">return</span> <span class="bn">int</span>(<span class="name">x</span>)
+ <span class="kw">except</span> <span class="exc">ValueError</span>:
+ <span class="kw">return</span> <span class="bn bn-pseudo">None</span>
+
+ <span class="name">_</span> <span class="op">=</span> <span class="name">req</span>.<span class="name">gettext</span>
+ <span class="name">msg</span> <span class="op">=</span> <span class="bn bn-pseudo">None</span>
+
+ <span class="name">form</span> <span class="op">=</span> <span class="name">Form</span>(<span class="name">req</span>, <span class="bn bn-pseudo">self</span>, <span class="st st-sg">&#39;</span><span class="st">POST</span><span class="st st-sg">&#39;</span>,
+ <span class="name">TextField</span>(<span class="st st-sg">&#39;</span><span class="st">posts_per_page</span><span class="st st-sg">&#39;</span>,
+ <span class="name">default</span><span class="op">=</span><span class="name">req</span>.<span class="name">user</span>.<span class="name">settings</span>.<span class="name">get</span>(<span class="st st-sg">&#39;</span><span class="st">posts_per_page</span><span class="st st-sg">&#39;</span>) <span class="op op-word">or</span> <span class="name">u</span><span class="st st-sg">&#39;&#39;</span>,
+ <span class="name">validator</span><span class="op">=</span><span class="name">mayEmpty</span>(<span class="name">doMultiCheck</span>(<span class="name">isInteger</span>(), <span class="name">isInRange</span>(<span class="nb nb-int">5</span>, <span class="nb nb-int">50</span>))),
+ <span class="name">manipulator</span><span class="op">=</span><span class="name">int_or_none</span>
+
+ ),
+ <span class="name">TextField</span>(<span class="st st-sg">&#39;</span><span class="st">threads_per_page</span><span class="st st-sg">&#39;</span>,
+ <span class="name">default</span><span class="op">=</span><span class="name">req</span>.<span class="name">user</span>.<span class="name">settings</span>.<span class="name">get</span>(<span class="st st-sg">&#39;</span><span class="st">threads_per_page</span><span class="st st-sg">&#39;</span>) <span class="op op-word">or</span> <span class="name">u</span><span class="st st-sg">&#39;&#39;</span>,
+ <span class="name">validator</span><span class="op">=</span><span class="name">mayEmpty</span>(<span class="name">doMultiCheck</span>(<span class="name">isInteger</span>(), <span class="name">isInRange</span>(<span class="nb nb-int">10</span>, <span class="nb nb-int">80</span>))),
+ <span class="name">manipulator</span><span class="op">=</span><span class="name">int_or_none</span>
+
+ ),
+ <span class="name">SelectBox</span>(<span class="st st-sg">&#39;</span><span class="st">view_mode</span><span class="st st-sg">&#39;</span>, [
+ (<span class="st st-sg">&#39;&#39;</span>, <span class="name">_</span>(<span class="st st-sg">&#39;</span><span class="st">default</span><span class="st st-sg">&#39;</span>)),
+ (<span class="st st-sg">&#39;</span><span class="st">threaded</span><span class="st st-sg">&#39;</span>, <span class="name">_</span>(<span class="st st-sg">&#39;</span><span class="st">threaded</span><span class="st st-sg">&#39;</span>)),
+ (<span class="st st-sg">&#39;</span><span class="st">flat</span><span class="st st-sg">&#39;</span>, <span class="name">_</span>(<span class="st st-sg">&#39;</span><span class="st">flat</span><span class="st st-sg">&#39;</span>))
+ ], <span class="name">default</span><span class="op">=</span><span class="name">req</span>.<span class="name">user</span>.<span class="name">settings</span>.<span class="name">get</span>(<span class="st st-sg">&#39;</span><span class="st">view_mode</span><span class="st st-sg">&#39;</span>) <span class="op op-word">or</span> <span class="name">u</span><span class="st st-sg">&#39;&#39;</span>
+
+ )
+ )
+
+ <span class="kw">if</span> <span class="name">req</span>.<span class="name">method</span> <span class="op">==</span> <span class="st st-sg">&#39;</span><span class="st">POST</span><span class="st st-sg">&#39;</span>:
+ <span class="name">form</span>.<span class="name">update</span>(<span class="name">req</span>.<span class="name">form</span>, <span class="name">prefix</span><span class="op">=</span><span class="st st-sg">&#39;</span><span class="st">f_</span><span class="st st-sg">&#39;</span>)
+ <span class="kw">if</span> <span class="op op-word">not</span> <span class="name">form</span>.<span class="name">has_errors</span>:
+ <span class="name">d</span> <span class="op">=</span> <span class="name">form</span>.<span class="name">to_dict</span>()
+ <span class="name">req</span>.<span class="name">user</span>.<span class="name">settings</span>.<span class="name">update</span>(<span class="name">d</span>)
+ <span class="name">req</span>.<span class="name">user</span>.<span class="name">save</span>()
+ <span class="name">msg</span> <span class="op">=</span> <span class="name">_</span>(<span class="st st-sg">&#39;</span><span class="st">Forum settings saved</span><span class="st st-sg">&#39;</span>)
+
+ <span class="kw">return</span> <span class="st st-sg">&#39;</span><span class="st">settings/forumsettings.html</span><span class="st st-sg">&#39;</span>, {
+ <span class="st st-sg">&#39;</span><span class="st">form</span><span class="st st-sg">&#39;</span>: <span class="name">form</span>.<span class="name">generate</span>(<span class="name">prefix</span><span class="op">=</span><span class="st st-sg">&#39;</span><span class="st">f_</span><span class="st st-sg">&#39;</span>),
+ <span class="st st-sg">&#39;</span><span class="st">msg</span><span class="st st-sg">&#39;</span>: <span class="name">msg</span>
+
+ }
+<span class="cm"># -*- coding: utf-8 -*-</span>
+<span class="st st-db">&quot;&quot;&quot;</span><span class="st">
+ pocoo.pkg.core.validators
+ ~~~~~~~~~~~~~~~~~~~~~~~~~
+
+ Special validation, in addition to `pocoo.utils.validators`.
+
+ For a general explanation of the validators system, please
+ see `pocoo.utils.form`.
+
+
+ :copyright: 2006 by Armin Ronacher.
+ :license: GNU GPL, see LICENSE for more details.
+</span><span class="st st-db">&quot;&quot;&quot;</span>
+<span class="kw">import </span><span class="cls">re</span>
+<span class="kw">import </span><span class="cls">unicodedata</span>
+<span class="kw">from </span><span class="cls">pocoo.utils.validators</span><span class="kw"> import</span> <span class="name">ValidationError</span>, <span class="name">_mail_re</span>
+
+<span class="kw">from </span><span class="cls">pocoo.pkg.core.user</span><span class="kw"> import</span> <span class="name">User</span>, <span class="name">get_id_by_name</span>
+
+<span class="name">_icq_re</span> <span class="op">=</span> <span class="name">re</span>.<span class="name">compile</span>(<span class="st st-sg">r&#39;</span><span class="st">^\d{6,9}$</span><span class="st st-sg">&#39;</span>)
+
+
+
+<span class="cm"># pylint: disable-msg=C0103</span>
+
+<span class="kw">def </span><span class="fun">isValidUsername</span>():
+ <span class="st st-db">&quot;&quot;&quot;</span><span class="st">Checks if the given string looks like a valid username.</span><span class="st st-db">&quot;&quot;&quot;</span>
+ <span class="kw">def </span><span class="fun">is_valid_username</span>(<span class="name">field</span>, <span class="name">form</span>):
+ <span class="name">errors</span> <span class="op">=</span> []
+ <span class="name">_</span> <span class="op">=</span> <span class="kw">lambda</span> <span class="name">s</span>: <span class="name">errors</span>.<span class="name">append</span>(<span class="name">form</span>.<span class="name">req</span>.<span class="name">gettext</span>(<span class="name">s</span>))
+ <span class="name">value</span> <span class="op">=</span> <span class="name">field</span>.<span class="name">value</span>.<span class="name">strip</span>()
+ <span class="kw">if</span> <span class="bn">len</span>(<span class="name">value</span>) <span class="op">&lt;</span> <span class="nb nb-int">2</span>:
+ <span class="name">_</span>(<span class="st st-sg">&#39;</span><span class="st">Please enter a username that is at least 2 characters long.</span><span class="st st-sg">&#39;</span>)
+ <span class="kw">if</span> <span class="bn">len</span>(<span class="name">value</span>) <span class="op">&gt;</span> <span class="nb nb-int">30</span>:
+ <span class="name">_</span>(<span class="st st-sg">&#39;</span><span class="st">Please enter a username that is no longer than 30 characters.</span><span class="st st-sg">&#39;</span>)
+ <span class="kw">for</span> <span class="name">char</span> <span class="op op-word">in</span> <span class="name">value</span>:
+ <span class="kw">if</span> <span class="name">char</span> <span class="op op-word">in</span> <span class="st st-sg">&#39;</span><span class="st">_-. </span><span class="st st-sg">&#39;</span>:
+ <span class="kw">continue</span>
+
+ <span class="kw">if</span> <span class="name">unicodedata</span>.<span class="name">category</span>(<span class="name">char</span>)[<span class="nb nb-int">0</span>] <span class="op op-word">not</span> <span class="op op-word">in</span> <span class="st st-sg">&#39;</span><span class="st">LN</span><span class="st st-sg">&#39;</span>:
+ <span class="name">_</span>(<span class="st st-sg">&#39;</span><span class="st">Please enter a username without special characters </span><span class="st st-sg">&#39;</span>
+
+ <span class="st st-sg">&#39;</span><span class="st">except &quot;_&quot;, &quot;-&quot;, &quot;.&quot;.</span><span class="st st-sg">&#39;</span>)
+ <span class="kw">break</span>
+ <span class="kw">if</span> <span class="name">errors</span>:
+ <span class="kw">raise</span> <span class="name">ValidationError</span>(<span class="op">*</span><span class="name">errors</span>)
+ <span class="kw">return</span> <span class="name">is_valid_username</span>
+
+
+<span class="kw">def </span><span class="fun">isAvailableUsername</span>():
+ <span class="st st-db">&quot;&quot;&quot;</span><span class="st">Checks if the username is valid and available.</span><span class="st st-db">&quot;&quot;&quot;</span>
+ <span class="kw">def </span><span class="fun">is_available_username</span>(<span class="name">field</span>, <span class="name">form</span>):
+ <span class="name">isValidUsername</span>()(<span class="name">field</span>, <span class="name">form</span>)
+ <span class="kw">try</span>:
+ <span class="name">get_id_by_name</span>(<span class="name">form</span>.<span class="name">req</span>.<span class="name">ctx</span>, <span class="name">field</span>.<span class="name">value</span>)
+ <span class="kw">except</span> <span class="name">User</span>.<span class="name">NotFound</span>:
+ <span class="kw">return</span>
+
+ <span class="name">_</span> <span class="op">=</span> <span class="name">form</span>.<span class="name">req</span>.<span class="name">gettext</span>
+ <span class="kw">raise</span> <span class="name">ValidationError</span>(<span class="name">_</span>(<span class="st st-sg">&#39;</span><span class="st">The username is already in use.</span><span class="st st-sg">&#39;</span>))
+ <span class="kw">return</span> <span class="name">is_available_username</span>
+
+
+<span class="kw">def </span><span class="fun">isAnonymousUsername</span>():
+ <span class="st st-db">&quot;&quot;&quot;</span><span class="st">Checks if this is a valid username for anonymous usage.</span><span class="st st-db">&quot;&quot;&quot;</span>
+ <span class="kw">def </span><span class="fun">is_anonymous_username</span>(<span class="name">field</span>, <span class="name">form</span>):
+ <span class="kw">if</span> <span class="name">field</span>.<span class="name">value</span> <span class="op">!=</span> <span class="st st-sg">&#39;</span><span class="st">anonymous</span><span class="st st-sg">&#39;</span>:
+ <span class="name">isAvailableUsername</span>()(<span class="name">field</span>, <span class="name">form</span>)
+ <span class="kw">return</span> <span class="name">is_anonymous_username</span>
+
+
+<span class="kw">def </span><span class="fun">isExistingUsername</span>():
+ <span class="st st-db">&quot;&quot;&quot;</span><span class="st">Checks if the username does exist.</span><span class="st st-db">&quot;&quot;&quot;</span>
+ <span class="kw">def </span><span class="fun">is_existing_username</span>(<span class="name">field</span>, <span class="name">form</span>):
+ <span class="kw">try</span>:
+ <span class="name">get_id_by_name</span>(<span class="name">form</span>.<span class="name">req</span>.<span class="name">ctx</span>, <span class="name">field</span>.<span class="name">value</span>)
+ <span class="kw">return</span>
+
+ <span class="kw">except</span> <span class="name">User</span>.<span class="name">NotFound</span>:
+ <span class="name">_</span> <span class="op">=</span> <span class="name">form</span>.<span class="name">req</span>.<span class="name">gettext</span>
+
+ <span class="kw">raise</span> <span class="name">ValidationError</span>(<span class="name">_</span>(<span class="st st-sg">&#39;</span><span class="st">The user </span><span class="st st-int">%s</span><span class="st"> does not exist.</span><span class="st st-sg">&#39;</span>) <span class="op">%</span> <span class="name">field</span>.<span class="name">value</span>)
+ <span class="kw">return</span> <span class="name">is_existing_username</span>
+
+
+<span class="kw">def </span><span class="fun">isStrongPassword</span>(<span class="name">strength</span><span class="op">=</span><span class="bn bn-pseudo">None</span>):
+ <span class="st st-db">&quot;&quot;&quot;</span><span class="st">Checks if the password is strong enough</span><span class="st st-db">&quot;&quot;&quot;</span>
+ <span class="kw">def </span><span class="fun">is_strong_password</span>(<span class="name">field</span>, <span class="name">form</span>):
+ <span class="name">errors</span> <span class="op">=</span> []
+ <span class="name">_</span> <span class="op">=</span> <span class="kw">lambda</span> <span class="name">s</span>: <span class="name">errors</span>.<span class="name">append</span>(<span class="name">form</span>.<span class="name">req</span>.<span class="name">gettext</span>(<span class="name">s</span>))
+ <span class="kw">if</span> <span class="name">strength</span> <span class="op op-word">is</span> <span class="bn bn-pseudo">None</span>:
+ <span class="name">s</span> <span class="op">=</span> <span class="name">form</span>.<span class="name">req</span>.<span class="name">ctx</span>.<span class="name">cfg</span>.<span class="name">get_int</span>(<span class="st st-sg">&#39;</span><span class="st">security</span><span class="st st-sg">&#39;</span>, <span class="st st-sg">&#39;</span><span class="st">password_strength</span><span class="st st-sg">&#39;</span>, <span class="nb nb-int">3</span>)
+ <span class="kw">else</span>:
+ <span class="name">s</span> <span class="op">=</span> <span class="name">strength</span>
+
+ <span class="name">s</span> <span class="op">=</span> <span class="bn">max</span>(<span class="nb nb-int">0</span>, <span class="bn">min</span>(<span class="nb nb-int">4</span>, <span class="name">s</span>))
+ <span class="kw">if</span> <span class="op op-word">not</span> <span class="name">s</span> <span class="op op-word">and</span> <span class="op op-word">not</span> <span class="name">field</span>.<span class="name">value</span>:
+ <span class="name">_</span>(<span class="st st-sg">&#39;</span><span class="st">Please fill out the password field.</span><span class="st st-sg">&#39;</span>)
+ <span class="kw">elif</span> <span class="name">s</span> <span class="op">==</span> <span class="nb nb-int">1</span>:
+ <span class="kw">if</span> <span class="bn">len</span>(<span class="name">field</span>.<span class="name">value</span>) <span class="op">&lt;</span> <span class="nb nb-int">4</span>:
+ <span class="name">_</span>(<span class="st st-sg">&#39;</span><span class="st">Please enter a password with at least 4 characters.</span><span class="st st-sg">&#39;</span>)
+ <span class="kw">elif</span> <span class="name">s</span> <span class="op">==</span> <span class="nb nb-int">2</span>:
+ <span class="kw">if</span> <span class="bn">len</span>(<span class="name">field</span>.<span class="name">value</span>) <span class="op">&lt;</span> <span class="nb nb-int">6</span>:
+ <span class="name">_</span>(<span class="st st-sg">&#39;</span><span class="st">Please enter a password with at least 6 characters.</span><span class="st st-sg">&#39;</span>)
+ <span class="kw">elif</span> <span class="name">s</span> <span class="op">==</span> <span class="nb nb-int">3</span>:
+ <span class="kw">def </span><span class="fun">test</span>():
+ <span class="name">have_letter</span> <span class="op">=</span> <span class="name">have_number</span> <span class="op">=</span> <span class="bn bn-pseudo">False</span>
+
+ <span class="kw">for</span> <span class="name">char</span> <span class="op op-word">in</span> <span class="name">field</span>.<span class="name">value</span>:
+ <span class="name">c</span> <span class="op">=</span> <span class="name">unicodedata</span>.<span class="name">category</span>(<span class="name">char</span>)[<span class="nb nb-int">0</span>]
+ <span class="kw">if</span> <span class="name">c</span> <span class="op">==</span> <span class="st st-sg">&#39;</span><span class="st">L</span><span class="st st-sg">&#39;</span>:
+ <span class="name">have_letter</span> <span class="op">=</span> <span class="bn bn-pseudo">True</span>
+
+ <span class="kw">elif</span> <span class="name">c</span> <span class="op">==</span> <span class="st st-sg">&#39;</span><span class="st">N</span><span class="st st-sg">&#39;</span>:
+ <span class="name">have_number</span> <span class="op">=</span> <span class="bn bn-pseudo">True</span>
+ <span class="kw">if</span> <span class="name">have_letter</span> <span class="op op-word">and</span> <span class="name">have_number</span>:
+ <span class="kw">return</span> <span class="bn bn-pseudo">True</span>
+
+ <span class="kw">return</span> <span class="bn bn-pseudo">False</span>
+ <span class="kw">if</span> <span class="bn">len</span>(<span class="name">field</span>.<span class="name">value</span>) <span class="op">&lt;</span> <span class="nb nb-int">6</span> <span class="op op-word">or</span> <span class="op op-word">not</span> <span class="name">test</span>():
+ <span class="name">_</span>(<span class="st st-sg">&#39;</span><span class="st">Please enter a password with at least 6 characters which </span><span class="st st-sg">&#39;</span>
+
+ <span class="st st-sg">&#39;</span><span class="st">contains both letters and numbers.</span><span class="st st-sg">&#39;</span>)
+ <span class="kw">elif</span> <span class="name">s</span> <span class="op">==</span> <span class="nb nb-int">4</span>:
+ <span class="kw">def </span><span class="fun">test</span>():
+ <span class="name">have_letter</span> <span class="op">=</span> <span class="name">have_number</span>, <span class="name">have_special</span> <span class="op">=</span> <span class="bn bn-pseudo">False</span>
+
+ <span class="kw">for</span> <span class="name">char</span> <span class="op op-word">in</span> <span class="name">field</span>.<span class="name">value</span>:
+ <span class="name">c</span> <span class="op">=</span> <span class="name">unicodedata</span>.<span class="name">category</span>(<span class="name">char</span>)[<span class="nb nb-int">0</span>]
+ <span class="kw">if</span> <span class="name">c</span> <span class="op">==</span> <span class="st st-sg">&#39;</span><span class="st">L</span><span class="st st-sg">&#39;</span>:
+ <span class="name">have_letter</span> <span class="op">=</span> <span class="bn bn-pseudo">True</span>
+
+ <span class="kw">elif</span> <span class="name">c</span> <span class="op">==</span> <span class="st st-sg">&#39;</span><span class="st">N</span><span class="st st-sg">&#39;</span>:
+ <span class="name">have_number</span> <span class="op">=</span> <span class="bn bn-pseudo">True</span>
+ <span class="kw">elif</span> <span class="name">c</span> <span class="op">==</span> <span class="st st-sg">&#39;</span><span class="st">S</span><span class="st st-sg">&#39;</span>:
+ <span class="name">have_special</span> <span class="op">=</span> <span class="bn bn-pseudo">True</span>
+
+ <span class="kw">if</span> <span class="name">have_letter</span> <span class="op op-word">and</span> <span class="name">have_number</span> <span class="op op-word">and</span> <span class="name">have_special</span>:
+ <span class="kw">return</span> <span class="bn bn-pseudo">True</span>
+
+ <span class="kw">return</span> <span class="bn bn-pseudo">False</span>
+ <span class="kw">if</span> <span class="bn">len</span>(<span class="name">field</span>.<span class="name">value</span>) <span class="op">&lt;</span> <span class="nb nb-int">6</span> <span class="op op-word">or</span> <span class="op op-word">not</span> <span class="name">test</span>():
+ <span class="name">_</span>(<span class="st st-sg">&#39;</span><span class="st">Please enter a password with at least 6 characters with </span><span class="st st-sg">&#39;</span>
+
+ <span class="st st-sg">&#39;</span><span class="st">contains of letters, numbers and at least one special </span><span class="st st-sg">&#39;</span>
+ <span class="st st-sg">&#39;</span><span class="st">character.</span><span class="st st-sg">&#39;</span>)
+ <span class="kw">if</span> <span class="name">errors</span>:
+ <span class="kw">raise</span> <span class="name">ValidationError</span>(<span class="op">*</span><span class="name">errors</span>)
+ <span class="kw">return</span> <span class="name">is_strong_password</span>
+
+
+
+<span class="kw">def </span><span class="fun">isValidSignature</span>():
+ <span class="st st-db">&quot;&quot;&quot;</span><span class="st">Checks if the signature is valid.</span><span class="st st-db">&quot;&quot;&quot;</span>
+ <span class="kw">def </span><span class="fun">is_valid_signature</span>(<span class="name">field</span>, <span class="name">form</span>):
+ <span class="name">errors</span> <span class="op">=</span> []
+ <span class="name">_</span> <span class="op">=</span> <span class="kw">lambda</span> <span class="name">s</span>: <span class="name">errors</span>.<span class="name">append</span>(<span class="name">form</span>.<span class="name">req</span>.<span class="name">gettext</span>(<span class="name">s</span>))
+ <span class="name">val</span> <span class="op">=</span> <span class="name">field</span>.<span class="name">value</span>.<span class="name">strip</span>()
+ <span class="name">max_len</span> <span class="op">=</span> <span class="name">form</span>.<span class="name">req</span>.<span class="name">ctx</span>.<span class="name">cfg</span>.<span class="name">get_int</span>(<span class="st st-sg">&#39;</span><span class="st">board</span><span class="st st-sg">&#39;</span>, <span class="st st-sg">&#39;</span><span class="st">signature_length</span><span class="st st-sg">&#39;</span>)
+ <span class="name">max_lines</span> <span class="op">=</span> <span class="name">form</span>.<span class="name">req</span>.<span class="name">ctx</span>.<span class="name">cfg</span>.<span class="name">get_int</span>(<span class="st st-sg">&#39;</span><span class="st">board</span><span class="st st-sg">&#39;</span>, <span class="st st-sg">&#39;</span><span class="st">signature_lines</span><span class="st st-sg">&#39;</span>)
+ <span class="kw">if</span> <span class="bn">len</span>(<span class="name">val</span>) <span class="op">&gt;</span> <span class="name">max_len</span>:
+ <span class="name">_</span>(<span class="st st-sg">&#39;</span><span class="st">Your signature must not be longer than </span><span class="st st-int">%d</span><span class="st"> characters.</span><span class="st st-sg">&#39;</span>) <span class="op">%</span> <span class="name">max_len</span>
+
+ <span class="kw">if</span> <span class="bn">len</span>(<span class="name">val</span>.<span class="name">splitlines</span>()) <span class="op">&gt;</span> <span class="name">max_lines</span>:
+ <span class="name">_</span>(<span class="st st-sg">&#39;</span><span class="st">Your signature must not be longer than </span><span class="st st-int">%d</span><span class="st"> lines.</span><span class="st st-sg">&#39;</span>) <span class="op">%</span> <span class="name">max_lines</span>
+
+ <span class="kw">if</span> <span class="name">errors</span>:
+ <span class="kw">raise</span> <span class="name">ValidationError</span>(<span class="op">*</span><span class="name">errors</span>)
+ <span class="kw">return</span> <span class="name">is_valid_signature</span>
+
+<span class="kw">def </span><span class="fun">isIcqMessengerId</span>():
+ <span class="st st-db">&quot;&quot;&quot;</span><span class="st">Checks if the value is a valid ICQ ID</span><span class="st st-db">&quot;&quot;&quot;</span>
+ <span class="kw">def </span><span class="fun">is_icq_messenger_id</span>(<span class="name">field</span>, <span class="name">form</span>):
+ <span class="kw">if</span> <span class="name">_icq_re</span>.<span class="name">search</span>(<span class="name">field</span>.<span class="name">value</span>) <span class="op op-word">is</span> <span class="bn bn-pseudo">None</span>:
+ <span class="name">_</span> <span class="op">=</span> <span class="name">form</span>.<span class="name">req</span>.<span class="name">gettext</span>
+
+ <span class="kw">raise</span> <span class="name">ValidationError</span>(<span class="name">_</span>(<span class="st st-sg">&#39;</span><span class="st">Please enter a valid ICQ ID</span><span class="st st-sg">&#39;</span>))
+ <span class="kw">return</span> <span class="name">is_icq_messenger_id</span>
+
+
+<span class="kw">def </span><span class="fun">isJabberMessengerId</span>():
+ <span class="st st-db">&quot;&quot;&quot;</span><span class="st">Checks if the value is a valid jabber ID</span><span class="st st-db">&quot;&quot;&quot;</span>
+
+ <span class="kw">def </span><span class="fun">is_jabber_messenger_id</span>(<span class="name">field</span>, <span class="name">form</span>):
+ <span class="kw">if</span> <span class="name">_mail_re</span>.<span class="name">search</span>(<span class="name">field</span>.<span class="name">value</span>) <span class="op op-word">is</span> <span class="bn bn-pseudo">None</span>:
+ <span class="name">_</span> <span class="op">=</span> <span class="name">form</span>.<span class="name">req</span>.<span class="name">gettext</span>
+
+ <span class="kw">raise</span> <span class="name">ValidationError</span>(<span class="name">_</span>(<span class="st st-sg">&#39;</span><span class="st">Please enter a valid jabber ID</span><span class="st st-sg">&#39;</span>))
+ <span class="kw">return</span> <span class="name">is_jabber_messenger_id</span>
+
+
+<span class="kw">def </span><span class="fun">isMsnMessengerId</span>():
+ <span class="st st-db">&quot;&quot;&quot;</span><span class="st">Checks if the value is a valid MSN ID</span><span class="st st-db">&quot;&quot;&quot;</span>
+
+ <span class="kw">def </span><span class="fun">is_msn_messenger_id</span>(<span class="name">field</span>, <span class="name">form</span>):
+ <span class="kw">if</span> <span class="name">_mail_re</span>.<span class="name">search</span>(<span class="name">field</span>.<span class="name">value</span>) <span class="op op-word">is</span> <span class="bn bn-pseudo">None</span>:
+ <span class="name">_</span> <span class="op">=</span> <span class="name">form</span>.<span class="name">req</span>.<span class="name">gettext</span>
+
+ <span class="kw">raise</span> <span class="name">ValidationError</span>(<span class="name">_</span>(<span class="st st-sg">&#39;</span><span class="st">Please enter a valid MSN ID.</span><span class="st st-sg">&#39;</span>))
+ <span class="kw">return</span> <span class="name">is_msn_messenger_id</span>
+
+
+<span class="cm"># TODO: I don&#39;t know how valid AIM and Y!M IDs look, so I can&#39;t write validators</span>
+
+<span class="cm"># for them. It looks like they are simple strings, but I&#39;m not sure...</span>
+</pre>
+<script type="text/javascript">initCodeBlock('code-block')</script>
+</body>
+</html>
diff --git a/tests/examplefiles/test.java b/tests/examplefiles/test.java
new file mode 100644
index 00000000..64c08531
--- /dev/null
+++ b/tests/examplefiles/test.java
@@ -0,0 +1,653 @@
+/*
+ * Created on 13-Mar-2004
+ * Created by James Yeh
+ * Copyright (C) 2004, 2005, 2006 Aelitis, All Rights Reserved.
+ *
+ * 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
+ * of the License, 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * AELITIS, SAS au capital de 46,603.30 euros
+ * 8 Allee Lenotre, La Grille Royale, 78600 Le Mesnil le Roi, France.
+ *
+ */
+
+package org.gudy.azureus2.platform.macosx;
+
+import org.gudy.azureus2.core3.logging.*;
+import org.gudy.azureus2.core3.util.AEMonitor;
+import org.gudy.azureus2.core3.util.Debug;
+import org.gudy.azureus2.core3.util.SystemProperties;
+import org.gudy.azureus2.platform.PlatformManager;
+import org.gudy.azureus2.platform.PlatformManagerCapabilities;
+import org.gudy.azureus2.platform.PlatformManagerListener;
+import org.gudy.azureus2.platform.macosx.access.jnilib.OSXAccess;
+
+import org.gudy.azureus2.plugins.platform.PlatformManagerException;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.text.MessageFormat;
+import java.util.HashSet;
+
+
+/**
+ * Performs platform-specific operations with Mac OS X
+ *
+ * @author James Yeh
+ * @version 1.0 Initial Version
+ * @see PlatformManager
+ */
+public class PlatformManagerImpl implements PlatformManager
+{
+ private static final LogIDs LOGID = LogIDs.CORE;
+
+ protected static PlatformManagerImpl singleton;
+ protected static AEMonitor class_mon = new AEMonitor("PlatformManager");
+
+ private static final String USERDATA_PATH = new File(System.getProperty("user.home") + "/Library/Application Support/").getPath();
+
+ //T: PlatformManagerCapabilities
+ private final HashSet capabilitySet = new HashSet();
+
+ /**
+ * Gets the platform manager singleton, which was already initialized
+ */
+ public static PlatformManagerImpl getSingleton()
+ {
+ return singleton;
+ }
+
+ /**
+ * Tries to enable cocoa-java access and instantiates the singleton
+ */
+ static
+ {
+ initializeSingleton();
+ }
+
+ /**
+ * Instantiates the singleton
+ */
+ private static void initializeSingleton()
+ {
+ try
+ {
+ class_mon.enter();
+ singleton = new PlatformManagerImpl();
+ }
+ catch (Throwable e)
+ {
+ Logger.log(new LogEvent(LOGID, "Failed to initialize platform manager"
+ + " for Mac OS X", e));
+ }
+ finally
+ {
+ class_mon.exit();
+ }
+ }
+
+ /**
+ * Creates a new PlatformManager and initializes its capabilities
+ */
+ public PlatformManagerImpl()
+ {
+ capabilitySet.add(PlatformManagerCapabilities.RecoverableFileDelete);
+ capabilitySet.add(PlatformManagerCapabilities.ShowFileInBrowser);
+ capabilitySet.add(PlatformManagerCapabilities.ShowPathInCommandLine);
+ capabilitySet.add(PlatformManagerCapabilities.CreateCommandLineProcess);
+ capabilitySet.add(PlatformManagerCapabilities.GetUserDataDirectory);
+ capabilitySet.add(PlatformManagerCapabilities.UseNativeScripting);
+ capabilitySet.add(PlatformManagerCapabilities.PlaySystemAlert);
+
+ if (OSXAccess.isLoaded()) {
+ capabilitySet.add(PlatformManagerCapabilities.GetVersion);
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public int getPlatformType()
+ {
+ return PT_MACOSX;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public String getVersion() throws PlatformManagerException
+ {
+ if (!OSXAccess.isLoaded()) {
+ throw new PlatformManagerException("Unsupported capability called on platform manager");
+ }
+
+ return OSXAccess.getVersion();
+ }
+
+ /**
+ * {@inheritDoc}
+ * @see org.gudy.azureus2.core3.util.SystemProperties#getUserPath()
+ */
+ public String getUserDataDirectory() throws PlatformManagerException
+ {
+ return USERDATA_PATH;
+ }
+
+ public File
+ getLocation(
+ long location_id )
+
+ throws PlatformManagerException
+ {
+ if ( location_id == LOC_USER_DATA ){
+
+ return( new File( USERDATA_PATH ));
+ }
+
+ return( null );
+ }
+ /**
+ * Not implemented; returns True
+ */
+ public boolean isApplicationRegistered() throws PlatformManagerException
+ {
+ return true;
+ }
+
+
+ public String
+ getApplicationCommandLine()
+ throws PlatformManagerException
+ {
+ try{
+ String bundle_path = System.getProperty("user.dir") +SystemProperties.SEP+ SystemProperties.getApplicationName() + ".app";
+
+ File osx_app_bundle = new File( bundle_path ).getAbsoluteFile();
+
+ if( !osx_app_bundle.exists() ) {
+ String msg = "OSX app bundle not found: [" +osx_app_bundle.toString()+ "]";
+ System.out.println( msg );
+ if (Logger.isEnabled())
+ Logger.log(new LogEvent(LOGID, msg));
+ throw new PlatformManagerException( msg );
+ }
+
+ return "open -a \"" +osx_app_bundle.toString()+ "\"";
+ //return osx_app_bundle.toString() +"/Contents/MacOS/JavaApplicationStub";
+
+ }
+ catch( Throwable t ){
+ t.printStackTrace();
+ return null;
+ }
+ }
+
+
+ public boolean
+ isAdditionalFileTypeRegistered(
+ String name, // e.g. "BitTorrent"
+ String type ) // e.g. ".torrent"
+
+ throws PlatformManagerException
+ {
+ throw new PlatformManagerException("Unsupported capability called on platform manager");
+ }
+
+ public void
+ unregisterAdditionalFileType(
+ String name, // e.g. "BitTorrent"
+ String type ) // e.g. ".torrent"
+
+ throws PlatformManagerException
+ {
+ throw new PlatformManagerException("Unsupported capability called on platform manager");
+ }
+
+ public void
+ registerAdditionalFileType(
+ String name, // e.g. "BitTorrent"
+ String description, // e.g. "BitTorrent File"
+ String type, // e.g. ".torrent"
+ String content_type ) // e.g. "application/x-bittorrent"
+
+ throws PlatformManagerException
+ {
+ throw new PlatformManagerException("Unsupported capability called on platform manager");
+ }
+
+ /**
+ * Not implemented; does nothing
+ */
+ public void registerApplication() throws PlatformManagerException
+ {
+ // handled by LaunchServices and/0r user interaction
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public void createProcess(String cmd, boolean inheritsHandles) throws PlatformManagerException
+ {
+ try
+ {
+ performRuntimeExec(cmd.split(" "));
+ }
+ catch (Throwable e)
+ {
+ throw new PlatformManagerException("Failed to create process", e);
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public void performRecoverableFileDelete(String path) throws PlatformManagerException
+ {
+ File file = new File(path);
+ if(!file.exists())
+ {
+ if (Logger.isEnabled())
+ Logger.log(new LogEvent(LOGID, LogEvent.LT_WARNING, "Cannot find "
+ + file.getName()));
+ return;
+ }
+
+ boolean useOSA = !NativeInvocationBridge.sharedInstance().isEnabled() || !NativeInvocationBridge.sharedInstance().performRecoverableFileDelete(file);
+
+ if(useOSA)
+ {
+ try
+ {
+ StringBuffer sb = new StringBuffer();
+ sb.append("tell application \"");
+ sb.append("Finder");
+ sb.append("\" to move (posix file \"");
+ sb.append(path);
+ sb.append("\" as alias) to the trash");
+
+ performOSAScript(sb);
+ }
+ catch (Throwable e)
+ {
+ throw new PlatformManagerException("Failed to move file", e);
+ }
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public boolean hasCapability(PlatformManagerCapabilities capability)
+ {
+ return capabilitySet.contains(capability);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public void dispose()
+ {
+ NativeInvocationBridge.sharedInstance().dispose();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public void setTCPTOSEnabled(boolean enabled) throws PlatformManagerException
+ {
+ throw new PlatformManagerException("Unsupported capability called on platform manager");
+ }
+
+ public void
+ copyFilePermissions(
+ String from_file_name,
+ String to_file_name )
+
+ throws PlatformManagerException
+ {
+ throw new PlatformManagerException("Unsupported capability called on platform manager");
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public void showFile(String path) throws PlatformManagerException
+ {
+ File file = new File(path);
+ if(!file.exists())
+ {
+ if (Logger.isEnabled())
+ Logger.log(new LogEvent(LOGID, LogEvent.LT_WARNING, "Cannot find "
+ + file.getName()));
+ throw new PlatformManagerException("File not found");
+ }
+
+ showInFinder(file);
+ }
+
+ // Public utility methods not shared across the interface
+
+ /**
+ * Plays the system alert (the jingle is specified by the user in System Preferences)
+ */
+ public void playSystemAlert()
+ {
+ try
+ {
+ performRuntimeExec(new String[]{"beep"});
+ }
+ catch (IOException e)
+ {
+ if (Logger.isEnabled())
+ Logger.log(new LogEvent(LOGID, LogEvent.LT_WARNING,
+ "Cannot play system alert"));
+ Logger.log(new LogEvent(LOGID, "", e));
+ }
+ }
+
+ /**
+ * <p>Shows the given file or directory in Finder</p>
+ * @param path Absolute path to the file or directory
+ */
+ public void showInFinder(File path)
+ {
+ boolean useOSA = !NativeInvocationBridge.sharedInstance().isEnabled() || !NativeInvocationBridge.sharedInstance().showInFinder(path);
+
+ if(useOSA)
+ {
+ StringBuffer sb = new StringBuffer();
+ sb.append("tell application \"");
+ sb.append(getFileBrowserName());
+ sb.append("\" to reveal (posix file \"");
+ sb.append(path);
+ sb.append("\" as alias)");
+
+ try
+ {
+ performOSAScript(sb);
+ }
+ catch (IOException e)
+ {
+ Logger.log(new LogAlert(LogAlert.UNREPEATABLE, LogAlert.AT_ERROR, e
+ .getMessage()));
+ }
+ }
+ }
+
+ /**
+ * <p>Shows the given file or directory in Terminal by executing cd /absolute/path/to</p>
+ * @param path Absolute path to the file or directory
+ */
+ public void showInTerminal(String path)
+ {
+ showInTerminal(new File(path));
+ }
+
+ /**
+ * <p>Shows the given file or directory in Terminal by executing cd /absolute/path/to</p>
+ * @param path Absolute path to the file or directory
+ */
+ public void showInTerminal(File path)
+ {
+ if (path.isFile())
+ {
+ path = path.getParentFile();
+ }
+
+ if (path != null && path.isDirectory())
+ {
+ StringBuffer sb = new StringBuffer();
+ sb.append("tell application \"");
+ sb.append("Terminal");
+ sb.append("\" to do script \"cd ");
+ sb.append(path.getAbsolutePath().replaceAll(" ", "\\ "));
+ sb.append("\"");
+
+ try
+ {
+ performOSAScript(sb);
+ }
+ catch (IOException e)
+ {
+ Logger.log(new LogAlert(LogAlert.UNREPEATABLE, LogAlert.AT_ERROR, e
+ .getMessage()));
+ }
+ }
+ else
+ {
+ if (Logger.isEnabled())
+ Logger.log(new LogEvent(LOGID, LogEvent.LT_WARNING, "Cannot find "
+ + path.getName()));
+ }
+ }
+
+ // Internal utility methods
+
+ /**
+ * Compiles a new AppleScript instance and runs it
+ * @param cmd AppleScript command to execute; do not surround command with extra quotation marks
+ * @return Output of the script
+ * @throws IOException If the script failed to execute
+ */
+ protected static String performOSAScript(CharSequence cmd) throws IOException
+ {
+ return performOSAScript(new CharSequence[]{cmd});
+ }
+
+ /**
+ * Compiles a new AppleScript instance and runs it
+ * @param cmds AppleScript Sequence of commands to execute; do not surround command with extra quotation marks
+ * @return Output of the script
+ * @throws IOException If the script failed to execute
+ */
+ protected static String performOSAScript(CharSequence[] cmds) throws IOException
+ {
+ long start = System.currentTimeMillis();
+ Debug.outNoStack("Executing OSAScript: ");
+ for (int i = 0; i < cmds.length; i++)
+ {
+ Debug.outNoStack("\t" + cmds[i]);
+ }
+
+ String[] cmdargs = new String[2 * cmds.length + 1];
+ cmdargs[0] = "osascript";
+ for (int i = 0; i < cmds.length; i++)
+ {
+ cmdargs[i * 2 + 1] = "-e";
+ cmdargs[i * 2 + 2] = String.valueOf(cmds[i]);
+ }
+
+ Process osaProcess = performRuntimeExec(cmdargs);
+ BufferedReader reader = new BufferedReader(new InputStreamReader(osaProcess.getInputStream()));
+ String line = reader.readLine();
+ reader.close();
+ Debug.outNoStack("OSAScript Output: " + line);
+
+ reader = new BufferedReader(new InputStreamReader(osaProcess.getErrorStream()));
+ String errorMsg = reader.readLine();
+ reader.close();
+
+ Debug.outNoStack("OSAScript Error (if any): " + errorMsg);
+
+ Debug.outNoStack(MessageFormat.format("OSAScript execution ended ({0}ms)", new Object[]{String.valueOf(System.currentTimeMillis() - start)}));
+
+ if (errorMsg != null)
+ {
+ throw new IOException(errorMsg);
+ }
+
+ return line;
+ }
+
+ /**
+ * Compiles a new AppleScript instance and runs it
+ * @param script AppleScript file (.scpt) to execute
+ * @return Output of the script
+ * @throws IOException If the script failed to execute
+ */
+ protected static String performOSAScript(File script) throws IOException
+ {
+ long start = System.currentTimeMillis();
+ Debug.outNoStack("Executing OSAScript from file: " + script.getPath());
+
+ Process osaProcess = performRuntimeExec(new String[]{"osascript", script.getPath()});
+ BufferedReader reader = new BufferedReader(new InputStreamReader(osaProcess.getInputStream()));
+ String line = reader.readLine();
+ reader.close();
+ Debug.outNoStack("OSAScript Output: " + line);
+
+ reader = new BufferedReader(new InputStreamReader(osaProcess.getErrorStream()));
+ String errorMsg = reader.readLine();
+ reader.close();
+
+ Debug.outNoStack("OSAScript Error (if any): " + errorMsg);
+
+ Debug.outNoStack(MessageFormat.format("OSAScript execution ended ({0}ms)", new Object[]{String.valueOf(System.currentTimeMillis() - start)}));
+
+ if (errorMsg != null)
+ {
+ throw new IOException(errorMsg);
+ }
+
+ return line;
+ }
+
+ /**
+ * Compiles a new AppleScript instance to the specified location
+ * @param cmd Command to compile; do not surround command with extra quotation marks
+ * @param destination Destination location of the AppleScript file
+ * @return True if compiled successfully
+ */
+ protected static boolean compileOSAScript(CharSequence cmd, File destination)
+ {
+ return compileOSAScript(new CharSequence[]{cmd}, destination);
+ }
+
+ /**
+ * Compiles a new AppleScript instance to the specified location
+ * @param cmds Sequence of commands to compile; do not surround command with extra quotation marks
+ * @param destination Destination location of the AppleScript file
+ * @return True if compiled successfully
+ */
+ protected static boolean compileOSAScript(CharSequence[] cmds, File destination)
+ {
+ long start = System.currentTimeMillis();
+ Debug.outNoStack("Compiling OSAScript: " + destination.getPath());
+ for (int i = 0; i < cmds.length; i++)
+ {
+ Debug.outNoStack("\t" + cmds[i]);
+ }
+
+ String[] cmdargs = new String[2 * cmds.length + 3];
+ cmdargs[0] = "osacompile";
+ for (int i = 0; i < cmds.length; i++)
+ {
+ cmdargs[i * 2 + 1] = "-e";
+ cmdargs[i * 2 + 2] = String.valueOf(cmds[i]);
+ }
+
+ cmdargs[cmdargs.length - 2] = "-o";
+ cmdargs[cmdargs.length - 1] = destination.getPath();
+
+ String errorMsg;
+ try
+ {
+ Process osaProcess = performRuntimeExec(cmdargs);
+
+ BufferedReader reader = new BufferedReader(new InputStreamReader(osaProcess.getErrorStream()));
+ errorMsg = reader.readLine();
+ reader.close();
+ }
+ catch (IOException e)
+ {
+ Debug.outNoStack("OSACompile Execution Failed: " + e.getMessage());
+ Debug.printStackTrace(e);
+ return false;
+ }
+
+ Debug.outNoStack("OSACompile Error (if any): " + errorMsg);
+
+ Debug.outNoStack(MessageFormat.format("OSACompile execution ended ({0}ms)", new Object[]{String.valueOf(System.currentTimeMillis() - start)}));
+
+ return (errorMsg == null);
+ }
+
+ /**
+ * @see Runtime#exec(String[])
+ */
+ protected static Process performRuntimeExec(String[] cmdargs) throws IOException
+ {
+ try
+ {
+ return Runtime.getRuntime().exec(cmdargs);
+ }
+ catch (IOException e)
+ {
+ Logger.log(new LogAlert(LogAlert.UNREPEATABLE, e.getMessage(), e));
+ throw e;
+ }
+ }
+
+ /**
+ * <p>Gets the preferred file browser name</p>
+ * <p>Currently supported browsers are Path Finder and Finder. If Path Finder is currently running
+ * (not just installed), then "Path Finder is returned; else, "Finder" is returned.</p>
+ * @return "Path Finder" if it is currently running; else "Finder"
+ */
+ private static String getFileBrowserName()
+ {
+ try
+ {
+ // slowwwwwwww
+ if ("true".equalsIgnoreCase(performOSAScript("tell application \"System Events\" to exists process \"Path Finder\"")))
+ {
+ Debug.outNoStack("Path Finder is running");
+
+ return "Path Finder";
+ }
+ else
+ {
+ return "Finder";
+ }
+ }
+ catch (IOException e)
+ {
+ Debug.printStackTrace(e);
+ Logger.log(new LogEvent(LOGID, e.getMessage(), e));
+
+ return "Finder";
+ }
+ }
+
+ public boolean
+ testNativeAvailability(
+ String name )
+
+ throws PlatformManagerException
+ {
+ throw new PlatformManagerException("Unsupported capability called on platform manager");
+ }
+
+ public void
+ addListener(
+ PlatformManagerListener listener )
+ {
+ }
+
+ public void
+ removeListener(
+ PlatformManagerListener listener )
+ {
+ }
+}
diff --git a/tests/examplefiles/test.pas b/tests/examplefiles/test.pas
new file mode 100644
index 00000000..7b9c2d04
--- /dev/null
+++ b/tests/examplefiles/test.pas
@@ -0,0 +1,107 @@
+//
+// Sourcecode from http://www.delphi-library.de/topic_47880.html
+//
+uses Windows, Messages;
+
+const
+ FFM_INIT = WM_USER + 1976;
+ FFM_ONFILEFOUND = WM_USER + 1974; // wParam: not used, lParam: Filename
+ FFM_ONDIRFOUND = WM_USER + 1975; // wParam: NumFolder, lParam: Directory
+var
+ CntFolders : Cardinal = 0;
+ NumFolder : Cardinal = 0;
+
+
+////////////////////////////////////////////////////////////////////////////////
+//
+// FindAllFilesInit
+//
+//
+procedure FindAllFilesInit;
+begin
+ CntFolders := 0;
+ NumFolder := 0;
+end;
+
+////////////////////////////////////////////////////////////////////////////////
+//
+// CountFolders
+//
+//
+procedure CountFolders(Handle: THandle; RootFolder: string; Recurse: Boolean = True);
+var
+ hFindFile : THandle;
+ wfd : TWin32FindData;
+begin
+ SendMessage(Handle, FFM_INIT, 0, 0);
+ if RootFolder[length(RootFolder)] <> '\' then
+ RootFolder := RootFolder + '\';
+ ZeroMemory(@wfd, sizeof(wfd));
+ wfd.dwFileAttributes := FILE_ATTRIBUTE_NORMAL;
+ if Recurse then
+ begin
+ hFindFile := FindFirstFile(pointer(RootFolder + '*.*'), wfd);
+ if hFindFile <> 0 then
+ try
+ repeat
+ if wfd.dwFileAttributes and FILE_ATTRIBUTE_DIRECTORY = FILE_ATTRIBUTE_DIRECTORY then
+ begin
+ if (string(wfd.cFileName) <> '.') and (string(wfd.cFileName) <> '..') then
+ begin
+ CountFolders(Handle, RootFolder + wfd.cFileName, Recurse);
+ end;
+ end;
+ until FindNextFile(hFindFile, wfd) = False;
+ Inc(CntFolders);
+ finally
+ Windows.FindClose(hFindFile);
+ end;
+ end;
+end;
+
+////////////////////////////////////////////////////////////////////////////////
+//
+// FindAllFiles
+//
+procedure FindAllFiles(Handle: THandle; RootFolder: string; Mask: string; Recurse: Boolean = True);
+var
+ hFindFile : THandle;
+ wfd : TWin32FindData;
+begin
+ if RootFolder[length(RootFolder)] <> '\' then
+ RootFolder := RootFolder + '\';
+ ZeroMemory(@wfd, sizeof(wfd));
+ wfd.dwFileAttributes := FILE_ATTRIBUTE_NORMAL;
+ if Recurse then
+ begin
+ hFindFile := FindFirstFile(pointer(RootFolder + '*.*'), wfd);
+ if hFindFile <> 0 then
+ try
+ repeat
+ if wfd.dwFileAttributes and FILE_ATTRIBUTE_DIRECTORY = FILE_ATTRIBUTE_DIRECTORY then
+ begin
+ if (string(wfd.cFileName) <> '.') and (string(wfd.cFileName) <> '..') then
+ begin
+ FindAllFiles(Handle, RootFolder + wfd.cFileName, Mask, Recurse);
+ end;
+ end;
+ until FindNextFile(hFindFile, wfd) = False;
+ Inc(NumFolder);
+ SendMessage(Handle, FFM_ONDIRFOUND, NumFolder, lParam(string(RootFolder)));
+ finally
+ Windows.FindClose(hFindFile);
+ end;
+ end;
+ hFindFile := FindFirstFile(pointer(RootFolder + Mask), wfd);
+ if hFindFile <> INVALID_HANDLE_VALUE then
+ try
+ repeat
+ if (wfd.dwFileAttributes and FILE_ATTRIBUTE_DIRECTORY <> FILE_ATTRIBUTE_DIRECTORY) then
+ begin
+ SendMessage(Handle, FFM_ONFILEFOUND, 0, lParam(string(RootFolder + wfd.cFileName)));
+ end;
+ until FindNextFile(hFindFile, wfd) = False;
+ finally
+ Windows.FindClose(hFindFile);
+ end;
+end;
diff --git a/tests/examplefiles/test.php b/tests/examplefiles/test.php
new file mode 100644
index 00000000..ca14b0a9
--- /dev/null
+++ b/tests/examplefiles/test.php
@@ -0,0 +1,493 @@
+<?php
+/**
+ * Zip class file
+ *
+ * @package fnord.bb
+ * @subpackage archive
+ */
+
+// Unlock?
+if(!defined('UNLOCK') || !UNLOCK)
+ die();
+
+// Load the parent archive class
+require_once(ROOT_PATH.'/classes/archive.class.php');
+
+/**
+ * Zip class
+ *
+ * @author Manni <manni@fnord.name>
+ * @copyright Copyright (c) 2006, Manni
+ * @version 1.0
+ * @link http://www.pkware.com/business_and_developers/developer/popups/appnote.txt
+ * @link http://mannithedark.is-a-geek.net/
+ * @since 1.0
+ * @package fnord.bb
+ * @subpackage archive
+ */
+class Zip extends Archive {
+ /**
+ * Outputs the zip file
+ *
+ * This function creates the zip file with the dirs and files given.
+ * If the optional parameter $file is given, the zip file is will be
+ * saved at that location. Otherwise the function returns the zip file's content.
+ *
+ * @access public
+ *
+ * @link http://www.pkware.com/business_and_developers/developer/popups/appnote.txt
+ * @param string $filename The path where the zip file will be saved
+ *
+ * @return bool|string Returns either true if the fil is sucessfully created or the content of the zip file
+ */
+ function out($filename = false) {
+ // Empty output
+ $file_data = array(); // Data of the file part
+ $cd_data = array(); // Data of the central directory
+
+ // Sort dirs and files by path length
+ uksort($this->dirs, 'sort_by_length');
+ uksort($this->files, 'sort_by_length');
+
+ // Handle dirs
+ foreach($this->dirs as $dir) {
+ $dir .= '/';
+ // File part
+
+ // Reset dir data
+ $dir_data = '';
+
+ // Local file header
+ $dir_data .= "\x50\x4b\x03\x04"; // Local file header signature
+ $dir_data .= pack("v", 10); // Version needed to extract
+ $dir_data .= pack("v", 0); // General purpose bit flag
+ $dir_data .= pack("v", 0); // Compression method
+ $dir_data .= pack("v", 0); // Last mod file time
+ $dir_data .= pack("v", 0); // Last mod file date
+ $dir_data .= pack("V", 0); // crc-32
+ $dir_data .= pack("V", 0); // Compressed size
+ $dir_data .= pack("V", 0); // Uncompressed size
+ $dir_data .= pack("v", strlen($dir)); // File name length
+ $dir_data .= pack("v", 0); // Extra field length
+
+ $dir_data .= $dir; // File name
+ $dir_data .= ''; // Extra field (is empty)
+
+ // File data
+ $dir_data .= ''; // Dirs have no file data
+
+ // Data descriptor
+ $dir_data .= pack("V", 0); // crc-32
+ $dir_data .= pack("V", 0); // Compressed size
+ $dir_data .= pack("V", 0); // Uncompressed size
+
+ // Save current offset
+ $offset = strlen(implode('', $file_data));
+
+ // Append dir data to the file part
+ $file_data[] = $dir_data;
+
+ // Central directory
+
+ // Reset dir data
+ $dir_data = '';
+
+ // File header
+ $dir_data .= "\x50\x4b\x01\x02"; // Local file header signature
+ $dir_data .= pack("v", 0); // Version made by
+ $dir_data .= pack("v", 10); // Version needed to extract
+ $dir_data .= pack("v", 0); // General purpose bit flag
+ $dir_data .= pack("v", 0); // Compression method
+ $dir_data .= pack("v", 0); // Last mod file time
+ $dir_data .= pack("v", 0); // Last mod file date
+ $dir_data .= pack("V", 0); // crc-32
+ $dir_data .= pack("V", 0); // Compressed size
+ $dir_data .= pack("V", 0); // Uncompressed size
+ $dir_data .= pack("v", strlen($dir)); // File name length
+ $dir_data .= pack("v", 0); // Extra field length
+ $dir_data .= pack("v", 0); // File comment length
+ $dir_data .= pack("v", 0); // Disk number start
+ $dir_data .= pack("v", 0); // Internal file attributes
+ $dir_data .= pack("V", 16); // External file attributes
+ $dir_data .= pack("V", $offset); // Relative offset of local header
+
+ $dir_data .= $dir; // File name
+ $dir_data .= ''; // Extra field (is empty)
+ $dir_data .= ''; // File comment (is empty)
+
+ /*
+ // Data descriptor
+ $dir_data .= pack("V", 0); // crc-32
+ $dir_data .= pack("V", 0); // Compressed size
+ $dir_data .= pack("V", 0); // Uncompressed size
+ */
+
+ // Append dir data to the central directory data
+ $cd_data[] = $dir_data;
+ }
+
+ // Handle files
+ foreach($this->files as $name => $file) {
+ // Get values
+ $content = $file[0];
+
+ // File part
+
+ // Reset file data
+ $fd = '';
+
+ // Detect possible compressions
+ // Use deflate
+ if(function_exists('gzdeflate')) {
+ $method = 8;
+
+ // Compress file content
+ $compressed_data = gzdeflate($content);
+
+ // Use bzip2
+ } elseif(function_exists('bzcompress')) {
+ $method = 12;
+
+ // Compress file content
+ $compressed_data = bzcompress($content);
+
+ // No compression
+ } else {
+ $method = 0;
+
+ // Do not compress the content :P
+ $compressed_data = $content;
+ }
+
+ // Local file header
+ $fd .= "\x50\x4b\x03\x04"; // Local file header signature
+ $fd .= pack("v", 20); // Version needed to extract
+ $fd .= pack("v", 0); // General purpose bit flag
+ $fd .= pack("v", $method); // Compression method
+ $fd .= pack("v", 0); // Last mod file time
+ $fd .= pack("v", 0); // Last mod file date
+ $fd .= pack("V", crc32($content)); // crc-32
+ $fd .= pack("V", strlen($compressed_data)); // Compressed size
+ $fd .= pack("V", strlen($content)); // Uncompressed size
+ $fd .= pack("v", strlen($name)); // File name length
+ $fd .= pack("v", 0); // Extra field length
+
+ $fd .= $name; // File name
+ $fd .= ''; // Extra field (is empty)
+
+ // File data
+ $fd .= $compressed_data;
+
+ // Data descriptor
+ $fd .= pack("V", crc32($content)); // crc-32
+ $fd .= pack("V", strlen($compressed_data)); // Compressed size
+ $fd .= pack("V", strlen($content)); // Uncompressed size
+
+ // Save current offset
+ $offset = strlen(implode('', $file_data));
+
+ // Append file data to the file part
+ $file_data[] = $fd;
+
+ // Central directory
+
+ // Reset file data
+ $fd = '';
+
+ // File header
+ $fd .= "\x50\x4b\x01\x02"; // Local file header signature
+ $fd .= pack("v", 0); // Version made by
+ $fd .= pack("v", 20); // Version needed to extract
+ $fd .= pack("v", 0); // General purpose bit flag
+ $fd .= pack("v", $method); // Compression method
+ $fd .= pack("v", 0); // Last mod file time
+ $fd .= pack("v", 0); // Last mod file date
+ $fd .= pack("V", crc32($content)); // crc-32
+ $fd .= pack("V", strlen($compressed_data)); // Compressed size
+ $fd .= pack("V", strlen($content)); // Uncompressed size
+ $fd .= pack("v", strlen($name)); // File name length
+ $fd .= pack("v", 0); // Extra field length
+ $fd .= pack("v", 0); // File comment length
+ $fd .= pack("v", 0); // Disk number start
+ $fd .= pack("v", 0); // Internal file attributes
+ $fd .= pack("V", 32); // External file attributes
+ $fd .= pack("V", $offset); // Relative offset of local header
+
+ $fd .= $name; // File name
+ $fd .= ''; // Extra field (is empty)
+ $fd .= ''; // File comment (is empty)
+
+ /*
+ // Data descriptor
+ $fd .= pack("V", crc32($content)); // crc-32
+ $fd .= pack("V", strlen($compressed_data)); // Compressed size
+ $fd .= pack("V", strlen($content)); // Uncompressed size
+ */
+
+ // Append file data to the central directory data
+ $cd_data[] = $fd;
+ }
+
+ // Digital signature
+ $digital_signature = '';
+ $digital_signature .= "\x50\x4b\x05\x05"; // Header signature
+ $digital_signature .= pack("v", 0); // Size of data
+ $digital_signature .= ''; // Signature data (is empty)
+
+ $tmp_file_data = implode('', $file_data); // File data
+ $tmp_cd_data = implode('', $cd_data). // Central directory
+ $digital_signature; // Digital signature
+
+ // End of central directory
+ $eof_cd = '';
+ $eof_cd .= "\x50\x4b\x05\x06"; // End of central dir signature
+ $eof_cd .= pack("v", 0); // Number of this disk
+ $eof_cd .= pack("v", 0); // Number of the disk with the start of the central directory
+ $eof_cd .= pack("v", count($cd_data)); // Total number of entries in the central directory on this disk
+ $eof_cd .= pack("v", count($cd_data)); // Total number of entries in the central directory
+ $eof_cd .= pack("V", strlen($tmp_cd_data)); // Size of the central directory
+ $eof_cd .= pack("V", strlen($tmp_file_data)); // Offset of start of central directory with respect to the starting disk number
+ $eof_cd .= pack("v", 0); // .ZIP file comment length
+ $eof_cd .= ''; // .ZIP file comment (is empty)
+
+ // Content of the zip file
+ $data = $tmp_file_data.
+ // $extra_data_record.
+ $tmp_cd_data.
+ $eof_cd;
+
+ // Return content?
+ if(!$filename)
+ return $data;
+
+ // Write to file
+ return file_put_contents($filename, $data);
+ }
+
+ /**
+ * Load a zip file
+ *
+ * This function loads the files and dirs from a zip file from the harddrive.
+ *
+ * @access public
+ *
+ * @param string $file The path to the zip file
+ * @param bool $reset Reset the files and dirs before adding the zip file's content?
+ *
+ * @return bool Returns true if the file was loaded sucessfully
+ */
+ function load_file($file, $reset = true) {
+ // Check whether the file exists
+ if(!file_exists($file))
+ return false;
+
+ // Load the files content
+ $content = @file_get_contents($file);
+
+ // Return false if the file cannot be opened
+ if(!$content)
+ return false;
+
+ // Read the zip
+ return $this->load_string($content, $reset);
+ }
+
+ /**
+ * Load a zip string
+ *
+ * This function loads the files and dirs from a string
+ *
+ * @access public
+ *
+ * @param string $string The string the zip is generated from
+ * @param bool $reset Reset the files and dirs before adding the zip file's content?
+ *
+ * @return bool Returns true if the string was loaded sucessfully
+ */
+ function load_string($string, $reset = true) {
+ // Reset the zip?
+ if($reset) {
+ $this->dirs = array();
+ $this->files = array();
+ }
+
+ // Get the starting position of the end of central directory record
+ $start = strpos($string, "\x50\x4b\x05\x06");
+
+ // Error
+ if($start === false)
+ die('Could not find the end of central directory record');
+
+ // Get the ecdr
+ $eof_cd = substr($string, $start+4, 18);
+
+ // Unpack the ecdr infos
+ $eof_cd = unpack('vdisc1/'.
+ 'vdisc2/'.
+ 'ventries1/'.
+ 'ventries2/'.
+ 'Vsize/'.
+ 'Voffset/'.
+ 'vcomment_lenght', $eof_cd);
+
+ // Do not allow multi disc zips
+ if($eof_cd['disc1'] != 0)
+ die('multi disk stuff is not yet implemented :/');
+
+ // Save the interesting values
+ $cd_entries = $eof_cd['entries1'];
+ $cd_size = $eof_cd['size'];
+ $cd_offset = $eof_cd['offset'];
+
+ // Get the central directory record
+ $cdr = substr($string, $cd_offset, $cd_size);
+
+ // Reset the position and the list of the entries
+ $pos = 0;
+ $entries = array();
+
+ // Handle cdr
+ while($pos < strlen($cdr)) {
+ // Check header signature
+ // Digital signature
+ if(substr($cdr, $pos, 4) == "\x50\x4b\x05\x05") {
+ // Get digital signature size
+ $tmp_info = unpack('vsize', substr($cdr, $pos + 4, 2));
+
+ // Read out the digital signature
+ $digital_sig = substr($header, $pos + 6, $tmp_info['size']);
+
+ break;
+ }
+
+ // Get file header
+ $header = substr($cdr, $pos, 46);
+
+ // Unpack the header information
+ $header_info = @unpack('Vheader/'.
+ 'vversion_made_by/'.
+ 'vversion_needed/'.
+ 'vgeneral_purpose/'.
+ 'vcompression_method/'.
+ 'vlast_mod_time/'.
+ 'vlast_mod_date/'.
+ 'Vcrc32/'.
+ 'Vcompressed_size/'.
+ 'Vuncompressed_size/'.
+ 'vname_length/'.
+ 'vextra_length/'.
+ 'vcomment_length/'.
+ 'vdisk_number/'.
+ 'vinternal_attributes/'.
+ 'Vexternal_attributes/'.
+ 'Voffset',
+ $header);
+
+ // Valid header?
+ if($header_info['header'] != 33639248)
+ return false;
+
+ // New position
+ $pos += 46;
+
+ // Read out the file name
+ $header_info['name'] = substr($cdr, $pos, $header_info['name_length']);
+
+ // New position
+ $pos += $header_info['name_length'];
+
+ // Read out the extra stuff
+ $header_info['extra'] = substr($cdr, $pos, $header_info['extra_length']);
+
+ // New position
+ $pos += $header_info['extra_length'];
+
+ // Read out the comment
+ $header_info['comment'] = substr($cdr, $pos, $header_info['comment_length']);
+
+ // New position
+ $pos += $header_info['comment_length'];
+
+ // Append this file/dir to the entry list
+ $entries[] = $header_info;
+ }
+
+ // Check whether all entries where read sucessfully
+ if(count($entries) != $cd_entries)
+ return false;
+
+ // Handle files/dirs
+ foreach($entries as $entry) {
+ // Is a dir?
+ if($entry['external_attributes'] & 16) {
+ $this->add_dir($entry['name']);
+ continue;
+ }
+
+ // Get local file header
+ $header = substr($string, $entry['offset'], 30);
+
+ // Unpack the header information
+ $header_info = @unpack('Vheader/'.
+ 'vversion_needed/'.
+ 'vgeneral_purpose/'.
+ 'vcompression_method/'.
+ 'vlast_mod_time/'.
+ 'vlast_mod_date/'.
+ 'Vcrc32/'.
+ 'Vcompressed_size/'.
+ 'Vuncompressed_size/'.
+ 'vname_length/'.
+ 'vextra_length',
+ $header);
+
+ // Valid header?
+ if($header_info['header'] != 67324752)
+ return false;
+
+ // Get content start position
+ $start = $entry['offset'] + 30 + $header_info['name_length'] + $header_info['extra_length'];
+
+ // Get the compressed data
+ $data = substr($string, $start, $header_info['compressed_size']);
+
+ // Detect compression type
+ switch($header_info['compression_method']) {
+ // No compression
+ case 0:
+ // Ne decompression needed
+ $content = $data;
+ break;
+
+ // Gzip
+ case 8:
+ if(!function_exists('gzinflate'))
+ return false;
+
+ // Uncompress data
+ $content = gzinflate($data);
+ break;
+
+ // Bzip2
+ case 12:
+ if(!function_exists('bzdecompress'))
+ return false;
+
+ // Decompress data
+ $content = bzdecompress($data);
+ break;
+
+ // Compression not supported -> error
+ default:
+ return false;
+ }
+
+ // Try to add file
+ if(!$this->add_file($entry['name'], $content))
+ return false;
+ }
+
+ return true;
+ }
+}
+?>
diff --git a/tests/examplefiles/test.rb b/tests/examplefiles/test.rb
new file mode 100644
index 00000000..cf365af7
--- /dev/null
+++ b/tests/examplefiles/test.rb
@@ -0,0 +1,158 @@
+a.each{|el|anz[el]=anz[el]?anz[el]+1:1}
+while x<10000
+#a bis f dienen dazu die Nachbarschaft festzulegen. Man stelle sich die #Zahl von 1 bis 64 im Binärcode vor 1 bedeutet an 0 aus
+ b=(p[x]%32)/16<1 ? 0 : 1
+
+ (x-102>=0? n[x-102].to_i : 0)*a+(x-101>=0?n[x-101].to_i : 0)*e+n[x-100].to_i+(x-99>=0? n[x-99].to_i : 0)*f+(x-98>=0? n[x-98].to_i : 0)*a+
+ n[x+199].to_i*b+n[x+200].to_i*d+n[x+201].to_i*b
+
+#und die Ausgabe folgt
+g=%w{}
+x=0
+
+while x<100
+ puts"#{g[x]}"
+ x+=1
+end
+
+puts""
+sleep(10)
+
+1E1E1
+puts 30.send(:/, 5) # prints 6
+
+"instance variables can be #@included, #@@class_variables\n and #$globals as well."
+`instance variables can be #@included, #@@class_variables\n and #$globals as well.`
+'instance variables can be #@included, #@@class_variables\n and #$globals as well.'
+/instance variables can be #@included, #@@class_variables\n and #$globals as well./mousenix
+:"instance variables can be #@included, #@@class_variables\n and #$globals as well."
+:'instance variables can be #@included, #@@class_variables\n and #$globals as well.'
+%'instance variables can be #@included, #@@class_variables\n and #$globals as well.'
+%q'instance variables can be #@included, #@@class_variables\n and #$globals as well.'
+%Q'instance variables can be #@included, #@@class_variables\n and #$globals as well.'
+%w'instance variables can be #@included, #@@class_variables\n and #$globals as well.'
+%W'instance variables can be #@included, #@@class_variables\n and #$globals as well.'
+%s'instance variables can be #@included, #@@class_variables\n and #$globals as well.'
+%r'instance variables can be #@included, #@@class_variables\n and #$globals as well.'
+%x'instance variables can be #@included, #@@class_variables\n and #$globals as well.'
+
+#%W[ but #@0illegal_values look strange.]
+
+%s#ruby allows strange#{constructs}
+%s#ruby allows strange#$constructs
+%s#ruby allows strange#@@constructs
+
+##################################################################
+# HEREDOCS
+foo(<<-A, <<-B)
+this is the text of a
+A
+and this is the text of b
+B
+
+a = <<"EOF"
+This is a multiline #$here document
+terminated by EOF on a line by itself
+EOF
+
+a = <<'EOF'
+This is a multiline #$here document
+terminated by EOF on a line by itself
+EOF
+
+b=(p[x] %32)/16<1 ? 0 : 1
+
+<<""
+#{test}
+#@bla
+#die suppe!!!
+\xfffff
+
+
+super <<-EOE % [
+ foo
+EOE
+
+<<X
+X
+X
+
+%s(uninter\)pre\ted) # comment here
+%q(uninter\)pre\ted) # comment here
+%Q(inter\)pre\ted) # comment here
+:"inter\)pre\ted" # comment here
+:'uninter\'pre\ted' # comment here
+
+%q[haha! [nesting [rocks] ! ] ] # commeht here
+
+
+##################################################################
+class NP
+def initialize a=@p=[], b=@b=[]; end
+def +@;@b<<1;b2c end;def-@;@b<<0;b2c end
+def b2c;if @b.size==8;c=0;@b.each{|b|c<<=1;c|=b};send(
+ 'lave'.reverse,(@p.join))if c==0;@p<< c.chr;@b=[] end
+ self end end ; begin _ = NP.new end
+
+
+# Regexes
+/
+this is a
+mutliline
+regex
+/
+
+this /is a
+multiline regex too/
+
+also /4
+is one/
+
+this(/
+too
+/)
+
+# this not
+2 /4
+asfsadf/
+
+
+#from: http://coderay.rubychan.de/rays/show/383
+class Object
+ alias :xeq :`
+ def `(cmd, p2)
+ self.method(cmd.to_sym).call(p2)
+ end
+end
+p [1,2,3].`('concat', [4,5,6]) # => [1, 2, 3, 4, 5, 6]
+p [1,2,3].`(:concat, [4,5,6]) # => [1, 2, 3, 4, 5, 6]
+p "Hurra! ".`(:*, 3) # => "Hurra! Hurra! Hurra! "
+p "Hurra! ".`('*', 3) # => "Hurra! Hurra! Hurra! "
+# Leider geht nicht die Wunschform
+# [1,2,3] `concat` [4,5,6]
+
+class Object
+ @@infixops = []
+ alias :xeq :`
+ def addinfix(operator)
+ @@infixops << operator
+ end
+ def `(expression)
+ @@infixops.each{|op|break if expression.match(/^(.*?) (#{op}) (.*)$/)}
+ raise "unknown infix operator in expression: #{expression}" if $2 == nil
+ eval($1).method($2.to_sym).call(eval($3))
+ end
+end
+addinfix("concat")
+p `[1,2,3] concat [4,5,6]` # => [1, 2, 3, 4, 5, 6]
+
+
+# HEREDOC FUN!!!!!!!1111
+foo(<<A, <<-B, <<C)
+this is the text of a
+ A!!!!
+A
+and this is text of B!!!!!!111
+ B
+and here some C
+C
diff --git a/tests/examplefiles/test.rhtml b/tests/examplefiles/test.rhtml
new file mode 100644
index 00000000..d1f61576
--- /dev/null
+++ b/tests/examplefiles/test.rhtml
@@ -0,0 +1,43 @@
+<table>
+ <% rows.each do |row| %>
+ <tr class="<%= row.cls %>">
+ <td><a href="<%= item.href %>"><%= item.title %></a></td>
+ <td><%= item.description %></td>
+ </tr>
+ <% end %>
+</table>
+
+
+<h1>Pages</h1>
+
+<table id="site-map" class="index" cellpadding="0" cellspacing="0" border="0">
+ <thead>
+ <tr>
+ <th class="page">Page</th>
+ <th class="status">Status</th>
+ <th class="modify" colspan="2">Modify</th>
+ </tr>
+ </thead>
+ <tbody>
+<% if @homepage -%>
+<%= render_node @homepage -%>
+<% else -%>
+ <tr>
+ <td colspan="4" class="note">No Pages</td>
+ </tr>
+<% end -%>
+ </tbody>
+</table>
+<script type="text/javascript">
+// <![CDATA[
+ new SiteMap('site-map');
+// ]]>
+</script>
+<form action="<%= clear_cache_url %>" method="post">
+ <p>
+<% unless @homepage -%>
+ <%= link_to image_tag('new-homepage', :alt => 'New Homepage'), homepage_new_url %>
+<% end -%>
+ <%= image_submit_tag 'clear-page-cache' %>
+ </p>
+</form>
diff --git a/tests/run.py b/tests/run.py
new file mode 100644
index 00000000..20bda02e
--- /dev/null
+++ b/tests/run.py
@@ -0,0 +1,115 @@
+# -*- coding: utf-8 -*-
+"""
+ Pygments unit tests
+ ~~~~~~~~~~~~~~~~~~
+
+ Usage::
+
+ python run.py [testfile ...]
+
+
+ :copyright: 2006 by Georg Brandl.
+ :license: GNU GPL, see LICENSE for more details.
+"""
+
+import sys, os, new
+import unittest
+import __builtin__
+
+from os.path import dirname, basename, join, abspath
+
+testdir = abspath(dirname(__file__))
+
+# useful for all tests
+__builtin__.testdir = testdir
+
+failed = []
+total_test_count = 0
+error_test_count = 0
+
+
+def err(file, what, exc):
+ print >>sys.stderr, file, 'failed %s:' % what,
+ print >>sys.stderr, exc
+ failed.append(file[:-3])
+
+
+class QuietTestRunner(object):
+ """Customized test runner for relatively quiet output"""
+
+ def __init__(self, testname, stream=sys.stderr):
+ self.testname = testname
+ self.stream = unittest._WritelnDecorator(stream)
+
+ def run(self, test):
+ global total_test_count
+ global error_test_count
+ result = unittest._TextTestResult(self.stream, True, 1)
+ test(result)
+ if not result.wasSuccessful():
+ self.stream.write(' FAIL:')
+ result.printErrors()
+ failed.append(self.testname)
+ else:
+ self.stream.write(' ok\n')
+ total_test_count += result.testsRun
+ error_test_count += len(result.errors) + len(result.failures)
+ return result
+
+
+def run_tests():
+ # needed to avoid confusion involving atexit handlers
+ import logging
+
+ orig_modules = sys.modules.keys()
+
+ if sys.argv[1:]:
+ # test only files given on cmdline
+ files = [entry + '.py' for entry in sys.argv[1:] if entry.startswith('test_')]
+ else:
+ files = [entry for entry in os.listdir(testdir)
+ if (entry.startswith('test_') and entry.endswith('.py'))]
+ files.sort()
+
+ print >>sys.stderr, ' Pygments Test Suite running, stand by... '
+ print >>sys.stderr, '==============================================='
+
+ for testfile in files:
+ globs = {}
+ try:
+ execfile(join(testdir, testfile), globs)
+ except Exception, exc:
+ raise
+ err(testfile, 'execfile', exc)
+ continue
+ sys.stderr.write(testfile[:-3] + ': ')
+ try:
+ runner = QuietTestRunner(testfile[:-3])
+ # make a test suite of all TestCases in the file
+ tests = []
+ for name, thing in globs.iteritems():
+ if name.endswith('Test'):
+ tests.append((name, unittest.makeSuite(thing)))
+ tests.sort(key=lambda x: x[0])
+ suite = unittest.TestSuite()
+ suite.addTests(x[1] for x in tests)
+ runner.run(suite)
+ except Exception, exc:
+ err(testfile, 'running test', exc)
+
+ print >>sys.stderr, '==============================================='
+ if failed:
+ print >>sys.stderr, '%d of %d tests failed.' % \
+ (error_test_count, total_test_count)
+ print >>sys.stderr, 'Tests failed in:', ', '.join(failed)
+ return 1
+ else:
+ if total_test_count == 1:
+ print >>sys.stderr, '1 test happy.'
+ else:
+ print >>sys.stderr, 'All %d tests happy.' % total_test_count
+ return 0
+
+
+if __name__ == '__main__':
+ sys.exit(run_tests())
diff --git a/tests/test_basic_api.py b/tests/test_basic_api.py
new file mode 100644
index 00000000..a4e55366
--- /dev/null
+++ b/tests/test_basic_api.py
@@ -0,0 +1,12 @@
+# -*- coding: utf-8 -*-
+"""
+ Pygments basic API tests
+ ~~~~~~~~~~~~~~~~~~~~~~~
+
+ :copyright: 2006 by Georg Brandl.
+ :license: GNU GPL, see LICENSE for more details.
+"""
+
+from pygments import highlight
+
+# ... TODO ...
diff --git a/tests/test_clexer.py b/tests/test_clexer.py
new file mode 100644
index 00000000..145c6510
--- /dev/null
+++ b/tests/test_clexer.py
@@ -0,0 +1,35 @@
+# -*- coding: utf-8 -*-
+"""
+ Basic CLexer Test
+ ~~~~~~~~~~~~~~~~~
+
+ :copyright: 2006 by Armin Ronacher.
+ :license: GNU GPL, see LICENSE for more details.
+"""
+
+import unittest
+import os
+
+from pygments.token import Text, Number
+from pygments.lexers import CLexer
+
+
+class CLexerTest(unittest.TestCase):
+
+ def setUp(self):
+ self.lexer = CLexer()
+
+ def testNumbers(self):
+ code = '42 23.42 23. .42 023 0xdeadbeef 23e+42 42e-23'
+ wanted = []
+ for item in zip([Number.Integer, Number.Float, Number.Float,
+ Number.Float, Number.Oct, Number.Hex,
+ Number.Float, Number.Float], code.split()):
+ wanted.append(item)
+ wanted.append((Text, ' '))
+ wanted = [(Text, '')] + wanted[:-1] + [(Text, '\n')]
+ self.assertEqual(list(self.lexer.get_tokens(code)), wanted)
+
+
+if __name__ == '__main__':
+ unittest.main(CLexerTest)
diff --git a/tests/test_examplefiles.py b/tests/test_examplefiles.py
new file mode 100644
index 00000000..6347ab88
--- /dev/null
+++ b/tests/test_examplefiles.py
@@ -0,0 +1,49 @@
+# -*- coding: utf-8 -*-
+"""
+ Pygments tests with example files
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+ :copyright: 2006 by Georg Brandl.
+ :license: GNU GPL, see LICENSE for more details.
+"""
+
+import unittest
+import os
+
+from pygments import highlight
+from pygments.lexers import get_lexer_for_filename, get_lexer_by_name
+from pygments.token import Error
+
+
+class ExampleFileTest(unittest.TestCase):
+ pass
+
+lfd = 0
+
+# generate methods
+for fn in os.listdir(os.path.join(testdir, 'examplefiles')):
+ absfn = os.path.join(testdir, 'examplefiles', fn)
+ if not os.path.isfile(absfn):
+ continue
+
+ try:
+ lx = get_lexer_for_filename(absfn)
+ except ValueError:
+ try:
+ name, rest = fn.split("_", 1)
+ lx = get_lexer_by_name(name)
+ except ValueError:
+ raise AssertionError('no lexer found for file %r' % fn)
+
+ def test(self, lx=lx, absfn=absfn):
+ text = file(absfn, 'U').read()
+ text = text.strip('\n') + '\n'
+ ntext = ''
+ for type, val in lx.get_tokens(text):
+ ntext += val
+ self.failIf(type == Error, 'lexer generated error token for '+absfn)
+ if ntext != text:
+ self.fail('round trip failed for '+absfn)
+
+ setattr(ExampleFileTest, 'test_%i' % lfd, test)
+ lfd += 1
diff --git a/tests/test_lexers.py b/tests/test_lexers.py
new file mode 100644
index 00000000..9fb6f54c
--- /dev/null
+++ b/tests/test_lexers.py
@@ -0,0 +1,10 @@
+import unittest
+
+import pygments.lexers
+
+class LexerTest(unittest.TestCase):
+
+ def testImportAll(self):
+ # instantiate every lexer, to see if the token type defs are correct
+ for x in pygments.lexers.LEXERS.keys():
+ c = getattr(pygments.lexers, x)()