summaryrefslogtreecommitdiffstats
path: root/doc/src/examples/sipdialog.qdoc
blob: 16081f0dea7966501f1cda994c5a904fb214cbb8 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
/****************************************************************************
**
** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
** All rights reserved.
** Contact: Nokia Corporation (qt-info@nokia.com)
**
** This file is part of the documentation of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:FDL$
** No Commercial Usage
** This file contains pre-release code and may not be distributed.
** You may use this file in accordance with the terms and conditions
** contained in the Technology Preview License Agreement accompanying
** this package.
**
** GNU Free Documentation License
** Alternatively, this file may be used under the terms of the GNU Free
** Documentation License version 1.3 as published by the Free Software
** Foundation and appearing in the file included in the packaging of this
** file.
**
** If you have questions regarding the use of this file, please contact
** Nokia at qt-info@nokia.com.
** $QT_END_LICENSE$
**
****************************************************************************/

/*!
    \example dialogs/sipdialog
    \title SIP Dialog Example
    \ingroup qtce

    The SIP Dialog example shows how to create a dialog that is aware of 
    the Windows Mobile SIP (Software Input Panel) and reacts to it.

    \table
    \row \o \inlineimage sipdialog-closed.png
         \o \inlineimage sipdialog-opened.png
    \endtable

    Sometimes it is necessary for a dialog to take the SIP into account,
    as the SIP may hide important input widgets. The SIP Dialog Example
    shows how a \c Dialog object, \c dialog, can be resized accordingly
    if the SIP is opened, by embedding the contents of \c dialog in a
    QScrollArea.

    \section1 Dialog Class Definition

    The \c Dialog class is a subclass of QDialog that implements a public
    slot, \c desktopResized(), and a public function, \c reactToSIP(). Also,
    it holds a private instance of QRect, \c desktopGeometry.

    \snippet dialogs/sipdialog/dialog.h Dialog header

    \section1 Dialog Class Implementation

    In the constructor of \c Dialog, we start by obtaining the
    available geometry of the screen with
    \l{QDesktopWidget::availableGeometry()}{availableGeometry()}. The
    parameter used is \c 0 to indicate that we require the primary screen.

    \snippet dialogs/sipdialog/dialog.cpp Dialog constructor part1

    We set the window's title to "SIP Dialog Example" and declare a QScrollArea
    object, \c scrollArea. Next we instantiate a QGroupBox, \c groupBox, with
    \c scrollArea as its parent. The title of \c groupBox is also set to
    "SIP Dialog Example". A QGridLayout object, \c gridLayout, is then used
    as \c{groupBox}'s layout.

    We create a QLineEdit, a QLabel and a QPushButton and we set the
    \l{QWidget::setMinimumWidth()}{minimumWidth} property to 220 pixels,
    respectively.

    \snippet dialogs/sipdialog/dialog.cpp Dialog constructor part2

    Also, all three widgets' text are set accordingly. The
    \l{QGridLayout::setVerticalSpacing()}{verticalSpacing} property of
    \c gridLayout is set based on the height of \c desktopGeometry. This
    is to adapt to the different form factors of Windows Mobile. Then, we
    add our widgets to the layout.

    \snippet dialogs/sipdialog/dialog.cpp Dialog constructor part3

    The \c{scrollArea}'s widget is set to \c groupBox. We use a QHBoxLayout
    object, \c layout, to contain \c scrollArea. The \c{Dialog}'s layout
    is set to \c layout and the scroll area's horizontal scroll bar is turned
    off.

    \snippet dialogs/sipdialog/dialog.cpp Dialog constructor part4

    The following signals are connected to their respective slots:
    \list
        \o \c{button}'s \l{QPushButton::pressed()}{pressed()} signal to
        \l{QApplication}'s \l{QApplication::closeAllWindows()}
        {closeAllWindows()} slot,
        \o \l{QDesktopWidget}'s \l{QDesktopWidget::workAreaResized()}
        {workAreaResized()} signal to \c{dialog}'s \c desktopResized() slot.
    \endlist

    \snippet dialogs/sipdialog/dialog.cpp Dialog constructor part5

    The \c desktopResized() function accepts an integer, \a screen,
    corresponding to the screen's index. We only invoke \c reactToSIP()
    if \a screen is the primary screen (e.g. index = 0).

    \snippet dialogs/sipdialog/dialog.cpp desktopResized() function

    The \c reactToSIP() function resizes \c dialog accordingly if the
    desktop's available geometry changed vertically, as this change signifies
    that the SIP may have been opened or closed.

    \snippet dialogs/sipdialog/dialog.cpp reactToSIP() function

    If the height has decreased, we unset the maximized window state.
    Otherwise, we set the maximized window state. Lastly, we update
    \c desktopGeometry to the desktop's available geometry.

    \section1 The \c main() function

    The \c main() function for the SIP Dialog example instantiates \c Dialog
    and invokes its \l{QDialog::exec()}{exec()} function.

    \snippet dialogs/sipdialog/main.cpp main() function

    \note Although this example uses a dialog, the techniques used here apply to
    all top-level widgets respectively.
*/
n264'>264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776
#!/usr/bin/env python2

