Initial import of Dillo

This commit is contained in:
2025-02-28 13:34:30 -05:00
parent bd4e3eebd8
commit 20fea64cb5
496 changed files with 156174 additions and 0 deletions

273
.github/workflows/build.yml vendored Normal file
View File

@ -0,0 +1,273 @@
name: CI
on:
pull_request:
push:
branches: master
jobs:
ubuntu-latest-html-tests:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v1
with:
fetch-depth: 1
- name: Install dependencies
run: sudo apt install -y libfltk1.3-dev libssl-dev libpng-dev libjpeg-dev libwebp-dev xvfb x11-apps x11-utils imagemagick
- name: autogen
run: ./autogen.sh
- name: Make install dir
run: mkdir install build
- name: configure
run: cd build && ../configure --prefix=$(readlink -f ../install) --enable-html-tests
- name: make
run: cd build && make
- name: make install
run: cd build && make install
- name: Copy config to .dillo
run: |
mkdir -p ~/.dillo/
cp install/etc/dillo/* ~/.dillo/
- name: make check
run: |
export DILLOBIN=$(readlink -f install/bin/dillo)
cd build && make check || (cat test/html/test-suite.log; false)
export DILLOBIN=
- name: Check release fits in a floppy disk of 1.44 MB
run: |
cd build
make dist-gzip
size=$(stat -c %s dillo-*.tar.gz)
floppy=$((1474560 - 32*1024)) # Leave room for FAT table
echo "Floppy occupation: $(($size * 100 / $floppy)) %"
if [ $size -lt $floppy ]; then
echo 'OK: Fits in floopy disk'
else
echo "FAIL: Release size too big: $size / $floppy"
exit 1
fi
- name: make distcheck (with HTML tests)
run: |
export DILLOBIN=
mkdir build-distcheck install-distcheck
cd build-distcheck && ../configure --prefix=$(readlink -f ../install-distcheck) --enable-html-tests
make distcheck DISTCHECK_CONFIGURE_FLAGS=--enable-html-tests
# - name: Remove pipes
# run: find test/html -type p -delete || true
# - name: Archive production artifacts
# uses: actions/upload-artifact@v3
# with:
# name: upload-html-test-results
# path: |
# build/test/html
ubuntu-latest-no-tls:
needs: ubuntu-latest-html-tests
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v1
with:
fetch-depth: 1
- name: Install dependencies
run: sudo apt install -y libfltk1.3-dev
- name: autogen
run: ./autogen.sh
- name: configure
run: ./configure --disable-tls
- name: make
run: make
- name: make check
run: make check
- name: make distcheck
run: make distcheck
ubuntu-latest-mbedtls2:
needs: ubuntu-latest-html-tests
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v1
with:
fetch-depth: 1
- name: Install dependencies
run: sudo apt install -y libfltk1.3-dev libmbedtls-dev
- name: autogen
run: ./autogen.sh
- name: configure
run: ./configure --disable-openssl
- name: make
run: make
- name: make check
run: make check
- name: make distcheck
run: make distcheck
ubuntu-latest-openssl-3:
needs: ubuntu-latest-html-tests
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v1
with:
fetch-depth: 1
- name: Install dependencies
run: sudo apt install -y libfltk1.3-dev libssl-dev
- name: autogen
run: ./autogen.sh
- name: configure
run: ./configure --disable-mbedtls
- name: make
run: make
- name: make check
run: make check
- name: make distcheck
run: make distcheck
ubuntu-latest-with-old-std:
needs: ubuntu-latest-html-tests
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v1
with:
fetch-depth: 1
- name: Install dependencies
run: sudo apt install -y libfltk1.3-dev libssl-dev
- name: autogen
run: ./autogen.sh
- name: configure
# Make sure we build with the C++11 standard, so we fail to build on newer
# features.
run: ./configure --disable-mbedtls CFLAGS="-Werror" CXXFLAGS="-Werror -std=c++11"
- name: make
run: make
- name: make check
run: make check
ubuntu-20-04-openssl-1-1:
needs: ubuntu-latest-html-tests
runs-on: ubuntu-20.04
steps:
- uses: actions/checkout@v1
with:
fetch-depth: 1
- name: Install dependencies
run: sudo apt install -y libfltk1.3-dev libssl-dev
- name: autogen
run: ./autogen.sh
- name: configure
run: ./configure --disable-mbedtls
- name: make
run: make
- name: make check
run: make check
- name: make distcheck
run: make distcheck
alpine-mbedtls-3_6_0:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: jirutka/setup-alpine@v1
with:
packages: >
build-base
autoconf
automake
fltk-dev
libpng-dev
libjpeg-turbo-dev
libwebp-dev
mbedtls-dev
- run: |
./autogen.sh
./configure
make
make check
shell: alpine.sh {0}
macOS-13-openssl-1-1:
needs: ubuntu-latest-html-tests
runs-on: macos-13
steps:
- uses: actions/checkout@v1
with:
fetch-depth: 1
- name: Install dependencies
run: brew install autoconf automake fltk@1.3
- name: autogen
run: ./autogen.sh
- name: configure
run: |
export PATH="/usr/local/opt/fltk@1.3/bin:$PATH"
./configure --disable-mbedtls
- name: make
run: make
- name: make check
run: make check
- name: make distcheck
run: |
export PATH="/usr/local/opt/fltk@1.3/bin:$PATH"
make distcheck
macOS-13-openssl-3:
needs: ubuntu-latest-html-tests
runs-on: macos-13
steps:
- uses: actions/checkout@v1
with:
fetch-depth: 1
- name: Remove old OpenSSL 1.1
run: brew uninstall openssl@1.1
- name: Install dependencies
run: brew install autoconf automake fltk@1.3 openssl@3
- name: autogen
run: ./autogen.sh
- name: configure
run: |
export PATH="/usr/local/opt/fltk@1.3/bin:$PATH"
./configure --disable-mbedtls
- name: make
run: make
- name: make check
run: make check
- name: make distcheck
run: |
export PATH="/usr/local/opt/fltk@1.3/bin:$PATH"
make distcheck
freebsd-14-openssl-3:
needs: ubuntu-latest-html-tests
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: FreeBSD VM build
id: test
uses: vmactions/freebsd-vm@v1
with:
release: "14.0"
usesh: true
prepare: |
set -x
pkg install -y automake fltk
run: |
set -x
pwd
freebsd-version
./autogen.sh
./configure CPPFLAGS='-I/usr/local/include' LDFLAGS='-L/usr/local/lib'
cat config.log
make
make check
ldd src/dillo
windows-mbedtls:
needs: ubuntu-latest-html-tests
runs-on: windows-latest
steps:
- run: git config --global core.autocrlf input
- uses: actions/checkout@v4
with:
fetch-depth: 1
- uses: cygwin/cygwin-install-action@master
with:
packages: gcc-core gcc-g++ autoconf automake make zlib-devel mbedtls-devel libfltk-devel libiconv-devel libpng-devel libjpeg-devel libwebp-devel libgif-devel
- shell: C:\cygwin\bin\bash.exe --login --norc -eo pipefail -o igncr '{0}'
run: |
set -x
cd ${GITHUB_WORKSPACE}
pwd
ls -l
./autogen.sh
./configure
make
make check
ls -l src/dillo

47
.gitignore vendored Normal file
View File

@ -0,0 +1,47 @@
*.a
*.o
config.*
autom4te.cache
Makefile
Makefile.in
configure
compile
build
aclocal.m4
depcomp
install-sh
stamp-h1
missing
tags
/src/dillo
/doc/dillo.1
/dpi/[^/]*\.dpi
/dpid/dpid
/dpid/dpidc
/dpid/dpidrc
/test/containers
/test/cookies
/test/dw-anchors-test
/test/dw-border-test
/test/dw-example
/test/dw-find-test
/test/dw-float-test
/test/dw-image-background
/test/dw-images-scaled
/test/dw-images-scaled2
/test/dw-images-simple
/test/dw-imgbuf-mem-test
/test/dw-links
/test/dw-links2
/test/dw-lists
/test/dw-resource-test
/test/dw-simple-container-test
/test/dw-table
/test/dw-table-aligned
/test/dw-ui-test
/test/identity
/test/liang
/test/notsosimplevector
/test/shapes
/test/trie
/test/unicode-test

12
.rtfl Normal file
View File

@ -0,0 +1,12 @@
[rtfl-obj-1.0]:0:0:class-color:dw\:\:*:#c0ff80
[rtfl-obj-1.0]:0:0:class-color:dw\:\:fltk\:\:*:#c0c0ff
[rtfl-obj-1.0]:0:0:class-color:dw\:\:core\:\:*:#ffa0a0
[rtfl-obj-1.0]:0:0:class-color:dw\:\:core\:\:style\:\:*:#ffe0a0
[rtfl-obj-1.0]:0:0:class-color:dw\:\:core\:\:SizeParams:#e0e0a0
[rtfl-obj-1.0]:0:0:class-color:dw\:\:Image:#80ffa0
[rtfl-obj-1.0]:0:0:class-color:dw\:\:Textblock:#f0ff80
[rtfl-obj-1.0]:0:0:class-color:dw\:\:OutOfFlowMgr:#d0ff80
[rtfl-obj-1.0]:0:0:class-color:dw\:\:AlignedTextblock:#e0ff80
[rtfl-obj-1.0]:0:0:class-color:dw\:\:ListItem:#b0ff80
[rtfl-obj-1.0]:0:0:class-color:dw\:\:TableCell:#80ff80
[rtfl-obj-1.0]:0:0:class-color:dw\:\:Table:#80ffc0

103
AUTHORS Normal file
View File

@ -0,0 +1,103 @@
_________________________________________________________________
Flenser project's team
_________________________________________________________________
Project maintainer:
* ADAM David Alan Martin
Core Developers:
* ADAM David Alan Martin
------------------
Forked from Dillo
------------------
_________________________________________________________________
Dillo project's team
_________________________________________________________________
Project maintainer:
* Jorge Arellano Cid
Core Developers:
* Jorge Arellano Cid
* Sebastian Geerken
* Johannes Hofmann
* Place (aka corvid)
* Luca Rota
Steady developers:
* Livio Baldini
* Eric Gaudet
* Jeremy Henty
* Jörgen Viksell
Contributors:
* Lars Clausen
* Ferdi Franceschini
* Matthias Franz
* Geoff Lane
* Sammy Mannaert
* James McCollough
* Justus Winter
Patches:
* Philip Blundell
* Francis Daly
* Sam Dennis
* Melvin Hadasht
* Madis Janson
* Frank de Lange
* Andrew McPherson
* Sean 'Shaleh' Perry
* Marcos Ramírez
* Adam Sampson
* Andreas Schweitzer
* Dominic Wong
_________________________________________________________________
Web site logo:
* Eric Gaudet
* Jarrod Henry
Gzilla author:
* Raph Levien
_________________________________________________________________
-------------------
Up to gzilla-0.1.7:
-------------------
Raph Levien <raph@acm.org> is the author of gzilla.
Christoph Thompson <obituary@freshmeat.net> and Tomas O"gren <stric@ing.umu.se>
added pixmaps for the buttons and the code encessary to make them work.
They also reorganized the source tree and gave general advice and tips.
(Tomas will be pleased to find that my personal bookmarks file is no longer
hard-coded into Gzilla. :))
Ian Main <slow@intergate.bc.ca> did the bookmarks.
Thanks to Otto Hammersmith, David Mosberger-Tang, and Peter Mattis for
patches.
Contributions are always welcome!
---------------
Non-Dillo code:
---------------
* dw/fltkcomplexbutton.(cc|hh) contain code from the FLTK project whose
copyright is held by Bill Spitzak and others.
* src/binaryconst.h contains code by Tom Torfs that the author placed in the
public domain.
* src/md5.[ch] contain code by L. Peter Deutsch whose copyright is held by
Aladdin Enterprises.
* src/tipwin.cc contains code by Greg Ercolano.

674
COPYING Normal file
View File

@ -0,0 +1,674 @@
GNU GENERAL PUBLIC LICENSE
Version 3, 29 June 2007
Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
Preamble
The GNU General Public License is a free, copyleft license for
software and other kinds of works.
The licenses for most software and other practical works are designed
to take away your freedom to share and change the works. By contrast,
the GNU General Public License is intended to guarantee your freedom to
share and change all versions of a program--to make sure it remains free
software for all its users. We, the Free Software Foundation, use the
GNU General Public License for most of our software; it applies also to
any other work released this way by its authors. You can apply it to
your programs, too.
When we speak of free software, we are referring to freedom, 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
them if you wish), that you receive source code or can get it if you
want it, that you can change the software or use pieces of it in new
free programs, and that you know you can do these things.
To protect your rights, we need to prevent others from denying you
these rights or asking you to surrender the rights. Therefore, you have
certain responsibilities if you distribute copies of the software, or if
you modify it: responsibilities to respect the freedom of others.
For example, if you distribute copies of such a program, whether
gratis or for a fee, you must pass on to the recipients the same
freedoms that you received. You must make sure that they, too, receive
or can get the source code. And you must show them these terms so they
know their rights.
Developers that use the GNU GPL protect your rights with two steps:
(1) assert copyright on the software, and (2) offer you this License
giving you legal permission to copy, distribute and/or modify it.
For the developers' and authors' protection, the GPL clearly explains
that there is no warranty for this free software. For both users' and
authors' sake, the GPL requires that modified versions be marked as
changed, so that their problems will not be attributed erroneously to
authors of previous versions.
Some devices are designed to deny users access to install or run
modified versions of the software inside them, although the manufacturer
can do so. This is fundamentally incompatible with the aim of
protecting users' freedom to change the software. The systematic
pattern of such abuse occurs in the area of products for individuals to
use, which is precisely where it is most unacceptable. Therefore, we
have designed this version of the GPL to prohibit the practice for those
products. If such problems arise substantially in other domains, we
stand ready to extend this provision to those domains in future versions
of the GPL, as needed to protect the freedom of users.
Finally, every program is threatened constantly by software patents.
States should not allow patents to restrict development and use of
software on general-purpose computers, but in those that do, we wish to
avoid the special danger that patents applied to a free program could
make it effectively proprietary. To prevent this, the GPL assures that
patents cannot be used to render the program non-free.
The precise terms and conditions for copying, distribution and
modification follow.
TERMS AND CONDITIONS
0. Definitions.
"This License" refers to version 3 of the GNU General Public License.
"Copyright" also means copyright-like laws that apply to other kinds of
works, such as semiconductor masks.
"The Program" refers to any copyrightable work licensed under this
License. Each licensee is addressed as "you". "Licensees" and
"recipients" may be individuals or organizations.
To "modify" a work means to copy from or adapt all or part of the work
in a fashion requiring copyright permission, other than the making of an
exact copy. The resulting work is called a "modified version" of the
earlier work or a work "based on" the earlier work.
A "covered work" means either the unmodified Program or a work based
on the Program.
To "propagate" a work means to do anything with it that, without
permission, would make you directly or secondarily liable for
infringement under applicable copyright law, except executing it on a
computer or modifying a private copy. Propagation includes copying,
distribution (with or without modification), making available to the
public, and in some countries other activities as well.
To "convey" a work means any kind of propagation that enables other
parties to make or receive copies. Mere interaction with a user through
a computer network, with no transfer of a copy, is not conveying.
An interactive user interface displays "Appropriate Legal Notices"
to the extent that it includes a convenient and prominently visible
feature that (1) displays an appropriate copyright notice, and (2)
tells the user that there is no warranty for the work (except to the
extent that warranties are provided), that licensees may convey the
work under this License, and how to view a copy of this License. If
the interface presents a list of user commands or options, such as a
menu, a prominent item in the list meets this criterion.
1. Source Code.
The "source code" for a work means the preferred form of the work
for making modifications to it. "Object code" means any non-source
form of a work.
A "Standard Interface" means an interface that either is an official
standard defined by a recognized standards body, or, in the case of
interfaces specified for a particular programming language, one that
is widely used among developers working in that language.
The "System Libraries" of an executable work include anything, other
than the work as a whole, that (a) is included in the normal form of
packaging a Major Component, but which is not part of that Major
Component, and (b) serves only to enable use of the work with that
Major Component, or to implement a Standard Interface for which an
implementation is available to the public in source code form. A
"Major Component", in this context, means a major essential component
(kernel, window system, and so on) of the specific operating system
(if any) on which the executable work runs, or a compiler used to
produce the work, or an object code interpreter used to run it.
The "Corresponding Source" for a work in object code form means all
the source code needed to generate, install, and (for an executable
work) run the object code and to modify the work, including scripts to
control those activities. However, it does not include the work's
System Libraries, or general-purpose tools or generally available free
programs which are used unmodified in performing those activities but
which are not part of the work. For example, Corresponding Source
includes interface definition files associated with source files for
the work, and the source code for shared libraries and dynamically
linked subprograms that the work is specifically designed to require,
such as by intimate data communication or control flow between those
subprograms and other parts of the work.
The Corresponding Source need not include anything that users
can regenerate automatically from other parts of the Corresponding
Source.
The Corresponding Source for a work in source code form is that
same work.
2. Basic Permissions.
All rights granted under this License are granted for the term of
copyright on the Program, and are irrevocable provided the stated
conditions are met. This License explicitly affirms your unlimited
permission to run the unmodified Program. The output from running a
covered work is covered by this License only if the output, given its
content, constitutes a covered work. This License acknowledges your
rights of fair use or other equivalent, as provided by copyright law.
You may make, run and propagate covered works that you do not
convey, without conditions so long as your license otherwise remains
in force. You may convey covered works to others for the sole purpose
of having them make modifications exclusively for you, or provide you
with facilities for running those works, provided that you comply with
the terms of this License in conveying all material for which you do
not control copyright. Those thus making or running the covered works
for you must do so exclusively on your behalf, under your direction
and control, on terms that prohibit them from making any copies of
your copyrighted material outside their relationship with you.
Conveying under any other circumstances is permitted solely under
the conditions stated below. Sublicensing is not allowed; section 10
makes it unnecessary.
3. Protecting Users' Legal Rights From Anti-Circumvention Law.
No covered work shall be deemed part of an effective technological
measure under any applicable law fulfilling obligations under article
11 of the WIPO copyright treaty adopted on 20 December 1996, or
similar laws prohibiting or restricting circumvention of such
measures.
When you convey a covered work, you waive any legal power to forbid
circumvention of technological measures to the extent such circumvention
is effected by exercising rights under this License with respect to
the covered work, and you disclaim any intention to limit operation or
modification of the work as a means of enforcing, against the work's
users, your or third parties' legal rights to forbid circumvention of
technological measures.
4. Conveying Verbatim Copies.
You may convey verbatim copies of the Program's source code as you
receive it, in any medium, provided that you conspicuously and
appropriately publish on each copy an appropriate copyright notice;
keep intact all notices stating that this License and any
non-permissive terms added in accord with section 7 apply to the code;
keep intact all notices of the absence of any warranty; and give all
recipients a copy of this License along with the Program.
You may charge any price or no price for each copy that you convey,
and you may offer support or warranty protection for a fee.
5. Conveying Modified Source Versions.
You may convey a work based on the Program, or the modifications to
produce it from the Program, in the form of source code under the
terms of section 4, provided that you also meet all of these conditions:
a) The work must carry prominent notices stating that you modified
it, and giving a relevant date.
b) The work must carry prominent notices stating that it is
released under this License and any conditions added under section
7. This requirement modifies the requirement in section 4 to
"keep intact all notices".
c) You must license the entire work, as a whole, under this
License to anyone who comes into possession of a copy. This
License will therefore apply, along with any applicable section 7
additional terms, to the whole of the work, and all its parts,
regardless of how they are packaged. This License gives no
permission to license the work in any other way, but it does not
invalidate such permission if you have separately received it.
d) If the work has interactive user interfaces, each must display
Appropriate Legal Notices; however, if the Program has interactive
interfaces that do not display Appropriate Legal Notices, your
work need not make them do so.
A compilation of a covered work with other separate and independent
works, which are not by their nature extensions of the covered work,
and which are not combined with it such as to form a larger program,
in or on a volume of a storage or distribution medium, is called an
"aggregate" if the compilation and its resulting copyright are not
used to limit the access or legal rights of the compilation's users
beyond what the individual works permit. Inclusion of a covered work
in an aggregate does not cause this License to apply to the other
parts of the aggregate.
6. Conveying Non-Source Forms.
You may convey a covered work in object code form under the terms
of sections 4 and 5, provided that you also convey the
machine-readable Corresponding Source under the terms of this License,
in one of these ways:
a) Convey the object code in, or embodied in, a physical product
(including a physical distribution medium), accompanied by the
Corresponding Source fixed on a durable physical medium
customarily used for software interchange.
b) Convey the object code in, or embodied in, a physical product
(including a physical distribution medium), accompanied by a
written offer, valid for at least three years and valid for as
long as you offer spare parts or customer support for that product
model, to give anyone who possesses the object code either (1) a
copy of the Corresponding Source for all the software in the
product that is covered by this License, on a durable physical
medium customarily used for software interchange, for a price no
more than your reasonable cost of physically performing this
conveying of source, or (2) access to copy the
Corresponding Source from a network server at no charge.
c) Convey individual copies of the object code with a copy of the
written offer to provide the Corresponding Source. This
alternative is allowed only occasionally and noncommercially, and
only if you received the object code with such an offer, in accord
with subsection 6b.
d) Convey the object code by offering access from a designated
place (gratis or for a charge), and offer equivalent access to the
Corresponding Source in the same way through the same place at no
further charge. You need not require recipients to copy the
Corresponding Source along with the object code. If the place to
copy the object code is a network server, the Corresponding Source
may be on a different server (operated by you or a third party)
that supports equivalent copying facilities, provided you maintain
clear directions next to the object code saying where to find the
Corresponding Source. Regardless of what server hosts the
Corresponding Source, you remain obligated to ensure that it is
available for as long as needed to satisfy these requirements.
e) Convey the object code using peer-to-peer transmission, provided
you inform other peers where the object code and Corresponding
Source of the work are being offered to the general public at no
charge under subsection 6d.
A separable portion of the object code, whose source code is excluded
from the Corresponding Source as a System Library, need not be
included in conveying the object code work.
A "User Product" is either (1) a "consumer product", which means any
tangible personal property which is normally used for personal, family,
or household purposes, or (2) anything designed or sold for incorporation
into a dwelling. In determining whether a product is a consumer product,
doubtful cases shall be resolved in favor of coverage. For a particular
product received by a particular user, "normally used" refers to a
typical or common use of that class of product, regardless of the status
of the particular user or of the way in which the particular user
actually uses, or expects or is expected to use, the product. A product
is a consumer product regardless of whether the product has substantial
commercial, industrial or non-consumer uses, unless such uses represent
the only significant mode of use of the product.
"Installation Information" for a User Product means any methods,
procedures, authorization keys, or other information required to install
and execute modified versions of a covered work in that User Product from
a modified version of its Corresponding Source. The information must
suffice to ensure that the continued functioning of the modified object
code is in no case prevented or interfered with solely because
modification has been made.
If you convey an object code work under this section in, or with, or
specifically for use in, a User Product, and the conveying occurs as
part of a transaction in which the right of possession and use of the
User Product is transferred to the recipient in perpetuity or for a
fixed term (regardless of how the transaction is characterized), the
Corresponding Source conveyed under this section must be accompanied
by the Installation Information. But this requirement does not apply
if neither you nor any third party retains the ability to install
modified object code on the User Product (for example, the work has
been installed in ROM).
The requirement to provide Installation Information does not include a
requirement to continue to provide support service, warranty, or updates
for a work that has been modified or installed by the recipient, or for
the User Product in which it has been modified or installed. Access to a
network may be denied when the modification itself materially and
adversely affects the operation of the network or violates the rules and
protocols for communication across the network.
Corresponding Source conveyed, and Installation Information provided,
in accord with this section must be in a format that is publicly
documented (and with an implementation available to the public in
source code form), and must require no special password or key for
unpacking, reading or copying.
7. Additional Terms.
"Additional permissions" are terms that supplement the terms of this
License by making exceptions from one or more of its conditions.
Additional permissions that are applicable to the entire Program shall
be treated as though they were included in this License, to the extent
that they are valid under applicable law. If additional permissions
apply only to part of the Program, that part may be used separately
under those permissions, but the entire Program remains governed by
this License without regard to the additional permissions.
When you convey a copy of a covered work, you may at your option
remove any additional permissions from that copy, or from any part of
it. (Additional permissions may be written to require their own
removal in certain cases when you modify the work.) You may place
additional permissions on material, added by you to a covered work,
for which you have or can give appropriate copyright permission.
Notwithstanding any other provision of this License, for material you
add to a covered work, you may (if authorized by the copyright holders of
that material) supplement the terms of this License with terms:
a) Disclaiming warranty or limiting liability differently from the
terms of sections 15 and 16 of this License; or
b) Requiring preservation of specified reasonable legal notices or
author attributions in that material or in the Appropriate Legal
Notices displayed by works containing it; or
c) Prohibiting misrepresentation of the origin of that material, or
requiring that modified versions of such material be marked in
reasonable ways as different from the original version; or
d) Limiting the use for publicity purposes of names of licensors or
authors of the material; or
e) Declining to grant rights under trademark law for use of some
trade names, trademarks, or service marks; or
f) Requiring indemnification of licensors and authors of that
material by anyone who conveys the material (or modified versions of
it) with contractual assumptions of liability to the recipient, for
any liability that these contractual assumptions directly impose on
those licensors and authors.
All other non-permissive additional terms are considered "further
restrictions" within the meaning of section 10. If the Program as you
received it, or any part of it, contains a notice stating that it is
governed by this License along with a term that is a further
restriction, you may remove that term. If a license document contains
a further restriction but permits relicensing or conveying under this
License, you may add to a covered work material governed by the terms
of that license document, provided that the further restriction does
not survive such relicensing or conveying.
If you add terms to a covered work in accord with this section, you
must place, in the relevant source files, a statement of the
additional terms that apply to those files, or a notice indicating
where to find the applicable terms.
Additional terms, permissive or non-permissive, may be stated in the
form of a separately written license, or stated as exceptions;
the above requirements apply either way.
8. Termination.
You may not propagate or modify a covered work except as expressly
provided under this License. Any attempt otherwise to propagate or
modify it is void, and will automatically terminate your rights under
this License (including any patent licenses granted under the third
paragraph of section 11).
However, if you cease all violation of this License, then your
license from a particular copyright holder is reinstated (a)
provisionally, unless and until the copyright holder explicitly and
finally terminates your license, and (b) permanently, if the copyright
holder fails to notify you of the violation by some reasonable means
prior to 60 days after the cessation.
Moreover, your license from a particular copyright holder is
reinstated permanently if the copyright holder notifies you of the
violation by some reasonable means, this is the first time you have
received notice of violation of this License (for any work) from that
copyright holder, and you cure the violation prior to 30 days after
your receipt of the notice.
Termination of your rights under this section does not terminate the
licenses of parties who have received copies or rights from you under
this License. If your rights have been terminated and not permanently
reinstated, you do not qualify to receive new licenses for the same
material under section 10.
9. Acceptance Not Required for Having Copies.
You are not required to accept this License in order to receive or
run a copy of the Program. Ancillary propagation of a covered work
occurring solely as a consequence of using peer-to-peer transmission
to receive a copy likewise does not require acceptance. However,
nothing other than this License grants you permission to propagate or
modify any covered work. These actions infringe copyright if you do
not accept this License. Therefore, by modifying or propagating a
covered work, you indicate your acceptance of this License to do so.
10. Automatic Licensing of Downstream Recipients.
Each time you convey a covered work, the recipient automatically
receives a license from the original licensors, to run, modify and
propagate that work, subject to this License. You are not responsible
for enforcing compliance by third parties with this License.
An "entity transaction" is a transaction transferring control of an
organization, or substantially all assets of one, or subdividing an
organization, or merging organizations. If propagation of a covered
work results from an entity transaction, each party to that
transaction who receives a copy of the work also receives whatever
licenses to the work the party's predecessor in interest had or could
give under the previous paragraph, plus a right to possession of the
Corresponding Source of the work from the predecessor in interest, if
the predecessor has it or can get it with reasonable efforts.
You may not impose any further restrictions on the exercise of the
rights granted or affirmed under this License. For example, you may
not impose a license fee, royalty, or other charge for exercise of
rights granted under this License, and you may not initiate litigation
(including a cross-claim or counterclaim in a lawsuit) alleging that
any patent claim is infringed by making, using, selling, offering for
sale, or importing the Program or any portion of it.
11. Patents.
A "contributor" is a copyright holder who authorizes use under this
License of the Program or a work on which the Program is based. The
work thus licensed is called the contributor's "contributor version".
A contributor's "essential patent claims" are all patent claims
owned or controlled by the contributor, whether already acquired or
hereafter acquired, that would be infringed by some manner, permitted
by this License, of making, using, or selling its contributor version,
but do not include claims that would be infringed only as a
consequence of further modification of the contributor version. For
purposes of this definition, "control" includes the right to grant
patent sublicenses in a manner consistent with the requirements of
this License.
Each contributor grants you a non-exclusive, worldwide, royalty-free
patent license under the contributor's essential patent claims, to
make, use, sell, offer for sale, import and otherwise run, modify and
propagate the contents of its contributor version.
In the following three paragraphs, a "patent license" is any express
agreement or commitment, however denominated, not to enforce a patent
(such as an express permission to practice a patent or covenant not to
sue for patent infringement). To "grant" such a patent license to a
party means to make such an agreement or commitment not to enforce a
patent against the party.
If you convey a covered work, knowingly relying on a patent license,
and the Corresponding Source of the work is not available for anyone
to copy, free of charge and under the terms of this License, through a
publicly available network server or other readily accessible means,
then you must either (1) cause the Corresponding Source to be so
available, or (2) arrange to deprive yourself of the benefit of the
patent license for this particular work, or (3) arrange, in a manner
consistent with the requirements of this License, to extend the patent
license to downstream recipients. "Knowingly relying" means you have
actual knowledge that, but for the patent license, your conveying the
covered work in a country, or your recipient's use of the covered work
in a country, would infringe one or more identifiable patents in that
country that you have reason to believe are valid.
If, pursuant to or in connection with a single transaction or
arrangement, you convey, or propagate by procuring conveyance of, a
covered work, and grant a patent license to some of the parties
receiving the covered work authorizing them to use, propagate, modify
or convey a specific copy of the covered work, then the patent license
you grant is automatically extended to all recipients of the covered
work and works based on it.
A patent license is "discriminatory" if it does not include within
the scope of its coverage, prohibits the exercise of, or is
conditioned on the non-exercise of one or more of the rights that are
specifically granted under this License. You may not convey a covered
work if you are a party to an arrangement with a third party that is
in the business of distributing software, under which you make payment
to the third party based on the extent of your activity of conveying
the work, and under which the third party grants, to any of the
parties who would receive the covered work from you, a discriminatory
patent license (a) in connection with copies of the covered work
conveyed by you (or copies made from those copies), or (b) primarily
for and in connection with specific products or compilations that
contain the covered work, unless you entered into that arrangement,
or that patent license was granted, prior to 28 March 2007.
Nothing in this License shall be construed as excluding or limiting
any implied license or other defenses to infringement that may
otherwise be available to you under applicable patent law.
12. No Surrender of Others' Freedom.
If 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 convey a
covered work so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you may
not convey it at all. For example, if you agree to terms that obligate you
to collect a royalty for further conveying from those to whom you convey
the Program, the only way you could satisfy both those terms and this
License would be to refrain entirely from conveying the Program.
13. Use with the GNU Affero General Public License.
Notwithstanding any other provision of this License, you have
permission to link or combine any covered work with a work licensed
under version 3 of the GNU Affero General Public License into a single
combined work, and to convey the resulting work. The terms of this
License will continue to apply to the part which is the covered work,
but the special requirements of the GNU Affero General Public License,
section 13, concerning interaction through a network will apply to the
combination as such.
14. Revised Versions of this License.
The Free Software Foundation may publish revised and/or new versions of
the GNU 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
Program specifies that a certain numbered version of the GNU General
Public License "or any later version" applies to it, you have the
option of following the terms and conditions either of that numbered
version or of any later version published by the Free Software
Foundation. If the Program does not specify a version number of the
GNU General Public License, you may choose any version ever published
by the Free Software Foundation.
If the Program specifies that a proxy can decide which future
versions of the GNU General Public License can be used, that proxy's
public statement of acceptance of a version permanently authorizes you
to choose that version for the Program.
Later license versions may give you additional or different
permissions. However, no additional obligations are imposed on any
author or copyright holder as a result of your choosing to follow a
later version.
15. Disclaimer of Warranty.
THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "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 PROGRAM
IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
16. Limitation of Liability.
IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
THE PROGRAM 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 PROGRAM (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 PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
SUCH DAMAGES.
17. Interpretation of Sections 15 and 16.
If the disclaimer of warranty and limitation of liability provided
above cannot be given local legal effect according to their terms,
reviewing courts shall apply local law that most closely approximates
an absolute waiver of all civil liability in connection with the
Program, unless a warranty or assumption of liability accompanies a
copy of the Program in return for a fee.
END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Programs
If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it
free software which everyone can redistribute and change under these terms.
To do so, attach the following notices to the program. It is safest
to attach them to the start of each source file to most effectively
state 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 program's name and a brief idea of what it does.>
Copyright (C) <year> <name of author>
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 3 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, see <http://www.gnu.org/licenses/>.
Also add information on how to contact you by electronic and paper mail.
If the program does terminal interaction, make it output a short
notice like this when it starts in an interactive mode:
<program> Copyright (C) <year> <name of author>
This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
This is free software, and you are welcome to redistribute it
under certain conditions; type `show c' for details.
The hypothetical commands `show w' and `show c' should show the appropriate
parts of the General Public License. Of course, your program's commands
might be different; for a GUI interface, you would use an "about box".
You should also get your employer (if you work as a programmer) or school,
if any, to sign a "copyright disclaimer" for the program, if necessary.
For more information on this, and how to apply and follow the GNU GPL, see
<http://www.gnu.org/licenses/>.
The GNU General Public License does not permit incorporating your program
into proprietary programs. If your program is a subroutine library, you
may consider it more useful to permit linking proprietary applications with
the library. If this is what you want to do, use the GNU Lesser General
Public License instead of this License. But first, please read
<http://www.gnu.org/philosophy/why-not-lgpl.html>.

2256
ChangeLog Normal file

File diff suppressed because it is too large Load Diff

2369
Doxyfile Normal file

File diff suppressed because it is too large Load Diff

1
FUNDING.yml Normal file
View File

@ -0,0 +1 @@
liberapay: dillo

365
INSTALL Normal file
View File

@ -0,0 +1,365 @@
Installation Instructions
*************************
Copyright (C) 1994, 1995, 1996, 1999, 2000, 2001, 2002, 2004, 2005,
2006, 2007, 2008, 2009 Free Software Foundation, Inc.
Copying and distribution of this file, with or without modification,
are permitted in any medium without royalty provided the copyright
notice and this notice are preserved. This file is offered as-is,
without warranty of any kind.
Basic Installation
==================
Briefly, the shell commands `./configure; make; make install' should
configure, build, and install this package. The following
more-detailed instructions are generic; see the `README' file for
instructions specific to this package. Some packages provide this
`INSTALL' file but do not implement all of the features documented
below. The lack of an optional feature in a given package is not
necessarily a bug. More recommendations for GNU packages can be found
in *note Makefile Conventions: (standards)Makefile Conventions.
The `configure' shell script attempts to guess correct values for
various system-dependent variables used during compilation. It uses
those values to create a `Makefile' in each directory of the package.
It may also create one or more `.h' files containing system-dependent
definitions. Finally, it creates a shell script `config.status' that
you can run in the future to recreate the current configuration, and a
file `config.log' containing compiler output (useful mainly for
debugging `configure').
It can also use an optional file (typically called `config.cache'
and enabled with `--cache-file=config.cache' or simply `-C') that saves
the results of its tests to speed up reconfiguring. Caching is
disabled by default to prevent problems with accidental use of stale
cache files.
If you need to do unusual things to compile the package, please try
to figure out how `configure' could check whether to do them, and mail
diffs or instructions to the address given in the `README' so they can
be considered for the next release. If you are using the cache, and at
some point `config.cache' contains results you don't want to keep, you
may remove or edit it.
The file `configure.ac' (or `configure.in') is used to create
`configure' by a program called `autoconf'. You need `configure.ac' if
you want to change it or regenerate `configure' using a newer version
of `autoconf'.
The simplest way to compile this package is:
1. `cd' to the directory containing the package's source code and type
`./configure' to configure the package for your system.
Running `configure' might take a while. While running, it prints
some messages telling which features it is checking for.
2. Type `make' to compile the package.
3. Optionally, type `make check' to run any self-tests that come with
the package, generally using the just-built uninstalled binaries.
4. Type `make install' to install the programs and any data files and
documentation. When installing into a prefix owned by root, it is
recommended that the package be configured and built as a regular
user, and only the `make install' phase executed with root
privileges.
5. Optionally, type `make installcheck' to repeat any self-tests, but
this time using the binaries in their final installed location.
This target does not install anything. Running this target as a
regular user, particularly if the prior `make install' required
root privileges, verifies that the installation completed
correctly.
6. You can remove the program binaries and object files from the
source code directory by typing `make clean'. To also remove the
files that `configure' created (so you can compile the package for
a different kind of computer), type `make distclean'. There is
also a `make maintainer-clean' target, but that is intended mainly
for the package's developers. If you use it, you may have to get
all sorts of other programs in order to regenerate files that came
with the distribution.
7. Often, you can also type `make uninstall' to remove the installed
files again. In practice, not all packages have tested that
uninstallation works correctly, even though it is required by the
GNU Coding Standards.
8. Some packages, particularly those that use Automake, provide `make
distcheck', which can by used by developers to test that all other
targets like `make install' and `make uninstall' work correctly.
This target is generally not run by end users.
Compilers and Options
=====================
Some systems require unusual options for compilation or linking that
the `configure' script does not know about. Run `./configure --help'
for details on some of the pertinent environment variables.
You can give `configure' initial values for configuration parameters
by setting variables in the command line or in the environment. Here
is an example:
./configure CC=c99 CFLAGS=-g LIBS=-lposix
*Note Defining Variables::, for more details.
Compiling For Multiple Architectures
====================================
You can compile the package for more than one kind of computer at the
same time, by placing the object files for each architecture in their
own directory. To do this, you can use GNU `make'. `cd' to the
directory where you want the object files and executables to go and run
the `configure' script. `configure' automatically checks for the
source code in the directory that `configure' is in and in `..'. This
is known as a "VPATH" build.
With a non-GNU `make', it is safer to compile the package for one
architecture at a time in the source code directory. After you have
installed the package for one architecture, use `make distclean' before
reconfiguring for another architecture.
On MacOS X 10.5 and later systems, you can create libraries and
executables that work on multiple system types--known as "fat" or
"universal" binaries--by specifying multiple `-arch' options to the
compiler but only a single `-arch' option to the preprocessor. Like
this:
./configure CC="gcc -arch i386 -arch x86_64 -arch ppc -arch ppc64" \
CXX="g++ -arch i386 -arch x86_64 -arch ppc -arch ppc64" \
CPP="gcc -E" CXXCPP="g++ -E"
This is not guaranteed to produce working output in all cases, you
may have to build one architecture at a time and combine the results
using the `lipo' tool if you have problems.
Installation Names
==================
By default, `make install' installs the package's commands under
`/usr/local/bin', include files under `/usr/local/include', etc. You
can specify an installation prefix other than `/usr/local' by giving
`configure' the option `--prefix=PREFIX', where PREFIX must be an
absolute file name.
You can specify separate installation prefixes for
architecture-specific files and architecture-independent files. If you
pass the option `--exec-prefix=PREFIX' to `configure', the package uses
PREFIX as the prefix for installing programs and libraries.
Documentation and other data files still use the regular prefix.
In addition, if you use an unusual directory layout you can give
options like `--bindir=DIR' to specify different values for particular
kinds of files. Run `configure --help' for a list of the directories
you can set and what kinds of files go in them. In general, the
default for these options is expressed in terms of `${prefix}', so that
specifying just `--prefix' will affect all of the other directory
specifications that were not explicitly provided.
The most portable way to affect installation locations is to pass the
correct locations to `configure'; however, many packages provide one or
both of the following shortcuts of passing variable assignments to the
`make install' command line to change installation locations without
having to reconfigure or recompile.
The first method involves providing an override variable for each
affected directory. For example, `make install
prefix=/alternate/directory' will choose an alternate location for all
directory configuration variables that were expressed in terms of
`${prefix}'. Any directories that were specified during `configure',
but not in terms of `${prefix}', must each be overridden at install
time for the entire installation to be relocated. The approach of
makefile variable overrides for each directory variable is required by
the GNU Coding Standards, and ideally causes no recompilation.
However, some platforms have known limitations with the semantics of
shared libraries that end up requiring recompilation when using this
method, particularly noticeable in packages that use GNU Libtool.
The second method involves providing the `DESTDIR' variable. For
example, `make install DESTDIR=/alternate/directory' will prepend
`/alternate/directory' before all installation names. The approach of
`DESTDIR' overrides is not required by the GNU Coding Standards, and
does not work on platforms that have drive letters. On the other hand,
it does better at avoiding recompilation issues, and works well even
when some directory options were not specified in terms of `${prefix}'
at `configure' time.
Optional Features
=================
If the package supports it, you can cause programs to be installed
with an extra prefix or suffix on their names by giving `configure' the
option `--program-prefix=PREFIX' or `--program-suffix=SUFFIX'.
Some packages pay attention to `--enable-FEATURE' options to
`configure', where FEATURE indicates an optional part of the package.
They may also pay attention to `--with-PACKAGE' options, where PACKAGE
is something like `gnu-as' or `x' (for the X Window System). The
`README' should mention any `--enable-' and `--with-' options that the
package recognizes.
For packages that use the X Window System, `configure' can usually
find the X include and library files automatically, but if it doesn't,
you can use the `configure' options `--x-includes=DIR' and
`--x-libraries=DIR' to specify their locations.
Some packages offer the ability to configure how verbose the
execution of `make' will be. For these packages, running `./configure
--enable-silent-rules' sets the default to minimal output, which can be
overridden with `make V=1'; while running `./configure
--disable-silent-rules' sets the default to verbose, which can be
overridden with `make V=0'.
Particular systems
==================
On HP-UX, the default C compiler is not ANSI C compatible. If GNU
CC is not installed, it is recommended to use the following options in
order to use an ANSI C compiler:
./configure CC="cc -Ae -D_XOPEN_SOURCE=500"
and if that doesn't work, install pre-built binaries of GCC for HP-UX.
On OSF/1 a.k.a. Tru64, some versions of the default C compiler cannot
parse its `<wchar.h>' header file. The option `-nodtk' can be used as
a workaround. If GNU CC is not installed, it is therefore recommended
to try
./configure CC="cc"
and if that doesn't work, try
./configure CC="cc -nodtk"
On Solaris, don't put `/usr/ucb' early in your `PATH'. This
directory contains several dysfunctional programs; working variants of
these programs are available in `/usr/bin'. So, if you need `/usr/ucb'
in your `PATH', put it _after_ `/usr/bin'.
On Haiku, software installed for all users goes in `/boot/common',
not `/usr/local'. It is recommended to use the following options:
./configure --prefix=/boot/common
Specifying the System Type
==========================
There may be some features `configure' cannot figure out
automatically, but needs to determine by the type of machine the package
will run on. Usually, assuming the package is built to be run on the
_same_ architectures, `configure' can figure that out, but if it prints
a message saying it cannot guess the machine type, give it the
`--build=TYPE' option. TYPE can either be a short name for the system
type, such as `sun4', or a canonical name which has the form:
CPU-COMPANY-SYSTEM
where SYSTEM can have one of these forms:
OS
KERNEL-OS
See the file `config.sub' for the possible values of each field. If
`config.sub' isn't included in this package, then this package doesn't
need to know the machine type.
If you are _building_ compiler tools for cross-compiling, you should
use the option `--target=TYPE' to select the type of system they will
produce code for.
If you want to _use_ a cross compiler, that generates code for a
platform different from the build platform, you should specify the
"host" platform (i.e., that on which the generated programs will
eventually be run) with `--host=TYPE'.
Sharing Defaults
================
If you want to set default values for `configure' scripts to share,
you can create a site shell script called `config.site' that gives
default values for variables like `CC', `cache_file', and `prefix'.
`configure' looks for `PREFIX/share/config.site' if it exists, then
`PREFIX/etc/config.site' if it exists. Or, you can set the
`CONFIG_SITE' environment variable to the location of the site script.
A warning: not all `configure' scripts look for a site script.
Defining Variables
==================
Variables not defined in a site shell script can be set in the
environment passed to `configure'. However, some packages may run
configure again during the build, and the customized values of these
variables may be lost. In order to avoid this problem, you should set
them in the `configure' command line, using `VAR=value'. For example:
./configure CC=/usr/local2/bin/gcc
causes the specified `gcc' to be used as the C compiler (unless it is
overridden in the site shell script).
Unfortunately, this technique does not work for `CONFIG_SHELL' due to
an Autoconf bug. Until the bug is fixed you can use this workaround:
CONFIG_SHELL=/bin/bash /bin/bash ./configure CONFIG_SHELL=/bin/bash
`configure' Invocation
======================
`configure' recognizes the following options to control how it
operates.
`--help'
`-h'
Print a summary of all of the options to `configure', and exit.
`--help=short'
`--help=recursive'
Print a summary of the options unique to this package's
`configure', and exit. The `short' variant lists options used
only in the top level, while the `recursive' variant lists options
also present in any nested packages.
`--version'
`-V'
Print the version of Autoconf used to generate the `configure'
script, and exit.
`--cache-file=FILE'
Enable the cache: use and save the results of the tests in FILE,
traditionally `config.cache'. FILE defaults to `/dev/null' to
disable caching.
`--config-cache'
`-C'
Alias for `--cache-file=config.cache'.
`--quiet'
`--silent'
`-q'
Do not print messages saying which checks are being made. To
suppress all normal output, redirect it to `/dev/null' (any error
messages will still be shown).
`--srcdir=DIR'
Look for the package's source code in directory DIR. Usually
`configure' can determine that directory automatically.
`--prefix=DIR'
Use DIR as the installation prefix. *note Installation Names::
for more details, including other options available for fine-tuning
the installation locations.
`--no-create'
`-n'
Run the configure checks, but stop before creating any output
files.
`configure' also accepts some other, not widely useful, options. Run
`configure --help' for more details.

18
Makefile.am Normal file
View File

@ -0,0 +1,18 @@
SUBDIRS = lout dw dlib dpip src doc dpid dpi test
dist_bin_SCRIPTS = dillo-install-hyphenation
EXTRA_DIST = README.md Doxyfile dillorc install-dpi-local d_size.h
sysconf_DATA = dillorc
desktopdir = $(datadir)/applications
desktop_DATA = dillo.desktop
appicon48dir = $(datadir)/icons/hicolor/48x48/apps/
appicon48_DATA = icons/48x48/dillo.png
appicon128dir = $(datadir)/icons/hicolor/128x128/apps/
appicon128_DATA = icons/128x128/dillo.png
EXTRA_DIST += $(desktop_DATA) $(appicon48_DATA) $(appicon128_DATA)

57
NEWS Normal file
View File

@ -0,0 +1,57 @@
----
NEWS
----
Nov 2000:
Introduced a new design layer between the IO and the Dw.
March 2001:
Finally the new dillo widget is ready! (dillo >= 0.4.0).
0.4.0 is able to cope with low resolution depths.
Apr 2002:
We moved to: http://dillo.cipsga.org.br/
Dec 2002
We moved to: http://dillo.auriga.wearlab.de/
Jun 2003
http://www.dillo.org/ (hosted at the wearlab!)
Sep 2007
The new FLTK2-based dillo code is released! (under GPL3)
Jul 2011
Dillo switches to fltk-1.3.0.
dillo-2.2.1, the last of the 2.x series is released.
Sep 2011
dillo-3.0, the first of the 3.x series is released.
dillo-3.0.1, a bugfix release for the new 3.x series is out.
Mar 2013
dillo-3.0.3, a new release for the new 3.x series is out.
Apr 2014
dillo-3.0.4, a new release for the new 3.x series is out.
Dec 2014
dillo-3.0.4.1, a new release for the new 3.x series is out.
Jorge.-
jcid@dillo.org
Project maintainer, core developer, patcher, you name it! :-)

1
README Symbolic link
View File

@ -0,0 +1 @@
README.md

36
README.dillo.md Normal file
View File

@ -0,0 +1,36 @@
# Dillo web browser
Dillo is a multi-platform graphical web browser, known for its speed and
small footprint, that is developed with a focus on personal security and
privacy. It is built with the [FLTK 1.3 GUI toolkit](http://fltk.org).
Screenshot of the [Dillo Website][dillo] rendered in Dillo:
[dillo]: https://dillo-browser.github.io/
<p align="center"><img src="doc/dillo.png" width="60%" /></p>
To install Dillo follow the [installation guide](doc/install.md).
This repository contains mostly the original code of Dillo with some
minor patches. Additional patches or pull requests are welcome.
See also other related forks: [dillo-plus][dillo-plus],
[dilloNG][dilloNG], [D+ browser][dplus-browser] and [Mobilized
Dillo][mobilized].
[dillo-plus]: https://github.com/crossbowerbt/dillo-plus
[dilloNG]: https://github.com/w00fpack/dilloNG
[dplus-browser]: https://sourceforge.net/projects/dplus-browser/
[mobilized]: https://www.toomanyatoms.com/software/mobilized_dillo.html
> [!WARNING]
> As of December 2023, the host `dillo.org` is [no longer under control][gone]
> of Dillo developers. A copy of the old website is archived in
> [GitHub Pages][old] and the [Wayback Machine (May 2022)][may].
[gone]: https://dillo-browser.github.io/dillo.org.html
[old]: https://dillo-browser.github.io/old/
[may]: http://web.archive.org/web/20220508022123/https://www.dillo.org/
Forked from dillo on the fbd719f93ab659fec6c42952e76f5e5b971728be commit.

59
autogen.sh Executable file
View File

@ -0,0 +1,59 @@
#!/bin/sh
#
# Script to generate configure&make stuff
#
#-----------------------------------------------------
# If defined, get these programs from the environment
#
: ${ACLOCAL:=aclocal}
: ${AUTOHEADER:=autoheader}
: ${AUTOCONF:=autoconf}
: ${AUTOMAKE:=automake}
#-------------------------
# Required binaries check
#
check_bin_file(){
command -v $1 > /dev/null 2>&1
if [ $? = 0 ]; then
return 0
else
return 1
fi
}
#------
# Main
#
#clear
ERR="no"
for cmd in "$ACLOCAL" "$AUTOHEADER" "$AUTOCONF" "$AUTOMAKE"
do
if check_bin_file "$cmd"
then
echo -e "$cmd \tfound"
else
echo -e "$cmd \tNOT found"
ERR="yes"
fi
done
if test $ERR = "yes"
then
echo
echo "ERROR: to run this program you need the following installed"
echo " $ACLOCAL $AUTOHEADER $AUTOCONF $AUTOMAKE"
echo
exit 1
fi
echo "[Checks passed]"
echo "Generating..."
"$ACLOCAL"
"$AUTOHEADER"
"$AUTOCONF"
"$AUTOMAKE" -a

798
configure.ac Normal file
View File

@ -0,0 +1,798 @@
dnl Process this file with aclocal, autoconf and automake.
AC_INIT([dillo], [3.2.0])
dnl Detect the canonical target build environment
AC_CANONICAL_TARGET
AM_INIT_AUTOMAKE
AC_CONFIG_SRCDIR([src/dillo.cc])
AC_CONFIG_HEADERS([config.h])
sysconfdir=${sysconfdir}/${PACKAGE}
dnl Options
AC_ARG_WITH([jpeg-lib],
[AS_HELP_STRING([--with-jpeg-lib=DIR], [Specify where to find libjpeg])],
LIBJPEG_LIBDIR=$withval)
AC_ARG_WITH([jpeg-inc],
[AS_HELP_STRING([--with-jpeg-inc=DIR], [Specify where to find libjpeg headers])],
LIBJPEG_INCDIR=$withval)
AC_ARG_ENABLE([efence],
[AS_HELP_STRING([--enable-efence], [Try to compile and run with Electric Fence])],
[enable_efence=$enableval],
[enable_efence=no])
AC_ARG_ENABLE([gprof],
[AS_HELP_STRING([--enable-gprof], [Try to compile and run with profiling enabled])],
[enable_gprof=$enableval],
[enable_gprof=no])
AC_ARG_ENABLE([insure],
[AS_HELP_STRING([--enable-insure], [Try to compile and run with Insure++])],
[enable_insure=$enableval],
[enable_insure=no])
AC_ARG_ENABLE([ipv6],
[AS_HELP_STRING([--enable-ipv6], [Build with support for IPv6])],
[enable_ipv6=$enableval],
[enable_ipv6=no])
AC_ARG_ENABLE([cookies],
[AS_HELP_STRING([--disable-cookies], [Dont compile support for cookies])],
[enable_cookies=$enableval],
[enable_cookies=yes])
AC_ARG_ENABLE([png],
[AS_HELP_STRING([--disable-png], [Disable support for PNG images])],
[enable_png=$enableval],
[enable_png=yes])
AC_ARG_ENABLE([webp],
[AS_HELP_STRING([--disable-webp], [Disable support for WEBP images])],
[enable_webp=$enableval],
[enable_webp=yes])
AC_ARG_ENABLE([jpeg],
[AS_HELP_STRING([--disable-jpeg], [Disable support for JPEG images])],
[enable_jpeg=$enableval],
[enable_jpeg=yes])
AC_ARG_ENABLE([gif],
[AS_HELP_STRING([--disable-gif], [Disable support for GIF images])],
[enable_gif=$enableval],
[enable_gif=yes])
AC_ARG_ENABLE([svg],
[AS_HELP_STRING([--disable-svg], [Disable support for SVG images])],
[enable_svg=$enableval],
[enable_svg=yes])
AC_ARG_ENABLE([threaded-dns],
[AS_HELP_STRING([--disable-threaded-dns], [Disable the advantage of a reentrant resolver library])],
[enable_threaded_dns=$enableval],
[enable_threaded_dns=yes])
AC_ARG_ENABLE([rtfl],
[AS_HELP_STRING([--enable-rtfl], [Print low-level RTFL messages for debugging the renderer (very large slowdown)])],
[enable_rtfl=$enableval],
[enable_rtfl=no])
AC_ARG_ENABLE([xembed],
[AS_HELP_STRING([--disable-xembed], [Disable support for X11 XEmbed])],
[enable_xembed=$enableval],
[enable_xembed=yes])
AC_ARG_ENABLE([tls],
[AS_HELP_STRING([--disable-tls], [Disable TLS support (for HTTPS)])],
[enable_tls=$enableval],
[enable_tls=yes])
dnl Deprecated, if given it will cause an error
AC_ARG_ENABLE([ssl],
[AS_HELP_STRING([--disable-ssl], [Disable TLS support (deprecated, use --disable-tls)])],
[enable_ssl=$enableval],
[enable_ssl=])
AC_ARG_ENABLE([openssl],
[AS_HELP_STRING([--disable-openssl], [Disable support for OpenSSL])],
[enable_openssl=$enableval],
[enable_openssl=yes])
AC_ARG_ENABLE([mbedtls],
[AS_HELP_STRING([--disable-mbedtls], [Disable support for mbedTLS])],
[enable_mbedtls=$enableval],
[enable_mbedtls=yes])
AC_ARG_WITH([ca-certs-file],
[AS_HELP_STRING([--with-ca-certs-file=FILE], [Specify where to find a bundle of trusted CA certificates for TLS])],
CA_CERTS_FILE=$withval)
AC_ARG_WITH([ca-certs-dir],
[AS_HELP_STRING([--with-ca-certs-dir=DIR], [Specify where to find a directory containing trusted CA certificates for TLS])],
CA_CERTS_DIR=$withval)
AC_ARG_ENABLE([html-tests],
[AS_HELP_STRING([--enable-html-tests], [Enable HTML tests (requires xvfb, xwd and imagemagick)])],
[enable_html_tests=$enableval],
[enable_html_tests=no])
if test "x$enable_ssl" = "xno"; then
AC_MSG_ERROR([The flag --disable-ssl is deprecated, use --disable-tls])
fi
dnl Also catch --enable-ssl=maybe
if test "x$enable_ssl" != "x"; then
AC_MSG_ERROR([The flag --enable-ssl is deprecated, use --enable-tls])
fi
AC_PROG_CC
AC_PROG_CXX
AC_PROG_RANLIB
AC_PROG_CPP
AC_MSG_CHECKING(if C++ compiler '$CXX' works)
AC_LANG_PUSH([C++])
AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[
#ifndef __cplusplus
#error "No C++ support, AC_PROG_CXX failed"
#endif
]])],
[AC_MSG_RESULT([yes])],
[AC_MSG_RESULT(no)
AC_MSG_FAILURE([C++ compiler doesn't work])]
)
AC_LANG_POP([C++])
dnl ----------------------------
dnl Check our char and int types
dnl ----------------------------
dnl
AC_CHECK_SIZEOF(char)
AC_CHECK_SIZEOF(short)
AC_CHECK_SIZEOF(long)
AC_CHECK_SIZEOF(int)
AC_CHECK_SIZEOF(void *)
AC_TYPE_INT16_T
AC_TYPE_UINT16_T
AC_TYPE_INT32_T
AC_TYPE_UINT32_T
dnl -----------------------------------------------------------------
dnl Check for absolute path of working directory.
dnl This is needed for RTFL, to get full the full paths of the source
dnl file names
dnl -----------------------------------------------------------------
dnl
BASE_CUR_WORKING_DIR=`pwd`
dnl ------------------------------------
dnl Check for socket libs (AIX, Solaris)
dnl ------------------------------------
dnl
AC_CHECK_FUNCS(gethostbyname,,
[AC_CHECK_LIB(nsl,gethostbyname,,[AC_CHECK_LIB(socket,gethostbyname)])])
AC_CHECK_FUNCS(setsockopt,,[AC_CHECK_LIB(socket,setsockopt)])
dnl --------------------
dnl Checks for socklen_t
dnl --------------------
dnl
AC_MSG_CHECKING([for socklen_t])
ac_cv_socklen_t=""
AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[
#include <sys/types.h>
#include <sys/socket.h>
]],[[
socklen_t a=0;
getsockname(0,(struct sockaddr*)0, &a);
]])],
ac_cv_socklen_t="socklen_t",
AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[
#include <sys/types.h>
#include <sys/socket.h>
]],[[
int a=0;
getsockname(0,(struct sockaddr*)0, &a);
]])],
ac_cv_socklen_t="int",
ac_cv_socklen_t="size_t"
)
)
AC_MSG_RESULT($ac_cv_socklen_t)
if test "$ac_cv_socklen_t" != "socklen_t"; then
AC_DEFINE_UNQUOTED([socklen_t], [$ac_cv_socklen_t],
[Define the real type of socklen_t])
fi
dnl -------------------------
dnl Test for FLTK 1.3 library
dnl -------------------------
dnl
dnl For debugging and to be user friendly
AC_PATH_PROG(FLTK_CONFIG,fltk-config)
AC_MSG_CHECKING([FLTK 1.3])
fltk_version="`$FLTK_CONFIG --version 2>/dev/null`"
case $fltk_version in
1.3.*) AC_MSG_RESULT(yes)
LIBFLTK_CXXFLAGS=`$FLTK_CONFIG --cxxflags`
LIBFLTK_CFLAGS=`$FLTK_CONFIG --cflags`
LIBFLTK_LIBS=`$FLTK_CONFIG --ldflags`;;
1.4.*) AC_MSG_RESULT(no)
AC_MSG_ERROR([FLTK $fltk_version not supported yet; use FLTK 1.3]);;
?*) AC_MSG_RESULT(no)
AC_MSG_ERROR(FLTK 1.3 required; version found: $fltk_version);;
*) AC_MSG_RESULT(no)
AC_MSG_ERROR(FLTK 1.3 required; fltk-config not found)
esac
dnl -----------------------------------
dnl Test for X11 (only on some systems)
dnl -----------------------------------
AC_MSG_CHECKING([whether to link to X11])
AC_LANG_PUSH([C++])
old_libs=$LIBS
old_cxxflags=$CXXFLAGS
LIBS=$LIBFLTK_LIBS
CXXFLAGS=$LIBFLTK_CXXFLAGS
AC_RUN_IFELSE([AC_LANG_PROGRAM([[
#define FL_INTERNALS
#include <FL/x.H>
]],[[
#ifdef X_PROTOCOL
return 0;
#else
return 1;
#endif
]])], [AC_MSG_RESULT(yes)
LIBX11_LIBS="-lX11"],
[AC_MSG_RESULT(no)],
[AC_MSG_RESULT(no)
AC_MSG_WARN([*** Test for X11 not possible when cross-compiling. ***])])
CXXFLAGS=$old_cxxflags
LIBS=$old_libs
AC_LANG_POP([C++])
dnl ----------------
dnl Test for libjpeg
dnl ----------------
dnl
jpeg_ok=no
if test "x$enable_jpeg" = "xyes"; then
AC_CHECK_HEADER(jpeglib.h, jpeg_ok=yes, jpeg_ok=no)
if test "x$jpeg_ok" = "xyes"; then
old_libs="$LIBS"
AC_CHECK_LIB(jpeg, jpeg_destroy_decompress, jpeg_ok=yes, jpeg_ok=no)
LIBS="$old_libs"
fi
if test "x$jpeg_ok" = "xyes"; then
LIBJPEG_LIBS="-ljpeg"
if test -n "$LIBJPEG_LIBDIR"; then
LIBJPEG_LDFLAGS="-L$LIBJPEG_LIBDIR"
fi
if test -n "$LIBJPEG_INCDIR"; then
LIBJPEG_CPPFLAGS="-I$LIBJPEG_INCDIR"
fi
else
AC_MSG_WARN([*** No libjpeg found. Disabling jpeg images.***])
fi
fi
if test "x$jpeg_ok" = "xyes"; then
AC_DEFINE([ENABLE_JPEG], [1], [Enable JPEG images])
fi
dnl ----------------
dnl Test for libwebp
dnl ----------------
dnl
if test "x$enable_webp" = "xyes"; then
AC_CHECK_HEADER(webp/decode.h, webp_ok=yes, webp_ok=no)
if test "x$webp_ok" = "xyes"; then
old_libs="$LIBS"
AC_CHECK_LIB(webp, WebPGetInfo, webp_ok=yes, webp_ok=no)
LIBS="$old_libs"
fi
if test "x$webp_ok" = "xyes"; then
LIBWEBP_LIBS="-lwebp"
else
AC_MSG_WARN([*** No libwebp found. Disabling webp images.***])
fi
fi
if test "x$webp_ok" = "xyes"; then
AC_DEFINE([ENABLE_WEBP], [1], [Enable webp images])
fi
dnl -------------
dnl Test for zlib
dnl -------------
dnl
AC_CHECK_HEADER(zlib.h, libz_ok=yes, libz_ok=no)
if test "x$libz_ok" = "xyes"; then
old_libs="$LIBS"
AC_CHECK_LIB(z, zlibVersion, libz_ok=yes, libz_ok=no)
LIBS="$old_libs"
fi
if test "x$libz_ok" = xyes; then
LIBZ_LIBS="-lz"
else
AC_MSG_ERROR(zlib must be installed!)
fi
dnl ---------------
dnl Test for libpng
dnl ---------------
dnl
png_ok="no"
if test "x$enable_png" = "xyes"; then
AC_MSG_CHECKING([for libpng-config])
dnl Check if the user hasn't set the variable $PNG_CONFIG
if test -z "$PNG_CONFIG"; then
PNG_CONFIG=`command -v libpng16-config`
if test -z "$PNG_CONFIG"; then
PNG_CONFIG=`command -v libpng14-config`
fi
if test -z "$PNG_CONFIG"; then
PNG_CONFIG=`command -v libpng12-config`
fi
if test -z "$PNG_CONFIG"; then
PNG_CONFIG=`command -v libpng-config`
fi
if test -z "$PNG_CONFIG"; then
PNG_CONFIG=`command -v libpng10-config`
fi
fi
dnl Check if the libpng-config script was found and is executable
if test -n "$PNG_CONFIG" && test -x "$PNG_CONFIG"; then
AC_MSG_RESULT([$PNG_CONFIG])
png_ok="yes"
else
AC_MSG_RESULT([missing])
png_ok="no"
fi
if test "x$png_ok" = "xyes"; then
dnl For debugging and to be user friendly
AC_MSG_CHECKING([for libpng version])
png_version=`$PNG_CONFIG --version`
case $png_version in
1.[[0246]].*) AC_MSG_RESULT([$png_version]) ;;
*) AC_MSG_RESULT([$png_version (unrecognised version)]) ;;
esac
dnl Try to use options that are supported by all libpng-config versions...
LIBPNG_CFLAGS=`$PNG_CONFIG --cflags`
LIBPNG_LIBS=`$PNG_CONFIG --ldflags`
case $png_version in
1.2.4*) LIBPNG_LIBS="$LIBPNG_LIBS `$PNG_CONFIG --libs`" ;;
esac
else
dnl Try to find libpng even though libpng-config wasn't found
AC_CHECK_HEADERS(png.h libpng/png.h, png_ok=yes && break, png_ok=no)
if test "x$png_ok" = "xyes"; then
old_libs="$LIBS"
AC_CHECK_LIB(png, png_sig_cmp, png_ok=yes, png_ok=no, $LIBZ_LIBS -lm)
LIBS="$old_libs"
if test "x$png_ok" = "xyes"; then
LIBPNG_LIBS="-lpng -lm"
fi
fi
if test "x$png_ok" = "xno"; then
AC_MSG_WARN([*** No libpng found. Disabling PNG images ***])
fi
fi
fi
if test "x$png_ok" = "xyes"; then
AC_DEFINE([ENABLE_PNG], [1], [Enable PNG images])
fi
dnl Check if support for GIF images should be compiled in
if test "x$enable_gif" = "xyes"; then
AC_DEFINE([ENABLE_GIF], [1], [Enable GIF images])
fi
dnl Check if support for SVG images should be compiled in
if test "x$enable_svg" = "xyes"; then
AC_DEFINE([ENABLE_SVG], [1], [Enable SVG images])
else
AC_MSG_NOTICE([Disabling SVG])
fi
dnl --------------------------
dnl Test for support for SSL/TLS
dnl --------------------------
dnl
tls_ok="no"
tls_impl="none"
if test "x$enable_tls" = "xyes"; then
if test "x$enable_openssl" = "xyes"; then
dnl Search for OpenSSL headers first
AC_CHECK_HEADER(openssl/ssl.h, openssl_ok=yes, openssl_ok=no)
dnl If the headers are found, try to link with -lssl and -lcrypto
if test "x$openssl_ok" = "xyes"; then
old_libs="$LIBS"
AC_CHECK_LIB(ssl, SSL_write, openssl_ok=yes, openssl_ok=no, -lcrypto)
LIBS="$old_libs"
fi
dnl If all went good, set OpenSSL
if test "x$openssl_ok" = "xyes"; then
AC_MSG_NOTICE([Using OpenSSL as TLS library.])
tls_impl="OpenSSL"
AC_DEFINE([HAVE_OPENSSL], [1], [OpenSSL works])
LIBSSL_LIBS="-lcrypto -lssl"
else
AC_MSG_NOTICE([Cannot find OpenSSL, trying mbedTLS...])
fi
else
AC_MSG_NOTICE([Skipping OpenSSL search, as it is disabled])
fi
dnl Try to find mbedTLS if OpenSSL failed or is disabled
if test "x$enable_mbedtls" = "xyes"; then
if test "x$openssl_ok" != "xyes"; then
dnl In mbed TLS 2.3.0, ssl.h needs platform.h but fails to include it.
AC_CHECK_HEADER(mbedtls/ssl.h, mbedtls_ok=yes, mbedtls_ok=no, [#include <mbedtls/platform.h>])
dnl If the headers are found, try to link with mbedTLS
if test "x$mbedtls_ok" = "xyes"; then
old_libs="$LIBS"
AC_CHECK_LIB(mbedtls, mbedtls_ssl_init, mbedtls_ok=yes, mbedtls_ok=no, -lmbedx509 -lmbedcrypto)
LIBS="$old_libs"
fi
dnl If it went good, use it, otherwise disable TLS support
if test "x$mbedtls_ok" = "xyes"; then
AC_MSG_NOTICE([Using mbedTLS as TLS library.])
tls_impl="mbedTLS"
AC_DEFINE([HAVE_MBEDTLS], [1], [mbedTLS works])
LIBSSL_LIBS="-lmbedtls -lmbedx509 -lmbedcrypto"
else
AC_MSG_NOTICE([Cannot find mbedTLS])
fi
fi
else
AC_MSG_NOTICE([Skipping mbedTLS search, as it is disabled])
fi
dnl Only need one that works
if test "x$openssl_ok" = "xyes" -o "x$mbedtls_ok" = "xyes"; then
tls_ok="yes"
AC_DEFINE([ENABLE_TLS], [1], [Enable TLS support])
else
AC_MSG_ERROR([No TLS library available])
fi
fi
AM_CONDITIONAL([USE_OPENSSL], [test "x$openssl_ok" = "xyes"])
AM_CONDITIONAL([USE_MBEDTLS], [test "x$mbedtls_ok" = "xyes"])
dnl ----------------------------------------------------------------
dnl Test for iconv implementation first in libiconv and then in libc
dnl ----------------------------------------------------------------
AC_CHECK_HEADER(iconv.h, iconv_ok=yes, iconv_ok=no)
if test "x$iconv_ok" = "xyes"; then
dnl First try to link with the libiconv library if available, otherwise
dnl fall back to the implementation of libc. It is required to be done
dnl in this order because FreeBSD distributes libiconv headers in
dnl /usr/include/local/iconv.h, in the same place the headers for FLTK
dnl are placed. If we select to link with libc, the headers provided by
dnl libiconv will be used while linking with libc. This causes a linkage
dnl error.
AC_CHECK_LIB(iconv, iconv_open, LIBICONV_LIBS="-liconv",
AC_CHECK_LIB(c, iconv_open, LIBICONV_LIBS="", iconv_ok=no))
fi
if test "x$iconv_ok" = "xno"; then
dnl Test for OpenBSD
old_libs="$LIBS"
LIBS="$old_libs -liconv"
AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[
#include <iconv.h>
]],[[
iconv_open("","");
]])],
iconv_ok=yes,iconv_ok=no)
LIBS="$old_libs"
if test "x$iconv_ok" = "xyes"; then
LIBICONV_LIBS="-liconv"
fi
fi
if test "x$iconv_ok" = "xno"; then
AC_MSG_ERROR(libiconv must be installed!)
fi
dnl ----------------------
dnl Check if we need to
dnl support the old
dnl iconv interface
dnl ----------------------
if test "x$iconv_ok" = "xyes"; then
old_libs="$LIBS"
LIBS="$old_libs $LIBICONV_LIBS"
old_cflags="$CFLAGS"
CFLAGS="$CFLAGS -Werror"
AC_LANG_PUSH([C++])
AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[
#include <iconv.h>
]],[[
const char *inPtr;
char *outPtr;
size_t inLeft = 0, outRoom = 0;
iconv_t encoder = iconv_open("ASCII", "UTF-8");
iconv(encoder, &inPtr, &inLeft, &outPtr, &outRoom);
]])],
iconv_old=yes,iconv_old=no)
AC_LANG_POP([C++])
LIBS="$old_libs"
CFLAGS="$old_cflags"
if test "x$iconv_old" = "xyes"; then
AC_DEFINE([inbuf_t], [const char], [Use const char pointers for older libiconv])
else
AC_DEFINE([inbuf_t], [char], [Use char pointers for newer libiconv])
fi
fi
dnl ----------------------
dnl Test for POSIX threads
dnl ----------------------
dnl
if test -z "$LIBPTHREAD_LIBS"; then
case $target in
*-*-linux*|*-*-solaris*)
old_libs="$LIBS"
AC_CHECK_LIB(pthread, pthread_create, LIBPTHREAD_LIBS="-lpthread")
LIBS="$old_libs"
;;
*-*-osf1*)
AC_MSG_CHECKING(whether pthreads work)
LIBPTHREAD_LIBS="-lpthread -lexc -ldb"
AC_MSG_WARN([*** _Untested pthreads_ try setting LIBPTHREAD_LIBS manually if it doesn't work ***])
;;
*-*-minix*)
AC_MSG_NOTICE([Minix detected, skipping pthread detection])
;;
*)
AC_MSG_CHECKING(whether threads work with -pthread)
LDSAVEFLAGS=$LDFLAGS
LDFLAGS="$LDFLAGS -pthread"
AC_LINK_IFELSE([AC_LANG_CALL([],[pthread_create])],
pthread_ok=yes, pthread_ok=no)
LDFLAGS=$LDSAVEFLAGS
if test "x$pthread_ok" = "xyes"; then
AC_MSG_RESULT(yes)
LIBPTHREAD_LDFLAGS="-pthread"
else
AC_MSG_RESULT(no. Now we will try some libraries.)
AC_SEARCH_LIBS(pthread_create, pthread,
LIBPTHREADS_LIBS="-lpthread",
AC_SEARCH_LIBS(pthread_create, pthreads,
LIBPTHREADS_LIBS="-lpthreads",
AC_SEARCH_LIBS(pthread_create, c_r,
LIBPTHREADS_LIBS="-lc_r", thread_ok=no)))
if test "x$thread_ok" = "xno"; then
AC_MSG_WARN([*** No pthreads found. ***])
AC_MSG_ERROR([*** Try setting LIBPTHREAD_LIBS manually to point to your pthreads library. ***])
exit 1
else
AC_MSG_WARN([found a way to link threads, but it may not work...])
fi
fi
;;
esac
fi
dnl ----------
dnl HTML tests
dnl ----------
dnl
html_tests_ok=no
if test "x$enable_html_tests" = "xyes" ; then
html_tests_ok=yes
AC_CHECK_PROG(xvfb_ok, Xvfb, yes, no)
AC_CHECK_PROG(xwd_ok, xwd, yes, no)
AC_CHECK_PROG(xwininfo_ok, xwininfo, yes, no)
AC_CHECK_PROG(convert_ok, convert, yes, no)
if test "x$xvfb_ok" != "xyes"; then
html_tests_ok=no
fi
if test "x$xwd_ok" != "xyes"; then
html_tests_ok=no
fi
if test "x$xwininfo_ok" != "xyes"; then
html_tests_ok=no
fi
if test "x$convert_ok" != "xyes"; then
html_tests_ok=no
fi
if test "x$html_tests_ok" != "xyes"; then
AC_MSG_ERROR([Cannot find all tools to enable HTML tests])
fi
fi
AM_CONDITIONAL([ENABLE_HTML_TESTS], [test "x$html_tests_ok" = "xyes"])
dnl --------------------
dnl Command line options
dnl --------------------
dnl
if test "x$enable_cookies" = "xno" ; then
CFLAGS="$CFLAGS -DDISABLE_COOKIES"
CXXFLAGS="$CXXFLAGS -DDISABLE_COOKIES"
fi
if test "x$enable_ipv6" = "xyes" ; then
CFLAGS="$CFLAGS -DENABLE_IPV6"
fi
if test "x$enable_efence" = "xyes" ; then
LIBS="-lefence $LIBS"
fi
if test "x$enable_gprof" = "xyes" ; then
CFLAGS="$CFLAGS -pg"
CXXFLAGS="$CXXFLAGS -pg"
fi
if test "x$enable_insure" = "xyes" ; then
CC="insure -Zoi \"compiler $CC\""
LIBS="$LIBS -lstdc++-2-libc6.1-1-2.9.0"
fi
if test "x$enable_threaded_dns" = "xyes" ; then
CFLAGS="$CFLAGS -DD_DNS_THREADED"
fi
if test "x$enable_rtfl" = "xyes" ; then
CXXFLAGS="$CXXFLAGS -DDBG_RTFL"
fi
if test "x$enable_xembed" = "xno" ; then
CXXFLAGS="$CFLAGS -DDISABLE_XEMBED"
fi
dnl -----------------------
dnl Checks for header files
dnl -----------------------
dnl
AC_CHECK_HEADERS(fcntl.h unistd.h sys/uio.h)
dnl --------------------------
dnl Check for compiler options
dnl --------------------------
dnl
if eval "test x$GCC = xyes"; then
if test "`echo $CFLAGS | grep '\-D_REENTRANT' 2> /dev/null`" = ""; then
CFLAGS="$CFLAGS -D_REENTRANT"
fi
if test "`echo $CFLAGS | grep '\-D_THREAD_SAFE' 2> /dev/null`" = ""; then
CFLAGS="$CFLAGS -D_THREAD_SAFE"
fi
if test "`echo $CFLAGS | grep '\-Wall' 2> /dev/null`" = ""; then
CFLAGS="$CFLAGS -Wall"
fi
if test "`echo $CFLAGS | grep -e '-W ' -e '-W$' 2> /dev/null`" = ""; then
CFLAGS="$CFLAGS -W"
fi
if test "`echo $CFLAGS | grep '\-Wno-unused-parameter' 2> /dev/null`" = ""; then
CFLAGS="$CFLAGS -Wno-unused-parameter"
fi
CFLAGS="$CFLAGS -pedantic -std=c99 -D_POSIX_C_SOURCE=200112L"
fi
dnl -----------
dnl CXX options
dnl -----------
dnl
if eval "test x$GCC = xyes"; then
CXXFLAGS="$CXXFLAGS -Wall -W -Wno-unused-parameter -fno-rtti -fno-exceptions -pedantic -D_POSIX_C_SOURCE=200112L"
fi
dnl ----------------------------------
dnl Check if we can use the git commit
dnl ----------------------------------
git_ok=no
AC_CHECK_PROG(git_found, git, yes, no)
if test "x$git_found" = "xyes" ; then
AC_MSG_CHECKING([.git directory exists])
if test -d "$srcdir/.git"; then
AC_MSG_RESULT([yes])
AC_MSG_CHECKING(git describe works)
git_version=`git --work-tree="$srcdir" describe --dirty`
if test "x$git_version" = "x"; then
AC_MSG_RESULT(no)
else
git_ok=yes
AC_MSG_RESULT([yes ($git_version)])
fi
else
AC_MSG_RESULT([no])
fi
fi
AM_CONDITIONAL([GIT_AVAILABLE], [test "x$git_ok" = "xyes"])
AC_SUBST(BASE_CUR_WORKING_DIR)
AC_SUBST(LIBJPEG_LIBS)
AC_SUBST(LIBJPEG_LDFLAGS)
AC_SUBST(LIBJPEG_CPPFLAGS)
AC_SUBST(LIBPNG_LIBS)
AC_SUBST(LIBPNG_CFLAGS)
AC_SUBST(LIBWEBP_LIBS)
AC_SUBST(LIBZ_LIBS)
AC_SUBST(LIBSSL_LIBS)
AC_SUBST(LIBPTHREAD_LIBS)
AC_SUBST(LIBPTHREAD_LDFLAGS)
AC_SUBST(LIBFLTK_CXXFLAGS)
AC_SUBST(LIBFLTK_CFLAGS)
AC_SUBST(LIBFLTK_LIBS)
AC_SUBST(LIBICONV_LIBS)
AC_SUBST(LIBX11_LIBS)
AC_SUBST(CA_CERTS_FILE)
AC_SUBST(CA_CERTS_DIR)
AC_SUBST(datadir)
AC_CONFIG_FILES([
Makefile
dlib/Makefile
dpip/Makefile
dpid/Makefile
dpi/Makefile
doc/Makefile
dw/Makefile
lout/Makefile
src/Makefile
src/IO/Makefile
test/Makefile
test/unit/Makefile
test/dw/Makefile
test/html/Makefile
])
AC_OUTPUT
_AS_ECHO([])
_AS_ECHO([Configuration summary:])
_AS_ECHO([])
_AS_ECHO([ CC : ${CC}])
_AS_ECHO([ CFLAGS : ${CFLAGS}])
_AS_ECHO([ CXX : ${CXX}])
_AS_ECHO([ CXXFLAGS: ${CXXFLAGS}])
_AS_ECHO([])
_AS_ECHO([ TLS enabled: ${tls_ok}])
_AS_ECHO([ TLS library: ${tls_impl}])
_AS_ECHO([ TLS flags : ${LIBSSL_LIBS}])
_AS_ECHO([])
_AS_ECHO([ Cookies enabled: ${enable_cookies}])
_AS_ECHO([ XEmbed enabled : ${enable_xembed}])
_AS_ECHO([ RTFL enabled : ${enable_rtfl}])
_AS_ECHO([ JPEG enabled : ${jpeg_ok}])
_AS_ECHO([ PNG enabled : ${png_ok}])
_AS_ECHO([ GIF enabled : ${enable_gif}])
_AS_ECHO([ SVG enabled : ${enable_svg}])
_AS_ECHO([ WEBP enabled : ${enable_webp}])
_AS_ECHO([])
_AS_ECHO([ HTML tests : ${html_tests_ok}])
_AS_ECHO([])

24
d_size.h Normal file
View File

@ -0,0 +1,24 @@
#ifndef __D_SIZE_H__
#define __D_SIZE_H__
#include "config.h"
#ifdef HAVE_STDINT_H
#include <stdint.h>
#else
#ifdef HAVE_INTTYPES_H
#include <inttypes.h>
#else
/* config.h defines {int,uint}*_t */
#endif /* HAVE_INTTYPES_H */
#endif /*HAVE_STDINT_H */
typedef unsigned char uchar_t;
typedef unsigned short ushort_t;
typedef unsigned long ulong_t;
typedef unsigned int uint_t;
typedef unsigned char bool_t;
#endif /* __D_SIZE_H__ */

153
devdoc/CCCwork.txt Normal file
View File

@ -0,0 +1,153 @@
Last review: August 04, 2009 --jcid
----------------------------
Internal working for the CCC
----------------------------
HTTP protocol
-------------
Query: |
.
1B --> 1B 1B --> 1B --> | -------------.
.----. .----. .----. . |
I |Capi| |http| | IO | | |
'----' '----' '----' . |
1F <-- 1F 1F <-- 1F | V
.
| [Server]
Answer: .
2B --> 2B 2B --> 2B | |
.----. .----. .----. . |
II |Capi| |Dpi | | IO | | |
'----' '----' '----' . |
2F <-- 2F 2F <-- 2F <-- | <------------'
.
|
* a_Capi_open_url() builds both the Answer and Query chains at
once (Answer first then Query), to ensure a uniform structure
that avoids complexity (e.g. race conditions).
* Http_get() sets a callback for the DNS hostname resolve.
Normally it comes later, but may also by issued immediately if
the hostname is cached.
* The socket FD is passed by means of OpSend by the http module
once the remote IP is known and the socket is connected.
Function calls for HTTP CCC
---------------------------
a_Capi_open_url
if (reload)
Capi OpStart 2B (answer) [Capi] --> [dpi] --> [IO]
Capi OpStart 1B (query) [Capi] --> [http] --> [IO]
Http_get
a_Cache_open_url
if URL_E2EReload -> prepare reload
if cached
client enqueue
delayed process queue
else
Cache_entry_add
client enqueue
-//->
a_Http_dns_cb
Http_connect_socket
OpSend FD, BCK
OpSend FD, FWD
Http_send_query
a_Http_make_query_str
OpSend, BCK
IO_submit
a_IOwatch_add_fd (DIO_WRITE, ...)
Note about 'web' structures. They're created using a_Web_new().
The web.c module keeps a list of valid webs, so anytime you're
unsure of a weak reference to 'web', it can be checked with
a_Web_valid(web).
------------
Dpi protocol
------------
Query: |
.
1B --> 1B 1B --> 1B --> | -------------.
.----. .----. .----. . |
I |Capi| |Dpi | | IO | | |
'----' '----' '----' . |
1F <-- 1F 1F <-- 1F | V
.
| [Server]
.
Answer (same as HTTP): | |
. |
2B --> 2B 2B --> 2B | |
.----. .----. .----. . |
II |Capi| |Dpi | | IO | | |
'----' '----' '----' . |
2F <-- 2F 2F <-- 2F <-- | <------------'
.
|
CCC Construction:
a_Capi_open_url() calls a_Capi_dpi_send_cmd() when the URL
belongs to a dpi and it is not cached.
a_Capi_dpi_send_cmd() builds both the Answer and Query chains
at once (Answer first then Query), in the same way as HTTP does.
Note that the answer chain is the same for both, and the query
chain only differs in the module in the middle ([http] or [dpi]).
Function calls for DPI CCC
--------------------------
a_Capi_open_url
a_Capi_dpi_send_cmd
Capi OpStart 2B (answer) [Capi] --> [dpi] --> [IO]
Capi OpStart 1B (query) [Capi] --> [http] --> [IO]
a_Cache_open_url
[...]
Normal termination:
When the dpi server is done, it closes the FD, and OpEnd flows
from IO to Capi (answer branch). When in Capi, capi propagates
OpEnd to the query branch.
Abnormal termination:
The transfer may be aborted by a_Capi_conn_abort_by_url(). The
OpAbort is not yet standardized and has an ad-hoc implementation.
One idea is to have OpAbort always propagate BCK and then FWD and
to jump into the other chain when it gets to [Capi].
Debugging CCC
-------------
A simple way to "look" inside it, is to "#define VERBOSE 1" in
chain.c, and then to follow its work with a printed copy of the
diagrams in this document.
Each new data request generates a CCC, so if you want to debug,
it's good to refine the testcase to the minimum possible number
of connections.

166
devdoc/Cache.txt Normal file
View File

@ -0,0 +1,166 @@
June 2000, --Jcid
Last update: Jul 09
-------
CACHE
-------
The cache module is the main abstraction layer between
rendering and networking.
The capi module acts as a discriminating wrapper which either
calls the cache or the dpi routines depending on the type of
request.
Every URL must be requested using a_Capi_open_url, which
sends the request to the cache if the data is cached, to dillo's
http module for http: URLs, and through dillo's DPI system for
other URLs.
Here we'll document non dpi requests.
----------------
CACHE PHILOSOPHY
----------------
Dillo's cache is very simple; every single resource that's
retrieved (URL) is kept in memory. NOTHING is saved to disk.
This is mainly for three reasons:
- Dillo encourages personal privacy and it assures there'll be
no recorded tracks of the sites you visited.
- The Network is full of intermediate transparent proxys that
serve as caches.
- If you still want to have cached stuff, you can install an
external cache server (such as WWWOFFLE), and benefit from it.
---------------
CACHE STRUCTURE
---------------
Currently, dillo's cache code is spread in different sources:
mainly in cache.[ch], dicache.[ch] and it uses some other
functions from mime.c and web.cc.
Cache.c is the principal source, and it also is the one
responsible for processing cache-clients (held in a queue).
Dicache.c is the interface to the decompressed RGB representations
of currently-displayed images held in DW's imgbuf.
mime.c and web.cc are used for secondary tasks such as
assigning the right "viewer" or "decoder" for a given URL.
----------------
A bit of history
----------------
Some time ago, the cache functions, URL retrieval and
external protocols were a whole mess of mixed code, and it was
getting REALLY hard to fix, improve or extend the functionality.
The main idea of this "layering" is to make code-portions as
independent as possible so they can be understood, fixed,
improved or replaced without affecting the rest of the browser.
An interesting part of the process is that, as resources are
retrieved, the client (dillo in this case) doesn't know the
Content-Type of the resource at request-time. It only becomes known
when the resource header is retrieved (think of http). This
happens when the cache has control, so the cache sets the
proper viewer for it (unless the Callback function was already
specified with the URL request).
You'll find a good example in http.c.
Note: All resources received by the cache have HTTP-style headers.
The file/data/ftp DPIs generate these headers when sending their
non-HTTP resources. Most importantly, a Content-Type header is
generated based on file extension or file contents.
-------------
Cache clients
-------------
Cache clients MUST use a_Capi_open_url to request an URL. The
client structure and the callback-function prototype are defined,
in cache.h, as follows:
struct _CacheClient {
int Key; /* Primary Key for this client */
const DilloUrl *Url; /* Pointer to a cache entry Url */
int Version; /* Dicache version of this Url (0 if not used) */
void *Buf; /* Pointer to cache-data */
uint_t BufSize; /* Valid size of cache-data */
CA_Callback_t Callback; /* Client function */
void *CbData; /* Client function data */
void *Web; /* Pointer to the Web structure of our client */
};
typedef void (*CA_Callback_t)(int Op, CacheClient_t *Client);
Notes:
* Op is the operation that the callback is asked to perform
by the cache. { CA_Send | CA_Close | CA_Abort }.
* Client: The Client structure that originated the request.
--------------------------
Key-functions descriptions
--------------------------
<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
int a_Cache_open_url(void *Web, CA_Callback_t Call, void *CbData)
if Web->url is not cached
Create a cache-entry for that URL
Send client to cache queue
else
Feed our client with cached data
<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
----------------------
Redirections mechanism
(HTTP 30x answers)
----------------------
This is by no means complete. It's a work in progress.
Whenever an URL is served under an HTTP 30x header, its cache
entry is flagged with 'CA_Redirect'. If it's a 301 answer, the
additional 'CA_ForceRedirect' flag is also set, if it's a 302
answer, 'CA_TempRedirect' is also set (this happens inside the
Cache_parse_header() function).
Later on, in Cache_process_queue(), when the entry is flagged
with 'CA_Redirect' Cache_redirect() is called.
-----------
Notes
-----------
The whole process is asynchronous and very complex. I'll try
to document it in more detail later (source is commented).
Currently I have a drawing to understand it; hope the ASCII
translation serves the same as the original.
If you're planning to understand the cache process thoroughly,
write me a note and I will assign higher priority to further
improvement of this doc.
Hope this helps!

96
devdoc/Dillo.txt Normal file
View File

@ -0,0 +1,96 @@
"Eliminate the guesswork and quality goes up."
-------
DILLO
-------
These notes are written with a view to make it less hard, not
easier yet ;), to get into Dillo development.
When I first got into it, I was totally unaware of the browser
internals. Now that I've made my way deep into the core of it,
(we rewrote it 90% and modified the rest), is time to write some
documentation, just to make a less steep learning curve for new
developers.
--Jcid
--------
OVERVIEW
--------
Dillo can be viewed as the sum of five main parts:
1.- Dillo Widget: A custom widget, FLTK-based, that holds the
necessary data structures and mechanisms for graphical rendering.
(Described in Dw*.txt, dw*.c files among the sources.)
2.- Dillo Cache: Integrated with a signal driven Input/Output
engine that handles file descriptor activity, the cache acts as
the main abstraction layer between rendering and networking.
Every URL, whether cached or not, must be retrieved using
a_Capi_open_url (Described briefly in Cache.txt, source
contained in capi.c).
IO is described in IO.txt (recommended), source in src/IO/.
3.- The HTML parser: A streamed parser that joins the Dillo
Widget and the Cache functionality to make browsing possible
(Described in HtmlParser.txt, source mainly inside html.cc).
4.- Image processing code: The part that handles image
retrieval, decoding, caching and displaying. (Described in
Images.txt. Sources: image.c, dw/image.cc, dicache.c, gif.c,
jpeg.c and png.c)
5.- The dpi framework: a gateway to interface the browser with
external programs (Example: the bookmarks server plugin).
Dpi spec: https://dillo-browser.github.io/old/dpi1.html
-------------------------
HOW IS THE PAGE RENDERED?
-------------------------
(A short description of the internal function calling process)
When the user requests a new URL, a_UIcmd_open_url
is queried to do the job; it calls a_Nav_push (The highest level
URL dispatcher); a_Nav_push updates current browsing history and
calls Nav_open_url. Nav_open_url closes all open connections by
calling a_Bw_stop_clients, and then calls
a_Capi_open_url which calls a_Cache_open_url (or the dpi module if
this gateway is used).
If Cache_entry_search hits (due to a cached url :), the client is
fed with cached data, but if the URL isn't cached yet, a new CCC
(Concomitant Control Chain) is created and committed to fetch the
URL.
The next CCC link is dynamically assigned by examining the
URL's protocol. It can be a_Http_ccc or a_Dpi_ccc.
If we have an HTTP URL, a_Http_ccc will succeed, and the http
module will be linked; it will create the proper HTTP query and
link the IO module to submit and deliver the answer.
Note that as the Content-Type of the URL is not always known
in advance, the answering branch decides where to dispatch it to
upon HTTP header arrival.
What happens then?
Well, the html parser gets fed, and proper functions are
called for each tag (to parse and call the appropriate methods)
and the whole page is constructed in a streamed way.
Somewhere in the middle of it, resize and repaint functions
are activated and idle functions draw to screen what has been
processed.
(The process for images is described in Images.txt)

331
devdoc/Dpid.txt Normal file
View File

@ -0,0 +1,331 @@
Aug 2003, Jorge Arellano Cid,
Ferdi Franceschini --
Last update: Nov 2009
------
dpid
------
-------------
Nomenclature:
-------------
dpi:
generic term referring to dillo's plugin system (version1).
dpi1:
specific term for dillo's plugin spec version 1.
at: https://dillo-browser.github.io/old/dpi1.html
dpi program:
any plugin program itself.
dpi framework:
the code base inside and outside dillo that makes dpi1
working possible (it doesn't include dpi programs).
dpip:
dillo plugin protocol. The HTML/XML like set of command tags
and information that goes inside the communication sockets.
Note: not yet fully defined, but functional.
Note2: it was designed to be extensible.
dpid:
dillo plugin daemon.
server plugin:
A plugin that is capable of accepting connections on a socket. Dpid will
never run more than one instance of a server plugin at a time.
filter plugin:
A dpi program that reads from stdin and writes to stdout, and that
exits after its task is done (they don't remain as server plugins).
Warning, dpid will run multiple instances of filter plugins if requested.
-----------
About dpid:
-----------
* dpid is a program which manages dpi connections.
* dpid is a daemon that serves dillo using IDS sockets.
* dpid launches dpi programs and arranges socket communication
between the dpi program and dillo.
The concept and motivation is similar to that of inetd. The
plugin manager (dpid) listens for a service request on a socket
and returns the socket/port pair of a plugin that handles the
service. It also watches sockets of inactive plugins and starts
them when a connection is requested.
-----------------------------------------------------------
What's the problem with managing dpi programs inside dillo?
-----------------------------------------------------------
That's the other way to handle it, but it started to show some
problems (briefly outlined here):
* When having two or more running instances of Dillo, one
should prevail, and take control of dpi managing (but all
dillos carry the managing code).
* If the managing-dillo exits, it must pass control to another
instance, or leave it void if there's no other dillo running!
* The need to synchronize all the running instances of
dillo arises.
* If the controlling instance finishes and quits, all the
dpi-program PIDs are lost.
* Terminating hanged dpis is hard if it's not done with signals
(PIDs)
* Forks can be expensive (Dillo had to fork its dpis).
* When a managing dillo exits, the new one is no longer the
parent of the forked dpis.
* If Unix domain sockets for the dpis were to be named
randomly, it gets very hard to recover their names if the
controlling instance of dillo exits and another must "take
over" the managing.
* It increments dillo's core size.
* If dillo hangs/crashes, dpi activity is lost (e.g. downloads)
* ...
That's why the managing daemon scheme was chosen.
----------------------
What does dpid handle?
----------------------
It solves all the above mentioned shortcomings and also can do:
* Multiple dillos:
dpid can communicate and serve more than one instance
of dillo.
* Multiple dillo windows:
two or more windows of the same dillo instance accessing dpis
at the same time.
* Different implementations of the same service
dpi programs ("dpis") are just an implementation of a
service. There's no problem in implementing a different one
for the same service (e.g. downloads).
* Upgrading a service:
to a new version or implementation without requiring
patching dillo's core or even bringing down the dpid.
And finally, being aware that this design can support the
following functions is very helpful:
SCHEME Example
------------------------------------------------------------
* "one demand/one response" man, preferences, ...
* "resident while working" downloads, mp3, ...
* "resident until exit request" bookmarks, ...
* "one client only" cd burner, ...
* "one client per instance" man, ...
* "multiple clients/one instance" downloads, cookies ...
--------
Features
--------
* Dpi programs go in: "EPREFIX/dillo/dpi" or "~/.dillo/dpi". The binaries
are named <name>.dpi as "bookmarks.dpi" and <name>.filter.dpi as in
"hello.filter.dpi". The ".filter" plugins simply read from stdin
and write to stdout.
* Register/update/remove dpis from list of available dpis when a
'register_all' command is received.
* dpid terminates when it receives a 'DpiBye' command.
* dpis can be terminated with a 'DpiBye' command.
* dpidc control program for dpid, currently allows register and stop.
-----
todo:
-----
These features are already designed, waiting for implementation:
* dpidc remove // May be not necessary after all...
-----------------
How does it work?
-----------------
o on startup dpid reads dpidrc for the path to the dpi directory
(usually EPREFIX/lib/dillo/dpi). ~/.dillo/dpi is scanned first.
o both directories are scanned for the list of available plugins.
~/.dillo/dpi overrides system-wide dpis.
o next it creates internet domain sockets for the available plugins and
then listens for service requests on its own socket,
and for connections to the sockets of inactive plugins.
o dpid returns the port of a plugin's socket when a client (dillo)
requests a service.
o if the requested plugin is a 'server' then
1) dpid stops watching the socket for activity
2) forks and starts the plugin
3) resumes watching the socket when the plugin exits
o if the requested plugin is a 'filter' then
1) dpid accepts the connection
2) maps the socket fd to stdin/stdout (with dup2)
3) forks and starts the plugin
4) continues to watch the socket for new connections
---------------------------
dpi service process diagram
---------------------------
These drawings should be worth a thousand words! :)
(I)
.--- s1 s2 s3 ... sn
|
[dpid] [dillo]
|
'--- srs
The dpid is running listening on several sockets.
(II)
.--- s1 s2 s3 ... sn
|
[dpid] [dillo]
| |
'--- srs ------------------'
dillo needs a service so it connects to the service request
socket of the dpid (srs) and asks for the socket name of the
required plugin (using dpip).
(III)
.--- s1 s2 s3 ... sn
| |
[dpid] | [dillo]
| | |
'--- srs '---------------'
then it connects to that socket (s3, still serviced by dpid!)
(IV)
.--- s1 s2 s3 ... sn
| |
.[dpid] | [dillo]
. | | |
. '--- srs '---------------'
.
.............[dpi program]
when s3 has activity (incoming data), dpid forks the dpi
program for it...
(V)
.--- s1 s2 (s3) ... sn
|
[dpid] [dillo]
| |
'--- srs .---------------'
|
[dpi program]
... and lets it "to take over" the socket.
Once there's a socket channel for dpi and dillo, the whole
communication process takes place until the task is done. When
the dpi program exits, dpid resumes listening on the socket (s3).
--------------------------------
So, how do I make my own plugin?
--------------------------------
Maybe the simplest way to get started is to understand a few
concepts and then to use the hands-on method by using/modifying
the hello dpi. It's designed as an example to get developers
started.
---------
Concepts:
---------
* Dillo plugins work by communicating two processes: dillo
and the dpi.
* The underlying protocol (DPIP) has a uniform API which is
powerful enough for both blocking and nonblocking IO, and
filter or server dpis.
* The simplest example is one-request one-answer (for example
dillo asks for a URL and the dpi sends it). You'll find
this and more complex examples in hello.c
First, you should get familiar with the hello dpi as a user:
$dillo dpi:/hello/
Once you've played enough with it, start reading the well
commented code in hello.c and start making changes!
---------------
Debugging a dpi
---------------
The simplest way is to add printf-like feedback using the MSG*
macros. You can start the dpid by hand on a terminal to force
messages to go there. Filter dpis use sdterr and server dpis
stdout.
Sometimes more complex dpis need more than MSG*. In this case
you can use gdb like this.
1.- Add an sleep(20) statement just after the dpi starts.
2.- Start dillo and issue a request for your dpi. This will
get your dpi started.
3.- Standing in the dpi source directory:
ps aux|grep dpi
4.- Take note of the dpi's PID and start gdb, then:
(gdb) attach <PID>
5.- Continue from there...
------------
Final Notes:
------------
1.- If you already understand the hello dpi and want to try
something more advanced:
* bookmarks.c is a good example of a blocking server
* file.c is an advanced example of a server handling multiple
non-blocking connections with select().
2.- Multiple instances of a filter plugin may be run
concurrently, this could be a problem if your plugin records data
in a file, however it is safe if you simply write to stdout.
Alternatively you could write a 'server' plugin instead as they
are guaranteed not to run concurrently.
3.- The hardest part is to try to modify the dpi framework code
inside dillo; you have been warned! It already supports a lot of
functionality, but if you need to do some very custom stuff, try
extending the "chat" command, or asking in dillo-dev.
>>>>>>>>>>>>>>>>>>>>> <<<<<<<<<<<<<<<<<<<<<

124
devdoc/HtmlParser.txt Normal file
View File

@ -0,0 +1,124 @@
October 2001, --Jcid
Last update: Jul 2009
---------------
THE HTML PARSER
---------------
Dillo's parser is more than just a HTML parser, it does XHTML
and plain text also. It has parsing 'modes' that define its
behaviour while working:
typedef enum {
DILLO_HTML_PARSE_MODE_INIT = 0,
DILLO_HTML_PARSE_MODE_STASH,
DILLO_HTML_PARSE_MODE_STASH_AND_BODY,
DILLO_HTML_PARSE_MODE_BODY,
DILLO_HTML_PARSE_MODE_VERBATIM,
DILLO_HTML_PARSE_MODE_PRE
} DilloHtmlParseMode;
The parser works upon a token-grained basis, i.e., the data
stream is parsed into tokens and the parser is fed with them. The
process is simple: whenever the cache has new data, it is
passed to Html_write, which groups data into tokens and calls the
appropriate functions for the token type (tag, space, or word).
Note: when in DILLO_HTML_PARSE_MODE_VERBATIM, the parser
doesn't try to split the data stream into tokens anymore; it
simply collects until the closing tag.
------
TOKENS
------
* A chunk of WHITE SPACE --> Html_process_space
* TAG --> Html_process_tag
The tag-start is defined by two adjacent characters:
first : '<'
second: ALPHA | '/' | '!' | '?'
Note: comments are discarded ( <!-- ... --> )
The tag's end is not as easy to find, nor to deal with!:
1) The HTML 4.01 sec. 3.2.2 states that "Attribute/value
pairs appear before the final '>' of an element's start tag",
but it doesn't define how to discriminate the "final" '>'.
2) '<' and '>' should be escaped as '&lt;' and '&gt;' inside
attribute values.
3) The XML SPEC for XHTML states:
AttrValue ::== '"' ([^<&"] | Reference)* '"' |
"'" ([^<&'] | Reference)* "'"
Current parser honors the XML SPEC.
As it's a common mistake for human authors to mistype or
forget one of the quote marks of an attribute value; the
parser solves the problem with a look-ahead technique
(otherwise the parser could skip significant amounts of
properly-written HTML).
* WORD --> Html_process_word
A word is anything that doesn't start with SPACE, that's
outside of a tag, up to the first SPACE or tag start.
SPACE = ' ' | \n | \r | \t | \f | \v
-----------------
THE PARSING STACK
-----------------
The parsing state of the document is kept in a stack:
class DilloHtml {
[...]
lout::misc::SimpleVector<DilloHtmlState> *stack;
[...]
};
struct _DilloHtmlState {
CssPropertyList *table_cell_props;
DilloHtmlParseMode parse_mode;
DilloHtmlTableMode table_mode;
bool cell_text_align_set;
DilloHtmlListMode list_type;
int list_number;
/* TagInfo index for the tag that's being processed */
int tag_idx;
dw::core::Widget *textblock, *table;
/* This is used to align list items (especially in enumerated lists) */
dw::core::Widget *ref_list_item;
/* This is used for list items etc; if it is set to TRUE, breaks
have to be "handed over" (see Html_add_indented and
Html_eventually_pop_dw). */
bool hand_over_break;
};
Basically, when a TAG is processed, a new state is pushed into
the 'stack' and its 'style' is set to reflect the desired
appearance (details in DwStyle.txt).
That way, when a word is processed later (added to the Dw), all
the information is within the top state.
Closing TAGs just pop the stack.

468
devdoc/IO.txt Normal file
View File

@ -0,0 +1,468 @@
This is the updated base of a paper I wrote with Horst.
It provides a good introduction to Dillo's internals.
(Highly recommended if you plan to patch or develop in Dillo)
It may not be exactly accurate (it's quite old), but it explains
the theory behind in some detail, so it's more than recommended
reading material.
--Jcid
-----------------------------------------------------
Parallel network programming of the Dillo web browser
-----------------------------------------------------
Jorge Arellano-Cid <jcid@dillo.org>
Horst H. von Brand <vonbrand@inf.utfsm.cl>
--------
Abstract
--------
Network programs face several delay sources when sending or
retrieving data. This is particularly problematic in programs
which interact directly with the user, most notably web browsers.
We present a hybrid approach using threads communicated through
pipes and signal driven I/O, which allows a non-blocking main
thread and overlapping waiting times.
------------
Introduction
------------
The Dillo project didn't start from scratch but mainly working
on the code base of gzilla (a light web browser written by Raph
Levien). As the project went by, the code of the whole source was
standardized, and the networking engine was replaced with a new,
faster design. Later, the parser was changed, the streamed
handling of data and its error control was put under the control
of the CCC (Concomitant Control Chain), and the old widget system
was replaced with a new one (Dw). The source code is currently
regarded as "very stable beta", and is available at
<https://github.com/dillo-browser/dillo>. Dillo is a project
licensed under the GNU General Public License.
This paper covers basic design aspects of the hybrid approach
that the Dillo web browser uses to solve several latency
problems. After introducing the main delay-sources, the main
points of the hybrid design will be addressed.
-------------
Delay sources
-------------
Network programs face several delay-sources while sending or
retrieving data. In the particular case of a web browser, they
are found in:
DNS querying:
The time required to solve a name.
Initiating the TCP connection:
The three way handshake of the TCP protocol.
Sending the query:
The time spent uploading queries to the remote server.
Retrieving data:
The time spent expecting and receiving the query answer.
Closing the TCP connection:
The four packet-sending closing sequence of the TCP protocol.
In a WAN context, every single item of this list has an
associated delay that is non deterministic and often measured in
seconds. If we add several connections per browsed page (each one
requiring at least the 4 last steps), the total latency can be
considerable.
-----------------------------------
The traditional (blocking) approach
-----------------------------------
The main problems with the blocking approach are:
When issuing an operation that can't be completed
immediately, the process is put to sleep waiting for
completion, and the program doesn't do any other
processing in the meantime.
When waiting for a specific socket operation to complete,
packets that belong to other connections may be arriving,
and have to wait for service.
Web browsers handle many small transactions,
if waiting times are not overlapped
the latency perceived by the user can be very annoying.
If the user interface is just put to sleep during network
operations, the program becomes unresponsive, confusing
and perhaps alarming the user.
Not overlapping waiting times and processing makes
graphical rendering (which is arguably the central function
of a browser) unnecessarily slow.
---------------------
Dillo's hybrid design
---------------------
Dillo uses threads and signal driven I/O extensively to
overlap waiting times and computation. Handling the user
interface in a thread that never blocks gives a good interactive
``feel.'' The use of GTK+, a sophisticated widget framework for
graphical user interfaces, helped very much to accomplish this
goal. All the interface, rendering and I/O engine was built upon
its facilities.
The design is said to be ``hybrid'' because it uses threads
for DNS querying and reading local files, and signal driven I/O
for TCP connections. The threaded DNS scheme is potentially
concurrent (this depends on underlying hardware), while the I/O
handling (both local files and remote connections) is
definitively parallel.
To simplify the structure of the browser, local files are
encapsulated into HTTP streams and presented to the rest of the
browser as such, in exactly the same way a remote connection is
handled. To create this illusion, a thread is launched. This
thread opens a pipe to the browser, it then synthesizes an
appropriate HTTP header, sends it together with the file to the
browser proper. In this way, all the browser sees is a handle,
the data on it can come from a remote connection or from a local
file.
To handle a remote connection is more complex. In this case,
the browser asks the cache manager for the URL. The name in the
URL has to be resolved through the DNS engine, a socket TCP
connection must be established, the HTTP request has to be sent,
and finally the result retrieved. Each of the steps mentioned
could give rise to errors, which have to be handled and somehow
communicated to the rest of the program. For performance reasons,
it is critical that responses are cached locally, so the remote
connection doesn't directly hand over the data to the browser;
the response is passed to the cache manager which then relays it
to the rest of the browser. The DNS engine caches DNS responses,
and either answers them from the cache or by querying the DNS.
Querying is done in a separate thread, so that the rest of the
browser isn't blocked by long waits here.
The activities mentioned do not happen strictly in the order
stated above. It is even possible that several URLs are being
handled at the same time, in order to overlap waiting and
downloading. The functions called directly from the user
interface have to return quickly to maintain interactive
response. Sometimes they return connection handlers that haven't
been completely set up yet. As stated, I/O is signal-driven, when
one of the descriptors is ready for data transfer (reading or
writing), it wakes up the I/O engine.
Data transfer between threads inside the browser is handled by
pipes, shared memory is little used. This almost obviates the
need for explicit synchronization, which is one of the main areas
of complexity and bugs in concurrent programs. Dillo handles its
threads in a way that its developers can think of it as running
on a single thread of control. This is accomplished by making the
DNS engine call-backs happen within the main thread, and by
isolating file loading with pipes.
Using threads in this way has three big advantages:
The browser doesn't block when one of its child threads
blocks. In particular, the user interface is responsive
even while resolving a name or downloading a file.
Developers don't need to deal with complex concurrent
concerns. Concurrency is hard to handle, and few developers
are adept at this. This gives access a much larger pool of
potential developers, something which can be critical
in an open-source development project.
By making the code mostly sequential, debugging the code
with traditional tools like gdb is possible. Debugging
parallel programs is very hard, and appropriate tools are
hard to come by.
Because of simplicity and portability concerns, DNS querying
is done in a separate thread. The standard C library doesn't
provide a function for making DNS queries that don't block. The
alternative is to implement a new, custom DNS querying function
that doesn't block. This is certainly a complex task, integrating
this mechanism into the thread structure of the program is much
simpler.
Using a thread and a pipe to read a local file adds a
buffering step to the process (and a certain latency), but it has
a couple of significative advantages:
By handling local files in the same way as remote
connections, a significant amount of code is reused.
A preprocessing step of the file data can be added easily,
if needed. In fact, the file is encapsulated into an HTTP
data stream.
-----------
DNS queries
-----------
Dillo handles DNS queries with threads, letting a child thread
wait until the DNS server answers the request. When the answer
arrives, a call-back function is called, and the program resumes
what it was doing at DNS-request time. The interesting thing is
that the call-back happens in the main thread, while the child
thread simply exits when done. This is implemented through a
server-channel design.
The server channel
------------------
There is one thread for each channel, and each channel can
have multiple clients. When the program requests an IP address,
the server first looks for a cached match; if it hits, the client
call-back is invoked immediately, but if not, the client is put
into a queue, a thread is spawned to query the DNS, and a GTK+
idle client is set to poll the channel 5~times per second for
completion, and when it finally succeeds, every client of that
channel is serviced.
This scheme allows all the further processing to continue on
the same thread it began: the main thread.
------------------------
Handling TCP connections
------------------------
Establishing a TCP connection requires the well known
three-way handshake packet-sending sequence. Depending on network
traffic and several other issues, significant delay can occur at
this phase.
Dillo handles the connection by a non blocking socket scheme.
Basically, a socket file descriptor of AF_INET type is requested
and set to non-blocking I/O. When the DNS server has resolved the
name, the socket connection process begins by calling connect(2);
{We use the Unix convention of identifying the manual section
where the concept is described, in this case
section 2 (system calls).}
which returns immediately with an EINPROGRESS error.
After the connection reaches the EINPROGRESS ``state,'' the
socket waits in background until connection succeeds (or fails),
when that happens, a callback function is awaked to perform the
following steps: set the I/O engine to send the query and expect
its answer (both in background).
The advantage of this scheme is that every required step is
quickly done without blocking the browser. Finally, the socket
will generate a signal whenever I/O is possible.
----------------
Handling queries
----------------
In the case of a HTTP URL, queries typically translate into a
short transmission (the HTTP query) and a lengthy retrieval
process. Queries are not always short though, specially when
requesting forms (all the form data is attached within the
query), and also when requesting CGI programs.
Regardless of query length, query sending is handled in
background. The thread that was initiated at TCP connecting time
has all the transmission framework already set up; at this point,
packet sending is just a matter of waiting for the
write signal (G_IO_OUT) to come and then sending the data. When
the socket gets ready for transmission, the data is sent using
IO_write.
--------------
Receiving data
--------------
Although conceptually similar to sending queries, retrieving
data is very different as the data received can easily exceed the
size of the query by many orders of magnitude (for example when
downloading images or files). This is one of the main sources of
latency, the retrieval can take several seconds or even minutes
when downloading large files.
The data retrieving process for a single file, that began by
setting up the expecting framework at TCP connecting time, simply
waits for the read signal (G_IO_IN). When it happens, the
low-level I/O engine gets called, the data is read into
pre-allocated buffers and the appropriate call-backs are
performed. Technically, whenever a G_IO_IN event is generated,
data is received from the socket file descriptor, by using the
IO_read function. This iterative process finishes upon EOF (or on
an error condition).
----------------------
Closing the connection
----------------------
Closing a TCP connection requires four data segments, not an
impressive amount but twice the round trip time, which can be
substantial. When data retrieval finishes, socket closing is
triggered. There's nothing but a IO_close_fd call on the socket's
file descriptor. This process was originally designed to split
the four segment close into two partial closes, one when query
sending is done and the other when all data is in. This scheme is
not currently used because the write close also stops the reading
part.
The low-level I/O engine
------------------------
Dillo I/O is carried out in the background. This is achieved
by using low level file descriptors and signals. Anytime a file
descriptor shows activity, a signal is raised and the signal
handler takes care of the I/O.
The low-level I/O engine ("I/O engine" from here on) was
designed as an internal abstraction layer for background file
descriptor activity. It is intended to be used by the cache
module only; higher level routines should ask the cache for its
URLs. Every operation that is meant to be carried out in
background should be handled by the I/O engine. In the case of
TCP sockets, they are created and submitted to the I/O engine for
any further processing.
The submitting process (client) must fill a request structure
and let the I/O engine handle the file descriptor activity, until
it receives a call-back for finally processing the data. This is
better understood by examining the request structure:
typedef struct {
gint Key; /* Primary Key (for klist) */
gint Op; /* IORead | IOWrite | IOWrites */
gint FD; /* Current File Descriptor */
gint Flags; /* Flag array */
glong Status; /* Number of bytes read, or -errno code */
void *Buf; /* Buffer place */
size_t BufSize; /* Buffer length */
void *BufStart; /* PRIVATE: only used inside IO.c! */
void *ExtData; /* External data reference (not used by IO.c) */
void *Info; /* CCC Info structure for this IO */
GIOChannel *GioCh; /* IO channel */
guint watch_id; /* glib's event source id */
} IOData_t;
To request an I/O operation, this structure must be filled and
passed to the I/O engine.
'Op' and 'Buf' and 'BufSize' MUST be provided.
'ExtData' MAY be provided.
'Status', 'FD' and 'GioCh' are set by I/O engine internal
routines.
When there is new data in the file descriptor, 'IO_callback'
gets called (by glib). Only after the I/O engine finishes
processing the data are the upper layers notified.
The I/O engine transfer buffer
------------------------------
The 'Buf' and 'BufSize' fields of the request structure
provide the transfer buffer for each operation. This buffer must
be set by the client (to increase performance by avoiding copying
data).
On reads, the client specifies the amount and where to place
the retrieved data; on writes, it specifies the amount and source
of the data segment that is to be sent. Although this scheme
increases complexity, it has proven very fast and powerful. For
instance, when the size of a document is known in advance, a
buffer for all the data can be allocated at once, eliminating the
need for multiple memory reallocations. Even more, if the size is
known and the data transfer is taking the form of multiple small
chunks of data, the client only needs to update 'Buf' and
BufSize' to point to the next byte in its large preallocated
reception buffer (by adding the chunk size to 'Buf'). On the
other hand, if the size of the transfer isn't known in advance,
the reception buffer can remain untouched until the connection
closes, but the client must then accomplish the usual buffer
copying and reallocation.
The I/O engine also lets the client specify a full length
transfer buffer when sending data. It doesn't matter (from the
client's point of view) if the data fits in a single packet or
not, it's the I/O engine's job to divide it into smaller chunks
if needed and to perform the operation accordingly.
------------------------------------------
Handling multiple simultaneous connections
------------------------------------------
The previous sections describe the internal work for a single
connection, the I/O engine handles several of them in parallel.
This is the normal downloading behavior of a web page. Normally,
after retrieving the main document (HTML code), several
references to other files (typically images) and sometimes even
to other sites (mostly advertising today) are found inside the
page. In order to parse and complete the page rendering, those
other documents must be fetched and displayed, so it is not
uncommon to have multiple downloading connections (every one
requiring the whole fetching process) happening at the same time.
Even though socket activity can reach a hectic pace, the
browser never blocks. Note also that the I/O engine is the one
that directs the execution flow of the program by triggering a
call-back chain whenever a file descriptor operation succeeds or
fails.
A key point for this multiple call-back chained I/O engine is
that every single function in the chain must be guaranteed to
return quickly. Otherwise, the whole system blocks until it
returns.
-----------
Conclusions
-----------
Dillo is currently in very stable beta state. It already shows
impressive performance, and its interactive ``feel'' is much
better than that of other web browsers.
The modular structure of Dillo, and its reliance on GTK1 allow
it to be very small. Not every feature of HTML-4.01 has been
implemented yet, but no significant problems are foreseen in
doing this.
The fact that Dillo's central I/O engine is written using
advanced features of POSIX and TCP/IP networking makes its
performance possible, but on the other hand this also means that
only a fraction of the interested hackers are able to work on it.
A simple code base is critical when trying to attract hackers
to work on a project like this one. Using the GTK+ framework
helped both in creating the graphical user interface and in
handling the concurrency inside the browser. By having threads
communicate through pipes the need for explicit synchronization
is almost completely eliminated, and with it most of the
complexity of concurrent programming disappears.
A clean, strictly applied layering approach based on clear
abstractions is vital in each programming project. A good,
supportive framework is of much help here.

129
devdoc/Images.txt Normal file
View File

@ -0,0 +1,129 @@
January 2009, --Jcid
Update June 2015: See also doc/dw-images-and-backgrounds.doc, or
../html/dw-images-and-backgrounds.html (generated by doxygen).
------
IMAGES
------
* When a image tag is found within a HTML page, Html_tag_open_img
handles it by:
- Parsing & getting attribute values.
- Creating a new image structure (DilloImage) and its
associated widget (DwImage).
i.e. If 'Image' is the var for the structure, then
'Image->dw' is the widget.
- Requesting the image to be fed by the cache.
- Sending some info to the browser interface.
* The cache can either request the image data from the net, or
feed it directly from the dicache (decompressed image cache).
* Both processes are somewhat different because the first one
requires to decode the image data into RGB format, and the second
one has the whole data already decoded.
* Regardless of the RGB-data feeding method, the decoded data is
passed to the widget (DwImage) and drawn in a streamed way.
Note that INDEXED images are also decoded into RGB format.
Html_tag_open_img // IMG element processing
Html_add_new_image // Read attributes, create image, add to HTML page
a_Image_new // Create a 'DilloImage' data structure, to coordinate
// decoded image-data transfer to an 'Imgbuf'.
Html_add_widget // Adds the dw::Image to the page
Html_load_image // Tells cache to retrieve image
---------------------
Fetching from the net
---------------------
* a_Capi_open_url initiates the resource request, and when
finally the answer arrives, the HTTP header is examined for MIME
type and either the GIF or PNG or JPEG decoder is set to handle
the incoming data stream.
Decoding functions:
a_Gif_callback, a_Jpeg_callback and a_Png_callback.
* The decoding function calls the following dicache methods as
the data is processed (listed in order):
a_Dicache_set_parms
a_Dicache_set_cmap (only for indexed-GIF images)
a_Dicache_write
a_Dicache_new_scan (MAY be called here or after set_cmap)
a_Dicache_close
* The dicache methods call the necessary functions to connect
with the widget code. This is done by calling image.c functions:
a_Image_set_parms
a_Image_set_cmap
a_Image_write
a_Image_new_scan
a_Image_close
* The functions in image.c make the required Dw calls.
-------------------------
Fetching from the dicache
-------------------------
* a_Capi_open_url() tests the cache for the image, and the cache,
via a_Cache_open_url(), enqueues a client for it, without asking
the network for the data. When the client queue is processed (a
bit later), Dicache_image() is set as the callback.
* When Dicache_image() is called, it sets the proper image data
decoder (RGB) and its data structure based on the entry's Type.
Then it substitutes itself with a_Dicache_callback() as the
handling function, and gets out of the way.
* Thenceforth the rest of the functions calls is driven by
a_Dicache_callback().
-----------
Misc. notes
-----------
* Repeated images generate new cache clients, but they may share
the imgbuf.
Note: Currently there's no proper support for transparent
images (i.e. decode to RGBA), but most of the time they render
the background color OK. This is: when first loaded, repeated
images share a background color, but when cached they render
correctly ;-). There's no point in trying to fix this because the
correct solution is to decode to RGBA and let the toolkit (FLTK)
handle the transparency.
* The first cache-client callback (Dicache_image()) is set when
the Content-type of the image is got.
* Later on, when there's a shared imgbuf, the dicache's logic
avoids decoding it multiple times and reuses what's already done.
* The dicache-entry and the Image structure hold bit arrays that
represent which rows have been decoded.
* The image processing can be found in the following sources:
- image.{cc,hh}
- dicache.[ch]
- gif.[ch], png.[ch], jpeg.[ch]
- dw/image.{cc,hh}
* Bear in mind that there are four data structures for image
code:
- DilloImage (image.hh)
- DICacheEntry (dicache.h)
- dw::Image (class Image in dw/image.hh)
- core::Imgbuf (imgbuf.hh)

127
devdoc/NC_design.txt Normal file
View File

@ -0,0 +1,127 @@
_________________________________________________________________
Naming&Coding design
_________________________________________________________________
Dillo's code is divided into modules. For instance: bookmark, cache,
dicache, gif.
Let's think of a module named "menu", then:
* Every internal routine of the module, should start with "Menu_"
prefix.
* "Menu_" prefixed functions are not meant to be called from outside
the module.
* If the function is to be exported to other modules (i.e. it will
be called from the outside), it should be wrapped with an "a_"
prefix.
For instance: if the function name is "Menu_create", then it's an
internal function, but if we need to call it from the outside, then it
should be renamed to "a_Menu_create".
Why the "a_" prefix?
Because of historical reasons.
And "a_Menu_create" reads better than "d_Menu_create" because the
first one suggests "a Menu create" function!
Another way of understanding this is thinking of "a_" prefixed
functions as Dillo's internal library, and the rest ("Menu_" prefixed
in our example) as a private module-library.
Indentation:
Source code must be indented with 3 blank spaces, no Tabs.
Why?
Because different editors expand or treat tabs in several ways; 8
spaces being the most common, but that makes code really wide and
we'll try to keep it within the 80 columns bounds (printer friendly).
You can use: indent -kr -sc -i3 -bad -nbbo -nut -l79 myfile.c
Function commenting:
Every single function of the module should start with a short comment
that explains its purpose; three lines must be enough, but if you
think it requires more, enlarge it.
/*
* Try finding the url in the cache. If it hits, send the contents
* to the caller. If it misses, set up a new connection.
*/
int a_Cache_open_url(const char *url, void *Data)
{
...
...
...
}
We also have the BUG:, TODO:, and WORKAROUND: tags.
Use them within source code comments to spot hidden issues. For
instance:
/* BUG: this counter is not accurate */
++i;
/* TODO: get color from the right place */
a = color;
/* WORKAROUND: the canonical way of doing it doesn't work yet. */
++a; ++a; ++a;
Function length:
Let's try to keep functions within the 45 lines boundary. This eases
code reading, following, understanding and maintenance.
Functions with a single exit:
It's much easier to follow and maintain functions with a single exit
point at the bottom (instead of multiple returns). The exception to
the rule are calls like dReturn_if_fail() at its head.
dlib functions:
* Dillo uses dlib extensively in its C sources. Before starting
to code something new, a good starting point is to check what
this library has to offer (check dlib/dlib.h).
* Memory management must be done using dNew, dNew0, dMalloc, dFree
and their relatives.
* For debugging purposes and error catching (not for normal flow):
dReturn_if_fail, dReturn_val_if_fail etc. are encouraged.
* The MSG macro is extensively used to output additional information
to the calling terminal.
_________________________________________________________________
C++
Source code in C++ should follow the same rules with these exceptions:
* Class method names are camel-cased and start with lowercase
e.g. appendInputMultipart
* Classes and types start uppercased
e.g. class DilloHtmlReceiver
* Class methods don't need to prefix its module name
e.g. links->get()
We also try to keep the C++ relatively simple. Dillo does use
inheritance and templates, but that's about all.
_________________________________________________________________
What do we get with this?
* A clear module API for Dillo; every function prefixed "a_" is to
be used outside the module.
* A way to identify where the function came from (the
capitalized word is the module name).
* An inner ADT (Abstract data type) for the module that can be
isolated, tested and replaced independently.
* A two stage instance for bug-fixing. You can change the exported
function algorithms while someone else fixes the internal
module-ADT!
* A coding standard ;)
_________________________________________________________________
Naming&Coding design by Jorge Arellano Cid

51
devdoc/README Normal file
View File

@ -0,0 +1,51 @@
README: Last update Jul 2009
These documents cover dillo's internals.
For user help, see https://dillo-browser.github.io/old/dillo3-help.html
--------------------------------------------------------------------------
These documents need a review.
*.txt were current with Dillo1, but many have since become more or
less out-of-date.
*.doc are doxygen source for the Dillo Widget (dw) component, and
were written for Dillo2.
They will give you an overview of what's going on, but take them
with a pinch of salt.
Of course I'd like to have *.txt as doxygen files too!
If somebody wants to make this conversion, please let me know
to assign higher priority to updating these docs.
--
Jorge.-
--------------------------------------------------------------------------
FILE DESCRIPTION STATE
--------------------------------------------------------------------------
NC_design.txt Naming&Coding design (Be sure to Current
read it before any other doc)
Dillo.txt General overview of the program Current
IO.txt Extensive introduction Current
Cache.txt Informative description Current
Images.txt Image handling and processing Current
HtmlParser.txt A versatile parser Current
Dw.txt The New Dillo Widget (Overview) Current
Imgbuf.txt Image buffers Pending
Selection.txt Selections, and link activation Current (?)
Cookies.txt Explains how to enable cookies Current
Dpid.txt Dillo plugin daemon Current
--------------------------------------------------------------------------
* BTW, there's a small program (srch) within the src/ dir. It searches
tokens within the whole code (*.[ch]). It has proven very useful.
Ex: ./srch a_Image_write
./srch todo:
* Please submit your patches with 'hg diff'.
Happy coding!
--Jcid

2669
devdoc/doxygen-awesome.css Normal file

File diff suppressed because it is too large Load Diff

105
devdoc/dw-changes.doc Normal file
View File

@ -0,0 +1,105 @@
/** \page dw-changes Changes to the GTK+-based Release Version
<h2>Changes in Dw</h2>
Related to the FLTK port, there have been many changes, this is a
(hopefully complete) list:
<ul>
<li> Rendering abstraction, read \ref dw-overview and \ref dw-layout-views
for details. Some important changes:
<ul>
<li> The underlying platform (e.g. the UI toolkit) is fully abstract,
there are several platform independent structures replacing
GTK+ structures, e.g. dw::core::Event.
<li> The central class managing the widget tree is not anymore
GtkDwViewport, but dw::core::Layout.
<li> Drawing is done via dw::core::View, a pointer is passed to
dw::core::Widget::draw.
<li> The distinction between viewport coordinates and canvas
coordinates (formerly world coordinates) has been mostly
removed. (Only for views, it sometimes plays a role, see
\ref dw-layout-views).
</ul>
<li> Cursors have been moved to dw::core::style, see
dw::core::style::Style::cursor. dw::core::Widget::setCursor is now
protected (and so only called by widget implementations).
<li> World coordinates are now called canvas coordinates.
<li> There is now a distinction between dw::core::style::StyleAttrs and
dw::core::style::Style.
<li> There is no base class for container widgets anymore. The former
DwContainer::for_all has been removed, instead this functionality
is now done via iterators (dw::core::Widget::iterator,
dw::core::Iterator).
<li> DwPage is now called dw::Textblock, and DwAlignedPage
dw::AlignedTextblock.
<li> dw::Textblock, all sub classes of it, and dw::Table do not read
"limit_text_width" from the preferences, but get it as an argument.
(May change again.)
<li> dw::Table has been rewritten.
<li> Instead of border_spacing in the old DwStyle, there are two attributes,
dw::core::style::Style::hBorderSpacing and
dw::core::style::Style::vBorderSpacing, since CSS allows to specify
two values. Without CSS, both attributes should have the same value.
<li> Images are handled differently, see \ref dw-images-and-backgrounds.
<li> Embedded UI widgets (formerly GtkWidget's) are handled differently,
see dw::core::ui.
<li> DwButton has been removed, instead, embedded UI widgets are used. See
dw::core::ui and dw::core::ui::ComplexButtonResource.
</ul>
Dw is now written C++, the transition should be obvious. All "Dw"
prefixes have been removed, instead, namespaces are used now:
<ul>
<li>dw::core contains the core,
<li>dw::core::style styles,
<li>dw::core::ui embedded UI resources,
<li>dw::fltk classes related to FLTK, and
<li>::dw the widgets.
</ul>
<h2>Documentation</h2>
The old documentation has been moved to:
<table>
<tr><th colspan="2">Old <th>New
<tr><td rowspan="2">Dw.txt
<td>general part <td>\ref dw-overview, \ref dw-usage,
\ref dw-layout-widgets,
\ref dw-widget-sizes
<tr><td>remarks on specific widgets <td>respective source files: dw::Bullet,
dw::core::ui::Embed
<tr><td rowspan="2">DwImage.txt
<td>signals <td>dw::core::Layout::LinkReceiver
<tr><td>rest <td>dw::Image,
\ref dw-images-and-backgrounds
<tr><td colspan="2">Imgbuf.txt <td>dw::core::Imgbuf,
\ref dw-images-and-backgrounds
<tr><td colspan="2">DwPage.txt <td>dw::Textblock
<tr><td colspan="2">DwRender.txt <td>\ref dw-overview, \ref dw-layout-views,
dw::core::ui
<tr><td colspan="2">DwStyle.txt <td>dw::core::style
<tr><td colspan="2">DwTable.txt <td>dw::Table
<tr><td colspan="2">DwWidget.txt <td>dw::core::Widget, \ref dw-layout-widgets,
\ref dw-widget-sizes
<tr><td colspan="2">Selection.txt <td>dw::core::SelectionState
</table>
*/

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

View File

@ -0,0 +1,63 @@
/** \page dw-fixed-positions Fixed positions
In some cases, widgets or widget content must be positioned relative
to the viewport. As in the CSS specification, these positions will be
called "fixed positions". This must not be confused with "fixedly
positioned elements" (see \ref dw-out-of-flow), which are a special
case of fixed positions.
Applications
============
As defined by CSS
-----------------
- "position: fixed"; see \ref dw-out-of-flow.
- "background-attachment: fixed"; see \ref dw-images-and-backgrounds.
Idea for tables
---------------
Often, tables have a header, which contains information necessary to
interpret the columns in the table body. For this, HTML defines the elements
&lt;thead&gt; and &lt;tbody&gt;
<sup><a href="#note-table-footer" id="ref-table-footer">[1]</a></sup>.
For large tables, the problem occurs that the table header gets out of
the reader's view. In paged media, where a large table covers multiple
pages, this is often solved by *repeating* the table header on each
page occupied by the table. When using a viewport, a table larger than
the viewport could be displayed like this:
1. If the top of the table is within the viewport, show the table
header at the usual position.
2. As soon as top of the table gets above the top border of the
viewport, keep the table header at the viewport top, so that it is
still visible (this means, it moves down, relative to the
*canvas*). This way, the header is still visible, so our objective
is achieved.
3. When scrolling further down, at some point the table body gets out
of the viewport again, and so should the table header.
(Some images would be nice.)
These ideas should be considered when developing a design for fixed
positions.
Design sketch
==============
[...]
----------------------------------------------------------------------
<sup><a href="#ref-table-footer" id="note-table-footer">[1]</a></sup>
... and also &lt;tfoot&gt;, which is not discussed here, for reasons
of simplicity. However, it is obvious that &lt;tfoot&gt; should be
dealt with in an analogue way as &lt;thead&gt;.
*/

BIN
devdoc/dw-floats-01.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 42 KiB

209
devdoc/dw-grows.doc Normal file
View File

@ -0,0 +1,209 @@
/** \page dw-grows GROWS - Grand Redesign Of Widget Sizes
<div style="border: 2px solid #ffff00; margin: 1em 0;
padding: 0.5em 1em; background-color: #ffffe0">The complex "widget
sizes" is currently divided into three documents: \ref
dw-widget-sizes, **Grand Redesign Of Widget Sizes** (this document),
and \ref dw-size-request-pos. Furthermore, there are some notes in
\ref dw-miscellaneous.</div>
This paper describes (will describe) some design changes to
calculating widget sizes. Goals are:
- Simplification of widget size calculation by the parent widget;
dw::Textblock::calcWidgetSize, dw::OutOfFlowMgr::ensureFloatSize
etc. should become simpler or perhaps even obsolete.
- Making the implementation of some features possible:
- *max-width*, *max-height*, *min-width*, *min-height*;
- correct aspect ratio for images with only one percentage size defined;
- *display: inline-block*;
- &lt;button&gt;.
A short sketch
==============
**dw::core::Widget::sizeRequest and dw::core::Widget::getExtremes will
return final results.** The caller does not have to correct the size,
e.&nbsp;g. when percentages are defined. As an example,
dw::Textblock::calcWidgetSize has already become much simpler.
**A new hierarchy, *container*:** Aside from dw::core::Widget::parent
and dw::core::Widget::generator, there is a third hierarchy
dw::core::Widget::container, which is (unlike *generator*) always a
direct ancestor, and represents what in CSS is called *containing
block*. Containers are important to define the "context size", which
is (not solely) used for percentage sizes.
(There is another "containing block", dw::Textblock::containingBlock;
these may be consolidated some day.)
**The process of size calculation is split between the widget itself
and its container:**
- The container provides some abstract methods:
dw::core::Widget::getAvailWidthOfChild,
dw::core::Widget::getAvailHeightOfChild,
dw::core::Widget::correctRequisitionOfChild, and
dw::core::Widget::correctExtremesOfChild, which can be used in the
actual implementation of dw::core::Widget::sizeRequestImpl;
different containers with different ways how to arrange their
children will implement these methods in a different way. (Simple
example: the *available width* for children within a textblock is
the *available width* for the textblock itself, minus
margin/border/padding; on the other hand, it is completely different
for children of tables, for which a complex column width calculation
is used.)
- The actual size calculation is, however, controlled by the widget
itself, which only *uses* these methods above.
<div style="border: 2px solid #ffff00; margin-top: 0.5em;
margin-bottom: 0.5em; padding: 0.5em 1em; background-color: #ffffe0">
<b>Update:</b> This is not fully correct; the parents are also involved
for calculating available widths and heights, at least when CSS 'width'
and 'height' are not set.</div>
**Size hints are removed.** Instead, the container methods in the
previous paragraph are used. Changes of container sizes (especially
viewport the size) are handled in a different way.
**Extremes are extended by intrinsic values.** In some cases (see
dw::Table::forceCalcCellSizes, case *minWidth* > *totalWidth*, for an
example) it is useful to know about minimal and maximal width of a
widget independent of CSS attributes. For this, dw::core::Extremes is
extended by:
- dw::core::Extremes::minWidthIntrinsic and
- dw::core::Extremes::maxWidthIntrinsic.
The rules for the calculation:
1. If a widget has no children, it calculates *minWidthIntrinsic* and
*maxWidthIntrinsic* as those values not affected by CSS hints.
(dw::core::Widget::correctExtremes will not change these values.)
2. A widget must calculate *minWidthIntrinsic* and *maxWidthIntrinsic*
from *minWidthIntrinsic* and *maxWidthIntrinsic* of its children,
and *minWidth* and *maxWidth* from *minWidth* and *maxWidth* of its
children.
3. At the end, *minWidth* and *maxWidth* of a widget are corrected by
CSS attributes. (dw::core::Widget::correctExtremes will do this.)
<div style="border: 2px solid #ffff00; margin-top: 0.5em;
margin-bottom: 0.5em; padding: 0.5em 1em; background-color: #ffffe0">
<b>Notice:</b> Currently, dw::core::Widget::getExtremesImpl must
set all four members in dw::core::Extremes; this may change.</div>
Another **extension of extremes: *adjustmentWidth*.** This is used as
minimum for the width, when "adjust_min_width" (or,
"adjust_table_min_width", respectively) is set.
The rules for the calculation:
1. If a widget has no children, it can choose a suitable value,
typically based on dw::core::Extremes::minWidth and
dw::core::Extremes::minWidthIntrinsic.
2. A widget must calculate *adjustmentWidth* from *adjustmentWidth* of
its children.
*Note:* An implementation of dw::core::Widget::getExtremesImpl may set
this value *after* calling dw::core::Widget::correctExtremesOfChild,
so that it cannot be used for the correction of extremes. In this case
*useAdjustmentWidth = false* should be passed to
dw::core::Widget::correctExtremesOfChild. On the other hand, if known
before, *useAdjustmentWidth* should be set to *true*.
Rules for *new* methods related to resizing
===========================================
- Of course, *sizeRequestImpl* may (should) call *correctRequisition*,
and *getExtremesImpl* may (should) call *correctExtremes*.
- *sizeRequestImpl* (and *correctRequisition*) is allowed to call
*getAvailWidth* and *getAvailHeight* with *forceValue* set, but
*getExtremesImpl* (and *correctExtremes*) is allowed to call these
only with *forceValue* unset.
- For this reason, *sizeRequestImpl* is indeed allowed to call
*getExtremes* (dw::Table does so), but the opposite
(*getExtremesImpl* calling *sizeRequest*) is not allowed
anymore. (Before GROWS, the standard implementation
dw::core::Widget::getExtremesImpl did so.)
- Finally, *getAvailWidth* and *getAvailHeight* may call
*getExtremes*, if and only if *forceValue* is set.
Here is a diagram showing all permitted dependencies:
\dot
digraph G {
node [shape=record, fontname=Helvetica, fontsize=10, color="#c0c0c0"];
edge [arrowhead="open", arrowtail="none", color="#404040"];
"sizeRequest[Impl]" -> "getExtremes[Impl]";
"sizeRequest[Impl]" -> correctRequisition;
"getExtremes[Impl]" -> correctExtremes;
"sizeRequest[Impl]" -> "getAvail[Width|Height] (true)";
"getExtremes[Impl]" -> "getAvail[Width|Height] (false)";
correctRequisition -> "getAvail[Width|Height] (true)";
correctExtremes -> "getAvail[Width|Height] (false)";
"getAvail[Width|Height] (true)" -> "getExtremes[Impl]";
}
\enddot
Open issues
===========
**Do CSS size dimensions override intrinsic sizes in all cases?** If a
textblock needs at least, say, 100 pixels width so that the text can
be read, but has a specification "width: 50px", should half of the
text be invisible? Or should the width be corrected again to 100
pixels?
Currently, in the CSS size specification is honoured in all cases,
with one exception: see dw::Textblock::sizeRequestImpl and see
dw::Textblock::getExtremesImpl (the time when
dw::core::Widget::correctRequisition and
dw::core::Widget::correctExtremes, respectively, is called).
*Not* honouring the CSS size specification in all cases could improve
readability in some cases, so this could depend on a user preference.
**Update:** There is now a dillorc option <tt>adjust_min_width</tt>,
which is implemented for widths, but not heights (since it is based on
width extremes, but there are currently no height extremes).
Another problem is that in most cases, there is no clippping, so that
contents may exceed the allocation of the widget, but redrawing is not
necessarily triggered.
**Percentage values for margins and paddings, as well as negative
margins** are interesting applications, but have not been considered
yet. For negative margins, a new attribute
dw::core::Widget::extraSpace could solve the problem of widgets
sticking out of the allocation of parent.
**Clarify percentage heights.** Search in widget.cc, and compare
section 10.5 ('height') of the CSS 2.1 specification to section 10.2
('width').
**Fast queue resize does not work fully.** Example: run
*test/dw-simple-container-test* (dw_simple_container_test.cc), resize
(best maximize) the window and follow (e.&nbsp;g. by using RTFL) what
happens in consequence of dw::core::Layout::viewportSizeChanged. The
dw::SimpleContainer in the middle is not affected, so only the two
dw::Textblock's (at the top and at the bottom) call queueResize with
*fast = true*, and so get *NEEDS_RESIZE* set; but since it is not set
for the dw::SimpleContainer, *sizeRequest* is never called for the
bottom dw::Textblock.
There does not seem to be a real case for this problem in dillo, since
all widgets which may contain other widgets (except
dw::SimpleContainer, which is not used outside tests) use the
available width and height (dw::core::Widget::usesAvailWidth and
dw::core::Widget::usesAvailHeight), and so are always affected by
viewport size changes.
*/

View File

@ -0,0 +1,235 @@
/** \page dw-images-and-backgrounds Images and Backgrounds in Dw
Image Buffers
=============
Representation of the image data is done by dw::core::Imgbuf, see
there for details. Drawing is done by dw::core::View
(dw::core::View::drawImage).
Since dw::core::Imgbuf provides memory management based on reference
counting, there may be an 1-to-n relation from image renderers (image
widgets or backgrounds, see below) and dw::core::Imgbuf. Since
dw::core::Imgbuf does not know about renderers, but just provides
rendering functionality, the caller must (typically after calling
dw::core::Imgbuf::copyRow) notify all renderers connected to the
buffer.
Image Renderer
==============
Generally, there are no restrictions on how to manage
dw::core::Imgbuf; but to handle image data from web resources, the
interface dw::core::ImgRenderer should be implemented. It is again
wrapped by DilloImage (to make access from the C part possible, since
dw::core::ImgRenderer is written in C++), which is referenced by
DilloWeb. There are two positions where retrieving image data is
initiated:
- Html_load_image: for embedded images (implemented by dw::Image,
which implements dw::core::ImgRenderer);
- StyleEngine::apply (search for "case
CSS_PROPERTY_BACKGROUND_IMAGE"): for background images; there are
some implementations of dw::core::ImgRenderer within the context of
dw::core::style::StyleImage.
Both are described in detail below. Notice that the code is quite
similar; only the implementation of dw::core::ImgRenderer differs.
At this time, dw::core::ImgRenderer has got two methods (see more
documentation there):
- dw::core::ImgRenderer::setBuffer,
- dw::core::ImgRenderer::drawRow,
- dw::core::ImgRenderer::finish, and
- dw::core::ImgRenderer::fatal.
Images
======
This is the simplest renderer, displaying an image. For each row to be
drawn,
- first dw::core::Imgbuf::copyRow, and then
- for each dw::Image, dw::Image::drawRow must be called, with the same
argument (no scaling is necessary).
dw::Image automatically scales the dw::core::Imgbuf, the root buffer
should be passed to dw::Image::setBuffer.
\see dw::Image for more details.
Background Images
=================
Since background images are style resources, they are associated with
dw::core::style::Style, as dw::core::style::StyleImage, which is
handled in a similar way (reference counting etc.) as
dw::core::style::Color and dw::core::style::Font, although it is
concrete and not platform-dependant.
The actual renderer (passed to Web) is an instance of
dw::core::ImgRendererDist (distributes all calls to a set of other
instances of dw::core::ImgRenderer), which contains two kinds of
renderers:
- one instance of dw::core::style::StyleImage::StyleImgRenderer, which
does everything needed for dw::core::style::StyleImage, and
- other renderers, used externally (widgets etc.), which are added by
dw::core::style::StyleImage::putExternalImgRenderer (and removed by
dw::core::style::StyleImage::removeExternalImgRenderer).
This diagram gives an comprehensive overview:
\dot
digraph G {
node [shape=record, fontname=Helvetica, fontsize=10];
edge [arrowhead="open", dir="both", arrowtail="none",
labelfontname=Helvetica, labelfontsize=10, color="#404040",
labelfontcolor="#000080"];
fontname=Helvetica; fontsize=10;
subgraph cluster_dw_style {
style="dashed"; color="#000080"; fontname=Helvetica; fontsize=10;
Style [URL="\ref dw::core::style::Style"];
StyleImage [URL="\ref dw::core::style::StyleImage"];
Imgbuf [URL="\ref dw::core::Imgbuf", color="#a0a0a0"];
StyleImgRenderer
[URL="\ref dw::core::style::StyleImage::StyleImgRenderer"];
ImgRenderer [URL="\ref dw::core::ImgRenderer", color="#ff8080"];
ImgRendererDist [URL="\ref dw::core::ImgRendererDist"];
ExternalImgRenderer
[URL="\ref dw::core::style::StyleImage::ExternalImgRenderer",
color="#a0a0a0"];
ExternalWidgetImgRenderer
[URL="\ref dw::core::style::StyleImage::ExternalWidgetImgRenderer",
color="#a0a0a0"];
}
subgraph cluster_dw_layout {
style="dashed"; color="#000080"; fontname=Helvetica; fontsize=10;
Layout [URL="\ref dw::core::Layout"];
LayoutImgRenderer [URL="\ref dw::core::Layout::LayoutImgRenderer"];
}
subgraph cluster_dw_widget {
style="dashed"; color="#000080"; fontname=Helvetica; fontsize=10;
Widget [URL="\ref dw::core::Widget", color="#a0a0a0"];
WidgetImgRenderer [URL="\ref dw::core::Widget::WidgetImgRenderer"];
}
subgraph cluster_dw_textblock {
style="dashed"; color="#000080"; fontname=Helvetica; fontsize=10;
Textblock [URL="\ref dw::Textblock"];
Word [URL="\ref dw::Textblock::Word"];
WordImgRenderer [URL="\ref dw::Textblock::WordImgRenderer"];
SpaceImgRenderer [URL="\ref dw::Textblock::SpaceImgRenderer"];
}
Style -> StyleImage [headlabel="*", taillabel="1"];
StyleImage -> Imgbuf [headlabel="*", taillabel="1"];
StyleImage -> StyleImgRenderer [headlabel="1", taillabel="1"];
StyleImage -> ImgRendererDist [headlabel="1", taillabel="1"];
ImgRendererDist -> StyleImgRenderer [headlabel="1", taillabel="1"];
ImgRendererDist -> ImgRenderer [headlabel="1", taillabel="*"];
ImgRenderer -> ImgRendererDist [arrowhead="none", arrowtail="empty",
dir="both", style="dashed"];
ImgRenderer -> StyleImgRenderer [arrowhead="none", arrowtail="empty",
dir="both", style="dashed"];
ImgRenderer -> ExternalImgRenderer [arrowhead="none", arrowtail="empty",
dir="both", style="dashed"];
ExternalImgRenderer -> ExternalWidgetImgRenderer [arrowhead="none",
arrowtail="empty", dir="both", style="dashed"];
Layout -> LayoutImgRenderer [headlabel="1", taillabel="0..1"];
ExternalImgRenderer -> LayoutImgRenderer [arrowhead="none",
arrowtail="empty", dir="both", style="dashed"];
Widget -> WidgetImgRenderer [headlabel="1", taillabel="0..1"];
ExternalWidgetImgRenderer -> WidgetImgRenderer [arrowhead="none",
arrowtail="empty", dir="both", style="dashed"];
Textblock -> Word [headlabel="1", taillabel="*"];
Word -> WordImgRenderer [headlabel="1", taillabel="0..1"];
Word -> SpaceImgRenderer [headlabel="1", taillabel="0..1"];
ExternalWidgetImgRenderer -> WordImgRenderer [arrowhead="none",
arrowtail="empty", dir="both", style="dashed"];
WordImgRenderer -> SpaceImgRenderer [arrowhead="none", arrowtail="empty",
dir="both", style="dashed"];
}
\enddot
<center>[\ref uml-legend "legend"]</center>
Memory management
-----------------
dw::core::style::StyleImage extends lout::signal::ObservedObject, so
that deleting this instance can be connected to code dealing with
cache clients etc. See StyleImageDeletionReceiver and how it is
attached in StyleEngine::apply ("case CSS_PROPERTY_BACKGROUND_IMAGE").
Bugs and Things Needing Improvement
===================================
(Mostly related to image backgrounds, when not otherwise mentioned.)
High Priority
-------------
**Configurability, security/privacy aspects, etc.,** which are
currently available for image widgets, should be adopted. Perhaps some
more configuration options specially for background images.
Medium Priority
---------------
**Background-attachment** is not yet implemented, and will be postponed.
**Calls to dw::core::ImgRenderer::fatal** are incomplete. As an
example, it is not called, when connecting to a server fails. (And so,
as far as I see, no cache client is started.)
Low Priority
------------
**Alpha support:** (not related to image backgrounds) currently alpha
support (and also colormap management) is done in dicache, while
dw::Image is only created with type RGB. This leads to several problems:
- One dicache entry (representing an image related to the same URL),
which has only one background color, may refer to different images
with different background colors.
- The dicache only handles background colors, not background images.
The solution is basically simple: keep alpha support out of dicache;
instead implement RGBA in dw::Image. As it seems, the main problem is
alpha support in FLTK/X11.
Solved (Must Be Documented)
---------------------------
*Drawing background images row by row may become slow. As an
alternative, dw::core::ImgRenderer::finish could be used. However,
drawing row by row could become an option.* There is now
dw::core::style::drawBackgroundLineByLine, which can be changed in the
code, and is set to *false*. The old code still exists, so changing
this to *true* activates again drawing line by line.
(For image widgets, this could also become an option: in contexts,
when image data is retrieved in a very fast way.)
*/

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 42 KiB

View File

@ -0,0 +1,706 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="210mm"
height="297mm"
id="svg2"
version="1.1"
inkscape:version="0.48.3.1 r9886"
sodipodi:docname="dw-interrupted-drawing-2.svg"
inkscape:export-filename="/home/sg/dev/dillo/dillo-grows-work-intdraw/doc/dw-interrupted-drawing-2.png"
inkscape:export-xdpi="69.620003"
inkscape:export-ydpi="69.620003">
<defs
id="defs4">
<marker
inkscape:stockid="Arrow2Lend"
orient="auto"
refY="0.0"
refX="0.0"
id="Arrow2Lend"
style="overflow:visible;">
<path
id="path4042"
style="fill-rule:evenodd;stroke-width:0.62500000;stroke-linejoin:round;"
d="M 8.7185878,4.0337352 L -2.2072895,0.016013256 L 8.7185884,-4.0017078 C 6.9730900,-1.6296469 6.9831476,1.6157441 8.7185878,4.0337352 z "
transform="scale(1.1) rotate(180) translate(1,0)" />
</marker>
<marker
inkscape:stockid="Arrow2Lend"
orient="auto"
refY="0"
refX="0"
id="Arrow2Lend-2"
style="overflow:visible">
<path
inkscape:connector-curvature="0"
id="path4042-4"
style="fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round"
d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z"
transform="matrix(-1.1,0,0,-1.1,-1.1,0)" />
</marker>
<marker
inkscape:stockid="Arrow2Lend"
orient="auto"
refY="0"
refX="0"
id="Arrow2Lend-8"
style="overflow:visible">
<path
inkscape:connector-curvature="0"
id="path4042-0"
style="fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round"
d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z"
transform="matrix(-1.1,0,0,-1.1,-1.1,0)" />
</marker>
<marker
inkscape:stockid="Arrow2Lend"
orient="auto"
refY="0"
refX="0"
id="Arrow2Lend-24"
style="overflow:visible">
<path
inkscape:connector-curvature="0"
id="path4042-2"
style="fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round"
d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z"
transform="matrix(-1.1,0,0,-1.1,-1.1,0)" />
</marker>
<marker
inkscape:stockid="Arrow2Lend"
orient="auto"
refY="0"
refX="0"
id="Arrow2Lend-29"
style="overflow:visible">
<path
inkscape:connector-curvature="0"
id="path4042-1"
style="fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round"
d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z"
transform="matrix(-1.1,0,0,-1.1,-1.1,0)" />
</marker>
<marker
inkscape:stockid="Arrow2Lend"
orient="auto"
refY="0"
refX="0"
id="Arrow2Lend-5"
style="overflow:visible">
<path
inkscape:connector-curvature="0"
id="path4042-41"
style="fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round"
d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z"
transform="matrix(-1.1,0,0,-1.1,-1.1,0)" />
</marker>
<marker
inkscape:stockid="Arrow2Lend"
orient="auto"
refY="0"
refX="0"
id="Arrow2Lend-23"
style="overflow:visible">
<path
inkscape:connector-curvature="0"
id="path4042-19"
style="fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round"
d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z"
transform="matrix(-1.1,0,0,-1.1,-1.1,0)" />
</marker>
<marker
inkscape:stockid="Arrow2Lend"
orient="auto"
refY="0"
refX="0"
id="Arrow2Lend-7"
style="overflow:visible">
<path
inkscape:connector-curvature="0"
id="path4042-9"
style="fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round"
d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z"
transform="matrix(-1.1,0,0,-1.1,-1.1,0)" />
</marker>
<marker
inkscape:stockid="Arrow2Lend"
orient="auto"
refY="0"
refX="0"
id="Arrow2Lend-0"
style="overflow:visible">
<path
inkscape:connector-curvature="0"
id="path4042-97"
style="fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round"
d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z"
transform="matrix(-1.1,0,0,-1.1,-1.1,0)" />
</marker>
<marker
inkscape:stockid="Arrow2Lend"
orient="auto"
refY="0"
refX="0"
id="Arrow2Lend-0-4"
style="overflow:visible">
<path
inkscape:connector-curvature="0"
id="path4042-97-1"
style="fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round"
d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z"
transform="matrix(-1.1,0,0,-1.1,-1.1,0)" />
</marker>
<marker
inkscape:stockid="Arrow2Lend"
orient="auto"
refY="0"
refX="0"
id="Arrow2Lend-0-7"
style="overflow:visible">
<path
inkscape:connector-curvature="0"
id="path4042-97-5"
style="fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round"
d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z"
transform="matrix(-1.1,0,0,-1.1,-1.1,0)" />
</marker>
<marker
inkscape:stockid="Arrow2Lend"
orient="auto"
refY="0"
refX="0"
id="Arrow2Lend-0-8"
style="overflow:visible">
<path
inkscape:connector-curvature="0"
id="path4042-97-8"
style="fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round"
d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z"
transform="matrix(-1.1,0,0,-1.1,-1.1,0)" />
</marker>
<marker
inkscape:stockid="Arrow2Lend"
orient="auto"
refY="0"
refX="0"
id="Arrow2Lend-0-3"
style="overflow:visible">
<path
inkscape:connector-curvature="0"
id="path4042-97-2"
style="fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round"
d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z"
transform="matrix(-1.1,0,0,-1.1,-1.1,0)" />
</marker>
<marker
inkscape:stockid="Arrow2Lend"
orient="auto"
refY="0"
refX="0"
id="Arrow2Lend-1"
style="overflow:visible">
<path
inkscape:connector-curvature="0"
id="path4042-7"
style="fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round"
d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z"
transform="matrix(-1.1,0,0,-1.1,-1.1,0)" />
</marker>
<marker
inkscape:stockid="Arrow2Lend"
orient="auto"
refY="0"
refX="0"
id="marker5604"
style="overflow:visible">
<path
inkscape:connector-curvature="0"
id="path5606"
style="fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round"
d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z"
transform="matrix(-1.1,0,0,-1.1,-1.1,0)" />
</marker>
</defs>
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="0.35355339"
inkscape:cx="437.30872"
inkscape:cy="337.60168"
inkscape:document-units="px"
inkscape:current-layer="layer1"
showgrid="true"
inkscape:window-width="1600"
inkscape:window-height="900"
inkscape:window-x="0"
inkscape:window-y="0"
inkscape:window-maximized="0">
<inkscape:grid
type="xygrid"
id="grid2985" />
</sodipodi:namedview>
<metadata
id="metadata7">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<g
inkscape:label="Ebene 1"
inkscape:groupmode="layer"
id="layer1">
<g
id="g3977"
transform="translate(2.992126e-6,177.16535)">
<rect
y="24.803127"
x="17.716536"
height="70.866142"
width="141.73228"
id="rect2997"
style="fill:none;stroke:#000000;stroke-width:2;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" />
<flowRoot
transform="translate(20.094709,-2.1798012)"
style="font-size:12px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans"
id="flowRoot3786"
xml:space="preserve"><flowRegion
id="flowRegion3788"><rect
y="48.270561"
x="46.467018"
height="28.284271"
width="75.256363"
id="rect3790" /></flowRegion><flowPara
style="font-size:20px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-family:Nimbus Sans L;-inkscape-font-specification:Nimbus Sans L"
id="flowPara3792">body</flowPara></flowRoot> </g>
<g
id="g3984"
transform="translate(2.992126e-6,177.16535)">
<rect
y="24.803127"
x="194.8819"
height="70.866142"
width="141.73228"
id="rect3767"
style="fill:#ffe0e0;fill-opacity:1;stroke:#000000;stroke-width:2;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" />
<text
sodipodi:linespacing="125%"
id="text3832"
y="67.096199"
x="243.2318"
style="font-size:12px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans"
xml:space="preserve"><tspan
style="font-size:20px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-family:Nimbus Sans L;-inkscape-font-specification:Nimbus Sans L"
y="67.096199"
x="243.2318"
id="tspan3834"
sodipodi:role="line">#sc-1</tspan></text>
</g>
<g
id="g3989"
transform="translate(176.96508,177.13197)">
<rect
y="24.803127"
x="372.04724"
height="70.866142"
width="141.73228"
id="rect3767-6"
style="fill:#b0ffb0;fill-opacity:1;stroke:#000000;stroke-width:2;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" />
<text
sodipodi:linespacing="125%"
id="text3836"
y="67.356201"
x="425.39713"
style="font-size:20px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans"
xml:space="preserve"><tspan
y="67.356201"
x="425.39713"
id="tspan3838"
sodipodi:role="line"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-family:Nimbus Sans L;-inkscape-font-specification:Nimbus Sans L">#fl-1</tspan></text>
</g>
<g
id="g3994"
transform="translate(-177.81225,177.39197)">
<rect
y="24.803127"
x="549.21259"
height="70.866142"
width="141.73228"
id="rect3767-2"
style="fill:#f0f0ff;fill-opacity:1;stroke:#000000;stroke-width:2;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" />
<text
sodipodi:linespacing="125%"
id="text3840"
y="67.096199"
x="595.92249"
style="font-size:20px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans"
xml:space="preserve"><tspan
y="67.096199"
x="595.92249"
id="tspan3842"
sodipodi:role="line"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-family:Nimbus Sans L;-inkscape-font-specification:Nimbus Sans L">#sc-2</tspan></text>
</g>
<path
style="fill:none;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
d="m 88.582679,272.83462 -2e-6,531.49607"
id="path3890"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cc" />
<path
style="fill:none;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
d="m 265.74803,272.83462 0,531.49607"
id="path3890-9"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cc" />
<path
style="fill:none;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
d="m 442.91339,272.83462 0,531.49607"
id="path3890-2"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cc" />
<path
style="fill:none;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
d="m 620.07874,272.83462 0,531.49607"
id="path3890-29"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cc" />
<rect
style="fill:#ffffa0;fill-opacity:1;stroke:#000000;stroke-width:2;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
id="rect3931"
width="35.433071"
height="460.62991"
x="70.866142"
y="308.2677" />
<rect
style="fill:#ffffa0;fill-opacity:1;stroke:#000000;stroke-width:2;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
id="rect3933"
width="35.433071"
height="354.33069"
x="248.03149"
y="343.70078" />
<flowRoot
xml:space="preserve"
id="flowRoot3935"
style="font-size:20px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans"
transform="translate(2.992126e-6,177.16535)"><flowRegion
id="flowRegion3937"><rect
id="rect3939"
width="835.71429"
height="147.14285"
x="-1.4285715"
y="-40.494953" /></flowRegion><flowPara
id="flowPara3941"></flowPara></flowRoot> <path
style="fill:none;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;marker-end:url(#Arrow2Lend)"
d="m 106.29921,343.70076 141.73229,0"
id="path3890-29-5"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cc" />
<rect
style="fill:#ffffa0;fill-opacity:1;stroke:#000000;stroke-width:3.00000191;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
id="rect4643"
width="35.433094"
height="70.866158"
x="602.36218"
y="414.56689" />
<path
style="fill:none;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;marker-end:url(#Arrow2Lend)"
d="m 283.46457,414.56691 318.89763,0"
id="path3890-29-5-2"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cc" />
<path
style="fill:none;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:6, 6;stroke-dashoffset:0;marker-end:url(#Arrow2Lend)"
d="m 602.36221,485.43305 -318.89764,0"
id="path3890-29-5-2-0-0"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cc" />
<path
style="fill:none;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:6, 6;stroke-dashoffset:0;marker-end:url(#Arrow2Lend)"
d="m 248.0315,698.03147 -141.73229,0"
id="path3890-29-5-2-0-0-8"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cc" />
<rect
style="fill:#ffffa0;fill-opacity:1;stroke:#000000;stroke-width:2;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0"
id="rect4767"
width="35.433071"
height="35.433071"
x="602.36218"
y="733.46454" />
<path
style="fill:none;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;marker-end:url(#Arrow2Lend)"
d="m 106.29921,733.46454 496.06299,10e-6"
id="path3890-29-5-4"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cc" />
<path
style="fill:none;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;marker-end:url(#Arrow2Lend)"
d="m 17.716536,308.26769 53.149608,0"
id="path3890-29-5-27"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cc" />
<path
style="fill:none;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:6, 6;stroke-dashoffset:0;marker-end:url(#Arrow2Lend)"
d="m 70.866142,768.89762 -53.149613,0"
id="path3890-29-5-2-0-0-8-4"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cc" />
<path
style="fill:none;stroke:#ff0000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
d="m 354.33071,715.74801 318.89763,53.14961 0,0 0,0 0,0"
id="path4865"
inkscape:connector-curvature="0"
sodipodi:nodetypes="ccccc" />
<path
style="fill:none;stroke:#ff0000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
d="m 354.3307,768.89762 318.89764,-53.14961 0,0 0,0"
id="path4865-9"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cccc" />
<path
style="fill:none;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;marker-end:url(#Arrow2Lend-0)"
d="m 88.582677,201.96848 c 17.716533,-88.58268 141.732283,-88.58268 159.448823,0"
id="path4914"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cc" />
<path
style="fill:none;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;marker-end:url(#Arrow2Lend-0)"
d="m 283.46456,201.96848 c 17.71654,-88.58268 141.73229,-88.58268 159.44883,0"
id="path4914-8"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cc" />
<path
style="fill:none;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;marker-end:url(#Arrow2Lend-0)"
d="m 88.582677,201.96848 c 35.433073,-212.598421 513.779523,-212.598423 549.212603,0"
id="path4914-0"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cc" />
<path
style="fill:none;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:2, 4;stroke-dashoffset:0;marker-end:url(#Arrow2Lend-0)"
d="m 265.74802,201.96848 c 35.43308,-159.448817 301.18111,-159.448817 336.61418,0"
id="path4914-8-3"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cc" />
<path
style="fill:none;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;stroke-miterlimit:4;stroke-dasharray:2,4;stroke-dashoffset:0"
d="m 318.89764,361.4173 c -17.71654,0 -35.43307,0 -35.43307,0 l 0,0"
id="path5443"
inkscape:connector-curvature="0" />
<text
xml:space="preserve"
style="font-size:20px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Nimbus Mono L;-inkscape-font-specification:Nimbus Mono L"
x="414.18863"
y="362.88965"
id="text5445"
sodipodi:linespacing="125%"><tspan
sodipodi:role="line"
id="tspan5447"
x="414.18863"
y="362.88965" /></text>
<g
id="g5586">
<rect
rx="0"
ry="17.716547"
y="325.98422"
x="318.89764"
height="70.866142"
width="194.8819"
id="rect5453"
style="fill:#ffffe0;fill-opacity:1;stroke:#000000;stroke-width:2;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0" />
<text
sodipodi:linespacing="125%"
id="text5455"
y="354.7933"
x="418.09937"
style="font-size:20px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Nimbus Mono L;-inkscape-font-specification:Nimbus Mono L"
xml:space="preserve"><tspan
style="font-size:18px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:center;text-anchor:middle;font-family:Nimbus Sans L;-inkscape-font-specification:Nimbus Sans L"
y="354.7933"
x="418.09937"
id="tspan5457"
sodipodi:role="line">#sc-1 detects that #fl-1</tspan><tspan
id="tspan5459"
style="font-size:18px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:center;text-anchor:middle;font-family:Nimbus Sans L;-inkscape-font-specification:Nimbus Sans L"
y="377.2933"
x="418.09937"
sodipodi:role="line">interrupts the drawing</tspan></text>
</g>
<path
style="fill:none;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:2, 4;stroke-dashoffset:0"
d="m 673.22836,450.00001 c -17.71654,0 -35.43307,0 -35.43307,0 l 0,0"
id="path5443-9"
inkscape:connector-curvature="0"
sodipodi:nodetypes="ccc" />
<g
id="g5531"
transform="translate(-88.582663,106.29923)">
<rect
rx="0"
ry="17.716547"
y="414.56689"
x="673.22833"
height="70.866142"
width="194.8819"
id="rect5453-7"
style="fill:#ffffe0;fill-opacity:1;stroke:#000000;stroke-width:2;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0" />
<text
sodipodi:linespacing="125%"
id="text5455-9"
y="443.37598"
x="770.91229"
style="font-size:20px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Nimbus Mono L;-inkscape-font-specification:Nimbus Mono L"
xml:space="preserve"><tspan
id="tspan5517"
style="font-size:18px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:center;text-anchor:middle;font-family:Nimbus Sans L;-inkscape-font-specification:Nimbus Sans L"
y="443.37598"
x="770.91229"
sodipodi:role="line">#fl-1 is drawn as</tspan><tspan
id="tspan5521"
style="font-size:18px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:center;text-anchor:middle;font-family:Nimbus Sans L;-inkscape-font-specification:Nimbus Sans L"
y="465.87598"
x="770.91229"
sodipodi:role="line">an interruption</tspan></text>
</g>
<flowRoot
xml:space="preserve"
id="flowRoot5523"
style="fill:black;stroke:none;stroke-opacity:1;stroke-width:1px;stroke-linejoin:miter;stroke-linecap:butt;fill-opacity:1;font-family:Nimbus Mono L;font-style:normal;font-weight:normal;font-size:20px;line-height:125%;letter-spacing:0px;word-spacing:0px;-inkscape-font-specification:Nimbus Mono L;font-stretch:normal;font-variant:normal"><flowRegion
id="flowRegion5525"><rect
id="rect5527"
width="230.37166"
height="100.60158"
x="662.54175"
y="400.53543" /></flowRegion><flowPara
id="flowPara5529"></flowPara></flowRoot> <g
id="g5699">
<rect
rx="0"
ry="17.716547"
y="502.68695"
x="318.90851"
height="70.866142"
width="194.8819"
id="rect5453-70"
style="fill:#ffffe0;fill-opacity:1;stroke:#000000;stroke-width:2;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0" />
<text
sodipodi:linespacing="125%"
id="text5455-7"
y="531.49603"
x="418.11023"
style="font-size:20px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Nimbus Mono L;-inkscape-font-specification:Nimbus Mono L"
xml:space="preserve"><tspan
id="tspan5459-7"
style="font-size:18px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:center;text-anchor:middle;font-family:Nimbus Sans L;-inkscape-font-specification:Nimbus Sans L"
y="531.49603"
x="418.11023"
sodipodi:role="line">drawing of #sc-1</tspan><tspan
id="tspan5584"
style="font-size:18px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:center;text-anchor:middle;font-family:Nimbus Sans L;-inkscape-font-specification:Nimbus Sans L"
y="553.99603"
x="418.11023"
sodipodi:role="line">is continued</tspan></text>
</g>
<rect
style="fill:#ffffa0;fill-opacity:1;stroke:#000000;stroke-width:3.00000191;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
id="rect4643-9"
width="35.433094"
height="70.866158"
x="425.19681"
y="591.73224" />
<path
style="fill:none;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;marker-end:url(#Arrow2Lend)"
d="m 283.46457,591.73226 141.73227,10e-6"
id="path3890-29-5-2-8"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cc" />
<path
style="fill:none;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:6, 6;stroke-dashoffset:0;marker-end:url(#Arrow2Lend)"
d="m 425.19685,662.59841 -141.73228,-10e-6"
id="path3890-29-5-2-0-0-4"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cc" />
<path
style="fill:none;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:2, 4;stroke-dashoffset:0"
d="m 673.22835,751.18108 c -17.71654,0 -35.43307,0 -35.43307,0 l 0,0"
id="path5443-8-2"
inkscape:connector-curvature="0" />
<g
id="g5713"
transform="translate(-88.571786,105.83661)">
<path
inkscape:connector-curvature="0"
id="path5443-8"
d="m 726.37796,786.61415 c -17.71654,0 -35.43307,0 -35.43307,0 l 0,0"
style="fill:none;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:2, 4;stroke-dashoffset:0" />
<rect
rx="0"
ry="17.716547"
y="715.74799"
x="673.22833"
height="70.866142"
width="194.8819"
id="rect5453-70-2"
style="fill:#ffffe0;fill-opacity:1;stroke:#000000;stroke-width:2;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0" />
<text
sodipodi:linespacing="125%"
id="text5455-7-4"
y="744.55707"
x="772.43005"
style="font-size:20px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Nimbus Mono L;-inkscape-font-specification:Nimbus Mono L"
xml:space="preserve"><tspan
id="tspan5707"
style="font-size:18px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:center;text-anchor:middle;font-family:Nimbus Sans L;-inkscape-font-specification:Nimbus Sans L"
y="744.55707"
x="772.43005"
sodipodi:role="line">no need anymore</tspan><tspan
id="tspan5711"
style="font-size:18px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:center;text-anchor:middle;font-family:Nimbus Sans L;-inkscape-font-specification:Nimbus Sans L"
y="767.05707"
x="772.43005"
sodipodi:role="line">to draw #fl-1</tspan></text>
</g>
<path
style="fill:none;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:2, 4;stroke-dashoffset:0"
d="m 318.89764,538.58266 c -17.71654,0 -35.43307,0 -35.43307,0 l 0,0"
id="path5443-9-0"
inkscape:connector-curvature="0"
sodipodi:nodetypes="ccc" />
<path
style="fill:none;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:2, 4;stroke-dashoffset:0"
d="m 673.22835,449.99998 0,0"
id="path5443-9-6"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cc" />
<path
style="fill:none;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;stroke-miterlimit:4;stroke-dasharray:2,4;stroke-dashoffset:0"
d="m 673.22835,520.86612 c 0,-70.86614 0,-70.86614 0,-70.86614"
id="path5738"
inkscape:connector-curvature="0" />
<path
style="fill:none;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:2, 4;stroke-dashoffset:0"
d="m 673.22835,822.04722 c 0,-70.86614 0,-70.86614 0,-70.86614"
id="path5738-7"
inkscape:connector-curvature="0" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 32 KiB

View File

@ -0,0 +1,121 @@
/** \page dw-interrupted-drawing Interrupted drawing
Describing the problem
======================
Without interrupting drawing (which is described below), a widget can
define the order in which its parts (background, non-widget content,
child widgets, etc.) are drawn, but it must be drawn as a whole. There
are situations when this is not possible.
Consider the following simple HTML document:
<head>
<style>
#sc-1 { position: relative; z-index: 1; background: #ffe0e0; }
#fl-1 { float: right; background: #b0ffb0; }
#sc-2 { position: relative; z-index: 1; background: #f0f0ff; }
</style>
</head>
<body>
<div id="sc-1">
<div id="fl-1">
Float, line 1/3<br/>
Float, line 2/3<br/>
Float, line 3/3
</div>
Stacking Context 1
<div id="sc-2">Stacking Context 2</div>
</div>
</body>
The rendering will look like this:
\image html dw-interrupted-drawing-1.png
Note the missing "Float, line 2/3" of element #fl-1, which is covered
by element #sc-2.
As described in \ref dw-out-of-flow, it has to be distinguished
between the *container* hierarchy (equivalent to the hierarchy of
dw::core::Widget.) and the the *generator* hierarchy. In the following
diagram, the former is represented by solid lines, the latter by
dotted lines:
\dot
digraph G {
node [shape=rect, fontname=Helvetica, fontsize=10];
edge [arrowhead="vee"];
"#sc-1" [fillcolor="#ffe0e0", style="filled"];
"#fl-1" [fillcolor="#b0ffb0", style="filled"];
"#sc-2" [fillcolor="#f0f0ff", style="filled"];
"body" -> "#sc-1";
"body" -> "#fl-1";
{ rank=same; "#sc-1" -> "#fl-1" [style=dotted]; }
"#sc-1" -> "#sc-2";
}
\enddot
The drawing order of the four elements (represented by widgets) is:
- body,
- #sc-1,
- #fl-1,
- #sc-2.
Since
1. #sc-2 is a child of #sc-1, but
2. #fl-1 is a child of the body, and
3. a widget can only draw its descendants (not necessary children,
but drawing siblings is not allowed),
#sc-1 cannot be drawn as a whole; instead drawing is **interrupted**
by #fl-1. This means:
1. the background and text of #sc-1 is drawn;
2. drawing of #sc-1 is **interrupted** by #fl-1 (see below for details),
3. drawing of #sc-1 is **continued**, by drawing #sc-2.
The exact control flow is described in this sequence diagram:
\image html dw-interrupted-drawing-2.png
When is drawing interrupted?
============================
A widget out of flow is regarded as part of the stacking context (see
\ref dw-stacking-context) of its *generator* (in the example above:
#fl-1 is part of the stacking context stablished by #sc-1, not the one
established by body). For this reason, a widget out of flow must, in
some cases, drawn while the *gerator* is drawn, as an
interruption. The exact rule:
A widget out of flow must be drawn as an interruption (while the
*generator* is drawn) if the stacking context of the generator (to
which this widget belongs) is in front of the stacking context of the
container (the parent widget).
See dw::oof::OOFAwareWidget::doesWidgetOOFInterruptDrawing.
How does interruption of drawing work?
======================================
When a widget detects that an other widget should be drawn as
interruption (see above), it calls dw::core::Widget::drawInterruption,
which
1. draws the widget within another "context" (area and reference
widget); for this the original drawing area
(dw::core::DrawingContext::getToplevelArea) is used.
2. Using dw::core::DrawingContext::addWidgetDrawnAsInterruption, and
checking later with
dw::core::DrawingContext::hasWidgetBeenDrawnAsInterruption prevents
these widgets from being drawn twice.
*/

256
devdoc/dw-layout-views.doc Normal file
View File

@ -0,0 +1,256 @@
/** \page dw-layout-views Layout and Views
Rendering of Dw is done in a way resembling the model-view pattern, at
least formally. Actually, the counterpart of the model, the layout
(dw::core::Layout), does a bit more than a typical model, namely the
layouting (delegated to the widget tree, see \ref dw-layout-widgets),
and the view does a bit less than a typical view, i.e. only the actual
drawing.
Additionally, there is a structure representing common properties of
the platform. A platform is typically related to the underlying UI
toolkit, but other uses may be thought of.
This design helps to achieve two important goals:
<ul>
<li> Abstraction of the actual drawing, by different implementations
of dw::core::View.
<li> It makes portability simple.
</ul>
<h2>Viewports</h2>
Although the design implies that the usage of viewports should be
fully transparent to the layout module, this cannot be fully achieved,
for the following reasons:
<ul>
<li> Some features, which are used on the level of dw::core::Widget,
e.g. anchors, refer to scrolling positions.
<li> Size hints (see \ref dw-layout-widgets) depend on the viewport
sizes, e.g. when the user changes the window size, and so also
the size of a viewport, the text within should be rewrapped.
</ul>
Therefore, dw::core::Layout keeps track of the viewport size, the
viewport position, and even the thickness of the scrollbars, they are
relevant, see below for more details.
If a viewport is not used, however, the size is not defined.
Whether a given dw::core::View implementation is a viewport or not, is
defined by the return value of dw::core::View::usesViewport. If this
method returns false, the following methods need not to be implemented
at all:
<ul>
<li> dw::core::View::getHScrollbarThickness,
<li> dw::core::View::getVScrollbarThickness,
<li> dw::core::View::scrollTo, and
<li> dw::core::View::setViewportSize.
</ul>
<h3>Scrolling Positions</h3>
The scrolling position is the canvas position at the upper left corner
of the viewport. Views using viewports must
<ol>
<li> change this value on request (dw::core::View::scrollTo), and
<li> tell other changes to the layout, e.g. caused by user events
(dw::core::Layout::scrollPosChanged).
</ol>
Applications of scrolling positions (anchors, test search etc.) are
handled by the layout, in a way fully transparent to the view.
<h3>Scrollbars</h3>
A feature of the viewport size model are scrollbars. There may be a
vertical scrollbar and a horizontal scrollbar, displaying the
relationship between canvas and viewport height or width,
respectively. If they are not needed, they are hidden, to save screen
space.
Since scrollbars decrease the usable space of a view, dw::core::Layout
must know how much space they take. The view returns, via
dw::core::View::getHScrollbarThickness and
dw::core::View::getVScrollbarThickness, how thick they will be, when
visible.
Viewport sizes, which denote the size of the viewport widgets, include
scrollbar thicknesses. When referring to the viewport \em excluding
the scrollbars space, we will call it "usable viewport size", this is
the area, which is used to display the canvas.
<h2>Drawing</h2>
A view must implement several drawing methods, which work on the whole
canvas. If it is necessary to convert them (e.g. into
dw::fltk::FltkViewport), this is done in a way fully transparent to
dw::core::Widget and dw::core::Layout, instead, this is done by the
view implementation.
There exist following situations:
<ul>
<li> A view gets an expose event: It will delegate this to the
layout (dw::core::Layout::draw), which will then pass it to the
widgets (dw::core::Widget::draw), with the view as a parameter.
Eventually, the widgets will call drawing methods of the view.
<li> A widget requests a redraw: In this case, the widget will
delegate this to the layout (dw::core::Layout::queueDraw), which
delegates it to the view (dw::core::View::queueDraw).
Typically, the view will queue these requests for efficiency.
<li> A widget requests a resize: This case is described below, in short,
dw::core::View::queueDrawTotal is called for the view.
</ul>
If the draw method of a widget is implemented in a way that it may
draw outside of the widget's allocation, it should draw into a
<i>clipping view.</i> A clipping view is a view related to the actual
view, which guarantees that the parts drawn outside are discarded. At
the end, the clipping view is merged into the actual view. Sample
code:
\code
void Foo::draw (dw::core::View *view, dw::core::Rectangle *area)
{
// 1. Create a clipping view.
dw::core::View clipView =
view->getClippingView (allocation.x, allocation.y,
allocation.width, getHeight ());
// 2. Draw into clip_view
clipView->doSomeDrawing (...);
// 3. Draw the children, they receive the clipping view as argument.
dw::core::Rectangle *childArea
for (<all relevant children>) {
if (child->intersects (area, &childArea))
child->draw (clipView, childArea);
}
// 4. Merge
view->mergeClippingView (clipView);
}
\endcode
A drawing process is always embedded into calls of
dw::core::View::startDrawing and dw::core::View::finishDrawing. An
implementation of this may e.g. use backing pixmaps, to prevent
flickering.
<h2>Sizes</h2>
In the simplest case, the view does not have any influence on
the canvas size, so it is told about changes of the
canvas size by a call to dw::core::View::setCanvasSize. This happens
in the following situations:
<ul>
<li> dw::core::Layout::addWidget,
<li> dw::core::Layout::removeWidget (called by dw::core::Widget::~Widget),
and
<li> dw::core::Layout::queueResize (called by
dw::core::Widget::queueResize, when a widget itself requests a size
change).
</ul>
<h3>Viewports</h3>
There are two cases where the viewport size changes:
<ul>
<li> As an reaction on a user event, e.g. when the user changes the
window size. In this case, the view delegates this
change to the layout, by calling
dw::core::Layout::viewportSizeChanged.
<li> The viewport size may also depend on the visibility of UI
widgets, which depend on the world size, e.g scrollbars,
generally called "viewport markers". This is described in a separate
section.
</ul>
After the creation of the layout, the viewport size is undefined. When
a view is attached to a layout, and this view can already specify
its viewport size, it may call
dw::core::Layout::viewportSizeChanged within the implementation of
dw::core::Layout::setLayout. If not, it may do this as soon as the
viewport size is known.
Generally, the scrollbars have to be considered. If e.g. an HTML page
is rather small, it looks like this:
\image html dw-viewport-without-scrollbar.png
If some more data is retrieved, so that the height exceeds the
viewport size, the text has to be rewrapped, since the available width
gets smaller, due to the vertical scrollbar:
\image html dw-viewport-with-scrollbar.png
Notice the different line breaks.
This means circular dependencies between these different sizes:
<ol>
<li> Whether the scrollbars are visible or not, determines the
usable space of the viewport.
<li> From the usable space of the viewport, the size hints for the
toplevel are calculated.
<li> The size hints for the toplevel widgets may have an effect on its
size, which is actually the canvas size.
<li> The canvas size determines the visibility of the scrollbarss.
</ol>
To make an implementation simpler, we simplify the model:
<ol>
<li> For the calls to dw::core::Widget::setAscent and
dw::core::Widget::setDescent, we will always exclude the
horizontal scrollbar thickness (i.e. assume the horizontal
scrollbar is used, although the visibility is determined correctly).
<li> For the calls to dw::core::Widget::setWidth, we will calculate
the usable viewport width, but with the general assumption, that
the widget generally gets higher.
</ol>
This results in the following rules:
<ol>
<li> Send always (when it changes) dw::core::Layout::viewportHeight
minus the maximal value of dw::core::View::getHScrollbarThickness as
argument to dw::core::Widget::setAscent, and 0 as argument to
dw::core::Widget::setDescent.
<li> There is a flag, dw::core::Layout::canvasHeightGreater, which is set
to false in the following cases:
<ul>
<li> dw::core::Layout::addWidget,
<li> dw::core::Layout::removeWidget (called by dw::core::Widget::~Widget),
and
<li> dw::core::Layout::viewportSizeChanged.
</ul>
Whenever the canvas size is calculated (dw::core::Layout::resizeIdle),
and dw::core::Layout::canvasHeightGreater is false, a test is made,
whether the widget has in the meantime grown that high, that the second
argument should be set to true (i.e. the vertical scrollbar gets visible).
As soon as and dw::core::Layout::canvasHeightGreater is true, no such test
is done anymore.
</ol>
*/

View File

@ -0,0 +1,267 @@
/** \page dw-layout-widgets Layout and Widgets
Both, the layouting and the drawing is delegated to a tree of
widgets. A widget represents a given part of the document, e.g. a text
block, a table, or an image. Widgets may be nested, so layouting and
drawing may be delegated by one widget to its child widgets.
Where to define the borders of a widget, whether to combine different
widgets to one, or to split one widget into multiple ones, should be
considered based on different concerns:
<ul>
<li> First, there are some restrictions of Dw:
<ul>
<li> The allocation (this is the space a widget allocates at
a time) of a dillo widget is always rectangular, and
<li> the allocation of a child widget must be a within the allocation
of the parent widget.
</ul>
<li> Since some widgets are already rather complex, an important goal
is to keep the implementation of the widget simple.
<li> Furthermore, the granularity should not be too fine, because of the
overhead each single widget adds.
</ul>
For CSS, there will be a document tree on top of Dw, this will be
flexible enough when mapping the document structure on the widget
structure, so you should not have the document structure in mind.
<h2>Sizes</h2>
\ref dw-widget-sizes
<h2>Styles</h2>
Each widget is assigned a style, see dw::core::style for more
information.
<h2>Iterators</h2>
Widgets must implement dw::core::Widget::iterator. There are several
common iterators:
<ul>
<li>dw::core::EmptyIterator, and
<li>dw::core::TextIterator.
</ul>
Both hide the constructor, use the \em create method.
These simple iterators only iterate through one widget, it does not
have to iterate recursively through child widgets. Instead, the type
dw::core::Content::WIDGET is returned, and the next call of
dw::core::Iterator::next will return the piece of contents \em after
(not within) this child widget.
This makes implementation much simpler, for recursive iteration, there
is dw::core::DeepIterator.
<h2>Anchors and Scrolling</h2>
\todo This section is not implemented yet, after the implementation,
the documentation should be reviewed.
Here is a description, what is to be done for a widget
implementation. How to jump to anchors, set scrolling positions
etc. is described in \ref dw-usage.
<h3>Anchors</h3>
Anchors are position markers, which are identified by a name, which is
unique in the widget tree. The widget must care about anchors in three
different situations:
<ol>
<li> Adding an anchor is inititiated by a specific widget method, e.g.
dw::Textblock::addAnchor. Here, dw::core::Widget::addAnchor must be
called,
<li> Whenever the position of an anchor is changed,
dw::core::Widget::changeAnchor is called (typically, this is done
in the implementation of dw::core::Widget::sizeAllocateImpl).
<li> When a widget is destroyed, the anchor must be removed, by calling
dw::core::Widget::removeAnchor.
</ol>
All these methods are delegated to dw::core::Layout, which manages
anchors centrally. If the anchor in question has been set to jump to,
the viewport position is automatically adjusted, see \ref
dw-usage.
<h2>Drawing</h2>
In two cases, a widget has to be drawn:
<ol>
<li> as a reaction on an expose event,
<li> if the widget content has changed and it needs to be redrawn.
</ol>
In both cases, drawing is done by the implementation of
dw::core::Widget::draw, which draws into the view.
Each view provides some primitive methods for drawing, most should be
obvious. Note that the views do not know anything about dillo
widgets, and so coordinates have to be passed as canvas coordinates.
A widget may only draw in its own allocation. If this cannot be
achieved, a <i>clipping view</i> can be used, this is described in
\ref dw-layout-views. Generally, drawing should then look like:
\code
void Foo::draw (dw::core::View *view, dw::core::Rectangle *area)
{
// 1. Create a clipping view.
dw::core::View clipView =
view->getClippingView (allocation.x, allocation.y,
allocation.width, getHeight ());
// 2. Draw into clip_view
clipView->doSomeDrawing (...);
// 3. Draw the children, they receive the clipping view as argument.
dw::core::Rectangle *childArea
for (<all relevant children>) {
if (child->intersects (area, &childArea))
child->draw (clipView, childArea);
}
// 4. Merge
view->mergeClippingView (clipView);
}
\endcode
Clipping views are expensive, so they should be avoided when possible.
The second argument to dw::core::Widget::draw is the region, which has
to be drawn. This may (but needs not) be used for optimization.
If a widget contains child widgets, it must explicitly draw these
children (see also code example above). For this, there is the useful
method dw::core::Widget::intersects, which returns, which area of the
child must be drawn.
<h3>Explicit Redrawing</h3>
If a widget changes its contents, so that it must be redrawn, it must
call dw::core::Widget::queueDrawArea or
dw::core::Widget::queueDraw. The first variant expects a region within
the widget, the second will cause the whole widget to be redrawn. This
will cause an asynchronous call of dw::core::Widget::draw.
If only the size changes, a call to dw::core::Widget::queueResize is
sufficient, this will also queue a complete redraw (see \ref
dw-widget-sizes.)
<h2>Mouse Events</h2>
A widget may process mouse events. The view (\ref dw-layout-views)
passes mouse events to the layout, which then passes them to the
widgets. There are two kinds of mouse events:
<ul>
<li>events returning bool, and
<li>events returning nothing (void).
</ul>
The first group consists of:
<ul>
<li>dw::core::Widget::buttonPressImpl,
<li>dw::core::Widget::buttonReleaseImpl, and
<li>dw::core::Widget::motionNotifyImpl.
</ul>
For these events, a widget returns a boolean value, which denotes,
whether the widget has processed this event (true) or not (false). In
the latter case, the event is delegated according to the following
rules:
<ol>
<li> First, this event is passed to the bottom-most widget, in which
allocation the mouse position is in.
<li> If the widget does not process this event (returning false), it is
passed to the parent, and so on.
<li> The final result (whether \em any widget has processed this event) is
returned to the view.
</ol>
The view may return this to the UI toolkit, which then interprets this
in a similar way (whether the viewport, a UI widget, has processed
this event).
These events return nothing:
<ul>
<li>dw::core::Widget::enterNotifyImpl and
<li>dw::core::Widget::leaveNotifyImpl.
</ul>
since they are bound to a widget.
When processing mouse events, the layout always deals with two
widgets: the widget, the mouse pointer was in, when the previous mouse
event was processed, (below called the "old widget") and the widget,
in which the mouse pointer is now ("new widget").
The following paths are calculated:
<ol>
<li> the path from the old widget to the nearest common ancestor of the old
and the new widget, and
<li> the path from this ancestor to the new widget.
</ol>
For the widgets along these paths, dw::core::Widget::enterNotifyImpl
and dw::core::Widget::leaveNotifyImpl are called.
<h3>Signals</h3>
If a caller outside of the widget is interested in these events, he
can connect a dw::core::Layout::LinkReceiver. For those events with a
boolean return value, the results of the signal emission is regarded,
i.e. the delegation of an event to the parent of the widget can be
stopped by a signal receiver returning true, even if the widget method
returns false.
First, the widget method is called, then (in any case) the signal is
emitted.
<h3>Selection</h3>
If your widget has selectable contents, it should delegate the events
to dw::core::SelectionState (dw::core::Layout::selectionState).
<h2>Miscellaneous</h2>
<h3>Cursors</h3>
Each widget has a cursor, which is set by
dw::core::Widget::setCursor. If a cursor is assigned to a part of a
widget, this widget must process mouse events, and call
dw::core::Widget::setCursor explicitly.
(This will change, cursors should become part of
dw::core::style::Style.)
<h3>Background</h3>
Backgrounds are part of styles
(dw::core::style::Style::backgroundColor). If a widget assigns
background colors to parts of a widget (as dw::Table does for rows),
it must call dw::core::Widget::setBgColor for the children inside this
part.
*/

470
devdoc/dw-line-breaking.doc Normal file
View File

@ -0,0 +1,470 @@
/** \page dw-line-breaking Changes in Line-Breaking and Hyphenation
<div style="border: 2px solid #ffff00; margin-bottom: 0.5em;
padding: 0.5em 1em; background-color: #ffffe0"><b>Info:</b>
Should be incorporated into dw::Textblock.</div>
Introduction
============
For the implementation of hyphenation in dillo, not only a
hyphenation algorithm was implemented, but also, the line breaking was
changed to a simple optimization per line. Aside from the improvement
by this change per se, an important aspect is the introduction of
"penalties". Before this change, dillo put all words into a line which
fitted into it; now, a "badness" is calculated for a possible
breakpoint, and the best breakpoint, i.&nbsp;e. the breakpoint with the
smallest value for "badness", is chosen. This can be simply refined
to define "good" and "bad" breakpoints by assigning a "penalty"; the
best breakpoint is then the one with the smallest value of "badness +
penalty". Details can be found below.
Example: Normal spaces have a penalty of 0, while hyphenation points
get a penalty of, say, 1, since hyphenation is generally considered as
a bit "ugly" and should rather be avoided. Consider a situation where
the word "dillo" could be hyphenated, with the following badnesses:
- before "dillo": 0.6;
- between "dil-" and "lo": 0.2;
- after "dillo": 0.5.
Since the penalty is added, the last value is the best one, so "dillo"
is put at the end of the line, without hyphenation.
Under other circumstances (e.&nbsp;g. narrower lines), the values
might be different:
- before "dillo": infinite;
- between "dil-" and "lo": 0.3;
- after "dillo": 1.5.
In this case, even the addition of the penalty makes hyphenation the
best choice.
Literature
==========
Breaking Paragraphs Into Lines
------------------------------
Although dillo does not (yet?) implement the algorithm T<sub>E</sub>X
uses for line breaking, this document shares much of the notation used
by the article *Breaking Paragraphs Into Lines* by Donald E. Knuth and
Michael F. Plass; originally published in: Software -- Practice and
Experience **11** (1981), 1119-1184; reprinted in: *Digital
Typography* by Donalt E. Knuth, CSLI Publications 1999. Anyway an
interesting reading.
Hyphenation
-----------
Dillo uses the algorithm by Frank Liang, which is described in his
doctoral dissertation found at http://www.tug.org/docs/liang/. There
is also a description in chapter H ("Hyphenation") of *The
T<sub>E</sub>Xbook* by Donald E. Knuth, Addison-Wesley 1984.
Pattern files can be found at
http://www.ctan.org/tex-archive/language/hyphenation.
Overview of Changes
===================
Starting with this change, dw/textblock.cc has been split up; anything
related to line breaking has been moved into
dw/textblock_linebreaking.cc. This will also be done for other aspects
like floats. (Better, however, would be a clean logical split.)
An important change relates to the way that lines are added: before,
dillo would add a line as soon as a new word for this line was
added. Now, a line is added not before the *last* word of this line is
known. This has two important implications:
- Some values in dw::Textblock::Line, which represented values
accumulated within the line, could be removed, since now, these
values can be calculated simply in a loop.
- On the other hand, this means that some words may not belong to any
line. For this reason, in some cases (e.&nbsp;g. in
dw::Textblock::sizeRequestImpl) dw::Textblock::showMissingLines is
called, which creates temporary lines, which must, under other
circumstances, be removed again by
dw::Textblock::removeTemporaryLines, since they have been created
based on limited information, and so possibly in a wrong way. (See
below for details.)
When a word can be hyphenated, an instance of dw::Textblock::Word is
used for each part. Notice that soft hyphens are evaluated
immediately, but automatic hyphenation is done in a lazy way (details
below), so the number of instances may change. There are some new
attributes: only when dw::Textblock::Word::canBeHyphenated is set to
*true*, automatic hyphenation is allowed; it is set to false when soft
hyphens are used for a word, and (of course) by the automatic
hyphenation itself. Furthermore, dw::Textblock::Word::hyphenWidth
(more details in the comment there) has to be included when
calculating line widths.
Some values should be configurable: dw::Textblock::HYPHEN_BREAK, the
penalty for hyphens. Also dw::Textblock::Word::stretchability,
dw::Textblock::Word::shrinkability, which are both set in
dw::Textblock::addSpace.
Criteria for Line-Breaking
==========================
Before these changes to line breaking, a word (represented by
dw::Textblock::Word) had the following attributes related to
line-breaking:
- the width of the word itself, represented by
dw::Textblock::Word::size;
- the width of the space following the word, represented by
dw::Textblock::Word::origSpace.
In a more mathematical notation, the \f$i\f$th word has a width
\f$w_i\f$ and a space \f$s_i\f$.
A break was possible, when there was a space between the two words,
and the first possible break was chosen.
With hyphenation, the criteria are refined. Hyphenation should only be
used when otherwise line breaking results in very large spaces. We
define:
- the badness \f$\beta\f$ of a line, which is greater the more the
spaces between the words differ from the ideal space;
- a penalty \f$p\f$ for any possible break point.
The goal is to find those break points, where \f$\beta + p\f$ is
minimal.
Examples for the penalty \f$p\f$:
- 0 for normal line breaks (between words);
- \f$\infty\f$ to prevent a line break at all costs;
- \f$-\infty\f$ to force a line
- a positive, but finite, value for hyphenation points.
So we need the following values:
- \f$w_i\f$ (the width of the word \f$i\f$ itself);
- \f$s_i\f$ (the width of the space following the word \f$i\f$);
- the stretchability \f$y_i\f$, a value denoting how much the space
after word\f$i\f$ can be stretched (typically \f${1\over 2} s_i\f$
for justified text; otherwise 0, since the spaces are not
stretched);
- the shrinkability \f$y_i\f$, a value denoting how much the space
after word\f$i\f$ can be shrunken (typically \f${1\over 3} s_i\f$
for justified text; otherwise 0, since the spaces are not shrunk);
- the penalty \f$p_i\f$, if the line is broken after word \f$i\f$;
- a width \f$h_i\f$, which is added, when the line is broken after
word \f$i\f$.
\f$h_i\f$ is the width of the hyphen, if the word \f$i\f$ is a part of
the hyphenated word (except the last part); otherwise 0.
Let \f$l\f$ be the (ideal) width (length) of the line, which is
e.&nbsp;at the top given by the browser window width. Furthermore, all words
from \f$a\f$ to \f$b\f$ are added to the line. \f$a\f$ is fixed: we do
not modify the previous lines anymore; but our task is to find a
suitable \f$b\f$.
We define:
\f[W_a^b = \sum_{i=a}^{b} w_i + \sum_{i=a}^{b-1} s_i + h_b\f]
\f[Y_a^b = {Y_0}_a^b + \sum_{i=a}^{b-1} y_i\f]
\f[Z_a^b = {Z_0}_a^b + \sum_{i=a}^{b-1} z_i\f]
\f$W_a^b\f$ is the total width, \f$Y_a^b\f$ the total stretchability,
and \f$Z_a^b\f$ the total shrinkability. \f${Y_0}_a^b\f$ and
\f${Z_0}_a^b\f$ are the stretchability and shrinkability defined per
line, and applied at the borders; they are 0 for justified text, but
\f${Y_0}_a^b\f$ has a positive value otherwise, see below for details.
Furthermore the *adjustment ratio* \f$r_a^b\f$:
- in the ideal case that \f$W_a^b = l\f$: \f$r_a^b = 0\f$;
- if \f$W_a^b < l\f$: \f$r_a^b = (l - W_a^b) / Y_a^b\f$
(\f$r_a^b < 0\f$ in this case);
- if \f$W_a^b > l\f$: \f$r_a^b = (l - W_a^b) / Z_a^b\f$
(\f$r_a^b < 0\f$ in this case).
The badness \f$\beta_a^b\f$ is defined as follows:
- if \f$r_a^b\f$ is undefined or \f$r_a^b < -1\f$: \f$\beta_a^b = \infty\f$;
- otherwise: \f$\beta_a^b = |r_a^b|^3\f$
The goal is to find the value of \f$b\f$ where \f$\beta_a^b + p_b\f$
is minimal. (\f$a\f$ is given, since we do not modify the previous
lines.)
After a couple of words, it is not predictable whether this minimum
has already been reached. There are two cases where this is possible
for a given \f$b'\f$:
- \f$\beta_{b'}^a = \infty\f$ (line gets too tight):
\f$a \le b < b'\f$, the minimum has to be searched between these two
values;
- \f$p_{b'} = -\infty\f$ (forced line break):
\f$a \le b \le b'\f$ (there may be another minimum of
\f$\beta_a^b\f$ before; note the \f$\le\f$ instead of \f$<\f$).
This leads to a problem that the last words of a text block are not
displayed this way, since they do not fulfill these rules for being
added to a line. For this reason, there are "temporary" lines already
described above.
(Note that the actual calculation differs from this description, since
integer arithmetic is used for performance, which make the actual
code more complicated. See dw::Textblock::BadnessAndPenalty for
details.)
Ragged Borders
--------------
For other than justified text (left-, right-aligned and centered), the
spaces between the words are not shrunk or stretched (so \f$y_i\f$
and \f$z_i\f$ are 0), but additional space is added to the left or
right border or to both. For this reason, an additional stretchability
\f${Y_0}_a^b\f$ is added (see definition above). Since this space at
the border is 0 in an ideal case (\f$W_a^b = l\f$), it cannot be
shrunken, so \f${Z_0}_a^b\f$ is 0.
This is not equivalent to the calculation of the total stretchability
as done for justified text, since in this case, the stretchability
depends on the number of words: consider the typical case that all
spaces and stretchabilities are equal (\f$y_a = y_{a + 1} = \ldots =
y_b\f$). With \f$n\f$ words, the total strechability would be \f$n
\cdot y_a\f$, so increase with an increasing number of words
(\f$y_a\f$ is constant). This is correct for justified text, but for
other alignments, where only one space (or two, for centered text) is
changed, this would mean that a line with many narrow words is more
stretchable than a line with few wide words.
It is obvious that left-aligned text can be handled in the same way as
right-aligned text. [... Centered text? ...]
The default value for the stretchability is the line height without
the space between the lines (more precisely: the maximum of all word
heights). The exact value not so important when comparing different
possible values for the badness \f$\beta_a^b\f$, when \f${Y_0}_a^b\f$
is nearly constant for different \f$b\f$ (which is the case for the
actual value), but it is important for the comparison with penalties,
which are constant. To be considered is also that for non-justified
text, hyphenation is differently (less) desirable; this effect can be
achieved by enlarging the stretchability, which will lead to a smaller
badness, and so make hyphenation less likely. The user can configure
the stretchability by changing the preference value
*stretchability_factor* (default: 1.0).
(Comparison to T<sub>E</sub>X: Knuth and Plass describe a method for
ragged borders, which is effectively the same as described here (Knuth
1999, pp.&nbsp;93--94). The value for the stretchability of the line
is slightly less, 1&nbsp;em (ibid., see also p.&nbsp;72 for the
definition of the units). However, this article suggests a value for
the hyphenation penalty, which is ten times larger than the value for
justified text; this would suggest a larger value for
*stretchability_factor*.)
Hyphens
=======
Words (instances of dw::Textblock::Word), which are actually part of a
hyphenated word, are always drawn as a whole, not separately. This
way, the underlying platform is able to apply kerning, ligatures, etc.
Calculating the width of such words causes some problems, since it is
not required that the width of text "AB" is identical to the width of
"A" plus the width of "B", just for the reasons mentioned above. It
gets even a bit more complicated, since it is required that a word
part (instance of dw::Textblock::Word) has always the same length,
independent of whether hyphenation is applied or not. Furthermore, the
hyphen length is fixed for a word; for practical reasons, it is always
the width of a hyphen, in the given font.
For calculating the widths, consider a word of four syllables:
A-B-C-D. There are 3 hyphenation points, and so 2<sup>3</sup> = 8
possible ways of hyphenation: ABCD, ABC-D, AB-CD, AB-C-D, A-BCD,
A-BC-D, A-B-CD, A-B-C-D. (Some of them, like the last one, are only
probable for very narrow lines.)
Let w(A), w(B), w(C), w(D) be the word widths (part of
dw::Textblock::Word::size), which have to be calculated, and l be a
shorthand for dw::core::Platform::textWidth. Without considering
this problem, the calculation would be simple: w(A) = l(A)
etc. However, it gets a bit more complicated. Since all
non-hyphenations are drawn as a whole, the following conditions can be
concluded:
- from drawing "ABCD" (not hyphenated at all): w(A) + w(B) + w(C) +
w(D) = l(ABCD);
- from drawing "BCD", when hyphenated as "A-BCD" ("A-" is not
considered here): w(B) + w(C) + w(D) = l(BCD);
- likewise, from drawing "CD" (cases "AB-CD" and "A-B-CD"): w(C) +
w(D) = l(CD);
- finally, for the cases "ABC-D", "AB-C-D", "A-BC-D", and "A-B-C-D":
w(D) = l(D).
So, the calculation is simple:
- w(D) = l(D)
- w(C) = l(CD) - w(D)
- w(B) = l(BCD) - (w(C) + w(D))
- w(A) = l(ABCD) - (w(B) + w(C) + w(D))
For calculation the hyphen widths, the exact conditions would be
over-determined, even when the possibility for individual hyphen
widths (instead of simply the text width of a hyphen character) would
be used. However, a simple approach of fixed hyphen widths will have
near-perfect results, so this is kept simple.
Automatic Hyphenation
=====================
When soft hyphens are used, words are immediately divided into
different parts, and so different instances of
dw::Textblock::Word. Automatic hyphenation (using Liang's algorithm)
is, however, not applied always, but only when possibly needed, after
calculating a line without hyphenation:
- When the line is tight, the last word of the line is hyphenated;
possibly this will result in a line with less parts of this word,
and so a less tight line.
- When the line is loose, and there is another word (for the next
line) available, this word is hyphenated; possibly, some parts of
this word are taken into this line, making it less loose.
After this, the line is re-calculated.
A problem arises when the textblock is rewrapped, e.&nbsp;g. when the
user changes the window width. In this case, some new instances of
dw::Textblock::Word must be inserted into the word list,
dw::Textblock::words. This word list is implemented as an array, which
is dynamically increased; a simple approach would involve moving all
of the <i>n</i> elements after position <i>i</i>, so
<i>n</i>&nbsp;-&nbsp;<i>i</i> steps are necessary. This would not be a
problem, since O(n) steps are necessary; however, this will be
necessary again for the next hyphenated word (at the end of a
following line), and so on, so that
(<i>n</i>&nbsp;-&nbsp;<i>i</i><sub>1</sub>) +
(<i>n</i>&nbsp;-&nbsp;<i>i</i><sub>2</sub>) + ..., with
<i>i</i><sub>1</sub>&nbsp;&lt;&nbsp;<i>i</i><sub>2</sub>&nbsp;&lt;&nbsp;...,
which results in O(n<sup>2</sup>) steps. For this reason, the word
list is managed by the class lout::misc::NotSoSimpleVector, which uses
a trick (a second array) to deal with exactly this problem. See there
for more details.
Tests
=====
There are test HTML files in the <i>test</i> directory. Also, there is
a program testing automatic hyphenation, <i>test/liang</i>, which can
be easily extended.
Bugs and Things Needing Improvement
===================================
High Priority
-------------
None.
Medium Priority
---------------
None.
Low Priority
------------
**Mark the end of a paragraph:** Should dw::core::Content::BREAK still
be used? Currently, this is redundant to
dw::Textblock::BadnessAndPenalty.
Solved (Must Be Documented)
---------------------------
These have been solved recently and should be documented above.
*Bugs in hyphenation:* There seem to be problems when breaking words
containing hyphens already. Example: "Abtei-Stadt", which is divided
into "Abtei-" and "Stadt", resulting possibly in
&quot;Abtei-<span></span>-[new line]Stadt&quot;. See also below under
"Medium Priority", on how to deal with hyphens and dashes.
**Solution:** See next.
*Break hyphens and dashes:* The following rules seem to be relevant:
- In English, an em-dash is used with no spaces around. Breaking
before and after the dash should be possible, perhaps with a
penalty > 0. (In German, an en-dash (Halbgeviert) with spaces around
is used instead.)
- After a hyphen, which is part of a compound word, a break should be
possible. As described above ("Abtei-Stadt"), this collides with
hyphenation.
Where to implement? In the same dynamic, lazy way like hyphenation? As
part of hyphenation?
Notice that Liang's algorithm may behave different regarding hyphens:
"Abtei-Stadt" is (using the patterns from CTAN) divided into "Abtei-"
and "Stadt", but "Nordrhein-Westfalen" is divided into "Nord",
"rhein-West", "fa", "len": the part containing the hyphen
("rhein-West") is untouched. (Sorry for the German words; if you have
got English examples, send them me.)
**Solution for both:** This has been implemented in
dw::Textblock::addText, in a similar way to soft hyphens. Liang's
algorithm now only operates on the parts: "Abtei" and "Stadt";
"Nordrhein" and "Westfalen".
*Hyphens in adjacent lines:* It should be simple to assign a larger
penalty for hyphens, when the line before is already hyphenated. This
way, hyphens in adjacent lines are penalized further.
**Solved:** There are always two penalties. Must be documented in
detail.
*Incorrect calculation of extremes:* The minimal width of a text block
(as part of the width extremes, which are mainly used for tables) is
defined by everything between two possible breaks. A possible break
may also be a hyphenation point; however, hyphenation points are
calculated in a lazy way, when the lines are broken, and not when
extremes are calculated. So, it is a matter of chance whether the
calculation of the minimal width will take the two parts "dil-" and
"lo" into account (when "dillo" has already been hyphenated), or only
one part, "dillo" (when "dillo" has not yet been hyphenated),
resulting possibly in a different value for the minimal width.
Possible strategies to deal with this problem:
- Ignore. The implications should be minimal.
- Any solution will make it necessary to hyphenate at least some
words when calculating extremes. Since the minimal widths of all
words are used to calculate the minimal width of the text block, the
simplest approach will hyphenate all words. This would, of course,
eliminate the performance gains of the current lazy approach.
- The latter approach could be optimized in some ways. Examples: (i)
If a word is already narrower than the current accumulated value for
the minimal width, it makes no sense to hyphenate it. (ii) In other
cases, heuristics may be used to estimate the number of syllables,
the width of the widest of them etc.
**Solved:** Hyphenated parts of a word are not considered anymore for
width extremes, but only whole words. This is also one reason for the
introduction of the paragraphs list.
**Also:**
- Configuration of penalties.
*/

59
devdoc/dw-map.doc Normal file
View File

@ -0,0 +1,59 @@
/** \page dw-map Dillo Widget Documentation Map
This maps includes special documentations as well as longer comments
in the sources. Arrows denote references between the documents.
\dot
digraph G {
rankdir=LR;
node [shape=record, fontname=Helvetica, fontsize=8];
fontname=Helvetica; fontsize=8;
dw_overview [label="Dillo Widget Overview", URL="\ref dw-overview"];
dw_usage [label="Dillo Widget Usage", URL="\ref dw-usage"];
dw_layout_views [label="Layout and Views", URL="\ref dw-layout-views"];
dw_layout_widgets [label="Layout and Widgets",
URL="\ref dw-layout-widgets"];
dw_widget_sizes [label="Sizes of Dillo Widgets",
URL="\ref dw-widget-sizes"];
dw_changes [label="Changes to the GTK+-based Release Version",
URL="\ref dw-changes"];
dw_images_and_backgrounds [label="Images and Backgrounds in Dw",
URL="\ref dw-images-and-backgrounds"];
dw_Image [label="dw::Image", URL="\ref dw::Image"];
dw_core_Imgbuf [label="dw::core::Imgbuf", URL="\ref dw::core::Imgbuf"];
dw_core_SelectionState [label="dw::core::SelectionState",
URL="\ref dw::core::SelectionState"];
dw_core_style [label="dw::core::style", URL="\ref dw::core::style"];
dw_Table [label="dw::Table", URL="\ref dw::Table"];
dw_Textblock [label="dw::Textblock", URL="\ref dw::Textblock"];
dw_core_ui [label="dw::core::ui", URL="\ref dw::core::ui"];
dw_overview -> dw_changes;
dw_overview -> dw_usage;
dw_overview -> dw_core_style;
dw_overview -> dw_core_ui;
dw_overview -> dw_images_and_backgrounds;
dw_overview -> dw_layout_widgets;
dw_overview -> dw_widget_sizes;
dw_overview -> dw_layout_views;
dw_usage -> dw_Table;
dw_usage -> dw_Textblock;
dw_usage -> dw_core_style;
dw_usage -> dw_core_ui;
dw_usage -> dw_images_and_backgrounds;
dw_layout_widgets -> dw_widget_sizes;
dw_layout_widgets -> dw_core_SelectionState;
dw_widget_sizes -> dw_Table;
dw_widget_sizes -> dw_Textblock;
dw_images_and_backgrounds -> dw_core_Imgbuf;
dw_images_and_backgrounds -> dw_Image;
dw_core_style -> dw_Textblock;
}
\enddot
*/

View File

@ -0,0 +1,97 @@
/** \page dw-miscellaneous Miscellaneous Notes on Dw
This is a barely sorted list of issues which I consider noteworthy,
but have yet to be moved to other parts of the documentation (which is
partly to be created).
General
=======
Widget allocation outside of parent allocation
----------------------------------------------
A widget allocation outside of the allocation of the parent is
allowed, but the part outside is not visible.
Which widgets may be drawn?
---------------------------
All drawing starts with the toplevel widget
(cf. dw::core::Widget::queueDrawArea, dw::core::Layout::queueDraw, and
dw::core::Layout::expose), and a widget has to draw its children, in a
way consistent with their stacking order.
There are two exceptions:
1. Direct descendants, which are not children, may be drawn, if the
parent can distinguish them and so omit drawing them a second
time. See dw::core::StackingContextMgr and \ref dw-stacking-context.
Parents should not draw children in flow for which
dw::core::StackingContextMgr::handledByStackingContextMgr returns
true.
2. Interrupted drawing: via dw::core::Widget::drawInterruption; see
\ref dw-interrupted-drawing.
Similar rules apply to handling mouse events
(dw::core::Widget::getWidgetAtPoint).
Interrupted drawing
-------------------
→ \ref dw-interrupted-drawing.
Similar rules apply to handling mouse events
(dw::core::Widget::getWidgetAtPoint).
Extra space
-----------
Should dw::core::Widget::calcExtraSpace be called from
dw::core::Widget::getExtremes?
Widgets out of flow
===================
dw::Textblock::getGeneratorWidth
--------------------------------
Re-evaluate dw::Textblock::getGeneratorWidth (especially the limitation on
instances of dw::Textblock) for positioned elements. Is this method really only
called for floats?
Widget sizes
============
Relation between dw::core::Widget::markSizeChange and dw::core::Widget::queueResize
------------------------------------------------------------------------------------
The following comment should be re-evaluated. Implementing incremental resizing
for dw::oof::OOFFloatsMgr seems to fix the performance problems, but this should
be examined further.
<div style="text-decoration: line-through; color: #606060">
dw::oof::OOFFloatsMgr::markSizeChange (called from
dw::Textblock::markSizeChange) calls dw::oof::OOFAwareWidget::updateReference,
whose implementation dw::Textblock::updateReference calls
dw::core::Widget::queueResize. This may result in a recursion,
- for which it is not clear whether it ends in all cases (although endless cases
are not known yet), and
- which nevertheless may take much time in cases where the number of calls
increases exponentially with the depth of the widget tree.
The recent change in dw::Textblock::updateReference (`if (lines->size () > 0)`)
seems to fix the performance problem, but the issue should be examined further,
albeit with lower priority. Especially, it has to be determined, under which
conditions it is allowed to (directly or indirectly) call
dw::core::Widget::queueResize within an implementation of
dw::core::Widget::markSizeChange.
Here is the original test case (slow, when `if (lines->size () > 0)` is removed
again):
(for i in $(seq 1 20); do echo '<div style="float:left"><div></div>'; done) > tmp.html; src/dillo tmp.html
You may change the number (20), or examine smaller cases with
<a href="http://home.gna.org/rtfl/">RTFL</a>:
(for i in $(seq 1 3); do echo '<div style="float:left"><div></div>'; done) > tmp.html; src/dillo tmp.html | rtfl-objview -OM -A "*" -a resize -a resize.oofm
</div>
*/

View File

@ -0,0 +1,151 @@
/** \page dw-out-of-flow-floats Handling Elements Out Of Flow: Floats
TODO: Much missing.
(Historical) Note: Floats make use of \ref dw-size-request-pos, which
reduces the complexity of a previous design.
Sorting floats
==============
Floats are sorted, to make binary search possible, in these lists:
- for each generator: dw::OutOfFlowMgr::TBInfo::leftFloatsGB and
dw::OutOfFlowMgr::TBInfo::rightFloatsGB;
- for the container: dw::OutOfFlowMgr::leftFloatsCB and
dw::OutOfFlowMgr::rightFloatsCB.
The other two lists, dw::OutOfFlowMgr::leftFloatsAll and
dw::OutOfFlowMgr::rightFloatsAll are not sorted at all.
New floats are always added to the end of either list; this order is
called *generation order*. See also above: *GB lists and CB lists*.
On the other hand, there are different sorting criteria, implemented
by different comparators, so that different kinds of keys may be used
for searching. These sorting criteria are equivalent to the generation
order.
dw::OutOfFlowMgr::Float::CompareSideSpanningIndex compares
*sideSpanningIndex* (used to compare floats to those on the respective
other side); if you look at the definition
(dw::OutOfFlowMgr::addWidgetOOF) it becomes clear that this order is
equivalent to the generation order.
dw::OutOfFlowMgr::Float::CompareGBAndExtIndex compares *externalIndex*
for floats with same generators, otherwise: (i) if one generator (T1)
is a direct ancestor of the other generator (T2), the child of T1,
which is an ancestor of, or identical to, T2 is compared to the float
generated by T1, using *externalIndex*, as in this example:
T1 -+-> child --> ... -> T2 -> Float
`-> Float
Otherwise, the two blocks are compared, according to their position in
dw::OutOfFlowMgr::tbInfos:
common ancestor -+-> ... --> T1 -> Float
`-> ... --> T2 -> Float
This is equivalent to the generation order, as long it is ensured that
*externalIndex* reflects the generation order within a generating
block, for both floats and child blocks.
dw::OutOfFlowMgr::Float::ComparePosition ...
Miscellaneous notes
===================
Handling collisions
-------------------
The CSS specification allows two strategies to deal with colliding
floats: placing the second float beside or below the first one. Many
other browsers implement the first approach, while dillo implements
the second one, which may cause problems when the author assumes the
first. Example: the "tabs" at the top of every page at Wikipedia
("Article", "Talk", ...).
Float containers in flow
------------------------
Consider the following HTML snippet:
<body>
<img src="....jpg" style="float:right">
<p style="overflow:hidden">Text</p>
</body>
Interestingly, dillo shows "Text" always *below* the image, even if
there is enough space left of it. An investigation shows that the
paragraph (&lt;p&gt;) is regarded as own floats container (because of
*overflow:hidden*), so the floats container above (&lt;body&gt;)
regards this block as widget which must be fit between the floats
(dw::Textblock::mustBorderBeRegarded &gt;
dw::Textblock::getWidgetRegardingBorderForLine). However, since a
textblock in flow always covers (at least) the whole available width,
which is defined *without* considering floats, the space left of the
float will always be to narrow, so that the paragraph is moved below
the float, by inserting an empty line before.
When searching for a solution, several difficulties show up:
1. The available width, which is used for the width of the textblock,
is defined independent of floats. Aside from problems when changing
this definition, a dependence on floats would be difficult to
implement, since *sizeRequest* is independent of a position. (See
also \ref dw-out-of-flow.)
2. I must admit that I do not remember the exact rationale and the
test case behind adding the exception in
dw::Textblock::getWidgetRegardingBorderForLine (see above), but
simply removing this exception will result in a possible
overlapping of floats from both containers, since no collisions are
tested for.
3. On the other hand, mixing the float containers (interaction of two
or more instances of dw::oof::OOFFloatsMgr), e.&nbsp;g. for
collision tests, would become too complex and possibly result in
performance problems.
Instead, this approach is focussed:
- Goal: the paragraph is narrowed so it fits, *as a whole*, between
the floats.
- The approach is to remove the exception in
dw::Textblock::getWidgetRegardingBorderForLine. A textblock, which
is a float container in flow (as this paragraph), is returned by
this method and so dw::Textblock::mustBorderBeRegarded returns
*true*. This will put this paragraph again at the correct position.
- To avoid overlappings, the linebreaking width of this paragraph
(which is also used for positioning of floats) is the available
width, minus the maximal float width on either side. (This is an
approach similar to the one dw::Ruler will use soon). Most likely,
some new methods will have to be added to calculate this.
- For paragraphs like this, dw::Textblock::borderChanged must rewrap
all lines; *y* is irrelevant in this case.
- Since the textblock will tend to become taller when getting
narrower, and so possibly cover more (wider) floats, and so become
narrower again etc., there may be multiple solutions for calculating
the size. Generally, a smaller height (and so larger width) is
preferred.
- There remains a problem: what if a word is too large? Should a
textblock of this kind then reard the floats in detail, to insert
empty lines when needed?
<b>Real-world cases:</b> *Overflow:hidden* is set for headings in
Wikipedia, and so this case occurs when there is a float (thumb image)
before a heading. See e.&nbsp;g.
<a href="http://de.wikipedia.org/wiki/Emmerich_am_Rhein#Ans.C3.A4ssige_Unternehmen">this page</a>
and scroll a bit up; the company logos should be right of this section.
<b>Priority:</b> Since this is not a regression, compared to not
supporting floats at all, a fix is not urgent for a new release.
Resizing
--------
Has the case that a float changes its position to be regarded? Probably yes, but
cases where no other mechanisms come into play are rather unlikely.
<b>Priority:</b> If this plays a role, this means a regression compared to not
supporting floats at all.
*/

View File

@ -0,0 +1,66 @@
/** \page dw-out-of-flow-positioned Handling Elements Out Of Flow: Positioned Elements
<div style="border: 2px solid #ffff00; margin: 1em 0; padding: 0.5em
1em; background-color: #ffffe0">Positioned elements have been
deactivated, during the development of \ref dw-size-request-pos, to
focus on floats. See dw::core::IMPL_POS in file dw/core.hh.</div>
Miscellaneous notes
===================
General
-------
(See also *relative positions* below.)
What about negative positions?
dw::oof::OutOfFlowMgr::tellPosition1 and
dw::oof::OutOfFlowMgr::tellPosition2 could be combined again, and
called in the respective circumstance, depending on
dw::oof::OutOfFlowMgr::mayAffectBordersAtAll.
Relative positions
------------------
**General Overview:** At the original position, a space as large as
the positioned element is left. This is implemented by assigning a
size to the widget *reference*. For this there are two new methods:
dw::oof::OutOfFlowMgr::calcWidgetRefSize and
dw::oof::OOFAwareWidget::widgetRefSizeChanged.
**Bug:** Since the size of a relatively positioned element should be
calculated as if it was in flow, the available width should be
delegated to the *generator*; however, since
dw::oof::OOFPosRelMgr::dealingWithSizeOfChild returns *false* in all
cases, it is delegated to the *container*. **Idea for fix:**
dw::oof::OOFPosRelMgr::dealingWithSizeOfChild should return *false* if
and only if the generator of the child is the container (to prevent an
endless recursion). In other cases,
dw::oof::OOFPosRelMgr::getAvailWidthOfChild and
dw::oof::OOFPosRelMgr::getAvailHeightOfChild should directly call the
respective methods of the *generator*, which must be made public then.
**Performance:** In many cases, the first call of
dw::oof::OOFPosRelMgr::sizeAllocateEnd will queue again the resize
idle, since some elements are not considered in
dw::oof::OOFPosRelMgr::getSize. One case could be removed: if a
positioned element has *left* = *top* = 0, and its total size (the
requisition) is equal to the space left at the original position, the
size of the widget *reference*
(dw::oof::OOFAwareWidget::getRequisitionWithoutOOF, see
dw::oof::OOFPosRelMgr::calcWidgetRefSize).
**Documentation:** Describe why the latter is not covered by
dw::oof::OOFPositionedMgr::doChildrenExceedContainer. (Is this really
the case?)
**Open:** Stacking order? Furthermore: a relatively positioned element
does not always constitute a containing block (see CSS specification).
Fixed positions
---------------
Currently, fixedly positioned elements are positioned relative to the
canvas, not to the viewport. For a complete implementation, see \ref
dw-fixed-positions.
*/

102
devdoc/dw-out-of-flow.doc Normal file
View File

@ -0,0 +1,102 @@
/** \page dw-out-of-flow Handling Elements Out Of Flow
Introduction
============
This texts deals with both floats and positioned elements, which have
in common that there is a distinction between generating block and
containing block (we are here using the same notation as in the
CSS&nbsp;2 specification). Consider this snippet (regarding floats):
<ul>
<li>Some text.</li>
<li>
<div style="float:right; width: 50%">Some longer text, so
that the effect described in this passage can be
demonstrated.
</div>
Some more and longer text.</li>
<li>Final text. Plus some more to demonstrate how text flows
around the float on the right side.</li>
</ul>
which may be rendered like this
\image html dw-floats-01.png
The float (the DIV section, yellow in the image) is defined
("generated") within the list item (green), so, in CSS 2 terms, the
list item is the generating block of the float. However, as the image
shows, the float is not contained by the list item, but another block,
several levels above (not shown here). In terms of ::dw, this means
that the dw::Textblock representing the float cannot be a child of the
dw::Textblock representing the generating block, the list item, since
the allocation of a child widget must be within the allocation of the
parent widget. Instead, to each dw::Textblock, another dw::Textblock
is assigned as the containing box.
(Notice also that other text blocks must regard floats to calculate
their borders, and so their size. In this example, the following list
item (gray) must consider the position of the float. This is discussed
in detail in the following section, _Implementation overview_.)
Implementation overview
=======================
- dw::oof::OOFAwareWidget is the base for both generators and containers.
dw::Textblock and dw::Table are based on this, but dw::Table is only relevant
for positioned elements, so much simpler than dw::Textblock.
- For a given textblock (or, generally, generator), the float container is not
necessary the same container as for positioned elements. For this reason,
dw::oof::OOFAwareWidget::oofContainer is an array.
- The containers are set in dw::oof::OOFAwareWidget::notifySetAsTopLevel and
dw::oof::OOFAwareWidget::notifySetParent.
- If a widget is out of flow, the generating widget keeps a reference with the
type dw::core::Content::WIDGET_OOF_REF, while the containing block refers to
it as dw::core::Content::WIDGET_OOF_CONT. For widgets within flow,
dw::core::Content::WIDGET_IN_FLOW is used. Notice that in the first case,
there are two pieces of content referring to the same widget. An application
of this distinction is iterators. (For selection and searching, the generating
hierarchy is used, which is different from the widget hierarchy.)
Handling widgets out of flow is partly the task of class implementing
dw::oof::OutOfFlowMgr, which is stored by dw::oof::OOFAwareWidget::outOfFlowMgr,
but only for containing blocks. Generating blocks should refer
to *container->outOfFlowMgr[...]*.
Overview of the dw::oof::OutOfFlowMgr hierarchy;
\dot
digraph G {
node [shape=record, fontname=Helvetica, fontsize=10];
edge [arrowhead="none", arrowtail="empty", dir="both"];
fontname=Helvetica; fontsize=8;
OutOfFlowMgr [URL="\ref dw::oof::OutOfFlowMgr"; color="#a0a0a0"];
OOFFloatsMgr [URL="\ref dw::oof::OOFFloatsMgr"];
OOFPositionedMgr [URL="\ref dw::oof::OOFPositionedMgr"; color="#a0a0a0"];
OOFPosAbsLikeMgr [URL="\ref dw::oof::OOFPosAbsLikeMgr"; color="#a0a0a0"];
OOFPosAbsMgr [URL="\ref dw::oof::OOFPosAbsMgr"];
OOFPosFixedMgr [URL="\ref dw::oof::OOFPosFixedMgr"];
OOFPosRelMgr [URL="\ref dw::oof::OOFPosRelMgr"];
OutOfFlowMgr -> OOFFloatsMgr;
OutOfFlowMgr -> OOFPositionedMgr;
OOFPositionedMgr -> OOFPosAbsLikeMgr;
OOFPosAbsLikeMgr -> OOFPosAbsMgr;
OOFPosAbsLikeMgr -> OOFPosFixedMgr;
OOFPositionedMgr -> OOFPosRelMgr;
}
\enddot
</div>
Further details
===============
- \ref dw-out-of-flow-floats
- \ref dw-out-of-flow-positioned
*/

158
devdoc/dw-overview.doc Normal file
View File

@ -0,0 +1,158 @@
/** \page dw-overview Dillo Widget Overview
Note: If you are already familiar with the Gtk+-based version of Dw,
read \ref dw-changes.
The module Dw (Dillo Widget) is responsible for the low-level rendering of
all resources, e.g. images, plain text, and HTML pages (this is the
most complex type). Dw is \em not responsible for parsing HTML, or
decoding image data. Furthermore, the document tree, which is planned
for CSS, is neither a part of Dw, instead, it is a new module on top
of Dw.
The rendering, as done by Dw, is split into two phases:
<ul>
<li> the \em layouting, this means calculating the exact positions of
words, lines, etc. (in pixel position), and
<li> the \em drawing, i.e. making the result of the layouting visible
on the screen.
</ul>
The result of the layouting allocates an area, which is called
\em canvas.
<h2>Structure</h2>
The whole Dw module can be split into the following parts:
\dot
digraph G {
node [shape=record, fontname=Helvetica, fontsize=10];
edge [arrowhead="open", fontname=Helvetica, fontsize=10,
labelfontname=Helvetica, labelfontsize=10,
color="#404040", labelfontcolor="#000080"];
subgraph cluster_core {
style="dashed"; color="#000080"; fontname=Helvetica; fontsize=10;
label="Platform independent core";
Layout [URL="\ref dw::core::Layout"];
Platform [URL="\ref dw::core::Platform", color="#ff8080"];
View [URL="\ref dw::core::View", color="#ff8080"];
Widget [URL="\ref dw::core::Widget", color="#a0a0a0"];
}
subgraph cluster_fltk {
style="dashed"; color="#000080"; fontname=Helvetica; fontsize=10;
label="FLTK specific part (as an\nexample for the platform specific\n\
implementations)";
subgraph cluster_fltkcore {
style="dashed"; color="#000080"; fontname=Helvetica; fontsize=10;
label="FLTK core";
FltkPlatform [URL="\ref dw::fltk::FltkPlatform"];
FltkView [URL="\ref dw::fltk::FltkView", color="#ff8080"];
}
FltkViewport [URL="\ref dw::fltk::FltkViewport"];
FltkPreview [URL="\ref dw::fltk::FltkPreview"];
}
subgraph cluster_widgets {
style="dashed"; color="#000080"; fontname=Helvetica; fontsize=10;
label="Platform independent widgets";
Textblock [URL="\ref dw::Textblock"];
AlignedTextblock [URL="\ref dw::AlignedTextblock", color="#a0a0a0"];
Table [URL="\ref dw::Table"];
Image [URL="\ref dw::Image"];
etc1 [label="..."];
etc2 [label="..."];
}
Layout -> Platform [headlabel="1", taillabel="1"];
Layout -> View [headlabel="*", taillabel="1"];
Layout -> Widget [headlabel="1", taillabel="1", label="topLevel"];
Widget -> Widget [headlabel="*", taillabel="1", label="children"];
Widget -> Textblock [arrowhead="none", arrowtail="empty", dir="both"];
Widget -> Table [arrowhead="none", arrowtail="empty", dir="both"];
Widget -> Image [arrowhead="none", arrowtail="empty", dir="both"];
Widget -> etc1 [arrowhead="none", arrowtail="empty", dir="both"];
Textblock -> AlignedTextblock [arrowhead="none", arrowtail="empty",
dir="both"];
AlignedTextblock -> etc2 [arrowhead="none", arrowtail="empty", dir="both"];
Platform -> FltkPlatform [arrowhead="none", arrowtail="empty", dir="both",
style="dashed"];
FltkPlatform -> FltkView [headlabel="*", taillabel="1"];
View -> FltkView [arrowhead="none", arrowtail="empty", dir="both"];
FltkView -> FltkViewport [arrowhead="none", arrowtail="empty", dir="both",
style="dashed"];
FltkView -> FltkPreview [arrowhead="none", arrowtail="empty", dir="both",
style="dashed"];
}
\enddot
<center>[\ref uml-legend "legend"]</center>
\em Platform means in most cases the underlying UI toolkit
(e.g. FLTK). A layout is bound to a specific platform, but multiple
platforms may be handled in one program.
A short overview:
<ul>
<li> dw::core::Layout is the central class, it manages the widgets and the
view, and provides delegation methods for the platform.
<li> The layouting is done by a tree of widgets (details are described in
\ref dw-layout-widgets), also the drawing, which is finally delegated
to the view.
<li> The view (implementation of dw::core::View) provides primitive methods
for drawing, but also have an influence on
the canvas size (via size hints). See \ref dw-layout-views for details.
<li> Some platform dependencies are handled by implementations
of dw::core::Platform.
</ul>
<h3>Header Files</h3>
The structures mentioned above can be found in the following header
files:
<ul>
<li> Anything from the Dw core in core.hh. Do not include the single files.
<li> The single widgets can be found in the respective header files, e.g.
image.hh for dw::Image.
<li> The core of the FLTK implementation is defined in fltkcore.hh. This
includes dw::fltk::FltkPlatform, dw::fltk::FltkView, but not the concrete
view implementations.
<li> The views can be found in single header files, e.g fltkviewport.hh for
dw::fltk::FltkViewport.
</ul>
<h2>Further Documentations</h2>
A complete map can be found at \ref dw-map.
<ul>
<li> For learning, how to use Dw, read \ref dw-usage and related documents,
dw::core::style, dw::core::ui and \ref dw-images-and-backgrounds.
<li> Advanced topics are described in \ref dw-layout-widgets,
\ref dw-widget-sizes and \ref dw-layout-views.
</ul>
*/

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

View File

@ -0,0 +1,35 @@
<p style="margin-bottom: 5em"><i>This is the source of the image
<a href="dw-size-request-pos-01.png">dw-size-request-pos-01.png</a>.</i></p>
<table lang="en">
<td style="width: 220px">
<p>
<div style="float:right">
<img src="xhttps://www.gnu.org/graphics/heckert_gnu.small.png">
<p style="text-align: center"><i>A GNU</i></p>
</div>
Short.
</p>
<p>A longer paragraph, into which a float extends, which has been
defined in the previous paragraph. </p>
</td>
<td style="vertical-align: middle">
<br/>
<br/>
<br/>
<br/>
<br/>
<br/>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<img src="http://upload.wikimedia.org/wikipedia/commons/8/80/CorrespMtl5.png" style="width: 80px">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</td>
<td style="width: 220px">
<p>
<div style="float:right">
<img src="https://www.gnu.org/graphics/heckert_gnu.small.png">
<p style="text-align: center"><i>A GNU</i></p>
</div>
Short.
</p>
<p>A longer paragraph, into which a float extends, which has been
defined in the previous paragraph. </p>
</td>
</table>

Binary file not shown.

After

Width:  |  Height:  |  Size: 31 KiB

View File

@ -0,0 +1,172 @@
/** \page dw-size-request-pos Size requisitions depending on positions
<div style="border: 2px solid #ffff00; margin: 1em 0;
padding: 0.5em 1em; background-color: #ffffe0">The complex "widget
sizes" is currently divided into three documents: \ref
dw-widget-sizes, \ref dw-grows, and **Size requisitions depending on
positions** (this document). Furthermore, there are some notes in
\ref dw-miscellaneous.</div>
Motivation
==========
As described in \ref dw-out-of-flow (*The sizeRequest/sizeAllocate
problem*), the principle that the size of a widget depends only on the
sizes of its children causes some problems with floats; the current
solution is a frequent correction by calling
dw::core::Widget::queueResize. In this document, an alternative
approach is presented.
<div style="border: 2px solid #ffff00; margin: 1em 0; padding: 0.5em 1em;
background-color: #ffffe0">This approach works very well for floats, but not
for positioned elements, which means that calling
dw::core::Widget::queueResize is still needed for the latter. On the other
hand, dw::oof::OOFFloatsMgr (which is much more complex than
dw::oof::OOFPositionedMgr) can be simplified quite much.</div>
General Idea
============
A widget size may depend on the position relative to an ancestor
widget. If a widget wants to get the size of a child widget, it should:
1. call the new methods dw::core::Widget::numSizeRequestReferences and
dw::core::Widget::sizeRequestReference, which return all widgets
relative to which the child's position must be calculated;
2. call dw::core::Widget::sizeRequest with the positions relative to
these widgets.
<div style="border: 2px solid #ffff00; margin: 1em 0;
padding: 0.5em 1em; background-color: #ffffe0">It is not sufficient
to work with *absolute* positions, since there may be an
interruption passing the positions so that absolute positions are
often not known.</div>
All positions passed to dw::core::Widget::sizeRequest must constitute
the position at which this child will be allocated.
There are situations where the parent widget is unable to determine
these positions before the size is known. An example: a textblock
widget cannot determine the positions of an inline widget (like an
image, or an inline block) before the line is finished; on the other
hand, finishing the line depends on knowing the sizes of the inline
widgets.
This may result in a conflict, when the size of an inline widget
depends on positions. Generally, the only widget whose size depends on
positions is dw::Textblock (the size will depend on the positions
within its oof container, see \ref dw-out-of-flow), so this conflict
occurs with inline blocks.
This conflict is handled in different ways:
1. Fortunately, this case is irrelevat for floats: an inline block
constitutes its own floats container, so that there is no dependence
on a position within another widget.
2. For positioned elements, this case is relevant, since an inline
block is in most cases not a container for positioned elements. In
this case, a generator will call the methods
dw::oof::OutOfFlowMgr::tellIncompletePosition1 and
dw::oof::OutOfFlowMgr::tellIncompletePosition2, instead of
dw::oof::OutOfFlowMgr::tellPosition and
dw::oof::OutOfFlowMgr::tellPosition2, respectively. (Since this
case is irrelevant for floats,
dw::oof::OOFFloatsMgr::tellIncompletePosition1 and
dw::oof::OOFFloatsMgr::tellIncompletePosition2 are not implemented but
simply abort.)
(This is not (yet) considered for borders: borders are only relevant
for floats, but conflicts do not occur for floats.)
Extremes
--------
Extremes may depend on the position in an analogue way, see:
- dw::core::Widget::numGetExtremesReferences,
- dw::core::Widget::getExtremesReference, and
- dw::core::Widget::getExtremes.
Resizing
--------
Currently, the size of a widget has to be recalculated when:
1. it has called dw::core::Widget::queueResize, or
2. the size of a child widget has to be recalculated.
Since for this new approach, the size does not only depend on the size of the
children, the second condition must be modified. See beginning of
dw::core::Widget::sizeRequest.
An implementation may have to consider, this too, especially when implementing
incremental resizing (see \ref dw-widget-sizes); see
dw::Textblock::sizeRequestImpl as an example.
Regarding changes of the position is not sufficient. Consider this example,
where a float size changes as soon as the image is loaded:
\image html dw-size-request-pos-01.png
The second paragraph ("A longer paragraph" ...) stays at the same position, both
absolute and relative to the float container, but has to be rewrapped because of
the float. Instead, this is handled by dw::oof::OutOfFlowMgr::markSizeChange
(and likewise dw::oof::OutOfFlowMgr::markExtremesChange), which is called by the
implementation of `markSizeChange` (or `markExtremesChange`, respectively) of
the OOF container. (See also the end of the comment of dw::oof::OOFAwareWidget.)
Plan
====
1. General design (dw::core::Widget::sizeRequestReference, changes to
dw::core::Widget::sizeRequest). Completed.
2. Implementation for dw::Textblock. Completed.
3. Change interface of dw::oof::OutOfFlowMgr (this affects mostly only
comments). Completed.
Affects methods dw::oof::OutOfFlowMgr::tellPosition1,
dw::oof::OutOfFlowMgr::tellPosition2,
dw::oof::OutOfFlowMgr::getLeftBorder,
dw::oof::OutOfFlowMgr::getRightBorder,
dw::oof::OutOfFlowMgr::hasFloatLeft,
dw::oof::OutOfFlowMgr::hasFloatRight,
dw::oof::OutOfFlowMgr::getLeftFloatHeight, and
dw::oof::OutOfFlowMgr::getRightFloatHeight.
4. Apply step 3 to calls within dw::Textblock. Completed.
<b>Attention:</b> After this step, and before completing the next steps, the
code is inconsistent and so incorrect.
5. Implement step 3 for floats (affects dw::oof::OOFFloatsMgr). **MOSTLY
COMPLETED.**
6. Implement step 3 for positioned elements (affects only
dw::oof::OOFPositionedMgr). **INCOMPLETE.** (But positioned elements are
currently deactivated.)
Issues
======
- Since the signature of dw::core::Widget::sizeRequestImpl changes quite often
during the development of *size requisitions depending on positions*, a
simpler option dw::core::Widget::sizeRequestSimpl has been added. May be
removed again, after the design is stable.
- As an alternative, passing the references may be done in a new method, which
is called *before* dw::core::Widget::sizeRequestImpl. This makes even more
sense, after dw::core::Widget::calcExtraSpace and
dw::core::Widget::calcExtraSpaceImpl have been extended by references.
- There may be inconsistencies for widget styles; see
[revision f797436687fe](http://flpsed.org/hgweb/dillo_grows/rev/f797436687fe)
as an example for a fix. Perhaps a different approach, where breaks are added,
_if `display` has the value `block`_ (or analogue), will work better.
*/

View File

@ -0,0 +1,114 @@
/** \page dw-stacking-context Handling stacking contexts
Stacking Context and dw::core::StackingContextMgr
=================================================
For the definition of stacking contexts, see CSS 2.1 specification,
- <a href="http://www.w3.org/TR/CSS2/visuren.html#z-index">section
9.9.1: Specifying the stack level: the 'z-index' property</a> and
- <a href="http://www.w3.org/TR/CSS2/zindex.html">appendix E</a>.
A widget establishes a stacking context when it is positioned and its
style value of *z-index* is different from *auto* (see
dw::core::StackingContextMgr::isEstablishingStackingContext). In this
case, it is assigned an instance of dw::core::StackingContextMgr,
which has also access to the widgets establishing the child contexts.
Stacking Order
==============
The stacking order influences
1. the order in which child widgets are drawn (dw::core::Widget::draw),
and
2. the order in which mouse events are dispatched to child widgets
(dw::core::Widget::getWidgetAtPoint).
The first is done from bottom to top, the latter from top to bottom.
I'm here referring to the simplified description in
<a href="http://www.w3.org/TR/CSS2/visuren.html#z-index">section
9.9.1</a>. The table shows a recommended order for the implementations
of dw::core::Widget::draw and dw::core::Widget::getWidgetAtPoint
(for the latter, read from bottom to top):
<table>
<tr>
<th> CSS specification <th> Drawing <th> Mouse events
<tr>
<td> *1. the background and borders of the element forming the
stacking context.*
<td> dw::core::Widget::drawBox
<td> Nothing necessary.
<tr>
<td> *2. the child stacking contexts with negative stack levels (most
negative first).*
<td> dw::core::StackingContextMgr::drawBottom (when defined)
<td> dw::core::StackingContextMgr::getBottomWidgetAtPoint (when defined)
<tr>
<td> *3. the in-flow, non-inline-level, non-positioned descendants.*
<td rowspan="4"> When (i) widget specific content is drawn, then (ii)
dw::oof::OOFAwareWidget::drawOOF is called, this will
have this effect:
1. all in-flow elements are drawn,
2. floats are drawn and
3. positioned elements with *z-index: auto* are drawn
(latter two done by
dw::oof::OOFAwareWidget::drawOOF, in this order).
This order differs from the specified order, but
since floats and in-flow elements do not overlap,
this difference has no effect.
Drawing in-line elements, floats and positioned
elements with *z-index: auto* and should avoid
duplicate calls: Widgets drawn by
dw::core::StackingContextMgr::drawBottom and by
dw::core::StackingContextMgr::drawTop should be
excluded here. This can be tested with
dw::core::StackingContextMgr::handledByStackingContextMgr.
<td rowspan="4"> Likewise, the implementation should (i) test
dw::oof::OOFAwareWidget::getWidgetOOFAtPoint, and
(ii) search through the children. Also, duplicate
calls should be avoided using
dw::core::StackingContextMgr::handledByStackingContextMgr.
There are already the implementations
dw::core::Widget::getWidgetAtPoint (ignoring
dw::oof::OutOfFlowMgr) and
dw::oof::OOFAwareWidget::getWidgetAtPoint (including
dw::oof::OutOfFlowMgr).
<tr>
<td> *4. the non-positioned floats.*
<tr>
<td> *5. the in-flow, inline-level, non-positioned descendants,
including inline tables and inline blocks.*
<tr>
<td> (What about positioned elements with *z-index: auto*? Seems to be
missing in
<a href="http://www.w3.org/TR/CSS2/visuren.html#z-index">section
9.9.1</a>, but mentioned in
<a href="http://www.w3.org/TR/CSS2/zindex.html">appendix E</a>,
item 8.
<tr>
<td> *6. the child stacking contexts with stack level 0 and the
positioned descendants with stack level 0.*
<td rowspan="2"> dw::core::StackingContextMgr::drawTop (when defined)
<td rowspan="2"> dw::core::StackingContextMgr::getTopWidgetAtPoint
(when defined)
<tr>
<td> *7. the child stacking contexts with positive stack levels (least
positive first).*
</table>
Note: This is not quite in conformance with the specification: this
description refers to any widget, not only widgets establishing a
stacking context. Does this make a difference?
*/

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 575 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 890 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 868 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 641 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 521 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 802 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 586 B

375
devdoc/dw-usage.doc Normal file
View File

@ -0,0 +1,375 @@
/** \page dw-usage Dillo Widget Usage
This document describes the usage of Dw, without going too much into
detail.
<h2>Getting Started</h2>
In this section, a small runnable example is described, based on the
FLTK implementation.
As described in \ref dw-overview, the following objects are needed:
<ul>
<li> dw::core::Layout,
<li> an implementation of dw::core::Platform (we will use
dw::fltk::FltkPlatform),
<li> at least one implementation of dw::core::View (dw::fltk::FltkViewport),
and
<li> some widgets (for this example, only a simple dw::Textblock).
</ul>
First of all, the necessary \#include's:
\code
#include <FL/Fl_Window.H>
#include <FL/Fl.H>
#include "dw/core.hh"
#include "dw/fltkcore.hh"
#include "dw/fltkviewport.hh"
#include "dw/textblock.hh"
\endcode
Everything is put into one function:
\code
int main(int argc, char **argv)
{
\endcode
As the first object, the platform is instantiated:
\code
dw::fltk::FltkPlatform *platform = new dw::fltk::FltkPlatform ();
\endcode
Then, the layout is created, with the platform attached:
\code
dw::core::Layout *layout = new dw::core::Layout (platform);
\endcode
For the view, we first need a FLTK window:
\code
Fl_Window *window = new Fl_Window(200, 300, "Dw Example");
window->begin();
\endcode
After this, we can create a viewport, and attach it to the layout:
\code
dw::fltk::FltkViewport *viewport =
new dw::fltk::FltkViewport (0, 0, 200, 300);
layout->attachView (viewport);
\endcode
Each widget needs a style (dw::core::style::Style, see dw::core::style),
so we construct it here. For this, we need to fill a
dw::core::style::StyleAttrs structure with values, and call
dw::core::style::Style::create (latter is done further below):
\code
dw::core::style::StyleAttrs styleAttrs;
styleAttrs.initValues ();
styleAttrs.margin.setVal (5);
\endcode
dw::core::style::StyleAttrs::initValues sets several default
values. The last line sets a margin of 5 pixels. Next, we need a
font. Fonts are created in a similar way, first, the attributes are
defined:
\code
dw::core::style::FontAttrs fontAttrs;
fontAttrs.name = "Bitstream Charter";
fontAttrs.size = 14;
fontAttrs.weight = 400;
fontAttrs.style = dw::core::style::FONT_STYLE_NORMAL;
fontAttrs.letterSpacing = 0;
fontAttrs.fontVariant = dw::core::style::FONT_VARIANT_NORMAL;
\endcode
Now, the font can be created:
\code
styleAttrs.font = dw::core::style::Font::create (layout, &fontAttrs);
\endcode
As the last attributes, the background and foreground colors are
defined, here dw::core::style::Color::createSimple must be called:
\code
styleAttrs.color =
dw::core::style::Color::create (layout, 0x000000);
styleAttrs.backgroundColor =
dw::core::style::Color::create (layout, 0xffffff);
\endcode
Finally, the style for the widget is created:
\code
dw::core::style::Style *widgetStyle =
dw::core::style::Style::create (layout, &styleAttrs);
\endcode
Now, we create a widget, assign a style to it, and set it as the
toplevel widget of the layout:
\code
dw::Textblock *textblock = new dw::Textblock (false);
textblock->setStyle (widgetStyle);
layout->setWidget (textblock);
\endcode
The style is not needed anymore (a reference is added in
dw::core::Widget::setStyle), so it should be unreferred:
\code
widgetStyle->unref();
\endcode
Now, some text should be added to the textblock. For this, we first
need another style. \em styleAttrs can still be used for this. We set
the margin to 0, and the background color to "transparent":
\code
styleAttrs.margin.setVal (0);
styleAttrs.backgroundColor = NULL;
dw::core::style::Style *wordStyle =
dw::core::style::Style::create (layout, &styleAttrs);
\endcode
This loop adds some paragraphs:
\code
for(int i = 1; i <= 10; i++) {
char buf[4];
sprintf(buf, "%d.", i);
char *words[] = { "This", "is", "the", buf, "paragraph.",
"Here", "comes", "some", "more", "text",
"to", "demonstrate", "word", "wrapping.",
NULL };
for(int j = 0; words[j]; j++) {
textblock->addText(strdup(words[j]), wordStyle);
\endcode
Notice the \em strdup, dw::Textblock::addText will feel responsible
for the string, and free the text at the end. (This has been done to
avoid some overhead in the HTML parser.)
The rest is simple, it also includes spaces (which also have styles):
\code
textblock->addSpace(wordStyle);
}
\endcode
Finally, a paragraph break is added, which is 10 pixels high:
\code
textblock->addParbreak(10, wordStyle);
}
\endcode
Again, this style should be unreferred:
\code
wordStyle->unref();
\endcode
After adding text, this method should always be called (for faster
adding large text blocks):
\code
textblock->flush ();
\endcode
Some FLTK stuff to finally show the window:
\code
window->resizable(viewport);
window->show();
int errorCode = Fl::run();
\endcode
For cleaning up, it is sufficient to destroy the layout:
\code
delete layout;
\endcode
And the rest
\code
return errorCode;
}
\endcode
If you compile and start the program, you should see the following:
\image html dw-example-screenshot.png
Try to scroll, or to resize the window, you will see, that everything
is done automatically.
Of course, creating new widgets, adding text to widgets etc. can also
be done while the program is running, i.e. after fltk::run has been
called, within timeouts, idles, I/O functions etc. Notice that Dw is
not thread safe, so that everything should be done within one thread.
With the exception, that you have to call dw::Textblock::flush,
everything gets immediately visible, within reasonable times; Dw has
been optimized for frequent updates.
<h2>List of all Widgets</h2>
These widgets are used within dillo:
<ul>
<li>dw::core::ui::Embed
<li>dw::AlignedTextblock
<li>dw::Bullet
<li>dw::Ruler
<li>dw::Image
<li>dw::ListItem
<li>dw::Table
<li>dw::TableCell
<li>dw::Textblock
</ul>
If you want to create a new widget, refer to \ref dw-layout-widgets.
<h2>List of Views</h2>
There are three dw::core::View implementations for FLTK:
<ul>
<li> dw::fltk::FltkViewport implements a viewport, which is used in the
example above.
<li> dw::fltk::FltkPreview implements a preview window, together with
dw::fltk::FltkPreviewButton, it is possible to have a scaled down
overview of the whole canvas.
<li> dw::fltk::FltkFlatView is a "flat" view, i.e. it does not support
scrolling. It is used for HTML buttons, see
dw::fltk::ui::FltkComplexButtonResource and especially
dw::fltk::ui::FltkComplexButtonResource::createNewWidget for details.
</ul>
More information about views in general can be found in \ref
dw-layout-views.
<h2>Iterators</h2>
For examining generally the contents of widgets, there are iterators
(dw::core::Iterator), created by the method
dw::core::Widget::iterator (see there for more details).
These simple iterators only iterate through one widget, and return
child widgets as dw::core::Content::WIDGET. The next call of
dw::core::Iterator::next will return the piece of contents \em after
(not within) this child widget.
If you want to iterate through the whole widget trees, there are two
possibilities:
<ol>
<li> Use a recursive function. Of course, with this approach, you are
limited by the program flow.
<li> Maintain a stack of iterators, so you can freely pass this stack
around. This is already implemented, as dw::core::DeepIterator.
</ol>
As an example, dw::core::SelectionState represents the selected region
as two instances of dw::core::DeepIterator.
<h2>Finding Text</h2>
See dw::core::Layout::findtextState and dw::core::FindtextState
(details in the latter). There are delegation methods:
<ul>
<li> dw::core::Layout::search and
<li> dw::core::Layout::resetSearch.
</ul>
<h2>Anchors and Scrolling</h2>
In some cases, it is necessary to scroll to a given position, or to
an anchor, programmatically.
<h3>Anchors</h3>
Anchors are defined by widgets, e.g. dw::Textblock defines them, when
dw::Textblock::addAnchor is called. To jump to a specific anchor
within the current widget tree, use dw::core::Layout::setAnchor.
This can be done immediately after assignig a toplevel widget, even
when the anchor has not yet been defined. The layout will remember the
anchor, and jump to the respective position, as soon as possible. Even
if the anchor position changes (e.g., when an anchor is moved
downwards, since some space is needed for an image in the text above),
the position is corrected.
As soon as the user scrolls the viewport, this correction is not done
anymore. If in dillo, the user request a page with an anchor, which is
quite at the bottom of the page, he may be get interested in the text
at the beginning of the page, and so scrolling down. If then, after
the anchor has been read and added to the dw::Textblock, this anchor
would be jumped at, the user would become confused.
The anchor is dismissed, too, when the toplevel widget is removed
again.
\todo Currently, anchors only define vertical positions.
<h3>Scrolling</h3>
To scroll to a given position, use the method
dw::core::Layout::scrollTo. It expects several parameters:
<ul>
<li>a horizontal adjustment parameter, defined by dw::core::HPosition,
<li>a vertical adjustment parameter, defined by dw::core::VPosition, and
<li>a rectangle (\em x, \em y, \em width and \em height) of the region
to be adjusted.
</ul>
If you just want to move the canvas coordinate (\em x, \em y) into the
upper left corner of the viewport, you can call:
\code
dw::core::Layout *layout;
// ...
layout->scrollTo(dw::core::HPOS_LEFT, dw::core::VPOS_TOP, 0, 0, 0, 0);
\endcode
By using dw::core::HPOS_NO_CHANGE or dw::core::VPOS_NO_CHANGE, you can
change only one dimension. dw::core::HPOS_INTO_VIEW and
dw::core::VPOS_INTO_VIEW will cause the viewport to move as much as
necessary, that the region is visible in the viewport (this is
e.g. used for finding text).
<h2>Further Documentations</h2>
<ul>
<li> dw::core::style
<li> dw::core::ui
<li> \ref dw-images-and-backgrounds
</ul>
*/

Binary file not shown.

After

Width:  |  Height:  |  Size: 755 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 542 B

278
devdoc/dw-widget-sizes.doc Normal file
View File

@ -0,0 +1,278 @@
/** \page dw-widget-sizes Sizes of Dillo Widgets
<div style="border: 2px solid #ffff00; margin: 1em 0;
padding: 0.5em 1em; background-color: #ffffe0">The complex "widget
sizes" is currently divided into three documents: **Sizes of Dillo
Widgets** (this document), \ref dw-grows, and \ref
dw-size-request-pos. Furthermore, there are some notes in
\ref dw-miscellaneous.</div>
<div style="border: 2px solid #ff4040; margin: 1em 0;
padding: 0.5em 1em; background-color: #fff0f0"><b>Info:</b>
Not up to date, see other documents.</div>
Allocation
==========
Each widget has an \em allocation at a given time, this includes
- the position (\em x, \em y) relative to the upper left corner of the
canvas, and
- the size (\em width, \em ascent, \em descent).
The \em canvas is the whole area available for the widgets, in most
cases, only a part is seen in a viewport. The allocation of the
toplevel widget is exactly the allocation of the canvas, i.e.
- the position of the toplevel widget is always (0, 0), and
- the canvas size is defined by the size of the toplevel widget.
The size of a widget is not simply defined by the width and the
height, instead, widgets may have a base line, and so are vertically
divided into an ascender (which height is called \em ascent), and a
descender (which height is called \em descent). The total height is so
the sum of \em ascent and \em descent.
Sizes of zero are allowed. The upper limit for the size of a widget is
defined by the limits of the C++ type \em int.
\image html dw-size-of-widget.png Allocation of a Widget
In the example in the image, the widget has the following allocation:
- \em x = 50
- \em y = 50
- \em width = 150
- \em ascent = 150
- \em descent = 100
The current allocation of a widget is hold in
dw::core::Widget::allocation. It can be set from outside by
calling dw::core::Widget::sizeAllocate. This is a concrete method,
which will call dw::core::Widget::sizeAllocateImpl (see code of
dw::core::Widget::sizeAllocate for details).
For trivial widgets (like dw::Bullet),
dw::core::Widget::sizeAllocateImpl does not need to be
implemented. For more complex widgets, the implementation should call
dw::core::Widget::sizeAllocate (not
dw::core::Widget::sizeAllocateImpl) on all child widgets, with
appropriate child allocations. dw::core::Widget::allocation should not
be changed here, this is already done in
dw::core::Widget::sizeAllocate.
Requisitions
============
A widget may prefer a given size for the allocation. This size, the
\em requisition, should be returned by the method
dw::core::Widget::sizeRequestImpl. In the simplest case, this is
independent of the context, e.g. for an
image. dw::Image::sizeRequestImpl returns the following size:
- If no buffer has yet been assigned (see dw::Image for more details),
the size necessary for the alternative text is returned. If no
alternative text has been set, zero is returned.
- If a buffer has been assigned (by dw::Image::setBuffer), the root
size is returned (i.e. the original size of the image to display).
This is a bit simplified, dw::Image::sizeRequestImpl should also deal
with margins, borders and paddings, see dw::core::style.
From the outside, dw::Image::sizeRequest should be called, which does
a bit of optimization. Notice that in dw::Image::sizeRequestImpl, no
optimization like lazy evaluation is necessary, this is already done
in dw::Image::sizeRequest.
A widget, which has children, will likely call dw::Image::sizeRequest
on its children, to calculate the total requisition.
The caller (this is either the dw::core::Layout, or the parent
widget), may, but also may not consider the requisition. Instead, a
widget must deal with any allocation. (For example, dw::Image scales
the image buffer when allocated at another size.)
Size Hints
==========
<div style="border: 2px solid #ff4040; margin-bottom: 0.5em;
padding: 0.5em 1em; background-color: #fff0f0"><b>Info:</b>
Size hints have been removed, see \ref dw-grows.</div>
Width Extremes
==============
dw::Table uses width extremes for fast calculation of column
widths. The structure dw::core::Extremes represents the minimal and
maximal width of a widget, as defined by:
- the minimal width is the smallest width, at which a widget can still
display contents, and
- the maximal width is the largest width, above which increasing the
width- does not make any sense.
Especially the latter is vaguely defined, here are some examples:
- For those widgets, which do not depend on size hints, the minimal
and the maximal width is the inherent width (the one returned by
dw::core::Widget::sizeRequest).
- For a textblock, the minimal width is the width of the widest
(unbreakable) word, the maximal width is the width of the total
paragraph (stretching a paragraph further would only waste space).
Actually, the implementation of dw::Textblock::getExtremesImpl is a
bit more complex.
- dw::Table is an example, where the width extremes are calculated
from the width extremes of the children.
Handling width extremes is similar to handling requisitions, a widget
must implement dw::core::Widget::getExtremesImpl, but a caller will
use dw::core::Widget::getExtremes.
Resizing
========
When the widget changes its size (requisition), it should call
dw::core::Widget::queueResize. The next call of
dw::core::Widget::sizeRequestImpl should then return the new
size. See dw::Image::setBuffer as an example.
Interna are described in the code of dw::core::Widget::queueResize.
<h3>Incremental Resizing</h3>
A widget may calculate its size based on size calculations already
done before. In this case, a widget must exactly know the reasons, why
a call of dw::core::Widget::sizeRequestImpl is necessary. To make use
of this, a widget must implement the following:
1. There is a member dw::core::Widget::parentRef, which is totally
under control of the parent widget (and so sometimes not used at
all). It is necessary to define how parentRef is used by a specific
parent widget, and it has to be set to the correct value whenever
necessary.
2. The widget must implement dw::core::Widget::markSizeChange and
dw::core::Widget::markExtremesChange, these methods are called in
two cases:
1. directly after dw::core::Widget::queueResize, with the
argument ref was passed to dw::core::Widget::queueResize,
and
2. if a child widget has called dw::core::Widget::queueResize,
with the value of the parent_ref member of this child.
This way, a widget can exactly keep track on size changes, and so
implement resizing in a faster way. A good example on how to use this
is dw::Textblock.
Rules for Methods Related to Resizing
=====================================
Which method can be called, when the call of another method is not
finished? These rules are important in two circumstances:
1. To know which method can be called, and, especially, which methods
*must not* be called, within the implementation of
*sizeRequestImpl* (called by *sizeRequest*), *markSizeChange*, and
*markExtremesChange* (the latter two are called by *queueResize*).
2. On the other hand, to make sure that the calls, which are allowed,
are handled correctly, especially in implementations of
*sizeRequestImpl*, *markSizeChange*, *markExtremesChange*
Generally, the rules defined below are, in case of doubt, rather
strict; when changing the rules, loosening is simpler than to tighten
them, since this will make it necessary to review old code for calls
previously allowed but now forbidden.
Short recap:
- *QueueResize* directly calls *markSizeChange* and
*markExtremesChanges*, and queues an idle function for the actual
resizing (dw::core::Layout::resizeIdle). (The idle function is
called some time after *queueResize* is finished.)
- The resize idle function first calls *sizeRequest*, then
*sizeAllocate*, for the toplevel widget.
In the following table, the rules are defined in detail. "Within call
of ..." includes all methods called from the original method: the
first row (*queueResize*) defines also the rules for
*markExtremesChanges* and *markExtremesChanges*, and in the second row
(*sizeAllocate*), even *sizeRequest* has to be considered.
<div style="border: 2px solid #ff4040; margin-bottom: 0.5em;
padding: 0.5em 1em; background-color: #fff0f0"><b>Info:</b>
Not up to date: *queueResize* can now be called recursively (so to
speak). See code there.</div>
<table>
<tr>
<th>Within call of ... ↓
<th>... is call allowed of ... ? →
<th>queueResize
<th>sizeAllocate
<th>sizeRequest
<th>getExtremes
<tr>
<th colspan=2>queueResize
<td>No
<td>No<sup>1</sup>
<td>No<sup>1</sup>
<td>No<sup>1</sup>
<tr>
<th colspan=2>sizeAllocate
<td>Yes
<td>Only for children<sup>2</sup>
<td>Yes(?)
<td>Yes(?)
<tr>
<th colspan=2>sizeRequest
<td>Yes<sup>3</sup>
<td>No
<td>Limited<sup>4</sup>
<td>Limited<sup>4</sup>
<tr>
<th colspan=2>getExtremes
<td>Yes<sup>3</sup>
<td>No
<td>Limited<sup>4</sup>
<td>Limited<sup>4</sup>
<tr>
<td colspan=6><sup>1</sup>) Otherwise, since these other methods
may be call *queueResize*, the limitation that *queueResize* must not
call *queueResize* can be violated.
<sup>2</sup>) Could perhaps be loosened as for *sizeRequest* and
*getExtremes*, but there is probably no need.
<sup>3</sup>) Therefore the distinction between *RESIZE_QUEUED* and
*NEEDS_RESIZE*, and *EXTREMES_QUEUED* and *EXTREMES_CHANGED*,
respectively.
<sup>4</sup>) Calls only for children are safe. In other cases, you
take a large responsibility to prevent endless recursions by
(typically indirectly) calling *sizeRequest* / *getExtremes* for
direct ancestors.
</table>
Furthermore, *sizeAllocate* can only be called within a call of
dw::core::Layout::resizeIdleId, so (if you do not touch dw::core) do
not call it outside of *sizeAllocateImpl*. The other methods can be
called outsize; e.&nbsp;g. *sizeRequest* is called in
dw::Textblock::addWidget.
To avoid painful debugging, there are some tests for the cases that
one method call is strictly forbidden while another method is called.
This could be done furthermore:
- The tests could be refined.
- Is it possible to define exacter rules, along with a proof that no
problems (like endless recursion) can occur?
*/

180
devdoc/fltk-problems.doc Normal file
View File

@ -0,0 +1,180 @@
/** \page fltk-problems Problems with FLTK
<h2>dw::fltk::FltkViewport</h2>
Current problems:
<ul>
<li> How should dw::fltk::FltkViewport::cancelQueueDraw be implemented?
<li> If the value of a scrollbar is changed by the program, not the user,
the callback seems not to be called. Can this be assured?
<li> The same for dw::fltk::FltkViewport::layout?
<li> Also, the problems with the widgets seems to work. Also sure?
<li> When drawing, clipping of 32 bit values is not working properly.
<li> The item group within a selection widget (menu) should not be selectable.
</ul>
<h2>dw::fltk::FltkPlatform</h2>
<ul>
<li> There is the problem, that fltk::font always returns a font, the
required one, or a replacements. The latter is not wanted in all
cases, e.g. when several fonts are tested. Perhaps, this could be
solved by searching in the font list. <i>[This was true of fltk2.
What is the state of font handling now with fltk-1.3?]</i>
<li> Distinction between italics and oblique would be nice
(dw::fltk::FltkFont::FltkFont).
</ul>
<h2>dw::fltk::ui::FltkCheckButtonResource</h2>
Groups of Fl_Radio_Button must be added to one Fl_Group, which is
not possible in this context. There are two alternatives:
<ol>
<li>there is a more flexible way to group radio buttons, or
<li>radio buttons are not grouped, instead, grouping (especially
unchecking other buttons) is done by the application.
</ol>
(This is mostly solved.)
<h2>dw::fltk::FltkImgbuf</h2>
Alpha transparency should be best abstracted by FLTK itself. If not,
perhaps different implementations for different window systems could
be used. Then, it is for X necessary to use GCs with clipping masks.
<h2>dw::fltk::ui::ComplexButton</h2>
Unfortunately, FLTK does not provide a button with Fl_Group as parent, so
that children may be added to the button. dw::fltk::ui::ComplexButton does
exactly this, and is, in an ugly way, a modified copy of the FLTK
button.
It would be nice, if this is merged with the standard FLTK
button. Furthermore, setting the type is strange.
If the files do not compile, it may be useful to create a new one from
the FLTK source:
<ol>
<li> Copy Fl_Button.H from FLTK to dw/fltkcomplexbutton.hh and
src/Button.cxx to dw/fltkcomplexbutton.cc.
<li> In both files, rename "Button" to "ComplexButton". Automatic replacing
should work.
<li> Apply the changes below.
</ol>
The following changes should be applied manually.
<h3>Changes in fltkcomplexbutton.hh</h3>
First of all, the \#define's for avoiding multiple includes:
\code
-#ifndef fltk_ComplexButton_h // fltk_Button_h formerly
-#define fltk_ComplexButton_h
+#ifndef __FLTK_COMPLEX_BUTTON_HH__
+#define __FLTK_COMPLEX_BUTTON_HH__
\endcode
at the beginning and
\code
-#endif
+#endif // __FLTK_COMPLEX_BUTTON_HH__
\endcode
at the end. Then, the namespace is changed:
\code
-namespace fltk {
+namespace dw {
+namespace fltk {
+namespace ui {
\endcode
at the beginning and
\code
-}
+} // namespace ui
+} // namespace fltk
+} // namespace dw
\endcode
at the end. Most important, the base class is changed:
\code
-#include "FL/Fl_Widget.H"
+#include <FL/Fl_Group.H>
\endcode
and
\code
-class FL_API ComplexButton : public Fl_Widget {
+class ComplexButton: public Fl_Group
+{
\endcode
Finally, for dw::fltk::ui::ComplexButton::default_style, there is a
namespace conflict:
\code
- static NamedStyle* default_style;
+ static ::fltk::NamedStyle* default_style;
\endcode
<h3>Changes in fltkcomplexbutton.cc</h3>
First, \#include's:
\code
#include <FL/Fl.H>
-#include <FL/ComplexButton.h> // <FL/Fl_Button.H> formerly
#include <FL/Fl_Group.H>
#include <FL/Fl_Window.H>
+
+#include "fltkcomplexbutton.hh"
\endcode
Second, namespaces:
\code
+using namespace dw::fltk::ui;
\endcode
Since the base class is now Fl_Group, the constructor must be changed:
\code
-ComplexButton::ComplexButton(int x,int y,int w,int h, const char *l) : Fl_Widget(x,y,w,h,l) {
+ComplexButton::ComplexButton(int x,int y,int w,int h, const char *l) :
+ Fl_Group(x,y,w,h,l)
+{
\endcode
Finally, the button must draw its children (end of
dw::fltk::ui::ComplexButton::draw()):
\code
+
+ for (int i = children () - 1; i >= 0; i--)
+ draw_child (*child (i));
}
\endcode
*/

48
devdoc/index.doc Normal file
View File

@ -0,0 +1,48 @@
/** \mainpage
<h2>Overview</h2>
This is a list of documents to start with:
<ul>
<li> \ref lout
<li> \ref dw-overview (map at \ref dw-map)
</ul>
Currently, a document \ref fltk-problems is maintained, ideally, it
will be removed soon.
<h2>Historical</h2>
<h3>Replacements for GTK+ and GLib</h3>
There are several classes etc., which are used for tasks formerly (in the GTK+
version of dillo) achieved by GtkObject (in 1.2.x, this is part of Gtk+) and
GLib. For an overview on all this, take a look at \ref lout.
GtkObject is replaced by the following:
<ul>
<li> lout::object::Object is a common base class for many classes used
dillo. In the namespace lout::object, there are also some more common
classes and interfaces.
<li> A sub class of lout::object::Object is
lout::identity::IdentifiableObject, which allows to determine the
class at run-time (equivalent to GTK_CHECK_CAST in GtkObject).
<li> For signals, there is the namespace lout::signal.
</ul>
Hash tables, linked lists etc. can be found in the lout::container namespace,
several useful macros from GLib have been implemented as inline functions
in the lout::misc namespace.
As an alternative to the macros defined in list.h, there is also a template
class, lout::misc::SimpleVector, which does the same.
<h3>Changes in Dw</h3>
If you have been familiar with Dw before, take a look at \ref dw-changes.
*/

95
devdoc/lout.doc Normal file
View File

@ -0,0 +1,95 @@
/** \page lout Lots of Useful Tools
In the "lout" directory, there are some common base functionality for
C++. Most is described as doxygen comments, this text gives an
overview.
<h2>Common Base Class</h2>
Many classes are derived from lout::object::Object, which defines some
general methods. See there for more information.
For the case, that you need primitive C++ types, there are some
wrappers:
<table>
<tr><th>C++ Type <th>Wrapper Class
<tr><td>void* <td>lout::object::Pointer
<tr><td>specific pointer <td>lout::object::TypedPointer (template class)
<tr><td>int <td>lout::object::Integer
<tr><td>const char* <td>lout::object::ConstString
<tr><td>char* <td>lout::object::String
</table>
<h2>Containers</h2>
In the namespace lout::container, several container classes are defined,
which all deal with instances of lout::object::Object.
<h3>Untyped Containers</h3>
In lout::container::untyped, there are the following containers:
<ul>
<li>lout::container::untyped::Vector, a dynamically increases array,
<li>lout::container::untyped::List, a linked list,
<li>lout::container::untyped::HashTable, a hash table, and
<li>lout::container::untyped::Stack, a stack.
</ul>
All provide specific methods, but since they have a common base class,
lout::container::untyped::Collection, they all provide iterators, by the
method lout::container::untyped::Collection::iterator.
<h3>Typed Containers</h3>
lout::container::typed provides wrappers for the container classes defined
in lout::container::untyped, which are more type safe, by using C++
templates.
<h2>Signals</h2>
For how to connect objects at run-time (to reduce dependencies), take a
look at the lout::signal namespace.
There is also a base class lout::signal::ObservedObject, which implements
signals for deletion.
<h2>Debugging</h2>
In debug.hh, there are some some useful macros for debugging messages,
see the file for mor information.
<h2>Identifying Classes at Runtime</h2>
If the class of an object must be identified at runtime,
lout::identity::IdentifiableObject should be used as the base class,
see there for more details.
<h2>Miscellaneous</h2>
The lout::misc namespace provides several miscellaneous stuff:
<ul>
<li> In some contexts, it is necessary to compare objects
(less/greater), for this, also lout::misc::Comparable must be
implemented. For example., lout::container::untyped::Vector::sort and
lout::container::typed::Vector::sort cast the elements to
lout::misc::Comparable. This can be mixed with lout::object::Object.
<li> lout::misc::SimpleVector, a simple, template based vector class
(not depending on lout::object::Object) (a variant for handling a
special case in an efficient way is lout::misc::NotSoSimpleVector),
<li> lout::misc::StringBuffer, class for fast concatenation of a large number
of strings,
<li> lout::misc::BitSet implements a bitset.
<li> useful (template) functions (lout::misc::min, lout::misc::max), and
<li> some functions useful for runtime checks (lout::misc::assert,
lout::misc::assertNotReached).
</ul>
*/

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

View File

@ -0,0 +1,785 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="210mm"
height="297mm"
id="svg2"
version="1.1"
inkscape:version="0.48.3.1 r9886"
sodipodi:docname="not-so-simple-container.svg"
inkscape:export-filename="/home/sg/dev/dillo/dillo/doc/not-so-simple-container.png"
inkscape:export-xdpi="69"
inkscape:export-ydpi="69">
<defs
id="defs4">
<marker
inkscape:stockid="Arrow1Mend"
orient="auto"
refY="0.0"
refX="0.0"
id="Arrow1Mend"
style="overflow:visible;">
<path
id="path3998"
d="M 0.0,0.0 L 5.0,-5.0 L -12.5,0.0 L 5.0,5.0 L 0.0,0.0 z "
style="fill-rule:evenodd;stroke:#000000;stroke-width:1.0pt;"
transform="scale(0.4) rotate(180) translate(10,0)" />
</marker>
<marker
inkscape:stockid="Arrow1Send"
orient="auto"
refY="0.0"
refX="0.0"
id="Arrow1Send"
style="overflow:visible;">
<path
id="path4004"
d="M 0.0,0.0 L 5.0,-5.0 L -12.5,0.0 L 5.0,5.0 L 0.0,0.0 z "
style="fill-rule:evenodd;stroke:#000000;stroke-width:1.0pt;"
transform="scale(0.2) rotate(180) translate(6,0)" />
</marker>
<marker
inkscape:stockid="Arrow1Lend"
orient="auto"
refY="0.0"
refX="0.0"
id="Arrow1Lend"
style="overflow:visible;">
<path
id="path3992"
d="M 0.0,0.0 L 5.0,-5.0 L -12.5,0.0 L 5.0,5.0 L 0.0,0.0 z "
style="fill-rule:evenodd;stroke:#000000;stroke-width:1.0pt;"
transform="scale(0.8) rotate(180) translate(12.5,0)" />
</marker>
<marker
inkscape:stockid="Arrow1Mend"
orient="auto"
refY="0"
refX="0"
id="Arrow1Mend-5"
style="overflow:visible">
<path
inkscape:connector-curvature="0"
id="path3998-5"
d="M 0,0 5,-5 -12.5,0 5,5 0,0 z"
style="fill-rule:evenodd;stroke:#000000;stroke-width:1pt"
transform="matrix(-0.4,0,0,-0.4,-4,0)" />
</marker>
<marker
inkscape:stockid="Arrow1Mend"
orient="auto"
refY="0"
refX="0"
id="Arrow1Mend-3"
style="overflow:visible">
<path
inkscape:connector-curvature="0"
id="path3998-2"
d="M 0,0 5,-5 -12.5,0 5,5 0,0 z"
style="fill-rule:evenodd;stroke:#000000;stroke-width:1pt"
transform="matrix(-0.4,0,0,-0.4,-4,0)" />
</marker>
<marker
inkscape:stockid="Arrow1Mend"
orient="auto"
refY="0"
refX="0"
id="Arrow1Mend-2"
style="overflow:visible">
<path
inkscape:connector-curvature="0"
id="path3998-22"
d="M 0,0 5,-5 -12.5,0 5,5 0,0 z"
style="fill-rule:evenodd;stroke:#000000;stroke-width:1pt"
transform="matrix(-0.4,0,0,-0.4,-4,0)" />
</marker>
<marker
inkscape:stockid="Arrow1Mend"
orient="auto"
refY="0"
refX="0"
id="marker4917"
style="overflow:visible">
<path
inkscape:connector-curvature="0"
id="path4919"
d="M 0,0 5,-5 -12.5,0 5,5 0,0 z"
style="fill-rule:evenodd;stroke:#000000;stroke-width:1pt"
transform="matrix(-0.4,0,0,-0.4,-4,0)" />
</marker>
<marker
inkscape:stockid="Arrow1Mend"
orient="auto"
refY="0"
refX="0"
id="marker4921"
style="overflow:visible">
<path
inkscape:connector-curvature="0"
id="path4923"
d="M 0,0 5,-5 -12.5,0 5,5 0,0 z"
style="fill-rule:evenodd;stroke:#000000;stroke-width:1pt"
transform="matrix(-0.4,0,0,-0.4,-4,0)" />
</marker>
<marker
inkscape:stockid="Arrow1Mend"
orient="auto"
refY="0"
refX="0"
id="Arrow1Mend-9"
style="overflow:visible">
<path
inkscape:connector-curvature="0"
id="path3998-7"
d="M 0,0 5,-5 -12.5,0 5,5 0,0 z"
style="fill-rule:evenodd;stroke:#000000;stroke-width:1pt"
transform="matrix(-0.4,0,0,-0.4,-4,0)" />
</marker>
<marker
inkscape:stockid="Arrow1Mend"
orient="auto"
refY="0"
refX="0"
id="Arrow1Mend-6"
style="overflow:visible">
<path
inkscape:connector-curvature="0"
id="path3998-6"
d="M 0,0 5,-5 -12.5,0 5,5 0,0 z"
style="fill-rule:evenodd;stroke:#000000;stroke-width:1pt"
transform="matrix(-0.4,0,0,-0.4,-4,0)" />
</marker>
<marker
inkscape:stockid="Arrow1Mend"
orient="auto"
refY="0"
refX="0"
id="Arrow1Mend-51"
style="overflow:visible">
<path
inkscape:connector-curvature="0"
id="path3998-3"
d="M 0,0 5,-5 -12.5,0 5,5 0,0 z"
style="fill-rule:evenodd;stroke:#000000;stroke-width:1pt"
transform="matrix(-0.4,0,0,-0.4,-4,0)" />
</marker>
</defs>
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="1"
inkscape:cx="226.09436"
inkscape:cy="741.31258"
inkscape:document-units="px"
inkscape:current-layer="layer1"
showgrid="true"
inkscape:window-width="1598"
inkscape:window-height="876"
inkscape:window-x="0"
inkscape:window-y="22"
inkscape:window-maximized="1">
<inkscape:grid
type="xygrid"
id="grid2985" />
</sodipodi:namedview>
<metadata
id="metadata7">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<g
inkscape:label="Ebene 1"
inkscape:groupmode="layer"
id="layer1">
<rect
style="fill:#e0e0e0;fill-opacity:1;stroke:#000000;stroke-width:2;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0"
id="rect3010"
width="35.433071"
height="17.716536"
x="442.91339"
y="25.611128" />
<text
xml:space="preserve"
style="font-size:20px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Nimbus Mono L;-inkscape-font-specification:Nimbus Mono L"
x="496.06299"
y="38.581394"
id="text3012"
sodipodi:linespacing="125%"><tspan
sodipodi:role="line"
id="tspan3014"
x="496.06299"
y="38.581394"
style="font-size:16px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-family:Nimbus Sans L;-inkscape-font-specification:Nimbus Sans L">unaffected (in main array)</tspan></text>
<flowRoot
xml:space="preserve"
id="flowRoot3800"
style="fill:black;stroke:none;stroke-opacity:1;stroke-width:1px;stroke-linejoin:miter;stroke-linecap:butt;fill-opacity:1;font-family:Nimbus Mono L;font-style:normal;font-weight:normal;font-size:20px;line-height:125%;letter-spacing:0px;word-spacing:0px;-inkscape-font-specification:Nimbus Mono L;font-stretch:normal;font-variant:normal"><flowRegion
id="flowRegion3802"><rect
id="rect3804"
width="276.7818"
height="34.345188"
x="424.26407"
y="20.996433" /></flowRegion><flowPara
id="flowPara3806" /></flowRoot> <rect
style="fill:#e0e0e0;fill-opacity:1;stroke:#000000;stroke-width:0;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0"
id="rect3812"
width="70.866142"
height="17.716536"
x="17.716558"
y="60.236198" />
<rect
style="fill:#b0b0ff;fill-opacity:1;stroke:#000000;stroke-width:2;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0"
id="rect3814"
width="35.433071"
height="17.716536"
x="442.91339"
y="61.480198" />
<rect
style="fill:#80ff00;fill-opacity:1;stroke:#000000;stroke-width:2;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0"
id="rect3816"
width="35.433071"
height="17.716536"
x="442.91339"
y="96.913269" />
<rect
style="fill:#ffff40;fill-opacity:1;stroke:#000000;stroke-width:2;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0"
id="rect3818"
width="35.433071"
height="17.716536"
x="442.91339"
y="131.10234" />
<rect
style="fill:#b0b0ff;fill-opacity:1;stroke:#000000;stroke-width:2;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0"
id="rect3816-2"
width="88.58268"
height="17.716536"
x="124.01577"
y="60.236198" />
<rect
style="fill:#e0e0e0;fill-opacity:1;stroke:#000000;stroke-width:0;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0"
id="rect3812-5"
width="88.582687"
height="17.716536"
x="301.18112"
y="60.236198" />
<rect
style="fill:#80ff00;fill-opacity:1;stroke:#000000;stroke-width:2;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0"
id="rect3816-4"
width="35.433071"
height="17.716536"
x="442.91339"
y="96.913269" />
<rect
style="fill:#80ff00;fill-opacity:1;stroke:#000000;stroke-width:2;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0"
id="rect3816-4-2"
width="35.433071"
height="17.716536"
x="88.582703"
y="95.669266" />
<path
style="fill:none;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
d="m 17.716558,60.236198 70.866142,0 0,17.716536 -70.866142,0"
id="path3939"
inkscape:connector-curvature="0" />
<path
style="fill:none;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
d="m 389.7638,60.236198 -88.58268,0 0,17.716536 88.58268,0"
id="path3941"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cccc" />
<rect
style="fill:#e0e0e0;fill-opacity:1;stroke:#000000;stroke-width:0;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0"
id="rect3812-3"
width="70.866142"
height="17.716536"
x="17.71656"
y="148.81888" />
<rect
style="fill:#e0e0e0;fill-opacity:1;stroke:#000000;stroke-width:0;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0"
id="rect3812-5-7"
width="88.582687"
height="17.716537"
x="301.18112"
y="148.81888" />
<path
style="fill:none;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
d="m 17.716558,148.81888 70.86614,0 0,17.71653 -70.86614,0"
id="path3939-4"
inkscape:connector-curvature="0" />
<path
style="fill:none;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
d="m 389.7638,148.81887 -88.58268,1e-5 0,17.71653 88.58268,0"
id="path3941-8"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cccc" />
<rect
style="fill:#80ff00;fill-opacity:1;stroke:#000000;stroke-width:2;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0"
id="rect3816-4-2-6"
width="35.433071"
height="17.716536"
x="124.01577"
y="148.81888" />
<rect
style="fill:#b0b0ff;fill-opacity:1;stroke:#000000;stroke-width:2;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0"
id="rect3816-2-5"
width="53.149605"
height="17.716537"
x="159.44885"
y="148.81888" />
<rect
style="fill:#b0b0ff;fill-opacity:1;stroke:#000000;stroke-width:2;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0"
id="rect3816-2-50"
width="35.433067"
height="17.716537"
x="212.59845"
y="184.25195" />
<rect
style="fill:#ffff40;fill-opacity:1;stroke:#000000;stroke-width:2;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0"
id="rect3818-4"
width="53.149609"
height="17.716534"
x="248.03152"
y="184.25195" />
<path
style="fill:none;stroke:#808080;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:4, 2;stroke-dashoffset:0"
d="m 88.5827,148.81887 35.43307,0"
id="path4078-6"
inkscape:connector-curvature="0" />
<path
style="fill:none;stroke:#808080;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:4, 2;stroke-dashoffset:0"
d="m 88.5827,166.53541 35.43307,0"
id="path4078-4-7"
inkscape:connector-curvature="0" />
<path
style="fill:none;stroke:#808080;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:4, 2;stroke-dashoffset:0"
d="m 212.59845,60.236198 88.58267,0"
id="path4078-66"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cc" />
<path
style="fill:none;stroke:#808080;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:4, 2;stroke-dashoffset:0"
d="m 212.59845,77.952734 88.58267,0"
id="path4078-4-0"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cc" />
<path
style="fill:none;stroke:#8080ff;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:0;stroke-dasharray:2, 4;stroke-dashoffset:0"
d="m 88.5827,77.952734 0,17.716535"
id="path4156"
inkscape:connector-curvature="0" />
<path
style="fill:none;stroke:#808000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:0;stroke-dasharray:4, 2;stroke-dashoffset:0"
d="m 88.5827,77.952734 0,17.716535"
id="path4176"
inkscape:connector-curvature="0" />
<path
style="fill:none;stroke:#808000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:0;stroke-dasharray:4, 2;stroke-dashoffset:0"
d="m 124.01577,95.669269 0,-17.716535"
id="path4178"
inkscape:connector-curvature="0" />
<path
style="fill:none;stroke:#808000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:0;stroke-dasharray:4, 2;stroke-dashoffset:0"
d="m 212.59845,166.53541 0,17.71654"
id="path4180"
inkscape:connector-curvature="0" />
<path
style="fill:none;stroke:#808000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:0;stroke-dasharray:4, 2;stroke-dashoffset:0"
d="m 301.18112,184.25195 0,-17.71654"
id="path4182"
inkscape:connector-curvature="0" />
<path
style="fill:none;stroke:#8080ff;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:0;stroke-dasharray:4, 2;stroke-dashoffset:0"
d="m 88.5827,77.952734 0,17.716535"
id="path4186"
inkscape:connector-curvature="0"
inkscape:export-filename="/home/sg/dev/dillo/dillo/doc/path4188.png"
inkscape:export-xdpi="69"
inkscape:export-ydpi="69" />
<path
style="fill:none;stroke:#8080ff;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:0;stroke-dasharray:4, 2;stroke-dashoffset:0"
d="m 124.01577,95.669269 0,-17.716535"
id="path4188"
inkscape:connector-curvature="0"
inkscape:export-xdpi="69"
inkscape:export-ydpi="69" />
<path
style="fill:none;stroke:#8080ff;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:0;stroke-dasharray:4, 2;stroke-dashoffset:0"
d="m 212.59845,166.53541 0,17.71654"
id="path4190"
inkscape:connector-curvature="0"
inkscape:export-filename="/home/sg/dev/dillo/dillo/doc/path4188.png"
inkscape:export-xdpi="69"
inkscape:export-ydpi="69" />
<path
style="fill:none;stroke:#8080ff;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:0;stroke-dasharray:4, 2;stroke-dashoffset:0"
d="m 301.18112,184.25195 0,-17.71654"
id="path4192"
inkscape:connector-curvature="0"
inkscape:export-filename="/home/sg/dev/dillo/dillo/doc/path4188.png"
inkscape:export-xdpi="69"
inkscape:export-ydpi="69" />
<path
style="fill:none;stroke:#808080;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:4, 2;stroke-dashoffset:0"
d="m 88.5827,77.952734 0,17.716535"
id="path4078-6-0"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cc" />
<path
style="fill:none;stroke:#808080;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:4, 2;stroke-dashoffset:0"
d="m 124.01577,77.952734 0,17.716535"
id="path4078-6-0-9"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cc" />
<path
style="fill:none;stroke:#808080;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:4, 2;stroke-dashoffset:0"
d="m 212.59845,166.53541 0,17.71654"
id="path4078-6-0-0"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cc" />
<path
style="fill:none;stroke:#808080;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:4, 2;stroke-dashoffset:0"
d="m 301.18112,166.53541 0,17.71654"
id="path4078-6-0-6"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cc" />
<path
style="fill:none;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;marker-start:none;marker-end:url(#Arrow1Mend)"
d="m 106.29923,116.92911 c 35.43307,28.34646 0,0 35.43307,28.34646"
id="path3803"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cc" />
<path
style="fill:none;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;marker-start:none;marker-end:url(#Arrow1Mend)"
d="m 148.81892,81.496041 c 35.43307,63.779529 0,0 35.43307,63.779529"
id="path3803-7"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cc" />
<path
style="fill:none;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:4, 2;stroke-dashoffset:0"
d="m 177.16537,60.236198 0,17.716535"
id="path4078-6-0-9-5"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cc" />
<path
style="fill:none;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;marker-start:none;marker-end:url(#Arrow1Mend)"
d="m 194.88191,81.496038 c 35.43307,99.212602 0,3e-6 35.43307,99.212602"
id="path3803-7-0"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cc" />
<text
xml:space="preserve"
style="font-size:20px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Nimbus Mono L;-inkscape-font-specification:Nimbus Mono L"
x="496.06299"
y="74.426468"
id="text3012-5"
sodipodi:linespacing="125%"><tspan
sodipodi:role="line"
id="tspan3014-0"
x="496.06299"
y="74.426468"
style="font-size:16px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-family:Nimbus Sans L;-inkscape-font-specification:Nimbus Sans L">main array (moved)</tspan></text>
<text
xml:space="preserve"
style="font-size:20px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Nimbus Sans L;-inkscape-font-specification:Nimbus Sans L"
x="496.06299"
y="109.85954"
id="text4897"
sodipodi:linespacing="125%"><tspan
sodipodi:role="line"
id="tspan4899"
x="496.06299"
y="109.85954"
style="font-size:16px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-family:Nimbus Sans L;-inkscape-font-specification:Nimbus Sans L">original extra array</tspan></text>
<text
xml:space="preserve"
style="font-size:20px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Nimbus Mono L;-inkscape-font-specification:Nimbus Mono L"
x="496.06299"
y="145.60861"
id="text4901"
sodipodi:linespacing="125%"><tspan
sodipodi:role="line"
id="tspan4903"
x="496.06299"
y="145.60861"
style="font-size:16px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-family:Nimbus Sans L;-inkscape-font-specification:Nimbus Sans L">new inserted area</tspan></text>
<rect
style="fill:#e0e0e0;fill-opacity:1;stroke:#000000;stroke-width:0;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0"
id="rect3812-37"
width="53.149609"
height="17.716536"
x="17.716558"
y="272.83463" />
<rect
style="fill:#b0b0ff;fill-opacity:1;stroke:#000000;stroke-width:2;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0"
id="rect3816-2-1"
width="35.433067"
height="17.716537"
x="159.44884"
y="272.83463" />
<rect
style="fill:#e0e0e0;fill-opacity:1;stroke:#000000;stroke-width:0;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0"
id="rect3812-5-5"
width="53.149582"
height="17.716537"
x="336.61423"
y="272.83463" />
<rect
style="fill:#80ff00;fill-opacity:1;stroke:#000000;stroke-width:2;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0"
id="rect3816-4-2-3"
width="88.582664"
height="17.716534"
x="70.866173"
y="308.2677" />
<path
style="fill:none;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
d="m 17.716558,272.83462 53.149615,0 0,17.71654 -53.149615,0"
id="path3939-43"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cccc" />
<path
style="fill:none;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
d="m 389.7638,272.83462 -53.14961,0 0,17.71654 53.14961,0"
id="path3941-6"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cccc" />
<rect
style="fill:#e0e0e0;fill-opacity:1;stroke:#000000;stroke-width:0;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0"
id="rect3812-3-3"
width="53.149624"
height="17.716515"
x="17.716558"
y="361.4173" />
<rect
style="fill:#e0e0e0;fill-opacity:1;stroke:#000000;stroke-width:0;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0"
id="rect3812-5-7-9"
width="53.149582"
height="17.716543"
x="336.61423"
y="361.4173" />
<path
style="fill:none;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
d="m 17.716559,361.4173 53.149604,0 0,17.71653 -53.149604,1e-5"
id="path3939-4-2"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cccc" />
<path
style="fill:none;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
d="m 389.7638,361.4173 -53.14961,0 0,17.71653 53.14961,1e-5"
id="path3941-8-1"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cccc" />
<rect
style="fill:#80ff00;fill-opacity:1;stroke:#000000;stroke-width:2;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0"
id="rect3816-4-2-6-7"
width="35.433071"
height="17.716536"
x="159.44884"
y="361.4173" />
<rect
style="fill:#b0b0ff;fill-opacity:1;stroke:#000000;stroke-width:2;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0"
id="rect3816-2-50-6"
width="35.433067"
height="17.716537"
x="248.03151"
y="396.85037" />
<rect
style="fill:#ffff40;fill-opacity:1;stroke:#000000;stroke-width:2;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0"
id="rect3818-4-9"
width="53.149609"
height="17.716534"
x="283.4646"
y="396.85037" />
<path
style="fill:none;stroke:#808080;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:4, 2;stroke-dashoffset:0"
d="m 70.866173,361.41729 88.582667,1e-5"
id="path4078-6-8"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cc" />
<path
style="fill:none;stroke:#808080;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:4, 2;stroke-dashoffset:0"
d="m 70.866173,379.13383 88.582667,1e-5"
id="path4078-4-7-4"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cc" />
<path
style="fill:none;stroke:#808080;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:4, 2;stroke-dashoffset:0"
d="m 194.88192,272.83462 141.73227,0"
id="path4078-66-9"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cc" />
<path
style="fill:none;stroke:#808080;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:4, 2;stroke-dashoffset:0"
d="m 194.88192,290.55116 141.73227,0"
id="path4078-4-0-6"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cc" />
<path
style="fill:none;stroke:#8080ff;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:0;stroke-dasharray:2, 4;stroke-dashoffset:0"
d="m 70.866173,290.55116 0,17.71653"
id="path4156-9"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cc" />
<path
style="fill:none;stroke:#808000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:0;stroke-dasharray:4, 2;stroke-dashoffset:0"
d="m 70.866173,290.55116 0,17.71653"
id="path4176-4"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cc" />
<path
style="fill:none;stroke:#808000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:0;stroke-dasharray:4, 2;stroke-dashoffset:0"
d="m 106.29924,308.26769 0,-17.71653"
id="path4178-9"
inkscape:connector-curvature="0" />
<path
style="fill:none;stroke:#808000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:0;stroke-dasharray:4, 2;stroke-dashoffset:0"
d="m 194.88192,379.13383 0,17.71654"
id="path4180-3"
inkscape:connector-curvature="0" />
<path
style="fill:none;stroke:#808000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:0;stroke-dasharray:4, 2;stroke-dashoffset:0"
d="m 336.61419,396.85037 0,-17.71654"
id="path4182-8"
inkscape:connector-curvature="0" />
<path
style="fill:none;stroke:#8080ff;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:0;stroke-dasharray:4, 2;stroke-dashoffset:0"
d="m 70.866173,290.55116 0,17.71653"
id="path4186-1"
inkscape:connector-curvature="0"
inkscape:export-filename="/home/sg/dev/dillo/dillo/doc/path4188.png"
inkscape:export-xdpi="69"
inkscape:export-ydpi="69"
sodipodi:nodetypes="cc" />
<path
style="fill:none;stroke:#8080ff;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:0;stroke-dasharray:4, 2;stroke-dashoffset:0"
d="m 106.29924,308.26769 0,-17.71653"
id="path4188-2"
inkscape:connector-curvature="0"
inkscape:export-xdpi="69"
inkscape:export-ydpi="69" />
<path
style="fill:none;stroke:#8080ff;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:0;stroke-dasharray:4, 2;stroke-dashoffset:0"
d="m 194.88192,379.13383 0,17.71654"
id="path4190-8"
inkscape:connector-curvature="0"
inkscape:export-filename="/home/sg/dev/dillo/dillo/doc/path4188.png"
inkscape:export-xdpi="69"
inkscape:export-ydpi="69" />
<path
style="fill:none;stroke:#8080ff;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:0;stroke-dasharray:4, 2;stroke-dashoffset:0"
d="m 336.61419,396.85037 0,-17.71654"
id="path4192-4"
inkscape:connector-curvature="0"
inkscape:export-filename="/home/sg/dev/dillo/dillo/doc/path4188.png"
inkscape:export-xdpi="69"
inkscape:export-ydpi="69" />
<path
style="fill:none;stroke:#808080;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:4, 2;stroke-dashoffset:0"
d="m 70.866173,290.55116 0,17.71653"
id="path4078-6-0-4"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cc" />
<path
style="fill:none;stroke:#808080;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:4, 2;stroke-dashoffset:0"
d="m 159.44883,290.55116 0,17.71653"
id="path4078-6-0-9-0"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cc" />
<path
style="fill:none;stroke:#808080;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:4, 2;stroke-dashoffset:0"
d="m 194.88192,379.13383 0,17.71654"
id="path4078-6-0-0-7"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cc" />
<path
style="fill:none;stroke:#808080;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:4, 2;stroke-dashoffset:0"
d="m 336.61419,379.13383 0,17.71654"
id="path4078-6-0-6-1"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cc" />
<rect
style="fill:#80ff00;fill-opacity:1;stroke:#000000;stroke-width:2;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0"
id="rect3816-4-2-6-7-8"
width="53.149616"
height="17.716539"
x="194.88191"
y="396.85037" />
<path
style="fill:none;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:4, 2;stroke-dashoffset:0"
d="m 106.29924,308.26769 0,17.71654"
id="path4078-6-0-9-5-1"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cc" />
<path
style="fill:none;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;marker-start:none;marker-end:url(#Arrow1Mend)"
d="m 88.5827,329.52754 c 88.58267,28.34645 0,0 88.58267,28.34645"
id="path3803-4"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cc" />
<path
style="fill:none;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;marker-start:none;marker-end:url(#Arrow1Mend)"
d="m 131.10238,329.52754 c 70.86614,24.80315 53.14961,3.5433 88.58268,63.77952"
id="path3803-4-0"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cc" />
<path
style="fill:none;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;marker-start:none;marker-end:url(#Arrow1Mend)"
d="m 177.16537,294.09447 c 88.58268,99.21259 0,0 88.58268,99.21259"
id="path3803-4-01"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cc" />
<text
xml:space="preserve"
style="font-size:20px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Nimbus Mono L;-inkscape-font-specification:Nimbus Mono L"
x="17.716536"
y="42.519665"
id="text3012-5-7"
sodipodi:linespacing="125%"><tspan
sodipodi:role="line"
id="tspan3014-0-2"
x="17.716536"
y="42.519665"
style="font-size:16px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-family:Nimbus Sans L;-inkscape-font-specification:Nimbus Sans L">Example 1:</tspan></text>
<text
xml:space="preserve"
style="font-size:20px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Nimbus Mono L;-inkscape-font-specification:Nimbus Mono L"
x="528"
y="389.36218"
id="text5205"
sodipodi:linespacing="125%"><tspan
sodipodi:role="line"
id="tspan5207"
x="528"
y="389.36218" /></text>
<text
xml:space="preserve"
style="font-size:20px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Nimbus Mono L;-inkscape-font-specification:Nimbus Mono L"
x="17.716536"
y="255.11809"
id="text3012-5-7-9"
sodipodi:linespacing="125%"><tspan
sodipodi:role="line"
x="17.716536"
y="255.11809"
style="font-size:16px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-family:Nimbus Sans L;-inkscape-font-specification:Nimbus Sans L"
id="tspan5230">Example 2:</tspan></text>
</g>
</svg>

After

Width:  |  Height:  |  Size: 35 KiB

View File

@ -0,0 +1,35 @@
/** \page rounding-errors How to Avoid Rounding Errors
(Probably, this is a standard algorithm, so if someone knows the name,
drop me a note.)
If something like
\f[y_i = {x_i a \over b}\f]
is to be calculated, and all numbers are integers, a naive
implementation would result in something, for which
\f[\sum y_i \ne {(\sum x_i) a \over b}\f]
because of rounding errors, due to the integer division. This can be
avoided by transforming the formula into
\f[y_i = {(\sum_{j=0}^{j=i} x_j) a \over b} - \sum_{j=0}^{j=i-1} y_j\f]
Of course, when all \f$y_i\f$ are calculated in a sequence,
\f$\sum_{j=0}^{j=i} x_j\f$ and \f$\sum_{j=0}^{j=i-1} y_j\f$ can be
accumulated in the same loop. Regard this as sample:
\code
int n, x[n], a, b; // Should all be initialized.
int y[n], cumX = 0, cumY = 0;
for (int i = 0; i < n; i++) {
cumX += x[i]
y[i] = (cumX * a) / b - cumY;
cumY += y[i];
}
\endcode
*/

195
devdoc/uml-legend.doc Normal file
View File

@ -0,0 +1,195 @@
/** \page uml-legend UML Legend
This page describes the notation for several diagrams used in the
documentation, which is a slight variation of UML.
<h2>Classes</h2>
Classes are represented by boxes, containing there names:
\dot
digraph G {
node [shape=record, fontname=Helvetica, fontsize=10];
fontname=Helvetica; fontsize=8;
"Concrete Class";
"Abstract Class" [color="#a0a0a0"];
Interface [color="#ff8080"];
}
\enddot
(In most cases, the attributes and operations are left away, for
better readability. Just click on it, to get to the detailed
description.)
Of course, in C++, there are no interfaces, but here, we call a class,
which has only virtual abstract methods, and so does not provide any
functionality, an interface.
Templates get a yellow background color:
\dot
digraph G {
node [shape=record, fontname=Helvetica, fontsize=10,
fillcolor="#ffffc0", style="filled"];
fontname=Helvetica; fontsize=8;
"Concrete Class Template";
"Abstract Class Template" [color="#a0a0a0"];
"Interface Template" [color="#ff8080"];
}
\enddot
<h2>Objects</h2>
In some cases, an example for a concrete constellation of objects is
shown. An object is represented by a box containing a name and the
class, separated by a colon.
\dot
digraph G {
node [shape=record, fontname=Helvetica, fontsize=10];
edge [arrowhead="open", labelfontname=Helvetica, labelfontsize=10,
color="#404040", labelfontcolor="#000080"];
fontname=Helvetica; fontsize=10;
"x: A" -> "y1: B";
"x: A" -> "y2: B";
}
\enddot
The names (\em x, \em y, and \em z) are only meant within the context
of the diagram, there needs not to be a relation to the actual names
in the program. They should be unique within the diagram.
Classes and objects may be mixed in one diagram.
<h2>Associations</h2>
\dot
digraph G {
node [shape=record, fontname=Helvetica, fontsize=10];
edge [arrowhead="open", labelfontname=Helvetica, labelfontsize=10,
color="#404040", labelfontcolor="#000080",
fontname=Helvetica, fontsize=10, fontcolor="#000080"];
fontname=Helvetica; fontsize=10;
A -> B [headlabel="*", taillabel="1", label="x"];
}
\enddot
In this example, one instance of A refers to an arbitrary number of B
instances (denoted by the "*"), and each instance of B is referred by
exactly one ("1") A. The label \em x is the name of the association,
in most cases the name of the field, e.g. A::x.
Possible other values for the \em multiplicity:
<ul>
<li> a concrete number, in most cases "1",
<li> a range, e.g. "0..1",
<li> "*", denoting an arbitrary number.
</ul>
<h2>Implementations and Inheritance</h2>
\dot
digraph G {
node [shape=record, fontname=Helvetica, fontsize=10];
edge [arrowhead="none", dir="both", arrowtail="empty",
labelfontname=Helvetica, labelfontsize=10, color="#404040",
labelfontcolor="#000080"];
fontname=Helvetica; fontsize=10;
A[color="#ff8080"];
B[color="#ff8080"];
C;
D;
A -> B;
A -> C [style="dashed"];
C -> D;
}
\enddot
In this example,
<ul>
<li> the interface B extends the interface A,
<li> the class C implements the interface A, and
<li> the class D extends the class C.
</ul>
<h2>Template Instantiations</h2>
Template instantiations are shown as own classes/interfaces, the
instantiation by the template is shown by a yellow dashed arrow:
\dot
digraph G {
node [shape=record, fontname=Helvetica, fontsize=10];
edge [arrowhead="none", arrowtail="empty", dir="both",
labelfontname=Helvetica, labelfontsize=10, color="#404040",
labelfontcolor="#000080"];
fontname=Helvetica; fontsize=10;
A[color="#ff8080"];
B[color="#ff8080"];
C[color="#ff8080", fillcolor="#ffffc0", style="filled"];
C_A[color="#ff8080", label="C \<A\>"];
C_B[color="#ff8080", label="C \<A\>"];
D;
C -> C_A [arrowhead="open", arrowtail="none", style="dashed",
color="#808000"];
C -> C_B [arrowhead="open", arrowtail="none", style="dashed",
color="#808000"];
A -> C_A;
B -> C_B;
C_A -> D [style="dashed"];
}
\enddot
In this example, the interface template C uses the template argument
as super interface.
<h2>Packages</h2>
Packages are presented by dashed rectangles:
\dot
digraph G {
node [shape=record, fontname=Helvetica, fontsize=10];
edge [arrowhead="none", arrowtail="empty", dir="both",
labelfontname=Helvetica, labelfontsize=10, color="#404040",
labelfontcolor="#000080"];
fontname=Helvetica; fontsize=10;
subgraph cluster_1 {
style="dashed"; color="#000080"; fontname=Helvetica; fontsize=10;
label="package 1";
A;
B [color="#a0a0a0"];
}
subgraph cluster_2 {
style="dashed"; color="#000080"; fontname=Helvetica; fontsize=10;
label="package 2";
C;
D [color="#a0a0a0"];
E
}
A -> C;
B -> D;
D -> E;
E -> A [arrowhead="open", arrowtail="none"];
}
\enddot
Packages may be nested.
*/

175
dillo-install-hyphenation Executable file
View File

@ -0,0 +1,175 @@
#!/usr/bin/env perl
use POSIX;
use File::Basename;
use Net::FTP;
use Getopt::Long;
$host = "mirrors.dotsrc.org";
$basesourcedir = "/ctan";
$sourcedir = "";
sub ftpmessage {
# I'm not sure whether $ftp->message is supposed to end with "\n"
# or not. To be sure (and have nicer output on the screen), it is
# removed here.
my $x = $ftp->message;
chomp $x;
return $x;
}
# Determine ${prefix}, for the default target directory for pattern
# files. Two different strategies ...
$makefile = (dirname $0) . "/Makefile";
if (-e $makefile) {
# 1. Makefile exists in the same directory, so it can be assumed
# that this script is not yet installed, but called from the source
# directory. Search Makefile for prexix.
open MF, $makefile or die "Cannot open $makefile: $!";
while (<MF>) {
if (/^\s*prefix\s*=\s*(.*)\s*$/) {
$prefix = $1;
}
}
close MF;
if ($prefix eq "") {
die "Prefix not defined in $makefile.";
}
} else {
# 2. Assume that this is script is installed, so its path contains
# the prefix.
if ($0 =~ /(.*)\/bin\/[^\/]+$/) {
$prefix = $1;
} else {
die "Failed to determine prefix from $0.";
}
}
$targetdir = "$prefix/lib/dillo/hyphenation";
if (!GetOptions ("host=s" => \$host,
"basesourcedir=s" => \$basesourcedir,
"sourcedir=s" => \$sourcedir,
"targetdir=s" => \$targetdir)
|| @ARGV == 0) {
print "Usage: $0 [OPTIONS] LANG [LANG ...]\n\n";
print <<EOT;
Download and install hyphenation patterns from CTAN for different languages,
via FTP. Languages are specified as defined by ISO 639-1 ("en" for English
etc.).
If there are multiple pattern files for a language (and so the filename is not
"hyph-LANG.pat.txt"), you must specify the respective part of the filename,
e. g. "en-gb" or "en-us". In this case, the extra part ("-gb" or "-us") is
automatically removed.
If you are not sure, simply specify the language code ("en" in this example);
you will then given a list of existing pattern files.
Options:
-h, --host=HOST the host to connect to (default:
mirrors.dotrc.org)
-s, --sourcedir=DIR the directory on the FTP server
-b, --basesourcedir=DIR alternatively, the base directory, after which
/language/hyph-utf8/tex/generic/hyph-utf8...
.../patterns/txt is added (default: /ctan/)
-t, --targetdir=DIR where to install the hyphenation patterns
(default: where they are read by dillo)
EOT
} else {
if ($sourcedir eq "") {
$sourcedir =
"$basesourcedir/language/hyph-utf8/tex/generic/hyph-utf8/patterns/txt";
}
if (!-e $targetdir) {
mkdir $targetdir or die "Cannot create directory $targetdir: $!";
print "Created $targetdir.\n";
}
# Connect to CTAN FTP server, change to the directory where the
# patterns lie, and read files list (which may be useful later).
$ftp = Net::FTP->new($host,Timeout=>240)
or die "Cannot connect to $host: $@";
$ftp->login() or die "Cannot login: ", ftpmessage;
$ftp->cwd($sourcedir)
or die "Cannot change to directory $sourcedir: ", ftpmessage;
@files = $ftp->ls or die "Cannot read directory: ", ftpmessage;
# Finally, read pattern files.
foreach $arg (@ARGV) {
if ($arg =~ /^([a-z]+)-.*$/) {
# More files per language, e. g. "en-gb".
$lang = $1;
} else {
# One file per language, e. g. "ru".
$lang = $arg;
}
# First, download the pattern file to a temporary file.
$tmppat = tmpnam();
if ($ftp->get ("hyph-$arg.pat.txt", $tmppat)) {
printf ("Successfully downloaded pattern file for \"$arg\".\n");
# Search for a licence file. (Only a warning, when it does
# not exist.)
$tmplic = tmpnam();
$licfound = 0;
if ($ftp->get ("hyph-$arg.lic.txt", $tmplic)) {
$licfound = 1;
} else {
print "Warning: Cannot download license file for \"$arg\": ",
ftpmessage, "\n";
}
# Combine both, licence and pattern, to the final pattern
# file.
$outfile = "$targetdir/$lang.pat";
open OUT, "> $outfile" or die "Cannot open $outfile: $!";
if ($licfound) {
print OUT
"% Licence from ftp://$host$sourcedir/hyph-$arg.lic.txt\n";
print OUT "%\n";
open IN, $tmplic or die "Cannot open $tmplic: $!";
while (<IN>) {
# Each line from the licence file must be a comment.
if (!/^%/) {
print OUT "% ";
}
print OUT;
}
close IN;
unlink $tmplic;
print OUT "%\n";
}
print OUT "% Patterns from ftp://$host$sourcedir/hyph-$arg.pat.txt\n";
print OUT "%\n";
open IN, $tmppat or die "Cannot open $tmppat: $!";
while (<IN>) {
print OUT;
}
close IN;
unlink $tmppat;
close OUT;
} else {
# Not found. If a single language was specified (e. g. "en"),
# search for possibilities.
print "Error: Cannot download pattern file for \"$arg\": ",
ftpmessage, "\n";
if ($lang eq $arg) {
print "Try one of these:\n";
foreach(@files) {
if (/^hyph-($lang-.*)\.pat\.txt$/) {
print " $1\n";
}
}
}
}
}
$ftp->quit;
}

10
dillo.desktop Normal file
View File

@ -0,0 +1,10 @@
[Desktop Entry]
GenericName=Web Browser
Name=Dillo
Comment=Fast and small graphical web browser
MimeType=text/html;text/xml;application/xhtml+xml;x-scheme-handler/http;x-scheme-handler/https;
Exec=dillo %U
Icon=dillo
Terminal=false
Type=Application
Categories=Network;WebBrowser;

469
dillorc Normal file
View File

@ -0,0 +1,469 @@
# dillorc
# Sample dillo initialization file.
#
# Lines that start with a '#' are comments.
# "#option=..." shows the built-in default.
# "# option=..." is an additional example.
# "option=..." overrides the built-in value.
#-------------------------------------------------------------------------
# FIRST SECTION :)
#-------------------------------------------------------------------------
# Set the desired initial browser size
# geometry=650x545+0+20
#geometry=780x580
# Change this (and the following option) if you want to have text-only browsing
# from the start. (While browsing, this can be changed from the tools/settings
# menu.)
#load_images=YES
# A space separated list of image formats that will be ignored (not viewed).
# An option to download the image will be offered instead.
# Available formats: gif, png, webp, jpeg and svg.
# ignore_image_formats="webp svg"
#ignore_image_formats=""
# Change this if you want background images to be loaded initially.
# (While browsing, this can be changed from the tools/settings menu.)
#load_background_images=NO
# Change this if you want to disable loading of CSS stylesheets initially.
# (While browsing, this can be changed from the tools/settings menu.)
#load_stylesheets=YES
# Change this if you want to disable parsing of embedded CSS initially.
# (While browsing, this can be changed from the tools/settings menu.)
#parse_embedded_css=YES
# Change the buffering scheme for drawing
# 0 no double buffering - useful for debugging
# 1 light buffering using a single back buffer for all windows
# 2 full fltk-based double buffering for all windows
#buffered_drawing=1
# Set your default directory for download/save operations
#save_dir=/tmp
# Set the increment in pixels that the page is moved up/down with each input
# from the mouse wheel, keyboard arrow keys, or scrollbar arrow buttons.
#scroll_step=100
# Controls the overlap in pixels when scrolling to the next or previous page.
# It is used to repeat the last line(s) to avoid losing the reading flow. It
# controls the overlap in both the vertical and horizontal directions.
#scroll_page_overlap=50
# Place the vertical scrollbar on the left side (default right).
#scrollbar_on_left=NO
# Enable the page mode for the vertical scrollbar. When this mode is enabled,
# clicking anywhere on the vertical scrollbar with the left button scrolls one
# page down. With the right button, one page up. Click with the middle button to
# jump to a given position.
#scrollbar_page_mode=NO
# Define custom actions for the link menu. The format is <label>:<cmd>. The
# command will be executed in the system shell using the system() call. You can
# implement your own handling logic in a script or program. The following
# environment variables are set:
# $url: URL being opened
# $origin: URL of the current document
# Examples:
# link_action="Debug variables:echo url=$url origin=$origin"
# link_action="Open in MPV:mpv $url"
# link_action="Open in Firefox:firefox $url"
#-------------------------------------------------------------------------
# RENDERING SECTION
#-------------------------------------------------------------------------
# Default fonts:
#
# If FLTK has been configured with Xft enabled (the default), you can use
# scalable fonts such as DejaVu or Liberation (try running
# "fc-list : family | cut -d ',' -f 2 | sort").
#font_serif="DejaVu Serif"
#font_sans_serif="DejaVu Sans"
#font_cursive="DejaVu Sans"
#font_fantasy="DejaVu Sans"
#font_monospace="DejaVu Sans Mono"
#
# Otherwise, use bitmapped fonts like the following (for a list, try running
# "xlsfonts -fn *-iso10646-1 | grep -v -e -0-0 | cut -d - -f 3 | sort | uniq").
# font_serif="times"
# font_sans_serif="helvetica"
# font_cursive="helvetica"
# font_fantasy="helvetica"
# font_monospace="courier"
# All font sizes are scaled by this value
# font_factor=1.5
#font_factor=1.0
# Maximum font size in pixels
#font_max_size=100
# Minimum font size in pixels
#font_min_size=6
# Show tooltip popups for HTML title attributes
#show_tooltip=YES
# Set this to YES to limit the word wrap width to the viewport width
#limit_text_width=NO
# If this is set to YES, all CSS size specifications are adjusted so that
# all contents can be displayed. (Except for tables, see below.)
#adjust_min_width=YES
# If this is set to YES, all CSS size specifications for tables are
# adjusted so that all contents can be displayed. This is separated
# from "adjust_min_width" so that it is able to mimic Firefox, which
# differentiates between tables and, say, textblocks (in some cases).
#adjust_table_min_width=YES
# Sets the initial zoom factor, which scales the size of all HTML elements.
# zoom_factor=1.5
#zoom_factor=1.0
#-------------------------------------------------------------------------
# PENALTIES
#-------------------------------------------------------------------------
# Penalties are used to control good and bad break points. The bigger
# the penalty for a given break point, the less likely the line is
# broken here. "inf" means that breaking is prohibited, "-inf" means
# that a line *must* be broken here. (The latter should not be used
# here, however.) Normal spaces get a penalty of 0. The exact
# definition can be found in doc/dw-line-breaking.doc.
# Penalties for hyphenation breaks; this covers automatic hyphenation,
# soft hyphens, and unconditional hyphens. Since hyphenation should
# rather be avoided, the default values are larger than 0.
# This is used for hyphenation points, when there is no hyphen or dash
# before:
#penalty_hyphen = 1
# This is used for hyphenation points, when the line before ends
# already with a hyphen or a dash. Consequent lines ending with
# hyphens or dashes should be avoided, so this value is bigger than
# "penalty_hyphen":
#penalty_hyphen_2 = 8
# The same for a break right of an em-dash, when there are no spaces
# surrounding it (as in English). The default values are the same as
# for hyphens:
#penalty_em_dash_right = 1
#penalty_em_dash_right_2 = 8
# Penalty for a break *left* of an em-dash. Since a line ending with
# an em-dash (and so breaking right of the em-dash) looks better than
# a line beginning with an em-dash (breaking left of an em-dash), the
# default value is bigger than "penalty_em_dash_right":
#penalty_em_dash_left = 8
# Notice that there is no "penalty_em_dash_left_2", since breaking
# left of an em-dash makes the line *begin*, not *end* with a dash.
# This factor is multiplied with the line height to get the
# stretchability of a non-justified line. The larger this factor (and
# thus, the stretchability), the less likely the words are hyphenated;
# so you can use this value to control hyphenation of non-justified
# text.
#stretchability_factor=1
#-------------------------------------------------------------------------
# NETWORK SECTION
#-------------------------------------------------------------------------
# Set the start page.
# start_page="about:blank"
# start_page="https://dillo-browser.github.io/"
# start_page="file:/home/jcid/custom_page.html"
#start_page="about:splash"
# Set the home location
# home="file:/home/jcid/HomePage/Home.html"
#home="https://dillo-browser.github.io/"
# Set the new tab page.
# new_tab_page="dpi:/bm/"
# new_tab_page="https://example.com/"
#new_tab_page="about:blank"
# Set the URLs used by the web search dialog.
# "%s" is replaced with the search keywords separated by '+'.
# Format: search_url="[prefix ][<label> ]<url>"
# You can enable multiple search_url strings at once and select from among
# them at runtime, with the first being the default.
# (the prefix serves to search from the Location Bar. e.g. "dd dillo image")
search_url="dd DuckDuckGo (https) https://duckduckgo.com/lite/?kp=-1&kd=-1&q=%s"
search_url="Wikipedia http://www.wikipedia.org/w/index.php?search=%s&go=Go"
search_url="Free Dictionary http://www.thefreedictionary.com/%s"
search_url="Startpage (https) https://www.startpage.com/do/search?query=%s"
search_url="Google https://www.google.com/search?ie=UTF-8&oe=UTF-8&gbv=1&q=%s"
# If set, dillo will ask web servers to send pages in this language.
# This setting does NOT change dillo's user interface.
# Format explained: www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.4
# Language-REGION values: www.iana.org/assignments/language-subtag-registry
# (by default, no Accept-Language header is sent)
# http_language="de"
# http_language="pt-BR"
# http_language="en-US,en;q=0.5"
# Maximum number of simultaneous TCP connections to a single server or proxy.
# http_max_conns=6
# If enabled, Dillo will reuse HTTP connections to a server or proxy when
# possible rather than making a new connection for every request for a new
# page/image/stylesheet.
#http_persistent_conns=YES
# This mechanism allows servers to specify that they are only to be contacted
# through HTTPS and not HTTP.
#
# Overall, this is a valuable security measure against TLS stripping
# attacks, etc., but in principle a site could contrive to use this as a
# tracking mechanism. The term is "HSTS super cookie", although note that these
# HSTS directives are not saved between browser sessions.
#http_strict_transport_security=YES
# If enabled, Dillo will force all HTTP connections to be upgraded to
# a more secure HTTPS connection. This will prevent sites from loading
# if they only support HTTP.
#http_force_https=NO
# Set the proxy information for http/https.
# Note that the http_proxy environment variable overrides this setting.
# WARNING: FTP and downloads plugins use wget. To use a proxy with them,
# you will need to configure wget accordingly. See
# http://www.gnu.org/software/wget/manual/html_node/Proxies.html
# http_proxy="http://localhost:8080/"
#(by default, no proxy is used)
# If you need to provide a user/password pair for the proxy,
# set the proxy user name here and Dillo will ask for the password later.
# http_proxyuser="joe"
#(by default, no proxy is used)
# Set the domains to access without proxy
# no_proxy = ".hola.com .mynet.cl .hi.de"
#no_proxy="localhost 127.0.0.1"
# Set the HTTP Referer (sic) header.
# Note that there is no option to reveal the page that you came from because it
# would endanger your privacy. 'host' and 'path' allow you to pretend that the
# link you followed was on the same site that you're going to.
# none : Don't send any Referer header at all.
# host : Send the requested URI's hostname.
# path : Send the requested URI's host and path.
#http_referer=host
# Set the HTTP User-Agent header.
# This can be useful for privacy and for working around servers who think
# Dillo is less capable than it really is. However, if you pretend to use a
# different browser, servers may send you pages that work with the features
# and bugs of that other browser -- or even disallow access in cases like
# wget or googlebot. Remember this before submitting bug reports.
#
# See http://zytrax.com/tech/web/browser_ids.htm for a compilation of strings.
#
# http_user_agent="Mozilla/5.0 (Windows NT 6.1; WOW64; rv:28.0) Gecko/20100101 Firefox/28.0"
# http_user_agent="Wget/1.13.4 (linux-gnu)"
#The default is "Dillo/"+current_version_number
#-------------------------------------------------------------------------
# COLORS SECTION
#-------------------------------------------------------------------------
# Set the page background color
# bg_color=gray
# bg_color=0xd6d6c0
#bg_color=0xdcd1ba
# If your eyes suffer with white backgrounds, change this.
#allow_white_bg=YES
# If allow_white_bg is set to NO, white backgrounds are replaced by
# this color.
#white_bg_replacement=0xe0e0a3
# When set to YES, the page author's visited link color may be overridden
# to allow better contrast with text/links/background
#contrast_visited_color=YES
#-------------------------------------------------------------------------
# USER INTERFACE SECTION
#-------------------------------------------------------------------------
# UI theme
# "none" is the default FLTK appearance, which "resembles old Windows...and
# old GTK/KDE".
# "plastic" "is inspired by the Aqua user interface on Mac OS X".
# "gtk+" "is inspired by the Red Hat Bluecurve theme".
#
# If you have fltk-1.3.3 or newer, you can specify "gleam", which
# is "a sort of Clearlooks Glossy scheme". ("fltk-config --version")
#theme=none
# theme=gtk+
# theme=plastic
# UI colors
# Note that FLTK may sometimes override colors, generally for contrast and
# readability.
#
# ui_fg_color, ui_main_bg_color, ui_text_bg_color, and ui_selection_color
# map to concepts in the underlying FLTK toolkit which are described as:
# "the default foreground color...used for labels and text", "default
# background color", "the default background color for text, list, and
# valuator widgets", and "the default selection/highlight color". They
# sometimes have other uses in the more complex FLTK widgets.
#
# ui_button_highlight_color is the background used when the mouse cursor is
# over a button. By default, this is a lightened version of the main
# background color.
#
# ui_tab_active_fg_color and ui_tab_active_bg_color are used for the current
# tab. By default, they are the main foreground color and the text background
# color, respectively.
#
# ui_tab_fg_color and ui_tab_bg_color are used for the other tabs. By default,
# they are the main foreground color and the main background color,
# respectively.
#
# ui_tab_height controls the height of the tabs (default 20).
#
# Note to packagers: leaving these variables for the system to guess
# gives different results in different environments, so we played it safe
# by defining the traditional colors. Please choose the color theme that
# better fits your distro.
#
# Gray theme (traditional)
#
ui_fg_color=black
ui_main_bg_color=#c6c6c6
ui_text_bg_color=#bfdabf
ui_selection_color=#191970
ui_button_highlight_color=#a9a9a9
ui_tab_active_bg_color=#87aca7
ui_tab_active_fg_color=black
ui_tab_bg_color=#b7beb7
#
# Earthly theme:
#
#ui_fg_color=#100404
#ui_main_bg_color=#c2a47b
#ui_text_bg_color=#cdc9a5
#ui_selection_color=#763024
#ui_tab_active_bg_color=#af4b3f
#ui_tab_active_fg_color=white
#ui_tab_bg_color=#d2b48c
#
# Greenish theme:
#
#ui_fg_color=#100404
#ui_main_bg_color=#c8d394
#ui_text_bg_color=#bdd8b6
#ui_selection_color=#7c5f42
#ui_button_highlight_color=#adad70
#ui_tab_active_bg_color=#b5b679
#ui_tab_active_fg_color=#b60907
#ui_tab_bg_color=#cac682
# Size of dillo panel
# tiny : buttons, location, and progress boxes in one row
# small : location in one row, buttons + progress boxes in another
# medium : adds text labels to buttons and boxes
# panel_size=tiny
# panel_size=small
#panel_size=medium
#small_icons=NO
# Here you can choose to hide some widgets of the dillo panel...
#show_back=YES
#show_forw=YES
#show_home=YES
#show_reload=YES
#show_save=YES
#show_stop=YES
#show_bookmarks=YES
#show_tools=YES
#show_filemenu=YES
#show_clear_url=YES
#show_url=YES
#show_search=YES
#show_help=YES
#show_progress_box=YES
# Show tooltip popups for the UI
#show_ui_tooltip=YES
# Start dillo with the panels hidden?
#fullwindow_start=NO
# When filling out forms, our default behaviour is to submit on enterpress,
# but only when there's a single text entry (to avoid incomplete submits).
# OTOH, if you have to fill out the same form repeatedly, you may find it
# useful to keep away from the mouse by forcing enter to submit.
#enterpress_forces_submit=NO
# A mouse's middle click over a link opens a new Tab.
# If you prefer to open a new Window instead, set it to NO.
#middle_click_opens_new_tab=YES
# A mouse's middle click over a tab closes the Tab.
# With mousewheel mouses, right click feels way better (set to YES).
#right_click_closes_tab=YES
# Scroll over tabs (using the mouse wheel) to switch among tabs.
# If set to NO, the page will be scrolled instead.
#scroll_switches_tabs=YES
# Reverse the direction of tab scrolling with mouse wheel.
#scroll_switches_tabs_reverse=NO
# Mouse middle click by default drives drag-scrolling.
# To paste an URL into the window instead of scrolling, set it to NO.
# Note: You could always paste the URL onto the URL box clear button.
#middle_click_drags_page=YES
# Focus follows new Tabs.
# You can hold SHIFT to temporarily revert this behaviour.
#focus_new_tab=NO
# Ask before quitting Dillo with more than one window or tab open.
#show_quit_dialog=NO
#-------------------------------------------------------------------------
# DEBUG MESSAGES SECTION
#-------------------------------------------------------------------------
# Soon we should add the "show_debug_messages=NO" option...
# Generic messages (mainly for debugging specific parts)
# Change this to disable them.
#show_msg=YES
#-------------------------------------------------------------------------
# HTML BUG MESSAGES SECTION
#-------------------------------------------------------------------------
# Accepted by the W3C validator but "strongly discouraged" by the SPEC.
# (Such as "TAB character inside <PRE>").
#show_extra_warnings=NO
# -----------------------------------------------------------------------
# dillorc ends here.

8
dlib/Makefile.am Normal file
View File

@ -0,0 +1,8 @@
AM_CPPFLAGS = \
-I$(top_srcdir)
noinst_LIBRARIES = libDlib.a
libDlib_a_SOURCES = \
dlib.h \
dlib.c

980
dlib/dlib.c Normal file
View File

@ -0,0 +1,980 @@
/*
* File: dlib.c
*
* Copyright (C) 2006-2007 Jorge Arellano Cid <jcid@dillo.org>
* Copyright (C) 2024 Rodrigo Arias Mallo <rodarima@gmail.com>
*
* 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 3 of the License, or
* (at your option) any later version.
*/
/* Memory allocation, Simple dynamic strings, Lists (simple and sorted),
* and a few utility functions
*/
/*
* TODO: vsnprintf() is in C99, maybe a simple replacement if necessary.
*/
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <ctype.h>
#include <time.h>
#include "dlib.h"
static bool_t dLib_show_msg = TRUE;
/* dlib msgs go to stderr to avoid problems with filter dpis */
#define DLIB_MSG(...) \
D_STMT_START { \
if (dLib_show_msg) \
fprintf(stderr, __VA_ARGS__); \
} D_STMT_END
/*
*- Memory --------------------------------------------------------------------
*/
void *dMalloc (size_t size)
{
void *value = malloc (size);
if (value == 0)
exit(1);
return value;
}
void *dRealloc (void *mem, size_t size)
{
void *value = realloc (mem, size);
if (value == 0)
exit(1);
return value;
}
void *dMalloc0 (size_t size)
{
void *value = dMalloc (size);
memset (value, 0, size);
return value;
}
void dFree (void *mem)
{
free(mem);
}
/*
*- strings (char *) ----------------------------------------------------------
*/
char *dStrdup(const char *s)
{
if (s) {
int len = strlen(s)+1;
char *ns = dNew(char, len);
memcpy(ns, s, len);
return ns;
}
return NULL;
}
char *dStrndup(const char *s, size_t sz)
{
if (s) {
char *ns = dNew(char, sz+1);
memcpy(ns, s, sz);
ns[sz] = 0;
return ns;
}
return NULL;
}
/**
* Concatenate a NULL-terminated list of strings
*/
char *dStrconcat(const char *s1, ...)
{
va_list args;
char *s, *ns = NULL;
if (s1) {
Dstr *dstr = dStr_sized_new(64);
va_start(args, s1);
for (s = (char*)s1; s; s = va_arg(args, char*))
dStr_append(dstr, s);
va_end(args);
ns = dstr->str;
dStr_free(dstr, 0);
}
return ns;
}
/**
* Remove leading and trailing whitespace
*/
char *dStrstrip(char *s)
{
char *p;
int len;
if (s && *s) {
for (p = s; dIsspace(*p); ++p);
for (len = strlen(p); len && dIsspace(p[len-1]); --len);
if (p > s)
memmove(s, p, len);
s[len] = 0;
}
return s;
}
/**
* Clear the contents of the string
*/
void dStrshred(char *s)
{
if (s)
memset(s, 0, strlen(s));
}
/**
* Return a new string of length 'len' filled with 'c' characters
*/
char *dStrnfill(size_t len, char c)
{
char *ret = dNew(char, len+1);
for (ret[len] = 0; len > 0; ret[--len] = c);
return ret;
}
/**
* strsep() implementation
*/
char *dStrsep(char **orig, const char *delim)
{
char *str, *p;
if (!(str = *orig))
return NULL;
p = strpbrk(str, delim);
if (p) {
*p++ = 0;
*orig = p;
} else {
*orig = NULL;
}
return str;
}
/*
* ASCII functions to avoid the case difficulties introduced by I/i in
* Turkic locales.
*/
/**
* Case insensitive strstr
*/
char *dStriAsciiStr(const char *haystack, const char *needle)
{
int i, j;
char *ret = NULL;
if (haystack && needle) {
for (i = 0, j = 0; haystack[i] && needle[j]; ++i)
if (D_ASCII_TOLOWER(haystack[i]) == D_ASCII_TOLOWER(needle[j])) {
++j;
} else if (j) {
i -= j;
j = 0;
}
if (!needle[j]) /* Got all */
ret = (char *)(haystack + i - j);
}
return ret;
}
int dStrAsciiCasecmp(const char *s1, const char *s2)
{
int ret = 0;
while ((*s1 || *s2) &&
!(ret = D_ASCII_TOLOWER(*s1) - D_ASCII_TOLOWER(*s2))) {
s1++;
s2++;
}
return ret;
}
int dStrnAsciiCasecmp(const char *s1, const char *s2, size_t n)
{
int ret = 0;
while (n-- && (*s1 || *s2) &&
!(ret = D_ASCII_TOLOWER(*s1) - D_ASCII_TOLOWER(*s2))) {
s1++;
s2++;
}
return ret;
}
/*
*- dStr ----------------------------------------------------------------------
*/
/**
* Private allocator
*/
static void dStr_resize(Dstr *ds, int n_sz, int keep)
{
if (n_sz >= 0) {
if (keep && n_sz > ds->len) {
ds->str = (Dstr_char_t*) dRealloc (ds->str, n_sz*sizeof(Dstr_char_t));
ds->sz = n_sz;
} else {
dFree(ds->str);
ds->str = dNew(Dstr_char_t, n_sz);
ds->sz = n_sz;
ds->len = 0;
ds->str[0] = 0;
}
}
}
/**
* Create a new string with a given size.
* Initialized to ""
*/
Dstr *dStr_sized_new (int sz)
{
Dstr *ds;
if (sz < 2)
sz = 2;
ds = dNew(Dstr, 1);
ds->str = NULL;
dStr_resize(ds, sz + 1, 0); /* (sz + 1) for the extra '\0' */
return ds;
}
/**
* Return memory if there's too much allocated.
*/
void dStr_fit (Dstr *ds)
{
dStr_resize(ds, ds->len + 1, 1);
}
/**
* Insert a C string, at a given position, into a Dstr (providing length).
* Note: It also works with embedded nil characters.
*/
void dStr_insert_l (Dstr *ds, int pos_0, const char *s, int l)
{
int n_sz;
if (ds && s && l && pos_0 >= 0 && pos_0 <= ds->len) {
for (n_sz = ds->sz; ds->len + l >= n_sz; n_sz *= 2);
if (n_sz > ds->sz) {
dStr_resize(ds, n_sz, (ds->len > 0) ? 1 : 0);
}
if (pos_0 < ds->len)
memmove(ds->str+pos_0+l, ds->str+pos_0, ds->len-pos_0);
memcpy(ds->str+pos_0, s, l);
ds->len += l;
ds->str[ds->len] = 0;
}
}
/**
* Insert a C string, at a given position, into a Dstr.
*/
void dStr_insert (Dstr *ds, int pos_0, const char *s)
{
if (s)
dStr_insert_l(ds, pos_0, s, strlen(s));
}
/**
* Append a C string to a Dstr (providing length).
* Note: It also works with embedded nil characters.
*/
void dStr_append_l (Dstr *ds, const char *s, int l)
{
dStr_insert_l(ds, ds->len, s, l);
}
/**
* Append a C string to a Dstr.
*/
void dStr_append (Dstr *ds, const char *s)
{
dStr_append_l(ds, s, strlen(s));
}
/**
* Create a new string.
* Initialized to 's' or empty if 's == NULL'
*/
Dstr *dStr_new (const char *s)
{
Dstr *ds = dStr_sized_new(0);
if (s && *s)
dStr_append(ds, s);
return ds;
}
/**
* Free a dillo string.
* if 'all' free everything, else free the structure only.
*/
void dStr_free (Dstr *ds, int all)
{
if (ds) {
if (all)
dFree(ds->str);
dFree(ds);
}
}
/**
* Append one character.
*/
void dStr_append_c (Dstr *ds, int c)
{
char cs[2];
if (ds) {
if (ds->sz > ds->len + 1) {
ds->str[ds->len++] = (Dstr_char_t)c;
ds->str[ds->len] = 0;
} else {
cs[0] = (Dstr_char_t)c;
cs[1] = 0;
dStr_append_l (ds, cs, 1);
}
}
}
/**
* Truncate a Dstr to be 'len' bytes long.
*/
void dStr_truncate (Dstr *ds, int len)
{
if (ds && len < ds->len) {
ds->str[len] = 0;
ds->len = len;
}
}
/**
* Clear a Dstr.
*/
void dStr_shred (Dstr *ds)
{
if (ds && ds->sz > 0)
memset(ds->str, '\0', ds->sz);
}
/**
* Erase a substring.
*/
void dStr_erase (Dstr *ds, int pos_0, int len)
{
if (ds && pos_0 >= 0 && len > 0 && pos_0 + len <= ds->len) {
memmove(ds->str + pos_0, ds->str + pos_0 + len, ds->len - pos_0 - len);
ds->len -= len;
ds->str[ds->len] = 0;
}
}
/**
* vsprintf-like function that appends.
* Used by: dStr_vsprintf(), dStr_sprintf() and dStr_sprintfa().
*/
void dStr_vsprintfa (Dstr *ds, const char *format, va_list argp)
{
int n, n_sz;
if (ds && format) {
va_list argp2; /* Needed in case of looping on non-32bit arch */
while (1) {
va_copy(argp2, argp);
n = vsnprintf(ds->str + ds->len, ds->sz - ds->len, format, argp2);
va_end(argp2);
#if defined(__sgi)
/* IRIX does not conform to C99; if the entire argument did not fit
* into the buffer, n = buffer space used (minus 1 for terminator)
*/
if (n > -1 && n + 1 < ds->sz - ds->len) {
ds->len += n; /* Success! */
break;
} else {
n_sz = ds->sz * 2;
}
#else
if (n > -1 && n < ds->sz - ds->len) {
ds->len += n; /* Success! */
break;
} else if (n > -1) { /* glibc >= 2.1 */
n_sz = ds->len + n + 1;
} else { /* old glibc */
n_sz = ds->sz * 2;
}
#endif
dStr_resize(ds, n_sz, (ds->len > 0) ? 1 : 0);
}
}
}
/**
* vsprintf-like function.
*/
void dStr_vsprintf (Dstr *ds, const char *format, va_list argp)
{
if (ds) {
dStr_truncate(ds, 0);
dStr_vsprintfa(ds, format, argp);
}
}
/**
* Printf-like function
*/
void dStr_sprintf (Dstr *ds, const char *format, ...)
{
va_list argp;
if (ds && format) {
va_start(argp, format);
dStr_vsprintf(ds, format, argp);
va_end(argp);
}
}
/**
* Printf-like function that appends.
*/
void dStr_sprintfa (Dstr *ds, const char *format, ...)
{
va_list argp;
if (ds && format) {
va_start(argp, format);
dStr_vsprintfa(ds, format, argp);
va_end(argp);
}
}
/**
* Compare two dStrs.
*/
int dStr_cmp(Dstr *ds1, Dstr *ds2)
{
int ret = 0;
if (ds1 && ds2)
ret = memcmp(ds1->str, ds2->str, MIN(ds1->len+1, ds2->len+1));
return ret;
}
/**
* Return a pointer to the first occurrence of needle in haystack.
*/
char *dStr_memmem(Dstr *haystack, Dstr *needle)
{
int i;
if (needle && haystack) {
if (needle->len == 0)
return haystack->str;
for (i = 0; i <= (haystack->len - needle->len); i++) {
if (haystack->str[i] == needle->str[0] &&
!memcmp(haystack->str + i, needle->str, needle->len))
return haystack->str + i;
}
}
return NULL;
}
/**
* Return a printable representation of the provided Dstr, limited to a length
* of roughly maxlen.
*
* This is NOT threadsafe.
*/
const char *dStr_printable(Dstr *in, int maxlen)
{
int i;
static const char *const HEX = "0123456789ABCDEF";
static Dstr *out = NULL;
if (in == NULL)
return NULL;
if (out)
dStr_truncate(out, 0);
else
out = dStr_sized_new(in->len);
for (i = 0; (i < in->len) && (out->len < maxlen); ++i) {
if (isprint(in->str[i]) || (in->str[i] == '\n')) {
dStr_append_c(out, in->str[i]);
} else {
dStr_append_l(out, "\\x", 2);
dStr_append_c(out, HEX[(in->str[i] >> 4) & 15]);
dStr_append_c(out, HEX[in->str[i] & 15]);
}
}
if (out->len >= maxlen)
dStr_append(out, "...");
return out->str;
}
/*
*- dList ---------------------------------------------------------------------
*/
/**
* Create a new empty list
*/
Dlist *dList_new(int size)
{
Dlist *l;
if (size <= 0)
return NULL;
l = dNew(Dlist, 1);
l->len = 0;
l->sz = size;
l->list = dNew(void*, l->sz);
return l;
}
/**
* Free a list (not its elements)
*/
void dList_free (Dlist *lp)
{
if (!lp)
return;
dFree(lp->list);
dFree(lp);
}
/**
* Insert an element at a given position [0 based]
*/
void dList_insert_pos (Dlist *lp, void *data, int pos0)
{
int i;
if (!lp || pos0 < 0 || pos0 > lp->len)
return;
if (lp->sz == lp->len) {
lp->sz *= 2;
lp->list = (void**) dRealloc (lp->list, lp->sz*sizeof(void*));
}
++lp->len;
for (i = lp->len - 1; i > pos0; --i)
lp->list[i] = lp->list[i - 1];
lp->list[pos0] = data;
}
/**
* Append a data item to the list
*/
void dList_append (Dlist *lp, void *data)
{
dList_insert_pos(lp, data, lp->len);
}
/**
* Prepend a data item to the list
*/
void dList_prepend (Dlist *lp, void *data)
{
dList_insert_pos(lp, data, 0);
}
/**
* For completing the ADT.
*/
int dList_length (Dlist *lp)
{
if (!lp)
return 0;
return lp->len;
}
/**
* Remove a data item without preserving order.
*/
void dList_remove_fast (Dlist *lp, const void *data)
{
int i;
if (!lp)
return;
for (i = 0; i < lp->len; ++i) {
if (lp->list[i] == data) {
lp->list[i] = lp->list[--lp->len];
break;
}
}
}
/*
* Remove a data item preserving order.
*/
void dList_remove (Dlist *lp, const void *data)
{
int i, j;
if (!lp)
return;
for (i = 0; i < lp->len; ++i) {
if (lp->list[i] == data) {
--lp->len;
for (j = i; j < lp->len; ++j)
lp->list[j] = lp->list[j + 1];
break;
}
}
}
/**
* Return the nth data item,
* NULL when not found or 'n0' is out of range
*/
void *dList_nth_data (Dlist *lp, int n0)
{
if (!lp || n0 < 0 || n0 >= lp->len)
return NULL;
return lp->list[n0];
}
/**
* Return the found data item, or NULL if not present.
*/
void *dList_find (Dlist *lp, const void *data)
{
int i = dList_find_idx(lp, data);
return (i >= 0) ? lp->list[i] : NULL;
}
/**
* Search a data item.
* Return value: its index if found, -1 if not present.
* (this is useful for a list of integers, for finding number zero).
*/
int dList_find_idx (Dlist *lp, const void *data)
{
int i, ret = -1;
if (!lp)
return ret;
for (i = 0; i < lp->len; ++i) {
if (lp->list[i] == data) {
ret = i;
break;
}
}
return ret;
}
/**
* Search a data item using a custom function.
* func() is given the list item and the user data as parameters.
* Return: data item when found, NULL otherwise.
*/
void *dList_find_custom (Dlist *lp, const void *data, dCompareFunc func)
{
int i;
void *ret = NULL;
if (!lp)
return ret;
for (i = 0; i < lp->len; ++i) {
if (func(lp->list[i], data) == 0) {
ret = lp->list[i];
break;
}
}
return ret;
}
/**
* QuickSort implementation.
* This allows for a simple compare function for all the ADT.
*/
static void QuickSort(void **left, void **right, dCompareFunc compare)
{
void **p = left, **q = right, **t = left;
while (1) {
while (p != t && compare(*p, *t) < 0)
++p;
while (q != t && compare(*q, *t) > 0)
--q;
if (p > q)
break;
if (p < q) {
void *tmp = *p;
*p = *q;
*q = tmp;
if (t == p)
t = q;
else if (t == q)
t = p;
}
if (++p > --q)
break;
}
if (left < q)
QuickSort(left, q, compare);
if (p < right)
QuickSort(p, right, compare);
}
/**
* Sort the list using a custom function
*/
void dList_sort (Dlist *lp, dCompareFunc func)
{
if (lp && lp->len > 1) {
QuickSort(lp->list, lp->list + lp->len - 1, func);
}
}
/**
* Insert an element into a sorted list.
* The comparison function receives two list elements.
*/
void dList_insert_sorted (Dlist *lp, void *data, dCompareFunc func)
{
int i, st, min, max;
if (lp) {
min = st = i = 0;
max = lp->len - 1;
while (min <= max) {
i = (min + max) / 2;
st = func(lp->list[i], data);
if (st < 0) {
min = i + 1;
} else if (st > 0) {
max = i - 1;
} else {
break;
}
}
dList_insert_pos(lp, data, (st >= 0) ? i : i+1);
}
}
/**
* Search a sorted list.
* Return the found data item, or NULL if not present.
* func() is given the list item and the user data as parameters.
*/
void *dList_find_sorted (Dlist *lp, const void *data, dCompareFunc func)
{
int i, st, min, max;
void *ret = NULL;
if (lp && lp->len) {
min = 0;
max = lp->len - 1;
while (min <= max) {
i = (min + max) / 2;
st = func(lp->list[i], data);
if (st < 0) {
min = i + 1;
} else if (st > 0) {
max = i - 1;
} else {
ret = lp->list[i];
break;
}
}
}
return ret;
}
/*
*- Parse function ------------------------------------------------------------
*/
/**
* Take a dillo rc line and return 'name' and 'value' pointers to it.
* Notes:
* - line is modified!
* - it skips blank lines and lines starting with '#'
*
* Return value: 1 on blank line or comment, 0 on successful value/pair,
* -1 otherwise.
*/
int dParser_parse_rc_line(char **line, char **name, char **value)
{
char *eq, *p;
int len, ret = -1;
dReturn_val_if_fail(*line, ret);
*name = NULL;
*value = NULL;
dStrstrip(*line);
if (!*line[0] || *line[0] == '#') {
/* blank line or comment */
ret = 1;
} else if ((eq = strchr(*line, '='))) {
/* get name */
for (p = *line; *p && *p != '=' && !dIsspace(*p); ++p);
*p = 0;
*name = *line;
/* skip whitespace */
if (p < eq)
for (++p; dIsspace(*p); ++p);
/* get value */
if (p == eq) {
for (++p; dIsspace(*p); ++p);
len = strlen(p);
if (len >= 2 && *p == '"' && p[len-1] == '"') {
p[len-1] = 0;
++p;
}
*value = p;
ret = 0;
}
}
return ret;
}
/*
*- Dlib messages -------------------------------------------------------------
*/
void dLib_show_messages(bool_t show)
{
dLib_show_msg = show;
}
/*
*- Misc utility functions ----------------------------------------------------
*/
/**
* Return the current working directory in a new string
*/
char *dGetcwd (void)
{
size_t size = 128;
while (1) {
char *buffer = dNew(char, size);
if (getcwd (buffer, size) == buffer)
return buffer;
dFree (buffer);
if (errno != ERANGE)
return 0;
size *= 2;
}
}
/**
* Return the home directory in a static string (don't free)
*/
char *dGethomedir (void)
{
static char *homedir = NULL;
if (!homedir) {
if (getenv("HOME")) {
homedir = dStrdup(getenv("HOME"));
} else if (getenv("HOMEDRIVE") && getenv("HOMEPATH")) {
homedir = dStrconcat(getenv("HOMEDRIVE"), getenv("HOMEPATH"), NULL);
} else {
DLIB_MSG("dGethomedir: $HOME not set, using '/'.\n");
homedir = dStrdup("/");
}
}
return homedir;
}
/**
* Get a line from a FILE stream.
* Return value: read line on success, NULL on EOF.
*/
char *dGetline (FILE *stream)
{
int ch;
Dstr *dstr;
char *line;
dReturn_val_if_fail (stream, 0);
dstr = dStr_sized_new(64);
while ((ch = fgetc(stream)) != EOF) {
dStr_append_c(dstr, ch);
if (ch == '\n')
break;
}
line = (dstr->len) ? dstr->str : NULL;
dStr_free(dstr, (line) ? 0 : 1);
return line;
}
/**
* Close a FD handling EINTR.
*/
int dClose(int fd)
{
int st;
do
st = close(fd);
while (st == -1 && errno == EINTR);
return st;
}
/**
* Portable usleep() function.
*
* The usleep() function is deprecated in POSIX.1-2001 and removed in
* POSIX.1-2008, see usleep(3).
*/
int dUsleep(unsigned long usec)
{
struct timespec ts;
int res;
ts.tv_sec = usec / 1000000UL;
ts.tv_nsec = (usec % 1000000UL) * 1000UL;
do {
res = nanosleep(&ts, &ts);
} while (res && errno == EINTR);
return res;
}

187
dlib/dlib.h Normal file
View File

@ -0,0 +1,187 @@
#ifndef __DLIB_H__
#define __DLIB_H__
#include <stdio.h> /* for FILE* */
#include <stddef.h> /* for size_t */
#include <stdarg.h> /* for va_list */
#include <string.h> /* for strerror */
#include "d_size.h"
#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */
/*
*-- Common macros -----------------------------------------------------------
*/
#ifndef FALSE
#define FALSE (0)
#endif
#ifndef TRUE
#define TRUE (!FALSE)
#endif
#undef MAX
#define MAX(a, b) (((a) > (b)) ? (a) : (b))
#undef MIN
#define MIN(a, b) (((a) < (b)) ? (a) : (b))
/* Handle signed char */
#define dIsspace(c) isspace((uchar_t)(c))
#define dIsalnum(c) isalnum((uchar_t)(c))
#define D_ASCII_TOUPPER(c) (((c) >= 'a' && (c) <= 'z') ? (c) - 0x20 : (c))
#define D_ASCII_TOLOWER(c) (((c) >= 'A' && (c) <= 'Z') ? (c) + 0x20 : (c))
/*
*-- Casts -------------------------------------------------------------------
*/
/* TODO: include a void* size test in configure.in */
/* (long) works for both 32bit and 64bit */
#define VOIDP2INT(p) ((long)(p))
#define INT2VOIDP(i) ((void*)((long)(i)))
/*
*-- Memory -------------------------------------------------------------------
*/
#define dNew(type, count) \
((type *) dMalloc ((unsigned) sizeof (type) * (count)))
#define dNew0(type, count) \
((type *) dMalloc0 ((unsigned) sizeof (type) * (count)))
void *dMalloc (size_t size);
void *dRealloc (void *mem, size_t size);
void *dMalloc0 (size_t size);
void dFree (void *mem);
/*
*- Debug macros --------------------------------------------------------------
*/
#define D_STMT_START do
#define D_STMT_END while (0)
#define dReturn_if(expr) \
D_STMT_START{ \
if (expr) { return; }; \
}D_STMT_END
#define dReturn_val_if(expr,val) \
D_STMT_START{ \
if (expr) { return val; }; \
}D_STMT_END
#define dReturn_if_fail(expr) \
D_STMT_START{ \
if (!(expr)) { return; }; \
}D_STMT_END
#define dReturn_val_if_fail(expr,val) \
D_STMT_START{ \
if (!(expr)) { return val; }; \
}D_STMT_END
/*
*- C strings -----------------------------------------------------------------
*/
char *dStrdup(const char *s);
char *dStrndup(const char *s, size_t sz);
char *dStrconcat(const char *s1, ...);
char *dStrstrip(char *s);
char *dStrnfill(size_t len, char c);
char *dStrsep(char **orig, const char *delim);
void dStrshred(char *s);
char *dStriAsciiStr(const char *haystack, const char *needle);
int dStrAsciiCasecmp(const char *s1, const char *s2);
int dStrnAsciiCasecmp(const char *s1, const char *s2, size_t n);
#define dStrerror strerror
/*
*-- dStr ---------------------------------------------------------------------
*/
#define Dstr_char_t char
typedef struct {
int sz; /* allocated size (private) */
int len;
Dstr_char_t *str;
} Dstr;
Dstr *dStr_new (const char *s);
Dstr *dStr_sized_new (int sz);
void dStr_fit (Dstr *ds);
void dStr_free (Dstr *ds, int all);
void dStr_append_c (Dstr *ds, int c);
void dStr_append (Dstr *ds, const char *s);
void dStr_append_l (Dstr *ds, const char *s, int l);
void dStr_insert (Dstr *ds, int pos_0, const char *s);
void dStr_insert_l (Dstr *ds, int pos_0, const char *s, int l);
void dStr_truncate (Dstr *ds, int len);
void dStr_shred (Dstr *ds);
void dStr_erase (Dstr *ds, int pos_0, int len);
void dStr_vsprintfa (Dstr *ds, const char *format, va_list argp);
void dStr_vsprintf (Dstr *ds, const char *format, va_list argp);
void dStr_sprintf (Dstr *ds, const char *format, ...);
void dStr_sprintfa (Dstr *ds, const char *format, ...);
int dStr_cmp(Dstr *ds1, Dstr *ds2);
char *dStr_memmem(Dstr *haystack, Dstr *needle);
const char *dStr_printable(Dstr *in, int maxlen);
/*
*-- dList --------------------------------------------------------------------
*/
typedef struct {
int sz; /* allocated size (private) */
int len;
void **list;
} Dlist;
/* dCompareFunc:
* Return: 0 if parameters are equal (for dList_find_custom).
* Return: 0 if equal, < 0 if (a < b), > 0 if (b < a) --for insert sorted.
*
* For finding a data node with an external key, the comparison function
* parameters are: first the data node, and then the key.
*/
typedef int (*dCompareFunc) (const void *a, const void *b);
Dlist *dList_new(int size);
void dList_free (Dlist *lp);
void dList_append (Dlist *lp, void *data);
void dList_prepend (Dlist *lp, void *data);
void dList_insert_pos (Dlist *lp, void *data, int pos0);
int dList_length (Dlist *lp);
void dList_remove (Dlist *lp, const void *data);
void dList_remove_fast (Dlist *lp, const void *data);
void *dList_nth_data (Dlist *lp, int n0);
void *dList_find (Dlist *lp, const void *data);
int dList_find_idx (Dlist *lp, const void *data);
void *dList_find_custom (Dlist *lp, const void *data, dCompareFunc func);
void dList_sort (Dlist *lp, dCompareFunc func);
void dList_insert_sorted (Dlist *lp, void *data, dCompareFunc func);
void *dList_find_sorted (Dlist *lp, const void *data, dCompareFunc func);
/*
*- Parse function ------------------------------------------------------------
*/
int dParser_parse_rc_line(char **line, char **name, char **value);
/*
*- Dlib messages -------------------------------------------------------------
*/
void dLib_show_messages(bool_t show);
/*
*- Misc utility functions ----------------------------------------------------
*/
char *dGetcwd(void);
char *dGethomedir(void);
char *dGetline(FILE *stream);
int dClose(int fd);
int dUsleep(unsigned long us);
#ifdef __cplusplus
}
#endif /* __cplusplus */
#endif /* __DLIB_H__ */

74
doc/Cookies.txt Normal file
View File

@ -0,0 +1,74 @@
Jan 2002, Jörgen Viksell - jorgen.viksell@telia.com,
Jorge Arellano Cid --
Last update: March 2010
==================
Cookies in Dillo
==================
The current specification for cookies is RFC 6265
( http://tools.ietf.org/html/rfc6265 ).
Cookies are handled by a dpi (plugin) which shares them between your
instances of Dillo.
Current cookie limits are: 20 per domain, and 1200 in total.
When the dpi exits, cookies that you have ACCEPTed are saved to
~/.dillo/cookies.txt, and ACCEPT_SESSION cookies are forgotten.
The dpi normally exits after a period of inactivity, but you can force it to
exit with the command "dpidc stop".
=====================
Controlling cookies
=====================
Out of the box, dillo rejects all cookies.
If you want to accept certain cookies, you can specify rules for different
domains in the file ~/.dillo/cookiesrc. The syntax looks like:
#host action
DEFAULT DENY
fltk.org ACCEPT
.host.com ACCEPT_SESSION
Line 0: Comment line begins with '#'.
Line 1: Deny all cookies from all domains not otherwise specified.
Line 2: Accept all cookies from fltk.org, and save them to
~/.dillo/cookies.txt when the cookies dpi exits.
Line 3: Accept all cookies from all subdomains of host.com, but
do not save them when the dpi exits.
If you are positive that you will never want any cookies, you can
configure/compile Dillo without cookie support. The option is:
./configure --disable-cookies
===================
Cookies & Privacy
===================
Cookies can be a severe threat to personal privacy. The pages you
visit can be tracked, logged, and associated to a personal data-record,
allowing the possibility of building a detailed profile of your
browsing habits.
This data is sold to companies that profit from direct use of such
information (SPAM, Spying, etc).
If this data is cross-referenced with other databases, they can end up
with more information than you have about yourself.
Some people may tell you this is "paranoid". But please, take my words
as those of someone who has written a web browser, a cookies implementation,
and who has deep understanding of HTTP and cookies.
The dillo project is especially concerned about privacy and security
issues. Our advice is to avoid cookies whenever possible and at most set
ACCEPT_SESSION to specific, trusted sites. -- You have been warned.

17
doc/Makefile.am Normal file
View File

@ -0,0 +1,17 @@
doc_DATA = user_help.html
man_MANS = dillo.1
EXTRA_DIST = \
README \
Cookies.txt \
dillo.1.in \
user_help.in.html \
install.md
dillo.1: $(srcdir)/dillo.1.in Makefile
sed 's%/usr/local%${prefix}%g' < $(srcdir)/dillo.1.in > dillo.1
# Use .in.html instead of .html.in so it is recognized as HTML.
user_help.html: $(srcdir)/user_help.in.html Makefile
sed 's/__VERSION__/${VERSION}/g' $(srcdir)/user_help.in.html > $@
DISTCLEANFILES = dillo.1 user_help.html

5
doc/README Normal file
View File

@ -0,0 +1,5 @@
Last update: June 2015
This directory contains user documentation. Developer documentation is
only stored in the Git repository at <https://github.com/dillo-browser/dillo>, in
the directory "devdoc", but not part of the tarball.

117
doc/dillo.1.in Normal file
View File

@ -0,0 +1,117 @@
.TH dillo 1 "May 28, 2015" "" "USER COMMANDS"
.SH NAME
dillo \- web browser
.SH SYNOPSIS
.B dillo
.RI [ OPTION ]...
.RB [ \-\- ]
.RI [ URL | FILE ]...
.SH DESCRIPTION
.PP
Dillo is a lightweight graphical web browser that aims to be secure.
It handles HTTP internally, and FILE, FTP, and
DATA URIs are handled through a plugin system (dpi). In addition,
.I EXPERIMENTAL
HTTPS support can be enabled. Both FTP and Dillo's download manager use the
.BR wget (1)
downloader.
.PP
Dillo displays HTML, text, PNG, JPEG, GIF, SVG and WebP files.
It handles cookies, HTTP authentication (basic and digest), proxying (basic),
and some CSS.
.PP
Framesets are displayed as links to frames, and there is currently
no support for javascript or video.
.PP
In order to use the hyphenation feature, pattern files from CTAN need to
be installed to
.IR /usr/local/lib/dillo/hyphenation/ .
This can be done with the script
.IR dillo-install-hyphenation .
Call it with ISO-639-1 language codes as arguments, or without arguments
to get more help.
.SH OPTIONS
.TP
\fB\-f\fR, \fB\-\-fullwindow\fR
Start in full window mode: hide address bar, navigation buttons, menu, and
status bar.
.TP
\fB\-g\fR, \fB\-geometry \fIGEO\fR
Set initial window position where \fIGEO\fR is
\fIW\fBx\fIH\fR[{\fB+\-\fR}\fIX\fR{\fB+\-\fR}\fIY\fR].
.TP
\fB\-h\fR, \fB\-\-help\fR
Display this help text and exit.
.TP
\fB\-l\fR, \fB\-\-local\fR
Don't load images or stylesheets, or follow redirections, for these FILEs or
URLs. This is intended for use with HTML email.
.TP
\fB\-v\fR, \fB\-\-version\fR
Display version info and exit.
.TP
\fB\-x\fR, \fB\-\-xid \fIXID\fR
Open first Dillo window in an existing window whose window ID is \fIXID\fR.
.SH EXIT STATUS
.TP
.B 0
No error.
.TP
.B 1
Internal error.
.TP
.B 2
Error in command line arguments.
.SH ENVIRONMENT
.TP
.BR "HOME " "(or " "HOMEDRIVE " "and " "HOMEPATH " "on Cygwin)"
User's home directory.
.TP
.B http_proxy
URL of proxy to send HTTP/HTTPS traffic through.
.SH FILES
.TP
.I dpid
Dillo plugin daemon
.TP
.I dpidc
Control program for dpid.
.TP
.I ~/.dillo/bm.txt
User bookmarks
.TP
.I ~/.dillo/certs/
Saved certificates for HTTPS.
.TP
.I ~/.dillo/cookies.txt
Stored cookies
.TP
.I ~/.dillo/cookiesrc
Cookie settings
.TP
.I ~/.dillo/dillorc
Configuration file.
.TP
.I ~/.dillo/domainrc
Rules for cross-domain requests.
.TP
.I ~/.dillo/dpid_comm_keys
Keys used in dpi daemon communication.
.TP
.I ~/.dillo/dpidrc
Contains name of directory containing dpis, and associates
dpi files with protocols.
.TP
.I ~/.dillo/keysrc
Keybindings.
.TP
.I ~/.dillo/style.css
User style sheet.
.TP
.I /usr/local/lib/dillo/hyphenation/
Hyphenation pattern files.
.SH SEE ALSO
.BR wget (1)
.PP
Dillo website:
.B https://dillo-browser.github.io/

BIN
doc/dillo.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 136 KiB

220
doc/install.md Normal file
View File

@ -0,0 +1,220 @@
# Installing Dillo on Linux
Dillo is already packaged in many Linux distributions. To use the binary
from your distribution check your package manager. Example in Arch
Linux:
```
$ sudo pacman -S dillo
```
## Building from source
Dillo requires FLTK-1.3, if you don't have it (try `fltk-config
--version` to check), follow the steps in the FLTK documentation to
install it:
https://www.fltk.org/doc-1.3/intro.html
Additionally, it is **strongly recommended** that you install a TLS
library to browse HTTPS pages. Currently, Dillo supports any of the
following libraries:
- OpenSSL 1.1 or 3
- LibreSSL
- mbedTLS 2 or 3 (TLSv1.3 is not supported yet)
If you don't want to use a TLS library, use the configure option
`--disable-tls` to disable TLS support. You can use `--disable-openssl`
and `--disable-mbedtls` to control the search. By default OpenSSL or
LibreSSL is search first, then mbedTLS.
For Debian, you can use the following command to install the required
packages to build Dillo:
```sh
$ sudo apt install gcc g++ autoconf automake make zlib1g-dev \
libfltk1.3-dev libssl-dev libc6-dev \
libpng-dev libjpeg-dev libwebp-dev
```
If you prefer to use mbedTLS, replace `libssl-dev` with
`libmbedtls-dev`.
To build and install Dillo follow the steps below.
### From a release
```sh
$ tar jxvf dillo-3.0.5.tar.bz2
$ cd dillo-3.0.5
$ mkdir build
$ cd build
$ ../configure --prefix=/usr/local
$ make
$ sudo make install
```
### From git
```sh
$ git clone https://github.com/dillo-browser/dillo.git
$ cd dillo
$ ./autogen.sh
$ mkdir build
$ cd build
$ ../configure --prefix=/usr/local
$ make
$ sudo make install
```
## Hyphenation database
In order to use the hyphenation feature, pattern files from CTAN need to
be installed. This can be done with the script
`dillo-install-hyphenation`. Call it with ISO-639-1 language codes
("en", "es", "de"...) as arguments, or without arguments to get more
help.
## Dpi programs
These are installed by `make install`. If you don't have root access,
copy "dillo" and "dpid" to some directory in your path and install
the dpis by running `./install-dpi-local` from the top directory (they
will be installed under ~/.dillo).
# Other systems
## BSD
On FreeBSD 14.0 you can install the required dependencies from ports as:
```
$ pkg install -y automake fltk
```
Notice that on BSD systems, some required dependencies like FLTK are
available as ports and are installed in /usr/local (instead of /usr).
Additionally, the compiler won't look for headers or libraries in
/usr/local by default (you can check with $CC -v), however the `$PATH`
may include /usr/local/bin, so the binaries may be available but not the
headers. Some dependencies like FLTK are searched invoking the
`fltk-config` command that inform where to find the headers and
libraries for that package.
Other libraries are searched by attempting to locate the headers and
libraries directly. By default, the configure script won't look in
non-default paths of the compiler, so to add /usr/local to the search
path, invoke the configure script with the following options:
```
$ ./configure CPPFLAGS='-I/usr/local/include' LDFLAGS='-L/usr/local/lib'
```
Note that you'll need GNU make to build Dillo.
If it crashes or locks at times, use the `--disable-threaded-dns`
option, so Dillo uses a single thread for name resolution.
## Solaris
Dillo may compile and run OK on Solaris but (please report).
Use gmake (a symbolic link make -> gmake works OK).
Solaris is very inconsistent so you may need to add/remove:
```
-lrt -lposix4
```
at link time.
## MacOS
Dillo is now packaged in [Homebrew](https://brew.sh/).
```
$ brew install dillo
```
### Building from source
To build Dillo on MacOS you would need to get FLTK as well as
autoconf and automake if you are building Dillo from the git repository.
They are available in the Homebrew package manager:
```
$ brew install autoconf automake fltk@1.3
```
For OpenSSL you can use either 1.1 or 3.X (recommended):
```
$ brew install openssl@1.1
$ brew install openssl@3
```
Homebrew installs libraries and headers in different folders depending on the
architecture (Intel vs ARM), so you will need to invoke the configure script
with the following options so it knows where to search:
```
$ ./configure LDFLAGS="-L`brew --prefix openssl`/lib" CPPFLAGS="-I`brew --prefix openssl`/include"
```
## Windows via Cygwin
Dillo can be built for Windows (tested on Windows 11) by using the
[Cygwin](https://www.cygwin.com/) POSIX portability layer and run with Xorg. You
will need the following dependencies to build Dillo with mbedTLS:
```
gcc-core gcc-g++ autoconf automake make zlib-devel mbedtls-devel libfltk-devel
libiconv-devel libpng-devel libjpeg-devel libwebp-devel
```
**Note**: Dillo can also be built with OpenSSL (libssl-devel) but there is a
[known problem with detached threads](https://github.com/dillo-browser/dillo/issues/172)
used by the DNS resolver and OpenSSL that causes a crash. If you use OpenSSL,
disable the threaded resolver with `--disable-threaded-dns`.
You will also need [Xorg](https://x.cygwin.com/docs/ug/cygwin-x-ug.html) to run
Dillo graphically:
```
xorg-server xinit
```
You can also install all the dependencies from the command line with:
```
setup-x86_64.exe -q -P gcc-core,gcc-g++,autoconf,automake,make,zlib-devel,mbedtls-devel,libfltk-devel,libiconv-devel,libpng-devel,libjpeg-devel,libwebp-devel,xorg-server,xinit
```
To build Dillo, follow the usual steps from a Cygwin shell:
```sh
$ git clone https://github.com/dillo-browser/dillo.git
$ cd dillo
$ ./autogen.sh
$ mkdir build
$ cd build
$ ../configure --prefix=/usr/local
$ make
$ make install
```
You should be able to find Dillo in the `$PATH` as it should be installed in
`/usr/local/bin/dillo`. To run it, you first need to have an [Xorg server
running](https://x.cygwin.com/docs/ug/using.html#using-starting), which you can
do from a shell:
```sh
$ startxwin
```
And then, from another shell:
```sh
$ export DISPLAY=:0
$ dillo
```

835
doc/user_help.in.html Normal file
View File

@ -0,0 +1,835 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<title>Dillo User Manual</title>
<style>
body {
background: white;
color: black;
line-height: 1.5;
margin: 3em;
font-family: sans-serif;
font-size: 16px;
}
.main {
margin: 0 auto; /* won't work on Dillo, yet */
max-width: 40em;
min-width: 20em;
}
ul.toc {
font-size: 14px;
padding: 1em;
}
ul.toc li {
padding: 0px;
}
h1 {
margin-top: 0.25em;
margin-bottom: 0.25em
}
h2 {
padding: 0.5em;
margin-top: 1em;
text-align: center;
}
h3 {
border-top: 1px solid #eee;
padding-top: 1em;
}
pre {
border-left: 4px solid #ccc;
padding: 0.5em;
background-color: #f8f8f8;
font-size: 14px;
}
ul {
margin-left: 0px;
padding-left: 1.5em;
}
li {
padding: 0.2em;
}
table {
width: 100%;
border-collapse: collapse;
}
th,td {
padding: 0.25em;
border-spacing: 0px;
border-top: solid 1px #ccc;
border-bottom: solid 1px #ccc;
}
th {
background-color: #eee;
}
footer {
color: #777;
margin: 2em;
font-size: 14px;
text-align: center;
}
</style>
</head>
<body>
<div class="main">
<h1>Dillo User Manual</h1>
<p>Welcome to the user manual of the Dillo browser. The manual is divided into
<em>sections</em> but is written in a <em>single page</em> to allow search by
keywords. Generated for version __VERSION__.</p>
<details><!-- Not supported in Dillo, but decays ok -->
<summary class="toc">Table of contents:</summary>
<ul class="toc">
<li><a href="#introduction">Introduction</a></li>
<li><a href="#reading">Reading</a>
<ul>
<li><a href="#basics">Basics</a></li>
<li><a href="#scrolling">Scrolling</a></li>
<li><a href="#find-text">Find text</a></li>
<li><a href="#copy-and-paste">Copy and paste</a></li>
<li><a href="#zoom">Zoom</a></li>
</ul>
</li>
<li><a href="#navigation">Navigation</a>
<ul>
<li><a href="#hyperlinks">Hyperlinks</a></li>
<li><a href="#history">History</a></li>
<li><a href="#location-bar">Location bar</a></li>
<li><a href="#web-search">Web search</a></li>
<li><a href="#tabs">Tabs</a></li>
<li><a href="#bookmarks">Bookmarks</a></li>
</ul>
</li>
<li><a href="#privacy-and-network">Privacy and Network</a>
<ul>
<li><a href="#https">HTTPS</a></li>
<li><a href="#cookies">Cookies</a></li>
<li><a href="#proxy">Proxy</a></li>
<li><a href="#ad-blocking">Ad-blocking</a></li>
<li><a href="#downloads">Downloads</a></li>
<li><a href="#images-off-mode">Images-off mode</a></li>
</ul>
<li><a href="#configuration">Configuration</a>
<ul>
<li><a href="#dillorc">Dillorc</a></li>
<li><a href="#cookiesrc">Cookiesrc</a></li>
<li><a href="#domainrc">Domainrc</a></li>
<li><a href="#style-css">Style.css</a></li>
<li><a href="#dpidrc">Dpidrc</a></li>
<li><a href="#bm-txt">bm.txt</a></li>
<li><a href="#keysrc">Keysrc</a></li>
</ul>
</li>
<li><a href="#advanced-usage">Advanced usage</a>
<ul>
<li><a href="#plugins">Plugins</a></li>
<li><a href="#bug-meter">Bug meter</a></li>
<li><a href="#keyboard-shortcuts">Keyboard shortcuts</a></li>
</ul>
</li>
</ul>
</details>
<h2 id="introduction">Introduction</h2>
<p>Dillo is a web browser designed to be fast, use few resources and support
slow and unreliable networks on resource-constrained machines. It can load local
and remote files via HTTP, HTTPS and FTP. Other protocols like Gemini, Gopher,
IPFS and others are available as
<a href="#plugins">plugins</a>.</p>
<p>Dillo supports a subset of HTML 4.01 and CSS 2.1 but it <b>doesn't support
JavaScript</b> and only implements some elements of HTML 5 and CSS 3. It also
renders plain text documents and images in PNG, JPG, GIF, SVG and WebP
formats.</p>
<h2 id="reading">Reading</h2>
<p>In this section we cover the basics to read a web page, scrolling and finding
or copying text.</p>
<h3 id="basics">Basics</h3>
<p>
The graphical interface is designed to be used with a mouse or pointing
device. At the top of the browser window you have the location bar and the main
buttons to control the browser. You can leave the mouse for a brief moment over
any part of the menu to show a tooltip with more information.
<p>
Dillo has <em>context sensitive menus</em>, which are opened with the right mouse
button, available on pages, links, images, forms, the Back and Forward buttons,
and the bug meter. They show different actions to be performed specific to the
element. For example, to save a page you can right-click on the page and select
"Save page as...". Or to copy a link URL you can right-click on a link and
select "Copy link location".
<p>
Dillo can hide all panels and use the whole window area to display the page. To
switch between modes use the ESC key. You can also choose the control panel size
by going to the Tools button and selecting a different one under "Panel size".
<p>
You can open this manual from Dillo by clicking on the top right "?" button. It
doesn't require a network connection.
<h3 id="scrolling">Scrolling</h3>
<p>There are several methods to move or scroll the view of the current page.</p>
<ul>
<li>Using the <em>keyboard</em>:
<ul>
<li>Use the <code>Home</code> or <code>End</code> keys to jump to the top
or to the end of a page.</li>
<li>Use <code>PgUp</code> or <code>PgDn</code> to advance a whole
<em>page</em> (window size) minus one <em>step</em>.</li>
<li>Use the arrow keys to move the view a <em>step</em> in that
direction.</li>
<li>You can also use the <code>Space</code> key as <code>PgDn</code> and
<code>b</code> as PgUp.</li>
</ul>
</li>
<li>Using the <em>mouse</em> or <em>pointing device</em>:
<ul>
<li>Rotate the mouse wheel to move one <em>step</em> in that direction.
Hold Shift to move one <em>page</em> instead.
<li>Keep the middle button (or mouse wheel button) pressed while
dragging to scroll the page precisely.
</ul>
</li>
<li>Using the <em>scrollbar</em>:
<ul>
<li>Drag the thumb in the scrollbar on the side up or down to scroll the
page.
<li>Left-click or right-click on the scrollbar above or below the thumb to
move one <em>page</em> in that direction.
<li>Middle-click on the scrollbar above or below the thumb to jump to that
specific position of the page.
<li>Click the scrollbar arrows to move the view one <em>step</em> in
that direction.
<li>Rotate the mouse wheel over the vertical scrollbar to move one
<em>page</em> in that direction.
</ul>
</li>
</ul>
<p>You can control the <b>size of a <em>step</em></b> by setting the
<code>scroll_step</code> option in the <a href="#dillorc">dillorc</a>
configuration file. By default it will scroll 100 pixels per step. The vertical
scrollbar can be positioned on the left side setting the
<code>scrollbar_on_left</code> option to <code>YES</code>, by default it is on
the right side.</p>
The vertical scrollbar has another mode of operation to navigate full
<em>pages</em> that can be enabled by setting the
<code>scrollbar_page_mode</code> option to <code>YES</code> or temporarily by
holding the Shift key. When this mode is active, left-clicking anywhere on the
scrollbar will scroll down one page and right-clicking will scroll up one page.
Middle clicking on the scrollbar will move the thumb to that position, which can
also be dragged. The Shift key can also be used to temporarily disable this
mode if it was enabled in the configuration.
<h3 id="find-text">Find text</h3>
<p>
To find text in a document right-click to open the <em>Page menu</em> and select
<em>Find text</em> or press <code>Ctrl+F</code> on the keyboard. Then type the
substring that you want to find and click Next (or the <code>Enter</code> key).
</p>
Dillo will scroll the page and highlight found text starting from the top. To
find a word (not a substring) use spaces around it to limit the search to
matching words only.
<p>
To close the find bar you can click on the red X on the bottom right or press
ESC.
<h3 id="copy-and-paste">Copy and paste</h3>
<p>
To copy some text just hold down the left mouse button and move to select the
area to copy. To paste, go to the target application and press the middle mouse
button.
<p>
If you want to select more than one screen, hold the mouse left button down and
scroll with PgUp, PgDn or the arrow keys.
<P>
If you want to paste an URL into Dillo, do it on the "clear-URL"
button (the "X" next to the <a href="#location-bar">location bar</a>).
<p>
<h3 id="zoom">Zoom</h3>
<p>
You can increase or decrease the size of the elements of a page by changing the
zoom factor. Use <code>Ctrl +</code> to increase the size, <code>Ctrl -</code>
to decrease it and <code>Ctrl 0</code> to reset the value to 100%.
<p>
The initial zoom factor is specified by the <code>zoom_factor</code> option in
the <a href="#dillorc">dillorc</a> configuration file. When a new tab or window
is opened, the current zoom factor value is inherited.
<h2 id="navigation">Navigation</h2>
This section focuses on how to navigate to other pages by following hyperlinks,
using bookmarks, typing or pasting a new URL or using the history.
<h3 id="hyperlinks">Hyperlinks</h3>
<p>Hyperlinks (or just links) allow you to navigate to other pages by clicking
them. They change the cursor to a hand to indicate that an element can be
clicked with the left button:
<p style="text-align: center">
<img
style="border: solid 1px #ddd;"
alt="hand shaped cursor"
src="data:image/gif;base64,R0lGODlhGAAYAPYAAAAAAAEBAQICAgQEBAcHBx4eHiwsLC8vLzo6Oj09PVZWVldXV1xcXF1dXWlpaWxsbG5uboODg52dnZ6enqCgoKKioqWlpaampqioqKqqqq+vr7S0tLW1tbe3t7u7u7y8vL6+vr+/v8HBwcTExMfHx8jIyMzMzNLS0tPT09jY2Nra2tvb29zc3N3d3d7e3t/f3+Dg4OLi4uPj4+Tk5OXl5efn5+jo6Onp6erq6uvr6+zs7O7u7u/v7/Hx8fLy8vT09Pb29vf39/j4+Pn5+fr6+vv7+/z8/P39/f7+/v///wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH5BAAAAAAALAAAAAAYABgAAAfMgEmCg4SFhoeFQUGIjINEIDQ0IESNiDoxCQg0OZWHMUkAAEkunYakoUkqpYWnoidBRIqrrUmRHJEePYuNtJlJmTqRk4y0qKiZm4ZEy6qhLsaiqKSOJioqHCNEodsNzqiqgz0iDgYSOdrb6duphDkgD6EGEer07IM6GTkL9Pyi4IJEYlCg0a1fOnuOXJjbZxAAASLTCEFc2NCAiogSFdJ4QIBfAREDEUGkMMyQwBK7DhGhMUKDhZcwM3A40aNTEB05curMoYPSqp9ADQUCADs=">
<p>
Links to other pages usually appear in
<span style="color:blue; text-decoration: underline">blue and underlined</span>
by default and once they are visited they change to
<span style="color:purple; text-decoration: underline">purple</span>.
However, this is not always the case, so using the mouse cursor shape is the
best indicator of a
<em><a style="color:black; text-decoration: none" href="#">link</a></em>.
<p>
When you follow a link by clicking on it, the previous pages will be remembered
in case you want to go back to them. Use the Back and Forward buttons on the top
bar to navigate among history pages.
<p>
<h3 id="history">History</h3>
<p>
When you follow several links across different web pages, they are remembered in
case you can to go back. Use the "Back" and "Forward" buttons of the panel to go
to the previous or next page.
<p>
You can also right-click on the Back or Forward buttons to open a menu with the
list of previous or next pages available, so you can jump directly to them. Use
the left button to open them in the current tab or the middle button to open
them in a new tab.
<p>
The <code>,</code> (comma) and <code>.</code> (dot) keys can be used to jump
backwards or forward (mnemonic: those keys are usually labeled "&lt;" and
"&gt;").
<h3 id="location-bar">Location bar</h3>
<p>
The location bar on the top displays the URL of the current page loaded and can
be used to access other pages by typing the new URL or by pasting it and
pressing Enter.
<p>
The red <b style="color: darkred">x</b>
on the left clears the location bar content when left clicked. Then you can type
or paste a new address from the clipboard by middle clicking on the location
bar.
As these two actions are usually performed together, you can simply middle-click
on the
<b style="color: darkred">x</b>
button to go to the URL in the clipboard.
<p>
You can also select the location bar content from the keyboard by pressing
<code>Ctrl+L</code>.
<h3 id="web-search">Web search</h3>
<p>
Several search engines are available by pressing the magnifying glass icon next
to the location bar or by pressing <code>Ctrl+S</code>. The search engines are
configured in <a href="#dillorc">Dillorc</a> with the option
<code>search_url</code>. Use it multiple times to define multiple search
engines.
<p>
The location bar can also be used to search the web by using a special prefix
for each search engine. For example, the following line:<br>
<pre>
search_url="dd DuckDuckGo http://duckduckgo.com/lite/?kp=-1&amp;kd=-1&amp;q=%s"
</pre>
<p>
Defines the "DuckDuckGo" search engine with the prefix "<code>dd</code>", so you
can type in the location bar "<code>dd dillo browser</code>" to search with
DuckDuckGo for the keywords "dillo browser".
<h3 id="tabs">Tabs</h3>
<p>
Dillo can open different web pages into tabs. To open a link in a new tab click
the middle button instead of the left button. The same applies to buttons to
submit a form. A new tab can also be opened from the <code>File</code> menu or
using the shortcut <code>Ctrl+T</code>.
<p>
Press <code>Shift</code> while middle-clicking a link to automatically focus the
new tab. If you want this behaviour to be the default, set the following option
in the <a href="#dillorc">dillorc</a> configuration file:
<pre>
focus_new_tab=YES
</pre>
If this option is set, pressing <code>Shift</code> middle-clicking will instead
avoid focusing the new tab.
<p>
It is also possible to have the middle-click open a window instead of a tab. For
this set the option:
<pre>
middle_click_opens_new_tab=NO
</pre>
<p>
To close a tab press the X button on the top right (only visible with multiple
tabs). You can also close the tab by clicking with the right button on the tab
label. The middle button can be used instead by setting the option:
<pre>
right_click_closes_tab=NO
</pre>
<p>
Use the <code>new_tab_page</code> option to control which page is loaded in a
newly opened tab, by default is an empty page. To open the
bookmarks page set the following line in <a href="#dillorc">dillorc</a>:
<pre>
new_tab_page="dpi:/bm/"
</pre>
<h3 id="bookmarks">Bookmarks</h3>
<p>
Dillo can save URLs of web pages as bookmarks so you can open them later. To
bookmark the current page, open the <em>Page menu</em> by right-clicking on the
page and select "Bookmark this page" (it also works over a link).
<p>
To see or edit the bookmarks, click on the Bookmarks button on the panel or
press <code>Ctrl+B</code>.
<p>
Bookmarks are handled by a built-in Dillo plugin named <code>bm</code> (read more
about plugins in the <a href="#plugins">Plugins section</a>) and are
synchronized across all instances of the browser. The list of bookmarks can also
be shown by opening the URL <a href="dpi:/bm/"><code>dpi:/bm/</code></a>.
<p>
The bookmarks are stored in a simple plain text file named <code>bm.txt</code>
(see the details of this file in the <a href="#bm-txt">bm.txt section</a>). You
can edit the file directly to change the bookmarks.
<h2 id="privacy-and-network">Privacy and network</h2>
<p>The default privacy policy in Dillo attempts to keep the user safe from
tracking, <em>even if this policy breaks a lot of website functionality</em>. In
this section you can add exceptions or relax the configuration at your own risk.
<h3 id="https">HTTPS</h3>
<p>Dillo has support for
<a href="https://en.wikipedia.org/wiki/HTTPS">HTTPS</a>,
allowing secure connections to remote websites. Use the protocol
"<code>https:</code>" to specify the use of HTTPS. When a problem is encountered
with the remote certificate, a warning dialog explains the details and allows
you to choose what to do: continue loading the website or cancel.
<p>
By default Dillo looks for a TLS library at build time, which can be OpenSSL or
mbedTLS. You can see which library is being used by looking at the console when
it starts:
<pre>
$ dillo
dillo_dns_init: Here we go! (threaded)
TLS library: <u>OpenSSL 3.2.1 30 Jan 2024</u>
...
</pre>
<h3 id="cookies">Cookies</h3>
<p>
Due to privacy concerns, <b>cookies are disabled by default</b> unless
explicitly enabled in the configuration.
<p>
Support for cookies is implemented using a built-in plugin that shares them
between several instances of Dillo and follows the
<a href="http://tools.ietf.org/html/rfc6265">RFC 6265</a>
specification. Current cookie limits are 20 per domain and
1200 in total.
<p>Cookies are configured by the <code>cookiesrc</code> configuration file. See
the <a href="#cookiesrc">Cookiesrc section</a> to see how to enable cookies for
some domains or accept them by default.</p>
<h3 id="proxy">Proxy</h3>
<p>Dillo can use a HTTP proxy by setting the environment variable
<code>http_proxy</code> or the
<a href="#dillorc">configuration option</a> with the same name. All HTTP and
HTTPS traffic will be sent through the proxy.
<p><b>Note</b>: Plugins may not implement proxy support.
</p>
<h3 id="ad-blocking">Ad blocking</h3>
<p>Dillo has the ability to block content when loading a page based on the domain
the embedded content is being loaded from. You can control how Dillo handles
automatic requests for resources (like images and style sheets) that aren't at
the same domain as the original page.</p>
<p>See the <a href="#domainrc">domainrc</a> configuration file to find out how to
specify which domains are blocked or allowed.</p>
<h3 id="downloads">Downloads</h3>
<p>Downloads are made using
<a href="http://www.gnu.org/software/wget/">wget</a>
with a <a href="http://www.fltk.org">FLTK</a>-based GUI wrapper, through
the <a href="#plugins">Dillo plugin (dpi) framework</a>.
If you close the browser window, downloads will continue.</p>
<h3 id="images-off-mode">Images-off mode</h3>
<p>
You can browse without images now:
<ul>
<li>There is an option in the Tools menu to disable automatic image loading.
An image's alt text (or <code>[IMG]</code> placeholder) will appear in the
page.
<li>If you want to load an individual image, left click on its text.
<li>You can set "no images" as the default mode in dillorc.
</ul>
<h2 id="configuration">Configuration</h2>
<p>Dillo has <b>several configuration files</b> that control the behavior of the
browser. Each configuration file is searched <em>first</em> in the
<a href="file:~/.dillo"><code>~/.dillo</code></a> directory and,
<em>if not found</em>, in the default system configuration directory (typically
<code>/etc/dillo</code>).</p>
<p>Most configuration files can include comments by <em>starting</em> a line
with the "<code>#</code>" symbol. Their specific syntax is described in the
following sections.</p>
<h3 id="dillorc">Dillorc</h3>
<p>The main configuration of Dillo is controlled by the
<a href="file:~/.dillo/dillorc"><code>dillorc</code></a> file.
The list of all available options can be found in the system
configuration file, typically
<a href="file:/etc/dillo/dillorc"><code>/etc/dillo/dillorc</code></a>. You may
want to copy it into
<a href="file:~/.dillo/dillorc"><code>~/.dillo/dillorc</code></a>
and edit it to suit your needs.</p>
The file is commented to describe what each option does. The default value for
each option as well as other interesting values are also available as comments:
<ul>
<li>"<code>#option=...</code>" shows the built-in default.</li>
<li>"<code># option=...</code>" is an additional example.</li>
<li>"<code>option=...</code>" overrides the built-in value.</li>
</ul>
<p>The <code>search_url</code> option can be specified multiple times:
<pre>
search_url="dd DuckDuckGo (https) https://duckduckgo.com/lite/?kp=-1&amp;q=%s"
search_url="Wikipedia http://www.wikipedia.org/w/index.php?search=%s&amp;go=Go"
</pre>
<h3 id="cookiesrc">Cookiesrc</h3>
<p>Cookies are configured in the
<a href="file:~/.dillo/cookiesrc"><code>~/.dillo/cookiesrc</code></a>
file by using rules, with one rule per line. The rule syntax is very simple,
first specify the <em>host</em> and then the <em>action</em>, separated by
white spaces. Comment lines start with <code>#</code> as the first character and
the whole line is ignored.
<p>
The host can be in the form <code>example.com</code> to match only that
domain or <code>.example.com</code> to match all subdomains of example.com. The
special word <code>DEFAULT</code> specifies the default policy, when no other
match occurs.
<p>
The action can be: <code>DENY</code> to ignore cookies,
<code>ACCEPT_SESSION</code> to only accept session cookies but don't save them
to disk and <code>ACCEPT</code> to accept and store cookies in disk (this will
allow sites to track you over time).
<p>
When the cookies plugin exits, only the accepted cookies by <code>ACCEPT</code>
are saved to
<a href="file:~/.dillo/cookies.txt"><code>~/.dillo/cookies.txt</code></a>,
and ACCEPT_SESSION cookies are
forgotten. The cookies plugin normally exits after a period of inactivity, but
you can force it to exit with the command <code>dpidc stop</code>.
<p>
Here is an example <code>cookiesrc</code> file:
<pre>
# host action
DEFAULT DENY
fltk.org ACCEPT
.example.com ACCEPT_SESSION
</pre>
<p> Which is parsed as follows:</p>
<ul>
<li>Line 1: Comment line begins with <code>#</code>.</li>
<li>Line 2: Deny all cookies from all domains not otherwise specified (this is
the default).
<li>Line 3: Accept all cookies from fltk.org, and save them to disk when the
cookie plugin exits.</li>
<li>Line 4: Accept all cookies from all subdomains of example.com, but do not
save them when the cookie plugin exits.</li>
</ul>
<p>
Dillo is especially concerned about privacy and security issues. Our advice is
to <em>avoid cookies whenever possible</em> and at most set ACCEPT_SESSION to
<em>specific trusted sites</em>.
<h3 id="domainrc">Domainrc</h3>
<p>With the
<code>~/.dillo/domainrc</code>
file, you can control how Dillo handles automatic requests for resources
(like images and style sheets) that aren't at the same domain as the
original page.
<p>
The file contains one rule per line. Comments are specified by starting the line
with the <code>#</code> symbol. The default rule is either
<code>default accept</code> or <code>default deny</code> and will cause Dillo
to, respectively, accept all requests by default or deny all requests by
default.
<p>
Depending on the default rule, the next rules behave as exceptions by denying
specific connections or allowing specific connections, respectively.
<p>
Exceptions to the default rule are written in the format
<code>source destination</code>, and match request from the source domain to the
destination domain. The source and destination domains can be specified in three
ways:
<ul>
<li>"<code>*</code>" to match any domain</li>
<li>"<code>example.com</code>" to match the specific host example.com</li>
<li>"<code>.example.com</code>" to match example.com and any of its
subdomains</li>
</ul>
<p>
Here is an example:
<pre>
# Accept all requests by default
default accept
# But block some ad-sites and trackers from any domain
* .doubleclick.net
* .googleadservices.com
* .quantserve.com
</pre>
<h3 id="style-css">Style.css</h3>
<p>Custom
<a href="https://en.wikipedia.org/wiki/CSS">CSS styles</a> can be placed in the
<a href="file:~/.dillo/style.css"><code>~/.dillo/style.css</code></a>
file to set default web page styles. To override page styles add the
"<code>!important</code>" flag.
<h3 id="dpidrc">Dpidrc</h3>
<p>The configuration for <a href="#plugins">plugins</a> is placed in the
<a href="file:~/.dillo/dpidrc"><code>~/.dillo/dpidrc</code></a> file.</p>
<p>Plugins are searched in the <code>~/.dillo/dpi/</code> directory first, and
then in the system directory if not found. The <code>dpi_dir</code> option
sets the system <code>dpi</code> directory and must be specified once. Here is
an example:</p>
<pre>
dpi_dir=/usr/lib/dillo/dpi
</pre>
<p>By default, plugins will receive requests at the URL
<code>dpi:/<em>name</em>/</code>, for example bookmarks are available at
<a href="dpi:/bm/"><code>dpi:/bm/</code></a>.
<p>Plugins can also be associated with a protocol (like "<code>file:</code>").
When a request is made using the given protocol the associated plugin is used to
process the request. The rest of the file assigns a plugin to a protocol, one
per line. The syntax is
<code>proto.<em>name</em>=<em>path-to-plugin.dpi</em></code>. The path of the
plugin is relative to the <code>dpi</code> directory. Here are protocols
used by the built-in plugins:</p>
<pre>
proto.file=file/file.dpi
proto.ftp=ftp/ftp.filter.dpi
proto.data=datauri/datauri.filter.dpi
</pre>
<p>Here (
<img alt="red dot"
src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12P4//8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg==">
) is an example image using the <code>data:</code> protocol used to define a PNG
image inline in a HTML document.
</p>
<h3 id="bm-txt">bm.txt</h3>
<p>Web page URLs can be saved as bookmarks so they can be accessed in the future.
Bookmarks are stored in a plain text file located at
<a href="file:~/.dillo/bm.txt"><code>~/.dillo/bm.txt</code></a>. The file can be
modified by using the
<a href="dpi:/bm/">Book button</a>
in the toolbar and the context menu on a page or link, but they can also be
edited manually. Bookmarks are organized into sections and are given a
description (usually the page title).</p>
<p>The syntax of the <code>bm.txt</code> file is described as follows:</p>
<p>First, the sections are defined at the beginning, prefixed with a
<code>:s<em>N</em>:</code> identifier, along with the description of the
section:</p>
<pre>
:s0: News
:s1: Blog
</pre>
<p>Then every bookmark is added in a single line, by writing the section
identifier, the URL and a description:</p>
<pre>
s0 https://slashdot.org Slashdot
s0 https://news.ycombinator.com/ Hacker News
s1 https://100r.co/site/log.html 100 Rabbits Log
</pre>
<p>The <code>bm.txt</code> file is designed to be <em>easily</em> read by humans
and is suitable to be kept updated among machines by using a version control
tool.
<h3 id="keysrc">Keysrc</h3>
<p>The mapping of keys in Dillo is controlled by the
<a href="file:~/.dillo/keysrc"><code>~/.dillo/keysrc</code></a> file.
The format is "<code>key = action</code>" or
"<code>&lt;modifier&gt;key = action</code>". Lines that begin with a "#" are comments.
The commented-out bindings below show the defaults built into Dillo.
<ul>
<li>Modifiers recognized: Shift, Ctrl, Alt, Meta (on Mac OS X use "Meta" for
Command).
<li>Key names recognized: Backspace, Delete, Down, End, Esc, F1 through
F12, Home, Insert, Left, Menu, PageDown, PageUp, Print, Return, Right, Space,
Tab, Up.
<li>Multimedia keys: Back, Favorites, Forward, HomePage, Mail, MediaNext,
MediaPlay, MediaPrev, MediaStop, Refresh, Search, Sleep, Stop, VolumeDown,
VolumeMute, VolumeUp.
</ul>
<p>If Dillo is running under X11, keys whose names are not recognized can be
specified using their keysym value in hexadecimal. Use <code>xev</code> to get
the keysym, for example:
<pre>
0x1008ff27 = forward
</pre>
<p>The action "nop" (no operation) can be used to remove a binding.
<p>Example file:</p>
<pre>
# "close-all" closes all tabs/windows and exits.
&lt;ctrl&gt;q = close-all
# "left-tab" and "right-tab" switch to the left/right of the current tab.
&lt;ctrl&gt;&lt;shift&gt;tab = left-tab
# Use HJKL to move around
k = line-up
j = line-down
h = left
l = right
</pre>
<h2 id="advanced-usage">Advanced usage</h2>
These sections focus on advanced topics and are recommended for experienced
users of Dillo.
<h3 id="plugins">Plugins</h3>
<p>The functionality of Dillo can be extended by using plugins, which can
translate other formats to HTML, implement new protocols or provide a custom
service.</p>
<p>Plugins can be written in <em>any programming language</em> and they
interact with the browser using the
<a href="https://dillo-browser.github.io/old/dpi1.html">DPI protocol</a>. A
<a href="https://dillo-browser.github.io/#plugins">list of plugins</a>
is available on the Dillo website. Some plugins are just a
<a href="https://raw.githubusercontent.com/dillo-browser/dillo-plugin-man/master/man.filter.dpi">few
lines of shell script</a>, so you are encouraged to read them to learn how to
write your own plugins. Plugins are searched by looking for files that end with
the <code>.dpi</code> extension (or <code>.dpi.exe</code> in Windows) in
<code>~/.dillo/dpi</code> and <code>dpi_dir</code> (see the
<a href="#dpidrc">Dpidrc</a> configuration section).</p>
<p>There are two types of plugins: <em>filters</em> and <em>servers</em>.
Filters are executed for each request like a
<a href="https://en.wikipedia.org/wiki/Pipeline_(Unix)">UNIX pipe</a>,
they read from the
standard input and write to the standard output. The name of filter plugin
programs must end in <code>.filter.dpi</code>. On the other hand, servers listen
on a socket for new requests. They can process several requests at the same
time, preventing the overhead of spawning multiple processes and they can easily
share information among requests.</p>
<p>You can install plugins from any third party, but you should always
review the source before running code written by others. To install a new
plugin, copy the files to <code>~/.dillo/dpi/<em>name</em>/</code> and
then associate the <em>name</em> protocol to the program that must run in
the
<a href="file:~/.dillo/dpidrc"><code>~/.dillo/dpidrc</code></a> file.
Plugins may have other software dependencies required for it to work.</p>
<p>Here is an example of how to manually install the
<a href="https://github.com/dillo-browser/dillo-plugin-gemini">Gemini protocol plugin</a>
(it comes with a Makefile that automates the process, so this is not necessary),
which is a filter plugin written in shell script:</p>
<pre>
$ mkdir -p ~/.dillo/dpi/gemini
$ cp gemini.filter.dpi ~/.dillo/dpi/gemini/
$ chmod +x ~/.dillo/dpi/gemini/gemini.filter.dpi
$ echo "proto.gemini=gemini/gemini.filter.dpi" >> ~/.dillo/dpidrc
$ dpidc stop
</pre>
<p>Now, when a request is made to an URL that begins with the
<code>gemini:</code> protocol, it will be processed by the
<code>gemini/gemini.filter.dpi</code> program, and the output will be displayed
by Dillo.</p>
<h3 id="bug-meter">Bug Meter</h3>
<p>
Dillo includes a
<a href='https://dillo-browser.github.io/old/help/bug_meter.html'>bug meter</a>
which shows the number of detected bugs inside the page. The bugs are caught at
parsing time, so the error messages also show the line where they occur and
provide a hint of what was expected instead.
<p>
The primary purpose of the bug meter is to help webmasters and page authors to
polish the contents of their sites with a view to making them
compliant with HTML standards.
<p>
The bug meter is located at the lower right corner of Dillo. Use the left-click
to see the messages, right-click for a menu to open other HTML validators.
<h3 id="keyboard-shortcuts">Keyboard shortcuts</h3>
<p>
Most actions can be issued by using a keyboard shortcut. The key bindings can be
changed in the
<code><a href="file:~/.dillo/keysrc">~/.dillo/keysrc</a></code> file (see the
<a href="#keysrc">keysrc</a> section).
The list of default bindings is given in the following table.
<p>
<table>
<tr><th>Shortcut <th>Mnemonic <th>Function
<tr><td>Ctrl-L <td>Location <td>Enter a new URL
<tr><td>Ctrl-F <td>Find <td>Find text
<tr><td>Ctrl-S <td>Search <td>Search the web
<tr><td>Ctrl-R <td>Reload <td>Reload current page
<tr><td>Ctrl-N <td>New <td>New browser window
<tr><td>Ctrl-T <td>Tab <td>New tab
<tr><td>Ctrl-W <td>Window <td>Quit tab/window
<tr><td>Ctrl-O <td>Open <td>Open file
<tr><td>Ctrl-U <td> <td>View source
<tr><td>Ctrl-B <td>Bookmarks <td>View bookmarks
<tr><td>Ctrl-Q <td>Quit <td>Quit dillo
<tr><td>Ctrl-+ or Ctrl-= <td>Bigger <td>Zoom in
<tr><td>Ctrl-- <td>Smaller <td>Zoom out
<tr><td>Ctrl-0 <td>100% <td>Reset zoom to 100%
<tr><td>Back or "<b>,</b>" <td>&lt; <td>Previous page
<tr><td>Shift-Back or "<b>.</b>" <td>&gt; <td>Next page
<tr><td>Alt-F <td>File <td>File menu
<tr><td>Ctrl-Tab or<br>
Ctrl-PageDown <td>Tab <td>Next tab
<tr><td>Ctrl-Shift-Tab or<br>
Ctrl-PageUp <td>Tab <td>Previous tab
<tr><td>Esc <td>escape <td>Close dialog,
Close findbar,<br>
Hide/show control panels
</table>
<footer>
<p>This manual has been written in HTML <em>by hand</em> using Vim.<br>
If you find any issue, please report it via
<a href="https://github.com/dillo-browser/dillo/issues/new">GitHub</a> or
<a href="mailto:dillo-dev@mailman3.com">email</a>.
</footer>
</div>
</body>
</html>

56
dpi/Makefile.am Normal file
View File

@ -0,0 +1,56 @@
AM_CPPFLAGS = \
-I$(top_srcdir)
bookmarksdir = $(libdir)/dillo/dpi/bookmarks
downloadsdir = $(libdir)/dillo/dpi/downloads
ftpdir = $(libdir)/dillo/dpi/ftp
hellodir = $(libdir)/dillo/dpi/hello
vsourcedir = $(libdir)/dillo/dpi/vsource
filedir = $(libdir)/dillo/dpi/file
cookiesdir = $(libdir)/dillo/dpi/cookies
datauridir = $(libdir)/dillo/dpi/datauri
bookmarks_PROGRAMS = bookmarks.dpi
downloads_PROGRAMS = downloads.dpi
ftp_PROGRAMS = ftp.filter.dpi
hello_PROGRAMS = hello.filter.dpi
vsource_PROGRAMS = vsource.filter.dpi
file_PROGRAMS = file.dpi
cookies_PROGRAMS = cookies.dpi
datauri_PROGRAMS = datauri.filter.dpi
bookmarks_dpi_LDADD = \
$(top_builddir)/dpip/libDpip.a \
$(top_builddir)/dlib/libDlib.a
downloads_dpi_LDADD = @LIBFLTK_LIBS@ \
$(top_builddir)/dpip/libDpip.a \
$(top_builddir)/dlib/libDlib.a
ftp_filter_dpi_LDADD = \
$(top_builddir)/dpip/libDpip.a \
$(top_builddir)/dlib/libDlib.a
hello_filter_dpi_LDADD = \
$(top_builddir)/dpip/libDpip.a \
$(top_builddir)/dlib/libDlib.a
vsource_filter_dpi_LDADD = \
$(top_builddir)/dpip/libDpip.a \
$(top_builddir)/dlib/libDlib.a
file_dpi_LDADD = \
$(top_builddir)/dpip/libDpip.a \
$(top_builddir)/dlib/libDlib.a
cookies_dpi_LDADD = \
$(top_builddir)/dpip/libDpip.a \
$(top_builddir)/dlib/libDlib.a
datauri_filter_dpi_LDADD = \
$(top_builddir)/dpip/libDpip.a \
$(top_builddir)/dlib/libDlib.a
downloads_dpi_CXXFLAGS = @LIBFLTK_CXXFLAGS@
bookmarks_dpi_SOURCES = bookmarks.c dpiutil.c dpiutil.h
downloads_dpi_SOURCES = downloads.cc dpiutil.c dpiutil.h
ftp_filter_dpi_SOURCES = ftp.c dpiutil.c dpiutil.h
hello_filter_dpi_SOURCES = hello.c dpiutil.c dpiutil.h
vsource_filter_dpi_SOURCES = vsource.c dpiutil.c dpiutil.h
file_dpi_SOURCES = file.c dpiutil.c dpiutil.h
cookies_dpi_SOURCES = cookies.c dpiutil.c dpiutil.h
datauri_filter_dpi_SOURCES = datauri.c dpiutil.c dpiutil.h

1680
dpi/bookmarks.c Normal file

File diff suppressed because it is too large Load Diff

1707
dpi/cookies.c Normal file

File diff suppressed because it is too large Load Diff

351
dpi/datauri.c Normal file
View File

@ -0,0 +1,351 @@
/*
* File: datauri.c
*
* Copyright (C) 2006-2007 Jorge Arellano Cid <jcid@dillo.org>
*
* Filter dpi for the "data:" URI scheme (RFC 2397).
*
* 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 3 of the License, or
* (at your option) any later version.
*/
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <errno.h>
#include "../dpip/dpip.h"
#include "dpiutil.h"
#include "../src/misc.h"
/*
* Debugging macros
*/
#define SILENT 1
#define _MSG(...)
#if SILENT
#define MSG(...)
#else
#define MSG(...) fprintf(stderr, "[datauri dpi]: " __VA_ARGS__)
#endif
/*
* Global variables
*/
static Dsh *sh = NULL;
static void b64strip_illegal_chars(unsigned char* str)
{
unsigned char *p, *s = str;
MSG("len=%d{%s}\n", strlen((char*)str), str);
for (p = s; (*p = *s); ++s) {
if (d_isascii(*p) && (isalnum(*p) || strchr("+/=", *p)))
++p;
}
MSG("len=%d{%s}\n", strlen((char *)str), str);
}
static int b64decode(unsigned char* str)
{
unsigned char *cur, *start;
int d, dlast, phase;
unsigned char c;
static int table[256] = {
-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, /* 00-0F */
-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, /* 10-1F */
-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,62,-1,-1,-1,63, /* 20-2F */
52,53,54,55,56,57,58,59,60,61,-1,-1,-1,-1,-1,-1, /* 30-3F */
-1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14, /* 40-4F */
15,16,17,18,19,20,21,22,23,24,25,-1,-1,-1,-1,-1, /* 50-5F */
-1,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40, /* 60-6F */
41,42,43,44,45,46,47,48,49,50,51,-1,-1,-1,-1,-1, /* 70-7F */
-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, /* 80-8F */
-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, /* 90-9F */
-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, /* A0-AF */
-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, /* B0-BF */
-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, /* C0-CF */
-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, /* D0-DF */
-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, /* E0-EF */
-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1 /* F0-FF */
};
d = dlast = phase = 0;
start = str;
for (cur = str; *cur != '\0'; ++cur ) {
// jer: treat line endings as physical breaks.
//if (*cur == '\n' || *cur == '\r'){phase = dlast = 0; continue;}
d = table[(int)*cur];
if (d != -1) {
switch(phase) {
case 0:
++phase;
break;
case 1:
c = ((dlast << 2) | ((d & 0x30) >> 4));
*str++ = c;
++phase;
break;
case 2:
c = (((dlast & 0xf) << 4) | ((d & 0x3c) >> 2));
*str++ = c;
++phase;
break;
case 3:
c = (((dlast & 0x03 ) << 6) | d);
*str++ = c;
phase = 0;
break;
}
dlast = d;
}
}
*str = '\0';
return str - start;
}
/* Modified from src/url.c --------------------------------------------------*/
/*
* Given an hex octet (e.g., e3, 2F, 20), return the corresponding
* character if the octet is valid, and -1 otherwise
*/
static int Url_decode_hex_octet(const char *s)
{
int hex_value;
char *tail, hex[3];
if (s && (hex[0] = s[0]) && (hex[1] = s[1])) {
hex[2] = 0;
hex_value = strtol(hex, &tail, 16);
if (tail - hex == 2)
return hex_value;
}
return -1;
}
/*
* Parse possible hexadecimal octets in the URI path.
* Returns a new allocated string.
*/
char *a_Url_decode_hex_str(const char *str, size_t *p_sz)
{
char *new_str, *dest;
int i, val;
if (!str) {
*p_sz = 0;
return NULL;
}
dest = new_str = dNew(char, strlen(str) + 1);
for (i = 0; str[i]; i++) {
*dest++ = (str[i] == '%' && (val = Url_decode_hex_octet(str+i+1)) >= 0) ?
i+=2, val : str[i];
}
*dest = 0;
*p_sz = (size_t)(dest - new_str);
new_str = dRealloc(new_str, sizeof(char) * (dest - new_str + 1));
return new_str;
}
/* end ----------------------------------------------------------------------*/
/*
* Send decoded data to dillo in an HTTP envelope.
*/
static void send_decoded_data(const char *url, const char *mime_type,
unsigned char *data, size_t data_sz)
{
char *d_cmd;
/* Send dpip tag */
d_cmd = a_Dpip_build_cmd("cmd=%s url=%s", "start_send_page", url);
a_Dpip_dsh_write_str(sh, 1, d_cmd);
dFree(d_cmd);
/* Send HTTP header. */
a_Dpip_dsh_write_str(sh, 0, "Content-type: ");
a_Dpip_dsh_write_str(sh, 0, mime_type);
a_Dpip_dsh_write_str(sh, 1, "\n\n");
/* Send message */
a_Dpip_dsh_write(sh, 0, (char *)data, data_sz);
}
static void send_failure_message(const char *url, const char *mime_type,
unsigned char *data, size_t data_sz)
{
char *d_cmd;
char buf[1024];
const char *msg =
"<!DOCTYPE HTML PUBLIC '-//W3C//DTD HTML 4.01 Transitional//EN'>\n"
"<html><body>\n"
"<hr><h1>Datauri dpi</h1><hr>\n"
"<p><b>Can't parse datauri:</b><br>\n";
const char *msg_mime_type="text/html";
/* Send dpip tag */
d_cmd = a_Dpip_build_cmd("cmd=%s url=%s", "start_send_page", url);
a_Dpip_dsh_write_str(sh, 1, d_cmd);
dFree(d_cmd);
/* Send HTTP header. */
a_Dpip_dsh_write_str(sh, 0, "Content-type: ");
a_Dpip_dsh_write_str(sh, 0, msg_mime_type);
a_Dpip_dsh_write_str(sh, 1, "\n\n");
/* Send message */
a_Dpip_dsh_write_str(sh, 0, msg);
/* send some debug info */
snprintf(buf, 1024, "mime_type: %s<br>data size: %d<br>data: %s<br>",
mime_type, (int)data_sz, data);
a_Dpip_dsh_write_str(sh, 0, buf);
/* close page */
a_Dpip_dsh_write_str(sh, 0, "</body></html>");
}
/*
* Get mime type from the data URI.
*/
static char *datauri_get_mime(char *url)
{
char buf[256];
char *mime_type = NULL, *p;
size_t len = 0;
if (dStrnAsciiCasecmp(url, "data:", 5) == 0) {
if ((p = strchr(url, ',')) && p - url < 256) {
url += 5;
len = p - url;
strncpy(buf, url, len);
buf[len] = 0;
/* strip ";base64" */
if (len >= 7 && dStrAsciiCasecmp(buf + len - 7, ";base64") == 0) {
len -= 7;
buf[len] = 0;
}
}
/* that's it, now handle omitted types */
if (len == 0) {
mime_type = dStrdup("text/plain;charset=US-ASCII");
} else if (!dStrnAsciiCasecmp(buf, "charset", 7)) {
mime_type = dStrconcat("text/plain;", buf, NULL);
} else {
mime_type = dStrdup(buf);
}
}
return mime_type;
}
/*
* Return a decoded data string.
*/
static unsigned char *datauri_get_data(char *url, size_t *p_sz)
{
char *p;
int is_base64 = 0;
unsigned char *data = NULL;
if ((p = strchr(url, ',')) && p - url >= 12 && /* "data:;base64" */
dStrnAsciiCasecmp(p - 7, ";base64", 7) == 0) {
is_base64 = 1;
}
if (p) {
++p;
if (is_base64) {
data = (unsigned char *)Unescape_uri_str(p);
b64strip_illegal_chars(data);
*p_sz = (size_t) b64decode(data);
} else {
data = (unsigned char *)a_Url_decode_hex_str(p, p_sz);
}
} else {
data = (unsigned char *)dStrdup("");
*p_sz = 0;
}
return data;
}
/*
*
*/
int main(void)
{
char *dpip_tag = NULL, *cmd = NULL, *url = NULL, *mime_type;
unsigned char *data;
int rc;
size_t data_size = 0;
/* Initialize the SockHandler */
sh = a_Dpip_dsh_new(STDIN_FILENO, STDOUT_FILENO, 8*1024);
rc = chdir("/tmp");
if (rc == -1) {
MSG("paths: error changing directory to /tmp: %s\n",
dStrerror(errno));
}
/* Authenticate our client... */
if (!(dpip_tag = a_Dpip_dsh_read_token(sh, 1)) ||
a_Dpip_check_auth(dpip_tag) < 0) {
MSG("can't authenticate request: %s\n", dStrerror(errno));
a_Dpip_dsh_close(sh);
return 1;
}
dFree(dpip_tag);
/* Read the dpi command from STDIN */
dpip_tag = a_Dpip_dsh_read_token(sh, 1);
MSG("[%s]\n", dpip_tag);
cmd = a_Dpip_get_attr(dpip_tag, "cmd");
url = a_Dpip_get_attr(dpip_tag, "url");
if (!cmd || !url) {
MSG("Error, cmd=%s, url=%s\n", cmd, url);
exit (EXIT_FAILURE);
}
/* Parse the data URI */
mime_type = datauri_get_mime(url);
data = datauri_get_data(url, &data_size);
MSG("mime_type: %s\n", mime_type);
MSG("data_size: %d\n", (int)data_size);
MSG("data: {%s}\n", data);
if (mime_type && data) {
/* good URI */
send_decoded_data(url, mime_type, data, data_size);
} else {
/* malformed URI */
send_failure_message(url, mime_type, data, data_size);
}
dFree(data);
dFree(mime_type);
dFree(url);
dFree(cmd);
dFree(dpip_tag);
/* Finish the SockHandler */
a_Dpip_dsh_close(sh);
a_Dpip_dsh_free(sh);
return 0;
}

1128
dpi/downloads.cc Normal file

File diff suppressed because it is too large Load Diff

168
dpi/dpiutil.c Normal file
View File

@ -0,0 +1,168 @@
/*
* File: dpiutil.c
*
* Copyright 2004-2007 Jorge Arellano Cid <jcid@dillo.org>
*
* 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 3 of the License, or
* (at your option) any later version.
*
*/
#include <unistd.h>
#include <stdio.h>
#include <stdarg.h>
#include <string.h>
#include <ctype.h>
#include <errno.h>
#include <sys/socket.h>
#include "dpiutil.h"
/*
* Debugging macros
*/
#define _MSG(...)
#define MSG(...) printf("[dpiutil.c]: " __VA_ARGS__)
/* Escaping/De-escaping ---------------------------------------------------*/
/*
* Escape URI characters in 'esc_set' as %XX sequences.
* Return value: New escaped string.
*/
char *Escape_uri_str(const char *str, const char *p_esc_set)
{
static const char *esc_set, *hex = "0123456789ABCDEF";
char *p;
Dstr *dstr;
int i;
esc_set = (p_esc_set) ? p_esc_set : "%#:' ";
dstr = dStr_sized_new(64);
for (i = 0; str[i]; ++i) {
if (str[i] <= 0x1F || str[i] == 0x7F || strchr(esc_set, str[i])) {
dStr_append_c(dstr, '%');
dStr_append_c(dstr, hex[(str[i] >> 4) & 15]);
dStr_append_c(dstr, hex[str[i] & 15]);
} else {
dStr_append_c(dstr, str[i]);
}
}
p = dstr->str;
dStr_free(dstr, FALSE);
return p;
}
/*
* Unescape %XX sequences in a string.
* Return value: a new unescaped string
*/
char *Unescape_uri_str(const char *s)
{
char *p, *buf = dStrdup(s);
if (strchr(s, '%')) {
for (p = buf; (*p = *s); ++s, ++p) {
if (*p == '%' && isxdigit(s[1]) && isxdigit(s[2])) {
*p = (isdigit(s[1]) ? (s[1] - '0')
: D_ASCII_TOUPPER(s[1]) - 'A' + 10) * 16;
*p += isdigit(s[2]) ? (s[2] - '0')
: D_ASCII_TOUPPER(s[2]) - 'A' + 10;
s += 2;
}
}
}
return buf;
}
static const char *unsafe_chars = "&<>\"'";
static const char *unsafe_rep[] =
{ "&amp;", "&lt;", "&gt;", "&quot;", "&#39;" };
static const int unsafe_rep_len[] = { 5, 4, 4, 6, 5 };
/*
* Escape unsafe characters as html entities.
* Return value: New escaped string.
*/
char *Escape_html_str(const char *str)
{
int i;
char *p;
Dstr *dstr = dStr_sized_new(64);
for (i = 0; str[i]; ++i) {
if ((p = strchr(unsafe_chars, str[i])))
dStr_append(dstr, unsafe_rep[p - unsafe_chars]);
else
dStr_append_c(dstr, str[i]);
}
p = dstr->str;
dStr_free(dstr, FALSE);
return p;
}
/*
* Unescape a few HTML entities (inverse of Escape_html_str)
* Return value: New unescaped string.
*/
char *Unescape_html_str(const char *str)
{
int i, j, k;
char *u_str = dStrdup(str);
if (!strchr(str, '&'))
return u_str;
for (i = 0, j = 0; str[i]; ++i) {
if (str[i] == '&') {
for (k = 0; k < 5; ++k) {
if (!dStrnAsciiCasecmp(str + i, unsafe_rep[k], unsafe_rep_len[k])) {
i += unsafe_rep_len[k] - 1;
break;
}
}
u_str[j++] = (k < 5) ? unsafe_chars[k] : str[i];
} else {
u_str[j++] = str[i];
}
}
u_str[j] = 0;
return u_str;
}
/*
* Filter '\n', '\r', "%0D" and "%0A" from the authority part of an FTP url.
* This helps to avoid a SMTP relaying hack. This filtering could be done
* only when port == 25, but if the mail server is listening on another
* port it wouldn't work.
* Note: AFAIS this should be done by wget.
*/
char *Filter_smtp_hack(char *url)
{
int i;
char c;
if (strlen(url) > 6) { /* ftp:// */
for (i = 6; (c = url[i]) && c != '/'; ++i) {
if (c == '\n' || c == '\r') {
memmove(url + i, url + i + 1, strlen(url + i));
--i;
} else if (c == '%' && url[i+1] == '0' &&
(D_ASCII_TOLOWER(url[i+2]) == 'a' ||
D_ASCII_TOLOWER(url[i+2]) == 'd')) {
memmove(url + i, url + i + 3, strlen(url + i + 2));
--i;
}
}
}
return url;
}

66
dpi/dpiutil.h Normal file
View File

@ -0,0 +1,66 @@
/*
* File: dpiutil.h
*
* Copyright 2004-2005 Jorge Arellano Cid <jcid@dillo.org>
*
* 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 3 of the License, or
* (at your option) any later version.
*
*/
/*
* This file contains common functions used by dpi programs.
* (i.e. a convenience library).
*/
#ifndef __DPIUTIL_H__
#define __DPIUTIL_H__
#include <stdio.h>
#include "d_size.h"
#include "../dlib/dlib.h"
#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */
/*
* Escape URI characters in 'esc_set' as %XX sequences.
* Return value: New escaped string.
*/
char *Escape_uri_str(const char *str, const char *p_esc_set);
/*
* Unescape %XX sequences in a string.
* Return value: a new unescaped string
*/
char *Unescape_uri_str(const char *str);
/*
* Escape unsafe characters as html entities.
* Return value: New escaped string.
*/
char *Escape_html_str(const char *str);
/*
* Unescape a few HTML entities (inverse of Escape_html_str)
* Return value: New unescaped string.
*/
char *Unescape_html_str(const char *str);
/*
* Filter an SMTP hack with a FTP URI
*/
char *Filter_smtp_hack(char *url);
#ifdef __cplusplus
}
#endif /* __cplusplus */
#endif /* __DPIUTIL_H__ */

1138
dpi/file.c Normal file

File diff suppressed because it is too large Load Diff

354
dpi/ftp.c Normal file
View File

@ -0,0 +1,354 @@
/*
* Dpi for FTP.
*
* This server checks the ftp-URL to be a directory (requires wget).
* If true, it sends back an html representation of it, and if not
* a dpip message (which is caught by dillo who redirects the ftp URL
* to the downloads server).
*
* Feel free to polish!
*
* Copyright 2003-2007 Jorge Arellano Cid <jcid@dillo.org>
*
* 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 3 of the License, or
* (at your option) any later version.
*
*/
/*
* TODO:
* - Send feedback about the FTP login process from wget's stderr.
* i.e. capture our child's stderr, process it, and report back.
* - Handle simultaneous connections.
* If ftp.dpi is implemented with a low level ftp library, it becomes
* possible to keep the connection alive, and thus make browsing of ftp
* directories faster (this avoids one login per page, and forks). Perhaps
* it's not worth, but can be done.
*/
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <signal.h>
#include <sys/wait.h>
#include <errno.h>
#include <sys/time.h>
#include <ctype.h>
#include "../dpip/dpip.h"
#include "dpiutil.h"
#include "d_size.h"
/*
* Debugging macros
* (Set debugging messages to stderr, to see them)
*/
#define _MSG(...)
//#define MSG(...) fprintf(stderr, "[ftp dpi]: " __VA_ARGS__)
#define MSG(...) printf("[ftp dpi]: " __VA_ARGS__)
/*
* Global variables
*/
static Dsh *sh = NULL;
static char **dl_argv = NULL;
/*---------------------------------------------------------------------------*/
/* TODO: could use dStr ADT! */
typedef struct {
const char *str;
int len;
} ContentType_t;
static const ContentType_t MimeTypes[] = {
{ "application/octet-stream", 24 },
{ "text/html", 9 },
{ "text/plain", 10 },
{ "image/gif", 9 },
{ "image/png", 9 },
{ "image/jpeg", 10 },
{ NULL, 0 }
};
/*
* Detects 'Content-Type' from a data stream sample.
*
* It uses the magic(5) logic from file(1). Currently, it
* only checks the few mime types that Dillo supports.
*
* 'Data' is a pointer to the first bytes of the raw data.
*
* Return value: (0 on success, 1 on doubt, 2 on lack of data).
*/
static int a_Misc_get_content_type_from_data2(void *Data, size_t Size,
const char **PT)
{
int st = 1; /* default to "doubt' */
int Type = 0; /* default to "application/octet-stream" */
char *p = Data;
uchar_t ch;
size_t i, non_ascci;
/* HTML try */
for (i = 0; i < Size && dIsspace(p[i]); ++i);
if ((Size - i >= 5 && !dStrnAsciiCasecmp(p+i, "<html", 5)) ||
(Size - i >= 5 && !dStrnAsciiCasecmp(p+i, "<head", 5)) ||
(Size - i >= 6 && !dStrnAsciiCasecmp(p+i, "<title", 6)) ||
(Size - i >= 14 && !dStrnAsciiCasecmp(p+i, "<!doctype html", 14)) ||
/* this line is workaround for FTP through the Squid proxy */
(Size - i >= 17 && !dStrnAsciiCasecmp(p+i, "<!-- HTML listing", 17))) {
Type = 1;
st = 0;
/* Images */
} else if (Size >= 4 && !strncmp(p, "GIF8", 4)) {
Type = 3;
st = 0;
} else if (Size >= 4 && !strncmp(p, "\x89PNG", 4)) {
Type = 4;
st = 0;
} else if (Size >= 2 && !strncmp(p, "\xff\xd8", 2)) {
/* JPEG has the first 2 bytes set to 0xffd8 in BigEndian - looking
* at the character representation should be machine independent. */
Type = 5;
st = 0;
/* Text */
} else {
/* We'll assume "text/plain" if the set of chars above 127 is <= 10%
* of the sample. This helps to catch ASCII, LATIN1 and UTF-8 as text.
* Better heuristics are welcomed! :-) */
non_ascci = 0;
Size = MIN (Size, 256);
for (i = 0; i < Size; i++) {
ch = (uchar_t) p[i];
if ((ch < 32 || ch > 126) && !dIsspace(ch))
++non_ascci;
}
if (Size == 256) {
Type = (non_ascci > Size/10) ? 0 : 2;
st = 0;
} else {
Type = (non_ascci > Size/10) ? 0 : 2;
}
}
*PT = MimeTypes[Type].str;
return st;
}
/*---------------------------------------------------------------------------*/
/*
* Build a shell command using wget for this URL.
*/
static void make_wget_argv(char *url)
{
char *esc_url;
if (dl_argv) {
dFree(dl_argv[3]);
dFree(dl_argv);
}
dl_argv = dNew(char*, 10);
esc_url = Escape_uri_str(url, "'");
/* avoid malicious SMTP relaying with FTP urls */
Filter_smtp_hack(esc_url);
dl_argv[0] = "wget";
dl_argv[1] = "-t1"; /* try once, default is 20 */
dl_argv[2] = "-O-";
dl_argv[3] = esc_url;
dl_argv[4] = NULL;
}
/*
* Fork, exec command, get its output and send via stdout.
* Return: Number of bytes transferred, -1 if file-not_found, -2 if aborted.
*/
static int try_ftp_transfer(char *url)
{
#define MIN_SZ 256
#define READ_SZ 16*1024
ssize_t n;
int nb, has_mime_type, has_html_header, no_such_file, offer_download;
const char *mime_type = "application/octet-stream";
char buf[READ_SZ], *d_cmd;
Dstr *dbuf = dStr_sized_new(READ_SZ);
pid_t ch_pid;
int aborted = 0;
int DataPipe[2];
MSG("try_ftp_transfer: url=%s\n", url);
if (pipe(DataPipe) < 0) {
MSG("pipe, %s\n", dStrerror(errno));
return 0;
}
/* Prepare args for execvp() */
make_wget_argv(url);
/* Start the child process */
if ((ch_pid = fork()) == 0) {
/* child */
/* start wget */
close(DataPipe[0]);
dup2(DataPipe[1], 1); /* stdout */
execvp(dl_argv[0], dl_argv);
_exit(1);
} else if (ch_pid < 0) {
perror("fork, ");
exit(1);
} else {
/* father continues below */
close(DataPipe[1]);
}
/* Read/Write the real data */
nb = 0;
has_mime_type = 0;
has_html_header = 0;
no_such_file = 0;
offer_download = 0;
do {
while ((n = read(DataPipe[0], buf, READ_SZ)) < 0 && errno == EINTR);
if (n > 0) {
dStr_append_l(dbuf, buf, n);
if (!has_mime_type && dbuf->len < MIN_SZ)
continue;
} else if (n < 0)
break;
if (!has_mime_type) {
if (dbuf->len == 0) {
/* When the file doesn't exist, the transfer size is zero */
no_such_file = 1;
break;
}
a_Misc_get_content_type_from_data2(dbuf->str, dbuf->len, &mime_type);
has_mime_type = 1;
if (strcmp(mime_type, "application/octet-stream") == 0) {
/* abort transfer */
kill(ch_pid, SIGTERM);
/* The "application/octet-stream" MIME type will be sent and
* Dillo will offer a download dialog */
offer_download = 1;
aborted = 1;
}
}
if (offer_download || (!aborted && !has_html_header && dbuf->len)) {
/* Send dpip tag */
d_cmd = a_Dpip_build_cmd("cmd=%s url=%s", "start_send_page", url);
a_Dpip_dsh_write_str(sh, 1, d_cmd);
dFree(d_cmd);
/* Send HTTP header. */
a_Dpip_dsh_write_str(sh, 0, "Content-type: ");
a_Dpip_dsh_write_str(sh, 0, mime_type);
a_Dpip_dsh_write_str(sh, 1, "\r\n\r\n");
has_html_header = 1;
}
if (!aborted && dbuf->len) {
a_Dpip_dsh_write(sh, 1, dbuf->str, dbuf->len);
nb += dbuf->len;
dStr_truncate(dbuf, 0);
}
} while (n > 0 && !aborted);
dStr_free(dbuf, 1);
return (no_such_file ? -1 : (aborted ? -2 : nb));
}
/*
*
*/
int main(int argc, char **argv)
{
const char *err_msg = "404 Not Found\nNo such file or directory";
char *dpip_tag = NULL, *cmd = NULL, *url = NULL, *url2 = NULL;
int st, rc;
char *p, *d_cmd;
/* wget may need to write a temporary file... */
rc = chdir("/tmp");
if (rc == -1) {
MSG("paths: error changing directory to /tmp: %s\n",
dStrerror(errno));
}
/* Initialize the SockHandler */
sh = a_Dpip_dsh_new(STDIN_FILENO, STDOUT_FILENO, 8*1024);
if (argc == 2) {
/* Debugging with a command line argument */
dpip_tag = dStrdup(argv[1]);
} else {
/* Authenticate our client... */
if (!(dpip_tag = a_Dpip_dsh_read_token(sh, 1)) ||
a_Dpip_check_auth(dpip_tag) < 0) {
MSG("can't authenticate request: %s\n", dStrerror(errno));
a_Dpip_dsh_close(sh);
return 1;
}
dFree(dpip_tag);
/* Read the dpi command from STDIN */
dpip_tag = a_Dpip_dsh_read_token(sh, 1);
}
MSG("tag=[%s]\n", dpip_tag);
cmd = a_Dpip_get_attr(dpip_tag, "cmd");
url = a_Dpip_get_attr(dpip_tag, "url");
if (!cmd || !url) {
MSG("ERROR, cmd=%s, url=%s\n", cmd, url);
exit (EXIT_FAILURE);
}
if ((st = try_ftp_transfer(url)) == -1) {
/* Transfer failed, the requested file may not exist or be a symlink
* to a directory. Try again... */
if ((p = strrchr(url, '/')) && p[1] &&
p > url && p[-1] != '/') {
url2 = dStrconcat(url, "/", NULL);
st = try_ftp_transfer(url2);
}
}
if (st == -1) {
/* The transfer failed, let dillo know... */
d_cmd = a_Dpip_build_cmd("cmd=%s url=%s", "start_send_page", url);
a_Dpip_dsh_write_str(sh, 0, d_cmd);
dFree(d_cmd);
a_Dpip_dsh_printf(sh, 1,
"HTTP/1.1 404 Not Found\r\n"
"Content-Type: text/plain\r\n"
"Content-Length: %d\r\n"
"\r\n"
"%s",
strlen(err_msg), err_msg);
}
dFree(cmd);
dFree(url);
dFree(url2);
dFree(dpip_tag);
/* Finish the SockHandler */
a_Dpip_dsh_close(sh);
a_Dpip_dsh_free(sh);
return 0;
}

190
dpi/hello.c Normal file
View File

@ -0,0 +1,190 @@
/*
* Dpi for "Hello World".
*
* This server is an example. Play with it and modify to your taste.
*
* Copyright 2003-2007 Jorge Arellano Cid <jcid@dillo.org>
*
* 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 3 of the License, or
* (at your option) any later version.
*
*/
#include <unistd.h>
#include <sys/types.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include "../dpip/dpip.h"
#include "dpiutil.h"
/*
* Debugging macros
*/
#define _MSG(...)
#define MSG(...) printf("[hello dpi]: " __VA_ARGS__)
/*---------------------------------------------------------------------------*/
/*
*
*/
int main(void)
{
FILE *in_stream;
Dsh *sh;
char *dpip_tag, *cmd = NULL, *url = NULL, *child_cmd = NULL;
char *esc_tag, *d_cmd;
size_t n;
int ret;
char buf[4096];
char *choice[] = {"Window was closed", "Yes", "No",
"Could be", "It's OK", "Cancel"};
/* "Could>be", ">It's OK", "Can'>cel"}; --for testing */
int choice_num = -1;
MSG("starting...\n");
/* sleep(20) */
/* Initialize the SockHandler.
* This means we'll use stdin for input and stdout for output.
* In case of a server dpi, we'd use a socket and pass its file descriptor
* twice (e.g. a_Dpip_dsh_new(sock_fd, sock_fd, 1024).
* (Note: by now the last parameter is not used) */
sh = a_Dpip_dsh_new(STDIN_FILENO, STDOUT_FILENO, 2*1024);
/* Authenticate our client...
* As we're using Internet domain sockets, DPIP checks whether the client
* runs with the user's ID, by means of a shared secret. The DPIP API does
* the work for us. */
if (!(dpip_tag = a_Dpip_dsh_read_token(sh, 1)) ||
a_Dpip_check_auth(dpip_tag) < 0) {
MSG("can't authenticate request: %s\n", dStrerror(errno));
a_Dpip_dsh_close(sh);
return 1;
}
dFree(dpip_tag);
/* Read the dpi command from STDIN
* Now we're past the authentication phase, let's see what's dillo
* asking from us. a_Dpip_dsh_read_token() will block and return
* a full dpip token or null on error (it's commented in dpip.c) */
dpip_tag = a_Dpip_dsh_read_token(sh, 1);
MSG("tag = [%s]\n", dpip_tag);
/* Now that we have the dpip_tag, let's isolate the command and url */
cmd = a_Dpip_get_attr(dpip_tag, "cmd");
url = a_Dpip_get_attr(dpip_tag, "url");
/*-- Dialog part */
/* This is the dialog window. This is an example of interaction with
* the user. If you're starting to understand dpis, comment this out
* by switching to "#if 0" and the dialog will be disabled. */
#if 1
{
char *dpip_tag2, *dialog_msg;
/* Let's confirm the request */
/* NOTE: you can send less alternatives (e.g. only alt1 and alt2) */
d_cmd = a_Dpip_build_cmd(
"cmd=%s title=%s msg=%s alt1=%s alt2=%s alt3=%s alt4=%s alt5=%s",
"dialog", "Dillo: Hello", "Do you want to see the hello page?",
choice[1], choice[2], choice[3], choice[4], choice[5]);
a_Dpip_dsh_write_str(sh, 1, d_cmd);
dFree(d_cmd);
/* Get the answer */
dpip_tag2 = a_Dpip_dsh_read_token(sh, 1);
MSG("tag = [%s]\n", dpip_tag2);
/* Get "msg" value */
dialog_msg = a_Dpip_get_attr(dpip_tag2, "msg");
choice_num = 0;
if (dialog_msg)
choice_num = *dialog_msg - '0';
dFree(dialog_msg);
dFree(dpip_tag2);
}
#endif
/*-- EOD part */
/* Start sending our answer.
* (You can read the comments for DPIP API functions in dpip/dpip.c) */
d_cmd = a_Dpip_build_cmd("cmd=%s url=%s", "start_send_page", url);
a_Dpip_dsh_write_str(sh, 0, d_cmd);
dFree(d_cmd);
a_Dpip_dsh_printf(sh, 0,
"Content-type: text/html\n\n"
"<!DOCTYPE HTML PUBLIC '-//W3C//DTD HTML 4.01 Transitional//EN'>\n"
"<html>\n"
"<head><title>Simple dpi test page (hello.dpi)</title></head>\n"
"<body><hr><h1>Hello world!</h1><hr>\n<br><br>\n");
/* Show the choice received with the dialog */
a_Dpip_dsh_printf(sh, 0,
"<hr>\n"
"<table width='100%%' border='1' bgcolor='burlywood'><tr><td>\n"
"<big><em>Dialog question:</em> Do you want to see the hello page?<br>\n"
"<em>Answer received:</em> <b>%s</b></big> </table>\n"
"<hr>\n",
choice_num < 0 ? "There was NO dialog!" : choice[choice_num]);
/* Show the dpip tag we received */
esc_tag = Escape_html_str(dpip_tag);
a_Dpip_dsh_printf(sh, 0,
"<h3>dpip tag received:</h3>\n"
"<pre>\n%s</pre>\n"
"<br><small>(<b>dpip:</b> dpi protocol)</small><br><br><br>\n",
esc_tag);
dFree(esc_tag);
/* Now something more interesting,
* fork a command and show its feedback.
* (An example of generating dynamic content with an external
* program). */
if (cmd && url) {
child_cmd = dStrdup("date -R");
MSG("[%s]\n", child_cmd);
/* Fork, exec command, get its output and answer */
if ((in_stream = popen(child_cmd, "r")) == NULL) {
perror("popen");
return EXIT_FAILURE;
}
a_Dpip_dsh_write_str(sh, 0, "<h3>date:</h3>\n");
a_Dpip_dsh_write_str(sh, 0, "<pre>\n");
/* Read/Write */
while ((n = fread (buf, 1, 4096, in_stream)) > 0) {
a_Dpip_dsh_write(sh, 0, buf, n);
}
a_Dpip_dsh_write_str(sh, 0, "</pre>\n");
if ((ret = pclose(in_stream)) != 0)
MSG("popen: [%d]\n", ret);
dFree(child_cmd);
}
a_Dpip_dsh_write_str(sh, 1, "</body></html>\n");
dFree(cmd);
dFree(url);
dFree(dpip_tag);
/* Finish the SockHandler */
a_Dpip_dsh_close(sh);
a_Dpip_dsh_free(sh);
return 0;
}

261
dpi/vsource.c Normal file
View File

@ -0,0 +1,261 @@
/*
* Dpi for "View source".
*
* This server is an example. Play with it and modify to your taste.
*
* Copyright 2010-2015 Jorge Arellano Cid <jcid@dillo.org>
* Copyright 2024 Rodrigo Arias Mallo <rodarima@gmail.com>
*
* 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 3 of the License, or
* (at your option) any later version.
*
*/
#include <unistd.h>
#include <sys/types.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include "../dpip/dpip.h"
#include "dpiutil.h"
/*
* Debugging macros
*/
#define _MSG(...)
#define MSG(...) fprintf(stderr, "[vsource dpi]: " __VA_ARGS__)
/*---------------------------------------------------------------------------*/
const char *DOCTYPE=
"<!DOCTYPE HTML PUBLIC '-//W3C//DTD HTML 4.01 Transitional//EN'>";
void send_dpip_tag(Dsh *sh, char *dpip_tag)
{
a_Dpip_dsh_write_str(sh, 0, "\nDpip tag received: ");
a_Dpip_dsh_write_str(sh, 0, dpip_tag ? dpip_tag : "None");
a_Dpip_dsh_write_str(sh, 1, "\n\n");
}
/*
* Send source as plain text
* (handles embedded null chars correctly).
*/
void send_plain_text(Dsh *sh, int data_size)
{
char *token;
int bytes_read = 0, token_size;
/* Send HTTP header for plain text MIME type */
a_Dpip_dsh_write_str(sh, 0, "Content-type: text/plain\n\n");
while (bytes_read < data_size &&
(token = a_Dpip_dsh_read_token2(sh, 1, &token_size))) {
bytes_read += token_size;
_MSG("data_size=%d bytes_read=%d\n", data_size, bytes_read);
a_Dpip_dsh_write(sh, 1, token, token_size);
dFree(token);
}
}
/*
* Send source as plain text with line numbers
* (handles embedded null chars correctly).
*/
void send_numbered_text(Dsh *sh, int data_size)
{
int bytes_read = 0, line = 1, token_size = 0;
char *p, *q, *token, line_str[32];
/* Send HTTP header for plain text MIME type */
a_Dpip_dsh_write_str(sh, 0, "Content-type: text/plain\n\n");
while (bytes_read < data_size &&
(token = a_Dpip_dsh_read_token2(sh, 1, &token_size))) {
bytes_read += token_size;
p = q = token;
while (*p) {
snprintf(line_str, 32, "%2d: ", line);
a_Dpip_dsh_write_str(sh, 0, line_str);
if ((p = strpbrk(q, "\r\n"))) {
a_Dpip_dsh_write(sh, 0, q, p - q + 1);
if (*p == '\r' && p[1] == '\n')
++p;
++line;
} else {
/* send all the rest */
a_Dpip_dsh_write(sh, 1, q, token_size - (q - token));
break;
}
q = ++p;
}
dFree(token);
}
}
/*
* Send source as html text with line numbers
* (handles embedded null chars correctly).
*/
void send_html_text(Dsh *sh, const char *url, int data_size)
{
int bytes_read = 0, old_line = 0, line = 1, token_size = 0;
char *p, *q, *token, line_str[128];
if (dStrnAsciiCasecmp(url, "dpi:", 4) == 0 &&
strncmp(url+4, "/vsource/:", 10) == 0)
url += 14;
/* Send HTTP header for html text MIME type */
a_Dpip_dsh_write_str(sh, 0, "Content-type: text/html\n\n");
a_Dpip_dsh_write_str(sh, 0, DOCTYPE);
a_Dpip_dsh_printf(sh, 0,
"\n"
"<html><head>\n"
"<title>Source for %s</title>\n"
"<style type=\"text/css\">\n"
" body {\n"
" white-space: pre-wrap;\n"
" font-family: monospace;\n"
" margin: 0;\n"
" width: 100%;\n"
" }\n"
" table { border:0 }\n"
" td.num {\n"
" padding-top: 1px;\n"
" padding-bottom: 1px;\n"
" padding-left: 0.5em;\n"
" padding-right: 0.5em;\n"
" text-align: right;\n"
" border-right: 1px solid #999999;\n"
" background-color: #c6c6c6;\n"
" }"
" td.src { padding-left:0.25em; }\n"
" a { color: black; text-decoration:none; }\n"
"</style>\n"
"</head>\n"
"<body id=\"dillo_vs\">\n<table cellspacing='0' cellpadding='0'>\n", url);
while (bytes_read < data_size &&
(token = a_Dpip_dsh_read_token2(sh, 1, &token_size))) {
bytes_read += token_size;
p = q = token;
while (*p) {
if (line > old_line) {
snprintf(line_str, 128,
"<tr><td class='num' id='L%d'><a href='#L%d'>%d</a><td class='src'>",
line, line, line);
a_Dpip_dsh_write_str(sh, 0, line_str);
old_line = line;
}
if ((p = strpbrk(q, "\r\n<&"))) {
if (*p == '\r' || *p == '\n') {
a_Dpip_dsh_write(sh, 0, q, p - q + 1);
if (*p == '\r' && p[1] == '\n')
p++;
++line;
} else {
a_Dpip_dsh_write(sh, 0, q, p - q);
a_Dpip_dsh_write_str(sh, 0, (*p == '<') ? "&lt;" : "&amp;");
}
} else {
/* send all the rest */
a_Dpip_dsh_write(sh, 1, q, token_size - (q - token));
break;
}
q = ++p;
}
dFree(token);
}
a_Dpip_dsh_write_str(sh, 1, "</table></body></html>");
}
/*
*
*/
int main(void)
{
Dsh *sh;
int data_size;
char *dpip_tag, *cmd = NULL, *cmd2 = NULL, *url = NULL, *size_str = NULL;
char *d_cmd;
_MSG("starting...\n");
//sleep(20);
/* Initialize the SockHandler.
* This means we'll use stdin for input and stdout for output.
* In case of a server dpi, we'd use a socket and pass its file descriptor
* twice (e.g. a_Dpip_dsh_new(sock_fd, sock_fd, 1024).
* (Note: by now the last parameter is not used) */
sh = a_Dpip_dsh_new(STDIN_FILENO, STDOUT_FILENO, 2*1024);
/* Authenticate our client...
* As we're using Internet domain sockets, DPIP checks whether the client
* runs with the user's ID, by means of a shared secret. The DPIP API does
* the work for us. */
if (!(dpip_tag = a_Dpip_dsh_read_token(sh, 1)) ||
a_Dpip_check_auth(dpip_tag) < 0) {
MSG("can't authenticate request: %s\n", dStrerror(errno));
a_Dpip_dsh_close(sh);
return 1;
}
dFree(dpip_tag);
/* Read the dpi command from STDIN
* Now we're past the authentication phase, let's see what's dillo
* asking from us. a_Dpip_dsh_read_token() will block and return
* a full dpip token or null on error (it's commented in dpip.c) */
dpip_tag = a_Dpip_dsh_read_token(sh, 1);
_MSG("tag = [%s]\n", dpip_tag);
/* Now that we have the dpip_tag, let's isolate the command and url */
cmd = a_Dpip_get_attr(dpip_tag, "cmd");
url = a_Dpip_get_attr(dpip_tag, "url");
/* Start sending our answer.
* (You can read the comments for DPIP API functions in dpip/dpip.c) */
d_cmd = a_Dpip_build_cmd("cmd=%s url=%s", "start_send_page", url);
a_Dpip_dsh_write_str(sh, 0, d_cmd);
dFree(d_cmd);
dFree(dpip_tag);
dpip_tag = a_Dpip_dsh_read_token(sh, 1);
cmd2 = a_Dpip_get_attr(dpip_tag, "cmd");
if (cmd2) {
if (strcmp(cmd2, "start_send_page") == 0 &&
(size_str = a_Dpip_get_attr(dpip_tag, "data_size"))) {
data_size = strtol(size_str, NULL, 10);
/* Choose your flavour */
//send_plain_text(sh, data_size);
//send_numbered_text(sh, data_size);
send_html_text(sh, url, data_size);
} else if (strcmp(cmd2, "DpiError") == 0) {
/* Dillo detected an error (other failures just close the socket) */
a_Dpip_dsh_write_str(sh, 0, "Content-type: text/plain\n\n");
a_Dpip_dsh_write_str(sh, 1, "[vsource dpi]: "
"ERROR: Page not cached.\n");
}
dFree(cmd2);
}
dFree(cmd);
dFree(url);
dFree(size_str);
dFree(dpip_tag);
/* Finish the SockHandler */
a_Dpip_dsh_close(sh);
a_Dpip_dsh_free(sh);
return 0;
}

36
dpid/Makefile.am Normal file
View File

@ -0,0 +1,36 @@
AM_CPPFLAGS = \
-I$(top_srcdir) \
-DEXEEXT='"$(EXEEXT)"' \
-DDPIDRC_SYS='"$(sysconfdir)/dpidrc"'
bin_PROGRAMS = dpid dpidc
dpid_LDADD = \
$(top_builddir)/dpip/libDpip.a \
$(top_builddir)/dlib/libDlib.a
dpidc_LDADD = \
$(top_builddir)/dpip/libDpip.a \
$(top_builddir)/dlib/libDlib.a
EXTRA_DIST = dpidrc.in
dpid_SOURCES = \
dpi.h \
dpi_socket_dir.h \
dpid.h \
dpid_common.h \
misc_new.h \
dpi.c \
dpi_socket_dir.c \
dpid.c \
dpid_common.c \
main.c \
misc_new.c
dpidc_SOURCES = dpidc.c
sysconf_DATA = dpidrc
CLEANFILES = $(sysconf_DATA)
dpidrc: $(srcdir)/dpidrc.in Makefile
sed -e 's|[@]libdir[@]|$(libdir)|;s|[@]EXEEXT[@]|$(EXEEXT)|g' $(srcdir)/dpidrc.in > dpidrc

32
dpid/TODO Normal file
View File

@ -0,0 +1,32 @@
Todo List
File dpi_service.c
This module should be removed because its original functions
have been removed or modified. Put these functions in dpid.c
File dpi_service.h
This module should be removed because its original functions
have been removed or modified. Put these functions in dpid.c
Add other file types, but first we need to add files associated
with a dpi to the design.
Global SRS_NAME
Should read this from dillorc
File dpid_common.h
The dpid error codes will be used in the next patch
Global main()
+ add new plugins when they become available
__________________________________________________________
Remove global variables.
Use a singly linked list for dpi_attr_list
Allow dpis to be registered one at a time
Only run dpis listed in users dillorc
MAYBE Have dpid accept all connections to dpis (fixes inf loop if dpi crashes before accept)

97
dpid/dpi.c Normal file
View File

@ -0,0 +1,97 @@
/*
Copyright (C) 2003 Ferdi Franceschini <ferdif@optusnet.com.au>
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 3 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, see <http://www.gnu.org/licenses/>.
*/
/*! \file
* Access functions for ~/.dillo/dpi_socket_dir.
* The most useful function for dillo is a_Dpi_srs, it returns
* the full path to the dpid service request socket.
*/
#include <errno.h>
#include <stdlib.h> /* for exit */
#include "dpid_common.h"
#include "dpi.h"
#include "misc_new.h"
/*! \Return
* Returns path to the dpi_socket_dir file
* Use dFree to free memory
*/
char *a_Dpi_sockdir_file(void)
{
char *dpi_socket_dir, *dirfile_path = "/.dillo/dpi_socket_dir";
dpi_socket_dir = dStrconcat(dGethomedir(), dirfile_path, NULL);
return dpi_socket_dir;
}
/*! Read socket directory path from ~/.dillo/dpi_socket_dir
* \Return
* socket directory path or NULL if the dpi_socket_dir file does not exist.
* \Note
* This function exits if ~/.dillo does not exist or
* if the dpi_socket_dir file cannot be opened for a
* reason other than it does not exist.
*/
char *a_Dpi_rd_dpi_socket_dir(char *dirname)
{
FILE *dir;
char *sockdir = NULL, *rcpath;
rcpath = dStrconcat(dGethomedir(), "/.dillo", NULL);
/* If .dillo does not exist it is an unrecoverable error */
if (access(rcpath, F_OK) == -1) {
ERRMSG("a_Dpi_rd_dpi_socket_dir", "access", errno);
MSG_ERR(" - %s\n", rcpath);
exit(1);
}
dFree(rcpath);
if ((dir = fopen(dirname, "r")) != NULL) {
sockdir = dGetline(dir);
fclose(dir);
} else if (errno == ENOENT) {
ERRMSG("a_Dpi_rd_dpi_socket_dir", "fopen", errno);
MSG_ERR(" - %s\n", dirname);
} else if (errno != ENOENT) {
ERRMSG("a_Dpi_rd_dpi_socket_dir", "fopen", errno);
MSG_ERR(" - %s\n", dirname);
exit(1);
}
return sockdir;
}
/*!
* \Modifies
* srs_name
* \Return
* The service request socket name with its complete path.
*/
char *a_Dpi_srs(void)
{
char *dirfile_path, *sockdir, *srs_name;
dirfile_path = a_Dpi_sockdir_file();
sockdir = dStrstrip(a_Dpi_rd_dpi_socket_dir(dirfile_path));
srs_name = dStrconcat(sockdir, "/", "dpid.srs", NULL);
dFree(sockdir);
dFree(dirfile_path);
return (srs_name);
}

45
dpid/dpi.h Normal file
View File

@ -0,0 +1,45 @@
/*! \file
* Access functions for ~/.dillo/dpi_socket_dir.
* The most useful function for dillo is a_Dpi_srs, it returns
* the full path to the dpid service request socket.
*/
#ifndef DPI_H
#define DPI_H
#include <unistd.h> /* for socklen_t */
#include <sys/socket.h> /* for socklen_t and AF_LOCAL */
/* Some systems may not have this one... */
#ifndef AF_LOCAL
#define AF_LOCAL AF_UNIX
#endif
/* This one is tricky, some sources state it should include the byte
* for the terminating NULL, and others say it shouldn't.
* The other way is to only use this one when a native SUN_LEN is not present,
* but as dillo has used this for a long time successfully, here it goes.
*/
# define D_SUN_LEN(ptr) ((size_t) (((struct sockaddr_un *) 0)->sun_path) \
+ strlen ((ptr)->sun_path))
/*!
* dpi commands
*/
enum {
UNKNOWN_CMD,
AUTH_CMD, /* authentication */
BYE_CMD, /* "DpiBye" */
CHECK_SERVER_CMD, /* "check_server" */
REGISTER_ALL_CMD, /* "register_all" */
REGISTER_SERVICE_CMD /* "register_service" */
};
char *a_Dpi_sockdir_file(void);
char *a_Dpi_rd_dpi_socket_dir(char *dirname);
char *a_Dpi_srs(void);
#endif

129
dpid/dpi_socket_dir.c Normal file
View File

@ -0,0 +1,129 @@
/*
Copyright (C) 2003 Ferdi Franceschini <ferdif@optusnet.com.au>
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 3 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, see <http://www.gnu.org/licenses/>.
*/
/*! \file
* Create a per user temporary directory for dpi sockets
*/
#include <errno.h>
#include <stdlib.h>
#include "dpid_common.h"
#include "dpi.h"
#include "misc_new.h"
#include "dpi_socket_dir.h" /* for function prototypes */
/*! Save socket directory name in ~/.dillo/dpi_socket_dir
* \Return
* \li 1 on success
* \li -1 on failure
*/
int w_dpi_socket_dir(char *dirname, char *sockdir)
{
FILE *dir;
if ((dir = fopen(dirname, "w")) == NULL) {
ERRMSG("w_dpi_socket_dir", "fopen", errno);
return (-1);
}
fprintf(dir, "%s", sockdir);
fclose(dir);
return (1);
}
/*! Test that socket directory exists and is a directory
* \Return
* \li 1 on success
* \li 0 on failure
* \bug Does not check permissions or that it's a symbolic link
* to another directory.
*/
int tst_dir(char *dir)
{
char *dirtest;
int ret = 0;
/* test for a directory */
dirtest = dStrconcat(dir, "/", NULL);
if (access(dirtest, F_OK) == -1) {
ERRMSG("tst_dir", "access", errno);
MSG_ERR(" - %s\n", dirtest);
} else {
ret = 1;
}
dFree(dirtest);
return ret;
}
/*! Create socket directory
* \Return
* \li Socket directory path on success
* \li NULL on failure
*/
char *mk_sockdir(void)
{
char *template, *logname;
logname = getenv("LOGNAME") ? getenv("LOGNAME") : "dillo";
template = dStrconcat("/tmp/", logname, "-", "XXXXXX", NULL);
if (a_Misc_mkdtemp(template) == NULL) {
ERRMSG("mk_sockdir", "a_Misc_mkdtemp", 0);
MSG_ERR(" - %s\n", template);
dFree(template);
return (NULL);
}
return template;
}
/*! Create socket directory if it does not exist and save its name in
* ~/.dillo/dpi_socket_dir.
* \Return
* \li Socket directory name on success
* \li NULL on failure.
*/
char *init_sockdir(char *dpi_socket_dir)
{
char *sockdir = NULL;
int dir_ok = 0;
if ((sockdir = a_Dpi_rd_dpi_socket_dir(dpi_socket_dir)) == NULL) {
MSG_ERR("init_sockdir: The dpi_socket_dir file %s does not exist\n",
dpi_socket_dir);
} else {
if ((dir_ok = tst_dir(sockdir)) == 1) {
MSG_ERR("init_sockdir: The socket directory %s exists and is OK\n",
sockdir);
} else {
MSG_ERR("init_sockdir: The socket directory %s does not exist "
"or is not a directory\n", sockdir);
dFree(sockdir);
}
}
if (!dir_ok) {
sockdir = mk_sockdir();
if (sockdir == NULL) {
ERRMSG("init_sockdir", "mk_sockdir", 0);
MSG_ERR(" - Failed to create dpi socket directory\n");
} else if ((w_dpi_socket_dir(dpi_socket_dir, sockdir)) == -1) {
ERRMSG("init_sockdir", "w_dpi_socket_dir", 0);
MSG_ERR(" - failed to save %s\n", sockdir);
dFree(sockdir);
sockdir = NULL;
}
}
return (sockdir);
}

17
dpid/dpi_socket_dir.h Normal file
View File

@ -0,0 +1,17 @@
/*! \file
* Create a per user temporary directory for dpi sockets
*/
#ifndef DPI_SOCKET_DIR_H
#define DPI_SOCKET_DIR_H
int w_dpi_socket_dir(char *dirname, char *sockdir);
int tst_dir(char *dir);
char *mk_sockdir(void);
char *init_sockdir(char *dpi_socket_dir);
#endif

908
dpid/dpid.c Normal file
View File

@ -0,0 +1,908 @@
/*
Copyright (C) 2003 Ferdi Franceschini <ferdif@optusnet.com.au>
Copyright (C) 2025 Rodrigo Arias Mallo <rodarima@gmail.com>
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 3 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, see <http://www.gnu.org/licenses/>.
*/
/*! \file
* Main functions to set-up dpi information and to initialise sockets
*/
#include <errno.h>
#include <stdlib.h> /* for exit */
#include <fcntl.h> /* for F_SETFD, F_GETFD, FD_CLOEXEC */
#include <sys/stat.h>
#include <sys/wait.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <arpa/inet.h>
#include <unistd.h>
#include "dpid_common.h"
#include "dpid.h"
#include "dpi.h"
#include "dpi_socket_dir.h"
#include "misc_new.h"
#include "../dpip/dpip.h"
#define DPI_EXT (".dpi" EXEEXT)
#define QUEUE 5
volatile sig_atomic_t caught_sigchld = 0;
char *SharedKey = NULL;
/*! Remove dpid_comm_keys file.
* This avoids that dillo instances connect to a stale port after dpid
* has exited (e.g. after a reboot).
*/
void cleanup(void)
{
char *fname;
fname = dStrconcat(dGethomedir(), "/", dotDILLO_DPID_COMM_KEYS, NULL);
unlink(fname);
dFree(fname);
}
/*! Free memory used to describe
* a set of dpi attributes
*/
void free_dpi_attr(struct dp *dpi_attr)
{
if (dpi_attr->id != NULL) {
dFree(dpi_attr->id);
dpi_attr->id = NULL;
}
if (dpi_attr->path != NULL) {
dFree(dpi_attr->path);
dpi_attr->path = NULL;
}
}
/*! Free memory used by the plugin list
*/
void free_plugin_list(struct dp **dpi_attr_list_ptr, int numdpis)
{
int i;
struct dp *dpi_attr_list = *dpi_attr_list_ptr;
if (dpi_attr_list == NULL)
return;
for (i = 0; i < numdpis; i++)
free_dpi_attr(dpi_attr_list + i);
dFree(dpi_attr_list);
*dpi_attr_list_ptr = NULL;
}
/*! Free memory used by the services list
*/
void free_services_list(Dlist *s_list)
{
int i = 0;
struct service *s;
for (i=0; i < dList_length(s_list) ; i++) {
s = dList_nth_data(s_list, i);
dFree(s->name);
}
dList_free(s_list);
}
/*! Signal handler for SIGINT, SIGQUIT, and SIGTERM. Calls cleanup
*/
static void terminator(int sig)
{
(void) sig; /* suppress unused parameter warning */
cleanup();
_exit(0);
}
/*! Establish handler for termination signals
* and register cleanup with atexit */
void est_dpi_terminator(void)
{
struct sigaction act;
sigset_t block;
sigemptyset(&block);
sigaddset(&block, SIGHUP);
sigaddset(&block, SIGINT);
sigaddset(&block, SIGQUIT);
sigaddset(&block, SIGTERM);
act.sa_handler = terminator;
act.sa_mask = block;
act.sa_flags = 0;
if (sigaction(SIGHUP, &act, NULL) ||
sigaction(SIGINT, &act, NULL) ||
sigaction(SIGQUIT, &act, NULL) ||
sigaction(SIGTERM, &act, NULL)) {
ERRMSG("est_dpi_terminator", "sigaction", errno);
exit(1);
}
if (atexit(cleanup) != 0) {
ERRMSG("est_dpi_terminator", "atexit", 0);
MSG_ERR("Hey! atexit failed, how did that happen?\n");
exit(1);
}
}
static int ends_with(const char *str, const char *suffix)
{
if (!str || !suffix)
return 0;
size_t lenstr = strlen(str);
size_t lensuffix = strlen(suffix);
if (lensuffix > lenstr)
return 0;
return strncmp(str + lenstr - lensuffix, suffix, lensuffix) == 0;
}
/*! Identify a given file
* Currently there is only one file type associated with dpis.
* More file types will be added as needed
*/
enum file_type get_file_type(char *file_name)
{
if (ends_with(file_name, DPI_EXT))
/* Any filename ending in ".dpi" (or ".dpi.exe" on Windows) */
return DPI_FILE;
else {
MSG_ERR("get_file_type: Unknown file type for %s\n", file_name);
return UNKNOWN_FILE;
}
}
/*! Get dpi directory path from dpidrc
* \Return
* dpi directory on success, NULL on failure
* \Important
* The dpi_dir definition in dpidrc must have no leading white space.
*/
char *get_dpi_dir(char *dpidrc)
{
FILE *In;
int len;
char *rcline = NULL, *value = NULL, *p;
if ((In = fopen(dpidrc, "r")) == NULL) {
ERRMSG("dpi_dir", "fopen", errno);
MSG_ERR(" - %s\n", dpidrc);
return (NULL);
}
while ((rcline = dGetline(In)) != NULL) {
if (strncmp(rcline, "dpi_dir", 7) == 0)
break;
dFree(rcline);
}
fclose(In);
if (!rcline) {
ERRMSG("dpi_dir", "Failed to find a dpi_dir entry in dpidrc", 0);
MSG_ERR("Put your dillo plugins path in %s\n", dpidrc);
MSG_ERR("e.g. dpi_dir=/usr/local/lib/dillo/dpi\n");
MSG_ERR("with no leading spaces.\n");
value = NULL;
} else {
len = (int) strlen(rcline);
if (len && rcline[len - 1] == '\n')
rcline[len - 1] = 0;
if ((p = strchr(rcline, '='))) {
while (*++p == ' ');
value = dStrdup(p);
} else {
ERRMSG("dpi_dir", "strchr", 0);
MSG_ERR(" - '=' not found in %s\n", rcline);
value = NULL;
}
}
dFree(rcline);
return (value);
}
/*! Scans a service directory in dpi_dir and fills dpi_attr
* \Note
* Caller must allocate memory for dpi_attr.
* \Return
* \li 0 on success
* \li -1 on failure
* \todo
* Add other file types, but first we need to add files associated with a dpi
* to the design.
*/
int get_dpi_attr(char *dpi_dir, char *service, struct dp *dpi_attr)
{
char *service_dir = NULL;
struct stat statinfo;
enum file_type ftype;
int ret = -1;
DIR *dir_stream;
struct dirent *dir_entry = NULL;
service_dir = dStrconcat(dpi_dir, "/", service, NULL);
if (stat(service_dir, &statinfo) == -1) {
ERRMSG("get_dpi_attr", "stat", errno);
MSG_ERR("file=%s\n", service_dir);
} else if ((dir_stream = opendir(service_dir)) == NULL) {
ERRMSG("get_dpi_attr", "opendir", errno);
} else {
/* Scan the directory looking for dpi files.
* (currently there's only the dpi program, but in the future
* there may also be helper scripts.) */
while ( (dir_entry = readdir(dir_stream)) != NULL) {
if (dir_entry->d_name[0] == '.')
continue;
ftype = get_file_type(dir_entry->d_name);
switch (ftype) {
case DPI_FILE:
dpi_attr->path =
dStrconcat(service_dir, "/", dir_entry->d_name, NULL);
dpi_attr->id = dStrdup(service);
dpi_attr->port = 0;
dpi_attr->pid = 1;
if (strstr(dpi_attr->path, ".filter") != NULL)
dpi_attr->filter = 1;
else
dpi_attr->filter = 0;
ret = 0;
break;
default:
break;
}
}
closedir(dir_stream);
if (ret != 0)
MSG_ERR("get_dpi_attr: No dpi plug-in in %s/%s\n",
dpi_dir, service);
}
dFree(service_dir);
return ret;
}
/*! Register a service
* Retrieves attributes for "service" and stores them
* in dpi_attr. It looks for "service" in ~/.dillo/dpi
* first, and then in the system wide dpi directory.
* Caller must allocate memory for dpi_attr.
* \Return
* \li 0 on success
* \li -1 on failure
*/
int register_service(struct dp *dpi_attr, char *service)
{
char *user_dpi_dir, *dpidrc, *user_service_dir, *dir = NULL;
int ret = -1;
user_dpi_dir = dStrconcat(dGethomedir(), "/", dotDILLO_DPI, NULL);
user_service_dir =
dStrconcat(dGethomedir(), "/", dotDILLO_DPI, "/", service, NULL);
dpidrc = dStrconcat(dGethomedir(), "/", dotDILLO_DPIDRC, NULL);
if (access(dpidrc, F_OK) == -1) {
if (access(DPIDRC_SYS, F_OK) == -1) {
ERRMSG("register_service", "Error ", 0);
MSG_ERR("\n - There is no %s or %s file\n", dpidrc,
DPIDRC_SYS);
dFree(user_dpi_dir);
dFree(user_service_dir);
dFree(dpidrc);
return(-1);
}
dFree(dpidrc);
dpidrc = dStrdup(DPIDRC_SYS);
}
/* Check home dir for dpis */
if (access(user_service_dir, F_OK) == 0) {
get_dpi_attr(user_dpi_dir, service, dpi_attr);
ret = 0;
} else { /* Check system wide dpis */
if ((dir = get_dpi_dir(dpidrc)) != NULL) {
if (access(dir, F_OK) == 0) {
get_dpi_attr(dir, service, dpi_attr);
ret = 0;
} else {
ERRMSG("register_service", "get_dpi_attr failed", 0);
}
} else {
ERRMSG("register_service", "dpi_dir: Error getting dpi dir.", 0);
}
}
dFree(user_dpi_dir);
dFree(user_service_dir);
dFree(dpidrc);
dFree(dir);
return ret;
}
/*!
* Create dpi directory for available
* plugins and create plugin list.
* \Return
* \li Returns number of available plugins on success
* \li -1 on failure
*/
int register_all(struct dp **attlist)
{
char *user_dpidir = NULL, *sys_dpidir = NULL, *dpidrc = NULL;
struct dirent *user_dirent, *sys_dirent;
int st;
int snum;
size_t dp_sz = sizeof(struct dp);
if (*attlist != NULL) {
ERRMSG("register_all", "attlist parameter should be NULL", 0);
return -1;
}
user_dpidir = dStrconcat(dGethomedir(), "/", dotDILLO_DPI, NULL);
if (access(user_dpidir, F_OK) == -1) {
/* no dpis in user's space */
dFree(user_dpidir);
user_dpidir = NULL;
}
dpidrc = dStrconcat(dGethomedir(), "/", dotDILLO_DPIDRC, NULL);
if (access(dpidrc, F_OK) == -1) {
dFree(dpidrc);
dpidrc = dStrdup(DPIDRC_SYS);
if (access(dpidrc, F_OK) == -1) {
dFree(dpidrc);
dpidrc = NULL;
}
}
if (!dpidrc || (sys_dpidir = get_dpi_dir(dpidrc)) == NULL)
sys_dpidir = NULL;
dFree(dpidrc);
if (!user_dpidir && !sys_dpidir) {
ERRMSG("register_all", "Fatal error ", 0);
MSG_ERR("\n - Can't find the directory for dpis.\n");
exit(1);
}
/* Get list of services in user's .dillo/dpi directory */
snum = 0;
if (user_dpidir) {
DIR *user_dir_stream = opendir(user_dpidir);
/* Only complain if error is other than not found as the user may not have
* any DPIs installed. */
if (user_dir_stream == NULL && errno != ENOENT) {
MSG_ERR("cannot open user dpi directory '%s': %s\n",
user_dpidir, strerror(errno));
} else {
while ((user_dirent = readdir(user_dir_stream)) != NULL) {
if (user_dirent->d_name[0] == '.')
continue;
*attlist = (struct dp *) dRealloc(*attlist, (snum + 1) * dp_sz);
st=get_dpi_attr(user_dpidir, user_dirent->d_name, &(*attlist)[snum]);
if (st == 0)
snum++;
}
closedir(user_dir_stream);
}
}
if (sys_dpidir) {
DIR *sys_dir_stream = opendir(sys_dpidir);
/* For system DPIs always complain. */
if (sys_dir_stream == NULL) {
MSG_ERR("cannot open system dpi directory '%s': %s\n",
sys_dpidir, strerror(errno));
} else {
/* if system service is not in user list then add it */
while ((sys_dirent = readdir(sys_dir_stream)) != NULL) {
if (sys_dirent->d_name[0] == '.')
continue;
*attlist = (struct dp *) dRealloc(*attlist, (snum + 1) * dp_sz);
st=get_dpi_attr(sys_dpidir, sys_dirent->d_name, &(*attlist)[snum]);
if (st == 0)
snum++;
}
closedir(sys_dir_stream);
}
}
dFree(sys_dpidir);
dFree(user_dpidir);
/* TODO: do we consider snum == 0 an error?
* (if so, we should return -1 ) */
return (snum);
}
/*
* Compare two struct service pointers
* This function is used for sorting services
*/
static int services_alpha_comp(const struct service *s1,
const struct service *s2)
{
return -strcmp(s1->name, s2->name);
}
/*! Add services reading a dpidrc file
* each non empty or commented line has the form
* service = path_relative_to_dpidir
* \Return:
* \li Returns number of available services on success
* \li -1 on failure
*/
int fill_services_list(struct dp *attlist, int numdpis, Dlist **services_list)
{
FILE *dpidrc_stream;
char *p, *line = NULL, *service, *path;
int i, st;
struct service *s;
char *user_dpidir = NULL, *sys_dpidir = NULL, *dpidrc = NULL;
user_dpidir = dStrconcat(dGethomedir(), "/", dotDILLO_DPI, NULL);
if (access(user_dpidir, F_OK) == -1) {
/* no dpis in user's space */
dFree(user_dpidir);
user_dpidir = NULL;
}
dpidrc = dStrconcat(dGethomedir(), "/", dotDILLO_DPIDRC, NULL);
if (access(dpidrc, F_OK) == -1) {
dFree(dpidrc);
dpidrc = dStrdup(DPIDRC_SYS);
if (access(dpidrc, F_OK) == -1) {
dFree(dpidrc);
dpidrc = NULL;
}
}
if (!dpidrc || (sys_dpidir = get_dpi_dir(dpidrc)) == NULL)
sys_dpidir = NULL;
if (!user_dpidir && !sys_dpidir) {
ERRMSG("fill_services_list", "Fatal error ", 0);
MSG_ERR("\n - Can't find the directory for dpis.\n");
exit(1);
}
if ((dpidrc_stream = fopen(dpidrc, "r")) == NULL) {
ERRMSG("fill_services_list", "popen failed", errno);
dFree(dpidrc);
dFree(sys_dpidir);
dFree(user_dpidir);
return (-1);
}
if (*services_list != NULL) {
ERRMSG("fill_services_list", "services_list parameter is not NULL", 0);
fclose(dpidrc_stream);
return -1;
}
*services_list = dList_new(8);
/* dpidrc parser loop */
for (;(line = dGetline(dpidrc_stream)) != NULL; dFree(line)) {
st = dParser_parse_rc_line(&line, &service, &path);
if (st < 0) {
MSG_ERR("dpid: Syntax error in %s: service=\"%s\" path=\"%s\"\n",
dpidrc, service, path);
continue;
} else if (st != 0) {
continue;
}
_MSG("dpid: service=%s, path=%s\n", service, path);
/* ignore dpi_dir silently */
if (strcmp(service, "dpi_dir") == 0)
continue;
s = dNew(struct service, 1);
/* init services list entry */
s->name = dStrdup(service);
s->dp_index = -1;
dList_append(*services_list, s);
/* search the dpi for a service by its path */
for (i = 0; i < numdpis; i++)
if ((p = strstr(attlist[i].path, path)) && *(p - 1) == '/' &&
strlen(p) == strlen(path))
break;
/* if the dpi exist bind service and dpi */
if (i < numdpis)
s->dp_index = i;
}
fclose(dpidrc_stream);
dList_sort(*services_list, (dCompareFunc)services_alpha_comp);
dFree(dpidrc);
dFree(sys_dpidir);
dFree(user_dpidir);
return (dList_length(*services_list));
}
/*
* Return a socket file descriptor
* (useful to set socket options in a uniform way)
*/
static int make_socket_fd(void)
{
int ret, one = 1;
if ((ret = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
ERRMSG("make_socket_fd", "socket", errno);
} else {
/* avoid delays when sending small pieces of data */
setsockopt(ret, IPPROTO_TCP, TCP_NODELAY, &one, sizeof(one));
}
/* set some buffering to increase the transfer's speed */
//setsockopt(sock_fd, SOL_SOCKET, SO_SNDBUF,
// &sock_buflen, (socklen_t)sizeof(sock_buflen));
return ret;
}
/*! Bind a socket port on localhost. Try to be close to base_port.
* \Return
* \li listening socket file descriptor on success
* \li -1 on failure
*/
int bind_socket_fd(int base_port, int *p_port)
{
int sock_fd, port;
struct sockaddr_in sin;
int ok = 0, last_port = base_port + 50;
if ((sock_fd = make_socket_fd()) == -1) {
return (-1); /* avoids nested ifs */
}
/* Set the socket FD to close on exec */
fcntl(sock_fd, F_SETFD, FD_CLOEXEC | fcntl(sock_fd, F_GETFD));
memset(&sin, 0, sizeof(sin));
sin.sin_family = AF_INET;
sin.sin_addr.s_addr = inet_addr("127.0.0.1");
/* Try to bind a port on localhost */
for (port = base_port; port <= last_port; ++port) {
sin.sin_port = htons(port);
if ((bind(sock_fd, (struct sockaddr *)&sin, sizeof(sin))) == -1) {
if (errno == EADDRINUSE || errno == EADDRNOTAVAIL)
continue;
ERRMSG("bind_socket_fd", "bind", errno);
} else if (listen(sock_fd, QUEUE) == -1) {
ERRMSG("bind_socket_fd", "listen", errno);
} else {
*p_port = port;
ok = 1;
break;
}
}
if (port > last_port) {
MSG_ERR("Hey! Can't find an available port from %d to %d\n",
base_port, last_port);
}
return ok ? sock_fd : -1;
}
/*! Save the current port and a shared secret in a file so dillo can find it.
* \Return:
* \li -1 on failure
*/
int save_comm_keys(int srs_port)
{
int fd, ret = -1;
char *fname, port_str[32];
fname = dStrconcat(dGethomedir(), "/", dotDILLO_DPID_COMM_KEYS, NULL);
fd = open(fname, O_WRONLY | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR);
dFree(fname);
if (fd == -1) {
MSG("save_comm_keys: open %s\n", dStrerror(errno));
} else {
snprintf(port_str, 16, "%d %s\n", srs_port, SharedKey);
if (CKD_WRITE(fd, port_str) != -1 && CKD_CLOSE(fd) != -1) {
ret = 1;
}
}
return ret;
}
/*! Initialise the service request socket (IDS)
* \Return:
* \li Number of sockets (1 == success)
* \li -1 on failure
*/
int init_ids_srs_socket(void)
{
int srs_port, ret = -1;
FD_ZERO(&sock_set);
if ((srs_fd = bind_socket_fd(DPID_BASE_PORT, &srs_port)) != -1) {
/* create the shared secret */
SharedKey = a_Misc_mksecret(8);
/* save port number and SharedKey */
if (save_comm_keys(srs_port) != -1) {
FD_SET(srs_fd, &sock_set);
ret = 1;
}
}
return ret;
}
/*! Initialize a single dpi socket
* \Return
* \li 1 on success
* \li -1 on failure
*/
int init_dpi_socket(struct dp *dpi_attr)
{
int s_fd, port, ret = -1;
if ((s_fd = bind_socket_fd(DPID_BASE_PORT, &port)) != -1) {
dpi_attr->sock_fd = s_fd;
dpi_attr->port = port;
FD_SET(s_fd, &sock_set);
ret = 1;
}
return ret;
}
/*! Setup sockets for the plugins and add them to
* the set of sockets (sock_set) watched by select.
* \Return
* \li Number of sockets on success
* \li -1 on failure
* \Modifies
* dpi_attr_list.sa, dpi_attr_list.socket, numsocks, sock_set, srs
* \Uses
* numdpis, srs, srs_name
*/
int init_all_dpi_sockets(struct dp *dpi_attr_list)
{
int i;
/* Initialise sockets for each dpi */
for (i = 0; i < numdpis; i++) {
if (init_dpi_socket(dpi_attr_list + i) == -1)
return (-1);
numsocks++;
}
return (numsocks);
}
/*! SIGCHLD handler
*/
void dpi_sigchld(int sig)
{
if (sig == SIGCHLD)
caught_sigchld = 1;
}
/*! Called by main loop when caught_sigchld == 1 */
void handle_sigchld(void)
{
// pid_t pid;
int i, status; //, num_active;
/* For all of the dpis in the current list
* add the ones that have exited to the set of sockets being
* watched by 'select'.
*/
for (i = 0; i < numdpis; i++) {
if (waitpid(dpi_attr_list[i].pid, &status, WNOHANG) > 0) {
dpi_attr_list[i].pid = 1;
FD_SET(dpi_attr_list[i].sock_fd, &sock_set);
numsocks++;
}
}
/* Wait for any old dpis that have exited */
while (waitpid(-1, &status, WNOHANG) > 0)
;
}
/*! Establish SIGCHLD handler */
void est_dpi_sigchld(void)
{
struct sigaction sigact;
sigset_t set;
(void) sigemptyset(&set);
sigact.sa_handler = dpi_sigchld;
sigact.sa_mask = set;
sigact.sa_flags = SA_NOCLDSTOP;
if (sigaction(SIGCHLD, &sigact, NULL) == -1) {
ERRMSG("est_dpi_sigchld", "sigaction", errno);
exit(1);
}
}
/*! EINTR aware connect() call */
int ckd_connect (int sock_fd, struct sockaddr *addr, socklen_t len)
{
ssize_t ret;
do {
ret = connect(sock_fd, addr, len);
} while (ret == -1 && errno == EINTR);
if (ret == -1) {
ERRMSG("dpid.c", "connect", errno);
}
return ret;
}
/*! Send DpiBye command to all active non-filter dpis
*/
void stop_active_dpis(struct dp *dpi_attr_list, int numdpis)
{
char *bye_cmd, *auth_cmd;
int i, sock_fd;
struct sockaddr_in sin;
bye_cmd = a_Dpip_build_cmd("cmd=%s", "DpiBye");
auth_cmd = a_Dpip_build_cmd("cmd=%s msg=%s", "auth", SharedKey);
memset(&sin, 0, sizeof(sin));
sin.sin_family = AF_INET;
sin.sin_addr.s_addr = inet_addr("127.0.0.1");
for (i = 0; i < numdpis; i++) {
/* Skip inactive dpis and filters */
if (dpi_attr_list[i].pid == 1 || dpi_attr_list[i].filter)
continue;
if ((sock_fd = make_socket_fd()) == -1) {
ERRMSG("stop_active_dpis", "socket", errno);
continue;
}
sin.sin_port = htons(dpi_attr_list[i].port);
if (ckd_connect(sock_fd, (struct sockaddr *)&sin, sizeof(sin)) == -1) {
ERRMSG("stop_active_dpis", "connect", errno);
MSG_ERR("%s\n", dpi_attr_list[i].path);
} else if (CKD_WRITE(sock_fd, auth_cmd) == -1) {
ERRMSG("stop_active_dpis", "write", errno);
} else if (CKD_WRITE(sock_fd, bye_cmd) == -1) {
ERRMSG("stop_active_dpis", "write", errno);
}
dClose(sock_fd);
}
dFree(auth_cmd);
dFree(bye_cmd);
/* Allow child dpis some time to read dpid_comm_keys before erasing it */
sleep (1);
}
/*! Removes dpis in dpi_attr_list from the
* set of sockets watched by select and
* closes their sockets.
*/
void ignore_dpi_sockets(struct dp *dpi_attr_list, int numdpis)
{
int i;
for (i = 0; i < numdpis; i++) {
FD_CLR(dpi_attr_list[i].sock_fd, &sock_set);
dClose(dpi_attr_list[i].sock_fd);
}
}
/*! Registers available dpis and stops active non-filter dpis.
* Called when dpid receives
* cmd='register' service='all'
* command
* \Return
* Number of available dpis
*/
int register_all_cmd(void)
{
stop_active_dpis(dpi_attr_list, numdpis);
free_plugin_list(&dpi_attr_list, numdpis);
free_services_list(services_list);
services_list = NULL;
numdpis = 0;
numsocks = 1; /* the srs socket */
FD_ZERO(&sock_set);
FD_SET(srs_fd, &sock_set);
numdpis = register_all(&dpi_attr_list);
fill_services_list(dpi_attr_list, numdpis, &services_list);
numsocks = init_all_dpi_sockets(dpi_attr_list);
return (numdpis);
}
/*!
* Get value of msg field from dpi_tag
* \Return
* message on success, NULL on failure
*/
char *get_message(int sock_fd, char *dpi_tag)
{
char *msg, *d_cmd;
msg = a_Dpip_get_attr(dpi_tag, "msg");
if (msg == NULL) {
ERRMSG("get_message", "failed to parse msg", 0);
d_cmd = a_Dpip_build_cmd("cmd=%s msg=%s",
"DpiError", "Failed to parse request");
(void) CKD_WRITE(sock_fd, d_cmd);
dFree(d_cmd);
}
return (msg);
}
/*
* Compare a struct service pointer and a service name
* This function is used for searching services by name
*/
int service_match(const struct service *A, const char *B)
{
int A_len, B_len, len;
A_len = strlen(A->name);
B_len = strlen(B);
len = MAX (A_len, B_len);
if (A->name[A_len - 1] == '*')
len = A_len - 1;
return(dStrnAsciiCasecmp(A->name, B, len));
}
/*!
* Send socket port that matches dpi_id to client
*/
void send_sockport(int sock_fd, char *dpi_tag, struct dp *dpi_attr_list)
{
int i;
char *dpi_id, *d_cmd, port_str[16];
struct service *serv;
dReturn_if_fail((dpi_id = get_message(sock_fd, dpi_tag)) != NULL);
serv = dList_find_custom(services_list,dpi_id,(dCompareFunc)service_match);
if (serv == NULL || (i = serv->dp_index) == -1)
for (i = 0; i < numdpis; i++)
if (!strncmp(dpi_attr_list[i].id, dpi_id,
dpi_attr_list[i].id - strchr(dpi_attr_list[i].id, '.')))
break;
if (i < numdpis) {
/* found */
snprintf(port_str, 8, "%d", dpi_attr_list[i].port);
d_cmd = a_Dpip_build_cmd("cmd=%s msg=%s", "send_data", port_str);
(void) CKD_WRITE(sock_fd, d_cmd);
dFree(d_cmd);
}
dFree(dpi_id);
}

Some files were not shown because too many files have changed in this diff Show More