#
# Copyright (c) 2003 Steven Knight
#
# 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.
#

__revision__ = "src/sconsoutput.py 0.D003 2003/09/22 22:17:34 software"

#
# sconsoutput.py -   an SGML preprocessor for capturing SCons output
#                    and inserting it into examples in our DocBook
#                    documentation
#
# This script looks for some SGML tags that describe SCons example
# configurations and commands to execute in those configurations, and
# uses TestCmd.py to execute the commands and insert the output from
# those commands into the SGML that we output.  This way, we can run a
# script and update all of our example documentation output without
# a lot of laborious by-hand checking.
#
# An "SCons example" looks like this, and essentially describes a set of
# input files (program source files as well as SConscript files):
#
#       <scons_example name="ex1">
#         <file name="SConstruct" printme="1">
#           env = Environment()
#           env.Program('foo')
#         </file>
#         <file name="foo.c">
#           int main() { printf("foo.c\n"); }
#         </file>
#       </scons_example>
#
# The <file> contents within the <scons_example> tag will get written
# into a temporary directory whenever example output needs to be
# generated.  By default, the <file> contents are not inserted into text
# directly, unless you set the "printme" attribute on one or more files,
# in which case they will get inserted within a <programlisting> tag.
# This makes it easy to define the example at the appropriate
# point in the text where you intend to show the SConstruct file.
#
# Note that you should usually give the <scons_example> a "name"
# attribute so that you can refer to the example configuration later to
# run SCons and generate output.
#
# If you just want to show a file's contents without worry about running
# SCons, there's a shorter <sconstruct> tag:
#
#       <sconstruct>
#         env = Environment()
#         env.Program('foo')
#       </sconstruct>
#
# This is essentially equivalent to <scons_example><file printme="1">,
# but it's more straightforward.
#
# SCons output is generated from the following sort of tag:
#
#       <scons_output example="ex1" os="posix">
#         <command>scons -Q foo</command>
#         <command>scons -Q foo</command>
#       </scons_output>
#
# You tell it which example to use with the "example" attribute, and
# then give it a list of <command> tags to execute.  You can also supply
# an "os" tag, which specifies the type of operating system this example
# is intended to show; if you omit this, default value is "posix".
#
# The generated SGML will show the command line (with the appropriate
# command-line prompt for the operating system), execute the command in
# a temporary directory with the example files, capture the standard
# output from SCons, and insert it into the text as appropriate.
# Error output gets passed through to your error output so you
# can see if there are any problems executing the command.
#

import os
import os.path
import re
import sgmllib
import string
import sys
import time

sys.path.append(os.path.join(os.getcwd(), 'etc'))
sys.path.append(os.path.join(os.getcwd(), 'build', 'etc'))

scons_py = os.path.join('bootstrap', 'src', 'script', 'scons.py')
if not os.path.exists(scons_py):
    scons_py = os.path.join('src', 'script', 'scons.py')

scons_lib_dir = os.path.join(os.getcwd(), 'bootstrap', 'src', 'engine')
if not os.path.exists(scons_lib_dir):
    scons_lib_dir = os.path.join(os.getcwd(), 'src', 'engine')

os.environ['SCONS_LIB_DIR'] = scons_lib_dir

import TestCmd

# The regular expression that identifies entity references in the
# standard sgmllib omits the underscore from the legal characters.
# Override it with our own regular expression that adds underscore.
sgmllib.entityref = re.compile('&([a-zA-Z][-_.a-zA-Z0-9]*)[^-_a-zA-Z0-9]')

# Classes for collecting different types of data we're interested in.
class DataCollector:
    """Generic class for collecting data between a start tag and end
    tag.  We subclass for various types of tags we care about."""
    def __init__(self):
        self.data = ""
    def afunc(self, data):
        self.data = self.data + data

class Example(DataCollector):
    """An SCons example.  This is essentially a list of files that
    will get written to a temporary directory to collect output
    from one or more SCons runs."""
    def __init__(self):
        DataCollector.__init__(self)
        self.files = []
        self.dirs = []

class File(DataCollector):
    """A file, that will get written out to a temporary directory
    for one or more SCons runs."""
    def __init__(self, name):
        DataCollector.__init__(self)
        self.name = name

class Directory(DataCollector):
    """A directory, that will get created in a temporary directory
    for one or more SCons runs."""
    def __init__(self, name):
        DataCollector.__init__(self)
        self.name = name

class Output(DataCollector):
    """Where the command output goes.  This is essentially
    a list of commands that will get executed."""
    def __init__(self):
        DataCollector.__init__(self)
        self.commandlist = []

class Command(DataCollector):
    """A tag for where the command output goes.  This is essentially
    a list of commands that will get executed."""
    def __init__(self):
        DataCollector.__init__(self)
        self.output = None

Prompt = {
    'posix' : '% ',
    'win32' : 'C:\\>'
}

# The magick SCons hackery that makes this work.
#
# So that our examples can still use the default SConstruct file, we
# actually feed the following into SCons via stdin and then have it
# SConscript() the SConstruct file.  This stdin wrapper creates a set
# of ToolSurrogates for the tools for the appropriate platform.  These
# Surrogates print output like the real tools and behave like them
# without actually having to be on the right platform or have the right
# tool installed.
#
# The upshot:  The wrapper transparently changes the world out from
# under the top-level SConstruct file in an example just so we can get
# the command output.

Stdin = """\
import os
import re
import string
import SCons.Action
import SCons.Defaults
import SCons.Node.FS

platform = '%s'

Sep = {
    'posix' : '/',
    'win32' : '\\\\',
}[platform]


#  Slip our own __str__() method into the EntryProxy class used to expand
#  $TARGET{S} and $SOURCE{S} to translate the path-name separators from
#  what's appropriate for the system we're running on to what's appropriate
#  for the example system.
orig = SCons.Node.FS.EntryProxy
class MyEntryProxy(orig):
    def __str__(self):
        return string.replace(str(self._Proxy__subject), os.sep, Sep)
SCons.Node.FS.EntryProxy = MyEntryProxy

# Slip our own RDirs() method into the Node.FS.File class so that the
# expansions of $_{CPPINC,F77INC,LIBDIR}FLAGS will have the path-name
# separators translated from what's appropriate for the system we're
# running on to what's appropriate for the example system.
orig_RDirs = SCons.Node.FS.File.RDirs
def my_RDirs(self, pathlist, orig_RDirs=orig_RDirs):
    return map(lambda x: string.replace(str(x), os.sep, Sep),
               orig_RDirs(self, pathlist))
SCons.Node.FS.File.RDirs = my_RDirs

class Curry:
    def __init__(self, fun, *args, **kwargs):
        self.fun = fun
        self.pending = args[:]
        self.kwargs = kwargs.copy()

    def __call__(self, *args, **kwargs):
        if kwargs and self.kwargs:
            kw = self.kwargs.copy()
            kw.update(kwargs)
        else:
            kw = kwargs or self.kwargs

        return apply(self.fun, self.pending + args, kw)

def Str(target, source, env, cmd=""):
    return env.subst(cmd, target=target, source=source)

class ToolSurrogate:
    def __init__(self, tool, variable, func, varlist):
        self.tool = tool
        if not type(variable) is type([]):
            variable = [variable]
        self.variable = variable
        self.func = func
        self.varlist = varlist
    def __call__(self, env):
        t = Tool(self.tool)
        t.generate(env)
        for v in self.variable:
            orig = env[v]
            try:
                strfunction = orig.strfunction
            except AttributeError:
                strfunction = Curry(Str, cmd=orig)
            # Don't call Action() through its global function name, because
            # that leads to infinite recursion in trying to initialize the
            # Default Environment.
            env[v] = SCons.Action.Action(self.func,
                                         strfunction=strfunction,
                                         varlist=self.varlist)

def Null(target, source, env):
    pass

def Cat(target, source, env):
    target = str(target[0])
    f = open(target, "wb")
    for src in map(str, source):
        f.write(open(src, "rb").read())
    f.close()

def CCCom(target, source, env):
    target = str(target[0])
    f = open(target, "wb")
    for src in map(str, source):
        lines = open(src, "rb").readlines()
        lines = filter(lambda l: l[:11] != "STRIP CCCOM", lines)
        f.write(string.join(lines))
    f.close()

public_class_re = re.compile('^public class (\S+)', re.MULTILINE)

def JavaCCom(target, source, env):
    # This is a fake Java compiler that just looks for
    #   public class FooBar
    # lines in the source file(s) and spits those out
    # to .class files named after the class.
    tlist = map(str, target)
    not_copied = {}
    for t in tlist:
       not_copied[t] = 1
    for src in map(str, source):
        contents = open(src, "rb").read()
        classes = public_class_re.findall(contents)
        for c in classes:
            for t in filter(lambda x: string.find(x, c) != -1, tlist):
                open(t, "wb").write(contents)
                del not_copied[t]
    for t in not_copied.keys():
        open(t, "wb").write("\\n")

def JavaHCom(target, source, env):
    tlist = map(str, target)
    slist = map(str, source)
    for t, s in zip(tlist, slist):
        open(t, "wb").write(open(s, "rb").read())

def find_class_files(arg, dirname, names):
    class_files = filter(lambda n: n[-6:] == '.class', names)
    paths = map(lambda n, d=dirname: os.path.join(d, n), class_files)
    arg.extend(paths)

def JarCom(target, source, env):
    target = str(target[0])
    class_files = []
    for src in map(str, source):
        os.path.walk(src, find_class_files, class_files)
    f = open(target, "wb")
    for cf in class_files:
        f.write(open(cf, "rb").read())
    f.close()

# XXX Adding COLOR, COLORS and PACKAGE to the 'cc' varlist(s) by hand
# here is bogus.  It's for the benefit of doc/user/command-line.in, which
# uses examples that want  to rebuild based on changes to these variables.
# It would be better to figure out a way to do it based on the content of
# the generated command-line, or else find a way to let the example markup
# language in doc/user/command-line.in tell this script what variables to
# add, but that's more difficult than I want to figure out how to do right
# now, so let's just use the simple brute force approach for the moment.

ToolList = {
    'posix' :   [('cc', ['CCCOM', 'SHCCCOM'], CCCom, ['CCFLAGS', 'CPPDEFINES', 'COLOR', 'COLORS', 'PACKAGE']),
                 ('link', ['LINKCOM', 'SHLINKCOM'], Cat, []),
                 ('ar', 'ARCOM', Cat, []),
                 ('tar', 'TARCOM', Null, []),
                 ('zip', 'ZIPCOM', Null, []),
                 ('BitKeeper', 'BITKEEPERCOM', Cat, []),
                 ('CVS', 'CVSCOM', Cat, []),
                 ('RCS', 'RCS_COCOM', Cat, []),
                 ('SCCS', 'SCCSCOM', Cat, []),
                 ('javac', 'JAVACCOM', JavaCCom, []),
                 ('javah', 'JAVAHCOM', JavaHCom, []),
                 ('jar', 'JARCOM', JarCom, []),
                 ('rmic', 'RMICCOM', Cat, []),
                ],
    'win32' :   [('msvc', ['CCCOM', 'SHCCCOM'], CCCom, ['CCFLAGS', 'CPPDEFINES', 'COLOR', 'COLORS', 'PACKAGE']),
                 ('mslink', ['LINKCOM', 'SHLINKCOM'], Cat, []),
                 ('mslib', 'ARCOM', Cat, []),
                 ('tar', 'TARCOM', Null, []),
                 ('zip', 'ZIPCOM', Null, []),
                 ('BitKeeper', 'BITKEEPERCOM', Cat, []),
                 ('CVS', 'CVSCOM', Cat, []),
                 ('RCS', 'RCS_COCOM', Cat, []),
                 ('SCCS', 'SCCSCOM', Cat, []),
                 ('javac', 'JAVACCOM', JavaCCom, []),
                 ('javah', 'JAVAHCOM', JavaHCom, []),
                 ('jar', 'JARCOM', JarCom, []),
                 ('rmic', 'RMICCOM', Cat, []),
                ],
}

tools = map(lambda t: apply(ToolSurrogate, t), ToolList[platform])

def surrogate_spawn(sh, escape, cmd, args, env):
    pass

def surrogate_pspawn(sh, escape, cmd, args, env, stdout, stderr):
    pass

SCons.Defaults.ConstructionEnvironment.update({
    'PLATFORM' : platform,
    'TOOLS'    : tools,
    'SPAWN'    : surrogate_spawn,
    'PSPAWN'   : surrogate_pspawn,
})

SConscript('SConstruct')
"""

# "Commands" that we will execute in our examples.
def command_scons(args, c, test, osname):
    save_vals = {}
    delete_keys = []
    try:
        ce = c.environment
    except AttributeError:
        pass
    else:
        for arg in string.split(c.environment):
            key, val = string.split(arg, '=')
            try:
                save_vals[key] = os.environ[key]
            except KeyError:
                delete_keys.append(key)
            os.environ[key] = val
    test.run(interpreter = sys.executable,
             program = scons_py,
             arguments = '-f - ' + string.join(args),
             chdir = test.workpath('WORK'),
             stdin = Stdin % osname)
    os.environ.update(save_vals)
    for key in delete_keys:
        del(os.environ[key])
    out = string.replace(test.stdout(), test.workpath('ROOT'), '')
    lines = string.split(out, '\n')
    if lines:
        while lines[-1] == '':
            lines = lines[:-1]
    #err = test.stderr()
    #if err:
    #    sys.stderr.write(err)
    return lines

def command_touch(args, c, test, osname):
    time.sleep(1)
    for file in args:
        if not os.path.isabs(file):
            file = os.path.join(test.workpath('WORK'), file)
        if not os.path.exists(file):
            open(file, 'wb')
        os.utime(file, None)
    return []

def command_edit(args, c, test, osname):
    try:
        add_string = c.edit[:]
    except AttributeError:
        add_string = 'void edit(void) { ; }\n'
    if add_string[-1] != '\n':
        add_string = add_string + '\n'
    for file in args:
        if not os.path.isabs(file):
            file = os.path.join(test.workpath('WORK'), file)
        contents = open(file, 'rb').read()
        open(file, 'wb').write(contents + add_string)
    return []

def command_ls(args, c, test, osname):
    def ls(a):
        files = os.listdir(a)
        files = filter(lambda x: x[0] != '.', files)
        files.sort()
        return [string.join(files, '  ')]
    if args:
        l = []
        for a in args:
            l.extend(ls(test.workpath('WORK', a)))
        return l
    else:
        return ls(test.workpath('WORK'))

CommandDict = {
    'scons' : command_scons,
    'touch' : command_touch,
    'edit'  : command_edit,
    'ls'    : command_ls,
}

def ExecuteCommand(args, c, t, osname):
    try:
        func = CommandDict[args[0]]
    except KeyError:
        func = lambda args, c, t, osname: []
    return func(args[1:], c, t, osname)

class MySGML(sgmllib.SGMLParser):
    """A subclass of the standard Python 2.2 sgmllib SGML parser.

    This extends the standard sgmllib parser to recognize, and do cool
    stuff with, the added tags that describe our SCons examples,
    commands, and other stuff.

    Note that this doesn't work with the 1.5.2 sgmllib module, because
    that didn't have the ability to work with ENTITY declarations.
    """
    def __init__(self):
        sgmllib.SGMLParser.__init__(self)
        self.examples = {}
        self.afunclist = []

    # The first set of methods here essentially implement pass-through
    # handling of most of the stuff in an SGML file.  We're really
    # only concerned with the tags specific to SCons example processing,
    # the methods for which get defined below.

    def handle_data(self, data):
        try:
            f = self.afunclist[-1]
        except IndexError:
            sys.stdout.write(data)
        else:
            f(data)

    def handle_comment(self, data):
        sys.stdout.write('<!--' + data + '-->')

    def handle_decl(self, data):
        sys.stdout.write('<!' + data + '>')

    def unknown_starttag(self, tag, attrs):
        try:
            f = self.example.afunc
        except AttributeError:
            f = sys.stdout.write
        if not attrs:
            f('<' + tag + '>')
        else:
            f('<' + tag)
            for name, value in attrs:
                f(' ' + name + '=' + '"' + value + '"')
            f('>')

    def unknown_endtag(self, tag):
        sys.stdout.write('</' + tag + '>')

    def unknown_entityref(self, ref):
        sys.stdout.write('&' + ref + ';')

    def unknown_charref(self, ref):
        sys.stdout.write('&#' + ref + ';')

    # Here is where the heavy lifting begins.  The following methods
    # handle the begin-end tags of our SCons examples.

    def start_scons_example(self, attrs):
        t = filter(lambda t: t[0] == 'name', attrs)
        if t:
            name = t[0][1]
            try:
               e = self.examples[name]
            except KeyError:
               e = self.examples[name] = Example()
        else:
            e = Example()
        for name, value in attrs:
            setattr(e, name, value)
        self.e = e
        self.afunclist.append(e.afunc)

    def end_scons_example(self):
        e = self.e
        files = filter(lambda f: f.printme, e.files)
        if files:
            sys.stdout.write('<programlisting>')
            for f in files:
                if f.printme:
                    i = len(f.data) - 1
                    while f.data[i] == ' ':
                        i = i - 1
                    output = string.replace(f.data[:i+1], '__ROOT__', '')
                    output = string.replace(output, '<', '&lt;')
                    output = string.replace(output, '>', '&gt;')
                    sys.stdout.write(output)
            if e.data and e.data[0] == '\n':
                e.data = e.data[1:]
            sys.stdout.write(e.data + '</programlisting>')
        delattr(self, 'e')
        self.afunclist = self.afunclist[:-1]

    def start_file(self, attrs):
        try:
            e = self.e
        except AttributeError:
            self.error("<file> tag outside of <scons_example>")
        t = filter(lambda t: t[0] == 'name', attrs)
        if not t:
            self.error("no <file> name attribute found")
        try:
            e.prefix
        except AttributeError:
            e.prefix = e.data
            e.data = ""
        f = File(t[0][1])
        f.printme = None
        for name, value in attrs:
            setattr(f, name, value)
        e.files.append(f)
        self.afunclist.append(f.afunc)

    def end_file(self):
        self.e.data = ""
        self.afunclist = self.afunclist[:-1]

    def start_directory(self, attrs):
        try:
            e = self.e
        except AttributeError:
            self.error("<directory> tag outside of <scons_example>")
        t = filter(lambda t: t[0] == 'name', attrs)
        if not t:
            self.error("no <directory> name attribute found")
        try:
            e.prefix
        except AttributeError:
            e.prefix = e.data
            e.data = ""
        d = Directory(t[0][1])
        for name, value in attrs:
            setattr(d, name, value)
        e.dirs.append(d)
        self.afunclist.append(d.afunc)

    def end_directory(self):
        self.e.data = ""
        self.afunclist = self.afunclist[:-1]

    def start_scons_example_file(self, attrs):
        t = filter(lambda t: t[0] == 'example', attrs)
        if not t:
            self.error("no <scons_example_file> example attribute found")
        exname = t[0][1]
        try:
            e = self.examples[exname]
        except KeyError:
            self.error("unknown example name '%s'" % exname)
        fattrs = filter(lambda t: t[0] == 'name', attrs)
        if not fattrs:
            self.error("no <scons_example_file> name attribute found")
        fname = fattrs[0][1]
        f = filter(lambda f, fname=fname: f.name == fname, e.files)
        if not f:
            self.error("example '%s' does not have a file named '%s'" % (exname, fname))
        self.f = f[0]

    def end_scons_example_file(self):
        f = self.f
        sys.stdout.write('<programlisting>')
        sys.stdout.write(f.data + '</programlisting>')
        delattr(self, 'f')

    def start_scons_output(self, attrs):
        t = filter(lambda t: t[0] == 'example', attrs)
        if not t:
            self.error("no <scons_output> example attribute found")
        exname = t[0][1]
        try:
            e = self.examples[exname]
        except KeyError:
            self.error("unknown example name '%s'" % exname)
        # Default values for an example.
        o = Output()
        o.preserve = None
        o.os = 'posix'
        o.e = e
        # Locally-set.
        for name, value in attrs:
            setattr(o, name, value)
        self.o = o
        self.afunclist.append(o.afunc)

    def end_scons_output(self):
        # The real raison d'etre for this script, this is where we
        # actually execute SCons to fetch the output.
        o = self.o
        e = o.e
        t = TestCmd.TestCmd(workdir='', combine=1)
        if o.preserve:
            t.preserve()
        t.subdir('ROOT', 'WORK')
        t.rootpath = string.replace(t.workpath('ROOT'), '\\', '\\\\')

        for d in e.dirs:
            dir = t.workpath('WORK', d.name)
            if not os.path.exists(dir):
                os.makedirs(dir)

        for f in e.files:
            i = 0
            while f.data[i] == '\n':
                i = i + 1
            lines = string.split(f.data[i:], '\n')
            i = 0
            while lines[0][i] == ' ':
                i = i + 1
            lines = map(lambda l, i=i: l[i:], lines)
            path = string.replace(f.name, '__ROOT__', t.rootpath)
            if not os.path.isabs(path):
                path = t.workpath('WORK', path)
            dir, name = os.path.split(path)
            if dir and not os.path.exists(dir):
                os.makedirs(dir)
            content = string.join(lines, '\n')
            content = string.replace(content, '__ROOT__', t.rootpath)
            path = t.workpath('WORK', path)
            t.write(path, content)
            if hasattr(f, 'chmod'):
                os.chmod(path, int(f.chmod, 0))

        i = len(o.prefix)
        while o.prefix[i-1] != '\n':
            i = i - 1

        sys.stdout.write('<screen>' + o.prefix[:i])
        p = o.prefix[i:]

        for c in o.commandlist:
            sys.stdout.write(p + Prompt[o.os])
            d = string.replace(c.data, '__ROOT__', '')
            sys.stdout.write('<userinput>' + d + '</userinput>\n')

            e = string.replace(c.data, '__ROOT__', t.workpath('ROOT'))
            args = string.split(e)
            lines = ExecuteCommand(args, c, t, o.os)
            if c.output:
                sys.stdout.write(p + c.output + '\n')
            elif lines:
                sys.stdout.write(p + string.join(lines, '\n' + p) + '\n')

        if o.data[0] == '\n':
            o.data = o.data[1:]
        sys.stdout.write(o.data + '</screen>')
        delattr(self, 'o')
        self.afunclist = self.afunclist[:-1]

    def start_command(self, attrs):
        try:
            o = self.o
        except AttributeError:
            self.error("<command> tag outside of <scons_output>")
        try:
            o.prefix
        except AttributeError:
            o.prefix = o.data
            o.data = ""
        c = Command()
        for name, value in attrs:
            setattr(c, name, value)
        o.commandlist.append(c)
        self.afunclist.append(c.afunc)

    def end_command(self):
        self.o.data = ""
        self.afunclist = self.afunclist[:-1]

    def start_sconstruct(self, attrs):
        f = File('')
        self.f = f
        self.afunclist.append(f.afunc)

    def end_sconstruct(self):
        f = self.f
        sys.stdout.write('<programlisting>')
        output = string.replace(f.data, '__ROOT__', '')
        sys.stdout.write(output + '</programlisting>')
        delattr(self, 'f')
        self.afunclist = self.afunclist[:-1]

# The main portion of the program itself, short and simple.
try:
    file = sys.argv[1]
except IndexError:
    file = '-'

if file == '-':
    f = sys.stdin
else:
    try:
        f = open(file, 'r')
    except IOError, msg:
        print file, ":", msg
        sys.exit(1)

data = f.read()
if f is not sys.stdin:
    f.close()

x = MySGML()
for c in data:
    x.feed(c)
x.close()